From patchwork Tue Dec 22 02:12:09 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mike Frysinger X-Patchwork-Id: 41576 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id C2C31B7B68 for ; Tue, 22 Dec 2009 13:11:49 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752472AbZLVCLW (ORCPT ); Mon, 21 Dec 2009 21:11:22 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1752363AbZLVCLU (ORCPT ); Mon, 21 Dec 2009 21:11:20 -0500 Received: from smtp.gentoo.org ([140.211.166.183]:49886 "EHLO smtp.gentoo.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752178AbZLVCLK (ORCPT ); Mon, 21 Dec 2009 21:11:10 -0500 Received: from localhost.localdomain (localhost [127.0.0.1]) by smtp.gentoo.org (Postfix) with ESMTP id 39F3E64146; Tue, 22 Dec 2009 02:11:09 +0000 (UTC) From: Mike Frysinger To: netdev@vger.kernel.org, "David S. Miller" Cc: uclinux-dist-devel@blackfin.uclinux.org, Michael Hennerich Subject: [PATCH] wireless: adf702x: new driver for ADF7020/21 parts Date: Mon, 21 Dec 2009 21:12:09 -0500 Message-Id: <1261447929-17106-1-git-send-email-vapier@gentoo.org> X-Mailer: git-send-email 1.6.5.4 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Michael Hennerich This is a driver for Analog Devices series of ADF702x Narrow-Band Short-Range Radio Transceiver chipsets, including the ADF7021 and the ADF7025. This Ethernet like driver implements a custom software PHY. Signed-off-by: Michael Hennerich Signed-off-by: Mike Frysinger --- drivers/net/wireless/Kconfig | 11 + drivers/net/wireless/Makefile | 2 + drivers/net/wireless/adf702x.c | 788 ++++++++++++++++++++++++++++++++++++++++ include/linux/spi/adf702x.h | 33 ++ 4 files changed, 834 insertions(+), 0 deletions(-) create mode 100644 drivers/net/wireless/adf702x.c create mode 100644 include/linux/spi/adf702x.h diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 56dd665..57974ac 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -44,6 +44,17 @@ config LIBERTAS_THINFIRM_USB ---help--- A driver for Marvell Libertas 8388 USB devices using thinfirm. +config ADF702X + tristate "ADF702x Narrow-Band Short-Range Radio Transceiver" + depends on EXPERIMENTAL && SPI && BLACKFIN + ---help--- + This is a driver for Analog Devices series of ADF702x Narrow-Band + Short-Range Radio Transceiver chipsets, including the ADF7021 and + the ADF7025. This Ethernet like driver implements a custom + software PHY. Say Y if you want it compiled into the kernel. + To compile this driver as a module, choose M here. The module will + be called adf702x. + config AIRO tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards" depends on ISA_DMA_API && (PCI || BROKEN) diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 5d4ce4d..7388a74 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -7,6 +7,8 @@ obj-$(CONFIG_IPW2200) += ipw2x00/ obj-$(CONFIG_HERMES) += orinoco/ +obj-$(CONFIG_ADF702X) += adf702x.o + obj-$(CONFIG_AIRO) += airo.o obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o diff --git a/drivers/net/wireless/adf702x.c b/drivers/net/wireless/adf702x.c new file mode 100644 index 0000000..ada0cb9 --- /dev/null +++ b/drivers/net/wireless/adf702x.c @@ -0,0 +1,788 @@ +/* + * ADF702x Narrow-Band Short-Range Radio Transceiver + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * DEBUG LEVEL + * 0 OFF + * 1 ERRORS + * 2 ERRORS + TRACE + * 3 ERRORS + TRACE + PACKET DUMP + */ + +#define ADF_DEBUG 0 +#define DBG(n, args...) do { if (ADF_DEBUG >= (n)) pr_debug(args); } while (0) + +struct adf702x_priv { + struct spi_device *spi; + struct net_device *ndev; + struct sk_buff *tx_skb; + struct delayed_work tx_work; + struct work_struct tx_done_work; + wait_queue_head_t waitq; + dma_addr_t dma_handle; + spinlock_t lock; + unsigned rx_preample:1; + unsigned rx:1; + unsigned rx_size; + u32 *rx_buf; + u32 *tx_buf; + + /* Base reg base of SPORT controller */ + volatile struct sport_register *sport; + unsigned dma_ch_rx; + unsigned dma_ch_tx; + unsigned irq_sport_err; + unsigned gpio_int_rfs; +}; + +static const u16 sym2chip[] = { + 0x744A, + 0x44AC, + 0x4AC3, + 0xAC39, + 0xC39B, + 0x39B7, + 0x9B74, + 0xB744, + 0xDEE2, + 0xEE26, + 0xE269, + 0x2693, + 0x6931, + 0x931D, + 0x31DE, + 0x1DEE, +}; + +#define MAGIC (0xA54662DA) +#define RX_HEADERSIZE 4 /* ?(PREAMPLE) + MAGIC + LEN_HI + LEN_LO */ +#define TX_HEADERSIZE 5 /* PREAMPLE + PREAMPLE + MAGIC + LEN_HI + LEN_LO */ +#define FIFO_WA 6 /* Transfer additional words to workaround DMA FIFO issues */ +#define BITERR 4 /* MAX Bit Errors Allowed */ +#define REG0_DELAY 40 /* PLL settling delay in us */ + +#define MAX_PACKET_SIZE (1550 * 4) + +/* + * ONES: A instruction only DSPs feature + * a XOR b -> return counted ONES (BIT Errors) + */ +static inline unsigned short adf702x_xor_ones(unsigned int a, unsigned int b) +{ + unsigned short val; + + __asm__ __volatile__ ( + "%0 = %1 ^ %2;" + "%0.l = ONES %0;" + : "=d"(val) : "d"(a), "d"(b) + ); + return val; +} + +/* + * Get 32-bit chip from 8-bit symbol + */ +static inline unsigned int adf702x_getchip(unsigned char sym) +{ + return (sym2chip[sym >> 4] << 16) | sym2chip[sym & 0xF]; +} + +/* + * Test packet MAGIC + */ +static inline int adf702x_testmagic(struct adf702x_priv *lp) +{ + if (adf702x_xor_ones(lp->rx_buf[1], MAGIC) < BITERR) + return 1; + + if (adf702x_xor_ones(lp->rx_buf[0], MAGIC) < BITERR) + return 0; + + return -1; +} + +/* + * Get 8-bit symbol from 32-bit chip + * Returns: symbol or -1 in case of an unrecoverable error + */ +static int adf702x_getsymbol(unsigned int chip) +{ + int symhi, symlo; + unsigned chiphi, chiplo; + + chiphi = chip >> 16; + chiplo = chip & 0xFFFF; + + for (symhi = 0; symhi < ARRAY_SIZE(sym2chip); symhi++) + if (adf702x_xor_ones(chiphi, sym2chip[symhi]) < BITERR) + break; + + if (symhi >= ARRAY_SIZE(sym2chip)) + return -1; + + for (symlo = 0; symlo < ARRAY_SIZE(sym2chip); symlo++) + if (adf702x_xor_ones(chiplo, sym2chip[symlo]) < BITERR) + break; + + if (symlo >= ARRAY_SIZE(sym2chip)) + return -1; + + return (symhi << 4) | symlo; +} + +/* + * Get Packet size from header + * Returns: size or 42 in case of an unrecoverable error + */ +inline unsigned short adf702x_getrxsize(struct adf702x_priv *lp, int offset) +{ + int size = adf702x_getsymbol(lp->rx_buf[offset + 1]) << 8 | + adf702x_getsymbol(lp->rx_buf[offset + 2]); + + if (size > 0) + return size; + + DBG(1, "%s :BITERR\n", __func__); + lp->ndev->stats.rx_errors++; + return 64; /* Keep the Receiver busy for some time */ +} + +static int adf702x_spi_write(struct spi_device *spi, unsigned int data) +{ + u16 msg[2]; + + msg[0] = data >> 16; + msg[1] = data; + + return spi_write(spi, (u8 *)msg, 4); +} + +static int adf702x_init(struct spi_device *spi) +{ + struct adf702x_platform_data *pdata = spi->dev.platform_data; + + switch (pdata->adf702x_model) { + case MODEL_ADF7025: + adf702x_spi_write(spi, pdata->adf702x_regs[2]); + adf702x_spi_write(spi, pdata->adf702x_regs[0]); + adf702x_spi_write(spi, pdata->adf702x_regs[1]); + udelay(1000); + adf702x_spi_write(spi, pdata->adf702x_regs[3]); + adf702x_spi_write(spi, pdata->adf702x_regs[4]); + adf702x_spi_write(spi, pdata->adf702x_regs[6]); + udelay(300); + adf702x_spi_write(spi, pdata->adf702x_regs[5]); + adf702x_spi_write(spi, pdata->adf702x_regs[9]); + break; + case MODEL_ADF7021: + adf702x_spi_write(spi, pdata->adf702x_regs[1]); + udelay(800); + adf702x_spi_write(spi, pdata->adf702x_regs[3]); + adf702x_spi_write(spi, pdata->adf702x_regs[6]); + adf702x_spi_write(spi, pdata->adf702x_regs[5]); + udelay(300); + adf702x_spi_write(spi, pdata->adf702x_regs[11]); + adf702x_spi_write(spi, pdata->adf702x_regs[12]); + adf702x_spi_write(spi, pdata->adf702x_regs[0]); + adf702x_spi_write(spi, pdata->adf702x_regs[4]); + adf702x_spi_write(spi, pdata->adf702x_regs[10]); + adf702x_spi_write(spi, pdata->adf702x_regs[2]); + break; + default: + dev_err(&spi->dev, "model not supported\n"); + return -ENODEV; + } + + return 0; +} + +static void adf702x_rx(struct spi_device *spi) +{ + struct adf702x_platform_data *pdata = spi->dev.platform_data; + adf702x_spi_write(spi, pdata->adf702x_regs[0]); + udelay(REG0_DELAY); + DBG(2, "%s():\n", __func__); +} + +static void adf702x_tx(struct spi_device *spi) +{ + struct adf702x_platform_data *pdata = spi->dev.platform_data; + adf702x_spi_write(spi, pdata->tx_reg); + udelay(REG0_DELAY); + DBG(2, "%s():\n", __func__); +} + +static void adf702x_sport_init(struct adf702x_priv *lp) +{ + struct adf702x_platform_data *pdata = lp->spi->dev.platform_data; + + lp->sport->tcr2 = DP_SLEN(32-1); + lp->sport->tcr1 = TCKFE | LATFS | DITFS | ITFS; + lp->sport->rcr2 = DP_SLEN(32-1); + lp->sport->rcr1 = RCKFE | LARFS; + + /* + * The ADF7025 requires SPORT TCLK generated externally + * it should be within 2% of CDR_CLK/32. + */ + if (pdata->adf702x_model == MODEL_ADF7025) { + lp->sport->tcr1 = TCKFE | LATFS | DITFS | ITFS | ITCLK; + lp->sport->tclkdiv = pdata->adf7025_tclkdiv; + } +} + +static void adf702x_setup_rx(struct adf702x_priv *lp) +{ + unsigned long flags; + + spin_lock_irqsave(&lp->lock, flags); + lp->rx_preample = 1; + lp->rx = 0; + set_dma_x_count(lp->dma_ch_rx, RX_HEADERSIZE); + set_dma_start_addr(lp->dma_ch_rx, (unsigned long)lp->rx_buf); + enable_dma(lp->dma_ch_rx); + lp->sport->rcr1 |= RSPEN; + spin_unlock_irqrestore(&lp->lock, flags); +} + +static void adf702x_tx_work(struct work_struct *work) +{ + struct adf702x_priv *lp = container_of(work, + struct adf702x_priv, tx_work.work); + + DBG(2, "%s: %s(): txDataCount(%d)\n", + lp->ndev->name, __func__, lp->tx_skb->len); + + DBG(2, "GPIO = %d rx = %d\n", gpio_get_value(lp->gpio_int_rfs), lp->rx); + + /* + * Do some media sense here + * Sleep while the media is busy + */ + + wait_event(lp->waitq, !(lp->rx || gpio_get_value(lp->gpio_int_rfs))); + + lp->sport->rcr1 &= ~RSPEN; + disable_dma(lp->dma_ch_rx); + clear_dma_irqstat(lp->dma_ch_rx); + + adf702x_tx(lp->spi); + + set_dma_x_count(lp->dma_ch_tx, lp->tx_skb->len + TX_HEADERSIZE + FIFO_WA); + set_dma_start_addr(lp->dma_ch_tx, (unsigned long)lp->tx_buf); + enable_dma(lp->dma_ch_tx); + + lp->sport->tcr1 |= TSPEN;; + + lp->ndev->stats.tx_packets++; + lp->ndev->stats.tx_bytes += lp->tx_skb->len; +} + +static void adf702x_tx_done_work(struct work_struct *work) +{ + struct adf702x_priv *lp = container_of(work, + struct adf702x_priv, tx_done_work); + + DBG(2, "%s: %s(): \n", lp->ndev->name, __func__); + + adf702x_setup_rx(lp); + adf702x_rx(lp->spi); + + if (lp->tx_skb) { + dev_kfree_skb(lp->tx_skb); + lp->tx_skb = NULL; + } + + netif_wake_queue(lp->ndev); +} + +static int adf702x_net_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct adf702x_priv *lp = netdev_priv(dev); + unsigned char *buf_ptr = skb->data; + int i; + unsigned char delay; + + DBG(2, "%s: %s(): transmitting %d bytes\n", + dev->name, __func__, skb->len); + + if (!skb->len) + return 0; + + /* Only one packet at a time. Once TXDONE interrupt is serviced, the + * queue will be restarted. + */ + netif_stop_queue(dev); + /* save the timestamp */ + lp->ndev->trans_start = jiffies; + /* Remember the skb for deferred processing */ + lp->tx_skb = skb; + + BUG_ON(lp->tx_skb->len > 0xFFFF); + + lp->tx_buf[3] = adf702x_getchip(skb->len >> 8); + lp->tx_buf[4] = adf702x_getchip(skb->len & 0xFF); + + DBG(3, "TX TX: "); + for (i = 0; i < skb->len; i++) { + lp->tx_buf[TX_HEADERSIZE + i] = adf702x_getchip(buf_ptr[i]); + DBG(3, "%x ", buf_ptr[i]); + } + DBG(3, "\n"); + + /* Avoid contentions + * Schedule transmission randomly (0..64ms) + * This allows other nodes to snip in + */ + + get_random_bytes(&delay, sizeof(delay)); + schedule_delayed_work(&lp->tx_work, msecs_to_jiffies(delay >> 2)); + + return 0; +} + +static int adf702x_receive(struct net_device *dev) +{ + struct sk_buff *skb; + struct adf702x_priv *lp = netdev_priv(dev); + int i, ret; + u8 *data; + + DBG(2, "%s: %s(): \n", lp->ndev->name, __func__); + + skb = dev_alloc_skb(lp->rx_size + NET_IP_ALIGN); + if (!skb) { + dev->stats.rx_dropped++; + return -ENOMEM; + } + + skb_reserve(skb, NET_IP_ALIGN); + + data = skb_put(skb, lp->rx_size); + + DBG(3, "RX RX: "); + for (i = 0; i < lp->rx_size; i++) { + data[i] = ret = adf702x_getsymbol(lp->rx_buf[i]); + if (ret < 0) { + dev->stats.rx_errors++; + dev_kfree_skb(skb); + DBG(1, "\nRX BITERR chip = %x\n", lp->rx_buf[i]); + return -EIO; + } + DBG(3, "%x ", data[i]); + } + DBG(3, "\n"); + + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_NONE; + + /* No MAC filtering + * we're always in Promiscuous Mode + */ + + netif_rx(skb); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += lp->rx_size; + + return 0; +} + +static irqreturn_t adf702x_sport_err_irq(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct adf702x_priv *lp = netdev_priv(dev); + unsigned int stat = lp->sport->stat; + + DBG(2, "%s: %s(): \n", lp->ndev->name, __func__); + /* Overflow in RX FIFO */ + if (stat & ROVF) + DBG(1, "%s: %s(): ROVF\n", lp->ndev->name, __func__); + + /* These should not happen */ + if (stat & (TOVF | TUVF | RUVF)) { + DBG(1, "SPORT Error:%s %s %s\n", + (stat & TOVF) ? "TX overflow" : "", + (stat & TUVF) ? "TX underflow" : "", + (stat & RUVF) ? "RX underflow" : ""); + } + + disable_dma(lp->dma_ch_rx); + clear_dma_irqstat(lp->dma_ch_rx); + + lp->sport->stat = ROVF | RUVF | TUVF | TOVF; /* Clear ROVF bit */ + lp->sport->rcr1 &= ~RSPEN; + + dev->stats.rx_over_errors++; + dev->stats.rx_errors++; + adf702x_setup_rx(lp); + wake_up(&lp->waitq); + + return IRQ_HANDLED; +} + +static irqreturn_t adf702x_tx_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct adf702x_priv *lp = netdev_priv(dev); + + DBG(2, "%s:%s(): got TXDone\n", + dev->name, __func__); + lp->sport->tcr1 &= ~TSPEN; + disable_dma(lp->dma_ch_tx); + clear_dma_irqstat(lp->dma_ch_tx); + schedule_work(&lp->tx_done_work); + + return IRQ_HANDLED; +} + +static irqreturn_t adf702x_rx_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct adf702x_priv *lp = netdev_priv(dev); + int offset; + disable_dma(lp->dma_ch_rx); + clear_dma_irqstat(lp->dma_ch_rx); + + if (lp->rx_preample) { + /* + * Keep the SPORT enabled + * The FIFO associated with the SPORT acts as buffer - + * while we setup the DMA for the remaining chips. + */ + + offset = adf702x_testmagic(lp); + if (offset >= 0) { + lp->rx_size = adf702x_getrxsize(lp, offset); + if (offset == 1) { + set_dma_x_count(lp->dma_ch_rx, lp->rx_size); + set_dma_start_addr(lp->dma_ch_rx, + (unsigned long)lp->rx_buf); + } else { + lp->rx_buf[0] = lp->rx_buf[3]; + set_dma_x_count(lp->dma_ch_rx, lp->rx_size - 1); + set_dma_start_addr(lp->dma_ch_rx, + (unsigned long)&lp->rx_buf[1]); + } + enable_dma(lp->dma_ch_rx); + SSYNC(); + lp->rx = 1; + lp->rx_preample = 0; + + DBG(2, "%s:%s(): got RX data %d\n", + dev->name, __func__, lp->rx_size); + + return IRQ_HANDLED; + + } else { + DBG(1, "%s:%s(): Failed MAGIC\n", + dev->name, __func__); + lp->sport->rcr1 &= ~RSPEN; + dev->stats.rx_dropped++; + dev->stats.rx_errors++; + } + } else { + lp->sport->rcr1 &= ~RSPEN; + adf702x_receive(dev); + } + + adf702x_setup_rx(lp); + wake_up(&lp->waitq); + + return IRQ_HANDLED; +} + +static int adf702x_net_open(struct net_device *dev) +{ + struct adf702x_priv *lp = netdev_priv(dev); + struct adf702x_platform_data *pdata = lp->spi->dev.platform_data; + unsigned int syncword; + + DBG(2, "%s: %s()\n", dev->name, __func__); + + adf702x_sport_init(lp); + + set_dma_config(lp->dma_ch_rx, + set_bfin_dma_config(DIR_WRITE, DMA_FLOW_STOP, + INTR_ON_BUF, DIMENSION_LINEAR, + DATA_SIZE_32, DMA_SYNC_RESTART)); + + set_dma_config(lp->dma_ch_tx, + set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP, + INTR_ON_BUF, DIMENSION_LINEAR, + DATA_SIZE_32, DMA_SYNC_RESTART)); + + set_dma_x_modify(lp->dma_ch_rx, 4); + set_dma_x_modify(lp->dma_ch_tx, 4); + + switch (pdata->adf702x_model) { + case MODEL_ADF7025: + syncword = 0xAA000000 | (pdata->adf702x_regs[5] >> 8); + break; + case MODEL_ADF7021: + syncword = 0xAA000000 | (pdata->adf702x_regs[11] >> 8); + break; + default: + return -ENODEV; + } + + lp->tx_buf[0] = syncword; + lp->tx_buf[1] = syncword; + lp->tx_buf[2] = MAGIC; + + adf702x_setup_rx(lp); + + dma_enable_irq(lp->dma_ch_rx); + adf702x_rx(lp->spi); + netif_start_queue(dev); + + return 0; +} + +static int adf702x_net_close(struct net_device *dev) +{ + struct adf702x_priv *lp = netdev_priv(dev); + DBG(2, "%s: %s()\n", dev->name, __func__); + + netif_stop_queue(dev); + dma_disable_irq(lp->dma_ch_rx); + + return 0; +} + +static const struct net_device_ops adf702x_netdev_ops = { + .ndo_open = adf702x_net_open, + .ndo_stop = adf702x_net_close, + .ndo_start_xmit = adf702x_net_xmit, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, +}; + +static int __devinit adf702x_probe(struct spi_device *spi) +{ + struct net_device *ndev; + struct adf702x_priv *lp; + struct adf702x_platform_data *pdata = spi->dev.platform_data; + int err; + + if (!pdata) { + dev_dbg(&spi->dev, "no platform data?\n"); + return -ENODEV; + } + + ndev = alloc_etherdev(sizeof(*lp)); + if (!ndev) { + err = -ENOMEM; + goto out; + } + + dev_set_drvdata(&spi->dev, ndev); + SET_NETDEV_DEV(ndev, &spi->dev); + + ndev->netdev_ops = &adf702x_netdev_ops; + + /* + * MAC address? we use a + * random one ... + */ + + random_ether_addr(ndev->dev_addr); + + ndev->mtu = 576; + ndev->tx_queue_len = 10; + ndev->watchdog_timeo = 0; + + err = register_netdev(ndev); + if (err) { + dev_err(&spi->dev, "failed to register netdev\n"); + goto out1; + } + + lp = netdev_priv(ndev); + lp->ndev = ndev; + lp->spi = spi; + + lp->sport = (struct sport_register *) pdata->regs_base; + lp->dma_ch_rx = pdata->dma_ch_rx; + lp->dma_ch_tx = pdata->dma_ch_tx; + lp->irq_sport_err = pdata->irq_sport_err; + lp->gpio_int_rfs = pdata->gpio_int_rfs; + + err = peripheral_request_list(pdata->pin_req, dev_name(&spi->dev)); + if (err) { + dev_err(&spi->dev, "failed to request SPORT\n"); + goto out2; + } + + err = request_dma(lp->dma_ch_rx, "SPORT RX Data"); + if (err) { + dev_err(&spi->dev, "failed to request RX dma %d\n", lp->dma_ch_rx); + goto out3; + } + + err = request_dma(lp->dma_ch_tx, "SPORT TX Data"); + if (err) { + dev_err(&spi->dev, "failed to request TX dma %d\n", lp->dma_ch_tx); + goto out4; + } + + err = set_dma_callback(lp->dma_ch_rx, adf702x_rx_interrupt, ndev); + if (err) { + dev_err(&spi->dev, "failed to request RX irq\n"); + goto out5; + } + + err = set_dma_callback(lp->dma_ch_tx, adf702x_tx_interrupt, ndev); + if (err) { + dev_err(&spi->dev, "failed to request TX irq\n"); + goto out5; + } + + dma_disable_irq(lp->dma_ch_rx); + + if (lp->irq_sport_err) { + err = request_irq(lp->irq_sport_err, adf702x_sport_err_irq, 0, + "ADF702x SPORT_ERROR", ndev); + if (err) { + dev_err(&spi->dev, "unable to request SPORT status interrupt\n"); + goto out5; + } + } + + lp->rx_buf = dma_alloc_coherent(NULL, MAX_PACKET_SIZE, + &lp->dma_handle, GFP_KERNEL); + + if (lp->rx_buf == NULL) { + err = -ENOMEM; + goto out6; + } + + lp->tx_buf = dma_alloc_coherent(NULL, MAX_PACKET_SIZE, + &lp->dma_handle, GFP_KERNEL); + + if (lp->rx_buf == NULL) { + err = -ENOMEM; + goto out7; + } + + /* + * This GPIO is connected to the ADF702X INT + * The ADF702X SWD feature starts the SPORT RX DMA (INT connected to SPORT RFS) + * While the initial transfer runs (Preample, MAGIC and Packet length), we sense + * this GPIO to see if there is an ongoing transfer. + */ + + err = gpio_request(lp->gpio_int_rfs, "ADF702X-INT"); + if (err) + goto out8; + + gpio_direction_input(lp->gpio_int_rfs); + + err = adf702x_init(lp->spi); + if (err) + goto out9; + + spin_lock_init(&lp->lock); + INIT_DELAYED_WORK(&lp->tx_work, adf702x_tx_work); + INIT_WORK(&lp->tx_done_work, adf702x_tx_done_work); + init_waitqueue_head(&lp->waitq); + + dev_info(&spi->dev, "ADF702XNet Wireless Ethernet driver"); + + return 0; + +out9: + gpio_free(lp->gpio_int_rfs); +out8: + dma_free_coherent(NULL, MAX_PACKET_SIZE, lp->tx_buf, lp->dma_handle); +out7: + dma_free_coherent(NULL, MAX_PACKET_SIZE, lp->rx_buf, lp->dma_handle); +out6: + if (lp->irq_sport_err) + free_irq(lp->irq_sport_err, ndev); +out5: + free_dma(lp->dma_ch_tx); +out4: + free_dma(lp->dma_ch_rx); +out3: + peripheral_free_list(pdata->pin_req); +out2: + unregister_netdev(ndev); +out1: + free_netdev(ndev); + dev_set_drvdata(&spi->dev, NULL); +out: + return err; +} + +static int __devexit adf702x_remove(struct spi_device *spi) +{ + struct adf702x_platform_data *pdata = spi->dev.platform_data; + struct net_device *dev = dev_get_drvdata(&spi->dev); + struct adf702x_priv *lp = netdev_priv(dev); + + dma_free_coherent(NULL, MAX_PACKET_SIZE, lp->rx_buf, lp->dma_handle); + dma_free_coherent(NULL, MAX_PACKET_SIZE, lp->tx_buf, lp->dma_handle); + + if (lp->irq_sport_err) + free_irq(lp->irq_sport_err, dev); + + gpio_free(lp->gpio_int_rfs); + free_dma(lp->dma_ch_rx); + free_dma(lp->dma_ch_tx); + peripheral_free_list(pdata->pin_req); + + unregister_netdev(dev); + free_netdev(dev); + dev_set_drvdata(&spi->dev, NULL); + return 0; +} + +static struct spi_driver adf702x_spi_driver = { + .driver = { + .name = "adf702x", + .owner = THIS_MODULE, + }, + .probe = adf702x_probe, + .remove = __devexit_p(adf702x_remove), +}; + +static int __init adf702x_init_module(void) +{ + return spi_register_driver(&adf702x_spi_driver);; +} + +static void __exit adf702x_exit_module(void) +{ + spi_unregister_driver(&adf702x_spi_driver); +} + +module_init(adf702x_init_module); +module_exit(adf702x_exit_module); +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("ADF702XNet Wireless Ethernet driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/spi/adf702x.h b/include/linux/spi/adf702x.h new file mode 100644 index 0000000..ea369f3 --- /dev/null +++ b/include/linux/spi/adf702x.h @@ -0,0 +1,33 @@ +/* + * include/linux/spi/adf702x.h + * + * Device characteristics are highly application specific + * and may vary between boards and models. The platform_data for the + * device's "struct device" holds this information. + * + * Copyright 2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __LINUX_SPI_ADF702X_H__ +#define __LINUX_SPI_ADF702X_H__ + +#define MODEL_ADF7021 7021 +#define MODEL_ADF7025 7025 + +struct adf702x_platform_data { + + /* Base reg base of SPORT controller */ + void __iomem *regs_base; + unsigned dma_ch_rx; + unsigned dma_ch_tx; + unsigned irq_sport_err; + unsigned gpio_int_rfs; + u16 pin_req[7]; + u32 adf702x_model; + const u32 *adf702x_regs; + u32 tx_reg; + u32 adf7025_tclkdiv; +}; +#endif