brcm47xx: update bcma and ssb to master-2011-07-21
[librecmc/librecmc.git] / target / linux / brcm47xx / patches-3.0 / 0016-mtd-bcm47xx-add-serial-flash-driver.patch
1 From 8a6398687998886c451c6df381c2320b6dddb3fe Mon Sep 17 00:00:00 2001
2 From: Hauke Mehrtens <hauke@hauke-m.de>
3 Date: Sun, 17 Jul 2011 14:55:45 +0200
4 Subject: [PATCH 16/22] mtd: bcm47xx: add serial flash driver
5
6
7 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
8 ---
9  drivers/mtd/maps/Kconfig          |    9 ++
10  drivers/mtd/maps/Makefile         |    1 +
11  drivers/mtd/maps/bcm47xx-sflash.c |  270 +++++++++++++++++++++++++++++++++++++
12  3 files changed, 280 insertions(+), 0 deletions(-)
13  create mode 100644 drivers/mtd/maps/bcm47xx-sflash.c
14
15 --- a/drivers/mtd/maps/Kconfig
16 +++ b/drivers/mtd/maps/Kconfig
17 @@ -273,6 +273,15 @@ config MTD_BCM47XX_PFLASH
18         help
19           Support for bcm47xx parallel flash
20  
21 +config MTD_BCM47XX_SFLASH
22 +       tristate "bcm47xx serial flash support"
23 +       default y
24 +       depends on BCM47XX
25 +       select MTD_PARTITIONS
26 +       select MTD_BCM47XX_PARTS
27 +       help
28 +         Support for bcm47xx parallel flash
29 +
30  config MTD_DILNETPC
31         tristate "CFI Flash device mapped on DIL/Net PC"
32         depends on X86 && MTD_CFI_INTELEXT && BROKEN
33 --- a/drivers/mtd/maps/Makefile
34 +++ b/drivers/mtd/maps/Makefile
35 @@ -61,3 +61,4 @@ obj-$(CONFIG_MTD_BCM963XX)    += bcm963xx-f
36  obj-$(CONFIG_MTD_LATCH_ADDR)   += latch-addr-flash.o
37  obj-$(CONFIG_MTD_LANTIQ)       += lantiq-flash.o
38  obj-$(CONFIG_MTD_BCM47XX_PFLASH)+= bcm47xx-pflash.o
39 +obj-$(CONFIG_MTD_BCM47XX_SFLASH)+= bcm47xx-sflash.o
40 --- /dev/null
41 +++ b/drivers/mtd/maps/bcm47xx-sflash.c
42 @@ -0,0 +1,270 @@
43 +/*
44 + * Broadcom SiliconBackplane chipcommon serial flash interface
45 + *
46 + * Copyright 2006, Broadcom Corporation      
47 + * All Rights Reserved.      
48 + *       
49 + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY      
50 + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM      
51 + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS      
52 + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.      
53 + *
54 + * $Id$
55 + */
56 +
57 +#define pr_fmt(fmt) "bcm47xx_sflash: " fmt
58 +#include <linux/module.h>
59 +#include <linux/slab.h>
60 +#include <linux/ioport.h>
61 +#include <linux/sched.h>
62 +#include <linux/mtd/mtd.h>
63 +#include <linux/mtd/map.h>
64 +#include <linux/mtd/partitions.h>
65 +#include <linux/errno.h>
66 +#include <linux/delay.h>
67 +
68 +#include <bcm47xx.h>
69 +
70 +#include <linux/bcma/bcma.h>
71 +#include <linux/bcma/bcma_driver_chipcommon.h>
72 +#include <linux/platform_device.h>
73 +
74 +struct sflash_mtd {
75 +       struct bcma_drv_cc *cc;
76 +       struct mtd_info mtd;
77 +       struct mtd_erase_region_info region;
78 +};
79 +
80 +static struct sflash_mtd *sflash;
81 +
82 +static int
83 +sflash_mtd_poll(struct sflash_mtd *sflash, unsigned int offset, int timeout)
84 +{
85 +       unsigned long now = jiffies;
86 +       int ret = 0;
87 +
88 +       for (;;) {
89 +               if (!bcma_sflash_poll(sflash->cc, offset)) {
90 +                       ret = 0;
91 +                       break;
92 +               }
93 +               if (time_after(jiffies, now + timeout)) {
94 +                       pr_err("timeout while polling\n");
95 +                       ret = -ETIMEDOUT;
96 +                       break;
97 +               }
98 +               udelay(1);
99 +       }
100 +
101 +       return ret;
102 +}
103 +
104 +static int
105 +sflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
106 +{
107 +       struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
108 +
109 +       /* Check address range */
110 +       if (!len)
111 +               return 0;
112 +
113 +       if ((from + len) > mtd->size)
114 +               return -EINVAL;
115 +       
116 +       *retlen = 0;
117 +
118 +       while (len) {
119 +               int ret = bcma_sflash_read(sflash->cc, from, len, buf);
120 +               if (ret < 0)
121 +                       return ret;
122 +
123 +               from += (loff_t) ret;
124 +               len -= ret;
125 +               buf += ret;
126 +               *retlen += ret;
127 +       }
128 +
129 +       return 0;
130 +}
131 +
132 +static int
133 +sflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
134 +{
135 +       struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
136 +
137 +       /* Check address range */
138 +       if (!len)
139 +               return 0;
140 +
141 +       if ((to + len) > mtd->size)
142 +               return -EINVAL;
143 +
144 +       *retlen = 0;
145 +       while (len) {
146 +               int bytes;
147 +               int ret = bcma_sflash_write(sflash->cc, to, len, buf);
148 +               if (ret < 0)
149 +                       return ret;
150 +
151 +               bytes = ret;
152 +
153 +               ret = sflash_mtd_poll(sflash, (unsigned int) to, HZ / 10);
154 +               if (ret)
155 +                       return ret;
156 +
157 +               to += (loff_t) bytes;
158 +               len -= bytes;
159 +               buf += bytes;
160 +               *retlen += bytes;
161 +       }
162 +
163 +       return 0;
164 +}
165 +
166 +static int
167 +sflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
168 +{
169 +       struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
170 +       int i, j, ret = 0;
171 +       unsigned int addr, len;
172 +
173 +       /* Check address range */
174 +       if (!erase->len)
175 +               return 0;
176 +       if ((erase->addr + erase->len) > mtd->size)
177 +               return -EINVAL;
178 +
179 +       addr = erase->addr;
180 +       len = erase->len;
181 +
182 +       /* Ensure that requested regions are aligned */
183 +       for (i = 0; i < mtd->numeraseregions; i++) {
184 +               for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
185 +                       if (addr == mtd->eraseregions[i].offset +
186 +                                       mtd->eraseregions[i].erasesize * j &&
187 +                           len >= mtd->eraseregions[i].erasesize) {
188 +                               if ((ret = bcma_sflash_erase(sflash->cc, addr)) < 0)
189 +                                       break;
190 +                               if ((ret = sflash_mtd_poll(sflash, addr, 10 * HZ)))
191 +                                       break;
192 +                               addr += mtd->eraseregions[i].erasesize;
193 +                               len -= mtd->eraseregions[i].erasesize;
194 +                       }
195 +               }
196 +               if (ret)
197 +                       break;
198 +       }
199 +
200 +       /* Set erase status */
201 +       if (ret)
202 +               erase->state = MTD_ERASE_FAILED;
203 +       else 
204 +               erase->state = MTD_ERASE_DONE;
205 +
206 +       /* Call erase callback */
207 +       if (erase->callback)
208 +               erase->callback(erase);
209 +
210 +       return ret;
211 +}
212 +
213 +static const char *probes[] = { "bcm47xx", NULL };
214 +
215 +static int bcm47xx_sflash_probe(struct platform_device *pdev)
216 +{
217 +       int ret = 0;
218 +
219 +       struct mtd_partition *parts;
220 +       int num_partitions = 0;
221 +
222 +       sflash = kzalloc(sizeof(struct sflash_mtd), GFP_KERNEL);
223 +       if (!sflash)
224 +               return -ENOMEM;
225 +
226 +       sflash->cc = &bcm47xx_bus.bcma.bus.drv_cc;
227 +       if (sflash->cc->flash_type != BCMA_SFLASH)
228 +               return -ENODEV;
229 +
230 +       pr_info("found serial flash: blocksize=%dKB, numblocks=%d, size=%dKB\n",
231 +                       sflash->cc->flash.sflash.blocksize/1024,
232 +                       sflash->cc->flash.sflash.numblocks,
233 +                       sflash->cc->flash.sflash.size/1024);
234 +
235 +       /* Setup region info */
236 +       sflash->region.offset = 0;
237 +       sflash->region.erasesize = sflash->cc->flash.sflash.blocksize;
238 +       sflash->region.numblocks = sflash->cc->flash.sflash.numblocks;
239 +       if (sflash->region.erasesize > sflash->mtd.erasesize)
240 +               sflash->mtd.erasesize = sflash->region.erasesize;
241 +       sflash->mtd.size = sflash->cc->flash.sflash.size;
242 +       sflash->mtd.numeraseregions = 1;
243 +
244 +       /* Register with MTD */
245 +       sflash->mtd.name = "bcm47xx-sflash";
246 +       sflash->mtd.type = MTD_NORFLASH;
247 +       sflash->mtd.flags = MTD_CAP_NORFLASH;
248 +       sflash->mtd.eraseregions = &sflash->region;
249 +       sflash->mtd.erase = sflash_mtd_erase;
250 +       sflash->mtd.read = sflash_mtd_read;
251 +       sflash->mtd.write = sflash_mtd_write;
252 +       sflash->mtd.writesize = 1;
253 +       sflash->mtd.priv = sflash;
254 +       sflash->mtd.owner = THIS_MODULE;
255 +
256 +       num_partitions = parse_mtd_partitions(&sflash->mtd, probes, &parts, 0);
257 +       if (num_partitions < 0) {
258 +               ret = num_partitions;
259 +               goto err_destroy;
260 +       }
261 +
262 +       ret = mtd_device_register(&sflash->mtd, parts, num_partitions);
263 +
264 +//     ret = mtd_device_parse_register(bcm47xx_mtd, "bcm47xx", NULL, NULL, 0);
265 +
266 +       if (ret) {
267 +               pr_err("mtd_device_register failed\n");
268 +               return ret;
269 +       }
270 +       return 0;
271 +
272 +err_destroy:
273 +       map_destroy(&sflash->mtd);
274 +       return ret;
275 +}
276 +
277 +static int __devexit bcm47xx_sflash_remove(struct platform_device *pdev)
278 +{
279 +       if (sflash) {
280 +               mtd_device_unregister(&sflash->mtd);
281 +               map_destroy(&sflash->mtd);
282 +       }
283 +       return 0;
284 +}
285 +
286 +static struct platform_driver bcm47xx_sflash_driver = {
287 +       .remove = __devexit_p(bcm47xx_sflash_remove),
288 +       .driver = {
289 +               .name = "bcm47xx_sflash",
290 +               .owner = THIS_MODULE,
291 +       },
292 +};
293 +
294 +static int __init init_bcm47xx_sflash(void)
295 +{
296 +       int ret = platform_driver_probe(&bcm47xx_sflash_driver, bcm47xx_sflash_probe);
297 +
298 +       if (ret)
299 +               pr_err("error registering platform driver: %i\n", ret);
300 +       return ret;
301 +}
302 +
303 +static void __exit exit_bcm47xx_sflash(void)
304 +{
305 +       platform_driver_unregister(&bcm47xx_sflash_driver);
306 +}
307 +
308 +module_init(init_bcm47xx_sflash);
309 +module_exit(exit_bcm47xx_sflash);
310 +
311 +MODULE_LICENSE("GPL");
312 +MODULE_DESCRIPTION("BCM47XX parallel flash driver");