回复: [PATCH v1] mailbox: add i.MX MU driver

Alice Guo (OSS) alice.guo at oss.nxp.com
Sun Sep 29 15:10:30 CEST 2024


Sorry, please ignore this patch. I used the wrong mail.

> -----邮件原件-----
> 发件人: U-Boot <u-boot-bounces at lists.denx.de> 代表 Alice Guo
> 发送时间: 2024年9月29日 9:55
> 收件人: u-boot at lists.denx.de
> 抄送: trini at konsulko.com; Peng Fan <peng.fan at nxp.com>; Viorel Suman
> <viorel.suman at nxp.com>; michal.simek at amd.com; Ye Li <ye.li at nxp.com>;
> Alice Guo <alice.guo at nxp.com>
> 主题: [PATCH v1] mailbox: add i.MX MU driver
> 
> From: Peng Fan <peng.fan at nxp.com>
> 
> Add i.MX MU based mailbox driver.
> 
> Signed-off-by: Viorel Suman <viorel.suman at nxp.com>
> Signed-off-by: Peng Fan <peng.fan at nxp.com>
> Signed-off-by: Alice Guo <alice.guo at nxp.com>
> Reviewed-by: Ye Li <ye.li at nxp.com>
> ---
>  drivers/mailbox/Kconfig       |   7 +
>  drivers/mailbox/Makefile      |   1 +
>  drivers/mailbox/imx-mailbox.c | 417
> ++++++++++++++++++++++++++++++++++
>  3 files changed, 425 insertions(+)
>  create mode 100644 drivers/mailbox/imx-mailbox.c
> 
> diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index
> 67d5ac1a74..4d9f004eba 100644
> --- a/drivers/mailbox/Kconfig
> +++ b/drivers/mailbox/Kconfig
> @@ -21,6 +21,13 @@ config APPLE_MBOX
>  	  such as the System Management Controller (SMC) and NVMe and this
>  	  driver is required to get that functionality up and running.
> 
> +config IMX_MU_MBOX
> +	bool "Enable i.MX MU MBOX support"
> +	depends on DM_MAILBOX
> +	help
> +	  Enable support for i.MX Messaging Unit for communication with other
> +	  processors on the SoC using mailbox interface
> +
>  config SANDBOX_MBOX
>  	bool "Enable the sandbox mailbox test driver"
>  	depends on DM_MAILBOX && SANDBOX
> diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index
> 59e8d0de93..96a6adb709 100644
> --- a/drivers/mailbox/Makefile
> +++ b/drivers/mailbox/Makefile
> @@ -5,6 +5,7 @@
> 
>  obj-$(CONFIG_$(SPL_)DM_MAILBOX) += mailbox-uclass.o
>  obj-$(CONFIG_APPLE_MBOX) += apple-mbox.o
> +obj-$(CONFIG_IMX_MU_MBOX) += imx-mailbox.o
>  obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox.o
>  obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox-test.o
>  obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o diff --git
> a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c new file
> mode 100644 index 0000000000..584950b20d
> --- /dev/null
> +++ b/drivers/mailbox/imx-mailbox.c
> @@ -0,0 +1,417 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright 2017-2023 NXP
> + */
> +
> +#include <asm/io.h>
> +#include <dm.h>
> +#include <dm/device_compat.h>
> +#include <mailbox-uclass.h>
> +#include <linux/bitfield.h>
> +#include <linux/bug.h>
> +#include <linux/iopoll.h>
> +#include <linux/compat.h>
> +
> +/* This driver only exposes the status bits to keep with the
> + * polling methodology of u-boot.
> + */
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +#define IMX_MU_CHANS		24
> +
> +#define IMX_MU_V2_PAR_OFF	0x4
> +#define IMX_MU_V2_TR_MASK	GENMASK(7, 0)
> +#define IMX_MU_V2_RR_MASK	GENMASK(15, 8)
> +
> +enum imx_mu_chan_type {
> +	IMX_MU_TYPE_TX          = 0, /* Tx */
> +	IMX_MU_TYPE_RX          = 1, /* Rx */
> +	IMX_MU_TYPE_TXDB        = 2, /* Tx doorbell */
> +	IMX_MU_TYPE_RXDB        = 3, /* Rx doorbell */
> +	IMX_MU_TYPE_RST         = 4, /* Reset */
> +	IMX_MU_TYPE_TXDB_V2     = 5, /* Tx doorbell with S/W ACK */
> +};
> +
> +enum imx_mu_xcr {
> +	IMX_MU_CR,
> +	IMX_MU_GIER,
> +	IMX_MU_GCR,
> +	IMX_MU_TCR,
> +	IMX_MU_RCR,
> +	IMX_MU_xCR_MAX,
> +};
> +
> +enum imx_mu_xsr {
> +	IMX_MU_SR,
> +	IMX_MU_GSR,
> +	IMX_MU_TSR,
> +	IMX_MU_RSR,
> +	IMX_MU_xSR_MAX,
> +};
> +
> +struct imx_mu_con_priv {
> +	unsigned int		idx;
> +	enum imx_mu_chan_type	type;
> +	struct mbox_chan	*chan;
> +};
> +
> +enum imx_mu_type {
> +	IMX_MU_V1,
> +	IMX_MU_V2 = BIT(1),
> +	IMX_MU_V2_S4 = BIT(15),
> +	IMX_MU_V2_IRQ = BIT(16),
> +};
> +
> +struct imx_mu {
> +	void __iomem *base;
> +	const struct imx_mu_dcfg *dcfg;
> +	u32 num_tr;
> +	u32 num_rr;
> +	/* use pointers to channel as a way to reserve channels */
> +	struct mbox_chan *channels[IMX_MU_CHANS];
> +	struct imx_mu_con_priv  con_priv[IMX_MU_CHANS]; };
> +
> +struct imx_mu_dcfg {
> +	int (*tx)(struct imx_mu *plat, struct imx_mu_con_priv *cp, const void
> *data);
> +	int (*rx)(struct imx_mu *plat, struct imx_mu_con_priv *cp);
> +	int (*rxdb)(struct imx_mu *plat, struct imx_mu_con_priv *cp);
> +	int (*init)(struct imx_mu *plat);
> +	int (*of_xlate)(struct mbox_chan *chan, struct ofnode_phandle_args
> *args);
> +	enum imx_mu_type type;
> +	u32	xTR;			/* Transmit Register0 */
> +	u32	xRR;			/* Receive Register0 */
> +	u32	xSR[IMX_MU_xSR_MAX];	/* Status Registers */
> +	u32	xCR[IMX_MU_xCR_MAX];	/* Control Registers */
> +};
> +
> +#define IMX_MU_xSR_GIPn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 +
> +(3 - (x)))) #define IMX_MU_xSR_RFn(type, x) (type & IMX_MU_V2 ? BIT(x)
> +: BIT(24 + (3 - (x)))) #define IMX_MU_xSR_TEn(type, x) (type &
> +IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x))))
> +
> +/* General Purpose Interrupt Enable */
> +#define IMX_MU_xCR_GIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 +
> +(3 - (x))))
> +/* Receive Interrupt Enable */
> +#define IMX_MU_xCR_RIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 +
> +(3 - (x))))
> +/* Transmit Interrupt Enable */
> +#define IMX_MU_xCR_TIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 +
> +(3 - (x))))
> +/* General Purpose Interrupt Request */ #define IMX_MU_xCR_GIRn(type,
> +x) (type & IMX_MU_V2 ? BIT(x) : BIT(16 + (3 - (x))))
> +/* MU reset */
> +#define IMX_MU_xCR_RST(type)	(type & IMX_MU_V2 ? BIT(0) : BIT(5))
> +#define IMX_MU_xSR_RST(type)	(type & IMX_MU_V2 ? BIT(0) : BIT(7))
> +
> +static void imx_mu_write(struct imx_mu *plat, u32 val, u32 offs) {
> +	iowrite32(val, plat->base + offs);
> +}
> +
> +static u32 imx_mu_read(struct imx_mu *plat, u32 offs) {
> +	return ioread32(plat->base + offs);
> +}
> +
> +static u32 imx_mu_xcr_rmw(struct imx_mu *plat, enum imx_mu_xcr type,
> +u32 set, u32 clr) {
> +	u32 val;
> +
> +	val = imx_mu_read(plat, plat->dcfg->xCR[type]);
> +	val &= ~clr;
> +	val |= set;
> +	imx_mu_write(plat, val, plat->dcfg->xCR[type]);
> +
> +	return val;
> +}
> +
> +/* check that the channel is open or owned by caller */ static int
> +imx_mu_check_channel(struct mbox_chan *chan) {
> +	struct imx_mu *plat = dev_get_plat(chan->dev);
> +
> +	if (plat->channels[chan->id] != NULL) {
> +		/* if reserved check that caller owns */
> +		if (plat->channels[chan->id] == chan)
> +			return 1; /* caller owns the channel */
> +
> +		return -EACCES;
> +	}
> +
> +	return 0; /* channel empty */
> +}
> +
> +static int imx_mu_chan_request(struct mbox_chan *chan) {
> +	struct imx_mu *plat = dev_get_plat(chan->dev);
> +	struct imx_mu_con_priv *cp;
> +	enum imx_mu_chan_type type;
> +	int idx;
> +
> +	type = chan->id / 4;
> +	idx = chan->id % 4;
> +
> +	if (imx_mu_check_channel(chan) < 0) /* check if channel already in use */
> +		return -EPERM;
> +
> +	plat->channels[chan->id] = chan;
> +	chan->con_priv = kcalloc(1, sizeof(struct imx_mu_con_priv), 0);
> +	if (!chan->con_priv)
> +		return -ENOMEM;
> +	cp = chan->con_priv;
> +	cp->idx = idx;
> +	cp->type = type;
> +	cp->chan = chan;
> +
> +	switch (type) {
> +	case IMX_MU_TYPE_RX:
> +		imx_mu_xcr_rmw(plat, IMX_MU_RCR,
> IMX_MU_xCR_RIEn(plat->dcfg->type, idx), 0);
> +		break;
> +	case IMX_MU_TYPE_TXDB_V2:
> +	case IMX_MU_TYPE_TXDB:
> +	case IMX_MU_TYPE_RXDB:
> +		imx_mu_xcr_rmw(plat, IMX_MU_GIER,
> IMX_MU_xCR_GIEn(plat->dcfg->type, idx), 0);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int imx_mu_chan_free(struct mbox_chan *chan) {
> +	struct imx_mu *plat = dev_get_plat(chan->dev);
> +	struct imx_mu_con_priv *cp = chan->con_priv;
> +
> +	if (imx_mu_check_channel(chan) <= 0) /* check that the channel is also
> not empty */
> +		return -EINVAL;
> +
> +	/* if you own channel and  channel is NOT empty */
> +	plat->channels[chan->id] = NULL;
> +	switch (cp->type) {
> +	case IMX_MU_TYPE_TX:
> +		imx_mu_xcr_rmw(plat, IMX_MU_TCR, 0,
> IMX_MU_xCR_TIEn(plat->dcfg->type, cp->idx));
> +		break;
> +	case IMX_MU_TYPE_RX:
> +		imx_mu_xcr_rmw(plat, IMX_MU_RCR, 0,
> IMX_MU_xCR_RIEn(plat->dcfg->type, cp->idx));
> +		break;
> +	case IMX_MU_TYPE_TXDB_V2:
> +	case IMX_MU_TYPE_TXDB:
> +	case IMX_MU_TYPE_RXDB:
> +		imx_mu_xcr_rmw(plat, IMX_MU_GIER, 0,
> IMX_MU_xCR_GIEn(plat->dcfg->type, cp->idx));
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	kfree(cp);
> +
> +	return 0;
> +}
> +
> +static int imx_mu_send(struct mbox_chan *chan, const void *data) {
> +	struct imx_mu *plat = dev_get_plat(chan->dev);
> +	struct imx_mu_con_priv *cp = chan->con_priv;
> +
> +	if (imx_mu_check_channel(chan) < 1) /* return if channel isn't owned */
> +		return -EPERM;
> +
> +	return plat->dcfg->tx(plat, cp, data); }
> +
> +static int imx_mu_recv(struct mbox_chan *chan, void *data) {
> +	struct imx_mu *plat = dev_get_plat(chan->dev);
> +	struct imx_mu_con_priv *cp = chan->con_priv;
> +	u32 ctrl, val;
> +
> +	if (imx_mu_check_channel(chan) < 1) /* return if channel isn't owned */
> +		return -EPERM;
> +
> +	switch (cp->type) {
> +	case IMX_MU_TYPE_TXDB_V2:
> +	case IMX_MU_TYPE_RXDB:
> +		/* check if GSR[GIRn] bit is set */
> +		if (readx_poll_timeout(ioread32, plat->base +
> plat->dcfg->xSR[IMX_MU_GSR],
> +		    val, val & BIT(cp->idx), 1000000) < 0)
> +			return -EBUSY;
> +
> +		ctrl = imx_mu_read(plat, plat->dcfg->xCR[IMX_MU_GIER]);
> +		val = imx_mu_read(plat, plat->dcfg->xSR[IMX_MU_GSR]);
> +		val &= IMX_MU_xSR_GIPn(plat->dcfg->type, cp->idx) &
> +			(ctrl & IMX_MU_xCR_GIEn(plat->dcfg->type, cp->idx));
> +		break;
> +	default:
> +		dev_warn(chan->dev, "Unhandled channel type %d\n", cp->type);
> +		return -EOPNOTSUPP;
> +	};
> +
> +	if (val == IMX_MU_xSR_GIPn(plat->dcfg->type, cp->idx))
> +		plat->dcfg->rxdb(plat, cp);
> +
> +	return 0;
> +}
> +
> +static int imx_mu_of_to_plat(struct udevice *dev) {
> +	struct imx_mu *plat = dev_get_plat(dev);
> +	fdt_addr_t addr;
> +
> +	addr = dev_read_addr(dev);
> +	if (addr == FDT_ADDR_T_NONE)
> +		return -ENODEV;
> +
> +	plat->base = (struct mu_type *)addr;
> +
> +	return 0;
> +}
> +
> +static int imx_mu_init_generic(struct imx_mu *plat) {
> +	unsigned int i;
> +	unsigned int val;
> +
> +	if (plat->num_rr > 4 || plat->num_tr > 4) {
> +		WARN_ONCE(true, "%s not support TR/RR larger than 4\n",
> __func__);
> +		return -EOPNOTSUPP;
> +	}
> +
> +	/* Set default MU configuration */
> +	for (i = 0; i < IMX_MU_xCR_MAX; i++)
> +		imx_mu_write(plat, 0, plat->dcfg->xCR[i]);
> +
> +	/* Clear any pending GIP */
> +	val = imx_mu_read(plat, plat->dcfg->xSR[IMX_MU_GSR]);
> +	imx_mu_write(plat, val, plat->dcfg->xSR[IMX_MU_GSR]);
> +
> +	/* Clear any pending RSR */
> +	for (i = 0; i < plat->num_rr; i++)
> +		imx_mu_read(plat, plat->dcfg->xRR + i * 4);
> +
> +	return 0;
> +}
> +
> +static int imx_mu_generic_of_xlate(struct mbox_chan *chan, struct
> +ofnode_phandle_args *args) {
> +	enum imx_mu_chan_type type;
> +	int idx, cid;
> +
> +	if (args->args_count != 2) {
> +		dev_err(chan->dev, "Invalid argument count %d\n",
> args->args_count);
> +		return -EINVAL;
> +	}
> +
> +	type = args->args[0]; /* channel type */
> +	idx = args->args[1]; /* index */
> +
> +	cid = type * 4 + idx;
> +	if (cid >= IMX_MU_CHANS) {
> +		dev_err(chan->dev, "Not supported channel number: %d. (type: %d,
> idx: %d)\n",
> +			cid, type, idx);
> +		return -EINVAL;
> +	}
> +
> +	chan->id = cid;
> +
> +	return 0;
> +}
> +
> +static int imx_mu_generic_tx(struct imx_mu *plat, struct imx_mu_con_priv
> *cp,
> +			     const void *data)
> +{
> +	switch (cp->type) {
> +	case IMX_MU_TYPE_TXDB_V2:
> +		imx_mu_xcr_rmw(plat, IMX_MU_GCR,
> IMX_MU_xCR_GIRn(plat->dcfg->type, cp->idx), 0);
> +		break;
> +	default:
> +		dev_warn(cp->chan->dev, "Send data on wrong channel type: %d\n",
> cp->type);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int imx_mu_generic_rxdb(struct imx_mu *plat, struct
> +imx_mu_con_priv *cp) {
> +	imx_mu_write(plat, IMX_MU_xSR_GIPn(plat->dcfg->type, cp->idx),
> +		     plat->dcfg->xSR[IMX_MU_GSR]);
> +
> +	return 0;
> +}
> +
> +static const struct imx_mu_dcfg imx_mu_cfg_imx95 = {
> +	.tx	= imx_mu_generic_tx,
> +	.rxdb	= imx_mu_generic_rxdb,
> +	.init	= imx_mu_init_generic,
> +	.of_xlate = imx_mu_generic_of_xlate,
> +	.type	= IMX_MU_V2,
> +	.xTR	= 0x200,
> +	.xRR	= 0x280,
> +	.xSR	= {0xC, 0x118, 0x124, 0x12C},
> +	.xCR	= {0x8, 0x110, 0x114, 0x120, 0x128},
> +};
> +
> +static const struct udevice_id ids[] = {
> +	{ .compatible = "fsl,imx95-mu", .data = (ulong)&imx_mu_cfg_imx95 },
> +	{ }
> +};
> +
> +int imx_mu_of_xlate(struct mbox_chan *chan, struct ofnode_phandle_args
> +*args) {
> +	struct imx_mu *plat = dev_get_plat(chan->dev);
> +
> +	return plat->dcfg->of_xlate(chan, args); }
> +
> +struct mbox_ops imx_mu_ops = {
> +	.of_xlate = imx_mu_of_xlate,
> +	.request  = imx_mu_chan_request,
> +	.rfree    = imx_mu_chan_free,
> +	.send     = imx_mu_send,
> +	.recv     = imx_mu_recv,
> +};
> +
> +static void imx_mu_get_tr_rr(struct imx_mu *plat) {
> +	u32 val;
> +
> +	if (plat->dcfg->type & IMX_MU_V2) {
> +		val = imx_mu_read(plat, IMX_MU_V2_PAR_OFF);
> +		plat->num_tr = FIELD_GET(IMX_MU_V2_TR_MASK, val);
> +		plat->num_rr = FIELD_GET(IMX_MU_V2_RR_MASK, val);
> +	} else {
> +		plat->num_tr = 4;
> +		plat->num_rr = 4;
> +	}
> +}
> +
> +static int imx_mu_probe(struct udevice *dev) {
> +	struct imx_mu *plat = dev_get_plat(dev);
> +	int ret;
> +
> +	debug("%s(dev=%p)\n", __func__, dev);
> +
> +	plat->dcfg = (void *)dev_get_driver_data(dev);
> +
> +	imx_mu_get_tr_rr(plat);
> +
> +	ret = plat->dcfg->init(plat);
> +	if (ret) {
> +		dev_err(dev, "Failed to init MU\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +U_BOOT_DRIVER(imx_mu) = {
> +	.name = "imx-mu",
> +	.id = UCLASS_MAILBOX,
> +	.of_match = ids,
> +	.of_to_plat = imx_mu_of_to_plat,
> +	.plat_auto = sizeof(struct imx_mu),
> +	.probe = imx_mu_probe,
> +	.ops = &imx_mu_ops,
> +	.flags = DM_FLAG_PRE_RELOC,
> +};
> --
> 2.34.1



More information about the U-Boot mailing list