11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 29c54004eSDavid Woodhouse /* 39c54004eSDavid Woodhouse * Driver for the Solos PCI ADSL2+ card, designed to support Linux by 49c54004eSDavid Woodhouse * Traverse Technologies -- http://www.traverse.com.au/ 59c54004eSDavid Woodhouse * Xrio Limited -- http://www.xrio.com/ 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 159c54004eSDavid Woodhouse #define DEBUG 169c54004eSDavid Woodhouse #define VERBOSE_DEBUG 179c54004eSDavid Woodhouse 189c54004eSDavid Woodhouse #include <linux/interrupt.h> 199c54004eSDavid Woodhouse #include <linux/module.h> 209c54004eSDavid Woodhouse #include <linux/kernel.h> 219c54004eSDavid Woodhouse #include <linux/errno.h> 229c54004eSDavid Woodhouse #include <linux/ioport.h> 239c54004eSDavid Woodhouse #include <linux/types.h> 249c54004eSDavid Woodhouse #include <linux/pci.h> 259c54004eSDavid Woodhouse #include <linux/atm.h> 269c54004eSDavid Woodhouse #include <linux/atmdev.h> 279c54004eSDavid Woodhouse #include <linux/skbuff.h> 289c54004eSDavid Woodhouse #include <linux/sysfs.h> 299c54004eSDavid Woodhouse #include <linux/device.h> 309c54004eSDavid Woodhouse #include <linux/kobject.h> 317c4015bdSSimon Farnsworth #include <linux/firmware.h> 3201e2ffacSDavid Woodhouse #include <linux/ctype.h> 3301e2ffacSDavid Woodhouse #include <linux/swab.h> 345a0e3ad6STejun Heo #include <linux/slab.h> 359c54004eSDavid Woodhouse 3613af8164SNathan Williams #define VERSION "1.04" 3713af8164SNathan Williams #define DRIVER_VERSION 0x01 389c54004eSDavid Woodhouse #define PTAG "solos-pci" 399c54004eSDavid Woodhouse 409c54004eSDavid Woodhouse #define CONFIG_RAM_SIZE 128 419c54004eSDavid Woodhouse #define FLAGS_ADDR 0x7C 429c54004eSDavid Woodhouse #define IRQ_EN_ADDR 0x78 439c54004eSDavid Woodhouse #define FPGA_VER 0x74 449c54004eSDavid Woodhouse #define IRQ_CLEAR 0x70 457c4015bdSSimon Farnsworth #define WRITE_FLASH 0x6C 467c4015bdSSimon Farnsworth #define PORTS 0x68 477c4015bdSSimon Farnsworth #define FLASH_BLOCK 0x64 487c4015bdSSimon Farnsworth #define FLASH_BUSY 0x60 497c4015bdSSimon Farnsworth #define FPGA_MODE 0x5C 507c4015bdSSimon Farnsworth #define FLASH_MODE 0x58 51f9baad02SNathan Williams #define GPIO_STATUS 0x54 5213af8164SNathan Williams #define DRIVER_VER 0x50 5390937231SDavid Woodhouse #define TX_DMA_ADDR(port) (0x40 + (4 * (port))) 5490937231SDavid Woodhouse #define RX_DMA_ADDR(port) (0x30 + (4 * (port))) 559c54004eSDavid Woodhouse 569c54004eSDavid Woodhouse #define DATA_RAM_SIZE 32768 574dbedf43SNathan Williams #define BUF_SIZE 2048 584dbedf43SNathan Williams #define OLD_BUF_SIZE 4096 /* For FPGA versions <= 2*/ 5913af8164SNathan Williams /* Old boards use ATMEL AD45DB161D flash */ 6013af8164SNathan Williams #define ATMEL_FPGA_PAGE 528 /* FPGA flash page size*/ 6113af8164SNathan Williams #define ATMEL_SOLOS_PAGE 512 /* Solos flash page size*/ 6213af8164SNathan Williams #define ATMEL_FPGA_BLOCK (ATMEL_FPGA_PAGE * 8) /* FPGA block size*/ 6313af8164SNathan Williams #define ATMEL_SOLOS_BLOCK (ATMEL_SOLOS_PAGE * 8) /* Solos block size*/ 6413af8164SNathan Williams /* Current boards use M25P/M25PE SPI flash */ 6513af8164SNathan Williams #define SPI_FLASH_BLOCK (256 * 64) 669c54004eSDavid Woodhouse 674dbedf43SNathan Williams #define RX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2) 684dbedf43SNathan Williams #define TX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2 + (card->buffer_size)) 694dbedf43SNathan Williams #define FLASH_BUF ((card->buffers) + 4*(card->buffer_size)*2) 709c54004eSDavid Woodhouse 71eaf83e39SDavid Woodhouse #define RX_DMA_SIZE 2048 72eaf83e39SDavid Woodhouse 734dbedf43SNathan Williams #define FPGA_VERSION(a,b) (((a) << 8) + (b)) 744dbedf43SNathan Williams #define LEGACY_BUFFERS 2 754dbedf43SNathan Williams #define DMA_SUPPORTED 4 764dbedf43SNathan Williams 77cc3657e1SDavid Woodhouse static int reset = 0; 789c54004eSDavid Woodhouse static int atmdebug = 0; 797c4015bdSSimon Farnsworth static int firmware_upgrade = 0; 807c4015bdSSimon Farnsworth static int fpga_upgrade = 0; 814dbedf43SNathan Williams static int db_firmware_upgrade = 0; 824dbedf43SNathan Williams static int db_fpga_upgrade = 0; 839c54004eSDavid Woodhouse 849c54004eSDavid Woodhouse struct pkt_hdr { 859c54004eSDavid Woodhouse __le16 size; 869c54004eSDavid Woodhouse __le16 vpi; 879c54004eSDavid Woodhouse __le16 vci; 889c54004eSDavid Woodhouse __le16 type; 899c54004eSDavid Woodhouse }; 909c54004eSDavid Woodhouse 9190937231SDavid Woodhouse struct solos_skb_cb { 9290937231SDavid Woodhouse struct atm_vcc *vcc; 9390937231SDavid Woodhouse uint32_t dma_addr; 9490937231SDavid Woodhouse }; 9590937231SDavid Woodhouse 9690937231SDavid Woodhouse 9790937231SDavid Woodhouse #define SKB_CB(skb) ((struct solos_skb_cb *)skb->cb) 9890937231SDavid Woodhouse 999c54004eSDavid Woodhouse #define PKT_DATA 0 1009c54004eSDavid Woodhouse #define PKT_COMMAND 1 1019c54004eSDavid Woodhouse #define PKT_POPEN 3 1029c54004eSDavid Woodhouse #define PKT_PCLOSE 4 10387ebb186SDavid Woodhouse #define PKT_STATUS 5 1049c54004eSDavid Woodhouse 1059c54004eSDavid Woodhouse struct solos_card { 1069c54004eSDavid Woodhouse void __iomem *config_regs; 1079c54004eSDavid Woodhouse void __iomem *buffers; 1089c54004eSDavid Woodhouse int nr_ports; 109f69e4170SDavid Woodhouse int tx_mask; 1109c54004eSDavid Woodhouse struct pci_dev *dev; 1119c54004eSDavid Woodhouse struct atm_dev *atmdev[4]; 1129c54004eSDavid Woodhouse struct tasklet_struct tlet; 1139c54004eSDavid Woodhouse spinlock_t tx_lock; 1149c54004eSDavid Woodhouse spinlock_t tx_queue_lock; 1159c54004eSDavid Woodhouse spinlock_t cli_queue_lock; 11601e2ffacSDavid Woodhouse spinlock_t param_queue_lock; 11701e2ffacSDavid Woodhouse struct list_head param_queue; 1189c54004eSDavid Woodhouse struct sk_buff_head tx_queue[4]; 1199c54004eSDavid Woodhouse struct sk_buff_head cli_queue[4]; 12090937231SDavid Woodhouse struct sk_buff *tx_skb[4]; 12190937231SDavid Woodhouse struct sk_buff *rx_skb[4]; 122152a2a8bSDavid Woodhouse unsigned char *dma_bounce; 12301e2ffacSDavid Woodhouse wait_queue_head_t param_wq; 124fa755b9fSDavid Woodhouse wait_queue_head_t fw_wq; 12590937231SDavid Woodhouse int using_dma; 126152a2a8bSDavid Woodhouse int dma_alignment; 1274dbedf43SNathan Williams int fpga_version; 1284dbedf43SNathan Williams int buffer_size; 12913af8164SNathan Williams int atmel_flash; 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); 167d9ca676bSDan Williams static int atm_init(struct solos_card *, struct device *); 1689c54004eSDavid Woodhouse static void atm_remove(struct solos_card *); 1699c54004eSDavid Woodhouse static int send_command(struct solos_card *card, int dev, const char *buf, size_t size); 1709c54004eSDavid Woodhouse static void solos_bh(unsigned long); 1719c54004eSDavid Woodhouse static int print_buffer(struct sk_buff *buf); 1729c54004eSDavid Woodhouse 1739c54004eSDavid Woodhouse static inline void solos_pop(struct atm_vcc *vcc, struct sk_buff *skb) 1749c54004eSDavid Woodhouse { 1759c54004eSDavid Woodhouse if (vcc->pop) 1769c54004eSDavid Woodhouse vcc->pop(vcc, skb); 1779c54004eSDavid Woodhouse else 1789c54004eSDavid Woodhouse dev_kfree_skb_any(skb); 1799c54004eSDavid Woodhouse } 1809c54004eSDavid Woodhouse 18101e2ffacSDavid Woodhouse static ssize_t solos_param_show(struct device *dev, struct device_attribute *attr, 18201e2ffacSDavid Woodhouse char *buf) 18301e2ffacSDavid Woodhouse { 18401e2ffacSDavid Woodhouse struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev); 18501e2ffacSDavid Woodhouse struct solos_card *card = atmdev->dev_data; 18601e2ffacSDavid Woodhouse struct solos_param prm; 18701e2ffacSDavid Woodhouse struct sk_buff *skb; 18801e2ffacSDavid Woodhouse struct pkt_hdr *header; 18901e2ffacSDavid Woodhouse int buflen; 19001e2ffacSDavid Woodhouse 19101e2ffacSDavid Woodhouse buflen = strlen(attr->attr.name) + 10; 19201e2ffacSDavid Woodhouse 1933456b221SDavid Woodhouse skb = alloc_skb(sizeof(*header) + buflen, GFP_KERNEL); 19401e2ffacSDavid Woodhouse if (!skb) { 19501e2ffacSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in solos_param_show()\n"); 19601e2ffacSDavid Woodhouse return -ENOMEM; 19701e2ffacSDavid Woodhouse } 19801e2ffacSDavid Woodhouse 1994df864c1SJohannes Berg header = skb_put(skb, sizeof(*header)); 20001e2ffacSDavid Woodhouse 20101e2ffacSDavid Woodhouse buflen = snprintf((void *)&header[1], buflen - 1, 20201e2ffacSDavid Woodhouse "L%05d\n%s\n", current->pid, attr->attr.name); 20301e2ffacSDavid Woodhouse skb_put(skb, buflen); 20401e2ffacSDavid Woodhouse 20501e2ffacSDavid Woodhouse header->size = cpu_to_le16(buflen); 20601e2ffacSDavid Woodhouse header->vpi = cpu_to_le16(0); 20701e2ffacSDavid Woodhouse header->vci = cpu_to_le16(0); 20801e2ffacSDavid Woodhouse header->type = cpu_to_le16(PKT_COMMAND); 20901e2ffacSDavid Woodhouse 21001e2ffacSDavid Woodhouse prm.pid = current->pid; 21101e2ffacSDavid Woodhouse prm.response = NULL; 21201e2ffacSDavid Woodhouse prm.port = SOLOS_CHAN(atmdev); 21301e2ffacSDavid Woodhouse 21401e2ffacSDavid Woodhouse spin_lock_irq(&card->param_queue_lock); 21501e2ffacSDavid Woodhouse list_add(&prm.list, &card->param_queue); 21601e2ffacSDavid Woodhouse spin_unlock_irq(&card->param_queue_lock); 21701e2ffacSDavid Woodhouse 21801e2ffacSDavid Woodhouse fpga_queue(card, prm.port, skb, NULL); 21901e2ffacSDavid Woodhouse 22001e2ffacSDavid Woodhouse wait_event_timeout(card->param_wq, prm.response, 5 * HZ); 22101e2ffacSDavid Woodhouse 22201e2ffacSDavid Woodhouse spin_lock_irq(&card->param_queue_lock); 22301e2ffacSDavid Woodhouse list_del(&prm.list); 22401e2ffacSDavid Woodhouse spin_unlock_irq(&card->param_queue_lock); 22501e2ffacSDavid Woodhouse 22601e2ffacSDavid Woodhouse if (!prm.response) 22701e2ffacSDavid Woodhouse return -EIO; 22801e2ffacSDavid Woodhouse 22901e2ffacSDavid Woodhouse buflen = prm.response->len; 23001e2ffacSDavid Woodhouse memcpy(buf, prm.response->data, buflen); 23101e2ffacSDavid Woodhouse kfree_skb(prm.response); 23201e2ffacSDavid Woodhouse 23301e2ffacSDavid Woodhouse return buflen; 23401e2ffacSDavid Woodhouse } 23501e2ffacSDavid Woodhouse 23601e2ffacSDavid Woodhouse static ssize_t solos_param_store(struct device *dev, struct device_attribute *attr, 23701e2ffacSDavid Woodhouse const char *buf, size_t count) 23801e2ffacSDavid Woodhouse { 23901e2ffacSDavid Woodhouse struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev); 24001e2ffacSDavid Woodhouse struct solos_card *card = atmdev->dev_data; 24101e2ffacSDavid Woodhouse struct solos_param prm; 24201e2ffacSDavid Woodhouse struct sk_buff *skb; 24301e2ffacSDavid Woodhouse struct pkt_hdr *header; 24401e2ffacSDavid Woodhouse int buflen; 24501e2ffacSDavid Woodhouse ssize_t ret; 24601e2ffacSDavid Woodhouse 24701e2ffacSDavid Woodhouse buflen = strlen(attr->attr.name) + 11 + count; 24801e2ffacSDavid Woodhouse 2493456b221SDavid Woodhouse skb = alloc_skb(sizeof(*header) + buflen, GFP_KERNEL); 25001e2ffacSDavid Woodhouse if (!skb) { 25101e2ffacSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in solos_param_store()\n"); 25201e2ffacSDavid Woodhouse return -ENOMEM; 25301e2ffacSDavid Woodhouse } 25401e2ffacSDavid Woodhouse 2554df864c1SJohannes Berg header = skb_put(skb, sizeof(*header)); 25601e2ffacSDavid Woodhouse 25701e2ffacSDavid Woodhouse buflen = snprintf((void *)&header[1], buflen - 1, 25801e2ffacSDavid Woodhouse "L%05d\n%s\n%s\n", current->pid, attr->attr.name, buf); 25901e2ffacSDavid Woodhouse 26001e2ffacSDavid Woodhouse skb_put(skb, buflen); 26101e2ffacSDavid Woodhouse header->size = cpu_to_le16(buflen); 26201e2ffacSDavid Woodhouse header->vpi = cpu_to_le16(0); 26301e2ffacSDavid Woodhouse header->vci = cpu_to_le16(0); 26401e2ffacSDavid Woodhouse header->type = cpu_to_le16(PKT_COMMAND); 26501e2ffacSDavid Woodhouse 26601e2ffacSDavid Woodhouse prm.pid = current->pid; 26701e2ffacSDavid Woodhouse prm.response = NULL; 26801e2ffacSDavid Woodhouse prm.port = SOLOS_CHAN(atmdev); 26901e2ffacSDavid Woodhouse 27001e2ffacSDavid Woodhouse spin_lock_irq(&card->param_queue_lock); 27101e2ffacSDavid Woodhouse list_add(&prm.list, &card->param_queue); 27201e2ffacSDavid Woodhouse spin_unlock_irq(&card->param_queue_lock); 27301e2ffacSDavid Woodhouse 27401e2ffacSDavid Woodhouse fpga_queue(card, prm.port, skb, NULL); 27501e2ffacSDavid Woodhouse 27601e2ffacSDavid Woodhouse wait_event_timeout(card->param_wq, prm.response, 5 * HZ); 27701e2ffacSDavid Woodhouse 27801e2ffacSDavid Woodhouse spin_lock_irq(&card->param_queue_lock); 27901e2ffacSDavid Woodhouse list_del(&prm.list); 28001e2ffacSDavid Woodhouse spin_unlock_irq(&card->param_queue_lock); 28101e2ffacSDavid Woodhouse 28201e2ffacSDavid Woodhouse skb = prm.response; 28301e2ffacSDavid Woodhouse 28401e2ffacSDavid Woodhouse if (!skb) 28501e2ffacSDavid Woodhouse return -EIO; 28601e2ffacSDavid Woodhouse 28701e2ffacSDavid Woodhouse buflen = skb->len; 28801e2ffacSDavid Woodhouse 28901e2ffacSDavid Woodhouse /* Sometimes it has a newline, sometimes it doesn't. */ 29001e2ffacSDavid Woodhouse if (skb->data[buflen - 1] == '\n') 29101e2ffacSDavid Woodhouse buflen--; 29201e2ffacSDavid Woodhouse 29301e2ffacSDavid Woodhouse if (buflen == 2 && !strncmp(skb->data, "OK", 2)) 29401e2ffacSDavid Woodhouse ret = count; 29501e2ffacSDavid Woodhouse else if (buflen == 5 && !strncmp(skb->data, "ERROR", 5)) 29601e2ffacSDavid Woodhouse ret = -EIO; 29701e2ffacSDavid Woodhouse else { 29801e2ffacSDavid Woodhouse /* We know we have enough space allocated for this; we allocated 29901e2ffacSDavid Woodhouse it ourselves */ 30001e2ffacSDavid Woodhouse skb->data[buflen] = 0; 30101e2ffacSDavid Woodhouse 30201e2ffacSDavid Woodhouse dev_warn(&card->dev->dev, "Unexpected parameter response: '%s'\n", 30301e2ffacSDavid Woodhouse skb->data); 30401e2ffacSDavid Woodhouse ret = -EIO; 30501e2ffacSDavid Woodhouse } 30601e2ffacSDavid Woodhouse kfree_skb(skb); 30701e2ffacSDavid Woodhouse 30801e2ffacSDavid Woodhouse return ret; 30901e2ffacSDavid Woodhouse } 31001e2ffacSDavid Woodhouse 31187ebb186SDavid Woodhouse static char *next_string(struct sk_buff *skb) 31287ebb186SDavid Woodhouse { 31387ebb186SDavid Woodhouse int i = 0; 31487ebb186SDavid Woodhouse char *this = skb->data; 31587ebb186SDavid Woodhouse 316c6428e52SDavid Woodhouse for (i = 0; i < skb->len; i++) { 31787ebb186SDavid Woodhouse if (this[i] == '\n') { 31887ebb186SDavid Woodhouse this[i] = 0; 319c6428e52SDavid Woodhouse skb_pull(skb, i + 1); 32087ebb186SDavid Woodhouse return this; 32187ebb186SDavid Woodhouse } 322c6428e52SDavid Woodhouse if (!isprint(this[i])) 323c6428e52SDavid Woodhouse return NULL; 32487ebb186SDavid Woodhouse } 32587ebb186SDavid Woodhouse return NULL; 32687ebb186SDavid Woodhouse } 32787ebb186SDavid Woodhouse 32887ebb186SDavid Woodhouse /* 32987ebb186SDavid Woodhouse * Status packet has fields separated by \n, starting with a version number 33087ebb186SDavid Woodhouse * for the information therein. Fields are.... 33187ebb186SDavid Woodhouse * 33287ebb186SDavid Woodhouse * packet version 33387ebb186SDavid Woodhouse * RxBitRate (version >= 1) 334f87b2ed2SDavid Woodhouse * TxBitRate (version >= 1) 33587ebb186SDavid Woodhouse * State (version >= 1) 336f87b2ed2SDavid Woodhouse * LocalSNRMargin (version >= 1) 337f87b2ed2SDavid Woodhouse * LocalLineAttn (version >= 1) 33887ebb186SDavid Woodhouse */ 33987ebb186SDavid Woodhouse static int process_status(struct solos_card *card, int port, struct sk_buff *skb) 34087ebb186SDavid Woodhouse { 341e94d91a6SLABBE Corentin char *str, *state_str, *snr, *attn; 342e94d91a6SLABBE Corentin int ver, rate_up, rate_down, err; 34387ebb186SDavid Woodhouse 34487ebb186SDavid Woodhouse if (!card->atmdev[port]) 34587ebb186SDavid Woodhouse return -ENODEV; 34687ebb186SDavid Woodhouse 34787ebb186SDavid Woodhouse str = next_string(skb); 34887ebb186SDavid Woodhouse if (!str) 34987ebb186SDavid Woodhouse return -EIO; 35087ebb186SDavid Woodhouse 351e94d91a6SLABBE Corentin err = kstrtoint(str, 10, &ver); 352e94d91a6SLABBE Corentin if (err) { 353e94d91a6SLABBE Corentin dev_warn(&card->dev->dev, "Unexpected status interrupt version\n"); 354e94d91a6SLABBE Corentin return err; 355e94d91a6SLABBE Corentin } 35687ebb186SDavid Woodhouse if (ver < 1) { 35787ebb186SDavid Woodhouse dev_warn(&card->dev->dev, "Unexpected status interrupt version %d\n", 35887ebb186SDavid Woodhouse ver); 35987ebb186SDavid Woodhouse return -EIO; 36087ebb186SDavid Woodhouse } 36187ebb186SDavid Woodhouse 36287ebb186SDavid Woodhouse str = next_string(skb); 363c6428e52SDavid Woodhouse if (!str) 364c6428e52SDavid Woodhouse return -EIO; 36595852f48SDavid Woodhouse if (!strcmp(str, "ERROR")) { 36695852f48SDavid Woodhouse dev_dbg(&card->dev->dev, "Status packet indicated Solos error on port %d (starting up?)\n", 36795852f48SDavid Woodhouse port); 36895852f48SDavid Woodhouse return 0; 36995852f48SDavid Woodhouse } 37095852f48SDavid Woodhouse 371e94d91a6SLABBE Corentin err = kstrtoint(str, 10, &rate_down); 372e94d91a6SLABBE Corentin if (err) 373e94d91a6SLABBE Corentin return err; 37487ebb186SDavid Woodhouse 37587ebb186SDavid Woodhouse str = next_string(skb); 376c6428e52SDavid Woodhouse if (!str) 377c6428e52SDavid Woodhouse return -EIO; 378e94d91a6SLABBE Corentin err = kstrtoint(str, 10, &rate_up); 379e94d91a6SLABBE Corentin if (err) 380e94d91a6SLABBE Corentin return err; 38187ebb186SDavid Woodhouse 382af780656SDavid Woodhouse state_str = next_string(skb); 383c6428e52SDavid Woodhouse if (!state_str) 384c6428e52SDavid Woodhouse return -EIO; 38587ebb186SDavid Woodhouse 386f87b2ed2SDavid Woodhouse /* Anything but 'Showtime' is down */ 387f87b2ed2SDavid Woodhouse if (strcmp(state_str, "Showtime")) { 38849d49106SKarl Hiramoto atm_dev_signal_change(card->atmdev[port], ATM_PHY_SIG_LOST); 38995852f48SDavid Woodhouse dev_info(&card->dev->dev, "Port %d: %s\n", port, state_str); 390f87b2ed2SDavid Woodhouse return 0; 391f87b2ed2SDavid Woodhouse } 392af780656SDavid Woodhouse 393c6428e52SDavid Woodhouse snr = next_string(skb); 3946cf5767cSJulia Lawall if (!snr) 395c6428e52SDavid Woodhouse return -EIO; 396c6428e52SDavid Woodhouse attn = next_string(skb); 397c6428e52SDavid Woodhouse if (!attn) 398c6428e52SDavid Woodhouse return -EIO; 399c6428e52SDavid Woodhouse 40095852f48SDavid Woodhouse dev_info(&card->dev->dev, "Port %d: %s @%d/%d kb/s%s%s%s%s\n", 401c6428e52SDavid Woodhouse port, state_str, rate_down/1000, rate_up/1000, 402c6428e52SDavid Woodhouse snr[0]?", SNR ":"", snr, attn[0]?", Attn ":"", attn); 403f87b2ed2SDavid Woodhouse 404c6428e52SDavid Woodhouse card->atmdev[port]->link_rate = rate_down / 424; 40549d49106SKarl Hiramoto atm_dev_signal_change(card->atmdev[port], ATM_PHY_SIG_FOUND); 40687ebb186SDavid Woodhouse 40787ebb186SDavid Woodhouse return 0; 40887ebb186SDavid Woodhouse } 40987ebb186SDavid Woodhouse 41001e2ffacSDavid Woodhouse static int process_command(struct solos_card *card, int port, struct sk_buff *skb) 41101e2ffacSDavid Woodhouse { 41201e2ffacSDavid Woodhouse struct solos_param *prm; 41301e2ffacSDavid Woodhouse unsigned long flags; 41401e2ffacSDavid Woodhouse int cmdpid; 415e94d91a6SLABBE Corentin int found = 0, err; 41601e2ffacSDavid Woodhouse 41701e2ffacSDavid Woodhouse if (skb->len < 7) 41801e2ffacSDavid Woodhouse return 0; 41901e2ffacSDavid Woodhouse 42001e2ffacSDavid Woodhouse if (skb->data[0] != 'L' || !isdigit(skb->data[1]) || 42101e2ffacSDavid Woodhouse !isdigit(skb->data[2]) || !isdigit(skb->data[3]) || 42201e2ffacSDavid Woodhouse !isdigit(skb->data[4]) || !isdigit(skb->data[5]) || 42301e2ffacSDavid Woodhouse skb->data[6] != '\n') 42401e2ffacSDavid Woodhouse return 0; 42501e2ffacSDavid Woodhouse 426e94d91a6SLABBE Corentin err = kstrtoint(&skb->data[1], 10, &cmdpid); 427e94d91a6SLABBE Corentin if (err) 428e94d91a6SLABBE Corentin return err; 42901e2ffacSDavid Woodhouse 43001e2ffacSDavid Woodhouse spin_lock_irqsave(&card->param_queue_lock, flags); 43101e2ffacSDavid Woodhouse list_for_each_entry(prm, &card->param_queue, list) { 43201e2ffacSDavid Woodhouse if (prm->port == port && prm->pid == cmdpid) { 43301e2ffacSDavid Woodhouse prm->response = skb; 43401e2ffacSDavid Woodhouse skb_pull(skb, 7); 43501e2ffacSDavid Woodhouse wake_up(&card->param_wq); 43601e2ffacSDavid Woodhouse found = 1; 43701e2ffacSDavid Woodhouse break; 43801e2ffacSDavid Woodhouse } 43901e2ffacSDavid Woodhouse } 44001e2ffacSDavid Woodhouse spin_unlock_irqrestore(&card->param_queue_lock, flags); 44101e2ffacSDavid Woodhouse return found; 44201e2ffacSDavid Woodhouse } 44301e2ffacSDavid Woodhouse 4449c54004eSDavid Woodhouse static ssize_t console_show(struct device *dev, struct device_attribute *attr, 4459c54004eSDavid Woodhouse char *buf) 4469c54004eSDavid Woodhouse { 4479c54004eSDavid Woodhouse struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev); 4489c54004eSDavid Woodhouse struct solos_card *card = atmdev->dev_data; 4499c54004eSDavid Woodhouse struct sk_buff *skb; 450f1ee89d5SJiri Slaby unsigned int len; 4519c54004eSDavid Woodhouse 4529c54004eSDavid Woodhouse spin_lock(&card->cli_queue_lock); 4539c54004eSDavid Woodhouse skb = skb_dequeue(&card->cli_queue[SOLOS_CHAN(atmdev)]); 4549c54004eSDavid Woodhouse spin_unlock(&card->cli_queue_lock); 4559c54004eSDavid Woodhouse if(skb == NULL) 4569c54004eSDavid Woodhouse return sprintf(buf, "No data.\n"); 4579c54004eSDavid Woodhouse 458f1ee89d5SJiri Slaby len = skb->len; 459f1ee89d5SJiri Slaby memcpy(buf, skb->data, len); 4609c54004eSDavid Woodhouse 4619c54004eSDavid Woodhouse kfree_skb(skb); 462f1ee89d5SJiri Slaby return len; 4639c54004eSDavid Woodhouse } 4649c54004eSDavid Woodhouse 4659c54004eSDavid Woodhouse static int send_command(struct solos_card *card, int dev, const char *buf, size_t size) 4669c54004eSDavid Woodhouse { 4679c54004eSDavid Woodhouse struct sk_buff *skb; 4689c54004eSDavid Woodhouse struct pkt_hdr *header; 4699c54004eSDavid Woodhouse 4709c54004eSDavid Woodhouse if (size > (BUF_SIZE - sizeof(*header))) { 4719c54004eSDavid Woodhouse dev_dbg(&card->dev->dev, "Command is too big. Dropping request\n"); 4729c54004eSDavid Woodhouse return 0; 4739c54004eSDavid Woodhouse } 4749c54004eSDavid Woodhouse skb = alloc_skb(size + sizeof(*header), GFP_ATOMIC); 4759c54004eSDavid Woodhouse if (!skb) { 4769c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in send_command()\n"); 4779c54004eSDavid Woodhouse return 0; 4789c54004eSDavid Woodhouse } 4799c54004eSDavid Woodhouse 4804df864c1SJohannes Berg header = skb_put(skb, sizeof(*header)); 4819c54004eSDavid Woodhouse 4829c54004eSDavid Woodhouse header->size = cpu_to_le16(size); 4839c54004eSDavid Woodhouse header->vpi = cpu_to_le16(0); 4849c54004eSDavid Woodhouse header->vci = cpu_to_le16(0); 4859c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_COMMAND); 4869c54004eSDavid Woodhouse 48759ae1d12SJohannes Berg skb_put_data(skb, buf, size); 4889c54004eSDavid Woodhouse 4899c54004eSDavid Woodhouse fpga_queue(card, dev, skb, NULL); 4909c54004eSDavid Woodhouse 4919c54004eSDavid Woodhouse return 0; 4929c54004eSDavid Woodhouse } 4939c54004eSDavid Woodhouse 4949c54004eSDavid Woodhouse static ssize_t console_store(struct device *dev, struct device_attribute *attr, 4959c54004eSDavid Woodhouse const char *buf, size_t count) 4969c54004eSDavid Woodhouse { 4979c54004eSDavid Woodhouse struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev); 4989c54004eSDavid Woodhouse struct solos_card *card = atmdev->dev_data; 4999c54004eSDavid Woodhouse int err; 5009c54004eSDavid Woodhouse 5019c54004eSDavid Woodhouse err = send_command(card, SOLOS_CHAN(atmdev), buf, count); 5029c54004eSDavid Woodhouse 5039c54004eSDavid Woodhouse return err?:count; 5049c54004eSDavid Woodhouse } 5059c54004eSDavid Woodhouse 506f9baad02SNathan Williams struct geos_gpio_attr { 507f9baad02SNathan Williams struct device_attribute attr; 508f9baad02SNathan Williams int offset; 509f9baad02SNathan Williams }; 510f9baad02SNathan Williams 511f9baad02SNathan Williams #define SOLOS_GPIO_ATTR(_name, _mode, _show, _store, _offset) \ 512f9baad02SNathan Williams struct geos_gpio_attr gpio_attr_##_name = { \ 513f9baad02SNathan Williams .attr = __ATTR(_name, _mode, _show, _store), \ 514f9baad02SNathan Williams .offset = _offset } 515f9baad02SNathan Williams 516f9baad02SNathan Williams static ssize_t geos_gpio_store(struct device *dev, struct device_attribute *attr, 517f9baad02SNathan Williams const char *buf, size_t count) 518f9baad02SNathan Williams { 519f9baad02SNathan Williams struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr); 520c349c0a2SChuhong Yuan struct solos_card *card = dev_get_drvdata(dev); 521f9baad02SNathan Williams uint32_t data32; 522f9baad02SNathan Williams 523f9baad02SNathan Williams if (count != 1 && (count != 2 || buf[1] != '\n')) 524f9baad02SNathan Williams return -EINVAL; 525f9baad02SNathan Williams 526f9baad02SNathan Williams spin_lock_irq(&card->param_queue_lock); 527f9baad02SNathan Williams data32 = ioread32(card->config_regs + GPIO_STATUS); 528f9baad02SNathan Williams if (buf[0] == '1') { 529f9baad02SNathan Williams data32 |= 1 << gattr->offset; 530f9baad02SNathan Williams iowrite32(data32, card->config_regs + GPIO_STATUS); 531f9baad02SNathan Williams } else if (buf[0] == '0') { 532f9baad02SNathan Williams data32 &= ~(1 << gattr->offset); 533f9baad02SNathan Williams iowrite32(data32, card->config_regs + GPIO_STATUS); 534f9baad02SNathan Williams } else { 535f9baad02SNathan Williams count = -EINVAL; 536f9baad02SNathan Williams } 53729042073SDan Carpenter spin_unlock_irq(&card->param_queue_lock); 538f9baad02SNathan Williams return count; 539f9baad02SNathan Williams } 540f9baad02SNathan Williams 541f9baad02SNathan Williams static ssize_t geos_gpio_show(struct device *dev, struct device_attribute *attr, 542f9baad02SNathan Williams char *buf) 543f9baad02SNathan Williams { 544f9baad02SNathan Williams struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr); 545c349c0a2SChuhong Yuan struct solos_card *card = dev_get_drvdata(dev); 546f9baad02SNathan Williams uint32_t data32; 547f9baad02SNathan Williams 548f9baad02SNathan Williams data32 = ioread32(card->config_regs + GPIO_STATUS); 549f9baad02SNathan Williams data32 = (data32 >> gattr->offset) & 1; 550f9baad02SNathan Williams 551f9baad02SNathan Williams return sprintf(buf, "%d\n", data32); 552f9baad02SNathan Williams } 553f9baad02SNathan Williams 554f9baad02SNathan Williams static ssize_t hardware_show(struct device *dev, struct device_attribute *attr, 555f9baad02SNathan Williams char *buf) 556f9baad02SNathan Williams { 557f9baad02SNathan Williams struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr); 558c349c0a2SChuhong Yuan struct solos_card *card = dev_get_drvdata(dev); 559f9baad02SNathan Williams uint32_t data32; 560f9baad02SNathan Williams 561f9baad02SNathan Williams data32 = ioread32(card->config_regs + GPIO_STATUS); 562f9baad02SNathan Williams switch (gattr->offset) { 563f9baad02SNathan Williams case 0: 564f9baad02SNathan Williams /* HardwareVersion */ 565f9baad02SNathan Williams data32 = data32 & 0x1F; 566f9baad02SNathan Williams break; 567f9baad02SNathan Williams case 1: 568f9baad02SNathan Williams /* HardwareVariant */ 569f9baad02SNathan Williams data32 = (data32 >> 5) & 0x0F; 570f9baad02SNathan Williams break; 571f9baad02SNathan Williams } 572f9baad02SNathan Williams return sprintf(buf, "%d\n", data32); 573f9baad02SNathan Williams } 574f9baad02SNathan Williams 57589d9123eSJulia Lawall static DEVICE_ATTR_RW(console); 5769c54004eSDavid Woodhouse 577d057f0a4SDavid Woodhouse 578d057f0a4SDavid Woodhouse #define SOLOS_ATTR_RO(x) static DEVICE_ATTR(x, 0444, solos_param_show, NULL); 579d057f0a4SDavid Woodhouse #define SOLOS_ATTR_RW(x) static DEVICE_ATTR(x, 0644, solos_param_show, solos_param_store); 580d057f0a4SDavid Woodhouse 581d057f0a4SDavid Woodhouse #include "solos-attrlist.c" 582d057f0a4SDavid Woodhouse 583f9baad02SNathan Williams static SOLOS_GPIO_ATTR(GPIO1, 0644, geos_gpio_show, geos_gpio_store, 9); 584f9baad02SNathan Williams static SOLOS_GPIO_ATTR(GPIO2, 0644, geos_gpio_show, geos_gpio_store, 10); 585f9baad02SNathan Williams static SOLOS_GPIO_ATTR(GPIO3, 0644, geos_gpio_show, geos_gpio_store, 11); 586f9baad02SNathan Williams static SOLOS_GPIO_ATTR(GPIO4, 0644, geos_gpio_show, geos_gpio_store, 12); 587f9baad02SNathan Williams static SOLOS_GPIO_ATTR(GPIO5, 0644, geos_gpio_show, geos_gpio_store, 13); 588f9baad02SNathan Williams static SOLOS_GPIO_ATTR(PushButton, 0444, geos_gpio_show, NULL, 14); 589f9baad02SNathan Williams static SOLOS_GPIO_ATTR(HardwareVersion, 0444, hardware_show, NULL, 0); 590f9baad02SNathan Williams static SOLOS_GPIO_ATTR(HardwareVariant, 0444, hardware_show, NULL, 1); 591d057f0a4SDavid Woodhouse #undef SOLOS_ATTR_RO 592d057f0a4SDavid Woodhouse #undef SOLOS_ATTR_RW 593d057f0a4SDavid Woodhouse 594d057f0a4SDavid Woodhouse #define SOLOS_ATTR_RO(x) &dev_attr_##x.attr, 595d057f0a4SDavid Woodhouse #define SOLOS_ATTR_RW(x) &dev_attr_##x.attr, 596d057f0a4SDavid Woodhouse 597d057f0a4SDavid Woodhouse static struct attribute *solos_attrs[] = { 598d057f0a4SDavid Woodhouse #include "solos-attrlist.c" 599d057f0a4SDavid Woodhouse NULL 600d057f0a4SDavid Woodhouse }; 601d057f0a4SDavid Woodhouse 602638ce0fcSAmitoj Kaur Chawla static const struct attribute_group solos_attr_group = { 603d057f0a4SDavid Woodhouse .attrs = solos_attrs, 604d057f0a4SDavid Woodhouse .name = "parameters", 605d057f0a4SDavid Woodhouse }; 6069c54004eSDavid Woodhouse 607f9baad02SNathan Williams static struct attribute *gpio_attrs[] = { 608f9baad02SNathan Williams &gpio_attr_GPIO1.attr.attr, 609f9baad02SNathan Williams &gpio_attr_GPIO2.attr.attr, 610f9baad02SNathan Williams &gpio_attr_GPIO3.attr.attr, 611f9baad02SNathan Williams &gpio_attr_GPIO4.attr.attr, 612f9baad02SNathan Williams &gpio_attr_GPIO5.attr.attr, 613f9baad02SNathan Williams &gpio_attr_PushButton.attr.attr, 614f9baad02SNathan Williams &gpio_attr_HardwareVersion.attr.attr, 615f9baad02SNathan Williams &gpio_attr_HardwareVariant.attr.attr, 616f9baad02SNathan Williams NULL 617f9baad02SNathan Williams }; 618f9baad02SNathan Williams 619638ce0fcSAmitoj Kaur Chawla static const struct attribute_group gpio_attr_group = { 620f9baad02SNathan Williams .attrs = gpio_attrs, 621f9baad02SNathan Williams .name = "gpio", 622f9baad02SNathan Williams }; 623f9baad02SNathan Williams 624fa755b9fSDavid Woodhouse static int flash_upgrade(struct solos_card *card, int chip) 625fa755b9fSDavid Woodhouse { 626fa755b9fSDavid Woodhouse const struct firmware *fw; 627fa755b9fSDavid Woodhouse const char *fw_name; 6287c4015bdSSimon Farnsworth int blocksize = 0; 6297c4015bdSSimon Farnsworth int numblocks = 0; 630fa755b9fSDavid Woodhouse int offset; 631fa755b9fSDavid Woodhouse 6327adcdb4cSAndrew Morton switch (chip) { 6337adcdb4cSAndrew Morton case 0: 634fa755b9fSDavid Woodhouse fw_name = "solos-FPGA.bin"; 63513af8164SNathan Williams if (card->atmel_flash) 63613af8164SNathan Williams blocksize = ATMEL_FPGA_BLOCK; 63713af8164SNathan Williams else 63813af8164SNathan Williams blocksize = SPI_FLASH_BLOCK; 6397adcdb4cSAndrew Morton break; 6407adcdb4cSAndrew Morton case 1: 641fa755b9fSDavid Woodhouse fw_name = "solos-Firmware.bin"; 64213af8164SNathan Williams if (card->atmel_flash) 64313af8164SNathan Williams blocksize = ATMEL_SOLOS_BLOCK; 64413af8164SNathan Williams else 64513af8164SNathan Williams blocksize = SPI_FLASH_BLOCK; 6467adcdb4cSAndrew Morton break; 6477adcdb4cSAndrew Morton case 2: 6484dbedf43SNathan Williams if (card->fpga_version > LEGACY_BUFFERS){ 6494dbedf43SNathan Williams fw_name = "solos-db-FPGA.bin"; 65013af8164SNathan Williams if (card->atmel_flash) 65113af8164SNathan Williams blocksize = ATMEL_FPGA_BLOCK; 65213af8164SNathan Williams else 65313af8164SNathan Williams blocksize = SPI_FLASH_BLOCK; 6544dbedf43SNathan Williams } else { 6557adcdb4cSAndrew Morton dev_info(&card->dev->dev, "FPGA version doesn't support" 6567adcdb4cSAndrew Morton " daughter board upgrades\n"); 6574dbedf43SNathan Williams return -EPERM; 6584dbedf43SNathan Williams } 6597adcdb4cSAndrew Morton break; 6607adcdb4cSAndrew Morton case 3: 6614dbedf43SNathan Williams if (card->fpga_version > LEGACY_BUFFERS){ 6624dbedf43SNathan Williams fw_name = "solos-Firmware.bin"; 66313af8164SNathan Williams if (card->atmel_flash) 66413af8164SNathan Williams blocksize = ATMEL_SOLOS_BLOCK; 66513af8164SNathan Williams else 66613af8164SNathan Williams blocksize = SPI_FLASH_BLOCK; 6674dbedf43SNathan Williams } else { 6687adcdb4cSAndrew Morton dev_info(&card->dev->dev, "FPGA version doesn't support" 6697adcdb4cSAndrew Morton " daughter board upgrades\n"); 6704dbedf43SNathan Williams return -EPERM; 6714dbedf43SNathan Williams } 6727adcdb4cSAndrew Morton break; 6737adcdb4cSAndrew Morton default: 6747adcdb4cSAndrew Morton return -ENODEV; 6754dbedf43SNathan Williams } 6764dbedf43SNathan Williams 677fa755b9fSDavid Woodhouse if (request_firmware(&fw, fw_name, &card->dev->dev)) 678fa755b9fSDavid Woodhouse return -ENOENT; 679fa755b9fSDavid Woodhouse 680fa755b9fSDavid Woodhouse dev_info(&card->dev->dev, "Flash upgrade starting\n"); 681fa755b9fSDavid Woodhouse 68213af8164SNathan Williams /* New FPGAs require driver version before permitting flash upgrades */ 68313af8164SNathan Williams iowrite32(DRIVER_VERSION, card->config_regs + DRIVER_VER); 68413af8164SNathan Williams 685fa755b9fSDavid Woodhouse numblocks = fw->size / blocksize; 686fa755b9fSDavid Woodhouse dev_info(&card->dev->dev, "Firmware size: %zd\n", fw->size); 687fa755b9fSDavid Woodhouse dev_info(&card->dev->dev, "Number of blocks: %d\n", numblocks); 6887c4015bdSSimon Farnsworth 6897c4015bdSSimon Farnsworth dev_info(&card->dev->dev, "Changing FPGA to Update mode\n"); 6907c4015bdSSimon Farnsworth iowrite32(1, card->config_regs + FPGA_MODE); 69106091ed6SDavid S. Miller (void) ioread32(card->config_regs + FPGA_MODE); 6927c4015bdSSimon Farnsworth 693fa755b9fSDavid Woodhouse /* Set mode to Chip Erase */ 6944dbedf43SNathan Williams if(chip == 0 || chip == 2) 6954dbedf43SNathan Williams dev_info(&card->dev->dev, "Set FPGA Flash mode to FPGA Chip Erase\n"); 6964dbedf43SNathan Williams if(chip == 1 || chip == 3) 6974dbedf43SNathan Williams dev_info(&card->dev->dev, "Set FPGA Flash mode to Solos Chip Erase\n"); 698fa755b9fSDavid Woodhouse iowrite32((chip * 2), card->config_regs + FLASH_MODE); 699fa755b9fSDavid Woodhouse 700fa755b9fSDavid Woodhouse 701fa755b9fSDavid Woodhouse iowrite32(1, card->config_regs + WRITE_FLASH); 702fa755b9fSDavid Woodhouse wait_event(card->fw_wq, !ioread32(card->config_regs + FLASH_BUSY)); 703fa755b9fSDavid Woodhouse 704fa755b9fSDavid Woodhouse for (offset = 0; offset < fw->size; offset += blocksize) { 7057c4015bdSSimon Farnsworth int i; 7067c4015bdSSimon Farnsworth 7077c4015bdSSimon Farnsworth /* Clear write flag */ 7087c4015bdSSimon Farnsworth iowrite32(0, card->config_regs + WRITE_FLASH); 709fa755b9fSDavid Woodhouse 7107c4015bdSSimon Farnsworth /* Set mode to Block Write */ 7117c4015bdSSimon Farnsworth /* dev_info(&card->dev->dev, "Set FPGA Flash mode to Block Write\n"); */ 712fa755b9fSDavid Woodhouse iowrite32(((chip * 2) + 1), card->config_regs + FLASH_MODE); 7137c4015bdSSimon Farnsworth 71413af8164SNathan Williams /* Copy block to buffer, swapping each 16 bits for Atmel flash */ 715fa755b9fSDavid Woodhouse for(i = 0; i < blocksize; i += 4) { 71613af8164SNathan Williams uint32_t word; 71713af8164SNathan Williams if (card->atmel_flash) 71813af8164SNathan Williams word = swahb32p((uint32_t *)(fw->data + offset + i)); 71913af8164SNathan Williams else 72013af8164SNathan Williams word = *(uint32_t *)(fw->data + offset + i); 7214dbedf43SNathan Williams if(card->fpga_version > LEGACY_BUFFERS) 7224dbedf43SNathan Williams iowrite32(word, FLASH_BUF + i); 7234dbedf43SNathan Williams else 724fa755b9fSDavid Woodhouse iowrite32(word, RX_BUF(card, 3) + i); 725fa755b9fSDavid Woodhouse } 726fa755b9fSDavid Woodhouse 727fa755b9fSDavid Woodhouse /* Specify block number and then trigger flash write */ 728fa755b9fSDavid Woodhouse iowrite32(offset / blocksize, card->config_regs + FLASH_BLOCK); 729fa755b9fSDavid Woodhouse iowrite32(1, card->config_regs + WRITE_FLASH); 730fa755b9fSDavid Woodhouse wait_event(card->fw_wq, !ioread32(card->config_regs + FLASH_BUSY)); 731fa755b9fSDavid Woodhouse } 732fa755b9fSDavid Woodhouse 733fa755b9fSDavid Woodhouse release_firmware(fw); 7347c4015bdSSimon Farnsworth iowrite32(0, card->config_regs + WRITE_FLASH); 7357c4015bdSSimon Farnsworth iowrite32(0, card->config_regs + FPGA_MODE); 7367c4015bdSSimon Farnsworth iowrite32(0, card->config_regs + FLASH_MODE); 7377c4015bdSSimon Farnsworth dev_info(&card->dev->dev, "Returning FPGA to Data mode\n"); 738fa755b9fSDavid Woodhouse return 0; 7397c4015bdSSimon Farnsworth } 7407c4015bdSSimon Farnsworth 7419c54004eSDavid Woodhouse static irqreturn_t solos_irq(int irq, void *dev_id) 7429c54004eSDavid Woodhouse { 7439c54004eSDavid Woodhouse struct solos_card *card = dev_id; 7449c54004eSDavid Woodhouse int handled = 1; 7459c54004eSDavid Woodhouse 7469c54004eSDavid Woodhouse iowrite32(0, card->config_regs + IRQ_CLEAR); 7479c54004eSDavid Woodhouse 74835c2221bSDavid Woodhouse /* If we're up and running, just kick the tasklet to process TX/RX */ 749fa755b9fSDavid Woodhouse if (card->atmdev[0]) 7509c54004eSDavid Woodhouse tasklet_schedule(&card->tlet); 751fa755b9fSDavid Woodhouse else 752fa755b9fSDavid Woodhouse wake_up(&card->fw_wq); 7539c54004eSDavid Woodhouse 7549c54004eSDavid Woodhouse return IRQ_RETVAL(handled); 7559c54004eSDavid Woodhouse } 7569c54004eSDavid Woodhouse 7575f4d4e3fSDaeseok Youn static void solos_bh(unsigned long card_arg) 7589c54004eSDavid Woodhouse { 7599c54004eSDavid Woodhouse struct solos_card *card = (void *)card_arg; 7609c54004eSDavid Woodhouse uint32_t card_flags; 7619c54004eSDavid Woodhouse uint32_t rx_done = 0; 76235c2221bSDavid Woodhouse int port; 7639c54004eSDavid Woodhouse 76435c2221bSDavid Woodhouse /* 76535c2221bSDavid Woodhouse * Since fpga_tx() is going to need to read the flags under its lock, 76635c2221bSDavid Woodhouse * it can return them to us so that we don't have to hit PCI MMIO 76735c2221bSDavid Woodhouse * again for the same information 76835c2221bSDavid Woodhouse */ 76935c2221bSDavid Woodhouse card_flags = fpga_tx(card); 7709c54004eSDavid Woodhouse 7719c54004eSDavid Woodhouse for (port = 0; port < card->nr_ports; port++) { 7729c54004eSDavid Woodhouse if (card_flags & (0x10 << port)) { 77390937231SDavid Woodhouse struct pkt_hdr _hdr, *header; 7749c54004eSDavid Woodhouse struct sk_buff *skb; 7759c54004eSDavid Woodhouse struct atm_vcc *vcc; 7769c54004eSDavid Woodhouse int size; 7779c54004eSDavid Woodhouse 77890937231SDavid Woodhouse if (card->using_dma) { 77990937231SDavid Woodhouse skb = card->rx_skb[port]; 780eaf83e39SDavid Woodhouse card->rx_skb[port] = NULL; 78190937231SDavid Woodhouse 782ede58ef2Schas williams - CONTRACTOR dma_unmap_single(&card->dev->dev, SKB_CB(skb)->dma_addr, 783ede58ef2Schas williams - CONTRACTOR RX_DMA_SIZE, DMA_FROM_DEVICE); 784eaf83e39SDavid Woodhouse 78590937231SDavid Woodhouse header = (void *)skb->data; 78690937231SDavid Woodhouse size = le16_to_cpu(header->size); 78790937231SDavid Woodhouse skb_put(skb, size + sizeof(*header)); 78890937231SDavid Woodhouse skb_pull(skb, sizeof(*header)); 78990937231SDavid Woodhouse } else { 79090937231SDavid Woodhouse header = &_hdr; 79190937231SDavid Woodhouse 7929c54004eSDavid Woodhouse rx_done |= 0x10 << port; 7939c54004eSDavid Woodhouse 79490937231SDavid Woodhouse memcpy_fromio(header, RX_BUF(card, port), sizeof(*header)); 7959c54004eSDavid Woodhouse 79690937231SDavid Woodhouse size = le16_to_cpu(header->size); 79778f857f2SNathan Williams if (size > (card->buffer_size - sizeof(*header))){ 79878f857f2SNathan Williams dev_warn(&card->dev->dev, "Invalid buffer size\n"); 79978f857f2SNathan Williams continue; 80078f857f2SNathan Williams } 8019c54004eSDavid Woodhouse 802ce816eb0SDavid Woodhouse /* Use netdev_alloc_skb() because it adds NET_SKB_PAD of 803ce816eb0SDavid Woodhouse * headroom, and ensures we can route packets back out an 804ce816eb0SDavid Woodhouse * Ethernet interface (for example) without having to 805ce816eb0SDavid Woodhouse * reallocate. Adding NET_IP_ALIGN also ensures that both 806ce816eb0SDavid Woodhouse * PPPoATM and PPPoEoBR2684 packets end up aligned. */ 807ce816eb0SDavid Woodhouse skb = netdev_alloc_skb_ip_align(NULL, size + 1); 8089c54004eSDavid Woodhouse if (!skb) { 8099c54004eSDavid Woodhouse if (net_ratelimit()) 8109c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff for RX\n"); 8119c54004eSDavid Woodhouse continue; 8129c54004eSDavid Woodhouse } 8139c54004eSDavid Woodhouse 8149c54004eSDavid Woodhouse memcpy_fromio(skb_put(skb, size), 81590937231SDavid Woodhouse RX_BUF(card, port) + sizeof(*header), 8169c54004eSDavid Woodhouse size); 81790937231SDavid Woodhouse } 8189c54004eSDavid Woodhouse if (atmdebug) { 81918b429e7SPhilip A. Prindeville dev_info(&card->dev->dev, "Received: port %d\n", port); 8209c54004eSDavid Woodhouse dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n", 82190937231SDavid Woodhouse size, le16_to_cpu(header->vpi), 82290937231SDavid Woodhouse le16_to_cpu(header->vci)); 8239c54004eSDavid Woodhouse print_buffer(skb); 8249c54004eSDavid Woodhouse } 8259c54004eSDavid Woodhouse 82690937231SDavid Woodhouse switch (le16_to_cpu(header->type)) { 8279c54004eSDavid Woodhouse case PKT_DATA: 82890937231SDavid Woodhouse vcc = find_vcc(card->atmdev[port], le16_to_cpu(header->vpi), 82990937231SDavid Woodhouse le16_to_cpu(header->vci)); 8309c54004eSDavid Woodhouse if (!vcc) { 8319c54004eSDavid Woodhouse if (net_ratelimit()) 8321e19e658SPhilip A. Prindeville dev_warn(&card->dev->dev, "Received packet for unknown VPI.VCI %d.%d on port %d\n", 8331e19e658SPhilip A. Prindeville le16_to_cpu(header->vpi), le16_to_cpu(header->vci), 8349c54004eSDavid Woodhouse port); 835007ef52bSNathan Williams dev_kfree_skb_any(skb); 836007ef52bSNathan Williams break; 8379c54004eSDavid Woodhouse } 8389c54004eSDavid Woodhouse atm_charge(vcc, skb->truesize); 8399c54004eSDavid Woodhouse vcc->push(vcc, skb); 8409c54004eSDavid Woodhouse atomic_inc(&vcc->stats->rx); 8419c54004eSDavid Woodhouse break; 8429c54004eSDavid Woodhouse 84387ebb186SDavid Woodhouse case PKT_STATUS: 84495852f48SDavid Woodhouse if (process_status(card, port, skb) && 84595852f48SDavid Woodhouse net_ratelimit()) { 84695852f48SDavid Woodhouse dev_warn(&card->dev->dev, "Bad status packet of %d bytes on port %d:\n", skb->len, port); 84795852f48SDavid Woodhouse print_buffer(skb); 84895852f48SDavid Woodhouse } 849eaf83e39SDavid Woodhouse dev_kfree_skb_any(skb); 85087ebb186SDavid Woodhouse break; 85187ebb186SDavid Woodhouse 8529c54004eSDavid Woodhouse case PKT_COMMAND: 8539c54004eSDavid Woodhouse default: /* FIXME: Not really, surely? */ 85401e2ffacSDavid Woodhouse if (process_command(card, port, skb)) 85501e2ffacSDavid Woodhouse break; 8569c54004eSDavid Woodhouse spin_lock(&card->cli_queue_lock); 8579c54004eSDavid Woodhouse if (skb_queue_len(&card->cli_queue[port]) > 10) { 8589c54004eSDavid Woodhouse if (net_ratelimit()) 8599c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Dropping console response on port %d\n", 8609c54004eSDavid Woodhouse port); 861eaf83e39SDavid Woodhouse dev_kfree_skb_any(skb); 8629c54004eSDavid Woodhouse } else 8639c54004eSDavid Woodhouse skb_queue_tail(&card->cli_queue[port], skb); 8649c54004eSDavid Woodhouse spin_unlock(&card->cli_queue_lock); 8659c54004eSDavid Woodhouse break; 8669c54004eSDavid Woodhouse } 8679c54004eSDavid Woodhouse } 868eaf83e39SDavid Woodhouse /* Allocate RX skbs for any ports which need them */ 869eaf83e39SDavid Woodhouse if (card->using_dma && card->atmdev[port] && 870eaf83e39SDavid Woodhouse !card->rx_skb[port]) { 871ce816eb0SDavid Woodhouse /* Unlike the MMIO case (qv) we can't add NET_IP_ALIGN 872ce816eb0SDavid Woodhouse * here; the FPGA can only DMA to addresses which are 873ce816eb0SDavid Woodhouse * aligned to 4 bytes. */ 874ce816eb0SDavid Woodhouse struct sk_buff *skb = dev_alloc_skb(RX_DMA_SIZE); 875eaf83e39SDavid Woodhouse if (skb) { 876eaf83e39SDavid Woodhouse SKB_CB(skb)->dma_addr = 877ede58ef2Schas williams - CONTRACTOR dma_map_single(&card->dev->dev, skb->data, 878ede58ef2Schas williams - CONTRACTOR RX_DMA_SIZE, DMA_FROM_DEVICE); 879eaf83e39SDavid Woodhouse iowrite32(SKB_CB(skb)->dma_addr, 880eaf83e39SDavid Woodhouse card->config_regs + RX_DMA_ADDR(port)); 881eaf83e39SDavid Woodhouse card->rx_skb[port] = skb; 882eaf83e39SDavid Woodhouse } else { 883eaf83e39SDavid Woodhouse if (net_ratelimit()) 884eaf83e39SDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate RX skb"); 885eaf83e39SDavid Woodhouse 886eaf83e39SDavid Woodhouse /* We'll have to try again later */ 887eaf83e39SDavid Woodhouse tasklet_schedule(&card->tlet); 888eaf83e39SDavid Woodhouse } 889eaf83e39SDavid Woodhouse } 8909c54004eSDavid Woodhouse } 8919c54004eSDavid Woodhouse if (rx_done) 8929c54004eSDavid Woodhouse iowrite32(rx_done, card->config_regs + FLAGS_ADDR); 8939c54004eSDavid Woodhouse 8949c54004eSDavid Woodhouse return; 8959c54004eSDavid Woodhouse } 8969c54004eSDavid Woodhouse 8979c54004eSDavid Woodhouse static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci) 8989c54004eSDavid Woodhouse { 8999c54004eSDavid Woodhouse struct hlist_head *head; 9009c54004eSDavid Woodhouse struct atm_vcc *vcc = NULL; 9019c54004eSDavid Woodhouse struct sock *s; 9029c54004eSDavid Woodhouse 9039c54004eSDavid Woodhouse read_lock(&vcc_sklist_lock); 9049c54004eSDavid Woodhouse head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; 905b67bfe0dSSasha Levin sk_for_each(s, head) { 9069c54004eSDavid Woodhouse vcc = atm_sk(s); 9079c54004eSDavid Woodhouse if (vcc->dev == dev && vcc->vci == vci && 9081f6ea6e5SDavid Woodhouse vcc->vpi == vpi && vcc->qos.rxtp.traffic_class != ATM_NONE && 9091f6ea6e5SDavid Woodhouse test_bit(ATM_VF_READY, &vcc->flags)) 9109c54004eSDavid Woodhouse goto out; 9119c54004eSDavid Woodhouse } 9129c54004eSDavid Woodhouse vcc = NULL; 9139c54004eSDavid Woodhouse out: 9149c54004eSDavid Woodhouse read_unlock(&vcc_sklist_lock); 9159c54004eSDavid Woodhouse return vcc; 9169c54004eSDavid Woodhouse } 9179c54004eSDavid Woodhouse 9189c54004eSDavid Woodhouse static int popen(struct atm_vcc *vcc) 9199c54004eSDavid Woodhouse { 9209c54004eSDavid Woodhouse struct solos_card *card = vcc->dev->dev_data; 9219c54004eSDavid Woodhouse struct sk_buff *skb; 9229c54004eSDavid Woodhouse struct pkt_hdr *header; 9239c54004eSDavid Woodhouse 924b28a4b9aSDavid Woodhouse if (vcc->qos.aal != ATM_AAL5) { 925b28a4b9aSDavid Woodhouse dev_warn(&card->dev->dev, "Unsupported ATM type %d\n", 926b28a4b9aSDavid Woodhouse vcc->qos.aal); 927b28a4b9aSDavid Woodhouse return -EINVAL; 928b28a4b9aSDavid Woodhouse } 929b28a4b9aSDavid Woodhouse 930a1db5c5bSDavid Woodhouse skb = alloc_skb(sizeof(*header), GFP_KERNEL); 931da1ab3e2SJesper Juhl if (!skb) { 932da1ab3e2SJesper Juhl if (net_ratelimit()) 9339c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in popen()\n"); 9349c54004eSDavid Woodhouse return -ENOMEM; 9359c54004eSDavid Woodhouse } 9364df864c1SJohannes Berg header = skb_put(skb, sizeof(*header)); 9379c54004eSDavid Woodhouse 938b76811afSDavid Woodhouse header->size = cpu_to_le16(0); 9399c54004eSDavid Woodhouse header->vpi = cpu_to_le16(vcc->vpi); 9409c54004eSDavid Woodhouse header->vci = cpu_to_le16(vcc->vci); 9419c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_POPEN); 9429c54004eSDavid Woodhouse 9439c54004eSDavid Woodhouse fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, NULL); 9449c54004eSDavid Woodhouse 945bdc54625SDavid Woodhouse set_bit(ATM_VF_ADDR, &vcc->flags); 9469c54004eSDavid Woodhouse set_bit(ATM_VF_READY, &vcc->flags); 9479c54004eSDavid Woodhouse 9489c54004eSDavid Woodhouse return 0; 9499c54004eSDavid Woodhouse } 9509c54004eSDavid Woodhouse 9519c54004eSDavid Woodhouse static void pclose(struct atm_vcc *vcc) 9529c54004eSDavid Woodhouse { 9539c54004eSDavid Woodhouse struct solos_card *card = vcc->dev->dev_data; 9547ad3eadeSDavid Woodhouse unsigned char port = SOLOS_CHAN(vcc->dev); 955213e85d3SDavid Woodhouse struct sk_buff *skb, *tmpskb; 9569c54004eSDavid Woodhouse struct pkt_hdr *header; 9579c54004eSDavid Woodhouse 958213e85d3SDavid Woodhouse /* Remove any yet-to-be-transmitted packets from the pending queue */ 959213e85d3SDavid Woodhouse spin_lock(&card->tx_queue_lock); 960213e85d3SDavid Woodhouse skb_queue_walk_safe(&card->tx_queue[port], skb, tmpskb) { 961213e85d3SDavid Woodhouse if (SKB_CB(skb)->vcc == vcc) { 962213e85d3SDavid Woodhouse skb_unlink(skb, &card->tx_queue[port]); 963213e85d3SDavid Woodhouse solos_pop(vcc, skb); 964213e85d3SDavid Woodhouse } 965213e85d3SDavid Woodhouse } 966213e85d3SDavid Woodhouse spin_unlock(&card->tx_queue_lock); 967213e85d3SDavid Woodhouse 968a1db5c5bSDavid Woodhouse skb = alloc_skb(sizeof(*header), GFP_KERNEL); 9699c54004eSDavid Woodhouse if (!skb) { 9709c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in pclose()\n"); 9719c54004eSDavid Woodhouse return; 9729c54004eSDavid Woodhouse } 9734df864c1SJohannes Berg header = skb_put(skb, sizeof(*header)); 9749c54004eSDavid Woodhouse 975b76811afSDavid Woodhouse header->size = cpu_to_le16(0); 9769c54004eSDavid Woodhouse header->vpi = cpu_to_le16(vcc->vpi); 9779c54004eSDavid Woodhouse header->vci = cpu_to_le16(vcc->vci); 9789c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_PCLOSE); 9799c54004eSDavid Woodhouse 9807ad3eadeSDavid Woodhouse skb_get(skb); 9817ad3eadeSDavid Woodhouse fpga_queue(card, port, skb, NULL); 9829c54004eSDavid Woodhouse 9837ad3eadeSDavid Woodhouse if (!wait_event_timeout(card->param_wq, !skb_shared(skb), 5 * HZ)) 9847ad3eadeSDavid Woodhouse dev_warn(&card->dev->dev, 9857ad3eadeSDavid Woodhouse "Timeout waiting for VCC close on port %d\n", port); 9867ad3eadeSDavid Woodhouse 9877ad3eadeSDavid Woodhouse dev_kfree_skb(skb); 9887ad3eadeSDavid Woodhouse 9891f6ea6e5SDavid Woodhouse /* Hold up vcc_destroy_socket() (our caller) until solos_bh() in the 9901f6ea6e5SDavid Woodhouse tasklet has finished processing any incoming packets (and, more to 9911f6ea6e5SDavid Woodhouse the point, using the vcc pointer). */ 9921f6ea6e5SDavid Woodhouse tasklet_unlock_wait(&card->tlet); 993213e85d3SDavid Woodhouse 994213e85d3SDavid Woodhouse clear_bit(ATM_VF_ADDR, &vcc->flags); 995213e85d3SDavid Woodhouse 9969c54004eSDavid Woodhouse return; 9979c54004eSDavid Woodhouse } 9989c54004eSDavid Woodhouse 9999c54004eSDavid Woodhouse static int print_buffer(struct sk_buff *buf) 10009c54004eSDavid Woodhouse { 10019c54004eSDavid Woodhouse int len,i; 10029c54004eSDavid Woodhouse char msg[500]; 10039c54004eSDavid Woodhouse char item[10]; 10049c54004eSDavid Woodhouse 10059c54004eSDavid Woodhouse len = buf->len; 10069c54004eSDavid Woodhouse for (i = 0; i < len; i++){ 10079c54004eSDavid Woodhouse if(i % 8 == 0) 10089c54004eSDavid Woodhouse sprintf(msg, "%02X: ", i); 10099c54004eSDavid Woodhouse 10109c54004eSDavid Woodhouse sprintf(item,"%02X ",*(buf->data + i)); 10119c54004eSDavid Woodhouse strcat(msg, item); 10129c54004eSDavid Woodhouse if(i % 8 == 7) { 10139c54004eSDavid Woodhouse sprintf(item, "\n"); 10149c54004eSDavid Woodhouse strcat(msg, item); 10159c54004eSDavid Woodhouse printk(KERN_DEBUG "%s", msg); 10169c54004eSDavid Woodhouse } 10179c54004eSDavid Woodhouse } 10189c54004eSDavid Woodhouse if (i % 8 != 0) { 10199c54004eSDavid Woodhouse sprintf(item, "\n"); 10209c54004eSDavid Woodhouse strcat(msg, item); 10219c54004eSDavid Woodhouse printk(KERN_DEBUG "%s", msg); 10229c54004eSDavid Woodhouse } 10239c54004eSDavid Woodhouse printk(KERN_DEBUG "\n"); 10249c54004eSDavid Woodhouse 10259c54004eSDavid Woodhouse return 0; 10269c54004eSDavid Woodhouse } 10279c54004eSDavid Woodhouse 10289c54004eSDavid Woodhouse static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb, 10299c54004eSDavid Woodhouse struct atm_vcc *vcc) 10309c54004eSDavid Woodhouse { 10319c54004eSDavid Woodhouse int old_len; 1032f69e4170SDavid Woodhouse unsigned long flags; 10339c54004eSDavid Woodhouse 103490937231SDavid Woodhouse SKB_CB(skb)->vcc = vcc; 10359c54004eSDavid Woodhouse 1036f69e4170SDavid Woodhouse spin_lock_irqsave(&card->tx_queue_lock, flags); 10379c54004eSDavid Woodhouse old_len = skb_queue_len(&card->tx_queue[port]); 10389c54004eSDavid Woodhouse skb_queue_tail(&card->tx_queue[port], skb); 103935c2221bSDavid Woodhouse if (!old_len) 1040f69e4170SDavid Woodhouse card->tx_mask |= (1 << port); 1041f69e4170SDavid Woodhouse spin_unlock_irqrestore(&card->tx_queue_lock, flags); 10429c54004eSDavid Woodhouse 1043f69e4170SDavid Woodhouse /* Theoretically we could just schedule the tasklet here, but 1044f69e4170SDavid Woodhouse that introduces latency we don't want -- it's noticeable */ 10459c54004eSDavid Woodhouse if (!old_len) 10469c54004eSDavid Woodhouse fpga_tx(card); 10479c54004eSDavid Woodhouse } 10489c54004eSDavid Woodhouse 104935c2221bSDavid Woodhouse static uint32_t fpga_tx(struct solos_card *card) 10509c54004eSDavid Woodhouse { 105135c2221bSDavid Woodhouse uint32_t tx_pending, card_flags; 10529c54004eSDavid Woodhouse uint32_t tx_started = 0; 10539c54004eSDavid Woodhouse struct sk_buff *skb; 10549c54004eSDavid Woodhouse struct atm_vcc *vcc; 10559c54004eSDavid Woodhouse unsigned char port; 10569c54004eSDavid Woodhouse unsigned long flags; 10579c54004eSDavid Woodhouse 10589c54004eSDavid Woodhouse spin_lock_irqsave(&card->tx_lock, flags); 10599c54004eSDavid Woodhouse 106035c2221bSDavid Woodhouse card_flags = ioread32(card->config_regs + FLAGS_ADDR); 106135c2221bSDavid Woodhouse /* 106235c2221bSDavid Woodhouse * The queue lock is required for _writing_ to tx_mask, but we're 106335c2221bSDavid Woodhouse * OK to read it here without locking. The only potential update 106435c2221bSDavid Woodhouse * that we could race with is in fpga_queue() where it sets a bit 106535c2221bSDavid Woodhouse * for a new port... but it's going to call this function again if 106635c2221bSDavid Woodhouse * it's doing that, anyway. 106735c2221bSDavid Woodhouse */ 106835c2221bSDavid Woodhouse tx_pending = card->tx_mask & ~card_flags; 10699c54004eSDavid Woodhouse 107035c2221bSDavid Woodhouse for (port = 0; tx_pending; tx_pending >>= 1, port++) { 107135c2221bSDavid Woodhouse if (tx_pending & 1) { 1072eaf83e39SDavid Woodhouse struct sk_buff *oldskb = card->tx_skb[port]; 1073cae49edeSDavid Woodhouse if (oldskb) { 1074ede58ef2Schas williams - CONTRACTOR dma_unmap_single(&card->dev->dev, SKB_CB(oldskb)->dma_addr, 1075ede58ef2Schas williams - CONTRACTOR oldskb->len, DMA_TO_DEVICE); 1076cae49edeSDavid Woodhouse card->tx_skb[port] = NULL; 1077cae49edeSDavid Woodhouse } 10789c54004eSDavid Woodhouse spin_lock(&card->tx_queue_lock); 10799c54004eSDavid Woodhouse skb = skb_dequeue(&card->tx_queue[port]); 1080f69e4170SDavid Woodhouse if (!skb) 1081f69e4170SDavid Woodhouse card->tx_mask &= ~(1 << port); 10829c54004eSDavid Woodhouse spin_unlock(&card->tx_queue_lock); 10839c54004eSDavid Woodhouse 1084eaf83e39SDavid Woodhouse if (skb && !card->using_dma) { 1085eaf83e39SDavid Woodhouse memcpy_toio(TX_BUF(card, port), skb->data, skb->len); 1086bdc54625SDavid Woodhouse tx_started |= 1 << port; 1087eaf83e39SDavid Woodhouse oldskb = skb; /* We're done with this skb already */ 1088eaf83e39SDavid Woodhouse } else if (skb && card->using_dma) { 1089152a2a8bSDavid Woodhouse unsigned char *data = skb->data; 1090152a2a8bSDavid Woodhouse if ((unsigned long)data & card->dma_alignment) { 1091152a2a8bSDavid Woodhouse data = card->dma_bounce + (BUF_SIZE * port); 1092152a2a8bSDavid Woodhouse memcpy(data, skb->data, skb->len); 1093152a2a8bSDavid Woodhouse } 1094ede58ef2Schas williams - CONTRACTOR SKB_CB(skb)->dma_addr = dma_map_single(&card->dev->dev, data, 1095ede58ef2Schas williams - CONTRACTOR skb->len, DMA_TO_DEVICE); 1096b4bd8ad9SDavid Woodhouse card->tx_skb[port] = skb; 1097eaf83e39SDavid Woodhouse iowrite32(SKB_CB(skb)->dma_addr, 1098eaf83e39SDavid Woodhouse card->config_regs + TX_DMA_ADDR(port)); 1099eaf83e39SDavid Woodhouse } 1100eaf83e39SDavid Woodhouse 1101eaf83e39SDavid Woodhouse if (!oldskb) 11029c54004eSDavid Woodhouse continue; 11039c54004eSDavid Woodhouse 1104eaf83e39SDavid Woodhouse /* Clean up and free oldskb now it's gone */ 11059c54004eSDavid Woodhouse if (atmdebug) { 110618b429e7SPhilip A. Prindeville struct pkt_hdr *header = (void *)oldskb->data; 110718b429e7SPhilip A. Prindeville int size = le16_to_cpu(header->size); 110818b429e7SPhilip A. Prindeville 110918b429e7SPhilip A. Prindeville skb_pull(oldskb, sizeof(*header)); 11109c54004eSDavid Woodhouse dev_info(&card->dev->dev, "Transmitted: port %d\n", 11119c54004eSDavid Woodhouse port); 111218b429e7SPhilip A. Prindeville dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n", 111318b429e7SPhilip A. Prindeville size, le16_to_cpu(header->vpi), 111418b429e7SPhilip A. Prindeville le16_to_cpu(header->vci)); 1115eaf83e39SDavid Woodhouse print_buffer(oldskb); 11169c54004eSDavid Woodhouse } 11179c54004eSDavid Woodhouse 111890937231SDavid Woodhouse vcc = SKB_CB(oldskb)->vcc; 11199c54004eSDavid Woodhouse 11209c54004eSDavid Woodhouse if (vcc) { 11219c54004eSDavid Woodhouse atomic_inc(&vcc->stats->tx); 112290937231SDavid Woodhouse solos_pop(vcc, oldskb); 11237ad3eadeSDavid Woodhouse } else { 112490937231SDavid Woodhouse dev_kfree_skb_irq(oldskb); 11257ad3eadeSDavid Woodhouse wake_up(&card->param_wq); 11267ad3eadeSDavid Woodhouse } 11279c54004eSDavid Woodhouse } 11289c54004eSDavid Woodhouse } 1129bdc54625SDavid Woodhouse /* For non-DMA TX, write the 'TX start' bit for all four ports simultaneously */ 11309c54004eSDavid Woodhouse if (tx_started) 11319c54004eSDavid Woodhouse iowrite32(tx_started, card->config_regs + FLAGS_ADDR); 11329c54004eSDavid Woodhouse 11339c54004eSDavid Woodhouse spin_unlock_irqrestore(&card->tx_lock, flags); 113435c2221bSDavid Woodhouse return card_flags; 11359c54004eSDavid Woodhouse } 11369c54004eSDavid Woodhouse 11379c54004eSDavid Woodhouse static int psend(struct atm_vcc *vcc, struct sk_buff *skb) 11389c54004eSDavid Woodhouse { 11399c54004eSDavid Woodhouse struct solos_card *card = vcc->dev->dev_data; 11409c54004eSDavid Woodhouse struct pkt_hdr *header; 1141b76811afSDavid Woodhouse int pktlen; 11429c54004eSDavid Woodhouse 1143b76811afSDavid Woodhouse pktlen = skb->len; 1144b76811afSDavid Woodhouse if (pktlen > (BUF_SIZE - sizeof(*header))) { 11459c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Length of PDU is too large. Dropping PDU.\n"); 11469c54004eSDavid Woodhouse solos_pop(vcc, skb); 11479c54004eSDavid Woodhouse return 0; 11489c54004eSDavid Woodhouse } 11499c54004eSDavid Woodhouse 11509c54004eSDavid Woodhouse if (!skb_clone_writable(skb, sizeof(*header))) { 11519c54004eSDavid Woodhouse int expand_by = 0; 11529c54004eSDavid Woodhouse int ret; 11539c54004eSDavid Woodhouse 11549c54004eSDavid Woodhouse if (skb_headroom(skb) < sizeof(*header)) 11559c54004eSDavid Woodhouse expand_by = sizeof(*header) - skb_headroom(skb); 11569c54004eSDavid Woodhouse 11579c54004eSDavid Woodhouse ret = pskb_expand_head(skb, expand_by, 0, GFP_ATOMIC); 11589c54004eSDavid Woodhouse if (ret) { 11594306cad6SSimon Farnsworth dev_warn(&card->dev->dev, "pskb_expand_head failed.\n"); 11609c54004eSDavid Woodhouse solos_pop(vcc, skb); 11619c54004eSDavid Woodhouse return ret; 11629c54004eSDavid Woodhouse } 11639c54004eSDavid Woodhouse } 11649c54004eSDavid Woodhouse 1165d58ff351SJohannes Berg header = skb_push(skb, sizeof(*header)); 11669c54004eSDavid Woodhouse 1167b76811afSDavid Woodhouse /* This does _not_ include the size of the header */ 1168b76811afSDavid Woodhouse header->size = cpu_to_le16(pktlen); 11699c54004eSDavid Woodhouse header->vpi = cpu_to_le16(vcc->vpi); 11709c54004eSDavid Woodhouse header->vci = cpu_to_le16(vcc->vci); 11719c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_DATA); 11729c54004eSDavid Woodhouse 11739c54004eSDavid Woodhouse fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, vcc); 11749c54004eSDavid Woodhouse 11759c54004eSDavid Woodhouse return 0; 11769c54004eSDavid Woodhouse } 11779c54004eSDavid Woodhouse 117846c4b7a5SBhumika Goyal static const struct atmdev_ops fpga_ops = { 11799c54004eSDavid Woodhouse .open = popen, 11809c54004eSDavid Woodhouse .close = pclose, 11819c54004eSDavid Woodhouse .ioctl = NULL, 11829c54004eSDavid Woodhouse .getsockopt = NULL, 11839c54004eSDavid Woodhouse .setsockopt = NULL, 11849c54004eSDavid Woodhouse .send = psend, 11859c54004eSDavid Woodhouse .send_oam = NULL, 11869c54004eSDavid Woodhouse .phy_put = NULL, 11879c54004eSDavid Woodhouse .phy_get = NULL, 11889c54004eSDavid Woodhouse .change_qos = NULL, 11899c54004eSDavid Woodhouse .proc_read = NULL, 11909c54004eSDavid Woodhouse .owner = THIS_MODULE 11919c54004eSDavid Woodhouse }; 11929c54004eSDavid Woodhouse 11939c54004eSDavid Woodhouse static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) 11949c54004eSDavid Woodhouse { 1195cd5549e0SDavid Woodhouse int err; 11969c54004eSDavid Woodhouse uint16_t fpga_ver; 11979c54004eSDavid Woodhouse uint8_t major_ver, minor_ver; 11989c54004eSDavid Woodhouse uint32_t data32; 11999c54004eSDavid Woodhouse struct solos_card *card; 12009c54004eSDavid Woodhouse 12019c54004eSDavid Woodhouse card = kzalloc(sizeof(*card), GFP_KERNEL); 12029c54004eSDavid Woodhouse if (!card) 12039c54004eSDavid Woodhouse return -ENOMEM; 12049c54004eSDavid Woodhouse 12059c54004eSDavid Woodhouse card->dev = dev; 1206fa755b9fSDavid Woodhouse init_waitqueue_head(&card->fw_wq); 120701e2ffacSDavid Woodhouse init_waitqueue_head(&card->param_wq); 12089c54004eSDavid Woodhouse 12099c54004eSDavid Woodhouse err = pci_enable_device(dev); 12109c54004eSDavid Woodhouse if (err) { 12119c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to enable PCI device\n"); 12129c54004eSDavid Woodhouse goto out; 12139c54004eSDavid Woodhouse } 12149c54004eSDavid Woodhouse 1215ede58ef2Schas williams - CONTRACTOR err = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); 121690937231SDavid Woodhouse if (err) { 121790937231SDavid Woodhouse dev_warn(&dev->dev, "Failed to set 32-bit DMA mask\n"); 121890937231SDavid Woodhouse goto out; 121990937231SDavid Woodhouse } 122090937231SDavid Woodhouse 12219c54004eSDavid Woodhouse err = pci_request_regions(dev, "solos"); 12229c54004eSDavid Woodhouse if (err) { 12239c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to request regions\n"); 12249c54004eSDavid Woodhouse goto out; 12259c54004eSDavid Woodhouse } 12269c54004eSDavid Woodhouse 12279c54004eSDavid Woodhouse card->config_regs = pci_iomap(dev, 0, CONFIG_RAM_SIZE); 12289c54004eSDavid Woodhouse if (!card->config_regs) { 12299c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to ioremap config registers\n"); 123073112f9bSJulia Lawall err = -ENOMEM; 12319c54004eSDavid Woodhouse goto out_release_regions; 12329c54004eSDavid Woodhouse } 12339c54004eSDavid Woodhouse card->buffers = pci_iomap(dev, 1, DATA_RAM_SIZE); 12349c54004eSDavid Woodhouse if (!card->buffers) { 12359c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to ioremap data buffers\n"); 123673112f9bSJulia Lawall err = -ENOMEM; 12379c54004eSDavid Woodhouse goto out_unmap_config; 12389c54004eSDavid Woodhouse } 12399c54004eSDavid Woodhouse 1240cc3657e1SDavid Woodhouse if (reset) { 1241cc3657e1SDavid Woodhouse iowrite32(1, card->config_regs + FPGA_MODE); 12421492a3a7SGustavo A. R. Silva ioread32(card->config_regs + FPGA_MODE); 12439c54004eSDavid Woodhouse 1244cc3657e1SDavid Woodhouse iowrite32(0, card->config_regs + FPGA_MODE); 12451492a3a7SGustavo A. R. Silva ioread32(card->config_regs + FPGA_MODE); 1246cc3657e1SDavid Woodhouse } 12479c54004eSDavid Woodhouse 12489c54004eSDavid Woodhouse data32 = ioread32(card->config_regs + FPGA_VER); 12499c54004eSDavid Woodhouse fpga_ver = (data32 & 0x0000FFFF); 12509c54004eSDavid Woodhouse major_ver = ((data32 & 0xFF000000) >> 24); 12519c54004eSDavid Woodhouse minor_ver = ((data32 & 0x00FF0000) >> 16); 12524dbedf43SNathan Williams card->fpga_version = FPGA_VERSION(major_ver,minor_ver); 12534dbedf43SNathan Williams if (card->fpga_version > LEGACY_BUFFERS) 12544dbedf43SNathan Williams card->buffer_size = BUF_SIZE; 12554dbedf43SNathan Williams else 12564dbedf43SNathan Williams card->buffer_size = OLD_BUF_SIZE; 12579c54004eSDavid Woodhouse dev_info(&dev->dev, "Solos FPGA Version %d.%02d svn-%d\n", 12589c54004eSDavid Woodhouse major_ver, minor_ver, fpga_ver); 12599c54004eSDavid Woodhouse 12603ce1227cSDavid Woodhouse if (fpga_ver < 37 && (fpga_upgrade || firmware_upgrade || 12613ce1227cSDavid Woodhouse db_fpga_upgrade || db_firmware_upgrade)) { 12623ce1227cSDavid Woodhouse dev_warn(&dev->dev, 12633ce1227cSDavid Woodhouse "FPGA too old; cannot upgrade flash. Use JTAG.\n"); 12643ce1227cSDavid Woodhouse fpga_upgrade = firmware_upgrade = 0; 12653ce1227cSDavid Woodhouse db_fpga_upgrade = db_firmware_upgrade = 0; 12663ce1227cSDavid Woodhouse } 12673ce1227cSDavid Woodhouse 126813af8164SNathan Williams /* Stopped using Atmel flash after 0.03-38 */ 126913af8164SNathan Williams if (fpga_ver < 39) 127013af8164SNathan Williams card->atmel_flash = 1; 127113af8164SNathan Williams else 127213af8164SNathan Williams card->atmel_flash = 0; 127313af8164SNathan Williams 1274152a2a8bSDavid Woodhouse data32 = ioread32(card->config_regs + PORTS); 1275152a2a8bSDavid Woodhouse card->nr_ports = (data32 & 0x000000FF); 1276152a2a8bSDavid Woodhouse 12774dbedf43SNathan Williams if (card->fpga_version >= DMA_SUPPORTED) { 1278b4bd8ad9SDavid Woodhouse pci_set_master(dev); 127990937231SDavid Woodhouse card->using_dma = 1; 1280152a2a8bSDavid Woodhouse if (1) { /* All known FPGA versions so far */ 1281152a2a8bSDavid Woodhouse card->dma_alignment = 3; 12826da2ec56SKees Cook card->dma_bounce = kmalloc_array(card->nr_ports, 12836da2ec56SKees Cook BUF_SIZE, GFP_KERNEL); 1284152a2a8bSDavid Woodhouse if (!card->dma_bounce) { 1285152a2a8bSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate DMA bounce buffers\n"); 1286c7bcae46SJulia Lawall err = -ENOMEM; 1287152a2a8bSDavid Woodhouse /* Fallback to MMIO doesn't work */ 1288152a2a8bSDavid Woodhouse goto out_unmap_both; 1289152a2a8bSDavid Woodhouse } 1290152a2a8bSDavid Woodhouse } 12914dbedf43SNathan Williams } else { 12924dbedf43SNathan Williams card->using_dma = 0; 1293eab50f73SDavid Woodhouse /* Set RX empty flag for all ports */ 1294eab50f73SDavid Woodhouse iowrite32(0xF0, card->config_regs + FLAGS_ADDR); 1295eab50f73SDavid Woodhouse } 12969c54004eSDavid Woodhouse 12979c54004eSDavid Woodhouse pci_set_drvdata(dev, card); 1298fa755b9fSDavid Woodhouse 12999c54004eSDavid Woodhouse tasklet_init(&card->tlet, solos_bh, (unsigned long)card); 13009c54004eSDavid Woodhouse spin_lock_init(&card->tx_lock); 13019c54004eSDavid Woodhouse spin_lock_init(&card->tx_queue_lock); 13029c54004eSDavid Woodhouse spin_lock_init(&card->cli_queue_lock); 130301e2ffacSDavid Woodhouse spin_lock_init(&card->param_queue_lock); 130401e2ffacSDavid Woodhouse INIT_LIST_HEAD(&card->param_queue); 13059c54004eSDavid Woodhouse 1306fcd82664SDavid Woodhouse err = request_irq(dev->irq, solos_irq, IRQF_SHARED, 13079c54004eSDavid Woodhouse "solos-pci", card); 1308fa755b9fSDavid Woodhouse if (err) { 13099c54004eSDavid Woodhouse dev_dbg(&card->dev->dev, "Failed to request interrupt IRQ: %d\n", dev->irq); 1310fa755b9fSDavid Woodhouse goto out_unmap_both; 1311fa755b9fSDavid Woodhouse } 13129c54004eSDavid Woodhouse 13139c54004eSDavid Woodhouse iowrite32(1, card->config_regs + IRQ_EN_ADDR); 13149c54004eSDavid Woodhouse 1315fa755b9fSDavid Woodhouse if (fpga_upgrade) 1316fa755b9fSDavid Woodhouse flash_upgrade(card, 0); 1317fa755b9fSDavid Woodhouse 1318fa755b9fSDavid Woodhouse if (firmware_upgrade) 1319fa755b9fSDavid Woodhouse flash_upgrade(card, 1); 1320fa755b9fSDavid Woodhouse 13214dbedf43SNathan Williams if (db_fpga_upgrade) 13224dbedf43SNathan Williams flash_upgrade(card, 2); 13234dbedf43SNathan Williams 13244dbedf43SNathan Williams if (db_firmware_upgrade) 13254dbedf43SNathan Williams flash_upgrade(card, 3); 13264dbedf43SNathan Williams 1327d9ca676bSDan Williams err = atm_init(card, &dev->dev); 1328fa755b9fSDavid Woodhouse if (err) 1329fa755b9fSDavid Woodhouse goto out_free_irq; 1330fa755b9fSDavid Woodhouse 1331f9baad02SNathan Williams if (card->fpga_version >= DMA_SUPPORTED && 1332f9baad02SNathan Williams sysfs_create_group(&card->dev->dev.kobj, &gpio_attr_group)) 1333f9baad02SNathan Williams dev_err(&card->dev->dev, "Could not register parameter group for GPIOs\n"); 1334f9baad02SNathan Williams 13359c54004eSDavid Woodhouse return 0; 13369c54004eSDavid Woodhouse 1337fa755b9fSDavid Woodhouse out_free_irq: 1338fa755b9fSDavid Woodhouse iowrite32(0, card->config_regs + IRQ_EN_ADDR); 1339fa755b9fSDavid Woodhouse free_irq(dev->irq, card); 1340fa755b9fSDavid Woodhouse tasklet_kill(&card->tlet); 1341fa755b9fSDavid Woodhouse 13429c54004eSDavid Woodhouse out_unmap_both: 1343152a2a8bSDavid Woodhouse kfree(card->dma_bounce); 13449c54004eSDavid Woodhouse pci_iounmap(dev, card->buffers); 13458ae0cfeeSJulia Lawall out_unmap_config: 13468ae0cfeeSJulia Lawall pci_iounmap(dev, card->config_regs); 13479c54004eSDavid Woodhouse out_release_regions: 13489c54004eSDavid Woodhouse pci_release_regions(dev); 13499c54004eSDavid Woodhouse out: 1350bc111d57SJulia Lawall kfree(card); 13519c54004eSDavid Woodhouse return err; 13529c54004eSDavid Woodhouse } 13539c54004eSDavid Woodhouse 1354d9ca676bSDan Williams static int atm_init(struct solos_card *card, struct device *parent) 13559c54004eSDavid Woodhouse { 13569c54004eSDavid Woodhouse int i; 13579c54004eSDavid Woodhouse 13589c54004eSDavid Woodhouse for (i = 0; i < card->nr_ports; i++) { 135987ebb186SDavid Woodhouse struct sk_buff *skb; 136087ebb186SDavid Woodhouse struct pkt_hdr *header; 136187ebb186SDavid Woodhouse 13629c54004eSDavid Woodhouse skb_queue_head_init(&card->tx_queue[i]); 13639c54004eSDavid Woodhouse skb_queue_head_init(&card->cli_queue[i]); 13649c54004eSDavid Woodhouse 1365d9ca676bSDan Williams card->atmdev[i] = atm_dev_register("solos-pci", parent, &fpga_ops, -1, NULL); 13669c54004eSDavid Woodhouse if (!card->atmdev[i]) { 13679c54004eSDavid Woodhouse dev_err(&card->dev->dev, "Could not register ATM device %d\n", i); 13689c54004eSDavid Woodhouse atm_remove(card); 13699c54004eSDavid Woodhouse return -ENODEV; 13709c54004eSDavid Woodhouse } 13719c54004eSDavid Woodhouse if (device_create_file(&card->atmdev[i]->class_dev, &dev_attr_console)) 13729c54004eSDavid Woodhouse dev_err(&card->dev->dev, "Could not register console for ATM device %d\n", i); 1373d057f0a4SDavid Woodhouse if (sysfs_create_group(&card->atmdev[i]->class_dev.kobj, &solos_attr_group)) 1374d057f0a4SDavid Woodhouse dev_err(&card->dev->dev, "Could not register parameter group for ATM device %d\n", i); 13759c54004eSDavid Woodhouse 13769c54004eSDavid Woodhouse dev_info(&card->dev->dev, "Registered ATM device %d\n", card->atmdev[i]->number); 13779c54004eSDavid Woodhouse 13789c54004eSDavid Woodhouse card->atmdev[i]->ci_range.vpi_bits = 8; 13799c54004eSDavid Woodhouse card->atmdev[i]->ci_range.vci_bits = 16; 13809c54004eSDavid Woodhouse card->atmdev[i]->dev_data = card; 13819c54004eSDavid Woodhouse card->atmdev[i]->phy_data = (void *)(unsigned long)i; 1382c031235bSPhilip A. Prindeville atm_dev_signal_change(card->atmdev[i], ATM_PHY_SIG_FOUND); 138387ebb186SDavid Woodhouse 1384a1db5c5bSDavid Woodhouse skb = alloc_skb(sizeof(*header), GFP_KERNEL); 138587ebb186SDavid Woodhouse if (!skb) { 138687ebb186SDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in atm_init()\n"); 138787ebb186SDavid Woodhouse continue; 138887ebb186SDavid Woodhouse } 138987ebb186SDavid Woodhouse 13904df864c1SJohannes Berg header = skb_put(skb, sizeof(*header)); 139187ebb186SDavid Woodhouse 139287ebb186SDavid Woodhouse header->size = cpu_to_le16(0); 139387ebb186SDavid Woodhouse header->vpi = cpu_to_le16(0); 139487ebb186SDavid Woodhouse header->vci = cpu_to_le16(0); 139587ebb186SDavid Woodhouse header->type = cpu_to_le16(PKT_STATUS); 139687ebb186SDavid Woodhouse 139787ebb186SDavid Woodhouse fpga_queue(card, i, skb, NULL); 13989c54004eSDavid Woodhouse } 13999c54004eSDavid Woodhouse return 0; 14009c54004eSDavid Woodhouse } 14019c54004eSDavid Woodhouse 14029c54004eSDavid Woodhouse static void atm_remove(struct solos_card *card) 14039c54004eSDavid Woodhouse { 14049c54004eSDavid Woodhouse int i; 14059c54004eSDavid Woodhouse 14069c54004eSDavid Woodhouse for (i = 0; i < card->nr_ports; i++) { 14079c54004eSDavid Woodhouse if (card->atmdev[i]) { 140897d759d3SDavid Woodhouse struct sk_buff *skb; 140997d759d3SDavid Woodhouse 14109c54004eSDavid Woodhouse dev_info(&card->dev->dev, "Unregistering ATM device %d\n", card->atmdev[i]->number); 1411c0fe3026SDavid Woodhouse 1412c0fe3026SDavid Woodhouse sysfs_remove_group(&card->atmdev[i]->class_dev.kobj, &solos_attr_group); 14139c54004eSDavid Woodhouse atm_dev_deregister(card->atmdev[i]); 141497d759d3SDavid Woodhouse 141597d759d3SDavid Woodhouse skb = card->rx_skb[i]; 141697d759d3SDavid Woodhouse if (skb) { 1417ede58ef2Schas williams - CONTRACTOR dma_unmap_single(&card->dev->dev, SKB_CB(skb)->dma_addr, 1418ede58ef2Schas williams - CONTRACTOR RX_DMA_SIZE, DMA_FROM_DEVICE); 141997d759d3SDavid Woodhouse dev_kfree_skb(skb); 142097d759d3SDavid Woodhouse } 142197d759d3SDavid Woodhouse skb = card->tx_skb[i]; 142297d759d3SDavid Woodhouse if (skb) { 1423ede58ef2Schas williams - CONTRACTOR dma_unmap_single(&card->dev->dev, SKB_CB(skb)->dma_addr, 1424ede58ef2Schas williams - CONTRACTOR skb->len, DMA_TO_DEVICE); 142597d759d3SDavid Woodhouse dev_kfree_skb(skb); 142697d759d3SDavid Woodhouse } 142797d759d3SDavid Woodhouse while ((skb = skb_dequeue(&card->tx_queue[i]))) 142897d759d3SDavid Woodhouse dev_kfree_skb(skb); 142997d759d3SDavid Woodhouse 14309c54004eSDavid Woodhouse } 14319c54004eSDavid Woodhouse } 14329c54004eSDavid Woodhouse } 14339c54004eSDavid Woodhouse 14349c54004eSDavid Woodhouse static void fpga_remove(struct pci_dev *dev) 14359c54004eSDavid Woodhouse { 14369c54004eSDavid Woodhouse struct solos_card *card = pci_get_drvdata(dev); 14379c54004eSDavid Woodhouse 143897d759d3SDavid Woodhouse /* Disable IRQs */ 143997d759d3SDavid Woodhouse iowrite32(0, card->config_regs + IRQ_EN_ADDR); 144097d759d3SDavid Woodhouse 144197d759d3SDavid Woodhouse /* Reset FPGA */ 144297d759d3SDavid Woodhouse iowrite32(1, card->config_regs + FPGA_MODE); 144397d759d3SDavid Woodhouse (void)ioread32(card->config_regs + FPGA_MODE); 14449c54004eSDavid Woodhouse 1445f9baad02SNathan Williams if (card->fpga_version >= DMA_SUPPORTED) 1446f9baad02SNathan Williams sysfs_remove_group(&card->dev->dev.kobj, &gpio_attr_group); 1447f9baad02SNathan Williams 14489c54004eSDavid Woodhouse atm_remove(card); 14499c54004eSDavid Woodhouse 14509c54004eSDavid Woodhouse free_irq(dev->irq, card); 14519c54004eSDavid Woodhouse tasklet_kill(&card->tlet); 14529c54004eSDavid Woodhouse 1453152a2a8bSDavid Woodhouse kfree(card->dma_bounce); 1454152a2a8bSDavid Woodhouse 145597d759d3SDavid Woodhouse /* Release device from reset */ 145697d759d3SDavid Woodhouse iowrite32(0, card->config_regs + FPGA_MODE); 145797d759d3SDavid Woodhouse (void)ioread32(card->config_regs + FPGA_MODE); 145897d759d3SDavid Woodhouse 14599c54004eSDavid Woodhouse pci_iounmap(dev, card->buffers); 14609c54004eSDavid Woodhouse pci_iounmap(dev, card->config_regs); 14619c54004eSDavid Woodhouse 14629c54004eSDavid Woodhouse pci_release_regions(dev); 14639c54004eSDavid Woodhouse pci_disable_device(dev); 14649c54004eSDavid Woodhouse 14659c54004eSDavid Woodhouse kfree(card); 14669c54004eSDavid Woodhouse } 14679c54004eSDavid Woodhouse 14686d6148b3SArvind Yadav static const struct pci_device_id fpga_pci_tbl[] = { 14699c54004eSDavid Woodhouse { 0x10ee, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 14709c54004eSDavid Woodhouse { 0, } 14719c54004eSDavid Woodhouse }; 14729c54004eSDavid Woodhouse 14739c54004eSDavid Woodhouse MODULE_DEVICE_TABLE(pci,fpga_pci_tbl); 14749c54004eSDavid Woodhouse 14759c54004eSDavid Woodhouse static struct pci_driver fpga_driver = { 14769c54004eSDavid Woodhouse .name = "solos", 14779c54004eSDavid Woodhouse .id_table = fpga_pci_tbl, 14789c54004eSDavid Woodhouse .probe = fpga_probe, 14799c54004eSDavid Woodhouse .remove = fpga_remove, 14809c54004eSDavid Woodhouse }; 14819c54004eSDavid Woodhouse 14829c54004eSDavid Woodhouse 14839c54004eSDavid Woodhouse static int __init solos_pci_init(void) 14849c54004eSDavid Woodhouse { 14857ad3eadeSDavid Woodhouse BUILD_BUG_ON(sizeof(struct solos_skb_cb) > sizeof(((struct sk_buff *)0)->cb)); 14867ad3eadeSDavid Woodhouse 14879c54004eSDavid Woodhouse printk(KERN_INFO "Solos PCI Driver Version %s\n", VERSION); 14889c54004eSDavid Woodhouse return pci_register_driver(&fpga_driver); 14899c54004eSDavid Woodhouse } 14909c54004eSDavid Woodhouse 14919c54004eSDavid Woodhouse static void __exit solos_pci_exit(void) 14929c54004eSDavid Woodhouse { 14939c54004eSDavid Woodhouse pci_unregister_driver(&fpga_driver); 14949c54004eSDavid Woodhouse printk(KERN_INFO "Solos PCI Driver %s Unloaded\n", VERSION); 14959c54004eSDavid Woodhouse } 14969c54004eSDavid Woodhouse 14979c54004eSDavid Woodhouse module_init(solos_pci_init); 14989c54004eSDavid Woodhouse module_exit(solos_pci_exit); 1499