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> 439c54004eSDavid Woodhouse 447c4015bdSSimon Farnsworth #define VERSION "0.07" 459c54004eSDavid Woodhouse #define PTAG "solos-pci" 469c54004eSDavid Woodhouse 479c54004eSDavid Woodhouse #define CONFIG_RAM_SIZE 128 489c54004eSDavid Woodhouse #define FLAGS_ADDR 0x7C 499c54004eSDavid Woodhouse #define IRQ_EN_ADDR 0x78 509c54004eSDavid Woodhouse #define FPGA_VER 0x74 519c54004eSDavid Woodhouse #define IRQ_CLEAR 0x70 527c4015bdSSimon Farnsworth #define WRITE_FLASH 0x6C 537c4015bdSSimon Farnsworth #define PORTS 0x68 547c4015bdSSimon Farnsworth #define FLASH_BLOCK 0x64 557c4015bdSSimon Farnsworth #define FLASH_BUSY 0x60 567c4015bdSSimon Farnsworth #define FPGA_MODE 0x5C 577c4015bdSSimon Farnsworth #define FLASH_MODE 0x58 5890937231SDavid Woodhouse #define TX_DMA_ADDR(port) (0x40 + (4 * (port))) 5990937231SDavid Woodhouse #define RX_DMA_ADDR(port) (0x30 + (4 * (port))) 609c54004eSDavid Woodhouse 619c54004eSDavid Woodhouse #define DATA_RAM_SIZE 32768 629c54004eSDavid Woodhouse #define BUF_SIZE 4096 637c4015bdSSimon Farnsworth #define FPGA_PAGE 528 /* FPGA flash page size*/ 647c4015bdSSimon Farnsworth #define SOLOS_PAGE 512 /* Solos flash page size*/ 657c4015bdSSimon Farnsworth #define FPGA_BLOCK (FPGA_PAGE * 8) /* FPGA flash block size*/ 667c4015bdSSimon Farnsworth #define SOLOS_BLOCK (SOLOS_PAGE * 8) /* Solos flash block size*/ 679c54004eSDavid Woodhouse 689c54004eSDavid Woodhouse #define RX_BUF(card, nr) ((card->buffers) + (nr)*BUF_SIZE*2) 699c54004eSDavid Woodhouse #define TX_BUF(card, nr) ((card->buffers) + (nr)*BUF_SIZE*2 + BUF_SIZE) 709c54004eSDavid Woodhouse 71eaf83e39SDavid Woodhouse #define RX_DMA_SIZE 2048 72eaf83e39SDavid Woodhouse 73cc3657e1SDavid Woodhouse static int reset = 0; 749c54004eSDavid Woodhouse static int atmdebug = 0; 757c4015bdSSimon Farnsworth static int firmware_upgrade = 0; 767c4015bdSSimon Farnsworth static int fpga_upgrade = 0; 779c54004eSDavid Woodhouse 789c54004eSDavid Woodhouse struct pkt_hdr { 799c54004eSDavid Woodhouse __le16 size; 809c54004eSDavid Woodhouse __le16 vpi; 819c54004eSDavid Woodhouse __le16 vci; 829c54004eSDavid Woodhouse __le16 type; 839c54004eSDavid Woodhouse }; 849c54004eSDavid Woodhouse 8590937231SDavid Woodhouse struct solos_skb_cb { 8690937231SDavid Woodhouse struct atm_vcc *vcc; 8790937231SDavid Woodhouse uint32_t dma_addr; 8890937231SDavid Woodhouse }; 8990937231SDavid Woodhouse 9090937231SDavid Woodhouse 9190937231SDavid Woodhouse #define SKB_CB(skb) ((struct solos_skb_cb *)skb->cb) 9290937231SDavid Woodhouse 939c54004eSDavid Woodhouse #define PKT_DATA 0 949c54004eSDavid Woodhouse #define PKT_COMMAND 1 959c54004eSDavid Woodhouse #define PKT_POPEN 3 969c54004eSDavid Woodhouse #define PKT_PCLOSE 4 9787ebb186SDavid Woodhouse #define PKT_STATUS 5 989c54004eSDavid Woodhouse 999c54004eSDavid Woodhouse struct solos_card { 1009c54004eSDavid Woodhouse void __iomem *config_regs; 1019c54004eSDavid Woodhouse void __iomem *buffers; 1029c54004eSDavid Woodhouse int nr_ports; 103f69e4170SDavid Woodhouse int tx_mask; 1049c54004eSDavid Woodhouse struct pci_dev *dev; 1059c54004eSDavid Woodhouse struct atm_dev *atmdev[4]; 1069c54004eSDavid Woodhouse struct tasklet_struct tlet; 1079c54004eSDavid Woodhouse spinlock_t tx_lock; 1089c54004eSDavid Woodhouse spinlock_t tx_queue_lock; 1099c54004eSDavid Woodhouse spinlock_t cli_queue_lock; 11001e2ffacSDavid Woodhouse spinlock_t param_queue_lock; 11101e2ffacSDavid Woodhouse struct list_head param_queue; 1129c54004eSDavid Woodhouse struct sk_buff_head tx_queue[4]; 1139c54004eSDavid Woodhouse struct sk_buff_head cli_queue[4]; 11490937231SDavid Woodhouse struct sk_buff *tx_skb[4]; 11590937231SDavid Woodhouse struct sk_buff *rx_skb[4]; 11601e2ffacSDavid Woodhouse wait_queue_head_t param_wq; 117fa755b9fSDavid Woodhouse wait_queue_head_t fw_wq; 11890937231SDavid Woodhouse int using_dma; 1199c54004eSDavid Woodhouse }; 1209c54004eSDavid Woodhouse 12101e2ffacSDavid Woodhouse 12201e2ffacSDavid Woodhouse struct solos_param { 12301e2ffacSDavid Woodhouse struct list_head list; 12401e2ffacSDavid Woodhouse pid_t pid; 12501e2ffacSDavid Woodhouse int port; 12601e2ffacSDavid Woodhouse struct sk_buff *response; 12701e2ffacSDavid Woodhouse }; 12801e2ffacSDavid Woodhouse 1299c54004eSDavid Woodhouse #define SOLOS_CHAN(atmdev) ((int)(unsigned long)(atmdev)->phy_data) 1309c54004eSDavid Woodhouse 1319c54004eSDavid Woodhouse MODULE_AUTHOR("Traverse Technologies <support@traverse.com.au>"); 1329c54004eSDavid Woodhouse MODULE_DESCRIPTION("Solos PCI driver"); 1339c54004eSDavid Woodhouse MODULE_VERSION(VERSION); 1349c54004eSDavid Woodhouse MODULE_LICENSE("GPL"); 135cc3657e1SDavid Woodhouse MODULE_PARM_DESC(reset, "Reset Solos chips on startup"); 1369c54004eSDavid Woodhouse MODULE_PARM_DESC(atmdebug, "Print ATM data"); 1377c4015bdSSimon Farnsworth MODULE_PARM_DESC(firmware_upgrade, "Initiate Solos firmware upgrade"); 1387c4015bdSSimon Farnsworth MODULE_PARM_DESC(fpga_upgrade, "Initiate FPGA upgrade"); 139cc3657e1SDavid Woodhouse module_param(reset, int, 0444); 1404306cad6SSimon Farnsworth module_param(atmdebug, int, 0644); 1417c4015bdSSimon Farnsworth module_param(firmware_upgrade, int, 0444); 1427c4015bdSSimon Farnsworth module_param(fpga_upgrade, int, 0444); 1439c54004eSDavid Woodhouse 1449c54004eSDavid Woodhouse static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb, 1459c54004eSDavid Woodhouse struct atm_vcc *vcc); 14635c2221bSDavid Woodhouse static uint32_t fpga_tx(struct solos_card *); 1479c54004eSDavid Woodhouse static irqreturn_t solos_irq(int irq, void *dev_id); 1489c54004eSDavid Woodhouse static struct atm_vcc* find_vcc(struct atm_dev *dev, short vpi, int vci); 1499c54004eSDavid Woodhouse static int list_vccs(int vci); 1501e615df6SDavid Woodhouse static void release_vccs(struct atm_dev *dev); 1519c54004eSDavid Woodhouse static int atm_init(struct solos_card *); 1529c54004eSDavid Woodhouse static void atm_remove(struct solos_card *); 1539c54004eSDavid Woodhouse static int send_command(struct solos_card *card, int dev, const char *buf, size_t size); 1549c54004eSDavid Woodhouse static void solos_bh(unsigned long); 1559c54004eSDavid Woodhouse static int print_buffer(struct sk_buff *buf); 1569c54004eSDavid Woodhouse 1579c54004eSDavid Woodhouse static inline void solos_pop(struct atm_vcc *vcc, struct sk_buff *skb) 1589c54004eSDavid Woodhouse { 1599c54004eSDavid Woodhouse if (vcc->pop) 1609c54004eSDavid Woodhouse vcc->pop(vcc, skb); 1619c54004eSDavid Woodhouse else 1629c54004eSDavid Woodhouse dev_kfree_skb_any(skb); 1639c54004eSDavid Woodhouse } 1649c54004eSDavid Woodhouse 16501e2ffacSDavid Woodhouse static ssize_t solos_param_show(struct device *dev, struct device_attribute *attr, 16601e2ffacSDavid Woodhouse char *buf) 16701e2ffacSDavid Woodhouse { 16801e2ffacSDavid Woodhouse struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev); 16901e2ffacSDavid Woodhouse struct solos_card *card = atmdev->dev_data; 17001e2ffacSDavid Woodhouse struct solos_param prm; 17101e2ffacSDavid Woodhouse struct sk_buff *skb; 17201e2ffacSDavid Woodhouse struct pkt_hdr *header; 17301e2ffacSDavid Woodhouse int buflen; 17401e2ffacSDavid Woodhouse 17501e2ffacSDavid Woodhouse buflen = strlen(attr->attr.name) + 10; 17601e2ffacSDavid Woodhouse 1773456b221SDavid Woodhouse skb = alloc_skb(sizeof(*header) + buflen, GFP_KERNEL); 17801e2ffacSDavid Woodhouse if (!skb) { 17901e2ffacSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in solos_param_show()\n"); 18001e2ffacSDavid Woodhouse return -ENOMEM; 18101e2ffacSDavid Woodhouse } 18201e2ffacSDavid Woodhouse 18301e2ffacSDavid Woodhouse header = (void *)skb_put(skb, sizeof(*header)); 18401e2ffacSDavid Woodhouse 18501e2ffacSDavid Woodhouse buflen = snprintf((void *)&header[1], buflen - 1, 18601e2ffacSDavid Woodhouse "L%05d\n%s\n", current->pid, attr->attr.name); 18701e2ffacSDavid Woodhouse skb_put(skb, buflen); 18801e2ffacSDavid Woodhouse 18901e2ffacSDavid Woodhouse header->size = cpu_to_le16(buflen); 19001e2ffacSDavid Woodhouse header->vpi = cpu_to_le16(0); 19101e2ffacSDavid Woodhouse header->vci = cpu_to_le16(0); 19201e2ffacSDavid Woodhouse header->type = cpu_to_le16(PKT_COMMAND); 19301e2ffacSDavid Woodhouse 19401e2ffacSDavid Woodhouse prm.pid = current->pid; 19501e2ffacSDavid Woodhouse prm.response = NULL; 19601e2ffacSDavid Woodhouse prm.port = SOLOS_CHAN(atmdev); 19701e2ffacSDavid Woodhouse 19801e2ffacSDavid Woodhouse spin_lock_irq(&card->param_queue_lock); 19901e2ffacSDavid Woodhouse list_add(&prm.list, &card->param_queue); 20001e2ffacSDavid Woodhouse spin_unlock_irq(&card->param_queue_lock); 20101e2ffacSDavid Woodhouse 20201e2ffacSDavid Woodhouse fpga_queue(card, prm.port, skb, NULL); 20301e2ffacSDavid Woodhouse 20401e2ffacSDavid Woodhouse wait_event_timeout(card->param_wq, prm.response, 5 * HZ); 20501e2ffacSDavid Woodhouse 20601e2ffacSDavid Woodhouse spin_lock_irq(&card->param_queue_lock); 20701e2ffacSDavid Woodhouse list_del(&prm.list); 20801e2ffacSDavid Woodhouse spin_unlock_irq(&card->param_queue_lock); 20901e2ffacSDavid Woodhouse 21001e2ffacSDavid Woodhouse if (!prm.response) 21101e2ffacSDavid Woodhouse return -EIO; 21201e2ffacSDavid Woodhouse 21301e2ffacSDavid Woodhouse buflen = prm.response->len; 21401e2ffacSDavid Woodhouse memcpy(buf, prm.response->data, buflen); 21501e2ffacSDavid Woodhouse kfree_skb(prm.response); 21601e2ffacSDavid Woodhouse 21701e2ffacSDavid Woodhouse return buflen; 21801e2ffacSDavid Woodhouse } 21901e2ffacSDavid Woodhouse 22001e2ffacSDavid Woodhouse static ssize_t solos_param_store(struct device *dev, struct device_attribute *attr, 22101e2ffacSDavid Woodhouse const char *buf, size_t count) 22201e2ffacSDavid Woodhouse { 22301e2ffacSDavid Woodhouse struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev); 22401e2ffacSDavid Woodhouse struct solos_card *card = atmdev->dev_data; 22501e2ffacSDavid Woodhouse struct solos_param prm; 22601e2ffacSDavid Woodhouse struct sk_buff *skb; 22701e2ffacSDavid Woodhouse struct pkt_hdr *header; 22801e2ffacSDavid Woodhouse int buflen; 22901e2ffacSDavid Woodhouse ssize_t ret; 23001e2ffacSDavid Woodhouse 23101e2ffacSDavid Woodhouse buflen = strlen(attr->attr.name) + 11 + count; 23201e2ffacSDavid Woodhouse 2333456b221SDavid Woodhouse skb = alloc_skb(sizeof(*header) + buflen, GFP_KERNEL); 23401e2ffacSDavid Woodhouse if (!skb) { 23501e2ffacSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in solos_param_store()\n"); 23601e2ffacSDavid Woodhouse return -ENOMEM; 23701e2ffacSDavid Woodhouse } 23801e2ffacSDavid Woodhouse 23901e2ffacSDavid Woodhouse header = (void *)skb_put(skb, sizeof(*header)); 24001e2ffacSDavid Woodhouse 24101e2ffacSDavid Woodhouse buflen = snprintf((void *)&header[1], buflen - 1, 24201e2ffacSDavid Woodhouse "L%05d\n%s\n%s\n", current->pid, attr->attr.name, buf); 24301e2ffacSDavid Woodhouse 24401e2ffacSDavid Woodhouse skb_put(skb, buflen); 24501e2ffacSDavid Woodhouse header->size = cpu_to_le16(buflen); 24601e2ffacSDavid Woodhouse header->vpi = cpu_to_le16(0); 24701e2ffacSDavid Woodhouse header->vci = cpu_to_le16(0); 24801e2ffacSDavid Woodhouse header->type = cpu_to_le16(PKT_COMMAND); 24901e2ffacSDavid Woodhouse 25001e2ffacSDavid Woodhouse prm.pid = current->pid; 25101e2ffacSDavid Woodhouse prm.response = NULL; 25201e2ffacSDavid Woodhouse prm.port = SOLOS_CHAN(atmdev); 25301e2ffacSDavid Woodhouse 25401e2ffacSDavid Woodhouse spin_lock_irq(&card->param_queue_lock); 25501e2ffacSDavid Woodhouse list_add(&prm.list, &card->param_queue); 25601e2ffacSDavid Woodhouse spin_unlock_irq(&card->param_queue_lock); 25701e2ffacSDavid Woodhouse 25801e2ffacSDavid Woodhouse fpga_queue(card, prm.port, skb, NULL); 25901e2ffacSDavid Woodhouse 26001e2ffacSDavid Woodhouse wait_event_timeout(card->param_wq, prm.response, 5 * HZ); 26101e2ffacSDavid Woodhouse 26201e2ffacSDavid Woodhouse spin_lock_irq(&card->param_queue_lock); 26301e2ffacSDavid Woodhouse list_del(&prm.list); 26401e2ffacSDavid Woodhouse spin_unlock_irq(&card->param_queue_lock); 26501e2ffacSDavid Woodhouse 26601e2ffacSDavid Woodhouse skb = prm.response; 26701e2ffacSDavid Woodhouse 26801e2ffacSDavid Woodhouse if (!skb) 26901e2ffacSDavid Woodhouse return -EIO; 27001e2ffacSDavid Woodhouse 27101e2ffacSDavid Woodhouse buflen = skb->len; 27201e2ffacSDavid Woodhouse 27301e2ffacSDavid Woodhouse /* Sometimes it has a newline, sometimes it doesn't. */ 27401e2ffacSDavid Woodhouse if (skb->data[buflen - 1] == '\n') 27501e2ffacSDavid Woodhouse buflen--; 27601e2ffacSDavid Woodhouse 27701e2ffacSDavid Woodhouse if (buflen == 2 && !strncmp(skb->data, "OK", 2)) 27801e2ffacSDavid Woodhouse ret = count; 27901e2ffacSDavid Woodhouse else if (buflen == 5 && !strncmp(skb->data, "ERROR", 5)) 28001e2ffacSDavid Woodhouse ret = -EIO; 28101e2ffacSDavid Woodhouse else { 28201e2ffacSDavid Woodhouse /* We know we have enough space allocated for this; we allocated 28301e2ffacSDavid Woodhouse it ourselves */ 28401e2ffacSDavid Woodhouse skb->data[buflen] = 0; 28501e2ffacSDavid Woodhouse 28601e2ffacSDavid Woodhouse dev_warn(&card->dev->dev, "Unexpected parameter response: '%s'\n", 28701e2ffacSDavid Woodhouse skb->data); 28801e2ffacSDavid Woodhouse ret = -EIO; 28901e2ffacSDavid Woodhouse } 29001e2ffacSDavid Woodhouse kfree_skb(skb); 29101e2ffacSDavid Woodhouse 29201e2ffacSDavid Woodhouse return ret; 29301e2ffacSDavid Woodhouse } 29401e2ffacSDavid Woodhouse 29587ebb186SDavid Woodhouse static char *next_string(struct sk_buff *skb) 29687ebb186SDavid Woodhouse { 29787ebb186SDavid Woodhouse int i = 0; 29887ebb186SDavid Woodhouse char *this = skb->data; 29987ebb186SDavid Woodhouse 300c6428e52SDavid Woodhouse for (i = 0; i < skb->len; i++) { 30187ebb186SDavid Woodhouse if (this[i] == '\n') { 30287ebb186SDavid Woodhouse this[i] = 0; 303c6428e52SDavid Woodhouse skb_pull(skb, i + 1); 30487ebb186SDavid Woodhouse return this; 30587ebb186SDavid Woodhouse } 306c6428e52SDavid Woodhouse if (!isprint(this[i])) 307c6428e52SDavid Woodhouse return NULL; 30887ebb186SDavid Woodhouse } 30987ebb186SDavid Woodhouse return NULL; 31087ebb186SDavid Woodhouse } 31187ebb186SDavid Woodhouse 31287ebb186SDavid Woodhouse /* 31387ebb186SDavid Woodhouse * Status packet has fields separated by \n, starting with a version number 31487ebb186SDavid Woodhouse * for the information therein. Fields are.... 31587ebb186SDavid Woodhouse * 31687ebb186SDavid Woodhouse * packet version 31787ebb186SDavid Woodhouse * TxBitRate (version >= 1) 31887ebb186SDavid Woodhouse * RxBitRate (version >= 1) 31987ebb186SDavid Woodhouse * State (version >= 1) 32087ebb186SDavid Woodhouse */ 32187ebb186SDavid Woodhouse static int process_status(struct solos_card *card, int port, struct sk_buff *skb) 32287ebb186SDavid Woodhouse { 323af780656SDavid Woodhouse char *str, *end, *state_str; 324c6428e52SDavid Woodhouse int ver, rate_up, rate_down, state; 32587ebb186SDavid Woodhouse 32687ebb186SDavid Woodhouse if (!card->atmdev[port]) 32787ebb186SDavid Woodhouse return -ENODEV; 32887ebb186SDavid Woodhouse 32987ebb186SDavid Woodhouse str = next_string(skb); 33087ebb186SDavid Woodhouse if (!str) 33187ebb186SDavid Woodhouse return -EIO; 33287ebb186SDavid Woodhouse 33387ebb186SDavid Woodhouse ver = simple_strtol(str, NULL, 10); 33487ebb186SDavid Woodhouse if (ver < 1) { 33587ebb186SDavid Woodhouse dev_warn(&card->dev->dev, "Unexpected status interrupt version %d\n", 33687ebb186SDavid Woodhouse ver); 33787ebb186SDavid Woodhouse return -EIO; 33887ebb186SDavid Woodhouse } 33987ebb186SDavid Woodhouse 34087ebb186SDavid Woodhouse str = next_string(skb); 341c6428e52SDavid Woodhouse if (!str) 342c6428e52SDavid Woodhouse return -EIO; 34395852f48SDavid Woodhouse if (!strcmp(str, "ERROR")) { 34495852f48SDavid Woodhouse dev_dbg(&card->dev->dev, "Status packet indicated Solos error on port %d (starting up?)\n", 34595852f48SDavid Woodhouse port); 34695852f48SDavid Woodhouse return 0; 34795852f48SDavid Woodhouse } 34895852f48SDavid Woodhouse 34987ebb186SDavid Woodhouse rate_up = simple_strtol(str, &end, 10); 35087ebb186SDavid Woodhouse if (*end) 35187ebb186SDavid Woodhouse return -EIO; 35287ebb186SDavid Woodhouse 35387ebb186SDavid Woodhouse str = next_string(skb); 354c6428e52SDavid Woodhouse if (!str) 355c6428e52SDavid Woodhouse return -EIO; 35687ebb186SDavid Woodhouse rate_down = simple_strtol(str, &end, 10); 35787ebb186SDavid Woodhouse if (*end) 35887ebb186SDavid Woodhouse return -EIO; 35987ebb186SDavid Woodhouse 360af780656SDavid Woodhouse state_str = next_string(skb); 361c6428e52SDavid Woodhouse if (!state_str) 362c6428e52SDavid Woodhouse return -EIO; 363af780656SDavid Woodhouse if (!strcmp(state_str, "Showtime")) 36487ebb186SDavid Woodhouse state = ATM_PHY_SIG_FOUND; 3651e615df6SDavid Woodhouse else { 3661e615df6SDavid Woodhouse state = ATM_PHY_SIG_LOST; 3671e615df6SDavid Woodhouse release_vccs(card->atmdev[port]); 3681e615df6SDavid Woodhouse } 36987ebb186SDavid Woodhouse 370c6428e52SDavid Woodhouse if (state == ATM_PHY_SIG_LOST) { 37195852f48SDavid Woodhouse dev_info(&card->dev->dev, "Port %d: %s\n", port, state_str); 372c6428e52SDavid Woodhouse } else { 373c6428e52SDavid Woodhouse char *snr, *attn; 374af780656SDavid Woodhouse 375c6428e52SDavid Woodhouse snr = next_string(skb); 376c6428e52SDavid Woodhouse if (!str) 377c6428e52SDavid Woodhouse return -EIO; 378c6428e52SDavid Woodhouse attn = next_string(skb); 379c6428e52SDavid Woodhouse if (!attn) 380c6428e52SDavid Woodhouse return -EIO; 381c6428e52SDavid Woodhouse 38295852f48SDavid Woodhouse dev_info(&card->dev->dev, "Port %d: %s @%d/%d kb/s%s%s%s%s\n", 383c6428e52SDavid Woodhouse port, state_str, rate_down/1000, rate_up/1000, 384c6428e52SDavid Woodhouse snr[0]?", SNR ":"", snr, attn[0]?", Attn ":"", attn); 385c6428e52SDavid Woodhouse } 386c6428e52SDavid Woodhouse card->atmdev[port]->link_rate = rate_down / 424; 38787ebb186SDavid Woodhouse card->atmdev[port]->signal = state; 38887ebb186SDavid Woodhouse 38987ebb186SDavid Woodhouse return 0; 39087ebb186SDavid Woodhouse } 39187ebb186SDavid Woodhouse 39201e2ffacSDavid Woodhouse static int process_command(struct solos_card *card, int port, struct sk_buff *skb) 39301e2ffacSDavid Woodhouse { 39401e2ffacSDavid Woodhouse struct solos_param *prm; 39501e2ffacSDavid Woodhouse unsigned long flags; 39601e2ffacSDavid Woodhouse int cmdpid; 39701e2ffacSDavid Woodhouse int found = 0; 39801e2ffacSDavid Woodhouse 39901e2ffacSDavid Woodhouse if (skb->len < 7) 40001e2ffacSDavid Woodhouse return 0; 40101e2ffacSDavid Woodhouse 40201e2ffacSDavid Woodhouse if (skb->data[0] != 'L' || !isdigit(skb->data[1]) || 40301e2ffacSDavid Woodhouse !isdigit(skb->data[2]) || !isdigit(skb->data[3]) || 40401e2ffacSDavid Woodhouse !isdigit(skb->data[4]) || !isdigit(skb->data[5]) || 40501e2ffacSDavid Woodhouse skb->data[6] != '\n') 40601e2ffacSDavid Woodhouse return 0; 40701e2ffacSDavid Woodhouse 40801e2ffacSDavid Woodhouse cmdpid = simple_strtol(&skb->data[1], NULL, 10); 40901e2ffacSDavid Woodhouse 41001e2ffacSDavid Woodhouse spin_lock_irqsave(&card->param_queue_lock, flags); 41101e2ffacSDavid Woodhouse list_for_each_entry(prm, &card->param_queue, list) { 41201e2ffacSDavid Woodhouse if (prm->port == port && prm->pid == cmdpid) { 41301e2ffacSDavid Woodhouse prm->response = skb; 41401e2ffacSDavid Woodhouse skb_pull(skb, 7); 41501e2ffacSDavid Woodhouse wake_up(&card->param_wq); 41601e2ffacSDavid Woodhouse found = 1; 41701e2ffacSDavid Woodhouse break; 41801e2ffacSDavid Woodhouse } 41901e2ffacSDavid Woodhouse } 42001e2ffacSDavid Woodhouse spin_unlock_irqrestore(&card->param_queue_lock, flags); 42101e2ffacSDavid Woodhouse return found; 42201e2ffacSDavid Woodhouse } 42301e2ffacSDavid Woodhouse 4249c54004eSDavid Woodhouse static ssize_t console_show(struct device *dev, struct device_attribute *attr, 4259c54004eSDavid Woodhouse char *buf) 4269c54004eSDavid Woodhouse { 4279c54004eSDavid Woodhouse struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev); 4289c54004eSDavid Woodhouse struct solos_card *card = atmdev->dev_data; 4299c54004eSDavid Woodhouse struct sk_buff *skb; 4309c54004eSDavid Woodhouse 4319c54004eSDavid Woodhouse spin_lock(&card->cli_queue_lock); 4329c54004eSDavid Woodhouse skb = skb_dequeue(&card->cli_queue[SOLOS_CHAN(atmdev)]); 4339c54004eSDavid Woodhouse spin_unlock(&card->cli_queue_lock); 4349c54004eSDavid Woodhouse if(skb == NULL) 4359c54004eSDavid Woodhouse return sprintf(buf, "No data.\n"); 4369c54004eSDavid Woodhouse 4379c54004eSDavid Woodhouse memcpy(buf, skb->data, skb->len); 4389c54004eSDavid Woodhouse dev_dbg(&card->dev->dev, "len: %d\n", skb->len); 4399c54004eSDavid Woodhouse 4409c54004eSDavid Woodhouse kfree_skb(skb); 4419c54004eSDavid Woodhouse return skb->len; 4429c54004eSDavid Woodhouse } 4439c54004eSDavid Woodhouse 4449c54004eSDavid Woodhouse static int send_command(struct solos_card *card, int dev, const char *buf, size_t size) 4459c54004eSDavid Woodhouse { 4469c54004eSDavid Woodhouse struct sk_buff *skb; 4479c54004eSDavid Woodhouse struct pkt_hdr *header; 4489c54004eSDavid Woodhouse 4499c54004eSDavid Woodhouse if (size > (BUF_SIZE - sizeof(*header))) { 4509c54004eSDavid Woodhouse dev_dbg(&card->dev->dev, "Command is too big. Dropping request\n"); 4519c54004eSDavid Woodhouse return 0; 4529c54004eSDavid Woodhouse } 4539c54004eSDavid Woodhouse skb = alloc_skb(size + sizeof(*header), GFP_ATOMIC); 4549c54004eSDavid Woodhouse if (!skb) { 4559c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in send_command()\n"); 4569c54004eSDavid Woodhouse return 0; 4579c54004eSDavid Woodhouse } 4589c54004eSDavid Woodhouse 4599c54004eSDavid Woodhouse header = (void *)skb_put(skb, sizeof(*header)); 4609c54004eSDavid Woodhouse 4619c54004eSDavid Woodhouse header->size = cpu_to_le16(size); 4629c54004eSDavid Woodhouse header->vpi = cpu_to_le16(0); 4639c54004eSDavid Woodhouse header->vci = cpu_to_le16(0); 4649c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_COMMAND); 4659c54004eSDavid Woodhouse 4669c54004eSDavid Woodhouse memcpy(skb_put(skb, size), buf, size); 4679c54004eSDavid Woodhouse 4689c54004eSDavid Woodhouse fpga_queue(card, dev, skb, NULL); 4699c54004eSDavid Woodhouse 4709c54004eSDavid Woodhouse return 0; 4719c54004eSDavid Woodhouse } 4729c54004eSDavid Woodhouse 4739c54004eSDavid Woodhouse static ssize_t console_store(struct device *dev, struct device_attribute *attr, 4749c54004eSDavid Woodhouse const char *buf, size_t count) 4759c54004eSDavid Woodhouse { 4769c54004eSDavid Woodhouse struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev); 4779c54004eSDavid Woodhouse struct solos_card *card = atmdev->dev_data; 4789c54004eSDavid Woodhouse int err; 4799c54004eSDavid Woodhouse 4809c54004eSDavid Woodhouse err = send_command(card, SOLOS_CHAN(atmdev), buf, count); 4819c54004eSDavid Woodhouse 4829c54004eSDavid Woodhouse return err?:count; 4839c54004eSDavid Woodhouse } 4849c54004eSDavid Woodhouse 4859c54004eSDavid Woodhouse static DEVICE_ATTR(console, 0644, console_show, console_store); 486d057f0a4SDavid Woodhouse 487d057f0a4SDavid Woodhouse 488d057f0a4SDavid Woodhouse #define SOLOS_ATTR_RO(x) static DEVICE_ATTR(x, 0444, solos_param_show, NULL); 489d057f0a4SDavid Woodhouse #define SOLOS_ATTR_RW(x) static DEVICE_ATTR(x, 0644, solos_param_show, solos_param_store); 490d057f0a4SDavid Woodhouse 491d057f0a4SDavid Woodhouse #include "solos-attrlist.c" 492d057f0a4SDavid Woodhouse 493d057f0a4SDavid Woodhouse #undef SOLOS_ATTR_RO 494d057f0a4SDavid Woodhouse #undef SOLOS_ATTR_RW 495d057f0a4SDavid Woodhouse 496d057f0a4SDavid Woodhouse #define SOLOS_ATTR_RO(x) &dev_attr_##x.attr, 497d057f0a4SDavid Woodhouse #define SOLOS_ATTR_RW(x) &dev_attr_##x.attr, 498d057f0a4SDavid Woodhouse 499d057f0a4SDavid Woodhouse static struct attribute *solos_attrs[] = { 500d057f0a4SDavid Woodhouse #include "solos-attrlist.c" 501d057f0a4SDavid Woodhouse NULL 502d057f0a4SDavid Woodhouse }; 503d057f0a4SDavid Woodhouse 504d057f0a4SDavid Woodhouse static struct attribute_group solos_attr_group = { 505d057f0a4SDavid Woodhouse .attrs = solos_attrs, 506d057f0a4SDavid Woodhouse .name = "parameters", 507d057f0a4SDavid Woodhouse }; 5089c54004eSDavid Woodhouse 509fa755b9fSDavid Woodhouse static int flash_upgrade(struct solos_card *card, int chip) 510fa755b9fSDavid Woodhouse { 511fa755b9fSDavid Woodhouse const struct firmware *fw; 512fa755b9fSDavid Woodhouse const char *fw_name; 5137c4015bdSSimon Farnsworth uint32_t data32 = 0; 5147c4015bdSSimon Farnsworth int blocksize = 0; 5157c4015bdSSimon Farnsworth int numblocks = 0; 516fa755b9fSDavid Woodhouse int offset; 517fa755b9fSDavid Woodhouse 518fa755b9fSDavid Woodhouse if (chip == 0) { 519fa755b9fSDavid Woodhouse fw_name = "solos-FPGA.bin"; 5207c4015bdSSimon Farnsworth blocksize = FPGA_BLOCK; 5217c4015bdSSimon Farnsworth } else { 522fa755b9fSDavid Woodhouse fw_name = "solos-Firmware.bin"; 5237c4015bdSSimon Farnsworth blocksize = SOLOS_BLOCK; 5247c4015bdSSimon Farnsworth } 5257c4015bdSSimon Farnsworth 526fa755b9fSDavid Woodhouse if (request_firmware(&fw, fw_name, &card->dev->dev)) 527fa755b9fSDavid Woodhouse return -ENOENT; 528fa755b9fSDavid Woodhouse 529fa755b9fSDavid Woodhouse dev_info(&card->dev->dev, "Flash upgrade starting\n"); 530fa755b9fSDavid Woodhouse 531fa755b9fSDavid Woodhouse numblocks = fw->size / blocksize; 532fa755b9fSDavid Woodhouse dev_info(&card->dev->dev, "Firmware size: %zd\n", fw->size); 533fa755b9fSDavid Woodhouse dev_info(&card->dev->dev, "Number of blocks: %d\n", numblocks); 5347c4015bdSSimon Farnsworth 5357c4015bdSSimon Farnsworth dev_info(&card->dev->dev, "Changing FPGA to Update mode\n"); 5367c4015bdSSimon Farnsworth iowrite32(1, card->config_regs + FPGA_MODE); 5377c4015bdSSimon Farnsworth data32 = ioread32(card->config_regs + FPGA_MODE); 5387c4015bdSSimon Farnsworth 539fa755b9fSDavid Woodhouse /* Set mode to Chip Erase */ 540fa755b9fSDavid Woodhouse dev_info(&card->dev->dev, "Set FPGA Flash mode to %s Chip Erase\n", 541fa755b9fSDavid Woodhouse chip?"Solos":"FPGA"); 542fa755b9fSDavid Woodhouse iowrite32((chip * 2), card->config_regs + FLASH_MODE); 543fa755b9fSDavid Woodhouse 544fa755b9fSDavid Woodhouse 545fa755b9fSDavid Woodhouse iowrite32(1, card->config_regs + WRITE_FLASH); 546fa755b9fSDavid Woodhouse wait_event(card->fw_wq, !ioread32(card->config_regs + FLASH_BUSY)); 547fa755b9fSDavid Woodhouse 548fa755b9fSDavid Woodhouse for (offset = 0; offset < fw->size; offset += blocksize) { 5497c4015bdSSimon Farnsworth int i; 5507c4015bdSSimon Farnsworth 5517c4015bdSSimon Farnsworth /* Clear write flag */ 5527c4015bdSSimon Farnsworth iowrite32(0, card->config_regs + WRITE_FLASH); 553fa755b9fSDavid Woodhouse 5547c4015bdSSimon Farnsworth /* Set mode to Block Write */ 5557c4015bdSSimon Farnsworth /* dev_info(&card->dev->dev, "Set FPGA Flash mode to Block Write\n"); */ 556fa755b9fSDavid Woodhouse iowrite32(((chip * 2) + 1), card->config_regs + FLASH_MODE); 5577c4015bdSSimon Farnsworth 558fa755b9fSDavid Woodhouse /* Copy block to buffer, swapping each 16 bits */ 559fa755b9fSDavid Woodhouse for(i = 0; i < blocksize; i += 4) { 560fa755b9fSDavid Woodhouse uint32_t word = swahb32p((uint32_t *)(fw->data + offset + i)); 561fa755b9fSDavid Woodhouse iowrite32(word, RX_BUF(card, 3) + i); 562fa755b9fSDavid Woodhouse } 563fa755b9fSDavid Woodhouse 564fa755b9fSDavid Woodhouse /* Specify block number and then trigger flash write */ 565fa755b9fSDavid Woodhouse iowrite32(offset / blocksize, card->config_regs + FLASH_BLOCK); 566fa755b9fSDavid Woodhouse iowrite32(1, card->config_regs + WRITE_FLASH); 567fa755b9fSDavid Woodhouse wait_event(card->fw_wq, !ioread32(card->config_regs + FLASH_BUSY)); 568fa755b9fSDavid Woodhouse } 569fa755b9fSDavid Woodhouse 570fa755b9fSDavid Woodhouse release_firmware(fw); 5717c4015bdSSimon Farnsworth iowrite32(0, card->config_regs + WRITE_FLASH); 5727c4015bdSSimon Farnsworth iowrite32(0, card->config_regs + FPGA_MODE); 5737c4015bdSSimon Farnsworth iowrite32(0, card->config_regs + FLASH_MODE); 5747c4015bdSSimon Farnsworth dev_info(&card->dev->dev, "Returning FPGA to Data mode\n"); 575fa755b9fSDavid Woodhouse return 0; 5767c4015bdSSimon Farnsworth } 5777c4015bdSSimon Farnsworth 5789c54004eSDavid Woodhouse static irqreturn_t solos_irq(int irq, void *dev_id) 5799c54004eSDavid Woodhouse { 5809c54004eSDavid Woodhouse struct solos_card *card = dev_id; 5819c54004eSDavid Woodhouse int handled = 1; 5829c54004eSDavid Woodhouse 5839c54004eSDavid Woodhouse iowrite32(0, card->config_regs + IRQ_CLEAR); 5849c54004eSDavid Woodhouse 58535c2221bSDavid Woodhouse /* If we're up and running, just kick the tasklet to process TX/RX */ 586fa755b9fSDavid Woodhouse if (card->atmdev[0]) 5879c54004eSDavid Woodhouse tasklet_schedule(&card->tlet); 588fa755b9fSDavid Woodhouse else 589fa755b9fSDavid Woodhouse wake_up(&card->fw_wq); 5909c54004eSDavid Woodhouse 5919c54004eSDavid Woodhouse return IRQ_RETVAL(handled); 5929c54004eSDavid Woodhouse } 5939c54004eSDavid Woodhouse 5949c54004eSDavid Woodhouse void solos_bh(unsigned long card_arg) 5959c54004eSDavid Woodhouse { 5969c54004eSDavid Woodhouse struct solos_card *card = (void *)card_arg; 5979c54004eSDavid Woodhouse uint32_t card_flags; 5989c54004eSDavid Woodhouse uint32_t rx_done = 0; 59935c2221bSDavid Woodhouse int port; 6009c54004eSDavid Woodhouse 60135c2221bSDavid Woodhouse /* 60235c2221bSDavid Woodhouse * Since fpga_tx() is going to need to read the flags under its lock, 60335c2221bSDavid Woodhouse * it can return them to us so that we don't have to hit PCI MMIO 60435c2221bSDavid Woodhouse * again for the same information 60535c2221bSDavid Woodhouse */ 60635c2221bSDavid Woodhouse card_flags = fpga_tx(card); 6079c54004eSDavid Woodhouse 6089c54004eSDavid Woodhouse for (port = 0; port < card->nr_ports; port++) { 6099c54004eSDavid Woodhouse if (card_flags & (0x10 << port)) { 61090937231SDavid Woodhouse struct pkt_hdr _hdr, *header; 6119c54004eSDavid Woodhouse struct sk_buff *skb; 6129c54004eSDavid Woodhouse struct atm_vcc *vcc; 6139c54004eSDavid Woodhouse int size; 6149c54004eSDavid Woodhouse 61590937231SDavid Woodhouse if (card->using_dma) { 61690937231SDavid Woodhouse skb = card->rx_skb[port]; 617eaf83e39SDavid Woodhouse card->rx_skb[port] = NULL; 61890937231SDavid Woodhouse 619eaf83e39SDavid Woodhouse pci_unmap_single(card->dev, SKB_CB(skb)->dma_addr, 620eaf83e39SDavid Woodhouse RX_DMA_SIZE, PCI_DMA_FROMDEVICE); 621eaf83e39SDavid Woodhouse 62290937231SDavid Woodhouse header = (void *)skb->data; 62390937231SDavid Woodhouse size = le16_to_cpu(header->size); 62490937231SDavid Woodhouse skb_put(skb, size + sizeof(*header)); 62590937231SDavid Woodhouse skb_pull(skb, sizeof(*header)); 62690937231SDavid Woodhouse } else { 62790937231SDavid Woodhouse header = &_hdr; 62890937231SDavid Woodhouse 6299c54004eSDavid Woodhouse rx_done |= 0x10 << port; 6309c54004eSDavid Woodhouse 63190937231SDavid Woodhouse memcpy_fromio(header, RX_BUF(card, port), sizeof(*header)); 6329c54004eSDavid Woodhouse 63390937231SDavid Woodhouse size = le16_to_cpu(header->size); 6349c54004eSDavid Woodhouse 63587ebb186SDavid Woodhouse skb = alloc_skb(size + 1, GFP_ATOMIC); 6369c54004eSDavid Woodhouse if (!skb) { 6379c54004eSDavid Woodhouse if (net_ratelimit()) 6389c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff for RX\n"); 6399c54004eSDavid Woodhouse continue; 6409c54004eSDavid Woodhouse } 6419c54004eSDavid Woodhouse 6429c54004eSDavid Woodhouse memcpy_fromio(skb_put(skb, size), 64390937231SDavid Woodhouse RX_BUF(card, port) + sizeof(*header), 6449c54004eSDavid Woodhouse size); 64590937231SDavid Woodhouse } 6469c54004eSDavid Woodhouse if (atmdebug) { 6479c54004eSDavid Woodhouse dev_info(&card->dev->dev, "Received: device %d\n", port); 6489c54004eSDavid Woodhouse dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n", 64990937231SDavid Woodhouse size, le16_to_cpu(header->vpi), 65090937231SDavid Woodhouse le16_to_cpu(header->vci)); 6519c54004eSDavid Woodhouse print_buffer(skb); 6529c54004eSDavid Woodhouse } 6539c54004eSDavid Woodhouse 65490937231SDavid Woodhouse switch (le16_to_cpu(header->type)) { 6559c54004eSDavid Woodhouse case PKT_DATA: 65690937231SDavid Woodhouse vcc = find_vcc(card->atmdev[port], le16_to_cpu(header->vpi), 65790937231SDavid Woodhouse le16_to_cpu(header->vci)); 6589c54004eSDavid Woodhouse if (!vcc) { 6599c54004eSDavid Woodhouse if (net_ratelimit()) 6609c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Received packet for unknown VCI.VPI %d.%d on port %d\n", 66190937231SDavid Woodhouse le16_to_cpu(header->vci), le16_to_cpu(header->vpi), 6629c54004eSDavid Woodhouse port); 6639c54004eSDavid Woodhouse continue; 6649c54004eSDavid Woodhouse } 6659c54004eSDavid Woodhouse atm_charge(vcc, skb->truesize); 6669c54004eSDavid Woodhouse vcc->push(vcc, skb); 6679c54004eSDavid Woodhouse atomic_inc(&vcc->stats->rx); 6689c54004eSDavid Woodhouse break; 6699c54004eSDavid Woodhouse 67087ebb186SDavid Woodhouse case PKT_STATUS: 67195852f48SDavid Woodhouse if (process_status(card, port, skb) && 67295852f48SDavid Woodhouse net_ratelimit()) { 67395852f48SDavid Woodhouse dev_warn(&card->dev->dev, "Bad status packet of %d bytes on port %d:\n", skb->len, port); 67495852f48SDavid Woodhouse print_buffer(skb); 67595852f48SDavid Woodhouse } 676eaf83e39SDavid Woodhouse dev_kfree_skb_any(skb); 67787ebb186SDavid Woodhouse break; 67887ebb186SDavid Woodhouse 6799c54004eSDavid Woodhouse case PKT_COMMAND: 6809c54004eSDavid Woodhouse default: /* FIXME: Not really, surely? */ 68101e2ffacSDavid Woodhouse if (process_command(card, port, skb)) 68201e2ffacSDavid Woodhouse break; 6839c54004eSDavid Woodhouse spin_lock(&card->cli_queue_lock); 6849c54004eSDavid Woodhouse if (skb_queue_len(&card->cli_queue[port]) > 10) { 6859c54004eSDavid Woodhouse if (net_ratelimit()) 6869c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Dropping console response on port %d\n", 6879c54004eSDavid Woodhouse port); 688eaf83e39SDavid Woodhouse dev_kfree_skb_any(skb); 6899c54004eSDavid Woodhouse } else 6909c54004eSDavid Woodhouse skb_queue_tail(&card->cli_queue[port], skb); 6919c54004eSDavid Woodhouse spin_unlock(&card->cli_queue_lock); 6929c54004eSDavid Woodhouse break; 6939c54004eSDavid Woodhouse } 6949c54004eSDavid Woodhouse } 695eaf83e39SDavid Woodhouse /* Allocate RX skbs for any ports which need them */ 696eaf83e39SDavid Woodhouse if (card->using_dma && card->atmdev[port] && 697eaf83e39SDavid Woodhouse !card->rx_skb[port]) { 698eaf83e39SDavid Woodhouse struct sk_buff *skb = alloc_skb(RX_DMA_SIZE, GFP_ATOMIC); 699eaf83e39SDavid Woodhouse if (skb) { 700eaf83e39SDavid Woodhouse SKB_CB(skb)->dma_addr = 701eaf83e39SDavid Woodhouse pci_map_single(card->dev, skb->data, 702eaf83e39SDavid Woodhouse RX_DMA_SIZE, PCI_DMA_FROMDEVICE); 703eaf83e39SDavid Woodhouse iowrite32(SKB_CB(skb)->dma_addr, 704eaf83e39SDavid Woodhouse card->config_regs + RX_DMA_ADDR(port)); 705eaf83e39SDavid Woodhouse card->rx_skb[port] = skb; 706eaf83e39SDavid Woodhouse } else { 707eaf83e39SDavid Woodhouse if (net_ratelimit()) 708eaf83e39SDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate RX skb"); 709eaf83e39SDavid Woodhouse 710eaf83e39SDavid Woodhouse /* We'll have to try again later */ 711eaf83e39SDavid Woodhouse tasklet_schedule(&card->tlet); 712eaf83e39SDavid Woodhouse } 713eaf83e39SDavid Woodhouse } 7149c54004eSDavid Woodhouse } 7159c54004eSDavid Woodhouse if (rx_done) 7169c54004eSDavid Woodhouse iowrite32(rx_done, card->config_regs + FLAGS_ADDR); 7179c54004eSDavid Woodhouse 7189c54004eSDavid Woodhouse return; 7199c54004eSDavid Woodhouse } 7209c54004eSDavid Woodhouse 7219c54004eSDavid Woodhouse static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci) 7229c54004eSDavid Woodhouse { 7239c54004eSDavid Woodhouse struct hlist_head *head; 7249c54004eSDavid Woodhouse struct atm_vcc *vcc = NULL; 7259c54004eSDavid Woodhouse struct hlist_node *node; 7269c54004eSDavid Woodhouse struct sock *s; 7279c54004eSDavid Woodhouse 7289c54004eSDavid Woodhouse read_lock(&vcc_sklist_lock); 7299c54004eSDavid Woodhouse head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; 7309c54004eSDavid Woodhouse sk_for_each(s, node, head) { 7319c54004eSDavid Woodhouse vcc = atm_sk(s); 7329c54004eSDavid Woodhouse if (vcc->dev == dev && vcc->vci == vci && 7339c54004eSDavid Woodhouse vcc->vpi == vpi && vcc->qos.rxtp.traffic_class != ATM_NONE) 7349c54004eSDavid Woodhouse goto out; 7359c54004eSDavid Woodhouse } 7369c54004eSDavid Woodhouse vcc = NULL; 7379c54004eSDavid Woodhouse out: 7389c54004eSDavid Woodhouse read_unlock(&vcc_sklist_lock); 7399c54004eSDavid Woodhouse return vcc; 7409c54004eSDavid Woodhouse } 7419c54004eSDavid Woodhouse 7429c54004eSDavid Woodhouse static int list_vccs(int vci) 7439c54004eSDavid Woodhouse { 7449c54004eSDavid Woodhouse struct hlist_head *head; 7459c54004eSDavid Woodhouse struct atm_vcc *vcc; 7469c54004eSDavid Woodhouse struct hlist_node *node; 7479c54004eSDavid Woodhouse struct sock *s; 7489c54004eSDavid Woodhouse int num_found = 0; 7499c54004eSDavid Woodhouse int i; 7509c54004eSDavid Woodhouse 7519c54004eSDavid Woodhouse read_lock(&vcc_sklist_lock); 7529c54004eSDavid Woodhouse if (vci != 0){ 7539c54004eSDavid Woodhouse head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; 7549c54004eSDavid Woodhouse sk_for_each(s, node, head) { 7559c54004eSDavid Woodhouse num_found ++; 7569c54004eSDavid Woodhouse vcc = atm_sk(s); 7579c54004eSDavid Woodhouse printk(KERN_DEBUG "Device: %d Vpi: %d Vci: %d\n", 7589c54004eSDavid Woodhouse vcc->dev->number, 7599c54004eSDavid Woodhouse vcc->vpi, 7609c54004eSDavid Woodhouse vcc->vci); 7619c54004eSDavid Woodhouse } 7629c54004eSDavid Woodhouse } else { 7631e615df6SDavid Woodhouse for(i = 0; i < VCC_HTABLE_SIZE; i++){ 7649c54004eSDavid Woodhouse head = &vcc_hash[i]; 7659c54004eSDavid Woodhouse sk_for_each(s, node, head) { 7669c54004eSDavid Woodhouse num_found ++; 7679c54004eSDavid Woodhouse vcc = atm_sk(s); 7689c54004eSDavid Woodhouse printk(KERN_DEBUG "Device: %d Vpi: %d Vci: %d\n", 7699c54004eSDavid Woodhouse vcc->dev->number, 7709c54004eSDavid Woodhouse vcc->vpi, 7719c54004eSDavid Woodhouse vcc->vci); 7729c54004eSDavid Woodhouse } 7739c54004eSDavid Woodhouse } 7749c54004eSDavid Woodhouse } 7759c54004eSDavid Woodhouse read_unlock(&vcc_sklist_lock); 7769c54004eSDavid Woodhouse return num_found; 7779c54004eSDavid Woodhouse } 7789c54004eSDavid Woodhouse 7791e615df6SDavid Woodhouse static void release_vccs(struct atm_dev *dev) 7801e615df6SDavid Woodhouse { 7811e615df6SDavid Woodhouse int i; 7821e615df6SDavid Woodhouse 7831e615df6SDavid Woodhouse write_lock_irq(&vcc_sklist_lock); 7841e615df6SDavid Woodhouse for (i = 0; i < VCC_HTABLE_SIZE; i++) { 7851e615df6SDavid Woodhouse struct hlist_head *head = &vcc_hash[i]; 7861e615df6SDavid Woodhouse struct hlist_node *node, *tmp; 7871e615df6SDavid Woodhouse struct sock *s; 7881e615df6SDavid Woodhouse struct atm_vcc *vcc; 7891e615df6SDavid Woodhouse 7901e615df6SDavid Woodhouse sk_for_each_safe(s, node, tmp, head) { 7911e615df6SDavid Woodhouse vcc = atm_sk(s); 7921e615df6SDavid Woodhouse if (vcc->dev == dev) { 7931e615df6SDavid Woodhouse vcc_release_async(vcc, -EPIPE); 7941e615df6SDavid Woodhouse sk_del_node_init(s); 7951e615df6SDavid Woodhouse } 7961e615df6SDavid Woodhouse } 7971e615df6SDavid Woodhouse } 7981e615df6SDavid Woodhouse write_unlock_irq(&vcc_sklist_lock); 7991e615df6SDavid Woodhouse } 8001e615df6SDavid Woodhouse 8019c54004eSDavid Woodhouse 8029c54004eSDavid Woodhouse static int popen(struct atm_vcc *vcc) 8039c54004eSDavid Woodhouse { 8049c54004eSDavid Woodhouse struct solos_card *card = vcc->dev->dev_data; 8059c54004eSDavid Woodhouse struct sk_buff *skb; 8069c54004eSDavid Woodhouse struct pkt_hdr *header; 8079c54004eSDavid Woodhouse 808b28a4b9aSDavid Woodhouse if (vcc->qos.aal != ATM_AAL5) { 809b28a4b9aSDavid Woodhouse dev_warn(&card->dev->dev, "Unsupported ATM type %d\n", 810b28a4b9aSDavid Woodhouse vcc->qos.aal); 811b28a4b9aSDavid Woodhouse return -EINVAL; 812b28a4b9aSDavid Woodhouse } 813b28a4b9aSDavid Woodhouse 8149c54004eSDavid Woodhouse skb = alloc_skb(sizeof(*header), GFP_ATOMIC); 8159c54004eSDavid Woodhouse if (!skb && net_ratelimit()) { 8169c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in popen()\n"); 8179c54004eSDavid Woodhouse return -ENOMEM; 8189c54004eSDavid Woodhouse } 8199c54004eSDavid Woodhouse header = (void *)skb_put(skb, sizeof(*header)); 8209c54004eSDavid Woodhouse 821b76811afSDavid Woodhouse header->size = cpu_to_le16(0); 8229c54004eSDavid Woodhouse header->vpi = cpu_to_le16(vcc->vpi); 8239c54004eSDavid Woodhouse header->vci = cpu_to_le16(vcc->vci); 8249c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_POPEN); 8259c54004eSDavid Woodhouse 8269c54004eSDavid Woodhouse fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, NULL); 8279c54004eSDavid Woodhouse 828bdc54625SDavid Woodhouse set_bit(ATM_VF_ADDR, &vcc->flags); 8299c54004eSDavid Woodhouse set_bit(ATM_VF_READY, &vcc->flags); 8309c54004eSDavid Woodhouse list_vccs(0); 8319c54004eSDavid Woodhouse 8329c54004eSDavid Woodhouse 8339c54004eSDavid Woodhouse return 0; 8349c54004eSDavid Woodhouse } 8359c54004eSDavid Woodhouse 8369c54004eSDavid Woodhouse static void pclose(struct atm_vcc *vcc) 8379c54004eSDavid Woodhouse { 8389c54004eSDavid Woodhouse struct solos_card *card = vcc->dev->dev_data; 8399c54004eSDavid Woodhouse struct sk_buff *skb; 8409c54004eSDavid Woodhouse struct pkt_hdr *header; 8419c54004eSDavid Woodhouse 8429c54004eSDavid Woodhouse skb = alloc_skb(sizeof(*header), GFP_ATOMIC); 8439c54004eSDavid Woodhouse if (!skb) { 8449c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in pclose()\n"); 8459c54004eSDavid Woodhouse return; 8469c54004eSDavid Woodhouse } 8479c54004eSDavid Woodhouse header = (void *)skb_put(skb, sizeof(*header)); 8489c54004eSDavid Woodhouse 849b76811afSDavid Woodhouse header->size = cpu_to_le16(0); 8509c54004eSDavid Woodhouse header->vpi = cpu_to_le16(vcc->vpi); 8519c54004eSDavid Woodhouse header->vci = cpu_to_le16(vcc->vci); 8529c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_PCLOSE); 8539c54004eSDavid Woodhouse 8549c54004eSDavid Woodhouse fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, NULL); 8559c54004eSDavid Woodhouse 8569c54004eSDavid Woodhouse clear_bit(ATM_VF_ADDR, &vcc->flags); 8579c54004eSDavid Woodhouse clear_bit(ATM_VF_READY, &vcc->flags); 8589c54004eSDavid Woodhouse 8599c54004eSDavid Woodhouse return; 8609c54004eSDavid Woodhouse } 8619c54004eSDavid Woodhouse 8629c54004eSDavid Woodhouse static int print_buffer(struct sk_buff *buf) 8639c54004eSDavid Woodhouse { 8649c54004eSDavid Woodhouse int len,i; 8659c54004eSDavid Woodhouse char msg[500]; 8669c54004eSDavid Woodhouse char item[10]; 8679c54004eSDavid Woodhouse 8689c54004eSDavid Woodhouse len = buf->len; 8699c54004eSDavid Woodhouse for (i = 0; i < len; i++){ 8709c54004eSDavid Woodhouse if(i % 8 == 0) 8719c54004eSDavid Woodhouse sprintf(msg, "%02X: ", i); 8729c54004eSDavid Woodhouse 8739c54004eSDavid Woodhouse sprintf(item,"%02X ",*(buf->data + i)); 8749c54004eSDavid Woodhouse strcat(msg, item); 8759c54004eSDavid Woodhouse if(i % 8 == 7) { 8769c54004eSDavid Woodhouse sprintf(item, "\n"); 8779c54004eSDavid Woodhouse strcat(msg, item); 8789c54004eSDavid Woodhouse printk(KERN_DEBUG "%s", msg); 8799c54004eSDavid Woodhouse } 8809c54004eSDavid Woodhouse } 8819c54004eSDavid Woodhouse if (i % 8 != 0) { 8829c54004eSDavid Woodhouse sprintf(item, "\n"); 8839c54004eSDavid Woodhouse strcat(msg, item); 8849c54004eSDavid Woodhouse printk(KERN_DEBUG "%s", msg); 8859c54004eSDavid Woodhouse } 8869c54004eSDavid Woodhouse printk(KERN_DEBUG "\n"); 8879c54004eSDavid Woodhouse 8889c54004eSDavid Woodhouse return 0; 8899c54004eSDavid Woodhouse } 8909c54004eSDavid Woodhouse 8919c54004eSDavid Woodhouse static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb, 8929c54004eSDavid Woodhouse struct atm_vcc *vcc) 8939c54004eSDavid Woodhouse { 8949c54004eSDavid Woodhouse int old_len; 895f69e4170SDavid Woodhouse unsigned long flags; 8969c54004eSDavid Woodhouse 89790937231SDavid Woodhouse SKB_CB(skb)->vcc = vcc; 8989c54004eSDavid Woodhouse 899f69e4170SDavid Woodhouse spin_lock_irqsave(&card->tx_queue_lock, flags); 9009c54004eSDavid Woodhouse old_len = skb_queue_len(&card->tx_queue[port]); 9019c54004eSDavid Woodhouse skb_queue_tail(&card->tx_queue[port], skb); 90235c2221bSDavid Woodhouse if (!old_len) 903f69e4170SDavid Woodhouse card->tx_mask |= (1 << port); 904f69e4170SDavid Woodhouse spin_unlock_irqrestore(&card->tx_queue_lock, flags); 9059c54004eSDavid Woodhouse 906f69e4170SDavid Woodhouse /* Theoretically we could just schedule the tasklet here, but 907f69e4170SDavid Woodhouse that introduces latency we don't want -- it's noticeable */ 9089c54004eSDavid Woodhouse if (!old_len) 9099c54004eSDavid Woodhouse fpga_tx(card); 9109c54004eSDavid Woodhouse } 9119c54004eSDavid Woodhouse 91235c2221bSDavid Woodhouse static uint32_t fpga_tx(struct solos_card *card) 9139c54004eSDavid Woodhouse { 91435c2221bSDavid Woodhouse uint32_t tx_pending, card_flags; 9159c54004eSDavid Woodhouse uint32_t tx_started = 0; 9169c54004eSDavid Woodhouse struct sk_buff *skb; 9179c54004eSDavid Woodhouse struct atm_vcc *vcc; 9189c54004eSDavid Woodhouse unsigned char port; 9199c54004eSDavid Woodhouse unsigned long flags; 9209c54004eSDavid Woodhouse 9219c54004eSDavid Woodhouse spin_lock_irqsave(&card->tx_lock, flags); 9229c54004eSDavid Woodhouse 92335c2221bSDavid Woodhouse card_flags = ioread32(card->config_regs + FLAGS_ADDR); 92435c2221bSDavid Woodhouse /* 92535c2221bSDavid Woodhouse * The queue lock is required for _writing_ to tx_mask, but we're 92635c2221bSDavid Woodhouse * OK to read it here without locking. The only potential update 92735c2221bSDavid Woodhouse * that we could race with is in fpga_queue() where it sets a bit 92835c2221bSDavid Woodhouse * for a new port... but it's going to call this function again if 92935c2221bSDavid Woodhouse * it's doing that, anyway. 93035c2221bSDavid Woodhouse */ 93135c2221bSDavid Woodhouse tx_pending = card->tx_mask & ~card_flags; 9329c54004eSDavid Woodhouse 93335c2221bSDavid Woodhouse for (port = 0; tx_pending; tx_pending >>= 1, port++) { 93435c2221bSDavid Woodhouse if (tx_pending & 1) { 935eaf83e39SDavid Woodhouse struct sk_buff *oldskb = card->tx_skb[port]; 936eaf83e39SDavid Woodhouse if (oldskb) 937eaf83e39SDavid Woodhouse pci_unmap_single(card->dev, SKB_CB(oldskb)->dma_addr, 938eaf83e39SDavid Woodhouse oldskb->len, PCI_DMA_TODEVICE); 9399c54004eSDavid Woodhouse 9409c54004eSDavid Woodhouse spin_lock(&card->tx_queue_lock); 9419c54004eSDavid Woodhouse skb = skb_dequeue(&card->tx_queue[port]); 942f69e4170SDavid Woodhouse if (!skb) 943f69e4170SDavid Woodhouse card->tx_mask &= ~(1 << port); 9449c54004eSDavid Woodhouse spin_unlock(&card->tx_queue_lock); 9459c54004eSDavid Woodhouse 946eaf83e39SDavid Woodhouse if (skb && !card->using_dma) { 947eaf83e39SDavid Woodhouse memcpy_toio(TX_BUF(card, port), skb->data, skb->len); 948bdc54625SDavid Woodhouse tx_started |= 1 << port; 949eaf83e39SDavid Woodhouse oldskb = skb; /* We're done with this skb already */ 950eaf83e39SDavid Woodhouse } else if (skb && card->using_dma) { 951eaf83e39SDavid Woodhouse SKB_CB(skb)->dma_addr = pci_map_single(card->dev, skb->data, 952eaf83e39SDavid Woodhouse skb->len, PCI_DMA_TODEVICE); 953eaf83e39SDavid Woodhouse iowrite32(SKB_CB(skb)->dma_addr, 954eaf83e39SDavid Woodhouse card->config_regs + TX_DMA_ADDR(port)); 955eaf83e39SDavid Woodhouse } 956eaf83e39SDavid Woodhouse 957eaf83e39SDavid Woodhouse if (!oldskb) 9589c54004eSDavid Woodhouse continue; 9599c54004eSDavid Woodhouse 960eaf83e39SDavid Woodhouse /* Clean up and free oldskb now it's gone */ 9619c54004eSDavid Woodhouse if (atmdebug) { 9629c54004eSDavid Woodhouse dev_info(&card->dev->dev, "Transmitted: port %d\n", 9639c54004eSDavid Woodhouse port); 964eaf83e39SDavid Woodhouse print_buffer(oldskb); 9659c54004eSDavid Woodhouse } 96690937231SDavid Woodhouse 96790937231SDavid Woodhouse vcc = SKB_CB(oldskb)->vcc; 96890937231SDavid Woodhouse 96990937231SDavid Woodhouse if (vcc) { 97090937231SDavid Woodhouse atomic_inc(&vcc->stats->tx); 97190937231SDavid Woodhouse solos_pop(vcc, oldskb); 97290937231SDavid Woodhouse } else 97390937231SDavid Woodhouse dev_kfree_skb_irq(oldskb); 97490937231SDavid Woodhouse 9759c54004eSDavid Woodhouse } 9769c54004eSDavid Woodhouse } 977bdc54625SDavid Woodhouse /* For non-DMA TX, write the 'TX start' bit for all four ports simultaneously */ 9789c54004eSDavid Woodhouse if (tx_started) 9799c54004eSDavid Woodhouse iowrite32(tx_started, card->config_regs + FLAGS_ADDR); 9809c54004eSDavid Woodhouse 9819c54004eSDavid Woodhouse spin_unlock_irqrestore(&card->tx_lock, flags); 98235c2221bSDavid Woodhouse return card_flags; 9839c54004eSDavid Woodhouse } 9849c54004eSDavid Woodhouse 9859c54004eSDavid Woodhouse static int psend(struct atm_vcc *vcc, struct sk_buff *skb) 9869c54004eSDavid Woodhouse { 9879c54004eSDavid Woodhouse struct solos_card *card = vcc->dev->dev_data; 9889c54004eSDavid Woodhouse struct pkt_hdr *header; 989b76811afSDavid Woodhouse int pktlen; 9909c54004eSDavid Woodhouse 991b76811afSDavid Woodhouse pktlen = skb->len; 992b76811afSDavid Woodhouse if (pktlen > (BUF_SIZE - sizeof(*header))) { 9939c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Length of PDU is too large. Dropping PDU.\n"); 9949c54004eSDavid Woodhouse solos_pop(vcc, skb); 9959c54004eSDavid Woodhouse return 0; 9969c54004eSDavid Woodhouse } 9979c54004eSDavid Woodhouse 9989c54004eSDavid Woodhouse if (!skb_clone_writable(skb, sizeof(*header))) { 9999c54004eSDavid Woodhouse int expand_by = 0; 10009c54004eSDavid Woodhouse int ret; 10019c54004eSDavid Woodhouse 10029c54004eSDavid Woodhouse if (skb_headroom(skb) < sizeof(*header)) 10039c54004eSDavid Woodhouse expand_by = sizeof(*header) - skb_headroom(skb); 10049c54004eSDavid Woodhouse 10059c54004eSDavid Woodhouse ret = pskb_expand_head(skb, expand_by, 0, GFP_ATOMIC); 10069c54004eSDavid Woodhouse if (ret) { 10074306cad6SSimon Farnsworth dev_warn(&card->dev->dev, "pskb_expand_head failed.\n"); 10089c54004eSDavid Woodhouse solos_pop(vcc, skb); 10099c54004eSDavid Woodhouse return ret; 10109c54004eSDavid Woodhouse } 10119c54004eSDavid Woodhouse } 10129c54004eSDavid Woodhouse 10139c54004eSDavid Woodhouse header = (void *)skb_push(skb, sizeof(*header)); 10149c54004eSDavid Woodhouse 1015b76811afSDavid Woodhouse /* This does _not_ include the size of the header */ 1016b76811afSDavid Woodhouse header->size = cpu_to_le16(pktlen); 10179c54004eSDavid Woodhouse header->vpi = cpu_to_le16(vcc->vpi); 10189c54004eSDavid Woodhouse header->vci = cpu_to_le16(vcc->vci); 10199c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_DATA); 10209c54004eSDavid Woodhouse 10219c54004eSDavid Woodhouse fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, vcc); 10229c54004eSDavid Woodhouse 10239c54004eSDavid Woodhouse return 0; 10249c54004eSDavid Woodhouse } 10259c54004eSDavid Woodhouse 10269c54004eSDavid Woodhouse static struct atmdev_ops fpga_ops = { 10279c54004eSDavid Woodhouse .open = popen, 10289c54004eSDavid Woodhouse .close = pclose, 10299c54004eSDavid Woodhouse .ioctl = NULL, 10309c54004eSDavid Woodhouse .getsockopt = NULL, 10319c54004eSDavid Woodhouse .setsockopt = NULL, 10329c54004eSDavid Woodhouse .send = psend, 10339c54004eSDavid Woodhouse .send_oam = NULL, 10349c54004eSDavid Woodhouse .phy_put = NULL, 10359c54004eSDavid Woodhouse .phy_get = NULL, 10369c54004eSDavid Woodhouse .change_qos = NULL, 10379c54004eSDavid Woodhouse .proc_read = NULL, 10389c54004eSDavid Woodhouse .owner = THIS_MODULE 10399c54004eSDavid Woodhouse }; 10409c54004eSDavid Woodhouse 10419c54004eSDavid Woodhouse static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) 10429c54004eSDavid Woodhouse { 1043cd5549e0SDavid Woodhouse int err; 10449c54004eSDavid Woodhouse uint16_t fpga_ver; 10459c54004eSDavid Woodhouse uint8_t major_ver, minor_ver; 10469c54004eSDavid Woodhouse uint32_t data32; 10479c54004eSDavid Woodhouse struct solos_card *card; 10489c54004eSDavid Woodhouse 10499c54004eSDavid Woodhouse card = kzalloc(sizeof(*card), GFP_KERNEL); 10509c54004eSDavid Woodhouse if (!card) 10519c54004eSDavid Woodhouse return -ENOMEM; 10529c54004eSDavid Woodhouse 10539c54004eSDavid Woodhouse card->dev = dev; 1054fa755b9fSDavid Woodhouse init_waitqueue_head(&card->fw_wq); 105501e2ffacSDavid Woodhouse init_waitqueue_head(&card->param_wq); 10569c54004eSDavid Woodhouse 10579c54004eSDavid Woodhouse err = pci_enable_device(dev); 10589c54004eSDavid Woodhouse if (err) { 10599c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to enable PCI device\n"); 10609c54004eSDavid Woodhouse goto out; 10619c54004eSDavid Woodhouse } 10629c54004eSDavid Woodhouse 106390937231SDavid Woodhouse err = pci_set_dma_mask(dev, DMA_32BIT_MASK); 106490937231SDavid Woodhouse if (err) { 106590937231SDavid Woodhouse dev_warn(&dev->dev, "Failed to set 32-bit DMA mask\n"); 106690937231SDavid Woodhouse goto out; 106790937231SDavid Woodhouse } 106890937231SDavid Woodhouse 10699c54004eSDavid Woodhouse err = pci_request_regions(dev, "solos"); 10709c54004eSDavid Woodhouse if (err) { 10719c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to request regions\n"); 10729c54004eSDavid Woodhouse goto out; 10739c54004eSDavid Woodhouse } 10749c54004eSDavid Woodhouse 10759c54004eSDavid Woodhouse card->config_regs = pci_iomap(dev, 0, CONFIG_RAM_SIZE); 10769c54004eSDavid Woodhouse if (!card->config_regs) { 10779c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to ioremap config registers\n"); 10789c54004eSDavid Woodhouse goto out_release_regions; 10799c54004eSDavid Woodhouse } 10809c54004eSDavid Woodhouse card->buffers = pci_iomap(dev, 1, DATA_RAM_SIZE); 10819c54004eSDavid Woodhouse if (!card->buffers) { 10829c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to ioremap data buffers\n"); 10839c54004eSDavid Woodhouse goto out_unmap_config; 10849c54004eSDavid Woodhouse } 10859c54004eSDavid Woodhouse 1086cc3657e1SDavid Woodhouse if (reset) { 1087cc3657e1SDavid Woodhouse iowrite32(1, card->config_regs + FPGA_MODE); 1088cc3657e1SDavid Woodhouse data32 = ioread32(card->config_regs + FPGA_MODE); 1089cc3657e1SDavid Woodhouse 1090cc3657e1SDavid Woodhouse iowrite32(0, card->config_regs + FPGA_MODE); 1091cc3657e1SDavid Woodhouse data32 = ioread32(card->config_regs + FPGA_MODE); 1092cc3657e1SDavid Woodhouse } 10939c54004eSDavid Woodhouse 10949c54004eSDavid Woodhouse data32 = ioread32(card->config_regs + FPGA_VER); 10959c54004eSDavid Woodhouse fpga_ver = (data32 & 0x0000FFFF); 10969c54004eSDavid Woodhouse major_ver = ((data32 & 0xFF000000) >> 24); 10979c54004eSDavid Woodhouse minor_ver = ((data32 & 0x00FF0000) >> 16); 10989c54004eSDavid Woodhouse dev_info(&dev->dev, "Solos FPGA Version %d.%02d svn-%d\n", 10999c54004eSDavid Woodhouse major_ver, minor_ver, fpga_ver); 11009c54004eSDavid Woodhouse 110190937231SDavid Woodhouse if (fpga_ver > 27) 110290937231SDavid Woodhouse card->using_dma = 1; 1103eab50f73SDavid Woodhouse else { 1104eab50f73SDavid Woodhouse /* Set RX empty flag for all ports */ 1105eab50f73SDavid Woodhouse iowrite32(0xF0, card->config_regs + FLAGS_ADDR); 1106eab50f73SDavid Woodhouse } 110790937231SDavid Woodhouse 11089c54004eSDavid Woodhouse card->nr_ports = 2; /* FIXME: Detect daughterboard */ 11099c54004eSDavid Woodhouse 11109c54004eSDavid Woodhouse pci_set_drvdata(dev, card); 1111fa755b9fSDavid Woodhouse 11129c54004eSDavid Woodhouse tasklet_init(&card->tlet, solos_bh, (unsigned long)card); 11139c54004eSDavid Woodhouse spin_lock_init(&card->tx_lock); 11149c54004eSDavid Woodhouse spin_lock_init(&card->tx_queue_lock); 11159c54004eSDavid Woodhouse spin_lock_init(&card->cli_queue_lock); 111601e2ffacSDavid Woodhouse spin_lock_init(&card->param_queue_lock); 111701e2ffacSDavid Woodhouse INIT_LIST_HEAD(&card->param_queue); 1118fa755b9fSDavid Woodhouse 1119fcd82664SDavid Woodhouse err = request_irq(dev->irq, solos_irq, IRQF_SHARED, 11209c54004eSDavid Woodhouse "solos-pci", card); 1121fa755b9fSDavid Woodhouse if (err) { 11229c54004eSDavid Woodhouse dev_dbg(&card->dev->dev, "Failed to request interrupt IRQ: %d\n", dev->irq); 1123fa755b9fSDavid Woodhouse goto out_unmap_both; 1124fa755b9fSDavid Woodhouse } 11259c54004eSDavid Woodhouse 11269c54004eSDavid Woodhouse iowrite32(1, card->config_regs + IRQ_EN_ADDR); 11279c54004eSDavid Woodhouse 1128fa755b9fSDavid Woodhouse if (fpga_upgrade) 1129fa755b9fSDavid Woodhouse flash_upgrade(card, 0); 1130fa755b9fSDavid Woodhouse 1131fa755b9fSDavid Woodhouse if (firmware_upgrade) 1132fa755b9fSDavid Woodhouse flash_upgrade(card, 1); 1133fa755b9fSDavid Woodhouse 1134fa755b9fSDavid Woodhouse err = atm_init(card); 1135fa755b9fSDavid Woodhouse if (err) 1136fa755b9fSDavid Woodhouse goto out_free_irq; 1137fa755b9fSDavid Woodhouse 11389c54004eSDavid Woodhouse return 0; 11399c54004eSDavid Woodhouse 1140fa755b9fSDavid Woodhouse out_free_irq: 1141fa755b9fSDavid Woodhouse iowrite32(0, card->config_regs + IRQ_EN_ADDR); 1142fa755b9fSDavid Woodhouse free_irq(dev->irq, card); 1143fa755b9fSDavid Woodhouse tasklet_kill(&card->tlet); 1144fa755b9fSDavid Woodhouse 11459c54004eSDavid Woodhouse out_unmap_both: 1146fa755b9fSDavid Woodhouse pci_set_drvdata(dev, NULL); 11479c54004eSDavid Woodhouse pci_iounmap(dev, card->config_regs); 11489c54004eSDavid Woodhouse out_unmap_config: 11499c54004eSDavid Woodhouse pci_iounmap(dev, card->buffers); 11509c54004eSDavid Woodhouse out_release_regions: 11519c54004eSDavid Woodhouse pci_release_regions(dev); 11529c54004eSDavid Woodhouse out: 11539c54004eSDavid Woodhouse return err; 11549c54004eSDavid Woodhouse } 11559c54004eSDavid Woodhouse 11569c54004eSDavid Woodhouse static int atm_init(struct solos_card *card) 11579c54004eSDavid Woodhouse { 11589c54004eSDavid Woodhouse int i; 11599c54004eSDavid Woodhouse 11609c54004eSDavid Woodhouse for (i = 0; i < card->nr_ports; i++) { 116187ebb186SDavid Woodhouse struct sk_buff *skb; 116287ebb186SDavid Woodhouse struct pkt_hdr *header; 116387ebb186SDavid Woodhouse 11649c54004eSDavid Woodhouse skb_queue_head_init(&card->tx_queue[i]); 11659c54004eSDavid Woodhouse skb_queue_head_init(&card->cli_queue[i]); 11669c54004eSDavid Woodhouse 11679c54004eSDavid Woodhouse card->atmdev[i] = atm_dev_register("solos-pci", &fpga_ops, -1, NULL); 11689c54004eSDavid Woodhouse if (!card->atmdev[i]) { 11699c54004eSDavid Woodhouse dev_err(&card->dev->dev, "Could not register ATM device %d\n", i); 11709c54004eSDavid Woodhouse atm_remove(card); 11719c54004eSDavid Woodhouse return -ENODEV; 11729c54004eSDavid Woodhouse } 11739c54004eSDavid Woodhouse if (device_create_file(&card->atmdev[i]->class_dev, &dev_attr_console)) 11749c54004eSDavid Woodhouse dev_err(&card->dev->dev, "Could not register console for ATM device %d\n", i); 1175d057f0a4SDavid Woodhouse if (sysfs_create_group(&card->atmdev[i]->class_dev.kobj, &solos_attr_group)) 1176d057f0a4SDavid Woodhouse dev_err(&card->dev->dev, "Could not register parameter group for ATM device %d\n", i); 11779c54004eSDavid Woodhouse 11789c54004eSDavid Woodhouse dev_info(&card->dev->dev, "Registered ATM device %d\n", card->atmdev[i]->number); 11799c54004eSDavid Woodhouse 11809c54004eSDavid Woodhouse card->atmdev[i]->ci_range.vpi_bits = 8; 11819c54004eSDavid Woodhouse card->atmdev[i]->ci_range.vci_bits = 16; 11829c54004eSDavid Woodhouse card->atmdev[i]->dev_data = card; 11839c54004eSDavid Woodhouse card->atmdev[i]->phy_data = (void *)(unsigned long)i; 118487ebb186SDavid Woodhouse card->atmdev[i]->signal = ATM_PHY_SIG_UNKNOWN; 118587ebb186SDavid Woodhouse 118687ebb186SDavid Woodhouse skb = alloc_skb(sizeof(*header), GFP_ATOMIC); 118787ebb186SDavid Woodhouse if (!skb) { 118887ebb186SDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in atm_init()\n"); 118987ebb186SDavid Woodhouse continue; 119087ebb186SDavid Woodhouse } 119187ebb186SDavid Woodhouse 119287ebb186SDavid Woodhouse header = (void *)skb_put(skb, sizeof(*header)); 119387ebb186SDavid Woodhouse 119487ebb186SDavid Woodhouse header->size = cpu_to_le16(0); 119587ebb186SDavid Woodhouse header->vpi = cpu_to_le16(0); 119687ebb186SDavid Woodhouse header->vci = cpu_to_le16(0); 119787ebb186SDavid Woodhouse header->type = cpu_to_le16(PKT_STATUS); 119887ebb186SDavid Woodhouse 119987ebb186SDavid Woodhouse fpga_queue(card, i, skb, NULL); 12009c54004eSDavid Woodhouse } 12019c54004eSDavid Woodhouse return 0; 12029c54004eSDavid Woodhouse } 12039c54004eSDavid Woodhouse 12049c54004eSDavid Woodhouse static void atm_remove(struct solos_card *card) 12059c54004eSDavid Woodhouse { 12069c54004eSDavid Woodhouse int i; 12079c54004eSDavid Woodhouse 12089c54004eSDavid Woodhouse for (i = 0; i < card->nr_ports; i++) { 12099c54004eSDavid Woodhouse if (card->atmdev[i]) { 12109c54004eSDavid Woodhouse dev_info(&card->dev->dev, "Unregistering ATM device %d\n", card->atmdev[i]->number); 1211c0fe3026SDavid Woodhouse 1212c0fe3026SDavid Woodhouse sysfs_remove_group(&card->atmdev[i]->class_dev.kobj, &solos_attr_group); 12139c54004eSDavid Woodhouse atm_dev_deregister(card->atmdev[i]); 12149c54004eSDavid Woodhouse } 12159c54004eSDavid Woodhouse } 12169c54004eSDavid Woodhouse } 12179c54004eSDavid Woodhouse 12189c54004eSDavid Woodhouse static void fpga_remove(struct pci_dev *dev) 12199c54004eSDavid Woodhouse { 12209c54004eSDavid Woodhouse struct solos_card *card = pci_get_drvdata(dev); 12219c54004eSDavid Woodhouse 12229c54004eSDavid Woodhouse atm_remove(card); 12239c54004eSDavid Woodhouse 12249c54004eSDavid Woodhouse iowrite32(0, card->config_regs + IRQ_EN_ADDR); 12259c54004eSDavid Woodhouse free_irq(dev->irq, card); 12269c54004eSDavid Woodhouse tasklet_kill(&card->tlet); 12279c54004eSDavid Woodhouse 12289c54004eSDavid Woodhouse pci_iounmap(dev, card->buffers); 12299c54004eSDavid Woodhouse pci_iounmap(dev, card->config_regs); 12309c54004eSDavid Woodhouse 12319c54004eSDavid Woodhouse pci_release_regions(dev); 12329c54004eSDavid Woodhouse pci_disable_device(dev); 12339c54004eSDavid Woodhouse 12349c54004eSDavid Woodhouse pci_set_drvdata(dev, NULL); 12359c54004eSDavid Woodhouse kfree(card); 12369c54004eSDavid Woodhouse } 12379c54004eSDavid Woodhouse 12389c54004eSDavid Woodhouse static struct pci_device_id fpga_pci_tbl[] __devinitdata = { 12399c54004eSDavid Woodhouse { 0x10ee, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 12409c54004eSDavid Woodhouse { 0, } 12419c54004eSDavid Woodhouse }; 12429c54004eSDavid Woodhouse 12439c54004eSDavid Woodhouse MODULE_DEVICE_TABLE(pci,fpga_pci_tbl); 12449c54004eSDavid Woodhouse 12459c54004eSDavid Woodhouse static struct pci_driver fpga_driver = { 12469c54004eSDavid Woodhouse .name = "solos", 12479c54004eSDavid Woodhouse .id_table = fpga_pci_tbl, 12489c54004eSDavid Woodhouse .probe = fpga_probe, 12499c54004eSDavid Woodhouse .remove = fpga_remove, 12509c54004eSDavid Woodhouse }; 12519c54004eSDavid Woodhouse 12529c54004eSDavid Woodhouse 12539c54004eSDavid Woodhouse static int __init solos_pci_init(void) 12549c54004eSDavid Woodhouse { 12559c54004eSDavid Woodhouse printk(KERN_INFO "Solos PCI Driver Version %s\n", VERSION); 12569c54004eSDavid Woodhouse return pci_register_driver(&fpga_driver); 12579c54004eSDavid Woodhouse } 12589c54004eSDavid Woodhouse 12599c54004eSDavid Woodhouse static void __exit solos_pci_exit(void) 12609c54004eSDavid Woodhouse { 12619c54004eSDavid Woodhouse pci_unregister_driver(&fpga_driver); 12629c54004eSDavid Woodhouse printk(KERN_INFO "Solos PCI Driver %s Unloaded\n", VERSION); 12639c54004eSDavid Woodhouse } 12649c54004eSDavid Woodhouse 12659c54004eSDavid Woodhouse module_init(solos_pci_init); 12669c54004eSDavid Woodhouse module_exit(solos_pci_exit); 1267