Commit ef64e782ece64a16d0d019c7da69682c78a04891

Authored by Peng Fan
Committed by Stefano Babic
1 parent 60b9de4f70

misc: add i.MX8 misc driver

Add i.MX8 MISC driver to handle the communication between
A35 Core and SCU.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
Reviewed-by: Anatolij Gustschin <agust@denx.de>
Cc: Stefano Babic <sbabic@denx.de>

Showing 3 changed files with 270 additions and 0 deletions Side-by-side Diff

drivers/misc/Makefile
... ... @@ -20,6 +20,7 @@
20 20 obj-$(CONFIG_LED_STATUS_GPIO) += gpio_led.o
21 21 obj-$(CONFIG_$(SPL_)I2C_EEPROM) += i2c_eeprom.o
22 22 obj-$(CONFIG_FSL_MC9SDZ60) += mc9sdz60.o
  23 +obj-$(CONFIG_IMX8) += imx8/
23 24 obj-$(CONFIG_MXC_OCOTP) += mxc_ocotp.o
24 25 obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o
25 26 obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o
drivers/misc/imx8/Makefile
  1 +# SPDX-License-Identifier: GPL-2.0+
  2 +
  3 +obj-y += scu.o
drivers/misc/imx8/scu.c
  1 +// SPDX-License-Identifier: GPL-2.0
  2 +/*
  3 + * Copyright 2018 NXP
  4 + *
  5 + * Peng Fan <peng.fan@nxp.com>
  6 + */
  7 +
  8 +#include <common.h>
  9 +#include <asm/io.h>
  10 +#include <dm.h>
  11 +#include <dm/lists.h>
  12 +#include <dm/root.h>
  13 +#include <dm/device-internal.h>
  14 +#include <asm/arch/sci/sci.h>
  15 +#include <linux/iopoll.h>
  16 +#include <misc.h>
  17 +
  18 +DECLARE_GLOBAL_DATA_PTR;
  19 +
  20 +struct mu_type {
  21 + u32 tr[4];
  22 + u32 rr[4];
  23 + u32 sr;
  24 + u32 cr;
  25 +};
  26 +
  27 +struct imx8_scu {
  28 + struct mu_type *base;
  29 + struct udevice *clk;
  30 + struct udevice *pinclk;
  31 +};
  32 +
  33 +#define MU_CR_GIE_MASK 0xF0000000u
  34 +#define MU_CR_RIE_MASK 0xF000000u
  35 +#define MU_CR_GIR_MASK 0xF0000u
  36 +#define MU_CR_TIE_MASK 0xF00000u
  37 +#define MU_CR_F_MASK 0x7u
  38 +#define MU_SR_TE0_MASK BIT(23)
  39 +#define MU_SR_RF0_MASK BIT(27)
  40 +#define MU_TR_COUNT 4
  41 +#define MU_RR_COUNT 4
  42 +
  43 +static inline void mu_hal_init(struct mu_type *base)
  44 +{
  45 + /* Clear GIEn, RIEn, TIEn, GIRn and ABFn. */
  46 + clrbits_le32(&base->cr, MU_CR_GIE_MASK | MU_CR_RIE_MASK |
  47 + MU_CR_TIE_MASK | MU_CR_GIR_MASK | MU_CR_F_MASK);
  48 +}
  49 +
  50 +static int mu_hal_sendmsg(struct mu_type *base, u32 reg_index, u32 msg)
  51 +{
  52 + u32 mask = MU_SR_TE0_MASK >> reg_index;
  53 + u32 val;
  54 + int ret;
  55 +
  56 + assert(reg_index < MU_TR_COUNT);
  57 +
  58 + /* Wait TX register to be empty. */
  59 + ret = readl_poll_timeout(&base->sr, val, val & mask, 10000);
  60 + if (ret < 0) {
  61 + printf("%s timeout\n", __func__);
  62 + return -ETIMEDOUT;
  63 + }
  64 +
  65 + writel(msg, &base->tr[reg_index]);
  66 +
  67 + return 0;
  68 +}
  69 +
  70 +static int mu_hal_receivemsg(struct mu_type *base, u32 reg_index, u32 *msg)
  71 +{
  72 + u32 mask = MU_SR_RF0_MASK >> reg_index;
  73 + u32 val;
  74 + int ret;
  75 +
  76 + assert(reg_index < MU_TR_COUNT);
  77 +
  78 + /* Wait RX register to be full. */
  79 + ret = readl_poll_timeout(&base->sr, val, val & mask, 10000);
  80 + if (ret < 0) {
  81 + printf("%s timeout\n", __func__);
  82 + return -ETIMEDOUT;
  83 + }
  84 +
  85 + *msg = readl(&base->rr[reg_index]);
  86 +
  87 + return 0;
  88 +}
  89 +
  90 +static int sc_ipc_read(struct mu_type *base, void *data)
  91 +{
  92 + struct sc_rpc_msg_s *msg = (struct sc_rpc_msg_s *)data;
  93 + int ret;
  94 + u8 count = 0;
  95 +
  96 + if (!msg)
  97 + return -EINVAL;
  98 +
  99 + /* Read first word */
  100 + ret = mu_hal_receivemsg(base, 0, (u32 *)msg);
  101 + if (ret)
  102 + return ret;
  103 + count++;
  104 +
  105 + /* Check size */
  106 + if (msg->size > SC_RPC_MAX_MSG) {
  107 + *((u32 *)msg) = 0;
  108 + return -EINVAL;
  109 + }
  110 +
  111 + /* Read remaining words */
  112 + while (count < msg->size) {
  113 + ret = mu_hal_receivemsg(base, count % MU_RR_COUNT,
  114 + &msg->DATA.u32[count - 1]);
  115 + if (ret)
  116 + return ret;
  117 + count++;
  118 + }
  119 +
  120 + return 0;
  121 +}
  122 +
  123 +static int sc_ipc_write(struct mu_type *base, void *data)
  124 +{
  125 + struct sc_rpc_msg_s *msg = (struct sc_rpc_msg_s *)data;
  126 + int ret;
  127 + u8 count = 0;
  128 +
  129 + if (!msg)
  130 + return -EINVAL;
  131 +
  132 + /* Check size */
  133 + if (msg->size > SC_RPC_MAX_MSG)
  134 + return -EINVAL;
  135 +
  136 + /* Write first word */
  137 + ret = mu_hal_sendmsg(base, 0, *((u32 *)msg));
  138 + if (ret)
  139 + return ret;
  140 + count++;
  141 +
  142 + /* Write remaining words */
  143 + while (count < msg->size) {
  144 + ret = mu_hal_sendmsg(base, count % MU_TR_COUNT,
  145 + msg->DATA.u32[count - 1]);
  146 + if (ret)
  147 + return ret;
  148 + count++;
  149 + }
  150 +
  151 + return 0;
  152 +}
  153 +
  154 +/*
  155 + * Note the function prototype use msgid as the 2nd parameter, here
  156 + * we take it as no_resp.
  157 + */
  158 +static int imx8_scu_call(struct udevice *dev, int no_resp, void *tx_msg,
  159 + int tx_size, void *rx_msg, int rx_size)
  160 +{
  161 + struct imx8_scu *priv = dev_get_priv(dev);
  162 + sc_err_t result;
  163 + int ret;
  164 +
  165 + /* Expect tx_msg, rx_msg are the same value */
  166 + if (rx_msg && tx_msg != rx_msg)
  167 + printf("tx_msg %p, rx_msg %p\n", tx_msg, rx_msg);
  168 +
  169 + ret = sc_ipc_write(priv->base, tx_msg);
  170 + if (ret)
  171 + return ret;
  172 + if (!no_resp) {
  173 + ret = sc_ipc_read(priv->base, rx_msg);
  174 + if (ret)
  175 + return ret;
  176 + }
  177 +
  178 + result = RPC_R8((struct sc_rpc_msg_s *)tx_msg);
  179 +
  180 + return sc_err_to_linux(result);
  181 +}
  182 +
  183 +static int imx8_scu_probe(struct udevice *dev)
  184 +{
  185 + struct imx8_scu *priv = dev_get_priv(dev);
  186 + fdt_addr_t addr;
  187 +
  188 + debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv);
  189 +
  190 + addr = devfdt_get_addr(dev);
  191 + if (addr == FDT_ADDR_T_NONE)
  192 + return -EINVAL;
  193 +
  194 + priv->base = (struct mu_type *)addr;
  195 +
  196 + /* U-Boot not enable interrupts, so need to enable RX interrupts */
  197 + mu_hal_init(priv->base);
  198 +
  199 + gd->arch.scu_dev = dev;
  200 +
  201 + device_probe(priv->clk);
  202 + device_probe(priv->pinclk);
  203 +
  204 + return 0;
  205 +}
  206 +
  207 +static int imx8_scu_remove(struct udevice *dev)
  208 +{
  209 + return 0;
  210 +}
  211 +
  212 +static int imx8_scu_bind(struct udevice *dev)
  213 +{
  214 + struct imx8_scu *priv = dev_get_priv(dev);
  215 + int ret;
  216 + struct udevice *child;
  217 + int node;
  218 +
  219 + debug("%s(dev=%p)\n", __func__, dev);
  220 +
  221 + node = fdt_node_offset_by_compatible(gd->fdt_blob, -1,
  222 + "fsl,imx8qxp-clk");
  223 + if (node < 0)
  224 + panic("No clk node found\n");
  225 +
  226 + ret = lists_bind_fdt(dev, offset_to_ofnode(node), &child);
  227 + if (ret)
  228 + return ret;
  229 +
  230 + priv->clk = child;
  231 +
  232 + node = fdt_node_offset_by_compatible(gd->fdt_blob, -1,
  233 + "fsl,imx8qxp-iomuxc");
  234 + if (node < 0)
  235 + panic("No iomuxc node found\n");
  236 +
  237 + ret = lists_bind_fdt(dev, offset_to_ofnode(node), &child);
  238 + if (ret)
  239 + return ret;
  240 +
  241 + priv->pinclk = child;
  242 +
  243 + return 0;
  244 +}
  245 +
  246 +static struct misc_ops imx8_scu_ops = {
  247 + .call = imx8_scu_call,
  248 +};
  249 +
  250 +static const struct udevice_id imx8_scu_ids[] = {
  251 + { .compatible = "fsl,imx8qxp-mu" },
  252 + { .compatible = "fsl,imx8-mu" },
  253 + { }
  254 +};
  255 +
  256 +U_BOOT_DRIVER(imx8_scu) = {
  257 + .name = "imx8_scu",
  258 + .id = UCLASS_MISC,
  259 + .of_match = imx8_scu_ids,
  260 + .probe = imx8_scu_probe,
  261 + .bind = imx8_scu_bind,
  262 + .remove = imx8_scu_remove,
  263 + .ops = &imx8_scu_ops,
  264 + .priv_auto_alloc_size = sizeof(struct imx8_scu),
  265 + .flags = DM_FLAG_PRE_RELOC,
  266 +};