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