* Re: [PATCH 10/14] [rndis_host] Add rndis_early_init function pointer to 'struct rndis_data'.
From: David Brownell @ 2008-01-25 1:10 UTC (permalink / raw)
To: Jussi Kivilinna
Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA, bjd-a1rhEgazXTw,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20080120001438.25718.96722.stgit-q/85JClnwdg@public.gmane.org>
On Saturday 19 January 2008, Jussi Kivilinna wrote:
> Function pointer is for rndis minidrivers that need to do work on device right
> after RNDIS_INIT. For example setting device specific configuration parameters
> with OID_GEN_RNDIS_CONFIG_PARAMETER.
>
> Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
Could this -- and #11/14 -- instead be generalized a bit,
so they're not RNDIS-specific? At least in name; the
only user for now would be the rndis_host code.
The generalization would presumably be "early_init" and
"link_change", paired with doc comments reflecting that
they're usable by any driver stack built over the usbnet
framework core.
There's no point IMO to having generalizable hooks be
restricted this way.
- Dave
> ---
>
> drivers/net/usb/rndis_host.c | 6 ++++++
> drivers/net/usb/usbnet.h | 3 +++
> 2 files changed, 9 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
> index 1d6bf0a..22e5ca1 100644
> --- a/drivers/net/usb/rndis_host.c
> +++ b/drivers/net/usb/rndis_host.c
> @@ -336,6 +336,12 @@ int generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf)
> dev->hard_mtu, tmp, dev->rx_urb_size,
> 1 << le32_to_cpu(u.init_c->packet_alignment));
>
> + /* module has some device initialization code needs to be done right
> + * after RNDIS_INIT */
> + if (dev->driver_info->rndis_early_init &&
> + dev->driver_info->rndis_early_init(dev) != 0)
> + goto halt_fail_and_release;
> +
> /* Get designated host ethernet address */
> reply_len = ETH_ALEN;
> retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS,
> diff --git a/drivers/net/usb/usbnet.h b/drivers/net/usb/usbnet.h
> index 0b4bf09..2bc5f76 100644
> --- a/drivers/net/usb/usbnet.h
> +++ b/drivers/net/usb/usbnet.h
> @@ -116,6 +116,9 @@ struct driver_info {
> struct sk_buff *(*tx_fixup)(struct usbnet *dev,
> struct sk_buff *skb, gfp_t flags);
>
> + /* rndis minidriver early initialization code, can sleep */
> + int (*rndis_early_init)(struct usbnet *dev);
> +
> /* for new devices, use the descriptor-reading code instead */
> int in; /* rx endpoint */
> int out; /* tx endpoint */
>
^ permalink raw reply
* Re: [PATCH 09/26] atl1: refactor tx processing
From: Chris Snook @ 2008-01-25 1:08 UTC (permalink / raw)
To: Jay Cliburn; +Cc: Jeff Garzik, linux-kernel, atl1-devel, netdev
In-Reply-To: <20080124190024.1b81238e@osprey.hogchain.net>
Jay Cliburn wrote:
> On Tue, 22 Jan 2008 18:31:09 -0600
> Jay Cliburn <jacliburn@bellsouth.net> wrote:
>
>> On Tue, 22 Jan 2008 04:58:17 -0500
>> Jeff Garzik <jeff@garzik.org> wrote:
>>
> [...]
>>> for such a huge patch, this description is very tiny. [describe]
>>> what is refactored, and why.
>
> Is this one any better?
This satisfies me.
Acked-by: Chris Snook <csnook@redhat.com>
> From df475e2eea401f9dc18ca23dab538b99fb9e710c Mon Sep 17 00:00:00 2001
> From: Jay Cliburn <jacliburn@bellsouth.net>
> Date: Wed, 23 Jan 2008 21:36:36 -0600
> Subject: [PATCH] atl1: simplify tx packet descriptor
>
> The transmit packet descriptor consists of four 32-bit words, with word 3
> upper bits overloaded depending upon the condition of its bits 3 and 4.
> The driver currently duplicates all word 2 and some word 3 register bit
> definitions unnecessarily and also uses a set of nested structures in its
> definition of the TPD without good cause. This patch adds a lengthy
> comment describing the TPD, eliminates duplicate TPD bit definitions,
> and simplifies the TPD structure itself. It also expands the TSO check
> to correctly handle custom checksum versus TSO processing using the revised
> TPD definitions. Finally, shorten some variable names in the transmit
> processing path to reduce line lengths, rename some variables to better
> describe their purpose (e.g., nseg versus m), and add a comment or two
> to better describe what the code is doing.
>
> Signed-off-by: Jay Cliburn <jacliburn@bellsouth.net>
^ permalink raw reply
* Re: [PATCH 09/26] atl1: refactor tx processing
From: Jay Cliburn @ 2008-01-25 1:00 UTC (permalink / raw)
To: Jay Cliburn; +Cc: Jeff Garzik, csnook, linux-kernel, atl1-devel, netdev
In-Reply-To: <20080122183109.5ab80802@osprey.hogchain.net>
On Tue, 22 Jan 2008 18:31:09 -0600
Jay Cliburn <jacliburn@bellsouth.net> wrote:
> On Tue, 22 Jan 2008 04:58:17 -0500
> Jeff Garzik <jeff@garzik.org> wrote:
>
[...]
> > for such a huge patch, this description is very tiny. [describe]
> > what is refactored, and why.
Is this one any better?
>From df475e2eea401f9dc18ca23dab538b99fb9e710c Mon Sep 17 00:00:00 2001
From: Jay Cliburn <jacliburn@bellsouth.net>
Date: Wed, 23 Jan 2008 21:36:36 -0600
Subject: [PATCH] atl1: simplify tx packet descriptor
The transmit packet descriptor consists of four 32-bit words, with word 3
upper bits overloaded depending upon the condition of its bits 3 and 4.
The driver currently duplicates all word 2 and some word 3 register bit
definitions unnecessarily and also uses a set of nested structures in its
definition of the TPD without good cause. This patch adds a lengthy
comment describing the TPD, eliminates duplicate TPD bit definitions,
and simplifies the TPD structure itself. It also expands the TSO check
to correctly handle custom checksum versus TSO processing using the revised
TPD definitions. Finally, shorten some variable names in the transmit
processing path to reduce line lengths, rename some variables to better
describe their purpose (e.g., nseg versus m), and add a comment or two
to better describe what the code is doing.
Signed-off-by: Jay Cliburn <jacliburn@bellsouth.net>
---
drivers/net/atlx/atl1.c | 265 +++++++++++++++++++++++++----------------------
drivers/net/atlx/atl1.h | 201 +++++++++++++++++++-----------------
2 files changed, 246 insertions(+), 220 deletions(-)
diff --git a/drivers/net/atlx/atl1.c b/drivers/net/atlx/atl1.c
index 1f564f0..f4add3c 100644
--- a/drivers/net/atlx/atl1.c
+++ b/drivers/net/atlx/atl1.c
@@ -1259,8 +1259,6 @@ static void atl1_intr_tx(struct atl1_adapter *adapter)
dev_kfree_skb_irq(buffer_info->skb);
buffer_info->skb = NULL;
}
- tpd->buffer_addr = 0;
- tpd->desc.data = 0;
if (++sw_tpd_next_to_clean == tpd_ring->count)
sw_tpd_next_to_clean = 0;
@@ -1282,48 +1280,69 @@ static u16 atl1_tpd_avail(struct atl1_tpd_ring *tpd_ring)
}
static int atl1_tso(struct atl1_adapter *adapter, struct sk_buff *skb,
- struct tso_param *tso)
+ struct tx_packet_desc *ptpd)
{
- /* We enter this function holding a spinlock. */
- u8 ipofst;
+ /* spinlock held */
+ u8 hdr_len, ip_off;
+ u32 real_len;
int err;
if (skb_shinfo(skb)->gso_size) {
if (skb_header_cloned(skb)) {
err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
if (unlikely(err))
- return err;
+ return -1;
}
if (skb->protocol == ntohs(ETH_P_IP)) {
struct iphdr *iph = ip_hdr(skb);
- iph->tot_len = 0;
+ real_len = (((unsigned char *)iph - skb->data) +
+ ntohs(iph->tot_len));
+ if (real_len < skb->len)
+ pskb_trim(skb, real_len);
+ hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb));
+ if (skb->len == hdr_len) {
+ iph->check = 0;
+ tcp_hdr(skb)->check =
+ ~csum_tcpudp_magic(iph->saddr,
+ iph->daddr, tcp_hdrlen(skb),
+ IPPROTO_TCP, 0);
+ ptpd->word3 |= (iph->ihl & TPD_IPHL_MASK) <<
+ TPD_IPHL_SHIFT;
+ ptpd->word3 |= ((tcp_hdrlen(skb) >> 2) &
+ TPD_TCPHDRLEN_MASK) <<
+ TPD_TCPHDRLEN_SHIFT;
+ ptpd->word3 |= 1 << TPD_IP_CSUM_SHIFT;
+ ptpd->word3 |= 1 << TPD_TCP_CSUM_SHIFT;
+ return 1;
+ }
+
iph->check = 0;
tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
- iph->daddr, 0, IPPROTO_TCP, 0);
- ipofst = skb_network_offset(skb);
- if (ipofst != ETH_HLEN) /* 802.3 frame */
- tso->tsopl |= 1 << TSO_PARAM_ETHTYPE_SHIFT;
-
- tso->tsopl |= (iph->ihl &
- TSO_PARAM_IPHL_MASK) << TSO_PARAM_IPHL_SHIFT;
- tso->tsopl |= ((tcp_hdrlen(skb) >> 2) &
- TSO_PARAM_TCPHDRLEN_MASK) <<
- TSO_PARAM_TCPHDRLEN_SHIFT;
- tso->tsopl |= (skb_shinfo(skb)->gso_size &
- TSO_PARAM_MSS_MASK) << TSO_PARAM_MSS_SHIFT;
- tso->tsopl |= 1 << TSO_PARAM_IPCKSUM_SHIFT;
- tso->tsopl |= 1 << TSO_PARAM_TCPCKSUM_SHIFT;
- tso->tsopl |= 1 << TSO_PARAM_SEGMENT_SHIFT;
- return true;
+ iph->daddr, 0, IPPROTO_TCP, 0);
+ ip_off = (unsigned char *)iph -
+ (unsigned char *) skb_network_header(skb);
+ if (ip_off == 8) /* 802.3-SNAP frame */
+ ptpd->word3 |= 1 << TPD_ETHTYPE_SHIFT;
+ else if (ip_off != 0)
+ return -2;
+
+ ptpd->word3 |= (iph->ihl & TPD_IPHL_MASK) <<
+ TPD_IPHL_SHIFT;
+ ptpd->word3 |= ((tcp_hdrlen(skb) >> 2) &
+ TPD_TCPHDRLEN_MASK) << TPD_TCPHDRLEN_SHIFT;
+ ptpd->word3 |= (skb_shinfo(skb)->gso_size &
+ TPD_MSS_MASK) << TPD_MSS_SHIFT;
+ ptpd->word3 |= 1 << TPD_SEGMENT_EN_SHIFT;
+ return 3;
}
}
return false;
}
static int atl1_tx_csum(struct atl1_adapter *adapter, struct sk_buff *skb,
- struct csum_param *csum)
+ struct tx_packet_desc *ptpd)
{
u8 css, cso;
@@ -1335,115 +1354,116 @@ static int atl1_tx_csum(struct atl1_adapter *adapter, struct sk_buff *skb,
"payload offset not an even number\n");
return -1;
}
- csum->csumpl |= (cso & CSUM_PARAM_PLOADOFFSET_MASK) <<
- CSUM_PARAM_PLOADOFFSET_SHIFT;
- csum->csumpl |= (css & CSUM_PARAM_XSUMOFFSET_MASK) <<
- CSUM_PARAM_XSUMOFFSET_SHIFT;
- csum->csumpl |= 1 << CSUM_PARAM_CUSTOMCKSUM_SHIFT;
+ ptpd->word3 |= (cso & TPD_PLOADOFFSET_MASK) <<
+ TPD_PLOADOFFSET_SHIFT;
+ ptpd->word3 |= (css & TPD_CCSUMOFFSET_MASK) <<
+ TPD_CCSUMOFFSET_SHIFT;
+ ptpd->word3 |= 1 << TPD_CUST_CSUM_EN_SHIFT;
return true;
}
-
- return true;
+ return 0;
}
static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb,
- bool tcp_seg)
+ struct tx_packet_desc *ptpd)
{
- /* We enter this function holding a spinlock. */
+ /* spinlock held */
struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
struct atl1_buffer *buffer_info;
+ u16 buf_len = skb->len;
struct page *page;
- int first_buf_len = skb->len;
unsigned long offset;
unsigned int nr_frags;
unsigned int f;
- u16 tpd_next_to_use;
- u16 proto_hdr_len;
- u16 len12;
+ int retval;
+ u16 next_to_use;
+ u16 data_len;
+ u8 hdr_len;
- first_buf_len -= skb->data_len;
+ buf_len -= skb->data_len;
nr_frags = skb_shinfo(skb)->nr_frags;
- tpd_next_to_use = atomic_read(&tpd_ring->next_to_use);
- buffer_info = &tpd_ring->buffer_info[tpd_next_to_use];
+ next_to_use = atomic_read(&tpd_ring->next_to_use);
+ buffer_info = &tpd_ring->buffer_info[next_to_use];
if (unlikely(buffer_info->skb))
BUG();
/* put skb in last TPD */
buffer_info->skb = NULL;
- if (tcp_seg) {
- /* TSO/GSO */
- proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
- buffer_info->length = proto_hdr_len;
+ retval = (ptpd->word3 >> TPD_SEGMENT_EN_SHIFT) & TPD_SEGMENT_EN_MASK;
+ if (retval) {
+ /* TSO */
+ hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+ buffer_info->length = hdr_len;
page = virt_to_page(skb->data);
offset = (unsigned long)skb->data & ~PAGE_MASK;
buffer_info->dma = pci_map_page(adapter->pdev, page,
- offset, proto_hdr_len,
+ offset, hdr_len,
PCI_DMA_TODEVICE);
- if (++tpd_next_to_use == tpd_ring->count)
- tpd_next_to_use = 0;
+ if (++next_to_use == tpd_ring->count)
+ next_to_use = 0;
- if (first_buf_len > proto_hdr_len) {
- int i, m;
+ if (buf_len > hdr_len) {
+ int i, nseg;
- len12 = first_buf_len - proto_hdr_len;
- m = (len12 + ATL1_MAX_TX_BUF_LEN - 1) /
+ data_len = buf_len - hdr_len;
+ nseg = (data_len + ATL1_MAX_TX_BUF_LEN - 1) /
ATL1_MAX_TX_BUF_LEN;
- for (i = 0; i < m; i++) {
+ for (i = 0; i < nseg; i++) {
buffer_info =
- &tpd_ring->buffer_info[tpd_next_to_use];
+ &tpd_ring->buffer_info[next_to_use];
buffer_info->skb = NULL;
buffer_info->length =
(ATL1_MAX_TX_BUF_LEN >=
- len12) ? ATL1_MAX_TX_BUF_LEN : len12;
- len12 -= buffer_info->length;
+ data_len) ? ATL1_MAX_TX_BUF_LEN : data_len;
+ data_len -= buffer_info->length;
page = virt_to_page(skb->data +
- (proto_hdr_len +
- i * ATL1_MAX_TX_BUF_LEN));
+ (hdr_len + i * ATL1_MAX_TX_BUF_LEN));
offset = (unsigned long)(skb->data +
- (proto_hdr_len +
- i * ATL1_MAX_TX_BUF_LEN)) & ~PAGE_MASK;
+ (hdr_len + i * ATL1_MAX_TX_BUF_LEN)) &
+ ~PAGE_MASK;
buffer_info->dma = pci_map_page(adapter->pdev,
page, offset, buffer_info->length,
PCI_DMA_TODEVICE);
- if (++tpd_next_to_use == tpd_ring->count)
- tpd_next_to_use = 0;
+ if (++next_to_use == tpd_ring->count)
+ next_to_use = 0;
}
}
} else {
- /* not TSO/GSO */
- buffer_info->length = first_buf_len;
+ /* not TSO */
+ buffer_info->length = buf_len;
page = virt_to_page(skb->data);
offset = (unsigned long)skb->data & ~PAGE_MASK;
buffer_info->dma = pci_map_page(adapter->pdev, page,
- offset, first_buf_len, PCI_DMA_TODEVICE);
- if (++tpd_next_to_use == tpd_ring->count)
- tpd_next_to_use = 0;
+ offset, buf_len, PCI_DMA_TODEVICE);
+ if (++next_to_use == tpd_ring->count)
+ next_to_use = 0;
}
for (f = 0; f < nr_frags; f++) {
struct skb_frag_struct *frag;
- u16 lenf, i, m;
+ u16 i, nseg;
frag = &skb_shinfo(skb)->frags[f];
- lenf = frag->size;
+ buf_len = frag->size;
- m = (lenf + ATL1_MAX_TX_BUF_LEN - 1) / ATL1_MAX_TX_BUF_LEN;
- for (i = 0; i < m; i++) {
- buffer_info = &tpd_ring->buffer_info[tpd_next_to_use];
+ nseg = (buf_len + ATL1_MAX_TX_BUF_LEN - 1) /
+ ATL1_MAX_TX_BUF_LEN;
+ for (i = 0; i < nseg; i++) {
+ buffer_info = &tpd_ring->buffer_info[next_to_use];
if (unlikely(buffer_info->skb))
BUG();
buffer_info->skb = NULL;
- buffer_info->length = (lenf > ATL1_MAX_TX_BUF_LEN) ?
- ATL1_MAX_TX_BUF_LEN : lenf;
- lenf -= buffer_info->length;
+ buffer_info->length = (buf_len > ATL1_MAX_TX_BUF_LEN) ?
+ ATL1_MAX_TX_BUF_LEN : buf_len;
+ buf_len -= buffer_info->length;
buffer_info->dma = pci_map_page(adapter->pdev,
frag->page,
frag->page_offset + (i * ATL1_MAX_TX_BUF_LEN),
buffer_info->length, PCI_DMA_TODEVICE);
- if (++tpd_next_to_use == tpd_ring->count)
- tpd_next_to_use = 0;
+ if (++next_to_use == tpd_ring->count)
+ next_to_use = 0;
}
}
@@ -1451,39 +1471,44 @@ static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb,
buffer_info->skb = skb;
}
-static void atl1_tx_queue(struct atl1_adapter *adapter, int count,
- union tpd_descr *descr)
+static void atl1_tx_queue(struct atl1_adapter *adapter, u16 count,
+ struct tx_packet_desc *ptpd)
{
- /* We enter this function holding a spinlock. */
+ /* spinlock held */
struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
- int j;
- u32 val;
struct atl1_buffer *buffer_info;
struct tx_packet_desc *tpd;
- u16 tpd_next_to_use = atomic_read(&tpd_ring->next_to_use);
+ u16 j;
+ u32 val;
+ u16 next_to_use = (u16) atomic_read(&tpd_ring->next_to_use);
for (j = 0; j < count; j++) {
- buffer_info = &tpd_ring->buffer_info[tpd_next_to_use];
- tpd = ATL1_TPD_DESC(&adapter->tpd_ring, tpd_next_to_use);
- tpd->desc.csum.csumpu = descr->csum.csumpu;
- tpd->desc.csum.csumpl = descr->csum.csumpl;
- tpd->desc.tso.tsopu = descr->tso.tsopu;
- tpd->desc.tso.tsopl = descr->tso.tsopl;
+ buffer_info = &tpd_ring->buffer_info[next_to_use];
+ tpd = ATL1_TPD_DESC(&adapter->tpd_ring, next_to_use);
+ if (tpd != ptpd)
+ memcpy(tpd, ptpd, sizeof(struct tx_packet_desc));
tpd->buffer_addr = cpu_to_le64(buffer_info->dma);
- tpd->desc.data = descr->data;
- tpd->desc.tso.tsopu |= (cpu_to_le16(buffer_info->length) &
- TSO_PARAM_BUFLEN_MASK) << TSO_PARAM_BUFLEN_SHIFT;
+ tpd->word2 = (cpu_to_le16(buffer_info->length) &
+ TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT;
- val = (descr->tso.tsopl >> TSO_PARAM_SEGMENT_SHIFT) &
- TSO_PARAM_SEGMENT_MASK;
- if (val && !j)
- tpd->desc.tso.tsopl |= 1 << TSO_PARAM_HDRFLAG_SHIFT;
+ /*
+ * if this is the first packet in a TSO chain, set
+ * TPD_HDRFLAG, otherwise, clear it.
+ */
+ val = (tpd->word3 >> TPD_SEGMENT_EN_SHIFT) &
+ TPD_SEGMENT_EN_MASK;
+ if (val) {
+ if (!j)
+ tpd->word3 |= 1 << TPD_HDRFLAG_SHIFT;
+ else
+ tpd->word3 &= ~(1 << TPD_HDRFLAG_SHIFT);
+ }
if (j == (count - 1))
- tpd->desc.tso.tsopl |= 1 << TSO_PARAM_EOP_SHIFT;
+ tpd->word3 |= 1 << TPD_EOP_SHIFT;
- if (++tpd_next_to_use == tpd_ring->count)
- tpd_next_to_use = 0;
+ if (++next_to_use == tpd_ring->count)
+ next_to_use = 0;
}
/*
* Force memory writes to complete before letting h/w
@@ -1493,18 +1518,18 @@ static void atl1_tx_queue(struct atl1_adapter *adapter, int count,
*/
wmb();
- atomic_set(&tpd_ring->next_to_use, (int)tpd_next_to_use);
+ atomic_set(&tpd_ring->next_to_use, next_to_use);
}
static int atl1_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring;
int len = skb->len;
int tso;
int count = 1;
int ret_val;
- u32 val;
- union tpd_descr param;
+ struct tx_packet_desc *ptpd;
u16 frag_size;
u16 vlan_tag;
unsigned long flags;
@@ -1515,18 +1540,11 @@ static int atl1_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
len -= skb->data_len;
- if (unlikely(skb->len == 0)) {
+ if (unlikely(skb->len <= 0)) {
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
- param.data = 0;
- param.tso.tsopu = 0;
- param.tso.tsopl = 0;
- param.csum.csumpu = 0;
- param.csum.csumpl = 0;
-
- /* nr_frags will be nonzero if we're doing scatter/gather (SG) */
nr_frags = skb_shinfo(skb)->nr_frags;
for (f = 0; f < nr_frags; f++) {
frag_size = skb_shinfo(skb)->frags[f].size;
@@ -1535,10 +1553,9 @@ static int atl1_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
ATL1_MAX_TX_BUF_LEN;
}
- /* mss will be nonzero if we're doing segment offload (TSO/GSO) */
mss = skb_shinfo(skb)->gso_size;
if (mss) {
- if (skb->protocol == htons(ETH_P_IP)) {
+ if (skb->protocol == ntohs(ETH_P_IP)) {
proto_hdr_len = (skb_transport_offset(skb) +
tcp_hdrlen(skb));
if (unlikely(proto_hdr_len > len)) {
@@ -1567,18 +1584,20 @@ static int atl1_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
return NETDEV_TX_BUSY;
}
- param.data = 0;
+ ptpd = ATL1_TPD_DESC(tpd_ring,
+ (u16) atomic_read(&tpd_ring->next_to_use));
+ memset(ptpd, 0, sizeof(struct tx_packet_desc));
if (adapter->vlgrp && vlan_tx_tag_present(skb)) {
vlan_tag = vlan_tx_tag_get(skb);
vlan_tag = (vlan_tag << 4) | (vlan_tag >> 13) |
((vlan_tag >> 9) & 0x8);
- param.tso.tsopl |= 1 << TSO_PARAM_INSVLAG_SHIFT;
- param.tso.tsopu |= (vlan_tag & TSO_PARAM_VLANTAG_MASK) <<
- TSO_PARAM_VLAN_SHIFT;
+ ptpd->word3 |= 1 << TPD_INS_VL_TAG_SHIFT;
+ ptpd->word3 |= (vlan_tag & TPD_VL_TAGGED_MASK) <<
+ TPD_VL_TAGGED_SHIFT;
}
- tso = atl1_tso(adapter, skb, ¶m.tso);
+ tso = atl1_tso(adapter, skb, ptpd);
if (tso < 0) {
spin_unlock_irqrestore(&adapter->lock, flags);
dev_kfree_skb_any(skb);
@@ -1586,7 +1605,7 @@ static int atl1_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
}
if (!tso) {
- ret_val = atl1_tx_csum(adapter, skb, ¶m.csum);
+ ret_val = atl1_tx_csum(adapter, skb, ptpd);
if (ret_val < 0) {
spin_unlock_irqrestore(&adapter->lock, flags);
dev_kfree_skb_any(skb);
@@ -1594,13 +1613,11 @@ static int atl1_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
}
}
- val = (param.tso.tsopl >> TSO_PARAM_SEGMENT_SHIFT) &
- TSO_PARAM_SEGMENT_MASK;
- atl1_tx_map(adapter, skb, 1 == val);
- atl1_tx_queue(adapter, count, ¶m);
- netdev->trans_start = jiffies;
- spin_unlock_irqrestore(&adapter->lock, flags);
+ atl1_tx_map(adapter, skb, ptpd);
+ atl1_tx_queue(adapter, count, ptpd);
atl1_update_mailbox(adapter);
+ spin_unlock_irqrestore(&adapter->lock, flags);
+ netdev->trans_start = jiffies;
return NETDEV_TX_OK;
}
@@ -2759,7 +2776,7 @@ const struct ethtool_ops atl1_ethtool_ops = {
.get_ringparam = atl1_get_ringparam,
.set_ringparam = atl1_set_ringparam,
.get_pauseparam = atl1_get_pauseparam,
- .set_pauseparam = atl1_set_pauseparam,
+ .set_pauseparam = atl1_set_pauseparam,
.get_rx_csum = atl1_get_rx_csum,
.set_tx_csum = ethtool_op_set_tx_hw_csum,
.get_link = ethtool_op_get_link,
diff --git a/drivers/net/atlx/atl1.h b/drivers/net/atlx/atl1.h
index 30c5a8d..4d3d65b 100644
--- a/drivers/net/atlx/atl1.h
+++ b/drivers/net/atlx/atl1.h
@@ -452,106 +452,115 @@ struct rx_free_desc {
/* __attribute__ ((packed)) is required */
} __attribute__ ((packed));
-/* tsopu defines */
-#define TSO_PARAM_BUFLEN_MASK 0x3FFF
-#define TSO_PARAM_BUFLEN_SHIFT 0
-#define TSO_PARAM_DMAINT_MASK 0x0001
-#define TSO_PARAM_DMAINT_SHIFT 14
-#define TSO_PARAM_PKTNT_MASK 0x0001
-#define TSO_PARAM_PKTINT_SHIFT 15
-#define TSO_PARAM_VLANTAG_MASK 0xFFFF
-#define TSO_PARAM_VLAN_SHIFT 16
-
-/* tsopl defines */
-#define TSO_PARAM_EOP_MASK 0x0001
-#define TSO_PARAM_EOP_SHIFT 0
-#define TSO_PARAM_COALESCE_MASK 0x0001
-#define TSO_PARAM_COALESCE_SHIFT 1
-#define TSO_PARAM_INSVLAG_MASK 0x0001
-#define TSO_PARAM_INSVLAG_SHIFT 2
-#define TSO_PARAM_CUSTOMCKSUM_MASK 0x0001
-#define TSO_PARAM_CUSTOMCKSUM_SHIFT 3
-#define TSO_PARAM_SEGMENT_MASK 0x0001
-#define TSO_PARAM_SEGMENT_SHIFT 4
-#define TSO_PARAM_IPCKSUM_MASK 0x0001
-#define TSO_PARAM_IPCKSUM_SHIFT 5
-#define TSO_PARAM_TCPCKSUM_MASK 0x0001
-#define TSO_PARAM_TCPCKSUM_SHIFT 6
-#define TSO_PARAM_UDPCKSUM_MASK 0x0001
-#define TSO_PARAM_UDPCKSUM_SHIFT 7
-#define TSO_PARAM_VLANTAGGED_MASK 0x0001
-#define TSO_PARAM_VLANTAGGED_SHIFT 8
-#define TSO_PARAM_ETHTYPE_MASK 0x0001
-#define TSO_PARAM_ETHTYPE_SHIFT 9
-#define TSO_PARAM_IPHL_MASK 0x000F
-#define TSO_PARAM_IPHL_SHIFT 10
-#define TSO_PARAM_TCPHDRLEN_MASK 0x000F
-#define TSO_PARAM_TCPHDRLEN_SHIFT 14
-#define TSO_PARAM_HDRFLAG_MASK 0x0001
-#define TSO_PARAM_HDRFLAG_SHIFT 18
-#define TSO_PARAM_MSS_MASK 0x1FFF
-#define TSO_PARAM_MSS_SHIFT 19
-
-/* csumpu defines */
-#define CSUM_PARAM_BUFLEN_MASK 0x3FFF
-#define CSUM_PARAM_BUFLEN_SHIFT 0
-#define CSUM_PARAM_DMAINT_MASK 0x0001
-#define CSUM_PARAM_DMAINT_SHIFT 14
-#define CSUM_PARAM_PKTINT_MASK 0x0001
-#define CSUM_PARAM_PKTINT_SHIFT 15
-#define CSUM_PARAM_VALANTAG_MASK 0xFFFF
-#define CSUM_PARAM_VALAN_SHIFT 16
-
-/* csumpl defines*/
-#define CSUM_PARAM_EOP_MASK 0x0001
-#define CSUM_PARAM_EOP_SHIFT 0
-#define CSUM_PARAM_COALESCE_MASK 0x0001
-#define CSUM_PARAM_COALESCE_SHIFT 1
-#define CSUM_PARAM_INSVLAG_MASK 0x0001
-#define CSUM_PARAM_INSVLAG_SHIFT 2
-#define CSUM_PARAM_CUSTOMCKSUM_MASK 0x0001
-#define CSUM_PARAM_CUSTOMCKSUM_SHIFT 3
-#define CSUM_PARAM_SEGMENT_MASK 0x0001
-#define CSUM_PARAM_SEGMENT_SHIFT 4
-#define CSUM_PARAM_IPCKSUM_MASK 0x0001
-#define CSUM_PARAM_IPCKSUM_SHIFT 5
-#define CSUM_PARAM_TCPCKSUM_MASK 0x0001
-#define CSUM_PARAM_TCPCKSUM_SHIFT 6
-#define CSUM_PARAM_UDPCKSUM_MASK 0x0001
-#define CSUM_PARAM_UDPCKSUM_SHIFT 7
-#define CSUM_PARAM_VLANTAGGED_MASK 0x0001
-#define CSUM_PARAM_VLANTAGGED_SHIFT 8
-#define CSUM_PARAM_ETHTYPE_MASK 0x0001
-#define CSUM_PARAM_ETHTYPE_SHIFT 9
-#define CSUM_PARAM_IPHL_MASK 0x000F
-#define CSUM_PARAM_IPHL_SHIFT 10
-#define CSUM_PARAM_PLOADOFFSET_MASK 0x00FF
-#define CSUM_PARAM_PLOADOFFSET_SHIFT 16
-#define CSUM_PARAM_XSUMOFFSET_MASK 0x00FF
-#define CSUM_PARAM_XSUMOFFSET_SHIFT 24
-
-/* TPD descriptor */
-struct tso_param {
- /* The order of these declarations is important -- don't change it */
- u32 tsopu; /* tso_param upper word */
- u32 tsopl; /* tso_param lower word */
-};
-
-struct csum_param {
- /* The order of these declarations is important -- don't change it */
- u32 csumpu; /* csum_param upper word */
- u32 csumpl; /* csum_param lower word */
-};
+/*
+ * The L1 transmit packet descriptor is comprised of four 32-bit words.
+ *
+ * 31 0
+ * +---------------------------------------+
+ * | Word 0: Buffer addr lo |
+ * +---------------------------------------+
+ * | Word 1: Buffer addr hi |
+ * +---------------------------------------+
+ * | Word 2 |
+ * +---------------------------------------+
+ * | Word 3 |
+ * +---------------------------------------+
+ *
+ * Words 0 and 1 combine to form a 64-bit buffer address.
+ *
+ * Word 2 is self explanatory in the #define block below.
+ *
+ * Word 3 has two forms, depending upon the state of bits 3 and 4.
+ * If bits 3 and 4 are both zero, then bits 14:31 are unused by the
+ * hardware. Otherwise, if either bit 3 or 4 is set, the definition
+ * of bits 14:31 vary according to the following depiction.
+ *
+ * 0 End of packet 0 End of packet
+ * 1 Coalesce 1 Coalesce
+ * 2 Insert VLAN tag 2 Insert VLAN tag
+ * 3 Custom csum enable = 0 3 Custom csum enable = 1
+ * 4 Segment enable = 1 4 Segment enable = 0
+ * 5 Generate IP checksum 5 Generate IP checksum
+ * 6 Generate TCP checksum 6 Generate TCP checksum
+ * 7 Generate UDP checksum 7 Generate UDP checksum
+ * 8 VLAN tagged 8 VLAN tagged
+ * 9 Ethernet frame type 9 Ethernet frame type
+ * 10-+ 10-+
+ * 11 | IP hdr length (10:13) 11 | IP hdr length (10:13)
+ * 12 | (num 32-bit words) 12 | (num 32-bit words)
+ * 13-+ 13-+
+ * 14-+ 14 Unused
+ * 15 | TCP hdr length (14:17) 15 Unused
+ * 16 | (num 32-bit words) 16-+
+ * 17-+ 17 |
+ * 18 Header TPD flag 18 |
+ * 19-+ 19 | Payload offset
+ * 20 | 20 | (16:23)
+ * 21 | 21 |
+ * 22 | 22 |
+ * 23 | 23-+
+ * 24 | 24-+
+ * 25 | MSS (19:31) 25 |
+ * 26 | 26 |
+ * 27 | 27 | Custom csum offset
+ * 28 | 28 | (24:31)
+ * 29 | 29 |
+ * 30 | 30 |
+ * 31-+ 31-+
+ */
-union tpd_descr {
- u64 data;
- struct csum_param csum;
- struct tso_param tso;
-};
+/* tpd word 2 */
+#define TPD_BUFLEN_MASK 0x3FFF
+#define TPD_BUFLEN_SHIFT 0
+#define TPD_DMAINT_MASK 0x0001
+#define TPD_DMAINT_SHIFT 14
+#define TPD_PKTNT_MASK 0x0001
+#define TPD_PKTINT_SHIFT 15
+#define TPD_VLANTAG_MASK 0xFFFF
+#define TPD_VLAN_SHIFT 16
+
+/* tpd word 3 bits 0:13 */
+#define TPD_EOP_MASK 0x0001
+#define TPD_EOP_SHIFT 0
+#define TPD_COALESCE_MASK 0x0001
+#define TPD_COALESCE_SHIFT 1
+#define TPD_INS_VL_TAG_MASK 0x0001
+#define TPD_INS_VL_TAG_SHIFT 2
+#define TPD_CUST_CSUM_EN_MASK 0x0001
+#define TPD_CUST_CSUM_EN_SHIFT 3
+#define TPD_SEGMENT_EN_MASK 0x0001
+#define TPD_SEGMENT_EN_SHIFT 4
+#define TPD_IP_CSUM_MASK 0x0001
+#define TPD_IP_CSUM_SHIFT 5
+#define TPD_TCP_CSUM_MASK 0x0001
+#define TPD_TCP_CSUM_SHIFT 6
+#define TPD_UDP_CSUM_MASK 0x0001
+#define TPD_UDP_CSUM_SHIFT 7
+#define TPD_VL_TAGGED_MASK 0x0001
+#define TPD_VL_TAGGED_SHIFT 8
+#define TPD_ETHTYPE_MASK 0x0001
+#define TPD_ETHTYPE_SHIFT 9
+#define TPD_IPHL_MASK 0x000F
+#define TPD_IPHL_SHIFT 10
+
+/* tpd word 3 bits 14:31 if segment enabled */
+#define TPD_TCPHDRLEN_MASK 0x000F
+#define TPD_TCPHDRLEN_SHIFT 14
+#define TPD_HDRFLAG_MASK 0x0001
+#define TPD_HDRFLAG_SHIFT 18
+#define TPD_MSS_MASK 0x1FFF
+#define TPD_MSS_SHIFT 19
+
+/* tpd word 3 bits 16:31 if custom csum enabled */
+#define TPD_PLOADOFFSET_MASK 0x00FF
+#define TPD_PLOADOFFSET_SHIFT 16
+#define TPD_CCSUMOFFSET_MASK 0x00FF
+#define TPD_CCSUMOFFSET_SHIFT 24
struct tx_packet_desc {
__le64 buffer_addr;
- union tpd_descr desc;
+ __le32 word2;
+ __le32 word3;
};
/* DMA Order Settings */
--
1.5.3.7
^ permalink raw reply related
* Re: [PATCH 09/14] [usbnet] add driver_priv pointer to 'struct usbnet'
From: David Brownell @ 2008-01-25 0:59 UTC (permalink / raw)
To: Jussi Kivilinna
Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA, bjd-a1rhEgazXTw,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20080120001432.25718.30378.stgit-q/85JClnwdg@public.gmane.org>
On Saturday 19 January 2008, Jussi Kivilinna wrote:
> Add a private data pointer to usbnet for rndis_wext module to use.
>
> Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
Acked-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
> ---
>
> drivers/net/usb/usbnet.h | 1 +
> 1 files changed, 1 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/net/usb/usbnet.h b/drivers/net/usb/usbnet.h
> index 29ab92e..0b4bf09 100644
> --- a/drivers/net/usb/usbnet.h
> +++ b/drivers/net/usb/usbnet.h
> @@ -31,6 +31,7 @@ struct usbnet {
> struct usb_interface *intf;
> struct driver_info *driver_info;
> const char *driver_name;
> + void *driver_priv;
> wait_queue_head_t *wait;
> struct mutex phy_mutex;
> unsigned char suspend_count;
>
^ permalink raw reply
* Re: [PATCH 06/14] [usbnet] Use wlan device name for RNDIS wireless devices
From: David Brownell @ 2008-01-25 0:56 UTC (permalink / raw)
To: Jussi Kivilinna
Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA, bjd-a1rhEgazXTw,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20080120001415.25718.1762.stgit-q/85JClnwdg@public.gmane.org>
On Saturday 19 January 2008, Jussi Kivilinna wrote:
> Use wlan device name for RNDIS wireless devices.
>
> Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
> Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
Acked-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
> ---
>
> drivers/net/usb/usbnet.c | 3 +++
> drivers/net/usb/usbnet.h | 2 ++
> 2 files changed, 5 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
> index 8ed1fc5..a2a2d5e 100644
> --- a/drivers/net/usb/usbnet.c
> +++ b/drivers/net/usb/usbnet.c
> @@ -1204,6 +1204,9 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
> if ((dev->driver_info->flags & FLAG_ETHER) != 0
> && (net->dev_addr [0] & 0x02) == 0)
> strcpy (net->name, "eth%d");
> + /* WLAN devices should always be named "wlan%d" */
> + if ((dev->driver_info->flags & FLAG_WLAN) != 0)
> + strcpy(net->name, "wlan%d");
>
> /* maybe the remote can't receive an Ethernet MTU */
> if (net->mtu > (dev->hard_mtu - net->hard_header_len))
> diff --git a/drivers/net/usb/usbnet.h b/drivers/net/usb/usbnet.h
> index 1fae434..29ab92e 100644
> --- a/drivers/net/usb/usbnet.h
> +++ b/drivers/net/usb/usbnet.h
> @@ -87,6 +87,8 @@ struct driver_info {
> #define FLAG_ETHER 0x0020 /* maybe use "eth%d" names */
>
> #define FLAG_FRAMING_AX 0x0040 /* AX88772/178 packets */
> +#define FLAG_WLAN 0x0080 /* use "wlan%d" names */
> +
>
> /* init device ... can sleep, or cause probe() failure */
> int (*bind)(struct usbnet *, struct usb_interface *);
>
^ permalink raw reply
* Re: [PATCH 04/14] [rndis_host] Halt device if rndis_bind fails.
From: David Brownell @ 2008-01-25 0:55 UTC (permalink / raw)
To: Jussi Kivilinna
Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA, bjd-a1rhEgazXTw,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20080120001404.25718.6901.stgit-q/85JClnwdg@public.gmane.org>
On Saturday 19 January 2008, Jussi Kivilinna wrote:
> When bind fails after device was initialized, shutdown device properly
> by sending RNDIS_MSG_HALT.
>
> Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
> Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
Acked-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
> ---
>
> drivers/net/usb/rndis_host.c | 12 +++++++++---
> 1 files changed, 9 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
> index 42b161c..c686025 100644
> --- a/drivers/net/usb/rndis_host.c
> +++ b/drivers/net/usb/rndis_host.c
> @@ -467,6 +467,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
> struct rndis_query_c *get_c;
> struct rndis_set *set;
> struct rndis_set_c *set_c;
> + struct rndis_halt *halt;
> } u;
> u32 tmp;
> int reply_len;
> @@ -517,7 +518,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
> "dev can't take %u byte packets (max %u)\n",
> dev->hard_mtu, tmp);
> retval = -EINVAL;
> - goto fail_and_release;
> + goto halt_fail_and_release;
> }
> dev->hard_mtu = tmp;
> net->mtu = dev->hard_mtu - net->hard_header_len;
> @@ -539,7 +540,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
> 48, (void **) &bp, &reply_len);
> if (unlikely(retval< 0)) {
> dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
> - goto fail_and_release;
> + goto halt_fail_and_release;
> }
> memcpy(net->dev_addr, bp, ETH_ALEN);
>
> @@ -555,7 +556,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
> retval = rndis_command(dev, u.header);
> if (unlikely(retval < 0)) {
> dev_err(&intf->dev, "rndis set packet filter, %d\n", retval);
> - goto fail_and_release;
> + goto halt_fail_and_release;
> }
>
> retval = 0;
> @@ -563,6 +564,11 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
> kfree(u.buf);
> return retval;
>
> +halt_fail_and_release:
> + memset(u.halt, 0, sizeof *u.halt);
> + u.halt->msg_type = RNDIS_MSG_HALT;
> + u.halt->msg_len = ccpu2(sizeof *u.halt);
> + (void) rndis_command(dev, (void *)u.halt);
> fail_and_release:
> usb_set_intfdata(info->data, NULL);
> usb_driver_release_interface(driver_of(intf), info->data);
>
^ permalink raw reply
* Re: [PATCH 01/14] Fix sparse warning: returning void-valued expression
From: David Brownell @ 2008-01-25 0:52 UTC (permalink / raw)
To: Jussi Kivilinna
Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA, bjd-a1rhEgazXTw,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20080120001348.25718.36756.stgit-q/85JClnwdg@public.gmane.org>
On Saturday 19 January 2008, Jussi Kivilinna wrote:
> From: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
>
> rndis_unbind and usbnet_cdc_unbind don't return anything.
>
> Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
> Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
Acked-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
> ---
>
> drivers/net/usb/rndis_host.c | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
> index 1ebe325..96ef6a9 100644
> --- a/drivers/net/usb/rndis_host.c
> +++ b/drivers/net/usb/rndis_host.c
> @@ -585,7 +585,7 @@ static void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
> kfree(halt);
> }
>
> - return usbnet_cdc_unbind(dev, intf);
> + usbnet_cdc_unbind(dev, intf);
> }
>
> /*
>
^ permalink raw reply
* [ofa-general] Re: [PATCH 2.6.25] RDMA/cxgb3: Fix the T3A workaround checks.
From: Roland Dreier @ 2008-01-24 23:49 UTC (permalink / raw)
To: Steve Wise; +Cc: netdev, linux-kernel, general
In-Reply-To: <20080124223016.772.18293.stgit@dell3.ogc.int>
thanks, applied.
^ permalink raw reply
* [PATCH] SMC91x: Use IRQ save and restore versions of spinlocks
From: Kevin Hilman @ 2008-01-24 23:29 UTC (permalink / raw)
To: netdev; +Cc: Nicolas Pitre, Kevin Hilman
Under certain circumstances (e.g. kgdb over ethernet), the TX code may
be called with interrupts disabled. The spin_unlock_irq() calls in
the driver unconditionally re-enable interrupts, which may trigger the
netdev softirq to run and cause spinlock recursion in the net stack.
For example, here's an example path with interrupts disabled:
kgdb exception
eth_flush_buf
netpoll_send_udp
netpoll_send_skb [ takes netdevice->xmit_lock via netif_tx_trylock() ]
smc_hard_start_xmit
smc_hardware_send_pkt
SMC_ENABLE_INT()
spin_unlock_irq(&lp->lock)
That spin_unlock_irq() will re-enable interrupts, even if they wer
disabled, thus triggering the netdev softirq to run. The
dev_watchdog() will try to take the netdevice->xmit_lock which is
currently locked by netpoll_send_skb() and then BUG!
Also, use spin_trylock_irqsave() instead of a wrapped version of
spin_trylock.
Signed-off-by: Kevin Hilman <khilman@mvista.com>
Acked-by: Nicolas Pitre <nico@cam.org>
---
drivers/net/smc91x.c | 42 ++++++++++++++++++------------------------
1 files changed, 18 insertions(+), 24 deletions(-)
diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c
index 7da7589..475de0f 100644
--- a/drivers/net/smc91x.c
+++ b/drivers/net/smc91x.c
@@ -222,21 +222,21 @@ static void PRINT_PKT(u_char *buf, int length)
/* this enables an interrupt in the interrupt mask register */
#define SMC_ENABLE_INT(x) do { \
unsigned char mask; \
- spin_lock_irq(&lp->lock); \
+ spin_lock_irqsave(&lp->lock, flags); \
mask = SMC_GET_INT_MASK(); \
mask |= (x); \
SMC_SET_INT_MASK(mask); \
- spin_unlock_irq(&lp->lock); \
+ spin_unlock_irqrestore(&lp->lock, flags); \
} while (0)
/* this disables an interrupt from the interrupt mask register */
#define SMC_DISABLE_INT(x) do { \
unsigned char mask; \
- spin_lock_irq(&lp->lock); \
+ spin_lock_irqsave(&lp->lock, flags); \
mask = SMC_GET_INT_MASK(); \
mask &= ~(x); \
SMC_SET_INT_MASK(mask); \
- spin_unlock_irq(&lp->lock); \
+ spin_unlock_irqrestore(&lp->lock, flags); \
} while (0)
/*
@@ -547,21 +547,13 @@ static inline void smc_rcv(struct net_device *dev)
* any other concurrent access and C would always interrupt B. But life
* isn't that easy in a SMP world...
*/
-#define smc_special_trylock(lock) \
-({ \
- int __ret; \
- local_irq_disable(); \
- __ret = spin_trylock(lock); \
- if (!__ret) \
- local_irq_enable(); \
- __ret; \
-})
-#define smc_special_lock(lock) spin_lock_irq(lock)
-#define smc_special_unlock(lock) spin_unlock_irq(lock)
+#define smc_special_trylock(lock, flags) spin_trylock_irqsave(lock, flags)
+#define smc_special_lock(lock, flags) spin_lock_irqsave(lock, flags)
+#define smc_special_unlock(lock, flags) spin_unlock_irqrestore(lock, flags)
#else
-#define smc_special_trylock(lock) (1)
-#define smc_special_lock(lock) do { } while (0)
-#define smc_special_unlock(lock) do { } while (0)
+#define smc_special_trylock(lock,flags) (1)
+#define smc_special_lock(lock,flags) do { } while (0)
+#define smc_special_unlock(lock,flags) do { } while (0)
#endif
/*
@@ -575,10 +567,11 @@ static void smc_hardware_send_pkt(unsigned long data)
struct sk_buff *skb;
unsigned int packet_no, len;
unsigned char *buf;
+ unsigned long flags;
DBG(3, "%s: %s\n", dev->name, __FUNCTION__);
- if (!smc_special_trylock(&lp->lock)) {
+ if (!smc_special_trylock(&lp->lock, flags)) {
netif_stop_queue(dev);
tasklet_schedule(&lp->tx_task);
return;
@@ -586,7 +579,7 @@ static void smc_hardware_send_pkt(unsigned long data)
skb = lp->pending_tx_skb;
if (unlikely(!skb)) {
- smc_special_unlock(&lp->lock);
+ smc_special_unlock(&lp->lock, flags);
return;
}
lp->pending_tx_skb = NULL;
@@ -596,7 +589,7 @@ static void smc_hardware_send_pkt(unsigned long data)
printk("%s: Memory allocation failed.\n", dev->name);
dev->stats.tx_errors++;
dev->stats.tx_fifo_errors++;
- smc_special_unlock(&lp->lock);
+ smc_special_unlock(&lp->lock, flags);
goto done;
}
@@ -635,7 +628,7 @@ static void smc_hardware_send_pkt(unsigned long data)
/* queue the packet for TX */
SMC_SET_MMU_CMD(MC_ENQUEUE);
- smc_special_unlock(&lp->lock);
+ smc_special_unlock(&lp->lock, flags);
dev->trans_start = jiffies;
dev->stats.tx_packets++;
@@ -660,6 +653,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct smc_local *lp = netdev_priv(dev);
void __iomem *ioaddr = lp->base;
unsigned int numPages, poll_count, status;
+ unsigned long flags;
DBG(3, "%s: %s\n", dev->name, __FUNCTION__);
@@ -685,7 +679,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
return 0;
}
- smc_special_lock(&lp->lock);
+ smc_special_lock(&lp->lock, flags);
/* now, try to allocate the memory */
SMC_SET_MMU_CMD(MC_ALLOC | numPages);
@@ -703,7 +697,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
} while (--poll_count);
- smc_special_unlock(&lp->lock);
+ smc_special_unlock(&lp->lock, flags);
lp->pending_tx_skb = skb;
if (!poll_count) {
--
1.5.3.7
^ permalink raw reply related
* [ofa-general] [PATCH 2.6.25] RDMA/cxgb3: Fix the T3A workaround checks.
From: Steve Wise @ 2008-01-24 22:30 UTC (permalink / raw)
To: rdreier; +Cc: netdev, linux-kernel, general
RDMA/cxgb3: Fix the T3A workaround checks.
Correctly work around T3A issues by checking "hwtype != T3A" instead of
"hwtype == T3B". Needed for new hw types.
Signed-off-by: Steve Wise <swise@opengridcomputing.com>
---
drivers/infiniband/hw/cxgb3/cxio_hal.c | 2 +-
drivers/infiniband/hw/cxgb3/iwch_cm.c | 4 ++--
drivers/infiniband/hw/cxgb3/iwch_provider.c | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/infiniband/hw/cxgb3/cxio_hal.c b/drivers/infiniband/hw/cxgb3/cxio_hal.c
index eec6a30..e220b44 100644
--- a/drivers/infiniband/hw/cxgb3/cxio_hal.c
+++ b/drivers/infiniband/hw/cxgb3/cxio_hal.c
@@ -179,7 +179,7 @@ int cxio_create_cq(struct cxio_rdev *rdev_p, struct t3_cq *cq)
setup.size = 1UL << cq->size_log2;
setup.credits = 65535;
setup.credit_thres = 1;
- if (rdev_p->t3cdev_p->type == T3B)
+ if (rdev_p->t3cdev_p->type != T3A)
setup.ovfl_mode = 0;
else
setup.ovfl_mode = 1;
diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c
index 20ba372..f8cb0fe 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_cm.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c
@@ -1118,7 +1118,7 @@ static int act_open_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
status2errno(rpl->status));
connect_reply_upcall(ep, status2errno(rpl->status));
state_set(&ep->com, DEAD);
- if (ep->com.tdev->type == T3B && act_open_has_tid(rpl->status))
+ if (ep->com.tdev->type != T3A && act_open_has_tid(rpl->status))
release_tid(ep->com.tdev, GET_TID(rpl), NULL);
cxgb3_free_atid(ep->com.tdev, ep->atid);
dst_release(ep->dst);
@@ -1249,7 +1249,7 @@ static void reject_cr(struct t3cdev *tdev, u32 hwtid, __be32 peer_ip,
skb_trim(skb, sizeof(struct cpl_tid_release));
skb_get(skb);
- if (tdev->type == T3B)
+ if (tdev->type != T3A)
release_tid(tdev, hwtid, skb);
else {
struct cpl_pass_accept_rpl *rpl;
diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c
index 69b1204..df1838f 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_provider.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c
@@ -646,7 +646,7 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
if (err)
goto err;
- if (udata && t3b_device(rhp)) {
+ if (udata && !t3a_device(rhp)) {
uresp.pbl_addr = (mhp->attr.pbl_addr -
rhp->rdev.rnic_info.pbl_base) >> 3;
PDBG("%s user resp pbl_addr 0x%x\n", __FUNCTION__,
^ permalink raw reply related
* Re: Slow OOM in netif_RX function
From: Francois Romieu @ 2008-01-24 21:51 UTC (permalink / raw)
To: Ivan H. Dichev; +Cc: netdev
In-Reply-To: <20080124211810.3E24A46E9A@smtp.obs.bg>
Ivan H. Dichev <idichev@obs.bg> :
[...]
> Any other ideas appreciated.
Plot the slab values and the counters of the iptables rules against time ?
--
Ueimor
^ permalink raw reply
* [PATCH] fib_trie: rescan if key is lost during dump
From: Stephen Hemminger @ 2008-01-24 21:51 UTC (permalink / raw)
To: David Miller; +Cc: kaber, netdev
In-Reply-To: <20080123.232747.75654058.davem@davemloft.net>
Normally during a dump the key of the last dumped entry is used for
continuation, but since lock is dropped it might be lost. In that case
fallback to the old counter based N^2 behaviour. This means the dump will end up
skipping some routes which matches what FIB_HASH does.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
--- a/include/linux/netlink.h 2008-01-24 13:39:40.000000000 -0800
+++ b/include/linux/netlink.h 2008-01-24 13:42:05.000000000 -0800
@@ -219,7 +219,7 @@ struct netlink_callback
int (*dump)(struct sk_buff * skb, struct netlink_callback *cb);
int (*done)(struct netlink_callback *cb);
int family;
- long args[5];
+ long args[6];
};
struct netlink_notify
--- a/net/ipv4/fib_trie.c 2008-01-24 13:39:40.000000000 -0800
+++ b/net/ipv4/fib_trie.c 2008-01-24 13:44:26.000000000 -0800
@@ -1743,6 +1743,19 @@ static struct leaf *trie_nextleaf(struct
return leaf_walk_rcu(p, c);
}
+static struct leaf *trie_leafindex(struct trie *t, int index)
+{
+ struct leaf *l = trie_firstleaf(t);
+
+ while (index-- > 0) {
+ l = trie_nextleaf(l);
+ if (!l)
+ break;
+ }
+ return l;
+}
+
+
/*
* Caller must hold RTNL.
*/
@@ -1848,7 +1861,7 @@ static int fn_trie_dump_fa(t_key key, in
struct fib_alias *fa;
__be32 xkey = htonl(key);
- s_i = cb->args[4];
+ s_i = cb->args[5];
i = 0;
/* rcu_read_lock is hold by caller */
@@ -1869,12 +1882,12 @@ static int fn_trie_dump_fa(t_key key, in
plen,
fa->fa_tos,
fa->fa_info, NLM_F_MULTI) < 0) {
- cb->args[4] = i;
+ cb->args[5] = i;
return -1;
}
i++;
}
- cb->args[4] = i;
+ cb->args[5] = i;
return skb->len;
}
@@ -1885,7 +1898,7 @@ static int fn_trie_dump_leaf(struct leaf
struct hlist_node *node;
int i, s_i;
- s_i = cb->args[3];
+ s_i = cb->args[4];
i = 0;
/* rcu_read_lock is hold by caller */
@@ -1896,19 +1909,19 @@ static int fn_trie_dump_leaf(struct leaf
}
if (i > s_i)
- cb->args[4] = 0;
+ cb->args[5] = 0;
if (list_empty(&li->falh))
continue;
if (fn_trie_dump_fa(l->key, li->plen, &li->falh, tb, skb, cb) < 0) {
- cb->args[3] = i;
+ cb->args[4] = i;
return -1;
}
i++;
}
- cb->args[3] = i;
+ cb->args[4] = i;
return skb->len;
}
@@ -1918,35 +1931,37 @@ static int fn_trie_dump(struct fib_table
struct leaf *l;
struct trie *t = (struct trie *) tb->tb_data;
t_key key = cb->args[2];
+ int count = cb->args[3];
rcu_read_lock();
/* Dump starting at last key.
* Note: 0.0.0.0/0 (ie default) is first key.
*/
- if (!key)
+ if (count == 0)
l = trie_firstleaf(t);
else {
+ /* Normally, continue from last key, but if that is missing
+ * fallback to using slow rescan
+ */
l = fib_find_node(t, key);
- if (!l) {
- /* The table changed during the dump, rather than
- * giving partial data, just make application retry.
- */
- rcu_read_unlock();
- return -EBUSY;
- }
+ if (!l)
+ l = trie_leafindex(t, count);
}
while (l) {
cb->args[2] = l->key;
if (fn_trie_dump_leaf(l, tb, skb, cb) < 0) {
+ cb->args[3] = count;
rcu_read_unlock();
return -1;
}
+ ++count;
l = trie_nextleaf(l);
- memset(&cb->args[3], 0,
- sizeof(cb->args) - 3*sizeof(cb->args[0]));
+ memset(&cb->args[4], 0,
+ sizeof(cb->args) - 4*sizeof(cb->args[0]));
}
+ cb->args[3] = count;
rcu_read_unlock();
return skb->len;
^ permalink raw reply
* Re: [Bugme-new] [Bug 9808] New: system hung with htb QoS
From: Andrew Morton @ 2008-01-24 21:50 UTC (permalink / raw)
To: Brandeburg, Jesse
Cc: e1000-devel, netdev, bilias, auke-jan.h.kok, bugme-daemon
In-Reply-To: <36D9DB17C6DE9E40B059440DB8D95F520444FDF4@orsmsx418.amr.corp.intel.com>
> On Thu, 24 Jan 2008 12:06:58 -0800 "Brandeburg, Jesse" <jesse.brandeburg@intel.com> wrote:
> Would you be willing to try the 7.6.15 driver at e1000.sourceforge.net,
> it has many more fixes for e1000 than what is available in the in-kernel
> driver. I just posted a patch in the "Tracker/Patches" area that
> patches 7.6.15 to support the e1000_dump code to dump rings when the tx
> hang occurs which will help us figure out a) what software did to the
> ring, b) if something is messed up in the ring which we know will hang
> the hardware
General hand-waving: if you have a debug patch which helps diagnose
rare problems when a tester hits them, please consider just putting
them straight into the mainline tree.
We can always take them out later when everything is resolved, and we
can help ourselves a lot this way.
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
^ permalink raw reply
* Slow OOM in netif_RX function
From: Ivan H. Dichev @ 2008-01-24 21:18 UTC (permalink / raw)
To: netdev
In-Reply-To: <4798E32E.6080003@cosmosbay.com>
Eric Dumazet writes:
> Ivan Dichev a écrit :
>> Hello,
>> I got problem with my linux router. It has slow persistent OOM
>> problems from few months ago.
>> Every working(I mean days when more traffic is generated) day my
>> router is leaking with 15-20 MB memory and
>> after 2 weeks the restart is a MUST.
>>> From /proc/slabinfo I saw that size-2048 and size-512 are growing
>> rapidly every day when traffic occur.
>>
>> --------- /proc/slabinfo --------------------
>> size-2048 20322 20349 2072 3 2 : tunables 24 12 0
>> : slabdata 6780 6783 0
>> size-512 50984 51016 536 7 1 : tunables 32 16 0
>> : slabdata 7288 7288 0
>>
>>
>> I was wondering who is allocating this mem pools and then I changed
>> the kernel with 2.6.23-rc12 including options
>> CONFIG_DEBUG_SLAB=y
>> CONFIG_DEBUG_SLAB_LEAK=y
>>
>>
>> Unfortunately changing the kernel didn't solve the mem leak....
>> Now /proc/slab_allocators is showing that 3c59x driver is allocating
>> 2048 and 512 bytes memory pools
>> caused by RX function.
>> --------- from /proc/slab_allocators ------------------------------
>> 7612 size-2048: boomerang_rx+0x33b/0x437 [3c59x]
>> 16018 size-512: boomerang_rx+0x165/0x437 [3c59x]
>>
>> I was thinking that the 3com driver is bogus, .. but not!
>> After few days I changed the cards with rtl8139 and now ....
>> --------- from /proc/slab_allocators ------------------------------
>> size-2048: 20159 rtl8139_rx+0x155/0x2dc [8139too]
>> size-1024: 2693 rtl8139_rx+0x155/0x2dc [8139too]
>> size-512: 50515 rtl8139_rx+0x155/0x2dc [8139too]
>>
>> the memory leak appear again in the same function(RX).
>>
>> I did search over the mailing list and found as similar only this
>> http://www.spinics.net/lists/kernel/old/2003-q4/msg03071.html
>>
>>
>> For sure it does not depend on kernel version and network
>> driver(except case if both drivers are bogus :)
>> Any ideas ?
>>
>
> Could you post your iptable rules "iptables -t nat -nvL ; iptables -nvL",
> and full "cat /proc/slabinfo" ?
>
>
Sorry but my firewall is 7000+ lines and I cant paste the rules.
And that's why it's very hard to debug the chains :(
I have better idea!
What could happen if I put different Lan card in every slot?
In ex. to-private -> 3com
to-inet -> VIA
to-dmz -> rtl8139
And then to look which RX function is consuming the memory.
(boomerang_rx, rtl8139_rx, ... etc)
With this it will be easier to understand which iptables rules(bound to the
found interface) have to be watched.
(I am not sure that it will work ?)
Any other ideas appreciated.
Ivan Dichev
^ permalink raw reply
* Re: [PATCH UCC TDM 1/3 Updated] Platform changes for UCC TDM driver for MPC8323eRDB. Also includes related QE changes and dts entries.
From: Scott Wood @ 2008-01-24 20:24 UTC (permalink / raw)
To: Timur Tabi
Cc: Poonam_Aggrwal-b10812, michael.barkowski, netdev, kumar.gala,
linux-kernel, rubini, linuxppc-dev, ashish.kalra, rich.cutler,
akpm
In-Reply-To: <4798BDEB.2010501@freescale.com>
On Thu, Jan 24, 2008 at 10:33:47AM -0600, Timur Tabi wrote: Yes, but
> deciding what the UCC does might not be static. At what point do we
> declare, "UCC5 is for eth0 and eth0 only"?
When the board designer decides to hook eth0 up to UCC5.
If the board designer decides to hook multiple devices up to UCC5, we first
smack the board designer, and then set it up to whichever configuration has
been jumpered.
-Scott
^ permalink raw reply
* Re: [PATCH UCC TDM 3/3 ] Modified Documentation to explain dts entries for TDM driver
From: Scott Wood @ 2008-01-24 20:12 UTC (permalink / raw)
To: Poonam_Aggrwal-b10812
Cc: kumar.gala, akpm, linux-kernel, netdev, rubini, linuxppc-dev,
michael.barkowski, rich.cutler, timur, ashish.kalra
In-Reply-To: <Pine.LNX.4.64.0801241022070.27491@linux121>
On Thu, Jan 24, 2008 at 10:24:13AM +0530, Poonam_Aggrwal-b10812 wrote:
> + ix) Baud Rate Generator (BRG)
> +
> + Required properties:
> + - compatible : shpuld be "fsl,cpm-brg"
> + - fsl,brg-sources : define the input clock for all 16 BRGs. The input
> + clock source could be 1 to 24 for CLK1 to CLK24. Zero means that the
> + particular BRG will be driven by QE clock(BRGCLK).
Should also have a clock-frequency property to specify what BRGCLK is.
-Scott
^ permalink raw reply
* Re: [Bugme-new] [Bug 9808] New: system hung with htb QoS
From: Brandeburg, Jesse @ 2008-01-24 20:06 UTC (permalink / raw)
To: Andrew Morton, netdev; +Cc: e1000-devel, bilias, Kok, Auke-jan H, bugme-daemon
In-Reply-To: <20080124061157.3512a308.akpm@linux-foundation.org>
Andrew Morton wrote:
>> I'm also receiving this quite often:
>> Jan 15 12:23:17 ftp kernel: e1000: eth0: e1000_clean_tx_irq:
>> Detected Tx Unit Hang Jan 15 12:23:17 ftp kernel: Tx Queue
>> <0>
>> Jan 15 12:23:17 ftp kernel: TDH <2a>
>> Jan 15 12:23:17 ftp kernel: TDT <17>
>> Jan 15 12:23:17 ftp kernel: next_to_use <17>
>> Jan 15 12:23:17 ftp kernel: next_to_clean <2a>
>> Jan 15 12:23:17 ftp kernel: buffer_info[next_to_clean]
>> Jan 15 12:23:17 ftp kernel: time_stamp <5798144>
>> Jan 15 12:23:17 ftp kernel: next_to_watch <2d>
>> Jan 15 12:23:17 ftp kernel: jiffies <57988ef>
>> Jan 15 12:23:17 ftp kernel: next_to_watch.status <0>
>> Jan 15 12:23:19 ftp kernel: e1000: eth0: e1000_clean_tx_irq:
>> Detected Tx Unit Hang
Looks like a real hardware hang.
Would you be willing to try the 7.6.15 driver at e1000.sourceforge.net,
it has many more fixes for e1000 than what is available in the in-kernel
driver. I just posted a patch in the "Tracker/Patches" area that
patches 7.6.15 to support the e1000_dump code to dump rings when the tx
hang occurs which will help us figure out a) what software did to the
ring, b) if something is messed up in the ring which we know will hang
the hardware
>> Today for the first time (after applying options to e1000 driver in
>> modprobe.conf) I got a kernel panic:
>>
>> BUG: unable to handle kernel paging request at virtual address
>> a0379120
>> EIP: 0060: [<c05db2dc>] Not Tainted VLI
>> EIP is at ip_rcv+0x286/0x4ba
>> Kernel panic - not syncing: Fatal exception in interrupt
>>
>> This is what I wrote on paper cause there wasn't logged anywhere.
>> Usually it hungs without a kernel panic.
Everyone involved will need more information about the panic to make
progress on the panic.
>>
>> System in Fedoca Core 8 up2date
>> 2.6.23.9-85.fc8PAE
>> 2x Intel(R) Xeon(TM) CPU 3.20GHz
>> 4G RAM
>>
>> Without the QoS loaded system never hungs. It must be related to
>> this. However the e1000 error I'm receiving must have to do with the
>> e1000 driver. I've seen this bug in the past that's why I tried to
>> apply the options in modprobe.conf
>> modprobe.conf options for e1000:
>> options e1000 XsumRX=0 Speed=1000 Duplex=2 InterruptThrottleRate=0
>> FlowControl=3 RxDescriptors=4096 TxDescriptors=4096 RxIntDelay=0
>> TxIntDelay=0
Please don't use any of these options unless you must, they seem to have
come from some debian forum that someone just posted a SWAG at changing
parameters that fixed him for some unknown reason.
Get back to us with the debug output, the e1000 issue can be covered on
e1000-devel@lists.sourceforge.net
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
^ permalink raw reply
* pull request: wireless-2.6 'upstream' 2008-01-24
From: John W. Linville @ 2008-01-24 19:53 UTC (permalink / raw)
To: davem-fT/PcQaiUtIeIZ0/mPfg9Q
Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
linux-wireless-u79uwXL29TY76Z2rM5mHXA
[-- Attachment #1: Type: text/plain, Size: 9062 bytes --]
Dave,
Here is one more big round of patches intended for 2.6.25. This time
features some updates for b43, b43legacy and iwlwifi as well as
some other misc fixes. There is a fixup of a minor rebasing error
in rt61pci, and there is a new driver for wireless RNDIS devices.
Finally, there is a big cfg80211 API change related to channels and
bitrates that we would like to see in the 2.6.25 cycle.
The cfg80211 API change breaks ath5k, so I have listed it as "depends
on BROKEN". I am assured that the ath5k team has agreed to fix
this ASAP. Meanwhile we wanted to have it in place so that we can
start shaking-out problems with other drivers.
Please let me know if there are problems!
Thanks,
John
---
Individual patches are available here:
http://www.kernel.org/pub/linux/kernel/people/linville/wireless-2.6/upstream
---
The following changes since commit f1d5fe9e51491cf3eef1276e790b375682ec078c:
Stephen Hemminger (1):
[PPP]: Sparse warning fixes.
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6.git upstream
Adrian Bassett (1):
rtl8180_dev.c: add support for 1799:700f
Bjorge Dijkstra (2):
Fix sparse warning: returning void-valued expression
Hardwire CDC descriptors when missing
Bruno Randolf (1):
ath5k: debug level improvements
Gregory Greenman (1):
iwlwifi: Fix uCode error on association
Iñaky Pérez-González (1):
rfkill: add the WiMAX radio type
Johannes Berg (3):
mac80211: fix alignment warning
cfg80211 API for channels/bitrates, mac80211 and driver conversion
nl80211: export hardware bitrate/channel capabilities
John W. Linville (1):
rt61pci: fix-up merge damage
Joonwoo Park (1):
iwlwifi: do not schedule tasklet when rcv unused irq
Jussi Kivilinna (12):
Use 1KB buffer in rndis_unbind
Halt device if rndis_bind fails.
Fix rndis packet filter flags.
Use wlan device name for RNDIS wireless devices
Split up rndis_host.c
export functions
add driver_priv pointer to 'struct usbnet'
Add rndis_early_init function pointer to 'struct rndis_data'.
Add rndis_link_change function pointer to 'struct rndis_data'.
Move usbnet.h and rndis_host.h to include/linux/usb
blacklist known wireless RNDIS devices
Add new driver 'rndis_wext' for wireless RNDIS devices.
Maarten Lankhorst (1):
iwlwifi: Fix an invalid bitmask test in iwl3945 and iwl4965
Michael Buesch (4):
b43: Fix rfkill allocation leakage in error paths
b43legacy: Fix rfkill allocation leakage in error paths
b43: Fix suspend/resume
b43: Drop packets that we are not able to encrypt
Reinette Chatre (3):
iwl4965: fix return code indicating one interface is supported
iwlwifi: initialize geo/channel information during probe
iwlwifi: cleanup usage of inline functions
Ron Rindjunsky (1):
mac80211: fixing null qos data frames check for reordering buffer
Stefano Brivio (1):
b43legacy: fix MAC control and microcode init
MAINTAINERS | 6 +
drivers/net/usb/asix.c | 3 +-
drivers/net/usb/cdc_ether.c | 13 +-
drivers/net/usb/cdc_subset.c | 3 +-
drivers/net/usb/dm9601.c | 3 +-
drivers/net/usb/gl620a.c | 3 +-
drivers/net/usb/mcs7830.c | 3 +-
drivers/net/usb/net1080.c | 3 +-
drivers/net/usb/plusb.c | 3 +-
drivers/net/usb/rndis_host.c | 377 ++---
drivers/net/usb/usbnet.c | 6 +-
drivers/net/usb/zaurus.c | 3 +-
drivers/net/wireless/Kconfig | 37 +-
drivers/net/wireless/Makefile | 2 +
drivers/net/wireless/adm8211.c | 80 +-
drivers/net/wireless/adm8211.h | 65 +-
drivers/net/wireless/ath5k/base.c | 10 +-
drivers/net/wireless/ath5k/debug.c | 124 +-
drivers/net/wireless/ath5k/debug.h | 18 +-
drivers/net/wireless/b43/b43.h | 5 -
drivers/net/wireless/b43/dma.c | 30 +-
drivers/net/wireless/b43/main.c | 138 +-
drivers/net/wireless/b43/sysfs.c | 89 +-
drivers/net/wireless/b43/xmit.c | 104 +-
drivers/net/wireless/b43/xmit.h | 10 +-
drivers/net/wireless/b43legacy/b43legacy.h | 35 +-
drivers/net/wireless/b43legacy/main.c | 292 ++--
drivers/net/wireless/b43legacy/phy.c | 14 +-
drivers/net/wireless/b43legacy/pio.c | 6 +-
drivers/net/wireless/b43legacy/radio.c | 16 +-
drivers/net/wireless/b43legacy/xmit.c | 64 +-
drivers/net/wireless/iwlwifi/iwl-3945-hw.h | 2 +-
drivers/net/wireless/iwlwifi/iwl-3945-rs.c | 88 +-
drivers/net/wireless/iwlwifi/iwl-3945.c | 49 +-
drivers/net/wireless/iwlwifi/iwl-3945.h | 15 +-
drivers/net/wireless/iwlwifi/iwl-4965-hw.h | 2 +-
drivers/net/wireless/iwlwifi/iwl-4965-rs.c | 82 +-
drivers/net/wireless/iwlwifi/iwl-4965.c | 50 +-
drivers/net/wireless/iwlwifi/iwl-4965.h | 17 +-
drivers/net/wireless/iwlwifi/iwl3945-base.c | 525 ++----
drivers/net/wireless/iwlwifi/iwl4965-base.c | 516 ++----
drivers/net/wireless/p54.h | 4 -
drivers/net/wireless/p54common.c | 74 +-
drivers/net/wireless/p54common.h | 75 -
drivers/net/wireless/rndis_wext.c | 2570 ++++++++++++++++++++++++++
drivers/net/wireless/rt2x00/rt2x00.h | 11 +-
drivers/net/wireless/rt2x00/rt2x00config.c | 33 +-
drivers/net/wireless/rt2x00/rt2x00dev.c | 149 +-
drivers/net/wireless/rt2x00/rt61pci.c | 26 +-
drivers/net/wireless/rt2x00/rt73usb.c | 23 +-
drivers/net/wireless/rtl8180.h | 2 +-
drivers/net/wireless/rtl8180_dev.c | 94 +-
drivers/net/wireless/rtl8180_grf5101.c | 5 +-
drivers/net/wireless/rtl8180_max2820.c | 5 +-
drivers/net/wireless/rtl8180_rtl8225.c | 15 +-
drivers/net/wireless/rtl8180_sa2400.c | 5 +-
drivers/net/wireless/rtl8187.h | 2 +-
drivers/net/wireless/rtl8187_dev.c | 82 +-
drivers/net/wireless/rtl8187_rtl8225.c | 15 +-
drivers/net/wireless/rtl818x.h | 70 -
drivers/net/wireless/zd1211rw/zd_chip.c | 15 +-
drivers/net/wireless/zd1211rw/zd_ieee80211.c | 11 +-
drivers/net/wireless/zd1211rw/zd_mac.c | 170 +-
drivers/net/wireless/zd1211rw/zd_mac.h | 2 +-
include/linux/input.h | 2 +
include/linux/nl80211.h | 64 +
include/linux/rfkill.h | 2 +
include/linux/usb/rndis_host.h | 257 +++
{drivers/net => include/linux}/usb/usbnet.h | 9 +
include/net/mac80211.h | 197 +--
include/net/wireless.h | 168 ++
net/mac80211/Makefile | 1 -
net/mac80211/cfg.c | 11 +-
net/mac80211/debugfs.c | 47 +-
net/mac80211/debugfs_sta.c | 18 -
net/mac80211/ieee80211.c | 112 +-
net/mac80211/ieee80211_i.h | 63 +-
net/mac80211/ieee80211_iface.c | 2 +
net/mac80211/ieee80211_ioctl.c | 127 +-
net/mac80211/ieee80211_rate.c | 15 +-
net/mac80211/ieee80211_rate.h | 28 +-
net/mac80211/ieee80211_sta.c | 384 +++--
net/mac80211/rc80211_pid_algo.c | 76 +-
net/mac80211/rc80211_simple.c | 66 +-
net/mac80211/regdomain.c | 152 --
net/mac80211/rx.c | 130 +-
net/mac80211/sta_info.c | 24 -
net/mac80211/sta_info.h | 10 +-
net/mac80211/tx.c | 164 +-
net/mac80211/util.c | 142 +--
net/rfkill/rfkill-input.c | 9 +
net/rfkill/rfkill.c | 3 +
net/wireless/Makefile | 2 +-
net/wireless/core.c | 41 +
net/wireless/core.h | 3 +
net/wireless/nl80211.c | 74 +
net/wireless/reg.c | 153 ++
net/wireless/util.c | 98 +
98 files changed, 5776 insertions(+), 3247 deletions(-)
create mode 100644 drivers/net/wireless/rndis_wext.c
create mode 100644 include/linux/usb/rndis_host.h
rename {drivers/net => include/linux}/usb/usbnet.h (95%)
delete mode 100644 net/mac80211/regdomain.c
create mode 100644 net/wireless/reg.c
create mode 100644 net/wireless/util.c
Omnibus patch attached as 'upstream.patch.bz2'
--
John W. Linville
linville-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org
[-- Attachment #2: upstream.patch.bz2 --]
[-- Type: application/x-bzip2, Size: 92671 bytes --]
^ permalink raw reply
* Re: [XFRM]: constify 'struct xfrm_type'
From: Joe Perches @ 2008-01-24 19:37 UTC (permalink / raw)
To: Eric Dumazet; +Cc: David Miller, netdev@vger.kernel.org
In-Reply-To: <4798E5E1.9050408@cosmosbay.com>
On Thu, 2008-01-24 at 20:24 +0100, Eric Dumazet wrote:
> I still dont understand what *you* want to do.
Mark the accesses as "* const" not "const *"
^ permalink raw reply
* Re: [XFRM]: constify 'struct xfrm_type'
From: Eric Dumazet @ 2008-01-24 19:24 UTC (permalink / raw)
To: Joe Perches; +Cc: David Miller, netdev@vger.kernel.org
In-Reply-To: <1201199858.16268.46.camel@localhost>
Joe Perches a écrit :
> On Thu, 2008-01-24 at 19:23 +0100, Eric Dumazet wrote:
>> Having const data is nice because moving them from .data to .rodata,
>> but what would be practical gains to use a const pointer ???
>
> const data is good, using pointers to const data is good.
Yes, this is what is done.
> using const pointers to const data is good.
const pointers are seldom used in linux kernel, because mostly useless.
> using const pointers to data not specified as const is not so good.
You misread the patch. I am not using const pointers at all, but const data.
>
> Here's what you are doing now.
>
> +static void xfrm_put_type(const struct xfrm_type *type)
> [...]
> + const struct xfrm_type *type;
>
Yes, this was the plan, I meant it.
I still dont understand what *you* want to do.
Doing :
int xfrm_unregister_type(const struct xfrm_type * const type, const unsigned
short family)
instead of :
int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family)
buys nothing for the caller.
^ permalink raw reply
* Re: Slow OOM in netif_RX function
From: Eric Dumazet @ 2008-01-24 19:12 UTC (permalink / raw)
To: Ivan Dichev; +Cc: netdev
In-Reply-To: <4798CAA9.1080005@obs.bg>
Ivan Dichev a écrit :
> Hello,
> I got problem with my linux router. It has slow persistent OOM
> problems from few months ago.
> Every working(I mean days when more traffic is generated) day my
> router is leaking with 15-20 MB memory and
> after 2 weeks the restart is a MUST.
>>From /proc/slabinfo I saw that size-2048 and size-512 are growing
> rapidly every day when traffic occur.
>
> --------- /proc/slabinfo --------------------
> size-2048 20322 20349 2072 3 2 : tunables 24
> 12 0 : slabdata 6780 6783 0
> size-512 50984 51016 536 7 1 : tunables 32
> 16 0 : slabdata 7288 7288 0
>
>
> I was wondering who is allocating this mem pools and then I changed
> the kernel with 2.6.23-rc12 including options
> CONFIG_DEBUG_SLAB=y
> CONFIG_DEBUG_SLAB_LEAK=y
>
>
> Unfortunately changing the kernel didn't solve the mem leak....
> Now /proc/slab_allocators is showing that 3c59x driver is allocating
> 2048 and 512 bytes memory pools
> caused by RX function.
> --------- from /proc/slab_allocators ------------------------------
> 7612 size-2048: boomerang_rx+0x33b/0x437 [3c59x]
> 16018 size-512: boomerang_rx+0x165/0x437 [3c59x]
>
> I was thinking that the 3com driver is bogus, .. but not!
> After few days I changed the cards with rtl8139 and now ....
> --------- from /proc/slab_allocators ------------------------------
> size-2048: 20159 rtl8139_rx+0x155/0x2dc [8139too]
> size-1024: 2693 rtl8139_rx+0x155/0x2dc [8139too]
> size-512: 50515 rtl8139_rx+0x155/0x2dc [8139too]
>
> the memory leak appear again in the same function(RX).
>
> I did search over the mailing list and found as similar only this
> http://www.spinics.net/lists/kernel/old/2003-q4/msg03071.html
>
>
> For sure it does not depend on kernel version and network
> driver(except case if both drivers are bogus :)
> Any ideas ?
>
Could you post your iptable rules "iptables -t nat -nvL ; iptables -nvL", and
full "cat /proc/slabinfo" ?
^ permalink raw reply
* Re: [XFRM]: constify 'struct xfrm_type'
From: Joe Perches @ 2008-01-24 18:37 UTC (permalink / raw)
To: Eric Dumazet; +Cc: David Miller, netdev@vger.kernel.org
In-Reply-To: <4798D7B9.5090406@cosmosbay.com>
On Thu, 2008-01-24 at 19:23 +0100, Eric Dumazet wrote:
> Having const data is nice because moving them from .data to .rodata,
> but what would be practical gains to use a const pointer ???
const data is good, using pointers to const data is good.
using const pointers to const data is good.
using const pointers to data not specified as const is not so good.
Here's what you are doing now.
+static void xfrm_put_type(const struct xfrm_type *type)
[...]
+ const struct xfrm_type *type;
cheers, Joe
^ permalink raw reply
* Re: Slow OOM in netif_RX function
From: Stephen Hemminger @ 2008-01-24 18:29 UTC (permalink / raw)
To: Ivan Dichev; +Cc: netdev
In-Reply-To: <4798CAA9.1080005@obs.bg>
On Thu, 24 Jan 2008 19:28:09 +0200
Ivan Dichev <idichev@obs.bg> wrote:
> Hello,
> I got problem with my linux router. It has slow persistent OOM
> problems from few months ago.
> Every working(I mean days when more traffic is generated) day my
> router is leaking with 15-20 MB memory and
> after 2 weeks the restart is a MUST.
> From /proc/slabinfo I saw that size-2048 and size-512 are growing
> rapidly every day when traffic occur.
>
> --------- /proc/slabinfo --------------------
> size-2048 20322 20349 2072 3 2 : tunables 24
> 12 0 : slabdata 6780 6783 0
> size-512 50984 51016 536 7 1 : tunables 32
> 16 0 : slabdata 7288 7288 0
>
>
> I was wondering who is allocating this mem pools and then I changed
> the kernel with 2.6.23-rc12 including options
> CONFIG_DEBUG_SLAB=y
> CONFIG_DEBUG_SLAB_LEAK=y
>
>
> Unfortunately changing the kernel didn't solve the mem leak....
> Now /proc/slab_allocators is showing that 3c59x driver is allocating
> 2048 and 512 bytes memory pools
> caused by RX function.
> --------- from /proc/slab_allocators ------------------------------
> 7612 size-2048: boomerang_rx+0x33b/0x437 [3c59x]
> 16018 size-512: boomerang_rx+0x165/0x437 [3c59x]
>
> I was thinking that the 3com driver is bogus, .. but not!
> After few days I changed the cards with rtl8139 and now ....
> --------- from /proc/slab_allocators ------------------------------
> size-2048: 20159 rtl8139_rx+0x155/0x2dc [8139too]
> size-1024: 2693 rtl8139_rx+0x155/0x2dc [8139too]
> size-512: 50515 rtl8139_rx+0x155/0x2dc [8139too]
>
> the memory leak appear again in the same function(RX).
>
> I did search over the mailing list and found as similar only this
> http://www.spinics.net/lists/kernel/old/2003-q4/msg03071.html
>
>
> For sure it does not depend on kernel version and network
> driver(except case if both drivers are bogus :)
> Any ideas ?
>
> Ivan
>
Receive packets are allocated by the driver, and then consumed by
the protocols or sockets. The problem is in the consumer side, so you need
to go looking to see if lots of data is getting queued to some application
that is never reading. Alternatively, it could be some form of control packet
that is not properly processed by a protocol.
Also look at firewall and classification rules, could be a buggy iptables rule?
--
Stephen Hemminger <stephen.hemminger@vyatta.com>
^ permalink raw reply
* Re: [XFRM]: constify 'struct xfrm_type'
From: Eric Dumazet @ 2008-01-24 18:23 UTC (permalink / raw)
To: Joe Perches; +Cc: David Miller, netdev@vger.kernel.org
In-Reply-To: <1201195057.16268.32.camel@localhost>
Joe Perches a écrit :
> On Thu, 2008-01-24 at 12:26 +0100, Eric Dumazet wrote:
>> - struct xfrm_type *type;
>> + const struct xfrm_type *type;
>
> Perhaps const foo * const bar; for most or all of these
> conversions?
>
Hum...
Having const data is nice because moving them from .data to .rodata,
but what would be practical gains to use a const pointer ???
^ permalink raw reply
* [PATCH] TI DaVinci ethernet support for DM6446
From: Anton Salnikov @ 2008-01-24 18:28 UTC (permalink / raw)
To: netdev; +Cc: jgarzik
This is support for DaVinci DM6446 ethernet onchip controller for kernel
version 2.6.24-rc8
Signed-off-by: Anton Salnikov <asalnikov@ru.mvista.com>
---
drivers/net/arm/Kconfig | 9
drivers/net/arm/Makefile | 4
drivers/net/arm/davinci_emac.c | 4766 +++++++++++++++++++++++++++++++++++
drivers/net/arm/davinci_emac.h | 1560 +++++++++++
drivers/net/arm/davinci_emac_debug.c | 658 ++++
drivers/net/arm/davinci_emac_phy.c | 723 +++++
drivers/net/arm/davinci_emac_phy.h | 106
7 files changed, 7826 insertions(+)
Index: 2.6.24-rc8.ether/drivers/net/arm/Kconfig
===================================================================
--- 2.6.24-rc8.ether.orig/drivers/net/arm/Kconfig
+++ 2.6.24-rc8.ether/drivers/net/arm/Kconfig
@@ -2,6 +2,7 @@
# Acorn Network device configuration
# These are for Acorn's Expansion card network interfaces
#
+
config ARM_AM79C961A
bool "ARM EBSA110 AM79C961A support"
depends on ARM && ARCH_EBSA110
@@ -47,3 +48,11 @@ config EP93XX_ETH
help
This is a driver for the ethernet hardware included in EP93xx CPUs.
Say Y if you are building a kernel for EP93xx based devices.
+
+config TI_DAVINCI_EMAC
+ tristate "TI DaVinci EMAC Support"
+ depends on NETDEVICES && MACH_DAVINCI_EVM
+ help
+ This driver supports TI's DaVinci Ethernet .
+ To compile this driver as a module, choose M here: the module
+ will be called ti_davinci_emac. This is recommended.
Index: 2.6.24-rc8.ether/drivers/net/arm/Makefile
===================================================================
--- 2.6.24-rc8.ether.orig/drivers/net/arm/Makefile
+++ 2.6.24-rc8.ether/drivers/net/arm/Makefile
@@ -9,3 +9,7 @@ obj-$(CONFIG_ARM_ETHER3) += ether3.o
obj-$(CONFIG_ARM_ETHER1) += ether1.o
obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
+
+obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac_driver.o
+davinci_emac_driver-objs := davinci_emac.o davinci_emac_phy.o
davinci_emac_debug.o
+
Index: 2.6.24-rc8.ether/drivers/net/arm/davinci_emac.c
===================================================================
--- /dev/null
+++ 2.6.24-rc8.ether/drivers/net/arm/davinci_emac.c
@@ -0,0 +1,4766 @@
+/*
+ * linux/drivers/net/davinci_emac.c
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) 2008 MontaVista Software, Inc. <source@mvista.com>
+ *
+ * This file is licensed under the terms of the
+ * GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * 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.
+ *----------------------------------------------------------------------------
+ * Modifications:
+ * ver. 0.0 Suraj Iyer - Original Linux drive
+ * 0.1 Anant Gole - Recoded as per TI PSPF architecture (converted to DDA)
+ * 2.0 Suraj Iyer, Sharath Kumar, Ajay Singh - Completed for TI BCG
+ * 3.0 Anant Gole - Modified and ported for DaVinci
+ * 4.0 Kevin Hilman, Anant Gole - Linuxification of the driver
+ * 4.? Paul Bartholomew - Use PHY_DUPLEX_* constants instead
+ * of hard-coded numbers. Also, fixed
+ * EMAC_IOCTL_READ_PHY_REG in emac_control() -
+ * the phy_num and reg_addr args were swapped
+ * in call to emac_mdio_read().
+ */
+
+/*
+ * Driver Features:
+ *
+ * The following flags should be defined by the make file for support
+ * of the features:
+ *
+ * (1) EMAC_CACHE_WRITEBACK_MODE to support write back cache mode.
+ *
+ * (2) EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY - to support of multiple
+ * Tx complete notifications. If this is defined the Tx complete
+ * DDA callback function contains multiple packet Tx complete
+ * events. Note: BY DEFAULT THIS DRIVER HANDLES MULTIPLE TX
+ * COMPLETE VIA ITS CALLBACK IN THE SAME FUNCTION FOR SINGLE
+ * PACKET COMPLETE NOTIFY.
+ *
+ *
+ * (3) CONFIG_EMAC_INIT_BUF_MALLOC - Not required for DaVinci driver
+ * - feature was added for another TI platform
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/highmem.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/version.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <asm/arch/hardware.h>
+
+#include "davinci_emac.h"
+
+/*
+ * linux module options
+ */
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Davinci EMAC Maintainer: Anant Gole <anantgole@ti.com>");
+MODULE_DESCRIPTION("DaVinci Ethernet driver - EMAC (EMAC)");
+MODULE_VERSION(EMAC_MODULE_VERSION);
+
+static int cfg_link_speed ;
+module_param(cfg_link_speed, int, 0);
+MODULE_PARM_DESC(cfg_link_speed, "Fixed speed of the Link: <100/10>");
+
+static char *cfg_link_mode = "auto";
+module_param(cfg_link_mode, charp, 0);
+MODULE_PARM_DESC(cfg_link_mode, "Fixed mode of the Link: <fd/hd>");
+
+static int debug_mode;
+module_param(debug_mode, int, 0);
+MODULE_PARM_DESC(debug_mode,
+ "Turn on the debug info: <0/1>. Default is 0 (off)");
+
+const char emac_version_string[] = "TI DaVinci EMAC Linux version updated 4.0";
+
+/*
+ * L3 Alignment mechanism: The below given macro returns the number of
+ * bytes required to align the given size to a L3 frame 4 byte
+ * boundry. This is typically required to add 2 bytes to the ethernet
+ * frame start to make sure the IP header (L3) is aligned on a 4 byte
+ * boundry
+ */
+static char emac_L3_align[] = { 0x02, 0x01, 0x00, 0x03 };
+
+/* 4 Byte alignment for skb's:
+ *
+ * Currently Skb's dont need to be on a 4 byte boundry, but other OS's
+ * have such requirements Just to make sure we comply if there is any
+ * such requirement on SKB's in future, we align it on a 4 byte
+ * boundry.
+ */
+static char emac_4byte_align[] = { 0x0, 0x03, 0x02, 0x1 };
+
+/* debug tracing */
+static int emac_link_status = 1;
+static int emac_debug_mode;
+
+/* global variables required during initialization */
+static int g_link_speed; /* 0=auto negotiate, 100=100mbps, 10=10mbps */
+static int g_link_mode; /* 0=Auto Negotiate, Full Duplex = 3;
+ *Half Duplex = 2 Unknown = 1 */
+static int g_init_enable_flag;
+
+/* global device array */
+static struct net_device *emac_net_dev[EMAC_MAX_INSTANCES];
+struct net_device *last_emac_device;
+int emac_devices_installed; /* number of EMAC instances */
+static struct proc_dir_entry *gp_stats_file; /* proc entries */
+static char emac_cfg[EMAC_MAX_INSTANCES][200];
+
+/* clock frequency for EMAC */
+static struct clk *emac_clk;
+static unsigned long emac_bus_frequency;
+
+/* MAC ethernet address string in 00:00:00:00:00:00 format */
+static unsigned char emac_eth_string[20] = "deadbeaf";
+
+static const char emac_ddcversion_string[] = "EMAC DDC version 0.5";
+static u32 emac_debug; /* no debug flags by default */
+static u32 emac_wrapper_ptr = EMAC_WRAPPER_RAM_ADDR;
+
+static int emac_dev_tx(struct sk_buff *skb, struct net_device *netdev);
+static irqreturn_t emac_hal_isr(int irq, void *dev_id);
+static void *emac_net_alloc_rx_buf(struct emac_dev_s *dev, int buf_size,
+ void **data_token,
+ u32 channel, void *alloc_args);
+static int emac_net_free_rx_buf(struct emac_dev_s *dev, void *buffer,
+ void *data_token,
+ u32 channel, void *free_args);
+static int emac_net_tx_complete(struct emac_dev_s *dev,
+ void **net_data_tokens,
+ int num_tokens, u32 channel);
+static int emac_net_rx_cb(struct emac_dev_s *dev,
+ struct net_pkt_obj *net_pkt_list,
+ void *rx_args);
+static int emac_poll(struct napi_struct *napi, int budget);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void emac_poll_controller(struct net_device *dev);
+#endif
+
+/* net device related private function prototypes */
+static int emac_dev_init(struct net_device *netdev);
+static int emac_dev_open(struct net_device *dev);
+static int emac_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd);
+static int emac_dev_close(struct net_device *netdev);
+static void emac_dev_mcast_set(struct net_device *netdev);
+static void emac_tx_timeout(struct net_device *netdev);
+static struct net_device_stats *emac_dev_get_net_stats(struct net_device
+ *dev);
+
+/* internal function prototypes */
+static int __init emac_p_detect_manual_cfg(int, char *, int);
+int emac_p_get_version(char *buf, char **start, off_t offset,
+ int count, int *eof, void *data);
+int emac_p_update_statistics(struct net_device *netdev);
+int emac_p_reset_statistics(struct net_device *netdev);
+static int emac_p_dev_enable(struct emac_dev_s *dev);
+static int emac_p_dev_disable(struct emac_dev_s *dev);
+static void emac_p_tick_timer_expiry(struct emac_dev_s *dev);
+static int emac_dev_set_mac_addr(struct net_device *netdev, void *addr);
+
+/* function prototype for emac_p_tick_timer_expiry() function as per
+ * linux timer API */
+typedef void (*timer_tick_func) (unsigned long);
+
+/* DDA function table */
+static int emac_control_cb(struct emac_dev_s *dev, int cmd,
+ void *cmd_arg, void *param);
+
+/* function prototypes */
+static int emac_send(struct emac_dev_s *dev, struct net_pkt_obj *pkt,
+ int channel, bool send_args);
+
+static int emac_tick(struct emac_dev_s *dev, void *tick_args);
+static int emac_pkt_process(struct emac_dev_s *dev, int *pkts_pending,
+ void *pkt_args);
+static int emac_pkt_process_end(struct emac_dev_s *dev, void *proc_args);
+static int emac_tx_bdproc(struct emac_dev_s *dev, u32 channel, u32 *more_pkts,
+ bool *is_eoq);
+static int emac_rx_bdproc(struct emac_dev_s *dev, u32 channel,
+ int *more_pkts);
+
+#ifdef EMAC_MULTIFRAGMENT
+static void emac_add_bdto_rx_queue(struct emac_dev_s *dev,
+ struct emac_rx_cppi_ch_t *rx_cppi,
+ struct emac_rx_bd *sop_bd,
+ struct emac_rx_bd *eop_bd,
+ u32 *buffer,
+ void **buf_token, u32 num_bd);
+#else
+static void emac_add_bdto_rx_queue(struct emac_dev_s *dev,
+ struct emac_rx_cppi_ch_t *rx_cppi,
+ struct emac_rx_bd *curr_bd, char *buffer,
+ void *buf_token);
+#endif
+
+static int emac_update_phy_status(struct emac_dev_s *dev);
+static int emac_init(struct emac_dev_s *dev, struct emac_init_config
*init_cfg);
+static int emac_de_init(struct emac_dev_s *dev, void *param);
+static int emac_open(struct emac_dev_s *dev, void *param);
+static int emac_close(struct emac_dev_s *dev, void *param);
+int emac_control(struct emac_dev_s *dev, int cmd, void *cmd_arg,
+ void *param);
+static int emac_ch_open(struct emac_dev_s *dev, struct emac_ch_info *ch_info,
+ void *ch_open_args);
+static int emac_ch_close(struct emac_dev_s *dev, int channel,
+ int direction, void *ch_close_args);
+static int emac_wait_for_teardown_complete(struct emac_dev_s *dev,
+ u32 channel,
+ enum net_ch_dir direction,
+ bool blocking);
+static int emac_enable_channel(struct emac_dev_s *dev, u32 channel,
+ u32 direction);
+static int emac_disable_channel(struct emac_dev_s *dev, u32 channel,
+ enum net_ch_dir direction);
+static int emac_init_tx_channel(struct emac_dev_s *dev,
+ struct emac_ch_info *ch_info,
+ void *ch_open_args);
+static int emac_init_rx_channel(struct emac_dev_s *dev,
+ struct emac_ch_info *ch_info,
+ void *ch_open_args);
+static int emac_un_init_tx_channel(struct emac_dev_s *dev, u32 channel,
+ void *ch_close_args);
+static int emac_un_init_rx_channel(struct emac_dev_s *dev, u32 channel,
+ void *ch_close_args);
+static void emac_set_mac_address(struct emac_dev_s *dev, u32 channel,
+ char *mac_addr);
+static void emac_ddcifcnt_clear(struct emac_dev_s *dev);
+static void emac_ddcifcnt_updt(struct emac_dev_s *dev);
+static void emac_ddcphycnt(struct emac_dev_s *dev, u32 *cmd_arg);
+
+/* string to ethernet address conversion */
+static void emac_str_to_ethaddr(unsigned char *ea, unsigned char *str)
+{
+ int i;
+ unsigned char num;
+
+ for (i = 0; i < 6; i++) {
+ if ((*str == '.') || (*str == ':'))
+ str++;
+ num = emac_str_to_hexnum(*str) << 4;
+ ++str;
+ num |= (emac_str_to_hexnum(*str));
+ ++str;
+ ea[i] = num;
+ }
+}
+
+static int emac_cfg_build(void)
+{
+ static int cfg_instance;
+ sprintf(emac_cfg[cfg_instance],
+ "%d:%x:%d:%d:%u:%d:%d:%d:%d:%d:%d:%d:%d:%d"
+ ":%d:%d:%x:%d:%d:%u:%u:%x:%d",
+ cfg_instance, EMAC_BASE_ADDR,
+ EMAC_INTERRUPT, 0, EMAC_BUS_FREQUENCY,
+ g_link_speed, g_link_mode, EMAC_DEFAULT_PROMISCOUS_ENABLE,
+ EMAC_DEFAULT_BROADCAST_ENABLE,
+ EMAC_DEFAULT_MULTICAST_ENABLE,
+ EMAC_DEFAULT_MAX_FRAME_SIZE,
+ EMAC_DEFAULT_TX_NUM_BD,
+ EMAC_DEFAULT_TX_MAX_SERVICE, EMAC_DEFAULT_RX_NUM_BD,
+ EMAC_DEFAULT_RX_MAX_SERVICE, 0,
+ EMAC_MDIO_BASE_ADDR, 0, 0,
+ EMAC_BUS_FREQUENCY, EMAC_MDIO_FREQUENCY, EMAC_PHY_MASK, 10);
+
+ DBG("Driver Config:\n%s\n", emac_cfg[cfg_instance]);
+
+ cfg_instance++;
+
+ return 0;
+}
+
+/*
+ * cmdline param format: dm6446eth=mac:00.11.22.33.44.55
+ */
+static int emac_cmdline_mac_setup(char *str)
+{
+ /* The first char passed from the bootloader is '=', so ignore it */
+ strcpy(&emac_eth_string[0], &str[1]);
+
+ printk(KERN_INFO"TI DaVinci EMAC: Kernel Boot params Eth address: %s\n",
+ emac_eth_string);
+
+ return (1);
+}
+__setup("eth", emac_cmdline_mac_setup);
+
+static int emac_cfg_probe(void)
+{
+ /* for DaVinci there is only 1 EMAC instance */
+ if (emac_cfg_build())
+ return -1;
+
+ return 0;
+}
+
+/*
+ * DDA Callback functions
+ */
+
+/* emac_control_cb - ioctl function to be called by the DDC */
+static int emac_control_cb(struct emac_dev_s *dev, int cmd,
+ void *cmd_arg, void *param)
+{
+ switch (cmd) {
+ case EMAC_IOCTL_TIMER_START:
+ /*
+ * cmd will directly have the timer period
+ * of the periodic timer, param not used
+ * asks for milliSecs. So calculate ticks
+ * from ticks per 1000 mSec
+ */
+ {
+ struct timer_list *p_timer = &dev->periodic_timer;
+ dev->periodic_ticks =
+ (EMAC_TICKS_PER_SEC * (int)cmd_arg) / 1000;
+ p_timer->expires = jiffies + dev->periodic_ticks;
+ add_timer(&dev->periodic_timer);
+ dev->timer_active = TRUE;
+ }
+ break;
+ case EMAC_IOCTL_TIMER_STOP:
+ /* cmd and param not used */
+ if (dev->timer_active == TRUE) {
+ del_timer_sync(&dev->periodic_timer);
+ dev->timer_active = FALSE;
+ }
+ break;
+ case EMAC_IOCTL_STATUS_UPDATE:
+ /* cmd_arg will point to status structure,
+ *param not used */
+ {
+ struct net_device *netdev = dev->owner;
+
+ struct emac_status *status = &dev->ddc_status;
+ dev->ddc_status = *((struct emac_status *) cmd_arg);
+ if ((status->hw_status & EMAC_TX_HOST_ERROR) ==
+ EMAC_TX_HOST_ERROR)
+ ERR("TX Host Error. Transmit Stopped %s\n",
+ netdev->name);
+
+ if ((status->hw_status & EMAC_RX_HOST_ERROR) ==
+ EMAC_RX_HOST_ERROR)
+ ERR("RX Host Error. Receive Stopped %s\n",
+ netdev->name);
+
+ if (status->phy_linked) {
+ /* link ON */
+ if (!netif_carrier_ok(netdev))
+ netif_carrier_on(netdev);
+ dev->link_speed =
+ ((status->phy_speed == 100) ?
+ 100000000 : 10000000);
+ dev->link_mode =
+ ((status->
+ phy_duplex == PHY_DUPLEX_FULL) ?
+ PHY_DUPLEX_FULL :
+ PHY_DUPLEX_HALF);
+
+ /* reactivate the transmit queue if it
+ *is stopped */
+ if (netif_running(netdev) &&
+ netif_queue_stopped(netdev))
+ netif_wake_queue(netdev);
+ } else {
+ /* link OFF */
+ if (netif_carrier_ok(netdev)) {
+ /* do we need to register
+ *synchronization issues with
+ *stats here. */
+ dev->link_speed = 100000000;
+ dev->link_mode = 1;
+ netif_carrier_off(netdev);
+ }
+ if (!netif_queue_stopped(netdev))
+ /* so that kernel does not
+ *keep on xmiting pkts. */
+ netif_stop_queue(netdev);
+ }
+
+ if (emac_link_status)
+ DBG("%s, PhyNum %d, %s, %s, %s\n",
+ ((struct net_device *)dev->owner)->name,
+ status->phy_num,
+ ((status->phy_duplex == PHY_DUPLEX_FULL) ?
+ "Full Duplex" : "Half Duplex"),
+ ((status->phy_speed == 100) ?
+ "100 Mbps" : "10 Mbps"),
+ ((status->phy_linked) ?
+ "Linked" : "NO LINK"));
+ }
+ break;
+ case EMAC_IOCTL_MIB64_CNT_TIMER_START:
+ /*
+ * cmd will directly have the timer period of the
+ * periodic timer, param not used
+ * asks for milli_secs. so calculate ticks
+ * from ticks per 1000 m_sec
+ */
+ {
+ struct timer_list *p_timer = &dev->mib_timer;
+
+ dev->mib_ticks =
+ (EMAC_TICKS_PER_SEC * (int)cmd_arg) / 1000;
+ p_timer->expires = jiffies + dev->mib_ticks;
+ add_timer(p_timer);
+ dev->mib_timer_active = TRUE;
+ }
+ break;
+ case EMAC_IOCTL_MIB64_CNT_TIMER_STOP:
+ /* cmd_arg and param not used */
+ if (dev->mib_timer_active == TRUE) {
+ del_timer_sync(&dev->mib_timer);
+ dev->mib_timer_active = FALSE;
+ }
+ break;
+ default:
+ DBG("Unhandled ioctl code %d\n", cmd);
+ break;
+ }
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ *
+ * emacEndGetConfig - Extract configuration for given unit number/instance
+ *
+ * This function gets the configuration information from the
+ * configuration service or by some means for the given unit
+ * number/emac instance
+ *
+ * Note: For debug/default, static information is obtained from the
+ * header file
+ *
+ * RETURNS: OK or ERROR.
+ */
+static int emac_net_get_config(struct emac_dev_s *dev)
+{
+ struct emac_init_config *i_cfg = &dev->init_cfg;
+ struct emac_ch_info *tx_ch_cfg = &dev->tx_ch_info[0];
+ struct emac_ch_info *rx_ch_cfg = &dev->rx_ch_info[0];
+ int speed, duplex, extra;
+ char local_string_val[200];
+ char *local_string = NULL;
+ char *tok;
+ char *p_holder = NULL;
+
+ local_string = emac_cfg[(dev->instance_num < EMAC_MAX_INSTANCES)?
+ dev->instance_num : 0];
+
+ strcpy(local_string_val, local_string);
+ local_string = local_string_val;
+ p_holder = NULL;
+ tok = (char *)strsep(&local_string, ":");
+ if (tok == NULL)
+ return -1;
+
+ i_cfg->inst_id = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("i_cfg->instId=%d", i_cfg->inst_id);
+
+ i_cfg->base_address = EMAC_TOKEN_GET_HEX;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->baseAddress=%08X", i_cfg->base_address);
+
+ i_cfg->intr_line = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->intrLine=%d", i_cfg->intr_line);
+
+ i_cfg->reset_line = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->resetLine=%d", i_cfg->reset_line);
+
+ i_cfg->emac_bus_frequency = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->emacBusFrequency=%d", i_cfg->emac_bus_frequency);
+
+ speed = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\nspeed=%d", speed);
+
+ duplex = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\nduplex=%d", duplex);
+
+ i_cfg->rx_cfg.promiscous_enable = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->rxCfg.promiscousEnable=%d",
+ i_cfg->rx_cfg.promiscous_enable);
+
+ i_cfg->rx_cfg.broadcast_enable = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->rxCfg.broadcastEnable=%d",
+ i_cfg->rx_cfg.broadcast_enable);
+
+ i_cfg->rx_cfg.multicast_enable = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->rxCfg.multicastEnable=%d",
+ i_cfg->rx_cfg.multicast_enable);
+
+ i_cfg->rx_cfg.max_rx_pkt_length = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->rxCfg.maxRxPktLength=%d",
+ i_cfg->rx_cfg.max_rx_pkt_length);
+
+ tx_ch_cfg->num_bd = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ntx_ch_cfg->num_bd=%d", tx_ch_cfg->num_bd);
+
+ tx_ch_cfg->service_max = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ntx_ch_cfg->service_max=%d", tx_ch_cfg->service_max);
+
+ rx_ch_cfg->num_bd = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\nrx_ch_cfg->num_bd=%d", rx_ch_cfg->num_bd);
+
+ rx_ch_cfg->service_max = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\nrx_ch_cfg->service_max=%d", rx_ch_cfg->service_max);
+
+ extra = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\nextra=%d", extra);
+
+ i_cfg->mdio_base_address = EMAC_TOKEN_GET_HEX;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->mdioBaseAddress=%08X", i_cfg->mdio_base_address);
+
+ i_cfg->mdio_intr_line = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->mdioIntrLine=%d", i_cfg->mdio_intr_line);
+
+ i_cfg->mdio_reset_line = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->mdioResetLine=%d", i_cfg->mdio_reset_line);
+
+ i_cfg->mdio_bus_frequency = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->MdioBusFrequency=%d", i_cfg->mdio_bus_frequency);
+
+ i_cfg->mdio_clock_frequency = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->MdioClockFrequency=%d", i_cfg->mdio_clock_frequency);
+
+ i_cfg->phy_mask = EMAC_TOKEN_GET_HEX;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->PhyMask=%08X", i_cfg->phy_mask);
+
+ i_cfg->mdio_tick_msec = EMAC_TOKEN_GET_INTEGER;
+ DBG("\ni_cfg->MdioTickMSec=%d", i_cfg->mdio_tick_msec);
+ DBG("\n");
+
+ i_cfg->mib64cnt_msec = CONFIG_EMAC_MIB_TIMER_TIMEOUT;
+ rx_ch_cfg->buf_size = i_cfg->rx_cfg.max_rx_pkt_length;
+ dev->rx_buf_offset =
+ EMAC_L3_ALIGN(extra) + EMAC_DEFAULT_EXTRA_RXBUF_SIZE;
+ dev->rx_buf_size = (rx_ch_cfg->buf_size + dev->rx_buf_offset);
+
+ /* align skb's on 4 byte boundry - no hard requirement currently - done
+ * for future use
+ */
+ dev->rx_buf_size += EMAC_4BYTE_ALIGN(dev->rx_buf_size);
+
+ /* determine phy speed/duplex mode - to be built as per MDIO
+ *module requirements */
+ if (speed == CONFIG_EMAC_NOPHY) {
+ i_cfg->phy_mode = SNWAY_NOPHY;
+ } else {
+ if ((speed == 0) && (duplex == PHY_DUPLEX_AUTO)) {
+ i_cfg->phy_mode = SNWAY_AUTOALL;
+ } else if (speed == 10) {
+ if (duplex == PHY_DUPLEX_HALF)
+ i_cfg->phy_mode = SNWAY_HD10;
+ else if (duplex == PHY_DUPLEX_FULL)
+ i_cfg->phy_mode = SNWAY_FD10;
+ else
+ i_cfg->phy_mode = SNWAY_HD10 | SNWAY_FD10;
+ } else if (speed == 100) {
+ if (duplex == PHY_DUPLEX_HALF)
+ i_cfg->phy_mode = SNWAY_HD100;
+ else if (duplex == PHY_DUPLEX_FULL)
+ i_cfg->phy_mode = SNWAY_FD100;
+ else
+ i_cfg->phy_mode = SNWAY_HD100 | SNWAY_FD100;
+ } else {
+ if (duplex == PHY_DUPLEX_FULL)
+ i_cfg->phy_mode = SNWAY_FD10 | SNWAY_FD100;
+ else
+ i_cfg->phy_mode = SNWAY_HD10 | SNWAY_HD100;
+ }
+ }
+
+ dev->vlan_enable = EMAC_DEFAULT_VLAN_ENABLE;
+ i_cfg->num_tx_channels = EMAC_DEFAULT_NUM_TX_CHANNELS;
+ i_cfg->num_rx_channels = EMAC_DEFAULT_NUM_RX_CHANNELS;
+ i_cfg->MLink_mask = EMAC_DEFAULT_MLINK_MASK;
+ i_cfg->rx_cfg.pass_crc = EMAC_DEFAULT_PASS_CRC;
+ i_cfg->rx_cfg.qos_enable = EMAC_DEFAULT_QOS_ENABLE;
+ i_cfg->rx_cfg.no_buffer_chaining = EMAC_DEFAULT_NO_BUFFER_CHAINING;
+ i_cfg->rx_cfg.copy_maccontrol_frames_enable =
+ EMAC_DEFAULT_COPY_MAC_CONTROL_FRAMES_ENABLE;
+ i_cfg->rx_cfg.copy_short_frames_enable =
+ EMAC_DEFAULT_COPY_SHORT_FRAMES_ENABLE;
+ i_cfg->rx_cfg.copy_error_frames_enable =
+ EMAC_DEFAULT_COPY_ERROR_FRAMES_ENABLE;
+ i_cfg->rx_cfg.promiscous_channel = EMAC_DEFAULT_PROMISCOUS_CHANNEL;
+ i_cfg->rx_cfg.broadcast_channel = EMAC_DEFAULT_BROADCAST_CHANNEL;
+ i_cfg->rx_cfg.multicast_channel = EMAC_DEFAULT_MULTICAST_CHANNEL;
+ i_cfg->rx_cfg.buffer_offset = EMAC_DEFAULT_BUFFER_OFFSET;
+ i_cfg->mac_cfg.p_type = EMAC_TXPRIO_FIXED;
+ i_cfg->mac_cfg.tx_short_gap_enable = FALSE;
+
+ if (speed == 1000)
+ i_cfg->mac_cfg.giga_bit_enable = TRUE;
+ else
+ i_cfg->mac_cfg.giga_bit_enable = FALSE;
+
+ i_cfg->mac_cfg.tx_pacing_enable = EMAC_DEFAULT_TX_PACING_ENABLE;
+ i_cfg->mac_cfg.mii_enable = EMAC_DEFAULT_MII_ENABLE;
+ i_cfg->mac_cfg.tx_flow_enable = EMAC_DEFAULT_TX_FLOW_ENABLE;
+ i_cfg->mac_cfg.rx_flow_enable = EMAC_DEFAULT_RX_FLOW_ENABLE;
+ i_cfg->mac_cfg.loopback_enable = EMAC_DEFAULT_LOOPBACK_ENABLE;
+ i_cfg->mac_cfg.full_duplex_enable = EMAC_DEFAULT_FULL_DUPLEX_ENABLE;
+ i_cfg->mac_cfg.tx_interrupt_disable = EMAC_DEFAULT_TX_INTERRUPT_DISABLE;
+ tx_ch_cfg->ch_num = EMAC_DEFAULT_TX_CHANNEL;
+ tx_ch_cfg->ch_dir = NET_CH_DIR_TX;
+ tx_ch_cfg->ch_state = NET_CH_UNINITIALIZED;
+ rx_ch_cfg->ch_num = EMAC_DEFAULT_RX_CHANNEL;
+ rx_ch_cfg->ch_dir = NET_CH_DIR_RX;
+ rx_ch_cfg->ch_state = NET_CH_UNINITIALIZED;
+
+ /* module control EWrap base address for DaVinci */
+ i_cfg->e_wrap_base_address = EMAC_WRAPPER_REGS_ADDR;
+
+ DBG("\n");
+ return 0;
+}
+
+/* detect manual config */
+static int __init emac_p_detect_manual_cfg(int link_speed, char *link_mode,
+ int debug)
+{
+ if (debug == 1) {
+ emac_debug_mode = 1;
+ DBG("Enabled debug print.\n");
+ }
+
+ if ((link_speed == 0) && (!strcmp(link_mode, "auto"))) {
+ /* Auto negotiation */
+ g_link_speed = 0;
+ g_link_mode = 0;
+ DBG("auto negotiation selected\n");
+ }
+
+ if (!link_speed || (link_speed != 10 && link_speed != 100)) {
+ g_link_speed = 0;
+ g_link_mode = 0;
+ DBG("Invalid or No value of link speed specified,"
+ "defaulting to auto negotiation .\n");
+ }
+
+ if (!link_mode
+ || (!strcmp(link_mode, "fd") && !strcmp(link_mode, "hd"))) {
+ g_link_speed = 0;
+ g_link_mode = 0;
+ DBG("Invalid or No value of link mode specified,"
+ "defaulting to auto mode.\n");
+ }
+
+ if ((link_speed == 10) && (!strcmp(link_mode, "fd"))) {
+ g_link_speed = 10;
+ g_link_mode = 3;
+ } else if ((link_speed == 10) && (!strcmp(link_mode, "hd"))) {
+ g_link_speed = 10;
+ g_link_mode = 2;
+ } else if ((link_speed == 100) && (!strcmp(link_mode, "fd"))) {
+ g_link_speed = 100;
+ g_link_mode = 3;
+ } else if ((link_speed == 100) && (!strcmp(link_mode, "hd"))) {
+ g_link_speed = 100;
+ g_link_mode = 2;
+ } else {
+ g_link_speed = 0;
+ g_link_mode = 0;
+ }
+
+ DBG("Link is set to the speed of"
+ "%s speed and %s mode.\n",
+ ((g_link_speed ==
+ 0) ? "auto" : ((g_link_speed == 100) ? "100" : "10")),
+ ((g_link_mode ==
+ 0) ? "auto" : ((g_link_mode == 2) ? "half" : "full")));
+
+ return 0;
+}
+
+int emac_p_reset_statistics(struct net_device *netdev)
+{
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ memset(&dev->device_mib, 0, sizeof(struct emac_hw_statistics));
+ memset(&dev->device_stats, 0, sizeof(struct emac_drv_stats));
+ memset(&dev->net_dev_stats, 0, sizeof(struct net_device_stats));
+
+ /* clear statistics */
+ if (emac_control(dev, EMAC_IOCTL_CLR_STATISTICS, NULL, NULL)
+ != EMAC_SUCCESS) {
+ ERR("Error clearing statistics in DDC for %s\n", netdev->name);
+ return -1;
+ }
+
+ return 0;
+}
+
+int emac_p_update_statistics(struct net_device *netdev)
+{
+ unsigned long rx_hal_errors = 0;
+ unsigned long rx_hal_discards = 0;
+ unsigned long tx_hal_errors = 0;
+ unsigned long if_out_discards = 0;
+ unsigned long if_in_discards = 0;
+ unsigned long if_out_errors = 0;
+ unsigned long if_in_errors = 0;
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+ struct emac_hw_statistics *p_device_mib = &dev->device_mib;
+ struct emac_hw_statistics local_mib;
+ struct emac_hw_statistics *p_local_mib = &local_mib;
+ struct net_device_stats *p_net_dev_stats = &dev->net_dev_stats;
+ int dev_mib_elem_count = 0;
+
+ /* do not access the hardware if it is in the reset state. */
+ if (!test_bit(0, &dev->set_to_close)) {
+ /* get hardware statistics from DDC */
+ if (emac_control(dev, EMAC_IOCTL_GET_STATISTICS,
+ (void *)p_local_mib, NULL)
+ != EMAC_SUCCESS) {
+ ERR("Error getting statistics for %s\n", netdev->name);
+
+ return -1;
+ }
+
+ dev_mib_elem_count =
+ sizeof(struct emac_hw_statistics) / sizeof(unsigned long);
+
+ /*
+ * Update the history of the stats. This takes care of
+ * any reset of the device and stats that might have
+ * taken place during the life time of the driver.
+ */
+ while (dev_mib_elem_count--)
+ *((unsigned long *)p_device_mib + dev_mib_elem_count) =
+ *((unsigned long *)p_local_mib +
+ dev_mib_elem_count);
+ }
+
+ /* RFC2665, section 3.2.7, page 9 */
+ rx_hal_errors =
+ p_device_mib->if_in_fragments +
+ p_device_mib->if_in_crcerrors +
+ p_device_mib->if_in_align_code_errors +
+ p_device_mib->if_in_jabber_frames;
+
+ /* RFC2233 */
+ rx_hal_discards = p_device_mib->if_rx_dmaoverruns;
+
+ /* RFC2665, section 3.2.7, page 9 */
+ tx_hal_errors =
+ p_device_mib->if_excessive_collision_frames +
+ p_device_mib->if_late_collisions +
+ p_device_mib->if_carrier_sense_errors +
+ p_device_mib->if_out_underrun;
+
+ /* if not set, the short frames (< 64 bytes) are considered as
+ errors */
+ if (dev->init_cfg.rx_cfg.copy_short_frames_enable == FALSE)
+ rx_hal_errors += p_device_mib->if_in_undersized_frames;
+
+ /* All frames greater than max rx frame length set in hardware
+ *should be considered error frames RFC2665, section 3.2.7,
+ *page 9. */
+ rx_hal_errors += p_device_mib->if_in_oversized_frames;
+
+ /* if not in promiscous, then non addr matching frames are discarded */
+ /* EMAC 2.0 manual section 2.8.1.14 */
+ if (dev->init_cfg.rx_cfg.promiscous_enable == FALSE)
+ if_in_discards += p_device_mib->if_in_filtered_frames;
+
+ /* total rx discards = hal discards */
+ if_in_discards = rx_hal_discards;
+ p_net_dev_stats->rx_dropped = rx_hal_discards;
+ p_net_dev_stats->rx_crc_errors = p_device_mib->if_in_crcerrors;
+ p_net_dev_stats->rx_frame_errors =
+ p_device_mib->if_in_align_code_errors;
+ p_net_dev_stats->multicast = p_device_mib->if_in_multicasts;
+ if_in_errors = rx_hal_errors;
+ if_out_errors = tx_hal_errors;
+ if_out_discards = p_net_dev_stats->tx_dropped;
+
+ /* let us update the net device stats struct. to be updated in
+ the later releases. */
+ dev->net_dev_stats.rx_errors = if_in_errors;
+ dev->net_dev_stats.collisions = p_device_mib->if_collision_frames;
+ dev->net_dev_stats.tx_carrier_errors =
+ p_device_mib->if_carrier_sense_errors;
+
+ return 0;
+}
+
+int emac_p_get_version(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ int len = 0;
+
+ len += sprintf(buf + len, "Texas Instruments : %s\n",
+ emac_version_string);
+ return len;
+}
+
+/* tick timer */
+static void emac_p_tick_timer_expiry(struct emac_dev_s *dev)
+{
+ struct timer_list *p_timer = &dev->periodic_timer;
+
+ if (test_bit(0, &dev->set_to_close))
+ return;
+
+ if (dev->timer_active == TRUE) {
+ emac_tick(dev, NULL);
+ /* restart the timer */
+ p_timer->expires = jiffies + dev->periodic_ticks;
+ add_timer(p_timer);
+ }
+}
+
+/* mib timer */
+static void emac_p_mib_timer_expiry(struct emac_dev_s *dev)
+{
+ struct timer_list *p_timer = &dev->mib_timer;
+
+ if (test_bit(0, &dev->set_to_close))
+ return;
+
+ if (dev->mib_timer_active == TRUE) {
+ emac_control(dev, EMAC_IOCTL_IF_PARAMS_UPDT, NULL, NULL);
+ /* restart the timer */
+ p_timer->expires = jiffies + dev->mib_ticks;
+ add_timer(p_timer);
+ }
+}
+
+/*
+ * Device enable/disable functions
+ */
+
+/**
+ * enable the device - init TX/RX channels and open DDC
+ */
+static int emac_p_dev_enable(struct emac_dev_s *dev)
+{
+ int ret_code;
+ struct net_device *netdev = dev->owner;
+
+ dev->set_to_close = 0;
+
+ /* create a TX channel */
+ ret_code = emac_ch_open(dev, &dev->tx_ch_info[0], NULL);
+
+ if (ret_code != EMAC_SUCCESS) {
+ ERR("%s error: Error %08X from EMAC TX Channel Open()\n",
+ netdev->name, ret_code);
+ return (-1);
+ }
+
+ /* create a RX channel - note that last param contains mac address */
+ ret_code =
+ emac_ch_open(dev, &dev->rx_ch_info[0], (void *)&dev->mac_addr[0]);
+ if (ret_code != EMAC_SUCCESS) {
+ ERR("%s error: Error %08X from EMAC RX Channel Open()\n",
+ netdev->name, ret_code);
+ return -1;
+ }
+ /* open DDC instance */
+ ret_code = emac_open(dev, NULL);
+ if (ret_code != EMAC_SUCCESS) {
+ ERR("%s error: Error %08X from EMAC Open()\n",
+ netdev->name, ret_code);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* disable the device - teardown chanels and close DDC */
+static int emac_p_dev_disable(struct emac_dev_s *dev)
+{
+ int ret_code;
+ struct net_device *netdev = dev->owner;
+
+ /* inform the upper layers. */
+ netif_stop_queue(dev->owner);
+
+ /* prepare to close */
+ set_bit(0, &dev->set_to_close);
+
+ /* closing the DDC instance will close all channels also */
+ ret_code = emac_close(dev, NULL);
+
+ if (ret_code != EMAC_SUCCESS) {
+ ERR("%s error: Error %08X from EMAC Close()\n",
+ netdev->name, ret_code);
+ return -1;
+ } else {
+ /* DDC should turn off the timer, but just in case */
+ if (dev->timer_active != FALSE) {
+ del_timer_sync(&dev->periodic_timer);
+ dev->timer_active = FALSE;
+ }
+
+ DBG("Device %s Closed.\n", netdev->name);
+ dev->device_stats.start_tick = jiffies;
+ dev->link_speed = 100000000;
+ dev->link_mode = 1;
+ netif_carrier_off(netdev);
+ }
+ return 0;
+}
+
+/*
+ * Net Device functions
+ */
+
+/* get statistics */
+static struct net_device_stats *emac_dev_get_net_stats(struct net_device
+ *netdev)
+{
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+ emac_p_update_statistics(netdev);
+ return &dev->net_dev_stats;
+}
+
+/* set multicast address in the driver */
+static void emac_dev_mcast_set(struct net_device *netdev)
+{
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ if (netdev->flags & IFF_PROMISC) {
+ /* enable promiscous mode */
+ dev->init_cfg.rx_cfg.promiscous_enable = TRUE;
+
+ emac_control(dev,
+ EMAC_IOCTL_SET_RXCFG,
+ (void *)&dev->init_cfg.rx_cfg, NULL);
+ } else if ((netdev->flags & IFF_ALLMULTI) ||
+ (netdev->mc_count > EMAC_DEFAULT_MAX_MULTICAST_ADDRESSES)) {
+ /* enable multicast - disable promiscous */
+ dev->init_cfg.rx_cfg.promiscous_enable = FALSE;
+ dev->init_cfg.rx_cfg.multicast_enable = TRUE;
+ emac_control(dev,
+ EMAC_IOCTL_SET_RXCFG,
+ (void *)&dev->init_cfg.rx_cfg, NULL);
+
+ /* enable all multicast addresses */
+ emac_control(dev, EMAC_IOCTL_ALL_MULTI, (void *)
+ EMAC_ALL_MULTI_SET, NULL);
+ } else if (netdev->mc_count == 0) {
+ /* only unicast mode to be set - clear promiscous and
+ clear multicast modes */
+ emac_control(dev, EMAC_IOCTL_ALL_MULTI, (void *)
+ EMAC_ALL_MULTI_CLR, NULL);
+
+ /* disable promiscous and multicast modes */
+ dev->init_cfg.rx_cfg.promiscous_enable = FALSE;
+ dev->init_cfg.rx_cfg.multicast_enable = FALSE;
+ emac_control(dev,
+ EMAC_IOCTL_SET_RXCFG,
+ (void *)&dev->init_cfg.rx_cfg, NULL);
+ } else if (netdev->mc_count) {
+ struct dev_mc_list *mc_ptr;
+
+ /* clear multicast list first */
+ emac_control(dev, EMAC_IOCTL_ALL_MULTI, (void *)
+ EMAC_ALL_MULTI_CLR, NULL);
+
+ /* enable multicast - disable promiscous */
+ dev->init_cfg.rx_cfg.promiscous_enable = FALSE;
+ dev->init_cfg.rx_cfg.multicast_enable = TRUE;
+ emac_control(dev,
+ EMAC_IOCTL_SET_RXCFG,
+ (void *)&dev->init_cfg.rx_cfg, NULL);
+
+ /* program multicast address list into EMAC hardware */
+ for (mc_ptr = netdev->mc_list; mc_ptr; mc_ptr = mc_ptr->next)
+ emac_control(dev, EMAC_IOCTL_MULTICAST_ADDR, (void *)
+ EMAC_MULTICAST_ADD, (void *)
+ mc_ptr->dmi_addr);
+ } else
+ DBG("%s:No Multicast address to set.\n", netdev->name);
+}
+
+static int emac_dev_set_mac_addr(struct net_device *netdev, void *addr)
+{
+ int ret_code;
+ struct emac_address_params address_params;
+ struct sockaddr *sa = addr;
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ address_params.channel = EMAC_DEFAULT_RX_CHANNEL;
+ address_params.mac_address = sa->sa_data;
+
+ ret_code = emac_control(dev, EMAC_IOCTL_SET_MAC_ADDRESS,
+ (struct emac_address_params *) &address_params, NULL);
+
+ if (ret_code != EMAC_SUCCESS) {
+ ERR("%s error: Error %08X from EMAC TX Channel Open()\n",
+ netdev->name, ret_code);
+
+ return -EIO;
+ }
+ memcpy(dev->mac_addr, sa->sa_data, netdev->addr_len);
+ memcpy(netdev->dev_addr, sa->sa_data, netdev->addr_len);
+ return 0;
+}
+
+static void emac_tx_timeout(struct net_device *netdev)
+{
+ int ret_code;
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ printk(KERN_NOTICE"EMAC Tx Timeout: Closing TX channel\n");
+ emac_ch_close(dev, dev->tx_ch_info[0].ch_num,
+ dev->tx_ch_info[0].ch_dir, 0);
+
+ printk(KERN_NOTICE"EMAC Tx Timeout: Opening TX channel\n");
+ ret_code = emac_ch_open(dev, &dev->tx_ch_info[0], NULL);
+
+ if (ret_code != EMAC_SUCCESS)
+ ERR("%s error: Error %08X from EMAC TX Channel Open()\n",
+ netdev->name, ret_code);
+ else
+ ERR("EMAC Tx Timeout: "
+ "successfully closed and opened channels\n");
+
+ /* update interface statistics */
+ dev->net_dev_stats.tx_errors++;
+}
+
+/**
+ * emac_dev_init
+ *
+ * Returns:
+ * 0 on success, error code otherwise.
+ * Parms:
+ * dev The structure of the device to be
+ * init'ed.
+ *
+ * This function completes the initialization of the
+ * device structure and driver. It reserves the IO
+ * addresses and assignes the device's methods.
+ *
+ */
+static int emac_dev_init(struct net_device *netdev)
+{
+ int cnt, init_status = 0;
+ int ret_code;
+ char *mac_name = NULL;
+ char *mac_string = NULL;
+ char *default_mac_string = NULL;
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+ int instance_num = dev->instance_num;
+
+ /* create mac name */
+ switch (instance_num) {
+ case 0:
+ mac_name = EMAC_MAC_ADDR_A;
+
+ /* we are getting default MAC address from bootloader */
+ if (strcmp(emac_eth_string, "deadbeaf") == 0)
+ default_mac_string = "08:00:28:32:06:08";
+ else
+ default_mac_string = &emac_eth_string[0];
+ break;
+ default:
+ mac_name = "";
+ default_mac_string = "08:00:28:32:06:08";
+ break;
+ }
+ printk(KERN_INFO "TI DaVinci EMAC: MAC address is %s\n",
+ default_mac_string);
+
+ mac_string = default_mac_string;
+ emac_str_to_ethaddr(dev->mac_addr, mac_string);
+ for (cnt = 0; cnt <= ETH_ALEN; cnt++)
+ netdev->dev_addr[cnt] = dev->mac_addr[cnt];
+
+ dev->set_to_close = 1;
+
+ /* get configuration information for this instance */
+ /* when config service is available, use it */
+ if (emac_net_get_config(dev) != 0) {
+ ERR("Could not fetch configuration information\n");
+ goto emac_dev_init_exit;
+ }
+
+ dev->init_cfg.inst_id = instance_num;
+ dev->drv_state = DRV_CREATED;
+ init_status = 1; /* instance created */
+
+ /* initialize instance by passing initial configuration struct */
+ ret_code = emac_init(dev, &dev->init_cfg);
+
+ if (ret_code != EMAC_SUCCESS) {
+ ERR("Error %08X from Init()\n", ret_code);
+ goto emac_dev_init_exit;
+ }
+
+ init_status = 2; /* instance initialized */
+
+ /* init spin lock */
+ spin_lock_init(&dev->tx_lock);
+ spin_lock_init(&dev->rx_lock);
+
+ /* set as per RFC 2665 */
+ dev->link_speed = 100000000;
+ dev->link_mode = 1;
+
+ /* initialize the timers for the net device - the timer will
+ be started by DDC calling the ioctl on DDA */
+ init_timer(&dev->periodic_timer);
+ dev->periodic_ticks = 0;
+ dev->periodic_timer.expires = 0;
+ dev->timer_active = FALSE;
+ dev->periodic_timer.data = (unsigned long)dev;
+ dev->periodic_timer.function =
+ (timer_tick_func) emac_p_tick_timer_expiry;
+ init_timer(&dev->mib_timer);
+ dev->mib_timer_active = FALSE;
+ dev->mib_timer.data = (unsigned long)dev;
+ dev->mib_timer.function = (timer_tick_func) emac_p_mib_timer_expiry;
+
+ /* populate the device structure */
+ netdev->addr_len = 6;
+ netdev->open = emac_dev_open; /* i.e. start device */
+ netdev->do_ioctl = emac_ioctl;
+ netdev->hard_start_xmit = emac_dev_tx;
+ netdev->stop = emac_dev_close;
+ netdev->get_stats = emac_dev_get_net_stats;
+ netdev->set_multicast_list = emac_dev_mcast_set;
+ netdev->tx_timeout = emac_tx_timeout;
+ netdev->set_mac_address = emac_dev_set_mac_addr;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ netdev->poll_controller = emac_poll_controller;
+#endif
+
+ /* reset the broadcast and multicast flags and enable them
+ based upon configuration of driver */
+ netdev->flags &= ~(IFF_PROMISC | IFF_BROADCAST | IFF_MULTICAST);
+ if (dev->init_cfg.rx_cfg.broadcast_enable == TRUE)
+ netdev->flags |= IFF_BROADCAST;
+ if (dev->init_cfg.rx_cfg.multicast_enable == TRUE)
+ netdev->flags |= IFF_MULTICAST;
+
+ netif_carrier_off(netdev);
+ netdev->irq = dev->init_cfg.intr_line;
+
+ /* request memory region from the kernel */
+ netdev->base_addr = dev->init_cfg.base_address;
+ request_mem_region(netdev->base_addr, EMAC_DEFAULT_EMAC_SIZE,
+ netdev->name);
+
+ /* if following flag ON then open DDC */
+ if (g_init_enable_flag) {
+ if (emac_p_dev_enable(dev)) {
+ ERR("device could not OPEN\n");
+ goto emac_dev_init_exit;
+ }
+ }
+
+ return 0;
+
+emac_dev_init_exit:
+ /* all resources allocated are freed - call the un-init
+ sequence on DDC */
+ switch (init_status) {
+ case 2:
+ ret_code = emac_de_init(dev, NULL);
+
+ if (ret_code != EMAC_SUCCESS)
+ ERR("%s: Error %08X from Deinit()\n",
+ netdev->name, ret_code);
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+/*
+ * Device Open/Close functions
+ */
+
+#if !defined(SA_INTERRUPT)
+#define SA_INTERRUPT 0
+#endif
+
+static int emac_dev_open(struct net_device *netdev)
+{
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ if (!g_init_enable_flag) {
+ if (emac_p_dev_enable(dev)) {
+ ERR("%s error: device could not OPEN\n", netdev->name);
+ return -1;
+ }
+ }
+
+ if (request_irq(dev->irq_line, emac_hal_isr, SA_INTERRUPT,
+ "EMAC", dev)) {
+ printk(KERN_NOTICE"EMAC: Failed to register the irq %d"
+ " for EMAC %s.\n", dev->init_cfg.intr_line,
+ netdev->name);
+ return -1;
+ }
+ if (netif_carrier_ok(netdev))
+ netif_start_queue(netdev);
+ else
+ netif_stop_queue(netdev);
+
+ dev->device_stats.start_tick = jiffies;
+ napi_enable(&dev->napi);
+ printk(KERN_INFO"Started the network queue for %s.\n", netdev->name);
+ return 0;
+}
+
+static int emac_dev_close(struct net_device *netdev)
+{
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ napi_disable(&dev->napi);
+
+ if (!g_init_enable_flag)
+ emac_p_dev_disable(dev);
+
+ /* free ISR */
+ free_irq(dev->init_cfg.intr_line, dev);
+
+ return 0;
+}
+
+static int emac_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
+{
+ struct emac_drv_priv_ioctl priv_ioctl;
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ if (cmd == SIOCDEVPRIVATE) {
+ /* copy user data */
+ if (copy_from_user
+ ((char *)&priv_ioctl, (char *)rq->ifr_data,
+ sizeof(struct emac_drv_priv_ioctl)))
+ return -EFAULT;
+
+ switch (priv_ioctl.cmd) {
+ /* program type 2/3 address filter */
+ case EMAC_PRIV_FILTERING:
+ {
+ struct emac_type2_3_addr_filter_params filter_params;
+
+ if (copy_from_user
+ ((char *)&filter_params,
+ (char *)priv_ioctl.data,
+ sizeof(struct emac_type2_3_addr_filter_params)))
+
+ return -EFAULT;
+
+ if (emac_control(dev,
+ EMAC_IOCTL_TYPE2_3_FILTERING,
+ (struct emac_type2_3_addr_filter_params
+ *) &filter_params, NULL)
+ != EMAC_SUCCESS) {
+ ERR("Failed to read params");
+ return -EFAULT;
+ }
+ break;
+ }
+ /* read PHY register via MII interface */
+ case EMAC_PRIV_MII_READ:
+ {
+ struct emac_phy_params phy_params;
+ unsigned long irq_flags;
+
+ /* copy user data into local variable */
+ if (copy_from_user((char *)&phy_params,
+ (char *)priv_ioctl.data,
+ sizeof(struct emac_phy_params)))
+ return -EFAULT;
+
+ /* make sure this function does not
+ *clash with mii access during tick
+ *function */
+ local_irq_save(irq_flags);
+
+ if (emac_control(dev, EMAC_IOCTL_READ_PHY_REG,
+ (void *)&phy_params,
+ NULL) != EMAC_SUCCESS) {
+ ERR("Failed to read params");
+ return -EFAULT;
+ }
+
+ /* copy the local data to user space */
+ if (copy_to_user((char *)priv_ioctl.data,
+ (char *)&phy_params,
+ sizeof(struct emac_phy_params)))
+ return -EFAULT;
+
+ /* enable tick timer to access phy now
+ if required */
+ local_irq_restore(irq_flags);
+ }
+ break;
+ /* write PHY register via MII interface */
+ case EMAC_PRIV_MII_WRITE:
+ {
+ struct emac_phy_params phy_params;
+ unsigned long irq_flags;
+
+ /* copy user data into local variable */
+ if (copy_from_user((char *)&phy_params,
+ (char *)priv_ioctl.data,
+ sizeof(struct emac_phy_params)))
+ return -EFAULT;
+
+ /* make sure this function does not
+ clash with mii access during tick
+ function */
+ local_irq_save(irq_flags);
+
+ if (emac_control(dev, EMAC_IOCTL_WRITE_PHY_REG,
+ (void *)&phy_params,
+ NULL) != EMAC_SUCCESS) {
+ ERR("Failed to read params");
+ return -EFAULT;
+ }
+
+ /* enable tick timer to access phy now
+ if required */
+ local_irq_restore(irq_flags);
+ }
+ break;
+ /* get statistics */
+ case EMAC_PRIV_GET_STATS:
+ {
+ struct emac_hw_statistics stats;
+
+ /* Caller provides memory for HW stats
+ structure via "data" pointer */
+ if (emac_control(dev, EMAC_IOCTL_GET_STATISTICS,
+ (void *)&stats, NULL) != EMAC_SUCCESS) {
+ ERR("Failed to get statistics");
+ return EMAC_INTERNAL_FAILURE;
+ }
+
+ /* copy the local data to user space */
+ if (copy_to_user
+ ((char *)priv_ioctl.data, (char *)&stats,
+ sizeof(struct emac_hw_statistics)))
+ return -EFAULT;
+ break;
+ }
+ /* clear statistics */
+ case EMAC_PRIV_CLR_STATS:
+ if (emac_control(dev,
+ EMAC_IOCTL_CLR_STATISTICS,
+ NULL, NULL)
+ != EMAC_SUCCESS) {
+ ERR("Failed to clear statistics");
+ return EMAC_INTERNAL_FAILURE;
+ }
+ break;
+ default:
+ return -EFAULT;
+ break;
+ }
+ }
+
+ else if (cmd == SIOTIMIB2) {
+ struct ti_snmp_cmd_t ti_snmp_cmd;
+
+ /* now copy the user data */
+ if (copy_from_user
+ ((char *)&ti_snmp_cmd, (char *)rq->ifr_data,
+ sizeof(struct ti_snmp_cmd_t)))
+ return -EFAULT;
+
+ switch (ti_snmp_cmd.cmd) {
+ case TI_SIOCGINTFCOUNTERS:
+ {
+ struct mib2_if_counters mib_counter;
+
+ /* Caller provides memory for HW stats
+ structure via "data" pointer */
+ if (emac_control(dev,
+ EMAC_IOCTL_IF_COUNTERS,
+ (void *)&mib_counter, NULL)
+ != EMAC_SUCCESS) {
+ ERR("Failed to get statistics");
+ return EMAC_INTERNAL_FAILURE;
+ }
+
+ /* copy the local data to user space */
+ if (copy_to_user
+ ((char *)ti_snmp_cmd.data,
+ (char *)&mib_counter,
+ sizeof(struct mib2_if_counters)))
+ return -EFAULT;
+ break;
+ }
+ case TI_SIOCGINTFPARAMS:
+ {
+ struct mib2_if_params local_params;
+
+ local_params.if_speed = dev->link_speed;
+ local_params.if_high_speed =
+ (local_params.if_speed) / 1000000;
+ local_params.if_oper_status =
+ ((netdev->
+ flags & IFF_UP) ? MIB2_STATUS_UP :
+ MIB2_STATUS_DOWN);
+ local_params.if_promiscuous_mode =
+ ((netdev->
+ flags & IFF_PROMISC) ? TRUE : FALSE);
+
+ /* now copy the counters to the user data */
+ if (copy_to_user
+ ((char *)ti_snmp_cmd.data,
+ (char *)&local_params,
+ sizeof(struct mib2_if_params)))
+ return -EFAULT;
+ }
+ break;
+
+ case TI_SIOCGETHERCOUNTERS:
+ {
+ struct mib2_phy_counters phy_counter;
+
+ /* Caller provides memory for HW stats
+ structure via "data" pointer */
+ if (emac_control(dev,
+ EMAC_IOCTL_ETHER_COUNTERS,
+ (void *)&phy_counter, NULL)
+ != EMAC_SUCCESS) {
+ ERR("Failed to get statistics");
+
+ return EMAC_INTERNAL_FAILURE;
+ }
+
+ /* copy the local data to user space */
+ if (copy_to_user
+ ((char *)ti_snmp_cmd.data,
+ (char *)&phy_counter,
+ sizeof(struct mib2_phy_counters)))
+ return -EFAULT;
+ break;
+ }
+ case TI_SIOCGETHERPARAMS:
+ {
+ struct mib2_eth_params local_params;
+
+ local_params.eth_duplex_status =
+ ((dev->link_mode ==
+ PHY_DUPLEX_FULL) ?
+ MIB2_FULL_DUPLEX : MIB2_HALF_DUPLEX);
+
+ /* now copy the counters to the user data */
+ if (copy_to_user
+ ((char *)ti_snmp_cmd.data,
+ (char *)&local_params,
+ sizeof(struct mib2_eth_params)))
+ return -EFAULT;
+ break;
+ }
+ default:
+ return -EFAULT;
+ }
+ } else
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * HASH SUPPORT FUNCTIONS
+ */
+
+/* get hash value using mechainsm in specs */
+static u32 hash_get(u8 *addr)
+{
+ register u8 tmpval;
+ register int cnt;
+ register u32 hash = 0;
+
+ for (cnt = 0; cnt < 2; cnt++) {
+ tmpval = *addr++;
+ hash ^= (tmpval >> 2) ^ (tmpval << 4);
+ tmpval = *addr++;
+ hash ^= (tmpval >> 4) ^ (tmpval << 2);
+ tmpval = *addr++;
+ hash ^= (tmpval >> 6) ^ (tmpval);
+ }
+
+ return (hash & 0x3F);
+}
+
+/**
+ * Hash Table Add
+ *- Adds mac address to hash table and upates hash bits in hardware
+ *- Returns negative if error, 0 if no change to registers, >0 if
+ * hash registers need to change
+ */
+static int hash_add(struct emac_dev_s *_dev, u8 *mac_address)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 hash_value;
+ u32 hash_bit;
+ u32 status = 0;
+ hash_value = hash_get(mac_address);
+
+ if (hash_value >= EMAC_NUM_MULTICAST_BITS) {
+ LOGERR("Invalid Hash Value=%d. Should not be greater than %d",
+ hash_value, (EMAC_NUM_MULTICAST_BITS - 1));
+ return EMAC_INVALID_PARAM;
+ }
+
+ /* set the hash bit only if not previously set */
+ if (dev->multicast_hash_cnt[hash_value] == 0) {
+ status = 1;
+ if (hash_value < 32) {
+ hash_bit = (1 << hash_value);
+ dev->mac_hash1 |= hash_bit;
+ } else {
+ hash_bit = (1 << (hash_value - 32));
+ dev->mac_hash2 |= hash_bit;
+ }
+ }
+
+ /* increment counter to maintain number of multicast address
+ that map to this hash bit */
+ ++dev->multicast_hash_cnt[hash_value];
+
+ return status;
+}
+
+/**
+ * Hash Table Del
+ *- Deletes a mac address from hash table and updates hash register bits
+ *- Returns negative if error, 0 if no change to registers, >0 if
+ * hash registers need to change
+ */
+static int hash_del(struct emac_dev_s *_dev, u8 *mac_address)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 hash_value;
+ u32 hash_bit;
+
+ hash_value = hash_get(mac_address);
+ if (dev->multicast_hash_cnt[hash_value] > 0)
+ /* decrement counter to reduce number of multicast
+ *address that map to this hash bit */
+ --dev->multicast_hash_cnt[hash_value];
+
+ /* if counter still > 0, at least one multicast address refers
+ *to this hash bit. so return 0 */
+ if (dev->multicast_hash_cnt[hash_value] > 0)
+ return 0;
+
+ if (hash_value < 32) {
+ hash_bit = (1 << hash_value);
+ dev->mac_hash1 &= ~hash_bit;
+ } else {
+ hash_bit = (1 << (hash_value - 32));
+ dev->mac_hash2 &= ~hash_bit;
+ }
+
+ /* return 1 to indicate change in mac_hash registers reqd */
+ return 1;
+}
+
+/**
+ * updates hash register bits with single multicast address add/delete
+ * operation
+ */
+static void emac_single_multi(struct emac_dev_s *_dev,
+ enum emac_single_multi_oper oper, u8 *addr)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ int status = -1;
+
+ switch (oper) {
+ case EMAC_MULTICAST_ADD:
+ status = hash_add(_dev, addr);
+ break;
+ case EMAC_MULTICAST_DEL:
+ status = hash_del(_dev, addr);
+ break;
+ default:
+ LOGERR("Unhandled Single Multicast operation %d", oper);
+ break;
+ }
+
+ /* write to the hardware only if the register status chances */
+ if (status > 0) {
+ davinci_writel(dev->mac_hash1, dev->emac_regs_base
+ + EMAC_MAC_HASH1_REG);
+ davinci_writel(dev->mac_hash2, dev->emac_regs_base
+ + EMAC_MAC_HASH2_REG);
+ }
+}
+
+/**
+ * updates hash register bits for all multi operation (set/clear)
+ */
+static void emac_all_multi(struct emac_dev_s *_dev,
+ enum emac_all_multi_oper oper)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ switch (oper) {
+ case EMAC_ALL_MULTI_SET:
+ dev->mac_hash1 = EMAC_ALL_MULTI_REG_VALUE;
+ dev->mac_hash2 = EMAC_ALL_MULTI_REG_VALUE;
+ break;
+ case EMAC_ALL_MULTI_CLR:
+ dev->mac_hash1 = 0;
+ dev->mac_hash2 = 0;
+ memset(&(dev->multicast_hash_cnt[0]), 0,
+ sizeof(dev->multicast_hash_cnt[0]) * EMAC_NUM_MULTICAST_BITS);
+ break;
+ default:
+ LOGERR("Unhandled All multi operation %d", oper);
+ break;
+ }
+
+ davinci_writel(dev->mac_hash1, dev->emac_regs_base
+ + EMAC_MAC_HASH1_REG);
+ davinci_writel(dev->mac_hash2, dev->emac_regs_base
+ + EMAC_MAC_HASH2_REG);
+}
+
+/*
+ * PHY related functions
+ */
+
+/**
+ * Cpmac Update Phy Status - updates phy status variables in
+ * hDDC->status "CpmacDDCStatus" structure
+ */
+static int emac_update_phy_status(struct emac_dev_s *_dev)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 set_phy_mode;
+
+ LOGMSG(EMAC_DEBUG_BUSY_FUNCTION_ENTRY, "");
+
+ /* verify proper device state */
+ if (dev->drv_state != DRV_OPENED) {
+ LOGERR("Device NOT Open");
+ return EMAC_ERR_DEV_NOT_OPEN;
+ }
+
+ set_phy_mode = dev->init_cfg.phy_mode;
+
+ /* no phy condition */
+ if (set_phy_mode & SNWAY_NOPHY) {
+ /* no phy condition, always linked */
+ dev->status.phy_linked = 1;
+ dev->status.phy_speed = 100;
+ dev->status.phy_duplex = PHY_DUPLEX_FULL;
+ dev->status.phy_num = 0xFFFFFFFF; /* no phy */
+ dev->mac_control |= (1 << EMAC_MACCONTROL_FULLDUPLEXEN_SHIFT);
+
+ /* write mac control register from stored value */
+ davinci_writel(dev->mac_control, dev->emac_regs_base
+ + EMAC_MAC_CONTROL_REG);
+ goto emac_update_phy_status_exit;
+ }
+
+ /* if loopback set in hardware, set link to ON */
+ if (dev->mac_control & EMAC_MACCONTROL_LOOPBKEN_MASK) {
+ dev->status.phy_linked = 1;
+ goto emac_update_phy_status_exit;
+ }
+ if (set_phy_mode & SNWAY_LPBK)
+ dev->status.phy_linked = emac_mdio_is_loopback();
+ else
+ dev->status.phy_linked = emac_mdio_is_linked();
+
+ if (dev->status.phy_linked) {
+ /* retreive duplex and speed and the phy number */
+ if (set_phy_mode & SNWAY_LPBK)
+ dev->status.phy_duplex = PHY_DUPLEX_FULL;
+ else
+ dev->status.phy_duplex = emac_mdio_get_duplex();
+ dev->status.phy_speed = emac_mdio_get_speed();
+ dev->status.phy_num = emac_mdio_get_phy_num();
+
+ /* set the duplex bit in maccontrol */
+ if (dev->status.phy_duplex == PHY_DUPLEX_FULL)
+ dev->mac_control |=
+ (1 << EMAC_MACCONTROL_FULLDUPLEXEN_SHIFT);
+ else
+ dev->mac_control &=
+ ~(1 << EMAC_MACCONTROL_FULLDUPLEXEN_SHIFT);
+
+ }
+
+ /* write mac control register from stored value */
+ davinci_writel(dev->mac_control, dev->emac_regs_base
+ + EMAC_MAC_CONTROL_REG);
+
+emac_update_phy_status_exit:
+ LOGMSG(EMAC_DEBUG_PORT_UPDATE,
+ "MacControl=%08X, Status: Phy=%d, Speed=%s, Duplex=%s",
+ dev->mac_control, dev->status.phy_num,
+ (dev->status.phy_speed == 100) ? "100" : "10",
+ (dev->status.phy_duplex == PHY_DUPLEX_FULL) ? "Full" : "Half");
+ LOGMSG(EMAC_DEBUG_BUSY_FUNCTION_EXIT, "");
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * set phy mode
+ */
+static int emac_set_phy_mode(struct emac_dev_s *_dev)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 set_phy_mode;
+ u32 phy_mode;
+
+ LOGMSG(EMAC_DEBUG_BUSY_FUNCTION_ENTRY, "");
+
+ /* verify proper device state */
+ if (dev->drv_state != DRV_OPENED) {
+ LOGERR("Device NOT Open");
+ return EMAC_ERR_DEV_NOT_OPEN;
+ }
+
+ set_phy_mode = dev->init_cfg.phy_mode;
+ phy_mode = 0;
+ if (set_phy_mode & SNWAY_AUTO)
+ phy_mode |= NWAY_AUTO;
+ if (set_phy_mode & SNWAY_FD10)
+ phy_mode |= NWAY_FD10;
+ if (set_phy_mode & SNWAY_FD100)
+ phy_mode |= NWAY_FD100;
+ if (set_phy_mode & SNWAY_HD10)
+ phy_mode |= NWAY_HD10;
+ if (set_phy_mode & SNWAY_HD100)
+ phy_mode |= NWAY_HD100;
+ if (set_phy_mode & SNWAY_LPBK)
+ phy_mode |= NWAY_LPBK;
+ if (set_phy_mode & SNWAY_AUTOMDIX)
+ phy_mode |= NWAY_AUTOMDIX;
+ /* check for EMAC bus frequency for correct speed operation */
+ if ((set_phy_mode & SNWAY_FD10) || (set_phy_mode & SNWAY_HD10)) {
+ if (dev->init_cfg.emac_bus_frequency <=
+ EMAC_MIN_FREQUENCY_FOR_10MBPS)
+ LOGERR("Bus speedemacSetPhyMode: CpmacFreq(%d) "
+ "is less than required %d freq for "
+ "10Mbps support. CANNOT SUPPORT 10Mbps",
+ dev->init_cfg.emac_bus_frequency,
+ EMAC_MIN_FREQUENCY_FOR_10MBPS);
+ }
+
+ else if ((set_phy_mode & SNWAY_FD100) || (set_phy_mode & SNWAY_HD100)) {
+ if (dev->init_cfg.emac_bus_frequency <=
+ EMAC_MIN_FREQUENCY_FOR_100MBPS)
+
+ LOGERR("freq(%d) is less than required %d freq for "
+ "100Mbps support. CANNOT SUPPORT 100Mbps",
+ dev->init_cfg.emac_bus_frequency,
+ EMAC_MIN_FREQUENCY_FOR_100MBPS);
+ }
+
+ /* TODO: check for gigabit mode when PHY mode defines for
+ *gigabit are available */
+ LOGMSG(EMAC_DEBUG_PORT_UPDATE,
+ "MdioPhyMode=%08X, PhyMode=%08d, Auto:%d, FD10:%d, "
+ "HD10:%d, FD100:%d, HD100:%d",
+ set_phy_mode, phy_mode,
+ (phy_mode & NWAY_AUTO), (phy_mode & NWAY_FD10),
+ (phy_mode & NWAY_HD10),
+ (phy_mode & NWAY_FD100), (phy_mode & NWAY_HD100));
+ emac_mdio_set_phy_mode(phy_mode);
+ emac_update_phy_status(_dev);
+ LOGMSG(EMAC_DEBUG_BUSY_FUNCTION_EXIT, "");
+
+ return EMAC_SUCCESS;
+}
+
+/*
+ * MAC ADDRESSING MODE SUPPORT FUNCTIONS
+ */
+
+/**
+ * this function sets / clears the unicast flag in hardware
+ */
+static void emac_rx_uni_cast(struct emac_dev_s *_dev, u32 channel, bool enable)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ /* update local copy of register to save cycles in reading the
+ *register */
+ if (enable == TRUE) {
+ dev->rx_unicast_set |= (1 << channel);
+ dev->rx_unicast_clear &= ~(1 << channel);
+ } else {
+ /* disable unicast channel setting */
+ dev->rx_unicast_clear |= (1 << channel);
+ dev->rx_unicast_set &= ~(1 << channel);
+ }
+
+ /* write to hardware if device is open */
+ if (dev->drv_state == DRV_OPENED) {
+ davinci_writel(dev->rx_unicast_set, dev->emac_regs_base
+ + EMAC_RX_UNICAST_SET_REG);
+ davinci_writel(dev->rx_unicast_clear,
+ dev->emac_regs_base + EMAC_RX_UNICAST_CLEAR_REG);
+ }
+}
+
+/**
+ * EMAC Add Type 0 Address
+ * - set mac address for type 0 addressing (EMAC)
+ *
+ * This is an internal function of the DDC called from channel
+ * enable API which does channel number range checking and hence its
+ * not required. It is assumed that this function will get the
+ * correct channel number always
+ */
+static void emac_add_type0addr(struct emac_dev_s *dev, u32 channel,
+ char *mac_address)
+{
+ davinci_writel((mac_address[0] << 8) | (mac_address[1]),
+ dev->emac_regs_base + EMAC_MAC_SRC_ADDR_LO_REG);
+ davinci_writel((mac_address[2] << 24) |
+ (mac_address[3] << 16) | (mac_address[4] << 8) |
+ (mac_address[5]),
+ dev->emac_regs_base + EMAC_MAC_SRC_ADDR_HI_REG);
+ /* enable unicast */
+ emac_rx_uni_cast(dev, channel, TRUE);
+}
+
+/**
+ * EMAC Add Type 1 Address
+ * - set mac address for type 1 addressing (EMAC)
+ *
+ * This is an internal function of the DDC called from channel enable
+ * API which does channel number range checking and hence its not required.
+ * It is assumed that this function will get the correct channel number always
+ */
+static void emac_add_type1addr(struct emac_dev_s *dev, u32 channel,
+ char *mac_address)
+{
+ /* set mac_index register with channel number */
+ davinci_writel(channel, dev->emac_regs_base + EMAC_MAC_INDEX_REG);
+
+ /* set mac_addr_hi register */
+ davinci_writel((mac_address[3] << 24) | (mac_address[2] << 16) |
+ (mac_address[1] << 8) | (mac_address[0]),
+ dev->emac_regs_base + EMAC_MAC_ADDR_HI_REG);
+
+ /* set mac_addr_lo register */
+ davinci_writel(((mac_address[5] << 8) | mac_address[4]),
+ dev->emac_regs_base + EMAC_MAC_ADDR_LO_REG);
+
+ /* set mac hash */
+ davinci_writel(0, dev->emac_regs_base + EMAC_MAC_HASH1_REG);
+ davinci_writel(0, dev->emac_regs_base + EMAC_MAC_HASH2_REG);
+
+ /* As per discussion with hardware folks, it is mandatory to
+ set the source address of the mac, else correct behaviour
+ is not guaranteed */
+ emac_add_type0addr(dev, channel, mac_address);
+
+ /* enable unicast */
+ emac_rx_uni_cast(dev, channel, TRUE);
+}
+
+/**
+ * CPGMAC CFIG 2/3 type addressing - filtering
+ */
+static void emac_add_type2addr(struct emac_dev_s *_dev, u32 channel,
+ char *mac_address,
+ int index, bool valid, int match)
+{
+ /* not supported in DaVinci */
+}
+
+/*
+ * HARDWARE CONFIGURATION SUPPORT FUNCTIONS
+ */
+
+/**
+ * set RX hardware configuration
+ */
+void emac_set_rx_hw_cfg(struct emac_dev_s *_dev)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ struct emac_rx_config *rx_cfg;
+ u32 rx_mbp_enable;
+
+ if (dev->drv_state != DRV_OPENED) {
+ LOGERR("Function called when device is NOT in open state");
+ return;
+ }
+
+ rx_cfg = &dev->init_cfg.rx_cfg;
+
+ /* set RX MBP enable register */
+ rx_mbp_enable =
+ ((rx_cfg->pass_crc & 0x1) << EMAC_RXMBP_PASSCRC_SHIFT) |
+ ((rx_cfg->qos_enable & 0x1) << EMAC_RXMBP_QOSEN_SHIFT) |
+ ((rx_cfg->no_buffer_chaining & 0x1) << EMAC_RXMBP_NOCHAIN_SHIFT) |
+ ((rx_cfg->
+ copy_maccontrol_frames_enable & 0x1) << EMAC_RXMBP_CMFEN_SHIFT) |
+ ((rx_cfg->
+ copy_short_frames_enable & 0x1) << EMAC_RXMBP_CSFEN_SHIFT) |
+ ((rx_cfg->
+ copy_error_frames_enable & 0x1) << EMAC_RXMBP_CEFEN_SHIFT) |
+ ((rx_cfg->
+ promiscous_enable & 0x1) << EMAC_RXMBP_CAFEN_SHIFT) |
+ ((rx_cfg->promiscous_channel & EMAC_RXMBP_CHMASK)
+ << EMAC_RXMBP_PROMCH_SHIFT) |
+ ((rx_cfg->broadcast_enable & 0x1) << EMAC_RXMBP_BROADEN_SHIFT)|
+ ((rx_cfg->broadcast_channel & EMAC_RXMBP_CHMASK) <<
+ EMAC_RXMBP_BROADCH_SHIFT) |
+ ((rx_cfg->multicast_enable & 0x1) << EMAC_RXMBP_MULTIEN_SHIFT)|
+ ((rx_cfg->multicast_channel & EMAC_RXMBP_CHMASK) <<
+ EMAC_RXMBP_MULTICH_SHIFT);
+
+ if (rx_cfg->promiscous_enable) {
+ /* disable mcast bcast and unicast: H/W limitation */
+ rx_mbp_enable &= ~(0x1 << EMAC_RXMBP_BROADEN_SHIFT);
+ rx_mbp_enable &= ~(0x1 << EMAC_RXMBP_MULTIEN_SHIFT);
+
+ /* disable unicast - warning!! assuming only one
+ *channel open */
+ emac_rx_uni_cast(_dev, (dev->rx_cppi[0])->ch_info.ch_num,
+ FALSE);
+ } else {
+ /* enable unicast - warning!! assuming only one
+ *channel open */
+ emac_rx_uni_cast(_dev, (dev->rx_cppi[0])->ch_info.ch_num, TRUE);
+ }
+
+ if (dev->rx_MBP_enable != rx_mbp_enable) {
+ dev->rx_MBP_enable = rx_mbp_enable;
+ davinci_writel(rx_mbp_enable, dev->emac_regs_base
+ + EMAC_RX_MBP_ENABLE_REG);
+ }
+
+ /* set max rx packet length */
+ davinci_writel((rx_cfg->max_rx_pkt_length & EMAC_RX_MAX_LEN_MASK),
+ dev->emac_regs_base + EMAC_RX_MAXLEN_REG);
+
+ /* set rx buffer offset */
+ davinci_writel((rx_cfg->buffer_offset & EMAC_RX_BUFFER_OFFSET_MASK),
+ dev->emac_regs_base + EMAC_RX_BUFFER_OFFSET_REG);
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT,
+ "Rx_MBP_Enable = 0x%08x\n", rx_mbp_enable);
+}
+
+/**
+ * set MAC configuration - MACControl register
+ */
+static void emac_set_mac_hw_cfg(struct emac_dev_s *_dev)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ struct emac_mac_config *mac_cfg;
+ u32 mac_control;
+
+ if (dev->drv_state != DRV_OPENED) {
+ LOGERR("Function called when device is NOT in open state");
+ return;
+ }
+
+ mac_cfg = &dev->init_cfg.mac_cfg;
+ mac_control =
+ ((mac_cfg->
+ tx_short_gap_enable & 0x1) << EMAC_MACCONTROL_TXSHORTGAPEN_SHIFT)
+ | (((mac_cfg->p_type == EMAC_TXPRIO_FIXED) ? 0x1 : 0) <<
+ EMAC_MACCONTROL_TXPTYPE_SHIFT)
+ | ((mac_cfg->giga_bit_enable & 0x1) <<
+ EMAC_MACCONTROL_GIGABITEN_SHIFT) | ((mac_cfg->
+ tx_pacing_enable & 0x1) <<
+ EMAC_MACCONTROL_TXPACEEN_SHIFT)
+ |
+ /* THIS LINE FOR REFERENCE ONLY ((mac_cfg->mii_enable & 0x1)
+ << EMAC_MACCONTROL_MIIEN_SHIFT) | */
+ (dev->mac_control & EMAC_MACCONTROL_MIIEN_MASK) |
+ ((mac_cfg->
+ tx_flow_enable & 0x1) << EMAC_MACCONTROL_TXFLOWEN_SHIFT) |
+ ((mac_cfg->
+ rx_flow_enable & 0x1) << EMAC_MACCONTROL_RXFLOWEN_SHIFT) |
+ ((mac_cfg->
+ loopback_enable & 0x1) << EMAC_MACCONTROL_LOOPBKEN_SHIFT) |
+ (dev->mac_control & EMAC_MACCONTROL_FULLDUPLEXEN_MASK);
+
+ if (dev->mac_control != mac_control) {
+ dev->mac_control = mac_control;
+ davinci_writel(mac_control, dev->emac_regs_base
+ + EMAC_MAC_CONTROL_REG);
+ }
+}
+
+/**
+ * EMAC Init
+ * - validates max TX/RX channels and stores initial configuration
+ *
+ * Initial configuration passed by via the "init_cfg" parameter
+ */
+static int emac_init(struct emac_dev_s *_dev, struct emac_init_config
*init_cfg)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "");
+
+ /* validate num_tx and num_rx channels */
+ if ((init_cfg->num_tx_channels > EMAC_MAX_TX_CHANNELS) ||
+ (init_cfg->num_rx_channels > EMAC_MAX_RX_CHANNELS)) {
+ LOGERR("Invalid number of TX/RX channels");
+ return EMAC_INVALID_PARAM;
+ }
+
+ /* save config info for later use */
+ dev->init_cfg = *init_cfg; /* structure copy */
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "");
+ return EMAC_SUCCESS;
+}
+
+/**
+ * EMAC DDC DeInit
+ * Stub function - no functionality required as per this implementation
+ */
+static int emac_de_init(struct emac_dev_s *dev, void *param)
+{
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "");
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "");
+
+ return EMAC_SUCCESS;
+}
+
+
+/**
+ * EMAC DDC Open
+ * - Brings module out of reset
+ * - Open's CSL, programs mandatory hardware init registers
+ * - Open's MII_MDIO module and enable poll timer via DDA
+ * - Enables earlier created TX/RX channels
+ * - Enables TX/RX operation in hardware
+ *
+ *"param" not used in this implementation
+ */
+static int emac_open(struct emac_dev_s *_dev, void *param)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 channel;
+ u32 mii_mod_id, mii_rev_maj, mii_rev_min;
+ int ret_val;
+ struct emac_init_config *init_cfg;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "");
+
+ /* NOTE: what about synchronization? */
+ /* NOTE: what about multiinstance? */
+ if (dev->drv_state == DRV_OPENED) {
+ LOGERR("Device already open");
+ return EMAC_ERR_DEV_ALREADY_OPEN;
+ }
+
+ /* get init config info structure pointer for easy access */
+ init_cfg = &dev->init_cfg;
+
+ /* set the BD memory pointer */
+ emac_wrapper_ptr = EMAC_WRAPPER_RAM_ADDR;
+
+ /* bring EMAC out of reset - for clean implementation, reset
+ *and then unreset the module */
+ /* for EMAC 2.6 and beyond, reset is internal to the module */
+ davinci_writel(1, dev->emac_regs_base + EMAC_SOFT_RESET_REG);
+
+ /* wait for reset to complete - do nothing */
+ while (davinci_readl(dev->emac_regs_base + EMAC_SOFT_RESET_REG));
+
+ for (channel = 0; channel < EMAC_MAX_TX_CHANNELS; channel++) {
+ /* program TX/RX HDP's to 0 */
+ davinci_writel(0, dev->emac_regs_base +
+ EMAC_TX_HDP_REG(channel));
+
+ /* initialize the completion pointers to 0 */
+ davinci_writel(0, dev->emac_regs_base
+ + EMAC_TX_CP_REG(channel));
+ }
+
+ for (channel = 0; channel < EMAC_MAX_RX_CHANNELS; channel++) {
+ davinci_writel(0, dev->emac_regs_base
+ + EMAC_RX_HDP_REG(channel));
+
+ /* initialize the completion pointers to 0 */
+ davinci_writel(0, dev->emac_regs_base
+ + EMAC_RX_CP_REG(channel));
+ }
+
+ /* enable TX/RX DMA */
+ davinci_orl(EMAC_TX_CONTROL_TX_ENABLE_VAL,
+ dev->emac_regs_base + EMAC_TX_CONTROL_REG);
+ davinci_orl(EMAC_RX_CONTROL_RX_ENABLE_VAL,
+ dev->emac_regs_base + EMAC_RX_CONTROL_REG);
+
+ /* enable adapter check interrupts - disable stats interupt */
+ davinci_writel(EMAC_MAC_HOST_ERR_INTMASK_VAL,
+ dev->emac_regs_base + EMAC_MAC_INT_MASK_SET_REG);
+ /* set device state - opened - useful when opening channels */
+ dev->drv_state = DRV_OPENED;
+
+ /* set the mac_control register */
+ emac_set_mac_hw_cfg(_dev);
+
+ /* start MDIO autonegotiation and set phy mode */
+ emac_mdio_get_ver(init_cfg->mdio_base_address,
+ &mii_mod_id, &mii_rev_maj, &mii_rev_min);
+
+ LOGMSG(EMAC_DEBUG_PORT_UPDATE,
+ "MII Module Id=%d, MII Base Address=%08X, Major Rev=%d, "
+ "Minor Rev=%d",
+ mii_mod_id, init_cfg->mdio_base_address,
+ mii_rev_maj, mii_rev_min);
+
+ /* no failure code returned from this function */
+ emac_mdio_init(init_cfg->mdio_base_address,
+ dev->init_cfg.inst_id,
+ init_cfg->phy_mask,
+ init_cfg->MLink_mask,
+ init_cfg->mdio_bus_frequency,
+ init_cfg->mdio_clock_frequency,
+ (emac_debug & EMAC_DEBUG_MII));
+
+ /* set the PHY to a given mode - as per config parameters and
+ *update DDA layer */
+ emac_set_phy_mode(_dev);
+
+ emac_control_cb(dev,
+ EMAC_IOCTL_STATUS_UPDATE, (void *)&dev->status, NULL);
+
+ /* start the tick timer via DDA */
+ emac_control_cb(dev,
+ EMAC_IOCTL_TIMER_START,
+ (void *)init_cfg->mdio_tick_msec, NULL);
+
+ /* enable opened TX channels */
+ for (channel = 0; channel < EMAC_MAX_TX_CHANNELS; channel++) {
+ if (dev->tx_cppi[channel] != NULL) {
+ ret_val =
+ emac_enable_channel(_dev, channel, NET_CH_DIR_TX);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR("Error enabling TX channel %d", channel);
+
+ /* TODECIDE: should we return from
+ *here or continue enabling other
+ *channels */
+ return ret_val;
+ }
+ }
+ }
+
+ /* set filter low threshold - not supported, hence set to 0 */
+ davinci_writel(0, dev->emac_regs_base + EMAC_RX_FILTER_LOW_THRESH_REG);
+
+ /* disable unicast on all channels first - enabled if channel
+ *is configured & enabled below */
+ davinci_writel(EMAC_RX_UNICAST_CLEAR_ALL,
+ dev->emac_regs_base + EMAC_RX_UNICAST_CLEAR_REG);
+
+ /* set MAC hash register */
+ davinci_writel(dev->mac_hash1,
+ dev->emac_regs_base + EMAC_MAC_HASH1_REG);
+ davinci_writel(dev->mac_hash2,
+ dev->emac_regs_base + EMAC_MAC_HASH2_REG);
+
+ /* RX MBP, RX pkt length and RX buffer offset registers taken
+ *care by this function */
+ emac_set_rx_hw_cfg(_dev);
+
+ /* read RX address matching/filtering type (0/1/2) */
+ dev->rx_addr_type =
+ (davinci_readl(dev->emac_regs_base + EMAC_MAC_CFIG_REG) >> 8) & 0xFF;
+
+ /* enable opened RX channels */
+ for (channel = 0; channel < EMAC_MAX_RX_CHANNELS; channel++) {
+ if (dev->rx_cppi[channel] != NULL) {
+ ret_val =
+ emac_enable_channel(_dev, channel, NET_CH_DIR_RX);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR("Error enabling RX channel %d", channel);
+
+ /* TODECIDE: should we return from
+ *here or continue enabling other
+ *channels */
+ return ret_val;
+ }
+ }
+
+ /* since flow threshold and free buffer feature is not
+ *supported, set it to 0 */
+ davinci_writel(0,
+ dev->emac_regs_base + EMAC_RX_FLOW_THRESH_REG(channel));
+ davinci_writel(0,
+ dev->emac_regs_base + EMAC_RX_FREE_BUFFER_REG(channel));
+ }
+
+ /* finally set MAC control register, enable MII */
+ dev->mac_control |= (1 << EMAC_MACCONTROL_MIIEN_SHIFT);
+ davinci_writel(dev->mac_control,
+ dev->emac_regs_base + EMAC_MAC_CONTROL_REG);
+
+ /* start the MIB cnt tick timer via DDA */
+ emac_control_cb(dev,
+ EMAC_IOCTL_MIB64_CNT_TIMER_START,
+ (void *)init_cfg->mib64cnt_msec, NULL);
+
+ /* enable interrupts via module control (wrapper) */
+ davinci_writel(1, dev->emac_wrap_regs_base + EMAC_WRAP_EWCTL_REG);
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "");
+ return EMAC_SUCCESS;
+}
+
+/**
+ * EMAC DDC Close
+ * - Disables poll timer via DDA
+ * - Disable and Close all open TX/RX channels
+ * - Closes CSL
+ * - Puts module in reset
+ *
+ *"param" not used in this implementation
+ */
+static int emac_close(struct emac_dev_s *_dev, void *param)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ int ret_val;
+ int err_val = EMAC_SUCCESS;
+ u32 channel;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "");
+
+ if (dev->drv_state == DRV_CLOSED) {
+ LOGERR("Device already closed");
+ return EMAC_ERR_DEV_ALREADY_CLOSED;
+ }
+
+ /* stop the tick timer via DDA */
+ emac_control_cb(dev, EMAC_IOCTL_TIMER_STOP, NULL, NULL);
+
+ /* stop the mib timer via DDA */
+ emac_control_cb(dev, EMAC_IOCTL_MIB64_CNT_TIMER_STOP, NULL, NULL);
+ /* close TX channels */
+ for (channel = 0; channel < EMAC_MAX_TX_CHANNELS; channel++) {
+ if (dev->tx_cppi[channel] != NULL) {
+ ret_val =
+ emac_ch_close(_dev, channel, NET_CH_DIR_TX, NULL);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR("Error closing TX channel %d", channel);
+
+ /* instead of returning immediatley on
+ *error, we close all possible
+ *channels */
+ err_val = ret_val;
+ }
+ }
+ }
+
+ /* close RX channels */
+ for (channel = 0; channel < EMAC_MAX_RX_CHANNELS; channel++) {
+ if (dev->rx_cppi[channel] != NULL) {
+ ret_val =
+ emac_ch_close(_dev, channel, NET_CH_DIR_RX, NULL);
+
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR("Error closing RX channel %d", channel);
+
+ /* instead of returning immediatley on
+ *error, we close all possible
+ *channels */
+ err_val = ret_val; /* return ret_val; */
+ }
+ }
+ }
+
+ /* disable interrupts via module control (wrapper) */
+ davinci_writel(0, dev->emac_wrap_regs_base + EMAC_WRAP_EWCTL_REG);
+
+ /* put EMAC in reset */
+ davinci_writel(1, dev->emac_regs_base + EMAC_SOFT_RESET_REG);
+ /* NOTE: we really do not need to wait for soft reset? */
+
+ /* put MDIO in reset - not required for davinci */
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "");
+
+ /* closed all channels successfully. mark the DDC as closed */
+ if (err_val == EMAC_SUCCESS)
+ dev->drv_state = DRV_CLOSED;
+
+ return err_val;
+}
+
+/**
+ * EMAC DDC Ioctl
+ * - Get Software (DDC) and Hardware Versions
+ * - Set/Modify RX and MAC configuration
+ * - Get DDC/module status
+ * - Read/Write MII registers (via PHY)
+ * - Get/Clr Statistics (hardware counters)
+ * - Add/Del/ Multicast operations AllMulti Set/Clear operations
+ * - Type2/3 Filtering operation
+ *
+ *"param" not used in this implementation
+ */
+int emac_control(struct emac_dev_s *_dev, int cmd, void *cmd_arg,
+ void *param)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ /* sanity check */
+ if (dev->drv_state != DRV_OPENED) {
+ LOGERR("ioctl called when device is NOT open");
+ return EMAC_ERR_DEV_NOT_OPEN;
+ }
+
+ switch (cmd) {
+ case EMAC_IOCTL_GET_HWVER:
+ /* read hardware versions only if device is in open state
+ * cmd is a ptr to an integer that will contain Tx Id
+ * ver and param is a pointer to an integer that will
+ * contain rx id ver after this call
+ */
+ if (dev->drv_state == DRV_OPENED) {
+ *((u32 *) cmd_arg) = davinci_readl(dev->emac_regs_base
+ + EMAC_TX_IDVER_REG);
+ *((u32 *) param) = davinci_readl(dev->emac_regs_base
+ + EMAC_TX_IDVER_REG);
+ } else
+ return EMAC_ERR_DEV_NOT_OPEN;
+ break;
+ case EMAC_IOCTL_SET_RXCFG:
+ /*
+ * rx configuration structure passed in structure
+ * pointed by cmd_arg, params not used
+ */
+ if (cmd_arg != NULL) {
+ dev->init_cfg.rx_cfg =
+ *((struct emac_rx_config *) cmd_arg);
+ emac_set_rx_hw_cfg(_dev);
+ } else
+ return EMAC_INVALID_PARAM;
+ break;
+ case EMAC_IOCTL_SET_MACCFG:
+ /*
+ * mac configuration structure passed in a structure
+ * pointed by cmd_arg, params not used
+ */
+ if (cmd_arg != NULL) {
+ dev->init_cfg.mac_cfg =
+ *((struct emac_mac_config *) cmd_arg);
+ emac_set_mac_hw_cfg(_dev);
+ } else
+ return EMAC_INVALID_PARAM;
+ break;
+ case EMAC_IOCTL_GET_STATUS:
+ /*
+ * returns struct emac_status structure back in cmd_arg
+ * pointer pointed structur
+ */
+ {
+ struct emac_status *status =
+ (struct emac_status *) cmd_arg;
+ *status = dev->status; /* structure copy */
+ }
+ break;
+ case EMAC_IOCTL_READ_PHY_REG:
+ /*
+ * cmd = pointer to CpmacPhyParams struct. data read
+ * back into "data" parameter in the structure
+ */
+ {
+ /* \warning: Read to the phy registers - Note
+ that this code loops on a completion bit in
+ the phy so there are chances of hanging" */
+ struct emac_phy_params *phy_params =
+ (struct emac_phy_params *) cmd_arg;
+
+ phy_params->data = emac_mdio_read(phy_params->phy_num,
+ phy_params->reg_addr);
+ }
+ break;
+ case EMAC_IOCTL_WRITE_PHY_REG:
+ /*
+ * cmd = pointer to CpmacPhyParams struct. data to be
+ * written is in "data" parameter in the structure
+ */
+ {
+ struct emac_phy_params *phy_params =
+ (struct emac_phy_params *) cmd_arg;
+
+ /*
+ * \warning: Write to the phy registers - Note
+ * that this code loops on a completion bit in
+ * the phy so there are chances of hanging"
+ */
+ emac_mdio_write(phy_params->reg_addr,
+ phy_params->phy_num, phy_params->data);
+ }
+ break;
+ case EMAC_IOCTL_GET_STATISTICS:
+ /*
+ * cmd_arg points to the user provided structure for
+ * statistics which match with hardware 36 regs, param
+ * is not used
+ */
+ {
+ u32 cnt;
+ u32 *user_stats = (u32 *) cmd_arg;
+
+ for (cnt = 0; cnt < EMAC_NUM_STAT_REGS;
+ cnt++, user_stats++)
+ * user_stats = davinci_readl(dev->emac_regs_base
+ + EMAC_RX_GOOD_FRAMES_REG + cnt * 4);
+ }
+
+ break;
+ case EMAC_IOCTL_CLR_STATISTICS:
+ /*
+ * cmd_arg or param is not used
+ */
+ {
+ u32 cnt;
+ for (cnt = 0; cnt < EMAC_NUM_STAT_REGS; cnt++) {
+ davinci_writel(EMAC_STAT_CLEAR,
+ dev->emac_regs_base
+ + EMAC_RX_GOOD_FRAMES_REG + cnt * 4);
+ /*addr = EMAC_STAT_CLEAR; */
+ /* 0xFFFFFFFF value */
+ }
+ emac_ddcifcnt_clear(_dev);
+ }
+ break;
+ case EMAC_IOCTL_MULTICAST_ADDR:
+ /*
+ * cmd_arg= emac_multicast_oper enum, param = pointer
+ * to multicast address - u8
+ */
+ {
+ u8 *addr = (u8 *) param;
+ emac_single_multi(_dev,
+ (enum emac_single_multi_oper) cmd_arg,
+ addr);
+ }
+ break;
+ case EMAC_IOCTL_ALL_MULTI:
+ /*
+ * cmd_arg= enum emac_all_multi_oper enum, param=not used
+ */
+ emac_all_multi(_dev, (enum emac_all_multi_oper) cmd_arg);
+ break;
+ case EMAC_IOCTL_TYPE2_3_FILTERING:
+ {
+ /*
+ * cmd_arg = pointer to
+ * struct emac_type2_3_addr_filter_params structure,
+ * param=not used
+ */
+ struct emac_type2_3_addr_filter_params *addr_params;
+
+ addr_params =
+ (struct emac_type2_3_addr_filter_params *) cmd_arg;
+ emac_add_type2addr(_dev, addr_params->channel,
+ addr_params->mac_address,
+ addr_params->index,
+ addr_params->valid,
+ addr_params->match);
+ }
+ break;
+ case EMAC_IOCTL_SET_MAC_ADDRESS:
+ {
+ /*
+ * cmd_arg = pointer to
+ * struct emac_type2_3_addr_filter_params structure,
+ * param=not used
+ */
+ struct emac_address_params *addr_params;
+ struct emac_rx_cppi_ch_t *rx_cppi;
+ int cnt;
+
+ if (dev->drv_state != DRV_OPENED) {
+ LOGERR("EMAC_IOCTL_TYPE2_3_FILTERING Ioctl"
+ " called when device is NOT in open"
+ " state");
+ return EMAC_ERR_DEV_NOT_OPEN;
+ }
+
+ addr_params = (struct emac_address_params *) cmd_arg;
+ rx_cppi = dev->rx_cppi[addr_params->channel];
+ if (rx_cppi == NULL) {
+ LOGERR
+ ("Invalid Channel %d. RX CPPI structure NULL",
+ addr_params->channel);
+ return EMAC_ERR_RX_CH_INVALID;
+ }
+
+ for (cnt = 0; cnt < 6; cnt++)
+ rx_cppi->mac_addr[cnt] =
+ addr_params->mac_address[cnt];
+
+ /* set interface MAC address */
+ emac_set_mac_address(_dev, addr_params->channel,
+ addr_params->mac_address);
+ }
+ break;
+ case EMAC_IOCTL_IF_COUNTERS:
+ emac_ddcifcnt_updt(_dev);
+ memcpy(cmd_arg, &dev->mib2if_hccounter.mib2if_counter,
+ sizeof(struct mib2_if_counters));
+ break;
+ case EMAC_IOCTL_ETHER_COUNTERS:
+ emac_ddcphycnt(_dev, cmd_arg);
+ break;
+ case EMAC_IOCTL_IF_PARAMS_UPDT:
+ emac_ddcifcnt_updt(_dev);
+ break;
+ default:
+ LOGERR("Unhandled ioctl code %d", cmd);
+ break;
+ }
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * EMAC DDC Channel Open
+ * - Verify channel info (range checking etc)
+ * - Allocate memory for the channel
+ * - Book-keep operations for the channel - ready to be enabled in hardware
+ *
+ * 1. If DDC instance is in "Opened" state, the channel is enabled in hardware
+ * 2. "chOpenArgs" is used only for opening RX channel
+ */
+static int emac_ch_open(struct emac_dev_s *_dev, struct emac_ch_info *ch_info,
+ void *ch_open_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ int ret_val;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY,
+ "ChannelNo=%d, Dir=%s",
+ ch_info->ch_num,
+ ((ch_info->ch_dir == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ /* if the channel state is not NET_CH_UNINITIALIZED, return error */
+ if (ch_info->ch_state != NET_CH_UNINITIALIZED) {
+ LOGERR
+ ("%s channel %d should be in NET_CH_UNINITIALIZED state",
+ ((ch_info->ch_dir == NET_CH_DIR_TX) ? "TX" : "RX"),
+ ch_info->ch_num);
+ return EMAC_INVALID_PARAM;
+ }
+
+ /* init channel */
+ if (ch_info->ch_dir == NET_CH_DIR_TX) {
+ if (ch_info->ch_num >= dev->init_cfg.num_tx_channels) {
+ LOGERR
+ ("Invalid TX Channel=%d specified",
+ ch_info->ch_num);
+ return EMAC_ERR_TX_CH_INVALID;
+ }
+
+ if (dev->tx_is_created[ch_info->ch_num] == TRUE) {
+ LOGERR("TX Channel %d already open", ch_info->ch_num);
+ return EMAC_ERR_TX_CH_ALREADY_INIT;
+ }
+
+ /*
+ * allocate channel memory and perform other book-keep
+ *functions for the channel
+ */
+ ret_val = emac_init_tx_channel(_dev, ch_info, ch_open_args);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR
+ ("Error in initializing TX channel %d",
+ ch_info->ch_num);
+ return ret_val;
+ }
+ } else if (ch_info->ch_dir == NET_CH_DIR_RX) {
+ if (ch_info->ch_num >= dev->init_cfg.num_rx_channels) {
+ LOGERR
+ ("Invalid RX Channel=%d specified",
+ ch_info->ch_num);
+ return EMAC_ERR_RX_CH_INVALID;
+ }
+
+ if (dev->rx_is_created[ch_info->ch_num] == TRUE) {
+ LOGERR("RX Channel %d already open", ch_info->ch_num);
+ return EMAC_ERR_RX_CH_ALREADY_INIT;
+ }
+
+ /*
+ * allocate channel memory and perform other book-keep
+ * functions for the channel
+ */
+ ret_val = emac_init_rx_channel(_dev, ch_info, ch_open_args);
+
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR
+ ("Error in initializing RX channel %d",
+ ch_info->ch_num);
+ return ret_val;
+ }
+ }
+
+ /* if device is opened already, enable this channel for use */
+ if (dev->drv_state == DRV_OPENED) {
+ ret_val =
+ emac_enable_channel(_dev, ch_info->ch_num, ch_info->ch_dir);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR
+ ("Error enabling channel %d in %d direction",
+ ch_info->ch_num, ch_info->ch_dir);
+ return ret_val;
+ }
+ }
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT,
+ "ChannelNo=%d, Dir=%s",
+ ch_info->ch_num,
+ ((ch_info->ch_dir == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * EMAC DDC Channel Close
+ * - If DDC instance is in "Opened" state, disable the channel in hardware
+ * - Un-initialize the channel (free memory previously allocated)
+ */
+static int emac_ch_close(struct emac_dev_s *_dev, int channel,
+ int direction, void *ch_close_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ int ret_val;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY,
+ "ChannelNo=%d, Dir=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ /* disable this channel */
+ if (dev->drv_state == DRV_OPENED) {
+ ret_val = emac_disable_channel(_dev, channel, direction);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR
+ ("Error disabling channel %d in %s direction",
+ channel,
+ ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+ return ret_val;
+ }
+ }
+
+ /* un_init channel */
+ if (direction == NET_CH_DIR_TX) {
+ ret_val = emac_un_init_tx_channel(_dev, channel, ch_close_args);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR("Error in UnInit of TX channel %d", channel);
+ return ret_val;
+ }
+ }
+
+ else if (direction == NET_CH_DIR_RX) {
+ ret_val = emac_un_init_rx_channel(_dev, channel, ch_close_args);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR("Error in UnInit of TX channel %d", channel);
+ return ret_val;
+ }
+ }
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT,
+ "ChannelNo=%d, Dir=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * Init Tx Channel
+ * - Allocates memory for TX Ch Control structure, Buffer descriptors
+ * - Initialize the above data structures as per channel configuration
+ * - Chain the TX BD list ready to be given to hardware
+ *
+ * 1. "chOpenArgs" not used in this implementation
+ *
+ * 2. This function assumes that the channel number passed is valid
+ * and the hDDC->txCppi[channel] pointer is NULL. This function will
+ * not do any error check on these parameters to avoid duplicate error
+ * checks (done in caller function).
+ */
+static int emac_init_tx_channel(struct emac_dev_s *_dev,
+ struct emac_ch_info *ch_info,
+ void *ch_open_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 cnt, bd_size;
+ char *alloc_mem;
+ struct emac_tx_bd *curr_bd;
+ struct emac_tx_cppi_ch *tx_cppi = NULL;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "ChannelNo=%d", ch_info->ch_num);
+
+ /* allocate memory for TX CPPI channel and set to 0 */
+ emac_malloc(sizeof(struct emac_tx_cppi_ch), (void **)&tx_cppi);
+
+ /* update the channel control structure in DDC */
+ dev->tx_cppi[ch_info->ch_num] = tx_cppi;
+
+ /* populate channel info */
+ tx_cppi->ch_info = *ch_info; /* structure copy */
+ tx_cppi->ch_info.ch_state = NET_CH_INITIALIZED;
+ tx_cppi->active_queue_head = 0;
+ tx_cppi->active_queue_tail = 0;
+ tx_cppi->queue_active = FALSE;
+ dev->tx_teardown_pending[ch_info->ch_num] = FALSE;
+
+#ifdef EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+ /* allocate memory for TX CPPI channel on a 4 byte boundry */
+ emac_malloc((ch_info->service_max * sizeof(u32)),
+ (void **)&tx_cppi->tx_complete);
+#endif
+
+ /*
+ * allocate buffer descriptor pool align every BD on four word
+ * boundry for future requirements
+ */
+ bd_size = (sizeof(struct emac_tx_bd) + 0xF) & ~0xF;
+ tx_cppi->alloc_size = (((bd_size * ch_info->num_bd) + 0xF) & ~0xF);
+
+ /* alloc TX BD memory */
+ tx_cppi->bd_mem = (char *)EMAC_TX_BD_MEM;
+ memzero(tx_cppi->bd_mem, tx_cppi->alloc_size);
+
+ /* initialize the BD linked list */
+ alloc_mem = (char *)(((u32) tx_cppi->bd_mem + 0xF) & ~0xF);
+
+ tx_cppi->bd_pool_head = 0;
+ for (cnt = 0; cnt < ch_info->num_bd; cnt++) {
+ curr_bd = (struct emac_tx_bd *) (alloc_mem + (cnt * bd_size));
+ curr_bd->next = tx_cppi->bd_pool_head;
+ tx_cppi->bd_pool_head = curr_bd;
+ }
+
+ /* reset statistics counters */
+ tx_cppi->out_of_tx_bd = 0;
+ tx_cppi->no_active_pkts = 0;
+ tx_cppi->active_queue_count = 0;
+ dev->tx_is_created[ch_info->ch_num] = TRUE;
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "ChannelNo=%d", ch_info->ch_num);
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * Un-Init Tx Channel
+ *
+ *- Frees memory previously allocated for Ch Control structure,
+ * Buffer descriptors
+ *
+ * 1. "chCloseArgs" not used in this implementation
+ * 2. This function assumes that the channel number passed is valid
+ * and this function will not do any error check to avoid duplicate
+ * error checks (done in caller function).
+ */
+static int emac_un_init_tx_channel(struct emac_dev_s *_dev, u32 channel,
+ void *ch_close_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ struct emac_tx_cppi_ch *tx_cppi;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "ChannelNo=%d", channel);
+
+ /* check if channel structure is already de-allocated */
+ if (dev->tx_is_created[channel] == FALSE) {
+ LOGERR("TX CPPI Channel %d structure already freed", channel);
+ return EMAC_ERR_TX_CH_ALREADY_CLOSED;
+ }
+
+ tx_cppi = dev->tx_cppi[channel];
+
+ /* free the buffer descriptors memory */
+ if (tx_cppi->bd_mem != NULL)
+ tx_cppi->bd_mem = NULL;
+
+#ifdef EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+ /* free the TX complete queue */
+ emac_free(tx_cppi->tx_complete);
+#endif
+
+ /* free the TX channel structure */
+ emac_free(tx_cppi);
+ dev->tx_cppi[channel] = NULL;
+ dev->tx_is_created[channel] = FALSE;
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "ChannelNo=%d", channel);
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * Init Rx Channel
+ * - Allocates memory for RX Ch Control structure, Buffer descriptors
+ * - Initialize the above data structures as per channel configuration
+ *- Allocate receive buffers from DDA and chain the RX BD list ready
+ * to be given to hardware
+ *
+ * 1. "chOpenArgs" Points to MAC address for this channel
+ * 2. This function assumes that the channel number passed is valid
+ * and the hDDC->rxCppi[channel] pointer is NULL. This function will
+ * not do any error check on these parameters to avoid duplicate error
+ * checks (done in caller function).
+ */
+static int emac_init_rx_channel(struct emac_dev_s *_dev,
+ struct emac_ch_info *ch_info,
+ void *ch_open_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 cnt, bd_size;
+ char *alloc_mem;
+ struct emac_rx_bd *curr_bd;
+ struct emac_rx_cppi_ch_t *rx_cppi = NULL;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "ChannelNo=%d", ch_info->ch_num);
+ /* allocate memory for RX CPPI channel */
+ emac_malloc(sizeof(struct emac_rx_cppi_ch_t), (void **)&rx_cppi);
+ /* update the channel control structure in DDC */
+ dev->rx_cppi[ch_info->ch_num] = rx_cppi;
+
+ rx_cppi->ch_info = *ch_info; /* structure copy */
+ rx_cppi->ch_info.ch_state = NET_CH_INITIALIZED;
+ dev->rx_teardown_pending[ch_info->ch_num] = FALSE;
+
+ /* save mac address */
+ alloc_mem = (char *)ch_open_args;
+ for (cnt = 0; cnt < 6; cnt++)
+ rx_cppi->mac_addr[cnt] = alloc_mem[cnt];
+
+ /*
+ * allocate buffer descriptor pool align every BD on four word
+ * boundry for future requirements
+ */
+ bd_size = (sizeof(struct emac_rx_bd) + 0xF) & ~0xF;
+ rx_cppi->alloc_size = (((bd_size * ch_info->num_bd) + 0xF) & ~0xF);
+
+ /* alloc RX BD memory */
+ rx_cppi->bd_mem = (char *) EMAC_RX_BD_MEM;
+ memzero(rx_cppi->bd_mem, rx_cppi->alloc_size);
+
+ rx_cppi->pkt_queue.buf_list = &rx_cppi->buf_queue[0];
+
+ /* allocate RX buffer and initialize the BD linked list */
+ alloc_mem = (char *)(((u32) rx_cppi->bd_mem + 0xF) & ~0xF);
+ rx_cppi->active_queue_head = 0;
+ rx_cppi->active_queue_tail = (struct emac_rx_bd *) alloc_mem;
+ for (cnt = 0; cnt < ch_info->num_bd; cnt++) {
+ curr_bd = (struct emac_rx_bd *) (alloc_mem + (cnt * bd_size));
+
+ /*
+ * for potential future use the last parameter
+ * contains the BD ptr
+ */
+ curr_bd->data_ptr =
+ (void *)(emac_net_alloc_rx_buf(dev,
+ ch_info->buf_size,
+ (void **) &
+ curr_bd->buf_token, 0,
+ (void *)curr_bd));
+ if (curr_bd->data_ptr == NULL) {
+ LOGERR
+ ("Error in RX Buffer allocation for channel %d",
+ ch_info->ch_num);
+ return EMAC_ERR_RX_BUFFER_ALLOC_FAIL;
+ }
+
+ /* populate the hardware descriptor */
+ curr_bd->h_next = EMAC_VIRT_TO_PHYS(rx_cppi->active_queue_head);
+ curr_bd->buff_ptr = EMAC_VIRT_TO_PHYS(curr_bd->data_ptr);
+ curr_bd->off_b_len = ch_info->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 = (void *)rx_cppi->active_queue_head;
+ rx_cppi->active_queue_head = curr_bd;
+ }
+
+ /*
+ * At this point rxCppi->activeQueueHead points to the first
+ * RX BD ready to be given to RX HDP and
+ * rx_cppi->active_queue_tail points to the last RX BD
+ */
+ dev->rx_is_created[ch_info->ch_num] = TRUE;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "ChannelNo=%d", ch_info->ch_num);
+ return EMAC_SUCCESS;
+}
+
+/**
+ * Un-Init Rx Channel
+ *- Frees memory previously allocated for Ch Control structure,
+ * Buffer descriptors
+ *- Returns (Frees) back receive buffers to DDA layer
+ *
+ * 1. "chCloseArgs" not used in this implementation
+ * 2. This function assumes that the channel number passed is valid
+ * and this function will not do any error check to avoid duplicate
+ * error checks (done in caller function).
+ */
+static int emac_un_init_rx_channel(struct emac_dev_s *_dev, u32 channel,
+ void *ch_close_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ struct emac_rx_cppi_ch_t *rx_cppi;
+ struct emac_rx_bd *curr_bd;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "ChannelNo=%d", channel);
+
+ /* check if channel structure is already de-allocated */
+ if (dev->rx_is_created[channel] == FALSE) {
+ LOGERR("RX CPPI Channel %d structure already freed", channel);
+ return EMAC_ERR_RX_CH_ALREADY_CLOSED;
+ }
+
+ rx_cppi = dev->rx_cppi[channel];
+
+ /* free the receive buffers previously allocated */
+ curr_bd = rx_cppi->active_queue_head;
+ while (curr_bd) {
+ if (emac_net_free_rx_buf(dev,
+ curr_bd->data_ptr,
+ (void *) curr_bd->
+ buf_token, 0, NULL) != EMAC_SUCCESS)
+ LOGERR("Failed to free RX buffer Ch %d", channel);
+ curr_bd = curr_bd->next;
+ }
+
+ /* free the buffer descriptors memory */
+ if (rx_cppi->bd_mem != NULL)
+ rx_cppi->bd_mem = NULL;
+
+ /* free the RX channel structure */
+ emac_free(rx_cppi);
+ dev->rx_cppi[channel] = NULL;
+ dev->rx_is_created[channel] = FALSE;
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "ChannelNo=%d", channel);
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * Set EMAC Mac address
+ * Functionality provided:
+ * - EMAC address is set in the hardware based on the address type
+ *
+ * 1. It is assumed that the channel is already "initialized"
+ */
+static void emac_set_mac_address(struct emac_dev_s *_dev, u32 channel,
+ char *mac_addr)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ /* enable unicast on this channel */
+ davinci_writel((1 << channel), dev->emac_regs_base
+ + EMAC_RX_UNICAST_SET_REG);
+
+ /* program MAC address for the channel depending upon emac/cpgmac */
+ if (dev->rx_addr_type == RX_ADDR_TYPE0)
+ emac_add_type0addr(_dev, channel, mac_addr);
+ else if (dev->rx_addr_type == RX_ADDR_TYPE1)
+ emac_add_type1addr(_dev, channel, mac_addr);
+ else if (dev->rx_addr_type == RX_ADDR_TYPE2)
+ emac_add_type2addr(_dev, channel, mac_addr, 0, 1, 1);
+ else
+ LOGERR
+ ("Wrong Rx Addressing Type - (Type2) detected in hardware");
+}
+
+/**
+ * Enable TX/RX Channel
+ * Functionality provided:
+ *- Channel is enabled in hardware. Data transfer can occur on this
+ * channel after this.
+ *
+ * 1. It is assumed that the channel is already "initialized"
+ * 2. To enable a channel after its disabled, it needs to be initialized again
+ */
+static int emac_enable_channel(struct emac_dev_s *_dev, u32 channel,
+ u32 direction)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ if (direction == NET_CH_DIR_TX) {
+ struct emac_tx_cppi_ch *tx_cppi;
+
+ tx_cppi = dev->tx_cppi[channel];
+ if (tx_cppi == NULL) {
+ LOGERR("Invalid Channel %d. TX CPPI structure NULL",
+ channel);
+
+ return EMAC_ERR_TX_CH_INVALID;
+ }
+
+ /* init head descriptor pointer */
+ davinci_writel(0, dev->emac_regs_base
+ + EMAC_TX_HDP_REG(channel));
+ {
+ struct emac_mac_config *mac_cfg;
+
+ mac_cfg = &dev->init_cfg.mac_cfg;
+ if (mac_cfg->tx_interrupt_disable == TRUE) {
+ /* disable channel interrupt */
+ davinci_writel((1 << channel),
+ dev->emac_regs_base
+ + EMAC_TX_INT_MASK_CLEAR_REG);
+ dev->tx_interrupt_disable = TRUE;
+ dev->tx_int_threshold[channel] =
+ dev->tx_cppi[channel]->ch_info.service_max;
+ } else {
+ /* enable channel interrupt */
+ davinci_writel((1 << channel),
+ dev->emac_regs_base
+ + EMAC_TX_INT_MASK_SET_REG);
+ dev->tx_interrupt_disable = FALSE;
+ }
+ }
+
+ /* mark channel open */
+ dev->tx_is_open[channel] = TRUE;
+ tx_cppi->ch_info.ch_state = NET_CH_OPENED;
+ }
+
+ else if (direction == NET_CH_DIR_RX) {
+ struct emac_rx_cppi_ch_t *rx_cppi;
+
+ rx_cppi = dev->rx_cppi[channel];
+ if (rx_cppi == NULL) {
+ LOGERR
+ ("Invalid Channel %d. RX CPPI structure NULL",
+ channel);
+
+ return EMAC_ERR_RX_CH_INVALID;
+ }
+
+ /* set interface MAC address */
+ emac_set_mac_address(_dev, channel, rx_cppi->mac_addr);
+
+ /* enable channel interrupt */
+ davinci_writel((1 << channel),
+ dev->emac_regs_base + EMAC_RX_INT_MASK_SET_REG);
+
+ /* mark queue active */
+ rx_cppi->queue_active = TRUE;
+
+ /* enable DMA */
+ davinci_writel(EMAC_VIRT_TO_PHYS(rx_cppi->active_queue_head),
+ dev->emac_regs_base + EMAC_RX_HDP_REG(channel));
+
+ /* mark channel open */
+ dev->rx_is_open[channel] = TRUE;
+
+ rx_cppi->ch_info.ch_state = NET_CH_OPENED;
+
+ }
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * Disable TX/RX Channel
+ * Functionality provided:
+ *- Channel is disabled in hardware. No data transfer can occur on
+ * this channel after this.
+ *
+ * 1. It is assumed that the channel number passed is valid
+ * 2. Resources for the channel will be released only when its closed
+ */
+static int emac_disable_channel(struct emac_dev_s *_dev, u32 channel,
+ enum net_ch_dir direction)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ if (direction == NET_CH_DIR_TX) {
+
+ /* set the TX teardown pending flag */
+ dev->tx_teardown_pending[channel] = TRUE;
+
+ /* initiate teardown of TX channel */
+ davinci_writel(channel,
+ dev->emac_regs_base + EMAC_TX_TEARDOWN_REG);
+
+ /* wait for teardown complete */
+ if (emac_wait_for_teardown_complete
+ (_dev, channel, direction, TRUE) != EMAC_SUCCESS) {
+
+ LOGERR("Failed to teardown TX channel %d", channel);
+
+ /* instead of quitting on error immediately,
+ *we continue so as to cleanup the channel */
+ }
+
+ dev->tx_teardown_pending[channel] = FALSE;
+
+ /* disable interrupt */
+ davinci_writel((1 << channel),
+ dev->emac_regs_base + EMAC_TX_INT_MASK_CLEAR_REG);
+
+ /* disable DMA */
+
+ /* mark channel closed */
+ dev->tx_is_open[channel] = FALSE;
+ }
+
+ else if (direction == NET_CH_DIR_RX) {
+ dev->rx_teardown_pending[channel] = TRUE;
+
+ /* initiate teardown of TX channel */
+ davinci_writel(channel,
+ dev->emac_regs_base + EMAC_RX_TEARDOWN_REG);
+
+ /* wait for teardown complete */
+ if (emac_wait_for_teardown_complete
+ (_dev, channel, direction, TRUE) != EMAC_SUCCESS)
+ LOGERR("Failed to teardown RX channel %d", channel);
+
+ dev->rx_teardown_pending[channel] = FALSE;
+
+ /* disable interrupt */
+ davinci_writel((1 << channel),
+ dev->emac_regs_base + EMAC_RX_INT_MASK_CLEAR_REG);
+
+ /* mark channel closed */
+ dev->rx_is_open[channel] = FALSE;
+ }
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * Wait for Teardown Complete
+ * - This function waits (blocking mode) for teardown completion.
+ * - blocking = TRUE(waits on OS timer wait untill teardown complete),
+ * = FALSE (returns immediately) - NOT SUPPORTED
+ * As of now this function supports blocking mode in polled mode only
+ */
+static int emac_wait_for_teardown_complete(struct emac_dev_s *_dev,
+ u32 channel,
+ enum net_ch_dir direction,
+ bool blocking)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ static unsigned int teardown_cnt = 0xFFFFFFF0;
+
+ if (direction == NET_CH_DIR_TX) {
+ struct emac_tx_bd *curr_bd;
+ struct emac_tx_cppi_ch *tx_cppi;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ while ((davinci_readl(dev->emac_regs_base +
+ EMAC_TX_CP_REG(channel)) & EMAC_TEARDOWN_VALUE) !=
+ EMAC_TEARDOWN_VALUE) {
+ /*
+ * wait here for tx teardown completion
+ * interrupt to occur
+ */
+
+ /*
+ * A task delay can be called here to pend
+ * rather than occupying CPU cycles - anyway
+ * it has been found that the teardown takes
+ * very few cpu cycles and does not affect
+ * functionality
+ */
+ --teardown_cnt;
+ if (teardown_cnt) {
+ printk(KERN_NOTICE "Tx teardown aborted\n");
+ break;
+ }
+ }
+
+ /* write to the completion pointer */
+ davinci_writel(EMAC_TEARDOWN_VALUE,
+ dev->emac_regs_base + EMAC_TX_CP_REG(channel));
+
+ /*
+ * TX teardown complete - process sent packets and
+ * return sent packets to DDA
+ */
+ tx_cppi = dev->tx_cppi[channel];
+ if (tx_cppi->queue_active == TRUE) {
+ curr_bd = tx_cppi->active_queue_head;
+ while (curr_bd != NULL) {
+ emac_net_tx_complete(dev,
+ &(curr_bd->buf_token),
+ 1, channel);
+
+ if (curr_bd != tx_cppi->active_queue_tail)
+ curr_bd = curr_bd->next;
+ else
+ break;
+ }
+ tx_cppi->bd_pool_head = tx_cppi->active_queue_head;
+ tx_cppi->active_queue_head =
+ tx_cppi->active_queue_tail = 0;
+ }
+
+ /* At this stage all TX BD's are available linked with
+ *"bdPoolHead" and can be freed */
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+ } else if (direction == NET_CH_DIR_RX) {
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ while ((davinci_readl(dev->emac_regs_base +
+ EMAC_RX_CP_REG(channel)) & EMAC_TEARDOWN_VALUE) !=
+ EMAC_TEARDOWN_VALUE) {
+
+ /*
+ * wait here for rx teardown completion
+ * interrupt to occur
+ */
+
+ /*
+ * A task delay can be called here to pend
+ * rather than occupying CPU cycles - anyway
+ * it has been found that the teardown takes
+ * very few cpu cycles and does not affect
+ * functionality
+ */
+ --teardown_cnt;
+ if (teardown_cnt) {
+ printk(KERN_NOTICE"Rx teardown aborted\n");
+ break;
+ }
+ }
+
+ /* write to the completion pointer */
+ davinci_writel(EMAC_TEARDOWN_VALUE,
+ dev->emac_regs_base + EMAC_RX_CP_REG(channel));
+
+ /* At this stage all TX BD's are available linked with
+ *"activeQueueHead" and can be freed */
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ }
+
+ return EMAC_SUCCESS;
+}
+
+static void emac_ddcphycnt(struct emac_dev_s *dev, u32 *cmd_arg)
+{
+ int result;
+ struct emac_hw_statistics stats;
+ struct mib2_phy_counters *mib2phy_counters =
+ (struct mib2_phy_counters *)cmd_arg;
+
+ result =
+ emac_control(dev, EMAC_IOCTL_GET_STATISTICS, (u32 *) &stats, NULL);
+
+ if (result != 0) {
+ LOGERR("Error from ioctl for EMAC_IOCTL_GET_STATISTICS \n");
+ return;
+ }
+
+ mib2phy_counters->eth_alignment_errors = stats.if_in_align_code_errors;
+ mib2phy_counters->eth_fcserrors = stats.if_in_crcerrors;
+ mib2phy_counters->eth_single_collisions =
+ stats.if_single_collision_frames;
+ mib2phy_counters->eth_multiple_collisions =
+ stats.if_multiple_collision_frames;
+ mib2phy_counters->eth_sqetest_errors = 0;
+ mib2phy_counters->eth_deferred_tx_frames =
+ stats.if_deferred_transmissions;
+ mib2phy_counters->eth_late_collisions = stats.if_late_collisions;
+ mib2phy_counters->eth_excessive_collisions =
+ stats.if_excessive_collision_frames;
+ mib2phy_counters->eth_internal_mac_tx_errors = 0;
+ mib2phy_counters->eth_carrier_sense_errors =
+ stats.if_carrier_sense_errors;
+ mib2phy_counters->eth_too_long_rx_frames = stats.if_in_oversized_frames;
+ mib2phy_counters->eth_internal_mac_rx_errors = 0;
+ mib2phy_counters->eth_symbol_errors = 0;
+}
+
+static void emac_ddcifcnt_clear(struct emac_dev_s *_dev)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ memzero((char *)&dev->mib2if_hccounter, sizeof(dev->mib2if_hccounter));
+}
+
+static void emac_ddcifcnt_updt(struct emac_dev_s *_dev)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ int result;
+ struct emac_hw_statistics stats;
+
+ result =
+ emac_control(_dev, EMAC_IOCTL_GET_STATISTICS,
+ (u32 *) &stats, NULL);
+
+ if (result != 0) {
+ LOGERR("Error from ioctl for DDC EMAC_IOCTL_GET_STATISTICS \n");
+ return;
+ }
+
+ if (stats.if_in_octets >= dev->mib2if_hccounter.in_bytes) {
+ dev->mib2if_hccounter.in_bytes_hc +=
+ (stats.if_in_octets - dev->mib2if_hccounter.in_bytes);
+ } else {
+ dev->mib2if_hccounter.in_bytes_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.in_bytes -
+ stats.if_in_octets);
+ }
+
+ dev->mib2if_hccounter.in_bytes = stats.if_in_octets;
+ if (stats.if_in_good_frames >=
+ dev->mib2if_hccounter.in_multicast_pkts +
+ dev->mib2if_hccounter.in_broadcast_pkts +
+ dev->mib2if_hccounter.in_unicast_pkts) {
+ dev->mib2if_hccounter.in_unicast_pkts_hc +=
+ ((stats.if_in_good_frames -
+ (stats.if_in_broadcasts + stats.if_in_multicasts))
+ - dev->mib2if_hccounter.in_unicast_pkts);
+ } else {
+ dev->mib2if_hccounter.in_unicast_pkts_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.in_unicast_pkts -
+ (stats.if_in_good_frames -
+ (stats.if_in_broadcasts +
+ stats.if_in_multicasts)));
+ }
+ dev->mib2if_hccounter.in_unicast_pkts = (stats.if_in_good_frames -
+ (stats.if_in_broadcasts +
+ stats.if_in_multicasts));
+ if (stats.if_in_multicasts >= dev->mib2if_hccounter.in_multicast_pkts) {
+ dev->mib2if_hccounter.in_multicast_pkts_hc +=
+ (stats.if_in_multicasts -
+ dev->mib2if_hccounter.in_multicast_pkts);
+ } else {
+ dev->mib2if_hccounter.in_multicast_pkts_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.in_multicast_pkts -
+ stats.if_in_multicasts);
+ }
+
+ dev->mib2if_hccounter.in_multicast_pkts = stats.if_in_multicasts;
+ if (stats.if_in_broadcasts >= dev->mib2if_hccounter.in_broadcast_pkts) {
+ dev->mib2if_hccounter.in_broadcast_pkts_hc +=
+ (stats.if_in_broadcasts -
+ dev->mib2if_hccounter.in_broadcast_pkts);
+
+ } else {
+ dev->mib2if_hccounter.in_broadcast_pkts_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.in_broadcast_pkts -
+ stats.if_in_broadcasts);
+ }
+
+ dev->mib2if_hccounter.in_broadcast_pkts = stats.if_in_broadcasts;
+ if (stats.if_out_octets >= dev->mib2if_hccounter.out_bytes) {
+ dev->mib2if_hccounter.out_bytes_hc +=
+ (stats.if_out_octets - dev->mib2if_hccounter.out_bytes);
+ } else {
+ dev->mib2if_hccounter.out_bytes_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.out_bytes -
+ stats.if_out_octets);
+ }
+
+ dev->mib2if_hccounter.out_bytes = stats.if_out_octets;
+ if (stats.if_out_good_frames >=
+ dev->mib2if_hccounter.out_multicast_pkts +
+ dev->mib2if_hccounter.out_broadcast_pkts +
+ dev->mib2if_hccounter.out_unicast_pkts)
+ dev->mib2if_hccounter.out_unicast_pkts_hc +=
+ ((stats.if_out_good_frames -
+ (stats.if_out_broadcasts + stats.if_out_multicasts))
+ - dev->mib2if_hccounter.out_unicast_pkts);
+ else
+ dev->mib2if_hccounter.out_unicast_pkts_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.out_unicast_pkts -
+ (stats.if_out_good_frames -
+ (stats.if_out_broadcasts +
+ stats.if_out_multicasts)));
+
+ dev->mib2if_hccounter.out_unicast_pkts = (stats.if_out_good_frames -
+ (stats.if_out_broadcasts +
+ stats.if_out_multicasts));
+
+ if (stats.if_out_multicasts >=
+ dev->mib2if_hccounter.out_multicast_pkts)
+ dev->mib2if_hccounter.out_multicast_pkts_hc +=
+ (stats.if_out_multicasts -
+ dev->mib2if_hccounter.out_multicast_pkts);
+ else
+ dev->mib2if_hccounter.out_multicast_pkts_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.out_multicast_pkts -
+ stats.if_out_multicasts);
+
+ dev->mib2if_hccounter.out_multicast_pkts = stats.if_out_multicasts;
+ if (stats.if_out_broadcasts >=
+ dev->mib2if_hccounter.out_broadcast_pkts)
+
+ dev->mib2if_hccounter.out_broadcast_pkts_hc +=
+ (stats.if_out_broadcasts -
+ dev->mib2if_hccounter.out_broadcast_pkts);
+ else
+ dev->mib2if_hccounter.out_broadcast_pkts_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.out_broadcast_pkts -
+ stats.if_out_broadcasts);
+ dev->mib2if_hccounter.out_broadcast_pkts = stats.if_out_broadcasts;
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_bytes_low =
+ (unsigned long)dev->mib2if_hccounter.in_bytes_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_bytes_high =
+ (dev->mib2if_hccounter.in_bytes_hc >> 32);
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_unicast_pkts_low =
+ (unsigned long)dev->mib2if_hccounter.in_unicast_pkts_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_unicast_pkts_high =
+ (dev->mib2if_hccounter.in_unicast_pkts_hc >> 32);
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_multicast_pkts_low =
+ (unsigned long)dev->mib2if_hccounter.in_multicast_pkts_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_multicast_pkts_high =
+ dev->mib2if_hccounter.in_multicast_pkts_hc >> 32;
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_broadcast_pkts_low =
+ (unsigned long)dev->mib2if_hccounter.in_broadcast_pkts_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_broadcast_pkts_high =
+ dev->mib2if_hccounter.in_broadcast_pkts_hc >> 32;
+
+ /* packets discarded due to resource limit */
+ dev->mib2if_hccounter.mib2if_counter.in_discard_pkts =
+ stats.if_rx_dmaoverruns
+ + stats.if_rx_mof_overruns
+ + stats.
+ if_rx_sof_overruns
+ + stats.if_in_crcerrors
+ + stats.
+ if_in_align_code_errors
+ + stats.if_in_jabber_frames
+ + stats.
+ if_in_fragments
+ + stats.if_in_oversized_frames
+ + stats.
+ if_in_undersized_frames
+ + stats.if_in_filtered_frames + stats.if_in_qos_filtered_frames;
+
+ /* packets discarded due to format errors */
+ dev->mib2if_hccounter.mib2if_counter.in_error_pkts =
+ stats.if_in_crcerrors
+ + stats.if_in_align_code_errors
+ + stats.if_in_jabber_frames + stats.if_in_fragments;
+
+ dev->mib2if_hccounter.mib2if_counter.in_unknown_prot_pkts = 0;
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_bytes_low =
+ (unsigned long)dev->mib2if_hccounter.out_bytes_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_bytes_high =
+ dev->mib2if_hccounter.out_bytes_hc >> 32;
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_unicast_pkts_low =
+ (unsigned long)dev->mib2if_hccounter.out_unicast_pkts_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_unicast_pkts_high =
+ dev->mib2if_hccounter.out_unicast_pkts_hc >> 32;
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_multicast_pkts_low =
+ (unsigned long)dev->mib2if_hccounter.out_multicast_pkts_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_multicast_pkts_high =
+ dev->mib2if_hccounter.out_multicast_pkts_hc >> 32;
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_broadcast_pkts_low =
+ (unsigned long)dev->mib2if_hccounter.out_broadcast_pkts_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_broadcast_pkts_high =
+ dev->mib2if_hccounter.out_broadcast_pkts_hc >> 32;
+
+ /* packets discarded due to format errors */
+ dev->mib2if_hccounter.mib2if_counter.out_error_pkts =
+ (stats.if_excessive_collision_frames
+ + stats.if_late_collisions + stats.if_carrier_sense_errors);
+
+ /* packets discarded due to resource limit */
+ dev->mib2if_hccounter.mib2if_counter.out_discard_pkts =
+ stats.if_out_underrun +
+ dev->mib2if_hccounter.mib2if_counter.out_error_pkts;
+}
+
+#define emac_min_val(a, b) ((a > b) ? b : a)
+
+#ifdef EMAC_DEBUG /* used only for debug printing */
+/* static global strings */
+static char *emac_tx_host_error_codes[16] = {
+ /* 0000 */ "No error",
+ /* 0001 */ "SOP error",
+ /* 0010 */ "Ownership bit not set in SOP buffer",
+ /* 0011 */ "Zero Next Buffer Descriptor Pointer Without EOP",
+ /* 0100 */ "Zero Buffer Pointer",
+ /* 0101 */ "Zero Buffer Length",
+ /* 0110 */ "Packet Length Error",
+ /* 0111 */ "Reserved",
+ /* 1000 */ "Reserved",
+ /* 1001 */ "Reserved",
+ /* 1010 */ "Reserved",
+ /* 1011 */ "Reserved",
+ /* 1100 */ "Reserved",
+ /* 1101 */ "Reserved",
+ /* 1110 */ "Reserved",
+ /* 1111 */ "Reserved"
+};
+
+static char *emac_rx_host_error_codes[16] = {
+ /* 0000 */ "No error",
+ /* 0001 */ "Reserved",
+ /* 0010 */ "Ownership bit not set in input buffer",
+ /* 0011 */ "Reserved",
+ /* 0100 */ "Zero Buffer Pointer",
+ /* 0101 */ "Reserved",
+ /* 0110 */ "Reserved",
+ /* 0111 */ "Reserved",
+ /* 1000 */ "Reserved",
+ /* 1001 */ "Reserved",
+ /* 1010 */ "Reserved",
+ /* 1011 */ "Reserved",
+ /* 1100 */ "Reserved",
+ /* 1101 */ "Reserved",
+ /* 1110 */ "Reserved",
+ /* 1111 */ "Reserved"
+};
+
+#endif /* EMAC_DEBUG */
+
+/**
+ * EMAC DDC Periodic Timer (Tick) Function
+ * - calls PHY polling function
+ * - If status changed, invokes DDA callback to propogate PHY / Devicestatus
+ *
+ *"tickArgs" is not used in this implementation
+ */
+static int emac_tick(struct emac_dev_s *_dev, void *tick_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ /* verify proper device state */
+ if (dev->drv_state != DRV_OPENED)
+ return EMAC_ERR_DEV_NOT_OPEN;
+
+ if (!(dev->init_cfg.phy_mode & SNWAY_NOPHY)) {
+ /* opened and phy available */
+ int tick_change;
+
+ tick_change = emac_mdio_tick();
+ if (tick_change == 1) {
+ /* MDIO indicated a change */
+ emac_update_phy_status((struct emac_dev_s *) dev);
+ emac_control_cb(dev,
+ EMAC_IOCTL_STATUS_UPDATE,
+ (void *)&dev->status, NULL);
+ }
+ }
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * EMAC DDC Packet processing function
+ * - Detects if there are host errors and invokes the DDA callback to inform
+ * the DDA layer about the hardware error.
+ *
+ */
+static void emac_process_host_error(struct emac_dev_s *_dev)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 channel = 0;
+ u32 vector = 0;
+ u32 status = 0;
+
+ /*
+ * the mac_status register bits starting from rx error channel
+ * have been mapped to hw_err_info LSB 16 bits
+ */
+ status = davinci_readl(dev->emac_regs_base + EMAC_MAC_STATUS_REG);
+
+ /* TX: reading the channel and cause */
+ channel =
+ ((status & EMAC_MACSTATUS_TXERRCH_MASK) >>
+ EMAC_MACSTATUS_TXERRCH_SHIFT);
+
+ dev->status.hw_err_info = channel << 16;
+
+ vector =
+ (status & EMAC_MACSTATUS_TXERRCODE_MASK) >>
+ EMAC_MACSTATUS_TXERRCODE_SHIFT;
+
+ if (vector) {
+ dev->status.hw_status = EMAC_TX_HOST_ERROR;
+ LOGERR
+ ("Ch=%d, EMAC_TX_HOST_ERROR. Cause=%s",
+ dev->status.hw_err_info,
+ &emac_tx_host_error_codes[vector][0]);
+ }
+
+ /* RX: reading the channel and cause (vector variable being
+ *re-used) */
+ channel =
+ ((status & EMAC_MACSTATUS_RXERRCH_MASK) >>
+ EMAC_MACSTATUS_RXERRCH_SHIFT);
+
+ dev->status.hw_err_info |= channel;
+ vector =
+ (status & EMAC_MACSTATUS_RXERRCODE_MASK) >>
+ EMAC_MACSTATUS_RXERRCODE_SHIFT;
+ if (vector) {
+ dev->status.hw_status = EMAC_RX_HOST_ERROR;
+ LOGERR
+ ("Ch=%d, EMAC_RX_HOST_ERROR. Cause=%s",
+ dev->status.hw_err_info,
+ &emac_rx_host_error_codes[vector][0]);
+ }
+
+ /* inform DDA layer about this critical failure */
+ emac_control_cb(dev,
+ EMAC_IOCTL_STATUS_UPDATE, (void *)&dev->status, NULL);
+}
+
+/**
+ * EMAC DDC Packet processing function
+
+ *- Reads the device interrupt status and invokes TX/RX BD processing
+ * function
+ *- Also detects if there are host errors and invokes the
+ * callback to inform about the hardware error.
+ *
+ *"pkts_pending" will contain number of packets still to be processed
+ * (TX + RX)
+ */
+static int emac_pkt_process(struct emac_dev_s *_dev, int *pkts_pending,
+ void *pkt_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 channel = 0;
+ u32 vector = 0;
+ u32 handle_pkts_and_status = 0;
+ u32 vector_channel = 0;
+ int pkts_processed = 0;
+
+ /* disable interrupts via module control (wrapper) */
+ davinci_writel(0, dev->emac_wrap_regs_base + EMAC_WRAP_EWCTL_REG);
+ vector = davinci_readl(dev->emac_regs_base + EMAC_MAC_IN_VECTOR_REG);
+
+ /* handle packet transmit completion */
+ if (vector & EMAC_MAC_IN_VECTOR_TX_INT_VEC) {
+ bool is_eoq;
+
+ vector_channel = (vector & EMAC_MAC_IN_VECTOR_TX_INT_VEC);
+ for (channel = 0; channel < 8; channel++) {
+ if (vector_channel & 0x1)
+ break;
+
+ vector_channel >>= 1;
+ }
+
+ handle_pkts_and_status =
+ dev->tx_cppi[channel]->ch_info.service_max;
+ if (pkt_args)
+ handle_pkts_and_status =
+ emac_min_val(((struct rx_tx_params *) pkt_args)->
+ tx_pkts, handle_pkts_and_status);
+
+ pkts_processed =
+ emac_tx_bdproc(_dev, channel, &handle_pkts_and_status,
+ &is_eoq);
+ if (pkt_args)
+ ((struct rx_tx_params *) pkt_args)->ret_tx_pkts =
+ pkts_processed;
+
+ if (dev->tx_interrupt_disable == TRUE) {
+ /* status */
+ if (!handle_pkts_and_status && is_eoq)
+ /* disable channel interrupt */
+ davinci_writel((1 << channel),
+ dev->emac_regs_base + EMAC_TX_INT_MASK_CLEAR_REG);
+ }
+ *pkts_pending = handle_pkts_and_status; /* status. */
+ }
+
+ /*
+ * Handle RX packets first - the thought process in this is
+ * that the received packets will be handled immediately
+ * reducing the latency (- but an equally opposite argument
+ * can also be made)
+ */
+ if (vector & EMAC_MAC_IN_VECTOR_RX_INT_VEC) {
+ vector_channel = (vector & EMAC_MAC_IN_VECTOR_RX_INT_VEC);
+ vector_channel >>= 8;
+ for (channel = 0; channel < 8; channel++) {
+ if (vector_channel & 0x1)
+ break;
+ vector_channel >>= 1;
+ }
+
+ handle_pkts_and_status =
+ dev->rx_cppi[channel]->ch_info.service_max;
+ if (pkt_args)
+ handle_pkts_and_status =
+ emac_min_val(((struct rx_tx_params *) pkt_args)->
+ rx_pkts, handle_pkts_and_status);
+
+ pkts_processed =
+ emac_rx_bdproc(_dev, channel,
+ &handle_pkts_and_status);
+
+ if (pkt_args)
+ ((struct rx_tx_params *) pkt_args)->ret_rx_pkts =
+ pkts_processed;
+
+ *pkts_pending |= handle_pkts_and_status; /* status */
+ }
+
+ /*
+ * handle host errors - being handled last does not mean its
+ * of least priority
+ */
+ if (vector & EMAC_MAC_IN_VECTOR_HOST_INT)
+ emac_process_host_error(_dev);
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * EMAC DDC Signal Packet processing end to hardware
+ *- programs the EOI vector register so that if there are pending
+ * packets in hardware queue *an interrupt can be generated by the
+ * hardware
+ */
+static int emac_pkt_process_end(struct emac_dev_s *dev, void *proc_args)
+{
+ /* enable interrupts via module control (wrapper) */
+ davinci_writel(1, dev->emac_wrap_regs_base + EMAC_WRAP_EWCTL_REG);
+
+ return EMAC_SUCCESS;
+}
+
+#ifdef EMAC_MULTIFRAGMENT
+#error "EMAC Multi fragment Not supported"
+#else
+
+/*
+ * SINGLE-FRAGMENT SUPPORT HERE
+ */
+
+/**
+ * EMAC DDC Send/Transmit function
+ * - Queues the packet provided by DDA into hardware queue
+ * - If the queue is stalled due to sync issues, re-trigger the hardware
+ *
+ * If "sendArgs" is TRUE (non zero) CRC is calculated by DDA or upper
+ * layer and not by hardware and is part of the packet data send to
+ * this function
+ */
+static int emac_send(struct emac_dev_s *_dev, struct net_pkt_obj *pkt,
+ int channel, bool send_args)
+{
+ unsigned long flags;
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ int ret_val = EMAC_SUCCESS;
+ struct emac_tx_bd *curr_bd;
+ struct emac_tx_cppi_ch *tx_cppi;
+ struct net_buf_obj *buf_list;
+
+ /* verify proper device state */
+ if (dev->drv_state != DRV_OPENED)
+ return EMAC_ERR_DEV_NOT_OPEN;
+
+ /* validate channel number and get channel control structure */
+ if (channel > EMAC_MAX_TX_CHANNELS)
+ return EMAC_ERR_TX_CH_INVALID;
+
+ if (dev->tx_is_open[channel] != TRUE)
+ return EMAC_ERR_TX_CH_NOT_OPEN;
+
+ /* check ethernet link state. if not linked, return error */
+ if (!dev->status.phy_linked)
+ return EMAC_ERR_TX_NO_LINK;
+
+ tx_cppi = dev->tx_cppi[channel];
+ buf_list = pkt->buf_list; /* get handle to the buffer array */
+
+ /* check packet size and if < EMAC_MIN_ETHERNET_PKT_SIZE, pad it up */
+ if (pkt->pkt_length < EMAC_MIN_ETHERNET_PKT_SIZE) {
+ buf_list->length +=
+ (EMAC_MIN_ETHERNET_PKT_SIZE - pkt->pkt_length);
+ pkt->pkt_length = EMAC_MIN_ETHERNET_PKT_SIZE;
+ }
+ spin_lock_irqsave(&dev->tx_lock, flags);
+
+ /* only one tx BD for the packet to be sent */
+ curr_bd = tx_cppi->bd_pool_head;
+ if (curr_bd == NULL) {
+#ifdef EMAC_GETSTATS
+ tx_cppi->out_of_tx_bd++;
+#endif
+ ret_val = EMAC_ERR_TX_OUT_OF_BD;
+ goto exit_emac_send;
+ }
+
+ tx_cppi->bd_pool_head = curr_bd->next;
+
+ /* populate the BD contents to be added to the TX list */
+ curr_bd->buf_token = buf_list->buf_token;
+ curr_bd->buff_ptr = EMAC_VIRT_TO_PHYS((int *)buf_list->data_ptr);
+ curr_bd->off_b_len = buf_list->length;
+ curr_bd->h_next = 0;
+ curr_bd->next = 0;
+ curr_bd->mode =
+ (EMAC_CPPI_SOP_BIT | EMAC_CPPI_OWNERSHIP_BIT | EMAC_CPPI_EOP_BIT
+ | pkt->pkt_length);
+
+ if ((bool) send_args == TRUE)
+ curr_bd->mode |= EMAC_CPPI_PASS_CRC_BIT;
+
+ /* 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 (tx_cppi->active_queue_head == 0) {
+ tx_cppi->active_queue_head = curr_bd;
+ tx_cppi->active_queue_tail = curr_bd;
+ if (tx_cppi->queue_active != TRUE) {
+ davinci_writel(EMAC_VIRT_TO_PHYS(curr_bd),
+ dev->emac_regs_base + EMAC_TX_HDP_REG(channel));
+ tx_cppi->queue_active = TRUE;
+ }
+#ifdef EMAC_GETSTATS
+ ++tx_cppi->queue_reinit;
+#endif
+ } else {
+ register struct emac_tx_bd *tail_bd;
+ register u32 frame_status;
+
+ tail_bd = tx_cppi->active_queue_tail;
+ tail_bd->next = curr_bd;
+ tx_cppi->active_queue_tail = curr_bd;
+ tail_bd = EMAC_VIRT_NOCACHE(tail_bd);
+ tail_bd->h_next = (int)EMAC_VIRT_TO_PHYS(curr_bd);
+ frame_status = tail_bd->mode;
+ if (frame_status & EMAC_CPPI_EOQ_BIT) {
+ davinci_writel(EMAC_VIRT_TO_PHYS(curr_bd),
+ dev->emac_regs_base + EMAC_TX_HDP_REG(channel));
+ frame_status &= ~(EMAC_CPPI_EOQ_BIT);
+ tail_bd->mode = frame_status;
+#ifdef EMAC_GETSTATS
+ ++tx_cppi->end_of_queue_add;
+#endif
+ } else {
+ if (dev->tx_interrupt_disable == TRUE) {
+ /* enable channel interrupt */
+ davinci_writel((1 << channel),
+ dev->emac_regs_base + EMAC_TX_INT_MASK_SET_REG);
+ }
+ }
+ }
+#ifdef EMAC_GETSTATS
+ tx_cppi->active_queue_count++;
+#endif
+
+exit_emac_send:
+ spin_unlock_irqrestore(&dev->tx_lock, flags);
+
+ if (dev->tx_interrupt_disable == TRUE) {
+ if (--dev->tx_int_threshold[channel] <= 0) {
+ bool is_eoq;
+ u32 handle_pkts_and_status;
+
+ handle_pkts_and_status =
+ dev->tx_cppi[channel]->ch_info.service_max;
+ emac_tx_bdproc(_dev, channel, &handle_pkts_and_status,
+ &is_eoq);
+ dev->tx_int_threshold[channel] =
+ dev->tx_cppi[channel]->ch_info.service_max;
+ }
+ }
+
+ return ret_val;
+}
+
+/**
+ * EMAC DDC TX Buffer Descriptor processing
+ * - processes transmit completed packets and returns the handles to DDA layer
+ * - If the queue is stalled due to sync issues, re-trigger the hardware
+ *
+ * returns number of pkts processed and 1 in morePkts if pkt
+ * completion processing pending
+ */
+static int emac_tx_bdproc(struct emac_dev_s *_dev, u32 channel,
+ u32 *handle_pkts_and_status, bool *is_eoq)
+{
+ unsigned long flags;
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ struct emac_tx_bd *curr_bd;
+ struct emac_tx_cppi_ch *tx_cppi;
+ u32 frame_status;
+ u32 pkts_processed = 0;
+ u32 pkts_to_process = *handle_pkts_and_status;
+#ifdef EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+ u32 tx_complete_cnt = 0;
+ u32 *tx_complete_ptr;
+#endif
+
+ *handle_pkts_and_status = 0; /* status. */
+ *is_eoq = TRUE;
+
+ /* Here no need to validate channel number, since it is taken
+ from the interrupt register instead channel structure
+ should be validated */
+ if (dev->tx_is_open[channel] == FALSE)
+ return EMAC_ERR_TX_CH_NOT_OPEN;
+
+ if (dev->tx_teardown_pending[channel] == TRUE)
+ return EMAC_SUCCESS; /* dont handle any pkt completions */
+
+ tx_cppi = dev->tx_cppi[channel];
+#ifdef EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+ tx_complete_ptr = &tx_cppi->tx_complete[0];
+#endif
+#ifdef EMAC_GETSTATS
+ ++tx_cppi->proc_count;
+#endif
+ spin_lock_irqsave(&dev->tx_lock, flags);
+
+ /* get first BD to process */
+ curr_bd = tx_cppi->active_queue_head;
+ if (curr_bd == 0) {
+ davinci_writel(EMAC_VIRT_TO_PHYS(tx_cppi->last_hw_bdprocessed),
+ dev->emac_regs_base + EMAC_TX_CP_REG(channel));
+
+#ifdef EMAC_GETSTATS
+ tx_cppi->no_active_pkts++;
+#endif
+
+ spin_unlock_irqrestore(&dev->tx_lock, flags);
+
+ return EMAC_SUCCESS;
+ }
+
+ /* invalidate BD */
+ 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 < pkts_to_process)) {
+
+ davinci_writel(EMAC_VIRT_TO_PHYS(curr_bd),
+ dev->emac_regs_base + EMAC_TX_CP_REG(channel));
+ tx_cppi->active_queue_head = curr_bd->next;
+ if (frame_status & EMAC_CPPI_EOQ_BIT) {
+ if (curr_bd->next) {
+ /* misqueued packet */
+ davinci_writel(curr_bd->h_next,
+ dev->emac_regs_base + EMAC_TX_HDP_REG(channel));
+#ifdef EMAC_GETSTATS
+ ++tx_cppi->mis_queued_packets;
+#endif
+ } else
+ /* end of queue */
+ tx_cppi->queue_active = FALSE;
+ }
+#ifdef EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+ *tx_complete_ptr = (u32) curr_bd->buf_token;
+ ++tx_complete_ptr;
+ ++tx_complete_cnt;
+#else
+ /* single packet TX complete notify - this function is
+ *called in the send critical section context */
+ emac_net_tx_complete(dev,
+ &curr_bd->buf_token, 1, (void *)channel);
+#endif
+ curr_bd->next = tx_cppi->bd_pool_head;
+ tx_cppi->bd_pool_head = curr_bd;
+#ifdef EMAC_GETSTATS
+ --tx_cppi->active_queue_count;
+#endif
+ pkts_processed++;
+ tx_cppi->last_hw_bdprocessed = curr_bd;
+ curr_bd = tx_cppi->active_queue_head;
+ if (curr_bd) {
+ BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
+ frame_status = curr_bd->mode;
+ }
+ } /* end of while loop */
+
+ if ((curr_bd) && ((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0))
+ *handle_pkts_and_status = 1;
+
+ /*
+ * This check is same as check for EOQ i.e framestatus and
+ * EMAC_CPPI_EOQ_BIT
+ */
+ if (curr_bd)
+ *is_eoq = FALSE;
+
+#ifdef EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+ /*
+ * multiple packet TX complete notify - this function is NOT
+ * called in the send critical section context
+ */
+ emac_net_tx_complete(dev, (void **) &tx_cppi->tx_complete[0],
+ tx_complete_cnt, channel);
+#endif
+ spin_unlock_irqrestore(&dev->tx_lock, flags);
+
+ return pkts_processed;
+}
+
+/**
+ * EMAC DDC Add Buffer to RX queue function
+ * - returns the BD to the Receive queue
+ * - If the queue is stalled due to sync issues, re-trigger the hardware
+ */
+static void emac_add_bdto_rx_queue(struct emac_dev_s *dev,
+ struct emac_rx_cppi_ch_t *rx_cppi,
+ struct emac_rx_bd *curr_bd, char *buffer,
+ void *buf_token)
+{
+ /* populate the hardware descriptor */
+ curr_bd->h_next = 0;
+ curr_bd->buff_ptr = EMAC_VIRT_TO_PHYS(buffer);
+ curr_bd->off_b_len = rx_cppi->ch_info.buf_size;
+ curr_bd->mode = EMAC_CPPI_OWNERSHIP_BIT;
+ curr_bd->next = 0;
+ 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 (rx_cppi->active_queue_head == 0) {
+ rx_cppi->active_queue_head = curr_bd;
+ rx_cppi->active_queue_tail = curr_bd;
+ if (rx_cppi->queue_active != FALSE) {
+ davinci_writel(
+ EMAC_VIRT_TO_PHYS(rx_cppi->active_queue_head),
+ dev->emac_regs_base
+ + EMAC_RX_HDP_REG(rx_cppi->ch_info.ch_num));
+ rx_cppi->queue_active = TRUE;
+ }
+ } else {
+ struct emac_rx_bd *tail_bd;
+ u32 frame_status;
+
+ tail_bd = rx_cppi->active_queue_tail;
+ rx_cppi->active_queue_tail = curr_bd;
+ tail_bd->next = (void *)curr_bd;
+ tail_bd = EMAC_VIRT_NOCACHE(tail_bd);
+ tail_bd->h_next = EMAC_VIRT_TO_PHYS(curr_bd);
+ frame_status = tail_bd->mode;
+ if (frame_status & EMAC_CPPI_EOQ_BIT) {
+ davinci_writel(EMAC_VIRT_TO_PHYS(curr_bd),
+ dev->emac_regs_base
+ + EMAC_RX_HDP_REG(rx_cppi->ch_info.ch_num));
+ frame_status &= ~(EMAC_CPPI_EOQ_BIT);
+ tail_bd->mode = frame_status;
+#ifdef EMAC_GETSTATS
+ ++rx_cppi->end_of_queue_add;
+#endif
+ }
+
+ }
+
+#ifdef EMAC_GETSTATS
+ ++rx_cppi->recycled_bd; /* maintain statistics of how many BD's were
+ queued back - recycled */
+#endif
+}
+
+/**
+ * EMAC DDC RX Buffer Descriptor processing
+ * - processes received packets and passes them to DDA layer
+ * - requeues the buffer descriptor to the receive pool
+ * - If the queue is stalled due to sync issues, re-trigger the hardware
+ */
+static int emac_rx_bdproc(struct emac_dev_s *_dev, u32 channel,
+ int *handle_pkts_and_status)
+{
+ unsigned long flags;
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ struct emac_rx_cppi_ch_t *rx_cppi;
+ struct emac_rx_bd *curr_bd, *last_bd;
+ u32 frame_status;
+ char *new_buffer;
+ void *new_buf_token;
+ struct net_buf_obj *rx_buf_obj;
+ u32 pkts_processed;
+ struct net_pkt_obj *curr_pkt, pkt_obj;
+ struct net_buf_obj buf_obj;
+ u32 pkts_to_be_processed = *handle_pkts_and_status;
+
+ /* Here no need to validate channel number, since it is taken
+ from the interrupt register instead channel structure
+ should be validated */
+ if (dev->rx_is_open[channel] == FALSE) {
+ *handle_pkts_and_status = 0;
+ return EMAC_ERR_RX_CH_NOT_OPEN;
+ }
+
+ /* check if channel teardown pending */
+ rx_cppi = dev->rx_cppi[channel];
+ if (dev->rx_teardown_pending[channel] == TRUE) {
+ *handle_pkts_and_status = 0;
+ return 0;
+ }
+#ifdef EMAC_GETSTATS
+ ++rx_cppi->proc_count;
+#endif
+ *handle_pkts_and_status = 0;
+ pkts_processed = 0;
+
+ spin_lock_irqsave(&dev->rx_lock, flags);
+
+ pkt_obj.buf_list = &buf_obj;
+ curr_pkt = &pkt_obj;
+ curr_bd = rx_cppi->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 < pkts_to_be_processed)) {
+
+ /* allocate new buffer */
+ new_buffer =
+ (void *)(emac_net_alloc_rx_buf(dev,
+ rx_cppi->ch_info.buf_size,
+ &new_buf_token, 0, NULL));
+ if (new_buffer == NULL) {
+ /* no buffer available. return error with packets
+ pending */
+#ifdef EMAC_GETSTATS
+ ++rx_cppi->out_of_rx_buffers;
+#endif
+ 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;
+ 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);
+ /* acknowledge RX interrupt for the channel */
+ davinci_writel(EMAC_VIRT_TO_PHYS(curr_bd),
+ dev->emac_regs_base + EMAC_RX_CP_REG(channel));
+
+#ifdef EMAC_GETSTATS
+ ++rx_cppi->processed_bd;
+#endif
+ last_bd = curr_bd;
+ curr_bd = last_bd->next;
+ rx_cppi->active_queue_head = curr_bd;
+
+ /* check if end of RX queue ? */
+ if (frame_status & EMAC_CPPI_EOQ_BIT) {
+ if (curr_bd) {
+ /* misqueued packet */
+#ifdef EMAC_GETSTATS
+ ++rx_cppi->mis_queued_packets;
+#endif
+ davinci_writel(EMAC_VIRT_TO_PHYS(curr_bd),
+ dev->emac_regs_base
+ + EMAC_RX_HDP_REG(channel));
+
+ } else {
+ /* end of queue */
+#ifdef EMAC_GETSTATS
+ ++rx_cppi->end_of_queue;
+#endif
+ rx_cppi->queue_active = FALSE; /* clear
+ software RX queue */
+ }
+ }
+
+ /* recycle BD */
+ emac_add_bdto_rx_queue(_dev, rx_cppi, 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(&dev->rx_lock, flags);
+ emac_net_rx_cb(dev, curr_pkt, (void *)channel);
+ spin_lock_irqsave(&dev->rx_lock, flags);
+
+ curr_bd = rx_cppi->active_queue_head;
+ if (curr_bd) {
+ BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
+ frame_status = curr_bd->mode;
+ }
+ ++pkts_processed;
+ }
+
+ if ((curr_bd) && ((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0))
+ *handle_pkts_and_status = 1;
+
+end_emac_rx_bdproc:
+ spin_unlock_irqrestore(&dev->rx_lock, flags);
+ return pkts_processed;
+}
+
+#endif /* !EMAC_MULTIFRAGMENT */
+
+/**
+ * Linux 2.6 Kernel Ethernet Poll function Call only RX processing in
+ * the poll function - TX is taken care of in interrupt context
+ */
+static int emac_poll(struct napi_struct *napi, int budget)
+{
+ struct emac_dev_s *dev = container_of(napi, struct emac_dev_s, napi);
+ struct net_device *netdev = dev->owner;
+
+ unsigned int pkts_pending = 0;
+ /* this is used to pass the rx packets to be processed and
+ *return the number of rx packets processed */
+ struct rx_tx_params *napi_params = &dev->napi_rx_tx;
+
+ if (!dev->set_to_close) {
+ napi_params->rx_pkts = budget;
+ napi_params->tx_pkts = EMAC_DEFAULT_TX_MAX_SERVICE;
+
+ /* process packets - call the DDC packet processing function */
+ emac_pkt_process(dev, &pkts_pending, napi_params);
+
+ /* if more packets reschedule the tasklet or call
+ *pkt_process_end */
+ if (!pkts_pending) {
+ if (test_bit(NAPI_STATE_SCHED, &napi->state))
+ netif_rx_complete(netdev, napi);
+ emac_pkt_process_end(dev, NULL);
+ }
+ }
+
+ /* we are closing down, so dont process anything */
+ return napi_params->ret_rx_pkts;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+
+/**
+ * Polling receive - used by netconsole and other diagnostic tools
+ * to allow network i/o with interrupts disabled.
+ */
+void emac_poll_controller(struct net_device *netdev)
+{
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ disable_irq(netdev->irq);
+ emac_hal_isr(netdev->irq, dev);
+ enable_irq(netdev->irq);
+}
+#endif
+
+/* allocate RX buffer */
+void *emac_net_alloc_rx_buf(struct emac_dev_s *dev, int buf_size,
+ void **data_token,
+ u32 channel, void *alloc_args)
+{
+ struct net_device *netdev = dev->owner;
+ struct sk_buff *p_skb;
+
+ p_skb = dev_alloc_skb(dev->rx_buf_size);
+ if (p_skb == NULL) {
+#ifdef EMAC_DEBUG
+ ERR("emac_net_alloc_rx_buf:Failed to allocate skb for %s.\n",
+ netdev->name);
+#endif
+ return NULL;
+ }
+
+ /* set device pointer in skb and reserve space for extra bytes */
+ p_skb->dev = netdev;
+ skb_reserve(p_skb, dev->rx_buf_offset);
+
+ /* set the data token */
+ *data_token = (void *) p_skb;
+#ifdef EMAC_CACHE_INVALIDATE_FIX
+ /* invalidate buffer */
+ EMAC_CACHE_INVALIDATE((unsigned long)p_skb->data, buf_size);
+#endif
+
+ return p_skb->data;
+}
+
+/* free RX buffer */
+static int emac_net_free_rx_buf(struct emac_dev_s *dev, void *buffer,
+ void *data_token,
+ u32 channel, void *free_args)
+{
+ dev_kfree_skb_any((struct sk_buff *)data_token);
+ return EMAC_SUCCESS;
+}
+
+
+/**
+ * Packet receive notification
+ *
+ * This function gets received packet via the netPktList and
+ * it queues the packet into the higher layer queue
+ *
+ * Note that rxArgs contains "channel" and is ignored for this
+ * implementation
+ */
+static int emac_net_rx_cb(struct emac_dev_s *dev,
+ struct net_pkt_obj *net_pkt_list,
+ void *rx_args)
+{
+ 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);
+#ifndef EMAC_CACHE_INVALIDATE_FIX
+ /* invalidate cache */
+ EMAC_CACHE_INVALIDATE((unsigned long)p_skb->data, p_skb->len);
+#endif
+ p_skb->protocol = eth_type_trans(p_skb, dev->owner);
+ p_skb->dev->last_rx = jiffies;
+ netif_receive_skb(p_skb);
+ dev->net_dev_stats.rx_bytes += net_pkt_list->pkt_length;
+ dev->net_dev_stats.rx_packets++;
+
+ return 0;
+}
+
+
+/* transmit complete callback */
+static int emac_net_tx_complete(struct emac_dev_s *dev,
+ void **net_data_tokens,
+ int num_tokens, u32 channel)
+{
+ u32 cnt;
+
+ if (num_tokens && netif_queue_stopped(dev->owner))
+ netif_start_queue(dev->owner);
+
+ for (cnt = 0; cnt < num_tokens; cnt++) {
+ struct sk_buff *skb = (struct sk_buff *)net_data_tokens[cnt];
+ if (skb != NULL) {
+ dev->net_dev_stats.tx_packets++;
+ dev->net_dev_stats.tx_bytes += skb->len;
+ dev_kfree_skb_any(skb);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Interrupt functions
+ */
+
+irqreturn_t emac_hal_isr(int irq, void *dev_id)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) dev_id;
+
+ ++dev->isr_count;
+ if (!dev->set_to_close) /* NAPI support */
+ netif_rx_schedule(dev->owner, &dev->napi);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * transmit function - only single fragment supported
+ */
+static int emac_dev_tx(struct sk_buff *skb, struct net_device *netdev)
+{
+ int ret_code;
+ /* buffer object - only single frame support */
+ struct net_buf_obj tx_buf;
+ struct net_pkt_obj tx_packet; /* packet object */
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+ /* ANANT HACK: unsigned long flags; */
+
+ /* 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;
+
+ /* flush data buffer if write back mode */
+ EMAC_CACHE_WRITEBACK((unsigned long)skb->data, skb->len);
+ netdev->trans_start = jiffies;
+
+ /* ANANT_HACK: Need to lock TX so that there is no contention
+ spin_lock_irqsave(&hDDA->lock, flags);
+ */
+
+ /* DDC send : last param FALSE so that hardware calculates CRC */
+ ret_code = emac_send(dev, &tx_packet, EMAC_DEFAULT_TX_CHANNEL, FALSE);
+
+ /* ANANT_HACK: Need to un-lock TX so that there is no contention
+ between two processes
+ spin_unlock_irqrestore(&hDDA->lock, flags);
+ */
+
+ if (ret_code == EMAC_SUCCESS)
+ return 0;
+ if (ret_code == EMAC_ERR_TX_OUT_OF_BD) {
+ ERR("WARN: emac_dev_tx: Out of TX BD's\n");
+ netif_stop_queue(dev->owner);
+ }
+ dev->net_dev_stats.tx_dropped++;
+ return -1;
+}
+
+/*
+ * Linux Driver Model
+ */
+
+
+static ssize_t emac_show_version(struct device_driver *drv, char *buf)
+{
+ return emac_p_get_version(buf, NULL, 0, 4096, NULL, NULL);
+}
+
+static DRIVER_ATTR(version, S_IRUGO, emac_show_version, NULL);
+
+
+
+/**
+ * probe number of EMAC instances and register net_device structure
+ */
+static int __devinit emac_dev_probe(struct device *ddev)
+{
+ struct platform_device *pdev = to_platform_device(ddev);
+ int ret_val = 0;
+ int unit;
+ /*int instance_count = EMAC_MAX_INSTANCES;*/
+
+ /* obtain clock rate from kernel clock API's */
+ struct resource *dev_res =
+ platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct resource *wrap_res =
+ platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ int irq_res = platform_get_irq(pdev, 0);
+
+ if (!dev_res) {
+ printk(KERN_NOTICE "TI DAVINCI EMAC: fail to get resource\n");
+ return -EINVAL;
+ }
+
+ if (!wrap_res) {
+ printk(KERN_NOTICE "TI DAVINCI EMAC: fail to get resource"
+ " for wrap regs\n");
+ return -EINVAL;
+ }
+
+ if (!irq_res) {
+ printk(KERN_NOTICE "TI DAVINCI EMAC: fail to get resource"
+ " for irq\n");
+ return -EINVAL;
+ }
+
+ emac_clk = clk_get(0, "EMACCLK");
+ if (IS_ERR(emac_clk)) {
+ printk("TI DAVINCI EMAC: Failed to get clock. Exiting\n");
+ return -EINVAL;
+ }
+
+ clk_enable(emac_clk);
+ emac_bus_frequency = clk_get_rate(emac_clk);
+
+ for (unit = 0; unit < EMAC_MAX_INSTANCES; unit++) {
+ struct emac_dev_s *dev;
+ int failed;
+ struct net_device *netdev =
+ alloc_etherdev(sizeof(struct emac_dev_s));
+ if (!netdev) {
+ printk(KERN_NOTICE
+ "TI DaVinci EMAC: Etherdev alloc failed for device inst %d.\n", unit);
+
+ ret_val = -ENOMEM;
+ /* if error, free EMAC clock */
+ clk_disable(emac_clk);
+ break;
+ }
+ dev = NETDEV_PRIV(netdev);
+ dev->owner = netdev;
+ dev->instance_num = unit;
+ dev->emac_regs_base = dev_res->start;
+ dev->emac_wrap_regs_base = wrap_res->start;
+ dev->irq_line = irq_res;
+ netdev->init = emac_dev_init;
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+ emac_net_dev[dev->instance_num] = netdev;
+#if defined CONFIG_EMAC_INIT_BUF_MALLOC
+ g_init_enable_flag = 1;
+#endif
+ emac_p_detect_manual_cfg(cfg_link_speed, cfg_link_mode,
+ debug_mode);
+ if (emac_cfg_probe()) {
+ printk("TI DAVINCI EMAC: Error in configuration.\n");
+ return (-1);
+ }
+ netif_napi_add(netdev, &dev->napi, emac_poll,
+ EMAC_DEFAULT_RX_MAX_SERVICE);
+
+ /* register the network device with linux */
+ failed = register_netdev(netdev);
+
+ if (failed) {
+ ERR("Could not register device: %d\n", failed);
+
+ ret_val = -1;
+
+ clk_disable(emac_clk);
+
+ FREE_NETDEV(netdev);
+ break;
+ } else {
+ dev->next_device = last_emac_device;
+ last_emac_device = netdev;
+ DBG("%s irq=%2d io=%04x\n",
+ netdev->name, (int)netdev->irq,
+ (int)netdev->base_addr);
+#ifdef EMAC_DEBUG
+ create_proc_read_entry("net/emac_rfc2665_stats", 0,
+ NULL, emac_p_read_rfc2665_stats,
+ netdev);
+#endif
+ }
+ }
+
+ if (ret_val == 0) {
+ /* to maintain backward compatibility with NSP. */
+#ifdef EMAC_DEBUG
+ gp_stats_file = create_proc_entry("net/emac_stats", 0644, NULL);
+ if (gp_stats_file) {
+ gp_stats_file->read_proc = emac_p_read_stats;
+ gp_stats_file->write_proc = emac_p_write_stats;
+ }
+
+ create_proc_read_entry("net/emac_ver", 0, NULL,
+ emac_p_get_version, NULL);
+
+ create_proc_read_entry("net/emac_config", 0, NULL,
+ emac_dump_config, NULL);
+ create_proc_read_entry("net/emac_link", 0, NULL,
+ emac_p_read_link, NULL);
+#endif
+ }
+ emac_devices_installed = unit;
+
+ printk(KERN_INFO"%s\n", emac_version_string);
+ printk(KERN_INFO"TI DaVinci EMAC: Installed %d instances.\n", unit);
+#if defined CONFIG_EMAC_INIT_BUF_MALLOC
+ printk
+ ("TI DAVINCI EMAC driver is allocating buffer memory at init time.\n");
+#endif
+
+ return ((unit >= 0) ? 0 : -ENODEV);
+}
+
+/* structure describing the EMAC driver */
+static struct device_driver emac_driver = {
+ .name = "ti_davinci_emac",
+ .bus = &platform_bus_type,
+ .probe = emac_dev_probe,
+ .remove = NULL, /* TODO: findout when probe would be called. */
+ .suspend = NULL,
+ .resume = NULL,
+};
+
+
+/*
+ * Linux Module Init/Exit
+ */
+
+static int __init emac_driver_init(void)
+{
+ register int rs = driver_register(&emac_driver);
+ if (rs)
+ return rs;
+
+ if (driver_create_file(&emac_driver, &driver_attr_version))
+ printk(KERN_NOTICE "TI DaVinci EMAC: fail to create sysfs node\n");
+ return 0;
+}
+
+static void emac_exit(void)
+{
+ struct net_device *netdev;
+ struct emac_dev_s *dev;
+ int ret_code;
+
+ while (emac_devices_installed) {
+ char proc_name[100];
+ int proc_category_name_len = 0;
+
+ netdev = last_emac_device;
+ dev = NETDEV_PRIV(netdev);
+
+ DBG("Unloading %s irq=%2d io=%04x\n",
+ netdev->name, (int)netdev->irq, (int)netdev->base_addr);
+
+ /* free EMAC clock */
+ clk_disable(emac_clk);
+
+ if (g_init_enable_flag)
+ emac_p_dev_disable(dev);
+
+ /* deinit DDC */
+ ret_code = emac_de_init(dev, NULL);
+
+ if (ret_code != EMAC_SUCCESS)
+ ERR("Error %08X from Deinit()\n", ret_code);
+ /*
+ * we dont want to quit from here, lets delete
+ * the instance also
+ */
+
+ /* delete the proc entry */
+ strcpy(proc_name, "davinci/");
+ strcat(proc_name, netdev->name);
+ proc_category_name_len = strlen(proc_name);
+ strcpy(proc_name + proc_category_name_len, "_rfc2665_stats");
+ remove_proc_entry(proc_name, NULL);
+
+ /* release memory region and unregister the device */
+ release_mem_region(netdev->base_addr, EMAC_DEFAULT_EMAC_SIZE);
+ unregister_netdev(netdev);
+
+ last_emac_device = dev->next_device;
+ if (netdev)
+ FREE_NETDEV(netdev);
+
+ emac_devices_installed--;
+ }
+
+#ifdef EMAC_DEBUG
+ if (gp_stats_file)
+ remove_proc_entry("net/emac_stats", NULL);
+
+ remove_proc_entry("net/emac_link", NULL);
+ remove_proc_entry("net/emac_ver", NULL);
+ remove_proc_entry("net/emac_config", NULL);
+#endif
+
+}
+
+module_init(emac_driver_init);
+module_exit(emac_exit);
Index: 2.6.24-rc8.ether/drivers/net/arm/davinci_emac.h
===================================================================
--- /dev/null
+++ 2.6.24-rc8.ether/drivers/net/arm/davinci_emac.h
@@ -0,0 +1,1560 @@
+/**
+ * Copyright (C) 2008 MontaVista Software, Inc. <source@mvista.com>
+ *
+ * This file is licensed under the terms of the
+ * GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ */
+
+#include "davinci_emac_phy.h"
+#include <asm/arch/hardware.h>
+
+/* version info */
+#define EMAC_MAJOR_VERSION 4
+#define EMAC_MINOR_VERSION 0
+#define EMAC_MODULE_VERSION "4.0"
+
+/* Debug options */
+#define EMAC_DEBUG
+
+#define EMAC_CACHE_WRITEBACK_MODE
+#define EMAC_CACHE_INVALIDATE_FIX
+
+#define TRUE ((bool) 1)
+#define FALSE ((bool) 0)
+
+#define EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+
+/* NO PHY used in case of external ethernet switches */
+#define CONFIG_EMAC_NOPHY 9999
+
+/* DaVinci specific configuration */
+#define EMAC_BASE_ADDR IO_ADDRESS(DAVINCI_EMAC_CNTRL_REGS_BASE)
+#define EMAC_WRAPPER_REGS_ADDR IO_ADDRESS(DAVINCI_EMAC_WRAPPER_CNTRL_REGS_BASE)
+#define EMAC_WRAPPER_RAM_ADDR IO_ADDRESS(DAVINCI_EMAC_WRAPPER_RAM_BASE)
+#define EMAC_WRAPPER_RAM_SIZE (8 << 10)
+#define EMAC_MDIO_BASE_ADDR IO_ADDRESS(DAVINCI_MDIO_CNTRL_REGS_BASE)
+
+#define EMAC_INTERRUPT 13
+#define EMAC_BUS_FREQUENCY 76500000 /* PLL/6 i.e 76.5 MHz */
+#define EMAC_MDIO_FREQUENCY 2200000 /* PHY bus frequency */
+#define EMAC_PHY_MASK 0x2 /* PHY chip is located at address 1 */
+
+/*
+ * Note: For DaVinci, Buffer Descriptors are located in Wrapper RAM
+ * (4K). Half of the Wrapper memory is for RX BD's and other half for
+ * TX BD's
+ */
+#define EMAC_TX_BD_MEM EMAC_WRAPPER_RAM_ADDR
+#define EMAC_RX_BD_MEM \
+ (EMAC_WRAPPER_RAM_ADDR + (EMAC_WRAPPER_RAM_SIZE >> 1))
+
+/*
+ * If multi packet Tx complete notifications is enabled (via
+ * EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY), Max number of Tx packets that
+ * can be notified - the actual number will depend upon user
+ * configuration for parameter "maxPktsToProcess"
+ */
+/* feature macros here */
+#define EMAC_MAX_TX_COMPLETE_PKTS_TO_NOTIFY 8
+
+/* config macros */
+#define EMAC_MAX_INSTANCES 1
+#define EMAC_MIN_ETHERNET_PKT_SIZE 60
+
+/**
+ * max RX fragments calculation - 1500 byte packet and 64 byte
+ * buffer. fragments=1500/64=24
+ */
+#define EMAC_MAX_RX_FRAGMENTS 24
+
+/* theoratically TX max fragments are equal to 24 */
+#define EMAC_MAX_TX_FRAGMENTS 8
+
+/* EMAC hardware specific */
+#define EMAC_RESET_CLOCKS_WAIT 64
+#define EMAC_MAX_TX_CHANNELS 8
+#define EMAC_MAX_RX_CHANNELS 8
+#define EMAC_MIN_FREQUENCY_FOR_10MBPS 5500000
+#define EMAC_MIN_FREQUENCY_FOR_100MBPS 55000000
+#define EMAC_MIN_FREQUENCY_FOR_1000MBPS 125000000
+
+/*
+ * The following are EMAC registers which have been removed from the
+ * CPGMAC register map. Thus we access them using macros to avoid
+ * having more CSL register overlay structures for older EMAC register
+ * map.
+ */
+
+/* statistics clear value */
+#define EMAC_NUM_STAT_REGS 36
+#define EMAC_STAT_CLEAR 0xFFFFFFFF
+
+/* EMAC all multicast set register value */
+#define EMAC_ALL_MULTI_REG_VALUE 0xFFFFFFFF
+
+/* EMAC number of multicast bits that can be set/cleared - currently
+ 64 bits - hash1/2 regs */
+#define EMAC_NUM_MULTICAST_BITS 64
+
+/* EMAC teardown value */
+#define EMAC_TEARDOWN_VALUE 0xFFFFFFFC
+
+/* TX / RX control bits */
+#define EMAC_TX_CONTROL_TX_ENABLE_VAL 0x1
+#define EMAC_RX_CONTROL_RX_ENABLE_VAL 0x1
+
+/* host interrupt bits */
+#define EMAC_MAC_HOST_ERR_INTMASK_VAL 0x2
+#define EMAC_MAC_STAT_INT_INTMASK_VAL 0x1
+
+/* rx config masks */
+#define EMAC_RX_UNICAST_CLEAR_ALL 0xFF
+
+/* type 0 address filtering macros */
+#define EMAC_TYPE_0_MACSRCADDR0_MASK 0xFF
+#define EMAC_TYPE_0_MACSRCADDR0_SHIFT 0
+#define EMAC_TYPE_0_MACSRCADDR1_MASK 0xFF
+#define EMAC_TYPE_0_MACSRCADDR1_SHIFT 0
+
+#define EMAC_TYPE_0_MACSRCADDR2_MASK (0xFF<<24)
+#define EMAC_TYPE_0_MACSRCADDR2_SHIFT 24
+#define EMAC_TYPE_0_MACSRCADDR3_MASK (0xFF<<16)
+#define EMAC_TYPE_0_MACSRCADDR3_SHIFT 16
+#define EMAC_TYPE_0_MACSRCADDR4_MASK (0xFF<<8)
+#define EMAC_TYPE_0_MACSRCADDR4_SHIFT 8
+#define EMAC_TYPE_0_MACSRCADDR5_MASK 0xFF
+#define EMAC_TYPE_0_MACSRCADDR5_SHIFT 0
+
+/* type 1 address filtering macros */
+#define EMAC_TYPE_1_MACSRCADDR0_MASK (0xFF<<8)
+#define EMAC_TYPE_1_MACSRCADDR0_SHIFT 8
+#define EMAC_TYPE_1_MACSRCADDR1_MASK 0xFF
+#define EMAC_TYPE_1_MACSRCADDR1_SHIFT 0
+
+#define EMAC_TYPE_1_MACSRCADDR2_MASK (0xFF<<24)
+#define EMAC_TYPE_1_MACSRCADDR2_SHIFT 24
+#define EMAC_TYPE_1_MACSRCADDR3_MASK (0xFF<<16)
+#define EMAC_TYPE_1_MACSRCADDR3_SHIFT 16
+#define EMAC_TYPE_1_MACSRCADDR4_MASK (0xFF<<8)
+#define EMAC_TYPE_1_MACSRCADDR4_SHIFT 8
+#define EMAC_TYPE_1_MACSRCADDR5_MASK 0xFF
+#define EMAC_TYPE_1_MACSRCADDR5_SHIFT 0
+
+/* CP(G)MAC address filtering bit macros */
+#define CPGMAC_VALID_MASK (0x1<<20)
+#define CPGMAC_VALID_SHIFT 20
+#define CPGMAC_MATCH_FILTER_MASK (0x1<<19)
+#define CPGMAC_MATCH_FILTER_SHIFT 19
+#define CPGMAC_CHANNEL_MASK (0x7<<16)
+#define CPGMAC_CHANNEL_SHIFT 16
+#define CPGMAC_TYPE_2_3_MACSRCADDR0_MASK (0xFF<<8)
+#define CPGMAC_TYPE_2_3_MACSRCADDR0_SHIFT 8
+#define CPGMAC_TYPE_2_3_MACSRCADDR1_MASK 0xFF
+#define CPGMAC_TYPE_2_3_MACSRCADDR1_SHIFT 0
+
+#define CPGMAC_TYPE_2_3_MACSRCADDR2_MASK (0xFF<<24)
+#define CPGMAC_TYPE_2_3_MACSRCADDR2_SHIFT 24
+#define CPGMAC_TYPE_2_3_MACSRCADDR3_MASK (0xFF<<16)
+#define CPGMAC_TYPE_2_3_MACSRCADDR3_SHIFT 16
+#define CPGMAC_TYPE_2_3_MACSRCADDR4_MASK (0xFF<<8)
+#define CPGMAC_TYPE_2_3_MACSRCADDR4_SHIFT 8
+#define CPGMAC_TYPE_2_3_MACSRCADDR5_MASK 0xFF
+#define CPGMAC_TYPE_2_3_MACSRCADDR5_SHIFT 0
+
+/* RX MBP register bit positions */
+#define EMAC_RXMBP_PASSCRC_SHIFT 30
+#define EMAC_RXMBP_PASSCRC_MASK (0x1 << 30)
+#define EMAC_RXMBP_QOSEN_SHIFT 29
+#define EMAC_RXMBP_QOSEN_MASK (0x1 << 29)
+#define EMAC_RXMBP_NOCHAIN_SHIFT 28
+#define EMAC_RXMBP_NOCHAIN_MASK (0x1 << 28)
+#define EMAC_RXMBP_CMFEN_SHIFT 24
+#define EMAC_RXMBP_CMFEN_MASK (0x1 << 24)
+#define EMAC_RXMBP_CSFEN_SHIFT 23
+#define EMAC_RXMBP_CSFEN_MASK (0x1 << 23)
+#define EMAC_RXMBP_CEFEN_SHIFT 22
+#define EMAC_RXMBP_CEFEN_MASK (0x1 << 22)
+#define EMAC_RXMBP_CAFEN_SHIFT 21
+#define EMAC_RXMBP_CAFEN_MASK (0x1 << 21)
+#define EMAC_RXMBP_PROMCH_SHIFT 16
+#define EMAC_RXMBP_PROMCH_MASK (0x7 << 16)
+#define EMAC_RXMBP_BROADEN_SHIFT 13
+#define EMAC_RXMBP_BROADEN_MASK (0x1 << 13)
+#define EMAC_RXMBP_BROADCH_SHIFT 8
+#define EMAC_RXMBP_BROADCH_MASK (0x7 << 8)
+#define EMAC_RXMBP_MULTIEN_SHIFT 5
+#define EMAC_RXMBP_MULTIEN_MASK (0x1 << 5)
+#define EMAC_RXMBP_MULTICH_SHIFT 0
+#define EMAC_RXMBP_MULTICH_MASK 0x7
+
+#define EMAC_RXMBP_CHMASK 0x7
+
+/* mac control register bit fields */
+#define EMAC_MACCONTROL_TXSHORTGAPEN_SHIFT 10
+#define EMAC_MACCONTROL_TXSHORTGAPEN_MASK (0x1 << 10)
+#define EMAC_MACCONTROL_TXPTYPE_SHIFT 9
+#define EMAC_MACCONTROL_TXPTYPE_MASK (0x1 << 9)
+#define EMAC_MACCONTROL_GIGABITEN_SHIFT 7
+#define EMAC_MACCONTROL_GIGABITEN_MASK (0x1 << 7)
+#define EMAC_MACCONTROL_TXPACEEN_SHIFT 6
+#define EMAC_MACCONTROL_TXPACEEN_MASK (0x1 << 6)
+#define EMAC_MACCONTROL_MIIEN_SHIFT 5
+#define EMAC_MACCONTROL_MIIEN_MASK (0x1 << 5)
+#define EMAC_MACCONTROL_TXFLOWEN_SHIFT 4
+#define EMAC_MACCONTROL_TXFLOWEN_MASK (0x1 << 4)
+#define EMAC_MACCONTROL_RXFLOWEN_SHIFT 3
+#define EMAC_MACCONTROL_RXFLOWEN_MASK (0x1 << 3)
+#define EMAC_MACCONTROL_LOOPBKEN_SHIFT 1
+#define EMAC_MACCONTROL_LOOPBKEN_MASK (0x1 << 1)
+#define EMAC_MACCONTROL_FULLDUPLEXEN_SHIFT 0
+#define EMAC_MACCONTROL_FULLDUPLEXEN_MASK 0x1
+
+/* mac_status register */
+#define EMAC_MACSTATUS_TXERRCODE_MASK 0xF00000
+#define EMAC_MACSTATUS_TXERRCODE_SHIFT 20
+#define EMAC_MACSTATUS_TXERRCH_MASK 0x7
+#define EMAC_MACSTATUS_TXERRCH_SHIFT 16
+#define EMAC_MACSTATUS_RXERRCODE_MASK 0xF000
+#define EMAC_MACSTATUS_RXERRCODE_SHIFT 12
+#define EMAC_MACSTATUS_RXERRCH_MASK 0x7
+#define EMAC_MACSTATUS_RXERRCH_SHIFT 8
+
+/* EMAC RX max packet length mask */
+#define EMAC_RX_MAX_LEN_SHIFT 0
+#define EMAC_RX_MAX_LEN_MASK 0xFFFF
+
+/* EMAC RX max packet length mask */
+#define EMAC_RX_BUFFER_OFFSET_SHIFT 0
+#define EMAC_RX_BUFFER_OFFSET_MASK 0xFFFF
+
+/* MAC_IN_VECTOR (0x180) register bit fields */
+#define EMAC_MAC_IN_VECTOR_HOST_INT 0x20000
+#define EMAC_MAC_IN_VECTOR_STATPEND_INT 0x10000
+#define EMAC_MAC_IN_VECTOR_RX_INT_VEC 0xFF00
+#define EMAC_MAC_IN_VECTOR_TX_INT_VEC 0xFF
+
+/* CPPI bit positions */
+#define EMAC_CPPI_SOP_BIT (1 << 31)
+#define EMAC_CPPI_EOP_BIT (1 << 30)
+#define EMAC_CPPI_OWNERSHIP_BIT (1 << 29)
+#define EMAC_CPPI_EOQ_BIT (1 << 28)
+#define EMAC_CPPI_TEARDOWN_COMPLETE_BIT (1 << 27)
+#define EMAC_CPPI_PASS_CRC_BIT (1 << 26)
+
+/* defining the macro EMAC_INSTANCE_CODE to 0 so that it can be usable in DDA*/
+#define EMAC_INSTANCE_CODE 0
+#define EMAC_ERROR_CODE (EMAC_INSTANCE_CODE << 16)
+#define EMAC_ERROR_INFO EMAC_ERROR_CODE
+#define EMAC_ERROR_WARNING (EMAC_ERROR_CODE | 0x10000000)
+#define EMAC_ERROR_MINOR (EMAC_ERROR_CODE | 0x20000000)
+#define EMAC_ERROR_MAJOR (EMAC_ERROR_CODE | 0x30000000)
+#define EMAC_ERROR_CRITICAL (EMAC_ERROR_CODE | 0x40000000)
+
+/* EMAC success code */
+#define EMAC_SUCCESS 0
+
+/* EMAC error codes */
+#define EMAC_ERR_DEV_ALREADY_INSTANTIATED(inst_id) \
+ (0x30000000 + ((inst_id) << 16))
+#define EMAC_ERR_DEV_NOT_INSTANTIATED (EMAC_ERROR_MAJOR + 1)
+#define EMAC_INVALID_PARAM (EMAC_ERROR_MAJOR + 2)
+#define EMAC_ERR_TX_CH_INVALID (EMAC_ERROR_CRITICAL + 3)
+#define EMAC_ERR_TX_CH_ALREADY_INIT (EMAC_ERROR_MAJOR + 4)
+#define EMAC_ERR_TX_CH_ALREADY_CLOSED (EMAC_ERROR_MAJOR + 5)
+#define EMAC_ERR_TX_CH_NOT_OPEN (EMAC_ERROR_MAJOR + 6)
+#define EMAC_ERR_TX_NO_LINK (EMAC_ERROR_MAJOR + 7)
+#define EMAC_ERR_TX_OUT_OF_BD (EMAC_ERROR_MAJOR + 8)
+#define EMAC_ERR_RX_CH_INVALID (EMAC_ERROR_CRITICAL + 9)
+#define EMAC_ERR_RX_CH_ALREADY_INIT (EMAC_ERROR_MAJOR + 10)
+#define EMAC_ERR_RX_CH_ALREADY_CLOSED (EMAC_ERROR_MAJOR + 11)
+#define EMAC_ERR_RX_CH_NOT_OPEN (EMAC_ERROR_MAJOR + 12)
+#define EMAC_ERR_DEV_ALREADY_CREATED (EMAC_ERROR_MAJOR + 13)
+#define EMAC_ERR_DEV_NOT_OPEN (EMAC_ERROR_MAJOR + 14)
+#define EMAC_ERR_DEV_ALREADY_CLOSED (EMAC_ERROR_MAJOR + 15)
+#define EMAC_ERR_DEV_ALREADY_OPEN (EMAC_ERROR_MAJOR + 16)
+#define EMAC_ERR_RX_BUFFER_ALLOC_FAIL (EMAC_ERROR_CRITICAL + 17)
+
+/*
+ * ioctls
+ */
+#define EMAC_IOCTL_BASE 0
+
+#define EMAC_IOCTL_GET_STATISTICS (EMAC_IOCTL_BASE + 0)
+#define EMAC_IOCTL_CLR_STATISTICS (EMAC_IOCTL_BASE + 1)
+#define EMAC_IOCTL_GET_SWVER (EMAC_IOCTL_BASE + 2)
+#define EMAC_IOCTL_GET_HWVER (EMAC_IOCTL_BASE + 3)
+#define EMAC_IOCTL_SET_RXCFG (EMAC_IOCTL_BASE + 4)
+#define EMAC_IOCTL_SET_MACCFG (EMAC_IOCTL_BASE + 5)
+#define EMAC_IOCTL_GET_STATUS (EMAC_IOCTL_BASE + 6)
+#define EMAC_IOCTL_READ_PHY_REG (EMAC_IOCTL_BASE + 7)
+#define EMAC_IOCTL_WRITE_PHY_REG (EMAC_IOCTL_BASE + 8)
+#define EMAC_IOCTL_MULTICAST_ADDR (EMAC_IOCTL_BASE + 9)
+#define EMAC_IOCTL_ALL_MULTI (EMAC_IOCTL_BASE + 10)
+#define EMAC_IOCTL_TYPE2_3_FILTERING (EMAC_IOCTL_BASE + 11)
+#define EMAC_IOCTL_SET_MAC_ADDRESS (EMAC_IOCTL_BASE + 12)
+#define EMAC_IOCTL_IF_COUNTERS (EMAC_IOCTL_BASE + 13)
+#define EMAC_IOCTL_ETHER_COUNTERS (EMAC_IOCTL_BASE + 14)
+#define EMAC_IOCTL_IF_PARAMS_UPDT (EMAC_IOCTL_BASE + 15)
+
+#define EMAC_IOCTL_TIMER_START (EMAC_IOCTL_BASE + 16)
+#define EMAC_IOCTL_TIMER_STOP (EMAC_IOCTL_BASE + 17)
+#define EMAC_IOCTL_STATUS_UPDATE (EMAC_IOCTL_BASE + 18)
+#define EMAC_IOCTL_MIB64_CNT_TIMER_START (EMAC_IOCTL_BASE + 19)
+#define EMAC_IOCTL_MIB64_CNT_TIMER_STOP (EMAC_IOCTL_BASE + 20)
+
+#define EMAC_PRIV_FILTERING (EMAC_IOCTL_BASE + 21)
+#define EMAC_PRIV_MII_READ (EMAC_IOCTL_BASE + 22)
+#define EMAC_PRIV_MII_WRITE (EMAC_IOCTL_BASE + 23)
+#define EMAC_PRIV_GET_STATS (EMAC_IOCTL_BASE + 24)
+#define EMAC_PRIV_CLR_STATS (EMAC_IOCTL_BASE + 25)
+#define EMAC_EXTERNAL_SWITCH (EMAC_IOCTL_BASE + 26)
+
+/*
+ * MII module port settings
+ *
+ * DDA sets the Phy mode as a combination of the following in "phyMode"
+ * parameter in the init configuration structure
+ */
+
+/* bit 16 and above unused by MII register*/
+#define SNWAY_AUTOMDIX (1<<16)
+#define SNWAY_FD1000 (1<<13)
+#define SNWAY_HD1000 (1<<12)
+#define SNWAY_NOPHY (1<<10)
+#define SNWAY_LPBK (1<<9)
+#define SNWAY_FD100 (1<<8)
+#define SNWAY_HD100 (1<<7)
+#define SNWAY_FD10 (1<<6)
+#define SNWAY_HD10 (1<<5)
+#define SNWAY_AUTO (1<<0)
+#define SNWAY_AUTOALL (SNWAY_AUTO|SNWAY_FD100|SNWAY_FD10|SNWAY_HD100|
SNWAY_HD10)
+
+/**
+ * DDC Status Ioctl - Error status
+ *
+ * Note that each error code is a bit position so that multiple
+ * errors can be clubbed together and passed in a integer value
+ */
+#define EMAC_NO_ERROR 0
+#define EMAC_TX_HOST_ERROR 0x1 /* MSB 8 bits: err code, channel no */
+#define EMAC_RX_HOST_ERROR 0x8 /* LSB 8 bits: err code, channel no */
+
+#define EGRESS_TRAILOR_LEN 0
+
+#define CFG_START_LINK_SPEED SNWAY_AUTOALL /* auto nego */
+
+/* defaut configuration values required for passing on to DDC */
+#define EMAC_DEFAULT_MLINK_MASK 0
+#define EMAC_DEFAULT_PASS_CRC FALSE
+#define EMAC_DEFAULT_QOS_ENABLE FALSE
+#define EMAC_DEFAULT_NO_BUFFER_CHAINING FALSE
+#define EMAC_DEFAULT_COPY_MAC_CONTROL_FRAMES_ENABLE FALSE
+#define EMAC_DEFAULT_COPY_SHORT_FRAMES_ENABLE FALSE
+#define EMAC_DEFAULT_COPY_ERROR_FRAMES_ENABLE FALSE
+#define EMAC_DEFAULT_PROMISCOUS_CHANNEL 0
+#define EMAC_DEFAULT_BROADCAST_CHANNEL 0
+#define EMAC_DEFAULT_MULTICAST_CHANNEL 0
+#define EMAC_DEFAULT_BUFFER_OFFSET 0
+#define EMAC_DEFAULT_TX_PRIO_TYPE EMAC_TXPRIO_FIXED
+#define EMAC_DEFAULT_TX_SHORT_GAP_ENABLE FALSE
+#define EMAC_DEFAULT_TX_PACING_ENABLE FALSE
+#define EMAC_DEFAULT_MII_ENABLE TRUE
+#define EMAC_DEFAULT_TX_FLOW_ENABLE FALSE
+#define EMAC_DEFAULT_RX_FLOW_ENABLE FALSE
+#define EMAC_DEFAULT_LOOPBACK_ENABLE FALSE
+#define EMAC_DEFAULT_FULL_DUPLEX_ENABLE TRUE
+#define EMAC_DEFAULT_TX_INTERRUPT_DISABLE TRUE
+#define CONFIG_EMAC_MIB_TIMER_TIMEOUT 5000 /* 5 seconds */
+
+#define EMAC_DEFAULT_PROMISCOUS_ENABLE 0
+#define EMAC_DEFAULT_BROADCAST_ENABLE 1
+#define EMAC_DEFAULT_MULTICAST_ENABLE 1
+
+/* NOT EXPLICIT SUPPORT PROVIDED AS OF NOW - vlan support in the driver */
+#define EMAC_DEFAULT_VLAN_ENABLE FALSE
+
+/* system value for ticks per seconds */
+#define EMAC_TICKS_PER_SEC HZ
+
+/**
+ * Extra bytes for Cache alignment of skbuf - should be equal to
+ * processor cache line size - in case of ARM926 its 32 bytes
+ */
+#define EMAC_DEFAULT_EXTRA_RXBUF_SIZE 32
+
+/*
+ * default max frame size = 1522 = 1500 byte data + 14 byte eth header
+ * + 4 byte checksum + 4 byte vlan tag + 32 bytes for cache
+ * alignment
+ */
+#define EMAC_DEFAULT_MAX_FRAME_SIZE \
+(1500 + 14 + 4 + 4 + EGRESS_TRAILOR_LEN + EMAC_DEFAULT_EXTRA_RXBUF_SIZE)
+
+/* default number of TX channels */
+#define EMAC_DEFAULT_NUM_TX_CHANNELS 1
+
+/* default TX channel number */
+#define EMAC_DEFAULT_TX_CHANNEL 0
+
+/* default TX number of BD's/buffers */
+#define EMAC_DEFAULT_TX_NUM_BD 128
+
+/* default TX max service BD's */
+#define EMAC_DEFAULT_TX_MAX_SERVICE 32
+
+/* default number of RX channels */
+#define EMAC_DEFAULT_NUM_RX_CHANNELS 1
+
+/* default RX channel number */
+#define EMAC_DEFAULT_RX_CHANNEL 0
+
+#define EMAC_DEFAULT_RX_NUM_BD 128
+
+/* default RX max service BD's */
+#define EMAC_DEFAULT_RX_MAX_SERVICE 32 /* should = netdev->weight */
+
+#if ((EMAC_DEFAULT_TX_NUM_BD + EMAC_DEFAULT_RX_NUM_BD) > 256)
+#error "Error. DaVinci has space for no more than 256 TX+RX BD's"
+#endif
+
+/*
+ * Size of EMAC peripheral footprint in memory that needs to be
+ * reserved in Linux Note that this value is actually a hardware
+ * memory footprint value taken from the specs and ideally should have
+ * been in the csl files. Keeping it for convinience since EMAC
+ * peripheral footprint will not change unless the peripheral itself
+ * changes drastically and it will be called with a different name and
+ * will have a different driver anyway
+ *
+ * For Davinci size = control regs (4k) + wrapper regs (4k) + wrapper
+ * RAM (8k) + mdio regs (2k)
+ */
+#define EMAC_DEFAULT_EMAC_SIZE 0x4800
+
+/* ENV variable names for obtaining MAC addresses */
+#define EMAC_MAC_ADDR_A "maca"
+#define EMAC_MAC_ADDR_B "macb"
+#define EMAC_MAC_ADDR_C "macc"
+#define EMAC_MAC_ADDR_D "macd"
+#define EMAC_MAC_ADDR_E "mace"
+#define EMAC_MAC_ADDR_F "macf"
+
+/**
+ * Maximum multicast addresses list to be handled by the driver - If
+ * this is not restricted then the driver will spend considerable time
+ * in handling multicast lists
+ */
+#define EMAC_DEFAULT_MAX_MULTICAST_ADDRESSES 64
+
+#define NETDEV_PRIV(net_dev) netdev_priv(net_dev)
+#define FREE_NETDEV(net_dev) free_netdev(net_dev)
+
+#define dbg_print { if (emac_debug_mode) \
+ printk }
+#define err_print printk
+
+/* misc error codes */
+#define EMAC_INTERNAL_FAILURE (-1)
+
+/* LED codes required for controlling LED's */
+#define EMAC_LINK_OFF 0
+#define EMAC_LINK_ON 1
+#define EMAC_SPEED_100 2
+#define EMAC_SPEED_10 3
+#define EMAC_FULL_DPLX 4
+#define EMAC_HALF_DPLX 5
+#define EMAC_TX_ACTIVITY 6
+#define EMAC_RX_ACTIVITY 7
+
+
+#define EMAC_L3_ALIGN(size) emac_L3_align[(size) & 3]
+
+
+#define EMAC_4BYTE_ALIGN(size) emac_4byte_align[(size) & 0x3]
+
+#define EMAC_DEBUG_FUNCTION_ENTRY (0x1 << 1)
+#define EMAC_DEBUG_FUNCTION_EXIT (0x1 << 2)
+#define EMAC_DEBUG_BUSY_FUNCTION_ENTRY (0x1 << 3)
+#define EMAC_DEBUG_BUSY_FUNCTION_EXIT (0x1 << 4)
+#define EMAC_DEBUG_TX (0x1 << 6)
+#define EMAC_DEBUG_RX (0x1 << 7)
+#define EMAC_DEBUG_PORT_UPDATE (0x1 << 8)
+#define EMAC_DEBUG_MII (0x1 << 9)
+#define EMAC_DEBUG_TEARDOWN (0x1 << 10)
+
+/*
+ * Debug flags
+ *
+ * IMPORTANT NOTE: The debug flags need to be enabled carefully as it
+ * could flood the console/sink point of the debug traces and also
+ * affect the functionality of the overall system
+ */
+#ifdef EMAC_DEBUG
+#define LOG(lvl, format, args...) \
+ printk(lvl "%s:%d[%d]" format, __FUNCTION__, __LINE__, \
+ dev->init_cfg.inst_id, ##args)
+#define LOGERR(format, args...) LOG(KERN_ERR, format, ##args)
+#define LOGMSG(flag, format, args...) \
+ do { if (flag & emac_debug) LOG(KERN_DEBUG, #flag format, ##args); } while
(0)
+#define DBG(format, args...) \
+ do { if (emac_debug_mode) printk(KERN_DEBUG "davinci_emac: " format, ##args);
\
+ } while (0)
+#define ERR(format, args...) \
+ printk(KERN_ERR "ERROR: davinci_emac: " format, ##args)
+#else
+#define DBG(format, args...)
+#define ERR(format, args...)
+#define LOGERR(format, args...)
+#define LOGMSG(flag, format, args...)
+#endif
+
+/* DDC internal macros */
+#define EMAC_RX_BD_BUF_SIZE 0xFFFF;
+#define EMAC_BD_LENGTH_FOR_CACHE 16 /* only CPPI bytes */
+#define EMAC_RX_BD_PKT_LENGTH_MASK 0xFFFF
+
+#define CFG_START_LINK_SPEED SNWAY_AUTOALL /* auto nego */
+
+/* defaut configuration values required for passing on to DDC */
+#define EMAC_DEFAULT_MLINK_MASK 0
+#define EMAC_DEFAULT_PASS_CRC FALSE
+#define EMAC_DEFAULT_QOS_ENABLE FALSE
+#define EMAC_DEFAULT_NO_BUFFER_CHAINING FALSE
+#define EMAC_DEFAULT_COPY_MAC_CONTROL_FRAMES_ENABLE FALSE
+#define EMAC_DEFAULT_COPY_SHORT_FRAMES_ENABLE FALSE
+#define EMAC_DEFAULT_COPY_ERROR_FRAMES_ENABLE FALSE
+#define EMAC_DEFAULT_PROMISCOUS_CHANNEL 0
+#define EMAC_DEFAULT_BROADCAST_CHANNEL 0
+#define EMAC_DEFAULT_MULTICAST_CHANNEL 0
+#define EMAC_DEFAULT_BUFFER_OFFSET 0
+#define EMAC_DEFAULT_TX_PRIO_TYPE EMAC_TXPRIO_FIXED
+#define EMAC_DEFAULT_TX_SHORT_GAP_ENABLE FALSE
+#define EMAC_DEFAULT_TX_PACING_ENABLE FALSE
+#define EMAC_DEFAULT_MII_ENABLE TRUE
+#define EMAC_DEFAULT_TX_FLOW_ENABLE FALSE
+#define EMAC_DEFAULT_RX_FLOW_ENABLE FALSE
+#define EMAC_DEFAULT_LOOPBACK_ENABLE FALSE
+#define EMAC_DEFAULT_FULL_DUPLEX_ENABLE TRUE
+#define EMAC_DEFAULT_TX_INTERRUPT_DISABLE TRUE
+#define CONFIG_EMAC_MIB_TIMER_TIMEOUT 5000 /* 5 sec */
+
+#define EMAC_DEFAULT_PROMISCOUS_ENABLE 0
+#define EMAC_DEFAULT_BROADCAST_ENABLE 1
+#define EMAC_DEFAULT_MULTICAST_ENABLE 1
+
+/**
+ * structs, enums
+ */
+enum emac_drv_state {
+ DRV_CREATED,
+ DRV_INITIALIZED,
+ DRV_OPENED,
+ DRV_CLOSED,
+ DRV_DEINITIALIZED,
+ DRV_POWERED_DOWN,
+};
+
+/**
+ * Network Buffer Object
+ *
+ * Holds attributes of a buffer/fragment
+ *
+ * Send: Usually when the buffers are allocated by DDA, the
+ * Start of Packet token will be the handle to the whole packet. This
+ * token/handle should be good enough to free the packet or return to
+ * its pool. When the buffers are allocated by DDC, typically token
+ * for each buffer needs to be indicated (TxComplete) rather than
+ * only the Start of Packet token.
+ *
+ * Receive: For each buffer the token will be a handle to the buffer
+ * that can be used by the allocater (DDA or DDC) of the buffer to
+ * free it or return to a pool.
+ */
+struct net_buf_obj {
+ void *buf_token;
+ char *data_ptr;
+ int length;
+};
+
+/**
+ * Network Packet Object
+ *
+ * Holds attributes of a network packet (NetBufObjs and packet size).
+ */
+struct net_pkt_obj {
+ void *pkt_token; /* data token may hold tx/rx chan id */
+ struct net_buf_obj *buf_list; /* array of network buffer objects */
+ int num_bufs; /* number of network buffer objects */
+ int pkt_length; /* packet length (number of bytes) */
+};
+
+/**
+ * Net Channel State
+ *
+ * State of the channel (initialized, about to be closed, closed etc
+ */
+enum net_ch_dir {
+ NET_CH_DIR_TX = 0, /* transmit only */
+ NET_CH_DIR_RX, /* receive only */
+ NET_CH_DIR_BIDIRECTIONAL, /* bidirectonaly - TX/RX */
+ NET_CH_DIR_UNDEFINED /* not defined */
+};
+
+/**
+ * Net Channel State
+ *
+ * State of the channel (initialized, about to be closed, closed etc
+ */
+enum net_ch_state {
+ NET_CH_UNINITIALIZED = 0,
+ NET_CH_INITIALIZED,
+ NET_CH_OPENED,
+ NET_CH_CLOSE_IN_PROGRESS,
+ NET_CH_CLOSED
+};
+
+/**
+ * EMAC Peripheral Device Register Memory Layout structure
+ *
+ * The structure instance variable points to CP(G)MAC register space in
+ * SOC memory map directly.
+ * This is a template only, no memory is ever allocated for this!
+ */
+
+#define EMAC_WRAP_REGS_BASE 0
+#define EMAC_WRAP_RSVD0_REG EMAC_WRAP_REGS_BASE
+#define EMAC_WRAP_EWCTL_REG (EMAC_WRAP_REGS_BASE + 4)
+#define EMAC_WRAP_EWINTTCNT_REG (EMAC_WRAP_REGS_BASE + 8)
+
+#define EMAC_REGS_BASE 0
+#define EMAC_TX_IDVER_REG (EMAC_REGS_BASE + 0x00)
+#define EMAC_TX_CONTROL_REG (EMAC_REGS_BASE + 0x04)
+#define EMAC_TX_TEARDOWN_REG (EMAC_REGS_BASE + 0x08)
+#define EMAC_RESERVED1_REG (EMAC_REGS_BASE + 0x0c)
+#define EMAC_RX_IDVER_REG (EMAC_REGS_BASE + 0x10)
+#define EMAC_RX_CONTROL_REG (EMAC_REGS_BASE + 0x14)
+#define EMAC_RX_TEARDOWN_REG (EMAC_REGS_BASE + 0x18)
+#define EMAC_PAD2_REG (EMAC_REGS_BASE + 0x1c)
+#define EMAC_TX_INTSTAT_RAW_REG (EMAC_REGS_BASE + 0x80)
+#define EMAC_TX_INTSTAT_MASKED (EMAC_REGS_BASE + 0x84)
+#define EMAC_TX_INT_MASK_SET_REG (EMAC_REGS_BASE + 0x88)
+#define EMAC_TX_INT_MASK_CLEAR_REG (EMAC_REGS_BASE + 0x8c)
+#define EMAC_MAC_IN_VECTOR_REG (EMAC_REGS_BASE + 0x90)
+#define EMAC_MAC_EOI_VECTOR (EMAC_REGS_BASE + 0x94)
+#define EMAC_PAD3_REG (EMAC_REGS_BASE + 0x98)
+#define EMAC_RX_INT_STAT_RAW_REG (EMAC_REGS_BASE + 0xa0)
+#define EMAC_RX_INT_STAT_MASKED_REG (EMAC_REGS_BASE + 0xa4)
+#define EMAC_RX_INT_MASK_SET_REG (EMAC_REGS_BASE + 0xa8)
+#define EMAC_RX_INT_MASK_CLEAR_REG (EMAC_REGS_BASE + 0xac)
+#define EMAC_MAC_INT_STAT_RAW_REG (EMAC_REGS_BASE + 0xb0)
+#define EMAC_MAC_INT_STAT_MASKED_REG (EMAC_REGS_BASE + 0xb4)
+#define EMAC_MAC_INT_MASK_SET_REG (EMAC_REGS_BASE + 0xb8)
+#define EMAC_MAC_INT_MASK_CLEAR_REG (EMAC_REGS_BASE + 0xbc)
+#define EMAC_PAD4_REG (EMAC_REGS_BASE + 0xc0)
+#define EMAC_RX_MBP_ENABLE_REG (EMAC_REGS_BASE + 0x100)
+#define EMAC_RX_UNICAST_SET_REG (EMAC_REGS_BASE + 0x104)
+#define EMAC_RX_UNICAST_CLEAR_REG (EMAC_REGS_BASE + 0x108)
+#define EMAC_RX_MAXLEN_REG (EMAC_REGS_BASE + 0x10c)
+#define EMAC_RX_BUFFER_OFFSET_REG (EMAC_REGS_BASE + 0x110)
+#define EMAC_RX_FILTER_LOW_THRESH_REG (EMAC_REGS_BASE + 0x114)
+#define EMAC_PAD5_REG (EMAC_REGS_BASE + 0x118)
+#define EMAC_RX_FLOW_THRESH_REG(i) (EMAC_REGS_BASE + 0x120 + (i<<2))
+#define EMAC_RX_FREE_BUFFER_REG(i) (EMAC_REGS_BASE + 0x140 + (i<<2))
+#define EMAC_MAC_CONTROL_REG (EMAC_REGS_BASE + 0x160)
+#define EMAC_MAC_STATUS_REG (EMAC_REGS_BASE + 0x164)
+#define EMAC_EMCONTROL_REG (EMAC_REGS_BASE + 0x168)
+#define EMAC_FIFO_CONTROL_REG (EMAC_REGS_BASE + 0x16c)
+#define EMAC_MAC_CFIG_REG (EMAC_REGS_BASE + 0x170)
+#define EMAC_SOFT_RESET_REG (EMAC_REGS_BASE + 0x174)
+#define EMAC_PAD6 (EMAC_REGS_BASE + 0x178)
+#define EMAC_MAC_SRC_ADDR_LO_REG (EMAC_REGS_BASE + 0x1d0)
+#define EMAC_MAC_SRC_ADDR_HI_REG (EMAC_REGS_BASE + 0x1d4)
+#define EMAC_MAC_HASH1_REG (EMAC_REGS_BASE + 0x1d8)
+#define EMAC_MAC_HASH2_REG (EMAC_REGS_BASE + 0x1dc)
+#define EMAC_BOFF_TEST_REG (EMAC_REGS_BASE + 0x1e0)
+#define EMAC_TPACE_TEST_REG (EMAC_REGS_BASE + 0x1e4)
+#define EMAC_RX_PAUSE_REG (EMAC_REGS_BASE + 0x1e8)
+#define EMAC_TX_PAUSE_REG (EMAC_REGS_BASE + 0x1ec)
+#define EMAC_PAD7 (EMAC_REGS_BASE + 0x1f0)
+#define EMAC_RX_GOOD_FRAMES_REG (EMAC_REGS_BASE + 0x200)
+#define EMAC_RX_BROADCAST_FRAMES_REG (EMAC_REGS_BASE + 0x204)
+#define EMAC_RX_MULTICAST_FRAMES_REG (EMAC_REGS_BASE + 0x208)
+#define EMAC_RX_PAUSE_FRAMES_REG (EMAC_REGS_BASE + 0x20c)
+#define EMAC_RX_CRCERROR_REG (EMAC_REGS_BASE + 0x210)
+#define EMAC_RX_ALIGN_CODE_ERRORS_REG (EMAC_REGS_BASE + 0x214)
+#define EMAC_RX_OVERSIZED_FRAMES_REG (EMAC_REGS_BASE + 0x218)
+#define EMAC_RX_JABBER_FRAME_REG (EMAC_REGS_BASE + 0x21c)
+#define EMAC_RX_UNDERSIZED_FRAMES (EMAC_REGS_BASE + 0x220)
+#define EMAC_RX_FRAGMENTS_REG (EMAC_REGS_BASE + 0x224)
+#define EMAC_RX_FILTERED_FRAMES_REG (EMAC_REGS_BASE + 0x228)
+#define EMAC_RX_QOS_FILTERED_FRAMES_REG (EMAC_REGS_BASE + 0x22c)
+#define EMAC_RX_OCTETS_REG (EMAC_REGS_BASE + 0x230)
+#define EMAC_TX_GOOD_FRAMES_REG (EMAC_REGS_BASE + 0x234)
+#define EMAC_TX_BROADCAST_FRAMES_REG (EMAC_REGS_BASE + 0x238)
+#define EMAC_TX_MULTICAST_FRAMES_REG (EMAC_REGS_BASE + 0x23c)
+#define EMAC_TX_PAUSE_FRAMES_REG (EMAC_REGS_BASE + 0x240)
+#define EMAC_TX_DEFERED_FRAMES_REG (EMAC_REGS_BASE + 0x244)
+#define EMAC_TX_COLLISION_FRAMES_REG (EMAC_REGS_BASE + 0x248)
+#define EMAC_TX_SINGLE_COLL_FRAMES_REG (EMAC_REGS_BASE + 0x24c)
+#define EMAC_TX_MULT_COLL_FRAMES_REG (EMAC_REGS_BASE + 0x250)
+#define EMAC_TX_EXCESSIVE_COLLISIONS_REG (EMAC_REGS_BASE + 0x254)
+#define EMAC_TX_LATE_COLLITIONS_REG (EMAC_REGS_BASE + 0x258)
+#define EMAC_TX_UNDERRUN_REG (EMAC_REGS_BASE + 0x25c)
+#define EMAC_TX_CARRIER_SENSE_ERRORS_REG (EMAC_REGS_BASE + 0x260)
+#define EMAC_TX_OCTETS_REG (EMAC_REGS_BASE + 0x264)
+#define EMAC_REG64OCTET_FRAMES_REG (EMAC_REGS_BASE + 0x268)
+#define EMAC_65_TO_127_OCTET_REG (EMAC_REGS_BASE + 0x26c)
+#define EMAC_128_TO_255_OCTET_REG (EMAC_REGS_BASE + 0x270)
+#define EMAC_256_TO_511_OCTET_REG (EMAC_REGS_BASE + 0x274)
+#define EMAC_512_TO_1023_OCTET_REG (EMAC_REGS_BASE + 0x278)
+#define EMAC_1024_UPOCTET_REG (EMAC_REGS_BASE + 0x27c)
+#define EMAC_NET_OCTETS_REG (EMAC_REGS_BASE + 0x280)
+#define EMAC_RX_SOF_OVERRUNS_REG (EMAC_REGS_BASE + 0x284)
+#define EMAC_RX_MOF_OVERRUNS_REG (EMAC_REGS_BASE + 0x288)
+#define EMAC_RX_DMA_OVERRUNS_REG (EMAC_REGS_BASE + 0x28c)
+#define EMAC_PAD8 (EMAC_REGS_BASE + 0x290)
+#define EMAC_MAC_ADDR_LO_REG (EMAC_REGS_BASE + 0x500)
+#define EMAC_MAC_ADDR_HI_REG (EMAC_REGS_BASE + 0x504)
+#define EMAC_MAC_INDEX_REG (EMAC_REGS_BASE + 0x508)
+#define EMAC_PAD9 (EMAC_REGS_BASE + 0x50c)
+#define EMAC_TX_HDP_REG(i) (EMAC_REGS_BASE + 0x600 + (i<<2))
+#define EMAC_RX_HDP_REG(i) (EMAC_REGS_BASE + 0x620 + (i<<2))
+#define EMAC_TX_CP_REG(i) (EMAC_REGS_BASE + 0x640 + (i<<2))
+#define EMAC_RX_CP_REG(i) (EMAC_REGS_BASE + 0x660 + (i<<2))
+
+
+/**
+ * EMAC Peripheral Device Register Enumerations
+ */
+enum emac_reg_ids {
+ tx_id_ver = 0,
+ tx_control,
+ tx_teardown,
+ rx_id_ver = 4,
+ rx_control,
+ rx_teardown,
+ rx_MBP_enable = 64,
+ rx_unicast_set,
+ rx_unicast_clear,
+ rx_maxlen,
+ rx_buffer_offset,
+
+ rx_filter_low_thresh,
+ rx0_flow_thresh = 72,
+ rx1_flow_thresh,
+ rx2_flow_thresh,
+ rx3_flow_thresh,
+ rx4_flow_thresh,
+
+ rx5_flow_thresh,
+ rx6_flow_thresh,
+ rx7_flow_thresh,
+ rx0_free_buffer,
+
+ rx1_free_buffer,
+ rx2_free_buffer,
+ rx3_free_buffer,
+ rx4_free_buffer,
+
+ rx5_free_buffer,
+ rx6_free_buffer,
+ rx7_free_buffer,
+ mac_control,
+ mac_status,
+
+ EMControl,
+ tx_fifo_control,
+ tx_int_stat_raw,
+ tx_int_stat_masked,
+
+ tx_int_mask_set,
+ tx_int_mask_clear,
+ mac_in_vector,
+ mac_EOI_vector,
+ mac_cfig,
+
+ rx_int_stat_raw = 100,
+ rx_int_stat_masked,
+ rx_int_mask_set,
+ rx_int_mask_clear,
+ mac_int_stat_raw,
+
+ mac_int_stat_masked,
+ mac_int_mask_set,
+ mac_int_mask_clear,
+ mac_src_addr_lo = 116,
+ mac_src_addr_hi,
+ mac_hash1,
+ mac_hash2,
+ boff_test,
+ tpace_test,
+ rx_pause,
+
+ tx_pause,
+ rx_good_frames = 128,
+ rx_broadcast_frames,
+ rx_multicast_frames,
+ rx_pause_frames,
+ rx_crcerrors,
+
+ rx_align_code_errors,
+ rx_oversized_frames,
+ rx_jabber_frames,
+ rx_undersized_frames,
+
+ rx_fragments,
+ rx_filtered_frames,
+ rx_qos_filtered_frames,
+ rx_octets,
+
+ tx_good_frames,
+ tx_broadcast_frames,
+ tx_multicast_frames,
+ tx_pause_frames,
+
+ tx_deferred_frames,
+ tx_collision_frames,
+ tx_single_coll_frames,
+ tx_mult_coll_frames,
+
+ tx_excessive_collisions,
+ tx_late_collisions,
+ tx_underrun,
+ tx_carrier_sense_errors,
+
+ tx_octets,
+ reg64octet_frames,
+ reg65t127octet_frames,
+ reg128t255octet_frames,
+
+ reg256t511octet_frames,
+ reg512t1023octet_frames,
+ reg1024t_upoctet_frames,
+
+ net_octets,
+ rx_sof_overruns,
+ rx_mof_overruns,
+ rx_dma_overruns,
+
+ RX_FIFO_processor_test_access = 192, /* first word of RX FIFO */
+ TX_FIFO_processor_test_access = 256, /* first word of TX FIFO */
+ mac_addr_lo = 320,
+ mac_addr_hi,
+ mac_index,
+ tx0_HDP = 384,
+ tx1_HDP,
+ tx2_HDP,
+ tx3_HDP,
+ tx4_HDP,
+ tx5_HDP,
+ tx6_HDP,
+
+ tx7_HDP,
+ rx0_HDP,
+ rx1_HDP,
+ rx2_HDP,
+ rx3_HDP,
+ rx4_HDP,
+
+ rx5_HDP,
+ rx6_HDP,
+ rx7_HDP,
+ tx0_CP,
+ tx1_CP,
+ tx2_CP,
+ tx3_CP,
+
+ tx4_CP,
+ tx5_CP,
+ tx6_CP,
+ tx7_CP,
+ rx0_CP,
+ rx1_CP,
+ rx2_CP,
+
+ rx3_CP,
+ rx4_CP,
+ rx5_CP,
+ rx6_CP,
+ rx7_CP,
+ stateram_test_access = 448 /* first word of state RAM */
+};
+
+/**
+ * EMAC Addressing Type
+ *
+ * Addressing type based upon cfig register. For EMAC peripheral cfig
+ * register reads a value of 0 i.e Type 0 addressing
+ */
+enum emac_rx_addr_type {
+ RX_ADDR_TYPE0 = 0, /* old style used in (EMAC) */
+ RX_ADDR_TYPE1 = 1, /* new CPGMAC style */
+ RX_ADDR_TYPE2 = 2, /* new CPGMAC "filtering" style */
+ RX_ADDR_TYPE3 = 3 /* new CPGMAC "filtering" style */
+};
+
+/**
+ * EMAC Single Multicast Ioctl - EMAC_IOCTL_MULTICAST_ADDR operations
+ *
+ * Add/Del operations for adding/deleting a single multicast address
+ */
+enum emac_single_multi_oper {
+ EMAC_MULTICAST_ADD = 0,
+ EMAC_MULTICAST_DEL
+};
+
+/**
+ * EMAC All Multicast Ioctl - EMAC_IOCTL_ALL_MULTI operations
+ *
+ * Set/Clear all multicast operation
+ */
+enum emac_all_multi_oper {
+ EMAC_ALL_MULTI_SET = 0,
+ EMAC_ALL_MULTI_CLR
+};
+
+/**
+ * MII Read/Write PHY register
+ *
+ * Parameters to read/write a PHY register via MII interface
+ */
+struct emac_phy_params {
+ u32 phy_num; /* phy number to be read/written */
+ u32 reg_addr; /* register to be read/written */
+ u32 data; /* data to be read/written */
+};
+
+/**
+ * MAC Address params
+ *
+ * Parameters for Configuring Mac address
+ */
+struct emac_address_params {
+ u32 channel;
+ char *mac_address;
+};
+
+/**
+ * Type 2/3 Addressing
+ *
+ * Parameters for programming CFIG 2/3 addressing mode
+ *
+ */
+struct emac_type2_3_addr_filter_params {
+ u32 channel; /* channel to which this filtering params apply */
+ char *mac_address; /* mac address for filtering */
+ int index; /* index of filtering list to update */
+ bool valid; /* entry valid */
+ int match; /* entry matching */
+};
+
+/**
+ * EMAC Hardware Statistics
+ *
+ * Statistics counters provided by EMAC Hardware. The names of the
+ * counters in this structure are of "MIB style" and corrospond
+ * directly to the hardware counters provided by EMAC
+ */
+struct emac_hw_statistics {
+ u32 if_in_good_frames;
+ u32 if_in_broadcasts;
+ u32 if_in_multicasts;
+ u32 if_in_pause_frames;
+ u32 if_in_crcerrors;
+ u32 if_in_align_code_errors;
+ u32 if_in_oversized_frames;
+ u32 if_in_jabber_frames;
+ u32 if_in_undersized_frames;
+ u32 if_in_fragments;
+ u32 if_in_filtered_frames;
+ u32 if_in_qos_filtered_frames;
+ u32 if_in_octets;
+ u32 if_out_good_frames;
+ u32 if_out_broadcasts;
+ u32 if_out_multicasts;
+ u32 if_out_pause_frames;
+ u32 if_deferred_transmissions;
+ u32 if_collision_frames;
+ u32 if_single_collision_frames;
+ u32 if_multiple_collision_frames;
+ u32 if_excessive_collision_frames;
+ u32 if_late_collisions;
+ u32 if_out_underrun;
+ u32 if_carrier_sense_errors;
+ u32 if_out_octets;
+ u32 if64octet_frames;
+ u32 if65to127octet_frames;
+ u32 if128to255octet_frames;
+ u32 if256to511octet_frames;
+ u32 if512to1023octet_frames;
+ u32 if1024to_upoctet_frames;
+ u32 if_net_octets;
+ u32 if_rx_sof_overruns;
+ u32 if_rx_mof_overruns;
+ u32 if_rx_dmaoverruns;
+};
+
+
+
+/*
+ * MIB-2 Common MIB Constants
+ */
+#define MIB2_TRUTH_VALUE_TRUE 1
+#define MIB2_TRUTH_VALUE_FALSE 2
+
+/* MIB-2 interface admin/oper status values */
+/* device is in operational status unless status is down. */
+#define MIB2_STATUS_UP 1
+#define MIB2_STATUS_DOWN 2
+#define MIB2_STATUS_TEST 3
+#define MIB2_STATUS_UNKNOWN 4
+#define MIB2_STATUS_DORMANT 5
+
+#define TI_SIOC_OFFSET 0
+
+/* definitions for interface group MIB variables: GET */
+#define TI_SIOCGINTFCOUNTERS (TI_SIOC_OFFSET + 0x01)
+#define TI_SIOCGINTFPARAMS (TI_SIOC_OFFSET + 0x02)
+
+/* SET command definitions */
+#define TI_SIOCSINTFADMINSTATUS (TI_SIOC_OFFSET + 0x03)
+
+/* definitions for ether-like group MIB variables: GET */
+#define TI_SIOCGETHERCOUNTERS (TI_SIOC_OFFSET + 0x04)
+#define TI_SIOCGETHERPARAMS (TI_SIOC_OFFSET + 0x05)
+
+/* defines MIB II INTERFACE objects */
+struct mib2_if_counters {
+ unsigned long in_bytes_low;
+ unsigned long in_bytes_high;
+ unsigned long in_unicast_pkts_low;
+ unsigned long in_unicast_pkts_high;
+ unsigned long in_multicast_pkts_low;
+ unsigned long in_multicast_pkts_high;
+ unsigned long in_broadcast_pkts_low;
+ unsigned long in_broadcast_pkts_high;
+ unsigned long in_discard_pkts;
+ unsigned long in_error_pkts;
+ unsigned long in_unknown_prot_pkts;
+ unsigned long out_bytes_low;
+ unsigned long out_bytes_high;
+ unsigned long out_unicast_pkts_low;
+ unsigned long out_unicast_pkts_high;
+ unsigned long out_multicast_pkts_low;
+ unsigned long out_multicast_pkts_high;
+ unsigned long out_broadcast_pkts_low;
+ unsigned long out_broadcast_pkts_high;
+ unsigned long out_discard_pkts;
+ unsigned long out_error_pkts;
+};
+
+struct mib2_if_hccounters {
+ struct mib2_if_counters mib2if_counter;
+ unsigned long long in_bytes_hc;
+ unsigned long long in_unicast_pkts_hc;
+ unsigned long long in_multicast_pkts_hc;
+ unsigned long long in_broadcast_pkts_hc;
+ unsigned long long out_bytes_hc;
+ unsigned long long out_unicast_pkts_hc;
+ unsigned long long out_multicast_pkts_hc;
+ unsigned long long out_broadcast_pkts_hc;
+ unsigned long long in_bytes;
+ unsigned long long in_unicast_pkts;
+ unsigned long long in_multicast_pkts;
+ unsigned long long in_broadcast_pkts;
+ unsigned long long out_bytes;
+ unsigned long long out_unicast_pkts;
+ unsigned long long out_multicast_pkts;
+ unsigned long long out_broadcast_pkts;
+};
+
+struct mib2_if_params {
+ unsigned long if_speed; /* speed in bits per second */
+ unsigned long if_high_speed; /* speed in mega-bits per second */
+ long if_oper_status;
+ long if_promiscuous_mode;
+};
+
+struct mib2_if_command {
+ long if_admin_status; /* desired interface state */
+};
+
+/* ether_like-MIB constants */
+#define MIB2_UNKNOWN_DUPLEX 1
+#define MIB2_HALF_DUPLEX 2
+#define MIB2_FULL_DUPLEX 3
+
+/* ioctl/cmd value to be used by snmpd like applications */
+#define SIOTIMIB2 SIOCDEVPRIVATE + 1
+
+/* defines MIB II ether_like-MIB objects */
+struct mib2_phy_counters {
+ unsigned long eth_alignment_errors;
+ unsigned long eth_fcserrors;
+ unsigned long eth_single_collisions;
+ unsigned long eth_multiple_collisions;
+ unsigned long eth_sqetest_errors;
+ unsigned long eth_deferred_tx_frames;
+ unsigned long eth_late_collisions;
+ unsigned long eth_excessive_collisions;
+ unsigned long eth_internal_mac_tx_errors;
+ unsigned long eth_carrier_sense_errors;
+ unsigned long eth_too_long_rx_frames;
+ unsigned long eth_internal_mac_rx_errors;
+ unsigned long eth_symbol_errors;
+};
+
+struct mib2_eth_params {
+ long eth_duplex_status; /* current emac duplex status */
+};
+
+struct ti_snmp_cmd_t {
+ unsigned long cmd;
+ unsigned long port;
+ void *data;
+};
+
+/**
+ * DDC Status values
+ *
+ * Provides status of the device - error status, phy status etc
+ *
+ */
+struct emac_status {
+ u32 hw_status;
+ u32 hw_err_info;
+ u32 phy_linked; /* link status: 1=linked, 0=no link */
+ u32 phy_duplex; /* duplex status:
+ * 3=full duplex,
+ * 2=half duplex */
+
+ u32 phy_speed; /* link speed = 10, 100, 1000 */
+ u32 phy_num; /* phy number - useful if phy number is
+ * discovered */
+};
+
+/**
+ * EMAC Channel Config Info
+ *
+ * Common to both TX/RX
+ * Used to pass channel config info from DDA to DDC for EMAC channels
+ */
+struct emac_ch_info {
+ int ch_num; /* DDC_net_ch_info: channel number */
+ enum net_ch_dir ch_dir; /* DDC_net_ch_info: channel direction */
+ enum net_ch_state ch_state; /* DDC_net_ch_info: channel state */
+ int num_bd; /* number of BD (& buffers for RX) */
+ int service_max; /* maximum BD's processed in one go */
+ int buf_size; /* buffer size (applicable for RX only) */
+};
+
+/**
+ * EMAC RX configuration
+ *
+ * This data structure configures the RX module of the device
+ */
+struct emac_rx_config {
+ bool pass_crc; /* pass CRC bytes to packet memory */
+ bool qos_enable; /* receive qo_s enable ? */
+ bool no_buffer_chaining; /* DEBUG ONLY - ALWAYS SET TO FALSE */
+ bool copy_maccontrol_frames_enable;
+ bool copy_short_frames_enable;
+ bool copy_error_frames_enable;
+ bool promiscous_enable;
+ u32 promiscous_channel; /* promiscous receive channel */
+ bool broadcast_enable; /* receive broadcast frames ? */
+ u32 broadcast_channel; /* broadcast receive channel */
+ bool multicast_enable; /* receive multicast frames ? */
+ u32 multicast_channel; /* multicast receive channel */
+ u32 max_rx_pkt_length; /* max receive packet length */
+ u32 buffer_offset; /* buffer offset for all RX channels */
+};
+
+/**
+ * Transmit Queue priority type
+ *
+ * Enums for transmit queue priority type - fixed/round robin
+ * available in hardware
+ */
+enum emac_tx_queue_priority_type {
+ EMAC_TXPRIO_ROUND_ROBIN = 0,
+ EMAC_TXPRIO_FIXED = 1
+};
+
+/**
+ * EMAC MAC configuration
+ *
+ * This data structure configures the MAC module parameters of the device
+ */
+struct emac_mac_config {
+ enum emac_tx_queue_priority_type p_type;
+ bool tx_short_gap_enable;
+ bool giga_bit_enable;
+ bool tx_pacing_enable;
+ bool mii_enable; /* DEBUG ONLY - ALWAYS SET TO TRUE */
+ bool tx_flow_enable;
+ bool rx_flow_enable;
+ bool loopback_enable;
+ bool full_duplex_enable; /* DEBUG ONLY - based upon phy_mode */
+ bool tx_interrupt_disable;
+};
+
+/**
+ * EMAC Init Configuration
+ *
+ * Configuration information provided to DDC layer during
+ * initialization. DDA gets the config information from the OS/PAL
+ * layer and passes the relevant config to the DDC during
+ * initialization. The config info can come from various sources -
+ * static compiled in info, boot time (ENV, Flash) info etc.
+ */
+struct emac_init_config {
+ u32 inst_id;
+ u32 num_tx_channels;
+ u32 num_rx_channels;
+ u32 emac_bus_frequency;
+ u32 base_address;
+ u32 e_wrap_base_address;
+ u32 intr_line;
+ u32 reset_line;
+ u32 mdio_base_address;
+ u32 mdio_reset_line;
+ u32 mdio_intr_line;
+ u32 phy_mask;
+ u32 MLink_mask;
+ u32 mdio_bus_frequency;
+ u32 mdio_clock_frequency;
+ u32 mdio_tick_msec;
+ u32 mib64cnt_msec;
+ u32 phy_mode;
+ struct emac_rx_config rx_cfg;
+ struct emac_mac_config mac_cfg;
+};
+
+struct rx_tx_params {
+ u32 rx_pkts; /* number of rx pkts to be processed */
+ u32 tx_pkts; /* number of tx pkts to be processed */
+ u32 ret_rx_pkts; /* number of rx pkts processed */
+ u32 ret_tx_pkts; /* number of tx pkts processed */
+};
+
+/*
+ * EMAC Private Ioctl Structure
+ *
+ * Private Ioctl commands provided by the EMAC Linux Driver use this
+ * structure
+ */
+struct emac_drv_priv_ioctl {
+ unsigned int cmd;
+ void *data;
+};
+
+/*
+ * EMAC DDA maintained statistics
+ *
+ * Driver maintained statistics (apart from Hardware statistics)
+ */
+struct emac_drv_stats {
+ unsigned long tx_discards;
+ unsigned long rx_discards;
+ unsigned long start_tick;
+};
+
+/*
+ * TX Buffer Descriptor
+ *
+ * CPPI 3.0 TX BD structure specific to EMAC.
+ */
+struct emac_tx_bd {
+ int h_next; /* next buffer descriptor pointer */
+ int buff_ptr; /* data buffer pointer */
+ int off_b_len; /* (buffer_offset_16)(buffer_length_16) */
+ int mode; /* SOP, EOP, ownership, EOQ, teardown,
+ *Qstarv, length */
+ void *next; /* next TX buffer descriptor (linked list) */
+ void *buf_token;
+ void *eop_bd; /* pointer to end of packet BD */
+};
+
+/* forward declaration */
+struct emac_rx_cppi_ch_t;
+
+/**
+ * RX Buffer Descriptor
+ *
+ * CPPI 3.0 RX BD structure specific to EMAC.
+ */
+struct emac_rx_bd {
+ int h_next; /* next (hardware) buffer descriptor pointer */
+ int buff_ptr; /* data buffer pointer */
+ int off_b_len; /* (buffer_offset_16)(buffer_length_16) */
+ int mode; /* SOP, EOP, ownership, EOQ, teardown,
+ *Q starv, length */
+ void *next; /* pointer to the next RX buffer in BD queue */
+ void *data_ptr; /* virtual address of the buffer allocated */
+ void *buf_token;
+ struct emac_rx_cppi_ch_t *rx_cppi;
+};
+
+/**
+ * TX Channel Control Structure
+ *
+ * Used by EMAC DDC code to process TX Buffer Descriptors
+ */
+struct emac_tx_cppi_ch {
+ /** configuration info */
+ struct emac_ch_info ch_info; /* channel config/info */
+
+ /* CPPI specific */
+ u32 alloc_size; /* BD pool allocated memory size */
+ char *bd_mem; /* buffer descriptor memory pointer */
+ struct emac_tx_bd *bd_pool_head; /* free BD pool head */
+ struct emac_tx_bd *active_queue_head; /* head of active packet queue */
+ struct emac_tx_bd *active_queue_tail; /* last hardware BD written */
+
+ struct emac_tx_bd *last_hw_bdprocessed; /* last HW BD processed */
+ bool queue_active; /* queue active ? TRUE/FALSE */
+#ifdef EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+ u32 *tx_complete; /* tx complete notification queue */
+#endif
+ /** statistics */
+ u32 proc_count; /* TX: # of times emac_tx_bdproc is called */
+ u32 mis_queued_packets; /* misqueued packets */
+ u32 queue_reinit; /* queue reinit - head ptr reinit */
+ u32 end_of_queue_add; /* packet added to end of queue in send */
+ u32 out_of_tx_bd; /* out of tx bd errors */
+ u32 no_active_pkts; /* IRQ when there were no packets to process */
+ u32 active_queue_count; /* active tx bd count */
+ u32 num_multi_frag_pkts;
+};
+
+/**
+ * RX Channel Control Structure
+ *
+ * Used by EMAC DDC code to process RX Buffer Descriptors
+ */
+struct emac_rx_cppi_ch_t {
+ /* configuration info */
+ struct emac_ch_info ch_info; /* channel config/info */
+
+ /* EMAC (ethernet) specific configuration info */
+ char mac_addr[6]; /* ethernet MAC address */
+
+ /** CPPI specific */
+ u32 alloc_size; /* BD pool allocated memory size */
+ char *bd_mem; /* buffer descriptor memory pointer */
+ struct emac_rx_bd *bd_pool_head;
+ struct emac_rx_bd *active_queue_head;
+ struct emac_rx_bd *active_queue_tail;
+ bool queue_active;
+
+ /* packet and buffer objects required for passing up to DDA
+ *layer for the given instance */
+ struct net_pkt_obj pkt_queue;
+ struct net_buf_obj buf_queue[EMAC_MAX_RX_FRAGMENTS];
+#ifdef EMAC_MULTIFRAGMENT
+ u32 rx_buffer_ptr[EMAC_MAX_RX_FRAGMENTS];
+ u32 rx_data_token[EMAC_MAX_RX_FRAGMENTS];
+#endif
+ /** statistics */
+ u32 proc_count; /* number of times struct emac_rx_bdproc
+ * is called */
+ u32 processed_bd; /* number of BD's processed */
+ u32 recycled_bd; /* number of recycled BD's */
+ u32 out_of_rx_bd; /* NO BD's available */
+ u32 out_of_rx_buffers; /* NO buffers available */
+ u32 queue_reinit; /* condition when recycling buffers */
+ u32 end_of_queue_add; /* when adding BD at end */
+ u32 end_of_queue; /* end of queue condition */
+ u32 mis_queued_packets; /* mis-queued packet condition */
+ u32 num_multi_frag_pkts;
+};
+
+/* data structures and header files required for MII-MDIO module */
+
+/**
+ * EMAC Private data structure
+ *
+ * Each EMAC device maintains its own private data structure and has a
+ * pointer to the net_device data structure representing the instance
+ * with the kernel. The private data structure contains a "owner"
+ * member pointing to the net_device structure and the net_device data
+ * structure's "priv" member points back to this data structure.
+ */
+struct emac_dev_s {
+ void *owner; /* pointer to the net_device struct */
+ unsigned int instance_num; /* instance number of the device */
+ struct net_device *next_device;
+ unsigned int link_speed;
+ unsigned int link_mode;
+ unsigned long set_to_close;
+ void *led_handle;
+
+ /* DDC related parameters */
+ struct emac_status ddc_status;
+
+ /* configuration parameters */
+ unsigned char mac_addr[6];
+ struct emac_init_config init_cfg;
+ unsigned int rx_buf_size;
+ unsigned int rx_buf_offset;
+
+ /* TODO: VLAN TX not supported as of now */
+ bool vlan_enable;
+
+ /* channel configuration - though only 1 TX/RX channel is
+ *supported, provision is made for max */
+ struct emac_ch_info tx_ch_info[EMAC_MAX_TX_CHANNELS];
+ struct emac_ch_info rx_ch_info[EMAC_MAX_RX_CHANNELS];
+
+ /* periodic timer required for MDIO polling */
+ struct timer_list periodic_timer;
+ u32 periodic_ticks; /* ticks for this timer */
+ bool timer_active; /* periodic timer active ??? */
+ struct timer_list mib_timer; /* for 64 bit MIB counter */
+ u32 mib_ticks; /* ticks for this timer */
+ bool mib_timer_active; /* periodic timer active ??? */
+
+ /* statistics */
+ struct emac_hw_statistics device_mib; /* hardware statistics counters */
+ struct emac_drv_stats device_stats; /* device statstics */
+ struct net_device_stats net_dev_stats;
+
+ /* statistics counters for debugging */
+ u32 isr_count;
+
+ /* tx_rx_param struct added */
+ struct rx_tx_params napi_rx_tx;
+
+ /* TX/RX locks */
+ spinlock_t tx_lock;
+ spinlock_t rx_lock;
+
+ enum emac_drv_state drv_state;
+
+ /** EMAC specific parameters - DDC device specifics */
+ struct emac_tx_cppi_ch *tx_cppi[EMAC_MAX_TX_CHANNELS];
+ struct emac_rx_cppi_ch_t *rx_cppi[EMAC_MAX_RX_CHANNELS];
+ bool tx_is_created[EMAC_MAX_TX_CHANNELS];
+ bool rx_is_created[EMAC_MAX_RX_CHANNELS];
+ bool tx_is_open[EMAC_MAX_TX_CHANNELS];
+ bool rx_is_open[EMAC_MAX_RX_CHANNELS];
+ bool tx_teardown_pending[EMAC_MAX_TX_CHANNELS];
+ bool rx_teardown_pending[EMAC_MAX_RX_CHANNELS];
+ int tx_int_threshold[EMAC_MAX_TX_CHANNELS];
+ bool tx_interrupt_disable;
+
+ /* register mirror values - maintained to avoid costly
+ *register access for reads */
+ u32 rx_unicast_set;
+ u32 rx_unicast_clear;
+ u32 rx_MBP_enable;
+ u32 mac_hash1;
+ u32 mac_hash2;
+ u32 mac_control;
+ struct emac_status status;
+
+ /* number of multicast hash bits used in hardware */
+ u32 multicast_hash_cnt[EMAC_NUM_MULTICAST_BITS];
+
+ u32 emac_regs_base;
+ u32 emac_wrap_regs_base;
+ /* EMAC/CPGMAC addressing mechanism */
+ u32 rx_addr_type; /* 0 (EMAC), 1 or 2 (CPGMAC) */
+ u32 irq_line;
+ struct napi_struct napi;
+
+ struct mib2_if_hccounters mib2if_hccounter;
+};
+
+#define davinci_orl(v, a) davinci_writel((davinci_readl(a) | v), a)
+
+#define EMAC_TOKEN_PARSE(str) \
+ { if ((tok = (char *)strsep((str), ":")) == NULL) return -1; }
+#define EMAC_TOKEN_GET_INTEGER simple_strtoul (tok, NULL, 10)
+#define EMAC_TOKEN_GET_HEX simple_strtoul (tok, NULL, 16)
+
+/*
+ * internal utility functions
+ */
+static inline u32 emac_virt_to_phys(u32 addr)
+{
+ /* NOTE: must handle memory and IO addresses */
+ return ((addr & 0xFFFF0000) == EMAC_BASE_ADDR)?
+ io_v2p(addr) : virt_to_phys((void *)addr);
+}
+
+#define EMAC_VIRT_TO_PHYS(x) emac_virt_to_phys((u32)x)
+#define EMAC_VIRT_NOCACHE(addr)(addr)
+
+/* alloc and zero memoy */
+static inline int emac_malloc(u32 n, void **buf)
+{
+ void *tmp = kcalloc(n, 1, GFP_KERNEL);
+
+ if (!tmp) {
+ printk(KERN_ERR "emac_malloc(): kmalloc() failed.\n");
+ dump_stack();
+ return -1;
+ }
+
+ *buf = tmp;
+ return 0;
+}
+
+static inline void emac_free(void *ptr)
+{
+ kfree(ptr);
+}
+
+#define EMAC_CACHE_INVALIDATE(addr, size) \
+ dma_cache_maint((void *)addr, size, DMA_FROM_DEVICE)
+#define EMAC_CACHE_WRITEBACK(addr, size) \
+ dma_cache_maint((void *)addr, size, DMA_TO_DEVICE)
+#define EMAC_CACHE_WRITEBACK_INVALIDATE(addr, size) \
+ dma_cache_maint((void *)addr, size, DMA_BIDIRECTIONAL)
+
+/* buffer-descriptors in IO space. No cache invalidation needed */
+#define BD_CACHE_INVALIDATE(addr, size)
+#define BD_CACHE_WRITEBACK(addr, size)
+#define BD_CACHE_WRITEBACK_INVALIDATE(addr, size)
+
+/* string to hex conversion */
+static inline unsigned char emac_str_to_hexnum(unsigned char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return 0;
+}
+
+extern int davinci_get_macaddr(char *ptr);
+
+#ifdef EMAC_DEBUG
+
+extern int emac_p_read_link(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data);
+extern int emac_dump_config(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data);
+extern int emac_p_update_statistics_debug(struct net_device *netdev, char *buf,
+ int limit, int *p_len);
+extern int emac_p_read_rfc2665_stats(char *buf, char **start, off_t offset,
+ int count, int *eof, void *data);
+extern int emac_p_read_stats(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data);
+extern int emac_p_write_stats(struct file *fp, const char *buf,
+ unsigned long count, void *data);
+extern int emac_devices_installed;
+extern struct net_device *last_emac_device;
+extern int emac_control(struct emac_dev_s *dev, int cmd, void *cmd_arg,
+ void *param);
+extern int emac_p_reset_statistics(struct net_device *netdev);
+extern int emac_p_update_statistics(struct net_device *netdev);
+
+#endif
Index: 2.6.24-rc8.ether/drivers/net/arm/davinci_emac_debug.c
===================================================================
--- /dev/null
+++ 2.6.24-rc8.ether/drivers/net/arm/davinci_emac_debug.c
@@ -0,0 +1,658 @@
+/*
+ * Copyright (C) 2008 MontaVista Software, Inc. <source@mvista.com>
+ *
+ * This file is licensed under the terms of the
+ * GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * 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/string.h>
+#include <linux/netdevice.h>
+#include "davinci_emac.h"
+
+/* dump configuration information for debug purposes */
+int emac_dump_config(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ int len = 0;
+ struct net_device *netdev;
+ struct net_device *emac_dev_list[emac_devices_installed];
+ int i;
+ struct emac_dev_s *dev;
+
+ len +=
+ sprintf(buf + len, "EMAC devices = %d\n", emac_devices_installed);
+
+ netdev = last_emac_device;
+
+ /*
+ * reverse the the device link list to list eth0,eth1...in
+ * correct order
+ */
+ for (i = 0; i < emac_devices_installed; i++) {
+ emac_dev_list[emac_devices_installed - (i + 1)] = netdev;
+ dev = NETDEV_PRIV(netdev);
+ netdev = dev->next_device;
+ }
+
+ for (i = 0; i < emac_devices_installed; i++) {
+ netdev = emac_dev_list[i];
+ dev = NETDEV_PRIV(netdev);
+
+ len +=
+ sprintf(buf + len,
+ "\nEMAC Driver Internal Config Info for Unit %d\n",
+ dev->instance_num);
+ len += sprintf(buf + len, "vlanEnable = %d\n",
+ dev->vlan_enable);
+ len += sprintf(buf + len, "rxBufSize = %d\n",
+ dev->rx_buf_size);
+ len += sprintf(buf + len, "rxBufOffset = %d\n",
+ dev->rx_buf_offset);
+ len += sprintf(buf + len, "instId = %d\n",
+ dev->init_cfg.inst_id);
+ len += sprintf(buf + len, "numTxChannels = %d\n",
+ dev->init_cfg.num_tx_channels);
+ len += sprintf(buf + len, "numRxChannels = %d\n",
+ dev->init_cfg.num_rx_channels);
+ len += sprintf(buf + len, "emacBusFrequency = %d\n",
+ dev->init_cfg.emac_bus_frequency);
+ len += sprintf(buf + len, "baseAddress = %08X\n",
+ dev->init_cfg.base_address);
+ len += sprintf(buf + len, "intrLine = %d\n",
+ dev->init_cfg.intr_line);
+ len += sprintf(buf + len, "resetLine = %d\n",
+ dev->init_cfg.reset_line);
+ len += sprintf(buf + len, "mdioBaseAddress = %08X\n",
+ dev->init_cfg.mdio_base_address);
+ len += sprintf(buf + len, "mdioResetLine = %d\n",
+ dev->init_cfg.mdio_reset_line);
+ len += sprintf(buf + len, "mdioIntrLine = %d\n",
+ dev->init_cfg.mdio_intr_line);
+ len += sprintf(buf + len, "PhyMask = %08X\n",
+ dev->init_cfg.phy_mask);
+ len += sprintf(buf + len, "MLinkMask = %08X\n",
+ dev->init_cfg.MLink_mask);
+ len += sprintf(buf + len, "MdioBusFrequency = %d\n",
+ dev->init_cfg.mdio_bus_frequency);
+ len += sprintf(buf + len, "MdioClockFrequency = %d\n",
+ dev->init_cfg.mdio_clock_frequency);
+ len += sprintf(buf + len, "MdioTickMSec = %d\n",
+ dev->init_cfg.mdio_tick_msec);
+ len += sprintf(buf + len, "phyMode = %d\n",
+ dev->init_cfg.phy_mode);
+ len += sprintf(buf + len, "passCRC = %d\n",
+ dev->init_cfg.rx_cfg.pass_crc);
+ len += sprintf(buf + len, "qosEnable = %d\n",
+ dev->init_cfg.rx_cfg.qos_enable);
+ len += sprintf(buf + len, "noBufferChaining = %d\n",
+ dev->init_cfg.rx_cfg.no_buffer_chaining);
+ len += sprintf(buf + len, "copyMACCntrlFrsEne = %d\n",
+ dev->init_cfg.rx_cfg.copy_maccontrol_frames_enable);
+ len += sprintf(buf + len, "copyShortFramesEn = %d\n",
+ dev->init_cfg.rx_cfg.copy_short_frames_enable);
+ len += sprintf(buf + len, "copyErrorFramesEn = %d\n",
+ dev->init_cfg.rx_cfg.copy_error_frames_enable);
+ len += sprintf(buf + len, "promiscousEnable = %d\n",
+ dev->init_cfg.rx_cfg.promiscous_enable);
+ len += sprintf(buf + len, "promiscousChannel = %d\n",
+ dev->init_cfg.rx_cfg.promiscous_channel);
+ len += sprintf(buf + len, "broadcastEnable = %d\n",
+ dev->init_cfg.rx_cfg.broadcast_enable);
+ len += sprintf(buf + len, "broadcastChannel = %d\n",
+ dev->init_cfg.rx_cfg.broadcast_channel);
+ len += sprintf(buf + len, "multicastEnable = %d\n",
+ dev->init_cfg.rx_cfg.multicast_enable);
+ len += sprintf(buf + len, "multicastChannel = %d\n",
+ dev->init_cfg.rx_cfg.multicast_channel);
+ len += sprintf(buf + len, "maxRxPktLength = %d\n",
+ dev->init_cfg.rx_cfg.max_rx_pkt_length);
+ len += sprintf(buf + len, "bufferOffset = %d\n",
+ dev->init_cfg.rx_cfg.buffer_offset);
+ len += sprintf(buf + len, "pType = %d\n",
+ dev->init_cfg.mac_cfg.p_type);
+ len += sprintf(buf + len, "txShortGapEnable = %d\n",
+ dev->init_cfg.mac_cfg.tx_short_gap_enable);
+ len += sprintf(buf + len, "gigaBitEnable = %d\n",
+ dev->init_cfg.mac_cfg.giga_bit_enable);
+ len += sprintf(buf + len, "txPacingEnable = %d\n",
+ dev->init_cfg.mac_cfg.tx_pacing_enable);
+ len += sprintf(buf + len, "miiEnable = %d\n",
+ dev->init_cfg.mac_cfg.mii_enable);
+ len += sprintf(buf + len, "txFlowEnable = %d\n",
+ dev->init_cfg.mac_cfg.tx_flow_enable);
+ len += sprintf(buf + len, "rxFlowEnable = %d\n",
+ dev->init_cfg.mac_cfg.rx_flow_enable);
+ len += sprintf(buf + len, "loopbackEnable = %d\n",
+ dev->init_cfg.mac_cfg.loopback_enable);
+ len += sprintf(buf + len, "fullDuplexEnable = %d\n",
+ dev->init_cfg.mac_cfg.full_duplex_enable);
+ netdev = dev->next_device;
+ }
+
+ return len;
+}
+
+
+/* link read support */
+int emac_p_read_link(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ int len = 0;
+ struct net_device *netdev;
+ struct emac_dev_s *dev;
+ struct net_device *emac_dev_list[emac_devices_installed];
+ int i;
+
+ len +=
+ sprintf(buf + len, "EMAC devices = %d\n", emac_devices_installed);
+ netdev = last_emac_device;
+
+ /* reverse the the device link list to list eth0,eth1...in correct
+ *order */
+ for (i = 0; i < emac_devices_installed; i++) {
+ emac_dev_list[emac_devices_installed - (i + 1)] = netdev;
+ dev = NETDEV_PRIV(netdev);
+ netdev = dev->next_device;
+ }
+
+ for (i = 0; i < emac_devices_installed; i++) {
+ netdev = emac_dev_list[i];
+ dev = NETDEV_PRIV(netdev);
+
+ /* this prints them out from high to low because of
+ how the devices are linked */
+ if (netif_carrier_ok(netdev)) {
+ len +=
+ sprintf(buf + len,
+ "eth%d: Link State: %s "
+ "Phy %d, Speed = %s, Duplex = %s\n",
+ dev->instance_num, "UP",
+ dev->ddc_status.phy_num,
+ (dev->link_speed == 100000000) ? "100" : "10",
+ (dev->link_mode == 2) ? "Half" : "Full");
+ } else {
+ len += sprintf(buf + len, "eth%d: Link State: DOWN\n",
+ dev->instance_num);
+ }
+ netdev = dev->next_device;
+ }
+
+ return len;
+}
+
+/* update RFC2665 statistics */
+int emac_p_read_rfc2665_stats(char *buf, char **start, off_t offset,
+ int count, int *eof, void *data)
+{
+ int limit = count - 80;
+ int len = 0;
+ struct net_device *netdev = (struct net_device *)data;
+
+ emac_p_update_statistics_debug(netdev, buf, limit, &len);
+ *eof = 1;
+
+ return len;
+}
+
+int emac_p_update_statistics_debug(struct net_device *netdev, char *buf,
+ int limit, int *p_len)
+{
+ unsigned long rx_hal_errors = 0;
+ unsigned long rx_hal_discards = 0;
+ unsigned long tx_hal_errors = 0;
+ unsigned long if_out_discards = 0;
+ unsigned long if_in_discards = 0;
+ unsigned long if_out_errors = 0;
+ unsigned long if_in_errors = 0;
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+ struct emac_hw_statistics *p_device_mib = &dev->device_mib;
+ struct emac_drv_stats *p_stats = &dev->device_stats;
+ struct emac_hw_statistics local_mib;
+ struct emac_hw_statistics *p_local_mib = &local_mib;
+ struct net_device_stats *p_net_dev_stats = &dev->net_dev_stats;
+ int len = 0;
+ int dev_mib_elem_count = 0;
+
+ /* do not access the hardware if it is in the reset state. */
+ if (!test_bit(0, &dev->set_to_close)) {
+ /* get hardware statistics from DDC */
+ if (emac_control(dev, EMAC_IOCTL_GET_STATISTICS,
+ (void *)p_local_mib, NULL)
+ != EMAC_SUCCESS) {
+ ERR("Error getting statistics for %s\n", netdev->name);
+
+ return -1;
+ }
+
+ dev_mib_elem_count = sizeof(struct emac_hw_statistics) /
+ sizeof(unsigned long);
+
+ /*
+ * Update the history of the stats. This takes care of
+ * any reset of the device and stats that might have
+ * taken place during the life time of the driver.
+ */
+ while (dev_mib_elem_count--)
+ *((unsigned long *)p_device_mib + dev_mib_elem_count) =
+ *((unsigned long *)p_local_mib +
+ dev_mib_elem_count);
+ }
+
+ /* RFC2665, section 3.2.7, page 9 */
+ rx_hal_errors = p_device_mib->if_in_fragments +
+ p_device_mib->if_in_crcerrors +
+ p_device_mib->if_in_align_code_errors +
+ p_device_mib->if_in_jabber_frames;
+
+ /* RFC2233 */
+ rx_hal_discards = p_device_mib->if_rx_dmaoverruns;
+
+ /* RFC2665, section 3.2.7, page 9 */
+ tx_hal_errors = p_device_mib->if_excessive_collision_frames +
+ p_device_mib->if_late_collisions +
+ p_device_mib->if_carrier_sense_errors +
+ p_device_mib->if_out_underrun;
+
+ /* if not set, the short frames (< 64 bytes) are considered as
+ errors */
+ if (dev->init_cfg.rx_cfg.copy_short_frames_enable == FALSE)
+ rx_hal_errors += p_device_mib->if_in_undersized_frames;
+
+ /* All frames greater than max rx frame length set in hardware
+ *should be considered error frames RFC2665, section 3.2.7,
+ *page 9. */
+ rx_hal_errors += p_device_mib->if_in_oversized_frames;
+
+ /* if not in promiscous, then non addr matching frames are discarded */
+ /* EMAC 2.0 manual section 2.8.1.14 */
+ if (dev->init_cfg.rx_cfg.promiscous_enable == FALSE)
+ if_in_discards += p_device_mib->if_in_filtered_frames;
+
+ /* total rx discards = hal discards */
+ if_in_discards = rx_hal_discards;
+ p_net_dev_stats->rx_dropped = rx_hal_discards;
+ p_net_dev_stats->rx_crc_errors = p_device_mib->if_in_crcerrors;
+ p_net_dev_stats->rx_frame_errors =
+ p_device_mib->if_in_align_code_errors;
+ p_net_dev_stats->multicast = p_device_mib->if_in_multicasts;
+ if_in_errors = rx_hal_errors;
+ if_out_errors = tx_hal_errors;
+ if_out_discards = p_net_dev_stats->tx_dropped;
+
+ /* let us update the net device stats struct. to be updated in
+ the later releases. */
+ dev->net_dev_stats.rx_errors = if_in_errors;
+ dev->net_dev_stats.collisions = p_device_mib->if_collision_frames;
+ dev->net_dev_stats.tx_carrier_errors =
+ p_device_mib->if_carrier_sense_errors;
+
+ if (buf == NULL || limit == 0)
+ return 0;
+
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifSpeed",
+ dev->link_speed);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "dot3StatsDuplexStatus", dev->link_mode);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifAdminStatus",
+ (netdev->flags & IFF_UP ? 1 : 2));
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifOperStatus",
+ (((netdev->flags & IFF_UP) &&
+ netif_carrier_ok(netdev)) ? 1 : 2));
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %lu\n", "ifLastChange",
+ p_stats->start_tick);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %lu\n", "ifInDiscards",
+ if_in_discards);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %lu\n", "ifInErrors",
+ if_in_errors);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %lu\n", "ifOutDiscards",
+ if_out_discards);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %lu\n", "ifOutErrors",
+ if_out_errors);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInGoodFrames",
+ p_device_mib->if_in_good_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInBroadcasts",
+ p_device_mib->if_in_broadcasts);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInMulticasts",
+ p_device_mib->if_in_multicasts);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInPauseFrames",
+ p_device_mib->if_in_pause_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInCRCErrors",
+ p_device_mib->if_in_crcerrors);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInAlignCodeErrors",
+ p_device_mib->if_in_align_code_errors);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInOversizedFrames",
+ p_device_mib->if_in_oversized_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInJabberFrames",
+ p_device_mib->if_in_jabber_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "ifInUndersizedFrames",
+ p_device_mib->if_in_undersized_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInFragments",
+ p_device_mib->if_in_fragments);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInFilteredFrames",
+ p_device_mib->if_in_filtered_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "ifInQosFilteredFrames",
+ p_device_mib->if_in_qos_filtered_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInOctets",
+ p_device_mib->if_in_octets);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifOutGoodFrames",
+ p_device_mib->if_out_good_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifOutBroadcasts",
+ p_device_mib->if_out_broadcasts);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifOutMulticasts",
+ p_device_mib->if_out_multicasts);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifOutPauseFrames",
+ p_device_mib->if_out_pause_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "ifDeferredTransmissions",
+ p_device_mib->if_deferred_transmissions);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifCollisionFrames",
+ p_device_mib->if_collision_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "ifSingleCollisionFrames",
+ p_device_mib->if_single_collision_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "ifMultipleCollisionFrames",
+ p_device_mib->if_multiple_collision_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "ifExcessiveCollisionFrames",
+ p_device_mib->if_excessive_collision_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifLateCollisions",
+ p_device_mib->if_late_collisions);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifOutUnderrun",
+ p_device_mib->if_out_underrun);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "ifCarrierSenseErrors",
+ p_device_mib->if_carrier_sense_errors);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifOutOctets",
+ p_device_mib->if_out_octets);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "if64OctetFrames",
+ p_device_mib->if64octet_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "if65To127POctetFrames",
+ p_device_mib->if65to127octet_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "if128To255OctetFrames",
+ p_device_mib->if128to255octet_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "if256To511OctetFrames",
+ p_device_mib->if256to511octet_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "if512To1023OctetFrames",
+ p_device_mib->if512to1023octet_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "if1024ToUpOctetFrames",
+ p_device_mib->if1024to_upoctet_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifNetOctets",
+ p_device_mib->if_net_octets);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifRxSofOverruns",
+ p_device_mib->if_rx_sof_overruns);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifRxMofOverruns",
+ p_device_mib->if_rx_mof_overruns);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifRxDMAOverruns",
+ p_device_mib->if_rx_dmaoverruns);
+ *p_len = len;
+
+ return 0;
+}
+
+int emac_p_read_stats(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *netdev = last_emac_device;
+ int len = 0;
+ int limit = count - 80;
+ int i;
+ struct net_device *emac_dev_list[emac_devices_installed];
+ struct emac_dev_s *dev;
+ struct emac_hw_statistics *p_device_mib;
+
+ /* reverse the the device link list to list eth0,eth1...in
+ correct order */
+ for (i = 0; i < emac_devices_installed; i++) {
+ emac_dev_list[emac_devices_installed - (i + 1)] = netdev;
+ dev = NETDEV_PRIV(netdev);
+ netdev = dev->next_device;
+ }
+
+ for (i = 0; i < emac_devices_installed; i++) {
+ netdev = emac_dev_list[i];
+ if (!netdev)
+ goto proc_error;
+
+ /* get stats */
+ emac_p_update_statistics(netdev);
+ dev = NETDEV_PRIV(netdev);
+ p_device_mib = &dev->device_mib;
+
+ /* transmit stats */
+ if (len <= limit)
+ len += sprintf(buf + len, "\nCpmac %d, Address %lx\n",
+ i + 1, netdev->base_addr);
+ if (len <= limit)
+ len += sprintf(buf + len, " Transmit Stats\n");
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Tx Valid Bytes Sent :%u\n",
+ p_device_mib->if_out_octets);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Tx Frames (Hardware) :%u\n",
+ p_device_mib->if_out_good_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Tx Frames (Software) :%lu\n",
+ dev->net_dev_stats.tx_packets);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Tx Broadcast Frames :%u\n",
+ p_device_mib->if_out_broadcasts);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Tx Multicast Frames :%u\n",
+ p_device_mib->if_out_multicasts);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Pause Frames Sent :%u\n",
+ p_device_mib->if_out_pause_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Collisions :%u\n",
+ p_device_mib->if_collision_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Tx Error Frames :%lu\n",
+ dev->net_dev_stats.tx_errors);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Carrier Sense Errors :%u\n",
+ p_device_mib->if_carrier_sense_errors);
+ if (len <= limit)
+ len += sprintf(buf + len, "\n");
+
+ /* receive stats */
+ if (len <= limit)
+ len += sprintf(buf + len, "\nCpmac %d, Address %lx\n",
+ i + 1, netdev->base_addr);
+ if (len <= limit)
+ len += sprintf(buf + len, " Receive Stats\n");
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx Valid Bytes Received :%u\n",
+ p_device_mib->if_in_octets);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Rx Frames (Hardware) :%u\n",
+ p_device_mib->if_in_good_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Rx Frames (Software) :%lu\n",
+ dev->net_dev_stats.rx_packets);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Rx Broadcast Frames :%u\n",
+ p_device_mib->if_in_broadcasts);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Rx Multicast Frames :%u\n",
+ p_device_mib->if_in_multicasts);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Pause Frames Received :%u\n",
+ p_device_mib->if_in_pause_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx CRC Errors :%u\n",
+ p_device_mib->if_in_crcerrors);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx Align/Code Errors :%u\n",
+ p_device_mib->if_in_align_code_errors);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx Jabbers :%u\n",
+ p_device_mib->if_in_oversized_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx Filtered Frames :%u\n",
+ p_device_mib->if_in_filtered_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx Fragments :%u\n",
+ p_device_mib->if_in_fragments);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx Undersized Frames :%u\n",
+ p_device_mib->if_in_undersized_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx Overruns :%u\n",
+ p_device_mib->if_rx_dmaoverruns);
+ }
+
+ return len;
+
+proc_error:
+ *eof = 1;
+
+ return len;
+}
+
+int emac_p_write_stats(struct file *fp, const char *buf,
+ unsigned long count, void *data)
+{
+ char local_buf[31];
+ int ret_val = 0;
+
+ if (count > 30) {
+ printk(KERN_NOTICE"Error : Buffer Overflow\n");
+ printk(KERN_NOTICE"Use \"echo 0 > emac_stat\" to reset"
+ " the statistics\n");
+ return -EFAULT;
+ }
+
+ count -= copy_from_user(local_buf, buf, count);
+ local_buf[(count)? (count - 1) : 0] = '\0'; /* ignoring last \n char */
+ ret_val = count;
+ if (strcmp("0", local_buf) == 0) {
+ struct net_device *netdev = last_emac_device;
+ int i;
+ struct net_device *emac_dev_list[emac_devices_installed];
+ struct emac_dev_s *dev;
+
+ /* valid command */
+ printk(KERN_INFO"Resetting statistics for EMAC interface.\n");
+
+ /*
+ * reverse the the device link list to list
+ * eth0,eth1...in correct order
+ */
+ for (i = 0; i < emac_devices_installed; i++) {
+ emac_dev_list[emac_devices_installed - (i + 1)] =
+ netdev;
+ dev = NETDEV_PRIV(netdev);
+ netdev = dev->next_device;
+ }
+
+ for (i = 0; i < emac_devices_installed; i++) {
+ netdev = emac_dev_list[i];
+ if (!netdev) {
+ ret_val = -EFAULT;
+ break;
+ }
+ emac_p_reset_statistics(netdev);
+ }
+ } else {
+ printk(KERN_NOTICE"Error: Unknown operation on"
+ "emac statistics\n");
+ printk(KERN_NOTICE"Use \"echo 0 > emac_stats\" to"
+ " reset the statistics\n");
+ return -EFAULT;
+ }
+
+ return ret_val;
+}
Index: 2.6.24-rc8.ether/drivers/net/arm/davinci_emac_phy.c
===================================================================
--- /dev/null
+++ 2.6.24-rc8.ether/drivers/net/arm/davinci_emac_phy.c
@@ -0,0 +1,723 @@
+/*
+ * linux/drivers/net/davinci_emac_phy.c
+ *
+ * EMAC MII-MDIO Module - Polling State Machine.
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) 2008 MontaVista Software, Inc. <source@mvista.com>
+ *
+ * This file is licensed under the terms of the
+ * GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * 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.
+ * ----------------------------------------------------------------------------
+ Modifications:
+ * HISTORY:
+ * Date Modifier Notes
+ * 2001/02 Denis, Bill, Michael Original
+ * 14Feb2006 Anant Gole Re-written for linux
+ * 07Dec2006 Paul Bartholomew Fix half-duplex,
+ * use PHY_DUPLEX_* constants
+ */
+#include <linux/kernel.h>
+
+#include <asm/io.h>
+
+#include "davinci_emac_phy.h"
+
+#define EMAC_PHY_DEBUG 0
+
+#if EMAC_PHY_DEBUG
+#define DPRINTK(fmt, args...) \
+ do { if (emac_phy->debug_mode) \
+ printk(KERN_ERR "\n%s: " fmt, __FUNCTION__ , ## args) \
+ } while (0)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+/* Phy Registers */
+#define PHY_CONTROL_REG 0
+#define MII_PHY_RESET (1<<15)
+#define MII_PHY_LOOP (1<<14)
+#define MII_PHY_100 (1<<13)
+#define MII_AUTO_NEGOTIATE_EN (1<<12)
+#define MII_PHY_PDOWN (1<<11)
+#define MII_PHY_ISOLATE (1<<10)
+#define MII_RENEGOTIATE (1<<9)
+#define MII_PHY_FD (1<<8)
+
+#define PHY_STATUS_REG 1
+#define MII_NWAY_COMPLETE (1<<5)
+#define MII_NWAY_CAPABLE (1<<3)
+#define MII_PHY_LINKED (1<<2)
+
+#define NWAY_ADVERTIZE_REG 4
+#define NWAY_REMADVERTISE_REG 5
+#define MII_NWAY_FD100 (1<<8)
+#define MII_NWAY_HD100 (1<<7)
+#define MII_NWAY_FD10 (1<<6)
+#define MII_NWAY_HD10 (1<<5)
+#define MII_NWAY_SEL (1<<0)
+
+/*
+ * Timeout values - since timer tikc is expected to be 10 mSecs fixed these
+ * values are in (value * 10 mSecs)
+ */
+#define PHY_FIND_TIMEOUT (2)
+#define PHY_RECK_TIMEOUT (200)
+#define PHY_LINK_TIMEOUT (500)
+#define PHY_NWST_TIMEOUT (500)
+#define PHY_NWDN_TIMEOUT (800)
+#define PHY_MDIX_TIMEOUT (274) /* 2.74 Seconds <--Spec and empirical */
+
+/* Mask & Control defines */
+#define MDIO_CONTROL_CLKDIV (0xFF)
+#define MDIO_CONTROL_ENABLE (1 << 30)
+#define MDIO_USERACCESS_GO (1 << 31)
+#define MDIO_USERACCESS_WRITE (1 << 30)
+#define MDIO_USERACCESS_READ (0 << 30)
+#define MDIO_USERACCESS_WRITE (1 << 30)
+#define MDIO_USERACCESS_REGADR (0x1F << 21)
+#define MDIO_USERACCESS_PHYADR (0x1F << 16)
+#define MDIO_USERACCESS_DATA (0xFFFF)
+#define MDIO_USERPHYSEL_LINKSEL (1 << 7)
+#define MDIO_VER_MODID (0xFFFF << 16)
+#define MDIO_VER_REVMAJ (0xFF << 8)
+#define MDIO_VER_REVMIN (0xFF)
+
+/* PHY Registers */
+#define MDIO_VER (0x00)
+#define MDIO_CONTROL (0x04)
+#define MDIO_ALIVE (0x08)
+#define MDIO_LINK (0x0C)
+#define MDIO_LINKINTRAW (0x10)
+#define MDIO_LINKINTMASKED (0x14)
+#define MDIO_USERINTRAW (0x20)
+#define MDIO_USERINTMASKED (0x24)
+#define MDIO_USERINTMASKED_SET (0x28)
+#define MDIO_USERINTMASKED_CLR (0x2C)
+#define MDIO_USERACCESS(inst) (0x80+(inst*8))
+#define MDIO_USERPHYSEL(inst) (0x84+(inst*8))
+
+#define MDIO_USERACCESS_INST MDIO_USERACCESS(emac_phy->inst)
+#define MDIO_USERPHYSEL_INST MDIO_USERPHYSEL(emac_phy->inst)
+
+#define MDIO_REG_READ(reg) (__raw_readl(emac_phy->base + (reg)))
+#define MDIO_REG_WRITE(reg, val) (__raw_writel(val, \
+ emac_phy->base + (reg)))
+
+/* Phy State */
+#define PHY_NULL 0
+#define PHY_INIT 1
+#define PHY_FINDING 2
+#define PHY_FOUND 3
+#define PHY_NWAY_START 4
+#define PHY_NWAY_WAIT 5
+#define PHY_LINK_WAIT 6
+#define PHY_LINKED 7
+#define PHY_LOOPBACK 8
+
+static char *phy_state_str[] = {
+ "NULL", "INIT", "FINDING", "FOUND", "NWAY_START", "NWAY_WAIT",
+ "LINK_WAIT", "LINKED", "LOOPBACK"
+};
+
+#define PHY_NOT_FOUND 0xFFFF /* Used in Phy Detection */
+
+struct phy_info {
+ int inst; /* Instance of PHY - for user sel register */
+ unsigned int base; /* Base address of mdio module */
+ int state; /* state of phy */
+ int state_change; /* phy state change ? */
+ unsigned int timeout; /* Timeout counter */
+ unsigned int phy_mode; /* requested phy mode */
+ unsigned int speed; /* current Speed - 10 / 100 */
+ unsigned int duplex; /* 0=Auto Negotiate,
+ * 3=Full,
+ * 2=Half,
+ * 1=Unknown
+ */
+ unsigned int phy_addr; /* phy address */
+ unsigned int phy_mask; /* phy mask */
+ unsigned int mlink_mask;/* mlink mask */
+ int debug_mode; /* debug mode */
+};
+
+/* Global phy structure instance */
+struct phy_info emac_phy_info;
+struct phy_info *emac_phy = &emac_phy_info;
+
+void emac_mdio_get_ver(unsigned int mdio_base, unsigned int *module_id,
+ unsigned int *rev_major, unsigned int *rev_minor)
+{
+ unsigned int ver;
+
+ emac_phy->base = mdio_base;
+ ver = MDIO_REG_READ(MDIO_VER);
+
+ *module_id = (ver & MDIO_VER_MODID) >> 16;
+ *rev_major = (ver & MDIO_VER_REVMAJ) >> 8;
+ *rev_minor = (ver & MDIO_VER_REVMIN);
+}
+
+/* Initialize mdio module */
+int emac_mdio_init(unsigned int mdio_base,
+ unsigned int inst,
+ unsigned int phy_mask,
+ unsigned int mlink_mask,
+ unsigned int mdio_bus_freq,
+ unsigned int mdio_clock_freq, unsigned int verbose)
+{
+ unsigned int clk_div;
+
+ /* Set base addr and init phy state */
+ emac_phy->inst = inst;
+ emac_phy->base = mdio_base;
+ emac_phy->phy_mask = phy_mask;
+ emac_phy->mlink_mask = mlink_mask;
+ emac_phy->state = PHY_INIT;
+ emac_phy->debug_mode = verbose;
+ emac_phy->speed = 10;
+ emac_phy->duplex = PHY_DUPLEX_HALF; /* Half duplex */
+
+ clk_div = ((mdio_clock_freq & mdio_bus_freq)?
+ ((mdio_bus_freq / mdio_clock_freq) - 1) : 0xff)
+ & MDIO_CONTROL_CLKDIV;
+
+ /* Set enable and clock divider in MDIOControl */
+ MDIO_REG_WRITE(MDIO_CONTROL, clk_div | MDIO_CONTROL_ENABLE);
+
+ return 0;
+}
+
+/* Set PHY mode - autonegotiation or any other */
+void emac_mdio_set_phy_mode(unsigned int phy_mode)
+{
+ emac_phy->phy_mode = phy_mode;
+
+ if ((emac_phy->state == PHY_NWAY_START) ||
+ (emac_phy->state == PHY_NWAY_WAIT) ||
+ (emac_phy->state == PHY_LINK_WAIT) ||
+ (emac_phy->state == PHY_LINKED) ||
+ (emac_phy->state == PHY_LOOPBACK)) {
+ emac_phy->state = PHY_FOUND;
+ emac_phy->state_change = 1;
+ }
+
+ DPRINTK("PhyMode:%08X Auto:%d, FD10:%d, HD10:%d, FD100:%d, HD100:%d\n",
+ phy_mode,
+ phy_mode & NWAY_AUTO, phy_mode & MII_NWAY_FD10,
+ phy_mode & MII_NWAY_HD10, phy_mode & MII_NWAY_FD100,
+ phy_mode & MII_NWAY_HD100);
+}
+
+/* Get linked status - check if link is on - 1=link on, 0=link off */
+inline int emac_mdio_is_linked(void)
+{
+ return ((emac_phy->state == PHY_LINKED) ? 1 : 0);
+}
+
+/* Get speed - 10 / 100 Mbps */
+inline int emac_mdio_get_speed(void)
+{
+ return emac_phy->speed;
+}
+
+/*
+ * Get duplex - 0=Auto Negotiate, Full Duplex = 3; Half Duplex = 2 Unknown = 1
+ */
+inline int emac_mdio_get_duplex(void)
+{
+ return emac_phy->duplex;
+}
+
+/* Get Phy number/address */
+inline int emac_mdio_get_phy_num(void)
+{
+ return emac_phy->phy_addr;
+}
+
+/* Check if loopback enabled on phy */
+inline int emac_mdio_is_loopback(void)
+{
+ return ((emac_phy->state == PHY_LOOPBACK) ? 1 : 0);
+}
+
+/* Wait until mdio is ready for next command */
+#define MDIO_WAIT_FOR_USER_ACCESS \
+ while ((MDIO_REG_READ(MDIO_USERACCESS_INST) & MDIO_USERACCESS_GO) != 0);
+
+/* Read from a phy register via mdio interface */
+unsigned int emac_mdio_read(unsigned int phy_addr, unsigned int phy_reg)
+{
+ unsigned int phy_data = 0;
+
+ /* Wait until mdio is ready for next command */
+ MDIO_WAIT_FOR_USER_ACCESS;
+
+ MDIO_REG_WRITE(MDIO_USERACCESS_INST,
+ (MDIO_USERACCESS_GO |
+ MDIO_USERACCESS_READ |
+ ((phy_reg << 21) & MDIO_USERACCESS_REGADR) |
+ ((phy_addr << 16) & MDIO_USERACCESS_PHYADR) |
+ (phy_data & MDIO_USERACCESS_DATA)));
+
+ /* Wait until mdio is ready for next command */
+ MDIO_WAIT_FOR_USER_ACCESS;
+
+ return (MDIO_REG_READ(MDIO_USERACCESS_INST) & MDIO_USERACCESS_DATA);
+}
+
+/* Write to a phy register via mdio interface */
+void emac_mdio_write(unsigned int phy_addr, unsigned int phy_reg,
+ unsigned int phy_data)
+{
+ /* Wait until mdio is ready for next command */
+ MDIO_WAIT_FOR_USER_ACCESS;
+
+ MDIO_REG_WRITE(MDIO_USERACCESS_INST,
+ (MDIO_USERACCESS_GO |
+ MDIO_USERACCESS_WRITE |
+ ((phy_reg << 21) & MDIO_USERACCESS_REGADR) |
+ ((phy_addr << 16) & MDIO_USERACCESS_PHYADR) |
+ (phy_data & MDIO_USERACCESS_DATA)));
+}
+
+/* Reset the selected phy */
+static void emac_mdio_phy_reset(unsigned int phy_addr)
+{
+ emac_mdio_write(phy_addr, PHY_CONTROL_REG, MII_PHY_RESET);
+ while (emac_mdio_read(phy_addr, PHY_CONTROL_REG) & MII_PHY_RESET);
+ /*
+ * CRITICAL: Fix for increasing PHY signal drive strength for
+ * TX lockup issue. On DaVinci EVM, the Intel LXT971 PHY
+ * signal strength was low causing TX to fail randomly. The
+ * fix is to Set bit 11 (Increased MII drive strength) of PHY
+ * register 26 (Digital Config register) on this phy.
+ */
+ emac_mdio_write(phy_addr, 26, ((emac_mdio_read(phy_addr, 26)) | 0x800));
+ emac_mdio_read(phy_addr, 26);
+}
+
+/* Timeout condition handler in PHY state machine */
+static void emac_mdio_phy_timeout(void)
+{
+ emac_phy->state = PHY_FOUND;
+ emac_phy->state_change = 1;
+}
+
+/* PHY state machine : Init state handler */
+static void emac_mdio_init_state(void)
+{
+ emac_phy->state = PHY_FINDING;
+ emac_phy->state_change = 1;
+ emac_phy->timeout = PHY_FIND_TIMEOUT;
+}
+
+/* PHY state machine : Finding state handler */
+static void emac_mdio_finding_state(void)
+{
+ unsigned int phy_alive_status;
+ int i;
+
+ emac_phy->phy_addr = PHY_NOT_FOUND;
+
+ /* Find if timeout complete */
+ if (emac_phy->timeout)
+ /* Allow some time for phy to show up in alive register */
+ --emac_phy->timeout;
+ else {
+ phy_alive_status = MDIO_REG_READ(MDIO_LINK);
+ /* Check phys based upon user mask */
+ phy_alive_status &= emac_phy->phy_mask;
+
+ /* Find the first interesting alive phy */
+ i = ffs(phy_alive_status) - 1;
+
+ if ((phy_alive_status) && (i < 32))
+ emac_phy->phy_addr = i;
+
+ if (emac_phy->phy_addr != PHY_NOT_FOUND) {
+ DPRINTK("PHY Found. Phy Number=%d\n",
+ emac_phy->phy_addr);
+ emac_phy->state = PHY_FOUND;
+ emac_phy->state_change = 1;
+ } else {
+ /* Set Timer for finding timeout */
+ DPRINTK("PHY NOT Found. Starting timeout\n");
+ emac_phy->timeout = PHY_RECK_TIMEOUT;
+ }
+ }
+}
+
+/* PHY state machine : Found state handler */
+static void emac_mdio_found_state(void)
+{
+ unsigned int phy_status;
+ unsigned int phy_num;
+ unsigned int cnt;
+ unsigned int nway_advertise;
+
+ /* Check if there is any phy mode requested by the user */
+ if (emac_phy->phy_mode == 0)
+ return;
+
+ /* Check alive phy's */
+ phy_status = MDIO_REG_READ(MDIO_LINK);
+ phy_status &= emac_phy->phy_mask; /* Check phys based upon user mask */
+
+ /*
+ * we will now isolate all our phys, except the one we have
+ * decided to use
+ */
+ for (phy_num = 0, cnt = 1; phy_num < 32; phy_num++, cnt <<= 1) {
+ if ((phy_status & cnt) && (phy_num != emac_phy->phy_addr)) {
+ /*
+ * Disable a phy that we are not using
+ *
+ * CRITICAL: Note that this code assums that there is
+ * only 1 phy connected if this is not the case then
+ * the next statement should be commented.
+ */
+ emac_mdio_write(emac_phy->phy_addr,
+ PHY_CONTROL_REG,
+ (MII_PHY_ISOLATE |
+ MII_PHY_PDOWN));
+ }
+ }
+
+ /* Reset the Phy and proceed with auto-negotiation */
+ emac_mdio_phy_reset(emac_phy->phy_addr);
+
+ /*
+ * Set the way Link will be Monitored, Check the Link Selection Method
+ */
+ if ((1 << emac_phy->phy_addr) & emac_phy->mlink_mask)
+ MDIO_REG_WRITE(MDIO_USERPHYSEL_INST,
+ (emac_phy->phy_addr | MDIO_USERPHYSEL_LINKSEL));
+
+ /* For Phy Internal loopback , need to wait until Phy found */
+ if (emac_phy->phy_mode & NWAY_LPBK) {
+ /* Set Phy in Loopback and read mdio to confirm */
+ emac_mdio_write(emac_phy->phy_addr, PHY_CONTROL_REG,
+ (MII_PHY_LOOP | MII_PHY_FD));
+ emac_mdio_read(emac_phy->phy_addr, PHY_STATUS_REG);
+ emac_phy->state = PHY_LOOPBACK;
+ emac_phy->state_change = 1;
+ return;
+ }
+
+ /* Start negotiation */
+ nway_advertise = MII_NWAY_SEL;
+ if (emac_phy->phy_mode & NWAY_FD100)
+ nway_advertise |= MII_NWAY_FD100;
+ if (emac_phy->phy_mode & NWAY_HD100)
+ nway_advertise |= MII_NWAY_HD100;
+ if (emac_phy->phy_mode & NWAY_FD10)
+ nway_advertise |= MII_NWAY_FD10;
+ if (emac_phy->phy_mode & NWAY_HD10)
+ nway_advertise |= MII_NWAY_HD10;
+
+ phy_status = emac_mdio_read(emac_phy->phy_addr, PHY_STATUS_REG);
+
+ if ((phy_status & MII_NWAY_CAPABLE)
+ && (emac_phy->phy_mode & NWAY_AUTO)) {
+ /*
+ * NWAY Phy Detected - following procedure for NWAY
+ * compliant Phys
+ */
+ emac_mdio_write(emac_phy->phy_addr, NWAY_ADVERTIZE_REG,
+ nway_advertise);
+ if (emac_phy->debug_mode) {
+ DPRINTK("NWAY Advertising: ");
+ if (nway_advertise & MII_NWAY_FD100)
+ DPRINTK("100 Mbps FullDuplex");
+ if (nway_advertise & MII_NWAY_HD100)
+ DPRINTK("100 Mbps HalfDuplex");
+ if (nway_advertise & MII_NWAY_FD10)
+ DPRINTK("10 Mbps FullDuplex");
+ if (nway_advertise & MII_NWAY_HD10)
+ DPRINTK("10 Mbps HalfDuplex");
+ DPRINTK("\n");
+ }
+
+ /* Start/Restart autonegotiation */
+ emac_mdio_write(emac_phy->phy_addr, PHY_CONTROL_REG,
+ MII_AUTO_NEGOTIATE_EN);
+ emac_mdio_write(emac_phy->phy_addr, PHY_CONTROL_REG,
+ (MII_AUTO_NEGOTIATE_EN | MII_RENEGOTIATE));
+ emac_phy->state = PHY_NWAY_START;
+ emac_phy->state_change = 1;
+ emac_phy->timeout = PHY_NWST_TIMEOUT;
+ } else {
+ /* Phy cannot do auto negotiation */
+ emac_phy->phy_mode &= ~NWAY_AUTO;
+ nway_advertise &= ~MII_NWAY_SEL;
+ phy_status = 0;
+
+ if (nway_advertise & (MII_NWAY_FD100 | MII_NWAY_HD100)) {
+ phy_status = MII_PHY_100;/* Set 100 Mbps if requested */
+ nway_advertise &= (MII_NWAY_FD100 | MII_NWAY_HD100);
+ } else
+ nway_advertise &= (MII_NWAY_FD10 | MII_NWAY_HD10);
+
+ /* Set Full duplex if requested */
+ if (nway_advertise & (MII_NWAY_FD100 | MII_NWAY_FD10))
+ phy_status |= MII_PHY_FD;
+
+ /* Set requested speed and duplex mode on phy */
+ emac_mdio_write(emac_phy->phy_addr, PHY_CONTROL_REG,
+ phy_status);
+
+ /* Set the phy speed and duplex mode */
+ emac_phy->speed = (phy_status & MII_PHY_100) ? 100 : 10;
+ emac_phy->duplex = (phy_status & MII_PHY_FD) ? 3 : 2;
+
+ emac_phy->state = PHY_LINK_WAIT;
+ emac_phy->state_change = 1;
+ emac_phy->timeout = PHY_LINK_TIMEOUT;
+ }
+}
+
+/* PHY state machine : NWAY Start state handler */
+static void emac_mdio_nwaystart_state(void)
+{
+ unsigned int status;
+
+ status = emac_mdio_read(emac_phy->phy_addr, PHY_CONTROL_REG);
+ if ((status & MII_RENEGOTIATE) == 0) {
+ /* Flush pending latched bits */
+ status = emac_mdio_read(emac_phy->phy_addr, PHY_STATUS_REG);
+ emac_phy->state = PHY_NWAY_WAIT;
+ emac_phy->state_change = 1;
+ emac_phy->timeout = PHY_NWDN_TIMEOUT;
+ } else {
+ if (emac_phy->timeout)
+ --emac_phy->timeout;
+ else
+ /*
+ * Timed Out for NWAY to start - very unlikely condition,
+ * back to Found
+ */
+ emac_mdio_phy_timeout();
+ }
+}
+
+/* PHY state machine : NWAY Wait state handler */
+static void emac_mdio_nwaywait_state(void)
+{
+ unsigned int status;
+ unsigned int my_cap, partner_cap, neg_mode;
+
+ /* Check if nway negotiation complete */
+ status = emac_mdio_read(emac_phy->phy_addr, PHY_STATUS_REG);
+
+ if (status & MII_NWAY_COMPLETE) {
+ /* negotiation complete, check for partner capabilities */
+ emac_phy->state_change = 1;
+ my_cap = emac_mdio_read(emac_phy->phy_addr, NWAY_ADVERTIZE_REG);
+ partner_cap =
+ emac_mdio_read(emac_phy->phy_addr, NWAY_REMADVERTISE_REG);
+
+ /* Negotiated mode is what we and partnet have in common */
+ neg_mode = my_cap & partner_cap;
+ if (emac_phy->debug_mode)
+ DPRINTK("Phy %d, neg_mode %04X, my_cap %04X,"
+ " partner_cap %04X\n", emac_phy->phy_addr,
+ neg_mode, my_cap, partner_cap);
+
+ /* Limit negotiation to fields below */
+ neg_mode &=
+ (MII_NWAY_FD100 | MII_NWAY_HD100 | MII_NWAY_FD10 |
+ MII_NWAY_HD10);
+ if (neg_mode == 0)
+ DPRINTK("WARNING: Negotiation complete but NO"
+ " agreement, default is 10HD\n");
+
+ if (neg_mode & MII_NWAY_FD100)
+ DPRINTK("100 Mbps FullDuplex");
+ if (neg_mode & MII_NWAY_HD100)
+ DPRINTK("100 Mbps HalfDuplex");
+ if (neg_mode & MII_NWAY_FD10)
+ DPRINTK("10 Mbps FullDuplex");
+ if (neg_mode & MII_NWAY_HD10)
+ DPRINTK("10 Mbps HalfDuplex");
+ DPRINTK("\n");
+
+ if (neg_mode != 0) {
+ if (status & MII_PHY_LINKED)
+ emac_phy->state = PHY_LINKED;
+ else
+ emac_phy->state = PHY_LINK_WAIT;
+ }
+
+ /* Set the phy speed and duplex mode */
+ emac_phy->speed =
+ (neg_mode & (MII_NWAY_FD100 | MII_NWAY_HD100)) ? 100 : 10;
+ emac_phy->duplex =
+ (neg_mode & (MII_NWAY_FD100 | MII_NWAY_FD10)) ?
+ PHY_DUPLEX_FULL : PHY_DUPLEX_HALF;
+ } else {
+
+ if (emac_phy->timeout)
+ --emac_phy->timeout;
+ else
+ /*
+ * Timed Out for NWAY to start - very unlikely
+ * condition, back to Found
+ */
+ emac_mdio_phy_timeout();
+ }
+}
+
+/* PHY state machine : Link Wait state handler */
+static void emac_mdio_linkwait_state(void)
+{
+ unsigned int status;
+
+ /* Check if nway negotiation complete */
+ status = emac_mdio_read(emac_phy->phy_addr, PHY_STATUS_REG);
+ if (status & MII_PHY_LINKED) {
+ emac_phy->state = PHY_LINKED;
+ emac_phy->state_change = 1;
+ } else {
+ if (emac_phy->timeout)
+ --emac_phy->timeout;
+ else
+ /*
+ * Timed Out for link - very unlikely condition,
+ * back to Found
+ */
+ emac_mdio_phy_timeout();
+ }
+}
+
+/* PHY state machine : Linked handler */
+static void emac_mdio_linked_state(void)
+{
+ if (MDIO_REG_READ(MDIO_LINK) & (1 << emac_phy->phy_addr))
+ return; /* do nothing if already linked */
+
+ /* If not linked, move mode to nway down or waiting for link */
+ emac_phy->state_change = 1;
+ if (emac_phy->phy_mode & NWAY_AUTO) {
+ emac_phy->state = PHY_NWAY_WAIT;
+ emac_phy->timeout = PHY_NWDN_TIMEOUT;
+ } else {
+ emac_phy->state = PHY_LINK_WAIT;
+ emac_phy->timeout = PHY_LINK_TIMEOUT;
+ }
+}
+
+/* PHY state machine : Loopback handler */
+static void emac_mdio_loopback_state(void)
+{
+}
+
+/* PHY state machine : Default handler */
+static void emac_mdio_default_state(void)
+{
+ /* Awaiting a init call */
+ emac_phy->state_change = 1;
+}
+
+/* Detailed PHY dump for debug */
+static void emac_mdio_phy_dump(void)
+{
+ unsigned int status;
+
+ DPRINTK("\n");
+ DPRINTK("PHY Addr/Num=%d, PHY State=%s, Speed=%d, Duplex=%d\n",
+ emac_phy->phy_addr, phy_state_str[emac_phy->state],
+ emac_phy->speed, emac_phy->duplex);
+
+ /* 0: Control register */
+ status = emac_mdio_read(emac_phy->phy_addr, PHY_CONTROL_REG);
+ DPRINTK("PhyControl: %04X, Loopback=%s, Speed=%s, Duplex=%s\n",
+ status,
+ status & MII_PHY_LOOP ? "On" : "Off",
+ status & MII_PHY_100 ? "100" : "10",
+ status & MII_PHY_FD ? "Full" : "Half");
+
+ /* 1: Status register */
+ status = emac_mdio_read(emac_phy->phy_addr, PHY_STATUS_REG);
+ DPRINTK("PhyStatus: %04X, AutoNeg=%s, Link=%s\n",
+ status,
+ status & MII_NWAY_COMPLETE ? "Complete" : "NotComplete",
+ status & MII_PHY_LINKED ? "Up" : "Down");
+
+ /* 4: Auto Negotiation Advertisement register */
+ status = emac_mdio_read(emac_phy->phy_addr, NWAY_ADVERTIZE_REG);
+ DPRINTK("PhyMyCapability: %04X, 100FD=%s, 100HD=%s, 10FD=%s, 10HD=%s\n",
+ status,
+ status & MII_NWAY_FD100 ? "Yes" : "No",
+ status & MII_NWAY_HD100 ? "Yes" : "No",
+ status & MII_NWAY_FD10 ? "Yes" : "No",
+ status & MII_NWAY_HD10 ? "Yes" : "No");
+
+ /* 5: Auto Negotiation Advertisement register */
+ status = emac_mdio_read(emac_phy->phy_addr, NWAY_REMADVERTISE_REG);
+ DPRINTK("PhyPartnerCapability: %04X, 100FD=%s,"
+ " 100HD=%s, 10FD=%s, 10HD=%s\n",
+ status, status & MII_NWAY_FD100 ? "Yes" : "No",
+ status & MII_NWAY_HD100 ? "Yes" : "No",
+ status & MII_NWAY_FD10 ? "Yes" : "No",
+ status & MII_NWAY_HD10 ? "Yes" : "No");
+}
+
+/* emac_mdio_tick is called every 10 mili seconds to process Phy states */
+int emac_mdio_tick(void)
+{
+ switch (emac_phy->state) {
+ case PHY_INIT:
+ emac_mdio_init_state();
+ break;
+ case PHY_FINDING:
+ emac_mdio_finding_state();
+ break;
+ case PHY_FOUND:
+ emac_mdio_found_state();
+ break;
+ case PHY_NWAY_START:
+ emac_mdio_nwaystart_state();
+ break;
+ case PHY_NWAY_WAIT:
+ emac_mdio_nwaywait_state();
+ break;
+ case PHY_LINK_WAIT:
+ emac_mdio_linkwait_state();
+ break;
+ case PHY_LINKED:
+ emac_mdio_linked_state();
+ break;
+ case PHY_LOOPBACK:
+ emac_mdio_loopback_state();
+ break;
+ default:
+ emac_mdio_default_state();
+ break;
+ }
+
+ /* Return state change to user */
+ if (emac_phy->state_change) {
+ emac_mdio_phy_dump();
+ emac_phy->state_change = 0;
+ return 1;
+ }
+ return 0;
+}
Index: 2.6.24-rc8.ether/drivers/net/arm/davinci_emac_phy.h
===================================================================
--- /dev/null
+++ 2.6.24-rc8.ether/drivers/net/arm/davinci_emac_phy.h
@@ -0,0 +1,106 @@
+/*
+ * linux/drivers/net/davinci_emac_phy.h
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) MontaVista Software, Inc. <source@mvista.com>
+ *
+ * This file is licensed under the terms of the
+ * GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * 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.
+ * ----------------------------------------------------------------------------
+ * Modifications:
+ * HISTORY:
+ * Date Modifier Ver Notes
+ * 01Jan01 Denis, Bill Original
+ * 27Mar02 Michael Hanrahan Original (modified from emacmdio.h)
+ * 04Apr02 Michael Hanrahan Added Interrupt Support
+ * 07Dec06 Paul Bartholomew Added PHY_DUPLEX_* defines
+ */
+#ifndef _DAVINCI_EMAC_PHY_H_
+#define _DAVINCI_EMAC_PHY_H_
+
+/* phy mode values */
+#define NWAY_AUTOMDIX (1<<16)
+#define NWAY_FD1000 (1<<13)
+#define NWAY_HD1000 (1<<12)
+#define NWAY_NOPHY (1<<10)
+#define NWAY_LPBK (1<<9)
+#define NWAY_FD100 (1<<8)
+#define NWAY_HD100 (1<<7)
+#define NWAY_FD10 (1<<6)
+#define NWAY_HD10 (1<<5)
+#define NWAY_AUTO (1<<0)
+
+/* phy duplex values */
+#define PHY_DUPLEX_AUTO 0 /* Auto Negotiate */
+#define PHY_DUPLEX_UNKNOWN 1 /* Unknown */
+#define PHY_DUPLEX_HALF 2 /* Half Duplex */
+#define PHY_DUPLEX_FULL 3 /* Full Duplex */
+
+/*
+ * Tic() return values
+ */
+
+#define _MIIMDIO_MDIXFLIP (1<<28)
+
+#define _AUTOMDIX_DELAY_MIN 80 /* milli-seconds */
+#define _AUTOMDIX_DELAY_MAX 200 /* milli-seconds */
+
+/* Get module version */
+void emac_mdio_get_ver(unsigned int mdio_base, unsigned int *module_id,
+ unsigned int *rev_major, unsigned int *rev_minor);
+
+/* Initialize mdio module */
+int emac_mdio_init(unsigned int mdio_base,
+ unsigned int inst,
+ unsigned int phy_mask,
+ unsigned int mlink_mask,
+ unsigned int mdio_bus_freq,
+ unsigned int mdio_clock_freq, unsigned int verbose);
+
+/* Set PHY mode - autonegotiation or any other */
+void emac_mdio_set_phy_mode(unsigned int phy_mode);
+
+/* Get linked status - check if link is on - 1=link on, 0=link off */
+int emac_mdio_is_linked(void);
+
+/* Get speed - 10 / 100 Mbps */
+int emac_mdio_get_speed(void);
+
+/* Get duplex - 2=full duplex, 1=half duplex */
+int emac_mdio_get_duplex(void);
+
+/* Get Phy number/address */
+int emac_mdio_get_phy_num(void);
+
+/* Check if loopback enabled on phy */
+int emac_mdio_is_loopback(void);
+
+/* Read from a phy register via mdio interface */
+unsigned int emac_mdio_read(unsigned int phy_addr, unsigned int phy_reg);
+
+/* Write to a phy register via mdio interface */
+void emac_mdio_write(unsigned int phy_addr, unsigned int phy_reg,
+ unsigned int phy_data);
+
+/* MDIO tick function - to be called every 10 mSecs */
+int emac_mdio_tick(void);
+
+#endif /* _DAVINIC_EMAC_PHY_H_ */
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox