* [PATCH ethtool] ethtool: Fix handling of zero weights for flow hash indirection
From: Ben Hutchings @ 2010-08-06 13:38 UTC (permalink / raw)
To: Jeff Garzik; +Cc: netdev, linux-net-drivers
The loop to generate an indirection table from a list of weights never
advances by more than one weight at a time. Thus, if there is a 0
in the list (except at the end) the corresponding RX ring will be
assigned 1 hash bucket rather than 0. Change 'if' to 'while'.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
Please include this in 2.6.35 if you haven't finalised it yet.
Ben.
ethtool.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/ethtool.c b/ethtool.c
index 0f7dec6..66b5c07 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -2977,7 +2977,7 @@ static int do_srxfhindir(int fd, struct ifreq *ifr)
j = -1;
for (i = 0; i < indir->size; i++) {
- if (i >= indir->size * partial / sum) {
+ while (i >= indir->size * partial / sum) {
j += 1;
weight = get_u32(rxfhindir_weight[j], 0);
partial += weight;
--
1.6.2.5
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply related
* [ANNOUNCE]: Release of iptables-1.4.9.1
From: Patrick McHardy @ 2010-08-06 13:51 UTC (permalink / raw)
To: Netfilter Development Mailinglist, NetDev, netfilter-announce,
"'netfilter@vger.kernel.org'" <netfilter
[-- Attachment #1: Type: text/plain, Size: 423 bytes --]
The netfilter coreteam presents:
iptables version 1.4.9.1
the iptables release for the 2.6.35 kernel. This release fixes
a compilation problem with static linking in the 1.4.9 release.
Version 1.4.9.1 can be obtained from:
http://www.netfilter.org/projects/iptables/downloads.html
ftp://ftp.netfilter.org/pub/iptables/
git://git.netfilter.org/iptables.git
On behalf of the Netfilter Core Team.
Happy firewalling!
[-- Attachment #2: changes-iptables-1.4.9.1.txt --]
[-- Type: text/plain, Size: 105 bytes --]
Jan Engelhardt (1):
build: fix static linking
Patrick McHardy (1):
Bump version to 1.4.9.1
^ permalink raw reply
* Re: [PATCH v2 0/2] netfilter: xtables: xt_condition inclusion and change to u32
From: Patrick McHardy @ 2010-08-06 13:54 UTC (permalink / raw)
To: luciano.coelho; +Cc: netfilter-devel, netdev, jengelh, sameo
In-Reply-To: <1281097308-1515-1-git-send-email-luciano.coelho@nokia.com>
Am 06.08.2010 14:21, schrieb luciano.coelho@nokia.com:
> From: Luciano Coelho <luciano.coelho@nokia.com>
>
> Hello,
>
> Here v2 of the xt_condition patch, it contains some changes suggested by Jan.
>
> When these patches get accepted, I'll send the implementation of the condition
> target, as discussed before.
>
> In RFC v2 I've made a few changes as discussed in the review:
>
> * Removed per-netns module parameters
> * Use par->net instead of current->nsproxy->net_ns
> * Fix file-leak in procfs when exiting the netns
>
> I didn't get any more comments in RFC v2, so I assume it is okay to send it for
> inclusion.
The merge window for networking patches is closed, I'm only taking
bugfixes at this time. Please resend once net-next opens up again.
^ permalink raw reply
* Re: IPVS network namespace aware ?
From: Simon Horman @ 2010-08-06 13:59 UTC (permalink / raw)
To: Hans Schillstrom; +Cc: netfilter-devel, netdev, lvs-devel
In-Reply-To: <1281099450.2518.21.camel@seasc0214.dyn.rnd.as.sw.ericsson.se>
[ Cced lvs-devel ]
On Fri, Aug 06, 2010 at 02:57:30PM +0200, Hans Schillstrom wrote:
> Hello
> Is there any one that have made ipvs network namespace aware ?
> There is an urgent need of it within Ericsson.
> I could not find any trace of it in any of the normal lists.
>
> I will start patching ipvs next week or give a helping hand to any
> existing work.
Hi Hans,
I am not aware of any other work in this area.
Please send any patches here :-)
^ permalink raw reply
* Re: [rfc 02/13] [RFC 02/13] netfilter: nf_conntrack_sip: Add callid parser
From: Patrick McHardy @ 2010-08-06 14:00 UTC (permalink / raw)
To: Simon Horman
Cc: lvs-devel, netdev, netfilter, netfilter-devel, Wensong Zhang,
Julian Anastasov
In-Reply-To: <20100805115239.880722744@vergenet.net>
Am 05.08.2010 13:47, schrieb Simon Horman:
> diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
> index 2fd1ea2..715ce54 100644
> --- a/net/netfilter/nf_conntrack_sip.c
> +++ b/net/netfilter/nf_conntrack_sip.c
> @@ -130,6 +130,44 @@ static int digits_len(const struct nf_conn *ct, const char *dptr,
> return len;
> }
>
> +static int iswordc(const char c)
> +{
> + if (isalnum(c) || c == '!' || c == '"' || c == '%' ||
> + (c >= '(' && c <= '/') || c == ':' || c == '<' || c == '>' ||
> + c == '?' || (c >= '[' && c <= ']') || c == '_' || c == '`' ||
> + c == '{' || c == '}' || c == '~')
> + return 1;
> + return 0;
> +}
> +
> +static int word_len(const char *dptr, const char *limit)
> +{
> + int len = 0;
> + while (dptr < limit && iswordc(*dptr)) {
> + dptr++;
> + len++;
> + }
> + return len;
> +}
Since the Call-ID can't contain whitespace, couldn't we simply
determine the length by looking for the next newline or whitespace
character?
^ permalink raw reply
* Re: [PATCH v2 0/2] netfilter: xtables: xt_condition inclusion and change to u32
From: Luciano Coelho @ 2010-08-06 14:06 UTC (permalink / raw)
To: ext Patrick McHardy
Cc: netfilter-devel@vger.kernel.org, netdev@vger.kernel.org,
jengelh@medozas.de, sameo@linux.intel.com
In-Reply-To: <4C5C1411.7020709@trash.net>
On Fri, 2010-08-06 at 15:54 +0200, ext Patrick McHardy wrote:
> Am 06.08.2010 14:21, schrieb luciano.coelho@nokia.com:
> > From: Luciano Coelho <luciano.coelho@nokia.com>
> >
> > Hello,
> >
> > Here v2 of the xt_condition patch, it contains some changes suggested by Jan.
> >
> > When these patches get accepted, I'll send the implementation of the condition
> > target, as discussed before.
> >
> > In RFC v2 I've made a few changes as discussed in the review:
> >
> > * Removed per-netns module parameters
> > * Use par->net instead of current->nsproxy->net_ns
> > * Fix file-leak in procfs when exiting the netns
> >
> > I didn't get any more comments in RFC v2, so I assume it is okay to send it for
> > inclusion.
>
> The merge window for networking patches is closed, I'm only taking
> bugfixes at this time. Please resend once net-next opens up again.
Ok, I'll resend once the trees are open again.
I'll probably include some more patches on top of this then.
--
Cheers,
Luca.
^ permalink raw reply
* Re: [RFC PATCH] platform: Faciliatate the creation of pseduo-platform busses
From: Greg KH @ 2010-08-06 14:27 UTC (permalink / raw)
To: Grant Likely
Cc: Patrick Pannuto, Patrick Pannuto, linux-kernel@vger.kernel.org,
linux-arm-msm@vger.kernel.org, linux-omap@vger.kernel.org,
damm@opensource.se, lethal@linux-sh.org, rjw@sisk.pl,
dtor@mail.ru, eric.y.miao@gmail.com, netdev@vger.kernel.org,
Kevin Hilman
In-Reply-To: <AANLkTikVzh9=DvvUPbKmDeo6k2-pPVSnjH+GxAPdQFO4@mail.gmail.com>
On Thu, Aug 05, 2010 at 04:59:35PM -0600, Grant Likely wrote:
> (On that point Greg, what is the reason for even having the
> /sys/devices/platform/ parent? Why not just let the platform devices
> sit at the root of the device tree? In the OF case (granted, I'm
> biased) all of the platform_device registrations reflect the actual
> device hierarchy expressed in the device tree data.)
If we sat them at the "root", there would be a bunch of them there. I
don't know, we could drop the parent, I guess whoever created the
platform device oh so long ago, decided that it would look nicer to be
in this type of structure.
> Now, having gone on this whole long tirade, it looks like having
> separate platform bus types may not be the best approach after all.
I totally agree, and thanks for the detailed explaination, it saved me
from having to write up the same thing :)
greg k-h
^ permalink raw reply
* [PATCH v2 resend] netfilter: ipt_REJECT can't send TCP reset
From: Changli Gao @ 2010-08-06 14:30 UTC (permalink / raw)
To: Patrick McHardy
Cc: David S. Miller, Alexey Kuznetsov, Pekka Savola (ipv6),
James Morris, Hideaki YOSHIFUJI, netfilter-devel, netdev,
Changli Gao
ip_route_me_harder can't create the route cache when the outdev is the same
with the indev for the skbs whichout a valid protocol set.
__mkroute_input functions has this check:
1998 if (skb->protocol != htons(ETH_P_IP)) {
1999 /* Not IP (i.e. ARP). Do not create route, if it is
2000 * invalid for proxy arp. DNAT routes are always valid.
2001 *
2002 * Proxy arp feature have been extended to allow, ARP
2003 * replies back to the same interface, to support
2004 * Private VLAN switch technologies. See arp.c.
2005 */
2006 if (out_dev == in_dev &&
2007 IN_DEV_PROXY_ARP_PVLAN(in_dev) == 0) {
2008 err = -EINVAL;
2009 goto cleanup;
2010 }
2011 }
This patch gives the new skb a valid protocol to bypass this check. In order
to make ipt_REJECT work with bridges, you also need to enable ip_forward.
This patch also fixes a regression. When we used skb_copy_expand(), we
didn't have this issue stated above, as the protocol was properly set.
Signed-off-by: Changli Gao <xiaosuo@gmail.com>
---
v2: updated changelog
net/ipv4/netfilter/ipt_REJECT.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c
index b254daf..43eec80 100644
--- a/net/ipv4/netfilter/ipt_REJECT.c
+++ b/net/ipv4/netfilter/ipt_REJECT.c
@@ -112,6 +112,7 @@ static void send_reset(struct sk_buff *oldskb, int hook)
/* ip_route_me_harder expects skb->dst to be set */
skb_dst_set_noref(nskb, skb_dst(oldskb));
+ nskb->protocol = htons(ETH_P_IP);
if (ip_route_me_harder(nskb, addr_type))
goto free_nskb;
^ permalink raw reply related
* Re: [rfc 02/13] [RFC 02/13] netfilter: nf_conntrack_sip: Add callid parser
From: Simon Horman @ 2010-08-06 14:31 UTC (permalink / raw)
To: Patrick McHardy
Cc: lvs-devel, netdev, netfilter, netfilter-devel, Wensong Zhang,
Julian Anastasov
In-Reply-To: <4C5C1591.90108@trash.net>
On Fri, Aug 06, 2010 at 04:00:49PM +0200, Patrick McHardy wrote:
> Am 05.08.2010 13:47, schrieb Simon Horman:
> > diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
> > index 2fd1ea2..715ce54 100644
> > --- a/net/netfilter/nf_conntrack_sip.c
> > +++ b/net/netfilter/nf_conntrack_sip.c
> > @@ -130,6 +130,44 @@ static int digits_len(const struct nf_conn *ct, const char *dptr,
> > return len;
> > }
> >
> > +static int iswordc(const char c)
> > +{
> > + if (isalnum(c) || c == '!' || c == '"' || c == '%' ||
> > + (c >= '(' && c <= '/') || c == ':' || c == '<' || c == '>' ||
> > + c == '?' || (c >= '[' && c <= ']') || c == '_' || c == '`' ||
> > + c == '{' || c == '}' || c == '~')
> > + return 1;
> > + return 0;
> > +}
> > +
> > +static int word_len(const char *dptr, const char *limit)
> > +{
> > + int len = 0;
> > + while (dptr < limit && iswordc(*dptr)) {
> > + dptr++;
> > + len++;
> > + }
> > + return len;
> > +}
>
> Since the Call-ID can't contain whitespace, couldn't we simply
> determine the length by looking for the next newline or whitespace
> character?
Well, there are other characters (e.g. '#') it can't contain - unless I
read the RFC incorrectly. Are you concerned about speed, code complexity,
or something else?
^ permalink raw reply
* [PATCH] net: add Fast Ethernet driver for PXA168.
From: Sachin Sanap @ 2010-08-06 19:39 UTC (permalink / raw)
To: buytenh; +Cc: netdev, akarkare, sarnaik, eric.y.miao, prakity, markb, ssanap
Signed-off-by: Sachin Sanap <ssanap@marvell.com>
---
arch/arm/mach-mmp/aspenite.c | 3 +-
arch/arm/mach-mmp/pxa168.c | 4 +-
drivers/net/Kconfig | 10 +
drivers/net/Makefile | 1 +
drivers/net/pxa168_eth.c | 1592 ++++++++++++++++++++++++++++++++++++++++++
include/linux/pxa168_eth.h | 20 +
6 files changed, 1626 insertions(+), 4 deletions(-)
create mode 100644 drivers/net/pxa168_eth.c
create mode 100644 include/linux/pxa168_eth.h
diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
index 61a6d6a..cec505f 100644
--- a/arch/arm/mach-mmp/aspenite.c
+++ b/arch/arm/mach-mmp/aspenite.c
@@ -200,8 +200,7 @@ static int pxa168_eth_init(void)
}
static struct pxa168_eth_platform_data pxa168_eth_data = {
- .phy_addr = 0, /* phy addr depends on boards */
- .port_number = 0,
+ .phy_addr = 0,
.init = pxa168_eth_init,
};
#endif
diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c
index f7d1158..6fcaf0c 100644
--- a/arch/arm/mach-mmp/pxa168.c
+++ b/arch/arm/mach-mmp/pxa168.c
@@ -97,7 +97,7 @@ static struct clk_lookup pxa168_clkregs[] = {
INIT_CLKREG(&clk_ssp4, "pxa168-ssp.3", NULL),
INIT_CLKREG(&clk_ssp5, "pxa168-ssp.4", NULL),
INIT_CLKREG(&clk_nand, "pxa3xx-nand", NULL),
- INIT_CLKREG(&clk_mfu, "pxa168-mfu", "MFUCLK"),
+ INIT_CLKREG(&clk_mfu, "pxa168-eth", "MFUCLK"),
};
static int __init pxa168_init(void)
@@ -149,4 +149,4 @@ PXA168_DEVICE(ssp2, "pxa168-ssp", 1, SSP2, 0xd401c000, 0x40, 54, 55);
PXA168_DEVICE(ssp3, "pxa168-ssp", 2, SSP3, 0xd401f000, 0x40, 56, 57);
PXA168_DEVICE(ssp4, "pxa168-ssp", 3, SSP4, 0xd4020000, 0x40, 58, 59);
PXA168_DEVICE(ssp5, "pxa168-ssp", 4, SSP5, 0xd4021000, 0x40, 60, 61);
-PXA168_DEVICE(mfu, "pxa168-mfu", -1, MFU, 0xc0800000, 0x0fff);
+PXA168_DEVICE(mfu, "pxa168-eth", -1, MFU, 0xc0800000, 0x0fff);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index ce2fcdd..78cd7e8 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -927,6 +927,16 @@ config SMC91X
The module will be called smc91x. If you want to compile it as a
module, say M here and read <file:Documentation/kbuild/modules.txt>.
+config PXA168_ETH
+ tristate "Marvell pxa168 ethernet support"
+ depends on CPU_PXA168
+ select PHYLIB
+ help
+ This driver supports the pxa168 Ethernet ports.
+
+ To compile this driver as a module, choose M here. The module
+ will be called pxa168_eth.
+
config NET_NETX
tristate "NetX Ethernet support"
select MII
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 0a0512a..a42d437 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -244,6 +244,7 @@ obj-$(CONFIG_MYRI10GE) += myri10ge/
obj-$(CONFIG_SMC91X) += smc91x.o
obj-$(CONFIG_SMC911X) += smc911x.o
obj-$(CONFIG_SMSC911X) += smsc911x.o
+obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
obj-$(CONFIG_DM9000) += dm9000.o
obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o
diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c
new file mode 100644
index 0000000..c00fc3c
--- /dev/null
+++ b/drivers/net/pxa168_eth.c
@@ -0,0 +1,1592 @@
+/*
+ * PXA168 ethernet driver.
+ * Most of the code is derived from mv643xx ethernet driver.
+ *
+ * Copyright (C) 2010 Marvell International Ltd.
+ * Philip Rakity <prakity@marvell.com>
+ * Mark Brown <markb@marvell.com>
+ * Sachin Sanap <ssanap@marvell.com>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/etherdevice.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
+#include <linux/clk.h>
+#include <linux/phy.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <asm/cacheflush.h>
+#include <linux/pxa168_eth.h>
+
+#define DRIVER_NAME "pxa168-eth"
+#define DRIVER_VERSION "0.3"
+
+/*
+ * Registers
+ */
+
+#define PHY_ADDRESS 0x0000
+#define SMI 0x0010
+#define PORT_CONFIG 0x0400
+#define PORT_CONFIG_EXT 0x0408
+#define PORT_COMMAND 0x0410
+#define PORT_STATUS 0x0418
+#define HTPR 0x0428
+#define SDMA_CONFIG 0x0440
+#define SDMA_CMD 0x0448
+#define INT_CAUSE 0x0450
+#define INT_W_CLEAR 0x0454
+#define INT_MASK 0x0458
+#define ETH_F_RX_DESC_0 0x0480
+#define ETH_C_RX_DESC_0 0x04A0
+#define ETH_C_TX_DESC_1 0x04E4
+
+/* smi register */
+#define SMI_BUSY (1 << 28) /* 0 - Write, 1 - Read */
+#define SMI_R_VALID (1 << 27) /* 0 - Write, 1 - Read */
+#define SMI_OP_W (0 << 26) /* Write operation */
+#define SMI_OP_R (1 << 26) /* Read operation */
+
+#define PHY_WAIT_ITERATIONS 500
+
+#define PXA168_ETH_PHY_ADDR_DEFAULT 0
+/* RX & TX descriptor command */
+#define BUF_OWNED_BY_DMA (1 << 31)
+
+/* RX descriptor status */
+#define RX_EN_INT (1 << 23)
+#define RX_FIRST_DESC (1 << 17)
+#define RX_LAST_DESC (1 << 16)
+#define RX_ERROR (1 << 15)
+
+/* TX descriptor command */
+#define TX_EN_INT (1 << 23)
+#define TX_GEN_CRC (1 << 22)
+#define TX_ZERO_PADDING (1 << 18)
+#define TX_FIRST_DESC (1 << 17)
+#define TX_LAST_DESC (1 << 16)
+#define TX_ERROR (1 << 15)
+
+/* SDMA_CMD */
+#define SDMA_CMD_AT (1 << 31)
+#define SDMA_CMD_TXDL (1 << 24)
+#define SDMA_CMD_TXDH (1 << 23)
+#define SDMA_CMD_AR (1 << 15)
+#define SDMA_CMD_ERD (1 << 7)
+
+/* Bit definitions of the Port Config Reg */
+#define PCR_HS (1 << 12)
+#define PCR_EN (1 << 7)
+#define PCR_PM (1 << 0)
+
+/* Bit definitions of the Port Config Extend Reg */
+#define PCXR_2BSM (1 << 28)
+#define PCXR_DSCP_EN (1 << 21)
+#define PCXR_MFL_1518 (0 << 14)
+#define PCXR_MFL_1536 (1 << 14)
+#define PCXR_MFL_2048 (2 << 14)
+#define PCXR_MFL_64K (3 << 14)
+#define PCXR_FLP (1 << 11)
+#define PCXR_PRIO_TX_OFF 3
+#define PCXR_TX_HIGH_PRI (7 << PCXR_PRIO_TX_OFF)
+
+/* Bit definitions of the SDMA Config Reg */
+#define SDCR_BSZ_OFF 12
+#define SDCR_BSZ8 (3 << SDCR_BSZ_OFF)
+#define SDCR_BSZ4 (2 << SDCR_BSZ_OFF)
+#define SDCR_BSZ2 (1 << SDCR_BSZ_OFF)
+#define SDCR_BSZ1 (0 << SDCR_BSZ_OFF)
+#define SDCR_BLMR (1 << 6)
+#define SDCR_BLMT (1 << 7)
+#define SDCR_RIFB (1 << 9)
+#define SDCR_RC_OFF 2
+#define SDCR_RC_MAX_RETRANS (0xf << SDCR_RC_OFF)
+
+/*
+ * Bit definitions of the Interrupt Cause Reg
+ * and Interrupt MASK Reg is the same
+ */
+#define ICR_RXBUF (1 << 0)
+#define ICR_TXBUF_H (1 << 2)
+#define ICR_TXBUF_L (1 << 3)
+#define ICR_TXEND_H (1 << 6)
+#define ICR_TXEND_L (1 << 7)
+#define ICR_RXERR (1 << 8)
+#define ICR_TXERR_H (1 << 10)
+#define ICR_TXERR_L (1 << 11)
+#define ICR_TX_UDR (1 << 13)
+#define ICR_MII_CH (1 << 28)
+
+#define ALL_INTS (ICR_TXBUF_H | ICR_TXBUF_L | ICR_TX_UDR |\
+ ICR_TXERR_H | ICR_TXERR_L |\
+ ICR_TXEND_H | ICR_TXEND_L |\
+ ICR_RXBUF | ICR_RXERR | ICR_MII_CH)
+
+#define ETH_HW_IP_ALIGN 2 /* hw aligns IP header */
+#define ETH_EXTRA_HEADER (6+6+2+4) /* dest+src addr+protocol id+crc */
+#define ETH_DATA_LEN 1500
+#define MAX_PKT_SIZE 1518
+
+#define NUM_RX_DESCS 64
+#define NUM_TX_DESCS 64
+#define MAX_DESCS_PER_HIGH (60)
+#define TX_DESC_COUNT_LOW (10)
+
+#define HASH_ADD 0
+#define HASH_DELETE 1
+#define HASH_ADDR_TABLE_SIZE 0x4000 /* 16K (1/2K address - PCR_HS == 1) */
+#define HOP_NUMBER 12
+
+/* Bit definitions for Port status */
+#define PORT_SPEED_100 (1 << 0)
+#define FULL_DUPLEX (1 << 1)
+#define FLOW_CONTROL_ENABLED (1 << 2)
+#define LINK_UP (1 << 3)
+
+/* Bit definitions for work to be done */
+#define WORK_LINK (1 << 0)
+#define WORK_TX_DONE (1 << 1)
+
+#define TX_DONE_INTERVAL 30
+
+struct rx_desc {
+ u32 cmd_sts; /* Descriptor command status */
+ u16 byte_cnt; /* Descriptor buffer byte count */
+ u16 buf_size; /* Buffer size */
+ u32 buf_ptr; /* Descriptor buffer pointer */
+ u32 next_desc_ptr; /* Next descriptor pointer */
+};
+
+struct tx_desc {
+ u32 cmd_sts; /* Command/status field */
+ u16 reserved;
+ u16 byte_cnt; /* buffer byte count */
+ u32 buf_ptr; /* pointer to buffer for this descriptor */
+ u32 next_desc_ptr; /* Pointer to next descriptor */
+};
+
+struct pxa168_eth_private {
+ int rx_resource_err; /* Rx ring resource error flag */
+
+ /* Next available and first returning Rx resource */
+ int rx_curr_desc_q, rx_used_desc_q;
+
+ /* Next available and first returning Tx resource */
+ int tx_curr_desc_q, tx_used_desc_q;
+
+ struct rx_desc *p_rx_desc_area;
+ dma_addr_t rx_desc_dma;
+ int rx_desc_area_size;
+ struct sk_buff **rx_skb;
+
+ struct tx_desc *p_tx_desc_area;
+ dma_addr_t tx_desc_dma;
+ int tx_desc_area_size;
+ struct sk_buff **tx_skb;
+
+ struct work_struct tx_timeout_task;
+
+ struct net_device *dev;
+ struct napi_struct napi;
+ u8 work_todo;
+
+ struct net_device_stats stats;
+ /* Size of Tx Ring per queue */
+ int tx_ring_size;
+ /* Number of tx descriptors in use */
+ int tx_desc_count;
+ /* Size of Rx Ring per queue */
+ int rx_ring_size;
+ /* Number of rx descriptors in use */
+ int rx_desc_count;
+
+ /*
+ * Used in case RX Ring is empty, which can occur when
+ * system does not have resources (skb's)
+ */
+ struct timer_list timeout;
+ struct mii_bus *smi_bus;
+ struct phy_device *phy;
+
+ /* clock */
+ struct clk *clk;
+ struct pxa168_eth_platform_data *pd;
+ /*
+ * Ethernet controller base address.
+ */
+ void __iomem *base;
+
+ /* Pointer to the hardware address filter table */
+ void *htpr;
+ dma_addr_t htpr_dma;
+};
+
+struct addr_table_entry {
+ u32 lo;
+ u32 hi;
+};
+
+/* Bit fields of a Hash Table Entry */
+enum hash_table_entry {
+ HASH_ENTRY_VALID = 1,
+ SKIP = 2,
+ HASH_ENTRY_RECEIVE_DISCARD = 4,
+ HASH_ENTRY_RECEIVE_DISCARD_BIT = 2
+};
+
+static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd *cmd);
+static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd *cmd);
+static int pxa168_init_hw(struct pxa168_eth_private *pep);
+static void eth_port_reset(struct net_device *dev);
+static void eth_port_start(struct net_device *dev);
+static int pxa168_eth_open(struct net_device *dev);
+static int pxa168_eth_stop(struct net_device *dev);
+static int ethernet_phy_setup(struct net_device *dev);
+
+static inline u32 rdl(struct pxa168_eth_private *pep, int offset)
+{
+ return readl(pep->base + offset);
+}
+
+static inline void wrl(struct pxa168_eth_private *pep, int offset, u32 data)
+{
+ writel(data, pep->base + offset);
+}
+
+static void abort_dma(struct pxa168_eth_private *pep)
+{
+ int delay;
+ int max_retries = 40;
+
+ do {
+ wrl(pep, SDMA_CMD, SDMA_CMD_AR | SDMA_CMD_AT);
+ udelay(100);
+
+ delay = 10;
+ while ((rdl(pep, SDMA_CMD) & (SDMA_CMD_AR | SDMA_CMD_AT))
+ && delay-- > 0) {
+ udelay(10);
+ }
+ } while (max_retries-- > 0 && delay <= 0);
+
+ if (max_retries <= 0)
+ printk(KERN_ERR "%s : DMA Stuck\n", __func__);
+}
+
+static int ethernet_phy_get(struct pxa168_eth_private *pep)
+{
+ unsigned int reg_data;
+
+ reg_data = rdl(pep, PHY_ADDRESS);
+
+ return reg_data & 0x1f;
+}
+
+static void ethernet_phy_set_addr(struct pxa168_eth_private *pep, int phy_addr)
+{
+ u32 reg_data;
+
+ reg_data = rdl(pep, PHY_ADDRESS);
+ reg_data &= ~(0x1f);
+ reg_data |= phy_addr & 0x1f;
+ wrl(pep, PHY_ADDRESS, reg_data);
+}
+
+static void ethernet_phy_reset(struct pxa168_eth_private *pep)
+{
+ int data;
+
+ data = phy_read(pep->phy, MII_BMCR);
+ if (data < 0)
+ return;
+
+ data |= BMCR_RESET;
+ if (phy_write(pep->phy, MII_BMCR, data) < 0)
+ return;
+
+ do {
+ data = phy_read(pep->phy, MII_BMCR);
+ } while (data >= 0 && data & BMCR_RESET);
+}
+
+static void rxq_refill(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct sk_buff *skb;
+ struct rx_desc *p_used_rx_desc;
+ int used_rx_desc;
+
+ while (pep->rx_desc_count < pep->rx_ring_size) {
+ skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN);
+ if (!skb)
+ break;
+ pep->rx_desc_count++;
+ /* Get 'used' Rx descriptor */
+ used_rx_desc = pep->rx_used_desc_q;
+ p_used_rx_desc = &pep->p_rx_desc_area[used_rx_desc];
+ p_used_rx_desc->buf_ptr = dma_map_single(NULL,
+ skb->data,
+ MAX_PKT_SIZE +
+ ETH_HW_IP_ALIGN,
+ DMA_FROM_DEVICE);
+ p_used_rx_desc->buf_size = MAX_PKT_SIZE + ETH_HW_IP_ALIGN;
+ pep->rx_skb[used_rx_desc] = skb;
+
+ /* Return the descriptor to DMA ownership */
+ wmb();
+ p_used_rx_desc->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT;
+ wmb();
+
+ /* Move the used descriptor pointer to the next descriptor */
+ pep->rx_used_desc_q = (used_rx_desc + 1) % pep->rx_ring_size;
+
+ /* Any Rx return cancels the Rx resource error status */
+ pep->rx_resource_err = 0;
+
+ skb_reserve(skb, ETH_HW_IP_ALIGN);
+ }
+
+ /*
+ * If RX ring is empty of SKB, set a timer to try allocating
+ * again at a later time.
+ */
+ if (pep->rx_desc_count == 0) {
+ pep->timeout.expires = jiffies + (HZ / 10); /* 100 mSec */
+ add_timer(&pep->timeout);
+ }
+}
+
+static inline void rxq_refill_timer_wrapper(unsigned long data)
+{
+ struct pxa168_eth_private *pep = (void *)data;
+ napi_schedule(&pep->napi);
+}
+
+static inline u32 nibble_swapping_32_bit(u32 x)
+{
+ return (((x) & 0xf0f0f0f0) >> 4) | (((x) & 0x0f0f0f0f) << 4);
+}
+
+static inline u32 nibble_swapping_16_bit(u32 x)
+{
+ return (((x) & 0x0000f0f0) >> 4) | (((x) & 0x00000f0f) << 4);
+}
+
+static inline u32 flip_4_bits(u32 x)
+{
+ return (((x) & 0x01) << 3) | (((x) & 0x002) << 1)
+ | (((x) & 0x04) >> 1) | (((x) & 0x008) >> 3);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * This function will calculate the hash function of the address.
+ * depends on the hash mode and hash size.
+ * Inputs
+ * mac_high - the 2 most significant bytes of the MAC address.
+ * mac_low - the 4 least significant bytes of the MAC address.
+ * Outputs
+ * return the calculated entry.
+ */
+static u32 hash_function(u32 mac_high, u32 mac_low)
+{
+ u32 hash_result;
+ u32 addr_high;
+ u32 addr_low;
+ u32 addr0;
+ u32 addr1;
+ u32 addr2;
+ u32 addr3;
+ u32 addr_high_swapped;
+ u32 addr_low_swapped;
+
+ addr_high = nibble_swapping_16_bit(mac_high);
+ addr_low = nibble_swapping_32_bit(mac_low);
+
+ addr_high_swapped = flip_4_bits(addr_high & 0xf)
+ + ((flip_4_bits((addr_high >> 4) & 0xf)) << 4)
+ + ((flip_4_bits((addr_high >> 8) & 0xf)) << 8)
+ + ((flip_4_bits((addr_high >> 12) & 0xf)) << 12);
+
+ addr_low_swapped = flip_4_bits(addr_low & 0xf)
+ + ((flip_4_bits((addr_low >> 4) & 0xf)) << 4)
+ + ((flip_4_bits((addr_low >> 8) & 0xf)) << 8)
+ + ((flip_4_bits((addr_low >> 12) & 0xf)) << 12)
+ + ((flip_4_bits((addr_low >> 16) & 0xf)) << 16)
+ + ((flip_4_bits((addr_low >> 20) & 0xf)) << 20)
+ + ((flip_4_bits((addr_low >> 24) & 0xf)) << 24)
+ + ((flip_4_bits((addr_low >> 28) & 0xf)) << 28);
+
+ addr_high = addr_high_swapped;
+ addr_low = addr_low_swapped;
+
+ addr0 = (addr_low >> 2) & 0x03f;
+ addr1 = (addr_low & 0x003) | ((addr_low >> 8) & 0x7f) << 2;
+ addr2 = (addr_low >> 15) & 0x1ff;
+ addr3 = ((addr_low >> 24) & 0x0ff) | ((addr_high & 1) << 8);
+
+ hash_result = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
+ hash_result = hash_result & 0x07ff;
+ return hash_result;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * This function will add/del an entry to the address table.
+ * Inputs
+ * pep - ETHERNET .
+ * mac_high - the 2 most significant bytes of the MAC address.
+ * mac_low - the 4 least significant bytes of the MAC address.
+ * skip - if 1, skip this address.Used in case of deleting an entry which is a
+ * part of chain in the hash table.We cant just delete the entry since
+ * that will break the chain.We need to defragment the tables time to
+ * time.
+ * rd - 0 Discard packet upon match.
+ * - 1 Receive packet upon match.
+ * Outputs
+ * address table entry is added/deleted.
+ * 0 if success.
+ * -ENOSPC if table full
+ */
+static int add_del_hash_entry(struct pxa168_eth_private *pep, u32 mac_high,
+ u32 mac_low, u32 rd, u32 skip, int del)
+{
+ struct addr_table_entry *entry, *start;
+ u32 new_high;
+ u32 new_low;
+ u32 i;
+
+ new_low = (((mac_high >> 4) & 0xf) << 15)
+ | (((mac_high >> 0) & 0xf) << 11)
+ | (((mac_high >> 12) & 0xf) << 7)
+ | (((mac_high >> 8) & 0xf) << 3)
+ | (((mac_low >> 20) & 0x1) << 31)
+ | (((mac_low >> 16) & 0xf) << 27)
+ | (((mac_low >> 28) & 0xf) << 23)
+ | (((mac_low >> 24) & 0xf) << 19)
+ | (skip << SKIP) | (rd << HASH_ENTRY_RECEIVE_DISCARD_BIT)
+ | HASH_ENTRY_VALID;
+
+ new_high = (((mac_low >> 4) & 0xf) << 15)
+ | (((mac_low >> 0) & 0xf) << 11)
+ | (((mac_low >> 12) & 0xf) << 7)
+ | (((mac_low >> 8) & 0xf) << 3)
+ | (((mac_low >> 21) & 0x7) << 0);
+
+ /*
+ * Pick the appropriate table, start scanning for free/reusable
+ * entries at the index obtained by hashing the specified MAC address
+ */
+ start = (struct addr_table_entry *)(pep->htpr);
+ entry = start + hash_function(mac_high, mac_low);
+ for (i = 0; i < HOP_NUMBER; i++) {
+ if (!(entry->lo & HASH_ENTRY_VALID)) {
+ break;
+ } else {
+ /* if same address put in same position */
+ if (((entry->lo & 0xfffffff8) == (new_low & 0xfffffff8))
+ && (entry->hi == new_high)) {
+ break;
+ }
+ }
+ if (entry == start + 0x7ff)
+ entry = start;
+ else
+ entry++;
+ }
+
+ if (((entry->lo & 0xfffffff8) != (new_low & 0xfffffff8)) &&
+ (entry->hi != new_high) && del)
+ return 0;
+
+ if (i == HOP_NUMBER) {
+ if (!del) {
+ printk(KERN_INFO "%s: table section is full\n",
+ __FILE__);
+ return -ENOSPC;
+ } else
+ return 0;
+ }
+
+ /*
+ * Update the selected entry
+ */
+ if (del) {
+ entry->hi = 0;
+ entry->lo = 0;
+ } else {
+ entry->hi = cpu_to_le32(new_high);
+ entry->lo = cpu_to_le32(new_low);
+ }
+
+ return 0;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * Create an addressTable entry from MAC address info
+ * found in the specifed net_device struct
+ *
+ * Input : pointer to ethernet interface network device structure
+ * Output : N/A
+ */
+static void update_hash_table_mac_address(struct pxa168_eth_private *pep,
+ u8 *oaddr, u8 *addr)
+{
+ u32 mac_high;
+ u32 mac_low;
+
+ /* Delete old entry */
+ if (oaddr) {
+ mac_high = cpu_to_le32((oaddr[0] << 8) | oaddr[1]);
+ mac_low = cpu_to_le32((oaddr[2] << 24) | (oaddr[3] << 16) |
+ (oaddr[4] << 8) | oaddr[5]);
+ add_del_hash_entry(pep, mac_high, mac_low, 1, 0, HASH_DELETE);
+ }
+ /* Add new entry */
+ mac_high = cpu_to_le32((addr[0] << 8) | addr[1]);
+ mac_low = cpu_to_le32((addr[2] << 24) | (addr[3] << 16) |
+ (addr[4] << 8) | addr[5]);
+ add_del_hash_entry(pep, mac_high, mac_low, 1, 0, HASH_ADD);
+}
+
+static int init_hash_table(struct pxa168_eth_private *pep)
+{
+ /*
+ * Hardware expects CPU to build a hash table based on a predefined
+ * hash function and populate it based on hardware address. The
+ * location of the hash table is identified by 32-bit pointer stored
+ * in HTPR internal register. Two possible sizes exists for the hash
+ * table 256kB and 16kB.We currently only support 16kB.
+ */
+ /* TODO: Add support for 256kB hash table */
+ if (pep->htpr == NULL) {
+ pep->htpr = dma_alloc_coherent(NULL,
+ HASH_ADDR_TABLE_SIZE,
+ &pep->htpr_dma, GFP_KERNEL);
+ if (pep->htpr == NULL)
+ return -ENOMEM;
+ }
+ memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
+ wrl(pep, HTPR, pep->htpr_dma);
+ return 0;
+}
+
+static void pxa168_eth_set_rx_mode(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct netdev_hw_addr *ha;
+ u32 val;
+
+ val = rdl(pep, PORT_CONFIG);
+ if (dev->flags & IFF_PROMISC)
+ val |= PCR_PM;
+ else
+ val &= ~PCR_PM;
+ wrl(pep, PORT_CONFIG, val);
+ netdev_for_each_mc_addr(ha, dev)
+ update_hash_table_mac_address(pep, NULL, ha->addr);
+}
+
+static int pxa168_eth_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = addr;
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ unsigned char oldMac[ETH_ALEN];
+
+ if (!is_valid_ether_addr(sa->sa_data))
+ return -EINVAL;
+ memcpy(oldMac, dev->dev_addr, ETH_ALEN);
+ memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
+ netif_addr_lock_bh(dev);
+ update_hash_table_mac_address(pep, oldMac, dev->dev_addr);
+ netif_addr_unlock_bh(dev);
+ return 0;
+}
+
+static void eth_port_start(struct net_device *dev)
+{
+ unsigned int val = 0;
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ int tx_curr_desc, rx_curr_desc;
+
+ /* Perform PHY reset, if there is a PHY. */
+ if (pep->phy != NULL) {
+ struct ethtool_cmd cmd;
+
+ pxa168_get_settings(pep->dev, &cmd);
+ ethernet_phy_reset(pep);
+ pxa168_set_settings(pep->dev, &cmd);
+ }
+
+ /* Assignment of Tx CTRP of given queue */
+ tx_curr_desc = pep->tx_curr_desc_q;
+ wrl(pep, ETH_C_TX_DESC_1,
+ (u32) ((struct tx_desc *)pep->tx_desc_dma + tx_curr_desc));
+
+ /* Assignment of Rx CRDP of given queue */
+ rx_curr_desc = pep->rx_curr_desc_q;
+ wrl(pep, ETH_C_RX_DESC_0,
+ (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
+
+ wrl(pep, ETH_F_RX_DESC_0,
+ (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
+
+ /* Clear all interrupts */
+ wrl(pep, INT_CAUSE, 0);
+
+ /* Enable all interrupts for receive, transmit and error. */
+ wrl(pep, INT_MASK, ALL_INTS);
+
+ val = rdl(pep, PORT_CONFIG);
+ val |= PCR_EN;
+ wrl(pep, PORT_CONFIG, val);
+
+ /* Start RX DMA engine */
+ val = rdl(pep, SDMA_CMD);
+ val |= SDMA_CMD_ERD;
+ wrl(pep, SDMA_CMD, val);
+}
+
+static void eth_port_reset(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ unsigned int val = 0;
+
+ /* Stop all interrupts for receive, transmit and error. */
+ wrl(pep, INT_MASK, 0);
+
+ /* Clear all interrupts */
+ wrl(pep, INT_CAUSE, 0);
+
+ /* Stop RX DMA */
+ val = rdl(pep, SDMA_CMD);
+ val &= ~SDMA_CMD_ERD; /* abort dma command */
+
+ /* Abort any transmit and receive operations and put DMA
+ * in idle state.
+ */
+ abort_dma(pep);
+
+ /* Disable port */
+ val = rdl(pep, PORT_CONFIG);
+ val &= ~PCR_EN;
+ wrl(pep, PORT_CONFIG, val);
+}
+
+/*
+ * txq_reclaim - Free the tx desc data for completed descriptors
+ * If force is non-zero, frees uncompleted descriptors as well
+ */
+static int txq_reclaim(struct net_device *dev, int force)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct tx_desc *desc;
+ u32 cmd_sts;
+ struct sk_buff *skb;
+ int tx_index;
+ dma_addr_t addr;
+ int count;
+ int released = 0;
+
+ netif_tx_lock(dev);
+
+ pep->work_todo &= ~(WORK_TX_DONE);
+ while (pep->tx_desc_count > 0) {
+ tx_index = pep->tx_used_desc_q;
+ desc = &pep->p_tx_desc_area[tx_index];
+ cmd_sts = desc->cmd_sts;
+ if (!force && (cmd_sts & BUF_OWNED_BY_DMA)) {
+ if (released > 0) {
+ goto txq_reclaim_end;
+ } else {
+ released = -1;
+ goto txq_reclaim_end;
+ }
+ }
+ pep->tx_used_desc_q = (tx_index + 1) % pep->tx_ring_size;
+ pep->tx_desc_count--;
+ addr = desc->buf_ptr;
+ count = desc->byte_cnt;
+ skb = pep->tx_skb[tx_index];
+ if (skb)
+ pep->tx_skb[tx_index] = NULL;
+
+ if (cmd_sts & TX_ERROR) {
+ if (net_ratelimit())
+ printk(KERN_ERR "%s: Error in TX\n", dev->name);
+ dev->stats.tx_errors++;
+ }
+ dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE);
+ if (skb)
+ dev_kfree_skb_irq(skb);
+ released++;
+ }
+txq_reclaim_end:
+ netif_tx_unlock(dev);
+ return released;
+}
+
+static void pxa168_eth_tx_timeout(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+
+ printk(KERN_INFO "%s: TX timeout desc_count %d\n",
+ dev->name, pep->tx_desc_count);
+
+ schedule_work(&pep->tx_timeout_task);
+}
+
+static void pxa168_eth_tx_timeout_task(struct work_struct *work)
+{
+ struct pxa168_eth_private *pep = container_of(work,
+ struct pxa168_eth_private,
+ tx_timeout_task);
+ struct net_device *dev = pep->dev;
+ pxa168_eth_stop(dev);
+ pxa168_eth_open(dev);
+}
+
+static int rxq_process(struct net_device *dev, int budget)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ unsigned int received_packets = 0;
+ struct sk_buff *skb;
+
+ while (budget-- > 0) {
+
+ int rx_next_curr_desc, rx_curr_desc, rx_used_desc;
+ struct rx_desc *rx_desc;
+ unsigned int cmd_sts;
+
+ /* Do not process Rx ring in case of Rx ring resource error */
+ if (pep->rx_resource_err)
+ break;
+ rx_curr_desc = pep->rx_curr_desc_q;
+ rx_used_desc = pep->rx_used_desc_q;
+ rx_desc = &pep->p_rx_desc_area[rx_curr_desc];
+ cmd_sts = rx_desc->cmd_sts;
+ rmb();
+ if (cmd_sts & (BUF_OWNED_BY_DMA))
+ break;
+ skb = pep->rx_skb[rx_curr_desc];
+ pep->rx_skb[rx_curr_desc] = NULL;
+
+ rx_next_curr_desc = (rx_curr_desc + 1) % pep->rx_ring_size;
+ pep->rx_curr_desc_q = rx_next_curr_desc;
+
+ /* Rx descriptors exhausted. */
+ /* Set the Rx ring resource error flag */
+ if (rx_next_curr_desc == rx_used_desc)
+ pep->rx_resource_err = 1;
+ pep->rx_desc_count--;
+ dma_unmap_single(NULL, rx_desc->buf_ptr,
+ MAX_PKT_SIZE + ETH_HW_IP_ALIGN,
+ DMA_FROM_DEVICE);
+ received_packets++;
+ /*
+ * Update statistics.
+ * Note byte count includes 4 byte CRC count
+ */
+ stats->rx_packets++;
+ stats->rx_bytes += rx_desc->byte_cnt;
+ /*
+ * In case received a packet without first / last bits on OR
+ * the error summary bit is on, the packets needs to be droped.
+ */
+ if (((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
+ (RX_FIRST_DESC | RX_LAST_DESC))
+ || (cmd_sts & RX_ERROR)) {
+
+ stats->rx_dropped++;
+ if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
+ (RX_FIRST_DESC | RX_LAST_DESC)) {
+ if (net_ratelimit())
+ printk(KERN_ERR
+ "%s: Rx pkt on multiple desc\n",
+ dev->name);
+ }
+ if (cmd_sts & RX_ERROR)
+ stats->rx_errors++;
+ dev_kfree_skb_irq(skb);
+ } else {
+ /*
+ * The -4 is for the CRC in the trailer of the
+ * received packet
+ */
+ skb_put(skb, rx_desc->byte_cnt - 4);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_receive_skb(skb);
+ }
+ dev->last_rx = jiffies;
+ }
+ /* Fill RX ring with skb's */
+ rxq_refill(dev);
+ return received_packets;
+}
+
+static int pxa168_eth_collect_events(struct pxa168_eth_private *pep,
+ struct net_device *dev)
+{
+ u32 icr;
+ int ret = 0;
+
+ icr = rdl(pep, INT_CAUSE);
+ if (0x00 == icr)
+ return IRQ_NONE;
+
+ wrl(pep, INT_CAUSE, icr ^ 0xffffffff);
+ if (icr & (ICR_TXBUF_H | ICR_TXBUF_L)) {
+ pep->work_todo |= WORK_TX_DONE;
+ ret = 1;
+ }
+ if (icr & ICR_RXBUF)
+ ret = 1;
+ if (icr & ICR_MII_CH) {
+ pep->work_todo |= WORK_LINK;
+ ret = 1;
+ }
+ return ret;
+}
+
+static void handle_link_event(struct pxa168_eth_private *pep)
+{
+ struct net_device *dev = pep->dev;
+ u32 port_status;
+ int speed;
+ int duplex;
+ int fc;
+
+ port_status = rdl(pep, PORT_STATUS);
+ if (!(port_status & LINK_UP)) {
+ if (netif_carrier_ok(dev)) {
+ printk(KERN_INFO "%s: link down\n", dev->name);
+ netif_carrier_off(dev);
+ txq_reclaim(dev, 1);
+ }
+ return;
+ }
+ if (port_status & PORT_SPEED_100)
+ speed = 100;
+ else
+ speed = 10;
+
+ duplex = (port_status & FULL_DUPLEX) ? 1 : 0;
+ fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0;
+ printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, "
+ "flow control %sabled\n", dev->name,
+ speed, duplex ? "full" : "half", fc ? "en" : "dis");
+ if (!netif_carrier_ok(dev))
+ netif_carrier_on(dev);
+}
+
+static irqreturn_t pxa168_eth_int_handler(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+
+ if (unlikely(!pxa168_eth_collect_events(pep, dev)))
+ return IRQ_NONE;
+ /* Disable interrupts */
+ wrl(pep, INT_MASK, 0);
+ napi_schedule(&pep->napi);
+ return IRQ_HANDLED;
+}
+
+static int set_port_config_ext(struct pxa168_eth_private *pep, int mtu)
+{
+ int mtu_size;
+
+ if ((mtu > ETH_DATA_LEN) || (mtu < 64))
+ return -EINVAL;
+
+ mtu_size = PCXR_MFL_1518;
+ /* Extended Port Configuration */
+ wrl(pep,
+ PORT_CONFIG_EXT, PCXR_2BSM | /* Two byte suffix aligns IP hdr */
+ PCXR_DSCP_EN | /* Enable DSCP in IP */
+ mtu_size | PCXR_FLP | /* do not force link pass */
+ PCXR_TX_HIGH_PRI); /* Transmit - high priority queue */
+
+ (pep->dev)->mtu = mtu;
+ return 0;
+}
+
+static int pxa168_init_hw(struct pxa168_eth_private *pep)
+{
+ int err = 0;
+
+ /* Disable interrupts */
+ wrl(pep, INT_MASK, 0);
+ wrl(pep, INT_CAUSE, 0);
+ /* Write to ICR to clear interrupts. */
+ wrl(pep, INT_W_CLEAR, 0);
+ /* Abort any transmit and receive operations and put DMA
+ * in idle state.
+ */
+ abort_dma(pep);
+ /* Initialize address hash table */
+ err = init_hash_table(pep);
+ if (err)
+ return err;
+ /* SDMA configuration */
+ wrl(pep, SDMA_CONFIG, SDCR_BSZ8 | /* Burst size = 32 bytes */
+ SDCR_RIFB | /* Rx interrupt on frame */
+ SDCR_BLMT | /* Little endian transmit */
+ SDCR_BLMR | /* Little endian receive */
+ SDCR_RC_MAX_RETRANS); /* Max retransmit count */
+ /* Port Configuration */
+ wrl(pep, PORT_CONFIG, PCR_HS); /* Hash size is 1/2kb */
+ set_port_config_ext(pep, (pep->dev)->mtu);
+ return err;
+}
+
+static int rxq_init(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct rx_desc *p_rx_desc;
+ int size = 0, i = 0;
+ int rx_desc_num = pep->rx_ring_size;
+
+ /* Allocate RX skb rings */
+ pep->rx_skb = kmalloc(sizeof(*pep->rx_skb) * pep->rx_ring_size,
+ GFP_KERNEL);
+ if (!pep->rx_skb) {
+ printk(KERN_ERR "%s: Cannot alloc RX skb ring\n", dev->name);
+ return -ENOMEM;
+ }
+ /* Allocate RX ring */
+ pep->rx_desc_count = 0;
+ size = pep->rx_ring_size * sizeof(struct rx_desc);
+ pep->rx_desc_area_size = size;
+ pep->p_rx_desc_area = dma_alloc_coherent(NULL, size,
+ &pep->rx_desc_dma, GFP_KERNEL);
+ if (!pep->p_rx_desc_area) {
+ printk(KERN_ERR "%s: Cannot alloc RX ring (size %d bytes)\n",
+ dev->name, size);
+ goto out;
+ }
+ memset((void *)pep->p_rx_desc_area, 0, size);
+ /* initialize the next_desc_ptr links in the Rx descriptors ring */
+ p_rx_desc = (struct rx_desc *)pep->p_rx_desc_area;
+ for (i = 0; i < rx_desc_num; i++) {
+ p_rx_desc[i].next_desc_ptr = pep->rx_desc_dma +
+ ((i + 1) % rx_desc_num) * sizeof(struct rx_desc);
+ }
+ /* Save Rx desc pointer to driver struct. */
+ pep->rx_curr_desc_q = 0;
+ pep->rx_used_desc_q = 0;
+ pep->rx_desc_area_size = rx_desc_num * sizeof(struct rx_desc);
+ return 0;
+out:
+ kfree(pep->rx_skb);
+ return -ENOMEM;
+}
+
+static void rxq_deinit(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ int curr;
+
+ /* Free preallocated skb's on RX rings */
+ for (curr = 0; pep->rx_desc_count && curr < pep->rx_ring_size; curr++) {
+ if (pep->rx_skb[curr]) {
+ dev_kfree_skb(pep->rx_skb[curr]);
+ pep->rx_desc_count--;
+ }
+ }
+ if (pep->rx_desc_count)
+ printk(KERN_ERR
+ "Error in freeing Rx Ring. %d skb's still\n",
+ pep->rx_desc_count);
+ /* Free RX ring */
+ if (pep->p_rx_desc_area)
+ dma_free_coherent(NULL, pep->rx_desc_area_size,
+ pep->p_rx_desc_area, pep->rx_desc_dma);
+ kfree(pep->rx_skb);
+}
+
+static int txq_init(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct tx_desc *p_tx_desc;
+ int size = 0, i = 0;
+ int tx_desc_num = pep->tx_ring_size;
+
+ pep->tx_skb = kmalloc(sizeof(*pep->tx_skb) * pep->tx_ring_size,
+ GFP_KERNEL);
+ if (!pep->tx_skb) {
+ printk(KERN_ERR "%s: Cannot alloc TX skb ring\n", dev->name);
+ return -ENOMEM;
+ }
+ /* Allocate TX ring */
+ pep->tx_desc_count = 0;
+ size = pep->tx_ring_size * sizeof(struct tx_desc);
+ pep->tx_desc_area_size = size;
+ pep->p_tx_desc_area = dma_alloc_coherent(NULL, size,
+ &pep->tx_desc_dma, GFP_KERNEL);
+ if (!pep->p_tx_desc_area) {
+ printk(KERN_ERR "%s: Cannot allocate Tx Ring (size %d bytes)\n",
+ dev->name, size);
+ goto out;
+ }
+ memset((void *)pep->p_tx_desc_area, 0, pep->tx_desc_area_size);
+ /* Initialize the next_desc_ptr links in the Tx descriptors ring */
+ p_tx_desc = (struct tx_desc *)pep->p_tx_desc_area;
+ for (i = 0; i < tx_desc_num; i++) {
+ p_tx_desc[i].next_desc_ptr = pep->tx_desc_dma +
+ ((i + 1) % tx_desc_num) * sizeof(struct tx_desc);
+ }
+ pep->tx_curr_desc_q = 0;
+ pep->tx_used_desc_q = 0;
+ pep->tx_desc_area_size = tx_desc_num * sizeof(struct tx_desc);
+ return 0;
+out:
+ kfree(pep->tx_skb);
+ return -ENOMEM;
+}
+
+static void txq_deinit(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+
+ /* Free outstanding skb's on TX ring */
+ txq_reclaim(dev, 1);
+ BUG_ON(pep->tx_used_desc_q != pep->tx_curr_desc_q);
+ /* Free TX ring */
+ if (pep->p_tx_desc_area)
+ dma_free_coherent(NULL, pep->tx_desc_area_size,
+ pep->p_tx_desc_area, pep->tx_desc_dma);
+ kfree(pep->tx_skb);
+}
+
+static int pxa168_eth_open(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ int err;
+
+ err = request_irq(dev->irq, pxa168_eth_int_handler,
+ IRQF_DISABLED , dev->name, dev);
+ if (err) {
+ dev_printk(KERN_ERR, &dev->dev, "can't assign irq\n");
+ return -EAGAIN;
+ }
+ pep->rx_resource_err = 0;
+ err = rxq_init(dev);
+ if (err != 0)
+ goto out_free_irq;
+ err = txq_init(dev);
+ if (err != 0)
+ goto out_free_rx_skb;
+ pep->rx_used_desc_q = 0;
+ pep->rx_curr_desc_q = 0;
+
+ /* Fill RX ring with skb's */
+ rxq_refill(dev);
+ pep->rx_used_desc_q = 0;
+ pep->rx_curr_desc_q = 0;
+ netif_carrier_off(dev);
+ eth_port_start(dev);
+ napi_enable(&pep->napi);
+ return 0;
+out_free_rx_skb:
+ rxq_deinit(dev);
+out_free_irq:
+ free_irq(dev->irq, dev);
+ return err;
+}
+
+static int pxa168_eth_stop(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ eth_port_reset(dev);
+
+ /* Disable interrupts */
+ wrl(pep, INT_MASK, 0);
+ wrl(pep, INT_CAUSE, 0);
+ /* Write to ICR to clear interrupts. */
+ wrl(pep, INT_W_CLEAR, 0);
+ napi_disable(&pep->napi);
+ del_timer_sync(&pep->timeout);
+ netif_carrier_off(dev);
+ free_irq(dev->irq, dev);
+ rxq_deinit(dev);
+ txq_deinit(dev);
+ return 0;
+}
+
+static int pxa168_eth_change_mtu(struct net_device *dev, int mtu)
+{
+ if ((mtu > ETH_DATA_LEN) || (mtu < 64))
+ return -EINVAL;
+ dev->mtu = mtu;
+ return 0;
+}
+
+static int eth_alloc_tx_desc_index(struct pxa168_eth_private *pep)
+{
+ int tx_desc_curr;
+
+ tx_desc_curr = pep->tx_curr_desc_q;
+ pep->tx_curr_desc_q = (tx_desc_curr + 1) % pep->tx_ring_size;
+ BUG_ON(pep->tx_curr_desc_q == pep->tx_used_desc_q);
+ pep->tx_desc_count++;
+ return tx_desc_curr;
+}
+
+static void eth_tx_submit_descs_for_skb(struct pxa168_eth_private *pep,
+ struct sk_buff *skb)
+{
+ int tx_index;
+ struct tx_desc *desc;
+ int length;
+
+ tx_index = eth_alloc_tx_desc_index(pep);
+ desc = &pep->p_tx_desc_area[tx_index];
+ length = skb->len;
+ pep->tx_skb[tx_index] = skb;
+ desc->byte_cnt = length;
+ desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);
+ wmb();
+ desc->cmd_sts = BUF_OWNED_BY_DMA | TX_GEN_CRC | TX_FIRST_DESC |
+ TX_ZERO_PADDING | TX_LAST_DESC;
+ if (unlikely(!(pep->tx_desc_count % TX_DONE_INTERVAL)))
+ desc->cmd_sts |= TX_EN_INT;
+ wmb();
+ wrl(pep, SDMA_CMD, SDMA_CMD_TXDH | SDMA_CMD_ERD);
+}
+
+static int pxa168_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct pxa168_eth_private *pep =
+ container_of(napi, struct pxa168_eth_private, napi);
+ struct net_device *dev = pep->dev;
+ int work_done = 0;
+
+ if (unlikely(pep->work_todo & WORK_LINK)) {
+ pep->work_todo &= ~(WORK_LINK);
+ handle_link_event(pep);
+ }
+ /*
+ * We call txq_reclaim every time since in NAPI interupts are disabled
+ * and due to this we miss the TX_DONE interrupt,which is not updated in
+ * interrupt status register.
+ */
+ txq_reclaim(dev, 0);
+ if (netif_queue_stopped(dev)
+ && pep->tx_ring_size - pep->tx_desc_count > 1) {
+ netif_wake_queue(dev);
+ }
+ work_done = rxq_process(dev, budget);
+ if (work_done < budget) {
+ napi_complete(napi);
+ wrl(pep, INT_MASK, ALL_INTS);
+ }
+ return work_done;
+}
+
+static int pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+
+ eth_tx_submit_descs_for_skb(pep, skb);
+ stats->tx_bytes += skb->len;
+ stats->tx_packets++;
+ dev->trans_start = jiffies;
+ if (pep->tx_ring_size - pep->tx_desc_count <= 1) {
+ /* We handled the current skb, but now we are out of space.*/
+ netif_stop_queue(dev);
+ }
+ return NETDEV_TX_OK;
+}
+
+static int pxa168_smi_read(struct mii_bus *bus, int phy_addr, int regnum)
+{
+ int val;
+ struct pxa168_eth_private *pep = bus->priv;
+ int i = 0;
+
+ /* wait for the SMI register to become available */
+ for (i = 0; (val = rdl(pep, SMI)) & SMI_BUSY; i++) {
+ if (i == PHY_WAIT_ITERATIONS) {
+ printk(KERN_ERR
+ "pxa168 PHY timeout, val=0x%x\n", val);
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+ wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) | SMI_OP_R);
+ /* now wait for the data to be valid */
+ for (i = 0; !((val = rdl(pep, SMI)) & SMI_R_VALID); i++) {
+ if (i == PHY_WAIT_ITERATIONS) {
+ printk(KERN_ERR
+ "pxa168 PHY RD timeout, val=0x%x\n", val);
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+ return val & 0xffff;
+}
+
+static int pxa168_smi_write(struct mii_bus *bus, int phy_addr, int regnum,
+ u16 value)
+{
+ struct pxa168_eth_private *pep = bus->priv;
+ int i;
+
+ /* wait for the SMI register to become available */
+ for (i = 0; rdl(pep, SMI) & SMI_BUSY; i++) {
+ if (i == PHY_WAIT_ITERATIONS) {
+ printk(KERN_ERR "pxa168 PHY busy timeout.\n");
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+ wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) |
+ SMI_OP_W | (value & 0xffff));
+
+ return 0;
+}
+
+static int pxa168_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr,
+ int cmd)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ if (pep->phy != NULL)
+ return phy_mii_ioctl(pep->phy, if_mii(ifr), cmd);
+ return -EOPNOTSUPP;
+}
+
+static struct phy_device *phy_scan(struct pxa168_eth_private *pep, int phy_addr)
+{
+ struct mii_bus *bus = pep->smi_bus;
+ struct phy_device *phydev;
+ int start;
+ int num;
+ int i;
+
+ if (phy_addr == PXA168_ETH_PHY_ADDR_DEFAULT) {
+ /* Scan entire range */
+ start = ethernet_phy_get(pep);
+ num = 32;
+ } else {
+ /* Use phy addr specific to platform */
+ start = phy_addr & 0x1f;
+ num = 1;
+ }
+ phydev = NULL;
+ for (i = 0; i < num; i++) {
+ int addr = (start + i) & 0x1f;
+ if (bus->phy_map[addr] == NULL)
+ mdiobus_scan(bus, addr);
+
+ if (phydev == NULL) {
+ phydev = bus->phy_map[addr];
+ if (phydev != NULL)
+ ethernet_phy_set_addr(pep, addr);
+ }
+ }
+
+ return phydev;
+}
+
+static void phy_init(struct pxa168_eth_private *pep, int speed, int duplex)
+{
+ struct phy_device *phy = pep->phy;
+ ethernet_phy_reset(pep);
+
+ phy_attach(pep->dev, dev_name(&phy->dev), 0, PHY_INTERFACE_MODE_MII);
+
+ if (speed == 0) {
+ phy->autoneg = AUTONEG_ENABLE;
+ phy->speed = 0;
+ phy->duplex = 0;
+ phy->supported &= PHY_BASIC_FEATURES;
+ phy->advertising = phy->supported | ADVERTISED_Autoneg;
+ } else {
+ phy->autoneg = AUTONEG_DISABLE;
+ phy->advertising = 0;
+ phy->speed = speed;
+ phy->duplex = duplex;
+ }
+ phy_start_aneg(phy);
+}
+
+static int ethernet_phy_setup(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+
+ if (pep->pd != NULL) {
+ if (pep->pd->init)
+ pep->pd->init();
+ }
+ pep->phy = phy_scan(pep, pep->pd->phy_addr & 0x1f);
+ if (pep->phy != NULL)
+ phy_init(pep, pep->pd->speed, pep->pd->duplex);
+ update_hash_table_mac_address(pep, NULL, dev->dev_addr);
+ return 0;
+}
+
+static int get_random_mac_addr(struct net_device *dev)
+{
+ printk(KERN_INFO "%s:Using random mac address\n", dev->name);
+ random_ether_addr(dev->dev_addr);
+ return 0;
+}
+
+static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ int err;
+
+ err = phy_read_status(pep->phy);
+ if (err == 0)
+ err = phy_ethtool_gset(pep->phy, cmd);
+ return err;
+}
+
+static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+
+ return phy_ethtool_sset(pep->phy, cmd);
+}
+
+static void pxa168_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strncpy(info->driver, DRIVER_NAME, 32);
+ strncpy(info->version, DRIVER_VERSION, 32);
+ strncpy(info->fw_version, "N/A", 32);
+ strncpy(info->bus_info, "N/A", 32);
+}
+
+static u32 pxa168_get_link(struct net_device *dev)
+{
+ return !!netif_carrier_ok(dev);
+}
+
+static const struct ethtool_ops pxa168_ethtool_ops = {
+ .get_settings = pxa168_get_settings,
+ .set_settings = pxa168_set_settings,
+ .get_drvinfo = pxa168_get_drvinfo,
+ .get_link = pxa168_get_link,
+};
+
+static const struct net_device_ops pxa168_eth_netdev_ops = {
+ .ndo_open = pxa168_eth_open,
+ .ndo_stop = pxa168_eth_stop,
+ .ndo_start_xmit = pxa168_eth_start_xmit,
+ .ndo_set_rx_mode = pxa168_eth_set_rx_mode,
+ .ndo_set_mac_address = pxa168_eth_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = pxa168_eth_do_ioctl,
+ .ndo_change_mtu = pxa168_eth_change_mtu,
+ .ndo_tx_timeout = pxa168_eth_tx_timeout,
+};
+
+static int pxa168_eth_probe(struct platform_device *pdev)
+{
+ struct pxa168_eth_private *pep = NULL;
+ struct net_device *dev = NULL;
+ struct resource *res;
+ struct clk *clk;
+ int err;
+
+ printk(KERN_NOTICE "PXA168 10/100 Ethernet Driver\n");
+
+ clk = clk_get(&pdev->dev, "MFUCLK");
+ if (IS_ERR(clk)) {
+ printk(KERN_ERR "fast Ethernet failed to get clock\n");
+ return -ENODEV;
+ }
+ clk_enable(clk);
+
+ dev = alloc_etherdev(sizeof(struct pxa168_eth_private));
+ if (!dev) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ platform_set_drvdata(pdev, dev);
+ pep = netdev_priv(dev);
+ pep->dev = dev;
+ pep->clk = clk;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ err = -ENODEV;
+ goto out;
+ }
+ pep->base = ioremap(res->start, res->end - res->start + 1);
+ if (pep->base == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ BUG_ON(!res);
+ dev->irq = res->start;
+ dev->netdev_ops = &pxa168_eth_netdev_ops;
+ dev->watchdog_timeo = 8 * HZ;
+ dev->base_addr = 0;
+ SET_ETHTOOL_OPS(dev, &pxa168_ethtool_ops);
+
+ INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task);
+
+ pep->rx_ring_size = NUM_RX_DESCS;
+ pep->tx_ring_size = NUM_TX_DESCS;
+ get_random_mac_addr(dev);
+ pep->pd = pdev->dev.platform_data;
+ netif_napi_add(dev, &pep->napi, pxa168_rx_poll, pep->rx_ring_size);
+
+ memset(&pep->timeout, 0, sizeof(struct timer_list));
+ init_timer(&pep->timeout);
+ pep->timeout.function = rxq_refill_timer_wrapper;
+ pep->timeout.data = (unsigned long)pep;
+
+ pep->smi_bus = mdiobus_alloc();
+ if (pep->smi_bus == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+ pep->smi_bus->priv = pep;
+ pep->smi_bus->name = "pxa168_eth smi";
+ pep->smi_bus->read = pxa168_smi_read;
+ pep->smi_bus->write = pxa168_smi_write;
+ snprintf(pep->smi_bus->id, MII_BUS_ID_SIZE, "%d", pdev->id);
+ pep->smi_bus->parent = &pdev->dev;
+ pep->smi_bus->phy_mask = 0xffffffff;
+ if (mdiobus_register(pep->smi_bus) < 0) {
+ err = -ENOMEM;
+ goto out;
+ }
+ pxa168_init_hw(pep);
+ err = ethernet_phy_setup(dev);
+ if (err)
+ goto out;
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ err = register_netdev(dev);
+ if (err)
+ goto out;
+ return 0;
+out:
+ if (pep->clk) {
+ clk_disable(pep->clk);
+ clk_put(pep->clk);
+ pep->clk = NULL;
+ }
+ if (pep->base) {
+ iounmap(pep->base);
+ pep->base = NULL;
+ }
+ if (dev)
+ free_netdev(dev);
+ return err;
+}
+
+static int pxa168_eth_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+
+ if (pep->htpr) {
+ dma_free_coherent(NULL, HASH_ADDR_TABLE_SIZE + 7,
+ pep->htpr, pep->htpr_dma);
+ pep->htpr = NULL;
+ }
+ if (pep->clk) {
+ clk_disable(pep->clk);
+ clk_put(pep->clk);
+ pep->clk = NULL;
+ }
+ if (pep->phy != NULL)
+ phy_detach(pep->phy);
+
+ iounmap(pep->base);
+ pep->base = NULL;
+ unregister_netdev(dev);
+ flush_scheduled_work();
+ free_netdev(dev);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static void pxa168_eth_shutdown(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ eth_port_reset(dev);
+}
+
+#ifdef CONFIG_PM
+static int pxa168_eth_resume(struct platform_device *pdev)
+{
+ return -ENOSYS;
+}
+
+static int pxa168_eth_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return -ENOSYS;
+}
+
+#else
+#define pxa168_eth_resume NULL
+#define pxa168_eth_suspend NULL
+#endif
+
+static struct platform_driver pxa168_eth_driver = {
+ .probe = pxa168_eth_probe,
+ .remove = pxa168_eth_remove,
+ .shutdown = pxa168_eth_shutdown,
+ .resume = pxa168_eth_resume,
+ .suspend = pxa168_eth_suspend,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+static int __init pxa168_init_module(void)
+{
+ return platform_driver_register(&pxa168_eth_driver);
+}
+
+static void __exit pxa168_cleanup_module(void)
+{
+ platform_driver_unregister(&pxa168_eth_driver);
+}
+
+module_init(pxa168_init_module);
+module_exit(pxa168_cleanup_module);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Ethernet driver for Marvell PXA168");
+MODULE_ALIAS("platform:pxa168_eth");
diff --git a/include/linux/pxa168_eth.h b/include/linux/pxa168_eth.h
new file mode 100644
index 0000000..152981c
--- /dev/null
+++ b/include/linux/pxa168_eth.h
@@ -0,0 +1,20 @@
+/*
+ *pxa168 ethernet platform device data definition file.
+ */
+#ifndef __LINUX_PXA168_ETH_H
+#define __LINUX_PXA168_ETH_H
+
+struct pxa168_eth_platform_data {
+ int phy_addr;
+
+ /* If speed is 0, then speed and duplex are autonegotiated. */
+ int speed; /* 0, SPEED_10, SPEED_100 */
+ int duplex; /* DUPLEX_HALF or DUPLEX_FULL */
+
+ /* init callback is used for board specific initialization
+ * e.g on Aspenite its used to initialize the PHY transceiver.
+ */
+ int (*init)(void);
+};
+
+#endif /* __LINUX_PXA168_ETH_H */
--
1.5.3.3
^ permalink raw reply related
* Re: [RFC PATCH] platform: Faciliatate the creation of pseduo-platform busses
From: Grant Likely @ 2010-08-06 15:12 UTC (permalink / raw)
To: Greg KH
Cc: Patrick Pannuto, Patrick Pannuto, linux-kernel@vger.kernel.org,
linux-arm-msm@vger.kernel.org, linux-omap@vger.kernel.org,
damm@opensource.se, lethal@linux-sh.org, rjw@sisk.pl,
dtor@mail.ru, eric.y.miao@gmail.com, netdev@vger.kernel.org,
Kevin Hilman
In-Reply-To: <20100806142727.GB4921@suse.de>
On Fri, Aug 6, 2010 at 8:27 AM, Greg KH <gregkh@suse.de> wrote:
> On Thu, Aug 05, 2010 at 04:59:35PM -0600, Grant Likely wrote:
>> (On that point Greg, what is the reason for even having the
>> /sys/devices/platform/ parent? Why not just let the platform devices
>> sit at the root of the device tree? In the OF case (granted, I'm
>> biased) all of the platform_device registrations reflect the actual
>> device hierarchy expressed in the device tree data.)
>
> If we sat them at the "root", there would be a bunch of them there. I
> don't know, we could drop the parent, I guess whoever created the
> platform device oh so long ago, decided that it would look nicer to be
> in this type of structure.
Personally I'd rather see a meaningful structure used here. Maybe
having them all in the root will encourage people to find realistic
parents for their platform devices. :-) Why don't I float a patch to
remove this and see if anybody freaks out. Should I wrap it with a
CONFIG_ so that it can be configurable for a release or to, or just
make it unconditional?
>> Now, having gone on this whole long tirade, it looks like having
>> separate platform bus types may not be the best approach after all.
>
> I totally agree, and thanks for the detailed explaination, it saved me
> from having to write up the same thing :)
:-)
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* RE: [PATCH] net: add Fast Ethernet driver for PXA168.
From: Sachin Sanap @ 2010-08-06 15:26 UTC (permalink / raw)
To: buytenh@wantstofly.org
Cc: netdev@vger.kernel.org, Ashish Karkare, Prabhanjan Sarnaik,
eric.y.miao@gmail.com, Philip Rakity, Mark Brown
In-Reply-To: <20100806193931.GA428@pe-dt013.marvell.com>
Sorry for resending the patch as new thread. Discard this patch. I will send the patch again as a reply to previous thread.
-Sachin
> -----Original Message-----
> From: Sachin Sanap [mailto:ssanap@marvell.com]
> Sent: Saturday, August 07, 2010 1:10 AM
> To: buytenh@wantstofly.org
> Cc: netdev@vger.kernel.org; Ashish Karkare; Prabhanjan Sarnaik;
> eric.y.miao@gmail.com; Philip Rakity; Mark Brown; Sachin Sanap
> Subject: [PATCH] net: add Fast Ethernet driver for PXA168.
>
> Signed-off-by: Sachin Sanap <ssanap@marvell.com>
> ---
> arch/arm/mach-mmp/aspenite.c | 3 +-
> arch/arm/mach-mmp/pxa168.c | 4 +-
> drivers/net/Kconfig | 10 +
> drivers/net/Makefile | 1 +
> drivers/net/pxa168_eth.c | 1592
> ++++++++++++++++++++++++++++++++++++++++++
> include/linux/pxa168_eth.h | 20 +
> 6 files changed, 1626 insertions(+), 4 deletions(-)
> create mode 100644 drivers/net/pxa168_eth.c
> create mode 100644 include/linux/pxa168_eth.h
>
> diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
> index 61a6d6a..cec505f 100644
> --- a/arch/arm/mach-mmp/aspenite.c
> +++ b/arch/arm/mach-mmp/aspenite.c
> @@ -200,8 +200,7 @@ static int pxa168_eth_init(void)
> }
>
> static struct pxa168_eth_platform_data pxa168_eth_data = {
> - .phy_addr = 0, /* phy addr depends on boards */
> - .port_number = 0,
> + .phy_addr = 0,
> .init = pxa168_eth_init,
> };
> #endif
> diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c
> index f7d1158..6fcaf0c 100644
> --- a/arch/arm/mach-mmp/pxa168.c
> +++ b/arch/arm/mach-mmp/pxa168.c
> @@ -97,7 +97,7 @@ static struct clk_lookup pxa168_clkregs[] = {
> INIT_CLKREG(&clk_ssp4, "pxa168-ssp.3", NULL),
> INIT_CLKREG(&clk_ssp5, "pxa168-ssp.4", NULL),
> INIT_CLKREG(&clk_nand, "pxa3xx-nand", NULL),
> - INIT_CLKREG(&clk_mfu, "pxa168-mfu", "MFUCLK"),
> + INIT_CLKREG(&clk_mfu, "pxa168-eth", "MFUCLK"),
> };
>
> static int __init pxa168_init(void)
> @@ -149,4 +149,4 @@ PXA168_DEVICE(ssp2, "pxa168-ssp", 1, SSP2, 0xd401c000,
> 0x40, 54, 55);
> PXA168_DEVICE(ssp3, "pxa168-ssp", 2, SSP3, 0xd401f000, 0x40, 56, 57);
> PXA168_DEVICE(ssp4, "pxa168-ssp", 3, SSP4, 0xd4020000, 0x40, 58, 59);
> PXA168_DEVICE(ssp5, "pxa168-ssp", 4, SSP5, 0xd4021000, 0x40, 60, 61);
> -PXA168_DEVICE(mfu, "pxa168-mfu", -1, MFU, 0xc0800000, 0x0fff);
> +PXA168_DEVICE(mfu, "pxa168-eth", -1, MFU, 0xc0800000, 0x0fff);
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index ce2fcdd..78cd7e8 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -927,6 +927,16 @@ config SMC91X
> The module will be called smc91x. If you want to compile it as a
> module, say M here and read
> <file:Documentation/kbuild/modules.txt>.
>
> +config PXA168_ETH
> + tristate "Marvell pxa168 ethernet support"
> + depends on CPU_PXA168
> + select PHYLIB
> + help
> + This driver supports the pxa168 Ethernet ports.
> +
> + To compile this driver as a module, choose M here. The module
> + will be called pxa168_eth.
> +
> config NET_NETX
> tristate "NetX Ethernet support"
> select MII
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index 0a0512a..a42d437 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -244,6 +244,7 @@ obj-$(CONFIG_MYRI10GE) += myri10ge/
> obj-$(CONFIG_SMC91X) += smc91x.o
> obj-$(CONFIG_SMC911X) += smc911x.o
> obj-$(CONFIG_SMSC911X) += smsc911x.o
> +obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
> obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
> obj-$(CONFIG_DM9000) += dm9000.o
> obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o
> diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c
> new file mode 100644
> index 0000000..c00fc3c
> --- /dev/null
> +++ b/drivers/net/pxa168_eth.c
> @@ -0,0 +1,1592 @@
> +/*
> + * PXA168 ethernet driver.
> + * Most of the code is derived from mv643xx ethernet driver.
> + *
> + * Copyright (C) 2010 Marvell International Ltd.
> + * Philip Rakity <prakity@marvell.com>
> + * Mark Brown <markb@marvell.com>
> + * Sachin Sanap <ssanap@marvell.com>
> + *
> + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
> USA.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/in.h>
> +#include <linux/ip.h>
> +#include <linux/tcp.h>
> +#include <linux/udp.h>
> +#include <linux/etherdevice.h>
> +#include <linux/bitops.h>
> +#include <linux/delay.h>
> +#include <linux/ethtool.h>
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/workqueue.h>
> +#include <linux/clk.h>
> +#include <linux/phy.h>
> +#include <linux/io.h>
> +#include <linux/types.h>
> +#include <asm/pgtable.h>
> +#include <asm/system.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <asm/cacheflush.h>
> +#include <linux/pxa168_eth.h>
> +
> +#define DRIVER_NAME "pxa168-eth"
> +#define DRIVER_VERSION "0.3"
> +
> +/*
> + * Registers
> + */
> +
> +#define PHY_ADDRESS 0x0000
> +#define SMI 0x0010
> +#define PORT_CONFIG 0x0400
> +#define PORT_CONFIG_EXT 0x0408
> +#define PORT_COMMAND 0x0410
> +#define PORT_STATUS 0x0418
> +#define HTPR 0x0428
> +#define SDMA_CONFIG 0x0440
> +#define SDMA_CMD 0x0448
> +#define INT_CAUSE 0x0450
> +#define INT_W_CLEAR 0x0454
> +#define INT_MASK 0x0458
> +#define ETH_F_RX_DESC_0 0x0480
> +#define ETH_C_RX_DESC_0 0x04A0
> +#define ETH_C_TX_DESC_1 0x04E4
> +
> +/* smi register */
> +#define SMI_BUSY (1 << 28) /* 0 - Write, 1 - Read */
> +#define SMI_R_VALID (1 << 27) /* 0 - Write, 1 - Read */
> +#define SMI_OP_W (0 << 26) /* Write operation */
> +#define SMI_OP_R (1 << 26) /* Read operation */
> +
> +#define PHY_WAIT_ITERATIONS 500
> +
> +#define PXA168_ETH_PHY_ADDR_DEFAULT 0
> +/* RX & TX descriptor command */
> +#define BUF_OWNED_BY_DMA (1 << 31)
> +
> +/* RX descriptor status */
> +#define RX_EN_INT (1 << 23)
> +#define RX_FIRST_DESC (1 << 17)
> +#define RX_LAST_DESC (1 << 16)
> +#define RX_ERROR (1 << 15)
> +
> +/* TX descriptor command */
> +#define TX_EN_INT (1 << 23)
> +#define TX_GEN_CRC (1 << 22)
> +#define TX_ZERO_PADDING (1 << 18)
> +#define TX_FIRST_DESC (1 << 17)
> +#define TX_LAST_DESC (1 << 16)
> +#define TX_ERROR (1 << 15)
> +
> +/* SDMA_CMD */
> +#define SDMA_CMD_AT (1 << 31)
> +#define SDMA_CMD_TXDL (1 << 24)
> +#define SDMA_CMD_TXDH (1 << 23)
> +#define SDMA_CMD_AR (1 << 15)
> +#define SDMA_CMD_ERD (1 << 7)
> +
> +/* Bit definitions of the Port Config Reg */
> +#define PCR_HS (1 << 12)
> +#define PCR_EN (1 << 7)
> +#define PCR_PM (1 << 0)
> +
> +/* Bit definitions of the Port Config Extend Reg */
> +#define PCXR_2BSM (1 << 28)
> +#define PCXR_DSCP_EN (1 << 21)
> +#define PCXR_MFL_1518 (0 << 14)
> +#define PCXR_MFL_1536 (1 << 14)
> +#define PCXR_MFL_2048 (2 << 14)
> +#define PCXR_MFL_64K (3 << 14)
> +#define PCXR_FLP (1 << 11)
> +#define PCXR_PRIO_TX_OFF 3
> +#define PCXR_TX_HIGH_PRI (7 << PCXR_PRIO_TX_OFF)
> +
> +/* Bit definitions of the SDMA Config Reg */
> +#define SDCR_BSZ_OFF 12
> +#define SDCR_BSZ8 (3 << SDCR_BSZ_OFF)
> +#define SDCR_BSZ4 (2 << SDCR_BSZ_OFF)
> +#define SDCR_BSZ2 (1 << SDCR_BSZ_OFF)
> +#define SDCR_BSZ1 (0 << SDCR_BSZ_OFF)
> +#define SDCR_BLMR (1 << 6)
> +#define SDCR_BLMT (1 << 7)
> +#define SDCR_RIFB (1 << 9)
> +#define SDCR_RC_OFF 2
> +#define SDCR_RC_MAX_RETRANS (0xf << SDCR_RC_OFF)
> +
> +/*
> + * Bit definitions of the Interrupt Cause Reg
> + * and Interrupt MASK Reg is the same
> + */
> +#define ICR_RXBUF (1 << 0)
> +#define ICR_TXBUF_H (1 << 2)
> +#define ICR_TXBUF_L (1 << 3)
> +#define ICR_TXEND_H (1 << 6)
> +#define ICR_TXEND_L (1 << 7)
> +#define ICR_RXERR (1 << 8)
> +#define ICR_TXERR_H (1 << 10)
> +#define ICR_TXERR_L (1 << 11)
> +#define ICR_TX_UDR (1 << 13)
> +#define ICR_MII_CH (1 << 28)
> +
> +#define ALL_INTS (ICR_TXBUF_H | ICR_TXBUF_L | ICR_TX_UDR |\
> + ICR_TXERR_H | ICR_TXERR_L |\
> + ICR_TXEND_H | ICR_TXEND_L |\
> + ICR_RXBUF | ICR_RXERR | ICR_MII_CH)
> +
> +#define ETH_HW_IP_ALIGN 2 /* hw aligns IP header */
> +#define ETH_EXTRA_HEADER (6+6+2+4) /* dest+src addr+protocol id+crc
> */
> +#define ETH_DATA_LEN 1500
> +#define MAX_PKT_SIZE 1518
> +
> +#define NUM_RX_DESCS 64
> +#define NUM_TX_DESCS 64
> +#define MAX_DESCS_PER_HIGH (60)
> +#define TX_DESC_COUNT_LOW (10)
> +
> +#define HASH_ADD 0
> +#define HASH_DELETE 1
> +#define HASH_ADDR_TABLE_SIZE 0x4000 /* 16K (1/2K address - PCR_HS ==
> 1) */
> +#define HOP_NUMBER 12
> +
> +/* Bit definitions for Port status */
> +#define PORT_SPEED_100 (1 << 0)
> +#define FULL_DUPLEX (1 << 1)
> +#define FLOW_CONTROL_ENABLED (1 << 2)
> +#define LINK_UP (1 << 3)
> +
> +/* Bit definitions for work to be done */
> +#define WORK_LINK (1 << 0)
> +#define WORK_TX_DONE (1 << 1)
> +
> +#define TX_DONE_INTERVAL 30
> +
> +struct rx_desc {
> + u32 cmd_sts; /* Descriptor command status */
> + u16 byte_cnt; /* Descriptor buffer byte count */
> + u16 buf_size; /* Buffer size */
> + u32 buf_ptr; /* Descriptor buffer pointer */
> + u32 next_desc_ptr; /* Next descriptor pointer */
> +};
> +
> +struct tx_desc {
> + u32 cmd_sts; /* Command/status field */
> + u16 reserved;
> + u16 byte_cnt; /* buffer byte count */
> + u32 buf_ptr; /* pointer to buffer for this descriptor */
> + u32 next_desc_ptr; /* Pointer to next descriptor */
> +};
> +
> +struct pxa168_eth_private {
> + int rx_resource_err; /* Rx ring resource error flag */
> +
> + /* Next available and first returning Rx resource */
> + int rx_curr_desc_q, rx_used_desc_q;
> +
> + /* Next available and first returning Tx resource */
> + int tx_curr_desc_q, tx_used_desc_q;
> +
> + struct rx_desc *p_rx_desc_area;
> + dma_addr_t rx_desc_dma;
> + int rx_desc_area_size;
> + struct sk_buff **rx_skb;
> +
> + struct tx_desc *p_tx_desc_area;
> + dma_addr_t tx_desc_dma;
> + int tx_desc_area_size;
> + struct sk_buff **tx_skb;
> +
> + struct work_struct tx_timeout_task;
> +
> + struct net_device *dev;
> + struct napi_struct napi;
> + u8 work_todo;
> +
> + struct net_device_stats stats;
> + /* Size of Tx Ring per queue */
> + int tx_ring_size;
> + /* Number of tx descriptors in use */
> + int tx_desc_count;
> + /* Size of Rx Ring per queue */
> + int rx_ring_size;
> + /* Number of rx descriptors in use */
> + int rx_desc_count;
> +
> + /*
> + * Used in case RX Ring is empty, which can occur when
> + * system does not have resources (skb's)
> + */
> + struct timer_list timeout;
> + struct mii_bus *smi_bus;
> + struct phy_device *phy;
> +
> + /* clock */
> + struct clk *clk;
> + struct pxa168_eth_platform_data *pd;
> + /*
> + * Ethernet controller base address.
> + */
> + void __iomem *base;
> +
> + /* Pointer to the hardware address filter table */
> + void *htpr;
> + dma_addr_t htpr_dma;
> +};
> +
> +struct addr_table_entry {
> + u32 lo;
> + u32 hi;
> +};
> +
> +/* Bit fields of a Hash Table Entry */
> +enum hash_table_entry {
> + HASH_ENTRY_VALID = 1,
> + SKIP = 2,
> + HASH_ENTRY_RECEIVE_DISCARD = 4,
> + HASH_ENTRY_RECEIVE_DISCARD_BIT = 2
> +};
> +
> +static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd
> *cmd);
> +static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd
> *cmd);
> +static int pxa168_init_hw(struct pxa168_eth_private *pep);
> +static void eth_port_reset(struct net_device *dev);
> +static void eth_port_start(struct net_device *dev);
> +static int pxa168_eth_open(struct net_device *dev);
> +static int pxa168_eth_stop(struct net_device *dev);
> +static int ethernet_phy_setup(struct net_device *dev);
> +
> +static inline u32 rdl(struct pxa168_eth_private *pep, int offset)
> +{
> + return readl(pep->base + offset);
> +}
> +
> +static inline void wrl(struct pxa168_eth_private *pep, int offset, u32
> data)
> +{
> + writel(data, pep->base + offset);
> +}
> +
> +static void abort_dma(struct pxa168_eth_private *pep)
> +{
> + int delay;
> + int max_retries = 40;
> +
> + do {
> + wrl(pep, SDMA_CMD, SDMA_CMD_AR | SDMA_CMD_AT);
> + udelay(100);
> +
> + delay = 10;
> + while ((rdl(pep, SDMA_CMD) & (SDMA_CMD_AR | SDMA_CMD_AT))
> + && delay-- > 0) {
> + udelay(10);
> + }
> + } while (max_retries-- > 0 && delay <= 0);
> +
> + if (max_retries <= 0)
> + printk(KERN_ERR "%s : DMA Stuck\n", __func__);
> +}
> +
> +static int ethernet_phy_get(struct pxa168_eth_private *pep)
> +{
> + unsigned int reg_data;
> +
> + reg_data = rdl(pep, PHY_ADDRESS);
> +
> + return reg_data & 0x1f;
> +}
> +
> +static void ethernet_phy_set_addr(struct pxa168_eth_private *pep, int
> phy_addr)
> +{
> + u32 reg_data;
> +
> + reg_data = rdl(pep, PHY_ADDRESS);
> + reg_data &= ~(0x1f);
> + reg_data |= phy_addr & 0x1f;
> + wrl(pep, PHY_ADDRESS, reg_data);
> +}
> +
> +static void ethernet_phy_reset(struct pxa168_eth_private *pep)
> +{
> + int data;
> +
> + data = phy_read(pep->phy, MII_BMCR);
> + if (data < 0)
> + return;
> +
> + data |= BMCR_RESET;
> + if (phy_write(pep->phy, MII_BMCR, data) < 0)
> + return;
> +
> + do {
> + data = phy_read(pep->phy, MII_BMCR);
> + } while (data >= 0 && data & BMCR_RESET);
> +}
> +
> +static void rxq_refill(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct sk_buff *skb;
> + struct rx_desc *p_used_rx_desc;
> + int used_rx_desc;
> +
> + while (pep->rx_desc_count < pep->rx_ring_size) {
> + skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN);
> + if (!skb)
> + break;
> + pep->rx_desc_count++;
> + /* Get 'used' Rx descriptor */
> + used_rx_desc = pep->rx_used_desc_q;
> + p_used_rx_desc = &pep->p_rx_desc_area[used_rx_desc];
> + p_used_rx_desc->buf_ptr = dma_map_single(NULL,
> + skb->data,
> + MAX_PKT_SIZE +
> + ETH_HW_IP_ALIGN,
> + DMA_FROM_DEVICE);
> + p_used_rx_desc->buf_size = MAX_PKT_SIZE + ETH_HW_IP_ALIGN;
> + pep->rx_skb[used_rx_desc] = skb;
> +
> + /* Return the descriptor to DMA ownership */
> + wmb();
> + p_used_rx_desc->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT;
> + wmb();
> +
> + /* Move the used descriptor pointer to the next descriptor */
> + pep->rx_used_desc_q = (used_rx_desc + 1) % pep->rx_ring_size;
> +
> + /* Any Rx return cancels the Rx resource error status */
> + pep->rx_resource_err = 0;
> +
> + skb_reserve(skb, ETH_HW_IP_ALIGN);
> + }
> +
> + /*
> + * If RX ring is empty of SKB, set a timer to try allocating
> + * again at a later time.
> + */
> + if (pep->rx_desc_count == 0) {
> + pep->timeout.expires = jiffies + (HZ / 10); /* 100 mSec */
> + add_timer(&pep->timeout);
> + }
> +}
> +
> +static inline void rxq_refill_timer_wrapper(unsigned long data)
> +{
> + struct pxa168_eth_private *pep = (void *)data;
> + napi_schedule(&pep->napi);
> +}
> +
> +static inline u32 nibble_swapping_32_bit(u32 x)
> +{
> + return (((x) & 0xf0f0f0f0) >> 4) | (((x) & 0x0f0f0f0f) << 4);
> +}
> +
> +static inline u32 nibble_swapping_16_bit(u32 x)
> +{
> + return (((x) & 0x0000f0f0) >> 4) | (((x) & 0x00000f0f) << 4);
> +}
> +
> +static inline u32 flip_4_bits(u32 x)
> +{
> + return (((x) & 0x01) << 3) | (((x) & 0x002) << 1)
> + | (((x) & 0x04) >> 1) | (((x) & 0x008) >> 3);
> +}
> +
> +/*
> + * ----------------------------------------------------------------------
> ------
> + * This function will calculate the hash function of the address.
> + * depends on the hash mode and hash size.
> + * Inputs
> + * mac_high - the 2 most significant bytes of the MAC address.
> + * mac_low - the 4 least significant bytes of the MAC address.
> + * Outputs
> + * return the calculated entry.
> + */
> +static u32 hash_function(u32 mac_high, u32 mac_low)
> +{
> + u32 hash_result;
> + u32 addr_high;
> + u32 addr_low;
> + u32 addr0;
> + u32 addr1;
> + u32 addr2;
> + u32 addr3;
> + u32 addr_high_swapped;
> + u32 addr_low_swapped;
> +
> + addr_high = nibble_swapping_16_bit(mac_high);
> + addr_low = nibble_swapping_32_bit(mac_low);
> +
> + addr_high_swapped = flip_4_bits(addr_high & 0xf)
> + + ((flip_4_bits((addr_high >> 4) & 0xf)) << 4)
> + + ((flip_4_bits((addr_high >> 8) & 0xf)) << 8)
> + + ((flip_4_bits((addr_high >> 12) & 0xf)) << 12);
> +
> + addr_low_swapped = flip_4_bits(addr_low & 0xf)
> + + ((flip_4_bits((addr_low >> 4) & 0xf)) << 4)
> + + ((flip_4_bits((addr_low >> 8) & 0xf)) << 8)
> + + ((flip_4_bits((addr_low >> 12) & 0xf)) << 12)
> + + ((flip_4_bits((addr_low >> 16) & 0xf)) << 16)
> + + ((flip_4_bits((addr_low >> 20) & 0xf)) << 20)
> + + ((flip_4_bits((addr_low >> 24) & 0xf)) << 24)
> + + ((flip_4_bits((addr_low >> 28) & 0xf)) << 28);
> +
> + addr_high = addr_high_swapped;
> + addr_low = addr_low_swapped;
> +
> + addr0 = (addr_low >> 2) & 0x03f;
> + addr1 = (addr_low & 0x003) | ((addr_low >> 8) & 0x7f) << 2;
> + addr2 = (addr_low >> 15) & 0x1ff;
> + addr3 = ((addr_low >> 24) & 0x0ff) | ((addr_high & 1) << 8);
> +
> + hash_result = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
> + hash_result = hash_result & 0x07ff;
> + return hash_result;
> +}
> +
> +/*
> + * ----------------------------------------------------------------------
> ------
> + * This function will add/del an entry to the address table.
> + * Inputs
> + * pep - ETHERNET .
> + * mac_high - the 2 most significant bytes of the MAC address.
> + * mac_low - the 4 least significant bytes of the MAC address.
> + * skip - if 1, skip this address.Used in case of deleting an entry which
> is a
> + * part of chain in the hash table.We cant just delete the entry
> since
> + * that will break the chain.We need to defragment the tables time to
> + * time.
> + * rd - 0 Discard packet upon match.
> + * - 1 Receive packet upon match.
> + * Outputs
> + * address table entry is added/deleted.
> + * 0 if success.
> + * -ENOSPC if table full
> + */
> +static int add_del_hash_entry(struct pxa168_eth_private *pep, u32
> mac_high,
> + u32 mac_low, u32 rd, u32 skip, int del)
> +{
> + struct addr_table_entry *entry, *start;
> + u32 new_high;
> + u32 new_low;
> + u32 i;
> +
> + new_low = (((mac_high >> 4) & 0xf) << 15)
> + | (((mac_high >> 0) & 0xf) << 11)
> + | (((mac_high >> 12) & 0xf) << 7)
> + | (((mac_high >> 8) & 0xf) << 3)
> + | (((mac_low >> 20) & 0x1) << 31)
> + | (((mac_low >> 16) & 0xf) << 27)
> + | (((mac_low >> 28) & 0xf) << 23)
> + | (((mac_low >> 24) & 0xf) << 19)
> + | (skip << SKIP) | (rd << HASH_ENTRY_RECEIVE_DISCARD_BIT)
> + | HASH_ENTRY_VALID;
> +
> + new_high = (((mac_low >> 4) & 0xf) << 15)
> + | (((mac_low >> 0) & 0xf) << 11)
> + | (((mac_low >> 12) & 0xf) << 7)
> + | (((mac_low >> 8) & 0xf) << 3)
> + | (((mac_low >> 21) & 0x7) << 0);
> +
> + /*
> + * Pick the appropriate table, start scanning for free/reusable
> + * entries at the index obtained by hashing the specified MAC
> address
> + */
> + start = (struct addr_table_entry *)(pep->htpr);
> + entry = start + hash_function(mac_high, mac_low);
> + for (i = 0; i < HOP_NUMBER; i++) {
> + if (!(entry->lo & HASH_ENTRY_VALID)) {
> + break;
> + } else {
> + /* if same address put in same position */
> + if (((entry->lo & 0xfffffff8) == (new_low & 0xfffffff8))
> + && (entry->hi == new_high)) {
> + break;
> + }
> + }
> + if (entry == start + 0x7ff)
> + entry = start;
> + else
> + entry++;
> + }
> +
> + if (((entry->lo & 0xfffffff8) != (new_low & 0xfffffff8)) &&
> + (entry->hi != new_high) && del)
> + return 0;
> +
> + if (i == HOP_NUMBER) {
> + if (!del) {
> + printk(KERN_INFO "%s: table section is full\n",
> + __FILE__);
> + return -ENOSPC;
> + } else
> + return 0;
> + }
> +
> + /*
> + * Update the selected entry
> + */
> + if (del) {
> + entry->hi = 0;
> + entry->lo = 0;
> + } else {
> + entry->hi = cpu_to_le32(new_high);
> + entry->lo = cpu_to_le32(new_low);
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * ----------------------------------------------------------------------
> ------
> + * Create an addressTable entry from MAC address info
> + * found in the specifed net_device struct
> + *
> + * Input : pointer to ethernet interface network device structure
> + * Output : N/A
> + */
> +static void update_hash_table_mac_address(struct pxa168_eth_private *pep,
> + u8 *oaddr, u8 *addr)
> +{
> + u32 mac_high;
> + u32 mac_low;
> +
> + /* Delete old entry */
> + if (oaddr) {
> + mac_high = cpu_to_le32((oaddr[0] << 8) | oaddr[1]);
> + mac_low = cpu_to_le32((oaddr[2] << 24) | (oaddr[3] << 16) |
> + (oaddr[4] << 8) | oaddr[5]);
> + add_del_hash_entry(pep, mac_high, mac_low, 1, 0, HASH_DELETE);
> + }
> + /* Add new entry */
> + mac_high = cpu_to_le32((addr[0] << 8) | addr[1]);
> + mac_low = cpu_to_le32((addr[2] << 24) | (addr[3] << 16) |
> + (addr[4] << 8) | addr[5]);
> + add_del_hash_entry(pep, mac_high, mac_low, 1, 0, HASH_ADD);
> +}
> +
> +static int init_hash_table(struct pxa168_eth_private *pep)
> +{
> + /*
> + * Hardware expects CPU to build a hash table based on a predefined
> + * hash function and populate it based on hardware address. The
> + * location of the hash table is identified by 32-bit pointer stored
> + * in HTPR internal register. Two possible sizes exists for the hash
> + * table 256kB and 16kB.We currently only support 16kB.
> + */
> + /* TODO: Add support for 256kB hash table */
> + if (pep->htpr == NULL) {
> + pep->htpr = dma_alloc_coherent(NULL,
> + HASH_ADDR_TABLE_SIZE,
> + &pep->htpr_dma, GFP_KERNEL);
> + if (pep->htpr == NULL)
> + return -ENOMEM;
> + }
> + memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
> + wrl(pep, HTPR, pep->htpr_dma);
> + return 0;
> +}
> +
> +static void pxa168_eth_set_rx_mode(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct netdev_hw_addr *ha;
> + u32 val;
> +
> + val = rdl(pep, PORT_CONFIG);
> + if (dev->flags & IFF_PROMISC)
> + val |= PCR_PM;
> + else
> + val &= ~PCR_PM;
> + wrl(pep, PORT_CONFIG, val);
> + netdev_for_each_mc_addr(ha, dev)
> + update_hash_table_mac_address(pep, NULL, ha->addr);
> +}
> +
> +static int pxa168_eth_set_mac_address(struct net_device *dev, void *addr)
> +{
> + struct sockaddr *sa = addr;
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + unsigned char oldMac[ETH_ALEN];
> +
> + if (!is_valid_ether_addr(sa->sa_data))
> + return -EINVAL;
> + memcpy(oldMac, dev->dev_addr, ETH_ALEN);
> + memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
> + netif_addr_lock_bh(dev);
> + update_hash_table_mac_address(pep, oldMac, dev->dev_addr);
> + netif_addr_unlock_bh(dev);
> + return 0;
> +}
> +
> +static void eth_port_start(struct net_device *dev)
> +{
> + unsigned int val = 0;
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int tx_curr_desc, rx_curr_desc;
> +
> + /* Perform PHY reset, if there is a PHY. */
> + if (pep->phy != NULL) {
> + struct ethtool_cmd cmd;
> +
> + pxa168_get_settings(pep->dev, &cmd);
> + ethernet_phy_reset(pep);
> + pxa168_set_settings(pep->dev, &cmd);
> + }
> +
> + /* Assignment of Tx CTRP of given queue */
> + tx_curr_desc = pep->tx_curr_desc_q;
> + wrl(pep, ETH_C_TX_DESC_1,
> + (u32) ((struct tx_desc *)pep->tx_desc_dma + tx_curr_desc));
> +
> + /* Assignment of Rx CRDP of given queue */
> + rx_curr_desc = pep->rx_curr_desc_q;
> + wrl(pep, ETH_C_RX_DESC_0,
> + (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
> +
> + wrl(pep, ETH_F_RX_DESC_0,
> + (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
> +
> + /* Clear all interrupts */
> + wrl(pep, INT_CAUSE, 0);
> +
> + /* Enable all interrupts for receive, transmit and error. */
> + wrl(pep, INT_MASK, ALL_INTS);
> +
> + val = rdl(pep, PORT_CONFIG);
> + val |= PCR_EN;
> + wrl(pep, PORT_CONFIG, val);
> +
> + /* Start RX DMA engine */
> + val = rdl(pep, SDMA_CMD);
> + val |= SDMA_CMD_ERD;
> + wrl(pep, SDMA_CMD, val);
> +}
> +
> +static void eth_port_reset(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + unsigned int val = 0;
> +
> + /* Stop all interrupts for receive, transmit and error. */
> + wrl(pep, INT_MASK, 0);
> +
> + /* Clear all interrupts */
> + wrl(pep, INT_CAUSE, 0);
> +
> + /* Stop RX DMA */
> + val = rdl(pep, SDMA_CMD);
> + val &= ~SDMA_CMD_ERD; /* abort dma command */
> +
> + /* Abort any transmit and receive operations and put DMA
> + * in idle state.
> + */
> + abort_dma(pep);
> +
> + /* Disable port */
> + val = rdl(pep, PORT_CONFIG);
> + val &= ~PCR_EN;
> + wrl(pep, PORT_CONFIG, val);
> +}
> +
> +/*
> + * txq_reclaim - Free the tx desc data for completed descriptors
> + * If force is non-zero, frees uncompleted descriptors as well
> + */
> +static int txq_reclaim(struct net_device *dev, int force)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct tx_desc *desc;
> + u32 cmd_sts;
> + struct sk_buff *skb;
> + int tx_index;
> + dma_addr_t addr;
> + int count;
> + int released = 0;
> +
> + netif_tx_lock(dev);
> +
> + pep->work_todo &= ~(WORK_TX_DONE);
> + while (pep->tx_desc_count > 0) {
> + tx_index = pep->tx_used_desc_q;
> + desc = &pep->p_tx_desc_area[tx_index];
> + cmd_sts = desc->cmd_sts;
> + if (!force && (cmd_sts & BUF_OWNED_BY_DMA)) {
> + if (released > 0) {
> + goto txq_reclaim_end;
> + } else {
> + released = -1;
> + goto txq_reclaim_end;
> + }
> + }
> + pep->tx_used_desc_q = (tx_index + 1) % pep->tx_ring_size;
> + pep->tx_desc_count--;
> + addr = desc->buf_ptr;
> + count = desc->byte_cnt;
> + skb = pep->tx_skb[tx_index];
> + if (skb)
> + pep->tx_skb[tx_index] = NULL;
> +
> + if (cmd_sts & TX_ERROR) {
> + if (net_ratelimit())
> + printk(KERN_ERR "%s: Error in TX\n", dev->name);
> + dev->stats.tx_errors++;
> + }
> + dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE);
> + if (skb)
> + dev_kfree_skb_irq(skb);
> + released++;
> + }
> +txq_reclaim_end:
> + netif_tx_unlock(dev);
> + return released;
> +}
> +
> +static void pxa168_eth_tx_timeout(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + printk(KERN_INFO "%s: TX timeout desc_count %d\n",
> + dev->name, pep->tx_desc_count);
> +
> + schedule_work(&pep->tx_timeout_task);
> +}
> +
> +static void pxa168_eth_tx_timeout_task(struct work_struct *work)
> +{
> + struct pxa168_eth_private *pep = container_of(work,
> + struct pxa168_eth_private,
> + tx_timeout_task);
> + struct net_device *dev = pep->dev;
> + pxa168_eth_stop(dev);
> + pxa168_eth_open(dev);
> +}
> +
> +static int rxq_process(struct net_device *dev, int budget)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct net_device_stats *stats = &dev->stats;
> + unsigned int received_packets = 0;
> + struct sk_buff *skb;
> +
> + while (budget-- > 0) {
> +
> + int rx_next_curr_desc, rx_curr_desc, rx_used_desc;
> + struct rx_desc *rx_desc;
> + unsigned int cmd_sts;
> +
> + /* Do not process Rx ring in case of Rx ring resource error */
> + if (pep->rx_resource_err)
> + break;
> + rx_curr_desc = pep->rx_curr_desc_q;
> + rx_used_desc = pep->rx_used_desc_q;
> + rx_desc = &pep->p_rx_desc_area[rx_curr_desc];
> + cmd_sts = rx_desc->cmd_sts;
> + rmb();
> + if (cmd_sts & (BUF_OWNED_BY_DMA))
> + break;
> + skb = pep->rx_skb[rx_curr_desc];
> + pep->rx_skb[rx_curr_desc] = NULL;
> +
> + rx_next_curr_desc = (rx_curr_desc + 1) % pep->rx_ring_size;
> + pep->rx_curr_desc_q = rx_next_curr_desc;
> +
> + /* Rx descriptors exhausted. */
> + /* Set the Rx ring resource error flag */
> + if (rx_next_curr_desc == rx_used_desc)
> + pep->rx_resource_err = 1;
> + pep->rx_desc_count--;
> + dma_unmap_single(NULL, rx_desc->buf_ptr,
> + MAX_PKT_SIZE + ETH_HW_IP_ALIGN,
> + DMA_FROM_DEVICE);
> + received_packets++;
> + /*
> + * Update statistics.
> + * Note byte count includes 4 byte CRC count
> + */
> + stats->rx_packets++;
> + stats->rx_bytes += rx_desc->byte_cnt;
> + /*
> + * In case received a packet without first / last bits on OR
> + * the error summary bit is on, the packets needs to be droped.
> + */
> + if (((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
> + (RX_FIRST_DESC | RX_LAST_DESC))
> + || (cmd_sts & RX_ERROR)) {
> +
> + stats->rx_dropped++;
> + if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
> + (RX_FIRST_DESC | RX_LAST_DESC)) {
> + if (net_ratelimit())
> + printk(KERN_ERR
> + "%s: Rx pkt on multiple desc\n",
> + dev->name);
> + }
> + if (cmd_sts & RX_ERROR)
> + stats->rx_errors++;
> + dev_kfree_skb_irq(skb);
> + } else {
> + /*
> + * The -4 is for the CRC in the trailer of the
> + * received packet
> + */
> + skb_put(skb, rx_desc->byte_cnt - 4);
> + skb->protocol = eth_type_trans(skb, dev);
> + netif_receive_skb(skb);
> + }
> + dev->last_rx = jiffies;
> + }
> + /* Fill RX ring with skb's */
> + rxq_refill(dev);
> + return received_packets;
> +}
> +
> +static int pxa168_eth_collect_events(struct pxa168_eth_private *pep,
> + struct net_device *dev)
> +{
> + u32 icr;
> + int ret = 0;
> +
> + icr = rdl(pep, INT_CAUSE);
> + if (0x00 == icr)
> + return IRQ_NONE;
> +
> + wrl(pep, INT_CAUSE, icr ^ 0xffffffff);
> + if (icr & (ICR_TXBUF_H | ICR_TXBUF_L)) {
> + pep->work_todo |= WORK_TX_DONE;
> + ret = 1;
> + }
> + if (icr & ICR_RXBUF)
> + ret = 1;
> + if (icr & ICR_MII_CH) {
> + pep->work_todo |= WORK_LINK;
> + ret = 1;
> + }
> + return ret;
> +}
> +
> +static void handle_link_event(struct pxa168_eth_private *pep)
> +{
> + struct net_device *dev = pep->dev;
> + u32 port_status;
> + int speed;
> + int duplex;
> + int fc;
> +
> + port_status = rdl(pep, PORT_STATUS);
> + if (!(port_status & LINK_UP)) {
> + if (netif_carrier_ok(dev)) {
> + printk(KERN_INFO "%s: link down\n", dev->name);
> + netif_carrier_off(dev);
> + txq_reclaim(dev, 1);
> + }
> + return;
> + }
> + if (port_status & PORT_SPEED_100)
> + speed = 100;
> + else
> + speed = 10;
> +
> + duplex = (port_status & FULL_DUPLEX) ? 1 : 0;
> + fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0;
> + printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, "
> + "flow control %sabled\n", dev->name,
> + speed, duplex ? "full" : "half", fc ? "en" : "dis");
> + if (!netif_carrier_ok(dev))
> + netif_carrier_on(dev);
> +}
> +
> +static irqreturn_t pxa168_eth_int_handler(int irq, void *dev_id)
> +{
> + struct net_device *dev = (struct net_device *)dev_id;
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + if (unlikely(!pxa168_eth_collect_events(pep, dev)))
> + return IRQ_NONE;
> + /* Disable interrupts */
> + wrl(pep, INT_MASK, 0);
> + napi_schedule(&pep->napi);
> + return IRQ_HANDLED;
> +}
> +
> +static int set_port_config_ext(struct pxa168_eth_private *pep, int mtu)
> +{
> + int mtu_size;
> +
> + if ((mtu > ETH_DATA_LEN) || (mtu < 64))
> + return -EINVAL;
> +
> + mtu_size = PCXR_MFL_1518;
> + /* Extended Port Configuration */
> + wrl(pep,
> + PORT_CONFIG_EXT, PCXR_2BSM | /* Two byte suffix aligns IP hdr */
> + PCXR_DSCP_EN | /* Enable DSCP in IP */
> + mtu_size | PCXR_FLP | /* do not force link pass */
> + PCXR_TX_HIGH_PRI); /* Transmit - high priority queue */
> +
> + (pep->dev)->mtu = mtu;
> + return 0;
> +}
> +
> +static int pxa168_init_hw(struct pxa168_eth_private *pep)
> +{
> + int err = 0;
> +
> + /* Disable interrupts */
> + wrl(pep, INT_MASK, 0);
> + wrl(pep, INT_CAUSE, 0);
> + /* Write to ICR to clear interrupts. */
> + wrl(pep, INT_W_CLEAR, 0);
> + /* Abort any transmit and receive operations and put DMA
> + * in idle state.
> + */
> + abort_dma(pep);
> + /* Initialize address hash table */
> + err = init_hash_table(pep);
> + if (err)
> + return err;
> + /* SDMA configuration */
> + wrl(pep, SDMA_CONFIG, SDCR_BSZ8 | /* Burst size = 32 bytes */
> + SDCR_RIFB | /* Rx interrupt on frame */
> + SDCR_BLMT | /* Little endian transmit */
> + SDCR_BLMR | /* Little endian receive */
> + SDCR_RC_MAX_RETRANS); /* Max retransmit count */
> + /* Port Configuration */
> + wrl(pep, PORT_CONFIG, PCR_HS); /* Hash size is 1/2kb */
> + set_port_config_ext(pep, (pep->dev)->mtu);
> + return err;
> +}
> +
> +static int rxq_init(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct rx_desc *p_rx_desc;
> + int size = 0, i = 0;
> + int rx_desc_num = pep->rx_ring_size;
> +
> + /* Allocate RX skb rings */
> + pep->rx_skb = kmalloc(sizeof(*pep->rx_skb) * pep->rx_ring_size,
> + GFP_KERNEL);
> + if (!pep->rx_skb) {
> + printk(KERN_ERR "%s: Cannot alloc RX skb ring\n", dev->name);
> + return -ENOMEM;
> + }
> + /* Allocate RX ring */
> + pep->rx_desc_count = 0;
> + size = pep->rx_ring_size * sizeof(struct rx_desc);
> + pep->rx_desc_area_size = size;
> + pep->p_rx_desc_area = dma_alloc_coherent(NULL, size,
> + &pep->rx_desc_dma, GFP_KERNEL);
> + if (!pep->p_rx_desc_area) {
> + printk(KERN_ERR "%s: Cannot alloc RX ring (size %d bytes)\n",
> + dev->name, size);
> + goto out;
> + }
> + memset((void *)pep->p_rx_desc_area, 0, size);
> + /* initialize the next_desc_ptr links in the Rx descriptors ring */
> + p_rx_desc = (struct rx_desc *)pep->p_rx_desc_area;
> + for (i = 0; i < rx_desc_num; i++) {
> + p_rx_desc[i].next_desc_ptr = pep->rx_desc_dma +
> + ((i + 1) % rx_desc_num) * sizeof(struct rx_desc);
> + }
> + /* Save Rx desc pointer to driver struct. */
> + pep->rx_curr_desc_q = 0;
> + pep->rx_used_desc_q = 0;
> + pep->rx_desc_area_size = rx_desc_num * sizeof(struct rx_desc);
> + return 0;
> +out:
> + kfree(pep->rx_skb);
> + return -ENOMEM;
> +}
> +
> +static void rxq_deinit(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int curr;
> +
> + /* Free preallocated skb's on RX rings */
> + for (curr = 0; pep->rx_desc_count && curr < pep->rx_ring_size;
> curr++) {
> + if (pep->rx_skb[curr]) {
> + dev_kfree_skb(pep->rx_skb[curr]);
> + pep->rx_desc_count--;
> + }
> + }
> + if (pep->rx_desc_count)
> + printk(KERN_ERR
> + "Error in freeing Rx Ring. %d skb's still\n",
> + pep->rx_desc_count);
> + /* Free RX ring */
> + if (pep->p_rx_desc_area)
> + dma_free_coherent(NULL, pep->rx_desc_area_size,
> + pep->p_rx_desc_area, pep->rx_desc_dma);
> + kfree(pep->rx_skb);
> +}
> +
> +static int txq_init(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct tx_desc *p_tx_desc;
> + int size = 0, i = 0;
> + int tx_desc_num = pep->tx_ring_size;
> +
> + pep->tx_skb = kmalloc(sizeof(*pep->tx_skb) * pep->tx_ring_size,
> + GFP_KERNEL);
> + if (!pep->tx_skb) {
> + printk(KERN_ERR "%s: Cannot alloc TX skb ring\n", dev->name);
> + return -ENOMEM;
> + }
> + /* Allocate TX ring */
> + pep->tx_desc_count = 0;
> + size = pep->tx_ring_size * sizeof(struct tx_desc);
> + pep->tx_desc_area_size = size;
> + pep->p_tx_desc_area = dma_alloc_coherent(NULL, size,
> + &pep->tx_desc_dma, GFP_KERNEL);
> + if (!pep->p_tx_desc_area) {
> + printk(KERN_ERR "%s: Cannot allocate Tx Ring (size %d
> bytes)\n",
> + dev->name, size);
> + goto out;
> + }
> + memset((void *)pep->p_tx_desc_area, 0, pep->tx_desc_area_size);
> + /* Initialize the next_desc_ptr links in the Tx descriptors ring */
> + p_tx_desc = (struct tx_desc *)pep->p_tx_desc_area;
> + for (i = 0; i < tx_desc_num; i++) {
> + p_tx_desc[i].next_desc_ptr = pep->tx_desc_dma +
> + ((i + 1) % tx_desc_num) * sizeof(struct tx_desc);
> + }
> + pep->tx_curr_desc_q = 0;
> + pep->tx_used_desc_q = 0;
> + pep->tx_desc_area_size = tx_desc_num * sizeof(struct tx_desc);
> + return 0;
> +out:
> + kfree(pep->tx_skb);
> + return -ENOMEM;
> +}
> +
> +static void txq_deinit(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + /* Free outstanding skb's on TX ring */
> + txq_reclaim(dev, 1);
> + BUG_ON(pep->tx_used_desc_q != pep->tx_curr_desc_q);
> + /* Free TX ring */
> + if (pep->p_tx_desc_area)
> + dma_free_coherent(NULL, pep->tx_desc_area_size,
> + pep->p_tx_desc_area, pep->tx_desc_dma);
> + kfree(pep->tx_skb);
> +}
> +
> +static int pxa168_eth_open(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int err;
> +
> + err = request_irq(dev->irq, pxa168_eth_int_handler,
> + IRQF_DISABLED , dev->name, dev);
> + if (err) {
> + dev_printk(KERN_ERR, &dev->dev, "can't assign irq\n");
> + return -EAGAIN;
> + }
> + pep->rx_resource_err = 0;
> + err = rxq_init(dev);
> + if (err != 0)
> + goto out_free_irq;
> + err = txq_init(dev);
> + if (err != 0)
> + goto out_free_rx_skb;
> + pep->rx_used_desc_q = 0;
> + pep->rx_curr_desc_q = 0;
> +
> + /* Fill RX ring with skb's */
> + rxq_refill(dev);
> + pep->rx_used_desc_q = 0;
> + pep->rx_curr_desc_q = 0;
> + netif_carrier_off(dev);
> + eth_port_start(dev);
> + napi_enable(&pep->napi);
> + return 0;
> +out_free_rx_skb:
> + rxq_deinit(dev);
> +out_free_irq:
> + free_irq(dev->irq, dev);
> + return err;
> +}
> +
> +static int pxa168_eth_stop(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + eth_port_reset(dev);
> +
> + /* Disable interrupts */
> + wrl(pep, INT_MASK, 0);
> + wrl(pep, INT_CAUSE, 0);
> + /* Write to ICR to clear interrupts. */
> + wrl(pep, INT_W_CLEAR, 0);
> + napi_disable(&pep->napi);
> + del_timer_sync(&pep->timeout);
> + netif_carrier_off(dev);
> + free_irq(dev->irq, dev);
> + rxq_deinit(dev);
> + txq_deinit(dev);
> + return 0;
> +}
> +
> +static int pxa168_eth_change_mtu(struct net_device *dev, int mtu)
> +{
> + if ((mtu > ETH_DATA_LEN) || (mtu < 64))
> + return -EINVAL;
> + dev->mtu = mtu;
> + return 0;
> +}
> +
> +static int eth_alloc_tx_desc_index(struct pxa168_eth_private *pep)
> +{
> + int tx_desc_curr;
> +
> + tx_desc_curr = pep->tx_curr_desc_q;
> + pep->tx_curr_desc_q = (tx_desc_curr + 1) % pep->tx_ring_size;
> + BUG_ON(pep->tx_curr_desc_q == pep->tx_used_desc_q);
> + pep->tx_desc_count++;
> + return tx_desc_curr;
> +}
> +
> +static void eth_tx_submit_descs_for_skb(struct pxa168_eth_private *pep,
> + struct sk_buff *skb)
> +{
> + int tx_index;
> + struct tx_desc *desc;
> + int length;
> +
> + tx_index = eth_alloc_tx_desc_index(pep);
> + desc = &pep->p_tx_desc_area[tx_index];
> + length = skb->len;
> + pep->tx_skb[tx_index] = skb;
> + desc->byte_cnt = length;
> + desc->buf_ptr = dma_map_single(NULL, skb->data, length,
> DMA_TO_DEVICE);
> + wmb();
> + desc->cmd_sts = BUF_OWNED_BY_DMA | TX_GEN_CRC | TX_FIRST_DESC |
> + TX_ZERO_PADDING | TX_LAST_DESC;
> + if (unlikely(!(pep->tx_desc_count % TX_DONE_INTERVAL)))
> + desc->cmd_sts |= TX_EN_INT;
> + wmb();
> + wrl(pep, SDMA_CMD, SDMA_CMD_TXDH | SDMA_CMD_ERD);
> +}
> +
> +static int pxa168_rx_poll(struct napi_struct *napi, int budget)
> +{
> + struct pxa168_eth_private *pep =
> + container_of(napi, struct pxa168_eth_private, napi);
> + struct net_device *dev = pep->dev;
> + int work_done = 0;
> +
> + if (unlikely(pep->work_todo & WORK_LINK)) {
> + pep->work_todo &= ~(WORK_LINK);
> + handle_link_event(pep);
> + }
> + /*
> + * We call txq_reclaim every time since in NAPI interupts are
> disabled
> + * and due to this we miss the TX_DONE interrupt,which is not
> updated in
> + * interrupt status register.
> + */
> + txq_reclaim(dev, 0);
> + if (netif_queue_stopped(dev)
> + && pep->tx_ring_size - pep->tx_desc_count > 1) {
> + netif_wake_queue(dev);
> + }
> + work_done = rxq_process(dev, budget);
> + if (work_done < budget) {
> + napi_complete(napi);
> + wrl(pep, INT_MASK, ALL_INTS);
> + }
> + return work_done;
> +}
> +
> +static int pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device
> *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct net_device_stats *stats = &dev->stats;
> +
> + eth_tx_submit_descs_for_skb(pep, skb);
> + stats->tx_bytes += skb->len;
> + stats->tx_packets++;
> + dev->trans_start = jiffies;
> + if (pep->tx_ring_size - pep->tx_desc_count <= 1) {
> + /* We handled the current skb, but now we are out of space.*/
> + netif_stop_queue(dev);
> + }
> + return NETDEV_TX_OK;
> +}
> +
> +static int pxa168_smi_read(struct mii_bus *bus, int phy_addr, int regnum)
> +{
> + int val;
> + struct pxa168_eth_private *pep = bus->priv;
> + int i = 0;
> +
> + /* wait for the SMI register to become available */
> + for (i = 0; (val = rdl(pep, SMI)) & SMI_BUSY; i++) {
> + if (i == PHY_WAIT_ITERATIONS) {
> + printk(KERN_ERR
> + "pxa168 PHY timeout, val=0x%x\n", val);
> + return -ETIMEDOUT;
> + }
> + udelay(1);
> + }
> + wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) | SMI_OP_R);
> + /* now wait for the data to be valid */
> + for (i = 0; !((val = rdl(pep, SMI)) & SMI_R_VALID); i++) {
> + if (i == PHY_WAIT_ITERATIONS) {
> + printk(KERN_ERR
> + "pxa168 PHY RD timeout, val=0x%x\n", val);
> + return -ETIMEDOUT;
> + }
> + udelay(1);
> + }
> + return val & 0xffff;
> +}
> +
> +static int pxa168_smi_write(struct mii_bus *bus, int phy_addr, int regnum,
> + u16 value)
> +{
> + struct pxa168_eth_private *pep = bus->priv;
> + int i;
> +
> + /* wait for the SMI register to become available */
> + for (i = 0; rdl(pep, SMI) & SMI_BUSY; i++) {
> + if (i == PHY_WAIT_ITERATIONS) {
> + printk(KERN_ERR "pxa168 PHY busy timeout.\n");
> + return -ETIMEDOUT;
> + }
> + udelay(1);
> + }
> + wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) |
> + SMI_OP_W | (value & 0xffff));
> +
> + return 0;
> +}
> +
> +static int pxa168_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr,
> + int cmd)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + if (pep->phy != NULL)
> + return phy_mii_ioctl(pep->phy, if_mii(ifr), cmd);
> + return -EOPNOTSUPP;
> +}
> +
> +static struct phy_device *phy_scan(struct pxa168_eth_private *pep, int
> phy_addr)
> +{
> + struct mii_bus *bus = pep->smi_bus;
> + struct phy_device *phydev;
> + int start;
> + int num;
> + int i;
> +
> + if (phy_addr == PXA168_ETH_PHY_ADDR_DEFAULT) {
> + /* Scan entire range */
> + start = ethernet_phy_get(pep);
> + num = 32;
> + } else {
> + /* Use phy addr specific to platform */
> + start = phy_addr & 0x1f;
> + num = 1;
> + }
> + phydev = NULL;
> + for (i = 0; i < num; i++) {
> + int addr = (start + i) & 0x1f;
> + if (bus->phy_map[addr] == NULL)
> + mdiobus_scan(bus, addr);
> +
> + if (phydev == NULL) {
> + phydev = bus->phy_map[addr];
> + if (phydev != NULL)
> + ethernet_phy_set_addr(pep, addr);
> + }
> + }
> +
> + return phydev;
> +}
> +
> +static void phy_init(struct pxa168_eth_private *pep, int speed, int
> duplex)
> +{
> + struct phy_device *phy = pep->phy;
> + ethernet_phy_reset(pep);
> +
> + phy_attach(pep->dev, dev_name(&phy->dev), 0,
> PHY_INTERFACE_MODE_MII);
> +
> + if (speed == 0) {
> + phy->autoneg = AUTONEG_ENABLE;
> + phy->speed = 0;
> + phy->duplex = 0;
> + phy->supported &= PHY_BASIC_FEATURES;
> + phy->advertising = phy->supported | ADVERTISED_Autoneg;
> + } else {
> + phy->autoneg = AUTONEG_DISABLE;
> + phy->advertising = 0;
> + phy->speed = speed;
> + phy->duplex = duplex;
> + }
> + phy_start_aneg(phy);
> +}
> +
> +static int ethernet_phy_setup(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + if (pep->pd != NULL) {
> + if (pep->pd->init)
> + pep->pd->init();
> + }
> + pep->phy = phy_scan(pep, pep->pd->phy_addr & 0x1f);
> + if (pep->phy != NULL)
> + phy_init(pep, pep->pd->speed, pep->pd->duplex);
> + update_hash_table_mac_address(pep, NULL, dev->dev_addr);
> + return 0;
> +}
> +
> +static int get_random_mac_addr(struct net_device *dev)
> +{
> + printk(KERN_INFO "%s:Using random mac address\n", dev->name);
> + random_ether_addr(dev->dev_addr);
> + return 0;
> +}
> +
> +static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd
> *cmd)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int err;
> +
> + err = phy_read_status(pep->phy);
> + if (err == 0)
> + err = phy_ethtool_gset(pep->phy, cmd);
> + return err;
> +}
> +
> +static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd
> *cmd)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + return phy_ethtool_sset(pep->phy, cmd);
> +}
> +
> +static void pxa168_get_drvinfo(struct net_device *dev,
> + struct ethtool_drvinfo *info)
> +{
> + strncpy(info->driver, DRIVER_NAME, 32);
> + strncpy(info->version, DRIVER_VERSION, 32);
> + strncpy(info->fw_version, "N/A", 32);
> + strncpy(info->bus_info, "N/A", 32);
> +}
> +
> +static u32 pxa168_get_link(struct net_device *dev)
> +{
> + return !!netif_carrier_ok(dev);
> +}
> +
> +static const struct ethtool_ops pxa168_ethtool_ops = {
> + .get_settings = pxa168_get_settings,
> + .set_settings = pxa168_set_settings,
> + .get_drvinfo = pxa168_get_drvinfo,
> + .get_link = pxa168_get_link,
> +};
> +
> +static const struct net_device_ops pxa168_eth_netdev_ops = {
> + .ndo_open = pxa168_eth_open,
> + .ndo_stop = pxa168_eth_stop,
> + .ndo_start_xmit = pxa168_eth_start_xmit,
> + .ndo_set_rx_mode = pxa168_eth_set_rx_mode,
> + .ndo_set_mac_address = pxa168_eth_set_mac_address,
> + .ndo_validate_addr = eth_validate_addr,
> + .ndo_do_ioctl = pxa168_eth_do_ioctl,
> + .ndo_change_mtu = pxa168_eth_change_mtu,
> + .ndo_tx_timeout = pxa168_eth_tx_timeout,
> +};
> +
> +static int pxa168_eth_probe(struct platform_device *pdev)
> +{
> + struct pxa168_eth_private *pep = NULL;
> + struct net_device *dev = NULL;
> + struct resource *res;
> + struct clk *clk;
> + int err;
> +
> + printk(KERN_NOTICE "PXA168 10/100 Ethernet Driver\n");
> +
> + clk = clk_get(&pdev->dev, "MFUCLK");
> + if (IS_ERR(clk)) {
> + printk(KERN_ERR "fast Ethernet failed to get clock\n");
> + return -ENODEV;
> + }
> + clk_enable(clk);
> +
> + dev = alloc_etherdev(sizeof(struct pxa168_eth_private));
> + if (!dev) {
> + err = -ENOMEM;
> + goto out;
> + }
> +
> + platform_set_drvdata(pdev, dev);
> + pep = netdev_priv(dev);
> + pep->dev = dev;
> + pep->clk = clk;
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (res == NULL) {
> + err = -ENODEV;
> + goto out;
> + }
> + pep->base = ioremap(res->start, res->end - res->start + 1);
> + if (pep->base == NULL) {
> + err = -ENOMEM;
> + goto out;
> + }
> + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + BUG_ON(!res);
> + dev->irq = res->start;
> + dev->netdev_ops = &pxa168_eth_netdev_ops;
> + dev->watchdog_timeo = 8 * HZ;
> + dev->base_addr = 0;
> + SET_ETHTOOL_OPS(dev, &pxa168_ethtool_ops);
> +
> + INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task);
> +
> + pep->rx_ring_size = NUM_RX_DESCS;
> + pep->tx_ring_size = NUM_TX_DESCS;
> + get_random_mac_addr(dev);
> + pep->pd = pdev->dev.platform_data;
> + netif_napi_add(dev, &pep->napi, pxa168_rx_poll, pep->rx_ring_size);
> +
> + memset(&pep->timeout, 0, sizeof(struct timer_list));
> + init_timer(&pep->timeout);
> + pep->timeout.function = rxq_refill_timer_wrapper;
> + pep->timeout.data = (unsigned long)pep;
> +
> + pep->smi_bus = mdiobus_alloc();
> + if (pep->smi_bus == NULL) {
> + err = -ENOMEM;
> + goto out;
> + }
> + pep->smi_bus->priv = pep;
> + pep->smi_bus->name = "pxa168_eth smi";
> + pep->smi_bus->read = pxa168_smi_read;
> + pep->smi_bus->write = pxa168_smi_write;
> + snprintf(pep->smi_bus->id, MII_BUS_ID_SIZE, "%d", pdev->id);
> + pep->smi_bus->parent = &pdev->dev;
> + pep->smi_bus->phy_mask = 0xffffffff;
> + if (mdiobus_register(pep->smi_bus) < 0) {
> + err = -ENOMEM;
> + goto out;
> + }
> + pxa168_init_hw(pep);
> + err = ethernet_phy_setup(dev);
> + if (err)
> + goto out;
> + SET_NETDEV_DEV(dev, &pdev->dev);
> + err = register_netdev(dev);
> + if (err)
> + goto out;
> + return 0;
> +out:
> + if (pep->clk) {
> + clk_disable(pep->clk);
> + clk_put(pep->clk);
> + pep->clk = NULL;
> + }
> + if (pep->base) {
> + iounmap(pep->base);
> + pep->base = NULL;
> + }
> + if (dev)
> + free_netdev(dev);
> + return err;
> +}
> +
> +static int pxa168_eth_remove(struct platform_device *pdev)
> +{
> + struct net_device *dev = platform_get_drvdata(pdev);
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + if (pep->htpr) {
> + dma_free_coherent(NULL, HASH_ADDR_TABLE_SIZE + 7,
> + pep->htpr, pep->htpr_dma);
> + pep->htpr = NULL;
> + }
> + if (pep->clk) {
> + clk_disable(pep->clk);
> + clk_put(pep->clk);
> + pep->clk = NULL;
> + }
> + if (pep->phy != NULL)
> + phy_detach(pep->phy);
> +
> + iounmap(pep->base);
> + pep->base = NULL;
> + unregister_netdev(dev);
> + flush_scheduled_work();
> + free_netdev(dev);
> + platform_set_drvdata(pdev, NULL);
> + return 0;
> +}
> +
> +static void pxa168_eth_shutdown(struct platform_device *pdev)
> +{
> + struct net_device *dev = platform_get_drvdata(pdev);
> + eth_port_reset(dev);
> +}
> +
> +#ifdef CONFIG_PM
> +static int pxa168_eth_resume(struct platform_device *pdev)
> +{
> + return -ENOSYS;
> +}
> +
> +static int pxa168_eth_suspend(struct platform_device *pdev, pm_message_t
> state)
> +{
> + return -ENOSYS;
> +}
> +
> +#else
> +#define pxa168_eth_resume NULL
> +#define pxa168_eth_suspend NULL
> +#endif
> +
> +static struct platform_driver pxa168_eth_driver = {
> + .probe = pxa168_eth_probe,
> + .remove = pxa168_eth_remove,
> + .shutdown = pxa168_eth_shutdown,
> + .resume = pxa168_eth_resume,
> + .suspend = pxa168_eth_suspend,
> + .driver = {
> + .name = DRIVER_NAME,
> + },
> +};
> +
> +static int __init pxa168_init_module(void)
> +{
> + return platform_driver_register(&pxa168_eth_driver);
> +}
> +
> +static void __exit pxa168_cleanup_module(void)
> +{
> + platform_driver_unregister(&pxa168_eth_driver);
> +}
> +
> +module_init(pxa168_init_module);
> +module_exit(pxa168_cleanup_module);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Ethernet driver for Marvell PXA168");
> +MODULE_ALIAS("platform:pxa168_eth");
> diff --git a/include/linux/pxa168_eth.h b/include/linux/pxa168_eth.h
> new file mode 100644
> index 0000000..152981c
> --- /dev/null
> +++ b/include/linux/pxa168_eth.h
> @@ -0,0 +1,20 @@
> +/*
> + *pxa168 ethernet platform device data definition file.
> + */
> +#ifndef __LINUX_PXA168_ETH_H
> +#define __LINUX_PXA168_ETH_H
> +
> +struct pxa168_eth_platform_data {
> + int phy_addr;
> +
> + /* If speed is 0, then speed and duplex are autonegotiated. */
> + int speed; /* 0, SPEED_10, SPEED_100 */
> + int duplex; /* DUPLEX_HALF or DUPLEX_FULL */
> +
> + /* init callback is used for board specific initialization
> + * e.g on Aspenite its used to initialize the PHY transceiver.
> + */
> + int (*init)(void);
> +};
> +
> +#endif /* __LINUX_PXA168_ETH_H */
> --
> 1.5.3.3
^ permalink raw reply
* RE: [PATCH] net: add Fast Ethernet driver for PXA168.
From: Sachin Sanap @ 2010-08-06 15:14 UTC (permalink / raw)
To: Lennert Buytenhek
Cc: netdev@vger.kernel.org, Ashish Karkare, Prabhanjan Sarnaik,
eric.y.miao@gmail.com, Philip Rakity, Mark Brown
In-Reply-To: <20100804065818.GI21121@mail.wantstofly.org>
Thanks Lennert,Eric for your review. Comments inline. I will resend the modified patch as a reply to this email.
>
> On Wed, Aug 04, 2010 at 04:17:50PM +0530, Sachin Sanap wrote:
>
> > This patch adds support for PXA168 Fast Ethernet on Aspenite
> > board.
>
> There's nothing Aspenite-specific in this patch (nor should there be),
> so you can leave that part out.
Removed.
>
>
> > Patch generated against Linux 2.6.35-rc5
> > commit cd5b8f8755a89a57fc8c408d284b8b613f090345
>
> This might be interesting to know but shouldn't be part of the commit
> message.
Removed.
>
>
> > diff --git a/arch/arm/mach-mmp/include/mach/pxa168_eth.h
> b/arch/arm/mach-mmp/include/mach/pxa168_eth.h
> > new file mode 100644
> > index 0000000..abfd335
> > --- /dev/null
> > +++ b/arch/arm/mach-mmp/include/mach/pxa168_eth.h
> > @@ -0,0 +1,18 @@
> > +/*
> > + *pxa168 ethernet platform device data definition file.
> > + */
> > +#ifndef __LINUX_PXA168_ETH_H
> > +#define __LINUX_PXA168_ETH_H
>
> This should just be in include/linux, IMHO, as this unit isn't only used
> in the PXA168, for one.
I have moved it to include/linux but the PXA168 name is still there.
>
>
> > +struct pxa168_eth_platform_data {
> > + int port_number;
> > + u16 phy_addr;
> > +
> > + /* If speed is 0, then speed and duplex are autonegotiated. */
> > + u32 speed; /* 0, SPEED_10, SPEED_100 */
> > + u32 duplex; /* DUPLEX_HALF or DUPLEX_FULL */
>
> phylib treats these three (phy address, speed and duplex) as ints, is
> there any reason you need these to be of different types?
Used int at all three places.
>
>
> > + int (*init)(void);
>
> What's this needed for? The name of this callback is entirely
> nondescriptive, there's no comment as to when it's called, etc.
Added a comment for it.
>
>
> > diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> > index ce2fcdd..5ebf287 100644
> > --- a/drivers/net/Kconfig
> > +++ b/drivers/net/Kconfig
> > @@ -927,6 +927,16 @@ config SMC91X
> > The module will be called smc91x. If you want to compile it as a
> > module, say M here and read
> <file:Documentation/kbuild/modules.txt>.
> >
> > +config PXA168_ETH
> > + tristate "Marvell pxa168 ethernet support"
> > + depends on MACH_ASPENITE
>
> You can have it depend on ARM or PXA168, but having it depend on support
> for one specific board support file is almost certainly wrong.
Depends on CPU_PXA168.
>
>
> > diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c
> > new file mode 100644
> > index 0000000..618e558
> > --- /dev/null
> > +++ b/drivers/net/pxa168_eth.c
> > @@ -0,0 +1,1723 @@
> > +/*
> > + * Driver for PXA168 based boards.
>
> Why not call this "PXA168 ethernet driver"?
Change done.
>
>
> > + * Based on MV643XX driver.
>
> The mv643xx ethernet driver, that is.
Change done.
>
>
> > +#define DRIVER_NAME "pxa168-mfu"
>
> mfu?
Its was for multi function unit on Aspenite, changed it to pxa168-eth
>
>
> > +/* smi register */
> > +#define SMI_BUSY (1<<28) /* 0 - Write, 1 - Read */
> > +#define SMI_R_VALID (1<<27) /* 0 - Write, 1 - Read */
> > +#define SMI_OP_W (0<<26) /* Write operation */
> > +#define SMI_OP_R (1<<26) /* Read operation */
>
> (1 << 28)
> (1 << 27)
> (0 << 26)
>
> etc (thoughout the file)
Changed.
>
>
> > +struct pxa168_private {
>
> ITYM pxa168_eth_private
Yes, changed.
>
>
> > + /*
> > + * Used in case RX Ring is empty, which can be caused when
> > + * system does not have resources (skb's)
> > + */
>
> can be caused by
> can occur when
Changed.
>
>
> > + struct timer_list timeout;
> > + struct mii_bus *smi_bus;
> > + struct phy_device *phy;
> > +
> > + /* clock */
> > + struct clk *clk;
> > + struct pxa168_eth_platform_data *pd;
> > + /*
> > + * Ethernet controller base address.
> > + */
> > + void __iomem *base;
> > +
> > + u8 *htpr; /* hash pointer */
>
> IMHO it'd look better if you'd either always put the comment on the same
> line or put the comment above the member (within the same struct).
>
> Also, 'hash pointer' as a comment isn't very helpful. Making it
> 'hardware address filter table' or so would be more descriptive.
Changed.
>
>
> > +struct addr_table_entry {
> > + u32 lo;
> > + u32 hi;
> > +};
>
> If the address filter table consists of these structures, it's
> confusing to make htpr a u8 * and then cast things around. Better make
> htpr a void * then.
Changed.
>
> Also, what about endian-cleanness? If you run the CPU core in big
> endian, will the address filter table handling logic in this driver
> still work?
Added cpu_to_le32 for the mac_high and mac_low variables before passing them to the add_del_hash_entry. I have not tested it on the Big endian but I think it should now work.
>
>
> > +static char marvell_OUI[3] = { 0x02, 0x50, 0x43 };
>
> This isn't even a marvell OUI -- 00:50:43 is a marvell OUI, and
> anything with 02: is a locally generated address.
>
> Why don't you just use random_ether_addr()?
Changed to use random_ether_addr.
>
>
> > +static inline u32 rdl(struct pxa168_private *mp, int offset)
> > +{
> > + return readl(mp->base + offset);
> > +}
>
> In mv643xx_eth, 'mp' refers to mv643xx_eth_private, it's funny that
> you've changed the name of the struct but not that of the variable.
>
> As the rest of the driver looks very very similar to mv643xx_eth, if
> not pretty much entirely identical in a lot of places, you should give
> more credit to mv643xx_eth than 'based on', in my opinion.
Changed the credit line for the same.
>
>
> > +static int ethernet_phy_get(struct pxa168_private *mp)
> > +{
> > + unsigned int reg_data;
> > +
> > + /* only support 3 ports */
> > + BUG_ON(mp->port_num > 2);
>
> Hm, does it actually support 3 ports?
No. changed the code.
>
>
> > +static void ethernet_phy_set_addr(struct pxa168_private *mp, int
> phy_addr)
> > +{
> > + u32 reg_data;
> > + int addr_shift = 5 * mp->port_num;
> > +
> > + /* only support 3 ports */
> > + BUG_ON(mp->port_num > 2);
> > +
> > + reg_data = rdl(mp, PHY_ADDRESS);
> > + reg_data &= ~(0x1f << addr_shift);
> > + reg_data |= (phy_addr & 0x1f) << addr_shift;
> > + wrl(mp, PHY_ADDRESS, reg_data);
> > +}
> > +static void ethernet_phy_reset(struct pxa168_private *mp)
> > +{
>
> Blank line between functions.
>
>
> > + do {
> > + data = phy_read(mp->phy, MII_BMCR);
> > + } while (data >= 0 && data & BMCR_RESET);
> > +
> > +}
>
> No blank line here.
>
>
> > +static void rxq_refill(struct net_device *dev)
> > +{
> > + struct pxa168_private *mp = netdev_priv(dev);
> > + struct sk_buff *skb;
> > + struct rx_desc *p_used_rx_desc;
> > + int used_rx_desc;
> > +
> > + while (mp->rx_desc_count < mp->rx_ring_size) {
> > +
> > + skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN);
>
> No blank line here.
>
>
> > + if (mp->rx_desc_count == 0) {
> > + printk(KERN_INFO "%s: Rx ring is empty\n", dev->name);
>
> I wouldn't printk for this (and certainly not without a rate limit) --
> going OOM will then just mean that you'll get endless console spam as
> well, which isn't helpful.
Hm, removed the printk.
>
>
> > +/*
> > + * --------------------------------------------------------------------
> --------
> > + * This function will calculate the hash function of the address.
> > + * depends on the hash mode and hash size.
>
> Where are the hash mode and size configured?
>
>
> > +static u32 hash_function(u32 mac_high, u32 mac_low)
> > +{
> > + u32 hash_result;
> > + u32 addr_high;
> > + u32 addr_low;
> > + u32 addr0;
> > + u32 addr1;
> > + u32 addr2;
> > + u32 addr3;
> > + u32 addr_high_swapped;
> > + u32 addr_low_swapped;
> > +
> > + addr_high = nibble_swapping_16_bit(mac_high);
> > + addr_low = nibble_swapping_32_bit(mac_low);
> > +
> > + addr_high_swapped = flip_4_bits(addr_high & 0xf)
> > + + ((flip_4_bits((addr_high >> 4) & 0xf)) << 4)
> > + + ((flip_4_bits((addr_high >> 8) & 0xf)) << 8)
> > + + ((flip_4_bits((addr_high >> 12) & 0xf)) << 12);
> > +
> > + addr_low_swapped = flip_4_bits(addr_low & 0xf)
> > + + ((flip_4_bits((addr_low >> 4) & 0xf)) << 4)
> > + + ((flip_4_bits((addr_low >> 8) & 0xf)) << 8)
> > + + ((flip_4_bits((addr_low >> 12) & 0xf)) << 12)
> > + + ((flip_4_bits((addr_low >> 16) & 0xf)) << 16)
> > + + ((flip_4_bits((addr_low >> 20) & 0xf)) << 20)
> > + + ((flip_4_bits((addr_low >> 24) & 0xf)) << 24)
> > + + ((flip_4_bits((addr_low >> 28) & 0xf)) << 28);
> > +
> > + addr_high = addr_high_swapped;
> > + addr_low = addr_low_swapped;
> > +
> > + addr0 = (addr_low >> 2) & 0x03f;
> > + addr1 = (addr_low & 0x003) | ((addr_low >> 8) & 0x7f) << 2;
> > + addr2 = (addr_low >> 15) & 0x1ff;
> > + addr3 = ((addr_low >> 24) & 0x0ff) | ((addr_high & 1) << 8);
> > +
> > + hash_result = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
> > + hash_result = hash_result & 0x07ff;
> > + return hash_result;
>
> As it's only a bunch of XORs, can you not calculate the hash function
> first and only then do the bit reversal?
>
>
> > + * --------------------------------------------------------------------
> --------
> > + * This function will add an entry to the address table.
> > + * depends on the hash mode and hash size that was initialized.
> > + * Inputs
> > + * mp - ETHERNET .
> > + * mac_high - the 2 most significant bytes of the MAC address.
> > + * mac_low - the 4 least significant bytes of the MAC address.
> > + * skip - if 1, skip this address.
>
> What does 'skip' do?
>
>
> > + * rd - the RD field in the address table.
>
> And what does that do?
Added more comments regarding the address filtering and the hash function.
>
> If one has hardware docs, this comment doesn't really add anything,
> while if one doesn't have hardware docs, this comment doesn't really
> explain anything -- so the comment isn't very useful.
>
>
> > +static void update_hash_table_mac_address(struct pxa168_private *mp,
> > + u8 *oaddr, u8 *addr)
> > [...]
> > +static int init_hashtable(struct pxa168_private *mp)
>
> hash_table?
>
>
> > +{
> > + u8 *addr;
> > + dma_addr_t reg_dma;
> > +
> > + if (mp->htpr == NULL) {
> > + mp->htpr = dma_alloc_coherent(NULL,
> > + HASH_ADDR_TABLE_SIZE + 7,
> > + &mp->htpr_dma, GFP_KERNEL);
> > + if (mp->htpr == NULL)
> > + return -ENOMEM;
> > + }
> > +
> > + /* align to 8 byte boundary */
> > + addr = (u8 *) (((u32) mp->htpr + 7) & ~0x7);
> > + reg_dma = (dma_addr_t) (((u32) mp->htpr_dma + 7) & ~0x7);
>
> DMA-API-HOWTO.txt says about dma_alloc_coherent():
>
> The cpu return address and the DMA bus master address are both
> guaranteed to be aligned to the smallest PAGE_SIZE order which
> is greater than or equal to the requested size.
>
> So this shuffling isn't needed.
Hm, removed the shuffling code.
>
>
> etc etc.
^ permalink raw reply
* Re: Using virtio as a physical (wire-level) transport
From: Ira W. Snyder @ 2010-08-06 15:34 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: Rusty Russell, virtualization, Zang Roy, netdev
In-Reply-To: <20100805232042.GA27651@redhat.com>
On Fri, Aug 06, 2010 at 02:20:42AM +0300, Michael S. Tsirkin wrote:
> On Thu, Aug 05, 2010 at 04:01:03PM -0700, Ira W. Snyder wrote:
> > On Fri, Aug 06, 2010 at 12:30:50AM +0300, Michael S. Tsirkin wrote:
> > > Hi Ira,
> > >
> > > > Making my life harder since the last time I tried this, mainline commit
> > > > 7c5e9ed0c (virtio_ring: remove a level of indirection) has removed the
> > > > possibility of using an alternative virtqueue implementation. The commit
> > > > message suggests that you might be willing to add this capability back.
> > > > Would this be an option?
> > >
> > > Sorry about that.
> > >
> > > With respect to this commit, we only had one implementation upstream
> > > and extra levels of indirection made extending the API
> > > much harder for no apparent benefit.
> > >
> > > When there's more than one ring implementation with very small amount of
> > > common code, I think that it might make sense to readd the indirection
> > > back, to separate the code cleanly.
> > >
> > > OTOH if the two implementations share a lot of code, I think that it
> > > might be better to just add a couple of if statements here and there.
> > > This way compiler even might have a chance to compile the code out if
> > > the feature is disabled in kernel config.
> > >
> >
> > The virtqueue implementation I envision will be almost identical to the
> > current virtio_ring virtqueue implementation, with the following
> > exceptions:
> >
> > * the "shared memory" will actually be remote, on the PCI BAR of a device
> > * iowrite32(), ioread32() and friends will be needed to access the memory
> > * there will only be a fixed number of virtqueues available, due to PCI
> > BAR size
> > * cross-endian virtqueues must work
> > * kick needs to be cross-machine (using PCI IRQ's)
> >
> > I don't think it is feasible to add this to the existing implementation.
> > I think the requirement of being cross-endian will be the hardest to
> > overcome. Rusty did not envision the cross-endian use case when he
> > designed this, and it shows, in virtio_ring, virtio_net and vhost. I
> > have no idea what to do about this. Do you have any ideas?
>
> My guess is sticking an if around each access in virtio would hurt,
> if this is what you are asking about.
>
Yes, I think so too. I think using le32 byte order everywhere in virtio
would be a good thing. In addition, it means that on all x86, things
continue to work as-is. It would also have no overhead in the most
common case: x86-on-x86.
This problem is not limited to my new use of virtio. Virtio is
completely useless in a relatively common virtualization scenario:
x86 host with qemu-ppc guest. Or any other big endian guest system.
> Just a crazy idea: vhost already uses wrappers like get_user etc,
> maybe when building kernel for your board you could
> redefine these to also byteswap?
>
I think idea is clever, but also psychotic :) I'm sure it would work,
but that only solves the problem of virtio ring descriptors. The
virtio-net header contains several __u16 fields which would also need
to be fixed-endianness.
Thanks,
Ira
^ permalink raw reply
* RE: [PATCH] net: add Fast Ethernet driver for PXA168.
From: Sachin Sanap @ 2010-08-06 15:18 UTC (permalink / raw)
To: 20100804104750.GA9652@marvell.com, buytenh@wantstofly.org
Cc: netdev@vger.kernel.org, Ashish Karkare, Prabhanjan Sarnaik,
eric.y.miao@gmail.com, Philip Rakity, Mark Brown
In-Reply-To: <20100806193931.GA428@pe-dt013.marvell.com>
Sorry for resending the patch as new thread. Discard this patch. I will send the patch again as a reply to previous thread.
-Sachin
> -----Original Message-----
> From: Sachin Sanap [mailto:ssanap@marvell.com]
> Sent: Saturday, August 07, 2010 1:10 AM
> To: buytenh@wantstofly.org
> Cc: netdev@vger.kernel.org; Ashish Karkare; Prabhanjan Sarnaik;
> eric.y.miao@gmail.com; Philip Rakity; Mark Brown; Sachin Sanap
> Subject: [PATCH] net: add Fast Ethernet driver for PXA168.
>
> Signed-off-by: Sachin Sanap <ssanap@marvell.com>
> ---
> arch/arm/mach-mmp/aspenite.c | 3 +-
> arch/arm/mach-mmp/pxa168.c | 4 +-
> drivers/net/Kconfig | 10 +
> drivers/net/Makefile | 1 +
> drivers/net/pxa168_eth.c | 1592
> ++++++++++++++++++++++++++++++++++++++++++
> include/linux/pxa168_eth.h | 20 +
> 6 files changed, 1626 insertions(+), 4 deletions(-)
> create mode 100644 drivers/net/pxa168_eth.c
> create mode 100644 include/linux/pxa168_eth.h
>
> diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c
> index 61a6d6a..cec505f 100644
> --- a/arch/arm/mach-mmp/aspenite.c
> +++ b/arch/arm/mach-mmp/aspenite.c
> @@ -200,8 +200,7 @@ static int pxa168_eth_init(void)
> }
>
> static struct pxa168_eth_platform_data pxa168_eth_data = {
> - .phy_addr = 0, /* phy addr depends on boards */
> - .port_number = 0,
> + .phy_addr = 0,
> .init = pxa168_eth_init,
> };
> #endif
> diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c
> index f7d1158..6fcaf0c 100644
> --- a/arch/arm/mach-mmp/pxa168.c
> +++ b/arch/arm/mach-mmp/pxa168.c
> @@ -97,7 +97,7 @@ static struct clk_lookup pxa168_clkregs[] = {
> INIT_CLKREG(&clk_ssp4, "pxa168-ssp.3", NULL),
> INIT_CLKREG(&clk_ssp5, "pxa168-ssp.4", NULL),
> INIT_CLKREG(&clk_nand, "pxa3xx-nand", NULL),
> - INIT_CLKREG(&clk_mfu, "pxa168-mfu", "MFUCLK"),
> + INIT_CLKREG(&clk_mfu, "pxa168-eth", "MFUCLK"),
> };
>
> static int __init pxa168_init(void)
> @@ -149,4 +149,4 @@ PXA168_DEVICE(ssp2, "pxa168-ssp", 1, SSP2, 0xd401c000,
> 0x40, 54, 55);
> PXA168_DEVICE(ssp3, "pxa168-ssp", 2, SSP3, 0xd401f000, 0x40, 56, 57);
> PXA168_DEVICE(ssp4, "pxa168-ssp", 3, SSP4, 0xd4020000, 0x40, 58, 59);
> PXA168_DEVICE(ssp5, "pxa168-ssp", 4, SSP5, 0xd4021000, 0x40, 60, 61);
> -PXA168_DEVICE(mfu, "pxa168-mfu", -1, MFU, 0xc0800000, 0x0fff);
> +PXA168_DEVICE(mfu, "pxa168-eth", -1, MFU, 0xc0800000, 0x0fff);
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index ce2fcdd..78cd7e8 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -927,6 +927,16 @@ config SMC91X
> The module will be called smc91x. If you want to compile it as a
> module, say M here and read
> <file:Documentation/kbuild/modules.txt>.
>
> +config PXA168_ETH
> + tristate "Marvell pxa168 ethernet support"
> + depends on CPU_PXA168
> + select PHYLIB
> + help
> + This driver supports the pxa168 Ethernet ports.
> +
> + To compile this driver as a module, choose M here. The module
> + will be called pxa168_eth.
> +
> config NET_NETX
> tristate "NetX Ethernet support"
> select MII
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index 0a0512a..a42d437 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -244,6 +244,7 @@ obj-$(CONFIG_MYRI10GE) += myri10ge/
> obj-$(CONFIG_SMC91X) += smc91x.o
> obj-$(CONFIG_SMC911X) += smc911x.o
> obj-$(CONFIG_SMSC911X) += smsc911x.o
> +obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
> obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
> obj-$(CONFIG_DM9000) += dm9000.o
> obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o
> diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c
> new file mode 100644
> index 0000000..c00fc3c
> --- /dev/null
> +++ b/drivers/net/pxa168_eth.c
> @@ -0,0 +1,1592 @@
> +/*
> + * PXA168 ethernet driver.
> + * Most of the code is derived from mv643xx ethernet driver.
> + *
> + * Copyright (C) 2010 Marvell International Ltd.
> + * Philip Rakity <prakity@marvell.com>
> + * Mark Brown <markb@marvell.com>
> + * Sachin Sanap <ssanap@marvell.com>
> + *
> + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
> USA.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/in.h>
> +#include <linux/ip.h>
> +#include <linux/tcp.h>
> +#include <linux/udp.h>
> +#include <linux/etherdevice.h>
> +#include <linux/bitops.h>
> +#include <linux/delay.h>
> +#include <linux/ethtool.h>
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/workqueue.h>
> +#include <linux/clk.h>
> +#include <linux/phy.h>
> +#include <linux/io.h>
> +#include <linux/types.h>
> +#include <asm/pgtable.h>
> +#include <asm/system.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <asm/cacheflush.h>
> +#include <linux/pxa168_eth.h>
> +
> +#define DRIVER_NAME "pxa168-eth"
> +#define DRIVER_VERSION "0.3"
> +
> +/*
> + * Registers
> + */
> +
> +#define PHY_ADDRESS 0x0000
> +#define SMI 0x0010
> +#define PORT_CONFIG 0x0400
> +#define PORT_CONFIG_EXT 0x0408
> +#define PORT_COMMAND 0x0410
> +#define PORT_STATUS 0x0418
> +#define HTPR 0x0428
> +#define SDMA_CONFIG 0x0440
> +#define SDMA_CMD 0x0448
> +#define INT_CAUSE 0x0450
> +#define INT_W_CLEAR 0x0454
> +#define INT_MASK 0x0458
> +#define ETH_F_RX_DESC_0 0x0480
> +#define ETH_C_RX_DESC_0 0x04A0
> +#define ETH_C_TX_DESC_1 0x04E4
> +
> +/* smi register */
> +#define SMI_BUSY (1 << 28) /* 0 - Write, 1 - Read */
> +#define SMI_R_VALID (1 << 27) /* 0 - Write, 1 - Read */
> +#define SMI_OP_W (0 << 26) /* Write operation */
> +#define SMI_OP_R (1 << 26) /* Read operation */
> +
> +#define PHY_WAIT_ITERATIONS 500
> +
> +#define PXA168_ETH_PHY_ADDR_DEFAULT 0
> +/* RX & TX descriptor command */
> +#define BUF_OWNED_BY_DMA (1 << 31)
> +
> +/* RX descriptor status */
> +#define RX_EN_INT (1 << 23)
> +#define RX_FIRST_DESC (1 << 17)
> +#define RX_LAST_DESC (1 << 16)
> +#define RX_ERROR (1 << 15)
> +
> +/* TX descriptor command */
> +#define TX_EN_INT (1 << 23)
> +#define TX_GEN_CRC (1 << 22)
> +#define TX_ZERO_PADDING (1 << 18)
> +#define TX_FIRST_DESC (1 << 17)
> +#define TX_LAST_DESC (1 << 16)
> +#define TX_ERROR (1 << 15)
> +
> +/* SDMA_CMD */
> +#define SDMA_CMD_AT (1 << 31)
> +#define SDMA_CMD_TXDL (1 << 24)
> +#define SDMA_CMD_TXDH (1 << 23)
> +#define SDMA_CMD_AR (1 << 15)
> +#define SDMA_CMD_ERD (1 << 7)
> +
> +/* Bit definitions of the Port Config Reg */
> +#define PCR_HS (1 << 12)
> +#define PCR_EN (1 << 7)
> +#define PCR_PM (1 << 0)
> +
> +/* Bit definitions of the Port Config Extend Reg */
> +#define PCXR_2BSM (1 << 28)
> +#define PCXR_DSCP_EN (1 << 21)
> +#define PCXR_MFL_1518 (0 << 14)
> +#define PCXR_MFL_1536 (1 << 14)
> +#define PCXR_MFL_2048 (2 << 14)
> +#define PCXR_MFL_64K (3 << 14)
> +#define PCXR_FLP (1 << 11)
> +#define PCXR_PRIO_TX_OFF 3
> +#define PCXR_TX_HIGH_PRI (7 << PCXR_PRIO_TX_OFF)
> +
> +/* Bit definitions of the SDMA Config Reg */
> +#define SDCR_BSZ_OFF 12
> +#define SDCR_BSZ8 (3 << SDCR_BSZ_OFF)
> +#define SDCR_BSZ4 (2 << SDCR_BSZ_OFF)
> +#define SDCR_BSZ2 (1 << SDCR_BSZ_OFF)
> +#define SDCR_BSZ1 (0 << SDCR_BSZ_OFF)
> +#define SDCR_BLMR (1 << 6)
> +#define SDCR_BLMT (1 << 7)
> +#define SDCR_RIFB (1 << 9)
> +#define SDCR_RC_OFF 2
> +#define SDCR_RC_MAX_RETRANS (0xf << SDCR_RC_OFF)
> +
> +/*
> + * Bit definitions of the Interrupt Cause Reg
> + * and Interrupt MASK Reg is the same
> + */
> +#define ICR_RXBUF (1 << 0)
> +#define ICR_TXBUF_H (1 << 2)
> +#define ICR_TXBUF_L (1 << 3)
> +#define ICR_TXEND_H (1 << 6)
> +#define ICR_TXEND_L (1 << 7)
> +#define ICR_RXERR (1 << 8)
> +#define ICR_TXERR_H (1 << 10)
> +#define ICR_TXERR_L (1 << 11)
> +#define ICR_TX_UDR (1 << 13)
> +#define ICR_MII_CH (1 << 28)
> +
> +#define ALL_INTS (ICR_TXBUF_H | ICR_TXBUF_L | ICR_TX_UDR |\
> + ICR_TXERR_H | ICR_TXERR_L |\
> + ICR_TXEND_H | ICR_TXEND_L |\
> + ICR_RXBUF | ICR_RXERR | ICR_MII_CH)
> +
> +#define ETH_HW_IP_ALIGN 2 /* hw aligns IP header */
> +#define ETH_EXTRA_HEADER (6+6+2+4) /* dest+src addr+protocol id+crc
> */
> +#define ETH_DATA_LEN 1500
> +#define MAX_PKT_SIZE 1518
> +
> +#define NUM_RX_DESCS 64
> +#define NUM_TX_DESCS 64
> +#define MAX_DESCS_PER_HIGH (60)
> +#define TX_DESC_COUNT_LOW (10)
> +
> +#define HASH_ADD 0
> +#define HASH_DELETE 1
> +#define HASH_ADDR_TABLE_SIZE 0x4000 /* 16K (1/2K address - PCR_HS ==
> 1) */
> +#define HOP_NUMBER 12
> +
> +/* Bit definitions for Port status */
> +#define PORT_SPEED_100 (1 << 0)
> +#define FULL_DUPLEX (1 << 1)
> +#define FLOW_CONTROL_ENABLED (1 << 2)
> +#define LINK_UP (1 << 3)
> +
> +/* Bit definitions for work to be done */
> +#define WORK_LINK (1 << 0)
> +#define WORK_TX_DONE (1 << 1)
> +
> +#define TX_DONE_INTERVAL 30
> +
> +struct rx_desc {
> + u32 cmd_sts; /* Descriptor command status */
> + u16 byte_cnt; /* Descriptor buffer byte count */
> + u16 buf_size; /* Buffer size */
> + u32 buf_ptr; /* Descriptor buffer pointer */
> + u32 next_desc_ptr; /* Next descriptor pointer */
> +};
> +
> +struct tx_desc {
> + u32 cmd_sts; /* Command/status field */
> + u16 reserved;
> + u16 byte_cnt; /* buffer byte count */
> + u32 buf_ptr; /* pointer to buffer for this descriptor */
> + u32 next_desc_ptr; /* Pointer to next descriptor */
> +};
> +
> +struct pxa168_eth_private {
> + int rx_resource_err; /* Rx ring resource error flag */
> +
> + /* Next available and first returning Rx resource */
> + int rx_curr_desc_q, rx_used_desc_q;
> +
> + /* Next available and first returning Tx resource */
> + int tx_curr_desc_q, tx_used_desc_q;
> +
> + struct rx_desc *p_rx_desc_area;
> + dma_addr_t rx_desc_dma;
> + int rx_desc_area_size;
> + struct sk_buff **rx_skb;
> +
> + struct tx_desc *p_tx_desc_area;
> + dma_addr_t tx_desc_dma;
> + int tx_desc_area_size;
> + struct sk_buff **tx_skb;
> +
> + struct work_struct tx_timeout_task;
> +
> + struct net_device *dev;
> + struct napi_struct napi;
> + u8 work_todo;
> +
> + struct net_device_stats stats;
> + /* Size of Tx Ring per queue */
> + int tx_ring_size;
> + /* Number of tx descriptors in use */
> + int tx_desc_count;
> + /* Size of Rx Ring per queue */
> + int rx_ring_size;
> + /* Number of rx descriptors in use */
> + int rx_desc_count;
> +
> + /*
> + * Used in case RX Ring is empty, which can occur when
> + * system does not have resources (skb's)
> + */
> + struct timer_list timeout;
> + struct mii_bus *smi_bus;
> + struct phy_device *phy;
> +
> + /* clock */
> + struct clk *clk;
> + struct pxa168_eth_platform_data *pd;
> + /*
> + * Ethernet controller base address.
> + */
> + void __iomem *base;
> +
> + /* Pointer to the hardware address filter table */
> + void *htpr;
> + dma_addr_t htpr_dma;
> +};
> +
> +struct addr_table_entry {
> + u32 lo;
> + u32 hi;
> +};
> +
> +/* Bit fields of a Hash Table Entry */
> +enum hash_table_entry {
> + HASH_ENTRY_VALID = 1,
> + SKIP = 2,
> + HASH_ENTRY_RECEIVE_DISCARD = 4,
> + HASH_ENTRY_RECEIVE_DISCARD_BIT = 2
> +};
> +
> +static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd
> *cmd);
> +static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd
> *cmd);
> +static int pxa168_init_hw(struct pxa168_eth_private *pep);
> +static void eth_port_reset(struct net_device *dev);
> +static void eth_port_start(struct net_device *dev);
> +static int pxa168_eth_open(struct net_device *dev);
> +static int pxa168_eth_stop(struct net_device *dev);
> +static int ethernet_phy_setup(struct net_device *dev);
> +
> +static inline u32 rdl(struct pxa168_eth_private *pep, int offset)
> +{
> + return readl(pep->base + offset);
> +}
> +
> +static inline void wrl(struct pxa168_eth_private *pep, int offset, u32
> data)
> +{
> + writel(data, pep->base + offset);
> +}
> +
> +static void abort_dma(struct pxa168_eth_private *pep)
> +{
> + int delay;
> + int max_retries = 40;
> +
> + do {
> + wrl(pep, SDMA_CMD, SDMA_CMD_AR | SDMA_CMD_AT);
> + udelay(100);
> +
> + delay = 10;
> + while ((rdl(pep, SDMA_CMD) & (SDMA_CMD_AR | SDMA_CMD_AT))
> + && delay-- > 0) {
> + udelay(10);
> + }
> + } while (max_retries-- > 0 && delay <= 0);
> +
> + if (max_retries <= 0)
> + printk(KERN_ERR "%s : DMA Stuck\n", __func__);
> +}
> +
> +static int ethernet_phy_get(struct pxa168_eth_private *pep)
> +{
> + unsigned int reg_data;
> +
> + reg_data = rdl(pep, PHY_ADDRESS);
> +
> + return reg_data & 0x1f;
> +}
> +
> +static void ethernet_phy_set_addr(struct pxa168_eth_private *pep, int
> phy_addr)
> +{
> + u32 reg_data;
> +
> + reg_data = rdl(pep, PHY_ADDRESS);
> + reg_data &= ~(0x1f);
> + reg_data |= phy_addr & 0x1f;
> + wrl(pep, PHY_ADDRESS, reg_data);
> +}
> +
> +static void ethernet_phy_reset(struct pxa168_eth_private *pep)
> +{
> + int data;
> +
> + data = phy_read(pep->phy, MII_BMCR);
> + if (data < 0)
> + return;
> +
> + data |= BMCR_RESET;
> + if (phy_write(pep->phy, MII_BMCR, data) < 0)
> + return;
> +
> + do {
> + data = phy_read(pep->phy, MII_BMCR);
> + } while (data >= 0 && data & BMCR_RESET);
> +}
> +
> +static void rxq_refill(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct sk_buff *skb;
> + struct rx_desc *p_used_rx_desc;
> + int used_rx_desc;
> +
> + while (pep->rx_desc_count < pep->rx_ring_size) {
> + skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN);
> + if (!skb)
> + break;
> + pep->rx_desc_count++;
> + /* Get 'used' Rx descriptor */
> + used_rx_desc = pep->rx_used_desc_q;
> + p_used_rx_desc = &pep->p_rx_desc_area[used_rx_desc];
> + p_used_rx_desc->buf_ptr = dma_map_single(NULL,
> + skb->data,
> + MAX_PKT_SIZE +
> + ETH_HW_IP_ALIGN,
> + DMA_FROM_DEVICE);
> + p_used_rx_desc->buf_size = MAX_PKT_SIZE + ETH_HW_IP_ALIGN;
> + pep->rx_skb[used_rx_desc] = skb;
> +
> + /* Return the descriptor to DMA ownership */
> + wmb();
> + p_used_rx_desc->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT;
> + wmb();
> +
> + /* Move the used descriptor pointer to the next descriptor */
> + pep->rx_used_desc_q = (used_rx_desc + 1) % pep->rx_ring_size;
> +
> + /* Any Rx return cancels the Rx resource error status */
> + pep->rx_resource_err = 0;
> +
> + skb_reserve(skb, ETH_HW_IP_ALIGN);
> + }
> +
> + /*
> + * If RX ring is empty of SKB, set a timer to try allocating
> + * again at a later time.
> + */
> + if (pep->rx_desc_count == 0) {
> + pep->timeout.expires = jiffies + (HZ / 10); /* 100 mSec */
> + add_timer(&pep->timeout);
> + }
> +}
> +
> +static inline void rxq_refill_timer_wrapper(unsigned long data)
> +{
> + struct pxa168_eth_private *pep = (void *)data;
> + napi_schedule(&pep->napi);
> +}
> +
> +static inline u32 nibble_swapping_32_bit(u32 x)
> +{
> + return (((x) & 0xf0f0f0f0) >> 4) | (((x) & 0x0f0f0f0f) << 4);
> +}
> +
> +static inline u32 nibble_swapping_16_bit(u32 x)
> +{
> + return (((x) & 0x0000f0f0) >> 4) | (((x) & 0x00000f0f) << 4);
> +}
> +
> +static inline u32 flip_4_bits(u32 x)
> +{
> + return (((x) & 0x01) << 3) | (((x) & 0x002) << 1)
> + | (((x) & 0x04) >> 1) | (((x) & 0x008) >> 3);
> +}
> +
> +/*
> + * ----------------------------------------------------------------------
> ------
> + * This function will calculate the hash function of the address.
> + * depends on the hash mode and hash size.
> + * Inputs
> + * mac_high - the 2 most significant bytes of the MAC address.
> + * mac_low - the 4 least significant bytes of the MAC address.
> + * Outputs
> + * return the calculated entry.
> + */
> +static u32 hash_function(u32 mac_high, u32 mac_low)
> +{
> + u32 hash_result;
> + u32 addr_high;
> + u32 addr_low;
> + u32 addr0;
> + u32 addr1;
> + u32 addr2;
> + u32 addr3;
> + u32 addr_high_swapped;
> + u32 addr_low_swapped;
> +
> + addr_high = nibble_swapping_16_bit(mac_high);
> + addr_low = nibble_swapping_32_bit(mac_low);
> +
> + addr_high_swapped = flip_4_bits(addr_high & 0xf)
> + + ((flip_4_bits((addr_high >> 4) & 0xf)) << 4)
> + + ((flip_4_bits((addr_high >> 8) & 0xf)) << 8)
> + + ((flip_4_bits((addr_high >> 12) & 0xf)) << 12);
> +
> + addr_low_swapped = flip_4_bits(addr_low & 0xf)
> + + ((flip_4_bits((addr_low >> 4) & 0xf)) << 4)
> + + ((flip_4_bits((addr_low >> 8) & 0xf)) << 8)
> + + ((flip_4_bits((addr_low >> 12) & 0xf)) << 12)
> + + ((flip_4_bits((addr_low >> 16) & 0xf)) << 16)
> + + ((flip_4_bits((addr_low >> 20) & 0xf)) << 20)
> + + ((flip_4_bits((addr_low >> 24) & 0xf)) << 24)
> + + ((flip_4_bits((addr_low >> 28) & 0xf)) << 28);
> +
> + addr_high = addr_high_swapped;
> + addr_low = addr_low_swapped;
> +
> + addr0 = (addr_low >> 2) & 0x03f;
> + addr1 = (addr_low & 0x003) | ((addr_low >> 8) & 0x7f) << 2;
> + addr2 = (addr_low >> 15) & 0x1ff;
> + addr3 = ((addr_low >> 24) & 0x0ff) | ((addr_high & 1) << 8);
> +
> + hash_result = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
> + hash_result = hash_result & 0x07ff;
> + return hash_result;
> +}
> +
> +/*
> + * ----------------------------------------------------------------------
> ------
> + * This function will add/del an entry to the address table.
> + * Inputs
> + * pep - ETHERNET .
> + * mac_high - the 2 most significant bytes of the MAC address.
> + * mac_low - the 4 least significant bytes of the MAC address.
> + * skip - if 1, skip this address.Used in case of deleting an entry which
> is a
> + * part of chain in the hash table.We cant just delete the entry
> since
> + * that will break the chain.We need to defragment the tables time to
> + * time.
> + * rd - 0 Discard packet upon match.
> + * - 1 Receive packet upon match.
> + * Outputs
> + * address table entry is added/deleted.
> + * 0 if success.
> + * -ENOSPC if table full
> + */
> +static int add_del_hash_entry(struct pxa168_eth_private *pep, u32
> mac_high,
> + u32 mac_low, u32 rd, u32 skip, int del)
> +{
> + struct addr_table_entry *entry, *start;
> + u32 new_high;
> + u32 new_low;
> + u32 i;
> +
> + new_low = (((mac_high >> 4) & 0xf) << 15)
> + | (((mac_high >> 0) & 0xf) << 11)
> + | (((mac_high >> 12) & 0xf) << 7)
> + | (((mac_high >> 8) & 0xf) << 3)
> + | (((mac_low >> 20) & 0x1) << 31)
> + | (((mac_low >> 16) & 0xf) << 27)
> + | (((mac_low >> 28) & 0xf) << 23)
> + | (((mac_low >> 24) & 0xf) << 19)
> + | (skip << SKIP) | (rd << HASH_ENTRY_RECEIVE_DISCARD_BIT)
> + | HASH_ENTRY_VALID;
> +
> + new_high = (((mac_low >> 4) & 0xf) << 15)
> + | (((mac_low >> 0) & 0xf) << 11)
> + | (((mac_low >> 12) & 0xf) << 7)
> + | (((mac_low >> 8) & 0xf) << 3)
> + | (((mac_low >> 21) & 0x7) << 0);
> +
> + /*
> + * Pick the appropriate table, start scanning for free/reusable
> + * entries at the index obtained by hashing the specified MAC
> address
> + */
> + start = (struct addr_table_entry *)(pep->htpr);
> + entry = start + hash_function(mac_high, mac_low);
> + for (i = 0; i < HOP_NUMBER; i++) {
> + if (!(entry->lo & HASH_ENTRY_VALID)) {
> + break;
> + } else {
> + /* if same address put in same position */
> + if (((entry->lo & 0xfffffff8) == (new_low & 0xfffffff8))
> + && (entry->hi == new_high)) {
> + break;
> + }
> + }
> + if (entry == start + 0x7ff)
> + entry = start;
> + else
> + entry++;
> + }
> +
> + if (((entry->lo & 0xfffffff8) != (new_low & 0xfffffff8)) &&
> + (entry->hi != new_high) && del)
> + return 0;
> +
> + if (i == HOP_NUMBER) {
> + if (!del) {
> + printk(KERN_INFO "%s: table section is full\n",
> + __FILE__);
> + return -ENOSPC;
> + } else
> + return 0;
> + }
> +
> + /*
> + * Update the selected entry
> + */
> + if (del) {
> + entry->hi = 0;
> + entry->lo = 0;
> + } else {
> + entry->hi = cpu_to_le32(new_high);
> + entry->lo = cpu_to_le32(new_low);
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * ----------------------------------------------------------------------
> ------
> + * Create an addressTable entry from MAC address info
> + * found in the specifed net_device struct
> + *
> + * Input : pointer to ethernet interface network device structure
> + * Output : N/A
> + */
> +static void update_hash_table_mac_address(struct pxa168_eth_private *pep,
> + u8 *oaddr, u8 *addr)
> +{
> + u32 mac_high;
> + u32 mac_low;
> +
> + /* Delete old entry */
> + if (oaddr) {
> + mac_high = cpu_to_le32((oaddr[0] << 8) | oaddr[1]);
> + mac_low = cpu_to_le32((oaddr[2] << 24) | (oaddr[3] << 16) |
> + (oaddr[4] << 8) | oaddr[5]);
> + add_del_hash_entry(pep, mac_high, mac_low, 1, 0, HASH_DELETE);
> + }
> + /* Add new entry */
> + mac_high = cpu_to_le32((addr[0] << 8) | addr[1]);
> + mac_low = cpu_to_le32((addr[2] << 24) | (addr[3] << 16) |
> + (addr[4] << 8) | addr[5]);
> + add_del_hash_entry(pep, mac_high, mac_low, 1, 0, HASH_ADD);
> +}
> +
> +static int init_hash_table(struct pxa168_eth_private *pep)
> +{
> + /*
> + * Hardware expects CPU to build a hash table based on a predefined
> + * hash function and populate it based on hardware address. The
> + * location of the hash table is identified by 32-bit pointer stored
> + * in HTPR internal register. Two possible sizes exists for the hash
> + * table 256kB and 16kB.We currently only support 16kB.
> + */
> + /* TODO: Add support for 256kB hash table */
> + if (pep->htpr == NULL) {
> + pep->htpr = dma_alloc_coherent(NULL,
> + HASH_ADDR_TABLE_SIZE,
> + &pep->htpr_dma, GFP_KERNEL);
> + if (pep->htpr == NULL)
> + return -ENOMEM;
> + }
> + memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
> + wrl(pep, HTPR, pep->htpr_dma);
> + return 0;
> +}
> +
> +static void pxa168_eth_set_rx_mode(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct netdev_hw_addr *ha;
> + u32 val;
> +
> + val = rdl(pep, PORT_CONFIG);
> + if (dev->flags & IFF_PROMISC)
> + val |= PCR_PM;
> + else
> + val &= ~PCR_PM;
> + wrl(pep, PORT_CONFIG, val);
> + netdev_for_each_mc_addr(ha, dev)
> + update_hash_table_mac_address(pep, NULL, ha->addr);
> +}
> +
> +static int pxa168_eth_set_mac_address(struct net_device *dev, void *addr)
> +{
> + struct sockaddr *sa = addr;
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + unsigned char oldMac[ETH_ALEN];
> +
> + if (!is_valid_ether_addr(sa->sa_data))
> + return -EINVAL;
> + memcpy(oldMac, dev->dev_addr, ETH_ALEN);
> + memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
> + netif_addr_lock_bh(dev);
> + update_hash_table_mac_address(pep, oldMac, dev->dev_addr);
> + netif_addr_unlock_bh(dev);
> + return 0;
> +}
> +
> +static void eth_port_start(struct net_device *dev)
> +{
> + unsigned int val = 0;
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int tx_curr_desc, rx_curr_desc;
> +
> + /* Perform PHY reset, if there is a PHY. */
> + if (pep->phy != NULL) {
> + struct ethtool_cmd cmd;
> +
> + pxa168_get_settings(pep->dev, &cmd);
> + ethernet_phy_reset(pep);
> + pxa168_set_settings(pep->dev, &cmd);
> + }
> +
> + /* Assignment of Tx CTRP of given queue */
> + tx_curr_desc = pep->tx_curr_desc_q;
> + wrl(pep, ETH_C_TX_DESC_1,
> + (u32) ((struct tx_desc *)pep->tx_desc_dma + tx_curr_desc));
> +
> + /* Assignment of Rx CRDP of given queue */
> + rx_curr_desc = pep->rx_curr_desc_q;
> + wrl(pep, ETH_C_RX_DESC_0,
> + (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
> +
> + wrl(pep, ETH_F_RX_DESC_0,
> + (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
> +
> + /* Clear all interrupts */
> + wrl(pep, INT_CAUSE, 0);
> +
> + /* Enable all interrupts for receive, transmit and error. */
> + wrl(pep, INT_MASK, ALL_INTS);
> +
> + val = rdl(pep, PORT_CONFIG);
> + val |= PCR_EN;
> + wrl(pep, PORT_CONFIG, val);
> +
> + /* Start RX DMA engine */
> + val = rdl(pep, SDMA_CMD);
> + val |= SDMA_CMD_ERD;
> + wrl(pep, SDMA_CMD, val);
> +}
> +
> +static void eth_port_reset(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + unsigned int val = 0;
> +
> + /* Stop all interrupts for receive, transmit and error. */
> + wrl(pep, INT_MASK, 0);
> +
> + /* Clear all interrupts */
> + wrl(pep, INT_CAUSE, 0);
> +
> + /* Stop RX DMA */
> + val = rdl(pep, SDMA_CMD);
> + val &= ~SDMA_CMD_ERD; /* abort dma command */
> +
> + /* Abort any transmit and receive operations and put DMA
> + * in idle state.
> + */
> + abort_dma(pep);
> +
> + /* Disable port */
> + val = rdl(pep, PORT_CONFIG);
> + val &= ~PCR_EN;
> + wrl(pep, PORT_CONFIG, val);
> +}
> +
> +/*
> + * txq_reclaim - Free the tx desc data for completed descriptors
> + * If force is non-zero, frees uncompleted descriptors as well
> + */
> +static int txq_reclaim(struct net_device *dev, int force)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct tx_desc *desc;
> + u32 cmd_sts;
> + struct sk_buff *skb;
> + int tx_index;
> + dma_addr_t addr;
> + int count;
> + int released = 0;
> +
> + netif_tx_lock(dev);
> +
> + pep->work_todo &= ~(WORK_TX_DONE);
> + while (pep->tx_desc_count > 0) {
> + tx_index = pep->tx_used_desc_q;
> + desc = &pep->p_tx_desc_area[tx_index];
> + cmd_sts = desc->cmd_sts;
> + if (!force && (cmd_sts & BUF_OWNED_BY_DMA)) {
> + if (released > 0) {
> + goto txq_reclaim_end;
> + } else {
> + released = -1;
> + goto txq_reclaim_end;
> + }
> + }
> + pep->tx_used_desc_q = (tx_index + 1) % pep->tx_ring_size;
> + pep->tx_desc_count--;
> + addr = desc->buf_ptr;
> + count = desc->byte_cnt;
> + skb = pep->tx_skb[tx_index];
> + if (skb)
> + pep->tx_skb[tx_index] = NULL;
> +
> + if (cmd_sts & TX_ERROR) {
> + if (net_ratelimit())
> + printk(KERN_ERR "%s: Error in TX\n", dev->name);
> + dev->stats.tx_errors++;
> + }
> + dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE);
> + if (skb)
> + dev_kfree_skb_irq(skb);
> + released++;
> + }
> +txq_reclaim_end:
> + netif_tx_unlock(dev);
> + return released;
> +}
> +
> +static void pxa168_eth_tx_timeout(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + printk(KERN_INFO "%s: TX timeout desc_count %d\n",
> + dev->name, pep->tx_desc_count);
> +
> + schedule_work(&pep->tx_timeout_task);
> +}
> +
> +static void pxa168_eth_tx_timeout_task(struct work_struct *work)
> +{
> + struct pxa168_eth_private *pep = container_of(work,
> + struct pxa168_eth_private,
> + tx_timeout_task);
> + struct net_device *dev = pep->dev;
> + pxa168_eth_stop(dev);
> + pxa168_eth_open(dev);
> +}
> +
> +static int rxq_process(struct net_device *dev, int budget)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct net_device_stats *stats = &dev->stats;
> + unsigned int received_packets = 0;
> + struct sk_buff *skb;
> +
> + while (budget-- > 0) {
> +
> + int rx_next_curr_desc, rx_curr_desc, rx_used_desc;
> + struct rx_desc *rx_desc;
> + unsigned int cmd_sts;
> +
> + /* Do not process Rx ring in case of Rx ring resource error */
> + if (pep->rx_resource_err)
> + break;
> + rx_curr_desc = pep->rx_curr_desc_q;
> + rx_used_desc = pep->rx_used_desc_q;
> + rx_desc = &pep->p_rx_desc_area[rx_curr_desc];
> + cmd_sts = rx_desc->cmd_sts;
> + rmb();
> + if (cmd_sts & (BUF_OWNED_BY_DMA))
> + break;
> + skb = pep->rx_skb[rx_curr_desc];
> + pep->rx_skb[rx_curr_desc] = NULL;
> +
> + rx_next_curr_desc = (rx_curr_desc + 1) % pep->rx_ring_size;
> + pep->rx_curr_desc_q = rx_next_curr_desc;
> +
> + /* Rx descriptors exhausted. */
> + /* Set the Rx ring resource error flag */
> + if (rx_next_curr_desc == rx_used_desc)
> + pep->rx_resource_err = 1;
> + pep->rx_desc_count--;
> + dma_unmap_single(NULL, rx_desc->buf_ptr,
> + MAX_PKT_SIZE + ETH_HW_IP_ALIGN,
> + DMA_FROM_DEVICE);
> + received_packets++;
> + /*
> + * Update statistics.
> + * Note byte count includes 4 byte CRC count
> + */
> + stats->rx_packets++;
> + stats->rx_bytes += rx_desc->byte_cnt;
> + /*
> + * In case received a packet without first / last bits on OR
> + * the error summary bit is on, the packets needs to be droped.
> + */
> + if (((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
> + (RX_FIRST_DESC | RX_LAST_DESC))
> + || (cmd_sts & RX_ERROR)) {
> +
> + stats->rx_dropped++;
> + if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
> + (RX_FIRST_DESC | RX_LAST_DESC)) {
> + if (net_ratelimit())
> + printk(KERN_ERR
> + "%s: Rx pkt on multiple desc\n",
> + dev->name);
> + }
> + if (cmd_sts & RX_ERROR)
> + stats->rx_errors++;
> + dev_kfree_skb_irq(skb);
> + } else {
> + /*
> + * The -4 is for the CRC in the trailer of the
> + * received packet
> + */
> + skb_put(skb, rx_desc->byte_cnt - 4);
> + skb->protocol = eth_type_trans(skb, dev);
> + netif_receive_skb(skb);
> + }
> + dev->last_rx = jiffies;
> + }
> + /* Fill RX ring with skb's */
> + rxq_refill(dev);
> + return received_packets;
> +}
> +
> +static int pxa168_eth_collect_events(struct pxa168_eth_private *pep,
> + struct net_device *dev)
> +{
> + u32 icr;
> + int ret = 0;
> +
> + icr = rdl(pep, INT_CAUSE);
> + if (0x00 == icr)
> + return IRQ_NONE;
> +
> + wrl(pep, INT_CAUSE, icr ^ 0xffffffff);
> + if (icr & (ICR_TXBUF_H | ICR_TXBUF_L)) {
> + pep->work_todo |= WORK_TX_DONE;
> + ret = 1;
> + }
> + if (icr & ICR_RXBUF)
> + ret = 1;
> + if (icr & ICR_MII_CH) {
> + pep->work_todo |= WORK_LINK;
> + ret = 1;
> + }
> + return ret;
> +}
> +
> +static void handle_link_event(struct pxa168_eth_private *pep)
> +{
> + struct net_device *dev = pep->dev;
> + u32 port_status;
> + int speed;
> + int duplex;
> + int fc;
> +
> + port_status = rdl(pep, PORT_STATUS);
> + if (!(port_status & LINK_UP)) {
> + if (netif_carrier_ok(dev)) {
> + printk(KERN_INFO "%s: link down\n", dev->name);
> + netif_carrier_off(dev);
> + txq_reclaim(dev, 1);
> + }
> + return;
> + }
> + if (port_status & PORT_SPEED_100)
> + speed = 100;
> + else
> + speed = 10;
> +
> + duplex = (port_status & FULL_DUPLEX) ? 1 : 0;
> + fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0;
> + printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, "
> + "flow control %sabled\n", dev->name,
> + speed, duplex ? "full" : "half", fc ? "en" : "dis");
> + if (!netif_carrier_ok(dev))
> + netif_carrier_on(dev);
> +}
> +
> +static irqreturn_t pxa168_eth_int_handler(int irq, void *dev_id)
> +{
> + struct net_device *dev = (struct net_device *)dev_id;
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + if (unlikely(!pxa168_eth_collect_events(pep, dev)))
> + return IRQ_NONE;
> + /* Disable interrupts */
> + wrl(pep, INT_MASK, 0);
> + napi_schedule(&pep->napi);
> + return IRQ_HANDLED;
> +}
> +
> +static int set_port_config_ext(struct pxa168_eth_private *pep, int mtu)
> +{
> + int mtu_size;
> +
> + if ((mtu > ETH_DATA_LEN) || (mtu < 64))
> + return -EINVAL;
> +
> + mtu_size = PCXR_MFL_1518;
> + /* Extended Port Configuration */
> + wrl(pep,
> + PORT_CONFIG_EXT, PCXR_2BSM | /* Two byte suffix aligns IP hdr */
> + PCXR_DSCP_EN | /* Enable DSCP in IP */
> + mtu_size | PCXR_FLP | /* do not force link pass */
> + PCXR_TX_HIGH_PRI); /* Transmit - high priority queue */
> +
> + (pep->dev)->mtu = mtu;
> + return 0;
> +}
> +
> +static int pxa168_init_hw(struct pxa168_eth_private *pep)
> +{
> + int err = 0;
> +
> + /* Disable interrupts */
> + wrl(pep, INT_MASK, 0);
> + wrl(pep, INT_CAUSE, 0);
> + /* Write to ICR to clear interrupts. */
> + wrl(pep, INT_W_CLEAR, 0);
> + /* Abort any transmit and receive operations and put DMA
> + * in idle state.
> + */
> + abort_dma(pep);
> + /* Initialize address hash table */
> + err = init_hash_table(pep);
> + if (err)
> + return err;
> + /* SDMA configuration */
> + wrl(pep, SDMA_CONFIG, SDCR_BSZ8 | /* Burst size = 32 bytes */
> + SDCR_RIFB | /* Rx interrupt on frame */
> + SDCR_BLMT | /* Little endian transmit */
> + SDCR_BLMR | /* Little endian receive */
> + SDCR_RC_MAX_RETRANS); /* Max retransmit count */
> + /* Port Configuration */
> + wrl(pep, PORT_CONFIG, PCR_HS); /* Hash size is 1/2kb */
> + set_port_config_ext(pep, (pep->dev)->mtu);
> + return err;
> +}
> +
> +static int rxq_init(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct rx_desc *p_rx_desc;
> + int size = 0, i = 0;
> + int rx_desc_num = pep->rx_ring_size;
> +
> + /* Allocate RX skb rings */
> + pep->rx_skb = kmalloc(sizeof(*pep->rx_skb) * pep->rx_ring_size,
> + GFP_KERNEL);
> + if (!pep->rx_skb) {
> + printk(KERN_ERR "%s: Cannot alloc RX skb ring\n", dev->name);
> + return -ENOMEM;
> + }
> + /* Allocate RX ring */
> + pep->rx_desc_count = 0;
> + size = pep->rx_ring_size * sizeof(struct rx_desc);
> + pep->rx_desc_area_size = size;
> + pep->p_rx_desc_area = dma_alloc_coherent(NULL, size,
> + &pep->rx_desc_dma, GFP_KERNEL);
> + if (!pep->p_rx_desc_area) {
> + printk(KERN_ERR "%s: Cannot alloc RX ring (size %d bytes)\n",
> + dev->name, size);
> + goto out;
> + }
> + memset((void *)pep->p_rx_desc_area, 0, size);
> + /* initialize the next_desc_ptr links in the Rx descriptors ring */
> + p_rx_desc = (struct rx_desc *)pep->p_rx_desc_area;
> + for (i = 0; i < rx_desc_num; i++) {
> + p_rx_desc[i].next_desc_ptr = pep->rx_desc_dma +
> + ((i + 1) % rx_desc_num) * sizeof(struct rx_desc);
> + }
> + /* Save Rx desc pointer to driver struct. */
> + pep->rx_curr_desc_q = 0;
> + pep->rx_used_desc_q = 0;
> + pep->rx_desc_area_size = rx_desc_num * sizeof(struct rx_desc);
> + return 0;
> +out:
> + kfree(pep->rx_skb);
> + return -ENOMEM;
> +}
> +
> +static void rxq_deinit(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int curr;
> +
> + /* Free preallocated skb's on RX rings */
> + for (curr = 0; pep->rx_desc_count && curr < pep->rx_ring_size;
> curr++) {
> + if (pep->rx_skb[curr]) {
> + dev_kfree_skb(pep->rx_skb[curr]);
> + pep->rx_desc_count--;
> + }
> + }
> + if (pep->rx_desc_count)
> + printk(KERN_ERR
> + "Error in freeing Rx Ring. %d skb's still\n",
> + pep->rx_desc_count);
> + /* Free RX ring */
> + if (pep->p_rx_desc_area)
> + dma_free_coherent(NULL, pep->rx_desc_area_size,
> + pep->p_rx_desc_area, pep->rx_desc_dma);
> + kfree(pep->rx_skb);
> +}
> +
> +static int txq_init(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct tx_desc *p_tx_desc;
> + int size = 0, i = 0;
> + int tx_desc_num = pep->tx_ring_size;
> +
> + pep->tx_skb = kmalloc(sizeof(*pep->tx_skb) * pep->tx_ring_size,
> + GFP_KERNEL);
> + if (!pep->tx_skb) {
> + printk(KERN_ERR "%s: Cannot alloc TX skb ring\n", dev->name);
> + return -ENOMEM;
> + }
> + /* Allocate TX ring */
> + pep->tx_desc_count = 0;
> + size = pep->tx_ring_size * sizeof(struct tx_desc);
> + pep->tx_desc_area_size = size;
> + pep->p_tx_desc_area = dma_alloc_coherent(NULL, size,
> + &pep->tx_desc_dma, GFP_KERNEL);
> + if (!pep->p_tx_desc_area) {
> + printk(KERN_ERR "%s: Cannot allocate Tx Ring (size %d
> bytes)\n",
> + dev->name, size);
> + goto out;
> + }
> + memset((void *)pep->p_tx_desc_area, 0, pep->tx_desc_area_size);
> + /* Initialize the next_desc_ptr links in the Tx descriptors ring */
> + p_tx_desc = (struct tx_desc *)pep->p_tx_desc_area;
> + for (i = 0; i < tx_desc_num; i++) {
> + p_tx_desc[i].next_desc_ptr = pep->tx_desc_dma +
> + ((i + 1) % tx_desc_num) * sizeof(struct tx_desc);
> + }
> + pep->tx_curr_desc_q = 0;
> + pep->tx_used_desc_q = 0;
> + pep->tx_desc_area_size = tx_desc_num * sizeof(struct tx_desc);
> + return 0;
> +out:
> + kfree(pep->tx_skb);
> + return -ENOMEM;
> +}
> +
> +static void txq_deinit(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + /* Free outstanding skb's on TX ring */
> + txq_reclaim(dev, 1);
> + BUG_ON(pep->tx_used_desc_q != pep->tx_curr_desc_q);
> + /* Free TX ring */
> + if (pep->p_tx_desc_area)
> + dma_free_coherent(NULL, pep->tx_desc_area_size,
> + pep->p_tx_desc_area, pep->tx_desc_dma);
> + kfree(pep->tx_skb);
> +}
> +
> +static int pxa168_eth_open(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int err;
> +
> + err = request_irq(dev->irq, pxa168_eth_int_handler,
> + IRQF_DISABLED , dev->name, dev);
> + if (err) {
> + dev_printk(KERN_ERR, &dev->dev, "can't assign irq\n");
> + return -EAGAIN;
> + }
> + pep->rx_resource_err = 0;
> + err = rxq_init(dev);
> + if (err != 0)
> + goto out_free_irq;
> + err = txq_init(dev);
> + if (err != 0)
> + goto out_free_rx_skb;
> + pep->rx_used_desc_q = 0;
> + pep->rx_curr_desc_q = 0;
> +
> + /* Fill RX ring with skb's */
> + rxq_refill(dev);
> + pep->rx_used_desc_q = 0;
> + pep->rx_curr_desc_q = 0;
> + netif_carrier_off(dev);
> + eth_port_start(dev);
> + napi_enable(&pep->napi);
> + return 0;
> +out_free_rx_skb:
> + rxq_deinit(dev);
> +out_free_irq:
> + free_irq(dev->irq, dev);
> + return err;
> +}
> +
> +static int pxa168_eth_stop(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + eth_port_reset(dev);
> +
> + /* Disable interrupts */
> + wrl(pep, INT_MASK, 0);
> + wrl(pep, INT_CAUSE, 0);
> + /* Write to ICR to clear interrupts. */
> + wrl(pep, INT_W_CLEAR, 0);
> + napi_disable(&pep->napi);
> + del_timer_sync(&pep->timeout);
> + netif_carrier_off(dev);
> + free_irq(dev->irq, dev);
> + rxq_deinit(dev);
> + txq_deinit(dev);
> + return 0;
> +}
> +
> +static int pxa168_eth_change_mtu(struct net_device *dev, int mtu)
> +{
> + if ((mtu > ETH_DATA_LEN) || (mtu < 64))
> + return -EINVAL;
> + dev->mtu = mtu;
> + return 0;
> +}
> +
> +static int eth_alloc_tx_desc_index(struct pxa168_eth_private *pep)
> +{
> + int tx_desc_curr;
> +
> + tx_desc_curr = pep->tx_curr_desc_q;
> + pep->tx_curr_desc_q = (tx_desc_curr + 1) % pep->tx_ring_size;
> + BUG_ON(pep->tx_curr_desc_q == pep->tx_used_desc_q);
> + pep->tx_desc_count++;
> + return tx_desc_curr;
> +}
> +
> +static void eth_tx_submit_descs_for_skb(struct pxa168_eth_private *pep,
> + struct sk_buff *skb)
> +{
> + int tx_index;
> + struct tx_desc *desc;
> + int length;
> +
> + tx_index = eth_alloc_tx_desc_index(pep);
> + desc = &pep->p_tx_desc_area[tx_index];
> + length = skb->len;
> + pep->tx_skb[tx_index] = skb;
> + desc->byte_cnt = length;
> + desc->buf_ptr = dma_map_single(NULL, skb->data, length,
> DMA_TO_DEVICE);
> + wmb();
> + desc->cmd_sts = BUF_OWNED_BY_DMA | TX_GEN_CRC | TX_FIRST_DESC |
> + TX_ZERO_PADDING | TX_LAST_DESC;
> + if (unlikely(!(pep->tx_desc_count % TX_DONE_INTERVAL)))
> + desc->cmd_sts |= TX_EN_INT;
> + wmb();
> + wrl(pep, SDMA_CMD, SDMA_CMD_TXDH | SDMA_CMD_ERD);
> +}
> +
> +static int pxa168_rx_poll(struct napi_struct *napi, int budget)
> +{
> + struct pxa168_eth_private *pep =
> + container_of(napi, struct pxa168_eth_private, napi);
> + struct net_device *dev = pep->dev;
> + int work_done = 0;
> +
> + if (unlikely(pep->work_todo & WORK_LINK)) {
> + pep->work_todo &= ~(WORK_LINK);
> + handle_link_event(pep);
> + }
> + /*
> + * We call txq_reclaim every time since in NAPI interupts are
> disabled
> + * and due to this we miss the TX_DONE interrupt,which is not
> updated in
> + * interrupt status register.
> + */
> + txq_reclaim(dev, 0);
> + if (netif_queue_stopped(dev)
> + && pep->tx_ring_size - pep->tx_desc_count > 1) {
> + netif_wake_queue(dev);
> + }
> + work_done = rxq_process(dev, budget);
> + if (work_done < budget) {
> + napi_complete(napi);
> + wrl(pep, INT_MASK, ALL_INTS);
> + }
> + return work_done;
> +}
> +
> +static int pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device
> *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + struct net_device_stats *stats = &dev->stats;
> +
> + eth_tx_submit_descs_for_skb(pep, skb);
> + stats->tx_bytes += skb->len;
> + stats->tx_packets++;
> + dev->trans_start = jiffies;
> + if (pep->tx_ring_size - pep->tx_desc_count <= 1) {
> + /* We handled the current skb, but now we are out of space.*/
> + netif_stop_queue(dev);
> + }
> + return NETDEV_TX_OK;
> +}
> +
> +static int pxa168_smi_read(struct mii_bus *bus, int phy_addr, int regnum)
> +{
> + int val;
> + struct pxa168_eth_private *pep = bus->priv;
> + int i = 0;
> +
> + /* wait for the SMI register to become available */
> + for (i = 0; (val = rdl(pep, SMI)) & SMI_BUSY; i++) {
> + if (i == PHY_WAIT_ITERATIONS) {
> + printk(KERN_ERR
> + "pxa168 PHY timeout, val=0x%x\n", val);
> + return -ETIMEDOUT;
> + }
> + udelay(1);
> + }
> + wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) | SMI_OP_R);
> + /* now wait for the data to be valid */
> + for (i = 0; !((val = rdl(pep, SMI)) & SMI_R_VALID); i++) {
> + if (i == PHY_WAIT_ITERATIONS) {
> + printk(KERN_ERR
> + "pxa168 PHY RD timeout, val=0x%x\n", val);
> + return -ETIMEDOUT;
> + }
> + udelay(1);
> + }
> + return val & 0xffff;
> +}
> +
> +static int pxa168_smi_write(struct mii_bus *bus, int phy_addr, int regnum,
> + u16 value)
> +{
> + struct pxa168_eth_private *pep = bus->priv;
> + int i;
> +
> + /* wait for the SMI register to become available */
> + for (i = 0; rdl(pep, SMI) & SMI_BUSY; i++) {
> + if (i == PHY_WAIT_ITERATIONS) {
> + printk(KERN_ERR "pxa168 PHY busy timeout.\n");
> + return -ETIMEDOUT;
> + }
> + udelay(1);
> + }
> + wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) |
> + SMI_OP_W | (value & 0xffff));
> +
> + return 0;
> +}
> +
> +static int pxa168_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr,
> + int cmd)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + if (pep->phy != NULL)
> + return phy_mii_ioctl(pep->phy, if_mii(ifr), cmd);
> + return -EOPNOTSUPP;
> +}
> +
> +static struct phy_device *phy_scan(struct pxa168_eth_private *pep, int
> phy_addr)
> +{
> + struct mii_bus *bus = pep->smi_bus;
> + struct phy_device *phydev;
> + int start;
> + int num;
> + int i;
> +
> + if (phy_addr == PXA168_ETH_PHY_ADDR_DEFAULT) {
> + /* Scan entire range */
> + start = ethernet_phy_get(pep);
> + num = 32;
> + } else {
> + /* Use phy addr specific to platform */
> + start = phy_addr & 0x1f;
> + num = 1;
> + }
> + phydev = NULL;
> + for (i = 0; i < num; i++) {
> + int addr = (start + i) & 0x1f;
> + if (bus->phy_map[addr] == NULL)
> + mdiobus_scan(bus, addr);
> +
> + if (phydev == NULL) {
> + phydev = bus->phy_map[addr];
> + if (phydev != NULL)
> + ethernet_phy_set_addr(pep, addr);
> + }
> + }
> +
> + return phydev;
> +}
> +
> +static void phy_init(struct pxa168_eth_private *pep, int speed, int
> duplex)
> +{
> + struct phy_device *phy = pep->phy;
> + ethernet_phy_reset(pep);
> +
> + phy_attach(pep->dev, dev_name(&phy->dev), 0,
> PHY_INTERFACE_MODE_MII);
> +
> + if (speed == 0) {
> + phy->autoneg = AUTONEG_ENABLE;
> + phy->speed = 0;
> + phy->duplex = 0;
> + phy->supported &= PHY_BASIC_FEATURES;
> + phy->advertising = phy->supported | ADVERTISED_Autoneg;
> + } else {
> + phy->autoneg = AUTONEG_DISABLE;
> + phy->advertising = 0;
> + phy->speed = speed;
> + phy->duplex = duplex;
> + }
> + phy_start_aneg(phy);
> +}
> +
> +static int ethernet_phy_setup(struct net_device *dev)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + if (pep->pd != NULL) {
> + if (pep->pd->init)
> + pep->pd->init();
> + }
> + pep->phy = phy_scan(pep, pep->pd->phy_addr & 0x1f);
> + if (pep->phy != NULL)
> + phy_init(pep, pep->pd->speed, pep->pd->duplex);
> + update_hash_table_mac_address(pep, NULL, dev->dev_addr);
> + return 0;
> +}
> +
> +static int get_random_mac_addr(struct net_device *dev)
> +{
> + printk(KERN_INFO "%s:Using random mac address\n", dev->name);
> + random_ether_addr(dev->dev_addr);
> + return 0;
> +}
> +
> +static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd
> *cmd)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> + int err;
> +
> + err = phy_read_status(pep->phy);
> + if (err == 0)
> + err = phy_ethtool_gset(pep->phy, cmd);
> + return err;
> +}
> +
> +static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd
> *cmd)
> +{
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + return phy_ethtool_sset(pep->phy, cmd);
> +}
> +
> +static void pxa168_get_drvinfo(struct net_device *dev,
> + struct ethtool_drvinfo *info)
> +{
> + strncpy(info->driver, DRIVER_NAME, 32);
> + strncpy(info->version, DRIVER_VERSION, 32);
> + strncpy(info->fw_version, "N/A", 32);
> + strncpy(info->bus_info, "N/A", 32);
> +}
> +
> +static u32 pxa168_get_link(struct net_device *dev)
> +{
> + return !!netif_carrier_ok(dev);
> +}
> +
> +static const struct ethtool_ops pxa168_ethtool_ops = {
> + .get_settings = pxa168_get_settings,
> + .set_settings = pxa168_set_settings,
> + .get_drvinfo = pxa168_get_drvinfo,
> + .get_link = pxa168_get_link,
> +};
> +
> +static const struct net_device_ops pxa168_eth_netdev_ops = {
> + .ndo_open = pxa168_eth_open,
> + .ndo_stop = pxa168_eth_stop,
> + .ndo_start_xmit = pxa168_eth_start_xmit,
> + .ndo_set_rx_mode = pxa168_eth_set_rx_mode,
> + .ndo_set_mac_address = pxa168_eth_set_mac_address,
> + .ndo_validate_addr = eth_validate_addr,
> + .ndo_do_ioctl = pxa168_eth_do_ioctl,
> + .ndo_change_mtu = pxa168_eth_change_mtu,
> + .ndo_tx_timeout = pxa168_eth_tx_timeout,
> +};
> +
> +static int pxa168_eth_probe(struct platform_device *pdev)
> +{
> + struct pxa168_eth_private *pep = NULL;
> + struct net_device *dev = NULL;
> + struct resource *res;
> + struct clk *clk;
> + int err;
> +
> + printk(KERN_NOTICE "PXA168 10/100 Ethernet Driver\n");
> +
> + clk = clk_get(&pdev->dev, "MFUCLK");
> + if (IS_ERR(clk)) {
> + printk(KERN_ERR "fast Ethernet failed to get clock\n");
> + return -ENODEV;
> + }
> + clk_enable(clk);
> +
> + dev = alloc_etherdev(sizeof(struct pxa168_eth_private));
> + if (!dev) {
> + err = -ENOMEM;
> + goto out;
> + }
> +
> + platform_set_drvdata(pdev, dev);
> + pep = netdev_priv(dev);
> + pep->dev = dev;
> + pep->clk = clk;
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (res == NULL) {
> + err = -ENODEV;
> + goto out;
> + }
> + pep->base = ioremap(res->start, res->end - res->start + 1);
> + if (pep->base == NULL) {
> + err = -ENOMEM;
> + goto out;
> + }
> + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + BUG_ON(!res);
> + dev->irq = res->start;
> + dev->netdev_ops = &pxa168_eth_netdev_ops;
> + dev->watchdog_timeo = 8 * HZ;
> + dev->base_addr = 0;
> + SET_ETHTOOL_OPS(dev, &pxa168_ethtool_ops);
> +
> + INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task);
> +
> + pep->rx_ring_size = NUM_RX_DESCS;
> + pep->tx_ring_size = NUM_TX_DESCS;
> + get_random_mac_addr(dev);
> + pep->pd = pdev->dev.platform_data;
> + netif_napi_add(dev, &pep->napi, pxa168_rx_poll, pep->rx_ring_size);
> +
> + memset(&pep->timeout, 0, sizeof(struct timer_list));
> + init_timer(&pep->timeout);
> + pep->timeout.function = rxq_refill_timer_wrapper;
> + pep->timeout.data = (unsigned long)pep;
> +
> + pep->smi_bus = mdiobus_alloc();
> + if (pep->smi_bus == NULL) {
> + err = -ENOMEM;
> + goto out;
> + }
> + pep->smi_bus->priv = pep;
> + pep->smi_bus->name = "pxa168_eth smi";
> + pep->smi_bus->read = pxa168_smi_read;
> + pep->smi_bus->write = pxa168_smi_write;
> + snprintf(pep->smi_bus->id, MII_BUS_ID_SIZE, "%d", pdev->id);
> + pep->smi_bus->parent = &pdev->dev;
> + pep->smi_bus->phy_mask = 0xffffffff;
> + if (mdiobus_register(pep->smi_bus) < 0) {
> + err = -ENOMEM;
> + goto out;
> + }
> + pxa168_init_hw(pep);
> + err = ethernet_phy_setup(dev);
> + if (err)
> + goto out;
> + SET_NETDEV_DEV(dev, &pdev->dev);
> + err = register_netdev(dev);
> + if (err)
> + goto out;
> + return 0;
> +out:
> + if (pep->clk) {
> + clk_disable(pep->clk);
> + clk_put(pep->clk);
> + pep->clk = NULL;
> + }
> + if (pep->base) {
> + iounmap(pep->base);
> + pep->base = NULL;
> + }
> + if (dev)
> + free_netdev(dev);
> + return err;
> +}
> +
> +static int pxa168_eth_remove(struct platform_device *pdev)
> +{
> + struct net_device *dev = platform_get_drvdata(pdev);
> + struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> + if (pep->htpr) {
> + dma_free_coherent(NULL, HASH_ADDR_TABLE_SIZE + 7,
> + pep->htpr, pep->htpr_dma);
> + pep->htpr = NULL;
> + }
> + if (pep->clk) {
> + clk_disable(pep->clk);
> + clk_put(pep->clk);
> + pep->clk = NULL;
> + }
> + if (pep->phy != NULL)
> + phy_detach(pep->phy);
> +
> + iounmap(pep->base);
> + pep->base = NULL;
> + unregister_netdev(dev);
> + flush_scheduled_work();
> + free_netdev(dev);
> + platform_set_drvdata(pdev, NULL);
> + return 0;
> +}
> +
> +static void pxa168_eth_shutdown(struct platform_device *pdev)
> +{
> + struct net_device *dev = platform_get_drvdata(pdev);
> + eth_port_reset(dev);
> +}
> +
> +#ifdef CONFIG_PM
> +static int pxa168_eth_resume(struct platform_device *pdev)
> +{
> + return -ENOSYS;
> +}
> +
> +static int pxa168_eth_suspend(struct platform_device *pdev, pm_message_t
> state)
> +{
> + return -ENOSYS;
> +}
> +
> +#else
> +#define pxa168_eth_resume NULL
> +#define pxa168_eth_suspend NULL
> +#endif
> +
> +static struct platform_driver pxa168_eth_driver = {
> + .probe = pxa168_eth_probe,
> + .remove = pxa168_eth_remove,
> + .shutdown = pxa168_eth_shutdown,
> + .resume = pxa168_eth_resume,
> + .suspend = pxa168_eth_suspend,
> + .driver = {
> + .name = DRIVER_NAME,
> + },
> +};
> +
> +static int __init pxa168_init_module(void)
> +{
> + return platform_driver_register(&pxa168_eth_driver);
> +}
> +
> +static void __exit pxa168_cleanup_module(void)
> +{
> + platform_driver_unregister(&pxa168_eth_driver);
> +}
> +
> +module_init(pxa168_init_module);
> +module_exit(pxa168_cleanup_module);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Ethernet driver for Marvell PXA168");
> +MODULE_ALIAS("platform:pxa168_eth");
> diff --git a/include/linux/pxa168_eth.h b/include/linux/pxa168_eth.h
> new file mode 100644
> index 0000000..152981c
> --- /dev/null
> +++ b/include/linux/pxa168_eth.h
> @@ -0,0 +1,20 @@
> +/*
> + *pxa168 ethernet platform device data definition file.
> + */
> +#ifndef __LINUX_PXA168_ETH_H
> +#define __LINUX_PXA168_ETH_H
> +
> +struct pxa168_eth_platform_data {
> + int phy_addr;
> +
> + /* If speed is 0, then speed and duplex are autonegotiated. */
> + int speed; /* 0, SPEED_10, SPEED_100 */
> + int duplex; /* DUPLEX_HALF or DUPLEX_FULL */
> +
> + /* init callback is used for board specific initialization
> + * e.g on Aspenite its used to initialize the PHY transceiver.
> + */
> + int (*init)(void);
> +};
> +
> +#endif /* __LINUX_PXA168_ETH_H */
> --
> 1.5.3.3
^ permalink raw reply
* Re: [PATCH] net: add Fast Ethernet driver for PXA168.
From: Philip Rakity @ 2010-08-06 15:44 UTC (permalink / raw)
To: Sachin Sanap
Cc: Lennert Buytenhek, netdev@vger.kernel.org, Ashish Karkare,
Prabhanjan Sarnaik, eric.y.miao@gmail.com, Mark Brown
In-Reply-To: <EC64E4604C8024438259B683BB2514E1082B1CB1B0@SC-VEXCH1.marvell.com>
Sachin,
You should change
>>> + skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN);
>>
to allocatie ADDITIONAL prepend space in the header. If you are doing ethernet to 802.11 routing or bridging then the wireless driver will not have enough space in front of the header to prepend its headers without having to allocate additional space. You should check what 802.11n requires. 64 is okay for 11b/g. I would imagine doing something like this.
#define EXTRA_PREPEND_SPACE 64 /* extra header space for wireless headers */
>>> skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN + EXTRA_PREPEND_SPACE);
> + skb_reserve(skb, ETH_HW_IP_ALIGN + EXTRA_PREPEND_SPACE);
etc
Philip
On Aug 6, 2010, at 8:14 AM, Sachin Sanap wrote:
> Thanks Lennert,Eric for your review. Comments inline. I will resend the modified patch as a reply to this email.
>>
>> On Wed, Aug 04, 2010 at 04:17:50PM +0530, Sachin Sanap wrote:
>>
>>> This patch adds support for PXA168 Fast Ethernet on Aspenite
>>> board.
>>
>> There's nothing Aspenite-specific in this patch (nor should there be),
>> so you can leave that part out.
>
> Removed.
>>
>>
>>> Patch generated against Linux 2.6.35-rc5
>>> commit cd5b8f8755a89a57fc8c408d284b8b613f090345
>>
>> This might be interesting to know but shouldn't be part of the commit
>> message.
>
> Removed.
>>
>>
>>> diff --git a/arch/arm/mach-mmp/include/mach/pxa168_eth.h
>> b/arch/arm/mach-mmp/include/mach/pxa168_eth.h
>>> new file mode 100644
>>> index 0000000..abfd335
>>> --- /dev/null
>>> +++ b/arch/arm/mach-mmp/include/mach/pxa168_eth.h
>>> @@ -0,0 +1,18 @@
>>> +/*
>>> + *pxa168 ethernet platform device data definition file.
>>> + */
>>> +#ifndef __LINUX_PXA168_ETH_H
>>> +#define __LINUX_PXA168_ETH_H
>>
>> This should just be in include/linux, IMHO, as this unit isn't only used
>> in the PXA168, for one.
> I have moved it to include/linux but the PXA168 name is still there.
>
>>
>>
>>> +struct pxa168_eth_platform_data {
>>> + int port_number;
>>> + u16 phy_addr;
>>> +
>>> + /* If speed is 0, then speed and duplex are autonegotiated. */
>>> + u32 speed; /* 0, SPEED_10, SPEED_100 */
>>> + u32 duplex; /* DUPLEX_HALF or DUPLEX_FULL */
>>
>> phylib treats these three (phy address, speed and duplex) as ints, is
>> there any reason you need these to be of different types?
>
>
> Used int at all three places.
>
>>
>>
>>> + int (*init)(void);
>>
>> What's this needed for? The name of this callback is entirely
>> nondescriptive, there's no comment as to when it's called, etc.
>
> Added a comment for it.
>>
>>
>>> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
>>> index ce2fcdd..5ebf287 100644
>>> --- a/drivers/net/Kconfig
>>> +++ b/drivers/net/Kconfig
>>> @@ -927,6 +927,16 @@ config SMC91X
>>> The module will be called smc91x. If you want to compile it as a
>>> module, say M here and read
>> <file:Documentation/kbuild/modules.txt>.
>>>
>>> +config PXA168_ETH
>>> + tristate "Marvell pxa168 ethernet support"
>>> + depends on MACH_ASPENITE
>>
>> You can have it depend on ARM or PXA168, but having it depend on support
>> for one specific board support file is almost certainly wrong.
>
> Depends on CPU_PXA168.
>>
>>
>>> diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c
>>> new file mode 100644
>>> index 0000000..618e558
>>> --- /dev/null
>>> +++ b/drivers/net/pxa168_eth.c
>>> @@ -0,0 +1,1723 @@
>>> +/*
>>> + * Driver for PXA168 based boards.
>>
>> Why not call this "PXA168 ethernet driver"?
> Change done.
>>
>>
>>> + * Based on MV643XX driver.
>>
>> The mv643xx ethernet driver, that is.
> Change done.
>>
>>
>>> +#define DRIVER_NAME "pxa168-mfu"
>>
>> mfu?
> Its was for multi function unit on Aspenite, changed it to pxa168-eth
>>
>>
>>> +/* smi register */
>>> +#define SMI_BUSY (1<<28) /* 0 - Write, 1 - Read */
>>> +#define SMI_R_VALID (1<<27) /* 0 - Write, 1 - Read */
>>> +#define SMI_OP_W (0<<26) /* Write operation */
>>> +#define SMI_OP_R (1<<26) /* Read operation */
>>
>> (1 << 28)
>> (1 << 27)
>> (0 << 26)
>>
>> etc (thoughout the file)
> Changed.
>
>>
>>
>>> +struct pxa168_private {
>>
>> ITYM pxa168_eth_private
> Yes, changed.
>>
>>
>>> + /*
>>> + * Used in case RX Ring is empty, which can be caused when
>>> + * system does not have resources (skb's)
>>> + */
>>
>> can be caused by
>> can occur when
> Changed.
>>
>>
>>> + struct timer_list timeout;
>>> + struct mii_bus *smi_bus;
>>> + struct phy_device *phy;
>>> +
>>> + /* clock */
>>> + struct clk *clk;
>>> + struct pxa168_eth_platform_data *pd;
>>> + /*
>>> + * Ethernet controller base address.
>>> + */
>>> + void __iomem *base;
>>> +
>>> + u8 *htpr; /* hash pointer */
>>
>> IMHO it'd look better if you'd either always put the comment on the same
>> line or put the comment above the member (within the same struct).
>>
>> Also, 'hash pointer' as a comment isn't very helpful. Making it
>> 'hardware address filter table' or so would be more descriptive.
> Changed.
>>
>>
>>> +struct addr_table_entry {
>>> + u32 lo;
>>> + u32 hi;
>>> +};
>>
>> If the address filter table consists of these structures, it's
>> confusing to make htpr a u8 * and then cast things around. Better make
>> htpr a void * then.
> Changed.
>>
>> Also, what about endian-cleanness? If you run the CPU core in big
>> endian, will the address filter table handling logic in this driver
>> still work?
>
>
> Added cpu_to_le32 for the mac_high and mac_low variables before passing them to the add_del_hash_entry. I have not tested it on the Big endian but I think it should now work.
>
>>
>>
>>> +static char marvell_OUI[3] = { 0x02, 0x50, 0x43 };
>>
>> This isn't even a marvell OUI -- 00:50:43 is a marvell OUI, and
>> anything with 02: is a locally generated address.
>>
>> Why don't you just use random_ether_addr()?
> Changed to use random_ether_addr.
>>
>>
>>> +static inline u32 rdl(struct pxa168_private *mp, int offset)
>>> +{
>>> + return readl(mp->base + offset);
>>> +}
>>
>> In mv643xx_eth, 'mp' refers to mv643xx_eth_private, it's funny that
>> you've changed the name of the struct but not that of the variable.
>>
>> As the rest of the driver looks very very similar to mv643xx_eth, if
>> not pretty much entirely identical in a lot of places, you should give
>> more credit to mv643xx_eth than 'based on', in my opinion.
> Changed the credit line for the same.
>>
>>
>>> +static int ethernet_phy_get(struct pxa168_private *mp)
>>> +{
>>> + unsigned int reg_data;
>>> +
>>> + /* only support 3 ports */
>>> + BUG_ON(mp->port_num > 2);
>>
>> Hm, does it actually support 3 ports?
> No. changed the code.
>>
>>
>>> +static void ethernet_phy_set_addr(struct pxa168_private *mp, int
>> phy_addr)
>>> +{
>>> + u32 reg_data;
>>> + int addr_shift = 5 * mp->port_num;
>>> +
>>> + /* only support 3 ports */
>>> + BUG_ON(mp->port_num > 2);
>>> +
>>> + reg_data = rdl(mp, PHY_ADDRESS);
>>> + reg_data &= ~(0x1f << addr_shift);
>>> + reg_data |= (phy_addr & 0x1f) << addr_shift;
>>> + wrl(mp, PHY_ADDRESS, reg_data);
>>> +}
>>> +static void ethernet_phy_reset(struct pxa168_private *mp)
>>> +{
>>
>> Blank line between functions.
>>
>>
>>> + do {
>>> + data = phy_read(mp->phy, MII_BMCR);
>>> + } while (data >= 0 && data & BMCR_RESET);
>>> +
>>> +}
>>
>> No blank line here.
>>
>>
>>> +static void rxq_refill(struct net_device *dev)
>>> +{
>>> + struct pxa168_private *mp = netdev_priv(dev);
>>> + struct sk_buff *skb;
>>> + struct rx_desc *p_used_rx_desc;
>>> + int used_rx_desc;
>>> +
>>> + while (mp->rx_desc_count < mp->rx_ring_size) {
>>> +
>>> + skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN);
>>
>> No blank line here.
>>
>>
>>> + if (mp->rx_desc_count == 0) {
>>> + printk(KERN_INFO "%s: Rx ring is empty\n", dev->name);
>>
>> I wouldn't printk for this (and certainly not without a rate limit) --
>> going OOM will then just mean that you'll get endless console spam as
>> well, which isn't helpful.
> Hm, removed the printk.
>
>>
>>
>>> +/*
>>> + * --------------------------------------------------------------------
>> --------
>>> + * This function will calculate the hash function of the address.
>>> + * depends on the hash mode and hash size.
>>
>> Where are the hash mode and size configured?
>>
>>
>>> +static u32 hash_function(u32 mac_high, u32 mac_low)
>>> +{
>>> + u32 hash_result;
>>> + u32 addr_high;
>>> + u32 addr_low;
>>> + u32 addr0;
>>> + u32 addr1;
>>> + u32 addr2;
>>> + u32 addr3;
>>> + u32 addr_high_swapped;
>>> + u32 addr_low_swapped;
>>> +
>>> + addr_high = nibble_swapping_16_bit(mac_high);
>>> + addr_low = nibble_swapping_32_bit(mac_low);
>>> +
>>> + addr_high_swapped = flip_4_bits(addr_high & 0xf)
>>> + + ((flip_4_bits((addr_high >> 4) & 0xf)) << 4)
>>> + + ((flip_4_bits((addr_high >> 8) & 0xf)) << 8)
>>> + + ((flip_4_bits((addr_high >> 12) & 0xf)) << 12);
>>> +
>>> + addr_low_swapped = flip_4_bits(addr_low & 0xf)
>>> + + ((flip_4_bits((addr_low >> 4) & 0xf)) << 4)
>>> + + ((flip_4_bits((addr_low >> 8) & 0xf)) << 8)
>>> + + ((flip_4_bits((addr_low >> 12) & 0xf)) << 12)
>>> + + ((flip_4_bits((addr_low >> 16) & 0xf)) << 16)
>>> + + ((flip_4_bits((addr_low >> 20) & 0xf)) << 20)
>>> + + ((flip_4_bits((addr_low >> 24) & 0xf)) << 24)
>>> + + ((flip_4_bits((addr_low >> 28) & 0xf)) << 28);
>>> +
>>> + addr_high = addr_high_swapped;
>>> + addr_low = addr_low_swapped;
>>> +
>>> + addr0 = (addr_low >> 2) & 0x03f;
>>> + addr1 = (addr_low & 0x003) | ((addr_low >> 8) & 0x7f) << 2;
>>> + addr2 = (addr_low >> 15) & 0x1ff;
>>> + addr3 = ((addr_low >> 24) & 0x0ff) | ((addr_high & 1) << 8);
>>> +
>>> + hash_result = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
>>> + hash_result = hash_result & 0x07ff;
>>> + return hash_result;
>>
>> As it's only a bunch of XORs, can you not calculate the hash function
>> first and only then do the bit reversal?
>>
>>
>>> + * --------------------------------------------------------------------
>> --------
>>> + * This function will add an entry to the address table.
>>> + * depends on the hash mode and hash size that was initialized.
>>> + * Inputs
>>> + * mp - ETHERNET .
>>> + * mac_high - the 2 most significant bytes of the MAC address.
>>> + * mac_low - the 4 least significant bytes of the MAC address.
>>> + * skip - if 1, skip this address.
>>
>> What does 'skip' do?
>>
>>
>>> + * rd - the RD field in the address table.
>>
>> And what does that do?
>
> Added more comments regarding the address filtering and the hash function.
>
>>
>> If one has hardware docs, this comment doesn't really add anything,
>> while if one doesn't have hardware docs, this comment doesn't really
>> explain anything -- so the comment isn't very useful.
>>
>>
>>> +static void update_hash_table_mac_address(struct pxa168_private *mp,
>>> + u8 *oaddr, u8 *addr)
>>> [...]
>>> +static int init_hashtable(struct pxa168_private *mp)
>>
>> hash_table?
>>
>>
>>> +{
>>> + u8 *addr;
>>> + dma_addr_t reg_dma;
>>> +
>>> + if (mp->htpr == NULL) {
>>> + mp->htpr = dma_alloc_coherent(NULL,
>>> + HASH_ADDR_TABLE_SIZE + 7,
>>> + &mp->htpr_dma, GFP_KERNEL);
>>> + if (mp->htpr == NULL)
>>> + return -ENOMEM;
>>> + }
>>> +
>>> + /* align to 8 byte boundary */
>>> + addr = (u8 *) (((u32) mp->htpr + 7) & ~0x7);
>>> + reg_dma = (dma_addr_t) (((u32) mp->htpr_dma + 7) & ~0x7);
>>
>> DMA-API-HOWTO.txt says about dma_alloc_coherent():
>>
>> The cpu return address and the DMA bus master address are both
>> guaranteed to be aligned to the smallest PAGE_SIZE order which
>> is greater than or equal to the requested size.
>>
>> So this shuffling isn't needed.
>
> Hm, removed the shuffling code.
>>
>>
>> etc etc.
^ permalink raw reply
* RE: [PATCH] virtio_net: implements ethtool_ops.get_drvinfo
From: Loke, Chetan @ 2010-08-06 15:59 UTC (permalink / raw)
To: Ben Hutchings, Rusty Russell; +Cc: netdev, Michael S. Tsirkin, Taku Izumi
In-Reply-To: <1280980041.13192.628.camel@localhost>
> This information is already available generically through sysfs:
> basename $(readlink /sys/class/net/eth3/device)
> basename $(readlink /sys/class/net/eth3/device/driver)
>
> Given that, we should either recommend that people use that method
> instead
sysfs-rules.txt::"Accessing /sys/class/net/eth0/device is a bug in the application."
I understand Dave has accepted the patch already(which is perfect). I'm not being sarcastic but I really want to understand if I can access these nodes.
Chetan Loke
^ permalink raw reply
* RE: [PATCH] virtio_net: implements ethtool_ops.get_drvinfo
From: Ben Hutchings @ 2010-08-06 16:15 UTC (permalink / raw)
To: Loke, Chetan; +Cc: Rusty Russell, netdev, Michael S. Tsirkin, Taku Izumi
In-Reply-To: <D3F292ADF945FB49B35E96C94C2061B90C96F8B5@nsmail.netscout.com>
On Fri, 2010-08-06 at 11:59 -0400, Loke, Chetan wrote:
> > This information is already available generically through sysfs:
> > basename $(readlink /sys/class/net/eth3/device)
> > basename $(readlink /sys/class/net/eth3/device/driver)
> >
> > Given that, we should either recommend that people use that method
> > instead
>
> sysfs-rules.txt::"Accessing /sys/class/net/eth0/device is a bug in the application."
>
> I understand Dave has accepted the patch already(which is perfect).
> I'm not being sarcastic but I really want to understand if I can
> access these nodes.
You should probably believe that document rather than me.
Ben.
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply
* [PATCH] solos-pci: Fix race condition in tasklet RX handling
From: David Woodhouse @ 2010-08-06 16:17 UTC (permalink / raw)
To: davem; +Cc: netdev, Nathan Williams, linux-atm-general
In-Reply-To: <1274872584.20576.13579.camel@macbook.infradead.org>
We were seeing faults in the solos-pci receive tasklet when packets
arrived for a VCC which was currently being closed:
[18842.727906] EIP: [<e082f490>] br2684_push+0x19/0x234 [br2684] SS:ESP 0068:dfb89d14
[18845.090712] [<c13ecff3>] ? do_page_fault+0x0/0x2e1
[18845.120042] [<e082f490>] ? br2684_push+0x19/0x234 [br2684]
[18845.153530] [<e084fa13>] solos_bh+0x28b/0x7c8 [solos_pci]
[18845.186488] [<e084f711>] ? solos_irq+0x2d/0x51 [solos_pci]
[18845.219960] [<c100387b>] ? handle_irq+0x3b/0x48
[18845.247732] [<c10265cb>] ? irq_exit+0x34/0x57
[18845.274437] [<c1025720>] tasklet_action+0x42/0x69
[18845.303247] [<c102643f>] __do_softirq+0x8e/0x129
[18845.331540] [<c10264ff>] do_softirq+0x25/0x2a
[18845.358274] [<c102664c>] _local_bh_enable_ip+0x5e/0x6a
[18845.389677] [<c102666d>] local_bh_enable+0xb/0xe
[18845.417944] [<e08490a8>] ppp_unregister_channel+0x32/0xbb [ppp_generic]
[18845.458193] [<e08731ad>] pppox_unbind_sock+0x18/0x1f [pppox]
This patch uses an RCU-inspired approach to fix it. In the RX tasklet's
find_vcc() function we first refuse to use a VCC which already has the
ATM_VF_READY bit cleared. And in the VCC close function, we synchronise
with the tasklet to ensure that it can't still be using the VCC before
we continue and allow the VCC to be destroyed.
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Tested-by: Nathan Williams <nathan@traverse.com.au>
Cc: stable@kernel.org
---
Nathan, you probably still ought to work out why your customer's setup
keeps disconnecting and closing the connection -- under normal operation
on a stable DSL line, this race should almost never have been triggered.
diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c
index c5f5186..a73f102 100644
--- a/drivers/atm/solos-pci.c
+++ b/drivers/atm/solos-pci.c
@@ -774,7 +774,8 @@ static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci)
sk_for_each(s, node, head) {
vcc = atm_sk(s);
if (vcc->dev == dev && vcc->vci == vci &&
- vcc->vpi == vpi && vcc->qos.rxtp.traffic_class != ATM_NONE)
+ vcc->vpi == vpi && vcc->qos.rxtp.traffic_class != ATM_NONE &&
+ test_bit(ATM_VF_READY, &vcc->flags))
goto out;
}
vcc = NULL;
@@ -900,6 +901,10 @@ static void pclose(struct atm_vcc *vcc)
clear_bit(ATM_VF_ADDR, &vcc->flags);
clear_bit(ATM_VF_READY, &vcc->flags);
+ /* Hold up vcc_destroy_socket() (our caller) until solos_bh() in the
+ tasklet has finished processing any incoming packets (and, more to
+ the point, using the vcc pointer). */
+ tasklet_unlock_wait(&card->tlet);
return;
}
--
David Woodhouse Open Source Technology Centre
David.Woodhouse@intel.com Intel Corporation
^ permalink raw reply related
* RE: [PATCH] net: add Fast Ethernet driver for PXA168.
From: Sachin Sanap @ 2010-08-06 16:55 UTC (permalink / raw)
To: Philip Rakity
Cc: Lennert Buytenhek, netdev@vger.kernel.org, Ashish Karkare,
Prabhanjan Sarnaik, eric.y.miao@gmail.com, Mark Brown
In-Reply-To: <26AE03C2-9D1A-4E63-82E3-DD47BD451B70@marvell.com>
>
> Sachin,
>
> You should change
>
> >>> + skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN);
>
> >>
>
> to allocatie ADDITIONAL prepend space in the header. If you are doing
> ethernet to 802.11 routing or bridging then the wireless driver will not
> have enough space in front of the header to prepend its headers without
> having to allocate additional space. You should check what 802.11n
> requires. 64 is okay for 11b/g. I would imagine doing something like this.
Yes in most of the cases it will be Ethernet to 802.11 routing or bridging. I will add a extra space and then send the patch.
-Sachin
>
> #define EXTRA_PREPEND_SPACE 64 /* extra header space for wireless
> headers */
>
> >>> skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN +
> EXTRA_PREPEND_SPACE);
>
> > + skb_reserve(skb, ETH_HW_IP_ALIGN + EXTRA_PREPEND_SPACE);
>
> etc
>
> Philip
>
>
> On Aug 6, 2010, at 8:14 AM, Sachin Sanap wrote:
>
> > Thanks Lennert,Eric for your review. Comments inline. I will resend the
> modified patch as a reply to this email.
> >>
> >> On Wed, Aug 04, 2010 at 04:17:50PM +0530, Sachin Sanap wrote:
> >>
> >>> This patch adds support for PXA168 Fast Ethernet on Aspenite
> >>> board.
> >>
> >> There's nothing Aspenite-specific in this patch (nor should there be),
> >> so you can leave that part out.
> >
> > Removed.
> >>
> >>
> >>> Patch generated against Linux 2.6.35-rc5
> >>> commit cd5b8f8755a89a57fc8c408d284b8b613f090345
> >>
> >> This might be interesting to know but shouldn't be part of the commit
> >> message.
> >
> > Removed.
> >>
> >>
> >>> diff --git a/arch/arm/mach-mmp/include/mach/pxa168_eth.h
> >> b/arch/arm/mach-mmp/include/mach/pxa168_eth.h
> >>> new file mode 100644
> >>> index 0000000..abfd335
> >>> --- /dev/null
> >>> +++ b/arch/arm/mach-mmp/include/mach/pxa168_eth.h
> >>> @@ -0,0 +1,18 @@
> >>> +/*
> >>> + *pxa168 ethernet platform device data definition file.
> >>> + */
> >>> +#ifndef __LINUX_PXA168_ETH_H
> >>> +#define __LINUX_PXA168_ETH_H
> >>
> >> This should just be in include/linux, IMHO, as this unit isn't only
> used
> >> in the PXA168, for one.
> > I have moved it to include/linux but the PXA168 name is still there.
> >
> >>
> >>
> >>> +struct pxa168_eth_platform_data {
> >>> + int port_number;
> >>> + u16 phy_addr;
> >>> +
> >>> + /* If speed is 0, then speed and duplex are autonegotiated. */
> >>> + u32 speed; /* 0, SPEED_10, SPEED_100 */
> >>> + u32 duplex; /* DUPLEX_HALF or DUPLEX_FULL */
> >>
> >> phylib treats these three (phy address, speed and duplex) as ints, is
> >> there any reason you need these to be of different types?
> >
> >
> > Used int at all three places.
> >
> >>
> >>
> >>> + int (*init)(void);
> >>
> >> What's this needed for? The name of this callback is entirely
> >> nondescriptive, there's no comment as to when it's called, etc.
> >
> > Added a comment for it.
> >>
> >>
> >>> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> >>> index ce2fcdd..5ebf287 100644
> >>> --- a/drivers/net/Kconfig
> >>> +++ b/drivers/net/Kconfig
> >>> @@ -927,6 +927,16 @@ config SMC91X
> >>> The module will be called smc91x. If you want to compile it as a
> >>> module, say M here and read
> >> <file:Documentation/kbuild/modules.txt>.
> >>>
> >>> +config PXA168_ETH
> >>> + tristate "Marvell pxa168 ethernet support"
> >>> + depends on MACH_ASPENITE
> >>
> >> You can have it depend on ARM or PXA168, but having it depend on
> support
> >> for one specific board support file is almost certainly wrong.
> >
> > Depends on CPU_PXA168.
> >>
> >>
> >>> diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c
> >>> new file mode 100644
> >>> index 0000000..618e558
> >>> --- /dev/null
> >>> +++ b/drivers/net/pxa168_eth.c
> >>> @@ -0,0 +1,1723 @@
> >>> +/*
> >>> + * Driver for PXA168 based boards.
> >>
> >> Why not call this "PXA168 ethernet driver"?
> > Change done.
> >>
> >>
> >>> + * Based on MV643XX driver.
> >>
> >> The mv643xx ethernet driver, that is.
> > Change done.
> >>
> >>
> >>> +#define DRIVER_NAME "pxa168-mfu"
> >>
> >> mfu?
> > Its was for multi function unit on Aspenite, changed it to pxa168-eth
> >>
> >>
> >>> +/* smi register */
> >>> +#define SMI_BUSY (1<<28) /* 0 - Write, 1 - Read */
> >>> +#define SMI_R_VALID (1<<27) /* 0 - Write, 1 - Read */
> >>> +#define SMI_OP_W (0<<26) /* Write operation */
> >>> +#define SMI_OP_R (1<<26) /* Read operation */
> >>
> >> (1 << 28)
> >> (1 << 27)
> >> (0 << 26)
> >>
> >> etc (thoughout the file)
> > Changed.
> >
> >>
> >>
> >>> +struct pxa168_private {
> >>
> >> ITYM pxa168_eth_private
> > Yes, changed.
> >>
> >>
> >>> + /*
> >>> + * Used in case RX Ring is empty, which can be caused when
> >>> + * system does not have resources (skb's)
> >>> + */
> >>
> >> can be caused by
> >> can occur when
> > Changed.
> >>
> >>
> >>> + struct timer_list timeout;
> >>> + struct mii_bus *smi_bus;
> >>> + struct phy_device *phy;
> >>> +
> >>> + /* clock */
> >>> + struct clk *clk;
> >>> + struct pxa168_eth_platform_data *pd;
> >>> + /*
> >>> + * Ethernet controller base address.
> >>> + */
> >>> + void __iomem *base;
> >>> +
> >>> + u8 *htpr; /* hash pointer */
> >>
> >> IMHO it'd look better if you'd either always put the comment on the
> same
> >> line or put the comment above the member (within the same struct).
> >>
> >> Also, 'hash pointer' as a comment isn't very helpful. Making it
> >> 'hardware address filter table' or so would be more descriptive.
> > Changed.
> >>
> >>
> >>> +struct addr_table_entry {
> >>> + u32 lo;
> >>> + u32 hi;
> >>> +};
> >>
> >> If the address filter table consists of these structures, it's
> >> confusing to make htpr a u8 * and then cast things around. Better make
> >> htpr a void * then.
> > Changed.
> >>
> >> Also, what about endian-cleanness? If you run the CPU core in big
> >> endian, will the address filter table handling logic in this driver
> >> still work?
> >
> >
> > Added cpu_to_le32 for the mac_high and mac_low variables before passing
> them to the add_del_hash_entry. I have not tested it on the Big endian but
> I think it should now work.
> >
> >>
> >>
> >>> +static char marvell_OUI[3] = { 0x02, 0x50, 0x43 };
> >>
> >> This isn't even a marvell OUI -- 00:50:43 is a marvell OUI, and
> >> anything with 02: is a locally generated address.
> >>
> >> Why don't you just use random_ether_addr()?
> > Changed to use random_ether_addr.
> >>
> >>
> >>> +static inline u32 rdl(struct pxa168_private *mp, int offset)
> >>> +{
> >>> + return readl(mp->base + offset);
> >>> +}
> >>
> >> In mv643xx_eth, 'mp' refers to mv643xx_eth_private, it's funny that
> >> you've changed the name of the struct but not that of the variable.
> >>
> >> As the rest of the driver looks very very similar to mv643xx_eth, if
> >> not pretty much entirely identical in a lot of places, you should give
> >> more credit to mv643xx_eth than 'based on', in my opinion.
> > Changed the credit line for the same.
> >>
> >>
> >>> +static int ethernet_phy_get(struct pxa168_private *mp)
> >>> +{
> >>> + unsigned int reg_data;
> >>> +
> >>> + /* only support 3 ports */
> >>> + BUG_ON(mp->port_num > 2);
> >>
> >> Hm, does it actually support 3 ports?
> > No. changed the code.
> >>
> >>
> >>> +static void ethernet_phy_set_addr(struct pxa168_private *mp, int
> >> phy_addr)
> >>> +{
> >>> + u32 reg_data;
> >>> + int addr_shift = 5 * mp->port_num;
> >>> +
> >>> + /* only support 3 ports */
> >>> + BUG_ON(mp->port_num > 2);
> >>> +
> >>> + reg_data = rdl(mp, PHY_ADDRESS);
> >>> + reg_data &= ~(0x1f << addr_shift);
> >>> + reg_data |= (phy_addr & 0x1f) << addr_shift;
> >>> + wrl(mp, PHY_ADDRESS, reg_data);
> >>> +}
> >>> +static void ethernet_phy_reset(struct pxa168_private *mp)
> >>> +{
> >>
> >> Blank line between functions.
> >>
> >>
> >>> + do {
> >>> + data = phy_read(mp->phy, MII_BMCR);
> >>> + } while (data >= 0 && data & BMCR_RESET);
> >>> +
> >>> +}
> >>
> >> No blank line here.
> >>
> >>
> >>> +static void rxq_refill(struct net_device *dev)
> >>> +{
> >>> + struct pxa168_private *mp = netdev_priv(dev);
> >>> + struct sk_buff *skb;
> >>> + struct rx_desc *p_used_rx_desc;
> >>> + int used_rx_desc;
> >>> +
> >>> + while (mp->rx_desc_count < mp->rx_ring_size) {
> >>> +
> >>> + skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN);
> >>
> >> No blank line here.
> >>
> >>
> >>> + if (mp->rx_desc_count == 0) {
> >>> + printk(KERN_INFO "%s: Rx ring is empty\n", dev->name);
> >>
> >> I wouldn't printk for this (and certainly not without a rate limit) --
> >> going OOM will then just mean that you'll get endless console spam as
> >> well, which isn't helpful.
> > Hm, removed the printk.
> >
> >>
> >>
> >>> +/*
> >>> + * ------------------------------------------------------------------
> --
> >> --------
> >>> + * This function will calculate the hash function of the address.
> >>> + * depends on the hash mode and hash size.
> >>
> >> Where are the hash mode and size configured?
> >>
> >>
> >>> +static u32 hash_function(u32 mac_high, u32 mac_low)
> >>> +{
> >>> + u32 hash_result;
> >>> + u32 addr_high;
> >>> + u32 addr_low;
> >>> + u32 addr0;
> >>> + u32 addr1;
> >>> + u32 addr2;
> >>> + u32 addr3;
> >>> + u32 addr_high_swapped;
> >>> + u32 addr_low_swapped;
> >>> +
> >>> + addr_high = nibble_swapping_16_bit(mac_high);
> >>> + addr_low = nibble_swapping_32_bit(mac_low);
> >>> +
> >>> + addr_high_swapped = flip_4_bits(addr_high & 0xf)
> >>> + + ((flip_4_bits((addr_high >> 4) & 0xf)) << 4)
> >>> + + ((flip_4_bits((addr_high >> 8) & 0xf)) << 8)
> >>> + + ((flip_4_bits((addr_high >> 12) & 0xf)) << 12);
> >>> +
> >>> + addr_low_swapped = flip_4_bits(addr_low & 0xf)
> >>> + + ((flip_4_bits((addr_low >> 4) & 0xf)) << 4)
> >>> + + ((flip_4_bits((addr_low >> 8) & 0xf)) << 8)
> >>> + + ((flip_4_bits((addr_low >> 12) & 0xf)) << 12)
> >>> + + ((flip_4_bits((addr_low >> 16) & 0xf)) << 16)
> >>> + + ((flip_4_bits((addr_low >> 20) & 0xf)) << 20)
> >>> + + ((flip_4_bits((addr_low >> 24) & 0xf)) << 24)
> >>> + + ((flip_4_bits((addr_low >> 28) & 0xf)) << 28);
> >>> +
> >>> + addr_high = addr_high_swapped;
> >>> + addr_low = addr_low_swapped;
> >>> +
> >>> + addr0 = (addr_low >> 2) & 0x03f;
> >>> + addr1 = (addr_low & 0x003) | ((addr_low >> 8) & 0x7f) << 2;
> >>> + addr2 = (addr_low >> 15) & 0x1ff;
> >>> + addr3 = ((addr_low >> 24) & 0x0ff) | ((addr_high & 1) << 8);
> >>> +
> >>> + hash_result = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
> >>> + hash_result = hash_result & 0x07ff;
> >>> + return hash_result;
> >>
> >> As it's only a bunch of XORs, can you not calculate the hash function
> >> first and only then do the bit reversal?
> >>
> >>
> >>> + * ------------------------------------------------------------------
> --
> >> --------
> >>> + * This function will add an entry to the address table.
> >>> + * depends on the hash mode and hash size that was initialized.
> >>> + * Inputs
> >>> + * mp - ETHERNET .
> >>> + * mac_high - the 2 most significant bytes of the MAC address.
> >>> + * mac_low - the 4 least significant bytes of the MAC address.
> >>> + * skip - if 1, skip this address.
> >>
> >> What does 'skip' do?
> >>
> >>
> >>> + * rd - the RD field in the address table.
> >>
> >> And what does that do?
> >
> > Added more comments regarding the address filtering and the hash
> function.
> >
> >>
> >> If one has hardware docs, this comment doesn't really add anything,
> >> while if one doesn't have hardware docs, this comment doesn't really
> >> explain anything -- so the comment isn't very useful.
> >>
> >>
> >>> +static void update_hash_table_mac_address(struct pxa168_private *mp,
> >>> + u8 *oaddr, u8 *addr)
> >>> [...]
> >>> +static int init_hashtable(struct pxa168_private *mp)
> >>
> >> hash_table?
> >>
> >>
> >>> +{
> >>> + u8 *addr;
> >>> + dma_addr_t reg_dma;
> >>> +
> >>> + if (mp->htpr == NULL) {
> >>> + mp->htpr = dma_alloc_coherent(NULL,
> >>> + HASH_ADDR_TABLE_SIZE + 7,
> >>> + &mp->htpr_dma, GFP_KERNEL);
> >>> + if (mp->htpr == NULL)
> >>> + return -ENOMEM;
> >>> + }
> >>> +
> >>> + /* align to 8 byte boundary */
> >>> + addr = (u8 *) (((u32) mp->htpr + 7) & ~0x7);
> >>> + reg_dma = (dma_addr_t) (((u32) mp->htpr_dma + 7) & ~0x7);
> >>
> >> DMA-API-HOWTO.txt says about dma_alloc_coherent():
> >>
> >> The cpu return address and the DMA bus master address are both
> >> guaranteed to be aligned to the smallest PAGE_SIZE order which
> >> is greater than or equal to the requested size.
> >>
> >> So this shuffling isn't needed.
> >
> > Hm, removed the shuffling code.
> >>
> >>
> >> etc etc.
^ permalink raw reply
* RE: [PATCH] net: add Fast Ethernet driver for PXA168.
From: Joe Perches @ 2010-08-06 17:02 UTC (permalink / raw)
To: Sachin Sanap
Cc: Philip Rakity, Lennert Buytenhek, netdev@vger.kernel.org,
Ashish Karkare, Prabhanjan Sarnaik, eric.y.miao@gmail.com,
Mark Brown
In-Reply-To: <EC64E4604C8024438259B683BB2514E1082B1CB20F@SC-VEXCH1.marvell.com>
On Fri, 2010-08-06 at 09:55 -0700, Sachin Sanap wrote:
> > >>> + skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN);
> > >>> skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN + EXTRA_PREPEND_SPACE);
You could also use:
skb = netdev_alloc_skb_ip_align(dev, MAX_PKT_SIZE + EXTRA_PREPEND_SPACE)
^ permalink raw reply
* Re: [PATCH] net: add Fast Ethernet driver for PXA168.
From: Philip Rakity @ 2010-08-06 18:02 UTC (permalink / raw)
To: Sachin Sanap
Cc: Lennert Buytenhek, netdev@vger.kernel.org, Ashish Karkare,
Prabhanjan Sarnaik, eric.y.miao@gmail.com, Mark Brown
In-Reply-To: <EC64E4604C8024438259B683BB2514E1082B1CB1B0@SC-VEXCH1.marvell.com>
Sachin,
I assume TX_DONE_INTERVAL is related to the number of tx descriptors in the ring and if so should be defined from the common base size .
+#define TX_DONE_INTERVAL 30
eg
#define TX_DONE_INTERVAL (NUM_TX_DESCS/2)
Philp
On Aug 6, 2010, at 8:14 AM, Sachin Sanap wrote:
> Thanks Lennert,Eric for your review. Comments inline. I will resend the modified patch as a reply to this email.
>>
>> On Wed, Aug 04, 2010 at 04:17:50PM +0530, Sachin Sanap wrote:
>>
>>> This patch adds support for PXA168 Fast Ethernet on Aspenite
>>> board.
>>
>> There's nothing Aspenite-specific in this patch (nor should there be),
>> so you can leave that part out.
>
> Removed.
>>
>>
>>> Patch generated against Linux 2.6.35-rc5
>>> commit cd5b8f8755a89a57fc8c408d284b8b613f090345
>>
>> This might be interesting to know but shouldn't be part of the commit
>> message.
>
> Removed.
>>
>>
>>> diff --git a/arch/arm/mach-mmp/include/mach/pxa168_eth.h
>> b/arch/arm/mach-mmp/include/mach/pxa168_eth.h
>>> new file mode 100644
>>> index 0000000..abfd335
>>> --- /dev/null
>>> +++ b/arch/arm/mach-mmp/include/mach/pxa168_eth.h
>>> @@ -0,0 +1,18 @@
>>> +/*
>>> + *pxa168 ethernet platform device data definition file.
>>> + */
>>> +#ifndef __LINUX_PXA168_ETH_H
>>> +#define __LINUX_PXA168_ETH_H
>>
>> This should just be in include/linux, IMHO, as this unit isn't only used
>> in the PXA168, for one.
> I have moved it to include/linux but the PXA168 name is still there.
>
>>
>>
>>> +struct pxa168_eth_platform_data {
>>> + int port_number;
>>> + u16 phy_addr;
>>> +
>>> + /* If speed is 0, then speed and duplex are autonegotiated. */
>>> + u32 speed; /* 0, SPEED_10, SPEED_100 */
>>> + u32 duplex; /* DUPLEX_HALF or DUPLEX_FULL */
>>
>> phylib treats these three (phy address, speed and duplex) as ints, is
>> there any reason you need these to be of different types?
>
>
> Used int at all three places.
>
>>
>>
>>> + int (*init)(void);
>>
>> What's this needed for? The name of this callback is entirely
>> nondescriptive, there's no comment as to when it's called, etc.
>
> Added a comment for it.
>>
>>
>>> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
>>> index ce2fcdd..5ebf287 100644
>>> --- a/drivers/net/Kconfig
>>> +++ b/drivers/net/Kconfig
>>> @@ -927,6 +927,16 @@ config SMC91X
>>> The module will be called smc91x. If you want to compile it as a
>>> module, say M here and read
>> <file:Documentation/kbuild/modules.txt>.
>>>
>>> +config PXA168_ETH
>>> + tristate "Marvell pxa168 ethernet support"
>>> + depends on MACH_ASPENITE
>>
>> You can have it depend on ARM or PXA168, but having it depend on support
>> for one specific board support file is almost certainly wrong.
>
> Depends on CPU_PXA168.
>>
>>
>>> diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c
>>> new file mode 100644
>>> index 0000000..618e558
>>> --- /dev/null
>>> +++ b/drivers/net/pxa168_eth.c
>>> @@ -0,0 +1,1723 @@
>>> +/*
>>> + * Driver for PXA168 based boards.
>>
>> Why not call this "PXA168 ethernet driver"?
> Change done.
>>
>>
>>> + * Based on MV643XX driver.
>>
>> The mv643xx ethernet driver, that is.
> Change done.
>>
>>
>>> +#define DRIVER_NAME "pxa168-mfu"
>>
>> mfu?
> Its was for multi function unit on Aspenite, changed it to pxa168-eth
>>
>>
>>> +/* smi register */
>>> +#define SMI_BUSY (1<<28) /* 0 - Write, 1 - Read */
>>> +#define SMI_R_VALID (1<<27) /* 0 - Write, 1 - Read */
>>> +#define SMI_OP_W (0<<26) /* Write operation */
>>> +#define SMI_OP_R (1<<26) /* Read operation */
>>
>> (1 << 28)
>> (1 << 27)
>> (0 << 26)
>>
>> etc (thoughout the file)
> Changed.
>
>>
>>
>>> +struct pxa168_private {
>>
>> ITYM pxa168_eth_private
> Yes, changed.
>>
>>
>>> + /*
>>> + * Used in case RX Ring is empty, which can be caused when
>>> + * system does not have resources (skb's)
>>> + */
>>
>> can be caused by
>> can occur when
> Changed.
>>
>>
>>> + struct timer_list timeout;
>>> + struct mii_bus *smi_bus;
>>> + struct phy_device *phy;
>>> +
>>> + /* clock */
>>> + struct clk *clk;
>>> + struct pxa168_eth_platform_data *pd;
>>> + /*
>>> + * Ethernet controller base address.
>>> + */
>>> + void __iomem *base;
>>> +
>>> + u8 *htpr; /* hash pointer */
>>
>> IMHO it'd look better if you'd either always put the comment on the same
>> line or put the comment above the member (within the same struct).
>>
>> Also, 'hash pointer' as a comment isn't very helpful. Making it
>> 'hardware address filter table' or so would be more descriptive.
> Changed.
>>
>>
>>> +struct addr_table_entry {
>>> + u32 lo;
>>> + u32 hi;
>>> +};
>>
>> If the address filter table consists of these structures, it's
>> confusing to make htpr a u8 * and then cast things around. Better make
>> htpr a void * then.
> Changed.
>>
>> Also, what about endian-cleanness? If you run the CPU core in big
>> endian, will the address filter table handling logic in this driver
>> still work?
>
>
> Added cpu_to_le32 for the mac_high and mac_low variables before passing them to the add_del_hash_entry. I have not tested it on the Big endian but I think it should now work.
>
>>
>>
>>> +static char marvell_OUI[3] = { 0x02, 0x50, 0x43 };
>>
>> This isn't even a marvell OUI -- 00:50:43 is a marvell OUI, and
>> anything with 02: is a locally generated address.
>>
>> Why don't you just use random_ether_addr()?
> Changed to use random_ether_addr.
>>
>>
>>> +static inline u32 rdl(struct pxa168_private *mp, int offset)
>>> +{
>>> + return readl(mp->base + offset);
>>> +}
>>
>> In mv643xx_eth, 'mp' refers to mv643xx_eth_private, it's funny that
>> you've changed the name of the struct but not that of the variable.
>>
>> As the rest of the driver looks very very similar to mv643xx_eth, if
>> not pretty much entirely identical in a lot of places, you should give
>> more credit to mv643xx_eth than 'based on', in my opinion.
> Changed the credit line for the same.
>>
>>
>>> +static int ethernet_phy_get(struct pxa168_private *mp)
>>> +{
>>> + unsigned int reg_data;
>>> +
>>> + /* only support 3 ports */
>>> + BUG_ON(mp->port_num > 2);
>>
>> Hm, does it actually support 3 ports?
> No. changed the code.
>>
>>
>>> +static void ethernet_phy_set_addr(struct pxa168_private *mp, int
>> phy_addr)
>>> +{
>>> + u32 reg_data;
>>> + int addr_shift = 5 * mp->port_num;
>>> +
>>> + /* only support 3 ports */
>>> + BUG_ON(mp->port_num > 2);
>>> +
>>> + reg_data = rdl(mp, PHY_ADDRESS);
>>> + reg_data &= ~(0x1f << addr_shift);
>>> + reg_data |= (phy_addr & 0x1f) << addr_shift;
>>> + wrl(mp, PHY_ADDRESS, reg_data);
>>> +}
>>> +static void ethernet_phy_reset(struct pxa168_private *mp)
>>> +{
>>
>> Blank line between functions.
>>
>>
>>> + do {
>>> + data = phy_read(mp->phy, MII_BMCR);
>>> + } while (data >= 0 && data & BMCR_RESET);
>>> +
>>> +}
>>
>> No blank line here.
>>
>>
>>> +static void rxq_refill(struct net_device *dev)
>>> +{
>>> + struct pxa168_private *mp = netdev_priv(dev);
>>> + struct sk_buff *skb;
>>> + struct rx_desc *p_used_rx_desc;
>>> + int used_rx_desc;
>>> +
>>> + while (mp->rx_desc_count < mp->rx_ring_size) {
>>> +
>>> + skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN);
>>
>> No blank line here.
>>
>>
>>> + if (mp->rx_desc_count == 0) {
>>> + printk(KERN_INFO "%s: Rx ring is empty\n", dev->name);
>>
>> I wouldn't printk for this (and certainly not without a rate limit) --
>> going OOM will then just mean that you'll get endless console spam as
>> well, which isn't helpful.
> Hm, removed the printk.
>
>>
>>
>>> +/*
>>> + * --------------------------------------------------------------------
>> --------
>>> + * This function will calculate the hash function of the address.
>>> + * depends on the hash mode and hash size.
>>
>> Where are the hash mode and size configured?
>>
>>
>>> +static u32 hash_function(u32 mac_high, u32 mac_low)
>>> +{
>>> + u32 hash_result;
>>> + u32 addr_high;
>>> + u32 addr_low;
>>> + u32 addr0;
>>> + u32 addr1;
>>> + u32 addr2;
>>> + u32 addr3;
>>> + u32 addr_high_swapped;
>>> + u32 addr_low_swapped;
>>> +
>>> + addr_high = nibble_swapping_16_bit(mac_high);
>>> + addr_low = nibble_swapping_32_bit(mac_low);
>>> +
>>> + addr_high_swapped = flip_4_bits(addr_high & 0xf)
>>> + + ((flip_4_bits((addr_high >> 4) & 0xf)) << 4)
>>> + + ((flip_4_bits((addr_high >> 8) & 0xf)) << 8)
>>> + + ((flip_4_bits((addr_high >> 12) & 0xf)) << 12);
>>> +
>>> + addr_low_swapped = flip_4_bits(addr_low & 0xf)
>>> + + ((flip_4_bits((addr_low >> 4) & 0xf)) << 4)
>>> + + ((flip_4_bits((addr_low >> 8) & 0xf)) << 8)
>>> + + ((flip_4_bits((addr_low >> 12) & 0xf)) << 12)
>>> + + ((flip_4_bits((addr_low >> 16) & 0xf)) << 16)
>>> + + ((flip_4_bits((addr_low >> 20) & 0xf)) << 20)
>>> + + ((flip_4_bits((addr_low >> 24) & 0xf)) << 24)
>>> + + ((flip_4_bits((addr_low >> 28) & 0xf)) << 28);
>>> +
>>> + addr_high = addr_high_swapped;
>>> + addr_low = addr_low_swapped;
>>> +
>>> + addr0 = (addr_low >> 2) & 0x03f;
>>> + addr1 = (addr_low & 0x003) | ((addr_low >> 8) & 0x7f) << 2;
>>> + addr2 = (addr_low >> 15) & 0x1ff;
>>> + addr3 = ((addr_low >> 24) & 0x0ff) | ((addr_high & 1) << 8);
>>> +
>>> + hash_result = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
>>> + hash_result = hash_result & 0x07ff;
>>> + return hash_result;
>>
>> As it's only a bunch of XORs, can you not calculate the hash function
>> first and only then do the bit reversal?
>>
>>
>>> + * --------------------------------------------------------------------
>> --------
>>> + * This function will add an entry to the address table.
>>> + * depends on the hash mode and hash size that was initialized.
>>> + * Inputs
>>> + * mp - ETHERNET .
>>> + * mac_high - the 2 most significant bytes of the MAC address.
>>> + * mac_low - the 4 least significant bytes of the MAC address.
>>> + * skip - if 1, skip this address.
>>
>> What does 'skip' do?
>>
>>
>>> + * rd - the RD field in the address table.
>>
>> And what does that do?
>
> Added more comments regarding the address filtering and the hash function.
>
>>
>> If one has hardware docs, this comment doesn't really add anything,
>> while if one doesn't have hardware docs, this comment doesn't really
>> explain anything -- so the comment isn't very useful.
>>
>>
>>> +static void update_hash_table_mac_address(struct pxa168_private *mp,
>>> + u8 *oaddr, u8 *addr)
>>> [...]
>>> +static int init_hashtable(struct pxa168_private *mp)
>>
>> hash_table?
>>
>>
>>> +{
>>> + u8 *addr;
>>> + dma_addr_t reg_dma;
>>> +
>>> + if (mp->htpr == NULL) {
>>> + mp->htpr = dma_alloc_coherent(NULL,
>>> + HASH_ADDR_TABLE_SIZE + 7,
>>> + &mp->htpr_dma, GFP_KERNEL);
>>> + if (mp->htpr == NULL)
>>> + return -ENOMEM;
>>> + }
>>> +
>>> + /* align to 8 byte boundary */
>>> + addr = (u8 *) (((u32) mp->htpr + 7) & ~0x7);
>>> + reg_dma = (dma_addr_t) (((u32) mp->htpr_dma + 7) & ~0x7);
>>
>> DMA-API-HOWTO.txt says about dma_alloc_coherent():
>>
>> The cpu return address and the DMA bus master address are both
>> guaranteed to be aligned to the smallest PAGE_SIZE order which
>> is greater than or equal to the requested size.
>>
>> So this shuffling isn't needed.
>
> Hm, removed the shuffling code.
>>
>>
>> etc etc.
^ permalink raw reply
* [09/38] xen: netfront: explicitly generate arp_notify event after migration.
From: Greg KH @ 2010-08-06 18:30 UTC (permalink / raw)
To: linux-kernel, stable
Cc: Jeremy Fitzhardinge, xen-devel, Ian Campbell, torvalds,
David S. Miller, netdev, akpm, Stephen Hemminger, stable-review,
alan
In-Reply-To: <20100806183250.GA23019@kroah.com>
2.6.35-stable review patch. If anyone has any objections, please let us know.
------------------
From: Ian Campbell <Ian.Campbell@citrix.com>
commit 592970675c9522bde588b945388c7995c8b51328 upstream.
Use newly introduced netif_notify_peers() method to ensure a gratuitous ARP is
generated after a migration.
Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
Cc: Stephen Hemminger <shemminger@linux-foundation.org>
Cc: Jeremy Fitzhardinge <jeremy@goop.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: netdev@vger.kernel.org
Cc: xen-devel@lists.xensource.com
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
drivers/net/xen-netfront.c | 1 +
1 file changed, 1 insertion(+)
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -1621,6 +1621,7 @@ static void backend_changed(struct xenbu
if (xennet_connect(netdev) != 0)
break;
xenbus_switch_state(dev, XenbusStateConnected);
+ netif_notify_peers(netdev);
break;
case XenbusStateClosing:
^ permalink raw reply
* [08/38] arp_notify: allow drivers to explicitly request a notification event.
From: Greg KH @ 2010-08-06 18:30 UTC (permalink / raw)
To: linux-kernel, stable
Cc: stable-review, torvalds, akpm, alan, Ian Campbell,
Stephen Hemminger, Jeremy Fitzhardinge, David S. Miller, netdev
In-Reply-To: <20100806183250.GA23019@kroah.com>
2.6.35-stable review patch. If anyone has any objections, please let us know.
------------------
From: Ian Campbell <Ian.Campbell@citrix.com>
commit 06c4648d46d1b757d6b9591a86810be79818b60c upstream.
Currently such notifications are only generated when the device comes up or the
address changes. However one use case for these notifications is to enable
faster network recovery after a virtual machine migration (by causing switches
to relearn their MAC tables). A migration appears to the network stack as a
temporary loss of carrier and therefore does not trigger either of the current
conditions. Rather than adding carrier up as a trigger (which can cause issues
when interfaces a flapping) simply add an interface which the driver can use
to explicitly trigger the notification.
Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
Cc: Stephen Hemminger <shemminger@linux-foundation.org>
Cc: Jeremy Fitzhardinge <jeremy@goop.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: netdev@vger.kernel.org
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
include/linux/netdevice.h | 2 ++
include/linux/notifier.h | 1 +
net/ipv4/devinet.c | 1 +
net/sched/sch_generic.c | 18 ++++++++++++++++++
4 files changed, 22 insertions(+)
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1775,6 +1775,8 @@ extern void netif_carrier_on(struct net_
extern void netif_carrier_off(struct net_device *dev);
+extern void netif_notify_peers(struct net_device *dev);
+
/**
* netif_dormant_on - mark device as dormant.
* @dev: network device
--- a/include/linux/notifier.h
+++ b/include/linux/notifier.h
@@ -210,6 +210,7 @@ static inline int notifier_to_errno(int
#define NETDEV_POST_INIT 0x0010
#define NETDEV_UNREGISTER_BATCH 0x0011
#define NETDEV_BONDING_DESLAVE 0x0012
+#define NETDEV_NOTIFY_PEERS 0x0012
#define SYS_DOWN 0x0001 /* Notify of system down */
#define SYS_RESTART SYS_DOWN
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -1081,6 +1081,7 @@ static int inetdev_event(struct notifier
}
ip_mc_up(in_dev);
/* fall through */
+ case NETDEV_NOTIFY_PEERS:
case NETDEV_CHANGEADDR:
/* Send gratuitous ARP to notify of link change */
if (IN_DEV_ARP_NOTIFY(in_dev)) {
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -327,6 +327,24 @@ void netif_carrier_off(struct net_device
}
EXPORT_SYMBOL(netif_carrier_off);
+/**
+ * netif_notify_peers - notify network peers about existence of @dev
+ * @dev: network device
+ *
+ * Generate traffic such that interested network peers are aware of
+ * @dev, such as by generating a gratuitous ARP. This may be used when
+ * a device wants to inform the rest of the network about some sort of
+ * reconfiguration such as a failover event or virtual machine
+ * migration.
+ */
+void netif_notify_peers(struct net_device *dev)
+{
+ rtnl_lock();
+ call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, dev);
+ rtnl_unlock();
+}
+EXPORT_SYMBOL(netif_notify_peers);
+
/* "NOOP" scheduler: the best scheduler, recommended for all interfaces
under all circumstances. It is difficult to invent anything faster or
cheaper.
^ permalink raw reply
* pull request: wireless-2.6 2010-08-06
From: John W. Linville @ 2010-08-06 19:14 UTC (permalink / raw)
To: davem; +Cc: linux-wireless, netdev, linux-kernel
Dave,
Here is the first round of fixes intended for 2.6.36. It is bigger than
I would prefer, but I hope you will still find it acceptable, especially
as we are still so early in the cycle.
The largest batch of fixes are from the ath9k team. These are primarily
calibration fixes that they assure me fix some real connectivity
problems. The patch from Luis fixes a potential regulatory compliance
issue as well.
The next largest batch is from the libertas guys. This is necessary to
correct some association problems introduced by some of the patches
already sent for 2.6.36.
Also included are a couple of fixes for recently introduced build
warnings, a MAINTAINERS update, a PCI ID, and some minor adjustments to
some user-visible log SPAM.
The remainder are a variety of small fixes of varying significance.
This also includes a few Bluetooth fixes by way of Marcel Holtmann.
I think all of these are worthwhile to have merged at or before -rc1.
Please let me know if there are problems!
Thanks,
John
---
The following changes since commit 4b030d4288a569d6bdeca884d7f102d951f097f2:
isdn: fix information leak (2010-08-05 13:21:25 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6.git master
Andy Lutomirski (1):
iwlagn: Improve aggregation failure error messages
Christoph Fritz (1):
wireless: ipw2100: check result of kzalloc()
Dan Williams (3):
libertas: get the right # of scanned BSSes
libertas: fix association with some APs by using extended rates
libertas: scan before assocation if no BSSID was given
David Miller (1):
Bluetooth: Use list_head for HCI blacklist head
Felix Fietkau (7):
ath9k_hw: clean up and fix initial noise floor calibration
ath9k_hw: fix periodic noise floor calibration on AR9003
ath9k: fix a crash in the PA predistortion apply function
ath9k_hw: fix analog shift register writes on AR9003
ath9k: prevent calibration during off-channel activity
ath9k_hw: clean up per-channel calibration data
ath9k_hw: fix a noise floor calibration related race condition
Gustavo F. Padovan (2):
Bluetooth: Remove __exit from rfcomm_cleanup_ttys()
Bluetooth: Don't send RFC for Basic Mode if only it is supported
Jan Friedrich (1):
ath9k: fix erased ieee80211_rx_status.mactime
Johannes Berg (5):
iwlwifi: fix possible recursive locking deadlock
mac80211: fix scan locking wrt. hw scan
iwlwifi: fix compile warning
iwlwifi: fix TX tracer
iwlwifi: fix locking assertions
John W. Linville (2):
Merge branch 'master' of git://git.kernel.org/.../holtmann/bluetooth-next-2.6
rtl8180: avoid potential NULL deref in rtl8180_beacon_work
Juuso Oikarinen (1):
mac80211: Fix compilation warning when CONFIG_INET is not set
Kulikov Vasiliy (2):
net: wl12xx: do not use kfree'd memory
rt2x00: do not use PCI resources before pci_enable_device()
Larry Finger (1):
p54pci: Add PCI ID for SMC2802W
Lorenzo Bianconi (1):
ath9k: fix an issue in ath_atx_tid paused flag management
Luis R. Rodriguez (1):
ath9k_hw: Fix regulatory CTL index usage for AR9003
Sujith (1):
ath9k: Remove myself from the MAINTAINERS list
Ville Tervo (1):
Bluetooth: Check result code of L2CAP information response
Wey-Yi Guy (2):
iwlwifi: BA scd_flow not match condition detected
iwlagn: fix typo in ucode_bt_stats_read debugfs
MAINTAINERS | 1 -
drivers/net/wireless/ath/ath9k/ar9002_calib.c | 43 ++-
drivers/net/wireless/ath/ath9k/ar9003_calib.c | 18 +-
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 388 +++++++++++++++++++++++-
drivers/net/wireless/ath/ath9k/ar9003_paprd.c | 17 +-
drivers/net/wireless/ath/ath9k/ar9003_phy.c | 6 +-
drivers/net/wireless/ath/ath9k/ath9k.h | 3 +-
drivers/net/wireless/ath/ath9k/calib.c | 118 ++++----
drivers/net/wireless/ath/ath9k/calib.h | 8 +-
drivers/net/wireless/ath/ath9k/htc.h | 2 +
drivers/net/wireless/ath/ath9k/htc_drv_main.c | 10 +-
drivers/net/wireless/ath/ath9k/hw.c | 25 +-
drivers/net/wireless/ath/ath9k/hw.h | 25 +-
drivers/net/wireless/ath/ath9k/main.c | 104 ++++---
drivers/net/wireless/ath/ath9k/recv.c | 10 +-
drivers/net/wireless/ath/ath9k/xmit.c | 36 +--
drivers/net/wireless/ipw2x00/ipw2100.c | 4 +
drivers/net/wireless/iwlwifi/iwl-agn-debugfs.c | 2 +-
drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 2 +-
drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 11 +-
drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 11 +-
drivers/net/wireless/iwlwifi/iwl-core.c | 6 +-
drivers/net/wireless/iwlwifi/iwl-debug.h | 2 +-
drivers/net/wireless/iwlwifi/iwl-devtrace.h | 2 +-
drivers/net/wireless/iwlwifi/iwl-scan.c | 2 +-
drivers/net/wireless/iwlwifi/iwl-sta.c | 6 +-
drivers/net/wireless/libertas/cfg.c | 214 ++++++++++---
drivers/net/wireless/libertas/dev.h | 5 +
drivers/net/wireless/libertas/main.c | 1 +
drivers/net/wireless/p54/p54pci.c | 2 +
drivers/net/wireless/rt2x00/rt2x00pci.c | 21 +-
drivers/net/wireless/rtl818x/rtl8180_dev.c | 2 +
drivers/net/wireless/wl12xx/wl1271_spi.c | 3 +-
include/net/bluetooth/hci_core.h | 2 +-
net/bluetooth/hci_core.c | 2 +-
net/bluetooth/hci_sock.c | 8 +-
net/bluetooth/hci_sysfs.c | 3 +-
net/bluetooth/l2cap.c | 24 ++-
net/bluetooth/rfcomm/tty.c | 2 +-
net/mac80211/main.c | 2 +
net/mac80211/scan.c | 14 -
41 files changed, 866 insertions(+), 301 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index b04b97f..1dcedaa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1085,7 +1085,6 @@ F: drivers/net/wireless/ath/ath5k/
ATHEROS ATH9K WIRELESS DRIVER
M: "Luis R. Rodriguez" <lrodriguez@atheros.com>
M: Jouni Malinen <jmalinen@atheros.com>
-M: Sujith Manoharan <Sujith.Manoharan@atheros.com>
M: Vasanthakumar Thiagarajan <vasanth@atheros.com>
M: Senthil Balasubramanian <senthilkumar@atheros.com>
L: linux-wireless@vger.kernel.org
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
index dabafb8..fe7418a 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
@@ -63,6 +63,7 @@ static bool ar9002_hw_per_calibration(struct ath_hw *ah,
u8 rxchainmask,
struct ath9k_cal_list *currCal)
{
+ struct ath9k_hw_cal_data *caldata = ah->caldata;
bool iscaldone = false;
if (currCal->calState == CAL_RUNNING) {
@@ -81,14 +82,14 @@ static bool ar9002_hw_per_calibration(struct ath_hw *ah,
}
currCal->calData->calPostProc(ah, numChains);
- ichan->CalValid |= currCal->calData->calType;
+ caldata->CalValid |= currCal->calData->calType;
currCal->calState = CAL_DONE;
iscaldone = true;
} else {
ar9002_hw_setup_calibration(ah, currCal);
}
}
- } else if (!(ichan->CalValid & currCal->calData->calType)) {
+ } else if (!(caldata->CalValid & currCal->calData->calType)) {
ath9k_hw_reset_calibration(ah, currCal);
}
@@ -686,8 +687,13 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah,
{
bool iscaldone = true;
struct ath9k_cal_list *currCal = ah->cal_list_curr;
+ bool nfcal, nfcal_pending = false;
- if (currCal &&
+ nfcal = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF);
+ if (ah->caldata)
+ nfcal_pending = ah->caldata->nfcal_pending;
+
+ if (currCal && !nfcal &&
(currCal->calState == CAL_RUNNING ||
currCal->calState == CAL_WAITING)) {
iscaldone = ar9002_hw_per_calibration(ah, chan,
@@ -703,7 +709,7 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah,
}
/* Do NF cal only at longer intervals */
- if (longcal) {
+ if (longcal || nfcal_pending) {
/* Do periodic PAOffset Cal */
ar9002_hw_pa_cal(ah, false);
ar9002_hw_olc_temp_compensation(ah);
@@ -712,16 +718,18 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah,
* Get the value from the previous NF cal and update
* history buffer.
*/
- ath9k_hw_getnf(ah, chan);
-
- /*
- * Load the NF from history buffer of the current channel.
- * NF is slow time-variant, so it is OK to use a historical
- * value.
- */
- ath9k_hw_loadnf(ah, ah->curchan);
+ if (ath9k_hw_getnf(ah, chan)) {
+ /*
+ * Load the NF from history buffer of the current
+ * channel.
+ * NF is slow time-variant, so it is OK to use a
+ * historical value.
+ */
+ ath9k_hw_loadnf(ah, ah->curchan);
+ }
- ath9k_hw_start_nfcal(ah);
+ if (longcal)
+ ath9k_hw_start_nfcal(ah, false);
}
return iscaldone;
@@ -869,8 +877,10 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan)
ar9002_hw_pa_cal(ah, true);
/* Do NF Calibration after DC offset and other calibrations */
- REG_WRITE(ah, AR_PHY_AGC_CONTROL,
- REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_NF);
+ ath9k_hw_start_nfcal(ah, true);
+
+ if (ah->caldata)
+ ah->caldata->nfcal_pending = true;
ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL;
@@ -901,7 +911,8 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan)
ath9k_hw_reset_calibration(ah, ah->cal_list_curr);
}
- chan->CalValid = 0;
+ if (ah->caldata)
+ ah->caldata->CalValid = 0;
return true;
}
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
index 5a06503..4674ea8 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
@@ -68,6 +68,7 @@ static bool ar9003_hw_per_calibration(struct ath_hw *ah,
u8 rxchainmask,
struct ath9k_cal_list *currCal)
{
+ struct ath9k_hw_cal_data *caldata = ah->caldata;
/* Cal is assumed not done until explicitly set below */
bool iscaldone = false;
@@ -95,7 +96,7 @@ static bool ar9003_hw_per_calibration(struct ath_hw *ah,
currCal->calData->calPostProc(ah, numChains);
/* Calibration has finished. */
- ichan->CalValid |= currCal->calData->calType;
+ caldata->CalValid |= currCal->calData->calType;
currCal->calState = CAL_DONE;
iscaldone = true;
} else {
@@ -106,7 +107,7 @@ static bool ar9003_hw_per_calibration(struct ath_hw *ah,
ar9003_hw_setup_calibration(ah, currCal);
}
}
- } else if (!(ichan->CalValid & currCal->calData->calType)) {
+ } else if (!(caldata->CalValid & currCal->calData->calType)) {
/* If current cal is marked invalid in channel, kick it off */
ath9k_hw_reset_calibration(ah, currCal);
}
@@ -149,6 +150,12 @@ static bool ar9003_hw_calibrate(struct ath_hw *ah,
/* Do NF cal only at longer intervals */
if (longcal) {
/*
+ * Get the value from the previous NF cal and update
+ * history buffer.
+ */
+ ath9k_hw_getnf(ah, chan);
+
+ /*
* Load the NF from history buffer of the current channel.
* NF is slow time-variant, so it is OK to use a historical
* value.
@@ -156,7 +163,7 @@ static bool ar9003_hw_calibrate(struct ath_hw *ah,
ath9k_hw_loadnf(ah, ah->curchan);
/* start NF calibration, without updating BB NF register */
- ath9k_hw_start_nfcal(ah);
+ ath9k_hw_start_nfcal(ah, false);
}
return iscaldone;
@@ -762,6 +769,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
/* Revert chainmasks to their original values before NF cal */
ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
+ ath9k_hw_start_nfcal(ah, true);
+
/* Initialize list pointers */
ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL;
@@ -785,7 +794,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
if (ah->cal_list_curr)
ath9k_hw_reset_calibration(ah, ah->cal_list_curr);
- chan->CalValid = 0;
+ if (ah->caldata)
+ ah->caldata->CalValid = 0;
return true;
}
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index ace8d26..b883b17 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -41,6 +41,20 @@
#define LE16(x) __constant_cpu_to_le16(x)
#define LE32(x) __constant_cpu_to_le32(x)
+/* Local defines to distinguish between extension and control CTL's */
+#define EXT_ADDITIVE (0x8000)
+#define CTL_11A_EXT (CTL_11A | EXT_ADDITIVE)
+#define CTL_11G_EXT (CTL_11G | EXT_ADDITIVE)
+#define CTL_11B_EXT (CTL_11B | EXT_ADDITIVE)
+#define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */
+#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 9 /* 10*log10(3)*2 */
+#define PWRINCR_3_TO_1_CHAIN 9 /* 10*log(3)*2 */
+#define PWRINCR_3_TO_2_CHAIN 3 /* floor(10*log(3/2)*2) */
+#define PWRINCR_2_TO_1_CHAIN 6 /* 10*log(2)*2 */
+
+#define SUB_NUM_CTL_MODES_AT_5G_40 2 /* excluding HT40, EXT-OFDM */
+#define SUB_NUM_CTL_MODES_AT_2G_40 3 /* excluding HT40, EXT-OFDM, EXT-CCK */
+
static const struct ar9300_eeprom ar9300_default = {
.eepromVersion = 2,
.templateVersion = 2,
@@ -609,6 +623,14 @@ static const struct ar9300_eeprom ar9300_default = {
}
};
+static u16 ath9k_hw_fbin2freq(u8 fbin, bool is2GHz)
+{
+ if (fbin == AR9300_BCHAN_UNUSED)
+ return fbin;
+
+ return (u16) ((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin));
+}
+
static int ath9k_hw_ar9300_check_eeprom(struct ath_hw *ah)
{
return 0;
@@ -1417,9 +1439,9 @@ static int ar9003_hw_tx_power_regwrite(struct ath_hw *ah, u8 * pPwrArray)
#undef POW_SM
}
-static void ar9003_hw_set_target_power_eeprom(struct ath_hw *ah, u16 freq)
+static void ar9003_hw_set_target_power_eeprom(struct ath_hw *ah, u16 freq,
+ u8 *targetPowerValT2)
{
- u8 targetPowerValT2[ar9300RateSize];
/* XXX: hard code for now, need to get from eeprom struct */
u8 ht40PowerIncForPdadc = 0;
bool is2GHz = false;
@@ -1553,9 +1575,6 @@ static void ar9003_hw_set_target_power_eeprom(struct ath_hw *ah, u16 freq)
"TPC[%02d] 0x%08x\n", i, targetPowerValT2[i]);
i++;
}
-
- /* Write target power array to registers */
- ar9003_hw_tx_power_regwrite(ah, targetPowerValT2);
}
static int ar9003_hw_cal_pier_get(struct ath_hw *ah,
@@ -1799,14 +1818,369 @@ static int ar9003_hw_calibration_apply(struct ath_hw *ah, int frequency)
return 0;
}
+static u16 ar9003_hw_get_direct_edge_power(struct ar9300_eeprom *eep,
+ int idx,
+ int edge,
+ bool is2GHz)
+{
+ struct cal_ctl_data_2g *ctl_2g = eep->ctlPowerData_2G;
+ struct cal_ctl_data_5g *ctl_5g = eep->ctlPowerData_5G;
+
+ if (is2GHz)
+ return ctl_2g[idx].ctlEdges[edge].tPower;
+ else
+ return ctl_5g[idx].ctlEdges[edge].tPower;
+}
+
+static u16 ar9003_hw_get_indirect_edge_power(struct ar9300_eeprom *eep,
+ int idx,
+ unsigned int edge,
+ u16 freq,
+ bool is2GHz)
+{
+ struct cal_ctl_data_2g *ctl_2g = eep->ctlPowerData_2G;
+ struct cal_ctl_data_5g *ctl_5g = eep->ctlPowerData_5G;
+
+ u8 *ctl_freqbin = is2GHz ?
+ &eep->ctl_freqbin_2G[idx][0] :
+ &eep->ctl_freqbin_5G[idx][0];
+
+ if (is2GHz) {
+ if (ath9k_hw_fbin2freq(ctl_freqbin[edge - 1], 1) < freq &&
+ ctl_2g[idx].ctlEdges[edge - 1].flag)
+ return ctl_2g[idx].ctlEdges[edge - 1].tPower;
+ } else {
+ if (ath9k_hw_fbin2freq(ctl_freqbin[edge - 1], 0) < freq &&
+ ctl_5g[idx].ctlEdges[edge - 1].flag)
+ return ctl_5g[idx].ctlEdges[edge - 1].tPower;
+ }
+
+ return AR9300_MAX_RATE_POWER;
+}
+
+/*
+ * Find the maximum conformance test limit for the given channel and CTL info
+ */
+static u16 ar9003_hw_get_max_edge_power(struct ar9300_eeprom *eep,
+ u16 freq, int idx, bool is2GHz)
+{
+ u16 twiceMaxEdgePower = AR9300_MAX_RATE_POWER;
+ u8 *ctl_freqbin = is2GHz ?
+ &eep->ctl_freqbin_2G[idx][0] :
+ &eep->ctl_freqbin_5G[idx][0];
+ u16 num_edges = is2GHz ?
+ AR9300_NUM_BAND_EDGES_2G : AR9300_NUM_BAND_EDGES_5G;
+ unsigned int edge;
+
+ /* Get the edge power */
+ for (edge = 0;
+ (edge < num_edges) && (ctl_freqbin[edge] != AR9300_BCHAN_UNUSED);
+ edge++) {
+ /*
+ * If there's an exact channel match or an inband flag set
+ * on the lower channel use the given rdEdgePower
+ */
+ if (freq == ath9k_hw_fbin2freq(ctl_freqbin[edge], is2GHz)) {
+ twiceMaxEdgePower =
+ ar9003_hw_get_direct_edge_power(eep, idx,
+ edge, is2GHz);
+ break;
+ } else if ((edge > 0) &&
+ (freq < ath9k_hw_fbin2freq(ctl_freqbin[edge],
+ is2GHz))) {
+ twiceMaxEdgePower =
+ ar9003_hw_get_indirect_edge_power(eep, idx,
+ edge, freq,
+ is2GHz);
+ /*
+ * Leave loop - no more affecting edges possible in
+ * this monotonic increasing list
+ */
+ break;
+ }
+ }
+ return twiceMaxEdgePower;
+}
+
+static void ar9003_hw_set_power_per_rate_table(struct ath_hw *ah,
+ struct ath9k_channel *chan,
+ u8 *pPwrArray, u16 cfgCtl,
+ u8 twiceAntennaReduction,
+ u8 twiceMaxRegulatoryPower,
+ u16 powerLimit)
+{
+ struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ar9300_eeprom *pEepData = &ah->eeprom.ar9300_eep;
+ u16 twiceMaxEdgePower = AR9300_MAX_RATE_POWER;
+ static const u16 tpScaleReductionTable[5] = {
+ 0, 3, 6, 9, AR9300_MAX_RATE_POWER
+ };
+ int i;
+ int16_t twiceLargestAntenna;
+ u16 scaledPower = 0, minCtlPower, maxRegAllowedPower;
+ u16 ctlModesFor11a[] = {
+ CTL_11A, CTL_5GHT20, CTL_11A_EXT, CTL_5GHT40
+ };
+ u16 ctlModesFor11g[] = {
+ CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT,
+ CTL_11G_EXT, CTL_2GHT40
+ };
+ u16 numCtlModes, *pCtlMode, ctlMode, freq;
+ struct chan_centers centers;
+ u8 *ctlIndex;
+ u8 ctlNum;
+ u16 twiceMinEdgePower;
+ bool is2ghz = IS_CHAN_2GHZ(chan);
+
+ ath9k_hw_get_channel_centers(ah, chan, ¢ers);
+
+ /* Compute TxPower reduction due to Antenna Gain */
+ if (is2ghz)
+ twiceLargestAntenna = pEepData->modalHeader2G.antennaGain;
+ else
+ twiceLargestAntenna = pEepData->modalHeader5G.antennaGain;
+
+ twiceLargestAntenna = (int16_t)min((twiceAntennaReduction) -
+ twiceLargestAntenna, 0);
+
+ /*
+ * scaledPower is the minimum of the user input power level
+ * and the regulatory allowed power level
+ */
+ maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna;
+
+ if (regulatory->tp_scale != ATH9K_TP_SCALE_MAX) {
+ maxRegAllowedPower -=
+ (tpScaleReductionTable[(regulatory->tp_scale)] * 2);
+ }
+
+ scaledPower = min(powerLimit, maxRegAllowedPower);
+
+ /*
+ * Reduce scaled Power by number of chains active to get
+ * to per chain tx power level
+ */
+ switch (ar5416_get_ntxchains(ah->txchainmask)) {
+ case 1:
+ break;
+ case 2:
+ scaledPower -= REDUCE_SCALED_POWER_BY_TWO_CHAIN;
+ break;
+ case 3:
+ scaledPower -= REDUCE_SCALED_POWER_BY_THREE_CHAIN;
+ break;
+ }
+
+ scaledPower = max((u16)0, scaledPower);
+
+ /*
+ * Get target powers from EEPROM - our baseline for TX Power
+ */
+ if (is2ghz) {
+ /* Setup for CTL modes */
+ /* CTL_11B, CTL_11G, CTL_2GHT20 */
+ numCtlModes =
+ ARRAY_SIZE(ctlModesFor11g) -
+ SUB_NUM_CTL_MODES_AT_2G_40;
+ pCtlMode = ctlModesFor11g;
+ if (IS_CHAN_HT40(chan))
+ /* All 2G CTL's */
+ numCtlModes = ARRAY_SIZE(ctlModesFor11g);
+ } else {
+ /* Setup for CTL modes */
+ /* CTL_11A, CTL_5GHT20 */
+ numCtlModes = ARRAY_SIZE(ctlModesFor11a) -
+ SUB_NUM_CTL_MODES_AT_5G_40;
+ pCtlMode = ctlModesFor11a;
+ if (IS_CHAN_HT40(chan))
+ /* All 5G CTL's */
+ numCtlModes = ARRAY_SIZE(ctlModesFor11a);
+ }
+
+ /*
+ * For MIMO, need to apply regulatory caps individually across
+ * dynamically running modes: CCK, OFDM, HT20, HT40
+ *
+ * The outer loop walks through each possible applicable runtime mode.
+ * The inner loop walks through each ctlIndex entry in EEPROM.
+ * The ctl value is encoded as [7:4] == test group, [3:0] == test mode.
+ */
+ for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) {
+ bool isHt40CtlMode = (pCtlMode[ctlMode] == CTL_5GHT40) ||
+ (pCtlMode[ctlMode] == CTL_2GHT40);
+ if (isHt40CtlMode)
+ freq = centers.synth_center;
+ else if (pCtlMode[ctlMode] & EXT_ADDITIVE)
+ freq = centers.ext_center;
+ else
+ freq = centers.ctl_center;
+
+ ath_print(common, ATH_DBG_REGULATORY,
+ "LOOP-Mode ctlMode %d < %d, isHt40CtlMode %d, "
+ "EXT_ADDITIVE %d\n",
+ ctlMode, numCtlModes, isHt40CtlMode,
+ (pCtlMode[ctlMode] & EXT_ADDITIVE));
+
+ /* walk through each CTL index stored in EEPROM */
+ if (is2ghz) {
+ ctlIndex = pEepData->ctlIndex_2G;
+ ctlNum = AR9300_NUM_CTLS_2G;
+ } else {
+ ctlIndex = pEepData->ctlIndex_5G;
+ ctlNum = AR9300_NUM_CTLS_5G;
+ }
+
+ for (i = 0; (i < ctlNum) && ctlIndex[i]; i++) {
+ ath_print(common, ATH_DBG_REGULATORY,
+ "LOOP-Ctlidx %d: cfgCtl 0x%2.2x "
+ "pCtlMode 0x%2.2x ctlIndex 0x%2.2x "
+ "chan %dn",
+ i, cfgCtl, pCtlMode[ctlMode], ctlIndex[i],
+ chan->channel);
+
+ /*
+ * compare test group from regulatory
+ * channel list with test mode from pCtlMode
+ * list
+ */
+ if ((((cfgCtl & ~CTL_MODE_M) |
+ (pCtlMode[ctlMode] & CTL_MODE_M)) ==
+ ctlIndex[i]) ||
+ (((cfgCtl & ~CTL_MODE_M) |
+ (pCtlMode[ctlMode] & CTL_MODE_M)) ==
+ ((ctlIndex[i] & CTL_MODE_M) |
+ SD_NO_CTL))) {
+ twiceMinEdgePower =
+ ar9003_hw_get_max_edge_power(pEepData,
+ freq, i,
+ is2ghz);
+
+ if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL)
+ /*
+ * Find the minimum of all CTL
+ * edge powers that apply to
+ * this channel
+ */
+ twiceMaxEdgePower =
+ min(twiceMaxEdgePower,
+ twiceMinEdgePower);
+ else {
+ /* specific */
+ twiceMaxEdgePower =
+ twiceMinEdgePower;
+ break;
+ }
+ }
+ }
+
+ minCtlPower = (u8)min(twiceMaxEdgePower, scaledPower);
+
+ ath_print(common, ATH_DBG_REGULATORY,
+ "SEL-Min ctlMode %d pCtlMode %d 2xMaxEdge %d "
+ "sP %d minCtlPwr %d\n",
+ ctlMode, pCtlMode[ctlMode], twiceMaxEdgePower,
+ scaledPower, minCtlPower);
+
+ /* Apply ctl mode to correct target power set */
+ switch (pCtlMode[ctlMode]) {
+ case CTL_11B:
+ for (i = ALL_TARGET_LEGACY_1L_5L;
+ i <= ALL_TARGET_LEGACY_11S; i++)
+ pPwrArray[i] =
+ (u8)min((u16)pPwrArray[i],
+ minCtlPower);
+ break;
+ case CTL_11A:
+ case CTL_11G:
+ for (i = ALL_TARGET_LEGACY_6_24;
+ i <= ALL_TARGET_LEGACY_54; i++)
+ pPwrArray[i] =
+ (u8)min((u16)pPwrArray[i],
+ minCtlPower);
+ break;
+ case CTL_5GHT20:
+ case CTL_2GHT20:
+ for (i = ALL_TARGET_HT20_0_8_16;
+ i <= ALL_TARGET_HT20_21; i++)
+ pPwrArray[i] =
+ (u8)min((u16)pPwrArray[i],
+ minCtlPower);
+ pPwrArray[ALL_TARGET_HT20_22] =
+ (u8)min((u16)pPwrArray[ALL_TARGET_HT20_22],
+ minCtlPower);
+ pPwrArray[ALL_TARGET_HT20_23] =
+ (u8)min((u16)pPwrArray[ALL_TARGET_HT20_23],
+ minCtlPower);
+ break;
+ case CTL_5GHT40:
+ case CTL_2GHT40:
+ for (i = ALL_TARGET_HT40_0_8_16;
+ i <= ALL_TARGET_HT40_23; i++)
+ pPwrArray[i] =
+ (u8)min((u16)pPwrArray[i],
+ minCtlPower);
+ break;
+ default:
+ break;
+ }
+ } /* end ctl mode checking */
+}
+
static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
struct ath9k_channel *chan, u16 cfgCtl,
u8 twiceAntennaReduction,
u8 twiceMaxRegulatoryPower,
u8 powerLimit)
{
- ah->txpower_limit = powerLimit;
- ar9003_hw_set_target_power_eeprom(ah, chan->channel);
+ struct ath_common *common = ath9k_hw_common(ah);
+ u8 targetPowerValT2[ar9300RateSize];
+ unsigned int i = 0;
+
+ ar9003_hw_set_target_power_eeprom(ah, chan->channel, targetPowerValT2);
+ ar9003_hw_set_power_per_rate_table(ah, chan,
+ targetPowerValT2, cfgCtl,
+ twiceAntennaReduction,
+ twiceMaxRegulatoryPower,
+ powerLimit);
+
+ while (i < ar9300RateSize) {
+ ath_print(common, ATH_DBG_EEPROM,
+ "TPC[%02d] 0x%08x ", i, targetPowerValT2[i]);
+ i++;
+ ath_print(common, ATH_DBG_EEPROM,
+ "TPC[%02d] 0x%08x ", i, targetPowerValT2[i]);
+ i++;
+ ath_print(common, ATH_DBG_EEPROM,
+ "TPC[%02d] 0x%08x ", i, targetPowerValT2[i]);
+ i++;
+ ath_print(common, ATH_DBG_EEPROM,
+ "TPC[%02d] 0x%08x\n\n", i, targetPowerValT2[i]);
+ i++;
+ }
+
+ /* Write target power array to registers */
+ ar9003_hw_tx_power_regwrite(ah, targetPowerValT2);
+
+ /*
+ * This is the TX power we send back to driver core,
+ * and it can use to pass to userspace to display our
+ * currently configured TX power setting.
+ *
+ * Since power is rate dependent, use one of the indices
+ * from the AR9300_Rates enum to select an entry from
+ * targetPowerValT2[] to report. Currently returns the
+ * power for HT40 MCS 0, HT20 MCS 0, or OFDM 6 Mbps
+ * as CCK power is less interesting (?).
+ */
+ i = ALL_TARGET_LEGACY_6_24; /* legacy */
+ if (IS_CHAN_HT40(chan))
+ i = ALL_TARGET_HT40_0_8_16; /* ht40 */
+ else if (IS_CHAN_HT20(chan))
+ i = ALL_TARGET_HT20_0_8_16; /* ht20 */
+
+ ah->txpower_limit = targetPowerValT2[i];
+
ar9003_hw_calibration_apply(ah, chan->channel);
}
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
index 49e0c86..7c38229 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
@@ -577,10 +577,11 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain)
}
void ar9003_paprd_populate_single_table(struct ath_hw *ah,
- struct ath9k_channel *chan, int chain)
+ struct ath9k_hw_cal_data *caldata,
+ int chain)
{
- u32 *paprd_table_val = chan->pa_table[chain];
- u32 small_signal_gain = chan->small_signal_gain[chain];
+ u32 *paprd_table_val = caldata->pa_table[chain];
+ u32 small_signal_gain = caldata->small_signal_gain[chain];
u32 training_power;
u32 reg = 0;
int i;
@@ -654,17 +655,17 @@ int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain)
}
EXPORT_SYMBOL(ar9003_paprd_setup_gain_table);
-int ar9003_paprd_create_curve(struct ath_hw *ah, struct ath9k_channel *chan,
- int chain)
+int ar9003_paprd_create_curve(struct ath_hw *ah,
+ struct ath9k_hw_cal_data *caldata, int chain)
{
- u16 *small_signal_gain = &chan->small_signal_gain[chain];
- u32 *pa_table = chan->pa_table[chain];
+ u16 *small_signal_gain = &caldata->small_signal_gain[chain];
+ u32 *pa_table = caldata->pa_table[chain];
u32 *data_L, *data_U;
int i, status = 0;
u32 *buf;
u32 reg;
- memset(chan->pa_table[chain], 0, sizeof(chan->pa_table[chain]));
+ memset(caldata->pa_table[chain], 0, sizeof(caldata->pa_table[chain]));
buf = kmalloc(2 * 48 * sizeof(u32), GFP_ATOMIC);
if (!buf)
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index a753a43..a491854 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -542,7 +542,11 @@ static void ar9003_hw_prog_ini(struct ath_hw *ah,
u32 reg = INI_RA(iniArr, i, 0);
u32 val = INI_RA(iniArr, i, column);
- REG_WRITE(ah, reg, val);
+ if (reg >= 0x16000 && reg < 0x17000)
+ ath9k_hw_analog_shift_regwrite(ah, reg, val);
+ else
+ REG_WRITE(ah, reg, val);
+
DO_DELAY(regWrites);
}
}
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 998ae2c..07f26ee 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -510,7 +510,7 @@ void ath_deinit_leds(struct ath_softc *sc);
#define SC_OP_BEACONS BIT(1)
#define SC_OP_RXAGGR BIT(2)
#define SC_OP_TXAGGR BIT(3)
-#define SC_OP_FULL_RESET BIT(4)
+#define SC_OP_OFFCHANNEL BIT(4)
#define SC_OP_PREAMBLE_SHORT BIT(5)
#define SC_OP_PROTECT_ENABLE BIT(6)
#define SC_OP_RXFLUSH BIT(7)
@@ -609,6 +609,7 @@ struct ath_softc {
struct ath_wiphy {
struct ath_softc *sc; /* shared for all virtual wiphys */
struct ieee80211_hw *hw;
+ struct ath9k_hw_cal_data caldata;
enum ath_wiphy_state {
ATH_WIPHY_INACTIVE,
ATH_WIPHY_ACTIVE,
diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c
index 139289e..4520869 100644
--- a/drivers/net/wireless/ath/ath9k/calib.c
+++ b/drivers/net/wireless/ath/ath9k/calib.c
@@ -22,23 +22,6 @@
/* We can tune this as we go by monitoring really low values */
#define ATH9K_NF_TOO_LOW -60
-/* AR5416 may return very high value (like -31 dBm), in those cases the nf
- * is incorrect and we should use the static NF value. Later we can try to
- * find out why they are reporting these values */
-
-static bool ath9k_hw_nf_in_range(struct ath_hw *ah, s16 nf)
-{
- if (nf > ATH9K_NF_TOO_LOW) {
- ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE,
- "noise floor value detected (%d) is "
- "lower than what we think is a "
- "reasonable value (%d)\n",
- nf, ATH9K_NF_TOO_LOW);
- return false;
- }
- return true;
-}
-
static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
{
int16_t nfval;
@@ -121,6 +104,19 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah,
ah->cal_samples = 0;
}
+static s16 ath9k_hw_get_default_nf(struct ath_hw *ah,
+ struct ath9k_channel *chan)
+{
+ struct ath_nf_limits *limit;
+
+ if (!chan || IS_CHAN_2GHZ(chan))
+ limit = &ah->nf_2g;
+ else
+ limit = &ah->nf_5g;
+
+ return limit->nominal;
+}
+
/* This is done for the currently configured channel */
bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
{
@@ -128,7 +124,7 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
struct ieee80211_conf *conf = &common->hw->conf;
struct ath9k_cal_list *currCal = ah->cal_list_curr;
- if (!ah->curchan)
+ if (!ah->caldata)
return true;
if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah))
@@ -151,37 +147,55 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
"Resetting Cal %d state for channel %u\n",
currCal->calData->calType, conf->channel->center_freq);
- ah->curchan->CalValid &= ~currCal->calData->calType;
+ ah->caldata->CalValid &= ~currCal->calData->calType;
currCal->calState = CAL_WAITING;
return false;
}
EXPORT_SYMBOL(ath9k_hw_reset_calvalid);
-void ath9k_hw_start_nfcal(struct ath_hw *ah)
+void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update)
{
+ if (ah->caldata)
+ ah->caldata->nfcal_pending = true;
+
REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
AR_PHY_AGC_CONTROL_ENABLE_NF);
- REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
+
+ if (update)
+ REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
+ AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
+ else
+ REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
+
REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
}
void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
{
- struct ath9k_nfcal_hist *h;
+ struct ath9k_nfcal_hist *h = NULL;
unsigned i, j;
int32_t val;
u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
struct ath_common *common = ath9k_hw_common(ah);
+ s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
- h = ah->nfCalHist;
+ if (ah->caldata)
+ h = ah->caldata->nfCalHist;
for (i = 0; i < NUM_NF_READINGS; i++) {
if (chainmask & (1 << i)) {
+ s16 nfval;
+
+ if (h)
+ nfval = h[i].privNF;
+ else
+ nfval = default_nf;
+
val = REG_READ(ah, ah->nf_regs[i]);
val &= 0xFFFFFE00;
- val |= (((u32) (h[i].privNF) << 1) & 0x1ff);
+ val |= (((u32) nfval << 1) & 0x1ff);
REG_WRITE(ah, ah->nf_regs[i], val);
}
}
@@ -277,22 +291,25 @@ static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf)
}
}
-int16_t ath9k_hw_getnf(struct ath_hw *ah,
- struct ath9k_channel *chan)
+bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
{
struct ath_common *common = ath9k_hw_common(ah);
int16_t nf, nfThresh;
int16_t nfarray[NUM_NF_READINGS] = { 0 };
struct ath9k_nfcal_hist *h;
struct ieee80211_channel *c = chan->chan;
+ struct ath9k_hw_cal_data *caldata = ah->caldata;
+
+ if (!caldata)
+ return false;
chan->channelFlags &= (~CHANNEL_CW_INT);
if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
ath_print(common, ATH_DBG_CALIBRATE,
"NF did not complete in calibration window\n");
nf = 0;
- chan->rawNoiseFloor = nf;
- return chan->rawNoiseFloor;
+ caldata->rawNoiseFloor = nf;
+ return false;
} else {
ath9k_hw_do_getnf(ah, nfarray);
ath9k_hw_nf_sanitize(ah, nfarray);
@@ -307,47 +324,40 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah,
}
}
- h = ah->nfCalHist;
-
+ h = caldata->nfCalHist;
+ caldata->nfcal_pending = false;
ath9k_hw_update_nfcal_hist_buffer(h, nfarray);
- chan->rawNoiseFloor = h[0].privNF;
-
- return chan->rawNoiseFloor;
+ caldata->rawNoiseFloor = h[0].privNF;
+ return true;
}
-void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah)
+void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
+ struct ath9k_channel *chan)
{
- struct ath_nf_limits *limit;
+ struct ath9k_nfcal_hist *h;
+ s16 default_nf;
int i, j;
- if (!ah->curchan || IS_CHAN_2GHZ(ah->curchan))
- limit = &ah->nf_2g;
- else
- limit = &ah->nf_5g;
+ if (!ah->caldata)
+ return;
+ h = ah->caldata->nfCalHist;
+ default_nf = ath9k_hw_get_default_nf(ah, chan);
for (i = 0; i < NUM_NF_READINGS; i++) {
- ah->nfCalHist[i].currIndex = 0;
- ah->nfCalHist[i].privNF = limit->nominal;
- ah->nfCalHist[i].invalidNFcount =
- AR_PHY_CCA_FILTERWINDOW_LENGTH;
+ h[i].currIndex = 0;
+ h[i].privNF = default_nf;
+ h[i].invalidNFcount = AR_PHY_CCA_FILTERWINDOW_LENGTH;
for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) {
- ah->nfCalHist[i].nfCalBuffer[j] = limit->nominal;
+ h[i].nfCalBuffer[j] = default_nf;
}
}
}
s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan)
{
- s16 nf;
-
- if (chan->rawNoiseFloor == 0)
- nf = -96;
- else
- nf = chan->rawNoiseFloor;
-
- if (!ath9k_hw_nf_in_range(ah, nf))
- nf = ATH_DEFAULT_NOISE_FLOOR;
+ if (!ah->caldata || !ah->caldata->rawNoiseFloor)
+ return ath9k_hw_get_default_nf(ah, chan);
- return nf;
+ return ah->caldata->rawNoiseFloor;
}
EXPORT_SYMBOL(ath9k_hw_getchan_noise);
diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h
index cd60d09..0a304b3 100644
--- a/drivers/net/wireless/ath/ath9k/calib.h
+++ b/drivers/net/wireless/ath/ath9k/calib.h
@@ -108,11 +108,11 @@ struct ath9k_pacal_info{
};
bool ath9k_hw_reset_calvalid(struct ath_hw *ah);
-void ath9k_hw_start_nfcal(struct ath_hw *ah);
+void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update);
void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan);
-int16_t ath9k_hw_getnf(struct ath_hw *ah,
- struct ath9k_channel *chan);
-void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah);
+bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan);
+void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
+ struct ath9k_channel *chan);
s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan);
void ath9k_hw_reset_calibration(struct ath_hw *ah,
struct ath9k_cal_list *currCal);
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index 3756400..43b9e21 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -353,6 +353,8 @@ struct ath9k_htc_priv {
u16 seq_no;
u32 bmiss_cnt;
+ struct ath9k_hw_cal_data caldata[38];
+
spinlock_t beacon_lock;
bool tx_queues_stop;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index cf9bcc6..ebed9d1 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -125,6 +125,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
struct ieee80211_conf *conf = &common->hw->conf;
bool fastcc = true;
struct ieee80211_channel *channel = hw->conf.channel;
+ struct ath9k_hw_cal_data *caldata;
enum htc_phymode mode;
__be16 htc_mode;
u8 cmd_rsp;
@@ -149,7 +150,8 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
priv->ah->curchan->channel,
channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf));
- ret = ath9k_hw_reset(ah, hchan, fastcc);
+ caldata = &priv->caldata[channel->hw_value];
+ ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
if (ret) {
ath_print(common, ATH_DBG_FATAL,
"Unable to reset channel (%u Mhz) "
@@ -1028,7 +1030,7 @@ static void ath9k_htc_radio_enable(struct ieee80211_hw *hw)
ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
/* Reset the HW */
- ret = ath9k_hw_reset(ah, ah->curchan, false);
+ ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
if (ret) {
ath_print(common, ATH_DBG_FATAL,
"Unable to reset hardware; reset status %d "
@@ -1091,7 +1093,7 @@ static void ath9k_htc_radio_disable(struct ieee80211_hw *hw)
ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
/* Reset the HW */
- ret = ath9k_hw_reset(ah, ah->curchan, false);
+ ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
if (ret) {
ath_print(common, ATH_DBG_FATAL,
"Unable to reset hardware; reset status %d "
@@ -1179,7 +1181,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
ath9k_hw_configpcipowersave(ah, 0, 0);
ath9k_hw_htc_resetinit(ah);
- ret = ath9k_hw_reset(ah, init_channel, false);
+ ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
if (ret) {
ath_print(common, ATH_DBG_FATAL,
"Unable to reset hardware; reset status %d "
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 8d291cc..3384ca1 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -610,7 +610,6 @@ static int __ath9k_hw_init(struct ath_hw *ah)
else
ah->tx_trig_level = (AR_FTRIG_512B >> AR_FTRIG_S);
- ath9k_init_nfcal_hist_buffer(ah);
ah->bb_watchdog_timeout_ms = 25;
common->state = ATH_HW_INITIALIZED;
@@ -1183,9 +1182,6 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
ath9k_hw_spur_mitigate_freq(ah, chan);
- if (!chan->oneTimeCalsDone)
- chan->oneTimeCalsDone = true;
-
return true;
}
@@ -1218,7 +1214,7 @@ bool ath9k_hw_check_alive(struct ath_hw *ah)
EXPORT_SYMBOL(ath9k_hw_check_alive);
int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
- bool bChannelChange)
+ struct ath9k_hw_cal_data *caldata, bool bChannelChange)
{
struct ath_common *common = ath9k_hw_common(ah);
u32 saveLedState;
@@ -1243,9 +1239,19 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
return -EIO;
- if (curchan && !ah->chip_fullsleep)
+ if (curchan && !ah->chip_fullsleep && ah->caldata)
ath9k_hw_getnf(ah, curchan);
+ ah->caldata = caldata;
+ if (caldata &&
+ (chan->channel != caldata->channel ||
+ (chan->channelFlags & ~CHANNEL_CW_INT) !=
+ (caldata->channelFlags & ~CHANNEL_CW_INT))) {
+ /* Operating channel changed, reset channel calibration data */
+ memset(caldata, 0, sizeof(*caldata));
+ ath9k_init_nfcal_hist_buffer(ah, chan);
+ }
+
if (bChannelChange &&
(ah->chip_fullsleep != true) &&
(ah->curchan != NULL) &&
@@ -1256,7 +1262,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
if (ath9k_hw_channel_change(ah, chan)) {
ath9k_hw_loadnf(ah, ah->curchan);
- ath9k_hw_start_nfcal(ah);
+ ath9k_hw_start_nfcal(ah, true);
return 0;
}
}
@@ -1461,11 +1467,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
if (ah->btcoex_hw.enabled)
ath9k_hw_btcoex_enable(ah);
- if (AR_SREV_9300_20_OR_LATER(ah)) {
- ath9k_hw_loadnf(ah, curchan);
- ath9k_hw_start_nfcal(ah);
+ if (AR_SREV_9300_20_OR_LATER(ah))
ar9003_hw_bb_watchdog_config(ah);
- }
return 0;
}
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 2d30efc..399f7c1 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -346,19 +346,25 @@ enum ath9k_int {
CHANNEL_HT40PLUS | \
CHANNEL_HT40MINUS)
-struct ath9k_channel {
- struct ieee80211_channel *chan;
+struct ath9k_hw_cal_data {
u16 channel;
u32 channelFlags;
- u32 chanmode;
int32_t CalValid;
- bool oneTimeCalsDone;
int8_t iCoff;
int8_t qCoff;
int16_t rawNoiseFloor;
bool paprd_done;
+ bool nfcal_pending;
u16 small_signal_gain[AR9300_MAX_CHAINS];
u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ];
+ struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
+};
+
+struct ath9k_channel {
+ struct ieee80211_channel *chan;
+ u16 channel;
+ u32 channelFlags;
+ u32 chanmode;
};
#define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \
@@ -669,7 +675,7 @@ struct ath_hw {
enum nl80211_iftype opmode;
enum ath9k_power_mode power_mode;
- struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
+ struct ath9k_hw_cal_data *caldata;
struct ath9k_pacal_info pacal_info;
struct ar5416Stats stats;
struct ath9k_tx_queue_info txq[ATH9K_NUM_TX_QUEUES];
@@ -863,7 +869,7 @@ const char *ath9k_hw_probe(u16 vendorid, u16 devid);
void ath9k_hw_deinit(struct ath_hw *ah);
int ath9k_hw_init(struct ath_hw *ah);
int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
- bool bChannelChange);
+ struct ath9k_hw_cal_data *caldata, bool bChannelChange);
int ath9k_hw_fill_cap_info(struct ath_hw *ah);
u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan);
@@ -958,9 +964,10 @@ void ar9003_hw_bb_watchdog_read(struct ath_hw *ah);
void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah);
void ar9003_paprd_enable(struct ath_hw *ah, bool val);
void ar9003_paprd_populate_single_table(struct ath_hw *ah,
- struct ath9k_channel *chan, int chain);
-int ar9003_paprd_create_curve(struct ath_hw *ah, struct ath9k_channel *chan,
- int chain);
+ struct ath9k_hw_cal_data *caldata,
+ int chain);
+int ar9003_paprd_create_curve(struct ath_hw *ah,
+ struct ath9k_hw_cal_data *caldata, int chain);
int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain);
int ar9003_paprd_init_table(struct ath_hw *ah);
bool ar9003_paprd_is_done(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 0429dda..3caa323 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -154,6 +154,27 @@ void ath9k_ps_restore(struct ath_softc *sc)
spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
}
+static void ath_start_ani(struct ath_common *common)
+{
+ struct ath_hw *ah = common->ah;
+ unsigned long timestamp = jiffies_to_msecs(jiffies);
+ struct ath_softc *sc = (struct ath_softc *) common->priv;
+
+ if (!(sc->sc_flags & SC_OP_ANI_RUN))
+ return;
+
+ if (sc->sc_flags & SC_OP_OFFCHANNEL)
+ return;
+
+ common->ani.longcal_timer = timestamp;
+ common->ani.shortcal_timer = timestamp;
+ common->ani.checkani_timer = timestamp;
+
+ mod_timer(&common->ani.timer,
+ jiffies +
+ msecs_to_jiffies((u32)ah->config.ani_poll_interval));
+}
+
/*
* Set/change channels. If the channel is really being changed, it's done
* by reseting the chip. To accomplish this we must first cleanup any pending
@@ -162,16 +183,23 @@ void ath9k_ps_restore(struct ath_softc *sc)
int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
struct ath9k_channel *hchan)
{
+ struct ath_wiphy *aphy = hw->priv;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_conf *conf = &common->hw->conf;
bool fastcc = true, stopped;
struct ieee80211_channel *channel = hw->conf.channel;
+ struct ath9k_hw_cal_data *caldata = NULL;
int r;
if (sc->sc_flags & SC_OP_INVALID)
return -EIO;
+ del_timer_sync(&common->ani.timer);
+ cancel_work_sync(&sc->paprd_work);
+ cancel_work_sync(&sc->hw_check_work);
+ cancel_delayed_work_sync(&sc->tx_complete_work);
+
ath9k_ps_wakeup(sc);
/*
@@ -191,9 +219,12 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
* to flush data frames already in queue because of
* changing channel. */
- if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET))
+ if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL))
fastcc = false;
+ if (!(sc->sc_flags & SC_OP_OFFCHANNEL))
+ caldata = &aphy->caldata;
+
ath_print(common, ATH_DBG_CONFIG,
"(%u MHz) -> (%u MHz), conf_is_ht40: %d\n",
sc->sc_ah->curchan->channel,
@@ -201,7 +232,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
spin_lock_bh(&sc->sc_resetlock);
- r = ath9k_hw_reset(ah, hchan, fastcc);
+ r = ath9k_hw_reset(ah, hchan, caldata, fastcc);
if (r) {
ath_print(common, ATH_DBG_FATAL,
"Unable to reset channel (%u MHz), "
@@ -212,8 +243,6 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
}
spin_unlock_bh(&sc->sc_resetlock);
- sc->sc_flags &= ~SC_OP_FULL_RESET;
-
if (ath_startrecv(sc) != 0) {
ath_print(common, ATH_DBG_FATAL,
"Unable to restart recv logic\n");
@@ -225,6 +254,12 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
ath_update_txpow(sc);
ath9k_hw_set_interrupts(ah, ah->imask);
+ if (!(sc->sc_flags & (SC_OP_OFFCHANNEL | SC_OP_SCANNING))) {
+ ath_start_ani(common);
+ ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
+ ath_beacon_config(sc, NULL);
+ }
+
ps_restore:
ath9k_ps_restore(sc);
return r;
@@ -233,17 +268,19 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
static void ath_paprd_activate(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
+ struct ath9k_hw_cal_data *caldata = ah->caldata;
int chain;
- if (!ah->curchan->paprd_done)
+ if (!caldata || !caldata->paprd_done)
return;
ath9k_ps_wakeup(sc);
+ ar9003_paprd_enable(ah, false);
for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
if (!(ah->caps.tx_chainmask & BIT(chain)))
continue;
- ar9003_paprd_populate_single_table(ah, ah->curchan, chain);
+ ar9003_paprd_populate_single_table(ah, caldata, chain);
}
ar9003_paprd_enable(ah, true);
@@ -261,6 +298,7 @@ void ath_paprd_calibrate(struct work_struct *work)
int band = hw->conf.channel->band;
struct ieee80211_supported_band *sband = &sc->sbands[band];
struct ath_tx_control txctl;
+ struct ath9k_hw_cal_data *caldata = ah->caldata;
int qnum, ftype;
int chain_ok = 0;
int chain;
@@ -268,6 +306,9 @@ void ath_paprd_calibrate(struct work_struct *work)
int time_left;
int i;
+ if (!caldata)
+ return;
+
skb = alloc_skb(len, GFP_KERNEL);
if (!skb)
return;
@@ -322,7 +363,7 @@ void ath_paprd_calibrate(struct work_struct *work)
if (!ar9003_paprd_is_done(ah))
break;
- if (ar9003_paprd_create_curve(ah, ah->curchan, chain) != 0)
+ if (ar9003_paprd_create_curve(ah, caldata, chain) != 0)
break;
chain_ok = 1;
@@ -330,7 +371,7 @@ void ath_paprd_calibrate(struct work_struct *work)
kfree_skb(skb);
if (chain_ok) {
- ah->curchan->paprd_done = true;
+ caldata->paprd_done = true;
ath_paprd_activate(sc);
}
@@ -439,33 +480,14 @@ set_timer:
cal_interval = min(cal_interval, (u32)short_cal_interval);
mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
- if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) &&
- !(sc->sc_flags & SC_OP_SCANNING)) {
- if (!sc->sc_ah->curchan->paprd_done)
+ if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->caldata) {
+ if (!ah->caldata->paprd_done)
ieee80211_queue_work(sc->hw, &sc->paprd_work);
else
ath_paprd_activate(sc);
}
}
-static void ath_start_ani(struct ath_common *common)
-{
- struct ath_hw *ah = common->ah;
- unsigned long timestamp = jiffies_to_msecs(jiffies);
- struct ath_softc *sc = (struct ath_softc *) common->priv;
-
- if (!(sc->sc_flags & SC_OP_ANI_RUN))
- return;
-
- common->ani.longcal_timer = timestamp;
- common->ani.shortcal_timer = timestamp;
- common->ani.checkani_timer = timestamp;
-
- mod_timer(&common->ani.timer,
- jiffies +
- msecs_to_jiffies((u32)ah->config.ani_poll_interval));
-}
-
/*
* Update tx/rx chainmask. For legacy association,
* hard code chainmask to 1x1, for 11n association, use
@@ -477,7 +499,7 @@ void ath_update_chainmask(struct ath_softc *sc, int is_ht)
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
- if ((sc->sc_flags & SC_OP_SCANNING) || is_ht ||
+ if ((sc->sc_flags & SC_OP_OFFCHANNEL) || is_ht ||
(ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE)) {
common->tx_chainmask = ah->caps.tx_chainmask;
common->rx_chainmask = ah->caps.rx_chainmask;
@@ -817,7 +839,7 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
ah->curchan = ath_get_curchannel(sc, sc->hw);
spin_lock_bh(&sc->sc_resetlock);
- r = ath9k_hw_reset(ah, ah->curchan, false);
+ r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
if (r) {
ath_print(common, ATH_DBG_FATAL,
"Unable to reset channel (%u MHz), "
@@ -877,7 +899,7 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
ah->curchan = ath_get_curchannel(sc, hw);
spin_lock_bh(&sc->sc_resetlock);
- r = ath9k_hw_reset(ah, ah->curchan, false);
+ r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
if (r) {
ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
"Unable to reset channel (%u MHz), "
@@ -910,7 +932,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
ath_flushrecv(sc);
spin_lock_bh(&sc->sc_resetlock);
- r = ath9k_hw_reset(ah, sc->sc_ah->curchan, false);
+ r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false);
if (r)
ath_print(common, ATH_DBG_FATAL,
"Unable to reset hardware; reset status %d\n", r);
@@ -1085,7 +1107,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
* and then setup of the interrupt mask.
*/
spin_lock_bh(&sc->sc_resetlock);
- r = ath9k_hw_reset(ah, init_channel, false);
+ r = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
if (r) {
ath_print(common, ATH_DBG_FATAL,
"Unable to reset hardware; reset status %d "
@@ -1579,6 +1601,10 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
aphy->chan_idx = pos;
aphy->chan_is_ht = conf_is_ht(conf);
+ if (hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
+ sc->sc_flags |= SC_OP_OFFCHANNEL;
+ else
+ sc->sc_flags &= ~SC_OP_OFFCHANNEL;
if (aphy->state == ATH_WIPHY_SCAN ||
aphy->state == ATH_WIPHY_ACTIVE)
@@ -1990,7 +2016,6 @@ static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
mutex_lock(&sc->mutex);
if (ath9k_wiphy_scanning(sc)) {
@@ -2008,10 +2033,6 @@ static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
aphy->state = ATH_WIPHY_SCAN;
ath9k_wiphy_pause_all_forced(sc, aphy);
sc->sc_flags |= SC_OP_SCANNING;
- del_timer_sync(&common->ani.timer);
- cancel_work_sync(&sc->paprd_work);
- cancel_work_sync(&sc->hw_check_work);
- cancel_delayed_work_sync(&sc->tx_complete_work);
mutex_unlock(&sc->mutex);
}
@@ -2023,15 +2044,10 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
mutex_lock(&sc->mutex);
aphy->state = ATH_WIPHY_ACTIVE;
sc->sc_flags &= ~SC_OP_SCANNING;
- sc->sc_flags |= SC_OP_FULL_RESET;
- ath_start_ani(common);
- ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
- ath_beacon_config(sc, NULL);
mutex_unlock(&sc->mutex);
}
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index da0cfe9..a3fc987 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -1140,6 +1140,11 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
if (flush)
goto requeue;
+ retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs,
+ rxs, &decrypt_error);
+ if (retval)
+ goto requeue;
+
rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp;
if (rs.rs_tstamp > tsf_lower &&
unlikely(rs.rs_tstamp - tsf_lower > 0x10000000))
@@ -1149,11 +1154,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
unlikely(tsf_lower - rs.rs_tstamp > 0x10000000))
rxs->mactime += 0x100000000ULL;
- retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs,
- rxs, &decrypt_error);
- if (retval)
- goto requeue;
-
/* Ensure we always have an skb to requeue once we are done
* processing the current buffer's skb */
requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC);
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 501b728..4dda14e 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -120,26 +120,14 @@ static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
list_add_tail(&ac->list, &txq->axq_acq);
}
-static void ath_tx_pause_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
-{
- struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum];
-
- spin_lock_bh(&txq->axq_lock);
- tid->paused++;
- spin_unlock_bh(&txq->axq_lock);
-}
-
static void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
{
struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum];
- BUG_ON(tid->paused <= 0);
- spin_lock_bh(&txq->axq_lock);
-
- tid->paused--;
+ WARN_ON(!tid->paused);
- if (tid->paused > 0)
- goto unlock;
+ spin_lock_bh(&txq->axq_lock);
+ tid->paused = false;
if (list_empty(&tid->buf_q))
goto unlock;
@@ -157,15 +145,10 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
struct list_head bf_head;
INIT_LIST_HEAD(&bf_head);
- BUG_ON(tid->paused <= 0);
- spin_lock_bh(&txq->axq_lock);
+ WARN_ON(!tid->paused);
- tid->paused--;
-
- if (tid->paused > 0) {
- spin_unlock_bh(&txq->axq_lock);
- return;
- }
+ spin_lock_bh(&txq->axq_lock);
+ tid->paused = false;
while (!list_empty(&tid->buf_q)) {
bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
@@ -811,7 +794,7 @@ void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
an = (struct ath_node *)sta->drv_priv;
txtid = ATH_AN_2_TID(an, tid);
txtid->state |= AGGR_ADDBA_PROGRESS;
- ath_tx_pause_tid(sc, txtid);
+ txtid->paused = true;
*ssn = txtid->seq_start;
}
@@ -835,10 +818,9 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
return;
}
- ath_tx_pause_tid(sc, txtid);
-
/* drop all software retried frames and mark this TID */
spin_lock_bh(&txq->axq_lock);
+ txtid->paused = true;
while (!list_empty(&txtid->buf_q)) {
bf = list_first_entry(&txtid->buf_q, struct ath_buf, list);
if (!bf_isretried(bf)) {
@@ -1181,7 +1163,7 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
"Failed to stop TX DMA. Resetting hardware!\n");
spin_lock_bh(&sc->sc_resetlock);
- r = ath9k_hw_reset(ah, sc->sc_ah->curchan, false);
+ r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false);
if (r)
ath_print(common, ATH_DBG_FATAL,
"Unable to reset hardware; reset status %d\n",
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
index 5bbff4c..a146240 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/ipw2x00/ipw2100.c
@@ -1924,6 +1924,10 @@ static int ipw2100_net_init(struct net_device *dev)
bg_band->channels =
kzalloc(geo->bg_channels *
sizeof(struct ieee80211_channel), GFP_KERNEL);
+ if (!bg_band->channels) {
+ ipw2100_down(priv);
+ return -ENOMEM;
+ }
/* translate geo->bg to bg_band.channels */
for (i = 0; i < geo->bg_channels; i++) {
bg_band->channels[i].band = IEEE80211_BAND_2GHZ;
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.c
index f052c6d..d706b8a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-debugfs.c
@@ -980,7 +980,7 @@ ssize_t iwl_ucode_bt_stats_read(struct file *file,
le32_to_cpu(bt->lo_priority_tx_req_cnt),
accum_bt->lo_priority_tx_req_cnt);
pos += scnprintf(buf + pos, bufsz - pos,
- "lo_priority_rx_denied_cnt:\t%u\t\t\t%u\n",
+ "lo_priority_tx_denied_cnt:\t%u\t\t\t%u\n",
le32_to_cpu(bt->lo_priority_tx_denied_cnt),
accum_bt->lo_priority_tx_denied_cnt);
pos += scnprintf(buf + pos, bufsz - pos,
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
index a1b6d20..9dd9e64 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
@@ -1429,7 +1429,7 @@ int iwlagn_manage_ibss_station(struct iwl_priv *priv,
void iwl_free_tfds_in_queue(struct iwl_priv *priv,
int sta_id, int tid, int freed)
{
- WARN_ON(!spin_is_locked(&priv->sta_lock));
+ lockdep_assert_held(&priv->sta_lock);
if (priv->stations[sta_id].tid[tid].tfds_in_queue >= freed)
priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
index 35c86d2..23e5c42 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
@@ -300,8 +300,9 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv,
struct ieee80211_sta *sta)
{
int ret = -EAGAIN;
+ u32 load = rs_tl_get_load(lq_data, tid);
- if (rs_tl_get_load(lq_data, tid) > IWL_AGG_LOAD_THRESHOLD) {
+ if (load > IWL_AGG_LOAD_THRESHOLD) {
IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n",
sta->addr, tid);
ret = ieee80211_start_tx_ba_session(sta, tid);
@@ -311,12 +312,14 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv,
* this might be cause by reloading firmware
* stop the tx ba session here
*/
- IWL_DEBUG_HT(priv, "Fail start Tx agg on tid: %d\n",
+ IWL_ERR(priv, "Fail start Tx agg on tid: %d\n",
tid);
ieee80211_stop_tx_ba_session(sta, tid);
}
- } else
- IWL_ERR(priv, "Fail finding valid aggregation tid: %d\n", tid);
+ } else {
+ IWL_ERR(priv, "Aggregation not enabled for tid %d "
+ "because load = %u\n", tid, load);
+ }
return ret;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
index 55a1b31..d04502d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
@@ -1117,7 +1117,7 @@ int iwlagn_txq_check_empty(struct iwl_priv *priv,
u8 *addr = priv->stations[sta_id].sta.sta.addr;
struct iwl_tid_data *tid_data = &priv->stations[sta_id].tid[tid];
- WARN_ON(!spin_is_locked(&priv->sta_lock));
+ lockdep_assert_held(&priv->sta_lock);
switch (priv->stations[sta_id].tid[tid].agg.state) {
case IWL_EMPTYING_HW_QUEUE_DELBA:
@@ -1331,7 +1331,14 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
tid = ba_resp->tid;
agg = &priv->stations[sta_id].tid[tid].agg;
if (unlikely(agg->txq_id != scd_flow)) {
- IWL_ERR(priv, "BA scd_flow %d does not match txq_id %d\n",
+ /*
+ * FIXME: this is a uCode bug which need to be addressed,
+ * log the information and return for now!
+ * since it is possible happen very often and in order
+ * not to fill the syslog, don't enable the logging by default
+ */
+ IWL_DEBUG_TX_REPLY(priv,
+ "BA scd_flow %d does not match txq_id %d\n",
scd_flow, agg->txq_id);
return;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index 8024d44..8ccb6d2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -2000,6 +2000,7 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct iwl_priv *priv = hw->priv;
+ bool scan_completed = false;
IWL_DEBUG_MAC80211(priv, "enter\n");
@@ -2013,7 +2014,7 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
if (priv->vif == vif) {
priv->vif = NULL;
if (priv->scan_vif == vif) {
- ieee80211_scan_completed(priv->hw, true);
+ scan_completed = true;
priv->scan_vif = NULL;
priv->scan_request = NULL;
}
@@ -2021,6 +2022,9 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
}
mutex_unlock(&priv->mutex);
+ if (scan_completed)
+ ieee80211_scan_completed(priv->hw, true);
+
IWL_DEBUG_MAC80211(priv, "leave\n");
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h
index 5c2bcef..0b961a3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debug.h
+++ b/drivers/net/wireless/iwlwifi/iwl-debug.h
@@ -71,7 +71,7 @@ do { \
#define IWL_DEBUG(__priv, level, fmt, args...)
#define IWL_DEBUG_LIMIT(__priv, level, fmt, args...)
static inline void iwl_print_hex_dump(struct iwl_priv *priv, int level,
- void *p, u32 len)
+ const void *p, u32 len)
{}
#endif /* CONFIG_IWLWIFI_DEBUG */
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h
index ae7319b..4cf864c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h
+++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h
@@ -193,7 +193,7 @@ TRACE_EVENT(iwlwifi_dev_tx,
__entry->framelen = buf0_len + buf1_len;
memcpy(__get_dynamic_array(tfd), tfd, tfdlen);
memcpy(__get_dynamic_array(buf0), buf0, buf0_len);
- memcpy(__get_dynamic_array(buf1), buf1, buf0_len);
+ memcpy(__get_dynamic_array(buf1), buf1, buf1_len);
),
TP_printk("[%p] TX %.2x (%zu bytes)",
__entry->priv,
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
index b0c6b04..a4b3663 100644
--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
+++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
@@ -298,7 +298,7 @@ EXPORT_SYMBOL(iwl_init_scan_params);
static int iwl_scan_initiate(struct iwl_priv *priv, struct ieee80211_vif *vif)
{
- WARN_ON(!mutex_is_locked(&priv->mutex));
+ lockdep_assert_held(&priv->mutex);
IWL_DEBUG_INFO(priv, "Starting scan...\n");
set_bit(STATUS_SCANNING, &priv->status);
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c
index 9511f03..7e0829b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-sta.c
+++ b/drivers/net/wireless/iwlwifi/iwl-sta.c
@@ -773,7 +773,7 @@ static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, u8 send_if_empty)
int iwl_restore_default_wep_keys(struct iwl_priv *priv)
{
- WARN_ON(!mutex_is_locked(&priv->mutex));
+ lockdep_assert_held(&priv->mutex);
return iwl_send_static_wepkey_cmd(priv, 0);
}
@@ -784,7 +784,7 @@ int iwl_remove_default_wep_key(struct iwl_priv *priv,
{
int ret;
- WARN_ON(!mutex_is_locked(&priv->mutex));
+ lockdep_assert_held(&priv->mutex);
IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n",
keyconf->keyidx);
@@ -808,7 +808,7 @@ int iwl_set_default_wep_key(struct iwl_priv *priv,
{
int ret;
- WARN_ON(!mutex_is_locked(&priv->mutex));
+ lockdep_assert_held(&priv->mutex);
if (keyconf->keylen != WEP_KEY_LEN_128 &&
keyconf->keylen != WEP_KEY_LEN_64) {
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index 25f9027..8e9fbfd 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -257,6 +257,29 @@ static int lbs_add_supported_rates_tlv(u8 *tlv)
return sizeof(rate_tlv->header) + i;
}
+/* Add common rates from a TLV and return the new end of the TLV */
+static u8 *
+add_ie_rates(u8 *tlv, const u8 *ie, int *nrates)
+{
+ int hw, ap, ap_max = ie[1];
+ u8 hw_rate;
+
+ /* Advance past IE header */
+ ie += 2;
+
+ lbs_deb_hex(LBS_DEB_ASSOC, "AP IE Rates", (u8 *) ie, ap_max);
+
+ for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) {
+ hw_rate = lbs_rates[hw].bitrate / 5;
+ for (ap = 0; ap < ap_max; ap++) {
+ if (hw_rate == (ie[ap] & 0x7f)) {
+ *tlv++ = ie[ap];
+ *nrates = *nrates + 1;
+ }
+ }
+ }
+ return tlv;
+}
/*
* Adds a TLV with all rates the hardware *and* BSS supports.
@@ -264,8 +287,11 @@ static int lbs_add_supported_rates_tlv(u8 *tlv)
static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss)
{
struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv;
- const u8 *rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
- int n;
+ const u8 *rates_eid, *ext_rates_eid;
+ int n = 0;
+
+ rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
+ ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
/*
* 01 00 TLV_TYPE_RATES
@@ -275,26 +301,21 @@ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss)
rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES);
tlv += sizeof(rate_tlv->header);
- if (!rates_eid) {
+ /* Add basic rates */
+ if (rates_eid) {
+ tlv = add_ie_rates(tlv, rates_eid, &n);
+
+ /* Add extended rates, if any */
+ if (ext_rates_eid)
+ tlv = add_ie_rates(tlv, ext_rates_eid, &n);
+ } else {
+ lbs_deb_assoc("assoc: bss had no basic rate IE\n");
/* Fallback: add basic 802.11b rates */
*tlv++ = 0x82;
*tlv++ = 0x84;
*tlv++ = 0x8b;
*tlv++ = 0x96;
n = 4;
- } else {
- int hw, ap;
- u8 ap_max = rates_eid[1];
- n = 0;
- for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) {
- u8 hw_rate = lbs_rates[hw].bitrate / 5;
- for (ap = 0; ap < ap_max; ap++) {
- if (hw_rate == (rates_eid[ap+2] & 0x7f)) {
- *tlv++ = rates_eid[ap+2];
- n++;
- }
- }
- }
}
rate_tlv->header.len = cpu_to_le16(n);
@@ -465,7 +486,15 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy,
lbs_deb_enter(LBS_DEB_CFG80211);
bsssize = get_unaligned_le16(&scanresp->bssdescriptsize);
- nr_sets = le16_to_cpu(resp->size);
+ nr_sets = le16_to_cpu(scanresp->nr_sets);
+
+ lbs_deb_scan("scan response: %d BSSs (%d bytes); resp size %d bytes\n",
+ nr_sets, bsssize, le16_to_cpu(resp->size));
+
+ if (nr_sets == 0) {
+ ret = 0;
+ goto done;
+ }
/*
* The general layout of the scan response is described in chapter
@@ -670,8 +699,13 @@ static void lbs_scan_worker(struct work_struct *work)
if (priv->scan_channel >= priv->scan_req->n_channels) {
/* Mark scan done */
- cfg80211_scan_done(priv->scan_req, false);
+ if (priv->internal_scan)
+ kfree(priv->scan_req);
+ else
+ cfg80211_scan_done(priv->scan_req, false);
+
priv->scan_req = NULL;
+ priv->last_scan = jiffies;
}
/* Restart network */
@@ -682,10 +716,33 @@ static void lbs_scan_worker(struct work_struct *work)
kfree(scan_cmd);
+ /* Wake up anything waiting on scan completion */
+ if (priv->scan_req == NULL) {
+ lbs_deb_scan("scan: waking up waiters\n");
+ wake_up_all(&priv->scan_q);
+ }
+
out_no_scan_cmd:
lbs_deb_leave(LBS_DEB_SCAN);
}
+static void _internal_start_scan(struct lbs_private *priv, bool internal,
+ struct cfg80211_scan_request *request)
+{
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n",
+ request->n_ssids, request->n_channels, request->ie_len);
+
+ priv->scan_channel = 0;
+ queue_delayed_work(priv->work_thread, &priv->scan_work,
+ msecs_to_jiffies(50));
+
+ priv->scan_req = request;
+ priv->internal_scan = internal;
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+}
static int lbs_cfg_scan(struct wiphy *wiphy,
struct net_device *dev,
@@ -702,18 +759,11 @@ static int lbs_cfg_scan(struct wiphy *wiphy,
goto out;
}
- lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n",
- request->n_ssids, request->n_channels, request->ie_len);
-
- priv->scan_channel = 0;
- queue_delayed_work(priv->work_thread, &priv->scan_work,
- msecs_to_jiffies(50));
+ _internal_start_scan(priv, false, request);
if (priv->surpriseremoved)
ret = -EIO;
- priv->scan_req = request;
-
out:
lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
return ret;
@@ -1000,6 +1050,7 @@ static int lbs_associate(struct lbs_private *priv,
int status;
int ret;
u8 *pos = &(cmd->iebuf[0]);
+ u8 *tmp;
lbs_deb_enter(LBS_DEB_CFG80211);
@@ -1044,7 +1095,9 @@ static int lbs_associate(struct lbs_private *priv,
pos += lbs_add_cf_param_tlv(pos);
/* add rates TLV */
+ tmp = pos + 4; /* skip Marvell IE header */
pos += lbs_add_common_rates_tlv(pos, bss);
+ lbs_deb_hex(LBS_DEB_ASSOC, "Common Rates", tmp, pos - tmp);
/* add auth type TLV */
if (priv->fwrelease >= 0x09000000)
@@ -1124,7 +1177,62 @@ done:
return ret;
}
+static struct cfg80211_scan_request *
+_new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme)
+{
+ struct cfg80211_scan_request *creq = NULL;
+ int i, n_channels = 0;
+ enum ieee80211_band band;
+
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ if (wiphy->bands[band])
+ n_channels += wiphy->bands[band]->n_channels;
+ }
+
+ creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
+ n_channels * sizeof(void *),
+ GFP_ATOMIC);
+ if (!creq)
+ return NULL;
+
+ /* SSIDs come after channels */
+ creq->ssids = (void *)&creq->channels[n_channels];
+ creq->n_channels = n_channels;
+ creq->n_ssids = 1;
+
+ /* Scan all available channels */
+ i = 0;
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ int j;
+
+ if (!wiphy->bands[band])
+ continue;
+
+ for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
+ /* ignore disabled channels */
+ if (wiphy->bands[band]->channels[j].flags &
+ IEEE80211_CHAN_DISABLED)
+ continue;
+
+ creq->channels[i] = &wiphy->bands[band]->channels[j];
+ i++;
+ }
+ }
+ if (i) {
+ /* Set real number of channels specified in creq->channels[] */
+ creq->n_channels = i;
+
+ /* Scan for the SSID we're going to connect to */
+ memcpy(creq->ssids[0].ssid, sme->ssid, sme->ssid_len);
+ creq->ssids[0].ssid_len = sme->ssid_len;
+ } else {
+ /* No channels found... */
+ kfree(creq);
+ creq = NULL;
+ }
+ return creq;
+}
static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_connect_params *sme)
@@ -1136,37 +1244,43 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
lbs_deb_enter(LBS_DEB_CFG80211);
- if (sme->bssid) {
- bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
- sme->ssid, sme->ssid_len,
- WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
- } else {
- /*
- * Here we have an impedance mismatch. The firmware command
- * CMD_802_11_ASSOCIATE always needs a BSSID, it cannot
- * connect otherwise. However, for the connect-API of
- * cfg80211 the bssid is purely optional. We don't get one,
- * except the user specifies one on the "iw" command line.
- *
- * If we don't got one, we could initiate a scan and look
- * for the best matching cfg80211_bss entry.
- *
- * Or, better yet, net/wireless/sme.c get's rewritten into
- * something more generally useful.
+ if (!sme->bssid) {
+ /* Run a scan if one isn't in-progress already and if the last
+ * scan was done more than 2 seconds ago.
*/
- lbs_pr_err("TODO: no BSS specified\n");
- ret = -ENOTSUPP;
- goto done;
- }
+ if (priv->scan_req == NULL &&
+ time_after(jiffies, priv->last_scan + (2 * HZ))) {
+ struct cfg80211_scan_request *creq;
+ creq = _new_connect_scan_req(wiphy, sme);
+ if (!creq) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ lbs_deb_assoc("assoc: scanning for compatible AP\n");
+ _internal_start_scan(priv, true, creq);
+ }
+
+ /* Wait for any in-progress scan to complete */
+ lbs_deb_assoc("assoc: waiting for scan to complete\n");
+ wait_event_interruptible_timeout(priv->scan_q,
+ (priv->scan_req == NULL),
+ (15 * HZ));
+ lbs_deb_assoc("assoc: scanning competed\n");
+ }
+ /* Find the BSS we want using available scan results */
+ bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
+ sme->ssid, sme->ssid_len,
+ WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
if (!bss) {
- lbs_pr_err("assicate: bss %pM not in scan results\n",
+ lbs_pr_err("assoc: bss %pM not in scan results\n",
sme->bssid);
ret = -ENOENT;
goto done;
}
- lbs_deb_assoc("trying %pM", sme->bssid);
+ lbs_deb_assoc("trying %pM\n", bss->bssid);
lbs_deb_assoc("cipher 0x%x, key index %d, key len %d\n",
sme->crypto.cipher_group,
sme->key_idx, sme->key_len);
@@ -1229,7 +1343,7 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
lbs_set_radio(priv, preamble, 1);
/* Do the actual association */
- lbs_associate(priv, bss, sme);
+ ret = lbs_associate(priv, bss, sme);
done:
if (bss)
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
index 3c7e255..f062ed5 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/libertas/dev.h
@@ -161,6 +161,11 @@ struct lbs_private {
/** Scanning */
struct delayed_work scan_work;
int scan_channel;
+ /* Queue of things waiting for scan completion */
+ wait_queue_head_t scan_q;
+ /* Whether the scan was initiated internally and not by cfg80211 */
+ bool internal_scan;
+ unsigned long last_scan;
};
extern struct cmd_confirm_sleep confirm_sleep;
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 2589671..24958a8 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -719,6 +719,7 @@ static int lbs_init_adapter(struct lbs_private *priv)
priv->deep_sleep_required = 0;
priv->wakeup_dev_required = 0;
init_waitqueue_head(&priv->ds_awake_q);
+ init_waitqueue_head(&priv->scan_q);
priv->authtype_auto = 1;
priv->is_host_sleep_configured = 0;
priv->is_host_sleep_activated = 0;
diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c
index 822f8dc..71a101f 100644
--- a/drivers/net/wireless/p54/p54pci.c
+++ b/drivers/net/wireless/p54/p54pci.c
@@ -43,6 +43,8 @@ static DEFINE_PCI_DEVICE_TABLE(p54p_table) = {
{ PCI_DEVICE(0x1260, 0x3886) },
/* Intersil PRISM Xbow Wireless LAN adapter (Symbol AP-300) */
{ PCI_DEVICE(0x1260, 0xffff) },
+ /* Standard Microsystems Corp SMC2802W Wireless PCI */
+ { PCI_DEVICE(0x10b8, 0x2802) },
{ },
};
diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c
index 19b262e..63c2cc4 100644
--- a/drivers/net/wireless/rt2x00/rt2x00pci.c
+++ b/drivers/net/wireless/rt2x00/rt2x00pci.c
@@ -240,16 +240,16 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
struct rt2x00_dev *rt2x00dev;
int retval;
- retval = pci_request_regions(pci_dev, pci_name(pci_dev));
+ retval = pci_enable_device(pci_dev);
if (retval) {
- ERROR_PROBE("PCI request regions failed.\n");
+ ERROR_PROBE("Enable device failed.\n");
return retval;
}
- retval = pci_enable_device(pci_dev);
+ retval = pci_request_regions(pci_dev, pci_name(pci_dev));
if (retval) {
- ERROR_PROBE("Enable device failed.\n");
- goto exit_release_regions;
+ ERROR_PROBE("PCI request regions failed.\n");
+ goto exit_disable_device;
}
pci_set_master(pci_dev);
@@ -260,14 +260,14 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) {
ERROR_PROBE("PCI DMA not supported.\n");
retval = -EIO;
- goto exit_disable_device;
+ goto exit_release_regions;
}
hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw);
if (!hw) {
ERROR_PROBE("Failed to allocate hardware.\n");
retval = -ENOMEM;
- goto exit_disable_device;
+ goto exit_release_regions;
}
pci_set_drvdata(pci_dev, hw);
@@ -300,13 +300,12 @@ exit_free_reg:
exit_free_device:
ieee80211_free_hw(hw);
-exit_disable_device:
- if (retval != -EBUSY)
- pci_disable_device(pci_dev);
-
exit_release_regions:
pci_release_regions(pci_dev);
+exit_disable_device:
+ pci_disable_device(pci_dev);
+
pci_set_drvdata(pci_dev, NULL);
return retval;
diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c
index 1d81785..b50c39a 100644
--- a/drivers/net/wireless/rtl818x/rtl8180_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c
@@ -695,6 +695,8 @@ static void rtl8180_beacon_work(struct work_struct *work)
/* grab a fresh beacon */
skb = ieee80211_beacon_get(dev, vif);
+ if (!skb)
+ goto resched;
/*
* update beacon timestamp w/ TSF value
diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c
index 96d25fb..4cb99c5 100644
--- a/drivers/net/wireless/wl12xx/wl1271_spi.c
+++ b/drivers/net/wireless/wl12xx/wl1271_spi.c
@@ -160,9 +160,8 @@ static void wl1271_spi_init(struct wl1271 *wl)
spi_message_add_tail(&t, &m);
spi_sync(wl_to_spi(wl), &m);
- kfree(cmd);
-
wl1271_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN);
+ kfree(cmd);
}
#define WL1271_BUSY_WORD_TIMEOUT 1000
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 8b28962..4568b93 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -132,7 +132,7 @@ struct hci_dev {
struct inquiry_cache inq_cache;
struct hci_conn_hash conn_hash;
- struct bdaddr_list blacklist;
+ struct list_head blacklist;
struct hci_dev_stats stat;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 8303f1c..c52f091 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -924,7 +924,7 @@ int hci_register_dev(struct hci_dev *hdev)
hci_conn_hash_init(hdev);
- INIT_LIST_HEAD(&hdev->blacklist.list);
+ INIT_LIST_HEAD(&hdev->blacklist);
memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 4f170a5..83acd16 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -168,9 +168,8 @@ static int hci_sock_release(struct socket *sock)
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
struct list_head *p;
- struct bdaddr_list *blacklist = &hdev->blacklist;
- list_for_each(p, &blacklist->list) {
+ list_for_each(p, &hdev->blacklist) {
struct bdaddr_list *b;
b = list_entry(p, struct bdaddr_list, list);
@@ -202,7 +201,7 @@ static int hci_blacklist_add(struct hci_dev *hdev, void __user *arg)
bacpy(&entry->bdaddr, &bdaddr);
- list_add(&entry->list, &hdev->blacklist.list);
+ list_add(&entry->list, &hdev->blacklist);
return 0;
}
@@ -210,9 +209,8 @@ static int hci_blacklist_add(struct hci_dev *hdev, void __user *arg)
int hci_blacklist_clear(struct hci_dev *hdev)
{
struct list_head *p, *n;
- struct bdaddr_list *blacklist = &hdev->blacklist;
- list_for_each_safe(p, n, &blacklist->list) {
+ list_for_each_safe(p, n, &hdev->blacklist) {
struct bdaddr_list *b;
b = list_entry(p, struct bdaddr_list, list);
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index ce44c47..8fb967b 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -439,12 +439,11 @@ static const struct file_operations inquiry_cache_fops = {
static int blacklist_show(struct seq_file *f, void *p)
{
struct hci_dev *hdev = f->private;
- struct bdaddr_list *blacklist = &hdev->blacklist;
struct list_head *l;
hci_dev_lock_bh(hdev);
- list_for_each(l, &blacklist->list) {
+ list_for_each(l, &hdev->blacklist) {
struct bdaddr_list *b;
bdaddr_t bdaddr;
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 9ba1e8e..3e3cd9d 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -2527,6 +2527,10 @@ done:
if (pi->imtu != L2CAP_DEFAULT_MTU)
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);
+ if (!(pi->conn->feat_mask & L2CAP_FEAT_ERTM) &&
+ !(pi->conn->feat_mask & L2CAP_FEAT_STREAMING))
+ break;
+
rfc.mode = L2CAP_MODE_BASIC;
rfc.txwin_size = 0;
rfc.max_transmit = 0;
@@ -2534,6 +2538,8 @@ done:
rfc.monitor_timeout = 0;
rfc.max_pdu_size = 0;
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+ (unsigned long) &rfc);
break;
case L2CAP_MODE_ERTM:
@@ -2546,6 +2552,9 @@ done:
if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10)
rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+ (unsigned long) &rfc);
+
if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS))
break;
@@ -2566,6 +2575,9 @@ done:
if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10)
rfc.max_pdu_size = cpu_to_le16(pi->conn->mtu - 10);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+ (unsigned long) &rfc);
+
if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS))
break;
@@ -2577,9 +2589,6 @@ done:
break;
}
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
- (unsigned long) &rfc);
-
/* FIXME: Need actual value of the flush timeout */
//if (flush_to != L2CAP_DEFAULT_FLUSH_TO)
// l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to);
@@ -3339,6 +3348,15 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
del_timer(&conn->info_timer);
+ if (result != L2CAP_IR_SUCCESS) {
+ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
+ conn->info_ident = 0;
+
+ l2cap_conn_start(conn);
+
+ return 0;
+ }
+
if (type == L2CAP_IT_FEAT_MASK) {
conn->feat_mask = get_unaligned_le32(rsp->data);
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index 026205c..befc3a5 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -1183,7 +1183,7 @@ int __init rfcomm_init_ttys(void)
return 0;
}
-void __exit rfcomm_cleanup_ttys(void)
+void rfcomm_cleanup_ttys(void)
{
tty_unregister_driver(rfcomm_tty_driver);
put_tty_driver(rfcomm_tty_driver);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 7cc4f91..798a91b 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -685,10 +685,12 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
return 0;
+#ifdef CONFIG_INET
fail_ifa:
pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
&local->network_latency_notifier);
rtnl_lock();
+#endif
fail_pm_qos:
ieee80211_led_exit(local);
ieee80211_remove_interfaces(local);
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 41f20fb..872d7b6 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -400,19 +400,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
else
__set_bit(SCAN_SW_SCANNING, &local->scanning);
- /*
- * Kicking off the scan need not be protected,
- * only the scan variable stuff, since now
- * local->scan_req is assigned and other callers
- * will abort their scan attempts.
- *
- * This avoids too many locking dependencies
- * so that the scan completed calls have more
- * locking freedom.
- */
-
ieee80211_recalc_idle(local);
- mutex_unlock(&local->scan_mtx);
if (local->ops->hw_scan) {
WARN_ON(!ieee80211_prep_hw_scan(local));
@@ -420,8 +408,6 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
} else
rc = ieee80211_start_sw_scan(local);
- mutex_lock(&local->scan_mtx);
-
if (rc) {
kfree(local->hw_scan_req);
local->hw_scan_req = NULL;
--
John W. Linville Someday the world will need a hero, and you
linville@tuxdriver.com might be all we have. Be ready.
^ permalink raw reply related
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