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