Hello,
I have made the implementation look more proper. Also made some changes
suggested by Suman. Changes since V1:
* Delete timer upon mailbox release
* Filled in the stub ipc_links_unregister()
* Check kzalloc return for errors.
* Add the controller driver for OMAP2 class. I have taken the patch
for mailbox's hwmod from Suman's. The clients are yet to be converted
but this should be enough to convey the idea that the API could
work for OMAP as well. If I had access to STE documentation, I might
have done some implementation for U8500 as well.
arch/arm/mach-omap2/omap_hwmod_2420_data.c | 12 +
arch/arm/mach-omap2/omap_hwmod_2430_data.c | 11 +
arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 11 +
arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 13 +
drivers/cpufreq/highbank-cpufreq.c | 22 +-
drivers/mailbox/Kconfig | 9 +
drivers/mailbox/Makefile | 7 +-
drivers/mailbox/mailbox.c | 494 ++++++++++++++++++++++++++++
drivers/mailbox/omap2.c | 420 +++++++++++++++++++++++
drivers/mailbox/pl320-ipc.c | 198 -----------
drivers/mailbox/pl320.c | 212 ++++++++++++
include/linux/mailbox.h | 28 +-
include/linux/mailbox_client.h | 85 +++++
include/linux/mailbox_controller.h | 102 ++++++
include/linux/platform_data/mailbox-omap.h | 64 ++++
15 files changed, 1473 insertions(+), 215 deletions(-)
Cheers,
-Jassi
From: Suman Anna <[email protected]>
The patch 30058677 "ARM / highbank: add support for pl320 IPC"
added a pl320 IPC specific header file as a generic mailbox.h.
This file has been renamed appropriately to allow the
introduction of the generic mailbox API framework.
Acked-by: Mark Langsdorf <[email protected]>
Cc: Rafael J. Wysocki <[email protected]>
Signed-off-by: Suman Anna <[email protected]>
---
drivers/cpufreq/highbank-cpufreq.c | 2 +-
drivers/mailbox/pl320-ipc.c | 2 +-
include/linux/mailbox.h | 17 -----------------
include/linux/pl320-ipc.h | 17 +++++++++++++++++
4 files changed, 19 insertions(+), 19 deletions(-)
delete mode 100644 include/linux/mailbox.h
create mode 100644 include/linux/pl320-ipc.h
diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c
index b61b5a3..3118b87 100644
--- a/drivers/cpufreq/highbank-cpufreq.c
+++ b/drivers/cpufreq/highbank-cpufreq.c
@@ -19,7 +19,7 @@
#include <linux/cpu.h>
#include <linux/err.h>
#include <linux/of.h>
-#include <linux/mailbox.h>
+#include <linux/pl320-ipc.h>
#include <linux/platform_device.h>
#define HB_CPUFREQ_CHANGE_NOTE 0x80000001
diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c
index d873cba..f3755e0 100644
--- a/drivers/mailbox/pl320-ipc.c
+++ b/drivers/mailbox/pl320-ipc.c
@@ -26,7 +26,7 @@
#include <linux/device.h>
#include <linux/amba/bus.h>
-#include <linux/mailbox.h>
+#include <linux/pl320-ipc.h>
#define IPCMxSOURCE(m) ((m) * 0x40)
#define IPCMxDSET(m) (((m) * 0x40) + 0x004)
diff --git a/include/linux/mailbox.h b/include/linux/mailbox.h
deleted file mode 100644
index 5161f63..0000000
--- a/include/linux/mailbox.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-int pl320_ipc_transmit(u32 *data);
-int pl320_ipc_register_notifier(struct notifier_block *nb);
-int pl320_ipc_unregister_notifier(struct notifier_block *nb);
diff --git a/include/linux/pl320-ipc.h b/include/linux/pl320-ipc.h
new file mode 100644
index 0000000..5161f63
--- /dev/null
+++ b/include/linux/pl320-ipc.h
@@ -0,0 +1,17 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+int pl320_ipc_transmit(u32 *data);
+int pl320_ipc_register_notifier(struct notifier_block *nb);
+int pl320_ipc_unregister_notifier(struct notifier_block *nb);
--
1.7.10.4
Introduce common framework for client/protocol drivers and
controller drivers of Inter-Processor-Communication (IPC).
Client driver developers should have a look at
include/linux/mailbox_client.h to understand the part of
the API exposed to client drivers.
Similarly controller driver developers should have a look
at include/linux/mailbox_controller.h
Signed-off-by: Jassi Brar <[email protected]>
---
drivers/mailbox/Makefile | 4 +
drivers/mailbox/mailbox.c | 494 ++++++++++++++++++++++++++++++++++++
include/linux/mailbox.h | 17 ++
include/linux/mailbox_client.h | 85 +++++++
include/linux/mailbox_controller.h | 102 ++++++++
5 files changed, 702 insertions(+)
create mode 100644 drivers/mailbox/mailbox.c
create mode 100644 include/linux/mailbox.h
create mode 100644 include/linux/mailbox_client.h
create mode 100644 include/linux/mailbox_controller.h
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 543ad6a..fefef7e 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -1 +1,5 @@
+# Generic MAILBOX API
+
+obj-$(CONFIG_MAILBOX) += mailbox.o
+
obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
new file mode 100644
index 0000000..a93c22f
--- /dev/null
+++ b/drivers/mailbox/mailbox.c
@@ -0,0 +1,494 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mailbox_client.h>
+#include <linux/mailbox_controller.h>
+
+/*
+ * The length of circular buffer for queuing messages from a client.
+ * 'msg_count' tracks the number of buffered messages while 'msg_free'
+ * is the index where the next message would be buffered.
+ * We shouldn't need it too big because every transferr is interrupt
+ * triggered and if we have lots of data to transfer, the interrupt
+ * latencies are going to be the bottleneck, not the buffer length.
+ * Besides, ipc_send_message could be called from atomic context and
+ * the client could also queue another message from the notifier 'txcb'
+ * of the last transfer done.
+ */
+#define MBOX_TX_QUEUE_LEN 10
+
+#define TXDONE_BY_IRQ (1 << 0) /* controller has remote RTR irq */
+#define TXDONE_BY_POLL (1 << 1) /* controller can read status of last TX */
+#define TXDONE_BY_ACK (1 << 2) /* S/W ACK recevied by Client ticks the TX */
+
+struct ipc_chan {
+ char chan_name[32]; /* controller_name:link_name */
+ unsigned txdone_method;
+
+ /* Cached values from controller */
+ struct ipc_link *link;
+ struct ipc_link_ops *link_ops;
+
+ /* Cached values from client */
+ void (*rxcb)(void *data);
+ void (*txcb)(request_token_t t, enum xfer_result r);
+ bool tx_block;
+ unsigned long tx_tout;
+ struct completion tx_complete;
+
+ request_token_t active_token;
+ unsigned msg_count, msg_free;
+ void *msg_data[MBOX_TX_QUEUE_LEN];
+ /* Timer shared by all links of a controller */
+ struct tx_poll_timer *timer;
+ bool assigned;
+ /* Serialize access to the channel */
+ spinlock_t lock;
+ /* Hook to add to the global list of channels */
+ struct list_head node;
+ /* Notifier to all clients waiting on aquiring this channel */
+ struct blocking_notifier_head avail;
+};
+
+/*
+ * If the controller supports only TXDONE_BY_POLL, this
+ * timer polls all the links for txdone.
+ */
+struct tx_poll_timer {
+ struct timer_list poll;
+ unsigned period;
+};
+
+static LIST_HEAD(ipc_channels);
+static DEFINE_MUTEX(chpool_mutex);
+
+static request_token_t _add_to_rbuf(struct ipc_chan *chan, void *data)
+{
+ request_token_t idx;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ /* See if there is any space left */
+ if (chan->msg_count == MBOX_TX_QUEUE_LEN) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return 0;
+ }
+
+ idx = chan->msg_free;
+ chan->msg_data[idx] = data;
+ chan->msg_count++;
+
+ if (idx == MBOX_TX_QUEUE_LEN - 1)
+ chan->msg_free = 0;
+ else
+ chan->msg_free++;
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ return idx + 1;
+}
+
+static void _msg_submit(struct ipc_chan *chan)
+{
+ struct ipc_link *link = chan->link;
+ unsigned count, idx;
+ unsigned long flags;
+ void *data;
+ int err;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ if (!chan->msg_count || chan->active_token) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return;
+ }
+
+ count = chan->msg_count;
+ idx = chan->msg_free;
+ if (idx >= count)
+ idx -= count;
+ else
+ idx += MBOX_TX_QUEUE_LEN - count;
+
+ data = chan->msg_data[idx];
+
+ /* Try to submit a message to the IPC controller */
+ err = chan->link_ops->send_data(link, data);
+ if (!err) {
+ chan->active_token = idx + 1;
+ chan->msg_count--;
+ }
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+
+static void tx_tick(struct ipc_chan *chan, enum xfer_result r)
+{
+ unsigned long flags;
+ request_token_t t;
+
+ spin_lock_irqsave(&chan->lock, flags);
+ t = chan->active_token;
+ chan->active_token = 0;
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ /* Submit next message */
+ _msg_submit(chan);
+
+ /* Notify the client */
+ if (chan->tx_block)
+ complete(&chan->tx_complete);
+ else if (t && chan->txcb)
+ chan->txcb(t, r);
+}
+
+static void poll_txdone(unsigned long data)
+{
+ struct tx_poll_timer *timer = (struct tx_poll_timer *)data;
+ bool txdone, resched = false;
+ struct ipc_chan *chan;
+
+ list_for_each_entry(chan, &ipc_channels, node) {
+ if (chan->timer == timer
+ && chan->active_token && chan->assigned) {
+ resched = true;
+ txdone = chan->link_ops->last_tx_done(chan->link);
+ if (txdone)
+ tx_tick(chan, XFER_OK);
+ }
+ }
+
+ if (resched)
+ mod_timer(&timer->poll,
+ jiffies + msecs_to_jiffies(timer->period));
+}
+
+/*
+ * After 'startup' and before 'shutdown', the IPC controller driver
+ * notifies the API of data received over the link.
+ * The controller driver should make sure the 'RTR' is de-asserted since
+ * reception of the packet and until after this call returns.
+ * This call could be made from atomic context.
+ */
+void ipc_link_received_data(struct ipc_link *link, void *data)
+{
+ struct ipc_chan *chan = (struct ipc_chan *)link->api_priv;
+
+ /* No buffering the received data */
+ if (chan->rxcb)
+ chan->rxcb(data);
+}
+EXPORT_SYMBOL(ipc_link_received_data);
+
+/*
+ * The IPC controller driver notifies the API that the remote has
+ * asserted RTR and it could now send another message on the link.
+ */
+void ipc_link_txdone(struct ipc_link *link, enum xfer_result r)
+{
+ struct ipc_chan *chan = (struct ipc_chan *)link->api_priv;
+
+ if (unlikely(!(chan->txdone_method & TXDONE_BY_IRQ))) {
+ pr_err("Controller can't run the TX ticker\n");
+ return;
+ }
+
+ tx_tick(chan, r);
+}
+EXPORT_SYMBOL(ipc_link_txdone);
+
+/*
+ * The client/protocol had received some 'ACK' packet and it notifies
+ * the API that the last packet was sent successfully. This only works
+ * if the controller doesn't get IRQ for TX done.
+ */
+void ipc_client_txdone(void *channel, enum xfer_result r)
+{
+ struct ipc_chan *chan = (struct ipc_chan *)channel;
+ bool txdone = true;
+
+ if (unlikely(!(chan->txdone_method & TXDONE_BY_ACK))) {
+ pr_err("Client can't run the TX ticker\n");
+ return;
+ }
+
+ if (chan->txdone_method & TXDONE_BY_POLL)
+ txdone = chan->link_ops->last_tx_done(chan->link);
+
+ if (txdone)
+ tx_tick(chan, r);
+}
+EXPORT_SYMBOL(ipc_client_txdone);
+
+/*
+ * Called by a client to "put data on the h/w channel" so that if
+ * everything else is fine we don't need to do anything more locally
+ * for the remote to receive the data intact.
+ * In reality, the remote may receive it intact, corrupted or not at all.
+ * This could be called from atomic context as it simply
+ * queues the data and returns a token (request_token_t)
+ * against the request.
+ * The client is later notified of successful transmission of
+ * data over the channel via the 'txcb'. The client could in
+ * turn queue more messages from txcb.
+ */
+request_token_t ipc_send_message(void *channel, void *data)
+{
+ struct ipc_chan *chan = (struct ipc_chan *)channel;
+ request_token_t t;
+
+ if (!chan || !chan->assigned)
+ return 0;
+
+ t = _add_to_rbuf(chan, data);
+ if (!t)
+ pr_err("Try increasing MBOX_TX_QUEUE_LEN\n");
+
+ _msg_submit(chan);
+
+ if (chan->txdone_method == TXDONE_BY_POLL)
+ poll_txdone((unsigned long)chan->timer);
+
+ if (chan->tx_block && chan->active_token) {
+ int ret;
+ init_completion(&chan->tx_complete);
+ ret = wait_for_completion_timeout(&chan->tx_complete,
+ chan->tx_tout);
+ if (ret == 0) {
+ t = 0;
+ tx_tick(chan, XFER_ERR);
+ }
+ }
+
+ return t;
+}
+EXPORT_SYMBOL(ipc_send_message);
+
+/*
+ * A client driver asks for exclusive use of a channel/mailbox.
+ * If assigned, the channel has to be 'freed' before it could
+ * be assigned to some other client.
+ * After assignment, any packet received on this channel will be
+ * handed over to the client via the 'rxcb' callback.
+ * The 'txcb' callback is used to notify client upon sending the
+ * packet over the channel, which may or may not have been yet
+ * read by the remote processor.
+ */
+void *ipc_request_channel(struct ipc_client *cl)
+{
+ struct ipc_chan *chan;
+ unsigned long flags;
+ int ret = 0;
+
+ mutex_lock(&chpool_mutex);
+
+ list_for_each_entry(chan, &ipc_channels, node)
+ if (!chan->assigned
+ && !strcmp(cl->chan_name, chan->chan_name)) {
+ spin_lock_irqsave(&chan->lock, flags);
+ chan->msg_free = 0;
+ chan->msg_count = 0;
+ chan->active_token = 0;
+ chan->rxcb = cl->rxcb;
+ chan->txcb = cl->txcb;
+ chan->assigned = true;
+ chan->tx_block = cl->tx_block;
+ if (!cl->tx_tout)
+ chan->tx_tout = ~0;
+ else
+ chan->tx_tout = msecs_to_jiffies(cl->tx_tout);
+ if (chan->txdone_method == TXDONE_BY_POLL
+ && cl->knows_txdone)
+ chan->txdone_method |= TXDONE_BY_ACK;
+ spin_unlock_irqrestore(&chan->lock, flags);
+ ret = 1;
+ break;
+ }
+
+ mutex_unlock(&chpool_mutex);
+
+ if (!ret) {
+ pr_err("Unable to assign mailbox(%s)\n", cl->chan_name);
+ return NULL;
+ }
+
+ ret = chan->link_ops->startup(chan->link, cl->cntlr_data);
+ if (ret) {
+ pr_err("Unable to startup the link\n");
+ ipc_free_channel((void *)chan);
+ return NULL;
+ }
+
+ return (void *)chan;
+}
+EXPORT_SYMBOL(ipc_request_channel);
+
+/* Drop any messages queued and release the channel */
+void ipc_free_channel(void *ch)
+{
+ struct ipc_chan *chan = (struct ipc_chan *)ch;
+ unsigned long flags;
+
+ if (!chan || !chan->assigned)
+ return;
+
+ chan->link_ops->shutdown(chan->link);
+
+ /* The queued TX requests are simply aborted, no callbacks are made */
+ spin_lock_irqsave(&chan->lock, flags);
+ chan->assigned = false;
+ chan->active_token = 0;
+ if (chan->txdone_method == (TXDONE_BY_POLL | TXDONE_BY_ACK))
+ chan->txdone_method = TXDONE_BY_POLL;
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ del_timer_sync(chan->timer);
+
+ blocking_notifier_call_chain(&chan->avail, 0, NULL);
+}
+EXPORT_SYMBOL(ipc_free_channel);
+
+static struct ipc_chan *name_to_chan(const char *name)
+{
+ struct ipc_chan *chan;
+ int ret = 0;
+
+ mutex_lock(&chpool_mutex);
+ list_for_each_entry(chan, &ipc_channels, node)
+ if (!strcmp(name, chan->chan_name)) {
+ ret = 1;
+ break;
+ }
+ mutex_unlock(&chpool_mutex);
+
+ if (!ret)
+ return NULL;
+
+ return chan;
+}
+
+int ipc_notify_chan_register(const char *name, struct notifier_block *nb)
+{
+ struct ipc_chan *chan = name_to_chan(name);
+
+ if (chan && nb)
+ return blocking_notifier_chain_register(&chan->avail, nb);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(ipc_notify_chan_register);
+
+void ipc_notify_chan_unregister(const char *name, struct notifier_block *nb)
+{
+ struct ipc_chan *chan = name_to_chan(name);
+
+ if (chan && nb)
+ blocking_notifier_chain_unregister(&chan->avail, nb);
+}
+EXPORT_SYMBOL(ipc_notify_chan_unregister);
+
+/*
+ * Call for IPC controller drivers to register a controller, adding
+ * its channels/mailboxes to the global pool.
+ */
+int ipc_links_register(struct ipc_controller *ipc_con)
+{
+ struct tx_poll_timer *timer = NULL;
+ struct ipc_chan *channel;
+ int i, num_links, txdone;
+
+ /* Are you f***ing with us, sir? */
+ if (!ipc_con || !ipc_con->ops)
+ return -EINVAL;
+
+ for (i = 0; ipc_con->links[i]; i++)
+ ;
+ if (!i)
+ return -EINVAL;
+ num_links = i;
+
+ if (ipc_con->txdone_irq)
+ txdone = TXDONE_BY_IRQ;
+ else if (ipc_con->txdone_poll)
+ txdone = TXDONE_BY_POLL;
+ else /* It has to be at least ACK */
+ txdone = TXDONE_BY_ACK;
+
+ if (txdone == TXDONE_BY_POLL) {
+ timer = kzalloc(sizeof(struct tx_poll_timer), GFP_KERNEL);
+ if (!timer)
+ return -ENOMEM;
+ timer->period = ipc_con->txpoll_period;
+ timer->poll.function = &poll_txdone;
+ timer->poll.data = (unsigned long)timer;
+ init_timer(&timer->poll);
+ }
+
+ channel = kzalloc(sizeof(struct ipc_chan) * num_links, GFP_KERNEL);
+ if (!channel) {
+ kfree(timer);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < num_links; i++) {
+ channel[i].timer = timer;
+ channel[i].assigned = false;
+ channel[i].txdone_method = txdone;
+ channel[i].link_ops = ipc_con->ops;
+ channel[i].link = ipc_con->links[i];
+ channel[i].link->api_priv = &channel[i];
+ snprintf(channel[i].chan_name, 32, "%s:%s",
+ ipc_con->controller_name,
+ ipc_con->links[i]->link_name);
+ spin_lock_init(&channel[i].lock);
+ BLOCKING_INIT_NOTIFIER_HEAD(&channel[i].avail);
+ mutex_lock(&chpool_mutex);
+ list_add_tail(&channel[i].node, &ipc_channels);
+ mutex_unlock(&chpool_mutex);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ipc_links_register);
+
+void ipc_links_unregister(struct ipc_controller *ipc_con)
+{
+ struct ipc_chan *chan, *t, *first = NULL;
+ struct tx_poll_timer *timer = NULL;
+ char *name;
+ int i;
+
+ mutex_lock(&chpool_mutex);
+ for (i = 0; ipc_con->links[i]; i++) {
+ snprintf(name, 32, "%s:%s",
+ ipc_con->controller_name,
+ ipc_con->links[i]->link_name);
+ list_for_each_entry_safe(chan, t, &ipc_channels, node) {
+ if (!strcmp(name, chan->chan_name)) {
+ if (!first)
+ first = chan;
+ if (!timer)
+ timer = chan->timer;
+ list_del(&chan->node);
+ ipc_free_channel((void *)chan);
+ break;
+ }
+ }
+ }
+ mutex_unlock(&chpool_mutex);
+
+ kfree(first);
+ kfree(timer);
+}
+EXPORT_SYMBOL(ipc_links_unregister);
diff --git a/include/linux/mailbox.h b/include/linux/mailbox.h
new file mode 100644
index 0000000..232e2c4
--- /dev/null
+++ b/include/linux/mailbox.h
@@ -0,0 +1,17 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MAILBOX_H
+#define __MAILBOX_H
+
+enum xfer_result {
+ XFER_OK = 0,
+ XFER_ERR,
+};
+
+typedef unsigned request_token_t;
+
+#endif /* __MAILBOX_H */
diff --git a/include/linux/mailbox_client.h b/include/linux/mailbox_client.h
new file mode 100644
index 0000000..232fdc7
--- /dev/null
+++ b/include/linux/mailbox_client.h
@@ -0,0 +1,85 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MAILBOX_CLIENT_H
+#define __MAILBOX_CLIENT_H
+
+#include <linux/mailbox.h>
+
+/**
+ * struct ipc_client - User of a mailbox
+ * @chan_name: the "controller:channel" this client wants
+ * @rxcb: atomic callback to provide client the data received
+ * @txcb: atomic callback to tell client of data transmission
+ * @tx_block: if the ipc_send_message should block until data is transmitted
+ * @tx_tout: Max block period in ms before TX is assumed failure
+ * @knows_txdone: if the client could run the TX state machine. Usually if
+ * the client receives some ACK packet for transmission. Unused if the
+ * controller already has TX_Done/RTR IRQ.
+ * @cntlr_data: Optional controller specific parameters during channel request
+ */
+struct ipc_client {
+ char *chan_name;
+ void (*rxcb)(void *data);
+ void (*txcb)(request_token_t t, enum xfer_result r);
+ bool tx_block;
+ unsigned long tx_tout;
+ bool knows_txdone;
+ void *cntlr_data;
+};
+
+/**
+ * The Client specifies it requirements and capabilities while asking for
+ * a channel/mailbox by name. It can't be called from atomic context.
+ * The channel is exclusively allocated and can't be used by another
+ * client before the owner calls ipc_free_channel.
+ */
+void *ipc_request_channel(struct ipc_client *cl);
+
+/**
+ * For client to submit data to the controller destined for a remote
+ * processor. If the client had set 'tx_block', the call will return
+ * either when the remote receives the data or when 'tx_tout' millisecs
+ * run out.
+ * In non-blocking mode, the requests are buffered by the API and a
+ * non-zero token is returned for each queued request. If the queue
+ * was full the returned token will be 0. Upon failure or successful
+ * TX, the API calls 'txcb' from atomic context, from which the client
+ * could submit yet another request.
+ * In blocking mode, 'txcb' is not called, effectively making the
+ * queue length 1. The returned value is 0 if TX timed out, some
+ * non-zero value upon success.
+ */
+request_token_t ipc_send_message(void *channel, void *data);
+
+/**
+ * The way for a client to run the TX state machine. This works
+ * only if the client sets 'knows_txdone' and the IPC controller
+ * don't get an IRQ for TX_Done.
+ */
+void ipc_client_txdone(void *channel, enum xfer_result r);
+
+/**
+ * The client relinquishes control of a mailbox by this call,
+ * make it available to other clients.
+ * The ipc_request/free_channel are light weight calls, so the
+ * client should avoid holding it when it doesn't need to
+ * transfer data.
+ */
+void ipc_free_channel(void *ch);
+
+/**
+ * The client make ask the API to be notified when a particular channel
+ * becomes available to be acquired again.
+ */
+int ipc_notify_chan_register(const char *name, struct notifier_block *nb);
+
+/**
+ * The client is no more interested in acquiring the channel.
+ */
+void ipc_notify_chan_unregister(const char *name, struct notifier_block *nb);
+
+#endif /* __MAILBOX_CLIENT_H */
diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h
new file mode 100644
index 0000000..23b80e3
--- /dev/null
+++ b/include/linux/mailbox_controller.h
@@ -0,0 +1,102 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MAILBOX_CONTROLLER_H
+#define __MAILBOX_CONTROLLER_H
+
+#include <linux/mailbox.h>
+
+/**
+ * struct ipc_link - s/w representation of a communication link
+ * @link_name: Literal name assigned to the link. Physically
+ * identical channels may have the same name.
+ * @api_priv: hook for the API to map its private data on the link
+ * Controller driver must not touch it.
+ */
+struct ipc_link {
+ char link_name[16];
+ void *api_priv;
+};
+
+/**
+ * struct ipc_link - s/w representation of a communication link
+ * @send_data: The API asks the IPC controller driver, in atomic
+ * context try to transmit a message on the bus. Returns 0 if
+ * data is accepted for transmission, -EBUSY while rejecting
+ * if the remote hasn't yet read the last data sent. Actual
+ * transmission of data is reported by the controller via
+ * ipc_link_txdone (if it has some TX ACK irq). It must not
+ * block.
+ * @startup: Called when a client requests the link. The controller
+ * could ask clients for additional parameters of communication
+ * to be provided via client's cntlr_data. This call may block.
+ * After this call the Controller must forward any data received
+ * on the link by calling ipc_link_received_data (which won't block)
+ * @shutdown: Called when a client relinquishes control of a link.
+ * This call may block too. The controller must not forwared
+ * any received data anymore.
+ * @last_tx_done: If the controller sets 'txdone_poll', the API calls
+ * this to poll status of last TX. The controller must give priority
+ * to IRQ method over polling and never set both txdone_poll and
+ * txdone_irq. Only in polling mode 'send_data' is expected to
+ * return -EBUSY. Used only if txdone_poll:=true && txdone_irq:=false
+ */
+struct ipc_link_ops {
+ int (*send_data)(struct ipc_link *link, void *data);
+ int (*startup)(struct ipc_link *link, void *params);
+ void (*shutdown)(struct ipc_link *link);
+ bool (*last_tx_done)(struct ipc_link *link);
+};
+
+/**
+ * struct ipc_controller - Controller of a class of communication links
+ * @controller_name: Literal name of the controller.
+ * @ops: Operators that work on each communication link
+ * @links: Null terminated array of links.
+ * @txdone_irq: Indicates if the controller can report to API when the
+ * last transmitted data was read by the remote. Eg, if it has some
+ * TX ACK irq.
+ * @txdone_poll: If the controller can read but not report the TX done.
+ * Eg, is some register shows the TX status but no interrupt rises.
+ * Ignored if 'txdone_irq' is set.
+ * @txpoll_period: If 'txdone_poll' is in effect, the API polls for
+ * last TX's status after these many millisecs
+ */
+struct ipc_controller {
+ char controller_name[16];
+ struct ipc_link_ops *ops;
+ struct ipc_link **links;
+ bool txdone_irq;
+ bool txdone_poll;
+ unsigned txpoll_period;
+};
+
+/**
+ * The controller driver registers its communication links to the
+ * global pool managed by the API.
+ */
+int ipc_links_register(struct ipc_controller *ipc_con);
+
+/**
+ * After startup and before shutdown any data received on the link
+ * is pused to the API via atomic ipc_link_received_data() API.
+ * The controller should ACK the RX only after this call returns.
+ */
+void ipc_link_received_data(struct ipc_link *link, void *data);
+
+/**
+ * The controller the has IRQ for TX ACK calls this atomic API
+ * to tick the TX state machine. It works only if txdone_irq
+ * is set by the controller.
+ */
+void ipc_link_txdone(struct ipc_link *link, enum xfer_result r);
+
+/**
+ * Purge the links from the global pool maintained by the API.
+ */
+void ipc_links_unregister(struct ipc_controller *ipc_con);
+
+#endif /* __MAILBOX_CONTROLLER_H */
--
1.7.10.4
Convert the PL320 controller driver to work with the common
mailbox API. Also convert the only user of PL320, highbank-cpufreq.c
to work with thee API. Drop the obsoleted driver pl320-ipc.c
Signed-off-by: Jassi Brar <[email protected]>
---
drivers/cpufreq/highbank-cpufreq.c | 22 +++-
drivers/mailbox/Makefile | 2 +-
drivers/mailbox/pl320-ipc.c | 198 ---------------------------------
drivers/mailbox/pl320.c | 212 ++++++++++++++++++++++++++++++++++++
include/linux/pl320-ipc.h | 17 ---
5 files changed, 233 insertions(+), 218 deletions(-)
delete mode 100644 drivers/mailbox/pl320-ipc.c
create mode 100644 drivers/mailbox/pl320.c
delete mode 100644 include/linux/pl320-ipc.h
diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c
index 3118b87..5c057e0 100644
--- a/drivers/cpufreq/highbank-cpufreq.c
+++ b/drivers/cpufreq/highbank-cpufreq.c
@@ -19,7 +19,7 @@
#include <linux/cpu.h>
#include <linux/err.h>
#include <linux/of.h>
-#include <linux/pl320-ipc.h>
+#include <linux/mailbox_client.h>
#include <linux/platform_device.h>
#define HB_CPUFREQ_CHANGE_NOTE 0x80000001
@@ -29,8 +29,26 @@
static int hb_voltage_change(unsigned int freq)
{
u32 msg[HB_CPUFREQ_IPC_LEN] = {HB_CPUFREQ_CHANGE_NOTE, freq / 1000000};
+ struct ipc_client cl;
+ int ret = -ETIMEDOUT;
+ void *chan;
- return pl320_ipc_transmit(msg);
+ cl.rxcb = NULL;
+ cl.txcb = NULL;
+ cl.tx_block = true;
+ cl.tx_tout = 1000; /* 1 sec */
+ cl.cntlr_data = NULL;
+ cl.knows_txdone = false;
+ cl.chan_name = "pl320:A9_to_M3";
+
+ chan = ipc_request_channel(&cl);
+
+ if (ipc_send_message(chan, (void *)msg))
+ ret = msg[1]; /* PL320 updates buffer with FIFO after ACK */
+
+ ipc_free_channel(chan);
+
+ return ret;
}
static int hb_cpufreq_clk_notify(struct notifier_block *nb,
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index fefef7e..7b897f3 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -2,4 +2,4 @@
obj-$(CONFIG_MAILBOX) += mailbox.o
-obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o
+obj-$(CONFIG_PL320_MBOX) += pl320.o
diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c
deleted file mode 100644
index f3755e0..0000000
--- a/drivers/mailbox/pl320-ipc.c
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright 2012 Calxeda, Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-#include <linux/types.h>
-#include <linux/err.h>
-#include <linux/delay.h>
-#include <linux/export.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/completion.h>
-#include <linux/mutex.h>
-#include <linux/notifier.h>
-#include <linux/spinlock.h>
-#include <linux/device.h>
-#include <linux/amba/bus.h>
-
-#include <linux/pl320-ipc.h>
-
-#define IPCMxSOURCE(m) ((m) * 0x40)
-#define IPCMxDSET(m) (((m) * 0x40) + 0x004)
-#define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008)
-#define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C)
-#define IPCMxMODE(m) (((m) * 0x40) + 0x010)
-#define IPCMxMSET(m) (((m) * 0x40) + 0x014)
-#define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018)
-#define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C)
-#define IPCMxSEND(m) (((m) * 0x40) + 0x020)
-#define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024)
-
-#define IPCMMIS(irq) (((irq) * 8) + 0x800)
-#define IPCMRIS(irq) (((irq) * 8) + 0x804)
-
-#define MBOX_MASK(n) (1 << (n))
-#define IPC_TX_MBOX 1
-#define IPC_RX_MBOX 2
-
-#define CHAN_MASK(n) (1 << (n))
-#define A9_SOURCE 1
-#define M3_SOURCE 0
-
-static void __iomem *ipc_base;
-static int ipc_irq;
-static DEFINE_MUTEX(ipc_m1_lock);
-static DECLARE_COMPLETION(ipc_completion);
-static ATOMIC_NOTIFIER_HEAD(ipc_notifier);
-
-static inline void set_destination(int source, int mbox)
-{
- __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox));
- __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox));
-}
-
-static inline void clear_destination(int source, int mbox)
-{
- __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox));
- __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox));
-}
-
-static void __ipc_send(int mbox, u32 *data)
-{
- int i;
- for (i = 0; i < 7; i++)
- __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i));
- __raw_writel(0x1, ipc_base + IPCMxSEND(mbox));
-}
-
-static u32 __ipc_rcv(int mbox, u32 *data)
-{
- int i;
- for (i = 0; i < 7; i++)
- data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i));
- return data[1];
-}
-
-/* blocking implmentation from the A9 side, not usuable in interrupts! */
-int pl320_ipc_transmit(u32 *data)
-{
- int ret;
-
- mutex_lock(&ipc_m1_lock);
-
- init_completion(&ipc_completion);
- __ipc_send(IPC_TX_MBOX, data);
- ret = wait_for_completion_timeout(&ipc_completion,
- msecs_to_jiffies(1000));
- if (ret == 0) {
- ret = -ETIMEDOUT;
- goto out;
- }
-
- ret = __ipc_rcv(IPC_TX_MBOX, data);
-out:
- mutex_unlock(&ipc_m1_lock);
- return ret;
-}
-EXPORT_SYMBOL_GPL(pl320_ipc_transmit);
-
-static irqreturn_t ipc_handler(int irq, void *dev)
-{
- u32 irq_stat;
- u32 data[7];
-
- irq_stat = __raw_readl(ipc_base + IPCMMIS(1));
- if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) {
- __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX));
- complete(&ipc_completion);
- }
- if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) {
- __ipc_rcv(IPC_RX_MBOX, data);
- atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1);
- __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX));
- }
-
- return IRQ_HANDLED;
-}
-
-int pl320_ipc_register_notifier(struct notifier_block *nb)
-{
- return atomic_notifier_chain_register(&ipc_notifier, nb);
-}
-EXPORT_SYMBOL_GPL(pl320_ipc_register_notifier);
-
-int pl320_ipc_unregister_notifier(struct notifier_block *nb)
-{
- return atomic_notifier_chain_unregister(&ipc_notifier, nb);
-}
-EXPORT_SYMBOL_GPL(pl320_ipc_unregister_notifier);
-
-static int pl320_probe(struct amba_device *adev, const struct amba_id *id)
-{
- int ret;
-
- ipc_base = ioremap(adev->res.start, resource_size(&adev->res));
- if (ipc_base == NULL)
- return -ENOMEM;
-
- __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX));
-
- ipc_irq = adev->irq[0];
- ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL);
- if (ret < 0)
- goto err;
-
- /* Init slow mailbox */
- __raw_writel(CHAN_MASK(A9_SOURCE),
- ipc_base + IPCMxSOURCE(IPC_TX_MBOX));
- __raw_writel(CHAN_MASK(M3_SOURCE),
- ipc_base + IPCMxDSET(IPC_TX_MBOX));
- __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE),
- ipc_base + IPCMxMSET(IPC_TX_MBOX));
-
- /* Init receive mailbox */
- __raw_writel(CHAN_MASK(M3_SOURCE),
- ipc_base + IPCMxSOURCE(IPC_RX_MBOX));
- __raw_writel(CHAN_MASK(A9_SOURCE),
- ipc_base + IPCMxDSET(IPC_RX_MBOX));
- __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE),
- ipc_base + IPCMxMSET(IPC_RX_MBOX));
-
- return 0;
-err:
- iounmap(ipc_base);
- return ret;
-}
-
-static struct amba_id pl320_ids[] = {
- {
- .id = 0x00041320,
- .mask = 0x000fffff,
- },
- { 0, 0 },
-};
-
-static struct amba_driver pl320_driver = {
- .drv = {
- .name = "pl320",
- },
- .id_table = pl320_ids,
- .probe = pl320_probe,
-};
-
-static int __init ipc_init(void)
-{
- return amba_driver_register(&pl320_driver);
-}
-module_init(ipc_init);
diff --git a/drivers/mailbox/pl320.c b/drivers/mailbox/pl320.c
new file mode 100644
index 0000000..7ddae5c
--- /dev/null
+++ b/drivers/mailbox/pl320.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2012 Calxeda, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/amba/bus.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mailbox_controller.h>
+
+#define IPCMSOURCE(m) ((m) * 0x40)
+#define IPCMDSET(m) (((m) * 0x40) + 0x004)
+#define IPCMDCLEAR(m) (((m) * 0x40) + 0x008)
+#define IPCMDSTATUS(m) (((m) * 0x40) + 0x00C)
+#define IPCMMODE(m) (((m) * 0x40) + 0x010)
+#define IPCMMSET(m) (((m) * 0x40) + 0x014)
+#define IPCMMCLEAR(m) (((m) * 0x40) + 0x018)
+#define IPCMMSTATUS(m) (((m) * 0x40) + 0x01C)
+#define IPCMSEND(m) (((m) * 0x40) + 0x020)
+#define IPCMDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024)
+
+#define IPCMMIS(irq) (((irq) * 8) + 0x800)
+#define IPCMRIS(irq) (((irq) * 8) + 0x804)
+
+#define MBOX_MASK(n) (1 << (n))
+#define IPC_TX_MBOX 1
+
+#define CHAN_MASK(n) (1 << (n))
+#define A9_SOURCE 1
+#define M3_SOURCE 0
+
+struct pl320_con {
+ u32 *data;
+ int ipc_irq;
+ struct device *dev;
+ struct ipc_link link;
+ void __iomem *ipc_base;
+ struct ipc_controller ipc_con;
+};
+
+static inline struct pl320_con *to_pl320(struct ipc_link *l)
+{
+ if (!l)
+ return NULL;
+
+ return container_of(l, struct pl320_con, link);
+}
+
+static irqreturn_t ipc_handler(int irq, void *p)
+{
+ struct ipc_link *link = (struct ipc_link *)p;
+ struct pl320_con *pl320 = to_pl320(link);
+ void __iomem *ipc_base = pl320->ipc_base;
+ u32 irq_stat;
+
+ irq_stat = __raw_readl(ipc_base + IPCMMIS(1));
+ if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) {
+ u32 *data = pl320->data;
+ int i;
+
+ __raw_writel(0, ipc_base + IPCMSEND(IPC_TX_MBOX));
+
+ /*
+ * The PL320 driver specifies that the send buffer
+ * will be overwritten by same fifo upon TX ACK.
+ */
+ for (i = 0; i < 7; i++)
+ data[i] = __raw_readl(ipc_base
+ + IPCMDR(IPC_TX_MBOX, i));
+
+ ipc_link_txdone(link, XFER_OK);
+
+ pl320->data = NULL;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int pl320_send_data(struct ipc_link *link, void *msg)
+{
+ struct pl320_con *pl320 = to_pl320(link);
+ void __iomem *ipc_base = pl320->ipc_base;
+ u32 *data = (u32 *)msg;
+ int i;
+
+ pl320->data = data;
+
+ for (i = 0; i < 7; i++)
+ __raw_writel(data[i], ipc_base + IPCMDR(IPC_TX_MBOX, i));
+
+ __raw_writel(0x1, ipc_base + IPCMSEND(IPC_TX_MBOX));
+
+ return 0;
+}
+
+static int pl320_startup(struct ipc_link *link, void *ignored)
+{
+ struct pl320_con *pl320 = to_pl320(link);
+ void __iomem *ipc_base = pl320->ipc_base;
+ int err, ipc_irq = pl320->ipc_irq;
+
+ __raw_writel(0, ipc_base + IPCMSEND(IPC_TX_MBOX));
+
+ err = request_irq(ipc_irq, ipc_handler, 0, dev_name(pl320->dev), link);
+ if (err)
+ return err;
+
+ /* Init slow mailbox */
+ __raw_writel(CHAN_MASK(A9_SOURCE),
+ ipc_base + IPCMSOURCE(IPC_TX_MBOX));
+ __raw_writel(CHAN_MASK(M3_SOURCE),
+ ipc_base + IPCMDSET(IPC_TX_MBOX));
+ __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE),
+ ipc_base + IPCMMSET(IPC_TX_MBOX));
+
+ pl320->data = NULL;
+ return 0;
+}
+
+static void pl320_shutdown(struct ipc_link *link)
+{
+ struct pl320_con *pl320 = to_pl320(link);
+
+ pl320->data = NULL;
+ free_irq(pl320->ipc_irq, link);
+}
+
+static struct ipc_link_ops pl320_ops = {
+ .send_data = pl320_send_data,
+ .startup = pl320_startup,
+ .shutdown = pl320_shutdown,
+};
+
+static int pl320_probe(struct amba_device *adev, const struct amba_id *id)
+{
+ struct pl320_con *pl320;
+ struct ipc_link *l[2];
+ int ret;
+
+ pl320 = kzalloc(sizeof(struct pl320_con), GFP_KERNEL);
+ if (!pl320)
+ return -ENOMEM;
+
+ pl320->ipc_base = ioremap(adev->res.start, resource_size(&adev->res));
+ if (pl320->ipc_base == NULL) {
+ kfree(pl320);
+ return -ENOMEM;
+ }
+
+ pl320->dev = &adev->dev;
+ pl320->ipc_irq = adev->irq[0];
+ amba_set_drvdata(adev, pl320);
+
+ l[0] = &pl320->link;
+ l[1] = NULL;
+ pl320->ipc_con.links = l;
+ pl320->ipc_con.txdone_irq = true;
+ pl320->ipc_con.ops = &pl320_ops;
+ snprintf(pl320->link.link_name, 16, "A9_to_M3");
+ snprintf(pl320->ipc_con.controller_name, 16, "pl320");
+
+ ret = ipc_links_register(&pl320->ipc_con);
+ if (ret) {
+ iounmap(pl320->ipc_base);
+ kfree(pl320);
+ }
+
+ return ret;
+}
+
+static struct amba_id pl320_ids[] = {
+ {
+ .id = 0x00041320,
+ .mask = 0x000fffff,
+ },
+ { 0, 0 },
+};
+
+static struct amba_driver pl320_driver = {
+ .drv = {
+ .name = "pl320",
+ },
+ .id_table = pl320_ids,
+ .probe = pl320_probe,
+};
+
+static int __init ipc_init(void)
+{
+ return amba_driver_register(&pl320_driver);
+}
+module_init(ipc_init);
diff --git a/include/linux/pl320-ipc.h b/include/linux/pl320-ipc.h
deleted file mode 100644
index 5161f63..0000000
--- a/include/linux/pl320-ipc.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-int pl320_ipc_transmit(u32 *data);
-int pl320_ipc_register_notifier(struct notifier_block *nb);
-int pl320_ipc_unregister_notifier(struct notifier_block *nb);
--
1.7.10.4
A new driver conforming to the common API.
Signed-off-by: Jassi Brar <[email protected]>
---
arch/arm/mach-omap2/omap_hwmod_2420_data.c | 12 +
arch/arm/mach-omap2/omap_hwmod_2430_data.c | 11 +
arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 11 +
arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 13 +
drivers/mailbox/Kconfig | 9 +
drivers/mailbox/Makefile | 1 +
drivers/mailbox/omap2.c | 420 ++++++++++++++++++++++++++++
include/linux/platform_data/mailbox-omap.h | 64 +++++
8 files changed, 541 insertions(+)
create mode 100644 drivers/mailbox/omap2.c
create mode 100644 include/linux/platform_data/mailbox-omap.h
diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c
index 5137cc8..dbcb928 100644
--- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c
@@ -16,6 +16,7 @@
#include <linux/i2c-omap.h>
#include <linux/platform_data/spi-omap2-mcspi.h>
#include <linux/omap-dma.h>
+#include <linux/platform_data/mailbox-omap.h>
#include <plat/dmtimer.h>
#include "omap_hwmod.h"
@@ -166,6 +167,16 @@ static struct omap_hwmod omap2420_dma_system_hwmod = {
};
/* mailbox */
+static struct omap_mbox_dev_info omap2420_mailbox_info[] = {
+ { .name = "dsp", .tx_id = 0, .rx_id = 1, .irq_id = 0, .usr_id = 0 },
+ { .name = "iva", .tx_id = 2, .rx_id = 3, .irq_id = 1, .usr_id = 3 },
+};
+
+static struct omap_mbox_pdata omap2420_mailbox_attrs = {
+ .info_cnt = ARRAY_SIZE(omap2420_mailbox_info),
+ .info = omap2420_mailbox_info,
+};
+
static struct omap_hwmod_irq_info omap2420_mailbox_irqs[] = {
{ .name = "dsp", .irq = 26 + OMAP_INTC_START, },
{ .name = "iva", .irq = 34 + OMAP_INTC_START, },
@@ -186,6 +197,7 @@ static struct omap_hwmod omap2420_mailbox_hwmod = {
.idlest_idle_bit = OMAP24XX_ST_MAILBOXES_SHIFT,
},
},
+ .dev_attr = &omap2420_mailbox_attrs,
};
/*
diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c
index 4ce999e..df2f874 100644
--- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c
@@ -17,6 +17,7 @@
#include <linux/platform_data/asoc-ti-mcbsp.h>
#include <linux/platform_data/spi-omap2-mcspi.h>
#include <linux/omap-dma.h>
+#include <linux/platform_data/mailbox-omap.h>
#include <plat/dmtimer.h>
#include "omap_hwmod.h"
@@ -170,6 +171,15 @@ static struct omap_hwmod omap2430_dma_system_hwmod = {
};
/* mailbox */
+static struct omap_mbox_dev_info omap2430_mailbox_info[] = {
+ { .name = "dsp", .tx_id = 0, .rx_id = 1 },
+};
+
+static struct omap_mbox_pdata omap2430_mailbox_attrs = {
+ .info_cnt = ARRAY_SIZE(omap2430_mailbox_info),
+ .info = omap2430_mailbox_info,
+};
+
static struct omap_hwmod_irq_info omap2430_mailbox_irqs[] = {
{ .irq = 26 + OMAP_INTC_START, },
{ .irq = -1 },
@@ -189,6 +199,7 @@ static struct omap_hwmod omap2430_mailbox_hwmod = {
.idlest_idle_bit = OMAP24XX_ST_MAILBOXES_SHIFT,
},
},
+ .dev_attr = &omap2430_mailbox_attrs,
};
/* mcspi3 */
diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index 4083606..3833e96 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -25,6 +25,7 @@
#include <linux/platform_data/asoc-ti-mcbsp.h>
#include <linux/platform_data/spi-omap2-mcspi.h>
#include <linux/platform_data/iommu-omap.h>
+#include <linux/platform_data/mailbox-omap.h>
#include <plat/dmtimer.h>
#include "am35xx.h"
@@ -1501,6 +1502,15 @@ static struct omap_hwmod_class omap3xxx_mailbox_hwmod_class = {
.sysc = &omap3xxx_mailbox_sysc,
};
+static struct omap_mbox_dev_info omap3xxx_mailbox_info[] = {
+ { .name = "dsp", .tx_id = 0, .rx_id = 1 },
+};
+
+static struct omap_mbox_pdata omap3xxx_mailbox_attrs = {
+ .info_cnt = ARRAY_SIZE(omap3xxx_mailbox_info),
+ .info = omap3xxx_mailbox_info,
+};
+
static struct omap_hwmod_irq_info omap3xxx_mailbox_irqs[] = {
{ .irq = 26 + OMAP_INTC_START, },
{ .irq = -1 },
@@ -1520,6 +1530,7 @@ static struct omap_hwmod omap3xxx_mailbox_hwmod = {
.idlest_idle_bit = OMAP3430_ST_MAILBOXES_SHIFT,
},
},
+ .dev_attr = &omap3xxx_mailbox_attrs,
};
/*
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index eaba9dc..44d99b6 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -29,6 +29,7 @@
#include <linux/platform_data/spi-omap2-mcspi.h>
#include <linux/platform_data/asoc-ti-mcbsp.h>
#include <linux/platform_data/iommu-omap.h>
+#include <linux/platform_data/mailbox-omap.h>
#include <plat/dmtimer.h>
#include "omap_hwmod.h"
@@ -1861,6 +1862,17 @@ static struct omap_hwmod_class omap44xx_mailbox_hwmod_class = {
};
/* mailbox */
+static struct omap_mbox_dev_info omap44xx_mailbox_info[] = {
+ { .name = "mbox-ipu", .tx_id = 0, .rx_id = 1 },
+ { .name = "mbox-dsp", .tx_id = 3, .rx_id = 2 },
+};
+
+static struct omap_mbox_pdata omap44xx_mailbox_attrs = {
+ .intr_type = MBOX_INTR_CFG_TYPE2,
+ .info_cnt = ARRAY_SIZE(omap44xx_mailbox_info),
+ .info = omap44xx_mailbox_info,
+};
+
static struct omap_hwmod_irq_info omap44xx_mailbox_irqs[] = {
{ .irq = 26 + OMAP44XX_IRQ_GIC_START },
{ .irq = -1 }
@@ -1877,6 +1889,7 @@ static struct omap_hwmod omap44xx_mailbox_hwmod = {
.context_offs = OMAP4_RM_L4CFG_MAILBOX_CONTEXT_OFFSET,
},
},
+ .dev_attr = &omap44xx_mailbox_attrs,
};
/*
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 9545c9f..897b1c7 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -16,4 +16,13 @@ config PL320_MBOX
Management Engine, primarily for cpufreq. Say Y here if you want
to use the PL320 IPCM support.
+config OMAP2PLUS_MBOX
+ tristate "OMAP2+ Mailbox framework support"
+ depends on ARCH_OMAP2PLUS
+ help
+ Mailbox implementation for OMAP family chips with hardware for
+ interprocessor communication involving DSP, IVA1.0 and IVA2 in
+ OMAP2/3; or IPU, IVA HD and DSP in OMAP4. Say Y here if you want
+ to use OMAP2+ Mailbox framework support.
+
endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 7b897f3..b1c9b14 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -3,3 +3,4 @@
obj-$(CONFIG_MAILBOX) += mailbox.o
obj-$(CONFIG_PL320_MBOX) += pl320.o
+obj-$(CONFIG_OMAP2PLUS_MBOX) += omap2.o
diff --git a/drivers/mailbox/omap2.c b/drivers/mailbox/omap2.c
new file mode 100644
index 0000000..c22a82a
--- /dev/null
+++ b/drivers/mailbox/omap2.c
@@ -0,0 +1,420 @@
+/*
+ * Mailbox driver for OMAP4
+ *
+ * Copyright (C) 2013 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/mailbox-omap.h>
+
+#define MAILBOX_REVISION 0x000
+#define MAILBOX_MESSAGE(m) (0x040 + 4 * (m))
+#define MAILBOX_FIFOSTATUS(m) (0x080 + 4 * (m))
+#define MAILBOX_MSGSTATUS(m) (0x0c0 + 4 * (m))
+#define MAILBOX_IRQSTATUS(u) (0x100 + 8 * (u))
+#define MAILBOX_IRQENABLE(u) (0x104 + 8 * (u))
+
+#define OMAP4_MAILBOX_IRQSTATUS(u) (0x104 + 0x10 * (u))
+#define OMAP4_MAILBOX_IRQENABLE(u) (0x108 + 0x10 * (u))
+#define OMAP4_MAILBOX_IRQENABLE_CLR(u) (0x10c + 0x10 * (u))
+
+#define MAILBOX_IRQ_NEWMSG(m) (1 << (2 * (m)))
+#define MAILBOX_IRQ_NOTFULL(m) (1 << (2 * (m) + 1))
+
+#define RX_IRQ 0
+#define TX_IRQ 1
+
+/* We assume peak RX activity won't be more than 32 packets together */
+#define RXBUF_LEN 32
+
+/*
+ * Logical link is a pair of independent physical TX+RX mailboxes.
+ * IDEALLY, a client should acquire RX and TX separately as 2 links, so
+ * that (for example) when it can't take in anymore packets it could
+ * release the RX link and re-acquire only when it has appetite once again.
+ */
+struct omap2_mbox_link {
+ int irq;
+ bool active;
+ unsigned usr_id;
+ unsigned mb_rx, mb_tx;
+ u32 rxbuf[RXBUF_LEN];
+ struct ipc_link link;
+ struct omap2_mbox_con *omc;
+};
+
+struct omap2_mbox_con {
+ struct device *dev;
+ unsigned intr_type;
+ void __iomem *mbox_base;
+ struct omap2_mbox_link oml[4];
+ struct ipc_controller ipc_con;
+ struct mutex lock;
+};
+
+static inline struct omap2_mbox_link *to_oml(struct ipc_link *l)
+{
+ if (!l)
+ return NULL;
+
+ return container_of(l, struct omap2_mbox_link, link);
+}
+
+static inline void omap_mb_write(struct omap2_mbox_con *omc, u32 val, u32 off)
+{
+ __raw_writel(val, omc->mbox_base + off);
+}
+
+static inline u32 omap_mb_read(struct omap2_mbox_con *omc, u32 off)
+{
+ return __raw_readl(omc->mbox_base + off);
+}
+
+static void omc_enable_irq(struct omap2_mbox_link *oml, int tx)
+{
+ struct omap2_mbox_con *omc = oml->omc;
+ u32 val, off;
+
+ if (omc->intr_type)
+ off = OMAP4_MAILBOX_IRQENABLE(oml->usr_id);
+ else
+ off = MAILBOX_IRQENABLE(oml->usr_id);
+
+ val = omap_mb_read(omc, off);
+ if (tx)
+ val |= MAILBOX_IRQ_NOTFULL(oml->mb_tx);
+ else
+ val |= MAILBOX_IRQ_NEWMSG(oml->mb_rx);
+
+ omap_mb_write(omc, val, off);
+}
+
+static void omc_disable_irq(struct omap2_mbox_link *oml, int tx)
+{
+ struct omap2_mbox_con *omc = oml->omc;
+ u32 val, off;
+
+ if (tx)
+ val = MAILBOX_IRQ_NOTFULL(oml->mb_tx);
+ else
+ val = MAILBOX_IRQ_NEWMSG(oml->mb_rx);
+
+ if (omc->intr_type) {
+ off = OMAP4_MAILBOX_IRQENABLE_CLR(oml->usr_id);
+ } else {
+ off = MAILBOX_IRQENABLE(oml->usr_id);
+ val = omap_mb_read(omc, off) & ~val;
+ }
+
+ omap_mb_write(omc, val, off);
+}
+
+static void omc_ack_irq(struct omap2_mbox_link *oml, int tx)
+{
+ struct omap2_mbox_con *omc = oml->omc;
+ u32 val, off;
+
+ if (tx)
+ val = MAILBOX_IRQ_NOTFULL(oml->mb_tx);
+ else
+ val = MAILBOX_IRQ_NEWMSG(oml->mb_rx);
+
+ if (omc->intr_type)
+ off = OMAP4_MAILBOX_IRQSTATUS(oml->usr_id);
+ else
+ off = MAILBOX_IRQSTATUS(oml->usr_id);
+
+ omap_mb_write(omc, val, off);
+ /* Flush posted write for irq status to avoid spurious interrupts */
+ omap_mb_read(omc, off);
+}
+
+static int omc_is_irq(struct omap2_mbox_link *oml, int tx)
+{
+ struct omap2_mbox_con *omc = oml->omc;
+ u32 val, off;
+
+ if (tx)
+ val = MAILBOX_IRQ_NOTFULL(oml->mb_tx);
+ else
+ val = MAILBOX_IRQ_NEWMSG(oml->mb_rx);
+
+ if (omc->intr_type)
+ off = OMAP4_MAILBOX_IRQSTATUS(oml->usr_id);
+ else
+ off = MAILBOX_IRQSTATUS(oml->usr_id);
+
+ return omap_mb_read(omc, off) & val;
+}
+
+static void omc_tx_irq(struct omap2_mbox_link *oml)
+{
+ omc_disable_irq(oml, TX_IRQ);
+ ipc_link_txdone(&oml->link, XFER_OK);
+}
+
+static void omc_rx_irq(struct omap2_mbox_link *oml)
+{
+ struct omap2_mbox_con *omc = oml->omc;
+ int i, j, count = omap_mb_read(omc, MAILBOX_MSGSTATUS(oml->mb_rx));
+ struct omap2_mbox_rxdata pkt;
+
+ j = 0;
+ while (count && j < RXBUF_LEN) {
+
+ for (i = 0; i < count; i++)
+ oml->rxbuf[j++] = omap_mb_read(omc,
+ MAILBOX_MESSAGE(oml->mb_rx));
+
+ count = omap_mb_read(omc, MAILBOX_MSGSTATUS(oml->mb_rx));
+ }
+ pkt.len = j;
+ pkt.buf = oml->rxbuf;
+ ipc_link_received_data(&oml->link, (void *)&pkt)
+}
+
+static irqreturn_t ipc_handler(int irq, void *p)
+{
+ struct omap2_mbox_link *oml = p;
+ struct omap2_mbox_con *omc = oml->omc;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ struct omap2_mbox_link *l = &omc->oml[i];
+
+ if (!l->active)
+ continue;
+
+ if (omc_is_irq(l, TX_IRQ)) {
+ omc_tx_irq(l);
+ omc_ack_irq(l, TX_IRQ);
+ }
+
+ if (omc_is_irq(l, RX_IRQ)) {
+ omc_rx_irq(l);
+ omc_ack_irq(l, RX_IRQ);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mhu_send_data(struct ipc_link *link, void *data)
+{
+ struct omap2_mbox_link *oml = to_oml(link);
+ struct omap2_mbox_con *omc = oml->omc;
+
+ /* If FIFO is already full */
+ if (omap_mb_read(omc, MAILBOX_FIFOSTATUS(oml->mb_tx)) & 0x1) {
+ /* Enable IRQ and return busy */
+ omc_enable_irq(oml, TX_IRQ);
+ return -EBUSY;
+ }
+
+ omap_mb_write(omc, (u32)data, MAILBOX_MESSAGE(oml->mb_tx));
+ ipc_link_txdone(link, XFER_OK);
+
+ return 0;
+}
+
+static int mhu_startup(struct ipc_link *link, void *ignored)
+{
+ struct omap2_mbox_link *oml = to_oml(link);
+ struct omap2_mbox_con *omc = oml->omc;
+ int i, irq_taken, err = 0;
+
+ pm_runtime_enable(omc->dev);
+ pm_runtime_get_sync(omc->dev);
+
+ mutex_lock(&omc->lock);
+
+ irq_taken = 0;
+ for (i = 0; i < 4; i++) {
+ struct omap2_mbox_link *l = &omc->oml[i];
+ if (l->irq == oml->irq && l->active)
+ irq_taken = 1;
+ }
+ oml->active = true;
+ if (!irq_taken) {
+ err = request_irq(oml->irq, ipc_handler,
+ 0, omc->oml[i].link.link_name, oml);
+ if (err)
+ oml->active = false;
+ }
+
+ mutex_unlock(&omc->lock);
+
+ if (err) {
+ pm_runtime_put_sync(omc->dev);
+ pm_runtime_disable(omc->dev);
+ } else {
+ omc_disable_irq(oml, TX_IRQ);
+ /* Start accepting messages */
+ omc_enable_irq(oml, RX_IRQ);
+ }
+
+ return err;
+}
+
+static void mhu_shutdown(struct ipc_link *link)
+{
+ struct omap2_mbox_link *oml = to_oml(link);
+ struct omap2_mbox_con *omc = oml->omc;
+ int irq_taken;
+
+ /* Stop accepting messages */
+ omc_disable_irq(oml, RX_IRQ);
+ /* Flush the TX FIFO */
+ omc_disable_irq(oml, TX_IRQ);
+ omap_mb_read(omc, MAILBOX_MESSAGE(oml->mb_tx));
+ omap_mb_read(omc, MAILBOX_MESSAGE(oml->mb_tx));
+ omap_mb_read(omc, MAILBOX_MESSAGE(oml->mb_tx));
+ omap_mb_read(omc, MAILBOX_MESSAGE(oml->mb_tx));
+
+ mutex_lock(&omc->lock);
+
+ irq_taken = 0;
+ oml->active = false;
+ for (i = 0; i < 4; i++) {
+ struct omap2_mbox_link *l = &omc->oml[i];
+ if (l->irq == oml->irq && l->active)
+ irq_taken = 1;
+ }
+ if (!irq_taken)
+ free_irq(oml->irq, omc);
+
+ mutex_unlock(&omc->lock);
+
+ pm_runtime_put_sync(omc->dev);
+ pm_runtime_disable(omc->dev);
+}
+
+static struct ipc_link_ops omap2_mbox_ops = {
+ .startup = omap2_mbox_startup,
+ .send_data = omap2_mbox_send_data,
+ .shutdown = omap2_mbox_shutdown,
+};
+
+static int omap2_mbox_probe(struct platform_device *pdev)
+{
+ struct omap_mbox_pdata *pdata = pdev->dev.platform_data;
+ struct omap_mbox_dev_info *info;
+ struct omap2_mbox_con *omc;
+ void __iomem *base;
+ struct ipc_link *l[5];
+ struct resource *mem;
+ int i, ret;
+
+ if (!pdata || !pdata->info_cnt || !pdata->info) {
+ pr_err("%s: platform not supported\n", __func__);
+ return -ENODEV;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ pr_err("%s: No mem resource\n", __func__);
+ return -ENOMEM;
+ }
+
+ base = ioremap(mem->start, resource_size(mem));
+ if (!base) {
+ pr_err("%s: Ioremap failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ omc = kzalloc(sizeof(*omc), GFP_KERNEL);
+ if (!omc) {
+ pr_err("%s: Unable to alloc omap2_mbox_con\n", __func__);
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ info = pdata->info;
+ for (i = 0; i < pdata->info_cnt; i++, info++, priv++) {
+ omc->oml[i].irq = platform_get_irq(pdev, info->irq_id);
+ omc->oml[i].active = false;
+ omc->oml[i].usr_id = info->usr_id;
+ omc->oml[i].mb_rx = info->rx_id;
+ omc->oml[i].mb_tx = info->tx_id;
+ omc->oml[i].omc = omc;
+ snprintf(omc->oml[i].link.link_name, 16, "%s", info->name);
+ l[i] = &omc->oml[i].link;
+ omc->intr_type = pdata->intr_type;
+ }
+ l[i] = NULL;
+ omc->ipc_con.links = l;
+ omc->mbox_base = base;
+ omc->dev = &pdev->dev;
+ /*
+ * OMAP has "TX-FIFO Not Full" IRQ, not the "TX-Done" IRQ
+ * So we simply put data in FIFO and assume we are done.
+ */
+ omc->ipc_con.txdone_irq = true;
+ omc->ipc_con.ops = &omap2_mbox_ops;
+ snprintf(ipcu->ipc_con.controller_name, 16, "omap2");
+ mutex_init(&omc->lock);
+
+ ret = ipc_links_register(&omc->ipc_con);
+ if (ret) {
+ pr_err("%s: IPC register failed\n", __func__);
+ ret = -EINVAL;
+ goto err_reg;
+ }
+
+ platform_set_drvdata(pdev, omc);
+
+ return ret;
+
+err_alloc:
+ iounmap(base);
+err_reg:
+ kfree(omc);
+ return ret;
+}
+
+static int omap2_mbox_remove(struct platform_device *pdev)
+{
+ struct omap2_mbox_con *omc = platform_set_drvdata(pdev);
+
+ ipc_links_unregister(&omc->ipc_con);
+ iounmap(omc->mbox_base);
+ kfree(omc);
+
+ return 0;
+}
+
+static struct platform_driver omap2_mbox_driver = {
+ .probe = omap2_mbox_probe,
+ .remove = omap2_mbox_remove,
+ .driver = {
+ .name = "omap-mailbox",
+ },
+};
+
+static int __init omap2_mbox_init(void)
+{
+ return platform_driver_register(&omap2_mbox_driver);
+}
+module_init(omap2_mbox_init);
+
+static void __exit omap2_mbox_exit(void)
+{
+ platform_driver_unregister(&omap2_mbox_driver);
+}
+module_exit(omap2_mbox_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("omap mailbox: omap2/3/4 architecture specific functions");
+MODULE_AUTHOR("Jaswinder Singh <[email protected]>");
+MODULE_AUTHOR("Suman Anna <[email protected]>");
+MODULE_ALIAS("platform:omap2-mailbox");
diff --git a/include/linux/platform_data/mailbox-omap.h b/include/linux/platform_data/mailbox-omap.h
new file mode 100644
index 0000000..b35eaee
--- /dev/null
+++ b/include/linux/platform_data/mailbox-omap.h
@@ -0,0 +1,64 @@
+/*
+ * mailbox-omap.h
+ *
+ * Copyright (C) 2013 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _PLAT_MAILBOX_H
+#define _PLAT_MAILBOX_H
+
+/* Interrupt register configuration types */
+#define MBOX_INTR_CFG_TYPE1 (0)
+#define MBOX_INTR_CFG_TYPE2 (1)
+
+/**
+ * struct omap_mbox_dev_info - OMAP mailbox device attribute info
+ * @name: name of the mailbox device
+ * @tx_id: mailbox queue id used for transmitting messages
+ * @rx_id: mailbox queue id on which messages are received
+ * @irq_id: irq identifier number to use from the hwmod data
+ * @usr_id: mailbox user id for identifying the interrupt into
+ * the MPU interrupt controller.
+ */
+struct omap_mbox_dev_info {
+ const char *name;
+ u32 tx_id;
+ u32 rx_id;
+ u32 irq_id;
+ u32 usr_id;
+};
+
+/**
+ * struct omap_mbox_pdata - OMAP mailbox platform data
+ * @intr_type: type of interrupt configuration registers used
+ while programming mailbox queue interrupts
+ * @info_cnt: number of mailbox devices for the platform
+ * @info: array of mailbox device attributes
+ */
+struct omap_mbox_pdata {
+ u32 intr_type;
+ u32 info_cnt;
+ struct omap_mbox_dev_info *info;
+};
+
+/**
+ * struct omap2_mbox_rxdata - RX Packet handed over to a client by controller.
+ * For TX, clients simply provide a (void *)u32 packet at a time.
+ * @len: Number of words received
+ * @buf: Array of received words
+ */
+struct omap2_mbox_rxdata {
+ int len;
+ u32 *buf;
+};
+
+#endif /* _PLAT_MAILBOX_H */
--
1.7.10.4
Hi Jassi,
On 05/06/2013 02:22 AM, Jassi Brar wrote:
> Hello,
> I have made the implementation look more proper. Also made some changes
> suggested by Suman. Changes since V1:
>
> * Delete timer upon mailbox release
> * Filled in the stub ipc_links_unregister()
> * Check kzalloc return for errors.
> * Add the controller driver for OMAP2 class. I have taken the patch
> for mailbox's hwmod from Suman's. The clients are yet to be converted
> but this should be enough to convey the idea that the API could
> work for OMAP as well. If I had access to STE documentation, I might
> have done some implementation for U8500 as well.
The bug fixes in this series look good in general. I have provided my
design comments in the previous RFC thread [1] still, since the overall
changes here are not that much different from the RFC.
Thanks for prototyping the OMAP portions, it demonstrates the adaptation
to the new API, but I have to take a step approach in moving the current
OMAP mailbox code and making incremental changes on top of it. The
mailbox driver is the bottom-most layer in our IPC stacks, so changes at
this layer are very critical to the functional behavior.
regards
Suman
[1] http://www.mail-archive.com/[email protected]/msg429678.html
On 05/06/2013 02:24 AM, Jassi Brar wrote:
> Convert the PL320 controller driver to work with the common
> mailbox API. Also convert the only user of PL320, highbank-cpufreq.c
> to work with thee API. Drop the obsoleted driver pl320-ipc.c
>
> Signed-off-by: Jassi Brar <[email protected]>
> ---
> drivers/cpufreq/highbank-cpufreq.c | 22 +++-
> drivers/mailbox/Makefile | 2 +-
> drivers/mailbox/pl320-ipc.c | 198 ---------------------------------
> drivers/mailbox/pl320.c | 212 ++++++++++++++++++++++++++++++++++++
Can you resend this using git format-patch -M option.
Rob
> include/linux/pl320-ipc.h | 17 ---
> 5 files changed, 233 insertions(+), 218 deletions(-)
> delete mode 100644 drivers/mailbox/pl320-ipc.c
> create mode 100644 drivers/mailbox/pl320.c
> delete mode 100644 include/linux/pl320-ipc.h
>
> diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c
> index 3118b87..5c057e0 100644
> --- a/drivers/cpufreq/highbank-cpufreq.c
> +++ b/drivers/cpufreq/highbank-cpufreq.c
> @@ -19,7 +19,7 @@
> #include <linux/cpu.h>
> #include <linux/err.h>
> #include <linux/of.h>
> -#include <linux/pl320-ipc.h>
> +#include <linux/mailbox_client.h>
> #include <linux/platform_device.h>
>
> #define HB_CPUFREQ_CHANGE_NOTE 0x80000001
> @@ -29,8 +29,26 @@
> static int hb_voltage_change(unsigned int freq)
> {
> u32 msg[HB_CPUFREQ_IPC_LEN] = {HB_CPUFREQ_CHANGE_NOTE, freq / 1000000};
> + struct ipc_client cl;
> + int ret = -ETIMEDOUT;
> + void *chan;
>
> - return pl320_ipc_transmit(msg);
> + cl.rxcb = NULL;
> + cl.txcb = NULL;
> + cl.tx_block = true;
> + cl.tx_tout = 1000; /* 1 sec */
> + cl.cntlr_data = NULL;
> + cl.knows_txdone = false;
> + cl.chan_name = "pl320:A9_to_M3";
> +
> + chan = ipc_request_channel(&cl);
> +
> + if (ipc_send_message(chan, (void *)msg))
> + ret = msg[1]; /* PL320 updates buffer with FIFO after ACK */
> +
> + ipc_free_channel(chan);
> +
> + return ret;
> }
>
> static int hb_cpufreq_clk_notify(struct notifier_block *nb,
> diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
> index fefef7e..7b897f3 100644
> --- a/drivers/mailbox/Makefile
> +++ b/drivers/mailbox/Makefile
> @@ -2,4 +2,4 @@
>
> obj-$(CONFIG_MAILBOX) += mailbox.o
>
> -obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o
> +obj-$(CONFIG_PL320_MBOX) += pl320.o
> diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c
> deleted file mode 100644
> index f3755e0..0000000
> --- a/drivers/mailbox/pl320-ipc.c
> +++ /dev/null
> @@ -1,198 +0,0 @@
> -/*
> - * Copyright 2012 Calxeda, Inc.
> - *
> - * This program is free software; you can redistribute it and/or modify it
> - * under the terms and conditions of the GNU General Public License,
> - * version 2, as published by the Free Software Foundation.
> - *
> - * This program is distributed in the hope it will be useful, but WITHOUT
> - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> - * more details.
> - *
> - * You should have received a copy of the GNU General Public License along with
> - * this program. If not, see <http://www.gnu.org/licenses/>.
> - */
> -#include <linux/types.h>
> -#include <linux/err.h>
> -#include <linux/delay.h>
> -#include <linux/export.h>
> -#include <linux/io.h>
> -#include <linux/interrupt.h>
> -#include <linux/completion.h>
> -#include <linux/mutex.h>
> -#include <linux/notifier.h>
> -#include <linux/spinlock.h>
> -#include <linux/device.h>
> -#include <linux/amba/bus.h>
> -
> -#include <linux/pl320-ipc.h>
> -
> -#define IPCMxSOURCE(m) ((m) * 0x40)
> -#define IPCMxDSET(m) (((m) * 0x40) + 0x004)
> -#define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008)
> -#define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C)
> -#define IPCMxMODE(m) (((m) * 0x40) + 0x010)
> -#define IPCMxMSET(m) (((m) * 0x40) + 0x014)
> -#define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018)
> -#define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C)
> -#define IPCMxSEND(m) (((m) * 0x40) + 0x020)
> -#define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024)
> -
> -#define IPCMMIS(irq) (((irq) * 8) + 0x800)
> -#define IPCMRIS(irq) (((irq) * 8) + 0x804)
> -
> -#define MBOX_MASK(n) (1 << (n))
> -#define IPC_TX_MBOX 1
> -#define IPC_RX_MBOX 2
> -
> -#define CHAN_MASK(n) (1 << (n))
> -#define A9_SOURCE 1
> -#define M3_SOURCE 0
> -
> -static void __iomem *ipc_base;
> -static int ipc_irq;
> -static DEFINE_MUTEX(ipc_m1_lock);
> -static DECLARE_COMPLETION(ipc_completion);
> -static ATOMIC_NOTIFIER_HEAD(ipc_notifier);
> -
> -static inline void set_destination(int source, int mbox)
> -{
> - __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox));
> - __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox));
> -}
> -
> -static inline void clear_destination(int source, int mbox)
> -{
> - __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox));
> - __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox));
> -}
> -
> -static void __ipc_send(int mbox, u32 *data)
> -{
> - int i;
> - for (i = 0; i < 7; i++)
> - __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i));
> - __raw_writel(0x1, ipc_base + IPCMxSEND(mbox));
> -}
> -
> -static u32 __ipc_rcv(int mbox, u32 *data)
> -{
> - int i;
> - for (i = 0; i < 7; i++)
> - data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i));
> - return data[1];
> -}
> -
> -/* blocking implmentation from the A9 side, not usuable in interrupts! */
> -int pl320_ipc_transmit(u32 *data)
> -{
> - int ret;
> -
> - mutex_lock(&ipc_m1_lock);
> -
> - init_completion(&ipc_completion);
> - __ipc_send(IPC_TX_MBOX, data);
> - ret = wait_for_completion_timeout(&ipc_completion,
> - msecs_to_jiffies(1000));
> - if (ret == 0) {
> - ret = -ETIMEDOUT;
> - goto out;
> - }
> -
> - ret = __ipc_rcv(IPC_TX_MBOX, data);
> -out:
> - mutex_unlock(&ipc_m1_lock);
> - return ret;
> -}
> -EXPORT_SYMBOL_GPL(pl320_ipc_transmit);
> -
> -static irqreturn_t ipc_handler(int irq, void *dev)
> -{
> - u32 irq_stat;
> - u32 data[7];
> -
> - irq_stat = __raw_readl(ipc_base + IPCMMIS(1));
> - if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) {
> - __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX));
> - complete(&ipc_completion);
> - }
> - if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) {
> - __ipc_rcv(IPC_RX_MBOX, data);
> - atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1);
> - __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX));
> - }
> -
> - return IRQ_HANDLED;
> -}
> -
> -int pl320_ipc_register_notifier(struct notifier_block *nb)
> -{
> - return atomic_notifier_chain_register(&ipc_notifier, nb);
> -}
> -EXPORT_SYMBOL_GPL(pl320_ipc_register_notifier);
> -
> -int pl320_ipc_unregister_notifier(struct notifier_block *nb)
> -{
> - return atomic_notifier_chain_unregister(&ipc_notifier, nb);
> -}
> -EXPORT_SYMBOL_GPL(pl320_ipc_unregister_notifier);
> -
> -static int pl320_probe(struct amba_device *adev, const struct amba_id *id)
> -{
> - int ret;
> -
> - ipc_base = ioremap(adev->res.start, resource_size(&adev->res));
> - if (ipc_base == NULL)
> - return -ENOMEM;
> -
> - __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX));
> -
> - ipc_irq = adev->irq[0];
> - ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL);
> - if (ret < 0)
> - goto err;
> -
> - /* Init slow mailbox */
> - __raw_writel(CHAN_MASK(A9_SOURCE),
> - ipc_base + IPCMxSOURCE(IPC_TX_MBOX));
> - __raw_writel(CHAN_MASK(M3_SOURCE),
> - ipc_base + IPCMxDSET(IPC_TX_MBOX));
> - __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE),
> - ipc_base + IPCMxMSET(IPC_TX_MBOX));
> -
> - /* Init receive mailbox */
> - __raw_writel(CHAN_MASK(M3_SOURCE),
> - ipc_base + IPCMxSOURCE(IPC_RX_MBOX));
> - __raw_writel(CHAN_MASK(A9_SOURCE),
> - ipc_base + IPCMxDSET(IPC_RX_MBOX));
> - __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE),
> - ipc_base + IPCMxMSET(IPC_RX_MBOX));
> -
> - return 0;
> -err:
> - iounmap(ipc_base);
> - return ret;
> -}
> -
> -static struct amba_id pl320_ids[] = {
> - {
> - .id = 0x00041320,
> - .mask = 0x000fffff,
> - },
> - { 0, 0 },
> -};
> -
> -static struct amba_driver pl320_driver = {
> - .drv = {
> - .name = "pl320",
> - },
> - .id_table = pl320_ids,
> - .probe = pl320_probe,
> -};
> -
> -static int __init ipc_init(void)
> -{
> - return amba_driver_register(&pl320_driver);
> -}
> -module_init(ipc_init);
> diff --git a/drivers/mailbox/pl320.c b/drivers/mailbox/pl320.c
> new file mode 100644
> index 0000000..7ddae5c
> --- /dev/null
> +++ b/drivers/mailbox/pl320.c
> @@ -0,0 +1,212 @@
> +/*
> + * Copyright 2012 Calxeda, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +#include <linux/types.h>
> +#include <linux/err.h>
> +#include <linux/delay.h>
> +#include <linux/export.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/completion.h>
> +#include <linux/mutex.h>
> +#include <linux/notifier.h>
> +#include <linux/spinlock.h>
> +#include <linux/device.h>
> +#include <linux/amba/bus.h>
> +#include <linux/slab.h>
> +#include <linux/platform_device.h>
> +#include <linux/mailbox_controller.h>
> +
> +#define IPCMSOURCE(m) ((m) * 0x40)
> +#define IPCMDSET(m) (((m) * 0x40) + 0x004)
> +#define IPCMDCLEAR(m) (((m) * 0x40) + 0x008)
> +#define IPCMDSTATUS(m) (((m) * 0x40) + 0x00C)
> +#define IPCMMODE(m) (((m) * 0x40) + 0x010)
> +#define IPCMMSET(m) (((m) * 0x40) + 0x014)
> +#define IPCMMCLEAR(m) (((m) * 0x40) + 0x018)
> +#define IPCMMSTATUS(m) (((m) * 0x40) + 0x01C)
> +#define IPCMSEND(m) (((m) * 0x40) + 0x020)
> +#define IPCMDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024)
> +
> +#define IPCMMIS(irq) (((irq) * 8) + 0x800)
> +#define IPCMRIS(irq) (((irq) * 8) + 0x804)
> +
> +#define MBOX_MASK(n) (1 << (n))
> +#define IPC_TX_MBOX 1
> +
> +#define CHAN_MASK(n) (1 << (n))
> +#define A9_SOURCE 1
> +#define M3_SOURCE 0
> +
> +struct pl320_con {
> + u32 *data;
> + int ipc_irq;
> + struct device *dev;
> + struct ipc_link link;
> + void __iomem *ipc_base;
> + struct ipc_controller ipc_con;
> +};
> +
> +static inline struct pl320_con *to_pl320(struct ipc_link *l)
> +{
> + if (!l)
> + return NULL;
> +
> + return container_of(l, struct pl320_con, link);
> +}
> +
> +static irqreturn_t ipc_handler(int irq, void *p)
> +{
> + struct ipc_link *link = (struct ipc_link *)p;
> + struct pl320_con *pl320 = to_pl320(link);
> + void __iomem *ipc_base = pl320->ipc_base;
> + u32 irq_stat;
> +
> + irq_stat = __raw_readl(ipc_base + IPCMMIS(1));
> + if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) {
> + u32 *data = pl320->data;
> + int i;
> +
> + __raw_writel(0, ipc_base + IPCMSEND(IPC_TX_MBOX));
> +
> + /*
> + * The PL320 driver specifies that the send buffer
> + * will be overwritten by same fifo upon TX ACK.
> + */
> + for (i = 0; i < 7; i++)
> + data[i] = __raw_readl(ipc_base
> + + IPCMDR(IPC_TX_MBOX, i));
> +
> + ipc_link_txdone(link, XFER_OK);
> +
> + pl320->data = NULL;
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int pl320_send_data(struct ipc_link *link, void *msg)
> +{
> + struct pl320_con *pl320 = to_pl320(link);
> + void __iomem *ipc_base = pl320->ipc_base;
> + u32 *data = (u32 *)msg;
> + int i;
> +
> + pl320->data = data;
> +
> + for (i = 0; i < 7; i++)
> + __raw_writel(data[i], ipc_base + IPCMDR(IPC_TX_MBOX, i));
> +
> + __raw_writel(0x1, ipc_base + IPCMSEND(IPC_TX_MBOX));
> +
> + return 0;
> +}
> +
> +static int pl320_startup(struct ipc_link *link, void *ignored)
> +{
> + struct pl320_con *pl320 = to_pl320(link);
> + void __iomem *ipc_base = pl320->ipc_base;
> + int err, ipc_irq = pl320->ipc_irq;
> +
> + __raw_writel(0, ipc_base + IPCMSEND(IPC_TX_MBOX));
> +
> + err = request_irq(ipc_irq, ipc_handler, 0, dev_name(pl320->dev), link);
> + if (err)
> + return err;
> +
> + /* Init slow mailbox */
> + __raw_writel(CHAN_MASK(A9_SOURCE),
> + ipc_base + IPCMSOURCE(IPC_TX_MBOX));
> + __raw_writel(CHAN_MASK(M3_SOURCE),
> + ipc_base + IPCMDSET(IPC_TX_MBOX));
> + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE),
> + ipc_base + IPCMMSET(IPC_TX_MBOX));
> +
> + pl320->data = NULL;
> + return 0;
> +}
> +
> +static void pl320_shutdown(struct ipc_link *link)
> +{
> + struct pl320_con *pl320 = to_pl320(link);
> +
> + pl320->data = NULL;
> + free_irq(pl320->ipc_irq, link);
> +}
> +
> +static struct ipc_link_ops pl320_ops = {
> + .send_data = pl320_send_data,
> + .startup = pl320_startup,
> + .shutdown = pl320_shutdown,
> +};
> +
> +static int pl320_probe(struct amba_device *adev, const struct amba_id *id)
> +{
> + struct pl320_con *pl320;
> + struct ipc_link *l[2];
> + int ret;
> +
> + pl320 = kzalloc(sizeof(struct pl320_con), GFP_KERNEL);
> + if (!pl320)
> + return -ENOMEM;
> +
> + pl320->ipc_base = ioremap(adev->res.start, resource_size(&adev->res));
> + if (pl320->ipc_base == NULL) {
> + kfree(pl320);
> + return -ENOMEM;
> + }
> +
> + pl320->dev = &adev->dev;
> + pl320->ipc_irq = adev->irq[0];
> + amba_set_drvdata(adev, pl320);
> +
> + l[0] = &pl320->link;
> + l[1] = NULL;
> + pl320->ipc_con.links = l;
> + pl320->ipc_con.txdone_irq = true;
> + pl320->ipc_con.ops = &pl320_ops;
> + snprintf(pl320->link.link_name, 16, "A9_to_M3");
> + snprintf(pl320->ipc_con.controller_name, 16, "pl320");
> +
> + ret = ipc_links_register(&pl320->ipc_con);
> + if (ret) {
> + iounmap(pl320->ipc_base);
> + kfree(pl320);
> + }
> +
> + return ret;
> +}
> +
> +static struct amba_id pl320_ids[] = {
> + {
> + .id = 0x00041320,
> + .mask = 0x000fffff,
> + },
> + { 0, 0 },
> +};
> +
> +static struct amba_driver pl320_driver = {
> + .drv = {
> + .name = "pl320",
> + },
> + .id_table = pl320_ids,
> + .probe = pl320_probe,
> +};
> +
> +static int __init ipc_init(void)
> +{
> + return amba_driver_register(&pl320_driver);
> +}
> +module_init(ipc_init);
> diff --git a/include/linux/pl320-ipc.h b/include/linux/pl320-ipc.h
> deleted file mode 100644
> index 5161f63..0000000
> --- a/include/linux/pl320-ipc.h
> +++ /dev/null
> @@ -1,17 +0,0 @@
> -/*
> - * This program is free software; you can redistribute it and/or modify it
> - * under the terms and conditions of the GNU General Public License,
> - * version 2, as published by the Free Software Foundation.
> - *
> - * This program is distributed in the hope it will be useful, but WITHOUT
> - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> - * more details.
> - *
> - * You should have received a copy of the GNU General Public License along with
> - * this program. If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -int pl320_ipc_transmit(u32 *data);
> -int pl320_ipc_register_notifier(struct notifier_block *nb);
> -int pl320_ipc_unregister_notifier(struct notifier_block *nb);
>
On 7 May 2013 07:28, Rob Herring <[email protected]> wrote:
> On 05/06/2013 02:24 AM, Jassi Brar wrote:
>> Convert the PL320 controller driver to work with the common
>> mailbox API. Also convert the only user of PL320, highbank-cpufreq.c
>> to work with thee API. Drop the obsoleted driver pl320-ipc.c
>>
>> Signed-off-by: Jassi Brar <[email protected]>
>> ---
>> drivers/cpufreq/highbank-cpufreq.c | 22 +++-
>> drivers/mailbox/Makefile | 2 +-
>> drivers/mailbox/pl320-ipc.c | 198 ---------------------------------
>> drivers/mailbox/pl320.c | 212 ++++++++++++++++++++++++++++++++++++
>
> Can you resend this using git format-patch -M option.
>
IIRC I already did that. Anyways I just checked -M doesn't produce
anything different for the patch. Except for headers, defines and
module init rituals, everything else is from scratch.
-Jassi
Hi Jassi,
On 05/06/2013 02:24 AM, Jassi Brar wrote:
> +++ b/include/linux/mailbox_client.h
> @@ -0,0 +1,85 @@
> +/*
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef __MAILBOX_CLIENT_H
> +#define __MAILBOX_CLIENT_H
> +
> +#include <linux/mailbox.h>
> +
> +/**
> + * struct ipc_client - User of a mailbox
> + * @chan_name: the "controller:channel" this client wants
> + * @rxcb: atomic callback to provide client the data received
> + * @txcb: atomic callback to tell client of data transmission
> + * @tx_block: if the ipc_send_message should block until data is transmitted
> + * @tx_tout: Max block period in ms before TX is assumed failure
> + * @knows_txdone: if the client could run the TX state machine. Usually if
> + * the client receives some ACK packet for transmission. Unused if the
> + * controller already has TX_Done/RTR IRQ.
> + * @cntlr_data: Optional controller specific parameters during channel request
> + */
> +struct ipc_client {
> + char *chan_name;
> + void (*rxcb)(void *data);
> + void (*txcb)(request_token_t t, enum xfer_result r);
We have to introduce a callback data pointer, so that the calling
clients can retrieve a context object variable or some other useful
data within the callback functions, just like most normal callback
function declarations and registrations do.
> + bool tx_block;
> + unsigned long tx_tout;
> + bool knows_txdone;
> + void *cntlr_data;
> +};
regards
Suman
On 9 May 2013 22:01, Suman Anna <[email protected]> wrote:
> Hi Jassi,
>
> On 05/06/2013 02:24 AM, Jassi Brar wrote:
>> +++ b/include/linux/mailbox_client.h
>> @@ -0,0 +1,85 @@
>> +/*
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#ifndef __MAILBOX_CLIENT_H
>> +#define __MAILBOX_CLIENT_H
>> +
>> +#include <linux/mailbox.h>
>> +
>> +/**
>> + * struct ipc_client - User of a mailbox
>> + * @chan_name: the "controller:channel" this client wants
>> + * @rxcb: atomic callback to provide client the data received
>> + * @txcb: atomic callback to tell client of data transmission
>> + * @tx_block: if the ipc_send_message should block until data is transmitted
>> + * @tx_tout: Max block period in ms before TX is assumed failure
>> + * @knows_txdone: if the client could run the TX state machine. Usually if
>> + * the client receives some ACK packet for transmission. Unused if the
>> + * controller already has TX_Done/RTR IRQ.
>> + * @cntlr_data: Optional controller specific parameters during channel request
>> + */
>> +struct ipc_client {
>> + char *chan_name;
>> + void (*rxcb)(void *data);
>> + void (*txcb)(request_token_t t, enum xfer_result r);
>
> We have to introduce a callback data pointer, so that the calling
> clients can retrieve a context object variable or some other useful
> data within the callback functions, just like most normal callback
> function declarations and registrations do.
>
I meant the request_token_t for the purpose. That's how we do with DMAEngine.
Regards,
-Jassi
On 05/09/2013 11:41 AM, Jassi Brar wrote:
> On 9 May 2013 22:01, Suman Anna <[email protected]> wrote:
>> Hi Jassi,
>>
>> On 05/06/2013 02:24 AM, Jassi Brar wrote:
>>> +++ b/include/linux/mailbox_client.h
>>> @@ -0,0 +1,85 @@
>>> +/*
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License version 2 as
>>> + * published by the Free Software Foundation.
>>> + */
>>> +
>>> +#ifndef __MAILBOX_CLIENT_H
>>> +#define __MAILBOX_CLIENT_H
>>> +
>>> +#include <linux/mailbox.h>
>>> +
>>> +/**
>>> + * struct ipc_client - User of a mailbox
>>> + * @chan_name: the "controller:channel" this client wants
>>> + * @rxcb: atomic callback to provide client the data received
>>> + * @txcb: atomic callback to tell client of data transmission
>>> + * @tx_block: if the ipc_send_message should block until data is transmitted
>>> + * @tx_tout: Max block period in ms before TX is assumed failure
>>> + * @knows_txdone: if the client could run the TX state machine. Usually if
>>> + * the client receives some ACK packet for transmission. Unused if the
>>> + * controller already has TX_Done/RTR IRQ.
>>> + * @cntlr_data: Optional controller specific parameters during channel request
>>> + */
>>> +struct ipc_client {
>>> + char *chan_name;
>>> + void (*rxcb)(void *data);
>>> + void (*txcb)(request_token_t t, enum xfer_result r);
>>
>> We have to introduce a callback data pointer, so that the calling
>> clients can retrieve a context object variable or some other useful
>> data within the callback functions, just like most normal callback
>> function declarations and registrations do.
>>
> I meant the request_token_t for the purpose. That's how we do with DMAEngine.
I faced this issue on the rxcb while adopting the
omap_rproc_mbox_callback. omap_remoteproc is a common driver for all the
OMAP co-processors and there can be multiple instances, providing the
same set of features. Look at the code in
drivers/remoteproc/omap_remoteproc.c and you will get the idea. But in
general, the users registering callback functions would prefer to get
some context pointer back.
regards
Suman
On Thu, May 9, 2013 at 10:10 PM, Suman Anna <[email protected]> wrote:
> On 05/09/2013 11:41 AM, Jassi Brar wrote:
>> On 9 May 2013 22:01, Suman Anna <[email protected]> wrote:
>>> Hi Jassi,
>>>
>>> On 05/06/2013 02:24 AM, Jassi Brar wrote:
>>>> +++ b/include/linux/mailbox_client.h
>>>> @@ -0,0 +1,85 @@
>>>> +/*
>>>> + * This program is free software; you can redistribute it and/or modify
>>>> + * it under the terms of the GNU General Public License version 2 as
>>>> + * published by the Free Software Foundation.
>>>> + */
>>>> +
>>>> +#ifndef __MAILBOX_CLIENT_H
>>>> +#define __MAILBOX_CLIENT_H
>>>> +
>>>> +#include <linux/mailbox.h>
>>>> +
>>>> +/**
>>>> + * struct ipc_client - User of a mailbox
>>>> + * @chan_name: the "controller:channel" this client wants
>>>> + * @rxcb: atomic callback to provide client the data received
>>>> + * @txcb: atomic callback to tell client of data transmission
>>>> + * @tx_block: if the ipc_send_message should block until data is transmitted
>>>> + * @tx_tout: Max block period in ms before TX is assumed failure
>>>> + * @knows_txdone: if the client could run the TX state machine. Usually if
>>>> + * the client receives some ACK packet for transmission. Unused if the
>>>> + * controller already has TX_Done/RTR IRQ.
>>>> + * @cntlr_data: Optional controller specific parameters during channel request
>>>> + */
>>>> +struct ipc_client {
>>>> + char *chan_name;
>>>> + void (*rxcb)(void *data);
>>>> + void (*txcb)(request_token_t t, enum xfer_result r);
>>>
>>> We have to introduce a callback data pointer, so that the calling
>>> clients can retrieve a context object variable or some other useful
>>> data within the callback functions, just like most normal callback
>>> function declarations and registrations do.
>>>
>> I meant the request_token_t for the purpose. That's how we do with DMAEngine.
>
> I faced this issue on the rxcb while adopting the
> omap_rproc_mbox_callback. omap_remoteproc is a common driver for all the
> OMAP co-processors and there can be multiple instances, providing the
> same set of features. Look at the code in
> drivers/remoteproc/omap_remoteproc.c and you will get the idea. But in
> general, the users registering callback functions would prefer to get
> some context pointer back.
>
Of course they do. The request_token_t returned corresponds to the
request they submit via ipc_send_message.
Perhaps we should change the following
void ipc_link_txdone(struct ipc_link *link, enum xfer_result r)
to
void ipc_link_txdone(struct ipc_link *link, enum xfer_result r, void *data)
So that the API could pass that onto clients ?
regards,
jassi
Jassi,
On 05/09/2013 12:48 PM, Jassi Brar wrote:
> On Thu, May 9, 2013 at 10:10 PM, Suman Anna <[email protected]> wrote:
>> On 05/09/2013 11:41 AM, Jassi Brar wrote:
>>> On 9 May 2013 22:01, Suman Anna <[email protected]> wrote:
>>>> Hi Jassi,
>>>>
>>>> On 05/06/2013 02:24 AM, Jassi Brar wrote:
>>>>> +++ b/include/linux/mailbox_client.h
>>>>> @@ -0,0 +1,85 @@
>>>>> +/*
>>>>> + * This program is free software; you can redistribute it and/or modify
>>>>> + * it under the terms of the GNU General Public License version 2 as
>>>>> + * published by the Free Software Foundation.
>>>>> + */
>>>>> +
>>>>> +#ifndef __MAILBOX_CLIENT_H
>>>>> +#define __MAILBOX_CLIENT_H
>>>>> +
>>>>> +#include <linux/mailbox.h>
>>>>> +
>>>>> +/**
>>>>> + * struct ipc_client - User of a mailbox
>>>>> + * @chan_name: the "controller:channel" this client wants
>>>>> + * @rxcb: atomic callback to provide client the data received
>>>>> + * @txcb: atomic callback to tell client of data transmission
>>>>> + * @tx_block: if the ipc_send_message should block until data is transmitted
>>>>> + * @tx_tout: Max block period in ms before TX is assumed failure
>>>>> + * @knows_txdone: if the client could run the TX state machine. Usually if
>>>>> + * the client receives some ACK packet for transmission. Unused if the
>>>>> + * controller already has TX_Done/RTR IRQ.
>>>>> + * @cntlr_data: Optional controller specific parameters during channel request
>>>>> + */
>>>>> +struct ipc_client {
>>>>> + char *chan_name;
>>>>> + void (*rxcb)(void *data);
>>>>> + void (*txcb)(request_token_t t, enum xfer_result r);
>>>>
>>>> We have to introduce a callback data pointer, so that the calling
>>>> clients can retrieve a context object variable or some other useful
>>>> data within the callback functions, just like most normal callback
>>>> function declarations and registrations do.
>>>>
>>> I meant the request_token_t for the purpose. That's how we do with DMAEngine.
>>
>> I faced this issue on the rxcb while adopting the
>> omap_rproc_mbox_callback. omap_remoteproc is a common driver for all the
>> OMAP co-processors and there can be multiple instances, providing the
>> same set of features. Look at the code in
>> drivers/remoteproc/omap_remoteproc.c and you will get the idea. But in
>> general, the users registering callback functions would prefer to get
>> some context pointer back.
>>
> Of course they do. The request_token_t returned corresponds to the
> request they submit via ipc_send_message.
>
> Perhaps we should change the following
>
> void ipc_link_txdone(struct ipc_link *link, enum xfer_result r)
> to
> void ipc_link_txdone(struct ipc_link *link, enum xfer_result r, void *data)
>
> So that the API could pass that onto clients ?
That's if the controller needs to pass some data back to client. I am
fine with that as well, but I am talking mainly about providing a client
user data ptr back to it during callbacks.
struct ipc_client {
char *chan_name;
+ void *cl_data; /* store it to ipc_chan as well */
- void (*rxcb)(void *data);
- void (*txcb)(request_token_t t, enum xfer_result r);
+ void (*rxcb)(void *cl_data, void *data);
+ void (*txcb)(request_token_t t, enum xfer_result r, void *cl_data);
...
}
I am obviously interested in the rxcb. The controller implementations do
not see the cl_data.
regards
Suman
On 9 May 2013 23:35, Suman Anna <[email protected]> wrote:
>>
>> Perhaps we should change the following
>>
>> void ipc_link_txdone(struct ipc_link *link, enum xfer_result r)
>> to
>> void ipc_link_txdone(struct ipc_link *link, enum xfer_result r, void *data)
>>
>> So that the API could pass that onto clients ?
>
> That's if the controller needs to pass some data back to client. I am
> fine with that as well,
No, I misunderstood you wanted request_token_t to be replaced with the
pointer of request that was executed.
> but I am talking mainly about providing a client
> user data ptr back to it during callbacks.
>
> struct ipc_client {
> char *chan_name;
> + void *cl_data; /* store it to ipc_chan as well */
> - void (*rxcb)(void *data);
> - void (*txcb)(request_token_t t, enum xfer_result r);
> + void (*rxcb)(void *cl_data, void *data);
> + void (*txcb)(request_token_t t, enum xfer_result r, void *cl_data);
> ...
> }
>
> I am obviously interested in the rxcb. The controller implementations do
> not see the cl_data.
>
OK I see what you mean. However the API storing and passing back
ad-hoc data to clients doesn't seem very neat.
Such purposes are usually served by :
- void (*rxcb)(void *data);
+ void (*rxcb)(struct ipc_client *cl, void *data); /* client for
which data was received */
- void (*txcb)(request_token_t t, enum xfer_result r);
+ void (*txcb)(struct ipc_client *cl, request_token_t t, enum
xfer_result r); /* client whose data was sent */
You could then get relevant omap_rproc using container_of() on 'cl',
in rxcb() and txcb().
Apart from this, in txcb, perhaps we should drop request_token_t in
favor of the request's pointer (void *data) that was last executed.
That should make things easier for clients.
regards,
-jassi
Jassi,
>>>
>>> Perhaps we should change the following
>>>
>>> void ipc_link_txdone(struct ipc_link *link, enum xfer_result r)
>>> to
>>> void ipc_link_txdone(struct ipc_link *link, enum xfer_result r, void *data)
>>>
>>> So that the API could pass that onto clients ?
>>
>> That's if the controller needs to pass some data back to client. I am
>> fine with that as well,
> No, I misunderstood you wanted request_token_t to be replaced with the
> pointer of request that was executed.
>
>> but I am talking mainly about providing a client
>> user data ptr back to it during callbacks.
>>
>> struct ipc_client {
>> char *chan_name;
>> + void *cl_data; /* store it to ipc_chan as well */
>> - void (*rxcb)(void *data);
>> - void (*txcb)(request_token_t t, enum xfer_result r);
>> + void (*rxcb)(void *cl_data, void *data);
>> + void (*txcb)(request_token_t t, enum xfer_result r, void *cl_data);
>> ...
>> }
>>
>> I am obviously interested in the rxcb. The controller implementations do
>> not see the cl_data.
>>
> OK I see what you mean. However the API storing and passing back
> ad-hoc data to clients doesn't seem very neat.
>
> Such purposes are usually served by :
>
> - void (*rxcb)(void *data);
> + void (*rxcb)(struct ipc_client *cl, void *data); /* client for
> which data was received */
>
> - void (*txcb)(request_token_t t, enum xfer_result r);
> + void (*txcb)(struct ipc_client *cl, request_token_t t, enum
> xfer_result r); /* client whose data was sent */
>
> You could then get relevant omap_rproc using container_of() on 'cl',
> in rxcb() and txcb().
The reason that I didn't suggest that way is because we do not use
ipc_client for any runtime API, and we would have to store the returned
handle anyway. I see ipc_client simply as a ipc_channel_request_info
structure, a one-time usage perspective. I made the suggestion as it
seemed in line if you had a xxx_register_callback API wherein you would
use a void *context if you want something back.
>
> Apart from this, in txcb, perhaps we should drop request_token_t in
> favor of the request's pointer (void *data) that was last executed.
> That should make things easier for clients.
Yes, that would be nice too.
regards
Suman