diff mbox

wireless: adf702x: new driver for ADF7020/21 parts

Message ID 1261447929-17106-1-git-send-email-vapier@gentoo.org
State Not Applicable, archived
Delegated to: David Miller
Headers show

Commit Message

Mike Frysinger Dec. 22, 2009, 2:12 a.m. UTC
From: Michael Hennerich <michael.hennerich@analog.com>

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 <michael.hennerich@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 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

Comments

Marcel Holtmann Dec. 22, 2009, 3:05 a.m. UTC | #1
Hi Mike,

> 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.

while it might be a little bit confusing, but the wireless directory
normally only contains Wireless LAN (aka 802.11 and alike) drivers.

If this implements some sort of Ethernet driver, then it might be better
to just put it under drivers/net/ to not intermix and confuse it with
actually 802.11 drivers.

>  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

And why is the header file under SPI includes. It could be as easily in
the same directory as the driver *.c file itself. No reason to spread it
over the whole tree if nothing else includes it.

Regards

Marcel


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ben Hutchings Dec. 22, 2009, 3:44 a.m. UTC | #2
On Mon, 2009-12-21 at 21:12 -0500, Mike Frysinger wrote:
> From: Michael Hennerich <michael.hennerich@analog.com>
> 
> 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.
[...]
> 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

Any particular reason this should be EXPERIMENTAL?

[...]
> 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
[...]
> +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;

Shouldn't this be named "rx_preamble" ("b" not "p")?

> +	unsigned rx:1;
> +	unsigned rx_size;
> +	u32 *rx_buf;
> +	u32 *tx_buf;
> +
> +	/* Base reg base of SPORT controller */
> +	volatile struct sport_register *sport;

MMIO pointers should normally be declared like:

	struct sport_register __iomem *sport;

and used with readl, writel etc.

[...]
> +#define MAGIC		(0xA54662DA)

You could choose a more meaningful name for this!

[...]
> +/*
> + * 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;
> +}

This could be written as hweight16(a ^ b).  I don't think the hweight
functions have optimised implementations for Blackfin, but you can fix
that.

[...]
> +/*
> + * 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)

Should be declared static inline, not just inline.

> +{
> +	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 */

64 != 42

[...]
> +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;

I don't think this is possible.

> +	/* 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;

Don't set trans_start; the networking core does this now.

> +	/* Remember the skb for deferred processing */
> +	lp->tx_skb = skb;
> +
> +	BUG_ON(lp->tx_skb->len > 0xFFFF);

If you're going to make assertions about the length, it would probably
be more useful to check against the packet buffer size.

> +	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));

get_random_bytes() uses up entropy that should be reserved for
applications that really need it.  Instead of that, I think you should
call random32() and use the most significant bits.

[...]
> +static irqreturn_t adf702x_rx_interrupt(int irq, void *dev_id)
> +{
[...]
> +			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();

Is this some odd kind of memory barrier?

[...]
> +static int __devinit adf702x_probe(struct spi_device *spi)
> +{
[...]
> +	/*
> +	 * MAC address? we use a
> +	 * random one ...
> +	 */
> +
> +	random_ether_addr(ndev->dev_addr);

Assuming that the MAC address is not available in non-volatile memory or
set directly by firmware, it should be provided in platform data.

> +	ndev->mtu = 576;

Even though you use Ethernet frames?

> +	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;
> +	}

Don't register the netdev yet!  It could be brought up immediately, even
though your private structure is not initialised.

[...]
> +	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);

...and now you'll be ready to register the netdev.

> +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);

First things first:

	rtnl_lock();
	dev_close(dev);
	unregister_netdevice(dev);
	rtnl_unlock();

> +	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);
[...]

This unregister_netdev() should be replaced by the above additions.

Ben.
Hennerich, Michael Dec. 22, 2009, 10:38 a.m. UTC | #3
Hi Marcel,

>From: Marcel Holtmann

>

>Hi Mike,

>

>> 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.

>

>while it might be a little bit confusing, but the wireless directory

>normally only contains Wireless LAN (aka 802.11 and alike) drivers.

>

>If this implements some sort of Ethernet driver, then it might be better

>to just put it under drivers/net/ to not intermix and confuse it with

>actually 802.11 drivers.


No problem we can relocate 

>

>>  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

>

>And why is the header file under SPI includes. It could be as easily in

>the same directory as the driver *.c file itself. No reason to spread it

>over the whole tree if nothing else includes it.


It under include/linux/spi/ because it includes platform data which is used by board support files.

Best regards,
Michael

>

>Regards

>

>Marcel
Hennerich, Michael Dec. 22, 2009, 11:46 a.m. UTC | #4
Hi Ben,

Thanks for your feedback.
See comments below.

>From: Ben Hutchings

>On Mon, 2009-12-21 at 21:12 -0500, Mike Frysinger wrote:

>> From: Michael Hennerich <michael.hennerich@analog.com>

>>

>> 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.

>[...]

>> 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

>

>Any particular reason this should be EXPERIMENTAL?


No

>

>[...]

>> 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

>[...]

>> +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;

>

>Shouldn't this be named "rx_preamble" ("b" not "p")?


Right

>

>> +	unsigned rx:1;

>> +	unsigned rx_size;

>> +	u32 *rx_buf;

>> +	u32 *tx_buf;

>> +

>> +	/* Base reg base of SPORT controller */

>> +	volatile struct sport_register *sport;

>

>MMIO pointers should normally be declared like:

>

>	struct sport_register __iomem *sport;

>

>and used with readl, writel etc.


Well SPORT is a SoC peripheral it's mapped into Blackfin System MMR space.
Our versions of readX writeX are implemented for off-chip peripherals.
If you take a look at Blackfin io.h you will notice that we disable global interrupts, 
to avoid killed and reissued reads. This is not necessary for the System MMR space.   

Preferably I like to keep this as is, but I could also change it to use the 
Blackfin MMR accessor functions bfin_read{32,16} bfin_write{32,16}  

>

>[...]

>> +#define MAGIC		(0xA54662DA)

>

>You could choose a more meaningful name for this!


sure

>

>[...]

>> +/*

>> + * 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;

>> +}

>

>This could be written as hweight16(a ^ b).  I don't think the hweight

>functions have optimised implementations for Blackfin, but you can fix

>that.


This is what I was looking for - I wasn't aware of hweight{64,32,16,8}
Thanks!

>

>[...]

>> +/*

>> + * 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)

>

>Should be declared static inline, not just inline.


ok

>

>> +{

>> +	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 */

>

>64 != 42


I change the description

>

>[...]

>> +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;

>

>I don't think this is possible.


Will remove

>

>> +	/* 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;

>

>Don't set trans_start; the networking core does this now.


ok

>

>> +	/* Remember the skb for deferred processing */

>> +	lp->tx_skb = skb;

>> +

>> +	BUG_ON(lp->tx_skb->len > 0xFFFF);

>

>If you're going to make assertions about the length, it would probably

>be more useful to check against the packet buffer size.


ok

>

>> +	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));

>

>get_random_bytes() uses up entropy that should be reserved for

>applications that really need it.  Instead of that, I think you should

>call random32() and use the most significant bits.


ok

>

>[...]

>> +static irqreturn_t adf702x_rx_interrupt(int irq, void *dev_id)

>> +{

>[...]

>> +			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();

>

>Is this some odd kind of memory barrier?


Well - it's an instruction that ensures that the write buffers are flushed and all instructions executed. 

>

>[...]

>> +static int __devinit adf702x_probe(struct spi_device *spi)

>> +{

>[...]

>> +	/*

>> +	 * MAC address? we use a

>> +	 * random one ...

>> +	 */

>> +

>> +	random_ether_addr(ndev->dev_addr);

>

>Assuming that the MAC address is not available in non-volatile memory or

>set directly by firmware, it should be provided in platform data.

>

>> +	ndev->mtu = 576;

>

>Even though you use Ethernet frames?


We could stick with the default MTU - but it would worsen the overall response time.

>

>> +	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;

>> +	}

>

>Don't register the netdev yet!  It could be brought up immediately, even

>though your private structure is not initialised.


ups

>

>[...]

>> +	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);

>

>...and now you'll be ready to register the netdev.


ok

>

>> +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);

>

>First things first:

>

>	rtnl_lock();

>	dev_close(dev);

>	unregister_netdevice(dev);

>	rtnl_unlock();

>

>> +	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);

>[...]

>

>This unregister_netdev() should be replaced by the above additions.

>

>Ben.

>

>--

>Ben Hutchings, Senior Software Engineer, Solarflare Communications

>Not speaking for my employer; that's the marketing department's job.

>They asked us to note that Solarflare product names are trademarked.



Thanks and best regards,
Michael
Marcel Holtmann Dec. 22, 2009, 12:46 p.m. UTC | #5
Hi Michael,

> >> 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.
> >
> >while it might be a little bit confusing, but the wireless directory
> >normally only contains Wireless LAN (aka 802.11 and alike) drivers.
> >
> >If this implements some sort of Ethernet driver, then it might be better
> >to just put it under drivers/net/ to not intermix and confuse it with
> >actually 802.11 drivers.
> 
> No problem we can relocate 
> 
> >
> >>  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
> >
> >And why is the header file under SPI includes. It could be as easily in
> >the same directory as the driver *.c file itself. No reason to spread it
> >over the whole tree if nothing else includes it.
> 
> It under include/linux/spi/ because it includes platform data which is used by board support files.

that is no argument to move it there. It is a driver specific header and
so keep it in the same location as the driver itself.

Regards

Marcel


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Hennerich, Michael Dec. 22, 2009, 12:56 p.m. UTC | #6
Hi Marcel,

>From: Marcel Holtmann [mailto:marcel@holtmann.org]

>

>Hi Michael,

>

>> >> 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.

>> >

>> >while it might be a little bit confusing, but the wireless directory

>> >normally only contains Wireless LAN (aka 802.11 and alike) drivers.

>> >

>> >If this implements some sort of Ethernet driver, then it might be

>better

>> >to just put it under drivers/net/ to not intermix and confuse it with

>> >actually 802.11 drivers.

>>

>> No problem we can relocate

>>

>> >

>> >>  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

>> >

>> >And why is the header file under SPI includes. It could be as easily

>in

>> >the same directory as the driver *.c file itself. No reason to spread

>it

>> >over the whole tree if nothing else includes it.

>>

>> It under include/linux/spi/ because it includes platform data which is

>used by board support files.

>

>that is no argument to move it there. It is a driver specific header and

>so keep it in the same location as the driver itself.

>

>Regards

>

>Marcel

>


Most of the other include files under include/spi, include/i2c, include/mfd, include/input are there for exactly the same reason.
I consider it common practice to locate struct platform data in the includes and not in the drivers.

-Michael
Mike Frysinger Dec. 22, 2009, 1:09 p.m. UTC | #7
On Tue, Dec 22, 2009 at 07:46, Marcel Holtmann wrote:
>> >>  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
>> >
>> >And why is the header file under SPI includes. It could be as easily in
>> >the same directory as the driver *.c file itself. No reason to spread it
>> >over the whole tree if nothing else includes it.
>>
>> It under include/linux/spi/ because it includes platform data which is used by board support files.
>
> that is no argument to move it there. It is a driver specific header and
> so keep it in the same location as the driver itself.

that is incorrect.  for devices that need platform resources, the
structure is in a header in the include/ tree.  it cannot be in
drivers/net/ where boards are unable to include it.

whether it goes in linux/spi/ or linux/net/ or somewhere else we dont
really care.  linux/spi/ seems to be the best match so far.
-mike
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ben Hutchings Dec. 22, 2009, 4:02 p.m. UTC | #8
On Tue, 2009-12-22 at 11:46 +0000, Hennerich, Michael wrote:
[...]
> >
> >> +	unsigned rx:1;
> >> +	unsigned rx_size;
> >> +	u32 *rx_buf;
> >> +	u32 *tx_buf;
> >> +
> >> +	/* Base reg base of SPORT controller */
> >> +	volatile struct sport_register *sport;
> >
> >MMIO pointers should normally be declared like:
> >
> >	struct sport_register __iomem *sport;
> >
> >and used with readl, writel etc.
> 
> Well SPORT is a SoC peripheral it's mapped into Blackfin System MMR space.
> Our versions of readX writeX are implemented for off-chip peripherals.
> If you take a look at Blackfin io.h you will notice that we disable global interrupts, 
> to avoid killed and reissued reads. This is not necessary for the System MMR space.   
> 
> Preferably I like to keep this as is, but I could also change it to use the 
> Blackfin MMR accessor functions bfin_read{32,16} bfin_write{32,16}  

I don't know any specifics of the Blackfin architecture, so you should
probably do what you think best!

[...]
> >> +static irqreturn_t adf702x_rx_interrupt(int irq, void *dev_id)
> >> +{
> >[...]
> >> +			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();
> >
> >Is this some odd kind of memory barrier?
> 
> Well - it's an instruction that ensures that the write buffers are flushed and all instructions executed. 

I suspect you only need a write memory barrier here (wmb()) not a
pipeline flush.

[...]
> >> +	ndev->mtu = 576;
> >
> >Even though you use Ethernet frames?
> 
> We could stick with the default MTU - but it would worsen the overall response time.
[...]

Do what you think is best; I was just checking that you have a good
reason for this.

Ben,
diff mbox

Patch

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 <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/random.h>
+#include <linux/spinlock.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+#include <asm/bfin_sport.h>
+
+#include <linux/spi/adf702x.h>
+
+/*
+ * 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 <hennerich@blackfin.uclinux.org>");
+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