mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2025-04-21 12:27:27 +03:00
add all source code from linksys/broadcom which is free, to cvs for better maintainence inside
openwrt. this gives us the ability to better support different hardware models, without changing any external tar-balls. only et.o and wl.o is missing and is fetched from my webserver. git-svn-id: svn://svn.openwrt.org/openwrt/trunk@379 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
@@ -0,0 +1,855 @@
|
||||
/*
|
||||
* Common Flash Interface support:
|
||||
* SST Standard Vendor Command Set (ID 0x0701)
|
||||
*
|
||||
* Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
|
||||
*
|
||||
* 2_by_8 routines added by Simon Munton
|
||||
*
|
||||
* This code is GPL
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/cfi.h>
|
||||
|
||||
static int cfi_sststd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||
static int cfi_sststd_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
|
||||
static int cfi_sststd_erase_onesize(struct mtd_info *, struct erase_info *);
|
||||
static int cfi_sststd_erase_varsize(struct mtd_info *, struct erase_info *);
|
||||
static void cfi_sststd_sync (struct mtd_info *);
|
||||
static int cfi_sststd_suspend (struct mtd_info *);
|
||||
static void cfi_sststd_resume (struct mtd_info *);
|
||||
|
||||
static void cfi_sststd_destroy(struct mtd_info *);
|
||||
|
||||
struct mtd_info *cfi_cmdset_0701(struct map_info *, int);
|
||||
static struct mtd_info *cfi_sststd_setup (struct map_info *);
|
||||
|
||||
|
||||
static struct mtd_chip_driver cfi_sststd_chipdrv = {
|
||||
probe: NULL, /* Not usable directly */
|
||||
destroy: cfi_sststd_destroy,
|
||||
name: "cfi_cmdset_0701",
|
||||
module: THIS_MODULE
|
||||
};
|
||||
|
||||
struct mtd_info *cfi_cmdset_0701(struct map_info *map, int primary)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
int ofs_factor = cfi->interleave * cfi->device_type;
|
||||
int i;
|
||||
__u8 major, minor;
|
||||
__u32 base = cfi->chips[0].start;
|
||||
|
||||
if (cfi->cfi_mode==1){
|
||||
__u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
|
||||
|
||||
cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL);
|
||||
|
||||
major = cfi_read_query(map, base + (adr+3)*ofs_factor);
|
||||
minor = cfi_read_query(map, base + (adr+4)*ofs_factor);
|
||||
|
||||
printk(" SST Query Table v%c.%c at 0x%4.4X\n",
|
||||
major, minor, adr);
|
||||
cfi_send_gen_cmd(0xf0, 0x5555, base, map, cfi, cfi->device_type, NULL);
|
||||
|
||||
cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x90, 0x5555, base, map, cfi, cfi->device_type, NULL);
|
||||
cfi->mfr = cfi_read_query(map, base);
|
||||
cfi->id = cfi_read_query(map, base + ofs_factor);
|
||||
|
||||
cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL);
|
||||
|
||||
switch (cfi->device_type) {
|
||||
case CFI_DEVICETYPE_X16:
|
||||
cfi->addr_unlock1 = 0x5555;
|
||||
cfi->addr_unlock2 = 0x2AAA;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_NOTICE "Eep. Unknown cfi_cmdset_0701 device type %d\n", cfi->device_type);
|
||||
return NULL;
|
||||
}
|
||||
} /* CFI mode */
|
||||
|
||||
for (i=0; i< cfi->numchips; i++) {
|
||||
cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
|
||||
cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp;
|
||||
cfi->chips[i].erase_time = 1<<cfi->cfiq->BlockEraseTimeoutTyp;
|
||||
}
|
||||
|
||||
map->fldrv = &cfi_sststd_chipdrv;
|
||||
MOD_INC_USE_COUNT;
|
||||
|
||||
cfi_send_gen_cmd(0xf0, 0x5555, base, map, cfi, cfi->device_type, NULL);
|
||||
return cfi_sststd_setup(map);
|
||||
}
|
||||
|
||||
static struct mtd_info *cfi_sststd_setup(struct map_info *map)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
struct mtd_info *mtd;
|
||||
unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave;
|
||||
|
||||
mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
|
||||
printk("number of %s chips: %d\n", (cfi->cfi_mode)?"JEDEC":"CFI",cfi->numchips);
|
||||
|
||||
if (!mtd) {
|
||||
printk("Failed to allocate memory for MTD device\n");
|
||||
kfree(cfi->cmdset_priv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(mtd, 0, sizeof(*mtd));
|
||||
mtd->priv = map;
|
||||
mtd->type = MTD_NORFLASH;
|
||||
/* Also select the correct geometry setup too */
|
||||
mtd->size = devsize * cfi->numchips;
|
||||
|
||||
if (cfi->cfiq->NumEraseRegions == 1) {
|
||||
/* No need to muck about with multiple erase sizes */
|
||||
mtd->erasesize = ((cfi->cfiq->EraseRegionInfo[0] >> 8) & ~0xff) * cfi->interleave;
|
||||
} else {
|
||||
unsigned long offset = 0;
|
||||
int i,j;
|
||||
|
||||
mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
|
||||
mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL);
|
||||
if (!mtd->eraseregions) {
|
||||
printk("Failed to allocate memory for MTD erase region info\n");
|
||||
kfree(cfi->cmdset_priv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
|
||||
unsigned long ernum, ersize;
|
||||
ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave;
|
||||
ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1;
|
||||
|
||||
if (mtd->erasesize < ersize) {
|
||||
mtd->erasesize = ersize;
|
||||
}
|
||||
for (j=0; j<cfi->numchips; j++) {
|
||||
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset;
|
||||
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize;
|
||||
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum;
|
||||
}
|
||||
offset += (ersize * ernum);
|
||||
}
|
||||
|
||||
// debug
|
||||
for (i=0; i<mtd->numeraseregions;i++){
|
||||
printk("%d: offset=0x%x,size=0x%x,blocks=%d\n",
|
||||
i,mtd->eraseregions[i].offset,
|
||||
mtd->eraseregions[i].erasesize,
|
||||
mtd->eraseregions[i].numblocks);
|
||||
}
|
||||
}
|
||||
|
||||
switch (CFIDEV_BUSWIDTH)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
if (mtd->numeraseregions > 1)
|
||||
mtd->erase = cfi_sststd_erase_varsize;
|
||||
else
|
||||
mtd->erase = cfi_sststd_erase_onesize;
|
||||
mtd->read = cfi_sststd_read;
|
||||
mtd->write = cfi_sststd_write;
|
||||
break;
|
||||
|
||||
default:
|
||||
printk("Unsupported buswidth\n");
|
||||
kfree(mtd);
|
||||
kfree(cfi->cmdset_priv);
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
mtd->sync = cfi_sststd_sync;
|
||||
mtd->suspend = cfi_sststd_suspend;
|
||||
mtd->resume = cfi_sststd_resume;
|
||||
mtd->flags = MTD_CAP_NORFLASH;
|
||||
map->fldrv = &cfi_sststd_chipdrv;
|
||||
mtd->name = map->name;
|
||||
MOD_INC_USE_COUNT;
|
||||
return mtd;
|
||||
}
|
||||
|
||||
static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
unsigned long timeo = jiffies + HZ;
|
||||
|
||||
retry:
|
||||
cfi_spin_lock(chip->mutex);
|
||||
|
||||
if (chip->state != FL_READY){
|
||||
printk("Waiting for chip to read, status = %d\n", chip->state);
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&chip->wq, &wait);
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
|
||||
schedule();
|
||||
remove_wait_queue(&chip->wq, &wait);
|
||||
timeo = jiffies + HZ;
|
||||
|
||||
goto retry;
|
||||
}
|
||||
|
||||
adr += chip->start;
|
||||
|
||||
chip->state = FL_READY;
|
||||
|
||||
map->copy_from(map, buf, adr, len);
|
||||
|
||||
wake_up(&chip->wq);
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cfi_sststd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
unsigned long ofs;
|
||||
int chipnum;
|
||||
int ret = 0;
|
||||
|
||||
/* ofs: offset within the first chip that the first read should start */
|
||||
|
||||
chipnum = (from >> cfi->chipshift);
|
||||
ofs = from - (chipnum << cfi->chipshift);
|
||||
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
while (len) {
|
||||
unsigned long thislen;
|
||||
|
||||
if (chipnum >= cfi->numchips)
|
||||
break;
|
||||
|
||||
if ((len + ofs -1) >> cfi->chipshift)
|
||||
thislen = (1<<cfi->chipshift) - ofs;
|
||||
else
|
||||
thislen = len;
|
||||
|
||||
ret = do_read_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
*retlen += thislen;
|
||||
len -= thislen;
|
||||
buf += thislen;
|
||||
|
||||
ofs = 0;
|
||||
chipnum++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum, int fast)
|
||||
{
|
||||
unsigned long timeo = jiffies + HZ;
|
||||
unsigned int Last[4];
|
||||
unsigned long Count = 0;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
int ret = 0;
|
||||
|
||||
retry:
|
||||
cfi_spin_lock(chip->mutex);
|
||||
|
||||
if (chip->state != FL_READY){
|
||||
printk("Waiting for chip to write, status = %d\n", chip->state);
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&chip->wq, &wait);
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
|
||||
schedule();
|
||||
remove_wait_queue(&chip->wq, &wait);
|
||||
printk("Wake up to write:\n");
|
||||
timeo = jiffies + HZ;
|
||||
|
||||
goto retry;
|
||||
}
|
||||
|
||||
chip->state = FL_WRITING;
|
||||
|
||||
adr += chip->start;
|
||||
ENABLE_VPP(map);
|
||||
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
|
||||
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
|
||||
cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
|
||||
|
||||
cfi_write(map, datum, adr);
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
cfi_udelay(chip->word_write_time);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
|
||||
Last[0] = cfi_read(map, adr);
|
||||
// printk("Last[0] is %x\n", Last[0]);
|
||||
Last[1] = cfi_read(map, adr);
|
||||
// printk("Last[1] is %x\n", Last[1]);
|
||||
Last[2] = cfi_read(map, adr);
|
||||
// printk("Last[2] is %x\n", Last[2]);
|
||||
|
||||
for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] && Count < 10000; Count++){
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
cfi_udelay(10);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
|
||||
Last[Count % 4] = cfi_read(map, adr);
|
||||
// printk("Last[%d%%4] is %x\n", Count, Last[Count%4]);
|
||||
}
|
||||
|
||||
if (Last[(Count - 1) % 4] != datum){
|
||||
printk("Last[%ld] is %x, datum is %x\n",(Count - 1) % 4,Last[(Count - 1) % 4],datum);
|
||||
cfi_send_gen_cmd(0xF0, 0, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
DISABLE_VPP(map);
|
||||
ret = -EIO;
|
||||
}
|
||||
DISABLE_VPP(map);
|
||||
chip->state = FL_READY;
|
||||
wake_up(&chip->wq);
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cfi_sststd_write (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
int ret = 0;
|
||||
int chipnum;
|
||||
unsigned long ofs, chipstart;
|
||||
|
||||
*retlen = 0;
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
chipnum = to >> cfi->chipshift;
|
||||
ofs = to - (chipnum << cfi->chipshift);
|
||||
chipstart = cfi->chips[chipnum].start;
|
||||
|
||||
/* If it's not bus-aligned, do the first byte write */
|
||||
if (ofs & (CFIDEV_BUSWIDTH-1)) {
|
||||
unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1);
|
||||
int i = ofs - bus_ofs;
|
||||
int n = 0;
|
||||
u_char tmp_buf[4];
|
||||
__u32 datum;
|
||||
|
||||
map->copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
|
||||
while (len && i < CFIDEV_BUSWIDTH)
|
||||
tmp_buf[i++] = buf[n++], len--;
|
||||
|
||||
if (cfi_buswidth_is_2()) {
|
||||
datum = *(__u16*)tmp_buf;
|
||||
} else if (cfi_buswidth_is_4()) {
|
||||
datum = *(__u32*)tmp_buf;
|
||||
} else {
|
||||
return -EINVAL; /* should never happen, but be safe */
|
||||
}
|
||||
|
||||
ret = do_write_oneword(map, &cfi->chips[chipnum],
|
||||
bus_ofs, datum, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ofs += n;
|
||||
buf += n;
|
||||
(*retlen) += n;
|
||||
|
||||
if (ofs >> cfi->chipshift) {
|
||||
chipnum ++;
|
||||
ofs = 0;
|
||||
if (chipnum == cfi->numchips)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* We are now aligned, write as much as possible */
|
||||
while(len >= CFIDEV_BUSWIDTH) {
|
||||
__u32 datum;
|
||||
|
||||
if (cfi_buswidth_is_1()) {
|
||||
datum = *(__u8*)buf;
|
||||
} else if (cfi_buswidth_is_2()) {
|
||||
datum = *(__u16*)buf;
|
||||
} else if (cfi_buswidth_is_4()) {
|
||||
datum = *(__u32*)buf;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = do_write_oneword(map, &cfi->chips[chipnum],
|
||||
ofs, datum, cfi->fast_prog);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ofs += CFIDEV_BUSWIDTH;
|
||||
buf += CFIDEV_BUSWIDTH;
|
||||
(*retlen) += CFIDEV_BUSWIDTH;
|
||||
len -= CFIDEV_BUSWIDTH;
|
||||
|
||||
if (ofs >> cfi->chipshift) {
|
||||
chipnum ++;
|
||||
ofs = 0;
|
||||
if (chipnum == cfi->numchips)
|
||||
return 0;
|
||||
chipstart = cfi->chips[chipnum].start;
|
||||
}
|
||||
}
|
||||
|
||||
if (len & (CFIDEV_BUSWIDTH-1)) {
|
||||
int i = 0, n = 0;
|
||||
u_char tmp_buf[4];
|
||||
__u32 datum;
|
||||
|
||||
map->copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
|
||||
while (len--)
|
||||
tmp_buf[i++] = buf[n++];
|
||||
|
||||
if (cfi_buswidth_is_2()) {
|
||||
datum = *(__u16*)tmp_buf;
|
||||
} else if (cfi_buswidth_is_4()) {
|
||||
datum = *(__u32*)tmp_buf;
|
||||
} else {
|
||||
return -EINVAL; /* should never happen, but be safe */
|
||||
}
|
||||
|
||||
ret = do_write_oneword(map, &cfi->chips[chipnum],
|
||||
ofs, datum, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
(*retlen) += n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr)
|
||||
{
|
||||
unsigned int status;
|
||||
unsigned long timeo = jiffies + HZ;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
unsigned int rdy_mask;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
retry:
|
||||
cfi_spin_lock(chip->mutex);
|
||||
|
||||
if (chip->state != FL_READY){
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&chip->wq, &wait);
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
|
||||
schedule();
|
||||
remove_wait_queue(&chip->wq, &wait);
|
||||
timeo = jiffies + HZ;
|
||||
|
||||
goto retry;
|
||||
}
|
||||
|
||||
chip->state = FL_ERASING;
|
||||
|
||||
adr += chip->start;
|
||||
ENABLE_VPP(map);
|
||||
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
|
||||
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
|
||||
cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
|
||||
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
|
||||
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
|
||||
cfi_write(map, CMD(0x30), adr);
|
||||
|
||||
timeo = jiffies + (HZ*20);
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
schedule_timeout(HZ);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
|
||||
rdy_mask = CMD(0x80);
|
||||
|
||||
/* Once the state machine's known to be working I'll do that */
|
||||
|
||||
while ( ( (status = cfi_read(map,adr)) & rdy_mask ) != rdy_mask ) {
|
||||
static int z=0;
|
||||
|
||||
if (chip->state != FL_ERASING) {
|
||||
/* Someone's suspended the erase. Sleep */
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&chip->wq, &wait);
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
printk("erase suspended. Sleeping\n");
|
||||
|
||||
schedule();
|
||||
remove_wait_queue(&chip->wq, &wait);
|
||||
timeo = jiffies + (HZ*2);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* OK Still waiting */
|
||||
if (time_after(jiffies, timeo)) {
|
||||
chip->state = FL_READY;
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
printk("waiting for erase to complete timed out.");
|
||||
DISABLE_VPP(map);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Latency issues. Drop the lock, wait a while and retry */
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
|
||||
z++;
|
||||
if ( 0 && !(z % 100 ))
|
||||
printk("chip not ready yet after erase. looping\n");
|
||||
|
||||
cfi_udelay(1);
|
||||
|
||||
cfi_spin_lock(chip->mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Done and happy. */
|
||||
DISABLE_VPP(map);
|
||||
chip->state = FL_READY;
|
||||
wake_up(&chip->wq);
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cfi_sststd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
unsigned long adr, len;
|
||||
int chipnum, ret = 0;
|
||||
int i, first;
|
||||
struct mtd_erase_region_info *regions = mtd->eraseregions;
|
||||
|
||||
if (instr->addr > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
if ((instr->len + instr->addr) > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check that both start and end of the requested erase are
|
||||
* aligned with the erasesize at the appropriate addresses.
|
||||
*/
|
||||
|
||||
i = 0;
|
||||
|
||||
/* Skip all erase regions which are ended before the start of
|
||||
the requested erase. Actually, to save on the calculations,
|
||||
we skip to the first erase region which starts after the
|
||||
start of the requested erase, and then go back one.
|
||||
*/
|
||||
|
||||
while (i < mtd->numeraseregions && instr->addr >= regions[i].offset)
|
||||
i++;
|
||||
i--;
|
||||
|
||||
/* OK, now i is pointing at the erase region in which this
|
||||
erase request starts. Check the start of the requested
|
||||
erase range is aligned with the erase size which is in
|
||||
effect here.
|
||||
*/
|
||||
|
||||
if (instr->addr & (regions[i].erasesize-1))
|
||||
return -EINVAL;
|
||||
|
||||
/* Remember the erase region we start on */
|
||||
first = i;
|
||||
|
||||
/* Next, check that the end of the requested erase is aligned
|
||||
* with the erase region at that address.
|
||||
*/
|
||||
|
||||
while (i<mtd->numeraseregions && (instr->addr + instr->len) >= regions[i].offset)
|
||||
i++;
|
||||
|
||||
/* As before, drop back one to point at the region in which
|
||||
the address actually falls
|
||||
*/
|
||||
i--;
|
||||
|
||||
if ((instr->addr + instr->len) & (regions[i].erasesize-1))
|
||||
return -EINVAL;
|
||||
|
||||
chipnum = instr->addr >> cfi->chipshift;
|
||||
adr = instr->addr - (chipnum << cfi->chipshift);
|
||||
len = instr->len;
|
||||
|
||||
i=first;
|
||||
|
||||
while(len) {
|
||||
ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adr += regions[i].erasesize;
|
||||
len -= regions[i].erasesize;
|
||||
|
||||
if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift)))
|
||||
i++;
|
||||
|
||||
if (adr >> cfi->chipshift) {
|
||||
adr = 0;
|
||||
chipnum++;
|
||||
|
||||
if (chipnum >= cfi->numchips)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
if (instr->callback)
|
||||
instr->callback(instr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cfi_sststd_erase_onesize(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
unsigned long adr, len;
|
||||
int chipnum, ret = 0;
|
||||
|
||||
if (instr->addr & (mtd->erasesize - 1))
|
||||
return -EINVAL;
|
||||
|
||||
if (instr->len & (mtd->erasesize -1))
|
||||
return -EINVAL;
|
||||
|
||||
if ((instr->len + instr->addr) > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
chipnum = instr->addr >> cfi->chipshift;
|
||||
adr = instr->addr - (chipnum << cfi->chipshift);
|
||||
len = instr->len;
|
||||
|
||||
while(len) {
|
||||
ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adr += mtd->erasesize;
|
||||
len -= mtd->erasesize;
|
||||
|
||||
if (adr >> cfi->chipshift) {
|
||||
adr = 0;
|
||||
chipnum++;
|
||||
|
||||
if (chipnum >= cfi->numchips)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
if (instr->callback)
|
||||
instr->callback(instr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cfi_sststd_sync (struct mtd_info *mtd)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
int i;
|
||||
struct flchip *chip;
|
||||
int ret = 0;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
for (i=0; !ret && i<cfi->numchips; i++) {
|
||||
chip = &cfi->chips[i];
|
||||
|
||||
retry:
|
||||
cfi_spin_lock(chip->mutex);
|
||||
|
||||
switch(chip->state) {
|
||||
case FL_READY:
|
||||
case FL_STATUS:
|
||||
case FL_CFI_QUERY:
|
||||
case FL_JEDEC_QUERY:
|
||||
chip->oldstate = chip->state;
|
||||
chip->state = FL_SYNCING;
|
||||
/* No need to wake_up() on this state change -
|
||||
* as the whole point is that nobody can do anything
|
||||
* with the chip now anyway.
|
||||
*/
|
||||
case FL_SYNCING:
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Not an idle state */
|
||||
add_wait_queue(&chip->wq, &wait);
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
|
||||
schedule();
|
||||
|
||||
remove_wait_queue(&chip->wq, &wait);
|
||||
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unlock the chips again */
|
||||
|
||||
for (i--; i >=0; i--) {
|
||||
chip = &cfi->chips[i];
|
||||
|
||||
cfi_spin_lock(chip->mutex);
|
||||
|
||||
if (chip->state == FL_SYNCING) {
|
||||
chip->state = chip->oldstate;
|
||||
wake_up(&chip->wq);
|
||||
}
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int cfi_sststd_suspend(struct mtd_info *mtd)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
int i;
|
||||
struct flchip *chip;
|
||||
int ret = 0;
|
||||
//printk("suspend\n");
|
||||
|
||||
for (i=0; !ret && i<cfi->numchips; i++) {
|
||||
chip = &cfi->chips[i];
|
||||
|
||||
cfi_spin_lock(chip->mutex);
|
||||
|
||||
switch(chip->state) {
|
||||
case FL_READY:
|
||||
case FL_STATUS:
|
||||
case FL_CFI_QUERY:
|
||||
case FL_JEDEC_QUERY:
|
||||
chip->oldstate = chip->state;
|
||||
chip->state = FL_PM_SUSPENDED;
|
||||
/* No need to wake_up() on this state change -
|
||||
* as the whole point is that nobody can do anything
|
||||
* with the chip now anyway.
|
||||
*/
|
||||
case FL_PM_SUSPENDED:
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
}
|
||||
|
||||
/* Unlock the chips again */
|
||||
|
||||
if (ret) {
|
||||
for (i--; i >=0; i--) {
|
||||
chip = &cfi->chips[i];
|
||||
|
||||
cfi_spin_lock(chip->mutex);
|
||||
|
||||
if (chip->state == FL_PM_SUSPENDED) {
|
||||
chip->state = chip->oldstate;
|
||||
wake_up(&chip->wq);
|
||||
}
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cfi_sststd_resume(struct mtd_info *mtd)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
int i;
|
||||
struct flchip *chip;
|
||||
//printk("resume\n");
|
||||
|
||||
for (i=0; i<cfi->numchips; i++) {
|
||||
|
||||
chip = &cfi->chips[i];
|
||||
|
||||
cfi_spin_lock(chip->mutex);
|
||||
|
||||
if (chip->state == FL_PM_SUSPENDED) {
|
||||
chip->state = FL_READY;
|
||||
cfi_write(map, CMD(0xF0), chip->start);
|
||||
wake_up(&chip->wq);
|
||||
}
|
||||
else
|
||||
printk("Argh. Chip not in PM_SUSPENDED state upon resume()\n");
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void cfi_sststd_destroy(struct mtd_info *mtd)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
kfree(cfi->cmdset_priv);
|
||||
kfree(cfi);
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
|
||||
#define cfi_sststd_init init_module
|
||||
#define cfi_sststd_exit cleanup_module
|
||||
#endif
|
||||
|
||||
static char im_name[]="cfi_cmdset_0701";
|
||||
|
||||
mod_init_t cfi_sststd_init(void)
|
||||
{
|
||||
inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0701);
|
||||
return 0;
|
||||
}
|
||||
|
||||
mod_exit_t cfi_sststd_exit(void)
|
||||
{
|
||||
inter_module_unregister(im_name);
|
||||
}
|
||||
|
||||
module_init(cfi_sststd_init);
|
||||
module_exit(cfi_sststd_exit);
|
||||
|
||||
283
openwrt/package/linux/kernel-source/drivers/mtd/devices/sflash.c
Normal file
283
openwrt/package/linux/kernel-source/drivers/mtd/devices/sflash.c
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Broadcom SiliconBackplane chipcommon serial flash interface
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#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 <asm/io.h>
|
||||
|
||||
#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>
|
||||
|
||||
#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 region;
|
||||
};
|
||||
|
||||
/* 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 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 region info */
|
||||
sflash.region.offset = 0;
|
||||
sflash.region.erasesize = info->blocksize;
|
||||
sflash.region.numblocks = info->numblocks;
|
||||
if (sflash.region.erasesize > sflash.mtd.erasesize)
|
||||
sflash.mtd.erasesize = sflash.region.erasesize;
|
||||
sflash.mtd.size = info->size;
|
||||
sflash.mtd.numeraseregions = 1;
|
||||
|
||||
/* Register with MTD */
|
||||
sflash.mtd.name = "sflash";
|
||||
sflash.mtd.type = MTD_NORFLASH;
|
||||
sflash.mtd.flags = MTD_CAP_NORFLASH;
|
||||
sflash.mtd.eraseregions = &sflash.region;
|
||||
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);
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Flash mapping for BCM947XX boards
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/config.h>
|
||||
|
||||
#include <typedefs.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
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
extern struct mtd_partition * init_mtd_partitions(struct mtd_info *mtd, size_t size);
|
||||
#endif
|
||||
|
||||
#define WINDOW_ADDR 0x1fc00000
|
||||
#define WINDOW_SIZE 0x400000
|
||||
#define BUSWIDTH 2
|
||||
|
||||
/* e.g., flash=2M or flash=4M */
|
||||
static int flash = 0;
|
||||
MODULE_PARM(flash, "i");
|
||||
static int __init
|
||||
bcm947xx_setup(char *str)
|
||||
{
|
||||
flash = memparse(str, &str);
|
||||
return 1;
|
||||
}
|
||||
__setup("flash=", bcm947xx_setup);
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
|
||||
#define init_bcm947xx_map init_module
|
||||
#define cleanup_bcm947xx_map cleanup_module
|
||||
#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 "pflash: ioremap failed\n");
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(bcm947xx_mtd = do_map_probe("cfi_probe", &bcm947xx_map))) {
|
||||
printk(KERN_ERR "pflash: cfi_probe failed\n");
|
||||
ret = -ENXIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bcm947xx_mtd->module = THIS_MODULE;
|
||||
|
||||
/* Allow size override for testing */
|
||||
size = flash ? : 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 "pflash: 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);
|
||||
@@ -0,0 +1,9 @@
|
||||
O_TARGET := diag.o
|
||||
|
||||
MAC_OBJS := diag_led.o
|
||||
|
||||
export-objs :=
|
||||
obj-y := $(MAC_OBJS)
|
||||
obj-m := $(O_TARGET)
|
||||
|
||||
include $(TOPDIR)/Rules.make
|
||||
210
openwrt/package/linux/kernel-source/drivers/net/diag/diag_led.c
Normal file
210
openwrt/package/linux/kernel-source/drivers/net/diag/diag_led.c
Normal file
@@ -0,0 +1,210 @@
|
||||
// replacement diag module
|
||||
// (c) 2004 openwrt
|
||||
// mbm at alt dot org
|
||||
//
|
||||
// initial release 2004/03/28
|
||||
//
|
||||
// 2004/08/26 asus & buffalo support added
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <asm/io.h>
|
||||
#include <typedefs.h>
|
||||
#include <bcm4710.h>
|
||||
#include <sbutils.h>
|
||||
|
||||
extern char * nvram_get(const char *name);
|
||||
static void *sbh;
|
||||
|
||||
// v2.x - - - - -
|
||||
#define DIAG_GPIO (1<<1)
|
||||
#define DMZ_GPIO (1<<7)
|
||||
|
||||
static void set_gpio(uint32 mask, uint32 value) {
|
||||
sb_gpiocontrol(sbh,mask,0);
|
||||
sb_gpioouten(sbh,mask,mask);
|
||||
sb_gpioout(sbh,mask,value);
|
||||
}
|
||||
|
||||
static void v2_set_diag(u8 state) {
|
||||
set_gpio(DIAG_GPIO,state);
|
||||
}
|
||||
static void v2_set_dmz(u8 state) {
|
||||
set_gpio(DMZ_GPIO,state);
|
||||
}
|
||||
|
||||
// v1.x - - - - -
|
||||
#define LED_DIAG 0x13
|
||||
#define LED_DMZ 0x12
|
||||
|
||||
static void v1_set_diag(u8 state) {
|
||||
if (!state) {
|
||||
*(volatile u8*)(KSEG1ADDR(BCM4710_EUART)+LED_DIAG)=0xFF;
|
||||
} else {
|
||||
*(volatile u8*)(KSEG1ADDR(BCM4710_EUART)+LED_DIAG);
|
||||
}
|
||||
}
|
||||
static void v1_set_dmz(u8 state) {
|
||||
if (!state) {
|
||||
*(volatile u8*)(KSEG1ADDR(BCM4710_EUART)+LED_DMZ)=0xFF;
|
||||
} else {
|
||||
*(volatile u8*)(KSEG1ADDR(BCM4710_EUART)+LED_DMZ);
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - -
|
||||
static void ignore(u8 ignored) {};
|
||||
|
||||
// - - - - -
|
||||
#define BIT_DMZ 0x01
|
||||
#define BIT_DIAG 0x04
|
||||
|
||||
void (*set_diag)(u8 state);
|
||||
void (*set_dmz)(u8 state);
|
||||
|
||||
static unsigned int diag = 0;
|
||||
|
||||
static void diag_change()
|
||||
{
|
||||
//printk(KERN_INFO "led -> %02x\n",diag);
|
||||
|
||||
set_diag(0xFF); // off
|
||||
set_dmz(0xFF); // off
|
||||
|
||||
if(diag & BIT_DIAG)
|
||||
set_diag(0x00); // on
|
||||
if(diag & BIT_DMZ)
|
||||
set_dmz(0x00); // on
|
||||
}
|
||||
|
||||
static int proc_diag(ctl_table *table, int write, struct file *filp,
|
||||
void *buffer, size_t *lenp)
|
||||
{
|
||||
int r;
|
||||
r = proc_dointvec(table, write, filp, buffer, lenp);
|
||||
if (write && !r) {
|
||||
diag_change();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// - - - - -
|
||||
static unsigned char reset_gpio = 0;
|
||||
static unsigned char reset_polarity = 0;
|
||||
static unsigned int reset = 0;
|
||||
|
||||
static int proc_reset(ctl_table *table, int write, struct file *filp,
|
||||
void *buffer, size_t *lenp)
|
||||
{
|
||||
|
||||
if (reset_gpio) {
|
||||
sb_gpiocontrol(sbh,reset_gpio,reset_gpio);
|
||||
sb_gpioouten(sbh,reset_gpio,0);
|
||||
reset=!(sb_gpioin(sbh)&reset_gpio);
|
||||
|
||||
if (reset_polarity) reset=!reset;
|
||||
} else {
|
||||
reset=0;
|
||||
}
|
||||
|
||||
return proc_dointvec(table, write, filp, buffer, lenp);
|
||||
}
|
||||
|
||||
// - - - - -
|
||||
static struct ctl_table_header *diag_sysctl_header;
|
||||
|
||||
static ctl_table sys_diag[] = {
|
||||
{
|
||||
ctl_name: 2000,
|
||||
procname: "diag",
|
||||
data: &diag,
|
||||
maxlen: sizeof(diag),
|
||||
mode: 0644,
|
||||
proc_handler: proc_diag
|
||||
},
|
||||
{
|
||||
ctl_name: 2001,
|
||||
procname: "reset",
|
||||
data: &reset,
|
||||
maxlen: sizeof(reset),
|
||||
mode: 0444,
|
||||
proc_handler: proc_reset
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static int __init diag_init()
|
||||
{
|
||||
char *buf;
|
||||
u32 board_type;
|
||||
sbh = sb_kattach();
|
||||
sb_gpiosetcore(sbh);
|
||||
|
||||
board_type = sb_boardtype(sbh);
|
||||
printk(KERN_INFO "diag boardtype: %08x\n",board_type);
|
||||
|
||||
set_diag=ignore;
|
||||
set_dmz=ignore;
|
||||
|
||||
if ((board_type & 0xf00) == 0x400) {
|
||||
board_type=1;
|
||||
buf=nvram_get("boardtype")?:"";
|
||||
if (!strcmp(buf,"bcm94710dev")) {
|
||||
buf=nvram_get("boardnum")?:"";
|
||||
if (!strcmp(buf,"42")) {
|
||||
// wrt54g v1.x
|
||||
set_diag=v1_set_diag;
|
||||
set_dmz=v1_set_dmz;
|
||||
reset_gpio=(1<<6);
|
||||
reset_polarity=0;
|
||||
} else if (!strcmp(buf,"asusX")) {
|
||||
//asus wl-500g
|
||||
//no leds
|
||||
reset_gpio=(1<<6);
|
||||
reset_polarity=1;
|
||||
}
|
||||
} else if (!strcmp(buf,"bcm94710ap")) {
|
||||
buf=nvram_get("boardnum")?:"";
|
||||
if (!strcmp(buf,"42")) {
|
||||
// buffalo
|
||||
set_dmz=v2_set_dmz;
|
||||
reset_gpio=(1<<4);
|
||||
reset_polarity=1;
|
||||
} else if (!strcmp(buf,"44")) {
|
||||
//dell truemobile
|
||||
set_dmz=v2_set_dmz;
|
||||
reset_gpio=(1<<0);
|
||||
reset_polarity=0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
board_type=2;
|
||||
set_diag=v2_set_diag;
|
||||
set_dmz=v2_set_dmz;
|
||||
reset_gpio=(1<<6);
|
||||
reset_polarity=0;
|
||||
buf=nvram_get("boardnum")?:"";
|
||||
if (!strcmp(buf,"44")) {
|
||||
set_diag=ignore;
|
||||
set_dmz=ignore;
|
||||
reset_gpio=(1<<5);
|
||||
reset_polarity=0;
|
||||
}
|
||||
}
|
||||
printk(KERN_INFO "using v%d hardware\n",board_type);
|
||||
|
||||
diag_sysctl_header = register_sysctl_table(sys_diag, 0);
|
||||
diag_change();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit diag_exit()
|
||||
{
|
||||
unregister_sysctl_table(diag_sysctl_header);
|
||||
}
|
||||
|
||||
module_init(diag_init);
|
||||
module_exit(diag_exit);
|
||||
34
openwrt/package/linux/kernel-source/drivers/net/et/Makefile
Normal file
34
openwrt/package/linux/kernel-source/drivers/net/et/Makefile
Normal file
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# Makefile for the Broadcom et driver
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
|
||||
O_TARGET := et.o
|
||||
|
||||
ET_OBJS := et_linux.o etc.o
|
||||
|
||||
ifeq ($(CONFIG_ET_47XX),y)
|
||||
ET_OBJS += etc47xx.o etc_robo.o etc_adm.o
|
||||
EXTRA_CFLAGS += -DBCM47XX_CHOPS
|
||||
endif
|
||||
ifeq ($(CONFIG_ET_4413),y)
|
||||
ET_OBJS += etc4413.o
|
||||
EXTRA_CFLAGS += -DBCM4413_CHOPS
|
||||
endif
|
||||
|
||||
export-objs :=
|
||||
obj-y := $(ET_OBJS)
|
||||
obj-m := $(O_TARGET)
|
||||
|
||||
EXTRA_CFLAGS += -DDMA
|
||||
|
||||
include $(TOPDIR)/Rules.make
|
||||
@@ -0,0 +1,35 @@
|
||||
#
|
||||
# Broadcom Home Networking Division (HND) driver configuration
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
mainmenu_option next_comment
|
||||
comment 'Broadcom HND network devices'
|
||||
tristate 'Broadcom HND network device support' CONFIG_HND $CONFIG_PCI
|
||||
if [ "$CONFIG_HND" != "n" ]; then
|
||||
dep_tristate ' Broadcom InsideLine HPNA support' CONFIG_IL $CONFIG_HND
|
||||
if [ "$CONFIG_IL" != "n" ]; then
|
||||
bool ' Broadcom BCM42xx support' CONFIG_IL_42XX
|
||||
bool ' Broadcom BCM47xx support' CONFIG_IL_47XX
|
||||
int ' LARQ buffer allocation (0 = tiny, 2 = huge)' CONFIG_LARQ_BUF 0
|
||||
fi
|
||||
dep_tristate ' Broadcom 10/100 Ethernet support' CONFIG_ET $CONFIG_HND
|
||||
if [ "$CONFIG_ET" != "n" ]; then
|
||||
bool ' Broadcom BCM4413 support' CONFIG_ET_4413
|
||||
bool ' Broadcom BCM47xx support' CONFIG_ET_47XX
|
||||
fi
|
||||
dep_tristate ' Broadcom BCM43xx 802.11 Wireless support' CONFIG_WL $CONFIG_HND
|
||||
if [ "$CONFIG_WL" != "n" ]; then
|
||||
bool ' Access Point Mode Supported' CONFIG_WL_AP
|
||||
bool ' STA Mode Supported' CONFIG_WL_STA
|
||||
bool ' OID Interface Supported' CONFIG_WL_OID
|
||||
fi
|
||||
fi
|
||||
endmenu
|
||||
30
openwrt/package/linux/kernel-source/drivers/net/hnd/Makefile
Normal file
30
openwrt/package/linux/kernel-source/drivers/net/hnd/Makefile
Normal file
@@ -0,0 +1,30 @@
|
||||
#
|
||||
# Makefile for Broadcom Home Networking Division (HND) shared driver code
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
|
||||
O_TARGET := hnd.o
|
||||
|
||||
HND_OBJS := bcmutils.o hnddma.o linux_osl.o sbutils.o bcmsrom.o
|
||||
|
||||
ifneq ($(CONFIG_BCM947XX),y)
|
||||
HND_OBJS += nvramstubs.o
|
||||
endif
|
||||
|
||||
export-objs := shared_ksyms.o
|
||||
obj-y := shared_ksyms.o $(HND_OBJS)
|
||||
obj-m := $(O_TARGET)
|
||||
|
||||
include $(TOPDIR)/Rules.make
|
||||
|
||||
shared_ksyms.c: shared_ksyms.sh $(HND_OBJS)
|
||||
sh -e $< $(HND_OBJS) > $@
|
||||
711
openwrt/package/linux/kernel-source/drivers/net/hnd/bcmsrom.c
Normal file
711
openwrt/package/linux/kernel-source/drivers/net/hnd/bcmsrom.c
Normal file
@@ -0,0 +1,711 @@
|
||||
/*
|
||||
* Misc useful routines to access NIC SROM
|
||||
*
|
||||
* 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.
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include <typedefs.h>
|
||||
#include <osl.h>
|
||||
#include <bcmutils.h>
|
||||
#include <bcmsrom.h>
|
||||
#include <bcmdevs.h>
|
||||
#include <bcmendian.h>
|
||||
#include <sbpcmcia.h>
|
||||
#include <pcicfg.h>
|
||||
#include <sbutils.h>
|
||||
|
||||
#include <proto/ethernet.h> /* for sprom content groking */
|
||||
|
||||
#define VARS_MAX 4096 /* should be reduced */
|
||||
|
||||
static int initvars_srom_pci(void *curmap, char **vars, int *count);
|
||||
static int initvars_cis_pcmcia(void *sbh, void *curmap, void *osh, char **vars, int *count);
|
||||
static int sprom_cmd_pcmcia(void *osh, uint8 cmd);
|
||||
static int sprom_read_pcmcia(void *osh, uint16 addr, uint16 *data);
|
||||
static int sprom_write_pcmcia(void *osh, uint16 addr, uint16 data);
|
||||
static int sprom_read_pci(uint16 *sprom, uint byteoff, uint16 *buf, uint nbytes, bool check_crc);
|
||||
|
||||
/*
|
||||
* Initialize the vars from the right source for this platform.
|
||||
* Return 0 on success, nonzero on error.
|
||||
*/
|
||||
int
|
||||
srom_var_init(void *sbh, uint bus, void *curmap, void *osh, char **vars, int *count)
|
||||
{
|
||||
if (vars == NULL)
|
||||
return (0);
|
||||
|
||||
switch (bus) {
|
||||
case SB_BUS:
|
||||
/* These two could be asserts ... */
|
||||
*vars = NULL;
|
||||
*count = 0;
|
||||
return(0);
|
||||
|
||||
case PCI_BUS:
|
||||
ASSERT(curmap); /* can not be NULL */
|
||||
return(initvars_srom_pci(curmap, vars, count));
|
||||
|
||||
case PCMCIA_BUS:
|
||||
return(initvars_cis_pcmcia(sbh, curmap, osh, vars, count));
|
||||
|
||||
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
|
||||
/* support only 16-bit word read from srom */
|
||||
int
|
||||
srom_read(uint bus, void *curmap, void *osh, uint byteoff, uint nbytes, uint16 *buf)
|
||||
{
|
||||
void *srom;
|
||||
uint i, off, nw;
|
||||
|
||||
/* check input - 16-bit access only */
|
||||
if (byteoff & 1 || nbytes & 1 || (byteoff + nbytes) > (SPROM_SIZE * 2))
|
||||
return 1;
|
||||
|
||||
if (bus == PCI_BUS) {
|
||||
if (!curmap)
|
||||
return 1;
|
||||
srom = (void *)((uint)curmap + PCI_BAR0_SPROM_OFFSET);
|
||||
if (sprom_read_pci(srom, byteoff, buf, nbytes, FALSE))
|
||||
return 1;
|
||||
} else if (bus == PCMCIA_BUS) {
|
||||
off = byteoff / 2;
|
||||
nw = nbytes / 2;
|
||||
for (i = 0; i < nw; i++) {
|
||||
if (sprom_read_pcmcia(osh, (uint16)(off + i), (uint16*)(buf + i)))
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* support only 16-bit word write into srom */
|
||||
int
|
||||
srom_write(uint bus, void *curmap, void *osh, uint byteoff, uint nbytes, uint16 *buf)
|
||||
{
|
||||
uint16 *srom;
|
||||
uint i, off, nw, crc_range;
|
||||
uint16 image[SPROM_SIZE], *p;
|
||||
uint8 crc;
|
||||
volatile uint32 val32;
|
||||
|
||||
/* check input - 16-bit access only */
|
||||
if (byteoff & 1 || nbytes & 1 || (byteoff + nbytes) > (SPROM_SIZE * 2))
|
||||
return 1;
|
||||
|
||||
crc_range = ((bus == PCMCIA_BUS) ? SPROM_SIZE : SPROM_CRC_RANGE) * 2;
|
||||
|
||||
/* if changes made inside crc cover range */
|
||||
if (byteoff < crc_range) {
|
||||
nw = (((byteoff + nbytes) > crc_range) ? byteoff + nbytes : crc_range) / 2;
|
||||
/* read data including entire first 64 words from srom */
|
||||
if (srom_read(bus, curmap, osh, 0, nw * 2, image))
|
||||
return 1;
|
||||
/* make changes */
|
||||
bcopy((void*)buf, (void*)&image[byteoff / 2], nbytes);
|
||||
/* calculate crc */
|
||||
htol16_buf(image, crc_range);
|
||||
crc = ~crc8((uint8 *)image, crc_range - 1, CRC8_INIT_VALUE);
|
||||
ltoh16_buf(image, crc_range);
|
||||
image[(crc_range / 2) - 1] = (crc << 8) | (image[(crc_range / 2) - 1] & 0xff);
|
||||
p = image;
|
||||
off = 0;
|
||||
} else {
|
||||
p = buf;
|
||||
off = byteoff / 2;
|
||||
nw = nbytes / 2;
|
||||
}
|
||||
|
||||
if (bus == PCI_BUS) {
|
||||
srom = (uint16*)((uint)curmap + PCI_BAR0_SPROM_OFFSET);
|
||||
/* enable writes to the SPROM */
|
||||
val32 = OSL_PCI_READ_CONFIG(osh, PCI_SPROM_CONTROL, sizeof(uint32));
|
||||
val32 |= SPROM_WRITEEN;
|
||||
OSL_PCI_WRITE_CONFIG(osh, PCI_SPROM_CONTROL, sizeof(uint32), val32);
|
||||
bcm_mdelay(500);
|
||||
/* write srom */
|
||||
for (i = 0; i < nw; i++) {
|
||||
W_REG(&srom[off + i], p[i]);
|
||||
bcm_mdelay(20);
|
||||
}
|
||||
/* disable writes to the SPROM */
|
||||
OSL_PCI_WRITE_CONFIG(osh, PCI_SPROM_CONTROL, sizeof(uint32), val32 & ~SPROM_WRITEEN);
|
||||
} else if (bus == PCMCIA_BUS) {
|
||||
/* enable writes to the SPROM */
|
||||
if (sprom_cmd_pcmcia(osh, SROM_WEN))
|
||||
return 1;
|
||||
bcm_mdelay(500);
|
||||
/* write srom */
|
||||
for (i = 0; i < nw; i++) {
|
||||
sprom_write_pcmcia(osh, (uint16)(off + i), p[i]);
|
||||
bcm_mdelay(20);
|
||||
}
|
||||
/* disable writes to the SPROM */
|
||||
if (sprom_cmd_pcmcia(osh, SROM_WDS))
|
||||
return 1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bcm_mdelay(500);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
srom_parsecis(uint8 *cis, char **vars, int *count)
|
||||
{
|
||||
char eabuf[32];
|
||||
char *vp, *base;
|
||||
uint8 tup, tlen, sromrev = 1;
|
||||
int i, j;
|
||||
uint varsize;
|
||||
bool ag_init = FALSE;
|
||||
uint16 w;
|
||||
|
||||
ASSERT(vars);
|
||||
ASSERT(count);
|
||||
|
||||
base = vp = MALLOC(VARS_MAX);
|
||||
ASSERT(vp);
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
tup = cis[i++];
|
||||
tlen = cis[i++];
|
||||
if ((i + tlen) >= CIS_SIZE)
|
||||
break;
|
||||
|
||||
switch (tup) {
|
||||
case CISTPL_MANFID:
|
||||
vp += sprintf(vp, "manfid=%d", (cis[i + 1] << 8) + cis[i]);
|
||||
vp++;
|
||||
vp += sprintf(vp, "prodid=%d", (cis[i + 3] << 8) + cis[i + 2]);
|
||||
vp++;
|
||||
break;
|
||||
|
||||
case CISTPL_FUNCE:
|
||||
if (cis[i] == LAN_NID) {
|
||||
ASSERT(cis[i + 1] == ETHER_ADDR_LEN);
|
||||
bcm_ether_ntoa((uchar*)&cis[i + 2], eabuf);
|
||||
vp += sprintf(vp, "il0macaddr=%s", eabuf);
|
||||
vp++;
|
||||
}
|
||||
break;
|
||||
|
||||
case CISTPL_CFTABLE:
|
||||
vp += sprintf(vp, "regwindowsz=%d", (cis[i + 7] << 8) | cis[i + 6]);
|
||||
vp++;
|
||||
break;
|
||||
|
||||
case CISTPL_BRCM_HNBU:
|
||||
switch (cis[i]) {
|
||||
case HNBU_CHIPID:
|
||||
vp += sprintf(vp, "vendid=%d", (cis[i + 2] << 8) + cis[i + 1]);
|
||||
vp++;
|
||||
vp += sprintf(vp, "devid=%d", (cis[i + 4] << 8) + cis[i + 3]);
|
||||
vp++;
|
||||
if (tlen == 7) {
|
||||
vp += sprintf(vp, "chiprev=%d", (cis[i + 6] << 8) + cis[i + 5]);
|
||||
vp++;
|
||||
}
|
||||
break;
|
||||
|
||||
case HNBU_BOARDREV:
|
||||
vp += sprintf(vp, "boardrev=%d", cis[i + 1]);
|
||||
vp++;
|
||||
break;
|
||||
|
||||
case HNBU_AA:
|
||||
vp += sprintf(vp, "aa0=%d", cis[i + 1]);
|
||||
vp++;
|
||||
break;
|
||||
|
||||
case HNBU_AG:
|
||||
vp += sprintf(vp, "ag0=%d", cis[i + 1]);
|
||||
vp++;
|
||||
ag_init = TRUE;
|
||||
break;
|
||||
|
||||
case HNBU_CC:
|
||||
vp += sprintf(vp, "cc=%d", cis[i + 1]);
|
||||
vp++;
|
||||
break;
|
||||
|
||||
case HNBU_PAPARMS:
|
||||
vp += sprintf(vp, "pa0maxpwr=%d", cis[i + tlen - 1]);
|
||||
vp++;
|
||||
if (tlen == 9) {
|
||||
/* New version */
|
||||
for (j = 0; j < 3; j++) {
|
||||
vp += sprintf(vp, "pa0b%d=%d", j,
|
||||
(cis[i + (j * 2) + 2] << 8) + cis[i + (j * 2) + 1]);
|
||||
vp++;
|
||||
}
|
||||
vp += sprintf(vp, "pa0itssit=%d", cis[i + 7]);
|
||||
vp++;
|
||||
}
|
||||
break;
|
||||
|
||||
case HNBU_OEM:
|
||||
vp += sprintf(vp, "oem=%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
cis[i + 1], cis[i + 2], cis[i + 3], cis[i + 4],
|
||||
cis[i + 5], cis[i + 6], cis[i + 7], cis[i + 8]);
|
||||
vp++;
|
||||
break;
|
||||
case HNBU_BOARDFLAGS:
|
||||
w = (cis[i + 2] << 8) + cis[i + 1];
|
||||
if (w == 0xffff) w = 0;
|
||||
vp += sprintf(vp, "boardflags=%d", w);
|
||||
vp++;
|
||||
break;
|
||||
case HNBU_LED:
|
||||
if (cis[i + 1] != 0xff) {
|
||||
vp += sprintf(vp, "wl0gpio0=%d", cis[i + 1]);
|
||||
vp++;
|
||||
}
|
||||
if (cis[i + 2] != 0xff) {
|
||||
vp += sprintf(vp, "wl0gpio1=%d", cis[i + 2]);
|
||||
vp++;
|
||||
}
|
||||
if (cis[i + 3] != 0xff) {
|
||||
vp += sprintf(vp, "wl0gpio2=%d", cis[i + 3]);
|
||||
vp++;
|
||||
}
|
||||
if (cis[i + 4] != 0xff) {
|
||||
vp += sprintf(vp, "wl0gpio3=%d", cis[i + 4]);
|
||||
vp++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
i += tlen;
|
||||
} while (tup != 0xff);
|
||||
|
||||
/* Set the srom version */
|
||||
vp += sprintf(vp, "sromrev=%d", sromrev);
|
||||
vp++;
|
||||
|
||||
/* For now just set boardflags2 to zero */
|
||||
vp += sprintf(vp, "boardflags2=0");
|
||||
vp++;
|
||||
|
||||
/* if there is no antenna gain field, set default */
|
||||
if (ag_init == FALSE) {
|
||||
vp += sprintf(vp, "ag0=%d", 0xff);
|
||||
vp++;
|
||||
}
|
||||
|
||||
/* final nullbyte terminator */
|
||||
*vp++ = '\0';
|
||||
varsize = (uint)vp - (uint)base;
|
||||
|
||||
ASSERT(varsize < VARS_MAX);
|
||||
|
||||
if (varsize == VARS_MAX) {
|
||||
*vars = base;
|
||||
} else {
|
||||
vp = MALLOC(varsize);
|
||||
ASSERT(vp);
|
||||
bcopy(base, vp, varsize);
|
||||
MFREE(base, VARS_MAX);
|
||||
*vars = vp;
|
||||
}
|
||||
*count = varsize;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/* set PCMCIA sprom command register */
|
||||
static int
|
||||
sprom_cmd_pcmcia(void *osh, uint8 cmd)
|
||||
{
|
||||
uint8 status;
|
||||
uint wait_cnt = 1000;
|
||||
|
||||
/* write sprom command register */
|
||||
OSL_PCMCIA_WRITE_ATTR(osh, SROM_CS, &cmd, 1);
|
||||
|
||||
/* wait status */
|
||||
while (wait_cnt--) {
|
||||
OSL_PCMCIA_READ_ATTR(osh, SROM_CS, &status, 1);
|
||||
if (status & SROM_DONE)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* read a word from the PCMCIA srom */
|
||||
static int
|
||||
sprom_read_pcmcia(void *osh, uint16 addr, uint16 *data)
|
||||
{
|
||||
uint8 addr_l, addr_h, data_l, data_h;
|
||||
|
||||
addr_l = (uint8)((addr * 2) & 0xff);
|
||||
addr_h = (uint8)(((addr * 2) >> 8) & 0xff);
|
||||
|
||||
/* set address */
|
||||
OSL_PCMCIA_WRITE_ATTR(osh, SROM_ADDRH, &addr_h, 1);
|
||||
OSL_PCMCIA_WRITE_ATTR(osh, SROM_ADDRL, &addr_l, 1);
|
||||
|
||||
/* do read */
|
||||
if (sprom_cmd_pcmcia(osh, SROM_READ))
|
||||
return 1;
|
||||
|
||||
/* read data */
|
||||
OSL_PCMCIA_READ_ATTR(osh, SROM_DATAH, &data_h, 1);
|
||||
OSL_PCMCIA_READ_ATTR(osh, SROM_DATAL, &data_l, 1);
|
||||
|
||||
*data = (data_h << 8) | data_l;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* write a word to the PCMCIA srom */
|
||||
static int
|
||||
sprom_write_pcmcia(void *osh, uint16 addr, uint16 data)
|
||||
{
|
||||
uint8 addr_l, addr_h, data_l, data_h;
|
||||
|
||||
addr_l = (uint8)((addr * 2) & 0xff);
|
||||
addr_h = (uint8)(((addr * 2) >> 8) & 0xff);
|
||||
data_l = (uint8)(data & 0xff);
|
||||
data_h = (uint8)((data >> 8) & 0xff);
|
||||
|
||||
/* set address */
|
||||
OSL_PCMCIA_WRITE_ATTR(osh, SROM_ADDRH, &addr_h, 1);
|
||||
OSL_PCMCIA_WRITE_ATTR(osh, SROM_ADDRL, &addr_l, 1);
|
||||
|
||||
/* write data */
|
||||
OSL_PCMCIA_WRITE_ATTR(osh, SROM_DATAH, &data_h, 1);
|
||||
OSL_PCMCIA_WRITE_ATTR(osh, SROM_DATAL, &data_l, 1);
|
||||
|
||||
/* do write */
|
||||
return sprom_cmd_pcmcia(osh, SROM_WRITE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read in and validate sprom.
|
||||
* Return 0 on success, nonzero on error.
|
||||
*/
|
||||
static int
|
||||
sprom_read_pci(uint16 *sprom, uint byteoff, uint16 *buf, uint nbytes, bool check_crc)
|
||||
{
|
||||
int off, nw;
|
||||
uint8 chk8;
|
||||
int i;
|
||||
|
||||
off = byteoff / 2;
|
||||
nw = ROUNDUP(nbytes, 2) / 2;
|
||||
|
||||
/* read the sprom */
|
||||
for (i = 0; i < nw; i++)
|
||||
buf[i] = R_REG(&sprom[off + i]);
|
||||
|
||||
if (check_crc) {
|
||||
/* fixup the endianness so crc8 will pass */
|
||||
htol16_buf(buf, nw * 2);
|
||||
if ((chk8 = crc8((uchar*)buf, nbytes, CRC8_INIT_VALUE)) != CRC8_GOOD_VALUE)
|
||||
return (1);
|
||||
/* now correct the endianness of the byte array */
|
||||
ltoh16_buf(buf, nw * 2);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize nonvolatile variable table from sprom.
|
||||
* Return 0 on success, nonzero on error.
|
||||
*/
|
||||
|
||||
static int
|
||||
initvars_srom_pci(void *curmap, char **vars, int *count)
|
||||
{
|
||||
uint16 w, b[64];
|
||||
uint8 sromrev;
|
||||
struct ether_addr ea;
|
||||
char eabuf[32];
|
||||
uint32 bfl;
|
||||
int c, woff, i;
|
||||
char *vp, *base;
|
||||
|
||||
if (sprom_read_pci((void *)((uint)curmap + PCI_BAR0_SPROM_OFFSET), 0, b, sizeof (b), TRUE))
|
||||
return (-1);
|
||||
|
||||
/* top word of sprom contains version and crc8 */
|
||||
sromrev = b[63] & 0xff;
|
||||
if ((sromrev != 1) && (sromrev != 2)) {
|
||||
return (-2);
|
||||
}
|
||||
|
||||
ASSERT(vars);
|
||||
ASSERT(count);
|
||||
|
||||
base = vp = MALLOC(VARS_MAX);
|
||||
ASSERT(vp);
|
||||
|
||||
vp += sprintf(vp, "sromrev=%d", sromrev);
|
||||
vp++;
|
||||
|
||||
if (sromrev >= 2) {
|
||||
/* New section takes over the 4th hardware function space */
|
||||
|
||||
/* Word 29 is max power 11a high/low */
|
||||
w = b[29];
|
||||
vp += sprintf(vp, "pa1himaxpwr=%d", w & 0xff);
|
||||
vp++;
|
||||
vp += sprintf(vp, "pa1lomaxpwr=%d", (w >> 8) & 0xff);
|
||||
vp++;
|
||||
|
||||
/* Words 30-32 set the 11alow pa settings,
|
||||
* 33-35 are the 11ahigh ones.
|
||||
*/
|
||||
for (i = 0; i < 3; i++) {
|
||||
vp += sprintf(vp, "pa1lob%d=%d", i, b[30 + i]);
|
||||
vp++;
|
||||
vp += sprintf(vp, "pa1hib%d=%d", i, b[33 + i]);
|
||||
vp++;
|
||||
}
|
||||
w = b[59];
|
||||
if (w == 0)
|
||||
vp += sprintf(vp, "ccode=");
|
||||
else
|
||||
vp += sprintf(vp, "ccode=%c%c", (w >> 8), (w & 0xff));
|
||||
vp++;
|
||||
|
||||
}
|
||||
|
||||
/* parameter section of sprom starts at byte offset 72 */
|
||||
woff = 72/2;
|
||||
|
||||
/* first 6 bytes are il0macaddr */
|
||||
ea.octet[0] = (b[woff] >> 8) & 0xff;
|
||||
ea.octet[1] = b[woff] & 0xff;
|
||||
ea.octet[2] = (b[woff+1] >> 8) & 0xff;
|
||||
ea.octet[3] = b[woff+1] & 0xff;
|
||||
ea.octet[4] = (b[woff+2] >> 8) & 0xff;
|
||||
ea.octet[5] = b[woff+2] & 0xff;
|
||||
woff += ETHER_ADDR_LEN/2 ;
|
||||
bcm_ether_ntoa((uchar*)&ea, eabuf);
|
||||
vp += sprintf(vp, "il0macaddr=%s", eabuf);
|
||||
vp++;
|
||||
|
||||
/* next 6 bytes are et0macaddr */
|
||||
ea.octet[0] = (b[woff] >> 8) & 0xff;
|
||||
ea.octet[1] = b[woff] & 0xff;
|
||||
ea.octet[2] = (b[woff+1] >> 8) & 0xff;
|
||||
ea.octet[3] = b[woff+1] & 0xff;
|
||||
ea.octet[4] = (b[woff+2] >> 8) & 0xff;
|
||||
ea.octet[5] = b[woff+2] & 0xff;
|
||||
woff += ETHER_ADDR_LEN/2 ;
|
||||
bcm_ether_ntoa((uchar*)&ea, eabuf);
|
||||
vp += sprintf(vp, "et0macaddr=%s", eabuf);
|
||||
vp++;
|
||||
|
||||
/* next 6 bytes are et1macaddr */
|
||||
ea.octet[0] = (b[woff] >> 8) & 0xff;
|
||||
ea.octet[1] = b[woff] & 0xff;
|
||||
ea.octet[2] = (b[woff+1] >> 8) & 0xff;
|
||||
ea.octet[3] = b[woff+1] & 0xff;
|
||||
ea.octet[4] = (b[woff+2] >> 8) & 0xff;
|
||||
ea.octet[5] = b[woff+2] & 0xff;
|
||||
woff += ETHER_ADDR_LEN/2 ;
|
||||
bcm_ether_ntoa((uchar*)&ea, eabuf);
|
||||
vp += sprintf(vp, "et1macaddr=%s", eabuf);
|
||||
vp++;
|
||||
|
||||
/*
|
||||
* Enet phy settings one or two singles or a dual
|
||||
* Bits 4-0 : MII address for enet0 (0x1f for not there)
|
||||
* Bits 9-5 : MII address for enet1 (0x1f for not there)
|
||||
* Bit 14 : Mdio for enet0
|
||||
* Bit 15 : Mdio for enet1
|
||||
*/
|
||||
w = b[woff];
|
||||
vp += sprintf(vp, "et0phyaddr=%d", (w & 0x1f));
|
||||
vp++;
|
||||
vp += sprintf(vp, "et1phyaddr=%d", ((w >> 5) & 0x1f));
|
||||
vp++;
|
||||
vp += sprintf(vp, "et0mdcport=%d", ((w >> 14) & 0x1));
|
||||
vp++;
|
||||
vp += sprintf(vp, "et1mdcport=%d", ((w >> 15) & 0x1));
|
||||
vp++;
|
||||
|
||||
/* Word 46 has board rev, antennas 0/1 & Country code/control */
|
||||
w = b[46];
|
||||
vp += sprintf(vp, "boardrev=%d", w & 0xff);
|
||||
vp++;
|
||||
|
||||
if (sromrev > 1)
|
||||
vp += sprintf(vp, "cctl=%d", (w >> 8) & 0xf);
|
||||
else
|
||||
vp += sprintf(vp, "cc=%d", (w >> 8) & 0xf);
|
||||
vp++;
|
||||
|
||||
vp += sprintf(vp, "aa0=%d", (w >> 12) & 0x3);
|
||||
vp++;
|
||||
|
||||
vp += sprintf(vp, "aa1=%d", (w >> 14) & 0x3);
|
||||
vp++;
|
||||
|
||||
/* Words 47-49 set the (wl) pa settings */
|
||||
woff = 47;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
vp += sprintf(vp, "pa0b%d=%d", i, b[woff+i]);
|
||||
vp++;
|
||||
vp += sprintf(vp, "pa1b%d=%d", i, b[woff+i+6]);
|
||||
vp++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Words 50-51 set the customer-configured wl led behavior.
|
||||
* 8 bits/gpio pin. High bit: activehi=0, activelo=1;
|
||||
* LED behavior values defined in wlioctl.h .
|
||||
*/
|
||||
w = b[50];
|
||||
if ((w != 0) && (w != 0xffff)) {
|
||||
/* gpio0 */
|
||||
vp += sprintf(vp, "wl0gpio0=%d", (w & 0xff));
|
||||
vp++;
|
||||
|
||||
/* gpio1 */
|
||||
vp += sprintf(vp, "wl0gpio1=%d", (w >> 8) & 0xff);
|
||||
vp++;
|
||||
}
|
||||
w = b[51];
|
||||
if ((w != 0) && (w != 0xffff)) {
|
||||
/* gpio2 */
|
||||
vp += sprintf(vp, "wl0gpio2=%d", w & 0xff);
|
||||
vp++;
|
||||
|
||||
/* gpio3 */
|
||||
vp += sprintf(vp, "wl0gpio3=%d", (w >> 8) & 0xff);
|
||||
vp++;
|
||||
}
|
||||
|
||||
/* Word 52 is max power 0/1 */
|
||||
w = b[52];
|
||||
vp += sprintf(vp, "pa0maxpwr=%d", w & 0xff);
|
||||
vp++;
|
||||
vp += sprintf(vp, "pa1maxpwr=%d", (w >> 8) & 0xff);
|
||||
vp++;
|
||||
|
||||
/* Word 56 is idle tssi target 0/1 */
|
||||
w = b[56];
|
||||
vp += sprintf(vp, "pa0itssit=%d", w & 0xff);
|
||||
vp++;
|
||||
vp += sprintf(vp, "pa1itssit=%d", (w >> 8) & 0xff);
|
||||
vp++;
|
||||
|
||||
/* Word 57 is boardflags, if not programmed make it zero */
|
||||
bfl = (uint32)b[57];
|
||||
if (bfl == 0xffff) bfl = 0;
|
||||
if (sromrev > 1) {
|
||||
/* Word 28 is boardflags2 */
|
||||
bfl |= (uint32)b[28] << 16;
|
||||
}
|
||||
vp += sprintf(vp, "boardflags=%d", bfl);
|
||||
vp++;
|
||||
|
||||
/* Word 58 is antenna gain 0/1 */
|
||||
w = b[58];
|
||||
vp += sprintf(vp, "ag0=%d", w & 0xff);
|
||||
vp++;
|
||||
|
||||
vp += sprintf(vp, "ag1=%d", (w >> 8) & 0xff);
|
||||
vp++;
|
||||
|
||||
if (sromrev == 1) {
|
||||
/* set the oem string */
|
||||
vp += sprintf(vp, "oem=%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
((b[59] >> 8) & 0xff), (b[59] & 0xff),
|
||||
((b[60] >> 8) & 0xff), (b[60] & 0xff),
|
||||
((b[61] >> 8) & 0xff), (b[61] & 0xff),
|
||||
((b[62] >> 8) & 0xff), (b[62] & 0xff));
|
||||
vp++;
|
||||
} else {
|
||||
if (sromrev >= 1){
|
||||
/* Word 60 OFDM tx power offset from CCK level */
|
||||
/* OFDM Power Offset - opo */
|
||||
w = b[60] & 0xff;
|
||||
if (w == 0xff)
|
||||
w = 16;
|
||||
vp += sprintf(vp, "opo=%d", w);
|
||||
vp++;
|
||||
}
|
||||
}
|
||||
|
||||
/* final nullbyte terminator */
|
||||
*vp++ = '\0';
|
||||
|
||||
c = vp - base;
|
||||
ASSERT(c <= VARS_MAX);
|
||||
|
||||
if (c == VARS_MAX) {
|
||||
*vars = base;
|
||||
} else {
|
||||
vp = MALLOC(c);
|
||||
ASSERT(vp);
|
||||
bcopy(base, vp, c);
|
||||
MFREE(base, VARS_MAX);
|
||||
*vars = vp;
|
||||
}
|
||||
*count = c;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the cis and call parsecis to initialize the vars.
|
||||
* Return 0 on success, nonzero on error.
|
||||
*/
|
||||
static int
|
||||
initvars_cis_pcmcia(void *sbh, void *curmap, void *osh, char **vars, int *count)
|
||||
{
|
||||
uint8 *cis = NULL;
|
||||
int rc;
|
||||
uint data_sz;
|
||||
|
||||
data_sz = (sb_pcmciarev(sbh) == 1) ? (SPROM_SIZE * 2) : CIS_SIZE;
|
||||
|
||||
if ((cis = MALLOC(data_sz)) == NULL)
|
||||
return (-1);
|
||||
|
||||
if (sb_pcmciarev(sbh) == 1) {
|
||||
if (srom_read(PCMCIA_BUS, (void *)NULL, osh, 0, data_sz, (uint16 *)cis)) {
|
||||
MFREE(cis, data_sz);
|
||||
return (-1);
|
||||
}
|
||||
/* fix up endianess for 16-bit data vs 8-bit parsing */
|
||||
ltoh16_buf((uint16 *)cis, data_sz);
|
||||
} else
|
||||
OSL_PCMCIA_READ_ATTR(osh, 0, cis, data_sz);
|
||||
|
||||
rc = srom_parsecis(cis, vars, count);
|
||||
|
||||
MFREE(cis, data_sz);
|
||||
|
||||
return (rc);
|
||||
}
|
||||
|
||||
803
openwrt/package/linux/kernel-source/drivers/net/hnd/bcmutils.c
Normal file
803
openwrt/package/linux/kernel-source/drivers/net/hnd/bcmutils.c
Normal file
@@ -0,0 +1,803 @@
|
||||
/*
|
||||
* Misc useful OS-independent routines.
|
||||
*
|
||||
* 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.
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include <typedefs.h>
|
||||
#include <osl.h>
|
||||
#include <bcmutils.h>
|
||||
#include <bcmendian.h>
|
||||
#include <bcmnvram.h>
|
||||
|
||||
unsigned char bcm_ctype[] = {
|
||||
_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 0-7 */
|
||||
_BCM_C,_BCM_C|_BCM_S,_BCM_C|_BCM_S,_BCM_C|_BCM_S,_BCM_C|_BCM_S,_BCM_C|_BCM_S,_BCM_C,_BCM_C, /* 8-15 */
|
||||
_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 16-23 */
|
||||
_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 24-31 */
|
||||
_BCM_S|_BCM_SP,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 32-39 */
|
||||
_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 40-47 */
|
||||
_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D, /* 48-55 */
|
||||
_BCM_D,_BCM_D,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 56-63 */
|
||||
_BCM_P,_BCM_U|_BCM_X,_BCM_U|_BCM_X,_BCM_U|_BCM_X,_BCM_U|_BCM_X,_BCM_U|_BCM_X,_BCM_U|_BCM_X,_BCM_U, /* 64-71 */
|
||||
_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 72-79 */
|
||||
_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 80-87 */
|
||||
_BCM_U,_BCM_U,_BCM_U,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 88-95 */
|
||||
_BCM_P,_BCM_L|_BCM_X,_BCM_L|_BCM_X,_BCM_L|_BCM_X,_BCM_L|_BCM_X,_BCM_L|_BCM_X,_BCM_L|_BCM_X,_BCM_L, /* 96-103 */
|
||||
_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 104-111 */
|
||||
_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 112-119 */
|
||||
_BCM_L,_BCM_L,_BCM_L,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_C, /* 120-127 */
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */
|
||||
_BCM_S|_BCM_SP,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 160-175 */
|
||||
_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 176-191 */
|
||||
_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 192-207 */
|
||||
_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_P,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_L, /* 208-223 */
|
||||
_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 224-239 */
|
||||
_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_P,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L /* 240-255 */
|
||||
};
|
||||
|
||||
uchar
|
||||
bcm_toupper(uchar c)
|
||||
{
|
||||
if (bcm_islower(c))
|
||||
c -= 'a'-'A';
|
||||
return (c);
|
||||
}
|
||||
|
||||
ulong
|
||||
bcm_strtoul(char *cp, char **endp, uint base)
|
||||
{
|
||||
ulong result, value;
|
||||
bool minus;
|
||||
|
||||
minus = FALSE;
|
||||
|
||||
while (bcm_isspace(*cp))
|
||||
cp++;
|
||||
|
||||
if (cp[0] == '+')
|
||||
cp++;
|
||||
else if (cp[0] == '-') {
|
||||
minus = TRUE;
|
||||
cp++;
|
||||
}
|
||||
|
||||
if (base == 0) {
|
||||
if (cp[0] == '0') {
|
||||
if ((cp[1] == 'x') || (cp[1] == 'X')) {
|
||||
base = 16;
|
||||
cp = &cp[2];
|
||||
} else {
|
||||
base = 8;
|
||||
cp = &cp[1];
|
||||
}
|
||||
} else
|
||||
base = 10;
|
||||
} else if (base == 16 && (cp[0] == '0') && ((cp[1] == 'x') || (cp[1] == 'X'))) {
|
||||
cp = &cp[2];
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
while (bcm_isxdigit(*cp) &&
|
||||
(value = bcm_isdigit(*cp) ? *cp-'0' : bcm_toupper(*cp)-'A'+10) < base) {
|
||||
result = result*base + value;
|
||||
cp++;
|
||||
}
|
||||
|
||||
if (minus)
|
||||
result = (ulong)(result * -1);
|
||||
|
||||
if (endp)
|
||||
*endp = (char *)cp;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
uint
|
||||
bcm_atoi(char *s)
|
||||
{
|
||||
uint n;
|
||||
|
||||
n = 0;
|
||||
|
||||
while (bcm_isdigit(*s))
|
||||
n = (n * 10) + *s++ - '0';
|
||||
return (n);
|
||||
}
|
||||
|
||||
void
|
||||
deadbeef(char *p, uint len)
|
||||
{
|
||||
static uchar meat[] = { 0xde, 0xad, 0xbe, 0xef };
|
||||
|
||||
while (len-- > 0) {
|
||||
*p = meat[((uint)p) & 3];
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
/* pretty hex print a contiguous buffer */
|
||||
void
|
||||
prhex(char *msg, uchar *buf, uint nbytes)
|
||||
{
|
||||
char line[256];
|
||||
char* p;
|
||||
uint i;
|
||||
|
||||
if (msg && (msg[0] != '\0'))
|
||||
printf("%s: ", msg);
|
||||
|
||||
p = line;
|
||||
for (i = 0; i < nbytes; i++) {
|
||||
if (i % 16 == 0) {
|
||||
p += sprintf(p, "%04d: ", i); /* line prefix */
|
||||
}
|
||||
p += sprintf(p, "%02x ", buf[i]);
|
||||
if (i % 16 == 15) {
|
||||
printf("%s\n", line); /* flush line */
|
||||
p = line;
|
||||
}
|
||||
}
|
||||
|
||||
/* flush last partial line */
|
||||
if (p != line)
|
||||
printf("%s\n", line);
|
||||
}
|
||||
|
||||
/* pretty hex print a pkt buffer chain */
|
||||
void
|
||||
prpkt(char *msg, void *drv, void *p0)
|
||||
{
|
||||
void *p;
|
||||
|
||||
if (msg && (msg[0] != '\0'))
|
||||
printf("%s: ", msg);
|
||||
|
||||
for (p = p0; p; p = PKTNEXT(drv, p))
|
||||
prhex(NULL, PKTDATA(drv, p), PKTLEN(drv, p));
|
||||
}
|
||||
|
||||
/* copy a pkt buffer chain into a buffer */
|
||||
uint
|
||||
pktcopy(void *drv, void *p, uint offset, int len, uchar *buf)
|
||||
{
|
||||
uint n, ret = 0;
|
||||
|
||||
if (len < 0)
|
||||
len = 4096; /* "infinite" */
|
||||
|
||||
/* skip 'offset' bytes */
|
||||
for (; p && offset; p = PKTNEXT(drv, p)) {
|
||||
if (offset < (uint)PKTLEN(drv, p))
|
||||
break;
|
||||
offset -= PKTLEN(drv, p);
|
||||
}
|
||||
|
||||
if (!p)
|
||||
return 0;
|
||||
|
||||
/* copy the data */
|
||||
for (; p && len; p = PKTNEXT(drv, p)) {
|
||||
n = MIN((uint)PKTLEN(drv, p) - offset, (uint)len);
|
||||
bcopy(PKTDATA(drv, p) + offset, buf, n);
|
||||
buf += n;
|
||||
len -= n;
|
||||
ret += n;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* return total length of buffer chain */
|
||||
uint
|
||||
pkttotlen(void *drv, void *p)
|
||||
{
|
||||
uint total;
|
||||
|
||||
total = 0;
|
||||
for (; p; p = PKTNEXT(drv, p))
|
||||
total += PKTLEN(drv, p);
|
||||
return (total);
|
||||
}
|
||||
|
||||
|
||||
uchar*
|
||||
bcm_ether_ntoa(char *ea, char *buf)
|
||||
{
|
||||
sprintf(buf,"%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
(uchar)ea[0]&0xff, (uchar)ea[1]&0xff, (uchar)ea[2]&0xff,
|
||||
(uchar)ea[3]&0xff, (uchar)ea[4]&0xff, (uchar)ea[5]&0xff);
|
||||
return (buf);
|
||||
}
|
||||
|
||||
/* parse a xx:xx:xx:xx:xx:xx format ethernet address */
|
||||
int
|
||||
bcm_ether_atoe(char *p, char *ea)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (;;) {
|
||||
ea[i++] = (char) bcm_strtoul(p, &p, 16);
|
||||
if (!*p++ || i == 6)
|
||||
break;
|
||||
}
|
||||
|
||||
return (i == 6);
|
||||
}
|
||||
|
||||
/*
|
||||
* Advance from the current 1-byte tag/1-byte length/variable-length value
|
||||
* triple, to the next, returning a pointer to the next.
|
||||
*/
|
||||
bcm_tlv_t *
|
||||
bcm_next_tlv(bcm_tlv_t *elt, int *buflen)
|
||||
{
|
||||
int len;
|
||||
|
||||
/* validate current elt */
|
||||
if (*buflen < 2) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = elt->len;
|
||||
|
||||
/* validate remaining buflen */
|
||||
if (*buflen >= (2 + len + 2)) {
|
||||
elt = (bcm_tlv_t*)(elt->data + len);
|
||||
*buflen -= (2 + len);
|
||||
} else {
|
||||
elt = NULL;
|
||||
}
|
||||
|
||||
return elt;
|
||||
}
|
||||
|
||||
/*
|
||||
* Traverse a string of 1-byte tag/1-byte length/variable-length value
|
||||
* triples, returning a pointer to the substring whose first element
|
||||
* matches tag. Stop parsing when we see an element whose ID is greater
|
||||
* than the target key.
|
||||
*/
|
||||
bcm_tlv_t *
|
||||
bcm_parse_ordered_tlvs(void *buf, int buflen, uint key)
|
||||
{
|
||||
bcm_tlv_t *elt;
|
||||
int totlen;
|
||||
|
||||
elt = (bcm_tlv_t*)buf;
|
||||
totlen = buflen;
|
||||
|
||||
/* find tagged parameter */
|
||||
while (totlen >= 2) {
|
||||
uint id = elt->id;
|
||||
int len = elt->len;
|
||||
|
||||
/* Punt if we start seeing IDs > than target key */
|
||||
if (id > key)
|
||||
return(NULL);
|
||||
|
||||
/* validate remaining totlen */
|
||||
if ((id == key) && (totlen >= (len + 2)))
|
||||
return (elt);
|
||||
|
||||
elt = (bcm_tlv_t*)((uint8*)elt + (len + 2));
|
||||
totlen -= (len + 2);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Traverse a string of 1-byte tag/1-byte length/variable-length value
|
||||
* triples, returning a pointer to the substring whose first element
|
||||
* matches tag
|
||||
*/
|
||||
bcm_tlv_t *
|
||||
bcm_parse_tlvs(void *buf, int buflen, uint key)
|
||||
{
|
||||
bcm_tlv_t *elt;
|
||||
int totlen;
|
||||
|
||||
elt = (bcm_tlv_t*)buf;
|
||||
totlen = buflen;
|
||||
|
||||
/* find tagged parameter */
|
||||
while (totlen >= 2) {
|
||||
int len = elt->len;
|
||||
|
||||
/* validate remaining totlen */
|
||||
if ((elt->id == key) && (totlen >= (len + 2)))
|
||||
return (elt);
|
||||
|
||||
elt = (bcm_tlv_t*)((uint8*)elt + (len + 2));
|
||||
totlen -= (len + 2);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
pktq_init(struct pktq *q, uint maxlen, bool priority)
|
||||
{
|
||||
q->head = q->tail = NULL;
|
||||
q->priority = priority;
|
||||
q->maxlen = maxlen;
|
||||
q->len = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
pktenq(struct pktq *q, void *p, bool lifo)
|
||||
{
|
||||
void *next, *prev;
|
||||
|
||||
/* Queue is full */
|
||||
if (q->len >= q->maxlen)
|
||||
return FALSE;
|
||||
|
||||
/* Queueing chains not allowed */
|
||||
ASSERT(PKTLINK(p) == NULL);
|
||||
|
||||
/* Queue is empty */
|
||||
if (q->tail == NULL) {
|
||||
ASSERT(q->head == NULL);
|
||||
q->head = q->tail = p;
|
||||
}
|
||||
|
||||
/* Insert at head or tail */
|
||||
else if (q->priority == FALSE) {
|
||||
/* Insert at head (LIFO) */
|
||||
if (lifo) {
|
||||
PKTSETLINK(p, q->head);
|
||||
q->head = p;
|
||||
}
|
||||
/* Insert at tail (FIFO) */
|
||||
else {
|
||||
ASSERT(PKTLINK(q->tail) == NULL);
|
||||
PKTSETLINK(q->tail, p);
|
||||
PKTSETLINK(p, NULL);
|
||||
q->tail = p;
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert by priority */
|
||||
else {
|
||||
ASSERT(q->head);
|
||||
ASSERT(q->tail);
|
||||
/* Shortcut to insertion at tail */
|
||||
if (PKTPRIO(p) < PKTPRIO(q->tail) ||
|
||||
(!lifo && PKTPRIO(p) <= PKTPRIO(q->tail))) {
|
||||
prev = q->tail;
|
||||
next = NULL;
|
||||
}
|
||||
/* Insert at head or in the middle */
|
||||
else {
|
||||
prev = NULL;
|
||||
next = q->head;
|
||||
}
|
||||
/* Walk the queue */
|
||||
for (; next; prev = next, next = PKTLINK(next)) {
|
||||
/* Priority queue invariant */
|
||||
ASSERT(!prev || PKTPRIO(prev) >= PKTPRIO(next));
|
||||
/* Insert at head of string of packets of same priority (LIFO) */
|
||||
if (lifo) {
|
||||
if (PKTPRIO(p) >= PKTPRIO(next))
|
||||
break;
|
||||
}
|
||||
/* Insert at tail of string of packets of same priority (FIFO) */
|
||||
else {
|
||||
if (PKTPRIO(p) > PKTPRIO(next))
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Insert at tail */
|
||||
if (next == NULL) {
|
||||
ASSERT(PKTLINK(q->tail) == NULL);
|
||||
PKTSETLINK(q->tail, p);
|
||||
PKTSETLINK(p, NULL);
|
||||
q->tail = p;
|
||||
}
|
||||
/* Insert in the middle */
|
||||
else if (prev) {
|
||||
PKTSETLINK(prev, p);
|
||||
PKTSETLINK(p, next);
|
||||
}
|
||||
/* Insert at head */
|
||||
else {
|
||||
PKTSETLINK(p, q->head);
|
||||
q->head = p;
|
||||
}
|
||||
}
|
||||
|
||||
/* List invariants after insertion */
|
||||
ASSERT(q->head);
|
||||
ASSERT(PKTLINK(q->tail) == NULL);
|
||||
|
||||
q->len++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void*
|
||||
pktdeq(struct pktq *q)
|
||||
{
|
||||
void *p;
|
||||
|
||||
if ((p = q->head)) {
|
||||
ASSERT(q->tail);
|
||||
q->head = PKTLINK(p);
|
||||
PKTSETLINK(p, NULL);
|
||||
q->len--;
|
||||
if (q->head == NULL)
|
||||
q->tail = NULL;
|
||||
}
|
||||
else {
|
||||
ASSERT(q->tail == NULL);
|
||||
}
|
||||
|
||||
return (p);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* crc8
|
||||
*
|
||||
* Computes a crc8 over the input data using the polynomial:
|
||||
*
|
||||
* x^8 + x^7 +x^6 + x^4 + x^2 + 1
|
||||
*
|
||||
* The caller provides the initial value (either CRC8_INIT_VALUE
|
||||
* or the previous returned value) to allow for processing of
|
||||
* discontiguous blocks of data. When generating the CRC the
|
||||
* caller is responsible for complementing the final return value
|
||||
* and inserting it into the byte stream. When checking, a final
|
||||
* return value of CRC8_GOOD_VALUE indicates a valid CRC.
|
||||
*
|
||||
* Reference: Dallas Semiconductor Application Note 27
|
||||
* Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
|
||||
* ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
|
||||
* ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
static uint8 crc8_table[256] = {
|
||||
0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
|
||||
0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
|
||||
0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
|
||||
0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
|
||||
0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
|
||||
0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
|
||||
0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
|
||||
0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
|
||||
0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
|
||||
0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
|
||||
0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
|
||||
0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
|
||||
0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
|
||||
0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
|
||||
0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
|
||||
0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
|
||||
0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
|
||||
0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
|
||||
0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
|
||||
0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
|
||||
0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
|
||||
0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
|
||||
0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
|
||||
0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
|
||||
0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
|
||||
0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
|
||||
0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
|
||||
0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
|
||||
0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
|
||||
0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
|
||||
0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
|
||||
0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F
|
||||
};
|
||||
|
||||
/*
|
||||
* Search the name=value vars for a specific one and return its value.
|
||||
* Returns NULL if not found.
|
||||
*/
|
||||
char*
|
||||
getvar(char *vars, char *name)
|
||||
{
|
||||
char *s;
|
||||
int len;
|
||||
|
||||
len = strlen(name);
|
||||
|
||||
/* first look in vars[] */
|
||||
for (s = vars; s && *s; ) {
|
||||
if ((bcmp(s, name, len) == 0) && (s[len] == '='))
|
||||
return (&s[len+1]);
|
||||
|
||||
while (*s++)
|
||||
;
|
||||
}
|
||||
|
||||
/* then query nvram */
|
||||
return (nvram_get(name));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search the vars for a specific one and return its value as
|
||||
* an integer. Returns 0 if not found.
|
||||
*/
|
||||
int
|
||||
getintvar(char *vars, char *name)
|
||||
{
|
||||
char *val;
|
||||
|
||||
if ((val = getvar(vars, name)) == NULL)
|
||||
return (0);
|
||||
|
||||
return (bcm_strtoul(val, NULL, 0));
|
||||
}
|
||||
|
||||
/* return pointer to location of substring 'needle' in 'haystack' */
|
||||
char*
|
||||
bcmstrstr(char *haystack, char *needle)
|
||||
{
|
||||
int len, nlen;
|
||||
int i;
|
||||
|
||||
if ((haystack == NULL) || (needle == NULL))
|
||||
return (haystack);
|
||||
|
||||
nlen = strlen(needle);
|
||||
len = strlen(haystack) - nlen + 1;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
if (bcmp(needle, &haystack[i], nlen) == 0)
|
||||
return (&haystack[i]);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void
|
||||
bcm_mdelay(uint ms)
|
||||
{
|
||||
uint i;
|
||||
|
||||
for (i = 0; i < ms; i++) {
|
||||
OSL_DELAY(1000);
|
||||
}
|
||||
}
|
||||
|
||||
#define CRC_INNER_LOOP(n, c, x) \
|
||||
(c) = ((c) >> 8) ^ crc##n##_table[((c) ^ (x)) & 0xff]
|
||||
|
||||
uint8
|
||||
crc8(
|
||||
uint8 *pdata, /* pointer to array of data to process */
|
||||
uint nbytes, /* number of input data bytes to process */
|
||||
uint8 crc /* either CRC8_INIT_VALUE or previous return value */
|
||||
)
|
||||
{
|
||||
/* hard code the crc loop instead of using CRC_INNER_LOOP macro
|
||||
* to avoid the undefined and unnecessary (uint8 >> 8) operation. */
|
||||
while (nbytes-- > 0)
|
||||
crc = crc8_table[(crc ^ *pdata++) & 0xff];
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* crc16
|
||||
*
|
||||
* Computes a crc16 over the input data using the polynomial:
|
||||
*
|
||||
* x^16 + x^12 +x^5 + 1
|
||||
*
|
||||
* The caller provides the initial value (either CRC16_INIT_VALUE
|
||||
* or the previous returned value) to allow for processing of
|
||||
* discontiguous blocks of data. When generating the CRC the
|
||||
* caller is responsible for complementing the final return value
|
||||
* and inserting it into the byte stream. When checking, a final
|
||||
* return value of CRC16_GOOD_VALUE indicates a valid CRC.
|
||||
*
|
||||
* Reference: Dallas Semiconductor Application Note 27
|
||||
* Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
|
||||
* ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
|
||||
* ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
static uint16 crc16_table[256] = {
|
||||
0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
|
||||
0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
|
||||
0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
|
||||
0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
|
||||
0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
|
||||
0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
|
||||
0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
|
||||
0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
|
||||
0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
|
||||
0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
|
||||
0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
|
||||
0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
|
||||
0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
|
||||
0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
|
||||
0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
|
||||
0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
|
||||
0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
|
||||
0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
|
||||
0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
|
||||
0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
|
||||
0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
|
||||
0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
|
||||
0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
|
||||
0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
|
||||
0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
|
||||
0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
|
||||
0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
|
||||
0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
|
||||
0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
|
||||
0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
|
||||
0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
|
||||
0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
|
||||
};
|
||||
|
||||
uint16
|
||||
crc16(
|
||||
uint8 *pdata, /* pointer to array of data to process */
|
||||
uint nbytes, /* number of input data bytes to process */
|
||||
uint16 crc /* either CRC16_INIT_VALUE or previous return value */
|
||||
)
|
||||
{
|
||||
while (nbytes-- > 0)
|
||||
CRC_INNER_LOOP(16, crc, *pdata++);
|
||||
return crc;
|
||||
}
|
||||
|
||||
static uint32 crc32_table[256] = {
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
|
||||
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
|
||||
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
|
||||
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
|
||||
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
|
||||
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
|
||||
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
|
||||
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
|
||||
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
|
||||
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
|
||||
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
|
||||
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
|
||||
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
|
||||
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
|
||||
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
|
||||
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
|
||||
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
|
||||
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
|
||||
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
|
||||
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
|
||||
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
|
||||
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
|
||||
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
|
||||
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
|
||||
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
|
||||
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
|
||||
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
|
||||
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
|
||||
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
|
||||
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
|
||||
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
|
||||
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
|
||||
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
|
||||
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
|
||||
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
|
||||
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
|
||||
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
|
||||
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
||||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
|
||||
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
|
||||
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
|
||||
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
|
||||
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
|
||||
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
|
||||
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
|
||||
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
|
||||
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
|
||||
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
|
||||
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
|
||||
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
|
||||
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
|
||||
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
|
||||
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
|
||||
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
||||
};
|
||||
|
||||
uint32
|
||||
crc32(
|
||||
uint8 *pdata, /* pointer to array of data to process */
|
||||
uint nbytes, /* number of input data bytes to process */
|
||||
uint32 crc /* either CRC32_INIT_VALUE or previous return value */
|
||||
)
|
||||
{
|
||||
uint8 *pend;
|
||||
#ifdef __mips__
|
||||
uint8 tmp[4];
|
||||
ulong *tptr = (ulong *)tmp;
|
||||
|
||||
/* in case the beginning of the buffer isn't aligned */
|
||||
pend = (uint8 *)((uint)(pdata + 3) & 0xfffffffc);
|
||||
nbytes -= (pend - pdata);
|
||||
while (pdata < pend)
|
||||
CRC_INNER_LOOP(32, crc, *pdata++);
|
||||
|
||||
/* handle bulk of data as 32-bit words */
|
||||
pend = pdata + (nbytes & 0xfffffffc);
|
||||
while (pdata < pend) {
|
||||
*tptr = *((ulong *)pdata)++;
|
||||
CRC_INNER_LOOP(32, crc, tmp[0]);
|
||||
CRC_INNER_LOOP(32, crc, tmp[1]);
|
||||
CRC_INNER_LOOP(32, crc, tmp[2]);
|
||||
CRC_INNER_LOOP(32, crc, tmp[3]);
|
||||
}
|
||||
|
||||
/* 1-3 bytes at end of buffer */
|
||||
pend = pdata + (nbytes & 0x03);
|
||||
while (pdata < pend)
|
||||
CRC_INNER_LOOP(32, crc, *pdata++);
|
||||
#else
|
||||
pend = pdata + nbytes;
|
||||
while (pdata < pend)
|
||||
CRC_INNER_LOOP(32, crc, *pdata++);
|
||||
#endif
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
#ifdef notdef
|
||||
#define CLEN 1499
|
||||
#define CBUFSIZ (CLEN+4)
|
||||
#define CNBUFS 5
|
||||
|
||||
void testcrc32(void)
|
||||
{
|
||||
uint j,k,l;
|
||||
uint8 *buf;
|
||||
uint len[CNBUFS];
|
||||
uint32 crcr;
|
||||
uint32 crc32tv[CNBUFS] =
|
||||
{0xd2cb1faa, 0xd385c8fa, 0xf5b4f3f3, 0x55789e20, 0x00343110};
|
||||
|
||||
ASSERT((buf = MALLOC(CBUFSIZ*CNBUFS)) != NULL);
|
||||
|
||||
/* step through all possible alignments */
|
||||
for (l=0;l<=4;l++) {
|
||||
for (j=0; j<CNBUFS; j++) {
|
||||
len[j] = CLEN;
|
||||
for (k=0; k<len[j]; k++)
|
||||
*(buf + j*CBUFSIZ + (k+l)) = (j+k) & 0xff;
|
||||
}
|
||||
|
||||
for (j=0; j<CNBUFS; j++) {
|
||||
crcr = crc32(buf + j*CBUFSIZ + l, len[j], CRC32_INIT_VALUE);
|
||||
ASSERT(crcr == crc32tv[j]);
|
||||
}
|
||||
}
|
||||
|
||||
MFREE(buf, CBUFSIZ*CNBUFS);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
841
openwrt/package/linux/kernel-source/drivers/net/hnd/hnddma.c
Normal file
841
openwrt/package/linux/kernel-source/drivers/net/hnd/hnddma.c
Normal file
@@ -0,0 +1,841 @@
|
||||
/*
|
||||
* Generic Broadcom Home Networking Division (HND) DMA module.
|
||||
* This supports the following chips: BCM42xx, 44xx, 47xx .
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include <typedefs.h>
|
||||
#include <osl.h>
|
||||
#include <bcmendian.h>
|
||||
#include <bcmutils.h>
|
||||
|
||||
struct dma_info; /* forward declaration */
|
||||
#define di_t struct dma_info
|
||||
#include <hnddma.h>
|
||||
|
||||
/* debug/trace */
|
||||
#define DMA_ERROR(args)
|
||||
#define DMA_TRACE(args)
|
||||
|
||||
/* default dma message level(if input msg_level pointer is null in dma_attach()) */
|
||||
static uint dma_msg_level = 0;
|
||||
|
||||
#define MAXNAMEL 8
|
||||
#define MAXDD (DMAMAXRINGSZ / sizeof (dmadd_t))
|
||||
|
||||
/* dma engine software state */
|
||||
typedef struct dma_info {
|
||||
hnddma_t hnddma; /* exported structure */
|
||||
uint *msg_level; /* message level pointer */
|
||||
|
||||
char name[MAXNAMEL]; /* callers name for diag msgs */
|
||||
void *drv; /* driver handle */
|
||||
void *dev; /* device handle */
|
||||
dmaregs_t *regs; /* dma engine registers */
|
||||
|
||||
dmadd_t *txd; /* pointer to chip-specific tx descriptor ring */
|
||||
uint txin; /* index of next descriptor to reclaim */
|
||||
uint txout; /* index of next descriptor to post */
|
||||
uint txavail; /* # free tx descriptors */
|
||||
void *txp[MAXDD]; /* parallel array of pointers to packets */
|
||||
ulong txdpa; /* physical address of descriptor ring */
|
||||
uint txdalign; /* #bytes added to alloc'd mem to align txd */
|
||||
|
||||
dmadd_t *rxd; /* pointer to chip-specific rx descriptor ring */
|
||||
uint rxin; /* index of next descriptor to reclaim */
|
||||
uint rxout; /* index of next descriptor to post */
|
||||
void *rxp[MAXDD]; /* parallel array of pointers to packets */
|
||||
ulong rxdpa; /* physical address of descriptor ring */
|
||||
uint rxdalign; /* #bytes added to alloc'd mem to align rxd */
|
||||
|
||||
/* tunables */
|
||||
uint ntxd; /* # tx descriptors */
|
||||
uint nrxd; /* # rx descriptors */
|
||||
uint rxbufsize; /* rx buffer size in bytes */
|
||||
uint nrxpost; /* # rx buffers to keep posted */
|
||||
uint rxoffset; /* rxcontrol offset */
|
||||
uint ddoffset; /* add to get dma address of descriptor ring */
|
||||
uint dataoffset; /* add to get dma address of data buffer */
|
||||
} dma_info_t;
|
||||
|
||||
/* descriptor bumping macros */
|
||||
#define TXD(x) ((x) & (di->ntxd - 1))
|
||||
#define RXD(x) ((x) & (di->nrxd - 1))
|
||||
#define NEXTTXD(i) TXD(i + 1)
|
||||
#define PREVTXD(i) TXD(i - 1)
|
||||
#define NEXTRXD(i) RXD(i + 1)
|
||||
#define NTXDACTIVE(h, t) TXD(t - h)
|
||||
#define NRXDACTIVE(h, t) RXD(t - h)
|
||||
|
||||
/* macros to convert between byte offsets and indexes */
|
||||
#define B2I(bytes) ((bytes) / sizeof (dmadd_t))
|
||||
#define I2B(index) ((index) * sizeof (dmadd_t))
|
||||
|
||||
void*
|
||||
dma_attach(void *drv, void *dev, char *name, dmaregs_t *regs, uint ntxd, uint nrxd,
|
||||
uint rxbufsize, uint nrxpost, uint rxoffset, uint ddoffset, uint dataoffset, uint *msg_level)
|
||||
{
|
||||
dma_info_t *di;
|
||||
void *va;
|
||||
|
||||
ASSERT(ntxd <= MAXDD);
|
||||
ASSERT(nrxd <= MAXDD);
|
||||
|
||||
/* allocate private info structure */
|
||||
if ((di = MALLOC(sizeof (dma_info_t))) == NULL)
|
||||
return (NULL);
|
||||
bzero((char*)di, sizeof (dma_info_t));
|
||||
|
||||
/* set message level */
|
||||
di->msg_level = msg_level ? msg_level : &dma_msg_level;
|
||||
|
||||
DMA_TRACE(("%s: dma_attach: drv 0x%x dev 0x%x regs 0x%x ntxd %d nrxd %d rxbufsize %d nrxpost %d rxoffset %d ddoffset 0x%x dataoffset 0x%x\n", name, (uint)drv, (uint)dev, (uint)regs, ntxd, nrxd, rxbufsize, nrxpost, rxoffset, ddoffset, dataoffset));
|
||||
|
||||
/* make a private copy of our callers name */
|
||||
strncpy(di->name, name, MAXNAMEL);
|
||||
di->name[MAXNAMEL-1] = '\0';
|
||||
|
||||
di->drv = drv;
|
||||
di->dev = dev;
|
||||
di->regs = regs;
|
||||
|
||||
/* allocate transmit descriptor ring */
|
||||
if (ntxd) {
|
||||
if ((va = DMA_ALLOC_CONSISTENT(dev, (DMAMAXRINGSZ + DMARINGALIGN), &di->txdpa)) == NULL)
|
||||
goto fail;
|
||||
di->txd = (dmadd_t*) ROUNDUP(va, DMARINGALIGN);
|
||||
di->txdalign = ((uint)di->txd - (uint)va);
|
||||
di->txdpa = di->txdpa + di->txdalign;
|
||||
ASSERT(ISALIGNED(di->txd, DMARINGALIGN));
|
||||
}
|
||||
|
||||
/* allocate receive descriptor ring */
|
||||
if (nrxd) {
|
||||
if ((va = DMA_ALLOC_CONSISTENT(dev, (DMAMAXRINGSZ + DMARINGALIGN), &di->rxdpa)) == NULL)
|
||||
goto fail;
|
||||
di->rxd = (dmadd_t*) ROUNDUP(va, DMARINGALIGN);
|
||||
di->rxdalign = ((uint)di->rxd - (uint)va);
|
||||
di->rxdpa = di->rxdpa + di->rxdalign;
|
||||
ASSERT(ISALIGNED(di->rxd, DMARINGALIGN));
|
||||
}
|
||||
|
||||
/* save tunables */
|
||||
di->ntxd = ntxd;
|
||||
di->nrxd = nrxd;
|
||||
di->rxbufsize = rxbufsize;
|
||||
di->nrxpost = nrxpost;
|
||||
di->rxoffset = rxoffset;
|
||||
di->ddoffset = ddoffset;
|
||||
di->dataoffset = dataoffset;
|
||||
|
||||
return ((void*)di);
|
||||
|
||||
fail:
|
||||
dma_detach((void*)di);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* may be called with core in reset */
|
||||
void
|
||||
dma_detach(dma_info_t *di)
|
||||
{
|
||||
if (di == NULL)
|
||||
return;
|
||||
|
||||
DMA_TRACE(("%s: dma_detach\n", di->name));
|
||||
|
||||
/* shouldn't be here if descriptors are unreclaimed */
|
||||
ASSERT(di->txin == di->txout);
|
||||
ASSERT(di->rxin == di->rxout);
|
||||
|
||||
/* free dma descriptor rings */
|
||||
if (di->txd)
|
||||
DMA_FREE_CONSISTENT(di->dev, (void *)((uint)di->txd - di->txdalign), (DMAMAXRINGSZ + DMARINGALIGN), di->txdpa);
|
||||
if (di->rxd)
|
||||
DMA_FREE_CONSISTENT(di->dev, (void *)((uint)di->rxd - di->rxdalign), (DMAMAXRINGSZ + DMARINGALIGN), di->rxdpa);
|
||||
|
||||
/* free our private info structure */
|
||||
MFREE((void*)di, sizeof (dma_info_t));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
dma_txreset(dma_info_t *di)
|
||||
{
|
||||
uint32 status;
|
||||
|
||||
DMA_TRACE(("%s: dma_txreset\n", di->name));
|
||||
|
||||
/* suspend tx DMA first */
|
||||
W_REG(&di->regs->xmtcontrol, XC_SE);
|
||||
SPINWAIT((status = (R_REG(&di->regs->xmtstatus) & XS_XS_MASK)) != XS_XS_DISABLED &&
|
||||
status != XS_XS_IDLE &&
|
||||
status != XS_XS_STOPPED,
|
||||
10000);
|
||||
|
||||
W_REG(&di->regs->xmtcontrol, 0);
|
||||
SPINWAIT((status = (R_REG(&di->regs->xmtstatus) & XS_XS_MASK)) != XS_XS_DISABLED,
|
||||
10000);
|
||||
|
||||
if (status != XS_XS_DISABLED) {
|
||||
DMA_ERROR(("%s: dma_txreset: dma cannot be stopped\n", di->name));
|
||||
}
|
||||
|
||||
/* wait for the last transaction to complete */
|
||||
OSL_DELAY(300);
|
||||
}
|
||||
|
||||
void
|
||||
dma_rxreset(dma_info_t *di)
|
||||
{
|
||||
uint32 status;
|
||||
|
||||
DMA_TRACE(("%s: dma_rxreset\n", di->name));
|
||||
|
||||
W_REG(&di->regs->rcvcontrol, 0);
|
||||
SPINWAIT((status = (R_REG(&di->regs->rcvstatus) & RS_RS_MASK)) != RS_RS_DISABLED,
|
||||
10000);
|
||||
|
||||
if (status != RS_RS_DISABLED) {
|
||||
DMA_ERROR(("%s: dma_rxreset: dma cannot be stopped\n", di->name));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dma_txinit(dma_info_t *di)
|
||||
{
|
||||
DMA_TRACE(("%s: dma_txinit\n", di->name));
|
||||
|
||||
di->txin = di->txout = 0;
|
||||
di->txavail = di->ntxd - 1;
|
||||
|
||||
/* clear tx descriptor ring */
|
||||
BZERO_SM((void*)di->txd, (di->ntxd * sizeof (dmadd_t)));
|
||||
|
||||
W_REG(&di->regs->xmtcontrol, XC_XE);
|
||||
W_REG(&di->regs->xmtaddr, (di->txdpa + di->ddoffset));
|
||||
}
|
||||
|
||||
bool
|
||||
dma_txenabled(dma_info_t *di)
|
||||
{
|
||||
uint32 xc;
|
||||
|
||||
/* If the chip is dead, it is not enabled :-) */
|
||||
xc = R_REG(&di->regs->xmtcontrol);
|
||||
return ((xc != 0xffffffff) && (xc & XC_XE));
|
||||
}
|
||||
|
||||
void
|
||||
dma_txsuspend(dma_info_t *di)
|
||||
{
|
||||
DMA_TRACE(("%s: dma_txsuspend\n", di->name));
|
||||
OR_REG(&di->regs->xmtcontrol, XC_SE);
|
||||
}
|
||||
|
||||
void
|
||||
dma_txresume(dma_info_t *di)
|
||||
{
|
||||
DMA_TRACE(("%s: dma_txresume\n", di->name));
|
||||
AND_REG(&di->regs->xmtcontrol, ~XC_SE);
|
||||
}
|
||||
|
||||
bool
|
||||
dma_txsuspended(dma_info_t *di)
|
||||
{
|
||||
if (!(R_REG(&di->regs->xmtcontrol) & XC_SE))
|
||||
return 0;
|
||||
|
||||
if ((R_REG(&di->regs->xmtstatus) & XS_XS_MASK) != XS_XS_IDLE)
|
||||
return 0;
|
||||
|
||||
OSL_DELAY(2);
|
||||
return ((R_REG(&di->regs->xmtstatus) & XS_XS_MASK) == XS_XS_IDLE);
|
||||
}
|
||||
|
||||
bool
|
||||
dma_txstopped(dma_info_t *di)
|
||||
{
|
||||
return ((R_REG(&di->regs->xmtstatus) & XS_XS_MASK) == XS_XS_STOPPED);
|
||||
}
|
||||
|
||||
bool
|
||||
dma_rxstopped(dma_info_t *di)
|
||||
{
|
||||
return ((R_REG(&di->regs->rcvstatus) & RS_RS_MASK) == RS_RS_STOPPED);
|
||||
}
|
||||
|
||||
void
|
||||
dma_fifoloopbackenable(dma_info_t *di)
|
||||
{
|
||||
DMA_TRACE(("%s: dma_fifoloopbackenable\n", di->name));
|
||||
OR_REG(&di->regs->xmtcontrol, XC_LE);
|
||||
}
|
||||
|
||||
void
|
||||
dma_rxinit(dma_info_t *di)
|
||||
{
|
||||
DMA_TRACE(("%s: dma_rxinit\n", di->name));
|
||||
|
||||
di->rxin = di->rxout = 0;
|
||||
|
||||
/* clear rx descriptor ring */
|
||||
BZERO_SM((void*)di->rxd, (di->nrxd * sizeof (dmadd_t)));
|
||||
|
||||
dma_rxenable(di);
|
||||
W_REG(&di->regs->rcvaddr, (di->rxdpa + di->ddoffset));
|
||||
}
|
||||
|
||||
void
|
||||
dma_rxenable(dma_info_t *di)
|
||||
{
|
||||
DMA_TRACE(("%s: dma_rxenable\n", di->name));
|
||||
W_REG(&di->regs->rcvcontrol, ((di->rxoffset << RC_RO_SHIFT) | RC_RE));
|
||||
}
|
||||
|
||||
bool
|
||||
dma_rxenabled(dma_info_t *di)
|
||||
{
|
||||
uint32 rc;
|
||||
|
||||
rc = R_REG(&di->regs->rcvcontrol);
|
||||
return ((rc != 0xffffffff) && (rc & RC_RE));
|
||||
}
|
||||
|
||||
/*
|
||||
* The BCM47XX family supports full 32bit dma engine buffer addressing so
|
||||
* dma buffers can cross 4 Kbyte page boundaries.
|
||||
*/
|
||||
int
|
||||
dma_txfast(dma_info_t *di, void *p0, uint32 coreflags)
|
||||
{
|
||||
void *p, *next;
|
||||
uchar *data;
|
||||
uint len;
|
||||
uint txout;
|
||||
uint32 ctrl;
|
||||
uint32 pa;
|
||||
|
||||
DMA_TRACE(("%s: dma_txfast\n", di->name));
|
||||
|
||||
txout = di->txout;
|
||||
ctrl = 0;
|
||||
|
||||
/*
|
||||
* Walk the chain of packet buffers
|
||||
* allocating and initializing transmit descriptor entries.
|
||||
*/
|
||||
for (p = p0; p; p = next) {
|
||||
data = PKTDATA(di->drv, p);
|
||||
len = PKTLEN(di->drv, p);
|
||||
next = PKTNEXT(di->drv, p);
|
||||
|
||||
/* return nonzero if out of tx descriptors */
|
||||
if (NEXTTXD(txout) == di->txin)
|
||||
goto outoftxd;
|
||||
|
||||
if (len == 0)
|
||||
continue;
|
||||
|
||||
/* get physical address of buffer start */
|
||||
pa = (uint32) DMA_MAP(di->dev, data, len, DMA_TX, p);
|
||||
|
||||
/* build the descriptor control value */
|
||||
ctrl = len & CTRL_BC_MASK;
|
||||
|
||||
ctrl |= coreflags;
|
||||
|
||||
if (p == p0)
|
||||
ctrl |= CTRL_SOF;
|
||||
if (next == NULL)
|
||||
ctrl |= (CTRL_IOC | CTRL_EOF);
|
||||
if (txout == (di->ntxd - 1))
|
||||
ctrl |= CTRL_EOT;
|
||||
|
||||
/* init the tx descriptor */
|
||||
W_SM(&di->txd[txout].ctrl, BUS_SWAP32(ctrl));
|
||||
W_SM(&di->txd[txout].addr, BUS_SWAP32(pa + di->dataoffset));
|
||||
|
||||
ASSERT(di->txp[txout] == NULL);
|
||||
|
||||
txout = NEXTTXD(txout);
|
||||
}
|
||||
|
||||
/* if last txd eof not set, fix it */
|
||||
if (!(ctrl & CTRL_EOF))
|
||||
W_SM(&di->txd[PREVTXD(txout)].ctrl, BUS_SWAP32(ctrl | CTRL_IOC | CTRL_EOF));
|
||||
|
||||
/* save the packet */
|
||||
di->txp[PREVTXD(txout)] = p0;
|
||||
|
||||
/* bump the tx descriptor index */
|
||||
di->txout = txout;
|
||||
|
||||
/* kick the chip */
|
||||
W_REG(&di->regs->xmtptr, I2B(txout));
|
||||
|
||||
/* tx flow control */
|
||||
di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
|
||||
|
||||
return (0);
|
||||
|
||||
outoftxd:
|
||||
DMA_ERROR(("%s: dma_txfast: out of txds\n", di->name));
|
||||
PKTFREE(di->drv, p0, TRUE);
|
||||
di->txavail = 0;
|
||||
di->hnddma.txnobuf++;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
#define PAGESZ 4096
|
||||
#define PAGEBASE(x) ((uint)(x) & ~4095)
|
||||
|
||||
/*
|
||||
* Just like above except go through the extra effort of splitting
|
||||
* buffers that cross 4Kbyte boundaries into multiple tx descriptors.
|
||||
*/
|
||||
int
|
||||
dma_tx(dma_info_t *di, void *p0, uint32 coreflags)
|
||||
{
|
||||
void *p, *next;
|
||||
uchar *data;
|
||||
uint plen, len;
|
||||
uchar *page, *start, *end;
|
||||
uint txout;
|
||||
uint32 ctrl;
|
||||
uint32 pa;
|
||||
|
||||
DMA_TRACE(("%s: dma_tx\n", di->name));
|
||||
|
||||
txout = di->txout;
|
||||
ctrl = 0;
|
||||
|
||||
/*
|
||||
* Walk the chain of packet buffers
|
||||
* splitting those that cross 4 Kbyte boundaries
|
||||
* allocating and initializing transmit descriptor entries.
|
||||
*/
|
||||
for (p = p0; p; p = next) {
|
||||
data = PKTDATA(di->drv, p);
|
||||
plen = PKTLEN(di->drv, p);
|
||||
next = PKTNEXT(di->drv, p);
|
||||
|
||||
if (plen == 0)
|
||||
continue;
|
||||
|
||||
for (page = (uchar*)PAGEBASE(data);
|
||||
page <= (uchar*)PAGEBASE(data + plen - 1);
|
||||
page += PAGESZ) {
|
||||
|
||||
/* return nonzero if out of tx descriptors */
|
||||
if (NEXTTXD(txout) == di->txin)
|
||||
goto outoftxd;
|
||||
|
||||
start = (page == (uchar*)PAGEBASE(data))? data: page;
|
||||
end = (page == (uchar*)PAGEBASE(data + plen))?
|
||||
(data + plen): (page + PAGESZ);
|
||||
len = end - start;
|
||||
|
||||
/* build the descriptor control value */
|
||||
ctrl = len & CTRL_BC_MASK;
|
||||
|
||||
ctrl |= coreflags;
|
||||
|
||||
if ((p == p0) && (start == data))
|
||||
ctrl |= CTRL_SOF;
|
||||
if ((next == NULL) && (end == (data + plen)))
|
||||
ctrl |= (CTRL_IOC | CTRL_EOF);
|
||||
if (txout == (di->ntxd - 1))
|
||||
ctrl |= CTRL_EOT;
|
||||
|
||||
/* get physical address of buffer start */
|
||||
pa = (uint32) DMA_MAP(di->dev, start, len, DMA_TX, p);
|
||||
|
||||
/* init the tx descriptor */
|
||||
W_SM(&di->txd[txout].ctrl, BUS_SWAP32(ctrl));
|
||||
W_SM(&di->txd[txout].addr, BUS_SWAP32(pa + di->dataoffset));
|
||||
|
||||
ASSERT(di->txp[txout] == NULL);
|
||||
|
||||
txout = NEXTTXD(txout);
|
||||
}
|
||||
}
|
||||
|
||||
/* if last txd eof not set, fix it */
|
||||
if (!(ctrl & CTRL_EOF))
|
||||
W_SM(&di->txd[PREVTXD(txout)].ctrl, BUS_SWAP32(ctrl | CTRL_IOC | CTRL_EOF));
|
||||
|
||||
/* save the packet */
|
||||
di->txp[PREVTXD(txout)] = p0;
|
||||
|
||||
/* bump the tx descriptor index */
|
||||
di->txout = txout;
|
||||
|
||||
/* kick the chip */
|
||||
W_REG(&di->regs->xmtptr, I2B(txout));
|
||||
|
||||
/* tx flow control */
|
||||
di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
|
||||
|
||||
return (0);
|
||||
|
||||
outoftxd:
|
||||
DMA_ERROR(("%s: dma_tx: out of txds\n", di->name));
|
||||
PKTFREE(di->drv, p0, TRUE);
|
||||
di->txavail = 0;
|
||||
di->hnddma.txnobuf++;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* returns a pointer to the next frame received, or NULL if there are no more */
|
||||
void*
|
||||
dma_rx(dma_info_t *di)
|
||||
{
|
||||
void *p;
|
||||
uint len;
|
||||
int skiplen = 0;
|
||||
|
||||
while ((p = dma_getnextrxp(di, FALSE))) {
|
||||
/* skip giant packets which span multiple rx descriptors */
|
||||
if (skiplen > 0) {
|
||||
skiplen -= di->rxbufsize;
|
||||
if (skiplen < 0)
|
||||
skiplen = 0;
|
||||
PKTFREE(di->drv, p, FALSE);
|
||||
continue;
|
||||
}
|
||||
|
||||
len = ltoh16(*(uint16*)(PKTDATA(di->drv, p)));
|
||||
DMA_TRACE(("%s: dma_rx len %d\n", di->name, len));
|
||||
|
||||
/* bad frame length check */
|
||||
if (len > (di->rxbufsize - di->rxoffset)) {
|
||||
DMA_ERROR(("%s: dma_rx: bad frame length (%d)\n", di->name, len));
|
||||
if (len > 0)
|
||||
skiplen = len - (di->rxbufsize - di->rxoffset);
|
||||
PKTFREE(di->drv, p, FALSE);
|
||||
di->hnddma.rxgiants++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* set actual length */
|
||||
PKTSETLEN(di->drv, p, (di->rxoffset + len));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return (p);
|
||||
}
|
||||
|
||||
/* post receive buffers */
|
||||
void
|
||||
dma_rxfill(dma_info_t *di)
|
||||
{
|
||||
void *p;
|
||||
uint rxin, rxout;
|
||||
uint ctrl;
|
||||
uint n;
|
||||
uint i;
|
||||
uint32 pa;
|
||||
uint rxbufsize;
|
||||
|
||||
/*
|
||||
* Determine how many receive buffers we're lacking
|
||||
* from the full complement, allocate, initialize,
|
||||
* and post them, then update the chip rx lastdscr.
|
||||
*/
|
||||
|
||||
rxin = di->rxin;
|
||||
rxout = di->rxout;
|
||||
rxbufsize = di->rxbufsize;
|
||||
|
||||
n = di->nrxpost - NRXDACTIVE(rxin, rxout);
|
||||
|
||||
DMA_TRACE(("%s: dma_rxfill: post %d\n", di->name, n));
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if ((p = PKTGET(di->drv, rxbufsize, FALSE)) == NULL) {
|
||||
DMA_ERROR(("%s: dma_rxfill: out of rxbufs\n", di->name));
|
||||
di->hnddma.rxnobuf++;
|
||||
break;
|
||||
}
|
||||
|
||||
*(uint32*)(OSL_UNCACHED(PKTDATA(di->drv, p))) = 0;
|
||||
|
||||
pa = (uint32) DMA_MAP(di->dev, PKTDATA(di->drv, p), rxbufsize, DMA_RX, p);
|
||||
ASSERT(ISALIGNED(pa, 4));
|
||||
|
||||
/* save the free packet pointer */
|
||||
ASSERT(di->rxp[rxout] == NULL);
|
||||
di->rxp[rxout] = p;
|
||||
|
||||
/* prep the descriptor control value */
|
||||
ctrl = rxbufsize;
|
||||
if (rxout == (di->nrxd - 1))
|
||||
ctrl |= CTRL_EOT;
|
||||
|
||||
/* init the rx descriptor */
|
||||
W_SM(&di->rxd[rxout].ctrl, BUS_SWAP32(ctrl));
|
||||
W_SM(&di->rxd[rxout].addr, BUS_SWAP32(pa + di->dataoffset));
|
||||
|
||||
rxout = NEXTRXD(rxout);
|
||||
}
|
||||
|
||||
di->rxout = rxout;
|
||||
|
||||
/* update the chip lastdscr pointer */
|
||||
W_REG(&di->regs->rcvptr, I2B(rxout));
|
||||
}
|
||||
|
||||
void
|
||||
dma_txreclaim(dma_info_t *di, bool forceall)
|
||||
{
|
||||
void *p;
|
||||
|
||||
DMA_TRACE(("%s: dma_txreclaim %s\n", di->name, forceall ? "all" : ""));
|
||||
|
||||
while ((p = dma_getnexttxp(di, forceall)))
|
||||
PKTFREE(di->drv, p, TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reclaim next completed txd (txds if using chained buffers) and
|
||||
* return associated packet.
|
||||
* If 'force' is true, reclaim txd(s) and return associated packet
|
||||
* regardless of the value of the hardware "curr" pointer.
|
||||
*/
|
||||
void*
|
||||
dma_getnexttxp(dma_info_t *di, bool forceall)
|
||||
{
|
||||
uint start, end, i;
|
||||
void *txp;
|
||||
|
||||
DMA_TRACE(("%s: dma_getnexttxp %s\n", di->name, forceall ? "all" : ""));
|
||||
|
||||
txp = NULL;
|
||||
|
||||
start = di->txin;
|
||||
if (forceall)
|
||||
end = di->txout;
|
||||
else
|
||||
end = B2I(R_REG(&di->regs->xmtstatus) & XS_CD_MASK);
|
||||
|
||||
if ((start == 0) && (end > di->txout))
|
||||
goto bogus;
|
||||
|
||||
for (i = start; i != end && !txp; i = NEXTTXD(i)) {
|
||||
DMA_UNMAP(di->dev, (BUS_SWAP32(R_SM(&di->txd[i].addr)) - di->dataoffset),
|
||||
(BUS_SWAP32(R_SM(&di->txd[i].ctrl)) & CTRL_BC_MASK), DMA_TX, di->txp[i]);
|
||||
W_SM(&di->txd[i].addr, 0xdeadbeef);
|
||||
txp = di->txp[i];
|
||||
di->txp[i] = NULL;
|
||||
}
|
||||
|
||||
di->txin = i;
|
||||
|
||||
/* tx flow control */
|
||||
di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
|
||||
|
||||
return (txp);
|
||||
|
||||
bogus:
|
||||
/*
|
||||
DMA_ERROR(("dma_getnexttxp: bogus curr: start %d end %d txout %d force %d\n",
|
||||
start, end, di->txout, forceall));
|
||||
*/
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* like getnexttxp but no reclaim */
|
||||
void*
|
||||
dma_peeknexttxp(dma_info_t *di)
|
||||
{
|
||||
uint end, i;
|
||||
|
||||
end = B2I(R_REG(&di->regs->xmtstatus) & XS_CD_MASK);
|
||||
|
||||
for (i = di->txin; i != end; i = NEXTTXD(i))
|
||||
if (di->txp[i])
|
||||
return (di->txp[i]);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void
|
||||
dma_rxreclaim(dma_info_t *di)
|
||||
{
|
||||
void *p;
|
||||
|
||||
DMA_TRACE(("%s: dma_rxreclaim\n", di->name));
|
||||
|
||||
while ((p = dma_getnextrxp(di, TRUE)))
|
||||
PKTFREE(di->drv, p, FALSE);
|
||||
}
|
||||
|
||||
void *
|
||||
dma_getnextrxp(dma_info_t *di, bool forceall)
|
||||
{
|
||||
uint i;
|
||||
void *rxp;
|
||||
|
||||
/* if forcing, dma engine must be disabled */
|
||||
ASSERT(!forceall || !dma_rxenabled(di));
|
||||
|
||||
i = di->rxin;
|
||||
|
||||
/* return if no packets posted */
|
||||
if (i == di->rxout)
|
||||
return (NULL);
|
||||
|
||||
/* ignore curr if forceall */
|
||||
if (!forceall && (i == B2I(R_REG(&di->regs->rcvstatus) & RS_CD_MASK)))
|
||||
return (NULL);
|
||||
|
||||
/* get the packet pointer that corresponds to the rx descriptor */
|
||||
rxp = di->rxp[i];
|
||||
ASSERT(rxp);
|
||||
di->rxp[i] = NULL;
|
||||
|
||||
/* clear this packet from the descriptor ring */
|
||||
DMA_UNMAP(di->dev, (BUS_SWAP32(R_SM(&di->rxd[i].addr)) - di->dataoffset),
|
||||
di->rxbufsize, DMA_RX, rxp);
|
||||
W_SM(&di->rxd[i].addr, 0xdeadbeef);
|
||||
|
||||
di->rxin = NEXTRXD(i);
|
||||
|
||||
return (rxp);
|
||||
}
|
||||
|
||||
char*
|
||||
dma_dumptx(dma_info_t *di, char *buf)
|
||||
{
|
||||
buf += sprintf(buf, "txd 0x%lx txdpa 0x%lx txp 0x%lx txin %d txout %d txavail %d\n",
|
||||
(ulong)di->txd, di->txdpa, (ulong)di->txp, di->txin, di->txout, di->txavail);
|
||||
buf += sprintf(buf, "xmtcontrol 0x%x xmtaddr 0x%x xmtptr 0x%x xmtstatus 0x%x\n",
|
||||
R_REG(&di->regs->xmtcontrol),
|
||||
R_REG(&di->regs->xmtaddr),
|
||||
R_REG(&di->regs->xmtptr),
|
||||
R_REG(&di->regs->xmtstatus));
|
||||
return (buf);
|
||||
}
|
||||
|
||||
char*
|
||||
dma_dumprx(dma_info_t *di, char *buf)
|
||||
{
|
||||
buf += sprintf(buf, "rxd 0x%lx rxdpa 0x%lx rxp 0x%lx rxin %d rxout %d\n",
|
||||
(ulong)di->rxd, di->rxdpa, (ulong)di->rxp, di->rxin, di->rxout);
|
||||
buf += sprintf(buf, "rcvcontrol 0x%x rcvaddr 0x%x rcvptr 0x%x rcvstatus 0x%x\n",
|
||||
R_REG(&di->regs->rcvcontrol),
|
||||
R_REG(&di->regs->rcvaddr),
|
||||
R_REG(&di->regs->rcvptr),
|
||||
R_REG(&di->regs->rcvstatus));
|
||||
return (buf);
|
||||
}
|
||||
|
||||
char*
|
||||
dma_dump(dma_info_t *di, char *buf)
|
||||
{
|
||||
buf = dma_dumptx(di, buf);
|
||||
buf = dma_dumprx(di, buf);
|
||||
return (buf);
|
||||
}
|
||||
|
||||
uint
|
||||
dma_getvar(dma_info_t *di, char *name)
|
||||
{
|
||||
if (!strcmp(name, "&txavail"))
|
||||
return ((uint) &di->txavail);
|
||||
else {
|
||||
ASSERT(0);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
dma_txblock(dma_info_t *di)
|
||||
{
|
||||
di->txavail = 0;
|
||||
}
|
||||
|
||||
void
|
||||
dma_txunblock(dma_info_t *di)
|
||||
{
|
||||
di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
|
||||
}
|
||||
|
||||
uint
|
||||
dma_txactive(dma_info_t *di)
|
||||
{
|
||||
return (NTXDACTIVE(di->txin, di->txout));
|
||||
}
|
||||
|
||||
/*
|
||||
* Rotate all active tx dma ring entries "forward" by (ActiveDescriptor - txin).
|
||||
*/
|
||||
void
|
||||
dma_txrotate(di_t *di)
|
||||
{
|
||||
uint ad;
|
||||
uint nactive;
|
||||
uint rot;
|
||||
uint old, new;
|
||||
uint32 w;
|
||||
uint first, last;
|
||||
|
||||
ASSERT(dma_txsuspended(di));
|
||||
|
||||
nactive = dma_txactive(di);
|
||||
ad = B2I((R_REG(&di->regs->xmtstatus) & XS_AD_MASK) >> XS_AD_SHIFT);
|
||||
rot = TXD(ad - di->txin);
|
||||
|
||||
ASSERT(rot < di->ntxd);
|
||||
|
||||
/* full-ring case is a lot harder - don't worry about this */
|
||||
if (rot >= (di->ntxd - nactive)) {
|
||||
DMA_ERROR(("%s: dma_txrotate: ring full - punt\n", di->name));
|
||||
return;
|
||||
}
|
||||
|
||||
first = di->txin;
|
||||
last = PREVTXD(di->txout);
|
||||
|
||||
/* move entries starting at last and moving backwards to first */
|
||||
for (old = last; old != PREVTXD(first); old = PREVTXD(old)) {
|
||||
new = TXD(old + rot);
|
||||
|
||||
/*
|
||||
* Move the tx dma descriptor.
|
||||
* EOT is set only in the last entry in the ring.
|
||||
*/
|
||||
w = R_SM(&di->txd[old].ctrl) & ~CTRL_EOT;
|
||||
if (new == (di->ntxd - 1))
|
||||
w |= CTRL_EOT;
|
||||
W_SM(&di->txd[new].ctrl, w);
|
||||
W_SM(&di->txd[new].addr, R_SM(&di->txd[old].addr));
|
||||
|
||||
/* zap the old tx dma descriptor address field */
|
||||
W_SM(&di->txd[old].addr, 0xdeadbeef);
|
||||
|
||||
/* move the corresponding txp[] entry */
|
||||
ASSERT(di->txp[new] == NULL);
|
||||
di->txp[new] = di->txp[old];
|
||||
di->txp[old] = NULL;
|
||||
}
|
||||
|
||||
/* update txin and txout */
|
||||
di->txin = ad;
|
||||
di->txout = TXD(di->txout + rot);
|
||||
di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
|
||||
|
||||
/* kick the chip */
|
||||
W_REG(&di->regs->xmtptr, I2B(di->txout));
|
||||
}
|
||||
465
openwrt/package/linux/kernel-source/drivers/net/hnd/linux_osl.c
Normal file
465
openwrt/package/linux/kernel-source/drivers/net/hnd/linux_osl.c
Normal file
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
* Linux OS Independent Layer
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#define LINUX_OSL
|
||||
|
||||
#include <typedefs.h>
|
||||
#include <bcmendian.h>
|
||||
#include <linuxver.h>
|
||||
#include <linux_osl.h>
|
||||
#include <bcmutils.h>
|
||||
#include <linux/delay.h>
|
||||
#ifdef mips
|
||||
#include <asm/paccess.h>
|
||||
#endif
|
||||
#include <pcicfg.h>
|
||||
|
||||
#define PCI_CFG_RETRY 10
|
||||
|
||||
void*
|
||||
osl_pktget(void *drv, uint len, bool send)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
if ((skb = dev_alloc_skb(len)) == NULL)
|
||||
return (NULL);
|
||||
|
||||
skb_put(skb, len);
|
||||
|
||||
/* ensure the cookie field is cleared */
|
||||
PKTSETCOOKIE(skb, NULL);
|
||||
|
||||
return ((void*) skb);
|
||||
}
|
||||
|
||||
void
|
||||
osl_pktfree(void *p)
|
||||
{
|
||||
struct sk_buff *skb, *nskb;
|
||||
|
||||
skb = (struct sk_buff*) p;
|
||||
|
||||
/* perversion: we use skb->next to chain multi-skb packets */
|
||||
while (skb) {
|
||||
nskb = skb->next;
|
||||
skb->next = NULL;
|
||||
if (skb->destructor) {
|
||||
/* cannot kfree_skb() on hard IRQ (net/core/skbuff.c) if destructor exists */
|
||||
dev_kfree_skb_any(skb);
|
||||
} else {
|
||||
/* can free immediately (even in_irq()) if destructor does not exist */
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
skb = nskb;
|
||||
}
|
||||
}
|
||||
|
||||
uint32
|
||||
osl_pci_read_config(void *loc, uint offset, uint size)
|
||||
{
|
||||
struct pci_dev *pdev;
|
||||
uint val;
|
||||
uint retry=PCI_CFG_RETRY;
|
||||
|
||||
/* only 4byte access supported */
|
||||
ASSERT(size == 4);
|
||||
|
||||
pdev = (struct pci_dev*)loc;
|
||||
do {
|
||||
pci_read_config_dword(pdev, offset, &val);
|
||||
if (val != 0xffffffff)
|
||||
break;
|
||||
} while (retry--);
|
||||
|
||||
|
||||
return (val);
|
||||
}
|
||||
|
||||
void
|
||||
osl_pci_write_config(void *loc, uint offset, uint size, uint val)
|
||||
{
|
||||
struct pci_dev *pdev;
|
||||
uint retry=PCI_CFG_RETRY;
|
||||
|
||||
/* only 4byte access supported */
|
||||
ASSERT(size == 4);
|
||||
|
||||
pdev = (struct pci_dev*)loc;
|
||||
|
||||
do {
|
||||
pci_write_config_dword(pdev, offset, val);
|
||||
if (offset!=PCI_BAR0_WIN)
|
||||
break;
|
||||
if (osl_pci_read_config(loc,offset,size) == val)
|
||||
break;
|
||||
} while (retry--);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
osl_pcmcia_attr(void *osh, uint offset, char *buf, int size, bool write)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
osl_pcmcia_read_attr(void *osh, uint offset, void *buf, int size)
|
||||
{
|
||||
osl_pcmcia_attr(osh, offset, (char *) buf, size, FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
osl_pcmcia_write_attr(void *osh, uint offset, void *buf, int size)
|
||||
{
|
||||
osl_pcmcia_attr(osh, offset, (char *) buf, size, TRUE);
|
||||
}
|
||||
|
||||
#if defined(BINOSL)
|
||||
|
||||
void
|
||||
osl_assert(char *exp, char *file, int line)
|
||||
{
|
||||
char tempbuf[255];
|
||||
|
||||
sprintf(tempbuf, "assertion \"%s\" failed: file \"%s\", line %d\n", exp, file, line);
|
||||
panic(tempbuf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void*
|
||||
osl_malloc(uint size)
|
||||
{
|
||||
return (kmalloc(size, GFP_ATOMIC));
|
||||
}
|
||||
|
||||
void
|
||||
osl_mfree(void *addr, uint size)
|
||||
{
|
||||
kfree(addr);
|
||||
}
|
||||
|
||||
uint
|
||||
osl_malloced(void)
|
||||
{
|
||||
#ifdef MODULE
|
||||
return malloced;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* defined(BCMDBG) || defined(BINOSL) */
|
||||
|
||||
/*
|
||||
* BINOSL selects the slightly slower function-call-based binary compatible osl.
|
||||
*/
|
||||
#ifdef BINOSL
|
||||
|
||||
int
|
||||
osl_printf(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
char buf[1024];
|
||||
int len;
|
||||
|
||||
/* sprintf into a local buffer because there *is* no "vprintk()".. */
|
||||
va_start(args, format);
|
||||
len = vsprintf(buf, format, args);
|
||||
va_end(args);
|
||||
|
||||
if (len > sizeof (buf)) {
|
||||
printk("osl_printf: buffer overrun\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (printk(buf));
|
||||
}
|
||||
|
||||
int
|
||||
osl_sprintf(char *buf, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
int rc;
|
||||
|
||||
va_start(args, format);
|
||||
rc = vsprintf(buf, format, args);
|
||||
va_end(args);
|
||||
return (rc);
|
||||
}
|
||||
|
||||
int
|
||||
osl_strcmp(const char *s1, const char *s2)
|
||||
{
|
||||
return (strcmp(s1, s2));
|
||||
}
|
||||
|
||||
int
|
||||
osl_strncmp(const char *s1, const char *s2, uint n)
|
||||
{
|
||||
return (strncmp(s1, s2, n));
|
||||
}
|
||||
|
||||
int
|
||||
osl_strlen(char *s)
|
||||
{
|
||||
return (strlen(s));
|
||||
}
|
||||
|
||||
char*
|
||||
osl_strcpy(char *d, const char *s)
|
||||
{
|
||||
return (strcpy(d, s));
|
||||
}
|
||||
|
||||
char*
|
||||
osl_strncpy(char *d, const char *s, uint n)
|
||||
{
|
||||
return (strncpy(d, s, n));
|
||||
}
|
||||
|
||||
void
|
||||
bcopy(const void *src, void *dst, int len)
|
||||
{
|
||||
memcpy(dst, src, len);
|
||||
}
|
||||
|
||||
int
|
||||
bcmp(const void *b1, const void *b2, int len)
|
||||
{
|
||||
return (memcmp(b1, b2, len));
|
||||
}
|
||||
|
||||
void
|
||||
bzero(void *b, int len)
|
||||
{
|
||||
memset(b, '\0', len);
|
||||
}
|
||||
|
||||
uint32
|
||||
osl_readl(volatile uint32 *r)
|
||||
{
|
||||
return (readl(r));
|
||||
}
|
||||
|
||||
uint16
|
||||
osl_readw(volatile uint16 *r)
|
||||
{
|
||||
return (readw(r));
|
||||
}
|
||||
|
||||
uint8
|
||||
osl_readb(volatile uint8 *r)
|
||||
{
|
||||
return (readb(r));
|
||||
}
|
||||
|
||||
void
|
||||
osl_writel(uint32 v, volatile uint32 *r)
|
||||
{
|
||||
writel(v, r);
|
||||
}
|
||||
|
||||
void
|
||||
osl_writew(uint16 v, volatile uint16 *r)
|
||||
{
|
||||
writew(v, r);
|
||||
}
|
||||
|
||||
void
|
||||
osl_writeb(uint8 v, volatile uint8 *r)
|
||||
{
|
||||
writeb(v, r);
|
||||
}
|
||||
|
||||
void *
|
||||
osl_uncached(void *va)
|
||||
{
|
||||
#ifdef mips
|
||||
return ((void*)KSEG1ADDR(va));
|
||||
#else
|
||||
return ((void*)va);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint
|
||||
osl_getcycles(void)
|
||||
{
|
||||
uint cycles;
|
||||
|
||||
#if defined(mips)
|
||||
cycles = read_c0_count() * 2;
|
||||
#elif defined(__i386__)
|
||||
rdtscl(cycles);
|
||||
#else
|
||||
cycles = 0;
|
||||
#endif
|
||||
return cycles;
|
||||
}
|
||||
|
||||
void *
|
||||
osl_reg_map(uint32 pa, uint size)
|
||||
{
|
||||
return (ioremap_nocache((unsigned long)pa, (unsigned long)size));
|
||||
}
|
||||
|
||||
void
|
||||
osl_reg_unmap(void *va)
|
||||
{
|
||||
iounmap(va);
|
||||
}
|
||||
|
||||
int
|
||||
osl_busprobe(uint32 *val, uint32 addr)
|
||||
{
|
||||
#ifdef mips
|
||||
return get_dbe(*val, (uint32*)addr);
|
||||
#else
|
||||
*val = readl(addr);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void*
|
||||
osl_dma_alloc_consistent(void *dev, uint size, ulong *pap)
|
||||
{
|
||||
return (pci_alloc_consistent((struct pci_dev*)dev, size, (dma_addr_t*)pap));
|
||||
}
|
||||
|
||||
void
|
||||
osl_dma_free_consistent(void *dev, void *va, uint size, ulong pa)
|
||||
{
|
||||
pci_free_consistent((struct pci_dev*)dev, size, va, (dma_addr_t)pa);
|
||||
}
|
||||
|
||||
uint
|
||||
osl_dma_map(void *dev, void *va, uint size, int direction)
|
||||
{
|
||||
int dir;
|
||||
|
||||
dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
|
||||
return (pci_map_single(dev, va, size, dir));
|
||||
}
|
||||
|
||||
void
|
||||
osl_dma_unmap(void *dev, uint pa, uint size, int direction)
|
||||
{
|
||||
int dir;
|
||||
|
||||
dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
|
||||
pci_unmap_single(dev, (uint32)pa, size, dir);
|
||||
}
|
||||
|
||||
void
|
||||
osl_delay(uint usec)
|
||||
{
|
||||
udelay(usec);
|
||||
}
|
||||
|
||||
uchar*
|
||||
osl_pktdata(void *drv, void *skb)
|
||||
{
|
||||
return (((struct sk_buff*)skb)->data);
|
||||
}
|
||||
|
||||
uint
|
||||
osl_pktlen(void *drv, void *skb)
|
||||
{
|
||||
return (((struct sk_buff*)skb)->len);
|
||||
}
|
||||
|
||||
uint
|
||||
osl_pktheadroom(void *drv, void *skb)
|
||||
{
|
||||
return (uint) skb_headroom((struct sk_buff *) skb);
|
||||
}
|
||||
|
||||
uint
|
||||
osl_pkttailroom(void *drv, void *skb)
|
||||
{
|
||||
return (uint) skb_tailroom((struct sk_buff *) skb);
|
||||
}
|
||||
|
||||
void*
|
||||
osl_pktnext(void *drv, void *skb)
|
||||
{
|
||||
return (((struct sk_buff*)skb)->next);
|
||||
}
|
||||
|
||||
void
|
||||
osl_pktsetnext(void *skb, void *x)
|
||||
{
|
||||
((struct sk_buff*)skb)->next = (struct sk_buff*)x;
|
||||
}
|
||||
|
||||
void
|
||||
osl_pktsetlen(void *drv, void *skb, uint len)
|
||||
{
|
||||
__skb_trim((struct sk_buff*)skb, len);
|
||||
}
|
||||
|
||||
uchar*
|
||||
osl_pktpush(void *drv, void *skb, int bytes)
|
||||
{
|
||||
return (skb_push((struct sk_buff*)skb, bytes));
|
||||
}
|
||||
|
||||
uchar*
|
||||
osl_pktpull(void *drv, void *skb, int bytes)
|
||||
{
|
||||
return (skb_pull((struct sk_buff*)skb, bytes));
|
||||
}
|
||||
|
||||
void*
|
||||
osl_pktdup(void *drv, void *skb)
|
||||
{
|
||||
return (skb_clone((struct sk_buff*)skb, GFP_ATOMIC));
|
||||
}
|
||||
|
||||
void*
|
||||
osl_pktcookie(void *skb)
|
||||
{
|
||||
return ((void*)((struct sk_buff*)skb)->csum);
|
||||
}
|
||||
|
||||
void
|
||||
osl_pktsetcookie(void *skb, void *x)
|
||||
{
|
||||
((struct sk_buff*)skb)->csum = (uint)x;
|
||||
}
|
||||
|
||||
void*
|
||||
osl_pktlink(void *skb)
|
||||
{
|
||||
return (((struct sk_buff*)skb)->prev);
|
||||
}
|
||||
|
||||
void
|
||||
osl_pktsetlink(void *skb, void *x)
|
||||
{
|
||||
((struct sk_buff*)skb)->prev = (struct sk_buff*)x;
|
||||
}
|
||||
|
||||
uint
|
||||
osl_pktprio(void *skb)
|
||||
{
|
||||
return (((struct sk_buff*)skb)->priority);
|
||||
}
|
||||
|
||||
void
|
||||
osl_pktsetprio(void *skb, uint x)
|
||||
{
|
||||
((struct sk_buff*)skb)->priority = x;
|
||||
}
|
||||
|
||||
#endif /* BINOSL */
|
||||
2164
openwrt/package/linux/kernel-source/drivers/net/hnd/sbutils.c
Normal file
2164
openwrt/package/linux/kernel-source/drivers/net/hnd/sbutils.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
|
||||
cat <<EOF
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
EOF
|
||||
|
||||
for file in $* ; do
|
||||
${NM} $file | sed -ne 's/[0-9A-Fa-f]* [DT] \([^ ]*\)/extern void \1; EXPORT_SYMBOL(\1);/p'
|
||||
done
|
||||
59
openwrt/package/linux/kernel-source/drivers/net/wl/Makefile
Normal file
59
openwrt/package/linux/kernel-source/drivers/net/wl/Makefile
Normal file
@@ -0,0 +1,59 @@
|
||||
#
|
||||
# Makefile for the Broadcom wl driver
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
|
||||
O_TARGET := wl.o
|
||||
|
||||
WL_OBJS := wl_linux.o wlc.o d11ucode.o wlc_phy.o wlc_rate.o wlc_led.o wlc_security.o rc4.o tkhash.o bcmwpa.o
|
||||
|
||||
INSUP_OBJS := aes.o aeskeywrap.o hmac.o md5.o passhash.o prf.o rijndael-alg-fst.o sha1.o
|
||||
|
||||
# Alternate ioctl interfaces
|
||||
ifeq ($(CONFIG_NET_WIRELESS),y)
|
||||
WL_OBJS += wlc_cmn_ioctl.o
|
||||
endif
|
||||
ifeq ($(CONFIG_WL_OID),y)
|
||||
WL_OBJS += wl_oid.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_WL_STA),y)
|
||||
WL_OBJS += $(INSUP_OBJS)
|
||||
endif
|
||||
|
||||
# Prefix driver variants
|
||||
WL_APOBJS := $(foreach obj,$(WL_OBJS),ap_$(obj))
|
||||
WL_STAOBJS := $(foreach obj,$(WL_OBJS) wlc_sup.o,sta_$(obj))
|
||||
WL_APSTAOBJS := $(foreach obj,$(WL_OBJS) wlc_sup.o,apsta_$(obj))
|
||||
ifneq ($(CONFIG_WL_STA),y)
|
||||
WL_APSTAOBJS += $(foreach obj,$(INSUP_OBJS), apsta_$(obj))
|
||||
endif
|
||||
|
||||
# Either or both
|
||||
ifeq ($(CONFIG_WL_AP),y)
|
||||
AP := AP
|
||||
endif
|
||||
ifeq ($(CONFIG_WL_STA),y)
|
||||
STA := STA
|
||||
endif
|
||||
|
||||
# Build all variants as modules but link only one of them
|
||||
export-objs :=
|
||||
obj-y := $(WL_$(AP)$(STA)OBJS)
|
||||
obj-m := $(O_TARGET)
|
||||
variant-objs := $(WL_APOBJS) $(WL_STAOBJS) $(WL_APSTAOBJS)
|
||||
|
||||
EXTRA_CFLAGS += -DDMA
|
||||
|
||||
include $(TOPDIR)/Rules.make
|
||||
|
||||
|
||||
@@ -0,0 +1,912 @@
|
||||
/*
|
||||
*
|
||||
* bcm47xx pcmcia driver
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Based on sa1100_generic.c from www.handhelds.org,
|
||||
* and au1000_generic.c from oss.sgi.com.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/config.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/tqueue.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include <pcmcia/version.h>
|
||||
#include <pcmcia/cs_types.h>
|
||||
#include <pcmcia/cs.h>
|
||||
#include <pcmcia/ss.h>
|
||||
#include <pcmcia/bulkmem.h>
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/bus_ops.h>
|
||||
#include "cs_internal.h"
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#include <typedefs.h>
|
||||
#include <bcm4710.h>
|
||||
#include <sbextif.h>
|
||||
|
||||
#include "bcm4710pcmcia.h"
|
||||
|
||||
#ifdef PCMCIA_DEBUG
|
||||
static int pc_debug = PCMCIA_DEBUG;
|
||||
#endif
|
||||
|
||||
MODULE_DESCRIPTION("Linux PCMCIA Card Services: bcm47xx Socket Controller");
|
||||
|
||||
/* This structure maintains housekeeping state for each socket, such
|
||||
* as the last known values of the card detect pins, or the Card Services
|
||||
* callback value associated with the socket:
|
||||
*/
|
||||
static struct bcm47xx_pcmcia_socket *pcmcia_socket;
|
||||
static int socket_count;
|
||||
|
||||
|
||||
/* Returned by the low-level PCMCIA interface: */
|
||||
static struct pcmcia_low_level *pcmcia_low_level;
|
||||
|
||||
/* Event poll timer structure */
|
||||
static struct timer_list poll_timer;
|
||||
|
||||
|
||||
/* Prototypes for routines which are used internally: */
|
||||
|
||||
static int bcm47xx_pcmcia_driver_init(void);
|
||||
static void bcm47xx_pcmcia_driver_shutdown(void);
|
||||
static void bcm47xx_pcmcia_task_handler(void *data);
|
||||
static void bcm47xx_pcmcia_poll_event(unsigned long data);
|
||||
static void bcm47xx_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs);
|
||||
static struct tq_struct bcm47xx_pcmcia_task;
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static int bcm47xx_pcmcia_proc_status(char *buf, char **start,
|
||||
off_t pos, int count, int *eof, void *data);
|
||||
#endif
|
||||
|
||||
|
||||
/* Prototypes for operations which are exported to the
|
||||
* in-kernel PCMCIA core:
|
||||
*/
|
||||
|
||||
static int bcm47xx_pcmcia_init(unsigned int sock);
|
||||
static int bcm47xx_pcmcia_suspend(unsigned int sock);
|
||||
static int bcm47xx_pcmcia_register_callback(unsigned int sock,
|
||||
void (*handler)(void *, unsigned int), void *info);
|
||||
static int bcm47xx_pcmcia_inquire_socket(unsigned int sock, socket_cap_t *cap);
|
||||
static int bcm47xx_pcmcia_get_status(unsigned int sock, u_int *value);
|
||||
static int bcm47xx_pcmcia_get_socket(unsigned int sock, socket_state_t *state);
|
||||
static int bcm47xx_pcmcia_set_socket(unsigned int sock, socket_state_t *state);
|
||||
static int bcm47xx_pcmcia_get_io_map(unsigned int sock, struct pccard_io_map *io);
|
||||
static int bcm47xx_pcmcia_set_io_map(unsigned int sock, struct pccard_io_map *io);
|
||||
static int bcm47xx_pcmcia_get_mem_map(unsigned int sock, struct pccard_mem_map *mem);
|
||||
static int bcm47xx_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *mem);
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static void bcm47xx_pcmcia_proc_setup(unsigned int sock, struct proc_dir_entry *base);
|
||||
#endif
|
||||
|
||||
static struct pccard_operations bcm47xx_pcmcia_operations = {
|
||||
bcm47xx_pcmcia_init,
|
||||
bcm47xx_pcmcia_suspend,
|
||||
bcm47xx_pcmcia_register_callback,
|
||||
bcm47xx_pcmcia_inquire_socket,
|
||||
bcm47xx_pcmcia_get_status,
|
||||
bcm47xx_pcmcia_get_socket,
|
||||
bcm47xx_pcmcia_set_socket,
|
||||
bcm47xx_pcmcia_get_io_map,
|
||||
bcm47xx_pcmcia_set_io_map,
|
||||
bcm47xx_pcmcia_get_mem_map,
|
||||
bcm47xx_pcmcia_set_mem_map,
|
||||
#ifdef CONFIG_PROC_FS
|
||||
bcm47xx_pcmcia_proc_setup
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_driver_init()
|
||||
*
|
||||
* This routine performs a basic sanity check to ensure that this
|
||||
* kernel has been built with the appropriate board-specific low-level
|
||||
* PCMCIA support, performs low-level PCMCIA initialization, registers
|
||||
* this socket driver with Card Services, and then spawns the daemon
|
||||
* thread which is the real workhorse of the socket driver.
|
||||
*
|
||||
* Please see linux/Documentation/arm/SA1100/PCMCIA for more information
|
||||
* on the low-level kernel interface.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
static int __init bcm47xx_pcmcia_driver_init(void)
|
||||
{
|
||||
servinfo_t info;
|
||||
struct pcmcia_init pcmcia_init;
|
||||
struct pcmcia_state state;
|
||||
unsigned int i;
|
||||
unsigned long tmp;
|
||||
|
||||
|
||||
printk("\nBCM47XX PCMCIA (CS release %s)\n", CS_RELEASE);
|
||||
|
||||
CardServices(GetCardServicesInfo, &info);
|
||||
|
||||
if (info.Revision != CS_RELEASE_CODE) {
|
||||
printk(KERN_ERR "Card Services release codes do not match\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BCM4710
|
||||
pcmcia_low_level=&bcm4710_pcmcia_ops;
|
||||
#else
|
||||
#error Unsupported Broadcom BCM47XX board.
|
||||
#endif
|
||||
|
||||
pcmcia_init.handler=bcm47xx_pcmcia_interrupt;
|
||||
|
||||
if ((socket_count = pcmcia_low_level->init(&pcmcia_init)) < 0) {
|
||||
printk(KERN_ERR "Unable to initialize PCMCIA service.\n");
|
||||
return -EIO;
|
||||
} else {
|
||||
printk("\t%d PCMCIA sockets initialized.\n", socket_count);
|
||||
}
|
||||
|
||||
pcmcia_socket =
|
||||
kmalloc(sizeof(struct bcm47xx_pcmcia_socket) * socket_count,
|
||||
GFP_KERNEL);
|
||||
memset(pcmcia_socket, 0,
|
||||
sizeof(struct bcm47xx_pcmcia_socket) * socket_count);
|
||||
if (!pcmcia_socket) {
|
||||
printk(KERN_ERR "Card Services can't get memory \n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < socket_count; i++) {
|
||||
if (pcmcia_low_level->socket_state(i, &state) < 0) {
|
||||
printk(KERN_ERR "Unable to get PCMCIA status\n");
|
||||
return -EIO;
|
||||
}
|
||||
pcmcia_socket[i].k_state = state;
|
||||
pcmcia_socket[i].cs_state.csc_mask = SS_DETECT;
|
||||
|
||||
if (i == 0) {
|
||||
pcmcia_socket[i].virt_io =
|
||||
(unsigned long)ioremap_nocache(EXTIF_PCMCIA_IOBASE(BCM4710_EXTIF), 0x1000);
|
||||
/* Substract ioport base which gets added by in/out */
|
||||
pcmcia_socket[i].virt_io -= mips_io_port_base;
|
||||
pcmcia_socket[i].phys_attr =
|
||||
(unsigned long)EXTIF_PCMCIA_CFGBASE(BCM4710_EXTIF);
|
||||
pcmcia_socket[i].phys_mem =
|
||||
(unsigned long)EXTIF_PCMCIA_MEMBASE(BCM4710_EXTIF);
|
||||
} else {
|
||||
printk(KERN_ERR "bcm4710: socket 1 not supported\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only advertise as many sockets as we can detect: */
|
||||
if (register_ss_entry(socket_count, &bcm47xx_pcmcia_operations) < 0) {
|
||||
printk(KERN_ERR "Unable to register socket service routine\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Start the event poll timer.
|
||||
* It will reschedule by itself afterwards.
|
||||
*/
|
||||
bcm47xx_pcmcia_poll_event(0);
|
||||
|
||||
DEBUG(1, "bcm4710: initialization complete\n");
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
module_init(bcm47xx_pcmcia_driver_init);
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_driver_shutdown()
|
||||
*
|
||||
* Invokes the low-level kernel service to free IRQs associated with this
|
||||
* socket controller and reset GPIO edge detection.
|
||||
*/
|
||||
static void __exit bcm47xx_pcmcia_driver_shutdown(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
del_timer_sync(&poll_timer);
|
||||
unregister_ss_entry(&bcm47xx_pcmcia_operations);
|
||||
pcmcia_low_level->shutdown();
|
||||
flush_scheduled_tasks();
|
||||
for (i = 0; i < socket_count; i++) {
|
||||
if (pcmcia_socket[i].virt_io)
|
||||
iounmap((void *)pcmcia_socket[i].virt_io);
|
||||
if (pcmcia_socket[i].phys_attr)
|
||||
iounmap((void *)pcmcia_socket[i].phys_attr);
|
||||
if (pcmcia_socket[i].phys_mem)
|
||||
iounmap((void *)pcmcia_socket[i].phys_mem);
|
||||
}
|
||||
DEBUG(1, "bcm4710: shutdown complete\n");
|
||||
}
|
||||
|
||||
module_exit(bcm47xx_pcmcia_driver_shutdown);
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_init()
|
||||
* We perform all of the interesting initialization tasks in
|
||||
* bcm47xx_pcmcia_driver_init().
|
||||
*
|
||||
* Returns: 0
|
||||
*/
|
||||
static int bcm47xx_pcmcia_init(unsigned int sock)
|
||||
{
|
||||
DEBUG(1, "%s(): initializing socket %u\n", __FUNCTION__, sock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_suspend()
|
||||
*
|
||||
* We don't currently perform any actions on a suspend.
|
||||
*
|
||||
* Returns: 0
|
||||
*/
|
||||
static int bcm47xx_pcmcia_suspend(unsigned int sock)
|
||||
{
|
||||
DEBUG(1, "%s(): suspending socket %u\n", __FUNCTION__, sock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_events()
|
||||
*
|
||||
* Helper routine to generate a Card Services event mask based on
|
||||
* state information obtained from the kernel low-level PCMCIA layer
|
||||
* in a recent (and previous) sampling. Updates `prev_state'.
|
||||
*
|
||||
* Returns: an event mask for the given socket state.
|
||||
*/
|
||||
static inline unsigned
|
||||
bcm47xx_pcmcia_events(struct pcmcia_state *state,
|
||||
struct pcmcia_state *prev_state,
|
||||
unsigned int mask, unsigned int flags)
|
||||
{
|
||||
unsigned int events=0;
|
||||
|
||||
if (state->bvd1 != prev_state->bvd1) {
|
||||
|
||||
DEBUG(3, "%s(): card BVD1 value %u\n", __FUNCTION__, state->bvd1);
|
||||
|
||||
events |= mask & (flags & SS_IOCARD) ? SS_STSCHG : SS_BATDEAD;
|
||||
}
|
||||
|
||||
if (state->bvd2 != prev_state->bvd2) {
|
||||
|
||||
DEBUG(3, "%s(): card BVD2 value %u\n", __FUNCTION__, state->bvd2);
|
||||
|
||||
events |= mask & (flags & SS_IOCARD) ? 0 : SS_BATWARN;
|
||||
}
|
||||
|
||||
if (state->detect != prev_state->detect) {
|
||||
|
||||
DEBUG(3, "%s(): card detect value %u\n", __FUNCTION__, state->detect);
|
||||
|
||||
events |= mask & SS_DETECT;
|
||||
}
|
||||
|
||||
|
||||
if (state->ready != prev_state->ready) {
|
||||
|
||||
DEBUG(3, "%s(): card ready value %u\n", __FUNCTION__, state->ready);
|
||||
|
||||
events |= mask & ((flags & SS_IOCARD) ? 0 : SS_READY);
|
||||
}
|
||||
|
||||
if (events != 0) {
|
||||
DEBUG(2, "events: %s%s%s%s%s\n",
|
||||
(events & SS_DETECT) ? "DETECT " : "",
|
||||
(events & SS_READY) ? "READY " : "",
|
||||
(events & SS_BATDEAD) ? "BATDEAD " : "",
|
||||
(events & SS_BATWARN) ? "BATWARN " : "",
|
||||
(events & SS_STSCHG) ? "STSCHG " : "");
|
||||
}
|
||||
|
||||
*prev_state=*state;
|
||||
return events;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_task_handler()
|
||||
*
|
||||
* Processes serviceable socket events using the "eventd" thread context.
|
||||
*
|
||||
* Event processing (specifically, the invocation of the Card Services event
|
||||
* callback) occurs in this thread rather than in the actual interrupt
|
||||
* handler due to the use of scheduling operations in the PCMCIA core.
|
||||
*/
|
||||
static void bcm47xx_pcmcia_task_handler(void *data)
|
||||
{
|
||||
struct pcmcia_state state;
|
||||
int i, events, irq_status;
|
||||
|
||||
DEBUG(4, "%s(): entering PCMCIA monitoring thread\n", __FUNCTION__);
|
||||
|
||||
for (i = 0; i < socket_count; i++) {
|
||||
if ((irq_status = pcmcia_low_level->socket_state(i, &state)) < 0)
|
||||
printk(KERN_ERR "Error in kernel low-level PCMCIA service.\n");
|
||||
|
||||
events = bcm47xx_pcmcia_events(&state,
|
||||
&pcmcia_socket[i].k_state,
|
||||
pcmcia_socket[i].cs_state.csc_mask,
|
||||
pcmcia_socket[i].cs_state.flags);
|
||||
|
||||
if (pcmcia_socket[i].handler != NULL) {
|
||||
pcmcia_socket[i].handler(pcmcia_socket[i].handler_info,
|
||||
events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct tq_struct bcm47xx_pcmcia_task = {
|
||||
routine: bcm47xx_pcmcia_task_handler
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_poll_event()
|
||||
*
|
||||
* Let's poll for events in addition to IRQs since IRQ only is unreliable...
|
||||
*/
|
||||
static void bcm47xx_pcmcia_poll_event(unsigned long dummy)
|
||||
{
|
||||
DEBUG(4, "%s(): polling for events\n", __FUNCTION__);
|
||||
|
||||
poll_timer.function = bcm47xx_pcmcia_poll_event;
|
||||
poll_timer.expires = jiffies + BCM47XX_PCMCIA_POLL_PERIOD;
|
||||
add_timer(&poll_timer);
|
||||
schedule_task(&bcm47xx_pcmcia_task);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_interrupt()
|
||||
*
|
||||
* Service routine for socket driver interrupts (requested by the
|
||||
* low-level PCMCIA init() operation via bcm47xx_pcmcia_thread()).
|
||||
*
|
||||
* The actual interrupt-servicing work is performed by
|
||||
* bcm47xx_pcmcia_task(), largely because the Card Services event-
|
||||
* handling code performs scheduling operations which cannot be
|
||||
* executed from within an interrupt context.
|
||||
*/
|
||||
static void
|
||||
bcm47xx_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs)
|
||||
{
|
||||
DEBUG(3, "%s(): servicing IRQ %d\n", __FUNCTION__, irq);
|
||||
schedule_task(&bcm47xx_pcmcia_task);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_register_callback()
|
||||
*
|
||||
* Implements the register_callback() operation for the in-kernel
|
||||
* PCMCIA service (formerly SS_RegisterCallback in Card Services). If
|
||||
* the function pointer `handler' is not NULL, remember the callback
|
||||
* location in the state for `sock', and increment the usage counter
|
||||
* for the driver module. (The callback is invoked from the interrupt
|
||||
* service routine, bcm47xx_pcmcia_interrupt(), to notify Card Services
|
||||
* of interesting events.) Otherwise, clear the callback pointer in the
|
||||
* socket state and decrement the module usage count.
|
||||
*
|
||||
* Returns: 0
|
||||
*/
|
||||
static int
|
||||
bcm47xx_pcmcia_register_callback(unsigned int sock,
|
||||
void (*handler)(void *, unsigned int), void *info)
|
||||
{
|
||||
if (handler == NULL) {
|
||||
pcmcia_socket[sock].handler = NULL;
|
||||
MOD_DEC_USE_COUNT;
|
||||
} else {
|
||||
MOD_INC_USE_COUNT;
|
||||
pcmcia_socket[sock].handler = handler;
|
||||
pcmcia_socket[sock].handler_info = info;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_inquire_socket()
|
||||
*
|
||||
* Implements the inquire_socket() operation for the in-kernel PCMCIA
|
||||
* service (formerly SS_InquireSocket in Card Services). Of note is
|
||||
* the setting of the SS_CAP_PAGE_REGS bit in the `features' field of
|
||||
* `cap' to "trick" Card Services into tolerating large "I/O memory"
|
||||
* addresses. Also set is SS_CAP_STATIC_MAP, which disables the memory
|
||||
* resource database check. (Mapped memory is set up within the socket
|
||||
* driver itself.)
|
||||
*
|
||||
* In conjunction with the STATIC_MAP capability is a new field,
|
||||
* `io_offset', recommended by David Hinds. Rather than go through
|
||||
* the SetIOMap interface (which is not quite suited for communicating
|
||||
* window locations up from the socket driver), we just pass up
|
||||
* an offset which is applied to client-requested base I/O addresses
|
||||
* in alloc_io_space().
|
||||
*
|
||||
* Returns: 0 on success, -1 if no pin has been configured for `sock'
|
||||
*/
|
||||
static int
|
||||
bcm47xx_pcmcia_inquire_socket(unsigned int sock, socket_cap_t *cap)
|
||||
{
|
||||
struct pcmcia_irq_info irq_info;
|
||||
|
||||
if (sock >= socket_count) {
|
||||
printk(KERN_ERR "bcm47xx: socket %u not configured\n", sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* SS_CAP_PAGE_REGS: used by setup_cis_mem() in cistpl.c to set the
|
||||
* force_low argument to validate_mem() in rsrc_mgr.c -- since in
|
||||
* general, the mapped * addresses of the PCMCIA memory regions
|
||||
* will not be within 0xffff, setting force_low would be
|
||||
* undesirable.
|
||||
*
|
||||
* SS_CAP_STATIC_MAP: don't bother with the (user-configured) memory
|
||||
* resource database; we instead pass up physical address ranges
|
||||
* and allow other parts of Card Services to deal with remapping.
|
||||
*
|
||||
* SS_CAP_PCCARD: we can deal with 16-bit PCMCIA & CF cards, but
|
||||
* not 32-bit CardBus devices.
|
||||
*/
|
||||
cap->features = (SS_CAP_PAGE_REGS | SS_CAP_STATIC_MAP | SS_CAP_PCCARD);
|
||||
|
||||
irq_info.sock = sock;
|
||||
irq_info.irq = -1;
|
||||
|
||||
if (pcmcia_low_level->get_irq_info(&irq_info) < 0) {
|
||||
printk(KERN_ERR "Error obtaining IRQ info socket %u\n", sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cap->irq_mask = 0;
|
||||
cap->map_size = PAGE_SIZE;
|
||||
cap->pci_irq = irq_info.irq;
|
||||
cap->io_offset = pcmcia_socket[sock].virt_io;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_get_status()
|
||||
*
|
||||
* Implements the get_status() operation for the in-kernel PCMCIA
|
||||
* service (formerly SS_GetStatus in Card Services). Essentially just
|
||||
* fills in bits in `status' according to internal driver state or
|
||||
* the value of the voltage detect chipselect register.
|
||||
*
|
||||
* As a debugging note, during card startup, the PCMCIA core issues
|
||||
* three set_socket() commands in a row the first with RESET deasserted,
|
||||
* the second with RESET asserted, and the last with RESET deasserted
|
||||
* again. Following the third set_socket(), a get_status() command will
|
||||
* be issued. The kernel is looking for the SS_READY flag (see
|
||||
* setup_socket(), reset_socket(), and unreset_socket() in cs.c).
|
||||
*
|
||||
* Returns: 0
|
||||
*/
|
||||
static int
|
||||
bcm47xx_pcmcia_get_status(unsigned int sock, unsigned int *status)
|
||||
{
|
||||
struct pcmcia_state state;
|
||||
|
||||
|
||||
if ((pcmcia_low_level->socket_state(sock, &state)) < 0) {
|
||||
printk(KERN_ERR "Unable to get PCMCIA status from kernel.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pcmcia_socket[sock].k_state = state;
|
||||
|
||||
*status = state.detect ? SS_DETECT : 0;
|
||||
|
||||
*status |= state.ready ? SS_READY : 0;
|
||||
|
||||
/* The power status of individual sockets is not available
|
||||
* explicitly from the hardware, so we just remember the state
|
||||
* and regurgitate it upon request:
|
||||
*/
|
||||
*status |= pcmcia_socket[sock].cs_state.Vcc ? SS_POWERON : 0;
|
||||
|
||||
if (pcmcia_socket[sock].cs_state.flags & SS_IOCARD)
|
||||
*status |= state.bvd1 ? SS_STSCHG : 0;
|
||||
else {
|
||||
if (state.bvd1 == 0)
|
||||
*status |= SS_BATDEAD;
|
||||
else if (state.bvd2 == 0)
|
||||
*status |= SS_BATWARN;
|
||||
}
|
||||
|
||||
*status |= state.vs_3v ? SS_3VCARD : 0;
|
||||
|
||||
*status |= state.vs_Xv ? SS_XVCARD : 0;
|
||||
|
||||
DEBUG(2, "\tstatus: %s%s%s%s%s%s%s%s\n",
|
||||
(*status&SS_DETECT)?"DETECT ":"",
|
||||
(*status&SS_READY)?"READY ":"",
|
||||
(*status&SS_BATDEAD)?"BATDEAD ":"",
|
||||
(*status&SS_BATWARN)?"BATWARN ":"",
|
||||
(*status&SS_POWERON)?"POWERON ":"",
|
||||
(*status&SS_STSCHG)?"STSCHG ":"",
|
||||
(*status&SS_3VCARD)?"3VCARD ":"",
|
||||
(*status&SS_XVCARD)?"XVCARD ":"");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_get_socket()
|
||||
*
|
||||
* Implements the get_socket() operation for the in-kernel PCMCIA
|
||||
* service (formerly SS_GetSocket in Card Services). Not a very
|
||||
* exciting routine.
|
||||
*
|
||||
* Returns: 0
|
||||
*/
|
||||
static int
|
||||
bcm47xx_pcmcia_get_socket(unsigned int sock, socket_state_t *state)
|
||||
{
|
||||
DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);
|
||||
|
||||
/* This information was given to us in an earlier call to set_socket(),
|
||||
* so we're just regurgitating it here:
|
||||
*/
|
||||
*state = pcmcia_socket[sock].cs_state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_set_socket()
|
||||
*
|
||||
* Implements the set_socket() operation for the in-kernel PCMCIA
|
||||
* service (formerly SS_SetSocket in Card Services). We more or
|
||||
* less punt all of this work and let the kernel handle the details
|
||||
* of power configuration, reset, &c. We also record the value of
|
||||
* `state' in order to regurgitate it to the PCMCIA core later.
|
||||
*
|
||||
* Returns: 0
|
||||
*/
|
||||
static int
|
||||
bcm47xx_pcmcia_set_socket(unsigned int sock, socket_state_t *state)
|
||||
{
|
||||
struct pcmcia_configure configure;
|
||||
|
||||
DEBUG(2, "\tmask: %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n"
|
||||
"\tVcc %d Vpp %d irq %d\n",
|
||||
(state->csc_mask == 0) ? "<NONE>" : "",
|
||||
(state->csc_mask & SS_DETECT) ? "DETECT " : "",
|
||||
(state->csc_mask & SS_READY) ? "READY " : "",
|
||||
(state->csc_mask & SS_BATDEAD) ? "BATDEAD " : "",
|
||||
(state->csc_mask & SS_BATWARN) ? "BATWARN " : "",
|
||||
(state->csc_mask & SS_STSCHG) ? "STSCHG " : "",
|
||||
(state->flags == 0) ? "<NONE>" : "",
|
||||
(state->flags & SS_PWR_AUTO) ? "PWR_AUTO " : "",
|
||||
(state->flags & SS_IOCARD) ? "IOCARD " : "",
|
||||
(state->flags & SS_RESET) ? "RESET " : "",
|
||||
(state->flags & SS_SPKR_ENA) ? "SPKR_ENA " : "",
|
||||
(state->flags & SS_OUTPUT_ENA) ? "OUTPUT_ENA " : "",
|
||||
state->Vcc, state->Vpp, state->io_irq);
|
||||
|
||||
configure.sock = sock;
|
||||
configure.vcc = state->Vcc;
|
||||
configure.vpp = state->Vpp;
|
||||
configure.output = (state->flags & SS_OUTPUT_ENA) ? 1 : 0;
|
||||
configure.speaker = (state->flags & SS_SPKR_ENA) ? 1 : 0;
|
||||
configure.reset = (state->flags & SS_RESET) ? 1 : 0;
|
||||
|
||||
if (pcmcia_low_level->configure_socket(&configure) < 0) {
|
||||
printk(KERN_ERR "Unable to configure socket %u\n", sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pcmcia_socket[sock].cs_state = *state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_get_io_map()
|
||||
*
|
||||
* Implements the get_io_map() operation for the in-kernel PCMCIA
|
||||
* service (formerly SS_GetIOMap in Card Services). Just returns an
|
||||
* I/O map descriptor which was assigned earlier by a set_io_map().
|
||||
*
|
||||
* Returns: 0 on success, -1 if the map index was out of range
|
||||
*/
|
||||
static int
|
||||
bcm47xx_pcmcia_get_io_map(unsigned int sock, struct pccard_io_map *map)
|
||||
{
|
||||
DEBUG(2, "bcm47xx_pcmcia_get_io_map: sock %d\n", sock);
|
||||
|
||||
if (map->map >= MAX_IO_WIN) {
|
||||
printk(KERN_ERR "%s(): map (%d) out of range\n",
|
||||
__FUNCTION__, map->map);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*map = pcmcia_socket[sock].io_map[map->map];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_set_io_map()
|
||||
*
|
||||
* Implements the set_io_map() operation for the in-kernel PCMCIA
|
||||
* service (formerly SS_SetIOMap in Card Services). We configure
|
||||
* the map speed as requested, but override the address ranges
|
||||
* supplied by Card Services.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
bcm47xx_pcmcia_set_io_map(unsigned int sock, struct pccard_io_map *map)
|
||||
{
|
||||
unsigned int speed;
|
||||
unsigned long start;
|
||||
|
||||
DEBUG(2, "\tmap %u speed %u\n\tstart 0x%08lx stop 0x%08lx\n"
|
||||
"\tflags: %s%s%s%s%s%s%s%s\n",
|
||||
map->map, map->speed, map->start, map->stop,
|
||||
(map->flags == 0) ? "<NONE>" : "",
|
||||
(map->flags & MAP_ACTIVE) ? "ACTIVE " : "",
|
||||
(map->flags & MAP_16BIT) ? "16BIT " : "",
|
||||
(map->flags & MAP_AUTOSZ) ? "AUTOSZ " : "",
|
||||
(map->flags & MAP_0WS) ? "0WS " : "",
|
||||
(map->flags & MAP_WRPROT) ? "WRPROT " : "",
|
||||
(map->flags & MAP_USE_WAIT) ? "USE_WAIT " : "",
|
||||
(map->flags & MAP_PREFETCH) ? "PREFETCH " : "");
|
||||
|
||||
if (map->map >= MAX_IO_WIN) {
|
||||
printk(KERN_ERR "%s(): map (%d) out of range\n",
|
||||
__FUNCTION__, map->map);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (map->flags & MAP_ACTIVE) {
|
||||
speed = (map->speed > 0) ? map->speed : BCM47XX_PCMCIA_IO_SPEED;
|
||||
pcmcia_socket[sock].speed_io = speed;
|
||||
}
|
||||
|
||||
start = map->start;
|
||||
|
||||
if (map->stop == 1) {
|
||||
map->stop = PAGE_SIZE - 1;
|
||||
}
|
||||
|
||||
map->start = pcmcia_socket[sock].virt_io;
|
||||
map->stop = map->start + (map->stop - start);
|
||||
pcmcia_socket[sock].io_map[map->map] = *map;
|
||||
DEBUG(2, "set_io_map %d start %x stop %x\n",
|
||||
map->map, map->start, map->stop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_get_mem_map()
|
||||
*
|
||||
* Implements the get_mem_map() operation for the in-kernel PCMCIA
|
||||
* service (formerly SS_GetMemMap in Card Services). Just returns a
|
||||
* memory map descriptor which was assigned earlier by a
|
||||
* set_mem_map() request.
|
||||
*
|
||||
* Returns: 0 on success, -1 if the map index was out of range
|
||||
*/
|
||||
static int
|
||||
bcm47xx_pcmcia_get_mem_map(unsigned int sock, struct pccard_mem_map *map)
|
||||
{
|
||||
DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);
|
||||
|
||||
if (map->map >= MAX_WIN) {
|
||||
printk(KERN_ERR "%s(): map (%d) out of range\n",
|
||||
__FUNCTION__, map->map);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*map = pcmcia_socket[sock].mem_map[map->map];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_set_mem_map()
|
||||
*
|
||||
* Implements the set_mem_map() operation for the in-kernel PCMCIA
|
||||
* service (formerly SS_SetMemMap in Card Services). We configure
|
||||
* the map speed as requested, but override the address ranges
|
||||
* supplied by Card Services.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
static int
|
||||
bcm47xx_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *map)
|
||||
{
|
||||
unsigned int speed;
|
||||
unsigned long start;
|
||||
u_long flags;
|
||||
|
||||
if (map->map >= MAX_WIN) {
|
||||
printk(KERN_ERR "%s(): map (%d) out of range\n",
|
||||
__FUNCTION__, map->map);
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG(2, "\tmap %u speed %u\n\tsys_start %#lx\n"
|
||||
"\tsys_stop %#lx\n\tcard_start %#x\n"
|
||||
"\tflags: %s%s%s%s%s%s%s%s\n",
|
||||
map->map, map->speed, map->sys_start, map->sys_stop,
|
||||
map->card_start, (map->flags == 0) ? "<NONE>" : "",
|
||||
(map->flags & MAP_ACTIVE) ? "ACTIVE " : "",
|
||||
(map->flags & MAP_16BIT) ? "16BIT " : "",
|
||||
(map->flags & MAP_AUTOSZ) ? "AUTOSZ " : "",
|
||||
(map->flags & MAP_0WS) ? "0WS " : "",
|
||||
(map->flags & MAP_WRPROT) ? "WRPROT " : "",
|
||||
(map->flags & MAP_ATTRIB) ? "ATTRIB " : "",
|
||||
(map->flags & MAP_USE_WAIT) ? "USE_WAIT " : "");
|
||||
|
||||
if (map->flags & MAP_ACTIVE) {
|
||||
/* When clients issue RequestMap, the access speed is not always
|
||||
* properly configured:
|
||||
*/
|
||||
speed = (map->speed > 0) ? map->speed : BCM47XX_PCMCIA_MEM_SPEED;
|
||||
|
||||
/* TBD */
|
||||
if (map->flags & MAP_ATTRIB) {
|
||||
pcmcia_socket[sock].speed_attr = speed;
|
||||
} else {
|
||||
pcmcia_socket[sock].speed_mem = speed;
|
||||
}
|
||||
}
|
||||
|
||||
save_flags(flags);
|
||||
cli();
|
||||
start = map->sys_start;
|
||||
|
||||
if (map->sys_stop == 0)
|
||||
map->sys_stop = PAGE_SIZE - 1;
|
||||
|
||||
if (map->flags & MAP_ATTRIB) {
|
||||
map->sys_start = pcmcia_socket[sock].phys_attr +
|
||||
map->card_start;
|
||||
} else {
|
||||
map->sys_start = pcmcia_socket[sock].phys_mem +
|
||||
map->card_start;
|
||||
}
|
||||
|
||||
map->sys_stop = map->sys_start + (map->sys_stop - start);
|
||||
pcmcia_socket[sock].mem_map[map->map] = *map;
|
||||
restore_flags(flags);
|
||||
DEBUG(2, "set_mem_map %d start %x stop %x card_start %x\n",
|
||||
map->map, map->sys_start, map->sys_stop,
|
||||
map->card_start);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if defined(CONFIG_PROC_FS)
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_proc_setup()
|
||||
*
|
||||
* Implements the proc_setup() operation for the in-kernel PCMCIA
|
||||
* service (formerly SS_ProcSetup in Card Services).
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
static void
|
||||
bcm47xx_pcmcia_proc_setup(unsigned int sock, struct proc_dir_entry *base)
|
||||
{
|
||||
struct proc_dir_entry *entry;
|
||||
|
||||
if ((entry = create_proc_entry("status", 0, base)) == NULL) {
|
||||
printk(KERN_ERR "Unable to install \"status\" procfs entry\n");
|
||||
return;
|
||||
}
|
||||
|
||||
entry->read_proc = bcm47xx_pcmcia_proc_status;
|
||||
entry->data = (void *)sock;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* bcm47xx_pcmcia_proc_status()
|
||||
*
|
||||
* Implements the /proc/bus/pccard/??/status file.
|
||||
*
|
||||
* Returns: the number of characters added to the buffer
|
||||
*/
|
||||
static int
|
||||
bcm47xx_pcmcia_proc_status(char *buf, char **start, off_t pos,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
char *p = buf;
|
||||
unsigned int sock = (unsigned int)data;
|
||||
|
||||
p += sprintf(p, "k_flags : %s%s%s%s%s%s%s\n",
|
||||
pcmcia_socket[sock].k_state.detect ? "detect " : "",
|
||||
pcmcia_socket[sock].k_state.ready ? "ready " : "",
|
||||
pcmcia_socket[sock].k_state.bvd1 ? "bvd1 " : "",
|
||||
pcmcia_socket[sock].k_state.bvd2 ? "bvd2 " : "",
|
||||
pcmcia_socket[sock].k_state.wrprot ? "wrprot " : "",
|
||||
pcmcia_socket[sock].k_state.vs_3v ? "vs_3v " : "",
|
||||
pcmcia_socket[sock].k_state.vs_Xv ? "vs_Xv " : "");
|
||||
|
||||
p += sprintf(p, "status : %s%s%s%s%s%s%s%s%s\n",
|
||||
pcmcia_socket[sock].k_state.detect ? "SS_DETECT " : "",
|
||||
pcmcia_socket[sock].k_state.ready ? "SS_READY " : "",
|
||||
pcmcia_socket[sock].cs_state.Vcc ? "SS_POWERON " : "",
|
||||
pcmcia_socket[sock].cs_state.flags & SS_IOCARD ? "SS_IOCARD " : "",
|
||||
(pcmcia_socket[sock].cs_state.flags & SS_IOCARD &&
|
||||
pcmcia_socket[sock].k_state.bvd1) ? "SS_STSCHG " : "",
|
||||
((pcmcia_socket[sock].cs_state.flags & SS_IOCARD) == 0 &&
|
||||
(pcmcia_socket[sock].k_state.bvd1 == 0)) ? "SS_BATDEAD " : "",
|
||||
((pcmcia_socket[sock].cs_state.flags & SS_IOCARD) == 0 &&
|
||||
(pcmcia_socket[sock].k_state.bvd2 == 0)) ? "SS_BATWARN " : "",
|
||||
pcmcia_socket[sock].k_state.vs_3v ? "SS_3VCARD " : "",
|
||||
pcmcia_socket[sock].k_state.vs_Xv ? "SS_XVCARD " : "");
|
||||
|
||||
p += sprintf(p, "mask : %s%s%s%s%s\n",
|
||||
pcmcia_socket[sock].cs_state.csc_mask & SS_DETECT ? "SS_DETECT " : "",
|
||||
pcmcia_socket[sock].cs_state.csc_mask & SS_READY ? "SS_READY " : "",
|
||||
pcmcia_socket[sock].cs_state.csc_mask & SS_BATDEAD ? "SS_BATDEAD " : "",
|
||||
pcmcia_socket[sock].cs_state.csc_mask & SS_BATWARN ? "SS_BATWARN " : "",
|
||||
pcmcia_socket[sock].cs_state.csc_mask & SS_STSCHG ? "SS_STSCHG " : "");
|
||||
|
||||
p += sprintf(p, "cs_flags : %s%s%s%s%s\n",
|
||||
pcmcia_socket[sock].cs_state.flags & SS_PWR_AUTO ?
|
||||
"SS_PWR_AUTO " : "",
|
||||
pcmcia_socket[sock].cs_state.flags & SS_IOCARD ?
|
||||
"SS_IOCARD " : "",
|
||||
pcmcia_socket[sock].cs_state.flags & SS_RESET ?
|
||||
"SS_RESET " : "",
|
||||
pcmcia_socket[sock].cs_state.flags & SS_SPKR_ENA ?
|
||||
"SS_SPKR_ENA " : "",
|
||||
pcmcia_socket[sock].cs_state.flags & SS_OUTPUT_ENA ?
|
||||
"SS_OUTPUT_ENA " : "");
|
||||
|
||||
p += sprintf(p, "Vcc : %d\n", pcmcia_socket[sock].cs_state.Vcc);
|
||||
p += sprintf(p, "Vpp : %d\n", pcmcia_socket[sock].cs_state.Vpp);
|
||||
p += sprintf(p, "irq : %d\n", pcmcia_socket[sock].cs_state.io_irq);
|
||||
p += sprintf(p, "I/O : %u\n", pcmcia_socket[sock].speed_io);
|
||||
p += sprintf(p, "attribute: %u\n", pcmcia_socket[sock].speed_attr);
|
||||
p += sprintf(p, "common : %u\n", pcmcia_socket[sock].speed_mem);
|
||||
return p-buf;
|
||||
}
|
||||
|
||||
|
||||
#endif /* defined(CONFIG_PROC_FS) */
|
||||
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* BCM4710 specific pcmcia routines.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/config.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/tqueue.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include <pcmcia/version.h>
|
||||
#include <pcmcia/cs_types.h>
|
||||
#include <pcmcia/cs.h>
|
||||
#include <pcmcia/ss.h>
|
||||
#include <pcmcia/bulkmem.h>
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/bus_ops.h>
|
||||
#include "cs_internal.h"
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
|
||||
#include <typedefs.h>
|
||||
#include <bcmdevs.h>
|
||||
#include <bcm4710.h>
|
||||
#include <sbconfig.h>
|
||||
#include <sbextif.h>
|
||||
|
||||
#include "bcm4710pcmcia.h"
|
||||
|
||||
/* Use a static var for irq dev_id */
|
||||
static int bcm47xx_pcmcia_dev_id;
|
||||
|
||||
/* Do we think we have a card or not? */
|
||||
static int bcm47xx_pcmcia_present = 0;
|
||||
|
||||
|
||||
static void bcm4710_pcmcia_reset(void)
|
||||
{
|
||||
extifregs_t *eir;
|
||||
unsigned long s;
|
||||
uint32 out0, out1, outen;
|
||||
|
||||
|
||||
eir = (extifregs_t *) ioremap_nocache(BCM4710_REG_EXTIF, sizeof(extifregs_t));
|
||||
|
||||
save_and_cli(s);
|
||||
|
||||
/* Use gpio7 to reset the pcmcia slot */
|
||||
outen = readl(&eir->gpio[0].outen);
|
||||
outen |= BCM47XX_PCMCIA_RESET;
|
||||
out0 = readl(&eir->gpio[0].out);
|
||||
out0 &= ~(BCM47XX_PCMCIA_RESET);
|
||||
out1 = out0 | BCM47XX_PCMCIA_RESET;
|
||||
|
||||
writel(out0, &eir->gpio[0].out);
|
||||
writel(outen, &eir->gpio[0].outen);
|
||||
mdelay(1);
|
||||
writel(out1, &eir->gpio[0].out);
|
||||
mdelay(1);
|
||||
writel(out0, &eir->gpio[0].out);
|
||||
|
||||
restore_flags(s);
|
||||
}
|
||||
|
||||
|
||||
static int bcm4710_pcmcia_init(struct pcmcia_init *init)
|
||||
{
|
||||
struct pci_dev *pdev;
|
||||
extifregs_t *eir;
|
||||
uint32 outen, intp, intm, tmp;
|
||||
uint16 *attrsp;
|
||||
int rc = 0, i;
|
||||
extern unsigned long bcm4710_cpu_cycle;
|
||||
|
||||
|
||||
if (!(pdev = pci_find_device(VENDOR_BROADCOM, SB_EXTIF, NULL))) {
|
||||
printk(KERN_ERR "bcm4710_pcmcia: extif not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
eir = (extifregs_t *) ioremap_nocache(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
|
||||
|
||||
/* Initialize the pcmcia i/f: 16bit no swap */
|
||||
writel(CF_EM_PCMCIA | CF_DS | CF_EN, &eir->pcmcia_config);
|
||||
|
||||
#ifdef notYet
|
||||
|
||||
/* Set the timing for memory accesses */
|
||||
tmp = (19 / bcm4710_cpu_cycle) << 24; /* W3 = 10nS */
|
||||
tmp = tmp | ((29 / bcm4710_cpu_cycle) << 16); /* W2 = 20nS */
|
||||
tmp = tmp | ((109 / bcm4710_cpu_cycle) << 8); /* W1 = 100nS */
|
||||
tmp = tmp | (129 / bcm4710_cpu_cycle); /* W0 = 120nS */
|
||||
writel(tmp, &eir->pcmcia_memwait); /* 0x01020a0c for a 100Mhz clock */
|
||||
|
||||
/* Set the timing for I/O accesses */
|
||||
tmp = (19 / bcm4710_cpu_cycle) << 24; /* W3 = 10nS */
|
||||
tmp = tmp | ((29 / bcm4710_cpu_cycle) << 16); /* W2 = 20nS */
|
||||
tmp = tmp | ((109 / bcm4710_cpu_cycle) << 8); /* W1 = 100nS */
|
||||
tmp = tmp | (129 / bcm4710_cpu_cycle); /* W0 = 120nS */
|
||||
writel(tmp, &eir->pcmcia_iowait); /* 0x01020a0c for a 100Mhz clock */
|
||||
|
||||
/* Set the timing for attribute accesses */
|
||||
tmp = (19 / bcm4710_cpu_cycle) << 24; /* W3 = 10nS */
|
||||
tmp = tmp | ((29 / bcm4710_cpu_cycle) << 16); /* W2 = 20nS */
|
||||
tmp = tmp | ((109 / bcm4710_cpu_cycle) << 8); /* W1 = 100nS */
|
||||
tmp = tmp | (129 / bcm4710_cpu_cycle); /* W0 = 120nS */
|
||||
writel(tmp, &eir->pcmcia_attrwait); /* 0x01020a0c for a 100Mhz clock */
|
||||
|
||||
#endif
|
||||
/* Make sure gpio0 and gpio5 are inputs */
|
||||
outen = readl(&eir->gpio[0].outen);
|
||||
outen &= ~(BCM47XX_PCMCIA_WP | BCM47XX_PCMCIA_STSCHG | BCM47XX_PCMCIA_RESET);
|
||||
writel(outen, &eir->gpio[0].outen);
|
||||
|
||||
/* Issue a reset to the pcmcia socket */
|
||||
bcm4710_pcmcia_reset();
|
||||
|
||||
#ifdef DO_BCM47XX_PCMCIA_INTERRUPTS
|
||||
/* Setup gpio5 to be the STSCHG interrupt */
|
||||
intp = readl(&eir->gpiointpolarity);
|
||||
writel(intp | BCM47XX_PCMCIA_STSCHG, &eir->gpiointpolarity); /* Active low */
|
||||
intm = readl(&eir->gpiointmask);
|
||||
writel(intm | BCM47XX_PCMCIA_STSCHG, &eir->gpiointmask); /* Enable it */
|
||||
#endif
|
||||
|
||||
DEBUG(2, "bcm4710_pcmcia after reset:\n");
|
||||
DEBUG(2, "\textstatus\t= 0x%08x:\n", readl(&eir->extstatus));
|
||||
DEBUG(2, "\tpcmcia_config\t= 0x%08x:\n", readl(&eir->pcmcia_config));
|
||||
DEBUG(2, "\tpcmcia_memwait\t= 0x%08x:\n", readl(&eir->pcmcia_memwait));
|
||||
DEBUG(2, "\tpcmcia_attrwait\t= 0x%08x:\n", readl(&eir->pcmcia_attrwait));
|
||||
DEBUG(2, "\tpcmcia_iowait\t= 0x%08x:\n", readl(&eir->pcmcia_iowait));
|
||||
DEBUG(2, "\tgpioin\t\t= 0x%08x:\n", readl(&eir->gpioin));
|
||||
DEBUG(2, "\tgpio_outen0\t= 0x%08x:\n", readl(&eir->gpio[0].outen));
|
||||
DEBUG(2, "\tgpio_out0\t= 0x%08x:\n", readl(&eir->gpio[0].out));
|
||||
DEBUG(2, "\tgpiointpolarity\t= 0x%08x:\n", readl(&eir->gpiointpolarity));
|
||||
DEBUG(2, "\tgpiointmask\t= 0x%08x:\n", readl(&eir->gpiointmask));
|
||||
|
||||
#ifdef DO_BCM47XX_PCMCIA_INTERRUPTS
|
||||
/* Request pcmcia interrupt */
|
||||
rc = request_irq(BCM47XX_PCMCIA_IRQ, init->handler, SA_INTERRUPT,
|
||||
"PCMCIA Interrupt", &bcm47xx_pcmcia_dev_id);
|
||||
#endif
|
||||
|
||||
attrsp = (uint16 *)ioremap_nocache(EXTIF_PCMCIA_CFGBASE(BCM4710_EXTIF), 0x1000);
|
||||
tmp = readw(&attrsp[0]);
|
||||
DEBUG(2, "\tattr[0] = 0x%04x\n", tmp);
|
||||
if ((tmp == 0x7fff) || (tmp == 0x7f00)) {
|
||||
bcm47xx_pcmcia_present = 0;
|
||||
} else {
|
||||
bcm47xx_pcmcia_present = 1;
|
||||
}
|
||||
|
||||
/* There's only one socket */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bcm4710_pcmcia_shutdown(void)
|
||||
{
|
||||
extifregs_t *eir;
|
||||
uint32 intm;
|
||||
|
||||
eir = (extifregs_t *) ioremap_nocache(BCM4710_REG_EXTIF, sizeof(extifregs_t));
|
||||
|
||||
/* Disable the pcmcia i/f */
|
||||
writel(0, &eir->pcmcia_config);
|
||||
|
||||
/* Reset gpio's */
|
||||
intm = readl(&eir->gpiointmask);
|
||||
writel(intm & ~BCM47XX_PCMCIA_STSCHG, &eir->gpiointmask); /* Disable it */
|
||||
|
||||
free_irq(BCM47XX_PCMCIA_IRQ, &bcm47xx_pcmcia_dev_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
bcm4710_pcmcia_socket_state(unsigned sock, struct pcmcia_state *state)
|
||||
{
|
||||
extifregs_t *eir;
|
||||
|
||||
eir = (extifregs_t *) ioremap_nocache(BCM4710_REG_EXTIF, sizeof(extifregs_t));
|
||||
|
||||
|
||||
if (sock != 0) {
|
||||
printk(KERN_ERR "bcm4710 socket_state bad sock %d\n", sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bcm47xx_pcmcia_present) {
|
||||
state->detect = 1;
|
||||
state->ready = 1;
|
||||
state->bvd1 = 1;
|
||||
state->bvd2 = 1;
|
||||
state->wrprot = (readl(&eir->gpioin) & BCM47XX_PCMCIA_WP) == BCM47XX_PCMCIA_WP;
|
||||
state->vs_3v = 0;
|
||||
state->vs_Xv = 0;
|
||||
} else {
|
||||
state->detect = 0;
|
||||
state->ready = 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int bcm4710_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
|
||||
{
|
||||
if (info->sock >= BCM47XX_PCMCIA_MAX_SOCK) return -1;
|
||||
|
||||
info->irq = BCM47XX_PCMCIA_IRQ;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
bcm4710_pcmcia_configure_socket(const struct pcmcia_configure *configure)
|
||||
{
|
||||
if (configure->sock >= BCM47XX_PCMCIA_MAX_SOCK) return -1;
|
||||
|
||||
|
||||
DEBUG(2, "Vcc %dV Vpp %dV output %d speaker %d reset %d\n", configure->vcc,
|
||||
configure->vpp, configure->output, configure->speaker, configure->reset);
|
||||
|
||||
if ((configure->vcc != 50) || (configure->vpp != 50)) {
|
||||
printk("%s: bad Vcc/Vpp (%d:%d)\n", __FUNCTION__, configure->vcc,
|
||||
configure->vpp);
|
||||
}
|
||||
|
||||
if (configure->reset) {
|
||||
/* Issue a reset to the pcmcia socket */
|
||||
DEBUG(1, "%s: Reseting socket\n", __FUNCTION__);
|
||||
bcm4710_pcmcia_reset();
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pcmcia_low_level bcm4710_pcmcia_ops = {
|
||||
bcm4710_pcmcia_init,
|
||||
bcm4710_pcmcia_shutdown,
|
||||
bcm4710_pcmcia_socket_state,
|
||||
bcm4710_pcmcia_get_irq_info,
|
||||
bcm4710_pcmcia_configure_socket
|
||||
};
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
*
|
||||
* bcm47xx pcmcia driver
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Based on sa1100.h and include/asm-arm/arch-sa1100/pcmica.h
|
||||
* from www.handhelds.org,
|
||||
* and au1000_generic.c from oss.sgi.com.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#if !defined(_BCM4710PCMCIA_H)
|
||||
#define _BCM4710PCMCIA_H
|
||||
|
||||
#include <pcmcia/cs_types.h>
|
||||
#include <pcmcia/ss.h>
|
||||
#include <pcmcia/bulkmem.h>
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include "cs_internal.h"
|
||||
|
||||
|
||||
/* The 47xx can only support one socket */
|
||||
#define BCM47XX_PCMCIA_MAX_SOCK 1
|
||||
|
||||
/* In the bcm947xx gpio's are used for some pcmcia functions */
|
||||
#define BCM47XX_PCMCIA_WP 0x01 /* Bit 0 is WP input */
|
||||
#define BCM47XX_PCMCIA_STSCHG 0x20 /* Bit 5 is STSCHG input/interrupt */
|
||||
#define BCM47XX_PCMCIA_RESET 0x80 /* Bit 7 is RESET */
|
||||
|
||||
#define BCM47XX_PCMCIA_IRQ 2
|
||||
|
||||
/* The socket driver actually works nicely in interrupt-driven form,
|
||||
* so the (relatively infrequent) polling is "just to be sure."
|
||||
*/
|
||||
#define BCM47XX_PCMCIA_POLL_PERIOD (2 * HZ)
|
||||
|
||||
#define BCM47XX_PCMCIA_IO_SPEED (255)
|
||||
#define BCM47XX_PCMCIA_MEM_SPEED (300)
|
||||
|
||||
|
||||
struct pcmcia_state {
|
||||
unsigned detect: 1,
|
||||
ready: 1,
|
||||
bvd1: 1,
|
||||
bvd2: 1,
|
||||
wrprot: 1,
|
||||
vs_3v: 1,
|
||||
vs_Xv: 1;
|
||||
};
|
||||
|
||||
|
||||
struct pcmcia_configure {
|
||||
unsigned sock: 8,
|
||||
vcc: 8,
|
||||
vpp: 8,
|
||||
output: 1,
|
||||
speaker: 1,
|
||||
reset: 1;
|
||||
};
|
||||
|
||||
struct pcmcia_irq_info {
|
||||
unsigned int sock;
|
||||
unsigned int irq;
|
||||
};
|
||||
|
||||
/* This structure encapsulates per-socket state which we might need to
|
||||
* use when responding to a Card Services query of some kind.
|
||||
*/
|
||||
struct bcm47xx_pcmcia_socket {
|
||||
socket_state_t cs_state;
|
||||
struct pcmcia_state k_state;
|
||||
unsigned int irq;
|
||||
void (*handler)(void *, unsigned int);
|
||||
void *handler_info;
|
||||
pccard_io_map io_map[MAX_IO_WIN];
|
||||
pccard_mem_map mem_map[MAX_WIN];
|
||||
ioaddr_t virt_io, phys_attr, phys_mem;
|
||||
unsigned short speed_io, speed_attr, speed_mem;
|
||||
};
|
||||
|
||||
struct pcmcia_init {
|
||||
void (*handler)(int irq, void *dev, struct pt_regs *regs);
|
||||
};
|
||||
|
||||
struct pcmcia_low_level {
|
||||
int (*init)(struct pcmcia_init *);
|
||||
int (*shutdown)(void);
|
||||
int (*socket_state)(unsigned sock, struct pcmcia_state *);
|
||||
int (*get_irq_info)(struct pcmcia_irq_info *);
|
||||
int (*configure_socket)(const struct pcmcia_configure *);
|
||||
};
|
||||
|
||||
extern struct pcmcia_low_level bcm47xx_pcmcia_ops;
|
||||
|
||||
/* I/O pins replacing memory pins
|
||||
* (PCMCIA System Architecture, 2nd ed., by Don Anderson, p.75)
|
||||
*
|
||||
* These signals change meaning when going from memory-only to
|
||||
* memory-or-I/O interface:
|
||||
*/
|
||||
#define iostschg bvd1
|
||||
#define iospkr bvd2
|
||||
|
||||
|
||||
/*
|
||||
* Declaration for implementation specific low_level operations.
|
||||
*/
|
||||
extern struct pcmcia_low_level bcm4710_pcmcia_ops;
|
||||
|
||||
#endif /* !defined(_BCM4710PCMCIA_H) */
|
||||
Reference in New Issue
Block a user