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 */ 28 static struct cm_ll_data null_cm_ll_data; 29 static const struct cm_ll_data *cm_ll_data = &null_cm_ll_data; 30 31 /* cm_base: base virtual address of the CM IP block */ 32 struct omap_domain_base cm_base; 33 34 /* cm2_base: base virtual address of the CM2 IP block (OMAP44xx only) */ 35 struct 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 */ 52 int 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 */ 81 int 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 */ 107 int 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 */ 131 int 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 */ 153 int 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 165 u32 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 */ 186 int 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 */ 210 int 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) 222 static struct omap_prcm_init_data cm_data __initdata = { 223 .index = TI_CLKM_CM, 224 .init = omap4_cm_init, 225 }; 226 227 static 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 234 static 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 242 static 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) 256 static 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 264 static 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 271 static 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 */ 310 int __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 return ret; 325 326 if (data->index == TI_CLKM_CM) 327 mem = &cm_base; 328 329 if (data->index == TI_CLKM_CM2) 330 mem = &cm2_base; 331 332 data->mem = ioremap(res.start, resource_size(&res)); 333 334 if (mem) { 335 mem->pa = res.start + data->offset; 336 mem->va = data->mem + data->offset; 337 mem->offset = data->offset; 338 } 339 340 data->np = np; 341 342 if (data->init && (data->flags & CM_SINGLE_INSTANCE || 343 (cm_base.va && cm2_base.va))) 344 data->init(data); 345 } 346 347 return 0; 348 } 349 350 /** 351 * omap_cm_init - low level init for the CM drivers 352 * 353 * Initializes the low level clock infrastructure for CM drivers. 354 * Returns 0 in success, negative error value in failure. 355 */ 356 int __init omap_cm_init(void) 357 { 358 struct device_node *np; 359 const struct of_device_id *match; 360 const struct omap_prcm_init_data *data; 361 int ret; 362 363 for_each_matching_node_and_match(np, omap_cm_dt_match_table, &match) { 364 data = match->data; 365 366 if (data->flags & CM_NO_CLOCKS) 367 continue; 368 369 ret = omap2_clk_provider_init(np, data->index, NULL, data->mem); 370 if (ret) 371 return ret; 372 } 373 374 return 0; 375 } 376