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) >