From: Grant Likely <[EMAIL PROTECTED]> Device drivers should not access the CDM registers directly to modify the clocking. Instead, provide a helper function for setting the MCLK value so that the registers can be properly protected from concurent access. ---
arch/powerpc/platforms/52xx/efika.c | 2 arch/powerpc/platforms/52xx/lite5200.c | 1 arch/powerpc/platforms/52xx/mpc52xx_common.c | 112 +++++++++++++++++++++++--- include/asm-powerpc/mpc52xx.h | 7 ++ 4 files changed, 107 insertions(+), 15 deletions(-) diff --git a/arch/powerpc/platforms/52xx/efika.c b/arch/powerpc/platforms/52xx/efika.c index a0da70c..0e3b1ac 100644 --- a/arch/powerpc/platforms/52xx/efika.c +++ b/arch/powerpc/platforms/52xx/efika.c @@ -180,6 +180,8 @@ static void __init efika_setup_arch(void) { rtas_initialize(); + mpc52xx_setup_clocks(); + efika_pcisetup(); #ifdef CONFIG_PM diff --git a/arch/powerpc/platforms/52xx/lite5200.c b/arch/powerpc/platforms/52xx/lite5200.c index 25d2bfa..7665e60 100644 --- a/arch/powerpc/platforms/52xx/lite5200.c +++ b/arch/powerpc/platforms/52xx/lite5200.c @@ -143,6 +143,7 @@ static void __init lite5200_setup_arch(void) lite5200_fix_port_config(); /* Some mpc5200 & mpc5200b related configuration */ + mpc5200_setup_clocks(); mpc5200_setup_xlb_arbiter(); /* Map wdt for mpc52xx_restart() */ diff --git a/arch/powerpc/platforms/52xx/mpc52xx_common.c b/arch/powerpc/platforms/52xx/mpc52xx_common.c index 9850685..ced046b 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_common.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_common.c @@ -14,6 +14,7 @@ #include <linux/kernel.h> #include <linux/of_platform.h> +#include <linux/spinlock.h> #include <asm/io.h> #include <asm/prom.h> #include <asm/mpc52xx.h> @@ -26,6 +27,20 @@ */ static volatile struct mpc52xx_gpt *mpc52xx_wdt = NULL; +/* + * Location of clock distibution module. The device regs are mapped at + * board init time to eliminate runtime lookups. All access to these + * registers is protected with the mpc52xx_cdm_lock spinlock + */ +static void __iomem *mpc52xx_cdm_regs = NULL; +static spinlock_t mpc52xx_cdm_lock = SPIN_LOCK_UNLOCKED; + +/* + * Cached clock values + */ +static unsigned int mpc52xx_system_freq; +static unsigned int mpc52xx_ipb_freq; + static void __iomem * mpc52xx_map_node(struct device_node *ofn) { @@ -74,26 +89,92 @@ EXPORT_SYMBOL(mpc52xx_find_and_map_path); unsigned int mpc52xx_find_ipb_freq(struct device_node *node) { - struct device_node *np; - const unsigned int *p_ipb_freq = NULL; + return mpc52xx_ipb_freq; +} +EXPORT_SYMBOL(mpc52xx_find_ipb_freq); - of_node_get(node); - while (node) { - p_ipb_freq = of_get_property(node, "bus-frequency", NULL); - if (p_ipb_freq) - break; +/* + * Clock support for PSCs + */ +struct mpc52xx_cdm_psc_clk_params { + int div_reg; + int enable; +}; - np = of_get_parent(node); - of_node_put(node); - node = np; - } - if (node) - of_node_put(node); +static struct mpc52xx_cdm_psc_clk_params mpc52xx_cdm_psc_clk_params[] = { + [0] = { .div_reg = MPC52xx_CDM_MCLKEN_DIV_PSC1_OFF, .enable = 0x20 }, + [1] = { .div_reg = MPC52xx_CDM_MCLKEN_DIV_PSC2_OFF, .enable = 0x40 }, + [2] = { .div_reg = MPC52xx_CDM_MCLKEN_DIV_PSC3_OFF, .enable = 0x80 }, + [5] = { .div_reg = MPC52xx_CDM_MCLKEN_DIV_PSC6_OFF, .enable = 0x10 }, +}; + +/** + * mpc52xx_cdm_set_psc_clk: Set input MCLK for a PSC + * @psc: id of PSC, based at 0 + * @freq_hz: desired frequency + */ +int mpc52xx_cdm_set_psc_clk(int psc, u32 freq_hz) +{ + struct mpc52xx_cdm_psc_clk_params *params; + unsigned long flags; + u16 mclken_div; + u32 reg; + + if (!mpc52xx_cdm_regs) + return -ENODEV; - return p_ipb_freq ? *p_ipb_freq : 0; + /* Calculate the parameters */ + params = &mpc52xx_cdm_psc_clk_params[psc]; + mclken_div = 0x8000 | (((mpc52xx_system_freq / freq_hz) - 1) & 0x1FF); + + spin_lock_irqsave(&mpc52xx_cdm_lock, flags); + + /* disable the clock before modifying frequency */ + reg = in_be32(mpc52xx_cdm_regs + MPC52xx_CDM_CLK_ENABLES_OFF); + reg &= ~params->enable; + + /* Set the new speed */ + out_be16(mpc52xx_cdm_regs + params->div_reg, mclken_div); + + /* Set the enable bit */ + reg |= params->enable; + out_be32(mpc52xx_cdm_regs + MPC52xx_CDM_CLK_ENABLES_OFF, reg); + + spin_unlock_irqrestore(&mpc52xx_cdm_lock, flags); + + return 0; } -EXPORT_SYMBOL(mpc52xx_find_ipb_freq); +EXPORT_SYMBOL_GPL(mpc52xx_cdm_set_psc_clk); +/** + * mpc5200_setup_clocks: called by platform code to setup clock frequencies + */ +void mpc5200_setup_clocks(void) +{ + struct device_node *node; + const unsigned int *prop = NULL; + + node = of_find_compatible_node(NULL, NULL, "fsl,mpc5200"); + if (!node) + node = of_find_compatible_node(NULL, NULL, "mpc5200"); + if (!node) { + printk(KERN_ERR"mpc5200_setup_clocks: could not find soc node\n"); + return; + } + + prop = of_get_property(node, "system-frequency", NULL); + if (prop) + mpc52xx_system_freq = *prop; + + prop = of_get_property(node, "bus-frequency", NULL); + if (prop) + mpc52xx_ipb_freq = *prop; + of_node_put(node); + + mpc52xx_cdm_regs = mpc52xx_find_and_map("fsl,mpc5200-cdm"); + if (!mpc52xx_cdm_regs) + mpc52xx_cdm_regs = mpc52xx_find_and_map("mpc5200-cdm"); +} /* * Configure the XLB arbiter settings to match what Linux expects. @@ -176,3 +257,4 @@ mpc52xx_restart(char *cmd) while (1); } + diff --git a/include/asm-powerpc/mpc52xx.h b/include/asm-powerpc/mpc52xx.h index fcb2ebb..08eb714 100644 --- a/include/asm-powerpc/mpc52xx.h +++ b/include/asm-powerpc/mpc52xx.h @@ -208,6 +208,7 @@ struct mpc52xx_cdm { u16 fd_counters; /* CDM + 0x12 reg4 byte2,3 */ u32 clk_enables; /* CDM + 0x14 reg5 */ +#define MPC52xx_CDM_CLK_ENABLES_OFF 0x14 u8 osc_disable; /* CDM + 0x18 reg6 byte0 */ u8 reserved0[3]; /* CDM + 0x19 reg6 byte1,2,3 */ @@ -228,15 +229,19 @@ struct mpc52xx_cdm { u16 reserved4; /* CDM + 0x28 reg10 byte0,1 */ u16 mclken_div_psc1; /* CDM + 0x2a reg10 byte2,3 */ +#define MPC52xx_CDM_MCLKEN_DIV_PSC1_OFF 0x2a u16 reserved5; /* CDM + 0x2c reg11 byte0,1 */ u16 mclken_div_psc2; /* CDM + 0x2e reg11 byte2,3 */ +#define MPC52xx_CDM_MCLKEN_DIV_PSC2_OFF 0x2e u16 reserved6; /* CDM + 0x30 reg12 byte0,1 */ u16 mclken_div_psc3; /* CDM + 0x32 reg12 byte2,3 */ +#define MPC52xx_CDM_MCLKEN_DIV_PSC3_OFF 0x32 u16 reserved7; /* CDM + 0x34 reg13 byte0,1 */ u16 mclken_div_psc6; /* CDM + 0x36 reg13 byte2,3 */ +#define MPC52xx_CDM_MCLKEN_DIV_PSC6_OFF 0x36 }; #endif /* __ASSEMBLY__ */ @@ -251,6 +256,8 @@ struct mpc52xx_cdm { extern void __iomem * mpc52xx_find_and_map(const char *); extern void __iomem * mpc52xx_find_and_map_path(const char *path); extern unsigned int mpc52xx_find_ipb_freq(struct device_node *node); +extern int mpc52xx_cdm_set_psc_clk(int psc, u32 freq_hz); +extern void mpc5200_setup_clocks(void); extern void mpc5200_setup_xlb_arbiter(void); extern void mpc52xx_declare_of_platform_devices(void); _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@ozlabs.org https://ozlabs.org/mailman/listinfo/linuxppc-dev