diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-08-01 18:36:01 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-08-01 18:36:01 -0400 |
commit | 43a0a98aa8da71583f84b84fd72e265c24d4c5f8 (patch) | |
tree | 3830aff2b36f48a67be5f485f00f56cf4269729d /drivers | |
parent | 6911a5281430cf6897376487698504620f454791 (diff) | |
parent | f8c6d88b2c874295f49b9ad1ca0826b9a8ef3180 (diff) |
Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC driver updates from Olof Johansson:
"Driver updates for ARM SoCs.
A slew of changes this release cycle. The reset driver tree, that we
merge through arm-soc for historical reasons, is also sizable this
time around.
Among the changes:
- clps711x: Treewide changes to compatible strings, merged here for simplicity.
- Qualcomm: SCM firmware driver cleanups, move to platform driver
- ux500: Major cleanups, removal of old mach-specific infrastructure.
- Atmel external bus memory driver
- Move of brcmstb platform to the rest of bcm
- PMC driver updates for tegra, various fixes and improvements
- Samsung platform driver updates to support 64-bit Exynos platforms
- Reset controller cleanups moving to devm_reset_controller_register() APIs
- Reset controller driver for Amlogic Meson
- Reset controller driver for Hisilicon hi6220
- ARM SCPI power domain support"
* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (100 commits)
ARM: ux500: consolidate base platform files
ARM: ux500: move soc_id driver to drivers/soc
ARM: ux500: call ux500_setup_id later
ARM: ux500: consolidate soc_device code in id.c
ARM: ux500: remove cpu_is_u* helpers
ARM: ux500: use CLK_OF_DECLARE()
ARM: ux500: move l2x0 init to .init_irq
mfd: db8500 stop passing around platform data
ASoC: ab8500-codec: remove platform data based probe
ARM: ux500: move ab8500_regulator_plat_data into driver
ARM: ux500: remove unused regulator data
soc: raspberrypi-power: add CONFIG_OF dependency
firmware: scpi: add CONFIG_OF dependency
video: clps711x-fb: Changing the compatibility string to match with the smallest supported chip
input: clps711x-keypad: Changing the compatibility string to match with the smallest supported chip
pwm: clps711x: Changing the compatibility string to match with the smallest supported chip
serial: clps711x: Changing the compatibility string to match with the smallest supported chip
irqchip: clps711x: Changing the compatibility string to match with the smallest supported chip
clocksource: clps711x: Changing the compatibility string to match with the smallest supported chip
clk: clps711x: Changing the compatibility string to match with the smallest supported chip
...
Diffstat (limited to 'drivers')
74 files changed, 4005 insertions, 744 deletions
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index c5a7de9bc783..3b205e212337 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig | |||
@@ -132,6 +132,19 @@ config SUNXI_RSB | |||
132 | with various RSB based devices, such as AXP223, AXP8XX PMICs, | 132 | with various RSB based devices, such as AXP223, AXP8XX PMICs, |
133 | and AC100/AC200 ICs. | 133 | and AC100/AC200 ICs. |
134 | 134 | ||
135 | # TODO: This uses pm_clk_*() symbols that aren't exported in v4.7 and hence | ||
136 | # the driver will fail to build as a module. However there are patches to | ||
137 | # address that queued for v4.8, so this can be turned into a tristate symbol | ||
138 | # after v4.8-rc1. | ||
139 | config TEGRA_ACONNECT | ||
140 | bool "Tegra ACONNECT Bus Driver" | ||
141 | depends on ARCH_TEGRA_210_SOC | ||
142 | depends on OF && PM | ||
143 | select PM_CLK | ||
144 | help | ||
145 | Driver for the Tegra ACONNECT bus which is used to interface with | ||
146 | the devices inside the Audio Processing Engine (APE) for Tegra210. | ||
147 | |||
135 | config UNIPHIER_SYSTEM_BUS | 148 | config UNIPHIER_SYSTEM_BUS |
136 | tristate "UniPhier System Bus driver" | 149 | tristate "UniPhier System Bus driver" |
137 | depends on ARCH_UNIPHIER && OF | 150 | depends on ARCH_UNIPHIER && OF |
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index ccff007ee7e8..ac84cc4348e3 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile | |||
@@ -17,5 +17,6 @@ obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o | |||
17 | obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o | 17 | obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o |
18 | obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o | 18 | obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o |
19 | obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o | 19 | obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o |
20 | obj-$(CONFIG_TEGRA_ACONNECT) += tegra-aconnect.o | ||
20 | obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o | 21 | obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o |
21 | obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o | 22 | obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o |
diff --git a/drivers/bus/tegra-aconnect.c b/drivers/bus/tegra-aconnect.c new file mode 100644 index 000000000000..7e4104b74fa8 --- /dev/null +++ b/drivers/bus/tegra-aconnect.c | |||
@@ -0,0 +1,112 @@ | |||
1 | /* | ||
2 | * Tegra ACONNECT Bus Driver | ||
3 | * | ||
4 | * Copyright (C) 2016, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file "COPYING" in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | |||
11 | #include <linux/clk.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/of_platform.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/pm_clock.h> | ||
16 | #include <linux/pm_runtime.h> | ||
17 | |||
18 | static int tegra_aconnect_add_clock(struct device *dev, char *name) | ||
19 | { | ||
20 | struct clk *clk; | ||
21 | int ret; | ||
22 | |||
23 | clk = clk_get(dev, name); | ||
24 | if (IS_ERR(clk)) { | ||
25 | dev_err(dev, "%s clock not found\n", name); | ||
26 | return PTR_ERR(clk); | ||
27 | } | ||
28 | |||
29 | ret = pm_clk_add_clk(dev, clk); | ||
30 | if (ret) | ||
31 | clk_put(clk); | ||
32 | |||
33 | return ret; | ||
34 | } | ||
35 | |||
36 | static int tegra_aconnect_probe(struct platform_device *pdev) | ||
37 | { | ||
38 | int ret; | ||
39 | |||
40 | if (!pdev->dev.of_node) | ||
41 | return -EINVAL; | ||
42 | |||
43 | ret = pm_clk_create(&pdev->dev); | ||
44 | if (ret) | ||
45 | return ret; | ||
46 | |||
47 | ret = tegra_aconnect_add_clock(&pdev->dev, "ape"); | ||
48 | if (ret) | ||
49 | goto clk_destroy; | ||
50 | |||
51 | ret = tegra_aconnect_add_clock(&pdev->dev, "apb2ape"); | ||
52 | if (ret) | ||
53 | goto clk_destroy; | ||
54 | |||
55 | pm_runtime_enable(&pdev->dev); | ||
56 | |||
57 | of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); | ||
58 | |||
59 | dev_info(&pdev->dev, "Tegra ACONNECT bus registered\n"); | ||
60 | |||
61 | return 0; | ||
62 | |||
63 | clk_destroy: | ||
64 | pm_clk_destroy(&pdev->dev); | ||
65 | |||
66 | return ret; | ||
67 | } | ||
68 | |||
69 | static int tegra_aconnect_remove(struct platform_device *pdev) | ||
70 | { | ||
71 | pm_runtime_disable(&pdev->dev); | ||
72 | |||
73 | pm_clk_destroy(&pdev->dev); | ||
74 | |||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static int tegra_aconnect_runtime_resume(struct device *dev) | ||
79 | { | ||
80 | return pm_clk_resume(dev); | ||
81 | } | ||
82 | |||
83 | static int tegra_aconnect_runtime_suspend(struct device *dev) | ||
84 | { | ||
85 | return pm_clk_suspend(dev); | ||
86 | } | ||
87 | |||
88 | static const struct dev_pm_ops tegra_aconnect_pm_ops = { | ||
89 | SET_RUNTIME_PM_OPS(tegra_aconnect_runtime_suspend, | ||
90 | tegra_aconnect_runtime_resume, NULL) | ||
91 | }; | ||
92 | |||
93 | static const struct of_device_id tegra_aconnect_of_match[] = { | ||
94 | { .compatible = "nvidia,tegra210-aconnect", }, | ||
95 | { } | ||
96 | }; | ||
97 | MODULE_DEVICE_TABLE(of, tegra_aconnect_of_match); | ||
98 | |||
99 | static struct platform_driver tegra_aconnect_driver = { | ||
100 | .probe = tegra_aconnect_probe, | ||
101 | .remove = tegra_aconnect_remove, | ||
102 | .driver = { | ||
103 | .name = "tegra-aconnect", | ||
104 | .of_match_table = tegra_aconnect_of_match, | ||
105 | .pm = &tegra_aconnect_pm_ops, | ||
106 | }, | ||
107 | }; | ||
108 | module_platform_driver(tegra_aconnect_driver); | ||
109 | |||
110 | MODULE_DESCRIPTION("NVIDIA Tegra ACONNECT Bus Driver"); | ||
111 | MODULE_AUTHOR("Jon Hunter <jonathanh@nvidia.com>"); | ||
112 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/clk/clk-clps711x.c b/drivers/clk/clk-clps711x.c index 1f60b02416a7..adaf109f2fe2 100644 --- a/drivers/clk/clk-clps711x.c +++ b/drivers/clk/clk-clps711x.c | |||
@@ -184,5 +184,5 @@ static void __init clps711x_clk_init_dt(struct device_node *np) | |||
184 | of_clk_add_provider(np, of_clk_src_onecell_get, | 184 | of_clk_add_provider(np, of_clk_src_onecell_get, |
185 | &clps711x_clk->clk_data); | 185 | &clps711x_clk->clk_data); |
186 | } | 186 | } |
187 | CLK_OF_DECLARE(clps711x, "cirrus,clps711x-clk", clps711x_clk_init_dt); | 187 | CLK_OF_DECLARE(clps711x, "cirrus,ep7209-clk", clps711x_clk_init_dt); |
188 | #endif | 188 | #endif |
diff --git a/drivers/clk/ux500/u8500_of_clk.c b/drivers/clk/ux500/u8500_of_clk.c index 9a736d939806..e960d686d9db 100644 --- a/drivers/clk/ux500/u8500_of_clk.c +++ b/drivers/clk/ux500/u8500_of_clk.c | |||
@@ -11,7 +11,6 @@ | |||
11 | #include <linux/of_address.h> | 11 | #include <linux/of_address.h> |
12 | #include <linux/clk-provider.h> | 12 | #include <linux/clk-provider.h> |
13 | #include <linux/mfd/dbx500-prcmu.h> | 13 | #include <linux/mfd/dbx500-prcmu.h> |
14 | #include <linux/platform_data/clk-ux500.h> | ||
15 | #include "clk.h" | 14 | #include "clk.h" |
16 | 15 | ||
17 | #define PRCC_NUM_PERIPH_CLUSTERS 6 | 16 | #define PRCC_NUM_PERIPH_CLUSTERS 6 |
@@ -48,11 +47,6 @@ static struct clk *ux500_twocell_get(struct of_phandle_args *clkspec, | |||
48 | return PRCC_SHOW(clk_data, base, bit); | 47 | return PRCC_SHOW(clk_data, base, bit); |
49 | } | 48 | } |
50 | 49 | ||
51 | static const struct of_device_id u8500_clk_of_match[] = { | ||
52 | { .compatible = "stericsson,u8500-clks", }, | ||
53 | { }, | ||
54 | }; | ||
55 | |||
56 | /* CLKRST4 is missing making it hard to index things */ | 50 | /* CLKRST4 is missing making it hard to index things */ |
57 | enum clkrst_index { | 51 | enum clkrst_index { |
58 | CLKRST1_INDEX = 0, | 52 | CLKRST1_INDEX = 0, |
@@ -63,22 +57,15 @@ enum clkrst_index { | |||
63 | CLKRST_MAX, | 57 | CLKRST_MAX, |
64 | }; | 58 | }; |
65 | 59 | ||
66 | void u8500_clk_init(void) | 60 | static void u8500_clk_init(struct device_node *np) |
67 | { | 61 | { |
68 | struct prcmu_fw_version *fw_version; | 62 | struct prcmu_fw_version *fw_version; |
69 | struct device_node *np = NULL; | ||
70 | struct device_node *child = NULL; | 63 | struct device_node *child = NULL; |
71 | const char *sgaclk_parent = NULL; | 64 | const char *sgaclk_parent = NULL; |
72 | struct clk *clk, *rtc_clk, *twd_clk; | 65 | struct clk *clk, *rtc_clk, *twd_clk; |
73 | u32 bases[CLKRST_MAX]; | 66 | u32 bases[CLKRST_MAX]; |
74 | int i; | 67 | int i; |
75 | 68 | ||
76 | if (of_have_populated_dt()) | ||
77 | np = of_find_matching_node(NULL, u8500_clk_of_match); | ||
78 | if (!np) { | ||
79 | pr_err("Either DT or U8500 Clock node not found\n"); | ||
80 | return; | ||
81 | } | ||
82 | for (i = 0; i < ARRAY_SIZE(bases); i++) { | 69 | for (i = 0; i < ARRAY_SIZE(bases); i++) { |
83 | struct resource r; | 70 | struct resource r; |
84 | 71 | ||
@@ -573,3 +560,4 @@ void u8500_clk_init(void) | |||
573 | of_clk_add_provider(child, of_clk_src_simple_get, twd_clk); | 560 | of_clk_add_provider(child, of_clk_src_simple_get, twd_clk); |
574 | } | 561 | } |
575 | } | 562 | } |
563 | CLK_OF_DECLARE(u8500_clks, "stericsson,u8500-clks", u8500_clk_init); | ||
diff --git a/drivers/clk/ux500/u8540_clk.c b/drivers/clk/ux500/u8540_clk.c index 86549e59fb42..133859f0e2bf 100644 --- a/drivers/clk/ux500/u8540_clk.c +++ b/drivers/clk/ux500/u8540_clk.c | |||
@@ -12,14 +12,8 @@ | |||
12 | #include <linux/clkdev.h> | 12 | #include <linux/clkdev.h> |
13 | #include <linux/clk-provider.h> | 13 | #include <linux/clk-provider.h> |
14 | #include <linux/mfd/dbx500-prcmu.h> | 14 | #include <linux/mfd/dbx500-prcmu.h> |
15 | #include <linux/platform_data/clk-ux500.h> | ||
16 | #include "clk.h" | 15 | #include "clk.h" |
17 | 16 | ||
18 | static const struct of_device_id u8540_clk_of_match[] = { | ||
19 | { .compatible = "stericsson,u8540-clks", }, | ||
20 | { } | ||
21 | }; | ||
22 | |||
23 | /* CLKRST4 is missing making it hard to index things */ | 17 | /* CLKRST4 is missing making it hard to index things */ |
24 | enum clkrst_index { | 18 | enum clkrst_index { |
25 | CLKRST1_INDEX = 0, | 19 | CLKRST1_INDEX = 0, |
@@ -30,19 +24,12 @@ enum clkrst_index { | |||
30 | CLKRST_MAX, | 24 | CLKRST_MAX, |
31 | }; | 25 | }; |
32 | 26 | ||
33 | void u8540_clk_init(void) | 27 | static void u8540_clk_init(struct device_node *np) |
34 | { | 28 | { |
35 | struct clk *clk; | 29 | struct clk *clk; |
36 | struct device_node *np = NULL; | ||
37 | u32 bases[CLKRST_MAX]; | 30 | u32 bases[CLKRST_MAX]; |
38 | int i; | 31 | int i; |
39 | 32 | ||
40 | if (of_have_populated_dt()) | ||
41 | np = of_find_matching_node(NULL, u8540_clk_of_match); | ||
42 | if (!np) { | ||
43 | pr_err("Either DT or U8540 Clock node not found\n"); | ||
44 | return; | ||
45 | } | ||
46 | for (i = 0; i < ARRAY_SIZE(bases); i++) { | 33 | for (i = 0; i < ARRAY_SIZE(bases); i++) { |
47 | struct resource r; | 34 | struct resource r; |
48 | 35 | ||
@@ -607,3 +594,4 @@ void u8540_clk_init(void) | |||
607 | bases[CLKRST6_INDEX], BIT(0), CLK_SET_RATE_GATE); | 594 | bases[CLKRST6_INDEX], BIT(0), CLK_SET_RATE_GATE); |
608 | clk_register_clkdev(clk, NULL, "rng"); | 595 | clk_register_clkdev(clk, NULL, "rng"); |
609 | } | 596 | } |
597 | CLK_OF_DECLARE(u8540_clks, "stericsson,u8540-clks", u8540_clk_init); | ||
diff --git a/drivers/clk/ux500/u9540_clk.c b/drivers/clk/ux500/u9540_clk.c index 2138a4c8cbca..7b6bca49ce42 100644 --- a/drivers/clk/ux500/u9540_clk.c +++ b/drivers/clk/ux500/u9540_clk.c | |||
@@ -9,10 +9,10 @@ | |||
9 | 9 | ||
10 | #include <linux/clk-provider.h> | 10 | #include <linux/clk-provider.h> |
11 | #include <linux/mfd/dbx500-prcmu.h> | 11 | #include <linux/mfd/dbx500-prcmu.h> |
12 | #include <linux/platform_data/clk-ux500.h> | ||
13 | #include "clk.h" | 12 | #include "clk.h" |
14 | 13 | ||
15 | void u9540_clk_init(void) | 14 | static void u9540_clk_init(struct device_node *np) |
16 | { | 15 | { |
17 | /* register clocks here */ | 16 | /* register clocks here */ |
18 | } | 17 | } |
18 | CLK_OF_DECLARE(u9540_clks, "stericsson,u9540-clks", u9540_clk_init); | ||
diff --git a/drivers/clocksource/clps711x-timer.c b/drivers/clocksource/clps711x-timer.c index 84aed78261e4..24db6d605549 100644 --- a/drivers/clocksource/clps711x-timer.c +++ b/drivers/clocksource/clps711x-timer.c | |||
@@ -119,5 +119,5 @@ static int __init clps711x_timer_init(struct device_node *np) | |||
119 | return -EINVAL; | 119 | return -EINVAL; |
120 | } | 120 | } |
121 | } | 121 | } |
122 | CLOCKSOURCE_OF_DECLARE(clps711x, "cirrus,clps711x-timer", clps711x_timer_init); | 122 | CLOCKSOURCE_OF_DECLARE(clps711x, "cirrus,ep7209-timer", clps711x_timer_init); |
123 | #endif | 123 | #endif |
diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c index 9e07588ea9f5..f82074eea779 100644 --- a/drivers/cpufreq/s5pv210-cpufreq.c +++ b/drivers/cpufreq/s5pv210-cpufreq.c | |||
@@ -220,7 +220,7 @@ static void s5pv210_set_refresh(enum s5pv210_dmc_port ch, unsigned long freq) | |||
220 | 220 | ||
221 | tmp1 /= tmp; | 221 | tmp1 /= tmp; |
222 | 222 | ||
223 | __raw_writel(tmp1, reg); | 223 | writel_relaxed(tmp1, reg); |
224 | } | 224 | } |
225 | 225 | ||
226 | static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) | 226 | static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) |
@@ -296,29 +296,29 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) | |||
296 | * 1. Temporary Change divider for MFC and G3D | 296 | * 1. Temporary Change divider for MFC and G3D |
297 | * SCLKA2M(200/1=200)->(200/4=50)Mhz | 297 | * SCLKA2M(200/1=200)->(200/4=50)Mhz |
298 | */ | 298 | */ |
299 | reg = __raw_readl(S5P_CLK_DIV2); | 299 | reg = readl_relaxed(S5P_CLK_DIV2); |
300 | reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK); | 300 | reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK); |
301 | reg |= (3 << S5P_CLKDIV2_G3D_SHIFT) | | 301 | reg |= (3 << S5P_CLKDIV2_G3D_SHIFT) | |
302 | (3 << S5P_CLKDIV2_MFC_SHIFT); | 302 | (3 << S5P_CLKDIV2_MFC_SHIFT); |
303 | __raw_writel(reg, S5P_CLK_DIV2); | 303 | writel_relaxed(reg, S5P_CLK_DIV2); |
304 | 304 | ||
305 | /* For MFC, G3D dividing */ | 305 | /* For MFC, G3D dividing */ |
306 | do { | 306 | do { |
307 | reg = __raw_readl(S5P_CLKDIV_STAT0); | 307 | reg = readl_relaxed(S5P_CLKDIV_STAT0); |
308 | } while (reg & ((1 << 16) | (1 << 17))); | 308 | } while (reg & ((1 << 16) | (1 << 17))); |
309 | 309 | ||
310 | /* | 310 | /* |
311 | * 2. Change SCLKA2M(200Mhz)to SCLKMPLL in MFC_MUX, G3D MUX | 311 | * 2. Change SCLKA2M(200Mhz)to SCLKMPLL in MFC_MUX, G3D MUX |
312 | * (200/4=50)->(667/4=166)Mhz | 312 | * (200/4=50)->(667/4=166)Mhz |
313 | */ | 313 | */ |
314 | reg = __raw_readl(S5P_CLK_SRC2); | 314 | reg = readl_relaxed(S5P_CLK_SRC2); |
315 | reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK); | 315 | reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK); |
316 | reg |= (1 << S5P_CLKSRC2_G3D_SHIFT) | | 316 | reg |= (1 << S5P_CLKSRC2_G3D_SHIFT) | |
317 | (1 << S5P_CLKSRC2_MFC_SHIFT); | 317 | (1 << S5P_CLKSRC2_MFC_SHIFT); |
318 | __raw_writel(reg, S5P_CLK_SRC2); | 318 | writel_relaxed(reg, S5P_CLK_SRC2); |
319 | 319 | ||
320 | do { | 320 | do { |
321 | reg = __raw_readl(S5P_CLKMUX_STAT1); | 321 | reg = readl_relaxed(S5P_CLKMUX_STAT1); |
322 | } while (reg & ((1 << 7) | (1 << 3))); | 322 | } while (reg & ((1 << 7) | (1 << 3))); |
323 | 323 | ||
324 | /* | 324 | /* |
@@ -330,19 +330,19 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) | |||
330 | s5pv210_set_refresh(DMC1, 133000); | 330 | s5pv210_set_refresh(DMC1, 133000); |
331 | 331 | ||
332 | /* 4. SCLKAPLL -> SCLKMPLL */ | 332 | /* 4. SCLKAPLL -> SCLKMPLL */ |
333 | reg = __raw_readl(S5P_CLK_SRC0); | 333 | reg = readl_relaxed(S5P_CLK_SRC0); |
334 | reg &= ~(S5P_CLKSRC0_MUX200_MASK); | 334 | reg &= ~(S5P_CLKSRC0_MUX200_MASK); |
335 | reg |= (0x1 << S5P_CLKSRC0_MUX200_SHIFT); | 335 | reg |= (0x1 << S5P_CLKSRC0_MUX200_SHIFT); |
336 | __raw_writel(reg, S5P_CLK_SRC0); | 336 | writel_relaxed(reg, S5P_CLK_SRC0); |
337 | 337 | ||
338 | do { | 338 | do { |
339 | reg = __raw_readl(S5P_CLKMUX_STAT0); | 339 | reg = readl_relaxed(S5P_CLKMUX_STAT0); |
340 | } while (reg & (0x1 << 18)); | 340 | } while (reg & (0x1 << 18)); |
341 | 341 | ||
342 | } | 342 | } |
343 | 343 | ||
344 | /* Change divider */ | 344 | /* Change divider */ |
345 | reg = __raw_readl(S5P_CLK_DIV0); | 345 | reg = readl_relaxed(S5P_CLK_DIV0); |
346 | 346 | ||
347 | reg &= ~(S5P_CLKDIV0_APLL_MASK | S5P_CLKDIV0_A2M_MASK | | 347 | reg &= ~(S5P_CLKDIV0_APLL_MASK | S5P_CLKDIV0_A2M_MASK | |
348 | S5P_CLKDIV0_HCLK200_MASK | S5P_CLKDIV0_PCLK100_MASK | | 348 | S5P_CLKDIV0_HCLK200_MASK | S5P_CLKDIV0_PCLK100_MASK | |
@@ -358,25 +358,25 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) | |||
358 | (clkdiv_val[index][6] << S5P_CLKDIV0_HCLK133_SHIFT) | | 358 | (clkdiv_val[index][6] << S5P_CLKDIV0_HCLK133_SHIFT) | |
359 | (clkdiv_val[index][7] << S5P_CLKDIV0_PCLK66_SHIFT)); | 359 | (clkdiv_val[index][7] << S5P_CLKDIV0_PCLK66_SHIFT)); |
360 | 360 | ||
361 | __raw_writel(reg, S5P_CLK_DIV0); | 361 | writel_relaxed(reg, S5P_CLK_DIV0); |
362 | 362 | ||
363 | do { | 363 | do { |
364 | reg = __raw_readl(S5P_CLKDIV_STAT0); | 364 | reg = readl_relaxed(S5P_CLKDIV_STAT0); |
365 | } while (reg & 0xff); | 365 | } while (reg & 0xff); |
366 | 366 | ||
367 | /* ARM MCS value changed */ | 367 | /* ARM MCS value changed */ |
368 | reg = __raw_readl(S5P_ARM_MCS_CON); | 368 | reg = readl_relaxed(S5P_ARM_MCS_CON); |
369 | reg &= ~0x3; | 369 | reg &= ~0x3; |
370 | if (index >= L3) | 370 | if (index >= L3) |
371 | reg |= 0x3; | 371 | reg |= 0x3; |
372 | else | 372 | else |
373 | reg |= 0x1; | 373 | reg |= 0x1; |
374 | 374 | ||
375 | __raw_writel(reg, S5P_ARM_MCS_CON); | 375 | writel_relaxed(reg, S5P_ARM_MCS_CON); |
376 | 376 | ||
377 | if (pll_changing) { | 377 | if (pll_changing) { |
378 | /* 5. Set Lock time = 30us*24Mhz = 0x2cf */ | 378 | /* 5. Set Lock time = 30us*24Mhz = 0x2cf */ |
379 | __raw_writel(0x2cf, S5P_APLL_LOCK); | 379 | writel_relaxed(0x2cf, S5P_APLL_LOCK); |
380 | 380 | ||
381 | /* | 381 | /* |
382 | * 6. Turn on APLL | 382 | * 6. Turn on APLL |
@@ -384,12 +384,12 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) | |||
384 | * 6-2. Wait untile the PLL is locked | 384 | * 6-2. Wait untile the PLL is locked |
385 | */ | 385 | */ |
386 | if (index == L0) | 386 | if (index == L0) |
387 | __raw_writel(APLL_VAL_1000, S5P_APLL_CON); | 387 | writel_relaxed(APLL_VAL_1000, S5P_APLL_CON); |
388 | else | 388 | else |
389 | __raw_writel(APLL_VAL_800, S5P_APLL_CON); | 389 | writel_relaxed(APLL_VAL_800, S5P_APLL_CON); |
390 | 390 | ||
391 | do { | 391 | do { |
392 | reg = __raw_readl(S5P_APLL_CON); | 392 | reg = readl_relaxed(S5P_APLL_CON); |
393 | } while (!(reg & (0x1 << 29))); | 393 | } while (!(reg & (0x1 << 29))); |
394 | 394 | ||
395 | /* | 395 | /* |
@@ -397,39 +397,39 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) | |||
397 | * to SCLKA2M(200Mhz) in MFC_MUX and G3D MUX | 397 | * to SCLKA2M(200Mhz) in MFC_MUX and G3D MUX |
398 | * (667/4=166)->(200/4=50)Mhz | 398 | * (667/4=166)->(200/4=50)Mhz |
399 | */ | 399 | */ |
400 | reg = __raw_readl(S5P_CLK_SRC2); | 400 | reg = readl_relaxed(S5P_CLK_SRC2); |
401 | reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK); | 401 | reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK); |
402 | reg |= (0 << S5P_CLKSRC2_G3D_SHIFT) | | 402 | reg |= (0 << S5P_CLKSRC2_G3D_SHIFT) | |
403 | (0 << S5P_CLKSRC2_MFC_SHIFT); | 403 | (0 << S5P_CLKSRC2_MFC_SHIFT); |
404 | __raw_writel(reg, S5P_CLK_SRC2); | 404 | writel_relaxed(reg, S5P_CLK_SRC2); |
405 | 405 | ||
406 | do { | 406 | do { |
407 | reg = __raw_readl(S5P_CLKMUX_STAT1); | 407 | reg = readl_relaxed(S5P_CLKMUX_STAT1); |
408 | } while (reg & ((1 << 7) | (1 << 3))); | 408 | } while (reg & ((1 << 7) | (1 << 3))); |
409 | 409 | ||
410 | /* | 410 | /* |
411 | * 8. Change divider for MFC and G3D | 411 | * 8. Change divider for MFC and G3D |
412 | * (200/4=50)->(200/1=200)Mhz | 412 | * (200/4=50)->(200/1=200)Mhz |
413 | */ | 413 | */ |
414 | reg = __raw_readl(S5P_CLK_DIV2); | 414 | reg = readl_relaxed(S5P_CLK_DIV2); |
415 | reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK); | 415 | reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK); |
416 | reg |= (clkdiv_val[index][10] << S5P_CLKDIV2_G3D_SHIFT) | | 416 | reg |= (clkdiv_val[index][10] << S5P_CLKDIV2_G3D_SHIFT) | |
417 | (clkdiv_val[index][9] << S5P_CLKDIV2_MFC_SHIFT); | 417 | (clkdiv_val[index][9] << S5P_CLKDIV2_MFC_SHIFT); |
418 | __raw_writel(reg, S5P_CLK_DIV2); | 418 | writel_relaxed(reg, S5P_CLK_DIV2); |
419 | 419 | ||
420 | /* For MFC, G3D dividing */ | 420 | /* For MFC, G3D dividing */ |
421 | do { | 421 | do { |
422 | reg = __raw_readl(S5P_CLKDIV_STAT0); | 422 | reg = readl_relaxed(S5P_CLKDIV_STAT0); |
423 | } while (reg & ((1 << 16) | (1 << 17))); | 423 | } while (reg & ((1 << 16) | (1 << 17))); |
424 | 424 | ||
425 | /* 9. Change MPLL to APLL in MSYS_MUX */ | 425 | /* 9. Change MPLL to APLL in MSYS_MUX */ |
426 | reg = __raw_readl(S5P_CLK_SRC0); | 426 | reg = readl_relaxed(S5P_CLK_SRC0); |
427 | reg &= ~(S5P_CLKSRC0_MUX200_MASK); | 427 | reg &= ~(S5P_CLKSRC0_MUX200_MASK); |
428 | reg |= (0x0 << S5P_CLKSRC0_MUX200_SHIFT); | 428 | reg |= (0x0 << S5P_CLKSRC0_MUX200_SHIFT); |
429 | __raw_writel(reg, S5P_CLK_SRC0); | 429 | writel_relaxed(reg, S5P_CLK_SRC0); |
430 | 430 | ||
431 | do { | 431 | do { |
432 | reg = __raw_readl(S5P_CLKMUX_STAT0); | 432 | reg = readl_relaxed(S5P_CLKMUX_STAT0); |
433 | } while (reg & (0x1 << 18)); | 433 | } while (reg & (0x1 << 18)); |
434 | 434 | ||
435 | /* | 435 | /* |
@@ -446,13 +446,13 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) | |||
446 | * and memory refresh parameter should be changed | 446 | * and memory refresh parameter should be changed |
447 | */ | 447 | */ |
448 | if (bus_speed_changing) { | 448 | if (bus_speed_changing) { |
449 | reg = __raw_readl(S5P_CLK_DIV6); | 449 | reg = readl_relaxed(S5P_CLK_DIV6); |
450 | reg &= ~S5P_CLKDIV6_ONEDRAM_MASK; | 450 | reg &= ~S5P_CLKDIV6_ONEDRAM_MASK; |
451 | reg |= (clkdiv_val[index][8] << S5P_CLKDIV6_ONEDRAM_SHIFT); | 451 | reg |= (clkdiv_val[index][8] << S5P_CLKDIV6_ONEDRAM_SHIFT); |
452 | __raw_writel(reg, S5P_CLK_DIV6); | 452 | writel_relaxed(reg, S5P_CLK_DIV6); |
453 | 453 | ||
454 | do { | 454 | do { |
455 | reg = __raw_readl(S5P_CLKDIV_STAT1); | 455 | reg = readl_relaxed(S5P_CLKDIV_STAT1); |
456 | } while (reg & (1 << 15)); | 456 | } while (reg & (1 << 15)); |
457 | 457 | ||
458 | /* Reconfigure DRAM refresh counter value */ | 458 | /* Reconfigure DRAM refresh counter value */ |
@@ -492,7 +492,7 @@ static int check_mem_type(void __iomem *dmc_reg) | |||
492 | { | 492 | { |
493 | unsigned long val; | 493 | unsigned long val; |
494 | 494 | ||
495 | val = __raw_readl(dmc_reg + 0x4); | 495 | val = readl_relaxed(dmc_reg + 0x4); |
496 | val = (val & (0xf << 8)); | 496 | val = (val & (0xf << 8)); |
497 | 497 | ||
498 | return val >> 8; | 498 | return val >> 8; |
@@ -537,10 +537,10 @@ static int s5pv210_cpu_init(struct cpufreq_policy *policy) | |||
537 | } | 537 | } |
538 | 538 | ||
539 | /* Find current refresh counter and frequency each DMC */ | 539 | /* Find current refresh counter and frequency each DMC */ |
540 | s5pv210_dram_conf[0].refresh = (__raw_readl(dmc_base[0] + 0x30) * 1000); | 540 | s5pv210_dram_conf[0].refresh = (readl_relaxed(dmc_base[0] + 0x30) * 1000); |
541 | s5pv210_dram_conf[0].freq = clk_get_rate(dmc0_clk); | 541 | s5pv210_dram_conf[0].freq = clk_get_rate(dmc0_clk); |
542 | 542 | ||
543 | s5pv210_dram_conf[1].refresh = (__raw_readl(dmc_base[1] + 0x30) * 1000); | 543 | s5pv210_dram_conf[1].refresh = (readl_relaxed(dmc_base[1] + 0x30) * 1000); |
544 | s5pv210_dram_conf[1].freq = clk_get_rate(dmc1_clk); | 544 | s5pv210_dram_conf[1].freq = clk_get_rate(dmc1_clk); |
545 | 545 | ||
546 | policy->suspend_freq = SLEEP_FREQ; | 546 | policy->suspend_freq = SLEEP_FREQ; |
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 6664f1108c7c..0e22f241403b 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig | |||
@@ -10,7 +10,7 @@ config ARM_PSCI_FW | |||
10 | 10 | ||
11 | config ARM_SCPI_PROTOCOL | 11 | config ARM_SCPI_PROTOCOL |
12 | tristate "ARM System Control and Power Interface (SCPI) Message Protocol" | 12 | tristate "ARM System Control and Power Interface (SCPI) Message Protocol" |
13 | depends on ARM_MHU | 13 | depends on MAILBOX |
14 | help | 14 | help |
15 | System Control and Power Interface (SCPI) Message Protocol is | 15 | System Control and Power Interface (SCPI) Message Protocol is |
16 | defined for the purpose of communication between the Application | 16 | defined for the purpose of communication between the Application |
@@ -27,6 +27,15 @@ config ARM_SCPI_PROTOCOL | |||
27 | This protocol library provides interface for all the client drivers | 27 | This protocol library provides interface for all the client drivers |
28 | making use of the features offered by the SCP. | 28 | making use of the features offered by the SCP. |
29 | 29 | ||
30 | config ARM_SCPI_POWER_DOMAIN | ||
31 | tristate "SCPI power domain driver" | ||
32 | depends on ARM_SCPI_PROTOCOL || (COMPILE_TEST && OF) | ||
33 | default y | ||
34 | select PM_GENERIC_DOMAINS if PM | ||
35 | help | ||
36 | This enables support for the SCPI power domains which can be | ||
37 | enabled or disabled via the SCP firmware | ||
38 | |||
30 | config EDD | 39 | config EDD |
31 | tristate "BIOS Enhanced Disk Drive calls determine boot disk" | 40 | tristate "BIOS Enhanced Disk Drive calls determine boot disk" |
32 | depends on X86 | 41 | depends on X86 |
@@ -184,6 +193,7 @@ config FW_CFG_SYSFS_CMDLINE | |||
184 | config QCOM_SCM | 193 | config QCOM_SCM |
185 | bool | 194 | bool |
186 | depends on ARM || ARM64 | 195 | depends on ARM || ARM64 |
196 | select RESET_CONTROLLER | ||
187 | 197 | ||
188 | config QCOM_SCM_32 | 198 | config QCOM_SCM_32 |
189 | def_bool y | 199 | def_bool y |
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 474bada56fcd..44a59dcfc398 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile | |||
@@ -3,6 +3,7 @@ | |||
3 | # | 3 | # |
4 | obj-$(CONFIG_ARM_PSCI_FW) += psci.o | 4 | obj-$(CONFIG_ARM_PSCI_FW) += psci.o |
5 | obj-$(CONFIG_ARM_SCPI_PROTOCOL) += arm_scpi.o | 5 | obj-$(CONFIG_ARM_SCPI_PROTOCOL) += arm_scpi.o |
6 | obj-$(CONFIG_ARM_SCPI_POWER_DOMAIN) += scpi_pm_domain.o | ||
6 | obj-$(CONFIG_DMI) += dmi_scan.o | 7 | obj-$(CONFIG_DMI) += dmi_scan.o |
7 | obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o | 8 | obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o |
8 | obj-$(CONFIG_EDD) += edd.o | 9 | obj-$(CONFIG_EDD) += edd.o |
diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index 7e3e595c9f30..438893762076 100644 --- a/drivers/firmware/arm_scpi.c +++ b/drivers/firmware/arm_scpi.c | |||
@@ -210,10 +210,6 @@ struct dvfs_info { | |||
210 | } opps[MAX_DVFS_OPPS]; | 210 | } opps[MAX_DVFS_OPPS]; |
211 | } __packed; | 211 | } __packed; |
212 | 212 | ||
213 | struct dvfs_get { | ||
214 | u8 index; | ||
215 | } __packed; | ||
216 | |||
217 | struct dvfs_set { | 213 | struct dvfs_set { |
218 | u8 domain; | 214 | u8 domain; |
219 | u8 index; | 215 | u8 index; |
@@ -235,6 +231,11 @@ struct sensor_value { | |||
235 | __le32 hi_val; | 231 | __le32 hi_val; |
236 | } __packed; | 232 | } __packed; |
237 | 233 | ||
234 | struct dev_pstate_set { | ||
235 | u16 dev_id; | ||
236 | u8 pstate; | ||
237 | } __packed; | ||
238 | |||
238 | static struct scpi_drvinfo *scpi_info; | 239 | static struct scpi_drvinfo *scpi_info; |
239 | 240 | ||
240 | static int scpi_linux_errmap[SCPI_ERR_MAX] = { | 241 | static int scpi_linux_errmap[SCPI_ERR_MAX] = { |
@@ -431,11 +432,11 @@ static int scpi_clk_set_val(u16 clk_id, unsigned long rate) | |||
431 | static int scpi_dvfs_get_idx(u8 domain) | 432 | static int scpi_dvfs_get_idx(u8 domain) |
432 | { | 433 | { |
433 | int ret; | 434 | int ret; |
434 | struct dvfs_get dvfs; | 435 | u8 dvfs_idx; |
435 | 436 | ||
436 | ret = scpi_send_message(SCPI_CMD_GET_DVFS, &domain, sizeof(domain), | 437 | ret = scpi_send_message(SCPI_CMD_GET_DVFS, &domain, sizeof(domain), |
437 | &dvfs, sizeof(dvfs)); | 438 | &dvfs_idx, sizeof(dvfs_idx)); |
438 | return ret ? ret : dvfs.index; | 439 | return ret ? ret : dvfs_idx; |
439 | } | 440 | } |
440 | 441 | ||
441 | static int scpi_dvfs_set_idx(u8 domain, u8 index) | 442 | static int scpi_dvfs_set_idx(u8 domain, u8 index) |
@@ -526,7 +527,7 @@ static int scpi_sensor_get_info(u16 sensor_id, struct scpi_sensor_info *info) | |||
526 | return ret; | 527 | return ret; |
527 | } | 528 | } |
528 | 529 | ||
529 | int scpi_sensor_get_value(u16 sensor, u64 *val) | 530 | static int scpi_sensor_get_value(u16 sensor, u64 *val) |
530 | { | 531 | { |
531 | __le16 id = cpu_to_le16(sensor); | 532 | __le16 id = cpu_to_le16(sensor); |
532 | struct sensor_value buf; | 533 | struct sensor_value buf; |
@@ -541,6 +542,29 @@ int scpi_sensor_get_value(u16 sensor, u64 *val) | |||
541 | return ret; | 542 | return ret; |
542 | } | 543 | } |
543 | 544 | ||
545 | static int scpi_device_get_power_state(u16 dev_id) | ||
546 | { | ||
547 | int ret; | ||
548 | u8 pstate; | ||
549 | __le16 id = cpu_to_le16(dev_id); | ||
550 | |||
551 | ret = scpi_send_message(SCPI_CMD_GET_DEVICE_PWR_STATE, &id, | ||
552 | sizeof(id), &pstate, sizeof(pstate)); | ||
553 | return ret ? ret : pstate; | ||
554 | } | ||
555 | |||
556 | static int scpi_device_set_power_state(u16 dev_id, u8 pstate) | ||
557 | { | ||
558 | int stat; | ||
559 | struct dev_pstate_set dev_set = { | ||
560 | .dev_id = cpu_to_le16(dev_id), | ||
561 | .pstate = pstate, | ||
562 | }; | ||
563 | |||
564 | return scpi_send_message(SCPI_CMD_SET_DEVICE_PWR_STATE, &dev_set, | ||
565 | sizeof(dev_set), &stat, sizeof(stat)); | ||
566 | } | ||
567 | |||
544 | static struct scpi_ops scpi_ops = { | 568 | static struct scpi_ops scpi_ops = { |
545 | .get_version = scpi_get_version, | 569 | .get_version = scpi_get_version, |
546 | .clk_get_range = scpi_clk_get_range, | 570 | .clk_get_range = scpi_clk_get_range, |
@@ -552,6 +576,8 @@ static struct scpi_ops scpi_ops = { | |||
552 | .sensor_get_capability = scpi_sensor_get_capability, | 576 | .sensor_get_capability = scpi_sensor_get_capability, |
553 | .sensor_get_info = scpi_sensor_get_info, | 577 | .sensor_get_info = scpi_sensor_get_info, |
554 | .sensor_get_value = scpi_sensor_get_value, | 578 | .sensor_get_value = scpi_sensor_get_value, |
579 | .device_get_power_state = scpi_device_get_power_state, | ||
580 | .device_set_power_state = scpi_device_set_power_state, | ||
555 | }; | 581 | }; |
556 | 582 | ||
557 | struct scpi_ops *get_scpi_ops(void) | 583 | struct scpi_ops *get_scpi_ops(void) |
diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index 0883292f640f..c6aeedbdcbb0 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c | |||
@@ -23,8 +23,7 @@ | |||
23 | #include <linux/errno.h> | 23 | #include <linux/errno.h> |
24 | #include <linux/err.h> | 24 | #include <linux/err.h> |
25 | #include <linux/qcom_scm.h> | 25 | #include <linux/qcom_scm.h> |
26 | 26 | #include <linux/dma-mapping.h> | |
27 | #include <asm/cacheflush.h> | ||
28 | 27 | ||
29 | #include "qcom_scm.h" | 28 | #include "qcom_scm.h" |
30 | 29 | ||
@@ -97,44 +96,6 @@ struct qcom_scm_response { | |||
97 | }; | 96 | }; |
98 | 97 | ||
99 | /** | 98 | /** |
100 | * alloc_qcom_scm_command() - Allocate an SCM command | ||
101 | * @cmd_size: size of the command buffer | ||
102 | * @resp_size: size of the response buffer | ||
103 | * | ||
104 | * Allocate an SCM command, including enough room for the command | ||
105 | * and response headers as well as the command and response buffers. | ||
106 | * | ||
107 | * Returns a valid &qcom_scm_command on success or %NULL if the allocation fails. | ||
108 | */ | ||
109 | static struct qcom_scm_command *alloc_qcom_scm_command(size_t cmd_size, size_t resp_size) | ||
110 | { | ||
111 | struct qcom_scm_command *cmd; | ||
112 | size_t len = sizeof(*cmd) + sizeof(struct qcom_scm_response) + cmd_size + | ||
113 | resp_size; | ||
114 | u32 offset; | ||
115 | |||
116 | cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL); | ||
117 | if (cmd) { | ||
118 | cmd->len = cpu_to_le32(len); | ||
119 | offset = offsetof(struct qcom_scm_command, buf); | ||
120 | cmd->buf_offset = cpu_to_le32(offset); | ||
121 | cmd->resp_hdr_offset = cpu_to_le32(offset + cmd_size); | ||
122 | } | ||
123 | return cmd; | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * free_qcom_scm_command() - Free an SCM command | ||
128 | * @cmd: command to free | ||
129 | * | ||
130 | * Free an SCM command. | ||
131 | */ | ||
132 | static inline void free_qcom_scm_command(struct qcom_scm_command *cmd) | ||
133 | { | ||
134 | kfree(cmd); | ||
135 | } | ||
136 | |||
137 | /** | ||
138 | * qcom_scm_command_to_response() - Get a pointer to a qcom_scm_response | 99 | * qcom_scm_command_to_response() - Get a pointer to a qcom_scm_response |
139 | * @cmd: command | 100 | * @cmd: command |
140 | * | 101 | * |
@@ -168,23 +129,6 @@ static inline void *qcom_scm_get_response_buffer(const struct qcom_scm_response | |||
168 | return (void *)rsp + le32_to_cpu(rsp->buf_offset); | 129 | return (void *)rsp + le32_to_cpu(rsp->buf_offset); |
169 | } | 130 | } |
170 | 131 | ||
171 | static int qcom_scm_remap_error(int err) | ||
172 | { | ||
173 | pr_err("qcom_scm_call failed with error code %d\n", err); | ||
174 | switch (err) { | ||
175 | case QCOM_SCM_ERROR: | ||
176 | return -EIO; | ||
177 | case QCOM_SCM_EINVAL_ADDR: | ||
178 | case QCOM_SCM_EINVAL_ARG: | ||
179 | return -EINVAL; | ||
180 | case QCOM_SCM_EOPNOTSUPP: | ||
181 | return -EOPNOTSUPP; | ||
182 | case QCOM_SCM_ENOMEM: | ||
183 | return -ENOMEM; | ||
184 | } | ||
185 | return -EINVAL; | ||
186 | } | ||
187 | |||
188 | static u32 smc(u32 cmd_addr) | 132 | static u32 smc(u32 cmd_addr) |
189 | { | 133 | { |
190 | int context_id; | 134 | int context_id; |
@@ -209,45 +153,9 @@ static u32 smc(u32 cmd_addr) | |||
209 | return r0; | 153 | return r0; |
210 | } | 154 | } |
211 | 155 | ||
212 | static int __qcom_scm_call(const struct qcom_scm_command *cmd) | ||
213 | { | ||
214 | int ret; | ||
215 | u32 cmd_addr = virt_to_phys(cmd); | ||
216 | |||
217 | /* | ||
218 | * Flush the command buffer so that the secure world sees | ||
219 | * the correct data. | ||
220 | */ | ||
221 | secure_flush_area(cmd, cmd->len); | ||
222 | |||
223 | ret = smc(cmd_addr); | ||
224 | if (ret < 0) | ||
225 | ret = qcom_scm_remap_error(ret); | ||
226 | |||
227 | return ret; | ||
228 | } | ||
229 | |||
230 | static void qcom_scm_inv_range(unsigned long start, unsigned long end) | ||
231 | { | ||
232 | u32 cacheline_size, ctr; | ||
233 | |||
234 | asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr)); | ||
235 | cacheline_size = 4 << ((ctr >> 16) & 0xf); | ||
236 | |||
237 | start = round_down(start, cacheline_size); | ||
238 | end = round_up(end, cacheline_size); | ||
239 | outer_inv_range(start, end); | ||
240 | while (start < end) { | ||
241 | asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) | ||
242 | : "memory"); | ||
243 | start += cacheline_size; | ||
244 | } | ||
245 | dsb(); | ||
246 | isb(); | ||
247 | } | ||
248 | |||
249 | /** | 156 | /** |
250 | * qcom_scm_call() - Send an SCM command | 157 | * qcom_scm_call() - Send an SCM command |
158 | * @dev: struct device | ||
251 | * @svc_id: service identifier | 159 | * @svc_id: service identifier |
252 | * @cmd_id: command identifier | 160 | * @cmd_id: command identifier |
253 | * @cmd_buf: command buffer | 161 | * @cmd_buf: command buffer |
@@ -264,42 +172,59 @@ static void qcom_scm_inv_range(unsigned long start, unsigned long end) | |||
264 | * and response buffers is taken care of by qcom_scm_call; however, callers are | 172 | * and response buffers is taken care of by qcom_scm_call; however, callers are |
265 | * responsible for any other cached buffers passed over to the secure world. | 173 | * responsible for any other cached buffers passed over to the secure world. |
266 | */ | 174 | */ |
267 | static int qcom_scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, | 175 | static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id, |
268 | size_t cmd_len, void *resp_buf, size_t resp_len) | 176 | const void *cmd_buf, size_t cmd_len, void *resp_buf, |
177 | size_t resp_len) | ||
269 | { | 178 | { |
270 | int ret; | 179 | int ret; |
271 | struct qcom_scm_command *cmd; | 180 | struct qcom_scm_command *cmd; |
272 | struct qcom_scm_response *rsp; | 181 | struct qcom_scm_response *rsp; |
273 | unsigned long start, end; | 182 | size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len; |
183 | dma_addr_t cmd_phys; | ||
274 | 184 | ||
275 | cmd = alloc_qcom_scm_command(cmd_len, resp_len); | 185 | cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL); |
276 | if (!cmd) | 186 | if (!cmd) |
277 | return -ENOMEM; | 187 | return -ENOMEM; |
278 | 188 | ||
189 | cmd->len = cpu_to_le32(alloc_len); | ||
190 | cmd->buf_offset = cpu_to_le32(sizeof(*cmd)); | ||
191 | cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len); | ||
192 | |||
279 | cmd->id = cpu_to_le32((svc_id << 10) | cmd_id); | 193 | cmd->id = cpu_to_le32((svc_id << 10) | cmd_id); |
280 | if (cmd_buf) | 194 | if (cmd_buf) |
281 | memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len); | 195 | memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len); |
282 | 196 | ||
197 | rsp = qcom_scm_command_to_response(cmd); | ||
198 | |||
199 | cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE); | ||
200 | if (dma_mapping_error(dev, cmd_phys)) { | ||
201 | kfree(cmd); | ||
202 | return -ENOMEM; | ||
203 | } | ||
204 | |||
283 | mutex_lock(&qcom_scm_lock); | 205 | mutex_lock(&qcom_scm_lock); |
284 | ret = __qcom_scm_call(cmd); | 206 | ret = smc(cmd_phys); |
207 | if (ret < 0) | ||
208 | ret = qcom_scm_remap_error(ret); | ||
285 | mutex_unlock(&qcom_scm_lock); | 209 | mutex_unlock(&qcom_scm_lock); |
286 | if (ret) | 210 | if (ret) |
287 | goto out; | 211 | goto out; |
288 | 212 | ||
289 | rsp = qcom_scm_command_to_response(cmd); | ||
290 | start = (unsigned long)rsp; | ||
291 | |||
292 | do { | 213 | do { |
293 | qcom_scm_inv_range(start, start + sizeof(*rsp)); | 214 | dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len, |
215 | sizeof(*rsp), DMA_FROM_DEVICE); | ||
294 | } while (!rsp->is_complete); | 216 | } while (!rsp->is_complete); |
295 | 217 | ||
296 | end = (unsigned long)qcom_scm_get_response_buffer(rsp) + resp_len; | 218 | if (resp_buf) { |
297 | qcom_scm_inv_range(start, end); | 219 | dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len + |
298 | 220 | le32_to_cpu(rsp->buf_offset), | |
299 | if (resp_buf) | 221 | resp_len, DMA_FROM_DEVICE); |
300 | memcpy(resp_buf, qcom_scm_get_response_buffer(rsp), resp_len); | 222 | memcpy(resp_buf, qcom_scm_get_response_buffer(rsp), |
223 | resp_len); | ||
224 | } | ||
301 | out: | 225 | out: |
302 | free_qcom_scm_command(cmd); | 226 | dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); |
227 | kfree(cmd); | ||
303 | return ret; | 228 | return ret; |
304 | } | 229 | } |
305 | 230 | ||
@@ -342,6 +267,41 @@ static s32 qcom_scm_call_atomic1(u32 svc, u32 cmd, u32 arg1) | |||
342 | return r0; | 267 | return r0; |
343 | } | 268 | } |
344 | 269 | ||
270 | /** | ||
271 | * qcom_scm_call_atomic2() - Send an atomic SCM command with two arguments | ||
272 | * @svc_id: service identifier | ||
273 | * @cmd_id: command identifier | ||
274 | * @arg1: first argument | ||
275 | * @arg2: second argument | ||
276 | * | ||
277 | * This shall only be used with commands that are guaranteed to be | ||
278 | * uninterruptable, atomic and SMP safe. | ||
279 | */ | ||
280 | static s32 qcom_scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2) | ||
281 | { | ||
282 | int context_id; | ||
283 | |||
284 | register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 2); | ||
285 | register u32 r1 asm("r1") = (u32)&context_id; | ||
286 | register u32 r2 asm("r2") = arg1; | ||
287 | register u32 r3 asm("r3") = arg2; | ||
288 | |||
289 | asm volatile( | ||
290 | __asmeq("%0", "r0") | ||
291 | __asmeq("%1", "r0") | ||
292 | __asmeq("%2", "r1") | ||
293 | __asmeq("%3", "r2") | ||
294 | __asmeq("%4", "r3") | ||
295 | #ifdef REQUIRES_SEC | ||
296 | ".arch_extension sec\n" | ||
297 | #endif | ||
298 | "smc #0 @ switch to secure world\n" | ||
299 | : "=r" (r0) | ||
300 | : "r" (r0), "r" (r1), "r" (r2), "r" (r3) | ||
301 | ); | ||
302 | return r0; | ||
303 | } | ||
304 | |||
345 | u32 qcom_scm_get_version(void) | 305 | u32 qcom_scm_get_version(void) |
346 | { | 306 | { |
347 | int context_id; | 307 | int context_id; |
@@ -378,22 +338,6 @@ u32 qcom_scm_get_version(void) | |||
378 | } | 338 | } |
379 | EXPORT_SYMBOL(qcom_scm_get_version); | 339 | EXPORT_SYMBOL(qcom_scm_get_version); |
380 | 340 | ||
381 | /* | ||
382 | * Set the cold/warm boot address for one of the CPU cores. | ||
383 | */ | ||
384 | static int qcom_scm_set_boot_addr(u32 addr, int flags) | ||
385 | { | ||
386 | struct { | ||
387 | __le32 flags; | ||
388 | __le32 addr; | ||
389 | } cmd; | ||
390 | |||
391 | cmd.addr = cpu_to_le32(addr); | ||
392 | cmd.flags = cpu_to_le32(flags); | ||
393 | return qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, | ||
394 | &cmd, sizeof(cmd), NULL, 0); | ||
395 | } | ||
396 | |||
397 | /** | 341 | /** |
398 | * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus | 342 | * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus |
399 | * @entry: Entry point function for the cpus | 343 | * @entry: Entry point function for the cpus |
@@ -423,7 +367,8 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) | |||
423 | set_cpu_present(cpu, false); | 367 | set_cpu_present(cpu, false); |
424 | } | 368 | } |
425 | 369 | ||
426 | return qcom_scm_set_boot_addr(virt_to_phys(entry), flags); | 370 | return qcom_scm_call_atomic2(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, |
371 | flags, virt_to_phys(entry)); | ||
427 | } | 372 | } |
428 | 373 | ||
429 | /** | 374 | /** |
@@ -434,11 +379,16 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) | |||
434 | * Set the Linux entry point for the SCM to transfer control to when coming | 379 | * Set the Linux entry point for the SCM to transfer control to when coming |
435 | * out of a power down. CPU power down may be executed on cpuidle or hotplug. | 380 | * out of a power down. CPU power down may be executed on cpuidle or hotplug. |
436 | */ | 381 | */ |
437 | int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) | 382 | int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, |
383 | const cpumask_t *cpus) | ||
438 | { | 384 | { |
439 | int ret; | 385 | int ret; |
440 | int flags = 0; | 386 | int flags = 0; |
441 | int cpu; | 387 | int cpu; |
388 | struct { | ||
389 | __le32 flags; | ||
390 | __le32 addr; | ||
391 | } cmd; | ||
442 | 392 | ||
443 | /* | 393 | /* |
444 | * Reassign only if we are switching from hotplug entry point | 394 | * Reassign only if we are switching from hotplug entry point |
@@ -454,7 +404,10 @@ int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) | |||
454 | if (!flags) | 404 | if (!flags) |
455 | return 0; | 405 | return 0; |
456 | 406 | ||
457 | ret = qcom_scm_set_boot_addr(virt_to_phys(entry), flags); | 407 | cmd.addr = cpu_to_le32(virt_to_phys(entry)); |
408 | cmd.flags = cpu_to_le32(flags); | ||
409 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, | ||
410 | &cmd, sizeof(cmd), NULL, 0); | ||
458 | if (!ret) { | 411 | if (!ret) { |
459 | for_each_cpu(cpu, cpus) | 412 | for_each_cpu(cpu, cpus) |
460 | qcom_scm_wb[cpu].entry = entry; | 413 | qcom_scm_wb[cpu].entry = entry; |
@@ -477,25 +430,133 @@ void __qcom_scm_cpu_power_down(u32 flags) | |||
477 | flags & QCOM_SCM_FLUSH_FLAG_MASK); | 430 | flags & QCOM_SCM_FLUSH_FLAG_MASK); |
478 | } | 431 | } |
479 | 432 | ||
480 | int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id) | 433 | int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id) |
481 | { | 434 | { |
482 | int ret; | 435 | int ret; |
483 | __le32 svc_cmd = cpu_to_le32((svc_id << 10) | cmd_id); | 436 | __le32 svc_cmd = cpu_to_le32((svc_id << 10) | cmd_id); |
484 | __le32 ret_val = 0; | 437 | __le32 ret_val = 0; |
485 | 438 | ||
486 | ret = qcom_scm_call(QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD, &svc_cmd, | 439 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD, |
487 | sizeof(svc_cmd), &ret_val, sizeof(ret_val)); | 440 | &svc_cmd, sizeof(svc_cmd), &ret_val, |
441 | sizeof(ret_val)); | ||
488 | if (ret) | 442 | if (ret) |
489 | return ret; | 443 | return ret; |
490 | 444 | ||
491 | return le32_to_cpu(ret_val); | 445 | return le32_to_cpu(ret_val); |
492 | } | 446 | } |
493 | 447 | ||
494 | int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) | 448 | int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, |
449 | u32 req_cnt, u32 *resp) | ||
495 | { | 450 | { |
496 | if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) | 451 | if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) |
497 | return -ERANGE; | 452 | return -ERANGE; |
498 | 453 | ||
499 | return qcom_scm_call(QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, | 454 | return qcom_scm_call(dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, |
500 | req, req_cnt * sizeof(*req), resp, sizeof(*resp)); | 455 | req, req_cnt * sizeof(*req), resp, sizeof(*resp)); |
501 | } | 456 | } |
457 | |||
458 | void __qcom_scm_init(void) | ||
459 | { | ||
460 | } | ||
461 | |||
462 | bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral) | ||
463 | { | ||
464 | __le32 out; | ||
465 | __le32 in; | ||
466 | int ret; | ||
467 | |||
468 | in = cpu_to_le32(peripheral); | ||
469 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, | ||
470 | QCOM_SCM_PAS_IS_SUPPORTED_CMD, | ||
471 | &in, sizeof(in), | ||
472 | &out, sizeof(out)); | ||
473 | |||
474 | return ret ? false : !!out; | ||
475 | } | ||
476 | |||
477 | int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, | ||
478 | dma_addr_t metadata_phys) | ||
479 | { | ||
480 | __le32 scm_ret; | ||
481 | int ret; | ||
482 | struct { | ||
483 | __le32 proc; | ||
484 | __le32 image_addr; | ||
485 | } request; | ||
486 | |||
487 | request.proc = cpu_to_le32(peripheral); | ||
488 | request.image_addr = cpu_to_le32(metadata_phys); | ||
489 | |||
490 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, | ||
491 | QCOM_SCM_PAS_INIT_IMAGE_CMD, | ||
492 | &request, sizeof(request), | ||
493 | &scm_ret, sizeof(scm_ret)); | ||
494 | |||
495 | return ret ? : le32_to_cpu(scm_ret); | ||
496 | } | ||
497 | |||
498 | int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, | ||
499 | phys_addr_t addr, phys_addr_t size) | ||
500 | { | ||
501 | __le32 scm_ret; | ||
502 | int ret; | ||
503 | struct { | ||
504 | __le32 proc; | ||
505 | __le32 addr; | ||
506 | __le32 len; | ||
507 | } request; | ||
508 | |||
509 | request.proc = cpu_to_le32(peripheral); | ||
510 | request.addr = cpu_to_le32(addr); | ||
511 | request.len = cpu_to_le32(size); | ||
512 | |||
513 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, | ||
514 | QCOM_SCM_PAS_MEM_SETUP_CMD, | ||
515 | &request, sizeof(request), | ||
516 | &scm_ret, sizeof(scm_ret)); | ||
517 | |||
518 | return ret ? : le32_to_cpu(scm_ret); | ||
519 | } | ||
520 | |||
521 | int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral) | ||
522 | { | ||
523 | __le32 out; | ||
524 | __le32 in; | ||
525 | int ret; | ||
526 | |||
527 | in = cpu_to_le32(peripheral); | ||
528 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, | ||
529 | QCOM_SCM_PAS_AUTH_AND_RESET_CMD, | ||
530 | &in, sizeof(in), | ||
531 | &out, sizeof(out)); | ||
532 | |||
533 | return ret ? : le32_to_cpu(out); | ||
534 | } | ||
535 | |||
536 | int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral) | ||
537 | { | ||
538 | __le32 out; | ||
539 | __le32 in; | ||
540 | int ret; | ||
541 | |||
542 | in = cpu_to_le32(peripheral); | ||
543 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, | ||
544 | QCOM_SCM_PAS_SHUTDOWN_CMD, | ||
545 | &in, sizeof(in), | ||
546 | &out, sizeof(out)); | ||
547 | |||
548 | return ret ? : le32_to_cpu(out); | ||
549 | } | ||
550 | |||
551 | int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) | ||
552 | { | ||
553 | __le32 out; | ||
554 | __le32 in = cpu_to_le32(reset); | ||
555 | int ret; | ||
556 | |||
557 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET, | ||
558 | &in, sizeof(in), | ||
559 | &out, sizeof(out)); | ||
560 | |||
561 | return ret ? : le32_to_cpu(out); | ||
562 | } | ||
diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index bb6555f6d63b..4a0f5ead4fb5 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c | |||
@@ -12,7 +12,150 @@ | |||
12 | 12 | ||
13 | #include <linux/io.h> | 13 | #include <linux/io.h> |
14 | #include <linux/errno.h> | 14 | #include <linux/errno.h> |
15 | #include <linux/delay.h> | ||
16 | #include <linux/mutex.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/types.h> | ||
15 | #include <linux/qcom_scm.h> | 19 | #include <linux/qcom_scm.h> |
20 | #include <linux/arm-smccc.h> | ||
21 | #include <linux/dma-mapping.h> | ||
22 | |||
23 | #include "qcom_scm.h" | ||
24 | |||
25 | #define QCOM_SCM_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) | ||
26 | |||
27 | #define MAX_QCOM_SCM_ARGS 10 | ||
28 | #define MAX_QCOM_SCM_RETS 3 | ||
29 | |||
30 | enum qcom_scm_arg_types { | ||
31 | QCOM_SCM_VAL, | ||
32 | QCOM_SCM_RO, | ||
33 | QCOM_SCM_RW, | ||
34 | QCOM_SCM_BUFVAL, | ||
35 | }; | ||
36 | |||
37 | #define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\ | ||
38 | (((a) & 0x3) << 4) | \ | ||
39 | (((b) & 0x3) << 6) | \ | ||
40 | (((c) & 0x3) << 8) | \ | ||
41 | (((d) & 0x3) << 10) | \ | ||
42 | (((e) & 0x3) << 12) | \ | ||
43 | (((f) & 0x3) << 14) | \ | ||
44 | (((g) & 0x3) << 16) | \ | ||
45 | (((h) & 0x3) << 18) | \ | ||
46 | (((i) & 0x3) << 20) | \ | ||
47 | (((j) & 0x3) << 22) | \ | ||
48 | ((num) & 0xf)) | ||
49 | |||
50 | #define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | ||
51 | |||
52 | /** | ||
53 | * struct qcom_scm_desc | ||
54 | * @arginfo: Metadata describing the arguments in args[] | ||
55 | * @args: The array of arguments for the secure syscall | ||
56 | * @res: The values returned by the secure syscall | ||
57 | */ | ||
58 | struct qcom_scm_desc { | ||
59 | u32 arginfo; | ||
60 | u64 args[MAX_QCOM_SCM_ARGS]; | ||
61 | }; | ||
62 | |||
63 | static u64 qcom_smccc_convention = -1; | ||
64 | static DEFINE_MUTEX(qcom_scm_lock); | ||
65 | |||
66 | #define QCOM_SCM_EBUSY_WAIT_MS 30 | ||
67 | #define QCOM_SCM_EBUSY_MAX_RETRY 20 | ||
68 | |||
69 | #define N_EXT_QCOM_SCM_ARGS 7 | ||
70 | #define FIRST_EXT_ARG_IDX 3 | ||
71 | #define N_REGISTER_ARGS (MAX_QCOM_SCM_ARGS - N_EXT_QCOM_SCM_ARGS + 1) | ||
72 | |||
73 | /** | ||
74 | * qcom_scm_call() - Invoke a syscall in the secure world | ||
75 | * @dev: device | ||
76 | * @svc_id: service identifier | ||
77 | * @cmd_id: command identifier | ||
78 | * @desc: Descriptor structure containing arguments and return values | ||
79 | * | ||
80 | * Sends a command to the SCM and waits for the command to finish processing. | ||
81 | * This should *only* be called in pre-emptible context. | ||
82 | */ | ||
83 | static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id, | ||
84 | const struct qcom_scm_desc *desc, | ||
85 | struct arm_smccc_res *res) | ||
86 | { | ||
87 | int arglen = desc->arginfo & 0xf; | ||
88 | int retry_count = 0, i; | ||
89 | u32 fn_id = QCOM_SCM_FNID(svc_id, cmd_id); | ||
90 | u64 cmd, x5 = desc->args[FIRST_EXT_ARG_IDX]; | ||
91 | dma_addr_t args_phys = 0; | ||
92 | void *args_virt = NULL; | ||
93 | size_t alloc_len; | ||
94 | |||
95 | if (unlikely(arglen > N_REGISTER_ARGS)) { | ||
96 | alloc_len = N_EXT_QCOM_SCM_ARGS * sizeof(u64); | ||
97 | args_virt = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL); | ||
98 | |||
99 | if (!args_virt) | ||
100 | return -ENOMEM; | ||
101 | |||
102 | if (qcom_smccc_convention == ARM_SMCCC_SMC_32) { | ||
103 | __le32 *args = args_virt; | ||
104 | |||
105 | for (i = 0; i < N_EXT_QCOM_SCM_ARGS; i++) | ||
106 | args[i] = cpu_to_le32(desc->args[i + | ||
107 | FIRST_EXT_ARG_IDX]); | ||
108 | } else { | ||
109 | __le64 *args = args_virt; | ||
110 | |||
111 | for (i = 0; i < N_EXT_QCOM_SCM_ARGS; i++) | ||
112 | args[i] = cpu_to_le64(desc->args[i + | ||
113 | FIRST_EXT_ARG_IDX]); | ||
114 | } | ||
115 | |||
116 | args_phys = dma_map_single(dev, args_virt, alloc_len, | ||
117 | DMA_TO_DEVICE); | ||
118 | |||
119 | if (dma_mapping_error(dev, args_phys)) { | ||
120 | kfree(args_virt); | ||
121 | return -ENOMEM; | ||
122 | } | ||
123 | |||
124 | x5 = args_phys; | ||
125 | } | ||
126 | |||
127 | do { | ||
128 | mutex_lock(&qcom_scm_lock); | ||
129 | |||
130 | cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, | ||
131 | qcom_smccc_convention, | ||
132 | ARM_SMCCC_OWNER_SIP, fn_id); | ||
133 | |||
134 | do { | ||
135 | arm_smccc_smc(cmd, desc->arginfo, desc->args[0], | ||
136 | desc->args[1], desc->args[2], x5, 0, 0, | ||
137 | res); | ||
138 | } while (res->a0 == QCOM_SCM_INTERRUPTED); | ||
139 | |||
140 | mutex_unlock(&qcom_scm_lock); | ||
141 | |||
142 | if (res->a0 == QCOM_SCM_V2_EBUSY) { | ||
143 | if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY) | ||
144 | break; | ||
145 | msleep(QCOM_SCM_EBUSY_WAIT_MS); | ||
146 | } | ||
147 | } while (res->a0 == QCOM_SCM_V2_EBUSY); | ||
148 | |||
149 | if (args_virt) { | ||
150 | dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE); | ||
151 | kfree(args_virt); | ||
152 | } | ||
153 | |||
154 | if (res->a0 < 0) | ||
155 | return qcom_scm_remap_error(res->a0); | ||
156 | |||
157 | return 0; | ||
158 | } | ||
16 | 159 | ||
17 | /** | 160 | /** |
18 | * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus | 161 | * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus |
@@ -29,13 +172,15 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) | |||
29 | 172 | ||
30 | /** | 173 | /** |
31 | * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus | 174 | * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus |
175 | * @dev: Device pointer | ||
32 | * @entry: Entry point function for the cpus | 176 | * @entry: Entry point function for the cpus |
33 | * @cpus: The cpumask of cpus that will use the entry point | 177 | * @cpus: The cpumask of cpus that will use the entry point |
34 | * | 178 | * |
35 | * Set the Linux entry point for the SCM to transfer control to when coming | 179 | * Set the Linux entry point for the SCM to transfer control to when coming |
36 | * out of a power down. CPU power down may be executed on cpuidle or hotplug. | 180 | * out of a power down. CPU power down may be executed on cpuidle or hotplug. |
37 | */ | 181 | */ |
38 | int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) | 182 | int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, |
183 | const cpumask_t *cpus) | ||
39 | { | 184 | { |
40 | return -ENOTSUPP; | 185 | return -ENOTSUPP; |
41 | } | 186 | } |
@@ -52,12 +197,164 @@ void __qcom_scm_cpu_power_down(u32 flags) | |||
52 | { | 197 | { |
53 | } | 198 | } |
54 | 199 | ||
55 | int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id) | 200 | int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id) |
56 | { | 201 | { |
57 | return -ENOTSUPP; | 202 | int ret; |
203 | struct qcom_scm_desc desc = {0}; | ||
204 | struct arm_smccc_res res; | ||
205 | |||
206 | desc.arginfo = QCOM_SCM_ARGS(1); | ||
207 | desc.args[0] = QCOM_SCM_FNID(svc_id, cmd_id) | | ||
208 | (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT); | ||
209 | |||
210 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD, | ||
211 | &desc, &res); | ||
212 | |||
213 | return ret ? : res.a1; | ||
58 | } | 214 | } |
59 | 215 | ||
60 | int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) | 216 | int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, |
217 | u32 req_cnt, u32 *resp) | ||
61 | { | 218 | { |
62 | return -ENOTSUPP; | 219 | int ret; |
220 | struct qcom_scm_desc desc = {0}; | ||
221 | struct arm_smccc_res res; | ||
222 | |||
223 | if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) | ||
224 | return -ERANGE; | ||
225 | |||
226 | desc.args[0] = req[0].addr; | ||
227 | desc.args[1] = req[0].val; | ||
228 | desc.args[2] = req[1].addr; | ||
229 | desc.args[3] = req[1].val; | ||
230 | desc.args[4] = req[2].addr; | ||
231 | desc.args[5] = req[2].val; | ||
232 | desc.args[6] = req[3].addr; | ||
233 | desc.args[7] = req[3].val; | ||
234 | desc.args[8] = req[4].addr; | ||
235 | desc.args[9] = req[4].val; | ||
236 | desc.arginfo = QCOM_SCM_ARGS(10); | ||
237 | |||
238 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, &desc, | ||
239 | &res); | ||
240 | *resp = res.a1; | ||
241 | |||
242 | return ret; | ||
243 | } | ||
244 | |||
245 | void __qcom_scm_init(void) | ||
246 | { | ||
247 | u64 cmd; | ||
248 | struct arm_smccc_res res; | ||
249 | u32 function = QCOM_SCM_FNID(QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD); | ||
250 | |||
251 | /* First try a SMC64 call */ | ||
252 | cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, | ||
253 | ARM_SMCCC_OWNER_SIP, function); | ||
254 | |||
255 | arm_smccc_smc(cmd, QCOM_SCM_ARGS(1), cmd & (~BIT(ARM_SMCCC_TYPE_SHIFT)), | ||
256 | 0, 0, 0, 0, 0, &res); | ||
257 | |||
258 | if (!res.a0 && res.a1) | ||
259 | qcom_smccc_convention = ARM_SMCCC_SMC_64; | ||
260 | else | ||
261 | qcom_smccc_convention = ARM_SMCCC_SMC_32; | ||
262 | } | ||
263 | |||
264 | bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral) | ||
265 | { | ||
266 | int ret; | ||
267 | struct qcom_scm_desc desc = {0}; | ||
268 | struct arm_smccc_res res; | ||
269 | |||
270 | desc.args[0] = peripheral; | ||
271 | desc.arginfo = QCOM_SCM_ARGS(1); | ||
272 | |||
273 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, | ||
274 | QCOM_SCM_PAS_IS_SUPPORTED_CMD, | ||
275 | &desc, &res); | ||
276 | |||
277 | return ret ? false : !!res.a1; | ||
278 | } | ||
279 | |||
280 | int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, | ||
281 | dma_addr_t metadata_phys) | ||
282 | { | ||
283 | int ret; | ||
284 | struct qcom_scm_desc desc = {0}; | ||
285 | struct arm_smccc_res res; | ||
286 | |||
287 | desc.args[0] = peripheral; | ||
288 | desc.args[1] = metadata_phys; | ||
289 | desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW); | ||
290 | |||
291 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_INIT_IMAGE_CMD, | ||
292 | &desc, &res); | ||
293 | |||
294 | return ret ? : res.a1; | ||
295 | } | ||
296 | |||
297 | int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, | ||
298 | phys_addr_t addr, phys_addr_t size) | ||
299 | { | ||
300 | int ret; | ||
301 | struct qcom_scm_desc desc = {0}; | ||
302 | struct arm_smccc_res res; | ||
303 | |||
304 | desc.args[0] = peripheral; | ||
305 | desc.args[1] = addr; | ||
306 | desc.args[2] = size; | ||
307 | desc.arginfo = QCOM_SCM_ARGS(3); | ||
308 | |||
309 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MEM_SETUP_CMD, | ||
310 | &desc, &res); | ||
311 | |||
312 | return ret ? : res.a1; | ||
313 | } | ||
314 | |||
315 | int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral) | ||
316 | { | ||
317 | int ret; | ||
318 | struct qcom_scm_desc desc = {0}; | ||
319 | struct arm_smccc_res res; | ||
320 | |||
321 | desc.args[0] = peripheral; | ||
322 | desc.arginfo = QCOM_SCM_ARGS(1); | ||
323 | |||
324 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, | ||
325 | QCOM_SCM_PAS_AUTH_AND_RESET_CMD, | ||
326 | &desc, &res); | ||
327 | |||
328 | return ret ? : res.a1; | ||
329 | } | ||
330 | |||
331 | int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral) | ||
332 | { | ||
333 | int ret; | ||
334 | struct qcom_scm_desc desc = {0}; | ||
335 | struct arm_smccc_res res; | ||
336 | |||
337 | desc.args[0] = peripheral; | ||
338 | desc.arginfo = QCOM_SCM_ARGS(1); | ||
339 | |||
340 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_SHUTDOWN_CMD, | ||
341 | &desc, &res); | ||
342 | |||
343 | return ret ? : res.a1; | ||
344 | } | ||
345 | |||
346 | int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) | ||
347 | { | ||
348 | struct qcom_scm_desc desc = {0}; | ||
349 | struct arm_smccc_res res; | ||
350 | int ret; | ||
351 | |||
352 | desc.args[0] = reset; | ||
353 | desc.args[1] = 0; | ||
354 | desc.arginfo = QCOM_SCM_ARGS(2); | ||
355 | |||
356 | ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET, &desc, | ||
357 | &res); | ||
358 | |||
359 | return ret ? : res.a1; | ||
63 | } | 360 | } |
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 45c008d68891..e64a501adbf4 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c | |||
@@ -10,19 +10,64 @@ | |||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. | 11 | * GNU General Public License for more details. |
12 | * | 12 | * |
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program; if not, write to the Free Software | ||
15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
16 | * 02110-1301, USA. | ||
17 | */ | 13 | */ |
18 | 14 | #include <linux/platform_device.h> | |
15 | #include <linux/module.h> | ||
19 | #include <linux/cpumask.h> | 16 | #include <linux/cpumask.h> |
20 | #include <linux/export.h> | 17 | #include <linux/export.h> |
18 | #include <linux/dma-mapping.h> | ||
21 | #include <linux/types.h> | 19 | #include <linux/types.h> |
22 | #include <linux/qcom_scm.h> | 20 | #include <linux/qcom_scm.h> |
21 | #include <linux/of.h> | ||
22 | #include <linux/of_platform.h> | ||
23 | #include <linux/clk.h> | ||
24 | #include <linux/reset-controller.h> | ||
23 | 25 | ||
24 | #include "qcom_scm.h" | 26 | #include "qcom_scm.h" |
25 | 27 | ||
28 | struct qcom_scm { | ||
29 | struct device *dev; | ||
30 | struct clk *core_clk; | ||
31 | struct clk *iface_clk; | ||
32 | struct clk *bus_clk; | ||
33 | struct reset_controller_dev reset; | ||
34 | }; | ||
35 | |||
36 | static struct qcom_scm *__scm; | ||
37 | |||
38 | static int qcom_scm_clk_enable(void) | ||
39 | { | ||
40 | int ret; | ||
41 | |||
42 | ret = clk_prepare_enable(__scm->core_clk); | ||
43 | if (ret) | ||
44 | goto bail; | ||
45 | |||
46 | ret = clk_prepare_enable(__scm->iface_clk); | ||
47 | if (ret) | ||
48 | goto disable_core; | ||
49 | |||
50 | ret = clk_prepare_enable(__scm->bus_clk); | ||
51 | if (ret) | ||
52 | goto disable_iface; | ||
53 | |||
54 | return 0; | ||
55 | |||
56 | disable_iface: | ||
57 | clk_disable_unprepare(__scm->iface_clk); | ||
58 | disable_core: | ||
59 | clk_disable_unprepare(__scm->core_clk); | ||
60 | bail: | ||
61 | return ret; | ||
62 | } | ||
63 | |||
64 | static void qcom_scm_clk_disable(void) | ||
65 | { | ||
66 | clk_disable_unprepare(__scm->core_clk); | ||
67 | clk_disable_unprepare(__scm->iface_clk); | ||
68 | clk_disable_unprepare(__scm->bus_clk); | ||
69 | } | ||
70 | |||
26 | /** | 71 | /** |
27 | * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus | 72 | * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus |
28 | * @entry: Entry point function for the cpus | 73 | * @entry: Entry point function for the cpus |
@@ -47,7 +92,7 @@ EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr); | |||
47 | */ | 92 | */ |
48 | int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) | 93 | int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) |
49 | { | 94 | { |
50 | return __qcom_scm_set_warm_boot_addr(entry, cpus); | 95 | return __qcom_scm_set_warm_boot_addr(__scm->dev, entry, cpus); |
51 | } | 96 | } |
52 | EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); | 97 | EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); |
53 | 98 | ||
@@ -72,12 +117,17 @@ EXPORT_SYMBOL(qcom_scm_cpu_power_down); | |||
72 | */ | 117 | */ |
73 | bool qcom_scm_hdcp_available(void) | 118 | bool qcom_scm_hdcp_available(void) |
74 | { | 119 | { |
75 | int ret; | 120 | int ret = qcom_scm_clk_enable(); |
76 | 121 | ||
77 | ret = __qcom_scm_is_call_available(QCOM_SCM_SVC_HDCP, | 122 | if (ret) |
78 | QCOM_SCM_CMD_HDCP); | 123 | return ret; |
79 | 124 | ||
80 | return (ret > 0) ? true : false; | 125 | ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP, |
126 | QCOM_SCM_CMD_HDCP); | ||
127 | |||
128 | qcom_scm_clk_disable(); | ||
129 | |||
130 | return ret > 0 ? true : false; | ||
81 | } | 131 | } |
82 | EXPORT_SYMBOL(qcom_scm_hdcp_available); | 132 | EXPORT_SYMBOL(qcom_scm_hdcp_available); |
83 | 133 | ||
@@ -91,6 +141,287 @@ EXPORT_SYMBOL(qcom_scm_hdcp_available); | |||
91 | */ | 141 | */ |
92 | int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) | 142 | int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) |
93 | { | 143 | { |
94 | return __qcom_scm_hdcp_req(req, req_cnt, resp); | 144 | int ret = qcom_scm_clk_enable(); |
145 | |||
146 | if (ret) | ||
147 | return ret; | ||
148 | |||
149 | ret = __qcom_scm_hdcp_req(__scm->dev, req, req_cnt, resp); | ||
150 | qcom_scm_clk_disable(); | ||
151 | return ret; | ||
95 | } | 152 | } |
96 | EXPORT_SYMBOL(qcom_scm_hdcp_req); | 153 | EXPORT_SYMBOL(qcom_scm_hdcp_req); |
154 | |||
155 | /** | ||
156 | * qcom_scm_pas_supported() - Check if the peripheral authentication service is | ||
157 | * available for the given peripherial | ||
158 | * @peripheral: peripheral id | ||
159 | * | ||
160 | * Returns true if PAS is supported for this peripheral, otherwise false. | ||
161 | */ | ||
162 | bool qcom_scm_pas_supported(u32 peripheral) | ||
163 | { | ||
164 | int ret; | ||
165 | |||
166 | ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, | ||
167 | QCOM_SCM_PAS_IS_SUPPORTED_CMD); | ||
168 | if (ret <= 0) | ||
169 | return false; | ||
170 | |||
171 | return __qcom_scm_pas_supported(__scm->dev, peripheral); | ||
172 | } | ||
173 | EXPORT_SYMBOL(qcom_scm_pas_supported); | ||
174 | |||
175 | /** | ||
176 | * qcom_scm_pas_init_image() - Initialize peripheral authentication service | ||
177 | * state machine for a given peripheral, using the | ||
178 | * metadata | ||
179 | * @peripheral: peripheral id | ||
180 | * @metadata: pointer to memory containing ELF header, program header table | ||
181 | * and optional blob of data used for authenticating the metadata | ||
182 | * and the rest of the firmware | ||
183 | * @size: size of the metadata | ||
184 | * | ||
185 | * Returns 0 on success. | ||
186 | */ | ||
187 | int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size) | ||
188 | { | ||
189 | dma_addr_t mdata_phys; | ||
190 | void *mdata_buf; | ||
191 | int ret; | ||
192 | |||
193 | /* | ||
194 | * During the scm call memory protection will be enabled for the meta | ||
195 | * data blob, so make sure it's physically contiguous, 4K aligned and | ||
196 | * non-cachable to avoid XPU violations. | ||
197 | */ | ||
198 | mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys, | ||
199 | GFP_KERNEL); | ||
200 | if (!mdata_buf) { | ||
201 | dev_err(__scm->dev, "Allocation of metadata buffer failed.\n"); | ||
202 | return -ENOMEM; | ||
203 | } | ||
204 | memcpy(mdata_buf, metadata, size); | ||
205 | |||
206 | ret = qcom_scm_clk_enable(); | ||
207 | if (ret) | ||
208 | goto free_metadata; | ||
209 | |||
210 | ret = __qcom_scm_pas_init_image(__scm->dev, peripheral, mdata_phys); | ||
211 | |||
212 | qcom_scm_clk_disable(); | ||
213 | |||
214 | free_metadata: | ||
215 | dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys); | ||
216 | |||
217 | return ret; | ||
218 | } | ||
219 | EXPORT_SYMBOL(qcom_scm_pas_init_image); | ||
220 | |||
221 | /** | ||
222 | * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral | ||
223 | * for firmware loading | ||
224 | * @peripheral: peripheral id | ||
225 | * @addr: start address of memory area to prepare | ||
226 | * @size: size of the memory area to prepare | ||
227 | * | ||
228 | * Returns 0 on success. | ||
229 | */ | ||
230 | int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size) | ||
231 | { | ||
232 | int ret; | ||
233 | |||
234 | ret = qcom_scm_clk_enable(); | ||
235 | if (ret) | ||
236 | return ret; | ||
237 | |||
238 | ret = __qcom_scm_pas_mem_setup(__scm->dev, peripheral, addr, size); | ||
239 | qcom_scm_clk_disable(); | ||
240 | |||
241 | return ret; | ||
242 | } | ||
243 | EXPORT_SYMBOL(qcom_scm_pas_mem_setup); | ||
244 | |||
245 | /** | ||
246 | * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware | ||
247 | * and reset the remote processor | ||
248 | * @peripheral: peripheral id | ||
249 | * | ||
250 | * Return 0 on success. | ||
251 | */ | ||
252 | int qcom_scm_pas_auth_and_reset(u32 peripheral) | ||
253 | { | ||
254 | int ret; | ||
255 | |||
256 | ret = qcom_scm_clk_enable(); | ||
257 | if (ret) | ||
258 | return ret; | ||
259 | |||
260 | ret = __qcom_scm_pas_auth_and_reset(__scm->dev, peripheral); | ||
261 | qcom_scm_clk_disable(); | ||
262 | |||
263 | return ret; | ||
264 | } | ||
265 | EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset); | ||
266 | |||
267 | /** | ||
268 | * qcom_scm_pas_shutdown() - Shut down the remote processor | ||
269 | * @peripheral: peripheral id | ||
270 | * | ||
271 | * Returns 0 on success. | ||
272 | */ | ||
273 | int qcom_scm_pas_shutdown(u32 peripheral) | ||
274 | { | ||
275 | int ret; | ||
276 | |||
277 | ret = qcom_scm_clk_enable(); | ||
278 | if (ret) | ||
279 | return ret; | ||
280 | |||
281 | ret = __qcom_scm_pas_shutdown(__scm->dev, peripheral); | ||
282 | qcom_scm_clk_disable(); | ||
283 | |||
284 | return ret; | ||
285 | } | ||
286 | EXPORT_SYMBOL(qcom_scm_pas_shutdown); | ||
287 | |||
288 | static int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev, | ||
289 | unsigned long idx) | ||
290 | { | ||
291 | if (idx != 0) | ||
292 | return -EINVAL; | ||
293 | |||
294 | return __qcom_scm_pas_mss_reset(__scm->dev, 1); | ||
295 | } | ||
296 | |||
297 | static int qcom_scm_pas_reset_deassert(struct reset_controller_dev *rcdev, | ||
298 | unsigned long idx) | ||
299 | { | ||
300 | if (idx != 0) | ||
301 | return -EINVAL; | ||
302 | |||
303 | return __qcom_scm_pas_mss_reset(__scm->dev, 0); | ||
304 | } | ||
305 | |||
306 | static const struct reset_control_ops qcom_scm_pas_reset_ops = { | ||
307 | .assert = qcom_scm_pas_reset_assert, | ||
308 | .deassert = qcom_scm_pas_reset_deassert, | ||
309 | }; | ||
310 | |||
311 | /** | ||
312 | * qcom_scm_is_available() - Checks if SCM is available | ||
313 | */ | ||
314 | bool qcom_scm_is_available(void) | ||
315 | { | ||
316 | return !!__scm; | ||
317 | } | ||
318 | EXPORT_SYMBOL(qcom_scm_is_available); | ||
319 | |||
320 | static int qcom_scm_probe(struct platform_device *pdev) | ||
321 | { | ||
322 | struct qcom_scm *scm; | ||
323 | int ret; | ||
324 | |||
325 | scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL); | ||
326 | if (!scm) | ||
327 | return -ENOMEM; | ||
328 | |||
329 | scm->core_clk = devm_clk_get(&pdev->dev, "core"); | ||
330 | if (IS_ERR(scm->core_clk)) { | ||
331 | if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER) | ||
332 | return PTR_ERR(scm->core_clk); | ||
333 | |||
334 | scm->core_clk = NULL; | ||
335 | } | ||
336 | |||
337 | if (of_device_is_compatible(pdev->dev.of_node, "qcom,scm")) { | ||
338 | scm->iface_clk = devm_clk_get(&pdev->dev, "iface"); | ||
339 | if (IS_ERR(scm->iface_clk)) { | ||
340 | if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER) | ||
341 | dev_err(&pdev->dev, "failed to acquire iface clk\n"); | ||
342 | return PTR_ERR(scm->iface_clk); | ||
343 | } | ||
344 | |||
345 | scm->bus_clk = devm_clk_get(&pdev->dev, "bus"); | ||
346 | if (IS_ERR(scm->bus_clk)) { | ||
347 | if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER) | ||
348 | dev_err(&pdev->dev, "failed to acquire bus clk\n"); | ||
349 | return PTR_ERR(scm->bus_clk); | ||
350 | } | ||
351 | } | ||
352 | |||
353 | scm->reset.ops = &qcom_scm_pas_reset_ops; | ||
354 | scm->reset.nr_resets = 1; | ||
355 | scm->reset.of_node = pdev->dev.of_node; | ||
356 | reset_controller_register(&scm->reset); | ||
357 | |||
358 | /* vote for max clk rate for highest performance */ | ||
359 | ret = clk_set_rate(scm->core_clk, INT_MAX); | ||
360 | if (ret) | ||
361 | return ret; | ||
362 | |||
363 | __scm = scm; | ||
364 | __scm->dev = &pdev->dev; | ||
365 | |||
366 | __qcom_scm_init(); | ||
367 | |||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | static const struct of_device_id qcom_scm_dt_match[] = { | ||
372 | { .compatible = "qcom,scm-apq8064",}, | ||
373 | { .compatible = "qcom,scm-msm8660",}, | ||
374 | { .compatible = "qcom,scm-msm8960",}, | ||
375 | { .compatible = "qcom,scm",}, | ||
376 | {} | ||
377 | }; | ||
378 | |||
379 | MODULE_DEVICE_TABLE(of, qcom_scm_dt_match); | ||
380 | |||
381 | static struct platform_driver qcom_scm_driver = { | ||
382 | .driver = { | ||
383 | .name = "qcom_scm", | ||
384 | .of_match_table = qcom_scm_dt_match, | ||
385 | }, | ||
386 | .probe = qcom_scm_probe, | ||
387 | }; | ||
388 | |||
389 | static int __init qcom_scm_init(void) | ||
390 | { | ||
391 | struct device_node *np, *fw_np; | ||
392 | int ret; | ||
393 | |||
394 | fw_np = of_find_node_by_name(NULL, "firmware"); | ||
395 | |||
396 | if (!fw_np) | ||
397 | return -ENODEV; | ||
398 | |||
399 | np = of_find_matching_node(fw_np, qcom_scm_dt_match); | ||
400 | |||
401 | if (!np) { | ||
402 | of_node_put(fw_np); | ||
403 | return -ENODEV; | ||
404 | } | ||
405 | |||
406 | of_node_put(np); | ||
407 | |||
408 | ret = of_platform_populate(fw_np, qcom_scm_dt_match, NULL, NULL); | ||
409 | |||
410 | of_node_put(fw_np); | ||
411 | |||
412 | if (ret) | ||
413 | return ret; | ||
414 | |||
415 | return platform_driver_register(&qcom_scm_driver); | ||
416 | } | ||
417 | |||
418 | subsys_initcall(qcom_scm_init); | ||
419 | |||
420 | static void __exit qcom_scm_exit(void) | ||
421 | { | ||
422 | platform_driver_unregister(&qcom_scm_driver); | ||
423 | } | ||
424 | module_exit(qcom_scm_exit); | ||
425 | |||
426 | MODULE_DESCRIPTION("Qualcomm SCM driver"); | ||
427 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 2cce75c08b99..3584b00fe7e6 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h | |||
@@ -19,7 +19,8 @@ | |||
19 | #define QCOM_SCM_FLAG_HLOS 0x01 | 19 | #define QCOM_SCM_FLAG_HLOS 0x01 |
20 | #define QCOM_SCM_FLAG_COLDBOOT_MC 0x02 | 20 | #define QCOM_SCM_FLAG_COLDBOOT_MC 0x02 |
21 | #define QCOM_SCM_FLAG_WARMBOOT_MC 0x04 | 21 | #define QCOM_SCM_FLAG_WARMBOOT_MC 0x04 |
22 | extern int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus); | 22 | extern int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, |
23 | const cpumask_t *cpus); | ||
23 | extern int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); | 24 | extern int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); |
24 | 25 | ||
25 | #define QCOM_SCM_CMD_TERMINATE_PC 0x2 | 26 | #define QCOM_SCM_CMD_TERMINATE_PC 0x2 |
@@ -29,14 +30,34 @@ extern void __qcom_scm_cpu_power_down(u32 flags); | |||
29 | 30 | ||
30 | #define QCOM_SCM_SVC_INFO 0x6 | 31 | #define QCOM_SCM_SVC_INFO 0x6 |
31 | #define QCOM_IS_CALL_AVAIL_CMD 0x1 | 32 | #define QCOM_IS_CALL_AVAIL_CMD 0x1 |
32 | extern int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id); | 33 | extern int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, |
34 | u32 cmd_id); | ||
33 | 35 | ||
34 | #define QCOM_SCM_SVC_HDCP 0x11 | 36 | #define QCOM_SCM_SVC_HDCP 0x11 |
35 | #define QCOM_SCM_CMD_HDCP 0x01 | 37 | #define QCOM_SCM_CMD_HDCP 0x01 |
36 | extern int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, | 38 | extern int __qcom_scm_hdcp_req(struct device *dev, |
37 | u32 *resp); | 39 | struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp); |
40 | |||
41 | extern void __qcom_scm_init(void); | ||
42 | |||
43 | #define QCOM_SCM_SVC_PIL 0x2 | ||
44 | #define QCOM_SCM_PAS_INIT_IMAGE_CMD 0x1 | ||
45 | #define QCOM_SCM_PAS_MEM_SETUP_CMD 0x2 | ||
46 | #define QCOM_SCM_PAS_AUTH_AND_RESET_CMD 0x5 | ||
47 | #define QCOM_SCM_PAS_SHUTDOWN_CMD 0x6 | ||
48 | #define QCOM_SCM_PAS_IS_SUPPORTED_CMD 0x7 | ||
49 | #define QCOM_SCM_PAS_MSS_RESET 0xa | ||
50 | extern bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral); | ||
51 | extern int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, | ||
52 | dma_addr_t metadata_phys); | ||
53 | extern int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, | ||
54 | phys_addr_t addr, phys_addr_t size); | ||
55 | extern int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral); | ||
56 | extern int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral); | ||
57 | extern int __qcom_scm_pas_mss_reset(struct device *dev, bool reset); | ||
38 | 58 | ||
39 | /* common error codes */ | 59 | /* common error codes */ |
60 | #define QCOM_SCM_V2_EBUSY -12 | ||
40 | #define QCOM_SCM_ENOMEM -5 | 61 | #define QCOM_SCM_ENOMEM -5 |
41 | #define QCOM_SCM_EOPNOTSUPP -4 | 62 | #define QCOM_SCM_EOPNOTSUPP -4 |
42 | #define QCOM_SCM_EINVAL_ADDR -3 | 63 | #define QCOM_SCM_EINVAL_ADDR -3 |
@@ -44,4 +65,22 @@ extern int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, | |||
44 | #define QCOM_SCM_ERROR -1 | 65 | #define QCOM_SCM_ERROR -1 |
45 | #define QCOM_SCM_INTERRUPTED 1 | 66 | #define QCOM_SCM_INTERRUPTED 1 |
46 | 67 | ||
68 | static inline int qcom_scm_remap_error(int err) | ||
69 | { | ||
70 | switch (err) { | ||
71 | case QCOM_SCM_ERROR: | ||
72 | return -EIO; | ||
73 | case QCOM_SCM_EINVAL_ADDR: | ||
74 | case QCOM_SCM_EINVAL_ARG: | ||
75 | return -EINVAL; | ||
76 | case QCOM_SCM_EOPNOTSUPP: | ||
77 | return -EOPNOTSUPP; | ||
78 | case QCOM_SCM_ENOMEM: | ||
79 | return -ENOMEM; | ||
80 | case QCOM_SCM_V2_EBUSY: | ||
81 | return -EBUSY; | ||
82 | } | ||
83 | return -EINVAL; | ||
84 | } | ||
85 | |||
47 | #endif | 86 | #endif |
diff --git a/drivers/firmware/scpi_pm_domain.c b/drivers/firmware/scpi_pm_domain.c new file mode 100644 index 000000000000..f395dec27113 --- /dev/null +++ b/drivers/firmware/scpi_pm_domain.c | |||
@@ -0,0 +1,163 @@ | |||
1 | /* | ||
2 | * SCPI Generic power domain support. | ||
3 | * | ||
4 | * Copyright (C) 2016 ARM Ltd. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along | ||
16 | * with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #include <linux/err.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/of_platform.h> | ||
23 | #include <linux/pm_domain.h> | ||
24 | #include <linux/scpi_protocol.h> | ||
25 | |||
26 | struct scpi_pm_domain { | ||
27 | struct generic_pm_domain genpd; | ||
28 | struct scpi_ops *ops; | ||
29 | u32 domain; | ||
30 | char name[30]; | ||
31 | }; | ||
32 | |||
33 | /* | ||
34 | * These device power state values are not well-defined in the specification. | ||
35 | * In case, different implementations use different values, we can make these | ||
36 | * specific to compatibles rather than getting these values from device tree. | ||
37 | */ | ||
38 | enum scpi_power_domain_state { | ||
39 | SCPI_PD_STATE_ON = 0, | ||
40 | SCPI_PD_STATE_OFF = 3, | ||
41 | }; | ||
42 | |||
43 | #define to_scpi_pd(gpd) container_of(gpd, struct scpi_pm_domain, genpd) | ||
44 | |||
45 | static int scpi_pd_power(struct scpi_pm_domain *pd, bool power_on) | ||
46 | { | ||
47 | int ret; | ||
48 | enum scpi_power_domain_state state; | ||
49 | |||
50 | if (power_on) | ||
51 | state = SCPI_PD_STATE_ON; | ||
52 | else | ||
53 | state = SCPI_PD_STATE_OFF; | ||
54 | |||
55 | ret = pd->ops->device_set_power_state(pd->domain, state); | ||
56 | if (ret) | ||
57 | return ret; | ||
58 | |||
59 | return !(state == pd->ops->device_get_power_state(pd->domain)); | ||
60 | } | ||
61 | |||
62 | static int scpi_pd_power_on(struct generic_pm_domain *domain) | ||
63 | { | ||
64 | struct scpi_pm_domain *pd = to_scpi_pd(domain); | ||
65 | |||
66 | return scpi_pd_power(pd, true); | ||
67 | } | ||
68 | |||
69 | static int scpi_pd_power_off(struct generic_pm_domain *domain) | ||
70 | { | ||
71 | struct scpi_pm_domain *pd = to_scpi_pd(domain); | ||
72 | |||
73 | return scpi_pd_power(pd, false); | ||
74 | } | ||
75 | |||
76 | static int scpi_pm_domain_probe(struct platform_device *pdev) | ||
77 | { | ||
78 | struct device *dev = &pdev->dev; | ||
79 | struct device_node *np = dev->of_node; | ||
80 | struct scpi_pm_domain *scpi_pd; | ||
81 | struct genpd_onecell_data *scpi_pd_data; | ||
82 | struct generic_pm_domain **domains; | ||
83 | struct scpi_ops *scpi_ops; | ||
84 | int ret, num_domains, i; | ||
85 | |||
86 | scpi_ops = get_scpi_ops(); | ||
87 | if (!scpi_ops) | ||
88 | return -EPROBE_DEFER; | ||
89 | |||
90 | if (!np) { | ||
91 | dev_err(dev, "device tree node not found\n"); | ||
92 | return -ENODEV; | ||
93 | } | ||
94 | |||
95 | if (!scpi_ops->device_set_power_state || | ||
96 | !scpi_ops->device_get_power_state) { | ||
97 | dev_err(dev, "power domains not supported in the firmware\n"); | ||
98 | return -ENODEV; | ||
99 | } | ||
100 | |||
101 | ret = of_property_read_u32(np, "num-domains", &num_domains); | ||
102 | if (ret) { | ||
103 | dev_err(dev, "number of domains not found\n"); | ||
104 | return -EINVAL; | ||
105 | } | ||
106 | |||
107 | scpi_pd = devm_kcalloc(dev, num_domains, sizeof(*scpi_pd), GFP_KERNEL); | ||
108 | if (!scpi_pd) | ||
109 | return -ENOMEM; | ||
110 | |||
111 | scpi_pd_data = devm_kzalloc(dev, sizeof(*scpi_pd_data), GFP_KERNEL); | ||
112 | if (!scpi_pd_data) | ||
113 | return -ENOMEM; | ||
114 | |||
115 | domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL); | ||
116 | if (!domains) | ||
117 | return -ENOMEM; | ||
118 | |||
119 | for (i = 0; i < num_domains; i++, scpi_pd++) { | ||
120 | domains[i] = &scpi_pd->genpd; | ||
121 | |||
122 | scpi_pd->domain = i; | ||
123 | scpi_pd->ops = scpi_ops; | ||
124 | sprintf(scpi_pd->name, "%s.%d", np->name, i); | ||
125 | scpi_pd->genpd.name = scpi_pd->name; | ||
126 | scpi_pd->genpd.power_off = scpi_pd_power_off; | ||
127 | scpi_pd->genpd.power_on = scpi_pd_power_on; | ||
128 | |||
129 | /* | ||
130 | * Treat all power domains as off at boot. | ||
131 | * | ||
132 | * The SCP firmware itself may have switched on some domains, | ||
133 | * but for reference counting purpose, keep it this way. | ||
134 | */ | ||
135 | pm_genpd_init(&scpi_pd->genpd, NULL, true); | ||
136 | } | ||
137 | |||
138 | scpi_pd_data->domains = domains; | ||
139 | scpi_pd_data->num_domains = num_domains; | ||
140 | |||
141 | of_genpd_add_provider_onecell(np, scpi_pd_data); | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static const struct of_device_id scpi_power_domain_ids[] = { | ||
147 | { .compatible = "arm,scpi-power-domains", }, | ||
148 | { /* sentinel */ } | ||
149 | }; | ||
150 | MODULE_DEVICE_TABLE(of, scpi_power_domain_ids); | ||
151 | |||
152 | static struct platform_driver scpi_power_domain_driver = { | ||
153 | .driver = { | ||
154 | .name = "scpi_power_domain", | ||
155 | .of_match_table = scpi_power_domain_ids, | ||
156 | }, | ||
157 | .probe = scpi_pm_domain_probe, | ||
158 | }; | ||
159 | module_platform_driver(scpi_power_domain_driver); | ||
160 | |||
161 | MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>"); | ||
162 | MODULE_DESCRIPTION("ARM SCPI power domain driver"); | ||
163 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c index b637f1af842e..997e3e97f573 100644 --- a/drivers/input/keyboard/clps711x-keypad.c +++ b/drivers/input/keyboard/clps711x-keypad.c | |||
@@ -101,7 +101,7 @@ static int clps711x_keypad_probe(struct platform_device *pdev) | |||
101 | return -ENOMEM; | 101 | return -ENOMEM; |
102 | 102 | ||
103 | priv->syscon = | 103 | priv->syscon = |
104 | syscon_regmap_lookup_by_compatible("cirrus,clps711x-syscon1"); | 104 | syscon_regmap_lookup_by_compatible("cirrus,ep7209-syscon1"); |
105 | if (IS_ERR(priv->syscon)) | 105 | if (IS_ERR(priv->syscon)) |
106 | return PTR_ERR(priv->syscon); | 106 | return PTR_ERR(priv->syscon); |
107 | 107 | ||
@@ -181,7 +181,7 @@ static int clps711x_keypad_remove(struct platform_device *pdev) | |||
181 | } | 181 | } |
182 | 182 | ||
183 | static const struct of_device_id clps711x_keypad_of_match[] = { | 183 | static const struct of_device_id clps711x_keypad_of_match[] = { |
184 | { .compatible = "cirrus,clps711x-keypad", }, | 184 | { .compatible = "cirrus,ep7209-keypad", }, |
185 | { } | 185 | { } |
186 | }; | 186 | }; |
187 | MODULE_DEVICE_TABLE(of, clps711x_keypad_of_match); | 187 | MODULE_DEVICE_TABLE(of, clps711x_keypad_of_match); |
diff --git a/drivers/irqchip/irq-clps711x.c b/drivers/irqchip/irq-clps711x.c index 2223b3f15d68..f913f4db7ae1 100644 --- a/drivers/irqchip/irq-clps711x.c +++ b/drivers/irqchip/irq-clps711x.c | |||
@@ -234,5 +234,5 @@ static int __init clps711x_intc_init_dt(struct device_node *np, | |||
234 | 234 | ||
235 | return _clps711x_intc_init(np, res.start, resource_size(&res)); | 235 | return _clps711x_intc_init(np, res.start, resource_size(&res)); |
236 | } | 236 | } |
237 | IRQCHIP_DECLARE(clps711x, "cirrus,clps711x-intc", clps711x_intc_init_dt); | 237 | IRQCHIP_DECLARE(clps711x, "cirrus,ep7209-intc", clps711x_intc_init_dt); |
238 | #endif | 238 | #endif |
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index bd4d68500085..370e16e07867 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig | |||
@@ -336,7 +336,7 @@ config IR_TTUSBIR | |||
336 | 336 | ||
337 | config IR_RX51 | 337 | config IR_RX51 |
338 | tristate "Nokia N900 IR transmitter diode" | 338 | tristate "Nokia N900 IR transmitter diode" |
339 | depends on OMAP_DM_TIMER && ARCH_OMAP2PLUS && LIRC && !ARCH_MULTIPLATFORM | 339 | depends on OMAP_DM_TIMER && PWM_OMAP_DMTIMER && ARCH_OMAP2PLUS && LIRC |
340 | ---help--- | 340 | ---help--- |
341 | Say Y or M here if you want to enable support for the IR | 341 | Say Y or M here if you want to enable support for the IR |
342 | transmitter diode built in the Nokia N900 (RX51) device. | 342 | transmitter diode built in the Nokia N900 (RX51) device. |
diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c index 4e1711a40466..82fb6f2ca011 100644 --- a/drivers/media/rc/ir-rx51.c +++ b/drivers/media/rc/ir-rx51.c | |||
@@ -12,22 +12,17 @@ | |||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | 15 | */ |
21 | 16 | #include <linux/clk.h> | |
22 | #include <linux/module.h> | 17 | #include <linux/module.h> |
23 | #include <linux/interrupt.h> | 18 | #include <linux/interrupt.h> |
24 | #include <linux/uaccess.h> | 19 | #include <linux/uaccess.h> |
25 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
26 | #include <linux/sched.h> | 21 | #include <linux/sched.h> |
27 | #include <linux/wait.h> | 22 | #include <linux/wait.h> |
28 | 23 | #include <linux/pwm.h> | |
29 | #include <plat/dmtimer.h> | 24 | #include <linux/of.h> |
30 | #include <plat/clock.h> | 25 | #include <linux/hrtimer.h> |
31 | 26 | ||
32 | #include <media/lirc.h> | 27 | #include <media/lirc.h> |
33 | #include <media/lirc_dev.h> | 28 | #include <media/lirc_dev.h> |
@@ -41,100 +36,51 @@ | |||
41 | 36 | ||
42 | #define WBUF_LEN 256 | 37 | #define WBUF_LEN 256 |
43 | 38 | ||
44 | #define TIMER_MAX_VALUE 0xffffffff | ||
45 | |||
46 | struct lirc_rx51 { | 39 | struct lirc_rx51 { |
47 | struct omap_dm_timer *pwm_timer; | 40 | struct pwm_device *pwm; |
48 | struct omap_dm_timer *pulse_timer; | 41 | struct hrtimer timer; |
49 | struct device *dev; | 42 | struct device *dev; |
50 | struct lirc_rx51_platform_data *pdata; | 43 | struct lirc_rx51_platform_data *pdata; |
51 | wait_queue_head_t wqueue; | 44 | wait_queue_head_t wqueue; |
52 | 45 | ||
53 | unsigned long fclk_khz; | ||
54 | unsigned int freq; /* carrier frequency */ | 46 | unsigned int freq; /* carrier frequency */ |
55 | unsigned int duty_cycle; /* carrier duty cycle */ | 47 | unsigned int duty_cycle; /* carrier duty cycle */ |
56 | unsigned int irq_num; | ||
57 | unsigned int match; | ||
58 | int wbuf[WBUF_LEN]; | 48 | int wbuf[WBUF_LEN]; |
59 | int wbuf_index; | 49 | int wbuf_index; |
60 | unsigned long device_is_open; | 50 | unsigned long device_is_open; |
61 | int pwm_timer_num; | ||
62 | }; | 51 | }; |
63 | 52 | ||
64 | static void lirc_rx51_on(struct lirc_rx51 *lirc_rx51) | 53 | static inline void lirc_rx51_on(struct lirc_rx51 *lirc_rx51) |
65 | { | 54 | { |
66 | omap_dm_timer_set_pwm(lirc_rx51->pwm_timer, 0, 1, | 55 | pwm_enable(lirc_rx51->pwm); |
67 | OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE); | ||
68 | } | 56 | } |
69 | 57 | ||
70 | static void lirc_rx51_off(struct lirc_rx51 *lirc_rx51) | 58 | static inline void lirc_rx51_off(struct lirc_rx51 *lirc_rx51) |
71 | { | 59 | { |
72 | omap_dm_timer_set_pwm(lirc_rx51->pwm_timer, 0, 1, | 60 | pwm_disable(lirc_rx51->pwm); |
73 | OMAP_TIMER_TRIGGER_NONE); | ||
74 | } | 61 | } |
75 | 62 | ||
76 | static int init_timing_params(struct lirc_rx51 *lirc_rx51) | 63 | static int init_timing_params(struct lirc_rx51 *lirc_rx51) |
77 | { | 64 | { |
78 | u32 load, match; | 65 | struct pwm_device *pwm = lirc_rx51->pwm; |
66 | int duty, period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, lirc_rx51->freq); | ||
79 | 67 | ||
80 | load = -(lirc_rx51->fclk_khz * 1000 / lirc_rx51->freq); | 68 | duty = DIV_ROUND_CLOSEST(lirc_rx51->duty_cycle * period, 100); |
81 | match = -(lirc_rx51->duty_cycle * -load / 100); | ||
82 | omap_dm_timer_set_load(lirc_rx51->pwm_timer, 1, load); | ||
83 | omap_dm_timer_set_match(lirc_rx51->pwm_timer, 1, match); | ||
84 | omap_dm_timer_write_counter(lirc_rx51->pwm_timer, TIMER_MAX_VALUE - 2); | ||
85 | omap_dm_timer_start(lirc_rx51->pwm_timer); | ||
86 | omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0); | ||
87 | omap_dm_timer_start(lirc_rx51->pulse_timer); | ||
88 | 69 | ||
89 | lirc_rx51->match = 0; | 70 | pwm_config(pwm, duty, period); |
90 | 71 | ||
91 | return 0; | 72 | return 0; |
92 | } | 73 | } |
93 | 74 | ||
94 | #define tics_after(a, b) ((long)(b) - (long)(a) < 0) | 75 | static enum hrtimer_restart lirc_rx51_timer_cb(struct hrtimer *timer) |
95 | |||
96 | static int pulse_timer_set_timeout(struct lirc_rx51 *lirc_rx51, int usec) | ||
97 | { | 76 | { |
98 | int counter; | 77 | struct lirc_rx51 *lirc_rx51 = |
99 | 78 | container_of(timer, struct lirc_rx51, timer); | |
100 | BUG_ON(usec < 0); | 79 | ktime_t now; |
101 | |||
102 | if (lirc_rx51->match == 0) | ||
103 | counter = omap_dm_timer_read_counter(lirc_rx51->pulse_timer); | ||
104 | else | ||
105 | counter = lirc_rx51->match; | ||
106 | |||
107 | counter += (u32)(lirc_rx51->fclk_khz * usec / (1000)); | ||
108 | omap_dm_timer_set_match(lirc_rx51->pulse_timer, 1, counter); | ||
109 | omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, | ||
110 | OMAP_TIMER_INT_MATCH); | ||
111 | if (tics_after(omap_dm_timer_read_counter(lirc_rx51->pulse_timer), | ||
112 | counter)) { | ||
113 | return 1; | ||
114 | } | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static irqreturn_t lirc_rx51_interrupt_handler(int irq, void *ptr) | ||
119 | { | ||
120 | unsigned int retval; | ||
121 | struct lirc_rx51 *lirc_rx51 = ptr; | ||
122 | |||
123 | retval = omap_dm_timer_read_status(lirc_rx51->pulse_timer); | ||
124 | if (!retval) | ||
125 | return IRQ_NONE; | ||
126 | 80 | ||
127 | if (retval & ~OMAP_TIMER_INT_MATCH) | ||
128 | dev_err_ratelimited(lirc_rx51->dev, | ||
129 | ": Unexpected interrupt source: %x\n", retval); | ||
130 | |||
131 | omap_dm_timer_write_status(lirc_rx51->pulse_timer, | ||
132 | OMAP_TIMER_INT_MATCH | | ||
133 | OMAP_TIMER_INT_OVERFLOW | | ||
134 | OMAP_TIMER_INT_CAPTURE); | ||
135 | if (lirc_rx51->wbuf_index < 0) { | 81 | if (lirc_rx51->wbuf_index < 0) { |
136 | dev_err_ratelimited(lirc_rx51->dev, | 82 | dev_err_ratelimited(lirc_rx51->dev, |
137 | ": BUG wbuf_index has value of %i\n", | 83 | "BUG wbuf_index has value of %i\n", |
138 | lirc_rx51->wbuf_index); | 84 | lirc_rx51->wbuf_index); |
139 | goto end; | 85 | goto end; |
140 | } | 86 | } |
@@ -144,6 +90,8 @@ static irqreturn_t lirc_rx51_interrupt_handler(int irq, void *ptr) | |||
144 | * pulses until we catch up. | 90 | * pulses until we catch up. |
145 | */ | 91 | */ |
146 | do { | 92 | do { |
93 | u64 ns; | ||
94 | |||
147 | if (lirc_rx51->wbuf_index >= WBUF_LEN) | 95 | if (lirc_rx51->wbuf_index >= WBUF_LEN) |
148 | goto end; | 96 | goto end; |
149 | if (lirc_rx51->wbuf[lirc_rx51->wbuf_index] == -1) | 97 | if (lirc_rx51->wbuf[lirc_rx51->wbuf_index] == -1) |
@@ -154,84 +102,24 @@ static irqreturn_t lirc_rx51_interrupt_handler(int irq, void *ptr) | |||
154 | else | 102 | else |
155 | lirc_rx51_on(lirc_rx51); | 103 | lirc_rx51_on(lirc_rx51); |
156 | 104 | ||
157 | retval = pulse_timer_set_timeout(lirc_rx51, | 105 | ns = 1000 * lirc_rx51->wbuf[lirc_rx51->wbuf_index]; |
158 | lirc_rx51->wbuf[lirc_rx51->wbuf_index]); | 106 | hrtimer_add_expires_ns(timer, ns); |
107 | |||
159 | lirc_rx51->wbuf_index++; | 108 | lirc_rx51->wbuf_index++; |
160 | 109 | ||
161 | } while (retval); | 110 | now = timer->base->get_time(); |
111 | |||
112 | } while (hrtimer_get_expires_tv64(timer) < now.tv64); | ||
162 | 113 | ||
163 | return IRQ_HANDLED; | 114 | return HRTIMER_RESTART; |
164 | end: | 115 | end: |
165 | /* Stop TX here */ | 116 | /* Stop TX here */ |
166 | lirc_rx51_off(lirc_rx51); | 117 | lirc_rx51_off(lirc_rx51); |
167 | lirc_rx51->wbuf_index = -1; | 118 | lirc_rx51->wbuf_index = -1; |
168 | omap_dm_timer_stop(lirc_rx51->pwm_timer); | ||
169 | omap_dm_timer_stop(lirc_rx51->pulse_timer); | ||
170 | omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0); | ||
171 | wake_up_interruptible(&lirc_rx51->wqueue); | ||
172 | |||
173 | return IRQ_HANDLED; | ||
174 | } | ||
175 | |||
176 | static int lirc_rx51_init_port(struct lirc_rx51 *lirc_rx51) | ||
177 | { | ||
178 | struct clk *clk_fclk; | ||
179 | int retval, pwm_timer = lirc_rx51->pwm_timer_num; | ||
180 | |||
181 | lirc_rx51->pwm_timer = omap_dm_timer_request_specific(pwm_timer); | ||
182 | if (lirc_rx51->pwm_timer == NULL) { | ||
183 | dev_err(lirc_rx51->dev, ": Error requesting GPT%d timer\n", | ||
184 | pwm_timer); | ||
185 | return -EBUSY; | ||
186 | } | ||
187 | |||
188 | lirc_rx51->pulse_timer = omap_dm_timer_request(); | ||
189 | if (lirc_rx51->pulse_timer == NULL) { | ||
190 | dev_err(lirc_rx51->dev, ": Error requesting pulse timer\n"); | ||
191 | retval = -EBUSY; | ||
192 | goto err1; | ||
193 | } | ||
194 | |||
195 | omap_dm_timer_set_source(lirc_rx51->pwm_timer, OMAP_TIMER_SRC_SYS_CLK); | ||
196 | omap_dm_timer_set_source(lirc_rx51->pulse_timer, | ||
197 | OMAP_TIMER_SRC_SYS_CLK); | ||
198 | |||
199 | omap_dm_timer_enable(lirc_rx51->pwm_timer); | ||
200 | omap_dm_timer_enable(lirc_rx51->pulse_timer); | ||
201 | |||
202 | lirc_rx51->irq_num = omap_dm_timer_get_irq(lirc_rx51->pulse_timer); | ||
203 | retval = request_irq(lirc_rx51->irq_num, lirc_rx51_interrupt_handler, | ||
204 | IRQF_SHARED, "lirc_pulse_timer", lirc_rx51); | ||
205 | if (retval) { | ||
206 | dev_err(lirc_rx51->dev, ": Failed to request interrupt line\n"); | ||
207 | goto err2; | ||
208 | } | ||
209 | |||
210 | clk_fclk = omap_dm_timer_get_fclk(lirc_rx51->pwm_timer); | ||
211 | lirc_rx51->fclk_khz = clk_fclk->rate / 1000; | ||
212 | |||
213 | return 0; | ||
214 | 119 | ||
215 | err2: | 120 | wake_up_interruptible(&lirc_rx51->wqueue); |
216 | omap_dm_timer_free(lirc_rx51->pulse_timer); | ||
217 | err1: | ||
218 | omap_dm_timer_free(lirc_rx51->pwm_timer); | ||
219 | |||
220 | return retval; | ||
221 | } | ||
222 | |||
223 | static int lirc_rx51_free_port(struct lirc_rx51 *lirc_rx51) | ||
224 | { | ||
225 | omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0); | ||
226 | free_irq(lirc_rx51->irq_num, lirc_rx51); | ||
227 | lirc_rx51_off(lirc_rx51); | ||
228 | omap_dm_timer_disable(lirc_rx51->pwm_timer); | ||
229 | omap_dm_timer_disable(lirc_rx51->pulse_timer); | ||
230 | omap_dm_timer_free(lirc_rx51->pwm_timer); | ||
231 | omap_dm_timer_free(lirc_rx51->pulse_timer); | ||
232 | lirc_rx51->wbuf_index = -1; | ||
233 | 121 | ||
234 | return 0; | 122 | return HRTIMER_NORESTART; |
235 | } | 123 | } |
236 | 124 | ||
237 | static ssize_t lirc_rx51_write(struct file *file, const char *buf, | 125 | static ssize_t lirc_rx51_write(struct file *file, const char *buf, |
@@ -270,8 +158,9 @@ static ssize_t lirc_rx51_write(struct file *file, const char *buf, | |||
270 | 158 | ||
271 | lirc_rx51_on(lirc_rx51); | 159 | lirc_rx51_on(lirc_rx51); |
272 | lirc_rx51->wbuf_index = 1; | 160 | lirc_rx51->wbuf_index = 1; |
273 | pulse_timer_set_timeout(lirc_rx51, lirc_rx51->wbuf[0]); | 161 | hrtimer_start(&lirc_rx51->timer, |
274 | 162 | ns_to_ktime(1000 * lirc_rx51->wbuf[0]), | |
163 | HRTIMER_MODE_REL); | ||
275 | /* | 164 | /* |
276 | * Don't return back to the userspace until the transfer has | 165 | * Don't return back to the userspace until the transfer has |
277 | * finished | 166 | * finished |
@@ -371,14 +260,24 @@ static int lirc_rx51_open(struct inode *inode, struct file *file) | |||
371 | if (test_and_set_bit(1, &lirc_rx51->device_is_open)) | 260 | if (test_and_set_bit(1, &lirc_rx51->device_is_open)) |
372 | return -EBUSY; | 261 | return -EBUSY; |
373 | 262 | ||
374 | return lirc_rx51_init_port(lirc_rx51); | 263 | lirc_rx51->pwm = pwm_get(lirc_rx51->dev, NULL); |
264 | if (IS_ERR(lirc_rx51->pwm)) { | ||
265 | int res = PTR_ERR(lirc_rx51->pwm); | ||
266 | |||
267 | dev_err(lirc_rx51->dev, "pwm_get failed: %d\n", res); | ||
268 | return res; | ||
269 | } | ||
270 | |||
271 | return 0; | ||
375 | } | 272 | } |
376 | 273 | ||
377 | static int lirc_rx51_release(struct inode *inode, struct file *file) | 274 | static int lirc_rx51_release(struct inode *inode, struct file *file) |
378 | { | 275 | { |
379 | struct lirc_rx51 *lirc_rx51 = file->private_data; | 276 | struct lirc_rx51 *lirc_rx51 = file->private_data; |
380 | 277 | ||
381 | lirc_rx51_free_port(lirc_rx51); | 278 | hrtimer_cancel(&lirc_rx51->timer); |
279 | lirc_rx51_off(lirc_rx51); | ||
280 | pwm_put(lirc_rx51->pwm); | ||
382 | 281 | ||
383 | clear_bit(1, &lirc_rx51->device_is_open); | 282 | clear_bit(1, &lirc_rx51->device_is_open); |
384 | 283 | ||
@@ -386,7 +285,6 @@ static int lirc_rx51_release(struct inode *inode, struct file *file) | |||
386 | } | 285 | } |
387 | 286 | ||
388 | static struct lirc_rx51 lirc_rx51 = { | 287 | static struct lirc_rx51 lirc_rx51 = { |
389 | .freq = 38000, | ||
390 | .duty_cycle = 50, | 288 | .duty_cycle = 50, |
391 | .wbuf_index = -1, | 289 | .wbuf_index = -1, |
392 | }; | 290 | }; |
@@ -444,9 +342,32 @@ static int lirc_rx51_resume(struct platform_device *dev) | |||
444 | 342 | ||
445 | static int lirc_rx51_probe(struct platform_device *dev) | 343 | static int lirc_rx51_probe(struct platform_device *dev) |
446 | { | 344 | { |
345 | struct pwm_device *pwm; | ||
346 | |||
447 | lirc_rx51_driver.features = LIRC_RX51_DRIVER_FEATURES; | 347 | lirc_rx51_driver.features = LIRC_RX51_DRIVER_FEATURES; |
448 | lirc_rx51.pdata = dev->dev.platform_data; | 348 | lirc_rx51.pdata = dev->dev.platform_data; |
449 | lirc_rx51.pwm_timer_num = lirc_rx51.pdata->pwm_timer; | 349 | |
350 | if (!lirc_rx51.pdata) { | ||
351 | dev_err(&dev->dev, "Platform Data is missing\n"); | ||
352 | return -ENXIO; | ||
353 | } | ||
354 | |||
355 | pwm = pwm_get(&dev->dev, NULL); | ||
356 | if (IS_ERR(pwm)) { | ||
357 | int err = PTR_ERR(pwm); | ||
358 | |||
359 | if (err != -EPROBE_DEFER) | ||
360 | dev_err(&dev->dev, "pwm_get failed: %d\n", err); | ||
361 | return err; | ||
362 | } | ||
363 | |||
364 | /* Use default, in case userspace does not set the carrier */ | ||
365 | lirc_rx51.freq = DIV_ROUND_CLOSEST(pwm_get_period(pwm), NSEC_PER_SEC); | ||
366 | pwm_put(pwm); | ||
367 | |||
368 | hrtimer_init(&lirc_rx51.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | ||
369 | lirc_rx51.timer.function = lirc_rx51_timer_cb; | ||
370 | |||
450 | lirc_rx51.dev = &dev->dev; | 371 | lirc_rx51.dev = &dev->dev; |
451 | lirc_rx51_driver.dev = &dev->dev; | 372 | lirc_rx51_driver.dev = &dev->dev; |
452 | lirc_rx51_driver.minor = lirc_register_driver(&lirc_rx51_driver); | 373 | lirc_rx51_driver.minor = lirc_register_driver(&lirc_rx51_driver); |
@@ -457,8 +378,6 @@ static int lirc_rx51_probe(struct platform_device *dev) | |||
457 | lirc_rx51_driver.minor); | 378 | lirc_rx51_driver.minor); |
458 | return lirc_rx51_driver.minor; | 379 | return lirc_rx51_driver.minor; |
459 | } | 380 | } |
460 | dev_info(lirc_rx51.dev, "registration ok, minor: %d, pwm: %d\n", | ||
461 | lirc_rx51_driver.minor, lirc_rx51.pwm_timer_num); | ||
462 | 381 | ||
463 | return 0; | 382 | return 0; |
464 | } | 383 | } |
@@ -468,6 +387,14 @@ static int lirc_rx51_remove(struct platform_device *dev) | |||
468 | return lirc_unregister_driver(lirc_rx51_driver.minor); | 387 | return lirc_unregister_driver(lirc_rx51_driver.minor); |
469 | } | 388 | } |
470 | 389 | ||
390 | static const struct of_device_id lirc_rx51_match[] = { | ||
391 | { | ||
392 | .compatible = "nokia,n900-ir", | ||
393 | }, | ||
394 | {}, | ||
395 | }; | ||
396 | MODULE_DEVICE_TABLE(of, lirc_rx51_match); | ||
397 | |||
471 | struct platform_driver lirc_rx51_platform_driver = { | 398 | struct platform_driver lirc_rx51_platform_driver = { |
472 | .probe = lirc_rx51_probe, | 399 | .probe = lirc_rx51_probe, |
473 | .remove = lirc_rx51_remove, | 400 | .remove = lirc_rx51_remove, |
@@ -475,7 +402,7 @@ struct platform_driver lirc_rx51_platform_driver = { | |||
475 | .resume = lirc_rx51_resume, | 402 | .resume = lirc_rx51_resume, |
476 | .driver = { | 403 | .driver = { |
477 | .name = DRIVER_NAME, | 404 | .name = DRIVER_NAME, |
478 | .owner = THIS_MODULE, | 405 | .of_match_table = of_match_ptr(lirc_rx51_match), |
479 | }, | 406 | }, |
480 | }; | 407 | }; |
481 | module_platform_driver(lirc_rx51_platform_driver); | 408 | module_platform_driver(lirc_rx51_platform_driver); |
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 81ddb17575a9..133712346911 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig | |||
@@ -25,6 +25,17 @@ config ATMEL_SDRAMC | |||
25 | Starting with the at91sam9g45, this controller supports SDR, DDR and | 25 | Starting with the at91sam9g45, this controller supports SDR, DDR and |
26 | LP-DDR memories. | 26 | LP-DDR memories. |
27 | 27 | ||
28 | config ATMEL_EBI | ||
29 | bool "Atmel EBI driver" | ||
30 | default y | ||
31 | depends on ARCH_AT91 && OF | ||
32 | select MFD_SYSCON | ||
33 | help | ||
34 | Driver for Atmel EBI controller. | ||
35 | Used to configure the EBI (external bus interface) when the device- | ||
36 | tree is used. This bus supports NANDs, external ethernet controller, | ||
37 | SRAMs, ATA devices, etc. | ||
38 | |||
28 | config TI_AEMIF | 39 | config TI_AEMIF |
29 | tristate "Texas Instruments AEMIF driver" | 40 | tristate "Texas Instruments AEMIF driver" |
30 | depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF | 41 | depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF |
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index cb0b7a1df11a..b20ae38b5bfb 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile | |||
@@ -7,6 +7,7 @@ obj-$(CONFIG_OF) += of_memory.o | |||
7 | endif | 7 | endif |
8 | obj-$(CONFIG_ARM_PL172_MPMC) += pl172.o | 8 | obj-$(CONFIG_ARM_PL172_MPMC) += pl172.o |
9 | obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o | 9 | obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o |
10 | obj-$(CONFIG_ATMEL_EBI) += atmel-ebi.o | ||
10 | obj-$(CONFIG_TI_AEMIF) += ti-aemif.o | 11 | obj-$(CONFIG_TI_AEMIF) += ti-aemif.o |
11 | obj-$(CONFIG_TI_EMIF) += emif.o | 12 | obj-$(CONFIG_TI_EMIF) += emif.o |
12 | obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o | 13 | obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o |
diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c new file mode 100644 index 000000000000..f87ad6f5d2dc --- /dev/null +++ b/drivers/memory/atmel-ebi.c | |||
@@ -0,0 +1,766 @@ | |||
1 | /* | ||
2 | * EBI driver for Atmel chips | ||
3 | * inspired by the fsl weim bus driver | ||
4 | * | ||
5 | * Copyright (C) 2013 Jean-Jacques Hiblot <jjhiblot@traphandler.com> | ||
6 | * | ||
7 | * This file is licensed under the terms of the GNU General Public | ||
8 | * License version 2. This program is licensed "as is" without any | ||
9 | * warranty of any kind, whether express or implied. | ||
10 | */ | ||
11 | |||
12 | #include <linux/clk.h> | ||
13 | #include <linux/io.h> | ||
14 | #include <linux/mfd/syscon.h> | ||
15 | #include <linux/mfd/syscon/atmel-matrix.h> | ||
16 | #include <linux/mfd/syscon/atmel-smc.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/of_device.h> | ||
19 | #include <linux/regmap.h> | ||
20 | |||
21 | struct at91sam9_smc_timings { | ||
22 | u32 ncs_rd_setup_ns; | ||
23 | u32 nrd_setup_ns; | ||
24 | u32 ncs_wr_setup_ns; | ||
25 | u32 nwe_setup_ns; | ||
26 | u32 ncs_rd_pulse_ns; | ||
27 | u32 nrd_pulse_ns; | ||
28 | u32 ncs_wr_pulse_ns; | ||
29 | u32 nwe_pulse_ns; | ||
30 | u32 nrd_cycle_ns; | ||
31 | u32 nwe_cycle_ns; | ||
32 | u32 tdf_ns; | ||
33 | }; | ||
34 | |||
35 | struct at91sam9_smc_generic_fields { | ||
36 | struct regmap_field *setup; | ||
37 | struct regmap_field *pulse; | ||
38 | struct regmap_field *cycle; | ||
39 | struct regmap_field *mode; | ||
40 | }; | ||
41 | |||
42 | struct at91sam9_ebi_dev_config { | ||
43 | struct at91sam9_smc_timings timings; | ||
44 | u32 mode; | ||
45 | }; | ||
46 | |||
47 | struct at91_ebi_dev_config { | ||
48 | int cs; | ||
49 | union { | ||
50 | struct at91sam9_ebi_dev_config sam9; | ||
51 | }; | ||
52 | }; | ||
53 | |||
54 | struct at91_ebi; | ||
55 | |||
56 | struct at91_ebi_dev { | ||
57 | struct list_head node; | ||
58 | struct at91_ebi *ebi; | ||
59 | u32 mode; | ||
60 | int numcs; | ||
61 | struct at91_ebi_dev_config configs[]; | ||
62 | }; | ||
63 | |||
64 | struct at91_ebi_caps { | ||
65 | unsigned int available_cs; | ||
66 | const struct reg_field *ebi_csa; | ||
67 | void (*get_config)(struct at91_ebi_dev *ebid, | ||
68 | struct at91_ebi_dev_config *conf); | ||
69 | int (*xlate_config)(struct at91_ebi_dev *ebid, | ||
70 | struct device_node *configs_np, | ||
71 | struct at91_ebi_dev_config *conf); | ||
72 | int (*apply_config)(struct at91_ebi_dev *ebid, | ||
73 | struct at91_ebi_dev_config *conf); | ||
74 | int (*init)(struct at91_ebi *ebi); | ||
75 | }; | ||
76 | |||
77 | struct at91_ebi { | ||
78 | struct clk *clk; | ||
79 | struct regmap *smc; | ||
80 | struct regmap *matrix; | ||
81 | |||
82 | struct regmap_field *ebi_csa; | ||
83 | |||
84 | struct device *dev; | ||
85 | const struct at91_ebi_caps *caps; | ||
86 | struct list_head devs; | ||
87 | union { | ||
88 | struct at91sam9_smc_generic_fields sam9; | ||
89 | }; | ||
90 | }; | ||
91 | |||
92 | static void at91sam9_ebi_get_config(struct at91_ebi_dev *ebid, | ||
93 | struct at91_ebi_dev_config *conf) | ||
94 | { | ||
95 | struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9; | ||
96 | unsigned int clk_rate = clk_get_rate(ebid->ebi->clk); | ||
97 | struct at91sam9_ebi_dev_config *config = &conf->sam9; | ||
98 | struct at91sam9_smc_timings *timings = &config->timings; | ||
99 | unsigned int val; | ||
100 | |||
101 | regmap_fields_read(fields->mode, conf->cs, &val); | ||
102 | config->mode = val & ~AT91_SMC_TDF; | ||
103 | |||
104 | val = (val & AT91_SMC_TDF) >> 16; | ||
105 | timings->tdf_ns = clk_rate * val; | ||
106 | |||
107 | regmap_fields_read(fields->setup, conf->cs, &val); | ||
108 | timings->ncs_rd_setup_ns = (val >> 24) & 0x1f; | ||
109 | timings->ncs_rd_setup_ns += ((val >> 29) & 0x1) * 128; | ||
110 | timings->ncs_rd_setup_ns *= clk_rate; | ||
111 | timings->nrd_setup_ns = (val >> 16) & 0x1f; | ||
112 | timings->nrd_setup_ns += ((val >> 21) & 0x1) * 128; | ||
113 | timings->nrd_setup_ns *= clk_rate; | ||
114 | timings->ncs_wr_setup_ns = (val >> 8) & 0x1f; | ||
115 | timings->ncs_wr_setup_ns += ((val >> 13) & 0x1) * 128; | ||
116 | timings->ncs_wr_setup_ns *= clk_rate; | ||
117 | timings->nwe_setup_ns = val & 0x1f; | ||
118 | timings->nwe_setup_ns += ((val >> 5) & 0x1) * 128; | ||
119 | timings->nwe_setup_ns *= clk_rate; | ||
120 | |||
121 | regmap_fields_read(fields->pulse, conf->cs, &val); | ||
122 | timings->ncs_rd_pulse_ns = (val >> 24) & 0x3f; | ||
123 | timings->ncs_rd_pulse_ns += ((val >> 30) & 0x1) * 256; | ||
124 | timings->ncs_rd_pulse_ns *= clk_rate; | ||
125 | timings->nrd_pulse_ns = (val >> 16) & 0x3f; | ||
126 | timings->nrd_pulse_ns += ((val >> 22) & 0x1) * 256; | ||
127 | timings->nrd_pulse_ns *= clk_rate; | ||
128 | timings->ncs_wr_pulse_ns = (val >> 8) & 0x3f; | ||
129 | timings->ncs_wr_pulse_ns += ((val >> 14) & 0x1) * 256; | ||
130 | timings->ncs_wr_pulse_ns *= clk_rate; | ||
131 | timings->nwe_pulse_ns = val & 0x3f; | ||
132 | timings->nwe_pulse_ns += ((val >> 6) & 0x1) * 256; | ||
133 | timings->nwe_pulse_ns *= clk_rate; | ||
134 | |||
135 | regmap_fields_read(fields->cycle, conf->cs, &val); | ||
136 | timings->nrd_cycle_ns = (val >> 16) & 0x7f; | ||
137 | timings->nrd_cycle_ns += ((val >> 23) & 0x3) * 256; | ||
138 | timings->nrd_cycle_ns *= clk_rate; | ||
139 | timings->nwe_cycle_ns = val & 0x7f; | ||
140 | timings->nwe_cycle_ns += ((val >> 7) & 0x3) * 256; | ||
141 | timings->nwe_cycle_ns *= clk_rate; | ||
142 | } | ||
143 | |||
144 | static int at91_xlate_timing(struct device_node *np, const char *prop, | ||
145 | u32 *val, bool *required) | ||
146 | { | ||
147 | if (!of_property_read_u32(np, prop, val)) { | ||
148 | *required = true; | ||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | if (*required) | ||
153 | return -EINVAL; | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static int at91sam9_smc_xslate_timings(struct at91_ebi_dev *ebid, | ||
159 | struct device_node *np, | ||
160 | struct at91sam9_smc_timings *timings, | ||
161 | bool *required) | ||
162 | { | ||
163 | int ret; | ||
164 | |||
165 | ret = at91_xlate_timing(np, "atmel,smc-ncs-rd-setup-ns", | ||
166 | &timings->ncs_rd_setup_ns, required); | ||
167 | if (ret) | ||
168 | goto out; | ||
169 | |||
170 | ret = at91_xlate_timing(np, "atmel,smc-nrd-setup-ns", | ||
171 | &timings->nrd_setup_ns, required); | ||
172 | if (ret) | ||
173 | goto out; | ||
174 | |||
175 | ret = at91_xlate_timing(np, "atmel,smc-ncs-wr-setup-ns", | ||
176 | &timings->ncs_wr_setup_ns, required); | ||
177 | if (ret) | ||
178 | goto out; | ||
179 | |||
180 | ret = at91_xlate_timing(np, "atmel,smc-nwe-setup-ns", | ||
181 | &timings->nwe_setup_ns, required); | ||
182 | if (ret) | ||
183 | goto out; | ||
184 | |||
185 | ret = at91_xlate_timing(np, "atmel,smc-ncs-rd-pulse-ns", | ||
186 | &timings->ncs_rd_pulse_ns, required); | ||
187 | if (ret) | ||
188 | goto out; | ||
189 | |||
190 | ret = at91_xlate_timing(np, "atmel,smc-nrd-pulse-ns", | ||
191 | &timings->nrd_pulse_ns, required); | ||
192 | if (ret) | ||
193 | goto out; | ||
194 | |||
195 | ret = at91_xlate_timing(np, "atmel,smc-ncs-wr-pulse-ns", | ||
196 | &timings->ncs_wr_pulse_ns, required); | ||
197 | if (ret) | ||
198 | goto out; | ||
199 | |||
200 | ret = at91_xlate_timing(np, "atmel,smc-nwe-pulse-ns", | ||
201 | &timings->nwe_pulse_ns, required); | ||
202 | if (ret) | ||
203 | goto out; | ||
204 | |||
205 | ret = at91_xlate_timing(np, "atmel,smc-nwe-cycle-ns", | ||
206 | &timings->nwe_cycle_ns, required); | ||
207 | if (ret) | ||
208 | goto out; | ||
209 | |||
210 | ret = at91_xlate_timing(np, "atmel,smc-nrd-cycle-ns", | ||
211 | &timings->nrd_cycle_ns, required); | ||
212 | if (ret) | ||
213 | goto out; | ||
214 | |||
215 | ret = at91_xlate_timing(np, "atmel,smc-tdf-ns", | ||
216 | &timings->tdf_ns, required); | ||
217 | |||
218 | out: | ||
219 | if (ret) | ||
220 | dev_err(ebid->ebi->dev, | ||
221 | "missing or invalid timings definition in %s", | ||
222 | np->full_name); | ||
223 | |||
224 | return ret; | ||
225 | } | ||
226 | |||
227 | static int at91sam9_ebi_xslate_config(struct at91_ebi_dev *ebid, | ||
228 | struct device_node *np, | ||
229 | struct at91_ebi_dev_config *conf) | ||
230 | { | ||
231 | struct at91sam9_ebi_dev_config *config = &conf->sam9; | ||
232 | bool required = false; | ||
233 | const char *tmp_str; | ||
234 | u32 tmp; | ||
235 | int ret; | ||
236 | |||
237 | ret = of_property_read_u32(np, "atmel,smc-bus-width", &tmp); | ||
238 | if (!ret) { | ||
239 | switch (tmp) { | ||
240 | case 8: | ||
241 | config->mode |= AT91_SMC_DBW_8; | ||
242 | break; | ||
243 | |||
244 | case 16: | ||
245 | config->mode |= AT91_SMC_DBW_16; | ||
246 | break; | ||
247 | |||
248 | case 32: | ||
249 | config->mode |= AT91_SMC_DBW_32; | ||
250 | break; | ||
251 | |||
252 | default: | ||
253 | return -EINVAL; | ||
254 | } | ||
255 | |||
256 | required = true; | ||
257 | } | ||
258 | |||
259 | if (of_property_read_bool(np, "atmel,smc-tdf-optimized")) { | ||
260 | config->mode |= AT91_SMC_TDFMODE_OPTIMIZED; | ||
261 | required = true; | ||
262 | } | ||
263 | |||
264 | tmp_str = NULL; | ||
265 | of_property_read_string(np, "atmel,smc-byte-access-type", &tmp_str); | ||
266 | if (tmp_str && !strcmp(tmp_str, "write")) { | ||
267 | config->mode |= AT91_SMC_BAT_WRITE; | ||
268 | required = true; | ||
269 | } | ||
270 | |||
271 | tmp_str = NULL; | ||
272 | of_property_read_string(np, "atmel,smc-read-mode", &tmp_str); | ||
273 | if (tmp_str && !strcmp(tmp_str, "nrd")) { | ||
274 | config->mode |= AT91_SMC_READMODE_NRD; | ||
275 | required = true; | ||
276 | } | ||
277 | |||
278 | tmp_str = NULL; | ||
279 | of_property_read_string(np, "atmel,smc-write-mode", &tmp_str); | ||
280 | if (tmp_str && !strcmp(tmp_str, "nwe")) { | ||
281 | config->mode |= AT91_SMC_WRITEMODE_NWE; | ||
282 | required = true; | ||
283 | } | ||
284 | |||
285 | tmp_str = NULL; | ||
286 | of_property_read_string(np, "atmel,smc-exnw-mode", &tmp_str); | ||
287 | if (tmp_str) { | ||
288 | if (!strcmp(tmp_str, "frozen")) | ||
289 | config->mode |= AT91_SMC_EXNWMODE_FROZEN; | ||
290 | else if (!strcmp(tmp_str, "ready")) | ||
291 | config->mode |= AT91_SMC_EXNWMODE_READY; | ||
292 | else if (strcmp(tmp_str, "disabled")) | ||
293 | return -EINVAL; | ||
294 | |||
295 | required = true; | ||
296 | } | ||
297 | |||
298 | ret = of_property_read_u32(np, "atmel,smc-page-mode", &tmp); | ||
299 | if (!ret) { | ||
300 | switch (tmp) { | ||
301 | case 4: | ||
302 | config->mode |= AT91_SMC_PS_4; | ||
303 | break; | ||
304 | |||
305 | case 8: | ||
306 | config->mode |= AT91_SMC_PS_8; | ||
307 | break; | ||
308 | |||
309 | case 16: | ||
310 | config->mode |= AT91_SMC_PS_16; | ||
311 | break; | ||
312 | |||
313 | case 32: | ||
314 | config->mode |= AT91_SMC_PS_32; | ||
315 | break; | ||
316 | |||
317 | default: | ||
318 | return -EINVAL; | ||
319 | } | ||
320 | |||
321 | config->mode |= AT91_SMC_PMEN; | ||
322 | required = true; | ||
323 | } | ||
324 | |||
325 | ret = at91sam9_smc_xslate_timings(ebid, np, &config->timings, | ||
326 | &required); | ||
327 | if (ret) | ||
328 | return ret; | ||
329 | |||
330 | return required; | ||
331 | } | ||
332 | |||
333 | static int at91sam9_ebi_apply_config(struct at91_ebi_dev *ebid, | ||
334 | struct at91_ebi_dev_config *conf) | ||
335 | { | ||
336 | unsigned int clk_rate = clk_get_rate(ebid->ebi->clk); | ||
337 | struct at91sam9_ebi_dev_config *config = &conf->sam9; | ||
338 | struct at91sam9_smc_timings *timings = &config->timings; | ||
339 | struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9; | ||
340 | u32 coded_val; | ||
341 | u32 val; | ||
342 | |||
343 | coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate, | ||
344 | timings->ncs_rd_setup_ns); | ||
345 | val = AT91SAM9_SMC_NCS_NRDSETUP(coded_val); | ||
346 | coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate, | ||
347 | timings->nrd_setup_ns); | ||
348 | val |= AT91SAM9_SMC_NRDSETUP(coded_val); | ||
349 | coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate, | ||
350 | timings->ncs_wr_setup_ns); | ||
351 | val |= AT91SAM9_SMC_NCS_WRSETUP(coded_val); | ||
352 | coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate, | ||
353 | timings->nwe_setup_ns); | ||
354 | val |= AT91SAM9_SMC_NWESETUP(coded_val); | ||
355 | regmap_fields_write(fields->setup, conf->cs, val); | ||
356 | |||
357 | coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate, | ||
358 | timings->ncs_rd_pulse_ns); | ||
359 | val = AT91SAM9_SMC_NCS_NRDPULSE(coded_val); | ||
360 | coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate, | ||
361 | timings->nrd_pulse_ns); | ||
362 | val |= AT91SAM9_SMC_NRDPULSE(coded_val); | ||
363 | coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate, | ||
364 | timings->ncs_wr_pulse_ns); | ||
365 | val |= AT91SAM9_SMC_NCS_WRPULSE(coded_val); | ||
366 | coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate, | ||
367 | timings->nwe_pulse_ns); | ||
368 | val |= AT91SAM9_SMC_NWEPULSE(coded_val); | ||
369 | regmap_fields_write(fields->pulse, conf->cs, val); | ||
370 | |||
371 | coded_val = at91sam9_smc_cycle_ns_to_cycles(clk_rate, | ||
372 | timings->nrd_cycle_ns); | ||
373 | val = AT91SAM9_SMC_NRDCYCLE(coded_val); | ||
374 | coded_val = at91sam9_smc_cycle_ns_to_cycles(clk_rate, | ||
375 | timings->nwe_cycle_ns); | ||
376 | val |= AT91SAM9_SMC_NWECYCLE(coded_val); | ||
377 | regmap_fields_write(fields->cycle, conf->cs, val); | ||
378 | |||
379 | val = DIV_ROUND_UP(timings->tdf_ns, clk_rate); | ||
380 | if (val > AT91_SMC_TDF_MAX) | ||
381 | val = AT91_SMC_TDF_MAX; | ||
382 | regmap_fields_write(fields->mode, conf->cs, | ||
383 | config->mode | AT91_SMC_TDF_(val)); | ||
384 | |||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static int at91sam9_ebi_init(struct at91_ebi *ebi) | ||
389 | { | ||
390 | struct at91sam9_smc_generic_fields *fields = &ebi->sam9; | ||
391 | struct reg_field field = REG_FIELD(0, 0, 31); | ||
392 | |||
393 | field.id_size = fls(ebi->caps->available_cs); | ||
394 | field.id_offset = AT91SAM9_SMC_GENERIC_BLK_SZ; | ||
395 | |||
396 | field.reg = AT91SAM9_SMC_SETUP(AT91SAM9_SMC_GENERIC); | ||
397 | fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); | ||
398 | if (IS_ERR(fields->setup)) | ||
399 | return PTR_ERR(fields->setup); | ||
400 | |||
401 | field.reg = AT91SAM9_SMC_PULSE(AT91SAM9_SMC_GENERIC); | ||
402 | fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); | ||
403 | if (IS_ERR(fields->pulse)) | ||
404 | return PTR_ERR(fields->pulse); | ||
405 | |||
406 | field.reg = AT91SAM9_SMC_CYCLE(AT91SAM9_SMC_GENERIC); | ||
407 | fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); | ||
408 | if (IS_ERR(fields->cycle)) | ||
409 | return PTR_ERR(fields->cycle); | ||
410 | |||
411 | field.reg = AT91SAM9_SMC_MODE(AT91SAM9_SMC_GENERIC); | ||
412 | fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); | ||
413 | if (IS_ERR(fields->mode)) | ||
414 | return PTR_ERR(fields->mode); | ||
415 | |||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | static int sama5d3_ebi_init(struct at91_ebi *ebi) | ||
420 | { | ||
421 | struct at91sam9_smc_generic_fields *fields = &ebi->sam9; | ||
422 | struct reg_field field = REG_FIELD(0, 0, 31); | ||
423 | |||
424 | field.id_size = fls(ebi->caps->available_cs); | ||
425 | field.id_offset = SAMA5_SMC_GENERIC_BLK_SZ; | ||
426 | |||
427 | field.reg = AT91SAM9_SMC_SETUP(SAMA5_SMC_GENERIC); | ||
428 | fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); | ||
429 | if (IS_ERR(fields->setup)) | ||
430 | return PTR_ERR(fields->setup); | ||
431 | |||
432 | field.reg = AT91SAM9_SMC_PULSE(SAMA5_SMC_GENERIC); | ||
433 | fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); | ||
434 | if (IS_ERR(fields->pulse)) | ||
435 | return PTR_ERR(fields->pulse); | ||
436 | |||
437 | field.reg = AT91SAM9_SMC_CYCLE(SAMA5_SMC_GENERIC); | ||
438 | fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); | ||
439 | if (IS_ERR(fields->cycle)) | ||
440 | return PTR_ERR(fields->cycle); | ||
441 | |||
442 | field.reg = SAMA5_SMC_MODE(SAMA5_SMC_GENERIC); | ||
443 | fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); | ||
444 | if (IS_ERR(fields->mode)) | ||
445 | return PTR_ERR(fields->mode); | ||
446 | |||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np, | ||
451 | int reg_cells) | ||
452 | { | ||
453 | const struct at91_ebi_caps *caps = ebi->caps; | ||
454 | struct at91_ebi_dev_config conf = { }; | ||
455 | struct device *dev = ebi->dev; | ||
456 | struct at91_ebi_dev *ebid; | ||
457 | int ret, numcs = 0, i; | ||
458 | bool apply = false; | ||
459 | |||
460 | numcs = of_property_count_elems_of_size(np, "reg", | ||
461 | reg_cells * sizeof(u32)); | ||
462 | if (numcs <= 0) { | ||
463 | dev_err(dev, "invalid reg property in %s\n", np->full_name); | ||
464 | return -EINVAL; | ||
465 | } | ||
466 | |||
467 | ebid = devm_kzalloc(ebi->dev, | ||
468 | sizeof(*ebid) + (numcs * sizeof(*ebid->configs)), | ||
469 | GFP_KERNEL); | ||
470 | if (!ebid) | ||
471 | return -ENOMEM; | ||
472 | |||
473 | ebid->ebi = ebi; | ||
474 | |||
475 | ret = caps->xlate_config(ebid, np, &conf); | ||
476 | if (ret < 0) | ||
477 | return ret; | ||
478 | else if (ret) | ||
479 | apply = true; | ||
480 | |||
481 | for (i = 0; i < numcs; i++) { | ||
482 | u32 cs; | ||
483 | |||
484 | ret = of_property_read_u32_index(np, "reg", i * reg_cells, | ||
485 | &cs); | ||
486 | if (ret) | ||
487 | return ret; | ||
488 | |||
489 | if (cs > AT91_MATRIX_EBI_NUM_CS || | ||
490 | !(ebi->caps->available_cs & BIT(cs))) { | ||
491 | dev_err(dev, "invalid reg property in %s\n", | ||
492 | np->full_name); | ||
493 | return -EINVAL; | ||
494 | } | ||
495 | |||
496 | ebid->configs[i].cs = cs; | ||
497 | |||
498 | if (apply) { | ||
499 | conf.cs = cs; | ||
500 | ret = caps->apply_config(ebid, &conf); | ||
501 | if (ret) | ||
502 | return ret; | ||
503 | } | ||
504 | |||
505 | caps->get_config(ebid, &ebid->configs[i]); | ||
506 | |||
507 | /* | ||
508 | * Attach the EBI device to the generic SMC logic if at least | ||
509 | * one "atmel,smc-" property is present. | ||
510 | */ | ||
511 | if (ebi->ebi_csa && ret) | ||
512 | regmap_field_update_bits(ebi->ebi_csa, | ||
513 | BIT(cs), 0); | ||
514 | } | ||
515 | |||
516 | list_add_tail(&ebid->node, &ebi->devs); | ||
517 | |||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | static const struct reg_field at91sam9260_ebi_csa = | ||
522 | REG_FIELD(AT91SAM9260_MATRIX_EBICSA, 0, | ||
523 | AT91_MATRIX_EBI_NUM_CS - 1); | ||
524 | |||
525 | static const struct at91_ebi_caps at91sam9260_ebi_caps = { | ||
526 | .available_cs = 0xff, | ||
527 | .ebi_csa = &at91sam9260_ebi_csa, | ||
528 | .get_config = at91sam9_ebi_get_config, | ||
529 | .xlate_config = at91sam9_ebi_xslate_config, | ||
530 | .apply_config = at91sam9_ebi_apply_config, | ||
531 | .init = at91sam9_ebi_init, | ||
532 | }; | ||
533 | |||
534 | static const struct reg_field at91sam9261_ebi_csa = | ||
535 | REG_FIELD(AT91SAM9261_MATRIX_EBICSA, 0, | ||
536 | AT91_MATRIX_EBI_NUM_CS - 1); | ||
537 | |||
538 | static const struct at91_ebi_caps at91sam9261_ebi_caps = { | ||
539 | .available_cs = 0xff, | ||
540 | .ebi_csa = &at91sam9261_ebi_csa, | ||
541 | .get_config = at91sam9_ebi_get_config, | ||
542 | .xlate_config = at91sam9_ebi_xslate_config, | ||
543 | .apply_config = at91sam9_ebi_apply_config, | ||
544 | .init = at91sam9_ebi_init, | ||
545 | }; | ||
546 | |||
547 | static const struct reg_field at91sam9263_ebi0_csa = | ||
548 | REG_FIELD(AT91SAM9263_MATRIX_EBI0CSA, 0, | ||
549 | AT91_MATRIX_EBI_NUM_CS - 1); | ||
550 | |||
551 | static const struct at91_ebi_caps at91sam9263_ebi0_caps = { | ||
552 | .available_cs = 0x3f, | ||
553 | .ebi_csa = &at91sam9263_ebi0_csa, | ||
554 | .get_config = at91sam9_ebi_get_config, | ||
555 | .xlate_config = at91sam9_ebi_xslate_config, | ||
556 | .apply_config = at91sam9_ebi_apply_config, | ||
557 | .init = at91sam9_ebi_init, | ||
558 | }; | ||
559 | |||
560 | static const struct reg_field at91sam9263_ebi1_csa = | ||
561 | REG_FIELD(AT91SAM9263_MATRIX_EBI1CSA, 0, | ||
562 | AT91_MATRIX_EBI_NUM_CS - 1); | ||
563 | |||
564 | static const struct at91_ebi_caps at91sam9263_ebi1_caps = { | ||
565 | .available_cs = 0x7, | ||
566 | .ebi_csa = &at91sam9263_ebi1_csa, | ||
567 | .get_config = at91sam9_ebi_get_config, | ||
568 | .xlate_config = at91sam9_ebi_xslate_config, | ||
569 | .apply_config = at91sam9_ebi_apply_config, | ||
570 | .init = at91sam9_ebi_init, | ||
571 | }; | ||
572 | |||
573 | static const struct reg_field at91sam9rl_ebi_csa = | ||
574 | REG_FIELD(AT91SAM9RL_MATRIX_EBICSA, 0, | ||
575 | AT91_MATRIX_EBI_NUM_CS - 1); | ||
576 | |||
577 | static const struct at91_ebi_caps at91sam9rl_ebi_caps = { | ||
578 | .available_cs = 0x3f, | ||
579 | .ebi_csa = &at91sam9rl_ebi_csa, | ||
580 | .get_config = at91sam9_ebi_get_config, | ||
581 | .xlate_config = at91sam9_ebi_xslate_config, | ||
582 | .apply_config = at91sam9_ebi_apply_config, | ||
583 | .init = at91sam9_ebi_init, | ||
584 | }; | ||
585 | |||
586 | static const struct reg_field at91sam9g45_ebi_csa = | ||
587 | REG_FIELD(AT91SAM9G45_MATRIX_EBICSA, 0, | ||
588 | AT91_MATRIX_EBI_NUM_CS - 1); | ||
589 | |||
590 | static const struct at91_ebi_caps at91sam9g45_ebi_caps = { | ||
591 | .available_cs = 0x3f, | ||
592 | .ebi_csa = &at91sam9g45_ebi_csa, | ||
593 | .get_config = at91sam9_ebi_get_config, | ||
594 | .xlate_config = at91sam9_ebi_xslate_config, | ||
595 | .apply_config = at91sam9_ebi_apply_config, | ||
596 | .init = at91sam9_ebi_init, | ||
597 | }; | ||
598 | |||
599 | static const struct at91_ebi_caps at91sam9x5_ebi_caps = { | ||
600 | .available_cs = 0x3f, | ||
601 | .ebi_csa = &at91sam9263_ebi0_csa, | ||
602 | .get_config = at91sam9_ebi_get_config, | ||
603 | .xlate_config = at91sam9_ebi_xslate_config, | ||
604 | .apply_config = at91sam9_ebi_apply_config, | ||
605 | .init = at91sam9_ebi_init, | ||
606 | }; | ||
607 | |||
608 | static const struct at91_ebi_caps sama5d3_ebi_caps = { | ||
609 | .available_cs = 0xf, | ||
610 | .get_config = at91sam9_ebi_get_config, | ||
611 | .xlate_config = at91sam9_ebi_xslate_config, | ||
612 | .apply_config = at91sam9_ebi_apply_config, | ||
613 | .init = sama5d3_ebi_init, | ||
614 | }; | ||
615 | |||
616 | static const struct of_device_id at91_ebi_id_table[] = { | ||
617 | { | ||
618 | .compatible = "atmel,at91sam9260-ebi", | ||
619 | .data = &at91sam9260_ebi_caps, | ||
620 | }, | ||
621 | { | ||
622 | .compatible = "atmel,at91sam9261-ebi", | ||
623 | .data = &at91sam9261_ebi_caps, | ||
624 | }, | ||
625 | { | ||
626 | .compatible = "atmel,at91sam9263-ebi0", | ||
627 | .data = &at91sam9263_ebi0_caps, | ||
628 | }, | ||
629 | { | ||
630 | .compatible = "atmel,at91sam9263-ebi1", | ||
631 | .data = &at91sam9263_ebi1_caps, | ||
632 | }, | ||
633 | { | ||
634 | .compatible = "atmel,at91sam9rl-ebi", | ||
635 | .data = &at91sam9rl_ebi_caps, | ||
636 | }, | ||
637 | { | ||
638 | .compatible = "atmel,at91sam9g45-ebi", | ||
639 | .data = &at91sam9g45_ebi_caps, | ||
640 | }, | ||
641 | { | ||
642 | .compatible = "atmel,at91sam9x5-ebi", | ||
643 | .data = &at91sam9x5_ebi_caps, | ||
644 | }, | ||
645 | { | ||
646 | .compatible = "atmel,sama5d3-ebi", | ||
647 | .data = &sama5d3_ebi_caps, | ||
648 | }, | ||
649 | { /* sentinel */ } | ||
650 | }; | ||
651 | |||
652 | static int at91_ebi_dev_disable(struct at91_ebi *ebi, struct device_node *np) | ||
653 | { | ||
654 | struct device *dev = ebi->dev; | ||
655 | struct property *newprop; | ||
656 | |||
657 | newprop = devm_kzalloc(dev, sizeof(*newprop), GFP_KERNEL); | ||
658 | if (!newprop) | ||
659 | return -ENOMEM; | ||
660 | |||
661 | newprop->name = devm_kstrdup(dev, "status", GFP_KERNEL); | ||
662 | if (!newprop->name) | ||
663 | return -ENOMEM; | ||
664 | |||
665 | newprop->value = devm_kstrdup(dev, "disabled", GFP_KERNEL); | ||
666 | if (!newprop->name) | ||
667 | return -ENOMEM; | ||
668 | |||
669 | newprop->length = sizeof("disabled"); | ||
670 | |||
671 | return of_update_property(np, newprop); | ||
672 | } | ||
673 | |||
674 | static int at91_ebi_probe(struct platform_device *pdev) | ||
675 | { | ||
676 | struct device *dev = &pdev->dev; | ||
677 | struct device_node *child, *np = dev->of_node; | ||
678 | const struct of_device_id *match; | ||
679 | struct at91_ebi *ebi; | ||
680 | int ret, reg_cells; | ||
681 | struct clk *clk; | ||
682 | u32 val; | ||
683 | |||
684 | match = of_match_device(at91_ebi_id_table, dev); | ||
685 | if (!match || !match->data) | ||
686 | return -EINVAL; | ||
687 | |||
688 | ebi = devm_kzalloc(dev, sizeof(*ebi), GFP_KERNEL); | ||
689 | if (!ebi) | ||
690 | return -ENOMEM; | ||
691 | |||
692 | INIT_LIST_HEAD(&ebi->devs); | ||
693 | ebi->caps = match->data; | ||
694 | ebi->dev = dev; | ||
695 | |||
696 | clk = devm_clk_get(dev, NULL); | ||
697 | if (IS_ERR(clk)) | ||
698 | return PTR_ERR(clk); | ||
699 | |||
700 | ebi->clk = clk; | ||
701 | |||
702 | ebi->smc = syscon_regmap_lookup_by_phandle(np, "atmel,smc"); | ||
703 | if (IS_ERR(ebi->smc)) | ||
704 | return PTR_ERR(ebi->smc); | ||
705 | |||
706 | /* | ||
707 | * The sama5d3 does not provide an EBICSA register and thus does need | ||
708 | * to access the matrix registers. | ||
709 | */ | ||
710 | if (ebi->caps->ebi_csa) { | ||
711 | ebi->matrix = | ||
712 | syscon_regmap_lookup_by_phandle(np, "atmel,matrix"); | ||
713 | if (IS_ERR(ebi->matrix)) | ||
714 | return PTR_ERR(ebi->matrix); | ||
715 | |||
716 | ebi->ebi_csa = regmap_field_alloc(ebi->matrix, | ||
717 | *ebi->caps->ebi_csa); | ||
718 | if (IS_ERR(ebi->ebi_csa)) | ||
719 | return PTR_ERR(ebi->ebi_csa); | ||
720 | } | ||
721 | |||
722 | ret = ebi->caps->init(ebi); | ||
723 | if (ret) | ||
724 | return ret; | ||
725 | |||
726 | ret = of_property_read_u32(np, "#address-cells", &val); | ||
727 | if (ret) { | ||
728 | dev_err(dev, "missing #address-cells property\n"); | ||
729 | return ret; | ||
730 | } | ||
731 | |||
732 | reg_cells = val; | ||
733 | |||
734 | ret = of_property_read_u32(np, "#size-cells", &val); | ||
735 | if (ret) { | ||
736 | dev_err(dev, "missing #address-cells property\n"); | ||
737 | return ret; | ||
738 | } | ||
739 | |||
740 | reg_cells += val; | ||
741 | |||
742 | for_each_available_child_of_node(np, child) { | ||
743 | if (!of_find_property(child, "reg", NULL)) | ||
744 | continue; | ||
745 | |||
746 | ret = at91_ebi_dev_setup(ebi, child, reg_cells); | ||
747 | if (ret) { | ||
748 | dev_err(dev, "failed to configure EBI bus for %s, disabling the device", | ||
749 | child->full_name); | ||
750 | |||
751 | ret = at91_ebi_dev_disable(ebi, child); | ||
752 | if (ret) | ||
753 | return ret; | ||
754 | } | ||
755 | } | ||
756 | |||
757 | return of_platform_populate(np, NULL, NULL, dev); | ||
758 | } | ||
759 | |||
760 | static struct platform_driver at91_ebi_driver = { | ||
761 | .driver = { | ||
762 | .name = "atmel-ebi", | ||
763 | .of_match_table = at91_ebi_id_table, | ||
764 | }, | ||
765 | }; | ||
766 | builtin_platform_driver_probe(at91_ebi_driver, at91_ebi_probe); | ||
diff --git a/drivers/memory/atmel-sdramc.c b/drivers/memory/atmel-sdramc.c index a3ebc8a87479..53a341f3b305 100644 --- a/drivers/memory/atmel-sdramc.c +++ b/drivers/memory/atmel-sdramc.c | |||
@@ -1,6 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * Atmel (Multi-port DDR-)SDRAM Controller driver | 2 | * Atmel (Multi-port DDR-)SDRAM Controller driver |
3 | * | 3 | * |
4 | * Author: Alexandre Belloni <alexandre.belloni@free-electrons.com> | ||
5 | * | ||
4 | * Copyright (C) 2014 Atmel | 6 | * Copyright (C) 2014 Atmel |
5 | * | 7 | * |
6 | * This program is free software: you can redistribute it and/or modify | 8 | * This program is free software: you can redistribute it and/or modify |
@@ -20,7 +22,7 @@ | |||
20 | #include <linux/clk.h> | 22 | #include <linux/clk.h> |
21 | #include <linux/err.h> | 23 | #include <linux/err.h> |
22 | #include <linux/kernel.h> | 24 | #include <linux/kernel.h> |
23 | #include <linux/module.h> | 25 | #include <linux/init.h> |
24 | #include <linux/of_platform.h> | 26 | #include <linux/of_platform.h> |
25 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
26 | 28 | ||
@@ -48,7 +50,6 @@ static const struct of_device_id atmel_ramc_of_match[] = { | |||
48 | { .compatible = "atmel,sama5d3-ddramc", .data = &sama5d3_caps, }, | 50 | { .compatible = "atmel,sama5d3-ddramc", .data = &sama5d3_caps, }, |
49 | {}, | 51 | {}, |
50 | }; | 52 | }; |
51 | MODULE_DEVICE_TABLE(of, atmel_ramc_of_match); | ||
52 | 53 | ||
53 | static int atmel_ramc_probe(struct platform_device *pdev) | 54 | static int atmel_ramc_probe(struct platform_device *pdev) |
54 | { | 55 | { |
@@ -90,8 +91,4 @@ static int __init atmel_ramc_init(void) | |||
90 | { | 91 | { |
91 | return platform_driver_register(&atmel_ramc_driver); | 92 | return platform_driver_register(&atmel_ramc_driver); |
92 | } | 93 | } |
93 | module_init(atmel_ramc_init); | 94 | device_initcall(atmel_ramc_init); |
94 | |||
95 | MODULE_LICENSE("GPL v2"); | ||
96 | MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>"); | ||
97 | MODULE_DESCRIPTION("Atmel (Multi-port DDR-)SDRAM Controller"); | ||
diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 4721b591994f..869c83fb3c5d 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c | |||
@@ -20,7 +20,6 @@ | |||
20 | #include <linux/ioport.h> | 20 | #include <linux/ioport.h> |
21 | #include <linux/spinlock.h> | 21 | #include <linux/spinlock.h> |
22 | #include <linux/io.h> | 22 | #include <linux/io.h> |
23 | #include <linux/module.h> | ||
24 | #include <linux/gpio/driver.h> | 23 | #include <linux/gpio/driver.h> |
25 | #include <linux/interrupt.h> | 24 | #include <linux/interrupt.h> |
26 | #include <linux/irqdomain.h> | 25 | #include <linux/irqdomain.h> |
@@ -1807,7 +1806,6 @@ static const struct of_device_id gpmc_dt_ids[] = { | |||
1807 | { .compatible = "ti,am3352-gpmc" }, /* am335x devices */ | 1806 | { .compatible = "ti,am3352-gpmc" }, /* am335x devices */ |
1808 | { } | 1807 | { } |
1809 | }; | 1808 | }; |
1810 | MODULE_DEVICE_TABLE(of, gpmc_dt_ids); | ||
1811 | 1809 | ||
1812 | /** | 1810 | /** |
1813 | * gpmc_read_settings_dt - read gpmc settings from device-tree | 1811 | * gpmc_read_settings_dt - read gpmc settings from device-tree |
@@ -2154,68 +2152,6 @@ err: | |||
2154 | return ret; | 2152 | return ret; |
2155 | } | 2153 | } |
2156 | 2154 | ||
2157 | static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) | ||
2158 | { | ||
2159 | return 1; /* we're input only */ | ||
2160 | } | ||
2161 | |||
2162 | static int gpmc_gpio_direction_input(struct gpio_chip *chip, | ||
2163 | unsigned int offset) | ||
2164 | { | ||
2165 | return 0; /* we're input only */ | ||
2166 | } | ||
2167 | |||
2168 | static int gpmc_gpio_direction_output(struct gpio_chip *chip, | ||
2169 | unsigned int offset, int value) | ||
2170 | { | ||
2171 | return -EINVAL; /* we're input only */ | ||
2172 | } | ||
2173 | |||
2174 | static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset, | ||
2175 | int value) | ||
2176 | { | ||
2177 | } | ||
2178 | |||
2179 | static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset) | ||
2180 | { | ||
2181 | u32 reg; | ||
2182 | |||
2183 | offset += 8; | ||
2184 | |||
2185 | reg = gpmc_read_reg(GPMC_STATUS) & BIT(offset); | ||
2186 | |||
2187 | return !!reg; | ||
2188 | } | ||
2189 | |||
2190 | static int gpmc_gpio_init(struct gpmc_device *gpmc) | ||
2191 | { | ||
2192 | int ret; | ||
2193 | |||
2194 | gpmc->gpio_chip.parent = gpmc->dev; | ||
2195 | gpmc->gpio_chip.owner = THIS_MODULE; | ||
2196 | gpmc->gpio_chip.label = DEVICE_NAME; | ||
2197 | gpmc->gpio_chip.ngpio = gpmc_nr_waitpins; | ||
2198 | gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction; | ||
2199 | gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input; | ||
2200 | gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output; | ||
2201 | gpmc->gpio_chip.set = gpmc_gpio_set; | ||
2202 | gpmc->gpio_chip.get = gpmc_gpio_get; | ||
2203 | gpmc->gpio_chip.base = -1; | ||
2204 | |||
2205 | ret = gpiochip_add(&gpmc->gpio_chip); | ||
2206 | if (ret < 0) { | ||
2207 | dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret); | ||
2208 | return ret; | ||
2209 | } | ||
2210 | |||
2211 | return 0; | ||
2212 | } | ||
2213 | |||
2214 | static void gpmc_gpio_exit(struct gpmc_device *gpmc) | ||
2215 | { | ||
2216 | gpiochip_remove(&gpmc->gpio_chip); | ||
2217 | } | ||
2218 | |||
2219 | static int gpmc_probe_dt(struct platform_device *pdev) | 2155 | static int gpmc_probe_dt(struct platform_device *pdev) |
2220 | { | 2156 | { |
2221 | int ret; | 2157 | int ret; |
@@ -2280,7 +2216,69 @@ static int gpmc_probe_dt_children(struct platform_device *pdev) | |||
2280 | { | 2216 | { |
2281 | return 0; | 2217 | return 0; |
2282 | } | 2218 | } |
2283 | #endif | 2219 | #endif /* CONFIG_OF */ |
2220 | |||
2221 | static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) | ||
2222 | { | ||
2223 | return 1; /* we're input only */ | ||
2224 | } | ||
2225 | |||
2226 | static int gpmc_gpio_direction_input(struct gpio_chip *chip, | ||
2227 | unsigned int offset) | ||
2228 | { | ||
2229 | return 0; /* we're input only */ | ||
2230 | } | ||
2231 | |||
2232 | static int gpmc_gpio_direction_output(struct gpio_chip *chip, | ||
2233 | unsigned int offset, int value) | ||
2234 | { | ||
2235 | return -EINVAL; /* we're input only */ | ||
2236 | } | ||
2237 | |||
2238 | static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset, | ||
2239 | int value) | ||
2240 | { | ||
2241 | } | ||
2242 | |||
2243 | static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset) | ||
2244 | { | ||
2245 | u32 reg; | ||
2246 | |||
2247 | offset += 8; | ||
2248 | |||
2249 | reg = gpmc_read_reg(GPMC_STATUS) & BIT(offset); | ||
2250 | |||
2251 | return !!reg; | ||
2252 | } | ||
2253 | |||
2254 | static int gpmc_gpio_init(struct gpmc_device *gpmc) | ||
2255 | { | ||
2256 | int ret; | ||
2257 | |||
2258 | gpmc->gpio_chip.parent = gpmc->dev; | ||
2259 | gpmc->gpio_chip.owner = THIS_MODULE; | ||
2260 | gpmc->gpio_chip.label = DEVICE_NAME; | ||
2261 | gpmc->gpio_chip.ngpio = gpmc_nr_waitpins; | ||
2262 | gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction; | ||
2263 | gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input; | ||
2264 | gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output; | ||
2265 | gpmc->gpio_chip.set = gpmc_gpio_set; | ||
2266 | gpmc->gpio_chip.get = gpmc_gpio_get; | ||
2267 | gpmc->gpio_chip.base = -1; | ||
2268 | |||
2269 | ret = gpiochip_add(&gpmc->gpio_chip); | ||
2270 | if (ret < 0) { | ||
2271 | dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret); | ||
2272 | return ret; | ||
2273 | } | ||
2274 | |||
2275 | return 0; | ||
2276 | } | ||
2277 | |||
2278 | static void gpmc_gpio_exit(struct gpmc_device *gpmc) | ||
2279 | { | ||
2280 | gpiochip_remove(&gpmc->gpio_chip); | ||
2281 | } | ||
2284 | 2282 | ||
2285 | static int gpmc_probe(struct platform_device *pdev) | 2283 | static int gpmc_probe(struct platform_device *pdev) |
2286 | { | 2284 | { |
@@ -2436,15 +2434,7 @@ static __init int gpmc_init(void) | |||
2436 | { | 2434 | { |
2437 | return platform_driver_register(&gpmc_driver); | 2435 | return platform_driver_register(&gpmc_driver); |
2438 | } | 2436 | } |
2439 | |||
2440 | static __exit void gpmc_exit(void) | ||
2441 | { | ||
2442 | platform_driver_unregister(&gpmc_driver); | ||
2443 | |||
2444 | } | ||
2445 | |||
2446 | postcore_initcall(gpmc_init); | 2437 | postcore_initcall(gpmc_init); |
2447 | module_exit(gpmc_exit); | ||
2448 | 2438 | ||
2449 | static struct omap3_gpmc_regs gpmc_context; | 2439 | static struct omap3_gpmc_regs gpmc_context; |
2450 | 2440 | ||
diff --git a/drivers/memory/samsung/exynos-srom.c b/drivers/memory/samsung/exynos-srom.c index 96756fb4d6bd..bf827a666694 100644 --- a/drivers/memory/samsung/exynos-srom.c +++ b/drivers/memory/samsung/exynos-srom.c | |||
@@ -11,7 +11,7 @@ | |||
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/io.h> | 13 | #include <linux/io.h> |
14 | #include <linux/module.h> | 14 | #include <linux/init.h> |
15 | #include <linux/of.h> | 15 | #include <linux/of.h> |
16 | #include <linux/of_address.h> | 16 | #include <linux/of_address.h> |
17 | #include <linux/of_platform.h> | 17 | #include <linux/of_platform.h> |
@@ -91,17 +91,17 @@ static int exynos_srom_configure_bank(struct exynos_srom *srom, | |||
91 | if (width == 2) | 91 | if (width == 2) |
92 | cs |= 1 << EXYNOS_SROM_BW__DATAWIDTH__SHIFT; | 92 | cs |= 1 << EXYNOS_SROM_BW__DATAWIDTH__SHIFT; |
93 | 93 | ||
94 | bw = __raw_readl(srom->reg_base + EXYNOS_SROM_BW); | 94 | bw = readl_relaxed(srom->reg_base + EXYNOS_SROM_BW); |
95 | bw = (bw & ~(EXYNOS_SROM_BW__CS_MASK << bank)) | (cs << bank); | 95 | bw = (bw & ~(EXYNOS_SROM_BW__CS_MASK << bank)) | (cs << bank); |
96 | __raw_writel(bw, srom->reg_base + EXYNOS_SROM_BW); | 96 | writel_relaxed(bw, srom->reg_base + EXYNOS_SROM_BW); |
97 | 97 | ||
98 | __raw_writel(pmc | (timing[0] << EXYNOS_SROM_BCX__TACP__SHIFT) | | 98 | writel_relaxed(pmc | (timing[0] << EXYNOS_SROM_BCX__TACP__SHIFT) | |
99 | (timing[1] << EXYNOS_SROM_BCX__TCAH__SHIFT) | | 99 | (timing[1] << EXYNOS_SROM_BCX__TCAH__SHIFT) | |
100 | (timing[2] << EXYNOS_SROM_BCX__TCOH__SHIFT) | | 100 | (timing[2] << EXYNOS_SROM_BCX__TCOH__SHIFT) | |
101 | (timing[3] << EXYNOS_SROM_BCX__TACC__SHIFT) | | 101 | (timing[3] << EXYNOS_SROM_BCX__TACC__SHIFT) | |
102 | (timing[4] << EXYNOS_SROM_BCX__TCOS__SHIFT) | | 102 | (timing[4] << EXYNOS_SROM_BCX__TCOS__SHIFT) | |
103 | (timing[5] << EXYNOS_SROM_BCX__TACS__SHIFT), | 103 | (timing[5] << EXYNOS_SROM_BCX__TACS__SHIFT), |
104 | srom->reg_base + EXYNOS_SROM_BC0 + bank); | 104 | srom->reg_base + EXYNOS_SROM_BC0 + bank); |
105 | 105 | ||
106 | return 0; | 106 | return 0; |
107 | } | 107 | } |
@@ -134,7 +134,7 @@ static int exynos_srom_probe(struct platform_device *pdev) | |||
134 | platform_set_drvdata(pdev, srom); | 134 | platform_set_drvdata(pdev, srom); |
135 | 135 | ||
136 | srom->reg_offset = exynos_srom_alloc_reg_dump(exynos_srom_offsets, | 136 | srom->reg_offset = exynos_srom_alloc_reg_dump(exynos_srom_offsets, |
137 | sizeof(exynos_srom_offsets)); | 137 | ARRAY_SIZE(exynos_srom_offsets)); |
138 | if (!srom->reg_offset) { | 138 | if (!srom->reg_offset) { |
139 | iounmap(srom->reg_base); | 139 | iounmap(srom->reg_base); |
140 | return -ENOMEM; | 140 | return -ENOMEM; |
@@ -159,16 +159,6 @@ static int exynos_srom_probe(struct platform_device *pdev) | |||
159 | return of_platform_populate(np, NULL, NULL, dev); | 159 | return of_platform_populate(np, NULL, NULL, dev); |
160 | } | 160 | } |
161 | 161 | ||
162 | static int exynos_srom_remove(struct platform_device *pdev) | ||
163 | { | ||
164 | struct exynos_srom *srom = platform_get_drvdata(pdev); | ||
165 | |||
166 | kfree(srom->reg_offset); | ||
167 | iounmap(srom->reg_base); | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | #ifdef CONFIG_PM_SLEEP | 162 | #ifdef CONFIG_PM_SLEEP |
173 | static void exynos_srom_save(void __iomem *base, | 163 | static void exynos_srom_save(void __iomem *base, |
174 | struct exynos_srom_reg_dump *rd, | 164 | struct exynos_srom_reg_dump *rd, |
@@ -211,21 +201,16 @@ static const struct of_device_id of_exynos_srom_ids[] = { | |||
211 | }, | 201 | }, |
212 | {}, | 202 | {}, |
213 | }; | 203 | }; |
214 | MODULE_DEVICE_TABLE(of, of_exynos_srom_ids); | ||
215 | 204 | ||
216 | static SIMPLE_DEV_PM_OPS(exynos_srom_pm_ops, exynos_srom_suspend, exynos_srom_resume); | 205 | static SIMPLE_DEV_PM_OPS(exynos_srom_pm_ops, exynos_srom_suspend, exynos_srom_resume); |
217 | 206 | ||
218 | static struct platform_driver exynos_srom_driver = { | 207 | static struct platform_driver exynos_srom_driver = { |
219 | .probe = exynos_srom_probe, | 208 | .probe = exynos_srom_probe, |
220 | .remove = exynos_srom_remove, | ||
221 | .driver = { | 209 | .driver = { |
222 | .name = "exynos-srom", | 210 | .name = "exynos-srom", |
223 | .of_match_table = of_exynos_srom_ids, | 211 | .of_match_table = of_exynos_srom_ids, |
224 | .pm = &exynos_srom_pm_ops, | 212 | .pm = &exynos_srom_pm_ops, |
213 | .suppress_bind_attrs = true, | ||
225 | }, | 214 | }, |
226 | }; | 215 | }; |
227 | module_platform_driver(exynos_srom_driver); | 216 | builtin_platform_driver(exynos_srom_driver); |
228 | |||
229 | MODULE_AUTHOR("Pankaj Dubey <pankaj.dubey@samsung.com>"); | ||
230 | MODULE_DESCRIPTION("Exynos SROM Controller Driver"); | ||
231 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index a1ae0cc2b86d..a4803ac192bb 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c | |||
@@ -186,8 +186,10 @@ static int load_timings(struct tegra_mc *mc, struct device_node *node) | |||
186 | timing = &mc->timings[i++]; | 186 | timing = &mc->timings[i++]; |
187 | 187 | ||
188 | err = load_one_timing(mc, timing, child); | 188 | err = load_one_timing(mc, timing, child); |
189 | if (err) | 189 | if (err) { |
190 | of_node_put(child); | ||
190 | return err; | 191 | return err; |
192 | } | ||
191 | } | 193 | } |
192 | 194 | ||
193 | return 0; | 195 | return 0; |
@@ -206,15 +208,13 @@ static int tegra_mc_setup_timings(struct tegra_mc *mc) | |||
206 | for_each_child_of_node(mc->dev->of_node, node) { | 208 | for_each_child_of_node(mc->dev->of_node, node) { |
207 | err = of_property_read_u32(node, "nvidia,ram-code", | 209 | err = of_property_read_u32(node, "nvidia,ram-code", |
208 | &node_ram_code); | 210 | &node_ram_code); |
209 | if (err || (node_ram_code != ram_code)) { | 211 | if (err || (node_ram_code != ram_code)) |
210 | of_node_put(node); | ||
211 | continue; | 212 | continue; |
212 | } | ||
213 | 213 | ||
214 | err = load_timings(mc, node); | 214 | err = load_timings(mc, node); |
215 | of_node_put(node); | ||
215 | if (err) | 216 | if (err) |
216 | return err; | 217 | return err; |
217 | of_node_put(node); | ||
218 | break; | 218 | break; |
219 | } | 219 | } |
220 | 220 | ||
diff --git a/drivers/memory/tegra/tegra124-emc.c b/drivers/memory/tegra/tegra124-emc.c index 3dac7be39654..06cc781ebac1 100644 --- a/drivers/memory/tegra/tegra124-emc.c +++ b/drivers/memory/tegra/tegra124-emc.c | |||
@@ -970,8 +970,10 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc, | |||
970 | timing = &emc->timings[i++]; | 970 | timing = &emc->timings[i++]; |
971 | 971 | ||
972 | err = load_one_timing_from_dt(emc, timing, child); | 972 | err = load_one_timing_from_dt(emc, timing, child); |
973 | if (err) | 973 | if (err) { |
974 | of_node_put(child); | ||
974 | return err; | 975 | return err; |
976 | } | ||
975 | } | 977 | } |
976 | 978 | ||
977 | sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings, | 979 | sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings, |
@@ -995,10 +997,8 @@ tegra_emc_find_node_by_ram_code(struct device_node *node, u32 ram_code) | |||
995 | u32 value; | 997 | u32 value; |
996 | 998 | ||
997 | err = of_property_read_u32(np, "nvidia,ram-code", &value); | 999 | err = of_property_read_u32(np, "nvidia,ram-code", &value); |
998 | if (err || (value != ram_code)) { | 1000 | if (err || (value != ram_code)) |
999 | of_node_put(np); | ||
1000 | continue; | 1001 | continue; |
1001 | } | ||
1002 | 1002 | ||
1003 | return np; | 1003 | return np; |
1004 | } | 1004 | } |
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index f3d689176fc2..589eebfc13df 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c | |||
@@ -1087,7 +1087,6 @@ static int ab8500_probe(struct platform_device *pdev) | |||
1087 | "Vbus Detect (USB)", | 1087 | "Vbus Detect (USB)", |
1088 | "USB ID Detect", | 1088 | "USB ID Detect", |
1089 | "UART Factory Mode Detect"}; | 1089 | "UART Factory Mode Detect"}; |
1090 | struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev); | ||
1091 | const struct platform_device_id *platid = platform_get_device_id(pdev); | 1090 | const struct platform_device_id *platid = platform_get_device_id(pdev); |
1092 | enum ab8500_version version = AB8500_VERSION_UNDEFINED; | 1091 | enum ab8500_version version = AB8500_VERSION_UNDEFINED; |
1093 | struct device_node *np = pdev->dev.of_node; | 1092 | struct device_node *np = pdev->dev.of_node; |
@@ -1219,9 +1218,6 @@ static int ab8500_probe(struct platform_device *pdev) | |||
1219 | pr_cont("None\n"); | 1218 | pr_cont("None\n"); |
1220 | } | 1219 | } |
1221 | 1220 | ||
1222 | if (plat && plat->init) | ||
1223 | plat->init(ab8500); | ||
1224 | |||
1225 | if (is_ab9540(ab8500)) { | 1221 | if (is_ab9540(ab8500)) { |
1226 | ret = get_register_interruptible(ab8500, AB8500_CHARGER, | 1222 | ret = get_register_interruptible(ab8500, AB8500_CHARGER, |
1227 | AB8500_CH_USBCH_STAT1_REG, &value); | 1223 | AB8500_CH_USBCH_STAT1_REG, &value); |
diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index b9f0010309f9..207cc497958a 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c | |||
@@ -127,45 +127,11 @@ EXPORT_SYMBOL(ab8500_sysctrl_write); | |||
127 | 127 | ||
128 | static int ab8500_sysctrl_probe(struct platform_device *pdev) | 128 | static int ab8500_sysctrl_probe(struct platform_device *pdev) |
129 | { | 129 | { |
130 | struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); | ||
131 | struct ab8500_platform_data *plat; | ||
132 | struct ab8500_sysctrl_platform_data *pdata; | ||
133 | |||
134 | plat = dev_get_platdata(pdev->dev.parent); | ||
135 | |||
136 | if (!plat) | ||
137 | return -EINVAL; | ||
138 | |||
139 | sysctrl_dev = &pdev->dev; | 130 | sysctrl_dev = &pdev->dev; |
140 | 131 | ||
141 | if (!pm_power_off) | 132 | if (!pm_power_off) |
142 | pm_power_off = ab8500_power_off; | 133 | pm_power_off = ab8500_power_off; |
143 | 134 | ||
144 | pdata = plat->sysctrl; | ||
145 | if (pdata) { | ||
146 | int last, ret, i, j; | ||
147 | |||
148 | if (is_ab8505(ab8500)) | ||
149 | last = AB8500_SYSCLKREQ4RFCLKBUF; | ||
150 | else | ||
151 | last = AB8500_SYSCLKREQ8RFCLKBUF; | ||
152 | |||
153 | for (i = AB8500_SYSCLKREQ1RFCLKBUF; i <= last; i++) { | ||
154 | j = i - AB8500_SYSCLKREQ1RFCLKBUF; | ||
155 | ret = ab8500_sysctrl_write(i, 0xff, | ||
156 | pdata->initial_req_buf_config[j]); | ||
157 | dev_dbg(&pdev->dev, | ||
158 | "Setting SysClkReq%dRfClkBuf 0x%X\n", | ||
159 | j + 1, | ||
160 | pdata->initial_req_buf_config[j]); | ||
161 | if (ret < 0) { | ||
162 | dev_err(&pdev->dev, | ||
163 | "Can't set sysClkReq%dRfClkBuf: %d\n", | ||
164 | j + 1, ret); | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | |||
169 | return 0; | 135 | return 0; |
170 | } | 136 | } |
171 | 137 | ||
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index c0a86aeb1733..388e268b9bcf 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c | |||
@@ -3094,8 +3094,7 @@ static void db8500_prcmu_update_cpufreq(void) | |||
3094 | } | 3094 | } |
3095 | } | 3095 | } |
3096 | 3096 | ||
3097 | static int db8500_prcmu_register_ab8500(struct device *parent, | 3097 | static int db8500_prcmu_register_ab8500(struct device *parent) |
3098 | struct ab8500_platform_data *pdata) | ||
3099 | { | 3098 | { |
3100 | struct device_node *np; | 3099 | struct device_node *np; |
3101 | struct resource ab8500_resource; | 3100 | struct resource ab8500_resource; |
@@ -3103,8 +3102,6 @@ static int db8500_prcmu_register_ab8500(struct device *parent, | |||
3103 | .name = "ab8500-core", | 3102 | .name = "ab8500-core", |
3104 | .of_compatible = "stericsson,ab8500", | 3103 | .of_compatible = "stericsson,ab8500", |
3105 | .id = AB8500_VERSION_AB8500, | 3104 | .id = AB8500_VERSION_AB8500, |
3106 | .platform_data = pdata, | ||
3107 | .pdata_size = sizeof(struct ab8500_platform_data), | ||
3108 | .resources = &ab8500_resource, | 3105 | .resources = &ab8500_resource, |
3109 | .num_resources = 1, | 3106 | .num_resources = 1, |
3110 | }; | 3107 | }; |
@@ -3133,7 +3130,6 @@ static int db8500_prcmu_register_ab8500(struct device *parent, | |||
3133 | static int db8500_prcmu_probe(struct platform_device *pdev) | 3130 | static int db8500_prcmu_probe(struct platform_device *pdev) |
3134 | { | 3131 | { |
3135 | struct device_node *np = pdev->dev.of_node; | 3132 | struct device_node *np = pdev->dev.of_node; |
3136 | struct prcmu_pdata *pdata = dev_get_platdata(&pdev->dev); | ||
3137 | int irq = 0, err = 0; | 3133 | int irq = 0, err = 0; |
3138 | struct resource *res; | 3134 | struct resource *res; |
3139 | 3135 | ||
@@ -3149,7 +3145,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev) | |||
3149 | return -ENOMEM; | 3145 | return -ENOMEM; |
3150 | } | 3146 | } |
3151 | init_prcm_registers(); | 3147 | init_prcm_registers(); |
3152 | dbx500_fw_version_init(pdev, pdata->version_offset); | 3148 | dbx500_fw_version_init(pdev, DB8500_PRCMU_FW_VERSION_OFFSET); |
3153 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm"); | 3149 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm"); |
3154 | if (!res) { | 3150 | if (!res) { |
3155 | dev_err(&pdev->dev, "no prcmu tcdm region provided\n"); | 3151 | dev_err(&pdev->dev, "no prcmu tcdm region provided\n"); |
@@ -3204,7 +3200,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev) | |||
3204 | } | 3200 | } |
3205 | } | 3201 | } |
3206 | 3202 | ||
3207 | err = db8500_prcmu_register_ab8500(&pdev->dev, pdata->ab_platdata); | 3203 | err = db8500_prcmu_register_ab8500(&pdev->dev); |
3208 | if (err) { | 3204 | if (err) { |
3209 | mfd_remove_devices(&pdev->dev); | 3205 | mfd_remove_devices(&pdev->dev); |
3210 | pr_err("prcmu: Failed to add ab8500 subdevice\n"); | 3206 | pr_err("prcmu: Failed to add ab8500 subdevice\n"); |
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 7053abced0bc..3bfac539334b 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig | |||
@@ -46,6 +46,16 @@ config POWER_RESET_AXXIA | |||
46 | 46 | ||
47 | Say Y if you have an Axxia family SoC. | 47 | Say Y if you have an Axxia family SoC. |
48 | 48 | ||
49 | config POWER_RESET_BRCMKONA | ||
50 | bool "Broadcom Kona reset driver" | ||
51 | depends on ARM || COMPILE_TEST | ||
52 | default ARCH_BCM_MOBILE | ||
53 | help | ||
54 | This driver provides restart support for Broadcom Kona chips. | ||
55 | |||
56 | Say Y here if you have a Broadcom Kona-based board and you wish | ||
57 | to have restart support. | ||
58 | |||
49 | config POWER_RESET_BRCMSTB | 59 | config POWER_RESET_BRCMSTB |
50 | bool "Broadcom STB reset driver" | 60 | bool "Broadcom STB reset driver" |
51 | depends on ARM || MIPS || COMPILE_TEST | 61 | depends on ARM || MIPS || COMPILE_TEST |
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index d6b2560d5c4a..1be307c7fc25 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile | |||
@@ -3,6 +3,7 @@ obj-$(CONFIG_POWER_RESET_AT91_POWEROFF) += at91-poweroff.o | |||
3 | obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o | 3 | obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o |
4 | obj-$(CONFIG_POWER_RESET_AT91_SAMA5D2_SHDWC) += at91-sama5d2_shdwc.o | 4 | obj-$(CONFIG_POWER_RESET_AT91_SAMA5D2_SHDWC) += at91-sama5d2_shdwc.o |
5 | obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o | 5 | obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o |
6 | obj-$(CONFIG_POWER_RESET_BRCMKONA) += brcm-kona-reset.o | ||
6 | obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o | 7 | obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o |
7 | obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o | 8 | obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o |
8 | obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o | 9 | obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o |
diff --git a/drivers/power/reset/brcm-kona-reset.c b/drivers/power/reset/brcm-kona-reset.c new file mode 100644 index 000000000000..8eaa959d8be6 --- /dev/null +++ b/drivers/power/reset/brcm-kona-reset.c | |||
@@ -0,0 +1,73 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Broadcom | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License as | ||
6 | * published by the Free Software Foundation version 2. | ||
7 | * | ||
8 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
9 | * kind, whether express or implied; without even the implied warranty | ||
10 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/io.h> | ||
15 | #include <linux/of_address.h> | ||
16 | #include <linux/of_platform.h> | ||
17 | #include <linux/reboot.h> | ||
18 | |||
19 | #define RSTMGR_REG_WR_ACCESS_OFFSET 0 | ||
20 | #define RSTMGR_REG_CHIP_SOFT_RST_OFFSET 4 | ||
21 | |||
22 | #define RSTMGR_WR_PASSWORD 0xa5a5 | ||
23 | #define RSTMGR_WR_PASSWORD_SHIFT 8 | ||
24 | #define RSTMGR_WR_ACCESS_ENABLE 1 | ||
25 | |||
26 | static void __iomem *kona_reset_base; | ||
27 | |||
28 | static int kona_reset_handler(struct notifier_block *this, | ||
29 | unsigned long mode, void *cmd) | ||
30 | { | ||
31 | /* | ||
32 | * A soft reset is triggered by writing a 0 to bit 0 of the soft reset | ||
33 | * register. To write to that register we must first write the password | ||
34 | * and the enable bit in the write access enable register. | ||
35 | */ | ||
36 | writel((RSTMGR_WR_PASSWORD << RSTMGR_WR_PASSWORD_SHIFT) | | ||
37 | RSTMGR_WR_ACCESS_ENABLE, | ||
38 | kona_reset_base + RSTMGR_REG_WR_ACCESS_OFFSET); | ||
39 | writel(0, kona_reset_base + RSTMGR_REG_CHIP_SOFT_RST_OFFSET); | ||
40 | |||
41 | return NOTIFY_DONE; | ||
42 | } | ||
43 | |||
44 | static struct notifier_block kona_reset_nb = { | ||
45 | .notifier_call = kona_reset_handler, | ||
46 | .priority = 128, | ||
47 | }; | ||
48 | |||
49 | static int kona_reset_probe(struct platform_device *pdev) | ||
50 | { | ||
51 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
52 | |||
53 | kona_reset_base = devm_ioremap_resource(&pdev->dev, res); | ||
54 | if (IS_ERR(kona_reset_base)) | ||
55 | return PTR_ERR(kona_reset_base); | ||
56 | |||
57 | return register_restart_handler(&kona_reset_nb); | ||
58 | } | ||
59 | |||
60 | static const struct of_device_id of_match[] = { | ||
61 | { .compatible = "brcm,bcm21664-resetmgr" }, | ||
62 | {}, | ||
63 | }; | ||
64 | |||
65 | static struct platform_driver bcm_kona_reset_driver = { | ||
66 | .probe = kona_reset_probe, | ||
67 | .driver = { | ||
68 | .name = "brcm-kona-reset", | ||
69 | .of_match_table = of_match, | ||
70 | }, | ||
71 | }; | ||
72 | |||
73 | builtin_platform_driver(bcm_kona_reset_driver); | ||
diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c index 7d335422cfda..26ec24e457b1 100644 --- a/drivers/pwm/pwm-clps711x.c +++ b/drivers/pwm/pwm-clps711x.c | |||
@@ -155,7 +155,7 @@ static int clps711x_pwm_remove(struct platform_device *pdev) | |||
155 | } | 155 | } |
156 | 156 | ||
157 | static const struct of_device_id __maybe_unused clps711x_pwm_dt_ids[] = { | 157 | static const struct of_device_id __maybe_unused clps711x_pwm_dt_ids[] = { |
158 | { .compatible = "cirrus,clps711x-pwm", }, | 158 | { .compatible = "cirrus,ep7209-pwm", }, |
159 | { } | 159 | { } |
160 | }; | 160 | }; |
161 | MODULE_DEVICE_TABLE(of, clps711x_pwm_dt_ids); | 161 | MODULE_DEVICE_TABLE(of, clps711x_pwm_dt_ids); |
diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c index 3e95090cd7cf..5ad42f33e70c 100644 --- a/drivers/pwm/pwm-omap-dmtimer.c +++ b/drivers/pwm/pwm-omap-dmtimer.c | |||
@@ -245,7 +245,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) | |||
245 | struct pwm_omap_dmtimer_chip *omap; | 245 | struct pwm_omap_dmtimer_chip *omap; |
246 | struct pwm_omap_dmtimer_pdata *pdata; | 246 | struct pwm_omap_dmtimer_pdata *pdata; |
247 | pwm_omap_dmtimer *dm_timer; | 247 | pwm_omap_dmtimer *dm_timer; |
248 | u32 prescaler; | 248 | u32 v; |
249 | int status; | 249 | int status; |
250 | 250 | ||
251 | pdata = dev_get_platdata(&pdev->dev); | 251 | pdata = dev_get_platdata(&pdev->dev); |
@@ -306,10 +306,12 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) | |||
306 | if (pm_runtime_active(&omap->dm_timer_pdev->dev)) | 306 | if (pm_runtime_active(&omap->dm_timer_pdev->dev)) |
307 | omap->pdata->stop(omap->dm_timer); | 307 | omap->pdata->stop(omap->dm_timer); |
308 | 308 | ||
309 | /* setup dmtimer prescaler */ | 309 | if (!of_property_read_u32(pdev->dev.of_node, "ti,prescaler", &v)) |
310 | if (!of_property_read_u32(pdev->dev.of_node, "ti,prescaler", | 310 | omap->pdata->set_prescaler(omap->dm_timer, v); |
311 | &prescaler)) | 311 | |
312 | omap->pdata->set_prescaler(omap->dm_timer, prescaler); | 312 | /* setup dmtimer clock source */ |
313 | if (!of_property_read_u32(pdev->dev.of_node, "ti,clock-source", &v)) | ||
314 | omap->pdata->set_source(omap->dm_timer, v); | ||
313 | 315 | ||
314 | omap->chip.dev = &pdev->dev; | 316 | omap->chip.dev = &pdev->dev; |
315 | omap->chip.ops = &pwm_omap_dmtimer_ops; | 317 | omap->chip.ops = &pwm_omap_dmtimer_ops; |
diff --git a/drivers/regulator/ab8500-ext.c b/drivers/regulator/ab8500-ext.c index 84c1ee39ddae..2ca00045eb99 100644 --- a/drivers/regulator/ab8500-ext.c +++ b/drivers/regulator/ab8500-ext.c | |||
@@ -25,6 +25,456 @@ | |||
25 | #include <linux/mfd/abx500/ab8500.h> | 25 | #include <linux/mfd/abx500/ab8500.h> |
26 | #include <linux/regulator/ab8500.h> | 26 | #include <linux/regulator/ab8500.h> |
27 | 27 | ||
28 | static struct regulator_consumer_supply ab8500_vaux1_consumers[] = { | ||
29 | /* Main display, u8500 R3 uib */ | ||
30 | REGULATOR_SUPPLY("vddi", "mcde_disp_sony_acx424akp.0"), | ||
31 | /* Main display, u8500 uib and ST uib */ | ||
32 | REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.0"), | ||
33 | /* Secondary display, ST uib */ | ||
34 | REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.1"), | ||
35 | /* SFH7741 proximity sensor */ | ||
36 | REGULATOR_SUPPLY("vcc", "gpio-keys.0"), | ||
37 | /* BH1780GLS ambient light sensor */ | ||
38 | REGULATOR_SUPPLY("vcc", "2-0029"), | ||
39 | /* lsm303dlh accelerometer */ | ||
40 | REGULATOR_SUPPLY("vdd", "2-0018"), | ||
41 | /* lsm303dlhc accelerometer */ | ||
42 | REGULATOR_SUPPLY("vdd", "2-0019"), | ||
43 | /* lsm303dlh magnetometer */ | ||
44 | REGULATOR_SUPPLY("vdd", "2-001e"), | ||
45 | /* Rohm BU21013 Touchscreen devices */ | ||
46 | REGULATOR_SUPPLY("avdd", "3-005c"), | ||
47 | REGULATOR_SUPPLY("avdd", "3-005d"), | ||
48 | /* Synaptics RMI4 Touchscreen device */ | ||
49 | REGULATOR_SUPPLY("vdd", "3-004b"), | ||
50 | /* L3G4200D Gyroscope device */ | ||
51 | REGULATOR_SUPPLY("vdd", "2-0068"), | ||
52 | /* Ambient light sensor device */ | ||
53 | REGULATOR_SUPPLY("vdd", "3-0029"), | ||
54 | /* Pressure sensor device */ | ||
55 | REGULATOR_SUPPLY("vdd", "2-005c"), | ||
56 | /* Cypress TrueTouch Touchscreen device */ | ||
57 | REGULATOR_SUPPLY("vcpin", "spi8.0"), | ||
58 | /* Camera device */ | ||
59 | REGULATOR_SUPPLY("vaux12v5", "mmio_camera"), | ||
60 | }; | ||
61 | |||
62 | static struct regulator_consumer_supply ab8500_vaux2_consumers[] = { | ||
63 | /* On-board eMMC power */ | ||
64 | REGULATOR_SUPPLY("vmmc", "sdi4"), | ||
65 | /* AB8500 audio codec */ | ||
66 | REGULATOR_SUPPLY("vcc-N2158", "ab8500-codec.0"), | ||
67 | /* AB8500 accessory detect 1 */ | ||
68 | REGULATOR_SUPPLY("vcc-N2158", "ab8500-acc-det.0"), | ||
69 | /* AB8500 Tv-out device */ | ||
70 | REGULATOR_SUPPLY("vcc-N2158", "mcde_tv_ab8500.4"), | ||
71 | /* AV8100 HDMI device */ | ||
72 | REGULATOR_SUPPLY("vcc-N2158", "av8100_hdmi.3"), | ||
73 | }; | ||
74 | |||
75 | static struct regulator_consumer_supply ab8500_vaux3_consumers[] = { | ||
76 | REGULATOR_SUPPLY("v-SD-STM", "stm"), | ||
77 | /* External MMC slot power */ | ||
78 | REGULATOR_SUPPLY("vmmc", "sdi0"), | ||
79 | }; | ||
80 | |||
81 | static struct regulator_consumer_supply ab8500_vtvout_consumers[] = { | ||
82 | /* TV-out DENC supply */ | ||
83 | REGULATOR_SUPPLY("vtvout", "ab8500-denc.0"), | ||
84 | /* Internal general-purpose ADC */ | ||
85 | REGULATOR_SUPPLY("vddadc", "ab8500-gpadc.0"), | ||
86 | /* ADC for charger */ | ||
87 | REGULATOR_SUPPLY("vddadc", "ab8500-charger.0"), | ||
88 | /* AB8500 Tv-out device */ | ||
89 | REGULATOR_SUPPLY("vtvout", "mcde_tv_ab8500.4"), | ||
90 | }; | ||
91 | |||
92 | static struct regulator_consumer_supply ab8500_vaud_consumers[] = { | ||
93 | /* AB8500 audio-codec main supply */ | ||
94 | REGULATOR_SUPPLY("vaud", "ab8500-codec.0"), | ||
95 | }; | ||
96 | |||
97 | static struct regulator_consumer_supply ab8500_vamic1_consumers[] = { | ||
98 | /* AB8500 audio-codec Mic1 supply */ | ||
99 | REGULATOR_SUPPLY("vamic1", "ab8500-codec.0"), | ||
100 | }; | ||
101 | |||
102 | static struct regulator_consumer_supply ab8500_vamic2_consumers[] = { | ||
103 | /* AB8500 audio-codec Mic2 supply */ | ||
104 | REGULATOR_SUPPLY("vamic2", "ab8500-codec.0"), | ||
105 | }; | ||
106 | |||
107 | static struct regulator_consumer_supply ab8500_vdmic_consumers[] = { | ||
108 | /* AB8500 audio-codec DMic supply */ | ||
109 | REGULATOR_SUPPLY("vdmic", "ab8500-codec.0"), | ||
110 | }; | ||
111 | |||
112 | static struct regulator_consumer_supply ab8500_vintcore_consumers[] = { | ||
113 | /* SoC core supply, no device */ | ||
114 | REGULATOR_SUPPLY("v-intcore", NULL), | ||
115 | /* USB Transceiver */ | ||
116 | REGULATOR_SUPPLY("vddulpivio18", "ab8500-usb.0"), | ||
117 | /* Handled by abx500 clk driver */ | ||
118 | REGULATOR_SUPPLY("v-intcore", "abx500-clk.0"), | ||
119 | }; | ||
120 | |||
121 | static struct regulator_consumer_supply ab8500_vana_consumers[] = { | ||
122 | /* DB8500 DSI */ | ||
123 | REGULATOR_SUPPLY("vdddsi1v2", "mcde"), | ||
124 | REGULATOR_SUPPLY("vdddsi1v2", "b2r2_core"), | ||
125 | REGULATOR_SUPPLY("vdddsi1v2", "b2r2_1_core"), | ||
126 | REGULATOR_SUPPLY("vdddsi1v2", "dsilink.0"), | ||
127 | REGULATOR_SUPPLY("vdddsi1v2", "dsilink.1"), | ||
128 | REGULATOR_SUPPLY("vdddsi1v2", "dsilink.2"), | ||
129 | /* DB8500 CSI */ | ||
130 | REGULATOR_SUPPLY("vddcsi1v2", "mmio_camera"), | ||
131 | }; | ||
132 | |||
133 | /* ab8500 regulator register initialization */ | ||
134 | static struct ab8500_regulator_reg_init ab8500_reg_init[] = { | ||
135 | /* | ||
136 | * VanaRequestCtrl = HP/LP depending on VxRequest | ||
137 | * VextSupply1RequestCtrl = HP/LP depending on VxRequest | ||
138 | */ | ||
139 | INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL2, 0xf0, 0x00), | ||
140 | /* | ||
141 | * VextSupply2RequestCtrl = HP/LP depending on VxRequest | ||
142 | * VextSupply3RequestCtrl = HP/LP depending on VxRequest | ||
143 | * Vaux1RequestCtrl = HP/LP depending on VxRequest | ||
144 | * Vaux2RequestCtrl = HP/LP depending on VxRequest | ||
145 | */ | ||
146 | INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL3, 0xff, 0x00), | ||
147 | /* | ||
148 | * Vaux3RequestCtrl = HP/LP depending on VxRequest | ||
149 | * SwHPReq = Control through SWValid disabled | ||
150 | */ | ||
151 | INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL4, 0x07, 0x00), | ||
152 | /* | ||
153 | * VanaSysClkReq1HPValid = disabled | ||
154 | * Vaux1SysClkReq1HPValid = disabled | ||
155 | * Vaux2SysClkReq1HPValid = disabled | ||
156 | * Vaux3SysClkReq1HPValid = disabled | ||
157 | */ | ||
158 | INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID1, 0xe8, 0x00), | ||
159 | /* | ||
160 | * VextSupply1SysClkReq1HPValid = disabled | ||
161 | * VextSupply2SysClkReq1HPValid = disabled | ||
162 | * VextSupply3SysClkReq1HPValid = SysClkReq1 controlled | ||
163 | */ | ||
164 | INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID2, 0x70, 0x40), | ||
165 | /* | ||
166 | * VanaHwHPReq1Valid = disabled | ||
167 | * Vaux1HwHPreq1Valid = disabled | ||
168 | * Vaux2HwHPReq1Valid = disabled | ||
169 | * Vaux3HwHPReqValid = disabled | ||
170 | */ | ||
171 | INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID1, 0xe8, 0x00), | ||
172 | /* | ||
173 | * VextSupply1HwHPReq1Valid = disabled | ||
174 | * VextSupply2HwHPReq1Valid = disabled | ||
175 | * VextSupply3HwHPReq1Valid = disabled | ||
176 | */ | ||
177 | INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID2, 0x07, 0x00), | ||
178 | /* | ||
179 | * VanaHwHPReq2Valid = disabled | ||
180 | * Vaux1HwHPReq2Valid = disabled | ||
181 | * Vaux2HwHPReq2Valid = disabled | ||
182 | * Vaux3HwHPReq2Valid = disabled | ||
183 | */ | ||
184 | INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID1, 0xe8, 0x00), | ||
185 | /* | ||
186 | * VextSupply1HwHPReq2Valid = disabled | ||
187 | * VextSupply2HwHPReq2Valid = disabled | ||
188 | * VextSupply3HwHPReq2Valid = HWReq2 controlled | ||
189 | */ | ||
190 | INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID2, 0x07, 0x04), | ||
191 | /* | ||
192 | * VanaSwHPReqValid = disabled | ||
193 | * Vaux1SwHPReqValid = disabled | ||
194 | */ | ||
195 | INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID1, 0xa0, 0x00), | ||
196 | /* | ||
197 | * Vaux2SwHPReqValid = disabled | ||
198 | * Vaux3SwHPReqValid = disabled | ||
199 | * VextSupply1SwHPReqValid = disabled | ||
200 | * VextSupply2SwHPReqValid = disabled | ||
201 | * VextSupply3SwHPReqValid = disabled | ||
202 | */ | ||
203 | INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID2, 0x1f, 0x00), | ||
204 | /* | ||
205 | * SysClkReq2Valid1 = SysClkReq2 controlled | ||
206 | * SysClkReq3Valid1 = disabled | ||
207 | * SysClkReq4Valid1 = SysClkReq4 controlled | ||
208 | * SysClkReq5Valid1 = disabled | ||
209 | * SysClkReq6Valid1 = SysClkReq6 controlled | ||
210 | * SysClkReq7Valid1 = disabled | ||
211 | * SysClkReq8Valid1 = disabled | ||
212 | */ | ||
213 | INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID1, 0xfe, 0x2a), | ||
214 | /* | ||
215 | * SysClkReq2Valid2 = disabled | ||
216 | * SysClkReq3Valid2 = disabled | ||
217 | * SysClkReq4Valid2 = disabled | ||
218 | * SysClkReq5Valid2 = disabled | ||
219 | * SysClkReq6Valid2 = SysClkReq6 controlled | ||
220 | * SysClkReq7Valid2 = disabled | ||
221 | * SysClkReq8Valid2 = disabled | ||
222 | */ | ||
223 | INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID2, 0xfe, 0x20), | ||
224 | /* | ||
225 | * VTVoutEna = disabled | ||
226 | * Vintcore12Ena = disabled | ||
227 | * Vintcore12Sel = 1.25 V | ||
228 | * Vintcore12LP = inactive (HP) | ||
229 | * VTVoutLP = inactive (HP) | ||
230 | */ | ||
231 | INIT_REGULATOR_REGISTER(AB8500_REGUMISC1, 0xfe, 0x10), | ||
232 | /* | ||
233 | * VaudioEna = disabled | ||
234 | * VdmicEna = disabled | ||
235 | * Vamic1Ena = disabled | ||
236 | * Vamic2Ena = disabled | ||
237 | */ | ||
238 | INIT_REGULATOR_REGISTER(AB8500_VAUDIOSUPPLY, 0x1e, 0x00), | ||
239 | /* | ||
240 | * Vamic1_dzout = high-Z when Vamic1 is disabled | ||
241 | * Vamic2_dzout = high-Z when Vamic2 is disabled | ||
242 | */ | ||
243 | INIT_REGULATOR_REGISTER(AB8500_REGUCTRL1VAMIC, 0x03, 0x00), | ||
244 | /* | ||
245 | * VPll = Hw controlled (NOTE! PRCMU bits) | ||
246 | * VanaRegu = force off | ||
247 | */ | ||
248 | INIT_REGULATOR_REGISTER(AB8500_VPLLVANAREGU, 0x0f, 0x02), | ||
249 | /* | ||
250 | * VrefDDREna = disabled | ||
251 | * VrefDDRSleepMode = inactive (no pulldown) | ||
252 | */ | ||
253 | INIT_REGULATOR_REGISTER(AB8500_VREFDDR, 0x03, 0x00), | ||
254 | /* | ||
255 | * VextSupply1Regu = force LP | ||
256 | * VextSupply2Regu = force OFF | ||
257 | * VextSupply3Regu = force HP (-> STBB2=LP and TPS=LP) | ||
258 | * ExtSupply2Bypass = ExtSupply12LPn ball is 0 when Ena is 0 | ||
259 | * ExtSupply3Bypass = ExtSupply3LPn ball is 0 when Ena is 0 | ||
260 | */ | ||
261 | INIT_REGULATOR_REGISTER(AB8500_EXTSUPPLYREGU, 0xff, 0x13), | ||
262 | /* | ||
263 | * Vaux1Regu = force HP | ||
264 | * Vaux2Regu = force off | ||
265 | */ | ||
266 | INIT_REGULATOR_REGISTER(AB8500_VAUX12REGU, 0x0f, 0x01), | ||
267 | /* | ||
268 | * Vaux3Regu = force off | ||
269 | */ | ||
270 | INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3REGU, 0x03, 0x00), | ||
271 | /* | ||
272 | * Vaux1Sel = 2.8 V | ||
273 | */ | ||
274 | INIT_REGULATOR_REGISTER(AB8500_VAUX1SEL, 0x0f, 0x0C), | ||
275 | /* | ||
276 | * Vaux2Sel = 2.9 V | ||
277 | */ | ||
278 | INIT_REGULATOR_REGISTER(AB8500_VAUX2SEL, 0x0f, 0x0d), | ||
279 | /* | ||
280 | * Vaux3Sel = 2.91 V | ||
281 | */ | ||
282 | INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3SEL, 0x07, 0x07), | ||
283 | /* | ||
284 | * VextSupply12LP = disabled (no LP) | ||
285 | */ | ||
286 | INIT_REGULATOR_REGISTER(AB8500_REGUCTRL2SPARE, 0x01, 0x00), | ||
287 | /* | ||
288 | * Vaux1Disch = short discharge time | ||
289 | * Vaux2Disch = short discharge time | ||
290 | * Vaux3Disch = short discharge time | ||
291 | * Vintcore12Disch = short discharge time | ||
292 | * VTVoutDisch = short discharge time | ||
293 | * VaudioDisch = short discharge time | ||
294 | */ | ||
295 | INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH, 0xfc, 0x00), | ||
296 | /* | ||
297 | * VanaDisch = short discharge time | ||
298 | * VdmicPullDownEna = pulldown disabled when Vdmic is disabled | ||
299 | * VdmicDisch = short discharge time | ||
300 | */ | ||
301 | INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH2, 0x16, 0x00), | ||
302 | }; | ||
303 | |||
304 | /* AB8500 regulators */ | ||
305 | static struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = { | ||
306 | /* supplies to the display/camera */ | ||
307 | [AB8500_LDO_AUX1] = { | ||
308 | .supply_regulator = "ab8500-ext-supply3", | ||
309 | .constraints = { | ||
310 | .name = "V-DISPLAY", | ||
311 | .min_uV = 2800000, | ||
312 | .max_uV = 3300000, | ||
313 | .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | | ||
314 | REGULATOR_CHANGE_STATUS, | ||
315 | .boot_on = 1, /* display is on at boot */ | ||
316 | }, | ||
317 | .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux1_consumers), | ||
318 | .consumer_supplies = ab8500_vaux1_consumers, | ||
319 | }, | ||
320 | /* supplies to the on-board eMMC */ | ||
321 | [AB8500_LDO_AUX2] = { | ||
322 | .supply_regulator = "ab8500-ext-supply3", | ||
323 | .constraints = { | ||
324 | .name = "V-eMMC1", | ||
325 | .min_uV = 1100000, | ||
326 | .max_uV = 3300000, | ||
327 | .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | | ||
328 | REGULATOR_CHANGE_STATUS | | ||
329 | REGULATOR_CHANGE_MODE, | ||
330 | .valid_modes_mask = REGULATOR_MODE_NORMAL | | ||
331 | REGULATOR_MODE_IDLE, | ||
332 | }, | ||
333 | .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux2_consumers), | ||
334 | .consumer_supplies = ab8500_vaux2_consumers, | ||
335 | }, | ||
336 | /* supply for VAUX3, supplies to SDcard slots */ | ||
337 | [AB8500_LDO_AUX3] = { | ||
338 | .supply_regulator = "ab8500-ext-supply3", | ||
339 | .constraints = { | ||
340 | .name = "V-MMC-SD", | ||
341 | .min_uV = 1100000, | ||
342 | .max_uV = 3300000, | ||
343 | .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | | ||
344 | REGULATOR_CHANGE_STATUS | | ||
345 | REGULATOR_CHANGE_MODE, | ||
346 | .valid_modes_mask = REGULATOR_MODE_NORMAL | | ||
347 | REGULATOR_MODE_IDLE, | ||
348 | }, | ||
349 | .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux3_consumers), | ||
350 | .consumer_supplies = ab8500_vaux3_consumers, | ||
351 | }, | ||
352 | /* supply for tvout, gpadc, TVOUT LDO */ | ||
353 | [AB8500_LDO_TVOUT] = { | ||
354 | .constraints = { | ||
355 | .name = "V-TVOUT", | ||
356 | .valid_ops_mask = REGULATOR_CHANGE_STATUS, | ||
357 | }, | ||
358 | .num_consumer_supplies = ARRAY_SIZE(ab8500_vtvout_consumers), | ||
359 | .consumer_supplies = ab8500_vtvout_consumers, | ||
360 | }, | ||
361 | /* supply for ab8500-vaudio, VAUDIO LDO */ | ||
362 | [AB8500_LDO_AUDIO] = { | ||
363 | .constraints = { | ||
364 | .name = "V-AUD", | ||
365 | .valid_ops_mask = REGULATOR_CHANGE_STATUS, | ||
366 | }, | ||
367 | .num_consumer_supplies = ARRAY_SIZE(ab8500_vaud_consumers), | ||
368 | .consumer_supplies = ab8500_vaud_consumers, | ||
369 | }, | ||
370 | /* supply for v-anamic1 VAMic1-LDO */ | ||
371 | [AB8500_LDO_ANAMIC1] = { | ||
372 | .constraints = { | ||
373 | .name = "V-AMIC1", | ||
374 | .valid_ops_mask = REGULATOR_CHANGE_STATUS, | ||
375 | }, | ||
376 | .num_consumer_supplies = ARRAY_SIZE(ab8500_vamic1_consumers), | ||
377 | .consumer_supplies = ab8500_vamic1_consumers, | ||
378 | }, | ||
379 | /* supply for v-amic2, VAMIC2 LDO, reuse constants for AMIC1 */ | ||
380 | [AB8500_LDO_ANAMIC2] = { | ||
381 | .constraints = { | ||
382 | .name = "V-AMIC2", | ||
383 | .valid_ops_mask = REGULATOR_CHANGE_STATUS, | ||
384 | }, | ||
385 | .num_consumer_supplies = ARRAY_SIZE(ab8500_vamic2_consumers), | ||
386 | .consumer_supplies = ab8500_vamic2_consumers, | ||
387 | }, | ||
388 | /* supply for v-dmic, VDMIC LDO */ | ||
389 | [AB8500_LDO_DMIC] = { | ||
390 | .constraints = { | ||
391 | .name = "V-DMIC", | ||
392 | .valid_ops_mask = REGULATOR_CHANGE_STATUS, | ||
393 | }, | ||
394 | .num_consumer_supplies = ARRAY_SIZE(ab8500_vdmic_consumers), | ||
395 | .consumer_supplies = ab8500_vdmic_consumers, | ||
396 | }, | ||
397 | /* supply for v-intcore12, VINTCORE12 LDO */ | ||
398 | [AB8500_LDO_INTCORE] = { | ||
399 | .constraints = { | ||
400 | .name = "V-INTCORE", | ||
401 | .min_uV = 1250000, | ||
402 | .max_uV = 1350000, | ||
403 | .input_uV = 1800000, | ||
404 | .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | | ||
405 | REGULATOR_CHANGE_STATUS | | ||
406 | REGULATOR_CHANGE_MODE | | ||
407 | REGULATOR_CHANGE_DRMS, | ||
408 | .valid_modes_mask = REGULATOR_MODE_NORMAL | | ||
409 | REGULATOR_MODE_IDLE, | ||
410 | }, | ||
411 | .num_consumer_supplies = ARRAY_SIZE(ab8500_vintcore_consumers), | ||
412 | .consumer_supplies = ab8500_vintcore_consumers, | ||
413 | }, | ||
414 | /* supply for U8500 CSI-DSI, VANA LDO */ | ||
415 | [AB8500_LDO_ANA] = { | ||
416 | .constraints = { | ||
417 | .name = "V-CSI-DSI", | ||
418 | .valid_ops_mask = REGULATOR_CHANGE_STATUS, | ||
419 | }, | ||
420 | .num_consumer_supplies = ARRAY_SIZE(ab8500_vana_consumers), | ||
421 | .consumer_supplies = ab8500_vana_consumers, | ||
422 | }, | ||
423 | }; | ||
424 | |||
425 | /* supply for VextSupply3 */ | ||
426 | static struct regulator_consumer_supply ab8500_ext_supply3_consumers[] = { | ||
427 | /* SIM supply for 3 V SIM cards */ | ||
428 | REGULATOR_SUPPLY("vinvsim", "sim-detect.0"), | ||
429 | }; | ||
430 | |||
431 | /* | ||
432 | * AB8500 external regulators | ||
433 | */ | ||
434 | static struct regulator_init_data ab8500_ext_regulators[] = { | ||
435 | /* fixed Vbat supplies VSMPS1_EXT_1V8 */ | ||
436 | [AB8500_EXT_SUPPLY1] = { | ||
437 | .constraints = { | ||
438 | .name = "ab8500-ext-supply1", | ||
439 | .min_uV = 1800000, | ||
440 | .max_uV = 1800000, | ||
441 | .initial_mode = REGULATOR_MODE_IDLE, | ||
442 | .boot_on = 1, | ||
443 | .always_on = 1, | ||
444 | }, | ||
445 | }, | ||
446 | /* fixed Vbat supplies VSMPS2_EXT_1V36 and VSMPS5_EXT_1V15 */ | ||
447 | [AB8500_EXT_SUPPLY2] = { | ||
448 | .constraints = { | ||
449 | .name = "ab8500-ext-supply2", | ||
450 | .min_uV = 1360000, | ||
451 | .max_uV = 1360000, | ||
452 | }, | ||
453 | }, | ||
454 | /* fixed Vbat supplies VSMPS3_EXT_3V4 and VSMPS4_EXT_3V4 */ | ||
455 | [AB8500_EXT_SUPPLY3] = { | ||
456 | .constraints = { | ||
457 | .name = "ab8500-ext-supply3", | ||
458 | .min_uV = 3400000, | ||
459 | .max_uV = 3400000, | ||
460 | .valid_ops_mask = REGULATOR_CHANGE_STATUS, | ||
461 | .boot_on = 1, | ||
462 | }, | ||
463 | .num_consumer_supplies = | ||
464 | ARRAY_SIZE(ab8500_ext_supply3_consumers), | ||
465 | .consumer_supplies = ab8500_ext_supply3_consumers, | ||
466 | }, | ||
467 | }; | ||
468 | |||
469 | static struct ab8500_regulator_platform_data ab8500_regulator_plat_data = { | ||
470 | .reg_init = ab8500_reg_init, | ||
471 | .num_reg_init = ARRAY_SIZE(ab8500_reg_init), | ||
472 | .regulator = ab8500_regulators, | ||
473 | .num_regulator = ARRAY_SIZE(ab8500_regulators), | ||
474 | .ext_regulator = ab8500_ext_regulators, | ||
475 | .num_ext_regulator = ARRAY_SIZE(ab8500_ext_regulators), | ||
476 | }; | ||
477 | |||
28 | /** | 478 | /** |
29 | * struct ab8500_ext_regulator_info - ab8500 regulator information | 479 | * struct ab8500_ext_regulator_info - ab8500 regulator information |
30 | * @dev: device pointer | 480 | * @dev: device pointer |
@@ -344,8 +794,7 @@ static struct of_regulator_match ab8500_ext_regulator_match[] = { | |||
344 | static int ab8500_ext_regulator_probe(struct platform_device *pdev) | 794 | static int ab8500_ext_regulator_probe(struct platform_device *pdev) |
345 | { | 795 | { |
346 | struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); | 796 | struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); |
347 | struct ab8500_platform_data *ppdata; | 797 | struct ab8500_regulator_platform_data *pdata = &ab8500_regulator_plat_data; |
348 | struct ab8500_regulator_platform_data *pdata; | ||
349 | struct device_node *np = pdev->dev.of_node; | 798 | struct device_node *np = pdev->dev.of_node; |
350 | struct regulator_config config = { }; | 799 | struct regulator_config config = { }; |
351 | int i, err; | 800 | int i, err; |
@@ -366,18 +815,6 @@ static int ab8500_ext_regulator_probe(struct platform_device *pdev) | |||
366 | return -EINVAL; | 815 | return -EINVAL; |
367 | } | 816 | } |
368 | 817 | ||
369 | ppdata = dev_get_platdata(ab8500->dev); | ||
370 | if (!ppdata) { | ||
371 | dev_err(&pdev->dev, "null parent pdata\n"); | ||
372 | return -EINVAL; | ||
373 | } | ||
374 | |||
375 | pdata = ppdata->regulator; | ||
376 | if (!pdata) { | ||
377 | dev_err(&pdev->dev, "null pdata\n"); | ||
378 | return -EINVAL; | ||
379 | } | ||
380 | |||
381 | /* make sure the platform data has the correct size */ | 818 | /* make sure the platform data has the correct size */ |
382 | if (pdata->num_ext_regulator != ARRAY_SIZE(ab8500_ext_regulator_info)) { | 819 | if (pdata->num_ext_regulator != ARRAY_SIZE(ab8500_ext_regulator_info)) { |
383 | dev_err(&pdev->dev, "Configuration error: size mismatch.\n"); | 820 | dev_err(&pdev->dev, "Configuration error: size mismatch.\n"); |
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 0b2733db0e9e..4be1b8c21f6f 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig | |||
@@ -12,8 +12,22 @@ menuconfig RESET_CONTROLLER | |||
12 | 12 | ||
13 | If unsure, say no. | 13 | If unsure, say no. |
14 | 14 | ||
15 | if RESET_CONTROLLER | ||
16 | |||
15 | config RESET_OXNAS | 17 | config RESET_OXNAS |
16 | bool | 18 | bool |
17 | 19 | ||
20 | config TI_SYSCON_RESET | ||
21 | tristate "TI SYSCON Reset Driver" | ||
22 | depends on HAS_IOMEM | ||
23 | select MFD_SYSCON | ||
24 | help | ||
25 | This enables the reset driver support for TI devices with | ||
26 | memory-mapped reset registers as part of a syscon device node. If | ||
27 | you wish to use the reset framework for such memory-mapped devices, | ||
28 | say Y here. Otherwise, say N. | ||
29 | |||
18 | source "drivers/reset/sti/Kconfig" | 30 | source "drivers/reset/sti/Kconfig" |
19 | source "drivers/reset/hisilicon/Kconfig" | 31 | source "drivers/reset/hisilicon/Kconfig" |
32 | |||
33 | endif | ||
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index f173fc3847b4..5d65a93d3c43 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile | |||
@@ -3,9 +3,11 @@ obj-$(CONFIG_ARCH_LPC18XX) += reset-lpc18xx.o | |||
3 | obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o | 3 | obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o |
4 | obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o | 4 | obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o |
5 | obj-$(CONFIG_MACH_PISTACHIO) += reset-pistachio.o | 5 | obj-$(CONFIG_MACH_PISTACHIO) += reset-pistachio.o |
6 | obj-$(CONFIG_ARCH_MESON) += reset-meson.o | ||
6 | obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o | 7 | obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o |
7 | obj-$(CONFIG_ARCH_STI) += sti/ | 8 | obj-$(CONFIG_ARCH_STI) += sti/ |
8 | obj-$(CONFIG_ARCH_HISI) += hisilicon/ | 9 | obj-$(CONFIG_ARCH_HISI) += hisilicon/ |
9 | obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o | 10 | obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o |
10 | obj-$(CONFIG_ATH79) += reset-ath79.o | 11 | obj-$(CONFIG_ATH79) += reset-ath79.o |
11 | obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o | 12 | obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o |
13 | obj-$(CONFIG_TI_SYSCON_RESET) += reset-ti-syscon.o | ||
diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 72b32bd15549..395dc9ce492e 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c | |||
@@ -93,6 +93,43 @@ void reset_controller_unregister(struct reset_controller_dev *rcdev) | |||
93 | } | 93 | } |
94 | EXPORT_SYMBOL_GPL(reset_controller_unregister); | 94 | EXPORT_SYMBOL_GPL(reset_controller_unregister); |
95 | 95 | ||
96 | static void devm_reset_controller_release(struct device *dev, void *res) | ||
97 | { | ||
98 | reset_controller_unregister(*(struct reset_controller_dev **)res); | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * devm_reset_controller_register - resource managed reset_controller_register() | ||
103 | * @dev: device that is registering this reset controller | ||
104 | * @rcdev: a pointer to the initialized reset controller device | ||
105 | * | ||
106 | * Managed reset_controller_register(). For reset controllers registered by | ||
107 | * this function, reset_controller_unregister() is automatically called on | ||
108 | * driver detach. See reset_controller_register() for more information. | ||
109 | */ | ||
110 | int devm_reset_controller_register(struct device *dev, | ||
111 | struct reset_controller_dev *rcdev) | ||
112 | { | ||
113 | struct reset_controller_dev **rcdevp; | ||
114 | int ret; | ||
115 | |||
116 | rcdevp = devres_alloc(devm_reset_controller_release, sizeof(*rcdevp), | ||
117 | GFP_KERNEL); | ||
118 | if (!rcdevp) | ||
119 | return -ENOMEM; | ||
120 | |||
121 | ret = reset_controller_register(rcdev); | ||
122 | if (!ret) { | ||
123 | *rcdevp = rcdev; | ||
124 | devres_add(dev, rcdevp); | ||
125 | } else { | ||
126 | devres_free(rcdevp); | ||
127 | } | ||
128 | |||
129 | return ret; | ||
130 | } | ||
131 | EXPORT_SYMBOL_GPL(devm_reset_controller_register); | ||
132 | |||
96 | /** | 133 | /** |
97 | * reset_control_reset - reset the controlled device | 134 | * reset_control_reset - reset the controlled device |
98 | * @rstc: reset controller | 135 | * @rstc: reset controller |
diff --git a/drivers/reset/hisilicon/hi6220_reset.c b/drivers/reset/hisilicon/hi6220_reset.c index 8f55fd4a2630..35ce53edabf9 100644 --- a/drivers/reset/hisilicon/hi6220_reset.c +++ b/drivers/reset/hisilicon/hi6220_reset.c | |||
@@ -1,7 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * Hisilicon Hi6220 reset controller driver | 2 | * Hisilicon Hi6220 reset controller driver |
3 | * | 3 | * |
4 | * Copyright (c) 2015 Hisilicon Limited. | 4 | * Copyright (c) 2016 Linaro Limited. |
5 | * Copyright (c) 2015-2016 Hisilicon Limited. | ||
5 | * | 6 | * |
6 | * Author: Feng Chen <puck.chen@hisilicon.com> | 7 | * Author: Feng Chen <puck.chen@hisilicon.com> |
7 | * | 8 | * |
@@ -15,81 +16,130 @@ | |||
15 | #include <linux/module.h> | 16 | #include <linux/module.h> |
16 | #include <linux/bitops.h> | 17 | #include <linux/bitops.h> |
17 | #include <linux/of.h> | 18 | #include <linux/of.h> |
19 | #include <linux/of_device.h> | ||
20 | #include <linux/regmap.h> | ||
21 | #include <linux/mfd/syscon.h> | ||
18 | #include <linux/reset-controller.h> | 22 | #include <linux/reset-controller.h> |
19 | #include <linux/reset.h> | 23 | #include <linux/reset.h> |
20 | #include <linux/platform_device.h> | 24 | #include <linux/platform_device.h> |
21 | 25 | ||
22 | #define ASSERT_OFFSET 0x300 | 26 | #define PERIPH_ASSERT_OFFSET 0x300 |
23 | #define DEASSERT_OFFSET 0x304 | 27 | #define PERIPH_DEASSERT_OFFSET 0x304 |
24 | #define MAX_INDEX 0x509 | 28 | #define PERIPH_MAX_INDEX 0x509 |
29 | |||
30 | #define SC_MEDIA_RSTEN 0x052C | ||
31 | #define SC_MEDIA_RSTDIS 0x0530 | ||
32 | #define MEDIA_MAX_INDEX 8 | ||
25 | 33 | ||
26 | #define to_reset_data(x) container_of(x, struct hi6220_reset_data, rc_dev) | 34 | #define to_reset_data(x) container_of(x, struct hi6220_reset_data, rc_dev) |
27 | 35 | ||
36 | enum hi6220_reset_ctrl_type { | ||
37 | PERIPHERAL, | ||
38 | MEDIA, | ||
39 | }; | ||
40 | |||
28 | struct hi6220_reset_data { | 41 | struct hi6220_reset_data { |
29 | void __iomem *assert_base; | 42 | struct reset_controller_dev rc_dev; |
30 | void __iomem *deassert_base; | 43 | struct regmap *regmap; |
31 | struct reset_controller_dev rc_dev; | ||
32 | }; | 44 | }; |
33 | 45 | ||
34 | static int hi6220_reset_assert(struct reset_controller_dev *rc_dev, | 46 | static int hi6220_peripheral_assert(struct reset_controller_dev *rc_dev, |
35 | unsigned long idx) | 47 | unsigned long idx) |
36 | { | 48 | { |
37 | struct hi6220_reset_data *data = to_reset_data(rc_dev); | 49 | struct hi6220_reset_data *data = to_reset_data(rc_dev); |
50 | struct regmap *regmap = data->regmap; | ||
51 | u32 bank = idx >> 8; | ||
52 | u32 offset = idx & 0xff; | ||
53 | u32 reg = PERIPH_ASSERT_OFFSET + bank * 0x10; | ||
38 | 54 | ||
39 | int bank = idx >> 8; | 55 | return regmap_write(regmap, reg, BIT(offset)); |
40 | int offset = idx & 0xff; | 56 | } |
41 | 57 | ||
42 | writel(BIT(offset), data->assert_base + (bank * 0x10)); | 58 | static int hi6220_peripheral_deassert(struct reset_controller_dev *rc_dev, |
59 | unsigned long idx) | ||
60 | { | ||
61 | struct hi6220_reset_data *data = to_reset_data(rc_dev); | ||
62 | struct regmap *regmap = data->regmap; | ||
63 | u32 bank = idx >> 8; | ||
64 | u32 offset = idx & 0xff; | ||
65 | u32 reg = PERIPH_DEASSERT_OFFSET + bank * 0x10; | ||
43 | 66 | ||
44 | return 0; | 67 | return regmap_write(regmap, reg, BIT(offset)); |
45 | } | 68 | } |
46 | 69 | ||
47 | static int hi6220_reset_deassert(struct reset_controller_dev *rc_dev, | 70 | static const struct reset_control_ops hi6220_peripheral_reset_ops = { |
48 | unsigned long idx) | 71 | .assert = hi6220_peripheral_assert, |
72 | .deassert = hi6220_peripheral_deassert, | ||
73 | }; | ||
74 | |||
75 | static int hi6220_media_assert(struct reset_controller_dev *rc_dev, | ||
76 | unsigned long idx) | ||
49 | { | 77 | { |
50 | struct hi6220_reset_data *data = to_reset_data(rc_dev); | 78 | struct hi6220_reset_data *data = to_reset_data(rc_dev); |
79 | struct regmap *regmap = data->regmap; | ||
51 | 80 | ||
52 | int bank = idx >> 8; | 81 | return regmap_write(regmap, SC_MEDIA_RSTEN, BIT(idx)); |
53 | int offset = idx & 0xff; | 82 | } |
54 | 83 | ||
55 | writel(BIT(offset), data->deassert_base + (bank * 0x10)); | 84 | static int hi6220_media_deassert(struct reset_controller_dev *rc_dev, |
85 | unsigned long idx) | ||
86 | { | ||
87 | struct hi6220_reset_data *data = to_reset_data(rc_dev); | ||
88 | struct regmap *regmap = data->regmap; | ||
56 | 89 | ||
57 | return 0; | 90 | return regmap_write(regmap, SC_MEDIA_RSTDIS, BIT(idx)); |
58 | } | 91 | } |
59 | 92 | ||
60 | static const struct reset_control_ops hi6220_reset_ops = { | 93 | static const struct reset_control_ops hi6220_media_reset_ops = { |
61 | .assert = hi6220_reset_assert, | 94 | .assert = hi6220_media_assert, |
62 | .deassert = hi6220_reset_deassert, | 95 | .deassert = hi6220_media_deassert, |
63 | }; | 96 | }; |
64 | 97 | ||
65 | static int hi6220_reset_probe(struct platform_device *pdev) | 98 | static int hi6220_reset_probe(struct platform_device *pdev) |
66 | { | 99 | { |
100 | struct device_node *np = pdev->dev.of_node; | ||
101 | struct device *dev = &pdev->dev; | ||
102 | enum hi6220_reset_ctrl_type type; | ||
67 | struct hi6220_reset_data *data; | 103 | struct hi6220_reset_data *data; |
68 | struct resource *res; | 104 | struct regmap *regmap; |
69 | void __iomem *src_base; | ||
70 | 105 | ||
71 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | 106 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); |
72 | if (!data) | 107 | if (!data) |
73 | return -ENOMEM; | 108 | return -ENOMEM; |
74 | 109 | ||
75 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 110 | type = (enum hi6220_reset_ctrl_type)of_device_get_match_data(dev); |
76 | src_base = devm_ioremap_resource(&pdev->dev, res); | 111 | |
77 | if (IS_ERR(src_base)) | 112 | regmap = syscon_node_to_regmap(np); |
78 | return PTR_ERR(src_base); | 113 | if (IS_ERR(regmap)) { |
114 | dev_err(dev, "failed to get reset controller regmap\n"); | ||
115 | return PTR_ERR(regmap); | ||
116 | } | ||
79 | 117 | ||
80 | data->assert_base = src_base + ASSERT_OFFSET; | 118 | data->regmap = regmap; |
81 | data->deassert_base = src_base + DEASSERT_OFFSET; | 119 | data->rc_dev.of_node = np; |
82 | data->rc_dev.nr_resets = MAX_INDEX; | 120 | if (type == MEDIA) { |
83 | data->rc_dev.ops = &hi6220_reset_ops; | 121 | data->rc_dev.ops = &hi6220_media_reset_ops; |
84 | data->rc_dev.of_node = pdev->dev.of_node; | 122 | data->rc_dev.nr_resets = MEDIA_MAX_INDEX; |
123 | } else { | ||
124 | data->rc_dev.ops = &hi6220_peripheral_reset_ops; | ||
125 | data->rc_dev.nr_resets = PERIPH_MAX_INDEX; | ||
126 | } | ||
85 | 127 | ||
86 | return reset_controller_register(&data->rc_dev); | 128 | return reset_controller_register(&data->rc_dev); |
87 | } | 129 | } |
88 | 130 | ||
89 | static const struct of_device_id hi6220_reset_match[] = { | 131 | static const struct of_device_id hi6220_reset_match[] = { |
90 | { .compatible = "hisilicon,hi6220-sysctrl" }, | 132 | { |
91 | { }, | 133 | .compatible = "hisilicon,hi6220-sysctrl", |
134 | .data = (void *)PERIPHERAL, | ||
135 | }, | ||
136 | { | ||
137 | .compatible = "hisilicon,hi6220-mediactrl", | ||
138 | .data = (void *)MEDIA, | ||
139 | }, | ||
140 | { /* sentinel */ }, | ||
92 | }; | 141 | }; |
142 | MODULE_DEVICE_TABLE(of, hi6220_reset_match); | ||
93 | 143 | ||
94 | static struct platform_driver hi6220_reset_driver = { | 144 | static struct platform_driver hi6220_reset_driver = { |
95 | .probe = hi6220_reset_probe, | 145 | .probe = hi6220_reset_probe, |
diff --git a/drivers/reset/reset-ath79.c b/drivers/reset/reset-ath79.c index ccb940a8d9fb..16d410cd6146 100644 --- a/drivers/reset/reset-ath79.c +++ b/drivers/reset/reset-ath79.c | |||
@@ -112,7 +112,7 @@ static int ath79_reset_probe(struct platform_device *pdev) | |||
112 | ath79_reset->rcdev.of_reset_n_cells = 1; | 112 | ath79_reset->rcdev.of_reset_n_cells = 1; |
113 | ath79_reset->rcdev.nr_resets = 32; | 113 | ath79_reset->rcdev.nr_resets = 32; |
114 | 114 | ||
115 | err = reset_controller_register(&ath79_reset->rcdev); | 115 | err = devm_reset_controller_register(&pdev->dev, &ath79_reset->rcdev); |
116 | if (err) | 116 | if (err) |
117 | return err; | 117 | return err; |
118 | 118 | ||
@@ -131,7 +131,6 @@ static int ath79_reset_remove(struct platform_device *pdev) | |||
131 | struct ath79_reset *ath79_reset = platform_get_drvdata(pdev); | 131 | struct ath79_reset *ath79_reset = platform_get_drvdata(pdev); |
132 | 132 | ||
133 | unregister_restart_handler(&ath79_reset->restart_nb); | 133 | unregister_restart_handler(&ath79_reset->restart_nb); |
134 | reset_controller_unregister(&ath79_reset->rcdev); | ||
135 | 134 | ||
136 | return 0; | 135 | return 0; |
137 | } | 136 | } |
diff --git a/drivers/reset/reset-meson.c b/drivers/reset/reset-meson.c new file mode 100644 index 000000000000..c32f11a30c5f --- /dev/null +++ b/drivers/reset/reset-meson.c | |||
@@ -0,0 +1,136 @@ | |||
1 | /* | ||
2 | * This file is provided under a dual BSD/GPLv2 license. When using or | ||
3 | * redistributing this file, you may do so under either license. | ||
4 | * | ||
5 | * GPL LICENSE SUMMARY | ||
6 | * | ||
7 | * Copyright (c) 2016 BayLibre, SAS. | ||
8 | * Author: Neil Armstrong <narmstrong@baylibre.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of version 2 of the GNU General Public License as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||
21 | * The full GNU General Public License is included in this distribution | ||
22 | * in the file called COPYING. | ||
23 | * | ||
24 | * BSD LICENSE | ||
25 | * | ||
26 | * Copyright (c) 2016 BayLibre, SAS. | ||
27 | * Author: Neil Armstrong <narmstrong@baylibre.com> | ||
28 | * | ||
29 | * Redistribution and use in source and binary forms, with or without | ||
30 | * modification, are permitted provided that the following conditions | ||
31 | * are met: | ||
32 | * | ||
33 | * * Redistributions of source code must retain the above copyright | ||
34 | * notice, this list of conditions and the following disclaimer. | ||
35 | * * Redistributions in binary form must reproduce the above copyright | ||
36 | * notice, this list of conditions and the following disclaimer in | ||
37 | * the documentation and/or other materials provided with the | ||
38 | * distribution. | ||
39 | * * Neither the name of Intel Corporation nor the names of its | ||
40 | * contributors may be used to endorse or promote products derived | ||
41 | * from this software without specific prior written permission. | ||
42 | * | ||
43 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
44 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
45 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
46 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
47 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
48 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
49 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
50 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
51 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
52 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
53 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
54 | */ | ||
55 | #include <linux/err.h> | ||
56 | #include <linux/module.h> | ||
57 | #include <linux/io.h> | ||
58 | #include <linux/of.h> | ||
59 | #include <linux/platform_device.h> | ||
60 | #include <linux/reset-controller.h> | ||
61 | #include <linux/slab.h> | ||
62 | #include <linux/types.h> | ||
63 | |||
64 | #define REG_COUNT 8 | ||
65 | #define BITS_PER_REG 32 | ||
66 | |||
67 | struct meson_reset { | ||
68 | void __iomem *reg_base; | ||
69 | struct reset_controller_dev rcdev; | ||
70 | }; | ||
71 | |||
72 | static int meson_reset_reset(struct reset_controller_dev *rcdev, | ||
73 | unsigned long id) | ||
74 | { | ||
75 | struct meson_reset *data = | ||
76 | container_of(rcdev, struct meson_reset, rcdev); | ||
77 | unsigned int bank = id / BITS_PER_REG; | ||
78 | unsigned int offset = id % BITS_PER_REG; | ||
79 | void __iomem *reg_addr = data->reg_base + (bank << 2); | ||
80 | |||
81 | if (bank >= REG_COUNT) | ||
82 | return -EINVAL; | ||
83 | |||
84 | writel(BIT(offset), reg_addr); | ||
85 | |||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static const struct reset_control_ops meson_reset_ops = { | ||
90 | .reset = meson_reset_reset, | ||
91 | }; | ||
92 | |||
93 | static const struct of_device_id meson_reset_dt_ids[] = { | ||
94 | { .compatible = "amlogic,meson8b-reset", }, | ||
95 | { .compatible = "amlogic,meson-gxbb-reset", }, | ||
96 | { /* sentinel */ }, | ||
97 | }; | ||
98 | MODULE_DEVICE_TABLE(of, meson_reset_dt_ids); | ||
99 | |||
100 | static int meson_reset_probe(struct platform_device *pdev) | ||
101 | { | ||
102 | struct meson_reset *data; | ||
103 | struct resource *res; | ||
104 | |||
105 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | ||
106 | if (!data) | ||
107 | return -ENOMEM; | ||
108 | |||
109 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
110 | data->reg_base = devm_ioremap_resource(&pdev->dev, res); | ||
111 | if (IS_ERR(data->reg_base)) | ||
112 | return PTR_ERR(data->reg_base); | ||
113 | |||
114 | platform_set_drvdata(pdev, data); | ||
115 | |||
116 | data->rcdev.owner = THIS_MODULE; | ||
117 | data->rcdev.nr_resets = REG_COUNT * BITS_PER_REG; | ||
118 | data->rcdev.ops = &meson_reset_ops; | ||
119 | data->rcdev.of_node = pdev->dev.of_node; | ||
120 | |||
121 | return devm_reset_controller_register(&pdev->dev, &data->rcdev); | ||
122 | } | ||
123 | |||
124 | static struct platform_driver meson_reset_driver = { | ||
125 | .probe = meson_reset_probe, | ||
126 | .driver = { | ||
127 | .name = "meson_reset", | ||
128 | .of_match_table = meson_reset_dt_ids, | ||
129 | }, | ||
130 | }; | ||
131 | |||
132 | module_platform_driver(meson_reset_driver); | ||
133 | |||
134 | MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); | ||
135 | MODULE_DESCRIPTION("Amlogic Meson Reset Controller driver"); | ||
136 | MODULE_LICENSE("Dual BSD/GPL"); | ||
diff --git a/drivers/reset/reset-oxnas.c b/drivers/reset/reset-oxnas.c index c60fb2dace3e..944980572f79 100644 --- a/drivers/reset/reset-oxnas.c +++ b/drivers/reset/reset-oxnas.c | |||
@@ -112,21 +112,11 @@ static int oxnas_reset_probe(struct platform_device *pdev) | |||
112 | data->rcdev.ops = &oxnas_reset_ops; | 112 | data->rcdev.ops = &oxnas_reset_ops; |
113 | data->rcdev.of_node = pdev->dev.of_node; | 113 | data->rcdev.of_node = pdev->dev.of_node; |
114 | 114 | ||
115 | return reset_controller_register(&data->rcdev); | 115 | return devm_reset_controller_register(&pdev->dev, &data->rcdev); |
116 | } | ||
117 | |||
118 | static int oxnas_reset_remove(struct platform_device *pdev) | ||
119 | { | ||
120 | struct oxnas_reset *data = platform_get_drvdata(pdev); | ||
121 | |||
122 | reset_controller_unregister(&data->rcdev); | ||
123 | |||
124 | return 0; | ||
125 | } | 116 | } |
126 | 117 | ||
127 | static struct platform_driver oxnas_reset_driver = { | 118 | static struct platform_driver oxnas_reset_driver = { |
128 | .probe = oxnas_reset_probe, | 119 | .probe = oxnas_reset_probe, |
129 | .remove = oxnas_reset_remove, | ||
130 | .driver = { | 120 | .driver = { |
131 | .name = "oxnas-reset", | 121 | .name = "oxnas-reset", |
132 | .of_match_table = oxnas_reset_dt_ids, | 122 | .of_match_table = oxnas_reset_dt_ids, |
diff --git a/drivers/reset/reset-pistachio.c b/drivers/reset/reset-pistachio.c index 72a97a15a4c8..bbc4c06dd33b 100644 --- a/drivers/reset/reset-pistachio.c +++ b/drivers/reset/reset-pistachio.c | |||
@@ -121,16 +121,7 @@ static int pistachio_reset_probe(struct platform_device *pdev) | |||
121 | rd->rcdev.ops = &pistachio_reset_ops; | 121 | rd->rcdev.ops = &pistachio_reset_ops; |
122 | rd->rcdev.of_node = np; | 122 | rd->rcdev.of_node = np; |
123 | 123 | ||
124 | return reset_controller_register(&rd->rcdev); | 124 | return devm_reset_controller_register(dev, &rd->rcdev); |
125 | } | ||
126 | |||
127 | static int pistachio_reset_remove(struct platform_device *pdev) | ||
128 | { | ||
129 | struct pistachio_reset_data *data = platform_get_drvdata(pdev); | ||
130 | |||
131 | reset_controller_unregister(&data->rcdev); | ||
132 | |||
133 | return 0; | ||
134 | } | 125 | } |
135 | 126 | ||
136 | static const struct of_device_id pistachio_reset_dt_ids[] = { | 127 | static const struct of_device_id pistachio_reset_dt_ids[] = { |
@@ -141,7 +132,6 @@ MODULE_DEVICE_TABLE(of, pistachio_reset_dt_ids); | |||
141 | 132 | ||
142 | static struct platform_driver pistachio_reset_driver = { | 133 | static struct platform_driver pistachio_reset_driver = { |
143 | .probe = pistachio_reset_probe, | 134 | .probe = pistachio_reset_probe, |
144 | .remove = pistachio_reset_remove, | ||
145 | .driver = { | 135 | .driver = { |
146 | .name = "pistachio-reset", | 136 | .name = "pistachio-reset", |
147 | .of_match_table = pistachio_reset_dt_ids, | 137 | .of_match_table = pistachio_reset_dt_ids, |
diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c index cd05a7032b17..12add9b0fa49 100644 --- a/drivers/reset/reset-socfpga.c +++ b/drivers/reset/reset-socfpga.c | |||
@@ -134,16 +134,7 @@ static int socfpga_reset_probe(struct platform_device *pdev) | |||
134 | data->rcdev.ops = &socfpga_reset_ops; | 134 | data->rcdev.ops = &socfpga_reset_ops; |
135 | data->rcdev.of_node = pdev->dev.of_node; | 135 | data->rcdev.of_node = pdev->dev.of_node; |
136 | 136 | ||
137 | return reset_controller_register(&data->rcdev); | 137 | return devm_reset_controller_register(dev, &data->rcdev); |
138 | } | ||
139 | |||
140 | static int socfpga_reset_remove(struct platform_device *pdev) | ||
141 | { | ||
142 | struct socfpga_reset_data *data = platform_get_drvdata(pdev); | ||
143 | |||
144 | reset_controller_unregister(&data->rcdev); | ||
145 | |||
146 | return 0; | ||
147 | } | 138 | } |
148 | 139 | ||
149 | static const struct of_device_id socfpga_reset_dt_ids[] = { | 140 | static const struct of_device_id socfpga_reset_dt_ids[] = { |
@@ -153,7 +144,6 @@ static const struct of_device_id socfpga_reset_dt_ids[] = { | |||
153 | 144 | ||
154 | static struct platform_driver socfpga_reset_driver = { | 145 | static struct platform_driver socfpga_reset_driver = { |
155 | .probe = socfpga_reset_probe, | 146 | .probe = socfpga_reset_probe, |
156 | .remove = socfpga_reset_remove, | ||
157 | .driver = { | 147 | .driver = { |
158 | .name = "socfpga-reset", | 148 | .name = "socfpga-reset", |
159 | .of_match_table = socfpga_reset_dt_ids, | 149 | .of_match_table = socfpga_reset_dt_ids, |
diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c index 677f86555212..3080190b3f90 100644 --- a/drivers/reset/reset-sunxi.c +++ b/drivers/reset/reset-sunxi.c | |||
@@ -165,21 +165,11 @@ static int sunxi_reset_probe(struct platform_device *pdev) | |||
165 | data->rcdev.ops = &sunxi_reset_ops; | 165 | data->rcdev.ops = &sunxi_reset_ops; |
166 | data->rcdev.of_node = pdev->dev.of_node; | 166 | data->rcdev.of_node = pdev->dev.of_node; |
167 | 167 | ||
168 | return reset_controller_register(&data->rcdev); | 168 | return devm_reset_controller_register(&pdev->dev, &data->rcdev); |
169 | } | ||
170 | |||
171 | static int sunxi_reset_remove(struct platform_device *pdev) | ||
172 | { | ||
173 | struct sunxi_reset_data *data = platform_get_drvdata(pdev); | ||
174 | |||
175 | reset_controller_unregister(&data->rcdev); | ||
176 | |||
177 | return 0; | ||
178 | } | 169 | } |
179 | 170 | ||
180 | static struct platform_driver sunxi_reset_driver = { | 171 | static struct platform_driver sunxi_reset_driver = { |
181 | .probe = sunxi_reset_probe, | 172 | .probe = sunxi_reset_probe, |
182 | .remove = sunxi_reset_remove, | ||
183 | .driver = { | 173 | .driver = { |
184 | .name = "sunxi-reset", | 174 | .name = "sunxi-reset", |
185 | .of_match_table = sunxi_reset_dt_ids, | 175 | .of_match_table = sunxi_reset_dt_ids, |
diff --git a/drivers/reset/reset-ti-syscon.c b/drivers/reset/reset-ti-syscon.c new file mode 100644 index 000000000000..47f0ffd3b013 --- /dev/null +++ b/drivers/reset/reset-ti-syscon.c | |||
@@ -0,0 +1,237 @@ | |||
1 | /* | ||
2 | * TI SYSCON regmap reset driver | ||
3 | * | ||
4 | * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ | ||
5 | * Andrew F. Davis <afd@ti.com> | ||
6 | * Suman Anna <afd@ti.com> | ||
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 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
13 | * kind, whether express or implied; without even the implied warranty | ||
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/mfd/syscon.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/regmap.h> | ||
23 | #include <linux/reset-controller.h> | ||
24 | |||
25 | #include <dt-bindings/reset/ti-syscon.h> | ||
26 | |||
27 | /** | ||
28 | * struct ti_syscon_reset_control - reset control structure | ||
29 | * @assert_offset: reset assert control register offset from syscon base | ||
30 | * @assert_bit: reset assert bit in the reset assert control register | ||
31 | * @deassert_offset: reset deassert control register offset from syscon base | ||
32 | * @deassert_bit: reset deassert bit in the reset deassert control register | ||
33 | * @status_offset: reset status register offset from syscon base | ||
34 | * @status_bit: reset status bit in the reset status register | ||
35 | * @flags: reset flag indicating how the (de)assert and status are handled | ||
36 | */ | ||
37 | struct ti_syscon_reset_control { | ||
38 | unsigned int assert_offset; | ||
39 | unsigned int assert_bit; | ||
40 | unsigned int deassert_offset; | ||
41 | unsigned int deassert_bit; | ||
42 | unsigned int status_offset; | ||
43 | unsigned int status_bit; | ||
44 | u32 flags; | ||
45 | }; | ||
46 | |||
47 | /** | ||
48 | * struct ti_syscon_reset_data - reset controller information structure | ||
49 | * @rcdev: reset controller entity | ||
50 | * @regmap: regmap handle containing the memory-mapped reset registers | ||
51 | * @controls: array of reset controls | ||
52 | * @nr_controls: number of controls in control array | ||
53 | */ | ||
54 | struct ti_syscon_reset_data { | ||
55 | struct reset_controller_dev rcdev; | ||
56 | struct regmap *regmap; | ||
57 | struct ti_syscon_reset_control *controls; | ||
58 | unsigned int nr_controls; | ||
59 | }; | ||
60 | |||
61 | #define to_ti_syscon_reset_data(rcdev) \ | ||
62 | container_of(rcdev, struct ti_syscon_reset_data, rcdev) | ||
63 | |||
64 | /** | ||
65 | * ti_syscon_reset_assert() - assert device reset | ||
66 | * @rcdev: reset controller entity | ||
67 | * @id: ID of the reset to be asserted | ||
68 | * | ||
69 | * This function implements the reset driver op to assert a device's reset. | ||
70 | * This asserts the reset in a manner prescribed by the reset flags. | ||
71 | * | ||
72 | * Return: 0 for successful request, else a corresponding error value | ||
73 | */ | ||
74 | static int ti_syscon_reset_assert(struct reset_controller_dev *rcdev, | ||
75 | unsigned long id) | ||
76 | { | ||
77 | struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev); | ||
78 | struct ti_syscon_reset_control *control; | ||
79 | unsigned int mask, value; | ||
80 | |||
81 | if (id >= data->nr_controls) | ||
82 | return -EINVAL; | ||
83 | |||
84 | control = &data->controls[id]; | ||
85 | |||
86 | if (control->flags & ASSERT_NONE) | ||
87 | return -ENOTSUPP; /* assert not supported for this reset */ | ||
88 | |||
89 | mask = BIT(control->assert_bit); | ||
90 | value = (control->flags & ASSERT_SET) ? mask : 0x0; | ||
91 | |||
92 | return regmap_update_bits(data->regmap, control->assert_offset, mask, value); | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * ti_syscon_reset_deassert() - deassert device reset | ||
97 | * @rcdev: reset controller entity | ||
98 | * @id: ID of reset to be deasserted | ||
99 | * | ||
100 | * This function implements the reset driver op to deassert a device's reset. | ||
101 | * This deasserts the reset in a manner prescribed by the reset flags. | ||
102 | * | ||
103 | * Return: 0 for successful request, else a corresponding error value | ||
104 | */ | ||
105 | static int ti_syscon_reset_deassert(struct reset_controller_dev *rcdev, | ||
106 | unsigned long id) | ||
107 | { | ||
108 | struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev); | ||
109 | struct ti_syscon_reset_control *control; | ||
110 | unsigned int mask, value; | ||
111 | |||
112 | if (id >= data->nr_controls) | ||
113 | return -EINVAL; | ||
114 | |||
115 | control = &data->controls[id]; | ||
116 | |||
117 | if (control->flags & DEASSERT_NONE) | ||
118 | return -ENOTSUPP; /* deassert not supported for this reset */ | ||
119 | |||
120 | mask = BIT(control->deassert_bit); | ||
121 | value = (control->flags & DEASSERT_SET) ? mask : 0x0; | ||
122 | |||
123 | return regmap_update_bits(data->regmap, control->deassert_offset, mask, value); | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * ti_syscon_reset_status() - check device reset status | ||
128 | * @rcdev: reset controller entity | ||
129 | * @id: ID of the reset for which the status is being requested | ||
130 | * | ||
131 | * This function implements the reset driver op to return the status of a | ||
132 | * device's reset. | ||
133 | * | ||
134 | * Return: 0 if reset is deasserted, true if reset is asserted, else a | ||
135 | * corresponding error value | ||
136 | */ | ||
137 | static int ti_syscon_reset_status(struct reset_controller_dev *rcdev, | ||
138 | unsigned long id) | ||
139 | { | ||
140 | struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev); | ||
141 | struct ti_syscon_reset_control *control; | ||
142 | unsigned int reset_state; | ||
143 | int ret; | ||
144 | |||
145 | if (id >= data->nr_controls) | ||
146 | return -EINVAL; | ||
147 | |||
148 | control = &data->controls[id]; | ||
149 | |||
150 | if (control->flags & STATUS_NONE) | ||
151 | return -ENOTSUPP; /* status not supported for this reset */ | ||
152 | |||
153 | ret = regmap_read(data->regmap, control->status_offset, &reset_state); | ||
154 | if (ret) | ||
155 | return ret; | ||
156 | |||
157 | return (reset_state & BIT(control->status_bit)) && | ||
158 | (control->flags & STATUS_SET); | ||
159 | } | ||
160 | |||
161 | static struct reset_control_ops ti_syscon_reset_ops = { | ||
162 | .assert = ti_syscon_reset_assert, | ||
163 | .deassert = ti_syscon_reset_deassert, | ||
164 | .status = ti_syscon_reset_status, | ||
165 | }; | ||
166 | |||
167 | static int ti_syscon_reset_probe(struct platform_device *pdev) | ||
168 | { | ||
169 | struct device *dev = &pdev->dev; | ||
170 | struct device_node *np = dev->of_node; | ||
171 | struct ti_syscon_reset_data *data; | ||
172 | struct regmap *regmap; | ||
173 | const __be32 *list; | ||
174 | struct ti_syscon_reset_control *controls; | ||
175 | int size, nr_controls, i; | ||
176 | |||
177 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | ||
178 | if (!data) | ||
179 | return -ENOMEM; | ||
180 | |||
181 | regmap = syscon_node_to_regmap(np->parent); | ||
182 | if (IS_ERR(regmap)) | ||
183 | return PTR_ERR(regmap); | ||
184 | |||
185 | list = of_get_property(np, "ti,reset-bits", &size); | ||
186 | if (!list || (size / sizeof(*list)) % 7 != 0) { | ||
187 | dev_err(dev, "invalid DT reset description\n"); | ||
188 | return -EINVAL; | ||
189 | } | ||
190 | |||
191 | nr_controls = (size / sizeof(*list)) / 7; | ||
192 | controls = devm_kzalloc(dev, nr_controls * sizeof(*controls), GFP_KERNEL); | ||
193 | if (!controls) | ||
194 | return -ENOMEM; | ||
195 | |||
196 | for (i = 0; i < nr_controls; i++) { | ||
197 | controls[i].assert_offset = be32_to_cpup(list++); | ||
198 | controls[i].assert_bit = be32_to_cpup(list++); | ||
199 | controls[i].deassert_offset = be32_to_cpup(list++); | ||
200 | controls[i].deassert_bit = be32_to_cpup(list++); | ||
201 | controls[i].status_offset = be32_to_cpup(list++); | ||
202 | controls[i].status_bit = be32_to_cpup(list++); | ||
203 | controls[i].flags = be32_to_cpup(list++); | ||
204 | } | ||
205 | |||
206 | data->rcdev.ops = &ti_syscon_reset_ops; | ||
207 | data->rcdev.owner = THIS_MODULE; | ||
208 | data->rcdev.of_node = np; | ||
209 | data->rcdev.nr_resets = nr_controls; | ||
210 | data->regmap = regmap; | ||
211 | data->controls = controls; | ||
212 | data->nr_controls = nr_controls; | ||
213 | |||
214 | platform_set_drvdata(pdev, data); | ||
215 | |||
216 | return devm_reset_controller_register(dev, &data->rcdev); | ||
217 | } | ||
218 | |||
219 | static const struct of_device_id ti_syscon_reset_of_match[] = { | ||
220 | { .compatible = "ti,syscon-reset", }, | ||
221 | { /* sentinel */ }, | ||
222 | }; | ||
223 | MODULE_DEVICE_TABLE(of, ti_syscon_reset_of_match); | ||
224 | |||
225 | static struct platform_driver ti_syscon_reset_driver = { | ||
226 | .probe = ti_syscon_reset_probe, | ||
227 | .driver = { | ||
228 | .name = "ti-syscon-reset", | ||
229 | .of_match_table = ti_syscon_reset_of_match, | ||
230 | }, | ||
231 | }; | ||
232 | module_platform_driver(ti_syscon_reset_driver); | ||
233 | |||
234 | MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); | ||
235 | MODULE_AUTHOR("Suman Anna <s-anna@ti.com>"); | ||
236 | MODULE_DESCRIPTION("TI SYSCON Regmap Reset Driver"); | ||
237 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/reset/reset-zynq.c b/drivers/reset/reset-zynq.c index a7e87bc45885..138f2f205662 100644 --- a/drivers/reset/reset-zynq.c +++ b/drivers/reset/reset-zynq.c | |||
@@ -122,16 +122,7 @@ static int zynq_reset_probe(struct platform_device *pdev) | |||
122 | priv->rcdev.ops = &zynq_reset_ops; | 122 | priv->rcdev.ops = &zynq_reset_ops; |
123 | priv->rcdev.of_node = pdev->dev.of_node; | 123 | priv->rcdev.of_node = pdev->dev.of_node; |
124 | 124 | ||
125 | return reset_controller_register(&priv->rcdev); | 125 | return devm_reset_controller_register(&pdev->dev, &priv->rcdev); |
126 | } | ||
127 | |||
128 | static int zynq_reset_remove(struct platform_device *pdev) | ||
129 | { | ||
130 | struct zynq_reset_data *priv = platform_get_drvdata(pdev); | ||
131 | |||
132 | reset_controller_unregister(&priv->rcdev); | ||
133 | |||
134 | return 0; | ||
135 | } | 126 | } |
136 | 127 | ||
137 | static const struct of_device_id zynq_reset_dt_ids[] = { | 128 | static const struct of_device_id zynq_reset_dt_ids[] = { |
@@ -141,7 +132,6 @@ static const struct of_device_id zynq_reset_dt_ids[] = { | |||
141 | 132 | ||
142 | static struct platform_driver zynq_reset_driver = { | 133 | static struct platform_driver zynq_reset_driver = { |
143 | .probe = zynq_reset_probe, | 134 | .probe = zynq_reset_probe, |
144 | .remove = zynq_reset_remove, | ||
145 | .driver = { | 135 | .driver = { |
146 | .name = KBUILD_MODNAME, | 136 | .name = KBUILD_MODNAME, |
147 | .of_match_table = zynq_reset_dt_ids, | 137 | .of_match_table = zynq_reset_dt_ids, |
diff --git a/drivers/reset/sti/Kconfig b/drivers/reset/sti/Kconfig index f8c15a37fb35..613178553612 100644 --- a/drivers/reset/sti/Kconfig +++ b/drivers/reset/sti/Kconfig | |||
@@ -2,7 +2,6 @@ if ARCH_STI | |||
2 | 2 | ||
3 | config STI_RESET_SYSCFG | 3 | config STI_RESET_SYSCFG |
4 | bool | 4 | bool |
5 | select RESET_CONTROLLER | ||
6 | 5 | ||
7 | config STIH415_RESET | 6 | config STIH415_RESET |
8 | bool | 7 | bool |
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index cb58ef0d9b2c..fe42a2fdf351 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig | |||
@@ -1,7 +1,6 @@ | |||
1 | menu "SOC (System On Chip) specific Drivers" | 1 | menu "SOC (System On Chip) specific Drivers" |
2 | 2 | ||
3 | source "drivers/soc/bcm/Kconfig" | 3 | source "drivers/soc/bcm/Kconfig" |
4 | source "drivers/soc/brcmstb/Kconfig" | ||
5 | source "drivers/soc/fsl/qe/Kconfig" | 4 | source "drivers/soc/fsl/qe/Kconfig" |
6 | source "drivers/soc/mediatek/Kconfig" | 5 | source "drivers/soc/mediatek/Kconfig" |
7 | source "drivers/soc/qcom/Kconfig" | 6 | source "drivers/soc/qcom/Kconfig" |
@@ -10,6 +9,7 @@ source "drivers/soc/samsung/Kconfig" | |||
10 | source "drivers/soc/sunxi/Kconfig" | 9 | source "drivers/soc/sunxi/Kconfig" |
11 | source "drivers/soc/tegra/Kconfig" | 10 | source "drivers/soc/tegra/Kconfig" |
12 | source "drivers/soc/ti/Kconfig" | 11 | source "drivers/soc/ti/Kconfig" |
12 | source "drivers/soc/ux500/Kconfig" | ||
13 | source "drivers/soc/versatile/Kconfig" | 13 | source "drivers/soc/versatile/Kconfig" |
14 | 14 | ||
15 | endmenu | 15 | endmenu |
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 380230f03874..50c23d0bd457 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile | |||
@@ -3,7 +3,6 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | obj-y += bcm/ | 5 | obj-y += bcm/ |
6 | obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/ | ||
7 | obj-$(CONFIG_ARCH_DOVE) += dove/ | 6 | obj-$(CONFIG_ARCH_DOVE) += dove/ |
8 | obj-$(CONFIG_MACH_DOVE) += dove/ | 7 | obj-$(CONFIG_MACH_DOVE) += dove/ |
9 | obj-y += fsl/ | 8 | obj-y += fsl/ |
@@ -15,4 +14,5 @@ obj-$(CONFIG_SOC_SAMSUNG) += samsung/ | |||
15 | obj-$(CONFIG_ARCH_SUNXI) += sunxi/ | 14 | obj-$(CONFIG_ARCH_SUNXI) += sunxi/ |
16 | obj-$(CONFIG_ARCH_TEGRA) += tegra/ | 15 | obj-$(CONFIG_ARCH_TEGRA) += tegra/ |
17 | obj-$(CONFIG_SOC_TI) += ti/ | 16 | obj-$(CONFIG_SOC_TI) += ti/ |
17 | obj-$(CONFIG_ARCH_U8500) += ux500/ | ||
18 | obj-$(CONFIG_PLAT_VERSATILE) += versatile/ | 18 | obj-$(CONFIG_PLAT_VERSATILE) += versatile/ |
diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig index 3066edea184d..a39b0d58ddd0 100644 --- a/drivers/soc/bcm/Kconfig +++ b/drivers/soc/bcm/Kconfig | |||
@@ -1,9 +1,23 @@ | |||
1 | menu "Broadcom SoC drivers" | ||
2 | |||
1 | config RASPBERRYPI_POWER | 3 | config RASPBERRYPI_POWER |
2 | bool "Raspberry Pi power domain driver" | 4 | bool "Raspberry Pi power domain driver" |
3 | depends on ARCH_BCM2835 || COMPILE_TEST | 5 | depends on ARCH_BCM2835 || (COMPILE_TEST && OF) |
4 | depends on RASPBERRYPI_FIRMWARE=y | 6 | depends on RASPBERRYPI_FIRMWARE=y |
5 | select PM_GENERIC_DOMAINS if PM | 7 | select PM_GENERIC_DOMAINS if PM |
6 | select PM_GENERIC_DOMAINS_OF if PM | ||
7 | help | 8 | help |
8 | This enables support for the RPi power domains which can be enabled | 9 | This enables support for the RPi power domains which can be enabled |
9 | or disabled via the RPi firmware. | 10 | or disabled via the RPi firmware. |
11 | |||
12 | config SOC_BRCMSTB | ||
13 | bool "Broadcom STB SoC drivers" | ||
14 | depends on ARM | ||
15 | select SOC_BUS | ||
16 | help | ||
17 | Enables drivers for the Broadcom Set-Top Box (STB) series of chips. | ||
18 | This option alone enables only some support code, while the drivers | ||
19 | can be enabled individually within this menu. | ||
20 | |||
21 | If unsure, say N. | ||
22 | |||
23 | endmenu | ||
diff --git a/drivers/soc/bcm/Makefile b/drivers/soc/bcm/Makefile index 63aa3eb23087..dc4fced72d21 100644 --- a/drivers/soc/bcm/Makefile +++ b/drivers/soc/bcm/Makefile | |||
@@ -1 +1,2 @@ | |||
1 | obj-$(CONFIG_RASPBERRYPI_POWER) += raspberrypi-power.o | 1 | obj-$(CONFIG_RASPBERRYPI_POWER) += raspberrypi-power.o |
2 | obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/ | ||
diff --git a/drivers/soc/brcmstb/Makefile b/drivers/soc/bcm/brcmstb/Makefile index 9120b2715d3e..9120b2715d3e 100644 --- a/drivers/soc/brcmstb/Makefile +++ b/drivers/soc/bcm/brcmstb/Makefile | |||
diff --git a/drivers/soc/brcmstb/biuctrl.c b/drivers/soc/bcm/brcmstb/biuctrl.c index 9049c076f9a1..3c39415d484f 100644 --- a/drivers/soc/brcmstb/biuctrl.c +++ b/drivers/soc/bcm/brcmstb/biuctrl.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/io.h> | 19 | #include <linux/io.h> |
20 | #include <linux/of_address.h> | 20 | #include <linux/of_address.h> |
21 | #include <linux/syscore_ops.h> | 21 | #include <linux/syscore_ops.h> |
22 | #include <linux/soc/brcmstb/brcmstb.h> | ||
22 | 23 | ||
23 | #define CPU_CREDIT_REG_OFFSET 0x184 | 24 | #define CPU_CREDIT_REG_OFFSET 0x184 |
24 | #define CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK 0x70000000 | 25 | #define CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK 0x70000000 |
diff --git a/drivers/soc/brcmstb/common.c b/drivers/soc/bcm/brcmstb/common.c index 94e7335553f4..94e7335553f4 100644 --- a/drivers/soc/brcmstb/common.c +++ b/drivers/soc/bcm/brcmstb/common.c | |||
diff --git a/drivers/soc/brcmstb/Kconfig b/drivers/soc/brcmstb/Kconfig deleted file mode 100644 index 7fec3b4c80a1..000000000000 --- a/drivers/soc/brcmstb/Kconfig +++ /dev/null | |||
@@ -1,10 +0,0 @@ | |||
1 | menuconfig SOC_BRCMSTB | ||
2 | bool "Broadcom STB SoC drivers" | ||
3 | depends on ARM | ||
4 | select SOC_BUS | ||
5 | help | ||
6 | Enables drivers for the Broadcom Set-Top Box (STB) series of chips. | ||
7 | This option alone enables only some support code, while the drivers | ||
8 | can be enabled individually within this menu. | ||
9 | |||
10 | If unsure, say N. | ||
diff --git a/drivers/soc/qcom/smem_state.c b/drivers/soc/qcom/smem_state.c index 54261decb369..d5437ca76ed9 100644 --- a/drivers/soc/qcom/smem_state.c +++ b/drivers/soc/qcom/smem_state.c | |||
@@ -104,26 +104,26 @@ struct qcom_smem_state *qcom_smem_state_get(struct device *dev, | |||
104 | 104 | ||
105 | if (con_id) { | 105 | if (con_id) { |
106 | index = of_property_match_string(dev->of_node, | 106 | index = of_property_match_string(dev->of_node, |
107 | "qcom,state-names", | 107 | "qcom,smem-state-names", |
108 | con_id); | 108 | con_id); |
109 | if (index < 0) { | 109 | if (index < 0) { |
110 | dev_err(dev, "missing qcom,state-names\n"); | 110 | dev_err(dev, "missing qcom,smem-state-names\n"); |
111 | return ERR_PTR(index); | 111 | return ERR_PTR(index); |
112 | } | 112 | } |
113 | } | 113 | } |
114 | 114 | ||
115 | ret = of_parse_phandle_with_args(dev->of_node, | 115 | ret = of_parse_phandle_with_args(dev->of_node, |
116 | "qcom,state", | 116 | "qcom,smem-states", |
117 | "#qcom,state-cells", | 117 | "#qcom,smem-state-cells", |
118 | index, | 118 | index, |
119 | &args); | 119 | &args); |
120 | if (ret) { | 120 | if (ret) { |
121 | dev_err(dev, "failed to parse qcom,state property\n"); | 121 | dev_err(dev, "failed to parse qcom,smem-states property\n"); |
122 | return ERR_PTR(ret); | 122 | return ERR_PTR(ret); |
123 | } | 123 | } |
124 | 124 | ||
125 | if (args.args_count != 1) { | 125 | if (args.args_count != 1) { |
126 | dev_err(dev, "invalid #qcom,state-cells\n"); | 126 | dev_err(dev, "invalid #qcom,smem-state-cells\n"); |
127 | return ERR_PTR(-EINVAL); | 127 | return ERR_PTR(-EINVAL); |
128 | } | 128 | } |
129 | 129 | ||
diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index f1eed7f9dd67..f51fb2ea7200 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c | |||
@@ -196,7 +196,7 @@ static irqreturn_t qcom_smp2p_intr(int irq, void *data) | |||
196 | /* Match newly created entries */ | 196 | /* Match newly created entries */ |
197 | for (i = smp2p->valid_entries; i < in->valid_entries; i++) { | 197 | for (i = smp2p->valid_entries; i < in->valid_entries; i++) { |
198 | list_for_each_entry(entry, &smp2p->inbound, node) { | 198 | list_for_each_entry(entry, &smp2p->inbound, node) { |
199 | memcpy_fromio(buf, in->entries[i].name, sizeof(buf)); | 199 | memcpy(buf, in->entries[i].name, sizeof(buf)); |
200 | if (!strcmp(buf, entry->name)) { | 200 | if (!strcmp(buf, entry->name)) { |
201 | entry->value = &in->entries[i].value; | 201 | entry->value = &in->entries[i].value; |
202 | break; | 202 | break; |
@@ -343,12 +343,13 @@ static int qcom_smp2p_outbound_entry(struct qcom_smp2p *smp2p, | |||
343 | 343 | ||
344 | /* Allocate an entry from the smem item */ | 344 | /* Allocate an entry from the smem item */ |
345 | strlcpy(buf, entry->name, SMP2P_MAX_ENTRY_NAME); | 345 | strlcpy(buf, entry->name, SMP2P_MAX_ENTRY_NAME); |
346 | memcpy_toio(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME); | 346 | memcpy(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME); |
347 | out->valid_entries++; | ||
348 | 347 | ||
349 | /* Make the logical entry reference the physical value */ | 348 | /* Make the logical entry reference the physical value */ |
350 | entry->value = &out->entries[out->valid_entries].value; | 349 | entry->value = &out->entries[out->valid_entries].value; |
351 | 350 | ||
351 | out->valid_entries++; | ||
352 | |||
352 | entry->state = qcom_smem_state_register(node, &smp2p_state_ops, entry); | 353 | entry->state = qcom_smem_state_register(node, &smp2p_state_ops, entry); |
353 | if (IS_ERR(entry->state)) { | 354 | if (IS_ERR(entry->state)) { |
354 | dev_err(smp2p->dev, "failed to register qcom_smem_state\n"); | 355 | dev_err(smp2p->dev, "failed to register qcom_smem_state\n"); |
diff --git a/drivers/soc/qcom/smsm.c b/drivers/soc/qcom/smsm.c index 6b777af1bc19..d0337b2a71c8 100644 --- a/drivers/soc/qcom/smsm.c +++ b/drivers/soc/qcom/smsm.c | |||
@@ -495,7 +495,7 @@ static int qcom_smsm_probe(struct platform_device *pdev) | |||
495 | if (!smsm->hosts) | 495 | if (!smsm->hosts) |
496 | return -ENOMEM; | 496 | return -ENOMEM; |
497 | 497 | ||
498 | local_node = of_find_node_with_property(pdev->dev.of_node, "#qcom,state-cells"); | 498 | local_node = of_find_node_with_property(pdev->dev.of_node, "#qcom,smem-state-cells"); |
499 | if (!local_node) { | 499 | if (!local_node) { |
500 | dev_err(&pdev->dev, "no state entry\n"); | 500 | dev_err(&pdev->dev, "no state entry\n"); |
501 | return -EINVAL; | 501 | return -EINVAL; |
diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c index c544f3d2c6ee..520aedd29965 100644 --- a/drivers/soc/qcom/wcnss_ctrl.c +++ b/drivers/soc/qcom/wcnss_ctrl.c | |||
@@ -1,4 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) 2016, Linaro Ltd. | ||
2 | * Copyright (c) 2015, Sony Mobile Communications Inc. | 3 | * Copyright (c) 2015, Sony Mobile Communications Inc. |
3 | * | 4 | * |
4 | * This program is free software; you can redistribute it and/or modify | 5 | * This program is free software; you can redistribute it and/or modify |
@@ -14,8 +15,16 @@ | |||
14 | #include <linux/module.h> | 15 | #include <linux/module.h> |
15 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
16 | #include <linux/soc/qcom/smd.h> | 17 | #include <linux/soc/qcom/smd.h> |
18 | #include <linux/io.h> | ||
19 | #include <linux/of_platform.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/soc/qcom/wcnss_ctrl.h> | ||
17 | 22 | ||
18 | #define WCNSS_REQUEST_TIMEOUT (5 * HZ) | 23 | #define WCNSS_REQUEST_TIMEOUT (5 * HZ) |
24 | #define WCNSS_CBC_TIMEOUT (10 * HZ) | ||
25 | |||
26 | #define WCNSS_ACK_DONE_BOOTING 1 | ||
27 | #define WCNSS_ACK_COLD_BOOTING 2 | ||
19 | 28 | ||
20 | #define NV_FRAGMENT_SIZE 3072 | 29 | #define NV_FRAGMENT_SIZE 3072 |
21 | #define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin" | 30 | #define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin" |
@@ -25,17 +34,19 @@ | |||
25 | * @dev: device handle | 34 | * @dev: device handle |
26 | * @channel: SMD channel handle | 35 | * @channel: SMD channel handle |
27 | * @ack: completion for outstanding requests | 36 | * @ack: completion for outstanding requests |
37 | * @cbc: completion for cbc complete indication | ||
28 | * @ack_status: status of the outstanding request | 38 | * @ack_status: status of the outstanding request |
29 | * @download_nv_work: worker for uploading nv binary | 39 | * @probe_work: worker for uploading nv binary |
30 | */ | 40 | */ |
31 | struct wcnss_ctrl { | 41 | struct wcnss_ctrl { |
32 | struct device *dev; | 42 | struct device *dev; |
33 | struct qcom_smd_channel *channel; | 43 | struct qcom_smd_channel *channel; |
34 | 44 | ||
35 | struct completion ack; | 45 | struct completion ack; |
46 | struct completion cbc; | ||
36 | int ack_status; | 47 | int ack_status; |
37 | 48 | ||
38 | struct work_struct download_nv_work; | 49 | struct work_struct probe_work; |
39 | }; | 50 | }; |
40 | 51 | ||
41 | /* message types */ | 52 | /* message types */ |
@@ -48,6 +59,11 @@ enum { | |||
48 | WCNSS_UPLOAD_CAL_RESP, | 59 | WCNSS_UPLOAD_CAL_RESP, |
49 | WCNSS_DOWNLOAD_CAL_REQ, | 60 | WCNSS_DOWNLOAD_CAL_REQ, |
50 | WCNSS_DOWNLOAD_CAL_RESP, | 61 | WCNSS_DOWNLOAD_CAL_RESP, |
62 | WCNSS_VBAT_LEVEL_IND, | ||
63 | WCNSS_BUILD_VERSION_REQ, | ||
64 | WCNSS_BUILD_VERSION_RESP, | ||
65 | WCNSS_PM_CONFIG_REQ, | ||
66 | WCNSS_CBC_COMPLETE_IND, | ||
51 | }; | 67 | }; |
52 | 68 | ||
53 | /** | 69 | /** |
@@ -128,7 +144,7 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel, | |||
128 | version->major, version->minor, | 144 | version->major, version->minor, |
129 | version->version, version->revision); | 145 | version->version, version->revision); |
130 | 146 | ||
131 | schedule_work(&wcnss->download_nv_work); | 147 | complete(&wcnss->ack); |
132 | break; | 148 | break; |
133 | case WCNSS_DOWNLOAD_NV_RESP: | 149 | case WCNSS_DOWNLOAD_NV_RESP: |
134 | if (count != sizeof(*nvresp)) { | 150 | if (count != sizeof(*nvresp)) { |
@@ -141,6 +157,10 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel, | |||
141 | wcnss->ack_status = nvresp->status; | 157 | wcnss->ack_status = nvresp->status; |
142 | complete(&wcnss->ack); | 158 | complete(&wcnss->ack); |
143 | break; | 159 | break; |
160 | case WCNSS_CBC_COMPLETE_IND: | ||
161 | dev_dbg(wcnss->dev, "cold boot complete\n"); | ||
162 | complete(&wcnss->cbc); | ||
163 | break; | ||
144 | default: | 164 | default: |
145 | dev_info(wcnss->dev, "unknown message type %d\n", hdr->type); | 165 | dev_info(wcnss->dev, "unknown message type %d\n", hdr->type); |
146 | break; | 166 | break; |
@@ -156,20 +176,32 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel, | |||
156 | static int wcnss_request_version(struct wcnss_ctrl *wcnss) | 176 | static int wcnss_request_version(struct wcnss_ctrl *wcnss) |
157 | { | 177 | { |
158 | struct wcnss_msg_hdr msg; | 178 | struct wcnss_msg_hdr msg; |
179 | int ret; | ||
159 | 180 | ||
160 | msg.type = WCNSS_VERSION_REQ; | 181 | msg.type = WCNSS_VERSION_REQ; |
161 | msg.len = sizeof(msg); | 182 | msg.len = sizeof(msg); |
183 | ret = qcom_smd_send(wcnss->channel, &msg, sizeof(msg)); | ||
184 | if (ret < 0) | ||
185 | return ret; | ||
186 | |||
187 | ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_CBC_TIMEOUT); | ||
188 | if (!ret) { | ||
189 | dev_err(wcnss->dev, "timeout waiting for version response\n"); | ||
190 | return -ETIMEDOUT; | ||
191 | } | ||
162 | 192 | ||
163 | return qcom_smd_send(wcnss->channel, &msg, sizeof(msg)); | 193 | return 0; |
164 | } | 194 | } |
165 | 195 | ||
166 | /** | 196 | /** |
167 | * wcnss_download_nv() - send nv binary to WCNSS | 197 | * wcnss_download_nv() - send nv binary to WCNSS |
168 | * @work: work struct to acquire wcnss context | 198 | * @wcnss: wcnss_ctrl state handle |
199 | * @expect_cbc: indicator to caller that an cbc event is expected | ||
200 | * | ||
201 | * Returns 0 on success. Negative errno on failure. | ||
169 | */ | 202 | */ |
170 | static void wcnss_download_nv(struct work_struct *work) | 203 | static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc) |
171 | { | 204 | { |
172 | struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, download_nv_work); | ||
173 | struct wcnss_download_nv_req *req; | 205 | struct wcnss_download_nv_req *req; |
174 | const struct firmware *fw; | 206 | const struct firmware *fw; |
175 | const void *data; | 207 | const void *data; |
@@ -178,10 +210,10 @@ static void wcnss_download_nv(struct work_struct *work) | |||
178 | 210 | ||
179 | req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL); | 211 | req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL); |
180 | if (!req) | 212 | if (!req) |
181 | return; | 213 | return -ENOMEM; |
182 | 214 | ||
183 | ret = request_firmware(&fw, NVBIN_FILE, wcnss->dev); | 215 | ret = request_firmware(&fw, NVBIN_FILE, wcnss->dev); |
184 | if (ret) { | 216 | if (ret < 0) { |
185 | dev_err(wcnss->dev, "Failed to load nv file %s: %d\n", | 217 | dev_err(wcnss->dev, "Failed to load nv file %s: %d\n", |
186 | NVBIN_FILE, ret); | 218 | NVBIN_FILE, ret); |
187 | goto free_req; | 219 | goto free_req; |
@@ -207,7 +239,7 @@ static void wcnss_download_nv(struct work_struct *work) | |||
207 | memcpy(req->fragment, data, req->frag_size); | 239 | memcpy(req->fragment, data, req->frag_size); |
208 | 240 | ||
209 | ret = qcom_smd_send(wcnss->channel, req, req->hdr.len); | 241 | ret = qcom_smd_send(wcnss->channel, req, req->hdr.len); |
210 | if (ret) { | 242 | if (ret < 0) { |
211 | dev_err(wcnss->dev, "failed to send smd packet\n"); | 243 | dev_err(wcnss->dev, "failed to send smd packet\n"); |
212 | goto release_fw; | 244 | goto release_fw; |
213 | } | 245 | } |
@@ -220,16 +252,58 @@ static void wcnss_download_nv(struct work_struct *work) | |||
220 | } while (left > 0); | 252 | } while (left > 0); |
221 | 253 | ||
222 | ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT); | 254 | ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT); |
223 | if (!ret) | 255 | if (!ret) { |
224 | dev_err(wcnss->dev, "timeout waiting for nv upload ack\n"); | 256 | dev_err(wcnss->dev, "timeout waiting for nv upload ack\n"); |
225 | else if (wcnss->ack_status != 1) | 257 | ret = -ETIMEDOUT; |
226 | dev_err(wcnss->dev, "nv upload response failed err: %d\n", | 258 | } else { |
227 | wcnss->ack_status); | 259 | *expect_cbc = wcnss->ack_status == WCNSS_ACK_COLD_BOOTING; |
260 | ret = 0; | ||
261 | } | ||
228 | 262 | ||
229 | release_fw: | 263 | release_fw: |
230 | release_firmware(fw); | 264 | release_firmware(fw); |
231 | free_req: | 265 | free_req: |
232 | kfree(req); | 266 | kfree(req); |
267 | |||
268 | return ret; | ||
269 | } | ||
270 | |||
271 | /** | ||
272 | * qcom_wcnss_open_channel() - open additional SMD channel to WCNSS | ||
273 | * @wcnss: wcnss handle, retrieved from drvdata | ||
274 | * @name: SMD channel name | ||
275 | * @cb: callback to handle incoming data on the channel | ||
276 | */ | ||
277 | struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb) | ||
278 | { | ||
279 | struct wcnss_ctrl *_wcnss = wcnss; | ||
280 | |||
281 | return qcom_smd_open_channel(_wcnss->channel, name, cb); | ||
282 | } | ||
283 | EXPORT_SYMBOL(qcom_wcnss_open_channel); | ||
284 | |||
285 | static void wcnss_async_probe(struct work_struct *work) | ||
286 | { | ||
287 | struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, probe_work); | ||
288 | bool expect_cbc; | ||
289 | int ret; | ||
290 | |||
291 | ret = wcnss_request_version(wcnss); | ||
292 | if (ret < 0) | ||
293 | return; | ||
294 | |||
295 | ret = wcnss_download_nv(wcnss, &expect_cbc); | ||
296 | if (ret < 0) | ||
297 | return; | ||
298 | |||
299 | /* Wait for pending cold boot completion if indicated by the nv downloader */ | ||
300 | if (expect_cbc) { | ||
301 | ret = wait_for_completion_timeout(&wcnss->cbc, WCNSS_REQUEST_TIMEOUT); | ||
302 | if (!ret) | ||
303 | dev_err(wcnss->dev, "expected cold boot completion\n"); | ||
304 | } | ||
305 | |||
306 | of_platform_populate(wcnss->dev->of_node, NULL, NULL, wcnss->dev); | ||
233 | } | 307 | } |
234 | 308 | ||
235 | static int wcnss_ctrl_probe(struct qcom_smd_device *sdev) | 309 | static int wcnss_ctrl_probe(struct qcom_smd_device *sdev) |
@@ -244,25 +318,38 @@ static int wcnss_ctrl_probe(struct qcom_smd_device *sdev) | |||
244 | wcnss->channel = sdev->channel; | 318 | wcnss->channel = sdev->channel; |
245 | 319 | ||
246 | init_completion(&wcnss->ack); | 320 | init_completion(&wcnss->ack); |
247 | INIT_WORK(&wcnss->download_nv_work, wcnss_download_nv); | 321 | init_completion(&wcnss->cbc); |
322 | INIT_WORK(&wcnss->probe_work, wcnss_async_probe); | ||
248 | 323 | ||
249 | qcom_smd_set_drvdata(sdev->channel, wcnss); | 324 | qcom_smd_set_drvdata(sdev->channel, wcnss); |
325 | dev_set_drvdata(&sdev->dev, wcnss); | ||
326 | |||
327 | schedule_work(&wcnss->probe_work); | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static void wcnss_ctrl_remove(struct qcom_smd_device *sdev) | ||
333 | { | ||
334 | struct wcnss_ctrl *wcnss = qcom_smd_get_drvdata(sdev->channel); | ||
250 | 335 | ||
251 | return wcnss_request_version(wcnss); | 336 | cancel_work_sync(&wcnss->probe_work); |
337 | of_platform_depopulate(&sdev->dev); | ||
252 | } | 338 | } |
253 | 339 | ||
254 | static const struct qcom_smd_id wcnss_ctrl_smd_match[] = { | 340 | static const struct of_device_id wcnss_ctrl_of_match[] = { |
255 | { .name = "WCNSS_CTRL" }, | 341 | { .compatible = "qcom,wcnss", }, |
256 | {} | 342 | {} |
257 | }; | 343 | }; |
258 | 344 | ||
259 | static struct qcom_smd_driver wcnss_ctrl_driver = { | 345 | static struct qcom_smd_driver wcnss_ctrl_driver = { |
260 | .probe = wcnss_ctrl_probe, | 346 | .probe = wcnss_ctrl_probe, |
347 | .remove = wcnss_ctrl_remove, | ||
261 | .callback = wcnss_ctrl_smd_callback, | 348 | .callback = wcnss_ctrl_smd_callback, |
262 | .smd_match_table = wcnss_ctrl_smd_match, | ||
263 | .driver = { | 349 | .driver = { |
264 | .name = "qcom_wcnss_ctrl", | 350 | .name = "qcom_wcnss_ctrl", |
265 | .owner = THIS_MODULE, | 351 | .owner = THIS_MODULE, |
352 | .of_match_table = wcnss_ctrl_of_match, | ||
266 | }, | 353 | }, |
267 | }; | 354 | }; |
268 | 355 | ||
diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig index d7fc123006a3..245533907d1b 100644 --- a/drivers/soc/samsung/Kconfig +++ b/drivers/soc/samsung/Kconfig | |||
@@ -10,4 +10,8 @@ config EXYNOS_PMU | |||
10 | bool "Exynos PMU controller driver" if COMPILE_TEST | 10 | bool "Exynos PMU controller driver" if COMPILE_TEST |
11 | depends on (ARM && ARCH_EXYNOS) || ((ARM || ARM64) && COMPILE_TEST) | 11 | depends on (ARM && ARCH_EXYNOS) || ((ARM || ARM64) && COMPILE_TEST) |
12 | 12 | ||
13 | config EXYNOS_PM_DOMAINS | ||
14 | bool "Exynos PM domains" if COMPILE_TEST | ||
15 | depends on PM_GENERIC_DOMAINS || COMPILE_TEST | ||
16 | |||
13 | endif | 17 | endif |
diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile index f64ac4d80564..3619f2ecddaa 100644 --- a/drivers/soc/samsung/Makefile +++ b/drivers/soc/samsung/Makefile | |||
@@ -1,2 +1,3 @@ | |||
1 | obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o exynos3250-pmu.o exynos4-pmu.o \ | 1 | obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o exynos3250-pmu.o exynos4-pmu.o \ |
2 | exynos5250-pmu.o exynos5420-pmu.o | 2 | exynos5250-pmu.o exynos5420-pmu.o |
3 | obj-$(CONFIG_EXYNOS_PM_DOMAINS) += pm_domains.o | ||
diff --git a/drivers/soc/samsung/exynos3250-pmu.c b/drivers/soc/samsung/exynos3250-pmu.c index 20b3ab8aa790..af2f54e14b83 100644 --- a/drivers/soc/samsung/exynos3250-pmu.c +++ b/drivers/soc/samsung/exynos3250-pmu.c | |||
@@ -14,7 +14,7 @@ | |||
14 | 14 | ||
15 | #include "exynos-pmu.h" | 15 | #include "exynos-pmu.h" |
16 | 16 | ||
17 | static struct exynos_pmu_conf exynos3250_pmu_config[] = { | 17 | static const struct exynos_pmu_conf exynos3250_pmu_config[] = { |
18 | /* { .offset = offset, .val = { AFTR, W-AFTR, SLEEP } */ | 18 | /* { .offset = offset, .val = { AFTR, W-AFTR, SLEEP } */ |
19 | { EXYNOS3_ARM_CORE0_SYS_PWR_REG, { 0x0, 0x0, 0x2} }, | 19 | { EXYNOS3_ARM_CORE0_SYS_PWR_REG, { 0x0, 0x0, 0x2} }, |
20 | { EXYNOS3_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, | 20 | { EXYNOS3_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, |
diff --git a/drivers/soc/samsung/exynos5420-pmu.c b/drivers/soc/samsung/exynos5420-pmu.c index b962fb6a5d22..3f2c64180ef8 100644 --- a/drivers/soc/samsung/exynos5420-pmu.c +++ b/drivers/soc/samsung/exynos5420-pmu.c | |||
@@ -17,7 +17,7 @@ | |||
17 | 17 | ||
18 | #include "exynos-pmu.h" | 18 | #include "exynos-pmu.h" |
19 | 19 | ||
20 | static struct exynos_pmu_conf exynos5420_pmu_config[] = { | 20 | static const struct exynos_pmu_conf exynos5420_pmu_config[] = { |
21 | /* { .offset = offset, .val = { AFTR, LPA, SLEEP } */ | 21 | /* { .offset = offset, .val = { AFTR, LPA, SLEEP } */ |
22 | { EXYNOS5_ARM_CORE0_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, | 22 | { EXYNOS5_ARM_CORE0_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, |
23 | { EXYNOS5_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, | 23 | { EXYNOS5_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, |
diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c new file mode 100644 index 000000000000..4822346aadc6 --- /dev/null +++ b/drivers/soc/samsung/pm_domains.c | |||
@@ -0,0 +1,245 @@ | |||
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/clk.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/of_address.h> | ||
23 | #include <linux/of_platform.h> | ||
24 | #include <linux/sched.h> | ||
25 | |||
26 | #define MAX_CLK_PER_DOMAIN 4 | ||
27 | |||
28 | struct exynos_pm_domain_config { | ||
29 | /* Value for LOCAL_PWR_CFG and STATUS fields for each domain */ | ||
30 | u32 local_pwr_cfg; | ||
31 | }; | ||
32 | |||
33 | /* | ||
34 | * Exynos specific wrapper around the generic power domain | ||
35 | */ | ||
36 | struct exynos_pm_domain { | ||
37 | void __iomem *base; | ||
38 | char const *name; | ||
39 | bool is_off; | ||
40 | struct generic_pm_domain pd; | ||
41 | struct clk *oscclk; | ||
42 | struct clk *clk[MAX_CLK_PER_DOMAIN]; | ||
43 | struct clk *pclk[MAX_CLK_PER_DOMAIN]; | ||
44 | struct clk *asb_clk[MAX_CLK_PER_DOMAIN]; | ||
45 | u32 local_pwr_cfg; | ||
46 | }; | ||
47 | |||
48 | static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) | ||
49 | { | ||
50 | struct exynos_pm_domain *pd; | ||
51 | void __iomem *base; | ||
52 | u32 timeout, pwr; | ||
53 | char *op; | ||
54 | int i; | ||
55 | |||
56 | pd = container_of(domain, struct exynos_pm_domain, pd); | ||
57 | base = pd->base; | ||
58 | |||
59 | for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { | ||
60 | if (IS_ERR(pd->asb_clk[i])) | ||
61 | break; | ||
62 | clk_prepare_enable(pd->asb_clk[i]); | ||
63 | } | ||
64 | |||
65 | /* Set oscclk before powering off a domain*/ | ||
66 | if (!power_on) { | ||
67 | for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { | ||
68 | if (IS_ERR(pd->clk[i])) | ||
69 | break; | ||
70 | pd->pclk[i] = clk_get_parent(pd->clk[i]); | ||
71 | if (clk_set_parent(pd->clk[i], pd->oscclk)) | ||
72 | pr_err("%s: error setting oscclk as parent to clock %d\n", | ||
73 | pd->name, i); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | pwr = power_on ? pd->local_pwr_cfg : 0; | ||
78 | writel_relaxed(pwr, base); | ||
79 | |||
80 | /* Wait max 1ms */ | ||
81 | timeout = 10; | ||
82 | |||
83 | while ((readl_relaxed(base + 0x4) & pd->local_pwr_cfg) != pwr) { | ||
84 | if (!timeout) { | ||
85 | op = (power_on) ? "enable" : "disable"; | ||
86 | pr_err("Power domain %s %s failed\n", domain->name, op); | ||
87 | return -ETIMEDOUT; | ||
88 | } | ||
89 | timeout--; | ||
90 | cpu_relax(); | ||
91 | usleep_range(80, 100); | ||
92 | } | ||
93 | |||
94 | /* Restore clocks after powering on a domain*/ | ||
95 | if (power_on) { | ||
96 | for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { | ||
97 | if (IS_ERR(pd->clk[i])) | ||
98 | break; | ||
99 | |||
100 | if (IS_ERR(pd->pclk[i])) | ||
101 | continue; /* Skip on first power up */ | ||
102 | if (clk_set_parent(pd->clk[i], pd->pclk[i])) | ||
103 | pr_err("%s: error setting parent to clock%d\n", | ||
104 | pd->name, i); | ||
105 | } | ||
106 | } | ||
107 | |||
108 | for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { | ||
109 | if (IS_ERR(pd->asb_clk[i])) | ||
110 | break; | ||
111 | clk_disable_unprepare(pd->asb_clk[i]); | ||
112 | } | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static int exynos_pd_power_on(struct generic_pm_domain *domain) | ||
118 | { | ||
119 | return exynos_pd_power(domain, true); | ||
120 | } | ||
121 | |||
122 | static int exynos_pd_power_off(struct generic_pm_domain *domain) | ||
123 | { | ||
124 | return exynos_pd_power(domain, false); | ||
125 | } | ||
126 | |||
127 | static const struct exynos_pm_domain_config exynos4210_cfg __initconst = { | ||
128 | .local_pwr_cfg = 0x7, | ||
129 | }; | ||
130 | |||
131 | static const struct of_device_id exynos_pm_domain_of_match[] __initconst = { | ||
132 | { | ||
133 | .compatible = "samsung,exynos4210-pd", | ||
134 | .data = &exynos4210_cfg, | ||
135 | }, | ||
136 | { }, | ||
137 | }; | ||
138 | |||
139 | static __init int exynos4_pm_init_power_domain(void) | ||
140 | { | ||
141 | struct device_node *np; | ||
142 | const struct of_device_id *match; | ||
143 | |||
144 | for_each_matching_node_and_match(np, exynos_pm_domain_of_match, &match) { | ||
145 | const struct exynos_pm_domain_config *pm_domain_cfg; | ||
146 | struct exynos_pm_domain *pd; | ||
147 | int on, i; | ||
148 | |||
149 | pm_domain_cfg = match->data; | ||
150 | |||
151 | pd = kzalloc(sizeof(*pd), GFP_KERNEL); | ||
152 | if (!pd) { | ||
153 | pr_err("%s: failed to allocate memory for domain\n", | ||
154 | __func__); | ||
155 | of_node_put(np); | ||
156 | return -ENOMEM; | ||
157 | } | ||
158 | pd->pd.name = kstrdup_const(strrchr(np->full_name, '/') + 1, | ||
159 | GFP_KERNEL); | ||
160 | if (!pd->pd.name) { | ||
161 | kfree(pd); | ||
162 | of_node_put(np); | ||
163 | return -ENOMEM; | ||
164 | } | ||
165 | |||
166 | pd->name = pd->pd.name; | ||
167 | pd->base = of_iomap(np, 0); | ||
168 | if (!pd->base) { | ||
169 | pr_warn("%s: failed to map memory\n", __func__); | ||
170 | kfree_const(pd->pd.name); | ||
171 | kfree(pd); | ||
172 | continue; | ||
173 | } | ||
174 | |||
175 | pd->pd.power_off = exynos_pd_power_off; | ||
176 | pd->pd.power_on = exynos_pd_power_on; | ||
177 | pd->local_pwr_cfg = pm_domain_cfg->local_pwr_cfg; | ||
178 | |||
179 | for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { | ||
180 | char clk_name[8]; | ||
181 | |||
182 | snprintf(clk_name, sizeof(clk_name), "asb%d", i); | ||
183 | pd->asb_clk[i] = of_clk_get_by_name(np, clk_name); | ||
184 | if (IS_ERR(pd->asb_clk[i])) | ||
185 | break; | ||
186 | } | ||
187 | |||
188 | pd->oscclk = of_clk_get_by_name(np, "oscclk"); | ||
189 | if (IS_ERR(pd->oscclk)) | ||
190 | goto no_clk; | ||
191 | |||
192 | for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { | ||
193 | char clk_name[8]; | ||
194 | |||
195 | snprintf(clk_name, sizeof(clk_name), "clk%d", i); | ||
196 | pd->clk[i] = of_clk_get_by_name(np, clk_name); | ||
197 | if (IS_ERR(pd->clk[i])) | ||
198 | break; | ||
199 | /* | ||
200 | * Skip setting parent on first power up. | ||
201 | * The parent at this time may not be useful at all. | ||
202 | */ | ||
203 | pd->pclk[i] = ERR_PTR(-EINVAL); | ||
204 | } | ||
205 | |||
206 | if (IS_ERR(pd->clk[0])) | ||
207 | clk_put(pd->oscclk); | ||
208 | |||
209 | no_clk: | ||
210 | on = readl_relaxed(pd->base + 0x4) & pd->local_pwr_cfg; | ||
211 | |||
212 | pm_genpd_init(&pd->pd, NULL, !on); | ||
213 | of_genpd_add_provider_simple(np, &pd->pd); | ||
214 | } | ||
215 | |||
216 | /* Assign the child power domains to their parents */ | ||
217 | for_each_matching_node(np, exynos_pm_domain_of_match) { | ||
218 | struct generic_pm_domain *child_domain, *parent_domain; | ||
219 | struct of_phandle_args args; | ||
220 | |||
221 | args.np = np; | ||
222 | args.args_count = 0; | ||
223 | child_domain = of_genpd_get_from_provider(&args); | ||
224 | if (IS_ERR(child_domain)) | ||
225 | continue; | ||
226 | |||
227 | if (of_parse_phandle_with_args(np, "power-domains", | ||
228 | "#power-domain-cells", 0, &args) != 0) | ||
229 | continue; | ||
230 | |||
231 | parent_domain = of_genpd_get_from_provider(&args); | ||
232 | if (IS_ERR(parent_domain)) | ||
233 | continue; | ||
234 | |||
235 | if (pm_genpd_add_subdomain(parent_domain, child_domain)) | ||
236 | pr_warn("%s failed to add subdomain: %s\n", | ||
237 | parent_domain->name, child_domain->name); | ||
238 | else | ||
239 | pr_info("%s has as child subdomain: %s.\n", | ||
240 | parent_domain->name, child_domain->name); | ||
241 | } | ||
242 | |||
243 | return 0; | ||
244 | } | ||
245 | core_initcall(exynos4_pm_init_power_domain); | ||
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index bb173456bbff..71c834f3847e 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c | |||
@@ -51,6 +51,7 @@ | |||
51 | #define PMC_CNTRL_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */ | 51 | #define PMC_CNTRL_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */ |
52 | #define PMC_CNTRL_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ | 52 | #define PMC_CNTRL_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ |
53 | #define PMC_CNTRL_INTR_POLARITY (1 << 17) /* inverts INTR polarity */ | 53 | #define PMC_CNTRL_INTR_POLARITY (1 << 17) /* inverts INTR polarity */ |
54 | #define PMC_CNTRL_MAIN_RST (1 << 4) | ||
54 | 55 | ||
55 | #define DPD_SAMPLE 0x020 | 56 | #define DPD_SAMPLE 0x020 |
56 | #define DPD_SAMPLE_ENABLE (1 << 0) | 57 | #define DPD_SAMPLE_ENABLE (1 << 0) |
@@ -80,6 +81,14 @@ | |||
80 | #define PMC_SENSOR_CTRL_SCRATCH_WRITE (1 << 2) | 81 | #define PMC_SENSOR_CTRL_SCRATCH_WRITE (1 << 2) |
81 | #define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1) | 82 | #define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1) |
82 | 83 | ||
84 | #define PMC_RST_STATUS 0x1b4 | ||
85 | #define PMC_RST_STATUS_POR 0 | ||
86 | #define PMC_RST_STATUS_WATCHDOG 1 | ||
87 | #define PMC_RST_STATUS_SENSOR 2 | ||
88 | #define PMC_RST_STATUS_SW_MAIN 3 | ||
89 | #define PMC_RST_STATUS_LP0 4 | ||
90 | #define PMC_RST_STATUS_AOTAG 5 | ||
91 | |||
83 | #define IO_DPD_REQ 0x1b8 | 92 | #define IO_DPD_REQ 0x1b8 |
84 | #define IO_DPD_REQ_CODE_IDLE (0 << 30) | 93 | #define IO_DPD_REQ_CODE_IDLE (0 << 30) |
85 | #define IO_DPD_REQ_CODE_OFF (1 << 30) | 94 | #define IO_DPD_REQ_CODE_OFF (1 << 30) |
@@ -399,6 +408,7 @@ static int tegra_powergate_power_up(struct tegra_powergate *pg, | |||
399 | disable_clks: | 408 | disable_clks: |
400 | tegra_powergate_disable_clocks(pg); | 409 | tegra_powergate_disable_clocks(pg); |
401 | usleep_range(10, 20); | 410 | usleep_range(10, 20); |
411 | |||
402 | powergate_off: | 412 | powergate_off: |
403 | tegra_powergate_set(pg->id, false); | 413 | tegra_powergate_set(pg->id, false); |
404 | 414 | ||
@@ -436,6 +446,7 @@ assert_resets: | |||
436 | usleep_range(10, 20); | 446 | usleep_range(10, 20); |
437 | tegra_powergate_reset_deassert(pg); | 447 | tegra_powergate_reset_deassert(pg); |
438 | usleep_range(10, 20); | 448 | usleep_range(10, 20); |
449 | |||
439 | disable_clks: | 450 | disable_clks: |
440 | tegra_powergate_disable_clocks(pg); | 451 | tegra_powergate_disable_clocks(pg); |
441 | 452 | ||
@@ -540,6 +551,9 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, | |||
540 | struct tegra_powergate pg; | 551 | struct tegra_powergate pg; |
541 | int err; | 552 | int err; |
542 | 553 | ||
554 | if (!tegra_powergate_is_available(id)) | ||
555 | return -EINVAL; | ||
556 | |||
543 | pg.id = id; | 557 | pg.id = id; |
544 | pg.clks = &clk; | 558 | pg.clks = &clk; |
545 | pg.num_clks = 1; | 559 | pg.num_clks = 1; |
@@ -638,9 +652,10 @@ static int tegra_pmc_restart_notify(struct notifier_block *this, | |||
638 | 652 | ||
639 | tegra_pmc_writel(value, PMC_SCRATCH0); | 653 | tegra_pmc_writel(value, PMC_SCRATCH0); |
640 | 654 | ||
641 | value = tegra_pmc_readl(0); | 655 | /* reset everything but PMC_SCRATCH0 and PMC_RST_STATUS */ |
642 | value |= 0x10; | 656 | value = tegra_pmc_readl(PMC_CNTRL); |
643 | tegra_pmc_writel(value, 0); | 657 | value |= PMC_CNTRL_MAIN_RST; |
658 | tegra_pmc_writel(value, PMC_CNTRL); | ||
644 | 659 | ||
645 | return NOTIFY_DONE; | 660 | return NOTIFY_DONE; |
646 | } | 661 | } |
@@ -722,13 +737,14 @@ static int tegra_powergate_of_get_clks(struct tegra_powergate *pg, | |||
722 | err: | 737 | err: |
723 | while (i--) | 738 | while (i--) |
724 | clk_put(pg->clks[i]); | 739 | clk_put(pg->clks[i]); |
740 | |||
725 | kfree(pg->clks); | 741 | kfree(pg->clks); |
726 | 742 | ||
727 | return err; | 743 | return err; |
728 | } | 744 | } |
729 | 745 | ||
730 | static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, | 746 | static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, |
731 | struct device_node *np) | 747 | struct device_node *np, bool off) |
732 | { | 748 | { |
733 | struct reset_control *rst; | 749 | struct reset_control *rst; |
734 | unsigned int i, count; | 750 | unsigned int i, count; |
@@ -748,6 +764,16 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, | |||
748 | err = PTR_ERR(pg->resets[i]); | 764 | err = PTR_ERR(pg->resets[i]); |
749 | goto error; | 765 | goto error; |
750 | } | 766 | } |
767 | |||
768 | if (off) | ||
769 | err = reset_control_assert(pg->resets[i]); | ||
770 | else | ||
771 | err = reset_control_deassert(pg->resets[i]); | ||
772 | |||
773 | if (err) { | ||
774 | reset_control_put(pg->resets[i]); | ||
775 | goto error; | ||
776 | } | ||
751 | } | 777 | } |
752 | 778 | ||
753 | pg->num_resets = count; | 779 | pg->num_resets = count; |
@@ -757,6 +783,7 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, | |||
757 | error: | 783 | error: |
758 | while (i--) | 784 | while (i--) |
759 | reset_control_put(pg->resets[i]); | 785 | reset_control_put(pg->resets[i]); |
786 | |||
760 | kfree(pg->resets); | 787 | kfree(pg->resets); |
761 | 788 | ||
762 | return err; | 789 | return err; |
@@ -765,16 +792,19 @@ error: | |||
765 | static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) | 792 | static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) |
766 | { | 793 | { |
767 | struct tegra_powergate *pg; | 794 | struct tegra_powergate *pg; |
795 | int id, err; | ||
768 | bool off; | 796 | bool off; |
769 | int id; | ||
770 | 797 | ||
771 | pg = kzalloc(sizeof(*pg), GFP_KERNEL); | 798 | pg = kzalloc(sizeof(*pg), GFP_KERNEL); |
772 | if (!pg) | 799 | if (!pg) |
773 | goto error; | 800 | return; |
774 | 801 | ||
775 | id = tegra_powergate_lookup(pmc, np->name); | 802 | id = tegra_powergate_lookup(pmc, np->name); |
776 | if (id < 0) | 803 | if (id < 0) { |
804 | dev_err(pmc->dev, "powergate lookup failed for %s: %d\n", | ||
805 | np->name, id); | ||
777 | goto free_mem; | 806 | goto free_mem; |
807 | } | ||
778 | 808 | ||
779 | /* | 809 | /* |
780 | * Clear the bit for this powergate so it cannot be managed | 810 | * Clear the bit for this powergate so it cannot be managed |
@@ -788,31 +818,64 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) | |||
788 | pg->genpd.power_on = tegra_genpd_power_on; | 818 | pg->genpd.power_on = tegra_genpd_power_on; |
789 | pg->pmc = pmc; | 819 | pg->pmc = pmc; |
790 | 820 | ||
791 | if (tegra_powergate_of_get_clks(pg, np)) | 821 | off = !tegra_powergate_is_powered(pg->id); |
822 | |||
823 | err = tegra_powergate_of_get_clks(pg, np); | ||
824 | if (err < 0) { | ||
825 | dev_err(pmc->dev, "failed to get clocks for %s: %d\n", | ||
826 | np->name, err); | ||
792 | goto set_available; | 827 | goto set_available; |
828 | } | ||
793 | 829 | ||
794 | if (tegra_powergate_of_get_resets(pg, np)) | 830 | err = tegra_powergate_of_get_resets(pg, np, off); |
831 | if (err < 0) { | ||
832 | dev_err(pmc->dev, "failed to get resets for %s: %d\n", | ||
833 | np->name, err); | ||
795 | goto remove_clks; | 834 | goto remove_clks; |
835 | } | ||
796 | 836 | ||
797 | off = !tegra_powergate_is_powered(pg->id); | 837 | if (!IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) |
838 | goto power_on_cleanup; | ||
839 | |||
840 | /* | ||
841 | * FIXME: If XHCI is enabled for Tegra, then power-up the XUSB | ||
842 | * host and super-speed partitions. Once the XHCI driver | ||
843 | * manages the partitions itself this code can be removed. Note | ||
844 | * that we don't register these partitions with the genpd core | ||
845 | * to avoid it from powering down the partitions as they appear | ||
846 | * to be unused. | ||
847 | */ | ||
848 | if (IS_ENABLED(CONFIG_USB_XHCI_TEGRA) && | ||
849 | (id == TEGRA_POWERGATE_XUSBA || id == TEGRA_POWERGATE_XUSBC)) | ||
850 | goto power_on_cleanup; | ||
798 | 851 | ||
799 | pm_genpd_init(&pg->genpd, NULL, off); | 852 | pm_genpd_init(&pg->genpd, NULL, off); |
800 | 853 | ||
801 | if (of_genpd_add_provider_simple(np, &pg->genpd)) | 854 | err = of_genpd_add_provider_simple(np, &pg->genpd); |
855 | if (err < 0) { | ||
856 | dev_err(pmc->dev, "failed to add genpd provider for %s: %d\n", | ||
857 | np->name, err); | ||
802 | goto remove_resets; | 858 | goto remove_resets; |
859 | } | ||
803 | 860 | ||
804 | dev_dbg(pmc->dev, "added power domain %s\n", pg->genpd.name); | 861 | dev_dbg(pmc->dev, "added power domain %s\n", pg->genpd.name); |
805 | 862 | ||
806 | return; | 863 | return; |
807 | 864 | ||
865 | power_on_cleanup: | ||
866 | if (off) | ||
867 | WARN_ON(tegra_powergate_power_up(pg, true)); | ||
868 | |||
808 | remove_resets: | 869 | remove_resets: |
809 | while (pg->num_resets--) | 870 | while (pg->num_resets--) |
810 | reset_control_put(pg->resets[pg->num_resets]); | 871 | reset_control_put(pg->resets[pg->num_resets]); |
872 | |||
811 | kfree(pg->resets); | 873 | kfree(pg->resets); |
812 | 874 | ||
813 | remove_clks: | 875 | remove_clks: |
814 | while (pg->num_clks--) | 876 | while (pg->num_clks--) |
815 | clk_put(pg->clks[pg->num_clks]); | 877 | clk_put(pg->clks[pg->num_clks]); |
878 | |||
816 | kfree(pg->clks); | 879 | kfree(pg->clks); |
817 | 880 | ||
818 | set_available: | 881 | set_available: |
@@ -820,16 +883,20 @@ set_available: | |||
820 | 883 | ||
821 | free_mem: | 884 | free_mem: |
822 | kfree(pg); | 885 | kfree(pg); |
823 | |||
824 | error: | ||
825 | dev_err(pmc->dev, "failed to create power domain for %s\n", np->name); | ||
826 | } | 886 | } |
827 | 887 | ||
828 | static void tegra_powergate_init(struct tegra_pmc *pmc) | 888 | static void tegra_powergate_init(struct tegra_pmc *pmc, |
889 | struct device_node *parent) | ||
829 | { | 890 | { |
830 | struct device_node *np, *child; | 891 | struct device_node *np, *child; |
892 | unsigned int i; | ||
893 | |||
894 | /* Create a bitmap of the available and valid partitions */ | ||
895 | for (i = 0; i < pmc->soc->num_powergates; i++) | ||
896 | if (pmc->soc->powergates[i]) | ||
897 | set_bit(i, pmc->powergates_available); | ||
831 | 898 | ||
832 | np = of_get_child_by_name(pmc->dev->of_node, "powergates"); | 899 | np = of_get_child_by_name(parent, "powergates"); |
833 | if (!np) | 900 | if (!np) |
834 | return; | 901 | return; |
835 | 902 | ||
@@ -1205,6 +1272,14 @@ static int tegra_pmc_probe(struct platform_device *pdev) | |||
1205 | struct resource *res; | 1272 | struct resource *res; |
1206 | int err; | 1273 | int err; |
1207 | 1274 | ||
1275 | /* | ||
1276 | * Early initialisation should have configured an initial | ||
1277 | * register mapping and setup the soc data pointer. If these | ||
1278 | * are not valid then something went badly wrong! | ||
1279 | */ | ||
1280 | if (WARN_ON(!pmc->base || !pmc->soc)) | ||
1281 | return -ENODEV; | ||
1282 | |||
1208 | err = tegra_pmc_parse_dt(pmc, pdev->dev.of_node); | 1283 | err = tegra_pmc_parse_dt(pmc, pdev->dev.of_node); |
1209 | if (err < 0) | 1284 | if (err < 0) |
1210 | return err; | 1285 | return err; |
@@ -1242,8 +1317,6 @@ static int tegra_pmc_probe(struct platform_device *pdev) | |||
1242 | return err; | 1317 | return err; |
1243 | } | 1318 | } |
1244 | 1319 | ||
1245 | tegra_powergate_init(pmc); | ||
1246 | |||
1247 | mutex_lock(&pmc->powergates_lock); | 1320 | mutex_lock(&pmc->powergates_lock); |
1248 | iounmap(pmc->base); | 1321 | iounmap(pmc->base); |
1249 | pmc->base = base; | 1322 | pmc->base = base; |
@@ -1477,10 +1550,11 @@ static int __init tegra_pmc_early_init(void) | |||
1477 | const struct of_device_id *match; | 1550 | const struct of_device_id *match; |
1478 | struct device_node *np; | 1551 | struct device_node *np; |
1479 | struct resource regs; | 1552 | struct resource regs; |
1480 | unsigned int i; | ||
1481 | bool invert; | 1553 | bool invert; |
1482 | u32 value; | 1554 | u32 value; |
1483 | 1555 | ||
1556 | mutex_init(&pmc->powergates_lock); | ||
1557 | |||
1484 | np = of_find_matching_node_and_match(NULL, tegra_pmc_match, &match); | 1558 | np = of_find_matching_node_and_match(NULL, tegra_pmc_match, &match); |
1485 | if (!np) { | 1559 | if (!np) { |
1486 | /* | 1560 | /* |
@@ -1515,39 +1589,40 @@ static int __init tegra_pmc_early_init(void) | |||
1515 | */ | 1589 | */ |
1516 | if (of_address_to_resource(np, 0, ®s) < 0) { | 1590 | if (of_address_to_resource(np, 0, ®s) < 0) { |
1517 | pr_err("failed to get PMC registers\n"); | 1591 | pr_err("failed to get PMC registers\n"); |
1592 | of_node_put(np); | ||
1518 | return -ENXIO; | 1593 | return -ENXIO; |
1519 | } | 1594 | } |
1520 | |||
1521 | pmc->soc = match->data; | ||
1522 | } | 1595 | } |
1523 | 1596 | ||
1524 | pmc->base = ioremap_nocache(regs.start, resource_size(®s)); | 1597 | pmc->base = ioremap_nocache(regs.start, resource_size(®s)); |
1525 | if (!pmc->base) { | 1598 | if (!pmc->base) { |
1526 | pr_err("failed to map PMC registers\n"); | 1599 | pr_err("failed to map PMC registers\n"); |
1600 | of_node_put(np); | ||
1527 | return -ENXIO; | 1601 | return -ENXIO; |
1528 | } | 1602 | } |
1529 | 1603 | ||
1530 | /* Create a bit-map of the available and valid partitions */ | 1604 | if (np) { |
1531 | for (i = 0; i < pmc->soc->num_powergates; i++) | 1605 | pmc->soc = match->data; |
1532 | if (pmc->soc->powergates[i]) | ||
1533 | set_bit(i, pmc->powergates_available); | ||
1534 | 1606 | ||
1535 | mutex_init(&pmc->powergates_lock); | 1607 | tegra_powergate_init(pmc, np); |
1536 | 1608 | ||
1537 | /* | 1609 | /* |
1538 | * Invert the interrupt polarity if a PMC device tree node exists and | 1610 | * Invert the interrupt polarity if a PMC device tree node |
1539 | * contains the nvidia,invert-interrupt property. | 1611 | * exists and contains the nvidia,invert-interrupt property. |
1540 | */ | 1612 | */ |
1541 | invert = of_property_read_bool(np, "nvidia,invert-interrupt"); | 1613 | invert = of_property_read_bool(np, "nvidia,invert-interrupt"); |
1542 | 1614 | ||
1543 | value = tegra_pmc_readl(PMC_CNTRL); | 1615 | value = tegra_pmc_readl(PMC_CNTRL); |
1544 | 1616 | ||
1545 | if (invert) | 1617 | if (invert) |
1546 | value |= PMC_CNTRL_INTR_POLARITY; | 1618 | value |= PMC_CNTRL_INTR_POLARITY; |
1547 | else | 1619 | else |
1548 | value &= ~PMC_CNTRL_INTR_POLARITY; | 1620 | value &= ~PMC_CNTRL_INTR_POLARITY; |
1549 | 1621 | ||
1550 | tegra_pmc_writel(value, PMC_CNTRL); | 1622 | tegra_pmc_writel(value, PMC_CNTRL); |
1623 | |||
1624 | of_node_put(np); | ||
1625 | } | ||
1551 | 1626 | ||
1552 | return 0; | 1627 | return 0; |
1553 | } | 1628 | } |
diff --git a/drivers/soc/ux500/Kconfig b/drivers/soc/ux500/Kconfig new file mode 100644 index 000000000000..025a44aef5db --- /dev/null +++ b/drivers/soc/ux500/Kconfig | |||
@@ -0,0 +1,7 @@ | |||
1 | config UX500_SOC_ID | ||
2 | bool "SoC bus for ST-Ericsson ux500" | ||
3 | depends on ARCH_U8500 || COMPILE_TEST | ||
4 | default ARCH_U8500 | ||
5 | help | ||
6 | Include support for the SoC bus on the ARM RealView platforms | ||
7 | providing some sysfs information about the ASIC variant. | ||
diff --git a/drivers/soc/ux500/Makefile b/drivers/soc/ux500/Makefile new file mode 100644 index 000000000000..0b87ad04b018 --- /dev/null +++ b/drivers/soc/ux500/Makefile | |||
@@ -0,0 +1 @@ | |||
obj-$(CONFIG_UX500_SOC_ID) += ux500-soc-id.o | |||
diff --git a/drivers/soc/ux500/ux500-soc-id.c b/drivers/soc/ux500/ux500-soc-id.c new file mode 100644 index 000000000000..6c1be74e5fcc --- /dev/null +++ b/drivers/soc/ux500/ux500-soc-id.c | |||
@@ -0,0 +1,222 @@ | |||
1 | /* | ||
2 | * Copyright (C) ST-Ericsson SA 2010 | ||
3 | * | ||
4 | * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson | ||
5 | * License terms: GNU General Public License (GPL) version 2 | ||
6 | */ | ||
7 | |||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/init.h> | ||
10 | #include <linux/io.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/random.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/of.h> | ||
15 | #include <linux/of_address.h> | ||
16 | #include <linux/sys_soc.h> | ||
17 | |||
18 | #include <asm/cputype.h> | ||
19 | #include <asm/tlbflush.h> | ||
20 | #include <asm/cacheflush.h> | ||
21 | #include <asm/mach/map.h> | ||
22 | |||
23 | /** | ||
24 | * struct dbx500_asic_id - fields of the ASIC ID | ||
25 | * @process: the manufacturing process, 0x40 is 40 nm 0x00 is "standard" | ||
26 | * @partnumber: hithereto 0x8500 for DB8500 | ||
27 | * @revision: version code in the series | ||
28 | */ | ||
29 | struct dbx500_asic_id { | ||
30 | u16 partnumber; | ||
31 | u8 revision; | ||
32 | u8 process; | ||
33 | }; | ||
34 | |||
35 | static struct dbx500_asic_id dbx500_id; | ||
36 | |||
37 | static unsigned int __init ux500_read_asicid(phys_addr_t addr) | ||
38 | { | ||
39 | void __iomem *virt = ioremap(addr, 4); | ||
40 | unsigned int asicid; | ||
41 | |||
42 | if (!virt) | ||
43 | return 0; | ||
44 | |||
45 | asicid = readl(virt); | ||
46 | iounmap(virt); | ||
47 | |||
48 | return asicid; | ||
49 | } | ||
50 | |||
51 | static void ux500_print_soc_info(unsigned int asicid) | ||
52 | { | ||
53 | unsigned int rev = dbx500_id.revision; | ||
54 | |||
55 | pr_info("DB%4x ", dbx500_id.partnumber); | ||
56 | |||
57 | if (rev == 0x01) | ||
58 | pr_cont("Early Drop"); | ||
59 | else if (rev >= 0xA0) | ||
60 | pr_cont("v%d.%d" , (rev >> 4) - 0xA + 1, rev & 0xf); | ||
61 | else | ||
62 | pr_cont("Unknown"); | ||
63 | |||
64 | pr_cont(" [%#010x]\n", asicid); | ||
65 | } | ||
66 | |||
67 | static unsigned int partnumber(unsigned int asicid) | ||
68 | { | ||
69 | return (asicid >> 8) & 0xffff; | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * SOC MIDR ASICID ADDRESS ASICID VALUE | ||
74 | * DB8500ed 0x410fc090 0x9001FFF4 0x00850001 | ||
75 | * DB8500v1 0x411fc091 0x9001FFF4 0x008500A0 | ||
76 | * DB8500v1.1 0x411fc091 0x9001FFF4 0x008500A1 | ||
77 | * DB8500v2 0x412fc091 0x9001DBF4 0x008500B0 | ||
78 | * DB8520v2.2 0x412fc091 0x9001DBF4 0x008500B2 | ||
79 | * DB5500v1 0x412fc091 0x9001FFF4 0x005500A0 | ||
80 | * DB9540 0x413fc090 0xFFFFDBF4 0x009540xx | ||
81 | */ | ||
82 | |||
83 | static void __init ux500_setup_id(void) | ||
84 | { | ||
85 | unsigned int cpuid = read_cpuid_id(); | ||
86 | unsigned int asicid = 0; | ||
87 | phys_addr_t addr = 0; | ||
88 | |||
89 | switch (cpuid) { | ||
90 | case 0x410fc090: /* DB8500ed */ | ||
91 | case 0x411fc091: /* DB8500v1 */ | ||
92 | addr = 0x9001FFF4; | ||
93 | break; | ||
94 | |||
95 | case 0x412fc091: /* DB8520 / DB8500v2 / DB5500v1 */ | ||
96 | asicid = ux500_read_asicid(0x9001DBF4); | ||
97 | if (partnumber(asicid) == 0x8500 || | ||
98 | partnumber(asicid) == 0x8520) | ||
99 | /* DB8500v2 */ | ||
100 | break; | ||
101 | |||
102 | /* DB5500v1 */ | ||
103 | addr = 0x9001FFF4; | ||
104 | break; | ||
105 | |||
106 | case 0x413fc090: /* DB9540 */ | ||
107 | addr = 0xFFFFDBF4; | ||
108 | break; | ||
109 | } | ||
110 | |||
111 | if (addr) | ||
112 | asicid = ux500_read_asicid(addr); | ||
113 | |||
114 | if (!asicid) { | ||
115 | pr_err("Unable to identify SoC\n"); | ||
116 | BUG(); | ||
117 | } | ||
118 | |||
119 | dbx500_id.process = asicid >> 24; | ||
120 | dbx500_id.partnumber = partnumber(asicid); | ||
121 | dbx500_id.revision = asicid & 0xff; | ||
122 | |||
123 | ux500_print_soc_info(asicid); | ||
124 | } | ||
125 | |||
126 | static const char * __init ux500_get_machine(void) | ||
127 | { | ||
128 | return kasprintf(GFP_KERNEL, "DB%4x", dbx500_id.partnumber); | ||
129 | } | ||
130 | |||
131 | static const char * __init ux500_get_family(void) | ||
132 | { | ||
133 | return kasprintf(GFP_KERNEL, "ux500"); | ||
134 | } | ||
135 | |||
136 | static const char * __init ux500_get_revision(void) | ||
137 | { | ||
138 | unsigned int rev = dbx500_id.revision; | ||
139 | |||
140 | if (rev == 0x01) | ||
141 | return kasprintf(GFP_KERNEL, "%s", "ED"); | ||
142 | else if (rev >= 0xA0) | ||
143 | return kasprintf(GFP_KERNEL, "%d.%d", | ||
144 | (rev >> 4) - 0xA + 1, rev & 0xf); | ||
145 | |||
146 | return kasprintf(GFP_KERNEL, "%s", "Unknown"); | ||
147 | } | ||
148 | |||
149 | static ssize_t ux500_get_process(struct device *dev, | ||
150 | struct device_attribute *attr, | ||
151 | char *buf) | ||
152 | { | ||
153 | if (dbx500_id.process == 0x00) | ||
154 | return sprintf(buf, "Standard\n"); | ||
155 | |||
156 | return sprintf(buf, "%02xnm\n", dbx500_id.process); | ||
157 | } | ||
158 | |||
159 | static const char *db8500_read_soc_id(struct device_node *backupram) | ||
160 | { | ||
161 | void __iomem *base; | ||
162 | void __iomem *uid; | ||
163 | const char *retstr; | ||
164 | |||
165 | base = of_iomap(backupram, 0); | ||
166 | if (!base) | ||
167 | return NULL; | ||
168 | uid = base + 0x1fc0; | ||
169 | |||
170 | /* Throw these device-specific numbers into the entropy pool */ | ||
171 | add_device_randomness(uid, 0x14); | ||
172 | retstr = kasprintf(GFP_KERNEL, "%08x%08x%08x%08x%08x", | ||
173 | readl((u32 *)uid+0), | ||
174 | readl((u32 *)uid+1), readl((u32 *)uid+2), | ||
175 | readl((u32 *)uid+3), readl((u32 *)uid+4)); | ||
176 | iounmap(base); | ||
177 | return retstr; | ||
178 | } | ||
179 | |||
180 | static void __init soc_info_populate(struct soc_device_attribute *soc_dev_attr, | ||
181 | struct device_node *backupram) | ||
182 | { | ||
183 | soc_dev_attr->soc_id = db8500_read_soc_id(backupram); | ||
184 | soc_dev_attr->machine = ux500_get_machine(); | ||
185 | soc_dev_attr->family = ux500_get_family(); | ||
186 | soc_dev_attr->revision = ux500_get_revision(); | ||
187 | } | ||
188 | |||
189 | static const struct device_attribute ux500_soc_attr = | ||
190 | __ATTR(process, S_IRUGO, ux500_get_process, NULL); | ||
191 | |||
192 | static int __init ux500_soc_device_init(void) | ||
193 | { | ||
194 | struct device *parent; | ||
195 | struct soc_device *soc_dev; | ||
196 | struct soc_device_attribute *soc_dev_attr; | ||
197 | struct device_node *backupram; | ||
198 | |||
199 | backupram = of_find_compatible_node(NULL, NULL, "ste,dbx500-backupram"); | ||
200 | if (!backupram) | ||
201 | return 0; | ||
202 | |||
203 | ux500_setup_id(); | ||
204 | |||
205 | soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); | ||
206 | if (!soc_dev_attr) | ||
207 | return -ENOMEM; | ||
208 | |||
209 | soc_info_populate(soc_dev_attr, backupram); | ||
210 | |||
211 | soc_dev = soc_device_register(soc_dev_attr); | ||
212 | if (IS_ERR(soc_dev)) { | ||
213 | kfree(soc_dev_attr); | ||
214 | return PTR_ERR(soc_dev); | ||
215 | } | ||
216 | |||
217 | parent = soc_device_to_device(soc_dev); | ||
218 | device_create_file(parent, &ux500_soc_attr); | ||
219 | |||
220 | return 0; | ||
221 | } | ||
222 | subsys_initcall(ux500_soc_device_init); | ||
diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 5beafd2d2218..ac1328629baa 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c | |||
@@ -539,7 +539,7 @@ static int uart_clps711x_remove(struct platform_device *pdev) | |||
539 | } | 539 | } |
540 | 540 | ||
541 | static const struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = { | 541 | static const struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = { |
542 | { .compatible = "cirrus,clps711x-uart", }, | 542 | { .compatible = "cirrus,ep7209-uart", }, |
543 | { } | 543 | { } |
544 | }; | 544 | }; |
545 | MODULE_DEVICE_TABLE(of, clps711x_uart_dt_ids); | 545 | MODULE_DEVICE_TABLE(of, clps711x_uart_dt_ids); |
diff --git a/drivers/video/fbdev/clps711x-fb.c b/drivers/video/fbdev/clps711x-fb.c index 649b32f78c08..ff561073ee4e 100644 --- a/drivers/video/fbdev/clps711x-fb.c +++ b/drivers/video/fbdev/clps711x-fb.c | |||
@@ -273,7 +273,7 @@ static int clps711x_fb_probe(struct platform_device *pdev) | |||
273 | } | 273 | } |
274 | 274 | ||
275 | cfb->syscon = | 275 | cfb->syscon = |
276 | syscon_regmap_lookup_by_compatible("cirrus,clps711x-syscon1"); | 276 | syscon_regmap_lookup_by_compatible("cirrus,ep7209-syscon1"); |
277 | if (IS_ERR(cfb->syscon)) { | 277 | if (IS_ERR(cfb->syscon)) { |
278 | ret = PTR_ERR(cfb->syscon); | 278 | ret = PTR_ERR(cfb->syscon); |
279 | goto out_fb_release; | 279 | goto out_fb_release; |
@@ -376,7 +376,7 @@ static int clps711x_fb_remove(struct platform_device *pdev) | |||
376 | } | 376 | } |
377 | 377 | ||
378 | static const struct of_device_id clps711x_fb_dt_ids[] = { | 378 | static const struct of_device_id clps711x_fb_dt_ids[] = { |
379 | { .compatible = "cirrus,clps711x-fb", }, | 379 | { .compatible = "cirrus,ep7209-fb", }, |
380 | { } | 380 | { } |
381 | }; | 381 | }; |
382 | MODULE_DEVICE_TABLE(of, clps711x_fb_dt_ids); | 382 | MODULE_DEVICE_TABLE(of, clps711x_fb_dt_ids); |