Skip to content

Commit 5dfb28c

Browse files
tnmyshmathieupoirier
authored andcommitted
remoteproc: xilinx: Add mailbox channels for rpmsg
This patch makes each r5 core mailbox client and uses tx and rx channels to send and receive data to/from remote processor respectively. This is needed for rpmsg communication to remote processor. Signed-off-by: Tanmay Shah <tanmay.shah@amd.com> Link: https://lore.kernel.org/r/20230311012407.1292118-6-tanmay.shah@amd.com Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
1 parent 81c18e0 commit 5dfb28c

1 file changed

Lines changed: 217 additions & 1 deletion

File tree

drivers/remoteproc/xlnx_r5_remoteproc.c

Lines changed: 217 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,23 @@
88
#include <linux/dma-mapping.h>
99
#include <linux/firmware/xlnx-zynqmp.h>
1010
#include <linux/kernel.h>
11+
#include <linux/mailbox_client.h>
12+
#include <linux/mailbox/zynqmp-ipi-message.h>
1113
#include <linux/module.h>
1214
#include <linux/of_address.h>
1315
#include <linux/of_platform.h>
1416
#include <linux/of_reserved_mem.h>
1517
#include <linux/platform_device.h>
1618
#include <linux/remoteproc.h>
17-
#include <linux/slab.h>
1819

1920
#include "remoteproc_internal.h"
2021

22+
/* IPI buffer MAX length */
23+
#define IPI_BUF_LEN_MAX 32U
24+
25+
/* RX mailbox client buffer max length */
26+
#define MBOX_CLIENT_BUF_MAX (IPI_BUF_LEN_MAX + \
27+
sizeof(struct zynqmp_ipi_message))
2128
/*
2229
* settings for RPU cluster mode which
2330
* reflects possible values of xlnx,cluster-mode dt-property
@@ -43,6 +50,27 @@ struct mem_bank_data {
4350
char *bank_name;
4451
};
4552

53+
/**
54+
* struct mbox_info
55+
*
56+
* @rx_mc_buf: to copy data from mailbox rx channel
57+
* @tx_mc_buf: to copy data to mailbox tx channel
58+
* @r5_core: this mailbox's corresponding r5_core pointer
59+
* @mbox_work: schedule work after receiving data from mailbox
60+
* @mbox_cl: mailbox client
61+
* @tx_chan: mailbox tx channel
62+
* @rx_chan: mailbox rx channel
63+
*/
64+
struct mbox_info {
65+
unsigned char rx_mc_buf[MBOX_CLIENT_BUF_MAX];
66+
unsigned char tx_mc_buf[MBOX_CLIENT_BUF_MAX];
67+
struct zynqmp_r5_core *r5_core;
68+
struct work_struct mbox_work;
69+
struct mbox_client mbox_cl;
70+
struct mbox_chan *tx_chan;
71+
struct mbox_chan *rx_chan;
72+
};
73+
4674
/*
4775
* Hardcoded TCM bank values. This will be removed once TCM bindings are
4876
* accepted for system-dt specifications and upstreamed in linux kernel
@@ -63,6 +91,7 @@ static const struct mem_bank_data zynqmp_tcm_banks[] = {
6391
* @tcm_banks: array of each TCM bank data
6492
* @rproc: rproc handle
6593
* @pm_domain_id: RPU CPU power domain id
94+
* @ipi: pointer to mailbox information
6695
*/
6796
struct zynqmp_r5_core {
6897
struct device *dev;
@@ -71,6 +100,7 @@ struct zynqmp_r5_core {
71100
struct mem_bank_data **tcm_banks;
72101
struct rproc *rproc;
73102
u32 pm_domain_id;
103+
struct mbox_info *ipi;
74104
};
75105

76106
/**
@@ -88,6 +118,178 @@ struct zynqmp_r5_cluster {
88118
struct zynqmp_r5_core **r5_cores;
89119
};
90120

121+
/**
122+
* event_notified_idr_cb() - callback for vq_interrupt per notifyid
123+
* @id: rproc->notify id
124+
* @ptr: pointer to idr private data
125+
* @data: data passed to idr_for_each callback
126+
*
127+
* Pass notification to remoteproc virtio
128+
*
129+
* Return: 0. having return is to satisfy the idr_for_each() function
130+
* pointer input argument requirement.
131+
**/
132+
static int event_notified_idr_cb(int id, void *ptr, void *data)
133+
{
134+
struct rproc *rproc = data;
135+
136+
if (rproc_vq_interrupt(rproc, id) == IRQ_NONE)
137+
dev_dbg(&rproc->dev, "data not found for vqid=%d\n", id);
138+
139+
return 0;
140+
}
141+
142+
/**
143+
* handle_event_notified() - remoteproc notification work function
144+
* @work: pointer to the work structure
145+
*
146+
* It checks each registered remoteproc notify IDs.
147+
*/
148+
static void handle_event_notified(struct work_struct *work)
149+
{
150+
struct mbox_info *ipi;
151+
struct rproc *rproc;
152+
153+
ipi = container_of(work, struct mbox_info, mbox_work);
154+
rproc = ipi->r5_core->rproc;
155+
156+
/*
157+
* We only use IPI for interrupt. The RPU firmware side may or may
158+
* not write the notifyid when it trigger IPI.
159+
* And thus, we scan through all the registered notifyids and
160+
* find which one is valid to get the message.
161+
* Even if message from firmware is NULL, we attempt to get vqid
162+
*/
163+
idr_for_each(&rproc->notifyids, event_notified_idr_cb, rproc);
164+
}
165+
166+
/**
167+
* zynqmp_r5_mb_rx_cb() - receive channel mailbox callback
168+
* @cl: mailbox client
169+
* @msg: message pointer
170+
*
171+
* Receive data from ipi buffer, ack interrupt and then
172+
* it will schedule the R5 notification work.
173+
*/
174+
static void zynqmp_r5_mb_rx_cb(struct mbox_client *cl, void *msg)
175+
{
176+
struct zynqmp_ipi_message *ipi_msg, *buf_msg;
177+
struct mbox_info *ipi;
178+
size_t len;
179+
180+
ipi = container_of(cl, struct mbox_info, mbox_cl);
181+
182+
/* copy data from ipi buffer to r5_core */
183+
ipi_msg = (struct zynqmp_ipi_message *)msg;
184+
buf_msg = (struct zynqmp_ipi_message *)ipi->rx_mc_buf;
185+
len = ipi_msg->len;
186+
if (len > IPI_BUF_LEN_MAX) {
187+
dev_warn(cl->dev, "msg size exceeded than %d\n",
188+
IPI_BUF_LEN_MAX);
189+
len = IPI_BUF_LEN_MAX;
190+
}
191+
buf_msg->len = len;
192+
memcpy(buf_msg->data, ipi_msg->data, len);
193+
194+
/* received and processed interrupt ack */
195+
if (mbox_send_message(ipi->rx_chan, NULL) < 0)
196+
dev_err(cl->dev, "ack failed to mbox rx_chan\n");
197+
198+
schedule_work(&ipi->mbox_work);
199+
}
200+
201+
/**
202+
* zynqmp_r5_setup_mbox() - Setup mailboxes related properties
203+
* this is used for each individual R5 core
204+
*
205+
* @cdev: child node device
206+
*
207+
* Function to setup mailboxes related properties
208+
* return : NULL if failed else pointer to mbox_info
209+
*/
210+
static struct mbox_info *zynqmp_r5_setup_mbox(struct device *cdev)
211+
{
212+
struct mbox_client *mbox_cl;
213+
struct mbox_info *ipi;
214+
215+
ipi = kzalloc(sizeof(*ipi), GFP_KERNEL);
216+
if (!ipi)
217+
return NULL;
218+
219+
mbox_cl = &ipi->mbox_cl;
220+
mbox_cl->rx_callback = zynqmp_r5_mb_rx_cb;
221+
mbox_cl->tx_block = false;
222+
mbox_cl->knows_txdone = false;
223+
mbox_cl->tx_done = NULL;
224+
mbox_cl->dev = cdev;
225+
226+
/* Request TX and RX channels */
227+
ipi->tx_chan = mbox_request_channel_byname(mbox_cl, "tx");
228+
if (IS_ERR(ipi->tx_chan)) {
229+
ipi->tx_chan = NULL;
230+
kfree(ipi);
231+
dev_warn(cdev, "mbox tx channel request failed\n");
232+
return NULL;
233+
}
234+
235+
ipi->rx_chan = mbox_request_channel_byname(mbox_cl, "rx");
236+
if (IS_ERR(ipi->rx_chan)) {
237+
mbox_free_channel(ipi->tx_chan);
238+
ipi->rx_chan = NULL;
239+
ipi->tx_chan = NULL;
240+
kfree(ipi);
241+
dev_warn(cdev, "mbox rx channel request failed\n");
242+
return NULL;
243+
}
244+
245+
INIT_WORK(&ipi->mbox_work, handle_event_notified);
246+
247+
return ipi;
248+
}
249+
250+
static void zynqmp_r5_free_mbox(struct mbox_info *ipi)
251+
{
252+
if (!ipi)
253+
return;
254+
255+
if (ipi->tx_chan) {
256+
mbox_free_channel(ipi->tx_chan);
257+
ipi->tx_chan = NULL;
258+
}
259+
260+
if (ipi->rx_chan) {
261+
mbox_free_channel(ipi->rx_chan);
262+
ipi->rx_chan = NULL;
263+
}
264+
265+
kfree(ipi);
266+
}
267+
268+
/*
269+
* zynqmp_r5_core_kick() - kick a firmware if mbox is provided
270+
* @rproc: r5 core's corresponding rproc structure
271+
* @vqid: virtqueue ID
272+
*/
273+
static void zynqmp_r5_rproc_kick(struct rproc *rproc, int vqid)
274+
{
275+
struct zynqmp_r5_core *r5_core = rproc->priv;
276+
struct device *dev = r5_core->dev;
277+
struct zynqmp_ipi_message *mb_msg;
278+
struct mbox_info *ipi;
279+
int ret;
280+
281+
ipi = r5_core->ipi;
282+
if (!ipi)
283+
return;
284+
285+
mb_msg = (struct zynqmp_ipi_message *)ipi->tx_mc_buf;
286+
memcpy(mb_msg->data, &vqid, sizeof(vqid));
287+
mb_msg->len = sizeof(vqid);
288+
ret = mbox_send_message(ipi->tx_chan, mb_msg);
289+
if (ret < 0)
290+
dev_warn(dev, "failed to send message\n");
291+
}
292+
91293
/*
92294
* zynqmp_r5_set_mode()
93295
*
@@ -624,6 +826,7 @@ static const struct rproc_ops zynqmp_r5_rproc_ops = {
624826
.find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
625827
.sanity_check = rproc_elf_sanity_check,
626828
.get_boot_addr = rproc_elf_get_boot_addr,
829+
.kick = zynqmp_r5_rproc_kick,
627830
};
628831

629832
/**
@@ -799,6 +1002,7 @@ static int zynqmp_r5_cluster_init(struct zynqmp_r5_cluster *cluster)
7991002
struct device_node *child;
8001003
enum rpu_tcm_comb tcm_mode;
8011004
int core_count, ret, i;
1005+
struct mbox_info *ipi;
8021006

8031007
ret = of_property_read_u32(dev_node, "xlnx,cluster-mode", &cluster_mode);
8041008

@@ -878,6 +1082,16 @@ static int zynqmp_r5_cluster_init(struct zynqmp_r5_cluster *cluster)
8781082
goto release_r5_cores;
8791083
}
8801084

1085+
/*
1086+
* If mailbox nodes are disabled using "status" property then
1087+
* setting up mailbox channels will fail.
1088+
*/
1089+
ipi = zynqmp_r5_setup_mbox(&child_pdev->dev);
1090+
if (ipi) {
1091+
r5_cores[i]->ipi = ipi;
1092+
ipi->r5_core = r5_cores[i];
1093+
}
1094+
8811095
/*
8821096
* If two child nodes are available in dts in lockstep mode,
8831097
* then ignore second child node.
@@ -915,6 +1129,7 @@ static int zynqmp_r5_cluster_init(struct zynqmp_r5_cluster *cluster)
9151129
while (i >= 0) {
9161130
put_device(child_devs[i]);
9171131
if (r5_cores[i]) {
1132+
zynqmp_r5_free_mbox(r5_cores[i]->ipi);
9181133
of_reserved_mem_device_release(r5_cores[i]->dev);
9191134
rproc_del(r5_cores[i]->rproc);
9201135
rproc_free(r5_cores[i]->rproc);
@@ -939,6 +1154,7 @@ static void zynqmp_r5_cluster_exit(void *data)
9391154

9401155
for (i = 0; i < cluster->core_count; i++) {
9411156
r5_core = cluster->r5_cores[i];
1157+
zynqmp_r5_free_mbox(r5_core->ipi);
9421158
of_reserved_mem_device_release(r5_core->dev);
9431159
put_device(r5_core->dev);
9441160
rproc_del(r5_core->rproc);

0 commit comments

Comments
 (0)