// SPDX-License-Identifier: GPL-2.0 /* * ARM Message Handling Unit Version 3 (MHUv3) driver. * * Copyright (C) 2024 ARM Ltd. * * Based on ARM MHUv2 driver.
*/
/* Maximum number of Doorbell channel windows */ #define MHUV3_DBCW_MAX 128 /* Number of DBCH combined interrupt status registers */ #define MHUV3_DBCH_CMB_INT_ST_REG_CNT 4
/* Number of FFCH combined interrupt status registers */ #define MHUV3_FFCH_CMB_INT_ST_REG_CNT 2
#define MHUV3_FLAG_BITS 32
/* Not a typo ... */ #define MHUV3_MAJOR_VERSION 2
/** * struct mhuv3_protocol_ops - MHUv3 operations * * @rx_startup: Receiver startup callback. * @rx_shutdown: Receiver shutdown callback. * @read_data: Read available Sender in-band LE data (if any). * @rx_complete: Acknowledge data reception to the Sender. Any out-of-band data * has to have been already retrieved before calling this. * @tx_startup: Sender startup callback. * @tx_shutdown: Sender shutdown callback. * @last_tx_done: Report back to the Sender if the last transfer has completed. * @send_data: Send data to the receiver. * * Each supported transport protocol provides its own implementation of * these operations.
*/ struct mhuv3_protocol_ops { int (*rx_startup)(struct mhuv3 *mhu, struct mbox_chan *chan); void (*rx_shutdown)(struct mhuv3 *mhu, struct mbox_chan *chan); void *(*read_data)(struct mhuv3 *mhu, struct mbox_chan *chan); void (*rx_complete)(struct mhuv3 *mhu, struct mbox_chan *chan); void (*tx_startup)(struct mhuv3 *mhu, struct mbox_chan *chan); void (*tx_shutdown)(struct mhuv3 *mhu, struct mbox_chan *chan); int (*last_tx_done)(struct mhuv3 *mhu, struct mbox_chan *chan); int (*send_data)(struct mhuv3 *mhu, struct mbox_chan *chan, void *arg);
};
/** * struct mhuv3_mbox_chan_priv - MHUv3 channel private information * * @ch_idx: Channel window index associated to this mailbox channel. * @doorbell: Doorbell bit number within the @ch_idx window. * Only relevant to Doorbell transport. * @ops: Transport protocol specific operations for this channel. * * Transport specific data attached to mmailbox channel priv data.
*/ struct mhuv3_mbox_chan_priv {
u32 ch_idx;
u32 doorbell; conststruct mhuv3_protocol_ops *ops;
};
/** * struct mhuv3_extension - MHUv3 extension descriptor * * @type: Type of extension * @num_chans: Max number of channels found for this extension. * @base_ch_idx: First channel number assigned to this extension, picked from * the set of all mailbox channels descriptors created. * @mbox_of_xlate: Extension specific helper to parse DT and lookup associated * channel from the related 'mboxes' property. * @combined_irq_setup: Extension specific helper to setup the combined irq. * @channels_init: Extension specific helper to initialize channels. * @chan_from_comb_irq_get: Extension specific helper to lookup which channel * triggered the combined irq. * @pending_db: Array of per-channel pending doorbells. * @pending_lock: Protect access to pending_db.
*/ struct mhuv3_extension { enum mhuv3_extension_type type; unsignedint num_chans; unsignedint base_ch_idx; struct mbox_chan *(*mbox_of_xlate)(struct mhuv3 *mhu, unsignedint channel, unsignedint param); void (*combined_irq_setup)(struct mhuv3 *mhu); int (*channels_init)(struct mhuv3 *mhu); struct mbox_chan *(*chan_from_comb_irq_get)(struct mhuv3 *mhu);
u32 pending_db[MHUV3_DBCW_MAX]; /* Protect access to pending_db */
spinlock_t pending_lock;
};
/** * struct mhuv3 - MHUv3 mailbox controller data * * @frame: Frame type: MBX_FRAME or PBX_FRAME. * @auto_op_full: Flag to indicate if the MHU supports AutoOp full mode. * @major: MHUv3 controller architectural major version. * @minor: MHUv3 controller architectural minor version. * @implem: MHUv3 controller IIDR implementer. * @rev: MHUv3 controller IIDR revision. * @var: MHUv3 controller IIDR variant. * @prod_id: MHUv3 controller IIDR product_id. * @num_chans: The total number of channnels discovered across all extensions. * @cmb_irq: Combined IRQ number if any found defined. * @ctrl: A reference to the MHUv3 control page for this block. * @pbx: Base address of the PBX register mapping region. * @mbx: Base address of the MBX register mapping region. * @ext: Array holding descriptors for any found implemented extension. * @mbox: Mailbox controller belonging to the MHU frame.
*/ struct mhuv3 { enum mhuv3_frame frame; bool auto_op_full; unsignedint major; unsignedint minor; unsignedint implem; unsignedint rev; unsignedint var; unsignedint prod_id; unsignedint num_chans; int cmb_irq; struct ctrl_page __iomem *ctrl; union { struct mhu3_pbx_frame_reg __iomem *pbx; struct mhu3_mbx_frame_reg __iomem *mbx;
}; struct mhuv3_extension *ext[NUM_EXT]; struct mbox_controller mbox;
};
/* Take care to clear the pending doorbell also when polling */
spin_lock_irqsave(&e->pending_lock, flags);
e->pending_db[priv->ch_idx] &= ~BIT(priv->doorbell);
spin_unlock_irqrestore(&e->pending_lock, flags);
}
scoped_guard(spinlock_irqsave, &e->pending_lock) { /* Only one in-flight Transfer is allowed per-doorbell */ if (e->pending_db[priv->ch_idx] & BIT(priv->doorbell)) return -EBUSY;
/* * Note that extensions initialization fails only when such * extension initialization routine fails and the extensions * was found to be supported in hardware and in software.
*/
ret = mhuv3_extension_init[i](mhu); if (ret) return dev_err_probe(dev, ret, "Failed to initialize %s %s\n",
mhuv3_str[mhu->frame],
mhuv3_ext_str[i]);
}
return 0;
}
static irqreturn_t mhuv3_pbx_comb_interrupt(int irq, void *arg)
{ unsignedint i, found = 0; struct mhuv3 *mhu = arg; struct mbox_chan *chan; struct device *dev; int ret = IRQ_NONE;
dev = mhu->mbox.dev; for (i = 0; i < NUM_EXT; i++) { struct mhuv3_mbox_chan_priv *priv;
/* FCE does not participate to the PBX combined */ if (i == FCE_EXT || !mhu->ext[i]) continue;
chan = mhu->ext[i]->chan_from_comb_irq_get(mhu); if (IS_ERR(chan)) continue;
found++;
priv = chan->con_priv; if (!chan->cl) {
dev_warn(dev, "TX Ack on UNBOUND channel (%u)\n",
priv->ch_idx); continue;
}
mbox_chan_txdone(chan, 0);
ret = IRQ_HANDLED;
}
if (found == 0)
dev_warn_once(dev, "Failed to find channel for the TX interrupt\n");
return ret;
}
static irqreturn_t mhuv3_mbx_comb_interrupt(int irq, void *arg)
{ unsignedint i, found = 0; struct mhuv3 *mhu = arg; struct mbox_chan *chan; struct device *dev; int ret = IRQ_NONE;
dev = mhu->mbox.dev; for (i = 0; i < NUM_EXT; i++) { struct mhuv3_mbox_chan_priv *priv; void *data __free(kfree) = NULL;
if (!mhu->ext[i]) continue;
/* Process any extension which could be source of the IRQ */
chan = mhu->ext[i]->chan_from_comb_irq_get(mhu); if (IS_ERR(chan)) continue;
found++; /* From here on we need to call rx_complete even on error */
priv = chan->con_priv; if (!chan->cl) {
dev_warn(dev, "RX Data on UNBOUND channel (%u)\n",
priv->ch_idx); goto rx_ack;
}
/* Read optional in-band LE data first. */ if (priv->ops->read_data) {
data = priv->ops->read_data(mhu, chan); if (IS_ERR(data)) {
dev_err(dev, "Failed to read in-band data. err:%ld\n",
PTR_ERR(no_free_ptr(data))); goto rx_ack;
}
}
mbox_chan_received_data(chan, data);
ret = IRQ_HANDLED;
/* * Acknowledge transfer after any possible optional * out-of-band data has also been retrieved via * mbox_chan_received_data().
*/
rx_ack: if (priv->ops->rx_complete)
priv->ops->rx_complete(mhu, chan);
}
if (found == 0)
dev_warn_once(dev, "Failed to find channel for the RX interrupt\n");
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.