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