xref: /OK3568_Linux_fs/kernel/drivers/firmware/tegra/bpmp-tegra186.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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