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