drivers/mailbox/mailbox-mpfs.c

Source file repositories/reference/linux-study-clean/drivers/mailbox/mailbox-mpfs.c

File Facts

System
Linux kernel
Corpus path
drivers/mailbox/mailbox-mpfs.c
Extension
.c
Size
9088 bytes
Lines
341
Domain
Driver Families
Bucket
drivers/mailbox
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct mpfs_mbox {
	struct mbox_controller controller;
	struct device *dev;
	int irq;
	void __iomem *ctrl_base;
	void __iomem *mbox_base;
	void __iomem *int_reg;
	struct mbox_chan chans[1];
	struct mpfs_mss_response *response;
	struct regmap *sysreg_scb, *control_scb;
	u16 resp_offset;
};

static bool mpfs_mbox_busy(struct mpfs_mbox *mbox)
{
	u32 status;

	if (mbox->control_scb)
		regmap_read(mbox->control_scb, SERVICES_SR_OFFSET, &status);
	else
		status = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET);

	return status & SCB_STATUS_BUSY_MASK;
}

static bool mpfs_mbox_last_tx_done(struct mbox_chan *chan)
{
	struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv;
	struct mpfs_mss_response *response = mbox->response;
	u32 val;

	if (mpfs_mbox_busy(mbox))
		return false;

	/*
	 * The service status is stored in bits 31:16 of the SERVICES_SR
	 * register & is only valid when the system controller is not busy.
	 * Failed services are intended to generated interrupts, but in reality
	 * this does not happen, so the status must be checked here.
	 */
	if (mbox->control_scb)
		regmap_read(mbox->control_scb, SERVICES_SR_OFFSET, &val);
	else
		val = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET);

	response->resp_status = (val & SCB_STATUS_MASK) >> SCB_STATUS_POS;

	return true;
}

static int mpfs_mbox_send_data(struct mbox_chan *chan, void *data)
{
	struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv;
	struct mpfs_mss_msg *msg = data;
	u32 tx_trigger;
	u16 opt_sel;
	u32 val = 0u;

	mbox->response = msg->response;
	mbox->resp_offset = msg->resp_offset;

	if (mpfs_mbox_busy(mbox))
		return -EBUSY;

	if (msg->cmd_data_size) {
		u32 index;
		u8 extra_bits = msg->cmd_data_size & 3;
		u32 *word_buf = (u32 *)msg->cmd_data;

		for (index = 0; index < (msg->cmd_data_size / 4); index++)
			writel_relaxed(word_buf[index],
				       mbox->mbox_base + msg->mbox_offset + index * 0x4);
		if (extra_bits) {
			u8 i;
			u8 byte_off = ALIGN_DOWN(msg->cmd_data_size, 4);
			u8 *byte_buf = msg->cmd_data + byte_off;

			val = readl_relaxed(mbox->mbox_base + msg->mbox_offset + index * 0x4);

			for (i = 0u; i < extra_bits; i++) {
				val &= ~(0xffu << (i * 8u));
				val |= (byte_buf[i] << (i * 8u));
			}

			writel_relaxed(val, mbox->mbox_base + msg->mbox_offset + index * 0x4);
		}
	}

	opt_sel = ((msg->mbox_offset << 7u) | (msg->cmd_opcode & 0x7fu));

Annotation

Implementation Notes