2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright 2006, 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.
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>
29 // #include <bcmutils.h>
37 #ifdef CONFIG_MTD_PARTITIONS
38 extern struct mtd_partition * init_mtd_partitions(struct mtd_info *mtd, size_t size);
44 struct semaphore lock;
46 struct mtd_erase_region_info region;
49 /* Private global state */
50 static struct sflash_mtd sflash;
53 sflash_mtd_poll(struct sflash_mtd *sflash, unsigned int offset, int timeout)
59 if (!sflash_poll(sflash->sbh, sflash->cc, offset)) {
63 if (time_after(jiffies, now + timeout)) {
64 printk(KERN_ERR "sflash: timeout\n");
68 if (current->need_resched) {
69 set_current_state(TASK_UNINTERRUPTIBLE);
70 schedule_timeout(timeout / 10);
79 sflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
81 struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
84 /* Check address range */
91 if ((from + len) > mtd->size)
98 if ((bytes = sflash_read(sflash->sbh, sflash->cc, (uint) from, len, buf)) < 0) {
102 from += (loff_t) bytes;
114 sflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
116 struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
119 /* Check address range */
126 if ((to + len) > mtd->size)
133 if ((bytes = sflash_write(sflash->sbh, sflash->cc, (uint)to, (uint)len, buf)) < 0) {
137 if ((ret = sflash_mtd_poll(sflash, (unsigned int) to, HZ / 10)))
139 to += (loff_t) bytes;
151 sflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
153 struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv;
155 unsigned int addr, len;
157 /* Check address range */
160 if ((erase->addr + erase->len) > mtd->size)
168 /* Ensure that requested region is aligned */
169 for (i = 0; i < mtd->numeraseregions; i++) {
170 for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
171 if (addr == mtd->eraseregions[i].offset + mtd->eraseregions[i].erasesize * j &&
172 len >= mtd->eraseregions[i].erasesize) {
173 if ((ret = sflash_erase(sflash->sbh, sflash->cc, addr)) < 0)
175 if ((ret = sflash_mtd_poll(sflash, addr, 10 * HZ)))
177 addr += mtd->eraseregions[i].erasesize;
178 len -= mtd->eraseregions[i].erasesize;
187 /* Set erase status */
189 erase->state = MTD_ERASE_FAILED;
191 erase->state = MTD_ERASE_DONE;
193 /* Call erase callback */
195 erase->callback(erase);
200 #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
201 #define sflash_mtd_init init_module
202 #define sflash_mtd_exit cleanup_module
206 sflash_mtd_init(void)
208 struct pci_dev *pdev;
212 #ifdef CONFIG_MTD_PARTITIONS
213 struct mtd_partition *parts;
216 if (!(pdev = pci_find_device(VENDOR_BROADCOM, SB_CC, NULL))) {
217 printk(KERN_ERR "sflash: chipcommon not found\n");
221 memset(&sflash, 0, sizeof(struct sflash_mtd));
222 init_MUTEX(&sflash.lock);
224 /* attach to the backplane */
225 if (!(sflash.sbh = sb_kattach(SB_OSH))) {
226 printk(KERN_ERR "sflash: error attaching to backplane\n");
231 /* Map registers and flash base */
232 if (!(sflash.cc = ioremap_nocache(pci_resource_start(pdev, 0),
233 pci_resource_len(pdev, 0)))) {
234 printk(KERN_ERR "sflash: error mapping registers\n");
239 /* Initialize serial flash access */
240 if (!(info = sflash_init(sflash.sbh, sflash.cc))) {
241 printk(KERN_ERR "sflash: found no supported devices\n");
246 printk(KERN_INFO "sflash: found serial flash; blocksize=%dKB, numblocks=%d, size=%dKB\n",info->blocksize/1024,info->numblocks,info->size/1024);
248 /* Setup region info */
249 sflash.region.offset = 0;
250 sflash.region.erasesize = info->blocksize;
251 sflash.region.numblocks = info->numblocks;
252 if (sflash.region.erasesize > sflash.mtd.erasesize)
253 sflash.mtd.erasesize = sflash.region.erasesize;
254 sflash.mtd.size = info->size;
255 sflash.mtd.numeraseregions = 1;
257 /* Register with MTD */
258 sflash.mtd.name = "sflash";
259 sflash.mtd.type = MTD_NORFLASH;
260 sflash.mtd.flags = MTD_CAP_NORFLASH;
261 sflash.mtd.eraseregions = &sflash.region;
262 sflash.mtd.module = THIS_MODULE;
263 sflash.mtd.erase = sflash_mtd_erase;
264 sflash.mtd.read = sflash_mtd_read;
265 sflash.mtd.write = sflash_mtd_write;
266 sflash.mtd.priv = &sflash;
268 #ifdef CONFIG_MTD_PARTITIONS
269 parts = init_mtd_partitions(&sflash.mtd, sflash.mtd.size);
270 for (i = 0; parts[i].name; i++);
271 ret = add_mtd_partitions(&sflash.mtd, parts, i);
273 ret = add_mtd_device(&sflash.mtd);
276 printk(KERN_ERR "sflash: add_mtd failed\n");
284 iounmap((void *) sflash.cc);
286 sb_detach(sflash.sbh);
291 sflash_mtd_exit(void)
293 #ifdef CONFIG_MTD_PARTITIONS
294 del_mtd_partitions(&sflash.mtd);
296 del_mtd_device(&sflash.mtd);
298 iounmap((void *) sflash.cc);
299 sb_detach(sflash.sbh);
302 module_init(sflash_mtd_init);
303 module_exit(sflash_mtd_exit);