1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2025-04-21 12:27:27 +03:00

convert brcm-2.4 to the new target structure

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@7092 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
nbd
2007-05-04 22:13:42 +00:00
parent 6c640d00de
commit 7ed9009bbd
55 changed files with 16571 additions and 17096 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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)