| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame^] | 1 | /* | 
|  | 2 | * Nomadik clock implementation | 
|  | 3 | * Copyright (C) 2013 ST-Ericsson AB | 
|  | 4 | * License terms: GNU General Public License (GPL) version 2 | 
|  | 5 | * Author: Linus Walleij <linus.walleij@linaro.org> | 
|  | 6 | */ | 
|  | 7 |  | 
|  | 8 | #define pr_fmt(fmt) "Nomadik SRC clocks: " fmt | 
|  | 9 |  | 
|  | 10 | #include <linux/bitops.h> | 
|  | 11 | #include <linux/slab.h> | 
|  | 12 | #include <linux/err.h> | 
|  | 13 | #include <linux/io.h> | 
|  | 14 | #include <linux/clk-provider.h> | 
|  | 15 | #include <linux/of.h> | 
|  | 16 | #include <linux/of_address.h> | 
|  | 17 | #include <linux/debugfs.h> | 
|  | 18 | #include <linux/seq_file.h> | 
|  | 19 | #include <linux/spinlock.h> | 
|  | 20 | #include <linux/reboot.h> | 
|  | 21 |  | 
|  | 22 | /* | 
|  | 23 | * The Nomadik clock tree is described in the STN8815A12 DB V4.2 | 
|  | 24 | * reference manual for the chip, page 94 ff. | 
|  | 25 | * Clock IDs are in the STn8815 Reference Manual table 3, page 27. | 
|  | 26 | */ | 
|  | 27 |  | 
|  | 28 | #define SRC_CR			0x00U | 
|  | 29 | #define SRC_CR_T0_ENSEL		BIT(15) | 
|  | 30 | #define SRC_CR_T1_ENSEL		BIT(17) | 
|  | 31 | #define SRC_CR_T2_ENSEL		BIT(19) | 
|  | 32 | #define SRC_CR_T3_ENSEL		BIT(21) | 
|  | 33 | #define SRC_CR_T4_ENSEL		BIT(23) | 
|  | 34 | #define SRC_CR_T5_ENSEL		BIT(25) | 
|  | 35 | #define SRC_CR_T6_ENSEL		BIT(27) | 
|  | 36 | #define SRC_CR_T7_ENSEL		BIT(29) | 
|  | 37 | #define SRC_XTALCR		0x0CU | 
|  | 38 | #define SRC_XTALCR_XTALTIMEN	BIT(20) | 
|  | 39 | #define SRC_XTALCR_SXTALDIS	BIT(19) | 
|  | 40 | #define SRC_XTALCR_MXTALSTAT	BIT(2) | 
|  | 41 | #define SRC_XTALCR_MXTALEN	BIT(1) | 
|  | 42 | #define SRC_XTALCR_MXTALOVER	BIT(0) | 
|  | 43 | #define SRC_PLLCR		0x10U | 
|  | 44 | #define SRC_PLLCR_PLLTIMEN	BIT(29) | 
|  | 45 | #define SRC_PLLCR_PLL2EN	BIT(28) | 
|  | 46 | #define SRC_PLLCR_PLL1STAT	BIT(2) | 
|  | 47 | #define SRC_PLLCR_PLL1EN	BIT(1) | 
|  | 48 | #define SRC_PLLCR_PLL1OVER	BIT(0) | 
|  | 49 | #define SRC_PLLFR		0x14U | 
|  | 50 | #define SRC_PCKEN0		0x24U | 
|  | 51 | #define SRC_PCKDIS0		0x28U | 
|  | 52 | #define SRC_PCKENSR0		0x2CU | 
|  | 53 | #define SRC_PCKSR0		0x30U | 
|  | 54 | #define SRC_PCKEN1		0x34U | 
|  | 55 | #define SRC_PCKDIS1		0x38U | 
|  | 56 | #define SRC_PCKENSR1		0x3CU | 
|  | 57 | #define SRC_PCKSR1		0x40U | 
|  | 58 |  | 
|  | 59 | /* Lock protecting the SRC_CR register */ | 
|  | 60 | static DEFINE_SPINLOCK(src_lock); | 
|  | 61 | /* Base address of the SRC */ | 
|  | 62 | static void __iomem *src_base; | 
|  | 63 |  | 
|  | 64 | static int nomadik_clk_reboot_handler(struct notifier_block *this, | 
|  | 65 | unsigned long code, | 
|  | 66 | void *unused) | 
|  | 67 | { | 
|  | 68 | u32 val; | 
|  | 69 |  | 
|  | 70 | /* The main chrystal need to be enabled for reboot to work */ | 
|  | 71 | val = readl(src_base + SRC_XTALCR); | 
|  | 72 | val &= ~SRC_XTALCR_MXTALOVER; | 
|  | 73 | val |= SRC_XTALCR_MXTALEN; | 
|  | 74 | pr_crit("force-enabling MXTALO\n"); | 
|  | 75 | writel(val, src_base + SRC_XTALCR); | 
|  | 76 | return NOTIFY_OK; | 
|  | 77 | } | 
|  | 78 |  | 
|  | 79 | static struct notifier_block nomadik_clk_reboot_notifier = { | 
|  | 80 | .notifier_call = nomadik_clk_reboot_handler, | 
|  | 81 | }; | 
|  | 82 |  | 
|  | 83 | static const struct of_device_id nomadik_src_match[] __initconst = { | 
|  | 84 | { .compatible = "stericsson,nomadik-src" }, | 
|  | 85 | { /* sentinel */ } | 
|  | 86 | }; | 
|  | 87 |  | 
|  | 88 | static void __init nomadik_src_init(void) | 
|  | 89 | { | 
|  | 90 | struct device_node *np; | 
|  | 91 | u32 val; | 
|  | 92 |  | 
|  | 93 | np = of_find_matching_node(NULL, nomadik_src_match); | 
|  | 94 | if (!np) { | 
|  | 95 | pr_crit("no matching node for SRC, aborting clock init\n"); | 
|  | 96 | return; | 
|  | 97 | } | 
|  | 98 | src_base = of_iomap(np, 0); | 
|  | 99 | if (!src_base) { | 
|  | 100 | pr_err("%s: must have src parent node with REGS (%s)\n", | 
|  | 101 | __func__, np->name); | 
|  | 102 | return; | 
|  | 103 | } | 
|  | 104 |  | 
|  | 105 | /* Set all timers to use the 2.4 MHz TIMCLK */ | 
|  | 106 | val = readl(src_base + SRC_CR); | 
|  | 107 | val |= SRC_CR_T0_ENSEL; | 
|  | 108 | val |= SRC_CR_T1_ENSEL; | 
|  | 109 | val |= SRC_CR_T2_ENSEL; | 
|  | 110 | val |= SRC_CR_T3_ENSEL; | 
|  | 111 | val |= SRC_CR_T4_ENSEL; | 
|  | 112 | val |= SRC_CR_T5_ENSEL; | 
|  | 113 | val |= SRC_CR_T6_ENSEL; | 
|  | 114 | val |= SRC_CR_T7_ENSEL; | 
|  | 115 | writel(val, src_base + SRC_CR); | 
|  | 116 |  | 
|  | 117 | val = readl(src_base + SRC_XTALCR); | 
|  | 118 | pr_info("SXTALO is %s\n", | 
|  | 119 | (val & SRC_XTALCR_SXTALDIS) ? "disabled" : "enabled"); | 
|  | 120 | pr_info("MXTAL is %s\n", | 
|  | 121 | (val & SRC_XTALCR_MXTALSTAT) ? "enabled" : "disabled"); | 
|  | 122 | if (of_property_read_bool(np, "disable-sxtalo")) { | 
|  | 123 | /* The machine uses an external oscillator circuit */ | 
|  | 124 | val |= SRC_XTALCR_SXTALDIS; | 
|  | 125 | pr_info("disabling SXTALO\n"); | 
|  | 126 | } | 
|  | 127 | if (of_property_read_bool(np, "disable-mxtalo")) { | 
|  | 128 | /* Disable this too: also run by external oscillator */ | 
|  | 129 | val |= SRC_XTALCR_MXTALOVER; | 
|  | 130 | val &= ~SRC_XTALCR_MXTALEN; | 
|  | 131 | pr_info("disabling MXTALO\n"); | 
|  | 132 | } | 
|  | 133 | writel(val, src_base + SRC_XTALCR); | 
|  | 134 | register_reboot_notifier(&nomadik_clk_reboot_notifier); | 
|  | 135 | } | 
|  | 136 |  | 
|  | 137 | /** | 
|  | 138 | * struct clk_pll1 - Nomadik PLL1 clock | 
|  | 139 | * @hw: corresponding clock hardware entry | 
|  | 140 | * @id: PLL instance: 1 or 2 | 
|  | 141 | */ | 
|  | 142 | struct clk_pll { | 
|  | 143 | struct clk_hw hw; | 
|  | 144 | int id; | 
|  | 145 | }; | 
|  | 146 |  | 
|  | 147 | /** | 
|  | 148 | * struct clk_src - Nomadik src clock | 
|  | 149 | * @hw: corresponding clock hardware entry | 
|  | 150 | * @id: the clock ID | 
|  | 151 | * @group1: true if the clock is in group1, else it is in group0 | 
|  | 152 | * @clkbit: bit 0...31 corresponding to the clock in each clock register | 
|  | 153 | */ | 
|  | 154 | struct clk_src { | 
|  | 155 | struct clk_hw hw; | 
|  | 156 | int id; | 
|  | 157 | bool group1; | 
|  | 158 | u32 clkbit; | 
|  | 159 | }; | 
|  | 160 |  | 
|  | 161 | #define to_pll(_hw) container_of(_hw, struct clk_pll, hw) | 
|  | 162 | #define to_src(_hw) container_of(_hw, struct clk_src, hw) | 
|  | 163 |  | 
|  | 164 | static int pll_clk_enable(struct clk_hw *hw) | 
|  | 165 | { | 
|  | 166 | struct clk_pll *pll = to_pll(hw); | 
|  | 167 | u32 val; | 
|  | 168 |  | 
|  | 169 | spin_lock(&src_lock); | 
|  | 170 | val = readl(src_base + SRC_PLLCR); | 
|  | 171 | if (pll->id == 1) { | 
|  | 172 | if (val & SRC_PLLCR_PLL1OVER) { | 
|  | 173 | val |= SRC_PLLCR_PLL1EN; | 
|  | 174 | writel(val, src_base + SRC_PLLCR); | 
|  | 175 | } | 
|  | 176 | } else if (pll->id == 2) { | 
|  | 177 | val |= SRC_PLLCR_PLL2EN; | 
|  | 178 | writel(val, src_base + SRC_PLLCR); | 
|  | 179 | } | 
|  | 180 | spin_unlock(&src_lock); | 
|  | 181 | return 0; | 
|  | 182 | } | 
|  | 183 |  | 
|  | 184 | static void pll_clk_disable(struct clk_hw *hw) | 
|  | 185 | { | 
|  | 186 | struct clk_pll *pll = to_pll(hw); | 
|  | 187 | u32 val; | 
|  | 188 |  | 
|  | 189 | spin_lock(&src_lock); | 
|  | 190 | val = readl(src_base + SRC_PLLCR); | 
|  | 191 | if (pll->id == 1) { | 
|  | 192 | if (val & SRC_PLLCR_PLL1OVER) { | 
|  | 193 | val &= ~SRC_PLLCR_PLL1EN; | 
|  | 194 | writel(val, src_base + SRC_PLLCR); | 
|  | 195 | } | 
|  | 196 | } else if (pll->id == 2) { | 
|  | 197 | val &= ~SRC_PLLCR_PLL2EN; | 
|  | 198 | writel(val, src_base + SRC_PLLCR); | 
|  | 199 | } | 
|  | 200 | spin_unlock(&src_lock); | 
|  | 201 | } | 
|  | 202 |  | 
|  | 203 | static int pll_clk_is_enabled(struct clk_hw *hw) | 
|  | 204 | { | 
|  | 205 | struct clk_pll *pll = to_pll(hw); | 
|  | 206 | u32 val; | 
|  | 207 |  | 
|  | 208 | val = readl(src_base + SRC_PLLCR); | 
|  | 209 | if (pll->id == 1) { | 
|  | 210 | if (val & SRC_PLLCR_PLL1OVER) | 
|  | 211 | return !!(val & SRC_PLLCR_PLL1EN); | 
|  | 212 | } else if (pll->id == 2) { | 
|  | 213 | return !!(val & SRC_PLLCR_PLL2EN); | 
|  | 214 | } | 
|  | 215 | return 1; | 
|  | 216 | } | 
|  | 217 |  | 
|  | 218 | static unsigned long pll_clk_recalc_rate(struct clk_hw *hw, | 
|  | 219 | unsigned long parent_rate) | 
|  | 220 | { | 
|  | 221 | struct clk_pll *pll = to_pll(hw); | 
|  | 222 | u32 val; | 
|  | 223 |  | 
|  | 224 | val = readl(src_base + SRC_PLLFR); | 
|  | 225 |  | 
|  | 226 | if (pll->id == 1) { | 
|  | 227 | u8 mul; | 
|  | 228 | u8 div; | 
|  | 229 |  | 
|  | 230 | mul = (val >> 8) & 0x3FU; | 
|  | 231 | mul += 2; | 
|  | 232 | div = val & 0x07U; | 
|  | 233 | return (parent_rate * mul) >> div; | 
|  | 234 | } | 
|  | 235 |  | 
|  | 236 | if (pll->id == 2) { | 
|  | 237 | u8 mul; | 
|  | 238 |  | 
|  | 239 | mul = (val >> 24) & 0x3FU; | 
|  | 240 | mul += 2; | 
|  | 241 | return (parent_rate * mul); | 
|  | 242 | } | 
|  | 243 |  | 
|  | 244 | /* Unknown PLL */ | 
|  | 245 | return 0; | 
|  | 246 | } | 
|  | 247 |  | 
|  | 248 |  | 
|  | 249 | static const struct clk_ops pll_clk_ops = { | 
|  | 250 | .enable = pll_clk_enable, | 
|  | 251 | .disable = pll_clk_disable, | 
|  | 252 | .is_enabled = pll_clk_is_enabled, | 
|  | 253 | .recalc_rate = pll_clk_recalc_rate, | 
|  | 254 | }; | 
|  | 255 |  | 
|  | 256 | static struct clk_hw * __init | 
|  | 257 | pll_clk_register(struct device *dev, const char *name, | 
|  | 258 | const char *parent_name, u32 id) | 
|  | 259 | { | 
|  | 260 | int ret; | 
|  | 261 | struct clk_pll *pll; | 
|  | 262 | struct clk_init_data init; | 
|  | 263 |  | 
|  | 264 | if (id != 1 && id != 2) { | 
|  | 265 | pr_err("%s: the Nomadik has only PLL 1 & 2\n", __func__); | 
|  | 266 | return ERR_PTR(-EINVAL); | 
|  | 267 | } | 
|  | 268 |  | 
|  | 269 | pll = kzalloc(sizeof(*pll), GFP_KERNEL); | 
|  | 270 | if (!pll) | 
|  | 271 | return ERR_PTR(-ENOMEM); | 
|  | 272 |  | 
|  | 273 | init.name = name; | 
|  | 274 | init.ops = &pll_clk_ops; | 
|  | 275 | init.parent_names = (parent_name ? &parent_name : NULL); | 
|  | 276 | init.num_parents = (parent_name ? 1 : 0); | 
|  | 277 | pll->hw.init = &init; | 
|  | 278 | pll->id = id; | 
|  | 279 |  | 
|  | 280 | pr_debug("register PLL1 clock \"%s\"\n", name); | 
|  | 281 |  | 
|  | 282 | ret = clk_hw_register(dev, &pll->hw); | 
|  | 283 | if (ret) { | 
|  | 284 | kfree(pll); | 
|  | 285 | return ERR_PTR(ret); | 
|  | 286 | } | 
|  | 287 |  | 
|  | 288 | return &pll->hw; | 
|  | 289 | } | 
|  | 290 |  | 
|  | 291 | /* | 
|  | 292 | * The Nomadik SRC clocks are gated, but not in the sense that | 
|  | 293 | * you read-modify-write a register. Instead there are separate | 
|  | 294 | * clock enable and clock disable registers. Writing a '1' bit in | 
|  | 295 | * the enable register for a certain clock ungates that clock without | 
|  | 296 | * affecting the other clocks. The disable register works the opposite | 
|  | 297 | * way. | 
|  | 298 | */ | 
|  | 299 |  | 
|  | 300 | static int src_clk_enable(struct clk_hw *hw) | 
|  | 301 | { | 
|  | 302 | struct clk_src *sclk = to_src(hw); | 
|  | 303 | u32 enreg = sclk->group1 ? SRC_PCKEN1 : SRC_PCKEN0; | 
|  | 304 | u32 sreg = sclk->group1 ? SRC_PCKSR1 : SRC_PCKSR0; | 
|  | 305 |  | 
|  | 306 | writel(sclk->clkbit, src_base + enreg); | 
|  | 307 | /* spin until enabled */ | 
|  | 308 | while (!(readl(src_base + sreg) & sclk->clkbit)) | 
|  | 309 | cpu_relax(); | 
|  | 310 | return 0; | 
|  | 311 | } | 
|  | 312 |  | 
|  | 313 | static void src_clk_disable(struct clk_hw *hw) | 
|  | 314 | { | 
|  | 315 | struct clk_src *sclk = to_src(hw); | 
|  | 316 | u32 disreg = sclk->group1 ? SRC_PCKDIS1 : SRC_PCKDIS0; | 
|  | 317 | u32 sreg = sclk->group1 ? SRC_PCKSR1 : SRC_PCKSR0; | 
|  | 318 |  | 
|  | 319 | writel(sclk->clkbit, src_base + disreg); | 
|  | 320 | /* spin until disabled */ | 
|  | 321 | while (readl(src_base + sreg) & sclk->clkbit) | 
|  | 322 | cpu_relax(); | 
|  | 323 | } | 
|  | 324 |  | 
|  | 325 | static int src_clk_is_enabled(struct clk_hw *hw) | 
|  | 326 | { | 
|  | 327 | struct clk_src *sclk = to_src(hw); | 
|  | 328 | u32 sreg = sclk->group1 ? SRC_PCKSR1 : SRC_PCKSR0; | 
|  | 329 | u32 val = readl(src_base + sreg); | 
|  | 330 |  | 
|  | 331 | return !!(val & sclk->clkbit); | 
|  | 332 | } | 
|  | 333 |  | 
|  | 334 | static unsigned long | 
|  | 335 | src_clk_recalc_rate(struct clk_hw *hw, | 
|  | 336 | unsigned long parent_rate) | 
|  | 337 | { | 
|  | 338 | return parent_rate; | 
|  | 339 | } | 
|  | 340 |  | 
|  | 341 | static const struct clk_ops src_clk_ops = { | 
|  | 342 | .enable = src_clk_enable, | 
|  | 343 | .disable = src_clk_disable, | 
|  | 344 | .is_enabled = src_clk_is_enabled, | 
|  | 345 | .recalc_rate = src_clk_recalc_rate, | 
|  | 346 | }; | 
|  | 347 |  | 
|  | 348 | static struct clk_hw * __init | 
|  | 349 | src_clk_register(struct device *dev, const char *name, | 
|  | 350 | const char *parent_name, u8 id) | 
|  | 351 | { | 
|  | 352 | int ret; | 
|  | 353 | struct clk_src *sclk; | 
|  | 354 | struct clk_init_data init; | 
|  | 355 |  | 
|  | 356 | sclk = kzalloc(sizeof(*sclk), GFP_KERNEL); | 
|  | 357 | if (!sclk) | 
|  | 358 | return ERR_PTR(-ENOMEM); | 
|  | 359 |  | 
|  | 360 | init.name = name; | 
|  | 361 | init.ops = &src_clk_ops; | 
|  | 362 | /* Do not force-disable the static SDRAM controller */ | 
|  | 363 | if (id == 2) | 
|  | 364 | init.flags = CLK_IGNORE_UNUSED; | 
|  | 365 | else | 
|  | 366 | init.flags = 0; | 
|  | 367 | init.parent_names = (parent_name ? &parent_name : NULL); | 
|  | 368 | init.num_parents = (parent_name ? 1 : 0); | 
|  | 369 | sclk->hw.init = &init; | 
|  | 370 | sclk->id = id; | 
|  | 371 | sclk->group1 = (id > 31); | 
|  | 372 | sclk->clkbit = BIT(id & 0x1f); | 
|  | 373 |  | 
|  | 374 | pr_debug("register clock \"%s\" ID: %d group: %d bits: %08x\n", | 
|  | 375 | name, id, sclk->group1, sclk->clkbit); | 
|  | 376 |  | 
|  | 377 | ret = clk_hw_register(dev, &sclk->hw); | 
|  | 378 | if (ret) { | 
|  | 379 | kfree(sclk); | 
|  | 380 | return ERR_PTR(ret); | 
|  | 381 | } | 
|  | 382 |  | 
|  | 383 | return &sclk->hw; | 
|  | 384 | } | 
|  | 385 |  | 
|  | 386 | #ifdef CONFIG_DEBUG_FS | 
|  | 387 |  | 
|  | 388 | static u32 src_pcksr0_boot; | 
|  | 389 | static u32 src_pcksr1_boot; | 
|  | 390 |  | 
|  | 391 | static const char * const src_clk_names[] = { | 
|  | 392 | "HCLKDMA0  ", | 
|  | 393 | "HCLKSMC   ", | 
|  | 394 | "HCLKSDRAM ", | 
|  | 395 | "HCLKDMA1  ", | 
|  | 396 | "HCLKCLCD  ", | 
|  | 397 | "PCLKIRDA  ", | 
|  | 398 | "PCLKSSP   ", | 
|  | 399 | "PCLKUART0 ", | 
|  | 400 | "PCLKSDI   ", | 
|  | 401 | "PCLKI2C0  ", | 
|  | 402 | "PCLKI2C1  ", | 
|  | 403 | "PCLKUART1 ", | 
|  | 404 | "PCLMSP0   ", | 
|  | 405 | "HCLKUSB   ", | 
|  | 406 | "HCLKDIF   ", | 
|  | 407 | "HCLKSAA   ", | 
|  | 408 | "HCLKSVA   ", | 
|  | 409 | "PCLKHSI   ", | 
|  | 410 | "PCLKXTI   ", | 
|  | 411 | "PCLKUART2 ", | 
|  | 412 | "PCLKMSP1  ", | 
|  | 413 | "PCLKMSP2  ", | 
|  | 414 | "PCLKOWM   ", | 
|  | 415 | "HCLKHPI   ", | 
|  | 416 | "PCLKSKE   ", | 
|  | 417 | "PCLKHSEM  ", | 
|  | 418 | "HCLK3D    ", | 
|  | 419 | "HCLKHASH  ", | 
|  | 420 | "HCLKCRYP  ", | 
|  | 421 | "PCLKMSHC  ", | 
|  | 422 | "HCLKUSBM  ", | 
|  | 423 | "HCLKRNG   ", | 
|  | 424 | "RESERVED  ", | 
|  | 425 | "RESERVED  ", | 
|  | 426 | "RESERVED  ", | 
|  | 427 | "RESERVED  ", | 
|  | 428 | "CLDCLK    ", | 
|  | 429 | "IRDACLK   ", | 
|  | 430 | "SSPICLK   ", | 
|  | 431 | "UART0CLK  ", | 
|  | 432 | "SDICLK    ", | 
|  | 433 | "I2C0CLK   ", | 
|  | 434 | "I2C1CLK   ", | 
|  | 435 | "UART1CLK  ", | 
|  | 436 | "MSPCLK0   ", | 
|  | 437 | "USBCLK    ", | 
|  | 438 | "DIFCLK    ", | 
|  | 439 | "IPI2CCLK  ", | 
|  | 440 | "IPBMCCLK  ", | 
|  | 441 | "HSICLKRX  ", | 
|  | 442 | "HSICLKTX  ", | 
|  | 443 | "UART2CLK  ", | 
|  | 444 | "MSPCLK1   ", | 
|  | 445 | "MSPCLK2   ", | 
|  | 446 | "OWMCLK    ", | 
|  | 447 | "RESERVED  ", | 
|  | 448 | "SKECLK    ", | 
|  | 449 | "RESERVED  ", | 
|  | 450 | "3DCLK     ", | 
|  | 451 | "PCLKMSP3  ", | 
|  | 452 | "MSPCLK3   ", | 
|  | 453 | "MSHCCLK   ", | 
|  | 454 | "USBMCLK   ", | 
|  | 455 | "RNGCCLK   ", | 
|  | 456 | }; | 
|  | 457 |  | 
|  | 458 | static int nomadik_src_clk_show(struct seq_file *s, void *what) | 
|  | 459 | { | 
|  | 460 | int i; | 
|  | 461 | u32 src_pcksr0 = readl(src_base + SRC_PCKSR0); | 
|  | 462 | u32 src_pcksr1 = readl(src_base + SRC_PCKSR1); | 
|  | 463 | u32 src_pckensr0 = readl(src_base + SRC_PCKENSR0); | 
|  | 464 | u32 src_pckensr1 = readl(src_base + SRC_PCKENSR1); | 
|  | 465 |  | 
|  | 466 | seq_puts(s, "Clock:      Boot:   Now:    Request: ASKED:\n"); | 
|  | 467 | for (i = 0; i < ARRAY_SIZE(src_clk_names); i++) { | 
|  | 468 | u32 pcksrb = (i < 0x20) ? src_pcksr0_boot : src_pcksr1_boot; | 
|  | 469 | u32 pcksr = (i < 0x20) ? src_pcksr0 : src_pcksr1; | 
|  | 470 | u32 pckreq = (i < 0x20) ? src_pckensr0 : src_pckensr1; | 
|  | 471 | u32 mask = BIT(i & 0x1f); | 
|  | 472 |  | 
|  | 473 | seq_printf(s, "%s  %s     %s     %s\n", | 
|  | 474 | src_clk_names[i], | 
|  | 475 | (pcksrb & mask) ? "on " : "off", | 
|  | 476 | (pcksr & mask) ? "on " : "off", | 
|  | 477 | (pckreq & mask) ? "on " : "off"); | 
|  | 478 | } | 
|  | 479 | return 0; | 
|  | 480 | } | 
|  | 481 |  | 
|  | 482 | static int nomadik_src_clk_open(struct inode *inode, struct file *file) | 
|  | 483 | { | 
|  | 484 | return single_open(file, nomadik_src_clk_show, NULL); | 
|  | 485 | } | 
|  | 486 |  | 
|  | 487 | static const struct file_operations nomadik_src_clk_debugfs_ops = { | 
|  | 488 | .open           = nomadik_src_clk_open, | 
|  | 489 | .read           = seq_read, | 
|  | 490 | .llseek         = seq_lseek, | 
|  | 491 | .release        = single_release, | 
|  | 492 | }; | 
|  | 493 |  | 
|  | 494 | static int __init nomadik_src_clk_init_debugfs(void) | 
|  | 495 | { | 
|  | 496 | /* Vital for multiplatform */ | 
|  | 497 | if (!src_base) | 
|  | 498 | return -ENODEV; | 
|  | 499 | src_pcksr0_boot = readl(src_base + SRC_PCKSR0); | 
|  | 500 | src_pcksr1_boot = readl(src_base + SRC_PCKSR1); | 
|  | 501 | debugfs_create_file("nomadik-src-clk", S_IFREG | S_IRUGO, | 
|  | 502 | NULL, NULL, &nomadik_src_clk_debugfs_ops); | 
|  | 503 | return 0; | 
|  | 504 | } | 
|  | 505 | device_initcall(nomadik_src_clk_init_debugfs); | 
|  | 506 |  | 
|  | 507 | #endif | 
|  | 508 |  | 
|  | 509 | static void __init of_nomadik_pll_setup(struct device_node *np) | 
|  | 510 | { | 
|  | 511 | struct clk_hw *hw; | 
|  | 512 | const char *clk_name = np->name; | 
|  | 513 | const char *parent_name; | 
|  | 514 | u32 pll_id; | 
|  | 515 |  | 
|  | 516 | if (!src_base) | 
|  | 517 | nomadik_src_init(); | 
|  | 518 |  | 
|  | 519 | if (of_property_read_u32(np, "pll-id", &pll_id)) { | 
|  | 520 | pr_err("%s: PLL \"%s\" missing pll-id property\n", | 
|  | 521 | __func__, clk_name); | 
|  | 522 | return; | 
|  | 523 | } | 
|  | 524 | parent_name = of_clk_get_parent_name(np, 0); | 
|  | 525 | hw = pll_clk_register(NULL, clk_name, parent_name, pll_id); | 
|  | 526 | if (!IS_ERR(hw)) | 
|  | 527 | of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); | 
|  | 528 | } | 
|  | 529 | CLK_OF_DECLARE(nomadik_pll_clk, | 
|  | 530 | "st,nomadik-pll-clock", of_nomadik_pll_setup); | 
|  | 531 |  | 
|  | 532 | static void __init of_nomadik_hclk_setup(struct device_node *np) | 
|  | 533 | { | 
|  | 534 | struct clk_hw *hw; | 
|  | 535 | const char *clk_name = np->name; | 
|  | 536 | const char *parent_name; | 
|  | 537 |  | 
|  | 538 | if (!src_base) | 
|  | 539 | nomadik_src_init(); | 
|  | 540 |  | 
|  | 541 | parent_name = of_clk_get_parent_name(np, 0); | 
|  | 542 | /* | 
|  | 543 | * The HCLK divides PLL1 with 1 (passthru), 2, 3 or 4. | 
|  | 544 | */ | 
|  | 545 | hw = clk_hw_register_divider(NULL, clk_name, parent_name, | 
|  | 546 | 0, src_base + SRC_CR, | 
|  | 547 | 13, 2, | 
|  | 548 | CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, | 
|  | 549 | &src_lock); | 
|  | 550 | if (!IS_ERR(hw)) | 
|  | 551 | of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); | 
|  | 552 | } | 
|  | 553 | CLK_OF_DECLARE(nomadik_hclk_clk, | 
|  | 554 | "st,nomadik-hclk-clock", of_nomadik_hclk_setup); | 
|  | 555 |  | 
|  | 556 | static void __init of_nomadik_src_clk_setup(struct device_node *np) | 
|  | 557 | { | 
|  | 558 | struct clk_hw *hw; | 
|  | 559 | const char *clk_name = np->name; | 
|  | 560 | const char *parent_name; | 
|  | 561 | u32 clk_id; | 
|  | 562 |  | 
|  | 563 | if (!src_base) | 
|  | 564 | nomadik_src_init(); | 
|  | 565 |  | 
|  | 566 | if (of_property_read_u32(np, "clock-id", &clk_id)) { | 
|  | 567 | pr_err("%s: SRC clock \"%s\" missing clock-id property\n", | 
|  | 568 | __func__, clk_name); | 
|  | 569 | return; | 
|  | 570 | } | 
|  | 571 | parent_name = of_clk_get_parent_name(np, 0); | 
|  | 572 | hw = src_clk_register(NULL, clk_name, parent_name, clk_id); | 
|  | 573 | if (!IS_ERR(hw)) | 
|  | 574 | of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); | 
|  | 575 | } | 
|  | 576 | CLK_OF_DECLARE(nomadik_src_clk, | 
|  | 577 | "st,nomadik-src-clock", of_nomadik_src_clk_setup); |