19c54004eSDavid Woodhouse /* 29c54004eSDavid Woodhouse * Driver for the Solos PCI ADSL2+ card, designed to support Linux by 39c54004eSDavid Woodhouse * Traverse Technologies -- http://www.traverse.com.au/ 49c54004eSDavid Woodhouse * Xrio Limited -- http://www.xrio.com/ 59c54004eSDavid Woodhouse * 69c54004eSDavid Woodhouse * 79c54004eSDavid Woodhouse * Copyright © 2008 Traverse Technologies 89c54004eSDavid Woodhouse * Copyright © 2008 Intel Corporation 99c54004eSDavid Woodhouse * 109c54004eSDavid Woodhouse * Authors: Nathan Williams <nathan@traverse.com.au> 119c54004eSDavid Woodhouse * David Woodhouse <dwmw2@infradead.org> 127c4015bdSSimon Farnsworth * Treker Chen <treker@xrio.com> 139c54004eSDavid Woodhouse * 149c54004eSDavid Woodhouse * This program is free software; you can redistribute it and/or 159c54004eSDavid Woodhouse * modify it under the terms of the GNU General Public License 169c54004eSDavid Woodhouse * version 2, as published by the Free Software Foundation. 179c54004eSDavid Woodhouse * 189c54004eSDavid Woodhouse * This program is distributed in the hope that it will be useful, 199c54004eSDavid Woodhouse * but WITHOUT ANY WARRANTY; without even the implied warranty of 209c54004eSDavid Woodhouse * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 219c54004eSDavid Woodhouse * GNU General Public License for more details. 229c54004eSDavid Woodhouse */ 239c54004eSDavid Woodhouse 249c54004eSDavid Woodhouse #define DEBUG 259c54004eSDavid Woodhouse #define VERBOSE_DEBUG 269c54004eSDavid Woodhouse 279c54004eSDavid Woodhouse #include <linux/interrupt.h> 289c54004eSDavid Woodhouse #include <linux/module.h> 299c54004eSDavid Woodhouse #include <linux/kernel.h> 309c54004eSDavid Woodhouse #include <linux/errno.h> 319c54004eSDavid Woodhouse #include <linux/ioport.h> 329c54004eSDavid Woodhouse #include <linux/types.h> 339c54004eSDavid Woodhouse #include <linux/pci.h> 349c54004eSDavid Woodhouse #include <linux/atm.h> 359c54004eSDavid Woodhouse #include <linux/atmdev.h> 369c54004eSDavid Woodhouse #include <linux/skbuff.h> 379c54004eSDavid Woodhouse #include <linux/sysfs.h> 389c54004eSDavid Woodhouse #include <linux/device.h> 399c54004eSDavid Woodhouse #include <linux/kobject.h> 407c4015bdSSimon Farnsworth #include <linux/firmware.h> 4101e2ffacSDavid Woodhouse #include <linux/ctype.h> 4201e2ffacSDavid Woodhouse #include <linux/swab.h> 435a0e3ad6STejun Heo #include <linux/slab.h> 449c54004eSDavid Woodhouse 457c4015bdSSimon Farnsworth #define VERSION "0.07" 469c54004eSDavid Woodhouse #define PTAG "solos-pci" 479c54004eSDavid Woodhouse 489c54004eSDavid Woodhouse #define CONFIG_RAM_SIZE 128 499c54004eSDavid Woodhouse #define FLAGS_ADDR 0x7C 509c54004eSDavid Woodhouse #define IRQ_EN_ADDR 0x78 519c54004eSDavid Woodhouse #define FPGA_VER 0x74 529c54004eSDavid Woodhouse #define IRQ_CLEAR 0x70 537c4015bdSSimon Farnsworth #define WRITE_FLASH 0x6C 547c4015bdSSimon Farnsworth #define PORTS 0x68 557c4015bdSSimon Farnsworth #define FLASH_BLOCK 0x64 567c4015bdSSimon Farnsworth #define FLASH_BUSY 0x60 577c4015bdSSimon Farnsworth #define FPGA_MODE 0x5C 587c4015bdSSimon Farnsworth #define FLASH_MODE 0x58 5990937231SDavid Woodhouse #define TX_DMA_ADDR(port) (0x40 + (4 * (port))) 6090937231SDavid Woodhouse #define RX_DMA_ADDR(port) (0x30 + (4 * (port))) 619c54004eSDavid Woodhouse 629c54004eSDavid Woodhouse #define DATA_RAM_SIZE 32768 634dbedf43SNathan Williams #define BUF_SIZE 2048 644dbedf43SNathan Williams #define OLD_BUF_SIZE 4096 /* For FPGA versions <= 2*/ 657c4015bdSSimon Farnsworth #define FPGA_PAGE 528 /* FPGA flash page size*/ 667c4015bdSSimon Farnsworth #define SOLOS_PAGE 512 /* Solos flash page size*/ 677c4015bdSSimon Farnsworth #define FPGA_BLOCK (FPGA_PAGE * 8) /* FPGA flash block size*/ 687c4015bdSSimon Farnsworth #define SOLOS_BLOCK (SOLOS_PAGE * 8) /* Solos flash block size*/ 699c54004eSDavid Woodhouse 704dbedf43SNathan Williams #define RX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2) 714dbedf43SNathan Williams #define TX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2 + (card->buffer_size)) 724dbedf43SNathan Williams #define FLASH_BUF ((card->buffers) + 4*(card->buffer_size)*2) 739c54004eSDavid Woodhouse 74eaf83e39SDavid Woodhouse #define RX_DMA_SIZE 2048 75eaf83e39SDavid Woodhouse 764dbedf43SNathan Williams #define FPGA_VERSION(a,b) (((a) << 8) + (b)) 774dbedf43SNathan Williams #define LEGACY_BUFFERS 2 784dbedf43SNathan Williams #define DMA_SUPPORTED 4 794dbedf43SNathan Williams 80cc3657e1SDavid Woodhouse static int reset = 0; 819c54004eSDavid Woodhouse static int atmdebug = 0; 827c4015bdSSimon Farnsworth static int firmware_upgrade = 0; 837c4015bdSSimon Farnsworth static int fpga_upgrade = 0; 844dbedf43SNathan Williams static int db_firmware_upgrade = 0; 854dbedf43SNathan Williams static int db_fpga_upgrade = 0; 869c54004eSDavid Woodhouse 879c54004eSDavid Woodhouse struct pkt_hdr { 889c54004eSDavid Woodhouse __le16 size; 899c54004eSDavid Woodhouse __le16 vpi; 909c54004eSDavid Woodhouse __le16 vci; 919c54004eSDavid Woodhouse __le16 type; 929c54004eSDavid Woodhouse }; 939c54004eSDavid Woodhouse 9490937231SDavid Woodhouse struct solos_skb_cb { 9590937231SDavid Woodhouse struct atm_vcc *vcc; 9690937231SDavid Woodhouse uint32_t dma_addr; 9790937231SDavid Woodhouse }; 9890937231SDavid Woodhouse 9990937231SDavid Woodhouse 10090937231SDavid Woodhouse #define SKB_CB(skb) ((struct solos_skb_cb *)skb->cb) 10190937231SDavid Woodhouse 1029c54004eSDavid Woodhouse #define PKT_DATA 0 1039c54004eSDavid Woodhouse #define PKT_COMMAND 1 1049c54004eSDavid Woodhouse #define PKT_POPEN 3 1059c54004eSDavid Woodhouse #define PKT_PCLOSE 4 10687ebb186SDavid Woodhouse #define PKT_STATUS 5 1079c54004eSDavid Woodhouse 1089c54004eSDavid Woodhouse struct solos_card { 1099c54004eSDavid Woodhouse void __iomem *config_regs; 1109c54004eSDavid Woodhouse void __iomem *buffers; 1119c54004eSDavid Woodhouse int nr_ports; 112f69e4170SDavid Woodhouse int tx_mask; 1139c54004eSDavid Woodhouse struct pci_dev *dev; 1149c54004eSDavid Woodhouse struct atm_dev *atmdev[4]; 1159c54004eSDavid Woodhouse struct tasklet_struct tlet; 1169c54004eSDavid Woodhouse spinlock_t tx_lock; 1179c54004eSDavid Woodhouse spinlock_t tx_queue_lock; 1189c54004eSDavid Woodhouse spinlock_t cli_queue_lock; 11901e2ffacSDavid Woodhouse spinlock_t param_queue_lock; 12001e2ffacSDavid Woodhouse struct list_head param_queue; 1219c54004eSDavid Woodhouse struct sk_buff_head tx_queue[4]; 1229c54004eSDavid Woodhouse struct sk_buff_head cli_queue[4]; 12390937231SDavid Woodhouse struct sk_buff *tx_skb[4]; 12490937231SDavid Woodhouse struct sk_buff *rx_skb[4]; 12501e2ffacSDavid Woodhouse wait_queue_head_t param_wq; 126fa755b9fSDavid Woodhouse wait_queue_head_t fw_wq; 12790937231SDavid Woodhouse int using_dma; 1284dbedf43SNathan Williams int fpga_version; 1294dbedf43SNathan Williams int buffer_size; 1309c54004eSDavid Woodhouse }; 1319c54004eSDavid Woodhouse 13201e2ffacSDavid Woodhouse 13301e2ffacSDavid Woodhouse struct solos_param { 13401e2ffacSDavid Woodhouse struct list_head list; 13501e2ffacSDavid Woodhouse pid_t pid; 13601e2ffacSDavid Woodhouse int port; 13701e2ffacSDavid Woodhouse struct sk_buff *response; 1389c54004eSDavid Woodhouse }; 1399c54004eSDavid Woodhouse 1409c54004eSDavid Woodhouse #define SOLOS_CHAN(atmdev) ((int)(unsigned long)(atmdev)->phy_data) 1419c54004eSDavid Woodhouse 1429c54004eSDavid Woodhouse MODULE_AUTHOR("Traverse Technologies <support@traverse.com.au>"); 1439c54004eSDavid Woodhouse MODULE_DESCRIPTION("Solos PCI driver"); 1449c54004eSDavid Woodhouse MODULE_VERSION(VERSION); 1459c54004eSDavid Woodhouse MODULE_LICENSE("GPL"); 1469fca79d6SBen Hutchings MODULE_FIRMWARE("solos-FPGA.bin"); 1479fca79d6SBen Hutchings MODULE_FIRMWARE("solos-Firmware.bin"); 1489fca79d6SBen Hutchings MODULE_FIRMWARE("solos-db-FPGA.bin"); 149cc3657e1SDavid Woodhouse MODULE_PARM_DESC(reset, "Reset Solos chips on startup"); 1509c54004eSDavid Woodhouse MODULE_PARM_DESC(atmdebug, "Print ATM data"); 1517c4015bdSSimon Farnsworth MODULE_PARM_DESC(firmware_upgrade, "Initiate Solos firmware upgrade"); 1527c4015bdSSimon Farnsworth MODULE_PARM_DESC(fpga_upgrade, "Initiate FPGA upgrade"); 1534dbedf43SNathan Williams MODULE_PARM_DESC(db_firmware_upgrade, "Initiate daughter board Solos firmware upgrade"); 1544dbedf43SNathan Williams MODULE_PARM_DESC(db_fpga_upgrade, "Initiate daughter board FPGA upgrade"); 155cc3657e1SDavid Woodhouse module_param(reset, int, 0444); 1564306cad6SSimon Farnsworth module_param(atmdebug, int, 0644); 1577c4015bdSSimon Farnsworth module_param(firmware_upgrade, int, 0444); 1587c4015bdSSimon Farnsworth module_param(fpga_upgrade, int, 0444); 1594dbedf43SNathan Williams module_param(db_firmware_upgrade, int, 0444); 1604dbedf43SNathan Williams module_param(db_fpga_upgrade, int, 0444); 1619c54004eSDavid Woodhouse 1629c54004eSDavid Woodhouse static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb, 1639c54004eSDavid Woodhouse struct atm_vcc *vcc); 16435c2221bSDavid Woodhouse static uint32_t fpga_tx(struct solos_card *); 1659c54004eSDavid Woodhouse static irqreturn_t solos_irq(int irq, void *dev_id); 1669c54004eSDavid Woodhouse static struct atm_vcc* find_vcc(struct atm_dev *dev, short vpi, int vci); 1679c54004eSDavid Woodhouse static int list_vccs(int vci); 1681e615df6SDavid Woodhouse static void release_vccs(struct atm_dev *dev); 1699c54004eSDavid Woodhouse static int atm_init(struct solos_card *); 1709c54004eSDavid Woodhouse static void atm_remove(struct solos_card *); 1719c54004eSDavid Woodhouse static int send_command(struct solos_card *card, int dev, const char *buf, size_t size); 1729c54004eSDavid Woodhouse static void solos_bh(unsigned long); 1739c54004eSDavid Woodhouse static int print_buffer(struct sk_buff *buf); 1749c54004eSDavid Woodhouse 1759c54004eSDavid Woodhouse static inline void solos_pop(struct atm_vcc *vcc, struct sk_buff *skb) 1769c54004eSDavid Woodhouse { 1779c54004eSDavid Woodhouse if (vcc->pop) 1789c54004eSDavid Woodhouse vcc->pop(vcc, skb); 1799c54004eSDavid Woodhouse else 1809c54004eSDavid Woodhouse dev_kfree_skb_any(skb); 1819c54004eSDavid Woodhouse } 1829c54004eSDavid Woodhouse 18301e2ffacSDavid Woodhouse static ssize_t solos_param_show(struct device *dev, struct device_attribute *attr, 18401e2ffacSDavid Woodhouse char *buf) 18501e2ffacSDavid Woodhouse { 18601e2ffacSDavid Woodhouse struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev); 18701e2ffacSDavid Woodhouse struct solos_card *card = atmdev->dev_data; 18801e2ffacSDavid Woodhouse struct solos_param prm; 18901e2ffacSDavid Woodhouse struct sk_buff *skb; 19001e2ffacSDavid Woodhouse struct pkt_hdr *header; 19101e2ffacSDavid Woodhouse int buflen; 19201e2ffacSDavid Woodhouse 19301e2ffacSDavid Woodhouse buflen = strlen(attr->attr.name) + 10; 19401e2ffacSDavid Woodhouse 1953456b221SDavid Woodhouse skb = alloc_skb(sizeof(*header) + buflen, GFP_KERNEL); 19601e2ffacSDavid Woodhouse if (!skb) { 19701e2ffacSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in solos_param_show()\n"); 19801e2ffacSDavid Woodhouse return -ENOMEM; 19901e2ffacSDavid Woodhouse } 20001e2ffacSDavid Woodhouse 20101e2ffacSDavid Woodhouse header = (void *)skb_put(skb, sizeof(*header)); 20201e2ffacSDavid Woodhouse 20301e2ffacSDavid Woodhouse buflen = snprintf((void *)&header[1], buflen - 1, 20401e2ffacSDavid Woodhouse "L%05d\n%s\n", current->pid, attr->attr.name); 20501e2ffacSDavid Woodhouse skb_put(skb, buflen); 20601e2ffacSDavid Woodhouse 20701e2ffacSDavid Woodhouse header->size = cpu_to_le16(buflen); 20801e2ffacSDavid Woodhouse header->vpi = cpu_to_le16(0); 20901e2ffacSDavid Woodhouse header->vci = cpu_to_le16(0); 21001e2ffacSDavid Woodhouse header->type = cpu_to_le16(PKT_COMMAND); 21101e2ffacSDavid Woodhouse 21201e2ffacSDavid Woodhouse prm.pid = current->pid; 21301e2ffacSDavid Woodhouse prm.response = NULL; 21401e2ffacSDavid Woodhouse prm.port = SOLOS_CHAN(atmdev); 21501e2ffacSDavid Woodhouse 21601e2ffacSDavid Woodhouse spin_lock_irq(&card->param_queue_lock); 21701e2ffacSDavid Woodhouse list_add(&prm.list, &card->param_queue); 21801e2ffacSDavid Woodhouse spin_unlock_irq(&card->param_queue_lock); 21901e2ffacSDavid Woodhouse 22001e2ffacSDavid Woodhouse fpga_queue(card, prm.port, skb, NULL); 22101e2ffacSDavid Woodhouse 22201e2ffacSDavid Woodhouse wait_event_timeout(card->param_wq, prm.response, 5 * HZ); 22301e2ffacSDavid Woodhouse 22401e2ffacSDavid Woodhouse spin_lock_irq(&card->param_queue_lock); 22501e2ffacSDavid Woodhouse list_del(&prm.list); 22601e2ffacSDavid Woodhouse spin_unlock_irq(&card->param_queue_lock); 22701e2ffacSDavid Woodhouse 22801e2ffacSDavid Woodhouse if (!prm.response) 22901e2ffacSDavid Woodhouse return -EIO; 23001e2ffacSDavid Woodhouse 23101e2ffacSDavid Woodhouse buflen = prm.response->len; 23201e2ffacSDavid Woodhouse memcpy(buf, prm.response->data, buflen); 23301e2ffacSDavid Woodhouse kfree_skb(prm.response); 23401e2ffacSDavid Woodhouse 23501e2ffacSDavid Woodhouse return buflen; 23601e2ffacSDavid Woodhouse } 23701e2ffacSDavid Woodhouse 23801e2ffacSDavid Woodhouse static ssize_t solos_param_store(struct device *dev, struct device_attribute *attr, 23901e2ffacSDavid Woodhouse const char *buf, size_t count) 24001e2ffacSDavid Woodhouse { 24101e2ffacSDavid Woodhouse struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev); 24201e2ffacSDavid Woodhouse struct solos_card *card = atmdev->dev_data; 24301e2ffacSDavid Woodhouse struct solos_param prm; 24401e2ffacSDavid Woodhouse struct sk_buff *skb; 24501e2ffacSDavid Woodhouse struct pkt_hdr *header; 24601e2ffacSDavid Woodhouse int buflen; 24701e2ffacSDavid Woodhouse ssize_t ret; 24801e2ffacSDavid Woodhouse 24901e2ffacSDavid Woodhouse buflen = strlen(attr->attr.name) + 11 + count; 25001e2ffacSDavid Woodhouse 2513456b221SDavid Woodhouse skb = alloc_skb(sizeof(*header) + buflen, GFP_KERNEL); 25201e2ffacSDavid Woodhouse if (!skb) { 25301e2ffacSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in solos_param_store()\n"); 25401e2ffacSDavid Woodhouse return -ENOMEM; 25501e2ffacSDavid Woodhouse } 25601e2ffacSDavid Woodhouse 25701e2ffacSDavid Woodhouse header = (void *)skb_put(skb, sizeof(*header)); 25801e2ffacSDavid Woodhouse 25901e2ffacSDavid Woodhouse buflen = snprintf((void *)&header[1], buflen - 1, 26001e2ffacSDavid Woodhouse "L%05d\n%s\n%s\n", current->pid, attr->attr.name, buf); 26101e2ffacSDavid Woodhouse 26201e2ffacSDavid Woodhouse skb_put(skb, buflen); 26301e2ffacSDavid Woodhouse header->size = cpu_to_le16(buflen); 26401e2ffacSDavid Woodhouse header->vpi = cpu_to_le16(0); 26501e2ffacSDavid Woodhouse header->vci = cpu_to_le16(0); 26601e2ffacSDavid Woodhouse header->type = cpu_to_le16(PKT_COMMAND); 26701e2ffacSDavid Woodhouse 26801e2ffacSDavid Woodhouse prm.pid = current->pid; 26901e2ffacSDavid Woodhouse prm.response = NULL; 27001e2ffacSDavid Woodhouse prm.port = SOLOS_CHAN(atmdev); 27101e2ffacSDavid Woodhouse 27201e2ffacSDavid Woodhouse spin_lock_irq(&card->param_queue_lock); 27301e2ffacSDavid Woodhouse list_add(&prm.list, &card->param_queue); 27401e2ffacSDavid Woodhouse spin_unlock_irq(&card->param_queue_lock); 27501e2ffacSDavid Woodhouse 27601e2ffacSDavid Woodhouse fpga_queue(card, prm.port, skb, NULL); 27701e2ffacSDavid Woodhouse 27801e2ffacSDavid Woodhouse wait_event_timeout(card->param_wq, prm.response, 5 * HZ); 27901e2ffacSDavid Woodhouse 28001e2ffacSDavid Woodhouse spin_lock_irq(&card->param_queue_lock); 28101e2ffacSDavid Woodhouse list_del(&prm.list); 28201e2ffacSDavid Woodhouse spin_unlock_irq(&card->param_queue_lock); 28301e2ffacSDavid Woodhouse 28401e2ffacSDavid Woodhouse skb = prm.response; 28501e2ffacSDavid Woodhouse 28601e2ffacSDavid Woodhouse if (!skb) 28701e2ffacSDavid Woodhouse return -EIO; 28801e2ffacSDavid Woodhouse 28901e2ffacSDavid Woodhouse buflen = skb->len; 29001e2ffacSDavid Woodhouse 29101e2ffacSDavid Woodhouse /* Sometimes it has a newline, sometimes it doesn't. */ 29201e2ffacSDavid Woodhouse if (skb->data[buflen - 1] == '\n') 29301e2ffacSDavid Woodhouse buflen--; 29401e2ffacSDavid Woodhouse 29501e2ffacSDavid Woodhouse if (buflen == 2 && !strncmp(skb->data, "OK", 2)) 29601e2ffacSDavid Woodhouse ret = count; 29701e2ffacSDavid Woodhouse else if (buflen == 5 && !strncmp(skb->data, "ERROR", 5)) 29801e2ffacSDavid Woodhouse ret = -EIO; 29901e2ffacSDavid Woodhouse else { 30001e2ffacSDavid Woodhouse /* We know we have enough space allocated for this; we allocated 30101e2ffacSDavid Woodhouse it ourselves */ 30201e2ffacSDavid Woodhouse skb->data[buflen] = 0; 30301e2ffacSDavid Woodhouse 30401e2ffacSDavid Woodhouse dev_warn(&card->dev->dev, "Unexpected parameter response: '%s'\n", 30501e2ffacSDavid Woodhouse skb->data); 30601e2ffacSDavid Woodhouse ret = -EIO; 30701e2ffacSDavid Woodhouse } 30801e2ffacSDavid Woodhouse kfree_skb(skb); 30901e2ffacSDavid Woodhouse 31001e2ffacSDavid Woodhouse return ret; 31101e2ffacSDavid Woodhouse } 31201e2ffacSDavid Woodhouse 31387ebb186SDavid Woodhouse static char *next_string(struct sk_buff *skb) 31487ebb186SDavid Woodhouse { 31587ebb186SDavid Woodhouse int i = 0; 31687ebb186SDavid Woodhouse char *this = skb->data; 31787ebb186SDavid Woodhouse 318c6428e52SDavid Woodhouse for (i = 0; i < skb->len; i++) { 31987ebb186SDavid Woodhouse if (this[i] == '\n') { 32087ebb186SDavid Woodhouse this[i] = 0; 321c6428e52SDavid Woodhouse skb_pull(skb, i + 1); 32287ebb186SDavid Woodhouse return this; 32387ebb186SDavid Woodhouse } 324c6428e52SDavid Woodhouse if (!isprint(this[i])) 325c6428e52SDavid Woodhouse return NULL; 32687ebb186SDavid Woodhouse } 32787ebb186SDavid Woodhouse return NULL; 32887ebb186SDavid Woodhouse } 32987ebb186SDavid Woodhouse 33087ebb186SDavid Woodhouse /* 33187ebb186SDavid Woodhouse * Status packet has fields separated by \n, starting with a version number 33287ebb186SDavid Woodhouse * for the information therein. Fields are.... 33387ebb186SDavid Woodhouse * 33487ebb186SDavid Woodhouse * packet version 33587ebb186SDavid Woodhouse * RxBitRate (version >= 1) 336f87b2ed2SDavid Woodhouse * TxBitRate (version >= 1) 33787ebb186SDavid Woodhouse * State (version >= 1) 338f87b2ed2SDavid Woodhouse * LocalSNRMargin (version >= 1) 339f87b2ed2SDavid Woodhouse * LocalLineAttn (version >= 1) 34087ebb186SDavid Woodhouse */ 34187ebb186SDavid Woodhouse static int process_status(struct solos_card *card, int port, struct sk_buff *skb) 34287ebb186SDavid Woodhouse { 343f87b2ed2SDavid Woodhouse char *str, *end, *state_str, *snr, *attn; 344f87b2ed2SDavid Woodhouse int ver, rate_up, rate_down; 34587ebb186SDavid Woodhouse 34687ebb186SDavid Woodhouse if (!card->atmdev[port]) 34787ebb186SDavid Woodhouse return -ENODEV; 34887ebb186SDavid Woodhouse 34987ebb186SDavid Woodhouse str = next_string(skb); 35087ebb186SDavid Woodhouse if (!str) 35187ebb186SDavid Woodhouse return -EIO; 35287ebb186SDavid Woodhouse 35387ebb186SDavid Woodhouse ver = simple_strtol(str, NULL, 10); 35487ebb186SDavid Woodhouse if (ver < 1) { 35587ebb186SDavid Woodhouse dev_warn(&card->dev->dev, "Unexpected status interrupt version %d\n", 35687ebb186SDavid Woodhouse ver); 35787ebb186SDavid Woodhouse return -EIO; 35887ebb186SDavid Woodhouse } 35987ebb186SDavid Woodhouse 36087ebb186SDavid Woodhouse str = next_string(skb); 361c6428e52SDavid Woodhouse if (!str) 362c6428e52SDavid Woodhouse return -EIO; 36395852f48SDavid Woodhouse if (!strcmp(str, "ERROR")) { 36495852f48SDavid Woodhouse dev_dbg(&card->dev->dev, "Status packet indicated Solos error on port %d (starting up?)\n", 36595852f48SDavid Woodhouse port); 36695852f48SDavid Woodhouse return 0; 36795852f48SDavid Woodhouse } 36895852f48SDavid Woodhouse 369f87b2ed2SDavid Woodhouse rate_down = simple_strtol(str, &end, 10); 37087ebb186SDavid Woodhouse if (*end) 37187ebb186SDavid Woodhouse return -EIO; 37287ebb186SDavid Woodhouse 37387ebb186SDavid Woodhouse str = next_string(skb); 374c6428e52SDavid Woodhouse if (!str) 375c6428e52SDavid Woodhouse return -EIO; 376f87b2ed2SDavid Woodhouse rate_up = simple_strtol(str, &end, 10); 37787ebb186SDavid Woodhouse if (*end) 37887ebb186SDavid Woodhouse return -EIO; 37987ebb186SDavid Woodhouse 380af780656SDavid Woodhouse state_str = next_string(skb); 381c6428e52SDavid Woodhouse if (!state_str) 382c6428e52SDavid Woodhouse return -EIO; 38387ebb186SDavid Woodhouse 384f87b2ed2SDavid Woodhouse /* Anything but 'Showtime' is down */ 385f87b2ed2SDavid Woodhouse if (strcmp(state_str, "Showtime")) { 38649d49106SKarl Hiramoto atm_dev_signal_change(card->atmdev[port], ATM_PHY_SIG_LOST); 387f87b2ed2SDavid Woodhouse release_vccs(card->atmdev[port]); 38895852f48SDavid Woodhouse dev_info(&card->dev->dev, "Port %d: %s\n", port, state_str); 389f87b2ed2SDavid Woodhouse return 0; 390f87b2ed2SDavid Woodhouse } 391af780656SDavid Woodhouse 392c6428e52SDavid Woodhouse snr = next_string(skb); 3936cf5767cSJulia Lawall if (!snr) 394c6428e52SDavid Woodhouse return -EIO; 395c6428e52SDavid Woodhouse attn = next_string(skb); 396c6428e52SDavid Woodhouse if (!attn) 397c6428e52SDavid Woodhouse return -EIO; 398c6428e52SDavid Woodhouse 39995852f48SDavid Woodhouse dev_info(&card->dev->dev, "Port %d: %s @%d/%d kb/s%s%s%s%s\n", 400c6428e52SDavid Woodhouse port, state_str, rate_down/1000, rate_up/1000, 401c6428e52SDavid Woodhouse snr[0]?", SNR ":"", snr, attn[0]?", Attn ":"", attn); 402f87b2ed2SDavid Woodhouse 403c6428e52SDavid Woodhouse card->atmdev[port]->link_rate = rate_down / 424; 40449d49106SKarl Hiramoto atm_dev_signal_change(card->atmdev[port], ATM_PHY_SIG_FOUND); 40587ebb186SDavid Woodhouse 40687ebb186SDavid Woodhouse return 0; 40787ebb186SDavid Woodhouse } 40887ebb186SDavid Woodhouse 40901e2ffacSDavid Woodhouse static int process_command(struct solos_card *card, int port, struct sk_buff *skb) 41001e2ffacSDavid Woodhouse { 41101e2ffacSDavid Woodhouse struct solos_param *prm; 41201e2ffacSDavid Woodhouse unsigned long flags; 41301e2ffacSDavid Woodhouse int cmdpid; 41401e2ffacSDavid Woodhouse int found = 0; 41501e2ffacSDavid Woodhouse 41601e2ffacSDavid Woodhouse if (skb->len < 7) 41701e2ffacSDavid Woodhouse return 0; 41801e2ffacSDavid Woodhouse 41901e2ffacSDavid Woodhouse if (skb->data[0] != 'L' || !isdigit(skb->data[1]) || 42001e2ffacSDavid Woodhouse !isdigit(skb->data[2]) || !isdigit(skb->data[3]) || 42101e2ffacSDavid Woodhouse !isdigit(skb->data[4]) || !isdigit(skb->data[5]) || 42201e2ffacSDavid Woodhouse skb->data[6] != '\n') 42301e2ffacSDavid Woodhouse return 0; 42401e2ffacSDavid Woodhouse 42501e2ffacSDavid Woodhouse cmdpid = simple_strtol(&skb->data[1], NULL, 10); 42601e2ffacSDavid Woodhouse 42701e2ffacSDavid Woodhouse spin_lock_irqsave(&card->param_queue_lock, flags); 42801e2ffacSDavid Woodhouse list_for_each_entry(prm, &card->param_queue, list) { 42901e2ffacSDavid Woodhouse if (prm->port == port && prm->pid == cmdpid) { 43001e2ffacSDavid Woodhouse prm->response = skb; 43101e2ffacSDavid Woodhouse skb_pull(skb, 7); 43201e2ffacSDavid Woodhouse wake_up(&card->param_wq); 43301e2ffacSDavid Woodhouse found = 1; 43401e2ffacSDavid Woodhouse break; 43501e2ffacSDavid Woodhouse } 43601e2ffacSDavid Woodhouse } 43701e2ffacSDavid Woodhouse spin_unlock_irqrestore(&card->param_queue_lock, flags); 43801e2ffacSDavid Woodhouse return found; 43901e2ffacSDavid Woodhouse } 44001e2ffacSDavid Woodhouse 4419c54004eSDavid Woodhouse static ssize_t console_show(struct device *dev, struct device_attribute *attr, 4429c54004eSDavid Woodhouse char *buf) 4439c54004eSDavid Woodhouse { 4449c54004eSDavid Woodhouse struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev); 4459c54004eSDavid Woodhouse struct solos_card *card = atmdev->dev_data; 4469c54004eSDavid Woodhouse struct sk_buff *skb; 4479c54004eSDavid Woodhouse 4489c54004eSDavid Woodhouse spin_lock(&card->cli_queue_lock); 4499c54004eSDavid Woodhouse skb = skb_dequeue(&card->cli_queue[SOLOS_CHAN(atmdev)]); 4509c54004eSDavid Woodhouse spin_unlock(&card->cli_queue_lock); 4519c54004eSDavid Woodhouse if(skb == NULL) 4529c54004eSDavid Woodhouse return sprintf(buf, "No data.\n"); 4539c54004eSDavid Woodhouse 4549c54004eSDavid Woodhouse memcpy(buf, skb->data, skb->len); 4559c54004eSDavid Woodhouse dev_dbg(&card->dev->dev, "len: %d\n", skb->len); 4569c54004eSDavid Woodhouse 4579c54004eSDavid Woodhouse kfree_skb(skb); 4589c54004eSDavid Woodhouse return skb->len; 4599c54004eSDavid Woodhouse } 4609c54004eSDavid Woodhouse 4619c54004eSDavid Woodhouse static int send_command(struct solos_card *card, int dev, const char *buf, size_t size) 4629c54004eSDavid Woodhouse { 4639c54004eSDavid Woodhouse struct sk_buff *skb; 4649c54004eSDavid Woodhouse struct pkt_hdr *header; 4659c54004eSDavid Woodhouse 4669c54004eSDavid Woodhouse if (size > (BUF_SIZE - sizeof(*header))) { 4679c54004eSDavid Woodhouse dev_dbg(&card->dev->dev, "Command is too big. Dropping request\n"); 4689c54004eSDavid Woodhouse return 0; 4699c54004eSDavid Woodhouse } 4709c54004eSDavid Woodhouse skb = alloc_skb(size + sizeof(*header), GFP_ATOMIC); 4719c54004eSDavid Woodhouse if (!skb) { 4729c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in send_command()\n"); 4739c54004eSDavid Woodhouse return 0; 4749c54004eSDavid Woodhouse } 4759c54004eSDavid Woodhouse 4769c54004eSDavid Woodhouse header = (void *)skb_put(skb, sizeof(*header)); 4779c54004eSDavid Woodhouse 4789c54004eSDavid Woodhouse header->size = cpu_to_le16(size); 4799c54004eSDavid Woodhouse header->vpi = cpu_to_le16(0); 4809c54004eSDavid Woodhouse header->vci = cpu_to_le16(0); 4819c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_COMMAND); 4829c54004eSDavid Woodhouse 4839c54004eSDavid Woodhouse memcpy(skb_put(skb, size), buf, size); 4849c54004eSDavid Woodhouse 4859c54004eSDavid Woodhouse fpga_queue(card, dev, skb, NULL); 4869c54004eSDavid Woodhouse 4879c54004eSDavid Woodhouse return 0; 4889c54004eSDavid Woodhouse } 4899c54004eSDavid Woodhouse 4909c54004eSDavid Woodhouse static ssize_t console_store(struct device *dev, struct device_attribute *attr, 4919c54004eSDavid Woodhouse const char *buf, size_t count) 4929c54004eSDavid Woodhouse { 4939c54004eSDavid Woodhouse struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev); 4949c54004eSDavid Woodhouse struct solos_card *card = atmdev->dev_data; 4959c54004eSDavid Woodhouse int err; 4969c54004eSDavid Woodhouse 4979c54004eSDavid Woodhouse err = send_command(card, SOLOS_CHAN(atmdev), buf, count); 4989c54004eSDavid Woodhouse 4999c54004eSDavid Woodhouse return err?:count; 5009c54004eSDavid Woodhouse } 5019c54004eSDavid Woodhouse 5029c54004eSDavid Woodhouse static DEVICE_ATTR(console, 0644, console_show, console_store); 5039c54004eSDavid Woodhouse 504d057f0a4SDavid Woodhouse 505d057f0a4SDavid Woodhouse #define SOLOS_ATTR_RO(x) static DEVICE_ATTR(x, 0444, solos_param_show, NULL); 506d057f0a4SDavid Woodhouse #define SOLOS_ATTR_RW(x) static DEVICE_ATTR(x, 0644, solos_param_show, solos_param_store); 507d057f0a4SDavid Woodhouse 508d057f0a4SDavid Woodhouse #include "solos-attrlist.c" 509d057f0a4SDavid Woodhouse 510d057f0a4SDavid Woodhouse #undef SOLOS_ATTR_RO 511d057f0a4SDavid Woodhouse #undef SOLOS_ATTR_RW 512d057f0a4SDavid Woodhouse 513d057f0a4SDavid Woodhouse #define SOLOS_ATTR_RO(x) &dev_attr_##x.attr, 514d057f0a4SDavid Woodhouse #define SOLOS_ATTR_RW(x) &dev_attr_##x.attr, 515d057f0a4SDavid Woodhouse 516d057f0a4SDavid Woodhouse static struct attribute *solos_attrs[] = { 517d057f0a4SDavid Woodhouse #include "solos-attrlist.c" 518d057f0a4SDavid Woodhouse NULL 519d057f0a4SDavid Woodhouse }; 520d057f0a4SDavid Woodhouse 521d057f0a4SDavid Woodhouse static struct attribute_group solos_attr_group = { 522d057f0a4SDavid Woodhouse .attrs = solos_attrs, 523d057f0a4SDavid Woodhouse .name = "parameters", 524d057f0a4SDavid Woodhouse }; 5259c54004eSDavid Woodhouse 526fa755b9fSDavid Woodhouse static int flash_upgrade(struct solos_card *card, int chip) 527fa755b9fSDavid Woodhouse { 528fa755b9fSDavid Woodhouse const struct firmware *fw; 529fa755b9fSDavid Woodhouse const char *fw_name; 5307c4015bdSSimon Farnsworth uint32_t data32 = 0; 5317c4015bdSSimon Farnsworth int blocksize = 0; 5327c4015bdSSimon Farnsworth int numblocks = 0; 533fa755b9fSDavid Woodhouse int offset; 534fa755b9fSDavid Woodhouse 5357adcdb4cSAndrew Morton switch (chip) { 5367adcdb4cSAndrew Morton case 0: 537fa755b9fSDavid Woodhouse fw_name = "solos-FPGA.bin"; 5387c4015bdSSimon Farnsworth blocksize = FPGA_BLOCK; 5397adcdb4cSAndrew Morton break; 5407adcdb4cSAndrew Morton case 1: 541fa755b9fSDavid Woodhouse fw_name = "solos-Firmware.bin"; 5427c4015bdSSimon Farnsworth blocksize = SOLOS_BLOCK; 5437adcdb4cSAndrew Morton break; 5447adcdb4cSAndrew Morton case 2: 5454dbedf43SNathan Williams if (card->fpga_version > LEGACY_BUFFERS){ 5464dbedf43SNathan Williams fw_name = "solos-db-FPGA.bin"; 5474dbedf43SNathan Williams blocksize = FPGA_BLOCK; 5484dbedf43SNathan Williams } else { 5497adcdb4cSAndrew Morton dev_info(&card->dev->dev, "FPGA version doesn't support" 5507adcdb4cSAndrew Morton " daughter board upgrades\n"); 5514dbedf43SNathan Williams return -EPERM; 5524dbedf43SNathan Williams } 5537adcdb4cSAndrew Morton break; 5547adcdb4cSAndrew Morton case 3: 5554dbedf43SNathan Williams if (card->fpga_version > LEGACY_BUFFERS){ 5564dbedf43SNathan Williams fw_name = "solos-Firmware.bin"; 5574dbedf43SNathan Williams blocksize = SOLOS_BLOCK; 5584dbedf43SNathan Williams } else { 5597adcdb4cSAndrew Morton dev_info(&card->dev->dev, "FPGA version doesn't support" 5607adcdb4cSAndrew Morton " daughter board upgrades\n"); 5614dbedf43SNathan Williams return -EPERM; 5624dbedf43SNathan Williams } 5637adcdb4cSAndrew Morton break; 5647adcdb4cSAndrew Morton default: 5657adcdb4cSAndrew Morton return -ENODEV; 5664dbedf43SNathan Williams } 5674dbedf43SNathan Williams 568fa755b9fSDavid Woodhouse if (request_firmware(&fw, fw_name, &card->dev->dev)) 569fa755b9fSDavid Woodhouse return -ENOENT; 570fa755b9fSDavid Woodhouse 571fa755b9fSDavid Woodhouse dev_info(&card->dev->dev, "Flash upgrade starting\n"); 572fa755b9fSDavid Woodhouse 573fa755b9fSDavid Woodhouse numblocks = fw->size / blocksize; 574fa755b9fSDavid Woodhouse dev_info(&card->dev->dev, "Firmware size: %zd\n", fw->size); 575fa755b9fSDavid Woodhouse dev_info(&card->dev->dev, "Number of blocks: %d\n", numblocks); 5767c4015bdSSimon Farnsworth 5777c4015bdSSimon Farnsworth dev_info(&card->dev->dev, "Changing FPGA to Update mode\n"); 5787c4015bdSSimon Farnsworth iowrite32(1, card->config_regs + FPGA_MODE); 5797c4015bdSSimon Farnsworth data32 = ioread32(card->config_regs + FPGA_MODE); 5807c4015bdSSimon Farnsworth 581fa755b9fSDavid Woodhouse /* Set mode to Chip Erase */ 5824dbedf43SNathan Williams if(chip == 0 || chip == 2) 5834dbedf43SNathan Williams dev_info(&card->dev->dev, "Set FPGA Flash mode to FPGA Chip Erase\n"); 5844dbedf43SNathan Williams if(chip == 1 || chip == 3) 5854dbedf43SNathan Williams dev_info(&card->dev->dev, "Set FPGA Flash mode to Solos Chip Erase\n"); 586fa755b9fSDavid Woodhouse iowrite32((chip * 2), card->config_regs + FLASH_MODE); 587fa755b9fSDavid Woodhouse 588fa755b9fSDavid Woodhouse 589fa755b9fSDavid Woodhouse iowrite32(1, card->config_regs + WRITE_FLASH); 590fa755b9fSDavid Woodhouse wait_event(card->fw_wq, !ioread32(card->config_regs + FLASH_BUSY)); 591fa755b9fSDavid Woodhouse 592fa755b9fSDavid Woodhouse for (offset = 0; offset < fw->size; offset += blocksize) { 5937c4015bdSSimon Farnsworth int i; 5947c4015bdSSimon Farnsworth 5957c4015bdSSimon Farnsworth /* Clear write flag */ 5967c4015bdSSimon Farnsworth iowrite32(0, card->config_regs + WRITE_FLASH); 597fa755b9fSDavid Woodhouse 5987c4015bdSSimon Farnsworth /* Set mode to Block Write */ 5997c4015bdSSimon Farnsworth /* dev_info(&card->dev->dev, "Set FPGA Flash mode to Block Write\n"); */ 600fa755b9fSDavid Woodhouse iowrite32(((chip * 2) + 1), card->config_regs + FLASH_MODE); 6017c4015bdSSimon Farnsworth 602fa755b9fSDavid Woodhouse /* Copy block to buffer, swapping each 16 bits */ 603fa755b9fSDavid Woodhouse for(i = 0; i < blocksize; i += 4) { 604fa755b9fSDavid Woodhouse uint32_t word = swahb32p((uint32_t *)(fw->data + offset + i)); 6054dbedf43SNathan Williams if(card->fpga_version > LEGACY_BUFFERS) 6064dbedf43SNathan Williams iowrite32(word, FLASH_BUF + i); 6074dbedf43SNathan Williams else 608fa755b9fSDavid Woodhouse iowrite32(word, RX_BUF(card, 3) + i); 609fa755b9fSDavid Woodhouse } 610fa755b9fSDavid Woodhouse 611fa755b9fSDavid Woodhouse /* Specify block number and then trigger flash write */ 612fa755b9fSDavid Woodhouse iowrite32(offset / blocksize, card->config_regs + FLASH_BLOCK); 613fa755b9fSDavid Woodhouse iowrite32(1, card->config_regs + WRITE_FLASH); 614fa755b9fSDavid Woodhouse wait_event(card->fw_wq, !ioread32(card->config_regs + FLASH_BUSY)); 615fa755b9fSDavid Woodhouse } 616fa755b9fSDavid Woodhouse 617fa755b9fSDavid Woodhouse release_firmware(fw); 6187c4015bdSSimon Farnsworth iowrite32(0, card->config_regs + WRITE_FLASH); 6197c4015bdSSimon Farnsworth iowrite32(0, card->config_regs + FPGA_MODE); 6207c4015bdSSimon Farnsworth iowrite32(0, card->config_regs + FLASH_MODE); 6217c4015bdSSimon Farnsworth dev_info(&card->dev->dev, "Returning FPGA to Data mode\n"); 622fa755b9fSDavid Woodhouse return 0; 6237c4015bdSSimon Farnsworth } 6247c4015bdSSimon Farnsworth 6259c54004eSDavid Woodhouse static irqreturn_t solos_irq(int irq, void *dev_id) 6269c54004eSDavid Woodhouse { 6279c54004eSDavid Woodhouse struct solos_card *card = dev_id; 6289c54004eSDavid Woodhouse int handled = 1; 6299c54004eSDavid Woodhouse 6309c54004eSDavid Woodhouse iowrite32(0, card->config_regs + IRQ_CLEAR); 6319c54004eSDavid Woodhouse 63235c2221bSDavid Woodhouse /* If we're up and running, just kick the tasklet to process TX/RX */ 633fa755b9fSDavid Woodhouse if (card->atmdev[0]) 6349c54004eSDavid Woodhouse tasklet_schedule(&card->tlet); 635fa755b9fSDavid Woodhouse else 636fa755b9fSDavid Woodhouse wake_up(&card->fw_wq); 6379c54004eSDavid Woodhouse 6389c54004eSDavid Woodhouse return IRQ_RETVAL(handled); 6399c54004eSDavid Woodhouse } 6409c54004eSDavid Woodhouse 6419c54004eSDavid Woodhouse void solos_bh(unsigned long card_arg) 6429c54004eSDavid Woodhouse { 6439c54004eSDavid Woodhouse struct solos_card *card = (void *)card_arg; 6449c54004eSDavid Woodhouse uint32_t card_flags; 6459c54004eSDavid Woodhouse uint32_t rx_done = 0; 64635c2221bSDavid Woodhouse int port; 6479c54004eSDavid Woodhouse 64835c2221bSDavid Woodhouse /* 64935c2221bSDavid Woodhouse * Since fpga_tx() is going to need to read the flags under its lock, 65035c2221bSDavid Woodhouse * it can return them to us so that we don't have to hit PCI MMIO 65135c2221bSDavid Woodhouse * again for the same information 65235c2221bSDavid Woodhouse */ 65335c2221bSDavid Woodhouse card_flags = fpga_tx(card); 6549c54004eSDavid Woodhouse 6559c54004eSDavid Woodhouse for (port = 0; port < card->nr_ports; port++) { 6569c54004eSDavid Woodhouse if (card_flags & (0x10 << port)) { 65790937231SDavid Woodhouse struct pkt_hdr _hdr, *header; 6589c54004eSDavid Woodhouse struct sk_buff *skb; 6599c54004eSDavid Woodhouse struct atm_vcc *vcc; 6609c54004eSDavid Woodhouse int size; 6619c54004eSDavid Woodhouse 66290937231SDavid Woodhouse if (card->using_dma) { 66390937231SDavid Woodhouse skb = card->rx_skb[port]; 664eaf83e39SDavid Woodhouse card->rx_skb[port] = NULL; 66590937231SDavid Woodhouse 666eaf83e39SDavid Woodhouse pci_unmap_single(card->dev, SKB_CB(skb)->dma_addr, 667eaf83e39SDavid Woodhouse RX_DMA_SIZE, PCI_DMA_FROMDEVICE); 668eaf83e39SDavid Woodhouse 66990937231SDavid Woodhouse header = (void *)skb->data; 67090937231SDavid Woodhouse size = le16_to_cpu(header->size); 67190937231SDavid Woodhouse skb_put(skb, size + sizeof(*header)); 67290937231SDavid Woodhouse skb_pull(skb, sizeof(*header)); 67390937231SDavid Woodhouse } else { 67490937231SDavid Woodhouse header = &_hdr; 67590937231SDavid Woodhouse 6769c54004eSDavid Woodhouse rx_done |= 0x10 << port; 6779c54004eSDavid Woodhouse 67890937231SDavid Woodhouse memcpy_fromio(header, RX_BUF(card, port), sizeof(*header)); 6799c54004eSDavid Woodhouse 68090937231SDavid Woodhouse size = le16_to_cpu(header->size); 68178f857f2SNathan Williams if (size > (card->buffer_size - sizeof(*header))){ 68278f857f2SNathan Williams dev_warn(&card->dev->dev, "Invalid buffer size\n"); 68378f857f2SNathan Williams continue; 68478f857f2SNathan Williams } 6859c54004eSDavid Woodhouse 68687ebb186SDavid Woodhouse skb = alloc_skb(size + 1, GFP_ATOMIC); 6879c54004eSDavid Woodhouse if (!skb) { 6889c54004eSDavid Woodhouse if (net_ratelimit()) 6899c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff for RX\n"); 6909c54004eSDavid Woodhouse continue; 6919c54004eSDavid Woodhouse } 6929c54004eSDavid Woodhouse 6939c54004eSDavid Woodhouse memcpy_fromio(skb_put(skb, size), 69490937231SDavid Woodhouse RX_BUF(card, port) + sizeof(*header), 6959c54004eSDavid Woodhouse size); 69690937231SDavid Woodhouse } 6979c54004eSDavid Woodhouse if (atmdebug) { 6989c54004eSDavid Woodhouse dev_info(&card->dev->dev, "Received: device %d\n", port); 6999c54004eSDavid Woodhouse dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n", 70090937231SDavid Woodhouse size, le16_to_cpu(header->vpi), 70190937231SDavid Woodhouse le16_to_cpu(header->vci)); 7029c54004eSDavid Woodhouse print_buffer(skb); 7039c54004eSDavid Woodhouse } 7049c54004eSDavid Woodhouse 70590937231SDavid Woodhouse switch (le16_to_cpu(header->type)) { 7069c54004eSDavid Woodhouse case PKT_DATA: 70790937231SDavid Woodhouse vcc = find_vcc(card->atmdev[port], le16_to_cpu(header->vpi), 70890937231SDavid Woodhouse le16_to_cpu(header->vci)); 7099c54004eSDavid Woodhouse if (!vcc) { 7109c54004eSDavid Woodhouse if (net_ratelimit()) 7119c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Received packet for unknown VCI.VPI %d.%d on port %d\n", 71290937231SDavid Woodhouse le16_to_cpu(header->vci), le16_to_cpu(header->vpi), 7139c54004eSDavid Woodhouse port); 7149c54004eSDavid Woodhouse continue; 7159c54004eSDavid Woodhouse } 7169c54004eSDavid Woodhouse atm_charge(vcc, skb->truesize); 7179c54004eSDavid Woodhouse vcc->push(vcc, skb); 7189c54004eSDavid Woodhouse atomic_inc(&vcc->stats->rx); 7199c54004eSDavid Woodhouse break; 7209c54004eSDavid Woodhouse 72187ebb186SDavid Woodhouse case PKT_STATUS: 72295852f48SDavid Woodhouse if (process_status(card, port, skb) && 72395852f48SDavid Woodhouse net_ratelimit()) { 72495852f48SDavid Woodhouse dev_warn(&card->dev->dev, "Bad status packet of %d bytes on port %d:\n", skb->len, port); 72595852f48SDavid Woodhouse print_buffer(skb); 72695852f48SDavid Woodhouse } 727eaf83e39SDavid Woodhouse dev_kfree_skb_any(skb); 72887ebb186SDavid Woodhouse break; 72987ebb186SDavid Woodhouse 7309c54004eSDavid Woodhouse case PKT_COMMAND: 7319c54004eSDavid Woodhouse default: /* FIXME: Not really, surely? */ 73201e2ffacSDavid Woodhouse if (process_command(card, port, skb)) 73301e2ffacSDavid Woodhouse break; 7349c54004eSDavid Woodhouse spin_lock(&card->cli_queue_lock); 7359c54004eSDavid Woodhouse if (skb_queue_len(&card->cli_queue[port]) > 10) { 7369c54004eSDavid Woodhouse if (net_ratelimit()) 7379c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Dropping console response on port %d\n", 7389c54004eSDavid Woodhouse port); 739eaf83e39SDavid Woodhouse dev_kfree_skb_any(skb); 7409c54004eSDavid Woodhouse } else 7419c54004eSDavid Woodhouse skb_queue_tail(&card->cli_queue[port], skb); 7429c54004eSDavid Woodhouse spin_unlock(&card->cli_queue_lock); 7439c54004eSDavid Woodhouse break; 7449c54004eSDavid Woodhouse } 7459c54004eSDavid Woodhouse } 746eaf83e39SDavid Woodhouse /* Allocate RX skbs for any ports which need them */ 747eaf83e39SDavid Woodhouse if (card->using_dma && card->atmdev[port] && 748eaf83e39SDavid Woodhouse !card->rx_skb[port]) { 749eaf83e39SDavid Woodhouse struct sk_buff *skb = alloc_skb(RX_DMA_SIZE, GFP_ATOMIC); 750eaf83e39SDavid Woodhouse if (skb) { 751eaf83e39SDavid Woodhouse SKB_CB(skb)->dma_addr = 752eaf83e39SDavid Woodhouse pci_map_single(card->dev, skb->data, 753eaf83e39SDavid Woodhouse RX_DMA_SIZE, PCI_DMA_FROMDEVICE); 754eaf83e39SDavid Woodhouse iowrite32(SKB_CB(skb)->dma_addr, 755eaf83e39SDavid Woodhouse card->config_regs + RX_DMA_ADDR(port)); 756eaf83e39SDavid Woodhouse card->rx_skb[port] = skb; 757eaf83e39SDavid Woodhouse } else { 758eaf83e39SDavid Woodhouse if (net_ratelimit()) 759eaf83e39SDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate RX skb"); 760eaf83e39SDavid Woodhouse 761eaf83e39SDavid Woodhouse /* We'll have to try again later */ 762eaf83e39SDavid Woodhouse tasklet_schedule(&card->tlet); 763eaf83e39SDavid Woodhouse } 764eaf83e39SDavid Woodhouse } 7659c54004eSDavid Woodhouse } 7669c54004eSDavid Woodhouse if (rx_done) 7679c54004eSDavid Woodhouse iowrite32(rx_done, card->config_regs + FLAGS_ADDR); 7689c54004eSDavid Woodhouse 7699c54004eSDavid Woodhouse return; 7709c54004eSDavid Woodhouse } 7719c54004eSDavid Woodhouse 7729c54004eSDavid Woodhouse static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci) 7739c54004eSDavid Woodhouse { 7749c54004eSDavid Woodhouse struct hlist_head *head; 7759c54004eSDavid Woodhouse struct atm_vcc *vcc = NULL; 7769c54004eSDavid Woodhouse struct hlist_node *node; 7779c54004eSDavid Woodhouse struct sock *s; 7789c54004eSDavid Woodhouse 7799c54004eSDavid Woodhouse read_lock(&vcc_sklist_lock); 7809c54004eSDavid Woodhouse head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; 7819c54004eSDavid Woodhouse sk_for_each(s, node, head) { 7829c54004eSDavid Woodhouse vcc = atm_sk(s); 7839c54004eSDavid Woodhouse if (vcc->dev == dev && vcc->vci == vci && 7841f6ea6e5SDavid Woodhouse vcc->vpi == vpi && vcc->qos.rxtp.traffic_class != ATM_NONE && 7851f6ea6e5SDavid Woodhouse test_bit(ATM_VF_READY, &vcc->flags)) 7869c54004eSDavid Woodhouse goto out; 7879c54004eSDavid Woodhouse } 7889c54004eSDavid Woodhouse vcc = NULL; 7899c54004eSDavid Woodhouse out: 7909c54004eSDavid Woodhouse read_unlock(&vcc_sklist_lock); 7919c54004eSDavid Woodhouse return vcc; 7929c54004eSDavid Woodhouse } 7939c54004eSDavid Woodhouse 7949c54004eSDavid Woodhouse static int list_vccs(int vci) 7959c54004eSDavid Woodhouse { 7969c54004eSDavid Woodhouse struct hlist_head *head; 7979c54004eSDavid Woodhouse struct atm_vcc *vcc; 7989c54004eSDavid Woodhouse struct hlist_node *node; 7999c54004eSDavid Woodhouse struct sock *s; 8009c54004eSDavid Woodhouse int num_found = 0; 8019c54004eSDavid Woodhouse int i; 8029c54004eSDavid Woodhouse 8039c54004eSDavid Woodhouse read_lock(&vcc_sklist_lock); 8049c54004eSDavid Woodhouse if (vci != 0){ 8059c54004eSDavid Woodhouse head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; 8069c54004eSDavid Woodhouse sk_for_each(s, node, head) { 8079c54004eSDavid Woodhouse num_found ++; 8089c54004eSDavid Woodhouse vcc = atm_sk(s); 8099c54004eSDavid Woodhouse printk(KERN_DEBUG "Device: %d Vpi: %d Vci: %d\n", 8109c54004eSDavid Woodhouse vcc->dev->number, 8119c54004eSDavid Woodhouse vcc->vpi, 8129c54004eSDavid Woodhouse vcc->vci); 8139c54004eSDavid Woodhouse } 8149c54004eSDavid Woodhouse } else { 8151e615df6SDavid Woodhouse for(i = 0; i < VCC_HTABLE_SIZE; i++){ 8169c54004eSDavid Woodhouse head = &vcc_hash[i]; 8179c54004eSDavid Woodhouse sk_for_each(s, node, head) { 8189c54004eSDavid Woodhouse num_found ++; 8199c54004eSDavid Woodhouse vcc = atm_sk(s); 8209c54004eSDavid Woodhouse printk(KERN_DEBUG "Device: %d Vpi: %d Vci: %d\n", 8219c54004eSDavid Woodhouse vcc->dev->number, 8229c54004eSDavid Woodhouse vcc->vpi, 8239c54004eSDavid Woodhouse vcc->vci); 8249c54004eSDavid Woodhouse } 8259c54004eSDavid Woodhouse } 8269c54004eSDavid Woodhouse } 8279c54004eSDavid Woodhouse read_unlock(&vcc_sklist_lock); 8289c54004eSDavid Woodhouse return num_found; 8299c54004eSDavid Woodhouse } 8309c54004eSDavid Woodhouse 8311e615df6SDavid Woodhouse static void release_vccs(struct atm_dev *dev) 8321e615df6SDavid Woodhouse { 8331e615df6SDavid Woodhouse int i; 8341e615df6SDavid Woodhouse 8351e615df6SDavid Woodhouse write_lock_irq(&vcc_sklist_lock); 8361e615df6SDavid Woodhouse for (i = 0; i < VCC_HTABLE_SIZE; i++) { 8371e615df6SDavid Woodhouse struct hlist_head *head = &vcc_hash[i]; 8381e615df6SDavid Woodhouse struct hlist_node *node, *tmp; 8391e615df6SDavid Woodhouse struct sock *s; 8401e615df6SDavid Woodhouse struct atm_vcc *vcc; 8411e615df6SDavid Woodhouse 8421e615df6SDavid Woodhouse sk_for_each_safe(s, node, tmp, head) { 8431e615df6SDavid Woodhouse vcc = atm_sk(s); 8441e615df6SDavid Woodhouse if (vcc->dev == dev) { 8451e615df6SDavid Woodhouse vcc_release_async(vcc, -EPIPE); 8461e615df6SDavid Woodhouse sk_del_node_init(s); 8471e615df6SDavid Woodhouse } 8481e615df6SDavid Woodhouse } 8491e615df6SDavid Woodhouse } 8501e615df6SDavid Woodhouse write_unlock_irq(&vcc_sklist_lock); 8511e615df6SDavid Woodhouse } 8521e615df6SDavid Woodhouse 8539c54004eSDavid Woodhouse 8549c54004eSDavid Woodhouse static int popen(struct atm_vcc *vcc) 8559c54004eSDavid Woodhouse { 8569c54004eSDavid Woodhouse struct solos_card *card = vcc->dev->dev_data; 8579c54004eSDavid Woodhouse struct sk_buff *skb; 8589c54004eSDavid Woodhouse struct pkt_hdr *header; 8599c54004eSDavid Woodhouse 860b28a4b9aSDavid Woodhouse if (vcc->qos.aal != ATM_AAL5) { 861b28a4b9aSDavid Woodhouse dev_warn(&card->dev->dev, "Unsupported ATM type %d\n", 862b28a4b9aSDavid Woodhouse vcc->qos.aal); 863b28a4b9aSDavid Woodhouse return -EINVAL; 864b28a4b9aSDavid Woodhouse } 865b28a4b9aSDavid Woodhouse 8669c54004eSDavid Woodhouse skb = alloc_skb(sizeof(*header), GFP_ATOMIC); 8679c54004eSDavid Woodhouse if (!skb && net_ratelimit()) { 8689c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in popen()\n"); 8699c54004eSDavid Woodhouse return -ENOMEM; 8709c54004eSDavid Woodhouse } 8719c54004eSDavid Woodhouse header = (void *)skb_put(skb, sizeof(*header)); 8729c54004eSDavid Woodhouse 873b76811afSDavid Woodhouse header->size = cpu_to_le16(0); 8749c54004eSDavid Woodhouse header->vpi = cpu_to_le16(vcc->vpi); 8759c54004eSDavid Woodhouse header->vci = cpu_to_le16(vcc->vci); 8769c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_POPEN); 8779c54004eSDavid Woodhouse 8789c54004eSDavid Woodhouse fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, NULL); 8799c54004eSDavid Woodhouse 880bdc54625SDavid Woodhouse set_bit(ATM_VF_ADDR, &vcc->flags); 8819c54004eSDavid Woodhouse set_bit(ATM_VF_READY, &vcc->flags); 8829c54004eSDavid Woodhouse list_vccs(0); 8839c54004eSDavid Woodhouse 8849c54004eSDavid Woodhouse 8859c54004eSDavid Woodhouse return 0; 8869c54004eSDavid Woodhouse } 8879c54004eSDavid Woodhouse 8889c54004eSDavid Woodhouse static void pclose(struct atm_vcc *vcc) 8899c54004eSDavid Woodhouse { 8909c54004eSDavid Woodhouse struct solos_card *card = vcc->dev->dev_data; 8919c54004eSDavid Woodhouse struct sk_buff *skb; 8929c54004eSDavid Woodhouse struct pkt_hdr *header; 8939c54004eSDavid Woodhouse 8949c54004eSDavid Woodhouse skb = alloc_skb(sizeof(*header), GFP_ATOMIC); 8959c54004eSDavid Woodhouse if (!skb) { 8969c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in pclose()\n"); 8979c54004eSDavid Woodhouse return; 8989c54004eSDavid Woodhouse } 8999c54004eSDavid Woodhouse header = (void *)skb_put(skb, sizeof(*header)); 9009c54004eSDavid Woodhouse 901b76811afSDavid Woodhouse header->size = cpu_to_le16(0); 9029c54004eSDavid Woodhouse header->vpi = cpu_to_le16(vcc->vpi); 9039c54004eSDavid Woodhouse header->vci = cpu_to_le16(vcc->vci); 9049c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_PCLOSE); 9059c54004eSDavid Woodhouse 9069c54004eSDavid Woodhouse fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, NULL); 9079c54004eSDavid Woodhouse 9089c54004eSDavid Woodhouse clear_bit(ATM_VF_ADDR, &vcc->flags); 9099c54004eSDavid Woodhouse clear_bit(ATM_VF_READY, &vcc->flags); 9109c54004eSDavid Woodhouse 9111f6ea6e5SDavid Woodhouse /* Hold up vcc_destroy_socket() (our caller) until solos_bh() in the 9121f6ea6e5SDavid Woodhouse tasklet has finished processing any incoming packets (and, more to 9131f6ea6e5SDavid Woodhouse the point, using the vcc pointer). */ 9141f6ea6e5SDavid Woodhouse tasklet_unlock_wait(&card->tlet); 9159c54004eSDavid Woodhouse return; 9169c54004eSDavid Woodhouse } 9179c54004eSDavid Woodhouse 9189c54004eSDavid Woodhouse static int print_buffer(struct sk_buff *buf) 9199c54004eSDavid Woodhouse { 9209c54004eSDavid Woodhouse int len,i; 9219c54004eSDavid Woodhouse char msg[500]; 9229c54004eSDavid Woodhouse char item[10]; 9239c54004eSDavid Woodhouse 9249c54004eSDavid Woodhouse len = buf->len; 9259c54004eSDavid Woodhouse for (i = 0; i < len; i++){ 9269c54004eSDavid Woodhouse if(i % 8 == 0) 9279c54004eSDavid Woodhouse sprintf(msg, "%02X: ", i); 9289c54004eSDavid Woodhouse 9299c54004eSDavid Woodhouse sprintf(item,"%02X ",*(buf->data + i)); 9309c54004eSDavid Woodhouse strcat(msg, item); 9319c54004eSDavid Woodhouse if(i % 8 == 7) { 9329c54004eSDavid Woodhouse sprintf(item, "\n"); 9339c54004eSDavid Woodhouse strcat(msg, item); 9349c54004eSDavid Woodhouse printk(KERN_DEBUG "%s", msg); 9359c54004eSDavid Woodhouse } 9369c54004eSDavid Woodhouse } 9379c54004eSDavid Woodhouse if (i % 8 != 0) { 9389c54004eSDavid Woodhouse sprintf(item, "\n"); 9399c54004eSDavid Woodhouse strcat(msg, item); 9409c54004eSDavid Woodhouse printk(KERN_DEBUG "%s", msg); 9419c54004eSDavid Woodhouse } 9429c54004eSDavid Woodhouse printk(KERN_DEBUG "\n"); 9439c54004eSDavid Woodhouse 9449c54004eSDavid Woodhouse return 0; 9459c54004eSDavid Woodhouse } 9469c54004eSDavid Woodhouse 9479c54004eSDavid Woodhouse static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb, 9489c54004eSDavid Woodhouse struct atm_vcc *vcc) 9499c54004eSDavid Woodhouse { 9509c54004eSDavid Woodhouse int old_len; 951f69e4170SDavid Woodhouse unsigned long flags; 9529c54004eSDavid Woodhouse 95390937231SDavid Woodhouse SKB_CB(skb)->vcc = vcc; 9549c54004eSDavid Woodhouse 955f69e4170SDavid Woodhouse spin_lock_irqsave(&card->tx_queue_lock, flags); 9569c54004eSDavid Woodhouse old_len = skb_queue_len(&card->tx_queue[port]); 9579c54004eSDavid Woodhouse skb_queue_tail(&card->tx_queue[port], skb); 95835c2221bSDavid Woodhouse if (!old_len) 959f69e4170SDavid Woodhouse card->tx_mask |= (1 << port); 960f69e4170SDavid Woodhouse spin_unlock_irqrestore(&card->tx_queue_lock, flags); 9619c54004eSDavid Woodhouse 962f69e4170SDavid Woodhouse /* Theoretically we could just schedule the tasklet here, but 963f69e4170SDavid Woodhouse that introduces latency we don't want -- it's noticeable */ 9649c54004eSDavid Woodhouse if (!old_len) 9659c54004eSDavid Woodhouse fpga_tx(card); 9669c54004eSDavid Woodhouse } 9679c54004eSDavid Woodhouse 96835c2221bSDavid Woodhouse static uint32_t fpga_tx(struct solos_card *card) 9699c54004eSDavid Woodhouse { 97035c2221bSDavid Woodhouse uint32_t tx_pending, card_flags; 9719c54004eSDavid Woodhouse uint32_t tx_started = 0; 9729c54004eSDavid Woodhouse struct sk_buff *skb; 9739c54004eSDavid Woodhouse struct atm_vcc *vcc; 9749c54004eSDavid Woodhouse unsigned char port; 9759c54004eSDavid Woodhouse unsigned long flags; 9769c54004eSDavid Woodhouse 9779c54004eSDavid Woodhouse spin_lock_irqsave(&card->tx_lock, flags); 9789c54004eSDavid Woodhouse 97935c2221bSDavid Woodhouse card_flags = ioread32(card->config_regs + FLAGS_ADDR); 98035c2221bSDavid Woodhouse /* 98135c2221bSDavid Woodhouse * The queue lock is required for _writing_ to tx_mask, but we're 98235c2221bSDavid Woodhouse * OK to read it here without locking. The only potential update 98335c2221bSDavid Woodhouse * that we could race with is in fpga_queue() where it sets a bit 98435c2221bSDavid Woodhouse * for a new port... but it's going to call this function again if 98535c2221bSDavid Woodhouse * it's doing that, anyway. 98635c2221bSDavid Woodhouse */ 98735c2221bSDavid Woodhouse tx_pending = card->tx_mask & ~card_flags; 9889c54004eSDavid Woodhouse 98935c2221bSDavid Woodhouse for (port = 0; tx_pending; tx_pending >>= 1, port++) { 99035c2221bSDavid Woodhouse if (tx_pending & 1) { 991eaf83e39SDavid Woodhouse struct sk_buff *oldskb = card->tx_skb[port]; 992eaf83e39SDavid Woodhouse if (oldskb) 993eaf83e39SDavid Woodhouse pci_unmap_single(card->dev, SKB_CB(oldskb)->dma_addr, 994eaf83e39SDavid Woodhouse oldskb->len, PCI_DMA_TODEVICE); 9959c54004eSDavid Woodhouse 9969c54004eSDavid Woodhouse spin_lock(&card->tx_queue_lock); 9979c54004eSDavid Woodhouse skb = skb_dequeue(&card->tx_queue[port]); 998f69e4170SDavid Woodhouse if (!skb) 999f69e4170SDavid Woodhouse card->tx_mask &= ~(1 << port); 10009c54004eSDavid Woodhouse spin_unlock(&card->tx_queue_lock); 10019c54004eSDavid Woodhouse 1002eaf83e39SDavid Woodhouse if (skb && !card->using_dma) { 1003eaf83e39SDavid Woodhouse memcpy_toio(TX_BUF(card, port), skb->data, skb->len); 1004bdc54625SDavid Woodhouse tx_started |= 1 << port; 1005eaf83e39SDavid Woodhouse oldskb = skb; /* We're done with this skb already */ 1006eaf83e39SDavid Woodhouse } else if (skb && card->using_dma) { 1007eaf83e39SDavid Woodhouse SKB_CB(skb)->dma_addr = pci_map_single(card->dev, skb->data, 1008eaf83e39SDavid Woodhouse skb->len, PCI_DMA_TODEVICE); 1009eaf83e39SDavid Woodhouse iowrite32(SKB_CB(skb)->dma_addr, 1010eaf83e39SDavid Woodhouse card->config_regs + TX_DMA_ADDR(port)); 1011eaf83e39SDavid Woodhouse } 1012eaf83e39SDavid Woodhouse 1013eaf83e39SDavid Woodhouse if (!oldskb) 10149c54004eSDavid Woodhouse continue; 10159c54004eSDavid Woodhouse 1016eaf83e39SDavid Woodhouse /* Clean up and free oldskb now it's gone */ 10179c54004eSDavid Woodhouse if (atmdebug) { 10189c54004eSDavid Woodhouse dev_info(&card->dev->dev, "Transmitted: port %d\n", 10199c54004eSDavid Woodhouse port); 1020eaf83e39SDavid Woodhouse print_buffer(oldskb); 10219c54004eSDavid Woodhouse } 10229c54004eSDavid Woodhouse 102390937231SDavid Woodhouse vcc = SKB_CB(oldskb)->vcc; 10249c54004eSDavid Woodhouse 10259c54004eSDavid Woodhouse if (vcc) { 10269c54004eSDavid Woodhouse atomic_inc(&vcc->stats->tx); 102790937231SDavid Woodhouse solos_pop(vcc, oldskb); 10289c54004eSDavid Woodhouse } else 102990937231SDavid Woodhouse dev_kfree_skb_irq(oldskb); 10309c54004eSDavid Woodhouse 10319c54004eSDavid Woodhouse } 10329c54004eSDavid Woodhouse } 1033bdc54625SDavid Woodhouse /* For non-DMA TX, write the 'TX start' bit for all four ports simultaneously */ 10349c54004eSDavid Woodhouse if (tx_started) 10359c54004eSDavid Woodhouse iowrite32(tx_started, card->config_regs + FLAGS_ADDR); 10369c54004eSDavid Woodhouse 10379c54004eSDavid Woodhouse spin_unlock_irqrestore(&card->tx_lock, flags); 103835c2221bSDavid Woodhouse return card_flags; 10399c54004eSDavid Woodhouse } 10409c54004eSDavid Woodhouse 10419c54004eSDavid Woodhouse static int psend(struct atm_vcc *vcc, struct sk_buff *skb) 10429c54004eSDavid Woodhouse { 10439c54004eSDavid Woodhouse struct solos_card *card = vcc->dev->dev_data; 10449c54004eSDavid Woodhouse struct pkt_hdr *header; 1045b76811afSDavid Woodhouse int pktlen; 10469c54004eSDavid Woodhouse 1047b76811afSDavid Woodhouse pktlen = skb->len; 1048b76811afSDavid Woodhouse if (pktlen > (BUF_SIZE - sizeof(*header))) { 10499c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Length of PDU is too large. Dropping PDU.\n"); 10509c54004eSDavid Woodhouse solos_pop(vcc, skb); 10519c54004eSDavid Woodhouse return 0; 10529c54004eSDavid Woodhouse } 10539c54004eSDavid Woodhouse 10549c54004eSDavid Woodhouse if (!skb_clone_writable(skb, sizeof(*header))) { 10559c54004eSDavid Woodhouse int expand_by = 0; 10569c54004eSDavid Woodhouse int ret; 10579c54004eSDavid Woodhouse 10589c54004eSDavid Woodhouse if (skb_headroom(skb) < sizeof(*header)) 10599c54004eSDavid Woodhouse expand_by = sizeof(*header) - skb_headroom(skb); 10609c54004eSDavid Woodhouse 10619c54004eSDavid Woodhouse ret = pskb_expand_head(skb, expand_by, 0, GFP_ATOMIC); 10629c54004eSDavid Woodhouse if (ret) { 10634306cad6SSimon Farnsworth dev_warn(&card->dev->dev, "pskb_expand_head failed.\n"); 10649c54004eSDavid Woodhouse solos_pop(vcc, skb); 10659c54004eSDavid Woodhouse return ret; 10669c54004eSDavid Woodhouse } 10679c54004eSDavid Woodhouse } 10689c54004eSDavid Woodhouse 10699c54004eSDavid Woodhouse header = (void *)skb_push(skb, sizeof(*header)); 10709c54004eSDavid Woodhouse 1071b76811afSDavid Woodhouse /* This does _not_ include the size of the header */ 1072b76811afSDavid Woodhouse header->size = cpu_to_le16(pktlen); 10739c54004eSDavid Woodhouse header->vpi = cpu_to_le16(vcc->vpi); 10749c54004eSDavid Woodhouse header->vci = cpu_to_le16(vcc->vci); 10759c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_DATA); 10769c54004eSDavid Woodhouse 10779c54004eSDavid Woodhouse fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, vcc); 10789c54004eSDavid Woodhouse 10799c54004eSDavid Woodhouse return 0; 10809c54004eSDavid Woodhouse } 10819c54004eSDavid Woodhouse 10829c54004eSDavid Woodhouse static struct atmdev_ops fpga_ops = { 10839c54004eSDavid Woodhouse .open = popen, 10849c54004eSDavid Woodhouse .close = pclose, 10859c54004eSDavid Woodhouse .ioctl = NULL, 10869c54004eSDavid Woodhouse .getsockopt = NULL, 10879c54004eSDavid Woodhouse .setsockopt = NULL, 10889c54004eSDavid Woodhouse .send = psend, 10899c54004eSDavid Woodhouse .send_oam = NULL, 10909c54004eSDavid Woodhouse .phy_put = NULL, 10919c54004eSDavid Woodhouse .phy_get = NULL, 10929c54004eSDavid Woodhouse .change_qos = NULL, 10939c54004eSDavid Woodhouse .proc_read = NULL, 10949c54004eSDavid Woodhouse .owner = THIS_MODULE 10959c54004eSDavid Woodhouse }; 10969c54004eSDavid Woodhouse 10979c54004eSDavid Woodhouse static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) 10989c54004eSDavid Woodhouse { 1099cd5549e0SDavid Woodhouse int err; 11009c54004eSDavid Woodhouse uint16_t fpga_ver; 11019c54004eSDavid Woodhouse uint8_t major_ver, minor_ver; 11029c54004eSDavid Woodhouse uint32_t data32; 11039c54004eSDavid Woodhouse struct solos_card *card; 11049c54004eSDavid Woodhouse 11059c54004eSDavid Woodhouse card = kzalloc(sizeof(*card), GFP_KERNEL); 11069c54004eSDavid Woodhouse if (!card) 11079c54004eSDavid Woodhouse return -ENOMEM; 11089c54004eSDavid Woodhouse 11099c54004eSDavid Woodhouse card->dev = dev; 1110fa755b9fSDavid Woodhouse init_waitqueue_head(&card->fw_wq); 111101e2ffacSDavid Woodhouse init_waitqueue_head(&card->param_wq); 11129c54004eSDavid Woodhouse 11139c54004eSDavid Woodhouse err = pci_enable_device(dev); 11149c54004eSDavid Woodhouse if (err) { 11159c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to enable PCI device\n"); 11169c54004eSDavid Woodhouse goto out; 11179c54004eSDavid Woodhouse } 11189c54004eSDavid Woodhouse 1119e930438cSYang Hongyang err = pci_set_dma_mask(dev, DMA_BIT_MASK(32)); 112090937231SDavid Woodhouse if (err) { 112190937231SDavid Woodhouse dev_warn(&dev->dev, "Failed to set 32-bit DMA mask\n"); 112290937231SDavid Woodhouse goto out; 112390937231SDavid Woodhouse } 112490937231SDavid Woodhouse 11259c54004eSDavid Woodhouse err = pci_request_regions(dev, "solos"); 11269c54004eSDavid Woodhouse if (err) { 11279c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to request regions\n"); 11289c54004eSDavid Woodhouse goto out; 11299c54004eSDavid Woodhouse } 11309c54004eSDavid Woodhouse 11319c54004eSDavid Woodhouse card->config_regs = pci_iomap(dev, 0, CONFIG_RAM_SIZE); 11329c54004eSDavid Woodhouse if (!card->config_regs) { 11339c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to ioremap config registers\n"); 11349c54004eSDavid Woodhouse goto out_release_regions; 11359c54004eSDavid Woodhouse } 11369c54004eSDavid Woodhouse card->buffers = pci_iomap(dev, 1, DATA_RAM_SIZE); 11379c54004eSDavid Woodhouse if (!card->buffers) { 11389c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to ioremap data buffers\n"); 11399c54004eSDavid Woodhouse goto out_unmap_config; 11409c54004eSDavid Woodhouse } 11419c54004eSDavid Woodhouse 1142cc3657e1SDavid Woodhouse if (reset) { 1143cc3657e1SDavid Woodhouse iowrite32(1, card->config_regs + FPGA_MODE); 1144cc3657e1SDavid Woodhouse data32 = ioread32(card->config_regs + FPGA_MODE); 11459c54004eSDavid Woodhouse 1146cc3657e1SDavid Woodhouse iowrite32(0, card->config_regs + FPGA_MODE); 1147cc3657e1SDavid Woodhouse data32 = ioread32(card->config_regs + FPGA_MODE); 1148cc3657e1SDavid Woodhouse } 11499c54004eSDavid Woodhouse 11509c54004eSDavid Woodhouse data32 = ioread32(card->config_regs + FPGA_VER); 11519c54004eSDavid Woodhouse fpga_ver = (data32 & 0x0000FFFF); 11529c54004eSDavid Woodhouse major_ver = ((data32 & 0xFF000000) >> 24); 11539c54004eSDavid Woodhouse minor_ver = ((data32 & 0x00FF0000) >> 16); 11544dbedf43SNathan Williams card->fpga_version = FPGA_VERSION(major_ver,minor_ver); 11554dbedf43SNathan Williams if (card->fpga_version > LEGACY_BUFFERS) 11564dbedf43SNathan Williams card->buffer_size = BUF_SIZE; 11574dbedf43SNathan Williams else 11584dbedf43SNathan Williams card->buffer_size = OLD_BUF_SIZE; 11599c54004eSDavid Woodhouse dev_info(&dev->dev, "Solos FPGA Version %d.%02d svn-%d\n", 11609c54004eSDavid Woodhouse major_ver, minor_ver, fpga_ver); 11619c54004eSDavid Woodhouse 11624dbedf43SNathan Williams if (card->fpga_version >= DMA_SUPPORTED){ 116390937231SDavid Woodhouse card->using_dma = 1; 11644dbedf43SNathan Williams } else { 11654dbedf43SNathan Williams card->using_dma = 0; 1166eab50f73SDavid Woodhouse /* Set RX empty flag for all ports */ 1167eab50f73SDavid Woodhouse iowrite32(0xF0, card->config_regs + FLAGS_ADDR); 1168eab50f73SDavid Woodhouse } 11699c54004eSDavid Woodhouse 11700fc36aa5SNathan Williams data32 = ioread32(card->config_regs + PORTS); 11710fc36aa5SNathan Williams card->nr_ports = (data32 & 0x000000FF); 11729c54004eSDavid Woodhouse 11739c54004eSDavid Woodhouse pci_set_drvdata(dev, card); 1174fa755b9fSDavid Woodhouse 11759c54004eSDavid Woodhouse tasklet_init(&card->tlet, solos_bh, (unsigned long)card); 11769c54004eSDavid Woodhouse spin_lock_init(&card->tx_lock); 11779c54004eSDavid Woodhouse spin_lock_init(&card->tx_queue_lock); 11789c54004eSDavid Woodhouse spin_lock_init(&card->cli_queue_lock); 117901e2ffacSDavid Woodhouse spin_lock_init(&card->param_queue_lock); 118001e2ffacSDavid Woodhouse INIT_LIST_HEAD(&card->param_queue); 11819c54004eSDavid Woodhouse 1182fcd82664SDavid Woodhouse err = request_irq(dev->irq, solos_irq, IRQF_SHARED, 11839c54004eSDavid Woodhouse "solos-pci", card); 1184fa755b9fSDavid Woodhouse if (err) { 11859c54004eSDavid Woodhouse dev_dbg(&card->dev->dev, "Failed to request interrupt IRQ: %d\n", dev->irq); 1186fa755b9fSDavid Woodhouse goto out_unmap_both; 1187fa755b9fSDavid Woodhouse } 11889c54004eSDavid Woodhouse 11899c54004eSDavid Woodhouse iowrite32(1, card->config_regs + IRQ_EN_ADDR); 11909c54004eSDavid Woodhouse 1191fa755b9fSDavid Woodhouse if (fpga_upgrade) 1192fa755b9fSDavid Woodhouse flash_upgrade(card, 0); 1193fa755b9fSDavid Woodhouse 1194fa755b9fSDavid Woodhouse if (firmware_upgrade) 1195fa755b9fSDavid Woodhouse flash_upgrade(card, 1); 1196fa755b9fSDavid Woodhouse 11974dbedf43SNathan Williams if (db_fpga_upgrade) 11984dbedf43SNathan Williams flash_upgrade(card, 2); 11994dbedf43SNathan Williams 12004dbedf43SNathan Williams if (db_firmware_upgrade) 12014dbedf43SNathan Williams flash_upgrade(card, 3); 12024dbedf43SNathan Williams 1203fa755b9fSDavid Woodhouse err = atm_init(card); 1204fa755b9fSDavid Woodhouse if (err) 1205fa755b9fSDavid Woodhouse goto out_free_irq; 1206fa755b9fSDavid Woodhouse 12079c54004eSDavid Woodhouse return 0; 12089c54004eSDavid Woodhouse 1209fa755b9fSDavid Woodhouse out_free_irq: 1210fa755b9fSDavid Woodhouse iowrite32(0, card->config_regs + IRQ_EN_ADDR); 1211fa755b9fSDavid Woodhouse free_irq(dev->irq, card); 1212fa755b9fSDavid Woodhouse tasklet_kill(&card->tlet); 1213fa755b9fSDavid Woodhouse 12149c54004eSDavid Woodhouse out_unmap_both: 1215fa755b9fSDavid Woodhouse pci_set_drvdata(dev, NULL); 12169c54004eSDavid Woodhouse pci_iounmap(dev, card->config_regs); 12179c54004eSDavid Woodhouse out_unmap_config: 12189c54004eSDavid Woodhouse pci_iounmap(dev, card->buffers); 12199c54004eSDavid Woodhouse out_release_regions: 12209c54004eSDavid Woodhouse pci_release_regions(dev); 12219c54004eSDavid Woodhouse out: 1222bc111d57SJulia Lawall kfree(card); 12239c54004eSDavid Woodhouse return err; 12249c54004eSDavid Woodhouse } 12259c54004eSDavid Woodhouse 12269c54004eSDavid Woodhouse static int atm_init(struct solos_card *card) 12279c54004eSDavid Woodhouse { 12289c54004eSDavid Woodhouse int i; 12299c54004eSDavid Woodhouse 12309c54004eSDavid Woodhouse for (i = 0; i < card->nr_ports; i++) { 123187ebb186SDavid Woodhouse struct sk_buff *skb; 123287ebb186SDavid Woodhouse struct pkt_hdr *header; 123387ebb186SDavid Woodhouse 12349c54004eSDavid Woodhouse skb_queue_head_init(&card->tx_queue[i]); 12359c54004eSDavid Woodhouse skb_queue_head_init(&card->cli_queue[i]); 12369c54004eSDavid Woodhouse 12379c54004eSDavid Woodhouse card->atmdev[i] = atm_dev_register("solos-pci", &fpga_ops, -1, NULL); 12389c54004eSDavid Woodhouse if (!card->atmdev[i]) { 12399c54004eSDavid Woodhouse dev_err(&card->dev->dev, "Could not register ATM device %d\n", i); 12409c54004eSDavid Woodhouse atm_remove(card); 12419c54004eSDavid Woodhouse return -ENODEV; 12429c54004eSDavid Woodhouse } 12439c54004eSDavid Woodhouse if (device_create_file(&card->atmdev[i]->class_dev, &dev_attr_console)) 12449c54004eSDavid Woodhouse dev_err(&card->dev->dev, "Could not register console for ATM device %d\n", i); 1245d057f0a4SDavid Woodhouse if (sysfs_create_group(&card->atmdev[i]->class_dev.kobj, &solos_attr_group)) 1246d057f0a4SDavid Woodhouse dev_err(&card->dev->dev, "Could not register parameter group for ATM device %d\n", i); 12479c54004eSDavid Woodhouse 12489c54004eSDavid Woodhouse dev_info(&card->dev->dev, "Registered ATM device %d\n", card->atmdev[i]->number); 12499c54004eSDavid Woodhouse 12509c54004eSDavid Woodhouse card->atmdev[i]->ci_range.vpi_bits = 8; 12519c54004eSDavid Woodhouse card->atmdev[i]->ci_range.vci_bits = 16; 12529c54004eSDavid Woodhouse card->atmdev[i]->dev_data = card; 12539c54004eSDavid Woodhouse card->atmdev[i]->phy_data = (void *)(unsigned long)i; 125449d49106SKarl Hiramoto atm_dev_signal_change(card->atmdev[i], ATM_PHY_SIG_UNKNOWN); 125587ebb186SDavid Woodhouse 125687ebb186SDavid Woodhouse skb = alloc_skb(sizeof(*header), GFP_ATOMIC); 125787ebb186SDavid Woodhouse if (!skb) { 125887ebb186SDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in atm_init()\n"); 125987ebb186SDavid Woodhouse continue; 126087ebb186SDavid Woodhouse } 126187ebb186SDavid Woodhouse 126287ebb186SDavid Woodhouse header = (void *)skb_put(skb, sizeof(*header)); 126387ebb186SDavid Woodhouse 126487ebb186SDavid Woodhouse header->size = cpu_to_le16(0); 126587ebb186SDavid Woodhouse header->vpi = cpu_to_le16(0); 126687ebb186SDavid Woodhouse header->vci = cpu_to_le16(0); 126787ebb186SDavid Woodhouse header->type = cpu_to_le16(PKT_STATUS); 126887ebb186SDavid Woodhouse 126987ebb186SDavid Woodhouse fpga_queue(card, i, skb, NULL); 12709c54004eSDavid Woodhouse } 12719c54004eSDavid Woodhouse return 0; 12729c54004eSDavid Woodhouse } 12739c54004eSDavid Woodhouse 12749c54004eSDavid Woodhouse static void atm_remove(struct solos_card *card) 12759c54004eSDavid Woodhouse { 12769c54004eSDavid Woodhouse int i; 12779c54004eSDavid Woodhouse 12789c54004eSDavid Woodhouse for (i = 0; i < card->nr_ports; i++) { 12799c54004eSDavid Woodhouse if (card->atmdev[i]) { 128097d759d3SDavid Woodhouse struct sk_buff *skb; 128197d759d3SDavid Woodhouse 12829c54004eSDavid Woodhouse dev_info(&card->dev->dev, "Unregistering ATM device %d\n", card->atmdev[i]->number); 1283c0fe3026SDavid Woodhouse 1284c0fe3026SDavid Woodhouse sysfs_remove_group(&card->atmdev[i]->class_dev.kobj, &solos_attr_group); 12859c54004eSDavid Woodhouse atm_dev_deregister(card->atmdev[i]); 128697d759d3SDavid Woodhouse 128797d759d3SDavid Woodhouse skb = card->rx_skb[i]; 128897d759d3SDavid Woodhouse if (skb) { 128997d759d3SDavid Woodhouse pci_unmap_single(card->dev, SKB_CB(skb)->dma_addr, 129097d759d3SDavid Woodhouse RX_DMA_SIZE, PCI_DMA_FROMDEVICE); 129197d759d3SDavid Woodhouse dev_kfree_skb(skb); 129297d759d3SDavid Woodhouse } 129397d759d3SDavid Woodhouse skb = card->tx_skb[i]; 129497d759d3SDavid Woodhouse if (skb) { 129597d759d3SDavid Woodhouse pci_unmap_single(card->dev, SKB_CB(skb)->dma_addr, 129697d759d3SDavid Woodhouse skb->len, PCI_DMA_TODEVICE); 129797d759d3SDavid Woodhouse dev_kfree_skb(skb); 129897d759d3SDavid Woodhouse } 129997d759d3SDavid Woodhouse while ((skb = skb_dequeue(&card->tx_queue[i]))) 130097d759d3SDavid Woodhouse dev_kfree_skb(skb); 130197d759d3SDavid Woodhouse 13029c54004eSDavid Woodhouse } 13039c54004eSDavid Woodhouse } 13049c54004eSDavid Woodhouse } 13059c54004eSDavid Woodhouse 13069c54004eSDavid Woodhouse static void fpga_remove(struct pci_dev *dev) 13079c54004eSDavid Woodhouse { 13089c54004eSDavid Woodhouse struct solos_card *card = pci_get_drvdata(dev); 13099c54004eSDavid Woodhouse 131097d759d3SDavid Woodhouse /* Disable IRQs */ 131197d759d3SDavid Woodhouse iowrite32(0, card->config_regs + IRQ_EN_ADDR); 131297d759d3SDavid Woodhouse 131397d759d3SDavid Woodhouse /* Reset FPGA */ 131497d759d3SDavid Woodhouse iowrite32(1, card->config_regs + FPGA_MODE); 131597d759d3SDavid Woodhouse (void)ioread32(card->config_regs + FPGA_MODE); 13169c54004eSDavid Woodhouse 13179c54004eSDavid Woodhouse atm_remove(card); 13189c54004eSDavid Woodhouse 13199c54004eSDavid Woodhouse free_irq(dev->irq, card); 13209c54004eSDavid Woodhouse tasklet_kill(&card->tlet); 13219c54004eSDavid Woodhouse 132297d759d3SDavid Woodhouse /* Release device from reset */ 132397d759d3SDavid Woodhouse iowrite32(0, card->config_regs + FPGA_MODE); 132497d759d3SDavid Woodhouse (void)ioread32(card->config_regs + FPGA_MODE); 132597d759d3SDavid Woodhouse 13269c54004eSDavid Woodhouse pci_iounmap(dev, card->buffers); 13279c54004eSDavid Woodhouse pci_iounmap(dev, card->config_regs); 13289c54004eSDavid Woodhouse 13299c54004eSDavid Woodhouse pci_release_regions(dev); 13309c54004eSDavid Woodhouse pci_disable_device(dev); 13319c54004eSDavid Woodhouse 13329c54004eSDavid Woodhouse pci_set_drvdata(dev, NULL); 13339c54004eSDavid Woodhouse kfree(card); 13349c54004eSDavid Woodhouse } 13359c54004eSDavid Woodhouse 13369c54004eSDavid Woodhouse static struct pci_device_id fpga_pci_tbl[] __devinitdata = { 13379c54004eSDavid Woodhouse { 0x10ee, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 13389c54004eSDavid Woodhouse { 0, } 13399c54004eSDavid Woodhouse }; 13409c54004eSDavid Woodhouse 13419c54004eSDavid Woodhouse MODULE_DEVICE_TABLE(pci,fpga_pci_tbl); 13429c54004eSDavid Woodhouse 13439c54004eSDavid Woodhouse static struct pci_driver fpga_driver = { 13449c54004eSDavid Woodhouse .name = "solos", 13459c54004eSDavid Woodhouse .id_table = fpga_pci_tbl, 13469c54004eSDavid Woodhouse .probe = fpga_probe, 13479c54004eSDavid Woodhouse .remove = fpga_remove, 13489c54004eSDavid Woodhouse }; 13499c54004eSDavid Woodhouse 13509c54004eSDavid Woodhouse 13519c54004eSDavid Woodhouse static int __init solos_pci_init(void) 13529c54004eSDavid Woodhouse { 13539c54004eSDavid Woodhouse printk(KERN_INFO "Solos PCI Driver Version %s\n", VERSION); 13549c54004eSDavid Woodhouse return pci_register_driver(&fpga_driver); 13559c54004eSDavid Woodhouse } 13569c54004eSDavid Woodhouse 13579c54004eSDavid Woodhouse static void __exit solos_pci_exit(void) 13589c54004eSDavid Woodhouse { 13599c54004eSDavid Woodhouse pci_unregister_driver(&fpga_driver); 13609c54004eSDavid Woodhouse printk(KERN_INFO "Solos PCI Driver %s Unloaded\n", VERSION); 13619c54004eSDavid Woodhouse } 13629c54004eSDavid Woodhouse 13639c54004eSDavid Woodhouse module_init(solos_pci_init); 13649c54004eSDavid Woodhouse module_exit(solos_pci_exit); 1365