2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright (C) 2009, Broadcom Corporation
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
12 * $Id: sflash.c,v 1.2 2009/11/06 09:55:49 Exp $
15 #include <linux/config.h>
16 #include <linux/module.h>
17 #include <linux/slab.h>
18 #include <linux/ioport.h>
19 #include <linux/mtd/compatmac.h>
20 #include <linux/mtd/mtd.h>
21 #include <linux/mtd/partitions.h>
22 #include <linux/errno.h>
23 #include <linux/pci.h>
24 #include <linux/delay.h>
39 #ifdef CONFIG_MTD_PARTITIONS
40 extern struct mtd_partition
* init_mtd_partitions(struct mtd_info
*mtd
, size_t size
);
48 struct mtd_erase_region_info region
;
51 /* Private global state */
52 static struct sflash_mtd sflash
;
55 sflash_mtd_poll(struct sflash_mtd
*sflash
, unsigned int offset
, int timeout
)
61 if (!sflash_poll(sflash
->sih
, sflash
->cc
, offset
)) {
65 if (time_after((unsigned long)jiffies
, (unsigned long)now
+ timeout
)) {
66 printk(KERN_ERR
"sflash: timeout\n");
77 sflash_mtd_read(struct mtd_info
*mtd
, loff_t from
, size_t len
, size_t *retlen
, u_char
*buf
)
79 struct sflash_mtd
*sflash
= (struct sflash_mtd
*) mtd
->priv
;
82 /* Check address range */
85 if ((from
+ len
) > mtd
->size
)
90 if ((bytes
= sflash_read(sflash
->sih
, sflash
->cc
, (uint
) from
, len
, buf
)) < 0) {
94 from
+= (loff_t
) bytes
;
104 sflash_mtd_write(struct mtd_info
*mtd
, loff_t to
, size_t len
, size_t *retlen
, const u_char
*buf
)
106 struct sflash_mtd
*sflash
= (struct sflash_mtd
*) mtd
->priv
;
109 /* Check address range */
112 if ((to
+ len
) > mtd
->size
)
117 if ((bytes
= sflash_write(sflash
->sih
, sflash
->cc
, (uint
) to
, len
, buf
)) < 0) {
121 if ((ret
= sflash_mtd_poll(sflash
, (unsigned int) to
, HZ
/ 10)))
123 to
+= (loff_t
) bytes
;
133 sflash_mtd_erase(struct mtd_info
*mtd
, struct erase_info
*erase
)
135 struct sflash_mtd
*sflash
= (struct sflash_mtd
*) mtd
->priv
;
137 unsigned int addr
, len
;
139 /* Check address range */
142 if ((erase
->addr
+ erase
->len
) > mtd
->size
)
148 /* Ensure that requested region is aligned */
149 for (i
= 0; i
< mtd
->numeraseregions
; i
++) {
150 for (j
= 0; j
< mtd
->eraseregions
[i
].numblocks
; j
++) {
151 if (addr
== mtd
->eraseregions
[i
].offset
+
152 mtd
->eraseregions
[i
].erasesize
* j
&&
153 len
>= mtd
->eraseregions
[i
].erasesize
) {
154 if ((ret
= sflash_erase(sflash
->sih
, sflash
->cc
, addr
)) < 0)
156 if ((ret
= sflash_mtd_poll(sflash
, addr
, 10 * HZ
)))
158 addr
+= mtd
->eraseregions
[i
].erasesize
;
159 len
-= mtd
->eraseregions
[i
].erasesize
;
166 /* Set erase status */
168 erase
->state
= MTD_ERASE_FAILED
;
170 erase
->state
= MTD_ERASE_DONE
;
172 /* Call erase callback */
174 erase
->callback(erase
);
179 #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
180 #define sflash_mtd_init init_module
181 #define sflash_mtd_exit cleanup_module
185 sflash_mtd_init(void)
189 struct pci_dev
*dev
= NULL
;
190 #ifdef CONFIG_MTD_PARTITIONS
191 struct mtd_partition
*parts
;
195 list_for_each_entry(dev
, &((pci_find_bus(0, 0))->devices
), bus_list
) {
196 if ((dev
!= NULL
) && (dev
->device
== CC_CORE_ID
))
201 printk(KERN_ERR
"sflash: chipcommon not found\n");
205 memset(&sflash
, 0, sizeof(struct sflash_mtd
));
207 /* attach to the backplane */
208 if (!(sflash
.sih
= si_kattach(SI_OSH
))) {
209 printk(KERN_ERR
"sflash: error attaching to backplane\n");
214 /* Map registers and flash base */
215 if (!(sflash
.cc
= ioremap_nocache(
216 pci_resource_start(dev
, 0),
217 pci_resource_len(dev
, 0)))) {
218 printk(KERN_ERR
"sflash: error mapping registers\n");
223 /* Initialize serial flash access */
224 if (!(info
= sflash_init(sflash
.sih
, sflash
.cc
))) {
225 printk(KERN_ERR
"sflash: found no supported devices\n");
230 /* Setup region info */
231 sflash
.region
.offset
= 0;
232 sflash
.region
.erasesize
= info
->blocksize
;
233 sflash
.region
.numblocks
= info
->numblocks
;
234 if (sflash
.region
.erasesize
> sflash
.mtd
.erasesize
)
235 sflash
.mtd
.erasesize
= sflash
.region
.erasesize
;
236 sflash
.mtd
.size
= info
->size
;
237 sflash
.mtd
.numeraseregions
= 1;
239 /* Register with MTD */
240 sflash
.mtd
.name
= "sflash";
241 sflash
.mtd
.type
= MTD_NORFLASH
;
242 sflash
.mtd
.flags
= MTD_CAP_NORFLASH
;
243 sflash
.mtd
.eraseregions
= &sflash
.region
;
244 sflash
.mtd
.erase
= sflash_mtd_erase
;
245 sflash
.mtd
.read
= sflash_mtd_read
;
246 sflash
.mtd
.write
= sflash_mtd_write
;
247 sflash
.mtd
.writesize
= 1;
248 sflash
.mtd
.priv
= &sflash
;
249 sflash
.mtd
.owner
= THIS_MODULE
;
252 #ifdef CONFIG_MTD_PARTITIONS
253 parts
= init_mtd_partitions(&sflash
.mtd
, sflash
.mtd
.size
);
254 for (i
= 0; parts
[i
].name
; i
++);
255 ret
= add_mtd_partitions(&sflash
.mtd
, parts
, i
);
257 printk(KERN_ERR
"sflash: add_mtd failed\n");
265 iounmap((void *) sflash
.cc
);
267 si_detach(sflash
.sih
);
272 sflash_mtd_exit(void)
274 #ifdef CONFIG_MTD_PARTITIONS
275 del_mtd_partitions(&sflash
.mtd
);
277 del_mtd_device(&sflash
.mtd
);
279 iounmap((void *) sflash
.cc
);
280 si_detach(sflash
.sih
);
283 module_init(sflash_mtd_init
);
284 module_exit(sflash_mtd_exit
);