From 5613570b133a294355d35fa66162afe7607a8abb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 1 Sep 2014 13:49:07 +0200 Subject: x86 / PM: Set IRQCHIP_SKIP_SET_WAKE for IOAPIC IRQ chip objects Set the IRQCHIP_SKIP_SET_WAKE for IOAPIC IRQ chip objects so that interrupts from them can work as wakeup interrupts for suspend-to-idle. After this change, running enable_irq_wake() on one of the IRQs in question will succeed and IRQD_WAKEUP_STATE will be set for it, so all of the suspend-to-idle wakeup mechanics introduced previously will work for it automatically. Signed-off-by: Rafael J. Wysocki --- arch/x86/kernel/apic/io_apic.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'arch') diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index 337ce5a9b15c..1183d545da1e 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c @@ -2623,6 +2623,7 @@ static struct irq_chip ioapic_chip __read_mostly = { .irq_eoi = ack_apic_level, .irq_set_affinity = native_ioapic_set_affinity, .irq_retrigger = ioapic_retrigger_irq, + .flags = IRQCHIP_SKIP_SET_WAKE, }; static inline void init_IO_APIC_traps(void) @@ -3173,6 +3174,7 @@ static struct irq_chip msi_chip = { .irq_ack = ack_apic_edge, .irq_set_affinity = msi_set_affinity, .irq_retrigger = ioapic_retrigger_irq, + .flags = IRQCHIP_SKIP_SET_WAKE, }; int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, @@ -3271,6 +3273,7 @@ static struct irq_chip dmar_msi_type = { .irq_ack = ack_apic_edge, .irq_set_affinity = dmar_msi_set_affinity, .irq_retrigger = ioapic_retrigger_irq, + .flags = IRQCHIP_SKIP_SET_WAKE, }; int arch_setup_dmar_msi(unsigned int irq) @@ -3321,6 +3324,7 @@ static struct irq_chip hpet_msi_type = { .irq_ack = ack_apic_edge, .irq_set_affinity = hpet_msi_set_affinity, .irq_retrigger = ioapic_retrigger_irq, + .flags = IRQCHIP_SKIP_SET_WAKE, }; int default_setup_hpet_msi(unsigned int irq, unsigned int id) @@ -3384,6 +3388,7 @@ static struct irq_chip ht_irq_chip = { .irq_ack = ack_apic_edge, .irq_set_affinity = ht_set_affinity, .irq_retrigger = ioapic_retrigger_irq, + .flags = IRQCHIP_SKIP_SET_WAKE, }; int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev) -- cgit v1.2.3-59-g8ed1b From 698616548528615052ccdff2f621f1fde3bd9a50 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 3 Sep 2014 12:52:20 +0200 Subject: ARM: shmobile: Drop dev_irq_safe from r8a7779 genpd config The dev_irq_safe configuration is redundant, genpd don't have any special treatmeant for handling it. Let's remove it. Signed-off-by: Ulf Hansson Reviewed-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- arch/arm/mach-shmobile/pm-r8a7779.c | 1 - 1 file changed, 1 deletion(-) (limited to 'arch') diff --git a/arch/arm/mach-shmobile/pm-r8a7779.c b/arch/arm/mach-shmobile/pm-r8a7779.c index 69f70b7f7fb2..82fe3d7f9662 100644 --- a/arch/arm/mach-shmobile/pm-r8a7779.c +++ b/arch/arm/mach-shmobile/pm-r8a7779.c @@ -87,7 +87,6 @@ static void r8a7779_init_pm_domain(struct r8a7779_pm_domain *r8a7779_pd) genpd->dev_ops.stop = pm_clk_suspend; genpd->dev_ops.start = pm_clk_resume; genpd->dev_ops.active_wakeup = pd_active_wakeup; - genpd->dev_irq_safe = true; genpd->power_off = pd_power_down; genpd->power_on = pd_power_up; -- cgit v1.2.3-59-g8ed1b From cec89297da88ae9e8df0e3b7ea2e7e464b038cc9 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 3 Sep 2014 12:52:21 +0200 Subject: ARM: shmobile: Drop dev_irq_safe from R-mobile genpd config The dev_irq_safe configuration is redundant, genpd don't have any special treatmeant for handling it. Let's remove it. Signed-off-by: Ulf Hansson Reviewed-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- arch/arm/mach-shmobile/pm-rmobile.c | 1 - 1 file changed, 1 deletion(-) (limited to 'arch') diff --git a/arch/arm/mach-shmobile/pm-rmobile.c b/arch/arm/mach-shmobile/pm-rmobile.c index ebdd16e94a84..818de2fddfd4 100644 --- a/arch/arm/mach-shmobile/pm-rmobile.c +++ b/arch/arm/mach-shmobile/pm-rmobile.c @@ -111,7 +111,6 @@ static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd) genpd->dev_ops.stop = pm_clk_suspend; genpd->dev_ops.start = pm_clk_resume; genpd->dev_ops.active_wakeup = rmobile_pd_active_wakeup; - genpd->dev_irq_safe = true; genpd->power_off = rmobile_pd_power_down; genpd->power_on = rmobile_pd_power_up; __rmobile_pd_power_up(rmobile_pd, false); -- cgit v1.2.3-59-g8ed1b From 0fcc455252d2ec766deb45bb735e32c7023811fe Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 3 Sep 2014 12:52:27 +0200 Subject: ARM: exynos: Leave disabling of unused PM domains to genpd Since genpd at late init, will try to disable unused PM domains we don't need to do it from here as well. Signed-off-by: Ulf Hansson Reviewed-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- arch/arm/mach-exynos/exynos.c | 1 - 1 file changed, 1 deletion(-) (limited to 'arch') diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index 6a24e111d6e1..b89e5f35db84 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -193,7 +193,6 @@ static void __init exynos_init_late(void) /* to be supported later */ return; - pm_genpd_poweroff_unused(); exynos_pm_init(); } -- cgit v1.2.3-59-g8ed1b From d40fce7e625dcecb196b1e4dba4dde19fdee919c Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 3 Sep 2014 12:52:28 +0200 Subject: ARM: s3c64xx: Leave disabling of unused PM domains to genpd Since genpd at late init, will try to disable unused PM domains we don't need to do it from the machine specific code as well. Signed-off-by: Ulf Hansson Reviewed-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- arch/arm/mach-s3c64xx/common.c | 5 ----- arch/arm/mach-s3c64xx/common.h | 7 ------- arch/arm/mach-s3c64xx/mach-anw6410.c | 1 - arch/arm/mach-s3c64xx/mach-crag6410.c | 1 - arch/arm/mach-s3c64xx/mach-hmt.c | 1 - arch/arm/mach-s3c64xx/mach-mini6410.c | 1 - arch/arm/mach-s3c64xx/mach-ncp.c | 1 - arch/arm/mach-s3c64xx/mach-real6410.c | 1 - arch/arm/mach-s3c64xx/mach-smartq5.c | 1 - arch/arm/mach-s3c64xx/mach-smartq7.c | 1 - arch/arm/mach-s3c64xx/mach-smdk6400.c | 1 - arch/arm/mach-s3c64xx/mach-smdk6410.c | 1 - arch/arm/mach-s3c64xx/pm.c | 7 ------- 13 files changed, 29 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-s3c64xx/common.c b/arch/arm/mach-s3c64xx/common.c index 5c45aae675b6..16547f2641a3 100644 --- a/arch/arm/mach-s3c64xx/common.c +++ b/arch/arm/mach-s3c64xx/common.c @@ -440,8 +440,3 @@ void s3c64xx_restart(enum reboot_mode mode, const char *cmd) /* if all else fails, or mode was for soft, jump to 0 */ soft_restart(0); } - -void __init s3c64xx_init_late(void) -{ - s3c64xx_pm_late_initcall(); -} diff --git a/arch/arm/mach-s3c64xx/common.h b/arch/arm/mach-s3c64xx/common.h index 7043e7a3a67e..9eb864412911 100644 --- a/arch/arm/mach-s3c64xx/common.h +++ b/arch/arm/mach-s3c64xx/common.h @@ -23,7 +23,6 @@ void s3c64xx_init_irq(u32 vic0, u32 vic1); void s3c64xx_init_io(struct map_desc *mach_desc, int size); void s3c64xx_restart(enum reboot_mode mode, const char *cmd); -void s3c64xx_init_late(void); void s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f, unsigned long xusbxti_f, bool is_s3c6400, void __iomem *reg_base); @@ -52,12 +51,6 @@ extern void s3c6410_map_io(void); #define s3c6410_init NULL #endif -#ifdef CONFIG_PM -int __init s3c64xx_pm_late_initcall(void); -#else -static inline int s3c64xx_pm_late_initcall(void) { return 0; } -#endif - #ifdef CONFIG_S3C64XX_PL080 extern struct pl08x_platform_data s3c64xx_dma0_plat_data; extern struct pl08x_platform_data s3c64xx_dma1_plat_data; diff --git a/arch/arm/mach-s3c64xx/mach-anw6410.c b/arch/arm/mach-s3c64xx/mach-anw6410.c index 60576dfbea8d..6224c67f5061 100644 --- a/arch/arm/mach-s3c64xx/mach-anw6410.c +++ b/arch/arm/mach-s3c64xx/mach-anw6410.c @@ -233,7 +233,6 @@ MACHINE_START(ANW6410, "A&W6410") .init_irq = s3c6410_init_irq, .map_io = anw6410_map_io, .init_machine = anw6410_machine_init, - .init_late = s3c64xx_init_late, .init_time = samsung_timer_init, .restart = s3c64xx_restart, MACHINE_END diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c index fe116334afda..10b913baab28 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410.c @@ -857,7 +857,6 @@ MACHINE_START(WLF_CRAGG_6410, "Wolfson Cragganmore 6410") .init_irq = s3c6410_init_irq, .map_io = crag6410_map_io, .init_machine = crag6410_machine_init, - .init_late = s3c64xx_init_late, .init_time = samsung_timer_init, .restart = s3c64xx_restart, MACHINE_END diff --git a/arch/arm/mach-s3c64xx/mach-hmt.c b/arch/arm/mach-s3c64xx/mach-hmt.c index 19e8feb908fd..e4b087c58ee6 100644 --- a/arch/arm/mach-s3c64xx/mach-hmt.c +++ b/arch/arm/mach-s3c64xx/mach-hmt.c @@ -277,7 +277,6 @@ MACHINE_START(HMT, "Airgoo-HMT") .init_irq = s3c6410_init_irq, .map_io = hmt_map_io, .init_machine = hmt_machine_init, - .init_late = s3c64xx_init_late, .init_time = samsung_timer_init, .restart = s3c64xx_restart, MACHINE_END diff --git a/arch/arm/mach-s3c64xx/mach-mini6410.c b/arch/arm/mach-s3c64xx/mach-mini6410.c index 9cbc07602ef3..ab61af50bfb9 100644 --- a/arch/arm/mach-s3c64xx/mach-mini6410.c +++ b/arch/arm/mach-s3c64xx/mach-mini6410.c @@ -366,7 +366,6 @@ MACHINE_START(MINI6410, "MINI6410") .init_irq = s3c6410_init_irq, .map_io = mini6410_map_io, .init_machine = mini6410_machine_init, - .init_late = s3c64xx_init_late, .init_time = samsung_timer_init, .restart = s3c64xx_restart, MACHINE_END diff --git a/arch/arm/mach-s3c64xx/mach-ncp.c b/arch/arm/mach-s3c64xx/mach-ncp.c index 4bae7dc49eea..80cb1446f69f 100644 --- a/arch/arm/mach-s3c64xx/mach-ncp.c +++ b/arch/arm/mach-s3c64xx/mach-ncp.c @@ -103,7 +103,6 @@ MACHINE_START(NCP, "NCP") .init_irq = s3c6410_init_irq, .map_io = ncp_map_io, .init_machine = ncp_machine_init, - .init_late = s3c64xx_init_late, .init_time = samsung_timer_init, .restart = s3c64xx_restart, MACHINE_END diff --git a/arch/arm/mach-s3c64xx/mach-real6410.c b/arch/arm/mach-s3c64xx/mach-real6410.c index fbad2af1ef16..85fa9598b980 100644 --- a/arch/arm/mach-s3c64xx/mach-real6410.c +++ b/arch/arm/mach-s3c64xx/mach-real6410.c @@ -335,7 +335,6 @@ MACHINE_START(REAL6410, "REAL6410") .init_irq = s3c6410_init_irq, .map_io = real6410_map_io, .init_machine = real6410_machine_init, - .init_late = s3c64xx_init_late, .init_time = samsung_timer_init, .restart = s3c64xx_restart, MACHINE_END diff --git a/arch/arm/mach-s3c64xx/mach-smartq5.c b/arch/arm/mach-s3c64xx/mach-smartq5.c index dec4c08e834f..33224ab36fac 100644 --- a/arch/arm/mach-s3c64xx/mach-smartq5.c +++ b/arch/arm/mach-s3c64xx/mach-smartq5.c @@ -156,7 +156,6 @@ MACHINE_START(SMARTQ5, "SmartQ 5") .init_irq = s3c6410_init_irq, .map_io = smartq_map_io, .init_machine = smartq5_machine_init, - .init_late = s3c64xx_init_late, .init_time = samsung_timer_init, .restart = s3c64xx_restart, MACHINE_END diff --git a/arch/arm/mach-s3c64xx/mach-smartq7.c b/arch/arm/mach-s3c64xx/mach-smartq7.c index 27b322069c7d..fc7fece22fb0 100644 --- a/arch/arm/mach-s3c64xx/mach-smartq7.c +++ b/arch/arm/mach-s3c64xx/mach-smartq7.c @@ -172,7 +172,6 @@ MACHINE_START(SMARTQ7, "SmartQ 7") .init_irq = s3c6410_init_irq, .map_io = smartq_map_io, .init_machine = smartq7_machine_init, - .init_late = s3c64xx_init_late, .init_time = samsung_timer_init, .restart = s3c64xx_restart, MACHINE_END diff --git a/arch/arm/mach-s3c64xx/mach-smdk6400.c b/arch/arm/mach-s3c64xx/mach-smdk6400.c index 910749768340..6f425126a735 100644 --- a/arch/arm/mach-s3c64xx/mach-smdk6400.c +++ b/arch/arm/mach-s3c64xx/mach-smdk6400.c @@ -92,7 +92,6 @@ MACHINE_START(SMDK6400, "SMDK6400") .init_irq = s3c6400_init_irq, .map_io = smdk6400_map_io, .init_machine = smdk6400_machine_init, - .init_late = s3c64xx_init_late, .init_time = samsung_timer_init, .restart = s3c64xx_restart, MACHINE_END diff --git a/arch/arm/mach-s3c64xx/mach-smdk6410.c b/arch/arm/mach-s3c64xx/mach-smdk6410.c index 1dc86d76b530..661eb662d051 100644 --- a/arch/arm/mach-s3c64xx/mach-smdk6410.c +++ b/arch/arm/mach-s3c64xx/mach-smdk6410.c @@ -705,7 +705,6 @@ MACHINE_START(SMDK6410, "SMDK6410") .init_irq = s3c6410_init_irq, .map_io = smdk6410_map_io, .init_machine = smdk6410_machine_init, - .init_late = s3c64xx_init_late, .init_time = samsung_timer_init, .restart = s3c64xx_restart, MACHINE_END diff --git a/arch/arm/mach-s3c64xx/pm.c b/arch/arm/mach-s3c64xx/pm.c index 6b37694fa335..aaf7bea4032f 100644 --- a/arch/arm/mach-s3c64xx/pm.c +++ b/arch/arm/mach-s3c64xx/pm.c @@ -347,10 +347,3 @@ static __init int s3c64xx_pm_initcall(void) return 0; } arch_initcall(s3c64xx_pm_initcall); - -int __init s3c64xx_pm_late_initcall(void) -{ - pm_genpd_poweroff_unused(); - - return 0; -} -- cgit v1.2.3-59-g8ed1b From a4a8c2c4962bb655e7152c53a0eb6ca31c47f159 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Fri, 19 Sep 2014 20:27:43 +0200 Subject: ARM: exynos: Move to generic PM domain DT bindings This patch moves Exynos PM domain code to use the new generic PM domain look-up framework introduced in previous patches, thus also allowing the new code to be compiled with CONFIG_ARCH_EXYNOS. This patch was originally submitted by Tomasz Figa when he was employed by Samsung. Link: http://marc.info/?l=linux-pm&m=139955336002083&w=2 Signed-off-by: Ulf Hansson Reviewed-by: Kevin Hilman Reviewed-by: Dmitry Torokhov Signed-off-by: Rafael J. Wysocki --- .../bindings/arm/exynos/power_domain.txt | 13 ++-- arch/arm/mach-exynos/pm_domains.c | 78 +--------------------- kernel/power/Kconfig | 2 +- 3 files changed, 8 insertions(+), 85 deletions(-) (limited to 'arch') diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt index 8b4f7b7fe88b..abde1ea8a119 100644 --- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt +++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt @@ -8,6 +8,8 @@ Required Properties: * samsung,exynos4210-pd - for exynos4210 type power domain. - reg: physical base address of the controller and length of memory mapped region. +- #power-domain-cells: number of cells in power domain specifier; + must be 0. Optional Properties: - clocks: List of clock handles. The parent clocks of the input clocks to the @@ -29,6 +31,7 @@ Example: lcd0: power-domain-lcd0 { compatible = "samsung,exynos4210-pd"; reg = <0x10023C00 0x10>; + #power-domain-cells = <0>; }; mfc_pd: power-domain@10044060 { @@ -37,12 +40,8 @@ Example: clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MOUT_SW_ACLK333>, <&clock CLK_MOUT_USER_ACLK333>; clock-names = "oscclk", "pclk0", "clk0"; + #power-domain-cells = <0>; }; -Example of the node using power domain: - - node { - /* ... */ - samsung,power-domain = <&lcd0>; - /* ... */ - }; +See Documentation/devicetree/bindings/power/power_domain.txt for description +of consumer-side bindings. diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index fd76e1b5a471..20f267121b3e 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -105,78 +105,6 @@ static int exynos_pd_power_off(struct generic_pm_domain *domain) return exynos_pd_power(domain, false); } -static void exynos_add_device_to_domain(struct exynos_pm_domain *pd, - struct device *dev) -{ - int ret; - - dev_dbg(dev, "adding to power domain %s\n", pd->pd.name); - - while (1) { - ret = pm_genpd_add_device(&pd->pd, dev); - if (ret != -EAGAIN) - break; - cond_resched(); - } - - pm_genpd_dev_need_restore(dev, true); -} - -static void exynos_remove_device_from_domain(struct device *dev) -{ - struct generic_pm_domain *genpd = dev_to_genpd(dev); - int ret; - - dev_dbg(dev, "removing from power domain %s\n", genpd->name); - - while (1) { - ret = pm_genpd_remove_device(genpd, dev); - if (ret != -EAGAIN) - break; - cond_resched(); - } -} - -static void exynos_read_domain_from_dt(struct device *dev) -{ - struct platform_device *pd_pdev; - struct exynos_pm_domain *pd; - struct device_node *node; - - node = of_parse_phandle(dev->of_node, "samsung,power-domain", 0); - if (!node) - return; - pd_pdev = of_find_device_by_node(node); - if (!pd_pdev) - return; - pd = platform_get_drvdata(pd_pdev); - exynos_add_device_to_domain(pd, dev); -} - -static int exynos_pm_notifier_call(struct notifier_block *nb, - unsigned long event, void *data) -{ - struct device *dev = data; - - switch (event) { - case BUS_NOTIFY_BIND_DRIVER: - if (dev->of_node) - exynos_read_domain_from_dt(dev); - - break; - - case BUS_NOTIFY_UNBOUND_DRIVER: - exynos_remove_device_from_domain(dev); - - break; - } - return NOTIFY_DONE; -} - -static struct notifier_block platform_nb = { - .notifier_call = exynos_pm_notifier_call, -}; - static __init int exynos4_pm_init_power_domain(void) { struct platform_device *pdev; @@ -202,7 +130,6 @@ static __init int exynos4_pm_init_power_domain(void) pd->base = of_iomap(np, 0); pd->pd.power_off = exynos_pd_power_off; pd->pd.power_on = exynos_pd_power_on; - pd->pd.of_node = np; pd->oscclk = clk_get(dev, "oscclk"); if (IS_ERR(pd->oscclk)) @@ -228,15 +155,12 @@ static __init int exynos4_pm_init_power_domain(void) clk_put(pd->oscclk); no_clk: - platform_set_drvdata(pdev, pd); - on = __raw_readl(pd->base + 0x4) & INT_LOCAL_PWR_EN; pm_genpd_init(&pd->pd, NULL, !on); + of_genpd_add_provider_simple(np, &pd->pd); } - bus_register_notifier(&platform_bus_type, &platform_nb); - return 0; } arch_initcall(exynos4_pm_init_power_domain); diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 897619b11fb2..bbef57f5bdfd 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -304,7 +304,7 @@ config PM_GENERIC_DOMAINS_RUNTIME config PM_GENERIC_DOMAINS_OF def_bool y - depends on PM_GENERIC_DOMAINS && OF && !ARCH_EXYNOS + depends on PM_GENERIC_DOMAINS && OF config CPU_PM bool -- cgit v1.2.3-59-g8ed1b From d2e5c871ed8a250f7ee1fe34dd52ed5757363fba Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Tue, 24 Jun 2014 16:20:28 +0100 Subject: drivers: cpuidle: initialize big.LITTLE driver through DT With the introduction of DT based idle states, CPUidle drivers for ARM can now initialize idle states data through properties in the device tree. This patch adds code to the big.LITTLE CPUidle driver to dynamically initialize idle states data through the updated device tree source file. Cc: Chander Kashyap Acked-by: Catalin Marinas Acked-by: Daniel Lezcano Signed-off-by: Lorenzo Pieralisi Signed-off-by: Daniel Lezcano --- arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 23 +++++++++++++++++++++++ drivers/cpuidle/Kconfig.arm | 1 + drivers/cpuidle/cpuidle-big_little.c | 19 +++++++++++++++++++ 3 files changed, 43 insertions(+) (limited to 'arch') diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts index a25c262326dc..322fd1519b09 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts @@ -38,6 +38,7 @@ compatible = "arm,cortex-a15"; reg = <0>; cci-control-port = <&cci_control1>; + cpu-idle-states = <&CLUSTER_SLEEP_BIG>; }; cpu1: cpu@1 { @@ -45,6 +46,7 @@ compatible = "arm,cortex-a15"; reg = <1>; cci-control-port = <&cci_control1>; + cpu-idle-states = <&CLUSTER_SLEEP_BIG>; }; cpu2: cpu@2 { @@ -52,6 +54,7 @@ compatible = "arm,cortex-a7"; reg = <0x100>; cci-control-port = <&cci_control2>; + cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>; }; cpu3: cpu@3 { @@ -59,6 +62,7 @@ compatible = "arm,cortex-a7"; reg = <0x101>; cci-control-port = <&cci_control2>; + cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>; }; cpu4: cpu@4 { @@ -66,6 +70,25 @@ compatible = "arm,cortex-a7"; reg = <0x102>; cci-control-port = <&cci_control2>; + cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>; + }; + + idle-states { + CLUSTER_SLEEP_BIG: cluster-sleep-big { + compatible = "arm,idle-state"; + local-timer-stop; + entry-latency-us = <1000>; + exit-latency-us = <700>; + min-residency-us = <2000>; + }; + + CLUSTER_SLEEP_LITTLE: cluster-sleep-little { + compatible = "arm,idle-state"; + local-timer-stop; + entry-latency-us = <1000>; + exit-latency-us = <500>; + min-residency-us = <2500>; + }; }; }; diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 38cff69ffe06..e339c7f2c2b7 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -7,6 +7,7 @@ config ARM_BIG_LITTLE_CPUIDLE depends on MCPM select ARM_CPU_SUSPEND select CPU_IDLE_MULTIPLE_DRIVERS + select DT_IDLE_STATES help Select this option to enable CPU idle driver for big.LITTLE based ARM systems. Driver manages CPUs coordination through MCPM and diff --git a/drivers/cpuidle/cpuidle-big_little.c b/drivers/cpuidle/cpuidle-big_little.c index 9342a94f417f..fbc00a1d3c48 100644 --- a/drivers/cpuidle/cpuidle-big_little.c +++ b/drivers/cpuidle/cpuidle-big_little.c @@ -24,6 +24,8 @@ #include #include +#include "dt_idle_states.h" + static int bl_enter_powerdown(struct cpuidle_device *dev, struct cpuidle_driver *drv, int idx); @@ -73,6 +75,12 @@ static struct cpuidle_driver bl_idle_little_driver = { .state_count = 2, }; +static const struct of_device_id bl_idle_state_match[] __initconst = { + { .compatible = "arm,idle-state", + .data = bl_enter_powerdown }, + { }, +}; + static struct cpuidle_driver bl_idle_big_driver = { .name = "big_idle", .owner = THIS_MODULE, @@ -191,6 +199,17 @@ static int __init bl_idle_init(void) if (ret) goto out_uninit_little; + /* Start at index 1, index 0 standard WFI */ + ret = dt_init_idle_driver(&bl_idle_big_driver, bl_idle_state_match, 1); + if (ret < 0) + goto out_uninit_big; + + /* Start at index 1, index 0 standard WFI */ + ret = dt_init_idle_driver(&bl_idle_little_driver, + bl_idle_state_match, 1); + if (ret < 0) + goto out_uninit_big; + ret = cpuidle_register(&bl_idle_little_driver, NULL); if (ret) goto out_uninit_big; -- cgit v1.2.3-59-g8ed1b From bbcf071969b20f356877c8067986be0a2dcaa2aa Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 9 Sep 2014 19:58:03 +0530 Subject: cpufreq: cpu0: rename driver and internals to 'cpufreq_dt' The naming convention of this driver was always under the scanner, people complained that it should have a more generic name than cpu0, as it manages all CPUs that are sharing clock lines. Also, in future it will be modified to support any number of clusters with separate clock/voltage lines. Lets rename it to 'cpufreq_dt' from 'cpufreq_cpu0'. Tested-by: Stephen Boyd Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- .../devicetree/bindings/cpufreq/cpufreq-cpu0.txt | 64 ---- .../devicetree/bindings/cpufreq/cpufreq-dt.txt | 64 ++++ arch/arm/configs/mvebu_v7_defconfig | 2 +- arch/arm/mach-imx/imx27-dt.c | 2 +- arch/arm/mach-imx/mach-imx51.c | 2 +- arch/arm/mach-mvebu/pmsu.c | 2 +- arch/arm/mach-omap2/pm.c | 2 +- arch/arm/mach-shmobile/board-ape6evm-reference.c | 2 +- arch/arm/mach-shmobile/cpufreq.c | 2 +- arch/arm/mach-shmobile/setup-sh73a0.c | 4 +- arch/arm/mach-zynq/common.c | 2 +- drivers/cpufreq/Kconfig | 8 +- drivers/cpufreq/Kconfig.arm | 2 +- drivers/cpufreq/Makefile | 2 +- drivers/cpufreq/cpufreq-cpu0.c | 363 --------------------- drivers/cpufreq/cpufreq-dt.c | 363 +++++++++++++++++++++ drivers/cpufreq/exynos4210-cpufreq.c | 2 +- drivers/cpufreq/exynos4x12-cpufreq.c | 2 +- drivers/cpufreq/exynos5250-cpufreq.c | 2 +- drivers/cpufreq/highbank-cpufreq.c | 6 +- drivers/cpufreq/s5pv210-cpufreq.c | 2 +- 21 files changed, 450 insertions(+), 450 deletions(-) delete mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt delete mode 100644 drivers/cpufreq/cpufreq-cpu0.c create mode 100644 drivers/cpufreq/cpufreq-dt.c (limited to 'arch') diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt deleted file mode 100644 index 366690cb86a3..000000000000 --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt +++ /dev/null @@ -1,64 +0,0 @@ -Generic CPU0 cpufreq driver - -It is a generic cpufreq driver for CPU0 frequency management. It -supports both uniprocessor (UP) and symmetric multiprocessor (SMP) -systems which share clock and voltage across all CPUs. - -Both required and optional properties listed below must be defined -under node /cpus/cpu@0. - -Required properties: -- None - -Optional properties: -- operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt for - details. OPPs *must* be supplied either via DT, i.e. this property, or - populated at runtime. -- clock-latency: Specify the possible maximum transition latency for clock, - in unit of nanoseconds. -- voltage-tolerance: Specify the CPU voltage tolerance in percentage. -- #cooling-cells: -- cooling-min-level: -- cooling-max-level: - Please refer to Documentation/devicetree/bindings/thermal/thermal.txt. - -Examples: - -cpus { - #address-cells = <1>; - #size-cells = <0>; - - cpu@0 { - compatible = "arm,cortex-a9"; - reg = <0>; - next-level-cache = <&L2>; - operating-points = < - /* kHz uV */ - 792000 1100000 - 396000 950000 - 198000 850000 - >; - clock-latency = <61036>; /* two CLK32 periods */ - #cooling-cells = <2>; - cooling-min-level = <0>; - cooling-max-level = <2>; - }; - - cpu@1 { - compatible = "arm,cortex-a9"; - reg = <1>; - next-level-cache = <&L2>; - }; - - cpu@2 { - compatible = "arm,cortex-a9"; - reg = <2>; - next-level-cache = <&L2>; - }; - - cpu@3 { - compatible = "arm,cortex-a9"; - reg = <3>; - next-level-cache = <&L2>; - }; -}; diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt new file mode 100644 index 000000000000..e41c98ffbccb --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt @@ -0,0 +1,64 @@ +Generic cpufreq driver + +It is a generic DT based cpufreq driver for frequency management. It supports +both uniprocessor (UP) and symmetric multiprocessor (SMP) systems which share +clock and voltage across all CPUs. + +Both required and optional properties listed below must be defined +under node /cpus/cpu@0. + +Required properties: +- None + +Optional properties: +- operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt for + details. OPPs *must* be supplied either via DT, i.e. this property, or + populated at runtime. +- clock-latency: Specify the possible maximum transition latency for clock, + in unit of nanoseconds. +- voltage-tolerance: Specify the CPU voltage tolerance in percentage. +- #cooling-cells: +- cooling-min-level: +- cooling-max-level: + Please refer to Documentation/devicetree/bindings/thermal/thermal.txt. + +Examples: + +cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a9"; + reg = <0>; + next-level-cache = <&L2>; + operating-points = < + /* kHz uV */ + 792000 1100000 + 396000 950000 + 198000 850000 + >; + clock-latency = <61036>; /* two CLK32 periods */ + #cooling-cells = <2>; + cooling-min-level = <0>; + cooling-max-level = <2>; + }; + + cpu@1 { + compatible = "arm,cortex-a9"; + reg = <1>; + next-level-cache = <&L2>; + }; + + cpu@2 { + compatible = "arm,cortex-a9"; + reg = <2>; + next-level-cache = <&L2>; + }; + + cpu@3 { + compatible = "arm,cortex-a9"; + reg = <3>; + next-level-cache = <&L2>; + }; +}; diff --git a/arch/arm/configs/mvebu_v7_defconfig b/arch/arm/configs/mvebu_v7_defconfig index fdfda1fa9521..7309988b0f1f 100644 --- a/arch/arm/configs/mvebu_v7_defconfig +++ b/arch/arm/configs/mvebu_v7_defconfig @@ -32,7 +32,7 @@ CONFIG_ARM_ATAG_DTB_COMPAT=y CONFIG_CPU_IDLE=y CONFIG_ARM_MVEBU_V7_CPUIDLE=y CONFIG_CPU_FREQ=y -CONFIG_CPUFREQ_GENERIC=y +CONFIG_CPUFREQ_DT=y CONFIG_VFP=y CONFIG_NET=y CONFIG_INET=y diff --git a/arch/arm/mach-imx/imx27-dt.c b/arch/arm/mach-imx/imx27-dt.c index 080e66c6a1d0..dc8f1a6f45f2 100644 --- a/arch/arm/mach-imx/imx27-dt.c +++ b/arch/arm/mach-imx/imx27-dt.c @@ -20,7 +20,7 @@ static void __init imx27_dt_init(void) { - struct platform_device_info devinfo = { .name = "cpufreq-cpu0", }; + struct platform_device_info devinfo = { .name = "cpufreq-dt", }; mxc_arch_reset_init_dt(); diff --git a/arch/arm/mach-imx/mach-imx51.c b/arch/arm/mach-imx/mach-imx51.c index c77deb3f0893..2c5fcaf8675b 100644 --- a/arch/arm/mach-imx/mach-imx51.c +++ b/arch/arm/mach-imx/mach-imx51.c @@ -51,7 +51,7 @@ static void __init imx51_ipu_mipi_setup(void) static void __init imx51_dt_init(void) { - struct platform_device_info devinfo = { .name = "cpufreq-cpu0", }; + struct platform_device_info devinfo = { .name = "cpufreq-dt", }; mxc_arch_reset_init_dt(); imx51_ipu_mipi_setup(); diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index 8a70a51533fd..bbd8664d1bac 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c @@ -644,7 +644,7 @@ static int __init armada_xp_pmsu_cpufreq_init(void) } } - platform_device_register_simple("cpufreq-generic", -1, NULL, 0); + platform_device_register_simple("cpufreq-dt", -1, NULL, 0); return 0; } diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index 828aee9ea6a8..58920bc8807b 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -282,7 +282,7 @@ static inline void omap_init_cpufreq(void) if (!of_have_populated_dt()) devinfo.name = "omap-cpufreq"; else - devinfo.name = "cpufreq-cpu0"; + devinfo.name = "cpufreq-dt"; platform_device_register_full(&devinfo); } diff --git a/arch/arm/mach-shmobile/board-ape6evm-reference.c b/arch/arm/mach-shmobile/board-ape6evm-reference.c index 2f7723e5fe91..0110751da511 100644 --- a/arch/arm/mach-shmobile/board-ape6evm-reference.c +++ b/arch/arm/mach-shmobile/board-ape6evm-reference.c @@ -50,7 +50,7 @@ static void __init ape6evm_add_standard_devices(void) r8a73a4_add_dt_devices(); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); - platform_device_register_simple("cpufreq-cpu0", -1, NULL, 0); + platform_device_register_simple("cpufreq-dt", -1, NULL, 0); } static const char *ape6evm_boards_compat_dt[] __initdata = { diff --git a/arch/arm/mach-shmobile/cpufreq.c b/arch/arm/mach-shmobile/cpufreq.c index 8a24b2be46ae..57fbff024dcd 100644 --- a/arch/arm/mach-shmobile/cpufreq.c +++ b/arch/arm/mach-shmobile/cpufreq.c @@ -12,6 +12,6 @@ int __init shmobile_cpufreq_init(void) { - platform_device_register_simple("cpufreq-cpu0", -1, NULL, 0); + platform_device_register_simple("cpufreq-dt", -1, NULL, 0); return 0; } diff --git a/arch/arm/mach-shmobile/setup-sh73a0.c b/arch/arm/mach-shmobile/setup-sh73a0.c index 2c802ae9b241..15b990cd8c70 100644 --- a/arch/arm/mach-shmobile/setup-sh73a0.c +++ b/arch/arm/mach-shmobile/setup-sh73a0.c @@ -775,7 +775,7 @@ void __init sh73a0_add_early_devices(void) void __init sh73a0_add_standard_devices_dt(void) { - struct platform_device_info devinfo = { .name = "cpufreq-cpu0", .id = -1, }; + struct platform_device_info devinfo = { .name = "cpufreq-dt", .id = -1, }; /* clocks are setup late during boot in the case of DT */ sh73a0_clock_init(); @@ -784,7 +784,7 @@ void __init sh73a0_add_standard_devices_dt(void) ARRAY_SIZE(sh73a0_devices_dt)); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); - /* Instantiate cpufreq-cpu0 */ + /* Instantiate cpufreq-dt */ platform_device_register_full(&devinfo); } diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c index 31a6fa40ba37..ec03ec40e9c6 100644 --- a/arch/arm/mach-zynq/common.c +++ b/arch/arm/mach-zynq/common.c @@ -104,7 +104,7 @@ static int __init zynq_get_revision(void) */ static void __init zynq_init_machine(void) { - struct platform_device_info devinfo = { .name = "cpufreq-cpu0", }; + struct platform_device_info devinfo = { .name = "cpufreq-dt", }; struct soc_device_attribute *soc_dev_attr; struct soc_device *soc_dev; struct device *parent = NULL; diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index ffe350f86bca..3489f8f5fada 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -183,14 +183,14 @@ config CPU_FREQ_GOV_CONSERVATIVE If in doubt, say N. -config GENERIC_CPUFREQ_CPU0 - tristate "Generic CPU0 cpufreq driver" +config CPUFREQ_DT + tristate "Generic DT based cpufreq driver" depends on HAVE_CLK && OF - # if CPU_THERMAL is on and THERMAL=m, CPU0 cannot be =y: + # if CPU_THERMAL is on and THERMAL=m, CPUFREQ_DT cannot be =y: depends on !CPU_THERMAL || THERMAL select PM_OPP help - This adds a generic cpufreq driver for CPU0 frequency management. + This adds a generic DT based cpufreq driver for frequency management. It supports both uniprocessor (UP) and symmetric multiprocessor (SMP) systems which share clock and voltage across all CPUs. diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 7364a538e056..48ed28b789f7 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -92,7 +92,7 @@ config ARM_EXYNOS_CPU_FREQ_BOOST_SW config ARM_HIGHBANK_CPUFREQ tristate "Calxeda Highbank-based" - depends on ARCH_HIGHBANK && GENERIC_CPUFREQ_CPU0 && REGULATOR + depends on ARCH_HIGHBANK && CPUFREQ_DT && REGULATOR default m help This adds the CPUFreq driver for Calxeda Highbank SoC diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index db6d9a2fea4d..40c53dc1937e 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o -obj-$(CONFIG_GENERIC_CPUFREQ_CPU0) += cpufreq-cpu0.o +obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o ################################################################################## # x86 drivers. diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c deleted file mode 100644 index a5f8c5f5f4f4..000000000000 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright (C) 2012 Freescale Semiconductor, Inc. - * - * Copyright (C) 2014 Linaro. - * Viresh Kumar - * - * The OPP code in function cpu0_set_target() is reused from - * drivers/cpufreq/omap-cpufreq.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct private_data { - struct device *cpu_dev; - struct regulator *cpu_reg; - struct thermal_cooling_device *cdev; - unsigned int voltage_tolerance; /* in percentage */ -}; - -static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) -{ - struct dev_pm_opp *opp; - struct cpufreq_frequency_table *freq_table = policy->freq_table; - struct clk *cpu_clk = policy->clk; - struct private_data *priv = policy->driver_data; - struct device *cpu_dev = priv->cpu_dev; - struct regulator *cpu_reg = priv->cpu_reg; - unsigned long volt = 0, volt_old = 0, tol = 0; - unsigned int old_freq, new_freq; - long freq_Hz, freq_exact; - int ret; - - freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); - if (freq_Hz <= 0) - freq_Hz = freq_table[index].frequency * 1000; - - freq_exact = freq_Hz; - new_freq = freq_Hz / 1000; - old_freq = clk_get_rate(cpu_clk) / 1000; - - if (!IS_ERR(cpu_reg)) { - rcu_read_lock(); - opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); - if (IS_ERR(opp)) { - rcu_read_unlock(); - dev_err(cpu_dev, "failed to find OPP for %ld\n", - freq_Hz); - return PTR_ERR(opp); - } - volt = dev_pm_opp_get_voltage(opp); - rcu_read_unlock(); - tol = volt * priv->voltage_tolerance / 100; - volt_old = regulator_get_voltage(cpu_reg); - } - - dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", - old_freq / 1000, volt_old ? volt_old / 1000 : -1, - new_freq / 1000, volt ? volt / 1000 : -1); - - /* scaling up? scale voltage before frequency */ - if (!IS_ERR(cpu_reg) && new_freq > old_freq) { - ret = regulator_set_voltage_tol(cpu_reg, volt, tol); - if (ret) { - dev_err(cpu_dev, "failed to scale voltage up: %d\n", - ret); - return ret; - } - } - - ret = clk_set_rate(cpu_clk, freq_exact); - if (ret) { - dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); - if (!IS_ERR(cpu_reg)) - regulator_set_voltage_tol(cpu_reg, volt_old, tol); - return ret; - } - - /* scaling down? scale voltage after frequency */ - if (!IS_ERR(cpu_reg) && new_freq < old_freq) { - ret = regulator_set_voltage_tol(cpu_reg, volt, tol); - if (ret) { - dev_err(cpu_dev, "failed to scale voltage down: %d\n", - ret); - clk_set_rate(cpu_clk, old_freq * 1000); - } - } - - return ret; -} - -static int allocate_resources(int cpu, struct device **cdev, - struct regulator **creg, struct clk **cclk) -{ - struct device *cpu_dev; - struct regulator *cpu_reg; - struct clk *cpu_clk; - int ret = 0; - char *reg_cpu0 = "cpu0", *reg_cpu = "cpu", *reg; - - cpu_dev = get_cpu_device(cpu); - if (!cpu_dev) { - pr_err("failed to get cpu%d device\n", cpu); - return -ENODEV; - } - - /* Try "cpu0" for older DTs */ - if (!cpu) - reg = reg_cpu0; - else - reg = reg_cpu; - -try_again: - cpu_reg = regulator_get_optional(cpu_dev, reg); - if (IS_ERR(cpu_reg)) { - /* - * If cpu's regulator supply node is present, but regulator is - * not yet registered, we should try defering probe. - */ - if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) { - dev_dbg(cpu_dev, "cpu%d regulator not ready, retry\n", - cpu); - return -EPROBE_DEFER; - } - - /* Try with "cpu-supply" */ - if (reg == reg_cpu0) { - reg = reg_cpu; - goto try_again; - } - - dev_warn(cpu_dev, "failed to get cpu%d regulator: %ld\n", - cpu, PTR_ERR(cpu_reg)); - } - - cpu_clk = clk_get(cpu_dev, NULL); - if (IS_ERR(cpu_clk)) { - /* put regulator */ - if (!IS_ERR(cpu_reg)) - regulator_put(cpu_reg); - - ret = PTR_ERR(cpu_clk); - - /* - * If cpu's clk node is present, but clock is not yet - * registered, we should try defering probe. - */ - if (ret == -EPROBE_DEFER) - dev_dbg(cpu_dev, "cpu%d clock not ready, retry\n", cpu); - else - dev_err(cpu_dev, "failed to get cpu%d clock: %d\n", ret, - cpu); - } else { - *cdev = cpu_dev; - *creg = cpu_reg; - *cclk = cpu_clk; - } - - return ret; -} - -static int cpu0_cpufreq_init(struct cpufreq_policy *policy) -{ - struct cpufreq_frequency_table *freq_table; - struct thermal_cooling_device *cdev; - struct device_node *np; - struct private_data *priv; - struct device *cpu_dev; - struct regulator *cpu_reg; - struct clk *cpu_clk; - unsigned int transition_latency; - int ret; - - ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk); - if (ret) { - pr_err("%s: Failed to allocate resources\n: %d", __func__, ret); - return ret; - } - - np = of_node_get(cpu_dev->of_node); - if (!np) { - dev_err(cpu_dev, "failed to find cpu%d node\n", policy->cpu); - ret = -ENOENT; - goto out_put_reg_clk; - } - - /* OPPs might be populated at runtime, don't check for error here */ - of_init_opp_table(cpu_dev); - - ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); - if (ret) { - dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); - goto out_put_node; - } - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - ret = -ENOMEM; - goto out_free_table; - } - - of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance); - - if (of_property_read_u32(np, "clock-latency", &transition_latency)) - transition_latency = CPUFREQ_ETERNAL; - - if (!IS_ERR(cpu_reg)) { - struct dev_pm_opp *opp; - unsigned long min_uV, max_uV; - int i; - - /* - * OPP is maintained in order of increasing frequency, and - * freq_table initialised from OPP is therefore sorted in the - * same order. - */ - for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) - ; - rcu_read_lock(); - opp = dev_pm_opp_find_freq_exact(cpu_dev, - freq_table[0].frequency * 1000, true); - min_uV = dev_pm_opp_get_voltage(opp); - opp = dev_pm_opp_find_freq_exact(cpu_dev, - freq_table[i-1].frequency * 1000, true); - max_uV = dev_pm_opp_get_voltage(opp); - rcu_read_unlock(); - ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV); - if (ret > 0) - transition_latency += ret * 1000; - } - - /* - * For now, just loading the cooling device; - * thermal DT code takes care of matching them. - */ - if (of_find_property(np, "#cooling-cells", NULL)) { - cdev = of_cpufreq_cooling_register(np, cpu_present_mask); - if (IS_ERR(cdev)) - dev_err(cpu_dev, - "running cpufreq without cooling device: %ld\n", - PTR_ERR(cdev)); - else - priv->cdev = cdev; - } - of_node_put(np); - - priv->cpu_dev = cpu_dev; - priv->cpu_reg = cpu_reg; - policy->driver_data = priv; - - policy->clk = cpu_clk; - ret = cpufreq_generic_init(policy, freq_table, transition_latency); - if (ret) - goto out_cooling_unregister; - - return 0; - -out_cooling_unregister: - cpufreq_cooling_unregister(priv->cdev); - kfree(priv); -out_free_table: - dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); -out_put_node: - of_node_put(np); -out_put_reg_clk: - clk_put(cpu_clk); - if (!IS_ERR(cpu_reg)) - regulator_put(cpu_reg); - - return ret; -} - -static int cpu0_cpufreq_exit(struct cpufreq_policy *policy) -{ - struct private_data *priv = policy->driver_data; - - cpufreq_cooling_unregister(priv->cdev); - dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); - clk_put(policy->clk); - if (!IS_ERR(priv->cpu_reg)) - regulator_put(priv->cpu_reg); - kfree(priv); - - return 0; -} - -static struct cpufreq_driver cpu0_cpufreq_driver = { - .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, - .verify = cpufreq_generic_frequency_table_verify, - .target_index = cpu0_set_target, - .get = cpufreq_generic_get, - .init = cpu0_cpufreq_init, - .exit = cpu0_cpufreq_exit, - .name = "generic_cpu0", - .attr = cpufreq_generic_attr, -}; - -static int cpu0_cpufreq_probe(struct platform_device *pdev) -{ - struct device *cpu_dev; - struct regulator *cpu_reg; - struct clk *cpu_clk; - int ret; - - /* - * All per-cluster (CPUs sharing clock/voltages) initialization is done - * from ->init(). In probe(), we just need to make sure that clk and - * regulators are available. Else defer probe and retry. - * - * FIXME: Is checking this only for CPU0 sufficient ? - */ - ret = allocate_resources(0, &cpu_dev, &cpu_reg, &cpu_clk); - if (ret) - return ret; - - clk_put(cpu_clk); - if (!IS_ERR(cpu_reg)) - regulator_put(cpu_reg); - - ret = cpufreq_register_driver(&cpu0_cpufreq_driver); - if (ret) - dev_err(cpu_dev, "failed register driver: %d\n", ret); - - return ret; -} - -static int cpu0_cpufreq_remove(struct platform_device *pdev) -{ - cpufreq_unregister_driver(&cpu0_cpufreq_driver); - return 0; -} - -static struct platform_driver cpu0_cpufreq_platdrv = { - .driver = { - .name = "cpufreq-cpu0", - .owner = THIS_MODULE, - }, - .probe = cpu0_cpufreq_probe, - .remove = cpu0_cpufreq_remove, -}; -module_platform_driver(cpu0_cpufreq_platdrv); - -MODULE_AUTHOR("Viresh Kumar "); -MODULE_AUTHOR("Shawn Guo "); -MODULE_DESCRIPTION("Generic CPU0 cpufreq driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c new file mode 100644 index 000000000000..e00265066a75 --- /dev/null +++ b/drivers/cpufreq/cpufreq-dt.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2012 Freescale Semiconductor, Inc. + * + * Copyright (C) 2014 Linaro. + * Viresh Kumar + * + * The OPP code in function set_target() is reused from + * drivers/cpufreq/omap-cpufreq.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct private_data { + struct device *cpu_dev; + struct regulator *cpu_reg; + struct thermal_cooling_device *cdev; + unsigned int voltage_tolerance; /* in percentage */ +}; + +static int set_target(struct cpufreq_policy *policy, unsigned int index) +{ + struct dev_pm_opp *opp; + struct cpufreq_frequency_table *freq_table = policy->freq_table; + struct clk *cpu_clk = policy->clk; + struct private_data *priv = policy->driver_data; + struct device *cpu_dev = priv->cpu_dev; + struct regulator *cpu_reg = priv->cpu_reg; + unsigned long volt = 0, volt_old = 0, tol = 0; + unsigned int old_freq, new_freq; + long freq_Hz, freq_exact; + int ret; + + freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); + if (freq_Hz <= 0) + freq_Hz = freq_table[index].frequency * 1000; + + freq_exact = freq_Hz; + new_freq = freq_Hz / 1000; + old_freq = clk_get_rate(cpu_clk) / 1000; + + if (!IS_ERR(cpu_reg)) { + rcu_read_lock(); + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); + if (IS_ERR(opp)) { + rcu_read_unlock(); + dev_err(cpu_dev, "failed to find OPP for %ld\n", + freq_Hz); + return PTR_ERR(opp); + } + volt = dev_pm_opp_get_voltage(opp); + rcu_read_unlock(); + tol = volt * priv->voltage_tolerance / 100; + volt_old = regulator_get_voltage(cpu_reg); + } + + dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", + old_freq / 1000, volt_old ? volt_old / 1000 : -1, + new_freq / 1000, volt ? volt / 1000 : -1); + + /* scaling up? scale voltage before frequency */ + if (!IS_ERR(cpu_reg) && new_freq > old_freq) { + ret = regulator_set_voltage_tol(cpu_reg, volt, tol); + if (ret) { + dev_err(cpu_dev, "failed to scale voltage up: %d\n", + ret); + return ret; + } + } + + ret = clk_set_rate(cpu_clk, freq_exact); + if (ret) { + dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); + if (!IS_ERR(cpu_reg)) + regulator_set_voltage_tol(cpu_reg, volt_old, tol); + return ret; + } + + /* scaling down? scale voltage after frequency */ + if (!IS_ERR(cpu_reg) && new_freq < old_freq) { + ret = regulator_set_voltage_tol(cpu_reg, volt, tol); + if (ret) { + dev_err(cpu_dev, "failed to scale voltage down: %d\n", + ret); + clk_set_rate(cpu_clk, old_freq * 1000); + } + } + + return ret; +} + +static int allocate_resources(int cpu, struct device **cdev, + struct regulator **creg, struct clk **cclk) +{ + struct device *cpu_dev; + struct regulator *cpu_reg; + struct clk *cpu_clk; + int ret = 0; + char *reg_cpu0 = "cpu0", *reg_cpu = "cpu", *reg; + + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("failed to get cpu%d device\n", cpu); + return -ENODEV; + } + + /* Try "cpu0" for older DTs */ + if (!cpu) + reg = reg_cpu0; + else + reg = reg_cpu; + +try_again: + cpu_reg = regulator_get_optional(cpu_dev, reg); + if (IS_ERR(cpu_reg)) { + /* + * If cpu's regulator supply node is present, but regulator is + * not yet registered, we should try defering probe. + */ + if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) { + dev_dbg(cpu_dev, "cpu%d regulator not ready, retry\n", + cpu); + return -EPROBE_DEFER; + } + + /* Try with "cpu-supply" */ + if (reg == reg_cpu0) { + reg = reg_cpu; + goto try_again; + } + + dev_warn(cpu_dev, "failed to get cpu%d regulator: %ld\n", + cpu, PTR_ERR(cpu_reg)); + } + + cpu_clk = clk_get(cpu_dev, NULL); + if (IS_ERR(cpu_clk)) { + /* put regulator */ + if (!IS_ERR(cpu_reg)) + regulator_put(cpu_reg); + + ret = PTR_ERR(cpu_clk); + + /* + * If cpu's clk node is present, but clock is not yet + * registered, we should try defering probe. + */ + if (ret == -EPROBE_DEFER) + dev_dbg(cpu_dev, "cpu%d clock not ready, retry\n", cpu); + else + dev_err(cpu_dev, "failed to get cpu%d clock: %d\n", ret, + cpu); + } else { + *cdev = cpu_dev; + *creg = cpu_reg; + *cclk = cpu_clk; + } + + return ret; +} + +static int cpufreq_init(struct cpufreq_policy *policy) +{ + struct cpufreq_frequency_table *freq_table; + struct thermal_cooling_device *cdev; + struct device_node *np; + struct private_data *priv; + struct device *cpu_dev; + struct regulator *cpu_reg; + struct clk *cpu_clk; + unsigned int transition_latency; + int ret; + + ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk); + if (ret) { + pr_err("%s: Failed to allocate resources\n: %d", __func__, ret); + return ret; + } + + np = of_node_get(cpu_dev->of_node); + if (!np) { + dev_err(cpu_dev, "failed to find cpu%d node\n", policy->cpu); + ret = -ENOENT; + goto out_put_reg_clk; + } + + /* OPPs might be populated at runtime, don't check for error here */ + of_init_opp_table(cpu_dev); + + ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); + if (ret) { + dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); + goto out_put_node; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto out_free_table; + } + + of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance); + + if (of_property_read_u32(np, "clock-latency", &transition_latency)) + transition_latency = CPUFREQ_ETERNAL; + + if (!IS_ERR(cpu_reg)) { + struct dev_pm_opp *opp; + unsigned long min_uV, max_uV; + int i; + + /* + * OPP is maintained in order of increasing frequency, and + * freq_table initialised from OPP is therefore sorted in the + * same order. + */ + for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) + ; + rcu_read_lock(); + opp = dev_pm_opp_find_freq_exact(cpu_dev, + freq_table[0].frequency * 1000, true); + min_uV = dev_pm_opp_get_voltage(opp); + opp = dev_pm_opp_find_freq_exact(cpu_dev, + freq_table[i-1].frequency * 1000, true); + max_uV = dev_pm_opp_get_voltage(opp); + rcu_read_unlock(); + ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV); + if (ret > 0) + transition_latency += ret * 1000; + } + + /* + * For now, just loading the cooling device; + * thermal DT code takes care of matching them. + */ + if (of_find_property(np, "#cooling-cells", NULL)) { + cdev = of_cpufreq_cooling_register(np, cpu_present_mask); + if (IS_ERR(cdev)) + dev_err(cpu_dev, + "running cpufreq without cooling device: %ld\n", + PTR_ERR(cdev)); + else + priv->cdev = cdev; + } + of_node_put(np); + + priv->cpu_dev = cpu_dev; + priv->cpu_reg = cpu_reg; + policy->driver_data = priv; + + policy->clk = cpu_clk; + ret = cpufreq_generic_init(policy, freq_table, transition_latency); + if (ret) + goto out_cooling_unregister; + + return 0; + +out_cooling_unregister: + cpufreq_cooling_unregister(priv->cdev); + kfree(priv); +out_free_table: + dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); +out_put_node: + of_node_put(np); +out_put_reg_clk: + clk_put(cpu_clk); + if (!IS_ERR(cpu_reg)) + regulator_put(cpu_reg); + + return ret; +} + +static int cpufreq_exit(struct cpufreq_policy *policy) +{ + struct private_data *priv = policy->driver_data; + + cpufreq_cooling_unregister(priv->cdev); + dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); + clk_put(policy->clk); + if (!IS_ERR(priv->cpu_reg)) + regulator_put(priv->cpu_reg); + kfree(priv); + + return 0; +} + +static struct cpufreq_driver dt_cpufreq_driver = { + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = set_target, + .get = cpufreq_generic_get, + .init = cpufreq_init, + .exit = cpufreq_exit, + .name = "cpufreq-dt", + .attr = cpufreq_generic_attr, +}; + +static int dt_cpufreq_probe(struct platform_device *pdev) +{ + struct device *cpu_dev; + struct regulator *cpu_reg; + struct clk *cpu_clk; + int ret; + + /* + * All per-cluster (CPUs sharing clock/voltages) initialization is done + * from ->init(). In probe(), we just need to make sure that clk and + * regulators are available. Else defer probe and retry. + * + * FIXME: Is checking this only for CPU0 sufficient ? + */ + ret = allocate_resources(0, &cpu_dev, &cpu_reg, &cpu_clk); + if (ret) + return ret; + + clk_put(cpu_clk); + if (!IS_ERR(cpu_reg)) + regulator_put(cpu_reg); + + ret = cpufreq_register_driver(&dt_cpufreq_driver); + if (ret) + dev_err(cpu_dev, "failed register driver: %d\n", ret); + + return ret; +} + +static int dt_cpufreq_remove(struct platform_device *pdev) +{ + cpufreq_unregister_driver(&dt_cpufreq_driver); + return 0; +} + +static struct platform_driver dt_cpufreq_platdrv = { + .driver = { + .name = "cpufreq-dt", + .owner = THIS_MODULE, + }, + .probe = dt_cpufreq_probe, + .remove = dt_cpufreq_remove, +}; +module_platform_driver(dt_cpufreq_platdrv); + +MODULE_AUTHOR("Viresh Kumar "); +MODULE_AUTHOR("Shawn Guo "); +MODULE_DESCRIPTION("Generic cpufreq driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index 61a54310a1b9..843ec824fd91 100644 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ b/drivers/cpufreq/exynos4210-cpufreq.c @@ -127,7 +127,7 @@ int exynos4210_cpufreq_init(struct exynos_dvfs_info *info) * dependencies on platform headers. It is necessary to enable * Exynos multi-platform support and will be removed together with * this whole driver as soon as Exynos gets migrated to use - * cpufreq-cpu0 driver. + * cpufreq-dt driver. */ np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-clock"); if (!np) { diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c index 351a2074cfea..9e78a850e29f 100644 --- a/drivers/cpufreq/exynos4x12-cpufreq.c +++ b/drivers/cpufreq/exynos4x12-cpufreq.c @@ -174,7 +174,7 @@ int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info) * dependencies on platform headers. It is necessary to enable * Exynos multi-platform support and will be removed together with * this whole driver as soon as Exynos gets migrated to use - * cpufreq-cpu0 driver. + * cpufreq-dt driver. */ np = of_find_compatible_node(NULL, NULL, "samsung,exynos4412-clock"); if (!np) { diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c index c91ce69dc631..3eafdc7ba787 100644 --- a/drivers/cpufreq/exynos5250-cpufreq.c +++ b/drivers/cpufreq/exynos5250-cpufreq.c @@ -153,7 +153,7 @@ int exynos5250_cpufreq_init(struct exynos_dvfs_info *info) * dependencies on platform headers. It is necessary to enable * Exynos multi-platform support and will be removed together with * this whole driver as soon as Exynos gets migrated to use - * cpufreq-cpu0 driver. + * cpufreq-dt driver. */ np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-clock"); if (!np) { diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c index bf8902a0866d..ec399ad2f059 100644 --- a/drivers/cpufreq/highbank-cpufreq.c +++ b/drivers/cpufreq/highbank-cpufreq.c @@ -6,7 +6,7 @@ * published by the Free Software Foundation. * * This driver provides the clk notifier callbacks that are used when - * the cpufreq-cpu0 driver changes to frequency to alert the highbank + * the cpufreq-dt driver changes to frequency to alert the highbank * EnergyCore Management Engine (ECME) about the need to change * voltage. The ECME interfaces with the actual voltage regulators. */ @@ -60,7 +60,7 @@ static struct notifier_block hb_cpufreq_clk_nb = { static int hb_cpufreq_driver_init(void) { - struct platform_device_info devinfo = { .name = "cpufreq-cpu0", }; + struct platform_device_info devinfo = { .name = "cpufreq-dt", }; struct device *cpu_dev; struct clk *cpu_clk; struct device_node *np; @@ -95,7 +95,7 @@ static int hb_cpufreq_driver_init(void) goto out_put_node; } - /* Instantiate cpufreq-cpu0 */ + /* Instantiate cpufreq-dt */ platform_device_register_full(&devinfo); out_put_node: diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c index 3f9791f07b8e..567caa6313ff 100644 --- a/drivers/cpufreq/s5pv210-cpufreq.c +++ b/drivers/cpufreq/s5pv210-cpufreq.c @@ -597,7 +597,7 @@ static int s5pv210_cpufreq_probe(struct platform_device *pdev) * and dependencies on platform headers. It is necessary to enable * S5PV210 multi-platform support and will be removed together with * this whole driver as soon as S5PV210 gets migrated to use - * cpufreq-cpu0 driver. + * cpufreq-dt driver. */ np = of_find_compatible_node(NULL, NULL, "samsung,s5pv210-clock"); if (!np) { -- cgit v1.2.3-59-g8ed1b