diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-30 17:07:18 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-30 17:07:18 -0800 |
commit | 4bcec913d0a98d991c750034a04675443d1f10b5 (patch) | |
tree | ce2e960ba26d50f09b6a7bd864b2b4d3c275e69b /drivers | |
parent | 03c7287dd22c18815964219c9a2e75054cd004df (diff) | |
parent | f878f84373aefda7f041a74b24a83b8b7dec1cf0 (diff) |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
Pull more powerpc bits from Ben Herrenschmidt:
"Here are a few more powerpc bits for this merge window. The bulk is
made of two pull requests from Scott and Anatolij that I had missed
previously (they arrived while I was away). Since both their branches
are in -next independently, and the content has been around for a
little while, they can still go in.
The rest is mostly bug and regression fixes, a small series of
cleanups to our pseries cpuidle code (including moving it to the right
place), and one new cpuidle bakend for the powernv platform. I also
wired up the new sched_attr syscalls"
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: (37 commits)
powerpc: Wire up sched_setattr and sched_getattr syscalls
powerpc/hugetlb: Replace __get_cpu_var with get_cpu_var
powerpc: Make sure "cache" directory is removed when offlining cpu
powerpc/mm: Fix mmap errno when MAP_FIXED is set and mapping exceeds the allowed address space
powerpc/powernv/cpuidle: Back-end cpuidle driver for powernv platform.
powerpc/pseries/cpuidle: smt-snooze-delay cleanup.
powerpc/pseries/cpuidle: Remove MAX_IDLE_STATE macro.
powerpc/pseries/cpuidle: Make cpuidle-pseries backend driver a non-module.
powerpc/pseries/cpuidle: Use cpuidle_register() for initialisation.
powerpc/pseries/cpuidle: Move processor_idle.c to drivers/cpuidle.
powerpc: Fix 32-bit frames for signals delivered when transactional
powerpc/iommu: Fix initialisation of DART iommu table
powerpc/numa: Fix decimal permissions
powerpc/mm: Fix compile error of pgtable-ppc64.h
powerpc: Fix hw breakpoints on !HAVE_HW_BREAKPOINT configurations
clk: corenet: Adds the clock binding
powerpc/booke64: Guard e6500 tlb handler with CONFIG_PPC_FSL_BOOK3E
powerpc/512x: dts: add MPC5125 clock specs
powerpc/512x: clk: support MPC5121/5123/5125 SoC variants
powerpc/512x: clk: enforce even SDHC divider values
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/cpuidle/Kconfig | 5 | ||||
-rw-r--r-- | drivers/cpuidle/Kconfig.powerpc | 20 | ||||
-rw-r--r-- | drivers/cpuidle/Makefile | 5 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle-powernv.c | 169 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle-pseries.c | 267 | ||||
-rw-r--r-- | drivers/media/platform/fsl-viu.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/mpc5121_nfc.c | 2 | ||||
-rw-r--r-- | drivers/net/can/mscan/mpc5xxx_can.c | 270 | ||||
-rw-r--r-- | drivers/spi/spi-mpc512x-psc.c | 26 | ||||
-rw-r--r-- | drivers/tty/serial/mpc52xx_uart.c | 90 | ||||
-rw-r--r-- | drivers/usb/host/fsl-mph-dr-of.c | 13 |
11 files changed, 720 insertions, 149 deletions
diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index b3fb81d7cf04..f04e25f6c98d 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -35,6 +35,11 @@ depends on ARM source "drivers/cpuidle/Kconfig.arm" endmenu +menu "POWERPC CPU Idle Drivers" +depends on PPC +source "drivers/cpuidle/Kconfig.powerpc" +endmenu + endif config ARCH_NEEDS_CPU_IDLE_COUPLED diff --git a/drivers/cpuidle/Kconfig.powerpc b/drivers/cpuidle/Kconfig.powerpc new file mode 100644 index 000000000000..66c3a09574e9 --- /dev/null +++ b/drivers/cpuidle/Kconfig.powerpc @@ -0,0 +1,20 @@ +# +# POWERPC CPU Idle Drivers +# +config PSERIES_CPUIDLE + bool "Cpuidle driver for pSeries platforms" + depends on CPU_IDLE + depends on PPC_PSERIES + default y + help + Select this option to enable processor idle state management + through cpuidle subsystem. + +config POWERNV_CPUIDLE + bool "Cpuidle driver for powernv platforms" + depends on CPU_IDLE + depends on PPC_POWERNV + default y + help + Select this option to enable processor idle state management + through cpuidle subsystem. diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 527be28e5c1e..f71ae1b373c5 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -13,3 +13,8 @@ obj-$(CONFIG_ARM_KIRKWOOD_CPUIDLE) += cpuidle-kirkwood.o obj-$(CONFIG_ARM_ZYNQ_CPUIDLE) += cpuidle-zynq.o obj-$(CONFIG_ARM_U8500_CPUIDLE) += cpuidle-ux500.o obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o + +############################################################################### +# POWERPC drivers +obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o +obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c new file mode 100644 index 000000000000..78fd174c57e8 --- /dev/null +++ b/drivers/cpuidle/cpuidle-powernv.c @@ -0,0 +1,169 @@ +/* + * cpuidle-powernv - idle state cpuidle driver. + * Adapted from drivers/cpuidle/cpuidle-pseries + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/moduleparam.h> +#include <linux/cpuidle.h> +#include <linux/cpu.h> +#include <linux/notifier.h> + +#include <asm/machdep.h> +#include <asm/firmware.h> + +struct cpuidle_driver powernv_idle_driver = { + .name = "powernv_idle", + .owner = THIS_MODULE, +}; + +static int max_idle_state; +static struct cpuidle_state *cpuidle_state_table; + +static int snooze_loop(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + local_irq_enable(); + set_thread_flag(TIF_POLLING_NRFLAG); + + while (!need_resched()) { + HMT_low(); + HMT_very_low(); + } + + HMT_medium(); + clear_thread_flag(TIF_POLLING_NRFLAG); + smp_mb(); + return index; +} + +static int nap_loop(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + power7_idle(); + return index; +} + +/* + * States for dedicated partition case. + */ +static struct cpuidle_state powernv_states[] = { + { /* Snooze */ + .name = "snooze", + .desc = "snooze", + .flags = CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 0, + .target_residency = 0, + .enter = &snooze_loop }, + { /* NAP */ + .name = "NAP", + .desc = "NAP", + .flags = CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 10, + .target_residency = 100, + .enter = &nap_loop }, +}; + +static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n, + unsigned long action, void *hcpu) +{ + int hotcpu = (unsigned long)hcpu; + struct cpuidle_device *dev = + per_cpu(cpuidle_devices, hotcpu); + + if (dev && cpuidle_get_driver()) { + switch (action) { + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + cpuidle_pause_and_lock(); + cpuidle_enable_device(dev); + cpuidle_resume_and_unlock(); + break; + + case CPU_DEAD: + case CPU_DEAD_FROZEN: + cpuidle_pause_and_lock(); + cpuidle_disable_device(dev); + cpuidle_resume_and_unlock(); + break; + + default: + return NOTIFY_DONE; + } + } + return NOTIFY_OK; +} + +static struct notifier_block setup_hotplug_notifier = { + .notifier_call = powernv_cpuidle_add_cpu_notifier, +}; + +/* + * powernv_cpuidle_driver_init() + */ +static int powernv_cpuidle_driver_init(void) +{ + int idle_state; + struct cpuidle_driver *drv = &powernv_idle_driver; + + drv->state_count = 0; + + for (idle_state = 0; idle_state < max_idle_state; ++idle_state) { + /* Is the state not enabled? */ + if (cpuidle_state_table[idle_state].enter == NULL) + continue; + + drv->states[drv->state_count] = /* structure copy */ + cpuidle_state_table[idle_state]; + + drv->state_count += 1; + } + + return 0; +} + +/* + * powernv_idle_probe() + * Choose state table for shared versus dedicated partition + */ +static int powernv_idle_probe(void) +{ + + if (cpuidle_disable != IDLE_NO_OVERRIDE) + return -ENODEV; + + if (firmware_has_feature(FW_FEATURE_OPALv3)) { + cpuidle_state_table = powernv_states; + max_idle_state = ARRAY_SIZE(powernv_states); + } else + return -ENODEV; + + return 0; +} + +static int __init powernv_processor_idle_init(void) +{ + int retval; + + retval = powernv_idle_probe(); + if (retval) + return retval; + + powernv_cpuidle_driver_init(); + retval = cpuidle_register(&powernv_idle_driver, NULL); + if (retval) { + printk(KERN_DEBUG "Registration of powernv driver failed.\n"); + return retval; + } + + register_cpu_notifier(&setup_hotplug_notifier); + printk(KERN_DEBUG "powernv_idle_driver registered\n"); + return 0; +} + +device_initcall(powernv_processor_idle_init); diff --git a/drivers/cpuidle/cpuidle-pseries.c b/drivers/cpuidle/cpuidle-pseries.c new file mode 100644 index 000000000000..7ab564aa0b1c --- /dev/null +++ b/drivers/cpuidle/cpuidle-pseries.c @@ -0,0 +1,267 @@ +/* + * cpuidle-pseries - idle state cpuidle driver. + * Adapted from drivers/idle/intel_idle.c and + * drivers/acpi/processor_idle.c + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/moduleparam.h> +#include <linux/cpuidle.h> +#include <linux/cpu.h> +#include <linux/notifier.h> + +#include <asm/paca.h> +#include <asm/reg.h> +#include <asm/machdep.h> +#include <asm/firmware.h> +#include <asm/plpar_wrappers.h> + +struct cpuidle_driver pseries_idle_driver = { + .name = "pseries_idle", + .owner = THIS_MODULE, +}; + +static int max_idle_state; +static struct cpuidle_state *cpuidle_state_table; + +static inline void idle_loop_prolog(unsigned long *in_purr) +{ + *in_purr = mfspr(SPRN_PURR); + /* + * Indicate to the HV that we are idle. Now would be + * a good time to find other work to dispatch. + */ + get_lppaca()->idle = 1; +} + +static inline void idle_loop_epilog(unsigned long in_purr) +{ + u64 wait_cycles; + + wait_cycles = be64_to_cpu(get_lppaca()->wait_state_cycles); + wait_cycles += mfspr(SPRN_PURR) - in_purr; + get_lppaca()->wait_state_cycles = cpu_to_be64(wait_cycles); + get_lppaca()->idle = 0; +} + +static int snooze_loop(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + unsigned long in_purr; + + idle_loop_prolog(&in_purr); + local_irq_enable(); + set_thread_flag(TIF_POLLING_NRFLAG); + + while (!need_resched()) { + HMT_low(); + HMT_very_low(); + } + + HMT_medium(); + clear_thread_flag(TIF_POLLING_NRFLAG); + smp_mb(); + + idle_loop_epilog(in_purr); + + return index; +} + +static void check_and_cede_processor(void) +{ + /* + * Ensure our interrupt state is properly tracked, + * also checks if no interrupt has occurred while we + * were soft-disabled + */ + if (prep_irq_for_idle()) { + cede_processor(); +#ifdef CONFIG_TRACE_IRQFLAGS + /* Ensure that H_CEDE returns with IRQs on */ + if (WARN_ON(!(mfmsr() & MSR_EE))) + __hard_irq_enable(); +#endif + } +} + +static int dedicated_cede_loop(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + unsigned long in_purr; + + idle_loop_prolog(&in_purr); + get_lppaca()->donate_dedicated_cpu = 1; + + HMT_medium(); + check_and_cede_processor(); + + get_lppaca()->donate_dedicated_cpu = 0; + + idle_loop_epilog(in_purr); + + return index; +} + +static int shared_cede_loop(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + unsigned long in_purr; + + idle_loop_prolog(&in_purr); + + /* + * Yield the processor to the hypervisor. We return if + * an external interrupt occurs (which are driven prior + * to returning here) or if a prod occurs from another + * processor. When returning here, external interrupts + * are enabled. + */ + check_and_cede_processor(); + + idle_loop_epilog(in_purr); + + return index; +} + +/* + * States for dedicated partition case. + */ +static struct cpuidle_state dedicated_states[] = { + { /* Snooze */ + .name = "snooze", + .desc = "snooze", + .flags = CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 0, + .target_residency = 0, + .enter = &snooze_loop }, + { /* CEDE */ + .name = "CEDE", + .desc = "CEDE", + .flags = CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 10, + .target_residency = 100, + .enter = &dedicated_cede_loop }, +}; + +/* + * States for shared partition case. + */ +static struct cpuidle_state shared_states[] = { + { /* Shared Cede */ + .name = "Shared Cede", + .desc = "Shared Cede", + .flags = CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 0, + .target_residency = 0, + .enter = &shared_cede_loop }, +}; + +static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n, + unsigned long action, void *hcpu) +{ + int hotcpu = (unsigned long)hcpu; + struct cpuidle_device *dev = + per_cpu(cpuidle_devices, hotcpu); + + if (dev && cpuidle_get_driver()) { + switch (action) { + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + cpuidle_pause_and_lock(); + cpuidle_enable_device(dev); + cpuidle_resume_and_unlock(); + break; + + case CPU_DEAD: + case CPU_DEAD_FROZEN: + cpuidle_pause_and_lock(); + cpuidle_disable_device(dev); + cpuidle_resume_and_unlock(); + break; + + default: + return NOTIFY_DONE; + } + } + return NOTIFY_OK; +} + +static struct notifier_block setup_hotplug_notifier = { + .notifier_call = pseries_cpuidle_add_cpu_notifier, +}; + +/* + * pseries_cpuidle_driver_init() + */ +static int pseries_cpuidle_driver_init(void) +{ + int idle_state; + struct cpuidle_driver *drv = &pseries_idle_driver; + + drv->state_count = 0; + + for (idle_state = 0; idle_state < max_idle_state; ++idle_state) { + /* Is the state not enabled? */ + if (cpuidle_state_table[idle_state].enter == NULL) + continue; + + drv->states[drv->state_count] = /* structure copy */ + cpuidle_state_table[idle_state]; + + drv->state_count += 1; + } + + return 0; +} + +/* + * pseries_idle_probe() + * Choose state table for shared versus dedicated partition + */ +static int pseries_idle_probe(void) +{ + + if (cpuidle_disable != IDLE_NO_OVERRIDE) + return -ENODEV; + + if (firmware_has_feature(FW_FEATURE_SPLPAR)) { + if (lppaca_shared_proc(get_lppaca())) { + cpuidle_state_table = shared_states; + max_idle_state = ARRAY_SIZE(shared_states); + } else { + cpuidle_state_table = dedicated_states; + max_idle_state = ARRAY_SIZE(dedicated_states); + } + } else + return -ENODEV; + + return 0; +} + +static int __init pseries_processor_idle_init(void) +{ + int retval; + + retval = pseries_idle_probe(); + if (retval) + return retval; + + pseries_cpuidle_driver_init(); + retval = cpuidle_register(&pseries_idle_driver, NULL); + if (retval) { + printk(KERN_DEBUG "Registration of pseries driver failed.\n"); + return retval; + } + + register_cpu_notifier(&setup_hotplug_notifier); + printk(KERN_DEBUG "pseries_idle_driver registered\n"); + return 0; +} + +device_initcall(pseries_processor_idle_init); diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 6a232239ee8c..dbf0ce38a8e7 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -1580,7 +1580,7 @@ static int viu_of_probe(struct platform_device *op) } /* enable VIU clock */ - clk = devm_clk_get(&op->dev, "viu_clk"); + clk = devm_clk_get(&op->dev, "ipg"); if (IS_ERR(clk)) { dev_err(&op->dev, "failed to lookup the clock!\n"); ret = PTR_ERR(clk); diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index 61e2abc216ad..31ee7cfbc12b 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -729,7 +729,7 @@ static int mpc5121_nfc_probe(struct platform_device *op) of_node_put(rootnode); /* Enable NFC clock */ - clk = devm_clk_get(dev, "nfc_clk"); + clk = devm_clk_get(dev, "ipg"); if (IS_ERR(clk)) { dev_err(dev, "Unable to acquire NFC clock!\n"); retval = PTR_ERR(clk); diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c index 035e235e3118..44725296f72a 100644 --- a/drivers/net/can/mscan/mpc5xxx_can.c +++ b/drivers/net/can/mscan/mpc5xxx_can.c @@ -108,135 +108,170 @@ static u32 mpc52xx_can_get_clock(struct platform_device *ofdev, #endif /* CONFIG_PPC_MPC52xx */ #ifdef CONFIG_PPC_MPC512x -struct mpc512x_clockctl { - u32 spmr; /* System PLL Mode Reg */ - u32 sccr[2]; /* System Clk Ctrl Reg 1 & 2 */ - u32 scfr1; /* System Clk Freq Reg 1 */ - u32 scfr2; /* System Clk Freq Reg 2 */ - u32 reserved; - u32 bcr; /* Bread Crumb Reg */ - u32 pccr[12]; /* PSC Clk Ctrl Reg 0-11 */ - u32 spccr; /* SPDIF Clk Ctrl Reg */ - u32 cccr; /* CFM Clk Ctrl Reg */ - u32 dccr; /* DIU Clk Cnfg Reg */ - u32 mccr[4]; /* MSCAN Clk Ctrl Reg 1-3 */ -}; - -static struct of_device_id mpc512x_clock_ids[] = { - { .compatible = "fsl,mpc5121-clock", }, - {} -}; - static u32 mpc512x_can_get_clock(struct platform_device *ofdev, - const char *clock_name, int *mscan_clksrc) + const char *clock_source, int *mscan_clksrc) { - struct mpc512x_clockctl __iomem *clockctl; - struct device_node *np_clock; - struct clk *sys_clk, *ref_clk; - int plen, clockidx, clocksrc = -1; - u32 sys_freq, val, clockdiv = 1, freq = 0; - const u32 *pval; - - np_clock = of_find_matching_node(NULL, mpc512x_clock_ids); - if (!np_clock) { - dev_err(&ofdev->dev, "couldn't find clock node\n"); - return 0; - } - clockctl = of_iomap(np_clock, 0); - if (!clockctl) { - dev_err(&ofdev->dev, "couldn't map clock registers\n"); - goto exit_put; - } + struct device_node *np; + u32 clockdiv; + enum { + CLK_FROM_AUTO, + CLK_FROM_IPS, + CLK_FROM_SYS, + CLK_FROM_REF, + } clk_from; + struct clk *clk_in, *clk_can; + unsigned long freq_calc; + struct mscan_priv *priv; + struct clk *clk_ipg; - /* Determine the MSCAN device index from the peripheral's - * physical address. Register address offsets against the - * IMMR base are: 0x1300, 0x1380, 0x2300, 0x2380 + /* the caller passed in the clock source spec that was read from + * the device tree, get the optional clock divider as well */ - pval = of_get_property(ofdev->dev.of_node, "reg", &plen); - BUG_ON(!pval || plen < sizeof(*pval)); - clockidx = (*pval & 0x80) ? 1 : 0; - if (*pval & 0x2000) - clockidx += 2; + np = ofdev->dev.of_node; + clockdiv = 1; + of_property_read_u32(np, "fsl,mscan-clock-divider", &clockdiv); + dev_dbg(&ofdev->dev, "device tree specs: clk src[%s] div[%d]\n", + clock_source ? clock_source : "<NULL>", clockdiv); + + /* when clock-source is 'ip', the CANCTL1[CLKSRC] bit needs to + * get set, and the 'ips' clock is the input to the MSCAN + * component + * + * for clock-source values of 'ref' or 'sys' the CANCTL1[CLKSRC] + * bit needs to get cleared, an optional clock-divider may have + * been specified (the default value is 1), the appropriate + * MSCAN related MCLK is the input to the MSCAN component + * + * in the absence of a clock-source spec, first an optimal clock + * gets determined based on the 'sys' clock, if that fails the + * 'ref' clock is used + */ + clk_from = CLK_FROM_AUTO; + if (clock_source) { + /* interpret the device tree's spec for the clock source */ + if (!strcmp(clock_source, "ip")) + clk_from = CLK_FROM_IPS; + else if (!strcmp(clock_source, "sys")) + clk_from = CLK_FROM_SYS; + else if (!strcmp(clock_source, "ref")) + clk_from = CLK_FROM_REF; + else + goto err_invalid; + dev_dbg(&ofdev->dev, "got a clk source spec[%d]\n", clk_from); + } + if (clk_from == CLK_FROM_AUTO) { + /* no spec so far, try the 'sys' clock; round to the + * next MHz and see if we can get a multiple of 16MHz + */ + dev_dbg(&ofdev->dev, "no clk source spec, trying SYS\n"); + clk_in = devm_clk_get(&ofdev->dev, "sys"); + if (IS_ERR(clk_in)) + goto err_notavail; + freq_calc = clk_get_rate(clk_in); + freq_calc += 499999; + freq_calc /= 1000000; + freq_calc *= 1000000; + if ((freq_calc % 16000000) == 0) { + clk_from = CLK_FROM_SYS; + clockdiv = freq_calc / 16000000; + dev_dbg(&ofdev->dev, + "clk fit, sys[%lu] div[%d] freq[%lu]\n", + freq_calc, clockdiv, freq_calc / clockdiv); + } + } + if (clk_from == CLK_FROM_AUTO) { + /* no spec so far, use the 'ref' clock */ + dev_dbg(&ofdev->dev, "no clk source spec, trying REF\n"); + clk_in = devm_clk_get(&ofdev->dev, "ref"); + if (IS_ERR(clk_in)) + goto err_notavail; + clk_from = CLK_FROM_REF; + freq_calc = clk_get_rate(clk_in); + dev_dbg(&ofdev->dev, + "clk fit, ref[%lu] (no div) freq[%lu]\n", + freq_calc, freq_calc); + } - /* - * Clock source and divider selection: 3 different clock sources - * can be selected: "ip", "ref" or "sys". For the latter two, a - * clock divider can be defined as well. If the clock source is - * not specified by the device tree, we first try to find an - * optimal CAN source clock based on the system clock. If that - * is not posslible, the reference clock will be used. + /* select IPS or MCLK as the MSCAN input (returned to the caller), + * setup the MCLK mux source and rate if applicable, apply the + * optionally specified or derived above divider, and determine + * the actual resulting clock rate to return to the caller */ - if (clock_name && !strcmp(clock_name, "ip")) { + switch (clk_from) { + case CLK_FROM_IPS: + clk_can = devm_clk_get(&ofdev->dev, "ips"); + if (IS_ERR(clk_can)) + goto err_notavail; + priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); + priv->clk_can = clk_can; + freq_calc = clk_get_rate(clk_can); *mscan_clksrc = MSCAN_CLKSRC_IPS; - freq = mpc5xxx_get_bus_frequency(ofdev->dev.of_node); - } else { + dev_dbg(&ofdev->dev, "clk from IPS, clksrc[%d] freq[%lu]\n", + *mscan_clksrc, freq_calc); + break; + case CLK_FROM_SYS: + case CLK_FROM_REF: + clk_can = devm_clk_get(&ofdev->dev, "mclk"); + if (IS_ERR(clk_can)) + goto err_notavail; + priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); + priv->clk_can = clk_can; + if (clk_from == CLK_FROM_SYS) + clk_in = devm_clk_get(&ofdev->dev, "sys"); + if (clk_from == CLK_FROM_REF) + clk_in = devm_clk_get(&ofdev->dev, "ref"); + if (IS_ERR(clk_in)) + goto err_notavail; + clk_set_parent(clk_can, clk_in); + freq_calc = clk_get_rate(clk_in); + freq_calc /= clockdiv; + clk_set_rate(clk_can, freq_calc); + freq_calc = clk_get_rate(clk_can); *mscan_clksrc = MSCAN_CLKSRC_BUS; - - pval = of_get_property(ofdev->dev.of_node, - "fsl,mscan-clock-divider", &plen); - if (pval && plen == sizeof(*pval)) - clockdiv = *pval; - if (!clockdiv) - clockdiv = 1; - - if (!clock_name || !strcmp(clock_name, "sys")) { - sys_clk = devm_clk_get(&ofdev->dev, "sys_clk"); - if (IS_ERR(sys_clk)) { - dev_err(&ofdev->dev, "couldn't get sys_clk\n"); - goto exit_unmap; - } - /* Get and round up/down sys clock rate */ - sys_freq = 1000000 * - ((clk_get_rate(sys_clk) + 499999) / 1000000); - - if (!clock_name) { - /* A multiple of 16 MHz would be optimal */ - if ((sys_freq % 16000000) == 0) { - clocksrc = 0; - clockdiv = sys_freq / 16000000; - freq = sys_freq / clockdiv; - } - } else { - clocksrc = 0; - freq = sys_freq / clockdiv; - } - } - - if (clocksrc < 0) { - ref_clk = devm_clk_get(&ofdev->dev, "ref_clk"); - if (IS_ERR(ref_clk)) { - dev_err(&ofdev->dev, "couldn't get ref_clk\n"); - goto exit_unmap; - } - clocksrc = 1; - freq = clk_get_rate(ref_clk) / clockdiv; - } + dev_dbg(&ofdev->dev, "clk from MCLK, clksrc[%d] freq[%lu]\n", + *mscan_clksrc, freq_calc); + break; + default: + goto err_invalid; } - /* Disable clock */ - out_be32(&clockctl->mccr[clockidx], 0x0); - if (clocksrc >= 0) { - /* Set source and divider */ - val = (clocksrc << 14) | ((clockdiv - 1) << 17); - out_be32(&clockctl->mccr[clockidx], val); - /* Enable clock */ - out_be32(&clockctl->mccr[clockidx], val | 0x10000); - } + /* the above clk_can item is used for the bitrate, access to + * the peripheral's register set needs the clk_ipg item + */ + clk_ipg = devm_clk_get(&ofdev->dev, "ipg"); + if (IS_ERR(clk_ipg)) + goto err_notavail_ipg; + if (clk_prepare_enable(clk_ipg)) + goto err_notavail_ipg; + priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); + priv->clk_ipg = clk_ipg; + + /* return the determined clock source rate */ + return freq_calc; + +err_invalid: + dev_err(&ofdev->dev, "invalid clock source specification\n"); + /* clock source rate could not get determined */ + return 0; - /* Enable MSCAN clock domain */ - val = in_be32(&clockctl->sccr[1]); - if (!(val & (1 << 25))) - out_be32(&clockctl->sccr[1], val | (1 << 25)); +err_notavail: + dev_err(&ofdev->dev, "cannot acquire or setup bitrate clock source\n"); + /* clock source rate could not get determined */ + return 0; - dev_dbg(&ofdev->dev, "using '%s' with frequency divider %d\n", - *mscan_clksrc == MSCAN_CLKSRC_IPS ? "ips_clk" : - clocksrc == 1 ? "ref_clk" : "sys_clk", clockdiv); +err_notavail_ipg: + dev_err(&ofdev->dev, "cannot acquire or setup register clock\n"); + /* clock source rate could not get determined */ + return 0; +} -exit_unmap: - iounmap(clockctl); -exit_put: - of_node_put(np_clock); - return freq; +static void mpc512x_can_put_clock(struct platform_device *ofdev) +{ + struct mscan_priv *priv; + + priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); + if (priv->clk_ipg) + clk_disable_unprepare(priv->clk_ipg); } #else /* !CONFIG_PPC_MPC512x */ static u32 mpc512x_can_get_clock(struct platform_device *ofdev, @@ -244,6 +279,7 @@ static u32 mpc512x_can_get_clock(struct platform_device *ofdev, { return 0; } +#define mpc512x_can_put_clock NULL #endif /* CONFIG_PPC_MPC512x */ static const struct of_device_id mpc5xxx_can_table[]; @@ -385,11 +421,13 @@ static int mpc5xxx_can_resume(struct platform_device *ofdev) static const struct mpc5xxx_can_data mpc5200_can_data = { .type = MSCAN_TYPE_MPC5200, .get_clock = mpc52xx_can_get_clock, + /* .put_clock not applicable */ }; static const struct mpc5xxx_can_data mpc5121_can_data = { .type = MSCAN_TYPE_MPC5121, .get_clock = mpc512x_can_get_clock, + .put_clock = mpc512x_can_put_clock, }; static const struct of_device_id mpc5xxx_can_table[] = { diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index 46d2313f7c6f..5032141eeeec 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -40,6 +40,7 @@ struct mpc512x_psc_spi { unsigned int irq; u8 bits_per_word; struct clk *clk_mclk; + struct clk *clk_ipg; u32 mclk_rate; struct completion txisrdone; @@ -475,8 +476,6 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, struct spi_master *master; int ret; void *tempp; - int psc_num; - char clk_name[16]; struct clk *clk; master = spi_alloc_master(dev, sizeof *mps); @@ -519,9 +518,7 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, goto free_master; init_completion(&mps->txisrdone); - psc_num = master->bus_num; - snprintf(clk_name, sizeof(clk_name), "psc%d_mclk", psc_num); - clk = devm_clk_get(dev, clk_name); + clk = devm_clk_get(dev, "mclk"); if (IS_ERR(clk)) { ret = PTR_ERR(clk); goto free_master; @@ -532,17 +529,29 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, mps->clk_mclk = clk; mps->mclk_rate = clk_get_rate(clk); + clk = devm_clk_get(dev, "ipg"); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto free_mclk_clock; + } + ret = clk_prepare_enable(clk); + if (ret) + goto free_mclk_clock; + mps->clk_ipg = clk; + ret = mpc512x_psc_spi_port_config(master, mps); if (ret < 0) - goto free_clock; + goto free_ipg_clock; ret = devm_spi_register_master(dev, master); if (ret < 0) - goto free_clock; + goto free_ipg_clock; return ret; -free_clock: +free_ipg_clock: + clk_disable_unprepare(mps->clk_ipg); +free_mclk_clock: clk_disable_unprepare(mps->clk_mclk); free_master: spi_master_put(master); @@ -556,6 +565,7 @@ static int mpc512x_psc_spi_do_remove(struct device *dev) struct mpc512x_psc_spi *mps = spi_master_get_devdata(master); clk_disable_unprepare(mps->clk_mclk); + clk_disable_unprepare(mps->clk_ipg); return 0; } diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index ec06505e3ae6..97888f4900ec 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -421,6 +421,7 @@ struct psc_fifoc { static struct psc_fifoc __iomem *psc_fifoc; static unsigned int psc_fifoc_irq; +static struct clk *psc_fifoc_clk; static void mpc512x_psc_fifo_init(struct uart_port *port) { @@ -568,36 +569,73 @@ static unsigned int mpc512x_psc_set_baudrate(struct uart_port *port, /* Init PSC FIFO Controller */ static int __init mpc512x_psc_fifoc_init(void) { + int err; struct device_node *np; + struct clk *clk; + + /* default error code, potentially overwritten by clock calls */ + err = -ENODEV; np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-psc-fifo"); if (!np) { pr_err("%s: Can't find FIFOC node\n", __func__); - return -ENODEV; + goto out_err; } + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + /* backwards compat with device trees that lack clock specs */ + clk = clk_get_sys(np->name, "ipg"); + } + if (IS_ERR(clk)) { + pr_err("%s: Can't lookup FIFO clock\n", __func__); + err = PTR_ERR(clk); + goto out_ofnode_put; + } + if (clk_prepare_enable(clk)) { + pr_err("%s: Can't enable FIFO clock\n", __func__); + clk_put(clk); + goto out_ofnode_put; + } + psc_fifoc_clk = clk; + psc_fifoc = of_iomap(np, 0); if (!psc_fifoc) { pr_err("%s: Can't map FIFOC\n", __func__); - of_node_put(np); - return -ENODEV; + goto out_clk_disable; } psc_fifoc_irq = irq_of_parse_and_map(np, 0); - of_node_put(np); if (psc_fifoc_irq == 0) { pr_err("%s: Can't get FIFOC irq\n", __func__); - iounmap(psc_fifoc); - return -ENODEV; + goto out_unmap; } + of_node_put(np); return 0; + +out_unmap: + iounmap(psc_fifoc); +out_clk_disable: + clk_disable_unprepare(psc_fifoc_clk); + clk_put(psc_fifoc_clk); +out_ofnode_put: + of_node_put(np); +out_err: + return err; } static void __exit mpc512x_psc_fifoc_uninit(void) { iounmap(psc_fifoc); + + /* disable the clock, errors are not fatal */ + if (psc_fifoc_clk) { + clk_disable_unprepare(psc_fifoc_clk); + clk_put(psc_fifoc_clk); + psc_fifoc_clk = NULL; + } } /* 512x specific interrupt handler. The caller holds the port lock */ @@ -619,29 +657,55 @@ static irqreturn_t mpc512x_psc_handle_irq(struct uart_port *port) } static struct clk *psc_mclk_clk[MPC52xx_PSC_MAXNUM]; +static struct clk *psc_ipg_clk[MPC52xx_PSC_MAXNUM]; /* called from within the .request_port() callback (allocation) */ static int mpc512x_psc_alloc_clock(struct uart_port *port) { int psc_num; - char clk_name[16]; struct clk *clk; int err; psc_num = (port->mapbase & 0xf00) >> 8; - snprintf(clk_name, sizeof(clk_name), "psc%d_mclk", psc_num); - clk = devm_clk_get(port->dev, clk_name); + + clk = devm_clk_get(port->dev, "mclk"); if (IS_ERR(clk)) { dev_err(port->dev, "Failed to get MCLK!\n"); - return PTR_ERR(clk); + err = PTR_ERR(clk); + goto out_err; } err = clk_prepare_enable(clk); if (err) { dev_err(port->dev, "Failed to enable MCLK!\n"); - return err; + goto out_err; } psc_mclk_clk[psc_num] = clk; + + clk = devm_clk_get(port->dev, "ipg"); + if (IS_ERR(clk)) { + dev_err(port->dev, "Failed to get IPG clock!\n"); + err = PTR_ERR(clk); + goto out_err; + } + err = clk_prepare_enable(clk); + if (err) { + dev_err(port->dev, "Failed to enable IPG clock!\n"); + goto out_err; + } + psc_ipg_clk[psc_num] = clk; + return 0; + +out_err: + if (psc_mclk_clk[psc_num]) { + clk_disable_unprepare(psc_mclk_clk[psc_num]); + psc_mclk_clk[psc_num] = NULL; + } + if (psc_ipg_clk[psc_num]) { + clk_disable_unprepare(psc_ipg_clk[psc_num]); + psc_ipg_clk[psc_num] = NULL; + } + return err; } /* called from within the .release_port() callback (release) */ @@ -656,6 +720,10 @@ static void mpc512x_psc_relse_clock(struct uart_port *port) clk_disable_unprepare(clk); psc_mclk_clk[psc_num] = NULL; } + if (psc_ipg_clk[psc_num]) { + clk_disable_unprepare(psc_ipg_clk[psc_num]); + psc_ipg_clk[psc_num] = NULL; + } } /* implementation of the .clock() callback (enable/disable) */ diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index abd5050a4899..9162d1b6c0a3 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -261,19 +261,8 @@ int fsl_usb2_mpc5121_init(struct platform_device *pdev) struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); struct clk *clk; int err; - char clk_name[10]; - int base, clk_num; - - base = pdev->resource->start & 0xf000; - if (base == 0x3000) - clk_num = 1; - else if (base == 0x4000) - clk_num = 2; - else - return -ENODEV; - snprintf(clk_name, sizeof(clk_name), "usb%d_clk", clk_num); - clk = devm_clk_get(pdev->dev.parent, clk_name); + clk = devm_clk_get(pdev->dev.parent, "ipg"); if (IS_ERR(clk)) { dev_err(&pdev->dev, "failed to get clk\n"); return PTR_ERR(clk); |