uboot/drivers/misc/imx8/scu.c
<<
>>
Prefs
   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
  18DECLARE_GLOBAL_DATA_PTR;
  19
  20struct mu_type {
  21        u32 tr[4];
  22        u32 rr[4];
  23        u32 sr;
  24        u32 cr;
  25};
  26
  27struct 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
  43static 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
  50static 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
  70static 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
  90static 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
 123static 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 */
 158static 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 *plat = dev_get_platdata(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(plat->base, tx_msg);
 170        if (ret)
 171                return ret;
 172        if (!no_resp) {
 173                ret = sc_ipc_read(plat->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
 183static int imx8_scu_probe(struct udevice *dev)
 184{
 185        struct imx8_scu *plat = dev_get_platdata(dev);
 186        fdt_addr_t addr;
 187
 188        debug("%s(dev=%p) (plat=%p)\n", __func__, dev, plat);
 189
 190        addr = devfdt_get_addr(dev);
 191        if (addr == FDT_ADDR_T_NONE)
 192                return -EINVAL;
 193
 194        plat->base = (struct mu_type *)addr;
 195
 196        /* U-Boot not enable interrupts, so need to enable RX interrupts */
 197        mu_hal_init(plat->base);
 198
 199        gd->arch.scu_dev = dev;
 200
 201        device_probe(plat->clk);
 202        device_probe(plat->pinclk);
 203
 204        return 0;
 205}
 206
 207static int imx8_scu_remove(struct udevice *dev)
 208{
 209        return 0;
 210}
 211
 212static int imx8_scu_bind(struct udevice *dev)
 213{
 214        struct imx8_scu *plat = dev_get_platdata(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, true);
 227        if (ret)
 228                return ret;
 229
 230        plat->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, true);
 238        if (ret)
 239                return ret;
 240
 241        plat->pinclk = child;
 242
 243        return 0;
 244}
 245
 246static struct misc_ops imx8_scu_ops = {
 247        .call = imx8_scu_call,
 248};
 249
 250static const struct udevice_id imx8_scu_ids[] = {
 251        { .compatible = "fsl,imx8qxp-mu" },
 252        { .compatible = "fsl,imx8-mu" },
 253        { }
 254};
 255
 256U_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        .platdata_auto_alloc_size = sizeof(struct imx8_scu),
 265        .flags          = DM_FLAG_PRE_RELOC,
 266};
 267