1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright 2020-2022 NXP 4 */ 5 6#include <common.h> 7#include <asm/io.h> 8#include <dm.h> 9#include <dm/lists.h> 10#include <dm/root.h> 11#include <dm/device-internal.h> 12#include <asm/mach-imx/ele_api.h> 13#include <asm/arch/imx-regs.h> 14#include <linux/iopoll.h> 15#include <misc.h> 16 17DECLARE_GLOBAL_DATA_PTR; 18 19struct imx8ulp_mu { 20 struct mu_type *base; 21}; 22 23#define MU_SR_TE0_MASK BIT(0) 24#define MU_SR_RF0_MASK BIT(0) 25#define MU_TR_COUNT 8 26#define MU_RR_COUNT 4 27 28void mu_hal_init(ulong base) 29{ 30 struct mu_type *mu_base = (struct mu_type *)base; 31 32 writel(0, &mu_base->tcr); 33 writel(0, &mu_base->rcr); 34} 35 36int mu_hal_sendmsg(ulong base, u32 reg_index, u32 msg) 37{ 38 struct mu_type *mu_base = (struct mu_type *)base; 39 u32 mask = MU_SR_TE0_MASK << reg_index; 40 u32 val; 41 int ret; 42 43 assert(reg_index < MU_TR_COUNT); 44 45 debug("sendmsg tsr 0x%x\n", readl(&mu_base->tsr)); 46 47 /* Wait TX register to be empty. */ 48 ret = readl_poll_timeout(&mu_base->tsr, val, val & mask, 10000); 49 if (ret < 0) { 50 debug("%s timeout\n", __func__); 51 return -ETIMEDOUT; 52 } 53 54 debug("tr[%d] 0x%x\n", reg_index, msg); 55 56 writel(msg, &mu_base->tr[reg_index]); 57 58 return 0; 59} 60 61int mu_hal_receivemsg(ulong base, u32 reg_index, u32 *msg) 62{ 63 struct mu_type *mu_base = (struct mu_type *)base; 64 u32 mask = MU_SR_RF0_MASK << reg_index; 65 u32 val; 66 int ret; 67 u32 count = 10; 68 69 assert(reg_index < MU_RR_COUNT); 70 71 debug("receivemsg rsr 0x%x\n", readl(&mu_base->rsr)); 72 73 do { 74 /* Wait RX register to be full. */ 75 ret = readl_poll_timeout(&mu_base->rsr, val, val & mask, 1000000); 76 if (ret < 0) { 77 count--; 78 printf("mu receive msg wait %us\n", 10 - count); 79 } else { 80 break; 81 } 82 } while (count > 0); 83 84 if (count == 0) { 85 debug("%s timeout\n", __func__); 86 return -ETIMEDOUT; 87 } 88 89 *msg = readl(&mu_base->rr[reg_index]); 90 91 debug("rr[%d] 0x%x\n", reg_index, *msg); 92 93 return 0; 94} 95 96static int imx8ulp_mu_read(struct mu_type *base, void *data) 97{ 98 struct ele_msg *msg = (struct ele_msg *)data; 99 int ret; 100 u8 count = 0; 101 102 if (!msg) 103 return -EINVAL; 104 105 /* Read first word */ 106 ret = mu_hal_receivemsg((ulong)base, 0, (u32 *)msg); 107 if (ret) 108 return ret; 109 count++; 110 111 /* Check size */ 112 if (msg->size > ELE_MAX_MSG) { 113 *((u32 *)msg) = 0; 114 return -EINVAL; 115 } 116 117 /* Read remaining words */ 118 while (count < msg->size) { 119 ret = mu_hal_receivemsg((ulong)base, count % MU_RR_COUNT, 120 &msg->data[count - 1]); 121 if (ret) 122 return ret; 123 count++; 124 } 125 126 return 0; 127} 128 129static int imx8ulp_mu_write(struct mu_type *base, void *data) 130{ 131 struct ele_msg *msg = (struct ele_msg *)data; 132 int ret; 133 u8 count = 0; 134 135 if (!msg) 136 return -EINVAL; 137 138 /* Check size */ 139 if (msg->size > ELE_MAX_MSG) 140 return -EINVAL; 141 142 /* Write first word */ 143 ret = mu_hal_sendmsg((ulong)base, 0, *((u32 *)msg)); 144 if (ret) 145 return ret; 146 count++; 147 148 /* Write remaining words */ 149 while (count < msg->size) { 150 ret = mu_hal_sendmsg((ulong)base, count % MU_TR_COUNT, 151 msg->data[count - 1]); 152 if (ret) 153 return ret; 154 count++; 155 } 156 157 return 0; 158} 159 160/* 161 * Note the function prototype use msgid as the 2nd parameter, here 162 * we take it as no_resp. 163 */ 164static int imx8ulp_mu_call(struct udevice *dev, int no_resp, void *tx_msg, 165 int tx_size, void *rx_msg, int rx_size) 166{ 167 struct imx8ulp_mu *priv = dev_get_priv(dev); 168 u32 result; 169 int ret; 170 171 /* Expect tx_msg, rx_msg are the same value */ 172 if (rx_msg && tx_msg != rx_msg) 173 printf("tx_msg %p, rx_msg %p\n", tx_msg, rx_msg); 174 175 ret = imx8ulp_mu_write(priv->base, tx_msg); 176 if (ret) 177 return ret; 178 if (!no_resp) { 179 ret = imx8ulp_mu_read(priv->base, rx_msg); 180 if (ret) 181 return ret; 182 } 183 184 result = ((struct ele_msg *)rx_msg)->data[0]; 185 if ((result & 0xff) == 0xd6) 186 return 0; 187 188 return -EIO; 189} 190 191static int imx8ulp_mu_probe(struct udevice *dev) 192{ 193 struct imx8ulp_mu *priv = dev_get_priv(dev); 194 fdt_addr_t addr; 195 196 debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv); 197 198 addr = devfdt_get_addr(dev); 199 if (addr == FDT_ADDR_T_NONE) 200 return -EINVAL; 201 202 priv->base = (struct mu_type *)addr; 203 204 debug("mu base 0x%lx\n", (ulong)priv->base); 205 206 /* U-Boot not enable interrupts, so need to enable RX interrupts */ 207 mu_hal_init((ulong)priv->base); 208 209 gd->arch.ele_dev = dev; 210 211 return 0; 212} 213 214static int imx8ulp_mu_remove(struct udevice *dev) 215{ 216 return 0; 217} 218 219static int imx8ulp_mu_bind(struct udevice *dev) 220{ 221 debug("%s(dev=%p)\n", __func__, dev); 222 223 return 0; 224} 225 226static struct misc_ops imx8ulp_mu_ops = { 227 .call = imx8ulp_mu_call, 228}; 229 230static const struct udevice_id imx8ulp_mu_ids[] = { 231 { .compatible = "fsl,imx8ulp-mu" }, 232 { .compatible = "fsl,imx93-mu-s4" }, 233 { } 234}; 235 236U_BOOT_DRIVER(imx8ulp_mu) = { 237 .name = "imx8ulp_mu", 238 .id = UCLASS_MISC, 239 .of_match = imx8ulp_mu_ids, 240 .probe = imx8ulp_mu_probe, 241 .bind = imx8ulp_mu_bind, 242 .remove = imx8ulp_mu_remove, 243 .ops = &imx8ulp_mu_ops, 244 .priv_auto = sizeof(struct imx8ulp_mu), 245}; 246