drivers/soc/apple/mailbox.c

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

File Facts

System
Linux kernel
Corpus path
drivers/soc/apple/mailbox.c
Extension
.c
Size
12721 bytes
Lines
464
Domain
Driver Families
Bucket
drivers/soc
Inferred role
Driver Families: exported/initcall integration point
Status
integration 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 apple_mbox_hw {
	unsigned int control_full;
	unsigned int control_empty;

	unsigned int a2i_control;
	unsigned int a2i_send0;
	unsigned int a2i_send1;

	unsigned int i2a_control;
	unsigned int i2a_recv0;
	unsigned int i2a_recv1;

	bool has_irq_controls;
	unsigned int irq_enable;
	unsigned int irq_ack;
	unsigned int irq_bit_recv_not_empty;
	unsigned int irq_bit_send_empty;
};

int apple_mbox_send(struct apple_mbox *mbox, const struct apple_mbox_msg msg,
		    bool atomic)
{
	unsigned long flags;
	int ret;
	u32 mbox_ctrl;
	long t;

	spin_lock_irqsave(&mbox->tx_lock, flags);
	mbox_ctrl = readl_relaxed(mbox->regs + mbox->hw->a2i_control);

	while (mbox_ctrl & mbox->hw->control_full) {
		if (atomic) {
			ret = readl_poll_timeout_atomic(
				mbox->regs + mbox->hw->a2i_control, mbox_ctrl,
				!(mbox_ctrl & mbox->hw->control_full), 100,
				APPLE_MBOX_TX_TIMEOUT * 1000);

			if (ret) {
				spin_unlock_irqrestore(&mbox->tx_lock, flags);
				return ret;
			}

			break;
		}
		/*
		 * The interrupt is level triggered and will keep firing as long as the
		 * FIFO is empty. It will also keep firing if the FIFO was empty
		 * at any point in the past until it has been acknowledged at the
		 * mailbox level. By acknowledging it here we can ensure that we will
		 * only get the interrupt once the FIFO has been cleared again.
		 * If the FIFO is already empty before the ack it will fire again
		 * immediately after the ack.
		 */
		if (mbox->hw->has_irq_controls) {
			writel_relaxed(mbox->hw->irq_bit_send_empty,
				       mbox->regs + mbox->hw->irq_ack);
		}
		enable_irq(mbox->irq_send_empty);
		reinit_completion(&mbox->tx_empty);
		spin_unlock_irqrestore(&mbox->tx_lock, flags);

		t = wait_for_completion_interruptible_timeout(
			&mbox->tx_empty,
			msecs_to_jiffies(APPLE_MBOX_TX_TIMEOUT));
		if (t < 0)
			return t;
		else if (t == 0)
			return -ETIMEDOUT;

		spin_lock_irqsave(&mbox->tx_lock, flags);
		mbox_ctrl = readl_relaxed(mbox->regs + mbox->hw->a2i_control);
	}

	writeq_relaxed(msg.msg0, mbox->regs + mbox->hw->a2i_send0);
	writeq_relaxed(FIELD_PREP(APPLE_MBOX_MSG1_MSG, msg.msg1),
		       mbox->regs + mbox->hw->a2i_send1);

	spin_unlock_irqrestore(&mbox->tx_lock, flags);

	return 0;
}
EXPORT_SYMBOL(apple_mbox_send);

static irqreturn_t apple_mbox_send_empty_irq(int irq, void *data)
{
	struct apple_mbox *mbox = data;

	/*
	 * We don't need to acknowledge the interrupt at the mailbox level
	 * here even if supported by the hardware. It will keep firing but that

Annotation

Implementation Notes