1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2018, NVIDIA CORPORATION.
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/genalloc.h>
7*4882a593Smuzhiyun #include <linux/mailbox_client.h>
8*4882a593Smuzhiyun #include <linux/platform_device.h>
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <soc/tegra/bpmp.h>
11*4882a593Smuzhiyun #include <soc/tegra/bpmp-abi.h>
12*4882a593Smuzhiyun #include <soc/tegra/ivc.h>
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include "bpmp-private.h"
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun struct tegra186_bpmp {
17*4882a593Smuzhiyun struct tegra_bpmp *parent;
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun struct {
20*4882a593Smuzhiyun struct gen_pool *pool;
21*4882a593Smuzhiyun dma_addr_t phys;
22*4882a593Smuzhiyun void *virt;
23*4882a593Smuzhiyun } tx, rx;
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun struct {
26*4882a593Smuzhiyun struct mbox_client client;
27*4882a593Smuzhiyun struct mbox_chan *channel;
28*4882a593Smuzhiyun } mbox;
29*4882a593Smuzhiyun };
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun static inline struct tegra_bpmp *
mbox_client_to_bpmp(struct mbox_client * client)32*4882a593Smuzhiyun mbox_client_to_bpmp(struct mbox_client *client)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun struct tegra186_bpmp *priv;
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun priv = container_of(client, struct tegra186_bpmp, mbox.client);
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun return priv->parent;
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun
tegra186_bpmp_is_message_ready(struct tegra_bpmp_channel * channel)41*4882a593Smuzhiyun static bool tegra186_bpmp_is_message_ready(struct tegra_bpmp_channel *channel)
42*4882a593Smuzhiyun {
43*4882a593Smuzhiyun void *frame;
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun frame = tegra_ivc_read_get_next_frame(channel->ivc);
46*4882a593Smuzhiyun if (IS_ERR(frame)) {
47*4882a593Smuzhiyun channel->ib = NULL;
48*4882a593Smuzhiyun return false;
49*4882a593Smuzhiyun }
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun channel->ib = frame;
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun return true;
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun
tegra186_bpmp_is_channel_free(struct tegra_bpmp_channel * channel)56*4882a593Smuzhiyun static bool tegra186_bpmp_is_channel_free(struct tegra_bpmp_channel *channel)
57*4882a593Smuzhiyun {
58*4882a593Smuzhiyun void *frame;
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun frame = tegra_ivc_write_get_next_frame(channel->ivc);
61*4882a593Smuzhiyun if (IS_ERR(frame)) {
62*4882a593Smuzhiyun channel->ob = NULL;
63*4882a593Smuzhiyun return false;
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun channel->ob = frame;
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun return true;
69*4882a593Smuzhiyun }
70*4882a593Smuzhiyun
tegra186_bpmp_ack_message(struct tegra_bpmp_channel * channel)71*4882a593Smuzhiyun static int tegra186_bpmp_ack_message(struct tegra_bpmp_channel *channel)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun return tegra_ivc_read_advance(channel->ivc);
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun
tegra186_bpmp_post_message(struct tegra_bpmp_channel * channel)76*4882a593Smuzhiyun static int tegra186_bpmp_post_message(struct tegra_bpmp_channel *channel)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun return tegra_ivc_write_advance(channel->ivc);
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
tegra186_bpmp_ring_doorbell(struct tegra_bpmp * bpmp)81*4882a593Smuzhiyun static int tegra186_bpmp_ring_doorbell(struct tegra_bpmp *bpmp)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun struct tegra186_bpmp *priv = bpmp->priv;
84*4882a593Smuzhiyun int err;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun err = mbox_send_message(priv->mbox.channel, NULL);
87*4882a593Smuzhiyun if (err < 0)
88*4882a593Smuzhiyun return err;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun mbox_client_txdone(priv->mbox.channel, 0);
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun return 0;
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun
tegra186_bpmp_ivc_notify(struct tegra_ivc * ivc,void * data)95*4882a593Smuzhiyun static void tegra186_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun struct tegra_bpmp *bpmp = data;
98*4882a593Smuzhiyun struct tegra186_bpmp *priv = bpmp->priv;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun if (WARN_ON(priv->mbox.channel == NULL))
101*4882a593Smuzhiyun return;
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun tegra186_bpmp_ring_doorbell(bpmp);
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
tegra186_bpmp_channel_init(struct tegra_bpmp_channel * channel,struct tegra_bpmp * bpmp,unsigned int index)106*4882a593Smuzhiyun static int tegra186_bpmp_channel_init(struct tegra_bpmp_channel *channel,
107*4882a593Smuzhiyun struct tegra_bpmp *bpmp,
108*4882a593Smuzhiyun unsigned int index)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun struct tegra186_bpmp *priv = bpmp->priv;
111*4882a593Smuzhiyun size_t message_size, queue_size;
112*4882a593Smuzhiyun unsigned int offset;
113*4882a593Smuzhiyun int err;
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc),
116*4882a593Smuzhiyun GFP_KERNEL);
117*4882a593Smuzhiyun if (!channel->ivc)
118*4882a593Smuzhiyun return -ENOMEM;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun message_size = tegra_ivc_align(MSG_MIN_SZ);
121*4882a593Smuzhiyun queue_size = tegra_ivc_total_queue_size(message_size);
122*4882a593Smuzhiyun offset = queue_size * index;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun err = tegra_ivc_init(channel->ivc, NULL,
125*4882a593Smuzhiyun priv->rx.virt + offset, priv->rx.phys + offset,
126*4882a593Smuzhiyun priv->tx.virt + offset, priv->tx.phys + offset,
127*4882a593Smuzhiyun 1, message_size, tegra186_bpmp_ivc_notify,
128*4882a593Smuzhiyun bpmp);
129*4882a593Smuzhiyun if (err < 0) {
130*4882a593Smuzhiyun dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n",
131*4882a593Smuzhiyun index, err);
132*4882a593Smuzhiyun return err;
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun init_completion(&channel->completion);
136*4882a593Smuzhiyun channel->bpmp = bpmp;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun return 0;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun
tegra186_bpmp_channel_reset(struct tegra_bpmp_channel * channel)141*4882a593Smuzhiyun static void tegra186_bpmp_channel_reset(struct tegra_bpmp_channel *channel)
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun /* reset the channel state */
144*4882a593Smuzhiyun tegra_ivc_reset(channel->ivc);
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun /* sync the channel state with BPMP */
147*4882a593Smuzhiyun while (tegra_ivc_notified(channel->ivc))
148*4882a593Smuzhiyun ;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
tegra186_bpmp_channel_cleanup(struct tegra_bpmp_channel * channel)151*4882a593Smuzhiyun static void tegra186_bpmp_channel_cleanup(struct tegra_bpmp_channel *channel)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun tegra_ivc_cleanup(channel->ivc);
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun
mbox_handle_rx(struct mbox_client * client,void * data)156*4882a593Smuzhiyun static void mbox_handle_rx(struct mbox_client *client, void *data)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client);
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun tegra_bpmp_handle_rx(bpmp);
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
tegra186_bpmp_init(struct tegra_bpmp * bpmp)163*4882a593Smuzhiyun static int tegra186_bpmp_init(struct tegra_bpmp *bpmp)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun struct tegra186_bpmp *priv;
166*4882a593Smuzhiyun unsigned int i;
167*4882a593Smuzhiyun int err;
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL);
170*4882a593Smuzhiyun if (!priv)
171*4882a593Smuzhiyun return -ENOMEM;
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun bpmp->priv = priv;
174*4882a593Smuzhiyun priv->parent = bpmp;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun priv->tx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 0);
177*4882a593Smuzhiyun if (!priv->tx.pool) {
178*4882a593Smuzhiyun dev_err(bpmp->dev, "TX shmem pool not found\n");
179*4882a593Smuzhiyun return -EPROBE_DEFER;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun priv->tx.virt = gen_pool_dma_alloc(priv->tx.pool, 4096, &priv->tx.phys);
183*4882a593Smuzhiyun if (!priv->tx.virt) {
184*4882a593Smuzhiyun dev_err(bpmp->dev, "failed to allocate from TX pool\n");
185*4882a593Smuzhiyun return -ENOMEM;
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun priv->rx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 1);
189*4882a593Smuzhiyun if (!priv->rx.pool) {
190*4882a593Smuzhiyun dev_err(bpmp->dev, "RX shmem pool not found\n");
191*4882a593Smuzhiyun err = -EPROBE_DEFER;
192*4882a593Smuzhiyun goto free_tx;
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun priv->rx.virt = gen_pool_dma_alloc(priv->rx.pool, 4096, &priv->rx.phys);
196*4882a593Smuzhiyun if (!priv->rx.virt) {
197*4882a593Smuzhiyun dev_err(bpmp->dev, "failed to allocate from RX pool\n");
198*4882a593Smuzhiyun err = -ENOMEM;
199*4882a593Smuzhiyun goto free_tx;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun err = tegra186_bpmp_channel_init(bpmp->tx_channel, bpmp,
203*4882a593Smuzhiyun bpmp->soc->channels.cpu_tx.offset);
204*4882a593Smuzhiyun if (err < 0)
205*4882a593Smuzhiyun goto free_rx;
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun err = tegra186_bpmp_channel_init(bpmp->rx_channel, bpmp,
208*4882a593Smuzhiyun bpmp->soc->channels.cpu_rx.offset);
209*4882a593Smuzhiyun if (err < 0)
210*4882a593Smuzhiyun goto cleanup_tx_channel;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun for (i = 0; i < bpmp->threaded.count; i++) {
213*4882a593Smuzhiyun unsigned int index = bpmp->soc->channels.thread.offset + i;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun err = tegra186_bpmp_channel_init(&bpmp->threaded_channels[i],
216*4882a593Smuzhiyun bpmp, index);
217*4882a593Smuzhiyun if (err < 0)
218*4882a593Smuzhiyun goto cleanup_channels;
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun /* mbox registration */
222*4882a593Smuzhiyun priv->mbox.client.dev = bpmp->dev;
223*4882a593Smuzhiyun priv->mbox.client.rx_callback = mbox_handle_rx;
224*4882a593Smuzhiyun priv->mbox.client.tx_block = false;
225*4882a593Smuzhiyun priv->mbox.client.knows_txdone = false;
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun priv->mbox.channel = mbox_request_channel(&priv->mbox.client, 0);
228*4882a593Smuzhiyun if (IS_ERR(priv->mbox.channel)) {
229*4882a593Smuzhiyun err = PTR_ERR(priv->mbox.channel);
230*4882a593Smuzhiyun dev_err(bpmp->dev, "failed to get HSP mailbox: %d\n", err);
231*4882a593Smuzhiyun goto cleanup_channels;
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun tegra186_bpmp_channel_reset(bpmp->tx_channel);
235*4882a593Smuzhiyun tegra186_bpmp_channel_reset(bpmp->rx_channel);
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun for (i = 0; i < bpmp->threaded.count; i++)
238*4882a593Smuzhiyun tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]);
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun return 0;
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun cleanup_channels:
243*4882a593Smuzhiyun for (i = 0; i < bpmp->threaded.count; i++) {
244*4882a593Smuzhiyun if (!bpmp->threaded_channels[i].bpmp)
245*4882a593Smuzhiyun continue;
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]);
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun tegra186_bpmp_channel_cleanup(bpmp->rx_channel);
251*4882a593Smuzhiyun cleanup_tx_channel:
252*4882a593Smuzhiyun tegra186_bpmp_channel_cleanup(bpmp->tx_channel);
253*4882a593Smuzhiyun free_rx:
254*4882a593Smuzhiyun gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096);
255*4882a593Smuzhiyun free_tx:
256*4882a593Smuzhiyun gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096);
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun return err;
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun
tegra186_bpmp_deinit(struct tegra_bpmp * bpmp)261*4882a593Smuzhiyun static void tegra186_bpmp_deinit(struct tegra_bpmp *bpmp)
262*4882a593Smuzhiyun {
263*4882a593Smuzhiyun struct tegra186_bpmp *priv = bpmp->priv;
264*4882a593Smuzhiyun unsigned int i;
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun mbox_free_channel(priv->mbox.channel);
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun for (i = 0; i < bpmp->threaded.count; i++)
269*4882a593Smuzhiyun tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]);
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun tegra186_bpmp_channel_cleanup(bpmp->rx_channel);
272*4882a593Smuzhiyun tegra186_bpmp_channel_cleanup(bpmp->tx_channel);
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096);
275*4882a593Smuzhiyun gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096);
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun
tegra186_bpmp_resume(struct tegra_bpmp * bpmp)278*4882a593Smuzhiyun static int tegra186_bpmp_resume(struct tegra_bpmp *bpmp)
279*4882a593Smuzhiyun {
280*4882a593Smuzhiyun unsigned int i;
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun /* reset message channels */
283*4882a593Smuzhiyun tegra186_bpmp_channel_reset(bpmp->tx_channel);
284*4882a593Smuzhiyun tegra186_bpmp_channel_reset(bpmp->rx_channel);
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun for (i = 0; i < bpmp->threaded.count; i++)
287*4882a593Smuzhiyun tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]);
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun return 0;
290*4882a593Smuzhiyun }
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun const struct tegra_bpmp_ops tegra186_bpmp_ops = {
293*4882a593Smuzhiyun .init = tegra186_bpmp_init,
294*4882a593Smuzhiyun .deinit = tegra186_bpmp_deinit,
295*4882a593Smuzhiyun .is_response_ready = tegra186_bpmp_is_message_ready,
296*4882a593Smuzhiyun .is_request_ready = tegra186_bpmp_is_message_ready,
297*4882a593Smuzhiyun .ack_response = tegra186_bpmp_ack_message,
298*4882a593Smuzhiyun .ack_request = tegra186_bpmp_ack_message,
299*4882a593Smuzhiyun .is_response_channel_free = tegra186_bpmp_is_channel_free,
300*4882a593Smuzhiyun .is_request_channel_free = tegra186_bpmp_is_channel_free,
301*4882a593Smuzhiyun .post_response = tegra186_bpmp_post_message,
302*4882a593Smuzhiyun .post_request = tegra186_bpmp_post_message,
303*4882a593Smuzhiyun .ring_doorbell = tegra186_bpmp_ring_doorbell,
304*4882a593Smuzhiyun .resume = tegra186_bpmp_resume,
305*4882a593Smuzhiyun };
306