summaryrefslogtreecommitdiffstats
path: root/drivers/remoteproc
diff options
context:
space:
mode:
authorDave Gerlach <d-gerlach@ti.com>2015-05-22 16:45:30 -0400
committerOhad Ben-Cohen <ohad@wizery.com>2015-06-17 02:58:08 -0400
commita01bc0d5f557bd8becd0ba75d09c39192004697e (patch)
treea91c592b18f56241b568f3b83d9d247a3863bcc3 /drivers/remoteproc
parentccbbb9faac946ce61c241ce9f08b3486fabf031d (diff)
remoteproc/wkup_m3: add a remoteproc driver for TI Wakeup M3
Add a remoteproc driver to load the firmware and boot a small Wakeup M3 processor present on TI AM33xx and AM43xx SoCs. This Wakeup M3 remote processor is an integrated Cortex M3 that allows the SoC to enter the lowest possible power state by taking control from the MPU after it has gone into its own low power state and shutting off any additional peripherals. The Wakeup M3 processor has two internal memory regions - 16 kB of unified instruction memory called UMEM used to store executable code, and 8 kB of data memory called DMEM used for all data sections. The Wakeup M3 processor executes its code entirely from within the UMEM and uses the DMEM for any data. It does not use any external memory or any other external resources. The device address view has the UMEM at address 0x0 and DMEM at address 0x80000, and these are computed automatically within the driver based on relative address calculation from the corresponding device tree IOMEM resources. These device addresses are used to aid the core remoteproc ELF loader code to properly translate and load the firmware segments through the .rproc_da_to_va ops. Signed-off-by: Dave Gerlach <d-gerlach@ti.com> Signed-off-by: Suman Anna <s-anna@ti.com> Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
Diffstat (limited to 'drivers/remoteproc')
-rw-r--r--drivers/remoteproc/Kconfig13
-rw-r--r--drivers/remoteproc/Makefile1
-rw-r--r--drivers/remoteproc/wkup_m3_rproc.c257
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
44config 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
44config DA8XX_REMOTEPROC 57config 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
9remoteproc-y += remoteproc_elf_loader.o 9remoteproc-y += remoteproc_elf_loader.o
10obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o 10obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
11obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o 11obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o
12obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o
12obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o 13obj-$(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 */
42struct 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 */
55struct wkup_m3_rproc {
56 struct rproc *rproc;
57 struct platform_device *pdev;
58 struct wkup_m3_mem mem[WKUPM3_MEM_MAX];
59};
60
61static 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
76static 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
91static 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
114static 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
120static 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
126static 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
207err_put_rproc:
208 rproc_put(rproc);
209err:
210 pm_runtime_put_noidle(dev);
211 pm_runtime_disable(dev);
212 return ret;
213}
214
215static 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
228static int wkup_m3_rpm_suspend(struct device *dev)
229{
230 return -EBUSY;
231}
232
233static int wkup_m3_rpm_resume(struct device *dev)
234{
235 return 0;
236}
237#endif
238
239static 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
243static 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
253module_platform_driver(wkup_m3_rproc_driver);
254
255MODULE_LICENSE("GPL v2");
256MODULE_DESCRIPTION("TI Wakeup M3 remote processor control driver");
257MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>");