diff options
-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 | 189 | ||||
-rw-r--r-- | include/linux/pm_domain.h | 22 |
11 files changed, 381 insertions, 251 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 @@ | |||
1 | * Samsung Exynos Power Domains | ||
2 | |||
3 | Exynos processors include support for multiple power domains which are used | ||
4 | to gate power to one or more peripherals on the processor. | ||
5 | |||
6 | Required Properties: | ||
7 | - compatiable: should be one of the following. | ||
8 | * samsung,exynos4210-pd - for exynos4210 type power domain. | ||
9 | - reg: physical base address of the controller and length of memory mapped | ||
10 | region. | ||
11 | |||
12 | Optional Properties: | ||
13 | - samsung,exynos4210-pd-off: Specifies that the power domain is in turned-off | ||
14 | state during boot and remains to be turned-off until explicitly turned-on. | ||
15 | |||
16 | Example: | ||
17 | |||
18 | lcd0: power-domain-lcd0 { | ||
19 | compatible = "samsung,exynos4210-pd"; | ||
20 | reg = <0x10023C00 0x10>; | ||
21 | }; | ||
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 | |||
34 | select ARM_CPU_SUSPEND if PM | 34 | select ARM_CPU_SUSPEND if PM |
35 | select S5P_PM if PM | 35 | select S5P_PM if PM |
36 | select S5P_SLEEP if PM | 36 | select S5P_SLEEP if PM |
37 | select PM_GENERIC_DOMAINS | ||
37 | help | 38 | help |
38 | Enable EXYNOS4210 CPU support | 39 | Enable EXYNOS4210 CPU support |
39 | 40 | ||
@@ -74,11 +75,6 @@ config EXYNOS4_SETUP_FIMD0 | |||
74 | help | 75 | help |
75 | Common setup code for FIMD0. | 76 | Common setup code for FIMD0. |
76 | 77 | ||
77 | config EXYNOS4_DEV_PD | ||
78 | bool | ||
79 | help | ||
80 | Compile in platform device definitions for Power Domain | ||
81 | |||
82 | config EXYNOS4_DEV_SYSMMU | 78 | config EXYNOS4_DEV_SYSMMU |
83 | bool | 79 | bool |
84 | help | 80 | help |
@@ -195,7 +191,6 @@ config MACH_SMDKV310 | |||
195 | select EXYNOS4_DEV_AHCI | 191 | select EXYNOS4_DEV_AHCI |
196 | select SAMSUNG_DEV_KEYPAD | 192 | select SAMSUNG_DEV_KEYPAD |
197 | select EXYNOS4_DEV_DMA | 193 | select EXYNOS4_DEV_DMA |
198 | select EXYNOS4_DEV_PD | ||
199 | select SAMSUNG_DEV_PWM | 194 | select SAMSUNG_DEV_PWM |
200 | select EXYNOS4_DEV_USB_OHCI | 195 | select EXYNOS4_DEV_USB_OHCI |
201 | select EXYNOS4_DEV_SYSMMU | 196 | select EXYNOS4_DEV_SYSMMU |
@@ -243,7 +238,6 @@ config MACH_UNIVERSAL_C210 | |||
243 | select S5P_DEV_ONENAND | 238 | select S5P_DEV_ONENAND |
244 | select S5P_DEV_TV | 239 | select S5P_DEV_TV |
245 | select EXYNOS4_DEV_DMA | 240 | select EXYNOS4_DEV_DMA |
246 | select EXYNOS4_DEV_PD | ||
247 | select EXYNOS4_SETUP_FIMD0 | 241 | select EXYNOS4_SETUP_FIMD0 |
248 | select EXYNOS4_SETUP_I2C1 | 242 | select EXYNOS4_SETUP_I2C1 |
249 | select EXYNOS4_SETUP_I2C3 | 243 | select EXYNOS4_SETUP_I2C3 |
@@ -277,7 +271,6 @@ config MACH_NURI | |||
277 | select S5P_DEV_USB_EHCI | 271 | select S5P_DEV_USB_EHCI |
278 | select S5P_SETUP_MIPIPHY | 272 | select S5P_SETUP_MIPIPHY |
279 | select EXYNOS4_DEV_DMA | 273 | select EXYNOS4_DEV_DMA |
280 | select EXYNOS4_DEV_PD | ||
281 | select EXYNOS4_SETUP_FIMC | 274 | select EXYNOS4_SETUP_FIMC |
282 | select EXYNOS4_SETUP_FIMD0 | 275 | select EXYNOS4_SETUP_FIMD0 |
283 | select EXYNOS4_SETUP_I2C1 | 276 | select EXYNOS4_SETUP_I2C1 |
@@ -310,7 +303,6 @@ config MACH_ORIGEN | |||
310 | select SAMSUNG_DEV_BACKLIGHT | 303 | select SAMSUNG_DEV_BACKLIGHT |
311 | select SAMSUNG_DEV_PWM | 304 | select SAMSUNG_DEV_PWM |
312 | select EXYNOS4_DEV_DMA | 305 | select EXYNOS4_DEV_DMA |
313 | select EXYNOS4_DEV_PD | ||
314 | select EXYNOS4_DEV_USB_OHCI | 306 | select EXYNOS4_DEV_USB_OHCI |
315 | select EXYNOS4_SETUP_FIMD0 | 307 | select EXYNOS4_SETUP_FIMD0 |
316 | select EXYNOS4_SETUP_SDHCI | 308 | 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 | |||
17 | obj-$(CONFIG_SOC_EXYNOS4212) += clock-exynos4212.o | 17 | obj-$(CONFIG_SOC_EXYNOS4212) += clock-exynos4212.o |
18 | 18 | ||
19 | obj-$(CONFIG_PM) += pm.o | 19 | obj-$(CONFIG_PM) += pm.o |
20 | obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o | ||
20 | obj-$(CONFIG_CPU_IDLE) += cpuidle.o | 21 | obj-$(CONFIG_CPU_IDLE) += cpuidle.o |
21 | 22 | ||
22 | obj-$(CONFIG_ARCH_EXYNOS4) += pmu.o | 23 | obj-$(CONFIG_ARCH_EXYNOS4) += pmu.o |
@@ -45,7 +46,6 @@ obj-$(CONFIG_MACH_EXYNOS4_DT) += mach-exynos4-dt.o | |||
45 | 46 | ||
46 | obj-$(CONFIG_ARCH_EXYNOS4) += dev-audio.o | 47 | obj-$(CONFIG_ARCH_EXYNOS4) += dev-audio.o |
47 | obj-$(CONFIG_EXYNOS4_DEV_AHCI) += dev-ahci.o | 48 | obj-$(CONFIG_EXYNOS4_DEV_AHCI) += dev-ahci.o |
48 | obj-$(CONFIG_EXYNOS4_DEV_PD) += dev-pd.o | ||
49 | obj-$(CONFIG_EXYNOS4_DEV_SYSMMU) += dev-sysmmu.o | 49 | obj-$(CONFIG_EXYNOS4_DEV_SYSMMU) += dev-sysmmu.o |
50 | obj-$(CONFIG_EXYNOS4_DEV_DWMCI) += dev-dwmci.o | 50 | obj-$(CONFIG_EXYNOS4_DEV_DWMCI) += dev-dwmci.o |
51 | obj-$(CONFIG_EXYNOS4_DEV_DMA) += dma.o | 51 | 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 @@ | |||
1 | /* linux/arch/arm/mach-exynos4/dev-pd.c | ||
2 | * | ||
3 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com | ||
5 | * | ||
6 | * EXYNOS4 - Power Domain support | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/io.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/delay.h> | ||
17 | |||
18 | #include <mach/regs-pmu.h> | ||
19 | |||
20 | #include <plat/pd.h> | ||
21 | |||
22 | static int exynos4_pd_enable(struct device *dev) | ||
23 | { | ||
24 | struct samsung_pd_info *pdata = dev->platform_data; | ||
25 | u32 timeout; | ||
26 | |||
27 | __raw_writel(S5P_INT_LOCAL_PWR_EN, pdata->base); | ||
28 | |||
29 | /* Wait max 1ms */ | ||
30 | timeout = 10; | ||
31 | while ((__raw_readl(pdata->base + 0x4) & S5P_INT_LOCAL_PWR_EN) | ||
32 | != S5P_INT_LOCAL_PWR_EN) { | ||
33 | if (timeout == 0) { | ||
34 | printk(KERN_ERR "Power domain %s enable failed.\n", | ||
35 | dev_name(dev)); | ||
36 | return -ETIMEDOUT; | ||
37 | } | ||
38 | timeout--; | ||
39 | udelay(100); | ||
40 | } | ||
41 | |||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | static int exynos4_pd_disable(struct device *dev) | ||
46 | { | ||
47 | struct samsung_pd_info *pdata = dev->platform_data; | ||
48 | u32 timeout; | ||
49 | |||
50 | __raw_writel(0, pdata->base); | ||
51 | |||
52 | /* Wait max 1ms */ | ||
53 | timeout = 10; | ||
54 | while (__raw_readl(pdata->base + 0x4) & S5P_INT_LOCAL_PWR_EN) { | ||
55 | if (timeout == 0) { | ||
56 | printk(KERN_ERR "Power domain %s disable failed.\n", | ||
57 | dev_name(dev)); | ||
58 | return -ETIMEDOUT; | ||
59 | } | ||
60 | timeout--; | ||
61 | udelay(100); | ||
62 | } | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | struct platform_device exynos4_device_pd[] = { | ||
68 | { | ||
69 | .name = "samsung-pd", | ||
70 | .id = 0, | ||
71 | .dev = { | ||
72 | .platform_data = &(struct samsung_pd_info) { | ||
73 | .enable = exynos4_pd_enable, | ||
74 | .disable = exynos4_pd_disable, | ||
75 | .base = S5P_PMU_MFC_CONF, | ||
76 | }, | ||
77 | }, | ||
78 | }, { | ||
79 | .name = "samsung-pd", | ||
80 | .id = 1, | ||
81 | .dev = { | ||
82 | .platform_data = &(struct samsung_pd_info) { | ||
83 | .enable = exynos4_pd_enable, | ||
84 | .disable = exynos4_pd_disable, | ||
85 | .base = S5P_PMU_G3D_CONF, | ||
86 | }, | ||
87 | }, | ||
88 | }, { | ||
89 | .name = "samsung-pd", | ||
90 | .id = 2, | ||
91 | .dev = { | ||
92 | .platform_data = &(struct samsung_pd_info) { | ||
93 | .enable = exynos4_pd_enable, | ||
94 | .disable = exynos4_pd_disable, | ||
95 | .base = S5P_PMU_LCD0_CONF, | ||
96 | }, | ||
97 | }, | ||
98 | }, { | ||
99 | .name = "samsung-pd", | ||
100 | .id = 3, | ||
101 | .dev = { | ||
102 | .platform_data = &(struct samsung_pd_info) { | ||
103 | .enable = exynos4_pd_enable, | ||
104 | .disable = exynos4_pd_disable, | ||
105 | .base = S5P_PMU_LCD1_CONF, | ||
106 | }, | ||
107 | }, | ||
108 | }, { | ||
109 | .name = "samsung-pd", | ||
110 | .id = 4, | ||
111 | .dev = { | ||
112 | .platform_data = &(struct samsung_pd_info) { | ||
113 | .enable = exynos4_pd_enable, | ||
114 | .disable = exynos4_pd_disable, | ||
115 | .base = S5P_PMU_TV_CONF, | ||
116 | }, | ||
117 | }, | ||
118 | }, { | ||
119 | .name = "samsung-pd", | ||
120 | .id = 5, | ||
121 | .dev = { | ||
122 | .platform_data = &(struct samsung_pd_info) { | ||
123 | .enable = exynos4_pd_enable, | ||
124 | .disable = exynos4_pd_disable, | ||
125 | .base = S5P_PMU_CAM_CONF, | ||
126 | }, | ||
127 | }, | ||
128 | }, { | ||
129 | .name = "samsung-pd", | ||
130 | .id = 6, | ||
131 | .dev = { | ||
132 | .platform_data = &(struct samsung_pd_info) { | ||
133 | .enable = exynos4_pd_enable, | ||
134 | .disable = exynos4_pd_disable, | ||
135 | .base = S5P_PMU_GPS_CONF, | ||
136 | }, | ||
137 | }, | ||
138 | }, | ||
139 | }; | ||
diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c index 435261f83f46..aa37179d776c 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 = { | |||
1263 | &s5p_device_mfc, | 1263 | &s5p_device_mfc, |
1264 | &s5p_device_mfc_l, | 1264 | &s5p_device_mfc_l, |
1265 | &s5p_device_mfc_r, | 1265 | &s5p_device_mfc_r, |
1266 | &exynos4_device_pd[PD_MFC], | ||
1267 | &exynos4_device_pd[PD_LCD0], | ||
1268 | &exynos4_device_pd[PD_CAM], | ||
1269 | &s5p_device_fimc_md, | 1266 | &s5p_device_fimc_md, |
1270 | 1267 | ||
1271 | /* NURI Devices */ | 1268 | /* NURI Devices */ |
@@ -1315,14 +1312,6 @@ static void __init nuri_machine_init(void) | |||
1315 | 1312 | ||
1316 | /* Last */ | 1313 | /* Last */ |
1317 | platform_add_devices(nuri_devices, ARRAY_SIZE(nuri_devices)); | 1314 | platform_add_devices(nuri_devices, ARRAY_SIZE(nuri_devices)); |
1318 | s5p_device_mfc.dev.parent = &exynos4_device_pd[PD_MFC].dev; | ||
1319 | s5p_device_fimd0.dev.parent = &exynos4_device_pd[PD_LCD0].dev; | ||
1320 | |||
1321 | s5p_device_fimc0.dev.parent = &exynos4_device_pd[PD_CAM].dev; | ||
1322 | s5p_device_fimc1.dev.parent = &exynos4_device_pd[PD_CAM].dev; | ||
1323 | s5p_device_fimc2.dev.parent = &exynos4_device_pd[PD_CAM].dev; | ||
1324 | s5p_device_fimc3.dev.parent = &exynos4_device_pd[PD_CAM].dev; | ||
1325 | s5p_device_mipi_csis0.dev.parent = &exynos4_device_pd[PD_CAM].dev; | ||
1326 | } | 1315 | } |
1327 | 1316 | ||
1328 | MACHINE_START(NURI, "NURI") | 1317 | 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 = { | |||
621 | &s5p_device_mfc_r, | 621 | &s5p_device_mfc_r, |
622 | &s5p_device_mixer, | 622 | &s5p_device_mixer, |
623 | &exynos4_device_ohci, | 623 | &exynos4_device_ohci, |
624 | &exynos4_device_pd[PD_LCD0], | ||
625 | &exynos4_device_pd[PD_TV], | ||
626 | &exynos4_device_pd[PD_G3D], | ||
627 | &exynos4_device_pd[PD_LCD1], | ||
628 | &exynos4_device_pd[PD_CAM], | ||
629 | &exynos4_device_pd[PD_GPS], | ||
630 | &exynos4_device_pd[PD_MFC], | ||
631 | &origen_device_gpiokeys, | 624 | &origen_device_gpiokeys, |
632 | &origen_lcd_hv070wsa, | 625 | &origen_lcd_hv070wsa, |
633 | }; | 626 | }; |
@@ -695,13 +688,6 @@ static void __init origen_machine_init(void) | |||
695 | 688 | ||
696 | platform_add_devices(origen_devices, ARRAY_SIZE(origen_devices)); | 689 | platform_add_devices(origen_devices, ARRAY_SIZE(origen_devices)); |
697 | 690 | ||
698 | s5p_device_fimd0.dev.parent = &exynos4_device_pd[PD_LCD0].dev; | ||
699 | |||
700 | s5p_device_hdmi.dev.parent = &exynos4_device_pd[PD_TV].dev; | ||
701 | s5p_device_mixer.dev.parent = &exynos4_device_pd[PD_TV].dev; | ||
702 | |||
703 | s5p_device_mfc.dev.parent = &exynos4_device_pd[PD_MFC].dev; | ||
704 | |||
705 | samsung_bl_set(&origen_bl_gpio_info, &origen_bl_data); | 691 | samsung_bl_set(&origen_bl_gpio_info, &origen_bl_data); |
706 | } | 692 | } |
707 | 693 | ||
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 = { | |||
277 | &s5p_device_mfc, | 277 | &s5p_device_mfc, |
278 | &s5p_device_mfc_l, | 278 | &s5p_device_mfc_l, |
279 | &s5p_device_mfc_r, | 279 | &s5p_device_mfc_r, |
280 | &exynos4_device_pd[PD_MFC], | ||
281 | &exynos4_device_pd[PD_G3D], | ||
282 | &exynos4_device_pd[PD_LCD0], | ||
283 | &exynos4_device_pd[PD_LCD1], | ||
284 | &exynos4_device_pd[PD_CAM], | ||
285 | &exynos4_device_pd[PD_TV], | ||
286 | &exynos4_device_pd[PD_GPS], | ||
287 | &exynos4_device_spdif, | 280 | &exynos4_device_spdif, |
288 | &exynos4_device_sysmmu, | 281 | &exynos4_device_sysmmu, |
289 | &samsung_asoc_dma, | 282 | &samsung_asoc_dma, |
@@ -336,10 +329,6 @@ static void s5p_tv_setup(void) | |||
336 | WARN_ON(gpio_request_one(EXYNOS4_GPX3(7), GPIOF_IN, "hpd-plug")); | 329 | WARN_ON(gpio_request_one(EXYNOS4_GPX3(7), GPIOF_IN, "hpd-plug")); |
337 | s3c_gpio_cfgpin(EXYNOS4_GPX3(7), S3C_GPIO_SFN(0x3)); | 330 | s3c_gpio_cfgpin(EXYNOS4_GPX3(7), S3C_GPIO_SFN(0x3)); |
338 | s3c_gpio_setpull(EXYNOS4_GPX3(7), S3C_GPIO_PULL_NONE); | 331 | s3c_gpio_setpull(EXYNOS4_GPX3(7), S3C_GPIO_PULL_NONE); |
339 | |||
340 | /* setup dependencies between TV devices */ | ||
341 | s5p_device_hdmi.dev.parent = &exynos4_device_pd[PD_TV].dev; | ||
342 | s5p_device_mixer.dev.parent = &exynos4_device_pd[PD_TV].dev; | ||
343 | } | 332 | } |
344 | 333 | ||
345 | static void __init smdkv310_map_io(void) | 334 | static void __init smdkv310_map_io(void) |
@@ -379,7 +368,6 @@ static void __init smdkv310_machine_init(void) | |||
379 | clk_xusbxti.rate = 24000000; | 368 | clk_xusbxti.rate = 24000000; |
380 | 369 | ||
381 | platform_add_devices(smdkv310_devices, ARRAY_SIZE(smdkv310_devices)); | 370 | platform_add_devices(smdkv310_devices, ARRAY_SIZE(smdkv310_devices)); |
382 | s5p_device_mfc.dev.parent = &exynos4_device_pd[PD_MFC].dev; | ||
383 | } | 371 | } |
384 | 372 | ||
385 | MACHINE_START(SMDKV310, "SMDKV310") | 373 | 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 0fc65ffde8ff..3de57a74a045 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 = { | |||
969 | &s3c_device_i2c5, | 969 | &s3c_device_i2c5, |
970 | &s5p_device_i2c_hdmiphy, | 970 | &s5p_device_i2c_hdmiphy, |
971 | &hdmi_fixed_voltage, | 971 | &hdmi_fixed_voltage, |
972 | &exynos4_device_pd[PD_TV], | ||
973 | &s5p_device_hdmi, | 972 | &s5p_device_hdmi, |
974 | &s5p_device_sdo, | 973 | &s5p_device_sdo, |
975 | &s5p_device_mixer, | 974 | &s5p_device_mixer, |
@@ -982,9 +981,6 @@ static struct platform_device *universal_devices[] __initdata = { | |||
982 | &s5p_device_mfc, | 981 | &s5p_device_mfc, |
983 | &s5p_device_mfc_l, | 982 | &s5p_device_mfc_l, |
984 | &s5p_device_mfc_r, | 983 | &s5p_device_mfc_r, |
985 | &exynos4_device_pd[PD_MFC], | ||
986 | &exynos4_device_pd[PD_LCD0], | ||
987 | &exynos4_device_pd[PD_CAM], | ||
988 | &cam_i_core_fixed_reg_dev, | 984 | &cam_i_core_fixed_reg_dev, |
989 | &cam_s_if_fixed_reg_dev, | 985 | &cam_s_if_fixed_reg_dev, |
990 | &s5p_device_fimc_md, | 986 | &s5p_device_fimc_md, |
@@ -1003,10 +999,6 @@ void s5p_tv_setup(void) | |||
1003 | gpio_request_one(EXYNOS4_GPX3(7), GPIOF_IN, "hpd-plug"); | 999 | gpio_request_one(EXYNOS4_GPX3(7), GPIOF_IN, "hpd-plug"); |
1004 | s3c_gpio_cfgpin(EXYNOS4_GPX3(7), S3C_GPIO_SFN(0x3)); | 1000 | s3c_gpio_cfgpin(EXYNOS4_GPX3(7), S3C_GPIO_SFN(0x3)); |
1005 | s3c_gpio_setpull(EXYNOS4_GPX3(7), S3C_GPIO_PULL_NONE); | 1001 | s3c_gpio_setpull(EXYNOS4_GPX3(7), S3C_GPIO_PULL_NONE); |
1006 | |||
1007 | /* setup dependencies between TV devices */ | ||
1008 | s5p_device_hdmi.dev.parent = &exynos4_device_pd[PD_TV].dev; | ||
1009 | s5p_device_mixer.dev.parent = &exynos4_device_pd[PD_TV].dev; | ||
1010 | } | 1002 | } |
1011 | 1003 | ||
1012 | static void __init universal_reserve(void) | 1004 | static void __init universal_reserve(void) |
@@ -1040,15 +1032,6 @@ static void __init universal_machine_init(void) | |||
1040 | 1032 | ||
1041 | /* Last */ | 1033 | /* Last */ |
1042 | platform_add_devices(universal_devices, ARRAY_SIZE(universal_devices)); | 1034 | platform_add_devices(universal_devices, ARRAY_SIZE(universal_devices)); |
1043 | |||
1044 | s5p_device_mfc.dev.parent = &exynos4_device_pd[PD_MFC].dev; | ||
1045 | s5p_device_fimd0.dev.parent = &exynos4_device_pd[PD_LCD0].dev; | ||
1046 | |||
1047 | s5p_device_fimc0.dev.parent = &exynos4_device_pd[PD_CAM].dev; | ||
1048 | s5p_device_fimc1.dev.parent = &exynos4_device_pd[PD_CAM].dev; | ||
1049 | s5p_device_fimc2.dev.parent = &exynos4_device_pd[PD_CAM].dev; | ||
1050 | s5p_device_fimc3.dev.parent = &exynos4_device_pd[PD_CAM].dev; | ||
1051 | s5p_device_mipi_csis0.dev.parent = &exynos4_device_pd[PD_CAM].dev; | ||
1052 | } | 1035 | } |
1053 | 1036 | ||
1054 | MACHINE_START(UNIVERSAL_C210, "UNIVERSAL_C210") | 1037 | 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 @@ | |||
1 | /* | ||
2 | * Exynos Generic power domain support. | ||
3 | * | ||
4 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
5 | * http://www.samsung.com | ||
6 | * | ||
7 | * Implementation of Exynos specific power domain control which is used in | ||
8 | * conjunction with runtime-pm. Support for both device-tree and non-device-tree | ||
9 | * based power domain support is included. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | */ | ||
15 | |||
16 | #include <linux/io.h> | ||
17 | #include <linux/err.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/pm_domain.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/of_address.h> | ||
22 | |||
23 | #include <mach/regs-pmu.h> | ||
24 | #include <plat/devs.h> | ||
25 | |||
26 | /* | ||
27 | * Exynos specific wrapper around the generic power domain | ||
28 | */ | ||
29 | struct exynos_pm_domain { | ||
30 | void __iomem *base; | ||
31 | char const *name; | ||
32 | bool is_off; | ||
33 | struct generic_pm_domain pd; | ||
34 | }; | ||
35 | |||
36 | static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) | ||
37 | { | ||
38 | struct exynos_pm_domain *pd; | ||
39 | void __iomem *base; | ||
40 | u32 timeout, pwr; | ||
41 | char *op; | ||
42 | |||
43 | pd = container_of(domain, struct exynos_pm_domain, pd); | ||
44 | base = pd->base; | ||
45 | |||
46 | pwr = power_on ? S5P_INT_LOCAL_PWR_EN : 0; | ||
47 | __raw_writel(pwr, base); | ||
48 | |||
49 | /* Wait max 1ms */ | ||
50 | timeout = 10; | ||
51 | |||
52 | while ((__raw_readl(base + 0x4) & S5P_INT_LOCAL_PWR_EN) != pwr) { | ||
53 | if (!timeout) { | ||
54 | op = (power_on) ? "enable" : "disable"; | ||
55 | pr_err("Power domain %s %s failed\n", domain->name, op); | ||
56 | return -ETIMEDOUT; | ||
57 | } | ||
58 | timeout--; | ||
59 | cpu_relax(); | ||
60 | usleep_range(80, 100); | ||
61 | } | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | static int exynos_pd_power_on(struct generic_pm_domain *domain) | ||
66 | { | ||
67 | return exynos_pd_power(domain, true); | ||
68 | } | ||
69 | |||
70 | static int exynos_pd_power_off(struct generic_pm_domain *domain) | ||
71 | { | ||
72 | return exynos_pd_power(domain, false); | ||
73 | } | ||
74 | |||
75 | #define EXYNOS_GPD(PD, BASE, NAME) \ | ||
76 | static struct exynos_pm_domain PD = { \ | ||
77 | .base = (void __iomem *)BASE, \ | ||
78 | .name = NAME, \ | ||
79 | .pd = { \ | ||
80 | .power_off = exynos_pd_power_off, \ | ||
81 | .power_on = exynos_pd_power_on, \ | ||
82 | }, \ | ||
83 | } | ||
84 | |||
85 | #ifdef CONFIG_OF | ||
86 | static __init int exynos_pm_dt_parse_domains(void) | ||
87 | { | ||
88 | struct device_node *np; | ||
89 | |||
90 | for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { | ||
91 | struct exynos_pm_domain *pd; | ||
92 | |||
93 | pd = kzalloc(sizeof(*pd), GFP_KERNEL); | ||
94 | if (!pd) { | ||
95 | pr_err("%s: failed to allocate memory for domain\n", | ||
96 | __func__); | ||
97 | return -ENOMEM; | ||
98 | } | ||
99 | |||
100 | if (of_get_property(np, "samsung,exynos4210-pd-off", NULL)) | ||
101 | pd->is_off = true; | ||
102 | pd->name = np->name; | ||
103 | pd->base = of_iomap(np, 0); | ||
104 | pd->pd.power_off = exynos_pd_power_off; | ||
105 | pd->pd.power_on = exynos_pd_power_on; | ||
106 | pd->pd.of_node = np; | ||
107 | pm_genpd_init(&pd->pd, NULL, false); | ||
108 | } | ||
109 | return 0; | ||
110 | } | ||
111 | #else | ||
112 | static __init int exynos_pm_dt_parse_domains(void) | ||
113 | { | ||
114 | return 0; | ||
115 | } | ||
116 | #endif /* CONFIG_OF */ | ||
117 | |||
118 | static __init void exynos_pm_add_dev_to_genpd(struct platform_device *pdev, | ||
119 | struct exynos_pm_domain *pd) | ||
120 | { | ||
121 | if (pdev->dev.bus) { | ||
122 | if (pm_genpd_add_device(&pd->pd, &pdev->dev)) | ||
123 | pr_info("%s: error in adding %s device to %s power" | ||
124 | "domain\n", __func__, dev_name(&pdev->dev), | ||
125 | pd->name); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | EXYNOS_GPD(exynos4_pd_mfc, S5P_PMU_MFC_CONF, "pd-mfc"); | ||
130 | EXYNOS_GPD(exynos4_pd_g3d, S5P_PMU_G3D_CONF, "pd-g3d"); | ||
131 | EXYNOS_GPD(exynos4_pd_lcd0, S5P_PMU_LCD0_CONF, "pd-lcd0"); | ||
132 | EXYNOS_GPD(exynos4_pd_lcd1, S5P_PMU_LCD1_CONF, "pd-lcd1"); | ||
133 | EXYNOS_GPD(exynos4_pd_tv, S5P_PMU_TV_CONF, "pd-tv"); | ||
134 | EXYNOS_GPD(exynos4_pd_cam, S5P_PMU_CAM_CONF, "pd-cam"); | ||
135 | EXYNOS_GPD(exynos4_pd_gps, S5P_PMU_GPS_CONF, "pd-gps"); | ||
136 | |||
137 | static struct exynos_pm_domain *exynos4_pm_domains[] = { | ||
138 | &exynos4_pd_mfc, | ||
139 | &exynos4_pd_g3d, | ||
140 | &exynos4_pd_lcd0, | ||
141 | &exynos4_pd_lcd1, | ||
142 | &exynos4_pd_tv, | ||
143 | &exynos4_pd_cam, | ||
144 | &exynos4_pd_gps, | ||
145 | }; | ||
146 | |||
147 | static __init int exynos4_pm_init_power_domain(void) | ||
148 | { | ||
149 | int idx; | ||
150 | |||
151 | if (of_have_populated_dt()) | ||
152 | return exynos_pm_dt_parse_domains(); | ||
153 | |||
154 | for (idx = 0; idx < ARRAY_SIZE(exynos4_pm_domains); idx++) | ||
155 | pm_genpd_init(&exynos4_pm_domains[idx]->pd, NULL, | ||
156 | exynos4_pm_domains[idx]->is_off); | ||
157 | |||
158 | #ifdef CONFIG_S5P_DEV_FIMD0 | ||
159 | exynos_pm_add_dev_to_genpd(&s5p_device_fimd0, &exynos4_pd_lcd0); | ||
160 | #endif | ||
161 | #ifdef CONFIG_S5P_DEV_TV | ||
162 | exynos_pm_add_dev_to_genpd(&s5p_device_hdmi, &exynos4_pd_tv); | ||
163 | exynos_pm_add_dev_to_genpd(&s5p_device_mixer, &exynos4_pd_tv); | ||
164 | #endif | ||
165 | #ifdef CONFIG_S5P_DEV_MFC | ||
166 | exynos_pm_add_dev_to_genpd(&s5p_device_mfc, &exynos4_pd_mfc); | ||
167 | #endif | ||
168 | #ifdef CONFIG_S5P_DEV_FIMC0 | ||
169 | exynos_pm_add_dev_to_genpd(&s5p_device_fimc0, &exynos4_pd_cam); | ||
170 | #endif | ||
171 | #ifdef CONFIG_S5P_DEV_FIMC1 | ||
172 | exynos_pm_add_dev_to_genpd(&s5p_device_fimc1, &exynos4_pd_cam); | ||
173 | #endif | ||
174 | #ifdef CONFIG_S5P_DEV_FIMC2 | ||
175 | exynos_pm_add_dev_to_genpd(&s5p_device_fimc2, &exynos4_pd_cam); | ||
176 | #endif | ||
177 | #ifdef CONFIG_S5P_DEV_FIMC3 | ||
178 | exynos_pm_add_dev_to_genpd(&s5p_device_fimc3, &exynos4_pd_cam); | ||
179 | #endif | ||
180 | #ifdef CONFIG_S5P_DEV_CSIS0 | ||
181 | exynos_pm_add_dev_to_genpd(&s5p_device_mipi_csis0, &exynos4_pd_cam); | ||
182 | #endif | ||
183 | #ifdef CONFIG_S5P_DEV_CSIS1 | ||
184 | exynos_pm_add_dev_to_genpd(&s5p_device_mipi_csis1, &exynos4_pd_cam); | ||
185 | #endif | ||
186 | return 0; | ||
187 | } | ||
188 | arch_initcall(exynos4_pm_init_power_domain); | ||
189 | |||
190 | static __init int exynos_pm_late_initcall(void) | ||
191 | { | ||
192 | pm_genpd_poweroff_unused(); | ||
193 | return 0; | ||
194 | } | ||
195 | late_initcall(exynos_pm_late_initcall); | ||
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 978bbf7ac6af..d2c03239abcf 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c | |||
@@ -820,17 +820,16 @@ static int pm_genpd_suspend(struct device *dev) | |||
820 | } | 820 | } |
821 | 821 | ||
822 | /** | 822 | /** |
823 | * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain. | 823 | * pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain. |
824 | * @dev: Device to suspend. | 824 | * @dev: Device to suspend. |
825 | * | 825 | * |
826 | * Carry out a late suspend of a device under the assumption that its | 826 | * Carry out a late suspend of a device under the assumption that its |
827 | * pm_domain field points to the domain member of an object of type | 827 | * pm_domain field points to the domain member of an object of type |
828 | * struct generic_pm_domain representing a PM domain consisting of I/O devices. | 828 | * struct generic_pm_domain representing a PM domain consisting of I/O devices. |
829 | */ | 829 | */ |
830 | static int pm_genpd_suspend_noirq(struct device *dev) | 830 | static int pm_genpd_suspend_late(struct device *dev) |
831 | { | 831 | { |
832 | struct generic_pm_domain *genpd; | 832 | struct generic_pm_domain *genpd; |
833 | int ret; | ||
834 | 833 | ||
835 | dev_dbg(dev, "%s()\n", __func__); | 834 | dev_dbg(dev, "%s()\n", __func__); |
836 | 835 | ||
@@ -838,14 +837,28 @@ static int pm_genpd_suspend_noirq(struct device *dev) | |||
838 | if (IS_ERR(genpd)) | 837 | if (IS_ERR(genpd)) |
839 | return -EINVAL; | 838 | return -EINVAL; |
840 | 839 | ||
841 | if (genpd->suspend_power_off) | 840 | return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev); |
842 | return 0; | 841 | } |
843 | 842 | ||
844 | ret = genpd_suspend_late(genpd, dev); | 843 | /** |
845 | if (ret) | 844 | * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. |
846 | return ret; | 845 | * @dev: Device to suspend. |
846 | * | ||
847 | * Stop the device and remove power from the domain if all devices in it have | ||
848 | * been stopped. | ||
849 | */ | ||
850 | static int pm_genpd_suspend_noirq(struct device *dev) | ||
851 | { | ||
852 | struct generic_pm_domain *genpd; | ||
847 | 853 | ||
848 | if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) | 854 | dev_dbg(dev, "%s()\n", __func__); |
855 | |||
856 | genpd = dev_to_genpd(dev); | ||
857 | if (IS_ERR(genpd)) | ||
858 | return -EINVAL; | ||
859 | |||
860 | if (genpd->suspend_power_off | ||
861 | || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) | ||
849 | return 0; | 862 | return 0; |
850 | 863 | ||
851 | genpd_stop_dev(genpd, dev); | 864 | genpd_stop_dev(genpd, dev); |
@@ -862,13 +875,10 @@ static int pm_genpd_suspend_noirq(struct device *dev) | |||
862 | } | 875 | } |
863 | 876 | ||
864 | /** | 877 | /** |
865 | * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain. | 878 | * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. |
866 | * @dev: Device to resume. | 879 | * @dev: Device to resume. |
867 | * | 880 | * |
868 | * Carry out an early resume of a device under the assumption that its | 881 | * Restore power to the device's PM domain, if necessary, and start the device. |
869 | * pm_domain field points to the domain member of an object of type | ||
870 | * struct generic_pm_domain representing a power domain consisting of I/O | ||
871 | * devices. | ||
872 | */ | 882 | */ |
873 | static int pm_genpd_resume_noirq(struct device *dev) | 883 | static int pm_genpd_resume_noirq(struct device *dev) |
874 | { | 884 | { |
@@ -890,13 +900,34 @@ static int pm_genpd_resume_noirq(struct device *dev) | |||
890 | */ | 900 | */ |
891 | pm_genpd_poweron(genpd); | 901 | pm_genpd_poweron(genpd); |
892 | genpd->suspended_count--; | 902 | genpd->suspended_count--; |
893 | genpd_start_dev(genpd, dev); | ||
894 | 903 | ||
895 | return genpd_resume_early(genpd, dev); | 904 | return genpd_start_dev(genpd, dev); |
896 | } | 905 | } |
897 | 906 | ||
898 | /** | 907 | /** |
899 | * pm_genpd_resume - Resume a device belonging to an I/O power domain. | 908 | * pm_genpd_resume_early - Early resume of a device in an I/O PM domain. |
909 | * @dev: Device to resume. | ||
910 | * | ||
911 | * Carry out an early resume of a device under the assumption that its | ||
912 | * pm_domain field points to the domain member of an object of type | ||
913 | * struct generic_pm_domain representing a power domain consisting of I/O | ||
914 | * devices. | ||
915 | */ | ||
916 | static int pm_genpd_resume_early(struct device *dev) | ||
917 | { | ||
918 | struct generic_pm_domain *genpd; | ||
919 | |||
920 | dev_dbg(dev, "%s()\n", __func__); | ||
921 | |||
922 | genpd = dev_to_genpd(dev); | ||
923 | if (IS_ERR(genpd)) | ||
924 | return -EINVAL; | ||
925 | |||
926 | return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev); | ||
927 | } | ||
928 | |||
929 | /** | ||
930 | * pm_genpd_resume - Resume of device in an I/O PM domain. | ||
900 | * @dev: Device to resume. | 931 | * @dev: Device to resume. |
901 | * | 932 | * |
902 | * Resume a device under the assumption that its pm_domain field points to the | 933 | * Resume a device under the assumption that its pm_domain field points to the |
@@ -917,7 +948,7 @@ static int pm_genpd_resume(struct device *dev) | |||
917 | } | 948 | } |
918 | 949 | ||
919 | /** | 950 | /** |
920 | * pm_genpd_freeze - Freeze a device belonging to an I/O power domain. | 951 | * pm_genpd_freeze - Freezing a device in an I/O PM domain. |
921 | * @dev: Device to freeze. | 952 | * @dev: Device to freeze. |
922 | * | 953 | * |
923 | * Freeze a device under the assumption that its pm_domain field points to the | 954 | * Freeze a device under the assumption that its pm_domain field points to the |
@@ -938,7 +969,29 @@ static int pm_genpd_freeze(struct device *dev) | |||
938 | } | 969 | } |
939 | 970 | ||
940 | /** | 971 | /** |
941 | * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain. | 972 | * pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain. |
973 | * @dev: Device to freeze. | ||
974 | * | ||
975 | * Carry out a late freeze of a device under the assumption that its | ||
976 | * pm_domain field points to the domain member of an object of type | ||
977 | * struct generic_pm_domain representing a power domain consisting of I/O | ||
978 | * devices. | ||
979 | */ | ||
980 | static int pm_genpd_freeze_late(struct device *dev) | ||
981 | { | ||
982 | struct generic_pm_domain *genpd; | ||
983 | |||
984 | dev_dbg(dev, "%s()\n", __func__); | ||
985 | |||
986 | genpd = dev_to_genpd(dev); | ||
987 | if (IS_ERR(genpd)) | ||
988 | return -EINVAL; | ||
989 | |||
990 | return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev); | ||
991 | } | ||
992 | |||
993 | /** | ||
994 | * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. | ||
942 | * @dev: Device to freeze. | 995 | * @dev: Device to freeze. |
943 | * | 996 | * |
944 | * Carry out a late freeze of a device under the assumption that its | 997 | * Carry out a late freeze of a device under the assumption that its |
@@ -949,7 +1002,6 @@ static int pm_genpd_freeze(struct device *dev) | |||
949 | static int pm_genpd_freeze_noirq(struct device *dev) | 1002 | static int pm_genpd_freeze_noirq(struct device *dev) |
950 | { | 1003 | { |
951 | struct generic_pm_domain *genpd; | 1004 | struct generic_pm_domain *genpd; |
952 | int ret; | ||
953 | 1005 | ||
954 | dev_dbg(dev, "%s()\n", __func__); | 1006 | dev_dbg(dev, "%s()\n", __func__); |
955 | 1007 | ||
@@ -957,20 +1009,31 @@ static int pm_genpd_freeze_noirq(struct device *dev) | |||
957 | if (IS_ERR(genpd)) | 1009 | if (IS_ERR(genpd)) |
958 | return -EINVAL; | 1010 | return -EINVAL; |
959 | 1011 | ||
960 | if (genpd->suspend_power_off) | 1012 | return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev); |
961 | return 0; | 1013 | } |
962 | 1014 | ||
963 | ret = genpd_freeze_late(genpd, dev); | 1015 | /** |
964 | if (ret) | 1016 | * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. |
965 | return ret; | 1017 | * @dev: Device to thaw. |
1018 | * | ||
1019 | * Start the device, unless power has been removed from the domain already | ||
1020 | * before the system transition. | ||
1021 | */ | ||
1022 | static int pm_genpd_thaw_noirq(struct device *dev) | ||
1023 | { | ||
1024 | struct generic_pm_domain *genpd; | ||
966 | 1025 | ||
967 | genpd_stop_dev(genpd, dev); | 1026 | dev_dbg(dev, "%s()\n", __func__); |
968 | 1027 | ||
969 | return 0; | 1028 | genpd = dev_to_genpd(dev); |
1029 | if (IS_ERR(genpd)) | ||
1030 | return -EINVAL; | ||
1031 | |||
1032 | return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev); | ||
970 | } | 1033 | } |
971 | 1034 | ||
972 | /** | 1035 | /** |
973 | * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain. | 1036 | * pm_genpd_thaw_early - Early thaw of device in an I/O PM domain. |
974 | * @dev: Device to thaw. | 1037 | * @dev: Device to thaw. |
975 | * | 1038 | * |
976 | * Carry out an early thaw of a device under the assumption that its | 1039 | * Carry out an early thaw of a device under the assumption that its |
@@ -978,7 +1041,7 @@ static int pm_genpd_freeze_noirq(struct device *dev) | |||
978 | * struct generic_pm_domain representing a power domain consisting of I/O | 1041 | * struct generic_pm_domain representing a power domain consisting of I/O |
979 | * devices. | 1042 | * devices. |
980 | */ | 1043 | */ |
981 | static int pm_genpd_thaw_noirq(struct device *dev) | 1044 | static int pm_genpd_thaw_early(struct device *dev) |
982 | { | 1045 | { |
983 | struct generic_pm_domain *genpd; | 1046 | struct generic_pm_domain *genpd; |
984 | 1047 | ||
@@ -988,12 +1051,7 @@ static int pm_genpd_thaw_noirq(struct device *dev) | |||
988 | if (IS_ERR(genpd)) | 1051 | if (IS_ERR(genpd)) |
989 | return -EINVAL; | 1052 | return -EINVAL; |
990 | 1053 | ||
991 | if (genpd->suspend_power_off) | 1054 | return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev); |
992 | return 0; | ||
993 | |||
994 | genpd_start_dev(genpd, dev); | ||
995 | |||
996 | return genpd_thaw_early(genpd, dev); | ||
997 | } | 1055 | } |
998 | 1056 | ||
999 | /** | 1057 | /** |
@@ -1018,13 +1076,11 @@ static int pm_genpd_thaw(struct device *dev) | |||
1018 | } | 1076 | } |
1019 | 1077 | ||
1020 | /** | 1078 | /** |
1021 | * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain. | 1079 | * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. |
1022 | * @dev: Device to resume. | 1080 | * @dev: Device to resume. |
1023 | * | 1081 | * |
1024 | * Carry out an early restore of a device under the assumption that its | 1082 | * Make sure the domain will be in the same power state as before the |
1025 | * pm_domain field points to the domain member of an object of type | 1083 | * hibernation the system is resuming from and start the device if necessary. |
1026 | * struct generic_pm_domain representing a power domain consisting of I/O | ||
1027 | * devices. | ||
1028 | */ | 1084 | */ |
1029 | static int pm_genpd_restore_noirq(struct device *dev) | 1085 | static int pm_genpd_restore_noirq(struct device *dev) |
1030 | { | 1086 | { |
@@ -1054,9 +1110,8 @@ static int pm_genpd_restore_noirq(struct device *dev) | |||
1054 | 1110 | ||
1055 | pm_genpd_poweron(genpd); | 1111 | pm_genpd_poweron(genpd); |
1056 | genpd->suspended_count--; | 1112 | genpd->suspended_count--; |
1057 | genpd_start_dev(genpd, dev); | ||
1058 | 1113 | ||
1059 | return genpd_resume_early(genpd, dev); | 1114 | return genpd_start_dev(genpd, dev); |
1060 | } | 1115 | } |
1061 | 1116 | ||
1062 | /** | 1117 | /** |
@@ -1099,11 +1154,15 @@ static void pm_genpd_complete(struct device *dev) | |||
1099 | 1154 | ||
1100 | #define pm_genpd_prepare NULL | 1155 | #define pm_genpd_prepare NULL |
1101 | #define pm_genpd_suspend NULL | 1156 | #define pm_genpd_suspend NULL |
1157 | #define pm_genpd_suspend_late NULL | ||
1102 | #define pm_genpd_suspend_noirq NULL | 1158 | #define pm_genpd_suspend_noirq NULL |
1159 | #define pm_genpd_resume_early NULL | ||
1103 | #define pm_genpd_resume_noirq NULL | 1160 | #define pm_genpd_resume_noirq NULL |
1104 | #define pm_genpd_resume NULL | 1161 | #define pm_genpd_resume NULL |
1105 | #define pm_genpd_freeze NULL | 1162 | #define pm_genpd_freeze NULL |
1163 | #define pm_genpd_freeze_late NULL | ||
1106 | #define pm_genpd_freeze_noirq NULL | 1164 | #define pm_genpd_freeze_noirq NULL |
1165 | #define pm_genpd_thaw_early NULL | ||
1107 | #define pm_genpd_thaw_noirq NULL | 1166 | #define pm_genpd_thaw_noirq NULL |
1108 | #define pm_genpd_thaw NULL | 1167 | #define pm_genpd_thaw NULL |
1109 | #define pm_genpd_restore_noirq NULL | 1168 | #define pm_genpd_restore_noirq NULL |
@@ -1171,6 +1230,38 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, | |||
1171 | } | 1230 | } |
1172 | 1231 | ||
1173 | /** | 1232 | /** |
1233 | * __pm_genpd_of_add_device - Add a device to an I/O PM domain. | ||
1234 | * @genpd_node: Device tree node pointer representing a PM domain to which the | ||
1235 | * the device is added to. | ||
1236 | * @dev: Device to be added. | ||
1237 | * @td: Set of PM QoS timing parameters to attach to the device. | ||
1238 | */ | ||
1239 | int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev, | ||
1240 | struct gpd_timing_data *td) | ||
1241 | { | ||
1242 | struct generic_pm_domain *genpd = NULL, *gpd; | ||
1243 | |||
1244 | dev_dbg(dev, "%s()\n", __func__); | ||
1245 | |||
1246 | if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev)) | ||
1247 | return -EINVAL; | ||
1248 | |||
1249 | mutex_lock(&gpd_list_lock); | ||
1250 | list_for_each_entry(gpd, &gpd_list, gpd_list_node) { | ||
1251 | if (gpd->of_node == genpd_node) { | ||
1252 | genpd = gpd; | ||
1253 | break; | ||
1254 | } | ||
1255 | } | ||
1256 | mutex_unlock(&gpd_list_lock); | ||
1257 | |||
1258 | if (!genpd) | ||
1259 | return -EINVAL; | ||
1260 | |||
1261 | return __pm_genpd_add_device(genpd, dev, td); | ||
1262 | } | ||
1263 | |||
1264 | /** | ||
1174 | * pm_genpd_remove_device - Remove a device from an I/O PM domain. | 1265 | * pm_genpd_remove_device - Remove a device from an I/O PM domain. |
1175 | * @genpd: PM domain to remove the device from. | 1266 | * @genpd: PM domain to remove the device from. |
1176 | * @dev: Device to be removed. | 1267 | * @dev: Device to be removed. |
@@ -1450,7 +1541,7 @@ static int pm_genpd_default_suspend_late(struct device *dev) | |||
1450 | { | 1541 | { |
1451 | int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late; | 1542 | int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late; |
1452 | 1543 | ||
1453 | return cb ? cb(dev) : pm_generic_suspend_noirq(dev); | 1544 | return cb ? cb(dev) : pm_generic_suspend_late(dev); |
1454 | } | 1545 | } |
1455 | 1546 | ||
1456 | /** | 1547 | /** |
@@ -1461,7 +1552,7 @@ static int pm_genpd_default_resume_early(struct device *dev) | |||
1461 | { | 1552 | { |
1462 | int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early; | 1553 | int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early; |
1463 | 1554 | ||
1464 | return cb ? cb(dev) : pm_generic_resume_noirq(dev); | 1555 | return cb ? cb(dev) : pm_generic_resume_early(dev); |
1465 | } | 1556 | } |
1466 | 1557 | ||
1467 | /** | 1558 | /** |
@@ -1494,7 +1585,7 @@ static int pm_genpd_default_freeze_late(struct device *dev) | |||
1494 | { | 1585 | { |
1495 | int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late; | 1586 | int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late; |
1496 | 1587 | ||
1497 | return cb ? cb(dev) : pm_generic_freeze_noirq(dev); | 1588 | return cb ? cb(dev) : pm_generic_freeze_late(dev); |
1498 | } | 1589 | } |
1499 | 1590 | ||
1500 | /** | 1591 | /** |
@@ -1505,7 +1596,7 @@ static int pm_genpd_default_thaw_early(struct device *dev) | |||
1505 | { | 1596 | { |
1506 | int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early; | 1597 | int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early; |
1507 | 1598 | ||
1508 | return cb ? cb(dev) : pm_generic_thaw_noirq(dev); | 1599 | return cb ? cb(dev) : pm_generic_thaw_early(dev); |
1509 | } | 1600 | } |
1510 | 1601 | ||
1511 | /** | 1602 | /** |
@@ -1564,16 +1655,22 @@ void pm_genpd_init(struct generic_pm_domain *genpd, | |||
1564 | genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; | 1655 | genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; |
1565 | genpd->domain.ops.prepare = pm_genpd_prepare; | 1656 | genpd->domain.ops.prepare = pm_genpd_prepare; |
1566 | genpd->domain.ops.suspend = pm_genpd_suspend; | 1657 | genpd->domain.ops.suspend = pm_genpd_suspend; |
1658 | genpd->domain.ops.suspend_late = pm_genpd_suspend_late; | ||
1567 | genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; | 1659 | genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; |
1568 | genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; | 1660 | genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; |
1661 | genpd->domain.ops.resume_early = pm_genpd_resume_early; | ||
1569 | genpd->domain.ops.resume = pm_genpd_resume; | 1662 | genpd->domain.ops.resume = pm_genpd_resume; |
1570 | genpd->domain.ops.freeze = pm_genpd_freeze; | 1663 | genpd->domain.ops.freeze = pm_genpd_freeze; |
1664 | genpd->domain.ops.freeze_late = pm_genpd_freeze_late; | ||
1571 | genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; | 1665 | genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; |
1572 | genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; | 1666 | genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; |
1667 | genpd->domain.ops.thaw_early = pm_genpd_thaw_early; | ||
1573 | genpd->domain.ops.thaw = pm_genpd_thaw; | 1668 | genpd->domain.ops.thaw = pm_genpd_thaw; |
1574 | genpd->domain.ops.poweroff = pm_genpd_suspend; | 1669 | genpd->domain.ops.poweroff = pm_genpd_suspend; |
1670 | genpd->domain.ops.poweroff_late = pm_genpd_suspend_late; | ||
1575 | genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; | 1671 | genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; |
1576 | genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; | 1672 | genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; |
1673 | genpd->domain.ops.restore_early = pm_genpd_resume_early; | ||
1577 | genpd->domain.ops.restore = pm_genpd_resume; | 1674 | genpd->domain.ops.restore = pm_genpd_resume; |
1578 | genpd->domain.ops.complete = pm_genpd_complete; | 1675 | genpd->domain.ops.complete = pm_genpd_complete; |
1579 | genpd->dev_ops.save_state = pm_genpd_default_save_state; | 1676 | genpd->dev_ops.save_state = pm_genpd_default_save_state; |
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index a03a0ad998b8..5c2bbc248c11 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h | |||
@@ -11,6 +11,7 @@ | |||
11 | 11 | ||
12 | #include <linux/device.h> | 12 | #include <linux/device.h> |
13 | #include <linux/err.h> | 13 | #include <linux/err.h> |
14 | #include <linux/of.h> | ||
14 | 15 | ||
15 | enum gpd_status { | 16 | enum gpd_status { |
16 | GPD_STATE_ACTIVE = 0, /* PM domain is active */ | 17 | GPD_STATE_ACTIVE = 0, /* PM domain is active */ |
@@ -70,6 +71,7 @@ struct generic_pm_domain { | |||
70 | s64 break_even_ns; /* Power break even for the entire domain. */ | 71 | s64 break_even_ns; /* Power break even for the entire domain. */ |
71 | s64 max_off_time_ns; /* Maximum allowed "suspended" time. */ | 72 | s64 max_off_time_ns; /* Maximum allowed "suspended" time. */ |
72 | ktime_t power_off_time; | 73 | ktime_t power_off_time; |
74 | struct device_node *of_node; /* Node in device tree */ | ||
73 | }; | 75 | }; |
74 | 76 | ||
75 | static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd) | 77 | static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd) |
@@ -99,12 +101,12 @@ struct generic_pm_domain_data { | |||
99 | bool need_restore; | 101 | bool need_restore; |
100 | }; | 102 | }; |
101 | 103 | ||
104 | #ifdef CONFIG_PM_GENERIC_DOMAINS | ||
102 | static inline struct generic_pm_domain_data *to_gpd_data(struct pm_domain_data *pdd) | 105 | static inline struct generic_pm_domain_data *to_gpd_data(struct pm_domain_data *pdd) |
103 | { | 106 | { |
104 | return container_of(pdd, struct generic_pm_domain_data, base); | 107 | return container_of(pdd, struct generic_pm_domain_data, base); |
105 | } | 108 | } |
106 | 109 | ||
107 | #ifdef CONFIG_PM_GENERIC_DOMAINS | ||
108 | static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev) | 110 | static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev) |
109 | { | 111 | { |
110 | return to_gpd_data(dev->power.subsys_data->domain_data); | 112 | return to_gpd_data(dev->power.subsys_data->domain_data); |
@@ -117,12 +119,22 @@ extern int __pm_genpd_add_device(struct generic_pm_domain *genpd, | |||
117 | struct device *dev, | 119 | struct device *dev, |
118 | struct gpd_timing_data *td); | 120 | struct gpd_timing_data *td); |
119 | 121 | ||
122 | extern int __pm_genpd_of_add_device(struct device_node *genpd_node, | ||
123 | struct device *dev, | ||
124 | struct gpd_timing_data *td); | ||
125 | |||
120 | static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, | 126 | static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, |
121 | struct device *dev) | 127 | struct device *dev) |
122 | { | 128 | { |
123 | return __pm_genpd_add_device(genpd, dev, NULL); | 129 | return __pm_genpd_add_device(genpd, dev, NULL); |
124 | } | 130 | } |
125 | 131 | ||
132 | static inline int pm_genpd_of_add_device(struct device_node *genpd_node, | ||
133 | struct device *dev) | ||
134 | { | ||
135 | return __pm_genpd_of_add_device(genpd_node, dev, NULL); | ||
136 | } | ||
137 | |||
126 | extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, | 138 | extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, |
127 | struct device *dev); | 139 | struct device *dev); |
128 | extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, | 140 | extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, |
@@ -143,6 +155,10 @@ extern bool default_stop_ok(struct device *dev); | |||
143 | extern struct dev_power_governor pm_domain_always_on_gov; | 155 | extern struct dev_power_governor pm_domain_always_on_gov; |
144 | #else | 156 | #else |
145 | 157 | ||
158 | static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev) | ||
159 | { | ||
160 | return ERR_PTR(-ENOSYS); | ||
161 | } | ||
146 | static inline struct generic_pm_domain *dev_to_genpd(struct device *dev) | 162 | static inline struct generic_pm_domain *dev_to_genpd(struct device *dev) |
147 | { | 163 | { |
148 | return ERR_PTR(-ENOSYS); | 164 | return ERR_PTR(-ENOSYS); |
@@ -183,7 +199,8 @@ static inline int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) | |||
183 | { | 199 | { |
184 | return -ENOSYS; | 200 | return -ENOSYS; |
185 | } | 201 | } |
186 | static inline void pm_genpd_init(struct generic_pm_domain *genpd, bool is_off) | 202 | static inline void pm_genpd_init(struct generic_pm_domain *genpd, |
203 | struct dev_power_governor *gov, bool is_off) | ||
187 | { | 204 | { |
188 | } | 205 | } |
189 | static inline int pm_genpd_poweron(struct generic_pm_domain *genpd) | 206 | static inline int pm_genpd_poweron(struct generic_pm_domain *genpd) |
@@ -194,6 +211,7 @@ static inline bool default_stop_ok(struct device *dev) | |||
194 | { | 211 | { |
195 | return false; | 212 | return false; |
196 | } | 213 | } |
214 | #define simple_qos_governor NULL | ||
197 | #define pm_domain_always_on_gov NULL | 215 | #define pm_domain_always_on_gov NULL |
198 | #endif | 216 | #endif |
199 | 217 | ||