06a5128a29
When the kernel is running in secure boot mode, we lock down the kernel to prevent userspace from modifying the running kernel image. Whilst this includes prohibiting access to things like /dev/mem, it must also prevent access by means of configuring driver modules in such a way as to cause a device to access or modify the kernel image. To this end, annotate module_param* statements that refer to hardware configuration and indicate for future reference what type of parameter they specify. The parameter parser in the core sees this information and can skip such parameters with an error message if the kernel is locked down. The module initialisation then runs as normal, but just sees whatever the default values for those parameters is. Note that we do still need to do the module initialisation because some drivers have viable defaults set in case parameters aren't specified and some drivers support automatic configuration (e.g. PNP or PCI) in addition to manually coded parameters. This patch annotates drivers in drivers/net/arcnet/. Suggested-by: Alan Cox <gnomes@lxorguk.ukuu.org.uk> Signed-off-by: David Howells <dhowells@redhat.com> cc: Michael Grzeschik <m.grzeschik@pengutronix.de> cc: netdev@vger.kernel.org
712 lines
19 KiB
C
712 lines
19 KiB
C
/*
|
|
* Linux ARCnet driver - COM90xx chipset (memory-mapped buffers)
|
|
*
|
|
* Written 1994-1999 by Avery Pennarun.
|
|
* Written 1999 by Martin Mares <mj@ucw.cz>.
|
|
* Derived from skeleton.c by Donald Becker.
|
|
*
|
|
* Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
|
|
* for sponsoring the further development of this driver.
|
|
*
|
|
* **********************
|
|
*
|
|
* The original copyright of skeleton.c was as follows:
|
|
*
|
|
* skeleton.c Written 1993 by Donald Becker.
|
|
* Copyright 1993 United States Government as represented by the
|
|
* Director, National Security Agency. This software may only be used
|
|
* and distributed according to the terms of the GNU General Public License as
|
|
* modified by SRC, incorporated herein by reference.
|
|
*
|
|
* **********************
|
|
*
|
|
* For more details, see drivers/net/arcnet.c
|
|
*
|
|
* **********************
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/init.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/io.h>
|
|
|
|
#include "arcdevice.h"
|
|
#include "com9026.h"
|
|
|
|
/* Define this to speed up the autoprobe by assuming if only one io port and
|
|
* shmem are left in the list at Stage 5, they must correspond to each
|
|
* other.
|
|
*
|
|
* This is undefined by default because it might not always be true, and the
|
|
* extra check makes the autoprobe even more careful. Speed demons can turn
|
|
* it on - I think it should be fine if you only have one ARCnet card
|
|
* installed.
|
|
*
|
|
* If no ARCnet cards are installed, this delay never happens anyway and thus
|
|
* the option has no effect.
|
|
*/
|
|
#undef FAST_PROBE
|
|
|
|
/* Internal function declarations */
|
|
static int com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem *);
|
|
static void com90xx_command(struct net_device *dev, int command);
|
|
static int com90xx_status(struct net_device *dev);
|
|
static void com90xx_setmask(struct net_device *dev, int mask);
|
|
static int com90xx_reset(struct net_device *dev, int really_reset);
|
|
static void com90xx_copy_to_card(struct net_device *dev, int bufnum, int offset,
|
|
void *buf, int count);
|
|
static void com90xx_copy_from_card(struct net_device *dev, int bufnum,
|
|
int offset, void *buf, int count);
|
|
|
|
/* Known ARCnet cards */
|
|
|
|
static struct net_device *cards[16];
|
|
static int numcards;
|
|
|
|
/* Handy defines for ARCnet specific stuff */
|
|
|
|
/* The number of low I/O ports used by the card */
|
|
#define ARCNET_TOTAL_SIZE 16
|
|
|
|
/* Amount of I/O memory used by the card */
|
|
#define BUFFER_SIZE (512)
|
|
#define MIRROR_SIZE (BUFFER_SIZE * 4)
|
|
|
|
static int com90xx_skip_probe __initdata = 0;
|
|
|
|
/* Module parameters */
|
|
|
|
static int io; /* use the insmod io= irq= shmem= options */
|
|
static int irq;
|
|
static int shmem;
|
|
static char device[9]; /* use eg. device=arc1 to change name */
|
|
|
|
module_param_hw(io, int, ioport, 0);
|
|
module_param_hw(irq, int, irq, 0);
|
|
module_param(shmem, int, 0);
|
|
module_param_string(device, device, sizeof(device), 0);
|
|
|
|
static void __init com90xx_probe(void)
|
|
{
|
|
int count, status, ioaddr, numprint, airq, openparen = 0;
|
|
unsigned long airqmask;
|
|
int ports[(0x3f0 - 0x200) / 16 + 1] = { 0 };
|
|
unsigned long *shmems;
|
|
void __iomem **iomem;
|
|
int numports, numshmems, *port;
|
|
u_long *p;
|
|
int index;
|
|
|
|
if (!io && !irq && !shmem && !*device && com90xx_skip_probe)
|
|
return;
|
|
|
|
shmems = kzalloc(((0x100000 - 0xa0000) / 0x800) * sizeof(unsigned long),
|
|
GFP_KERNEL);
|
|
if (!shmems)
|
|
return;
|
|
iomem = kzalloc(((0x100000 - 0xa0000) / 0x800) * sizeof(void __iomem *),
|
|
GFP_KERNEL);
|
|
if (!iomem) {
|
|
kfree(shmems);
|
|
return;
|
|
}
|
|
|
|
if (BUGLVL(D_NORMAL))
|
|
pr_info("%s\n", "COM90xx chipset support");
|
|
|
|
/* set up the arrays where we'll store the possible probe addresses */
|
|
numports = numshmems = 0;
|
|
if (io)
|
|
ports[numports++] = io;
|
|
else
|
|
for (count = 0x200; count <= 0x3f0; count += 16)
|
|
ports[numports++] = count;
|
|
if (shmem)
|
|
shmems[numshmems++] = shmem;
|
|
else
|
|
for (count = 0xA0000; count <= 0xFF800; count += 2048)
|
|
shmems[numshmems++] = count;
|
|
|
|
/* Stage 1: abandon any reserved ports, or ones with status==0xFF
|
|
* (empty), and reset any others by reading the reset port.
|
|
*/
|
|
numprint = -1;
|
|
for (port = &ports[0]; port - ports < numports; port++) {
|
|
numprint++;
|
|
numprint %= 8;
|
|
if (!numprint) {
|
|
arc_cont(D_INIT, "\n");
|
|
arc_cont(D_INIT, "S1: ");
|
|
}
|
|
arc_cont(D_INIT, "%Xh ", *port);
|
|
|
|
ioaddr = *port;
|
|
|
|
if (!request_region(*port, ARCNET_TOTAL_SIZE,
|
|
"arcnet (90xx)")) {
|
|
arc_cont(D_INIT_REASONS, "(request_region)\n");
|
|
arc_cont(D_INIT_REASONS, "S1: ");
|
|
if (BUGLVL(D_INIT_REASONS))
|
|
numprint = 0;
|
|
*port-- = ports[--numports];
|
|
continue;
|
|
}
|
|
if (arcnet_inb(ioaddr, COM9026_REG_R_STATUS) == 0xFF) {
|
|
arc_cont(D_INIT_REASONS, "(empty)\n");
|
|
arc_cont(D_INIT_REASONS, "S1: ");
|
|
if (BUGLVL(D_INIT_REASONS))
|
|
numprint = 0;
|
|
release_region(*port, ARCNET_TOTAL_SIZE);
|
|
*port-- = ports[--numports];
|
|
continue;
|
|
}
|
|
/* begin resetting card */
|
|
arcnet_inb(ioaddr, COM9026_REG_R_RESET);
|
|
|
|
arc_cont(D_INIT_REASONS, "\n");
|
|
arc_cont(D_INIT_REASONS, "S1: ");
|
|
if (BUGLVL(D_INIT_REASONS))
|
|
numprint = 0;
|
|
}
|
|
arc_cont(D_INIT, "\n");
|
|
|
|
if (!numports) {
|
|
arc_cont(D_NORMAL, "S1: No ARCnet cards found.\n");
|
|
kfree(shmems);
|
|
kfree(iomem);
|
|
return;
|
|
}
|
|
/* Stage 2: we have now reset any possible ARCnet cards, so we can't
|
|
* do anything until they finish. If D_INIT, print the list of
|
|
* cards that are left.
|
|
*/
|
|
numprint = -1;
|
|
for (port = &ports[0]; port < ports + numports; port++) {
|
|
numprint++;
|
|
numprint %= 8;
|
|
if (!numprint) {
|
|
arc_cont(D_INIT, "\n");
|
|
arc_cont(D_INIT, "S2: ");
|
|
}
|
|
arc_cont(D_INIT, "%Xh ", *port);
|
|
}
|
|
arc_cont(D_INIT, "\n");
|
|
mdelay(RESETtime);
|
|
|
|
/* Stage 3: abandon any shmem addresses that don't have the signature
|
|
* 0xD1 byte in the right place, or are read-only.
|
|
*/
|
|
numprint = -1;
|
|
for (index = 0, p = &shmems[0]; index < numshmems; p++, index++) {
|
|
void __iomem *base;
|
|
|
|
numprint++;
|
|
numprint %= 8;
|
|
if (!numprint) {
|
|
arc_cont(D_INIT, "\n");
|
|
arc_cont(D_INIT, "S3: ");
|
|
}
|
|
arc_cont(D_INIT, "%lXh ", *p);
|
|
|
|
if (!request_mem_region(*p, MIRROR_SIZE, "arcnet (90xx)")) {
|
|
arc_cont(D_INIT_REASONS, "(request_mem_region)\n");
|
|
arc_cont(D_INIT_REASONS, "Stage 3: ");
|
|
if (BUGLVL(D_INIT_REASONS))
|
|
numprint = 0;
|
|
goto out;
|
|
}
|
|
base = ioremap(*p, MIRROR_SIZE);
|
|
if (!base) {
|
|
arc_cont(D_INIT_REASONS, "(ioremap)\n");
|
|
arc_cont(D_INIT_REASONS, "Stage 3: ");
|
|
if (BUGLVL(D_INIT_REASONS))
|
|
numprint = 0;
|
|
goto out1;
|
|
}
|
|
if (arcnet_readb(base, COM9026_REG_R_STATUS) != TESTvalue) {
|
|
arc_cont(D_INIT_REASONS, "(%02Xh != %02Xh)\n",
|
|
arcnet_readb(base, COM9026_REG_R_STATUS),
|
|
TESTvalue);
|
|
arc_cont(D_INIT_REASONS, "S3: ");
|
|
if (BUGLVL(D_INIT_REASONS))
|
|
numprint = 0;
|
|
goto out2;
|
|
}
|
|
/* By writing 0x42 to the TESTvalue location, we also make
|
|
* sure no "mirror" shmem areas show up - if they occur
|
|
* in another pass through this loop, they will be discarded
|
|
* because *cptr != TESTvalue.
|
|
*/
|
|
arcnet_writeb(0x42, base, COM9026_REG_W_INTMASK);
|
|
if (arcnet_readb(base, COM9026_REG_R_STATUS) != 0x42) {
|
|
arc_cont(D_INIT_REASONS, "(read only)\n");
|
|
arc_cont(D_INIT_REASONS, "S3: ");
|
|
goto out2;
|
|
}
|
|
arc_cont(D_INIT_REASONS, "\n");
|
|
arc_cont(D_INIT_REASONS, "S3: ");
|
|
if (BUGLVL(D_INIT_REASONS))
|
|
numprint = 0;
|
|
iomem[index] = base;
|
|
continue;
|
|
out2:
|
|
iounmap(base);
|
|
out1:
|
|
release_mem_region(*p, MIRROR_SIZE);
|
|
out:
|
|
*p-- = shmems[--numshmems];
|
|
index--;
|
|
}
|
|
arc_cont(D_INIT, "\n");
|
|
|
|
if (!numshmems) {
|
|
arc_cont(D_NORMAL, "S3: No ARCnet cards found.\n");
|
|
for (port = &ports[0]; port < ports + numports; port++)
|
|
release_region(*port, ARCNET_TOTAL_SIZE);
|
|
kfree(shmems);
|
|
kfree(iomem);
|
|
return;
|
|
}
|
|
/* Stage 4: something of a dummy, to report the shmems that are
|
|
* still possible after stage 3.
|
|
*/
|
|
numprint = -1;
|
|
for (p = &shmems[0]; p < shmems + numshmems; p++) {
|
|
numprint++;
|
|
numprint %= 8;
|
|
if (!numprint) {
|
|
arc_cont(D_INIT, "\n");
|
|
arc_cont(D_INIT, "S4: ");
|
|
}
|
|
arc_cont(D_INIT, "%lXh ", *p);
|
|
}
|
|
arc_cont(D_INIT, "\n");
|
|
|
|
/* Stage 5: for any ports that have the correct status, can disable
|
|
* the RESET flag, and (if no irq is given) generate an autoirq,
|
|
* register an ARCnet device.
|
|
*
|
|
* Currently, we can only register one device per probe, so quit
|
|
* after the first one is found.
|
|
*/
|
|
numprint = -1;
|
|
for (port = &ports[0]; port < ports + numports; port++) {
|
|
int found = 0;
|
|
|
|
numprint++;
|
|
numprint %= 8;
|
|
if (!numprint) {
|
|
arc_cont(D_INIT, "\n");
|
|
arc_cont(D_INIT, "S5: ");
|
|
}
|
|
arc_cont(D_INIT, "%Xh ", *port);
|
|
|
|
ioaddr = *port;
|
|
status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
|
|
|
|
if ((status & 0x9D)
|
|
!= (NORXflag | RECONflag | TXFREEflag | RESETflag)) {
|
|
arc_cont(D_INIT_REASONS, "(status=%Xh)\n", status);
|
|
arc_cont(D_INIT_REASONS, "S5: ");
|
|
if (BUGLVL(D_INIT_REASONS))
|
|
numprint = 0;
|
|
release_region(*port, ARCNET_TOTAL_SIZE);
|
|
*port-- = ports[--numports];
|
|
continue;
|
|
}
|
|
arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,
|
|
ioaddr, COM9026_REG_W_COMMAND);
|
|
status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
|
|
if (status & RESETflag) {
|
|
arc_cont(D_INIT_REASONS, " (eternal reset, status=%Xh)\n",
|
|
status);
|
|
arc_cont(D_INIT_REASONS, "S5: ");
|
|
if (BUGLVL(D_INIT_REASONS))
|
|
numprint = 0;
|
|
release_region(*port, ARCNET_TOTAL_SIZE);
|
|
*port-- = ports[--numports];
|
|
continue;
|
|
}
|
|
/* skip this completely if an IRQ was given, because maybe
|
|
* we're on a machine that locks during autoirq!
|
|
*/
|
|
if (!irq) {
|
|
/* if we do this, we're sure to get an IRQ since the
|
|
* card has just reset and the NORXflag is on until
|
|
* we tell it to start receiving.
|
|
*/
|
|
airqmask = probe_irq_on();
|
|
arcnet_outb(NORXflag, ioaddr, COM9026_REG_W_INTMASK);
|
|
udelay(1);
|
|
arcnet_outb(0, ioaddr, COM9026_REG_W_INTMASK);
|
|
airq = probe_irq_off(airqmask);
|
|
|
|
if (airq <= 0) {
|
|
arc_cont(D_INIT_REASONS, "(airq=%d)\n", airq);
|
|
arc_cont(D_INIT_REASONS, "S5: ");
|
|
if (BUGLVL(D_INIT_REASONS))
|
|
numprint = 0;
|
|
release_region(*port, ARCNET_TOTAL_SIZE);
|
|
*port-- = ports[--numports];
|
|
continue;
|
|
}
|
|
} else {
|
|
airq = irq;
|
|
}
|
|
|
|
arc_cont(D_INIT, "(%d,", airq);
|
|
openparen = 1;
|
|
|
|
/* Everything seems okay. But which shmem, if any, puts
|
|
* back its signature byte when the card is reset?
|
|
*
|
|
* If there are multiple cards installed, there might be
|
|
* multiple shmems still in the list.
|
|
*/
|
|
#ifdef FAST_PROBE
|
|
if (numports > 1 || numshmems > 1) {
|
|
arcnet_inb(ioaddr, COM9026_REG_R_RESET);
|
|
mdelay(RESETtime);
|
|
} else {
|
|
/* just one shmem and port, assume they match */
|
|
arcnet_writeb(TESTvalue, iomem[0],
|
|
COM9026_REG_W_INTMASK);
|
|
}
|
|
#else
|
|
arcnet_inb(ioaddr, COM9026_REG_R_RESET);
|
|
mdelay(RESETtime);
|
|
#endif
|
|
|
|
for (index = 0; index < numshmems; index++) {
|
|
u_long ptr = shmems[index];
|
|
void __iomem *base = iomem[index];
|
|
|
|
if (arcnet_readb(base, COM9026_REG_R_STATUS) == TESTvalue) { /* found one */
|
|
arc_cont(D_INIT, "%lXh)\n", *p);
|
|
openparen = 0;
|
|
|
|
/* register the card */
|
|
if (com90xx_found(*port, airq, ptr, base) == 0)
|
|
found = 1;
|
|
numprint = -1;
|
|
|
|
/* remove shmem from the list */
|
|
shmems[index] = shmems[--numshmems];
|
|
iomem[index] = iomem[numshmems];
|
|
break; /* go to the next I/O port */
|
|
} else {
|
|
arc_cont(D_INIT_REASONS, "%Xh-",
|
|
arcnet_readb(base, COM9026_REG_R_STATUS));
|
|
}
|
|
}
|
|
|
|
if (openparen) {
|
|
if (BUGLVL(D_INIT))
|
|
pr_cont("no matching shmem)\n");
|
|
if (BUGLVL(D_INIT_REASONS)) {
|
|
pr_cont("S5: ");
|
|
numprint = 0;
|
|
}
|
|
}
|
|
if (!found)
|
|
release_region(*port, ARCNET_TOTAL_SIZE);
|
|
*port-- = ports[--numports];
|
|
}
|
|
|
|
if (BUGLVL(D_INIT_REASONS))
|
|
pr_cont("\n");
|
|
|
|
/* Now put back TESTvalue on all leftover shmems. */
|
|
for (index = 0; index < numshmems; index++) {
|
|
arcnet_writeb(TESTvalue, iomem[index], COM9026_REG_W_INTMASK);
|
|
iounmap(iomem[index]);
|
|
release_mem_region(shmems[index], MIRROR_SIZE);
|
|
}
|
|
kfree(shmems);
|
|
kfree(iomem);
|
|
}
|
|
|
|
static int __init check_mirror(unsigned long addr, size_t size)
|
|
{
|
|
void __iomem *p;
|
|
int res = -1;
|
|
|
|
if (!request_mem_region(addr, size, "arcnet (90xx)"))
|
|
return -1;
|
|
|
|
p = ioremap(addr, size);
|
|
if (p) {
|
|
if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue)
|
|
res = 1;
|
|
else
|
|
res = 0;
|
|
iounmap(p);
|
|
}
|
|
|
|
release_mem_region(addr, size);
|
|
return res;
|
|
}
|
|
|
|
/* Set up the struct net_device associated with this card. Called after
|
|
* probing succeeds.
|
|
*/
|
|
static int __init com90xx_found(int ioaddr, int airq, u_long shmem,
|
|
void __iomem *p)
|
|
{
|
|
struct net_device *dev = NULL;
|
|
struct arcnet_local *lp;
|
|
u_long first_mirror, last_mirror;
|
|
int mirror_size;
|
|
|
|
/* allocate struct net_device */
|
|
dev = alloc_arcdev(device);
|
|
if (!dev) {
|
|
arc_cont(D_NORMAL, "com90xx: Can't allocate device!\n");
|
|
iounmap(p);
|
|
release_mem_region(shmem, MIRROR_SIZE);
|
|
return -ENOMEM;
|
|
}
|
|
lp = netdev_priv(dev);
|
|
/* find the real shared memory start/end points, including mirrors */
|
|
|
|
/* guess the actual size of one "memory mirror" - the number of
|
|
* bytes between copies of the shared memory. On most cards, it's
|
|
* 2k (or there are no mirrors at all) but on some, it's 4k.
|
|
*/
|
|
mirror_size = MIRROR_SIZE;
|
|
if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue &&
|
|
check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 &&
|
|
check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1)
|
|
mirror_size = 2 * MIRROR_SIZE;
|
|
|
|
first_mirror = shmem - mirror_size;
|
|
while (check_mirror(first_mirror, mirror_size) == 1)
|
|
first_mirror -= mirror_size;
|
|
first_mirror += mirror_size;
|
|
|
|
last_mirror = shmem + mirror_size;
|
|
while (check_mirror(last_mirror, mirror_size) == 1)
|
|
last_mirror += mirror_size;
|
|
last_mirror -= mirror_size;
|
|
|
|
dev->mem_start = first_mirror;
|
|
dev->mem_end = last_mirror + MIRROR_SIZE - 1;
|
|
|
|
iounmap(p);
|
|
release_mem_region(shmem, MIRROR_SIZE);
|
|
|
|
if (!request_mem_region(dev->mem_start,
|
|
dev->mem_end - dev->mem_start + 1,
|
|
"arcnet (90xx)"))
|
|
goto err_free_dev;
|
|
|
|
/* reserve the irq */
|
|
if (request_irq(airq, arcnet_interrupt, 0, "arcnet (90xx)", dev)) {
|
|
arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", airq);
|
|
goto err_release_mem;
|
|
}
|
|
dev->irq = airq;
|
|
|
|
/* Initialize the rest of the device structure. */
|
|
lp->card_name = "COM90xx";
|
|
lp->hw.command = com90xx_command;
|
|
lp->hw.status = com90xx_status;
|
|
lp->hw.intmask = com90xx_setmask;
|
|
lp->hw.reset = com90xx_reset;
|
|
lp->hw.owner = THIS_MODULE;
|
|
lp->hw.copy_to_card = com90xx_copy_to_card;
|
|
lp->hw.copy_from_card = com90xx_copy_from_card;
|
|
lp->mem_start = ioremap(dev->mem_start,
|
|
dev->mem_end - dev->mem_start + 1);
|
|
if (!lp->mem_start) {
|
|
arc_printk(D_NORMAL, dev, "Can't remap device memory!\n");
|
|
goto err_free_irq;
|
|
}
|
|
|
|
/* get and check the station ID from offset 1 in shmem */
|
|
dev->dev_addr[0] = arcnet_readb(lp->mem_start, COM9026_REG_R_STATION);
|
|
|
|
dev->base_addr = ioaddr;
|
|
|
|
arc_printk(D_NORMAL, dev, "COM90xx station %02Xh found at %03lXh, IRQ %d, ShMem %lXh (%ld*%xh).\n",
|
|
dev->dev_addr[0],
|
|
dev->base_addr, dev->irq, dev->mem_start,
|
|
(dev->mem_end - dev->mem_start + 1) / mirror_size,
|
|
mirror_size);
|
|
|
|
if (register_netdev(dev))
|
|
goto err_unmap;
|
|
|
|
cards[numcards++] = dev;
|
|
return 0;
|
|
|
|
err_unmap:
|
|
iounmap(lp->mem_start);
|
|
err_free_irq:
|
|
free_irq(dev->irq, dev);
|
|
err_release_mem:
|
|
release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1);
|
|
err_free_dev:
|
|
free_netdev(dev);
|
|
return -EIO;
|
|
}
|
|
|
|
static void com90xx_command(struct net_device *dev, int cmd)
|
|
{
|
|
short ioaddr = dev->base_addr;
|
|
|
|
arcnet_outb(cmd, ioaddr, COM9026_REG_W_COMMAND);
|
|
}
|
|
|
|
static int com90xx_status(struct net_device *dev)
|
|
{
|
|
short ioaddr = dev->base_addr;
|
|
|
|
return arcnet_inb(ioaddr, COM9026_REG_R_STATUS);
|
|
}
|
|
|
|
static void com90xx_setmask(struct net_device *dev, int mask)
|
|
{
|
|
short ioaddr = dev->base_addr;
|
|
|
|
arcnet_outb(mask, ioaddr, COM9026_REG_W_INTMASK);
|
|
}
|
|
|
|
/* Do a hardware reset on the card, and set up necessary registers.
|
|
*
|
|
* This should be called as little as possible, because it disrupts the
|
|
* token on the network (causes a RECON) and requires a significant delay.
|
|
*
|
|
* However, it does make sure the card is in a defined state.
|
|
*/
|
|
static int com90xx_reset(struct net_device *dev, int really_reset)
|
|
{
|
|
struct arcnet_local *lp = netdev_priv(dev);
|
|
short ioaddr = dev->base_addr;
|
|
|
|
arc_printk(D_INIT, dev, "Resetting (status=%02Xh)\n",
|
|
arcnet_inb(ioaddr, COM9026_REG_R_STATUS));
|
|
|
|
if (really_reset) {
|
|
/* reset the card */
|
|
arcnet_inb(ioaddr, COM9026_REG_R_RESET);
|
|
mdelay(RESETtime);
|
|
}
|
|
/* clear flags & end reset */
|
|
arcnet_outb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND);
|
|
arcnet_outb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND);
|
|
|
|
#if 0
|
|
/* don't do this until we verify that it doesn't hurt older cards! */
|
|
arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) | ENABLE16flag,
|
|
ioaddr, COM9026_REG_RW_CONFIG);
|
|
#endif
|
|
|
|
/* verify that the ARCnet signature byte is present */
|
|
if (arcnet_readb(lp->mem_start, COM9026_REG_R_STATUS) != TESTvalue) {
|
|
if (really_reset)
|
|
arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n");
|
|
return 1;
|
|
}
|
|
/* enable extended (512-byte) packets */
|
|
arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND);
|
|
|
|
/* clean out all the memory to make debugging make more sense :) */
|
|
if (BUGLVL(D_DURING))
|
|
memset_io(lp->mem_start, 0x42, 2048);
|
|
|
|
/* done! return success. */
|
|
return 0;
|
|
}
|
|
|
|
static void com90xx_copy_to_card(struct net_device *dev, int bufnum,
|
|
int offset, void *buf, int count)
|
|
{
|
|
struct arcnet_local *lp = netdev_priv(dev);
|
|
void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
|
|
|
|
TIME(dev, "memcpy_toio", count, memcpy_toio(memaddr, buf, count));
|
|
}
|
|
|
|
static void com90xx_copy_from_card(struct net_device *dev, int bufnum,
|
|
int offset, void *buf, int count)
|
|
{
|
|
struct arcnet_local *lp = netdev_priv(dev);
|
|
void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
|
|
|
|
TIME(dev, "memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
|
|
}
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
static int __init com90xx_init(void)
|
|
{
|
|
if (irq == 2)
|
|
irq = 9;
|
|
com90xx_probe();
|
|
if (!numcards)
|
|
return -EIO;
|
|
return 0;
|
|
}
|
|
|
|
static void __exit com90xx_exit(void)
|
|
{
|
|
struct net_device *dev;
|
|
struct arcnet_local *lp;
|
|
int count;
|
|
|
|
for (count = 0; count < numcards; count++) {
|
|
dev = cards[count];
|
|
lp = netdev_priv(dev);
|
|
|
|
unregister_netdev(dev);
|
|
free_irq(dev->irq, dev);
|
|
iounmap(lp->mem_start);
|
|
release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
|
|
release_mem_region(dev->mem_start,
|
|
dev->mem_end - dev->mem_start + 1);
|
|
free_netdev(dev);
|
|
}
|
|
}
|
|
|
|
module_init(com90xx_init);
|
|
module_exit(com90xx_exit);
|
|
|
|
#ifndef MODULE
|
|
static int __init com90xx_setup(char *s)
|
|
{
|
|
int ints[8];
|
|
|
|
s = get_options(s, 8, ints);
|
|
if (!ints[0] && !*s) {
|
|
pr_notice("Disabled\n");
|
|
return 1;
|
|
}
|
|
|
|
switch (ints[0]) {
|
|
default: /* ERROR */
|
|
pr_err("Too many arguments\n");
|
|
case 3: /* Mem address */
|
|
shmem = ints[3];
|
|
case 2: /* IRQ */
|
|
irq = ints[2];
|
|
case 1: /* IO address */
|
|
io = ints[1];
|
|
}
|
|
|
|
if (*s)
|
|
snprintf(device, sizeof(device), "%s", s);
|
|
|
|
return 1;
|
|
}
|
|
|
|
__setup("com90xx=", com90xx_setup);
|
|
#endif
|