Mark Salter | 81ec988 | 2011-10-04 11:10:50 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Port on Texas Instruments TMS320C6x architecture |
| 3 | * |
| 4 | * Copyright (C) 2011 Texas Instruments Incorporated |
| 5 | * Author: Mark Salter <msalter@redhat.com> |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License version 2 as |
| 9 | * published by the Free Software Foundation. |
| 10 | */ |
| 11 | #include <linux/kernel.h> |
| 12 | #include <linux/delay.h> |
| 13 | #include <linux/errno.h> |
| 14 | #include <linux/string.h> |
| 15 | #include <linux/ioport.h> |
| 16 | #include <linux/clkdev.h> |
| 17 | #include <linux/of.h> |
| 18 | #include <linux/of_address.h> |
| 19 | |
| 20 | #include <asm/clock.h> |
| 21 | #include <asm/setup.h> |
| 22 | #include <asm/irq.h> |
| 23 | |
| 24 | /* |
| 25 | * Common SoC clock support. |
| 26 | */ |
| 27 | |
| 28 | /* Default input for PLL1 */ |
| 29 | struct clk clkin1 = { |
| 30 | .name = "clkin1", |
| 31 | .node = LIST_HEAD_INIT(clkin1.node), |
| 32 | .children = LIST_HEAD_INIT(clkin1.children), |
| 33 | .childnode = LIST_HEAD_INIT(clkin1.childnode), |
| 34 | }; |
| 35 | |
| 36 | struct pll_data c6x_soc_pll1 = { |
| 37 | .num = 1, |
| 38 | .sysclks = { |
| 39 | { |
| 40 | .name = "pll1", |
| 41 | .parent = &clkin1, |
| 42 | .pll_data = &c6x_soc_pll1, |
| 43 | .flags = CLK_PLL, |
| 44 | }, |
| 45 | { |
| 46 | .name = "pll1_sysclk1", |
| 47 | .parent = &c6x_soc_pll1.sysclks[0], |
| 48 | .flags = CLK_PLL, |
| 49 | }, |
| 50 | { |
| 51 | .name = "pll1_sysclk2", |
| 52 | .parent = &c6x_soc_pll1.sysclks[0], |
| 53 | .flags = CLK_PLL, |
| 54 | }, |
| 55 | { |
| 56 | .name = "pll1_sysclk3", |
| 57 | .parent = &c6x_soc_pll1.sysclks[0], |
| 58 | .flags = CLK_PLL, |
| 59 | }, |
| 60 | { |
| 61 | .name = "pll1_sysclk4", |
| 62 | .parent = &c6x_soc_pll1.sysclks[0], |
| 63 | .flags = CLK_PLL, |
| 64 | }, |
| 65 | { |
| 66 | .name = "pll1_sysclk5", |
| 67 | .parent = &c6x_soc_pll1.sysclks[0], |
| 68 | .flags = CLK_PLL, |
| 69 | }, |
| 70 | { |
| 71 | .name = "pll1_sysclk6", |
| 72 | .parent = &c6x_soc_pll1.sysclks[0], |
| 73 | .flags = CLK_PLL, |
| 74 | }, |
| 75 | { |
| 76 | .name = "pll1_sysclk7", |
| 77 | .parent = &c6x_soc_pll1.sysclks[0], |
| 78 | .flags = CLK_PLL, |
| 79 | }, |
| 80 | { |
| 81 | .name = "pll1_sysclk8", |
| 82 | .parent = &c6x_soc_pll1.sysclks[0], |
| 83 | .flags = CLK_PLL, |
| 84 | }, |
| 85 | { |
| 86 | .name = "pll1_sysclk9", |
| 87 | .parent = &c6x_soc_pll1.sysclks[0], |
| 88 | .flags = CLK_PLL, |
| 89 | }, |
| 90 | { |
| 91 | .name = "pll1_sysclk10", |
| 92 | .parent = &c6x_soc_pll1.sysclks[0], |
| 93 | .flags = CLK_PLL, |
| 94 | }, |
| 95 | { |
| 96 | .name = "pll1_sysclk11", |
| 97 | .parent = &c6x_soc_pll1.sysclks[0], |
| 98 | .flags = CLK_PLL, |
| 99 | }, |
| 100 | { |
| 101 | .name = "pll1_sysclk12", |
| 102 | .parent = &c6x_soc_pll1.sysclks[0], |
| 103 | .flags = CLK_PLL, |
| 104 | }, |
| 105 | { |
| 106 | .name = "pll1_sysclk13", |
| 107 | .parent = &c6x_soc_pll1.sysclks[0], |
| 108 | .flags = CLK_PLL, |
| 109 | }, |
| 110 | { |
| 111 | .name = "pll1_sysclk14", |
| 112 | .parent = &c6x_soc_pll1.sysclks[0], |
| 113 | .flags = CLK_PLL, |
| 114 | }, |
| 115 | { |
| 116 | .name = "pll1_sysclk15", |
| 117 | .parent = &c6x_soc_pll1.sysclks[0], |
| 118 | .flags = CLK_PLL, |
| 119 | }, |
| 120 | { |
| 121 | .name = "pll1_sysclk16", |
| 122 | .parent = &c6x_soc_pll1.sysclks[0], |
| 123 | .flags = CLK_PLL, |
| 124 | }, |
| 125 | }, |
| 126 | }; |
| 127 | |
| 128 | /* CPU core clock */ |
| 129 | struct clk c6x_core_clk = { |
| 130 | .name = "core", |
| 131 | }; |
| 132 | |
| 133 | /* miscellaneous IO clocks */ |
| 134 | struct clk c6x_i2c_clk = { |
| 135 | .name = "i2c", |
| 136 | }; |
| 137 | |
| 138 | struct clk c6x_watchdog_clk = { |
| 139 | .name = "watchdog", |
| 140 | }; |
| 141 | |
| 142 | struct clk c6x_mcbsp1_clk = { |
| 143 | .name = "mcbsp1", |
| 144 | }; |
| 145 | |
| 146 | struct clk c6x_mcbsp2_clk = { |
| 147 | .name = "mcbsp2", |
| 148 | }; |
| 149 | |
| 150 | struct clk c6x_mdio_clk = { |
| 151 | .name = "mdio", |
| 152 | }; |
| 153 | |
| 154 | |
| 155 | #ifdef CONFIG_SOC_TMS320C6455 |
| 156 | static struct clk_lookup c6455_clks[] = { |
| 157 | CLK(NULL, "pll1", &c6x_soc_pll1.sysclks[0]), |
| 158 | CLK(NULL, "pll1_sysclk2", &c6x_soc_pll1.sysclks[2]), |
| 159 | CLK(NULL, "pll1_sysclk3", &c6x_soc_pll1.sysclks[3]), |
| 160 | CLK(NULL, "pll1_sysclk4", &c6x_soc_pll1.sysclks[4]), |
| 161 | CLK(NULL, "pll1_sysclk5", &c6x_soc_pll1.sysclks[5]), |
| 162 | CLK(NULL, "core", &c6x_core_clk), |
| 163 | CLK("i2c_davinci.1", NULL, &c6x_i2c_clk), |
| 164 | CLK("watchdog", NULL, &c6x_watchdog_clk), |
| 165 | CLK("2c81800.mdio", NULL, &c6x_mdio_clk), |
| 166 | CLK("", NULL, NULL) |
| 167 | }; |
| 168 | |
| 169 | |
| 170 | static void __init c6455_setup_clocks(struct device_node *node) |
| 171 | { |
| 172 | struct pll_data *pll = &c6x_soc_pll1; |
| 173 | struct clk *sysclks = pll->sysclks; |
| 174 | |
| 175 | pll->flags = PLL_HAS_PRE | PLL_HAS_MUL; |
| 176 | |
| 177 | sysclks[2].flags |= FIXED_DIV_PLL; |
| 178 | sysclks[2].div = 3; |
| 179 | sysclks[3].flags |= FIXED_DIV_PLL; |
| 180 | sysclks[3].div = 6; |
| 181 | sysclks[4].div = PLLDIV4; |
| 182 | sysclks[5].div = PLLDIV5; |
| 183 | |
| 184 | c6x_core_clk.parent = &sysclks[0]; |
| 185 | c6x_i2c_clk.parent = &sysclks[3]; |
| 186 | c6x_watchdog_clk.parent = &sysclks[3]; |
| 187 | c6x_mdio_clk.parent = &sysclks[3]; |
| 188 | |
| 189 | c6x_clks_init(c6455_clks); |
| 190 | } |
| 191 | #endif /* CONFIG_SOC_TMS320C6455 */ |
| 192 | |
| 193 | #ifdef CONFIG_SOC_TMS320C6457 |
| 194 | static struct clk_lookup c6457_clks[] = { |
| 195 | CLK(NULL, "pll1", &c6x_soc_pll1.sysclks[0]), |
| 196 | CLK(NULL, "pll1_sysclk1", &c6x_soc_pll1.sysclks[1]), |
| 197 | CLK(NULL, "pll1_sysclk2", &c6x_soc_pll1.sysclks[2]), |
| 198 | CLK(NULL, "pll1_sysclk3", &c6x_soc_pll1.sysclks[3]), |
| 199 | CLK(NULL, "pll1_sysclk4", &c6x_soc_pll1.sysclks[4]), |
| 200 | CLK(NULL, "pll1_sysclk5", &c6x_soc_pll1.sysclks[5]), |
| 201 | CLK(NULL, "core", &c6x_core_clk), |
| 202 | CLK("i2c_davinci.1", NULL, &c6x_i2c_clk), |
| 203 | CLK("watchdog", NULL, &c6x_watchdog_clk), |
| 204 | CLK("2c81800.mdio", NULL, &c6x_mdio_clk), |
| 205 | CLK("", NULL, NULL) |
| 206 | }; |
| 207 | |
| 208 | static void __init c6457_setup_clocks(struct device_node *node) |
| 209 | { |
| 210 | struct pll_data *pll = &c6x_soc_pll1; |
| 211 | struct clk *sysclks = pll->sysclks; |
| 212 | |
| 213 | pll->flags = PLL_HAS_MUL | PLL_HAS_POST; |
| 214 | |
| 215 | sysclks[1].flags |= FIXED_DIV_PLL; |
| 216 | sysclks[1].div = 1; |
| 217 | sysclks[2].flags |= FIXED_DIV_PLL; |
| 218 | sysclks[2].div = 3; |
| 219 | sysclks[3].flags |= FIXED_DIV_PLL; |
| 220 | sysclks[3].div = 6; |
| 221 | sysclks[4].div = PLLDIV4; |
| 222 | sysclks[5].div = PLLDIV5; |
| 223 | |
| 224 | c6x_core_clk.parent = &sysclks[1]; |
| 225 | c6x_i2c_clk.parent = &sysclks[3]; |
| 226 | c6x_watchdog_clk.parent = &sysclks[5]; |
| 227 | c6x_mdio_clk.parent = &sysclks[5]; |
| 228 | |
| 229 | c6x_clks_init(c6457_clks); |
| 230 | } |
| 231 | #endif /* CONFIG_SOC_TMS320C6455 */ |
| 232 | |
| 233 | #ifdef CONFIG_SOC_TMS320C6472 |
| 234 | static struct clk_lookup c6472_clks[] = { |
| 235 | CLK(NULL, "pll1", &c6x_soc_pll1.sysclks[0]), |
| 236 | CLK(NULL, "pll1_sysclk1", &c6x_soc_pll1.sysclks[1]), |
| 237 | CLK(NULL, "pll1_sysclk2", &c6x_soc_pll1.sysclks[2]), |
| 238 | CLK(NULL, "pll1_sysclk3", &c6x_soc_pll1.sysclks[3]), |
| 239 | CLK(NULL, "pll1_sysclk4", &c6x_soc_pll1.sysclks[4]), |
| 240 | CLK(NULL, "pll1_sysclk5", &c6x_soc_pll1.sysclks[5]), |
| 241 | CLK(NULL, "pll1_sysclk6", &c6x_soc_pll1.sysclks[6]), |
| 242 | CLK(NULL, "pll1_sysclk7", &c6x_soc_pll1.sysclks[7]), |
| 243 | CLK(NULL, "pll1_sysclk8", &c6x_soc_pll1.sysclks[8]), |
| 244 | CLK(NULL, "pll1_sysclk9", &c6x_soc_pll1.sysclks[9]), |
| 245 | CLK(NULL, "pll1_sysclk10", &c6x_soc_pll1.sysclks[10]), |
| 246 | CLK(NULL, "core", &c6x_core_clk), |
| 247 | CLK("i2c_davinci.1", NULL, &c6x_i2c_clk), |
| 248 | CLK("watchdog", NULL, &c6x_watchdog_clk), |
| 249 | CLK("2c81800.mdio", NULL, &c6x_mdio_clk), |
| 250 | CLK("", NULL, NULL) |
| 251 | }; |
| 252 | |
| 253 | /* assumptions used for delay loop calculations */ |
| 254 | #define MIN_CLKIN1_KHz 15625 |
| 255 | #define MAX_CORE_KHz 700000 |
| 256 | #define MIN_PLLOUT_KHz MIN_CLKIN1_KHz |
| 257 | |
| 258 | static void __init c6472_setup_clocks(struct device_node *node) |
| 259 | { |
| 260 | struct pll_data *pll = &c6x_soc_pll1; |
| 261 | struct clk *sysclks = pll->sysclks; |
| 262 | int i; |
| 263 | |
| 264 | pll->flags = PLL_HAS_MUL; |
| 265 | |
| 266 | for (i = 1; i <= 6; i++) { |
| 267 | sysclks[i].flags |= FIXED_DIV_PLL; |
| 268 | sysclks[i].div = 1; |
| 269 | } |
| 270 | |
| 271 | sysclks[7].flags |= FIXED_DIV_PLL; |
| 272 | sysclks[7].div = 3; |
| 273 | sysclks[8].flags |= FIXED_DIV_PLL; |
| 274 | sysclks[8].div = 6; |
| 275 | sysclks[9].flags |= FIXED_DIV_PLL; |
| 276 | sysclks[9].div = 2; |
| 277 | sysclks[10].div = PLLDIV10; |
| 278 | |
| 279 | c6x_core_clk.parent = &sysclks[get_coreid() + 1]; |
| 280 | c6x_i2c_clk.parent = &sysclks[8]; |
| 281 | c6x_watchdog_clk.parent = &sysclks[8]; |
| 282 | c6x_mdio_clk.parent = &sysclks[5]; |
| 283 | |
| 284 | c6x_clks_init(c6472_clks); |
| 285 | } |
| 286 | #endif /* CONFIG_SOC_TMS320C6472 */ |
| 287 | |
| 288 | |
| 289 | #ifdef CONFIG_SOC_TMS320C6474 |
| 290 | static struct clk_lookup c6474_clks[] = { |
| 291 | CLK(NULL, "pll1", &c6x_soc_pll1.sysclks[0]), |
| 292 | CLK(NULL, "pll1_sysclk7", &c6x_soc_pll1.sysclks[7]), |
| 293 | CLK(NULL, "pll1_sysclk9", &c6x_soc_pll1.sysclks[9]), |
| 294 | CLK(NULL, "pll1_sysclk10", &c6x_soc_pll1.sysclks[10]), |
| 295 | CLK(NULL, "pll1_sysclk11", &c6x_soc_pll1.sysclks[11]), |
| 296 | CLK(NULL, "pll1_sysclk12", &c6x_soc_pll1.sysclks[12]), |
| 297 | CLK(NULL, "pll1_sysclk13", &c6x_soc_pll1.sysclks[13]), |
| 298 | CLK(NULL, "core", &c6x_core_clk), |
| 299 | CLK("i2c_davinci.1", NULL, &c6x_i2c_clk), |
| 300 | CLK("mcbsp.1", NULL, &c6x_mcbsp1_clk), |
| 301 | CLK("mcbsp.2", NULL, &c6x_mcbsp2_clk), |
| 302 | CLK("watchdog", NULL, &c6x_watchdog_clk), |
| 303 | CLK("2c81800.mdio", NULL, &c6x_mdio_clk), |
| 304 | CLK("", NULL, NULL) |
| 305 | }; |
| 306 | |
| 307 | static void __init c6474_setup_clocks(struct device_node *node) |
| 308 | { |
| 309 | struct pll_data *pll = &c6x_soc_pll1; |
| 310 | struct clk *sysclks = pll->sysclks; |
| 311 | |
| 312 | pll->flags = PLL_HAS_MUL; |
| 313 | |
| 314 | sysclks[7].flags |= FIXED_DIV_PLL; |
| 315 | sysclks[7].div = 1; |
| 316 | sysclks[9].flags |= FIXED_DIV_PLL; |
| 317 | sysclks[9].div = 3; |
| 318 | sysclks[10].flags |= FIXED_DIV_PLL; |
| 319 | sysclks[10].div = 6; |
| 320 | |
| 321 | sysclks[11].div = PLLDIV11; |
| 322 | |
| 323 | sysclks[12].flags |= FIXED_DIV_PLL; |
| 324 | sysclks[12].div = 2; |
| 325 | |
| 326 | sysclks[13].div = PLLDIV13; |
| 327 | |
| 328 | c6x_core_clk.parent = &sysclks[7]; |
| 329 | c6x_i2c_clk.parent = &sysclks[10]; |
| 330 | c6x_watchdog_clk.parent = &sysclks[10]; |
| 331 | c6x_mcbsp1_clk.parent = &sysclks[10]; |
| 332 | c6x_mcbsp2_clk.parent = &sysclks[10]; |
| 333 | |
| 334 | c6x_clks_init(c6474_clks); |
| 335 | } |
| 336 | #endif /* CONFIG_SOC_TMS320C6474 */ |
| 337 | |
| 338 | static struct of_device_id c6x_clkc_match[] __initdata = { |
| 339 | #ifdef CONFIG_SOC_TMS320C6455 |
| 340 | { .compatible = "ti,c6455-pll", .data = c6455_setup_clocks }, |
| 341 | #endif |
| 342 | #ifdef CONFIG_SOC_TMS320C6457 |
| 343 | { .compatible = "ti,c6457-pll", .data = c6457_setup_clocks }, |
| 344 | #endif |
| 345 | #ifdef CONFIG_SOC_TMS320C6472 |
| 346 | { .compatible = "ti,c6472-pll", .data = c6472_setup_clocks }, |
| 347 | #endif |
| 348 | #ifdef CONFIG_SOC_TMS320C6474 |
| 349 | { .compatible = "ti,c6474-pll", .data = c6474_setup_clocks }, |
| 350 | #endif |
| 351 | { .compatible = "ti,c64x+pll" }, |
| 352 | {} |
| 353 | }; |
| 354 | |
| 355 | void __init c64x_setup_clocks(void) |
| 356 | { |
| 357 | void (*__setup_clocks)(struct device_node *np); |
| 358 | struct pll_data *pll = &c6x_soc_pll1; |
| 359 | struct device_node *node; |
| 360 | const struct of_device_id *id; |
| 361 | int err; |
| 362 | u32 val; |
| 363 | |
| 364 | node = of_find_matching_node(NULL, c6x_clkc_match); |
| 365 | if (!node) |
| 366 | return; |
| 367 | |
| 368 | pll->base = of_iomap(node, 0); |
| 369 | if (!pll->base) |
| 370 | goto out; |
| 371 | |
| 372 | err = of_property_read_u32(node, "clock-frequency", &val); |
| 373 | if (err || val == 0) { |
| 374 | pr_err("%s: no clock-frequency found! Using %dMHz\n", |
| 375 | node->full_name, (int)val / 1000000); |
| 376 | val = 25000000; |
| 377 | } |
| 378 | clkin1.rate = val; |
| 379 | |
| 380 | err = of_property_read_u32(node, "ti,c64x+pll-bypass-delay", &val); |
| 381 | if (err) |
| 382 | val = 5000; |
| 383 | pll->bypass_delay = val; |
| 384 | |
| 385 | err = of_property_read_u32(node, "ti,c64x+pll-reset-delay", &val); |
| 386 | if (err) |
| 387 | val = 30000; |
| 388 | pll->reset_delay = val; |
| 389 | |
| 390 | err = of_property_read_u32(node, "ti,c64x+pll-lock-delay", &val); |
| 391 | if (err) |
| 392 | val = 30000; |
| 393 | pll->lock_delay = val; |
| 394 | |
| 395 | /* id->data is a pointer to SoC-specific setup */ |
| 396 | id = of_match_node(c6x_clkc_match, node); |
| 397 | if (id && id->data) { |
| 398 | __setup_clocks = id->data; |
| 399 | __setup_clocks(node); |
| 400 | } |
| 401 | |
| 402 | out: |
| 403 | of_node_put(node); |
| 404 | } |