X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fatm%2Fsolos-pci.c;h=c5f5186d62a3160404a50b5819c440239b311870;hb=c677189af9faa3f26fae0fcb7ac59f477048b89a;hp=5e228a3f750235a3c6d00c061f863da191212e0a;hpb=c6428e52facd03dfac971a44abca4bc058104fec;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index 5e228a3..c5f5186 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -59,20 +59,29 @@ #define RX_DMA_ADDR(port) (0x30 + (4 * (port))) #define DATA_RAM_SIZE 32768 -#define BUF_SIZE 4096 +#define BUF_SIZE 2048 +#define OLD_BUF_SIZE 4096 /* For FPGA versions <= 2*/ #define FPGA_PAGE 528 /* FPGA flash page size*/ #define SOLOS_PAGE 512 /* Solos flash page size*/ #define FPGA_BLOCK (FPGA_PAGE * 8) /* FPGA flash block size*/ #define SOLOS_BLOCK (SOLOS_PAGE * 8) /* Solos flash block size*/ -#define RX_BUF(card, nr) ((card->buffers) + (nr)*BUF_SIZE*2) -#define TX_BUF(card, nr) ((card->buffers) + (nr)*BUF_SIZE*2 + BUF_SIZE) +#define RX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2) +#define TX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2 + (card->buffer_size)) +#define FLASH_BUF ((card->buffers) + 4*(card->buffer_size)*2) #define RX_DMA_SIZE 2048 +#define FPGA_VERSION(a,b) (((a) << 8) + (b)) +#define LEGACY_BUFFERS 2 +#define DMA_SUPPORTED 4 + +static int reset = 0; static int atmdebug = 0; static int firmware_upgrade = 0; static int fpga_upgrade = 0; +static int db_firmware_upgrade = 0; +static int db_fpga_upgrade = 0; struct pkt_hdr { __le16 size; @@ -115,6 +124,8 @@ struct solos_card { wait_queue_head_t param_wq; wait_queue_head_t fw_wq; int using_dma; + int fpga_version; + int buffer_size; }; @@ -131,16 +142,22 @@ MODULE_AUTHOR("Traverse Technologies "); MODULE_DESCRIPTION("Solos PCI driver"); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); +MODULE_PARM_DESC(reset, "Reset Solos chips on startup"); MODULE_PARM_DESC(atmdebug, "Print ATM data"); MODULE_PARM_DESC(firmware_upgrade, "Initiate Solos firmware upgrade"); MODULE_PARM_DESC(fpga_upgrade, "Initiate FPGA upgrade"); +MODULE_PARM_DESC(db_firmware_upgrade, "Initiate daughter board Solos firmware upgrade"); +MODULE_PARM_DESC(db_fpga_upgrade, "Initiate daughter board FPGA upgrade"); +module_param(reset, int, 0444); module_param(atmdebug, int, 0644); module_param(firmware_upgrade, int, 0444); module_param(fpga_upgrade, int, 0444); +module_param(db_firmware_upgrade, int, 0444); +module_param(db_fpga_upgrade, int, 0444); static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb, struct atm_vcc *vcc); -static int fpga_tx(struct solos_card *); +static uint32_t fpga_tx(struct solos_card *); static irqreturn_t solos_irq(int irq, void *dev_id); static struct atm_vcc* find_vcc(struct atm_dev *dev, short vpi, int vci); static int list_vccs(int vci); @@ -311,14 +328,16 @@ static char *next_string(struct sk_buff *skb) * for the information therein. Fields are.... * * packet version - * TxBitRate (version >= 1) * RxBitRate (version >= 1) + * TxBitRate (version >= 1) * State (version >= 1) + * LocalSNRMargin (version >= 1) + * LocalLineAttn (version >= 1) */ static int process_status(struct solos_card *card, int port, struct sk_buff *skb) { - char *str, *end, *state_str; - int ver, rate_up, rate_down, state; + char *str, *end, *state_str, *snr, *attn; + int ver, rate_up, rate_down; if (!card->atmdev[port]) return -ENODEV; @@ -337,46 +356,48 @@ static int process_status(struct solos_card *card, int port, struct sk_buff *skb str = next_string(skb); if (!str) return -EIO; - rate_up = simple_strtol(str, &end, 10); + if (!strcmp(str, "ERROR")) { + dev_dbg(&card->dev->dev, "Status packet indicated Solos error on port %d (starting up?)\n", + port); + return 0; + } + + rate_down = simple_strtol(str, &end, 10); if (*end) return -EIO; str = next_string(skb); if (!str) return -EIO; - rate_down = simple_strtol(str, &end, 10); + rate_up = simple_strtol(str, &end, 10); if (*end) return -EIO; state_str = next_string(skb); if (!state_str) return -EIO; - if (!strcmp(state_str, "Showtime")) - state = ATM_PHY_SIG_FOUND; - else { - state = ATM_PHY_SIG_LOST; + + /* Anything but 'Showtime' is down */ + if (strcmp(state_str, "Showtime")) { + card->atmdev[port]->signal = ATM_PHY_SIG_LOST; release_vccs(card->atmdev[port]); + dev_info(&card->dev->dev, "Port %d: %s\n", port, state_str); + return 0; } - if (state == ATM_PHY_SIG_LOST) { - dev_info(&card->dev->dev, "Port %d ATM state: %s\n", - port, state_str); - } else { - char *snr, *attn; - - snr = next_string(skb); - if (!str) - return -EIO; - attn = next_string(skb); - if (!attn) - return -EIO; - - dev_info(&card->dev->dev, "Port %d: %s (%d/%d kb/s%s%s%s%s)\n", - port, state_str, rate_down/1000, rate_up/1000, - snr[0]?", SNR ":"", snr, attn[0]?", Attn ":"", attn); - } + snr = next_string(skb); + if (!snr) + return -EIO; + attn = next_string(skb); + if (!attn) + return -EIO; + + dev_info(&card->dev->dev, "Port %d: %s @%d/%d kb/s%s%s%s%s\n", + port, state_str, rate_down/1000, rate_up/1000, + snr[0]?", SNR ":"", snr, attn[0]?", Attn ":"", attn); + card->atmdev[port]->link_rate = rate_down / 424; - card->atmdev[port]->signal = state; + card->atmdev[port]->signal = ATM_PHY_SIG_FOUND; return 0; } @@ -438,8 +459,6 @@ static int send_command(struct solos_card *card, int dev, const char *buf, size_ struct sk_buff *skb; struct pkt_hdr *header; -// dev_dbg(&card->dev->dev, "size: %d\n", size); - if (size > (BUF_SIZE - sizeof(*header))) { dev_dbg(&card->dev->dev, "Command is too big. Dropping request\n"); return 0; @@ -512,10 +531,32 @@ static int flash_upgrade(struct solos_card *card, int chip) if (chip == 0) { fw_name = "solos-FPGA.bin"; blocksize = FPGA_BLOCK; - } else { + } + + if (chip == 1) { fw_name = "solos-Firmware.bin"; blocksize = SOLOS_BLOCK; } + + if (chip == 2){ + if (card->fpga_version > LEGACY_BUFFERS){ + fw_name = "solos-db-FPGA.bin"; + blocksize = FPGA_BLOCK; + } else { + dev_info(&card->dev->dev, "FPGA version doesn't support daughter board upgrades\n"); + return -EPERM; + } + } + + if (chip == 3){ + if (card->fpga_version > LEGACY_BUFFERS){ + fw_name = "solos-Firmware.bin"; + blocksize = SOLOS_BLOCK; + } else { + dev_info(&card->dev->dev, "FPGA version doesn't support daughter board upgrades\n"); + return -EPERM; + } + } if (request_firmware(&fw, fw_name, &card->dev->dev)) return -ENOENT; @@ -531,8 +572,10 @@ static int flash_upgrade(struct solos_card *card, int chip) data32 = ioread32(card->config_regs + FPGA_MODE); /* Set mode to Chip Erase */ - dev_info(&card->dev->dev, "Set FPGA Flash mode to %s Chip Erase\n", - chip?"Solos":"FPGA"); + if(chip == 0 || chip == 2) + dev_info(&card->dev->dev, "Set FPGA Flash mode to FPGA Chip Erase\n"); + if(chip == 1 || chip == 3) + dev_info(&card->dev->dev, "Set FPGA Flash mode to Solos Chip Erase\n"); iowrite32((chip * 2), card->config_regs + FLASH_MODE); @@ -552,7 +595,10 @@ static int flash_upgrade(struct solos_card *card, int chip) /* Copy block to buffer, swapping each 16 bits */ for(i = 0; i < blocksize; i += 4) { uint32_t word = swahb32p((uint32_t *)(fw->data + offset + i)); - iowrite32(word, RX_BUF(card, 3) + i); + if(card->fpga_version > LEGACY_BUFFERS) + iowrite32(word, FLASH_BUF + i); + else + iowrite32(word, RX_BUF(card, 3) + i); } /* Specify block number and then trigger flash write */ @@ -574,9 +620,9 @@ static irqreturn_t solos_irq(int irq, void *dev_id) struct solos_card *card = dev_id; int handled = 1; - //ACK IRQ iowrite32(0, card->config_regs + IRQ_CLEAR); + /* If we're up and running, just kick the tasklet to process TX/RX */ if (card->atmdev[0]) tasklet_schedule(&card->tlet); else @@ -588,16 +634,16 @@ static irqreturn_t solos_irq(int irq, void *dev_id) void solos_bh(unsigned long card_arg) { struct solos_card *card = (void *)card_arg; - int port; uint32_t card_flags; uint32_t rx_done = 0; + int port; - card_flags = ioread32(card->config_regs + FLAGS_ADDR); - - /* The TX bits are set if the channel is busy; clear if not. We want to - invoke fpga_tx() unless _all_ the bits for active channels are set */ - if ((card_flags & card->tx_mask) != card->tx_mask) - fpga_tx(card); + /* + * Since fpga_tx() is going to need to read the flags under its lock, + * it can return them to us so that we don't have to hit PCI MMIO + * again for the same information + */ + card_flags = fpga_tx(card); for (port = 0; port < card->nr_ports; port++) { if (card_flags & (0x10 << port)) { @@ -625,6 +671,10 @@ void solos_bh(unsigned long card_arg) memcpy_fromio(header, RX_BUF(card, port), sizeof(*header)); size = le16_to_cpu(header->size); + if (size > (card->buffer_size - sizeof(*header))){ + dev_warn(&card->dev->dev, "Invalid buffer size\n"); + continue; + } skb = alloc_skb(size + 1, GFP_ATOMIC); if (!skb) { @@ -662,7 +712,11 @@ void solos_bh(unsigned long card_arg) break; case PKT_STATUS: - process_status(card, port, skb); + if (process_status(card, port, skb) && + net_ratelimit()) { + dev_warn(&card->dev->dev, "Bad status packet of %d bytes on port %d:\n", skb->len, port); + print_buffer(skb); + } dev_kfree_skb_any(skb); break; @@ -815,8 +869,7 @@ static int popen(struct atm_vcc *vcc) fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, NULL); -// dev_dbg(&card->dev->dev, "Open for vpi %d and vci %d on interface %d\n", vcc->vpi, vcc->vci, SOLOS_CHAN(vcc->dev)); - set_bit(ATM_VF_ADDR, &vcc->flags); // accept the vpi / vci + set_bit(ATM_VF_ADDR, &vcc->flags); set_bit(ATM_VF_READY, &vcc->flags); list_vccs(0); @@ -844,8 +897,6 @@ static void pclose(struct atm_vcc *vcc) fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, NULL); -// dev_dbg(&card->dev->dev, "Close for vpi %d and vci %d on interface %d\n", vcc->vpi, vcc->vci, SOLOS_CHAN(vcc->dev)); - clear_bit(ATM_VF_ADDR, &vcc->flags); clear_bit(ATM_VF_READY, &vcc->flags); @@ -892,9 +943,8 @@ static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb, spin_lock_irqsave(&card->tx_queue_lock, flags); old_len = skb_queue_len(&card->tx_queue[port]); skb_queue_tail(&card->tx_queue[port], skb); - if (!old_len) { + if (!old_len) card->tx_mask |= (1 << port); - } spin_unlock_irqrestore(&card->tx_queue_lock, flags); /* Theoretically we could just schedule the tasklet here, but @@ -903,9 +953,9 @@ static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb, fpga_tx(card); } -static int fpga_tx(struct solos_card *card) +static uint32_t fpga_tx(struct solos_card *card) { - uint32_t tx_pending; + uint32_t tx_pending, card_flags; uint32_t tx_started = 0; struct sk_buff *skb; struct atm_vcc *vcc; @@ -913,19 +963,24 @@ static int fpga_tx(struct solos_card *card) unsigned long flags; spin_lock_irqsave(&card->tx_lock, flags); - - tx_pending = ioread32(card->config_regs + FLAGS_ADDR) & card->tx_mask; - - dev_vdbg(&card->dev->dev, "TX Flags are %X\n", tx_pending); - - for (port = 0; port < card->nr_ports; port++) { - if (card->atmdev[port] && !(tx_pending & (1 << port))) { + + card_flags = ioread32(card->config_regs + FLAGS_ADDR); + /* + * The queue lock is required for _writing_ to tx_mask, but we're + * OK to read it here without locking. The only potential update + * that we could race with is in fpga_queue() where it sets a bit + * for a new port... but it's going to call this function again if + * it's doing that, anyway. + */ + tx_pending = card->tx_mask & ~card_flags; + + for (port = 0; tx_pending; tx_pending >>= 1, port++) { + if (tx_pending & 1) { struct sk_buff *oldskb = card->tx_skb[port]; - if (oldskb) pci_unmap_single(card->dev, SKB_CB(oldskb)->dma_addr, oldskb->len, PCI_DMA_TODEVICE); - + spin_lock(&card->tx_queue_lock); skb = skb_dequeue(&card->tx_queue[port]); if (!skb) @@ -934,7 +989,7 @@ static int fpga_tx(struct solos_card *card) if (skb && !card->using_dma) { memcpy_toio(TX_BUF(card, port), skb->data, skb->len); - tx_started |= 1 << port; //Set TX full flag + tx_started |= 1 << port; oldskb = skb; /* We're done with this skb already */ } else if (skb && card->using_dma) { SKB_CB(skb)->dma_addr = pci_map_single(card->dev, skb->data, @@ -963,11 +1018,12 @@ static int fpga_tx(struct solos_card *card) } } + /* For non-DMA TX, write the 'TX start' bit for all four ports simultaneously */ if (tx_started) iowrite32(tx_started, card->config_regs + FLAGS_ADDR); spin_unlock_irqrestore(&card->tx_lock, flags); - return 0; + return card_flags; } static int psend(struct atm_vcc *vcc, struct sk_buff *skb) @@ -976,9 +1032,6 @@ static int psend(struct atm_vcc *vcc, struct sk_buff *skb) struct pkt_hdr *header; int pktlen; - //dev_dbg(&card->dev->dev, "psend called.\n"); - //dev_dbg(&card->dev->dev, "dev,vpi,vci = %d,%d,%d\n",SOLOS_CHAN(vcc->dev),vcc->vpi,vcc->vci); - pktlen = skb->len; if (pktlen > (BUF_SIZE - sizeof(*header))) { dev_warn(&card->dev->dev, "Length of PDU is too large. Dropping PDU.\n"); @@ -1031,7 +1084,7 @@ static struct atmdev_ops fpga_ops = { static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) { - int err, i; + int err; uint16_t fpga_ver; uint8_t major_ver, minor_ver; uint32_t data32; @@ -1051,7 +1104,7 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) goto out; } - err = pci_set_dma_mask(dev, DMA_32BIT_MASK); + err = pci_set_dma_mask(dev, DMA_BIT_MASK(32)); if (err) { dev_warn(&dev->dev, "Failed to set 32-bit DMA mask\n"); goto out; @@ -1074,29 +1127,36 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) goto out_unmap_config; } -// for(i=0;i<64 ;i+=4){ -// data32=ioread32(card->buffers + i); -// dev_dbg(&card->dev->dev, "%08lX\n",(unsigned long)data32); -// } - - //Fill Config Mem with zeros - for(i = 0; i < 128; i += 4) - iowrite32(0, card->config_regs + i); + if (reset) { + iowrite32(1, card->config_regs + FPGA_MODE); + data32 = ioread32(card->config_regs + FPGA_MODE); - //Set RX empty flags - iowrite32(0xF0, card->config_regs + FLAGS_ADDR); + iowrite32(0, card->config_regs + FPGA_MODE); + data32 = ioread32(card->config_regs + FPGA_MODE); + } data32 = ioread32(card->config_regs + FPGA_VER); fpga_ver = (data32 & 0x0000FFFF); major_ver = ((data32 & 0xFF000000) >> 24); minor_ver = ((data32 & 0x00FF0000) >> 16); + card->fpga_version = FPGA_VERSION(major_ver,minor_ver); + if (card->fpga_version > LEGACY_BUFFERS) + card->buffer_size = BUF_SIZE; + else + card->buffer_size = OLD_BUF_SIZE; dev_info(&dev->dev, "Solos FPGA Version %d.%02d svn-%d\n", major_ver, minor_ver, fpga_ver); - if (fpga_ver > 27) + if (card->fpga_version >= DMA_SUPPORTED){ card->using_dma = 1; + } else { + card->using_dma = 0; + /* Set RX empty flag for all ports */ + iowrite32(0xF0, card->config_regs + FLAGS_ADDR); + } - card->nr_ports = 2; /* FIXME: Detect daughterboard */ + data32 = ioread32(card->config_regs + PORTS); + card->nr_ports = (data32 & 0x000000FF); pci_set_drvdata(dev, card); @@ -1107,33 +1167,6 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) spin_lock_init(&card->param_queue_lock); INIT_LIST_HEAD(&card->param_queue); -/* - // Set Loopback mode - data32 = 0x00010000; - iowrite32(data32,card->config_regs + FLAGS_ADDR); -*/ -/* - // Fill Buffers with zeros - for (i = 0; i < BUF_SIZE * 8; i += 4) - iowrite32(0, card->buffers + i); -*/ -/* - for(i = 0; i < (BUF_SIZE * 1); i += 4) - iowrite32(0x12345678, card->buffers + i + (0*BUF_SIZE)); - for(i = 0; i < (BUF_SIZE * 1); i += 4) - iowrite32(0xabcdef98, card->buffers + i + (1*BUF_SIZE)); - - // Read Config Memory - printk(KERN_DEBUG "Reading Config MEM\n"); - i = 0; - for(i = 0; i < 16; i++) { - data32=ioread32(card->buffers + i*(BUF_SIZE/2)); - printk(KERN_ALERT "Addr: %lX Data: %08lX\n", - (unsigned long)(addr_start + i*(BUF_SIZE/2)), - (unsigned long)data32); - } -*/ - //dev_dbg(&card->dev->dev, "Requesting IRQ: %d\n",dev->irq); err = request_irq(dev->irq, solos_irq, IRQF_SHARED, "solos-pci", card); if (err) { @@ -1141,7 +1174,6 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) goto out_unmap_both; } - // Enable IRQs iowrite32(1, card->config_regs + IRQ_EN_ADDR); if (fpga_upgrade) @@ -1150,6 +1182,12 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) if (firmware_upgrade) flash_upgrade(card, 1); + if (db_fpga_upgrade) + flash_upgrade(card, 2); + + if (db_firmware_upgrade) + flash_upgrade(card, 3); + err = atm_init(card); if (err) goto out_free_irq; @@ -1169,6 +1207,7 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) out_release_regions: pci_release_regions(dev); out: + kfree(card); return err; } @@ -1226,10 +1265,28 @@ static void atm_remove(struct solos_card *card) for (i = 0; i < card->nr_ports; i++) { if (card->atmdev[i]) { + struct sk_buff *skb; + dev_info(&card->dev->dev, "Unregistering ATM device %d\n", card->atmdev[i]->number); sysfs_remove_group(&card->atmdev[i]->class_dev.kobj, &solos_attr_group); atm_dev_deregister(card->atmdev[i]); + + skb = card->rx_skb[i]; + if (skb) { + pci_unmap_single(card->dev, SKB_CB(skb)->dma_addr, + RX_DMA_SIZE, PCI_DMA_FROMDEVICE); + dev_kfree_skb(skb); + } + skb = card->tx_skb[i]; + if (skb) { + pci_unmap_single(card->dev, SKB_CB(skb)->dma_addr, + skb->len, PCI_DMA_TODEVICE); + dev_kfree_skb(skb); + } + while ((skb = skb_dequeue(&card->tx_queue[i]))) + dev_kfree_skb(skb); + } } } @@ -1237,28 +1294,31 @@ static void atm_remove(struct solos_card *card) static void fpga_remove(struct pci_dev *dev) { struct solos_card *card = pci_get_drvdata(dev); + + /* Disable IRQs */ + iowrite32(0, card->config_regs + IRQ_EN_ADDR); + + /* Reset FPGA */ + iowrite32(1, card->config_regs + FPGA_MODE); + (void)ioread32(card->config_regs + FPGA_MODE); atm_remove(card); - dev_vdbg(&dev->dev, "Freeing IRQ\n"); - // Disable IRQs from FPGA - iowrite32(0, card->config_regs + IRQ_EN_ADDR); free_irq(dev->irq, card); tasklet_kill(&card->tlet); - // iowrite32(0x01,pciregs); - dev_vdbg(&dev->dev, "Unmapping PCI resource\n"); + /* Release device from reset */ + iowrite32(0, card->config_regs + FPGA_MODE); + (void)ioread32(card->config_regs + FPGA_MODE); + pci_iounmap(dev, card->buffers); pci_iounmap(dev, card->config_regs); - dev_vdbg(&dev->dev, "Releasing PCI Region\n"); pci_release_regions(dev); pci_disable_device(dev); pci_set_drvdata(dev, NULL); kfree(card); -// dev_dbg(&card->dev->dev, "fpga_remove\n"); - return; } static struct pci_device_id fpga_pci_tbl[] __devinitdata = {