// SPDX-License-Identifier: GPL-2.0 /* * ARM Message Handling Unit Version 2 (MHUv2) driver. * * Copyright (C) 2020 ARM Ltd. * Copyright (C) 2020 Linaro Ltd. * * An MHUv2 mailbox controller can provide up to 124 channel windows (each 32 * bit long) and the driver allows any combination of both the transport * protocol modes: data-transfer and doorbell, to be used on those channel * windows. * * The transport protocols should be specified in the device tree entry for the * device. The transport protocols determine how the underlying hardware * resources of the device are utilized when transmitting data. Refer to the * device tree bindings of the ARM MHUv2 controller for more details. * * The number of registered mailbox channels is dependent on both the underlying * hardware - mainly the number of channel windows implemented by the platform, * as well as the selected transport protocols. * * The MHUv2 controller can work both as a sender and receiver, but the driver * and the DT bindings support unidirectional transfers for better allocation of * the channels. That is, this driver will be probed for two separate devices * for each mailbox controller, a sender device and a receiver device.
*/
/* Maximum number of channel windows */ #define MHUV2_CH_WN_MAX 124 /* Number of combined interrupt status registers */ #define MHUV2_CMB_INT_ST_REG_CNT 4 #define MHUV2_STAT_BYTES (sizeof(u32)) #define MHUV2_STAT_BITS (MHUV2_STAT_BYTES * __CHAR_BIT__)
/** * struct mhuv2 - MHUv2 mailbox controller data * * @mbox: Mailbox controller belonging to the MHU frame. * @send: Base address of the register mapping region. * @recv: Base address of the register mapping region. * @frame: Frame type: RECEIVER_FRAME or SENDER_FRAME. * @irq: Interrupt. * @windows: Channel windows implemented by the platform. * @minor: Minor version of the controller. * @length: Length of the protocols array in bytes. * @protocols: Raw protocol information, derived from device tree. * @doorbell_pending_lock: spinlock required for correct operation of Tx * interrupt for doorbells.
*/ struct mhuv2 { struct mbox_controller mbox; union { struct mhu2_send_frame_reg __iomem *send; struct mhu2_recv_frame_reg __iomem *recv;
}; enum mhuv2_frame frame; unsignedint irq; unsignedint windows; unsignedint minor; unsignedint length;
u32 *protocols;
/** * struct mhuv2_protocol_ops - MHUv2 operations * * Each transport protocol must provide an implementation of the operations * provided here. * * @rx_startup: Startup callback for receiver. * @rx_shutdown: Shutdown callback for receiver. * @read_data: Reads and clears newly available data. * @tx_startup: Startup callback for receiver. * @tx_shutdown: Shutdown callback for receiver. * @last_tx_done: Report back if the last tx is completed or not. * @send_data: Send data to the receiver.
*/ struct mhuv2_protocol_ops { int (*rx_startup)(struct mhuv2 *mhu, struct mbox_chan *chan); void (*rx_shutdown)(struct mhuv2 *mhu, struct mbox_chan *chan); void *(*read_data)(struct mhuv2 *mhu, struct mbox_chan *chan);
/* * MHUv2 mailbox channel's private information * * @ops: protocol specific ops for the channel. * @ch_wn_idx: Channel window index allocated to the channel. * @windows: Total number of windows consumed by the channel, only relevant * in DATA_TRANSFER protocol. * @doorbell: Doorbell bit number within the ch_wn_idx window, only relevant * in DOORBELL protocol. * @pending: Flag indicating pending doorbell interrupt, only relevant in * DOORBELL protocol.
*/ struct mhuv2_mbox_chan_priv { conststruct mhuv2_protocol_ops *ops;
u32 ch_wn_idx; union {
u32 windows; struct {
u32 doorbell;
u32 pending;
};
};
};
/* Macro for reading a bitfield within a physically mapped packed struct */ #define readl_relaxed_bitfield(_regptr, _type, _field) \
({ \
u32 _regval; \
_regval = readl_relaxed((_regptr)); \
(*(_type *)(&_regval))._field; \
})
/* Macro for writing a bitfield within a physically mapped packed struct */ #define writel_relaxed_bitfield(_value, _regptr, _type, _field) \
({ \
u32 _regval; \
_regval = readl_relaxed(_regptr); \
(*(_type *)(&_regval))._field = _value; \
writel_relaxed(_regval, _regptr); \
})
/* =================== Doorbell transport protocol operations =============== */
/* * The protocol mandates that all but the last status register must be * masked.
*/
writel_relaxed(0xFFFFFFFF, &mhu->recv->ch_wn[i].mask_clear); return 0;
}
staticvoid *mhuv2_data_transfer_read_data(struct mhuv2 *mhu, struct mbox_chan *chan)
{ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; constint windows = priv->windows; struct arm_mhuv2_mbox_msg *msg;
u32 *data; int i, idx;
msg = kzalloc(sizeof(*msg) + windows * MHUV2_STAT_BYTES, GFP_KERNEL); if (!msg) return ERR_PTR(-ENOMEM);
data = msg->data = msg + 1;
msg->len = windows * MHUV2_STAT_BYTES;
/* * Messages are expected in order of most significant word to least * significant word. Refer mhuv2_data_transfer_send_data() for more * details. * * We also need to read the stat register instead of stat_masked, as we * masked all but the last window. * * Last channel window must be cleared as the final operation. Upon * clearing the last channel window register, which is unmasked in * data-transfer protocol, the interrupt is de-asserted.
*/ for (i = 0; i < windows; i++) {
idx = priv->ch_wn_idx + i;
data[windows - 1 - i] = readl_relaxed(&mhu->recv->ch_wn[idx].stat);
writel_relaxed(0xFFFFFFFF, &mhu->recv->ch_wn[idx].stat_clear);
}
/* Enable interrupts only for the last window */ if (mhu->minor) {
writel_relaxed(0x1, &mhu->send->ch_wn[i].int_clr);
writel_relaxed(0x1, &mhu->send->ch_wn[i].int_en);
}
}
/* Just checking the last channel window should be enough */ return !readl_relaxed(&mhu->send->ch_wn[i].stat);
}
/* * Message will be transmitted from most significant to least significant word. * This is to allow for messages shorter than channel windows to still trigger * the receiver interrupt which gets activated when the last stat register is * written. As an example, a 6-word message is to be written on a 4-channel MHU * connection: Registers marked with '*' are masked, and will not generate an * interrupt on the receiver side once written. * * u32 *data = [0x00000001], [0x00000002], [0x00000003], [0x00000004], * [0x00000005], [0x00000006] * * ROUND 1: * stat reg To write Write sequence * [ stat 3 ] <- [0x00000001] 4 <- triggers interrupt on receiver * [ stat 2 ] <- [0x00000002] 3 * [ stat 1 ] <- [0x00000003] 2 * [ stat 0 ] <- [0x00000004] 1 * * data += 4 // Increment data pointer by number of stat regs * * ROUND 2: * stat reg To write Write sequence * [ stat 3 ] <- [0x00000005] 2 <- triggers interrupt on receiver * [ stat 2 ] <- [0x00000006] 1 * [ stat 1 ] <- [0x00000000] * [ stat 0 ] <- [0x00000000]
*/ staticint mhuv2_data_transfer_send_data(struct mhuv2 *mhu, struct mbox_chan *chan, void *arg)
{ conststruct arm_mhuv2_mbox_msg *msg = arg; int bytes_left = msg->len, bytes_to_send, bytes_in_round, i; struct mhuv2_mbox_chan_priv *priv = chan->con_priv; int windows = priv->windows;
u32 *data = msg->data, word;
while (bytes_left) { if (!data[0]) {
dev_err(mhu->mbox.dev, "Data aligned at first window can't be zero to guarantee interrupt generation at receiver"); return -EINVAL;
}
chan = get_irq_chan_comb(mhu, mhu->send->chcomb_int_st); if (IS_ERR(chan)) {
dev_warn(dev, "Failed to find channel for the Tx interrupt\n"); return IRQ_NONE;
}
priv = chan->con_priv;
if (!IS_PROTOCOL_DOORBELL(priv)) { for (i = 0; i < priv->windows; i++)
writel_relaxed(1, &mhu->send->ch_wn[priv->ch_wn_idx + i].int_clr);
if (chan->cl) {
mbox_chan_txdone(chan, 0); return IRQ_HANDLED;
}
dev_warn(dev, "Tx interrupt Received on channel (%u) not currently attached to a mailbox client\n",
priv->ch_wn_idx); return IRQ_NONE;
}
/* Clear the interrupt first, so we don't miss any doorbell later */
writel_relaxed(1, &mhu->send->ch_wn[priv->ch_wn_idx].int_clr);
/* * In Doorbell mode, make sure no new transitions happen while the * interrupt handler is trying to find the finished doorbell tx * operations, else we may think few of the transfers were complete * before they actually were.
*/
spin_lock_irqsave(&mhu->doorbell_pending_lock, flags);
/* * In case of doorbell mode, the first channel of the window is returned * by get_irq_chan_comb(). Find all the pending channels here.
*/
stat = readl_relaxed(&mhu->send->ch_wn[priv->ch_wn_idx].stat);
for (i = 0; i < MHUV2_STAT_BITS; i++) {
priv = chan[i].con_priv;
/* Find cases where pending was 1, but stat's bit is cleared */ if (priv->pending ^ ((stat >> i) & 0x1)) {
BUG_ON(!priv->pending);
if (!chan->cl) {
dev_warn(dev, "Tx interrupt received on doorbell (%u : %u) channel not currently attached to a mailbox client\n",
priv->ch_wn_idx, i); continue;
}
if (!found) { /* * We may have already processed the doorbell in the previous * iteration if the interrupt came right after we cleared it but * before we read the stat register.
*/
dev_dbg(dev, "Couldn't find the doorbell (%u) for the Tx interrupt interrupt\n",
priv->ch_wn_idx); return IRQ_NONE;
}
chan = get_irq_chan_comb(mhu, mhu->recv->chcomb_int_st); if (IS_ERR(chan)) return chan;
priv = chan->con_priv; if (!IS_PROTOCOL_DOORBELL(priv)) return chan;
/* * In case of doorbell mode, the first channel of the window is returned * by the routine. Find the exact channel here.
*/
stat = readl_relaxed(&mhu->recv->ch_wn[priv->ch_wn_idx].stat_masked);
BUG_ON(!stat);
return chan + __builtin_ctz(stat);
}
staticstruct mbox_chan *get_irq_chan_stat_rx(struct mhuv2 *mhu)
{ struct mbox_chan *chans = mhu->mbox.chans; struct mhuv2_mbox_chan_priv *priv;
u32 stat; int i = 0;
while (i < mhu->mbox.num_chans) {
priv = chans[i].con_priv;
stat = readl_relaxed(&mhu->recv->ch_wn[priv->ch_wn_idx].stat_masked);
if (stat) { if (IS_PROTOCOL_DOORBELL(priv))
i += __builtin_ctz(stat); return &chans[i];
}
i += IS_PROTOCOL_DOORBELL(priv) ? MHUV2_STAT_BITS : 1;
}
return ERR_PTR(-EIO);
}
staticstruct mbox_chan *get_irq_chan_rx(struct mhuv2 *mhu)
{ if (!mhu->minor) return get_irq_chan_stat_rx(mhu);
if (IS_ERR(chan)) {
dev_warn(dev, "Failed to find channel for the rx interrupt\n"); return IRQ_NONE;
}
priv = chan->con_priv;
/* Read and clear the data first */
data = priv->ops->read_data(mhu, chan);
if (!chan->cl) {
dev_warn(dev, "Received data on channel (%u) not currently attached to a mailbox client\n",
priv->ch_wn_idx);
} elseif (IS_ERR(data)) {
dev_err(dev, "Failed to read data: %lu\n", PTR_ERR(data));
} else {
mbox_chan_received_data(chan, data);
ret = IRQ_HANDLED;
}
for (i = 0; i < mhu->length; i += 2) {
protocol = mhu->protocols[i];
windows = mhu->protocols[i + 1];
if (!windows) {
dev_err(dev, "Window size can't be zero (%d)\n", i); return -EINVAL;
}
total_windows += windows;
if (protocol == DOORBELL) {
channels += MHUV2_STAT_BITS * windows;
} elseif (protocol == DATA_TRANSFER) {
channels++;
} else {
dev_err(dev, "Invalid protocol (%d) present in %s property at index %d\n",
protocol, MHUV2_PROTOCOL_PROP, i); return -EINVAL;
}
}
if (total_windows > mhu->windows) {
dev_err(dev, "Channel windows can't be more than what's implemented by the hardware ( %d: %d)\n",
total_windows, mhu->windows); return -EINVAL;
}
/* * Permanently enable interrupt as we can't * control it per doorbell.
*/ if (mhu->frame == SENDER_FRAME && mhu->minor)
writel_relaxed(0x1, &mhu->send->ch_wn[priv->ch_wn_idx].int_en);
}
}
/* Make sure we have initialized all channels */
BUG_ON(chans - mbox->chans != mbox->num_chans);
/* * For minor version 1 and forward, tx interrupt is provided by * the controller.
*/ if (mhu->minor && adev->irq[0]) {
ret = devm_request_threaded_irq(dev, adev->irq[0], NULL,
mhuv2_sender_interrupt,
IRQF_ONESHOT, "mhuv2-tx", mhu); if (ret) {
dev_err(dev, "Failed to request tx IRQ, fallback to polling mode: %d\n",
ret);
} else {
mhu->mbox.txdone_irq = true;
mhu->mbox.txdone_poll = false;
mhu->irq = adev->irq[0];
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.22Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.