Re: [PATCH 2/2] mailbox: Documentation: add the latest documentation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Jassi,

Wondering if you have any comments. Thanks.

On Sat, Oct 19, 2024 at 12:55 PM anish kumar <yesanishhere@xxxxxxxxx> wrote:
>
> 1. added client documentation
> 2. added controller documentation.
> 3. added framework documentation
>
> Signed-off-by: anish kumar <yesanishhere@xxxxxxxxx>
> ---
>  Documentation/driver-api/mailbox/client.rst   | 192 ++++++++++++++++++
>  Documentation/driver-api/mailbox/core.rst     | 182 +++++++++++++++++
>  Documentation/driver-api/mailbox/index.rst    |  45 ++++
>  .../writing_mailbox_controller_drivers.rst    | 179 ++++++++++++++++
>  4 files changed, 598 insertions(+)
>  create mode 100644 Documentation/driver-api/mailbox/client.rst
>  create mode 100644 Documentation/driver-api/mailbox/core.rst
>  create mode 100644 Documentation/driver-api/mailbox/index.rst
>  create mode 100644 Documentation/driver-api/mailbox/writing_mailbox_controller_drivers.rst
>
> diff --git a/Documentation/driver-api/mailbox/client.rst b/Documentation/driver-api/mailbox/client.rst
> new file mode 100644
> index 000000000000..9088f8373423
> --- /dev/null
> +++ b/Documentation/driver-api/mailbox/client.rst
> @@ -0,0 +1,192 @@
> +Mailbox Client Documentation
> +============================
> +
> +Overview
> +--------
> +The mailbox client driver is responsible for sending and receiving messages
> +to and from a remote processor. It uses mailbox APIs provided by the
> +mailbox framework.
> +
> +Mailbox Structure
> +-----------------
> +The mailbox structure is defined as follows:
> +
> +.. code-block:: c
> +
> +   struct mbox_client {
> +       //device associated with the mailbox
> +       struct device *dev;
> +       // callback for transmission completion
> +       void (*tx_done)(struct mbox_client *client);
> +       // callback to prepare for sending a message
> +       void (*tx_prepare)(struct mbox_client *client);
> +       // callback for received messages
> +       void (*rx_callback)(struct mbox_client *client, void *data);
> +       // flag to indicate if transmission should block
> +       bool tx_block;
> +       // indicates if the client knows when transmission is done
> +       bool knows_txdone;
> +   };
> +
> +Key Functions
> +-------------
> +1. Requesting a Mailbox Channel
> +   - **Function**: `mbox_request_channel(struct mbox_client *client,
> +     unsigned int channel)`
> +   - **Description**: Requests a mailbox channel for sending messages.
> +   - **Parameters**:
> +     - `client`: Pointer to the mailbox client structure.
> +     - `channel`: The specific mailbox channel to request.
> +   - **Returns**: A pointer to the mailbox channel on success, or an error
> +     code on failure.
> +
> +2. Sending a Message
> +   - **Function**: `mbox_send_message(struct mbox_chan *chan, void *msg)`
> +   - **Description**: Sends a message through the mailbox channel.
> +   - **Parameters**:
> +     - `chan`: The mailbox channel used for communication.
> +     - `msg`: Pointer to the message to be sent (usually NULL for dummy
> +       messages).
> +   - **Returns**: 0 on success, or a negative error code on failure.
> +
> +3. Transmitting Completion
> +   - **Function**: `mbox_client_txdone(struct mbox_chan *chan, unsigned int
> +     msg_id)`
> +   - **Description**: Notifies the mailbox framework that message
> +     transmission is complete.
> +   - **Parameters**:
> +     - `chan`: The mailbox channel associated with the message.
> +     - `msg_id`: The identifier of the message that was transmitted.
> +
> +Usage Example
> +-------------
> +In a typical mailbox client driver, the following steps are typically
> +performed:
> +
> +1. Initialize the Mailbox Client:
> +
> +   .. code-block:: c
> +
> +      struct mbox_client my_mbox_client = {
> +          .dev = &my_device,
> +          .tx_done = my_tx_done_callback,
> +          .rx_callback = my_rx_callback,
> +          .tx_block = false,
> +          .knows_txdone = true,
> +      };
> +
> +2. Request a Mailbox Channel:
> +
> +   .. code-block:: c
> +
> +      mbox_chan = mbox_request_channel(&my_mbox_client, 0);
> +      if (IS_ERR(mbox_chan)) {
> +          // Handle error
> +      }
> +
> +3. Send a Message:
> +
> +   .. code-block:: c
> +
> +      int ret = mbox_send_message(mbox_chan, NULL); // Sending a dummy message
> +      if (ret < 0) {
> +          // Handle error
> +      }
> +
> +4. Complete Transmission:
> +
> +   .. code-block:: c
> +
> +      mbox_client_txdone(mbox_chan, 0);
> +
> +Interrupt Handling
> +------------------
> +The mailbox interface can trigger interrupts upon message receipt. Handlers
> +should be implemented in the `rx_callback` function defined in the mailbox
> +client structure to process incoming messages.
> +
> +Example Mailbox Client Driver
> +-----------------------------
> +.. code-block:: c
> +
> +   struct demo_client {
> +       struct mbox_client cl;
> +       struct mbox_chan *mbox;
> +       struct completion c;
> +       bool async;
> +       /* ... */
> +   };
> +
> +   /*
> +   * This is the handler for data received from remote. The behaviour is purely
> +   * dependent upon the protocol. This is just an example.
> +   */
> +   static void message_from_remote(struct mbox_client *cl, void *mssg)
> +   {
> +       struct demo_client *dc = container_of(cl, struct demo_client, cl);
> +       if (dc->async) {
> +           if (is_an_ack(mssg)) {
> +               /* An ACK to our last sample sent */
> +               return; /* Or do something else here */
> +           } else { /* A new message from remote */
> +               queue_req(mssg);
> +           }
> +       } else {
> +           /* Remote f/w sends only ACK packets on this channel */
> +           return;
> +       }
> +   }
> +
> +   static void sample_sent(struct mbox_client *cl, void *mssg, int r)
> +   {
> +       struct demo_client *dc = container_of(cl, struct demo_client, cl);
> +       complete(&dc->c);
> +   }
> +
> +   static void client_demo(struct platform_device *pdev)
> +   {
> +       struct demo_client *dc_sync, *dc_async;
> +       /* The controller already knows async_pkt and sync_pkt */
> +       struct async_pkt ap;
> +       struct sync_pkt sp;
> +
> +       dc_sync = kzalloc(sizeof(*dc_sync), GFP_KERNEL);
> +       dc_async = kzalloc(sizeof(*dc_async), GFP_KERNEL);
> +
> +       /* Populate non-blocking mode client */
> +       dc_async->cl.dev = &pdev->dev;
> +       dc_async->cl.rx_callback = message_from_remote;
> +       dc_async->cl.tx_done = sample_sent;
> +       dc_async->cl.tx_block = false;
> +       dc_async->cl.tx_tout = 0; /* doesn't matter here */
> +       dc_async->cl.knows_txdone = false; /* depending upon protocol */
> +       dc_async->async = true;
> +       init_completion(&dc_async->c);
> +
> +       /* Populate blocking mode client */
> +       dc_sync->cl.dev = &pdev->dev;
> +       dc_sync->cl.rx_callback = message_from_remote;
> +       dc_sync->cl.tx_done = NULL; /* operate in blocking mode */
> +       dc_sync->cl.tx_block = true;
> +       dc_sync->cl.tx_tout = 500; /* by half a second */
> +       dc_sync->cl.knows_txdone = false; /* depending upon protocol */
> +       dc_sync->async = false;
> +
> +       /* ASync mailbox is listed second in 'mboxes' property */
> +       dc_async->mbox = mbox_request_channel(&dc_async->cl, 1);
> +       /* Populate data packet */
> +       /* ap.xxx = 123; etc */
> +       /* Send async message to remote */
> +       mbox_send_message(dc_async->mbox, &ap);
> +
> +       /* Sync mailbox is listed first in 'mboxes' property */
> +       dc_sync->mbox = mbox_request_channel(&dc_sync->cl, 0);
> +       /* Populate data packet */
> +       /* sp.abc = 123; etc */
> +       /* Send message to remote in blocking mode */
> +       mbox_send_message(dc_sync->mbox, &sp);
> +       /* At this point 'sp' has been sent */
> +
> +       /* Now wait for async chan to be done */
> +       wait_for_completion(&dc_async->c);
> +   }
> diff --git a/Documentation/driver-api/mailbox/core.rst b/Documentation/driver-api/mailbox/core.rst
> new file mode 100644
> index 000000000000..d1220086da67
> --- /dev/null
> +++ b/Documentation/driver-api/mailbox/core.rst
> @@ -0,0 +1,182 @@
> +=====================
> +mailbox documentation
> +=====================
> +
> +Hardware Introduction
> +=====================
> +
> +Mailbox hardware is a specialized component found in multi-core
> +processors and embedded systems that facilitates inter-processor
> +communication (IPC) or communication between different hardware
> +components. It provides a structured mechanism for sending and
> +receiving messages, allowing various processors or devices to
> +exchange data efficiently. Here's an overview of its key
> +characteristics and functions:
> +
> +Key Characteristics of Mailbox Hardware
> +Interrupt Handling: Many mailbox implementations support
> +interrupt-driven communication. This allows a receiving processor
> +to be alerted when a new message arrives, facilitating immediate
> +processing without polling the mailbox constantly.
> +
> +Hardware Registers: Mailbox hardware often includes registers for
> +configuration and status monitoring. These registers can be used
> +to control the mailbox's behavior, check for available messages,
> +or acknowledge message receipt.
> +
> +Support for Multiple Protocols: Mailboxes can support various
> +communication protocols, enabling interoperability between different
> +hardware components and simplifying the integration of diverse systems.
> +
> +Synchronous and Asynchronous Modes: Mailbox hardware can operate in
> +both synchronous and asynchronous modes. In synchronous mode, the
> +sender may wait for the receiver to acknowledge receipt before
> +proceeding, while in asynchronous mode, the sender can continue
> +executing other tasks immediately after sending the message.
> +
> +
> +Mailbox framework design
> +========================
> +
> +The mailbox facilitates interprocessor communication by allowing processors to
> +exchange messages or signals. The mailbox framework consists of:
> +
> +Mailbox Controller: This is platform-specific and is responsible for configuring
> +and managing interrupts from the remote processor. It offers a generic API for
> +the mailbox client.
> +
> +Mailbox Client: This component handles the sending and receiving of messages.
> +
> +
> +............................................................................
> +:  client driver      client_a            client_b                         :
> +............................................................................
> +                            ^-------------------^
> +                                    |
> +                                    |
> +............................................................................
> +:  controller framework          mailbox                                   :
> +....................................|.......................................
> +                                    |
> +                                    |
> +............................................................................
> +:  controller driver          device specific                              :
> +....................................|.......................................
> +                                    |
> +                                    |
> +kernel                              |
> +............................................................................
> +hardware                            |
> +                                    |
> +                                    |
> +............................................................................
> +:                             remote processor                             :
> +............................................................................
> +
> +
> +In the context of a mailbox framework, a channel refers to a dedicated
> +communication pathway between two or more processors or components. By using
> +channels, the framework abstracts the complexity of interprocessor communication.
> +
> +Data Structures
> +================
> +
> +- **struct mbox_client**
> +  This structure represents a client that communicates over a mailbox
> +  channel. It holds information such as:
> +  - A pointer to the device associated with the client (`dev`).
> +  - Callback functions for handling message transmission events, including:
> +    - `rx_callback`: Called when a message is received.
> +    - `tx_done`: Called when a message transmission is acknowledged.
> +  - Flags that specify the client’s configuration, such as whether it operates
> +    in blocking mode.
> +
> +- **struct mbox_chan**
> +  This structure represents an individual mailbox channel. It maintains the
> +  state required for message queuing and transmission. Key members include:
> +  - `msg_data`: Array of messages queued for transmission.
> +  - `msg_count`: Number of messages currently queued.
> +  - `msg_free`: Index of the next free slot in the message queue.
> +  - `active_req`: Pointer to the currently active message being transmitted.
> +  - Synchronization primitives to manage access from multiple contexts.
> +
> +- **struct mbox_controller**
> +  This structure represents a mailbox controller that manages multiple
> +  channels. It includes:
> +  - A pointer to the device managing the mailbox.
> +  - Operations for sending and receiving messages, as well as initializing
> +    and shutting down the mailbox.
> +  - A list of associated channels and the total number of channels available.
> +
> +controller framework APIs
> +=========================
> +
> +``struct `mbox_controller` Initialization
> +-----------------------------------------
> +
> +Just like any other kernel framework, the whole mailbox controller registration
> +relies on the driver filling a structure and registering against the
> +framework. In our case, that structure is mbox_controller.
> +
> +The first thing you need to do in your driver is to allocate this
> +structure. Any of the usual memory allocators will do, but you'll also
> +need to initialize a few fields in there:
> +
> +- ``dev``: should hold the pointer to the ``struct device`` associated
> +  to your current driver instance.
> +
> +- ``ops``: Operators that work on each communication channel.
> +
> +- ``chans``: Array of channels.
> +
> +- ``num_chans``: Number of channels in the `chans` array.
> +
> +- ``txdone_irq``: Indicates if the controller can report to the API
> +  when the last transmitted data was read by the
> +                          remote (e.g., if it has a TX ACK interrupt).
> +
> +All the below fields are not mandatory.
> +
> +- ``txdone_poll``: Indicates if the controller can read but not report
> +                          the TX done. For example, some register may show
> +                          the TX status, but no interrupt is raised. This
> +                          field is ignored if `txdone_irq` is set.
> +
> +- ``txpoll_period``: If `txdone_poll` is in effect, the API polls for
> +                          the last TX status after this many milliseconds.
> +
> +- ``of_xlate``: Controller driver-specific mapping of channel via
> +                          Device Tree (DT).
> +
> +
> +Key Functions
> +-------------
> +
> +- **int devm_mbox_controller_register(struct mbox_controller *mbox)**
> +  This function registers a mailbox controller with the kernel. It makes the
> +  channels associated with the controller available for client requests. The
> +  function performs sanity checks on the controller structure to ensure all
> +  necessary fields are populated.
> +
> +- **struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)**
> +  This function requests a mailbox channel for a specified client, identified
> +  by an index. It searches for the appropriate mailbox channel, and if found,
> +  it returns a pointer to the channel. If the request fails (e.g., if the
> +  index is invalid), it returns an error pointer.
> +
> +- **void mbox_free_channel(struct mbox_chan *chan)**
> +  This function releases a mailbox channel that was previously allocated for a
> +  client. It ensures that the channel can be reused by other clients. If any
> +  messages are still in the queue, they are aborted, and no callbacks are made.
> +
> +- **int mbox_send_message(struct mbox_chan *chan, void *mssg)**
> +  This function is used by clients to send a message through the specified
> +  mailbox channel. The function can operate in either blocking or non-blocking
> +  mode, depending on the client’s configuration. It will queue the message for
> +  transmission and notify the client once the message is acknowledged.
> +
> +- **void mbox_chan_received_data(struct mbox_chan *chan, void *mssg)**
> +  This function is called by the controller driver to notify the mailbox
> +  framework that a message has been received on the specified channel. The
> +  received message is then passed to the appropriate client's `rx_callback`
> +  function for processing.
> diff --git a/Documentation/driver-api/mailbox/index.rst b/Documentation/driver-api/mailbox/index.rst
> new file mode 100644
> index 000000000000..e254a8fdb66a
> --- /dev/null
> +++ b/Documentation/driver-api/mailbox/index.rst
> @@ -0,0 +1,45 @@
> +=======================
> +Mailbox documentation
> +=======================
> +
> +Mailbox documentation provides documents for various aspects of mailbox
> +framework.
> +
> +Mailbox development documentation
> +---------------------------------
> +
> +This book helps with mailbox internal APIs and guide for mailbox device
> +driver writers.
> +
> +.. toctree::
> +   :maxdepth: 1
> +
> +   core
> +
> +mailbox controller driver documentation
> +------------------------------
> +
> +This book is a guide to device driver writers on how to register
> +mailbox controller to the mailbox framework.
> +
> +.. toctree::
> +   :maxdepth: 1
> +
> +   writing_mailbox_controller_drivers
> +
> +mailbox client driver documentation
> +------------------------------
> +
> +This book is a guide to mailbox client driver writers.
> +
> +.. toctree::
> +   :maxdepth: 1
> +
> +   client
> +
> +.. only::  subproject and html
> +
> +   Indices
> +   =======
> +
> +   * :ref:`genindex`
> diff --git a/Documentation/driver-api/mailbox/writing_mailbox_controller_drivers.rst b/Documentation/driver-api/mailbox/writing_mailbox_controller_drivers.rst
> new file mode 100644
> index 000000000000..2a82645c1357
> --- /dev/null
> +++ b/Documentation/driver-api/mailbox/writing_mailbox_controller_drivers.rst
> @@ -0,0 +1,179 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +.. _writing_mailbox_controller_drivers:
> +
> +==================================
> +Writing Mailbox Controller Drivers
> +==================================
> +
> +Introduction
> +============
> +
> +This document serves as a basic guideline for driver programmers that need
> +to hack a new mailbox controller driver or understand the essentials of
> +the existing ones.
> +
> +Driver Boilerplate
> +==================
> +
> +As a bare minimum, a mailbox controller driver needs to call
> +``mbox_controller_register`` function to register with the framework.
> +
> +A basic driver skeleton could look like this for a mailbox hardware that
> +has the following characteristics:
> +a. It supports only a single channel, i.e., only the remote processor can
> +   send interrupts.
> +b. Data transfer is over the registers associated with mailbox hardware.
> +c. Mailbox hardware is configured to receive interrupts.
> +d. When the remote processor is ready to send data, it triggers a mailbox
> +   interrupt.
> +e. As part of interrupt handling by Linux, it copies data from the registers.
> +
> +.. code-block:: c
> +
> +   #include <linux/device.h>
> +   #include <linux/interrupt.h>
> +   #include <linux/io.h>
> +   #include <linux/kernel.h>
> +   #include <linux/mailbox_controller.h>
> +   #include <linux/module.h>
> +   #include <linux/of.h>
> +   #include <linux/platform_device.h>
> +   #define DRIVER_NAME "dummy_controller"
> +
> +   struct dummy_mbox {
> +       struct device *dev;
> +       struct mbox_controller controller;
> +       int irq;
> +   };
> +
> +   static void dummy_mbox_receive(struct mbox_chan *chan)
> +   {
> +       struct dummy_mbox *mbox = chan->con_priv;
> +       int val;
> +
> +       // Data copied from registers
> +       val = read_register();
> +       mbox_chan_received_data(chan, &val);
> +   }
> +
> +   static irqreturn_t dummy_mbox_irq_handler(int irq, void *data)
> +   {
> +       struct mbox_chan *chan = data;
> +       struct dummy_mbox *mbox = chan->con_priv;
> +       u32 reg;
> +
> +       // Read registers to see if data is received
> +       dummy_mbox_receive(chan);
> +       mbox_chan_txdone(chan, 0);
> +       return reg ? IRQ_HANDLED : IRQ_NONE;
> +   }
> +
> +   static int dummy_mbox_send_data(struct mbox_chan *chan, void *data)
> +   {
> +       // Write data in registers to send it to the remote processor
> +       return 0;
> +   }
> +
> +   static int dummy_mbox_startup(struct mbox_chan *chan)
> +   {
> +       struct dummy_mbox *mbox = chan->con_priv;
> +       u32 reg;
> +       int ret;
> +
> +       ret = devm_request_irq(mbox->dev, mbox->irq, dummy_mbox_irq_handler, 0,
> +               DRIVER_NAME, chan);
> +       if (ret < 0) {
> +           dev_err(mbox->dev, "Cannot request irq\n");
> +           return ret;
> +       }
> +
> +       /* Register write to enable IRQ generation */
> +
> +       return 0;
> +   }
> +
> +   static void dummy_mbox_shutdown(struct mbox_chan *chan)
> +   {
> +       struct dummy_mbox *mbox = chan->con_priv;
> +
> +       /* Disable interrupt generation */
> +       devm_free_irq(mbox->dev, mbox->irq, chan);
> +   }
> +
> +   static const struct mbox_chan_ops dummy_mbox_ops = {
> +       .send_data = dummy_mbox_send_data,
> +       .startup = dummy_mbox_startup,
> +       .shutdown = dummy_mbox_shutdown,
> +   };
> +
> +   static int dummy_mbox_probe(struct platform_device *pdev)
> +   {
> +       struct dummy_mbox *mbox;
> +       struct mbox_chan *chans;
> +       int ret;
> +
> +       mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL);
> +       if (!mbox)
> +           return -ENOMEM;
> +
> +       /* Allocate one channel */
> +       chans = devm_kzalloc(&pdev->dev, sizeof(*chans), GFP_KERNEL);
> +       if (!chans)
> +           return -ENOMEM;
> +
> +       mbox->base = devm_platform_ioremap_resource(pdev, 0);
> +       if (IS_ERR(mbox->base))
> +           return PTR_ERR(mbox->base);
> +
> +       mbox->irq = platform_get_irq(pdev, 0);
> +       if (mbox->irq < 0)
> +           return mbox->irq;
> +
> +       mbox->dev = &pdev->dev;
> +
> +       /* Hardware supports only one channel. */
> +       mbox->controller.dev = mbox->dev;
> +       mbox->controller.num_chans = 1;
> +       mbox->controller.chans = chans;
> +       mbox->controller.ops = &dummy_mbox_ops;
> +       mbox->controller.txdone_irq = true;
> +
> +       ret = devm_mbox_controller_register(mbox->dev, &mbox->controller);
> +       if (ret) {
> +           dev_err(&pdev->dev, "Could not register mailbox controller\n");
> +           return ret;
> +       }
> +
> +       return ret;
> +   }
> +
> +   static const struct of_device_id dummy_mbox_match[] = {
> +       { .compatible = "dummy,dummy-mailbox" },
> +       { },
> +   };
> +
> +   MODULE_DEVICE_TABLE(of, dummy_mbox_match);
> +
> +   static struct platform_driver dummy_mbox_driver = {
> +       .probe = dummy_mbox_probe,
> +       .driver = {
> +           .name = DRIVER_NAME,
> +           .of_match_table = dummy_mbox_match,
> +       },
> +   };
> +
> +   module_platform_driver(dummy_mbox_driver);
> +   MODULE_LICENSE("GPL v2");
> +   MODULE_DESCRIPTION("Dummy mailbox controller driver");
> +
> +In the above code, a couple of things are done:
> +a. The controller is registered in the probe along with callbacks, which in
> +   this case are the bare minimum: ``startup``, ``shutdown``, and
> +   ``send_data``.
> +b. IRQ is registered to get notifications from the remote processor.
> +c. In the IRQ handler, registers are read to copy data, and
> +   ``mbox_chan_received_data`` is called to hand over the data to the client.
> +d. ``mbox_chan_txdone`` is called to let the framework know that this data
> +   is the last data and no more data is to be expected for the current transfer.
> +
> --
> 2.39.3 (Apple Git-146)
>





[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux