/* * arch/arm/mach-pnx4008/clock.c * * Clock control driver for PNX4008 * * Authors: Vitaly Wool, Dmitry Chigirev * Generic clock management functions are partially based on: * linux/arch/arm/mach-omap/clock.c * * 2005-2006 (c) MontaVista Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. */ #include #include #include #include #include #include #include #include #include #include #include "clock.h" /*forward declaration*/ static struct clk per_ck; static struct clk hclk_ck; static struct clk ck_1MHz; static struct clk ck_13MHz; static struct clk ck_pll1; static int local_set_rate(struct clk *clk, u32 rate); static inline void clock_lock(void) { local_irq_disable(); } static inline void clock_unlock(void) { local_irq_enable(); } static void propagate_rate(struct clk *clk) { struct clk *tmp_clk; tmp_clk = clk; while (tmp_clk->propagate_next) { tmp_clk = tmp_clk->propagate_next; local_set_rate(tmp_clk, tmp_clk->user_rate); } } static inline void clk_reg_disable(struct clk *clk) { if (clk->enable_reg) __raw_writel(__raw_readl(clk->enable_reg) & ~(1 << clk->enable_shift), clk->enable_reg); } static inline void clk_reg_enable(struct clk *clk) { if (clk->enable_reg) __raw_writel(__raw_readl(clk->enable_reg) | (1 << clk->enable_shift), clk->enable_reg); } static inline void clk_reg_disable1(struct clk *clk) { if (clk->enable_reg1) __raw_writel(__raw_readl(clk->enable_reg1) & ~(1 << clk->enable_shift1), clk->enable_reg1); } static inline void clk_reg_enable1(struct clk *clk) { if (clk->enable_reg1) __raw_writel(__raw_readl(clk->enable_reg1) | (1 << clk->enable_shift1), clk->enable_reg1); } static int clk_wait_for_pll_lock(struct clk *clk) { int i; i = 0; while (i++ < 0xFFF && !(__raw_readl(clk->scale_reg) & 1)) ; /*wait for PLL to lock */ if (!(__raw_readl(clk->scale_reg) & 1)) { printk(KERN_ERR "%s ERROR: failed to lock, scale reg data: %x\n", clk->name, __raw_readl(clk->scale_reg)); return -1; } return 0; } static int switch_to_dirty_13mhz(struct clk *clk) { int i; int ret; u32 tmp_reg; ret = 0; if (!clk->rate) clk_reg_enable1(clk); tmp_reg = __raw_readl(clk->parent_switch_reg); /*if 13Mhz clock selected, select 13'MHz (dirty) source from OSC */ if (!(tmp_reg & 1)) { tmp_reg |= (1 << 1); /* Trigger switch to 13'MHz (dirty) clock */ __raw_writel(tmp_reg, clk->parent_switch_reg); i = 0; while (i++ < 0xFFF && !(__raw_readl(clk->parent_switch_reg) & 1)) ; /*wait for 13'MHz selection status */ if (!(__raw_readl(clk->parent_switch_reg) & 1)) { printk(KERN_ERR "%s ERROR: failed to select 13'MHz, parent sw reg data: %x\n", clk->name, __raw_readl(clk->parent_switch_reg)); ret = -1; } } if (!clk->rate) clk_reg_disable1(clk); return ret; } static int switch_to_clean_13mhz(struct clk *clk) { int i; int ret; u32 tmp_reg; ret = 0; if (!clk->rate) clk_reg_enable1(clk); tmp_reg = __raw_readl(clk->parent_switch_reg); /*if 13'Mhz clock selected, select 13MHz (clean) source from OSC */ if (tmp_reg & 1) { tmp_reg &= ~(1 << 1); /* Trigger switch to 13MHz (clean) clock */ __raw_writel(tmp_reg, clk->parent_switch_reg); i = 0; while (i++ < 0xFFF && (__raw_readl(clk->parent_switch_reg) & 1)) ; /*wait for 13MHz selection status */ if (__raw_readl(clk->parent_switch_reg) & 1) { printk(KERN_ERR "%s ERROR: failed to select 13MHz, parent sw reg data: %x\n", clk->name, __raw_readl(clk->parent_switch_reg)); ret = -1; } } if (!clk->rate) clk_reg_disable1(clk); return ret; } static int set_13MHz_parent(struct clk *clk, struct clk *parent) { int ret = -EINVAL; if (parent == &ck_13MHz) ret = switch_to_clean_13mhz(clk); else if (parent == &ck_pll1) ret = switch_to_dirty_13mhz(clk); return ret; } #define PLL160_MIN_FCCO 156000 #define PLL160_MAX_FCCO 320000 /* * Calculate pll160 settings. * Possible input: up to 320MHz with step of clk->parent->rate. * In PNX4008 parent rate for pll160s may be either 1 or 13MHz. * Ignored paths: "feedback" (bit 13 set), "div-by-N". * Setting ARM PLL4 rate to 0 will put CPU into direct run mode. * Setting PLL5 and PLL3 rate to 0 will disable USB and DSP clock input. * Please refer to PNX4008 IC manual for details. */ static int pll160_set_rate(struct clk *clk, u32 rate) { u32 tmp_reg, tmp_m, tmp_2p, i; u32 parent_rate; int ret = -EINVAL; parent_rate = clk->parent->rate; if (!parent_rate) goto out; /* set direct run for ARM or disable output for others */ clk_reg_disable(clk); /* disable source input as well (ignored for ARM) */ clk_reg_disable1(clk); tmp_reg = __raw_readl(clk->scale_reg); tmp_reg &= ~0x1ffff; /*clear all settings, power down */ __raw_writel(tmp_reg, clk->scale_reg); rate -= rate % parent_rate; /*round down the input */ if (rate > PLL160_MAX_FCCO) rate = PLL160_MAX_FCCO; if (!rate) { clk->rate = 0; ret = 0; goto out; } clk_reg_enable1(clk); tmp_reg = __raw_readl(clk->scale_reg); if (rate == parent_rate) { /*enter direct bypass mode */ tmp_reg |= ((1 << 14) | (1 << 15)); __raw_writel(tmp_reg, clk->scale_reg); clk->rate = parent_rate; clk_reg_enable(clk); ret = 0; goto out; } i = 0; for (tmp_2p = 1; tmp_2p < 16; tmp_2p <<= 1) { if (rate * tmp_2p >= PLL160_MIN_FCCO) break; i++; } if (tmp_2p > 1) tmp_reg |= ((i - 1) << 11); else tmp_reg |= (1 << 14); /*direct mode, no divide */ tmp_m = rate * tmp_2p; tmp_m /= parent_rate; tmp_reg |= (tmp_m - 1) << 1; /*calculate M */ tmp_reg |= (1 << 16); /*power up PLL */ __raw_writel(tmp_reg, clk->scale_reg); if (clk_wait_for_pll_lock(clk) < 0) { clk_reg_disable(clk); clk_reg_disable1(clk); tmp_reg = __raw_readl(clk->scale_reg); tmp_reg &= ~0x1ffff; /*clear all settings, power down */ __raw_writel(tmp_reg, clk->scale_reg); clk->rate = 0; ret = -EFAULT; goto out; } clk->rate = (tmp_m * parent_rate) / tmp_2p; if (clk->flags & RATE_PROPAGATES) propagate_rate(clk); clk_reg_enable(clk); ret = 0; out: return ret; } /*configure PER_CLK*/ static int per_clk_set_rate(struct clk *clk, u32 rate) { u32 tmp; tmp = __raw_readl(clk->scale_reg); tmp &= ~(0x1f << 2); tmp |= ((clk->parent->rate / clk->rate) - 1) << 2; __raw_writel(tmp, clk->scale_reg); clk->rate = rate; return 0; } /*configure HCLK*/ static int hclk_set_rate(struct clk *clk, u32 rate) { u32 tmp; tmp = __raw_readl(clk->scale_reg); tmp = tmp & ~0x3; switch (rate) { case 1: break; case 2: tmp |= 1; break; case 4: tmp |= 2; break; } __raw_writel(tmp, clk->scale_reg); clk->rate = rate; return 0; } static u32 hclk_round_rate(struct clk *clk, u32 rate) { switch (rate) { case 1: case 4: return rate; } return 2; } static u32 per_clk_round_rate(struct clk *clk, u32 rate) { return CLK_RATE_13MHZ; } static int on_off_set_rate(struct clk *clk, u32 rate) { if (rate) { clk_reg_enable(clk); clk->rate = 1; } else { clk_reg_disable(clk); clk->rate = 0; } return 0; } static int on_off_inv_set_rate(struct clk *clk, u32 rate) { if (rate) { clk_reg_disable(clk); /*enable bit is inverted */ clk->rate = 1; } else { clk_reg_enable(clk); clk->rate = 0; } return 0; } static u32 on_off_round_rate(struct clk *clk, u32 rate) { return (rate ? 1 : 0); } static u32 pll4_round_rate(struct clk *clk, u32 rate) { if (rate > CLK_RATE_208MHZ) rate = CLK_RATE_208MHZ; if (rate == CLK_RATE_208MHZ && hclk_ck.user_rate == 1) rate = CLK_RATE_208MHZ - CLK_RATE_13MHZ; return (rate - (rate % (hclk_ck.user_rate * CLK_RATE_13MHZ))); } static u32 pll3_round_rate(struct clk *clk, u32 rate) { if (rate > CLK_RATE_208MHZ) rate = CLK_RATE_208MHZ; return (rate - rate % CLK_RATE_13MHZ); } static u32 pll5_round_rate(struct clk *clk, u32 rate) { return (rate ? CLK_RATE_48MHZ : 0); } static u32 ck_13MHz_round_rate(struct clk *clk, u32 rate) { return (rate ? CLK_RATE_13MHZ : 0); } static int ck_13MHz_set_rate(struct clk *clk, u32 rate) { if (rate) { clk_reg_disable(clk); /*enable bit is inverted */ udelay(500); clk->rate = CLK_RATE_13MHZ; ck_1MHz.rate = CLK_RATE_1MHZ; } else { clk_reg_enable(clk); clk->rate = 0; ck_1MHz.rate = 0; } return 0; } static int pll1_set_rate(struct clk *clk, u32 rate) { #if 0 /* doesn't work on some boards, probably a HW BUG */ if (rate) { clk_reg_disable(clk); /*enable bit is inverted */ if (!clk_wait_for_pll_lock(clk)) { clk->rate = CLK_RATE_13MHZ; } else { clk_reg_enable(clk); clk->rate = 0; } } else { clk_reg_enable(clk); clk->rate = 0; } #endif return 0; } /* Clock sources */ static struct clk osc_13MHz = { .name = "osc_13MHz", .flags = FIXED_RATE, .rate = CLK_RATE_13MHZ, }; static struct clk ck_13MHz = { .name = "ck_13MHz", .parent = &osc_13MHz, .flags = NEEDS_INITIALIZATION, .round_rate = &ck_13MHz_round_rate, .set_rate = &ck_13MHz_set_rate, .enable_reg = OSC13CTRL_REG, .enable_shift = 0, .rate = CLK_RATE_13MHZ, }; static struct clk osc_32KHz = { .name = "osc_32KHz", .flags = FIXED_RATE, .rate = CLK_RATE_32KHZ, }; /*attached to PLL5*/ static struct clk ck_1MHz = { .name = "ck_1MHz", .flags = FIXED_RATE | PARENT_SET_RATE, .parent = &ck_13MHz, }; /* PLL1 (397) - provides 13' MHz clock */ static struct clk ck_pll1 = { .name = "ck_pll1", .parent = &osc_32KHz, .flags = NEEDS_INITIALIZATION, .round_rate = &ck_13MHz_round_rate, .set_rate = &pll1_set_rate, .enable_reg = PLLCTRL_REG, .enable_shift = 1, .scale_reg = PLLCTRL_REG, .rate = CLK_RATE_13MHZ, }; /* CPU/Bus PLL */ static struct clk ck_pll4 = { .name = "ck_pll4", .parent = &ck_pll1, .flags = RATE_PROPAGATES | NEEDS_INITIALIZATION, .propagate_next = &per_ck, .round_rate = &pll4_round_rate, .set_rate = &pll160_set_rate, .rate = CLK_RATE_208MHZ, .scale_reg = HCLKPLLCTRL_REG, .enable_reg = PWRCTRL_REG, .enable_shift = 2, .parent_switch_reg = SYSCLKCTRL_REG, .set_parent = &set_13MHz_parent, }; /* USB PLL */ static struct clk ck_pll5 = { .name = "ck_pll5", .parent = &ck_1MHz, .flags = NEEDS_INITIALIZATION, .round_rate = &pll5_round_rate, .set_rate = &pll160_set_rate, .scale_reg = USBCTRL_REG, .enable_reg = USBCTRL_REG, .enable_shift = 18, .enable_reg1 = USBCTRL_REG, .enable_shift1 = 17, }; /* XPERTTeak DSP PLL */ static struct clk ck_pll3 = { .name = "ck_pll3", .parent = &ck_pll1, .flags = NEEDS_INITIALIZATION, .round_rate = &pll3_round_rate, .set_rate = &pll160_set_rate, .scale_reg = DSPPLLCTRL_REG, .enable_reg = DSPCLKCTRL_REG, .enable_shift = 3, .enable_reg1 = DSPCLKCTRL_REG, .enable_shift1 = 2, .parent_switch_reg = DSPCLKCTRL_REG, .set_parent = &set_13MHz_parent, }; static struct clk hclk_ck = { .name = "hclk_ck", .parent = &ck_pll4, .flags = PARENT_SET_RATE, .set_rate = &hclk_set_rate, .round_rate = &hclk_round_rate, .scale_reg = HCLKDIVCTRL_REG, .rate = 2, .user_rate = 2, }; static struct clk per_ck = { .name = "per_ck", .parent = &ck_pll4, .flags = FIXED_RATE, .propagate_next = &hclk_ck, .set_rate = &per_clk_set_rate, .round_rate = &per_clk_round_rate, .scale_reg = HCLKDIVCTRL_REG, .rate = CLK_RATE_13MHZ, .user_rate = CLK_RATE_13MHZ, }; static struct clk m2hclk_ck = { .name = "m2hclk_ck", .parent = &hclk_ck, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_inv_set_rate, .rate = 1, .enable_shift = 6, .enable_reg = PWRCTRL_REG, }; static struct clk vfp9_ck = { .name = "vfp9_ck", .parent = &ck_pll4, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .rate = 1, .enable_shift = 4, .enable_reg = VFP9CLKCTRL_REG, }; static struct clk keyscan_ck = { .name = "keyscan_ck", .parent = &osc_32KHz, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 0, .enable_reg = KEYCLKCTRL_REG, }; static struct clk touch_ck = { .name = "touch_ck", .parent = &osc_32KHz, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 0, .enable_reg = TSCLKCTRL_REG, }; static struct clk pwm1_ck = { .name = "pwm1_ck", .parent = &osc_32KHz, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 0, .enable_reg = PWMCLKCTRL_REG, }; static struct clk pwm2_ck = { .name = "pwm2_ck", .parent = &osc_32KHz, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 2, .enable_reg = PWMCLKCTRL_REG, }; static struct clk jpeg_ck = { .name = "jpeg_ck", .parent = &hclk_ck, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 0, .enable_reg = JPEGCLKCTRL_REG, }; static struct clk ms_ck = { .name = "ms_ck", .parent = &ck_pll4, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 5, .enable_reg = MSCTRL_REG, }; static struct clk dum_ck = { .name = "dum_ck", .parent = &hclk_ck, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 0, .enable_reg = DUMCLKCTRL_REG, }; static struct clk flash_ck = { .name = "flash_ck", .parent = &hclk_ck, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 1, /* Only MLC clock supported */ .enable_reg = FLASHCLKCTRL_REG, }; static struct clk i2c0_ck = { .name = "i2c0_ck", .parent = &per_ck, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 0, .enable_reg = I2CCLKCTRL_REG, }; static struct clk i2c1_ck = { .name = "i2c1_ck", .parent = &per_ck, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 1, .enable_reg = I2CCLKCTRL_REG, }; static struct clk i2c2_ck = { .name = "i2c2_ck", .parent = &per_ck, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 2, .enable_reg = USB_OTG_CLKCTRL_REG, }; static struct clk spi0_ck = { .name = "spi0_ck", .parent = &hclk_ck, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 0, .enable_reg = SPICTRL_REG, }; static struct clk spi1_ck = { .name = "spi1_ck", .parent = &hclk_ck, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 4, .enable_reg = SPICTRL_REG, }; static struct clk dma_ck = { .name = "dma_ck", .parent = &hclk_ck, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 0, .enable_reg = DMACLKCTRL_REG, }; static struct clk uart3_ck = { .name = "uart3_ck", .parent = &per_ck, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .rate = 1, .enable_shift = 0, .enable_reg = UARTCLKCTRL_REG, }; static struct clk uart4_ck = { .name = "uart4_ck", .parent = &per_ck, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 1, .enable_reg = UARTCLKCTRL_REG, }; static struct clk uart5_ck = { .name = "uart5_ck", .parent = &per_ck, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .rate = 1, .enable_shift = 2, .enable_reg = UARTCLKCTRL_REG, }; static struct clk uart6_ck = { .name = "uart6_ck", .parent = &per_ck, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 3, .enable_reg = UARTCLKCTRL_REG, }; static struct clk wdt_ck = { .name = "wdt_ck", .parent = &per_ck, .flags = NEEDS_INITIALIZATION, .round_rate = &on_off_round_rate, .set_rate = &on_off_set_rate, .enable_shift = 0, .enable_reg = TIMCLKCTRL_REG, }; /* These clocks are visible outside this module * and can be initialized */ static struct clk *onchip_clks[] = { &ck_13MHz, &ck_pll1, &ck_pll4, &ck_pll5, &ck_pll3, &vfp9_ck, &m2hclk_ck, &hclk_ck, &dma_ck, &flash_ck, &dum_ck, &keyscan_ck, &pwm1_ck, &pwm2_ck, &jpeg_ck, &ms_ck, &touch_ck, &i2c0_ck, &i2c1_ck, &i2c2_ck, &spi0_ck, &spi1_ck, &uart3_ck, &uart4_ck, &uart5_ck, &uart6_ck, &wdt_ck, }; static int local_clk_enable(struct clk *clk) { int ret = 0; if (!(clk->flags & FIXED_RATE) && !clk->rate && clk->set_rate && clk->user_rate) ret = clk->set_rate(clk, clk->user_rate); return ret; } static void local_clk_disable(struct clk *clk) { if (!(clk->flags & FIXED_RATE) && clk->rate && clk->set_rate) clk->set_rate(clk, 0); } static void local_clk_unuse(struct clk *clk) { if (clk->usecount > 0 && !(--clk->usecount)) { local_clk_disable(clk); if (clk->parent) local_clk_unuse(clk->parent); } } static int local_clk_use(struct clk *clk) { int ret = 0; if (clk->usecount++ == 0) { if (clk->parent) ret = local_clk_use(clk->parent); if (ret != 0) { clk->usecount--; goto out; } ret = local_clk_enable(clk); if (ret != 0 && clk->parent) { local_clk_unuse(clk->parent); clk->usecount--; } } out: return ret; } static int local_set_rate(struct clk *clk, u32 rate) { int ret = -EINVAL; if (clk->set_rate) { if (clk->user_rate == clk->rate && clk->parent->rate) { /* if clock enabled or rate not set */ clk->user_rate = clk->round_rate(clk, rate); ret = clk->set_rate(clk, clk->user_rate); } else clk->user_rate = clk->round_rate(clk, rate); ret = 0; } return ret; } int clk_set_rate(struct clk *clk, unsigned long rate) { int ret = -EINVAL; if (clk->flags & FIXED_RATE) goto out; clock_lock(); if ((clk->flags & PARENT_SET_RATE) && clk->parent) { clk->user_rate = clk->round_rate(clk, rate); /* parent clock needs to be refreshed for the setting to take effect */ } else { ret = local_set_rate(clk, rate); } ret = 0; clock_unlock(); out: return ret; } EXPORT_SYMBOL(clk_set_rate); struct clk *clk_get(struct device *dev, const char *id) { struct clk *clk = ERR_PTR(-ENOENT); struct clk **clkp; clock_lock(); for (clkp = onchip_clks; clkp < onchip_clks + ARRAY_SIZE(onchip_clks); clkp++) { if (strcmp(id, (*clkp)->name) == 0 && try_module_get((*clkp)->owner)) { clk = (*clkp); break; } } clock_unlock(); return clk; } EXPORT_SYMBOL(clk_get); void clk_put(struct clk *clk) { clock_lock(); if (clk && !IS_ERR(clk)) module_put(clk->owner); clock_unlock(); } EXPORT_SYMBOL(clk_put); unsigned long clk_get_rate(struct clk *clk) { unsigned long ret; clock_lock(); ret = clk->rate; clock_unlock(); return ret; } EXPORT_SYMBOL(clk_get_rate); int clk_enable(struct clk *clk) { int ret = 0; clock_lock(); ret = local_clk_use(clk); clock_unlock(); return ret; } EXPORT_SYMBOL(clk_enable); void clk_disable(struct clk *clk) { clock_lock(); local_clk_unuse(clk); clock_unlock(); } EXPORT_SYMBOL(clk_disable); long clk_round_rate(struct clk *clk, unsigned long rate) { long ret; clock_lock(); if (clk->round_rate) ret = clk->round_rate(clk, rate); else ret = clk->rate; clock_unlock(); return ret; } EXPORT_SYMBOL(clk_round_rate); int clk_set_parent(struct clk *clk, struct clk *parent) { int ret = -ENODEV; if (!clk->set_parent) goto out; clock_lock(); ret = clk->set_parent(clk, parent); if (!ret) clk->parent = parent; clock_unlock(); out: return ret; } EXPORT_SYMBOL(clk_set_parent); static int __init clk_init(void) { struct clk **clkp; /* Disable autoclocking, as it doesn't seem to work */ __raw_writel(0xff, AUTOCLK_CTRL); for (clkp = onchip_clks; clkp < onchip_clks + ARRAY_SIZE(onchip_clks); clkp++) { if (((*clkp)->flags & NEEDS_INITIALIZATION) && ((*clkp)->set_rate)) { (*clkp)->user_rate = (*clkp)->rate; local_set_rate((*clkp), (*clkp)->user_rate); if ((*clkp)->set_parent) (*clkp)->set_parent((*clkp), (*clkp)->parent); } pr_debug("%s: clock %s, rate %ld\n", __func__, (*clkp)->name, (*clkp)->rate); } local_clk_use(&ck_pll4); /* if ck_13MHz is not used, disable it. */ if (ck_13MHz.usecount == 0) local_clk_disable(&ck_13MHz); /* Disable autoclocking */ __raw_writeb(0xff, AUTOCLK_CTRL); return 0; } arch_initcall(clk_init);