cm_common.c (10704B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * OMAP2+ common Clock Management (CM) IP block functions 4 * 5 * Copyright (C) 2012 Texas Instruments, Inc. 6 * Paul Walmsley 7 * 8 * XXX This code should eventually be moved to a CM driver. 9 */ 10 11#include <linux/kernel.h> 12#include <linux/init.h> 13#include <linux/errno.h> 14#include <linux/bug.h> 15#include <linux/of.h> 16#include <linux/of_address.h> 17 18#include "cm2xxx.h" 19#include "cm3xxx.h" 20#include "cm33xx.h" 21#include "cm44xx.h" 22#include "clock.h" 23 24/* 25 * cm_ll_data: function pointers to SoC-specific implementations of 26 * common CM functions 27 */ 28static struct cm_ll_data null_cm_ll_data; 29static const struct cm_ll_data *cm_ll_data = &null_cm_ll_data; 30 31/* cm_base: base virtual address of the CM IP block */ 32struct omap_domain_base cm_base; 33 34/* cm2_base: base virtual address of the CM2 IP block (OMAP44xx only) */ 35struct omap_domain_base cm2_base; 36 37#define CM_NO_CLOCKS 0x1 38#define CM_SINGLE_INSTANCE 0x2 39 40/** 41 * cm_split_idlest_reg - split CM_IDLEST reg addr into its components 42 * @idlest_reg: CM_IDLEST* virtual address 43 * @prcm_inst: pointer to an s16 to return the PRCM instance offset 44 * @idlest_reg_id: pointer to a u8 to return the CM_IDLESTx register ID 45 * 46 * Given an absolute CM_IDLEST register address @idlest_reg, passes 47 * the PRCM instance offset and IDLEST register ID back to the caller 48 * via the @prcm_inst and @idlest_reg_id. Returns -EINVAL upon error, 49 * or 0 upon success. XXX This function is only needed until absolute 50 * register addresses are removed from the OMAP struct clk records. 51 */ 52int cm_split_idlest_reg(struct clk_omap_reg *idlest_reg, s16 *prcm_inst, 53 u8 *idlest_reg_id) 54{ 55 int ret; 56 if (!cm_ll_data->split_idlest_reg) { 57 WARN_ONCE(1, "cm: %s: no low-level function defined\n", 58 __func__); 59 return -EINVAL; 60 } 61 62 ret = cm_ll_data->split_idlest_reg(idlest_reg, prcm_inst, 63 idlest_reg_id); 64 *prcm_inst -= cm_base.offset; 65 return ret; 66} 67 68/** 69 * omap_cm_wait_module_ready - wait for a module to leave idle or standby 70 * @part: PRCM partition 71 * @prcm_mod: PRCM module offset 72 * @idlest_reg: CM_IDLESTx register 73 * @idlest_shift: shift of the bit in the CM_IDLEST* register to check 74 * 75 * Wait for the PRCM to indicate that the module identified by 76 * (@prcm_mod, @idlest_id, @idlest_shift) is clocked. Return 0 upon 77 * success, -EBUSY if the module doesn't enable in time, or -EINVAL if 78 * no per-SoC wait_module_ready() function pointer has been registered 79 * or if the idlest register is unknown on the SoC. 80 */ 81int omap_cm_wait_module_ready(u8 part, s16 prcm_mod, u16 idlest_reg, 82 u8 idlest_shift) 83{ 84 if (!cm_ll_data->wait_module_ready) { 85 WARN_ONCE(1, "cm: %s: no low-level function defined\n", 86 __func__); 87 return -EINVAL; 88 } 89 90 return cm_ll_data->wait_module_ready(part, prcm_mod, idlest_reg, 91 idlest_shift); 92} 93 94/** 95 * omap_cm_wait_module_idle - wait for a module to enter idle or standby 96 * @part: PRCM partition 97 * @prcm_mod: PRCM module offset 98 * @idlest_reg: CM_IDLESTx register 99 * @idlest_shift: shift of the bit in the CM_IDLEST* register to check 100 * 101 * Wait for the PRCM to indicate that the module identified by 102 * (@prcm_mod, @idlest_id, @idlest_shift) is no longer clocked. Return 103 * 0 upon success, -EBUSY if the module doesn't enable in time, or 104 * -EINVAL if no per-SoC wait_module_idle() function pointer has been 105 * registered or if the idlest register is unknown on the SoC. 106 */ 107int omap_cm_wait_module_idle(u8 part, s16 prcm_mod, u16 idlest_reg, 108 u8 idlest_shift) 109{ 110 if (!cm_ll_data->wait_module_idle) { 111 WARN_ONCE(1, "cm: %s: no low-level function defined\n", 112 __func__); 113 return -EINVAL; 114 } 115 116 return cm_ll_data->wait_module_idle(part, prcm_mod, idlest_reg, 117 idlest_shift); 118} 119 120/** 121 * omap_cm_module_enable - enable a module 122 * @mode: target mode for the module 123 * @part: PRCM partition 124 * @inst: PRCM instance 125 * @clkctrl_offs: CM_CLKCTRL register offset for the module 126 * 127 * Enables clocks for a module identified by (@part, @inst, @clkctrl_offs) 128 * making its IO space accessible. Return 0 upon success, -EINVAL if no 129 * per-SoC module_enable() function pointer has been registered. 130 */ 131int omap_cm_module_enable(u8 mode, u8 part, u16 inst, u16 clkctrl_offs) 132{ 133 if (!cm_ll_data->module_enable) { 134 WARN_ONCE(1, "cm: %s: no low-level function defined\n", 135 __func__); 136 return -EINVAL; 137 } 138 139 cm_ll_data->module_enable(mode, part, inst, clkctrl_offs); 140 return 0; 141} 142 143/** 144 * omap_cm_module_disable - disable a module 145 * @part: PRCM partition 146 * @inst: PRCM instance 147 * @clkctrl_offs: CM_CLKCTRL register offset for the module 148 * 149 * Disables clocks for a module identified by (@part, @inst, @clkctrl_offs) 150 * makings its IO space inaccessible. Return 0 upon success, -EINVAL if 151 * no per-SoC module_disable() function pointer has been registered. 152 */ 153int omap_cm_module_disable(u8 part, u16 inst, u16 clkctrl_offs) 154{ 155 if (!cm_ll_data->module_disable) { 156 WARN_ONCE(1, "cm: %s: no low-level function defined\n", 157 __func__); 158 return -EINVAL; 159 } 160 161 cm_ll_data->module_disable(part, inst, clkctrl_offs); 162 return 0; 163} 164 165u32 omap_cm_xlate_clkctrl(u8 part, u16 inst, u16 clkctrl_offs) 166{ 167 if (!cm_ll_data->xlate_clkctrl) { 168 WARN_ONCE(1, "cm: %s: no low-level function defined\n", 169 __func__); 170 return 0; 171 } 172 return cm_ll_data->xlate_clkctrl(part, inst, clkctrl_offs); 173} 174 175/** 176 * cm_register - register per-SoC low-level data with the CM 177 * @cld: low-level per-SoC OMAP CM data & function pointers to register 178 * 179 * Register per-SoC low-level OMAP CM data and function pointers with 180 * the OMAP CM common interface. The caller must keep the data 181 * pointed to by @cld valid until it calls cm_unregister() and 182 * it returns successfully. Returns 0 upon success, -EINVAL if @cld 183 * is NULL, or -EEXIST if cm_register() has already been called 184 * without an intervening cm_unregister(). 185 */ 186int cm_register(const struct cm_ll_data *cld) 187{ 188 if (!cld) 189 return -EINVAL; 190 191 if (cm_ll_data != &null_cm_ll_data) 192 return -EEXIST; 193 194 cm_ll_data = cld; 195 196 return 0; 197} 198 199/** 200 * cm_unregister - unregister per-SoC low-level data & function pointers 201 * @cld: low-level per-SoC OMAP CM data & function pointers to unregister 202 * 203 * Unregister per-SoC low-level OMAP CM data and function pointers 204 * that were previously registered with cm_register(). The 205 * caller may not destroy any of the data pointed to by @cld until 206 * this function returns successfully. Returns 0 upon success, or 207 * -EINVAL if @cld is NULL or if @cld does not match the struct 208 * cm_ll_data * previously registered by cm_register(). 209 */ 210int cm_unregister(const struct cm_ll_data *cld) 211{ 212 if (!cld || cm_ll_data != cld) 213 return -EINVAL; 214 215 cm_ll_data = &null_cm_ll_data; 216 217 return 0; 218} 219 220#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) || \ 221 defined(CONFIG_SOC_DRA7XX) 222static struct omap_prcm_init_data cm_data __initdata = { 223 .index = TI_CLKM_CM, 224 .init = omap4_cm_init, 225}; 226 227static struct omap_prcm_init_data cm2_data __initdata = { 228 .index = TI_CLKM_CM2, 229 .init = omap4_cm_init, 230}; 231#endif 232 233#ifdef CONFIG_ARCH_OMAP2 234static struct omap_prcm_init_data omap2_prcm_data __initdata = { 235 .index = TI_CLKM_CM, 236 .init = omap2xxx_cm_init, 237 .flags = CM_NO_CLOCKS | CM_SINGLE_INSTANCE, 238}; 239#endif 240 241#ifdef CONFIG_ARCH_OMAP3 242static struct omap_prcm_init_data omap3_cm_data __initdata = { 243 .index = TI_CLKM_CM, 244 .init = omap3xxx_cm_init, 245 .flags = CM_SINGLE_INSTANCE, 246 247 /* 248 * IVA2 offset is a negative value, must offset the cm_base address 249 * by this to get it to positive side on the iomap 250 */ 251 .offset = -OMAP3430_IVA2_MOD, 252}; 253#endif 254 255#if defined(CONFIG_SOC_AM33XX) || defined(CONFIG_SOC_TI81XX) 256static struct omap_prcm_init_data am3_prcm_data __initdata = { 257 .index = TI_CLKM_CM, 258 .flags = CM_NO_CLOCKS | CM_SINGLE_INSTANCE, 259 .init = am33xx_cm_init, 260}; 261#endif 262 263#ifdef CONFIG_SOC_AM43XX 264static struct omap_prcm_init_data am4_prcm_data __initdata = { 265 .index = TI_CLKM_CM, 266 .flags = CM_NO_CLOCKS | CM_SINGLE_INSTANCE, 267 .init = omap4_cm_init, 268}; 269#endif 270 271static const struct of_device_id omap_cm_dt_match_table[] __initconst = { 272#ifdef CONFIG_ARCH_OMAP2 273 { .compatible = "ti,omap2-prcm", .data = &omap2_prcm_data }, 274#endif 275#ifdef CONFIG_ARCH_OMAP3 276 { .compatible = "ti,omap3-cm", .data = &omap3_cm_data }, 277#endif 278#ifdef CONFIG_ARCH_OMAP4 279 { .compatible = "ti,omap4-cm1", .data = &cm_data }, 280 { .compatible = "ti,omap4-cm2", .data = &cm2_data }, 281#endif 282#ifdef CONFIG_SOC_OMAP5 283 { .compatible = "ti,omap5-cm-core-aon", .data = &cm_data }, 284 { .compatible = "ti,omap5-cm-core", .data = &cm2_data }, 285#endif 286#ifdef CONFIG_SOC_DRA7XX 287 { .compatible = "ti,dra7-cm-core-aon", .data = &cm_data }, 288 { .compatible = "ti,dra7-cm-core", .data = &cm2_data }, 289#endif 290#ifdef CONFIG_SOC_AM33XX 291 { .compatible = "ti,am3-prcm", .data = &am3_prcm_data }, 292#endif 293#ifdef CONFIG_SOC_AM43XX 294 { .compatible = "ti,am4-prcm", .data = &am4_prcm_data }, 295#endif 296#ifdef CONFIG_SOC_TI81XX 297 { .compatible = "ti,dm814-prcm", .data = &am3_prcm_data }, 298 { .compatible = "ti,dm816-prcm", .data = &am3_prcm_data }, 299#endif 300 { } 301}; 302 303/** 304 * omap2_cm_base_init - initialize iomappings for the CM drivers 305 * 306 * Detects and initializes the iomappings for the CM driver, based 307 * on the DT data. Returns 0 in success, negative error value 308 * otherwise. 309 */ 310int __init omap2_cm_base_init(void) 311{ 312 struct device_node *np; 313 const struct of_device_id *match; 314 struct omap_prcm_init_data *data; 315 struct resource res; 316 int ret; 317 struct omap_domain_base *mem = NULL; 318 319 for_each_matching_node_and_match(np, omap_cm_dt_match_table, &match) { 320 data = (struct omap_prcm_init_data *)match->data; 321 322 ret = of_address_to_resource(np, 0, &res); 323 if (ret) { 324 of_node_put(np); 325 return ret; 326 } 327 328 if (data->index == TI_CLKM_CM) 329 mem = &cm_base; 330 331 if (data->index == TI_CLKM_CM2) 332 mem = &cm2_base; 333 334 data->mem = ioremap(res.start, resource_size(&res)); 335 336 if (mem) { 337 mem->pa = res.start + data->offset; 338 mem->va = data->mem + data->offset; 339 mem->offset = data->offset; 340 } 341 342 data->np = np; 343 344 if (data->init && (data->flags & CM_SINGLE_INSTANCE || 345 (cm_base.va && cm2_base.va))) 346 data->init(data); 347 } 348 349 return 0; 350} 351 352/** 353 * omap_cm_init - low level init for the CM drivers 354 * 355 * Initializes the low level clock infrastructure for CM drivers. 356 * Returns 0 in success, negative error value in failure. 357 */ 358int __init omap_cm_init(void) 359{ 360 struct device_node *np; 361 const struct of_device_id *match; 362 const struct omap_prcm_init_data *data; 363 int ret; 364 365 for_each_matching_node_and_match(np, omap_cm_dt_match_table, &match) { 366 data = match->data; 367 368 if (data->flags & CM_NO_CLOCKS) 369 continue; 370 371 ret = omap2_clk_provider_init(np, data->index, NULL, data->mem); 372 if (ret) { 373 of_node_put(np); 374 return ret; 375 } 376 } 377 378 return 0; 379}