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 <firmware/imx/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