diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2012-02-01 22:18:43 +0100 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2012-02-01 22:18:43 +0100 |
commit | dcaad77a8074b6de58e745546bc543d5538404f2 (patch) | |
tree | 5f988393b64f1a8053f2f5d0be7113e991d40886 | |
parent | 0496c8ae366724a0a2136cec09a2e277e782c126 (diff) | |
parent | 91cfbd4ee0875f8a826731983378670012ba6e01 (diff) |
Merge branch 'v3.4-for-rafael' of git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung into pm-domains
* 'v3.4-for-rafael' of git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung:
ARM: EXYNOS: Hook up power domains to generic power domain infrastructure
PM / Domains: Add OF support
-rw-r--r-- | Documentation/devicetree/bindings/arm/exynos/power_domain.txt | 21 | ||||
-rw-r--r-- | arch/arm/mach-exynos/Kconfig | 10 | ||||
-rw-r--r-- | arch/arm/mach-exynos/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-exynos/dev-pd.c | 139 | ||||
-rw-r--r-- | arch/arm/mach-exynos/mach-nuri.c | 11 | ||||
-rw-r--r-- | arch/arm/mach-exynos/mach-origen.c | 14 | ||||
-rw-r--r-- | arch/arm/mach-exynos/mach-smdkv310.c | 12 | ||||
-rw-r--r-- | arch/arm/mach-exynos/mach-universal_c210.c | 17 | ||||
-rw-r--r-- | arch/arm/mach-exynos/pm_domains.c | 195 | ||||
-rw-r--r-- | drivers/base/power/domain.c | 32 | ||||
-rw-r--r-- | include/linux/pm_domain.h | 12 |
11 files changed, 262 insertions, 203 deletions
diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt new file mode 100644 index 000000000000..6528e215c5fe --- /dev/null +++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt @@ -0,0 +1,21 @@ +* Samsung Exynos Power Domains + +Exynos processors include support for multiple power domains which are used +to gate power to one or more peripherals on the processor. + +Required Properties: +- compatiable: should be one of the following. + * samsung,exynos4210-pd - for exynos4210 type power domain. +- reg: physical base address of the controller and length of memory mapped + region. + +Optional Properties: +- samsung,exynos4210-pd-off: Specifies that the power domain is in turned-off + state during boot and remains to be turned-off until explicitly turned-on. + +Example: + + lcd0: power-domain-lcd0 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C00 0x10>; + }; diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index 5d602f68a0e8..dfad6538b273 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -34,6 +34,7 @@ config CPU_EXYNOS4210 select ARM_CPU_SUSPEND if PM select S5P_PM if PM select S5P_SLEEP if PM + select PM_GENERIC_DOMAINS help Enable EXYNOS4210 CPU support @@ -74,11 +75,6 @@ config EXYNOS4_SETUP_FIMD0 help Common setup code for FIMD0. -config EXYNOS4_DEV_PD - bool - help - Compile in platform device definitions for Power Domain - config EXYNOS4_DEV_SYSMMU bool help @@ -195,7 +191,6 @@ config MACH_SMDKV310 select EXYNOS4_DEV_AHCI select SAMSUNG_DEV_KEYPAD select EXYNOS4_DEV_DMA - select EXYNOS4_DEV_PD select SAMSUNG_DEV_PWM select EXYNOS4_DEV_USB_OHCI select EXYNOS4_DEV_SYSMMU @@ -243,7 +238,6 @@ config MACH_UNIVERSAL_C210 select S5P_DEV_ONENAND select S5P_DEV_TV select EXYNOS4_DEV_DMA - select EXYNOS4_DEV_PD select EXYNOS4_SETUP_FIMD0 select EXYNOS4_SETUP_I2C1 select EXYNOS4_SETUP_I2C3 @@ -277,7 +271,6 @@ config MACH_NURI select S5P_DEV_USB_EHCI select S5P_SETUP_MIPIPHY select EXYNOS4_DEV_DMA - select EXYNOS4_DEV_PD select EXYNOS4_SETUP_FIMC select EXYNOS4_SETUP_FIMD0 select EXYNOS4_SETUP_I2C1 @@ -310,7 +303,6 @@ config MACH_ORIGEN select SAMSUNG_DEV_BACKLIGHT select SAMSUNG_DEV_PWM select EXYNOS4_DEV_DMA - select EXYNOS4_DEV_PD select EXYNOS4_DEV_USB_OHCI select EXYNOS4_SETUP_FIMD0 select EXYNOS4_SETUP_SDHCI diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index 5fc202cdfdb6..d9191f9a7af8 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_CPU_EXYNOS4210) += clock-exynos4210.o obj-$(CONFIG_SOC_EXYNOS4212) += clock-exynos4212.o obj-$(CONFIG_PM) += pm.o +obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_ARCH_EXYNOS4) += pmu.o @@ -45,7 +46,6 @@ obj-$(CONFIG_MACH_EXYNOS4_DT) += mach-exynos4-dt.o obj-$(CONFIG_ARCH_EXYNOS4) += dev-audio.o obj-$(CONFIG_EXYNOS4_DEV_AHCI) += dev-ahci.o -obj-$(CONFIG_EXYNOS4_DEV_PD) += dev-pd.o obj-$(CONFIG_EXYNOS4_DEV_SYSMMU) += dev-sysmmu.o obj-$(CONFIG_EXYNOS4_DEV_DWMCI) += dev-dwmci.o obj-$(CONFIG_EXYNOS4_DEV_DMA) += dma.o diff --git a/arch/arm/mach-exynos/dev-pd.c b/arch/arm/mach-exynos/dev-pd.c deleted file mode 100644 index 3273f25d6a75..000000000000 --- a/arch/arm/mach-exynos/dev-pd.c +++ /dev/null @@ -1,139 +0,0 @@ -/* linux/arch/arm/mach-exynos4/dev-pd.c - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * EXYNOS4 - Power Domain support - * - * 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. -*/ - -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/platform_device.h> -#include <linux/delay.h> - -#include <mach/regs-pmu.h> - -#include <plat/pd.h> - -static int exynos4_pd_enable(struct device *dev) -{ - struct samsung_pd_info *pdata = dev->platform_data; - u32 timeout; - - __raw_writel(S5P_INT_LOCAL_PWR_EN, pdata->base); - - /* Wait max 1ms */ - timeout = 10; - while ((__raw_readl(pdata->base + 0x4) & S5P_INT_LOCAL_PWR_EN) - != S5P_INT_LOCAL_PWR_EN) { - if (timeout == 0) { - printk(KERN_ERR "Power domain %s enable failed.\n", - dev_name(dev)); - return -ETIMEDOUT; - } - timeout--; - udelay(100); - } - - return 0; -} - -static int exynos4_pd_disable(struct device *dev) -{ - struct samsung_pd_info *pdata = dev->platform_data; - u32 timeout; - - __raw_writel(0, pdata->base); - - /* Wait max 1ms */ - timeout = 10; - while (__raw_readl(pdata->base + 0x4) & S5P_INT_LOCAL_PWR_EN) { - if (timeout == 0) { - printk(KERN_ERR "Power domain %s disable failed.\n", - dev_name(dev)); - return -ETIMEDOUT; - } - timeout--; - udelay(100); - } - - return 0; -} - -struct platform_device exynos4_device_pd[] = { - { - .name = "samsung-pd", - .id = 0, - .dev = { - .platform_data = &(struct samsung_pd_info) { - .enable = exynos4_pd_enable, - .disable = exynos4_pd_disable, - .base = S5P_PMU_MFC_CONF, - }, - }, - }, { - .name = "samsung-pd", - .id = 1, - .dev = { - .platform_data = &(struct samsung_pd_info) { - .enable = exynos4_pd_enable, - .disable = exynos4_pd_disable, - .base = S5P_PMU_G3D_CONF, - }, - }, - }, { - .name = "samsung-pd", - .id = 2, - .dev = { - .platform_data = &(struct samsung_pd_info) { - .enable = exynos4_pd_enable, - .disable = exynos4_pd_disable, - .base = S5P_PMU_LCD0_CONF, - }, - }, - }, { - .name = "samsung-pd", - .id = 3, - .dev = { - .platform_data = &(struct samsung_pd_info) { - .enable = exynos4_pd_enable, - .disable = exynos4_pd_disable, - .base = S5P_PMU_LCD1_CONF, - }, - }, - }, { - .name = "samsung-pd", - .id = 4, - .dev = { - .platform_data = &(struct samsung_pd_info) { - .enable = exynos4_pd_enable, - .disable = exynos4_pd_disable, - .base = S5P_PMU_TV_CONF, - }, - }, - }, { - .name = "samsung-pd", - .id = 5, - .dev = { - .platform_data = &(struct samsung_pd_info) { - .enable = exynos4_pd_enable, - .disable = exynos4_pd_disable, - .base = S5P_PMU_CAM_CONF, - }, - }, - }, { - .name = "samsung-pd", - .id = 6, - .dev = { - .platform_data = &(struct samsung_pd_info) { - .enable = exynos4_pd_enable, - .disable = exynos4_pd_disable, - .base = S5P_PMU_GPS_CONF, - }, - }, - }, -}; diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c index b895ec031105..df705db08b06 100644 --- a/arch/arm/mach-exynos/mach-nuri.c +++ b/arch/arm/mach-exynos/mach-nuri.c @@ -1263,9 +1263,6 @@ static struct platform_device *nuri_devices[] __initdata = { &s5p_device_mfc, &s5p_device_mfc_l, &s5p_device_mfc_r, - &exynos4_device_pd[PD_MFC], - &exynos4_device_pd[PD_LCD0], - &exynos4_device_pd[PD_CAM], &s5p_device_fimc_md, /* NURI Devices */ @@ -1315,14 +1312,6 @@ static void __init nuri_machine_init(void) /* Last */ platform_add_devices(nuri_devices, ARRAY_SIZE(nuri_devices)); - s5p_device_mfc.dev.parent = &exynos4_device_pd[PD_MFC].dev; - s5p_device_fimd0.dev.parent = &exynos4_device_pd[PD_LCD0].dev; - - s5p_device_fimc0.dev.parent = &exynos4_device_pd[PD_CAM].dev; - s5p_device_fimc1.dev.parent = &exynos4_device_pd[PD_CAM].dev; - s5p_device_fimc2.dev.parent = &exynos4_device_pd[PD_CAM].dev; - s5p_device_fimc3.dev.parent = &exynos4_device_pd[PD_CAM].dev; - s5p_device_mipi_csis0.dev.parent = &exynos4_device_pd[PD_CAM].dev; } MACHINE_START(NURI, "NURI") diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c index 0679b8ad2d1e..fa5c4a59b0aa 100644 --- a/arch/arm/mach-exynos/mach-origen.c +++ b/arch/arm/mach-exynos/mach-origen.c @@ -621,13 +621,6 @@ static struct platform_device *origen_devices[] __initdata = { &s5p_device_mfc_r, &s5p_device_mixer, &exynos4_device_ohci, - &exynos4_device_pd[PD_LCD0], - &exynos4_device_pd[PD_TV], - &exynos4_device_pd[PD_G3D], - &exynos4_device_pd[PD_LCD1], - &exynos4_device_pd[PD_CAM], - &exynos4_device_pd[PD_GPS], - &exynos4_device_pd[PD_MFC], &origen_device_gpiokeys, &origen_lcd_hv070wsa, }; @@ -695,13 +688,6 @@ static void __init origen_machine_init(void) platform_add_devices(origen_devices, ARRAY_SIZE(origen_devices)); - s5p_device_fimd0.dev.parent = &exynos4_device_pd[PD_LCD0].dev; - - s5p_device_hdmi.dev.parent = &exynos4_device_pd[PD_TV].dev; - s5p_device_mixer.dev.parent = &exynos4_device_pd[PD_TV].dev; - - s5p_device_mfc.dev.parent = &exynos4_device_pd[PD_MFC].dev; - samsung_bl_set(&origen_bl_gpio_info, &origen_bl_data); } diff --git a/arch/arm/mach-exynos/mach-smdkv310.c b/arch/arm/mach-exynos/mach-smdkv310.c index b2c5557f50e4..5258b8563676 100644 --- a/arch/arm/mach-exynos/mach-smdkv310.c +++ b/arch/arm/mach-exynos/mach-smdkv310.c @@ -277,13 +277,6 @@ static struct platform_device *smdkv310_devices[] __initdata = { &s5p_device_mfc, &s5p_device_mfc_l, &s5p_device_mfc_r, - &exynos4_device_pd[PD_MFC], - &exynos4_device_pd[PD_G3D], - &exynos4_device_pd[PD_LCD0], - &exynos4_device_pd[PD_LCD1], - &exynos4_device_pd[PD_CAM], - &exynos4_device_pd[PD_TV], - &exynos4_device_pd[PD_GPS], &exynos4_device_spdif, &exynos4_device_sysmmu, &samsung_asoc_dma, @@ -336,10 +329,6 @@ static void s5p_tv_setup(void) WARN_ON(gpio_request_one(EXYNOS4_GPX3(7), GPIOF_IN, "hpd-plug")); s3c_gpio_cfgpin(EXYNOS4_GPX3(7), S3C_GPIO_SFN(0x3)); s3c_gpio_setpull(EXYNOS4_GPX3(7), S3C_GPIO_PULL_NONE); - - /* setup dependencies between TV devices */ - s5p_device_hdmi.dev.parent = &exynos4_device_pd[PD_TV].dev; - s5p_device_mixer.dev.parent = &exynos4_device_pd[PD_TV].dev; } static void __init smdkv310_map_io(void) @@ -379,7 +368,6 @@ static void __init smdkv310_machine_init(void) clk_xusbxti.rate = 24000000; platform_add_devices(smdkv310_devices, ARRAY_SIZE(smdkv310_devices)); - s5p_device_mfc.dev.parent = &exynos4_device_pd[PD_MFC].dev; } MACHINE_START(SMDKV310, "SMDKV310") diff --git a/arch/arm/mach-exynos/mach-universal_c210.c b/arch/arm/mach-exynos/mach-universal_c210.c index 37ac93e8d6d9..026241737919 100644 --- a/arch/arm/mach-exynos/mach-universal_c210.c +++ b/arch/arm/mach-exynos/mach-universal_c210.c @@ -969,7 +969,6 @@ static struct platform_device *universal_devices[] __initdata = { &s3c_device_i2c5, &s5p_device_i2c_hdmiphy, &hdmi_fixed_voltage, - &exynos4_device_pd[PD_TV], &s5p_device_hdmi, &s5p_device_sdo, &s5p_device_mixer, @@ -982,9 +981,6 @@ static struct platform_device *universal_devices[] __initdata = { &s5p_device_mfc, &s5p_device_mfc_l, &s5p_device_mfc_r, - &exynos4_device_pd[PD_MFC], - &exynos4_device_pd[PD_LCD0], - &exynos4_device_pd[PD_CAM], &cam_i_core_fixed_reg_dev, &cam_s_if_fixed_reg_dev, &s5p_device_fimc_md, @@ -1003,10 +999,6 @@ void s5p_tv_setup(void) gpio_request_one(EXYNOS4_GPX3(7), GPIOF_IN, "hpd-plug"); s3c_gpio_cfgpin(EXYNOS4_GPX3(7), S3C_GPIO_SFN(0x3)); s3c_gpio_setpull(EXYNOS4_GPX3(7), S3C_GPIO_PULL_NONE); - - /* setup dependencies between TV devices */ - s5p_device_hdmi.dev.parent = &exynos4_device_pd[PD_TV].dev; - s5p_device_mixer.dev.parent = &exynos4_device_pd[PD_TV].dev; } static void __init universal_reserve(void) @@ -1040,15 +1032,6 @@ static void __init universal_machine_init(void) /* Last */ platform_add_devices(universal_devices, ARRAY_SIZE(universal_devices)); - - s5p_device_mfc.dev.parent = &exynos4_device_pd[PD_MFC].dev; - s5p_device_fimd0.dev.parent = &exynos4_device_pd[PD_LCD0].dev; - - s5p_device_fimc0.dev.parent = &exynos4_device_pd[PD_CAM].dev; - s5p_device_fimc1.dev.parent = &exynos4_device_pd[PD_CAM].dev; - s5p_device_fimc2.dev.parent = &exynos4_device_pd[PD_CAM].dev; - s5p_device_fimc3.dev.parent = &exynos4_device_pd[PD_CAM].dev; - s5p_device_mipi_csis0.dev.parent = &exynos4_device_pd[PD_CAM].dev; } MACHINE_START(UNIVERSAL_C210, "UNIVERSAL_C210") diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c new file mode 100644 index 000000000000..0b04af2b13cc --- /dev/null +++ b/arch/arm/mach-exynos/pm_domains.c @@ -0,0 +1,195 @@ +/* + * Exynos Generic power domain support. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Implementation of Exynos specific power domain control which is used in + * conjunction with runtime-pm. Support for both device-tree and non-device-tree + * based power domain support is included. + * + * 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. +*/ + +#include <linux/io.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/pm_domain.h> +#include <linux/delay.h> +#include <linux/of_address.h> + +#include <mach/regs-pmu.h> +#include <plat/devs.h> + +/* + * Exynos specific wrapper around the generic power domain + */ +struct exynos_pm_domain { + void __iomem *base; + char const *name; + bool is_off; + struct generic_pm_domain pd; +}; + +static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) +{ + struct exynos_pm_domain *pd; + void __iomem *base; + u32 timeout, pwr; + char *op; + + pd = container_of(domain, struct exynos_pm_domain, pd); + base = pd->base; + + pwr = power_on ? S5P_INT_LOCAL_PWR_EN : 0; + __raw_writel(pwr, base); + + /* Wait max 1ms */ + timeout = 10; + + while ((__raw_readl(base + 0x4) & S5P_INT_LOCAL_PWR_EN) != pwr) { + if (!timeout) { + op = (power_on) ? "enable" : "disable"; + pr_err("Power domain %s %s failed\n", domain->name, op); + return -ETIMEDOUT; + } + timeout--; + cpu_relax(); + usleep_range(80, 100); + } + return 0; +} + +static int exynos_pd_power_on(struct generic_pm_domain *domain) +{ + return exynos_pd_power(domain, true); +} + +static int exynos_pd_power_off(struct generic_pm_domain *domain) +{ + return exynos_pd_power(domain, false); +} + +#define EXYNOS_GPD(PD, BASE, NAME) \ +static struct exynos_pm_domain PD = { \ + .base = (void __iomem *)BASE, \ + .name = NAME, \ + .pd = { \ + .power_off = exynos_pd_power_off, \ + .power_on = exynos_pd_power_on, \ + }, \ +} + +#ifdef CONFIG_OF +static __init int exynos_pm_dt_parse_domains(void) +{ + struct device_node *np; + + for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { + struct exynos_pm_domain *pd; + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) { + pr_err("%s: failed to allocate memory for domain\n", + __func__); + return -ENOMEM; + } + + if (of_get_property(np, "samsung,exynos4210-pd-off", NULL)) + pd->is_off = true; + pd->name = np->name; + 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; + pm_genpd_init(&pd->pd, NULL, false); + } + return 0; +} +#else +static __init int exynos_pm_dt_parse_domains(void) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static __init void exynos_pm_add_dev_to_genpd(struct platform_device *pdev, + struct exynos_pm_domain *pd) +{ + if (pdev->dev.bus) { + if (pm_genpd_add_device(&pd->pd, &pdev->dev)) + pr_info("%s: error in adding %s device to %s power" + "domain\n", __func__, dev_name(&pdev->dev), + pd->name); + } +} + +EXYNOS_GPD(exynos4_pd_mfc, S5P_PMU_MFC_CONF, "pd-mfc"); +EXYNOS_GPD(exynos4_pd_g3d, S5P_PMU_G3D_CONF, "pd-g3d"); +EXYNOS_GPD(exynos4_pd_lcd0, S5P_PMU_LCD0_CONF, "pd-lcd0"); +EXYNOS_GPD(exynos4_pd_lcd1, S5P_PMU_LCD1_CONF, "pd-lcd1"); +EXYNOS_GPD(exynos4_pd_tv, S5P_PMU_TV_CONF, "pd-tv"); +EXYNOS_GPD(exynos4_pd_cam, S5P_PMU_CAM_CONF, "pd-cam"); +EXYNOS_GPD(exynos4_pd_gps, S5P_PMU_GPS_CONF, "pd-gps"); + +static struct exynos_pm_domain *exynos4_pm_domains[] = { + &exynos4_pd_mfc, + &exynos4_pd_g3d, + &exynos4_pd_lcd0, + &exynos4_pd_lcd1, + &exynos4_pd_tv, + &exynos4_pd_cam, + &exynos4_pd_gps, +}; + +static __init int exynos4_pm_init_power_domain(void) +{ + int idx; + + if (of_have_populated_dt()) + return exynos_pm_dt_parse_domains(); + + for (idx = 0; idx < ARRAY_SIZE(exynos4_pm_domains); idx++) + pm_genpd_init(&exynos4_pm_domains[idx]->pd, NULL, + exynos4_pm_domains[idx]->is_off); + +#ifdef CONFIG_S5P_DEV_FIMD0 + exynos_pm_add_dev_to_genpd(&s5p_device_fimd0, &exynos4_pd_lcd0); +#endif +#ifdef CONFIG_S5P_DEV_TV + exynos_pm_add_dev_to_genpd(&s5p_device_hdmi, &exynos4_pd_tv); + exynos_pm_add_dev_to_genpd(&s5p_device_mixer, &exynos4_pd_tv); +#endif +#ifdef CONFIG_S5P_DEV_MFC + exynos_pm_add_dev_to_genpd(&s5p_device_mfc, &exynos4_pd_mfc); +#endif +#ifdef CONFIG_S5P_DEV_FIMC0 + exynos_pm_add_dev_to_genpd(&s5p_device_fimc0, &exynos4_pd_cam); +#endif +#ifdef CONFIG_S5P_DEV_FIMC1 + exynos_pm_add_dev_to_genpd(&s5p_device_fimc1, &exynos4_pd_cam); +#endif +#ifdef CONFIG_S5P_DEV_FIMC2 + exynos_pm_add_dev_to_genpd(&s5p_device_fimc2, &exynos4_pd_cam); +#endif +#ifdef CONFIG_S5P_DEV_FIMC3 + exynos_pm_add_dev_to_genpd(&s5p_device_fimc3, &exynos4_pd_cam); +#endif +#ifdef CONFIG_S5P_DEV_CSIS0 + exynos_pm_add_dev_to_genpd(&s5p_device_mipi_csis0, &exynos4_pd_cam); +#endif +#ifdef CONFIG_S5P_DEV_CSIS1 + exynos_pm_add_dev_to_genpd(&s5p_device_mipi_csis1, &exynos4_pd_cam); +#endif + return 0; +} +arch_initcall(exynos4_pm_init_power_domain); + +static __init int exynos_pm_late_initcall(void) +{ + pm_genpd_poweroff_unused(); + return 0; +} +late_initcall(exynos_pm_late_initcall); diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 12a03afe5305..d2c03239abcf 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1230,6 +1230,38 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, } /** + * __pm_genpd_of_add_device - Add a device to an I/O PM domain. + * @genpd_node: Device tree node pointer representing a PM domain to which the + * the device is added to. + * @dev: Device to be added. + * @td: Set of PM QoS timing parameters to attach to the device. + */ +int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev, + struct gpd_timing_data *td) +{ + struct generic_pm_domain *genpd = NULL, *gpd; + + dev_dbg(dev, "%s()\n", __func__); + + if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev)) + return -EINVAL; + + mutex_lock(&gpd_list_lock); + list_for_each_entry(gpd, &gpd_list, gpd_list_node) { + if (gpd->of_node == genpd_node) { + genpd = gpd; + break; + } + } + mutex_unlock(&gpd_list_lock); + + if (!genpd) + return -EINVAL; + + return __pm_genpd_add_device(genpd, dev, td); +} + +/** * pm_genpd_remove_device - Remove a device from an I/O PM domain. * @genpd: PM domain to remove the device from. * @dev: Device to be removed. diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index a03a0ad998b8..e3ff87550eeb 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -11,6 +11,7 @@ #include <linux/device.h> #include <linux/err.h> +#include <linux/of.h> enum gpd_status { GPD_STATE_ACTIVE = 0, /* PM domain is active */ @@ -70,6 +71,7 @@ struct generic_pm_domain { s64 break_even_ns; /* Power break even for the entire domain. */ s64 max_off_time_ns; /* Maximum allowed "suspended" time. */ ktime_t power_off_time; + struct device_node *of_node; /* Node in device tree */ }; static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd) @@ -117,12 +119,22 @@ extern int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, struct gpd_timing_data *td); +extern int __pm_genpd_of_add_device(struct device_node *genpd_node, + struct device *dev, + struct gpd_timing_data *td); + static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) { return __pm_genpd_add_device(genpd, dev, NULL); } +static inline int pm_genpd_of_add_device(struct device_node *genpd_node, + struct device *dev) +{ + return __pm_genpd_of_add_device(genpd_node, dev, NULL); +} + extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, struct device *dev); extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, |