OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | From be64c4dbf6be17ce20ee214478509a3738256d46 Mon Sep 17 00:00:00 2001 |
2 | From: Luke Wren <wren6991@gmail.com> |
||
3 | Date: Sat, 5 Sep 2015 01:16:10 +0100 |
||
4 | Subject: [PATCH] Add SMI NAND driver |
||
5 | |||
6 | Signed-off-by: Luke Wren <wren6991@gmail.com> |
||
7 | --- |
||
8 | .../bindings/mtd/brcm,bcm2835-smi-nand.txt | 42 ++++ |
||
9 | drivers/mtd/nand/Kconfig | 7 + |
||
10 | drivers/mtd/nand/Makefile | 1 + |
||
11 | drivers/mtd/nand/bcm2835_smi_nand.c | 267 +++++++++++++++++++++ |
||
12 | 4 files changed, 317 insertions(+) |
||
13 | create mode 100644 Documentation/devicetree/bindings/mtd/brcm,bcm2835-smi-nand.txt |
||
14 | create mode 100644 drivers/mtd/nand/bcm2835_smi_nand.c |
||
15 | |||
16 | --- /dev/null |
||
17 | +++ b/Documentation/devicetree/bindings/mtd/brcm,bcm2835-smi-nand.txt |
||
18 | @@ -0,0 +1,42 @@ |
||
19 | +* BCM2835 SMI NAND flash |
||
20 | + |
||
21 | +This driver is a shim between the BCM2835 SMI driver (SMI is a peripheral for |
||
22 | +talking to parallel register interfaces) and Linux's MTD layer. |
||
23 | + |
||
24 | +Required properties: |
||
25 | +- compatible: "brcm,bcm2835-smi-nand" |
||
26 | +- status: "okay" |
||
27 | + |
||
28 | +Optional properties: |
||
29 | +- partition@n, where n is an integer from a consecutive sequence starting at 0 |
||
30 | + - Difficult to store partition table on NAND device - normally put it |
||
31 | + in the source code, kernel bootparams, or device tree (the best way!) |
||
32 | + - Sub-properties: |
||
33 | + - label: the partition name, as shown by mtdinfo /dev/mtd* |
||
34 | + - reg: the size and offset of this partition. |
||
35 | + - (optional) read-only: an empty property flagging as read only |
||
36 | + |
||
37 | +Example: |
||
38 | + |
||
39 | +nand: flash@0 { |
||
40 | + compatible = "brcm,bcm2835-smi-nand"; |
||
41 | + status = "okay"; |
||
42 | + |
||
43 | + partition@0 { |
||
44 | + label = "stage2"; |
||
45 | + // 128k |
||
46 | + reg = <0 0x20000>; |
||
47 | + read-only; |
||
48 | + }; |
||
49 | + partition@1 { |
||
50 | + label = "firmware"; |
||
51 | + // 16M |
||
52 | + reg = <0x20000 0x1000000>; |
||
53 | + read-only; |
||
54 | + }; |
||
55 | + partition@2 { |
||
56 | + label = "root"; |
||
57 | + // 2G |
||
58 | + reg = <0x1020000 0x80000000>; |
||
59 | + }; |
||
60 | +}; |
||
61 | \ No newline at end of file |
||
62 | --- a/drivers/mtd/nand/Kconfig |
||
63 | +++ b/drivers/mtd/nand/Kconfig |
||
64 | @@ -41,6 +41,13 @@ config MTD_SM_COMMON |
||
65 | tristate |
||
66 | default n |
||
67 | |||
68 | +config MTD_NAND_BCM2835_SMI |
||
69 | + tristate "Use Broadcom's Secondary Memory Interface as a NAND controller (BCM283x)" |
||
70 | + depends on BCM2835_SMI |
||
71 | + default m |
||
72 | + help |
||
73 | + Uses the BCM2835's SMI peripheral as a NAND controller. |
||
74 | + |
||
75 | config MTD_NAND_DENALI |
||
76 | tristate |
||
77 | |||
78 | --- a/drivers/mtd/nand/Makefile |
||
79 | +++ b/drivers/mtd/nand/Makefile |
||
80 | @@ -14,6 +14,7 @@ obj-$(CONFIG_MTD_NAND_DENALI) += denali |
||
81 | obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o |
||
82 | obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o |
||
83 | obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o |
||
84 | +obj-$(CONFIG_MTD_NAND_BCM2835_SMI) += bcm2835_smi_nand.o |
||
85 | obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o |
||
86 | obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o |
||
87 | obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o |
||
88 | --- /dev/null |
||
89 | +++ b/drivers/mtd/nand/bcm2835_smi_nand.c |
||
90 | @@ -0,0 +1,267 @@ |
||
91 | +/** |
||
92 | + * NAND flash driver for Broadcom Secondary Memory Interface |
||
93 | + * |
||
94 | + * Written by Luke Wren <luke@raspberrypi.org> |
||
95 | + * Copyright (c) 2015, Raspberry Pi (Trading) Ltd. |
||
96 | + * |
||
97 | + * Redistribution and use in source and binary forms, with or without |
||
98 | + * modification, are permitted provided that the following conditions |
||
99 | + * are met: |
||
100 | + * 1. Redistributions of source code must retain the above copyright |
||
101 | + * notice, this list of conditions, and the following disclaimer, |
||
102 | + * without modification. |
||
103 | + * 2. Redistributions in binary form must reproduce the above copyright |
||
104 | + * notice, this list of conditions and the following disclaimer in the |
||
105 | + * documentation and/or other materials provided with the distribution. |
||
106 | + * 3. The names of the above-listed copyright holders may not be used |
||
107 | + * to endorse or promote products derived from this software without |
||
108 | + * specific prior written permission. |
||
109 | + * |
||
110 | + * ALTERNATIVELY, this software may be distributed under the terms of the |
||
111 | + * GNU General Public License ("GPL") version 2, as published by the Free |
||
112 | + * Software Foundation. |
||
113 | + * |
||
114 | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
||
115 | + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
||
116 | + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||
117 | + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||
118 | + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||
119 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||
120 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||
121 | + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||
122 | + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||
123 | + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
124 | + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
125 | + */ |
||
126 | + |
||
127 | +#include <linux/kernel.h> |
||
128 | +#include <linux/module.h> |
||
129 | +#include <linux/of.h> |
||
130 | +#include <linux/platform_device.h> |
||
131 | +#include <linux/slab.h> |
||
132 | +#include <linux/mtd/nand.h> |
||
133 | +#include <linux/mtd/partitions.h> |
||
134 | + |
||
135 | +#include <linux/broadcom/bcm2835_smi.h> |
||
136 | + |
||
137 | +#define DEVICE_NAME "bcm2835-smi-nand" |
||
138 | +#define DRIVER_NAME "smi-nand-bcm2835" |
||
139 | + |
||
140 | +struct bcm2835_smi_nand_host { |
||
141 | + struct bcm2835_smi_instance *smi_inst; |
||
142 | + struct nand_chip nand_chip; |
||
143 | + struct mtd_info mtd; |
||
144 | + struct device *dev; |
||
145 | +}; |
||
146 | + |
||
147 | +/**************************************************************************** |
||
148 | +* |
||
149 | +* NAND functionality implementation |
||
150 | +* |
||
151 | +****************************************************************************/ |
||
152 | + |
||
153 | +#define SMI_NAND_CLE_PIN 0x01 |
||
154 | +#define SMI_NAND_ALE_PIN 0x02 |
||
155 | + |
||
156 | +static inline void bcm2835_smi_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, |
||
157 | + unsigned int ctrl) |
||
158 | +{ |
||
159 | + uint32_t cmd32 = cmd; |
||
160 | + uint32_t addr = ~(SMI_NAND_CLE_PIN | SMI_NAND_ALE_PIN); |
||
161 | + struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent); |
||
162 | + struct bcm2835_smi_instance *inst = host->smi_inst; |
||
163 | + |
||
164 | + if (ctrl & NAND_CLE) |
||
165 | + addr |= SMI_NAND_CLE_PIN; |
||
166 | + if (ctrl & NAND_ALE) |
||
167 | + addr |= SMI_NAND_ALE_PIN; |
||
168 | + /* Lower ALL the CS pins! */ |
||
169 | + if (ctrl & NAND_NCE) |
||
170 | + addr &= (SMI_NAND_CLE_PIN | SMI_NAND_ALE_PIN); |
||
171 | + |
||
172 | + bcm2835_smi_set_address(inst, addr); |
||
173 | + |
||
174 | + if (cmd != NAND_CMD_NONE) |
||
175 | + bcm2835_smi_write_buf(inst, &cmd32, 1); |
||
176 | +} |
||
177 | + |
||
178 | +static inline uint8_t bcm2835_smi_nand_read_byte(struct mtd_info *mtd) |
||
179 | +{ |
||
180 | + uint8_t byte; |
||
181 | + struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent); |
||
182 | + struct bcm2835_smi_instance *inst = host->smi_inst; |
||
183 | + |
||
184 | + bcm2835_smi_read_buf(inst, &byte, 1); |
||
185 | + return byte; |
||
186 | +} |
||
187 | + |
||
188 | +static inline void bcm2835_smi_nand_write_byte(struct mtd_info *mtd, |
||
189 | + uint8_t byte) |
||
190 | +{ |
||
191 | + struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent); |
||
192 | + struct bcm2835_smi_instance *inst = host->smi_inst; |
||
193 | + |
||
194 | + bcm2835_smi_write_buf(inst, &byte, 1); |
||
195 | +} |
||
196 | + |
||
197 | +static inline void bcm2835_smi_nand_write_buf(struct mtd_info *mtd, |
||
198 | + const uint8_t *buf, int len) |
||
199 | +{ |
||
200 | + struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent); |
||
201 | + struct bcm2835_smi_instance *inst = host->smi_inst; |
||
202 | + |
||
203 | + bcm2835_smi_write_buf(inst, buf, len); |
||
204 | +} |
||
205 | + |
||
206 | +static inline void bcm2835_smi_nand_read_buf(struct mtd_info *mtd, |
||
207 | + uint8_t *buf, int len) |
||
208 | +{ |
||
209 | + struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent); |
||
210 | + struct bcm2835_smi_instance *inst = host->smi_inst; |
||
211 | + |
||
212 | + bcm2835_smi_read_buf(inst, buf, len); |
||
213 | +} |
||
214 | + |
||
215 | +/**************************************************************************** |
||
216 | +* |
||
217 | +* Probe and remove functions |
||
218 | +* |
||
219 | +***************************************************************************/ |
||
220 | + |
||
221 | +static int bcm2835_smi_nand_probe(struct platform_device *pdev) |
||
222 | +{ |
||
223 | + struct bcm2835_smi_nand_host *host; |
||
224 | + struct nand_chip *this; |
||
225 | + struct mtd_info *mtd; |
||
226 | + struct device *dev = &pdev->dev; |
||
227 | + struct device_node *node = dev->of_node, *smi_node; |
||
228 | + struct mtd_part_parser_data ppdata; |
||
229 | + struct smi_settings *smi_settings; |
||
230 | + struct bcm2835_smi_instance *smi_inst; |
||
231 | + int ret = -ENXIO; |
||
232 | + |
||
233 | + if (!node) { |
||
234 | + dev_err(dev, "No device tree node supplied!"); |
||
235 | + return -EINVAL; |
||
236 | + } |
||
237 | + |
||
238 | + smi_node = of_parse_phandle(node, "smi_handle", 0); |
||
239 | + |
||
240 | + /* Request use of SMI peripheral: */ |
||
241 | + smi_inst = bcm2835_smi_get(smi_node); |
||
242 | + |
||
243 | + if (!smi_inst) { |
||
244 | + dev_err(dev, "Could not register with SMI."); |
||
245 | + return -EPROBE_DEFER; |
||
246 | + } |
||
247 | + |
||
248 | + /* Set SMI timing and bus width */ |
||
249 | + |
||
250 | + smi_settings = bcm2835_smi_get_settings_from_regs(smi_inst); |
||
251 | + |
||
252 | + smi_settings->data_width = SMI_WIDTH_8BIT; |
||
253 | + smi_settings->read_setup_time = 2; |
||
254 | + smi_settings->read_hold_time = 1; |
||
255 | + smi_settings->read_pace_time = 1; |
||
256 | + smi_settings->read_strobe_time = 3; |
||
257 | + |
||
258 | + smi_settings->write_setup_time = 2; |
||
259 | + smi_settings->write_hold_time = 1; |
||
260 | + smi_settings->write_pace_time = 1; |
||
261 | + smi_settings->write_strobe_time = 3; |
||
262 | + |
||
263 | + bcm2835_smi_set_regs_from_settings(smi_inst); |
||
264 | + |
||
265 | + host = devm_kzalloc(dev, sizeof(struct bcm2835_smi_nand_host), |
||
266 | + GFP_KERNEL); |
||
267 | + if (!host) |
||
268 | + return -ENOMEM; |
||
269 | + |
||
270 | + host->dev = dev; |
||
271 | + host->smi_inst = smi_inst; |
||
272 | + |
||
273 | + platform_set_drvdata(pdev, host); |
||
274 | + |
||
275 | + /* Link the structures together */ |
||
276 | + |
||
277 | + this = &host->nand_chip; |
||
278 | + mtd = &host->mtd; |
||
279 | + mtd->priv = this; |
||
280 | + mtd->owner = THIS_MODULE; |
||
281 | + mtd->dev.parent = dev; |
||
282 | + mtd->name = DRIVER_NAME; |
||
283 | + |
||
284 | + /* 20 us command delay time... */ |
||
285 | + this->chip_delay = 20; |
||
286 | + |
||
287 | + this->priv = host; |
||
288 | + this->cmd_ctrl = bcm2835_smi_nand_cmd_ctrl; |
||
289 | + this->read_byte = bcm2835_smi_nand_read_byte; |
||
290 | + this->write_byte = bcm2835_smi_nand_write_byte; |
||
291 | + this->write_buf = bcm2835_smi_nand_write_buf; |
||
292 | + this->read_buf = bcm2835_smi_nand_read_buf; |
||
293 | + |
||
294 | + this->ecc.mode = NAND_ECC_SOFT; |
||
295 | + |
||
296 | + /* Should never be accessed directly: */ |
||
297 | + |
||
298 | + this->IO_ADDR_R = (void *)0xdeadbeef; |
||
299 | + this->IO_ADDR_W = (void *)0xdeadbeef; |
||
300 | + |
||
301 | + /* First scan to find the device and get the page size */ |
||
302 | + |
||
303 | + if (nand_scan_ident(mtd, 1, NULL)) |
||
304 | + return -ENXIO; |
||
305 | + |
||
306 | + /* Second phase scan */ |
||
307 | + |
||
308 | + if (nand_scan_tail(mtd)) |
||
309 | + return -ENXIO; |
||
310 | + |
||
311 | + ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); |
||
312 | + if (!ret) |
||
313 | + return 0; |
||
314 | + |
||
315 | + nand_release(mtd); |
||
316 | + return -EINVAL; |
||
317 | +} |
||
318 | + |
||
319 | +static int bcm2835_smi_nand_remove(struct platform_device *pdev) |
||
320 | +{ |
||
321 | + struct bcm2835_smi_nand_host *host = platform_get_drvdata(pdev); |
||
322 | + |
||
323 | + nand_release(&host->mtd); |
||
324 | + |
||
325 | + return 0; |
||
326 | +} |
||
327 | + |
||
328 | +/**************************************************************************** |
||
329 | +* |
||
330 | +* Register the driver with device tree |
||
331 | +* |
||
332 | +***************************************************************************/ |
||
333 | + |
||
334 | +static const struct of_device_id bcm2835_smi_nand_of_match[] = { |
||
335 | + {.compatible = "brcm,bcm2835-smi-nand",}, |
||
336 | + { /* sentinel */ } |
||
337 | +}; |
||
338 | + |
||
339 | +MODULE_DEVICE_TABLE(of, bcm2835_smi_nand_of_match); |
||
340 | + |
||
341 | +static struct platform_driver bcm2835_smi_nand_driver = { |
||
342 | + .probe = bcm2835_smi_nand_probe, |
||
343 | + .remove = bcm2835_smi_nand_remove, |
||
344 | + .driver = { |
||
345 | + .name = DRIVER_NAME, |
||
346 | + .owner = THIS_MODULE, |
||
347 | + .of_match_table = bcm2835_smi_nand_of_match, |
||
348 | + }, |
||
349 | +}; |
||
350 | + |
||
351 | +module_platform_driver(bcm2835_smi_nand_driver); |
||
352 | + |
||
353 | +MODULE_ALIAS("platform:smi-nand-bcm2835"); |
||
354 | +MODULE_LICENSE("GPL"); |
||
355 | +MODULE_DESCRIPTION |
||
356 | + ("Driver for NAND chips using Broadcom Secondary Memory Interface"); |
||
357 | +MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>"); |