am437x_pruss_wrapper.c
5.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*
* PRU-ICSS SYSCFG IP wrapper driver for TI AM437x SoCs
*
* Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
* Suman Anna <s-anna@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/platform_data/remoteproc-pruss.h>
#define SYSCFG_OFFSET 0x4
#define SYSCFG_STANDBY_INIT BIT(4)
#define SYSCFG_SUB_MWAIT_READY BIT(5)
/**
* struct am437x_pruss_wrapper - PRUSS wrapper structure
* @base: kernel mapped address for wrapper register base
* @in_standby: flag for storing standby status
*/
struct am437x_pruss_wrapper {
void __iomem *base;
bool in_standby;
};
static inline void pruss_wrapper_rmw(void __iomem *base, unsigned int offset,
u32 mask, u32 set)
{
u32 val;
val = readl_relaxed(base + offset);
val &= ~mask;
val |= (set & mask);
writel_relaxed(val, base + offset);
}
/*
* This function programs the PRUSS_SYSCFG.STANDBY_INIT bit to achieve dual
* functionalities - one is to deassert the MStandby signal to the device
* PRCM, and the other is to enable OCP master ports to allow accesses
* outside of the PRU-ICSS. The function has to wait for the PRCM to
* acknowledge through the monitoring of the PRUSS_SYSCFG.SUB_MWAIT bit.
*/
static int pruss_wrapper_enable_ocp_master_ports(struct device *dev)
{
struct am437x_pruss_wrapper *wrapper = dev_get_drvdata(dev);
u32 syscfg_val, i;
bool ready = false;
pruss_wrapper_rmw(wrapper->base, SYSCFG_OFFSET, SYSCFG_STANDBY_INIT, 0);
/* wait till we are ready for transactions - delay is arbitrary */
for (i = 0; i < 10; i++) {
syscfg_val = readl_relaxed(wrapper->base + SYSCFG_OFFSET);
ready = !(syscfg_val & SYSCFG_SUB_MWAIT_READY);
if (ready)
break;
udelay(5);
}
if (!ready) {
dev_err(dev, "timeout waiting for SUB_MWAIT_READY\n");
return -ETIMEDOUT;
}
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int am437x_pruss_wrapper_suspend(struct device *dev)
{
struct am437x_pruss_wrapper *wrapper = dev_get_drvdata(dev);
u32 syscfg_val;
syscfg_val = readl_relaxed(wrapper->base + SYSCFG_OFFSET);
wrapper->in_standby = syscfg_val & SYSCFG_STANDBY_INIT;
/* initiate MStandby, undo the MStandby config in probe */
if (!wrapper->in_standby) {
pruss_wrapper_rmw(wrapper->base, SYSCFG_OFFSET,
SYSCFG_STANDBY_INIT, SYSCFG_STANDBY_INIT);
}
return 0;
}
static int am437x_pruss_wrapper_resume(struct device *dev)
{
struct am437x_pruss_wrapper *wrapper = dev_get_drvdata(dev);
int ret = 0;
/* re-enable OCP master ports/disable MStandby */
if (!wrapper->in_standby) {
ret = pruss_wrapper_enable_ocp_master_ports(dev);
if (ret)
dev_err(dev, "%s failed\n", __func__);
}
return ret;
}
#endif /* CONFIG_PM_SLEEP */
static int am437x_pruss_wrapper_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct pruss_platform_data *pdata = dev_get_platdata(dev);
struct am437x_pruss_wrapper *wrapper;
int ret;
if (!pdata || !pdata->deassert_reset || !pdata->assert_reset ||
!pdata->reset_name) {
dev_err(dev, "platform data (reset configuration information) missing\n");
return -ENODEV;
}
wrapper = devm_kzalloc(dev, sizeof(*wrapper), GFP_KERNEL);
if (!wrapper)
return -ENOMEM;
wrapper->base = of_iomap(node, 0);
if (!wrapper->base)
return -ENOMEM;
platform_set_drvdata(pdev, wrapper);
ret = pdata->deassert_reset(pdev, pdata->reset_name);
if (ret) {
dev_err(dev, "deassert_reset failed: %d\n", ret);
goto fail_reset;
}
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
pm_runtime_put_noidle(dev);
goto fail_clock;
}
ret = pruss_wrapper_enable_ocp_master_ports(dev);
if (ret)
goto fail_ocp;
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret)
goto fail_ocp;
return 0;
fail_ocp:
pm_runtime_put_sync(dev);
fail_clock:
pm_runtime_disable(dev);
pdata->assert_reset(pdev, pdata->reset_name);
fail_reset:
iounmap(wrapper->base);
return ret;
}
static int am437x_pruss_wrapper_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct pruss_platform_data *pdata = dev_get_platdata(dev);
struct am437x_pruss_wrapper *wrapper = platform_get_drvdata(pdev);
of_platform_depopulate(dev);
pruss_wrapper_rmw(wrapper->base, SYSCFG_OFFSET,
SYSCFG_STANDBY_INIT, SYSCFG_STANDBY_INIT);
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
pdata->assert_reset(pdev, pdata->reset_name);
iounmap(wrapper->base);
return 0;
}
static SIMPLE_DEV_PM_OPS(am437x_pruss_wrapper_pm_ops,
am437x_pruss_wrapper_suspend,
am437x_pruss_wrapper_resume);
static const struct of_device_id am437x_pruss_wrapper_of_match[] = {
{ .compatible = "ti,am4372-pruss-wrapper" },
{ },
};
MODULE_DEVICE_TABLE(of, am437x_pruss_wrapper_of_match);
static struct platform_driver am437x_pruss_wrapper_driver = {
.driver = {
.name = "am437x-pruss-wrapper",
.pm = &am437x_pruss_wrapper_pm_ops,
.of_match_table = am437x_pruss_wrapper_of_match,
},
.probe = am437x_pruss_wrapper_probe,
.remove = am437x_pruss_wrapper_remove,
};
module_platform_driver(am437x_pruss_wrapper_driver);
MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
MODULE_DESCRIPTION("PRU-ICSS SYSCFG IP Wrapper Driver for AM437x SoCs");
MODULE_LICENSE("GPL v2");