Netdev List
 help / color / mirror / Atom feed
* Re: stmmac ethernet in kernel 4.9-rc6: coalescing related pauses.
From: Lino Sanfilippo @ 2016-12-05 22:37 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Giuseppe CAVALLARO, alexandre.torgue, David Miller, netdev,
	linux-kernel
In-Reply-To: <20161205220221.GA19135@amd>

Hi Pavel,

On 05.12.2016 23:02, Pavel Machek wrote:
> 
> we need spin_lock_bh at minimum, as we are locking user context
> against timer.
> 
> Best regards,
> 									Pavel
> 

I was referring to stmmac_tx_clean() which AFAICS is only called from softirq context,
(one time in the timer handler and one time in napi poll handler) so a spin_lock() should
be sufficient. I cant see how this is called from userspace. If it were, a spin_lock_bh() had
to be used, of course.

Regards,
Lino 

^ permalink raw reply

* Re: [PATCH nf-next] netfilter: xt_bpf: support ebpf
From: Pablo Neira Ayuso @ 2016-12-05 22:34 UTC (permalink / raw)
  To: Florian Westphal
  Cc: Eric Dumazet, Willem de Bruijn, netfilter-devel, netdev, daniel,
	Willem de Bruijn
In-Reply-To: <20161205213001.GA16819@breakpoint.cc>

On Mon, Dec 05, 2016 at 10:30:01PM +0100, Florian Westphal wrote:
> Eric Dumazet <eric.dumazet@gmail.com> wrote:
> > On Mon, 2016-12-05 at 15:28 -0500, Willem de Bruijn wrote:
> > > From: Willem de Bruijn <willemb@google.com>
> > > 
> > > Add support for attaching an eBPF object by file descriptor.
> > > 
> > > The iptables binary can be called with a path to an elf object or a
> > > pinned bpf object. Also pass the mode and path to the kernel to be
> > > able to return it later for iptables dump and save.
> > > 
> > > Signed-off-by: Willem de Bruijn <willemb@google.com>
> > > ---
> > 
> > Assuming there is no simple way to get variable matchsize in iptables,
> > this looks good to me, thanks.
> 
> It should be possible by setting kernel .matchsize to ~0 which
> suppresses strict size enforcement.
> 
> Its currently only used by ebt_among, but this should work for any xtables
> module.

This is likely going to trigger a large rewrite of the core userspace
iptables codebase, and likely going to pull part of the mess we have
in ebtables into iptables. So I'd prefer not to follow this path.

^ permalink raw reply

* [PATCH v3 net-next v3 4/4] net: dsa: mv88e6xxx: add PPU operations
From: Vivien Didelot @ 2016-12-05 22:30 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, kernel, David S. Miller, Florian Fainelli,
	Andrew Lunn, Stefan Eichenberger, Richard Cochran, Vivien Didelot
In-Reply-To: <20161205223028.20308-1-vivien.didelot@savoirfairelinux.com>

Some Marvell chips can enable/disable the PPU on demand. This is needed
to access the PHY registers when there is no indirection mechanism.

Add two new ppu_enable and ppu_disable ops to describe this and finally
get rid of the MV88E6XXX_FLAG_PPU* flags.

Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
---
 drivers/net/dsa/mv88e6xxx/chip.c      | 76 ++++++++---------------------------
 drivers/net/dsa/mv88e6xxx/global1.c   | 57 ++++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx/global1.h   |  3 ++
 drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 19 ++-------
 4 files changed, 81 insertions(+), 74 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 5aae5d7..731d262 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -527,58 +527,18 @@ int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update)
 
 static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip)
 {
-	u16 val;
-	int i, err;
+	if (!chip->info->ops->ppu_disable)
+		return 0;
 
-	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
-	if (err)
-		return err;
-
-	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL,
-				 val & ~GLOBAL_CONTROL_PPU_ENABLE);
-	if (err)
-		return err;
-
-	for (i = 0; i < 16; i++) {
-		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val);
-		if (err)
-			return err;
-
-		usleep_range(1000, 2000);
-		val &= GLOBAL_STATUS_PPU_STATE_MASK;
-		if (val != GLOBAL_STATUS_PPU_STATE_POLLING)
-			return 0;
-	}
-
-	return -ETIMEDOUT;
+	return chip->info->ops->ppu_disable(chip);
 }
 
 static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip)
 {
-	u16 val;
-	int i, err;
+	if (!chip->info->ops->ppu_enable)
+		return 0;
 
-	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
-	if (err)
-		return err;
-
-	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL,
-				 val | GLOBAL_CONTROL_PPU_ENABLE);
-	if (err)
-		return err;
-
-	for (i = 0; i < 16; i++) {
-		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val);
-		if (err)
-			return err;
-
-		usleep_range(1000, 2000);
-		val &= GLOBAL_STATUS_PPU_STATE_MASK;
-		if (val == GLOBAL_STATUS_PPU_STATE_POLLING)
-			return 0;
-	}
-
-	return -ETIMEDOUT;
+	return chip->info->ops->ppu_enable(chip);
 }
 
 static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
@@ -2746,22 +2706,12 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
 {
 	struct dsa_switch *ds = chip->ds;
 	u32 upstream_port = dsa_upstream_port(ds);
-	u16 reg;
 	int err;
 
 	/* Enable the PHY Polling Unit if present, don't discard any packets,
 	 * and mask all interrupt sources.
 	 */
-	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &reg);
-	if (err < 0)
-		return err;
-
-	reg &= ~GLOBAL_CONTROL_PPU_ENABLE;
-	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU) ||
-	    mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE))
-		reg |= GLOBAL_CONTROL_PPU_ENABLE;
-
-	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg);
+	err = mv88e6xxx_ppu_enable(chip);
 	if (err)
 		return err;
 
@@ -3223,6 +3173,8 @@ static const struct mv88e6xxx_ops mv88e6085_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.ppu_enable = mv88e6185_g1_ppu_enable,
+	.ppu_disable = mv88e6185_g1_ppu_disable,
 	.reset = mv88e6185_g1_reset,
 };
 
@@ -3241,6 +3193,8 @@ static const struct mv88e6xxx_ops mv88e6095_ops = {
 	.stats_get_strings = mv88e6095_stats_get_strings,
 	.stats_get_stats = mv88e6095_stats_get_stats,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.ppu_enable = mv88e6185_g1_ppu_enable,
+	.ppu_disable = mv88e6185_g1_ppu_disable,
 	.reset = mv88e6185_g1_reset,
 };
 
@@ -3311,6 +3265,8 @@ static const struct mv88e6xxx_ops mv88e6131_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.ppu_enable = mv88e6185_g1_ppu_enable,
+	.ppu_disable = mv88e6185_g1_ppu_disable,
 	.reset = mv88e6185_g1_reset,
 };
 
@@ -3483,6 +3439,8 @@ static const struct mv88e6xxx_ops mv88e6185_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.ppu_enable = mv88e6185_g1_ppu_enable,
+	.ppu_disable = mv88e6185_g1_ppu_disable,
 	.reset = mv88e6185_g1_reset,
 };
 
@@ -4262,13 +4220,13 @@ static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
 
 static void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip)
 {
-	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
+	if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable)
 		mv88e6xxx_ppu_state_init(chip);
 }
 
 static void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip)
 {
-	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
+	if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable)
 		mv88e6xxx_ppu_state_destroy(chip);
 }
 
diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c
index c868eb0..75af86a 100644
--- a/drivers/net/dsa/mv88e6xxx/global1.c
+++ b/drivers/net/dsa/mv88e6xxx/global1.c
@@ -35,6 +35,27 @@ int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask)
 
 /* Offset 0x00: Switch Global Status Register */
 
+static int mv88e6185_g1_wait_ppu_disabled(struct mv88e6xxx_chip *chip)
+{
+	u16 state;
+	int i, err;
+
+	for (i = 0; i < 16; i++) {
+		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state);
+		if (err)
+			return err;
+
+		/* Check the value of the PPUState bits 15:14 */
+		state &= GLOBAL_STATUS_PPU_STATE_MASK;
+		if (state != GLOBAL_STATUS_PPU_STATE_POLLING)
+			return 0;
+
+		usleep_range(1000, 2000);
+	}
+
+	return -ETIMEDOUT;
+}
+
 static int mv88e6185_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip)
 {
 	u16 state;
@@ -154,6 +175,42 @@ int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip)
 	return mv88e6352_g1_wait_ppu_polling(chip);
 }
 
+int mv88e6185_g1_ppu_enable(struct mv88e6xxx_chip *chip)
+{
+	u16 val;
+	int err;
+
+	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
+	if (err)
+		return err;
+
+	val |= GLOBAL_CONTROL_PPU_ENABLE;
+
+	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val);
+	if (err)
+		return err;
+
+	return mv88e6185_g1_wait_ppu_polling(chip);
+}
+
+int mv88e6185_g1_ppu_disable(struct mv88e6xxx_chip *chip)
+{
+	u16 val;
+	int err;
+
+	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
+	if (err)
+		return err;
+
+	val &= ~GLOBAL_CONTROL_PPU_ENABLE;
+
+	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val);
+	if (err)
+		return err;
+
+	return mv88e6185_g1_wait_ppu_disabled(chip);
+}
+
 /* Offset 0x1a: Monitor Control */
 /* Offset 0x1a: Monitor & MGMT Control on some devices */
 
diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h
index 9fca215..1aec738 100644
--- a/drivers/net/dsa/mv88e6xxx/global1.h
+++ b/drivers/net/dsa/mv88e6xxx/global1.h
@@ -23,6 +23,9 @@ int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask);
 int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip);
 int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip);
 
+int mv88e6185_g1_ppu_enable(struct mv88e6xxx_chip *chip);
+int mv88e6185_g1_ppu_disable(struct mv88e6xxx_chip *chip);
+
 int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip);
 int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port);
 int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port);
diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
index f201d13..af54bae 100644
--- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
@@ -490,12 +490,6 @@ enum mv88e6xxx_cap {
 	MV88E6XXX_CAP_G2_PVT_DATA,	/* (0x0c) Cross Chip Port VLAN Data */
 	MV88E6XXX_CAP_G2_POT,		/* (0x0f) Priority Override Table */
 
-	/* PHY Polling Unit.
-	 * See GLOBAL_CONTROL_PPU_ENABLE and GLOBAL_STATUS_PPU_POLLING.
-	 */
-	MV88E6XXX_CAP_PPU,
-	MV88E6XXX_CAP_PPU_ACTIVE,
-
 	/* Per VLAN Spanning Tree Unit (STU).
 	 * The Port State database, if present, is accessed through VTU
 	 * operations and dedicated SID registers. See GLOBAL_VTU_SID.
@@ -537,8 +531,6 @@ enum mv88e6xxx_cap {
 #define MV88E6XXX_FLAG_G2_PVT_DATA	BIT_ULL(MV88E6XXX_CAP_G2_PVT_DATA)
 #define MV88E6XXX_FLAG_G2_POT		BIT_ULL(MV88E6XXX_CAP_G2_POT)
 
-#define MV88E6XXX_FLAG_PPU		BIT_ULL(MV88E6XXX_CAP_PPU)
-#define MV88E6XXX_FLAG_PPU_ACTIVE	BIT_ULL(MV88E6XXX_CAP_PPU_ACTIVE)
 #define MV88E6XXX_FLAG_STU		BIT_ULL(MV88E6XXX_CAP_STU)
 #define MV88E6XXX_FLAG_TEMP		BIT_ULL(MV88E6XXX_CAP_TEMP)
 #define MV88E6XXX_FLAG_TEMP_LIMIT	BIT_ULL(MV88E6XXX_CAP_TEMP_LIMIT)
@@ -567,7 +559,6 @@ enum mv88e6xxx_cap {
 #define MV88E6XXX_FLAGS_FAMILY_6095	\
 	(MV88E6XXX_FLAG_GLOBAL2 |	\
 	 MV88E6XXX_FLAG_G2_MGMT_EN_0X |	\
-	 MV88E6XXX_FLAG_PPU |		\
 	 MV88E6XXX_FLAG_VTU |		\
 	 MV88E6XXX_FLAGS_MULTI_CHIP)
 
@@ -578,7 +569,6 @@ enum mv88e6xxx_cap {
 	 MV88E6XXX_FLAG_G2_MGMT_EN_2X |	\
 	 MV88E6XXX_FLAG_G2_MGMT_EN_0X |	\
 	 MV88E6XXX_FLAG_G2_POT |	\
-	 MV88E6XXX_FLAG_PPU |		\
 	 MV88E6XXX_FLAG_STU |		\
 	 MV88E6XXX_FLAG_VTU |		\
 	 MV88E6XXX_FLAGS_IRL |		\
@@ -605,7 +595,6 @@ enum mv88e6xxx_cap {
 	 MV88E6XXX_FLAG_G2_INT |	\
 	 MV88E6XXX_FLAG_G2_MGMT_EN_0X |	\
 	 MV88E6XXX_FLAGS_MULTI_CHIP |	\
-	 MV88E6XXX_FLAG_PPU |		\
 	 MV88E6XXX_FLAG_VTU)
 
 #define MV88E6XXX_FLAGS_FAMILY_6320	\
@@ -614,7 +603,6 @@ enum mv88e6xxx_cap {
 	 MV88E6XXX_FLAG_G2_MGMT_EN_2X |	\
 	 MV88E6XXX_FLAG_G2_MGMT_EN_0X |	\
 	 MV88E6XXX_FLAG_G2_POT |	\
-	 MV88E6XXX_FLAG_PPU_ACTIVE |	\
 	 MV88E6XXX_FLAG_TEMP |		\
 	 MV88E6XXX_FLAG_TEMP_LIMIT |	\
 	 MV88E6XXX_FLAG_VTU |		\
@@ -630,7 +618,6 @@ enum mv88e6xxx_cap {
 	 MV88E6XXX_FLAG_G2_MGMT_EN_2X |	\
 	 MV88E6XXX_FLAG_G2_MGMT_EN_0X |	\
 	 MV88E6XXX_FLAG_G2_POT |	\
-	 MV88E6XXX_FLAG_PPU_ACTIVE |	\
 	 MV88E6XXX_FLAG_STU |		\
 	 MV88E6XXX_FLAG_TEMP |		\
 	 MV88E6XXX_FLAG_VTU |		\
@@ -647,7 +634,6 @@ enum mv88e6xxx_cap {
 	 MV88E6XXX_FLAG_G2_MGMT_EN_2X |	\
 	 MV88E6XXX_FLAG_G2_MGMT_EN_0X |	\
 	 MV88E6XXX_FLAG_G2_POT |	\
-	 MV88E6XXX_FLAG_PPU_ACTIVE |	\
 	 MV88E6XXX_FLAG_STU |		\
 	 MV88E6XXX_FLAG_TEMP |		\
 	 MV88E6XXX_FLAG_TEMP_LIMIT |	\
@@ -662,7 +648,6 @@ struct mv88e6xxx_ops;
 #define MV88E6XXX_FLAGS_FAMILY_6390	\
 	(MV88E6XXX_FLAG_EEE |		\
 	 MV88E6XXX_FLAG_GLOBAL2 |	\
-	 MV88E6XXX_FLAG_PPU_ACTIVE |	\
 	 MV88E6XXX_FLAG_STU |		\
 	 MV88E6XXX_FLAG_TEMP |		\
 	 MV88E6XXX_FLAG_TEMP_LIMIT |	\
@@ -792,6 +777,10 @@ struct mv88e6xxx_ops {
 	int (*phy_write)(struct mv88e6xxx_chip *chip, int addr, int reg,
 			 u16 val);
 
+	/* PHY Polling Unit (PPU) operations */
+	int (*ppu_enable)(struct mv88e6xxx_chip *chip);
+	int (*ppu_disable)(struct mv88e6xxx_chip *chip);
+
 	/* Switch Software Reset */
 	int (*reset)(struct mv88e6xxx_chip *chip);
 
-- 
2.10.2

^ permalink raw reply related

* [PATCH v3 net-next v3 3/4] net: dsa: mv88e6xxx: add a soft reset operation
From: Vivien Didelot @ 2016-12-05 22:30 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, kernel, David S. Miller, Florian Fainelli,
	Andrew Lunn, Stefan Eichenberger, Richard Cochran, Vivien Didelot
In-Reply-To: <20161205223028.20308-1-vivien.didelot@savoirfairelinux.com>

Marvell chips have different way to issue a software reset.

Old chips (such as 88E6060) have a reset bit in an ATU control register.

Newer chips moved this bit in a Global control register. Chips with
controllable PPU should reset the PPU when resetting the switch.

Add a new reset operation to implement these differences and introduce a
mv88e6xxx_software_reset() helper to wrap it conveniently.

Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
---
 drivers/net/dsa/mv88e6xxx/chip.c      |  72 ++++++++++----------
 drivers/net/dsa/mv88e6xxx/global1.c   | 121 ++++++++++++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx/global1.h   |   4 ++
 drivers/net/dsa/mv88e6xxx/mv88e6xxx.h |  15 +++--
 4 files changed, 172 insertions(+), 40 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 27dfb5d..5aae5d7 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -545,7 +545,8 @@ static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip)
 			return err;
 
 		usleep_range(1000, 2000);
-		if ((val & GLOBAL_STATUS_PPU_MASK) != GLOBAL_STATUS_PPU_POLLING)
+		val &= GLOBAL_STATUS_PPU_STATE_MASK;
+		if (val != GLOBAL_STATUS_PPU_STATE_POLLING)
 			return 0;
 	}
 
@@ -572,7 +573,8 @@ static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip)
 			return err;
 
 		usleep_range(1000, 2000);
-		if ((val & GLOBAL_STATUS_PPU_MASK) == GLOBAL_STATUS_PPU_POLLING)
+		val &= GLOBAL_STATUS_PPU_STATE_MASK;
+		if (val == GLOBAL_STATUS_PPU_STATE_POLLING)
 			return 0;
 	}
 
@@ -2356,6 +2358,14 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
 	mutex_unlock(&chip->reg_lock);
 }
 
+static int mv88e6xxx_software_reset(struct mv88e6xxx_chip *chip)
+{
+	if (chip->info->ops->reset)
+		return chip->info->ops->reset(chip);
+
+	return 0;
+}
+
 static void mv88e6xxx_hardware_reset(struct mv88e6xxx_chip *chip)
 {
 	struct gpio_desc *gpiod = chip->reset;
@@ -2391,10 +2401,6 @@ static int mv88e6xxx_disable_ports(struct mv88e6xxx_chip *chip)
 
 static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
 {
-	bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE);
-	u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
-	unsigned long timeout;
-	u16 reg;
 	int err;
 
 	err = mv88e6xxx_disable_ports(chip);
@@ -2403,34 +2409,7 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
 
 	mv88e6xxx_hardware_reset(chip);
 
-	/* Reset the switch. Keep the PPU active if requested. The PPU
-	 * needs to be active to support indirect phy register access
-	 * through global registers 0x18 and 0x19.
-	 */
-	if (ppu_active)
-		err = mv88e6xxx_g1_write(chip, 0x04, 0xc000);
-	else
-		err = mv88e6xxx_g1_write(chip, 0x04, 0xc400);
-	if (err)
-		return err;
-
-	/* Wait up to one second for reset to complete. */
-	timeout = jiffies + 1 * HZ;
-	while (time_before(jiffies, timeout)) {
-		err = mv88e6xxx_g1_read(chip, 0x00, &reg);
-		if (err)
-			return err;
-
-		if ((reg & is_reset) == is_reset)
-			break;
-		usleep_range(1000, 2000);
-	}
-	if (time_after(jiffies, timeout))
-		err = -ETIMEDOUT;
-	else
-		err = 0;
-
-	return err;
+	return mv88e6xxx_software_reset(chip);
 }
 
 static int mv88e6xxx_serdes_power_on(struct mv88e6xxx_chip *chip)
@@ -3244,6 +3223,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6185_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6095_ops = {
@@ -3261,6 +3241,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = {
 	.stats_get_strings = mv88e6095_stats_get_strings,
 	.stats_get_stats = mv88e6095_stats_get_stats,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6185_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6097_ops = {
@@ -3285,6 +3266,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6123_ops = {
@@ -3304,6 +3286,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6131_ops = {
@@ -3328,6 +3311,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6185_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6161_ops = {
@@ -3352,6 +3336,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6165_ops = {
@@ -3369,6 +3354,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6171_ops = {
@@ -3394,6 +3380,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6172_ops = {
@@ -3421,6 +3408,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6175_ops = {
@@ -3446,6 +3434,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6176_ops = {
@@ -3473,6 +3462,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6185_ops = {
@@ -3493,6 +3483,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6185_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6190_ops = {
@@ -3517,6 +3508,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
 	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6390_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6190x_ops = {
@@ -3541,6 +3533,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
 	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6390_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6191_ops = {
@@ -3565,6 +3558,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
 	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6390_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6240_ops = {
@@ -3592,6 +3586,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6290_ops = {
@@ -3616,6 +3611,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
 	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6390_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6320_ops = {
@@ -3642,6 +3638,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6321_ops = {
@@ -3667,6 +3664,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
 	.stats_get_stats = mv88e6320_stats_get_stats,
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6350_ops = {
@@ -3692,6 +3690,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6351_ops = {
@@ -3717,6 +3716,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6352_ops = {
@@ -3744,6 +3744,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6390_ops = {
@@ -3770,6 +3771,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
 	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6390_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6390x_ops = {
@@ -3796,6 +3798,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
 	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6390_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static const struct mv88e6xxx_ops mv88e6391_ops = {
@@ -3820,6 +3823,7 @@ static const struct mv88e6xxx_ops mv88e6391_ops = {
 	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
 	.g1_set_egress_port = mv88e6390_g1_set_egress_port,
 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
+	.reset = mv88e6352_g1_reset,
 };
 
 static int mv88e6xxx_verify_madatory_ops(struct mv88e6xxx_chip *chip,
diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c
index 44136ee..c868eb0 100644
--- a/drivers/net/dsa/mv88e6xxx/global1.c
+++ b/drivers/net/dsa/mv88e6xxx/global1.c
@@ -33,6 +33,127 @@ int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask)
 	return mv88e6xxx_wait(chip, chip->info->global1_addr, reg, mask);
 }
 
+/* Offset 0x00: Switch Global Status Register */
+
+static int mv88e6185_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip)
+{
+	u16 state;
+	int i, err;
+
+	for (i = 0; i < 16; ++i) {
+		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state);
+		if (err)
+			return err;
+
+		/* Check the value of the PPUState bits 15:14 */
+		state &= GLOBAL_STATUS_PPU_STATE_MASK;
+		if (state == GLOBAL_STATUS_PPU_STATE_POLLING)
+			return 0;
+
+		usleep_range(1000, 2000);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int mv88e6352_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip)
+{
+	u16 state;
+	int i, err;
+
+	for (i = 0; i < 16; ++i) {
+		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state);
+		if (err)
+			return err;
+
+		/* Check the value of the PPUState (or InitState) bit 15 */
+		if (state & GLOBAL_STATUS_PPU_STATE)
+			return 0;
+
+		usleep_range(1000, 2000);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int mv88e6xxx_g1_wait_init_ready(struct mv88e6xxx_chip *chip)
+{
+	const unsigned long timeout = jiffies + 1 * HZ;
+	u16 val;
+	int err;
+
+	/* Wait up to 1 second for the switch to be ready. The InitReady bit 11
+	 * is set to a one when all units inside the device (ATU, VTU, etc.)
+	 * have finished their initialization and are ready to accept frames.
+	 */
+	while (time_before(jiffies, timeout)) {
+		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val);
+		if (err)
+			return err;
+
+		if (val & GLOBAL_STATUS_INIT_READY)
+			break;
+
+		usleep_range(1000, 2000);
+	}
+
+	if (time_after(jiffies, timeout))
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+/* Offset 0x04: Switch Global Control Register */
+
+int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip)
+{
+	u16 val;
+	int err;
+
+	/* Set the SWReset bit 15 along with the PPUEn bit 14, to also restart
+	 * the PPU, including re-doing PHY detection and initialization
+	 */
+	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
+	if (err)
+		return err;
+
+	val |= GLOBAL_CONTROL_SW_RESET;
+	val |= GLOBAL_CONTROL_PPU_ENABLE;
+
+	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val);
+	if (err)
+		return err;
+
+	err = mv88e6xxx_g1_wait_init_ready(chip);
+	if (err)
+		return err;
+
+	return mv88e6185_g1_wait_ppu_polling(chip);
+}
+
+int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip)
+{
+	u16 val;
+	int err;
+
+	/* Set the SWReset bit 15 */
+	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
+	if (err)
+		return err;
+
+	val |= GLOBAL_CONTROL_SW_RESET;
+
+	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val);
+	if (err)
+		return err;
+
+	err = mv88e6xxx_g1_wait_init_ready(chip);
+	if (err)
+		return err;
+
+	return mv88e6352_g1_wait_ppu_polling(chip);
+}
+
 /* Offset 0x1a: Monitor Control */
 /* Offset 0x1a: Monitor & MGMT Control on some devices */
 
diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h
index cb61378..9fca215 100644
--- a/drivers/net/dsa/mv88e6xxx/global1.h
+++ b/drivers/net/dsa/mv88e6xxx/global1.h
@@ -19,6 +19,10 @@
 int mv88e6xxx_g1_read(struct mv88e6xxx_chip *chip, int reg, u16 *val);
 int mv88e6xxx_g1_write(struct mv88e6xxx_chip *chip, int reg, u16 val);
 int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask);
+
+int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip);
+int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip);
+
 int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip);
 int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port);
 int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port);
diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
index 13c7cc4..f201d13 100644
--- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
@@ -193,12 +193,12 @@
 
 #define GLOBAL_STATUS		0x00
 #define GLOBAL_STATUS_PPU_STATE BIT(15) /* 6351 and 6171 */
-/* Two bits for 6165, 6185 etc */
-#define GLOBAL_STATUS_PPU_MASK		(0x3 << 14)
-#define GLOBAL_STATUS_PPU_DISABLED_RST	(0x0 << 14)
-#define GLOBAL_STATUS_PPU_INITIALIZING	(0x1 << 14)
-#define GLOBAL_STATUS_PPU_DISABLED	(0x2 << 14)
-#define GLOBAL_STATUS_PPU_POLLING	(0x3 << 14)
+#define GLOBAL_STATUS_PPU_STATE_MASK		(0x3 << 14) /* 6165 6185 */
+#define GLOBAL_STATUS_PPU_STATE_DISABLED_RST	(0x0 << 14)
+#define GLOBAL_STATUS_PPU_STATE_INITIALIZING	(0x1 << 14)
+#define GLOBAL_STATUS_PPU_STATE_DISABLED	(0x2 << 14)
+#define GLOBAL_STATUS_PPU_STATE_POLLING		(0x3 << 14)
+#define GLOBAL_STATUS_INIT_READY	BIT(11)
 #define GLOBAL_STATUS_IRQ_AVB		8
 #define GLOBAL_STATUS_IRQ_DEVICE	7
 #define GLOBAL_STATUS_IRQ_STATS		6
@@ -792,6 +792,9 @@ struct mv88e6xxx_ops {
 	int (*phy_write)(struct mv88e6xxx_chip *chip, int addr, int reg,
 			 u16 val);
 
+	/* Switch Software Reset */
+	int (*reset)(struct mv88e6xxx_chip *chip);
+
 	/* RGMII Receive/Transmit Timing Control
 	 * Add delay on PHY_INTERFACE_MODE_RGMII_*ID, no delay otherwise.
 	 */
-- 
2.10.2

^ permalink raw reply related

* [PATCH v3 net-next v3 2/4] net: dsa: mv88e6xxx: add helper to hardware reset
From: Vivien Didelot @ 2016-12-05 22:30 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, kernel, David S. Miller, Florian Fainelli,
	Andrew Lunn, Stefan Eichenberger, Richard Cochran, Vivien Didelot
In-Reply-To: <20161205223028.20308-1-vivien.didelot@savoirfairelinux.com>

Add an helper to toggle the eventual GPIO connected to the reset pin.

Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
 drivers/net/dsa/mv88e6xxx/chip.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 1d4d3be..27dfb5d 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -2356,6 +2356,19 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
 	mutex_unlock(&chip->reg_lock);
 }
 
+static void mv88e6xxx_hardware_reset(struct mv88e6xxx_chip *chip)
+{
+	struct gpio_desc *gpiod = chip->reset;
+
+	/* If there is a GPIO connected to the reset pin, toggle it */
+	if (gpiod) {
+		gpiod_set_value_cansleep(gpiod, 1);
+		usleep_range(10000, 20000);
+		gpiod_set_value_cansleep(gpiod, 0);
+		usleep_range(10000, 20000);
+	}
+}
+
 static int mv88e6xxx_disable_ports(struct mv88e6xxx_chip *chip)
 {
 	int i, err;
@@ -2380,7 +2393,6 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
 {
 	bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE);
 	u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
-	struct gpio_desc *gpiod = chip->reset;
 	unsigned long timeout;
 	u16 reg;
 	int err;
@@ -2389,13 +2401,7 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
 	if (err)
 		return err;
 
-	/* If there is a gpio connected to the reset pin, toggle it */
-	if (gpiod) {
-		gpiod_set_value_cansleep(gpiod, 1);
-		usleep_range(10000, 20000);
-		gpiod_set_value_cansleep(gpiod, 0);
-		usleep_range(10000, 20000);
-	}
+	mv88e6xxx_hardware_reset(chip);
 
 	/* Reset the switch. Keep the PPU active if requested. The PPU
 	 * needs to be active to support indirect phy register access
-- 
2.10.2

^ permalink raw reply related

* [PATCH v3 net-next v3 1/4] net: dsa: mv88e6xxx: add helper to disable ports
From: Vivien Didelot @ 2016-12-05 22:30 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, kernel, David S. Miller, Florian Fainelli,
	Andrew Lunn, Stefan Eichenberger, Richard Cochran, Vivien Didelot
In-Reply-To: <20161205223028.20308-1-vivien.didelot@savoirfairelinux.com>

Before resetting a switch, the ports should be set to the Disabled state
and the transmit queues should be drained.

Add an helper to explicit that.

Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
 drivers/net/dsa/mv88e6xxx/chip.c | 34 +++++++++++++++++++++++-----------
 1 file changed, 23 insertions(+), 11 deletions(-)

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index ca453f3..1d4d3be 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -2356,6 +2356,26 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
 	mutex_unlock(&chip->reg_lock);
 }
 
+static int mv88e6xxx_disable_ports(struct mv88e6xxx_chip *chip)
+{
+	int i, err;
+
+	/* Set all ports to the Disabled state */
+	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
+		err = mv88e6xxx_port_set_state(chip, i,
+					       PORT_CONTROL_STATE_DISABLED);
+		if (err)
+			return err;
+	}
+
+	/* Wait for transmit queues to drain,
+	 * i.e. 2ms for a maximum frame to be transmitted at 10 Mbps.
+	 */
+	usleep_range(2000, 4000);
+
+	return 0;
+}
+
 static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
 {
 	bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE);
@@ -2364,18 +2384,10 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
 	unsigned long timeout;
 	u16 reg;
 	int err;
-	int i;
 
-	/* Set all ports to the disabled state. */
-	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
-		err = mv88e6xxx_port_set_state(chip, i,
-					       PORT_CONTROL_STATE_DISABLED);
-		if (err)
-			return err;
-	}
-
-	/* Wait for transmit queues to drain. */
-	usleep_range(2000, 4000);
+	err = mv88e6xxx_disable_ports(chip);
+	if (err)
+		return err;
 
 	/* If there is a gpio connected to the reset pin, toggle it */
 	if (gpiod) {
-- 
2.10.2

^ permalink raw reply related

* [PATCH v3 net-next v3 0/4] net: dsa: mv88e6xxx: rework reset and PPU code
From: Vivien Didelot @ 2016-12-05 22:30 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, kernel, David S. Miller, Florian Fainelli,
	Andrew Lunn, Stefan Eichenberger, Richard Cochran, Vivien Didelot

Old Marvell chips (like 88E6060) don't have a PHY Polling Unit (PPU).

Next chips (like 88E6185) have a PPU, which has exclusive access to the
PHY registers, thus must be disabled before access.

Newer chips (like 88E6352) have an indirect mechanism to access the PHY
registers whenever, thus loose control over the PPU (always enabled).

Here's a summary:

Model | PPU? | Has PPU ctrl?  | PPU state readable? | PHY access
----- | ---- | -------------- | ------------------- | ----------
 6060 | no   | no             | no                  | direct
 6185 | yes  | yes, PPUEn bit | yes, PPUState 2-bit | direct w/ PPU dis.
 6352 | yes  | no             | yes, PPUState 1-bit | indirect
 6390 | yes  | no             | yes, InitState bit  | indirect

Depending on the PPU control, a switch may have to restart the PPU when
resetting the switch. Once the switch is reset, we must wait for the PPU
state to be active polling again before accessing the registers.

For that purpose, add new operations to the chips to enable/disable the
PPU, and execute software reset. With these new ops in place, rework the
switch reset code and finally get rid of the MV88E6XXX_FLAG_PPU* flags.

Changes in v3:
  - consider 6097 as 6352 (no PPU ops and use mv88e6352_g1_reset).

Changes in v2:
  - wait in ppu/reset ops so that ppu_polling is not needed anymore.

Vivien Didelot (4):
  net: dsa: mv88e6xxx: add helper to disable ports
  net: dsa: mv88e6xxx: add helper to hardware reset
  net: dsa: mv88e6xxx: add a soft reset operation
  net: dsa: mv88e6xxx: add PPU operations

 drivers/net/dsa/mv88e6xxx/chip.c      | 176 +++++++++++++++------------------
 drivers/net/dsa/mv88e6xxx/global1.c   | 178 ++++++++++++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx/global1.h   |   7 ++
 drivers/net/dsa/mv88e6xxx/mv88e6xxx.h |  34 +++----
 4 files changed, 276 insertions(+), 119 deletions(-)

-- 
2.10.2

^ permalink raw reply

* Re: [Intel-wired-lan] [RFC PATCH] i40e: enable PCIe relax ordering for SPARC
From: tndave @ 2016-12-05 22:23 UTC (permalink / raw)
  To: Alexander Duyck; +Cc: Jeff Kirsher, intel-wired-lan, Netdev
In-Reply-To: <CAKgT0UcyRSub0NCi7oHX2Vf+N4vQxzwdWmcc+V+jOX_J=RYgfg@mail.gmail.com>



On 12/05/2016 01:54 PM, Alexander Duyck wrote:
> On Mon, Dec 5, 2016 at 9:07 AM, Tushar Dave <tushar.n.dave@oracle.com> wrote:
>> Unlike previous generation NIC (e.g. ixgbe) i40e doesn't seem to have
>> standard CSR where PCIe relaxed ordering can be set. Without PCIe relax
>> ordering enabled, i40e performance is significantly low on SPARC.
>>
>> This patch sets PCIe relax ordering for SPARC arch by setting dma attr
>> DMA_ATTR_WEAK_ORDERING for every tx and rx DMA map/unmap.
>> This has shown 10x increase in performance numbers.
>>
>> e.g.
>> iperf TCP test with 10 threads on SPARC S7
>>
>> Test 1: Without this patch
>>
>> # iperf -s
>> ------------------------------------------------------------
>> Server listening on TCP port 5001
>> TCP window size: 85.3 KByte (default)
>> ------------------------------------------------------------
>> [  4] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40926
>> [  5] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40934
>> [  6] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40930
>> [  7] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40928
>> [  8] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40922
>> [  9] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40932
>> [ 10] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40920
>> [ 11] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40924
>> [ 14] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40982
>> [ 12] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40980
>> [ ID] Interval       Transfer     Bandwidth
>> [  4]  0.0-20.0 sec   566 MBytes   237 Mbits/sec
>> [  5]  0.0-20.0 sec   532 MBytes   223 Mbits/sec
>> [  6]  0.0-20.0 sec   537 MBytes   225 Mbits/sec
>> [  8]  0.0-20.0 sec   546 MBytes   229 Mbits/sec
>> [ 11]  0.0-20.0 sec   592 MBytes   248 Mbits/sec
>> [  7]  0.0-20.0 sec   539 MBytes   226 Mbits/sec
>> [  9]  0.0-20.0 sec   572 MBytes   240 Mbits/sec
>> [ 10]  0.0-20.0 sec   604 MBytes   253 Mbits/sec
>> [ 14]  0.0-20.0 sec   567 MBytes   238 Mbits/sec
>> [ 12]  0.0-20.0 sec   511 MBytes   214 Mbits/sec
>> [SUM]  0.0-20.0 sec  5.44 GBytes  2.33 Gbits/sec
>>
>> Test 2: with this patch:
>>
>> # iperf -s
>> ------------------------------------------------------------
>> Server listening on TCP port 5001
>> TCP window size: 85.3 KByte (default)
>> ------------------------------------------------------------
>> TCP: request_sock_TCP: Possible SYN flooding on port 5001. Sending
>> cookies.  Check SNMP counters.
>> [  4] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46876
>> [  5] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46874
>> [  6] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46872
>> [  7] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46880
>> [  8] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46878
>> [  9] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46884
>> [ 10] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46886
>> [ 11] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46890
>> [ 12] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46888
>> [ 13] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46882
>> [ ID] Interval       Transfer     Bandwidth
>> [  4]  0.0-20.0 sec  7.45 GBytes  3.19 Gbits/sec
>> [  5]  0.0-20.0 sec  7.48 GBytes  3.21 Gbits/sec
>> [  7]  0.0-20.0 sec  7.34 GBytes  3.15 Gbits/sec
>> [  8]  0.0-20.0 sec  7.42 GBytes  3.18 Gbits/sec
>> [  9]  0.0-20.0 sec  7.24 GBytes  3.11 Gbits/sec
>> [ 10]  0.0-20.0 sec  7.40 GBytes  3.17 Gbits/sec
>> [ 12]  0.0-20.0 sec  7.49 GBytes  3.21 Gbits/sec
>> [  6]  0.0-20.0 sec  7.30 GBytes  3.13 Gbits/sec
>> [ 11]  0.0-20.0 sec  7.44 GBytes  3.19 Gbits/sec
>> [ 13]  0.0-20.0 sec  7.22 GBytes  3.10 Gbits/sec
>> [SUM]  0.0-20.0 sec  73.8 GBytes  31.6 Gbits/sec
>>
>> NOTE: In my testing, this patch does _not_ show any harm to i40e
>> performance numbers on x86.
>>
>> Signed-off-by: Tushar Dave <tushar.n.dave@oracle.com>
>
> You went through and replaced all of the dma_unmap/map_page calls with
> dma_map/unmap_single_attrs  I would prefer you didn't do that.  I have
Yes, because currently there is no DMA API for dma_map/unmap_page with 
dma attr*
> patches to add the ability to map and unmap pages with attributes that
> should be available for 4.10-rc1 so if you could wait on this patch
> until then it would be preferred.
:-) thanks. I will wait until your patches are out.
>
>> ---
>>  drivers/net/ethernet/intel/i40e/i40e_txrx.c | 69 ++++++++++++++++++++---------
>>  drivers/net/ethernet/intel/i40e/i40e_txrx.h |  1 +
>>  2 files changed, 49 insertions(+), 21 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
>> index 6287bf6..800dca7 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
>> @@ -551,15 +551,17 @@ static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring,
>>                 else
>>                         dev_kfree_skb_any(tx_buffer->skb);
>>                 if (dma_unmap_len(tx_buffer, len))
>> -                       dma_unmap_single(ring->dev,
>> -                                        dma_unmap_addr(tx_buffer, dma),
>> -                                        dma_unmap_len(tx_buffer, len),
>> -                                        DMA_TO_DEVICE);
>> +                       dma_unmap_single_attrs(ring->dev,
>> +                                              dma_unmap_addr(tx_buffer, dma),
>> +                                              dma_unmap_len(tx_buffer, len),
>> +                                              DMA_TO_DEVICE,
>> +                                              ring->dma_attrs);
>>         } else if (dma_unmap_len(tx_buffer, len)) {
>> -               dma_unmap_page(ring->dev,
>> -                              dma_unmap_addr(tx_buffer, dma),
>> -                              dma_unmap_len(tx_buffer, len),
>> -                              DMA_TO_DEVICE);
>> +               dma_unmap_single_attrs(ring->dev,
>> +                                      dma_unmap_addr(tx_buffer, dma),
>> +                                      dma_unmap_len(tx_buffer, len),
>> +                                      DMA_TO_DEVICE,
>> +                                      ring->dma_attrs);
>>         }
>>
>>         tx_buffer->next_to_watch = NULL;
>> @@ -662,6 +664,8 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi,
>>         struct i40e_tx_buffer *tx_buf;
>>         struct i40e_tx_desc *tx_head;
>>         struct i40e_tx_desc *tx_desc;
>> +       dma_addr_t addr;
>> +       size_t size;
>>         unsigned int total_bytes = 0, total_packets = 0;
>>         unsigned int budget = vsi->work_limit;
>>
>> @@ -696,10 +700,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi,
>>                 napi_consume_skb(tx_buf->skb, napi_budget);
>>
>>                 /* unmap skb header data */
>> -               dma_unmap_single(tx_ring->dev,
>> -                                dma_unmap_addr(tx_buf, dma),
>> -                                dma_unmap_len(tx_buf, len),
>> -                                DMA_TO_DEVICE);
>> +               dma_unmap_single_attrs(tx_ring->dev,
>> +                                      dma_unmap_addr(tx_buf, dma),
>> +                                      dma_unmap_len(tx_buf, len),
>> +                                      DMA_TO_DEVICE,
>> +                                      tx_ring->dma_attrs);
>>
>>                 /* clear tx_buffer data */
>>                 tx_buf->skb = NULL;
>> @@ -717,12 +722,15 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi,
>>                                 tx_desc = I40E_TX_DESC(tx_ring, 0);
>>                         }
>>
>> +                       addr = dma_unmap_addr(tx_buf, dma);
>> +                       size = dma_unmap_len(tx_buf, len);
>
> On some architectures this change could lead to issues since
> dma_unmap_len could be 0 meaning that addr would never be used.
I see. Thanks.
>
>>                         /* unmap any remaining paged data */
>>                         if (dma_unmap_len(tx_buf, len)) {
>> -                               dma_unmap_page(tx_ring->dev,
>> -                                              dma_unmap_addr(tx_buf, dma),
>> -                                              dma_unmap_len(tx_buf, len),
>> -                                              DMA_TO_DEVICE);
>> +                               dma_unmap_single_attrs(tx_ring->dev,
>> +                                                      addr,
>> +                                                      size,
>> +                                                      DMA_TO_DEVICE,
>> +                                                      tx_ring->dma_attrs);
>>                                 dma_unmap_len_set(tx_buf, len, 0);
>>                         }
>>                 }
>> @@ -1010,6 +1018,11 @@ int i40e_setup_tx_descriptors(struct i40e_ring *tx_ring)
>>          */
>>         tx_ring->size += sizeof(u32);
>>         tx_ring->size = ALIGN(tx_ring->size, 4096);
>> +#ifdef CONFIG_SPARC
>> +       tx_ring->dma_attrs = DMA_ATTR_WEAK_ORDERING;
>> +#else
>> +       tx_ring->dma_attrs = 0;
>> +#endif
>>         tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size,
>>                                            &tx_ring->dma, GFP_KERNEL);
>>         if (!tx_ring->desc) {
>
> Also not a fan of adding yet ring attribute.  Is there any reason why
> you couldn't simply add a set of inline functions at the start of
> i40e_txrx.c that could replace the DMA map/unmap operations in this
> code but pass either 0 or DMA_ATTR_WEAK_ORDERING as needed for the
> drivers?  Then the x86 code doesn't have to change while the SPARC
> code will be able to be passed the attribute.
Sure I can do that.

I will follow up with patch after your patches for map/unmap page with 
dma attr will be out.

Thanks.

-Tushar
>
>> @@ -1053,7 +1066,11 @@ void i40e_clean_rx_ring(struct i40e_ring *rx_ring)
>>                 if (!rx_bi->page)
>>                         continue;
>>
>> -               dma_unmap_page(dev, rx_bi->dma, PAGE_SIZE, DMA_FROM_DEVICE);
>> +               dma_unmap_single_attrs(dev,
>> +                                      rx_bi->dma,
>> +                                      PAGE_SIZE,
>> +                                      DMA_FROM_DEVICE,
>> +                                      rx_ring->dma_attrs);
>>                 __free_pages(rx_bi->page, 0);
>>
>>                 rx_bi->page = NULL;
>> @@ -1113,6 +1130,11 @@ int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring)
>>         /* Round up to nearest 4K */
>>         rx_ring->size = rx_ring->count * sizeof(union i40e_32byte_rx_desc);
>>         rx_ring->size = ALIGN(rx_ring->size, 4096);
>> +#ifdef CONFIG_SPARC
>> +       rx_ring->dma_attrs = DMA_ATTR_WEAK_ORDERING;
>> +#else
>> +       rx_ring->dma_attrs = 0;
>> +#endif
>>         rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size,
>>                                            &rx_ring->dma, GFP_KERNEL);
>>
>> @@ -1182,7 +1204,8 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring,
>>         }
>>
>>         /* map page for use */
>> -       dma = dma_map_page(rx_ring->dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE);
>> +       dma = dma_map_single_attrs(rx_ring->dev, page_address(page), PAGE_SIZE,
>> +                                  DMA_FROM_DEVICE, rx_ring->dma_attrs);
>>
>>         /* if mapping failed free memory back to system since
>>          * there isn't much point in holding memory we can't use
>> @@ -1695,8 +1718,11 @@ struct sk_buff *i40e_fetch_rx_buffer(struct i40e_ring *rx_ring,
>>                 rx_ring->rx_stats.page_reuse_count++;
>>         } else {
>>                 /* we are not reusing the buffer so unmap it */
>> -               dma_unmap_page(rx_ring->dev, rx_buffer->dma, PAGE_SIZE,
>> -                              DMA_FROM_DEVICE);
>> +               dma_unmap_single_attrs(rx_ring->dev,
>> +                                      rx_buffer->dma,
>> +                                      PAGE_SIZE,
>> +                                      DMA_FROM_DEVICE,
>> +                                      rx_ring->dma_attrs);
>>         }
>>
>>         /* clear contents of buffer_info */
>> @@ -2737,7 +2763,8 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
>>         first->skb = skb;
>>         first->tx_flags = tx_flags;
>>
>> -       dma = dma_map_single(tx_ring->dev, skb->data, size, DMA_TO_DEVICE);
>> +       dma = dma_map_single_attrs(tx_ring->dev, skb->data, size,
>> +                                  DMA_TO_DEVICE, tx_ring->dma_attrs);
>>
>>         tx_desc = I40E_TX_DESC(tx_ring, i);
>>         tx_bi = first;
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
>> index 5088405..9a86212 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
>> @@ -327,6 +327,7 @@ struct i40e_ring {
>>
>>         unsigned int size;              /* length of descriptor ring in bytes */
>>         dma_addr_t dma;                 /* physical address of ring */
>> +       unsigned long dma_attrs;        /* DMA attributes */
>>
>>         struct i40e_vsi *vsi;           /* Backreference to associated VSI */
>>         struct i40e_q_vector *q_vector; /* Backreference to associated vector */
>> --
>> 1.9.1
>>
>> _______________________________________________
>> Intel-wired-lan mailing list
>> Intel-wired-lan@lists.osuosl.org
>> http://lists.osuosl.org/mailman/listinfo/intel-wired-lan
>

^ permalink raw reply

* Re: [PATCH v2 net-next v2 4/4] net: dsa: mv88e6xxx: add PPU operations
From: Vivien Didelot @ 2016-12-05 22:18 UTC (permalink / raw)
  To: Stefan Eichenberger
  Cc: netdev, linux-kernel, kernel, David S. Miller, Florian Fainelli,
	Andrew Lunn, Richard Cochran
In-Reply-To: <20161205210439.GA6038@eichest-notebook>

Stefan Eichenberger <eichest@gmail.com> writes:

> Hi Vivien,
>
> On Mon, Dec 05, 2016 at 11:27:03AM -0500, Vivien Didelot wrote:
>> @@ -3266,6 +3220,8 @@ static const struct mv88e6xxx_ops mv88e6097_ops = {
>>  	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
>>  	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
>>  	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
>> +	.ppu_enable = mv88e6185_g1_ppu_enable,
>> +	.ppu_disable = mv88e6185_g1_ppu_disable,
>>  	.reset = mv88e6185_g1_reset,
>>  };
>
> The mv88e6097 should use the indirect access to the phys, bit 14 in g1
> control is marked as reserved. They write in the datasheet that
> disabling the PPU is still supported but indirect access via g2 should
> be used because disabling the PPU  is no longer recommended.

Ho ok thanks, I respin a v3 right away with this removed and with
mv88e6352_g1_reset instead.

    Vivien

^ permalink raw reply

* [PATCH v4 net-next 2/2] MAINTAINERS: add entry for slicoss ethernet driver
From: Lino Sanfilippo @ 2016-12-05 22:07 UTC (permalink / raw)
  To: davem, charrer, liodot, gregkh
  Cc: devel, andrew, f.fainelli, roszenrami, netdev, linux-kernel,
	Lino Sanfilippo
In-Reply-To: <1480975637-18245-1-git-send-email-LinoSanfilippo@gmx.de>

Add myself as maintainer for the slicoss ethernet driver.

Signed-off-by: Lino Sanfilippo <LinoSanfilippo@gmx.de>
---
 MAINTAINERS | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 6781a3f..bb9af28 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -562,6 +562,11 @@ T:	git git://linuxtv.org/anttip/media_tree.git
 S:	Maintained
 F:	drivers/media/usb/airspy/
 
+ALACRITECH GIGABIT ETHERNET DRIVER
+M:	Lino Sanfilippo <LinoSanfilippo@gmx.de>
+S:	Maintained
+F:	drivers/net/ethernet/alacritech/*
+
 ALCATEL SPEEDTOUCH USB DRIVER
 M:	Duncan Sands <duncan.sands@free.fr>
 L:	linux-usb@vger.kernel.org
-- 
1.9.1

^ permalink raw reply related

* [PATCH v4 net-next 1/2] net: ethernet: slicoss: add slicoss gigabit ethernet driver
From: Lino Sanfilippo @ 2016-12-05 22:07 UTC (permalink / raw)
  To: davem, charrer, liodot, gregkh
  Cc: devel, andrew, f.fainelli, roszenrami, netdev, linux-kernel,
	Lino Sanfilippo
In-Reply-To: <1480975637-18245-1-git-send-email-LinoSanfilippo@gmx.de>

Add driver for Alacritech gigabit ethernet cards with SLIC (session-layer
interface control) technology. The driver provides basic support without
SLIC for the following devices:

- Mojave cards (single port PCI Gigabit) both copper and fiber
- Oasis cards (single and dual port PCI-x Gigabit) copper and fiber
- Kalahari cards (dual and quad port PCI-e Gigabit) copper and fiber

Signed-off-by: Lino Sanfilippo <LinoSanfilippo@gmx.de>
---
 drivers/net/ethernet/Kconfig              |    1 +
 drivers/net/ethernet/Makefile             |    1 +
 drivers/net/ethernet/alacritech/Kconfig   |   28 +
 drivers/net/ethernet/alacritech/Makefile  |    4 +
 drivers/net/ethernet/alacritech/slic.h    |  575 +++++++++
 drivers/net/ethernet/alacritech/slicoss.c | 1882 +++++++++++++++++++++++++++++
 6 files changed, 2491 insertions(+)
 create mode 100644 drivers/net/ethernet/alacritech/Kconfig
 create mode 100644 drivers/net/ethernet/alacritech/Makefile
 create mode 100644 drivers/net/ethernet/alacritech/slic.h
 create mode 100644 drivers/net/ethernet/alacritech/slicoss.c

diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 2ffd634..a4cc87fe 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -21,6 +21,7 @@ source "drivers/net/ethernet/3com/Kconfig"
 source "drivers/net/ethernet/adaptec/Kconfig"
 source "drivers/net/ethernet/aeroflex/Kconfig"
 source "drivers/net/ethernet/agere/Kconfig"
+source "drivers/net/ethernet/alacritech/Kconfig"
 source "drivers/net/ethernet/allwinner/Kconfig"
 source "drivers/net/ethernet/alteon/Kconfig"
 source "drivers/net/ethernet/altera/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 1d349e9..b448027 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_NET_VENDOR_8390) += 8390/
 obj-$(CONFIG_NET_VENDOR_ADAPTEC) += adaptec/
 obj-$(CONFIG_GRETH) += aeroflex/
 obj-$(CONFIG_NET_VENDOR_AGERE) += agere/
+obj-$(CONFIG_NET_VENDOR_ALACRITECH) += alacritech/
 obj-$(CONFIG_NET_VENDOR_ALLWINNER) += allwinner/
 obj-$(CONFIG_NET_VENDOR_ALTEON) += alteon/
 obj-$(CONFIG_ALTERA_TSE) += altera/
diff --git a/drivers/net/ethernet/alacritech/Kconfig b/drivers/net/ethernet/alacritech/Kconfig
new file mode 100644
index 0000000..09496e1
--- /dev/null
+++ b/drivers/net/ethernet/alacritech/Kconfig
@@ -0,0 +1,28 @@
+config NET_VENDOR_ALACRITECH
+	bool "Alacritech devices"
+	default y
+	---help---
+	  If you have a network (Ethernet) card belonging to this class, say Y.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all the
+	  questions about Alacritech devices. If you say Y, you will be asked
+	  for your specific device in the following questions.
+
+if NET_VENDOR_ALACRITECH
+
+config SLICOSS
+	tristate "Alacritech Slicoss support"
+	depends on PCI
+	select CRC32
+	---help---
+	  This driver supports Gigabit Ethernet adapters based on the
+	  Session Layer Interface (SLIC) technology by Alacritech.
+
+	  Supported are Mojave (1 port) and Oasis (1, 2 and 4 port) cards,
+	  both copper and fiber.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called slicoss. This is recommended.
+
+endif # NET_VENDOR_ALACRITECH
diff --git a/drivers/net/ethernet/alacritech/Makefile b/drivers/net/ethernet/alacritech/Makefile
new file mode 100644
index 0000000..8790e9e
--- /dev/null
+++ b/drivers/net/ethernet/alacritech/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for the Alacritech Slicoss driver
+#
+obj-$(CONFIG_SLICOSS) += slicoss.o
diff --git a/drivers/net/ethernet/alacritech/slic.h b/drivers/net/ethernet/alacritech/slic.h
new file mode 100644
index 0000000..08931b4
--- /dev/null
+++ b/drivers/net/ethernet/alacritech/slic.h
@@ -0,0 +1,575 @@
+
+#ifndef _SLIC_H
+#define _SLIC_H
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock_types.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/list.h>
+#include <linux/u64_stats_sync.h>
+
+#define SLIC_VGBSTAT_XPERR		0x40000000
+#define SLIC_VGBSTAT_XERRSHFT		25
+#define SLIC_VGBSTAT_XCSERR		0x23
+#define SLIC_VGBSTAT_XUFLOW		0x22
+#define SLIC_VGBSTAT_XHLEN		0x20
+#define SLIC_VGBSTAT_NETERR		0x01000000
+#define SLIC_VGBSTAT_NERRSHFT		16
+#define SLIC_VGBSTAT_NERRMSK		0x1ff
+#define SLIC_VGBSTAT_NCSERR		0x103
+#define SLIC_VGBSTAT_NUFLOW		0x102
+#define SLIC_VGBSTAT_NHLEN		0x100
+#define SLIC_VGBSTAT_LNKERR		0x00000080
+#define SLIC_VGBSTAT_LERRMSK		0xff
+#define SLIC_VGBSTAT_LDEARLY		0x86
+#define SLIC_VGBSTAT_LBOFLO		0x85
+#define SLIC_VGBSTAT_LCODERR		0x84
+#define SLIC_VGBSTAT_LDBLNBL		0x83
+#define SLIC_VGBSTAT_LCRCERR		0x82
+#define SLIC_VGBSTAT_LOFLO		0x81
+#define SLIC_VGBSTAT_LUFLO		0x80
+
+#define SLIC_IRHDDR_FLEN_MSK		0x0000ffff
+#define SLIC_IRHDDR_SVALID		0x80000000
+#define SLIC_IRHDDR_ERR			0x10000000
+
+#define SLIC_VRHSTAT_802OE		0x80000000
+#define SLIC_VRHSTAT_TPOFLO		0x10000000
+#define SLIC_VRHSTATB_802UE		0x80000000
+#define SLIC_VRHSTATB_RCVE		0x40000000
+#define SLIC_VRHSTATB_BUFF		0x20000000
+#define SLIC_VRHSTATB_CARRE		0x08000000
+#define SLIC_VRHSTATB_LONGE		0x02000000
+#define SLIC_VRHSTATB_PREA		0x01000000
+#define SLIC_VRHSTATB_CRC		0x00800000
+#define SLIC_VRHSTATB_DRBL		0x00400000
+#define SLIC_VRHSTATB_CODE		0x00200000
+#define SLIC_VRHSTATB_TPCSUM		0x00100000
+#define SLIC_VRHSTATB_TPHLEN		0x00080000
+#define SLIC_VRHSTATB_IPCSUM		0x00040000
+#define SLIC_VRHSTATB_IPLERR		0x00020000
+#define SLIC_VRHSTATB_IPHERR		0x00010000
+
+#define SLIC_CMD_XMT_REQ		0x01
+#define SLIC_CMD_TYPE_DUMB		3
+
+#define SLIC_RESET_MAGIC		0xDEAD
+#define SLIC_ICR_INT_OFF		0
+#define SLIC_ICR_INT_ON			1
+#define SLIC_ICR_INT_MASK		2
+
+#define SLIC_ISR_ERR			0x80000000
+#define SLIC_ISR_RCV			0x40000000
+#define SLIC_ISR_CMD			0x20000000
+#define SLIC_ISR_IO			0x60000000
+#define SLIC_ISR_UPC			0x10000000
+#define SLIC_ISR_LEVENT			0x08000000
+#define SLIC_ISR_RMISS			0x02000000
+#define SLIC_ISR_UPCERR			0x01000000
+#define SLIC_ISR_XDROP			0x00800000
+#define SLIC_ISR_UPCBSY			0x00020000
+
+#define SLIC_ISR_PING_MASK		0x00700000
+#define SLIC_ISR_UPCERR_MASK		(SLIC_ISR_UPCERR | SLIC_ISR_UPCBSY)
+#define SLIC_ISR_UPC_MASK		(SLIC_ISR_UPC | SLIC_ISR_UPCERR_MASK)
+#define SLIC_WCS_START			0x80000000
+#define SLIC_WCS_COMPARE		0x40000000
+#define SLIC_RCVWCS_BEGIN		0x40000000
+#define SLIC_RCVWCS_FINISH		0x80000000
+
+#define SLIC_MIICR_REG_16		0x00100000
+#define SLIC_MRV_REG16_XOVERON		0x0068
+
+#define SLIC_GIG_LINKUP			0x0001
+#define SLIC_GIG_FULLDUPLEX		0x0002
+#define SLIC_GIG_SPEED_MASK		0x000C
+#define SLIC_GIG_SPEED_1000		0x0008
+#define SLIC_GIG_SPEED_100		0x0004
+#define SLIC_GIG_SPEED_10		0x0000
+
+#define SLIC_GMCR_RESET			0x80000000
+#define SLIC_GMCR_GBIT			0x20000000
+#define SLIC_GMCR_FULLD			0x10000000
+#define SLIC_GMCR_GAPBB_SHIFT		14
+#define SLIC_GMCR_GAPR1_SHIFT		7
+#define SLIC_GMCR_GAPR2_SHIFT		0
+#define SLIC_GMCR_GAPBB_1000		0x60
+#define SLIC_GMCR_GAPR1_1000		0x2C
+#define SLIC_GMCR_GAPR2_1000		0x40
+#define SLIC_GMCR_GAPBB_100		0x70
+#define SLIC_GMCR_GAPR1_100		0x2C
+#define SLIC_GMCR_GAPR2_100		0x40
+
+#define SLIC_XCR_RESET			0x80000000
+#define SLIC_XCR_XMTEN			0x40000000
+#define SLIC_XCR_PAUSEEN		0x20000000
+#define SLIC_XCR_LOADRNG		0x10000000
+
+#define SLIC_GXCR_RESET			0x80000000
+#define SLIC_GXCR_XMTEN			0x40000000
+#define SLIC_GXCR_PAUSEEN		0x20000000
+
+#define SLIC_GRCR_RESET			0x80000000
+#define SLIC_GRCR_RCVEN			0x40000000
+#define SLIC_GRCR_RCVALL		0x20000000
+#define SLIC_GRCR_RCVBAD		0x10000000
+#define SLIC_GRCR_CTLEN			0x08000000
+#define SLIC_GRCR_ADDRAEN		0x02000000
+#define SLIC_GRCR_HASHSIZE_SHIFT	17
+#define SLIC_GRCR_HASHSIZE		14
+
+/* Reset Register */
+#define SLIC_REG_RESET			0x0000
+/* Interrupt Control Register */
+#define SLIC_REG_ICR			0x0008
+/* Interrupt status pointer */
+#define SLIC_REG_ISP			0x0010
+/* Interrupt status */
+#define SLIC_REG_ISR			0x0018
+/* Header buffer address reg
+ * 31-8 - phy addr of set of contiguous hdr buffers
+ *  7-0 - number of buffers passed
+ * Buffers are 256 bytes long on 256-byte boundaries.
+ */
+#define SLIC_REG_HBAR			0x0020
+/* Data buffer handle & address reg
+ * 4 sets of registers; Buffers are 2K bytes long 2 per 4K page.
+ */
+#define SLIC_REG_DBAR			0x0028
+/* Xmt Cmd buf addr regs.
+ * 1 per XMT interface
+ * 31-5 - phy addr of host command buffer
+ *  4-0 - length of cmd in multiples of 32 bytes
+ * Buffers are 32 bytes up to 512 bytes long
+ */
+#define SLIC_REG_CBAR			0x0030
+/* Write control store */
+#define	SLIC_REG_WCS			0x0034
+/*Response buffer address reg.
+ * 31-8 - phy addr of set of contiguous response buffers
+ * 7-0 - number of buffers passed
+ * Buffers are 32 bytes long on 32-byte boundaries.
+ */
+#define	SLIC_REG_RBAR			0x0038
+/* Read statistics (UPR) */
+#define	SLIC_REG_RSTAT			0x0040
+/* Read link status */
+#define	SLIC_REG_LSTAT			0x0048
+/* Write Mac Config */
+#define	SLIC_REG_WMCFG			0x0050
+/* Write phy register */
+#define SLIC_REG_WPHY			0x0058
+/* Rcv Cmd buf addr reg */
+#define	SLIC_REG_RCBAR			0x0060
+/* Read SLIC Config*/
+#define SLIC_REG_RCONFIG		0x0068
+/* Interrupt aggregation time */
+#define SLIC_REG_INTAGG			0x0070
+/* Write XMIT config reg */
+#define	SLIC_REG_WXCFG			0x0078
+/* Write RCV config reg */
+#define	SLIC_REG_WRCFG			0x0080
+/* Write rcv addr a low */
+#define	SLIC_REG_WRADDRAL		0x0088
+/* Write rcv addr a high */
+#define	SLIC_REG_WRADDRAH		0x0090
+/* Write rcv addr b low */
+#define	SLIC_REG_WRADDRBL		0x0098
+/* Write rcv addr b high */
+#define	SLIC_REG_WRADDRBH		0x00a0
+/* Low bits of mcast mask */
+#define	SLIC_REG_MCASTLOW		0x00a8
+/* High bits of mcast mask */
+#define	SLIC_REG_MCASTHIGH		0x00b0
+/* Ping the card */
+#define SLIC_REG_PING			0x00b8
+/* Dump command */
+#define SLIC_REG_DUMP_CMD		0x00c0
+/* Dump data pointer */
+#define SLIC_REG_DUMP_DATA		0x00c8
+/* Read card's pci_status register */
+#define	SLIC_REG_PCISTATUS		0x00d0
+/* Write hostid field */
+#define SLIC_REG_WRHOSTID		0x00d8
+/* Put card in a low power state */
+#define SLIC_REG_LOW_POWER		0x00e0
+/* Force slic into quiescent state  before soft reset */
+#define SLIC_REG_QUIESCE		0x00e8
+/* Reset interface queues */
+#define SLIC_REG_RESET_IFACE		0x00f0
+/* Register is only written when it has changed.
+ * Bits 63-32 for host i/f addrs.
+ */
+#define SLIC_REG_ADDR_UPPER		0x00f8
+/* 64 bit Header buffer address reg */
+#define SLIC_REG_HBAR64			0x0100
+/* 64 bit Data buffer handle & address reg */
+#define SLIC_REG_DBAR64			0x0108
+/* 64 bit Xmt Cmd buf addr regs. */
+#define SLIC_REG_CBAR64			0x0110
+/* 64 bit Response buffer address reg.*/
+#define SLIC_REG_RBAR64			0x0118
+/* 64 bit Rcv Cmd buf addr reg*/
+#define	SLIC_REG_RCBAR64		0x0120
+/* Read statistics (64 bit UPR) */
+#define	SLIC_REG_RSTAT64		0x0128
+/* Download Gigabit RCV sequencer ucode */
+#define SLIC_REG_RCV_WCS		0x0130
+/* Write VlanId field */
+#define SLIC_REG_WRVLANID		0x0138
+/* Read Transformer info */
+#define SLIC_REG_READ_XF_INFO		0x0140
+/* Write Transformer info */
+#define SLIC_REG_WRITE_XF_INFO		0x0148
+/* Write card ticks per second */
+#define SLIC_REG_TICKS_PER_SEC		0x0170
+#define SLIC_REG_HOSTID			0x1554
+
+#define PCI_VENDOR_ID_ALACRITECH		0x139A
+#define PCI_DEVICE_ID_ALACRITECH_MOJAVE		0x0005
+#define PCI_SUBDEVICE_ID_ALACRITECH_1000X1	0x0005
+#define PCI_SUBDEVICE_ID_ALACRITECH_1000X1_2	0x0006
+#define PCI_SUBDEVICE_ID_ALACRITECH_1000X1F	0x0007
+#define PCI_SUBDEVICE_ID_ALACRITECH_CICADA	0x0008
+#define PCI_SUBDEVICE_ID_ALACRITECH_SES1001T	0x2006
+#define PCI_SUBDEVICE_ID_ALACRITECH_SES1001F	0x2007
+#define PCI_DEVICE_ID_ALACRITECH_OASIS		0x0007
+#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2002XT	0x000B
+#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2002XF	0x000C
+#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2001XT	0x000D
+#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2001XF	0x000E
+#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2104EF	0x000F
+#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2104ET	0x0010
+#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2102EF	0x0011
+#define PCI_SUBDEVICE_ID_ALACRITECH_SEN2102ET	0x0012
+
+/* Note: power of two required for number descriptors  */
+#define SLIC_NUM_RX_LES			256
+#define SLIC_RX_BUFF_SIZE		2048
+#define SLIC_RX_BUFF_ALIGN		256
+#define SLIC_RX_BUFF_HDR_SIZE		34
+#define SLIC_MAX_REQ_RX_DESCS		1
+
+#define SLIC_NUM_TX_DESCS		256
+#define SLIC_TX_DESC_ALIGN		32
+#define SLIC_MIN_TX_WAKEUP_DESCS	10
+#define SLIC_MAX_REQ_TX_DESCS		1
+#define SLIC_MAX_TX_COMPLETIONS		100
+
+#define SLIC_NUM_STAT_DESCS		128
+#define SLIC_STATS_DESC_ALIGN		256
+
+#define SLIC_NUM_STAT_DESC_ARRAYS	4
+#define SLIC_INVALID_STAT_DESC_IDX	0xffffffff
+
+#define SLIC_NAPI_WEIGHT		64
+
+#define SLIC_UPR_LSTAT			0
+#define SLIC_UPR_CONFIG			1
+
+#define SLIC_EEPROM_SIZE		128
+#define SLIC_EEPROM_MAGIC		0xa5a5
+
+#define SLIC_FIRMWARE_MOJAVE		"slicoss/gbdownload.sys"
+#define SLIC_FIRMWARE_OASIS		"slicoss/oasisdownload.sys"
+#define SLIC_RCV_FIRMWARE_MOJAVE	"slicoss/gbrcvucode.sys"
+#define SLIC_RCV_FIRMWARE_OASIS		"slicoss/oasisrcvucode.sys"
+#define SLIC_FIRMWARE_MIN_SIZE		64
+#define SLIC_FIRMWARE_MAX_SECTIONS	3
+
+#define SLIC_MODEL_MOJAVE		0
+#define SLIC_MODEL_OASIS		1
+
+#define SLIC_INC_STATS_COUNTER(st, counter)	\
+do {						\
+	u64_stats_update_begin(&(st)->syncp);	\
+	(st)->counter++;			\
+	u64_stats_update_end(&(st)->syncp);	\
+} while (0)
+
+#define SLIC_GET_STATS_COUNTER(newst, st, counter)			\
+{									\
+	unsigned int start;						\
+	do {							\
+		start = u64_stats_fetch_begin_irq(&(st)->syncp);	\
+		newst = (st)->counter;					\
+	} while (u64_stats_fetch_retry_irq(&(st)->syncp, start));	\
+}
+
+struct slic_upr {
+	dma_addr_t paddr;
+	unsigned int type;
+	struct list_head list;
+};
+
+struct slic_upr_list {
+	bool pending;
+	struct list_head list;
+	/* upr list lock */
+	spinlock_t lock;
+};
+
+/* SLIC EEPROM structure for Mojave */
+struct slic_mojave_eeprom {
+	__le16 id;		/* 00 EEPROM/FLASH Magic code 'A5A5'*/
+	__le16 eeprom_code_size;/* 01 Size of EEPROM Codes (bytes * 4)*/
+	__le16 flash_size;	/* 02 Flash size */
+	__le16 eeprom_size;	/* 03 EEPROM Size */
+	__le16 vendor_id;	/* 04 Vendor ID */
+	__le16 dev_id;		/* 05 Device ID */
+	u8 rev_id;		/* 06 Revision ID */
+	u8 class_code[3];	/* 07 Class Code */
+	u8 irqpin_dbg;		/* 08 Debug Interrupt pin */
+	u8 irqpin;		/*    Network Interrupt Pin */
+	u8 min_grant;		/* 09 Minimum grant */
+	u8 max_lat;		/*    Maximum Latency */
+	__le16 pci_stat;	/* 10 PCI Status */
+	__le16 sub_vendor_id;	/* 11 Subsystem Vendor Id */
+	__le16 sub_id;		/* 12 Subsystem ID */
+	__le16 dev_id_dbg;	/* 13 Debug Device Id */
+	__le16 ramrom;		/* 14 Dram/Rom function */
+	__le16 dram_size2pci;	/* 15 DRAM size to PCI (bytes * 64K) */
+	__le16 rom_size2pci;	/* 16 ROM extension size to PCI (bytes * 4k) */
+	u8 pad[2];		/* 17 Padding */
+	u8 freetime;		/* 18 FreeTime setting */
+	u8 ifctrl;		/* 10-bit interface control (Mojave only) */
+	__le16 dram_size;	/* 19 DRAM size (bytes * 64k) */
+	u8 mac[ETH_ALEN];	/* 20 MAC addresses */
+	u8 mac2[ETH_ALEN];
+	u8 pad2[6];
+	u16 dev_id2;		/* Device ID for 2nd PCI function */
+	u8 irqpin2;		/* Interrupt pin for 2nd PCI function */
+	u8 class_code2[3];	/* Class Code for 2nd PCI function */
+	u16 cfg_byte6;		/* Config Byte 6 */
+	u16 pme_cap;		/* Power Mgment capabilities */
+	u16 nwclk_ctrl;		/* NetworkClockControls */
+	u8 fru_format;		/* Alacritech FRU format type */
+	u8 fru_assembly[6];	/* Alacritech FRU information */
+	u8 fru_rev[2];
+	u8 fru_serial[14];
+	u8 fru_pad[3];
+	u8 oem_fru[28];		/* optional OEM FRU format type */
+	u8 pad3[4];		/* Pad to 128 bytes - includes 2 cksum bytes
+				 * (if OEM FRU info exists) and two unusable
+				 * bytes at the end
+				 */
+};
+
+/* SLIC EEPROM structure for Oasis */
+struct slic_oasis_eeprom {
+	__le16 id;		/* 00 EEPROM/FLASH Magic code 'A5A5' */
+	__le16 eeprom_code_size;/* 01 Size of EEPROM Codes (bytes * 4)*/
+	__le16 spidev0_cfg;	/* 02 Flash Config for SPI device 0 */
+	__le16 spidev1_cfg;	/* 03 Flash Config for SPI device 1 */
+	__le16 vendor_id;	/* 04 Vendor ID */
+	__le16 dev_id;		/* 05 Device ID (function 0) */
+	u8 rev_id;		/* 06 Revision ID */
+	u8 class_code0[3];	/* 07 Class Code for PCI function 0 */
+	u8 irqpin1;		/* 08 Interrupt pin for PCI function 1*/
+	u8 class_code1[3];	/* 09 Class Code for PCI function 1 */
+	u8 irqpin2;		/* 10 Interrupt pin for PCI function 2*/
+	u8 irqpin0;		/*    Interrupt pin for PCI function 0*/
+	u8 min_grant;		/* 11 Minimum grant */
+	u8 max_lat;		/*    Maximum Latency */
+	__le16 sub_vendor_id;	/* 12 Subsystem Vendor Id */
+	__le16 sub_id;		/* 13 Subsystem ID */
+	__le16 flash_size;	/* 14 Flash size (bytes / 4K) */
+	__le16 dram_size2pci;	/* 15 DRAM size to PCI (bytes / 64K) */
+	__le16 rom_size2pci;	/* 16 Flash (ROM extension) size to PCI
+				 *   (bytes / 4K)
+				 */
+	__le16 dev_id1;		/* 17 Device Id (function 1) */
+	__le16 dev_id2;		/* 18 Device Id (function 2) */
+	__le16 dev_stat_cfg;	/* 19 Device Status Config Bytes 6-7 */
+	__le16 pme_cap;		/* 20 Power Mgment capabilities */
+	u8 msi_cap;		/* 21 MSI capabilities */
+	u8 clock_div;		/*    Clock divider */
+	__le16 pci_stat_lo;	/* 22 PCI Status bits 15:0 */
+	__le16 pci_stat_hi;	/* 23 PCI Status bits 31:16 */
+	__le16 dram_cfg_lo;	/* 24 DRAM Configuration bits 15:0 */
+	__le16 dram_cfg_hi;	/* 25 DRAM Configuration bits 31:16 */
+	__le16 dram_size;	/* 26 DRAM size (bytes / 64K) */
+	__le16 gpio_tbi_ctrl;	/* 27 GPIO/TBI controls for functions 1/0 */
+	__le16 eeprom_size;	/* 28 EEPROM Size */
+	u8 mac[ETH_ALEN];	/* 29 MAC addresses (2 ports) */
+	u8 mac2[ETH_ALEN];
+	u8 fru_format;		/* 35 Alacritech FRU format type */
+	u8 fru_assembly[6];	/* Alacritech FRU information */
+	u8 fru_rev[2];
+	u8 fru_serial[14];
+	u8 fru_pad[3];
+	u8 oem_fru[28];		/* optional OEM FRU information */
+	u8 pad[4];		/* Pad to 128 bytes - includes 2 checksum bytes
+				 * (if OEM FRU info exists) and two unusable
+				 * bytes at the end
+				 */
+};
+
+struct slic_stats {
+	u64 rx_packets;
+	u64 rx_bytes;
+	u64 rx_mcasts;
+	u64 rx_errors;
+	u64 tx_packets;
+	u64 tx_bytes;
+	/* HW STATS */
+	u64 rx_buff_miss;
+	u64 tx_dropped;
+	u64 irq_errs;
+	/* transport layer */
+	u64 rx_tpcsum;
+	u64 rx_tpoflow;
+	u64 rx_tphlen;
+	/* ip layer */
+	u64 rx_ipcsum;
+	u64 rx_iplen;
+	u64 rx_iphlen;
+	/* link layer */
+	u64 rx_early;
+	u64 rx_buffoflow;
+	u64 rx_lcode;
+	u64 rx_drbl;
+	u64 rx_crc;
+	u64 rx_oflow802;
+	u64 rx_uflow802;
+	/* oasis only */
+	u64 tx_carrier;
+	struct u64_stats_sync syncp;
+};
+
+struct slic_shmem_data {
+	__le32 isr;
+	__le32 link;
+};
+
+struct slic_shmem {
+	dma_addr_t isr_paddr;
+	dma_addr_t link_paddr;
+	struct slic_shmem_data *shmem_data;
+};
+
+struct slic_rx_info_oasis {
+	__le32 frame_status;
+	__le32 frame_status_b;
+	__le32 time_stamp;
+	__le32 checksum;
+};
+
+struct slic_rx_info_mojave {
+	__le32 frame_status;
+	__le16 byte_cnt;
+	__le16 tp_chksum;
+	__le16 ctx_hash;
+	__le16 mac_hash;
+	__le16 buff_lnk;
+};
+
+struct slic_stat_desc {
+	__le32 hnd;
+	__u8 pad[8];
+	__le32 status;
+	__u8 pad2[16];
+};
+
+struct slic_stat_queue {
+	struct slic_stat_desc *descs[SLIC_NUM_STAT_DESC_ARRAYS];
+	dma_addr_t paddr[SLIC_NUM_STAT_DESC_ARRAYS];
+	unsigned int addr_offset[SLIC_NUM_STAT_DESC_ARRAYS];
+	unsigned int active_array;
+	unsigned int len;
+	unsigned int done_idx;
+	size_t mem_size;
+};
+
+struct slic_tx_desc {
+	__le32 hnd;
+	__le32 rsvd;
+	u8 cmd;
+	u8 flags;
+	__le16 rsvd2;
+	__le32 totlen;
+	__le32 paddrl;
+	__le32 paddrh;
+	__le32 len;
+	__le32 type;
+};
+
+struct slic_tx_buffer {
+	struct sk_buff *skb;
+	DEFINE_DMA_UNMAP_ADDR(map_addr);
+	DEFINE_DMA_UNMAP_LEN(map_len);
+	struct slic_tx_desc *desc;
+	dma_addr_t desc_paddr;
+};
+
+struct slic_tx_queue {
+	struct dma_pool *dma_pool;
+	struct slic_tx_buffer *txbuffs;
+	unsigned int len;
+	unsigned int put_idx;
+	unsigned int done_idx;
+};
+
+struct slic_rx_desc {
+	u8 pad[16];
+	__le32 buffer;
+	__le32 length;
+	__le32 status;
+};
+
+struct slic_rx_buffer {
+	struct sk_buff *skb;
+	DEFINE_DMA_UNMAP_ADDR(map_addr);
+	DEFINE_DMA_UNMAP_LEN(map_len);
+	unsigned int addr_offset;
+};
+
+struct slic_rx_queue {
+	struct slic_rx_buffer *rxbuffs;
+	unsigned int len;
+	unsigned int done_idx;
+	unsigned int put_idx;
+};
+
+struct slic_device {
+	struct pci_dev *pdev;
+	struct net_device *netdev;
+	void __iomem *regs;
+	/* upper address setting lock */
+	spinlock_t upper_lock;
+	struct slic_shmem shmem;
+	struct napi_struct napi;
+	struct slic_rx_queue rxq;
+	struct slic_tx_queue txq;
+	struct slic_stat_queue stq;
+	struct slic_stats stats;
+	struct slic_upr_list upr_list;
+	/* link configuration lock */
+	spinlock_t link_lock;
+	bool promisc;
+	int speed;
+	unsigned int duplex;
+	bool is_fiber;
+	unsigned char model;
+};
+
+static inline u32 slic_read(struct slic_device *sdev, unsigned int reg)
+{
+	return ioread32(sdev->regs + reg);
+}
+
+static inline void slic_write(struct slic_device *sdev, unsigned int reg,
+			      u32 val)
+{
+	iowrite32(val, sdev->regs + reg);
+}
+
+static inline void slic_flush_write(struct slic_device *sdev)
+{
+	(void)ioread32(sdev->regs + SLIC_REG_HOSTID);
+}
+
+#endif /* _SLIC_H */
diff --git a/drivers/net/ethernet/alacritech/slicoss.c b/drivers/net/ethernet/alacritech/slicoss.c
new file mode 100644
index 0000000..e77ecd5
--- /dev/null
+++ b/drivers/net/ethernet/alacritech/slicoss.c
@@ -0,0 +1,1882 @@
+/*
+ * Driver for Gigabit Ethernet adapters based on the Session Layer
+ * Interface (SLIC) technology by Alacritech. The driver does not
+ * support the hardware acceleration features provided by these cards.
+ *
+ * Copyright (C) 2016 Lino Sanfilippo <LinoSanfilippo@gmx.de>
+ *
+ * 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.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/crc32.h>
+#include <linux/dma-mapping.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/list.h>
+#include <linux/u64_stats_sync.h>
+
+#include "slic.h"
+
+#define DRV_NAME			"slicoss"
+#define DRV_VERSION			"1.0"
+
+static const struct pci_device_id slic_id_tbl[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_ALACRITECH,
+		     PCI_DEVICE_ID_ALACRITECH_MOJAVE) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_ALACRITECH,
+		     PCI_DEVICE_ID_ALACRITECH_OASIS) },
+	{ 0 }
+};
+
+static const char slic_stats_strings[][ETH_GSTRING_LEN] = {
+	"rx_packets",
+	"rx_bytes",
+	"rx_multicasts",
+	"rx_errors",
+	"rx_buff_miss",
+	"rx_tp_csum",
+	"rx_tp_oflow",
+	"rx_tp_hlen",
+	"rx_ip_csum",
+	"rx_ip_len",
+	"rx_ip_hdr_len",
+	"rx_early",
+	"rx_buff_oflow",
+	"rx_lcode",
+	"rx_drbl",
+	"rx_crc",
+	"rx_oflow_802",
+	"rx_uflow_802",
+	"tx_packets",
+	"tx_bytes",
+	"tx_carrier",
+	"tx_dropped",
+	"irq_errs",
+};
+
+static inline int slic_next_queue_idx(unsigned int idx, unsigned int qlen)
+{
+	return (idx + 1) & (qlen - 1);
+}
+
+static inline int slic_get_free_queue_descs(unsigned int put_idx,
+					    unsigned int done_idx,
+					    unsigned int qlen)
+{
+	if (put_idx >= done_idx)
+		return (qlen - (put_idx - done_idx) - 1);
+	return (done_idx - put_idx - 1);
+}
+
+static unsigned int slic_next_compl_idx(struct slic_device *sdev)
+{
+	struct slic_stat_queue *stq = &sdev->stq;
+	unsigned int active = stq->active_array;
+	struct slic_stat_desc *descs;
+	struct slic_stat_desc *stat;
+	unsigned int idx;
+
+	descs = stq->descs[active];
+	stat = &descs[stq->done_idx];
+
+	if (!stat->status)
+		return SLIC_INVALID_STAT_DESC_IDX;
+
+	idx = (le32_to_cpu(stat->hnd) & 0xffff) - 1;
+	/* reset desc */
+	stat->hnd = 0;
+	stat->status = 0;
+
+	stq->done_idx = slic_next_queue_idx(stq->done_idx, stq->len);
+	/* check for wraparound */
+	if (!stq->done_idx) {
+		dma_addr_t paddr = stq->paddr[active];
+
+		slic_write(sdev, SLIC_REG_RBAR, lower_32_bits(paddr) |
+						stq->len);
+		/* make sure new status descriptors are immediately available */
+		slic_flush_write(sdev);
+		active++;
+		active &= (SLIC_NUM_STAT_DESC_ARRAYS - 1);
+		stq->active_array = active;
+	}
+	return idx;
+}
+
+static unsigned int slic_get_free_tx_descs(struct slic_tx_queue *txq)
+{
+	/* ensure tail idx is updated */
+	smp_mb();
+	return slic_get_free_queue_descs(txq->put_idx, txq->done_idx, txq->len);
+}
+
+static unsigned int slic_get_free_rx_descs(struct slic_rx_queue *rxq)
+{
+	return slic_get_free_queue_descs(rxq->put_idx, rxq->done_idx, rxq->len);
+}
+
+static void slic_clear_upr_list(struct slic_upr_list *upr_list)
+{
+	struct slic_upr *upr;
+	struct slic_upr *tmp;
+
+	spin_lock_bh(&upr_list->lock);
+	list_for_each_entry_safe(upr, tmp, &upr_list->list, list) {
+		list_del(&upr->list);
+		kfree(upr);
+	}
+	upr_list->pending = false;
+	spin_unlock_bh(&upr_list->lock);
+}
+
+static void slic_start_upr(struct slic_device *sdev, struct slic_upr *upr)
+{
+	u32 reg;
+
+	reg = (upr->type == SLIC_UPR_CONFIG) ? SLIC_REG_RCONFIG :
+					       SLIC_REG_LSTAT;
+	slic_write(sdev, reg, lower_32_bits(upr->paddr));
+	slic_flush_write(sdev);
+}
+
+static void slic_queue_upr(struct slic_device *sdev, struct slic_upr *upr)
+{
+	struct slic_upr_list *upr_list = &sdev->upr_list;
+	bool pending;
+
+	spin_lock_bh(&upr_list->lock);
+	pending = upr_list->pending;
+	INIT_LIST_HEAD(&upr->list);
+	list_add_tail(&upr->list, &upr_list->list);
+	upr_list->pending = true;
+	spin_unlock_bh(&upr_list->lock);
+
+	if (!pending)
+		slic_start_upr(sdev, upr);
+}
+
+static struct slic_upr *slic_dequeue_upr(struct slic_device *sdev)
+{
+	struct slic_upr_list *upr_list = &sdev->upr_list;
+	struct slic_upr *next_upr = NULL;
+	struct slic_upr *upr = NULL;
+
+	spin_lock_bh(&upr_list->lock);
+	if (!list_empty(&upr_list->list)) {
+		upr = list_first_entry(&upr_list->list, struct slic_upr, list);
+		list_del(&upr->list);
+
+		if (list_empty(&upr_list->list))
+			upr_list->pending = false;
+		else
+			next_upr = list_first_entry(&upr_list->list,
+						    struct slic_upr, list);
+	}
+	spin_unlock_bh(&upr_list->lock);
+	/* trigger processing of the next upr in list */
+	if (next_upr)
+		slic_start_upr(sdev, next_upr);
+
+	return upr;
+}
+
+static int slic_new_upr(struct slic_device *sdev, unsigned int type,
+			dma_addr_t paddr)
+{
+	struct slic_upr *upr;
+
+	upr = kmalloc(sizeof(*upr), GFP_ATOMIC);
+	if (!upr)
+		return -ENOMEM;
+	upr->type = type;
+	upr->paddr = paddr;
+
+	slic_queue_upr(sdev, upr);
+
+	return 0;
+}
+
+static void slic_set_mcast_bit(u64 *mcmask, unsigned char const *addr)
+{
+	u64 mask = *mcmask;
+	u8 crc;
+	/* Get the CRC polynomial for the mac address: we use bits 1-8 (lsb),
+	 * bitwise reversed, msb (= lsb bit 0 before bitrev) is automatically
+	 * discarded.
+	 */
+	crc = ether_crc(ETH_ALEN, addr) >> 23;
+	 /* we only have space on the SLIC for 64 entries */
+	crc &= 0x3F;
+	mask |= (u64)1 << crc;
+	*mcmask = mask;
+}
+
+/* must be called with link_lock held */
+static void slic_configure_rcv(struct slic_device *sdev)
+{
+	u32 val;
+
+	val = SLIC_GRCR_RESET | SLIC_GRCR_ADDRAEN | SLIC_GRCR_RCVEN |
+	      SLIC_GRCR_HASHSIZE << SLIC_GRCR_HASHSIZE_SHIFT | SLIC_GRCR_RCVBAD;
+
+	if (sdev->duplex == DUPLEX_FULL)
+		val |= SLIC_GRCR_CTLEN;
+
+	if (sdev->promisc)
+		val |= SLIC_GRCR_RCVALL;
+
+	slic_write(sdev, SLIC_REG_WRCFG, val);
+}
+
+/* must be called with link_lock held */
+static void slic_configure_xmt(struct slic_device *sdev)
+{
+	u32 val;
+
+	val = SLIC_GXCR_RESET | SLIC_GXCR_XMTEN;
+
+	if (sdev->duplex == DUPLEX_FULL)
+		val |= SLIC_GXCR_PAUSEEN;
+
+	slic_write(sdev, SLIC_REG_WXCFG, val);
+}
+
+/* must be called with link_lock held */
+static void slic_configure_mac(struct slic_device *sdev)
+{
+	u32 val;
+
+	if (sdev->speed == SPEED_1000) {
+		val = SLIC_GMCR_GAPBB_1000 << SLIC_GMCR_GAPBB_SHIFT |
+		      SLIC_GMCR_GAPR1_1000 << SLIC_GMCR_GAPR1_SHIFT |
+		      SLIC_GMCR_GAPR2_1000 << SLIC_GMCR_GAPR2_SHIFT |
+		      SLIC_GMCR_GBIT; /* enable GMII */
+	} else {
+		val = SLIC_GMCR_GAPBB_100 << SLIC_GMCR_GAPBB_SHIFT |
+		      SLIC_GMCR_GAPR1_100 << SLIC_GMCR_GAPR1_SHIFT |
+		      SLIC_GMCR_GAPR2_100 << SLIC_GMCR_GAPR2_SHIFT;
+	}
+
+	if (sdev->duplex == DUPLEX_FULL)
+		val |= SLIC_GMCR_FULLD;
+
+	slic_write(sdev, SLIC_REG_WMCFG, val);
+}
+
+static void slic_configure_link_locked(struct slic_device *sdev, int speed,
+				       unsigned int duplex)
+{
+	struct net_device *dev = sdev->netdev;
+
+	if (sdev->speed == speed && sdev->duplex == duplex)
+		return;
+
+	sdev->speed = speed;
+	sdev->duplex = duplex;
+
+	if (sdev->speed == SPEED_UNKNOWN) {
+		if (netif_carrier_ok(dev))
+			netif_carrier_off(dev);
+	} else {
+		/* (re)configure link settings */
+		slic_configure_mac(sdev);
+		slic_configure_xmt(sdev);
+		slic_configure_rcv(sdev);
+		slic_flush_write(sdev);
+
+		if (!netif_carrier_ok(dev))
+			netif_carrier_on(dev);
+	}
+}
+
+static void slic_configure_link(struct slic_device *sdev, int speed,
+				unsigned int duplex)
+{
+	spin_lock_bh(&sdev->link_lock);
+	slic_configure_link_locked(sdev, speed, duplex);
+	spin_unlock_bh(&sdev->link_lock);
+}
+
+static void slic_set_rx_mode(struct net_device *dev)
+{
+	struct slic_device *sdev = netdev_priv(dev);
+	struct netdev_hw_addr *hwaddr;
+	bool set_promisc;
+	u64 mcmask;
+
+	if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
+		/* Turn on all multicast addresses. We have to do this for
+		 * promiscuous mode as well as ALLMCAST mode (it saves the
+		 * microcode from having to keep state about the MAC
+		 * configuration).
+		 */
+		mcmask = ~(u64)0;
+	} else  {
+		mcmask = 0;
+
+		netdev_for_each_mc_addr(hwaddr, dev) {
+			slic_set_mcast_bit(&mcmask, hwaddr->addr);
+		}
+	}
+
+	slic_write(sdev, SLIC_REG_MCASTLOW, lower_32_bits(mcmask));
+	slic_write(sdev, SLIC_REG_MCASTHIGH, upper_32_bits(mcmask));
+
+	set_promisc = !!(dev->flags & IFF_PROMISC);
+
+	spin_lock_bh(&sdev->link_lock);
+	if (sdev->promisc != set_promisc) {
+		sdev->promisc = set_promisc;
+		slic_configure_rcv(sdev);
+		/* make sure writes to receiver cant leak out of the lock */
+		mmiowb();
+	}
+	spin_unlock_bh(&sdev->link_lock);
+}
+
+static void slic_xmit_complete(struct slic_device *sdev)
+{
+	struct slic_tx_queue *txq = &sdev->txq;
+	struct net_device *dev = sdev->netdev;
+	unsigned int idx = txq->done_idx;
+	struct slic_tx_buffer *buff;
+	unsigned int frames = 0;
+	unsigned int bytes = 0;
+
+	/* Limit processing to SLIC_MAX_TX_COMPLETIONS frames to avoid that new
+	 * completions during processing keeps the loop running endlessly.
+	 */
+	do {
+		idx = slic_next_compl_idx(sdev);
+		if (idx == SLIC_INVALID_STAT_DESC_IDX)
+			break;
+
+		txq->done_idx = idx;
+		buff = &txq->txbuffs[idx];
+
+		if (unlikely(!buff->skb)) {
+			netdev_warn(dev,
+				    "no skb found for desc idx %i\n", idx);
+			continue;
+		}
+		dma_unmap_single(&sdev->pdev->dev,
+				 dma_unmap_addr(buff, map_addr),
+				 dma_unmap_len(buff, map_len), DMA_TO_DEVICE);
+
+		bytes += buff->skb->len;
+		frames++;
+
+		dev_kfree_skb_any(buff->skb);
+		buff->skb = NULL;
+	} while (frames < SLIC_MAX_TX_COMPLETIONS);
+	/* make sure xmit sees the new value for done_idx */
+	smp_wmb();
+
+	u64_stats_update_begin(&sdev->stats.syncp);
+	sdev->stats.tx_bytes += bytes;
+	sdev->stats.tx_packets += frames;
+	u64_stats_update_end(&sdev->stats.syncp);
+
+	netif_tx_lock(dev);
+	if (netif_queue_stopped(dev) &&
+	    (slic_get_free_tx_descs(txq) >= SLIC_MIN_TX_WAKEUP_DESCS))
+		netif_wake_queue(dev);
+	netif_tx_unlock(dev);
+}
+
+static void slic_refill_rx_queue(struct slic_device *sdev, gfp_t gfp)
+{
+	const unsigned int ALIGN_MASK = SLIC_RX_BUFF_ALIGN - 1;
+	unsigned int maplen = SLIC_RX_BUFF_SIZE;
+	struct slic_rx_queue *rxq = &sdev->rxq;
+	struct net_device *dev = sdev->netdev;
+	struct slic_rx_buffer *buff;
+	struct slic_rx_desc *desc;
+	unsigned int misalign;
+	unsigned int offset;
+	struct sk_buff *skb;
+	dma_addr_t paddr;
+
+	while (slic_get_free_rx_descs(rxq) > SLIC_MAX_REQ_RX_DESCS) {
+		skb = alloc_skb(maplen + ALIGN_MASK, gfp);
+		if (!skb)
+			break;
+
+		paddr = dma_map_single(&sdev->pdev->dev, skb->data, maplen,
+				       DMA_FROM_DEVICE);
+		if (dma_mapping_error(&sdev->pdev->dev, paddr)) {
+			netdev_err(dev, "mapping rx packet failed\n");
+			/* drop skb */
+			dev_kfree_skb_any(skb);
+			break;
+		}
+		/* ensure head buffer descriptors are 256 byte aligned */
+		offset = 0;
+		misalign = paddr & ALIGN_MASK;
+		if (misalign) {
+			offset = SLIC_RX_BUFF_ALIGN - misalign;
+			skb_reserve(skb, offset);
+		}
+		/* the HW expects dma chunks for descriptor + frame data */
+		desc = (struct slic_rx_desc *)skb->data;
+		/* temporarily sync descriptor for CPU to clear status */
+		dma_sync_single_for_cpu(&sdev->pdev->dev, paddr,
+					offset + sizeof(*desc),
+					DMA_FROM_DEVICE);
+		desc->status = 0;
+		/* return it to HW again */
+		dma_sync_single_for_device(&sdev->pdev->dev, paddr,
+					   offset + sizeof(*desc),
+					   DMA_FROM_DEVICE);
+
+		buff = &rxq->rxbuffs[rxq->put_idx];
+		buff->skb = skb;
+		dma_unmap_addr_set(buff, map_addr, paddr);
+		dma_unmap_len_set(buff, map_len, maplen);
+		buff->addr_offset = offset;
+		/* complete write to descriptor before it is handed to HW */
+		wmb();
+		/* head buffer descriptors are placed immediately before skb */
+		slic_write(sdev, SLIC_REG_HBAR, lower_32_bits(paddr) + offset);
+		rxq->put_idx = slic_next_queue_idx(rxq->put_idx, rxq->len);
+	}
+}
+
+static void slic_handle_frame_error(struct slic_device *sdev,
+				    struct sk_buff *skb)
+{
+	struct slic_stats *stats = &sdev->stats;
+
+	if (sdev->model == SLIC_MODEL_OASIS) {
+		struct slic_rx_info_oasis *info;
+		u32 status_b;
+		u32 status;
+
+		info = (struct slic_rx_info_oasis *)skb->data;
+		status = le32_to_cpu(info->frame_status);
+		status_b = le32_to_cpu(info->frame_status_b);
+		/* transport layer */
+		if (status_b & SLIC_VRHSTATB_TPCSUM)
+			SLIC_INC_STATS_COUNTER(stats, rx_tpcsum);
+		if (status & SLIC_VRHSTAT_TPOFLO)
+			SLIC_INC_STATS_COUNTER(stats, rx_tpoflow);
+		if (status_b & SLIC_VRHSTATB_TPHLEN)
+			SLIC_INC_STATS_COUNTER(stats, rx_tphlen);
+		/* ip layer */
+		if (status_b & SLIC_VRHSTATB_IPCSUM)
+			SLIC_INC_STATS_COUNTER(stats, rx_ipcsum);
+		if (status_b & SLIC_VRHSTATB_IPLERR)
+			SLIC_INC_STATS_COUNTER(stats, rx_iplen);
+		if (status_b & SLIC_VRHSTATB_IPHERR)
+			SLIC_INC_STATS_COUNTER(stats, rx_iphlen);
+		/* link layer */
+		if (status_b & SLIC_VRHSTATB_RCVE)
+			SLIC_INC_STATS_COUNTER(stats, rx_early);
+		if (status_b & SLIC_VRHSTATB_BUFF)
+			SLIC_INC_STATS_COUNTER(stats, rx_buffoflow);
+		if (status_b & SLIC_VRHSTATB_CODE)
+			SLIC_INC_STATS_COUNTER(stats, rx_lcode);
+		if (status_b & SLIC_VRHSTATB_DRBL)
+			SLIC_INC_STATS_COUNTER(stats, rx_drbl);
+		if (status_b & SLIC_VRHSTATB_CRC)
+			SLIC_INC_STATS_COUNTER(stats, rx_crc);
+		if (status & SLIC_VRHSTAT_802OE)
+			SLIC_INC_STATS_COUNTER(stats, rx_oflow802);
+		if (status_b & SLIC_VRHSTATB_802UE)
+			SLIC_INC_STATS_COUNTER(stats, rx_uflow802);
+		if (status_b & SLIC_VRHSTATB_CARRE)
+			SLIC_INC_STATS_COUNTER(stats, tx_carrier);
+	} else { /* mojave */
+		struct slic_rx_info_mojave *info;
+		u32 status;
+
+		info = (struct slic_rx_info_mojave *)skb->data;
+		status = le32_to_cpu(info->frame_status);
+		/* transport layer */
+		if (status & SLIC_VGBSTAT_XPERR) {
+			u32 xerr = status >> SLIC_VGBSTAT_XERRSHFT;
+
+			if (xerr == SLIC_VGBSTAT_XCSERR)
+				SLIC_INC_STATS_COUNTER(stats, rx_tpcsum);
+			if (xerr == SLIC_VGBSTAT_XUFLOW)
+				SLIC_INC_STATS_COUNTER(stats, rx_tpoflow);
+			if (xerr == SLIC_VGBSTAT_XHLEN)
+				SLIC_INC_STATS_COUNTER(stats, rx_tphlen);
+		}
+		/* ip layer */
+		if (status & SLIC_VGBSTAT_NETERR) {
+			u32 nerr = status >> SLIC_VGBSTAT_NERRSHFT &
+				   SLIC_VGBSTAT_NERRMSK;
+
+			if (nerr == SLIC_VGBSTAT_NCSERR)
+				SLIC_INC_STATS_COUNTER(stats, rx_ipcsum);
+			if (nerr == SLIC_VGBSTAT_NUFLOW)
+				SLIC_INC_STATS_COUNTER(stats, rx_iplen);
+			if (nerr == SLIC_VGBSTAT_NHLEN)
+				SLIC_INC_STATS_COUNTER(stats, rx_iphlen);
+		}
+		/* link layer */
+		if (status & SLIC_VGBSTAT_LNKERR) {
+			u32 lerr = status & SLIC_VGBSTAT_LERRMSK;
+
+			if (lerr == SLIC_VGBSTAT_LDEARLY)
+				SLIC_INC_STATS_COUNTER(stats, rx_early);
+			if (lerr == SLIC_VGBSTAT_LBOFLO)
+				SLIC_INC_STATS_COUNTER(stats, rx_buffoflow);
+			if (lerr == SLIC_VGBSTAT_LCODERR)
+				SLIC_INC_STATS_COUNTER(stats, rx_lcode);
+			if (lerr == SLIC_VGBSTAT_LDBLNBL)
+				SLIC_INC_STATS_COUNTER(stats, rx_drbl);
+			if (lerr == SLIC_VGBSTAT_LCRCERR)
+				SLIC_INC_STATS_COUNTER(stats, rx_crc);
+			if (lerr == SLIC_VGBSTAT_LOFLO)
+				SLIC_INC_STATS_COUNTER(stats, rx_oflow802);
+			if (lerr == SLIC_VGBSTAT_LUFLO)
+				SLIC_INC_STATS_COUNTER(stats, rx_uflow802);
+		}
+	}
+	SLIC_INC_STATS_COUNTER(stats, rx_errors);
+}
+
+static void slic_handle_receive(struct slic_device *sdev, unsigned int todo,
+				unsigned int *done)
+{
+	struct slic_rx_queue *rxq = &sdev->rxq;
+	struct net_device *dev = sdev->netdev;
+	struct slic_rx_buffer *buff;
+	struct slic_rx_desc *desc;
+	unsigned int frames = 0;
+	unsigned int bytes = 0;
+	struct sk_buff *skb;
+	u32 status;
+	u32 len;
+
+	while (todo && (rxq->done_idx != rxq->put_idx)) {
+		buff = &rxq->rxbuffs[rxq->done_idx];
+
+		skb = buff->skb;
+		if (!skb)
+			break;
+
+		desc = (struct slic_rx_desc *)skb->data;
+
+		dma_sync_single_for_cpu(&sdev->pdev->dev,
+					dma_unmap_addr(buff, map_addr),
+					buff->addr_offset + sizeof(*desc),
+					DMA_FROM_DEVICE);
+
+		status = le32_to_cpu(desc->status);
+		if (!(status & SLIC_IRHDDR_SVALID)) {
+			dma_sync_single_for_device(&sdev->pdev->dev,
+						   dma_unmap_addr(buff,
+								  map_addr),
+						   buff->addr_offset +
+						   sizeof(*desc),
+						   DMA_FROM_DEVICE);
+			break;
+		}
+
+		buff->skb = NULL;
+
+		dma_unmap_single(&sdev->pdev->dev,
+				 dma_unmap_addr(buff, map_addr),
+				 dma_unmap_len(buff, map_len),
+				 DMA_FROM_DEVICE);
+
+		/* skip rx descriptor that is placed before the frame data */
+		skb_reserve(skb, SLIC_RX_BUFF_HDR_SIZE);
+
+		if (unlikely(status & SLIC_IRHDDR_ERR)) {
+			slic_handle_frame_error(sdev, skb);
+			dev_kfree_skb_any(skb);
+		} else {
+			struct ethhdr *eh = (struct ethhdr *)skb->data;
+
+			if (is_multicast_ether_addr(eh->h_dest))
+				SLIC_INC_STATS_COUNTER(&sdev->stats, rx_mcasts);
+
+			len = le32_to_cpu(desc->length) & SLIC_IRHDDR_FLEN_MSK;
+			skb_put(skb, len);
+			skb->protocol = eth_type_trans(skb, dev);
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+			napi_gro_receive(&sdev->napi, skb);
+
+			bytes += len;
+			frames++;
+		}
+		rxq->done_idx = slic_next_queue_idx(rxq->done_idx, rxq->len);
+		todo--;
+	}
+
+	u64_stats_update_begin(&sdev->stats.syncp);
+	sdev->stats.rx_bytes += bytes;
+	sdev->stats.rx_packets += frames;
+	u64_stats_update_end(&sdev->stats.syncp);
+
+	slic_refill_rx_queue(sdev, GFP_ATOMIC);
+}
+
+static void slic_handle_link_irq(struct slic_device *sdev)
+{
+	struct slic_shmem *sm = &sdev->shmem;
+	struct slic_shmem_data *sm_data = sm->shmem_data;
+	unsigned int duplex;
+	int speed;
+	u32 link;
+
+	link = le32_to_cpu(sm_data->link);
+
+	if (link & SLIC_GIG_LINKUP) {
+		if (link & SLIC_GIG_SPEED_1000)
+			speed = SPEED_1000;
+		else if (link & SLIC_GIG_SPEED_100)
+			speed = SPEED_100;
+		else
+			speed = SPEED_10;
+
+		duplex = (link & SLIC_GIG_FULLDUPLEX) ? DUPLEX_FULL :
+							DUPLEX_HALF;
+	} else {
+		duplex = DUPLEX_UNKNOWN;
+		speed = SPEED_UNKNOWN;
+	}
+	slic_configure_link(sdev, speed, duplex);
+}
+
+static void slic_handle_upr_irq(struct slic_device *sdev, u32 irqs)
+{
+	struct slic_upr *upr;
+
+	/* remove upr that caused this irq (always the first entry in list) */
+	upr = slic_dequeue_upr(sdev);
+	if (!upr) {
+		netdev_warn(sdev->netdev, "no upr found on list\n");
+		return;
+	}
+
+	if (upr->type == SLIC_UPR_LSTAT) {
+		if (unlikely(irqs & SLIC_ISR_UPCERR_MASK)) {
+			/* try again */
+			slic_queue_upr(sdev, upr);
+			return;
+		}
+		slic_handle_link_irq(sdev);
+	}
+	kfree(upr);
+}
+
+static int slic_handle_link_change(struct slic_device *sdev)
+{
+	return slic_new_upr(sdev, SLIC_UPR_LSTAT, sdev->shmem.link_paddr);
+}
+
+static void slic_handle_err_irq(struct slic_device *sdev, u32 isr)
+{
+	struct slic_stats *stats = &sdev->stats;
+
+	if (isr & SLIC_ISR_RMISS)
+		SLIC_INC_STATS_COUNTER(stats, rx_buff_miss);
+	if (isr & SLIC_ISR_XDROP)
+		SLIC_INC_STATS_COUNTER(stats, tx_dropped);
+	if (!(isr & (SLIC_ISR_RMISS | SLIC_ISR_XDROP)))
+		SLIC_INC_STATS_COUNTER(stats, irq_errs);
+}
+
+static void slic_handle_irq(struct slic_device *sdev, u32 isr,
+			    unsigned int todo, unsigned int *done)
+{
+	if (isr & SLIC_ISR_ERR)
+		slic_handle_err_irq(sdev, isr);
+
+	if (isr & SLIC_ISR_LEVENT)
+		slic_handle_link_change(sdev);
+
+	if (isr & SLIC_ISR_UPC_MASK)
+		slic_handle_upr_irq(sdev, isr);
+
+	if (isr & SLIC_ISR_RCV)
+		slic_handle_receive(sdev, todo, done);
+
+	if (isr & SLIC_ISR_CMD)
+		slic_xmit_complete(sdev);
+}
+
+static int slic_poll(struct napi_struct *napi, int todo)
+{
+	struct slic_device *sdev = container_of(napi, struct slic_device, napi);
+	struct slic_shmem *sm = &sdev->shmem;
+	struct slic_shmem_data *sm_data = sm->shmem_data;
+	u32 isr = le32_to_cpu(sm_data->isr);
+	int done = 0;
+
+	slic_handle_irq(sdev, isr, todo, &done);
+
+	if (done < todo) {
+		napi_complete_done(napi, done);
+		/* reenable irqs */
+		sm_data->isr = 0;
+		/* make sure sm_data->isr is cleard before irqs are reenabled */
+		wmb();
+		slic_write(sdev, SLIC_REG_ISR, 0);
+		slic_flush_write(sdev);
+	}
+
+	return done;
+}
+
+static irqreturn_t slic_irq(int irq, void *dev_id)
+{
+	struct slic_device *sdev = dev_id;
+	struct slic_shmem *sm = &sdev->shmem;
+	struct slic_shmem_data *sm_data = sm->shmem_data;
+
+	slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_MASK);
+	slic_flush_write(sdev);
+	/* make sure sm_data->isr is read after ICR_INT_MASK is set */
+	wmb();
+
+	if (!sm_data->isr) {
+		dma_rmb();
+		/* spurious interrupt */
+		slic_write(sdev, SLIC_REG_ISR, 0);
+		slic_flush_write(sdev);
+		return IRQ_NONE;
+	}
+
+	napi_schedule_irqoff(&sdev->napi);
+
+	return IRQ_HANDLED;
+}
+
+static void slic_card_reset(struct slic_device *sdev)
+{
+	u16 cmd;
+
+	slic_write(sdev, SLIC_REG_RESET, SLIC_RESET_MAGIC);
+	/* flush write by means of config space */
+	pci_read_config_word(sdev->pdev, PCI_COMMAND, &cmd);
+	mdelay(1);
+}
+
+static int slic_init_stat_queue(struct slic_device *sdev)
+{
+	const unsigned int DESC_ALIGN_MASK = SLIC_STATS_DESC_ALIGN - 1;
+	struct slic_stat_queue *stq = &sdev->stq;
+	struct slic_stat_desc *descs;
+	unsigned int misalign;
+	unsigned int offset;
+	dma_addr_t paddr;
+	size_t size;
+	int err;
+	int i;
+
+	stq->len = SLIC_NUM_STAT_DESCS;
+	stq->active_array = 0;
+	stq->done_idx = 0;
+
+	size = stq->len * sizeof(*descs) + DESC_ALIGN_MASK;
+
+	for (i = 0; i < SLIC_NUM_STAT_DESC_ARRAYS; i++) {
+		descs = dma_zalloc_coherent(&sdev->pdev->dev, size, &paddr,
+					    GFP_KERNEL);
+		if (!descs) {
+			netdev_err(sdev->netdev,
+				   "failed to allocate status descriptors\n");
+			err = -ENOMEM;
+			goto free_descs;
+		}
+		/* ensure correct alignment */
+		offset = 0;
+		misalign = paddr & DESC_ALIGN_MASK;
+		if (misalign) {
+			offset = SLIC_STATS_DESC_ALIGN - misalign;
+			descs += offset;
+			paddr += offset;
+		}
+
+		slic_write(sdev, SLIC_REG_RBAR, lower_32_bits(paddr) |
+						stq->len);
+		stq->descs[i] = descs;
+		stq->paddr[i] = paddr;
+		stq->addr_offset[i] = offset;
+	}
+
+	stq->mem_size = size;
+
+	return 0;
+
+free_descs:
+	while (i--) {
+		dma_free_coherent(&sdev->pdev->dev, stq->mem_size,
+				  stq->descs[i] - stq->addr_offset[i],
+				  stq->paddr[i] - stq->addr_offset[i]);
+	}
+
+	return err;
+}
+
+static void slic_free_stat_queue(struct slic_device *sdev)
+{
+	struct slic_stat_queue *stq = &sdev->stq;
+	int i;
+
+	for (i = 0; i < SLIC_NUM_STAT_DESC_ARRAYS; i++) {
+		dma_free_coherent(&sdev->pdev->dev, stq->mem_size,
+				  stq->descs[i] - stq->addr_offset[i],
+				  stq->paddr[i] - stq->addr_offset[i]);
+	}
+}
+
+static int slic_init_tx_queue(struct slic_device *sdev)
+{
+	struct slic_tx_queue *txq = &sdev->txq;
+	struct slic_tx_buffer *buff;
+	struct slic_tx_desc *desc;
+	unsigned int i;
+	int err;
+
+	txq->len = SLIC_NUM_TX_DESCS;
+	txq->put_idx = 0;
+	txq->done_idx = 0;
+
+	txq->txbuffs = kcalloc(txq->len, sizeof(*buff), GFP_KERNEL);
+	if (!txq->txbuffs)
+		return -ENOMEM;
+
+	txq->dma_pool = dma_pool_create("slic_pool", &sdev->pdev->dev,
+					sizeof(*desc), SLIC_TX_DESC_ALIGN,
+					4096);
+	if (!txq->dma_pool) {
+		err = -ENOMEM;
+		netdev_err(sdev->netdev, "failed to create dma pool\n");
+		goto free_buffs;
+	}
+
+	for (i = 0; i < txq->len; i++) {
+		buff = &txq->txbuffs[i];
+		desc = dma_pool_zalloc(txq->dma_pool, GFP_KERNEL,
+				       &buff->desc_paddr);
+		if (!desc) {
+			netdev_err(sdev->netdev,
+				   "failed to alloc pool chunk (%i)\n", i);
+			err = -ENOMEM;
+			goto free_descs;
+		}
+
+		desc->hnd = cpu_to_le32((u32)(i + 1));
+		desc->cmd = SLIC_CMD_XMT_REQ;
+		desc->flags = 0;
+		desc->type = cpu_to_le32(SLIC_CMD_TYPE_DUMB);
+		buff->desc = desc;
+	}
+
+	return 0;
+
+free_descs:
+	while (i--) {
+		buff = &txq->txbuffs[i];
+		dma_pool_free(txq->dma_pool, buff->desc, buff->desc_paddr);
+	}
+	dma_pool_destroy(txq->dma_pool);
+
+free_buffs:
+	kfree(txq->txbuffs);
+
+	return err;
+}
+
+static void slic_free_tx_queue(struct slic_device *sdev)
+{
+	struct slic_tx_queue *txq = &sdev->txq;
+	struct slic_tx_buffer *buff;
+	unsigned int i;
+
+	for (i = 0; i < txq->len; i++) {
+		buff = &txq->txbuffs[i];
+		dma_pool_free(txq->dma_pool, buff->desc, buff->desc_paddr);
+		if (!buff->skb)
+			continue;
+
+		dma_unmap_single(&sdev->pdev->dev,
+				 dma_unmap_addr(buff, map_addr),
+				 dma_unmap_len(buff, map_len), DMA_TO_DEVICE);
+		consume_skb(buff->skb);
+	}
+	dma_pool_destroy(txq->dma_pool);
+
+	kfree(txq->txbuffs);
+}
+
+static int slic_init_rx_queue(struct slic_device *sdev)
+{
+	struct slic_rx_queue *rxq = &sdev->rxq;
+	struct slic_rx_buffer *buff;
+
+	rxq->len = SLIC_NUM_RX_LES;
+	rxq->done_idx = 0;
+	rxq->put_idx = 0;
+
+	buff = kcalloc(rxq->len, sizeof(*buff), GFP_KERNEL);
+	if (!buff)
+		return -ENOMEM;
+
+	rxq->rxbuffs = buff;
+	slic_refill_rx_queue(sdev, GFP_KERNEL);
+
+	return 0;
+}
+
+static void slic_free_rx_queue(struct slic_device *sdev)
+{
+	struct slic_rx_queue *rxq = &sdev->rxq;
+	struct slic_rx_buffer *buff;
+	unsigned int i;
+
+	/* free rx buffers */
+	for (i = 0; i < rxq->len; i++) {
+		buff = &rxq->rxbuffs[i];
+
+		if (!buff->skb)
+			continue;
+
+		dma_unmap_single(&sdev->pdev->dev,
+				 dma_unmap_addr(buff, map_addr),
+				 dma_unmap_len(buff, map_len),
+				 DMA_FROM_DEVICE);
+		consume_skb(buff->skb);
+	}
+	kfree(rxq->rxbuffs);
+}
+
+static void slic_set_link_autoneg(struct slic_device *sdev)
+{
+	unsigned int subid = sdev->pdev->subsystem_device;
+	u32 val;
+
+	if (sdev->is_fiber) {
+		/* We've got a fiber gigabit interface, and register 4 is
+		 * different in fiber mode than in copper mode.
+		 */
+		/* advertise FD only @1000 Mb */
+		val = MII_ADVERTISE << 16 | ADVERTISE_1000XFULL |
+		      ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM;
+		/* enable PAUSE frames */
+		slic_write(sdev, SLIC_REG_WPHY, val);
+		/* reset phy, enable auto-neg  */
+		val = MII_BMCR << 16 | BMCR_RESET | BMCR_ANENABLE |
+		      BMCR_ANRESTART;
+		slic_write(sdev, SLIC_REG_WPHY, val);
+	} else {	/* copper gigabit */
+		/* We've got a copper gigabit interface, and register 4 is
+		 * different in copper mode than in fiber mode.
+		 */
+		/* advertise 10/100 Mb modes   */
+		val = MII_ADVERTISE << 16 | ADVERTISE_100FULL |
+		      ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF;
+		/* enable PAUSE frames  */
+		val |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+		/* required by the Cicada PHY  */
+		val |= ADVERTISE_CSMA;
+		slic_write(sdev, SLIC_REG_WPHY, val);
+
+		/* advertise FD only @1000 Mb  */
+		val = MII_CTRL1000 << 16 | ADVERTISE_1000FULL;
+		slic_write(sdev, SLIC_REG_WPHY, val);
+
+		if (subid != PCI_SUBDEVICE_ID_ALACRITECH_CICADA) {
+			 /* if a Marvell PHY enable auto crossover */
+			val = SLIC_MIICR_REG_16 | SLIC_MRV_REG16_XOVERON;
+			slic_write(sdev, SLIC_REG_WPHY, val);
+
+			/* reset phy, enable auto-neg  */
+			val = MII_BMCR << 16 | BMCR_RESET | BMCR_ANENABLE |
+			      BMCR_ANRESTART;
+			slic_write(sdev, SLIC_REG_WPHY, val);
+		} else {
+			/* enable and restart auto-neg (don't reset)  */
+			val = MII_BMCR << 16 | BMCR_ANENABLE | BMCR_ANRESTART;
+			slic_write(sdev, SLIC_REG_WPHY, val);
+		}
+	}
+}
+
+static void slic_set_mac_address(struct slic_device *sdev)
+{
+	u8 *addr = sdev->netdev->dev_addr;
+	u32 val;
+
+	val = addr[5] | addr[4] << 8 | addr[3] << 16 | addr[2] << 24;
+
+	slic_write(sdev, SLIC_REG_WRADDRAL, val);
+	slic_write(sdev, SLIC_REG_WRADDRBL, val);
+
+	val = addr[0] << 8 | addr[1];
+
+	slic_write(sdev, SLIC_REG_WRADDRAH, val);
+	slic_write(sdev, SLIC_REG_WRADDRBH, val);
+	slic_flush_write(sdev);
+}
+
+static u32 slic_read_dword_from_firmware(const struct firmware *fw, int *offset)
+{
+	int idx = *offset;
+	__le32 val;
+
+	memcpy(&val, fw->data + *offset, sizeof(val));
+	idx += 4;
+	*offset = idx;
+
+	return le32_to_cpu(val);
+}
+
+MODULE_FIRMWARE(SLIC_RCV_FIRMWARE_MOJAVE);
+MODULE_FIRMWARE(SLIC_RCV_FIRMWARE_OASIS);
+
+static int slic_load_rcvseq_firmware(struct slic_device *sdev)
+{
+	const struct firmware *fw;
+	const char *file;
+	u32 codelen;
+	int idx = 0;
+	u32 instr;
+	u32 addr;
+	int err;
+
+	file = (sdev->model == SLIC_MODEL_OASIS) ?  SLIC_RCV_FIRMWARE_OASIS :
+						    SLIC_RCV_FIRMWARE_MOJAVE;
+	err = request_firmware(&fw, file, &sdev->pdev->dev);
+	if (err) {
+		dev_err(&sdev->pdev->dev,
+			"failed to load receive sequencer firmware %s\n", file);
+		return err;
+	}
+	/* Do an initial sanity check concerning firmware size now. A further
+	 * check follows below.
+	 */
+	if (fw->size < SLIC_FIRMWARE_MIN_SIZE) {
+		dev_err(&sdev->pdev->dev,
+			"invalid firmware size %zu (min %u expected)\n",
+			fw->size, SLIC_FIRMWARE_MIN_SIZE);
+		err = -EINVAL;
+		goto release;
+	}
+
+	codelen = slic_read_dword_from_firmware(fw, &idx);
+
+	/* do another sanity check against firmware size */
+	if ((codelen + 4) > fw->size) {
+		dev_err(&sdev->pdev->dev,
+			"invalid rcv-sequencer firmware size %zu\n", fw->size);
+		err = -EINVAL;
+		goto release;
+	}
+
+	/* download sequencer code to card */
+	slic_write(sdev, SLIC_REG_RCV_WCS, SLIC_RCVWCS_BEGIN);
+	for (addr = 0; addr < codelen; addr++) {
+		__le32 val;
+		/* write out instruction address */
+		slic_write(sdev, SLIC_REG_RCV_WCS, addr);
+
+		instr = slic_read_dword_from_firmware(fw, &idx);
+		/* write out the instruction data low addr */
+		slic_write(sdev, SLIC_REG_RCV_WCS, instr);
+
+		val = (__le32)fw->data[idx];
+		instr = le32_to_cpu(val);
+		idx++;
+		/* write out the instruction data high addr */
+		slic_write(sdev, SLIC_REG_RCV_WCS, instr);
+	}
+	/* finish download */
+	slic_write(sdev, SLIC_REG_RCV_WCS, SLIC_RCVWCS_FINISH);
+	slic_flush_write(sdev);
+release:
+	release_firmware(fw);
+
+	return err;
+}
+
+MODULE_FIRMWARE(SLIC_FIRMWARE_MOJAVE);
+MODULE_FIRMWARE(SLIC_FIRMWARE_OASIS);
+
+static int slic_load_firmware(struct slic_device *sdev)
+{
+	u32 sectstart[SLIC_FIRMWARE_MAX_SECTIONS];
+	u32 sectsize[SLIC_FIRMWARE_MAX_SECTIONS];
+	const struct firmware *fw;
+	unsigned int datalen;
+	const char *file;
+	int code_start;
+	unsigned int i;
+	u32 numsects;
+	int idx = 0;
+	u32 sect;
+	u32 instr;
+	u32 addr;
+	u32 base;
+	int err;
+
+	file = (sdev->model == SLIC_MODEL_OASIS) ?  SLIC_FIRMWARE_OASIS :
+						    SLIC_FIRMWARE_MOJAVE;
+	err = request_firmware(&fw, file, &sdev->pdev->dev);
+	if (err) {
+		dev_err(&sdev->pdev->dev, "failed to load firmware %s\n", file);
+		return err;
+	}
+	/* Do an initial sanity check concerning firmware size now. A further
+	 * check follows below.
+	 */
+	if (fw->size < SLIC_FIRMWARE_MIN_SIZE) {
+		dev_err(&sdev->pdev->dev,
+			"invalid firmware size %zu (min is %u)\n", fw->size,
+			SLIC_FIRMWARE_MIN_SIZE);
+		err = -EINVAL;
+		goto release;
+	}
+
+	numsects = slic_read_dword_from_firmware(fw, &idx);
+	if (numsects == 0 || numsects > SLIC_FIRMWARE_MAX_SECTIONS) {
+		dev_err(&sdev->pdev->dev,
+			"invalid number of sections in firmware: %u", numsects);
+		err = -EINVAL;
+		goto release;
+	}
+
+	datalen = numsects * 8 + 4;
+	for (i = 0; i < numsects; i++) {
+		sectsize[i] = slic_read_dword_from_firmware(fw, &idx);
+		datalen += sectsize[i];
+	}
+
+	/* do another sanity check against firmware size */
+	if (datalen > fw->size) {
+		dev_err(&sdev->pdev->dev,
+			"invalid firmware size %zu (expected >= %u)\n",
+			fw->size, datalen);
+		err = -EINVAL;
+		goto release;
+	}
+	/* get sections */
+	for (i = 0; i < numsects; i++)
+		sectstart[i] = slic_read_dword_from_firmware(fw, &idx);
+
+	code_start = idx;
+	instr = slic_read_dword_from_firmware(fw, &idx);
+
+	for (sect = 0; sect < numsects; sect++) {
+		unsigned int ssize = sectsize[sect] >> 3;
+
+		base = sectstart[sect];
+
+		for (addr = 0; addr < ssize; addr++) {
+			/* write out instruction address */
+			slic_write(sdev, SLIC_REG_WCS, base + addr);
+			/* write out instruction to low addr */
+			slic_write(sdev, SLIC_REG_WCS, instr);
+			instr = slic_read_dword_from_firmware(fw, &idx);
+			/* write out instruction to high addr */
+			slic_write(sdev, SLIC_REG_WCS, instr);
+			instr = slic_read_dword_from_firmware(fw, &idx);
+		}
+	}
+
+	idx = code_start;
+
+	for (sect = 0; sect < numsects; sect++) {
+		unsigned int ssize = sectsize[sect] >> 3;
+
+		instr = slic_read_dword_from_firmware(fw, &idx);
+		base = sectstart[sect];
+		if (base < 0x8000)
+			continue;
+
+		for (addr = 0; addr < ssize; addr++) {
+			/* write out instruction address */
+			slic_write(sdev, SLIC_REG_WCS,
+				   SLIC_WCS_COMPARE | (base + addr));
+			/* write out instruction to low addr */
+			slic_write(sdev, SLIC_REG_WCS, instr);
+			instr = slic_read_dword_from_firmware(fw, &idx);
+			/* write out instruction to high addr */
+			slic_write(sdev, SLIC_REG_WCS, instr);
+			instr = slic_read_dword_from_firmware(fw, &idx);
+		}
+	}
+	slic_flush_write(sdev);
+	mdelay(10);
+	/* everything OK, kick off the card */
+	slic_write(sdev, SLIC_REG_WCS, SLIC_WCS_START);
+	slic_flush_write(sdev);
+	/* wait long enough for ucode to init card and reach the mainloop */
+	mdelay(20);
+release:
+	release_firmware(fw);
+
+	return err;
+}
+
+static int slic_init_shmem(struct slic_device *sdev)
+{
+	struct slic_shmem *sm = &sdev->shmem;
+	struct slic_shmem_data *sm_data;
+	dma_addr_t paddr;
+
+	sm_data = dma_zalloc_coherent(&sdev->pdev->dev, sizeof(*sm_data),
+				      &paddr, GFP_KERNEL);
+	if (!sm_data) {
+		dev_err(&sdev->pdev->dev, "failed to allocate shared memory\n");
+		return -ENOMEM;
+	}
+
+	sm->shmem_data = sm_data;
+	sm->isr_paddr = paddr;
+	sm->link_paddr = paddr + offsetof(struct slic_shmem_data, link);
+
+	return 0;
+}
+
+static void slic_free_shmem(struct slic_device *sdev)
+{
+	struct slic_shmem *sm = &sdev->shmem;
+	struct slic_shmem_data *sm_data = sm->shmem_data;
+
+	dma_free_coherent(&sdev->pdev->dev, sizeof(*sm_data), sm_data,
+			  sm->isr_paddr);
+}
+
+static int slic_init_iface(struct slic_device *sdev)
+{
+	struct slic_shmem *sm = &sdev->shmem;
+	int err;
+
+	sdev->upr_list.pending = false;
+
+	err = slic_init_shmem(sdev);
+	if (err) {
+		netdev_err(sdev->netdev, "failed to init shared memory\n");
+		return err;
+	}
+
+	err = slic_load_firmware(sdev);
+	if (err) {
+		netdev_err(sdev->netdev, "failed to load firmware\n");
+		goto free_sm;
+	}
+
+	err = slic_load_rcvseq_firmware(sdev);
+	if (err) {
+		netdev_err(sdev->netdev,
+			   "failed to load firmware for receive sequencer\n");
+		goto free_sm;
+	}
+
+	slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_OFF);
+	slic_flush_write(sdev);
+	mdelay(1);
+
+	err = slic_init_rx_queue(sdev);
+	if (err) {
+		netdev_err(sdev->netdev, "failed to init rx queue: %u\n", err);
+		goto free_sm;
+	}
+
+	err = slic_init_tx_queue(sdev);
+	if (err) {
+		netdev_err(sdev->netdev, "failed to init tx queue: %u\n", err);
+		goto free_rxq;
+	}
+
+	err = slic_init_stat_queue(sdev);
+	if (err) {
+		netdev_err(sdev->netdev, "failed to init status queue: %u\n",
+			   err);
+		goto free_txq;
+	}
+
+	slic_write(sdev, SLIC_REG_ISP, lower_32_bits(sm->isr_paddr));
+	napi_enable(&sdev->napi);
+	/* disable irq mitigation */
+	slic_write(sdev, SLIC_REG_INTAGG, 0);
+	slic_write(sdev, SLIC_REG_ISR, 0);
+	slic_flush_write(sdev);
+
+	slic_set_mac_address(sdev);
+
+	spin_lock_bh(&sdev->link_lock);
+	sdev->duplex = DUPLEX_UNKNOWN;
+	sdev->speed = SPEED_UNKNOWN;
+	spin_unlock_bh(&sdev->link_lock);
+
+	slic_set_link_autoneg(sdev);
+
+	err = request_irq(sdev->pdev->irq, slic_irq, IRQF_SHARED, DRV_NAME,
+			  sdev);
+	if (err) {
+		netdev_err(sdev->netdev, "failed to request irq: %u\n", err);
+		goto disable_napi;
+	}
+
+	slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_ON);
+	slic_flush_write(sdev);
+	/* request initial link status */
+	err = slic_handle_link_change(sdev);
+	if (err)
+		netdev_warn(sdev->netdev,
+			    "failed to set initial link state: %u\n", err);
+	return 0;
+
+disable_napi:
+	napi_disable(&sdev->napi);
+	slic_free_stat_queue(sdev);
+free_txq:
+	slic_free_tx_queue(sdev);
+free_rxq:
+	slic_free_rx_queue(sdev);
+free_sm:
+	slic_free_shmem(sdev);
+	slic_card_reset(sdev);
+
+	return err;
+}
+
+static int slic_open(struct net_device *dev)
+{
+	struct slic_device *sdev = netdev_priv(dev);
+	int err;
+
+	netif_carrier_off(dev);
+
+	err = slic_init_iface(sdev);
+	if (err) {
+		netdev_err(dev, "failed to initialize interface: %i\n", err);
+		return err;
+	}
+
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+static int slic_close(struct net_device *dev)
+{
+	struct slic_device *sdev = netdev_priv(dev);
+	u32 val;
+
+	netif_stop_queue(dev);
+
+	/* stop irq handling */
+	napi_disable(&sdev->napi);
+	slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_OFF);
+	slic_write(sdev, SLIC_REG_ISR, 0);
+	slic_flush_write(sdev);
+
+	free_irq(sdev->pdev->irq, sdev);
+	/* turn off RCV and XMT and power down PHY */
+	val = SLIC_GXCR_RESET | SLIC_GXCR_PAUSEEN;
+	slic_write(sdev, SLIC_REG_WXCFG, val);
+
+	val = SLIC_GRCR_RESET | SLIC_GRCR_CTLEN | SLIC_GRCR_ADDRAEN |
+	      SLIC_GRCR_HASHSIZE << SLIC_GRCR_HASHSIZE_SHIFT;
+	slic_write(sdev, SLIC_REG_WRCFG, val);
+
+	val = MII_BMCR << 16 | BMCR_PDOWN;
+	slic_write(sdev, SLIC_REG_WPHY, val);
+	slic_flush_write(sdev);
+
+	slic_clear_upr_list(&sdev->upr_list);
+	slic_write(sdev, SLIC_REG_QUIESCE, 0);
+
+	slic_free_stat_queue(sdev);
+	slic_free_tx_queue(sdev);
+	slic_free_rx_queue(sdev);
+	slic_free_shmem(sdev);
+
+	slic_card_reset(sdev);
+	netif_carrier_off(dev);
+
+	return 0;
+}
+
+static netdev_tx_t slic_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct slic_device *sdev = netdev_priv(dev);
+	struct slic_tx_queue *txq = &sdev->txq;
+	struct slic_tx_buffer *buff;
+	struct slic_tx_desc *desc;
+	dma_addr_t paddr;
+	u32 cbar_val;
+	u32 maplen;
+
+	if (unlikely(slic_get_free_tx_descs(txq) < SLIC_MAX_REQ_TX_DESCS)) {
+		netdev_err(dev, "BUG! not enough tx LEs left: %u\n",
+			   slic_get_free_tx_descs(txq));
+		return NETDEV_TX_BUSY;
+	}
+
+	maplen = skb_headlen(skb);
+	paddr = dma_map_single(&sdev->pdev->dev, skb->data, maplen,
+			       DMA_TO_DEVICE);
+	if (dma_mapping_error(&sdev->pdev->dev, paddr)) {
+		netdev_err(dev, "failed to map tx buffer\n");
+		goto drop_skb;
+	}
+
+	buff = &txq->txbuffs[txq->put_idx];
+	buff->skb = skb;
+	dma_unmap_addr_set(buff, map_addr, paddr);
+	dma_unmap_len_set(buff, map_len, maplen);
+
+	desc = buff->desc;
+	desc->totlen = cpu_to_le32(maplen);
+	desc->paddrl = cpu_to_le32(lower_32_bits(paddr));
+	desc->paddrh = cpu_to_le32(upper_32_bits(paddr));
+	desc->len = cpu_to_le32(maplen);
+
+	txq->put_idx = slic_next_queue_idx(txq->put_idx, txq->len);
+
+	cbar_val = lower_32_bits(buff->desc_paddr) | 1;
+	/* complete writes to RAM and DMA before hardware is informed */
+	wmb();
+
+	slic_write(sdev, SLIC_REG_CBAR, cbar_val);
+
+	if (slic_get_free_tx_descs(txq) < SLIC_MAX_REQ_TX_DESCS)
+		netif_stop_queue(dev);
+	/* make sure writes to io-memory cant leak out of tx queue lock */
+	mmiowb();
+
+	return NETDEV_TX_OK;
+drop_skb:
+	dev_kfree_skb_any(skb);
+
+	return NETDEV_TX_OK;
+}
+
+static struct rtnl_link_stats64 *slic_get_stats(struct net_device *dev,
+						struct rtnl_link_stats64 *lst)
+{
+	struct slic_device *sdev = netdev_priv(dev);
+	struct slic_stats *stats = &sdev->stats;
+
+	SLIC_GET_STATS_COUNTER(lst->rx_packets, stats, rx_packets);
+	SLIC_GET_STATS_COUNTER(lst->tx_packets, stats, tx_packets);
+	SLIC_GET_STATS_COUNTER(lst->rx_bytes, stats, rx_bytes);
+	SLIC_GET_STATS_COUNTER(lst->tx_bytes, stats, tx_bytes);
+	SLIC_GET_STATS_COUNTER(lst->rx_errors, stats, rx_errors);
+	SLIC_GET_STATS_COUNTER(lst->rx_dropped, stats, rx_buff_miss);
+	SLIC_GET_STATS_COUNTER(lst->tx_dropped, stats, tx_dropped);
+	SLIC_GET_STATS_COUNTER(lst->multicast, stats, rx_mcasts);
+	SLIC_GET_STATS_COUNTER(lst->rx_over_errors, stats, rx_buffoflow);
+	SLIC_GET_STATS_COUNTER(lst->rx_crc_errors, stats, rx_crc);
+	SLIC_GET_STATS_COUNTER(lst->rx_fifo_errors, stats, rx_oflow802);
+	SLIC_GET_STATS_COUNTER(lst->tx_carrier_errors, stats, tx_carrier);
+
+	return lst;
+}
+
+static int slic_get_sset_count(struct net_device *dev, int sset)
+{
+	switch (sset) {
+	case ETH_SS_STATS:
+		return ARRAY_SIZE(slic_stats_strings);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static void slic_get_ethtool_stats(struct net_device *dev,
+				   struct ethtool_stats *eth_stats, u64 *data)
+{
+	struct slic_device *sdev = netdev_priv(dev);
+	struct slic_stats *stats = &sdev->stats;
+
+	SLIC_GET_STATS_COUNTER(data[0], stats, rx_packets);
+	SLIC_GET_STATS_COUNTER(data[1], stats, rx_bytes);
+	SLIC_GET_STATS_COUNTER(data[2], stats, rx_mcasts);
+	SLIC_GET_STATS_COUNTER(data[3], stats, rx_errors);
+	SLIC_GET_STATS_COUNTER(data[4], stats, rx_buff_miss);
+	SLIC_GET_STATS_COUNTER(data[5], stats, rx_tpcsum);
+	SLIC_GET_STATS_COUNTER(data[6], stats, rx_tpoflow);
+	SLIC_GET_STATS_COUNTER(data[7], stats, rx_tphlen);
+	SLIC_GET_STATS_COUNTER(data[8], stats, rx_ipcsum);
+	SLIC_GET_STATS_COUNTER(data[9], stats, rx_iplen);
+	SLIC_GET_STATS_COUNTER(data[10], stats, rx_iphlen);
+	SLIC_GET_STATS_COUNTER(data[11], stats, rx_early);
+	SLIC_GET_STATS_COUNTER(data[12], stats, rx_buffoflow);
+	SLIC_GET_STATS_COUNTER(data[13], stats, rx_lcode);
+	SLIC_GET_STATS_COUNTER(data[14], stats, rx_drbl);
+	SLIC_GET_STATS_COUNTER(data[15], stats, rx_crc);
+	SLIC_GET_STATS_COUNTER(data[16], stats, rx_oflow802);
+	SLIC_GET_STATS_COUNTER(data[17], stats, rx_uflow802);
+	SLIC_GET_STATS_COUNTER(data[18], stats, tx_packets);
+	SLIC_GET_STATS_COUNTER(data[19], stats, tx_bytes);
+	SLIC_GET_STATS_COUNTER(data[20], stats, tx_carrier);
+	SLIC_GET_STATS_COUNTER(data[21], stats, tx_dropped);
+	SLIC_GET_STATS_COUNTER(data[22], stats, irq_errs);
+}
+
+static void slic_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+	if (stringset == ETH_SS_STATS) {
+		memcpy(data, slic_stats_strings, sizeof(slic_stats_strings));
+		data += sizeof(slic_stats_strings);
+	}
+}
+
+static void slic_get_drvinfo(struct net_device *dev,
+			     struct ethtool_drvinfo *info)
+{
+	struct slic_device *sdev = netdev_priv(dev);
+
+	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+	strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+	strlcpy(info->bus_info, pci_name(sdev->pdev), sizeof(info->bus_info));
+}
+
+static const struct ethtool_ops slic_ethtool_ops = {
+	.get_drvinfo		= slic_get_drvinfo,
+	.get_link		= ethtool_op_get_link,
+	.get_strings		= slic_get_strings,
+	.get_ethtool_stats	= slic_get_ethtool_stats,
+	.get_sset_count		= slic_get_sset_count,
+};
+
+static const struct net_device_ops slic_netdev_ops = {
+	.ndo_open		= slic_open,
+	.ndo_stop		= slic_close,
+	.ndo_start_xmit		= slic_xmit,
+	.ndo_set_mac_address	= eth_mac_addr,
+	.ndo_get_stats64	= slic_get_stats,
+	.ndo_set_rx_mode	= slic_set_rx_mode,
+	.ndo_change_mtu		= eth_change_mtu,
+	.ndo_validate_addr	= eth_validate_addr,
+};
+
+static u16 slic_eeprom_csum(unsigned char *eeprom, unsigned int len)
+{
+	unsigned char *ptr = eeprom;
+	u32 csum = 0;
+	__le16 data;
+
+	while (len > 1) {
+		memcpy(&data, ptr, sizeof(data));
+		csum += le16_to_cpu(data);
+		ptr += 2;
+		len -= 2;
+	}
+	if (len > 0)
+		csum += *(u8 *)ptr;
+	while (csum >> 16)
+		csum = (csum & 0xFFFF) + ((csum >> 16) & 0xFFFF);
+	return ~csum;
+}
+
+/* check eeprom size, magic and checksum */
+static bool slic_eeprom_valid(unsigned char *eeprom, unsigned int size)
+{
+	const unsigned int MAX_SIZE = 128;
+	const unsigned int MIN_SIZE = 98;
+	__le16 magic;
+	__le16 csum;
+
+	if (size < MIN_SIZE || size > MAX_SIZE)
+		return false;
+	memcpy(&magic, eeprom, sizeof(magic));
+	if (le16_to_cpu(magic) != SLIC_EEPROM_MAGIC)
+		return false;
+	/* cut checksum bytes */
+	size -= 2;
+	memcpy(&csum, eeprom + size, sizeof(csum));
+
+	return (le16_to_cpu(csum) == slic_eeprom_csum(eeprom, size));
+}
+
+static int slic_read_eeprom(struct slic_device *sdev)
+{
+	unsigned int devfn = PCI_FUNC(sdev->pdev->devfn);
+	struct slic_shmem *sm = &sdev->shmem;
+	struct slic_shmem_data *sm_data = sm->shmem_data;
+	const unsigned int MAX_LOOPS = 5000;
+	unsigned int codesize;
+	unsigned char *eeprom;
+	struct slic_upr *upr;
+	unsigned int i = 0;
+	dma_addr_t paddr;
+	int err = 0;
+	u8 *mac[2];
+
+	eeprom = dma_zalloc_coherent(&sdev->pdev->dev, SLIC_EEPROM_SIZE,
+				     &paddr, GFP_KERNEL);
+	if (!eeprom)
+		return -ENOMEM;
+
+	slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_OFF);
+	/* setup ISP temporarily */
+	slic_write(sdev, SLIC_REG_ISP, lower_32_bits(sm->isr_paddr));
+
+	err = slic_new_upr(sdev, SLIC_UPR_CONFIG, paddr);
+	if (!err) {
+		for (i = 0; i < MAX_LOOPS; i++) {
+			if (le32_to_cpu(sm_data->isr) & SLIC_ISR_UPC)
+				break;
+			mdelay(1);
+		}
+		if (i == MAX_LOOPS) {
+			dev_err(&sdev->pdev->dev,
+				"timed out while waiting for eeprom data\n");
+			err = -ETIMEDOUT;
+		}
+		upr = slic_dequeue_upr(sdev);
+		kfree(upr);
+	}
+
+	slic_write(sdev, SLIC_REG_ISP, 0);
+	slic_write(sdev, SLIC_REG_ISR, 0);
+	slic_flush_write(sdev);
+
+	if (err)
+		goto free_eeprom;
+
+	if (sdev->model == SLIC_MODEL_OASIS) {
+		struct slic_oasis_eeprom *oee;
+
+		oee = (struct slic_oasis_eeprom *)eeprom;
+		mac[0] = oee->mac;
+		mac[1] = oee->mac2;
+		codesize = le16_to_cpu(oee->eeprom_code_size);
+	} else {
+		struct slic_mojave_eeprom *mee;
+
+		mee = (struct slic_mojave_eeprom *)eeprom;
+		mac[0] = mee->mac;
+		mac[1] = mee->mac2;
+		codesize = le16_to_cpu(mee->eeprom_code_size);
+	}
+
+	if (!slic_eeprom_valid(eeprom, codesize)) {
+		dev_err(&sdev->pdev->dev, "invalid checksum in eeprom\n");
+		err = -EINVAL;
+		goto free_eeprom;
+	}
+	/* set mac address */
+	ether_addr_copy(sdev->netdev->dev_addr, mac[devfn]);
+free_eeprom:
+	dma_free_coherent(&sdev->pdev->dev, SLIC_EEPROM_SIZE, eeprom, paddr);
+
+	return err;
+}
+
+static int slic_init(struct slic_device *sdev)
+{
+	int err;
+
+	spin_lock_init(&sdev->upper_lock);
+	spin_lock_init(&sdev->link_lock);
+	INIT_LIST_HEAD(&sdev->upr_list.list);
+	spin_lock_init(&sdev->upr_list.lock);
+	u64_stats_init(&sdev->stats.syncp);
+
+	slic_card_reset(sdev);
+
+	err = slic_load_firmware(sdev);
+	if (err) {
+		dev_err(&sdev->pdev->dev, "failed to load firmware\n");
+		return err;
+	}
+
+	/* we need the shared memory to read EEPROM so set it up temporarily */
+	err = slic_init_shmem(sdev);
+	if (err) {
+		dev_err(&sdev->pdev->dev, "failed to init shared memory\n");
+		return err;
+	}
+
+	err = slic_read_eeprom(sdev);
+	if (err) {
+		dev_err(&sdev->pdev->dev, "failed to read eeprom\n");
+		goto free_sm;
+	}
+
+	slic_card_reset(sdev);
+	slic_free_shmem(sdev);
+
+	return 0;
+free_sm:
+	slic_free_shmem(sdev);
+
+	return err;
+}
+
+static bool slic_is_fiber(unsigned short subdev)
+{
+	switch (subdev) {
+	/* Mojave */
+	case PCI_SUBDEVICE_ID_ALACRITECH_1000X1F: /* fallthrough */
+	case PCI_SUBDEVICE_ID_ALACRITECH_SES1001F: /* fallthrough */
+	/* Oasis */
+	case PCI_SUBDEVICE_ID_ALACRITECH_SEN2002XF: /* fallthrough */
+	case PCI_SUBDEVICE_ID_ALACRITECH_SEN2001XF: /* fallthrough */
+	case PCI_SUBDEVICE_ID_ALACRITECH_SEN2104EF: /* fallthrough */
+	case PCI_SUBDEVICE_ID_ALACRITECH_SEN2102EF: /* fallthrough */
+		return true;
+	}
+	return false;
+}
+
+static void slic_configure_pci(struct pci_dev *pdev)
+{
+	u16 old;
+	u16 cmd;
+
+	pci_read_config_word(pdev, PCI_COMMAND, &old);
+
+	cmd = old | PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
+	if (old != cmd)
+		pci_write_config_word(pdev, PCI_COMMAND, cmd);
+}
+
+static int slic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct slic_device *sdev;
+	struct net_device *dev;
+	int err;
+
+	err = pci_enable_device(pdev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to enable PCI device\n");
+		return err;
+	}
+
+	pci_set_master(pdev);
+	pci_try_set_mwi(pdev);
+
+	slic_configure_pci(pdev);
+
+	err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+	if (err) {
+		dev_err(&pdev->dev, "failed to setup DMA\n");
+		goto disable;
+	}
+
+	dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+
+	err = pci_request_regions(pdev, DRV_NAME);
+	if (err) {
+		dev_err(&pdev->dev, "failed to obtain PCI regions\n");
+		goto disable;
+	}
+
+	dev = alloc_etherdev(sizeof(*sdev));
+	if (!dev) {
+		dev_err(&pdev->dev, "failed to alloc ethernet device\n");
+		err = -ENOMEM;
+		goto free_regions;
+	}
+
+	SET_NETDEV_DEV(dev, &pdev->dev);
+	pci_set_drvdata(pdev, dev);
+	dev->irq = pdev->irq;
+	dev->netdev_ops = &slic_netdev_ops;
+	dev->hw_features = NETIF_F_RXCSUM;
+	dev->features |= dev->hw_features;
+
+	dev->ethtool_ops = &slic_ethtool_ops;
+
+	sdev = netdev_priv(dev);
+	sdev->model = (pdev->device == PCI_DEVICE_ID_ALACRITECH_OASIS) ?
+		      SLIC_MODEL_OASIS : SLIC_MODEL_MOJAVE;
+	sdev->is_fiber = slic_is_fiber(pdev->subsystem_device);
+	sdev->pdev = pdev;
+	sdev->netdev = dev;
+	sdev->regs = ioremap_nocache(pci_resource_start(pdev, 0),
+				     pci_resource_len(pdev, 0));
+	if (!sdev->regs) {
+		dev_err(&pdev->dev, "failed to map registers\n");
+		err = -ENOMEM;
+		goto free_netdev;
+	}
+
+	err = slic_init(sdev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to initialize driver\n");
+		goto unmap;
+	}
+
+	netif_napi_add(dev, &sdev->napi, slic_poll, SLIC_NAPI_WEIGHT);
+	netif_carrier_off(dev);
+
+	err = register_netdev(dev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to register net device: %i\n", err);
+		goto unmap;
+	}
+
+	return 0;
+
+unmap:
+	iounmap(sdev->regs);
+free_netdev:
+	free_netdev(dev);
+free_regions:
+	pci_release_regions(pdev);
+disable:
+	pci_disable_device(pdev);
+
+	return err;
+}
+
+static void slic_remove(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct slic_device *sdev = netdev_priv(dev);
+
+	unregister_netdev(dev);
+	iounmap(sdev->regs);
+	free_netdev(dev);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+}
+
+static struct pci_driver slic_driver = {
+	.name = DRV_NAME,
+	.id_table = slic_id_tbl,
+	.probe = slic_probe,
+	.remove = slic_remove,
+};
+
+static int __init slic_init_module(void)
+{
+	return pci_register_driver(&slic_driver);
+}
+
+static void __exit slic_cleanup_module(void)
+{
+	pci_unregister_driver(&slic_driver);
+}
+
+module_init(slic_init_module);
+module_exit(slic_cleanup_module);
+
+MODULE_DESCRIPTION("Alacritech non-accelerated SLIC driver");
+MODULE_AUTHOR("Lino Sanfilippo <LinoSanfilippo@gmx.de>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
-- 
1.9.1

^ permalink raw reply related

* Gigabit ethernet driver for Alacritechs SLIC devices (v4)
From: Lino Sanfilippo @ 2016-12-05 22:07 UTC (permalink / raw)
  To: davem, charrer, liodot, gregkh
  Cc: andrew, roszenrami, markus.boehme, f.fainelli, devel,
	linux-kernel, netdev

Hi,

this is the forth version of the slicoss gigabit ethernet driver (which is a
rework of the driver from Alacritech which can currently be found under
drivers/staging/slicoss). The driver is supposed to support Mojave, Oasis and
Kalahari cards, for both copper and fiber.

If this code is accepted the staging version can be removed.

The driver has been tested on a SEN2104ET adapter (4 Port PCIe copper).

v4:
- fix wrong driver name in Kconfig file (reported by Rami Rosen)
- remove unused variable from driver struct (reported by Rami Rosen)
- return "err" instead of 0 in slic_load_rcvseq_firmware() (reported by Rami Rosen)
- Fix typos in constants, comments and error message (reported by Markus Böhme)
- fix various warnings concerning signedness (reported by Markus Böhme)
- improve line formatting (reported by Markus Böhme)
- add comment describing the need for SLIC_MAX_TX_COMPLETIONS (suggested by Florian Fainelli)
- do not zero out complete rx descriptor (suggested by Florian Fainelli)
- add missing write barrier (reported by Florian Fainelli)
- remove unneeded assignment of net_device to skb (reported by Florian Fainelli)
- use napi_complete_done() instead of napi_complete (suggested by Florian Fainelli)
- use napi_schedule_irqoff() instead of napi_schedule (suggested by Florian Fainelli)
- do not map error returned by slic_init() to -ENOMEM
- do proper dma syncs before and after rx descriptor status is set to 0
- if after dma sync for CPU rx descriptor is not used return it to HW by means of dma sync for device

v3:
- dont add defines to pci_ids.h but instead put it into the drivers header file
(requested by Greg Kroah-Hartman)

v2:
- remove unusual padding in statistic strings (suggested by Andrew Lunn)
- for mdio register and bit names use defines from mii.h instead of own ones
  (suggested by Andrew Lunn)
- remove unused defines
- ensure PCI flush at two more places
- use mmiowb before lock to prevent mmio writes leaking out of lock
- fix some typos in comments
- add copyright and GPL header

Regards,
Lino 

^ permalink raw reply

* Kernel panic in netfilter 4.8.10 probably on conntrack -L
From: Denys Fedoryshchenko @ 2016-12-05 22:05 UTC (permalink / raw)
  To: Linux Kernel Network Developers, netfilter-devel, coreteam

Hi!

I have quite loaded NAT server (approx 17Gbps of traffic) where periodic 
"conntrack -L" might trigger once per day kernel panic.
I am not definitely sure it is triggered exactly at running tool, or 
just by enabling events.
Here is panic message:

<udp> [221287.380762] general protection fault: 0000 [#1] SMP
<udp> [221287.381029] Modules linked in:
<udp> xt_rateest
<udp> xt_RATEEST
<udp> nf_conntrack_netlink
<udp> netconsole
<udp> configfs
<udp> tun
<udp> nf_nat_pptp
<udp> nf_nat_proto_gre
<udp> xt_TCPMSS
<udp> xt_connmark
<udp> ipt_MASQUERADE
<udp> nf_nat_masquerade_ipv4
<udp> xt_nat
<udp> nf_conntrack_pptp
<udp> nf_conntrack_proto_gre
<udp> xt_CT
<udp> xt_set
<udp> xt_hl
<udp> xt_tcpudp
<udp> ip_set_hash_net
<udp> ip_set
<udp> nfnetlink
<udp> iptable_raw
<udp> iptable_mangle
<udp> iptable_nat
<udp> nf_conntrack_ipv4
<udp> nf_defrag_ipv4
<udp> nf_nat_ipv4
<udp> nf_nat
<udp> nf_conntrack
<udp> iptable_filter
<udp> ip_tables
<udp> x_tables
<udp> 8021q
<udp> garp
<udp> mrp
<udp> stp
<udp> llc
<udp> bonding
<udp> ixgbe
<udp> dca
<udp>
<udp> [221287.384913] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 
4.8.10-build-0121 #10
<udp> [221287.385184] Hardware name: Intel Corporation 
S2600WTT/S2600WTT, BIOS SE5C610.86B.01.01.1008.031920151331 03/19/2015
<udp> [221287.385634] task: ffffffff8200b4c0 task.stack: 
ffffffff82000000
<udp> [221287.385900] RIP: 0010:[<ffffffffa009fb95>]
<udp> [<ffffffffa009fb95>] nf_conntrack_eventmask_report+0xba/0x123 
[nf_conntrack]
<udp> [221287.386428] RSP: 0018:ffff882fbf603df8  EFLAGS: 00010202
<udp> [221287.386693] RAX: 0000000000000000 RBX: ffff882f96a51da8 RCX: 
0000000000000000
<udp> [221287.387134] RDX: 0000000000000000 RSI: ffff882fbf603e00 RDI: 
0000000000000004
<udp> [221287.387575] RBP: ffff882fbf603e38 R08: ff81822024ffffff R09: 
0000000000000004
<udp> [221287.388011] R10: ffff882fbf603de0 R11: ffffffff820050c0 R12: 
ffff882f810bf0c0
<udp> [221287.388445] R13: 0000000000000000 R14: 0000000000000000 R15: 
0000000000000004
<udp> [221287.388877] FS:  0000000000000000(0000) 
GS:ffff882fbf600000(0000) knlGS:0000000000000000
<udp> [221287.389311] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
<udp> [221287.389567] CR2: 00007faff0bd8978 CR3: 0000000002006000 CR4: 
00000000001406f0
<udp> [221287.389998] Stack:
<udp> [221287.390238]  000000049f292300
<udp> ffff882f810bf0c0
<udp> 0000000000000000
<udp> ffff882f810bf0c0
<udp>
<udp> [221287.390913]  ffff882f96a51d80
<udp> 0000000000000000
<udp> 0000000000000000
<udp> ffffffff820050c8
<udp>
<udp> [221287.391587]  ffff882fbf603e68
<udp> ffffffffa0098bd3
<udp> 0000000080000100
<udp> ffffffffa0098c85
<udp>
<udp> [221287.392262] Call Trace:
<udp> [221287.392508]  <IRQ>
<udp>
<udp> [221287.392579]  [<ffffffffa0098bd3>] nf_ct_delete+0x7a/0x12c 
[nf_conntrack]
<udp> [221287.393082]  [<ffffffffa0098c85>] ? nf_ct_delete+0x12c/0x12c 
[nf_conntrack]
<udp> [221287.393351]  [<ffffffffa0098c92>] death_by_timeout+0xd/0xf 
[nf_conntrack]
<udp> [221287.393617]  [<ffffffff811078ad>] 
call_timer_fn.isra.5+0x17/0x6b
<udp> [221287.393881]  [<ffffffff81107970>] expire_timers+0x6f/0x7e
<udp> [221287.394134]  [<ffffffff81107a68>] run_timer_softirq+0x69/0x8b
<udp> [221287.394390]  [<ffffffff810cefbc>] __do_softirq+0xbd/0x1aa
<udp> [221287.394643]  [<ffffffff810cf1f0>] irq_exit+0x37/0x7c
<udp> [221287.394898]  [<ffffffff8102a703>] 
smp_trace_call_function_single_interrupt+0x2e/0x30
<udp> [221287.395341]  [<ffffffff8102a70e>] 
smp_call_function_single_interrupt+0x9/0xb
<udp> [221287.395600]  [<ffffffff818dbcbc>] 
call_function_single_interrupt+0x7c/0x90
<udp> [221287.395857]  <EOI>
<udp>
<udp> [221287.395926]  [<ffffffff8101b8d7>] ? mwait_idle+0x64/0x7a
<udp> [221287.396413]  [<ffffffff8101bcb2>] arch_cpu_idle+0xa/0xc
<udp> [221287.396665]  [<ffffffff810f670a>] default_idle_call+0x27/0x29
<udp> [221287.396919]  [<ffffffff810f6829>] 
cpu_startup_entry+0x11d/0x1c7
<udp> [221287.397175]  [<ffffffff818d6c3b>] rest_init+0x72/0x74
<udp> [221287.397428]  [<ffffffff820d0e5c>] start_kernel+0x3ba/0x3c7
<udp> [221287.397681]  [<ffffffff820d028f>] 
x86_64_start_reservations+0x2a/0x2c
<udp> [221287.397937]  [<ffffffff820d03bb>] 
x86_64_start_kernel+0x12a/0x135
<udp> [221287.402124] Code:
<udp> f2
<udp> 89
<udp> 75
<udp> d0
<udp> 75
<udp> 04
<udp> 4c
<udp> 8b
<udp> 73
<udp> 08
<udp> 0f
<udp> b7
<udp> 73
<udp> 10
<udp> 41
<udp> 89
<udp> ff
<udp> 4d
<udp> 89
<udp> f1
<udp> 4d
<udp> 09
<udp> f9
<udp> 31
<udp> c0
<udp> 49
<udp> 85
<udp> f1
<udp> 74
<udp> 67
<udp> 41
<udp> 89
<udp> d5
<udp> 89
<udp> 7d
<udp> c4
<udp> 48
<udp> 8d
<udp> 75
<udp> c8
<udp> 44
<udp> 09
<udp> f7
<udp>
<udp> ff
<udp> 10
<udp> 89
<udp> c2
<udp> c1
<udp> ea
<udp> 1f
<udp> 75
<udp> 05
<udp> 4d
<udp> 85
<udp> f6
<udp> 74
<udp> 4b
<udp> 49
<udp> 83
<udp> c4
<udp> 04
<udp> 89
<udp> 45
<udp>
<udp> [221287.406724] RIP
<udp> [<ffffffffa009fb95>] nf_conntrack_eventmask_report+0xba/0x123 
[nf_conntrack]
<udp> [221287.407234]  RSP <ffff882fbf603df8>
<udp> [221287.407489] ---[ end trace 4b077b9412fc7065 ]---
<udp> [221287.407746] Kernel panic - not syncing: Fatal exception in 
interrupt
<udp> [221287.408013] Kernel Offset: disabled
<udp> [221287.408270] Rebooting in 5 seconds..
Dec  5 23:17:58 10.0.253.34
Dec  5 23:17:58 10.0.253.34 [221292.408645] ACPI MEMORY or I/O 
RESET_REG.

^ permalink raw reply

* Re: stmmac ethernet in kernel 4.9-rc6: coalescing related pauses.
From: Pavel Machek @ 2016-12-05 22:02 UTC (permalink / raw)
  To: Lino Sanfilippo
  Cc: Giuseppe CAVALLARO, alexandre.torgue, David Miller, netdev,
	linux-kernel
In-Reply-To: <trinity-efd00e93-2b37-4543-a199-e2cbed9fe455-1480937999976@3capp-gmx-bs18>

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

Hi!

> > 
> > Actually, I was wrong. irqlock protection is needed, since
> > stmmac_tx_clean() is called from timer, and that's interrupt context,
> > as you can confirm using BUG_ON(in_interrupt());
> > 
> 
> in_interrupt() can mean both softirq and hardirq context. In this case it
> means softirq. So I guess you were right before, and no irq locking is needed.

Are you absolutely sure? Because my testing seems to indicate
otherwise (but I may have made a mistake).

According to

https://www.kernel.org/pub/linux/kernel/people/rusty/kernel-locking/c214.html

we need spin_lock_bh at minimum, as we are locking user context
against timer.

Best regards,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

^ permalink raw reply

* Re: [Intel-wired-lan] [RFC PATCH] i40e: enable PCIe relax ordering for SPARC
From: Alexander Duyck @ 2016-12-05 21:54 UTC (permalink / raw)
  To: Tushar Dave; +Cc: Jeff Kirsher, intel-wired-lan, Netdev
In-Reply-To: <1480957627-16825-1-git-send-email-tushar.n.dave@oracle.com>

On Mon, Dec 5, 2016 at 9:07 AM, Tushar Dave <tushar.n.dave@oracle.com> wrote:
> Unlike previous generation NIC (e.g. ixgbe) i40e doesn't seem to have
> standard CSR where PCIe relaxed ordering can be set. Without PCIe relax
> ordering enabled, i40e performance is significantly low on SPARC.
>
> This patch sets PCIe relax ordering for SPARC arch by setting dma attr
> DMA_ATTR_WEAK_ORDERING for every tx and rx DMA map/unmap.
> This has shown 10x increase in performance numbers.
>
> e.g.
> iperf TCP test with 10 threads on SPARC S7
>
> Test 1: Without this patch
>
> [root@brm-snt1-03 net]# iperf -s
> ------------------------------------------------------------
> Server listening on TCP port 5001
> TCP window size: 85.3 KByte (default)
> ------------------------------------------------------------
> [  4] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40926
> [  5] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40934
> [  6] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40930
> [  7] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40928
> [  8] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40922
> [  9] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40932
> [ 10] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40920
> [ 11] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40924
> [ 14] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40982
> [ 12] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 40980
> [ ID] Interval       Transfer     Bandwidth
> [  4]  0.0-20.0 sec   566 MBytes   237 Mbits/sec
> [  5]  0.0-20.0 sec   532 MBytes   223 Mbits/sec
> [  6]  0.0-20.0 sec   537 MBytes   225 Mbits/sec
> [  8]  0.0-20.0 sec   546 MBytes   229 Mbits/sec
> [ 11]  0.0-20.0 sec   592 MBytes   248 Mbits/sec
> [  7]  0.0-20.0 sec   539 MBytes   226 Mbits/sec
> [  9]  0.0-20.0 sec   572 MBytes   240 Mbits/sec
> [ 10]  0.0-20.0 sec   604 MBytes   253 Mbits/sec
> [ 14]  0.0-20.0 sec   567 MBytes   238 Mbits/sec
> [ 12]  0.0-20.0 sec   511 MBytes   214 Mbits/sec
> [SUM]  0.0-20.0 sec  5.44 GBytes  2.33 Gbits/sec
>
> Test 2: with this patch:
>
> [root@brm-snt1-03 net]# iperf -s
> ------------------------------------------------------------
> Server listening on TCP port 5001
> TCP window size: 85.3 KByte (default)
> ------------------------------------------------------------
> TCP: request_sock_TCP: Possible SYN flooding on port 5001. Sending
> cookies.  Check SNMP counters.
> [  4] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46876
> [  5] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46874
> [  6] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46872
> [  7] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46880
> [  8] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46878
> [  9] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46884
> [ 10] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46886
> [ 11] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46890
> [ 12] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46888
> [ 13] local 16.0.0.7 port 5001 connected with 16.0.0.1 port 46882
> [ ID] Interval       Transfer     Bandwidth
> [  4]  0.0-20.0 sec  7.45 GBytes  3.19 Gbits/sec
> [  5]  0.0-20.0 sec  7.48 GBytes  3.21 Gbits/sec
> [  7]  0.0-20.0 sec  7.34 GBytes  3.15 Gbits/sec
> [  8]  0.0-20.0 sec  7.42 GBytes  3.18 Gbits/sec
> [  9]  0.0-20.0 sec  7.24 GBytes  3.11 Gbits/sec
> [ 10]  0.0-20.0 sec  7.40 GBytes  3.17 Gbits/sec
> [ 12]  0.0-20.0 sec  7.49 GBytes  3.21 Gbits/sec
> [  6]  0.0-20.0 sec  7.30 GBytes  3.13 Gbits/sec
> [ 11]  0.0-20.0 sec  7.44 GBytes  3.19 Gbits/sec
> [ 13]  0.0-20.0 sec  7.22 GBytes  3.10 Gbits/sec
> [SUM]  0.0-20.0 sec  73.8 GBytes  31.6 Gbits/sec
>
> NOTE: In my testing, this patch does _not_ show any harm to i40e
> performance numbers on x86.
>
> Signed-off-by: Tushar Dave <tushar.n.dave@oracle.com>

You went through and replaced all of the dma_unmap/map_page calls with
dma_map/unmap_single_attrs  I would prefer you didn't do that.  I have
patches to add the ability to map and unmap pages with attributes that
should be available for 4.10-rc1 so if you could wait on this patch
until then it would be preferred.

> ---
>  drivers/net/ethernet/intel/i40e/i40e_txrx.c | 69 ++++++++++++++++++++---------
>  drivers/net/ethernet/intel/i40e/i40e_txrx.h |  1 +
>  2 files changed, 49 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> index 6287bf6..800dca7 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> @@ -551,15 +551,17 @@ static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring,
>                 else
>                         dev_kfree_skb_any(tx_buffer->skb);
>                 if (dma_unmap_len(tx_buffer, len))
> -                       dma_unmap_single(ring->dev,
> -                                        dma_unmap_addr(tx_buffer, dma),
> -                                        dma_unmap_len(tx_buffer, len),
> -                                        DMA_TO_DEVICE);
> +                       dma_unmap_single_attrs(ring->dev,
> +                                              dma_unmap_addr(tx_buffer, dma),
> +                                              dma_unmap_len(tx_buffer, len),
> +                                              DMA_TO_DEVICE,
> +                                              ring->dma_attrs);
>         } else if (dma_unmap_len(tx_buffer, len)) {
> -               dma_unmap_page(ring->dev,
> -                              dma_unmap_addr(tx_buffer, dma),
> -                              dma_unmap_len(tx_buffer, len),
> -                              DMA_TO_DEVICE);
> +               dma_unmap_single_attrs(ring->dev,
> +                                      dma_unmap_addr(tx_buffer, dma),
> +                                      dma_unmap_len(tx_buffer, len),
> +                                      DMA_TO_DEVICE,
> +                                      ring->dma_attrs);
>         }
>
>         tx_buffer->next_to_watch = NULL;
> @@ -662,6 +664,8 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi,
>         struct i40e_tx_buffer *tx_buf;
>         struct i40e_tx_desc *tx_head;
>         struct i40e_tx_desc *tx_desc;
> +       dma_addr_t addr;
> +       size_t size;
>         unsigned int total_bytes = 0, total_packets = 0;
>         unsigned int budget = vsi->work_limit;
>
> @@ -696,10 +700,11 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi,
>                 napi_consume_skb(tx_buf->skb, napi_budget);
>
>                 /* unmap skb header data */
> -               dma_unmap_single(tx_ring->dev,
> -                                dma_unmap_addr(tx_buf, dma),
> -                                dma_unmap_len(tx_buf, len),
> -                                DMA_TO_DEVICE);
> +               dma_unmap_single_attrs(tx_ring->dev,
> +                                      dma_unmap_addr(tx_buf, dma),
> +                                      dma_unmap_len(tx_buf, len),
> +                                      DMA_TO_DEVICE,
> +                                      tx_ring->dma_attrs);
>
>                 /* clear tx_buffer data */
>                 tx_buf->skb = NULL;
> @@ -717,12 +722,15 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi,
>                                 tx_desc = I40E_TX_DESC(tx_ring, 0);
>                         }
>
> +                       addr = dma_unmap_addr(tx_buf, dma);
> +                       size = dma_unmap_len(tx_buf, len);

On some architectures this change could lead to issues since
dma_unmap_len could be 0 meaning that addr would never be used.

>                         /* unmap any remaining paged data */
>                         if (dma_unmap_len(tx_buf, len)) {
> -                               dma_unmap_page(tx_ring->dev,
> -                                              dma_unmap_addr(tx_buf, dma),
> -                                              dma_unmap_len(tx_buf, len),
> -                                              DMA_TO_DEVICE);
> +                               dma_unmap_single_attrs(tx_ring->dev,
> +                                                      addr,
> +                                                      size,
> +                                                      DMA_TO_DEVICE,
> +                                                      tx_ring->dma_attrs);
>                                 dma_unmap_len_set(tx_buf, len, 0);
>                         }
>                 }
> @@ -1010,6 +1018,11 @@ int i40e_setup_tx_descriptors(struct i40e_ring *tx_ring)
>          */
>         tx_ring->size += sizeof(u32);
>         tx_ring->size = ALIGN(tx_ring->size, 4096);
> +#ifdef CONFIG_SPARC
> +       tx_ring->dma_attrs = DMA_ATTR_WEAK_ORDERING;
> +#else
> +       tx_ring->dma_attrs = 0;
> +#endif
>         tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size,
>                                            &tx_ring->dma, GFP_KERNEL);
>         if (!tx_ring->desc) {

Also not a fan of adding yet ring attribute.  Is there any reason why
you couldn't simply add a set of inline functions at the start of
i40e_txrx.c that could replace the DMA map/unmap operations in this
code but pass either 0 or DMA_ATTR_WEAK_ORDERING as needed for the
drivers?  Then the x86 code doesn't have to change while the SPARC
code will be able to be passed the attribute.

> @@ -1053,7 +1066,11 @@ void i40e_clean_rx_ring(struct i40e_ring *rx_ring)
>                 if (!rx_bi->page)
>                         continue;
>
> -               dma_unmap_page(dev, rx_bi->dma, PAGE_SIZE, DMA_FROM_DEVICE);
> +               dma_unmap_single_attrs(dev,
> +                                      rx_bi->dma,
> +                                      PAGE_SIZE,
> +                                      DMA_FROM_DEVICE,
> +                                      rx_ring->dma_attrs);
>                 __free_pages(rx_bi->page, 0);
>
>                 rx_bi->page = NULL;
> @@ -1113,6 +1130,11 @@ int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring)
>         /* Round up to nearest 4K */
>         rx_ring->size = rx_ring->count * sizeof(union i40e_32byte_rx_desc);
>         rx_ring->size = ALIGN(rx_ring->size, 4096);
> +#ifdef CONFIG_SPARC
> +       rx_ring->dma_attrs = DMA_ATTR_WEAK_ORDERING;
> +#else
> +       rx_ring->dma_attrs = 0;
> +#endif
>         rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size,
>                                            &rx_ring->dma, GFP_KERNEL);
>
> @@ -1182,7 +1204,8 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring,
>         }
>
>         /* map page for use */
> -       dma = dma_map_page(rx_ring->dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE);
> +       dma = dma_map_single_attrs(rx_ring->dev, page_address(page), PAGE_SIZE,
> +                                  DMA_FROM_DEVICE, rx_ring->dma_attrs);
>
>         /* if mapping failed free memory back to system since
>          * there isn't much point in holding memory we can't use
> @@ -1695,8 +1718,11 @@ struct sk_buff *i40e_fetch_rx_buffer(struct i40e_ring *rx_ring,
>                 rx_ring->rx_stats.page_reuse_count++;
>         } else {
>                 /* we are not reusing the buffer so unmap it */
> -               dma_unmap_page(rx_ring->dev, rx_buffer->dma, PAGE_SIZE,
> -                              DMA_FROM_DEVICE);
> +               dma_unmap_single_attrs(rx_ring->dev,
> +                                      rx_buffer->dma,
> +                                      PAGE_SIZE,
> +                                      DMA_FROM_DEVICE,
> +                                      rx_ring->dma_attrs);
>         }
>
>         /* clear contents of buffer_info */
> @@ -2737,7 +2763,8 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
>         first->skb = skb;
>         first->tx_flags = tx_flags;
>
> -       dma = dma_map_single(tx_ring->dev, skb->data, size, DMA_TO_DEVICE);
> +       dma = dma_map_single_attrs(tx_ring->dev, skb->data, size,
> +                                  DMA_TO_DEVICE, tx_ring->dma_attrs);
>
>         tx_desc = I40E_TX_DESC(tx_ring, i);
>         tx_bi = first;
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
> index 5088405..9a86212 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
> @@ -327,6 +327,7 @@ struct i40e_ring {
>
>         unsigned int size;              /* length of descriptor ring in bytes */
>         dma_addr_t dma;                 /* physical address of ring */
> +       unsigned long dma_attrs;        /* DMA attributes */
>
>         struct i40e_vsi *vsi;           /* Backreference to associated VSI */
>         struct i40e_q_vector *q_vector; /* Backreference to associated vector */
> --
> 1.9.1
>
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan@lists.osuosl.org
> http://lists.osuosl.org/mailman/listinfo/intel-wired-lan

^ permalink raw reply

* Re: [PATCH nf-next] netfilter: xt_bpf: support ebpf
From: Florian Westphal @ 2016-12-05 21:30 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: Willem de Bruijn, netfilter-devel, pablo, netdev, daniel,
	Willem de Bruijn
In-Reply-To: <1480972006.18162.559.camel@edumazet-glaptop3.roam.corp.google.com>

Eric Dumazet <eric.dumazet@gmail.com> wrote:
> On Mon, 2016-12-05 at 15:28 -0500, Willem de Bruijn wrote:
> > From: Willem de Bruijn <willemb@google.com>
> > 
> > Add support for attaching an eBPF object by file descriptor.
> > 
> > The iptables binary can be called with a path to an elf object or a
> > pinned bpf object. Also pass the mode and path to the kernel to be
> > able to return it later for iptables dump and save.
> > 
> > Signed-off-by: Willem de Bruijn <willemb@google.com>
> > ---
> 
> Assuming there is no simple way to get variable matchsize in iptables,
> this looks good to me, thanks.

It should be possible by setting kernel .matchsize to ~0 which
suppresses strict size enforcement.

Its currently only used by ebt_among, but this should work for any xtables
module.

^ permalink raw reply

* Re: [PATCH v2 net-next v2 3/4] net: dsa: mv88e6xxx: add a soft reset operation
From: Stefan Eichenberger @ 2016-12-05 21:24 UTC (permalink / raw)
  To: Vivien Didelot
  Cc: netdev, linux-kernel, kernel, David S. Miller, Florian Fainelli,
	Andrew Lunn, Richard Cochran
In-Reply-To: <20161205162703.22567-4-vivien.didelot@savoirfairelinux.com>

Hi Vivien

On Mon, Dec 05, 2016 at 11:27:02AM -0500, Vivien Didelot wrote:
>  static const struct mv88e6xxx_ops mv88e6097_ops = {
> @@ -3285,6 +3266,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = {
>  	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
>  	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
>  	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
> +	.reset = mv88e6185_g1_reset,
>  };

Because it is not necessary to disable/enable the PPU and bit 14 is
marked as reserved in the datasheet, I think the following should be
used instead:
.reset = mv88e6352_g1_reset

Regards,
Stefan

^ permalink raw reply

* Re: [PATCH nf-next] netfilter: xt_bpf: support ebpf
From: Eric Dumazet @ 2016-12-05 21:06 UTC (permalink / raw)
  To: Willem de Bruijn; +Cc: netfilter-devel, pablo, netdev, daniel, Willem de Bruijn
In-Reply-To: <1480969684-74414-1-git-send-email-willemdebruijn.kernel@gmail.com>

On Mon, 2016-12-05 at 15:28 -0500, Willem de Bruijn wrote:
> From: Willem de Bruijn <willemb@google.com>
> 
> Add support for attaching an eBPF object by file descriptor.
> 
> The iptables binary can be called with a path to an elf object or a
> pinned bpf object. Also pass the mode and path to the kernel to be
> able to return it later for iptables dump and save.
> 
> Signed-off-by: Willem de Bruijn <willemb@google.com>
> ---

Assuming there is no simple way to get variable matchsize in iptables,
this looks good to me, thanks.

Reviewed-by: Eric Dumazet <edumazet@google.com>





^ permalink raw reply

* Re: [PATCH v2 net-next v2 4/4] net: dsa: mv88e6xxx: add PPU operations
From: Stefan Eichenberger @ 2016-12-05 21:04 UTC (permalink / raw)
  To: Vivien Didelot
  Cc: netdev, linux-kernel, kernel, David S. Miller, Florian Fainelli,
	Andrew Lunn, Richard Cochran
In-Reply-To: <20161205162703.22567-5-vivien.didelot@savoirfairelinux.com>

Hi Vivien,

On Mon, Dec 05, 2016 at 11:27:03AM -0500, Vivien Didelot wrote:
> @@ -3266,6 +3220,8 @@ static const struct mv88e6xxx_ops mv88e6097_ops = {
>  	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
>  	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
>  	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
> +	.ppu_enable = mv88e6185_g1_ppu_enable,
> +	.ppu_disable = mv88e6185_g1_ppu_disable,
>  	.reset = mv88e6185_g1_reset,
>  };

The mv88e6097 should use the indirect access to the phys, bit 14 in g1
control is marked as reserved. They write in the datasheet that
disabling the PPU is still supported but indirect access via g2 should
be used because disabling the PPU  is no longer recommended.

Best regards,
Stefan

^ permalink raw reply

* Re: [PATCH 0/5] cpsw: add per channel shaper configuration
From: Ivan Khoronzhuk @ 2016-12-05 20:59 UTC (permalink / raw)
  To: Grygorii Strashko; +Cc: netdev, linux-kernel, mugunthanvnm, linux-omap
In-Reply-To: <4640ce88-75b0-8508-05f0-849e8fd4927f@ti.com>

On Mon, Dec 05, 2016 at 02:33:40PM -0600, Grygorii Strashko wrote:
> Hi Ivan,
> 
> On 11/29/2016 09:00 AM, Ivan Khoronzhuk wrote:
> > This series is intended to allow user to set rate for per channel
> > shapers at cpdma level. This patchset doesn't have impact on performance.
> > The rate can be set with:
> > 
> > echo 100 > /sys/class/net/ethX/queues/tx-0/tx_maxrate
> > 
> > Tested on am572xx
> > Based on net-next/master
> > 
> > Ivan Khoronzhuk (5):
> >   net: ethernet: ti: davinci_cpdma: add weight function for channels
> >   net: ethernet: ti: davinci_cpdma: add set rate for a channel
> >   net: ethernet: ti: cpsw: add .ndo to set per-queue rate
> >   net: ethernet: ti: cpsw: optimize end of poll cycle
> >   net: ethernet: ti: cpsw: split tx budget according between channels
> > 
> >  drivers/net/ethernet/ti/cpsw.c          | 264 +++++++++++++++----
> >  drivers/net/ethernet/ti/davinci_cpdma.c | 453 ++++++++++++++++++++++++++++----
> >  drivers/net/ethernet/ti/davinci_cpdma.h |   6 +
> >  3 files changed, 624 insertions(+), 99 deletions(-)
> > 
> 
> 
> I've just tried net-next on BBB and got below back-trace:
I'd not tested on BBB, and expected some review before apply.
Seems that's because of phy speed, let me finish a fix patch.
Thanks.


> INIT: Entering runlevel: 5
> Configuring network interfaces... [   15.018356] net eth0: initializing cpsw version 1.12 (0)
> [   15.120153] SMSC LAN8710/LAN8720 4a101000.mdio:00: attached PHY driver [SMSC LAN8710/LAN8720] (mii_bus:phy_addr=4a101000.mdio:00, irq=-1)
> [   15.138578] Division by zero in kernel.
> [   15.142667] CPU: 0 PID: 755 Comm: ifconfig Not tainted 4.9.0-rc7-01617-g6ea3f00 #5
> [   15.150277] Hardware name: Generic AM33XX (Flattened Device Tree)
> [   15.156399] Backtrace: 
> [   15.158898] [<c010bbf8>] (dump_backtrace) from [<c010beb4>] (show_stack+0x18/0x1c)
> [   15.166508]  r7:00000000 r6:600f0013 r5:00000000 r4:c0d395d0
> [   15.172200] [<c010be9c>] (show_stack) from [<c03ce6cc>] (dump_stack+0x8c/0xa0)
> [   15.179460] [<c03ce640>] (dump_stack) from [<c010bd60>] (__div0+0x1c/0x20)
> [   15.186368]  r7:00000000 r6:ddf1c010 r5:00000001 r4:00000001
> [   15.192068] [<c010bd44>] (__div0) from [<c03cd17c>] (Ldiv0+0x8/0x14)
> [   15.198503] [<bf07ba38>] (cpsw_split_budget [ti_cpsw]) from [<bf07ee8c>] (cpsw_ndo_open+0x4b8/0x5e4 [ti_cpsw])
> [   15.208554]  r10:ddf1c010 r9:00000000 r8:00000000 r7:ddf1c010 r6:dcc88000 r5:dcc88500
> [   15.216418]  r4:ddf1c0b0
> [   15.218985] [<bf07e9d4>] (cpsw_ndo_open [ti_cpsw]) from [<c0731c68>] (__dev_open+0xb0/0x114)
> [   15.227466]  r10:00000000 r9:00000000 r8:00000000 r7:dcc88030 r6:bf080364 r5:00000000
> [   15.235330]  r4:dcc88000
> [   15.237880] [<c0731bb8>] (__dev_open) from [<c0731f24>] (__dev_change_flags+0x9c/0x14c)
> [   15.245923]  r7:00001002 r6:00000001 r5:00001043 r4:dcc88000
> [   15.251613] [<c0731e88>] (__dev_change_flags) from [<c0731ff4>] (dev_change_flags+0x20/0x50)
> [   15.260093]  r9:00000000 r8:00000000 r7:dcbabf0c r6:00001002 r5:dcc8813c r4:dcc88000
> [   15.267886] [<c0731fd4>] (dev_change_flags) from [<c07a3168>] (devinet_ioctl+0x6d4/0x794)
> [   15.276105]  r9:00000000 r8:bee8cc64 r7:dcbabf0c r6:00000000 r5:dc921e80 r4:00000000
> [   15.283891] [<c07a2a94>] (devinet_ioctl) from [<c07a54bc>] (inet_ioctl+0x19c/0x1c8)
> [   15.291587]  r10:00000000 r9:dc920000 r8:bee8cc64 r7:c0d64bc0 r6:bee8cc64 r5:dd2728e0
> [   15.299450]  r4:00008914
> [   15.302002] [<c07a5320>] (inet_ioctl) from [<c07112dc>] (sock_ioctl+0x14c/0x300)
> [   15.309443] [<c0711190>] (sock_ioctl) from [<c0241868>] (do_vfs_ioctl+0xa8/0x98c)
> [   15.316962]  r7:00000003 r6:ddf0a780 r5:dd2728e0 r4:bee8cc64
> [   15.322653] [<c02417c0>] (do_vfs_ioctl) from [<c0242188>] (SyS_ioctl+0x3c/0x64)
> [   15.330000]  r10:00000000 r9:dc920000 r8:bee8cc64 r7:00008914 r6:ddf0a780 r5:00000003
> [   15.337864]  r4:ddf0a780
> [   15.340416] [<c024214c>] (SyS_ioctl) from [<c0107d60>] (ret_fast_syscall+0x0/0x3c)
> [   15.348024]  r9:dc920000 r8:c0107f24 r7:00000036 r6:bee8cf4d r5:bee8ce4c r4:000949f0
> [   15.361174] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready
> udhcpc (v1.23.1) started
> 
> 
> -- 
> regards,
> -grygorii

^ permalink raw reply

* [PATCH net v4] tcp: warn on bogus MSS and try to amend it
From: Marcelo Ricardo Leitner @ 2016-12-05 20:37 UTC (permalink / raw)
  To: netdev
  Cc: Jon Maxwell, Alex Sidorenko, Alexey Kuznetsov, James Morris,
	Hideaki YOSHIFUJI, Patrick McHardy, tlfalcon, Brian King,
	Eric Dumazet, davem, marcelo.leitner

There have been some reports lately about TCP connection stalls caused
by NIC drivers that aren't setting gso_size on aggregated packets on rx
path. This causes TCP to assume that the MSS is actually the size of the
aggregated packet, which is invalid.

Although the proper fix is to be done at each driver, it's often hard
and cumbersome for one to debug, come to such root cause and report/fix
it.

This patch amends this situation in two ways. First, it adds a warning
on when this situation occurs, so it gives a hint to those trying to
debug this. It also limit the maximum probed MSS to the adverised MSS,
as it should never be any higher than that.

The result is that the connection may not have the best performance ever
but it shouldn't stall, and the admin will have a hint on what to look
for.

Tested with virtio by forcing gso_size to 0.

v2: updated msg per David's suggestion
v3: use skb_iif to find the interface and also log its name, per Eric
    Dumazet's suggestion. As the skb may be backlogged and the interface
    gone by then, we need to check if the number still has a meaning.
v4: use helper tcp_gro_dev_warn() and avoid pr_warn_once inside __once, per
    David's suggestion

Cc: Jonathan Maxwell <jmaxwell37@gmail.com>
Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
---
Thanks

 net/ipv4/tcp_input.c | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index a27b9c0e27c08b4e4aeaff3d0bfdf3ae561ba4d8..c71d49ce0c9379cd68317bcc135b7a2761110887 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -128,6 +128,23 @@ int sysctl_tcp_invalid_ratelimit __read_mostly = HZ/2;
 #define REXMIT_LOST	1 /* retransmit packets marked lost */
 #define REXMIT_NEW	2 /* FRTO-style transmit of unsent/new packets */
 
+static void tcp_gro_dev_warn(struct sock *sk, const struct sk_buff *skb)
+{
+	static bool __once __read_mostly;
+
+	if (!__once) {
+		struct net_device *dev;
+
+		__once = true;
+
+		rcu_read_lock();
+		dev = dev_get_by_index_rcu(sock_net(sk), skb->skb_iif);
+		pr_warn("%s: Driver has suspect GRO implementation, TCP performance may be compromised.\n",
+			dev ? dev->name : "Unknown driver");
+		rcu_read_unlock();
+	}
+}
+
 /* Adapt the MSS value used to make delayed ack decision to the
  * real world.
  */
@@ -144,7 +161,10 @@ static void tcp_measure_rcv_mss(struct sock *sk, const struct sk_buff *skb)
 	 */
 	len = skb_shinfo(skb)->gso_size ? : skb->len;
 	if (len >= icsk->icsk_ack.rcv_mss) {
-		icsk->icsk_ack.rcv_mss = len;
+		icsk->icsk_ack.rcv_mss = min_t(unsigned int, len,
+					       tcp_sk(sk)->advmss);
+		if (unlikely(icsk->icsk_ack.rcv_mss != len))
+			tcp_gro_dev_warn(sk, skb);
 	} else {
 		/* Otherwise, we make more careful check taking into account,
 		 * that SACKs block is variable.
-- 
2.9.3

^ permalink raw reply related

* Re: [PATCH net] net: ep93xx_eth: Do not crash unloading module
From: David Miller @ 2016-12-05 20:36 UTC (permalink / raw)
  To: f.fainelli; +Cc: netdev, hsweeten
In-Reply-To: <20161205032205.14127-1-f.fainelli@gmail.com>

From: Florian Fainelli <f.fainelli@gmail.com>
Date: Sun,  4 Dec 2016 19:22:05 -0800

> When we unload the ep93xx_eth, whether we have opened the network
> interface or not, we will either hit a kernel paging request error, or a
> simple NULL pointer de-reference because:
> 
> - if ep93xx_open has been called, we have created a valid DMA mapping
>   for ep->descs, when we call ep93xx_stop, we also call
>   ep93xx_free_buffers, ep->descs now has a stale value
> 
> - if ep93xx_open has not been called, we have a NULL pointer for
>   ep->descs, so performing any operation against that address just won't
>   work
> 
> Fix this by adding a NULL pointer check for ep->descs which means that
> ep93xx_free_buffers() was able to successfully tear down the descriptors
> and free the DMA cookie as well.
> 
> Fixes: 1d22e05df818 ("[PATCH] Cirrus Logic ep93xx ethernet driver")
> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>

Applied, thanks Florian.

^ permalink raw reply

* Re: [PATCH] net: calxeda: xgmac: use new api ethtool_{get|set}_link_ksettings
From: David Miller @ 2016-12-05 20:34 UTC (permalink / raw)
  To: tremyfr; +Cc: jarod, netdev, linux-kernel
In-Reply-To: <1480891073-23166-1-git-send-email-tremyfr@gmail.com>

From: Philippe Reynes <tremyfr@gmail.com>
Date: Sun,  4 Dec 2016 23:37:53 +0100

> The ethtool api {get|set}_settings is deprecated.
> We move this driver to new api {get|set}_link_ksettings.
> 
> Signed-off-by: Philippe Reynes <tremyfr@gmail.com>

Applied.

^ permalink raw reply

* Re: [PATCH 0/5] cpsw: add per channel shaper configuration
From: Grygorii Strashko @ 2016-12-05 20:33 UTC (permalink / raw)
  To: Ivan Khoronzhuk, netdev, linux-kernel, mugunthanvnm; +Cc: linux-omap
In-Reply-To: <1480431651-6081-1-git-send-email-ivan.khoronzhuk@linaro.org>

Hi Ivan,

On 11/29/2016 09:00 AM, Ivan Khoronzhuk wrote:
> This series is intended to allow user to set rate for per channel
> shapers at cpdma level. This patchset doesn't have impact on performance.
> The rate can be set with:
> 
> echo 100 > /sys/class/net/ethX/queues/tx-0/tx_maxrate
> 
> Tested on am572xx
> Based on net-next/master
> 
> Ivan Khoronzhuk (5):
>   net: ethernet: ti: davinci_cpdma: add weight function for channels
>   net: ethernet: ti: davinci_cpdma: add set rate for a channel
>   net: ethernet: ti: cpsw: add .ndo to set per-queue rate
>   net: ethernet: ti: cpsw: optimize end of poll cycle
>   net: ethernet: ti: cpsw: split tx budget according between channels
> 
>  drivers/net/ethernet/ti/cpsw.c          | 264 +++++++++++++++----
>  drivers/net/ethernet/ti/davinci_cpdma.c | 453 ++++++++++++++++++++++++++++----
>  drivers/net/ethernet/ti/davinci_cpdma.h |   6 +
>  3 files changed, 624 insertions(+), 99 deletions(-)
> 


I've just tried net-next on BBB and got below back-trace:
INIT: Entering runlevel: 5
Configuring network interfaces... [   15.018356] net eth0: initializing cpsw version 1.12 (0)
[   15.120153] SMSC LAN8710/LAN8720 4a101000.mdio:00: attached PHY driver [SMSC LAN8710/LAN8720] (mii_bus:phy_addr=4a101000.mdio:00, irq=-1)
[   15.138578] Division by zero in kernel.
[   15.142667] CPU: 0 PID: 755 Comm: ifconfig Not tainted 4.9.0-rc7-01617-g6ea3f00 #5
[   15.150277] Hardware name: Generic AM33XX (Flattened Device Tree)
[   15.156399] Backtrace: 
[   15.158898] [<c010bbf8>] (dump_backtrace) from [<c010beb4>] (show_stack+0x18/0x1c)
[   15.166508]  r7:00000000 r6:600f0013 r5:00000000 r4:c0d395d0
[   15.172200] [<c010be9c>] (show_stack) from [<c03ce6cc>] (dump_stack+0x8c/0xa0)
[   15.179460] [<c03ce640>] (dump_stack) from [<c010bd60>] (__div0+0x1c/0x20)
[   15.186368]  r7:00000000 r6:ddf1c010 r5:00000001 r4:00000001
[   15.192068] [<c010bd44>] (__div0) from [<c03cd17c>] (Ldiv0+0x8/0x14)
[   15.198503] [<bf07ba38>] (cpsw_split_budget [ti_cpsw]) from [<bf07ee8c>] (cpsw_ndo_open+0x4b8/0x5e4 [ti_cpsw])
[   15.208554]  r10:ddf1c010 r9:00000000 r8:00000000 r7:ddf1c010 r6:dcc88000 r5:dcc88500
[   15.216418]  r4:ddf1c0b0
[   15.218985] [<bf07e9d4>] (cpsw_ndo_open [ti_cpsw]) from [<c0731c68>] (__dev_open+0xb0/0x114)
[   15.227466]  r10:00000000 r9:00000000 r8:00000000 r7:dcc88030 r6:bf080364 r5:00000000
[   15.235330]  r4:dcc88000
[   15.237880] [<c0731bb8>] (__dev_open) from [<c0731f24>] (__dev_change_flags+0x9c/0x14c)
[   15.245923]  r7:00001002 r6:00000001 r5:00001043 r4:dcc88000
[   15.251613] [<c0731e88>] (__dev_change_flags) from [<c0731ff4>] (dev_change_flags+0x20/0x50)
[   15.260093]  r9:00000000 r8:00000000 r7:dcbabf0c r6:00001002 r5:dcc8813c r4:dcc88000
[   15.267886] [<c0731fd4>] (dev_change_flags) from [<c07a3168>] (devinet_ioctl+0x6d4/0x794)
[   15.276105]  r9:00000000 r8:bee8cc64 r7:dcbabf0c r6:00000000 r5:dc921e80 r4:00000000
[   15.283891] [<c07a2a94>] (devinet_ioctl) from [<c07a54bc>] (inet_ioctl+0x19c/0x1c8)
[   15.291587]  r10:00000000 r9:dc920000 r8:bee8cc64 r7:c0d64bc0 r6:bee8cc64 r5:dd2728e0
[   15.299450]  r4:00008914
[   15.302002] [<c07a5320>] (inet_ioctl) from [<c07112dc>] (sock_ioctl+0x14c/0x300)
[   15.309443] [<c0711190>] (sock_ioctl) from [<c0241868>] (do_vfs_ioctl+0xa8/0x98c)
[   15.316962]  r7:00000003 r6:ddf0a780 r5:dd2728e0 r4:bee8cc64
[   15.322653] [<c02417c0>] (do_vfs_ioctl) from [<c0242188>] (SyS_ioctl+0x3c/0x64)
[   15.330000]  r10:00000000 r9:dc920000 r8:bee8cc64 r7:00008914 r6:ddf0a780 r5:00000003
[   15.337864]  r4:ddf0a780
[   15.340416] [<c024214c>] (SyS_ioctl) from [<c0107d60>] (ret_fast_syscall+0x0/0x3c)
[   15.348024]  r9:dc920000 r8:c0107f24 r7:00000036 r6:bee8cf4d r5:bee8ce4c r4:000949f0
[   15.361174] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready
udhcpc (v1.23.1) started


-- 
regards,
-grygorii

^ permalink raw reply

* Re: [PATCH net-next 0/3] Minor BPF cleanups and digest
From: David Miller @ 2016-12-05 20:33 UTC (permalink / raw)
  To: daniel; +Cc: alexei.starovoitov, netdev
In-Reply-To: <cover.1480889473.git.daniel@iogearbox.net>

From: Daniel Borkmann <daniel@iogearbox.net>
Date: Sun,  4 Dec 2016 23:19:38 +0100

> First two patches are minor cleanups, and the third one adds
> a prog digest. For details, please see individual patches.
> After this one, I have a set with tracepoint support that makes
> use of this facility as well.

Series applied, thanks.

^ permalink raw reply


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