diff options
author | Felix Fietkau <nbd@openwrt.org> | 2007-05-04 22:13:42 +0000 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2007-05-04 22:13:42 +0000 |
commit | 833bb8f485c66b226387a4c1416ae4b171cccef9 (patch) | |
tree | d86fcca8f8dd54c88c107d5f8f5ff5b29bb17dd2 /target/linux/brcm-2.4/files/drivers | |
parent | 7d3baf4131083226f877c1f571f1ae0cb025c018 (diff) | |
download | mtk-20170518-833bb8f485c66b226387a4c1416ae4b171cccef9.zip mtk-20170518-833bb8f485c66b226387a4c1416ae4b171cccef9.tar.gz mtk-20170518-833bb8f485c66b226387a4c1416ae4b171cccef9.tar.bz2 |
convert brcm-2.4 to the new target structure
SVN-Revision: 7092
Diffstat (limited to 'target/linux/brcm-2.4/files/drivers')
3 files changed, 1147 insertions, 0 deletions
diff --git a/target/linux/brcm-2.4/files/drivers/mtd/devices/sflash.c b/target/linux/brcm-2.4/files/drivers/mtd/devices/sflash.c new file mode 100644 index 0000000..a987388 --- /dev/null +++ b/target/linux/brcm-2.4/files/drivers/mtd/devices/sflash.c @@ -0,0 +1,298 @@ +/* + * Broadcom SiliconBackplane chipcommon serial flash interface + * + * Copyright 2001-2003, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id: sflash.c,v 1.1.1.3 2003/11/10 17:43:38 hyin Exp $ + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <linux/mtd/compatmac.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <asm/io.h> + +#ifdef CONFIG_MTD_PARTITIONS +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/minix_fs.h> +#include <linux/ext2_fs.h> +#include <linux/romfs_fs.h> +#include <linux/cramfs_fs.h> +#include <linux/jffs2.h> +#endif + +#include <typedefs.h> +#include <bcmdevs.h> +#include <bcmutils.h> +#include <osl.h> +#include <bcmutils.h> +#include <bcmnvram.h> +#include <sbconfig.h> +#include <sbchipc.h> +#include <sflash.h> +#include <trxhdr.h> + +#ifdef CONFIG_MTD_PARTITIONS +extern struct mtd_partition * init_mtd_partitions(struct mtd_info *mtd, size_t size); +#endif + +struct sflash_mtd { + chipcregs_t *cc; + struct semaphore lock; + struct mtd_info mtd; + struct mtd_erase_region_info regions[1]; +}; + +/* Private global state */ +static struct sflash_mtd sflash; + +static int +sflash_mtd_poll(struct sflash_mtd *sflash, unsigned int offset, int timeout) +{ + int now = jiffies; + int ret = 0; + + for (;;) { + if (!sflash_poll(sflash->cc, offset)) { + ret = 0; + break; + } + if (time_after(jiffies, now + timeout)) { + printk(KERN_ERR "sflash: timeout\n"); + ret = -ETIMEDOUT; + break; + } + if (current->need_resched) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(timeout / 10); + } else + udelay(1); + } + + return ret; +} + +static int +sflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv; + int bytes, ret = 0; + + /* Check address range */ + if (!len) + return 0; + if ((from + len) > mtd->size) + return -EINVAL; + + down(&sflash->lock); + + *retlen = 0; + while (len) { + if ((bytes = sflash_read(sflash->cc, (uint) from, len, buf)) < 0) { + ret = bytes; + break; + } + from += (loff_t) bytes; + len -= bytes; + buf += bytes; + *retlen += bytes; + } + + up(&sflash->lock); + + return ret; +} + +static int +sflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ + struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv; + int bytes, ret = 0; + + /* Check address range */ + if (!len) + return 0; + if ((to + len) > mtd->size) + return -EINVAL; + + down(&sflash->lock); + + *retlen = 0; + while (len) { + if ((bytes = sflash_write(sflash->cc, (uint) to, len, buf)) < 0) { + ret = bytes; + break; + } + if ((ret = sflash_mtd_poll(sflash, (unsigned int) to, HZ / 10))) + break; + to += (loff_t) bytes; + len -= bytes; + buf += bytes; + *retlen += bytes; + } + + up(&sflash->lock); + + return ret; +} + +static int +sflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) +{ + struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv; + int i, j, ret = 0; + unsigned int addr, len; + + /* Check address range */ + if (!erase->len) + return 0; + if ((erase->addr + erase->len) > mtd->size) + return -EINVAL; + + addr = erase->addr; + len = erase->len; + + down(&sflash->lock); + + /* Ensure that requested region is aligned */ + for (i = 0; i < mtd->numeraseregions; i++) { + for (j = 0; j < mtd->eraseregions[i].numblocks; j++) { + if (addr == mtd->eraseregions[i].offset + mtd->eraseregions[i].erasesize * j && + len >= mtd->eraseregions[i].erasesize) { + if ((ret = sflash_erase(sflash->cc, addr)) < 0) + break; + if ((ret = sflash_mtd_poll(sflash, addr, 10 * HZ))) + break; + addr += mtd->eraseregions[i].erasesize; + len -= mtd->eraseregions[i].erasesize; + } + } + if (ret) + break; + } + + up(&sflash->lock); + + /* Set erase status */ + if (ret) + erase->state = MTD_ERASE_FAILED; + else + erase->state = MTD_ERASE_DONE; + + /* Call erase callback */ + if (erase->callback) + erase->callback(erase); + + return ret; +} + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define sflash_mtd_init init_module +#define sflash_mtd_exit cleanup_module +#endif + +mod_init_t +sflash_mtd_init(void) +{ + struct pci_dev *pdev; + int ret = 0; + struct sflash *info; + uint bank, i; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; +#endif + + if (!(pdev = pci_find_device(VENDOR_BROADCOM, SB_CC, NULL))) { + printk(KERN_ERR "sflash: chipcommon not found\n"); + return -ENODEV; + } + + memset(&sflash, 0, sizeof(struct sflash_mtd)); + init_MUTEX(&sflash.lock); + + /* Map registers and flash base */ + if (!(sflash.cc = ioremap_nocache(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)))) { + printk(KERN_ERR "sflash: error mapping registers\n"); + ret = -EIO; + goto fail; + } + + /* Initialize serial flash access */ + info = sflash_init(sflash.cc); + + if (!info) { + printk(KERN_ERR "sflash: found no supported devices\n"); + ret = -ENODEV; + goto fail; + } + + /* Setup banks */ + sflash.regions[0].offset = 0; + sflash.regions[0].erasesize = info->blocksize; + sflash.regions[0].numblocks = info->numblocks; + if (sflash.regions[0].erasesize > sflash.mtd.erasesize) + sflash.mtd.erasesize = sflash.regions[0].erasesize; + if (sflash.regions[0].erasesize * sflash.regions[0].numblocks) { + sflash.mtd.size += sflash.regions[0].erasesize * sflash.regions[0].numblocks; + } + sflash.mtd.numeraseregions = 1; + ASSERT(sflash.mtd.size == info->size); + + /* Register with MTD */ + sflash.mtd.name = "sflash"; + sflash.mtd.type = MTD_NORFLASH; + sflash.mtd.flags = MTD_CAP_NORFLASH; + sflash.mtd.eraseregions = sflash.regions; + sflash.mtd.module = THIS_MODULE; + sflash.mtd.erase = sflash_mtd_erase; + sflash.mtd.read = sflash_mtd_read; + sflash.mtd.write = sflash_mtd_write; + sflash.mtd.priv = &sflash; + +#ifdef CONFIG_MTD_PARTITIONS + parts = init_mtd_partitions(&sflash.mtd, sflash.mtd.size); + for (i = 0; parts[i].name; i++); + ret = add_mtd_partitions(&sflash.mtd, parts, i); +#else + ret = add_mtd_device(&sflash.mtd); +#endif + if (ret) { + printk(KERN_ERR "sflash: add_mtd failed\n"); + goto fail; + } + + return 0; + + fail: + if (sflash.cc) + iounmap((void *) sflash.cc); + return ret; +} + +mod_exit_t +sflash_mtd_exit(void) +{ +#ifdef CONFIG_MTD_PARTITIONS + del_mtd_partitions(&sflash.mtd); +#else + del_mtd_device(&sflash.mtd); +#endif + iounmap((void *) sflash.cc); +} + +module_init(sflash_mtd_init); +module_exit(sflash_mtd_exit); diff --git a/target/linux/brcm-2.4/files/drivers/mtd/maps/bcm947xx-flash.c b/target/linux/brcm-2.4/files/drivers/mtd/maps/bcm947xx-flash.c new file mode 100644 index 0000000..ee02563 --- /dev/null +++ b/target/linux/brcm-2.4/files/drivers/mtd/maps/bcm947xx-flash.c @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2005 Waldemar Brodkorb <wbx@openwrt.org> + * Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org) + * + * original functions for finding root filesystem from Mike Baker + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + * + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * Flash mapping for BCM947XX boards + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/wait.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#ifdef CONFIG_MTD_PARTITIONS +#include <linux/mtd/partitions.h> +#endif +#include <linux/config.h> +#include <linux/squashfs_fs.h> +#include <linux/jffs2.h> +#include <linux/crc32.h> +#include <asm/io.h> + +#include <typedefs.h> +#include <osl.h> +#include <bcmnvram.h> +#include <bcmutils.h> +#include <sbconfig.h> +#include <sbchipc.h> +#include <sbutils.h> +#include <trxhdr.h> + +/* Global SB handle */ +extern void *bcm947xx_sbh; +extern spinlock_t bcm947xx_sbh_lock; + +/* Convenience */ +#define sbh bcm947xx_sbh +#define sbh_lock bcm947xx_sbh_lock + +#define WINDOW_ADDR 0x1fc00000 +#define WINDOW_SIZE 0x400000 +#define BUSWIDTH 2 + +static struct mtd_info *bcm947xx_mtd; + +__u8 bcm947xx_map_read8(struct map_info *map, unsigned long ofs) +{ + if (map->map_priv_2 == 1) + return __raw_readb(map->map_priv_1 + ofs); + + u16 val = __raw_readw(map->map_priv_1 + (ofs & ~1)); + if (ofs & 1) + return ((val >> 8) & 0xff); + else + return (val & 0xff); +} + +__u16 bcm947xx_map_read16(struct map_info *map, unsigned long ofs) +{ + return __raw_readw(map->map_priv_1 + ofs); +} + +__u32 bcm947xx_map_read32(struct map_info *map, unsigned long ofs) +{ + return __raw_readl(map->map_priv_1 + ofs); +} + +void bcm947xx_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + if (len==1) { + memcpy_fromio(to, map->map_priv_1 + from, len); + } else { + int i; + u16 *dest = (u16 *) to; + u16 *src = (u16 *) (map->map_priv_1 + from); + for (i = 0; i < (len / 2); i++) { + dest[i] = src[i]; + } + if (len & 1) + *((u8 *)dest+len-1) = src[i] & 0xff; + } +} + +void bcm947xx_map_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + __raw_writeb(d, map->map_priv_1 + adr); + mb(); +} + +void bcm947xx_map_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + __raw_writew(d, map->map_priv_1 + adr); + mb(); +} + +void bcm947xx_map_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + __raw_writel(d, map->map_priv_1 + adr); + mb(); +} + +void bcm947xx_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio(map->map_priv_1 + to, from, len); +} + +struct map_info bcm947xx_map = { + name: "Physically mapped flash", + size: WINDOW_SIZE, + buswidth: BUSWIDTH, + read8: bcm947xx_map_read8, + read16: bcm947xx_map_read16, + read32: bcm947xx_map_read32, + copy_from: bcm947xx_map_copy_from, + write8: bcm947xx_map_write8, + write16: bcm947xx_map_write16, + write32: bcm947xx_map_write32, + copy_to: bcm947xx_map_copy_to +}; + +#ifdef CONFIG_MTD_PARTITIONS + +static struct mtd_partition bcm947xx_parts[] = { + { name: "cfe", offset: 0, size: 0, mask_flags: MTD_WRITEABLE, }, + { name: "linux", offset: 0, size: 0, }, + { name: "rootfs", offset: 0, size: 0, }, + { name: "nvram", offset: 0, size: 0, }, + { name: NULL, }, +}; + +static int __init +find_cfe_size(struct mtd_info *mtd, size_t size) +{ + struct trx_header *trx; + unsigned char buf[512]; + int off; + size_t len; + int blocksize; + + trx = (struct trx_header *) buf; + + blocksize = mtd->erasesize; + if (blocksize < 0x10000) + blocksize = 0x10000; + + for (off = (128*1024); off < size; off += blocksize) { + memset(buf, 0xe5, sizeof(buf)); + + /* + * Read into buffer + */ + if (MTD_READ(mtd, off, sizeof(buf), &len, buf) || + len != sizeof(buf)) + continue; + + /* found a TRX header */ + if (le32_to_cpu(trx->magic) == TRX_MAGIC) { + goto found; + } + } + + printk(KERN_NOTICE + "%s: Couldn't find bootloader size\n", + mtd->name); + return -1; + + found: + printk(KERN_NOTICE "bootloader size: %d\n", off); + return off; + +} + +/* + * Copied from mtdblock.c + * + * Cache stuff... + * + * Since typical flash erasable sectors are much larger than what Linux's + * buffer cache can handle, we must implement read-modify-write on flash + * sectors for each block write requests. To avoid over-erasing flash sectors + * and to speed things up, we locally cache a whole flash sector while it is + * being written to until a different sector is required. + */ + +static void erase_callback(struct erase_info *done) +{ + wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; + wake_up(wait_q); +} + +static int erase_write (struct mtd_info *mtd, unsigned long pos, + int len, const char *buf) +{ + struct erase_info erase; + DECLARE_WAITQUEUE(wait, current); + wait_queue_head_t wait_q; + size_t retlen; + int ret; + + /* + * First, let's erase the flash block. + */ + + init_waitqueue_head(&wait_q); + erase.mtd = mtd; + erase.callback = erase_callback; + erase.addr = pos; + erase.len = len; + erase.priv = (u_long)&wait_q; + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&wait_q, &wait); + + ret = MTD_ERASE(mtd, &erase); + if (ret) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&wait_q, &wait); + printk (KERN_WARNING "erase of region [0x%lx, 0x%x] " + "on \"%s\" failed\n", + pos, len, mtd->name); + return ret; + } + + schedule(); /* Wait for erase to finish. */ + remove_wait_queue(&wait_q, &wait); + + /* + * Next, writhe data to flash. + */ + + ret = MTD_WRITE (mtd, pos, len, &retlen, buf); + if (ret) + return ret; + if (retlen != len) + return -EIO; + return 0; +} + + + + +static int __init +find_root(struct mtd_info *mtd, size_t size, struct mtd_partition *part) +{ + struct trx_header trx, *trx2; + unsigned char buf[512], *block; + int off, blocksize; + u32 i, crc = ~0; + size_t len; + struct squashfs_super_block *sb = (struct squashfs_super_block *) buf; + + blocksize = mtd->erasesize; + if (blocksize < 0x10000) + blocksize = 0x10000; + + for (off = (128*1024); off < size; off += blocksize) { + memset(&trx, 0xe5, sizeof(trx)); + + /* + * Read into buffer + */ + if (MTD_READ(mtd, off, sizeof(trx), &len, (char *) &trx) || + len != sizeof(trx)) + continue; + + /* found a TRX header */ + if (le32_to_cpu(trx.magic) == TRX_MAGIC) { + part->offset = le32_to_cpu(trx.offsets[2]) ? : + le32_to_cpu(trx.offsets[1]); + part->size = le32_to_cpu(trx.len); + + part->size -= part->offset; + part->offset += off; + + goto found; + } + } + + printk(KERN_NOTICE + "%s: Couldn't find root filesystem\n", + mtd->name); + return -1; + + found: + if (part->size == 0) + return 0; + + if (MTD_READ(mtd, part->offset, sizeof(buf), &len, buf) || len != sizeof(buf)) + return 0; + + /* Move the filesystem outside of the trx */ + part->size = 0; + + if (trx.len != part->offset + part->size - off) { + /* Update the trx offsets and length */ + trx.len = part->offset + part->size - off; + + /* Update the trx crc32 */ + for (i = (u32) &(((struct trx_header *)NULL)->flag_version); i <= trx.len; i += sizeof(buf)) { + if (MTD_READ(mtd, off + i, sizeof(buf), &len, buf) || len != sizeof(buf)) + return 0; + crc = crc32_le(crc, buf, min(sizeof(buf), trx.len - i)); + } + trx.crc32 = crc; + + /* read first eraseblock from the trx */ + trx2 = block = kmalloc(mtd->erasesize, GFP_KERNEL); + if (MTD_READ(mtd, off, mtd->erasesize, &len, block) || len != mtd->erasesize) { + printk("Error accessing the first trx eraseblock\n"); + return 0; + } + + printk("Updating TRX offsets and length:\n"); + printk("old trx = [0x%08x, 0x%08x, 0x%08x], len=0x%08x crc32=0x%08x\n", trx2->offsets[0], trx2->offsets[1], trx2->offsets[2], trx2->len, trx2->crc32); + printk("new trx = [0x%08x, 0x%08x, 0x%08x], len=0x%08x crc32=0x%08x\n", trx.offsets[0], trx.offsets[1], trx.offsets[2], trx.len, trx.crc32); + + /* Write updated trx header to the flash */ + memcpy(block, &trx, sizeof(trx)); + if (mtd->unlock) + mtd->unlock(mtd, off, mtd->erasesize); + erase_write(mtd, off, mtd->erasesize, block); + if (mtd->sync) + mtd->sync(mtd); + kfree(block); + printk("Done\n"); + } + + return part->size; +} + +struct mtd_partition * __init +init_mtd_partitions(struct mtd_info *mtd, size_t size) +{ + int cfe_size; + + if ((cfe_size = find_cfe_size(mtd,size)) < 0) + return NULL; + + /* boot loader */ + bcm947xx_parts[0].offset = 0; + bcm947xx_parts[0].size = cfe_size; + + /* nvram */ + if (cfe_size != 384 * 1024) { + bcm947xx_parts[3].offset = size - ROUNDUP(NVRAM_SPACE, mtd->erasesize); + bcm947xx_parts[3].size = ROUNDUP(NVRAM_SPACE, mtd->erasesize); + } else { + /* nvram (old 128kb config partition on netgear wgt634u) */ + bcm947xx_parts[3].offset = bcm947xx_parts[0].size; + bcm947xx_parts[3].size = ROUNDUP(NVRAM_SPACE, mtd->erasesize); + } + + /* linux (kernel and rootfs) */ + if (cfe_size != 384 * 1024) { + bcm947xx_parts[1].offset = bcm947xx_parts[0].size; + bcm947xx_parts[1].size = bcm947xx_parts[3].offset - + bcm947xx_parts[1].offset; + } else { + /* do not count the elf loader, which is on one block */ + bcm947xx_parts[1].offset = bcm947xx_parts[0].size + + bcm947xx_parts[3].size + mtd->erasesize; + bcm947xx_parts[1].size = size - + bcm947xx_parts[0].size - + (2*bcm947xx_parts[3].size) - + mtd->erasesize; + } + + find_root(mtd,size,&bcm947xx_parts[2]); + bcm947xx_parts[2].size = size - bcm947xx_parts[2].offset - bcm947xx_parts[3].size; + + return bcm947xx_parts; +} + +#endif + + +mod_init_t init_bcm947xx_map(void) +{ + ulong flags; + uint coreidx; + chipcregs_t *cc; + uint32 fltype; + uint window_addr = 0, window_size = 0; + size_t size; + int ret = 0; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; + int i; +#endif + + spin_lock_irqsave(&sbh_lock, flags); + coreidx = sb_coreidx(sbh); + + /* Check strapping option if chipcommon exists */ + if ((cc = sb_setcore(sbh, SB_CC, 0))) { + fltype = readl(&cc->capabilities) & CAP_FLASH_MASK; + if (fltype == PFLASH) { + bcm947xx_map.map_priv_2 = 1; + window_addr = 0x1c000000; + bcm947xx_map.size = window_size = 32 * 1024 * 1024; + if ((readl(&cc->flash_config) & CC_CFG_DS) == 0) + bcm947xx_map.buswidth = 1; + } + } else { + fltype = PFLASH; + bcm947xx_map.map_priv_2 = 0; + window_addr = WINDOW_ADDR; + window_size = WINDOW_SIZE; + } + + sb_setcoreidx(sbh, coreidx); + spin_unlock_irqrestore(&sbh_lock, flags); + + if (fltype != PFLASH) { + printk(KERN_ERR "pflash: found no supported devices\n"); + ret = -ENODEV; + goto fail; + } + + bcm947xx_map.map_priv_1 = (unsigned long) ioremap(window_addr, window_size); + + if (!bcm947xx_map.map_priv_1) { + printk(KERN_ERR "Failed to ioremap\n"); + return -EIO; + } + + if (!(bcm947xx_mtd = do_map_probe("cfi_probe", &bcm947xx_map))) { + printk(KERN_ERR "pflash: cfi_probe failed\n"); + iounmap((void *)bcm947xx_map.map_priv_1); + return -ENXIO; + } + + bcm947xx_mtd->module = THIS_MODULE; + + size = bcm947xx_mtd->size; + + printk(KERN_NOTICE "Flash device: 0x%x at 0x%x\n", size, window_addr); + +#ifdef CONFIG_MTD_PARTITIONS + parts = init_mtd_partitions(bcm947xx_mtd, size); + for (i = 0; parts[i].name; i++); + ret = add_mtd_partitions(bcm947xx_mtd, parts, i); + if (ret) { + printk(KERN_ERR "Flash: add_mtd_partitions failed\n"); + goto fail; + } +#endif + + return 0; + + fail: + if (bcm947xx_mtd) + map_destroy(bcm947xx_mtd); + if (bcm947xx_map.map_priv_1) + iounmap((void *) bcm947xx_map.map_priv_1); + bcm947xx_map.map_priv_1 = 0; + return ret; +} + +mod_exit_t cleanup_bcm947xx_map(void) +{ +#ifdef CONFIG_MTD_PARTITIONS + del_mtd_partitions(bcm947xx_mtd); +#endif + map_destroy(bcm947xx_mtd); + iounmap((void *) bcm947xx_map.map_priv_1); + bcm947xx_map.map_priv_1 = 0; +} + +module_init(init_bcm947xx_map); +module_exit(cleanup_bcm947xx_map); diff --git a/target/linux/brcm-2.4/files/drivers/parport/parport_splink.c b/target/linux/brcm-2.4/files/drivers/parport/parport_splink.c new file mode 100644 index 0000000..0f5a81b --- /dev/null +++ b/target/linux/brcm-2.4/files/drivers/parport/parport_splink.c @@ -0,0 +1,345 @@ +/* Low-level parallel port routines for the ASUS WL-500g built-in port + * + * Author: Nuno Grilo <nuno.grilo@netcabo.pt> + * Based on parport_pc source + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/parport.h> +#include <linux/parport_pc.h> + +#define SPLINK_ADDRESS 0xBF800010 + +#undef DEBUG + +#ifdef DEBUG +#define DPRINTK printk +#else +#define DPRINTK(stuff...) +#endif + + +/* __parport_splink_frob_control differs from parport_splink_frob_control in that + * it doesn't do any extra masking. */ +static __inline__ unsigned char __parport_splink_frob_control (struct parport *p, + unsigned char mask, + unsigned char val) +{ + struct parport_pc_private *priv = p->physport->private_data; + unsigned char *io = (unsigned char *) p->base; + unsigned char ctr = priv->ctr; +#ifdef DEBUG_PARPORT + printk (KERN_DEBUG + "__parport_splink_frob_control(%02x,%02x): %02x -> %02x\n", + mask, val, ctr, ((ctr & ~mask) ^ val) & priv->ctr_writable); +#endif + ctr = (ctr & ~mask) ^ val; + ctr &= priv->ctr_writable; /* only write writable bits. */ + *(io+2) = ctr; + priv->ctr = ctr; /* Update soft copy */ + return ctr; +} + + + +static void parport_splink_data_forward (struct parport *p) +{ + DPRINTK(KERN_DEBUG "parport_splink: parport_data_forward called\n"); + __parport_splink_frob_control (p, 0x20, 0); +} + +static void parport_splink_data_reverse (struct parport *p) +{ + DPRINTK(KERN_DEBUG "parport_splink: parport_data_forward called\n"); + __parport_splink_frob_control (p, 0x20, 0x20); +} + +/* +static void parport_splink_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + DPRINTK(KERN_DEBUG "parport_splink: IRQ handler called\n"); + parport_generic_irq(irq, (struct parport *) dev_id, regs); +} +*/ + +static void parport_splink_enable_irq(struct parport *p) +{ + DPRINTK(KERN_DEBUG "parport_splink: parport_splink_enable_irq called\n"); + __parport_splink_frob_control (p, 0x10, 0x10); +} + +static void parport_splink_disable_irq(struct parport *p) +{ + DPRINTK(KERN_DEBUG "parport_splink: parport_splink_disable_irq called\n"); + __parport_splink_frob_control (p, 0x10, 0); +} + +static void parport_splink_init_state(struct pardevice *dev, struct parport_state *s) +{ + DPRINTK(KERN_DEBUG "parport_splink: parport_splink_init_state called\n"); + s->u.pc.ctr = 0xc | (dev->irq_func ? 0x10 : 0x0); + if (dev->irq_func && + dev->port->irq != PARPORT_IRQ_NONE) + /* Set ackIntEn */ + s->u.pc.ctr |= 0x10; +} + +static void parport_splink_save_state(struct parport *p, struct parport_state *s) +{ + const struct parport_pc_private *priv = p->physport->private_data; + DPRINTK(KERN_DEBUG "parport_splink: parport_splink_save_state called\n"); + s->u.pc.ctr = priv->ctr; +} + +static void parport_splink_restore_state(struct parport *p, struct parport_state *s) +{ + struct parport_pc_private *priv = p->physport->private_data; + unsigned char *io = (unsigned char *) p->base; + unsigned char ctr = s->u.pc.ctr; + + DPRINTK(KERN_DEBUG "parport_splink: parport_splink_restore_state called\n"); + *(io+2) = ctr; + priv->ctr = ctr; +} + +static void parport_splink_setup_interrupt(void) { + return; +} + +static void parport_splink_write_data(struct parport *p, unsigned char d) { + DPRINTK(KERN_DEBUG "parport_splink: write data called\n"); + unsigned char *io = (unsigned char *) p->base; + *io = d; +} + +static unsigned char parport_splink_read_data(struct parport *p) { + DPRINTK(KERN_DEBUG "parport_splink: read data called\n"); + unsigned char *io = (unsigned char *) p->base; + return *io; +} + +static void parport_splink_write_control(struct parport *p, unsigned char d) +{ + const unsigned char wm = (PARPORT_CONTROL_STROBE | + PARPORT_CONTROL_AUTOFD | + PARPORT_CONTROL_INIT | + PARPORT_CONTROL_SELECT); + + DPRINTK(KERN_DEBUG "parport_splink: write control called\n"); + /* Take this out when drivers have adapted to the newer interface. */ + if (d & 0x20) { + printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n", + p->name, p->cad->name); + parport_splink_data_reverse (p); + } + + __parport_splink_frob_control (p, wm, d & wm); +} + +static unsigned char parport_splink_read_control(struct parport *p) +{ + const unsigned char wm = (PARPORT_CONTROL_STROBE | + PARPORT_CONTROL_AUTOFD | + PARPORT_CONTROL_INIT | + PARPORT_CONTROL_SELECT); + DPRINTK(KERN_DEBUG "parport_splink: read control called\n"); + const struct parport_pc_private *priv = p->physport->private_data; + return priv->ctr & wm; /* Use soft copy */ +} + +static unsigned char parport_splink_frob_control (struct parport *p, unsigned char mask, + unsigned char val) +{ + const unsigned char wm = (PARPORT_CONTROL_STROBE | + PARPORT_CONTROL_AUTOFD | + PARPORT_CONTROL_INIT | + PARPORT_CONTROL_SELECT); + + DPRINTK(KERN_DEBUG "parport_splink: frob control called\n"); + /* Take this out when drivers have adapted to the newer interface. */ + if (mask & 0x20) { + printk (KERN_DEBUG "%s (%s): use data_%s for this!\n", + p->name, p->cad->name, + (val & 0x20) ? "reverse" : "forward"); + if (val & 0x20) + parport_splink_data_reverse (p); + else + parport_splink_data_forward (p); + } + + /* Restrict mask and val to control lines. */ + mask &= wm; + val &= wm; + + return __parport_splink_frob_control (p, mask, val); +} + +static unsigned char parport_splink_read_status(struct parport *p) +{ + DPRINTK(KERN_DEBUG "parport_splink: read status called\n"); + unsigned char *io = (unsigned char *) p->base; + return *(io+1); +} + +static void parport_splink_inc_use_count(void) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void parport_splink_dec_use_count(void) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +static struct parport_operations parport_splink_ops = +{ + parport_splink_write_data, + parport_splink_read_data, + + parport_splink_write_control, + parport_splink_read_control, + parport_splink_frob_control, + + parport_splink_read_status, + + parport_splink_enable_irq, + parport_splink_disable_irq, + + parport_splink_data_forward, + parport_splink_data_reverse, + + parport_splink_init_state, + parport_splink_save_state, + parport_splink_restore_state, + + parport_splink_inc_use_count, + parport_splink_dec_use_count, + + parport_ieee1284_epp_write_data, + parport_ieee1284_epp_read_data, + parport_ieee1284_epp_write_addr, + parport_ieee1284_epp_read_addr, + + parport_ieee1284_ecp_write_data, + parport_ieee1284_ecp_read_data, + parport_ieee1284_ecp_write_addr, + + parport_ieee1284_write_compat, + parport_ieee1284_read_nibble, + parport_ieee1284_read_byte, +}; + +/* --- Initialisation code -------------------------------- */ + +static struct parport *parport_splink_probe_port (unsigned long int base) +{ + struct parport_pc_private *priv; + struct parport_operations *ops; + struct parport *p; + + if (check_mem_region(base, 3)) { + printk (KERN_DEBUG "parport (0x%lx): iomem region not available\n", base); + return NULL; + } + priv = kmalloc (sizeof (struct parport_pc_private), GFP_KERNEL); + if (!priv) { + printk (KERN_DEBUG "parport (0x%lx): no memory!\n", base); + return NULL; + } + ops = kmalloc (sizeof (struct parport_operations), GFP_KERNEL); + if (!ops) { + printk (KERN_DEBUG "parport (0x%lx): no memory for ops!\n", + base); + kfree (priv); + return NULL; + } + memcpy (ops, &parport_splink_ops, sizeof (struct parport_operations)); + priv->ctr = 0xc; + priv->ctr_writable = 0xff; + + if (!(p = parport_register_port(base, PARPORT_IRQ_NONE, + PARPORT_DMA_NONE, ops))) { + printk (KERN_DEBUG "parport (0x%lx): registration failed!\n", + base); + kfree (priv); + kfree (ops); + return NULL; + } + + p->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT; + p->size = (p->modes & PARPORT_MODE_EPP)?8:3; + p->private_data = priv; + + parport_proc_register(p); + request_mem_region (p->base, 3, p->name); + + /* Done probing. Now put the port into a sensible start-up state. */ + parport_splink_write_data(p, 0); + parport_splink_data_forward (p); + + /* Now that we've told the sharing engine about the port, and + found out its characteristics, let the high-level drivers + know about it. */ + parport_announce_port (p); + + DPRINTK(KERN_DEBUG "parport (0x%lx): init ok!\n", + base); + return p; +} + +static void parport_splink_unregister_port(struct parport *p) { + struct parport_pc_private *priv = p->private_data; + struct parport_operations *ops = p->ops; + + if (p->irq != PARPORT_IRQ_NONE) + free_irq(p->irq, p); + release_mem_region(p->base, 3); + parport_proc_unregister(p); + kfree (priv); + parport_unregister_port(p); + kfree (ops); +} + + +int parport_splink_init(void) +{ + int ret; + + DPRINTK(KERN_DEBUG "parport_splink init called\n"); + parport_splink_setup_interrupt(); + ret = !parport_splink_probe_port(SPLINK_ADDRESS); + + return ret; +} + +void parport_splink_cleanup(void) { + struct parport *p = parport_enumerate(), *tmp; + DPRINTK(KERN_DEBUG "parport_splink cleanup called\n"); + if (p->size) { + if (p->modes & PARPORT_MODE_PCSPP) { + while(p) { + tmp = p->next; + parport_splink_unregister_port(p); + p = tmp; + } + } + } +} + +MODULE_AUTHOR("Nuno Grilo <nuno.grilo@netcabo.pt>"); +MODULE_DESCRIPTION("Parport Driver for ASUS WL-500g router builtin Port"); +MODULE_SUPPORTED_DEVICE("ASUS WL-500g builtin Parallel Port"); +MODULE_LICENSE("GPL"); + +module_init(parport_splink_init) +module_exit(parport_splink_cleanup) + |