xref: /linux/drivers/spmi/spmi-mtk-pmif.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
1b45b3cceSJames Lo // SPDX-License-Identifier: GPL-2.0
2b45b3cceSJames Lo //
3b45b3cceSJames Lo // Copyright (c) 2021 MediaTek Inc.
4b45b3cceSJames Lo 
5b45b3cceSJames Lo #include <linux/clk.h>
6b45b3cceSJames Lo #include <linux/iopoll.h>
7b45b3cceSJames Lo #include <linux/module.h>
8b45b3cceSJames Lo #include <linux/of.h>
9b45b3cceSJames Lo #include <linux/platform_device.h>
10b45b3cceSJames Lo #include <linux/property.h>
11b45b3cceSJames Lo #include <linux/spmi.h>
12b45b3cceSJames Lo 
13b45b3cceSJames Lo #define SWINF_IDLE	0x00
14b45b3cceSJames Lo #define SWINF_WFVLDCLR	0x06
15b45b3cceSJames Lo 
16b45b3cceSJames Lo #define GET_SWINF(x)	(((x) >> 1) & 0x7)
17b45b3cceSJames Lo 
18b45b3cceSJames Lo #define PMIF_CMD_REG_0		0
19b45b3cceSJames Lo #define PMIF_CMD_REG		1
20b45b3cceSJames Lo #define PMIF_CMD_EXT_REG	2
21b45b3cceSJames Lo #define PMIF_CMD_EXT_REG_LONG	3
22b45b3cceSJames Lo 
23b45b3cceSJames Lo #define PMIF_DELAY_US   10
24b45b3cceSJames Lo #define PMIF_TIMEOUT_US (10 * 1000)
25b45b3cceSJames Lo 
26b45b3cceSJames Lo #define PMIF_CHAN_OFFSET 0x5
27b45b3cceSJames Lo 
28b45b3cceSJames Lo #define PMIF_MAX_CLKS	3
29b45b3cceSJames Lo 
30b45b3cceSJames Lo #define SPMI_OP_ST_BUSY 1
31b45b3cceSJames Lo 
32b45b3cceSJames Lo struct ch_reg {
33b45b3cceSJames Lo 	u32 ch_sta;
34b45b3cceSJames Lo 	u32 wdata;
35b45b3cceSJames Lo 	u32 rdata;
36b45b3cceSJames Lo 	u32 ch_send;
37b45b3cceSJames Lo 	u32 ch_rdy;
38b45b3cceSJames Lo };
39b45b3cceSJames Lo 
40b45b3cceSJames Lo struct pmif_data {
41b45b3cceSJames Lo 	const u32	*regs;
42b45b3cceSJames Lo 	const u32	*spmimst_regs;
43b45b3cceSJames Lo 	u32	soc_chan;
44b45b3cceSJames Lo };
45b45b3cceSJames Lo 
46b45b3cceSJames Lo struct pmif {
47b45b3cceSJames Lo 	void __iomem	*base;
48b45b3cceSJames Lo 	void __iomem	*spmimst_base;
49b45b3cceSJames Lo 	struct ch_reg	chan;
50b45b3cceSJames Lo 	struct clk_bulk_data clks[PMIF_MAX_CLKS];
51b45b3cceSJames Lo 	size_t nclks;
52b45b3cceSJames Lo 	const struct pmif_data *data;
53b45b3cceSJames Lo };
54b45b3cceSJames Lo 
55b45b3cceSJames Lo static const char * const pmif_clock_names[] = {
56b45b3cceSJames Lo 	"pmif_sys_ck", "pmif_tmr_ck", "spmimst_clk_mux",
57b45b3cceSJames Lo };
58b45b3cceSJames Lo 
59b45b3cceSJames Lo enum pmif_regs {
60b45b3cceSJames Lo 	PMIF_INIT_DONE,
61b45b3cceSJames Lo 	PMIF_INF_EN,
62b45b3cceSJames Lo 	PMIF_ARB_EN,
63b45b3cceSJames Lo 	PMIF_CMDISSUE_EN,
64b45b3cceSJames Lo 	PMIF_TIMER_CTRL,
65b45b3cceSJames Lo 	PMIF_SPI_MODE_CTRL,
66b45b3cceSJames Lo 	PMIF_IRQ_EVENT_EN_0,
67b45b3cceSJames Lo 	PMIF_IRQ_FLAG_0,
68b45b3cceSJames Lo 	PMIF_IRQ_CLR_0,
69b45b3cceSJames Lo 	PMIF_IRQ_EVENT_EN_1,
70b45b3cceSJames Lo 	PMIF_IRQ_FLAG_1,
71b45b3cceSJames Lo 	PMIF_IRQ_CLR_1,
72b45b3cceSJames Lo 	PMIF_IRQ_EVENT_EN_2,
73b45b3cceSJames Lo 	PMIF_IRQ_FLAG_2,
74b45b3cceSJames Lo 	PMIF_IRQ_CLR_2,
75b45b3cceSJames Lo 	PMIF_IRQ_EVENT_EN_3,
76b45b3cceSJames Lo 	PMIF_IRQ_FLAG_3,
77b45b3cceSJames Lo 	PMIF_IRQ_CLR_3,
78b45b3cceSJames Lo 	PMIF_IRQ_EVENT_EN_4,
79b45b3cceSJames Lo 	PMIF_IRQ_FLAG_4,
80b45b3cceSJames Lo 	PMIF_IRQ_CLR_4,
81b45b3cceSJames Lo 	PMIF_WDT_EVENT_EN_0,
82b45b3cceSJames Lo 	PMIF_WDT_FLAG_0,
83b45b3cceSJames Lo 	PMIF_WDT_EVENT_EN_1,
84b45b3cceSJames Lo 	PMIF_WDT_FLAG_1,
85b45b3cceSJames Lo 	PMIF_SWINF_0_STA,
86b45b3cceSJames Lo 	PMIF_SWINF_0_WDATA_31_0,
87b45b3cceSJames Lo 	PMIF_SWINF_0_RDATA_31_0,
88b45b3cceSJames Lo 	PMIF_SWINF_0_ACC,
89b45b3cceSJames Lo 	PMIF_SWINF_0_VLD_CLR,
90b45b3cceSJames Lo 	PMIF_SWINF_1_STA,
91b45b3cceSJames Lo 	PMIF_SWINF_1_WDATA_31_0,
92b45b3cceSJames Lo 	PMIF_SWINF_1_RDATA_31_0,
93b45b3cceSJames Lo 	PMIF_SWINF_1_ACC,
94b45b3cceSJames Lo 	PMIF_SWINF_1_VLD_CLR,
95b45b3cceSJames Lo 	PMIF_SWINF_2_STA,
96b45b3cceSJames Lo 	PMIF_SWINF_2_WDATA_31_0,
97b45b3cceSJames Lo 	PMIF_SWINF_2_RDATA_31_0,
98b45b3cceSJames Lo 	PMIF_SWINF_2_ACC,
99b45b3cceSJames Lo 	PMIF_SWINF_2_VLD_CLR,
100b45b3cceSJames Lo 	PMIF_SWINF_3_STA,
101b45b3cceSJames Lo 	PMIF_SWINF_3_WDATA_31_0,
102b45b3cceSJames Lo 	PMIF_SWINF_3_RDATA_31_0,
103b45b3cceSJames Lo 	PMIF_SWINF_3_ACC,
104b45b3cceSJames Lo 	PMIF_SWINF_3_VLD_CLR,
105b45b3cceSJames Lo };
106b45b3cceSJames Lo 
107b45b3cceSJames Lo static const u32 mt6873_regs[] = {
108b45b3cceSJames Lo 	[PMIF_INIT_DONE] = 0x0000,
109b45b3cceSJames Lo 	[PMIF_INF_EN] = 0x0024,
110b45b3cceSJames Lo 	[PMIF_ARB_EN] = 0x0150,
111b45b3cceSJames Lo 	[PMIF_CMDISSUE_EN] = 0x03B4,
112b45b3cceSJames Lo 	[PMIF_TIMER_CTRL] = 0x03E0,
113b45b3cceSJames Lo 	[PMIF_SPI_MODE_CTRL] = 0x0400,
114b45b3cceSJames Lo 	[PMIF_IRQ_EVENT_EN_0] = 0x0418,
115b45b3cceSJames Lo 	[PMIF_IRQ_FLAG_0] = 0x0420,
116b45b3cceSJames Lo 	[PMIF_IRQ_CLR_0] = 0x0424,
117b45b3cceSJames Lo 	[PMIF_IRQ_EVENT_EN_1] = 0x0428,
118b45b3cceSJames Lo 	[PMIF_IRQ_FLAG_1] = 0x0430,
119b45b3cceSJames Lo 	[PMIF_IRQ_CLR_1] = 0x0434,
120b45b3cceSJames Lo 	[PMIF_IRQ_EVENT_EN_2] = 0x0438,
121b45b3cceSJames Lo 	[PMIF_IRQ_FLAG_2] = 0x0440,
122b45b3cceSJames Lo 	[PMIF_IRQ_CLR_2] = 0x0444,
123b45b3cceSJames Lo 	[PMIF_IRQ_EVENT_EN_3] = 0x0448,
124b45b3cceSJames Lo 	[PMIF_IRQ_FLAG_3] = 0x0450,
125b45b3cceSJames Lo 	[PMIF_IRQ_CLR_3] = 0x0454,
126b45b3cceSJames Lo 	[PMIF_IRQ_EVENT_EN_4] = 0x0458,
127b45b3cceSJames Lo 	[PMIF_IRQ_FLAG_4] = 0x0460,
128b45b3cceSJames Lo 	[PMIF_IRQ_CLR_4] = 0x0464,
129b45b3cceSJames Lo 	[PMIF_WDT_EVENT_EN_0] = 0x046C,
130b45b3cceSJames Lo 	[PMIF_WDT_FLAG_0] = 0x0470,
131b45b3cceSJames Lo 	[PMIF_WDT_EVENT_EN_1] = 0x0474,
132b45b3cceSJames Lo 	[PMIF_WDT_FLAG_1] = 0x0478,
133b45b3cceSJames Lo 	[PMIF_SWINF_0_ACC] = 0x0C00,
134b45b3cceSJames Lo 	[PMIF_SWINF_0_WDATA_31_0] = 0x0C04,
135b45b3cceSJames Lo 	[PMIF_SWINF_0_RDATA_31_0] = 0x0C14,
136b45b3cceSJames Lo 	[PMIF_SWINF_0_VLD_CLR] = 0x0C24,
137b45b3cceSJames Lo 	[PMIF_SWINF_0_STA] = 0x0C28,
138b45b3cceSJames Lo 	[PMIF_SWINF_1_ACC] = 0x0C40,
139b45b3cceSJames Lo 	[PMIF_SWINF_1_WDATA_31_0] = 0x0C44,
140b45b3cceSJames Lo 	[PMIF_SWINF_1_RDATA_31_0] = 0x0C54,
141b45b3cceSJames Lo 	[PMIF_SWINF_1_VLD_CLR] = 0x0C64,
142b45b3cceSJames Lo 	[PMIF_SWINF_1_STA] = 0x0C68,
143b45b3cceSJames Lo 	[PMIF_SWINF_2_ACC] = 0x0C80,
144b45b3cceSJames Lo 	[PMIF_SWINF_2_WDATA_31_0] = 0x0C84,
145b45b3cceSJames Lo 	[PMIF_SWINF_2_RDATA_31_0] = 0x0C94,
146b45b3cceSJames Lo 	[PMIF_SWINF_2_VLD_CLR] = 0x0CA4,
147b45b3cceSJames Lo 	[PMIF_SWINF_2_STA] = 0x0CA8,
148b45b3cceSJames Lo 	[PMIF_SWINF_3_ACC] = 0x0CC0,
149b45b3cceSJames Lo 	[PMIF_SWINF_3_WDATA_31_0] = 0x0CC4,
150b45b3cceSJames Lo 	[PMIF_SWINF_3_RDATA_31_0] = 0x0CD4,
151b45b3cceSJames Lo 	[PMIF_SWINF_3_VLD_CLR] = 0x0CE4,
152b45b3cceSJames Lo 	[PMIF_SWINF_3_STA] = 0x0CE8,
153b45b3cceSJames Lo };
154b45b3cceSJames Lo 
155504eb71eSJames Lo static const u32 mt8195_regs[] = {
156504eb71eSJames Lo 	[PMIF_INIT_DONE] = 0x0000,
157504eb71eSJames Lo 	[PMIF_INF_EN] = 0x0024,
158504eb71eSJames Lo 	[PMIF_ARB_EN] = 0x0150,
159504eb71eSJames Lo 	[PMIF_CMDISSUE_EN] = 0x03B8,
160504eb71eSJames Lo 	[PMIF_TIMER_CTRL] = 0x03E4,
161504eb71eSJames Lo 	[PMIF_SPI_MODE_CTRL] = 0x0408,
162504eb71eSJames Lo 	[PMIF_IRQ_EVENT_EN_0] = 0x0420,
163504eb71eSJames Lo 	[PMIF_IRQ_FLAG_0] = 0x0428,
164504eb71eSJames Lo 	[PMIF_IRQ_CLR_0] = 0x042C,
165504eb71eSJames Lo 	[PMIF_IRQ_EVENT_EN_1] = 0x0430,
166504eb71eSJames Lo 	[PMIF_IRQ_FLAG_1] = 0x0438,
167504eb71eSJames Lo 	[PMIF_IRQ_CLR_1] = 0x043C,
168504eb71eSJames Lo 	[PMIF_IRQ_EVENT_EN_2] = 0x0440,
169504eb71eSJames Lo 	[PMIF_IRQ_FLAG_2] = 0x0448,
170504eb71eSJames Lo 	[PMIF_IRQ_CLR_2] = 0x044C,
171504eb71eSJames Lo 	[PMIF_IRQ_EVENT_EN_3] = 0x0450,
172504eb71eSJames Lo 	[PMIF_IRQ_FLAG_3] = 0x0458,
173504eb71eSJames Lo 	[PMIF_IRQ_CLR_3] = 0x045C,
174504eb71eSJames Lo 	[PMIF_IRQ_EVENT_EN_4] = 0x0460,
175504eb71eSJames Lo 	[PMIF_IRQ_FLAG_4] = 0x0468,
176504eb71eSJames Lo 	[PMIF_IRQ_CLR_4] = 0x046C,
177504eb71eSJames Lo 	[PMIF_WDT_EVENT_EN_0] = 0x0474,
178504eb71eSJames Lo 	[PMIF_WDT_FLAG_0] = 0x0478,
179504eb71eSJames Lo 	[PMIF_WDT_EVENT_EN_1] = 0x047C,
180504eb71eSJames Lo 	[PMIF_WDT_FLAG_1] = 0x0480,
181504eb71eSJames Lo 	[PMIF_SWINF_0_ACC] = 0x0800,
182504eb71eSJames Lo 	[PMIF_SWINF_0_WDATA_31_0] = 0x0804,
183504eb71eSJames Lo 	[PMIF_SWINF_0_RDATA_31_0] = 0x0814,
184504eb71eSJames Lo 	[PMIF_SWINF_0_VLD_CLR] = 0x0824,
185504eb71eSJames Lo 	[PMIF_SWINF_0_STA] = 0x0828,
186504eb71eSJames Lo 	[PMIF_SWINF_1_ACC] = 0x0840,
187504eb71eSJames Lo 	[PMIF_SWINF_1_WDATA_31_0] = 0x0844,
188504eb71eSJames Lo 	[PMIF_SWINF_1_RDATA_31_0] = 0x0854,
189504eb71eSJames Lo 	[PMIF_SWINF_1_VLD_CLR] = 0x0864,
190504eb71eSJames Lo 	[PMIF_SWINF_1_STA] = 0x0868,
191504eb71eSJames Lo 	[PMIF_SWINF_2_ACC] = 0x0880,
192504eb71eSJames Lo 	[PMIF_SWINF_2_WDATA_31_0] = 0x0884,
193504eb71eSJames Lo 	[PMIF_SWINF_2_RDATA_31_0] = 0x0894,
194504eb71eSJames Lo 	[PMIF_SWINF_2_VLD_CLR] = 0x08A4,
195504eb71eSJames Lo 	[PMIF_SWINF_2_STA] = 0x08A8,
196504eb71eSJames Lo 	[PMIF_SWINF_3_ACC] = 0x08C0,
197504eb71eSJames Lo 	[PMIF_SWINF_3_WDATA_31_0] = 0x08C4,
198504eb71eSJames Lo 	[PMIF_SWINF_3_RDATA_31_0] = 0x08D4,
199504eb71eSJames Lo 	[PMIF_SWINF_3_VLD_CLR] = 0x08E4,
200504eb71eSJames Lo 	[PMIF_SWINF_3_STA] = 0x08E8,
201504eb71eSJames Lo };
202504eb71eSJames Lo 
203b45b3cceSJames Lo enum spmi_regs {
204b45b3cceSJames Lo 	SPMI_OP_ST_CTRL,
205b45b3cceSJames Lo 	SPMI_GRP_ID_EN,
206b45b3cceSJames Lo 	SPMI_OP_ST_STA,
207b45b3cceSJames Lo 	SPMI_MST_SAMPL,
208b45b3cceSJames Lo 	SPMI_MST_REQ_EN,
209b45b3cceSJames Lo 	SPMI_REC_CTRL,
210b45b3cceSJames Lo 	SPMI_REC0,
211b45b3cceSJames Lo 	SPMI_REC1,
212b45b3cceSJames Lo 	SPMI_REC2,
213b45b3cceSJames Lo 	SPMI_REC3,
214b45b3cceSJames Lo 	SPMI_REC4,
215b45b3cceSJames Lo 	SPMI_MST_DBG,
216504eb71eSJames Lo 
217504eb71eSJames Lo 	/* MT8195 spmi regs */
218504eb71eSJames Lo 	SPMI_MST_RCS_CTRL,
219504eb71eSJames Lo 	SPMI_SLV_3_0_EINT,
220504eb71eSJames Lo 	SPMI_SLV_7_4_EINT,
221504eb71eSJames Lo 	SPMI_SLV_B_8_EINT,
222504eb71eSJames Lo 	SPMI_SLV_F_C_EINT,
223504eb71eSJames Lo 	SPMI_REC_CMD_DEC,
224504eb71eSJames Lo 	SPMI_DEC_DBG,
225b45b3cceSJames Lo };
226b45b3cceSJames Lo 
227b45b3cceSJames Lo static const u32 mt6873_spmi_regs[] = {
228b45b3cceSJames Lo 	[SPMI_OP_ST_CTRL] = 0x0000,
229b45b3cceSJames Lo 	[SPMI_GRP_ID_EN] = 0x0004,
230b45b3cceSJames Lo 	[SPMI_OP_ST_STA] = 0x0008,
231b45b3cceSJames Lo 	[SPMI_MST_SAMPL] = 0x000c,
232b45b3cceSJames Lo 	[SPMI_MST_REQ_EN] = 0x0010,
233b45b3cceSJames Lo 	[SPMI_REC_CTRL] = 0x0040,
234b45b3cceSJames Lo 	[SPMI_REC0] = 0x0044,
235b45b3cceSJames Lo 	[SPMI_REC1] = 0x0048,
236b45b3cceSJames Lo 	[SPMI_REC2] = 0x004c,
237b45b3cceSJames Lo 	[SPMI_REC3] = 0x0050,
238b45b3cceSJames Lo 	[SPMI_REC4] = 0x0054,
239b45b3cceSJames Lo 	[SPMI_MST_DBG] = 0x00fc,
240b45b3cceSJames Lo };
241b45b3cceSJames Lo 
242504eb71eSJames Lo static const u32 mt8195_spmi_regs[] = {
243504eb71eSJames Lo 	[SPMI_OP_ST_CTRL] = 0x0000,
244504eb71eSJames Lo 	[SPMI_GRP_ID_EN] = 0x0004,
245504eb71eSJames Lo 	[SPMI_OP_ST_STA] = 0x0008,
246504eb71eSJames Lo 	[SPMI_MST_SAMPL] = 0x000C,
247504eb71eSJames Lo 	[SPMI_MST_REQ_EN] = 0x0010,
248504eb71eSJames Lo 	[SPMI_MST_RCS_CTRL] = 0x0014,
249504eb71eSJames Lo 	[SPMI_SLV_3_0_EINT] = 0x0020,
250504eb71eSJames Lo 	[SPMI_SLV_7_4_EINT] = 0x0024,
251504eb71eSJames Lo 	[SPMI_SLV_B_8_EINT] = 0x0028,
252504eb71eSJames Lo 	[SPMI_SLV_F_C_EINT] = 0x002C,
253504eb71eSJames Lo 	[SPMI_REC_CTRL] = 0x0040,
254504eb71eSJames Lo 	[SPMI_REC0] = 0x0044,
255504eb71eSJames Lo 	[SPMI_REC1] = 0x0048,
256504eb71eSJames Lo 	[SPMI_REC2] = 0x004C,
257504eb71eSJames Lo 	[SPMI_REC3] = 0x0050,
258504eb71eSJames Lo 	[SPMI_REC4] = 0x0054,
259504eb71eSJames Lo 	[SPMI_REC_CMD_DEC] = 0x005C,
260504eb71eSJames Lo 	[SPMI_DEC_DBG] = 0x00F8,
261504eb71eSJames Lo 	[SPMI_MST_DBG] = 0x00FC,
262504eb71eSJames Lo };
263504eb71eSJames Lo 
264b45b3cceSJames Lo static u32 pmif_readl(struct pmif *arb, enum pmif_regs reg)
265b45b3cceSJames Lo {
266b45b3cceSJames Lo 	return readl(arb->base + arb->data->regs[reg]);
267b45b3cceSJames Lo }
268b45b3cceSJames Lo 
269b45b3cceSJames Lo static void pmif_writel(struct pmif *arb, u32 val, enum pmif_regs reg)
270b45b3cceSJames Lo {
271b45b3cceSJames Lo 	writel(val, arb->base + arb->data->regs[reg]);
272b45b3cceSJames Lo }
273b45b3cceSJames Lo 
274b45b3cceSJames Lo static void mtk_spmi_writel(struct pmif *arb, u32 val, enum spmi_regs reg)
275b45b3cceSJames Lo {
276b45b3cceSJames Lo 	writel(val, arb->spmimst_base + arb->data->spmimst_regs[reg]);
277b45b3cceSJames Lo }
278b45b3cceSJames Lo 
279b45b3cceSJames Lo static bool pmif_is_fsm_vldclr(struct pmif *arb)
280b45b3cceSJames Lo {
281b45b3cceSJames Lo 	u32 reg_rdata;
282b45b3cceSJames Lo 
283b45b3cceSJames Lo 	reg_rdata = pmif_readl(arb, arb->chan.ch_sta);
284b45b3cceSJames Lo 
285b45b3cceSJames Lo 	return GET_SWINF(reg_rdata) == SWINF_WFVLDCLR;
286b45b3cceSJames Lo }
287b45b3cceSJames Lo 
288b45b3cceSJames Lo static int pmif_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
289b45b3cceSJames Lo {
290b45b3cceSJames Lo 	struct pmif *arb = spmi_controller_get_drvdata(ctrl);
291b45b3cceSJames Lo 	u32 rdata, cmd;
292b45b3cceSJames Lo 	int ret;
293b45b3cceSJames Lo 
294b45b3cceSJames Lo 	/* Check the opcode */
295b45b3cceSJames Lo 	if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP)
296b45b3cceSJames Lo 		return -EINVAL;
297b45b3cceSJames Lo 
298b45b3cceSJames Lo 	cmd = opc - SPMI_CMD_RESET;
299b45b3cceSJames Lo 
300b45b3cceSJames Lo 	mtk_spmi_writel(arb, (cmd << 0x4) | sid, SPMI_OP_ST_CTRL);
301b45b3cceSJames Lo 	ret = readl_poll_timeout_atomic(arb->spmimst_base + arb->data->spmimst_regs[SPMI_OP_ST_STA],
302b45b3cceSJames Lo 					rdata, (rdata & SPMI_OP_ST_BUSY) == SPMI_OP_ST_BUSY,
303b45b3cceSJames Lo 					PMIF_DELAY_US, PMIF_TIMEOUT_US);
304b45b3cceSJames Lo 	if (ret < 0)
305b45b3cceSJames Lo 		dev_err(&ctrl->dev, "timeout, err = %d\n", ret);
306b45b3cceSJames Lo 
307b45b3cceSJames Lo 	return ret;
308b45b3cceSJames Lo }
309b45b3cceSJames Lo 
310b45b3cceSJames Lo static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
311b45b3cceSJames Lo 			      u16 addr, u8 *buf, size_t len)
312b45b3cceSJames Lo {
313b45b3cceSJames Lo 	struct pmif *arb = spmi_controller_get_drvdata(ctrl);
314b45b3cceSJames Lo 	struct ch_reg *inf_reg;
315b45b3cceSJames Lo 	int ret;
316b45b3cceSJames Lo 	u32 data, cmd;
317b45b3cceSJames Lo 
318b45b3cceSJames Lo 	/* Check for argument validation. */
319b45b3cceSJames Lo 	if (sid & ~0xf) {
320b45b3cceSJames Lo 		dev_err(&ctrl->dev, "exceed the max slv id\n");
321b45b3cceSJames Lo 		return -EINVAL;
322b45b3cceSJames Lo 	}
323b45b3cceSJames Lo 
324b45b3cceSJames Lo 	if (len > 4) {
325b45b3cceSJames Lo 		dev_err(&ctrl->dev, "pmif supports 1..4 bytes per trans, but:%zu requested", len);
326b45b3cceSJames Lo 
327b45b3cceSJames Lo 		return -EINVAL;
328b45b3cceSJames Lo 	}
329b45b3cceSJames Lo 
330b45b3cceSJames Lo 	if (opc >= 0x60 && opc <= 0x7f)
331b45b3cceSJames Lo 		opc = PMIF_CMD_REG;
332b45b3cceSJames Lo 	else if ((opc >= 0x20 && opc <= 0x2f) || (opc >= 0x38 && opc <= 0x3f))
333b45b3cceSJames Lo 		opc = PMIF_CMD_EXT_REG_LONG;
334b45b3cceSJames Lo 	else
335b45b3cceSJames Lo 		return -EINVAL;
336b45b3cceSJames Lo 
337b45b3cceSJames Lo 	/* Wait for Software Interface FSM state to be IDLE. */
338b45b3cceSJames Lo 	inf_reg = &arb->chan;
339b45b3cceSJames Lo 	ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta],
340b45b3cceSJames Lo 					data, GET_SWINF(data) == SWINF_IDLE,
341b45b3cceSJames Lo 					PMIF_DELAY_US, PMIF_TIMEOUT_US);
342b45b3cceSJames Lo 	if (ret < 0) {
343b45b3cceSJames Lo 		/* set channel ready if the data has transferred */
344b45b3cceSJames Lo 		if (pmif_is_fsm_vldclr(arb))
345b45b3cceSJames Lo 			pmif_writel(arb, 1, inf_reg->ch_rdy);
346b45b3cceSJames Lo 		dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n");
347b45b3cceSJames Lo 		return ret;
348b45b3cceSJames Lo 	}
349b45b3cceSJames Lo 
350b45b3cceSJames Lo 	/* Send the command. */
351b45b3cceSJames Lo 	cmd = (opc << 30) | (sid << 24) | ((len - 1) << 16) | addr;
352b45b3cceSJames Lo 	pmif_writel(arb, cmd, inf_reg->ch_send);
353b45b3cceSJames Lo 
354b45b3cceSJames Lo 	/*
355b45b3cceSJames Lo 	 * Wait for Software Interface FSM state to be WFVLDCLR,
356b45b3cceSJames Lo 	 * read the data and clear the valid flag.
357b45b3cceSJames Lo 	 */
358b45b3cceSJames Lo 	ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta],
359b45b3cceSJames Lo 					data, GET_SWINF(data) == SWINF_WFVLDCLR,
360b45b3cceSJames Lo 					PMIF_DELAY_US, PMIF_TIMEOUT_US);
361b45b3cceSJames Lo 	if (ret < 0) {
362b45b3cceSJames Lo 		dev_err(&ctrl->dev, "failed to wait for SWINF_WFVLDCLR\n");
363b45b3cceSJames Lo 		return ret;
364b45b3cceSJames Lo 	}
365b45b3cceSJames Lo 
366b45b3cceSJames Lo 	data = pmif_readl(arb, inf_reg->rdata);
367b45b3cceSJames Lo 	memcpy(buf, &data, len);
368b45b3cceSJames Lo 	pmif_writel(arb, 1, inf_reg->ch_rdy);
369b45b3cceSJames Lo 
370b45b3cceSJames Lo 	return 0;
371b45b3cceSJames Lo }
372b45b3cceSJames Lo 
373b45b3cceSJames Lo static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
374b45b3cceSJames Lo 			       u16 addr, const u8 *buf, size_t len)
375b45b3cceSJames Lo {
376b45b3cceSJames Lo 	struct pmif *arb = spmi_controller_get_drvdata(ctrl);
377b45b3cceSJames Lo 	struct ch_reg *inf_reg;
378b45b3cceSJames Lo 	int ret;
379b45b3cceSJames Lo 	u32 data, cmd;
380b45b3cceSJames Lo 
381b45b3cceSJames Lo 	if (len > 4) {
382b45b3cceSJames Lo 		dev_err(&ctrl->dev, "pmif supports 1..4 bytes per trans, but:%zu requested", len);
383b45b3cceSJames Lo 
384b45b3cceSJames Lo 		return -EINVAL;
385b45b3cceSJames Lo 	}
386b45b3cceSJames Lo 
387b45b3cceSJames Lo 	/* Check the opcode */
388b45b3cceSJames Lo 	if (opc >= 0x40 && opc <= 0x5F)
389b45b3cceSJames Lo 		opc = PMIF_CMD_REG;
390b45b3cceSJames Lo 	else if ((opc <= 0xF) || (opc >= 0x30 && opc <= 0x37))
391b45b3cceSJames Lo 		opc = PMIF_CMD_EXT_REG_LONG;
392b45b3cceSJames Lo 	else if (opc >= 0x80)
393b45b3cceSJames Lo 		opc = PMIF_CMD_REG_0;
394b45b3cceSJames Lo 	else
395b45b3cceSJames Lo 		return -EINVAL;
396b45b3cceSJames Lo 
397b45b3cceSJames Lo 	/* Wait for Software Interface FSM state to be IDLE. */
398b45b3cceSJames Lo 	inf_reg = &arb->chan;
399b45b3cceSJames Lo 	ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta],
400b45b3cceSJames Lo 					data, GET_SWINF(data) == SWINF_IDLE,
401b45b3cceSJames Lo 					PMIF_DELAY_US, PMIF_TIMEOUT_US);
402b45b3cceSJames Lo 	if (ret < 0) {
403b45b3cceSJames Lo 		/* set channel ready if the data has transferred */
404b45b3cceSJames Lo 		if (pmif_is_fsm_vldclr(arb))
405b45b3cceSJames Lo 			pmif_writel(arb, 1, inf_reg->ch_rdy);
406b45b3cceSJames Lo 		dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n");
407b45b3cceSJames Lo 		return ret;
408b45b3cceSJames Lo 	}
409b45b3cceSJames Lo 
410b45b3cceSJames Lo 	/* Set the write data. */
411b45b3cceSJames Lo 	memcpy(&data, buf, len);
412b45b3cceSJames Lo 	pmif_writel(arb, data, inf_reg->wdata);
413b45b3cceSJames Lo 
414b45b3cceSJames Lo 	/* Send the command. */
415b45b3cceSJames Lo 	cmd = (opc << 30) | BIT(29) | (sid << 24) | ((len - 1) << 16) | addr;
416b45b3cceSJames Lo 	pmif_writel(arb, cmd, inf_reg->ch_send);
417b45b3cceSJames Lo 
418b45b3cceSJames Lo 	return 0;
419b45b3cceSJames Lo }
420b45b3cceSJames Lo 
421b45b3cceSJames Lo static const struct pmif_data mt6873_pmif_arb = {
422b45b3cceSJames Lo 	.regs = mt6873_regs,
423b45b3cceSJames Lo 	.spmimst_regs = mt6873_spmi_regs,
424b45b3cceSJames Lo 	.soc_chan = 2,
425b45b3cceSJames Lo };
426b45b3cceSJames Lo 
427504eb71eSJames Lo static const struct pmif_data mt8195_pmif_arb = {
428504eb71eSJames Lo 	.regs = mt8195_regs,
429504eb71eSJames Lo 	.spmimst_regs = mt8195_spmi_regs,
430504eb71eSJames Lo 	.soc_chan = 2,
431504eb71eSJames Lo };
432504eb71eSJames Lo 
433b45b3cceSJames Lo static int mtk_spmi_probe(struct platform_device *pdev)
434b45b3cceSJames Lo {
435b45b3cceSJames Lo 	struct pmif *arb;
436b45b3cceSJames Lo 	struct spmi_controller *ctrl;
437b45b3cceSJames Lo 	int err, i;
438b45b3cceSJames Lo 	u32 chan_offset;
439b45b3cceSJames Lo 
440b45b3cceSJames Lo 	ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*arb));
441b45b3cceSJames Lo 	if (!ctrl)
442b45b3cceSJames Lo 		return -ENOMEM;
443b45b3cceSJames Lo 
444b45b3cceSJames Lo 	arb = spmi_controller_get_drvdata(ctrl);
445b45b3cceSJames Lo 	arb->data = device_get_match_data(&pdev->dev);
446b45b3cceSJames Lo 	if (!arb->data) {
447b45b3cceSJames Lo 		err = -EINVAL;
448b45b3cceSJames Lo 		dev_err(&pdev->dev, "Cannot get drv_data\n");
449b45b3cceSJames Lo 		goto err_put_ctrl;
450b45b3cceSJames Lo 	}
451b45b3cceSJames Lo 
452b45b3cceSJames Lo 	arb->base = devm_platform_ioremap_resource_byname(pdev, "pmif");
453b45b3cceSJames Lo 	if (IS_ERR(arb->base)) {
454b45b3cceSJames Lo 		err = PTR_ERR(arb->base);
455b45b3cceSJames Lo 		goto err_put_ctrl;
456b45b3cceSJames Lo 	}
457b45b3cceSJames Lo 
458b45b3cceSJames Lo 	arb->spmimst_base = devm_platform_ioremap_resource_byname(pdev, "spmimst");
459b45b3cceSJames Lo 	if (IS_ERR(arb->spmimst_base)) {
460b45b3cceSJames Lo 		err = PTR_ERR(arb->spmimst_base);
461b45b3cceSJames Lo 		goto err_put_ctrl;
462b45b3cceSJames Lo 	}
463b45b3cceSJames Lo 
464b45b3cceSJames Lo 	arb->nclks = ARRAY_SIZE(pmif_clock_names);
465b45b3cceSJames Lo 	for (i = 0; i < arb->nclks; i++)
466b45b3cceSJames Lo 		arb->clks[i].id = pmif_clock_names[i];
467b45b3cceSJames Lo 
468b45b3cceSJames Lo 	err = devm_clk_bulk_get(&pdev->dev, arb->nclks, arb->clks);
469b45b3cceSJames Lo 	if (err) {
470b45b3cceSJames Lo 		dev_err(&pdev->dev, "Failed to get clocks: %d\n", err);
471b45b3cceSJames Lo 		goto err_put_ctrl;
472b45b3cceSJames Lo 	}
473b45b3cceSJames Lo 
474b45b3cceSJames Lo 	err = clk_bulk_prepare_enable(arb->nclks, arb->clks);
475b45b3cceSJames Lo 	if (err) {
476b45b3cceSJames Lo 		dev_err(&pdev->dev, "Failed to enable clocks: %d\n", err);
477b45b3cceSJames Lo 		goto err_put_ctrl;
478b45b3cceSJames Lo 	}
479b45b3cceSJames Lo 
480b45b3cceSJames Lo 	ctrl->cmd = pmif_arb_cmd;
481b45b3cceSJames Lo 	ctrl->read_cmd = pmif_spmi_read_cmd;
482b45b3cceSJames Lo 	ctrl->write_cmd = pmif_spmi_write_cmd;
483b45b3cceSJames Lo 
484b45b3cceSJames Lo 	chan_offset = PMIF_CHAN_OFFSET * arb->data->soc_chan;
485b45b3cceSJames Lo 	arb->chan.ch_sta = PMIF_SWINF_0_STA + chan_offset;
486b45b3cceSJames Lo 	arb->chan.wdata = PMIF_SWINF_0_WDATA_31_0 + chan_offset;
487b45b3cceSJames Lo 	arb->chan.rdata = PMIF_SWINF_0_RDATA_31_0 + chan_offset;
488b45b3cceSJames Lo 	arb->chan.ch_send = PMIF_SWINF_0_ACC + chan_offset;
489b45b3cceSJames Lo 	arb->chan.ch_rdy = PMIF_SWINF_0_VLD_CLR + chan_offset;
490b45b3cceSJames Lo 
491b45b3cceSJames Lo 	platform_set_drvdata(pdev, ctrl);
492b45b3cceSJames Lo 
493b45b3cceSJames Lo 	err = spmi_controller_add(ctrl);
494b45b3cceSJames Lo 	if (err)
495b45b3cceSJames Lo 		goto err_domain_remove;
496b45b3cceSJames Lo 
497b45b3cceSJames Lo 	return 0;
498b45b3cceSJames Lo 
499b45b3cceSJames Lo err_domain_remove:
500b45b3cceSJames Lo 	clk_bulk_disable_unprepare(arb->nclks, arb->clks);
501b45b3cceSJames Lo err_put_ctrl:
502b45b3cceSJames Lo 	spmi_controller_put(ctrl);
503b45b3cceSJames Lo 	return err;
504b45b3cceSJames Lo }
505b45b3cceSJames Lo 
506b45b3cceSJames Lo static int mtk_spmi_remove(struct platform_device *pdev)
507b45b3cceSJames Lo {
508b45b3cceSJames Lo 	struct spmi_controller *ctrl = platform_get_drvdata(pdev);
509b45b3cceSJames Lo 	struct pmif *arb = spmi_controller_get_drvdata(ctrl);
510b45b3cceSJames Lo 
511b45b3cceSJames Lo 	clk_bulk_disable_unprepare(arb->nclks, arb->clks);
512b45b3cceSJames Lo 	spmi_controller_remove(ctrl);
513b45b3cceSJames Lo 	spmi_controller_put(ctrl);
514b45b3cceSJames Lo 	return 0;
515b45b3cceSJames Lo }
516b45b3cceSJames Lo 
517b45b3cceSJames Lo static const struct of_device_id mtk_spmi_match_table[] = {
518b45b3cceSJames Lo 	{
519b45b3cceSJames Lo 		.compatible = "mediatek,mt6873-spmi",
520b45b3cceSJames Lo 		.data = &mt6873_pmif_arb,
521b45b3cceSJames Lo 	}, {
522504eb71eSJames Lo 		.compatible = "mediatek,mt8195-spmi",
523504eb71eSJames Lo 		.data = &mt8195_pmif_arb,
524504eb71eSJames Lo 	}, {
525b45b3cceSJames Lo 		/* sentinel */
526b45b3cceSJames Lo 	},
527b45b3cceSJames Lo };
528b45b3cceSJames Lo MODULE_DEVICE_TABLE(of, mtk_spmi_match_table);
529b45b3cceSJames Lo 
530b45b3cceSJames Lo static struct platform_driver mtk_spmi_driver = {
531b45b3cceSJames Lo 	.driver		= {
532b45b3cceSJames Lo 		.name	= "spmi-mtk",
533b45b3cceSJames Lo 		.of_match_table = of_match_ptr(mtk_spmi_match_table),
534b45b3cceSJames Lo 	},
535b45b3cceSJames Lo 	.probe		= mtk_spmi_probe,
536b45b3cceSJames Lo 	.remove		= mtk_spmi_remove,
537b45b3cceSJames Lo };
538b45b3cceSJames Lo module_platform_driver(mtk_spmi_driver);
539b45b3cceSJames Lo 
540b45b3cceSJames Lo MODULE_AUTHOR("Hsin-Hsiung Wang <hsin-hsiung.wang@mediatek.com>");
541b45b3cceSJames Lo MODULE_DESCRIPTION("MediaTek SPMI Driver");
542b45b3cceSJames Lo MODULE_LICENSE("GPL");
543