diff options
-rw-r--r-- | drivers/net/irda/nsc-ircc.c | 185 |
1 files changed, 151 insertions, 34 deletions
diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c index ee717d0e939..282414ba22f 100644 --- a/drivers/net/irda/nsc-ircc.c +++ b/drivers/net/irda/nsc-ircc.c @@ -12,6 +12,7 @@ * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no> * Copyright (c) 1998 Lichen Wang, <lwang@actisys.com> * Copyright (c) 1998 Actisys Corp., www.actisys.com + * Copyright (c) 2000-2004 Jean Tourrilhes <jt@hpl.hp.com> * All Rights Reserved * * This program is free software; you can redistribute it and/or @@ -53,6 +54,7 @@ #include <linux/init.h> #include <linux/rtnetlink.h> #include <linux/dma-mapping.h> +#include <linux/pnp.h> #include <asm/io.h> #include <asm/dma.h> @@ -78,8 +80,8 @@ static int dongle_id; /* Use BIOS settions by default, but user may supply module parameters */ static unsigned int io[] = { ~0, ~0, ~0, ~0 }; -static unsigned int irq[] = { 0, 0, 0, 0, 0 }; -static unsigned int dma[] = { 0, 0, 0, 0, 0 }; +static unsigned int irq[] = { 0, 0, 0, 0 }; +static unsigned int dma[] = { 0, 0, 0, 0 }; static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info); static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info); @@ -87,6 +89,7 @@ static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info); static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info); static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info); static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *id); /* These are the known NSC chips */ static nsc_chip_t chips[] = { @@ -104,7 +107,6 @@ static nsc_chip_t chips[] = { { NULL } }; -/* Max 4 instances for now */ static struct nsc_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL }; static char *dongle_types[] = { @@ -126,8 +128,24 @@ static char *dongle_types[] = { "No dongle connected", }; +/* PNP probing */ +static chipio_t pnp_info; +static const struct pnp_device_id nsc_ircc_pnp_table[] = { + { .id = "NSC6001", .driver_data = 0 }, + { .id = "IBM0071", .driver_data = 0 }, + { } +}; + +MODULE_DEVICE_TABLE(pnp, nsc_ircc_pnp_table); + +static struct pnp_driver nsc_ircc_pnp_driver = { + .name = "nsc-ircc", + .id_table = nsc_ircc_pnp_table, + .probe = nsc_ircc_pnp_probe, +}; + /* Some prototypes */ -static int nsc_ircc_open(int i, chipio_t *info); +static int nsc_ircc_open(chipio_t *info); static int nsc_ircc_close(struct nsc_ircc_cb *self); static int nsc_ircc_setup(chipio_t *info); static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self); @@ -148,6 +166,10 @@ static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev); static int nsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data); +/* Globals */ +static int pnp_registered; +static int pnp_succeeded; + /* * Function nsc_ircc_init () * @@ -158,28 +180,30 @@ static int __init nsc_ircc_init(void) { chipio_t info; nsc_chip_t *chip; - int ret = -ENODEV; + int ret; int cfg_base; int cfg, id; int reg; int i = 0; + /* Register with PnP subsystem to detect disable ports */ + ret = pnp_register_driver(&nsc_ircc_pnp_driver); + + if (ret >= 0) + pnp_registered = 1; + + ret = -ENODEV; + /* Probe for all the NSC chipsets we know about */ - for (chip=chips; chip->name ; chip++) { + for (chip = chips; chip->name ; chip++) { IRDA_DEBUG(2, "%s(), Probing for %s ...\n", __FUNCTION__, chip->name); /* Try all config registers for this chip */ - for (cfg=0; cfg<3; cfg++) { + for (cfg = 0; cfg < ARRAY_SIZE(chip->cfg); cfg++) { cfg_base = chip->cfg[cfg]; if (!cfg_base) continue; - - memset(&info, 0, sizeof(chipio_t)); - info.cfg_base = cfg_base; - info.fir_base = io[i]; - info.dma = dma[i]; - info.irq = irq[i]; /* Read index register */ reg = inb(cfg_base); @@ -194,24 +218,64 @@ static int __init nsc_ircc_init(void) if ((id & chip->cid_mask) == chip->cid_value) { IRDA_DEBUG(2, "%s() Found %s chip, revision=%d\n", __FUNCTION__, chip->name, id & ~chip->cid_mask); - /* - * If the user supplies the base address, then - * we init the chip, if not we probe the values - * set by the BIOS - */ - if (io[i] < 0x2000) { - chip->init(chip, &info); - } else - chip->probe(chip, &info); - if (nsc_ircc_open(i, &info) == 0) - ret = 0; + /* + * If we found a correct PnP setting, + * we first try it. + */ + if (pnp_succeeded) { + memset(&info, 0, sizeof(chipio_t)); + info.cfg_base = cfg_base; + info.fir_base = pnp_info.fir_base; + info.dma = pnp_info.dma; + info.irq = pnp_info.irq; + + if (info.fir_base < 0x2000) { + IRDA_MESSAGE("%s, chip->init\n", driver_name); + chip->init(chip, &info); + } else + chip->probe(chip, &info); + + if (nsc_ircc_open(&info) >= 0) + ret = 0; + } + + /* + * Opening based on PnP values failed. + * Let's fallback to user values, or probe + * the chip. + */ + if (ret) { + IRDA_DEBUG(2, "%s, PnP init failed\n", driver_name); + memset(&info, 0, sizeof(chipio_t)); + info.cfg_base = cfg_base; + info.fir_base = io[i]; + info.dma = dma[i]; + info.irq = irq[i]; + + /* + * If the user supplies the base address, then + * we init the chip, if not we probe the values + * set by the BIOS + */ + if (io[i] < 0x2000) { + chip->init(chip, &info); + } else + chip->probe(chip, &info); + + if (nsc_ircc_open(&info) >= 0) + ret = 0; + } i++; } else { IRDA_DEBUG(2, "%s(), Wrong chip id=0x%02x\n", __FUNCTION__, id); } } - + } + + if (ret) { + pnp_unregister_driver(&nsc_ircc_pnp_driver); + pnp_registered = 0; } return ret; @@ -229,10 +293,15 @@ static void __exit nsc_ircc_cleanup(void) pm_unregister_all(nsc_ircc_pmproc); - for (i=0; i < 4; i++) { + for (i = 0; i < ARRAY_SIZE(dev_self); i++) { if (dev_self[i]) nsc_ircc_close(dev_self[i]); } + + if (pnp_registered) + pnp_unregister_driver(&nsc_ircc_pnp_driver); + + pnp_registered = 0; } /* @@ -241,16 +310,27 @@ static void __exit nsc_ircc_cleanup(void) * Open driver instance * */ -static int __init nsc_ircc_open(int i, chipio_t *info) +static int __init nsc_ircc_open(chipio_t *info) { struct net_device *dev; struct nsc_ircc_cb *self; - struct pm_dev *pmdev; + struct pm_dev *pmdev; void *ret; - int err; + int err, chip_index; IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + for (chip_index = 0; chip_index < ARRAY_SIZE(dev_self); chip_index++) { + if (!dev_self[chip_index]) + break; + } + + if (chip_index == ARRAY_SIZE(dev_self)) { + IRDA_ERROR("%s(), maximum number of supported chips reached!\n", __FUNCTION__); + return -ENOMEM; + } + IRDA_MESSAGE("%s, Found chip at base=0x%03x\n", driver_name, info->cfg_base); @@ -271,8 +351,8 @@ static int __init nsc_ircc_open(int i, chipio_t *info) spin_lock_init(&self->lock); /* Need to store self somewhere */ - dev_self[i] = self; - self->index = i; + dev_self[chip_index] = self; + self->index = chip_index; /* Initialize IO */ self->io.cfg_base = info->cfg_base; @@ -351,7 +431,7 @@ static int __init nsc_ircc_open(int i, chipio_t *info) /* Check if user has supplied a valid dongle id or not */ if ((dongle_id <= 0) || - (dongle_id >= (sizeof(dongle_types) / sizeof(dongle_types[0]))) ) { + (dongle_id >= ARRAY_SIZE(dongle_types))) { dongle_id = nsc_ircc_read_dongle_id(self->io.fir_base); IRDA_MESSAGE("%s, Found dongle: %s\n", driver_name, @@ -368,7 +448,7 @@ static int __init nsc_ircc_open(int i, chipio_t *info) if (pmdev) pmdev->data = self; - return 0; + return chip_index; out4: dma_free_coherent(NULL, self->tx_buff.truesize, self->tx_buff.head, self->tx_buff_dma); @@ -379,7 +459,7 @@ static int __init nsc_ircc_open(int i, chipio_t *info) release_region(self->io.fir_base, self->io.fir_ext); out1: free_netdev(dev); - dev_self[i] = NULL; + dev_self[chip_index] = NULL; return err; } @@ -806,6 +886,43 @@ static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info) return 0; } +/* PNP probing */ +static int nsc_ircc_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *id) +{ + memset(&pnp_info, 0, sizeof(chipio_t)); + pnp_info.irq = -1; + pnp_info.dma = -1; + pnp_succeeded = 1; + + /* There don't seem to be any way to get the cfg_base. + * On my box, cfg_base is in the PnP descriptor of the + * motherboard. Oh well... Jean II */ + + if (pnp_port_valid(dev, 0) && + !(pnp_port_flags(dev, 0) & IORESOURCE_DISABLED)) + pnp_info.fir_base = pnp_port_start(dev, 0); + + if (pnp_irq_valid(dev, 0) && + !(pnp_irq_flags(dev, 0) & IORESOURCE_DISABLED)) + pnp_info.irq = pnp_irq(dev, 0); + + if (pnp_dma_valid(dev, 0) && + !(pnp_dma_flags(dev, 0) & IORESOURCE_DISABLED)) + pnp_info.dma = pnp_dma(dev, 0); + + IRDA_DEBUG(0, "%s() : From PnP, found firbase 0x%03X ; irq %d ; dma %d.\n", + __FUNCTION__, pnp_info.fir_base, pnp_info.irq, pnp_info.dma); + + if((pnp_info.fir_base == 0) || + (pnp_info.irq == -1) || (pnp_info.dma == -1)) { + /* Returning an error will disable the device. Yuck ! */ + //return -EINVAL; + pnp_succeeded = 0; + } + + return 0; +} + /* * Function nsc_ircc_setup (info) * |