diff options
author | Jiancheng Xue <xuejiancheng@hisilicon.com> | 2016-06-28 03:48:19 -0400 |
---|---|---|
committer | Brian Norris <computersforpeace@gmail.com> | 2016-07-13 20:49:45 -0400 |
commit | e523f11141bdc24f65775f0b1fa4a7ed404e68cc (patch) | |
tree | f6d98109ccdf996afcc28150778a2a1a9cdf1938 | |
parent | 595f0e101d4c250010ca0763ae15b863f01bb90e (diff) |
mtd: spi-nor: add hisilicon spi-nor flash controller driver
Add hisilicon spi-nor flash controller driver
Signed-off-by: Binquan Peng <pengbinquan@hisilicon.com>
Signed-off-by: Jiancheng Xue <xuejiancheng@hisilicon.com>
Acked-by: Rob Herring <robh@kernel.org>
Reviewed-by: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
Reviewed-by: Jagan Teki <jteki@openedev.com>
Reviewed-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
-rw-r--r-- | Documentation/devicetree/bindings/mtd/hisilicon,fmc-spi-nor.txt | 24 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/Kconfig | 7 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/hisi-sfc.c | 489 |
4 files changed, 521 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/mtd/hisilicon,fmc-spi-nor.txt b/Documentation/devicetree/bindings/mtd/hisilicon,fmc-spi-nor.txt new file mode 100644 index 000000000000..74981520d6dd --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/hisilicon,fmc-spi-nor.txt | |||
@@ -0,0 +1,24 @@ | |||
1 | HiSilicon SPI-NOR Flash Controller | ||
2 | |||
3 | Required properties: | ||
4 | - compatible : Should be "hisilicon,fmc-spi-nor" and one of the following strings: | ||
5 | "hisilicon,hi3519-spi-nor" | ||
6 | - address-cells : Should be 1. | ||
7 | - size-cells : Should be 0. | ||
8 | - reg : Offset and length of the register set for the controller device. | ||
9 | - reg-names : Must include the following two entries: "control", "memory". | ||
10 | - clocks : handle to spi-nor flash controller clock. | ||
11 | |||
12 | Example: | ||
13 | spi-nor-controller@10000000 { | ||
14 | compatible = "hisilicon,hi3519-spi-nor", "hisilicon,fmc-spi-nor"; | ||
15 | #address-cells = <1>; | ||
16 | #size-cells = <0>; | ||
17 | reg = <0x10000000 0x1000>, <0x14000000 0x1000000>; | ||
18 | reg-names = "control", "memory"; | ||
19 | clocks = <&clock HI3519_FMC_CLK>; | ||
20 | spi-nor@0 { | ||
21 | compatible = "jedec,spi-nor"; | ||
22 | reg = <0>; | ||
23 | }; | ||
24 | }; | ||
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index d42c98e1f581..6f14f2b66c60 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig | |||
@@ -38,6 +38,13 @@ config SPI_FSL_QUADSPI | |||
38 | This controller does not support generic SPI. It only supports | 38 | This controller does not support generic SPI. It only supports |
39 | SPI NOR. | 39 | SPI NOR. |
40 | 40 | ||
41 | config SPI_HISI_SFC | ||
42 | tristate "Hisilicon SPI-NOR Flash Controller(SFC)" | ||
43 | depends on ARCH_HISI || COMPILE_TEST | ||
44 | depends on HAS_IOMEM && HAS_DMA | ||
45 | help | ||
46 | This enables support for hisilicon SPI-NOR flash controller. | ||
47 | |||
41 | config SPI_NXP_SPIFI | 48 | config SPI_NXP_SPIFI |
42 | tristate "NXP SPI Flash Interface (SPIFI)" | 49 | tristate "NXP SPI Flash Interface (SPIFI)" |
43 | depends on OF && (ARCH_LPC18XX || COMPILE_TEST) | 50 | depends on OF && (ARCH_LPC18XX || COMPILE_TEST) |
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 0bf3a7f81675..8a6fa6970f37 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o | 1 | obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o |
2 | obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o | 2 | obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o |
3 | obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o | ||
3 | obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o | 4 | obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o |
4 | obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o | 5 | obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o |
diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c new file mode 100644 index 000000000000..20378b0d55e9 --- /dev/null +++ b/drivers/mtd/spi-nor/hisi-sfc.c | |||
@@ -0,0 +1,489 @@ | |||
1 | /* | ||
2 | * HiSilicon SPI Nor Flash Controller Driver | ||
3 | * | ||
4 | * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | #include <linux/bitops.h> | ||
20 | #include <linux/clk.h> | ||
21 | #include <linux/dma-mapping.h> | ||
22 | #include <linux/iopoll.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/mtd/mtd.h> | ||
25 | #include <linux/mtd/spi-nor.h> | ||
26 | #include <linux/of.h> | ||
27 | #include <linux/platform_device.h> | ||
28 | #include <linux/slab.h> | ||
29 | |||
30 | /* Hardware register offsets and field definitions */ | ||
31 | #define FMC_CFG 0x00 | ||
32 | #define FMC_CFG_OP_MODE_MASK BIT_MASK(0) | ||
33 | #define FMC_CFG_OP_MODE_BOOT 0 | ||
34 | #define FMC_CFG_OP_MODE_NORMAL 1 | ||
35 | #define FMC_CFG_FLASH_SEL(type) (((type) & 0x3) << 1) | ||
36 | #define FMC_CFG_FLASH_SEL_MASK 0x6 | ||
37 | #define FMC_ECC_TYPE(type) (((type) & 0x7) << 5) | ||
38 | #define FMC_ECC_TYPE_MASK GENMASK(7, 5) | ||
39 | #define SPI_NOR_ADDR_MODE_MASK BIT_MASK(10) | ||
40 | #define SPI_NOR_ADDR_MODE_3BYTES (0x0 << 10) | ||
41 | #define SPI_NOR_ADDR_MODE_4BYTES (0x1 << 10) | ||
42 | #define FMC_GLOBAL_CFG 0x04 | ||
43 | #define FMC_GLOBAL_CFG_WP_ENABLE BIT(6) | ||
44 | #define FMC_SPI_TIMING_CFG 0x08 | ||
45 | #define TIMING_CFG_TCSH(nr) (((nr) & 0xf) << 8) | ||
46 | #define TIMING_CFG_TCSS(nr) (((nr) & 0xf) << 4) | ||
47 | #define TIMING_CFG_TSHSL(nr) ((nr) & 0xf) | ||
48 | #define CS_HOLD_TIME 0x6 | ||
49 | #define CS_SETUP_TIME 0x6 | ||
50 | #define CS_DESELECT_TIME 0xf | ||
51 | #define FMC_INT 0x18 | ||
52 | #define FMC_INT_OP_DONE BIT(0) | ||
53 | #define FMC_INT_CLR 0x20 | ||
54 | #define FMC_CMD 0x24 | ||
55 | #define FMC_CMD_CMD1(cmd) ((cmd) & 0xff) | ||
56 | #define FMC_ADDRL 0x2c | ||
57 | #define FMC_OP_CFG 0x30 | ||
58 | #define OP_CFG_FM_CS(cs) ((cs) << 11) | ||
59 | #define OP_CFG_MEM_IF_TYPE(type) (((type) & 0x7) << 7) | ||
60 | #define OP_CFG_ADDR_NUM(addr) (((addr) & 0x7) << 4) | ||
61 | #define OP_CFG_DUMMY_NUM(dummy) ((dummy) & 0xf) | ||
62 | #define FMC_DATA_NUM 0x38 | ||
63 | #define FMC_DATA_NUM_CNT(cnt) ((cnt) & GENMASK(13, 0)) | ||
64 | #define FMC_OP 0x3c | ||
65 | #define FMC_OP_DUMMY_EN BIT(8) | ||
66 | #define FMC_OP_CMD1_EN BIT(7) | ||
67 | #define FMC_OP_ADDR_EN BIT(6) | ||
68 | #define FMC_OP_WRITE_DATA_EN BIT(5) | ||
69 | #define FMC_OP_READ_DATA_EN BIT(2) | ||
70 | #define FMC_OP_READ_STATUS_EN BIT(1) | ||
71 | #define FMC_OP_REG_OP_START BIT(0) | ||
72 | #define FMC_DMA_LEN 0x40 | ||
73 | #define FMC_DMA_LEN_SET(len) ((len) & GENMASK(27, 0)) | ||
74 | #define FMC_DMA_SADDR_D0 0x4c | ||
75 | #define HIFMC_DMA_MAX_LEN (4096) | ||
76 | #define HIFMC_DMA_MASK (HIFMC_DMA_MAX_LEN - 1) | ||
77 | #define FMC_OP_DMA 0x68 | ||
78 | #define OP_CTRL_RD_OPCODE(code) (((code) & 0xff) << 16) | ||
79 | #define OP_CTRL_WR_OPCODE(code) (((code) & 0xff) << 8) | ||
80 | #define OP_CTRL_RW_OP(op) ((op) << 1) | ||
81 | #define OP_CTRL_DMA_OP_READY BIT(0) | ||
82 | #define FMC_OP_READ 0x0 | ||
83 | #define FMC_OP_WRITE 0x1 | ||
84 | #define FMC_WAIT_TIMEOUT 1000000 | ||
85 | |||
86 | enum hifmc_iftype { | ||
87 | IF_TYPE_STD, | ||
88 | IF_TYPE_DUAL, | ||
89 | IF_TYPE_DIO, | ||
90 | IF_TYPE_QUAD, | ||
91 | IF_TYPE_QIO, | ||
92 | }; | ||
93 | |||
94 | struct hifmc_priv { | ||
95 | u32 chipselect; | ||
96 | u32 clkrate; | ||
97 | struct hifmc_host *host; | ||
98 | }; | ||
99 | |||
100 | #define HIFMC_MAX_CHIP_NUM 2 | ||
101 | struct hifmc_host { | ||
102 | struct device *dev; | ||
103 | struct mutex lock; | ||
104 | |||
105 | void __iomem *regbase; | ||
106 | void __iomem *iobase; | ||
107 | struct clk *clk; | ||
108 | void *buffer; | ||
109 | dma_addr_t dma_buffer; | ||
110 | |||
111 | struct spi_nor *nor[HIFMC_MAX_CHIP_NUM]; | ||
112 | u32 num_chip; | ||
113 | }; | ||
114 | |||
115 | static inline int wait_op_finish(struct hifmc_host *host) | ||
116 | { | ||
117 | u32 reg; | ||
118 | |||
119 | return readl_poll_timeout(host->regbase + FMC_INT, reg, | ||
120 | (reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT); | ||
121 | } | ||
122 | |||
123 | static int get_if_type(enum read_mode flash_read) | ||
124 | { | ||
125 | enum hifmc_iftype if_type; | ||
126 | |||
127 | switch (flash_read) { | ||
128 | case SPI_NOR_DUAL: | ||
129 | if_type = IF_TYPE_DUAL; | ||
130 | break; | ||
131 | case SPI_NOR_QUAD: | ||
132 | if_type = IF_TYPE_QUAD; | ||
133 | break; | ||
134 | case SPI_NOR_NORMAL: | ||
135 | case SPI_NOR_FAST: | ||
136 | default: | ||
137 | if_type = IF_TYPE_STD; | ||
138 | break; | ||
139 | } | ||
140 | |||
141 | return if_type; | ||
142 | } | ||
143 | |||
144 | static void hisi_spi_nor_init(struct hifmc_host *host) | ||
145 | { | ||
146 | u32 reg; | ||
147 | |||
148 | reg = TIMING_CFG_TCSH(CS_HOLD_TIME) | ||
149 | | TIMING_CFG_TCSS(CS_SETUP_TIME) | ||
150 | | TIMING_CFG_TSHSL(CS_DESELECT_TIME); | ||
151 | writel(reg, host->regbase + FMC_SPI_TIMING_CFG); | ||
152 | } | ||
153 | |||
154 | static int hisi_spi_nor_prep(struct spi_nor *nor, enum spi_nor_ops ops) | ||
155 | { | ||
156 | struct hifmc_priv *priv = nor->priv; | ||
157 | struct hifmc_host *host = priv->host; | ||
158 | int ret; | ||
159 | |||
160 | mutex_lock(&host->lock); | ||
161 | |||
162 | ret = clk_set_rate(host->clk, priv->clkrate); | ||
163 | if (ret) | ||
164 | goto out; | ||
165 | |||
166 | ret = clk_prepare_enable(host->clk); | ||
167 | if (ret) | ||
168 | goto out; | ||
169 | |||
170 | return 0; | ||
171 | |||
172 | out: | ||
173 | mutex_unlock(&host->lock); | ||
174 | return ret; | ||
175 | } | ||
176 | |||
177 | static void hisi_spi_nor_unprep(struct spi_nor *nor, enum spi_nor_ops ops) | ||
178 | { | ||
179 | struct hifmc_priv *priv = nor->priv; | ||
180 | struct hifmc_host *host = priv->host; | ||
181 | |||
182 | clk_disable_unprepare(host->clk); | ||
183 | mutex_unlock(&host->lock); | ||
184 | } | ||
185 | |||
186 | static int hisi_spi_nor_op_reg(struct spi_nor *nor, | ||
187 | u8 opcode, int len, u8 optype) | ||
188 | { | ||
189 | struct hifmc_priv *priv = nor->priv; | ||
190 | struct hifmc_host *host = priv->host; | ||
191 | u32 reg; | ||
192 | |||
193 | reg = FMC_CMD_CMD1(opcode); | ||
194 | writel(reg, host->regbase + FMC_CMD); | ||
195 | |||
196 | reg = FMC_DATA_NUM_CNT(len); | ||
197 | writel(reg, host->regbase + FMC_DATA_NUM); | ||
198 | |||
199 | reg = OP_CFG_FM_CS(priv->chipselect); | ||
200 | writel(reg, host->regbase + FMC_OP_CFG); | ||
201 | |||
202 | writel(0xff, host->regbase + FMC_INT_CLR); | ||
203 | reg = FMC_OP_CMD1_EN | FMC_OP_REG_OP_START | optype; | ||
204 | writel(reg, host->regbase + FMC_OP); | ||
205 | |||
206 | return wait_op_finish(host); | ||
207 | } | ||
208 | |||
209 | static int hisi_spi_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, | ||
210 | int len) | ||
211 | { | ||
212 | struct hifmc_priv *priv = nor->priv; | ||
213 | struct hifmc_host *host = priv->host; | ||
214 | int ret; | ||
215 | |||
216 | ret = hisi_spi_nor_op_reg(nor, opcode, len, FMC_OP_READ_DATA_EN); | ||
217 | if (ret) | ||
218 | return ret; | ||
219 | |||
220 | memcpy_fromio(buf, host->iobase, len); | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static int hisi_spi_nor_write_reg(struct spi_nor *nor, u8 opcode, | ||
225 | u8 *buf, int len) | ||
226 | { | ||
227 | struct hifmc_priv *priv = nor->priv; | ||
228 | struct hifmc_host *host = priv->host; | ||
229 | |||
230 | if (len) | ||
231 | memcpy_toio(host->iobase, buf, len); | ||
232 | |||
233 | return hisi_spi_nor_op_reg(nor, opcode, len, FMC_OP_WRITE_DATA_EN); | ||
234 | } | ||
235 | |||
236 | static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off, | ||
237 | dma_addr_t dma_buf, size_t len, u8 op_type) | ||
238 | { | ||
239 | struct hifmc_priv *priv = nor->priv; | ||
240 | struct hifmc_host *host = priv->host; | ||
241 | u8 if_type = 0; | ||
242 | u32 reg; | ||
243 | |||
244 | reg = readl(host->regbase + FMC_CFG); | ||
245 | reg &= ~(FMC_CFG_OP_MODE_MASK | SPI_NOR_ADDR_MODE_MASK); | ||
246 | reg |= FMC_CFG_OP_MODE_NORMAL; | ||
247 | reg |= (nor->addr_width == 4) ? SPI_NOR_ADDR_MODE_4BYTES | ||
248 | : SPI_NOR_ADDR_MODE_3BYTES; | ||
249 | writel(reg, host->regbase + FMC_CFG); | ||
250 | |||
251 | writel(start_off, host->regbase + FMC_ADDRL); | ||
252 | writel(dma_buf, host->regbase + FMC_DMA_SADDR_D0); | ||
253 | writel(FMC_DMA_LEN_SET(len), host->regbase + FMC_DMA_LEN); | ||
254 | |||
255 | reg = OP_CFG_FM_CS(priv->chipselect); | ||
256 | if_type = get_if_type(nor->flash_read); | ||
257 | reg |= OP_CFG_MEM_IF_TYPE(if_type); | ||
258 | if (op_type == FMC_OP_READ) | ||
259 | reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3); | ||
260 | writel(reg, host->regbase + FMC_OP_CFG); | ||
261 | |||
262 | writel(0xff, host->regbase + FMC_INT_CLR); | ||
263 | reg = OP_CTRL_RW_OP(op_type) | OP_CTRL_DMA_OP_READY; | ||
264 | reg |= (op_type == FMC_OP_READ) | ||
265 | ? OP_CTRL_RD_OPCODE(nor->read_opcode) | ||
266 | : OP_CTRL_WR_OPCODE(nor->program_opcode); | ||
267 | writel(reg, host->regbase + FMC_OP_DMA); | ||
268 | |||
269 | return wait_op_finish(host); | ||
270 | } | ||
271 | |||
272 | static ssize_t hisi_spi_nor_read(struct spi_nor *nor, loff_t from, size_t len, | ||
273 | u_char *read_buf) | ||
274 | { | ||
275 | struct hifmc_priv *priv = nor->priv; | ||
276 | struct hifmc_host *host = priv->host; | ||
277 | size_t offset; | ||
278 | int ret; | ||
279 | |||
280 | for (offset = 0; offset < len; offset += HIFMC_DMA_MAX_LEN) { | ||
281 | size_t trans = min_t(size_t, HIFMC_DMA_MAX_LEN, len - offset); | ||
282 | |||
283 | ret = hisi_spi_nor_dma_transfer(nor, | ||
284 | from + offset, host->dma_buffer, trans, FMC_OP_READ); | ||
285 | if (ret) { | ||
286 | dev_warn(nor->dev, "DMA read timeout\n"); | ||
287 | return ret; | ||
288 | } | ||
289 | memcpy(read_buf + offset, host->buffer, trans); | ||
290 | } | ||
291 | |||
292 | return len; | ||
293 | } | ||
294 | |||
295 | static ssize_t hisi_spi_nor_write(struct spi_nor *nor, loff_t to, | ||
296 | size_t len, const u_char *write_buf) | ||
297 | { | ||
298 | struct hifmc_priv *priv = nor->priv; | ||
299 | struct hifmc_host *host = priv->host; | ||
300 | size_t offset; | ||
301 | int ret; | ||
302 | |||
303 | for (offset = 0; offset < len; offset += HIFMC_DMA_MAX_LEN) { | ||
304 | size_t trans = min_t(size_t, HIFMC_DMA_MAX_LEN, len - offset); | ||
305 | |||
306 | memcpy(host->buffer, write_buf + offset, trans); | ||
307 | ret = hisi_spi_nor_dma_transfer(nor, | ||
308 | to + offset, host->dma_buffer, trans, FMC_OP_WRITE); | ||
309 | if (ret) { | ||
310 | dev_warn(nor->dev, "DMA write timeout\n"); | ||
311 | return ret; | ||
312 | } | ||
313 | } | ||
314 | |||
315 | return len; | ||
316 | } | ||
317 | |||
318 | /** | ||
319 | * Get spi flash device information and register it as a mtd device. | ||
320 | */ | ||
321 | static int hisi_spi_nor_register(struct device_node *np, | ||
322 | struct hifmc_host *host) | ||
323 | { | ||
324 | struct device *dev = host->dev; | ||
325 | struct spi_nor *nor; | ||
326 | struct hifmc_priv *priv; | ||
327 | struct mtd_info *mtd; | ||
328 | int ret; | ||
329 | |||
330 | nor = devm_kzalloc(dev, sizeof(*nor), GFP_KERNEL); | ||
331 | if (!nor) | ||
332 | return -ENOMEM; | ||
333 | |||
334 | nor->dev = dev; | ||
335 | spi_nor_set_flash_node(nor, np); | ||
336 | |||
337 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
338 | if (!priv) | ||
339 | return -ENOMEM; | ||
340 | |||
341 | ret = of_property_read_u32(np, "reg", &priv->chipselect); | ||
342 | if (ret) { | ||
343 | dev_err(dev, "There's no reg property for %s\n", | ||
344 | np->full_name); | ||
345 | return ret; | ||
346 | } | ||
347 | |||
348 | ret = of_property_read_u32(np, "spi-max-frequency", | ||
349 | &priv->clkrate); | ||
350 | if (ret) { | ||
351 | dev_err(dev, "There's no spi-max-frequency property for %s\n", | ||
352 | np->full_name); | ||
353 | return ret; | ||
354 | } | ||
355 | priv->host = host; | ||
356 | nor->priv = priv; | ||
357 | |||
358 | nor->prepare = hisi_spi_nor_prep; | ||
359 | nor->unprepare = hisi_spi_nor_unprep; | ||
360 | nor->read_reg = hisi_spi_nor_read_reg; | ||
361 | nor->write_reg = hisi_spi_nor_write_reg; | ||
362 | nor->read = hisi_spi_nor_read; | ||
363 | nor->write = hisi_spi_nor_write; | ||
364 | nor->erase = NULL; | ||
365 | ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD); | ||
366 | if (ret) | ||
367 | return ret; | ||
368 | |||
369 | mtd = &nor->mtd; | ||
370 | mtd->name = np->name; | ||
371 | ret = mtd_device_register(mtd, NULL, 0); | ||
372 | if (ret) | ||
373 | return ret; | ||
374 | |||
375 | host->nor[host->num_chip] = nor; | ||
376 | host->num_chip++; | ||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | static void hisi_spi_nor_unregister_all(struct hifmc_host *host) | ||
381 | { | ||
382 | int i; | ||
383 | |||
384 | for (i = 0; i < host->num_chip; i++) | ||
385 | mtd_device_unregister(&host->nor[i]->mtd); | ||
386 | } | ||
387 | |||
388 | static int hisi_spi_nor_register_all(struct hifmc_host *host) | ||
389 | { | ||
390 | struct device *dev = host->dev; | ||
391 | struct device_node *np; | ||
392 | int ret; | ||
393 | |||
394 | for_each_available_child_of_node(dev->of_node, np) { | ||
395 | ret = hisi_spi_nor_register(np, host); | ||
396 | if (ret) | ||
397 | goto fail; | ||
398 | |||
399 | if (host->num_chip == HIFMC_MAX_CHIP_NUM) { | ||
400 | dev_warn(dev, "Flash device number exceeds the maximum chipselect number\n"); | ||
401 | break; | ||
402 | } | ||
403 | } | ||
404 | |||
405 | return 0; | ||
406 | |||
407 | fail: | ||
408 | hisi_spi_nor_unregister_all(host); | ||
409 | return ret; | ||
410 | } | ||
411 | |||
412 | static int hisi_spi_nor_probe(struct platform_device *pdev) | ||
413 | { | ||
414 | struct device *dev = &pdev->dev; | ||
415 | struct resource *res; | ||
416 | struct hifmc_host *host; | ||
417 | int ret; | ||
418 | |||
419 | host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); | ||
420 | if (!host) | ||
421 | return -ENOMEM; | ||
422 | |||
423 | platform_set_drvdata(pdev, host); | ||
424 | host->dev = dev; | ||
425 | |||
426 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control"); | ||
427 | host->regbase = devm_ioremap_resource(dev, res); | ||
428 | if (IS_ERR(host->regbase)) | ||
429 | return PTR_ERR(host->regbase); | ||
430 | |||
431 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "memory"); | ||
432 | host->iobase = devm_ioremap_resource(dev, res); | ||
433 | if (IS_ERR(host->iobase)) | ||
434 | return PTR_ERR(host->iobase); | ||
435 | |||
436 | host->clk = devm_clk_get(dev, NULL); | ||
437 | if (IS_ERR(host->clk)) | ||
438 | return PTR_ERR(host->clk); | ||
439 | |||
440 | ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); | ||
441 | if (ret) { | ||
442 | dev_warn(dev, "Unable to set dma mask\n"); | ||
443 | return ret; | ||
444 | } | ||
445 | |||
446 | host->buffer = dmam_alloc_coherent(dev, HIFMC_DMA_MAX_LEN, | ||
447 | &host->dma_buffer, GFP_KERNEL); | ||
448 | if (!host->buffer) | ||
449 | return -ENOMEM; | ||
450 | |||
451 | mutex_init(&host->lock); | ||
452 | clk_prepare_enable(host->clk); | ||
453 | hisi_spi_nor_init(host); | ||
454 | ret = hisi_spi_nor_register_all(host); | ||
455 | if (ret) | ||
456 | mutex_destroy(&host->lock); | ||
457 | |||
458 | clk_disable_unprepare(host->clk); | ||
459 | return ret; | ||
460 | } | ||
461 | |||
462 | static int hisi_spi_nor_remove(struct platform_device *pdev) | ||
463 | { | ||
464 | struct hifmc_host *host = platform_get_drvdata(pdev); | ||
465 | |||
466 | hisi_spi_nor_unregister_all(host); | ||
467 | mutex_destroy(&host->lock); | ||
468 | clk_disable_unprepare(host->clk); | ||
469 | return 0; | ||
470 | } | ||
471 | |||
472 | static const struct of_device_id hisi_spi_nor_dt_ids[] = { | ||
473 | { .compatible = "hisilicon,fmc-spi-nor"}, | ||
474 | { /* sentinel */ } | ||
475 | }; | ||
476 | MODULE_DEVICE_TABLE(of, hisi_spi_nor_dt_ids); | ||
477 | |||
478 | static struct platform_driver hisi_spi_nor_driver = { | ||
479 | .driver = { | ||
480 | .name = "hisi-sfc", | ||
481 | .of_match_table = hisi_spi_nor_dt_ids, | ||
482 | }, | ||
483 | .probe = hisi_spi_nor_probe, | ||
484 | .remove = hisi_spi_nor_remove, | ||
485 | }; | ||
486 | module_platform_driver(hisi_spi_nor_driver); | ||
487 | |||
488 | MODULE_LICENSE("GPL v2"); | ||
489 | MODULE_DESCRIPTION("HiSilicon SPI Nor Flash Controller Driver"); | ||