* [PATCHv2 1/4] mailbox: rename pl320-ipc specific mailbox.h
2013-05-06 7:22 [PATCHv2 0/4] mailbox: Common API Jassi Brar
@ 2013-05-06 7:23 ` Jassi Brar
2013-05-06 7:24 ` [PATCHv2 2/4] mailbox: Introduce a new common API Jassi Brar
` (3 subsequent siblings)
4 siblings, 0 replies; 16+ messages in thread
From: Jassi Brar @ 2013-05-06 7:23 UTC (permalink / raw)
To: linux-arm-kernel
From: Suman Anna <s-anna@ti.com>
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 <mark.langsdorf@calxeda.com>
Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Suman Anna <s-anna@ti.com>
---
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
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCHv2 2/4] mailbox: Introduce a new common API
2013-05-06 7:22 [PATCHv2 0/4] mailbox: Common API Jassi Brar
2013-05-06 7:23 ` [PATCHv2 1/4] mailbox: rename pl320-ipc specific mailbox.h Jassi Brar
@ 2013-05-06 7:24 ` Jassi Brar
2013-05-09 16:31 ` Suman Anna
2013-05-13 19:09 ` Loic PALLARDY
2013-05-06 7:24 ` [PATCHv2 3/4] mailbox: pl320: Introduce common API driver Jassi Brar
` (2 subsequent siblings)
4 siblings, 2 replies; 16+ messages in thread
From: Jassi Brar @ 2013-05-06 7:24 UTC (permalink / raw)
To: linux-arm-kernel
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 <jaswinder.singh@linaro.org>
---
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
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCHv2 2/4] mailbox: Introduce a new common API
2013-05-06 7:24 ` [PATCHv2 2/4] mailbox: Introduce a new common API Jassi Brar
@ 2013-05-09 16:31 ` Suman Anna
2013-05-09 16:41 ` Jassi Brar
2013-05-13 19:09 ` Loic PALLARDY
1 sibling, 1 reply; 16+ messages in thread
From: Suman Anna @ 2013-05-09 16:31 UTC (permalink / raw)
To: linux-arm-kernel
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
^ permalink raw reply [flat|nested] 16+ messages in thread* [PATCHv2 2/4] mailbox: Introduce a new common API
2013-05-09 16:31 ` Suman Anna
@ 2013-05-09 16:41 ` Jassi Brar
2013-05-09 16:40 ` Suman Anna
0 siblings, 1 reply; 16+ messages in thread
From: Jassi Brar @ 2013-05-09 16:41 UTC (permalink / raw)
To: linux-arm-kernel
On 9 May 2013 22:01, Suman Anna <s-anna@ti.com> 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
^ permalink raw reply [flat|nested] 16+ messages in thread* [PATCHv2 2/4] mailbox: Introduce a new common API
2013-05-09 16:41 ` Jassi Brar
@ 2013-05-09 16:40 ` Suman Anna
2013-05-09 17:48 ` Jassi Brar
0 siblings, 1 reply; 16+ messages in thread
From: Suman Anna @ 2013-05-09 16:40 UTC (permalink / raw)
To: linux-arm-kernel
On 05/09/2013 11:41 AM, Jassi Brar wrote:
> On 9 May 2013 22:01, Suman Anna <s-anna@ti.com> 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
^ permalink raw reply [flat|nested] 16+ messages in thread* [PATCHv2 2/4] mailbox: Introduce a new common API
2013-05-09 16:40 ` Suman Anna
@ 2013-05-09 17:48 ` Jassi Brar
2013-05-09 18:05 ` Suman Anna
0 siblings, 1 reply; 16+ messages in thread
From: Jassi Brar @ 2013-05-09 17:48 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, May 9, 2013 at 10:10 PM, Suman Anna <s-anna@ti.com> wrote:
> On 05/09/2013 11:41 AM, Jassi Brar wrote:
>> On 9 May 2013 22:01, Suman Anna <s-anna@ti.com> 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
^ permalink raw reply [flat|nested] 16+ messages in thread* [PATCHv2 2/4] mailbox: Introduce a new common API
2013-05-09 17:48 ` Jassi Brar
@ 2013-05-09 18:05 ` Suman Anna
2013-05-09 18:49 ` Jassi Brar
0 siblings, 1 reply; 16+ messages in thread
From: Suman Anna @ 2013-05-09 18:05 UTC (permalink / raw)
To: linux-arm-kernel
Jassi,
On 05/09/2013 12:48 PM, Jassi Brar wrote:
> On Thu, May 9, 2013 at 10:10 PM, Suman Anna <s-anna@ti.com> wrote:
>> On 05/09/2013 11:41 AM, Jassi Brar wrote:
>>> On 9 May 2013 22:01, Suman Anna <s-anna@ti.com> 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
^ permalink raw reply [flat|nested] 16+ messages in thread* [PATCHv2 2/4] mailbox: Introduce a new common API
2013-05-09 18:05 ` Suman Anna
@ 2013-05-09 18:49 ` Jassi Brar
2013-05-09 23:43 ` Suman Anna
0 siblings, 1 reply; 16+ messages in thread
From: Jassi Brar @ 2013-05-09 18:49 UTC (permalink / raw)
To: linux-arm-kernel
On 9 May 2013 23:35, Suman Anna <s-anna@ti.com> 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
^ permalink raw reply [flat|nested] 16+ messages in thread* [PATCHv2 2/4] mailbox: Introduce a new common API
2013-05-09 18:49 ` Jassi Brar
@ 2013-05-09 23:43 ` Suman Anna
0 siblings, 0 replies; 16+ messages in thread
From: Suman Anna @ 2013-05-09 23:43 UTC (permalink / raw)
To: linux-arm-kernel
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
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCHv2 2/4] mailbox: Introduce a new common API
2013-05-06 7:24 ` [PATCHv2 2/4] mailbox: Introduce a new common API Jassi Brar
2013-05-09 16:31 ` Suman Anna
@ 2013-05-13 19:09 ` Loic PALLARDY
1 sibling, 0 replies; 16+ messages in thread
From: Loic PALLARDY @ 2013-05-13 19:09 UTC (permalink / raw)
To: linux-arm-kernel
Hi Jassi,
Back at work :)
Where can I find last version of framework which integrates all
modifications discussed during the 3 last weeks?
I would like to review and work on last code version.
Regards,
Loic
On 05/06/2013 09:24 AM, Jassi Brar wrote:
> 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<jaswinder.singh@linaro.org>
> ---
> 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
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCHv2 3/4] mailbox: pl320: Introduce common API driver
2013-05-06 7:22 [PATCHv2 0/4] mailbox: Common API Jassi Brar
2013-05-06 7:23 ` [PATCHv2 1/4] mailbox: rename pl320-ipc specific mailbox.h Jassi Brar
2013-05-06 7:24 ` [PATCHv2 2/4] mailbox: Introduce a new common API Jassi Brar
@ 2013-05-06 7:24 ` Jassi Brar
2013-05-07 1:58 ` Rob Herring
2013-05-06 7:24 ` [PATCHv2 4/4] mailbox: omap2: " Jassi Brar
2013-05-07 0:02 ` [PATCHv2 0/4] mailbox: Common API Suman Anna
4 siblings, 1 reply; 16+ messages in thread
From: Jassi Brar @ 2013-05-06 7:24 UTC (permalink / raw)
To: linux-arm-kernel
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 <jaswinder.singh@linaro.org>
---
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
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCHv2 3/4] mailbox: pl320: Introduce common API driver
2013-05-06 7:24 ` [PATCHv2 3/4] mailbox: pl320: Introduce common API driver Jassi Brar
@ 2013-05-07 1:58 ` Rob Herring
2013-05-07 16:56 ` Jassi Brar
0 siblings, 1 reply; 16+ messages in thread
From: Rob Herring @ 2013-05-07 1:58 UTC (permalink / raw)
To: linux-arm-kernel
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 <jaswinder.singh@linaro.org>
> ---
> 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);
>
^ permalink raw reply [flat|nested] 16+ messages in thread* [PATCHv2 3/4] mailbox: pl320: Introduce common API driver
2013-05-07 1:58 ` Rob Herring
@ 2013-05-07 16:56 ` Jassi Brar
0 siblings, 0 replies; 16+ messages in thread
From: Jassi Brar @ 2013-05-07 16:56 UTC (permalink / raw)
To: linux-arm-kernel
On 7 May 2013 07:28, Rob Herring <robherring2@gmail.com> 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 <jaswinder.singh@linaro.org>
>> ---
>> 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
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCHv2 4/4] mailbox: omap2: Introduce common API driver
2013-05-06 7:22 [PATCHv2 0/4] mailbox: Common API Jassi Brar
` (2 preceding siblings ...)
2013-05-06 7:24 ` [PATCHv2 3/4] mailbox: pl320: Introduce common API driver Jassi Brar
@ 2013-05-06 7:24 ` Jassi Brar
2013-05-07 0:02 ` [PATCHv2 0/4] mailbox: Common API Suman Anna
4 siblings, 0 replies; 16+ messages in thread
From: Jassi Brar @ 2013-05-06 7:24 UTC (permalink / raw)
To: linux-arm-kernel
A new driver conforming to the common API.
Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
---
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 <jaswinder.singh@linaro.org>");
+MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
+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
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCHv2 0/4] mailbox: Common API
2013-05-06 7:22 [PATCHv2 0/4] mailbox: Common API Jassi Brar
` (3 preceding siblings ...)
2013-05-06 7:24 ` [PATCHv2 4/4] mailbox: omap2: " Jassi Brar
@ 2013-05-07 0:02 ` Suman Anna
4 siblings, 0 replies; 16+ messages in thread
From: Suman Anna @ 2013-05-07 0:02 UTC (permalink / raw)
To: linux-arm-kernel
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/linux-kernel at vger.kernel.org/msg429678.html
^ permalink raw reply [flat|nested] 16+ messages in thread