xref: /linux/drivers/spi/spi-sg2044-nor.c (revision 1260ed77798502de9c98020040d2995008de10cc)
1de16c322SLongbin Li // SPDX-License-Identifier: GPL-2.0-only
2de16c322SLongbin Li /*
3de16c322SLongbin Li  * SG2044 SPI NOR controller driver
4de16c322SLongbin Li  *
5de16c322SLongbin Li  * Copyright (c) 2025 Longbin Li <looong.bin@gmail.com>
6de16c322SLongbin Li  */
7de16c322SLongbin Li 
8de16c322SLongbin Li #include <linux/bitfield.h>
9de16c322SLongbin Li #include <linux/clk.h>
10de16c322SLongbin Li #include <linux/iopoll.h>
11de16c322SLongbin Li #include <linux/module.h>
12de16c322SLongbin Li #include <linux/of.h>
13de16c322SLongbin Li #include <linux/platform_device.h>
14de16c322SLongbin Li #include <linux/spi/spi-mem.h>
15de16c322SLongbin Li 
16de16c322SLongbin Li /* Hardware register definitions */
17de16c322SLongbin Li #define SPIFMC_CTRL				0x00
18de16c322SLongbin Li #define SPIFMC_CTRL_CPHA			BIT(12)
19de16c322SLongbin Li #define SPIFMC_CTRL_CPOL			BIT(13)
20de16c322SLongbin Li #define SPIFMC_CTRL_HOLD_OL			BIT(14)
21de16c322SLongbin Li #define SPIFMC_CTRL_WP_OL			BIT(15)
22de16c322SLongbin Li #define SPIFMC_CTRL_LSBF			BIT(20)
23de16c322SLongbin Li #define SPIFMC_CTRL_SRST			BIT(21)
24de16c322SLongbin Li #define SPIFMC_CTRL_SCK_DIV_SHIFT		0
25de16c322SLongbin Li #define SPIFMC_CTRL_FRAME_LEN_SHIFT		16
26de16c322SLongbin Li #define SPIFMC_CTRL_SCK_DIV_MASK		0x7FF
27de16c322SLongbin Li 
28de16c322SLongbin Li #define SPIFMC_CE_CTRL				0x04
29de16c322SLongbin Li #define SPIFMC_CE_CTRL_CEMANUAL			BIT(0)
30de16c322SLongbin Li #define SPIFMC_CE_CTRL_CEMANUAL_EN		BIT(1)
31de16c322SLongbin Li 
32de16c322SLongbin Li #define SPIFMC_DLY_CTRL				0x08
33de16c322SLongbin Li #define SPIFMC_CTRL_FM_INTVL_MASK		0x000f
34de16c322SLongbin Li #define SPIFMC_CTRL_FM_INTVL			BIT(0)
35de16c322SLongbin Li #define SPIFMC_CTRL_CET_MASK			0x0f00
36de16c322SLongbin Li #define SPIFMC_CTRL_CET				BIT(8)
37de16c322SLongbin Li 
38de16c322SLongbin Li #define SPIFMC_DMMR				0x0c
39de16c322SLongbin Li 
40de16c322SLongbin Li #define SPIFMC_TRAN_CSR				0x10
41de16c322SLongbin Li #define SPIFMC_TRAN_CSR_TRAN_MODE_MASK		GENMASK(1, 0)
42de16c322SLongbin Li #define SPIFMC_TRAN_CSR_TRAN_MODE_RX		BIT(0)
43de16c322SLongbin Li #define SPIFMC_TRAN_CSR_TRAN_MODE_TX		BIT(1)
44de16c322SLongbin Li #define SPIFMC_TRAN_CSR_FAST_MODE		BIT(3)
45de16c322SLongbin Li #define SPIFMC_TRAN_CSR_BUS_WIDTH_1_BIT		(0x00 << 4)
46de16c322SLongbin Li #define SPIFMC_TRAN_CSR_BUS_WIDTH_2_BIT		(0x01 << 4)
47de16c322SLongbin Li #define SPIFMC_TRAN_CSR_BUS_WIDTH_4_BIT		(0x02 << 4)
48de16c322SLongbin Li #define SPIFMC_TRAN_CSR_DMA_EN			BIT(6)
49de16c322SLongbin Li #define SPIFMC_TRAN_CSR_MISO_LEVEL		BIT(7)
50de16c322SLongbin Li #define SPIFMC_TRAN_CSR_ADDR_BYTES_MASK		GENMASK(10, 8)
51de16c322SLongbin Li #define SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT	8
52de16c322SLongbin Li #define SPIFMC_TRAN_CSR_WITH_CMD		BIT(11)
53de16c322SLongbin Li #define SPIFMC_TRAN_CSR_FIFO_TRG_LVL_MASK	GENMASK(13, 12)
54de16c322SLongbin Li #define SPIFMC_TRAN_CSR_FIFO_TRG_LVL_1_BYTE	(0x00 << 12)
55de16c322SLongbin Li #define SPIFMC_TRAN_CSR_FIFO_TRG_LVL_2_BYTE	(0x01 << 12)
56de16c322SLongbin Li #define SPIFMC_TRAN_CSR_FIFO_TRG_LVL_4_BYTE	(0x02 << 12)
57de16c322SLongbin Li #define SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE	(0x03 << 12)
58de16c322SLongbin Li #define SPIFMC_TRAN_CSR_GO_BUSY			BIT(15)
59de16c322SLongbin Li #define SPIFMC_TRAN_CSR_ADDR4B_SHIFT		20
60de16c322SLongbin Li #define SPIFMC_TRAN_CSR_CMD4B_SHIFT		21
61de16c322SLongbin Li 
62de16c322SLongbin Li #define SPIFMC_TRAN_NUM				0x14
63de16c322SLongbin Li #define SPIFMC_FIFO_PORT			0x18
64de16c322SLongbin Li #define SPIFMC_FIFO_PT				0x20
65de16c322SLongbin Li 
66de16c322SLongbin Li #define SPIFMC_INT_STS				0x28
67de16c322SLongbin Li #define SPIFMC_INT_TRAN_DONE			BIT(0)
68de16c322SLongbin Li #define SPIFMC_INT_RD_FIFO			BIT(2)
69de16c322SLongbin Li #define SPIFMC_INT_WR_FIFO			BIT(3)
70de16c322SLongbin Li #define SPIFMC_INT_RX_FRAME			BIT(4)
71de16c322SLongbin Li #define SPIFMC_INT_TX_FRAME			BIT(5)
72de16c322SLongbin Li 
73de16c322SLongbin Li #define SPIFMC_INT_EN				0x2c
74de16c322SLongbin Li #define SPIFMC_INT_TRAN_DONE_EN			BIT(0)
75de16c322SLongbin Li #define SPIFMC_INT_RD_FIFO_EN			BIT(2)
76de16c322SLongbin Li #define SPIFMC_INT_WR_FIFO_EN			BIT(3)
77de16c322SLongbin Li #define SPIFMC_INT_RX_FRAME_EN			BIT(4)
78de16c322SLongbin Li #define SPIFMC_INT_TX_FRAME_EN			BIT(5)
79de16c322SLongbin Li 
80de16c322SLongbin Li #define SPIFMC_OPT				0x030
81de16c322SLongbin Li #define SPIFMC_OPT_DISABLE_FIFO_FLUSH		BIT(1)
82de16c322SLongbin Li 
83de16c322SLongbin Li #define SPIFMC_MAX_FIFO_DEPTH			8
84de16c322SLongbin Li 
85de16c322SLongbin Li #define SPIFMC_MAX_READ_SIZE			0x10000
86de16c322SLongbin Li 
87de16c322SLongbin Li struct sg2044_spifmc {
88de16c322SLongbin Li 	struct spi_controller *ctrl;
89de16c322SLongbin Li 	void __iomem *io_base;
90de16c322SLongbin Li 	struct device *dev;
91de16c322SLongbin Li 	struct mutex lock;
92de16c322SLongbin Li 	struct clk *clk;
93de16c322SLongbin Li };
94de16c322SLongbin Li 
95de16c322SLongbin Li static int sg2044_spifmc_wait_int(struct sg2044_spifmc *spifmc, u8 int_type)
96de16c322SLongbin Li {
97de16c322SLongbin Li 	u32 stat;
98de16c322SLongbin Li 
99de16c322SLongbin Li 	return readl_poll_timeout(spifmc->io_base + SPIFMC_INT_STS, stat,
100de16c322SLongbin Li 				  (stat & int_type), 0, 1000000);
101de16c322SLongbin Li }
102de16c322SLongbin Li 
103de16c322SLongbin Li static int sg2044_spifmc_wait_xfer_size(struct sg2044_spifmc *spifmc,
104de16c322SLongbin Li 					int xfer_size)
105de16c322SLongbin Li {
106de16c322SLongbin Li 	u8 stat;
107de16c322SLongbin Li 
108de16c322SLongbin Li 	return readl_poll_timeout(spifmc->io_base + SPIFMC_FIFO_PT, stat,
109de16c322SLongbin Li 				  ((stat & 0xf) == xfer_size), 1, 1000000);
110de16c322SLongbin Li }
111de16c322SLongbin Li 
112de16c322SLongbin Li static u32 sg2044_spifmc_init_reg(struct sg2044_spifmc *spifmc)
113de16c322SLongbin Li {
114de16c322SLongbin Li 	u32 reg;
115de16c322SLongbin Li 
116de16c322SLongbin Li 	reg = readl(spifmc->io_base + SPIFMC_TRAN_CSR);
117de16c322SLongbin Li 	reg &= ~(SPIFMC_TRAN_CSR_TRAN_MODE_MASK |
118de16c322SLongbin Li 		 SPIFMC_TRAN_CSR_FAST_MODE |
119de16c322SLongbin Li 		 SPIFMC_TRAN_CSR_BUS_WIDTH_2_BIT |
120de16c322SLongbin Li 		 SPIFMC_TRAN_CSR_BUS_WIDTH_4_BIT |
121de16c322SLongbin Li 		 SPIFMC_TRAN_CSR_DMA_EN |
122de16c322SLongbin Li 		 SPIFMC_TRAN_CSR_ADDR_BYTES_MASK |
123de16c322SLongbin Li 		 SPIFMC_TRAN_CSR_WITH_CMD |
124de16c322SLongbin Li 		 SPIFMC_TRAN_CSR_FIFO_TRG_LVL_MASK);
125de16c322SLongbin Li 
126de16c322SLongbin Li 	writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR);
127de16c322SLongbin Li 
128de16c322SLongbin Li 	return reg;
129de16c322SLongbin Li }
130de16c322SLongbin Li 
131de16c322SLongbin Li static ssize_t sg2044_spifmc_read_64k(struct sg2044_spifmc *spifmc,
132de16c322SLongbin Li 				      const struct spi_mem_op *op, loff_t from,
133de16c322SLongbin Li 				      size_t len, u_char *buf)
134de16c322SLongbin Li {
135de16c322SLongbin Li 	int xfer_size, offset;
136de16c322SLongbin Li 	u32 reg;
137de16c322SLongbin Li 	int ret;
138de16c322SLongbin Li 	int i;
139de16c322SLongbin Li 
140de16c322SLongbin Li 	reg = sg2044_spifmc_init_reg(spifmc);
141de16c322SLongbin Li 	reg |= (op->addr.nbytes + op->dummy.nbytes) << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT;
142de16c322SLongbin Li 	reg |= SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE;
143de16c322SLongbin Li 	reg |= SPIFMC_TRAN_CSR_WITH_CMD;
144de16c322SLongbin Li 	reg |= SPIFMC_TRAN_CSR_TRAN_MODE_RX;
145de16c322SLongbin Li 
146de16c322SLongbin Li 	writel(0, spifmc->io_base + SPIFMC_FIFO_PT);
147de16c322SLongbin Li 	writeb(op->cmd.opcode, spifmc->io_base + SPIFMC_FIFO_PORT);
148de16c322SLongbin Li 
149de16c322SLongbin Li 	for (i = op->addr.nbytes - 1; i >= 0; i--)
150de16c322SLongbin Li 		writeb((from >> i * 8) & 0xff, spifmc->io_base + SPIFMC_FIFO_PORT);
151de16c322SLongbin Li 
152de16c322SLongbin Li 	for (i = 0; i < op->dummy.nbytes; i++)
153de16c322SLongbin Li 		writeb(0xff, spifmc->io_base + SPIFMC_FIFO_PORT);
154de16c322SLongbin Li 
155de16c322SLongbin Li 	writel(len, spifmc->io_base + SPIFMC_TRAN_NUM);
156de16c322SLongbin Li 	writel(0, spifmc->io_base + SPIFMC_INT_STS);
157de16c322SLongbin Li 	reg |= SPIFMC_TRAN_CSR_GO_BUSY;
158de16c322SLongbin Li 	writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR);
159de16c322SLongbin Li 
160de16c322SLongbin Li 	ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_RD_FIFO);
161de16c322SLongbin Li 	if (ret < 0)
162de16c322SLongbin Li 		return ret;
163de16c322SLongbin Li 
164de16c322SLongbin Li 	offset = 0;
165de16c322SLongbin Li 	while (offset < len) {
166de16c322SLongbin Li 		xfer_size = min_t(size_t, SPIFMC_MAX_FIFO_DEPTH, len - offset);
167de16c322SLongbin Li 
168de16c322SLongbin Li 		ret = sg2044_spifmc_wait_xfer_size(spifmc, xfer_size);
169de16c322SLongbin Li 		if (ret < 0)
170de16c322SLongbin Li 			return ret;
171de16c322SLongbin Li 
172de16c322SLongbin Li 		for (i = 0; i < xfer_size; i++)
173de16c322SLongbin Li 			buf[i + offset] = readb(spifmc->io_base + SPIFMC_FIFO_PORT);
174de16c322SLongbin Li 
175de16c322SLongbin Li 		offset += xfer_size;
176de16c322SLongbin Li 	}
177de16c322SLongbin Li 
178de16c322SLongbin Li 	ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_TRAN_DONE);
179de16c322SLongbin Li 	if (ret < 0)
180de16c322SLongbin Li 		return ret;
181de16c322SLongbin Li 
182de16c322SLongbin Li 	writel(0, spifmc->io_base + SPIFMC_FIFO_PT);
183de16c322SLongbin Li 
184de16c322SLongbin Li 	return len;
185de16c322SLongbin Li }
186de16c322SLongbin Li 
187de16c322SLongbin Li static ssize_t sg2044_spifmc_read(struct sg2044_spifmc *spifmc,
188de16c322SLongbin Li 				  const struct spi_mem_op *op)
189de16c322SLongbin Li {
190de16c322SLongbin Li 	size_t xfer_size;
191de16c322SLongbin Li 	size_t offset;
192de16c322SLongbin Li 	loff_t from = op->addr.val;
193de16c322SLongbin Li 	size_t len = op->data.nbytes;
194de16c322SLongbin Li 	int ret;
195de16c322SLongbin Li 	u8 *din = op->data.buf.in;
196de16c322SLongbin Li 
197de16c322SLongbin Li 	offset = 0;
198de16c322SLongbin Li 	while (offset < len) {
199de16c322SLongbin Li 		xfer_size = min_t(size_t, SPIFMC_MAX_READ_SIZE, len - offset);
200de16c322SLongbin Li 
201de16c322SLongbin Li 		ret = sg2044_spifmc_read_64k(spifmc, op, from, xfer_size, din);
202de16c322SLongbin Li 		if (ret < 0)
203de16c322SLongbin Li 			return ret;
204de16c322SLongbin Li 
205de16c322SLongbin Li 		offset += xfer_size;
206de16c322SLongbin Li 		din += xfer_size;
207de16c322SLongbin Li 		from += xfer_size;
208de16c322SLongbin Li 	}
209de16c322SLongbin Li 
210de16c322SLongbin Li 	return 0;
211de16c322SLongbin Li }
212de16c322SLongbin Li 
213de16c322SLongbin Li static ssize_t sg2044_spifmc_write(struct sg2044_spifmc *spifmc,
214de16c322SLongbin Li 				   const struct spi_mem_op *op)
215de16c322SLongbin Li {
216de16c322SLongbin Li 	size_t xfer_size;
217de16c322SLongbin Li 	const u8 *dout = op->data.buf.out;
218de16c322SLongbin Li 	int i, offset;
219*3c9403f1SQasim Ijaz 	int ret;
220de16c322SLongbin Li 	u32 reg;
221de16c322SLongbin Li 
222de16c322SLongbin Li 	reg = sg2044_spifmc_init_reg(spifmc);
223de16c322SLongbin Li 	reg |= (op->addr.nbytes + op->dummy.nbytes) << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT;
224de16c322SLongbin Li 	reg |= SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE;
225de16c322SLongbin Li 	reg |= SPIFMC_TRAN_CSR_WITH_CMD;
226de16c322SLongbin Li 	reg |= SPIFMC_TRAN_CSR_TRAN_MODE_TX;
227de16c322SLongbin Li 
228de16c322SLongbin Li 	writel(0, spifmc->io_base + SPIFMC_FIFO_PT);
229de16c322SLongbin Li 	writeb(op->cmd.opcode, spifmc->io_base + SPIFMC_FIFO_PORT);
230de16c322SLongbin Li 
231de16c322SLongbin Li 	for (i = op->addr.nbytes - 1; i >= 0; i--)
232de16c322SLongbin Li 		writeb((op->addr.val >> i * 8) & 0xff, spifmc->io_base + SPIFMC_FIFO_PORT);
233de16c322SLongbin Li 
234de16c322SLongbin Li 	for (i = 0; i < op->dummy.nbytes; i++)
235de16c322SLongbin Li 		writeb(0xff, spifmc->io_base + SPIFMC_FIFO_PORT);
236de16c322SLongbin Li 
237de16c322SLongbin Li 	writel(0, spifmc->io_base + SPIFMC_INT_STS);
238de16c322SLongbin Li 	writel(op->data.nbytes, spifmc->io_base + SPIFMC_TRAN_NUM);
239de16c322SLongbin Li 	reg |= SPIFMC_TRAN_CSR_GO_BUSY;
240de16c322SLongbin Li 	writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR);
241de16c322SLongbin Li 
242de16c322SLongbin Li 	ret = sg2044_spifmc_wait_xfer_size(spifmc, 0);
243de16c322SLongbin Li 	if (ret < 0)
244de16c322SLongbin Li 		return ret;
245de16c322SLongbin Li 
246de16c322SLongbin Li 	writel(0, spifmc->io_base + SPIFMC_FIFO_PT);
247de16c322SLongbin Li 
248de16c322SLongbin Li 	offset = 0;
249de16c322SLongbin Li 	while (offset < op->data.nbytes) {
250de16c322SLongbin Li 		xfer_size = min_t(size_t, SPIFMC_MAX_FIFO_DEPTH, op->data.nbytes - offset);
251de16c322SLongbin Li 
252de16c322SLongbin Li 		ret = sg2044_spifmc_wait_xfer_size(spifmc, 0);
253de16c322SLongbin Li 		if (ret < 0)
254de16c322SLongbin Li 			return ret;
255de16c322SLongbin Li 
256de16c322SLongbin Li 		for (i = 0; i < xfer_size; i++)
257de16c322SLongbin Li 			writeb(dout[i + offset], spifmc->io_base + SPIFMC_FIFO_PORT);
258de16c322SLongbin Li 
259de16c322SLongbin Li 		offset += xfer_size;
260de16c322SLongbin Li 	}
261de16c322SLongbin Li 
262de16c322SLongbin Li 	ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_TRAN_DONE);
263de16c322SLongbin Li 	if (ret < 0)
264de16c322SLongbin Li 		return ret;
265de16c322SLongbin Li 
266de16c322SLongbin Li 	writel(0, spifmc->io_base + SPIFMC_FIFO_PT);
267de16c322SLongbin Li 
268de16c322SLongbin Li 	return 0;
269de16c322SLongbin Li }
270de16c322SLongbin Li 
271de16c322SLongbin Li static ssize_t sg2044_spifmc_tran_cmd(struct sg2044_spifmc *spifmc,
272de16c322SLongbin Li 				      const struct spi_mem_op *op)
273de16c322SLongbin Li {
274de16c322SLongbin Li 	int i, ret;
275de16c322SLongbin Li 	u32 reg;
276de16c322SLongbin Li 
277de16c322SLongbin Li 	reg = sg2044_spifmc_init_reg(spifmc);
278de16c322SLongbin Li 	reg |= (op->addr.nbytes + op->dummy.nbytes) << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT;
279de16c322SLongbin Li 	reg |= SPIFMC_TRAN_CSR_FIFO_TRG_LVL_1_BYTE;
280de16c322SLongbin Li 	reg |= SPIFMC_TRAN_CSR_WITH_CMD;
281de16c322SLongbin Li 
282de16c322SLongbin Li 	writel(0, spifmc->io_base + SPIFMC_FIFO_PT);
283de16c322SLongbin Li 	writeb(op->cmd.opcode, spifmc->io_base + SPIFMC_FIFO_PORT);
284de16c322SLongbin Li 
285de16c322SLongbin Li 	for (i = op->addr.nbytes - 1; i >= 0; i--)
286de16c322SLongbin Li 		writeb((op->addr.val >> i * 8) & 0xff, spifmc->io_base + SPIFMC_FIFO_PORT);
287de16c322SLongbin Li 
288de16c322SLongbin Li 	for (i = 0; i < op->dummy.nbytes; i++)
289de16c322SLongbin Li 		writeb(0xff, spifmc->io_base + SPIFMC_FIFO_PORT);
290de16c322SLongbin Li 
291de16c322SLongbin Li 	writel(0, spifmc->io_base + SPIFMC_INT_STS);
292de16c322SLongbin Li 	reg |= SPIFMC_TRAN_CSR_GO_BUSY;
293de16c322SLongbin Li 	writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR);
294de16c322SLongbin Li 
295de16c322SLongbin Li 	ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_TRAN_DONE);
296de16c322SLongbin Li 	if (ret < 0)
297de16c322SLongbin Li 		return ret;
298de16c322SLongbin Li 
299de16c322SLongbin Li 	writel(0, spifmc->io_base + SPIFMC_FIFO_PT);
300de16c322SLongbin Li 
301de16c322SLongbin Li 	return 0;
302de16c322SLongbin Li }
303de16c322SLongbin Li 
304de16c322SLongbin Li static void sg2044_spifmc_trans(struct sg2044_spifmc *spifmc,
305de16c322SLongbin Li 				const struct spi_mem_op *op)
306de16c322SLongbin Li {
307de16c322SLongbin Li 	if (op->data.dir == SPI_MEM_DATA_IN)
308de16c322SLongbin Li 		sg2044_spifmc_read(spifmc, op);
309de16c322SLongbin Li 	else if (op->data.dir == SPI_MEM_DATA_OUT)
310de16c322SLongbin Li 		sg2044_spifmc_write(spifmc, op);
311de16c322SLongbin Li 	else
312de16c322SLongbin Li 		sg2044_spifmc_tran_cmd(spifmc, op);
313de16c322SLongbin Li }
314de16c322SLongbin Li 
315de16c322SLongbin Li static ssize_t sg2044_spifmc_trans_reg(struct sg2044_spifmc *spifmc,
316de16c322SLongbin Li 				       const struct spi_mem_op *op)
317de16c322SLongbin Li {
318de16c322SLongbin Li 	const u8 *dout = NULL;
319de16c322SLongbin Li 	u8 *din = NULL;
320de16c322SLongbin Li 	size_t len = op->data.nbytes;
321de16c322SLongbin Li 	int ret, i;
322de16c322SLongbin Li 	u32 reg;
323de16c322SLongbin Li 
324de16c322SLongbin Li 	if (op->data.dir == SPI_MEM_DATA_IN)
325de16c322SLongbin Li 		din = op->data.buf.in;
326de16c322SLongbin Li 	else
327de16c322SLongbin Li 		dout = op->data.buf.out;
328de16c322SLongbin Li 
329de16c322SLongbin Li 	reg = sg2044_spifmc_init_reg(spifmc);
330de16c322SLongbin Li 	reg |= SPIFMC_TRAN_CSR_FIFO_TRG_LVL_1_BYTE;
331de16c322SLongbin Li 	reg |= SPIFMC_TRAN_CSR_WITH_CMD;
332de16c322SLongbin Li 
333de16c322SLongbin Li 	if (din) {
334de16c322SLongbin Li 		reg |= SPIFMC_TRAN_CSR_BUS_WIDTH_1_BIT;
335de16c322SLongbin Li 		reg |= SPIFMC_TRAN_CSR_TRAN_MODE_RX;
336de16c322SLongbin Li 		reg |= SPIFMC_TRAN_CSR_TRAN_MODE_TX;
337de16c322SLongbin Li 
338de16c322SLongbin Li 		writel(SPIFMC_OPT_DISABLE_FIFO_FLUSH, spifmc->io_base + SPIFMC_OPT);
339de16c322SLongbin Li 	} else {
340de16c322SLongbin Li 		/*
341de16c322SLongbin Li 		 * If write values to the Status Register,
342de16c322SLongbin Li 		 * configure TRAN_CSR register as the same as
343de16c322SLongbin Li 		 * sg2044_spifmc_read_reg.
344de16c322SLongbin Li 		 */
345de16c322SLongbin Li 		if (op->cmd.opcode == 0x01) {
346de16c322SLongbin Li 			reg |= SPIFMC_TRAN_CSR_TRAN_MODE_RX;
347de16c322SLongbin Li 			reg |= SPIFMC_TRAN_CSR_TRAN_MODE_TX;
348de16c322SLongbin Li 			writel(len, spifmc->io_base + SPIFMC_TRAN_NUM);
349de16c322SLongbin Li 		}
350de16c322SLongbin Li 	}
351de16c322SLongbin Li 
352de16c322SLongbin Li 	writel(0, spifmc->io_base + SPIFMC_FIFO_PT);
353de16c322SLongbin Li 	writeb(op->cmd.opcode, spifmc->io_base + SPIFMC_FIFO_PORT);
354de16c322SLongbin Li 
355de16c322SLongbin Li 	for (i = 0; i < len; i++) {
356de16c322SLongbin Li 		if (din)
357de16c322SLongbin Li 			writeb(0xff, spifmc->io_base + SPIFMC_FIFO_PORT);
358de16c322SLongbin Li 		else
359de16c322SLongbin Li 			writeb(dout[i], spifmc->io_base + SPIFMC_FIFO_PORT);
360de16c322SLongbin Li 	}
361de16c322SLongbin Li 
362de16c322SLongbin Li 	writel(0, spifmc->io_base + SPIFMC_INT_STS);
363de16c322SLongbin Li 	writel(len, spifmc->io_base + SPIFMC_TRAN_NUM);
364de16c322SLongbin Li 	reg |= SPIFMC_TRAN_CSR_GO_BUSY;
365de16c322SLongbin Li 	writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR);
366de16c322SLongbin Li 
367de16c322SLongbin Li 	ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_TRAN_DONE);
368de16c322SLongbin Li 	if (ret < 0)
369de16c322SLongbin Li 		return ret;
370de16c322SLongbin Li 
371de16c322SLongbin Li 	if (din) {
372de16c322SLongbin Li 		while (len--)
373de16c322SLongbin Li 			*din++ = readb(spifmc->io_base + SPIFMC_FIFO_PORT);
374de16c322SLongbin Li 	}
375de16c322SLongbin Li 
376de16c322SLongbin Li 	writel(0, spifmc->io_base + SPIFMC_FIFO_PT);
377de16c322SLongbin Li 
378de16c322SLongbin Li 	return 0;
379de16c322SLongbin Li }
380de16c322SLongbin Li 
381de16c322SLongbin Li static int sg2044_spifmc_exec_op(struct spi_mem *mem,
382de16c322SLongbin Li 				 const struct spi_mem_op *op)
383de16c322SLongbin Li {
384de16c322SLongbin Li 	struct sg2044_spifmc *spifmc;
385de16c322SLongbin Li 
386de16c322SLongbin Li 	spifmc = spi_controller_get_devdata(mem->spi->controller);
387de16c322SLongbin Li 
388de16c322SLongbin Li 	mutex_lock(&spifmc->lock);
389de16c322SLongbin Li 
390de16c322SLongbin Li 	if (op->addr.nbytes == 0)
391de16c322SLongbin Li 		sg2044_spifmc_trans_reg(spifmc, op);
392de16c322SLongbin Li 	else
393de16c322SLongbin Li 		sg2044_spifmc_trans(spifmc, op);
394de16c322SLongbin Li 
395de16c322SLongbin Li 	mutex_unlock(&spifmc->lock);
396de16c322SLongbin Li 
397de16c322SLongbin Li 	return 0;
398de16c322SLongbin Li }
399de16c322SLongbin Li 
400de16c322SLongbin Li static const struct spi_controller_mem_ops sg2044_spifmc_mem_ops = {
401de16c322SLongbin Li 	.exec_op = sg2044_spifmc_exec_op,
402de16c322SLongbin Li };
403de16c322SLongbin Li 
404de16c322SLongbin Li static void sg2044_spifmc_init(struct sg2044_spifmc *spifmc)
405de16c322SLongbin Li {
406de16c322SLongbin Li 	u32 tran_csr;
407de16c322SLongbin Li 	u32 reg;
408de16c322SLongbin Li 
409de16c322SLongbin Li 	writel(0, spifmc->io_base + SPIFMC_DMMR);
410de16c322SLongbin Li 
411de16c322SLongbin Li 	reg = readl(spifmc->io_base + SPIFMC_CTRL);
412de16c322SLongbin Li 	reg |= SPIFMC_CTRL_SRST;
413de16c322SLongbin Li 	reg &= ~(SPIFMC_CTRL_SCK_DIV_MASK);
414de16c322SLongbin Li 	reg |= 1;
415de16c322SLongbin Li 	writel(reg, spifmc->io_base + SPIFMC_CTRL);
416de16c322SLongbin Li 
417de16c322SLongbin Li 	writel(0, spifmc->io_base + SPIFMC_CE_CTRL);
418de16c322SLongbin Li 
419de16c322SLongbin Li 	tran_csr = readl(spifmc->io_base + SPIFMC_TRAN_CSR);
420de16c322SLongbin Li 	tran_csr |= (0 << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT);
421de16c322SLongbin Li 	tran_csr |= SPIFMC_TRAN_CSR_FIFO_TRG_LVL_4_BYTE;
422de16c322SLongbin Li 	tran_csr |= SPIFMC_TRAN_CSR_WITH_CMD;
423de16c322SLongbin Li 	writel(tran_csr, spifmc->io_base + SPIFMC_TRAN_CSR);
424de16c322SLongbin Li }
425de16c322SLongbin Li 
426de16c322SLongbin Li static int sg2044_spifmc_probe(struct platform_device *pdev)
427de16c322SLongbin Li {
428c6d94963SAndy Shevchenko 	struct device *dev = &pdev->dev;
429de16c322SLongbin Li 	struct spi_controller *ctrl;
430de16c322SLongbin Li 	struct sg2044_spifmc *spifmc;
431de16c322SLongbin Li 	int ret;
432de16c322SLongbin Li 
433de16c322SLongbin Li 	ctrl = devm_spi_alloc_host(&pdev->dev, sizeof(*spifmc));
434de16c322SLongbin Li 	if (!ctrl)
435de16c322SLongbin Li 		return -ENOMEM;
436de16c322SLongbin Li 
437de16c322SLongbin Li 	spifmc = spi_controller_get_devdata(ctrl);
438de16c322SLongbin Li 
439de16c322SLongbin Li 	spifmc->clk = devm_clk_get_enabled(&pdev->dev, NULL);
440de16c322SLongbin Li 	if (IS_ERR(spifmc->clk))
441085cf53dSAndy Shevchenko 		return dev_err_probe(dev, PTR_ERR(spifmc->clk), "Cannot get and enable AHB clock\n");
442de16c322SLongbin Li 
443de16c322SLongbin Li 	spifmc->dev = &pdev->dev;
444de16c322SLongbin Li 	spifmc->ctrl = ctrl;
445de16c322SLongbin Li 
446de16c322SLongbin Li 	spifmc->io_base = devm_platform_ioremap_resource(pdev, 0);
447a1d8f709SDan Carpenter 	if (IS_ERR(spifmc->io_base))
448a1d8f709SDan Carpenter 		return PTR_ERR(spifmc->io_base);
449de16c322SLongbin Li 
450de16c322SLongbin Li 	ctrl->num_chipselect = 1;
451de16c322SLongbin Li 	ctrl->dev.of_node = pdev->dev.of_node;
452de16c322SLongbin Li 	ctrl->bits_per_word_mask = SPI_BPW_MASK(8);
453de16c322SLongbin Li 	ctrl->auto_runtime_pm = false;
454de16c322SLongbin Li 	ctrl->mem_ops = &sg2044_spifmc_mem_ops;
455de16c322SLongbin Li 	ctrl->mode_bits = SPI_RX_DUAL | SPI_TX_DUAL | SPI_RX_QUAD | SPI_TX_QUAD;
456de16c322SLongbin Li 
457c6d94963SAndy Shevchenko 	ret = devm_mutex_init(dev, &spifmc->lock);
458c6d94963SAndy Shevchenko 	if (ret)
459c6d94963SAndy Shevchenko 		return ret;
460de16c322SLongbin Li 
461de16c322SLongbin Li 	sg2044_spifmc_init(spifmc);
462de16c322SLongbin Li 	sg2044_spifmc_init_reg(spifmc);
463de16c322SLongbin Li 
464de16c322SLongbin Li 	ret = devm_spi_register_controller(&pdev->dev, ctrl);
465085cf53dSAndy Shevchenko 	if (ret)
466085cf53dSAndy Shevchenko 		return dev_err_probe(dev, ret, "spi_register_controller failed\n");
467de16c322SLongbin Li 
468de16c322SLongbin Li 	return 0;
469de16c322SLongbin Li }
470de16c322SLongbin Li 
471de16c322SLongbin Li static const struct of_device_id sg2044_spifmc_match[] = {
472de16c322SLongbin Li 	{ .compatible = "sophgo,sg2044-spifmc-nor" },
473de16c322SLongbin Li 	{ /* sentinel */ }
474de16c322SLongbin Li };
475de16c322SLongbin Li MODULE_DEVICE_TABLE(of, sg2044_spifmc_match);
476de16c322SLongbin Li 
477de16c322SLongbin Li static struct platform_driver sg2044_nor_driver = {
478de16c322SLongbin Li 	.driver = {
479de16c322SLongbin Li 		.name = "sg2044,spifmc-nor",
480de16c322SLongbin Li 		.of_match_table = sg2044_spifmc_match,
481de16c322SLongbin Li 	},
482de16c322SLongbin Li 	.probe = sg2044_spifmc_probe,
483de16c322SLongbin Li };
484de16c322SLongbin Li module_platform_driver(sg2044_nor_driver);
485de16c322SLongbin Li 
486de16c322SLongbin Li MODULE_DESCRIPTION("SG2044 SPI NOR controller driver");
487de16c322SLongbin Li MODULE_AUTHOR("Longbin Li <looong.bin@gmail.com>");
488de16c322SLongbin Li MODULE_LICENSE("GPL");
489