diff options
author | Timo Alho <talho@nvidia.com> | 2019-01-24 12:03:53 -0500 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2019-01-25 09:58:47 -0500 |
commit | cdfa358b248efd36c6a9cb4d4d0a3ba7509f8387 (patch) | |
tree | db7d882df0d9233280ad0e4e300d3d3794f2b3f2 /drivers/firmware/tegra | |
parent | 165ce6e01d2d00bff2f10eba8dd679cee44163bd (diff) |
firmware: tegra: Refactor BPMP driver
Split BPMP driver into common and chip specific parts to facilitate
adding support for previous and future Tegra chips that are using BPMP
as co-processor.
Signed-off-by: Timo Alho <talho@nvidia.com>
Acked-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/firmware/tegra')
-rw-r--r-- | drivers/firmware/tegra/Makefile | 1 | ||||
-rw-r--r-- | drivers/firmware/tegra/bpmp-private.h | 28 | ||||
-rw-r--r-- | drivers/firmware/tegra/bpmp-tegra186.c | 310 | ||||
-rw-r--r-- | drivers/firmware/tegra/bpmp.c | 257 |
4 files changed, 394 insertions, 202 deletions
diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile index 1b826dcca719..559355674bca 100644 --- a/drivers/firmware/tegra/Makefile +++ b/drivers/firmware/tegra/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | tegra-bpmp-y = bpmp.o | 1 | tegra-bpmp-y = bpmp.o |
2 | tegra-bpmp-$(CONFIG_ARCH_TEGRA_186_SOC) += bpmp-tegra186.o | ||
2 | tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o | 3 | tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o |
3 | obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o | 4 | obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o |
4 | obj-$(CONFIG_TEGRA_IVC) += ivc.o | 5 | obj-$(CONFIG_TEGRA_IVC) += ivc.o |
diff --git a/drivers/firmware/tegra/bpmp-private.h b/drivers/firmware/tegra/bpmp-private.h new file mode 100644 index 000000000000..6fb10f1cfb8f --- /dev/null +++ b/drivers/firmware/tegra/bpmp-private.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * Copyright (c) 2018, NVIDIA CORPORATION. | ||
4 | */ | ||
5 | |||
6 | #ifndef __FIRMWARE_TEGRA_BPMP_PRIVATE_H | ||
7 | #define __FIRMWARE_TEGRA_BPMP_PRIVATE_H | ||
8 | |||
9 | #include <soc/tegra/bpmp.h> | ||
10 | |||
11 | struct tegra_bpmp_ops { | ||
12 | int (*init)(struct tegra_bpmp *bpmp); | ||
13 | void (*deinit)(struct tegra_bpmp *bpmp); | ||
14 | bool (*is_response_ready)(struct tegra_bpmp_channel *channel); | ||
15 | bool (*is_request_ready)(struct tegra_bpmp_channel *channel); | ||
16 | int (*ack_response)(struct tegra_bpmp_channel *channel); | ||
17 | int (*ack_request)(struct tegra_bpmp_channel *channel); | ||
18 | bool (*is_response_channel_free)(struct tegra_bpmp_channel *channel); | ||
19 | bool (*is_request_channel_free)(struct tegra_bpmp_channel *channel); | ||
20 | int (*post_response)(struct tegra_bpmp_channel *channel); | ||
21 | int (*post_request)(struct tegra_bpmp_channel *channel); | ||
22 | int (*ring_doorbell)(struct tegra_bpmp *bpmp); | ||
23 | int (*resume)(struct tegra_bpmp *bpmp); | ||
24 | }; | ||
25 | |||
26 | extern const struct tegra_bpmp_ops tegra186_bpmp_ops; | ||
27 | |||
28 | #endif | ||
diff --git a/drivers/firmware/tegra/bpmp-tegra186.c b/drivers/firmware/tegra/bpmp-tegra186.c new file mode 100644 index 000000000000..89bb32b97345 --- /dev/null +++ b/drivers/firmware/tegra/bpmp-tegra186.c | |||
@@ -0,0 +1,310 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (c) 2018, NVIDIA CORPORATION. | ||
4 | */ | ||
5 | |||
6 | #include <linux/clk/tegra.h> | ||
7 | #include <linux/genalloc.h> | ||
8 | #include <linux/mailbox_client.h> | ||
9 | #include <linux/of.h> | ||
10 | #include <linux/of_address.h> | ||
11 | #include <linux/of_device.h> | ||
12 | #include <linux/platform_device.h> | ||
13 | #include <linux/semaphore.h> | ||
14 | |||
15 | #include <soc/tegra/bpmp.h> | ||
16 | #include <soc/tegra/bpmp-abi.h> | ||
17 | #include <soc/tegra/ivc.h> | ||
18 | |||
19 | #include "bpmp-private.h" | ||
20 | |||
21 | struct tegra186_bpmp { | ||
22 | struct tegra_bpmp *parent; | ||
23 | |||
24 | struct { | ||
25 | struct gen_pool *pool; | ||
26 | dma_addr_t phys; | ||
27 | void *virt; | ||
28 | } tx, rx; | ||
29 | |||
30 | struct { | ||
31 | struct mbox_client client; | ||
32 | struct mbox_chan *channel; | ||
33 | } mbox; | ||
34 | }; | ||
35 | |||
36 | static inline struct tegra_bpmp * | ||
37 | mbox_client_to_bpmp(struct mbox_client *client) | ||
38 | { | ||
39 | struct tegra186_bpmp *priv; | ||
40 | |||
41 | priv = container_of(client, struct tegra186_bpmp, mbox.client); | ||
42 | |||
43 | return priv->parent; | ||
44 | } | ||
45 | |||
46 | static bool tegra186_bpmp_is_message_ready(struct tegra_bpmp_channel *channel) | ||
47 | { | ||
48 | void *frame; | ||
49 | |||
50 | frame = tegra_ivc_read_get_next_frame(channel->ivc); | ||
51 | if (IS_ERR(frame)) { | ||
52 | channel->ib = NULL; | ||
53 | return false; | ||
54 | } | ||
55 | |||
56 | channel->ib = frame; | ||
57 | |||
58 | return true; | ||
59 | } | ||
60 | |||
61 | static bool tegra186_bpmp_is_channel_free(struct tegra_bpmp_channel *channel) | ||
62 | { | ||
63 | void *frame; | ||
64 | |||
65 | frame = tegra_ivc_write_get_next_frame(channel->ivc); | ||
66 | if (IS_ERR(frame)) { | ||
67 | channel->ob = NULL; | ||
68 | return false; | ||
69 | } | ||
70 | |||
71 | channel->ob = frame; | ||
72 | |||
73 | return true; | ||
74 | } | ||
75 | |||
76 | static int tegra186_bpmp_ack_message(struct tegra_bpmp_channel *channel) | ||
77 | { | ||
78 | return tegra_ivc_read_advance(channel->ivc); | ||
79 | } | ||
80 | |||
81 | static int tegra186_bpmp_post_message(struct tegra_bpmp_channel *channel) | ||
82 | { | ||
83 | return tegra_ivc_write_advance(channel->ivc); | ||
84 | } | ||
85 | |||
86 | static int tegra186_bpmp_ring_doorbell(struct tegra_bpmp *bpmp) | ||
87 | { | ||
88 | struct tegra186_bpmp *priv = bpmp->priv; | ||
89 | int err; | ||
90 | |||
91 | err = mbox_send_message(priv->mbox.channel, NULL); | ||
92 | if (err < 0) | ||
93 | return err; | ||
94 | |||
95 | mbox_client_txdone(priv->mbox.channel, 0); | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static void tegra186_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data) | ||
101 | { | ||
102 | struct tegra_bpmp *bpmp = data; | ||
103 | struct tegra186_bpmp *priv = bpmp->priv; | ||
104 | |||
105 | if (WARN_ON(priv->mbox.channel == NULL)) | ||
106 | return; | ||
107 | |||
108 | tegra186_bpmp_ring_doorbell(bpmp); | ||
109 | } | ||
110 | |||
111 | static int tegra186_bpmp_channel_init(struct tegra_bpmp_channel *channel, | ||
112 | struct tegra_bpmp *bpmp, | ||
113 | unsigned int index) | ||
114 | { | ||
115 | struct tegra186_bpmp *priv = bpmp->priv; | ||
116 | size_t message_size, queue_size; | ||
117 | unsigned int offset; | ||
118 | int err; | ||
119 | |||
120 | channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc), | ||
121 | GFP_KERNEL); | ||
122 | if (!channel->ivc) | ||
123 | return -ENOMEM; | ||
124 | |||
125 | message_size = tegra_ivc_align(MSG_MIN_SZ); | ||
126 | queue_size = tegra_ivc_total_queue_size(message_size); | ||
127 | offset = queue_size * index; | ||
128 | |||
129 | err = tegra_ivc_init(channel->ivc, NULL, | ||
130 | priv->rx.virt + offset, priv->rx.phys + offset, | ||
131 | priv->tx.virt + offset, priv->tx.phys + offset, | ||
132 | 1, message_size, tegra186_bpmp_ivc_notify, | ||
133 | bpmp); | ||
134 | if (err < 0) { | ||
135 | dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n", | ||
136 | index, err); | ||
137 | return err; | ||
138 | } | ||
139 | |||
140 | init_completion(&channel->completion); | ||
141 | channel->bpmp = bpmp; | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static void tegra186_bpmp_channel_reset(struct tegra_bpmp_channel *channel) | ||
147 | { | ||
148 | /* reset the channel state */ | ||
149 | tegra_ivc_reset(channel->ivc); | ||
150 | |||
151 | /* sync the channel state with BPMP */ | ||
152 | while (tegra_ivc_notified(channel->ivc)) | ||
153 | ; | ||
154 | } | ||
155 | |||
156 | static void tegra186_bpmp_channel_cleanup(struct tegra_bpmp_channel *channel) | ||
157 | { | ||
158 | tegra_ivc_cleanup(channel->ivc); | ||
159 | } | ||
160 | |||
161 | static void mbox_handle_rx(struct mbox_client *client, void *data) | ||
162 | { | ||
163 | struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client); | ||
164 | |||
165 | tegra_bpmp_handle_rx(bpmp); | ||
166 | } | ||
167 | |||
168 | static int tegra186_bpmp_init(struct tegra_bpmp *bpmp) | ||
169 | { | ||
170 | struct tegra186_bpmp *priv; | ||
171 | unsigned int i; | ||
172 | int err; | ||
173 | |||
174 | priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL); | ||
175 | if (!priv) | ||
176 | return -ENOMEM; | ||
177 | |||
178 | bpmp->priv = priv; | ||
179 | priv->parent = bpmp; | ||
180 | |||
181 | priv->tx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 0); | ||
182 | if (!priv->tx.pool) { | ||
183 | dev_err(bpmp->dev, "TX shmem pool not found\n"); | ||
184 | return -ENOMEM; | ||
185 | } | ||
186 | |||
187 | priv->tx.virt = gen_pool_dma_alloc(priv->tx.pool, 4096, &priv->tx.phys); | ||
188 | if (!priv->tx.virt) { | ||
189 | dev_err(bpmp->dev, "failed to allocate from TX pool\n"); | ||
190 | return -ENOMEM; | ||
191 | } | ||
192 | |||
193 | priv->rx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 1); | ||
194 | if (!priv->rx.pool) { | ||
195 | dev_err(bpmp->dev, "RX shmem pool not found\n"); | ||
196 | err = -ENOMEM; | ||
197 | goto free_tx; | ||
198 | } | ||
199 | |||
200 | priv->rx.virt = gen_pool_dma_alloc(priv->rx.pool, 4096, &priv->rx.phys); | ||
201 | if (!priv->rx.virt) { | ||
202 | dev_err(bpmp->dev, "failed to allocate from RX pool\n"); | ||
203 | err = -ENOMEM; | ||
204 | goto free_tx; | ||
205 | } | ||
206 | |||
207 | err = tegra186_bpmp_channel_init(bpmp->tx_channel, bpmp, | ||
208 | bpmp->soc->channels.cpu_tx.offset); | ||
209 | if (err < 0) | ||
210 | goto free_rx; | ||
211 | |||
212 | err = tegra186_bpmp_channel_init(bpmp->rx_channel, bpmp, | ||
213 | bpmp->soc->channels.cpu_rx.offset); | ||
214 | if (err < 0) | ||
215 | goto cleanup_tx_channel; | ||
216 | |||
217 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
218 | unsigned int index = bpmp->soc->channels.thread.offset + i; | ||
219 | |||
220 | err = tegra186_bpmp_channel_init(&bpmp->threaded_channels[i], | ||
221 | bpmp, index); | ||
222 | if (err < 0) | ||
223 | goto cleanup_channels; | ||
224 | } | ||
225 | |||
226 | /* mbox registration */ | ||
227 | priv->mbox.client.dev = bpmp->dev; | ||
228 | priv->mbox.client.rx_callback = mbox_handle_rx; | ||
229 | priv->mbox.client.tx_block = false; | ||
230 | priv->mbox.client.knows_txdone = false; | ||
231 | |||
232 | priv->mbox.channel = mbox_request_channel(&priv->mbox.client, 0); | ||
233 | if (IS_ERR(priv->mbox.channel)) { | ||
234 | err = PTR_ERR(priv->mbox.channel); | ||
235 | dev_err(bpmp->dev, "failed to get HSP mailbox: %d\n", err); | ||
236 | goto cleanup_channels; | ||
237 | } | ||
238 | |||
239 | tegra186_bpmp_channel_reset(bpmp->tx_channel); | ||
240 | tegra186_bpmp_channel_reset(bpmp->rx_channel); | ||
241 | |||
242 | for (i = 0; i < bpmp->threaded.count; i++) | ||
243 | tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
244 | |||
245 | return 0; | ||
246 | |||
247 | cleanup_channels: | ||
248 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
249 | if (!bpmp->threaded_channels[i].bpmp) | ||
250 | continue; | ||
251 | |||
252 | tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); | ||
253 | } | ||
254 | |||
255 | tegra186_bpmp_channel_cleanup(bpmp->rx_channel); | ||
256 | cleanup_tx_channel: | ||
257 | tegra186_bpmp_channel_cleanup(bpmp->tx_channel); | ||
258 | free_rx: | ||
259 | gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096); | ||
260 | free_tx: | ||
261 | gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096); | ||
262 | |||
263 | return err; | ||
264 | } | ||
265 | |||
266 | static void tegra186_bpmp_deinit(struct tegra_bpmp *bpmp) | ||
267 | { | ||
268 | struct tegra186_bpmp *priv = bpmp->priv; | ||
269 | unsigned int i; | ||
270 | |||
271 | mbox_free_channel(priv->mbox.channel); | ||
272 | |||
273 | for (i = 0; i < bpmp->threaded.count; i++) | ||
274 | tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); | ||
275 | |||
276 | tegra186_bpmp_channel_cleanup(bpmp->rx_channel); | ||
277 | tegra186_bpmp_channel_cleanup(bpmp->tx_channel); | ||
278 | |||
279 | gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096); | ||
280 | gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096); | ||
281 | } | ||
282 | |||
283 | static int tegra186_bpmp_resume(struct tegra_bpmp *bpmp) | ||
284 | { | ||
285 | unsigned int i; | ||
286 | |||
287 | /* reset message channels */ | ||
288 | tegra186_bpmp_channel_reset(bpmp->tx_channel); | ||
289 | tegra186_bpmp_channel_reset(bpmp->rx_channel); | ||
290 | |||
291 | for (i = 0; i < bpmp->threaded.count; i++) | ||
292 | tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
293 | |||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | const struct tegra_bpmp_ops tegra186_bpmp_ops = { | ||
298 | .init = tegra186_bpmp_init, | ||
299 | .deinit = tegra186_bpmp_deinit, | ||
300 | .is_response_ready = tegra186_bpmp_is_message_ready, | ||
301 | .is_request_ready = tegra186_bpmp_is_message_ready, | ||
302 | .ack_response = tegra186_bpmp_ack_message, | ||
303 | .ack_request = tegra186_bpmp_ack_message, | ||
304 | .is_response_channel_free = tegra186_bpmp_is_channel_free, | ||
305 | .is_request_channel_free = tegra186_bpmp_is_channel_free, | ||
306 | .post_response = tegra186_bpmp_post_message, | ||
307 | .post_request = tegra186_bpmp_post_message, | ||
308 | .ring_doorbell = tegra186_bpmp_ring_doorbell, | ||
309 | .resume = tegra186_bpmp_resume, | ||
310 | }; | ||
diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index e7fd68ebedd0..2b498deacdbc 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c | |||
@@ -26,6 +26,8 @@ | |||
26 | #include <soc/tegra/bpmp-abi.h> | 26 | #include <soc/tegra/bpmp-abi.h> |
27 | #include <soc/tegra/ivc.h> | 27 | #include <soc/tegra/ivc.h> |
28 | 28 | ||
29 | #include "bpmp-private.h" | ||
30 | |||
29 | #define MSG_ACK BIT(0) | 31 | #define MSG_ACK BIT(0) |
30 | #define MSG_RING BIT(1) | 32 | #define MSG_RING BIT(1) |
31 | #define TAG_SZ 32 | 33 | #define TAG_SZ 32 |
@@ -36,6 +38,14 @@ mbox_client_to_bpmp(struct mbox_client *client) | |||
36 | return container_of(client, struct tegra_bpmp, mbox.client); | 38 | return container_of(client, struct tegra_bpmp, mbox.client); |
37 | } | 39 | } |
38 | 40 | ||
41 | static inline const struct tegra_bpmp_ops * | ||
42 | channel_to_ops(struct tegra_bpmp_channel *channel) | ||
43 | { | ||
44 | struct tegra_bpmp *bpmp = channel->bpmp; | ||
45 | |||
46 | return bpmp->soc->ops; | ||
47 | } | ||
48 | |||
39 | struct tegra_bpmp *tegra_bpmp_get(struct device *dev) | 49 | struct tegra_bpmp *tegra_bpmp_get(struct device *dev) |
40 | { | 50 | { |
41 | struct platform_device *pdev; | 51 | struct platform_device *pdev; |
@@ -98,22 +108,16 @@ static bool tegra_bpmp_message_valid(const struct tegra_bpmp_message *msg) | |||
98 | 108 | ||
99 | static bool tegra_bpmp_is_response_ready(struct tegra_bpmp_channel *channel) | 109 | static bool tegra_bpmp_is_response_ready(struct tegra_bpmp_channel *channel) |
100 | { | 110 | { |
101 | void *frame; | 111 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
102 | 112 | ||
103 | frame = tegra_ivc_read_get_next_frame(channel->ivc); | 113 | return ops->is_response_ready(channel); |
104 | if (IS_ERR(frame)) { | ||
105 | channel->ib = NULL; | ||
106 | return false; | ||
107 | } | ||
108 | |||
109 | channel->ib = frame; | ||
110 | |||
111 | return true; | ||
112 | } | 114 | } |
113 | 115 | ||
114 | static bool tegra_bpmp_is_request_ready(struct tegra_bpmp_channel *channel) | 116 | static bool tegra_bpmp_is_request_ready(struct tegra_bpmp_channel *channel) |
115 | { | 117 | { |
116 | return tegra_bpmp_is_response_ready(channel); | 118 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
119 | |||
120 | return ops->is_request_ready(channel); | ||
117 | } | 121 | } |
118 | 122 | ||
119 | static int tegra_bpmp_wait_response(struct tegra_bpmp_channel *channel) | 123 | static int tegra_bpmp_wait_response(struct tegra_bpmp_channel *channel) |
@@ -133,34 +137,32 @@ static int tegra_bpmp_wait_response(struct tegra_bpmp_channel *channel) | |||
133 | 137 | ||
134 | static int tegra_bpmp_ack_response(struct tegra_bpmp_channel *channel) | 138 | static int tegra_bpmp_ack_response(struct tegra_bpmp_channel *channel) |
135 | { | 139 | { |
136 | return tegra_ivc_read_advance(channel->ivc); | 140 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
141 | |||
142 | return ops->ack_response(channel); | ||
137 | } | 143 | } |
138 | 144 | ||
139 | static int tegra_bpmp_ack_request(struct tegra_bpmp_channel *channel) | 145 | static int tegra_bpmp_ack_request(struct tegra_bpmp_channel *channel) |
140 | { | 146 | { |
141 | return tegra_ivc_read_advance(channel->ivc); | 147 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
148 | |||
149 | return ops->ack_request(channel); | ||
142 | } | 150 | } |
143 | 151 | ||
144 | static bool | 152 | static bool |
145 | tegra_bpmp_is_request_channel_free(struct tegra_bpmp_channel *channel) | 153 | tegra_bpmp_is_request_channel_free(struct tegra_bpmp_channel *channel) |
146 | { | 154 | { |
147 | void *frame; | 155 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
148 | |||
149 | frame = tegra_ivc_write_get_next_frame(channel->ivc); | ||
150 | if (IS_ERR(frame)) { | ||
151 | channel->ob = NULL; | ||
152 | return false; | ||
153 | } | ||
154 | |||
155 | channel->ob = frame; | ||
156 | 156 | ||
157 | return true; | 157 | return ops->is_request_channel_free(channel); |
158 | } | 158 | } |
159 | 159 | ||
160 | static bool | 160 | static bool |
161 | tegra_bpmp_is_response_channel_free(struct tegra_bpmp_channel *channel) | 161 | tegra_bpmp_is_response_channel_free(struct tegra_bpmp_channel *channel) |
162 | { | 162 | { |
163 | return tegra_bpmp_is_request_channel_free(channel); | 163 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
164 | |||
165 | return ops->is_response_channel_free(channel); | ||
164 | } | 166 | } |
165 | 167 | ||
166 | static int | 168 | static int |
@@ -183,28 +185,21 @@ tegra_bpmp_wait_request_channel_free(struct tegra_bpmp_channel *channel) | |||
183 | 185 | ||
184 | static int tegra_bpmp_post_request(struct tegra_bpmp_channel *channel) | 186 | static int tegra_bpmp_post_request(struct tegra_bpmp_channel *channel) |
185 | { | 187 | { |
186 | return tegra_ivc_write_advance(channel->ivc); | 188 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
189 | |||
190 | return ops->post_request(channel); | ||
187 | } | 191 | } |
188 | 192 | ||
189 | static int tegra_bpmp_post_response(struct tegra_bpmp_channel *channel) | 193 | static int tegra_bpmp_post_response(struct tegra_bpmp_channel *channel) |
190 | { | 194 | { |
191 | return tegra_ivc_write_advance(channel->ivc); | 195 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
196 | |||
197 | return ops->post_response(channel); | ||
192 | } | 198 | } |
193 | 199 | ||
194 | static int tegra_bpmp_ring_doorbell(struct tegra_bpmp *bpmp) | 200 | static int tegra_bpmp_ring_doorbell(struct tegra_bpmp *bpmp) |
195 | { | 201 | { |
196 | int err; | 202 | return bpmp->soc->ops->ring_doorbell(bpmp); |
197 | |||
198 | if (WARN_ON(bpmp->mbox.channel == NULL)) | ||
199 | return -EINVAL; | ||
200 | |||
201 | err = mbox_send_message(bpmp->mbox.channel, NULL); | ||
202 | if (err < 0) | ||
203 | return err; | ||
204 | |||
205 | mbox_client_txdone(bpmp->mbox.channel, 0); | ||
206 | |||
207 | return 0; | ||
208 | } | 203 | } |
209 | 204 | ||
210 | static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel, | 205 | static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel, |
@@ -668,9 +663,8 @@ static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel) | |||
668 | complete(&channel->completion); | 663 | complete(&channel->completion); |
669 | } | 664 | } |
670 | 665 | ||
671 | static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) | 666 | void tegra_bpmp_handle_rx(struct tegra_bpmp *bpmp) |
672 | { | 667 | { |
673 | struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client); | ||
674 | struct tegra_bpmp_channel *channel; | 668 | struct tegra_bpmp_channel *channel; |
675 | unsigned int i, count; | 669 | unsigned int i, count; |
676 | unsigned long *busy; | 670 | unsigned long *busy; |
@@ -698,66 +692,9 @@ static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) | |||
698 | spin_unlock(&bpmp->lock); | 692 | spin_unlock(&bpmp->lock); |
699 | } | 693 | } |
700 | 694 | ||
701 | static void tegra_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data) | ||
702 | { | ||
703 | struct tegra_bpmp *bpmp = data; | ||
704 | |||
705 | WARN_ON(tegra_bpmp_ring_doorbell(bpmp)); | ||
706 | } | ||
707 | |||
708 | static int tegra_bpmp_channel_init(struct tegra_bpmp_channel *channel, | ||
709 | struct tegra_bpmp *bpmp, | ||
710 | unsigned int index) | ||
711 | { | ||
712 | size_t message_size, queue_size; | ||
713 | unsigned int offset; | ||
714 | int err; | ||
715 | |||
716 | channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc), | ||
717 | GFP_KERNEL); | ||
718 | if (!channel->ivc) | ||
719 | return -ENOMEM; | ||
720 | |||
721 | message_size = tegra_ivc_align(MSG_MIN_SZ); | ||
722 | queue_size = tegra_ivc_total_queue_size(message_size); | ||
723 | offset = queue_size * index; | ||
724 | |||
725 | err = tegra_ivc_init(channel->ivc, NULL, | ||
726 | bpmp->rx.virt + offset, bpmp->rx.phys + offset, | ||
727 | bpmp->tx.virt + offset, bpmp->tx.phys + offset, | ||
728 | 1, message_size, tegra_bpmp_ivc_notify, | ||
729 | bpmp); | ||
730 | if (err < 0) { | ||
731 | dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n", | ||
732 | index, err); | ||
733 | return err; | ||
734 | } | ||
735 | |||
736 | init_completion(&channel->completion); | ||
737 | channel->bpmp = bpmp; | ||
738 | |||
739 | return 0; | ||
740 | } | ||
741 | |||
742 | static void tegra_bpmp_channel_reset(struct tegra_bpmp_channel *channel) | ||
743 | { | ||
744 | /* reset the channel state */ | ||
745 | tegra_ivc_reset(channel->ivc); | ||
746 | |||
747 | /* sync the channel state with BPMP */ | ||
748 | while (tegra_ivc_notified(channel->ivc)) | ||
749 | ; | ||
750 | } | ||
751 | |||
752 | static void tegra_bpmp_channel_cleanup(struct tegra_bpmp_channel *channel) | ||
753 | { | ||
754 | tegra_ivc_cleanup(channel->ivc); | ||
755 | } | ||
756 | |||
757 | static int tegra_bpmp_probe(struct platform_device *pdev) | 695 | static int tegra_bpmp_probe(struct platform_device *pdev) |
758 | { | 696 | { |
759 | struct tegra_bpmp *bpmp; | 697 | struct tegra_bpmp *bpmp; |
760 | unsigned int i; | ||
761 | char tag[TAG_SZ]; | 698 | char tag[TAG_SZ]; |
762 | size_t size; | 699 | size_t size; |
763 | int err; | 700 | int err; |
@@ -769,32 +706,6 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
769 | bpmp->soc = of_device_get_match_data(&pdev->dev); | 706 | bpmp->soc = of_device_get_match_data(&pdev->dev); |
770 | bpmp->dev = &pdev->dev; | 707 | bpmp->dev = &pdev->dev; |
771 | 708 | ||
772 | bpmp->tx.pool = of_gen_pool_get(pdev->dev.of_node, "shmem", 0); | ||
773 | if (!bpmp->tx.pool) { | ||
774 | dev_err(&pdev->dev, "TX shmem pool not found\n"); | ||
775 | return -ENOMEM; | ||
776 | } | ||
777 | |||
778 | bpmp->tx.virt = gen_pool_dma_alloc(bpmp->tx.pool, 4096, &bpmp->tx.phys); | ||
779 | if (!bpmp->tx.virt) { | ||
780 | dev_err(&pdev->dev, "failed to allocate from TX pool\n"); | ||
781 | return -ENOMEM; | ||
782 | } | ||
783 | |||
784 | bpmp->rx.pool = of_gen_pool_get(pdev->dev.of_node, "shmem", 1); | ||
785 | if (!bpmp->rx.pool) { | ||
786 | dev_err(&pdev->dev, "RX shmem pool not found\n"); | ||
787 | err = -ENOMEM; | ||
788 | goto free_tx; | ||
789 | } | ||
790 | |||
791 | bpmp->rx.virt = gen_pool_dma_alloc(bpmp->rx.pool, 4096, &bpmp->rx.phys); | ||
792 | if (!bpmp->rx.virt) { | ||
793 | dev_err(&pdev->dev, "failed to allocate from RX pool\n"); | ||
794 | err = -ENOMEM; | ||
795 | goto free_tx; | ||
796 | } | ||
797 | |||
798 | INIT_LIST_HEAD(&bpmp->mrqs); | 709 | INIT_LIST_HEAD(&bpmp->mrqs); |
799 | spin_lock_init(&bpmp->lock); | 710 | spin_lock_init(&bpmp->lock); |
800 | 711 | ||
@@ -804,81 +715,38 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
804 | size = BITS_TO_LONGS(bpmp->threaded.count) * sizeof(long); | 715 | size = BITS_TO_LONGS(bpmp->threaded.count) * sizeof(long); |
805 | 716 | ||
806 | bpmp->threaded.allocated = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); | 717 | bpmp->threaded.allocated = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); |
807 | if (!bpmp->threaded.allocated) { | 718 | if (!bpmp->threaded.allocated) |
808 | err = -ENOMEM; | 719 | return -ENOMEM; |
809 | goto free_rx; | ||
810 | } | ||
811 | 720 | ||
812 | bpmp->threaded.busy = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); | 721 | bpmp->threaded.busy = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); |
813 | if (!bpmp->threaded.busy) { | 722 | if (!bpmp->threaded.busy) |
814 | err = -ENOMEM; | 723 | return -ENOMEM; |
815 | goto free_rx; | ||
816 | } | ||
817 | 724 | ||
818 | spin_lock_init(&bpmp->atomic_tx_lock); | 725 | spin_lock_init(&bpmp->atomic_tx_lock); |
819 | bpmp->tx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->tx_channel), | 726 | bpmp->tx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->tx_channel), |
820 | GFP_KERNEL); | 727 | GFP_KERNEL); |
821 | if (!bpmp->tx_channel) { | 728 | if (!bpmp->tx_channel) |
822 | err = -ENOMEM; | 729 | return -ENOMEM; |
823 | goto free_rx; | ||
824 | } | ||
825 | 730 | ||
826 | bpmp->rx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->rx_channel), | 731 | bpmp->rx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->rx_channel), |
827 | GFP_KERNEL); | 732 | GFP_KERNEL); |
828 | if (!bpmp->rx_channel) { | 733 | if (!bpmp->rx_channel) |
829 | err = -ENOMEM; | 734 | return -ENOMEM; |
830 | goto free_rx; | ||
831 | } | ||
832 | 735 | ||
833 | bpmp->threaded_channels = devm_kcalloc(&pdev->dev, bpmp->threaded.count, | 736 | bpmp->threaded_channels = devm_kcalloc(&pdev->dev, bpmp->threaded.count, |
834 | sizeof(*bpmp->threaded_channels), | 737 | sizeof(*bpmp->threaded_channels), |
835 | GFP_KERNEL); | 738 | GFP_KERNEL); |
836 | if (!bpmp->threaded_channels) { | 739 | if (!bpmp->threaded_channels) |
837 | err = -ENOMEM; | 740 | return -ENOMEM; |
838 | goto free_rx; | ||
839 | } | ||
840 | |||
841 | err = tegra_bpmp_channel_init(bpmp->tx_channel, bpmp, | ||
842 | bpmp->soc->channels.cpu_tx.offset); | ||
843 | if (err < 0) | ||
844 | goto free_rx; | ||
845 | 741 | ||
846 | err = tegra_bpmp_channel_init(bpmp->rx_channel, bpmp, | 742 | err = bpmp->soc->ops->init(bpmp); |
847 | bpmp->soc->channels.cpu_rx.offset); | ||
848 | if (err < 0) | 743 | if (err < 0) |
849 | goto cleanup_tx_channel; | 744 | return err; |
850 | |||
851 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
852 | err = tegra_bpmp_channel_init( | ||
853 | &bpmp->threaded_channels[i], bpmp, | ||
854 | bpmp->soc->channels.thread.offset + i); | ||
855 | if (err < 0) | ||
856 | goto cleanup_threaded_channels; | ||
857 | } | ||
858 | |||
859 | /* mbox registration */ | ||
860 | bpmp->mbox.client.dev = &pdev->dev; | ||
861 | bpmp->mbox.client.rx_callback = tegra_bpmp_handle_rx; | ||
862 | bpmp->mbox.client.tx_block = false; | ||
863 | bpmp->mbox.client.knows_txdone = false; | ||
864 | |||
865 | bpmp->mbox.channel = mbox_request_channel(&bpmp->mbox.client, 0); | ||
866 | if (IS_ERR(bpmp->mbox.channel)) { | ||
867 | err = PTR_ERR(bpmp->mbox.channel); | ||
868 | dev_err(&pdev->dev, "failed to get HSP mailbox: %d\n", err); | ||
869 | goto cleanup_threaded_channels; | ||
870 | } | ||
871 | |||
872 | /* reset message channels */ | ||
873 | tegra_bpmp_channel_reset(bpmp->tx_channel); | ||
874 | tegra_bpmp_channel_reset(bpmp->rx_channel); | ||
875 | for (i = 0; i < bpmp->threaded.count; i++) | ||
876 | tegra_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
877 | 745 | ||
878 | err = tegra_bpmp_request_mrq(bpmp, MRQ_PING, | 746 | err = tegra_bpmp_request_mrq(bpmp, MRQ_PING, |
879 | tegra_bpmp_mrq_handle_ping, bpmp); | 747 | tegra_bpmp_mrq_handle_ping, bpmp); |
880 | if (err < 0) | 748 | if (err < 0) |
881 | goto free_mbox; | 749 | goto deinit; |
882 | 750 | ||
883 | err = tegra_bpmp_ping(bpmp); | 751 | err = tegra_bpmp_ping(bpmp); |
884 | if (err < 0) { | 752 | if (err < 0) { |
@@ -920,37 +788,21 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
920 | 788 | ||
921 | free_mrq: | 789 | free_mrq: |
922 | tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp); | 790 | tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp); |
923 | free_mbox: | 791 | deinit: |
924 | mbox_free_channel(bpmp->mbox.channel); | 792 | if (bpmp->soc->ops->deinit) |
925 | cleanup_threaded_channels: | 793 | bpmp->soc->ops->deinit(bpmp); |
926 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
927 | if (bpmp->threaded_channels[i].bpmp) | ||
928 | tegra_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); | ||
929 | } | ||
930 | 794 | ||
931 | tegra_bpmp_channel_cleanup(bpmp->rx_channel); | ||
932 | cleanup_tx_channel: | ||
933 | tegra_bpmp_channel_cleanup(bpmp->tx_channel); | ||
934 | free_rx: | ||
935 | gen_pool_free(bpmp->rx.pool, (unsigned long)bpmp->rx.virt, 4096); | ||
936 | free_tx: | ||
937 | gen_pool_free(bpmp->tx.pool, (unsigned long)bpmp->tx.virt, 4096); | ||
938 | return err; | 795 | return err; |
939 | } | 796 | } |
940 | 797 | ||
941 | static int __maybe_unused tegra_bpmp_resume(struct device *dev) | 798 | static int __maybe_unused tegra_bpmp_resume(struct device *dev) |
942 | { | 799 | { |
943 | struct tegra_bpmp *bpmp = dev_get_drvdata(dev); | 800 | struct tegra_bpmp *bpmp = dev_get_drvdata(dev); |
944 | unsigned int i; | ||
945 | |||
946 | /* reset message channels */ | ||
947 | tegra_bpmp_channel_reset(bpmp->tx_channel); | ||
948 | tegra_bpmp_channel_reset(bpmp->rx_channel); | ||
949 | |||
950 | for (i = 0; i < bpmp->threaded.count; i++) | ||
951 | tegra_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
952 | 801 | ||
953 | return 0; | 802 | if (bpmp->soc->ops->resume) |
803 | return bpmp->soc->ops->resume(bpmp); | ||
804 | else | ||
805 | return 0; | ||
954 | } | 806 | } |
955 | 807 | ||
956 | static SIMPLE_DEV_PM_OPS(tegra_bpmp_pm_ops, NULL, tegra_bpmp_resume); | 808 | static SIMPLE_DEV_PM_OPS(tegra_bpmp_pm_ops, NULL, tegra_bpmp_resume); |
@@ -971,6 +823,7 @@ static const struct tegra_bpmp_soc tegra186_soc = { | |||
971 | .timeout = 0, | 823 | .timeout = 0, |
972 | }, | 824 | }, |
973 | }, | 825 | }, |
826 | .ops = &tegra186_bpmp_ops, | ||
974 | .num_resets = 193, | 827 | .num_resets = 193, |
975 | }; | 828 | }; |
976 | 829 | ||