xref: /openbmc/linux/drivers/spi/spi-amlogic-spifc-a1.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1909fac05SMartin Kurbanov // SPDX-License-Identifier: GPL-2.0
2909fac05SMartin Kurbanov /*
3909fac05SMartin Kurbanov  * Driver for Amlogic A1 SPI flash controller (SPIFC)
4909fac05SMartin Kurbanov  *
5909fac05SMartin Kurbanov  * Copyright (c) 2023, SberDevices. All Rights Reserved.
6909fac05SMartin Kurbanov  *
7909fac05SMartin Kurbanov  * Author: Martin Kurbanov <mmkurbanov@sberdevices.ru>
8909fac05SMartin Kurbanov  */
9909fac05SMartin Kurbanov 
10909fac05SMartin Kurbanov #include <linux/bitfield.h>
11909fac05SMartin Kurbanov #include <linux/clk.h>
12909fac05SMartin Kurbanov #include <linux/device.h>
13909fac05SMartin Kurbanov #include <linux/io.h>
14909fac05SMartin Kurbanov #include <linux/iopoll.h>
15909fac05SMartin Kurbanov #include <linux/kernel.h>
16909fac05SMartin Kurbanov #include <linux/module.h>
17909fac05SMartin Kurbanov #include <linux/of.h>
18909fac05SMartin Kurbanov #include <linux/platform_device.h>
19909fac05SMartin Kurbanov #include <linux/pm_runtime.h>
20909fac05SMartin Kurbanov #include <linux/spi/spi.h>
21909fac05SMartin Kurbanov #include <linux/spi/spi-mem.h>
22909fac05SMartin Kurbanov #include <linux/types.h>
23909fac05SMartin Kurbanov 
24909fac05SMartin Kurbanov #define SPIFC_A1_AHB_CTRL_REG		0x0
25909fac05SMartin Kurbanov #define SPIFC_A1_AHB_BUS_EN		BIT(31)
26909fac05SMartin Kurbanov 
27909fac05SMartin Kurbanov #define SPIFC_A1_USER_CTRL0_REG		0x200
28909fac05SMartin Kurbanov #define SPIFC_A1_USER_REQUEST_ENABLE	BIT(31)
29909fac05SMartin Kurbanov #define SPIFC_A1_USER_REQUEST_FINISH	BIT(30)
30909fac05SMartin Kurbanov #define SPIFC_A1_USER_DATA_UPDATED	BIT(0)
31909fac05SMartin Kurbanov 
32909fac05SMartin Kurbanov #define SPIFC_A1_USER_CTRL1_REG		0x204
33909fac05SMartin Kurbanov #define SPIFC_A1_USER_CMD_ENABLE	BIT(30)
34909fac05SMartin Kurbanov #define SPIFC_A1_USER_CMD_MODE		GENMASK(29, 28)
35909fac05SMartin Kurbanov #define SPIFC_A1_USER_CMD_CODE		GENMASK(27, 20)
36909fac05SMartin Kurbanov #define SPIFC_A1_USER_ADDR_ENABLE	BIT(19)
37909fac05SMartin Kurbanov #define SPIFC_A1_USER_ADDR_MODE		GENMASK(18, 17)
38909fac05SMartin Kurbanov #define SPIFC_A1_USER_ADDR_BYTES	GENMASK(16, 15)
39909fac05SMartin Kurbanov #define SPIFC_A1_USER_DOUT_ENABLE	BIT(14)
40909fac05SMartin Kurbanov #define SPIFC_A1_USER_DOUT_MODE		GENMASK(11, 10)
41909fac05SMartin Kurbanov #define SPIFC_A1_USER_DOUT_BYTES	GENMASK(9, 0)
42909fac05SMartin Kurbanov 
43909fac05SMartin Kurbanov #define SPIFC_A1_USER_CTRL2_REG		0x208
44909fac05SMartin Kurbanov #define SPIFC_A1_USER_DUMMY_ENABLE	BIT(31)
45909fac05SMartin Kurbanov #define SPIFC_A1_USER_DUMMY_MODE	GENMASK(30, 29)
46909fac05SMartin Kurbanov #define SPIFC_A1_USER_DUMMY_CLK_SYCLES	GENMASK(28, 23)
47909fac05SMartin Kurbanov 
48909fac05SMartin Kurbanov #define SPIFC_A1_USER_CTRL3_REG		0x20c
49909fac05SMartin Kurbanov #define SPIFC_A1_USER_DIN_ENABLE	BIT(31)
50909fac05SMartin Kurbanov #define SPIFC_A1_USER_DIN_MODE		GENMASK(28, 27)
51909fac05SMartin Kurbanov #define SPIFC_A1_USER_DIN_BYTES		GENMASK(25, 16)
52909fac05SMartin Kurbanov 
53909fac05SMartin Kurbanov #define SPIFC_A1_USER_ADDR_REG		0x210
54909fac05SMartin Kurbanov 
55909fac05SMartin Kurbanov #define SPIFC_A1_AHB_REQ_CTRL_REG	0x214
56909fac05SMartin Kurbanov #define SPIFC_A1_AHB_REQ_ENABLE		BIT(31)
57909fac05SMartin Kurbanov 
58909fac05SMartin Kurbanov #define SPIFC_A1_ACTIMING0_REG		(0x0088 << 2)
59909fac05SMartin Kurbanov #define SPIFC_A1_TSLCH			GENMASK(31, 30)
60909fac05SMartin Kurbanov #define SPIFC_A1_TCLSH			GENMASK(29, 28)
61909fac05SMartin Kurbanov #define SPIFC_A1_TSHWL			GENMASK(20, 16)
62909fac05SMartin Kurbanov #define SPIFC_A1_TSHSL2			GENMASK(15, 12)
63909fac05SMartin Kurbanov #define SPIFC_A1_TSHSL1			GENMASK(11, 8)
64909fac05SMartin Kurbanov #define SPIFC_A1_TWHSL			GENMASK(7, 0)
65909fac05SMartin Kurbanov 
66909fac05SMartin Kurbanov #define SPIFC_A1_DBUF_CTRL_REG		0x240
67909fac05SMartin Kurbanov #define SPIFC_A1_DBUF_DIR		BIT(31)
68909fac05SMartin Kurbanov #define SPIFC_A1_DBUF_AUTO_UPDATE_ADDR	BIT(30)
69909fac05SMartin Kurbanov #define SPIFC_A1_DBUF_ADDR		GENMASK(7, 0)
70909fac05SMartin Kurbanov 
71909fac05SMartin Kurbanov #define SPIFC_A1_DBUF_DATA_REG		0x244
72909fac05SMartin Kurbanov 
73909fac05SMartin Kurbanov #define SPIFC_A1_USER_DBUF_ADDR_REG	0x248
74909fac05SMartin Kurbanov 
7568a19964SMartin Kurbanov #define SPIFC_A1_BUFFER_SIZE		512U
76909fac05SMartin Kurbanov 
77909fac05SMartin Kurbanov #define SPIFC_A1_MAX_HZ			200000000
78909fac05SMartin Kurbanov #define SPIFC_A1_MIN_HZ			1000000
79909fac05SMartin Kurbanov 
80909fac05SMartin Kurbanov #define SPIFC_A1_USER_CMD(op) ( \
81909fac05SMartin Kurbanov 	SPIFC_A1_USER_CMD_ENABLE | \
82909fac05SMartin Kurbanov 	FIELD_PREP(SPIFC_A1_USER_CMD_CODE, (op)->cmd.opcode) | \
83909fac05SMartin Kurbanov 	FIELD_PREP(SPIFC_A1_USER_CMD_MODE, ilog2((op)->cmd.buswidth)))
84909fac05SMartin Kurbanov 
85909fac05SMartin Kurbanov #define SPIFC_A1_USER_ADDR(op) ( \
86909fac05SMartin Kurbanov 	SPIFC_A1_USER_ADDR_ENABLE | \
87909fac05SMartin Kurbanov 	FIELD_PREP(SPIFC_A1_USER_ADDR_MODE, ilog2((op)->addr.buswidth)) | \
88909fac05SMartin Kurbanov 	FIELD_PREP(SPIFC_A1_USER_ADDR_BYTES, (op)->addr.nbytes - 1))
89909fac05SMartin Kurbanov 
90909fac05SMartin Kurbanov #define SPIFC_A1_USER_DUMMY(op) ( \
91909fac05SMartin Kurbanov 	SPIFC_A1_USER_DUMMY_ENABLE | \
92909fac05SMartin Kurbanov 	FIELD_PREP(SPIFC_A1_USER_DUMMY_MODE, ilog2((op)->dummy.buswidth)) | \
93909fac05SMartin Kurbanov 	FIELD_PREP(SPIFC_A1_USER_DUMMY_CLK_SYCLES, (op)->dummy.nbytes << 3))
94909fac05SMartin Kurbanov 
95909fac05SMartin Kurbanov #define SPIFC_A1_TSLCH_VAL	FIELD_PREP(SPIFC_A1_TSLCH, 1)
96909fac05SMartin Kurbanov #define SPIFC_A1_TCLSH_VAL	FIELD_PREP(SPIFC_A1_TCLSH, 1)
97909fac05SMartin Kurbanov #define SPIFC_A1_TSHWL_VAL	FIELD_PREP(SPIFC_A1_TSHWL, 7)
98909fac05SMartin Kurbanov #define SPIFC_A1_TSHSL2_VAL	FIELD_PREP(SPIFC_A1_TSHSL2, 7)
99909fac05SMartin Kurbanov #define SPIFC_A1_TSHSL1_VAL	FIELD_PREP(SPIFC_A1_TSHSL1, 7)
100909fac05SMartin Kurbanov #define SPIFC_A1_TWHSL_VAL	FIELD_PREP(SPIFC_A1_TWHSL, 2)
101909fac05SMartin Kurbanov #define SPIFC_A1_ACTIMING0_VAL	(SPIFC_A1_TSLCH_VAL | SPIFC_A1_TCLSH_VAL | \
102909fac05SMartin Kurbanov 				 SPIFC_A1_TSHWL_VAL | SPIFC_A1_TSHSL2_VAL | \
103909fac05SMartin Kurbanov 				 SPIFC_A1_TSHSL1_VAL | SPIFC_A1_TWHSL_VAL)
104909fac05SMartin Kurbanov 
105909fac05SMartin Kurbanov struct amlogic_spifc_a1 {
106909fac05SMartin Kurbanov 	struct spi_controller *ctrl;
107909fac05SMartin Kurbanov 	struct clk *clk;
108909fac05SMartin Kurbanov 	struct device *dev;
109909fac05SMartin Kurbanov 	void __iomem *base;
1108d4d4c68SMartin Kurbanov 	u32 curr_speed_hz;
111909fac05SMartin Kurbanov };
112909fac05SMartin Kurbanov 
amlogic_spifc_a1_request(struct amlogic_spifc_a1 * spifc,bool read)113909fac05SMartin Kurbanov static int amlogic_spifc_a1_request(struct amlogic_spifc_a1 *spifc, bool read)
114909fac05SMartin Kurbanov {
115909fac05SMartin Kurbanov 	u32 mask = SPIFC_A1_USER_REQUEST_FINISH |
116909fac05SMartin Kurbanov 		   (read ? SPIFC_A1_USER_DATA_UPDATED : 0);
117909fac05SMartin Kurbanov 	u32 val;
118909fac05SMartin Kurbanov 
119909fac05SMartin Kurbanov 	writel(SPIFC_A1_USER_REQUEST_ENABLE,
120909fac05SMartin Kurbanov 	       spifc->base + SPIFC_A1_USER_CTRL0_REG);
121909fac05SMartin Kurbanov 
122909fac05SMartin Kurbanov 	return readl_poll_timeout(spifc->base + SPIFC_A1_USER_CTRL0_REG,
123909fac05SMartin Kurbanov 				  val, (val & mask) == mask, 0,
124909fac05SMartin Kurbanov 				  200 * USEC_PER_MSEC);
125909fac05SMartin Kurbanov }
126909fac05SMartin Kurbanov 
amlogic_spifc_a1_drain_buffer(struct amlogic_spifc_a1 * spifc,char * buf,u32 len)127909fac05SMartin Kurbanov static void amlogic_spifc_a1_drain_buffer(struct amlogic_spifc_a1 *spifc,
128909fac05SMartin Kurbanov 					  char *buf, u32 len)
129909fac05SMartin Kurbanov {
130909fac05SMartin Kurbanov 	u32 data;
131909fac05SMartin Kurbanov 	const u32 count = len / sizeof(data);
132909fac05SMartin Kurbanov 	const u32 pad = len % sizeof(data);
133909fac05SMartin Kurbanov 
134909fac05SMartin Kurbanov 	writel(SPIFC_A1_DBUF_AUTO_UPDATE_ADDR,
135909fac05SMartin Kurbanov 	       spifc->base + SPIFC_A1_DBUF_CTRL_REG);
136909fac05SMartin Kurbanov 	ioread32_rep(spifc->base + SPIFC_A1_DBUF_DATA_REG, buf, count);
137909fac05SMartin Kurbanov 
138909fac05SMartin Kurbanov 	if (pad) {
139909fac05SMartin Kurbanov 		data = readl(spifc->base + SPIFC_A1_DBUF_DATA_REG);
140909fac05SMartin Kurbanov 		memcpy(buf + len - pad, &data, pad);
141909fac05SMartin Kurbanov 	}
142909fac05SMartin Kurbanov }
143909fac05SMartin Kurbanov 
amlogic_spifc_a1_fill_buffer(struct amlogic_spifc_a1 * spifc,const char * buf,u32 len)144909fac05SMartin Kurbanov static void amlogic_spifc_a1_fill_buffer(struct amlogic_spifc_a1 *spifc,
145909fac05SMartin Kurbanov 					 const char *buf, u32 len)
146909fac05SMartin Kurbanov {
147909fac05SMartin Kurbanov 	u32 data;
148909fac05SMartin Kurbanov 	const u32 count = len / sizeof(data);
149909fac05SMartin Kurbanov 	const u32 pad = len % sizeof(data);
150909fac05SMartin Kurbanov 
151909fac05SMartin Kurbanov 	writel(SPIFC_A1_DBUF_DIR | SPIFC_A1_DBUF_AUTO_UPDATE_ADDR,
152909fac05SMartin Kurbanov 	       spifc->base + SPIFC_A1_DBUF_CTRL_REG);
153909fac05SMartin Kurbanov 	iowrite32_rep(spifc->base + SPIFC_A1_DBUF_DATA_REG, buf, count);
154909fac05SMartin Kurbanov 
155909fac05SMartin Kurbanov 	if (pad) {
156909fac05SMartin Kurbanov 		memcpy(&data, buf + len - pad, pad);
157909fac05SMartin Kurbanov 		writel(data, spifc->base + SPIFC_A1_DBUF_DATA_REG);
158909fac05SMartin Kurbanov 	}
159909fac05SMartin Kurbanov }
160909fac05SMartin Kurbanov 
amlogic_spifc_a1_user_init(struct amlogic_spifc_a1 * spifc)161909fac05SMartin Kurbanov static void amlogic_spifc_a1_user_init(struct amlogic_spifc_a1 *spifc)
162909fac05SMartin Kurbanov {
163909fac05SMartin Kurbanov 	writel(0, spifc->base + SPIFC_A1_USER_CTRL0_REG);
164909fac05SMartin Kurbanov 	writel(0, spifc->base + SPIFC_A1_USER_CTRL1_REG);
165909fac05SMartin Kurbanov 	writel(0, spifc->base + SPIFC_A1_USER_CTRL2_REG);
166909fac05SMartin Kurbanov 	writel(0, spifc->base + SPIFC_A1_USER_CTRL3_REG);
167909fac05SMartin Kurbanov }
168909fac05SMartin Kurbanov 
amlogic_spifc_a1_set_cmd(struct amlogic_spifc_a1 * spifc,u32 cmd_cfg)169909fac05SMartin Kurbanov static void amlogic_spifc_a1_set_cmd(struct amlogic_spifc_a1 *spifc,
170909fac05SMartin Kurbanov 				     u32 cmd_cfg)
171909fac05SMartin Kurbanov {
172909fac05SMartin Kurbanov 	u32 val;
173909fac05SMartin Kurbanov 
174909fac05SMartin Kurbanov 	val = readl(spifc->base + SPIFC_A1_USER_CTRL1_REG);
175909fac05SMartin Kurbanov 	val &= ~(SPIFC_A1_USER_CMD_MODE | SPIFC_A1_USER_CMD_CODE);
176909fac05SMartin Kurbanov 	val |= cmd_cfg;
177909fac05SMartin Kurbanov 	writel(val, spifc->base + SPIFC_A1_USER_CTRL1_REG);
178909fac05SMartin Kurbanov }
179909fac05SMartin Kurbanov 
amlogic_spifc_a1_set_addr(struct amlogic_spifc_a1 * spifc,u32 addr,u32 addr_cfg)180909fac05SMartin Kurbanov static void amlogic_spifc_a1_set_addr(struct amlogic_spifc_a1 *spifc, u32 addr,
181909fac05SMartin Kurbanov 				      u32 addr_cfg)
182909fac05SMartin Kurbanov {
183909fac05SMartin Kurbanov 	u32 val;
184909fac05SMartin Kurbanov 
185909fac05SMartin Kurbanov 	writel(addr, spifc->base + SPIFC_A1_USER_ADDR_REG);
186909fac05SMartin Kurbanov 
187909fac05SMartin Kurbanov 	val = readl(spifc->base + SPIFC_A1_USER_CTRL1_REG);
188909fac05SMartin Kurbanov 	val &= ~(SPIFC_A1_USER_ADDR_MODE | SPIFC_A1_USER_ADDR_BYTES);
189909fac05SMartin Kurbanov 	val |= addr_cfg;
190909fac05SMartin Kurbanov 	writel(val, spifc->base + SPIFC_A1_USER_CTRL1_REG);
191909fac05SMartin Kurbanov }
192909fac05SMartin Kurbanov 
amlogic_spifc_a1_set_dummy(struct amlogic_spifc_a1 * spifc,u32 dummy_cfg)193909fac05SMartin Kurbanov static void amlogic_spifc_a1_set_dummy(struct amlogic_spifc_a1 *spifc,
194909fac05SMartin Kurbanov 				       u32 dummy_cfg)
195909fac05SMartin Kurbanov {
196909fac05SMartin Kurbanov 	u32 val = readl(spifc->base + SPIFC_A1_USER_CTRL2_REG);
197909fac05SMartin Kurbanov 
198909fac05SMartin Kurbanov 	val &= ~(SPIFC_A1_USER_DUMMY_MODE | SPIFC_A1_USER_DUMMY_CLK_SYCLES);
199909fac05SMartin Kurbanov 	val |= dummy_cfg;
200909fac05SMartin Kurbanov 	writel(val, spifc->base + SPIFC_A1_USER_CTRL2_REG);
201909fac05SMartin Kurbanov }
202909fac05SMartin Kurbanov 
amlogic_spifc_a1_read(struct amlogic_spifc_a1 * spifc,void * buf,u32 size,u32 mode)203909fac05SMartin Kurbanov static int amlogic_spifc_a1_read(struct amlogic_spifc_a1 *spifc, void *buf,
204909fac05SMartin Kurbanov 				 u32 size, u32 mode)
205909fac05SMartin Kurbanov {
206909fac05SMartin Kurbanov 	u32 val = readl(spifc->base + SPIFC_A1_USER_CTRL3_REG);
207909fac05SMartin Kurbanov 	int ret;
208909fac05SMartin Kurbanov 
209909fac05SMartin Kurbanov 	val &= ~(SPIFC_A1_USER_DIN_MODE | SPIFC_A1_USER_DIN_BYTES);
210909fac05SMartin Kurbanov 	val |= SPIFC_A1_USER_DIN_ENABLE;
211909fac05SMartin Kurbanov 	val |= FIELD_PREP(SPIFC_A1_USER_DIN_MODE, mode);
212909fac05SMartin Kurbanov 	val |= FIELD_PREP(SPIFC_A1_USER_DIN_BYTES, size);
213909fac05SMartin Kurbanov 	writel(val, spifc->base + SPIFC_A1_USER_CTRL3_REG);
214909fac05SMartin Kurbanov 
215909fac05SMartin Kurbanov 	ret = amlogic_spifc_a1_request(spifc, true);
216909fac05SMartin Kurbanov 	if (!ret)
217909fac05SMartin Kurbanov 		amlogic_spifc_a1_drain_buffer(spifc, buf, size);
218909fac05SMartin Kurbanov 
219909fac05SMartin Kurbanov 	return ret;
220909fac05SMartin Kurbanov }
221909fac05SMartin Kurbanov 
amlogic_spifc_a1_write(struct amlogic_spifc_a1 * spifc,const void * buf,u32 size,u32 mode)222909fac05SMartin Kurbanov static int amlogic_spifc_a1_write(struct amlogic_spifc_a1 *spifc,
223909fac05SMartin Kurbanov 				  const void *buf, u32 size, u32 mode)
224909fac05SMartin Kurbanov {
225909fac05SMartin Kurbanov 	u32 val;
226909fac05SMartin Kurbanov 
227909fac05SMartin Kurbanov 	amlogic_spifc_a1_fill_buffer(spifc, buf, size);
228909fac05SMartin Kurbanov 
229909fac05SMartin Kurbanov 	val = readl(spifc->base + SPIFC_A1_USER_CTRL1_REG);
230909fac05SMartin Kurbanov 	val &= ~(SPIFC_A1_USER_DOUT_MODE | SPIFC_A1_USER_DOUT_BYTES);
231909fac05SMartin Kurbanov 	val |= FIELD_PREP(SPIFC_A1_USER_DOUT_MODE, mode);
232909fac05SMartin Kurbanov 	val |= FIELD_PREP(SPIFC_A1_USER_DOUT_BYTES, size);
233909fac05SMartin Kurbanov 	val |= SPIFC_A1_USER_DOUT_ENABLE;
234909fac05SMartin Kurbanov 	writel(val, spifc->base + SPIFC_A1_USER_CTRL1_REG);
235909fac05SMartin Kurbanov 
236909fac05SMartin Kurbanov 	return amlogic_spifc_a1_request(spifc, false);
237909fac05SMartin Kurbanov }
238909fac05SMartin Kurbanov 
amlogic_spifc_a1_set_freq(struct amlogic_spifc_a1 * spifc,u32 freq)2398d4d4c68SMartin Kurbanov static int amlogic_spifc_a1_set_freq(struct amlogic_spifc_a1 *spifc, u32 freq)
2408d4d4c68SMartin Kurbanov {
2418d4d4c68SMartin Kurbanov 	int ret;
2428d4d4c68SMartin Kurbanov 
2438d4d4c68SMartin Kurbanov 	if (freq == spifc->curr_speed_hz)
2448d4d4c68SMartin Kurbanov 		return 0;
2458d4d4c68SMartin Kurbanov 
2468d4d4c68SMartin Kurbanov 	ret = clk_set_rate(spifc->clk, freq);
2478d4d4c68SMartin Kurbanov 	if (ret)
2488d4d4c68SMartin Kurbanov 		return ret;
2498d4d4c68SMartin Kurbanov 
2508d4d4c68SMartin Kurbanov 	spifc->curr_speed_hz = freq;
2518d4d4c68SMartin Kurbanov 	return 0;
2528d4d4c68SMartin Kurbanov }
2538d4d4c68SMartin Kurbanov 
amlogic_spifc_a1_exec_op(struct spi_mem * mem,const struct spi_mem_op * op)254909fac05SMartin Kurbanov static int amlogic_spifc_a1_exec_op(struct spi_mem *mem,
255909fac05SMartin Kurbanov 				    const struct spi_mem_op *op)
256909fac05SMartin Kurbanov {
257909fac05SMartin Kurbanov 	struct amlogic_spifc_a1 *spifc =
258909fac05SMartin Kurbanov 		spi_controller_get_devdata(mem->spi->controller);
25968a19964SMartin Kurbanov 	size_t data_size = op->data.nbytes;
260909fac05SMartin Kurbanov 	int ret;
261909fac05SMartin Kurbanov 
2628d4d4c68SMartin Kurbanov 	ret = amlogic_spifc_a1_set_freq(spifc, mem->spi->max_speed_hz);
2638d4d4c68SMartin Kurbanov 	if (ret)
2648d4d4c68SMartin Kurbanov 		return ret;
2658d4d4c68SMartin Kurbanov 
266909fac05SMartin Kurbanov 	amlogic_spifc_a1_user_init(spifc);
26768a19964SMartin Kurbanov 	amlogic_spifc_a1_set_cmd(spifc, SPIFC_A1_USER_CMD(op));
268909fac05SMartin Kurbanov 
269909fac05SMartin Kurbanov 	if (op->addr.nbytes)
27068a19964SMartin Kurbanov 		amlogic_spifc_a1_set_addr(spifc, op->addr.val,
27168a19964SMartin Kurbanov 					  SPIFC_A1_USER_ADDR(op));
272909fac05SMartin Kurbanov 
273909fac05SMartin Kurbanov 	if (op->dummy.nbytes)
27468a19964SMartin Kurbanov 		amlogic_spifc_a1_set_dummy(spifc, SPIFC_A1_USER_DUMMY(op));
27568a19964SMartin Kurbanov 
27668a19964SMartin Kurbanov 	if (data_size) {
27768a19964SMartin Kurbanov 		u32 mode = ilog2(op->data.buswidth);
278909fac05SMartin Kurbanov 
279909fac05SMartin Kurbanov 		writel(0, spifc->base + SPIFC_A1_USER_DBUF_ADDR_REG);
280909fac05SMartin Kurbanov 
281909fac05SMartin Kurbanov 		if (op->data.dir == SPI_MEM_DATA_IN)
28268a19964SMartin Kurbanov 			ret = amlogic_spifc_a1_read(spifc, op->data.buf.in,
28368a19964SMartin Kurbanov 						    data_size, mode);
284909fac05SMartin Kurbanov 		else
28568a19964SMartin Kurbanov 			ret = amlogic_spifc_a1_write(spifc, op->data.buf.out,
28668a19964SMartin Kurbanov 						     data_size, mode);
28768a19964SMartin Kurbanov 	} else {
28868a19964SMartin Kurbanov 		ret = amlogic_spifc_a1_request(spifc, false);
28968a19964SMartin Kurbanov 	}
290909fac05SMartin Kurbanov 
291909fac05SMartin Kurbanov 	return ret;
292909fac05SMartin Kurbanov }
293909fac05SMartin Kurbanov 
amlogic_spifc_a1_adjust_op_size(struct spi_mem * mem,struct spi_mem_op * op)29468a19964SMartin Kurbanov static int amlogic_spifc_a1_adjust_op_size(struct spi_mem *mem,
29568a19964SMartin Kurbanov 					   struct spi_mem_op *op)
29668a19964SMartin Kurbanov {
29768a19964SMartin Kurbanov 	op->data.nbytes = min(op->data.nbytes, SPIFC_A1_BUFFER_SIZE);
29868a19964SMartin Kurbanov 	return 0;
29968a19964SMartin Kurbanov }
30068a19964SMartin Kurbanov 
amlogic_spifc_a1_hw_init(struct amlogic_spifc_a1 * spifc)301909fac05SMartin Kurbanov static void amlogic_spifc_a1_hw_init(struct amlogic_spifc_a1 *spifc)
302909fac05SMartin Kurbanov {
303909fac05SMartin Kurbanov 	u32 regv;
304909fac05SMartin Kurbanov 
305909fac05SMartin Kurbanov 	regv = readl(spifc->base + SPIFC_A1_AHB_REQ_CTRL_REG);
306909fac05SMartin Kurbanov 	regv &= ~(SPIFC_A1_AHB_REQ_ENABLE);
307909fac05SMartin Kurbanov 	writel(regv, spifc->base + SPIFC_A1_AHB_REQ_CTRL_REG);
308909fac05SMartin Kurbanov 
309909fac05SMartin Kurbanov 	regv = readl(spifc->base + SPIFC_A1_AHB_CTRL_REG);
310909fac05SMartin Kurbanov 	regv &= ~(SPIFC_A1_AHB_BUS_EN);
311909fac05SMartin Kurbanov 	writel(regv, spifc->base + SPIFC_A1_AHB_CTRL_REG);
312909fac05SMartin Kurbanov 
313909fac05SMartin Kurbanov 	writel(SPIFC_A1_ACTIMING0_VAL, spifc->base + SPIFC_A1_ACTIMING0_REG);
314909fac05SMartin Kurbanov 
315909fac05SMartin Kurbanov 	writel(0, spifc->base + SPIFC_A1_USER_DBUF_ADDR_REG);
316909fac05SMartin Kurbanov }
317909fac05SMartin Kurbanov 
318909fac05SMartin Kurbanov static const struct spi_controller_mem_ops amlogic_spifc_a1_mem_ops = {
319909fac05SMartin Kurbanov 	.exec_op = amlogic_spifc_a1_exec_op,
32068a19964SMartin Kurbanov 	.adjust_op_size = amlogic_spifc_a1_adjust_op_size,
321909fac05SMartin Kurbanov };
322909fac05SMartin Kurbanov 
amlogic_spifc_a1_probe(struct platform_device * pdev)323909fac05SMartin Kurbanov static int amlogic_spifc_a1_probe(struct platform_device *pdev)
324909fac05SMartin Kurbanov {
325909fac05SMartin Kurbanov 	struct spi_controller *ctrl;
326909fac05SMartin Kurbanov 	struct amlogic_spifc_a1 *spifc;
327909fac05SMartin Kurbanov 	int ret;
328909fac05SMartin Kurbanov 
329*40f78b74SYang Yingliang 	ctrl = devm_spi_alloc_host(&pdev->dev, sizeof(*spifc));
330909fac05SMartin Kurbanov 	if (!ctrl)
331909fac05SMartin Kurbanov 		return -ENOMEM;
332909fac05SMartin Kurbanov 
333909fac05SMartin Kurbanov 	spifc = spi_controller_get_devdata(ctrl);
334909fac05SMartin Kurbanov 	platform_set_drvdata(pdev, spifc);
335909fac05SMartin Kurbanov 
336909fac05SMartin Kurbanov 	spifc->dev = &pdev->dev;
337909fac05SMartin Kurbanov 	spifc->ctrl = ctrl;
338909fac05SMartin Kurbanov 
339909fac05SMartin Kurbanov 	spifc->base = devm_platform_ioremap_resource(pdev, 0);
340909fac05SMartin Kurbanov 	if (IS_ERR(spifc->base))
341909fac05SMartin Kurbanov 		return PTR_ERR(spifc->base);
342909fac05SMartin Kurbanov 
343909fac05SMartin Kurbanov 	spifc->clk = devm_clk_get_enabled(spifc->dev, NULL);
344909fac05SMartin Kurbanov 	if (IS_ERR(spifc->clk))
345909fac05SMartin Kurbanov 		return dev_err_probe(spifc->dev, PTR_ERR(spifc->clk),
346909fac05SMartin Kurbanov 				     "unable to get clock\n");
347909fac05SMartin Kurbanov 
348909fac05SMartin Kurbanov 	amlogic_spifc_a1_hw_init(spifc);
349909fac05SMartin Kurbanov 
350909fac05SMartin Kurbanov 	pm_runtime_set_autosuspend_delay(spifc->dev, 500);
351909fac05SMartin Kurbanov 	pm_runtime_use_autosuspend(spifc->dev);
352909fac05SMartin Kurbanov 	devm_pm_runtime_enable(spifc->dev);
353909fac05SMartin Kurbanov 
354909fac05SMartin Kurbanov 	ctrl->num_chipselect = 1;
355909fac05SMartin Kurbanov 	ctrl->dev.of_node = pdev->dev.of_node;
356909fac05SMartin Kurbanov 	ctrl->bits_per_word_mask = SPI_BPW_MASK(8);
357909fac05SMartin Kurbanov 	ctrl->auto_runtime_pm = true;
358909fac05SMartin Kurbanov 	ctrl->mem_ops = &amlogic_spifc_a1_mem_ops;
359909fac05SMartin Kurbanov 	ctrl->min_speed_hz = SPIFC_A1_MIN_HZ;
360909fac05SMartin Kurbanov 	ctrl->max_speed_hz = SPIFC_A1_MAX_HZ;
361909fac05SMartin Kurbanov 	ctrl->mode_bits = (SPI_RX_DUAL | SPI_TX_DUAL |
362909fac05SMartin Kurbanov 			   SPI_RX_QUAD | SPI_TX_QUAD);
363909fac05SMartin Kurbanov 
364909fac05SMartin Kurbanov 	ret = devm_spi_register_controller(spifc->dev, ctrl);
365909fac05SMartin Kurbanov 	if (ret)
366909fac05SMartin Kurbanov 		return dev_err_probe(spifc->dev, ret,
367909fac05SMartin Kurbanov 				     "failed to register spi controller\n");
368909fac05SMartin Kurbanov 
369909fac05SMartin Kurbanov 	return 0;
370909fac05SMartin Kurbanov }
371909fac05SMartin Kurbanov 
372909fac05SMartin Kurbanov #ifdef CONFIG_PM_SLEEP
amlogic_spifc_a1_suspend(struct device * dev)373909fac05SMartin Kurbanov static int amlogic_spifc_a1_suspend(struct device *dev)
374909fac05SMartin Kurbanov {
375909fac05SMartin Kurbanov 	struct amlogic_spifc_a1 *spifc = dev_get_drvdata(dev);
376909fac05SMartin Kurbanov 	int ret;
377909fac05SMartin Kurbanov 
378909fac05SMartin Kurbanov 	ret = spi_controller_suspend(spifc->ctrl);
379909fac05SMartin Kurbanov 	if (ret)
380909fac05SMartin Kurbanov 		return ret;
381909fac05SMartin Kurbanov 
382909fac05SMartin Kurbanov 	if (!pm_runtime_suspended(dev))
383909fac05SMartin Kurbanov 		clk_disable_unprepare(spifc->clk);
384909fac05SMartin Kurbanov 
385909fac05SMartin Kurbanov 	return 0;
386909fac05SMartin Kurbanov }
387909fac05SMartin Kurbanov 
amlogic_spifc_a1_resume(struct device * dev)388909fac05SMartin Kurbanov static int amlogic_spifc_a1_resume(struct device *dev)
389909fac05SMartin Kurbanov {
390909fac05SMartin Kurbanov 	struct amlogic_spifc_a1 *spifc = dev_get_drvdata(dev);
391909fac05SMartin Kurbanov 	int ret = 0;
392909fac05SMartin Kurbanov 
393909fac05SMartin Kurbanov 	if (!pm_runtime_suspended(dev)) {
394909fac05SMartin Kurbanov 		ret = clk_prepare_enable(spifc->clk);
395909fac05SMartin Kurbanov 		if (ret)
396909fac05SMartin Kurbanov 			return ret;
397909fac05SMartin Kurbanov 	}
398909fac05SMartin Kurbanov 
399909fac05SMartin Kurbanov 	amlogic_spifc_a1_hw_init(spifc);
400909fac05SMartin Kurbanov 
401909fac05SMartin Kurbanov 	ret = spi_controller_resume(spifc->ctrl);
402909fac05SMartin Kurbanov 	if (ret)
403909fac05SMartin Kurbanov 		clk_disable_unprepare(spifc->clk);
404909fac05SMartin Kurbanov 
405909fac05SMartin Kurbanov 	return ret;
406909fac05SMartin Kurbanov }
407909fac05SMartin Kurbanov #endif /* CONFIG_PM_SLEEP */
408909fac05SMartin Kurbanov 
409909fac05SMartin Kurbanov #ifdef CONFIG_PM
amlogic_spifc_a1_runtime_suspend(struct device * dev)410909fac05SMartin Kurbanov static int amlogic_spifc_a1_runtime_suspend(struct device *dev)
411909fac05SMartin Kurbanov {
412909fac05SMartin Kurbanov 	struct amlogic_spifc_a1 *spifc = dev_get_drvdata(dev);
413909fac05SMartin Kurbanov 
414909fac05SMartin Kurbanov 	clk_disable_unprepare(spifc->clk);
415909fac05SMartin Kurbanov 
416909fac05SMartin Kurbanov 	return 0;
417909fac05SMartin Kurbanov }
418909fac05SMartin Kurbanov 
amlogic_spifc_a1_runtime_resume(struct device * dev)419909fac05SMartin Kurbanov static int amlogic_spifc_a1_runtime_resume(struct device *dev)
420909fac05SMartin Kurbanov {
421909fac05SMartin Kurbanov 	struct amlogic_spifc_a1 *spifc = dev_get_drvdata(dev);
422909fac05SMartin Kurbanov 	int ret;
423909fac05SMartin Kurbanov 
424909fac05SMartin Kurbanov 	ret = clk_prepare_enable(spifc->clk);
425909fac05SMartin Kurbanov 	if (!ret)
426909fac05SMartin Kurbanov 		amlogic_spifc_a1_hw_init(spifc);
427909fac05SMartin Kurbanov 
428909fac05SMartin Kurbanov 	return ret;
429909fac05SMartin Kurbanov }
430909fac05SMartin Kurbanov #endif /* CONFIG_PM */
431909fac05SMartin Kurbanov 
432909fac05SMartin Kurbanov static const struct dev_pm_ops amlogic_spifc_a1_pm_ops = {
433909fac05SMartin Kurbanov 	SET_SYSTEM_SLEEP_PM_OPS(amlogic_spifc_a1_suspend,
434909fac05SMartin Kurbanov 				amlogic_spifc_a1_resume)
435909fac05SMartin Kurbanov 	SET_RUNTIME_PM_OPS(amlogic_spifc_a1_runtime_suspend,
436909fac05SMartin Kurbanov 			   amlogic_spifc_a1_runtime_resume,
437909fac05SMartin Kurbanov 			   NULL)
438909fac05SMartin Kurbanov };
439909fac05SMartin Kurbanov 
440909fac05SMartin Kurbanov #ifdef CONFIG_OF
441909fac05SMartin Kurbanov static const struct of_device_id amlogic_spifc_a1_dt_match[] = {
442909fac05SMartin Kurbanov 	{ .compatible = "amlogic,a1-spifc", },
443909fac05SMartin Kurbanov 	{ },
444909fac05SMartin Kurbanov };
445909fac05SMartin Kurbanov MODULE_DEVICE_TABLE(of, amlogic_spifc_a1_dt_match);
446909fac05SMartin Kurbanov #endif /* CONFIG_OF */
447909fac05SMartin Kurbanov 
448909fac05SMartin Kurbanov static struct platform_driver amlogic_spifc_a1_driver = {
449909fac05SMartin Kurbanov 	.probe	= amlogic_spifc_a1_probe,
450909fac05SMartin Kurbanov 	.driver	= {
451909fac05SMartin Kurbanov 		.name		= "amlogic-spifc-a1",
452909fac05SMartin Kurbanov 		.of_match_table	= of_match_ptr(amlogic_spifc_a1_dt_match),
453909fac05SMartin Kurbanov 		.pm		= &amlogic_spifc_a1_pm_ops,
454909fac05SMartin Kurbanov 	},
455909fac05SMartin Kurbanov };
456909fac05SMartin Kurbanov module_platform_driver(amlogic_spifc_a1_driver);
457909fac05SMartin Kurbanov 
458909fac05SMartin Kurbanov MODULE_AUTHOR("Martin Kurbanov <mmkurbanov@sberdevices.ru>");
459909fac05SMartin Kurbanov MODULE_DESCRIPTION("Amlogic A1 SPIFC driver");
460909fac05SMartin Kurbanov MODULE_LICENSE("GPL");
461