1/*
2 * OMAP Remote Processor driver
3 *
4 * Copyright (C) 2011 Texas Instruments, Inc.
5 * Copyright (C) 2011 Google, Inc.
6 *
7 * Ohad Ben-Cohen <ohad@wizery.com>
8 * Brian Swetland <swetland@google.com>
9 * Fernando Guzman Lugo <fernando.lugo@ti.com>
10 * Mark Grosen <mgrosen@ti.com>
11 * Suman Anna <s-anna@ti.com>
12 * Hari Kanigeri <h-kanigeri2@ti.com>
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * version 2 as published by the Free Software Foundation.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 * GNU General Public License for more details.
22 */
23
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <linux/err.h>
27#include <linux/platform_device.h>
28#include <linux/dma-mapping.h>
29#include <linux/remoteproc.h>
30#include <linux/mailbox_client.h>
31#include <linux/omap-mailbox.h>
32
33#include <linux/platform_data/remoteproc-omap.h>
34
35#include "omap_remoteproc.h"
36#include "remoteproc_internal.h"
37
38/**
39 * struct omap_rproc - omap remote processor state
40 * @mbox: mailbox channel handle
41 * @client: mailbox client to request the mailbox channel
42 * @rproc: rproc handle
43 */
44struct omap_rproc {
45	struct mbox_chan *mbox;
46	struct mbox_client client;
47	struct rproc *rproc;
48};
49
50/**
51 * omap_rproc_mbox_callback() - inbound mailbox message handler
52 * @client: mailbox client pointer used for requesting the mailbox channel
53 * @data: mailbox payload
54 *
55 * This handler is invoked by omap's mailbox driver whenever a mailbox
56 * message is received. Usually, the mailbox payload simply contains
57 * the index of the virtqueue that is kicked by the remote processor,
58 * and we let remoteproc core handle it.
59 *
60 * In addition to virtqueue indices, we also have some out-of-band values
61 * that indicates different events. Those values are deliberately very
62 * big so they don't coincide with virtqueue indices.
63 */
64static void omap_rproc_mbox_callback(struct mbox_client *client, void *data)
65{
66	struct omap_rproc *oproc = container_of(client, struct omap_rproc,
67						client);
68	struct device *dev = oproc->rproc->dev.parent;
69	const char *name = oproc->rproc->name;
70	u32 msg = (u32)data;
71
72	dev_dbg(dev, "mbox msg: 0x%x\n", msg);
73
74	switch (msg) {
75	case RP_MBOX_CRASH:
76		/* just log this for now. later, we'll also do recovery */
77		dev_err(dev, "omap rproc %s crashed\n", name);
78		break;
79	case RP_MBOX_ECHO_REPLY:
80		dev_info(dev, "received echo reply from %s\n", name);
81		break;
82	default:
83		/* msg contains the index of the triggered vring */
84		if (rproc_vq_interrupt(oproc->rproc, msg) == IRQ_NONE)
85			dev_dbg(dev, "no message was found in vqid %d\n", msg);
86	}
87}
88
89/* kick a virtqueue */
90static void omap_rproc_kick(struct rproc *rproc, int vqid)
91{
92	struct omap_rproc *oproc = rproc->priv;
93	struct device *dev = rproc->dev.parent;
94	int ret;
95
96	/* send the index of the triggered virtqueue in the mailbox payload */
97	ret = mbox_send_message(oproc->mbox, (void *)vqid);
98	if (ret < 0)
99		dev_err(dev, "omap_mbox_msg_send failed: %d\n", ret);
100}
101
102/*
103 * Power up the remote processor.
104 *
105 * This function will be invoked only after the firmware for this rproc
106 * was loaded, parsed successfully, and all of its resource requirements
107 * were met.
108 */
109static int omap_rproc_start(struct rproc *rproc)
110{
111	struct omap_rproc *oproc = rproc->priv;
112	struct device *dev = rproc->dev.parent;
113	struct platform_device *pdev = to_platform_device(dev);
114	struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
115	int ret;
116	struct mbox_client *client = &oproc->client;
117
118	if (pdata->set_bootaddr)
119		pdata->set_bootaddr(rproc->bootaddr);
120
121	client->dev = dev;
122	client->tx_done = NULL;
123	client->rx_callback = omap_rproc_mbox_callback;
124	client->tx_block = false;
125	client->knows_txdone = false;
126
127	oproc->mbox = omap_mbox_request_channel(client, pdata->mbox_name);
128	if (IS_ERR(oproc->mbox)) {
129		ret = -EBUSY;
130		dev_err(dev, "mbox_request_channel failed: %ld\n",
131			PTR_ERR(oproc->mbox));
132		return ret;
133	}
134
135	/*
136	 * Ping the remote processor. this is only for sanity-sake;
137	 * there is no functional effect whatsoever.
138	 *
139	 * Note that the reply will _not_ arrive immediately: this message
140	 * will wait in the mailbox fifo until the remote processor is booted.
141	 */
142	ret = mbox_send_message(oproc->mbox, (void *)RP_MBOX_ECHO_REQUEST);
143	if (ret < 0) {
144		dev_err(dev, "mbox_send_message failed: %d\n", ret);
145		goto put_mbox;
146	}
147
148	ret = pdata->device_enable(pdev);
149	if (ret) {
150		dev_err(dev, "omap_device_enable failed: %d\n", ret);
151		goto put_mbox;
152	}
153
154	return 0;
155
156put_mbox:
157	mbox_free_channel(oproc->mbox);
158	return ret;
159}
160
161/* power off the remote processor */
162static int omap_rproc_stop(struct rproc *rproc)
163{
164	struct device *dev = rproc->dev.parent;
165	struct platform_device *pdev = to_platform_device(dev);
166	struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
167	struct omap_rproc *oproc = rproc->priv;
168	int ret;
169
170	ret = pdata->device_shutdown(pdev);
171	if (ret)
172		return ret;
173
174	mbox_free_channel(oproc->mbox);
175
176	return 0;
177}
178
179static struct rproc_ops omap_rproc_ops = {
180	.start		= omap_rproc_start,
181	.stop		= omap_rproc_stop,
182	.kick		= omap_rproc_kick,
183};
184
185static int omap_rproc_probe(struct platform_device *pdev)
186{
187	struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
188	struct omap_rproc *oproc;
189	struct rproc *rproc;
190	int ret;
191
192	ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
193	if (ret) {
194		dev_err(&pdev->dev, "dma_set_coherent_mask: %d\n", ret);
195		return ret;
196	}
197
198	rproc = rproc_alloc(&pdev->dev, pdata->name, &omap_rproc_ops,
199				pdata->firmware, sizeof(*oproc));
200	if (!rproc)
201		return -ENOMEM;
202
203	oproc = rproc->priv;
204	oproc->rproc = rproc;
205	/* All existing OMAP IPU and DSP processors have an MMU */
206	rproc->has_iommu = true;
207
208	platform_set_drvdata(pdev, rproc);
209
210	ret = rproc_add(rproc);
211	if (ret)
212		goto free_rproc;
213
214	return 0;
215
216free_rproc:
217	rproc_put(rproc);
218	return ret;
219}
220
221static int omap_rproc_remove(struct platform_device *pdev)
222{
223	struct rproc *rproc = platform_get_drvdata(pdev);
224
225	rproc_del(rproc);
226	rproc_put(rproc);
227
228	return 0;
229}
230
231static struct platform_driver omap_rproc_driver = {
232	.probe = omap_rproc_probe,
233	.remove = omap_rproc_remove,
234	.driver = {
235		.name = "omap-rproc",
236	},
237};
238
239module_platform_driver(omap_rproc_driver);
240
241MODULE_LICENSE("GPL v2");
242MODULE_DESCRIPTION("OMAP Remote Processor control driver");
243