1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * PRU-ICSS SoC bus driver for various TI SoCs
4 *
5 * Copyright (C) 2016-2019 Texas Instruments Incorporated - http://www.ti.com/
6 * Suman Anna <s-anna@ti.com>
7 * Keerthy <j-keerthy@ti.com>
8 */
10 #include <linux/delay.h>
11 #include <linux/io.h>
12 #include <linux/module.h>
13 #include <linux/of_platform.h>
14 #include <linux/of_address.h>
15 #include <linux/platform_device.h>
16 #include <linux/pm_runtime.h>
18 #include <linux/platform_data/ti-pruss.h>
20 #define SYSCFG_STANDBY_INIT BIT(4)
21 #define SYSCFG_SUB_MWAIT_READY BIT(5)
23 #define SYSCFG_STANDBY_MODE_FORCE (0 << 2)
24 #define SYSCFG_STANDBY_MODE_NO (1 << 2)
25 #define SYSCFG_STANDBY_MODE_SMART (2 << 2)
26 #define SYSCFG_STANDBY_MODE_MASK (3 << 2)
28 #define SYSCFG_IDLE_MODE_FORCE 0
29 #define SYSCFG_IDLE_MODE_NO 1
30 #define SYSCFG_IDLE_MODE_SMART 2
31 #define SYSCFG_IDLE_MODE_MASK 3
33 /**
34 * struct pruss_soc_bus - PRUSS SoC bus structure
35 * @syscfg: kernel mapped address for SYSCFG register
36 * @in_standby: flag for storing standby status
37 * @has_reset: cached variable for storing global module reset flag
38 * @skip_syscfg: flag to indicate if PRCM master standby/slave idle is needed
39 */
40 struct pruss_soc_bus {
41 void __iomem *syscfg;
42 bool in_standby;
43 bool has_reset;
44 bool skip_syscfg;
45 };
47 /**
48 * struct pruss_soc_bus_match_data - PRUSS SoC bus driver match data
49 * @has_reset: flag to indicate the presence of global module reset
50 * @uses_prcm: flag to indicate the usage of PRCM master standby/slave idle
51 * protocol
52 */
53 struct pruss_soc_bus_match_data {
54 bool has_reset;
55 bool uses_prcm;
56 };
58 static inline void pruss_soc_bus_rmw(void __iomem *reg, u32 mask, u32 set)
59 {
60 u32 val;
62 val = readl_relaxed(reg);
63 val &= ~mask;
64 val |= (set & mask);
65 writel_relaxed(val, reg);
66 }
68 /*
69 * This function programs the PRUSS_SYSCFG.STANDBY_INIT bit to achieve dual
70 * functionalities - one is to deassert the MStandby signal to the device
71 * PRCM, and the other is to enable OCP master ports to allow accesses
72 * outside of the PRU-ICSS. The function has to wait for the PRCM to
73 * acknowledge through the monitoring of the PRUSS_SYSCFG.SUB_MWAIT bit.
74 */
75 static int pruss_soc_bus_enable_ocp_master_ports(struct device *dev)
76 {
77 struct pruss_soc_bus *psoc_bus = dev_get_drvdata(dev);
78 u32 syscfg_val, i;
79 bool ready = false;
81 pruss_soc_bus_rmw(psoc_bus->syscfg, SYSCFG_STANDBY_INIT, 0);
83 /* wait till we are ready for transactions - delay is arbitrary */
84 for (i = 0; i < 10; i++) {
85 syscfg_val = readl_relaxed(psoc_bus->syscfg);
86 ready = !(syscfg_val & SYSCFG_SUB_MWAIT_READY);
87 if (ready)
88 break;
89 udelay(5);
90 }
92 if (!ready) {
93 dev_err(dev, "timeout waiting for SUB_MWAIT_READY\n");
94 return -ETIMEDOUT;
95 }
97 return 0;
98 }
100 static int __maybe_unused pruss_soc_bus_suspend(struct device *dev)
101 {
102 struct pruss_soc_bus *psoc_bus = dev_get_drvdata(dev);
103 u32 syscfg_val;
105 if (psoc_bus->skip_syscfg)
106 return 0;
108 syscfg_val = readl_relaxed(psoc_bus->syscfg);
109 psoc_bus->in_standby = syscfg_val & SYSCFG_STANDBY_INIT;
111 /* initiate MStandby, undo the MStandby config in probe */
112 if (!psoc_bus->in_standby) {
113 pruss_soc_bus_rmw(psoc_bus->syscfg, SYSCFG_STANDBY_INIT,
114 SYSCFG_STANDBY_INIT);
115 }
117 return 0;
118 }
120 static int __maybe_unused pruss_soc_bus_resume(struct device *dev)
121 {
122 struct pruss_soc_bus *psoc_bus = dev_get_drvdata(dev);
123 int ret = 0;
125 /* re-enable OCP master ports/disable MStandby */
126 if (!psoc_bus->skip_syscfg && !psoc_bus->in_standby) {
127 ret = pruss_soc_bus_enable_ocp_master_ports(dev);
128 if (ret)
129 dev_err(dev, "%s failed\n", __func__);
130 }
132 return ret;
133 }
135 /* firmware must be idle when calling this function */
136 static void pruss_disable_module(struct device *dev)
137 {
138 struct pruss_soc_bus *psoc_bus = dev_get_drvdata(dev);
140 if (psoc_bus->skip_syscfg)
141 goto put_sync;
143 /* configure Smart Standby */
144 pruss_soc_bus_rmw(psoc_bus->syscfg, SYSCFG_STANDBY_MODE_MASK,
145 SYSCFG_STANDBY_MODE_SMART);
147 /* initiate MStandby */
148 pruss_soc_bus_rmw(psoc_bus->syscfg, SYSCFG_STANDBY_INIT,
149 SYSCFG_STANDBY_INIT);
151 put_sync:
152 /* initiate IDLE request, disable clocks */
153 pm_runtime_put_sync(dev);
154 }
156 static int pruss_enable_module(struct device *dev)
157 {
158 struct pruss_soc_bus *psoc_bus = dev_get_drvdata(dev);
159 int ret;
161 /* enable clocks, de-assert IDLE request */
162 ret = pm_runtime_get_sync(dev);
163 if (ret < 0) {
164 pm_runtime_put_noidle(dev);
165 return ret;
166 }
168 if (psoc_bus->skip_syscfg)
169 return ret;
171 /* configure for Smart Idle & Smart Standby */
172 pruss_soc_bus_rmw(psoc_bus->syscfg, SYSCFG_IDLE_MODE_MASK,
173 SYSCFG_IDLE_MODE_SMART);
174 pruss_soc_bus_rmw(psoc_bus->syscfg, SYSCFG_STANDBY_MODE_MASK,
175 SYSCFG_STANDBY_MODE_SMART);
177 /* enable OCP master ports/disable MStandby */
178 ret = pruss_soc_bus_enable_ocp_master_ports(dev);
179 if (ret)
180 pruss_disable_module(dev);
182 return ret;
183 }
185 static int pruss_soc_bus_probe(struct platform_device *pdev)
186 {
187 struct device *dev = &pdev->dev;
188 struct device_node *node = dev->of_node;
189 struct pruss_platform_data *pdata = dev_get_platdata(dev);
190 struct pruss_soc_bus *psoc_bus;
191 const struct pruss_soc_bus_match_data *data;
192 int ret;
194 psoc_bus = devm_kzalloc(dev, sizeof(*psoc_bus), GFP_KERNEL);
195 if (!psoc_bus)
196 return -ENOMEM;
198 psoc_bus->syscfg = of_iomap(node, 0);
199 if (!psoc_bus->syscfg)
200 return -ENOMEM;
202 data = of_device_get_match_data(dev);
203 if (!data) {
204 dev_err(dev, "missing match data\n");
205 return -ENODEV;
206 }
208 if (data->has_reset && (!pdata || !pdata->deassert_reset ||
209 !pdata->assert_reset || !pdata->reset_name)) {
210 dev_err(dev, "platform data (reset configuration information) missing\n");
211 return -ENODEV;
212 }
213 psoc_bus->has_reset = data->has_reset;
214 psoc_bus->skip_syscfg = !data->uses_prcm;
215 platform_set_drvdata(pdev, psoc_bus);
217 if (psoc_bus->has_reset) {
218 ret = pdata->deassert_reset(pdev, pdata->reset_name);
219 if (ret) {
220 dev_err(dev, "deassert_reset failed: %d\n", ret);
221 goto fail_reset;
222 }
223 }
225 pm_runtime_enable(dev);
226 ret = pruss_enable_module(dev);
227 if (ret < 0) {
228 dev_err(dev, "couldn't enable module\n");
229 goto fail_module;
230 }
232 ret = of_platform_populate(node, NULL, NULL, dev);
233 if (ret)
234 goto fail_of;
236 return 0;
238 fail_of:
239 pruss_disable_module(dev);
240 fail_module:
241 pm_runtime_disable(dev);
242 if (psoc_bus->has_reset)
243 pdata->assert_reset(pdev, pdata->reset_name);
244 fail_reset:
245 iounmap(psoc_bus->syscfg);
246 return ret;
247 }
249 static int pruss_soc_bus_remove(struct platform_device *pdev)
250 {
251 struct device *dev = &pdev->dev;
252 struct pruss_platform_data *pdata = dev_get_platdata(dev);
253 struct pruss_soc_bus *psoc_bus = platform_get_drvdata(pdev);
255 of_platform_depopulate(dev);
257 pruss_disable_module(dev);
258 pm_runtime_disable(dev);
260 if (psoc_bus->has_reset)
261 pdata->assert_reset(pdev, pdata->reset_name);
262 iounmap(psoc_bus->syscfg);
264 return 0;
265 }
267 /* instance-specific driver private data */
268 static const struct pruss_soc_bus_match_data am335x_data = {
269 .has_reset = true,
270 .uses_prcm = true,
271 };
273 static const struct pruss_soc_bus_match_data am437x_data = {
274 .has_reset = true,
275 .uses_prcm = true,
276 };
278 static const struct pruss_soc_bus_match_data am57xx_data = {
279 .has_reset = false,
280 .uses_prcm = true,
281 };
283 static const struct pruss_soc_bus_match_data k2g_data = {
284 .has_reset = false,
285 .uses_prcm = false,
286 };
288 static const struct of_device_id pruss_soc_bus_of_match[] = {
289 { .compatible = "ti,am3356-pruss-soc-bus", .data = &am335x_data, },
290 { .compatible = "ti,am4376-pruss-soc-bus", .data = &am437x_data, },
291 { .compatible = "ti,am5728-pruss-soc-bus", .data = &am57xx_data, },
292 { .compatible = "ti,k2g-pruss-soc-bus", .data = &k2g_data, },
293 { .compatible = "ti,am654-icssg-soc-bus", .data = &k2g_data, },
294 { /* sentinel */ },
295 };
296 MODULE_DEVICE_TABLE(of, pruss_soc_bus_of_match);
298 static SIMPLE_DEV_PM_OPS(pruss_soc_bus_pm_ops,
299 pruss_soc_bus_suspend, pruss_soc_bus_resume);
301 static struct platform_driver pruss_soc_bus_driver = {
302 .driver = {
303 .name = "pruss-soc-bus",
304 .pm = &pruss_soc_bus_pm_ops,
305 .of_match_table = pruss_soc_bus_of_match,
306 },
307 .probe = pruss_soc_bus_probe,
308 .remove = pruss_soc_bus_remove,
309 };
310 module_platform_driver(pruss_soc_bus_driver);
312 MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
313 MODULE_AUTHOR("Keerthy <j-keerthy@ti.com>");
314 MODULE_DESCRIPTION("PRU-ICSS SoC Bus Driver for TI SoCs");
315 MODULE_LICENSE("GPL v2");