diff options
Diffstat (limited to 'drivers/remoteproc')
-rw-r--r-- | drivers/remoteproc/Kconfig | 13 | ||||
-rw-r--r-- | drivers/remoteproc/Makefile | 1 | ||||
-rw-r--r-- | drivers/remoteproc/wkup_m3_rproc.c | 257 |
3 files changed, 271 insertions, 0 deletions
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 5e343bab9458..28c711f0ac6b 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig | |||
@@ -41,6 +41,19 @@ config STE_MODEM_RPROC | |||
41 | This can be either built-in or a loadable module. | 41 | This can be either built-in or a loadable module. |
42 | If unsure say N. | 42 | If unsure say N. |
43 | 43 | ||
44 | config WKUP_M3_RPROC | ||
45 | tristate "AMx3xx Wakeup M3 remoteproc support" | ||
46 | depends on SOC_AM33XX || SOC_AM43XX | ||
47 | select REMOTEPROC | ||
48 | help | ||
49 | Say y here to support Wakeup M3 remote processor on TI AM33xx | ||
50 | and AM43xx family of SoCs. | ||
51 | |||
52 | Required for Suspend-to-RAM on AM33xx and AM43xx SoCs. Also needed | ||
53 | for deep CPUIdle states on AM33xx SoCs. Allows for loading of the | ||
54 | firmware onto these remote processors. | ||
55 | If unsure say N. | ||
56 | |||
44 | config DA8XX_REMOTEPROC | 57 | config DA8XX_REMOTEPROC |
45 | tristate "DA8xx/OMAP-L13x remoteproc support" | 58 | tristate "DA8xx/OMAP-L13x remoteproc support" |
46 | depends on ARCH_DAVINCI_DA8XX | 59 | depends on ARCH_DAVINCI_DA8XX |
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index ac2ff75686d2..81b04d1e2e58 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile | |||
@@ -9,4 +9,5 @@ remoteproc-y += remoteproc_virtio.o | |||
9 | remoteproc-y += remoteproc_elf_loader.o | 9 | remoteproc-y += remoteproc_elf_loader.o |
10 | obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o | 10 | obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o |
11 | obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o | 11 | obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o |
12 | obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o | ||
12 | obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o | 13 | obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o |
diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c new file mode 100644 index 000000000000..edf81819cce1 --- /dev/null +++ b/drivers/remoteproc/wkup_m3_rproc.c | |||
@@ -0,0 +1,257 @@ | |||
1 | /* | ||
2 | * TI AMx3 Wakeup M3 Remote Processor driver | ||
3 | * | ||
4 | * Copyright (C) 2014-2015 Texas Instruments, Inc. | ||
5 | * | ||
6 | * Dave Gerlach <d-gerlach@ti.com> | ||
7 | * Suman Anna <s-anna@ti.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * version 2 as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | */ | ||
18 | |||
19 | #include <linux/err.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/of_device.h> | ||
24 | #include <linux/of_address.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <linux/pm_runtime.h> | ||
27 | #include <linux/remoteproc.h> | ||
28 | |||
29 | #include <linux/platform_data/wkup_m3.h> | ||
30 | |||
31 | #include "remoteproc_internal.h" | ||
32 | |||
33 | #define WKUPM3_MEM_MAX 2 | ||
34 | |||
35 | /** | ||
36 | * struct wkup_m3_mem - WkupM3 internal memory structure | ||
37 | * @cpu_addr: MPU virtual address of the memory region | ||
38 | * @bus_addr: Bus address used to access the memory region | ||
39 | * @dev_addr: Device address from Wakeup M3 view | ||
40 | * @size: Size of the memory region | ||
41 | */ | ||
42 | struct wkup_m3_mem { | ||
43 | void __iomem *cpu_addr; | ||
44 | phys_addr_t bus_addr; | ||
45 | u32 dev_addr; | ||
46 | size_t size; | ||
47 | }; | ||
48 | |||
49 | /** | ||
50 | * struct wkup_m3_rproc - WkupM3 remote processor state | ||
51 | * @rproc: rproc handle | ||
52 | * @pdev: pointer to platform device | ||
53 | * @mem: WkupM3 memory information | ||
54 | */ | ||
55 | struct wkup_m3_rproc { | ||
56 | struct rproc *rproc; | ||
57 | struct platform_device *pdev; | ||
58 | struct wkup_m3_mem mem[WKUPM3_MEM_MAX]; | ||
59 | }; | ||
60 | |||
61 | static int wkup_m3_rproc_start(struct rproc *rproc) | ||
62 | { | ||
63 | struct wkup_m3_rproc *wkupm3 = rproc->priv; | ||
64 | struct platform_device *pdev = wkupm3->pdev; | ||
65 | struct device *dev = &pdev->dev; | ||
66 | struct wkup_m3_platform_data *pdata = dev_get_platdata(dev); | ||
67 | |||
68 | if (pdata->deassert_reset(pdev, pdata->reset_name)) { | ||
69 | dev_err(dev, "Unable to reset wkup_m3!\n"); | ||
70 | return -ENODEV; | ||
71 | } | ||
72 | |||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static int wkup_m3_rproc_stop(struct rproc *rproc) | ||
77 | { | ||
78 | struct wkup_m3_rproc *wkupm3 = rproc->priv; | ||
79 | struct platform_device *pdev = wkupm3->pdev; | ||
80 | struct device *dev = &pdev->dev; | ||
81 | struct wkup_m3_platform_data *pdata = dev_get_platdata(dev); | ||
82 | |||
83 | if (pdata->assert_reset(pdev, pdata->reset_name)) { | ||
84 | dev_err(dev, "Unable to assert reset of wkup_m3!\n"); | ||
85 | return -ENODEV; | ||
86 | } | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static void *wkup_m3_rproc_da_to_va(struct rproc *rproc, u64 da, int len) | ||
92 | { | ||
93 | struct wkup_m3_rproc *wkupm3 = rproc->priv; | ||
94 | void *va = NULL; | ||
95 | int i; | ||
96 | u32 offset; | ||
97 | |||
98 | if (len <= 0) | ||
99 | return NULL; | ||
100 | |||
101 | for (i = 0; i < WKUPM3_MEM_MAX; i++) { | ||
102 | if (da >= wkupm3->mem[i].dev_addr && da + len <= | ||
103 | wkupm3->mem[i].dev_addr + wkupm3->mem[i].size) { | ||
104 | offset = da - wkupm3->mem[i].dev_addr; | ||
105 | /* __force to make sparse happy with type conversion */ | ||
106 | va = (__force void *)(wkupm3->mem[i].cpu_addr + offset); | ||
107 | break; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | return va; | ||
112 | } | ||
113 | |||
114 | static struct rproc_ops wkup_m3_rproc_ops = { | ||
115 | .start = wkup_m3_rproc_start, | ||
116 | .stop = wkup_m3_rproc_stop, | ||
117 | .da_to_va = wkup_m3_rproc_da_to_va, | ||
118 | }; | ||
119 | |||
120 | static const struct of_device_id wkup_m3_rproc_of_match[] = { | ||
121 | { .compatible = "ti,am3352-wkup-m3", }, | ||
122 | { .compatible = "ti,am4372-wkup-m3", }, | ||
123 | {}, | ||
124 | }; | ||
125 | |||
126 | static int wkup_m3_rproc_probe(struct platform_device *pdev) | ||
127 | { | ||
128 | struct device *dev = &pdev->dev; | ||
129 | struct wkup_m3_platform_data *pdata = dev->platform_data; | ||
130 | /* umem always needs to be processed first */ | ||
131 | const char *mem_names[WKUPM3_MEM_MAX] = { "umem", "dmem" }; | ||
132 | struct wkup_m3_rproc *wkupm3; | ||
133 | const char *fw_name; | ||
134 | struct rproc *rproc; | ||
135 | struct resource *res; | ||
136 | const __be32 *addrp; | ||
137 | u32 l4_offset = 0; | ||
138 | u64 size; | ||
139 | int ret; | ||
140 | int i; | ||
141 | |||
142 | if (!(pdata && pdata->deassert_reset && pdata->assert_reset && | ||
143 | pdata->reset_name)) { | ||
144 | dev_err(dev, "Platform data missing!\n"); | ||
145 | return -ENODEV; | ||
146 | } | ||
147 | |||
148 | ret = of_property_read_string(dev->of_node, "ti,pm-firmware", | ||
149 | &fw_name); | ||
150 | if (ret) { | ||
151 | dev_err(dev, "No firmware filename given\n"); | ||
152 | return -ENODEV; | ||
153 | } | ||
154 | |||
155 | pm_runtime_enable(&pdev->dev); | ||
156 | ret = pm_runtime_get_sync(&pdev->dev); | ||
157 | if (ret < 0) { | ||
158 | dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n"); | ||
159 | goto err; | ||
160 | } | ||
161 | |||
162 | rproc = rproc_alloc(dev, "wkup_m3", &wkup_m3_rproc_ops, | ||
163 | fw_name, sizeof(*wkupm3)); | ||
164 | if (!rproc) { | ||
165 | ret = -ENOMEM; | ||
166 | goto err; | ||
167 | } | ||
168 | |||
169 | wkupm3 = rproc->priv; | ||
170 | wkupm3->rproc = rproc; | ||
171 | wkupm3->pdev = pdev; | ||
172 | |||
173 | for (i = 0; i < ARRAY_SIZE(mem_names); i++) { | ||
174 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | ||
175 | mem_names[i]); | ||
176 | wkupm3->mem[i].cpu_addr = devm_ioremap_resource(dev, res); | ||
177 | if (IS_ERR(wkupm3->mem[i].cpu_addr)) { | ||
178 | dev_err(&pdev->dev, "devm_ioremap_resource failed for resource %d\n", | ||
179 | i); | ||
180 | ret = PTR_ERR(wkupm3->mem[i].cpu_addr); | ||
181 | goto err; | ||
182 | } | ||
183 | wkupm3->mem[i].bus_addr = res->start; | ||
184 | wkupm3->mem[i].size = resource_size(res); | ||
185 | addrp = of_get_address(dev->of_node, i, &size, NULL); | ||
186 | /* | ||
187 | * The wkupm3 has umem at address 0 in its view, so the device | ||
188 | * addresses for each memory region is computed as a relative | ||
189 | * offset of the bus address for umem, and therefore needs to be | ||
190 | * processed first. | ||
191 | */ | ||
192 | if (!strcmp(mem_names[i], "umem")) | ||
193 | l4_offset = be32_to_cpu(*addrp); | ||
194 | wkupm3->mem[i].dev_addr = be32_to_cpu(*addrp) - l4_offset; | ||
195 | } | ||
196 | |||
197 | dev_set_drvdata(dev, rproc); | ||
198 | |||
199 | ret = rproc_add(rproc); | ||
200 | if (ret) { | ||
201 | dev_err(dev, "rproc_add failed\n"); | ||
202 | goto err_put_rproc; | ||
203 | } | ||
204 | |||
205 | return 0; | ||
206 | |||
207 | err_put_rproc: | ||
208 | rproc_put(rproc); | ||
209 | err: | ||
210 | pm_runtime_put_noidle(dev); | ||
211 | pm_runtime_disable(dev); | ||
212 | return ret; | ||
213 | } | ||
214 | |||
215 | static int wkup_m3_rproc_remove(struct platform_device *pdev) | ||
216 | { | ||
217 | struct rproc *rproc = platform_get_drvdata(pdev); | ||
218 | |||
219 | rproc_del(rproc); | ||
220 | rproc_put(rproc); | ||
221 | pm_runtime_put_sync(&pdev->dev); | ||
222 | pm_runtime_disable(&pdev->dev); | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | #ifdef CONFIG_PM | ||
228 | static int wkup_m3_rpm_suspend(struct device *dev) | ||
229 | { | ||
230 | return -EBUSY; | ||
231 | } | ||
232 | |||
233 | static int wkup_m3_rpm_resume(struct device *dev) | ||
234 | { | ||
235 | return 0; | ||
236 | } | ||
237 | #endif | ||
238 | |||
239 | static const struct dev_pm_ops wkup_m3_rproc_pm_ops = { | ||
240 | SET_RUNTIME_PM_OPS(wkup_m3_rpm_suspend, wkup_m3_rpm_resume, NULL) | ||
241 | }; | ||
242 | |||
243 | static struct platform_driver wkup_m3_rproc_driver = { | ||
244 | .probe = wkup_m3_rproc_probe, | ||
245 | .remove = wkup_m3_rproc_remove, | ||
246 | .driver = { | ||
247 | .name = "wkup_m3_rproc", | ||
248 | .of_match_table = wkup_m3_rproc_of_match, | ||
249 | .pm = &wkup_m3_rproc_pm_ops, | ||
250 | }, | ||
251 | }; | ||
252 | |||
253 | module_platform_driver(wkup_m3_rproc_driver); | ||
254 | |||
255 | MODULE_LICENSE("GPL v2"); | ||
256 | MODULE_DESCRIPTION("TI Wakeup M3 remote processor control driver"); | ||
257 | MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>"); | ||