xref: /openbmc/linux/drivers/spi/spi-mxic.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1b942d80bSMason Yang // SPDX-License-Identifier: GPL-2.0
2b942d80bSMason Yang //
3b942d80bSMason Yang // Copyright (C) 2018 Macronix International Co., Ltd.
4b942d80bSMason Yang //
5b942d80bSMason Yang // Authors:
6b942d80bSMason Yang //	Mason Yang <masonccyang@mxic.com.tw>
7b942d80bSMason Yang //	zhengxunli <zhengxunli@mxic.com.tw>
8b942d80bSMason Yang //	Boris Brezillon <boris.brezillon@bootlin.com>
9b942d80bSMason Yang //
10b942d80bSMason Yang 
11b942d80bSMason Yang #include <linux/clk.h>
12b942d80bSMason Yang #include <linux/io.h>
13b942d80bSMason Yang #include <linux/iopoll.h>
14b942d80bSMason Yang #include <linux/module.h>
1500360ebaSMiquel Raynal #include <linux/mtd/nand.h>
1600360ebaSMiquel Raynal #include <linux/mtd/nand-ecc-mxic.h>
17b942d80bSMason Yang #include <linux/platform_device.h>
18b942d80bSMason Yang #include <linux/pm_runtime.h>
19b942d80bSMason Yang #include <linux/spi/spi.h>
20b942d80bSMason Yang #include <linux/spi/spi-mem.h>
21b942d80bSMason Yang 
22b942d80bSMason Yang #define HC_CFG			0x0
23b942d80bSMason Yang #define HC_CFG_IF_CFG(x)	((x) << 27)
24b942d80bSMason Yang #define HC_CFG_DUAL_SLAVE	BIT(31)
25b942d80bSMason Yang #define HC_CFG_INDIVIDUAL	BIT(30)
26b942d80bSMason Yang #define HC_CFG_NIO(x)		(((x) / 4) << 27)
27b942d80bSMason Yang #define HC_CFG_TYPE(s, t)	((t) << (23 + ((s) * 2)))
28b942d80bSMason Yang #define HC_CFG_TYPE_SPI_NOR	0
29b942d80bSMason Yang #define HC_CFG_TYPE_SPI_NAND	1
30b942d80bSMason Yang #define HC_CFG_TYPE_SPI_RAM	2
31b942d80bSMason Yang #define HC_CFG_TYPE_RAW_NAND	3
32b942d80bSMason Yang #define HC_CFG_SLV_ACT(x)	((x) << 21)
33b942d80bSMason Yang #define HC_CFG_CLK_PH_EN	BIT(20)
34b942d80bSMason Yang #define HC_CFG_CLK_POL_INV	BIT(19)
35b942d80bSMason Yang #define HC_CFG_BIG_ENDIAN	BIT(18)
36b942d80bSMason Yang #define HC_CFG_DATA_PASS	BIT(17)
37b942d80bSMason Yang #define HC_CFG_IDLE_SIO_LVL(x)	((x) << 16)
38b942d80bSMason Yang #define HC_CFG_MAN_START_EN	BIT(3)
39b942d80bSMason Yang #define HC_CFG_MAN_START	BIT(2)
40b942d80bSMason Yang #define HC_CFG_MAN_CS_EN	BIT(1)
41b942d80bSMason Yang #define HC_CFG_MAN_CS_ASSERT	BIT(0)
42b942d80bSMason Yang 
43b942d80bSMason Yang #define INT_STS			0x4
44b942d80bSMason Yang #define INT_STS_EN		0x8
45b942d80bSMason Yang #define INT_SIG_EN		0xc
46b942d80bSMason Yang #define INT_STS_ALL		GENMASK(31, 0)
47b942d80bSMason Yang #define INT_RDY_PIN		BIT(26)
48b942d80bSMason Yang #define INT_RDY_SR		BIT(25)
49b942d80bSMason Yang #define INT_LNR_SUSP		BIT(24)
50b942d80bSMason Yang #define INT_ECC_ERR		BIT(17)
51b942d80bSMason Yang #define INT_CRC_ERR		BIT(16)
52b942d80bSMason Yang #define INT_LWR_DIS		BIT(12)
53b942d80bSMason Yang #define INT_LRD_DIS		BIT(11)
54b942d80bSMason Yang #define INT_SDMA_INT		BIT(10)
55b942d80bSMason Yang #define INT_DMA_FINISH		BIT(9)
56b942d80bSMason Yang #define INT_RX_NOT_FULL		BIT(3)
57b942d80bSMason Yang #define INT_RX_NOT_EMPTY	BIT(2)
58b942d80bSMason Yang #define INT_TX_NOT_FULL		BIT(1)
59b942d80bSMason Yang #define INT_TX_EMPTY		BIT(0)
60b942d80bSMason Yang 
61b942d80bSMason Yang #define HC_EN			0x10
62b942d80bSMason Yang #define HC_EN_BIT		BIT(0)
63b942d80bSMason Yang 
64b942d80bSMason Yang #define TXD(x)			(0x14 + ((x) * 4))
65b942d80bSMason Yang #define RXD			0x24
66b942d80bSMason Yang 
67b942d80bSMason Yang #define SS_CTRL(s)		(0x30 + ((s) * 4))
68b942d80bSMason Yang #define LRD_CFG			0x44
69b942d80bSMason Yang #define LWR_CFG			0x80
70b942d80bSMason Yang #define RWW_CFG			0x70
71b942d80bSMason Yang #define OP_READ			BIT(23)
72b942d80bSMason Yang #define OP_DUMMY_CYC(x)		((x) << 17)
73b942d80bSMason Yang #define OP_ADDR_BYTES(x)	((x) << 14)
74b942d80bSMason Yang #define OP_CMD_BYTES(x)		(((x) - 1) << 13)
75b942d80bSMason Yang #define OP_OCTA_CRC_EN		BIT(12)
76b942d80bSMason Yang #define OP_DQS_EN		BIT(11)
77b942d80bSMason Yang #define OP_ENHC_EN		BIT(10)
78b942d80bSMason Yang #define OP_PREAMBLE_EN		BIT(9)
79b942d80bSMason Yang #define OP_DATA_DDR		BIT(8)
80b942d80bSMason Yang #define OP_DATA_BUSW(x)		((x) << 6)
81b942d80bSMason Yang #define OP_ADDR_DDR		BIT(5)
82b942d80bSMason Yang #define OP_ADDR_BUSW(x)		((x) << 3)
83b942d80bSMason Yang #define OP_CMD_DDR		BIT(2)
84b942d80bSMason Yang #define OP_CMD_BUSW(x)		(x)
85b942d80bSMason Yang #define OP_BUSW_1		0
86b942d80bSMason Yang #define OP_BUSW_2		1
87b942d80bSMason Yang #define OP_BUSW_4		2
88b942d80bSMason Yang #define OP_BUSW_8		3
89b942d80bSMason Yang 
90b942d80bSMason Yang #define OCTA_CRC		0x38
91b942d80bSMason Yang #define OCTA_CRC_IN_EN(s)	BIT(3 + ((s) * 16))
92b942d80bSMason Yang #define OCTA_CRC_CHUNK(s, x)	((fls((x) / 32)) << (1 + ((s) * 16)))
93b942d80bSMason Yang #define OCTA_CRC_OUT_EN(s)	BIT(0 + ((s) * 16))
94b942d80bSMason Yang 
95b942d80bSMason Yang #define ONFI_DIN_CNT(s)		(0x3c + (s))
96b942d80bSMason Yang 
97b942d80bSMason Yang #define LRD_CTRL		0x48
98b942d80bSMason Yang #define RWW_CTRL		0x74
99b942d80bSMason Yang #define LWR_CTRL		0x84
100b942d80bSMason Yang #define LMODE_EN		BIT(31)
101b942d80bSMason Yang #define LMODE_SLV_ACT(x)	((x) << 21)
102b942d80bSMason Yang #define LMODE_CMD1(x)		((x) << 8)
103b942d80bSMason Yang #define LMODE_CMD0(x)		(x)
104b942d80bSMason Yang 
105b942d80bSMason Yang #define LRD_ADDR		0x4c
106b942d80bSMason Yang #define LWR_ADDR		0x88
107b942d80bSMason Yang #define LRD_RANGE		0x50
108b942d80bSMason Yang #define LWR_RANGE		0x8c
109b942d80bSMason Yang 
110b942d80bSMason Yang #define AXI_SLV_ADDR		0x54
111b942d80bSMason Yang 
112b942d80bSMason Yang #define DMAC_RD_CFG		0x58
113b942d80bSMason Yang #define DMAC_WR_CFG		0x94
114b942d80bSMason Yang #define DMAC_CFG_PERIPH_EN	BIT(31)
115b942d80bSMason Yang #define DMAC_CFG_ALLFLUSH_EN	BIT(30)
116b942d80bSMason Yang #define DMAC_CFG_LASTFLUSH_EN	BIT(29)
117b942d80bSMason Yang #define DMAC_CFG_QE(x)		(((x) + 1) << 16)
118b942d80bSMason Yang #define DMAC_CFG_BURST_LEN(x)	(((x) + 1) << 12)
119b942d80bSMason Yang #define DMAC_CFG_BURST_SZ(x)	((x) << 8)
120b942d80bSMason Yang #define DMAC_CFG_DIR_READ	BIT(1)
121b942d80bSMason Yang #define DMAC_CFG_START		BIT(0)
122b942d80bSMason Yang 
123b942d80bSMason Yang #define DMAC_RD_CNT		0x5c
124b942d80bSMason Yang #define DMAC_WR_CNT		0x98
125b942d80bSMason Yang 
126b942d80bSMason Yang #define SDMA_ADDR		0x60
127b942d80bSMason Yang 
128b942d80bSMason Yang #define DMAM_CFG		0x64
129b942d80bSMason Yang #define DMAM_CFG_START		BIT(31)
130b942d80bSMason Yang #define DMAM_CFG_CONT		BIT(30)
131b942d80bSMason Yang #define DMAM_CFG_SDMA_GAP(x)	(fls((x) / 8192) << 2)
132b942d80bSMason Yang #define DMAM_CFG_DIR_READ	BIT(1)
133b942d80bSMason Yang #define DMAM_CFG_EN		BIT(0)
134b942d80bSMason Yang 
135b942d80bSMason Yang #define DMAM_CNT		0x68
136b942d80bSMason Yang 
137b942d80bSMason Yang #define LNR_TIMER_TH		0x6c
138b942d80bSMason Yang 
139b942d80bSMason Yang #define RDM_CFG0		0x78
140b942d80bSMason Yang #define RDM_CFG0_POLY(x)	(x)
141b942d80bSMason Yang 
142b942d80bSMason Yang #define RDM_CFG1		0x7c
143b942d80bSMason Yang #define RDM_CFG1_RDM_EN		BIT(31)
144b942d80bSMason Yang #define RDM_CFG1_SEED(x)	(x)
145b942d80bSMason Yang 
146b942d80bSMason Yang #define LWR_SUSP_CTRL		0x90
147b942d80bSMason Yang #define LWR_SUSP_CTRL_EN	BIT(31)
148b942d80bSMason Yang 
149b942d80bSMason Yang #define DMAS_CTRL		0x9c
1506fe7ab38SMiquel Raynal #define DMAS_CTRL_EN		BIT(31)
1516fe7ab38SMiquel Raynal #define DMAS_CTRL_DIR_READ	BIT(30)
152b942d80bSMason Yang 
153b942d80bSMason Yang #define DATA_STROB		0xa0
154b942d80bSMason Yang #define DATA_STROB_EDO_EN	BIT(2)
155b942d80bSMason Yang #define DATA_STROB_INV_POL	BIT(1)
156b942d80bSMason Yang #define DATA_STROB_DELAY_2CYC	BIT(0)
157b942d80bSMason Yang 
158b942d80bSMason Yang #define IDLY_CODE(x)		(0xa4 + ((x) * 4))
159b942d80bSMason Yang #define IDLY_CODE_VAL(x, v)	((v) << (((x) % 4) * 8))
160b942d80bSMason Yang 
161b942d80bSMason Yang #define GPIO			0xc4
162b942d80bSMason Yang #define GPIO_PT(x)		BIT(3 + ((x) * 16))
163b942d80bSMason Yang #define GPIO_RESET(x)		BIT(2 + ((x) * 16))
164b942d80bSMason Yang #define GPIO_HOLDB(x)		BIT(1 + ((x) * 16))
165b942d80bSMason Yang #define GPIO_WPB(x)		BIT((x) * 16)
166b942d80bSMason Yang 
167b942d80bSMason Yang #define HC_VER			0xd0
168b942d80bSMason Yang 
169b942d80bSMason Yang #define HW_TEST(x)		(0xe0 + ((x) * 4))
170b942d80bSMason Yang 
171b942d80bSMason Yang struct mxic_spi {
17200360ebaSMiquel Raynal 	struct device *dev;
173b942d80bSMason Yang 	struct clk *ps_clk;
174b942d80bSMason Yang 	struct clk *send_clk;
175b942d80bSMason Yang 	struct clk *send_dly_clk;
176b942d80bSMason Yang 	void __iomem *regs;
177b942d80bSMason Yang 	u32 cur_speed_hz;
17833fce1d8SMiquel Raynal 	struct {
17933fce1d8SMiquel Raynal 		void __iomem *map;
18033fce1d8SMiquel Raynal 		dma_addr_t dma;
18133fce1d8SMiquel Raynal 		size_t size;
18233fce1d8SMiquel Raynal 	} linear;
18300360ebaSMiquel Raynal 
18400360ebaSMiquel Raynal 	struct {
18500360ebaSMiquel Raynal 		bool use_pipelined_conf;
18600360ebaSMiquel Raynal 		struct nand_ecc_engine *pipelined_engine;
18700360ebaSMiquel Raynal 		void *ctx;
18800360ebaSMiquel Raynal 	} ecc;
189b942d80bSMason Yang };
190b942d80bSMason Yang 
mxic_spi_clk_enable(struct mxic_spi * mxic)191b942d80bSMason Yang static int mxic_spi_clk_enable(struct mxic_spi *mxic)
192b942d80bSMason Yang {
193b942d80bSMason Yang 	int ret;
194b942d80bSMason Yang 
195b942d80bSMason Yang 	ret = clk_prepare_enable(mxic->send_clk);
196b942d80bSMason Yang 	if (ret)
197b942d80bSMason Yang 		return ret;
198b942d80bSMason Yang 
199b942d80bSMason Yang 	ret = clk_prepare_enable(mxic->send_dly_clk);
200b942d80bSMason Yang 	if (ret)
201b942d80bSMason Yang 		goto err_send_dly_clk;
202b942d80bSMason Yang 
203b942d80bSMason Yang 	return ret;
204b942d80bSMason Yang 
205b942d80bSMason Yang err_send_dly_clk:
206b942d80bSMason Yang 	clk_disable_unprepare(mxic->send_clk);
207b942d80bSMason Yang 
208b942d80bSMason Yang 	return ret;
209b942d80bSMason Yang }
210b942d80bSMason Yang 
mxic_spi_clk_disable(struct mxic_spi * mxic)211b942d80bSMason Yang static void mxic_spi_clk_disable(struct mxic_spi *mxic)
212b942d80bSMason Yang {
213b942d80bSMason Yang 	clk_disable_unprepare(mxic->send_clk);
214b942d80bSMason Yang 	clk_disable_unprepare(mxic->send_dly_clk);
215b942d80bSMason Yang }
216b942d80bSMason Yang 
mxic_spi_set_input_delay_dqs(struct mxic_spi * mxic,u8 idly_code)217b942d80bSMason Yang static void mxic_spi_set_input_delay_dqs(struct mxic_spi *mxic, u8 idly_code)
218b942d80bSMason Yang {
219b942d80bSMason Yang 	writel(IDLY_CODE_VAL(0, idly_code) |
220b942d80bSMason Yang 	       IDLY_CODE_VAL(1, idly_code) |
221b942d80bSMason Yang 	       IDLY_CODE_VAL(2, idly_code) |
222b942d80bSMason Yang 	       IDLY_CODE_VAL(3, idly_code),
223b942d80bSMason Yang 	       mxic->regs + IDLY_CODE(0));
224b942d80bSMason Yang 	writel(IDLY_CODE_VAL(4, idly_code) |
225b942d80bSMason Yang 	       IDLY_CODE_VAL(5, idly_code) |
226b942d80bSMason Yang 	       IDLY_CODE_VAL(6, idly_code) |
227b942d80bSMason Yang 	       IDLY_CODE_VAL(7, idly_code),
228b942d80bSMason Yang 	       mxic->regs + IDLY_CODE(1));
229b942d80bSMason Yang }
230b942d80bSMason Yang 
mxic_spi_clk_setup(struct mxic_spi * mxic,unsigned long freq)231b942d80bSMason Yang static int mxic_spi_clk_setup(struct mxic_spi *mxic, unsigned long freq)
232b942d80bSMason Yang {
233b942d80bSMason Yang 	int ret;
234b942d80bSMason Yang 
235b942d80bSMason Yang 	ret = clk_set_rate(mxic->send_clk, freq);
236b942d80bSMason Yang 	if (ret)
237b942d80bSMason Yang 		return ret;
238b942d80bSMason Yang 
239b942d80bSMason Yang 	ret = clk_set_rate(mxic->send_dly_clk, freq);
240b942d80bSMason Yang 	if (ret)
241b942d80bSMason Yang 		return ret;
242b942d80bSMason Yang 
243b942d80bSMason Yang 	/*
244b942d80bSMason Yang 	 * A constant delay range from 0x0 ~ 0x1F for input delay,
245b942d80bSMason Yang 	 * the unit is 78 ps, the max input delay is 2.418 ns.
246b942d80bSMason Yang 	 */
247b942d80bSMason Yang 	mxic_spi_set_input_delay_dqs(mxic, 0xf);
248b942d80bSMason Yang 
249b942d80bSMason Yang 	/*
250b942d80bSMason Yang 	 * Phase degree = 360 * freq * output-delay
251b942d80bSMason Yang 	 * where output-delay is a constant value 1 ns in FPGA.
252b942d80bSMason Yang 	 *
253b942d80bSMason Yang 	 * Get Phase degree = 360 * freq * 1 ns
254b942d80bSMason Yang 	 *                  = 360 * freq * 1 sec / 1000000000
255b942d80bSMason Yang 	 *                  = 9 * freq / 25000000
256b942d80bSMason Yang 	 */
257b942d80bSMason Yang 	ret = clk_set_phase(mxic->send_dly_clk, 9 * freq / 25000000);
258b942d80bSMason Yang 	if (ret)
259b942d80bSMason Yang 		return ret;
260b942d80bSMason Yang 
261b942d80bSMason Yang 	return 0;
262b942d80bSMason Yang }
263b942d80bSMason Yang 
mxic_spi_set_freq(struct mxic_spi * mxic,unsigned long freq)264b942d80bSMason Yang static int mxic_spi_set_freq(struct mxic_spi *mxic, unsigned long freq)
265b942d80bSMason Yang {
266b942d80bSMason Yang 	int ret;
267b942d80bSMason Yang 
268b942d80bSMason Yang 	if (mxic->cur_speed_hz == freq)
269b942d80bSMason Yang 		return 0;
270b942d80bSMason Yang 
271b942d80bSMason Yang 	mxic_spi_clk_disable(mxic);
272b942d80bSMason Yang 	ret = mxic_spi_clk_setup(mxic, freq);
273b942d80bSMason Yang 	if (ret)
274b942d80bSMason Yang 		return ret;
275b942d80bSMason Yang 
276b942d80bSMason Yang 	ret = mxic_spi_clk_enable(mxic);
277b942d80bSMason Yang 	if (ret)
278b942d80bSMason Yang 		return ret;
279b942d80bSMason Yang 
280b942d80bSMason Yang 	mxic->cur_speed_hz = freq;
281b942d80bSMason Yang 
282b942d80bSMason Yang 	return 0;
283b942d80bSMason Yang }
284b942d80bSMason Yang 
mxic_spi_hw_init(struct mxic_spi * mxic)285b942d80bSMason Yang static void mxic_spi_hw_init(struct mxic_spi *mxic)
286b942d80bSMason Yang {
287b942d80bSMason Yang 	writel(0, mxic->regs + DATA_STROB);
288b942d80bSMason Yang 	writel(INT_STS_ALL, mxic->regs + INT_STS_EN);
289b942d80bSMason Yang 	writel(0, mxic->regs + HC_EN);
290b942d80bSMason Yang 	writel(0, mxic->regs + LRD_CFG);
291b942d80bSMason Yang 	writel(0, mxic->regs + LRD_CTRL);
2924a82fe0eSMiquel Raynal 	writel(HC_CFG_NIO(1) | HC_CFG_TYPE(0, HC_CFG_TYPE_SPI_NOR) |
293b942d80bSMason Yang 	       HC_CFG_SLV_ACT(0) | HC_CFG_MAN_CS_EN | HC_CFG_IDLE_SIO_LVL(1),
294b942d80bSMason Yang 	       mxic->regs + HC_CFG);
295b942d80bSMason Yang }
296b942d80bSMason Yang 
mxic_spi_prep_hc_cfg(struct spi_device * spi,u32 flags)297b7b64db7SMiquel Raynal static u32 mxic_spi_prep_hc_cfg(struct spi_device *spi, u32 flags)
298b7b64db7SMiquel Raynal {
299b7b64db7SMiquel Raynal 	int nio = 1;
300b7b64db7SMiquel Raynal 
301b7b64db7SMiquel Raynal 	if (spi->mode & (SPI_TX_OCTAL | SPI_RX_OCTAL))
302b7b64db7SMiquel Raynal 		nio = 8;
303b7b64db7SMiquel Raynal 	else if (spi->mode & (SPI_TX_QUAD | SPI_RX_QUAD))
304b7b64db7SMiquel Raynal 		nio = 4;
305b7b64db7SMiquel Raynal 	else if (spi->mode & (SPI_TX_DUAL | SPI_RX_DUAL))
306b7b64db7SMiquel Raynal 		nio = 2;
307b7b64db7SMiquel Raynal 
308b7b64db7SMiquel Raynal 	return flags | HC_CFG_NIO(nio) |
309*9e264f3fSAmit Kumar Mahapatra via Alsa-devel 	       HC_CFG_TYPE(spi_get_chipselect(spi, 0), HC_CFG_TYPE_SPI_NOR) |
310*9e264f3fSAmit Kumar Mahapatra via Alsa-devel 	       HC_CFG_SLV_ACT(spi_get_chipselect(spi, 0)) | HC_CFG_IDLE_SIO_LVL(1);
311b7b64db7SMiquel Raynal }
312b7b64db7SMiquel Raynal 
mxic_spi_mem_prep_op_cfg(const struct spi_mem_op * op,unsigned int data_len)31333fce1d8SMiquel Raynal static u32 mxic_spi_mem_prep_op_cfg(const struct spi_mem_op *op,
31433fce1d8SMiquel Raynal 				    unsigned int data_len)
315d1b64bb2SMiquel Raynal {
316d1b64bb2SMiquel Raynal 	u32 cfg = OP_CMD_BYTES(op->cmd.nbytes) |
317d1b64bb2SMiquel Raynal 		  OP_CMD_BUSW(fls(op->cmd.buswidth) - 1) |
318d1b64bb2SMiquel Raynal 		  (op->cmd.dtr ? OP_CMD_DDR : 0);
319d1b64bb2SMiquel Raynal 
320d1b64bb2SMiquel Raynal 	if (op->addr.nbytes)
321d1b64bb2SMiquel Raynal 		cfg |= OP_ADDR_BYTES(op->addr.nbytes) |
322d1b64bb2SMiquel Raynal 		       OP_ADDR_BUSW(fls(op->addr.buswidth) - 1) |
323d1b64bb2SMiquel Raynal 		       (op->addr.dtr ? OP_ADDR_DDR : 0);
324d1b64bb2SMiquel Raynal 
325d1b64bb2SMiquel Raynal 	if (op->dummy.nbytes)
326d1b64bb2SMiquel Raynal 		cfg |= OP_DUMMY_CYC(op->dummy.nbytes);
327d1b64bb2SMiquel Raynal 
32833fce1d8SMiquel Raynal 	/* Direct mapping data.nbytes field is not populated */
32933fce1d8SMiquel Raynal 	if (data_len) {
330d1b64bb2SMiquel Raynal 		cfg |= OP_DATA_BUSW(fls(op->data.buswidth) - 1) |
331d1b64bb2SMiquel Raynal 		       (op->data.dtr ? OP_DATA_DDR : 0);
332d1b64bb2SMiquel Raynal 		if (op->data.dir == SPI_MEM_DATA_IN) {
333d1b64bb2SMiquel Raynal 			cfg |= OP_READ;
334d1b64bb2SMiquel Raynal 			if (op->data.dtr)
335d1b64bb2SMiquel Raynal 				cfg |= OP_DQS_EN;
336d1b64bb2SMiquel Raynal 		}
337d1b64bb2SMiquel Raynal 	}
338d1b64bb2SMiquel Raynal 
339d1b64bb2SMiquel Raynal 	return cfg;
340d1b64bb2SMiquel Raynal }
341d1b64bb2SMiquel Raynal 
mxic_spi_data_xfer(struct mxic_spi * mxic,const void * txbuf,void * rxbuf,unsigned int len)342b942d80bSMason Yang static int mxic_spi_data_xfer(struct mxic_spi *mxic, const void *txbuf,
343b942d80bSMason Yang 			      void *rxbuf, unsigned int len)
344b942d80bSMason Yang {
345b942d80bSMason Yang 	unsigned int pos = 0;
346b942d80bSMason Yang 
347b942d80bSMason Yang 	while (pos < len) {
348b942d80bSMason Yang 		unsigned int nbytes = len - pos;
349b942d80bSMason Yang 		u32 data = 0xffffffff;
350b942d80bSMason Yang 		u32 sts;
351b942d80bSMason Yang 		int ret;
352b942d80bSMason Yang 
353b942d80bSMason Yang 		if (nbytes > 4)
354b942d80bSMason Yang 			nbytes = 4;
355b942d80bSMason Yang 
356b942d80bSMason Yang 		if (txbuf)
357b942d80bSMason Yang 			memcpy(&data, txbuf + pos, nbytes);
358b942d80bSMason Yang 
359b942d80bSMason Yang 		ret = readl_poll_timeout(mxic->regs + INT_STS, sts,
360b942d80bSMason Yang 					 sts & INT_TX_EMPTY, 0, USEC_PER_SEC);
361b942d80bSMason Yang 		if (ret)
362b942d80bSMason Yang 			return ret;
363b942d80bSMason Yang 
364b942d80bSMason Yang 		writel(data, mxic->regs + TXD(nbytes % 4));
365b942d80bSMason Yang 
366b942d80bSMason Yang 		ret = readl_poll_timeout(mxic->regs + INT_STS, sts,
3675fd6739eSMiquel Raynal 					 sts & INT_TX_EMPTY, 0, USEC_PER_SEC);
368b942d80bSMason Yang 		if (ret)
369b942d80bSMason Yang 			return ret;
370b942d80bSMason Yang 
371b942d80bSMason Yang 		ret = readl_poll_timeout(mxic->regs + INT_STS, sts,
372b942d80bSMason Yang 					 sts & INT_RX_NOT_EMPTY, 0,
373b942d80bSMason Yang 					 USEC_PER_SEC);
374b942d80bSMason Yang 		if (ret)
375b942d80bSMason Yang 			return ret;
376b942d80bSMason Yang 
377b942d80bSMason Yang 		data = readl(mxic->regs + RXD);
3785fd6739eSMiquel Raynal 		if (rxbuf) {
379b942d80bSMason Yang 			data >>= (8 * (4 - nbytes));
380b942d80bSMason Yang 			memcpy(rxbuf + pos, &data, nbytes);
381b942d80bSMason Yang 		}
382b942d80bSMason Yang 		WARN_ON(readl(mxic->regs + INT_STS) & INT_RX_NOT_EMPTY);
383b942d80bSMason Yang 
384b942d80bSMason Yang 		pos += nbytes;
385b942d80bSMason Yang 	}
386b942d80bSMason Yang 
387b942d80bSMason Yang 	return 0;
388b942d80bSMason Yang }
389b942d80bSMason Yang 
mxic_spi_mem_dirmap_read(struct spi_mem_dirmap_desc * desc,u64 offs,size_t len,void * buf)39033fce1d8SMiquel Raynal static ssize_t mxic_spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
39133fce1d8SMiquel Raynal 					u64 offs, size_t len, void *buf)
39233fce1d8SMiquel Raynal {
39333fce1d8SMiquel Raynal 	struct mxic_spi *mxic = spi_master_get_devdata(desc->mem->spi->master);
39433fce1d8SMiquel Raynal 	int ret;
39533fce1d8SMiquel Raynal 	u32 sts;
39633fce1d8SMiquel Raynal 
39733fce1d8SMiquel Raynal 	if (WARN_ON(offs + desc->info.offset + len > U32_MAX))
39833fce1d8SMiquel Raynal 		return -EINVAL;
39933fce1d8SMiquel Raynal 
40033fce1d8SMiquel Raynal 	writel(mxic_spi_prep_hc_cfg(desc->mem->spi, 0), mxic->regs + HC_CFG);
40133fce1d8SMiquel Raynal 
40233fce1d8SMiquel Raynal 	writel(mxic_spi_mem_prep_op_cfg(&desc->info.op_tmpl, len),
40333fce1d8SMiquel Raynal 	       mxic->regs + LRD_CFG);
40433fce1d8SMiquel Raynal 	writel(desc->info.offset + offs, mxic->regs + LRD_ADDR);
40533fce1d8SMiquel Raynal 	len = min_t(size_t, len, mxic->linear.size);
40633fce1d8SMiquel Raynal 	writel(len, mxic->regs + LRD_RANGE);
40733fce1d8SMiquel Raynal 	writel(LMODE_CMD0(desc->info.op_tmpl.cmd.opcode) |
408*9e264f3fSAmit Kumar Mahapatra via Alsa-devel 	       LMODE_SLV_ACT(spi_get_chipselect(desc->mem->spi, 0)) |
40933fce1d8SMiquel Raynal 	       LMODE_EN,
41033fce1d8SMiquel Raynal 	       mxic->regs + LRD_CTRL);
41133fce1d8SMiquel Raynal 
41200360ebaSMiquel Raynal 	if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl.data.ecc) {
41300360ebaSMiquel Raynal 		ret = mxic_ecc_process_data_pipelined(mxic->ecc.pipelined_engine,
41400360ebaSMiquel Raynal 						      NAND_PAGE_READ,
41500360ebaSMiquel Raynal 						      mxic->linear.dma + offs);
41600360ebaSMiquel Raynal 		if (ret)
41700360ebaSMiquel Raynal 			return ret;
41800360ebaSMiquel Raynal 	} else {
41933fce1d8SMiquel Raynal 		memcpy_fromio(buf, mxic->linear.map, len);
42000360ebaSMiquel Raynal 	}
42133fce1d8SMiquel Raynal 
42233fce1d8SMiquel Raynal 	writel(INT_LRD_DIS, mxic->regs + INT_STS);
42333fce1d8SMiquel Raynal 	writel(0, mxic->regs + LRD_CTRL);
42433fce1d8SMiquel Raynal 
42533fce1d8SMiquel Raynal 	ret = readl_poll_timeout(mxic->regs + INT_STS, sts,
42633fce1d8SMiquel Raynal 				 sts & INT_LRD_DIS, 0, USEC_PER_SEC);
42733fce1d8SMiquel Raynal 	if (ret)
42833fce1d8SMiquel Raynal 		return ret;
42933fce1d8SMiquel Raynal 
43033fce1d8SMiquel Raynal 	return len;
43133fce1d8SMiquel Raynal }
43233fce1d8SMiquel Raynal 
mxic_spi_mem_dirmap_write(struct spi_mem_dirmap_desc * desc,u64 offs,size_t len,const void * buf)43333fce1d8SMiquel Raynal static ssize_t mxic_spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
43433fce1d8SMiquel Raynal 					 u64 offs, size_t len,
43533fce1d8SMiquel Raynal 					 const void *buf)
43633fce1d8SMiquel Raynal {
43733fce1d8SMiquel Raynal 	struct mxic_spi *mxic = spi_master_get_devdata(desc->mem->spi->master);
43833fce1d8SMiquel Raynal 	u32 sts;
43933fce1d8SMiquel Raynal 	int ret;
44033fce1d8SMiquel Raynal 
44133fce1d8SMiquel Raynal 	if (WARN_ON(offs + desc->info.offset + len > U32_MAX))
44233fce1d8SMiquel Raynal 		return -EINVAL;
44333fce1d8SMiquel Raynal 
44433fce1d8SMiquel Raynal 	writel(mxic_spi_prep_hc_cfg(desc->mem->spi, 0), mxic->regs + HC_CFG);
44533fce1d8SMiquel Raynal 
44633fce1d8SMiquel Raynal 	writel(mxic_spi_mem_prep_op_cfg(&desc->info.op_tmpl, len),
44733fce1d8SMiquel Raynal 	       mxic->regs + LWR_CFG);
44833fce1d8SMiquel Raynal 	writel(desc->info.offset + offs, mxic->regs + LWR_ADDR);
44933fce1d8SMiquel Raynal 	len = min_t(size_t, len, mxic->linear.size);
45033fce1d8SMiquel Raynal 	writel(len, mxic->regs + LWR_RANGE);
45133fce1d8SMiquel Raynal 	writel(LMODE_CMD0(desc->info.op_tmpl.cmd.opcode) |
452*9e264f3fSAmit Kumar Mahapatra via Alsa-devel 	       LMODE_SLV_ACT(spi_get_chipselect(desc->mem->spi, 0)) |
45333fce1d8SMiquel Raynal 	       LMODE_EN,
45433fce1d8SMiquel Raynal 	       mxic->regs + LWR_CTRL);
45533fce1d8SMiquel Raynal 
45600360ebaSMiquel Raynal 	if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl.data.ecc) {
45700360ebaSMiquel Raynal 		ret = mxic_ecc_process_data_pipelined(mxic->ecc.pipelined_engine,
45800360ebaSMiquel Raynal 						      NAND_PAGE_WRITE,
45900360ebaSMiquel Raynal 						      mxic->linear.dma + offs);
46000360ebaSMiquel Raynal 		if (ret)
46100360ebaSMiquel Raynal 			return ret;
46200360ebaSMiquel Raynal 	} else {
46333fce1d8SMiquel Raynal 		memcpy_toio(mxic->linear.map, buf, len);
46400360ebaSMiquel Raynal 	}
46533fce1d8SMiquel Raynal 
46633fce1d8SMiquel Raynal 	writel(INT_LWR_DIS, mxic->regs + INT_STS);
46733fce1d8SMiquel Raynal 	writel(0, mxic->regs + LWR_CTRL);
46833fce1d8SMiquel Raynal 
46933fce1d8SMiquel Raynal 	ret = readl_poll_timeout(mxic->regs + INT_STS, sts,
47033fce1d8SMiquel Raynal 				 sts & INT_LWR_DIS, 0, USEC_PER_SEC);
47133fce1d8SMiquel Raynal 	if (ret)
47233fce1d8SMiquel Raynal 		return ret;
47333fce1d8SMiquel Raynal 
47433fce1d8SMiquel Raynal 	return len;
47533fce1d8SMiquel Raynal }
47633fce1d8SMiquel Raynal 
mxic_spi_mem_supports_op(struct spi_mem * mem,const struct spi_mem_op * op)477b942d80bSMason Yang static bool mxic_spi_mem_supports_op(struct spi_mem *mem,
478b942d80bSMason Yang 				     const struct spi_mem_op *op)
479b942d80bSMason Yang {
480d05aaa66SZhengxun Li 	if (op->data.buswidth > 8 || op->addr.buswidth > 8 ||
481d05aaa66SZhengxun Li 	    op->dummy.buswidth > 8 || op->cmd.buswidth > 8)
482b942d80bSMason Yang 		return false;
483b942d80bSMason Yang 
484b942d80bSMason Yang 	if (op->data.nbytes && op->dummy.nbytes &&
485b942d80bSMason Yang 	    op->data.buswidth != op->dummy.buswidth)
486b942d80bSMason Yang 		return false;
487b942d80bSMason Yang 
488b942d80bSMason Yang 	if (op->addr.nbytes > 7)
489b942d80bSMason Yang 		return false;
490b942d80bSMason Yang 
491539ff248SMiquel Raynal 	return spi_mem_default_supports_op(mem, op);
492b942d80bSMason Yang }
493b942d80bSMason Yang 
mxic_spi_mem_dirmap_create(struct spi_mem_dirmap_desc * desc)49433fce1d8SMiquel Raynal static int mxic_spi_mem_dirmap_create(struct spi_mem_dirmap_desc *desc)
49533fce1d8SMiquel Raynal {
49633fce1d8SMiquel Raynal 	struct mxic_spi *mxic = spi_master_get_devdata(desc->mem->spi->master);
49733fce1d8SMiquel Raynal 
49833fce1d8SMiquel Raynal 	if (!mxic->linear.map)
49933fce1d8SMiquel Raynal 		return -EINVAL;
50033fce1d8SMiquel Raynal 
50133fce1d8SMiquel Raynal 	if (desc->info.offset + desc->info.length > U32_MAX)
50233fce1d8SMiquel Raynal 		return -EINVAL;
50333fce1d8SMiquel Raynal 
50433fce1d8SMiquel Raynal 	if (!mxic_spi_mem_supports_op(desc->mem, &desc->info.op_tmpl))
50533fce1d8SMiquel Raynal 		return -EOPNOTSUPP;
50633fce1d8SMiquel Raynal 
50733fce1d8SMiquel Raynal 	return 0;
50833fce1d8SMiquel Raynal }
50933fce1d8SMiquel Raynal 
mxic_spi_mem_exec_op(struct spi_mem * mem,const struct spi_mem_op * op)510b942d80bSMason Yang static int mxic_spi_mem_exec_op(struct spi_mem *mem,
511b942d80bSMason Yang 				const struct spi_mem_op *op)
512b942d80bSMason Yang {
513b942d80bSMason Yang 	struct mxic_spi *mxic = spi_master_get_devdata(mem->spi->master);
514b7b64db7SMiquel Raynal 	int i, ret;
515d05aaa66SZhengxun Li 	u8 addr[8], cmd[2];
516b942d80bSMason Yang 
517b942d80bSMason Yang 	ret = mxic_spi_set_freq(mxic, mem->spi->max_speed_hz);
518b942d80bSMason Yang 	if (ret)
519b942d80bSMason Yang 		return ret;
520b942d80bSMason Yang 
521b7b64db7SMiquel Raynal 	writel(mxic_spi_prep_hc_cfg(mem->spi, HC_CFG_MAN_CS_EN),
522b942d80bSMason Yang 	       mxic->regs + HC_CFG);
523b7b64db7SMiquel Raynal 
524b942d80bSMason Yang 	writel(HC_EN_BIT, mxic->regs + HC_EN);
525b942d80bSMason Yang 
52633fce1d8SMiquel Raynal 	writel(mxic_spi_mem_prep_op_cfg(op, op->data.nbytes),
527*9e264f3fSAmit Kumar Mahapatra via Alsa-devel 	       mxic->regs + SS_CTRL(spi_get_chipselect(mem->spi, 0)));
528b942d80bSMason Yang 
529b942d80bSMason Yang 	writel(readl(mxic->regs + HC_CFG) | HC_CFG_MAN_CS_ASSERT,
530b942d80bSMason Yang 	       mxic->regs + HC_CFG);
531b942d80bSMason Yang 
532d05aaa66SZhengxun Li 	for (i = 0; i < op->cmd.nbytes; i++)
533d05aaa66SZhengxun Li 		cmd[i] = op->cmd.opcode >> (8 * (op->cmd.nbytes - i - 1));
534d05aaa66SZhengxun Li 
535d05aaa66SZhengxun Li 	ret = mxic_spi_data_xfer(mxic, cmd, NULL, op->cmd.nbytes);
536b942d80bSMason Yang 	if (ret)
537b942d80bSMason Yang 		goto out;
538b942d80bSMason Yang 
539b942d80bSMason Yang 	for (i = 0; i < op->addr.nbytes; i++)
540b942d80bSMason Yang 		addr[i] = op->addr.val >> (8 * (op->addr.nbytes - i - 1));
541b942d80bSMason Yang 
542b942d80bSMason Yang 	ret = mxic_spi_data_xfer(mxic, addr, NULL, op->addr.nbytes);
543b942d80bSMason Yang 	if (ret)
544b942d80bSMason Yang 		goto out;
545b942d80bSMason Yang 
546b942d80bSMason Yang 	ret = mxic_spi_data_xfer(mxic, NULL, NULL, op->dummy.nbytes);
547b942d80bSMason Yang 	if (ret)
548b942d80bSMason Yang 		goto out;
549b942d80bSMason Yang 
550b942d80bSMason Yang 	ret = mxic_spi_data_xfer(mxic,
551b942d80bSMason Yang 				 op->data.dir == SPI_MEM_DATA_OUT ?
552b942d80bSMason Yang 				 op->data.buf.out : NULL,
553b942d80bSMason Yang 				 op->data.dir == SPI_MEM_DATA_IN ?
554b942d80bSMason Yang 				 op->data.buf.in : NULL,
555b942d80bSMason Yang 				 op->data.nbytes);
556b942d80bSMason Yang 
557b942d80bSMason Yang out:
558b942d80bSMason Yang 	writel(readl(mxic->regs + HC_CFG) & ~HC_CFG_MAN_CS_ASSERT,
559b942d80bSMason Yang 	       mxic->regs + HC_CFG);
560b942d80bSMason Yang 	writel(0, mxic->regs + HC_EN);
561b942d80bSMason Yang 
562b942d80bSMason Yang 	return ret;
563b942d80bSMason Yang }
564b942d80bSMason Yang 
565b942d80bSMason Yang static const struct spi_controller_mem_ops mxic_spi_mem_ops = {
566b942d80bSMason Yang 	.supports_op = mxic_spi_mem_supports_op,
567b942d80bSMason Yang 	.exec_op = mxic_spi_mem_exec_op,
56833fce1d8SMiquel Raynal 	.dirmap_create = mxic_spi_mem_dirmap_create,
56933fce1d8SMiquel Raynal 	.dirmap_read = mxic_spi_mem_dirmap_read,
57033fce1d8SMiquel Raynal 	.dirmap_write = mxic_spi_mem_dirmap_write,
571b942d80bSMason Yang };
572b942d80bSMason Yang 
5730e450c7cSMiquel Raynal static const struct spi_controller_mem_caps mxic_spi_mem_caps = {
5740e450c7cSMiquel Raynal 	.dtr = true,
57500360ebaSMiquel Raynal 	.ecc = true,
5760e450c7cSMiquel Raynal };
5770e450c7cSMiquel Raynal 
mxic_spi_set_cs(struct spi_device * spi,bool lvl)578b942d80bSMason Yang static void mxic_spi_set_cs(struct spi_device *spi, bool lvl)
579b942d80bSMason Yang {
580b942d80bSMason Yang 	struct mxic_spi *mxic = spi_master_get_devdata(spi->master);
581b942d80bSMason Yang 
582b942d80bSMason Yang 	if (!lvl) {
583b942d80bSMason Yang 		writel(readl(mxic->regs + HC_CFG) | HC_CFG_MAN_CS_EN,
584b942d80bSMason Yang 		       mxic->regs + HC_CFG);
585b942d80bSMason Yang 		writel(HC_EN_BIT, mxic->regs + HC_EN);
586b942d80bSMason Yang 		writel(readl(mxic->regs + HC_CFG) | HC_CFG_MAN_CS_ASSERT,
587b942d80bSMason Yang 		       mxic->regs + HC_CFG);
588b942d80bSMason Yang 	} else {
589b942d80bSMason Yang 		writel(readl(mxic->regs + HC_CFG) & ~HC_CFG_MAN_CS_ASSERT,
590b942d80bSMason Yang 		       mxic->regs + HC_CFG);
591b942d80bSMason Yang 		writel(0, mxic->regs + HC_EN);
592b942d80bSMason Yang 	}
593b942d80bSMason Yang }
594b942d80bSMason Yang 
mxic_spi_transfer_one(struct spi_master * master,struct spi_device * spi,struct spi_transfer * t)595b942d80bSMason Yang static int mxic_spi_transfer_one(struct spi_master *master,
596b942d80bSMason Yang 				 struct spi_device *spi,
597b942d80bSMason Yang 				 struct spi_transfer *t)
598b942d80bSMason Yang {
599b942d80bSMason Yang 	struct mxic_spi *mxic = spi_master_get_devdata(master);
600b942d80bSMason Yang 	unsigned int busw = OP_BUSW_1;
601b942d80bSMason Yang 	int ret;
602b942d80bSMason Yang 
603b942d80bSMason Yang 	if (t->rx_buf && t->tx_buf) {
604b942d80bSMason Yang 		if (((spi->mode & SPI_TX_QUAD) &&
605b942d80bSMason Yang 		     !(spi->mode & SPI_RX_QUAD)) ||
606b942d80bSMason Yang 		    ((spi->mode & SPI_TX_DUAL) &&
607b942d80bSMason Yang 		     !(spi->mode & SPI_RX_DUAL)))
608b942d80bSMason Yang 			return -ENOTSUPP;
609b942d80bSMason Yang 	}
610b942d80bSMason Yang 
611b942d80bSMason Yang 	ret = mxic_spi_set_freq(mxic, t->speed_hz);
612b942d80bSMason Yang 	if (ret)
613b942d80bSMason Yang 		return ret;
614b942d80bSMason Yang 
615b942d80bSMason Yang 	if (t->tx_buf) {
616b942d80bSMason Yang 		if (spi->mode & SPI_TX_QUAD)
617b942d80bSMason Yang 			busw = OP_BUSW_4;
618b942d80bSMason Yang 		else if (spi->mode & SPI_TX_DUAL)
619b942d80bSMason Yang 			busw = OP_BUSW_2;
620b942d80bSMason Yang 	} else if (t->rx_buf) {
621b942d80bSMason Yang 		if (spi->mode & SPI_RX_QUAD)
622b942d80bSMason Yang 			busw = OP_BUSW_4;
623b942d80bSMason Yang 		else if (spi->mode & SPI_RX_DUAL)
624b942d80bSMason Yang 			busw = OP_BUSW_2;
625b942d80bSMason Yang 	}
626b942d80bSMason Yang 
627b942d80bSMason Yang 	writel(OP_CMD_BYTES(1) | OP_CMD_BUSW(busw) |
628b942d80bSMason Yang 	       OP_DATA_BUSW(busw) | (t->rx_buf ? OP_READ : 0),
629b942d80bSMason Yang 	       mxic->regs + SS_CTRL(0));
630b942d80bSMason Yang 
631b942d80bSMason Yang 	ret = mxic_spi_data_xfer(mxic, t->tx_buf, t->rx_buf, t->len);
632b942d80bSMason Yang 	if (ret)
633b942d80bSMason Yang 		return ret;
634b942d80bSMason Yang 
635b942d80bSMason Yang 	spi_finalize_current_transfer(master);
636b942d80bSMason Yang 
637b942d80bSMason Yang 	return 0;
638b942d80bSMason Yang }
639b942d80bSMason Yang 
64000360ebaSMiquel Raynal /* ECC wrapper */
mxic_spi_mem_ecc_init_ctx(struct nand_device * nand)64100360ebaSMiquel Raynal static int mxic_spi_mem_ecc_init_ctx(struct nand_device *nand)
64200360ebaSMiquel Raynal {
64300360ebaSMiquel Raynal 	struct nand_ecc_engine_ops *ops = mxic_ecc_get_pipelined_ops();
64400360ebaSMiquel Raynal 	struct mxic_spi *mxic = nand->ecc.engine->priv;
64500360ebaSMiquel Raynal 
64600360ebaSMiquel Raynal 	mxic->ecc.use_pipelined_conf = true;
64700360ebaSMiquel Raynal 
64800360ebaSMiquel Raynal 	return ops->init_ctx(nand);
64900360ebaSMiquel Raynal }
65000360ebaSMiquel Raynal 
mxic_spi_mem_ecc_cleanup_ctx(struct nand_device * nand)65100360ebaSMiquel Raynal static void mxic_spi_mem_ecc_cleanup_ctx(struct nand_device *nand)
65200360ebaSMiquel Raynal {
65300360ebaSMiquel Raynal 	struct nand_ecc_engine_ops *ops = mxic_ecc_get_pipelined_ops();
65400360ebaSMiquel Raynal 	struct mxic_spi *mxic = nand->ecc.engine->priv;
65500360ebaSMiquel Raynal 
65600360ebaSMiquel Raynal 	mxic->ecc.use_pipelined_conf = false;
65700360ebaSMiquel Raynal 
65800360ebaSMiquel Raynal 	ops->cleanup_ctx(nand);
65900360ebaSMiquel Raynal }
66000360ebaSMiquel Raynal 
mxic_spi_mem_ecc_prepare_io_req(struct nand_device * nand,struct nand_page_io_req * req)66100360ebaSMiquel Raynal static int mxic_spi_mem_ecc_prepare_io_req(struct nand_device *nand,
66200360ebaSMiquel Raynal 					   struct nand_page_io_req *req)
66300360ebaSMiquel Raynal {
66400360ebaSMiquel Raynal 	struct nand_ecc_engine_ops *ops = mxic_ecc_get_pipelined_ops();
66500360ebaSMiquel Raynal 
66600360ebaSMiquel Raynal 	return ops->prepare_io_req(nand, req);
66700360ebaSMiquel Raynal }
66800360ebaSMiquel Raynal 
mxic_spi_mem_ecc_finish_io_req(struct nand_device * nand,struct nand_page_io_req * req)66900360ebaSMiquel Raynal static int mxic_spi_mem_ecc_finish_io_req(struct nand_device *nand,
67000360ebaSMiquel Raynal 					  struct nand_page_io_req *req)
67100360ebaSMiquel Raynal {
67200360ebaSMiquel Raynal 	struct nand_ecc_engine_ops *ops = mxic_ecc_get_pipelined_ops();
67300360ebaSMiquel Raynal 
67400360ebaSMiquel Raynal 	return ops->finish_io_req(nand, req);
67500360ebaSMiquel Raynal }
67600360ebaSMiquel Raynal 
67700360ebaSMiquel Raynal static struct nand_ecc_engine_ops mxic_spi_mem_ecc_engine_pipelined_ops = {
67800360ebaSMiquel Raynal 	.init_ctx = mxic_spi_mem_ecc_init_ctx,
67900360ebaSMiquel Raynal 	.cleanup_ctx = mxic_spi_mem_ecc_cleanup_ctx,
68000360ebaSMiquel Raynal 	.prepare_io_req = mxic_spi_mem_ecc_prepare_io_req,
68100360ebaSMiquel Raynal 	.finish_io_req = mxic_spi_mem_ecc_finish_io_req,
68200360ebaSMiquel Raynal };
68300360ebaSMiquel Raynal 
mxic_spi_mem_ecc_remove(struct mxic_spi * mxic)68400360ebaSMiquel Raynal static void mxic_spi_mem_ecc_remove(struct mxic_spi *mxic)
68500360ebaSMiquel Raynal {
68600360ebaSMiquel Raynal 	if (mxic->ecc.pipelined_engine) {
68700360ebaSMiquel Raynal 		mxic_ecc_put_pipelined_engine(mxic->ecc.pipelined_engine);
68800360ebaSMiquel Raynal 		nand_ecc_unregister_on_host_hw_engine(mxic->ecc.pipelined_engine);
68900360ebaSMiquel Raynal 	}
69000360ebaSMiquel Raynal }
69100360ebaSMiquel Raynal 
mxic_spi_mem_ecc_probe(struct platform_device * pdev,struct mxic_spi * mxic)69200360ebaSMiquel Raynal static int mxic_spi_mem_ecc_probe(struct platform_device *pdev,
69300360ebaSMiquel Raynal 				  struct mxic_spi *mxic)
69400360ebaSMiquel Raynal {
69500360ebaSMiquel Raynal 	struct nand_ecc_engine *eng;
69600360ebaSMiquel Raynal 
69700360ebaSMiquel Raynal 	if (!mxic_ecc_get_pipelined_ops())
69800360ebaSMiquel Raynal 		return -EOPNOTSUPP;
69900360ebaSMiquel Raynal 
70000360ebaSMiquel Raynal 	eng = mxic_ecc_get_pipelined_engine(pdev);
70100360ebaSMiquel Raynal 	if (IS_ERR(eng))
70200360ebaSMiquel Raynal 		return PTR_ERR(eng);
70300360ebaSMiquel Raynal 
70400360ebaSMiquel Raynal 	eng->dev = &pdev->dev;
70500360ebaSMiquel Raynal 	eng->integration = NAND_ECC_ENGINE_INTEGRATION_PIPELINED;
70600360ebaSMiquel Raynal 	eng->ops = &mxic_spi_mem_ecc_engine_pipelined_ops;
70700360ebaSMiquel Raynal 	eng->priv = mxic;
70800360ebaSMiquel Raynal 	mxic->ecc.pipelined_engine = eng;
70900360ebaSMiquel Raynal 	nand_ecc_register_on_host_hw_engine(eng);
71000360ebaSMiquel Raynal 
71100360ebaSMiquel Raynal 	return 0;
71200360ebaSMiquel Raynal }
71300360ebaSMiquel Raynal 
mxic_spi_runtime_suspend(struct device * dev)714b942d80bSMason Yang static int __maybe_unused mxic_spi_runtime_suspend(struct device *dev)
715b942d80bSMason Yang {
716f457cb70SWolfram Sang 	struct spi_master *master = dev_get_drvdata(dev);
717b942d80bSMason Yang 	struct mxic_spi *mxic = spi_master_get_devdata(master);
718b942d80bSMason Yang 
719b942d80bSMason Yang 	mxic_spi_clk_disable(mxic);
720b942d80bSMason Yang 	clk_disable_unprepare(mxic->ps_clk);
721b942d80bSMason Yang 
722b942d80bSMason Yang 	return 0;
723b942d80bSMason Yang }
724b942d80bSMason Yang 
mxic_spi_runtime_resume(struct device * dev)725b942d80bSMason Yang static int __maybe_unused mxic_spi_runtime_resume(struct device *dev)
726b942d80bSMason Yang {
727f457cb70SWolfram Sang 	struct spi_master *master = dev_get_drvdata(dev);
728b942d80bSMason Yang 	struct mxic_spi *mxic = spi_master_get_devdata(master);
729b942d80bSMason Yang 	int ret;
730b942d80bSMason Yang 
731b942d80bSMason Yang 	ret = clk_prepare_enable(mxic->ps_clk);
732b942d80bSMason Yang 	if (ret) {
733b942d80bSMason Yang 		dev_err(dev, "Cannot enable ps_clock.\n");
734b942d80bSMason Yang 		return ret;
735b942d80bSMason Yang 	}
736b942d80bSMason Yang 
737b942d80bSMason Yang 	return mxic_spi_clk_enable(mxic);
738b942d80bSMason Yang }
739b942d80bSMason Yang 
740b942d80bSMason Yang static const struct dev_pm_ops mxic_spi_dev_pm_ops = {
741b942d80bSMason Yang 	SET_RUNTIME_PM_OPS(mxic_spi_runtime_suspend,
742b942d80bSMason Yang 			   mxic_spi_runtime_resume, NULL)
743b942d80bSMason Yang };
744b942d80bSMason Yang 
mxic_spi_probe(struct platform_device * pdev)745b942d80bSMason Yang static int mxic_spi_probe(struct platform_device *pdev)
746b942d80bSMason Yang {
747b942d80bSMason Yang 	struct spi_master *master;
748b942d80bSMason Yang 	struct resource *res;
749b942d80bSMason Yang 	struct mxic_spi *mxic;
750b942d80bSMason Yang 	int ret;
751b942d80bSMason Yang 
752cc53711bSLukas Wunner 	master = devm_spi_alloc_master(&pdev->dev, sizeof(struct mxic_spi));
753b942d80bSMason Yang 	if (!master)
754b942d80bSMason Yang 		return -ENOMEM;
755b942d80bSMason Yang 
756b942d80bSMason Yang 	platform_set_drvdata(pdev, master);
757b942d80bSMason Yang 
758b942d80bSMason Yang 	mxic = spi_master_get_devdata(master);
75900360ebaSMiquel Raynal 	mxic->dev = &pdev->dev;
760b942d80bSMason Yang 
761b942d80bSMason Yang 	master->dev.of_node = pdev->dev.of_node;
762b942d80bSMason Yang 
763b942d80bSMason Yang 	mxic->ps_clk = devm_clk_get(&pdev->dev, "ps_clk");
764b942d80bSMason Yang 	if (IS_ERR(mxic->ps_clk))
765b942d80bSMason Yang 		return PTR_ERR(mxic->ps_clk);
766b942d80bSMason Yang 
767b942d80bSMason Yang 	mxic->send_clk = devm_clk_get(&pdev->dev, "send_clk");
768b942d80bSMason Yang 	if (IS_ERR(mxic->send_clk))
769b942d80bSMason Yang 		return PTR_ERR(mxic->send_clk);
770b942d80bSMason Yang 
771b942d80bSMason Yang 	mxic->send_dly_clk = devm_clk_get(&pdev->dev, "send_dly_clk");
772b942d80bSMason Yang 	if (IS_ERR(mxic->send_dly_clk))
773b942d80bSMason Yang 		return PTR_ERR(mxic->send_dly_clk);
774b942d80bSMason Yang 
775347ad8f2SYang Yingliang 	mxic->regs = devm_platform_ioremap_resource_byname(pdev, "regs");
776b942d80bSMason Yang 	if (IS_ERR(mxic->regs))
777b942d80bSMason Yang 		return PTR_ERR(mxic->regs);
778b942d80bSMason Yang 
77933fce1d8SMiquel Raynal 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dirmap");
78033fce1d8SMiquel Raynal 	mxic->linear.map = devm_ioremap_resource(&pdev->dev, res);
78133fce1d8SMiquel Raynal 	if (!IS_ERR(mxic->linear.map)) {
78233fce1d8SMiquel Raynal 		mxic->linear.dma = res->start;
78333fce1d8SMiquel Raynal 		mxic->linear.size = resource_size(res);
78433fce1d8SMiquel Raynal 	} else {
78533fce1d8SMiquel Raynal 		mxic->linear.map = NULL;
78633fce1d8SMiquel Raynal 	}
78733fce1d8SMiquel Raynal 
788b942d80bSMason Yang 	pm_runtime_enable(&pdev->dev);
789b942d80bSMason Yang 	master->auto_runtime_pm = true;
790b942d80bSMason Yang 
791b942d80bSMason Yang 	master->num_chipselect = 1;
792b942d80bSMason Yang 	master->mem_ops = &mxic_spi_mem_ops;
7930e450c7cSMiquel Raynal 	master->mem_caps = &mxic_spi_mem_caps;
794b942d80bSMason Yang 
795b942d80bSMason Yang 	master->set_cs = mxic_spi_set_cs;
796b942d80bSMason Yang 	master->transfer_one = mxic_spi_transfer_one;
797b942d80bSMason Yang 	master->bits_per_word_mask = SPI_BPW_MASK(8);
798b942d80bSMason Yang 	master->mode_bits = SPI_CPOL | SPI_CPHA |
799b942d80bSMason Yang 			SPI_RX_DUAL | SPI_TX_DUAL |
800d05aaa66SZhengxun Li 			SPI_RX_QUAD | SPI_TX_QUAD |
801d05aaa66SZhengxun Li 			SPI_RX_OCTAL | SPI_TX_OCTAL;
802b942d80bSMason Yang 
803b942d80bSMason Yang 	mxic_spi_hw_init(mxic);
804b942d80bSMason Yang 
80500360ebaSMiquel Raynal 	ret = mxic_spi_mem_ecc_probe(pdev, mxic);
80600360ebaSMiquel Raynal 	if (ret == -EPROBE_DEFER) {
80700360ebaSMiquel Raynal 		pm_runtime_disable(&pdev->dev);
80800360ebaSMiquel Raynal 		return ret;
80900360ebaSMiquel Raynal 	}
81000360ebaSMiquel Raynal 
811b942d80bSMason Yang 	ret = spi_register_master(master);
812b942d80bSMason Yang 	if (ret) {
813b942d80bSMason Yang 		dev_err(&pdev->dev, "spi_register_master failed\n");
814b942d80bSMason Yang 		pm_runtime_disable(&pdev->dev);
81535d516bdSChristophe JAILLET 		mxic_spi_mem_ecc_remove(mxic);
816cc53711bSLukas Wunner 	}
817b942d80bSMason Yang 
818b942d80bSMason Yang 	return ret;
819b942d80bSMason Yang }
820b942d80bSMason Yang 
mxic_spi_remove(struct platform_device * pdev)821c64e92dfSUwe Kleine-König static void mxic_spi_remove(struct platform_device *pdev)
822b942d80bSMason Yang {
823b942d80bSMason Yang 	struct spi_master *master = platform_get_drvdata(pdev);
82400360ebaSMiquel Raynal 	struct mxic_spi *mxic = spi_master_get_devdata(master);
825b942d80bSMason Yang 
826b942d80bSMason Yang 	pm_runtime_disable(&pdev->dev);
82700360ebaSMiquel Raynal 	mxic_spi_mem_ecc_remove(mxic);
828b942d80bSMason Yang 	spi_unregister_master(master);
829b942d80bSMason Yang }
830b942d80bSMason Yang 
831b942d80bSMason Yang static const struct of_device_id mxic_spi_of_ids[] = {
832b942d80bSMason Yang 	{ .compatible = "mxicy,mx25f0a-spi", },
833b942d80bSMason Yang 	{ /* sentinel */ }
834b942d80bSMason Yang };
835b942d80bSMason Yang MODULE_DEVICE_TABLE(of, mxic_spi_of_ids);
836b942d80bSMason Yang 
837b942d80bSMason Yang static struct platform_driver mxic_spi_driver = {
838b942d80bSMason Yang 	.probe = mxic_spi_probe,
839c64e92dfSUwe Kleine-König 	.remove_new = mxic_spi_remove,
840b942d80bSMason Yang 	.driver = {
841b942d80bSMason Yang 		.name = "mxic-spi",
842b942d80bSMason Yang 		.of_match_table = mxic_spi_of_ids,
843b942d80bSMason Yang 		.pm = &mxic_spi_dev_pm_ops,
844b942d80bSMason Yang 	},
845b942d80bSMason Yang };
846b942d80bSMason Yang module_platform_driver(mxic_spi_driver);
847b942d80bSMason Yang 
848b942d80bSMason Yang MODULE_AUTHOR("Mason Yang <masonccyang@mxic.com.tw>");
849b942d80bSMason Yang MODULE_DESCRIPTION("MX25F0A SPI controller driver");
850b942d80bSMason Yang MODULE_LICENSE("GPL v2");
851