1
0
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:
wbx
2005-03-16 13:50:00 +00:00
parent cfb6561e9b
commit 2cea1e6b9a
85 changed files with 18591 additions and 16 deletions

View File

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

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

View File

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

View File

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

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

View 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

View File

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

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

View 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);
}

View 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

View 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));
}

View 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 */

File diff suppressed because it is too large Load Diff

View File

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

View 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

View File

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

View File

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

View File

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