clk-3xxx.c (12244B)
1/* 2 * OMAP3 Clock init 3 * 4 * Copyright (C) 2013 Texas Instruments, Inc 5 * Tero Kristo (t-kristo@ti.com) 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation version 2. 10 * 11 * This program is distributed "as is" WITHOUT ANY WARRANTY of any 12 * kind, whether express or implied; without even the implied warranty 13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 */ 16 17#include <linux/kernel.h> 18#include <linux/list.h> 19#include <linux/clk.h> 20#include <linux/clk-provider.h> 21#include <linux/clk/ti.h> 22 23#include "clock.h" 24 25#define OMAP3430ES2_ST_DSS_IDLE_SHIFT 1 26#define OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT 5 27#define OMAP3430ES2_ST_SSI_IDLE_SHIFT 8 28 29#define OMAP34XX_CM_IDLEST_VAL 1 30 31/* 32 * In AM35xx IPSS, the {ICK,FCK} enable bits for modules are exported 33 * in the same register at a bit offset of 0x8. The EN_ACK for ICK is 34 * at an offset of 4 from ICK enable bit. 35 */ 36#define AM35XX_IPSS_ICK_MASK 0xF 37#define AM35XX_IPSS_ICK_EN_ACK_OFFSET 0x4 38#define AM35XX_IPSS_ICK_FCK_OFFSET 0x8 39#define AM35XX_IPSS_CLK_IDLEST_VAL 0 40 41#define AM35XX_ST_IPSS_SHIFT 5 42 43/** 44 * omap3430es2_clk_ssi_find_idlest - return CM_IDLEST info for SSI 45 * @clk: struct clk * being enabled 46 * @idlest_reg: void __iomem ** to store CM_IDLEST reg address into 47 * @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into 48 * @idlest_val: pointer to a u8 to store the CM_IDLEST indicator 49 * 50 * The OMAP3430ES2 SSI target CM_IDLEST bit is at a different shift 51 * from the CM_{I,F}CLKEN bit. Pass back the correct info via 52 * @idlest_reg and @idlest_bit. No return value. 53 */ 54static void omap3430es2_clk_ssi_find_idlest(struct clk_hw_omap *clk, 55 struct clk_omap_reg *idlest_reg, 56 u8 *idlest_bit, 57 u8 *idlest_val) 58{ 59 memcpy(idlest_reg, &clk->enable_reg, sizeof(*idlest_reg)); 60 idlest_reg->offset &= ~0xf0; 61 idlest_reg->offset |= 0x20; 62 *idlest_bit = OMAP3430ES2_ST_SSI_IDLE_SHIFT; 63 *idlest_val = OMAP34XX_CM_IDLEST_VAL; 64} 65 66const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_ssi_wait = { 67 .allow_idle = omap2_clkt_iclk_allow_idle, 68 .deny_idle = omap2_clkt_iclk_deny_idle, 69 .find_idlest = omap3430es2_clk_ssi_find_idlest, 70 .find_companion = omap2_clk_dflt_find_companion, 71}; 72 73/** 74 * omap3430es2_clk_dss_usbhost_find_idlest - CM_IDLEST info for DSS, USBHOST 75 * @clk: struct clk * being enabled 76 * @idlest_reg: void __iomem ** to store CM_IDLEST reg address into 77 * @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into 78 * @idlest_val: pointer to a u8 to store the CM_IDLEST indicator 79 * 80 * Some OMAP modules on OMAP3 ES2+ chips have both initiator and 81 * target IDLEST bits. For our purposes, we are concerned with the 82 * target IDLEST bits, which exist at a different bit position than 83 * the *CLKEN bit position for these modules (DSS and USBHOST) (The 84 * default find_idlest code assumes that they are at the same 85 * position.) No return value. 86 */ 87static void 88omap3430es2_clk_dss_usbhost_find_idlest(struct clk_hw_omap *clk, 89 struct clk_omap_reg *idlest_reg, 90 u8 *idlest_bit, u8 *idlest_val) 91{ 92 memcpy(idlest_reg, &clk->enable_reg, sizeof(*idlest_reg)); 93 94 idlest_reg->offset &= ~0xf0; 95 idlest_reg->offset |= 0x20; 96 /* USBHOST_IDLE has same shift */ 97 *idlest_bit = OMAP3430ES2_ST_DSS_IDLE_SHIFT; 98 *idlest_val = OMAP34XX_CM_IDLEST_VAL; 99} 100 101const struct clk_hw_omap_ops clkhwops_omap3430es2_dss_usbhost_wait = { 102 .find_idlest = omap3430es2_clk_dss_usbhost_find_idlest, 103 .find_companion = omap2_clk_dflt_find_companion, 104}; 105 106const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_dss_usbhost_wait = { 107 .allow_idle = omap2_clkt_iclk_allow_idle, 108 .deny_idle = omap2_clkt_iclk_deny_idle, 109 .find_idlest = omap3430es2_clk_dss_usbhost_find_idlest, 110 .find_companion = omap2_clk_dflt_find_companion, 111}; 112 113/** 114 * omap3430es2_clk_hsotgusb_find_idlest - return CM_IDLEST info for HSOTGUSB 115 * @clk: struct clk * being enabled 116 * @idlest_reg: void __iomem ** to store CM_IDLEST reg address into 117 * @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into 118 * @idlest_val: pointer to a u8 to store the CM_IDLEST indicator 119 * 120 * The OMAP3430ES2 HSOTGUSB target CM_IDLEST bit is at a different 121 * shift from the CM_{I,F}CLKEN bit. Pass back the correct info via 122 * @idlest_reg and @idlest_bit. No return value. 123 */ 124static void 125omap3430es2_clk_hsotgusb_find_idlest(struct clk_hw_omap *clk, 126 struct clk_omap_reg *idlest_reg, 127 u8 *idlest_bit, 128 u8 *idlest_val) 129{ 130 memcpy(idlest_reg, &clk->enable_reg, sizeof(*idlest_reg)); 131 idlest_reg->offset &= ~0xf0; 132 idlest_reg->offset |= 0x20; 133 *idlest_bit = OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT; 134 *idlest_val = OMAP34XX_CM_IDLEST_VAL; 135} 136 137const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_hsotgusb_wait = { 138 .allow_idle = omap2_clkt_iclk_allow_idle, 139 .deny_idle = omap2_clkt_iclk_deny_idle, 140 .find_idlest = omap3430es2_clk_hsotgusb_find_idlest, 141 .find_companion = omap2_clk_dflt_find_companion, 142}; 143 144/** 145 * am35xx_clk_find_idlest - return clock ACK info for AM35XX IPSS 146 * @clk: struct clk * being enabled 147 * @idlest_reg: void __iomem ** to store CM_IDLEST reg address into 148 * @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into 149 * @idlest_val: pointer to a u8 to store the CM_IDLEST indicator 150 * 151 * The interface clocks on AM35xx IPSS reflects the clock idle status 152 * in the enable register itsel at a bit offset of 4 from the enable 153 * bit. A value of 1 indicates that clock is enabled. 154 */ 155static void am35xx_clk_find_idlest(struct clk_hw_omap *clk, 156 struct clk_omap_reg *idlest_reg, 157 u8 *idlest_bit, 158 u8 *idlest_val) 159{ 160 memcpy(idlest_reg, &clk->enable_reg, sizeof(*idlest_reg)); 161 *idlest_bit = clk->enable_bit + AM35XX_IPSS_ICK_EN_ACK_OFFSET; 162 *idlest_val = AM35XX_IPSS_CLK_IDLEST_VAL; 163} 164 165/** 166 * am35xx_clk_find_companion - find companion clock to @clk 167 * @clk: struct clk * to find the companion clock of 168 * @other_reg: void __iomem ** to return the companion clock CM_*CLKEN va in 169 * @other_bit: u8 ** to return the companion clock bit shift in 170 * 171 * Some clocks don't have companion clocks. For example, modules with 172 * only an interface clock (such as HECC) don't have a companion 173 * clock. Right now, this code relies on the hardware exporting a bit 174 * in the correct companion register that indicates that the 175 * nonexistent 'companion clock' is active. Future patches will 176 * associate this type of code with per-module data structures to 177 * avoid this issue, and remove the casts. No return value. 178 */ 179static void am35xx_clk_find_companion(struct clk_hw_omap *clk, 180 struct clk_omap_reg *other_reg, 181 u8 *other_bit) 182{ 183 memcpy(other_reg, &clk->enable_reg, sizeof(*other_reg)); 184 if (clk->enable_bit & AM35XX_IPSS_ICK_MASK) 185 *other_bit = clk->enable_bit + AM35XX_IPSS_ICK_FCK_OFFSET; 186 else 187 *other_bit = clk->enable_bit - AM35XX_IPSS_ICK_FCK_OFFSET; 188} 189 190const struct clk_hw_omap_ops clkhwops_am35xx_ipss_module_wait = { 191 .find_idlest = am35xx_clk_find_idlest, 192 .find_companion = am35xx_clk_find_companion, 193}; 194 195/** 196 * am35xx_clk_ipss_find_idlest - return CM_IDLEST info for IPSS 197 * @clk: struct clk * being enabled 198 * @idlest_reg: void __iomem ** to store CM_IDLEST reg address into 199 * @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into 200 * @idlest_val: pointer to a u8 to store the CM_IDLEST indicator 201 * 202 * The IPSS target CM_IDLEST bit is at a different shift from the 203 * CM_{I,F}CLKEN bit. Pass back the correct info via @idlest_reg 204 * and @idlest_bit. No return value. 205 */ 206static void am35xx_clk_ipss_find_idlest(struct clk_hw_omap *clk, 207 struct clk_omap_reg *idlest_reg, 208 u8 *idlest_bit, 209 u8 *idlest_val) 210{ 211 memcpy(idlest_reg, &clk->enable_reg, sizeof(*idlest_reg)); 212 213 idlest_reg->offset &= ~0xf0; 214 idlest_reg->offset |= 0x20; 215 *idlest_bit = AM35XX_ST_IPSS_SHIFT; 216 *idlest_val = OMAP34XX_CM_IDLEST_VAL; 217} 218 219const struct clk_hw_omap_ops clkhwops_am35xx_ipss_wait = { 220 .allow_idle = omap2_clkt_iclk_allow_idle, 221 .deny_idle = omap2_clkt_iclk_deny_idle, 222 .find_idlest = am35xx_clk_ipss_find_idlest, 223 .find_companion = omap2_clk_dflt_find_companion, 224}; 225 226static struct ti_dt_clk omap3xxx_clks[] = { 227 DT_CLK(NULL, "timer_32k_ck", "omap_32k_fck"), 228 DT_CLK(NULL, "timer_sys_ck", "sys_ck"), 229 { .node_name = NULL }, 230}; 231 232static struct ti_dt_clk omap36xx_omap3430es2plus_clks[] = { 233 DT_CLK(NULL, "ssi_ssr_fck", "ssi_ssr_fck_3430es2"), 234 DT_CLK(NULL, "ssi_sst_fck", "ssi_sst_fck_3430es2"), 235 DT_CLK(NULL, "hsotgusb_ick", "hsotgusb_ick_3430es2"), 236 DT_CLK(NULL, "ssi_ick", "ssi_ick_3430es2"), 237 { .node_name = NULL }, 238}; 239 240static struct ti_dt_clk omap3430es1_clks[] = { 241 DT_CLK(NULL, "ssi_ssr_fck", "ssi_ssr_fck_3430es1"), 242 DT_CLK(NULL, "ssi_sst_fck", "ssi_sst_fck_3430es1"), 243 DT_CLK(NULL, "hsotgusb_ick", "hsotgusb_ick_3430es1"), 244 DT_CLK(NULL, "ssi_ick", "ssi_ick_3430es1"), 245 DT_CLK(NULL, "dss1_alwon_fck", "dss1_alwon_fck_3430es1"), 246 DT_CLK(NULL, "dss_ick", "dss_ick_3430es1"), 247 { .node_name = NULL }, 248}; 249 250static struct ti_dt_clk omap36xx_am35xx_omap3430es2plus_clks[] = { 251 DT_CLK(NULL, "dss1_alwon_fck", "dss1_alwon_fck_3430es2"), 252 DT_CLK(NULL, "dss_ick", "dss_ick_3430es2"), 253 { .node_name = NULL }, 254}; 255 256static struct ti_dt_clk am35xx_clks[] = { 257 DT_CLK(NULL, "hsotgusb_ick", "hsotgusb_ick_am35xx"), 258 DT_CLK(NULL, "hsotgusb_fck", "hsotgusb_fck_am35xx"), 259 DT_CLK(NULL, "uart4_ick", "uart4_ick_am35xx"), 260 DT_CLK(NULL, "uart4_fck", "uart4_fck_am35xx"), 261 { .node_name = NULL }, 262}; 263 264static const char *enable_init_clks[] = { 265 "sdrc_ick", 266 "gpmc_fck", 267 "omapctrl_ick", 268}; 269 270enum { 271 OMAP3_SOC_AM35XX, 272 OMAP3_SOC_OMAP3430_ES1, 273 OMAP3_SOC_OMAP3430_ES2_PLUS, 274 OMAP3_SOC_OMAP3630, 275}; 276 277/** 278 * omap3_clk_lock_dpll5 - locks DPLL5 279 * 280 * Locks DPLL5 to a pre-defined frequency. This is required for proper 281 * operation of USB. 282 */ 283void __init omap3_clk_lock_dpll5(void) 284{ 285 struct clk *dpll5_clk; 286 struct clk *dpll5_m2_clk; 287 288 /* 289 * Errata sprz319f advisory 2.1 documents a USB host clock drift issue 290 * that can be worked around using specially crafted dpll5 settings 291 * with a dpll5_m2 divider set to 8. Set the dpll5 rate to 8x the USB 292 * host clock rate, its .set_rate handler() will detect that frequency 293 * and use the errata settings. 294 */ 295 dpll5_clk = clk_get(NULL, "dpll5_ck"); 296 clk_set_rate(dpll5_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST * 8); 297 clk_prepare_enable(dpll5_clk); 298 299 /* Program dpll5_m2_clk divider */ 300 dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck"); 301 clk_prepare_enable(dpll5_m2_clk); 302 clk_set_rate(dpll5_m2_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST); 303 304 clk_disable_unprepare(dpll5_m2_clk); 305 clk_disable_unprepare(dpll5_clk); 306} 307 308static int __init omap3xxx_dt_clk_init(int soc_type) 309{ 310 if (soc_type == OMAP3_SOC_AM35XX || soc_type == OMAP3_SOC_OMAP3630 || 311 soc_type == OMAP3_SOC_OMAP3430_ES1 || 312 soc_type == OMAP3_SOC_OMAP3430_ES2_PLUS) 313 ti_dt_clocks_register(omap3xxx_clks); 314 315 if (soc_type == OMAP3_SOC_AM35XX) 316 ti_dt_clocks_register(am35xx_clks); 317 318 if (soc_type == OMAP3_SOC_OMAP3630 || soc_type == OMAP3_SOC_AM35XX || 319 soc_type == OMAP3_SOC_OMAP3430_ES2_PLUS) 320 ti_dt_clocks_register(omap36xx_am35xx_omap3430es2plus_clks); 321 322 if (soc_type == OMAP3_SOC_OMAP3430_ES1) 323 ti_dt_clocks_register(omap3430es1_clks); 324 325 if (soc_type == OMAP3_SOC_OMAP3430_ES2_PLUS || 326 soc_type == OMAP3_SOC_OMAP3630) 327 ti_dt_clocks_register(omap36xx_omap3430es2plus_clks); 328 329 omap2_clk_disable_autoidle_all(); 330 331 ti_clk_add_aliases(); 332 333 omap2_clk_enable_init_clocks(enable_init_clks, 334 ARRAY_SIZE(enable_init_clks)); 335 336 pr_info("Clocking rate (Crystal/Core/MPU): %ld.%01ld/%ld/%ld MHz\n", 337 (clk_get_rate(clk_get_sys(NULL, "osc_sys_ck")) / 1000000), 338 (clk_get_rate(clk_get_sys(NULL, "osc_sys_ck")) / 100000) % 10, 339 (clk_get_rate(clk_get_sys(NULL, "core_ck")) / 1000000), 340 (clk_get_rate(clk_get_sys(NULL, "arm_fck")) / 1000000)); 341 342 if (soc_type != OMAP3_SOC_OMAP3430_ES1) 343 omap3_clk_lock_dpll5(); 344 345 return 0; 346} 347 348int __init omap3430_dt_clk_init(void) 349{ 350 return omap3xxx_dt_clk_init(OMAP3_SOC_OMAP3430_ES2_PLUS); 351} 352 353int __init omap3630_dt_clk_init(void) 354{ 355 return omap3xxx_dt_clk_init(OMAP3_SOC_OMAP3630); 356} 357 358int __init am35xx_dt_clk_init(void) 359{ 360 return omap3xxx_dt_clk_init(OMAP3_SOC_AM35XX); 361}