Netdev List
 help / color / mirror / Atom feed
* Re: RFC: New BPF 'LOOP' instruction
From: Paul LeoNerd Evans @ 2010-08-03 14:13 UTC (permalink / raw)
  To: Rémi Denis-Courmont, netdev
In-Reply-To: <72ff93eb4581b198faa6ce62d2b5162b@chewa.net>

[-- Attachment #1: Type: text/plain, Size: 1122 bytes --]

On Tue, Aug 03, 2010 at 04:09:53PM +0200, Rémi Denis-Courmont wrote:
> 
> On Tue, 3 Aug 2010 14:34:43 +0100, Paul LeoNerd Evans
> <leonerd@leonerd.org.uk> wrote:
> > What if we added a new constant SKF_TRANS_OFF to store the start address
> > of the transport header, and a new SKF_AD storage area for the transport
> > protocol:
> (...)
> > Does this sound workable?
> 
> The network header has not been processed by the time the skbuff hits the
> packet socket. So the transport header offset is not defined yet.

Is there any way it could be done lazily, at the moment that filter.c
knows it has to provide either SKF_TRANS_OFF or SKF_AD_TRANSPROTO? It
doesn't even need to do a full parse, just enough to find the length and
protocol type.

That processing -has- to be done one way or another, if BPF is ever to
support filtering on TCP headers in IPv6 in a sane way. Either encode
the algorithm in BPF, or in compiled C code - the latter would be more
performant.

-- 
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk
ICQ# 4135350       |  Registered Linux# 179460
http://www.leonerd.org.uk/

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

^ permalink raw reply

* Re: RFC: New BGF 'LOOP' instruction
From: Paul LeoNerd Evans @ 2010-08-03 14:11 UTC (permalink / raw)
  To: Andi Kleen, netdev; +Cc: David Miller
In-Reply-To: <20100803140539.GE31096@basil.fritz.box>

[-- Attachment #1: Type: text/plain, Size: 1706 bytes --]

On Tue, Aug 03, 2010 at 04:05:39PM +0200, Andi Kleen wrote:
> Well you could generalize it, like "SKIP headers where length 
> is at offset X and type at offset Y" 

Except that doesn't work for IPv6.

Some IPv6 headers are implied-length; their length never appears in the
packet. You have to "just know".

Some IPv6 headers store their length somewhere in the header body.
Different headers use different offsets within the body.

Some IPv6 headers do not make their length known on the wire -at all-,
such as IPsec's AH. Only the IPsec endpoints know how long this header
is.

This is what makes IPv6 -really- difficult to actually parse like this.

Ignoring even for a moment the impossible ones (IPsec's AH and ESP), the
rest of the headers end up becoming a giant lookup table, analogous to:

  switch(hdrtype)
  {
     case 1:
       length = someconst; break;
     case 2:
       length = someotherconst; break;
     case 3:
       length = b[someoffset]; break;
     ...
  }


This is why I wanted a LOOP instruction, the above switch code could
then be written -once- in BPF and LOOP'ed over to find the required
header. Instead, that loop must be statically unrolled some number of
times into an n-times-longer program script of less-than-equivalent
power. E.g. tcpdump/libpcap unrolls it a statically-configured 6 times,
meaning if the packet is particularly large, and the header comes 7th,
you'll never see it. My LOOP idea would mean the code is run once for
every header in the packet, regardless how many there were.

-- 
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk
ICQ# 4135350       |  Registered Linux# 179460
http://www.leonerd.org.uk/

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

^ permalink raw reply

* Re: RFC: New BPF 'LOOP' instruction
From: Rémi Denis-Courmont @ 2010-08-03 14:09 UTC (permalink / raw)
  To: Paul LeoNerd Evans; +Cc: netdev
In-Reply-To: <20100803133442.GQ11110@cel.leo>


On Tue, 3 Aug 2010 14:34:43 +0100, Paul LeoNerd Evans
<leonerd@leonerd.org.uk> wrote:
> What if we added a new constant SKF_TRANS_OFF to store the start address
> of the transport header, and a new SKF_AD storage area for the transport
> protocol:
(...)
> Does this sound workable?

The network header has not been processed by the time the skbuff hits the
packet socket. So the transport header offset is not defined yet.

-- 
Rémi Denis-Courmont
http://www.remlab.net
http://fi.linkedin.com/in/remidenis


^ permalink raw reply

* Re: [PATCH 2/8] davinci: add mdio platform devices
From: Igor Grinberg @ 2010-08-03 14:06 UTC (permalink / raw)
  To: Cyril Chemparathy
  Cc: netdev, davinci-linux-open-source, linux-omap, nsekhar, srk
In-Reply-To: <1280842313-3743-3-git-send-email-cyril@ti.com>

On 08/03/10 16:31, Cyril Chemparathy wrote:
> This patch adds mdio platform devices on SoCs that have the necessary
> hardware.  Clock lookup entries (aliases) have also been added, so that the
> MDIO and EMAC drivers can independently enable/disable a shared underlying
> clock.  Further, the EMAC MMR region has been split down into separate MDIO
> and EMAC regions.
>
> Signed-off-by: Cyril Chemparathy <cyril@ti.com>
> ---
>  arch/arm/mach-davinci/devices-da8xx.c       |   29 +++++++++++++++++++++++++-
>  arch/arm/mach-davinci/dm365.c               |   22 +++++++++++++++++++-
>  arch/arm/mach-davinci/dm644x.c              |   22 +++++++++++++++++++-
>  arch/arm/mach-davinci/dm646x.c              |   22 +++++++++++++++++++-
>  arch/arm/mach-davinci/include/mach/dm365.h  |    1 +
>  arch/arm/mach-davinci/include/mach/dm644x.h |    1 +
>  arch/arm/mach-davinci/include/mach/dm646x.h |    1 +
>  7 files changed, 93 insertions(+), 5 deletions(-)
>   

Shouldn't arch/arm/mach-omap2/board-am3517evm.c be on your list?
I think it should, or else it will be broken by:
[PATCH 5/8] netdev: cleanup unused davinci mdio emac code

-- 
Regards,
Igor.


^ permalink raw reply

* Re: RFC: New BGF 'LOOP' instruction
From: Andi Kleen @ 2010-08-03 14:05 UTC (permalink / raw)
  To: David Miller; +Cc: andi, leonerd, netdev
In-Reply-To: <20100803.060754.26959298.davem@davemloft.net>

On Tue, Aug 03, 2010 at 06:07:54AM -0700, David Miller wrote:
> From: Andi Kleen <andi@firstfloor.org>
> Date: Tue, 03 Aug 2010 14:58:02 +0200
> 
> > David Miller <davem@davemloft.net> writes:
> >>
> >> That makes the looping construct largely useless, which I mentioned in
> >> my second reply to this thread.
> > 
> > How about simply adding a "skip ipv6 extension headers until header type
> > X" opcode?
> > 
> > I bet that would solve most of the problems here in practice.
> 
> BPF really should not have protocol specific opcodes.

Well you could generalize it, like "SKIP headers where length 
is at offset X and type at offset Y" 

-Andi

-- 
ak@linux.intel.com -- Speaking for myself only.

^ permalink raw reply

* Re: RFC: New BPF 'LOOP' instruction
From: Paul LeoNerd Evans @ 2010-08-03 13:42 UTC (permalink / raw)
  To: netdev; +Cc: andi
In-Reply-To: <20100803133442.GQ11110@cel.leo>

[-- Attachment #1: Type: text/plain, Size: 467 bytes --]

On Tue, Aug 03, 2010 at 02:34:43PM +0100, Paul LeoNerd Evans wrote:
>     LD BYTE[SKF_NET_OFF+0]
>     JEQ 80, #accept, 0
>     LD BYTE[SKF_NET_OFF+2]
>     JEQ 80, 0, #reject

My apologies; theabove should be "LD HALF" instructions; TCP ports are
16 bit quantities. Hopefully this mistake does not detract from my
argument...

-- 
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk
ICQ# 4135350       |  Registered Linux# 179460
http://www.leonerd.org.uk/

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

^ permalink raw reply

* Re: RFC: New BGF 'LOOP' instruction
From: Paul LeoNerd Evans @ 2010-08-03 13:40 UTC (permalink / raw)
  To: Hagen Paul Pfeifer, netdev; +Cc: David Miller
In-Reply-To: <6809423e656a160df11216ea5acc3d8b@localhost>

[-- Attachment #1: Type: text/plain, Size: 1812 bytes --]

On Tue, Aug 03, 2010 at 11:10:28AM +0200, Hagen Paul Pfeifer wrote:
> >> Rightnow, BPF is all but useless for parsing, say, IPv6. I only pick
> >> IPv6 as one example, I'm sure there must exist a great number more
> >> packet-based protocols that use a "linked-list" style approach to
> >> headers. None of those are currently filterable on the current set of
> >> instructions. LOOP would allow these.
> > 
> > It's not meant for detailed packet protocol header analysis,
> > it's for stateless straight line matching of masked values
> > in packet headers.
> 
> David is right, BPF cannot - and will not - keep with any high level
> connection tracking packet filter. There is an processing trade-off between
> packet classification and packet storage with post processing analysis.

This has nothing to do with high-level connection tracking.

I want to accept all (IPv4 or IPv6) TCP packets concerning port 80.
That's all. No connection tracking. Simply a "stateless straight line
matching of masked values in packet headers". Namely, the TCP source or
destination ports, being 80. 

Should BPF be allowed to implement such a filter?

This is the core question.

If yes, then we either need LOOP, or alternatively my SKF_AD_TRANSPROTO
/ SKF_TRANS_OFF idea (see the other thread fork). Without either LOOP or
TRANSPROTO, it becomes next-to-impossible to -find- the TCP header in an
IPv6 packet, and hence make filtering decisions based on it.

If no, please justify what BPF -is- for then, given that right now
applications like tcpdump/libpcap already use it for this very purpose.
Please further justify why BPF has the "LDX MSH" instruction

-- 
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk
ICQ# 4135350       |  Registered Linux# 179460
http://www.leonerd.org.uk/

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

^ permalink raw reply

* Re: [PATCH net-next 3/3] drivers/net/bfin_mac.c: Misc function cleanups, neatening
From: Robin Getz @ 2010-08-03 13:40 UTC (permalink / raw)
  To: Joe Perches
  Cc: Mike Frysinger, Hennerich, Michael, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org
In-Reply-To: <1280454323.21777.4.camel@Joe-Laptop.home>

On Thu 29 Jul 2010 21:45, Joe Perches pondered:
> On Thu, 2010-07-29 at 20:18 -0400, Mike Frysinger wrote:
> > i'll ack the idea, but i think it better i pull these patches into the
> > Blackfin repo to get some real hardware testing before i forward them
> > back to David/netdev ...
> > 
> > that OK with you ?
> 
> Fine by me.  These are trivial patches.
> I don't have the hardware.

Do you want the hardware?

I'm moving my office, and found some extras if anyone wants them...

-Robin

^ permalink raw reply

* Re: RFC: New BPF 'LOOP' instruction
From: Paul LeoNerd Evans @ 2010-08-03 13:34 UTC (permalink / raw)
  To: David Miller, netdev; +Cc: andi
In-Reply-To: <20100803.060754.26959298.davem@davemloft.net>

[-- Attachment #1: Type: text/plain, Size: 2260 bytes --]

On Tue, Aug 03, 2010 at 06:07:54AM -0700, David Miller wrote:
> > How about simply adding a "skip ipv6 extension headers until header type
> > X" opcode?
> > 
> > I bet that would solve most of the problems here in practice.
> 
> BPF really should not have protocol specific opcodes.

You mean like the LD MSH instruction, the "load a byte, mask by 0x0f then
shift up two bits" one that's specific to fetching an IPv4 header length?

Lets look at this another way around then. Ignore my LOOP instruction
idea.

Already -right now- BPF has the SKF_NET_OFF + SFK_AD_PROTO information.
Lets consider an Ethernet/IP/TCP packet we've received:

[Ethernet header | IP header | TCP header ....]
^                ^
|                |
|                +-- SKF_NET_OFF is here
+-- 0 is here

SKF_AD_PROTO == 0x0800 (IPv4)


What if we added a new constant SKF_TRANS_OFF to store the start address
of the transport header, and a new SKF_AD storage area for the transport
protocol:

[Ethernet header | IP header | TCP header ....]
^                ^           ^
|                |           |
|                |           +--  SKF_TRANS_OFF is here
|                +-- SKF_NET_OFF is here
+-- 0 is here

SKF_AD_PROTO == 0x0800 (IPv4)
SKF_AD_TRANSPROTO == 6 (IPPROTO_TCP)


Now it's easy to see how IPv6 header processing fits into this. No
longer do we have to calculate the length of the IPv6 header, we can
just start off directly looking at the TCP header. I wanted TCP port 80;
no problem:

    LD BYTE[SKF_AD_PROTO]
    JEQ 0x0800, 1, #reject
    JEQ 0x86dd, 0, #reject
    LD BYTE[SKF_AD_TRANSPROTO]
    JEQ 6, 0, #reject
    LD BYTE[SKF_NET_OFF+0]
    JEQ 80, #accept, 0
    LD BYTE[SKF_NET_OFF+2]
    JEQ 80, 0, #reject
  accept:
    LD len
    RET A
  reject:
    RET 0

Hey presto; I've just accepted TCP src or dest port 80 on IPv4 or IPv6
without having any code to actually -parse- IPv4 or '6 headers.


Does this sound workable?

-- 
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk
ICQ# 4135350       |  Registered Linux# 179460
http://www.leonerd.org.uk/

-- 
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk
ICQ# 4135350       |  Registered Linux# 179460
http://www.leonerd.org.uk/

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 190 bytes --]

^ permalink raw reply

* [PATCH 8/8] netdev: cleanup unused davinci emac cpdma code
From: Cyril Chemparathy @ 2010-08-03 13:31 UTC (permalink / raw)
  To: netdev, davinci-linux-open-source, linux-omap
  Cc: nsekhar, srk, Cyril Chemparathy
In-Reply-To: <1280842313-3743-1-git-send-email-cyril@ti.com>

Having switched over to the newly introduced cpdma layer, this patch now
removes a whole bunch of code that is now unused.  This patch has been
maintained separate strictly for reasons of readability.

Signed-off-by: Cyril Chemparathy <cyril@ti.com>
---
 drivers/net/davinci_emac.c |  929 --------------------------------------------
 1 files changed, 0 insertions(+), 929 deletions(-)

diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c
index 3efa42d..00f26fa 100644
--- a/drivers/net/davinci_emac.c
+++ b/drivers/net/davinci_emac.c
@@ -127,7 +127,6 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
 /* EMAC register related defines */
 #define EMAC_ALL_MULTI_REG_VALUE	(0xFFFFFFFF)
 #define EMAC_NUM_MULTICAST_BITS		(64)
-#define EMAC_TEARDOWN_VALUE		(0xFFFFFFFC)
 #define EMAC_TX_CONTROL_TX_ENABLE_VAL	(0x1)
 #define EMAC_RX_CONTROL_RX_ENABLE_VAL	(0x1)
 #define EMAC_MAC_HOST_ERR_INTMASK_VAL	(0x2)
@@ -214,24 +213,10 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
 #define EMAC_DEF_MAX_MULTICAST_ADDRESSES (64) /* Max mcast addr's */
 
 /* EMAC Peripheral Device Register Memory Layout structure */
-#define EMAC_TXIDVER		0x0
-#define EMAC_TXCONTROL		0x4
-#define EMAC_TXTEARDOWN		0x8
-#define EMAC_RXIDVER		0x10
-#define EMAC_RXCONTROL		0x14
-#define EMAC_RXTEARDOWN		0x18
-#define EMAC_TXINTSTATRAW	0x80
-#define EMAC_TXINTSTATMASKED	0x84
-#define EMAC_TXINTMASKSET	0x88
-#define EMAC_TXINTMASKCLEAR	0x8C
 #define EMAC_MACINVECTOR	0x90
 
 #define EMAC_DM646X_MACEOIVECTOR	0x94
 
-#define EMAC_RXINTSTATRAW	0xA0
-#define EMAC_RXINTSTATMASKED	0xA4
-#define EMAC_RXINTMASKSET	0xA8
-#define EMAC_RXINTMASKCLEAR	0xAC
 #define EMAC_MACINTSTATRAW	0xB0
 #define EMAC_MACINTSTATMASKED	0xB4
 #define EMAC_MACINTMASKSET	0xB8
@@ -258,12 +243,6 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
 #define EMAC_MACADDRHI		0x504
 #define EMAC_MACINDEX		0x508
 
-/* EMAC HDP and Completion registors */
-#define EMAC_TXHDP(ch)		(0x600 + (ch * 4))
-#define EMAC_RXHDP(ch)		(0x620 + (ch * 4))
-#define EMAC_TXCP(ch)		(0x640 + (ch * 4))
-#define EMAC_RXCP(ch)		(0x660 + (ch * 4))
-
 /* EMAC statistics registers */
 #define EMAC_RXGOODFRAMES	0x200
 #define EMAC_RXBCASTFRAMES	0x204
@@ -311,120 +290,6 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
 /* EMAC Stats Clear Mask */
 #define EMAC_STATS_CLR_MASK    (0xFFFFFFFF)
 
-/** net_buf_obj: EMAC network bufferdata structure
- *
- * EMAC network buffer data structure
- */
-struct emac_netbufobj {
-	void *buf_token;
-	char *data_ptr;
-	int length;
-};
-
-/** net_pkt_obj: EMAC network packet data structure
- *
- * EMAC network packet data structure - supports buffer list (for future)
- */
-struct emac_netpktobj {
-	void *pkt_token; /* data token may hold tx/rx chan id */
-	struct emac_netbufobj *buf_list; /* array of network buffer objects */
-	int num_bufs;
-	int pkt_length;
-};
-
-/** emac_tx_bd: EMAC TX Buffer descriptor data structure
- *
- * EMAC TX Buffer descriptor data structure
- */
-struct emac_tx_bd {
-	int h_next;
-	int buff_ptr;
-	int off_b_len;
-	int mode; /* SOP, EOP, ownership, EOQ, teardown,Qstarv, length */
-	struct emac_tx_bd __iomem *next;
-	void *buf_token;
-};
-
-/** emac_txch: EMAC TX Channel data structure
- *
- * EMAC TX Channel data structure
- */
-struct emac_txch {
-	/* Config related */
-	u32 num_bd;
-	u32 service_max;
-
-	/* CPPI specific */
-	u32 alloc_size;
-	void __iomem *bd_mem;
-	struct emac_tx_bd __iomem *bd_pool_head;
-	struct emac_tx_bd __iomem *active_queue_head;
-	struct emac_tx_bd __iomem *active_queue_tail;
-	struct emac_tx_bd __iomem *last_hw_bdprocessed;
-	u32 queue_active;
-	u32 teardown_pending;
-	u32 *tx_complete;
-
-	/** statistics */
-	u32 proc_count;     /* TX: # of times emac_tx_bdproc is called */
-	u32 mis_queued_packets;
-	u32 queue_reinit;
-	u32 end_of_queue_add;
-	u32 out_of_tx_bd;
-	u32 no_active_pkts; /* IRQ when there were no packets to process */
-	u32 active_queue_count;
-};
-
-/** emac_rx_bd: EMAC RX Buffer descriptor data structure
- *
- * EMAC RX Buffer descriptor data structure
- */
-struct emac_rx_bd {
-	int h_next;
-	int buff_ptr;
-	int off_b_len;
-	int mode;
-	struct emac_rx_bd __iomem *next;
-	void *data_ptr;
-	void *buf_token;
-};
-
-/** emac_rxch: EMAC RX Channel data structure
- *
- * EMAC RX Channel data structure
- */
-struct emac_rxch {
-	/* configuration info */
-	u32 num_bd;
-	u32 service_max;
-	u32 buf_size;
-	char mac_addr[6];
-
-	/** CPPI specific */
-	u32 alloc_size;
-	void __iomem *bd_mem;
-	struct emac_rx_bd __iomem *bd_pool_head;
-	struct emac_rx_bd __iomem *active_queue_head;
-	struct emac_rx_bd __iomem *active_queue_tail;
-	u32 queue_active;
-	u32 teardown_pending;
-
-	/* packet and buffer objects */
-	struct emac_netpktobj pkt_queue;
-	struct emac_netbufobj buf_queue;
-
-	/** statistics */
-	u32 proc_count; /* number of times emac_rx_bdproc is called */
-	u32 processed_bd;
-	u32 recycled_bd;
-	u32 out_of_rx_bd;
-	u32 out_of_rx_buffers;
-	u32 queue_reinit;
-	u32 end_of_queue_add;
-	u32 end_of_queue;
-	u32 mis_queued_packets;
-};
-
 /* emac_priv: EMAC private data structure
  *
  * EMAC adapter private data structure
@@ -435,17 +300,10 @@ struct emac_priv {
 	struct platform_device *pdev;
 	struct napi_struct napi;
 	char mac_addr[6];
-	spinlock_t tx_lock;
-	spinlock_t rx_lock;
 	void __iomem *remap_addr;
 	u32 emac_base_phys;
 	void __iomem *emac_base;
 	void __iomem *ctrl_base;
-	void __iomem *emac_ctrl_ram;
-	u32 ctrl_ram_size;
-	u32 hw_ram_addr;
-	struct emac_txch *txch[EMAC_DEF_MAX_TX_CH];
-	struct emac_rxch *rxch[EMAC_DEF_MAX_RX_CH];
 	struct cpdma_ctlr *dma;
 	struct cpdma_chan *txchan;
 	struct cpdma_chan *rxchan;
@@ -473,18 +331,6 @@ struct emac_priv {
 static struct clk *emac_clk;
 static unsigned long emac_bus_frequency;
 
-#define emac_virt_to_phys(addr, priv) \
-	(((u32 __force)(addr) - (u32 __force)(priv->emac_ctrl_ram)) \
-	+ priv->hw_ram_addr)
-
-/* Cache macros - Packet buffers would be from skb pool which is cached */
-#define EMAC_VIRT_NOCACHE(addr) (addr)
-
-/* DM644x does not have BD's in cached memory - so no cache functions */
-#define BD_CACHE_INVALIDATE(addr, size)
-#define BD_CACHE_WRITEBACK(addr, size)
-#define BD_CACHE_WRITEBACK_INVALIDATE(addr, size)
-
 /* EMAC TX Host Error description strings */
 static char *emac_txhost_errcodes[16] = {
 	"No error", "SOP error", "Ownership bit not set in SOP buffer",
@@ -525,20 +371,6 @@ static void emac_dump_regs(struct emac_priv *priv)
 	dev_info(emac_dev, "EMAC: EWCTL: %08X, EWINTTCNT: %08X\n",
 		emac_ctrl_read(EMAC_CTRL_EWCTL),
 		emac_ctrl_read(EMAC_CTRL_EWINTTCNT));
-	dev_info(emac_dev, "EMAC: TXID: %08X %s, RXID: %08X %s\n",
-		emac_read(EMAC_TXIDVER),
-		((emac_read(EMAC_TXCONTROL)) ? "enabled" : "disabled"),
-		emac_read(EMAC_RXIDVER),
-		((emac_read(EMAC_RXCONTROL)) ? "enabled" : "disabled"));
-	dev_info(emac_dev, "EMAC: TXIntRaw:%08X, TxIntMasked: %08X, "\
-		"TxIntMasSet: %08X\n", emac_read(EMAC_TXINTSTATRAW),
-		emac_read(EMAC_TXINTSTATMASKED), emac_read(EMAC_TXINTMASKSET));
-	dev_info(emac_dev, "EMAC: RXIntRaw:%08X, RxIntMasked: %08X, "\
-		"RxIntMasSet: %08X\n", emac_read(EMAC_RXINTSTATRAW),
-		emac_read(EMAC_RXINTSTATMASKED), emac_read(EMAC_RXINTMASKSET));
-	dev_info(emac_dev, "EMAC: MacIntRaw:%08X, MacIntMasked: %08X, "\
-		"MacInVector=%08X\n", emac_read(EMAC_MACINTSTATRAW),
-		emac_read(EMAC_MACINTSTATMASKED), emac_read(EMAC_MACINVECTOR));
 	dev_info(emac_dev, "EMAC: EmuControl:%08X, FifoControl: %08X\n",
 		emac_read(EMAC_EMCONTROL), emac_read(EMAC_FIFOCONTROL));
 	dev_info(emac_dev, "EMAC: MBPEnable:%08X, RXUnicastSet: %08X, "\
@@ -547,8 +379,6 @@ static void emac_dump_regs(struct emac_priv *priv)
 	dev_info(emac_dev, "EMAC: MacControl:%08X, MacStatus: %08X, "\
 		"MacConfig=%08X\n", emac_read(EMAC_MACCONTROL),
 		emac_read(EMAC_MACSTATUS), emac_read(EMAC_MACCONFIG));
-	dev_info(emac_dev, "EMAC: TXHDP[0]:%08X, RXHDP[0]: %08X\n",
-		emac_read(EMAC_TXHDP(0)), emac_read(EMAC_RXHDP(0)));
 	dev_info(emac_dev, "EMAC Statistics\n");
 	dev_info(emac_dev, "EMAC: rx_good_frames:%d\n",
 		emac_read(EMAC_RXGOODFRAMES));
@@ -1090,372 +920,6 @@ static void emac_tx_handler(void *token, int len, int status)
 	dev_kfree_skb_any(skb);
 }
 
-/** EMAC on-chip buffer descriptor memory
- *
- * WARNING: Please note that the on chip memory is used for both TX and RX
- * buffer descriptor queues and is equally divided between TX and RX desc's
- * If the number of TX or RX descriptors change this memory pointers need
- * to be adjusted. If external memory is allocated then these pointers can
- * pointer to the memory
- *
- */
-#define EMAC_TX_BD_MEM(priv)	((priv)->emac_ctrl_ram)
-#define EMAC_RX_BD_MEM(priv)	((priv)->emac_ctrl_ram + \
-				(((priv)->ctrl_ram_size) >> 1))
-
-/**
- * emac_init_txch: TX channel initialization
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: RX channel number
- *
- * Called during device init to setup a TX channel (allocate buffer desc
- * create free pool and keep ready for transmission
- *
- * Returns success(0) or mem alloc failures error code
- */
-static int emac_init_txch(struct emac_priv *priv, u32 ch)
-{
-	struct device *emac_dev = &priv->ndev->dev;
-	u32 cnt, bd_size;
-	void __iomem *mem;
-	struct emac_tx_bd __iomem *curr_bd;
-	struct emac_txch *txch = NULL;
-
-	txch = kzalloc(sizeof(struct emac_txch), GFP_KERNEL);
-	if (NULL == txch) {
-		dev_err(emac_dev, "DaVinci EMAC: TX Ch mem alloc failed");
-		return -ENOMEM;
-	}
-	priv->txch[ch] = txch;
-	txch->service_max = EMAC_DEF_TX_MAX_SERVICE;
-	txch->active_queue_head = NULL;
-	txch->active_queue_tail = NULL;
-	txch->queue_active = 0;
-	txch->teardown_pending = 0;
-
-	/* allocate memory for TX CPPI channel on a 4 byte boundry */
-	txch->tx_complete = kzalloc(txch->service_max * sizeof(u32),
-				    GFP_KERNEL);
-	if (NULL == txch->tx_complete) {
-		dev_err(emac_dev, "DaVinci EMAC: Tx service mem alloc failed");
-		kfree(txch);
-		return -ENOMEM;
-	}
-
-	/* allocate buffer descriptor pool align every BD on four word
-	 * boundry for future requirements */
-	bd_size = (sizeof(struct emac_tx_bd) + 0xF) & ~0xF;
-	txch->num_bd = (priv->ctrl_ram_size >> 1) / bd_size;
-	txch->alloc_size = (((bd_size * txch->num_bd) + 0xF) & ~0xF);
-
-	/* alloc TX BD memory */
-	txch->bd_mem = EMAC_TX_BD_MEM(priv);
-	__memzero((void __force *)txch->bd_mem, txch->alloc_size);
-
-	/* initialize the BD linked list */
-	mem = (void __force __iomem *)
-			(((u32 __force) txch->bd_mem + 0xF) & ~0xF);
-	txch->bd_pool_head = NULL;
-	for (cnt = 0; cnt < txch->num_bd; cnt++) {
-		curr_bd = mem + (cnt * bd_size);
-		curr_bd->next = txch->bd_pool_head;
-		txch->bd_pool_head = curr_bd;
-	}
-
-	/* reset statistics counters */
-	txch->out_of_tx_bd = 0;
-	txch->no_active_pkts = 0;
-	txch->active_queue_count = 0;
-
-	return 0;
-}
-
-/**
- * emac_cleanup_txch: Book-keep function to clean TX channel resources
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: TX channel number
- *
- * Called to clean up TX channel resources
- *
- */
-static void emac_cleanup_txch(struct emac_priv *priv, u32 ch)
-{
-	struct emac_txch *txch = priv->txch[ch];
-
-	if (txch) {
-		if (txch->bd_mem)
-			txch->bd_mem = NULL;
-		kfree(txch->tx_complete);
-		kfree(txch);
-		priv->txch[ch] = NULL;
-	}
-}
-
-/**
- * emac_net_tx_complete: TX packet completion function
- * @priv: The DaVinci EMAC private adapter structure
- * @net_data_tokens: packet token - skb pointer
- * @num_tokens: number of skb's to free
- * @ch: TX channel number
- *
- * Frees the skb once packet is transmitted
- *
- */
-static int emac_net_tx_complete(struct emac_priv *priv,
-				void **net_data_tokens,
-				int num_tokens, u32 ch)
-{
-	u32 cnt;
-
-	if (unlikely(num_tokens && netif_queue_stopped(priv->ndev)))
-		netif_start_queue(priv->ndev);
-	for (cnt = 0; cnt < num_tokens; cnt++) {
-		struct sk_buff *skb = (struct sk_buff *)net_data_tokens[cnt];
-		if (skb == NULL)
-			continue;
-		priv->net_dev_stats.tx_packets++;
-		priv->net_dev_stats.tx_bytes += skb->len;
-		dev_kfree_skb_any(skb);
-	}
-	return 0;
-}
-
-/**
- * emac_txch_teardown: TX channel teardown
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: TX channel number
- *
- * Called to teardown TX channel
- *
- */
-static void emac_txch_teardown(struct emac_priv *priv, u32 ch)
-{
-	struct device *emac_dev = &priv->ndev->dev;
-	u32 teardown_cnt = 0xFFFFFFF0; /* Some high value */
-	struct emac_txch *txch = priv->txch[ch];
-	struct emac_tx_bd __iomem *curr_bd;
-
-	while ((emac_read(EMAC_TXCP(ch)) & EMAC_TEARDOWN_VALUE) !=
-	       EMAC_TEARDOWN_VALUE) {
-		/* wait till tx teardown complete */
-		cpu_relax(); /* TODO: check if this helps ... */
-		--teardown_cnt;
-		if (0 == teardown_cnt) {
-			dev_err(emac_dev, "EMAC: TX teardown aborted\n");
-			break;
-		}
-	}
-	emac_write(EMAC_TXCP(ch), EMAC_TEARDOWN_VALUE);
-
-	/* process sent packets and return skb's to upper layer */
-	if (1 == txch->queue_active) {
-		curr_bd = txch->active_queue_head;
-		while (curr_bd != NULL) {
-			dma_unmap_single(emac_dev, curr_bd->buff_ptr,
-				curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE,
-				DMA_TO_DEVICE);
-
-			emac_net_tx_complete(priv, (void __force *)
-					&curr_bd->buf_token, 1, ch);
-			if (curr_bd != txch->active_queue_tail)
-				curr_bd = curr_bd->next;
-			else
-				break;
-		}
-		txch->bd_pool_head = txch->active_queue_head;
-		txch->active_queue_head =
-		txch->active_queue_tail = NULL;
-	}
-}
-
-/**
- * emac_stop_txch: Stop TX channel operation
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: TX channel number
- *
- * Called to stop TX channel operation
- *
- */
-static void emac_stop_txch(struct emac_priv *priv, u32 ch)
-{
-	struct emac_txch *txch = priv->txch[ch];
-
-	if (txch) {
-		txch->teardown_pending = 1;
-		emac_write(EMAC_TXTEARDOWN, 0);
-		emac_txch_teardown(priv, ch);
-		txch->teardown_pending = 0;
-		emac_write(EMAC_TXINTMASKCLEAR, BIT(ch));
-	}
-}
-
-/**
- * emac_tx_bdproc: TX buffer descriptor (packet) processing
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: TX channel number to process buffer descriptors for
- * @budget: number of packets allowed to process
- * @pending: indication to caller that packets are pending to process
- *
- * Processes TX buffer descriptors after packets are transmitted - checks
- * ownership bit on the TX * descriptor and requeues it to free pool & frees
- * the SKB buffer. Only "budget" number of packets are processed and
- * indication of pending packets provided to the caller
- *
- * Returns number of packets processed
- */
-static int emac_tx_bdproc(struct emac_priv *priv, u32 ch, u32 budget)
-{
-	struct device *emac_dev = &priv->ndev->dev;
-	unsigned long flags;
-	u32 frame_status;
-	u32 pkts_processed = 0;
-	u32 tx_complete_cnt = 0;
-	struct emac_tx_bd __iomem *curr_bd;
-	struct emac_txch *txch = priv->txch[ch];
-	u32 *tx_complete_ptr = txch->tx_complete;
-
-	if (unlikely(1 == txch->teardown_pending)) {
-		if (netif_msg_tx_err(priv) && net_ratelimit()) {
-			dev_err(emac_dev, "DaVinci EMAC:emac_tx_bdproc: "\
-				"teardown pending\n");
-		}
-		return 0;  /* dont handle any pkt completions */
-	}
-
-	++txch->proc_count;
-	spin_lock_irqsave(&priv->tx_lock, flags);
-	curr_bd = txch->active_queue_head;
-	if (NULL == curr_bd) {
-		emac_write(EMAC_TXCP(ch),
-			   emac_virt_to_phys(txch->last_hw_bdprocessed, priv));
-		txch->no_active_pkts++;
-		spin_unlock_irqrestore(&priv->tx_lock, flags);
-		return 0;
-	}
-	BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
-	frame_status = curr_bd->mode;
-	while ((curr_bd) &&
-	      ((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0) &&
-	      (pkts_processed < budget)) {
-		emac_write(EMAC_TXCP(ch), emac_virt_to_phys(curr_bd, priv));
-		txch->active_queue_head = curr_bd->next;
-		if (frame_status & EMAC_CPPI_EOQ_BIT) {
-			if (curr_bd->next) {	/* misqueued packet */
-				emac_write(EMAC_TXHDP(ch), curr_bd->h_next);
-				++txch->mis_queued_packets;
-			} else {
-				txch->queue_active = 0; /* end of queue */
-			}
-		}
-
-		dma_unmap_single(emac_dev, curr_bd->buff_ptr,
-				curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE,
-				DMA_TO_DEVICE);
-
-		*tx_complete_ptr = (u32) curr_bd->buf_token;
-		++tx_complete_ptr;
-		++tx_complete_cnt;
-		curr_bd->next = txch->bd_pool_head;
-		txch->bd_pool_head = curr_bd;
-		--txch->active_queue_count;
-		pkts_processed++;
-		txch->last_hw_bdprocessed = curr_bd;
-		curr_bd = txch->active_queue_head;
-		if (curr_bd) {
-			BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
-			frame_status = curr_bd->mode;
-		}
-	} /* end of pkt processing loop */
-
-	emac_net_tx_complete(priv,
-			     (void *)&txch->tx_complete[0],
-			     tx_complete_cnt, ch);
-	spin_unlock_irqrestore(&priv->tx_lock, flags);
-	return pkts_processed;
-}
-
-#define EMAC_ERR_TX_OUT_OF_BD -1
-
-/**
- * emac_send: EMAC Transmit function (internal)
- * @priv: The DaVinci EMAC private adapter structure
- * @pkt: packet pointer (contains skb ptr)
- * @ch: TX channel number
- *
- * Called by the transmit function to queue the packet in EMAC hardware queue
- *
- * Returns success(0) or error code (typically out of desc's)
- */
-static int emac_send(struct emac_priv *priv, struct emac_netpktobj *pkt, u32 ch)
-{
-	unsigned long flags;
-	struct emac_tx_bd __iomem *curr_bd;
-	struct emac_txch *txch;
-	struct emac_netbufobj *buf_list;
-
-	txch = priv->txch[ch];
-	buf_list = pkt->buf_list;   /* get handle to the buffer array */
-
-	/* check packet size and pad if short */
-	if (pkt->pkt_length < EMAC_DEF_MIN_ETHPKTSIZE) {
-		buf_list->length += (EMAC_DEF_MIN_ETHPKTSIZE - pkt->pkt_length);
-		pkt->pkt_length = EMAC_DEF_MIN_ETHPKTSIZE;
-	}
-
-	spin_lock_irqsave(&priv->tx_lock, flags);
-	curr_bd = txch->bd_pool_head;
-	if (curr_bd == NULL) {
-		txch->out_of_tx_bd++;
-		spin_unlock_irqrestore(&priv->tx_lock, flags);
-		return EMAC_ERR_TX_OUT_OF_BD;
-	}
-
-	txch->bd_pool_head = curr_bd->next;
-	curr_bd->buf_token = buf_list->buf_token;
-	curr_bd->buff_ptr = dma_map_single(&priv->ndev->dev, buf_list->data_ptr,
-			buf_list->length, DMA_TO_DEVICE);
-	curr_bd->off_b_len = buf_list->length;
-	curr_bd->h_next = 0;
-	curr_bd->next = NULL;
-	curr_bd->mode = (EMAC_CPPI_SOP_BIT | EMAC_CPPI_OWNERSHIP_BIT |
-			 EMAC_CPPI_EOP_BIT | pkt->pkt_length);
-
-	/* flush the packet from cache if write back cache is present */
-	BD_CACHE_WRITEBACK_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
-
-	/* send the packet */
-	if (txch->active_queue_head == NULL) {
-		txch->active_queue_head = curr_bd;
-		txch->active_queue_tail = curr_bd;
-		if (1 != txch->queue_active) {
-			emac_write(EMAC_TXHDP(ch),
-					emac_virt_to_phys(curr_bd, priv));
-			txch->queue_active = 1;
-		}
-		++txch->queue_reinit;
-	} else {
-		register struct emac_tx_bd __iomem *tail_bd;
-		register u32 frame_status;
-
-		tail_bd = txch->active_queue_tail;
-		tail_bd->next = curr_bd;
-		txch->active_queue_tail = curr_bd;
-		tail_bd = EMAC_VIRT_NOCACHE(tail_bd);
-		tail_bd->h_next = (int)emac_virt_to_phys(curr_bd, priv);
-		frame_status = tail_bd->mode;
-		if (frame_status & EMAC_CPPI_EOQ_BIT) {
-			emac_write(EMAC_TXHDP(ch),
-				emac_virt_to_phys(curr_bd, priv));
-			frame_status &= ~(EMAC_CPPI_EOQ_BIT);
-			tail_bd->mode = frame_status;
-			++txch->end_of_queue_add;
-		}
-	}
-	txch->active_queue_count++;
-	spin_unlock_irqrestore(&priv->tx_lock, flags);
-	return 0;
-}
-
 /**
  * emac_dev_xmit: EMAC Transmit function
  * @skb: SKB pointer
@@ -1528,207 +992,6 @@ static void emac_dev_tx_timeout(struct net_device *ndev)
 }
 
 /**
- * emac_net_alloc_rx_buf: Allocate a skb for RX
- * @priv: The DaVinci EMAC private adapter structure
- * @buf_size: size of SKB data buffer to allocate
- * @data_token: data token returned (skb handle for storing in buffer desc)
- * @ch: RX channel number
- *
- * Called during RX channel setup - allocates skb buffer of required size
- * and provides the skb handle and allocated buffer data pointer to caller
- *
- * Returns skb data pointer or 0 on failure to alloc skb
- */
-static void *emac_net_alloc_rx_buf(struct emac_priv *priv, int buf_size,
-		void **data_token, u32 ch)
-{
-	struct net_device *ndev = priv->ndev;
-	struct device *emac_dev = &ndev->dev;
-	struct sk_buff *p_skb;
-
-	p_skb = dev_alloc_skb(buf_size);
-	if (unlikely(NULL == p_skb)) {
-		if (netif_msg_rx_err(priv) && net_ratelimit())
-			dev_err(emac_dev, "DaVinci EMAC: failed to alloc skb");
-		return NULL;
-	}
-
-	/* set device pointer in skb and reserve space for extra bytes */
-	p_skb->dev = ndev;
-	skb_reserve(p_skb, NET_IP_ALIGN);
-	*data_token = (void *) p_skb;
-	return p_skb->data;
-}
-
-/**
- * emac_init_rxch: RX channel initialization
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: RX channel number
- * @param: mac address for RX channel
- *
- * Called during device init to setup a RX channel (allocate buffers and
- * buffer descriptors, create queue and keep ready for reception
- *
- * Returns success(0) or mem alloc failures error code
- */
-static int emac_init_rxch(struct emac_priv *priv, u32 ch, char *param)
-{
-	struct device *emac_dev = &priv->ndev->dev;
-	u32 cnt, bd_size;
-	void __iomem *mem;
-	struct emac_rx_bd __iomem *curr_bd;
-	struct emac_rxch *rxch = NULL;
-
-	rxch = kzalloc(sizeof(struct emac_rxch), GFP_KERNEL);
-	if (NULL == rxch) {
-		dev_err(emac_dev, "DaVinci EMAC: RX Ch mem alloc failed");
-		return -ENOMEM;
-	}
-	priv->rxch[ch] = rxch;
-	rxch->buf_size = priv->rx_buf_size;
-	rxch->service_max = EMAC_DEF_RX_MAX_SERVICE;
-	rxch->queue_active = 0;
-	rxch->teardown_pending = 0;
-
-	/* save mac address */
-	for (cnt = 0; cnt < 6; cnt++)
-		rxch->mac_addr[cnt] = param[cnt];
-
-	/* allocate buffer descriptor pool align every BD on four word
-	 * boundry for future requirements */
-	bd_size = (sizeof(struct emac_rx_bd) + 0xF) & ~0xF;
-	rxch->num_bd = (priv->ctrl_ram_size >> 1) / bd_size;
-	rxch->alloc_size = (((bd_size * rxch->num_bd) + 0xF) & ~0xF);
-	rxch->bd_mem = EMAC_RX_BD_MEM(priv);
-	__memzero((void __force *)rxch->bd_mem, rxch->alloc_size);
-	rxch->pkt_queue.buf_list = &rxch->buf_queue;
-
-	/* allocate RX buffer and initialize the BD linked list */
-	mem = (void __force __iomem *)
-			(((u32 __force) rxch->bd_mem + 0xF) & ~0xF);
-	rxch->active_queue_head = NULL;
-	rxch->active_queue_tail = mem;
-	for (cnt = 0; cnt < rxch->num_bd; cnt++) {
-		curr_bd = mem + (cnt * bd_size);
-		/* for future use the last parameter contains the BD ptr */
-		curr_bd->data_ptr = emac_net_alloc_rx_buf(priv,
-				    rxch->buf_size,
-				    (void __force **)&curr_bd->buf_token,
-				    EMAC_DEF_RX_CH);
-		if (curr_bd->data_ptr == NULL) {
-			dev_err(emac_dev, "DaVinci EMAC: RX buf mem alloc " \
-				"failed for ch %d\n", ch);
-			kfree(rxch);
-			return -ENOMEM;
-		}
-
-		/* populate the hardware descriptor */
-		curr_bd->h_next = emac_virt_to_phys(rxch->active_queue_head,
-				priv);
-		curr_bd->buff_ptr = dma_map_single(emac_dev, curr_bd->data_ptr,
-				rxch->buf_size, DMA_FROM_DEVICE);
-		curr_bd->off_b_len = rxch->buf_size;
-		curr_bd->mode = EMAC_CPPI_OWNERSHIP_BIT;
-
-		/* write back to hardware memory */
-		BD_CACHE_WRITEBACK_INVALIDATE((u32) curr_bd,
-					      EMAC_BD_LENGTH_FOR_CACHE);
-		curr_bd->next = rxch->active_queue_head;
-		rxch->active_queue_head = curr_bd;
-	}
-
-	/* At this point rxCppi->activeQueueHead points to the first
-	   RX BD ready to be given to RX HDP and rxch->active_queue_tail
-	   points to the last RX BD
-	 */
-	return 0;
-}
-
-/**
- * emac_rxch_teardown: RX channel teardown
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: RX channel number
- *
- * Called during device stop to teardown RX channel
- *
- */
-static void emac_rxch_teardown(struct emac_priv *priv, u32 ch)
-{
-	struct device *emac_dev = &priv->ndev->dev;
-	u32 teardown_cnt = 0xFFFFFFF0; /* Some high value */
-
-	while ((emac_read(EMAC_RXCP(ch)) & EMAC_TEARDOWN_VALUE) !=
-	       EMAC_TEARDOWN_VALUE) {
-		/* wait till tx teardown complete */
-		cpu_relax(); /* TODO: check if this helps ... */
-		--teardown_cnt;
-		if (0 == teardown_cnt) {
-			dev_err(emac_dev, "EMAC: RX teardown aborted\n");
-			break;
-		}
-	}
-	emac_write(EMAC_RXCP(ch), EMAC_TEARDOWN_VALUE);
-}
-
-/**
- * emac_stop_rxch: Stop RX channel operation
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: RX channel number
- *
- * Called during device stop to stop RX channel operation
- *
- */
-static void emac_stop_rxch(struct emac_priv *priv, u32 ch)
-{
-	struct emac_rxch *rxch = priv->rxch[ch];
-
-	if (rxch) {
-		rxch->teardown_pending = 1;
-		emac_write(EMAC_RXTEARDOWN, ch);
-		/* wait for teardown complete */
-		emac_rxch_teardown(priv, ch);
-		rxch->teardown_pending = 0;
-		emac_write(EMAC_RXINTMASKCLEAR, BIT(ch));
-	}
-}
-
-/**
- * emac_cleanup_rxch: Book-keep function to clean RX channel resources
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: RX channel number
- *
- * Called during device stop to clean up RX channel resources
- *
- */
-static void emac_cleanup_rxch(struct emac_priv *priv, u32 ch)
-{
-	struct emac_rxch *rxch = priv->rxch[ch];
-	struct emac_rx_bd __iomem *curr_bd;
-
-	if (rxch) {
-		/* free the receive buffers previously allocated */
-		curr_bd = rxch->active_queue_head;
-		while (curr_bd) {
-			if (curr_bd->buf_token) {
-				dma_unmap_single(&priv->ndev->dev,
-					curr_bd->buff_ptr,
-					curr_bd->off_b_len
-						& EMAC_RX_BD_BUF_SIZE,
-					DMA_FROM_DEVICE);
-
-				dev_kfree_skb_any((struct sk_buff *)\
-						  curr_bd->buf_token);
-			}
-			curr_bd = curr_bd->next;
-		}
-		if (rxch->bd_mem)
-			rxch->bd_mem = NULL;
-		kfree(rxch);
-		priv->rxch[ch] = NULL;
-	}
-}
-
-/**
  * emac_set_type0addr: Set EMAC Type0 mac address
  * @priv: The DaVinci EMAC private adapter structure
  * @ch: RX channel number
@@ -1868,194 +1131,6 @@ static int emac_dev_setmac_addr(struct net_device *ndev, void *addr)
 }
 
 /**
- * emac_addbd_to_rx_queue: Recycle RX buffer descriptor
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: RX channel number to process buffer descriptors for
- * @curr_bd: current buffer descriptor
- * @buffer: buffer pointer for descriptor
- * @buf_token: buffer token (stores skb information)
- *
- * Prepares the recycled buffer descriptor and addes it to hardware
- * receive queue - if queue empty this descriptor becomes the head
- * else addes the descriptor to end of queue
- *
- */
-static void emac_addbd_to_rx_queue(struct emac_priv *priv, u32 ch,
-		struct emac_rx_bd __iomem *curr_bd,
-		char *buffer, void *buf_token)
-{
-	struct emac_rxch *rxch = priv->rxch[ch];
-
-	/* populate the hardware descriptor */
-	curr_bd->h_next = 0;
-	curr_bd->buff_ptr = dma_map_single(&priv->ndev->dev, buffer,
-				rxch->buf_size, DMA_FROM_DEVICE);
-	curr_bd->off_b_len = rxch->buf_size;
-	curr_bd->mode = EMAC_CPPI_OWNERSHIP_BIT;
-	curr_bd->next = NULL;
-	curr_bd->data_ptr = buffer;
-	curr_bd->buf_token = buf_token;
-
-	/* write back  */
-	BD_CACHE_WRITEBACK_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
-	if (rxch->active_queue_head == NULL) {
-		rxch->active_queue_head = curr_bd;
-		rxch->active_queue_tail = curr_bd;
-		if (0 != rxch->queue_active) {
-			emac_write(EMAC_RXHDP(ch),
-			   emac_virt_to_phys(rxch->active_queue_head, priv));
-			rxch->queue_active = 1;
-		}
-	} else {
-		struct emac_rx_bd __iomem *tail_bd;
-		u32 frame_status;
-
-		tail_bd = rxch->active_queue_tail;
-		rxch->active_queue_tail = curr_bd;
-		tail_bd->next = curr_bd;
-		tail_bd = EMAC_VIRT_NOCACHE(tail_bd);
-		tail_bd->h_next = emac_virt_to_phys(curr_bd, priv);
-		frame_status = tail_bd->mode;
-		if (frame_status & EMAC_CPPI_EOQ_BIT) {
-			emac_write(EMAC_RXHDP(ch),
-					emac_virt_to_phys(curr_bd, priv));
-			frame_status &= ~(EMAC_CPPI_EOQ_BIT);
-			tail_bd->mode = frame_status;
-			++rxch->end_of_queue_add;
-		}
-	}
-	++rxch->recycled_bd;
-}
-
-/**
- * emac_net_rx_cb: Prepares packet and sends to upper layer
- * @priv: The DaVinci EMAC private adapter structure
- * @net_pkt_list: Network packet list (received packets)
- *
- * Invalidates packet buffer memory and sends the received packet to upper
- * layer
- *
- * Returns success or appropriate error code (none as of now)
- */
-static int emac_net_rx_cb(struct emac_priv *priv,
-			  struct emac_netpktobj *net_pkt_list)
-{
-	struct sk_buff *p_skb;
-	p_skb = (struct sk_buff *)net_pkt_list->pkt_token;
-	/* set length of packet */
-	skb_put(p_skb, net_pkt_list->pkt_length);
-	p_skb->protocol = eth_type_trans(p_skb, priv->ndev);
-	netif_receive_skb(p_skb);
-	priv->net_dev_stats.rx_bytes += net_pkt_list->pkt_length;
-	priv->net_dev_stats.rx_packets++;
-	return 0;
-}
-
-/**
- * emac_rx_bdproc: RX buffer descriptor (packet) processing
- * @priv: The DaVinci EMAC private adapter structure
- * @ch: RX channel number to process buffer descriptors for
- * @budget: number of packets allowed to process
- * @pending: indication to caller that packets are pending to process
- *
- * Processes RX buffer descriptors - checks ownership bit on the RX buffer
- * descriptor, sends the receive packet to upper layer, allocates a new SKB
- * and recycles the buffer descriptor (requeues it in hardware RX queue).
- * Only "budget" number of packets are processed and indication of pending
- * packets provided to the caller.
- *
- * Returns number of packets processed (and indication of pending packets)
- */
-static int emac_rx_bdproc(struct emac_priv *priv, u32 ch, u32 budget)
-{
-	unsigned long flags;
-	u32 frame_status;
-	u32 pkts_processed = 0;
-	char *new_buffer;
-	struct emac_rx_bd __iomem *curr_bd;
-	struct emac_rx_bd __iomem *last_bd;
-	struct emac_netpktobj *curr_pkt, pkt_obj;
-	struct emac_netbufobj buf_obj;
-	struct emac_netbufobj *rx_buf_obj;
-	void *new_buf_token;
-	struct emac_rxch *rxch = priv->rxch[ch];
-
-	if (unlikely(1 == rxch->teardown_pending))
-		return 0;
-	++rxch->proc_count;
-	spin_lock_irqsave(&priv->rx_lock, flags);
-	pkt_obj.buf_list = &buf_obj;
-	curr_pkt = &pkt_obj;
-	curr_bd = rxch->active_queue_head;
-	BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
-	frame_status = curr_bd->mode;
-
-	while ((curr_bd) &&
-	       ((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0) &&
-	       (pkts_processed < budget)) {
-
-		new_buffer = emac_net_alloc_rx_buf(priv, rxch->buf_size,
-					&new_buf_token, EMAC_DEF_RX_CH);
-		if (unlikely(NULL == new_buffer)) {
-			++rxch->out_of_rx_buffers;
-			goto end_emac_rx_bdproc;
-		}
-
-		/* populate received packet data structure */
-		rx_buf_obj = &curr_pkt->buf_list[0];
-		rx_buf_obj->data_ptr = (char *)curr_bd->data_ptr;
-		rx_buf_obj->length = curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE;
-		rx_buf_obj->buf_token = curr_bd->buf_token;
-
-		dma_unmap_single(&priv->ndev->dev, curr_bd->buff_ptr,
-				curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE,
-				DMA_FROM_DEVICE);
-
-		curr_pkt->pkt_token = curr_pkt->buf_list->buf_token;
-		curr_pkt->num_bufs = 1;
-		curr_pkt->pkt_length =
-			(frame_status & EMAC_RX_BD_PKT_LENGTH_MASK);
-		emac_write(EMAC_RXCP(ch), emac_virt_to_phys(curr_bd, priv));
-		++rxch->processed_bd;
-		last_bd = curr_bd;
-		curr_bd = last_bd->next;
-		rxch->active_queue_head = curr_bd;
-
-		/* check if end of RX queue ? */
-		if (frame_status & EMAC_CPPI_EOQ_BIT) {
-			if (curr_bd) {
-				++rxch->mis_queued_packets;
-				emac_write(EMAC_RXHDP(ch),
-					   emac_virt_to_phys(curr_bd, priv));
-			} else {
-				++rxch->end_of_queue;
-				rxch->queue_active = 0;
-			}
-		}
-
-		/* recycle BD */
-		emac_addbd_to_rx_queue(priv, ch, last_bd, new_buffer,
-				       new_buf_token);
-
-		/* return the packet to the user - BD ptr passed in
-		 * last parameter for potential *future* use */
-		spin_unlock_irqrestore(&priv->rx_lock, flags);
-		emac_net_rx_cb(priv, curr_pkt);
-		spin_lock_irqsave(&priv->rx_lock, flags);
-		curr_bd = rxch->active_queue_head;
-		if (curr_bd) {
-			BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
-			frame_status = curr_bd->mode;
-		}
-		++pkts_processed;
-	}
-
-end_emac_rx_bdproc:
-	spin_unlock_irqrestore(&priv->rx_lock, flags);
-	return pkts_processed;
-}
-
-/**
  * emac_hw_enable: Enable EMAC hardware for packet transmission/reception
  * @priv: The DaVinci EMAC private adapter structure
  *
@@ -2578,8 +1653,6 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
 	priv->ndev = ndev;
 	priv->msg_enable = netif_msg_init(debug_level, DAVINCI_EMAC_DEBUG);
 
-	spin_lock_init(&priv->tx_lock);
-	spin_lock_init(&priv->rx_lock);
 	spin_lock_init(&priv->lock);
 
 	pdata = pdev->dev.platform_data;
@@ -2624,8 +1697,6 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
 	ndev->base_addr = (unsigned long)priv->remap_addr;
 
 	priv->ctrl_base = priv->remap_addr + pdata->ctrl_mod_reg_offset;
-	priv->ctrl_ram_size = pdata->ctrl_ram_size;
-	priv->emac_ctrl_ram = priv->remap_addr + pdata->ctrl_ram_offset;
 
 	hw_ram_addr = pdata->hw_ram_addr;
 	if (!hw_ram_addr)
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 2/8] davinci: add mdio platform devices
From: Cyril Chemparathy @ 2010-08-03 13:31 UTC (permalink / raw)
  To: netdev, davinci-linux-open-source, linux-omap
  Cc: nsekhar, srk, Cyril Chemparathy
In-Reply-To: <1280842313-3743-1-git-send-email-cyril@ti.com>

This patch adds mdio platform devices on SoCs that have the necessary
hardware.  Clock lookup entries (aliases) have also been added, so that the
MDIO and EMAC drivers can independently enable/disable a shared underlying
clock.  Further, the EMAC MMR region has been split down into separate MDIO
and EMAC regions.

Signed-off-by: Cyril Chemparathy <cyril@ti.com>
---
 arch/arm/mach-davinci/devices-da8xx.c       |   29 +++++++++++++++++++++++++-
 arch/arm/mach-davinci/dm365.c               |   22 +++++++++++++++++++-
 arch/arm/mach-davinci/dm644x.c              |   22 +++++++++++++++++++-
 arch/arm/mach-davinci/dm646x.c              |   22 +++++++++++++++++++-
 arch/arm/mach-davinci/include/mach/dm365.h  |    1 +
 arch/arm/mach-davinci/include/mach/dm644x.h |    1 +
 arch/arm/mach-davinci/include/mach/dm646x.h |    1 +
 7 files changed, 93 insertions(+), 5 deletions(-)

diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c
index 8cda729..c673e2a 100644
--- a/arch/arm/mach-davinci/devices-da8xx.c
+++ b/arch/arm/mach-davinci/devices-da8xx.c
@@ -341,7 +341,7 @@ int __init da8xx_register_watchdog(void)
 static struct resource da8xx_emac_resources[] = {
 	{
 		.start	= DA8XX_EMAC_CPPI_PORT_BASE,
-		.end	= DA8XX_EMAC_CPPI_PORT_BASE + 0x5000 - 1,
+		.end	= DA8XX_EMAC_CPPI_PORT_BASE + SZ_16K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
 	{
@@ -385,9 +385,34 @@ static struct platform_device da8xx_emac_device = {
 	.resource	= da8xx_emac_resources,
 };
 
+static struct resource da8xx_mdio_resources[] = {
+	{
+		.start	= DA8XX_EMAC_MDIO_BASE,
+		.end	= DA8XX_EMAC_MDIO_BASE + SZ_4K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device da8xx_mdio_device = {
+	.name		= "davinci_mdio",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(da8xx_mdio_resources),
+	.resource	= da8xx_mdio_resources,
+};
+
 int __init da8xx_register_emac(void)
 {
-	return platform_device_register(&da8xx_emac_device);
+	int ret;
+
+	ret = platform_device_register(&da8xx_mdio_device);
+	if (ret < 0)
+		return ret;
+	ret = platform_device_register(&da8xx_emac_device);
+	if (ret < 0)
+		return ret;
+	ret = clk_add_alias(NULL, dev_name(&da8xx_mdio_device.dev),
+			    NULL, &da8xx_emac_device.dev);
+	return ret;
 }
 
 static struct resource da830_mcasp1_resources[] = {
diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c
index 652f4b6..4a58f03 100644
--- a/arch/arm/mach-davinci/dm365.c
+++ b/arch/arm/mach-davinci/dm365.c
@@ -699,7 +699,7 @@ static struct emac_platform_data dm365_emac_pdata = {
 static struct resource dm365_emac_resources[] = {
 	{
 		.start	= DM365_EMAC_BASE,
-		.end	= DM365_EMAC_BASE + 0x47ff,
+		.end	= DM365_EMAC_BASE + SZ_16K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
 	{
@@ -734,6 +734,21 @@ static struct platform_device dm365_emac_device = {
 	.resource	= dm365_emac_resources,
 };
 
+static struct resource dm365_mdio_resources[] = {
+	{
+		.start	= DM365_EMAC_MDIO_BASE,
+		.end	= DM365_EMAC_MDIO_BASE + SZ_4K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device dm365_mdio_device = {
+	.name		= "davinci_mdio",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(dm365_mdio_resources),
+	.resource	= dm365_mdio_resources,
+};
+
 static u8 dm365_default_priorities[DAVINCI_N_AINTC_IRQ] = {
 	[IRQ_VDINT0]			= 2,
 	[IRQ_VDINT1]			= 6,
@@ -1218,7 +1233,12 @@ static int __init dm365_init_devices(void)
 
 	davinci_cfg_reg(DM365_INT_EDMA_CC);
 	platform_device_register(&dm365_edma_device);
+
+	platform_device_register(&dm365_mdio_device);
 	platform_device_register(&dm365_emac_device);
+	clk_add_alias(NULL, dev_name(&dm365_mdio_device.dev),
+		      NULL, &dm365_emac_device.dev);
+
 	/* Add isif clock alias */
 	clk_add_alias("master", dm365_isif_dev.name, "vpss_master", NULL);
 	platform_device_register(&dm365_vpss_device);
diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c
index 7ad1520..2673723 100644
--- a/arch/arm/mach-davinci/dm644x.c
+++ b/arch/arm/mach-davinci/dm644x.c
@@ -330,7 +330,7 @@ static struct emac_platform_data dm644x_emac_pdata = {
 static struct resource dm644x_emac_resources[] = {
 	{
 		.start	= DM644X_EMAC_BASE,
-		.end	= DM644X_EMAC_BASE + 0x47ff,
+		.end	= DM644X_EMAC_BASE + SZ_16K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
 	{
@@ -350,6 +350,21 @@ static struct platform_device dm644x_emac_device = {
        .resource	= dm644x_emac_resources,
 };
 
+static struct resource dm644x_mdio_resources[] = {
+	{
+		.start	= DM644X_EMAC_MDIO_BASE,
+		.end	= DM644X_EMAC_MDIO_BASE + SZ_4K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device dm644x_mdio_device = {
+       .name		= "davinci_mdio",
+       .id		= 0,
+       .num_resources	= ARRAY_SIZE(dm644x_mdio_resources),
+       .resource	= dm644x_mdio_resources,
+};
+
 /*
  * Device specific mux setup
  *
@@ -775,7 +790,12 @@ static int __init dm644x_init_devices(void)
 	clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL);
 	clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL);
 	platform_device_register(&dm644x_edma_device);
+
+	platform_device_register(&dm644x_mdio_device);
 	platform_device_register(&dm644x_emac_device);
+	clk_add_alias(NULL, dev_name(&dm644x_mdio_device.dev),
+		      NULL, &dm644x_emac_device.dev);
+
 	platform_device_register(&dm644x_vpss_device);
 	platform_device_register(&dm644x_ccdc_dev);
 	platform_device_register(&vpfe_capture_dev);
diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c
index 9404565..4e90511 100644
--- a/arch/arm/mach-davinci/dm646x.c
+++ b/arch/arm/mach-davinci/dm646x.c
@@ -366,7 +366,7 @@ static struct emac_platform_data dm646x_emac_pdata = {
 static struct resource dm646x_emac_resources[] = {
 	{
 		.start	= DM646X_EMAC_BASE,
-		.end	= DM646X_EMAC_BASE + 0x47ff,
+		.end	= DM646X_EMAC_BASE + SZ_16K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
 	{
@@ -401,6 +401,21 @@ static struct platform_device dm646x_emac_device = {
 	.resource	= dm646x_emac_resources,
 };
 
+static struct resource dm646x_mdio_resources[] = {
+	{
+		.start	= DM646X_EMAC_MDIO_BASE,
+		.end	= DM646X_EMAC_MDIO_BASE + SZ_4K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device dm646x_mdio_device = {
+	.name		= "davinci_mdio",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(dm646x_mdio_resources),
+	.resource	= dm646x_mdio_resources,
+};
+
 /*
  * Device specific mux setup
  *
@@ -889,7 +904,12 @@ static int __init dm646x_init_devices(void)
 		return 0;
 
 	platform_device_register(&dm646x_edma_device);
+
+	platform_device_register(&dm646x_mdio_device);
 	platform_device_register(&dm646x_emac_device);
+	clk_add_alias(NULL, dev_name(&dm646x_mdio_device.dev),
+		      NULL, &dm646x_emac_device.dev);
+
 	return 0;
 }
 postcore_initcall(dm646x_init_devices);
diff --git a/arch/arm/mach-davinci/include/mach/dm365.h b/arch/arm/mach-davinci/include/mach/dm365.h
index ea5df3b..dbb5052 100644
--- a/arch/arm/mach-davinci/include/mach/dm365.h
+++ b/arch/arm/mach-davinci/include/mach/dm365.h
@@ -21,6 +21,7 @@
 #include <media/davinci/vpfe_capture.h>
 
 #define DM365_EMAC_BASE			(0x01D07000)
+#define DM365_EMAC_MDIO_BASE		(DM365_EMAC_BASE + 0x4000)
 #define DM365_EMAC_CNTRL_OFFSET		(0x0000)
 #define DM365_EMAC_CNTRL_MOD_OFFSET	(0x3000)
 #define DM365_EMAC_CNTRL_RAM_OFFSET	(0x1000)
diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h
index 6fca568..5159117 100644
--- a/arch/arm/mach-davinci/include/mach/dm644x.h
+++ b/arch/arm/mach-davinci/include/mach/dm644x.h
@@ -28,6 +28,7 @@
 #include <media/davinci/vpfe_capture.h>
 
 #define DM644X_EMAC_BASE		(0x01C80000)
+#define DM644X_EMAC_MDIO_BASE		(DM644X_EMAC_BASE + 0x4000)
 #define DM644X_EMAC_CNTRL_OFFSET	(0x0000)
 #define DM644X_EMAC_CNTRL_MOD_OFFSET	(0x1000)
 #define DM644X_EMAC_CNTRL_RAM_OFFSET	(0x2000)
diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h
index add6f79..fc2bdd0 100644
--- a/arch/arm/mach-davinci/include/mach/dm646x.h
+++ b/arch/arm/mach-davinci/include/mach/dm646x.h
@@ -19,6 +19,7 @@
 #include <linux/davinci_emac.h>
 
 #define DM646X_EMAC_BASE		(0x01C80000)
+#define DM646X_EMAC_MDIO_BASE		(DM646X_EMAC_BASE + 0x4000)
 #define DM646X_EMAC_CNTRL_OFFSET	(0x0000)
 #define DM646X_EMAC_CNTRL_MOD_OFFSET	(0x1000)
 #define DM646X_EMAC_CNTRL_RAM_OFFSET	(0x2000)
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 3/8] netdev: switch davinci emac to new mdio driver
From: Cyril Chemparathy @ 2010-08-03 13:31 UTC (permalink / raw)
  To: netdev, davinci-linux-open-source, linux-omap
  Cc: nsekhar, srk, Cyril Chemparathy
In-Reply-To: <1280842313-3743-1-git-send-email-cyril@ti.com>

This patch switches the emac implementation over to the newly separated
MDIO driver.

With this, the mdio bus frequency defaults to a safe 2.2MHz.  Boards may
optionally specify a bus frequency via platform data.

The phy identification scheme has been modified to use a phy bus id instead
of a mask.  This largely serves to eliminate the "phy search" code in emac
init.

Signed-off-by: Cyril Chemparathy <cyril@ti.com>
---
 drivers/net/Kconfig          |    1 +
 drivers/net/davinci_emac.c   |   89 ++++++++++++++---------------------------
 include/linux/davinci_emac.h |    1 +
 3 files changed, 33 insertions(+), 58 deletions(-)

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index fe6658a..3c324c2 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -940,6 +940,7 @@ config NET_NETX
 config TI_DAVINCI_EMAC
 	tristate "TI DaVinci EMAC Support"
 	depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
+	select TI_DAVINCI_MDIO
 	select PHYLIB
 	help
 	  This driver supports TI's DaVinci Ethernet .
diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c
index 08e82b1..8e21a42 100644
--- a/drivers/net/davinci_emac.c
+++ b/drivers/net/davinci_emac.c
@@ -482,6 +482,7 @@ struct emac_priv {
 	u32 phy_mask;
 	/* mii_bus,phy members */
 	struct mii_bus *mii_bus;
+	const char *phy_id;
 	struct phy_device *phydev;
 	spinlock_t lock;
 	/*platform specific members*/
@@ -666,7 +667,7 @@ static int emac_get_settings(struct net_device *ndev,
 			     struct ethtool_cmd *ecmd)
 {
 	struct emac_priv *priv = netdev_priv(ndev);
-	if (priv->phy_mask)
+	if (priv->phydev)
 		return phy_ethtool_gset(priv->phydev, ecmd);
 	else
 		return -EOPNOTSUPP;
@@ -684,7 +685,7 @@ static int emac_get_settings(struct net_device *ndev,
 static int emac_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
 {
 	struct emac_priv *priv = netdev_priv(ndev);
-	if (priv->phy_mask)
+	if (priv->phydev)
 		return phy_ethtool_sset(priv->phydev, ecmd);
 	else
 		return -EOPNOTSUPP;
@@ -722,7 +723,7 @@ static void emac_update_phystatus(struct emac_priv *priv)
 	mac_control = emac_read(EMAC_MACCONTROL);
 	cur_duplex = (mac_control & EMAC_MACCONTROL_FULLDUPLEXEN) ?
 			DUPLEX_FULL : DUPLEX_HALF;
-	if (priv->phy_mask)
+	if (priv->phydev)
 		new_duplex = priv->phydev->duplex;
 	else
 		new_duplex = DUPLEX_FULL;
@@ -2370,6 +2371,11 @@ static int emac_devioctl(struct net_device *ndev, struct ifreq *ifrq, int cmd)
 	return -EOPNOTSUPP;
 }
 
+static int match_first_device(struct device *dev, void *data)
+{
+	return 1;
+}
+
 /**
  * emac_dev_open: EMAC device open
  * @ndev: The DaVinci EMAC network adapter
@@ -2384,7 +2390,6 @@ static int emac_dev_open(struct net_device *ndev)
 {
 	struct device *emac_dev = &ndev->dev;
 	u32 rc, cnt, ch;
-	int phy_addr;
 	struct resource *res;
 	int q, m;
 	int i = 0;
@@ -2437,28 +2442,27 @@ static int emac_dev_open(struct net_device *ndev)
 	/* Start/Enable EMAC hardware */
 	emac_hw_enable(priv);
 
-	/* find the first phy */
 	priv->phydev = NULL;
-	if (priv->phy_mask) {
-		emac_mii_reset(priv->mii_bus);
-		for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
-			if (priv->mii_bus->phy_map[phy_addr]) {
-				priv->phydev = priv->mii_bus->phy_map[phy_addr];
-				break;
-			}
-		}
 
-		if (!priv->phydev) {
-			printk(KERN_ERR "%s: no PHY found\n", ndev->name);
-			return -1;
-		}
+	/* use the first phy on the bus if pdata did not give us a phy id */
+	if (!priv->phy_id) {
+		struct device *phy;
+
+		phy = bus_find_device(&mdio_bus_type, NULL, NULL,
+				      match_first_device);
+		if (phy)
+			priv->phy_id = dev_name(phy);
+	}
 
-		priv->phydev = phy_connect(ndev, dev_name(&priv->phydev->dev),
-				&emac_adjust_link, 0, PHY_INTERFACE_MODE_MII);
+	if (priv->phy_id) {
+		priv->phydev = phy_connect(ndev, priv->phy_id,
+					   &emac_adjust_link, 0,
+					   PHY_INTERFACE_MODE_MII);
 
 		if (IS_ERR(priv->phydev)) {
-			printk(KERN_ERR "%s: Could not attach to PHY\n",
-								ndev->name);
+			dev_err(emac_dev, "could not connect to phy %s\n",
+				priv->phy_id);
+			priv->phydev = NULL;
 			return PTR_ERR(priv->phydev);
 		}
 
@@ -2466,12 +2470,13 @@ static int emac_dev_open(struct net_device *ndev)
 		priv->speed = 0;
 		priv->duplex = ~0;
 
-		printk(KERN_INFO "%s: attached PHY driver [%s] "
-			"(mii_bus:phy_addr=%s, id=%x)\n", ndev->name,
+		dev_info(emac_dev, "attached PHY driver [%s] "
+			"(mii_bus:phy_addr=%s, id=%x)\n",
 			priv->phydev->drv->name, dev_name(&priv->phydev->dev),
 			priv->phydev->phy_id);
-	} else{
+	} else {
 		/* No PHY , fix the link, speed and duplex settings */
+		dev_notice(emac_dev, "no phy, defaulting to 100/full\n");
 		priv->link = 1;
 		priv->speed = SPEED_100;
 		priv->duplex = DUPLEX_FULL;
@@ -2484,7 +2489,7 @@ static int emac_dev_open(struct net_device *ndev)
 	if (netif_msg_drv(priv))
 		dev_notice(emac_dev, "DaVinci EMAC: Opened %s\n", ndev->name);
 
-	if (priv->phy_mask)
+	if (priv->phydev)
 		phy_start(priv->phydev);
 
 	return 0;
@@ -2671,7 +2676,7 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
 
 	/* MAC addr and PHY mask , RMII enable info from platform_data */
 	memcpy(priv->mac_addr, pdata->mac_addr, 6);
-	priv->phy_mask = pdata->phy_mask;
+	priv->phy_id = pdata->phy_id;
 	priv->rmii_en = pdata->rmii_en;
 	priv->version = pdata->version;
 	priv->int_enable = pdata->interrupt_enable;
@@ -2745,32 +2750,6 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
 	}
 
 
-	/* MII/Phy intialisation, mdio bus registration */
-	emac_mii = mdiobus_alloc();
-	if (emac_mii == NULL) {
-		dev_err(emac_dev, "DaVinci EMAC: Error allocating mii_bus\n");
-		rc = -ENOMEM;
-		goto mdio_alloc_err;
-	}
-
-	priv->mii_bus = emac_mii;
-	emac_mii->name  = "emac-mii",
-	emac_mii->read  = emac_mii_read,
-	emac_mii->write = emac_mii_write,
-	emac_mii->reset = emac_mii_reset,
-	emac_mii->irq   = mii_irqs,
-	emac_mii->phy_mask = ~(priv->phy_mask);
-	emac_mii->parent = &pdev->dev;
-	emac_mii->priv = priv->remap_addr + pdata->mdio_reg_offset;
-	snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%x", priv->pdev->id);
-	mdio_max_freq = pdata->mdio_max_freq;
-	emac_mii->reset(emac_mii);
-
-	/* Register the MII bus */
-	rc = mdiobus_register(emac_mii);
-	if (rc)
-		goto mdiobus_quit;
-
 	if (netif_msg_probe(priv)) {
 		dev_notice(emac_dev, "DaVinci EMAC Probe found device "\
 			   "(regs: %p, irq: %d)\n",
@@ -2778,11 +2757,7 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
 	}
 	return 0;
 
-mdiobus_quit:
-	mdiobus_free(emac_mii);
-
 netdev_reg_err:
-mdio_alloc_err:
 	clk_disable(emac_clk);
 no_irq_res:
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -2812,8 +2787,6 @@ static int __devexit davinci_emac_remove(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, NULL);
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	mdiobus_unregister(priv->mii_bus);
-	mdiobus_free(priv->mii_bus);
 
 	release_mem_region(res->start, res->end - res->start + 1);
 
diff --git a/include/linux/davinci_emac.h b/include/linux/davinci_emac.h
index a04fd8c..6bcd247 100644
--- a/include/linux/davinci_emac.h
+++ b/include/linux/davinci_emac.h
@@ -28,6 +28,7 @@ struct emac_platform_data {
 	u32 ctrl_ram_size;
 	u32 phy_mask;
 	u32 mdio_max_freq;
+	const char *phy_id;
 	u8 rmii_en;
 	u8 version;
 	void (*interrupt_enable) (void);
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 7/8] netdev: switch davinci emac to new cpdma layer
From: Cyril Chemparathy @ 2010-08-03 13:31 UTC (permalink / raw)
  To: netdev, davinci-linux-open-source, linux-omap
  Cc: nsekhar, srk, Cyril Chemparathy
In-Reply-To: <1280842313-3743-1-git-send-email-cyril@ti.com>

This patch hooks up the emac driver with the newly separated cpdma driver.

Key differences introduced here:

 - The old buffer list scheme is no longer required

 - The original code maintained mac address per rx channel, even if only one
   rx channel was being used.  With this change, mac address is maintained
   device wide.  If support for multiple rx channels is added in future, this
   will need to be reworked a bit.

 - The new CPDMA code handles short packets better than before.  The
   earlier code was adjusting the length up, without ensuring that the tail
   end of the padding was cleared - a possible security issue.  This has been
   fixed to use skb_padto().

Signed-off-by: Cyril Chemparathy <cyril@ti.com>
---
 drivers/net/davinci_emac.c |  238 ++++++++++++++++++++++++++++----------------
 1 files changed, 150 insertions(+), 88 deletions(-)

diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c
index 33dc8b5..3efa42d 100644
--- a/drivers/net/davinci_emac.c
+++ b/drivers/net/davinci_emac.c
@@ -63,6 +63,8 @@
 #include <asm/irq.h>
 #include <asm/page.h>
 
+#include "davinci_cpdma.h"
+
 static int debug_level;
 module_param(debug_level, int, 0);
 MODULE_PARM_DESC(debug_level, "DaVinci EMAC debug level (NETIF_MSG bits)");
@@ -113,6 +115,7 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
 #define EMAC_DEF_MAX_FRAME_SIZE		(1500 + 14 + 4 + 4)
 #define EMAC_DEF_TX_CH			(0) /* Default 0th channel */
 #define EMAC_DEF_RX_CH			(0) /* Default 0th channel */
+#define EMAC_DEF_RX_NUM_DESC		(128)
 #define EMAC_DEF_MAX_TX_CH		(1) /* Max TX channels configured */
 #define EMAC_DEF_MAX_RX_CH		(1) /* Max RX channels configured */
 #define EMAC_POLL_WEIGHT		(64) /* Default NAPI poll weight */
@@ -443,6 +446,9 @@ struct emac_priv {
 	u32 hw_ram_addr;
 	struct emac_txch *txch[EMAC_DEF_MAX_TX_CH];
 	struct emac_rxch *rxch[EMAC_DEF_MAX_RX_CH];
+	struct cpdma_ctlr *dma;
+	struct cpdma_chan *txchan;
+	struct cpdma_chan *rxchan;
 	u32 link; /* 1=link on, 0=link off */
 	u32 speed; /* 0=Auto Neg, 1=No PHY, 10,100, 1000 - mbps */
 	u32 duplex; /* Link duplex: 0=Half, 1=Full */
@@ -1032,6 +1038,58 @@ static irqreturn_t emac_irq(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static struct sk_buff *emac_rx_alloc(struct emac_priv *priv)
+{
+	struct sk_buff *skb = dev_alloc_skb(priv->rx_buf_size);
+	if (WARN_ON(!skb))
+		return NULL;
+	skb->dev = priv->ndev;
+	skb_reserve(skb, NET_IP_ALIGN);
+	return skb;
+}
+
+static void emac_rx_handler(void *token, int len, int status)
+{
+	struct sk_buff		*skb = token;
+	struct net_device	*ndev = skb->dev;
+	struct emac_priv	*priv = netdev_priv(ndev);
+	int			ret;
+
+	if (status < 0) {
+		/* error */
+		goto recycle;
+	}
+
+	skb_put(skb, len);
+	skb->protocol = eth_type_trans(skb, ndev);
+	netif_receive_skb(skb);
+	priv->net_dev_stats.rx_bytes += len;
+	priv->net_dev_stats.rx_packets++;
+
+	/* alloc a new packet for receive */
+	skb = emac_rx_alloc(priv);
+
+recycle:
+	if (skb) {
+		ret = cpdma_chan_submit(priv->rxchan, skb, skb->data,
+					skb_tailroom(skb), GFP_KERNEL);
+		WARN_ON(ret < 0);
+	}
+}
+
+static void emac_tx_handler(void *token, int len, int status)
+{
+	struct sk_buff		*skb = token;
+	struct net_device	*ndev = skb->dev;
+	struct emac_priv	*priv = netdev_priv(ndev);
+
+	if (unlikely(netif_queue_stopped(ndev)))
+		netif_start_queue(ndev);
+	priv->net_dev_stats.tx_packets++;
+	priv->net_dev_stats.tx_bytes += len;
+	dev_kfree_skb_any(skb);
+}
+
 /** EMAC on-chip buffer descriptor memory
  *
  * WARNING: Please note that the on chip memory is used for both TX and RX
@@ -1412,42 +1470,36 @@ static int emac_dev_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
 	struct device *emac_dev = &ndev->dev;
 	int ret_code;
-	struct emac_netbufobj tx_buf; /* buffer obj-only single frame support */
-	struct emac_netpktobj tx_packet;  /* packet object */
 	struct emac_priv *priv = netdev_priv(ndev);
 
 	/* If no link, return */
 	if (unlikely(!priv->link)) {
 		if (netif_msg_tx_err(priv) && net_ratelimit())
 			dev_err(emac_dev, "DaVinci EMAC: No link to transmit");
-		return NETDEV_TX_BUSY;
+		goto fail_tx;
 	}
 
-	/* Build the buffer and packet objects - Since only single fragment is
-	 * supported, need not set length and token in both packet & object.
-	 * Doing so for completeness sake & to show that this needs to be done
-	 * in multifragment case
-	 */
-	tx_packet.buf_list = &tx_buf;
-	tx_packet.num_bufs = 1; /* only single fragment supported */
-	tx_packet.pkt_length = skb->len;
-	tx_packet.pkt_token = (void *)skb;
-	tx_buf.length = skb->len;
-	tx_buf.buf_token = (void *)skb;
-	tx_buf.data_ptr = skb->data;
-	ret_code = emac_send(priv, &tx_packet, EMAC_DEF_TX_CH);
+	ret_code = skb_padto(skb, EMAC_DEF_MIN_ETHPKTSIZE);
+	if (unlikely(ret_code < 0)) {
+		if (netif_msg_tx_err(priv) && net_ratelimit())
+			dev_err(emac_dev, "DaVinci EMAC: packet pad failed");
+		goto fail_tx;
+	}
+
+	ret_code = cpdma_chan_submit(priv->txchan, skb, skb->data, skb->len,
+				     GFP_KERNEL);
 	if (unlikely(ret_code != 0)) {
-		if (ret_code == EMAC_ERR_TX_OUT_OF_BD) {
-			if (netif_msg_tx_err(priv) && net_ratelimit())
-				dev_err(emac_dev, "DaVinci EMAC: xmit() fatal"\
-					" err. Out of TX BD's");
-			netif_stop_queue(priv->ndev);
-		}
-		priv->net_dev_stats.tx_dropped++;
-		return NETDEV_TX_BUSY;
+		if (netif_msg_tx_err(priv) && net_ratelimit())
+			dev_err(emac_dev, "DaVinci EMAC: desc submit failed");
+		goto fail_tx;
 	}
 
 	return NETDEV_TX_OK;
+
+fail_tx:
+	priv->net_dev_stats.tx_dropped++;
+	netif_stop_queue(priv->ndev);
+	return NETDEV_TX_BUSY;
 }
 
 /**
@@ -1470,11 +1522,8 @@ static void emac_dev_tx_timeout(struct net_device *ndev)
 
 	priv->net_dev_stats.tx_errors++;
 	emac_int_disable(priv);
-	emac_stop_txch(priv, EMAC_DEF_TX_CH);
-	emac_cleanup_txch(priv, EMAC_DEF_TX_CH);
-	emac_init_txch(priv, EMAC_DEF_TX_CH);
-	emac_write(EMAC_TXHDP(0), 0);
-	emac_write(EMAC_TXINTMASKSET, BIT(EMAC_DEF_TX_CH));
+	cpdma_chan_stop(priv->txchan);
+	cpdma_chan_start(priv->txchan);
 	emac_int_enable(priv);
 }
 
@@ -1795,7 +1844,6 @@ static void emac_setmac(struct emac_priv *priv, u32 ch, char *mac_addr)
 static int emac_dev_setmac_addr(struct net_device *ndev, void *addr)
 {
 	struct emac_priv *priv = netdev_priv(ndev);
-	struct emac_rxch *rxch = priv->rxch[EMAC_DEF_RX_CH];
 	struct device *emac_dev = &priv->ndev->dev;
 	struct sockaddr *sa = addr;
 
@@ -1806,11 +1854,10 @@ static int emac_dev_setmac_addr(struct net_device *ndev, void *addr)
 	memcpy(priv->mac_addr, sa->sa_data, ndev->addr_len);
 	memcpy(ndev->dev_addr, sa->sa_data, ndev->addr_len);
 
-	/* If the interface is down - rxch is NULL. */
 	/* MAC address is configured only after the interface is enabled. */
 	if (netif_running(ndev)) {
-		memcpy(rxch->mac_addr, sa->sa_data, ndev->addr_len);
-		emac_setmac(priv, EMAC_DEF_RX_CH, rxch->mac_addr);
+		memcpy(priv->mac_addr, sa->sa_data, ndev->addr_len);
+		emac_setmac(priv, EMAC_DEF_RX_CH, priv->mac_addr);
 	}
 
 	if (netif_msg_drv(priv))
@@ -2019,7 +2066,7 @@ end_emac_rx_bdproc:
  */
 static int emac_hw_enable(struct emac_priv *priv)
 {
-	u32 ch, val, mbp_enable, mac_control;
+	u32 val, mbp_enable, mac_control;
 
 	/* Soft reset */
 	emac_write(EMAC_SOFTRESET, 1);
@@ -2062,26 +2109,9 @@ static int emac_hw_enable(struct emac_priv *priv)
 	emac_write(EMAC_RXUNICASTCLEAR, EMAC_RX_UNICAST_CLEAR_ALL);
 	priv->rx_addr_type = (emac_read(EMAC_MACCONFIG) >> 8) & 0xFF;
 
-	val = emac_read(EMAC_TXCONTROL);
-	val |= EMAC_TX_CONTROL_TX_ENABLE_VAL;
-	emac_write(EMAC_TXCONTROL, val);
-	val = emac_read(EMAC_RXCONTROL);
-	val |= EMAC_RX_CONTROL_RX_ENABLE_VAL;
-	emac_write(EMAC_RXCONTROL, val);
 	emac_write(EMAC_MACINTMASKSET, EMAC_MAC_HOST_ERR_INTMASK_VAL);
 
-	for (ch = 0; ch < EMAC_DEF_MAX_TX_CH; ch++) {
-		emac_write(EMAC_TXHDP(ch), 0);
-		emac_write(EMAC_TXINTMASKSET, BIT(ch));
-	}
-	for (ch = 0; ch < EMAC_DEF_MAX_RX_CH; ch++) {
-		struct emac_rxch *rxch = priv->rxch[ch];
-		emac_setmac(priv, ch, rxch->mac_addr);
-		emac_write(EMAC_RXINTMASKSET, BIT(ch));
-		rxch->queue_active = 1;
-		emac_write(EMAC_RXHDP(ch),
-			   emac_virt_to_phys(rxch->active_queue_head, priv));
-	}
+	emac_setmac(priv, EMAC_DEF_RX_CH, priv->mac_addr);
 
 	/* Enable MII */
 	val = emac_read(EMAC_MACCONTROL);
@@ -2126,9 +2156,9 @@ static int emac_poll(struct napi_struct *napi, int budget)
 		mask = EMAC_DM646X_MAC_IN_VECTOR_TX_INT_VEC;
 
 	if (status & mask) {
-		num_pkts = emac_tx_bdproc(priv, EMAC_DEF_TX_CH,
-					  EMAC_DEF_TX_MAX_SERVICE);
-	} /* TX processing */
+		num_pkts = cpdma_chan_process(priv->txchan,
+					      EMAC_DEF_TX_MAX_SERVICE);
+	}
 
 	if (num_pkts)
 		return budget;
@@ -2138,9 +2168,8 @@ static int emac_poll(struct napi_struct *napi, int budget)
 	if (priv->version == EMAC_VERSION_2)
 		mask = EMAC_DM646X_MAC_IN_VECTOR_RX_INT_VEC;
 
-	if (status & mask) {
-		num_pkts = emac_rx_bdproc(priv, EMAC_DEF_RX_CH, budget);
-	} /* RX processing */
+	if (status & mask)
+		num_pkts = cpdma_chan_process(priv->rxchan, budget);
 
 	if (num_pkts < budget) {
 		napi_complete(napi);
@@ -2282,9 +2311,9 @@ static int match_first_device(struct device *dev, void *data)
 static int emac_dev_open(struct net_device *ndev)
 {
 	struct device *emac_dev = &ndev->dev;
-	u32 rc, cnt, ch;
+	u32 cnt;
 	struct resource *res;
-	int q, m;
+	int q, m, ret;
 	int i = 0;
 	int k = 0;
 	struct emac_priv *priv = netdev_priv(ndev);
@@ -2296,29 +2325,21 @@ static int emac_dev_open(struct net_device *ndev)
 	/* Configuration items */
 	priv->rx_buf_size = EMAC_DEF_MAX_FRAME_SIZE + NET_IP_ALIGN;
 
-	/* Clear basic hardware */
-	for (ch = 0; ch < EMAC_MAX_TXRX_CHANNELS; ch++) {
-		emac_write(EMAC_TXHDP(ch), 0);
-		emac_write(EMAC_RXHDP(ch), 0);
-		emac_write(EMAC_RXHDP(ch), 0);
-		emac_write(EMAC_RXINTMASKCLEAR, EMAC_INT_MASK_CLEAR);
-		emac_write(EMAC_TXINTMASKCLEAR, EMAC_INT_MASK_CLEAR);
-	}
 	priv->mac_hash1 = 0;
 	priv->mac_hash2 = 0;
 	emac_write(EMAC_MACHASH1, 0);
 	emac_write(EMAC_MACHASH2, 0);
 
-	/* multi ch not supported - open 1 TX, 1RX ch by default */
-	rc = emac_init_txch(priv, EMAC_DEF_TX_CH);
-	if (0 != rc) {
-		dev_err(emac_dev, "DaVinci EMAC: emac_init_txch() failed");
-		return rc;
-	}
-	rc = emac_init_rxch(priv, EMAC_DEF_RX_CH, priv->mac_addr);
-	if (0 != rc) {
-		dev_err(emac_dev, "DaVinci EMAC: emac_init_rxch() failed");
-		return rc;
+	for (i = 0; i < EMAC_DEF_RX_NUM_DESC; i++) {
+		struct sk_buff *skb = emac_rx_alloc(priv);
+
+		if (!skb)
+			break;
+
+		ret = cpdma_chan_submit(priv->rxchan, skb, skb->data,
+					skb_tailroom(skb), GFP_KERNEL);
+		if (WARN_ON(ret < 0))
+			break;
 	}
 
 	/* Request IRQ */
@@ -2335,6 +2356,8 @@ static int emac_dev_open(struct net_device *ndev)
 	/* Start/Enable EMAC hardware */
 	emac_hw_enable(priv);
 
+	cpdma_ctlr_start(priv->dma);
+
 	priv->phydev = NULL;
 
 	/* use the first phy on the bus if pdata did not give us a phy id */
@@ -2423,10 +2446,7 @@ static int emac_dev_stop(struct net_device *ndev)
 
 	netif_carrier_off(ndev);
 	emac_int_disable(priv);
-	emac_stop_txch(priv, EMAC_DEF_TX_CH);
-	emac_stop_rxch(priv, EMAC_DEF_RX_CH);
-	emac_cleanup_txch(priv, EMAC_DEF_TX_CH);
-	emac_cleanup_rxch(priv, EMAC_DEF_RX_CH);
+	cpdma_ctlr_stop(priv->dma);
 	emac_write(EMAC_SOFTRESET, 1);
 
 	if (priv->phydev)
@@ -2531,9 +2551,10 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
 	struct resource *res;
 	struct net_device *ndev;
 	struct emac_priv *priv;
-	unsigned long size;
+	unsigned long size, hw_ram_addr;
 	struct emac_platform_data *pdata;
 	struct device *emac_dev;
+	struct cpdma_params dma_params;
 
 	/* obtain emac clock from kernel */
 	emac_clk = clk_get(&pdev->dev, NULL);
@@ -2606,11 +2627,40 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
 	priv->ctrl_ram_size = pdata->ctrl_ram_size;
 	priv->emac_ctrl_ram = priv->remap_addr + pdata->ctrl_ram_offset;
 
-	if (pdata->hw_ram_addr)
-		priv->hw_ram_addr = pdata->hw_ram_addr;
-	else
-		priv->hw_ram_addr = (u32 __force)res->start +
-					pdata->ctrl_ram_offset;
+	hw_ram_addr = pdata->hw_ram_addr;
+	if (!hw_ram_addr)
+		hw_ram_addr = (u32 __force)res->start + pdata->ctrl_ram_offset;
+
+	memset(&dma_params, 0, sizeof(dma_params));
+	dma_params.dev			= emac_dev;
+	dma_params.dmaregs		= priv->emac_base;
+	dma_params.rxthresh		= priv->emac_base + 0x120;
+	dma_params.rxfree		= priv->emac_base + 0x140;
+	dma_params.txhdp		= priv->emac_base + 0x600;
+	dma_params.rxhdp		= priv->emac_base + 0x620;
+	dma_params.txcp			= priv->emac_base + 0x640;
+	dma_params.rxcp			= priv->emac_base + 0x660;
+	dma_params.num_chan		= EMAC_MAX_TXRX_CHANNELS;
+	dma_params.min_packet_size	= EMAC_DEF_MIN_ETHPKTSIZE;
+	dma_params.desc_mem_phys	= hw_ram_addr;
+	dma_params.desc_mem_size	= pdata->ctrl_ram_size;
+	dma_params.desc_align		= 16;
+
+	priv->dma = cpdma_ctlr_create(&dma_params);
+	if (!priv->dma) {
+		dev_err(emac_dev, "DaVinci EMAC: Error initializing DMA\n");
+		rc = -ENOMEM;
+		goto no_dma;
+	}
+
+	priv->txchan = cpdma_chan_create(priv->dma, tx_chan_num(EMAC_DEF_TX_CH),
+				       emac_tx_handler);
+	priv->rxchan = cpdma_chan_create(priv->dma, rx_chan_num(EMAC_DEF_RX_CH),
+				       emac_rx_handler);
+	if (WARN_ON(!priv->txchan || !priv->rxchan)) {
+		rc = -ENOMEM;
+		goto no_irq_res;
+	}
 
 	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 	if (!res) {
@@ -2653,6 +2703,12 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
 netdev_reg_err:
 	clk_disable(emac_clk);
 no_irq_res:
+	if (priv->txchan)
+		cpdma_chan_destroy(priv->txchan);
+	if (priv->rxchan)
+		cpdma_chan_destroy(priv->rxchan);
+	cpdma_ctlr_destroy(priv->dma);
+no_dma:
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	release_mem_region(res->start, res->end - res->start + 1);
 	iounmap(priv->remap_addr);
@@ -2681,6 +2737,12 @@ static int __devexit davinci_emac_remove(struct platform_device *pdev)
 	platform_set_drvdata(pdev, NULL);
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
+	if (priv->txchan)
+		cpdma_chan_destroy(priv->txchan);
+	if (priv->rxchan)
+		cpdma_chan_destroy(priv->rxchan);
+	cpdma_ctlr_destroy(priv->dma);
+
 	release_mem_region(res->start, res->end - res->start + 1);
 
 	unregister_netdev(ndev);
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 6/8] netdev: separate out davinci cpdma controller code
From: Cyril Chemparathy @ 2010-08-03 13:31 UTC (permalink / raw)
  To: netdev, davinci-linux-open-source, linux-omap
  Cc: nsekhar, srk, Cyril Chemparathy
In-Reply-To: <1280842313-3743-1-git-send-email-cyril@ti.com>

In addition to being embedded into the EMAC controller, the CPDMA hardware
block is used in TI's CPSW switch controller.  Fortunately, the programming
interface to this hardware block remains pretty nicely consistent across these
devices.

This patch adds a new CPDMA services layer, which can then be reused across
EMAC and CPSW drivers.

Signed-off-by: Cyril Chemparathy <cyril@ti.com>
---
 drivers/net/Kconfig         |   10 +
 drivers/net/Makefile        |    1 +
 drivers/net/davinci_cpdma.c |  836 +++++++++++++++++++++++++++++++++++++++++++
 drivers/net/davinci_cpdma.h |  105 ++++++
 4 files changed, 952 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/davinci_cpdma.c
 create mode 100644 drivers/net/davinci_cpdma.h

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 3c324c2..462ade5 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -941,6 +941,7 @@ config TI_DAVINCI_EMAC
 	tristate "TI DaVinci EMAC Support"
 	depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
 	select TI_DAVINCI_MDIO
+	select TI_DAVINCI_CPDMA
 	select PHYLIB
 	help
 	  This driver supports TI's DaVinci Ethernet .
@@ -958,6 +959,15 @@ config TI_DAVINCI_MDIO
 	  To compile this driver as a module, choose M here: the module
 	  will be called davinci_mdio.  This is recommended.
 
+config TI_DAVINCI_CPDMA
+	tristate "TI DaVinci CPDMA Support"
+	depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
+	help
+	  This driver supports TI's DaVinci CPDMA dma engine.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called davinci_cpdma.  This is recommended.
+
 config DM9000
 	tristate "DM9000 support"
 	depends on ARM || BLACKFIN || MIPS
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 84cf18d..4780a9c 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_PHYLIB) += phy/
 
 obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac.o
 obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o
+obj-$(CONFIG_TI_DAVINCI_CPDMA) += davinci_cpdma.o
 
 obj-$(CONFIG_E1000) += e1000/
 obj-$(CONFIG_E1000E) += e1000e/
diff --git a/drivers/net/davinci_cpdma.c b/drivers/net/davinci_cpdma.c
new file mode 100644
index 0000000..acce1c1
--- /dev/null
+++ b/drivers/net/davinci_cpdma.c
@@ -0,0 +1,836 @@
+/*
+ * Texas Instruments CPDMA Driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+
+#include "davinci_cpdma.h"
+
+/* DMA Registers */
+#define CPDMA_TXIDVER		0x00
+#define CPDMA_TXCONTROL		0x04
+#define CPDMA_TXTEARDOWN	0x08
+#define CPDMA_RXIDVER		0x10
+#define CPDMA_RXCONTROL		0x14
+#define CPDMA_SOFTRESET		0x1c
+#define CPDMA_RXTEARDOWN	0x18
+#define CPDMA_TXINTSTATRAW	0x80
+#define CPDMA_TXINTSTATMASKED	0x84
+#define CPDMA_TXINTMASKSET	0x88
+#define CPDMA_TXINTMASKCLEAR	0x8c
+#define CPDMA_MACINVECTOR	0x90
+#define CPDMA_MACEOIVECTOR	0x94
+#define CPDMA_RXINTSTATRAW	0xa0
+#define CPDMA_RXINTSTATMASKED	0xa4
+#define CPDMA_RXINTMASKSET	0xa8
+#define CPDMA_RXINTMASKCLEAR	0xac
+#define CPDMA_DMAINTSTATRAW	0xb0
+#define CPDMA_DMAINTSTATMASKED	0xb4
+#define CPDMA_DMAINTMASKSET	0xb8
+#define CPDMA_DMAINTMASKCLEAR	0xbc
+#define CPDMA_DMAINT_HOSTERR	BIT(1)
+
+/* the following exist only if has_ext_regs is set */
+#define CPDMA_DMACONTROL	0x20
+#define CPDMA_DMASTATUS		0x24
+#define CPDMA_RXBUFFOFS		0x28
+#define CPDMA_EM_CONTROL	0x2c
+
+/* Descriptor mode bits */
+#define CPDMA_DESC_SOP		BIT(31)
+#define CPDMA_DESC_EOP		BIT(30)
+#define CPDMA_DESC_OWNER	BIT(29)
+#define CPDMA_DESC_EOQ		BIT(28)
+#define CPDMA_DESC_TD_COMPLETE	BIT(27)
+#define CPDMA_DESC_PASS_CRC	BIT(26)
+
+#define CPDMA_TEARDOWN_VALUE	0xfffffffc
+
+struct cpdma_desc {
+	/* hardware fields */
+	u32			hw_next;
+	u32			hw_buffer;
+	u32			hw_len;
+	u32			hw_mode;
+	/* software fields */
+	void			*sw_token;
+	u32			sw_buffer;
+	u32			sw_len;
+};
+
+struct cpdma_desc_pool {
+	u32			phys;
+	void __iomem		*iomap;		/* ioremap map */
+	void			*cpumap;	/* dma_alloc map */
+	int			desc_size, mem_size;
+	int			num_desc, used_desc;
+	unsigned long		*bitmap;
+	struct device		*dev;
+	spinlock_t		lock;
+};
+
+enum cpdma_state {
+	CPDMA_STATE_IDLE,
+	CPDMA_STATE_ACTIVE,
+	CPDMA_STATE_TEARDOWN,
+};
+
+struct cpdma_ctlr {
+	enum cpdma_state	state;
+	struct cpdma_params	params;
+	struct device		*dev;
+	struct cpdma_desc_pool	*pool;
+	spinlock_t		lock;
+	struct cpdma_chan	*channels[2 * CPDMA_MAX_CHANNELS];
+};
+
+struct cpdma_chan {
+	enum cpdma_state		state;
+	struct cpdma_ctlr		*ctlr;
+	int				chan_num;
+	spinlock_t			lock;
+	struct cpdma_desc __iomem	*head, *tail;
+	int				count;
+	void __iomem			*hdp, *cp, *rxfree;
+	u32				mask;
+	cpdma_handler_fn		handler;
+	enum dma_data_direction		dir;
+	struct cpdma_chan_stats		stats;
+	/* offsets into dmaregs */
+	int	int_set, int_clear, td;
+};
+
+/* The following make access to common cpdma_ctlr params more readable */
+#define dmaregs		params.dmaregs
+#define num_chan	params.num_chan
+
+/* various accessors */
+#define dma_reg_read(ctlr, ofs)		__raw_readl((ctlr)->dmaregs + (ofs))
+#define chan_read(chan, fld)		__raw_readl((chan)->fld)
+#define desc_read(desc, fld)		__raw_readl(&(desc)->fld)
+#define dma_reg_write(ctlr, ofs, v)	__raw_writel(v, (ctlr)->dmaregs + (ofs))
+#define chan_write(chan, fld, v)	__raw_writel(v, (chan)->fld)
+#define desc_write(desc, fld, v)	__raw_writel((u32)(v), &(desc)->fld)
+
+/*
+ * Utility constructs for a cpdma descriptor pool.  Some devices (e.g. davinci
+ * emac) have dedicated on-chip memory for these descriptors.  Some other
+ * devices (e.g. cpsw switches) use plain old memory.  Descriptor pools
+ * abstract out these details
+ */
+static struct cpdma_desc_pool *
+cpdma_desc_pool_create(struct device *dev, u32 phys, int size, int align)
+{
+	int bitmap_size;
+	struct cpdma_desc_pool *pool;
+
+	pool = kzalloc(sizeof(*pool), GFP_KERNEL);
+	if (!pool)
+		return NULL;
+
+	spin_lock_init(&pool->lock);
+
+	pool->dev	= dev;
+	pool->mem_size	= size;
+	pool->desc_size	= ALIGN(sizeof(struct cpdma_desc), align);
+	pool->num_desc	= size / pool->desc_size;
+
+	bitmap_size  = (pool->num_desc / BITS_PER_LONG) * sizeof(long);
+	pool->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+	if (!pool->bitmap)
+		goto fail;
+
+	if (phys) {
+		pool->phys  = phys;
+		pool->iomap = ioremap(phys, size);
+	} else {
+		pool->cpumap = dma_alloc_coherent(dev, size, &pool->phys,
+						  GFP_KERNEL);
+		pool->iomap = (void __force __iomem *)pool->cpumap;
+	}
+
+	if (pool->iomap)
+		return pool;
+
+fail:
+	kfree(pool->bitmap);
+	kfree(pool);
+	return NULL;
+}
+
+static void cpdma_desc_pool_destroy(struct cpdma_desc_pool *pool)
+{
+	unsigned long flags;
+
+	if (!pool)
+		return;
+
+	spin_lock_irqsave(&pool->lock, flags);
+	WARN_ON(pool->used_desc);
+	kfree(pool->bitmap);
+	if (pool->cpumap) {
+		dma_free_coherent(pool->dev, pool->mem_size, pool->cpumap,
+				  pool->phys);
+	} else {
+		iounmap(pool->iomap);
+	}
+	spin_unlock_irqrestore(&pool->lock, flags);
+	kfree(pool);
+}
+
+static inline dma_addr_t desc_phys(struct cpdma_desc_pool *pool,
+		  struct cpdma_desc __iomem *desc)
+{
+	if (!desc)
+		return 0;
+	return pool->phys + (__force dma_addr_t)desc -
+			    (__force dma_addr_t)pool->iomap;
+}
+
+static inline struct cpdma_desc __iomem *
+desc_from_phys(struct cpdma_desc_pool *pool, dma_addr_t dma)
+{
+	return dma ? pool->iomap + dma - pool->phys : NULL;
+}
+
+static struct cpdma_desc __iomem *
+cpdma_desc_alloc(struct cpdma_desc_pool *pool, int num_desc)
+{
+	unsigned long flags;
+	int index;
+	struct cpdma_desc __iomem *desc = NULL;
+
+	spin_lock_irqsave(&pool->lock, flags);
+
+	index = bitmap_find_next_zero_area(pool->bitmap, pool->num_desc, 0,
+					   num_desc, 0);
+	if (index < pool->num_desc) {
+		bitmap_set(pool->bitmap, index, num_desc);
+		desc = pool->iomap + pool->desc_size * index;
+		pool->used_desc++;
+	}
+
+	spin_unlock_irqrestore(&pool->lock, flags);
+	return desc;
+}
+
+static void cpdma_desc_free(struct cpdma_desc_pool *pool,
+			    struct cpdma_desc __iomem *desc, int num_desc)
+{
+	unsigned long flags, index;
+
+	index = ((unsigned long)desc - (unsigned long)pool->iomap) /
+		pool->desc_size;
+	spin_lock_irqsave(&pool->lock, flags);
+	bitmap_clear(pool->bitmap, index, num_desc);
+	pool->used_desc--;
+	spin_unlock_irqrestore(&pool->lock, flags);
+}
+
+struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params)
+{
+	struct cpdma_ctlr *ctlr;
+
+	ctlr = kzalloc(sizeof(*ctlr), GFP_KERNEL);
+	if (!ctlr)
+		return NULL;
+
+	ctlr->state = CPDMA_STATE_IDLE;
+	ctlr->params = *params;
+	ctlr->dev = params->dev;
+	spin_lock_init(&ctlr->lock);
+
+	ctlr->pool = cpdma_desc_pool_create(ctlr->dev,
+					    ctlr->params.desc_mem_phys,
+					    ctlr->params.desc_mem_size,
+					    ctlr->params.desc_align);
+	if (!ctlr->pool) {
+		kfree(ctlr);
+		return NULL;
+	}
+
+	if (WARN_ON(ctlr->num_chan > CPDMA_MAX_CHANNELS))
+		ctlr->num_chan = CPDMA_MAX_CHANNELS;
+	return ctlr;
+}
+
+int cpdma_ctlr_start(struct cpdma_ctlr *ctlr)
+{
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&ctlr->lock, flags);
+	if (ctlr->state != CPDMA_STATE_IDLE) {
+		spin_unlock_irqrestore(&ctlr->lock, flags);
+		return -EBUSY;
+	}
+
+	if (ctlr->params.has_soft_reset) {
+		unsigned long timeout = jiffies + HZ/10;
+
+		dma_reg_write(ctlr, CPDMA_SOFTRESET, 1);
+		while (time_before(jiffies, timeout)) {
+			if (dma_reg_read(ctlr, CPDMA_SOFTRESET) == 0)
+				break;
+		}
+		WARN_ON(!time_before(jiffies, timeout));
+	}
+
+	for (i = 0; i < ctlr->num_chan; i++) {
+		__raw_writel(0, ctlr->params.txhdp + 4 * i);
+		__raw_writel(0, ctlr->params.rxhdp + 4 * i);
+		__raw_writel(0, ctlr->params.txcp + 4 * i);
+		__raw_writel(0, ctlr->params.rxcp + 4 * i);
+	}
+
+	dma_reg_write(ctlr, CPDMA_RXINTMASKCLEAR, 0xffffffff);
+	dma_reg_write(ctlr, CPDMA_TXINTMASKCLEAR, 0xffffffff);
+
+	dma_reg_write(ctlr, CPDMA_TXCONTROL, 1);
+	dma_reg_write(ctlr, CPDMA_RXCONTROL, 1);
+
+	ctlr->state = CPDMA_STATE_ACTIVE;
+
+	for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) {
+		if (ctlr->channels[i])
+			cpdma_chan_start(ctlr->channels[i]);
+	}
+	spin_unlock_irqrestore(&ctlr->lock, flags);
+	return 0;
+}
+
+int cpdma_ctlr_stop(struct cpdma_ctlr *ctlr)
+{
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&ctlr->lock, flags);
+	if (ctlr->state != CPDMA_STATE_ACTIVE) {
+		spin_unlock_irqrestore(&ctlr->lock, flags);
+		return -EINVAL;
+	}
+
+	ctlr->state = CPDMA_STATE_TEARDOWN;
+
+	for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) {
+		if (ctlr->channels[i])
+			cpdma_chan_stop(ctlr->channels[i]);
+	}
+
+	dma_reg_write(ctlr, CPDMA_RXINTMASKCLEAR, 0xffffffff);
+	dma_reg_write(ctlr, CPDMA_TXINTMASKCLEAR, 0xffffffff);
+
+	dma_reg_write(ctlr, CPDMA_TXCONTROL, 0);
+	dma_reg_write(ctlr, CPDMA_RXCONTROL, 0);
+
+	ctlr->state = CPDMA_STATE_IDLE;
+
+	spin_unlock_irqrestore(&ctlr->lock, flags);
+	return 0;
+}
+
+int cpdma_ctlr_destroy(struct cpdma_ctlr *ctlr)
+{
+	unsigned long flags;
+	int ret = 0, i;
+
+	if (!ctlr)
+		return -EINVAL;
+
+	spin_lock_irqsave(&ctlr->lock, flags);
+	if (ctlr->state != CPDMA_STATE_IDLE)
+		cpdma_ctlr_stop(ctlr);
+
+	for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) {
+		if (ctlr->channels[i])
+			cpdma_chan_destroy(ctlr->channels[i]);
+	}
+
+	cpdma_desc_pool_destroy(ctlr->pool);
+	spin_unlock_irqrestore(&ctlr->lock, flags);
+	kfree(ctlr);
+	return ret;
+}
+
+int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable)
+{
+	unsigned long flags;
+	int i, reg;
+
+	spin_lock_irqsave(&ctlr->lock, flags);
+	if (ctlr->state != CPDMA_STATE_ACTIVE) {
+		spin_unlock_irqrestore(&ctlr->lock, flags);
+		return -EINVAL;
+	}
+
+	reg = enable ? CPDMA_DMAINTMASKSET : CPDMA_DMAINTMASKCLEAR;
+	dma_reg_write(ctlr, reg, CPDMA_DMAINT_HOSTERR);
+
+	for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) {
+		if (ctlr->channels[i])
+			cpdma_chan_int_ctrl(ctlr->channels[i], enable);
+	}
+
+	spin_unlock_irqrestore(&ctlr->lock, flags);
+	return 0;
+}
+
+void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr)
+{
+	dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, 0);
+}
+
+struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
+				     cpdma_handler_fn handler)
+{
+	struct cpdma_chan *chan;
+	int ret, offset = (chan_num % CPDMA_MAX_CHANNELS) * 4;
+	unsigned long flags;
+
+	if (__chan_linear(chan_num) >= ctlr->num_chan)
+		return NULL;
+
+	ret = -ENOMEM;
+	chan = kzalloc(sizeof(*chan), GFP_KERNEL);
+	if (!chan)
+		goto err_chan_alloc;
+
+	spin_lock_irqsave(&ctlr->lock, flags);
+	ret = -EBUSY;
+	if (ctlr->channels[chan_num])
+		goto err_chan_busy;
+
+	chan->ctlr	= ctlr;
+	chan->state	= CPDMA_STATE_IDLE;
+	chan->chan_num	= chan_num;
+	chan->handler	= handler;
+
+	if (is_rx_chan(chan)) {
+		chan->hdp	= ctlr->params.rxhdp + offset;
+		chan->cp	= ctlr->params.rxcp + offset;
+		chan->rxfree	= ctlr->params.rxfree + offset;
+		chan->int_set	= CPDMA_RXINTMASKSET;
+		chan->int_clear	= CPDMA_RXINTMASKCLEAR;
+		chan->td	= CPDMA_RXTEARDOWN;
+		chan->dir	= DMA_FROM_DEVICE;
+	} else {
+		chan->hdp	= ctlr->params.txhdp + offset;
+		chan->cp	= ctlr->params.txcp + offset;
+		chan->int_set	= CPDMA_TXINTMASKSET;
+		chan->int_clear	= CPDMA_TXINTMASKCLEAR;
+		chan->td	= CPDMA_TXTEARDOWN;
+		chan->dir	= DMA_TO_DEVICE;
+	}
+	chan->mask = BIT(chan_linear(chan));
+
+	spin_lock_init(&chan->lock);
+
+	ctlr->channels[chan_num] = chan;
+	spin_unlock_irqrestore(&ctlr->lock, flags);
+	return chan;
+
+err_chan_busy:
+	spin_unlock_irqrestore(&ctlr->lock, flags);
+err_chan_alloc:
+	return ERR_PTR(ret);
+}
+
+int cpdma_chan_destroy(struct cpdma_chan *chan)
+{
+	struct cpdma_ctlr *ctlr = chan->ctlr;
+	unsigned long flags;
+
+	if (!chan)
+		return -EINVAL;
+
+	spin_lock_irqsave(&ctlr->lock, flags);
+	if (chan->state != CPDMA_STATE_IDLE)
+		cpdma_chan_stop(chan);
+	ctlr->channels[chan->chan_num] = NULL;
+	spin_unlock_irqrestore(&ctlr->lock, flags);
+	kfree(chan);
+	return 0;
+}
+
+int cpdma_chan_get_stats(struct cpdma_chan *chan,
+			 struct cpdma_chan_stats *stats)
+{
+	unsigned long flags;
+	if (!chan)
+		return -EINVAL;
+	spin_lock_irqsave(&chan->lock, flags);
+	memcpy(stats, &chan->stats, sizeof(*stats));
+	spin_unlock_irqrestore(&chan->lock, flags);
+	return 0;
+}
+
+static void __cpdma_chan_submit(struct cpdma_chan *chan,
+				struct cpdma_desc __iomem *desc)
+{
+	struct cpdma_ctlr		*ctlr = chan->ctlr;
+	struct cpdma_desc __iomem	*prev = chan->tail;
+	struct cpdma_desc_pool		*pool = ctlr->pool;
+	dma_addr_t			desc_dma;
+
+	desc_dma = desc_phys(pool, desc);
+
+	/* simple case - idle channel */
+	if (!chan->head) {
+		chan->stats.head_enqueue++;
+		chan->head = desc;
+		chan->tail = desc;
+		if (chan->state == CPDMA_STATE_ACTIVE)
+			chan_write(chan, hdp, desc_dma);
+		return;
+	}
+
+	/* first chain the descriptor at the tail of the list */
+	desc_write(prev, hw_next, desc_dma);
+	chan->tail = desc;
+	chan->stats.tail_enqueue++;
+
+	/* next check if EOQ has been triggered already */
+	if (desc_read(prev, hw_mode) & CPDMA_DESC_EOQ &&
+	    chan->state == CPDMA_STATE_ACTIVE) {
+		chan_write(chan, hdp, desc_dma);
+		chan->stats.misqueued++;
+	}
+}
+
+int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data,
+		      int len, gfp_t gfp_mask)
+{
+	struct cpdma_ctlr		*ctlr = chan->ctlr;
+	struct cpdma_desc __iomem	*desc;
+	dma_addr_t			buffer;
+	unsigned long			flags;
+	u32				mode;
+	int				ret = 0;
+
+	spin_lock_irqsave(&chan->lock, flags);
+
+	if (chan->state == CPDMA_STATE_TEARDOWN) {
+		ret = -EINVAL;
+		goto unlock_ret;
+	}
+
+	desc = cpdma_desc_alloc(ctlr->pool, 1);
+	if (!desc) {
+		chan->stats.desc_alloc_fail++;
+		ret = -ENOMEM;
+		goto unlock_ret;
+	}
+
+	if (len < ctlr->params.min_packet_size) {
+		len = ctlr->params.min_packet_size;
+		chan->stats.runt_transmit_buff++;
+	}
+
+	buffer = dma_map_single(ctlr->dev, data, len, chan->dir);
+	mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP;
+
+	desc_write(desc, hw_next,   0);
+	desc_write(desc, hw_buffer, buffer);
+	desc_write(desc, hw_len,    len);
+	desc_write(desc, hw_mode,   mode | len);
+	desc_write(desc, sw_token,  token);
+	desc_write(desc, sw_buffer, buffer);
+	desc_write(desc, sw_len,    len);
+
+	__cpdma_chan_submit(chan, desc);
+
+	if (chan->state == CPDMA_STATE_ACTIVE && chan->rxfree)
+		chan_write(chan, rxfree, 1);
+
+	chan->count++;
+
+unlock_ret:
+	spin_unlock_irqrestore(&chan->lock, flags);
+	return ret;
+}
+
+static void __cpdma_chan_free(struct cpdma_chan *chan,
+			      struct cpdma_desc __iomem *desc,
+			      int outlen, int status)
+{
+	struct cpdma_ctlr		*ctlr = chan->ctlr;
+	struct cpdma_desc_pool		*pool = ctlr->pool;
+	dma_addr_t			buff_dma;
+	int				origlen;
+	void				*token;
+
+	token      = (void *)desc_read(desc, sw_token);
+	buff_dma   = desc_read(desc, sw_buffer);
+	origlen    = desc_read(desc, sw_len);
+
+	dma_unmap_single(ctlr->dev, buff_dma, origlen, chan->dir);
+	cpdma_desc_free(pool, desc, 1);
+	(*chan->handler)(token, outlen, status);
+}
+
+static int __cpdma_chan_process(struct cpdma_chan *chan)
+{
+	struct cpdma_ctlr		*ctlr = chan->ctlr;
+	struct cpdma_desc __iomem	*desc;
+	int				status, outlen;
+	struct cpdma_desc_pool		*pool = ctlr->pool;
+	dma_addr_t			desc_dma;
+	unsigned long			flags;
+
+	spin_lock_irqsave(&chan->lock, flags);
+
+	desc = chan->head;
+	if (!desc) {
+		chan->stats.empty_dequeue++;
+		status = -ENOENT;
+		goto unlock_ret;
+	}
+	desc_dma = desc_phys(pool, desc);
+
+	status	= __raw_readl(&desc->hw_mode);
+	outlen	= status & 0x7ff;
+	if (status & CPDMA_DESC_OWNER) {
+		chan->stats.busy_dequeue++;
+		status = -EBUSY;
+		goto unlock_ret;
+	}
+	status	= status & (CPDMA_DESC_EOQ | CPDMA_DESC_TD_COMPLETE);
+
+	chan->head = desc_from_phys(pool, desc_read(desc, hw_next));
+	chan_write(chan, cp, desc_dma);
+	chan->count--;
+	chan->stats.good_dequeue++;
+
+	spin_unlock_irqrestore(&chan->lock, flags);
+
+	__cpdma_chan_free(chan, desc, outlen, status);
+	return status;
+
+unlock_ret:
+	spin_unlock_irqrestore(&chan->lock, flags);
+	return status;
+}
+
+int cpdma_chan_process(struct cpdma_chan *chan, int quota)
+{
+	int used = 0, ret = 0;
+
+	if (chan->state != CPDMA_STATE_ACTIVE)
+		return -EINVAL;
+
+	while (used < quota) {
+		ret = __cpdma_chan_process(chan);
+		if (ret < 0)
+			break;
+		used++;
+	}
+	return used;
+}
+
+int cpdma_chan_start(struct cpdma_chan *chan)
+{
+	struct cpdma_ctlr	*ctlr = chan->ctlr;
+	struct cpdma_desc_pool	*pool = ctlr->pool;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&chan->lock, flags);
+	if (chan->state != CPDMA_STATE_IDLE) {
+		spin_unlock_irqrestore(&chan->lock, flags);
+		return -EBUSY;
+	}
+	if (ctlr->state != CPDMA_STATE_ACTIVE) {
+		spin_unlock_irqrestore(&chan->lock, flags);
+		return -EINVAL;
+	}
+	dma_reg_write(ctlr, chan->int_set, chan->mask);
+	chan->state = CPDMA_STATE_ACTIVE;
+	if (chan->head) {
+		chan_write(chan, hdp, desc_phys(pool, chan->head));
+		if (chan->rxfree)
+			chan_write(chan, rxfree, chan->count);
+	}
+
+	spin_unlock_irqrestore(&chan->lock, flags);
+	return 0;
+}
+
+int cpdma_chan_stop(struct cpdma_chan *chan)
+{
+	struct cpdma_ctlr	*ctlr = chan->ctlr;
+	struct cpdma_desc_pool	*pool = ctlr->pool;
+	unsigned long		flags;
+	int			ret;
+	unsigned long		timeout;
+
+	spin_lock_irqsave(&chan->lock, flags);
+	if (chan->state != CPDMA_STATE_ACTIVE) {
+		spin_unlock_irqrestore(&chan->lock, flags);
+		return -EINVAL;
+	}
+
+	chan->state = CPDMA_STATE_TEARDOWN;
+	dma_reg_write(ctlr, chan->int_clear, chan->mask);
+
+	/* trigger teardown */
+	dma_reg_write(ctlr, chan->td, chan->chan_num);
+
+	/* wait for teardown complete */
+	timeout = jiffies + HZ/10;	/* 100 msec */
+	while (time_before(jiffies, timeout)) {
+		u32 cp = chan_read(chan, cp);
+		if ((cp & CPDMA_TEARDOWN_VALUE) == CPDMA_TEARDOWN_VALUE)
+			break;
+		cpu_relax();
+	}
+	WARN_ON(!time_before(jiffies, timeout));
+	chan_write(chan, cp, CPDMA_TEARDOWN_VALUE);
+
+	/* handle completed packets */
+	do {
+		ret = __cpdma_chan_process(chan);
+		if (ret < 0)
+			break;
+	} while ((ret & CPDMA_DESC_TD_COMPLETE) == 0);
+
+	/* remaining packets haven't been tx/rx'ed, clean them up */
+	while (chan->head) {
+		struct cpdma_desc __iomem *desc = chan->head;
+		dma_addr_t next_dma;
+
+		next_dma = desc_read(desc, hw_next);
+		chan->head = desc_from_phys(pool, next_dma);
+		chan->stats.teardown_dequeue++;
+
+		/* issue callback without locks held */
+		spin_unlock_irqrestore(&chan->lock, flags);
+		__cpdma_chan_free(chan, desc, 0, -ENOSYS);
+		spin_lock_irqsave(&chan->lock, flags);
+	}
+
+	chan->state = CPDMA_STATE_IDLE;
+	spin_unlock_irqrestore(&chan->lock, flags);
+	return 0;
+}
+
+int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chan->lock, flags);
+	if (chan->state != CPDMA_STATE_ACTIVE) {
+		spin_unlock_irqrestore(&chan->lock, flags);
+		return -EINVAL;
+	}
+
+	dma_reg_write(chan->ctlr, enable ? chan->int_set : chan->int_clear,
+		      chan->mask);
+	spin_unlock_irqrestore(&chan->lock, flags);
+
+	return 0;
+}
+
+struct cpdma_control_info {
+	u32		reg;
+	u32		shift, mask;
+	int		access;
+#define ACCESS_RO	BIT(0)
+#define ACCESS_WO	BIT(1)
+#define ACCESS_RW	(ACCESS_RO | ACCESS_WO)
+};
+
+struct cpdma_control_info controls[] = {
+	[CPDMA_CMD_IDLE]	  = {CPDMA_DMACONTROL,	3,  1,      ACCESS_WO},
+	[CPDMA_COPY_ERROR_FRAMES] = {CPDMA_DMACONTROL,	4,  1,      ACCESS_RW},
+	[CPDMA_RX_OFF_LEN_UPDATE] = {CPDMA_DMACONTROL,	2,  1,      ACCESS_RW},
+	[CPDMA_RX_OWNERSHIP_FLIP] = {CPDMA_DMACONTROL,	1,  1,      ACCESS_RW},
+	[CPDMA_TX_PRIO_FIXED]	  = {CPDMA_DMACONTROL,	0,  1,      ACCESS_RW},
+	[CPDMA_STAT_IDLE]	  = {CPDMA_DMASTATUS,	31, 1,      ACCESS_RO},
+	[CPDMA_STAT_TX_ERR_CODE]  = {CPDMA_DMASTATUS,	20, 0xf,    ACCESS_RW},
+	[CPDMA_STAT_TX_ERR_CHAN]  = {CPDMA_DMASTATUS,	16, 0x7,    ACCESS_RW},
+	[CPDMA_STAT_RX_ERR_CODE]  = {CPDMA_DMASTATUS,	12, 0xf,    ACCESS_RW},
+	[CPDMA_STAT_RX_ERR_CHAN]  = {CPDMA_DMASTATUS,	8,  0x7,    ACCESS_RW},
+	[CPDMA_RX_BUFFER_OFFSET]  = {CPDMA_RXBUFFOFS,	0,  0xffff, ACCESS_RW},
+};
+
+int cpdma_control_get(struct cpdma_ctlr *ctlr, int control)
+{
+	unsigned long flags;
+	struct cpdma_control_info *info = &controls[control];
+	int ret;
+
+	spin_lock_irqsave(&ctlr->lock, flags);
+
+	ret = -ENOTSUPP;
+	if (!ctlr->params.has_ext_regs)
+		goto unlock_ret;
+
+	ret = -EINVAL;
+	if (ctlr->state != CPDMA_STATE_ACTIVE)
+		goto unlock_ret;
+
+	ret = -ENOENT;
+	if (control < 0 || control >= ARRAY_SIZE(controls))
+		goto unlock_ret;
+
+	ret = -EPERM;
+	if ((info->access & ACCESS_RO) != ACCESS_RO)
+		goto unlock_ret;
+
+	ret = (dma_reg_read(ctlr, info->reg) >> info->shift) & info->mask;
+
+unlock_ret:
+	spin_unlock_irqrestore(&ctlr->lock, flags);
+	return ret;
+}
+
+int cpdma_control_set(struct cpdma_ctlr *ctlr, int control, int value)
+{
+	unsigned long flags;
+	struct cpdma_control_info *info = &controls[control];
+	int ret;
+	u32 val;
+
+	spin_lock_irqsave(&ctlr->lock, flags);
+
+	ret = -ENOTSUPP;
+	if (!ctlr->params.has_ext_regs)
+		goto unlock_ret;
+
+	ret = -EINVAL;
+	if (ctlr->state != CPDMA_STATE_ACTIVE)
+		goto unlock_ret;
+
+	ret = -ENOENT;
+	if (control < 0 || control >= ARRAY_SIZE(controls))
+		goto unlock_ret;
+
+	ret = -EPERM;
+	if ((info->access & ACCESS_WO) != ACCESS_WO)
+		goto unlock_ret;
+
+	val  = dma_reg_read(ctlr, info->reg);
+	val &= ~(info->mask << info->shift);
+	val |= (value & info->mask) << info->shift;
+	dma_reg_write(ctlr, info->reg, val);
+	ret = 0;
+
+unlock_ret:
+	spin_unlock_irqrestore(&ctlr->lock, flags);
+	return ret;
+}
diff --git a/drivers/net/davinci_cpdma.h b/drivers/net/davinci_cpdma.h
new file mode 100644
index 0000000..d50ee35
--- /dev/null
+++ b/drivers/net/davinci_cpdma.h
@@ -0,0 +1,105 @@
+/*
+ * Texas Instruments CPDMA Driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __DAVINCI_CPDMA_H__
+#define __DAVINCI_CPDMA_H__
+
+#define CPDMA_MAX_CHANNELS	BITS_PER_LONG
+
+#define tx_chan_num(chan)	(chan)
+#define rx_chan_num(chan)	((chan) + CPDMA_MAX_CHANNELS)
+#define is_rx_chan(chan)	((chan)->chan_num >= CPDMA_MAX_CHANNELS)
+#define is_tx_chan(chan)	(!is_rx_chan(chan))
+#define __chan_linear(chan_num)	((chan_num) & (CPDMA_MAX_CHANNELS - 1))
+#define chan_linear(chan)	__chan_linear((chan)->chan_num)
+
+struct cpdma_params {
+	struct device		*dev;
+	void __iomem		*dmaregs;
+	void __iomem		*txhdp, *rxhdp, *txcp, *rxcp;
+	void __iomem		*rxthresh, *rxfree;
+	int			num_chan;
+	bool			has_soft_reset;
+	int			min_packet_size;
+	u32			desc_mem_phys;
+	int			desc_mem_size;
+	int			desc_align;
+
+	/*
+	 * Some instances of embedded cpdma controllers have extra control and
+	 * status registers.  The following flag enables access to these
+	 * "extended" registers.
+	 */
+	bool			has_ext_regs;
+};
+
+struct cpdma_chan_stats {
+	u32			head_enqueue;
+	u32			tail_enqueue;
+	u32			pad_enqueue;
+	u32			misqueued;
+	u32			desc_alloc_fail;
+	u32			pad_alloc_fail;
+	u32			runt_receive_buff;
+	u32			runt_transmit_buff;
+	u32			empty_dequeue;
+	u32			busy_dequeue;
+	u32			good_dequeue;
+	u32			teardown_dequeue;
+};
+
+struct cpdma_ctlr;
+struct cpdma_chan;
+
+typedef void (*cpdma_handler_fn)(void *token, int len, int status);
+
+struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params);
+int cpdma_ctlr_destroy(struct cpdma_ctlr *ctlr);
+int cpdma_ctlr_start(struct cpdma_ctlr *ctlr);
+int cpdma_ctlr_stop(struct cpdma_ctlr *ctlr);
+
+struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
+				     cpdma_handler_fn handler);
+int cpdma_chan_destroy(struct cpdma_chan *chan);
+int cpdma_chan_start(struct cpdma_chan *chan);
+int cpdma_chan_stop(struct cpdma_chan *chan);
+
+int cpdma_chan_get_stats(struct cpdma_chan *chan,
+			 struct cpdma_chan_stats *stats);
+int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data,
+		      int len, gfp_t gfp_mask);
+int cpdma_chan_process(struct cpdma_chan *chan, int quota);
+
+int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable);
+void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr);
+int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable);
+
+enum cpdma_control {
+	CPDMA_CMD_IDLE,			/* write-only */
+	CPDMA_COPY_ERROR_FRAMES,	/* read-write */
+	CPDMA_RX_OFF_LEN_UPDATE,	/* read-write */
+	CPDMA_RX_OWNERSHIP_FLIP,	/* read-write */
+	CPDMA_TX_PRIO_FIXED,		/* read-write */
+	CPDMA_STAT_IDLE,		/* read-only */
+	CPDMA_STAT_TX_ERR_CHAN,		/* read-only */
+	CPDMA_STAT_TX_ERR_CODE,		/* read-only */
+	CPDMA_STAT_RX_ERR_CHAN,		/* read-only */
+	CPDMA_STAT_RX_ERR_CODE,		/* read-only */
+	CPDMA_RX_BUFFER_OFFSET,		/* read-write */
+};
+
+int cpdma_control_get(struct cpdma_ctlr *ctlr, int control);
+int cpdma_control_set(struct cpdma_ctlr *ctlr, int control, int value);
+
+#endif
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 5/8] netdev: cleanup unused davinci mdio emac code
From: Cyril Chemparathy @ 2010-08-03 13:31 UTC (permalink / raw)
  To: netdev, davinci-linux-open-source, linux-omap
  Cc: nsekhar, srk, Cyril Chemparathy
In-Reply-To: <1280842313-3743-1-git-send-email-cyril@ti.com>

This patch removes code that has been rendered useless by the previous patches
in this series.

Signed-off-by: Cyril Chemparathy <cyril@ti.com>
---
 drivers/net/davinci_emac.c   |  107 ------------------------------------------
 include/linux/davinci_emac.h |    3 -
 2 files changed, 0 insertions(+), 110 deletions(-)

diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c
index 8e21a42..33dc8b5 100644
--- a/drivers/net/davinci_emac.c
+++ b/drivers/net/davinci_emac.c
@@ -113,7 +113,6 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
 #define EMAC_DEF_MAX_FRAME_SIZE		(1500 + 14 + 4 + 4)
 #define EMAC_DEF_TX_CH			(0) /* Default 0th channel */
 #define EMAC_DEF_RX_CH			(0) /* Default 0th channel */
-#define EMAC_DEF_MDIO_TICK_MS		(10) /* typically 1 tick=1 ms) */
 #define EMAC_DEF_MAX_TX_CH		(1) /* Max TX channels configured */
 #define EMAC_DEF_MAX_RX_CH		(1) /* Max RX channels configured */
 #define EMAC_POLL_WEIGHT		(64) /* Default NAPI poll weight */
@@ -298,25 +297,6 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
 #define EMAC_CTRL_EWCTL		(0x4)
 #define EMAC_CTRL_EWINTTCNT	(0x8)
 
-/* EMAC MDIO related */
-/* Mask & Control defines */
-#define MDIO_CONTROL_CLKDIV	(0xFF)
-#define MDIO_CONTROL_ENABLE	BIT(30)
-#define MDIO_USERACCESS_GO	BIT(31)
-#define MDIO_USERACCESS_WRITE	BIT(30)
-#define MDIO_USERACCESS_READ	(0)
-#define MDIO_USERACCESS_REGADR	(0x1F << 21)
-#define MDIO_USERACCESS_PHYADR	(0x1F << 16)
-#define MDIO_USERACCESS_DATA	(0xFFFF)
-#define MDIO_USERPHYSEL_LINKSEL	BIT(7)
-#define MDIO_VER_MODID		(0xFFFF << 16)
-#define MDIO_VER_REVMAJ		(0xFF   << 8)
-#define MDIO_VER_REVMIN		(0xFF)
-
-#define MDIO_USERACCESS(inst)	(0x80 + (inst * 8))
-#define MDIO_USERPHYSEL(inst)	(0x84 + (inst * 8))
-#define MDIO_CONTROL		(0x04)
-
 /* EMAC DM646X control module registers */
 #define EMAC_DM646X_CMRXINTEN	(0x14)
 #define EMAC_DM646X_CMTXINTEN	(0x18)
@@ -475,13 +455,6 @@ struct emac_priv {
 	u32 mac_hash2;
 	u32 multicast_hash_cnt[EMAC_NUM_MULTICAST_BITS];
 	u32 rx_addr_type;
-	/* periodic timer required for MDIO polling */
-	struct timer_list periodic_timer;
-	u32 periodic_ticks;
-	u32 timer_active;
-	u32 phy_mask;
-	/* mii_bus,phy members */
-	struct mii_bus *mii_bus;
 	const char *phy_id;
 	struct phy_device *phydev;
 	spinlock_t lock;
@@ -493,7 +466,6 @@ struct emac_priv {
 /* clock frequency for EMAC */
 static struct clk *emac_clk;
 static unsigned long emac_bus_frequency;
-static unsigned long mdio_max_freq;
 
 #define emac_virt_to_phys(addr, priv) \
 	(((u32 __force)(addr) - (u32 __force)(priv->emac_ctrl_ram)) \
@@ -531,9 +503,6 @@ static char *emac_rxhost_errcodes[16] = {
 #define emac_ctrl_read(reg)	  ioread32((priv->ctrl_base + (reg)))
 #define emac_ctrl_write(reg, val) iowrite32(val, (priv->ctrl_base + (reg)))
 
-#define emac_mdio_read(reg)	  ioread32(bus->priv + (reg))
-#define emac_mdio_write(reg, val) iowrite32(val, (bus->priv + (reg)))
-
 /**
  * emac_dump_regs: Dump important EMAC registers to debug terminal
  * @priv: The DaVinci EMAC private adapter structure
@@ -637,9 +606,6 @@ static void emac_dump_regs(struct emac_priv *priv)
 		emac_read(EMAC_RXDMAOVERRUNS));
 }
 
-/*************************************************************************
- *  EMAC MDIO/Phy Functionality
- *************************************************************************/
 /**
  * emac_get_drvinfo: Get EMAC driver information
  * @ndev: The DaVinci EMAC network adapter
@@ -2234,79 +2200,6 @@ void emac_poll_controller(struct net_device *ndev)
 }
 #endif
 
-/* PHY/MII bus related */
-
-/* Wait until mdio is ready for next command */
-#define MDIO_WAIT_FOR_USER_ACCESS\
-		while ((emac_mdio_read((MDIO_USERACCESS(0))) &\
-			MDIO_USERACCESS_GO) != 0)
-
-static int emac_mii_read(struct mii_bus *bus, int phy_id, int phy_reg)
-{
-	unsigned int phy_data = 0;
-	unsigned int phy_control;
-
-	/* Wait until mdio is ready for next command */
-	MDIO_WAIT_FOR_USER_ACCESS;
-
-	phy_control = (MDIO_USERACCESS_GO |
-		       MDIO_USERACCESS_READ |
-		       ((phy_reg << 21) & MDIO_USERACCESS_REGADR) |
-		       ((phy_id << 16) & MDIO_USERACCESS_PHYADR) |
-		       (phy_data & MDIO_USERACCESS_DATA));
-	emac_mdio_write(MDIO_USERACCESS(0), phy_control);
-
-	/* Wait until mdio is ready for next command */
-	MDIO_WAIT_FOR_USER_ACCESS;
-
-	return emac_mdio_read(MDIO_USERACCESS(0)) & MDIO_USERACCESS_DATA;
-
-}
-
-static int emac_mii_write(struct mii_bus *bus, int phy_id,
-			  int phy_reg, u16 phy_data)
-{
-
-	unsigned int control;
-
-	/*  until mdio is ready for next command */
-	MDIO_WAIT_FOR_USER_ACCESS;
-
-	control = (MDIO_USERACCESS_GO |
-		   MDIO_USERACCESS_WRITE |
-		   ((phy_reg << 21) & MDIO_USERACCESS_REGADR) |
-		   ((phy_id << 16) & MDIO_USERACCESS_PHYADR) |
-		   (phy_data & MDIO_USERACCESS_DATA));
-	emac_mdio_write(MDIO_USERACCESS(0), control);
-
-	return 0;
-}
-
-static int emac_mii_reset(struct mii_bus *bus)
-{
-	unsigned int clk_div;
-	int mdio_bus_freq = emac_bus_frequency;
-
-	if (mdio_max_freq && mdio_bus_freq)
-		clk_div = ((mdio_bus_freq / mdio_max_freq) - 1);
-	else
-		clk_div = 0xFF;
-
-	clk_div &= MDIO_CONTROL_CLKDIV;
-
-	/* Set enable and clock divider in MDIOControl */
-	emac_mdio_write(MDIO_CONTROL, (clk_div | MDIO_CONTROL_ENABLE));
-
-	return 0;
-
-}
-
-static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, PHY_POLL };
-
-/* emac_driver: EMAC MII bus structure */
-
-static struct mii_bus *emac_mii;
-
 static void emac_adjust_link(struct net_device *ndev)
 {
 	struct emac_priv *priv = netdev_priv(ndev);
diff --git a/include/linux/davinci_emac.h b/include/linux/davinci_emac.h
index 6bcd247..7508e49 100644
--- a/include/linux/davinci_emac.h
+++ b/include/linux/davinci_emac.h
@@ -24,10 +24,7 @@ struct emac_platform_data {
 	u32 ctrl_mod_reg_offset;
 	u32 ctrl_ram_offset;
 	u32 hw_ram_addr;
-	u32 mdio_reg_offset;
 	u32 ctrl_ram_size;
-	u32 phy_mask;
-	u32 mdio_max_freq;
 	const char *phy_id;
 	u8 rmii_en;
 	u8 version;
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 4/8] davinci: cleanup unused davinci mdio arch code
From: Cyril Chemparathy @ 2010-08-03 13:31 UTC (permalink / raw)
  To: netdev, davinci-linux-open-source, linux-omap
  Cc: nsekhar, srk, Cyril Chemparathy
In-Reply-To: <1280842313-3743-1-git-send-email-cyril@ti.com>

This patch removes davinci architecture code that has now been rendered
useless by the previous patches in the MDIO separation series.

Signed-off-by: Cyril Chemparathy <cyril@ti.com>
---
 arch/arm/mach-davinci/board-da830-evm.c     |    5 -----
 arch/arm/mach-davinci/board-da850-evm.c     |    6 ------
 arch/arm/mach-davinci/board-dm365-evm.c     |    7 -------
 arch/arm/mach-davinci/board-dm644x-evm.c    |    7 -------
 arch/arm/mach-davinci/board-dm646x-evm.c    |    8 --------
 arch/arm/mach-davinci/board-neuros-osd2.c   |    7 -------
 arch/arm/mach-davinci/board-sffsdr.c        |    7 -------
 arch/arm/mach-davinci/devices-da8xx.c       |    2 --
 arch/arm/mach-davinci/dm365.c               |    1 -
 arch/arm/mach-davinci/dm644x.c              |    1 -
 arch/arm/mach-davinci/dm646x.c              |    1 -
 arch/arm/mach-davinci/include/mach/dm365.h  |    1 -
 arch/arm/mach-davinci/include/mach/dm644x.h |    1 -
 arch/arm/mach-davinci/include/mach/dm646x.h |    1 -
 14 files changed, 0 insertions(+), 55 deletions(-)

diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c
index 212d970..76e9a9b 100644
--- a/arch/arm/mach-davinci/board-da830-evm.c
+++ b/arch/arm/mach-davinci/board-da830-evm.c
@@ -30,9 +30,6 @@
 #include <mach/da8xx.h>
 #include <mach/usb.h>
 
-#define DA830_EVM_PHY_MASK		0x0
-#define DA830_EVM_MDIO_FREQUENCY	2200000	/* PHY bus frequency */
-
 /*
  * USB1 VBUS is controlled by GPIO1[15], over-current is reported on GPIO2[4].
  */
@@ -516,8 +513,6 @@ static __init void da830_evm_init(void)
 
 	da830_evm_usb_init();
 
-	soc_info->emac_pdata->phy_mask = DA830_EVM_PHY_MASK;
-	soc_info->emac_pdata->mdio_max_freq = DA830_EVM_MDIO_FREQUENCY;
 	soc_info->emac_pdata->rmii_en = 1;
 
 	ret = davinci_cfg_reg_list(da830_cpgmac_pins);
diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c
index 2ec3095..c6e39cc 100644
--- a/arch/arm/mach-davinci/board-da850-evm.c
+++ b/arch/arm/mach-davinci/board-da850-evm.c
@@ -36,9 +36,6 @@
 #include <mach/nand.h>
 #include <mach/mux.h>
 
-#define DA850_EVM_PHY_MASK		0x1
-#define DA850_EVM_MDIO_FREQUENCY	2200000 /* PHY bus frequency */
-
 #define DA850_LCD_PWR_PIN		GPIO_TO_PIN(2, 8)
 #define DA850_LCD_BL_PIN		GPIO_TO_PIN(2, 15)
 
@@ -617,9 +614,6 @@ static int __init da850_evm_config_emac(void)
 	/* Enable/Disable MII MDIO clock */
 	gpio_direction_output(DA850_MII_MDIO_CLKEN_PIN, rmii_en);
 
-	soc_info->emac_pdata->phy_mask = DA850_EVM_PHY_MASK;
-	soc_info->emac_pdata->mdio_max_freq = DA850_EVM_MDIO_FREQUENCY;
-
 	ret = da8xx_register_emac();
 	if (ret)
 		pr_warning("da850_evm_init: emac registration failed: %d\n",
diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c
index 5bb86b2..e89263e 100644
--- a/arch/arm/mach-davinci/board-dm365-evm.c
+++ b/arch/arm/mach-davinci/board-dm365-evm.c
@@ -54,9 +54,6 @@ static inline int have_tvp7002(void)
 	return 0;
 }
 
-#define DM365_EVM_PHY_MASK		(0x2)
-#define DM365_EVM_MDIO_FREQUENCY	(2200000) /* PHY bus frequency */
-
 /*
  * A MAX-II CPLD is used for various board control functions.
  */
@@ -528,16 +525,12 @@ fail:
 		/* externally mux MMC1/ENET/AIC33 to imager */
 		mux |= BIT(6) | BIT(5) | BIT(3);
 	} else {
-		struct davinci_soc_info *soc_info = &davinci_soc_info;
-
 		/* we can use MMC1 ... */
 		dm365evm_mmc_configure();
 		davinci_setup_mmc(1, &dm365evm_mmc_config);
 
 		/* ... and ENET ... */
 		dm365evm_emac_configure();
-		soc_info->emac_pdata->phy_mask = DM365_EVM_PHY_MASK;
-		soc_info->emac_pdata->mdio_max_freq = DM365_EVM_MDIO_FREQUENCY;
 		resets &= ~BIT(3);
 
 		/* ... and AIC33 */
diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c
index 34c8b41..9e8193e 100644
--- a/arch/arm/mach-davinci/board-dm644x-evm.c
+++ b/arch/arm/mach-davinci/board-dm644x-evm.c
@@ -38,9 +38,6 @@
 #include <mach/mmc.h>
 #include <mach/usb.h>
 
-#define DM644X_EVM_PHY_MASK		(0x2)
-#define DM644X_EVM_MDIO_FREQUENCY	(2200000) /* PHY bus frequency */
-
 #define LXT971_PHY_ID	(0x001378e2)
 #define LXT971_PHY_MASK	(0xfffffff0)
 
@@ -660,7 +657,6 @@ static int davinci_phy_fixup(struct phy_device *phydev)
 static __init void davinci_evm_init(void)
 {
 	struct clk *aemif_clk;
-	struct davinci_soc_info *soc_info = &davinci_soc_info;
 
 	aemif_clk = clk_get(NULL, "aemif");
 	clk_enable(aemif_clk);
@@ -695,9 +691,6 @@ static __init void davinci_evm_init(void)
 	davinci_serial_init(&uart_config);
 	dm644x_init_asp(&dm644x_evm_snd_data);
 
-	soc_info->emac_pdata->phy_mask = DM644X_EVM_PHY_MASK;
-	soc_info->emac_pdata->mdio_max_freq = DM644X_EVM_MDIO_FREQUENCY;
-
 	/* Register the fixup for PHY on DaVinci */
 	phy_register_fixup_for_uid(LXT971_PHY_ID, LXT971_PHY_MASK,
 					davinci_phy_fixup);
diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c
index 6d88893..a3ce716 100644
--- a/arch/arm/mach-davinci/board-dm646x-evm.c
+++ b/arch/arm/mach-davinci/board-dm646x-evm.c
@@ -718,13 +718,8 @@ static struct davinci_uart_config uart_config __initdata = {
 	.enabled_uarts = (1 << 0),
 };
 
-#define DM646X_EVM_PHY_MASK		(0x2)
-#define DM646X_EVM_MDIO_FREQUENCY	(2200000) /* PHY bus frequency */
-
 static __init void evm_init(void)
 {
-	struct davinci_soc_info *soc_info = &davinci_soc_info;
-
 	evm_init_i2c();
 	davinci_serial_init(&uart_config);
 	dm646x_init_mcasp0(&dm646x_evm_snd_data[0]);
@@ -734,9 +729,6 @@ static __init void evm_init(void)
 
 	if (HAS_ATA)
 		davinci_init_ide();
-
-	soc_info->emac_pdata->phy_mask = DM646X_EVM_PHY_MASK;
-	soc_info->emac_pdata->mdio_max_freq = DM646X_EVM_MDIO_FREQUENCY;
 }
 
 #define DM646X_EVM_REF_FREQ		27000000
diff --git a/arch/arm/mach-davinci/board-neuros-osd2.c b/arch/arm/mach-davinci/board-neuros-osd2.c
index 4c30e92..4b32f0a 100644
--- a/arch/arm/mach-davinci/board-neuros-osd2.c
+++ b/arch/arm/mach-davinci/board-neuros-osd2.c
@@ -39,9 +39,6 @@
 #include <mach/mmc.h>
 #include <mach/usb.h>
 
-#define NEUROS_OSD2_PHY_MASK		0x2
-#define NEUROS_OSD2_MDIO_FREQUENCY	2200000 /* PHY bus frequency */
-
 #define LXT971_PHY_ID			0x001378e2
 #define LXT971_PHY_MASK			0xfffffff0
 
@@ -218,7 +215,6 @@ static struct davinci_mmc_config davinci_ntosd2_mmc_config = {
 static __init void davinci_ntosd2_init(void)
 {
 	struct clk *aemif_clk;
-	struct davinci_soc_info *soc_info = &davinci_soc_info;
 	int	status;
 
 	aemif_clk = clk_get(NULL, "aemif");
@@ -252,9 +248,6 @@ static __init void davinci_ntosd2_init(void)
 	davinci_serial_init(&uart_config);
 	dm644x_init_asp(&dm644x_ntosd2_snd_data);
 
-	soc_info->emac_pdata->phy_mask = NEUROS_OSD2_PHY_MASK;
-	soc_info->emac_pdata->mdio_max_freq = NEUROS_OSD2_MDIO_FREQUENCY;
-
 	davinci_setup_usb(1000, 8);
 	/*
 	 * Mux the pins to be GPIOs, VLYNQEN is already done at startup.
diff --git a/arch/arm/mach-davinci/board-sffsdr.c b/arch/arm/mach-davinci/board-sffsdr.c
index 23e664a..b372d7f 100644
--- a/arch/arm/mach-davinci/board-sffsdr.c
+++ b/arch/arm/mach-davinci/board-sffsdr.c
@@ -42,9 +42,6 @@
 #include <mach/mux.h>
 #include <mach/usb.h>
 
-#define SFFSDR_PHY_MASK		(0x2)
-#define SFFSDR_MDIO_FREQUENCY	(2200000) /* PHY bus frequency */
-
 static struct mtd_partition davinci_sffsdr_nandflash_partition[] = {
 	/* U-Boot Environment: Block 0
 	 * UBL:                Block 1
@@ -137,14 +134,10 @@ static void __init davinci_sffsdr_map_io(void)
 
 static __init void davinci_sffsdr_init(void)
 {
-	struct davinci_soc_info *soc_info = &davinci_soc_info;
-
 	platform_add_devices(davinci_sffsdr_devices,
 			     ARRAY_SIZE(davinci_sffsdr_devices));
 	sffsdr_init_i2c();
 	davinci_serial_init(&uart_config);
-	soc_info->emac_pdata->phy_mask = SFFSDR_PHY_MASK;
-	soc_info->emac_pdata->mdio_max_freq = SFFSDR_MDIO_FREQUENCY;
 	davinci_setup_usb(0, 0); /* We support only peripheral mode. */
 
 	/* mux VLYNQ pins */
diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c
index c673e2a..b905e5c 100644
--- a/arch/arm/mach-davinci/devices-da8xx.c
+++ b/arch/arm/mach-davinci/devices-da8xx.c
@@ -41,7 +41,6 @@
 #define DA8XX_EMAC_CTRL_REG_OFFSET	0x3000
 #define DA8XX_EMAC_MOD_REG_OFFSET	0x2000
 #define DA8XX_EMAC_RAM_OFFSET		0x0000
-#define DA8XX_MDIO_REG_OFFSET		0x4000
 #define DA8XX_EMAC_CTRL_RAM_SIZE	SZ_8K
 
 void __iomem *da8xx_syscfg0_base;
@@ -370,7 +369,6 @@ struct emac_platform_data da8xx_emac_pdata = {
 	.ctrl_reg_offset	= DA8XX_EMAC_CTRL_REG_OFFSET,
 	.ctrl_mod_reg_offset	= DA8XX_EMAC_MOD_REG_OFFSET,
 	.ctrl_ram_offset	= DA8XX_EMAC_RAM_OFFSET,
-	.mdio_reg_offset	= DA8XX_MDIO_REG_OFFSET,
 	.ctrl_ram_size		= DA8XX_EMAC_CTRL_RAM_SIZE,
 	.version		= EMAC_VERSION_2,
 };
diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c
index 4a58f03..7b69790 100644
--- a/arch/arm/mach-davinci/dm365.c
+++ b/arch/arm/mach-davinci/dm365.c
@@ -691,7 +691,6 @@ static struct emac_platform_data dm365_emac_pdata = {
 	.ctrl_reg_offset	= DM365_EMAC_CNTRL_OFFSET,
 	.ctrl_mod_reg_offset	= DM365_EMAC_CNTRL_MOD_OFFSET,
 	.ctrl_ram_offset	= DM365_EMAC_CNTRL_RAM_OFFSET,
-	.mdio_reg_offset	= DM365_EMAC_MDIO_OFFSET,
 	.ctrl_ram_size		= DM365_EMAC_CNTRL_RAM_SIZE,
 	.version		= EMAC_VERSION_2,
 };
diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c
index 2673723..6de35ef 100644
--- a/arch/arm/mach-davinci/dm644x.c
+++ b/arch/arm/mach-davinci/dm644x.c
@@ -322,7 +322,6 @@ static struct emac_platform_data dm644x_emac_pdata = {
 	.ctrl_reg_offset	= DM644X_EMAC_CNTRL_OFFSET,
 	.ctrl_mod_reg_offset	= DM644X_EMAC_CNTRL_MOD_OFFSET,
 	.ctrl_ram_offset	= DM644X_EMAC_CNTRL_RAM_OFFSET,
-	.mdio_reg_offset	= DM644X_EMAC_MDIO_OFFSET,
 	.ctrl_ram_size		= DM644X_EMAC_CNTRL_RAM_SIZE,
 	.version		= EMAC_VERSION_1,
 };
diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c
index 4e90511..f8ba8f4 100644
--- a/arch/arm/mach-davinci/dm646x.c
+++ b/arch/arm/mach-davinci/dm646x.c
@@ -358,7 +358,6 @@ static struct emac_platform_data dm646x_emac_pdata = {
 	.ctrl_reg_offset	= DM646X_EMAC_CNTRL_OFFSET,
 	.ctrl_mod_reg_offset	= DM646X_EMAC_CNTRL_MOD_OFFSET,
 	.ctrl_ram_offset	= DM646X_EMAC_CNTRL_RAM_OFFSET,
-	.mdio_reg_offset	= DM646X_EMAC_MDIO_OFFSET,
 	.ctrl_ram_size		= DM646X_EMAC_CNTRL_RAM_SIZE,
 	.version		= EMAC_VERSION_2,
 };
diff --git a/arch/arm/mach-davinci/include/mach/dm365.h b/arch/arm/mach-davinci/include/mach/dm365.h
index dbb5052..2563bf4 100644
--- a/arch/arm/mach-davinci/include/mach/dm365.h
+++ b/arch/arm/mach-davinci/include/mach/dm365.h
@@ -25,7 +25,6 @@
 #define DM365_EMAC_CNTRL_OFFSET		(0x0000)
 #define DM365_EMAC_CNTRL_MOD_OFFSET	(0x3000)
 #define DM365_EMAC_CNTRL_RAM_OFFSET	(0x1000)
-#define DM365_EMAC_MDIO_OFFSET		(0x4000)
 #define DM365_EMAC_CNTRL_RAM_SIZE	(0x2000)
 
 /* Base of key scan register bank */
diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h
index 5159117..5a1b26d 100644
--- a/arch/arm/mach-davinci/include/mach/dm644x.h
+++ b/arch/arm/mach-davinci/include/mach/dm644x.h
@@ -32,7 +32,6 @@
 #define DM644X_EMAC_CNTRL_OFFSET	(0x0000)
 #define DM644X_EMAC_CNTRL_MOD_OFFSET	(0x1000)
 #define DM644X_EMAC_CNTRL_RAM_OFFSET	(0x2000)
-#define DM644X_EMAC_MDIO_OFFSET		(0x4000)
 #define DM644X_EMAC_CNTRL_RAM_SIZE	(0x2000)
 
 #define DM644X_ASYNC_EMIF_CONTROL_BASE	0x01E00000
diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h
index fc2bdd0..38e54c6 100644
--- a/arch/arm/mach-davinci/include/mach/dm646x.h
+++ b/arch/arm/mach-davinci/include/mach/dm646x.h
@@ -23,7 +23,6 @@
 #define DM646X_EMAC_CNTRL_OFFSET	(0x0000)
 #define DM646X_EMAC_CNTRL_MOD_OFFSET	(0x1000)
 #define DM646X_EMAC_CNTRL_RAM_OFFSET	(0x2000)
-#define DM646X_EMAC_MDIO_OFFSET		(0x4000)
 #define DM646X_EMAC_CNTRL_RAM_SIZE	(0x2000)
 
 #define DM646X_ASYNC_EMIF_CONTROL_BASE	0x20008000
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 1/8] netdev: separate out davinci mdio controller code
From: Cyril Chemparathy @ 2010-08-03 13:31 UTC (permalink / raw)
  To: netdev, davinci-linux-open-source, linux-omap
  Cc: nsekhar, srk, Cyril Chemparathy
In-Reply-To: <1280842313-3743-1-git-send-email-cyril@ti.com>

Davinci's MDIO controller is present on other TI devices, without an
accompanying EMAC.  For example, on tnetv107x, the same MDIO module is used in
conjunction with a 3-port switch hardware.

By separating the MDIO controller code into its own platform driver, this
patch allows common logic to be reused on such platforms.

Signed-off-by: Cyril Chemparathy <cyril@ti.com>
---
 drivers/net/Kconfig          |   10 +
 drivers/net/Makefile         |    1 +
 drivers/net/davinci_mdio.c   |  386 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/davinci_emac.h |    4 +
 4 files changed, 401 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/davinci_mdio.c

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 2decc59..fe6658a 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -947,6 +947,16 @@ config TI_DAVINCI_EMAC
 	  To compile this driver as a module, choose M here: the module
 	  will be called davinci_emac_driver.  This is recommended.
 
+config TI_DAVINCI_MDIO
+	tristate "TI DaVinci MDIO Support"
+	depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
+	select PHYLIB
+	help
+	  This driver supports TI's DaVinci MDIO module.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called davinci_mdio.  This is recommended.
+
 config DM9000
 	tristate "DM9000 support"
 	depends on ARM || BLACKFIN || MIPS
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 0a0512a..84cf18d 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_MDIO) += mdio.o
 obj-$(CONFIG_PHYLIB) += phy/
 
 obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac.o
+obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o
 
 obj-$(CONFIG_E1000) += e1000/
 obj-$(CONFIG_E1000E) += e1000e/
diff --git a/drivers/net/davinci_mdio.c b/drivers/net/davinci_mdio.c
new file mode 100644
index 0000000..d34a53a
--- /dev/null
+++ b/drivers/net/davinci_mdio.c
@@ -0,0 +1,386 @@
+/*
+ * DaVinci MDIO Module driver
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * Shamelessly ripped out of davinci_emac.c, original copyrights follow:
+ *
+ * Copyright (C) 2009 Texas Instruments.
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ---------------------------------------------------------------------------
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/phy.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/davinci_emac.h>
+
+#define PHY_REG_MASK		0x1f
+#define PHY_ID_MASK		0x1f
+
+#define DEF_OUT_FREQ		2200000		/* 2.2 MHz */
+
+struct davinci_mdio_regs {
+	u32	version;
+	u32	control;
+#define CONTROL_IDLE		BIT(31)
+#define CONTROL_ENABLE		BIT(30)
+#define CONTROL_MAX_DIV		(0xff)
+
+	u32	alive;
+	u32	link;
+	u32	linkintraw;
+	u32	linkintmasked;
+	u32	__reserved_0[2];
+	u32	userintraw;
+	u32	userintmasked;
+	u32	userintmaskset;
+	u32	userintmaskclr;
+	u32	__reserved_1[20];
+
+	struct {
+		u32	access;
+#define USERACCESS_GO		BIT(31)
+#define USERACCESS_WRITE	BIT(30)
+#define USERACCESS_ACK		BIT(29)
+#define USERACCESS_READ		(0)
+#define USERACCESS_DATA		(0xffff)
+
+		u32	physel;
+	}	user[0];
+};
+
+struct mdio_platform_data default_pdata = {
+	.bus_freq = DEF_OUT_FREQ,
+};
+
+struct davinci_mdio_data {
+	struct mdio_platform_data pdata;
+	struct davinci_mdio_regs __iomem *regs;
+	spinlock_t	lock;
+	struct clk	*clk;
+	struct device	*dev;
+	struct mii_bus	*bus;
+	bool		suspended;
+};
+
+/* wait until hardware is ready for another user access */
+static inline u32 wait_for_user_access(struct davinci_mdio_data *data)
+{
+	struct davinci_mdio_regs __iomem *regs = data->regs;
+	u32 reg;
+
+	while ((reg = __raw_readl(&regs->user[0].access)) & USERACCESS_GO)
+		;
+
+	return reg;
+}
+
+/* wait until hardware state machine is idle */
+static inline void wait_for_idle(struct davinci_mdio_data *data)
+{
+	struct davinci_mdio_regs __iomem *regs = data->regs;
+
+	while ((__raw_readl(&regs->control) & CONTROL_IDLE) == 0)
+		;
+}
+
+static int davinci_mdio_read(struct mii_bus *bus, int phy_id, int phy_reg)
+{
+	struct davinci_mdio_data *data = bus->priv;
+	u32 reg;
+
+	if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
+		return -EINVAL;
+
+	spin_lock(&data->lock);
+
+	if (data->suspended) {
+		spin_unlock(&data->lock);
+		return -ENODEV;
+	}
+
+	wait_for_user_access(data);
+	reg = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) |
+	       (phy_id << 16));
+	__raw_writel(reg, &data->regs->user[0].access);
+	reg = wait_for_user_access(data);
+	spin_unlock(&data->lock);
+
+	return (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -EIO;
+}
+
+static int davinci_mdio_write(struct mii_bus *bus, int phy_id,
+			      int phy_reg, u16 phy_data)
+{
+	struct davinci_mdio_data *data = bus->priv;
+	u32 reg;
+
+	if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
+		return -EINVAL;
+
+	spin_lock(&data->lock);
+
+	if (data->suspended) {
+		spin_unlock(&data->lock);
+		return -ENODEV;
+	}
+
+	wait_for_user_access(data);
+	reg = (USERACCESS_GO | USERACCESS_WRITE | (phy_reg << 21) |
+		   (phy_id << 16) | (phy_data & USERACCESS_DATA));
+	__raw_writel(reg, &data->regs->user[0].access);
+	wait_for_user_access(data);
+	spin_unlock(&data->lock);
+
+	return 0;
+}
+
+static int __devinit davinci_mdio_probe(struct platform_device *pdev)
+{
+	struct mdio_platform_data *pdata = pdev->dev.platform_data;
+	struct device *dev = &pdev->dev;
+	struct davinci_mdio_data *data;
+	struct resource *res;
+	u32 mdio_in_freq, mdio_out_freq, div, phy_mask, ver;
+	struct phy_device *phy;
+	int ret, addr;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		dev_err(dev, "failed to alloc device data\n");
+		return -ENOMEM;
+	}
+
+	data->pdata = pdata ? (*pdata) : default_pdata;
+
+	data->bus = mdiobus_alloc();
+	if (!data->bus) {
+		dev_err(dev, "failed to alloc mii bus\n");
+		ret = -ENOMEM;
+		goto bail_out;
+	}
+
+	data->bus->name		= dev_name(dev);
+	data->bus->read		= davinci_mdio_read,
+	data->bus->write	= davinci_mdio_write,
+	data->bus->parent	= dev;
+	data->bus->priv		= data;
+	snprintf(data->bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);
+
+	data->clk = clk_get(dev, NULL);
+	if (IS_ERR(data->clk)) {
+		data->clk = NULL;
+		dev_err(dev, "failed to get device clock\n");
+		ret = PTR_ERR(data->clk);
+		goto bail_out;
+	}
+
+	clk_enable(data->clk);
+
+	dev_set_drvdata(dev, data);
+	data->dev = dev;
+	spin_lock_init(&data->lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "could not find register map resource\n");
+		ret = -ENOENT;
+		goto bail_out;
+	}
+
+	res = devm_request_mem_region(dev, res->start, resource_size(res),
+					    dev_name(dev));
+	if (!res) {
+		dev_err(dev, "could not allocate register map resource\n");
+		ret = -ENXIO;
+		goto bail_out;
+	}
+
+	data->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
+	if (!data->regs) {
+		dev_err(dev, "could not map mdio registers\n");
+		ret = -ENOMEM;
+		goto bail_out;
+	}
+
+	mdio_in_freq = clk_get_rate(data->clk);
+	div = (mdio_in_freq / data->pdata.bus_freq) - 1;
+	if (div > CONTROL_MAX_DIV)
+		div = CONTROL_MAX_DIV;
+	mdio_out_freq = mdio_in_freq / (div + 1);
+
+	/* set enable and clock divider */
+	__raw_writel(div | CONTROL_ENABLE, &data->regs->control);
+
+	/*
+	 * wait for scan logic to settle:
+	 * the scan time consists of (a) a large fixed component, and (b) a
+	 * small component that varies with the mii bus frequency.  These
+	 * were estimated using measurements at 1.1 and 2.2 MHz on tnetv107x
+	 * silicon.  Since the effect of (b) was found to be largely
+	 * negligible, we keep things simple here.
+	 */
+	mdelay(1);
+
+	/* dump hardware version info */
+	ver = __raw_readl(&data->regs->version);
+	dev_info(dev, "davinci mdio revision %d.%d\n",
+			(ver >> 8) & 0xff, ver & 0xff);
+
+	/* get phy mask from the alive register */
+	phy_mask = __raw_readl(&data->regs->alive);
+	if (phy_mask) {
+		/* restrict mdio bus to live phys only */
+		dev_info(dev, "detected phy mask %x\n", ~phy_mask);
+		phy_mask = ~phy_mask;
+	} else {
+		/* desperately scan all phys */
+		dev_warn(dev, "failed to detect live phys, scanning all\n");
+		phy_mask = 0;
+	}
+	data->bus->phy_mask = phy_mask;
+
+	/* register the mii bus */
+	ret = mdiobus_register(data->bus);
+	if (ret)
+		goto bail_out;
+
+	/* scan and dump the bus */
+	for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
+		phy = data->bus->phy_map[addr];
+		if (phy) {
+			dev_info(dev, "phy[%d]: device %s, driver %s\n",
+				 phy->addr, dev_name(&phy->dev),
+				 phy->drv ? phy->drv->name : "unknown");
+		}
+	}
+
+	return 0;
+
+bail_out:
+	if (data->bus)
+		mdiobus_free(data->bus);
+
+	if (data->clk) {
+		clk_disable(data->clk);
+		clk_put(data->clk);
+	}
+
+	kfree(data);
+
+	return ret;
+}
+
+static int __devexit davinci_mdio_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct davinci_mdio_data *data = dev_get_drvdata(dev);
+
+	if (data->bus)
+		mdiobus_free(data->bus);
+
+	if (data->clk) {
+		clk_disable(data->clk);
+		clk_put(data->clk);
+	}
+
+	dev_set_drvdata(dev, NULL);
+
+	kfree(data);
+
+	return 0;
+}
+
+static int davinci_mdio_suspend(struct device *dev)
+{
+	struct davinci_mdio_data *data = dev_get_drvdata(dev);
+	u32 ctrl;
+
+	spin_lock(&data->lock);
+
+	/* shutdown the scan state machine */
+	ctrl = __raw_readl(&data->regs->control);
+	ctrl &= ~CONTROL_ENABLE;
+	__raw_writel(ctrl, &data->regs->control);
+	wait_for_idle(data);
+
+	if (data->clk)
+		clk_disable(data->clk);
+
+	data->suspended = true;
+	spin_unlock(&data->lock);
+
+	return 0;
+}
+
+static int davinci_mdio_resume(struct device *dev)
+{
+	struct davinci_mdio_data *data = dev_get_drvdata(dev);
+	u32 ctrl;
+
+	spin_lock(&data->lock);
+	if (data->clk)
+		clk_enable(data->clk);
+
+	/* restart the scan state machine */
+	ctrl = __raw_readl(&data->regs->control);
+	ctrl |= CONTROL_ENABLE;
+	__raw_writel(ctrl, &data->regs->control);
+
+	data->suspended = false;
+	spin_unlock(&data->lock);
+
+	return 0;
+}
+
+static const struct dev_pm_ops davinci_mdio_pm_ops = {
+	.suspend	= davinci_mdio_suspend,
+	.resume		= davinci_mdio_resume,
+};
+
+static struct platform_driver davinci_mdio_driver = {
+	.driver = {
+		.name	 = "davinci_mdio",
+		.owner	 = THIS_MODULE,
+		.pm	 = &davinci_mdio_pm_ops,
+	},
+	.probe = davinci_mdio_probe,
+	.remove = __devexit_p(davinci_mdio_remove),
+};
+
+static int __init davinci_mdio_init(void)
+{
+	return platform_driver_register(&davinci_mdio_driver);
+}
+device_initcall(davinci_mdio_init);
+
+static void __exit davinci_mdio_exit(void)
+{
+	platform_driver_unregister(&davinci_mdio_driver);
+}
+module_exit(davinci_mdio_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DaVinci MDIO driver");
diff --git a/include/linux/davinci_emac.h b/include/linux/davinci_emac.h
index 7c930db..a04fd8c 100644
--- a/include/linux/davinci_emac.h
+++ b/include/linux/davinci_emac.h
@@ -14,6 +14,10 @@
 #include <linux/if_ether.h>
 #include <linux/memory.h>
 
+struct mdio_platform_data {
+	unsigned long		bus_freq;
+};
+
 struct emac_platform_data {
 	char mac_addr[ETH_ALEN];
 	u32 ctrl_reg_offset;
-- 
1.7.0.4


^ permalink raw reply related

* [PATCH 0/8] split out emac cpdma and mdio for reuse
From: Cyril Chemparathy @ 2010-08-03 13:31 UTC (permalink / raw)
  To: netdev, davinci-linux-open-source, linux-omap
  Cc: nsekhar, srk, Cyril Chemparathy

Davinci's EMAC device has an in-built MDIO controller and a CPDMA engine.
These hardware modules are not restricted to EMAC device alone.  For example,
CPSW3G (3-port gigabit ethernet switch) hardware uses these very same modules
internally.  This patch series separates out EMAC's MDIO and CPDMA
functionality, allowing these individual pieces to be reused across TI
hardware.  

This patch series has been broadly organized as follows:

MDIO:
  - Add new functionality
      netdev: separate out davinci mdio controller code
  - Hookup new functionality
      davinci: add mdio platform devices
      netdev: switch davinci emac to new mdio driver
  - Cleanup left over cruft
      davinci: cleanup unused davinci mdio arch code
      netdev: cleanup unused davinci mdio emac code

CPDMA:
  - Add new functionality
     netdev: separate out davinci cpdma controller code
  - Hookup new functionality
     netdev: switch davinci emac to new cpdma layer
  - Cleanup left over cruft
     netdev: cleanup unused davinci emac cpdma code

This series has been tested on dm365 and tnetv107x (with additional cpsw
patches) hardware.  Additional omap tree patches will be required for am35xx
as well (not included here).


Cyril Chemparathy (8):
  netdev: separate out davinci mdio controller code
  davinci: add mdio platform devices
  netdev: switch davinci emac to new mdio driver
  davinci: cleanup unused davinci mdio arch code
  netdev: cleanup unused davinci mdio emac code
  netdev: separate out davinci cpdma controller code
  netdev: switch davinci emac to new cpdma layer
  netdev: cleanup unused davinci emac cpdma code

 arch/arm/mach-davinci/board-da830-evm.c     |    5 -
 arch/arm/mach-davinci/board-da850-evm.c     |    6 -
 arch/arm/mach-davinci/board-dm365-evm.c     |    7 -
 arch/arm/mach-davinci/board-dm644x-evm.c    |    7 -
 arch/arm/mach-davinci/board-dm646x-evm.c    |    8 -
 arch/arm/mach-davinci/board-neuros-osd2.c   |    7 -
 arch/arm/mach-davinci/board-sffsdr.c        |    7 -
 arch/arm/mach-davinci/devices-da8xx.c       |   31 +-
 arch/arm/mach-davinci/dm365.c               |   23 +-
 arch/arm/mach-davinci/dm644x.c              |   23 +-
 arch/arm/mach-davinci/dm646x.c              |   23 +-
 arch/arm/mach-davinci/include/mach/dm365.h  |    2 +-
 arch/arm/mach-davinci/include/mach/dm644x.h |    2 +-
 arch/arm/mach-davinci/include/mach/dm646x.h |    2 +-
 drivers/net/Kconfig                         |   21 +
 drivers/net/Makefile                        |    2 +
 drivers/net/davinci_cpdma.c                 |  836 +++++++++++++++++
 drivers/net/davinci_cpdma.h                 |  105 +++
 drivers/net/davinci_emac.c                  | 1335 ++++-----------------------
 drivers/net/davinci_mdio.c                  |  386 ++++++++
 include/linux/davinci_emac.h                |    8 +-
 21 files changed, 1615 insertions(+), 1231 deletions(-)
 create mode 100644 drivers/net/davinci_cpdma.c
 create mode 100644 drivers/net/davinci_cpdma.h
 create mode 100644 drivers/net/davinci_mdio.c


^ permalink raw reply

* Re: RFC: New BGF 'LOOP' instruction
From: David Miller @ 2010-08-03 13:07 UTC (permalink / raw)
  To: andi; +Cc: leonerd, netdev
In-Reply-To: <87mxt3etut.fsf@basil.nowhere.org>

From: Andi Kleen <andi@firstfloor.org>
Date: Tue, 03 Aug 2010 14:58:02 +0200

> David Miller <davem@davemloft.net> writes:
>>
>> That makes the looping construct largely useless, which I mentioned in
>> my second reply to this thread.
> 
> How about simply adding a "skip ipv6 extension headers until header type
> X" opcode?
> 
> I bet that would solve most of the problems here in practice.

BPF really should not have protocol specific opcodes.

^ permalink raw reply

* Re: RFC: New BGF 'LOOP' instruction
From: Andi Kleen @ 2010-08-03 12:58 UTC (permalink / raw)
  To: David Miller; +Cc: leonerd, netdev
In-Reply-To: <20100803.001809.25133218.davem@davemloft.net>

David Miller <davem@davemloft.net> writes:
>
> That makes the looping construct largely useless, which I mentioned in
> my second reply to this thread.

How about simply adding a "skip ipv6 extension headers until header type
X" opcode?

I bet that would solve most of the problems here in practice.

-Andi
-- 
ak@linux.intel.com -- Speaking for myself only.

^ permalink raw reply

* [PATCH v5] can: Add driver for esd CAN-USB/2 device
From: Matthias Fuchs @ 2010-08-03 12:55 UTC (permalink / raw)
  To: netdev-u79uwXL29TY76Z2rM5mHXA
  Cc: Socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	linux-usb-u79uwXL29TY76Z2rM5mHXA

This patch adds a driver for esd's USB high speed
CAN interface. The driver supports devices with
multiple CAN interfaces.

Signed-off-by: Matthias Fuchs <matthias.fuchs-iOnpLzIbIdM@public.gmane.org>
Acked-by: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
---
version 5:
 - remove CONFIG_SYSFS

version 4:
 - use WARN_ON instead of BUG_ON
 - add more error handling (handle esd_us2_send_msg() return code)
 - use single point of return from functions where appropriate
 - eliminate race condition during stopping/restarting queue

version 3:
 - remove bus-error reporting feature because
   it cannot be controlled by user on demand
   with current device's firmware
 - rebased against current net-next-2.6 tree

version 2:
 - use bus-error reporting and counters
 - minor cleanup
 - rebased against current net-next-2.6 tree
 - initial post to linux-usb list for review

 drivers/net/can/usb/Kconfig    |    6 +
 drivers/net/can/usb/Makefile   |    1 +
 drivers/net/can/usb/esd_usb2.c | 1132 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1139 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/can/usb/esd_usb2.c

diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index 97ff6fe..0452549 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -7,4 +7,10 @@ config CAN_EMS_USB
 	  This driver is for the one channel CPC-USB/ARM7 CAN/USB interface
 	  from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de).
 
+config CAN_ESD_USB2
+        tristate "ESD USB/2 CAN/USB interface"
+        ---help---
+          This driver supports the CAN-USB/2 interface
+          from esd electronic system design gmbh (http://www.esd.eu).
+
 endmenu
diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
index 0afd51d..fce3cf1 100644
--- a/drivers/net/can/usb/Makefile
+++ b/drivers/net/can/usb/Makefile
@@ -3,5 +3,6 @@
 #
 
 obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
+obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
 
 ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c
new file mode 100644
index 0000000..05a5275
--- /dev/null
+++ b/drivers/net/can/usb/esd_usb2.c
@@ -0,0 +1,1132 @@
+/*
+ * CAN driver for esd CAN-USB/2
+ *
+ * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs-iOnpLzIbIdM@public.gmane.org>, esd gmbh
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <linux/init.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+MODULE_AUTHOR("Matthias Fuchs <matthias.fuchs-iOnpLzIbIdM@public.gmane.org>");
+MODULE_DESCRIPTION("CAN driver for esd CAN-USB/2 interfaces");
+MODULE_LICENSE("GPL v2");
+
+/* Define these values to match your devices */
+#define USB_ESDGMBH_VENDOR_ID	0x0ab4
+#define USB_CANUSB2_PRODUCT_ID	0x0010
+
+#define ESD_USB2_CAN_CLOCK	60000000
+#define ESD_USB2_MAX_NETS	2
+
+/* USB2 commands */
+#define CMD_VERSION		1 /* also used for VERSION_REPLY */
+#define CMD_CAN_RX		2 /* device to host only */
+#define CMD_CAN_TX		3 /* also used for TX_DONE */
+#define CMD_SETBAUD		4 /* also used for SETBAUD_REPLY */
+#define CMD_TS			5 /* also used for TS_REPLY */
+#define CMD_IDADD		6 /* also used for IDADD_REPLY */
+
+/* esd CAN message flags - dlc field */
+#define ESD_RTR			0x10
+
+/* esd CAN message flags - id field */
+#define ESD_EXTID		0x20000000
+#define ESD_EVENT		0x40000000
+#define ESD_IDMASK		0x1fffffff
+
+/* esd CAN event ids used by this driver */
+#define ESD_EV_CAN_ERROR_EXT	2
+
+/* baudrate message flags */
+#define ESD_USB2_UBR		0x80000000
+#define ESD_USB2_LOM		0x40000000
+#define ESD_USB2_NO_BAUDRATE	0x7fffffff
+#define ESD_USB2_TSEG1_MIN	1
+#define ESD_USB2_TSEG1_MAX	16
+#define ESD_USB2_TSEG1_SHIFT	16
+#define ESD_USB2_TSEG2_MIN	1
+#define ESD_USB2_TSEG2_MAX	8
+#define ESD_USB2_TSEG2_SHIFT	20
+#define ESD_USB2_SJW_MAX	4
+#define ESD_USB2_SJW_SHIFT	14
+#define ESD_USB2_BRP_MIN	1
+#define ESD_USB2_BRP_MAX	1024
+#define ESD_USB2_BRP_INC	1
+#define ESD_USB2_3_SAMPLES	0x00800000
+
+/* esd IDADD message */
+#define ESD_ID_ENABLE		0x80
+#define ESD_MAX_ID_SEGMENT	64
+
+/* SJA1000 ECC register (emulated by usb2 firmware) */
+#define SJA1000_ECC_SEG		0x1F
+#define SJA1000_ECC_DIR		0x20
+#define SJA1000_ECC_ERR		0x06
+#define SJA1000_ECC_BIT		0x00
+#define SJA1000_ECC_FORM	0x40
+#define SJA1000_ECC_STUFF	0x80
+#define SJA1000_ECC_MASK	0xc0
+
+/* esd bus state event codes */
+#define ESD_BUSSTATE_MASK	0xc0
+#define ESD_BUSSTATE_WARN	0x40
+#define ESD_BUSSTATE_ERRPASSIVE	0x80
+#define ESD_BUSSTATE_BUSOFF	0xc0
+
+#define RX_BUFFER_SIZE		1024
+#define MAX_RX_URBS		4
+#define MAX_TX_URBS		16 /* must be power of 2 */
+
+struct header_msg {
+	u8 len; /* len is always the total message length in 32bit words */
+	u8 cmd;
+	u8 rsvd[2];
+};
+
+struct version_msg {
+	u8 len;
+	u8 cmd;
+	u8 rsvd;
+	u8 flags;
+	__le32 drv_version;
+};
+
+struct version_reply_msg {
+	u8 len;
+	u8 cmd;
+	u8 nets;
+	u8 features;
+	__le32 version;
+	u8 name[16];
+	__le32 rsvd;
+	__le32 ts;
+};
+
+struct rx_msg {
+	u8 len;
+	u8 cmd;
+	u8 net;
+	u8 dlc;
+	__le32 ts;
+	__le32 id; /* upper 3 bits contain flags */
+	u8 data[8];
+};
+
+struct tx_msg {
+	u8 len;
+	u8 cmd;
+	u8 net;
+	u8 dlc;
+	__le32 hnd;
+	__le32 id; /* upper 3 bits contain flags */
+	u8 data[8];
+};
+
+struct tx_done_msg {
+	u8 len;
+	u8 cmd;
+	u8 net;
+	u8 status;
+	__le32 hnd;
+	__le32 ts;
+};
+
+struct id_filter_msg {
+	u8 len;
+	u8 cmd;
+	u8 net;
+	u8 option;
+	__le32 mask[ESD_MAX_ID_SEGMENT + 1];
+};
+
+struct set_baudrate_msg {
+	u8 len;
+	u8 cmd;
+	u8 net;
+	u8 rsvd;
+	__le32 baud;
+};
+
+/* Main message type used between library and application */
+struct __attribute__ ((packed)) esd_usb2_msg {
+	union {
+		struct header_msg hdr;
+		struct version_msg version;
+		struct version_reply_msg version_reply;
+		struct rx_msg rx;
+		struct tx_msg tx;
+		struct tx_done_msg txdone;
+		struct set_baudrate_msg setbaud;
+		struct id_filter_msg filter;
+	} msg;
+};
+
+static struct usb_device_id esd_usb2_table[] = {
+	{USB_DEVICE(USB_ESDGMBH_VENDOR_ID, USB_CANUSB2_PRODUCT_ID)},
+	{}
+};
+MODULE_DEVICE_TABLE(usb, esd_usb2_table);
+
+struct esd_usb2_net_priv;
+
+struct esd_tx_urb_context {
+	struct esd_usb2_net_priv *priv;
+	u32 echo_index;
+	int dlc;
+};
+
+struct esd_usb2 {
+	struct usb_device *udev;
+	struct esd_usb2_net_priv *nets[ESD_USB2_MAX_NETS];
+
+	struct usb_anchor rx_submitted;
+
+	int net_count;
+	u32 version;
+	int rxinitdone;
+};
+
+struct esd_usb2_net_priv {
+	struct can_priv can; /* must be the first member */
+
+	atomic_t active_tx_jobs;
+	struct usb_anchor tx_submitted;
+	struct esd_tx_urb_context tx_contexts[MAX_TX_URBS];
+
+	int open_time;
+	struct esd_usb2 *usb2;
+	struct net_device *netdev;
+	int index;
+	u8 old_state;
+	struct can_berr_counter bec;
+};
+
+static void esd_usb2_rx_event(struct esd_usb2_net_priv *priv,
+			      struct esd_usb2_msg *msg)
+{
+	struct net_device_stats *stats = &priv->netdev->stats;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+	u32 id = le32_to_cpu(msg->msg.rx.id) & ESD_IDMASK;
+
+	if (id == ESD_EV_CAN_ERROR_EXT) {
+		u8 state = msg->msg.rx.data[0];
+		u8 ecc = msg->msg.rx.data[1];
+		u8 txerr = msg->msg.rx.data[2];
+		u8 rxerr = msg->msg.rx.data[3];
+
+		skb = alloc_can_err_skb(priv->netdev, &cf);
+		if (skb == NULL) {
+			stats->rx_dropped++;
+			return;
+		}
+
+		if (state != priv->old_state) {
+			priv->old_state = state;
+
+			switch (state & ESD_BUSSTATE_MASK) {
+			case ESD_BUSSTATE_BUSOFF:
+				priv->can.state = CAN_STATE_BUS_OFF;
+				cf->can_id |= CAN_ERR_BUSOFF;
+				can_bus_off(priv->netdev);
+				break;
+			case ESD_BUSSTATE_WARN:
+				priv->can.state = CAN_STATE_ERROR_WARNING;
+				priv->can.can_stats.error_warning++;
+				break;
+			case ESD_BUSSTATE_ERRPASSIVE:
+				priv->can.state = CAN_STATE_ERROR_PASSIVE;
+				priv->can.can_stats.error_passive++;
+				break;
+			default:
+				priv->can.state = CAN_STATE_ERROR_ACTIVE;
+				break;
+			}
+		} else {
+			priv->can.can_stats.bus_error++;
+			stats->rx_errors++;
+
+			cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+			switch (ecc & SJA1000_ECC_MASK) {
+			case SJA1000_ECC_BIT:
+				cf->data[2] |= CAN_ERR_PROT_BIT;
+				break;
+			case SJA1000_ECC_FORM:
+				cf->data[2] |= CAN_ERR_PROT_FORM;
+				break;
+			case SJA1000_ECC_STUFF:
+				cf->data[2] |= CAN_ERR_PROT_STUFF;
+				break;
+			default:
+				cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+				cf->data[3] = ecc & SJA1000_ECC_SEG;
+				break;
+			}
+
+			/* Error occured during transmission? */
+			if (!(ecc & SJA1000_ECC_DIR))
+				cf->data[2] |= CAN_ERR_PROT_TX;
+
+			if (priv->can.state == CAN_STATE_ERROR_WARNING ||
+			    priv->can.state == CAN_STATE_ERROR_PASSIVE) {
+				cf->data[1] = (txerr > rxerr) ?
+					CAN_ERR_CRTL_TX_PASSIVE :
+					CAN_ERR_CRTL_RX_PASSIVE;
+			}
+			cf->data[6] = txerr;
+			cf->data[7] = rxerr;
+		}
+
+		netif_rx(skb);
+
+		priv->bec.txerr = txerr;
+		priv->bec.rxerr = rxerr;
+
+		stats->rx_packets++;
+		stats->rx_bytes += cf->can_dlc;
+	}
+}
+
+static void esd_usb2_rx_can_msg(struct esd_usb2_net_priv *priv,
+				struct esd_usb2_msg *msg)
+{
+	struct net_device_stats *stats = &priv->netdev->stats;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+	int i;
+	u32 id;
+
+	if (!netif_device_present(priv->netdev))
+		return;
+
+	id = le32_to_cpu(msg->msg.rx.id);
+
+	if (id & ESD_EVENT) {
+		esd_usb2_rx_event(priv, msg);
+	} else {
+		skb = alloc_can_skb(priv->netdev, &cf);
+		if (skb == NULL) {
+			stats->rx_dropped++;
+			return;
+		}
+
+		cf->can_id = id & ESD_IDMASK;
+		cf->can_dlc = get_can_dlc(msg->msg.rx.dlc);
+
+		if (id & ESD_EXTID)
+			cf->can_id |= CAN_EFF_FLAG;
+
+		if (msg->msg.rx.dlc & ESD_RTR) {
+			cf->can_id |= CAN_RTR_FLAG;
+		} else {
+			for (i = 0; i < cf->can_dlc; i++)
+				cf->data[i] = msg->msg.rx.data[i];
+		}
+
+		netif_rx(skb);
+
+		stats->rx_packets++;
+		stats->rx_bytes += cf->can_dlc;
+	}
+
+	return;
+}
+
+static void esd_usb2_tx_done_msg(struct esd_usb2_net_priv *priv,
+				 struct esd_usb2_msg *msg)
+{
+	struct net_device_stats *stats = &priv->netdev->stats;
+	struct net_device *netdev = priv->netdev;
+	struct esd_tx_urb_context *context;
+
+	if (!netif_device_present(netdev))
+		return;
+
+	context = &priv->tx_contexts[msg->msg.txdone.hnd & (MAX_TX_URBS - 1)];
+
+	if (!msg->msg.txdone.status) {
+		stats->tx_packets++;
+		stats->tx_bytes += context->dlc;
+		can_get_echo_skb(netdev, context->echo_index);
+	} else {
+		stats->tx_errors++;
+		can_free_echo_skb(netdev, context->echo_index);
+	}
+
+	/* Release context */
+	context->echo_index = MAX_TX_URBS;
+	atomic_dec(&priv->active_tx_jobs);
+
+	netif_wake_queue(netdev);
+}
+
+static void esd_usb2_read_bulk_callback(struct urb *urb)
+{
+	struct esd_usb2 *dev = urb->context;
+	int retval;
+	int pos = 0;
+	int i;
+
+	switch (urb->status) {
+	case 0: /* success */
+		break;
+
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+
+	default:
+		dev_info(dev->udev->dev.parent,
+			 "Rx URB aborted (%d)\n", urb->status);
+		goto resubmit_urb;
+	}
+
+	while (pos < urb->actual_length) {
+		struct esd_usb2_msg *msg;
+
+		msg = (struct esd_usb2_msg *)(urb->transfer_buffer + pos);
+
+		switch (msg->msg.hdr.cmd) {
+		case CMD_CAN_RX:
+			esd_usb2_rx_can_msg(dev->nets[msg->msg.rx.net], msg);
+			break;
+
+		case CMD_CAN_TX:
+			esd_usb2_tx_done_msg(dev->nets[msg->msg.txdone.net],
+					     msg);
+			break;
+		}
+
+		pos += msg->msg.hdr.len << 2;
+
+		if (pos > urb->actual_length) {
+			dev_err(dev->udev->dev.parent, "format error\n");
+			break;
+		}
+	}
+
+resubmit_urb:
+	usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
+			  urb->transfer_buffer, RX_BUFFER_SIZE,
+			  esd_usb2_read_bulk_callback, dev);
+
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval == -ENODEV) {
+		for (i = 0; i < dev->net_count; i++) {
+			if (dev->nets[i])
+				netif_device_detach(dev->nets[i]->netdev);
+		}
+	} else if (retval) {
+		dev_err(dev->udev->dev.parent,
+			"failed resubmitting read bulk urb: %d\n", retval);
+	}
+
+	return;
+}
+
+/*
+ * callback for bulk IN urb
+ */
+static void esd_usb2_write_bulk_callback(struct urb *urb)
+{
+	struct esd_tx_urb_context *context = urb->context;
+	struct esd_usb2_net_priv *priv;
+	struct esd_usb2 *dev;
+	struct net_device *netdev;
+	size_t size = sizeof(struct esd_usb2_msg);
+
+	WARN_ON(!context);
+
+	priv = context->priv;
+	netdev = priv->netdev;
+	dev = priv->usb2;
+
+	/* free up our allocated buffer */
+	usb_free_coherent(urb->dev, size,
+			  urb->transfer_buffer, urb->transfer_dma);
+
+	if (!netif_device_present(netdev))
+		return;
+
+	if (urb->status)
+		dev_info(netdev->dev.parent, "Tx URB aborted (%d)\n",
+			 urb->status);
+
+	netdev->trans_start = jiffies;
+}
+
+static ssize_t show_firmware(struct device *d,
+			     struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(d);
+	struct esd_usb2 *dev = usb_get_intfdata(intf);
+
+	return sprintf(buf, "%d.%d.%d\n",
+		       (dev->version >> 12) & 0xf,
+		       (dev->version >> 8) & 0xf,
+		       dev->version & 0xff);
+}
+static DEVICE_ATTR(firmware, S_IRUGO, show_firmware, NULL);
+
+static ssize_t show_hardware(struct device *d,
+			     struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(d);
+	struct esd_usb2 *dev = usb_get_intfdata(intf);
+
+	return sprintf(buf, "%d.%d.%d\n",
+		       (dev->version >> 28) & 0xf,
+		       (dev->version >> 24) & 0xf,
+		       (dev->version >> 16) & 0xff);
+}
+static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL);
+
+static ssize_t show_nets(struct device *d,
+			 struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(d);
+	struct esd_usb2 *dev = usb_get_intfdata(intf);
+
+	return sprintf(buf, "%d", dev->net_count);
+}
+static DEVICE_ATTR(nets, S_IRUGO, show_nets, NULL);
+
+static int esd_usb2_send_msg(struct esd_usb2 *dev, struct esd_usb2_msg *msg)
+{
+	int actual_length;
+
+	return usb_bulk_msg(dev->udev,
+			    usb_sndbulkpipe(dev->udev, 2),
+			    msg,
+			    msg->msg.hdr.len << 2,
+			    &actual_length,
+			    1000);
+}
+
+static int esd_usb2_wait_msg(struct esd_usb2 *dev,
+			     struct esd_usb2_msg *msg)
+{
+	int actual_length;
+
+	return usb_bulk_msg(dev->udev,
+			    usb_rcvbulkpipe(dev->udev, 1),
+			    msg,
+			    sizeof(*msg),
+			    &actual_length,
+			    1000);
+}
+
+static int esd_usb2_setup_rx_urbs(struct esd_usb2 *dev)
+{
+	int i, err = 0;
+
+	if (dev->rxinitdone)
+		return 0;
+
+	for (i = 0; i < MAX_RX_URBS; i++) {
+		struct urb *urb = NULL;
+		u8 *buf = NULL;
+
+		/* create a URB, and a buffer for it */
+		urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!urb) {
+			dev_warn(dev->udev->dev.parent,
+				 "No memory left for URBs\n");
+			err = -ENOMEM;
+			break;
+		}
+
+		buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE, GFP_KERNEL,
+					 &urb->transfer_dma);
+		if (!buf) {
+			dev_warn(dev->udev->dev.parent,
+				 "No memory left for USB buffer\n");
+			err = -ENOMEM;
+			goto freeurb;
+		}
+
+		usb_fill_bulk_urb(urb, dev->udev,
+				  usb_rcvbulkpipe(dev->udev, 1),
+				  buf, RX_BUFFER_SIZE,
+				  esd_usb2_read_bulk_callback, dev);
+		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+		usb_anchor_urb(urb, &dev->rx_submitted);
+
+		err = usb_submit_urb(urb, GFP_KERNEL);
+		if (err) {
+			usb_unanchor_urb(urb);
+			usb_free_coherent(dev->udev, RX_BUFFER_SIZE, buf,
+					  urb->transfer_dma);
+		}
+
+freeurb:
+		/* Drop reference, USB core will take care of freeing it */
+		usb_free_urb(urb);
+		if (err)
+			break;
+	}
+
+	/* Did we submit any URBs */
+	if (i == 0) {
+		dev_err(dev->udev->dev.parent, "couldn't setup read URBs\n");
+		return err;
+	}
+
+	/* Warn if we've couldn't transmit all the URBs */
+	if (i < MAX_RX_URBS) {
+		dev_warn(dev->udev->dev.parent,
+			 "rx performance may be slow\n");
+	}
+
+	dev->rxinitdone = 1;
+	return 0;
+}
+
+/*
+ * Start interface
+ */
+static int esd_usb2_start(struct esd_usb2_net_priv *priv)
+{
+	struct esd_usb2 *dev = priv->usb2;
+	struct net_device *netdev = priv->netdev;
+	struct esd_usb2_msg msg;
+	int err, i;
+
+	/*
+	 * Enable all IDs
+	 * The IDADD message takes up to 64 32 bit bitmasks (2048 bits).
+	 * Each bit represents one 11 bit CAN identifier. A set bit
+	 * enables reception of the corresponding CAN identifier. A cleared
+	 * bit disabled this identifier. An additional bitmask value
+	 * following the CAN 2.0A bits is used to enable reception of
+	 * extended CAN frames. Only the LSB of this final mask is checked
+	 * for the complete 29 bit ID range. The IDADD message also allows
+	 * filter configuration for an ID subset. In this case you can add
+	 * the number of the starting bitmask (0..64) to the filter.option
+	 * field followed by only some bitmasks.
+	 */
+	msg.msg.hdr.cmd = CMD_IDADD;
+	msg.msg.hdr.len = 2 + ESD_MAX_ID_SEGMENT;
+	msg.msg.filter.net = priv->index;
+	msg.msg.filter.option = ESD_ID_ENABLE; /* start with segment 0 */
+	for (i = 0; i < ESD_MAX_ID_SEGMENT; i++)
+		msg.msg.filter.mask[i] = cpu_to_le32(0xffffffff);
+	/* enable 29bit extended IDs */
+	msg.msg.filter.mask[ESD_MAX_ID_SEGMENT] = cpu_to_le32(0x00000001);
+
+	err = esd_usb2_send_msg(dev, &msg);
+	if (err)
+		goto failed;
+
+	err = esd_usb2_setup_rx_urbs(dev);
+	if (err)
+		goto failed;
+
+	priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+	return 0;
+
+failed:
+	if (err == -ENODEV)
+		netif_device_detach(netdev);
+
+	dev_err(netdev->dev.parent, "couldn't start device: %d\n", err);
+
+	return err;
+}
+
+static void unlink_all_urbs(struct esd_usb2 *dev)
+{
+	struct esd_usb2_net_priv *priv;
+	int i;
+
+	usb_kill_anchored_urbs(&dev->rx_submitted);
+	for (i = 0; i < dev->net_count; i++) {
+		priv = dev->nets[i];
+		if (priv) {
+			usb_kill_anchored_urbs(&priv->tx_submitted);
+			atomic_set(&priv->active_tx_jobs, 0);
+
+			for (i = 0; i < MAX_TX_URBS; i++)
+				priv->tx_contexts[i].echo_index = MAX_TX_URBS;
+		}
+	}
+}
+
+static int esd_usb2_open(struct net_device *netdev)
+{
+	struct esd_usb2_net_priv *priv = netdev_priv(netdev);
+	int err;
+
+	/* common open */
+	err = open_candev(netdev);
+	if (err)
+		return err;
+
+	/* finally start device */
+	err = esd_usb2_start(priv);
+	if (err) {
+		dev_warn(netdev->dev.parent,
+			 "couldn't start device: %d\n", err);
+		close_candev(netdev);
+		return err;
+	}
+
+	priv->open_time = jiffies;
+
+	netif_start_queue(netdev);
+
+	return 0;
+}
+
+static netdev_tx_t esd_usb2_start_xmit(struct sk_buff *skb,
+				      struct net_device *netdev)
+{
+	struct esd_usb2_net_priv *priv = netdev_priv(netdev);
+	struct esd_usb2 *dev = priv->usb2;
+	struct esd_tx_urb_context *context = NULL;
+	struct net_device_stats *stats = &netdev->stats;
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	struct esd_usb2_msg *msg;
+	struct urb *urb;
+	u8 *buf;
+	int i, err;
+	int ret = NETDEV_TX_OK;
+	size_t size = sizeof(struct esd_usb2_msg);
+
+	if (can_dropped_invalid_skb(netdev, skb))
+		return NETDEV_TX_OK;
+
+	/* create a URB, and a buffer for it, and copy the data to the URB */
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		dev_err(netdev->dev.parent, "No memory left for URBs\n");
+		stats->tx_dropped++;
+		dev_kfree_skb(skb);
+		goto nourbmem;
+	}
+
+	buf = usb_alloc_coherent(dev->udev, size, GFP_ATOMIC,
+				 &urb->transfer_dma);
+	if (!buf) {
+		dev_err(netdev->dev.parent, "No memory left for USB buffer\n");
+		stats->tx_dropped++;
+		dev_kfree_skb(skb);
+		goto nobufmem;
+	}
+
+	msg = (struct esd_usb2_msg *)buf;
+
+	msg->msg.hdr.len = 3; /* minimal length */
+	msg->msg.hdr.cmd = CMD_CAN_TX;
+	msg->msg.tx.net = priv->index;
+	msg->msg.tx.dlc = cf->can_dlc;
+	msg->msg.tx.id = cpu_to_le32(cf->can_id & CAN_ERR_MASK);
+
+	if (cf->can_id & CAN_RTR_FLAG)
+		msg->msg.tx.dlc |= ESD_RTR;
+
+	if (cf->can_id & CAN_EFF_FLAG)
+		msg->msg.tx.id |= cpu_to_le32(ESD_EXTID);
+
+	for (i = 0; i < cf->can_dlc; i++)
+		msg->msg.tx.data[i] = cf->data[i];
+
+	msg->msg.hdr.len += (cf->can_dlc + 3) >> 2;
+
+	for (i = 0; i < MAX_TX_URBS; i++) {
+		if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
+			context = &priv->tx_contexts[i];
+			break;
+		}
+	}
+
+	/*
+	 * This may never happen.
+	 */
+	if (!context) {
+		dev_warn(netdev->dev.parent, "couldn't find free context\n");
+		ret = NETDEV_TX_BUSY;
+		goto releasebuf;
+	}
+
+	context->priv = priv;
+	context->echo_index = i;
+	context->dlc = cf->can_dlc;
+
+	/* hnd must not be 0 - MSB is stripped in txdone handling */
+	msg->msg.tx.hnd = 0x80000000 | i; /* returned in TX done message */
+
+	usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), buf,
+			  msg->msg.hdr.len << 2,
+			  esd_usb2_write_bulk_callback, context);
+
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	usb_anchor_urb(urb, &priv->tx_submitted);
+
+	can_put_echo_skb(skb, netdev, context->echo_index);
+
+	atomic_inc(&priv->active_tx_jobs);
+
+	/* Slow down tx path */
+	if (atomic_read(&priv->active_tx_jobs) >= MAX_TX_URBS)
+		netif_stop_queue(netdev);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		can_free_echo_skb(netdev, context->echo_index);
+
+		atomic_dec(&priv->active_tx_jobs);
+		usb_unanchor_urb(urb);
+
+		stats->tx_dropped++;
+
+		if (err == -ENODEV)
+			netif_device_detach(netdev);
+		else
+			dev_warn(netdev->dev.parent, "failed tx_urb %d\n", err);
+
+		goto releasebuf;
+	}
+
+	netdev->trans_start = jiffies;
+
+	/*
+	 * Release our reference to this URB, the USB core will eventually free
+	 * it entirely.
+	 */
+	usb_free_urb(urb);
+
+	return NETDEV_TX_OK;
+
+releasebuf:
+	usb_free_coherent(dev->udev, size, buf, urb->transfer_dma);
+
+nobufmem:
+	usb_free_urb(urb);
+
+nourbmem:
+	return ret;
+}
+
+static int esd_usb2_close(struct net_device *netdev)
+{
+	struct esd_usb2_net_priv *priv = netdev_priv(netdev);
+	struct esd_usb2_msg msg;
+	int i;
+
+	/* Disable all IDs (see esd_usb2_start()) */
+	msg.msg.hdr.cmd = CMD_IDADD;
+	msg.msg.hdr.len = 2 + ESD_MAX_ID_SEGMENT;
+	msg.msg.filter.net = priv->index;
+	msg.msg.filter.option = ESD_ID_ENABLE; /* start with segment 0 */
+	for (i = 0; i <= ESD_MAX_ID_SEGMENT; i++)
+		msg.msg.filter.mask[i] = 0;
+	if (esd_usb2_send_msg(priv->usb2, &msg) < 0)
+		dev_err(netdev->dev.parent, "sending idadd message failed\n");
+
+	/* set CAN controller to reset mode */
+	msg.msg.hdr.len = 2;
+	msg.msg.hdr.cmd = CMD_SETBAUD;
+	msg.msg.setbaud.net = priv->index;
+	msg.msg.setbaud.rsvd = 0;
+	msg.msg.setbaud.baud = cpu_to_le32(ESD_USB2_NO_BAUDRATE);
+	if (esd_usb2_send_msg(priv->usb2, &msg) < 0)
+		dev_err(netdev->dev.parent, "sending setbaud message failed\n");
+
+	priv->can.state = CAN_STATE_STOPPED;
+
+	netif_stop_queue(netdev);
+
+	close_candev(netdev);
+
+	priv->open_time = 0;
+
+	return 0;
+}
+
+static const struct net_device_ops esd_usb2_netdev_ops = {
+	.ndo_open = esd_usb2_open,
+	.ndo_stop = esd_usb2_close,
+	.ndo_start_xmit = esd_usb2_start_xmit,
+};
+
+static struct can_bittiming_const esd_usb2_bittiming_const = {
+	.name = "esd_usb2",
+	.tseg1_min = ESD_USB2_TSEG1_MIN,
+	.tseg1_max = ESD_USB2_TSEG1_MAX,
+	.tseg2_min = ESD_USB2_TSEG2_MIN,
+	.tseg2_max = ESD_USB2_TSEG2_MAX,
+	.sjw_max = ESD_USB2_SJW_MAX,
+	.brp_min = ESD_USB2_BRP_MIN,
+	.brp_max = ESD_USB2_BRP_MAX,
+	.brp_inc = ESD_USB2_BRP_INC,
+};
+
+static int esd_usb2_set_bittiming(struct net_device *netdev)
+{
+	struct esd_usb2_net_priv *priv = netdev_priv(netdev);
+	struct can_bittiming *bt = &priv->can.bittiming;
+	struct esd_usb2_msg msg;
+	u32 canbtr;
+
+	canbtr = ESD_USB2_UBR;
+	canbtr |= (bt->brp - 1) & (ESD_USB2_BRP_MAX - 1);
+	canbtr |= ((bt->sjw - 1) & (ESD_USB2_SJW_MAX - 1))
+		<< ESD_USB2_SJW_SHIFT;
+	canbtr |= ((bt->prop_seg + bt->phase_seg1 - 1)
+		   & (ESD_USB2_TSEG1_MAX - 1))
+		<< ESD_USB2_TSEG1_SHIFT;
+	canbtr |= ((bt->phase_seg2 - 1) & (ESD_USB2_TSEG2_MAX - 1))
+		<< ESD_USB2_TSEG2_SHIFT;
+	if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+		canbtr |= ESD_USB2_3_SAMPLES;
+
+	msg.msg.hdr.len = 2;
+	msg.msg.hdr.cmd = CMD_SETBAUD;
+	msg.msg.setbaud.net = priv->index;
+	msg.msg.setbaud.rsvd = 0;
+	msg.msg.setbaud.baud = cpu_to_le32(canbtr);
+
+	dev_info(netdev->dev.parent, "setting BTR=%#x\n", canbtr);
+
+	return esd_usb2_send_msg(priv->usb2, &msg);
+}
+
+static int esd_usb2_get_berr_counter(const struct net_device *netdev,
+				     struct can_berr_counter *bec)
+{
+	struct esd_usb2_net_priv *priv = netdev_priv(netdev);
+
+	bec->txerr = priv->bec.txerr;
+	bec->rxerr = priv->bec.rxerr;
+
+	return 0;
+}
+
+static int esd_usb2_set_mode(struct net_device *netdev, enum can_mode mode)
+{
+	struct esd_usb2_net_priv *priv = netdev_priv(netdev);
+
+	if (!priv->open_time)
+		return -EINVAL;
+
+	switch (mode) {
+	case CAN_MODE_START:
+		netif_wake_queue(netdev);
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int esd_usb2_probe_one_net(struct usb_interface *intf, int index)
+{
+	struct esd_usb2 *dev = usb_get_intfdata(intf);
+	struct net_device *netdev;
+	struct esd_usb2_net_priv *priv;
+	int err = 0;
+	int i;
+
+	netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
+	if (!netdev) {
+		dev_err(&intf->dev, "couldn't alloc candev\n");
+		err = -ENOMEM;
+		goto done;
+	}
+
+	priv = netdev_priv(netdev);
+
+	init_usb_anchor(&priv->tx_submitted);
+	atomic_set(&priv->active_tx_jobs, 0);
+
+	for (i = 0; i < MAX_TX_URBS; i++)
+		priv->tx_contexts[i].echo_index = MAX_TX_URBS;
+
+	priv->usb2 = dev;
+	priv->netdev = netdev;
+	priv->index = index;
+
+	priv->can.state = CAN_STATE_STOPPED;
+	priv->can.clock.freq = ESD_USB2_CAN_CLOCK;
+	priv->can.bittiming_const = &esd_usb2_bittiming_const;
+	priv->can.do_set_bittiming = esd_usb2_set_bittiming;
+	priv->can.do_set_mode = esd_usb2_set_mode;
+	priv->can.do_get_berr_counter = esd_usb2_get_berr_counter;
+	priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
+
+	netdev->flags |= IFF_ECHO; /* we support local echo */
+
+	netdev->netdev_ops = &esd_usb2_netdev_ops;
+
+	SET_NETDEV_DEV(netdev, &intf->dev);
+
+	err = register_candev(netdev);
+	if (err) {
+		dev_err(&intf->dev,
+			"couldn't register CAN device: %d\n", err);
+		free_candev(netdev);
+		err = -ENOMEM;
+		goto done;
+	}
+
+	dev->nets[index] = priv;
+	dev_info(netdev->dev.parent, "device %s registered\n", netdev->name);
+
+done:
+	return err;
+}
+
+/*
+ * probe function for new USB2 devices
+ *
+ * check version information and number of available
+ * CAN interfaces
+ */
+static int esd_usb2_probe(struct usb_interface *intf,
+			 const struct usb_device_id *id)
+{
+	struct esd_usb2 *dev;
+	struct esd_usb2_msg msg;
+	int i, err;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		err = -ENOMEM;
+		goto done;
+	}
+
+	dev->udev = interface_to_usbdev(intf);
+
+	init_usb_anchor(&dev->rx_submitted);
+
+	usb_set_intfdata(intf, dev);
+
+	/* query number of CAN interfaces (nets) */
+	msg.msg.hdr.cmd = CMD_VERSION;
+	msg.msg.hdr.len = 2;
+	msg.msg.version.rsvd = 0;
+	msg.msg.version.flags = 0;
+	msg.msg.version.drv_version = 0;
+
+	err = esd_usb2_send_msg(dev, &msg);
+	if (err < 0) {
+		dev_err(&intf->dev, "sending version message failed\n");
+		goto free_dev;
+	}
+
+	err = esd_usb2_wait_msg(dev, &msg);
+	if (err < 0) {
+		dev_err(&intf->dev, "no version message answer\n");
+		goto free_dev;
+	}
+
+	dev->net_count = (int)msg.msg.version_reply.nets;
+	dev->version = le32_to_cpu(msg.msg.version_reply.version);
+
+	if (device_create_file(&intf->dev, &dev_attr_firmware))
+		dev_err(&intf->dev,
+			"Couldn't create device file for firmware\n");
+
+	if (device_create_file(&intf->dev, &dev_attr_hardware))
+		dev_err(&intf->dev,
+			"Couldn't create device file for hardware\n");
+
+	if (device_create_file(&intf->dev, &dev_attr_nets))
+		dev_err(&intf->dev,
+			"Couldn't create device file for nets\n");
+
+	/* do per device probing */
+	for (i = 0; i < dev->net_count; i++)
+		esd_usb2_probe_one_net(intf, i);
+
+	return 0;
+
+free_dev:
+	kfree(dev);
+done:
+	return err;
+}
+
+/*
+ * called by the usb core when the device is removed from the system
+ */
+static void esd_usb2_disconnect(struct usb_interface *intf)
+{
+	struct esd_usb2 *dev = usb_get_intfdata(intf);
+	struct net_device *netdev;
+	int i;
+
+	device_remove_file(&intf->dev, &dev_attr_firmware);
+	device_remove_file(&intf->dev, &dev_attr_hardware);
+	device_remove_file(&intf->dev, &dev_attr_nets);
+
+	usb_set_intfdata(intf, NULL);
+
+	if (dev) {
+		for (i = 0; i < dev->net_count; i++) {
+			if (dev->nets[i]) {
+				netdev = dev->nets[i]->netdev;
+				unregister_netdev(netdev);
+				free_candev(netdev);
+			}
+		}
+		unlink_all_urbs(dev);
+	}
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver esd_usb2_driver = {
+	.name = "esd_usb2",
+	.probe = esd_usb2_probe,
+	.disconnect = esd_usb2_disconnect,
+	.id_table = esd_usb2_table,
+};
+
+static int __init esd_usb2_init(void)
+{
+	int err;
+
+	/* register this driver with the USB subsystem */
+	err = usb_register(&esd_usb2_driver);
+
+	if (err) {
+		err("usb_register failed. Error number %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+module_init(esd_usb2_init);
+
+static void __exit esd_usb2_exit(void)
+{
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&esd_usb2_driver);
+}
+module_exit(esd_usb2_exit);
-- 
1.5.6.3

^ permalink raw reply related

* Re: [PATCH] [Bug 16494] NFS client over TCP hangs due to packet loss
From: Andy Chittenden @ 2010-08-03 10:25 UTC (permalink / raw)
  To: Andrew Morton
  Cc: David Miller, kuznet-v/Mj1YrvjDBInbfyfbPRSQ,
	pekkas-UjJjq++bwZ7HOG6cAo2yLw, jmorris-gx6/JNMH7DfYtjvyW6yDsg,
	yoshfuji-VfPWfsRibaP+Ru+s062T9g, kaber-dcUjhNyLwpNeoWH0uzbU5w,
	eric.dumazet-Re5JQEeQqe8AvxtiuMwx3w,
	William.Allen.Simpson-Re5JQEeQqe8AvxtiuMwx3w,
	gilad-f4XOiQkOAtcdH0auuBZGHA,
	ilpo.jarvinen-pxSi+dnQzZMxHbG02/KK1g,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-nfs-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20100803021110.f0b3877b.akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b@public.gmane.org>

On 2010-08-03 10:11, Andrew Morton wrote:
> (cc linux-nfs)
>
> On Tue, 03 Aug 2010 01:21:44 -0700 (PDT) David Miller<davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>  wrote:
>
>> From: "Andy Chittenden"<andyc.bluearc-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> Date: Tue, 3 Aug 2010 09:14:31 +0100
>>
>>> I don't know whether this patch is the correct fix or not but it enables the
>>> NFS client to recover.
>>>
>>> Kernel version: 2.6.34.1 and 2.6.32.
>>>
>>> Fixes<https://bugzilla.kernel.org/show_bug.cgi?id=16494>. It clears down
>>> any previous shutdown attempts so that reconnects on a socket that's been
>>> shutdown leave the socket in a usable state (otherwise tcp_sendmsg() returns
>>> -EPIPE).
>>
>> If the SunRPC code wants to close a TCP socket then use it again,
>> it should disconnect by doing a connect() with sa_family == AF_UNSPEC

There is code to do that in the SunRPC code in xs_abort_connection() but 
that's conditionally called from xs_tcp_reuse_connection():

static void xs_tcp_reuse_connection(struct rpc_xprt *xprt, struct 
sock_xprt *transport)
{
	unsigned int state = transport->inet->sk_state;

	if (state == TCP_CLOSE && transport->sock->state == SS_UNCONNECTED)
		return;
	if ((1 << state) & (TCPF_ESTABLISHED|TCPF_SYN_SENT))
		return;
	xs_abort_connection(xprt, transport);
}

That's changed since 2.6.26 where it unconditionally did the connect() 
with sa_family == AF_UNSPEC. FWIW we cannot reproduce this problem with 
2.6.26.


--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH] [Bug 16494] NFS client over TCP hangs due to packet loss
From: Andrew Morton @ 2010-08-03  9:11 UTC (permalink / raw)
  To: David Miller
  Cc: andyc.bluearc-Re5JQEeQqe8AvxtiuMwx3w,
	kuznet-v/Mj1YrvjDBInbfyfbPRSQ, pekkas-UjJjq++bwZ7HOG6cAo2yLw,
	jmorris-gx6/JNMH7DfYtjvyW6yDsg, yoshfuji-VfPWfsRibaP+Ru+s062T9g,
	kaber-dcUjhNyLwpNeoWH0uzbU5w, eric.dumazet-Re5JQEeQqe8AvxtiuMwx3w,
	William.Allen.Simpson-Re5JQEeQqe8AvxtiuMwx3w,
	gilad-f4XOiQkOAtcdH0auuBZGHA,
	ilpo.jarvinen-pxSi+dnQzZMxHbG02/KK1g,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-nfs-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20100803.012144.267950450.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>

(cc linux-nfs)

On Tue, 03 Aug 2010 01:21:44 -0700 (PDT) David Miller <davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org> wrote:

> From: "Andy Chittenden" <andyc.bluearc-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Date: Tue, 3 Aug 2010 09:14:31 +0100
> 
> > I don't know whether this patch is the correct fix or not but it enables the
> > NFS client to recover.
> > 
> > Kernel version: 2.6.34.1 and 2.6.32.
> > 
> > Fixes <https://bugzilla.kernel.org/show_bug.cgi?id=16494>. It clears down
> > any previous shutdown attempts so that reconnects on a socket that's been
> > shutdown leave the socket in a usable state (otherwise tcp_sendmsg() returns
> > -EPIPE).
> 
> If the SunRPC code wants to close a TCP socket then use it again,
> it should disconnect by doing a connect() with sa_family == AF_UNSPEC
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: RFC: New BGF 'LOOP' instruction
From: Hagen Paul Pfeifer @ 2010-08-03  9:10 UTC (permalink / raw)
  To: David Miller; +Cc: leonerd, netdev
In-Reply-To: <20100803.001904.63020040.davem@davemloft.net>


On Tue, 03 Aug 2010 00:19:04 -0700 (PDT), David Miller wrote:

> From: Paul LeoNerd Evans <leonerd@leonerd.org.uk>



>> Rightnow, BPF is all but useless for parsing, say, IPv6. I only pick

>> IPv6 as one example, I'm sure there must exist a great number more

>> packet-based protocols that use a "linked-list" style approach to

>> headers. None of those are currently filterable on the current set of

>> instructions. LOOP would allow these.

> 

> It's not meant for detailed packet protocol header analysis,

> it's for stateless straight line matching of masked values

> in packet headers.



David is right, BPF cannot - and will not - keep with any high level

connection tracking packet filter. There is an processing trade-off between

packet classification and packet storage with post processing analysis.



Hagen

^ permalink raw reply

* Re: RFC: New BGF 'LOOP' instruction
From: Hagen Paul Pfeifer @ 2010-08-03  9:03 UTC (permalink / raw)
  To: David Miller; +Cc: leonerd, netdev
In-Reply-To: <20100802.221813.43045517.davem@davemloft.net>


On Mon, 02 Aug 2010 22:18:13 -0700 (PDT), David Miller wrote:



> Oh yeah, what is an iteration in your definition?  See this is why I

> totally refuse to add a looping construct to BPF.

> 

> If you just check for a single loop hitting, the user will just use

> a chaining of two looping constructs.  And then three levels of

> indirection, then four, etc.  He can run up to just before exhasting

> the "iteration limit" of one loop, and branch to the next one, and

> so on and so forth.



I am aware of any problems caused by complex instructions. David, I was

rather curious to see an unrecognized and ground breaking instructions, I

don't wanted to scotch any (possible) improvement.



HGN

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox