X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fatm%2Fhorizon.c;h=f96446c358bae79fe41ae51b254f5ad7f486a899;hb=refs%2Fheads%2Fvserver;hp=90e407a9a5a8418e1f4ecb6d5ca49b5b06ee90d5;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/atm/horizon.c b/drivers/atm/horizon.c index 90e407a9a..f96446c35 100644 --- a/drivers/atm/horizon.c +++ b/drivers/atm/horizon.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -354,8 +355,7 @@ static inline void __init show_version (void) { /********** globals **********/ -static hrz_dev * hrz_devs = NULL; -static struct timer_list housekeeping; +static void do_housekeeping (unsigned long arg); static unsigned short debug = 0; static unsigned short vpi_bits = 0; @@ -597,126 +597,112 @@ static inline u16 rx_q_entry_to_rx_channel (u32 x) { // p ranges from 1 to a power of 2 #define CR_MAXPEXP 4 - + static int make_rate (const hrz_dev * dev, u32 c, rounding r, - u16 * bits, unsigned int * actual) { - - // note: rounding the rate down means rounding 'p' up + u16 * bits, unsigned int * actual) +{ + // note: rounding the rate down means rounding 'p' up + const unsigned long br = test_bit(ultra, &dev->flags) ? BR_ULT : BR_HRZ; - const unsigned long br = test_bit(ultra, &dev->flags) ? BR_ULT : BR_HRZ; + u32 div = CR_MIND; + u32 pre; - u32 div = CR_MIND; - u32 pre; + // br_exp and br_man are used to avoid overflowing (c*maxp*2^d) in + // the tests below. We could think harder about exact possibilities + // of failure... - // local fn to build the timer bits - int set_cr (void) { - // paranoia - if (div > CR_MAXD || (!pre) || pre > 1<> 1; - ++br_exp; - } - // (br >>br_exp) <> 1; + ++br_exp; + } + // (br >>br_exp) < CR_MAXD || (!pre) || pre > 1<vcc; // VC layer stats atomic_inc(&vcc->stats->rx); - do_gettimeofday(&skb->stamp); + __net_timestamp(skb); // end of our responsability vcc->push (vcc, skb); } @@ -1104,13 +1090,11 @@ static inline void rx_bus_master_complete_handler (hrz_dev * dev) { /********** (queue to) become the next TX thread **********/ static inline int tx_hold (hrz_dev * dev) { - while (test_and_set_bit (tx_busy, &dev->flags)) { - PRINTD (DBG_TX, "sleeping at tx lock %p %lu", dev, dev->flags); - interruptible_sleep_on (&dev->tx_queue); - PRINTD (DBG_TX, "woken at tx lock %p %lu", dev, dev->flags); - if (signal_pending (current)) - return -1; - } + PRINTD (DBG_TX, "sleeping at tx lock %p %lu", dev, dev->flags); + wait_event_interruptible(dev->tx_queue, (!test_and_set_bit(tx_busy, &dev->flags))); + PRINTD (DBG_TX, "woken at tx lock %p %lu", dev, dev->flags); + if (signal_pending (current)) + return -1; PRINTD (DBG_TX, "set tx_busy for dev %p", dev); return 0; } @@ -1198,7 +1182,7 @@ static void tx_schedule (hrz_dev * const dev, int irq) { // tx_regions == 0 // that's all folks - end of frame struct sk_buff * skb = dev->tx_skb; - dev->tx_iovec = 0; + dev->tx_iovec = NULL; // VC layer stats atomic_inc(&ATM_SKB(skb)->vcc->stats->tx); @@ -1398,34 +1382,13 @@ static inline void rx_data_av_handler (hrz_dev * dev) { /********** interrupt handler **********/ -static irqreturn_t interrupt_handler(int irq, void *dev_id, - struct pt_regs *pt_regs) { - hrz_dev * dev = hrz_devs; +static irqreturn_t interrupt_handler(int irq, void *dev_id) { + hrz_dev * dev = (hrz_dev *) dev_id; u32 int_source; unsigned int irq_ok; - (void) pt_regs; PRINTD (DBG_FLOW, "interrupt_handler: %p", dev_id); - if (!dev_id) { - PRINTD (DBG_IRQ|DBG_ERR, "irq with NULL dev_id: %d", irq); - return IRQ_NONE; - } - // Did one of our cards generate the interrupt? - while (dev) { - if (dev == dev_id) - break; - dev = dev->prev; - } - if (!dev) { - PRINTD (DBG_IRQ, "irq not for me: %d", irq); - return IRQ_NONE; - } - if (irq != dev->irq) { - PRINTD (DBG_IRQ|DBG_ERR, "irq mismatch: %d", irq); - return IRQ_NONE; - } - // definitely for us irq_ok = 0; while ((int_source = rd_regl (dev, INT_SOURCE_REG_OFF) @@ -1476,28 +1439,18 @@ static irqreturn_t interrupt_handler(int irq, void *dev_id, /********** housekeeping **********/ -static void set_timer (struct timer_list * timer, unsigned int delay) { - timer->expires = jiffies + delay; - add_timer (timer); - return; -} - static void do_housekeeping (unsigned long arg) { // just stats at the moment - hrz_dev * dev = hrz_devs; - (void) arg; - // data is set to zero at module unload - if (housekeeping.data) { - while (dev) { - // collect device-specific (not driver/atm-linux) stats here - dev->tx_cell_count += rd_regw (dev, TX_CELL_COUNT_OFF); - dev->rx_cell_count += rd_regw (dev, RX_CELL_COUNT_OFF); - dev->hec_error_count += rd_regw (dev, HEC_ERROR_COUNT_OFF); - dev->unassigned_cell_count += rd_regw (dev, UNASSIGNED_CELL_COUNT_OFF); - dev = dev->prev; - } - set_timer (&housekeeping, HZ/10); - } + hrz_dev * dev = (hrz_dev *) arg; + + // collect device-specific (not driver/atm-linux) stats here + dev->tx_cell_count += rd_regw (dev, TX_CELL_COUNT_OFF); + dev->rx_cell_count += rd_regw (dev, RX_CELL_COUNT_OFF); + dev->hec_error_count += rd_regw (dev, HEC_ERROR_COUNT_OFF); + dev->unassigned_cell_count += rd_regw (dev, UNASSIGNED_CELL_COUNT_OFF); + + mod_timer (&dev->housekeeping, jiffies + HZ/10); + return; } @@ -1547,8 +1500,8 @@ static inline short setup_idle_tx_channel (hrz_dev * dev, hrz_vcc * vcc) { // a.k.a. prepare the channel and remember that we have done so. tx_ch_desc * tx_desc = &memmap->tx_descs[tx_channel]; - u16 rd_ptr; - u16 wr_ptr; + u32 rd_ptr; + u32 wr_ptr; u16 channel = vcc->channel; unsigned long flags; @@ -1775,7 +1728,7 @@ static int hrz_send (struct atm_vcc * atm_vcc, struct sk_buff * skb) { if (tx_iovcnt) { // scatter gather transfer dev->tx_regions = tx_iovcnt; - dev->tx_iovec = 0; /* @@@ needs rewritten */ + dev->tx_iovec = NULL; /* @@@ needs rewritten */ dev->tx_bytes = 0; PRINTD (DBG_TX|DBG_BUS, "TX start scatter-gather transfer (iovec %p, len %d)", skb->data, tx_len); @@ -1785,7 +1738,7 @@ static int hrz_send (struct atm_vcc * atm_vcc, struct sk_buff * skb) { } else { // simple transfer dev->tx_regions = 0; - dev->tx_iovec = 0; + dev->tx_iovec = NULL; dev->tx_bytes = tx_len; dev->tx_addr = skb->data; PRINTD (DBG_TX|DBG_BUS, "TX start simple transfer (addr %p, len %d)", @@ -1823,23 +1776,23 @@ static void hrz_reset (const hrz_dev * dev) { /********** read the burnt in address **********/ -static u16 __init read_bia (const hrz_dev * dev, u16 addr) { +static inline void WRITE_IT_WAIT (const hrz_dev *dev, u32 ctrl) +{ + wr_regl (dev, CONTROL_0_REG, ctrl); + udelay (5); +} +static inline void CLOCK_IT (const hrz_dev *dev, u32 ctrl) +{ + // DI must be valid around rising SK edge + WRITE_IT_WAIT(dev, ctrl & ~SEEPROM_SK); + WRITE_IT_WAIT(dev, ctrl | SEEPROM_SK); +} + +static u16 __devinit read_bia (const hrz_dev * dev, u16 addr) +{ u32 ctrl = rd_regl (dev, CONTROL_0_REG); - void WRITE_IT_WAIT (void) { - wr_regl (dev, CONTROL_0_REG, ctrl); - udelay (5); - } - - void CLOCK_IT (void) { - // DI must be valid around rising SK edge - ctrl &= ~SEEPROM_SK; - WRITE_IT_WAIT(); - ctrl |= SEEPROM_SK; - WRITE_IT_WAIT(); - } - const unsigned int addr_bits = 6; const unsigned int data_bits = 16; @@ -1848,17 +1801,17 @@ static u16 __init read_bia (const hrz_dev * dev, u16 addr) { u16 res; ctrl &= ~(SEEPROM_CS | SEEPROM_SK | SEEPROM_DI); - WRITE_IT_WAIT(); + WRITE_IT_WAIT(dev, ctrl); // wake Serial EEPROM and send 110 (READ) command ctrl |= (SEEPROM_CS | SEEPROM_DI); - CLOCK_IT(); + CLOCK_IT(dev, ctrl); ctrl |= SEEPROM_DI; - CLOCK_IT(); + CLOCK_IT(dev, ctrl); ctrl &= ~SEEPROM_DI; - CLOCK_IT(); + CLOCK_IT(dev, ctrl); for (i=0; i> 1; - CLOCK_IT(); + CLOCK_IT(dev, ctrl); if (rd_regl (dev, CONTROL_0_REG) & SEEPROM_DO) res |= (1 << (data_bits-1)); } ctrl &= ~(SEEPROM_SK | SEEPROM_CS); - WRITE_IT_WAIT(); + WRITE_IT_WAIT(dev, ctrl); return res; } /********** initialise a card **********/ -static int __init hrz_init (hrz_dev * dev) { +static int __devinit hrz_init (hrz_dev * dev) { int onefivefive; u16 chan; @@ -2292,7 +2245,7 @@ static int hrz_open (struct atm_vcc *atm_vcc) // we take "the PCR" as a rate-cap // not reserved vcc.tx_rate = 0; - make_rate (dev, 1<<30, round_nearest, &vcc.tx_pcr_bits, 0); + make_rate (dev, 1<<30, round_nearest, &vcc.tx_pcr_bits, NULL); vcc.tx_xbr_bits = ABR_RATE_TYPE; break; } @@ -2597,7 +2550,7 @@ static void hrz_close (struct atm_vcc * atm_vcc) { PRINTK (KERN_ERR, "%s atm_vcc=%p rxer[channel]=%p", "arghhh! we're going to die!", atm_vcc, dev->rxer[channel]); - dev->rxer[channel] = 0; + dev->rxer[channel] = NULL; } // atomically release our rate reservation @@ -2733,157 +2686,175 @@ static const struct atmdev_ops hrz_ops = { .owner = THIS_MODULE, }; -static int __init hrz_probe (void) { - struct pci_dev * pci_dev; - int devs; - - PRINTD (DBG_FLOW, "hrz_probe"); - - devs = 0; - pci_dev = NULL; - while ((pci_dev = pci_find_device - (PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_HORIZON, pci_dev) - )) { - hrz_dev * dev; - - // adapter slot free, read resources from PCI configuration space - u32 iobase = pci_resource_start (pci_dev, 0); - u32 * membase = bus_to_virt (pci_resource_start (pci_dev, 1)); - u8 irq = pci_dev->irq; - - /* XXX DEV_LABEL is a guess */ - if (!request_region (iobase, HRZ_IO_EXTENT, DEV_LABEL)) - continue; +static int __devinit hrz_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_ent) +{ + hrz_dev * dev; + int err = 0; - if (pci_enable_device (pci_dev)) - continue; - - dev = kmalloc (sizeof(hrz_dev), GFP_KERNEL); - if (!dev) { - // perhaps we should be nice: deregister all adapters and abort? - PRINTD (DBG_ERR, "out of memory"); - continue; - } - - memset (dev, 0, sizeof(hrz_dev)); - - // grab IRQ and install handler - move this someplace more sensible - if (request_irq (irq, - interrupt_handler, - SA_SHIRQ, /* irqflags guess */ - DEV_LABEL, /* name guess */ - dev)) { - PRINTD (DBG_WARN, "request IRQ failed!"); - // free_irq is at "endif" - } else { - - PRINTD (DBG_INFO, "found Madge ATM adapter (hrz) at: IO %x, IRQ %u, MEM %p", - iobase, irq, membase); - - dev->atm_dev = atm_dev_register (DEV_LABEL, &hrz_ops, -1, NULL); - if (!(dev->atm_dev)) { - PRINTD (DBG_ERR, "failed to register Madge ATM adapter"); - } else { + // adapter slot free, read resources from PCI configuration space + u32 iobase = pci_resource_start (pci_dev, 0); + u32 * membase = bus_to_virt (pci_resource_start (pci_dev, 1)); + unsigned int irq; unsigned char lat; - - PRINTD (DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p", - dev->atm_dev->number, dev, dev->atm_dev); + + PRINTD (DBG_FLOW, "hrz_probe"); + + if (pci_enable_device(pci_dev)) + return -EINVAL; + + /* XXX DEV_LABEL is a guess */ + if (!request_region(iobase, HRZ_IO_EXTENT, DEV_LABEL)) { + return -EINVAL; + goto out_disable; + } + + dev = kzalloc(sizeof(hrz_dev), GFP_KERNEL); + if (!dev) { + // perhaps we should be nice: deregister all adapters and abort? + PRINTD(DBG_ERR, "out of memory"); + err = -ENOMEM; + goto out_release; + } + + pci_set_drvdata(pci_dev, dev); + + // grab IRQ and install handler - move this someplace more sensible + irq = pci_dev->irq; + if (request_irq(irq, + interrupt_handler, + IRQF_SHARED, /* irqflags guess */ + DEV_LABEL, /* name guess */ + dev)) { + PRINTD(DBG_WARN, "request IRQ failed!"); + err = -EINVAL; + goto out_free; + } + + PRINTD(DBG_INFO, "found Madge ATM adapter (hrz) at: IO %x, IRQ %u, MEM %p", + iobase, irq, membase); + + dev->atm_dev = atm_dev_register(DEV_LABEL, &hrz_ops, -1, NULL); + if (!(dev->atm_dev)) { + PRINTD(DBG_ERR, "failed to register Madge ATM adapter"); + err = -EINVAL; + goto out_free_irq; + } + + PRINTD(DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p", + dev->atm_dev->number, dev, dev->atm_dev); dev->atm_dev->dev_data = (void *) dev; dev->pci_dev = pci_dev; - + // enable bus master accesses - pci_set_master (pci_dev); - + pci_set_master(pci_dev); + // frobnicate latency (upwards, usually) - pci_read_config_byte (pci_dev, PCI_LATENCY_TIMER, &lat); + pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &lat); if (pci_lat) { - PRINTD (DBG_INFO, "%s PCI latency timer from %hu to %hu", - "changing", lat, pci_lat); - pci_write_config_byte (pci_dev, PCI_LATENCY_TIMER, pci_lat); + PRINTD(DBG_INFO, "%s PCI latency timer from %hu to %hu", + "changing", lat, pci_lat); + pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, pci_lat); } else if (lat < MIN_PCI_LATENCY) { - PRINTK (KERN_INFO, "%s PCI latency timer from %hu to %hu", - "increasing", lat, MIN_PCI_LATENCY); - pci_write_config_byte (pci_dev, PCI_LATENCY_TIMER, MIN_PCI_LATENCY); + PRINTK(KERN_INFO, "%s PCI latency timer from %hu to %hu", + "increasing", lat, MIN_PCI_LATENCY); + pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, MIN_PCI_LATENCY); } - + dev->iobase = iobase; dev->irq = irq; dev->membase = membase; - + dev->rx_q_entry = dev->rx_q_reset = &memmap->rx_q_entries[0]; dev->rx_q_wrap = &memmap->rx_q_entries[RX_CHANS-1]; - + // these next three are performance hacks dev->last_vc = -1; dev->tx_last = -1; dev->tx_idle = 0; - + dev->tx_regions = 0; dev->tx_bytes = 0; - dev->tx_skb = 0; - dev->tx_iovec = 0; - + dev->tx_skb = NULL; + dev->tx_iovec = NULL; + dev->tx_cell_count = 0; dev->rx_cell_count = 0; dev->hec_error_count = 0; dev->unassigned_cell_count = 0; - + dev->noof_spare_buffers = 0; - + { - unsigned int i; - for (i = 0; i < TX_CHANS; ++i) - dev->tx_channel_record[i] = -1; + unsigned int i; + for (i = 0; i < TX_CHANS; ++i) + dev->tx_channel_record[i] = -1; } - + dev->flags = 0; - + // Allocate cell rates and remember ASIC version // Fibre: ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53 // Copper: (WRONG) we want 6 into the above, close to 25Mb/s // Copper: (plagarise!) 25600000/8/270*260/53 - n/53 - - if (hrz_init (dev)) { - // to be really pedantic, this should be ATM_OC3c_PCR - dev->tx_avail = ATM_OC3_PCR; - dev->rx_avail = ATM_OC3_PCR; - set_bit (ultra, &dev->flags); // NOT "|= ultra" ! + + if (hrz_init(dev)) { + // to be really pedantic, this should be ATM_OC3c_PCR + dev->tx_avail = ATM_OC3_PCR; + dev->rx_avail = ATM_OC3_PCR; + set_bit(ultra, &dev->flags); // NOT "|= ultra" ! } else { - dev->tx_avail = ((25600000/8)*26)/(27*53); - dev->rx_avail = ((25600000/8)*26)/(27*53); - PRINTD (DBG_WARN, "Buggy ASIC: no TX bus-mastering."); + dev->tx_avail = ((25600000/8)*26)/(27*53); + dev->rx_avail = ((25600000/8)*26)/(27*53); + PRINTD(DBG_WARN, "Buggy ASIC: no TX bus-mastering."); } - + // rate changes spinlock - spin_lock_init (&dev->rate_lock); - + spin_lock_init(&dev->rate_lock); + // on-board memory access spinlock; we want atomic reads and // writes to adapter memory (handles IRQ and SMP) - spin_lock_init (&dev->mem_lock); - - init_waitqueue_head (&dev->tx_queue); - + spin_lock_init(&dev->mem_lock); + + init_waitqueue_head(&dev->tx_queue); + // vpi in 0..4, vci in 6..10 dev->atm_dev->ci_range.vpi_bits = vpi_bits; dev->atm_dev->ci_range.vci_bits = 10-vpi_bits; - - // update count and linked list - ++devs; - dev->prev = hrz_devs; - hrz_devs = dev; - // success - continue; - - /* not currently reached */ - atm_dev_deregister (dev->atm_dev); - } /* atm_dev_register */ - free_irq (irq, dev); - - } /* request_irq */ - kfree (dev); - release_region(iobase, HRZ_IO_EXTENT); - } /* kmalloc and while */ - return devs; + + init_timer(&dev->housekeeping); + dev->housekeeping.function = do_housekeeping; + dev->housekeeping.data = (unsigned long) dev; + mod_timer(&dev->housekeeping, jiffies); + +out: + return err; + +out_free_irq: + free_irq(dev->irq, dev); +out_free: + kfree(dev); +out_release: + release_region(iobase, HRZ_IO_EXTENT); +out_disable: + pci_disable_device(pci_dev); + goto out; +} + +static void __devexit hrz_remove_one(struct pci_dev *pci_dev) +{ + hrz_dev *dev; + + dev = pci_get_drvdata(pci_dev); + + PRINTD(DBG_INFO, "closing %p (atm_dev = %p)", dev, dev->atm_dev); + del_timer_sync(&dev->housekeeping); + hrz_reset(dev); + atm_dev_deregister(dev->atm_dev); + free_irq(dev->irq, dev); + release_region(dev->iobase, HRZ_IO_EXTENT); + kfree(dev); + + pci_disable_device(pci_dev); } static void __init hrz_check_args (void) { @@ -2912,22 +2883,35 @@ static void __init hrz_check_args (void) { MODULE_AUTHOR(maintainer_string); MODULE_DESCRIPTION(description_string); MODULE_LICENSE("GPL"); -MODULE_PARM(debug, "h"); -MODULE_PARM(vpi_bits, "h"); -MODULE_PARM(max_tx_size, "i"); -MODULE_PARM(max_rx_size, "i"); -MODULE_PARM(pci_lat, "b"); +module_param(debug, ushort, 0644); +module_param(vpi_bits, ushort, 0); +module_param(max_tx_size, int, 0); +module_param(max_rx_size, int, 0); +module_param(pci_lat, byte, 0); MODULE_PARM_DESC(debug, "debug bitmap, see .h file"); MODULE_PARM_DESC(vpi_bits, "number of bits (0..4) to allocate to VPIs"); MODULE_PARM_DESC(max_tx_size, "maximum size of TX AAL5 frames"); MODULE_PARM_DESC(max_rx_size, "maximum size of RX AAL5 frames"); MODULE_PARM_DESC(pci_lat, "PCI latency in bus cycles"); +static struct pci_device_id hrz_pci_tbl[] = { + { PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_HORIZON, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, hrz_pci_tbl); + +static struct pci_driver hrz_driver = { + .name = "horizon", + .probe = hrz_probe, + .remove = __devexit_p(hrz_remove_one), + .id_table = hrz_pci_tbl, +}; + /********** module entry **********/ static int __init hrz_module_init (void) { - int devs; - // sanity check - cast is needed since printk does not support %Zu if (sizeof(struct MEMMAP) != 128*1024/4) { PRINTK (KERN_ERR, "Fix struct MEMMAP (is %lu fakewords).", @@ -2941,44 +2925,15 @@ static int __init hrz_module_init (void) { hrz_check_args(); // get the juice - devs = hrz_probe(); - - if (devs) { - init_timer (&housekeeping); - housekeeping.function = do_housekeeping; - // paranoia - housekeeping.data = 1; - set_timer (&housekeeping, 0); - } else { - PRINTK (KERN_ERR, "no (usable) adapters found"); - } - - return devs ? 0 : -ENODEV; + return pci_register_driver(&hrz_driver); } /********** module exit **********/ static void __exit hrz_module_exit (void) { - hrz_dev * dev; PRINTD (DBG_FLOW, "cleanup_module"); - - // paranoia - housekeeping.data = 0; - del_timer (&housekeeping); - - while (hrz_devs) { - dev = hrz_devs; - hrz_devs = dev->prev; - - PRINTD (DBG_INFO, "closing %p (atm_dev = %p)", dev, dev->atm_dev); - hrz_reset (dev); - atm_dev_deregister (dev->atm_dev); - free_irq (dev->irq, dev); - release_region (dev->iobase, HRZ_IO_EXTENT); - kfree (dev); - } - - return; + + pci_unregister_driver(&hrz_driver); } module_init(hrz_module_init);