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> 129c54004eSDavid Woodhouse * 139c54004eSDavid Woodhouse * This program is free software; you can redistribute it and/or 149c54004eSDavid Woodhouse * modify it under the terms of the GNU General Public License 159c54004eSDavid Woodhouse * version 2, as published by the Free Software Foundation. 169c54004eSDavid Woodhouse * 179c54004eSDavid Woodhouse * This program is distributed in the hope that it will be useful, 189c54004eSDavid Woodhouse * but WITHOUT ANY WARRANTY; without even the implied warranty of 199c54004eSDavid Woodhouse * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 209c54004eSDavid Woodhouse * GNU General Public License for more details. 219c54004eSDavid Woodhouse */ 229c54004eSDavid Woodhouse 239c54004eSDavid Woodhouse #define DEBUG 249c54004eSDavid Woodhouse #define VERBOSE_DEBUG 259c54004eSDavid Woodhouse 269c54004eSDavid Woodhouse #include <linux/interrupt.h> 279c54004eSDavid Woodhouse #include <linux/module.h> 289c54004eSDavid Woodhouse #include <linux/kernel.h> 299c54004eSDavid Woodhouse #include <linux/errno.h> 309c54004eSDavid Woodhouse #include <linux/ioport.h> 319c54004eSDavid Woodhouse #include <linux/types.h> 329c54004eSDavid Woodhouse #include <linux/pci.h> 339c54004eSDavid Woodhouse #include <linux/atm.h> 349c54004eSDavid Woodhouse #include <linux/atmdev.h> 359c54004eSDavid Woodhouse #include <linux/skbuff.h> 369c54004eSDavid Woodhouse #include <linux/sysfs.h> 379c54004eSDavid Woodhouse #include <linux/device.h> 389c54004eSDavid Woodhouse #include <linux/kobject.h> 399c54004eSDavid Woodhouse 409c54004eSDavid Woodhouse #define VERSION "0.04" 419c54004eSDavid Woodhouse #define PTAG "solos-pci" 429c54004eSDavid Woodhouse 439c54004eSDavid Woodhouse #define CONFIG_RAM_SIZE 128 449c54004eSDavid Woodhouse #define FLAGS_ADDR 0x7C 459c54004eSDavid Woodhouse #define IRQ_EN_ADDR 0x78 469c54004eSDavid Woodhouse #define FPGA_VER 0x74 479c54004eSDavid Woodhouse #define IRQ_CLEAR 0x70 489c54004eSDavid Woodhouse #define BUG_FLAG 0x6C 499c54004eSDavid Woodhouse 509c54004eSDavid Woodhouse #define DATA_RAM_SIZE 32768 519c54004eSDavid Woodhouse #define BUF_SIZE 4096 529c54004eSDavid Woodhouse 539c54004eSDavid Woodhouse #define RX_BUF(card, nr) ((card->buffers) + (nr)*BUF_SIZE*2) 549c54004eSDavid Woodhouse #define TX_BUF(card, nr) ((card->buffers) + (nr)*BUF_SIZE*2 + BUF_SIZE) 559c54004eSDavid Woodhouse 569c54004eSDavid Woodhouse static int debug = 0; 579c54004eSDavid Woodhouse static int atmdebug = 0; 589c54004eSDavid Woodhouse 599c54004eSDavid Woodhouse struct pkt_hdr { 609c54004eSDavid Woodhouse __le16 size; 619c54004eSDavid Woodhouse __le16 vpi; 629c54004eSDavid Woodhouse __le16 vci; 639c54004eSDavid Woodhouse __le16 type; 649c54004eSDavid Woodhouse }; 659c54004eSDavid Woodhouse 669c54004eSDavid Woodhouse #define PKT_DATA 0 679c54004eSDavid Woodhouse #define PKT_COMMAND 1 689c54004eSDavid Woodhouse #define PKT_POPEN 3 699c54004eSDavid Woodhouse #define PKT_PCLOSE 4 709c54004eSDavid Woodhouse 719c54004eSDavid Woodhouse struct solos_card { 729c54004eSDavid Woodhouse void __iomem *config_regs; 739c54004eSDavid Woodhouse void __iomem *buffers; 749c54004eSDavid Woodhouse int nr_ports; 759c54004eSDavid Woodhouse struct pci_dev *dev; 769c54004eSDavid Woodhouse struct atm_dev *atmdev[4]; 779c54004eSDavid Woodhouse struct tasklet_struct tlet; 789c54004eSDavid Woodhouse spinlock_t tx_lock; 799c54004eSDavid Woodhouse spinlock_t tx_queue_lock; 809c54004eSDavid Woodhouse spinlock_t cli_queue_lock; 819c54004eSDavid Woodhouse struct sk_buff_head tx_queue[4]; 829c54004eSDavid Woodhouse struct sk_buff_head cli_queue[4]; 839c54004eSDavid Woodhouse }; 849c54004eSDavid Woodhouse 859c54004eSDavid Woodhouse #define SOLOS_CHAN(atmdev) ((int)(unsigned long)(atmdev)->phy_data) 869c54004eSDavid Woodhouse 879c54004eSDavid Woodhouse MODULE_AUTHOR("Traverse Technologies <support@traverse.com.au>"); 889c54004eSDavid Woodhouse MODULE_DESCRIPTION("Solos PCI driver"); 899c54004eSDavid Woodhouse MODULE_VERSION(VERSION); 909c54004eSDavid Woodhouse MODULE_LICENSE("GPL"); 919c54004eSDavid Woodhouse MODULE_PARM_DESC(debug, "Enable Loopback"); 929c54004eSDavid Woodhouse MODULE_PARM_DESC(atmdebug, "Print ATM data"); 939c54004eSDavid Woodhouse module_param(debug, int, 0444); 949c54004eSDavid Woodhouse module_param(atmdebug, int, 0444); 959c54004eSDavid Woodhouse 969c54004eSDavid Woodhouse static int opens; 979c54004eSDavid Woodhouse 989c54004eSDavid Woodhouse static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb, 999c54004eSDavid Woodhouse struct atm_vcc *vcc); 1009c54004eSDavid Woodhouse static int fpga_tx(struct solos_card *); 1019c54004eSDavid Woodhouse static irqreturn_t solos_irq(int irq, void *dev_id); 1029c54004eSDavid Woodhouse static struct atm_vcc* find_vcc(struct atm_dev *dev, short vpi, int vci); 1039c54004eSDavid Woodhouse static int list_vccs(int vci); 1049c54004eSDavid Woodhouse static int atm_init(struct solos_card *); 1059c54004eSDavid Woodhouse static void atm_remove(struct solos_card *); 1069c54004eSDavid Woodhouse static int send_command(struct solos_card *card, int dev, const char *buf, size_t size); 1079c54004eSDavid Woodhouse static void solos_bh(unsigned long); 1089c54004eSDavid Woodhouse static int print_buffer(struct sk_buff *buf); 1099c54004eSDavid Woodhouse 1109c54004eSDavid Woodhouse static inline void solos_pop(struct atm_vcc *vcc, struct sk_buff *skb) 1119c54004eSDavid Woodhouse { 1129c54004eSDavid Woodhouse if (vcc->pop) 1139c54004eSDavid Woodhouse vcc->pop(vcc, skb); 1149c54004eSDavid Woodhouse else 1159c54004eSDavid Woodhouse dev_kfree_skb_any(skb); 1169c54004eSDavid Woodhouse } 1179c54004eSDavid Woodhouse 1189c54004eSDavid Woodhouse static ssize_t console_show(struct device *dev, struct device_attribute *attr, 1199c54004eSDavid Woodhouse char *buf) 1209c54004eSDavid Woodhouse { 1219c54004eSDavid Woodhouse struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev); 1229c54004eSDavid Woodhouse struct solos_card *card = atmdev->dev_data; 1239c54004eSDavid Woodhouse struct sk_buff *skb; 1249c54004eSDavid Woodhouse 1259c54004eSDavid Woodhouse spin_lock(&card->cli_queue_lock); 1269c54004eSDavid Woodhouse skb = skb_dequeue(&card->cli_queue[SOLOS_CHAN(atmdev)]); 1279c54004eSDavid Woodhouse spin_unlock(&card->cli_queue_lock); 1289c54004eSDavid Woodhouse if(skb == NULL) 1299c54004eSDavid Woodhouse return sprintf(buf, "No data.\n"); 1309c54004eSDavid Woodhouse 1319c54004eSDavid Woodhouse memcpy(buf, skb->data, skb->len); 1329c54004eSDavid Woodhouse dev_dbg(&card->dev->dev, "len: %d\n", skb->len); 1339c54004eSDavid Woodhouse 1349c54004eSDavid Woodhouse kfree_skb(skb); 1359c54004eSDavid Woodhouse return skb->len; 1369c54004eSDavid Woodhouse } 1379c54004eSDavid Woodhouse 1389c54004eSDavid Woodhouse static int send_command(struct solos_card *card, int dev, const char *buf, size_t size) 1399c54004eSDavid Woodhouse { 1409c54004eSDavid Woodhouse struct sk_buff *skb; 1419c54004eSDavid Woodhouse struct pkt_hdr *header; 1429c54004eSDavid Woodhouse 1439c54004eSDavid Woodhouse // dev_dbg(&card->dev->dev, "size: %d\n", size); 1449c54004eSDavid Woodhouse 1459c54004eSDavid Woodhouse if (size > (BUF_SIZE - sizeof(*header))) { 1469c54004eSDavid Woodhouse dev_dbg(&card->dev->dev, "Command is too big. Dropping request\n"); 1479c54004eSDavid Woodhouse return 0; 1489c54004eSDavid Woodhouse } 1499c54004eSDavid Woodhouse skb = alloc_skb(size + sizeof(*header), GFP_ATOMIC); 1509c54004eSDavid Woodhouse if (!skb) { 1519c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in send_command()\n"); 1529c54004eSDavid Woodhouse return 0; 1539c54004eSDavid Woodhouse } 1549c54004eSDavid Woodhouse 1559c54004eSDavid Woodhouse header = (void *)skb_put(skb, sizeof(*header)); 1569c54004eSDavid Woodhouse 1579c54004eSDavid Woodhouse header->size = cpu_to_le16(size); 1589c54004eSDavid Woodhouse header->vpi = cpu_to_le16(0); 1599c54004eSDavid Woodhouse header->vci = cpu_to_le16(0); 1609c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_COMMAND); 1619c54004eSDavid Woodhouse 1629c54004eSDavid Woodhouse memcpy(skb_put(skb, size), buf, size); 1639c54004eSDavid Woodhouse 1649c54004eSDavid Woodhouse fpga_queue(card, dev, skb, NULL); 1659c54004eSDavid Woodhouse 1669c54004eSDavid Woodhouse return 0; 1679c54004eSDavid Woodhouse } 1689c54004eSDavid Woodhouse 1699c54004eSDavid Woodhouse static ssize_t console_store(struct device *dev, struct device_attribute *attr, 1709c54004eSDavid Woodhouse const char *buf, size_t count) 1719c54004eSDavid Woodhouse { 1729c54004eSDavid Woodhouse struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev); 1739c54004eSDavid Woodhouse struct solos_card *card = atmdev->dev_data; 1749c54004eSDavid Woodhouse int err; 1759c54004eSDavid Woodhouse 1769c54004eSDavid Woodhouse err = send_command(card, SOLOS_CHAN(atmdev), buf, count); 1779c54004eSDavid Woodhouse 1789c54004eSDavid Woodhouse return err?:count; 1799c54004eSDavid Woodhouse } 1809c54004eSDavid Woodhouse 1819c54004eSDavid Woodhouse static DEVICE_ATTR(console, 0644, console_show, console_store); 1829c54004eSDavid Woodhouse 1839c54004eSDavid Woodhouse static irqreturn_t solos_irq(int irq, void *dev_id) 1849c54004eSDavid Woodhouse { 1859c54004eSDavid Woodhouse struct solos_card *card = dev_id; 1869c54004eSDavid Woodhouse int handled = 1; 1879c54004eSDavid Woodhouse 1889c54004eSDavid Woodhouse //ACK IRQ 1899c54004eSDavid Woodhouse iowrite32(0, card->config_regs + IRQ_CLEAR); 1909c54004eSDavid Woodhouse //Disable IRQs from FPGA 1919c54004eSDavid Woodhouse iowrite32(0, card->config_regs + IRQ_EN_ADDR); 1929c54004eSDavid Woodhouse 1939c54004eSDavid Woodhouse /* If we only do it when the device is open, we lose console 1949c54004eSDavid Woodhouse messages */ 1959c54004eSDavid Woodhouse if (1 || opens) 1969c54004eSDavid Woodhouse tasklet_schedule(&card->tlet); 1979c54004eSDavid Woodhouse 1989c54004eSDavid Woodhouse //Enable IRQs from FPGA 1999c54004eSDavid Woodhouse iowrite32(1, card->config_regs + IRQ_EN_ADDR); 2009c54004eSDavid Woodhouse return IRQ_RETVAL(handled); 2019c54004eSDavid Woodhouse } 2029c54004eSDavid Woodhouse 2039c54004eSDavid Woodhouse void solos_bh(unsigned long card_arg) 2049c54004eSDavid Woodhouse { 2059c54004eSDavid Woodhouse struct solos_card *card = (void *)card_arg; 2069c54004eSDavid Woodhouse int port; 2079c54004eSDavid Woodhouse uint32_t card_flags; 2089c54004eSDavid Woodhouse uint32_t tx_mask; 2099c54004eSDavid Woodhouse uint32_t rx_done = 0; 2109c54004eSDavid Woodhouse 2119c54004eSDavid Woodhouse card_flags = ioread32(card->config_regs + FLAGS_ADDR); 2129c54004eSDavid Woodhouse 2139c54004eSDavid Woodhouse /* The TX bits are set if the channel is busy; clear if not. We want to 2149c54004eSDavid Woodhouse invoke fpga_tx() unless _all_ the bits for active channels are set */ 2159c54004eSDavid Woodhouse tx_mask = (1 << card->nr_ports) - 1; 2169c54004eSDavid Woodhouse if ((card_flags & tx_mask) != tx_mask) 2179c54004eSDavid Woodhouse fpga_tx(card); 2189c54004eSDavid Woodhouse 2199c54004eSDavid Woodhouse for (port = 0; port < card->nr_ports; port++) { 2209c54004eSDavid Woodhouse if (card_flags & (0x10 << port)) { 2219c54004eSDavid Woodhouse struct pkt_hdr header; 2229c54004eSDavid Woodhouse struct sk_buff *skb; 2239c54004eSDavid Woodhouse struct atm_vcc *vcc; 2249c54004eSDavid Woodhouse int size; 2259c54004eSDavid Woodhouse 2269c54004eSDavid Woodhouse rx_done |= 0x10 << port; 2279c54004eSDavid Woodhouse 2289c54004eSDavid Woodhouse memcpy_fromio(&header, RX_BUF(card, port), sizeof(header)); 2299c54004eSDavid Woodhouse 2309c54004eSDavid Woodhouse size = le16_to_cpu(header.size); 2319c54004eSDavid Woodhouse 2329c54004eSDavid Woodhouse skb = alloc_skb(size, GFP_ATOMIC); 2339c54004eSDavid Woodhouse if (!skb) { 2349c54004eSDavid Woodhouse if (net_ratelimit()) 2359c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff for RX\n"); 2369c54004eSDavid Woodhouse continue; 2379c54004eSDavid Woodhouse } 2389c54004eSDavid Woodhouse 2399c54004eSDavid Woodhouse memcpy_fromio(skb_put(skb, size), 2409c54004eSDavid Woodhouse RX_BUF(card, port) + sizeof(header), 2419c54004eSDavid Woodhouse size); 2429c54004eSDavid Woodhouse 2439c54004eSDavid Woodhouse if (atmdebug) { 2449c54004eSDavid Woodhouse dev_info(&card->dev->dev, "Received: device %d\n", port); 2459c54004eSDavid Woodhouse dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n", 2469c54004eSDavid Woodhouse size, le16_to_cpu(header.vpi), 2479c54004eSDavid Woodhouse le16_to_cpu(header.vci)); 2489c54004eSDavid Woodhouse print_buffer(skb); 2499c54004eSDavid Woodhouse } 2509c54004eSDavid Woodhouse 2519c54004eSDavid Woodhouse switch (le16_to_cpu(header.type)) { 2529c54004eSDavid Woodhouse case PKT_DATA: 2539c54004eSDavid Woodhouse vcc = find_vcc(card->atmdev[port], le16_to_cpu(header.vpi), 2549c54004eSDavid Woodhouse le16_to_cpu(header.vci)); 2559c54004eSDavid Woodhouse if (!vcc) { 2569c54004eSDavid Woodhouse if (net_ratelimit()) 2579c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Received packet for unknown VCI.VPI %d.%d on port %d\n", 2589c54004eSDavid Woodhouse le16_to_cpu(header.vci), le16_to_cpu(header.vpi), 2599c54004eSDavid Woodhouse port); 2609c54004eSDavid Woodhouse continue; 2619c54004eSDavid Woodhouse } 2629c54004eSDavid Woodhouse atm_charge(vcc, skb->truesize); 2639c54004eSDavid Woodhouse vcc->push(vcc, skb); 2649c54004eSDavid Woodhouse atomic_inc(&vcc->stats->rx); 2659c54004eSDavid Woodhouse break; 2669c54004eSDavid Woodhouse 2679c54004eSDavid Woodhouse case PKT_COMMAND: 2689c54004eSDavid Woodhouse default: /* FIXME: Not really, surely? */ 2699c54004eSDavid Woodhouse spin_lock(&card->cli_queue_lock); 2709c54004eSDavid Woodhouse if (skb_queue_len(&card->cli_queue[port]) > 10) { 2719c54004eSDavid Woodhouse if (net_ratelimit()) 2729c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Dropping console response on port %d\n", 2739c54004eSDavid Woodhouse port); 2749c54004eSDavid Woodhouse } else 2759c54004eSDavid Woodhouse skb_queue_tail(&card->cli_queue[port], skb); 2769c54004eSDavid Woodhouse spin_unlock(&card->cli_queue_lock); 2779c54004eSDavid Woodhouse break; 2789c54004eSDavid Woodhouse } 2799c54004eSDavid Woodhouse } 2809c54004eSDavid Woodhouse } 2819c54004eSDavid Woodhouse if (rx_done) 2829c54004eSDavid Woodhouse iowrite32(rx_done, card->config_regs + FLAGS_ADDR); 2839c54004eSDavid Woodhouse 2849c54004eSDavid Woodhouse return; 2859c54004eSDavid Woodhouse } 2869c54004eSDavid Woodhouse 2879c54004eSDavid Woodhouse static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci) 2889c54004eSDavid Woodhouse { 2899c54004eSDavid Woodhouse struct hlist_head *head; 2909c54004eSDavid Woodhouse struct atm_vcc *vcc = NULL; 2919c54004eSDavid Woodhouse struct hlist_node *node; 2929c54004eSDavid Woodhouse struct sock *s; 2939c54004eSDavid Woodhouse 2949c54004eSDavid Woodhouse read_lock(&vcc_sklist_lock); 2959c54004eSDavid Woodhouse head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; 2969c54004eSDavid Woodhouse sk_for_each(s, node, head) { 2979c54004eSDavid Woodhouse vcc = atm_sk(s); 2989c54004eSDavid Woodhouse if (vcc->dev == dev && vcc->vci == vci && 2999c54004eSDavid Woodhouse vcc->vpi == vpi && vcc->qos.rxtp.traffic_class != ATM_NONE) 3009c54004eSDavid Woodhouse goto out; 3019c54004eSDavid Woodhouse } 3029c54004eSDavid Woodhouse vcc = NULL; 3039c54004eSDavid Woodhouse out: 3049c54004eSDavid Woodhouse read_unlock(&vcc_sklist_lock); 3059c54004eSDavid Woodhouse return vcc; 3069c54004eSDavid Woodhouse } 3079c54004eSDavid Woodhouse 3089c54004eSDavid Woodhouse static int list_vccs(int vci) 3099c54004eSDavid Woodhouse { 3109c54004eSDavid Woodhouse struct hlist_head *head; 3119c54004eSDavid Woodhouse struct atm_vcc *vcc; 3129c54004eSDavid Woodhouse struct hlist_node *node; 3139c54004eSDavid Woodhouse struct sock *s; 3149c54004eSDavid Woodhouse int num_found = 0; 3159c54004eSDavid Woodhouse int i; 3169c54004eSDavid Woodhouse 3179c54004eSDavid Woodhouse read_lock(&vcc_sklist_lock); 3189c54004eSDavid Woodhouse if (vci != 0){ 3199c54004eSDavid Woodhouse head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; 3209c54004eSDavid Woodhouse sk_for_each(s, node, head) { 3219c54004eSDavid Woodhouse num_found ++; 3229c54004eSDavid Woodhouse vcc = atm_sk(s); 3239c54004eSDavid Woodhouse printk(KERN_DEBUG "Device: %d Vpi: %d Vci: %d\n", 3249c54004eSDavid Woodhouse vcc->dev->number, 3259c54004eSDavid Woodhouse vcc->vpi, 3269c54004eSDavid Woodhouse vcc->vci); 3279c54004eSDavid Woodhouse } 3289c54004eSDavid Woodhouse } else { 3299c54004eSDavid Woodhouse for(i=0; i<32; i++){ 3309c54004eSDavid Woodhouse head = &vcc_hash[i]; 3319c54004eSDavid Woodhouse sk_for_each(s, node, head) { 3329c54004eSDavid Woodhouse num_found ++; 3339c54004eSDavid Woodhouse vcc = atm_sk(s); 3349c54004eSDavid Woodhouse printk(KERN_DEBUG "Device: %d Vpi: %d Vci: %d\n", 3359c54004eSDavid Woodhouse vcc->dev->number, 3369c54004eSDavid Woodhouse vcc->vpi, 3379c54004eSDavid Woodhouse vcc->vci); 3389c54004eSDavid Woodhouse } 3399c54004eSDavid Woodhouse } 3409c54004eSDavid Woodhouse } 3419c54004eSDavid Woodhouse read_unlock(&vcc_sklist_lock); 3429c54004eSDavid Woodhouse return num_found; 3439c54004eSDavid Woodhouse } 3449c54004eSDavid Woodhouse 3459c54004eSDavid Woodhouse 3469c54004eSDavid Woodhouse static int popen(struct atm_vcc *vcc) 3479c54004eSDavid Woodhouse { 3489c54004eSDavid Woodhouse struct solos_card *card = vcc->dev->dev_data; 3499c54004eSDavid Woodhouse struct sk_buff *skb; 3509c54004eSDavid Woodhouse struct pkt_hdr *header; 3519c54004eSDavid Woodhouse 3529c54004eSDavid Woodhouse skb = alloc_skb(sizeof(*header), GFP_ATOMIC); 3539c54004eSDavid Woodhouse if (!skb && net_ratelimit()) { 3549c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in popen()\n"); 3559c54004eSDavid Woodhouse return -ENOMEM; 3569c54004eSDavid Woodhouse } 3579c54004eSDavid Woodhouse header = (void *)skb_put(skb, sizeof(*header)); 3589c54004eSDavid Woodhouse 359b76811afSDavid Woodhouse header->size = cpu_to_le16(0); 3609c54004eSDavid Woodhouse header->vpi = cpu_to_le16(vcc->vpi); 3619c54004eSDavid Woodhouse header->vci = cpu_to_le16(vcc->vci); 3629c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_POPEN); 3639c54004eSDavid Woodhouse 3649c54004eSDavid Woodhouse fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, NULL); 3659c54004eSDavid Woodhouse 3669c54004eSDavid Woodhouse // dev_dbg(&card->dev->dev, "Open for vpi %d and vci %d on interface %d\n", vcc->vpi, vcc->vci, SOLOS_CHAN(vcc->dev)); 3679c54004eSDavid Woodhouse set_bit(ATM_VF_ADDR, &vcc->flags); // accept the vpi / vci 3689c54004eSDavid Woodhouse set_bit(ATM_VF_READY, &vcc->flags); 3699c54004eSDavid Woodhouse list_vccs(0); 3709c54004eSDavid Woodhouse 3719c54004eSDavid Woodhouse if (!opens) 3729c54004eSDavid Woodhouse iowrite32(1, card->config_regs + IRQ_EN_ADDR); 3739c54004eSDavid Woodhouse 3749c54004eSDavid Woodhouse opens++; //count open PVCs 3759c54004eSDavid Woodhouse 3769c54004eSDavid Woodhouse return 0; 3779c54004eSDavid Woodhouse } 3789c54004eSDavid Woodhouse 3799c54004eSDavid Woodhouse static void pclose(struct atm_vcc *vcc) 3809c54004eSDavid Woodhouse { 3819c54004eSDavid Woodhouse struct solos_card *card = vcc->dev->dev_data; 3829c54004eSDavid Woodhouse struct sk_buff *skb; 3839c54004eSDavid Woodhouse struct pkt_hdr *header; 3849c54004eSDavid Woodhouse 3859c54004eSDavid Woodhouse skb = alloc_skb(sizeof(*header), GFP_ATOMIC); 3869c54004eSDavid Woodhouse if (!skb) { 3879c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Failed to allocate sk_buff in pclose()\n"); 3889c54004eSDavid Woodhouse return; 3899c54004eSDavid Woodhouse } 3909c54004eSDavid Woodhouse header = (void *)skb_put(skb, sizeof(*header)); 3919c54004eSDavid Woodhouse 392b76811afSDavid Woodhouse header->size = cpu_to_le16(0); 3939c54004eSDavid Woodhouse header->vpi = cpu_to_le16(vcc->vpi); 3949c54004eSDavid Woodhouse header->vci = cpu_to_le16(vcc->vci); 3959c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_PCLOSE); 3969c54004eSDavid Woodhouse 3979c54004eSDavid Woodhouse fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, NULL); 3989c54004eSDavid Woodhouse 3999c54004eSDavid Woodhouse // dev_dbg(&card->dev->dev, "Close for vpi %d and vci %d on interface %d\n", vcc->vpi, vcc->vci, SOLOS_CHAN(vcc->dev)); 4009c54004eSDavid Woodhouse if (!--opens) 4019c54004eSDavid Woodhouse iowrite32(0, card->config_regs + IRQ_EN_ADDR); 4029c54004eSDavid Woodhouse 4039c54004eSDavid Woodhouse clear_bit(ATM_VF_ADDR, &vcc->flags); 4049c54004eSDavid Woodhouse clear_bit(ATM_VF_READY, &vcc->flags); 4059c54004eSDavid Woodhouse 4069c54004eSDavid Woodhouse return; 4079c54004eSDavid Woodhouse } 4089c54004eSDavid Woodhouse 4099c54004eSDavid Woodhouse static int print_buffer(struct sk_buff *buf) 4109c54004eSDavid Woodhouse { 4119c54004eSDavid Woodhouse int len,i; 4129c54004eSDavid Woodhouse char msg[500]; 4139c54004eSDavid Woodhouse char item[10]; 4149c54004eSDavid Woodhouse 4159c54004eSDavid Woodhouse len = buf->len; 4169c54004eSDavid Woodhouse for (i = 0; i < len; i++){ 4179c54004eSDavid Woodhouse if(i % 8 == 0) 4189c54004eSDavid Woodhouse sprintf(msg, "%02X: ", i); 4199c54004eSDavid Woodhouse 4209c54004eSDavid Woodhouse sprintf(item,"%02X ",*(buf->data + i)); 4219c54004eSDavid Woodhouse strcat(msg, item); 4229c54004eSDavid Woodhouse if(i % 8 == 7) { 4239c54004eSDavid Woodhouse sprintf(item, "\n"); 4249c54004eSDavid Woodhouse strcat(msg, item); 4259c54004eSDavid Woodhouse printk(KERN_DEBUG "%s", msg); 4269c54004eSDavid Woodhouse } 4279c54004eSDavid Woodhouse } 4289c54004eSDavid Woodhouse if (i % 8 != 0) { 4299c54004eSDavid Woodhouse sprintf(item, "\n"); 4309c54004eSDavid Woodhouse strcat(msg, item); 4319c54004eSDavid Woodhouse printk(KERN_DEBUG "%s", msg); 4329c54004eSDavid Woodhouse } 4339c54004eSDavid Woodhouse printk(KERN_DEBUG "\n"); 4349c54004eSDavid Woodhouse 4359c54004eSDavid Woodhouse return 0; 4369c54004eSDavid Woodhouse } 4379c54004eSDavid Woodhouse 4389c54004eSDavid Woodhouse static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb, 4399c54004eSDavid Woodhouse struct atm_vcc *vcc) 4409c54004eSDavid Woodhouse { 4419c54004eSDavid Woodhouse int old_len; 4429c54004eSDavid Woodhouse 4439c54004eSDavid Woodhouse *(void **)skb->cb = vcc; 4449c54004eSDavid Woodhouse 4459c54004eSDavid Woodhouse spin_lock(&card->tx_queue_lock); 4469c54004eSDavid Woodhouse old_len = skb_queue_len(&card->tx_queue[port]); 4479c54004eSDavid Woodhouse skb_queue_tail(&card->tx_queue[port], skb); 4489c54004eSDavid Woodhouse spin_unlock(&card->tx_queue_lock); 4499c54004eSDavid Woodhouse 4509c54004eSDavid Woodhouse /* If TX might need to be started, do so */ 4519c54004eSDavid Woodhouse if (!old_len) 4529c54004eSDavid Woodhouse fpga_tx(card); 4539c54004eSDavid Woodhouse } 4549c54004eSDavid Woodhouse 4559c54004eSDavid Woodhouse static int fpga_tx(struct solos_card *card) 4569c54004eSDavid Woodhouse { 4579c54004eSDavid Woodhouse uint32_t tx_pending; 4589c54004eSDavid Woodhouse uint32_t tx_started = 0; 4599c54004eSDavid Woodhouse struct sk_buff *skb; 4609c54004eSDavid Woodhouse struct atm_vcc *vcc; 4619c54004eSDavid Woodhouse unsigned char port; 4629c54004eSDavid Woodhouse unsigned long flags; 4639c54004eSDavid Woodhouse 4649c54004eSDavid Woodhouse spin_lock_irqsave(&card->tx_lock, flags); 4659c54004eSDavid Woodhouse 4669c54004eSDavid Woodhouse tx_pending = ioread32(card->config_regs + FLAGS_ADDR); 4679c54004eSDavid Woodhouse 4689c54004eSDavid Woodhouse dev_vdbg(&card->dev->dev, "TX Flags are %X\n", tx_pending); 4699c54004eSDavid Woodhouse 4709c54004eSDavid Woodhouse for (port = 0; port < card->nr_ports; port++) { 4719c54004eSDavid Woodhouse if (!(tx_pending & (1 << port))) { 4729c54004eSDavid Woodhouse 4739c54004eSDavid Woodhouse spin_lock(&card->tx_queue_lock); 4749c54004eSDavid Woodhouse skb = skb_dequeue(&card->tx_queue[port]); 4759c54004eSDavid Woodhouse spin_unlock(&card->tx_queue_lock); 4769c54004eSDavid Woodhouse 4779c54004eSDavid Woodhouse if (!skb) 4789c54004eSDavid Woodhouse continue; 4799c54004eSDavid Woodhouse 4809c54004eSDavid Woodhouse if (atmdebug) { 4819c54004eSDavid Woodhouse dev_info(&card->dev->dev, "Transmitted: port %d\n", 4829c54004eSDavid Woodhouse port); 4839c54004eSDavid Woodhouse print_buffer(skb); 4849c54004eSDavid Woodhouse } 4859c54004eSDavid Woodhouse memcpy_toio(TX_BUF(card, port), skb->data, skb->len); 4869c54004eSDavid Woodhouse 4879c54004eSDavid Woodhouse vcc = *(void **)skb->cb; 4889c54004eSDavid Woodhouse 4899c54004eSDavid Woodhouse if (vcc) { 4909c54004eSDavid Woodhouse atomic_inc(&vcc->stats->tx); 4919c54004eSDavid Woodhouse solos_pop(vcc, skb); 4929c54004eSDavid Woodhouse } else 4939c54004eSDavid Woodhouse dev_kfree_skb_irq(skb); 4949c54004eSDavid Woodhouse 4959c54004eSDavid Woodhouse tx_started |= 1 << port; //Set TX full flag 4969c54004eSDavid Woodhouse } 4979c54004eSDavid Woodhouse } 4989c54004eSDavid Woodhouse if (tx_started) 4999c54004eSDavid Woodhouse iowrite32(tx_started, card->config_regs + FLAGS_ADDR); 5009c54004eSDavid Woodhouse 5019c54004eSDavid Woodhouse spin_unlock_irqrestore(&card->tx_lock, flags); 5029c54004eSDavid Woodhouse return 0; 5039c54004eSDavid Woodhouse } 5049c54004eSDavid Woodhouse 5059c54004eSDavid Woodhouse static int psend(struct atm_vcc *vcc, struct sk_buff *skb) 5069c54004eSDavid Woodhouse { 5079c54004eSDavid Woodhouse struct solos_card *card = vcc->dev->dev_data; 5089c54004eSDavid Woodhouse struct sk_buff *skb2 = NULL; 5099c54004eSDavid Woodhouse struct pkt_hdr *header; 510b76811afSDavid Woodhouse int pktlen; 5119c54004eSDavid Woodhouse 5129c54004eSDavid Woodhouse //dev_dbg(&card->dev->dev, "psend called.\n"); 5139c54004eSDavid Woodhouse //dev_dbg(&card->dev->dev, "dev,vpi,vci = %d,%d,%d\n",SOLOS_CHAN(vcc->dev),vcc->vpi,vcc->vci); 5149c54004eSDavid Woodhouse 5159c54004eSDavid Woodhouse if (debug) { 5169c54004eSDavid Woodhouse skb2 = atm_alloc_charge(vcc, skb->len, GFP_ATOMIC); 5179c54004eSDavid Woodhouse if (skb2) { 5189c54004eSDavid Woodhouse memcpy(skb2->data, skb->data, skb->len); 5199c54004eSDavid Woodhouse skb_put(skb2, skb->len); 5209c54004eSDavid Woodhouse vcc->push(vcc, skb2); 5219c54004eSDavid Woodhouse atomic_inc(&vcc->stats->rx); 5229c54004eSDavid Woodhouse } 5239c54004eSDavid Woodhouse atomic_inc(&vcc->stats->tx); 5249c54004eSDavid Woodhouse solos_pop(vcc, skb); 5259c54004eSDavid Woodhouse return 0; 5269c54004eSDavid Woodhouse } 5279c54004eSDavid Woodhouse 528b76811afSDavid Woodhouse pktlen = skb->len; 529b76811afSDavid Woodhouse if (pktlen > (BUF_SIZE - sizeof(*header))) { 5309c54004eSDavid Woodhouse dev_warn(&card->dev->dev, "Length of PDU is too large. Dropping PDU.\n"); 5319c54004eSDavid Woodhouse solos_pop(vcc, skb); 5329c54004eSDavid Woodhouse return 0; 5339c54004eSDavid Woodhouse } 5349c54004eSDavid Woodhouse 5359c54004eSDavid Woodhouse if (!skb_clone_writable(skb, sizeof(*header))) { 5369c54004eSDavid Woodhouse int expand_by = 0; 5379c54004eSDavid Woodhouse int ret; 5389c54004eSDavid Woodhouse 5399c54004eSDavid Woodhouse if (skb_headroom(skb) < sizeof(*header)) 5409c54004eSDavid Woodhouse expand_by = sizeof(*header) - skb_headroom(skb); 5419c54004eSDavid Woodhouse 5429c54004eSDavid Woodhouse ret = pskb_expand_head(skb, expand_by, 0, GFP_ATOMIC); 5439c54004eSDavid Woodhouse if (ret) { 5449c54004eSDavid Woodhouse solos_pop(vcc, skb); 5459c54004eSDavid Woodhouse return ret; 5469c54004eSDavid Woodhouse } 5479c54004eSDavid Woodhouse } 5489c54004eSDavid Woodhouse 5499c54004eSDavid Woodhouse header = (void *)skb_push(skb, sizeof(*header)); 5509c54004eSDavid Woodhouse 551b76811afSDavid Woodhouse /* This does _not_ include the size of the header */ 552b76811afSDavid Woodhouse header->size = cpu_to_le16(pktlen); 5539c54004eSDavid Woodhouse header->vpi = cpu_to_le16(vcc->vpi); 5549c54004eSDavid Woodhouse header->vci = cpu_to_le16(vcc->vci); 5559c54004eSDavid Woodhouse header->type = cpu_to_le16(PKT_DATA); 5569c54004eSDavid Woodhouse 5579c54004eSDavid Woodhouse fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, vcc); 5589c54004eSDavid Woodhouse 5599c54004eSDavid Woodhouse return 0; 5609c54004eSDavid Woodhouse } 5619c54004eSDavid Woodhouse 5629c54004eSDavid Woodhouse static struct atmdev_ops fpga_ops = { 5639c54004eSDavid Woodhouse .open = popen, 5649c54004eSDavid Woodhouse .close = pclose, 5659c54004eSDavid Woodhouse .ioctl = NULL, 5669c54004eSDavid Woodhouse .getsockopt = NULL, 5679c54004eSDavid Woodhouse .setsockopt = NULL, 5689c54004eSDavid Woodhouse .send = psend, 5699c54004eSDavid Woodhouse .send_oam = NULL, 5709c54004eSDavid Woodhouse .phy_put = NULL, 5719c54004eSDavid Woodhouse .phy_get = NULL, 5729c54004eSDavid Woodhouse .change_qos = NULL, 5739c54004eSDavid Woodhouse .proc_read = NULL, 5749c54004eSDavid Woodhouse .owner = THIS_MODULE 5759c54004eSDavid Woodhouse }; 5769c54004eSDavid Woodhouse 5779c54004eSDavid Woodhouse static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) 5789c54004eSDavid Woodhouse { 5799c54004eSDavid Woodhouse int err, i; 5809c54004eSDavid Woodhouse uint16_t fpga_ver; 5819c54004eSDavid Woodhouse uint8_t major_ver, minor_ver; 5829c54004eSDavid Woodhouse uint32_t data32; 5839c54004eSDavid Woodhouse struct solos_card *card; 5849c54004eSDavid Woodhouse 5859c54004eSDavid Woodhouse if (debug) 5869c54004eSDavid Woodhouse return 0; 5879c54004eSDavid Woodhouse 5889c54004eSDavid Woodhouse card = kzalloc(sizeof(*card), GFP_KERNEL); 5899c54004eSDavid Woodhouse if (!card) 5909c54004eSDavid Woodhouse return -ENOMEM; 5919c54004eSDavid Woodhouse 5929c54004eSDavid Woodhouse card->dev = dev; 5939c54004eSDavid Woodhouse 5949c54004eSDavid Woodhouse err = pci_enable_device(dev); 5959c54004eSDavid Woodhouse if (err) { 5969c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to enable PCI device\n"); 5979c54004eSDavid Woodhouse goto out; 5989c54004eSDavid Woodhouse } 5999c54004eSDavid Woodhouse 6009c54004eSDavid Woodhouse err = pci_request_regions(dev, "solos"); 6019c54004eSDavid Woodhouse if (err) { 6029c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to request regions\n"); 6039c54004eSDavid Woodhouse goto out; 6049c54004eSDavid Woodhouse } 6059c54004eSDavid Woodhouse 6069c54004eSDavid Woodhouse card->config_regs = pci_iomap(dev, 0, CONFIG_RAM_SIZE); 6079c54004eSDavid Woodhouse if (!card->config_regs) { 6089c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to ioremap config registers\n"); 6099c54004eSDavid Woodhouse goto out_release_regions; 6109c54004eSDavid Woodhouse } 6119c54004eSDavid Woodhouse card->buffers = pci_iomap(dev, 1, DATA_RAM_SIZE); 6129c54004eSDavid Woodhouse if (!card->buffers) { 6139c54004eSDavid Woodhouse dev_warn(&dev->dev, "Failed to ioremap data buffers\n"); 6149c54004eSDavid Woodhouse goto out_unmap_config; 6159c54004eSDavid Woodhouse } 6169c54004eSDavid Woodhouse 6179c54004eSDavid Woodhouse // for(i=0;i<64 ;i+=4){ 6189c54004eSDavid Woodhouse // data32=ioread32(card->buffers + i); 6199c54004eSDavid Woodhouse // dev_dbg(&card->dev->dev, "%08lX\n",(unsigned long)data32); 6209c54004eSDavid Woodhouse // } 6219c54004eSDavid Woodhouse 6229c54004eSDavid Woodhouse //Fill Config Mem with zeros 6239c54004eSDavid Woodhouse for(i = 0; i < 128; i += 4) 6249c54004eSDavid Woodhouse iowrite32(0, card->config_regs + i); 6259c54004eSDavid Woodhouse 6269c54004eSDavid Woodhouse //Set RX empty flags 6279c54004eSDavid Woodhouse iowrite32(0xF0, card->config_regs + FLAGS_ADDR); 6289c54004eSDavid Woodhouse 6299c54004eSDavid Woodhouse data32 = ioread32(card->config_regs + FPGA_VER); 6309c54004eSDavid Woodhouse fpga_ver = (data32 & 0x0000FFFF); 6319c54004eSDavid Woodhouse major_ver = ((data32 & 0xFF000000) >> 24); 6329c54004eSDavid Woodhouse minor_ver = ((data32 & 0x00FF0000) >> 16); 6339c54004eSDavid Woodhouse dev_info(&dev->dev, "Solos FPGA Version %d.%02d svn-%d\n", 6349c54004eSDavid Woodhouse major_ver, minor_ver, fpga_ver); 6359c54004eSDavid Woodhouse 6369c54004eSDavid Woodhouse card->nr_ports = 2; /* FIXME: Detect daughterboard */ 6379c54004eSDavid Woodhouse 6389c54004eSDavid Woodhouse err = atm_init(card); 6399c54004eSDavid Woodhouse if (err) 6409c54004eSDavid Woodhouse goto out_unmap_both; 6419c54004eSDavid Woodhouse 6429c54004eSDavid Woodhouse pci_set_drvdata(dev, card); 6439c54004eSDavid Woodhouse tasklet_init(&card->tlet, solos_bh, (unsigned long)card); 6449c54004eSDavid Woodhouse spin_lock_init(&card->tx_lock); 6459c54004eSDavid Woodhouse spin_lock_init(&card->tx_queue_lock); 6469c54004eSDavid Woodhouse spin_lock_init(&card->cli_queue_lock); 6479c54004eSDavid Woodhouse /* 6489c54004eSDavid Woodhouse // Set Loopback mode 6499c54004eSDavid Woodhouse data32 = 0x00010000; 6509c54004eSDavid Woodhouse iowrite32(data32,card->config_regs + FLAGS_ADDR); 6519c54004eSDavid Woodhouse */ 6529c54004eSDavid Woodhouse /* 6539c54004eSDavid Woodhouse // Fill Buffers with zeros 6549c54004eSDavid Woodhouse for (i = 0; i < BUF_SIZE * 8; i += 4) 6559c54004eSDavid Woodhouse iowrite32(0, card->buffers + i); 6569c54004eSDavid Woodhouse */ 6579c54004eSDavid Woodhouse /* 6589c54004eSDavid Woodhouse for(i = 0; i < (BUF_SIZE * 1); i += 4) 6599c54004eSDavid Woodhouse iowrite32(0x12345678, card->buffers + i + (0*BUF_SIZE)); 6609c54004eSDavid Woodhouse for(i = 0; i < (BUF_SIZE * 1); i += 4) 6619c54004eSDavid Woodhouse iowrite32(0xabcdef98, card->buffers + i + (1*BUF_SIZE)); 6629c54004eSDavid Woodhouse 6639c54004eSDavid Woodhouse // Read Config Memory 6649c54004eSDavid Woodhouse printk(KERN_DEBUG "Reading Config MEM\n"); 6659c54004eSDavid Woodhouse i = 0; 6669c54004eSDavid Woodhouse for(i = 0; i < 16; i++) { 6679c54004eSDavid Woodhouse data32=ioread32(card->buffers + i*(BUF_SIZE/2)); 6689c54004eSDavid Woodhouse printk(KERN_ALERT "Addr: %lX Data: %08lX\n", 6699c54004eSDavid Woodhouse (unsigned long)(addr_start + i*(BUF_SIZE/2)), 6709c54004eSDavid Woodhouse (unsigned long)data32); 6719c54004eSDavid Woodhouse } 6729c54004eSDavid Woodhouse */ 6739c54004eSDavid Woodhouse //dev_dbg(&card->dev->dev, "Requesting IRQ: %d\n",dev->irq); 6749c54004eSDavid Woodhouse err = request_irq(dev->irq, solos_irq, IRQF_DISABLED|IRQF_SHARED, 6759c54004eSDavid Woodhouse "solos-pci", card); 6769c54004eSDavid Woodhouse if (err) 6779c54004eSDavid Woodhouse dev_dbg(&card->dev->dev, "Failed to request interrupt IRQ: %d\n", dev->irq); 6789c54004eSDavid Woodhouse 6799c54004eSDavid Woodhouse // Enable IRQs 6809c54004eSDavid Woodhouse iowrite32(1, card->config_regs + IRQ_EN_ADDR); 6819c54004eSDavid Woodhouse 6829c54004eSDavid Woodhouse return 0; 6839c54004eSDavid Woodhouse 6849c54004eSDavid Woodhouse out_unmap_both: 6859c54004eSDavid Woodhouse pci_iounmap(dev, card->config_regs); 6869c54004eSDavid Woodhouse out_unmap_config: 6879c54004eSDavid Woodhouse pci_iounmap(dev, card->buffers); 6889c54004eSDavid Woodhouse out_release_regions: 6899c54004eSDavid Woodhouse pci_release_regions(dev); 6909c54004eSDavid Woodhouse out: 6919c54004eSDavid Woodhouse return err; 6929c54004eSDavid Woodhouse } 6939c54004eSDavid Woodhouse 6949c54004eSDavid Woodhouse static int atm_init(struct solos_card *card) 6959c54004eSDavid Woodhouse { 6969c54004eSDavid Woodhouse int i; 6979c54004eSDavid Woodhouse 6989c54004eSDavid Woodhouse opens = 0; 6999c54004eSDavid Woodhouse 7009c54004eSDavid Woodhouse for (i = 0; i < card->nr_ports; i++) { 7019c54004eSDavid Woodhouse skb_queue_head_init(&card->tx_queue[i]); 7029c54004eSDavid Woodhouse skb_queue_head_init(&card->cli_queue[i]); 7039c54004eSDavid Woodhouse 7049c54004eSDavid Woodhouse card->atmdev[i] = atm_dev_register("solos-pci", &fpga_ops, -1, NULL); 7059c54004eSDavid Woodhouse if (!card->atmdev[i]) { 7069c54004eSDavid Woodhouse dev_err(&card->dev->dev, "Could not register ATM device %d\n", i); 7079c54004eSDavid Woodhouse atm_remove(card); 7089c54004eSDavid Woodhouse return -ENODEV; 7099c54004eSDavid Woodhouse } 7109c54004eSDavid Woodhouse if (device_create_file(&card->atmdev[i]->class_dev, &dev_attr_console)) 7119c54004eSDavid Woodhouse dev_err(&card->dev->dev, "Could not register console for ATM device %d\n", i); 7129c54004eSDavid Woodhouse 7139c54004eSDavid Woodhouse dev_info(&card->dev->dev, "Registered ATM device %d\n", card->atmdev[i]->number); 7149c54004eSDavid Woodhouse 7159c54004eSDavid Woodhouse card->atmdev[i]->ci_range.vpi_bits = 8; 7169c54004eSDavid Woodhouse card->atmdev[i]->ci_range.vci_bits = 16; 7179c54004eSDavid Woodhouse card->atmdev[i]->dev_data = card; 7189c54004eSDavid Woodhouse card->atmdev[i]->phy_data = (void *)(unsigned long)i; 7199c54004eSDavid Woodhouse } 7209c54004eSDavid Woodhouse return 0; 7219c54004eSDavid Woodhouse } 7229c54004eSDavid Woodhouse 7239c54004eSDavid Woodhouse static void atm_remove(struct solos_card *card) 7249c54004eSDavid Woodhouse { 7259c54004eSDavid Woodhouse int i; 7269c54004eSDavid Woodhouse 7279c54004eSDavid Woodhouse for (i = 0; i < card->nr_ports; i++) { 7289c54004eSDavid Woodhouse if (card->atmdev[i]) { 7299c54004eSDavid Woodhouse dev_info(&card->dev->dev, "Unregistering ATM device %d\n", card->atmdev[i]->number); 7309c54004eSDavid Woodhouse atm_dev_deregister(card->atmdev[i]); 7319c54004eSDavid Woodhouse } 7329c54004eSDavid Woodhouse } 7339c54004eSDavid Woodhouse } 7349c54004eSDavid Woodhouse 7359c54004eSDavid Woodhouse static void fpga_remove(struct pci_dev *dev) 7369c54004eSDavid Woodhouse { 7379c54004eSDavid Woodhouse struct solos_card *card = pci_get_drvdata(dev); 7389c54004eSDavid Woodhouse 7399c54004eSDavid Woodhouse if (debug) 7409c54004eSDavid Woodhouse return; 7419c54004eSDavid Woodhouse 7429c54004eSDavid Woodhouse atm_remove(card); 7439c54004eSDavid Woodhouse 7449c54004eSDavid Woodhouse dev_vdbg(&dev->dev, "Freeing IRQ\n"); 7459c54004eSDavid Woodhouse // Disable IRQs from FPGA 7469c54004eSDavid Woodhouse iowrite32(0, card->config_regs + IRQ_EN_ADDR); 7479c54004eSDavid Woodhouse free_irq(dev->irq, card); 7489c54004eSDavid Woodhouse tasklet_kill(&card->tlet); 7499c54004eSDavid Woodhouse 7509c54004eSDavid Woodhouse // iowrite32(0x01,pciregs); 7519c54004eSDavid Woodhouse dev_vdbg(&dev->dev, "Unmapping PCI resource\n"); 7529c54004eSDavid Woodhouse pci_iounmap(dev, card->buffers); 7539c54004eSDavid Woodhouse pci_iounmap(dev, card->config_regs); 7549c54004eSDavid Woodhouse 7559c54004eSDavid Woodhouse dev_vdbg(&dev->dev, "Releasing PCI Region\n"); 7569c54004eSDavid Woodhouse pci_release_regions(dev); 7579c54004eSDavid Woodhouse pci_disable_device(dev); 7589c54004eSDavid Woodhouse 7599c54004eSDavid Woodhouse pci_set_drvdata(dev, NULL); 7609c54004eSDavid Woodhouse kfree(card); 7619c54004eSDavid Woodhouse // dev_dbg(&card->dev->dev, "fpga_remove\n"); 7629c54004eSDavid Woodhouse return; 7639c54004eSDavid Woodhouse } 7649c54004eSDavid Woodhouse 7659c54004eSDavid Woodhouse static struct pci_device_id fpga_pci_tbl[] __devinitdata = { 7669c54004eSDavid Woodhouse { 0x10ee, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 7679c54004eSDavid Woodhouse { 0, } 7689c54004eSDavid Woodhouse }; 7699c54004eSDavid Woodhouse 7709c54004eSDavid Woodhouse MODULE_DEVICE_TABLE(pci,fpga_pci_tbl); 7719c54004eSDavid Woodhouse 7729c54004eSDavid Woodhouse static struct pci_driver fpga_driver = { 7739c54004eSDavid Woodhouse .name = "solos", 7749c54004eSDavid Woodhouse .id_table = fpga_pci_tbl, 7759c54004eSDavid Woodhouse .probe = fpga_probe, 7769c54004eSDavid Woodhouse .remove = fpga_remove, 7779c54004eSDavid Woodhouse }; 7789c54004eSDavid Woodhouse 7799c54004eSDavid Woodhouse 7809c54004eSDavid Woodhouse static int __init solos_pci_init(void) 7819c54004eSDavid Woodhouse { 7829c54004eSDavid Woodhouse printk(KERN_INFO "Solos PCI Driver Version %s\n", VERSION); 7839c54004eSDavid Woodhouse return pci_register_driver(&fpga_driver); 7849c54004eSDavid Woodhouse } 7859c54004eSDavid Woodhouse 7869c54004eSDavid Woodhouse static void __exit solos_pci_exit(void) 7879c54004eSDavid Woodhouse { 7889c54004eSDavid Woodhouse pci_unregister_driver(&fpga_driver); 7899c54004eSDavid Woodhouse printk(KERN_INFO "Solos PCI Driver %s Unloaded\n", VERSION); 7909c54004eSDavid Woodhouse } 7919c54004eSDavid Woodhouse 7929c54004eSDavid Woodhouse module_init(solos_pci_init); 7939c54004eSDavid Woodhouse module_exit(solos_pci_exit); 794