Skip to content

Commit 5451b49

Browse files
Wren6991popcornmix
authored andcommitted
Add SMI NAND driver
Signed-off-by: Luke Wren <wren6991@gmail.com>
1 parent 0d7dc50 commit 5451b49

File tree

4 files changed

+317
-0
lines changed

4 files changed

+317
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
* BCM2835 SMI NAND flash
2+
3+
This driver is a shim between the BCM2835 SMI driver (SMI is a peripheral for
4+
talking to parallel register interfaces) and Linux's MTD layer.
5+
6+
Required properties:
7+
- compatible: "brcm,bcm2835-smi-nand"
8+
- status: "okay"
9+
10+
Optional properties:
11+
- partition@n, where n is an integer from a consecutive sequence starting at 0
12+
- Difficult to store partition table on NAND device - normally put it
13+
in the source code, kernel bootparams, or device tree (the best way!)
14+
- Sub-properties:
15+
- label: the partition name, as shown by mtdinfo /dev/mtd*
16+
- reg: the size and offset of this partition.
17+
- (optional) read-only: an empty property flagging as read only
18+
19+
Example:
20+
21+
nand: flash@0 {
22+
compatible = "brcm,bcm2835-smi-nand";
23+
status = "okay";
24+
25+
partition@0 {
26+
label = "stage2";
27+
// 128k
28+
reg = <0 0x20000>;
29+
read-only;
30+
};
31+
partition@1 {
32+
label = "firmware";
33+
// 16M
34+
reg = <0x20000 0x1000000>;
35+
read-only;
36+
};
37+
partition@2 {
38+
label = "root";
39+
// 2G
40+
reg = <0x1020000 0x80000000>;
41+
};
42+
};

Diff for: drivers/mtd/nand/Kconfig

+7
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ config MTD_SM_COMMON
4040
tristate
4141
default n
4242

43+
config MTD_NAND_BCM2835_SMI
44+
tristate "Use Broadcom's Secondary Memory Interface as a NAND controller (BCM283x)"
45+
depends on BCM2835_SMI
46+
default m
47+
help
48+
Uses the BCM2835's SMI peripheral as a NAND controller.
49+
4350
config MTD_NAND_DENALI
4451
tristate
4552

Diff for: drivers/mtd/nand/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ obj-$(CONFIG_MTD_NAND_DENALI) += denali.o
1414
obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o
1515
obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o
1616
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
17+
obj-$(CONFIG_MTD_NAND_BCM2835_SMI) += bcm2835_smi_nand.o
1718
obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o
1819
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
1920
obj-$(CONFIG_MTD_NAND_TANGO) += tango_nand.o

Diff for: drivers/mtd/nand/bcm2835_smi_nand.c

+267
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
/**
2+
* NAND flash driver for Broadcom Secondary Memory Interface
3+
*
4+
* Written by Luke Wren <luke@raspberrypi.org>
5+
* Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
6+
*
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions
9+
* are met:
10+
* 1. Redistributions of source code must retain the above copyright
11+
* notice, this list of conditions, and the following disclaimer,
12+
* without modification.
13+
* 2. Redistributions in binary form must reproduce the above copyright
14+
* notice, this list of conditions and the following disclaimer in the
15+
* documentation and/or other materials provided with the distribution.
16+
* 3. The names of the above-listed copyright holders may not be used
17+
* to endorse or promote products derived from this software without
18+
* specific prior written permission.
19+
*
20+
* ALTERNATIVELY, this software may be distributed under the terms of the
21+
* GNU General Public License ("GPL") version 2, as published by the Free
22+
* Software Foundation.
23+
*
24+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25+
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
26+
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35+
*/
36+
37+
#include <linux/kernel.h>
38+
#include <linux/module.h>
39+
#include <linux/of.h>
40+
#include <linux/platform_device.h>
41+
#include <linux/slab.h>
42+
#include <linux/mtd/rawnand.h>
43+
#include <linux/mtd/partitions.h>
44+
45+
#include <linux/broadcom/bcm2835_smi.h>
46+
47+
#define DEVICE_NAME "bcm2835-smi-nand"
48+
#define DRIVER_NAME "smi-nand-bcm2835"
49+
50+
struct bcm2835_smi_nand_host {
51+
struct bcm2835_smi_instance *smi_inst;
52+
struct nand_chip nand_chip;
53+
struct mtd_info mtd;
54+
struct device *dev;
55+
};
56+
57+
/****************************************************************************
58+
*
59+
* NAND functionality implementation
60+
*
61+
****************************************************************************/
62+
63+
#define SMI_NAND_CLE_PIN 0x01
64+
#define SMI_NAND_ALE_PIN 0x02
65+
66+
static inline void bcm2835_smi_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
67+
unsigned int ctrl)
68+
{
69+
uint32_t cmd32 = cmd;
70+
uint32_t addr = ~(SMI_NAND_CLE_PIN | SMI_NAND_ALE_PIN);
71+
struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent);
72+
struct bcm2835_smi_instance *inst = host->smi_inst;
73+
74+
if (ctrl & NAND_CLE)
75+
addr |= SMI_NAND_CLE_PIN;
76+
if (ctrl & NAND_ALE)
77+
addr |= SMI_NAND_ALE_PIN;
78+
/* Lower ALL the CS pins! */
79+
if (ctrl & NAND_NCE)
80+
addr &= (SMI_NAND_CLE_PIN | SMI_NAND_ALE_PIN);
81+
82+
bcm2835_smi_set_address(inst, addr);
83+
84+
if (cmd != NAND_CMD_NONE)
85+
bcm2835_smi_write_buf(inst, &cmd32, 1);
86+
}
87+
88+
static inline uint8_t bcm2835_smi_nand_read_byte(struct mtd_info *mtd)
89+
{
90+
uint8_t byte;
91+
struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent);
92+
struct bcm2835_smi_instance *inst = host->smi_inst;
93+
94+
bcm2835_smi_read_buf(inst, &byte, 1);
95+
return byte;
96+
}
97+
98+
static inline void bcm2835_smi_nand_write_byte(struct mtd_info *mtd,
99+
uint8_t byte)
100+
{
101+
struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent);
102+
struct bcm2835_smi_instance *inst = host->smi_inst;
103+
104+
bcm2835_smi_write_buf(inst, &byte, 1);
105+
}
106+
107+
static inline void bcm2835_smi_nand_write_buf(struct mtd_info *mtd,
108+
const uint8_t *buf, int len)
109+
{
110+
struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent);
111+
struct bcm2835_smi_instance *inst = host->smi_inst;
112+
113+
bcm2835_smi_write_buf(inst, buf, len);
114+
}
115+
116+
static inline void bcm2835_smi_nand_read_buf(struct mtd_info *mtd,
117+
uint8_t *buf, int len)
118+
{
119+
struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent);
120+
struct bcm2835_smi_instance *inst = host->smi_inst;
121+
122+
bcm2835_smi_read_buf(inst, buf, len);
123+
}
124+
125+
/****************************************************************************
126+
*
127+
* Probe and remove functions
128+
*
129+
***************************************************************************/
130+
131+
static int bcm2835_smi_nand_probe(struct platform_device *pdev)
132+
{
133+
struct bcm2835_smi_nand_host *host;
134+
struct nand_chip *this;
135+
struct mtd_info *mtd;
136+
struct device *dev = &pdev->dev;
137+
struct device_node *node = dev->of_node, *smi_node;
138+
struct mtd_part_parser_data ppdata;
139+
struct smi_settings *smi_settings;
140+
struct bcm2835_smi_instance *smi_inst;
141+
int ret = -ENXIO;
142+
143+
if (!node) {
144+
dev_err(dev, "No device tree node supplied!");
145+
return -EINVAL;
146+
}
147+
148+
smi_node = of_parse_phandle(node, "smi_handle", 0);
149+
150+
/* Request use of SMI peripheral: */
151+
smi_inst = bcm2835_smi_get(smi_node);
152+
153+
if (!smi_inst) {
154+
dev_err(dev, "Could not register with SMI.");
155+
return -EPROBE_DEFER;
156+
}
157+
158+
/* Set SMI timing and bus width */
159+
160+
smi_settings = bcm2835_smi_get_settings_from_regs(smi_inst);
161+
162+
smi_settings->data_width = SMI_WIDTH_8BIT;
163+
smi_settings->read_setup_time = 2;
164+
smi_settings->read_hold_time = 1;
165+
smi_settings->read_pace_time = 1;
166+
smi_settings->read_strobe_time = 3;
167+
168+
smi_settings->write_setup_time = 2;
169+
smi_settings->write_hold_time = 1;
170+
smi_settings->write_pace_time = 1;
171+
smi_settings->write_strobe_time = 3;
172+
173+
bcm2835_smi_set_regs_from_settings(smi_inst);
174+
175+
host = devm_kzalloc(dev, sizeof(struct bcm2835_smi_nand_host),
176+
GFP_KERNEL);
177+
if (!host)
178+
return -ENOMEM;
179+
180+
host->dev = dev;
181+
host->smi_inst = smi_inst;
182+
183+
platform_set_drvdata(pdev, host);
184+
185+
/* Link the structures together */
186+
187+
this = &host->nand_chip;
188+
mtd = &host->mtd;
189+
mtd->priv = this;
190+
mtd->owner = THIS_MODULE;
191+
mtd->dev.parent = dev;
192+
mtd->name = DRIVER_NAME;
193+
194+
/* 20 us command delay time... */
195+
this->chip_delay = 20;
196+
197+
this->priv = host;
198+
this->cmd_ctrl = bcm2835_smi_nand_cmd_ctrl;
199+
this->read_byte = bcm2835_smi_nand_read_byte;
200+
this->write_byte = bcm2835_smi_nand_write_byte;
201+
this->write_buf = bcm2835_smi_nand_write_buf;
202+
this->read_buf = bcm2835_smi_nand_read_buf;
203+
204+
this->ecc.mode = NAND_ECC_SOFT;
205+
206+
/* Should never be accessed directly: */
207+
208+
this->IO_ADDR_R = (void *)0xdeadbeef;
209+
this->IO_ADDR_W = (void *)0xdeadbeef;
210+
211+
/* First scan to find the device and get the page size */
212+
213+
if (nand_scan_ident(mtd, 1, NULL))
214+
return -ENXIO;
215+
216+
/* Second phase scan */
217+
218+
if (nand_scan_tail(mtd))
219+
return -ENXIO;
220+
221+
ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
222+
if (!ret)
223+
return 0;
224+
225+
nand_release(mtd);
226+
return -EINVAL;
227+
}
228+
229+
static int bcm2835_smi_nand_remove(struct platform_device *pdev)
230+
{
231+
struct bcm2835_smi_nand_host *host = platform_get_drvdata(pdev);
232+
233+
nand_release(&host->mtd);
234+
235+
return 0;
236+
}
237+
238+
/****************************************************************************
239+
*
240+
* Register the driver with device tree
241+
*
242+
***************************************************************************/
243+
244+
static const struct of_device_id bcm2835_smi_nand_of_match[] = {
245+
{.compatible = "brcm,bcm2835-smi-nand",},
246+
{ /* sentinel */ }
247+
};
248+
249+
MODULE_DEVICE_TABLE(of, bcm2835_smi_nand_of_match);
250+
251+
static struct platform_driver bcm2835_smi_nand_driver = {
252+
.probe = bcm2835_smi_nand_probe,
253+
.remove = bcm2835_smi_nand_remove,
254+
.driver = {
255+
.name = DRIVER_NAME,
256+
.owner = THIS_MODULE,
257+
.of_match_table = bcm2835_smi_nand_of_match,
258+
},
259+
};
260+
261+
module_platform_driver(bcm2835_smi_nand_driver);
262+
263+
MODULE_ALIAS("platform:smi-nand-bcm2835");
264+
MODULE_LICENSE("GPL");
265+
MODULE_DESCRIPTION
266+
("Driver for NAND chips using Broadcom Secondary Memory Interface");
267+
MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>");

0 commit comments

Comments
 (0)