2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright (C) 2012, Broadcom Corporation. All Rights Reserved.
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #include <linux/version.h>
23 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
24 #include <linux/config.h>
26 #include <linux/module.h>
27 #include <linux/slab.h>
28 #include <linux/ioport.h>
29 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
30 #include <linux/mtd/compatmac.h>
32 #include <linux/mtd/mtd.h>
33 #include <linux/mtd/partitions.h>
34 #include <linux/errno.h>
35 #include <linux/pci.h>
36 #include <linux/delay.h>
48 #include <hndsflash.h>
51 #ifdef CONFIG_MTD_PARTITIONS
52 extern struct mtd_partition
*
53 init_mtd_partitions(hndsflash_t
*sfl
, struct mtd_info
*mtd
, size_t size
);
56 extern void *partitions_lock_init(void);
57 #define BCMSFLASH_LOCK(lock) if (lock) spin_lock(lock)
58 #define BCMSFLASH_UNLOCK(lock) if (lock) spin_unlock(lock)
60 struct bcmsflash_mtd
{
64 struct mtd_erase_region_info region
;
67 /* Private global state */
68 static struct bcmsflash_mtd bcmsflash
;
71 bcmsflash_mtd_poll(hndsflash_t
*sfl
, unsigned int offset
, int timeout
)
77 if (!hndsflash_poll(sfl
, offset
))
80 if (time_after((unsigned long)jiffies
, (unsigned long)now
+ timeout
)) {
81 if (!hndsflash_poll(sfl
, offset
))
84 printk(KERN_ERR
"sflash: timeout\n");
95 bcmsflash_mtd_read(struct mtd_info
*mtd
, loff_t from
, size_t len
, size_t *retlen
, u_char
*buf
)
97 hndsflash_t
*sfl
= ((struct bcmsflash_mtd
*)mtd
->priv
)->sfl
;
100 /* Check address range */
103 if ((from
+ len
) > mtd
->size
)
106 BCMSFLASH_LOCK(mtd
->mlock
);
110 if ((bytes
= hndsflash_read(sfl
, (uint
)from
, len
, buf
))
115 from
+= (loff_t
) bytes
;
121 BCMSFLASH_UNLOCK(mtd
->mlock
);
126 bcmsflash_mtd_write(struct mtd_info
*mtd
, loff_t to
, size_t len
, size_t *retlen
, const u_char
*buf
)
128 hndsflash_t
*sfl
= ((struct bcmsflash_mtd
*)mtd
->priv
)->sfl
;
131 /* Check address range */
134 if ((to
+ len
) > mtd
->size
)
137 BCMSFLASH_LOCK(mtd
->mlock
);
140 if ((bytes
= hndsflash_write(sfl
, (uint
)to
, len
, (u_char
*)buf
))
145 if ((ret
= bcmsflash_mtd_poll(sfl
, (unsigned int)to
, HZ
)))
147 to
+= (loff_t
) bytes
;
153 BCMSFLASH_UNLOCK(mtd
->mlock
);
158 bcmsflash_mtd_erase(struct mtd_info
*mtd
, struct erase_info
*erase
)
160 hndsflash_t
*sfl
= ((struct bcmsflash_mtd
*)mtd
->priv
)->sfl
;
162 unsigned int addr
, len
;
164 /* Check address range */
167 if ((erase
->addr
+ erase
->len
) > mtd
->size
)
170 BCMSFLASH_LOCK(mtd
->mlock
);
174 /* Ensure that requested region is aligned */
175 for (i
= 0; i
< mtd
->numeraseregions
; i
++) {
176 for (j
= 0; j
< mtd
->eraseregions
[i
].numblocks
; j
++) {
177 if (addr
== mtd
->eraseregions
[i
].offset
+
178 mtd
->eraseregions
[i
].erasesize
* j
&&
179 len
>= mtd
->eraseregions
[i
].erasesize
) {
180 if ((ret
= hndsflash_erase(sfl
, addr
)) < 0)
182 if ((ret
= bcmsflash_mtd_poll(sfl
, addr
, 10 * HZ
)))
184 addr
+= mtd
->eraseregions
[i
].erasesize
;
185 len
-= mtd
->eraseregions
[i
].erasesize
;
192 /* Set erase status */
194 erase
->state
= MTD_ERASE_FAILED
;
196 erase
->state
= MTD_ERASE_DONE
;
200 BCMSFLASH_UNLOCK(mtd
->mlock
);
202 /* Call erase callback */
204 erase
->callback(erase
);
210 bcmsflash_mtd_init(void)
214 #ifdef CONFIG_MTD_PARTITIONS
215 struct mtd_partition
*parts
;
219 memset(&bcmsflash
, 0, sizeof(struct bcmsflash_mtd
));
221 /* attach to the backplane */
222 if (!(bcmsflash
.sih
= si_kattach(SI_OSH
))) {
223 printk(KERN_ERR
"bcmsflash: error attaching to backplane\n");
228 /* Initialize serial flash access */
229 if (!(info
= hndsflash_init(bcmsflash
.sih
))) {
230 printk(KERN_ERR
"bcmsflash: found no supported devices\n");
234 bcmsflash
.sfl
= info
;
236 /* Setup region info */
237 bcmsflash
.region
.offset
= 0;
238 bcmsflash
.region
.erasesize
= info
->blocksize
;
239 bcmsflash
.region
.numblocks
= info
->numblocks
;
240 if (bcmsflash
.region
.erasesize
> bcmsflash
.mtd
.erasesize
)
241 bcmsflash
.mtd
.erasesize
= bcmsflash
.region
.erasesize
;
242 bcmsflash
.mtd
.size
= info
->size
;
243 bcmsflash
.mtd
.numeraseregions
= 1;
245 /* Register with MTD */
246 bcmsflash
.mtd
.name
= "bcmsflash";
247 bcmsflash
.mtd
.type
= MTD_NORFLASH
;
248 bcmsflash
.mtd
.flags
= MTD_CAP_NORFLASH
;
249 bcmsflash
.mtd
.eraseregions
= &bcmsflash
.region
;
250 bcmsflash
.mtd
.erase
= bcmsflash_mtd_erase
;
251 bcmsflash
.mtd
.read
= bcmsflash_mtd_read
;
252 bcmsflash
.mtd
.write
= bcmsflash_mtd_write
;
253 bcmsflash
.mtd
.writesize
= 1;
254 bcmsflash
.mtd
.priv
= &bcmsflash
;
255 bcmsflash
.mtd
.owner
= THIS_MODULE
;
256 bcmsflash
.mtd
.mlock
= partitions_lock_init();
257 if (!bcmsflash
.mtd
.mlock
)
260 #ifdef CONFIG_MTD_PARTITIONS
261 parts
= init_mtd_partitions(info
, &bcmsflash
.mtd
, bcmsflash
.mtd
.size
);
263 for (i
= 0; parts
[i
].name
; i
++);
264 ret
= add_mtd_partitions(&bcmsflash
.mtd
, parts
, i
);
266 printk(KERN_ERR
"bcmsflash: add_mtd failed\n");
279 bcmsflash_mtd_exit(void)
281 #ifdef CONFIG_MTD_PARTITIONS
282 del_mtd_partitions(&bcmsflash
.mtd
);
284 del_mtd_device(&bcmsflash
.mtd
);
288 module_init(bcmsflash_mtd_init
);
289 module_exit(bcmsflash_mtd_exit
);