xref: /openbmc/linux/drivers/atm/solos-pci.c (revision b76811af)
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