summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--target/linux/generic/patches-3.6/132-solos-dma.patch416
-rw-r--r--target/linux/generic/patches-3.7/132-solos-dma.patch416
2 files changed, 800 insertions, 32 deletions
diff --git a/target/linux/generic/patches-3.6/132-solos-dma.patch b/target/linux/generic/patches-3.6/132-solos-dma.patch
index 9e7eb82..c95ecbe 100644
--- a/target/linux/generic/patches-3.6/132-solos-dma.patch
+++ b/target/linux/generic/patches-3.6/132-solos-dma.patch
@@ -1,23 +1,320 @@
-From cae49ede00ec3d0cda290b03fee55b72b49efc11 Mon Sep 17 00:00:00 2001
-From: David Woodhouse <dwmw2@infradead.org>
-Date: Tue, 11 Dec 2012 14:57:14 +0000
-Subject: [PATCH] solos-pci: fix double-free of TX skb in DMA mode
-
-We weren't clearing card->tx_skb[port] when processing the TX done interrupt.
-If there wasn't another skb ready to transmit immediately, this led to a
-double-free because we'd free it *again* next time we did have a packet to
-send.
-
-Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-Cc: stable@kernel.org
-Signed-off-by: David S. Miller <davem@davemloft.net>
+commit 152a2a8b5e1d4cbe91a7c66f1028db15164a3766
+Author: David Woodhouse <David.Woodhouse@intel.com>
+Date: Wed Dec 19 11:01:21 2012 +0000
+
+ solos-pci: ensure all TX packets are aligned to 4 bytes
+
+ The FPGA can't handled unaligned DMA (yet). So copy into an aligned buffer,
+ if skb->data isn't suitably aligned.
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit 13af816469db3449c072afbae6c4c1bd9ccecccb
+Author: Nathan Williams <nathan@traverse.com.au>
+Date: Wed Dec 19 11:01:20 2012 +0000
+
+ solos-pci: add firmware upgrade support for new models
+
+ Signed-off-by: Nathan Williams <nathan@traverse.com.au>
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit 7fbdadb5e951e4f0c0fc991ff5f50295568786e6
+Author: Nathan Williams <nathan@traverse.com.au>
+Date: Wed Dec 19 11:01:19 2012 +0000
+
+ solos-pci: remove superfluous debug output
+
+ Signed-off-by: Nathan Williams <nathan@traverse.com.au>
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit f9baad02e7411d9f38d5ebe1a1cdcde4ceec100d
+Author: Nathan Williams <nathan@traverse.com.au>
+Date: Wed Dec 19 11:01:18 2012 +0000
+
+ solos-pci: add GPIO support for newer versions on Geos board
+
+ dwmw2: Tidy up a little, simpler matching on which GPIO is being accessed,
+ only register on newer boards, register under PCI device instead of
+ duplicating them under each ATM device.
+
+ Signed-off-by: Nathan Williams <nathan@traverse.com.au>
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit cae49ede00ec3d0cda290b03fee55b72b49efc11
+Author: David Woodhouse <dwmw2@infradead.org>
+Date: Tue Dec 11 14:57:14 2012 +0000
+
+ solos-pci: fix double-free of TX skb in DMA mode
+
+ We weren't clearing card->tx_skb[port] when processing the TX done interrupt.
+ If there wasn't another skb ready to transmit immediately, this led to a
+ double-free because we'd free it *again* next time we did have a packet to
+ send.
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Cc: stable@kernel.org
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+==
+There is a typo here so we do a double lock instead of an unlock.
+
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
---
- drivers/atm/solos-pci.c | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
+Only needed in linux-next. Introduced in f9baad02e7411d9 [14/17]
+solos-pci: add GPIO support for newer versions on Geos board
+
--- a/drivers/atm/solos-pci.c
+++ b/drivers/atm/solos-pci.c
-@@ -945,10 +945,11 @@ static uint32_t fpga_tx(struct solos_car
+@@ -42,7 +42,8 @@
+ #include <linux/swab.h>
+ #include <linux/slab.h>
+
+-#define VERSION "0.07"
++#define VERSION "1.04"
++#define DRIVER_VERSION 0x01
+ #define PTAG "solos-pci"
+
+ #define CONFIG_RAM_SIZE 128
+@@ -56,16 +57,21 @@
+ #define FLASH_BUSY 0x60
+ #define FPGA_MODE 0x5C
+ #define FLASH_MODE 0x58
++#define GPIO_STATUS 0x54
++#define DRIVER_VER 0x50
+ #define TX_DMA_ADDR(port) (0x40 + (4 * (port)))
+ #define RX_DMA_ADDR(port) (0x30 + (4 * (port)))
+
+ #define DATA_RAM_SIZE 32768
+ #define BUF_SIZE 2048
+ #define OLD_BUF_SIZE 4096 /* For FPGA versions <= 2*/
+-#define FPGA_PAGE 528 /* FPGA flash page size*/
+-#define SOLOS_PAGE 512 /* Solos flash page size*/
+-#define FPGA_BLOCK (FPGA_PAGE * 8) /* FPGA flash block size*/
+-#define SOLOS_BLOCK (SOLOS_PAGE * 8) /* Solos flash block size*/
++/* Old boards use ATMEL AD45DB161D flash */
++#define ATMEL_FPGA_PAGE 528 /* FPGA flash page size*/
++#define ATMEL_SOLOS_PAGE 512 /* Solos flash page size*/
++#define ATMEL_FPGA_BLOCK (ATMEL_FPGA_PAGE * 8) /* FPGA block size*/
++#define ATMEL_SOLOS_BLOCK (ATMEL_SOLOS_PAGE * 8) /* Solos block size*/
++/* Current boards use M25P/M25PE SPI flash */
++#define SPI_FLASH_BLOCK (256 * 64)
+
+ #define RX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2)
+ #define TX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2 + (card->buffer_size))
+@@ -123,11 +129,14 @@ struct solos_card {
+ struct sk_buff_head cli_queue[4];
+ struct sk_buff *tx_skb[4];
+ struct sk_buff *rx_skb[4];
++ unsigned char *dma_bounce;
+ wait_queue_head_t param_wq;
+ wait_queue_head_t fw_wq;
+ int using_dma;
++ int dma_alignment;
+ int fpga_version;
+ int buffer_size;
++ int atmel_flash;
+ };
+
+
+@@ -452,7 +461,6 @@ static ssize_t console_show(struct devic
+
+ len = skb->len;
+ memcpy(buf, skb->data, len);
+- dev_dbg(&card->dev->dev, "len: %d\n", len);
+
+ kfree_skb(skb);
+ return len;
+@@ -499,6 +507,78 @@ static ssize_t console_store(struct devi
+ return err?:count;
+ }
+
++struct geos_gpio_attr {
++ struct device_attribute attr;
++ int offset;
++};
++
++#define SOLOS_GPIO_ATTR(_name, _mode, _show, _store, _offset) \
++ struct geos_gpio_attr gpio_attr_##_name = { \
++ .attr = __ATTR(_name, _mode, _show, _store), \
++ .offset = _offset }
++
++static ssize_t geos_gpio_store(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
++ struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr);
++ struct solos_card *card = pci_get_drvdata(pdev);
++ uint32_t data32;
++
++ if (count != 1 && (count != 2 || buf[1] != '\n'))
++ return -EINVAL;
++
++ spin_lock_irq(&card->param_queue_lock);
++ data32 = ioread32(card->config_regs + GPIO_STATUS);
++ if (buf[0] == '1') {
++ data32 |= 1 << gattr->offset;
++ iowrite32(data32, card->config_regs + GPIO_STATUS);
++ } else if (buf[0] == '0') {
++ data32 &= ~(1 << gattr->offset);
++ iowrite32(data32, card->config_regs + GPIO_STATUS);
++ } else {
++ count = -EINVAL;
++ }
++ spin_unlock_irq(&card->param_queue_lock);
++ return count;
++}
++
++static ssize_t geos_gpio_show(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
++ struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr);
++ struct solos_card *card = pci_get_drvdata(pdev);
++ uint32_t data32;
++
++ data32 = ioread32(card->config_regs + GPIO_STATUS);
++ data32 = (data32 >> gattr->offset) & 1;
++
++ return sprintf(buf, "%d\n", data32);
++}
++
++static ssize_t hardware_show(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
++ struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr);
++ struct solos_card *card = pci_get_drvdata(pdev);
++ uint32_t data32;
++
++ data32 = ioread32(card->config_regs + GPIO_STATUS);
++ switch (gattr->offset) {
++ case 0:
++ /* HardwareVersion */
++ data32 = data32 & 0x1F;
++ break;
++ case 1:
++ /* HardwareVariant */
++ data32 = (data32 >> 5) & 0x0F;
++ break;
++ }
++ return sprintf(buf, "%d\n", data32);
++}
++
+ static DEVICE_ATTR(console, 0644, console_show, console_store);
+
+
+@@ -507,6 +587,14 @@ static DEVICE_ATTR(console, 0644, consol
+
+ #include "solos-attrlist.c"
+
++static SOLOS_GPIO_ATTR(GPIO1, 0644, geos_gpio_show, geos_gpio_store, 9);
++static SOLOS_GPIO_ATTR(GPIO2, 0644, geos_gpio_show, geos_gpio_store, 10);
++static SOLOS_GPIO_ATTR(GPIO3, 0644, geos_gpio_show, geos_gpio_store, 11);
++static SOLOS_GPIO_ATTR(GPIO4, 0644, geos_gpio_show, geos_gpio_store, 12);
++static SOLOS_GPIO_ATTR(GPIO5, 0644, geos_gpio_show, geos_gpio_store, 13);
++static SOLOS_GPIO_ATTR(PushButton, 0444, geos_gpio_show, NULL, 14);
++static SOLOS_GPIO_ATTR(HardwareVersion, 0444, hardware_show, NULL, 0);
++static SOLOS_GPIO_ATTR(HardwareVariant, 0444, hardware_show, NULL, 1);
+ #undef SOLOS_ATTR_RO
+ #undef SOLOS_ATTR_RW
+
+@@ -523,6 +611,23 @@ static struct attribute_group solos_attr
+ .name = "parameters",
+ };
+
++static struct attribute *gpio_attrs[] = {
++ &gpio_attr_GPIO1.attr.attr,
++ &gpio_attr_GPIO2.attr.attr,
++ &gpio_attr_GPIO3.attr.attr,
++ &gpio_attr_GPIO4.attr.attr,
++ &gpio_attr_GPIO5.attr.attr,
++ &gpio_attr_PushButton.attr.attr,
++ &gpio_attr_HardwareVersion.attr.attr,
++ &gpio_attr_HardwareVariant.attr.attr,
++ NULL
++};
++
++static struct attribute_group gpio_attr_group = {
++ .attrs = gpio_attrs,
++ .name = "gpio",
++};
++
+ static int flash_upgrade(struct solos_card *card, int chip)
+ {
+ const struct firmware *fw;
+@@ -534,16 +639,25 @@ static int flash_upgrade(struct solos_ca
+ switch (chip) {
+ case 0:
+ fw_name = "solos-FPGA.bin";
+- blocksize = FPGA_BLOCK;
++ if (card->atmel_flash)
++ blocksize = ATMEL_FPGA_BLOCK;
++ else
++ blocksize = SPI_FLASH_BLOCK;
+ break;
+ case 1:
+ fw_name = "solos-Firmware.bin";
+- blocksize = SOLOS_BLOCK;
++ if (card->atmel_flash)
++ blocksize = ATMEL_SOLOS_BLOCK;
++ else
++ blocksize = SPI_FLASH_BLOCK;
+ break;
+ case 2:
+ if (card->fpga_version > LEGACY_BUFFERS){
+ fw_name = "solos-db-FPGA.bin";
+- blocksize = FPGA_BLOCK;
++ if (card->atmel_flash)
++ blocksize = ATMEL_FPGA_BLOCK;
++ else
++ blocksize = SPI_FLASH_BLOCK;
+ } else {
+ dev_info(&card->dev->dev, "FPGA version doesn't support"
+ " daughter board upgrades\n");
+@@ -553,7 +667,10 @@ static int flash_upgrade(struct solos_ca
+ case 3:
+ if (card->fpga_version > LEGACY_BUFFERS){
+ fw_name = "solos-Firmware.bin";
+- blocksize = SOLOS_BLOCK;
++ if (card->atmel_flash)
++ blocksize = ATMEL_SOLOS_BLOCK;
++ else
++ blocksize = SPI_FLASH_BLOCK;
+ } else {
+ dev_info(&card->dev->dev, "FPGA version doesn't support"
+ " daughter board upgrades\n");
+@@ -569,6 +686,9 @@ static int flash_upgrade(struct solos_ca
+
+ dev_info(&card->dev->dev, "Flash upgrade starting\n");
+
++ /* New FPGAs require driver version before permitting flash upgrades */
++ iowrite32(DRIVER_VERSION, card->config_regs + DRIVER_VER);
++
+ numblocks = fw->size / blocksize;
+ dev_info(&card->dev->dev, "Firmware size: %zd\n", fw->size);
+ dev_info(&card->dev->dev, "Number of blocks: %d\n", numblocks);
+@@ -598,9 +718,13 @@ static int flash_upgrade(struct solos_ca
+ /* dev_info(&card->dev->dev, "Set FPGA Flash mode to Block Write\n"); */
+ iowrite32(((chip * 2) + 1), card->config_regs + FLASH_MODE);
+
+- /* Copy block to buffer, swapping each 16 bits */
++ /* Copy block to buffer, swapping each 16 bits for Atmel flash */
+ for(i = 0; i < blocksize; i += 4) {
+- uint32_t word = swahb32p((uint32_t *)(fw->data + offset + i));
++ uint32_t word;
++ if (card->atmel_flash)
++ word = swahb32p((uint32_t *)(fw->data + offset + i));
++ else
++ word = *(uint32_t *)(fw->data + offset + i);
+ if(card->fpga_version > LEGACY_BUFFERS)
+ iowrite32(word, FLASH_BUF + i);
+ else
+@@ -945,10 +1069,11 @@ static uint32_t fpga_tx(struct solos_car
for (port = 0; tx_pending; tx_pending >>= 1, port++) {
if (tx_pending & 1) {
struct sk_buff *oldskb = card->tx_skb[port];
@@ -31,3 +328,90 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
spin_lock(&card->tx_queue_lock);
skb = skb_dequeue(&card->tx_queue[port]);
if (!skb)
+@@ -960,7 +1085,12 @@ static uint32_t fpga_tx(struct solos_car
+ tx_started |= 1 << port;
+ oldskb = skb; /* We're done with this skb already */
+ } else if (skb && card->using_dma) {
+- SKB_CB(skb)->dma_addr = pci_map_single(card->dev, skb->data,
++ unsigned char *data = skb->data;
++ if ((unsigned long)data & card->dma_alignment) {
++ data = card->dma_bounce + (BUF_SIZE * port);
++ memcpy(data, skb->data, skb->len);
++ }
++ SKB_CB(skb)->dma_addr = pci_map_single(card->dev, data,
+ skb->len, PCI_DMA_TODEVICE);
+ card->tx_skb[port] = skb;
+ iowrite32(SKB_CB(skb)->dma_addr,
+@@ -1134,18 +1264,33 @@ static int fpga_probe(struct pci_dev *de
+ db_fpga_upgrade = db_firmware_upgrade = 0;
+ }
+
++ /* Stopped using Atmel flash after 0.03-38 */
++ if (fpga_ver < 39)
++ card->atmel_flash = 1;
++ else
++ card->atmel_flash = 0;
++
++ data32 = ioread32(card->config_regs + PORTS);
++ card->nr_ports = (data32 & 0x000000FF);
++
+ if (card->fpga_version >= DMA_SUPPORTED) {
+ pci_set_master(dev);
+ card->using_dma = 1;
++ if (1) { /* All known FPGA versions so far */
++ card->dma_alignment = 3;
++ card->dma_bounce = kmalloc(card->nr_ports * BUF_SIZE, GFP_KERNEL);
++ if (!card->dma_bounce) {
++ dev_warn(&card->dev->dev, "Failed to allocate DMA bounce buffers\n");
++ /* Fallback to MMIO doesn't work */
++ goto out_unmap_both;
++ }
++ }
+ } else {
+ card->using_dma = 0;
+ /* Set RX empty flag for all ports */
+ iowrite32(0xF0, card->config_regs + FLAGS_ADDR);
+ }
+
+- data32 = ioread32(card->config_regs + PORTS);
+- card->nr_ports = (data32 & 0x000000FF);
+-
+ pci_set_drvdata(dev, card);
+
+ tasklet_init(&card->tlet, solos_bh, (unsigned long)card);
+@@ -1180,6 +1325,10 @@ static int fpga_probe(struct pci_dev *de
+ if (err)
+ goto out_free_irq;
+
++ if (card->fpga_version >= DMA_SUPPORTED &&
++ sysfs_create_group(&card->dev->dev.kobj, &gpio_attr_group))
++ dev_err(&card->dev->dev, "Could not register parameter group for GPIOs\n");
++
+ return 0;
+
+ out_free_irq:
+@@ -1188,6 +1337,7 @@ static int fpga_probe(struct pci_dev *de
+ tasklet_kill(&card->tlet);
+
+ out_unmap_both:
++ kfree(card->dma_bounce);
+ pci_set_drvdata(dev, NULL);
+ pci_iounmap(dev, card->buffers);
+ out_unmap_config:
+@@ -1290,11 +1440,16 @@ static void fpga_remove(struct pci_dev *
+ iowrite32(1, card->config_regs + FPGA_MODE);
+ (void)ioread32(card->config_regs + FPGA_MODE);
+
++ if (card->fpga_version >= DMA_SUPPORTED)
++ sysfs_remove_group(&card->dev->dev.kobj, &gpio_attr_group);
++
+ atm_remove(card);
+
+ free_irq(dev->irq, card);
+ tasklet_kill(&card->tlet);
+
++ kfree(card->dma_bounce);
++
+ /* Release device from reset */
+ iowrite32(0, card->config_regs + FPGA_MODE);
+ (void)ioread32(card->config_regs + FPGA_MODE);
diff --git a/target/linux/generic/patches-3.7/132-solos-dma.patch b/target/linux/generic/patches-3.7/132-solos-dma.patch
index 9e7eb82..c95ecbe 100644
--- a/target/linux/generic/patches-3.7/132-solos-dma.patch
+++ b/target/linux/generic/patches-3.7/132-solos-dma.patch
@@ -1,23 +1,320 @@
-From cae49ede00ec3d0cda290b03fee55b72b49efc11 Mon Sep 17 00:00:00 2001
-From: David Woodhouse <dwmw2@infradead.org>
-Date: Tue, 11 Dec 2012 14:57:14 +0000
-Subject: [PATCH] solos-pci: fix double-free of TX skb in DMA mode
-
-We weren't clearing card->tx_skb[port] when processing the TX done interrupt.
-If there wasn't another skb ready to transmit immediately, this led to a
-double-free because we'd free it *again* next time we did have a packet to
-send.
-
-Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-Cc: stable@kernel.org
-Signed-off-by: David S. Miller <davem@davemloft.net>
+commit 152a2a8b5e1d4cbe91a7c66f1028db15164a3766
+Author: David Woodhouse <David.Woodhouse@intel.com>
+Date: Wed Dec 19 11:01:21 2012 +0000
+
+ solos-pci: ensure all TX packets are aligned to 4 bytes
+
+ The FPGA can't handled unaligned DMA (yet). So copy into an aligned buffer,
+ if skb->data isn't suitably aligned.
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit 13af816469db3449c072afbae6c4c1bd9ccecccb
+Author: Nathan Williams <nathan@traverse.com.au>
+Date: Wed Dec 19 11:01:20 2012 +0000
+
+ solos-pci: add firmware upgrade support for new models
+
+ Signed-off-by: Nathan Williams <nathan@traverse.com.au>
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit 7fbdadb5e951e4f0c0fc991ff5f50295568786e6
+Author: Nathan Williams <nathan@traverse.com.au>
+Date: Wed Dec 19 11:01:19 2012 +0000
+
+ solos-pci: remove superfluous debug output
+
+ Signed-off-by: Nathan Williams <nathan@traverse.com.au>
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit f9baad02e7411d9f38d5ebe1a1cdcde4ceec100d
+Author: Nathan Williams <nathan@traverse.com.au>
+Date: Wed Dec 19 11:01:18 2012 +0000
+
+ solos-pci: add GPIO support for newer versions on Geos board
+
+ dwmw2: Tidy up a little, simpler matching on which GPIO is being accessed,
+ only register on newer boards, register under PCI device instead of
+ duplicating them under each ATM device.
+
+ Signed-off-by: Nathan Williams <nathan@traverse.com.au>
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit cae49ede00ec3d0cda290b03fee55b72b49efc11
+Author: David Woodhouse <dwmw2@infradead.org>
+Date: Tue Dec 11 14:57:14 2012 +0000
+
+ solos-pci: fix double-free of TX skb in DMA mode
+
+ We weren't clearing card->tx_skb[port] when processing the TX done interrupt.
+ If there wasn't another skb ready to transmit immediately, this led to a
+ double-free because we'd free it *again* next time we did have a packet to
+ send.
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Cc: stable@kernel.org
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+==
+There is a typo here so we do a double lock instead of an unlock.
+
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
---
- drivers/atm/solos-pci.c | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
+Only needed in linux-next. Introduced in f9baad02e7411d9 [14/17]
+solos-pci: add GPIO support for newer versions on Geos board
+
--- a/drivers/atm/solos-pci.c
+++ b/drivers/atm/solos-pci.c
-@@ -945,10 +945,11 @@ static uint32_t fpga_tx(struct solos_car
+@@ -42,7 +42,8 @@
+ #include <linux/swab.h>
+ #include <linux/slab.h>
+
+-#define VERSION "0.07"
++#define VERSION "1.04"
++#define DRIVER_VERSION 0x01
+ #define PTAG "solos-pci"
+
+ #define CONFIG_RAM_SIZE 128
+@@ -56,16 +57,21 @@
+ #define FLASH_BUSY 0x60
+ #define FPGA_MODE 0x5C
+ #define FLASH_MODE 0x58
++#define GPIO_STATUS 0x54
++#define DRIVER_VER 0x50
+ #define TX_DMA_ADDR(port) (0x40 + (4 * (port)))
+ #define RX_DMA_ADDR(port) (0x30 + (4 * (port)))
+
+ #define DATA_RAM_SIZE 32768
+ #define BUF_SIZE 2048
+ #define OLD_BUF_SIZE 4096 /* For FPGA versions <= 2*/
+-#define FPGA_PAGE 528 /* FPGA flash page size*/
+-#define SOLOS_PAGE 512 /* Solos flash page size*/
+-#define FPGA_BLOCK (FPGA_PAGE * 8) /* FPGA flash block size*/
+-#define SOLOS_BLOCK (SOLOS_PAGE * 8) /* Solos flash block size*/
++/* Old boards use ATMEL AD45DB161D flash */
++#define ATMEL_FPGA_PAGE 528 /* FPGA flash page size*/
++#define ATMEL_SOLOS_PAGE 512 /* Solos flash page size*/
++#define ATMEL_FPGA_BLOCK (ATMEL_FPGA_PAGE * 8) /* FPGA block size*/
++#define ATMEL_SOLOS_BLOCK (ATMEL_SOLOS_PAGE * 8) /* Solos block size*/
++/* Current boards use M25P/M25PE SPI flash */
++#define SPI_FLASH_BLOCK (256 * 64)
+
+ #define RX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2)
+ #define TX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2 + (card->buffer_size))
+@@ -123,11 +129,14 @@ struct solos_card {
+ struct sk_buff_head cli_queue[4];
+ struct sk_buff *tx_skb[4];
+ struct sk_buff *rx_skb[4];
++ unsigned char *dma_bounce;
+ wait_queue_head_t param_wq;
+ wait_queue_head_t fw_wq;
+ int using_dma;
++ int dma_alignment;
+ int fpga_version;
+ int buffer_size;
++ int atmel_flash;
+ };
+
+
+@@ -452,7 +461,6 @@ static ssize_t console_show(struct devic
+
+ len = skb->len;
+ memcpy(buf, skb->data, len);
+- dev_dbg(&card->dev->dev, "len: %d\n", len);
+
+ kfree_skb(skb);
+ return len;
+@@ -499,6 +507,78 @@ static ssize_t console_store(struct devi
+ return err?:count;
+ }
+
++struct geos_gpio_attr {
++ struct device_attribute attr;
++ int offset;
++};
++
++#define SOLOS_GPIO_ATTR(_name, _mode, _show, _store, _offset) \
++ struct geos_gpio_attr gpio_attr_##_name = { \
++ .attr = __ATTR(_name, _mode, _show, _store), \
++ .offset = _offset }
++
++static ssize_t geos_gpio_store(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
++ struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr);
++ struct solos_card *card = pci_get_drvdata(pdev);
++ uint32_t data32;
++
++ if (count != 1 && (count != 2 || buf[1] != '\n'))
++ return -EINVAL;
++
++ spin_lock_irq(&card->param_queue_lock);
++ data32 = ioread32(card->config_regs + GPIO_STATUS);
++ if (buf[0] == '1') {
++ data32 |= 1 << gattr->offset;
++ iowrite32(data32, card->config_regs + GPIO_STATUS);
++ } else if (buf[0] == '0') {
++ data32 &= ~(1 << gattr->offset);
++ iowrite32(data32, card->config_regs + GPIO_STATUS);
++ } else {
++ count = -EINVAL;
++ }
++ spin_unlock_irq(&card->param_queue_lock);
++ return count;
++}
++
++static ssize_t geos_gpio_show(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
++ struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr);
++ struct solos_card *card = pci_get_drvdata(pdev);
++ uint32_t data32;
++
++ data32 = ioread32(card->config_regs + GPIO_STATUS);
++ data32 = (data32 >> gattr->offset) & 1;
++
++ return sprintf(buf, "%d\n", data32);
++}
++
++static ssize_t hardware_show(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
++ struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr);
++ struct solos_card *card = pci_get_drvdata(pdev);
++ uint32_t data32;
++
++ data32 = ioread32(card->config_regs + GPIO_STATUS);
++ switch (gattr->offset) {
++ case 0:
++ /* HardwareVersion */
++ data32 = data32 & 0x1F;
++ break;
++ case 1:
++ /* HardwareVariant */
++ data32 = (data32 >> 5) & 0x0F;
++ break;
++ }
++ return sprintf(buf, "%d\n", data32);
++}
++
+ static DEVICE_ATTR(console, 0644, console_show, console_store);
+
+
+@@ -507,6 +587,14 @@ static DEVICE_ATTR(console, 0644, consol
+
+ #include "solos-attrlist.c"
+
++static SOLOS_GPIO_ATTR(GPIO1, 0644, geos_gpio_show, geos_gpio_store, 9);
++static SOLOS_GPIO_ATTR(GPIO2, 0644, geos_gpio_show, geos_gpio_store, 10);
++static SOLOS_GPIO_ATTR(GPIO3, 0644, geos_gpio_show, geos_gpio_store, 11);
++static SOLOS_GPIO_ATTR(GPIO4, 0644, geos_gpio_show, geos_gpio_store, 12);
++static SOLOS_GPIO_ATTR(GPIO5, 0644, geos_gpio_show, geos_gpio_store, 13);
++static SOLOS_GPIO_ATTR(PushButton, 0444, geos_gpio_show, NULL, 14);
++static SOLOS_GPIO_ATTR(HardwareVersion, 0444, hardware_show, NULL, 0);
++static SOLOS_GPIO_ATTR(HardwareVariant, 0444, hardware_show, NULL, 1);
+ #undef SOLOS_ATTR_RO
+ #undef SOLOS_ATTR_RW
+
+@@ -523,6 +611,23 @@ static struct attribute_group solos_attr
+ .name = "parameters",
+ };
+
++static struct attribute *gpio_attrs[] = {
++ &gpio_attr_GPIO1.attr.attr,
++ &gpio_attr_GPIO2.attr.attr,
++ &gpio_attr_GPIO3.attr.attr,
++ &gpio_attr_GPIO4.attr.attr,
++ &gpio_attr_GPIO5.attr.attr,
++ &gpio_attr_PushButton.attr.attr,
++ &gpio_attr_HardwareVersion.attr.attr,
++ &gpio_attr_HardwareVariant.attr.attr,
++ NULL
++};
++
++static struct attribute_group gpio_attr_group = {
++ .attrs = gpio_attrs,
++ .name = "gpio",
++};
++
+ static int flash_upgrade(struct solos_card *card, int chip)
+ {
+ const struct firmware *fw;
+@@ -534,16 +639,25 @@ static int flash_upgrade(struct solos_ca
+ switch (chip) {
+ case 0:
+ fw_name = "solos-FPGA.bin";
+- blocksize = FPGA_BLOCK;
++ if (card->atmel_flash)
++ blocksize = ATMEL_FPGA_BLOCK;
++ else
++ blocksize = SPI_FLASH_BLOCK;
+ break;
+ case 1:
+ fw_name = "solos-Firmware.bin";
+- blocksize = SOLOS_BLOCK;
++ if (card->atmel_flash)
++ blocksize = ATMEL_SOLOS_BLOCK;
++ else
++ blocksize = SPI_FLASH_BLOCK;
+ break;
+ case 2:
+ if (card->fpga_version > LEGACY_BUFFERS){
+ fw_name = "solos-db-FPGA.bin";
+- blocksize = FPGA_BLOCK;
++ if (card->atmel_flash)
++ blocksize = ATMEL_FPGA_BLOCK;
++ else
++ blocksize = SPI_FLASH_BLOCK;
+ } else {
+ dev_info(&card->dev->dev, "FPGA version doesn't support"
+ " daughter board upgrades\n");
+@@ -553,7 +667,10 @@ static int flash_upgrade(struct solos_ca
+ case 3:
+ if (card->fpga_version > LEGACY_BUFFERS){
+ fw_name = "solos-Firmware.bin";
+- blocksize = SOLOS_BLOCK;
++ if (card->atmel_flash)
++ blocksize = ATMEL_SOLOS_BLOCK;
++ else
++ blocksize = SPI_FLASH_BLOCK;
+ } else {
+ dev_info(&card->dev->dev, "FPGA version doesn't support"
+ " daughter board upgrades\n");
+@@ -569,6 +686,9 @@ static int flash_upgrade(struct solos_ca
+
+ dev_info(&card->dev->dev, "Flash upgrade starting\n");
+
++ /* New FPGAs require driver version before permitting flash upgrades */
++ iowrite32(DRIVER_VERSION, card->config_regs + DRIVER_VER);
++
+ numblocks = fw->size / blocksize;
+ dev_info(&card->dev->dev, "Firmware size: %zd\n", fw->size);
+ dev_info(&card->dev->dev, "Number of blocks: %d\n", numblocks);
+@@ -598,9 +718,13 @@ static int flash_upgrade(struct solos_ca
+ /* dev_info(&card->dev->dev, "Set FPGA Flash mode to Block Write\n"); */
+ iowrite32(((chip * 2) + 1), card->config_regs + FLASH_MODE);
+
+- /* Copy block to buffer, swapping each 16 bits */
++ /* Copy block to buffer, swapping each 16 bits for Atmel flash */
+ for(i = 0; i < blocksize; i += 4) {
+- uint32_t word = swahb32p((uint32_t *)(fw->data + offset + i));
++ uint32_t word;
++ if (card->atmel_flash)
++ word = swahb32p((uint32_t *)(fw->data + offset + i));
++ else
++ word = *(uint32_t *)(fw->data + offset + i);
+ if(card->fpga_version > LEGACY_BUFFERS)
+ iowrite32(word, FLASH_BUF + i);
+ else
+@@ -945,10 +1069,11 @@ static uint32_t fpga_tx(struct solos_car
for (port = 0; tx_pending; tx_pending >>= 1, port++) {
if (tx_pending & 1) {
struct sk_buff *oldskb = card->tx_skb[port];
@@ -31,3 +328,90 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
spin_lock(&card->tx_queue_lock);
skb = skb_dequeue(&card->tx_queue[port]);
if (!skb)
+@@ -960,7 +1085,12 @@ static uint32_t fpga_tx(struct solos_car
+ tx_started |= 1 << port;
+ oldskb = skb; /* We're done with this skb already */
+ } else if (skb && card->using_dma) {
+- SKB_CB(skb)->dma_addr = pci_map_single(card->dev, skb->data,
++ unsigned char *data = skb->data;
++ if ((unsigned long)data & card->dma_alignment) {
++ data = card->dma_bounce + (BUF_SIZE * port);
++ memcpy(data, skb->data, skb->len);
++ }
++ SKB_CB(skb)->dma_addr = pci_map_single(card->dev, data,
+ skb->len, PCI_DMA_TODEVICE);
+ card->tx_skb[port] = skb;
+ iowrite32(SKB_CB(skb)->dma_addr,
+@@ -1134,18 +1264,33 @@ static int fpga_probe(struct pci_dev *de
+ db_fpga_upgrade = db_firmware_upgrade = 0;
+ }
+
++ /* Stopped using Atmel flash after 0.03-38 */
++ if (fpga_ver < 39)
++ card->atmel_flash = 1;
++ else
++ card->atmel_flash = 0;
++
++ data32 = ioread32(card->config_regs + PORTS);
++ card->nr_ports = (data32 & 0x000000FF);
++
+ if (card->fpga_version >= DMA_SUPPORTED) {
+ pci_set_master(dev);
+ card->using_dma = 1;
++ if (1) { /* All known FPGA versions so far */
++ card->dma_alignment = 3;
++ card->dma_bounce = kmalloc(card->nr_ports * BUF_SIZE, GFP_KERNEL);
++ if (!card->dma_bounce) {
++ dev_warn(&card->dev->dev, "Failed to allocate DMA bounce buffers\n");
++ /* Fallback to MMIO doesn't work */
++ goto out_unmap_both;
++ }
++ }
+ } else {
+ card->using_dma = 0;
+ /* Set RX empty flag for all ports */
+ iowrite32(0xF0, card->config_regs + FLAGS_ADDR);
+ }
+
+- data32 = ioread32(card->config_regs + PORTS);
+- card->nr_ports = (data32 & 0x000000FF);
+-
+ pci_set_drvdata(dev, card);
+
+ tasklet_init(&card->tlet, solos_bh, (unsigned long)card);
+@@ -1180,6 +1325,10 @@ static int fpga_probe(struct pci_dev *de
+ if (err)
+ goto out_free_irq;
+
++ if (card->fpga_version >= DMA_SUPPORTED &&
++ sysfs_create_group(&card->dev->dev.kobj, &gpio_attr_group))
++ dev_err(&card->dev->dev, "Could not register parameter group for GPIOs\n");
++
+ return 0;
+
+ out_free_irq:
+@@ -1188,6 +1337,7 @@ static int fpga_probe(struct pci_dev *de
+ tasklet_kill(&card->tlet);
+
+ out_unmap_both:
++ kfree(card->dma_bounce);
+ pci_set_drvdata(dev, NULL);
+ pci_iounmap(dev, card->buffers);
+ out_unmap_config:
+@@ -1290,11 +1440,16 @@ static void fpga_remove(struct pci_dev *
+ iowrite32(1, card->config_regs + FPGA_MODE);
+ (void)ioread32(card->config_regs + FPGA_MODE);
+
++ if (card->fpga_version >= DMA_SUPPORTED)
++ sysfs_remove_group(&card->dev->dev.kobj, &gpio_attr_group);
++
+ atm_remove(card);
+
+ free_irq(dev->irq, card);
+ tasklet_kill(&card->tlet);
+
++ kfree(card->dma_bounce);
++
+ /* Release device from reset */
+ iowrite32(0, card->config_regs + FPGA_MODE);
+ (void)ioread32(card->config_regs + FPGA_MODE);