]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - rpmsg/rpmsg.git/blob - drivers/soc/ti/pruss_soc_bus.c
soc: ti: pruss: enable support for ICSSG subsystems on K3 AM65x SoCs
[rpmsg/rpmsg.git] / drivers / soc / ti / pruss_soc_bus.c
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)
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;
120 static int __maybe_unused pruss_soc_bus_resume(struct device *dev)
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;
135 /* firmware must be idle when calling this function */
136 static void pruss_disable_module(struct device *dev)
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);
156 static int pruss_enable_module(struct device *dev)
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;
185 static int pruss_soc_bus_probe(struct platform_device *pdev)
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;
249 static int pruss_soc_bus_remove(struct platform_device *pdev)
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;
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");