]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blob - drivers/mtd/nand/bcm2835_smi_nand.c
Add SMI NAND driver
[mirror_ubuntu-zesty-kernel.git] / drivers / mtd / nand / bcm2835_smi_nand.c
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/nand.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>");