Message ID | 20180806025047.25320-8-peng.fan@nxp.com |
---|---|
State | Changes Requested |
Delegated to: | Stefano Babic |
Headers | show |
Series | i.MX: Add i.MX8QXP support | expand |
Hi Peng, On Mon, 6 Aug 2018 10:50:22 +0800 Peng Fan peng.fan@nxp.com wrote: > Add i.MX8 MISC driver to handle the communication between > A35 Core and SCU. Thanks for working on this! I think we should rename this driver to imx-mu and unify it, so that it can be used on i.MX7 and i.MX6SX, if needed. ... > diff --git a/drivers/misc/imx8/scu.c b/drivers/misc/imx8/scu.c > new file mode 100644 > index 0000000000..3cc6b719e3 > --- /dev/null > +++ b/drivers/misc/imx8/scu.c > @@ -0,0 +1,247 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright 2018 NXP > + * > + * Peng Fan <peng.fan@nxp.com> > + */ > + > +#include <common.h> > +#include <asm/io.h> > +#include <dm.h> > +#include <dm/lists.h> > +#include <dm/root.h> > +#include <dm/device-internal.h> > +#include <asm/arch/sci/sci.h> > +#include <linux/iopoll.h> > +#include <misc.h> > + > +DECLARE_GLOBAL_DATA_PTR; > + > +struct mu_type { > + u32 tr[4]; > + u32 rr[4]; > + u32 sr; > + u32 cr; > +}; > + > +struct imx8_scu { > + struct mu_type *base; > + struct udevice *clk; > + struct udevice *pinclk; > +}; > + > +#define MU_CR_GIE_MASK 0xF0000000u > +#define MU_CR_RIE_MASK 0xF000000u > +#define MU_CR_GIR_MASK 0xF0000u > +#define MU_CR_TIE_MASK 0xF00000u > +#define MU_CR_F_MASK 0x7u > +#define MU_SR_TE0_MASK BIT(23) > +#define MU_SR_RF0_MASK BIT(27) > +#define MU_TR_COUNT 4 > +#define MU_RR_COUNT 4 > +static inline void mu_hal_init(struct mu_type *base) Please use empty line after macro definition. ... > +static int mu_hal_receivemsg(struct mu_type *base, u32 reg_index, u32 *msg) > +{ > + u32 mask = MU_SR_RF0_MASK >> reg_index; > + u32 val; > + int ret; > + > + assert(reg_index < MU_TR_COUNT); > + > + /* Wait RX register to be full. */ > + ret = readl_poll_timeout(&base->sr, val, val & mask, 10000); > + if (ret < 0) { > + printf("%s timeout\n", __func__); > + return -ETIMEDOUT; > + } > + > + *msg = readl(&base->rr[reg_index]); > + > + return 0; > +} > + > +static void sc_ipc_read(struct mu_type *base, void *data) > +{ > + struct sc_rpc_msg_s *msg = (struct sc_rpc_msg_s *)data; > + u8 count = 0; > + > + /* Check parms */ > + if (!base || !msg) Can base ever be NULL? If the driver is bound, base is always initialized, so I think base check can be dropped. ... > + /* Read first word */ > + mu_hal_receivemsg(base, 0, (u32 *)msg); The return value is never checked. I don't know the internal details of the message protocol, but does it make sense to read more data if reading first word failed? > + count++; > + > + /* Check size */ > + if (msg->size > SC_RPC_MAX_MSG) { > + *((u32 *)msg) = 0; > + return; Should we return an error in this case to propagate it to the caller? > + } > + > + /* Read remaining words */ > + while (count < msg->size) { > + mu_hal_receivemsg(base, count % MU_RR_COUNT, > + &msg->DATA.u32[count - 1]); Return value check needed? > + count++; > + } > +} > + > +static void sc_ipc_write(struct mu_type *base, void *data) > +{ > + struct sc_rpc_msg_s *msg = (struct sc_rpc_msg_s *)data; > + u8 count = 0; > + > + /* Check parms */ > + if (!base || !msg) Drop base check? > + return; > + > + /* Check size */ > + if (msg->size > SC_RPC_MAX_MSG) > + return; > + > + /* Write first word */ > + mu_hal_sendmsg(base, 0, *((u32 *)msg)); Return value is not checked here. Does it make sense to send remaining data if transmitting of the first word failed? Wouldn't there be a protocol error at the receiver side when the message is partially received? I think we should stop sending here and return an error code to the caller. > + count++; > + > + /* Write remaining words */ > + while (count < msg->size) { > + mu_hal_sendmsg(base, count % MU_TR_COUNT, > + msg->DATA.u32[count - 1]); Return value check? > + count++; > + } > +} > + > +/* > + * Note the function prototype use msgid as the 2nd parameter, here > + * we take it as no_resp. > + */ > +static int imx8_scu_call(struct udevice *dev, int no_resp, void *tx_msg, > + int tx_size, void *rx_msg, int rx_size) > +{ > + struct imx8_scu *priv = dev_get_priv(dev); > + sc_err_t result; > + > + /* Expect tx_msg, rx_msg are the same value */ > + if (rx_msg && tx_msg != rx_msg) > + printf("tx_msg %p, rx_msg %p\n", tx_msg, rx_msg); > + > + sc_ipc_write(priv->base, tx_msg); Check and propagate error code here? > + if (!no_resp) > + sc_ipc_read(priv->base, rx_msg); > + > + result = RPC_R8((struct sc_rpc_msg_s *)tx_msg); > + > + return sc_err_to_linux(result); > +} > + > +static int imx8_scu_probe(struct udevice *dev) > +{ > + struct imx8_scu *priv = dev_get_priv(dev); > + fdt_addr_t addr; > + > + debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv); > + > + addr = devfdt_get_addr(dev); > + if (addr == FDT_ADDR_T_NONE) > + return -EINVAL; > + > + priv->base = (struct mu_type *)addr; > + > + /* U-Boot not enable interrupts, so need to enable RX interrupts */ > + mu_hal_init(priv->base); > + > + gd->arch.scu_dev = dev; Can we avoid using the dev pointer in global data? -- Anatolij
Hi Anatolij, > -----Original Message----- > From: Anatolij Gustschin [mailto:agust@denx.de] > Sent: 2018年8月10日 0:19 > To: Peng Fan <peng.fan@nxp.com>; u-boot@lists.denx.de > Cc: sbabic@denx.de; Fabio Estevam <fabio.estevam@nxp.com>; dl-linux-imx > <linux-imx@nxp.com> > Subject: Re: [U-Boot] [PATCH V3 07/32] misc: add i.MX8 misc driver > > Hi Peng, > > On Mon, 6 Aug 2018 10:50:22 +0800 > Peng Fan peng.fan@nxp.com wrote: > > > Add i.MX8 MISC driver to handle the communication between > > A35 Core and SCU. > > Thanks for working on this! I think we should rename this driver to imx-mu and > unify it, so that it can be used on i.MX7 and i.MX6SX, if needed. Thanks for reviewing the patcset. Communicating with SCU needs dedicated mu designed for SCU, and the scfw requires all the 4 channels being used. It will introduce too much complexity to develop a generic mu driver support generic mu and scu mu. I would not do that. > > ... > > diff --git a/drivers/misc/imx8/scu.c b/drivers/misc/imx8/scu.c new > > file mode 100644 index 0000000000..3cc6b719e3 > > --- /dev/null > > +++ b/drivers/misc/imx8/scu.c > > @@ -0,0 +1,247 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Copyright 2018 NXP > > + * > > + * Peng Fan <peng.fan@nxp.com> > > + */ > > + > > +#include <common.h> > > +#include <asm/io.h> > > +#include <dm.h> > > +#include <dm/lists.h> > > +#include <dm/root.h> > > +#include <dm/device-internal.h> > > +#include <asm/arch/sci/sci.h> > > +#include <linux/iopoll.h> > > +#include <misc.h> > > + > > +DECLARE_GLOBAL_DATA_PTR; > > + > > +struct mu_type { > > + u32 tr[4]; > > + u32 rr[4]; > > + u32 sr; > > + u32 cr; > > +}; > > + > > +struct imx8_scu { > > + struct mu_type *base; > > + struct udevice *clk; > > + struct udevice *pinclk; > > +}; > > + > > +#define MU_CR_GIE_MASK 0xF0000000u > > +#define MU_CR_RIE_MASK 0xF000000u > > +#define MU_CR_GIR_MASK 0xF0000u > > +#define MU_CR_TIE_MASK 0xF00000u > > +#define MU_CR_F_MASK 0x7u > > +#define MU_SR_TE0_MASK BIT(23) > > +#define MU_SR_RF0_MASK BIT(27) > > +#define MU_TR_COUNT 4 > > +#define MU_RR_COUNT 4 > > +static inline void mu_hal_init(struct mu_type *base) > > Please use empty line after macro definition. > > ... > > +static int mu_hal_receivemsg(struct mu_type *base, u32 reg_index, u32 > > +*msg) { > > + u32 mask = MU_SR_RF0_MASK >> reg_index; > > + u32 val; > > + int ret; > > + > > + assert(reg_index < MU_TR_COUNT); > > + > > + /* Wait RX register to be full. */ > > + ret = readl_poll_timeout(&base->sr, val, val & mask, 10000); > > + if (ret < 0) { > > + printf("%s timeout\n", __func__); > > + return -ETIMEDOUT; > > + } > > + > > + *msg = readl(&base->rr[reg_index]); > > + > > + return 0; > > +} > > + > > +static void sc_ipc_read(struct mu_type *base, void *data) { > > + struct sc_rpc_msg_s *msg = (struct sc_rpc_msg_s *)data; > > + u8 count = 0; > > + > > + /* Check parms */ > > + if (!base || !msg) > > Can base ever be NULL? If the driver is bound, base is always initialized, so I think > base check can be dropped. ok. Fix in V4. > > ... > > + /* Read first word */ > > + mu_hal_receivemsg(base, 0, (u32 *)msg); > > The return value is never checked. I don't know the internal details of the > message protocol, but does it make sense to read more data if reading first > word failed? Actually it should not fail, there is already a error msg in mu_hal_receivemsg. You point is also valid, Fix in V4. > > > + count++; > > + > > + /* Check size */ > > + if (msg->size > SC_RPC_MAX_MSG) { > > + *((u32 *)msg) = 0; > > + return; > > Should we return an error in this case to propagate it to the caller? Fix in V4. > > > + } > > + > > + /* Read remaining words */ > > + while (count < msg->size) { > > + mu_hal_receivemsg(base, count % MU_RR_COUNT, > > + &msg->DATA.u32[count - 1]); > > Return value check needed? Fix in V4. > > > + count++; > > + } > > +} > > + > > +static void sc_ipc_write(struct mu_type *base, void *data) { > > + struct sc_rpc_msg_s *msg = (struct sc_rpc_msg_s *)data; > > + u8 count = 0; > > + > > + /* Check parms */ > > + if (!base || !msg) > > Drop base check? Fix in V4. > > > + return; > > + > > + /* Check size */ > > + if (msg->size > SC_RPC_MAX_MSG) > > + return; > > + > > + /* Write first word */ > > + mu_hal_sendmsg(base, 0, *((u32 *)msg)); > > Return value is not checked here. Does it make sense to send remaining data if > transmitting of the first word failed? Wouldn't there be a protocol error at the > receiver side when the message is partially received? I think we should stop > sending here and return an error code to the caller. Fix in V4. > > > + count++; > > + > > + /* Write remaining words */ > > + while (count < msg->size) { > > + mu_hal_sendmsg(base, count % MU_TR_COUNT, > > + msg->DATA.u32[count - 1]); > > Return value check? > > > + count++; > > + } > > +} > > + > > +/* > > + * Note the function prototype use msgid as the 2nd parameter, here > > + * we take it as no_resp. > > + */ > > +static int imx8_scu_call(struct udevice *dev, int no_resp, void *tx_msg, > > + int tx_size, void *rx_msg, int rx_size) { > > + struct imx8_scu *priv = dev_get_priv(dev); > > + sc_err_t result; > > + > > + /* Expect tx_msg, rx_msg are the same value */ > > + if (rx_msg && tx_msg != rx_msg) > > + printf("tx_msg %p, rx_msg %p\n", tx_msg, rx_msg); > > + > > + sc_ipc_write(priv->base, tx_msg); > > Check and propagate error code here? Fix in v4. > > > + if (!no_resp) > > + sc_ipc_read(priv->base, rx_msg); > > + > > + result = RPC_R8((struct sc_rpc_msg_s *)tx_msg); > > + > > + return sc_err_to_linux(result); > > +} > > + > > +static int imx8_scu_probe(struct udevice *dev) { > > + struct imx8_scu *priv = dev_get_priv(dev); > > + fdt_addr_t addr; > > + > > + debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv); > > + > > + addr = devfdt_get_addr(dev); > > + if (addr == FDT_ADDR_T_NONE) > > + return -EINVAL; > > + > > + priv->base = (struct mu_type *)addr; > > + > > + /* U-Boot not enable interrupts, so need to enable RX interrupts */ > > + mu_hal_init(priv->base); > > + > > + gd->arch.scu_dev = dev; > > Can we avoid using the dev pointer in global data? I could not find a better solution to make other components could easily refer the scu dev. Do you have any suggestions? Do you have time to review the remaining patches in the patchset? Thanks, Peng > > -- > Anatolij
Hi Peng, On Fri, 10 Aug 2018 02:30:09 +0000 Peng Fan peng.fan@nxp.com wrote: ... > > Thanks for working on this! I think we should rename this driver to imx-mu and > > unify it, so that it can be used on i.MX7 and i.MX6SX, if needed. > > > Thanks for reviewing the patcset. Communicating with SCU needs > dedicated mu designed for SCU, and the scfw requires all the 4 channels > being used. It will introduce too much complexity to develop a generic mu > driver support generic mu and scu mu. I would not do that. OK. ... > > > + gd->arch.scu_dev = dev; > > > > Can we avoid using the dev pointer in global data? > > I could not find a better solution to make other components could > easily refer the scu dev. Do you have any suggestions? No. I think we will have to stick with the scu_dev in global data, now I see that it is needed just after relocation and before initr_dm(), so we can't get it just after relocation via dm functions. > Do you have time to review the remaining patches in the patchset? I'm working on ENET and I2C/USB/LVDS support for i.MX8QXP MEK based on this patch series and will try to review remaining patches over the weekend. Thanks, Anatolij
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 4ce9d213f0..57d34dbd9a 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_FSL_IIM) += fsl_iim.o obj-$(CONFIG_LED_STATUS_GPIO) += gpio_led.o obj-$(CONFIG_$(SPL_)I2C_EEPROM) += i2c_eeprom.o obj-$(CONFIG_FSL_MC9SDZ60) += mc9sdz60.o +obj-$(CONFIG_IMX8) += imx8/ obj-$(CONFIG_MXC_OCOTP) += mxc_ocotp.o obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o diff --git a/drivers/misc/imx8/Makefile b/drivers/misc/imx8/Makefile new file mode 100644 index 0000000000..3395340d22 --- /dev/null +++ b/drivers/misc/imx8/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-y += scu.o diff --git a/drivers/misc/imx8/scu.c b/drivers/misc/imx8/scu.c new file mode 100644 index 0000000000..3cc6b719e3 --- /dev/null +++ b/drivers/misc/imx8/scu.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include <common.h> +#include <asm/io.h> +#include <dm.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <dm/device-internal.h> +#include <asm/arch/sci/sci.h> +#include <linux/iopoll.h> +#include <misc.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct mu_type { + u32 tr[4]; + u32 rr[4]; + u32 sr; + u32 cr; +}; + +struct imx8_scu { + struct mu_type *base; + struct udevice *clk; + struct udevice *pinclk; +}; + +#define MU_CR_GIE_MASK 0xF0000000u +#define MU_CR_RIE_MASK 0xF000000u +#define MU_CR_GIR_MASK 0xF0000u +#define MU_CR_TIE_MASK 0xF00000u +#define MU_CR_F_MASK 0x7u +#define MU_SR_TE0_MASK BIT(23) +#define MU_SR_RF0_MASK BIT(27) +#define MU_TR_COUNT 4 +#define MU_RR_COUNT 4 +static inline void mu_hal_init(struct mu_type *base) +{ + /* Clear GIEn, RIEn, TIEn, GIRn and ABFn. */ + clrbits_le32(&base->cr, MU_CR_GIE_MASK | MU_CR_RIE_MASK | + MU_CR_TIE_MASK | MU_CR_GIR_MASK | MU_CR_F_MASK); +} + +static int mu_hal_sendmsg(struct mu_type *base, u32 reg_index, u32 msg) +{ + u32 mask = MU_SR_TE0_MASK >> reg_index; + u32 val; + int ret; + + assert(reg_index < MU_TR_COUNT); + + /* Wait TX register to be empty. */ + ret = readl_poll_timeout(&base->sr, val, val & mask, 10000); + if (ret < 0) { + printf("%s timeout\n", __func__); + return -ETIMEDOUT; + } + + writel(msg, &base->tr[reg_index]); + + return 0; +} + +static int mu_hal_receivemsg(struct mu_type *base, u32 reg_index, u32 *msg) +{ + u32 mask = MU_SR_RF0_MASK >> reg_index; + u32 val; + int ret; + + assert(reg_index < MU_TR_COUNT); + + /* Wait RX register to be full. */ + ret = readl_poll_timeout(&base->sr, val, val & mask, 10000); + if (ret < 0) { + printf("%s timeout\n", __func__); + return -ETIMEDOUT; + } + + *msg = readl(&base->rr[reg_index]); + + return 0; +} + +static void sc_ipc_read(struct mu_type *base, void *data) +{ + struct sc_rpc_msg_s *msg = (struct sc_rpc_msg_s *)data; + u8 count = 0; + + /* Check parms */ + if (!base || !msg) + return; + + /* Read first word */ + mu_hal_receivemsg(base, 0, (u32 *)msg); + count++; + + /* Check size */ + if (msg->size > SC_RPC_MAX_MSG) { + *((u32 *)msg) = 0; + return; + } + + /* Read remaining words */ + while (count < msg->size) { + mu_hal_receivemsg(base, count % MU_RR_COUNT, + &msg->DATA.u32[count - 1]); + count++; + } +} + +static void sc_ipc_write(struct mu_type *base, void *data) +{ + struct sc_rpc_msg_s *msg = (struct sc_rpc_msg_s *)data; + u8 count = 0; + + /* Check parms */ + if (!base || !msg) + return; + + /* Check size */ + if (msg->size > SC_RPC_MAX_MSG) + return; + + /* Write first word */ + mu_hal_sendmsg(base, 0, *((u32 *)msg)); + count++; + + /* Write remaining words */ + while (count < msg->size) { + mu_hal_sendmsg(base, count % MU_TR_COUNT, + msg->DATA.u32[count - 1]); + count++; + } +} + +/* + * Note the function prototype use msgid as the 2nd parameter, here + * we take it as no_resp. + */ +static int imx8_scu_call(struct udevice *dev, int no_resp, void *tx_msg, + int tx_size, void *rx_msg, int rx_size) +{ + struct imx8_scu *priv = dev_get_priv(dev); + sc_err_t result; + + /* Expect tx_msg, rx_msg are the same value */ + if (rx_msg && tx_msg != rx_msg) + printf("tx_msg %p, rx_msg %p\n", tx_msg, rx_msg); + + sc_ipc_write(priv->base, tx_msg); + if (!no_resp) + sc_ipc_read(priv->base, rx_msg); + + result = RPC_R8((struct sc_rpc_msg_s *)tx_msg); + + return sc_err_to_linux(result); +} + +static int imx8_scu_probe(struct udevice *dev) +{ + struct imx8_scu *priv = dev_get_priv(dev); + fdt_addr_t addr; + + debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv); + + addr = devfdt_get_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->base = (struct mu_type *)addr; + + /* U-Boot not enable interrupts, so need to enable RX interrupts */ + mu_hal_init(priv->base); + + gd->arch.scu_dev = dev; + + device_probe(priv->clk); + device_probe(priv->pinclk); + + return 0; +} + +static int imx8_scu_remove(struct udevice *dev) +{ + return 0; +} + +static int imx8_scu_bind(struct udevice *dev) +{ + struct imx8_scu *priv = dev_get_priv(dev); + int ret; + struct udevice *child; + int node; + + debug("%s(dev=%p)\n", __func__, dev); + + node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, + "fsl,imx8qxp-clk"); + if (node < 0) + panic("No clk node found\n"); + + ret = lists_bind_fdt(dev, offset_to_ofnode(node), &child); + if (ret) + return ret; + + priv->clk = child; + + node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, + "fsl,imx8qxp-iomuxc"); + if (node < 0) + panic("No clk node found\n"); + + ret = lists_bind_fdt(dev, offset_to_ofnode(node), &child); + if (ret) + return ret; + + priv->pinclk = child; + + return 0; +} + +static struct misc_ops imx8_scu_ops = { + .call = imx8_scu_call, +}; + +static const struct udevice_id imx8_scu_ids[] = { + { .compatible = "fsl,imx8qxp-mu" }, + { .compatible = "fsl,imx8-mu" }, + { } +}; + +U_BOOT_DRIVER(imx8_scu) = { + .name = "imx8_scu", + .id = UCLASS_MISC, + .of_match = imx8_scu_ids, + .probe = imx8_scu_probe, + .bind = imx8_scu_bind, + .remove = imx8_scu_remove, + .ops = &imx8_scu_ops, + .priv_auto_alloc_size = sizeof(struct imx8_scu), + .flags = DM_FLAG_PRE_RELOC, +};
Add i.MX8 MISC driver to handle the communication between A35 Core and SCU. Signed-off-by: Peng Fan <peng.fan@nxp.com> --- drivers/misc/Makefile | 1 + drivers/misc/imx8/Makefile | 3 + drivers/misc/imx8/scu.c | 247 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 251 insertions(+) create mode 100644 drivers/misc/imx8/Makefile create mode 100644 drivers/misc/imx8/scu.c