* [NET]: should explicitely initialize atomic_t field in struct dst_ops
From: Eric Dumazet @ 2008-01-24 15:11 UTC (permalink / raw)
To: David Miller; +Cc: netdev@vger.kernel.org
All but one struct dst_ops static initializations miss explicit
initialization of entries field.
As this field is atomic_t, we should use ATOMIC_INIT(0), and not
rely on atomic_t implementation.
Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
net/ipv4/route.c | 2 ++
net/ipv4/xfrm4_policy.c | 1 +
net/ipv6/route.c | 2 ++
net/ipv6/xfrm6_policy.c | 1 +
4 files changed, 6 insertions(+)
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 896c768..163086b 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -169,6 +169,7 @@ static struct dst_ops ipv4_dst_ops = {
.update_pmtu = ip_rt_update_pmtu,
.local_out = ip_local_out,
.entry_size = sizeof(struct rtable),
+ .entries = ATOMIC_INIT(0),
};
#define ECN_OR_COST(class) TC_PRIO_##class
@@ -2498,6 +2499,7 @@ static struct dst_ops ipv4_dst_blackhole_ops = {
.check = ipv4_dst_check,
.update_pmtu = ipv4_rt_blackhole_update_pmtu,
.entry_size = sizeof(struct rtable),
+ .entries = ATOMIC_INIT(0),
};
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index 3783e3e..10ed704 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -247,6 +247,7 @@ static struct dst_ops xfrm4_dst_ops = {
.local_out = __ip_local_out,
.gc_thresh = 1024,
.entry_size = sizeof(struct xfrm_dst),
+ .entries = ATOMIC_INIT(0),
};
static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 4004c5f..8d669bb 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -107,6 +107,7 @@ static struct dst_ops ip6_dst_ops = {
.update_pmtu = ip6_rt_update_pmtu,
.local_out = ip6_local_out,
.entry_size = sizeof(struct rt6_info),
+ .entries = ATOMIC_INIT(0),
};
static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu)
@@ -120,6 +121,7 @@ static struct dst_ops ip6_dst_blackhole_ops = {
.check = ip6_dst_check,
.update_pmtu = ip6_rt_blackhole_update_pmtu,
.entry_size = sizeof(struct rt6_info),
+ .entries = ATOMIC_INIT(0),
};
struct rt6_info ip6_null_entry = {
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index c25a6b5..7d20199 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -272,6 +272,7 @@ static struct dst_ops xfrm6_dst_ops = {
.local_out = __ip6_local_out,
.gc_thresh = 1024,
.entry_size = sizeof(struct xfrm_dst),
+ .entries = ATOMIC_INIT(0),
};
static struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
^ permalink raw reply related
* Re: [PATCH] UCC TDM driver for QE based MPC83xx platforms.
From: Timur Tabi @ 2008-01-24 15:39 UTC (permalink / raw)
To: Stephen Rothwell
Cc: Poonam_Aggrwal-b10812, kumar.gala, akpm, linux-kernel, netdev,
rubini, linuxppc-dev, michael.barkowski, rich.cutler,
ashish.kalra
In-Reply-To: <20080124171920.49d805cb.sfr@canb.auug.org.au>
Stephen Rothwell wrote:
>> + tdm_ctrl[device_num]->ut_info->uf_info.tdm_tx_clk =
>> + (char *) of_get_property(np, "fsl,tdm-tx-clk", NULL);
> ^
> We don't normall put spaces here.
Since when?
--
Timur Tabi
Linux kernel developer at Freescale
^ 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: Anton Vorontsov @ 2008-01-24 15:48 UTC (permalink / raw)
To: Poonam_Aggrwal-b10812
Cc: kumar.gala, akpm, linux-kernel, netdev, rubini, linuxppc-dev,
michael.barkowski, rich.cutler, ashish.kalra, timur
In-Reply-To: <Pine.LNX.4.64.0801241553270.4984@linux121>
Hello Poonam,
On Thu, Jan 24, 2008 at 04:00:06PM +0530, Poonam_Aggrwal-b10812 wrote:
> Thanks Stephen for your comments, incorporated them.
> From: Poonam Aggrwal <b10812@freescale.com>
>
> This patch makes necessary changes in the QE and UCC framework to support
> TDM. It also adds support to configure the BRG properly through device
> tree entries. Includes the device tree changes for UCC TDM driver as well.
> It also includes device tree entries for UCC TDM driver.
>
> Tested on MPC8323ERDB platform.
>
> Signed-off-by: Poonam Aggrwal <b10812@freescale.com>
> Signed-off-by: Ashish Kalra <ashish.kalra@freescale.com>
> Signed-off-by: Kim Phillips <Kim.Phillips@freescale.com>
> Signed-off-by: Michael Barkowski <michael.barkowski@freescale.com>
> ---
> arch/powerpc/boot/dts/mpc832x_rdb.dts | 58 +++++++
> arch/powerpc/sysdev/qe_lib/qe.c | 184 +++++++++++++++++++++--
> arch/powerpc/sysdev/qe_lib/ucc.c | 265 +++++++++++++++++++++++++++++++++
> arch/powerpc/sysdev/qe_lib/ucc_fast.c | 37 +++++
> include/asm-powerpc/qe.h | 8 +
> include/asm-powerpc/ucc.h | 4 +
> include/asm-powerpc/ucc_fast.h | 4 +
> 7 files changed, 548 insertions(+), 12 deletions(-)
>
> diff --git a/arch/powerpc/boot/dts/mpc832x_rdb.dts b/arch/powerpc/boot/dts/mpc832x_rdb.dts
> index 388c8a7..c0e6283 100644
> --- a/arch/powerpc/boot/dts/mpc832x_rdb.dts
> +++ b/arch/powerpc/boot/dts/mpc832x_rdb.dts
> @@ -105,6 +105,17 @@
> device_type = "par_io";
> num-ports = <7>;
>
> + ucc1pio:ucc_pin@01 {
> + pio-map = <
> + /* port pin dir open_drain assignment has_irq */
> + 0 e 2 0 1 0 /* CLK11 */
> + 3 16 1 0 2 0 /* BRG9 */
> + 3 1b 1 0 2 0 /* BRG3 */
> + 0 0 3 0 2 0 /* TDMATxD0 */
> + 0 4 3 0 2 0 /* TDMARxD0 */
> + 3 1b 2 0 1 0>; /* CLK1 */
> + };
> +
Can we not introduce new pio-maps in the device trees? There
were debates regarding this, and if I understood everything
correctly, pio-maps considered as a bad taste. Better
do bunch of par_io_config_pin() in the board file. Better
yet fixup the firmware (u-boot) to set up dedicated pins
correctly.
Thanks,
--
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.net/bd2
^ permalink raw reply
* [PATCH net-2.6.25][IPV6]: Introduce the INET6_TW_MATCH() macro.
From: Pavel Emelyanov @ 2008-01-24 15:54 UTC (permalink / raw)
To: David Miller; +Cc: Linux Netdev List, devel
We have INET_MATCH, INET_TW_MATCH and INET6_MATCH to test
sockets and twbuckets for matching, but ipv6 twbuckets are
tested manually.
Here's the INET6_TW_MATCH to help with it.
Since the commit b3652b2dc5ec6ccd946ae9136b30c6babb81305a
[IPV6]: Mischecked tw match in __inet6_check_established.
is accepted this patch will not break the __inet6_check_est.
logic.
This will also help with per-namespace socket lookup patches
in the nearest future.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
---
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 5d35a4c..c347860 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -465,6 +465,14 @@ static inline struct raw6_sock *raw6_sk(const struct sock *sk)
ipv6_addr_equal(&inet6_sk(__sk)->rcv_saddr, (__daddr)) && \
(!((__sk)->sk_bound_dev_if) || ((__sk)->sk_bound_dev_if == (__dif))))
+#define INET6_TW_MATCH(__sk, __hash, __saddr, __daddr, __ports, __dif) \
+ (((__sk)->sk_hash == (__hash)) && \
+ (*((__portpair *)&(inet_twsk(__sk)->tw_dport)) == (__ports)) && \
+ ((__sk)->sk_family == PF_INET6) && \
+ (ipv6_addr_equal(&inet6_twsk(__sk)->tw_v6_daddr, (__saddr))) && \
+ (ipv6_addr_equal(&inet6_twsk(__sk)->tw_v6_rcv_saddr, (__daddr))) && \
+ (!((__sk)->sk_bound_dev_if) || ((__sk)->sk_bound_dev_if == (__dif))))
+
#endif /* __KERNEL__ */
#endif /* _IPV6_H */
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c
index d0b3447..06b01be 100644
--- a/net/ipv6/inet6_hashtables.c
+++ b/net/ipv6/inet6_hashtables.c
@@ -80,17 +80,8 @@ struct sock *__inet6_lookup_established(struct inet_hashinfo *hashinfo,
}
/* Must check for a TIME_WAIT'er before going to listener hash. */
sk_for_each(sk, node, &head->twchain) {
- const struct inet_timewait_sock *tw = inet_twsk(sk);
-
- if(*((__portpair *)&(tw->tw_dport)) == ports &&
- sk->sk_family == PF_INET6) {
- const struct inet6_timewait_sock *tw6 = inet6_twsk(sk);
-
- if (ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) &&
- ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) &&
- (!sk->sk_bound_dev_if || sk->sk_bound_dev_if == dif))
- goto hit;
- }
+ if (INET6_TW_MATCH(sk, hash, saddr, daddr, ports, dif))
+ goto hit;
}
read_unlock(lock);
return NULL;
@@ -185,15 +176,9 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row,
/* Check TIME-WAIT sockets first. */
sk_for_each(sk2, node, &head->twchain) {
- const struct inet6_timewait_sock *tw6 = inet6_twsk(sk2);
-
tw = inet_twsk(sk2);
- if(*((__portpair *)&(tw->tw_dport)) == ports &&
- sk2->sk_family == PF_INET6 &&
- ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) &&
- ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) &&
- sk2->sk_bound_dev_if == sk->sk_bound_dev_if) {
+ if (INET6_TW_MATCH(sk2, hash, saddr, daddr, ports, dif)) {
if (twsk_unique(sk, sk2, twp))
goto unique;
else
^ permalink raw reply related
* Re: [PATCH UCC TDM 1/3 Updated] Platform changes for UCC TDM driver for MPC8323eRDB. Also includes related QE changes and dts entries.
From: Timur Tabi @ 2008-01-24 15:55 UTC (permalink / raw)
To: avorontsov
Cc: Poonam_Aggrwal-b10812, kumar.gala, akpm, linux-kernel, netdev,
rubini, linuxppc-dev, michael.barkowski, rich.cutler,
ashish.kalra
In-Reply-To: <20080124154804.GA22178@localhost.localdomain>
Anton Vorontsov wrote:
> Can we not introduce new pio-maps in the device trees? There
> were debates regarding this, and if I understood everything
> correctly, pio-maps considered as a bad taste. Better
> do bunch of par_io_config_pin() in the board file. Better
> yet fixup the firmware (u-boot) to set up dedicated pins
> correctly.
I'm on the fence with respect to pio-maps vs. par_io_config_pin() calls. The
problem is that the configuration of these pins is board-specific, but pins are
used by devices. A device driver can't call par_io_config_pin(), because the
calls are different depending on which SoC and which UCC you're using. The
platform code can't call par_io_config_pin(), because that configuration depends
on which drivers are loaded.
In other words, the pin configurations are dependent on the UCC configurations,
and the UCC configurations are stored in the device tree. So it makes sense to
put the pin configurations in the device tree, too.
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* [VLAN] set_rx_mode support for unicast address list
From: Chris Leech @ 2008-01-24 16:07 UTC (permalink / raw)
To: netdev, Patrick McHardy; +Cc: Chris Leech
Reuse the existing logic for multicast list synchronization for the unicast
address list. The core of dev_mc_sync/unsync are split out as
__dev_addr_sync/unsync and moved from dev_mcast.c to dev.c. These are then
used to implement dev_unicast_sync/unsync as well.
I'm working on cleaning up Intel's FCoE stack, which generates new MAC
addresses from the fibre channel device id assigned by the fabric as per the
current draft specification in T11. When using such a protocol in a VLAN
environment it would be nice to not always be forced into promiscuous mode,
assuming the underlying Ethernet driver supports multiple unicast addresses as
well.
Signed-off-by: Chris Leech <christopher.leech@intel.com>
---
include/linux/netdevice.h | 4 ++
net/8021q/vlan_dev.c | 7 ++-
net/core/dev.c | 96 +++++++++++++++++++++++++++++++++++++++++++++
net/core/dev_mcast.c | 39 ++----------------
4 files changed, 110 insertions(+), 36 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index b0813c3..047d432 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1414,12 +1414,16 @@ extern void dev_set_rx_mode(struct net_device *dev);
extern void __dev_set_rx_mode(struct net_device *dev);
extern int dev_unicast_delete(struct net_device *dev, void *addr, int alen);
extern int dev_unicast_add(struct net_device *dev, void *addr, int alen);
+extern int dev_unicast_sync(struct net_device *to, struct net_device *from);
+extern void dev_unicast_unsync(struct net_device *to, struct net_device *from);
extern int dev_mc_delete(struct net_device *dev, void *addr, int alen, int all);
extern int dev_mc_add(struct net_device *dev, void *addr, int alen, int newonly);
extern int dev_mc_sync(struct net_device *to, struct net_device *from);
extern void dev_mc_unsync(struct net_device *to, struct net_device *from);
extern int __dev_addr_delete(struct dev_addr_list **list, int *count, void *addr, int alen, int all);
extern int __dev_addr_add(struct dev_addr_list **list, int *count, void *addr, int alen, int newonly);
+extern int __dev_addr_sync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count);
+extern void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count);
extern void dev_set_promiscuity(struct net_device *dev, int inc);
extern void dev_set_allmulti(struct net_device *dev, int inc);
extern void netdev_state_change(struct net_device *dev);
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 8059fa4..77f04e4 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -563,6 +563,7 @@ static int vlan_dev_stop(struct net_device *dev)
struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
dev_mc_unsync(real_dev, dev);
+ dev_unicast_unsync(real_dev, dev);
if (dev->flags & IFF_ALLMULTI)
dev_set_allmulti(real_dev, -1);
if (dev->flags & IFF_PROMISC)
@@ -634,9 +635,10 @@ static void vlan_dev_change_rx_flags(struct net_device *dev, int change)
dev_set_promiscuity(real_dev, dev->flags & IFF_PROMISC ? 1 : -1);
}
-static void vlan_dev_set_multicast_list(struct net_device *vlan_dev)
+static void vlan_dev_set_rx_mode(struct net_device *vlan_dev)
{
dev_mc_sync(vlan_dev_info(vlan_dev)->real_dev, vlan_dev);
+ dev_unicast_sync(vlan_dev_info(vlan_dev)->real_dev, vlan_dev);
}
/*
@@ -702,7 +704,8 @@ void vlan_setup(struct net_device *dev)
dev->open = vlan_dev_open;
dev->stop = vlan_dev_stop;
dev->set_mac_address = vlan_dev_set_mac_address;
- dev->set_multicast_list = vlan_dev_set_multicast_list;
+ dev->set_rx_mode = vlan_dev_set_rx_mode;
+ dev->set_multicast_list = vlan_dev_set_rx_mode;
dev->change_rx_flags = vlan_dev_change_rx_flags;
dev->do_ioctl = vlan_dev_ioctl;
dev->destructor = free_netdev;
diff --git a/net/core/dev.c b/net/core/dev.c
index c9c593e..edaff27 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2962,6 +2962,102 @@ int dev_unicast_add(struct net_device *dev, void *addr, int alen)
}
EXPORT_SYMBOL(dev_unicast_add);
+int __dev_addr_sync(struct dev_addr_list **to, int *to_count,
+ struct dev_addr_list **from, int *from_count)
+{
+ struct dev_addr_list *da, *next;
+ int err = 0;
+
+ da = *from;
+ while (da != NULL) {
+ next = da->next;
+ if (!da->da_synced) {
+ err = __dev_addr_add(to, to_count,
+ da->da_addr, da->da_addrlen, 0);
+ if (err < 0)
+ break;
+ da->da_synced = 1;
+ da->da_users++;
+ } else if (da->da_users == 1) {
+ __dev_addr_delete(to, to_count,
+ da->da_addr, da->da_addrlen, 0);
+ __dev_addr_delete(from, from_count,
+ da->da_addr, da->da_addrlen, 0);
+ }
+ da = next;
+ }
+ return err;
+}
+
+void __dev_addr_unsync(struct dev_addr_list **to, int *to_count,
+ struct dev_addr_list **from, int *from_count)
+{
+ struct dev_addr_list *da, *next;
+
+ da = *from;
+ while (da != NULL) {
+ next = da->next;
+ if (da->da_synced) {
+ __dev_addr_delete(to, to_count,
+ da->da_addr, da->da_addrlen, 0);
+ da->da_synced = 0;
+ __dev_addr_delete(from, from_count,
+ da->da_addr, da->da_addrlen, 0);
+ }
+ da = next;
+ }
+}
+
+/**
+ * dev_unicast_sync - Synchronize device's unicast list to another device
+ * @to: destination device
+ * @from: source device
+ *
+ * Add newly added addresses to the destination device and release
+ * addresses that have no users left. The source device must be
+ * locked by netif_tx_lock_bh.
+ *
+ * This function is intended to be called from the dev->set_rx_mode
+ * function of layered software devices.
+ */
+int dev_unicast_sync(struct net_device *to, struct net_device *from)
+{
+ int err = 0;
+
+ netif_tx_lock_bh(to);
+ err = __dev_addr_sync(&to->uc_list, &to->uc_count,
+ &from->uc_list, &from->uc_count);
+ if (!err)
+ __dev_set_rx_mode(to);
+ netif_tx_unlock_bh(to);
+ return err;
+}
+EXPORT_SYMBOL(dev_unicast_sync);
+
+/**
+ * dev_unicast_unsync - Remove synchronized addresses from the destination
+ * device
+ * @to: destination device
+ * @from: source device
+ *
+ * Remove all addresses that were added to the destination device by
+ * dev_unicast_sync(). This function is intended to be called from the
+ * dev->stop function of layered software devices.
+ */
+void dev_unicast_unsync(struct net_device *to, struct net_device *from)
+{
+ netif_tx_lock_bh(from);
+ netif_tx_lock_bh(to);
+
+ __dev_addr_unsync(&to->uc_list, &to->uc_count,
+ &from->uc_list, &from->uc_count);
+ __dev_set_rx_mode(to);
+
+ netif_tx_unlock_bh(to);
+ netif_tx_unlock_bh(from);
+}
+EXPORT_SYMBOL(dev_unicast_unsync);
+
static void __dev_addr_discard(struct dev_addr_list **list)
{
struct dev_addr_list *tmp;
diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c
index cadbfbf..cec5825 100644
--- a/net/core/dev_mcast.c
+++ b/net/core/dev_mcast.c
@@ -113,32 +113,15 @@ int dev_mc_add(struct net_device *dev, void *addr, int alen, int glbl)
* locked by netif_tx_lock_bh.
*
* This function is intended to be called from the dev->set_multicast_list
- * function of layered software devices.
+ * or dev->set_rx_mode function of layered software devices.
*/
int dev_mc_sync(struct net_device *to, struct net_device *from)
{
- struct dev_addr_list *da, *next;
int err = 0;
netif_tx_lock_bh(to);
- da = from->mc_list;
- while (da != NULL) {
- next = da->next;
- if (!da->da_synced) {
- err = __dev_addr_add(&to->mc_list, &to->mc_count,
- da->da_addr, da->da_addrlen, 0);
- if (err < 0)
- break;
- da->da_synced = 1;
- da->da_users++;
- } else if (da->da_users == 1) {
- __dev_addr_delete(&to->mc_list, &to->mc_count,
- da->da_addr, da->da_addrlen, 0);
- __dev_addr_delete(&from->mc_list, &from->mc_count,
- da->da_addr, da->da_addrlen, 0);
- }
- da = next;
- }
+ err = __dev_addr_sync(&to->mc_list, &to->mc_count,
+ &from->mc_list, &from->mc_count);
if (!err)
__dev_set_rx_mode(to);
netif_tx_unlock_bh(to);
@@ -160,23 +143,11 @@ EXPORT_SYMBOL(dev_mc_sync);
*/
void dev_mc_unsync(struct net_device *to, struct net_device *from)
{
- struct dev_addr_list *da, *next;
-
netif_tx_lock_bh(from);
netif_tx_lock_bh(to);
- da = from->mc_list;
- while (da != NULL) {
- next = da->next;
- if (da->da_synced) {
- __dev_addr_delete(&to->mc_list, &to->mc_count,
- da->da_addr, da->da_addrlen, 0);
- da->da_synced = 0;
- __dev_addr_delete(&from->mc_list, &from->mc_count,
- da->da_addr, da->da_addrlen, 0);
- }
- da = next;
- }
+ __dev_addr_unsync(&to->mc_list, &to->mc_count,
+ &from->mc_list, &from->mc_count);
__dev_set_rx_mode(to);
netif_tx_unlock_bh(to);
^ permalink raw reply related
* Re: [IPV4 0/9] TRIE performance patches
From: Stephen Hemminger @ 2008-01-24 16:18 UTC (permalink / raw)
To: Robert Olsson; +Cc: Robert Olsson, David Miller, netdev
In-Reply-To: <18328.23597.205001.332244@robur.slu.se>
On Thu, 24 Jan 2008 10:36:45 +0100
Robert Olsson <Robert.Olsson@data.slu.se> wrote:
>
> Stephen Hemminger writes:
>
> > Dumping by prefix is possible, but unless 32x slower. Dumping in
> > address order is just as logical. Like I said, I'm investigating what
> > quagga handles.
>
> How about taking a snapshot to in address order (as you did) to some
> allocated memory, returning from that memory in prefix order? This would
> solve the -EBUSY too and give a consistent view of the routing table at
> the time for the dump/snapshot.
>
> Cheers
> --ro
Snapshotting is going to work, because of scale and because the kernel can't
tell when application is going to come back.
--
Stephen Hemminger <stephen.hemminger@vyatta.com>
^ 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: Anton Vorontsov @ 2008-01-24 16:23 UTC (permalink / raw)
To: Timur Tabi
Cc: Poonam_Aggrwal-b10812, kumar.gala, akpm, linux-kernel, netdev,
rubini, linuxppc-dev, michael.barkowski, rich.cutler,
ashish.kalra
In-Reply-To: <4798B4F3.2010101@freescale.com>
On Thu, Jan 24, 2008 at 09:55:31AM -0600, Timur Tabi wrote:
> Anton Vorontsov wrote:
>
> >Can we not introduce new pio-maps in the device trees? There
> >were debates regarding this, and if I understood everything
> >correctly, pio-maps considered as a bad taste. Better
> >do bunch of par_io_config_pin() in the board file. Better
> >yet fixup the firmware (u-boot) to set up dedicated pins
> >correctly.
>
> I'm on the fence with respect to pio-maps vs. par_io_config_pin() calls.
> The problem is that the configuration of these pins is board-specific, but
> pins are used by devices. A device driver can't call par_io_config_pin(),
> because the calls are different depending on which SoC and which UCC you're
> using. The platform code can't call par_io_config_pin(), because that
> configuration depends on which drivers are loaded.
Are you saying that TDM is sharing same pins with the other QE device,
and we can choose to use/not use some device depending on which driver
is loaded? I think this particular board and patch isn't that case.
Even if someday there will be the case when drivers are mutually
exclusive, i.e. presence of some driver should trigger pins
reconfiguration, then anyway this should be handled differently.
That is, we should not _register_ two mutually exclusive devices
in the first place, so drivers will not probe them. That's board
setup code authority, and pins configuration still should happen
there.
[ Irrelevant to UCCs and this particular case: lately I've
encountered one interesting case of Par IO usage. FHCI USB needs
switching between pin's dedicated functions and GPIO functions.
So, firstly it is using pins as dedicated, and later (at the bus
reset) driver turns them to act as GPIOs. This is still handled
without pio-map though, via gpios = <> property for that driver. ]
> In other words, the pin configurations are dependent on the UCC
> configurations, and the UCC configurations are stored in the device tree.
> So it makes sense to put the pin configurations in the device tree, too.
In that particular case UCC configuration is static, for every UCC.
So, we can set up all pins in the firmware/board file.
Please correct me if I'm wrong.
--
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.net/bd2
^ 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: Timur Tabi @ 2008-01-24 16:33 UTC (permalink / raw)
To: avorontsov
Cc: Poonam_Aggrwal-b10812, kumar.gala, akpm, linux-kernel, netdev,
rubini, linuxppc-dev, michael.barkowski, rich.cutler,
ashish.kalra
In-Reply-To: <20080124162345.GA27359@localhost.localdomain>
Anton Vorontsov wrote:
> Are you saying that TDM is sharing same pins with the other QE device,
> and we can choose to use/not use some device depending on which driver
> is loaded?
No. I'd have to closely examine the DTS, but I don't think that UCC devices
share pins at all. But that isn't my point.
> In that particular case UCC configuration is static, for every UCC.
> So, we can set up all pins in the firmware/board file.
Yes, but deciding what the UCC does might not be static. At what point do we
declare, "UCC5 is for eth0 and eth0 only"?
The advantage of putting the pin configurations in the device tree is that they
now become configurable. I can envision a scenario where UCC5 could be either
an Ethernet or a UART, depending on the setting of some jumpers on the board.
That's what the QE was designed for: any UCC can do any task, and you can even
have a UCC change its purpose while the system is running. So I don't want the
pin configurations hard-coded into the kernel. Having them in the device tree
gives me some flexibility.
For instance, I have a plan (that I keep postponing) to introduce a new feature
in U-Boot where U-Boot can determine the settings of some board jumpers and
modify the device tree accordingly. The instructions on how to modify the
device tree would be embedded in the tree itself. I can't support this feature
if the kernel calls par_io_config_pin() regardless of what's in the device tree.
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* Re: [Bugme-new] [Bug 9806] New: (tun dev) Impossible to deassert IFF_ONE_QUEUE or IFF_NO_PI
From: Nathaniel Filardo @ 2008-01-24 17:12 UTC (permalink / raw)
To: maxk, vtun, netdev
In-Reply-To: <20080124003356.ec51432a.akpm@linux-foundation.org>
[-- Attachment #1: Type: text/plain, Size: 330 bytes --]
On Jan 24, 2008 3:33 AM, Andrew Morton <akpm@linux-foundation.org> wrote:
> > On Wed, 23 Jan 2008 13:13:13 -0800 (PST) bugme-daemon@bugzilla.kernel.org wrote:
> > http://bugzilla.kernel.org/show_bug.cgi?id=9806
> Thanks. Could you please submit the patch via email? Send it to
> all recipients of this email.
Attached.
--nwf;
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: tun.patch --]
[-- Type: text/x-diff; name=tun.patch, Size: 381 bytes --]
--- tun.c.orig 2008-01-23 16:09:25.000000000 -0500
+++ tun.c 2008-01-23 16:12:26.000000000 -0500
@@ -540,9 +540,13 @@
if (ifr->ifr_flags & IFF_NO_PI)
tun->flags |= TUN_NO_PI;
+ else
+ tun->flags &= ~TUN_NO_PI;
if (ifr->ifr_flags & IFF_ONE_QUEUE)
tun->flags |= TUN_ONE_QUEUE;
+ else
+ tun->flags &= ~TUN_ONE_QUEUE;
file->private_data = tun;
tun->attached = 1;
^ permalink raw reply
* Re: [XFRM]: constify 'struct xfrm_type'
From: Joe Perches @ 2008-01-24 17:17 UTC (permalink / raw)
To: Eric Dumazet; +Cc: David Miller, netdev@vger.kernel.org
In-Reply-To: <20080124122621.3248c651.dada1@cosmosbay.com>
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?
^ 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: Anton Vorontsov @ 2008-01-24 17:23 UTC (permalink / raw)
To: Timur Tabi
Cc: Poonam_Aggrwal-b10812, kumar.gala, akpm, linux-kernel, netdev,
rubini, linuxppc-dev, michael.barkowski, rich.cutler,
ashish.kalra
In-Reply-To: <4798BDEB.2010501@freescale.com>
On Thu, Jan 24, 2008 at 10:33:47AM -0600, Timur Tabi wrote:
> Anton Vorontsov wrote:
>
> >Are you saying that TDM is sharing same pins with the other QE device,
> >and we can choose to use/not use some device depending on which driver
> >is loaded?
>
> No. I'd have to closely examine the DTS, but I don't think that UCC
> devices share pins at all. But that isn't my point.
>
> >In that particular case UCC configuration is static, for every UCC.
> >So, we can set up all pins in the firmware/board file.
>
> Yes, but deciding what the UCC does might not be static. At what point do
> we declare, "UCC5 is for eth0 and eth0 only"?
>
> The advantage of putting the pin configurations in the device tree is that
> they now become configurable. I can envision a scenario where UCC5 could
> be either an Ethernet or a UART, depending on the setting of some jumpers
> on the board. That's what the QE was designed for: any UCC can do any task,
> and you can even have a UCC change its purpose while the system is running.
> So I don't want the pin configurations hard-coded into the kernel. Having
> them in the device tree gives me some flexibility.
If hardware configuration is selected at the bootup time, by jumpers
or switches, it's even easier to do it right. Without pio-map.
> For instance, I have a plan (that I keep postponing) to introduce a new
> feature in U-Boot where U-Boot can determine the settings of some board
> jumpers and modify the device tree accordingly. The instructions on how to
> modify the device tree would be embedded in the tree itself.
Why you need to modify the device tree for that? Let the U-Boot simply
setup pins for the kernel. Regarding kernel overwriting pins
configuration...
> I can't
> support this feature if the kernel calls par_io_config_pin() regardless of
> what's in the device tree.
What I've understood from the previous debates, is that ideally kernel
should not touch pins' configuration. Today we're using pio-map solely
to fix up some old firmware misconfiguration. And we can do this in the
board file still. To determine if we need to fixup the firmware or not,
we can use some device tree property instead (firmware version?).
p.s.
I'm neither for pio-map nor against. I just want some consequence
regarding this. Last thread ended with consequence that pio-map is a
bad thing to use...
--
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.net/bd2
^ permalink raw reply
* Re: [VLAN] set_rx_mode support for unicast address list
From: Patrick McHardy @ 2008-01-24 17:27 UTC (permalink / raw)
To: Chris Leech; +Cc: netdev
In-Reply-To: <20080124160738.11781.23745.stgit@localhost.localdomain>
Chris Leech wrote:
> Reuse the existing logic for multicast list synchronization for the unicast
> address list. The core of dev_mc_sync/unsync are split out as
> __dev_addr_sync/unsync and moved from dev_mcast.c to dev.c. These are then
> used to implement dev_unicast_sync/unsync as well.
>
> I'm working on cleaning up Intel's FCoE stack, which generates new MAC
> addresses from the fibre channel device id assigned by the fabric as per the
> current draft specification in T11. When using such a protocol in a VLAN
> environment it would be nice to not always be forced into promiscuous mode,
> assuming the underlying Ethernet driver supports multiple unicast addresses as
> well.
Looks good, applied. Thanks Chris.
^ permalink raw reply
* Re: [PATCH 1/5] netns netfilter: per-netns ip6tables
From: Patrick McHardy @ 2008-01-24 17:33 UTC (permalink / raw)
To: Alexey Dobriyan; +Cc: netdev, netfilter-devel, devel
In-Reply-To: <20080124122328.GA27337@localhost.sw.ru>
Alexey Dobriyan wrote:
> * Propagate netns from userspace down to xt_find_table_lock()
> * Register ip6 tables in netns (modules still use init_net)
>
> Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
> ---
>
> include/linux/netfilter_ipv6/ip6_tables.h | 3 +
> net/ipv6/netfilter/ip6_tables.c | 50 +++++++++++++++---------------
> net/ipv6/netfilter/ip6table_filter.c | 2 -
> net/ipv6/netfilter/ip6table_mangle.c | 2 -
> net/ipv6/netfilter/ip6table_raw.c | 2 -
> 5 files changed, 31 insertions(+), 28 deletions(-)
This adds checkpatch warnings:
WARNING: line over 80 characters
#96: FILE: net/ipv6/netfilter/ip6_tables.c:1361:
+do_add_counters(struct net *net, void __user *user, unsigned int len,
int compat)
WARNING: line over 80 characters
#229: FILE: net/ipv6/netfilter/ip6table_filter.c:135:
+ packet_filter = ip6t_register_table(&init_net, &__packet_filter,
&initial_table.repl);
WARNING: line over 80 characters
#242: FILE: net/ipv6/netfilter/ip6table_mangle.c:167:
+ packet_mangler = ip6t_register_table(&init_net,
&__packet_mangler, &initial_table.repl);
WARNING: line over 80 characters
#255: FILE: net/ipv6/netfilter/ip6table_raw.c:80:
+ packet_raw = ip6t_register_table(&init_net, &__packet_raw,
&initial_table.repl);
ERROR: Missing Signed-off-by: line(s)
total: 1 errors, 4 warnings, 214 lines checked
I'll fix them up, lets hope that it doesn't cause clashes
with the following patches.
^ permalink raw reply
* Re: [PATCH 2/5] netns netfilter: per-netns IPv6 FILTER, MANGLE, RAW
From: Patrick McHardy @ 2008-01-24 17:40 UTC (permalink / raw)
To: Alexey Dobriyan; +Cc: netdev, netfilter-devel, devel
In-Reply-To: <20080124122748.GB27337@localhost.sw.ru>
Alexey Dobriyan wrote:
> Now it's possible to list and manipulate per-netns ip6tables rules.
> Filtering decisions are based on init_net's table so far.
>
> P.S.: remove init_net check in inet6_create() to see the effect
OK, this patch fixes all but one checkpatch warning again.
Please try to make each patch checkpatch-clean next time
since that causes unnecessary work. Thanks (and applied).
^ permalink raw reply
* Re: [PATCH 3/5] netns netfilter: per-netns arp_tables
From: Patrick McHardy @ 2008-01-24 17:41 UTC (permalink / raw)
To: Alexey Dobriyan; +Cc: netdev, netfilter-devel, devel
In-Reply-To: <20080124122852.GC27337@localhost.sw.ru>
Alexey Dobriyan wrote:
> * Propagate netns from userspace.
> * arpt_register_table() registers table in supplied netns.
Applied.
^ permalink raw reply
* Re: [PATCH 4/5] netns netfilter: per-netns arp_tables FILTER
From: Patrick McHardy @ 2008-01-24 17:46 UTC (permalink / raw)
To: Alexey Dobriyan; +Cc: netdev, netfilter-devel, devel
In-Reply-To: <20080124122930.GD27337@localhost.sw.ru>
Alexey Dobriyan wrote:
> Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Applied.
^ permalink raw reply
* Re: [PATCH 5/5] netns netfilter: put table module on netns stop
From: Patrick McHardy @ 2008-01-24 17:48 UTC (permalink / raw)
To: Alexey Dobriyan; +Cc: netdev, netfilter-devel, devel
In-Reply-To: <20080124123002.GE27337@localhost.sw.ru>
Alexey Dobriyan wrote:
> When number of entries exceeds number of initial entries, foo-tables code
> will pin table module. But during table unregister on netns stop,
> that additional pin was forgotten.
Applied, thanks.
^ permalink raw reply
* Slow OOM in netif_RX function
From: Ivan Dichev @ 2008-01-24 17:28 UTC (permalink / raw)
To: netdev
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
^ permalink raw reply
* [Patch 2.6.24 1/1]S2io: Support for vlan_rx_kill_vid entry point
From: Sreenivasa Honnur @ 2008-01-24 18:07 UTC (permalink / raw)
To: netdev, jeff; +Cc: support
- Added s2io_vlan_rx_kill_vid entry point function for unregistering vlan.
- Fix to aggregate vlan packets. IP offset is incremented by
4 bytes if the packet contains vlan header.
Signed-off-by: Surjit Reang <surjit.reang@neterion.com>
Signed-off-by: Ramkrishna Vepa <ram.vepa@neterion.com>
---
diff -Nurp 2-0-26-17/drivers/net/s2io.c 2-0-26-18-1/drivers/net/s2io.c
--- 2-0-26-17/drivers/net/s2io.c 2008-01-24 04:22:30.000000000 +0530
+++ 2-0-26-18-1/drivers/net/s2io.c 2008-01-24 04:32:22.000000000 +0530
@@ -380,6 +380,19 @@ static void s2io_vlan_rx_register(struct
/* A flag indicating whether 'RX_PA_CFG_STRIP_VLAN_TAG' bit is set or not */
static int vlan_strip_flag;
+/* Unregister the vlan */
+static void s2io_vlan_rx_kill_vid(struct net_device *dev, unsigned long vid)
+{
+ unsigned long flags;
+ struct s2io_nic *nic = dev->priv;
+
+ spin_lock_irqsave(&nic->tx_lock, flags);
+ if (nic->vlgrp)
+ vlan_group_set_device(nic->vlgrp, vid, NULL);
+
+ spin_unlock_irqrestore(&nic->tx_lock, flags);
+}
+
/*
* Constants to be programmed into the Xena's registers, to configure
* the XAUI.
@@ -2905,7 +2918,7 @@ static void rx_intr_handler(struct ring_
struct lro *lro = &nic->lro0_n[i];
if (lro->in_use) {
update_L3L4_header(nic, lro);
- queue_rx_frame(lro->parent);
+ queue_rx_frame(lro->parent, lro->vlan_tag);
clear_lro_session(lro);
}
}
@@ -7177,7 +7190,8 @@ static int rx_osm_handler(struct ring_in
{
lro_append_pkt(sp, lro,
skb, tcp_len);
- queue_rx_frame(lro->parent);
+ queue_rx_frame(lro->parent,
+ lro->vlan_tag);
clear_lro_session(lro);
sp->mac_control.stats_info->
sw_stat.flush_max_pkts++;
@@ -7188,7 +7202,8 @@ static int rx_osm_handler(struct ring_in
lro->frags_len;
sp->mac_control.stats_info->
sw_stat.sending_both++;
- queue_rx_frame(lro->parent);
+ queue_rx_frame(lro->parent,
+ lro->vlan_tag);
clear_lro_session(lro);
goto send_up;
case 0: /* sessions exceeded */
@@ -7214,31 +7229,12 @@ static int rx_osm_handler(struct ring_in
*/
skb->ip_summed = CHECKSUM_NONE;
}
- } else {
+ } else
skb->ip_summed = CHECKSUM_NONE;
- }
+
sp->mac_control.stats_info->sw_stat.mem_freed += skb->truesize;
- if (!sp->lro) {
- skb->protocol = eth_type_trans(skb, dev);
- if ((sp->vlgrp && RXD_GET_VLAN_TAG(rxdp->Control_2) &&
- vlan_strip_flag)) {
- /* Queueing the vlan frame to the upper layer */
- if (napi)
- vlan_hwaccel_receive_skb(skb, sp->vlgrp,
- RXD_GET_VLAN_TAG(rxdp->Control_2));
- else
- vlan_hwaccel_rx(skb, sp->vlgrp,
- RXD_GET_VLAN_TAG(rxdp->Control_2));
- } else {
- if (napi)
- netif_receive_skb(skb);
- else
- netif_rx(skb);
- }
- } else {
send_up:
- queue_rx_frame(skb);
- }
+ queue_rx_frame(skb, RXD_GET_VLAN_TAG(rxdp->Control_2));
dev->last_rx = jiffies;
aggregate:
atomic_dec(&sp->rx_bufs_left[ring_no]);
@@ -7589,6 +7585,7 @@ s2io_init_nic(struct pci_dev *pdev, cons
SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
dev->vlan_rx_register = s2io_vlan_rx_register;
+ dev->vlan_rx_kill_vid = (void *)s2io_vlan_rx_kill_vid;
/*
* will use eth_mac_addr() for dev->set_mac_address
@@ -7845,7 +7842,8 @@ module_init(s2io_starter);
module_exit(s2io_closer);
static int check_L2_lro_capable(u8 *buffer, struct iphdr **ip,
- struct tcphdr **tcp, struct RxD_t *rxdp)
+ struct tcphdr **tcp, struct RxD_t *rxdp,
+ struct s2io_nic *sp)
{
int ip_off;
u8 l2_type = (u8)((rxdp->Control_1 >> 37) & 0x7), ip_len;
@@ -7856,19 +7854,20 @@ static int check_L2_lro_capable(u8 *buff
return -1;
}
- /* TODO:
- * By default the VLAN field in the MAC is stripped by the card, if this
- * feature is turned off in rx_pa_cfg register, then the ip_off field
- * has to be shifted by a further 2 bytes
- */
- switch (l2_type) {
- case 0: /* DIX type */
- case 4: /* DIX type with VLAN */
- ip_off = HEADER_ETHERNET_II_802_3_SIZE;
- break;
+ /* Checking for DIX type or DIX type with VLAN */
+ if ((l2_type == 0)
+ || (l2_type == 4)) {
+ ip_off = HEADER_ETHERNET_II_802_3_SIZE;
+ /*
+ * If vlan stripping is disabled and the frame is VLAN tagged,
+ * shift the offset by the VLAN header size bytes.
+ */
+ if ((!vlan_strip_flag) &&
+ (rxdp->Control_1 & RXD_FRAME_VLAN_TAG))
+ ip_off += HEADER_VLAN_SIZE;
+ } else {
/* LLC, SNAP etc are considered non-mergeable */
- default:
- return -1;
+ return -1;
}
*ip = (struct iphdr *)((u8 *)buffer + ip_off);
@@ -7895,7 +7894,7 @@ static inline int get_l4_pyld_length(str
}
static void initiate_new_session(struct lro *lro, u8 *l2h,
- struct iphdr *ip, struct tcphdr *tcp, u32 tcp_pyld_len)
+ struct iphdr *ip, struct tcphdr *tcp, u32 tcp_pyld_len, u16 vlan_tag)
{
DBG_PRINT(INFO_DBG,"%s: Been here...\n", __FUNCTION__);
lro->l2h = l2h;
@@ -7906,6 +7905,7 @@ static void initiate_new_session(struct
lro->sg_num = 1;
lro->total_len = ntohs(ip->tot_len);
lro->frags_len = 0;
+ lro->vlan_tag = vlan_tag;
/*
* check if we saw TCP timestamp. Other consistency checks have
* already been done.
@@ -8037,15 +8037,16 @@ s2io_club_tcp_session(u8 *buffer, u8 **t
struct iphdr *ip;
struct tcphdr *tcph;
int ret = 0, i;
+ u16 vlan_tag = 0;
if (!(ret = check_L2_lro_capable(buffer, &ip, (struct tcphdr **)tcp,
- rxdp))) {
+ rxdp, sp))) {
DBG_PRINT(INFO_DBG,"IP Saddr: %x Daddr: %x\n",
ip->saddr, ip->daddr);
- } else {
+ } else
return ret;
- }
+ vlan_tag = RXD_GET_VLAN_TAG(rxdp->Control_2);
tcph = (struct tcphdr *)*tcp;
*tcp_len = get_l4_pyld_length(ip, tcph);
for (i=0; i<MAX_LRO_SESSIONS; i++) {
@@ -8105,7 +8106,8 @@ s2io_club_tcp_session(u8 *buffer, u8 **t
switch (ret) {
case 3:
- initiate_new_session(*lro, buffer, ip, tcph, *tcp_len);
+ initiate_new_session(*lro, buffer, ip, tcph, *tcp_len,
+ vlan_tag);
break;
case 2:
update_L3L4_header(sp, *lro);
@@ -8133,15 +8135,25 @@ static void clear_lro_session(struct lro
memset(lro, 0, lro_struct_size);
}
-static void queue_rx_frame(struct sk_buff *skb)
+static void queue_rx_frame(struct sk_buff *skb, u16 vlan_tag)
{
struct net_device *dev = skb->dev;
+ struct s2io_nic *sp = dev->priv;
skb->protocol = eth_type_trans(skb, dev);
- if (napi)
- netif_receive_skb(skb);
- else
- netif_rx(skb);
+ if (sp->vlgrp && vlan_tag
+ && (vlan_strip_flag)) {
+ /* Queueing the vlan frame to the upper layer */
+ if (sp->config.napi)
+ vlan_hwaccel_receive_skb(skb, sp->vlgrp, vlan_tag);
+ else
+ vlan_hwaccel_rx(skb, sp->vlgrp, vlan_tag);
+ } else {
+ if (sp->config.napi)
+ netif_receive_skb(skb);
+ else
+ netif_rx(skb);
+ }
}
static void lro_append_pkt(struct s2io_nic *sp, struct lro *lro,
diff -Nurp 2-0-26-17/drivers/net/s2io.h 2-0-26-18-1/drivers/net/s2io.h
--- 2-0-26-17/drivers/net/s2io.h 2008-01-24 04:22:30.000000000 +0530
+++ 2-0-26-18-1/drivers/net/s2io.h 2008-01-24 04:26:10.000000000 +0530
@@ -528,6 +528,7 @@ struct RxD_t {
#define RXD_OWN_XENA s2BIT(7)
#define RXD_T_CODE (s2BIT(12)|s2BIT(13)|s2BIT(14)|s2BIT(15))
#define RXD_FRAME_PROTO vBIT(0xFFFF,24,8)
+#define RXD_FRAME_VLAN_TAG s2BIT(24)
#define RXD_FRAME_PROTO_IPV4 s2BIT(27)
#define RXD_FRAME_PROTO_IPV6 s2BIT(28)
#define RXD_FRAME_IP_FRAG s2BIT(29)
@@ -796,10 +797,12 @@ struct lro {
int sg_num;
int in_use;
__be16 window;
+ u16 vlan_tag;
u32 cur_tsval;
u32 cur_tsecr;
u8 saw_ts;
-};
+} ____cacheline_aligned;
+
/* These flags represent the devices temporary state */
enum s2io_device_state_t
@@ -1071,7 +1074,7 @@ static int
s2io_club_tcp_session(u8 *buffer, u8 **tcp, u32 *tcp_len, struct lro **lro,
struct RxD_t *rxdp, struct s2io_nic *sp);
static void clear_lro_session(struct lro *lro);
-static void queue_rx_frame(struct sk_buff *skb);
+static void queue_rx_frame(struct sk_buff *skb, u16 vlan_tag);
static void update_L3L4_header(struct s2io_nic *sp, struct lro *lro);
static void lro_append_pkt(struct s2io_nic *sp, struct lro *lro,
struct sk_buff *skb, u32 tcp_len);
^ permalink raw reply
* [Patch 2.6.24 1/2]S2io: Fix for LRO Bugs
From: Sreenivasa Honnur @ 2008-01-24 18:08 UTC (permalink / raw)
To: netdev, jeff; +Cc: support
Resubmitting patch from Al Viro <viro@zeniv.linux.org.uk>, with subject -
[PATCH] s2io LRO bugs.
a) initiate_new_session() sets ->tcp_ack to ntohl(...); everything
else stores and expects to find there the net-endian value.
b) check for monotonic timestamps in verify_l3_l4_lro_capable()
compares the value sitting in TCP option (right there in the skb->data,
net-endian 32bit) with the value picked from earlier packet.
Doing that without ntohl() is an interesting idea and it might even
work occasionally; unfortunately, it's quite broken.
Signed-off-by: Surjit Reang <surjit.reang@neterion.com>
Signed-off-by: Ramkrishna Vepa <ram.vepa@neterion.com>
---
diff -Nurp 2-0-26-18-1/drivers/net/s2io.c 2-0-26-18-2/drivers/net/s2io.c
--- 2-0-26-18-1/drivers/net/s2io.c 2008-01-24 04:32:22.000000000 +0530
+++ 2-0-26-18-2/drivers/net/s2io.c 2008-01-24 04:43:36.000000000 +0530
@@ -7901,7 +7901,7 @@ static void initiate_new_session(struct
lro->iph = ip;
lro->tcph = tcp;
lro->tcp_next_seq = tcp_pyld_len + ntohl(tcp->seq);
- lro->tcp_ack = ntohl(tcp->ack_seq);
+ lro->tcp_ack = tcp->ack_seq;
lro->sg_num = 1;
lro->total_len = ntohs(ip->tot_len);
lro->frags_len = 0;
@@ -7911,10 +7911,10 @@ static void initiate_new_session(struct
* already been done.
*/
if (tcp->doff == 8) {
- u32 *ptr;
- ptr = (u32 *)(tcp+1);
+ __be32 *ptr;
+ ptr = (__be32 *)(tcp+1);
lro->saw_ts = 1;
- lro->cur_tsval = *(ptr+1);
+ lro->cur_tsval = ntohl(*(ptr+1));
lro->cur_tsecr = *(ptr+2);
}
lro->in_use = 1;
@@ -7940,7 +7940,7 @@ static void update_L3L4_header(struct s2
/* Update tsecr field if this session has timestamps enabled */
if (lro->saw_ts) {
- u32 *ptr = (u32 *)(tcp + 1);
+ __be32 *ptr = (__be32 *)(tcp + 1);
*(ptr+2) = lro->cur_tsecr;
}
@@ -7965,10 +7965,10 @@ static void aggregate_new_rx(struct lro
lro->window = tcp->window;
if (lro->saw_ts) {
- u32 *ptr;
+ __be32 *ptr;
/* Update tsecr and tsval from this packet */
- ptr = (u32 *) (tcp + 1);
- lro->cur_tsval = *(ptr + 1);
+ ptr = (__be32 *)(tcp+1);
+ lro->cur_tsval = ntohl(*(ptr+1));
lro->cur_tsecr = *(ptr + 2);
}
}
@@ -8019,11 +8019,11 @@ static int verify_l3_l4_lro_capable(stru
/* Ensure timestamp value increases monotonically */
if (l_lro)
- if (l_lro->cur_tsval > *((u32 *)(ptr+2)))
+ if (l_lro->cur_tsval > ntohl(*((__be32 *)(ptr+2))))
return -1;
/* timestamp echo reply should be non-zero */
- if (*((u32 *)(ptr+6)) == 0)
+ if (*((__be32 *)(ptr+6)) == 0)
return -1;
}
diff -Nurp 2-0-26-18-1/drivers/net/s2io.h 2-0-26-18-2/drivers/net/s2io.h
--- 2-0-26-18-1/drivers/net/s2io.h 2008-01-24 04:26:10.000000000 +0530
+++ 2-0-26-18-2/drivers/net/s2io.h 2008-01-24 04:27:28.000000000 +0530
@@ -799,7 +799,7 @@ struct lro {
__be16 window;
u16 vlan_tag;
u32 cur_tsval;
- u32 cur_tsecr;
+ __be32 cur_tsecr;
u8 saw_ts;
} ____cacheline_aligned;
^ permalink raw reply
* [Patch 2.6.24 3/3]S2io: Version update for [Patch 2.6.24 1/3] & [Patch 2.6.24 2/3]
From: Sreenivasa Honnur @ 2008-01-24 18:11 UTC (permalink / raw)
To: netdev, jeff; +Cc: support
- Updated version number.
Signed-off-by: Surjit Reang <surjit.reang@neterion.com>
Signed-off-by: Ramkrishna Vepa <ram.vepa@neterion.com>
---
diff -Nurp 2-0-26-18-2/drivers/net/s2io.c 2-0-26-18-3/drivers/net/s2io.c
--- 2-0-26-18-2/drivers/net/s2io.c 2008-01-24 04:27:23.000000000 +0530
+++ 2-0-26-18-3/drivers/net/s2io.c 2008-01-24 04:39:07.000000000 +0530
@@ -84,7 +84,7 @@
#include "s2io.h"
#include "s2io-regs.h"
-#define DRV_VERSION "2.0.26.17"
+#define DRV_VERSION "2.0.26.18"
/* S2io Driver name & version. */
static char s2io_driver_name[] = "Neterion";
^ permalink raw reply
* [Patch 2.6.24 1/3]S2io: Support for vlan_rx_kill_vid entry point
From: Sreenivasa Honnur @ 2008-01-24 18:13 UTC (permalink / raw)
To: netdev, jeff; +Cc: support
- Added s2io_vlan_rx_kill_vid entry point function for unregistering vlan.
- Fix to aggregate vlan packets. IP offset is incremented by
4 bytes if the packet contains vlan header.
Signed-off-by: Surjit Reang <surjit.reang@neterion.com>
Signed-off-by: Ramkrishna Vepa <ram.vepa@neterion.com>
---
diff -Nurp 2-0-26-17/drivers/net/s2io.c 2-0-26-18-1/drivers/net/s2io.c
--- 2-0-26-17/drivers/net/s2io.c 2008-01-24 04:22:30.000000000 +0530
+++ 2-0-26-18-1/drivers/net/s2io.c 2008-01-24 04:32:22.000000000 +0530
@@ -380,6 +380,19 @@ static void s2io_vlan_rx_register(struct
/* A flag indicating whether 'RX_PA_CFG_STRIP_VLAN_TAG' bit is set or not */
static int vlan_strip_flag;
+/* Unregister the vlan */
+static void s2io_vlan_rx_kill_vid(struct net_device *dev, unsigned long vid)
+{
+ unsigned long flags;
+ struct s2io_nic *nic = dev->priv;
+
+ spin_lock_irqsave(&nic->tx_lock, flags);
+ if (nic->vlgrp)
+ vlan_group_set_device(nic->vlgrp, vid, NULL);
+
+ spin_unlock_irqrestore(&nic->tx_lock, flags);
+}
+
/*
* Constants to be programmed into the Xena's registers, to configure
* the XAUI.
@@ -2905,7 +2918,7 @@ static void rx_intr_handler(struct ring_
struct lro *lro = &nic->lro0_n[i];
if (lro->in_use) {
update_L3L4_header(nic, lro);
- queue_rx_frame(lro->parent);
+ queue_rx_frame(lro->parent, lro->vlan_tag);
clear_lro_session(lro);
}
}
@@ -7177,7 +7190,8 @@ static int rx_osm_handler(struct ring_in
{
lro_append_pkt(sp, lro,
skb, tcp_len);
- queue_rx_frame(lro->parent);
+ queue_rx_frame(lro->parent,
+ lro->vlan_tag);
clear_lro_session(lro);
sp->mac_control.stats_info->
sw_stat.flush_max_pkts++;
@@ -7188,7 +7202,8 @@ static int rx_osm_handler(struct ring_in
lro->frags_len;
sp->mac_control.stats_info->
sw_stat.sending_both++;
- queue_rx_frame(lro->parent);
+ queue_rx_frame(lro->parent,
+ lro->vlan_tag);
clear_lro_session(lro);
goto send_up;
case 0: /* sessions exceeded */
@@ -7214,31 +7229,12 @@ static int rx_osm_handler(struct ring_in
*/
skb->ip_summed = CHECKSUM_NONE;
}
- } else {
+ } else
skb->ip_summed = CHECKSUM_NONE;
- }
+
sp->mac_control.stats_info->sw_stat.mem_freed += skb->truesize;
- if (!sp->lro) {
- skb->protocol = eth_type_trans(skb, dev);
- if ((sp->vlgrp && RXD_GET_VLAN_TAG(rxdp->Control_2) &&
- vlan_strip_flag)) {
- /* Queueing the vlan frame to the upper layer */
- if (napi)
- vlan_hwaccel_receive_skb(skb, sp->vlgrp,
- RXD_GET_VLAN_TAG(rxdp->Control_2));
- else
- vlan_hwaccel_rx(skb, sp->vlgrp,
- RXD_GET_VLAN_TAG(rxdp->Control_2));
- } else {
- if (napi)
- netif_receive_skb(skb);
- else
- netif_rx(skb);
- }
- } else {
send_up:
- queue_rx_frame(skb);
- }
+ queue_rx_frame(skb, RXD_GET_VLAN_TAG(rxdp->Control_2));
dev->last_rx = jiffies;
aggregate:
atomic_dec(&sp->rx_bufs_left[ring_no]);
@@ -7589,6 +7585,7 @@ s2io_init_nic(struct pci_dev *pdev, cons
SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
dev->vlan_rx_register = s2io_vlan_rx_register;
+ dev->vlan_rx_kill_vid = (void *)s2io_vlan_rx_kill_vid;
/*
* will use eth_mac_addr() for dev->set_mac_address
@@ -7845,7 +7842,8 @@ module_init(s2io_starter);
module_exit(s2io_closer);
static int check_L2_lro_capable(u8 *buffer, struct iphdr **ip,
- struct tcphdr **tcp, struct RxD_t *rxdp)
+ struct tcphdr **tcp, struct RxD_t *rxdp,
+ struct s2io_nic *sp)
{
int ip_off;
u8 l2_type = (u8)((rxdp->Control_1 >> 37) & 0x7), ip_len;
@@ -7856,19 +7854,20 @@ static int check_L2_lro_capable(u8 *buff
return -1;
}
- /* TODO:
- * By default the VLAN field in the MAC is stripped by the card, if this
- * feature is turned off in rx_pa_cfg register, then the ip_off field
- * has to be shifted by a further 2 bytes
- */
- switch (l2_type) {
- case 0: /* DIX type */
- case 4: /* DIX type with VLAN */
- ip_off = HEADER_ETHERNET_II_802_3_SIZE;
- break;
+ /* Checking for DIX type or DIX type with VLAN */
+ if ((l2_type == 0)
+ || (l2_type == 4)) {
+ ip_off = HEADER_ETHERNET_II_802_3_SIZE;
+ /*
+ * If vlan stripping is disabled and the frame is VLAN tagged,
+ * shift the offset by the VLAN header size bytes.
+ */
+ if ((!vlan_strip_flag) &&
+ (rxdp->Control_1 & RXD_FRAME_VLAN_TAG))
+ ip_off += HEADER_VLAN_SIZE;
+ } else {
/* LLC, SNAP etc are considered non-mergeable */
- default:
- return -1;
+ return -1;
}
*ip = (struct iphdr *)((u8 *)buffer + ip_off);
@@ -7895,7 +7894,7 @@ static inline int get_l4_pyld_length(str
}
static void initiate_new_session(struct lro *lro, u8 *l2h,
- struct iphdr *ip, struct tcphdr *tcp, u32 tcp_pyld_len)
+ struct iphdr *ip, struct tcphdr *tcp, u32 tcp_pyld_len, u16 vlan_tag)
{
DBG_PRINT(INFO_DBG,"%s: Been here...\n", __FUNCTION__);
lro->l2h = l2h;
@@ -7906,6 +7905,7 @@ static void initiate_new_session(struct
lro->sg_num = 1;
lro->total_len = ntohs(ip->tot_len);
lro->frags_len = 0;
+ lro->vlan_tag = vlan_tag;
/*
* check if we saw TCP timestamp. Other consistency checks have
* already been done.
@@ -8037,15 +8037,16 @@ s2io_club_tcp_session(u8 *buffer, u8 **t
struct iphdr *ip;
struct tcphdr *tcph;
int ret = 0, i;
+ u16 vlan_tag = 0;
if (!(ret = check_L2_lro_capable(buffer, &ip, (struct tcphdr **)tcp,
- rxdp))) {
+ rxdp, sp))) {
DBG_PRINT(INFO_DBG,"IP Saddr: %x Daddr: %x\n",
ip->saddr, ip->daddr);
- } else {
+ } else
return ret;
- }
+ vlan_tag = RXD_GET_VLAN_TAG(rxdp->Control_2);
tcph = (struct tcphdr *)*tcp;
*tcp_len = get_l4_pyld_length(ip, tcph);
for (i=0; i<MAX_LRO_SESSIONS; i++) {
@@ -8105,7 +8106,8 @@ s2io_club_tcp_session(u8 *buffer, u8 **t
switch (ret) {
case 3:
- initiate_new_session(*lro, buffer, ip, tcph, *tcp_len);
+ initiate_new_session(*lro, buffer, ip, tcph, *tcp_len,
+ vlan_tag);
break;
case 2:
update_L3L4_header(sp, *lro);
@@ -8133,15 +8135,25 @@ static void clear_lro_session(struct lro
memset(lro, 0, lro_struct_size);
}
-static void queue_rx_frame(struct sk_buff *skb)
+static void queue_rx_frame(struct sk_buff *skb, u16 vlan_tag)
{
struct net_device *dev = skb->dev;
+ struct s2io_nic *sp = dev->priv;
skb->protocol = eth_type_trans(skb, dev);
- if (napi)
- netif_receive_skb(skb);
- else
- netif_rx(skb);
+ if (sp->vlgrp && vlan_tag
+ && (vlan_strip_flag)) {
+ /* Queueing the vlan frame to the upper layer */
+ if (sp->config.napi)
+ vlan_hwaccel_receive_skb(skb, sp->vlgrp, vlan_tag);
+ else
+ vlan_hwaccel_rx(skb, sp->vlgrp, vlan_tag);
+ } else {
+ if (sp->config.napi)
+ netif_receive_skb(skb);
+ else
+ netif_rx(skb);
+ }
}
static void lro_append_pkt(struct s2io_nic *sp, struct lro *lro,
diff -Nurp 2-0-26-17/drivers/net/s2io.h 2-0-26-18-1/drivers/net/s2io.h
--- 2-0-26-17/drivers/net/s2io.h 2008-01-24 04:22:30.000000000 +0530
+++ 2-0-26-18-1/drivers/net/s2io.h 2008-01-24 04:26:10.000000000 +0530
@@ -528,6 +528,7 @@ struct RxD_t {
#define RXD_OWN_XENA s2BIT(7)
#define RXD_T_CODE (s2BIT(12)|s2BIT(13)|s2BIT(14)|s2BIT(15))
#define RXD_FRAME_PROTO vBIT(0xFFFF,24,8)
+#define RXD_FRAME_VLAN_TAG s2BIT(24)
#define RXD_FRAME_PROTO_IPV4 s2BIT(27)
#define RXD_FRAME_PROTO_IPV6 s2BIT(28)
#define RXD_FRAME_IP_FRAG s2BIT(29)
@@ -796,10 +797,12 @@ struct lro {
int sg_num;
int in_use;
__be16 window;
+ u16 vlan_tag;
u32 cur_tsval;
u32 cur_tsecr;
u8 saw_ts;
-};
+} ____cacheline_aligned;
+
/* These flags represent the devices temporary state */
enum s2io_device_state_t
@@ -1071,7 +1074,7 @@ static int
s2io_club_tcp_session(u8 *buffer, u8 **tcp, u32 *tcp_len, struct lro **lro,
struct RxD_t *rxdp, struct s2io_nic *sp);
static void clear_lro_session(struct lro *lro);
-static void queue_rx_frame(struct sk_buff *skb);
+static void queue_rx_frame(struct sk_buff *skb, u16 vlan_tag);
static void update_L3L4_header(struct s2io_nic *sp, struct lro *lro);
static void lro_append_pkt(struct s2io_nic *sp, struct lro *lro,
struct sk_buff *skb, u32 tcp_len);
^ permalink raw reply
* [Patch 2.6.24 2/3]S2io: Fix for LRO Bugs
From: Sreenivasa Honnur @ 2008-01-24 18:14 UTC (permalink / raw)
To: netdev, jeff; +Cc: support
Resubmitting patch from Al Viro <viro@zeniv.linux.org.uk>, with subject -
[PATCH] s2io LRO bugs.
a) initiate_new_session() sets ->tcp_ack to ntohl(...); everything
else stores and expects to find there the net-endian value.
b) check for monotonic timestamps in verify_l3_l4_lro_capable()
compares the value sitting in TCP option (right there in the skb->data,
net-endian 32bit) with the value picked from earlier packet.
Doing that without ntohl() is an interesting idea and it might even
work occasionally; unfortunately, it's quite broken.
Signed-off-by: Surjit Reang <surjit.reang@neterion.com>
Signed-off-by: Ramkrishna Vepa <ram.vepa@neterion.com>
---
diff -Nurp 2-0-26-18-1/drivers/net/s2io.c 2-0-26-18-2/drivers/net/s2io.c
--- 2-0-26-18-1/drivers/net/s2io.c 2008-01-24 04:32:22.000000000 +0530
+++ 2-0-26-18-2/drivers/net/s2io.c 2008-01-24 04:43:36.000000000 +0530
@@ -7901,7 +7901,7 @@ static void initiate_new_session(struct
lro->iph = ip;
lro->tcph = tcp;
lro->tcp_next_seq = tcp_pyld_len + ntohl(tcp->seq);
- lro->tcp_ack = ntohl(tcp->ack_seq);
+ lro->tcp_ack = tcp->ack_seq;
lro->sg_num = 1;
lro->total_len = ntohs(ip->tot_len);
lro->frags_len = 0;
@@ -7911,10 +7911,10 @@ static void initiate_new_session(struct
* already been done.
*/
if (tcp->doff == 8) {
- u32 *ptr;
- ptr = (u32 *)(tcp+1);
+ __be32 *ptr;
+ ptr = (__be32 *)(tcp+1);
lro->saw_ts = 1;
- lro->cur_tsval = *(ptr+1);
+ lro->cur_tsval = ntohl(*(ptr+1));
lro->cur_tsecr = *(ptr+2);
}
lro->in_use = 1;
@@ -7940,7 +7940,7 @@ static void update_L3L4_header(struct s2
/* Update tsecr field if this session has timestamps enabled */
if (lro->saw_ts) {
- u32 *ptr = (u32 *)(tcp + 1);
+ __be32 *ptr = (__be32 *)(tcp + 1);
*(ptr+2) = lro->cur_tsecr;
}
@@ -7965,10 +7965,10 @@ static void aggregate_new_rx(struct lro
lro->window = tcp->window;
if (lro->saw_ts) {
- u32 *ptr;
+ __be32 *ptr;
/* Update tsecr and tsval from this packet */
- ptr = (u32 *) (tcp + 1);
- lro->cur_tsval = *(ptr + 1);
+ ptr = (__be32 *)(tcp+1);
+ lro->cur_tsval = ntohl(*(ptr+1));
lro->cur_tsecr = *(ptr + 2);
}
}
@@ -8019,11 +8019,11 @@ static int verify_l3_l4_lro_capable(stru
/* Ensure timestamp value increases monotonically */
if (l_lro)
- if (l_lro->cur_tsval > *((u32 *)(ptr+2)))
+ if (l_lro->cur_tsval > ntohl(*((__be32 *)(ptr+2))))
return -1;
/* timestamp echo reply should be non-zero */
- if (*((u32 *)(ptr+6)) == 0)
+ if (*((__be32 *)(ptr+6)) == 0)
return -1;
}
diff -Nurp 2-0-26-18-1/drivers/net/s2io.h 2-0-26-18-2/drivers/net/s2io.h
--- 2-0-26-18-1/drivers/net/s2io.h 2008-01-24 04:26:10.000000000 +0530
+++ 2-0-26-18-2/drivers/net/s2io.h 2008-01-24 04:27:28.000000000 +0530
@@ -799,7 +799,7 @@ struct lro {
__be16 window;
u16 vlan_tag;
u32 cur_tsval;
- u32 cur_tsecr;
+ __be32 cur_tsecr;
u8 saw_ts;
} ____cacheline_aligned;
^ 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