xref: /openbmc/u-boot/drivers/misc/imx8/scu.c (revision 026381fc)
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 
18 DECLARE_GLOBAL_DATA_PTR;
19 
20 struct mu_type {
21 	u32 tr[4];
22 	u32 rr[4];
23 	u32 sr;
24 	u32 cr;
25 };
26 
27 struct 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 
43 static 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 
50 static 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 
70 static 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 
90 static 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 
123 static 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  */
158 static 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 
183 static 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 
207 static int imx8_scu_remove(struct udevice *dev)
208 {
209 	return 0;
210 }
211 
212 static 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 
246 static struct misc_ops imx8_scu_ops = {
247 	.call = imx8_scu_call,
248 };
249 
250 static const struct udevice_id imx8_scu_ids[] = {
251 	{ .compatible = "fsl,imx8qxp-mu" },
252 	{ .compatible = "fsl,imx8-mu" },
253 	{ }
254 };
255 
256 U_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