xref: /linux/drivers/mtd/spi-nor/controllers/hisi-sfc.c (revision 1ac71ec0130cce5bed3ec11ffc88651097a24173)
11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2e523f111SJiancheng Xue /*
3*1ac71ec0STudor Ambarus  * HiSilicon FMC SPI NOR flash controller driver
4e523f111SJiancheng Xue  *
5e523f111SJiancheng Xue  * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd.
6e523f111SJiancheng Xue  */
7e523f111SJiancheng Xue #include <linux/bitops.h>
8e523f111SJiancheng Xue #include <linux/clk.h>
9e523f111SJiancheng Xue #include <linux/dma-mapping.h>
10e523f111SJiancheng Xue #include <linux/iopoll.h>
11e523f111SJiancheng Xue #include <linux/module.h>
12e523f111SJiancheng Xue #include <linux/mtd/mtd.h>
13e523f111SJiancheng Xue #include <linux/mtd/spi-nor.h>
14e523f111SJiancheng Xue #include <linux/of.h>
15e523f111SJiancheng Xue #include <linux/platform_device.h>
16e523f111SJiancheng Xue #include <linux/slab.h>
17e523f111SJiancheng Xue 
18e523f111SJiancheng Xue /* Hardware register offsets and field definitions */
19e523f111SJiancheng Xue #define FMC_CFG				0x00
20e523f111SJiancheng Xue #define FMC_CFG_OP_MODE_MASK		BIT_MASK(0)
21e523f111SJiancheng Xue #define FMC_CFG_OP_MODE_BOOT		0
22e523f111SJiancheng Xue #define FMC_CFG_OP_MODE_NORMAL		1
23e523f111SJiancheng Xue #define FMC_CFG_FLASH_SEL(type)		(((type) & 0x3) << 1)
24e523f111SJiancheng Xue #define FMC_CFG_FLASH_SEL_MASK		0x6
25e523f111SJiancheng Xue #define FMC_ECC_TYPE(type)		(((type) & 0x7) << 5)
26e523f111SJiancheng Xue #define FMC_ECC_TYPE_MASK		GENMASK(7, 5)
27e523f111SJiancheng Xue #define SPI_NOR_ADDR_MODE_MASK		BIT_MASK(10)
28e523f111SJiancheng Xue #define SPI_NOR_ADDR_MODE_3BYTES	(0x0 << 10)
29e523f111SJiancheng Xue #define SPI_NOR_ADDR_MODE_4BYTES	(0x1 << 10)
30e523f111SJiancheng Xue #define FMC_GLOBAL_CFG			0x04
31e523f111SJiancheng Xue #define FMC_GLOBAL_CFG_WP_ENABLE	BIT(6)
32e523f111SJiancheng Xue #define FMC_SPI_TIMING_CFG		0x08
33e523f111SJiancheng Xue #define TIMING_CFG_TCSH(nr)		(((nr) & 0xf) << 8)
34e523f111SJiancheng Xue #define TIMING_CFG_TCSS(nr)		(((nr) & 0xf) << 4)
35e523f111SJiancheng Xue #define TIMING_CFG_TSHSL(nr)		((nr) & 0xf)
36e523f111SJiancheng Xue #define CS_HOLD_TIME			0x6
37e523f111SJiancheng Xue #define CS_SETUP_TIME			0x6
38e523f111SJiancheng Xue #define CS_DESELECT_TIME		0xf
39e523f111SJiancheng Xue #define FMC_INT				0x18
40e523f111SJiancheng Xue #define FMC_INT_OP_DONE			BIT(0)
41e523f111SJiancheng Xue #define FMC_INT_CLR			0x20
42e523f111SJiancheng Xue #define FMC_CMD				0x24
43e523f111SJiancheng Xue #define FMC_CMD_CMD1(cmd)		((cmd) & 0xff)
44e523f111SJiancheng Xue #define FMC_ADDRL			0x2c
45e523f111SJiancheng Xue #define FMC_OP_CFG			0x30
46e523f111SJiancheng Xue #define OP_CFG_FM_CS(cs)		((cs) << 11)
47e523f111SJiancheng Xue #define OP_CFG_MEM_IF_TYPE(type)	(((type) & 0x7) << 7)
48e523f111SJiancheng Xue #define OP_CFG_ADDR_NUM(addr)		(((addr) & 0x7) << 4)
49e523f111SJiancheng Xue #define OP_CFG_DUMMY_NUM(dummy)		((dummy) & 0xf)
50e523f111SJiancheng Xue #define FMC_DATA_NUM			0x38
51e523f111SJiancheng Xue #define FMC_DATA_NUM_CNT(cnt)		((cnt) & GENMASK(13, 0))
52e523f111SJiancheng Xue #define FMC_OP				0x3c
53e523f111SJiancheng Xue #define FMC_OP_DUMMY_EN			BIT(8)
54e523f111SJiancheng Xue #define FMC_OP_CMD1_EN			BIT(7)
55e523f111SJiancheng Xue #define FMC_OP_ADDR_EN			BIT(6)
56e523f111SJiancheng Xue #define FMC_OP_WRITE_DATA_EN		BIT(5)
57e523f111SJiancheng Xue #define FMC_OP_READ_DATA_EN		BIT(2)
58e523f111SJiancheng Xue #define FMC_OP_READ_STATUS_EN		BIT(1)
59e523f111SJiancheng Xue #define FMC_OP_REG_OP_START		BIT(0)
60e523f111SJiancheng Xue #define FMC_DMA_LEN			0x40
61e523f111SJiancheng Xue #define FMC_DMA_LEN_SET(len)		((len) & GENMASK(27, 0))
62e523f111SJiancheng Xue #define FMC_DMA_SADDR_D0		0x4c
63e523f111SJiancheng Xue #define HIFMC_DMA_MAX_LEN		(4096)
64e523f111SJiancheng Xue #define HIFMC_DMA_MASK			(HIFMC_DMA_MAX_LEN - 1)
65e523f111SJiancheng Xue #define FMC_OP_DMA			0x68
66e523f111SJiancheng Xue #define OP_CTRL_RD_OPCODE(code)		(((code) & 0xff) << 16)
67e523f111SJiancheng Xue #define OP_CTRL_WR_OPCODE(code)		(((code) & 0xff) << 8)
68e523f111SJiancheng Xue #define OP_CTRL_RW_OP(op)		((op) << 1)
69e523f111SJiancheng Xue #define OP_CTRL_DMA_OP_READY		BIT(0)
70e523f111SJiancheng Xue #define FMC_OP_READ			0x0
71e523f111SJiancheng Xue #define FMC_OP_WRITE			0x1
72e523f111SJiancheng Xue #define FMC_WAIT_TIMEOUT		1000000
73e523f111SJiancheng Xue 
74e523f111SJiancheng Xue enum hifmc_iftype {
75e523f111SJiancheng Xue 	IF_TYPE_STD,
76e523f111SJiancheng Xue 	IF_TYPE_DUAL,
77e523f111SJiancheng Xue 	IF_TYPE_DIO,
78e523f111SJiancheng Xue 	IF_TYPE_QUAD,
79e523f111SJiancheng Xue 	IF_TYPE_QIO,
80e523f111SJiancheng Xue };
81e523f111SJiancheng Xue 
82e523f111SJiancheng Xue struct hifmc_priv {
83e523f111SJiancheng Xue 	u32 chipselect;
84e523f111SJiancheng Xue 	u32 clkrate;
85e523f111SJiancheng Xue 	struct hifmc_host *host;
86e523f111SJiancheng Xue };
87e523f111SJiancheng Xue 
88e523f111SJiancheng Xue #define HIFMC_MAX_CHIP_NUM		2
89e523f111SJiancheng Xue struct hifmc_host {
90e523f111SJiancheng Xue 	struct device *dev;
91e523f111SJiancheng Xue 	struct mutex lock;
92e523f111SJiancheng Xue 
93e523f111SJiancheng Xue 	void __iomem *regbase;
94e523f111SJiancheng Xue 	void __iomem *iobase;
95e523f111SJiancheng Xue 	struct clk *clk;
96e523f111SJiancheng Xue 	void *buffer;
97e523f111SJiancheng Xue 	dma_addr_t dma_buffer;
98e523f111SJiancheng Xue 
99e523f111SJiancheng Xue 	struct spi_nor	*nor[HIFMC_MAX_CHIP_NUM];
100e523f111SJiancheng Xue 	u32 num_chip;
101e523f111SJiancheng Xue };
102e523f111SJiancheng Xue 
10364070249SEzequiel Garcia static inline int hisi_spi_nor_wait_op_finish(struct hifmc_host *host)
104e523f111SJiancheng Xue {
105e523f111SJiancheng Xue 	u32 reg;
106e523f111SJiancheng Xue 
107e523f111SJiancheng Xue 	return readl_poll_timeout(host->regbase + FMC_INT, reg,
108e523f111SJiancheng Xue 		(reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT);
109e523f111SJiancheng Xue }
110e523f111SJiancheng Xue 
11164070249SEzequiel Garcia static int hisi_spi_nor_get_if_type(enum spi_nor_protocol proto)
112e523f111SJiancheng Xue {
113e523f111SJiancheng Xue 	enum hifmc_iftype if_type;
114e523f111SJiancheng Xue 
115cfc5604cSCyrille Pitchen 	switch (proto) {
116cfc5604cSCyrille Pitchen 	case SNOR_PROTO_1_1_2:
117e523f111SJiancheng Xue 		if_type = IF_TYPE_DUAL;
118e523f111SJiancheng Xue 		break;
119cfc5604cSCyrille Pitchen 	case SNOR_PROTO_1_2_2:
120cfc5604cSCyrille Pitchen 		if_type = IF_TYPE_DIO;
121cfc5604cSCyrille Pitchen 		break;
122cfc5604cSCyrille Pitchen 	case SNOR_PROTO_1_1_4:
123e523f111SJiancheng Xue 		if_type = IF_TYPE_QUAD;
124e523f111SJiancheng Xue 		break;
125cfc5604cSCyrille Pitchen 	case SNOR_PROTO_1_4_4:
126cfc5604cSCyrille Pitchen 		if_type = IF_TYPE_QIO;
127cfc5604cSCyrille Pitchen 		break;
128cfc5604cSCyrille Pitchen 	case SNOR_PROTO_1_1_1:
129e523f111SJiancheng Xue 	default:
130e523f111SJiancheng Xue 		if_type = IF_TYPE_STD;
131e523f111SJiancheng Xue 		break;
132e523f111SJiancheng Xue 	}
133e523f111SJiancheng Xue 
134e523f111SJiancheng Xue 	return if_type;
135e523f111SJiancheng Xue }
136e523f111SJiancheng Xue 
137e523f111SJiancheng Xue static void hisi_spi_nor_init(struct hifmc_host *host)
138e523f111SJiancheng Xue {
139e523f111SJiancheng Xue 	u32 reg;
140e523f111SJiancheng Xue 
141e523f111SJiancheng Xue 	reg = TIMING_CFG_TCSH(CS_HOLD_TIME)
142e523f111SJiancheng Xue 		| TIMING_CFG_TCSS(CS_SETUP_TIME)
143e523f111SJiancheng Xue 		| TIMING_CFG_TSHSL(CS_DESELECT_TIME);
144e523f111SJiancheng Xue 	writel(reg, host->regbase + FMC_SPI_TIMING_CFG);
145e523f111SJiancheng Xue }
146e523f111SJiancheng Xue 
14752bbd2dcSMichael Walle static int hisi_spi_nor_prep(struct spi_nor *nor)
148e523f111SJiancheng Xue {
149e523f111SJiancheng Xue 	struct hifmc_priv *priv = nor->priv;
150e523f111SJiancheng Xue 	struct hifmc_host *host = priv->host;
151e523f111SJiancheng Xue 	int ret;
152e523f111SJiancheng Xue 
153e523f111SJiancheng Xue 	mutex_lock(&host->lock);
154e523f111SJiancheng Xue 
155e523f111SJiancheng Xue 	ret = clk_set_rate(host->clk, priv->clkrate);
156e523f111SJiancheng Xue 	if (ret)
157e523f111SJiancheng Xue 		goto out;
158e523f111SJiancheng Xue 
159e523f111SJiancheng Xue 	ret = clk_prepare_enable(host->clk);
160e523f111SJiancheng Xue 	if (ret)
161e523f111SJiancheng Xue 		goto out;
162e523f111SJiancheng Xue 
163e523f111SJiancheng Xue 	return 0;
164e523f111SJiancheng Xue 
165e523f111SJiancheng Xue out:
166e523f111SJiancheng Xue 	mutex_unlock(&host->lock);
167e523f111SJiancheng Xue 	return ret;
168e523f111SJiancheng Xue }
169e523f111SJiancheng Xue 
17052bbd2dcSMichael Walle static void hisi_spi_nor_unprep(struct spi_nor *nor)
171e523f111SJiancheng Xue {
172e523f111SJiancheng Xue 	struct hifmc_priv *priv = nor->priv;
173e523f111SJiancheng Xue 	struct hifmc_host *host = priv->host;
174e523f111SJiancheng Xue 
175e523f111SJiancheng Xue 	clk_disable_unprepare(host->clk);
176e523f111SJiancheng Xue 	mutex_unlock(&host->lock);
177e523f111SJiancheng Xue }
178e523f111SJiancheng Xue 
179e523f111SJiancheng Xue static int hisi_spi_nor_op_reg(struct spi_nor *nor,
18045397787STudor Ambarus 				u8 opcode, size_t len, u8 optype)
181e523f111SJiancheng Xue {
182e523f111SJiancheng Xue 	struct hifmc_priv *priv = nor->priv;
183e523f111SJiancheng Xue 	struct hifmc_host *host = priv->host;
184e523f111SJiancheng Xue 	u32 reg;
185e523f111SJiancheng Xue 
186e523f111SJiancheng Xue 	reg = FMC_CMD_CMD1(opcode);
187e523f111SJiancheng Xue 	writel(reg, host->regbase + FMC_CMD);
188e523f111SJiancheng Xue 
189e523f111SJiancheng Xue 	reg = FMC_DATA_NUM_CNT(len);
190e523f111SJiancheng Xue 	writel(reg, host->regbase + FMC_DATA_NUM);
191e523f111SJiancheng Xue 
192e523f111SJiancheng Xue 	reg = OP_CFG_FM_CS(priv->chipselect);
193e523f111SJiancheng Xue 	writel(reg, host->regbase + FMC_OP_CFG);
194e523f111SJiancheng Xue 
195e523f111SJiancheng Xue 	writel(0xff, host->regbase + FMC_INT_CLR);
196e523f111SJiancheng Xue 	reg = FMC_OP_CMD1_EN | FMC_OP_REG_OP_START | optype;
197e523f111SJiancheng Xue 	writel(reg, host->regbase + FMC_OP);
198e523f111SJiancheng Xue 
19964070249SEzequiel Garcia 	return hisi_spi_nor_wait_op_finish(host);
200e523f111SJiancheng Xue }
201e523f111SJiancheng Xue 
202e523f111SJiancheng Xue static int hisi_spi_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
20345397787STudor Ambarus 				 size_t len)
204e523f111SJiancheng Xue {
205e523f111SJiancheng Xue 	struct hifmc_priv *priv = nor->priv;
206e523f111SJiancheng Xue 	struct hifmc_host *host = priv->host;
207e523f111SJiancheng Xue 	int ret;
208e523f111SJiancheng Xue 
209e523f111SJiancheng Xue 	ret = hisi_spi_nor_op_reg(nor, opcode, len, FMC_OP_READ_DATA_EN);
210e523f111SJiancheng Xue 	if (ret)
211e523f111SJiancheng Xue 		return ret;
212e523f111SJiancheng Xue 
213e523f111SJiancheng Xue 	memcpy_fromio(buf, host->iobase, len);
214e523f111SJiancheng Xue 	return 0;
215e523f111SJiancheng Xue }
216e523f111SJiancheng Xue 
217e523f111SJiancheng Xue static int hisi_spi_nor_write_reg(struct spi_nor *nor, u8 opcode,
21845397787STudor Ambarus 				  const u8 *buf, size_t len)
219e523f111SJiancheng Xue {
220e523f111SJiancheng Xue 	struct hifmc_priv *priv = nor->priv;
221e523f111SJiancheng Xue 	struct hifmc_host *host = priv->host;
222e523f111SJiancheng Xue 
223e523f111SJiancheng Xue 	if (len)
224e523f111SJiancheng Xue 		memcpy_toio(host->iobase, buf, len);
225e523f111SJiancheng Xue 
226e523f111SJiancheng Xue 	return hisi_spi_nor_op_reg(nor, opcode, len, FMC_OP_WRITE_DATA_EN);
227e523f111SJiancheng Xue }
228e523f111SJiancheng Xue 
229e523f111SJiancheng Xue static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
230e523f111SJiancheng Xue 		dma_addr_t dma_buf, size_t len, u8 op_type)
231e523f111SJiancheng Xue {
232e523f111SJiancheng Xue 	struct hifmc_priv *priv = nor->priv;
233e523f111SJiancheng Xue 	struct hifmc_host *host = priv->host;
234e523f111SJiancheng Xue 	u8 if_type = 0;
235e523f111SJiancheng Xue 	u32 reg;
236e523f111SJiancheng Xue 
237e523f111SJiancheng Xue 	reg = readl(host->regbase + FMC_CFG);
238e523f111SJiancheng Xue 	reg &= ~(FMC_CFG_OP_MODE_MASK | SPI_NOR_ADDR_MODE_MASK);
239e523f111SJiancheng Xue 	reg |= FMC_CFG_OP_MODE_NORMAL;
240e523f111SJiancheng Xue 	reg |= (nor->addr_width == 4) ? SPI_NOR_ADDR_MODE_4BYTES
241e523f111SJiancheng Xue 		: SPI_NOR_ADDR_MODE_3BYTES;
242e523f111SJiancheng Xue 	writel(reg, host->regbase + FMC_CFG);
243e523f111SJiancheng Xue 
244e523f111SJiancheng Xue 	writel(start_off, host->regbase + FMC_ADDRL);
245e523f111SJiancheng Xue 	writel(dma_buf, host->regbase + FMC_DMA_SADDR_D0);
246e523f111SJiancheng Xue 	writel(FMC_DMA_LEN_SET(len), host->regbase + FMC_DMA_LEN);
247e523f111SJiancheng Xue 
248e523f111SJiancheng Xue 	reg = OP_CFG_FM_CS(priv->chipselect);
249cfc5604cSCyrille Pitchen 	if (op_type == FMC_OP_READ)
25064070249SEzequiel Garcia 		if_type = hisi_spi_nor_get_if_type(nor->read_proto);
251cfc5604cSCyrille Pitchen 	else
25264070249SEzequiel Garcia 		if_type = hisi_spi_nor_get_if_type(nor->write_proto);
253e523f111SJiancheng Xue 	reg |= OP_CFG_MEM_IF_TYPE(if_type);
254e523f111SJiancheng Xue 	if (op_type == FMC_OP_READ)
255e523f111SJiancheng Xue 		reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3);
256e523f111SJiancheng Xue 	writel(reg, host->regbase + FMC_OP_CFG);
257e523f111SJiancheng Xue 
258e523f111SJiancheng Xue 	writel(0xff, host->regbase + FMC_INT_CLR);
259e523f111SJiancheng Xue 	reg = OP_CTRL_RW_OP(op_type) | OP_CTRL_DMA_OP_READY;
260e523f111SJiancheng Xue 	reg |= (op_type == FMC_OP_READ)
261e523f111SJiancheng Xue 		? OP_CTRL_RD_OPCODE(nor->read_opcode)
262e523f111SJiancheng Xue 		: OP_CTRL_WR_OPCODE(nor->program_opcode);
263e523f111SJiancheng Xue 	writel(reg, host->regbase + FMC_OP_DMA);
264e523f111SJiancheng Xue 
26564070249SEzequiel Garcia 	return hisi_spi_nor_wait_op_finish(host);
266e523f111SJiancheng Xue }
267e523f111SJiancheng Xue 
268e523f111SJiancheng Xue static ssize_t hisi_spi_nor_read(struct spi_nor *nor, loff_t from, size_t len,
269e523f111SJiancheng Xue 		u_char *read_buf)
270e523f111SJiancheng Xue {
271e523f111SJiancheng Xue 	struct hifmc_priv *priv = nor->priv;
272e523f111SJiancheng Xue 	struct hifmc_host *host = priv->host;
273e523f111SJiancheng Xue 	size_t offset;
274e523f111SJiancheng Xue 	int ret;
275e523f111SJiancheng Xue 
276e523f111SJiancheng Xue 	for (offset = 0; offset < len; offset += HIFMC_DMA_MAX_LEN) {
277e523f111SJiancheng Xue 		size_t trans = min_t(size_t, HIFMC_DMA_MAX_LEN, len - offset);
278e523f111SJiancheng Xue 
279e523f111SJiancheng Xue 		ret = hisi_spi_nor_dma_transfer(nor,
280e523f111SJiancheng Xue 			from + offset, host->dma_buffer, trans, FMC_OP_READ);
281e523f111SJiancheng Xue 		if (ret) {
282e523f111SJiancheng Xue 			dev_warn(nor->dev, "DMA read timeout\n");
283e523f111SJiancheng Xue 			return ret;
284e523f111SJiancheng Xue 		}
285e523f111SJiancheng Xue 		memcpy(read_buf + offset, host->buffer, trans);
286e523f111SJiancheng Xue 	}
287e523f111SJiancheng Xue 
288e523f111SJiancheng Xue 	return len;
289e523f111SJiancheng Xue }
290e523f111SJiancheng Xue 
291e523f111SJiancheng Xue static ssize_t hisi_spi_nor_write(struct spi_nor *nor, loff_t to,
292e523f111SJiancheng Xue 			size_t len, const u_char *write_buf)
293e523f111SJiancheng Xue {
294e523f111SJiancheng Xue 	struct hifmc_priv *priv = nor->priv;
295e523f111SJiancheng Xue 	struct hifmc_host *host = priv->host;
296e523f111SJiancheng Xue 	size_t offset;
297e523f111SJiancheng Xue 	int ret;
298e523f111SJiancheng Xue 
299e523f111SJiancheng Xue 	for (offset = 0; offset < len; offset += HIFMC_DMA_MAX_LEN) {
300e523f111SJiancheng Xue 		size_t trans = min_t(size_t, HIFMC_DMA_MAX_LEN, len - offset);
301e523f111SJiancheng Xue 
302e523f111SJiancheng Xue 		memcpy(host->buffer, write_buf + offset, trans);
303e523f111SJiancheng Xue 		ret = hisi_spi_nor_dma_transfer(nor,
304e523f111SJiancheng Xue 			to + offset, host->dma_buffer, trans, FMC_OP_WRITE);
305e523f111SJiancheng Xue 		if (ret) {
306e523f111SJiancheng Xue 			dev_warn(nor->dev, "DMA write timeout\n");
307e523f111SJiancheng Xue 			return ret;
308e523f111SJiancheng Xue 		}
309e523f111SJiancheng Xue 	}
310e523f111SJiancheng Xue 
311e523f111SJiancheng Xue 	return len;
312e523f111SJiancheng Xue }
313e523f111SJiancheng Xue 
31445397787STudor Ambarus static const struct spi_nor_controller_ops hisi_controller_ops = {
31545397787STudor Ambarus 	.prepare = hisi_spi_nor_prep,
31645397787STudor Ambarus 	.unprepare = hisi_spi_nor_unprep,
31745397787STudor Ambarus 	.read_reg = hisi_spi_nor_read_reg,
31845397787STudor Ambarus 	.write_reg = hisi_spi_nor_write_reg,
31945397787STudor Ambarus 	.read = hisi_spi_nor_read,
32045397787STudor Ambarus 	.write = hisi_spi_nor_write,
32145397787STudor Ambarus };
32245397787STudor Ambarus 
323e523f111SJiancheng Xue /**
324e523f111SJiancheng Xue  * Get spi flash device information and register it as a mtd device.
325e523f111SJiancheng Xue  */
326e523f111SJiancheng Xue static int hisi_spi_nor_register(struct device_node *np,
327e523f111SJiancheng Xue 				struct hifmc_host *host)
328e523f111SJiancheng Xue {
329cfc5604cSCyrille Pitchen 	const struct spi_nor_hwcaps hwcaps = {
330cfc5604cSCyrille Pitchen 		.mask = SNOR_HWCAPS_READ |
331cfc5604cSCyrille Pitchen 			SNOR_HWCAPS_READ_FAST |
332cfc5604cSCyrille Pitchen 			SNOR_HWCAPS_READ_1_1_2 |
333cfc5604cSCyrille Pitchen 			SNOR_HWCAPS_READ_1_1_4 |
334cfc5604cSCyrille Pitchen 			SNOR_HWCAPS_PP,
335cfc5604cSCyrille Pitchen 	};
336e523f111SJiancheng Xue 	struct device *dev = host->dev;
337e523f111SJiancheng Xue 	struct spi_nor *nor;
338e523f111SJiancheng Xue 	struct hifmc_priv *priv;
339e523f111SJiancheng Xue 	struct mtd_info *mtd;
340e523f111SJiancheng Xue 	int ret;
341e523f111SJiancheng Xue 
342e523f111SJiancheng Xue 	nor = devm_kzalloc(dev, sizeof(*nor), GFP_KERNEL);
343e523f111SJiancheng Xue 	if (!nor)
344e523f111SJiancheng Xue 		return -ENOMEM;
345e523f111SJiancheng Xue 
346e523f111SJiancheng Xue 	nor->dev = dev;
347e523f111SJiancheng Xue 	spi_nor_set_flash_node(nor, np);
348e523f111SJiancheng Xue 
349e523f111SJiancheng Xue 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
350e523f111SJiancheng Xue 	if (!priv)
351e523f111SJiancheng Xue 		return -ENOMEM;
352e523f111SJiancheng Xue 
353e523f111SJiancheng Xue 	ret = of_property_read_u32(np, "reg", &priv->chipselect);
354e523f111SJiancheng Xue 	if (ret) {
3551d706077SRob Herring 		dev_err(dev, "There's no reg property for %pOF\n",
3561d706077SRob Herring 			np);
357e523f111SJiancheng Xue 		return ret;
358e523f111SJiancheng Xue 	}
359e523f111SJiancheng Xue 
360e523f111SJiancheng Xue 	ret = of_property_read_u32(np, "spi-max-frequency",
361e523f111SJiancheng Xue 			&priv->clkrate);
362e523f111SJiancheng Xue 	if (ret) {
3631d706077SRob Herring 		dev_err(dev, "There's no spi-max-frequency property for %pOF\n",
3641d706077SRob Herring 			np);
365e523f111SJiancheng Xue 		return ret;
366e523f111SJiancheng Xue 	}
367e523f111SJiancheng Xue 	priv->host = host;
368e523f111SJiancheng Xue 	nor->priv = priv;
36945397787STudor Ambarus 	nor->controller_ops = &hisi_controller_ops;
370e523f111SJiancheng Xue 
371cfc5604cSCyrille Pitchen 	ret = spi_nor_scan(nor, NULL, &hwcaps);
372e523f111SJiancheng Xue 	if (ret)
373e523f111SJiancheng Xue 		return ret;
374e523f111SJiancheng Xue 
375e523f111SJiancheng Xue 	mtd = &nor->mtd;
376e523f111SJiancheng Xue 	mtd->name = np->name;
377e523f111SJiancheng Xue 	ret = mtd_device_register(mtd, NULL, 0);
378e523f111SJiancheng Xue 	if (ret)
379e523f111SJiancheng Xue 		return ret;
380e523f111SJiancheng Xue 
381e523f111SJiancheng Xue 	host->nor[host->num_chip] = nor;
382e523f111SJiancheng Xue 	host->num_chip++;
383e523f111SJiancheng Xue 	return 0;
384e523f111SJiancheng Xue }
385e523f111SJiancheng Xue 
386e523f111SJiancheng Xue static void hisi_spi_nor_unregister_all(struct hifmc_host *host)
387e523f111SJiancheng Xue {
388e523f111SJiancheng Xue 	int i;
389e523f111SJiancheng Xue 
390e523f111SJiancheng Xue 	for (i = 0; i < host->num_chip; i++)
391e523f111SJiancheng Xue 		mtd_device_unregister(&host->nor[i]->mtd);
392e523f111SJiancheng Xue }
393e523f111SJiancheng Xue 
394e523f111SJiancheng Xue static int hisi_spi_nor_register_all(struct hifmc_host *host)
395e523f111SJiancheng Xue {
396e523f111SJiancheng Xue 	struct device *dev = host->dev;
397e523f111SJiancheng Xue 	struct device_node *np;
398e523f111SJiancheng Xue 	int ret;
399e523f111SJiancheng Xue 
400e523f111SJiancheng Xue 	for_each_available_child_of_node(dev->of_node, np) {
401e523f111SJiancheng Xue 		ret = hisi_spi_nor_register(np, host);
402e523f111SJiancheng Xue 		if (ret)
403e523f111SJiancheng Xue 			goto fail;
404e523f111SJiancheng Xue 
405e523f111SJiancheng Xue 		if (host->num_chip == HIFMC_MAX_CHIP_NUM) {
406e523f111SJiancheng Xue 			dev_warn(dev, "Flash device number exceeds the maximum chipselect number\n");
4077ae2227bSNishka Dasgupta 			of_node_put(np);
408e523f111SJiancheng Xue 			break;
409e523f111SJiancheng Xue 		}
410e523f111SJiancheng Xue 	}
411e523f111SJiancheng Xue 
412e523f111SJiancheng Xue 	return 0;
413e523f111SJiancheng Xue 
414e523f111SJiancheng Xue fail:
415e523f111SJiancheng Xue 	hisi_spi_nor_unregister_all(host);
416e523f111SJiancheng Xue 	return ret;
417e523f111SJiancheng Xue }
418e523f111SJiancheng Xue 
419e523f111SJiancheng Xue static int hisi_spi_nor_probe(struct platform_device *pdev)
420e523f111SJiancheng Xue {
421e523f111SJiancheng Xue 	struct device *dev = &pdev->dev;
422e523f111SJiancheng Xue 	struct resource *res;
423e523f111SJiancheng Xue 	struct hifmc_host *host;
424e523f111SJiancheng Xue 	int ret;
425e523f111SJiancheng Xue 
426e523f111SJiancheng Xue 	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
427e523f111SJiancheng Xue 	if (!host)
428e523f111SJiancheng Xue 		return -ENOMEM;
429e523f111SJiancheng Xue 
430e523f111SJiancheng Xue 	platform_set_drvdata(pdev, host);
431e523f111SJiancheng Xue 	host->dev = dev;
432e523f111SJiancheng Xue 
433e523f111SJiancheng Xue 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control");
434e523f111SJiancheng Xue 	host->regbase = devm_ioremap_resource(dev, res);
435e523f111SJiancheng Xue 	if (IS_ERR(host->regbase))
436e523f111SJiancheng Xue 		return PTR_ERR(host->regbase);
437e523f111SJiancheng Xue 
438e523f111SJiancheng Xue 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "memory");
439e523f111SJiancheng Xue 	host->iobase = devm_ioremap_resource(dev, res);
440e523f111SJiancheng Xue 	if (IS_ERR(host->iobase))
441e523f111SJiancheng Xue 		return PTR_ERR(host->iobase);
442e523f111SJiancheng Xue 
443e523f111SJiancheng Xue 	host->clk = devm_clk_get(dev, NULL);
444e523f111SJiancheng Xue 	if (IS_ERR(host->clk))
445e523f111SJiancheng Xue 		return PTR_ERR(host->clk);
446e523f111SJiancheng Xue 
447e523f111SJiancheng Xue 	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
448e523f111SJiancheng Xue 	if (ret) {
449e523f111SJiancheng Xue 		dev_warn(dev, "Unable to set dma mask\n");
450e523f111SJiancheng Xue 		return ret;
451e523f111SJiancheng Xue 	}
452e523f111SJiancheng Xue 
453e523f111SJiancheng Xue 	host->buffer = dmam_alloc_coherent(dev, HIFMC_DMA_MAX_LEN,
454e523f111SJiancheng Xue 			&host->dma_buffer, GFP_KERNEL);
455e523f111SJiancheng Xue 	if (!host->buffer)
456e523f111SJiancheng Xue 		return -ENOMEM;
457e523f111SJiancheng Xue 
4580a5165a8SAlexey Khoroshilov 	ret = clk_prepare_enable(host->clk);
4590a5165a8SAlexey Khoroshilov 	if (ret)
4600a5165a8SAlexey Khoroshilov 		return ret;
4610a5165a8SAlexey Khoroshilov 
462e523f111SJiancheng Xue 	mutex_init(&host->lock);
463e523f111SJiancheng Xue 	hisi_spi_nor_init(host);
464e523f111SJiancheng Xue 	ret = hisi_spi_nor_register_all(host);
465e523f111SJiancheng Xue 	if (ret)
466e523f111SJiancheng Xue 		mutex_destroy(&host->lock);
467e523f111SJiancheng Xue 
468e523f111SJiancheng Xue 	clk_disable_unprepare(host->clk);
469e523f111SJiancheng Xue 	return ret;
470e523f111SJiancheng Xue }
471e523f111SJiancheng Xue 
472e523f111SJiancheng Xue static int hisi_spi_nor_remove(struct platform_device *pdev)
473e523f111SJiancheng Xue {
474e523f111SJiancheng Xue 	struct hifmc_host *host = platform_get_drvdata(pdev);
475e523f111SJiancheng Xue 
476e523f111SJiancheng Xue 	hisi_spi_nor_unregister_all(host);
477e523f111SJiancheng Xue 	mutex_destroy(&host->lock);
478e523f111SJiancheng Xue 	clk_disable_unprepare(host->clk);
479e523f111SJiancheng Xue 	return 0;
480e523f111SJiancheng Xue }
481e523f111SJiancheng Xue 
482e523f111SJiancheng Xue static const struct of_device_id hisi_spi_nor_dt_ids[] = {
483e523f111SJiancheng Xue 	{ .compatible = "hisilicon,fmc-spi-nor"},
484e523f111SJiancheng Xue 	{ /* sentinel */ }
485e523f111SJiancheng Xue };
486e523f111SJiancheng Xue MODULE_DEVICE_TABLE(of, hisi_spi_nor_dt_ids);
487e523f111SJiancheng Xue 
488e523f111SJiancheng Xue static struct platform_driver hisi_spi_nor_driver = {
489e523f111SJiancheng Xue 	.driver = {
490e523f111SJiancheng Xue 		.name	= "hisi-sfc",
491e523f111SJiancheng Xue 		.of_match_table = hisi_spi_nor_dt_ids,
492e523f111SJiancheng Xue 	},
493e523f111SJiancheng Xue 	.probe	= hisi_spi_nor_probe,
494e523f111SJiancheng Xue 	.remove	= hisi_spi_nor_remove,
495e523f111SJiancheng Xue };
496e523f111SJiancheng Xue module_platform_driver(hisi_spi_nor_driver);
497e523f111SJiancheng Xue 
498e523f111SJiancheng Xue MODULE_LICENSE("GPL v2");
499e523f111SJiancheng Xue MODULE_DESCRIPTION("HiSilicon SPI Nor Flash Controller Driver");
500