diff mbox

[PATCHv2,2/4] mailbox: Introduce a new common API

Message ID 1367825046-6229-1-git-send-email-jaswinder.singh@linaro.org (mailing list archive)
State New, archived
Headers show

Commit Message

Jassi Brar May 6, 2013, 7:24 a.m. UTC
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

Comments

Suman Anna May 9, 2013, 4:31 p.m. UTC | #1
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
Suman Anna May 9, 2013, 4:40 p.m. UTC | #2
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
Jassi Brar May 9, 2013, 4:41 p.m. UTC | #3
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
Jassi Brar May 9, 2013, 5:48 p.m. UTC | #4
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
Suman Anna May 9, 2013, 6:05 p.m. UTC | #5
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
Jassi Brar May 9, 2013, 6:49 p.m. UTC | #6
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
Suman Anna May 9, 2013, 11:43 p.m. UTC | #7
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
Loic PALLARDY May 13, 2013, 7:09 p.m. UTC | #8
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
>
diff mbox

Patch

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 */