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