Release 4.11 drivers/atm/solos-pci.c
/*
* Driver for the Solos PCI ADSL2+ card, designed to support Linux by
* Traverse Technologies -- http://www.traverse.com.au/
* Xrio Limited -- http://www.xrio.com/
*
*
* Copyright © 2008 Traverse Technologies
* Copyright © 2008 Intel Corporation
*
* Authors: Nathan Williams <nathan@traverse.com.au>
* David Woodhouse <dwmw2@infradead.org>
* Treker Chen <treker@xrio.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define DEBUG
#define VERBOSE_DEBUG
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/skbuff.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/kobject.h>
#include <linux/firmware.h>
#include <linux/ctype.h>
#include <linux/swab.h>
#include <linux/slab.h>
#define VERSION "1.04"
#define DRIVER_VERSION 0x01
#define PTAG "solos-pci"
#define CONFIG_RAM_SIZE 128
#define FLAGS_ADDR 0x7C
#define IRQ_EN_ADDR 0x78
#define FPGA_VER 0x74
#define IRQ_CLEAR 0x70
#define WRITE_FLASH 0x6C
#define PORTS 0x68
#define FLASH_BLOCK 0x64
#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*/
/* 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))
#define FLASH_BUF ((card->buffers) + 4*(card->buffer_size)*2)
#define RX_DMA_SIZE 2048
#define FPGA_VERSION(a,b) (((a) << 8) + (b))
#define LEGACY_BUFFERS 2
#define DMA_SUPPORTED 4
static int reset = 0;
static int atmdebug = 0;
static int firmware_upgrade = 0;
static int fpga_upgrade = 0;
static int db_firmware_upgrade = 0;
static int db_fpga_upgrade = 0;
struct pkt_hdr {
__le16 size;
__le16 vpi;
__le16 vci;
__le16 type;
};
struct solos_skb_cb {
struct atm_vcc *vcc;
uint32_t dma_addr;
};
#define SKB_CB(skb) ((struct solos_skb_cb *)skb->cb)
#define PKT_DATA 0
#define PKT_COMMAND 1
#define PKT_POPEN 3
#define PKT_PCLOSE 4
#define PKT_STATUS 5
struct solos_card {
void __iomem *config_regs;
void __iomem *buffers;
int nr_ports;
int tx_mask;
struct pci_dev *dev;
struct atm_dev *atmdev[4];
struct tasklet_struct tlet;
spinlock_t tx_lock;
spinlock_t tx_queue_lock;
spinlock_t cli_queue_lock;
spinlock_t param_queue_lock;
struct list_head param_queue;
struct sk_buff_head tx_queue[4];
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;
};
struct solos_param {
struct list_head list;
pid_t pid;
int port;
struct sk_buff *response;
};
#define SOLOS_CHAN(atmdev) ((int)(unsigned long)(atmdev)->phy_data)
MODULE_AUTHOR("Traverse Technologies <support@traverse.com.au>");
MODULE_DESCRIPTION("Solos PCI driver");
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("solos-FPGA.bin");
MODULE_FIRMWARE("solos-Firmware.bin");
MODULE_FIRMWARE("solos-db-FPGA.bin");
MODULE_PARM_DESC(reset, "Reset Solos chips on startup");
MODULE_PARM_DESC(atmdebug, "Print ATM data");
MODULE_PARM_DESC(firmware_upgrade, "Initiate Solos firmware upgrade");
MODULE_PARM_DESC(fpga_upgrade, "Initiate FPGA upgrade");
MODULE_PARM_DESC(db_firmware_upgrade, "Initiate daughter board Solos firmware upgrade");
MODULE_PARM_DESC(db_fpga_upgrade, "Initiate daughter board FPGA upgrade");
module_param(reset, int, 0444);
module_param(atmdebug, int, 0644);
module_param(firmware_upgrade, int, 0444);
module_param(fpga_upgrade, int, 0444);
module_param(db_firmware_upgrade, int, 0444);
module_param(db_fpga_upgrade, int, 0444);
static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb,
struct atm_vcc *vcc);
static uint32_t fpga_tx(struct solos_card *);
static irqreturn_t solos_irq(int irq, void *dev_id);
static struct atm_vcc* find_vcc(struct atm_dev *dev, short vpi, int vci);
static int atm_init(struct solos_card *, struct device *);
static void atm_remove(struct solos_card *);
static int send_command(struct solos_card *card, int dev, const char *buf, size_t size);
static void solos_bh(unsigned long);
static int print_buffer(struct sk_buff *buf);
static inline void solos_pop(struct atm_vcc *vcc, struct sk_buff *skb)
{
if (vcc->pop)
vcc->pop(vcc, skb);
else
dev_kfree_skb_any(skb);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David Woodhouse | 38 | 100.00% | 1 | 100.00% |
Total | 38 | 100.00% | 1 | 100.00% |
static ssize_t solos_param_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev);
struct solos_card *card = atmdev->dev_data;
struct solos_param prm;
struct sk_buff *skb;
struct pkt_hdr *header;
int buflen;
buflen = strlen(attr->attr.name) + 10;
skb = alloc_skb(sizeof(*header) + buflen, GFP_KERNEL);
if (!skb) {
dev_warn(&card->dev->dev, "Failed to allocate sk_buff in solos_param_show()\n");
return -ENOMEM;
}
header = (void *)skb_put(skb, sizeof(*header));
buflen = snprintf((void *)&header[1], buflen - 1,
"L%05d\n%s\n", current->pid, attr->attr.name);
skb_put(skb, buflen);
header->size = cpu_to_le16(buflen);
header->vpi = cpu_to_le16(0);
header->vci = cpu_to_le16(0);
header->type = cpu_to_le16(PKT_COMMAND);
prm.pid = current->pid;
prm.response = NULL;
prm.port = SOLOS_CHAN(atmdev);
spin_lock_irq(&card->param_queue_lock);
list_add(&prm.list, &card->param_queue);
spin_unlock_irq(&card->param_queue_lock);
fpga_queue(card, prm.port, skb, NULL);
wait_event_timeout(card->param_wq, prm.response, 5 * HZ);
spin_lock_irq(&card->param_queue_lock);
list_del(&prm.list);
spin_unlock_irq(&card->param_queue_lock);
if (!prm.response)
return -EIO;
buflen = prm.response->len;
memcpy(buf, prm.response->data, buflen);
kfree_skb(prm.response);
return buflen;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David Woodhouse | 349 | 100.00% | 3 | 100.00% |
Total | 349 | 100.00% | 3 | 100.00% |
static ssize_t solos_param_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev);
struct solos_card *card = atmdev->dev_data;
struct solos_param prm;
struct sk_buff *skb;
struct pkt_hdr *header;
int buflen;
ssize_t ret;
buflen = strlen(attr->attr.name) + 11 + count;
skb = alloc_skb(sizeof(*header) + buflen, GFP_KERNEL);
if (!skb) {
dev_warn(&card->dev->dev, "Failed to allocate sk_buff in solos_param_store()\n");
return -ENOMEM;
}
header = (void *)skb_put(skb, sizeof(*header));
buflen = snprintf((void *)&header[1], buflen - 1,
"L%05d\n%s\n%s\n", current->pid, attr->attr.name, buf);
skb_put(skb, buflen);
header->size = cpu_to_le16(buflen);
header->vpi = cpu_to_le16(0);
header->vci = cpu_to_le16(0);
header->type = cpu_to_le16(PKT_COMMAND);
prm.pid = current->pid;
prm.response = NULL;
prm.port = SOLOS_CHAN(atmdev);
spin_lock_irq(&card->param_queue_lock);
list_add(&prm.list, &card->param_queue);
spin_unlock_irq(&card->param_queue_lock);
fpga_queue(card, prm.port, skb, NULL);
wait_event_timeout(card->param_wq, prm.response, 5 * HZ);
spin_lock_irq(&card->param_queue_lock);
list_del(&prm.list);
spin_unlock_irq(&card->param_queue_lock);
skb = prm.response;
if (!skb)
return -EIO;
buflen = skb->len;
/* Sometimes it has a newline, sometimes it doesn't. */
if (skb->data[buflen - 1] == '\n')
buflen--;
if (buflen == 2 && !strncmp(skb->data, "OK", 2))
ret = count;
else if (buflen == 5 && !strncmp(skb->data, "ERROR", 5))
ret = -EIO;
else {
/* We know we have enough space allocated for this; we allocated
it ourselves */
skb->data[buflen] = 0;
dev_warn(&card->dev->dev, "Unexpected parameter response: '%s'\n",
skb->data);
ret = -EIO;
}
kfree_skb(skb);
return ret;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David Woodhouse | 444 | 100.00% | 3 | 100.00% |
Total | 444 | 100.00% | 3 | 100.00% |
static char *next_string(struct sk_buff *skb)
{
int i = 0;
char *this = skb->data;
for (i = 0; i < skb->len; i++) {
if (this[i] == '\n') {
this[i] = 0;
skb_pull(skb, i + 1);
return this;
}
if (!isprint(this[i]))
return NULL;
}
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David Woodhouse | 89 | 100.00% | 2 | 100.00% |
Total | 89 | 100.00% | 2 | 100.00% |
/*
* Status packet has fields separated by \n, starting with a version number
* for the information therein. Fields are....
*
* packet version
* RxBitRate (version >= 1)
* TxBitRate (version >= 1)
* State (version >= 1)
* LocalSNRMargin (version >= 1)
* LocalLineAttn (version >= 1)
*/
static int process_status(struct solos_card *card, int port, struct sk_buff *skb)
{
char *str, *state_str, *snr, *attn;
int ver, rate_up, rate_down, err;
if (!card->atmdev[port])
return -ENODEV;
str = next_string(skb);
if (!str)
return -EIO;
err = kstrtoint(str, 10, &ver);
if (err) {
dev_warn(&card->dev->dev, "Unexpected status interrupt version\n");
return err;
}
if (ver < 1) {
dev_warn(&card->dev->dev, "Unexpected status interrupt version %d\n",
ver);
return -EIO;
}
str = next_string(skb);
if (!str)
return -EIO;
if (!strcmp(str, "ERROR")) {
dev_dbg(&card->dev->dev, "Status packet indicated Solos error on port %d (starting up?)\n",
port);
return 0;
}
err = kstrtoint(str, 10, &rate_down);
if (err)
return err;
str = next_string(skb);
if (!str)
return -EIO;
err = kstrtoint(str, 10, &rate_up);
if (err)
return err;
state_str = next_string(skb);
if (!state_str)
return -EIO;
/* Anything but 'Showtime' is down */
if (strcmp(state_str, "Showtime")) {
atm_dev_signal_change(card->atmdev[port], ATM_PHY_SIG_LOST);
dev_info(&card->dev->dev, "Port %d: %s\n", port, state_str);
return 0;
}
snr = next_string(skb);
if (!snr)
return -EIO;
attn = next_string(skb);
if (!attn)
return -EIO;
dev_info(&card->dev->dev, "Port %d: %s @%d/%d kb/s%s%s%s%s\n",
port, state_str, rate_down/1000, rate_up/1000,
snr[0]?", SNR ":"", snr, attn[0]?", Attn ":"", attn);
card->atmdev[port]->link_rate = rate_down / 424;
atm_dev_signal_change(card->atmdev[port], ATM_PHY_SIG_FOUND);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David Woodhouse | 343 | 87.06% | 6 | 66.67% |
Corentin Labbe | 42 | 10.66% | 1 | 11.11% |
Karl Hiramoto | 8 | 2.03% | 1 | 11.11% |
Julia Lawall | 1 | 0.25% | 1 | 11.11% |
Total | 394 | 100.00% | 9 | 100.00% |
static int process_command(struct solos_card *card, int port, struct sk_buff *skb)
{
struct solos_param *prm;
unsigned long flags;
int cmdpid;
int found = 0, err;
if (skb->len < 7)
return 0;
if (skb->data[0] != 'L' || !isdigit(skb->data[1]) ||
!isdigit(skb->data[2]) || !isdigit(skb->data[3]) ||
!isdigit(skb->data[4]) || !isdigit(skb->data[5]) ||
skb->data[6] != '\n')
return 0;
err = kstrtoint(&skb->data[1], 10, &cmdpid);
if (err)
return err;
spin_lock_irqsave(&card->param_queue_lock, flags);
list_for_each_entry(prm, &card->param_queue, list) {
if (prm->port == port && prm->pid == cmdpid) {
prm->response = skb;
skb_pull(skb, 7);
wake_up(&card->param_wq);
found = 1;
break;
}
}
spin_unlock_irqrestore(&card->param_queue_lock, flags);
return found;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David Woodhouse | 213 | 93.83% | 2 | 66.67% |
Corentin Labbe | 14 | 6.17% | 1 | 33.33% |
Total | 227 | 100.00% | 3 | 100.00% |
static ssize_t console_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev);
struct solos_card *card = atmdev->dev_data;
struct sk_buff *skb;
unsigned int len;
spin_lock(&card->cli_queue_lock);
skb = skb_dequeue(&card->cli_queue[SOLOS_CHAN(atmdev)]);
spin_unlock(&card->cli_queue_lock);
if(skb == NULL)
return sprintf(buf, "No data.\n");
len = skb->len;
memcpy(buf, skb->data, len);
kfree_skb(skb);
return len;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David Woodhouse | 114 | 91.94% | 1 | 50.00% |
Jiri Slaby | 10 | 8.06% | 1 | 50.00% |
Total | 124 | 100.00% | 2 | 100.00% |
static int send_command(struct solos_card *card, int dev, const char *buf, size_t size)
{
struct sk_buff *skb;
struct pkt_hdr *header;
if (size > (BUF_SIZE - sizeof(*header))) {
dev_dbg(&card->dev->dev, "Command is too big. Dropping request\n");
return 0;
}
skb = alloc_skb(size + sizeof(*header), GFP_ATOMIC);
if (!skb) {
dev_warn(&card->dev->dev, "Failed to allocate sk_buff in send_command()\n");
return 0;
}
header = (void *)skb_put(skb, sizeof(*header));
header->size = cpu_to_le16(size);
header->vpi = cpu_to_le16(0);
header->vci = cpu_to_le16(0);
header->type = cpu_to_le16(PKT_COMMAND);
memcpy(skb_put(skb, size), buf, size);
fpga_queue(card, dev, skb, NULL);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David Woodhouse | 145 | 80.11% | 1 | 50.00% |
Simon Farnsworth | 36 | 19.89% | 1 | 50.00% |
Total | 181 | 100.00% | 2 | 100.00% |
static ssize_t console_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev);
struct solos_card *card = atmdev->dev_data;
int err;
err = send_command(card, SOLOS_CHAN(atmdev), buf, count);
return err?:count;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Simon Farnsworth | 52 | 71.23% | 1 | 50.00% |
Nathan Williams | 21 | 28.77% | 1 | 50.00% |
Total | 73 | 100.00% | 2 | 100.00% |
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 = to_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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Nathan Williams | 185 | 98.93% | 1 | 33.33% |
Geliang Tang | 1 | 0.53% | 1 | 33.33% |
Dan Carpenter | 1 | 0.53% | 1 | 33.33% |
Total | 187 | 100.00% | 3 | 100.00% |
static ssize_t geos_gpio_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct pci_dev *pdev = to_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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Nathan Williams | 90 | 98.90% | 1 | 50.00% |
Geliang Tang | 1 | 1.10% | 1 | 50.00% |
Total | 91 | 100.00% | 2 | 100.00% |
static ssize_t hardware_show(struct device *dev, struct device_attribute