Commit 169fbdc6 authored by David S. Miller's avatar David S. Miller

Merge branch 'dsa-new-binding'

Andrew Lunn says:

====================
New DSA bind, switches as devices

The interesting patches here are the last three. They implement a new
binding for DSA, which removes a few limitations of the current DSA
binding. In particular, it allows switches to be true Linux devices.
These devices can be on any type of bus, unlike the old DSA binding
which assumes MDIO. See the commit log for more details. The second to
last patch modifies an existing boards device tree to use the new
binding, giving a good example of how switches can be true MDIO
devices. The last patch documents the new binding.

Thanks go to Florian and Vivien for reviewing, testing and bug fixing
these patches.
Tested-by: default avatarVivien Didelot <vivien.didelot@savoirfairelinux.com>
Tested-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>

Since V1:

* Add lots of reviewed-by's
* Fix rtable comment
* dsa2: Clear cpu port mask in dsa_cpu_port_unapply()
* dsa2: Only set dsa_port_mask when port successfully configured
* dsa: clear {dsa|cpu}_port_mask on destroy

Since RFC:

* Split the mv88e6xxx MDIO refactor into a rename patch and a refactor
  patch.
* Extend commit message with comment about wrong of_node_put()
* Fix destroy of cpu and dsa ports.
* Rename _DSA_TAG_LAST to DSA_TAG_LAST and add a comment.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 76f21b99 8c5ad1d6
Marvell Distributed Switch Architecture Device Tree Bindings Distributed Switch Architecture Device Tree Bindings
------------------------------------------------------------ ----------------------------------------------------
Two bindings exist, one of which has been deprecated due to
limitations.
Current Binding
---------------
Switches are true Linux devices and can be probes by any means. Once
probed, they register to the DSA framework, passing a node
pointer. This node is expected to fulfil the following binding, and
may contain additional properties as required by the device it is
embedded within.
Required properties:
- ports : A container for child nodes representing switch ports.
Optional properties:
- dsa,member : A two element list indicates which DSA cluster, and position
within the cluster a switch takes. <0 0> is cluster 0,
switch 0. <0 1> is cluster 0, switch 1. <1 0> is cluster 1,
switch 0. A switch not part of any cluster (single device
hanging off a CPU port) must not specify this property
The ports container has the following properties
Required properties:
- #address-cells : Must be 1
- #size-cells : Must be 0
Each port children node must have the following mandatory properties:
- reg : Describes the port address in the switch
- label : Describes the label associated with this port, which
will become the netdev name. Special labels are
"cpu" to indicate a CPU port and "dsa" to
indicate an uplink/downlink port between switches in
the cluster.
A port labelled "dsa" has the following mandatory property:
- link : Should be a list of phandles to other switch's DSA
port. This port is used as the outgoing port
towards the phandle ports. The full routing
information must be given, not just the one hop
routes to neighbouring switches.
A port labelled "cpu" has the following mandatory property:
- ethernet : Should be a phandle to a valid Ethernet device node.
This host device is what the switch port is
connected to.
Port child nodes may also contain the following optional standardised
properties, described in binding documents:
- phy-handle : Phandle to a PHY on an MDIO bus. See
Documentation/devicetree/bindings/net/ethernet.txt
for details.
- phy-mode : See
Documentation/devicetree/bindings/net/ethernet.txt
for details.
- fixed-link : Fixed-link subnode describing a link to a non-MDIO
managed entity. See
Documentation/devicetree/bindings/net/fixed-link.txt
for details.
Example
The following example shows three switches on three MDIO busses,
linked into one DSA cluster.
&mdio1 {
#address-cells = <1>;
#size-cells = <0>;
switch0: switch0@0 {
compatible = "marvell,mv88e6085";
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
dsa,member = <0 0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
label = "lan0";
};
port@1 {
reg = <1>;
label = "lan1";
};
port@2 {
reg = <2>;
label = "lan2";
};
switch0port5: port@5 {
reg = <5>;
label = "dsa";
phy-mode = "rgmii-txid";
link = <&switch1port6
&switch2port9>;
fixed-link {
speed = <1000>;
full-duplex;
};
};
port@6 {
reg = <6>;
label = "cpu";
ethernet = <&fec1>;
fixed-link {
speed = <100>;
full-duplex;
};
};
};
};
};
&mdio2 {
#address-cells = <1>;
#size-cells = <0>;
switch1: switch1@0 {
compatible = "marvell,mv88e6085";
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
dsa,member = <0 1>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
label = "lan3";
phy-handle = <&switch1phy0>;
};
port@1 {
reg = <1>;
label = "lan4";
phy-handle = <&switch1phy1>;
};
port@2 {
reg = <2>;
label = "lan5";
phy-handle = <&switch1phy2>;
};
switch1port5: port@5 {
reg = <5>;
label = "dsa";
link = <&switch2port9>;
phy-mode = "rgmii-txid";
fixed-link {
speed = <1000>;
full-duplex;
};
};
switch1port6: port@6 {
reg = <6>;
label = "dsa";
phy-mode = "rgmii-txid";
link = <&switch0port5>;
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
mdio-bus {
#address-cells = <1>;
#size-cells = <0>;
switch1phy0: switch1phy0@0 {
reg = <0>;
};
switch1phy1: switch1phy0@1 {
reg = <1>;
};
switch1phy2: switch1phy0@2 {
reg = <2>;
};
};
};
};
&mdio4 {
#address-cells = <1>;
#size-cells = <0>;
switch2: switch2@0 {
compatible = "marvell,mv88e6085";
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
dsa,member = <0 2>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
label = "lan6";
};
port@1 {
reg = <1>;
label = "lan7";
};
port@2 {
reg = <2>;
label = "lan8";
};
port@3 {
reg = <3>;
label = "optical3";
fixed-link {
speed = <1000>;
full-duplex;
link-gpios = <&gpio6 2
GPIO_ACTIVE_HIGH>;
};
};
port@4 {
reg = <4>;
label = "optical4";
fixed-link {
speed = <1000>;
full-duplex;
link-gpios = <&gpio6 3
GPIO_ACTIVE_HIGH>;
};
};
switch2port9: port@9 {
reg = <9>;
label = "dsa";
phy-mode = "rgmii-txid";
link = <&switch1port5
&switch0port5>;
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};
};
Deprecated Binding
------------------
The deprecated binding makes use of a platform device to represent the
switches. The switches themselves are not Linux devices, and make use
of an MDIO bus for management.
Required properties: Required properties:
- compatible : Should be "marvell,dsa" - compatible : Should be "marvell,dsa"
......
...@@ -85,187 +85,199 @@ mdio_mux_1: mdio@1 { ...@@ -85,187 +85,199 @@ mdio_mux_1: mdio@1 {
reg = <1>; reg = <1>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
switch0: switch0@0 {
compatible = "marvell,mv88e6085";
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
dsa,member = <0 0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
label = "lan0";
};
port@1 {
reg = <1>;
label = "lan1";
};
port@2 {
reg = <2>;
label = "lan2";
};
switch0port5: port@5 {
reg = <5>;
label = "dsa";
phy-mode = "rgmii-txid";
link = <&switch1port6
&switch2port9>;
fixed-link {
speed = <1000>;
full-duplex;
};
};
port@6 {
reg = <6>;
label = "cpu";
ethernet = <&fec1>;
fixed-link {
speed = <100>;
full-duplex;
};
};
};
};
}; };
mdio_mux_2: mdio@2 { mdio_mux_2: mdio@2 {
reg = <2>; reg = <2>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
};
mdio_mux_4: mdio@4 {
reg = <4>;
#address-cells = <1>;
#size-cells = <0>;
};
mdio_mux_8: mdio@8 {
reg = <8>;
#address-cells = <1>;
#size-cells = <0>;
};
};
dsa {
compatible = "marvell,dsa";
#address-cells = <2>;
#size-cells = <0>;
dsa,ethernet = <&fec1>;
dsa,mii-bus = <&mdio_mux_1>;
/* 6352 - Primary - 7 ports */
switch0: switch@0-0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0x00 0>;
eeprom-length = <512>;
port@0 { switch1: switch1@0 {
compatible = "marvell,mv88e6085";
#address-cells = <1>;
#size-cells = <0>;
reg = <0>; reg = <0>;
label = "lan0"; dsa,member = <0 1>;
};
ports {
port@1 { #address-cells = <1>;
reg = <1>; #size-cells = <0>;
label = "lan1"; port@0 {
}; reg = <0>;
label = "lan3";
port@2 { phy-handle = <&switch1phy0>;
reg = <2>; };
label = "lan2";
}; port@1 {
reg = <1>;
switch0port5: port@5 { label = "lan4";
reg = <5>; phy-handle = <&switch1phy1>;
label = "dsa"; };
phy-mode = "rgmii-txid";
link = <&switch1port6 port@2 {
&switch2port9>; reg = <2>;
label = "lan5";
fixed-link { phy-handle = <&switch1phy2>;
speed = <1000>; };
full-duplex;
switch1port5: port@5 {
reg = <5>;
label = "dsa";
link = <&switch2port9>;
phy-mode = "rgmii-txid";
fixed-link {
speed = <1000>;
full-duplex;
};
};
switch1port6: port@6 {
reg = <6>;
label = "dsa";
phy-mode = "rgmii-txid";
link = <&switch0port5>;
fixed-link {
speed = <1000>;
full-duplex;
};
};
}; };
}; mdio {
#address-cells = <1>;
port@6 { #size-cells = <0>;
reg = <6>; switch1phy0: switch1phy0@0 {
label = "cpu"; reg = <0>;
};
fixed-link { switch1phy1: switch1phy0@1 {
speed = <100>; reg = <1>;
full-duplex; };
switch1phy2: switch1phy0@2 {
reg = <2>;
};
}; };
}; };
}; };
/* 6352 - Secondary - 7 ports */ mdio_mux_4: mdio@4 {
switch1: switch@0-1 {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
reg = <0x00 1>; reg = <4>;
eeprom-length = <512>;
mii-bus = <&mdio_mux_2>;
port@0 { switch2: switch2@0 {
compatible = "marvell,mv88e6085";
#address-cells = <1>;
#size-cells = <0>;
reg = <0>; reg = <0>;
label = "lan3"; dsa,member = <0 2>;
};
ports {
port@1 { #address-cells = <1>;
reg = <1>; #size-cells = <0>;
label = "lan4"; port@0 {
}; reg = <0>;
label = "lan6";
port@2 { };
reg = <2>;
label = "lan5"; port@1 {
}; reg = <1>;
label = "lan7";
switch1port5: port@5 { };
reg = <5>;
label = "dsa"; port@2 {
link = <&switch2port9>; reg = <2>;
phy-mode = "rgmii-txid"; label = "lan8";
};
fixed-link {
speed = <1000>; port@3 {
full-duplex; reg = <3>;
}; label = "optical3";
}; fixed-link {
speed = <1000>;
switch1port6: port@6 { full-duplex;
reg = <6>; link-gpios = <&gpio6 2
label = "dsa"; GPIO_ACTIVE_HIGH>;
phy-mode = "rgmii-txid"; };
link = <&switch0port5>; };
fixed-link { port@4 {
speed = <1000>; reg = <4>;
full-duplex; label = "optical4";
fixed-link {
speed = <1000>;
full-duplex;
link-gpios = <&gpio6 3
GPIO_ACTIVE_HIGH>;
};
};
switch2port9: port@9 {
reg = <9>;
label = "dsa";
phy-mode = "rgmii-txid";
link = <&switch1port5
&switch0port5>;
fixed-link {
speed = <1000>;
full-duplex;
};
};
}; };
}; };
}; };
/* 6185 - 10 ports */ mdio_mux_8: mdio@8 {
switch2: switch@0-2 { reg = <8>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
reg = <0x00 2>;
mii-bus = <&mdio_mux_4>;
port@0 {
reg = <0>;
label = "lan6";
};
port@1 {
reg = <1>;
label = "lan7";
};
port@2 {
reg = <2>;
label = "lan8";
};
port@3 {
reg = <3>;
label = "optical3";
fixed-link {
speed = <1000>;
full-duplex;
link-gpios = <&gpio6 2
GPIO_ACTIVE_HIGH>;
};
};
port@4 {
reg = <4>;
label = "optical4";
fixed-link {
speed = <1000>;
full-duplex;
link-gpios = <&gpio6 3
GPIO_ACTIVE_HIGH>;
};
};
switch2port9: port@9 {
reg = <9>;
label = "dsa";
phy-mode = "rgmii-txid";
link = <&switch1port5
&switch0port5>;
fixed-link {
speed = <1000>;
full-duplex;
};
};
}; };
}; };
......
...@@ -804,7 +804,7 @@ static int bcm_sf2_sw_fdb_dump(struct dsa_switch *ds, int port, ...@@ -804,7 +804,7 @@ static int bcm_sf2_sw_fdb_dump(struct dsa_switch *ds, int port,
int (*cb)(struct switchdev_obj *obj)) int (*cb)(struct switchdev_obj *obj))
{ {
struct bcm_sf2_priv *priv = ds_to_priv(ds); struct bcm_sf2_priv *priv = ds_to_priv(ds);
struct net_device *dev = ds->ports[port]; struct net_device *dev = ds->ports[port].netdev;
struct bcm_sf2_arl_entry results[2]; struct bcm_sf2_arl_entry results[2];
unsigned int count = 0; unsigned int count = 0;
int ret; int ret;
...@@ -1248,7 +1248,7 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, ...@@ -1248,7 +1248,7 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
* state machine and make it go in PHY_FORCING state instead. * state machine and make it go in PHY_FORCING state instead.
*/ */
if (!status->link) if (!status->link)
netif_carrier_off(ds->ports[port]); netif_carrier_off(ds->ports[port].netdev);
status->duplex = 1; status->duplex = 1;
} else { } else {
status->link = 1; status->link = 1;
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/mdio.h> #include <linux/mdio.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_mdio.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/phy.h> #include <linux/phy.h>
...@@ -238,16 +239,16 @@ int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr) ...@@ -238,16 +239,16 @@ int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
return mv88e6xxx_set_addr_direct(ds, addr); return mv88e6xxx_set_addr_direct(ds, addr);
} }
static int _mv88e6xxx_phy_read(struct mv88e6xxx_priv_state *ps, int addr, static int mv88e6xxx_mdio_read_direct(struct mv88e6xxx_priv_state *ps,
int regnum) int addr, int regnum)
{ {
if (addr >= 0) if (addr >= 0)
return _mv88e6xxx_reg_read(ps, addr, regnum); return _mv88e6xxx_reg_read(ps, addr, regnum);
return 0xffff; return 0xffff;
} }
static int _mv88e6xxx_phy_write(struct mv88e6xxx_priv_state *ps, int addr, static int mv88e6xxx_mdio_write_direct(struct mv88e6xxx_priv_state *ps,
int regnum, u16 val) int addr, int regnum, u16 val)
{ {
if (addr >= 0) if (addr >= 0)
return _mv88e6xxx_reg_write(ps, addr, regnum, val); return _mv88e6xxx_reg_write(ps, addr, regnum, val);
...@@ -288,18 +289,18 @@ static int mv88e6xxx_ppu_enable(struct mv88e6xxx_priv_state *ps) ...@@ -288,18 +289,18 @@ static int mv88e6xxx_ppu_enable(struct mv88e6xxx_priv_state *ps)
int ret, err; int ret, err;
unsigned long timeout; unsigned long timeout;
ret = mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL); ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL);
if (ret < 0) if (ret < 0)
return ret; return ret;
err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL,
ret | GLOBAL_CONTROL_PPU_ENABLE); ret | GLOBAL_CONTROL_PPU_ENABLE);
if (err) if (err)
return err; return err;
timeout = jiffies + 1 * HZ; timeout = jiffies + 1 * HZ;
while (time_before(jiffies, timeout)) { while (time_before(jiffies, timeout)) {
ret = mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS); ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -317,11 +318,16 @@ static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly) ...@@ -317,11 +318,16 @@ static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
struct mv88e6xxx_priv_state *ps; struct mv88e6xxx_priv_state *ps;
ps = container_of(ugly, struct mv88e6xxx_priv_state, ppu_work); ps = container_of(ugly, struct mv88e6xxx_priv_state, ppu_work);
mutex_lock(&ps->smi_mutex);
if (mutex_trylock(&ps->ppu_mutex)) { if (mutex_trylock(&ps->ppu_mutex)) {
if (mv88e6xxx_ppu_enable(ps) == 0) if (mv88e6xxx_ppu_enable(ps) == 0)
ps->ppu_disabled = 0; ps->ppu_disabled = 0;
mutex_unlock(&ps->ppu_mutex); mutex_unlock(&ps->ppu_mutex);
} }
mutex_unlock(&ps->smi_mutex);
} }
static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps) static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
...@@ -373,8 +379,8 @@ void mv88e6xxx_ppu_state_init(struct mv88e6xxx_priv_state *ps) ...@@ -373,8 +379,8 @@ void mv88e6xxx_ppu_state_init(struct mv88e6xxx_priv_state *ps)
ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer; ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
} }
static int mv88e6xxx_phy_read_ppu(struct mv88e6xxx_priv_state *ps, int addr, static int mv88e6xxx_mdio_read_ppu(struct mv88e6xxx_priv_state *ps, int addr,
int regnum) int regnum)
{ {
int ret; int ret;
...@@ -387,8 +393,8 @@ static int mv88e6xxx_phy_read_ppu(struct mv88e6xxx_priv_state *ps, int addr, ...@@ -387,8 +393,8 @@ static int mv88e6xxx_phy_read_ppu(struct mv88e6xxx_priv_state *ps, int addr,
return ret; return ret;
} }
static int mv88e6xxx_phy_write_ppu(struct mv88e6xxx_priv_state *ps, int addr, static int mv88e6xxx_mdio_write_ppu(struct mv88e6xxx_priv_state *ps, int addr,
int regnum, u16 val) int regnum, u16 val)
{ {
int ret; int ret;
...@@ -824,7 +830,7 @@ static int mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg, ...@@ -824,7 +830,7 @@ static int mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg,
return ret; return ret;
} }
static int _mv88e6xxx_phy_wait(struct mv88e6xxx_priv_state *ps) static int mv88e6xxx_mdio_wait(struct mv88e6xxx_priv_state *ps)
{ {
return _mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_SMI_OP, return _mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
GLOBAL2_SMI_OP_BUSY); GLOBAL2_SMI_OP_BUSY);
...@@ -1071,7 +1077,7 @@ static int _mv88e6xxx_atu_wait(struct mv88e6xxx_priv_state *ps) ...@@ -1071,7 +1077,7 @@ static int _mv88e6xxx_atu_wait(struct mv88e6xxx_priv_state *ps)
GLOBAL_ATU_OP_BUSY); GLOBAL_ATU_OP_BUSY);
} }
static int _mv88e6xxx_phy_read_indirect(struct mv88e6xxx_priv_state *ps, static int mv88e6xxx_mdio_read_indirect(struct mv88e6xxx_priv_state *ps,
int addr, int regnum) int addr, int regnum)
{ {
int ret; int ret;
...@@ -1082,7 +1088,7 @@ static int _mv88e6xxx_phy_read_indirect(struct mv88e6xxx_priv_state *ps, ...@@ -1082,7 +1088,7 @@ static int _mv88e6xxx_phy_read_indirect(struct mv88e6xxx_priv_state *ps,
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = _mv88e6xxx_phy_wait(ps); ret = mv88e6xxx_mdio_wait(ps);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -1091,7 +1097,7 @@ static int _mv88e6xxx_phy_read_indirect(struct mv88e6xxx_priv_state *ps, ...@@ -1091,7 +1097,7 @@ static int _mv88e6xxx_phy_read_indirect(struct mv88e6xxx_priv_state *ps,
return ret; return ret;
} }
static int _mv88e6xxx_phy_write_indirect(struct mv88e6xxx_priv_state *ps, static int mv88e6xxx_mdio_write_indirect(struct mv88e6xxx_priv_state *ps,
int addr, int regnum, u16 val) int addr, int regnum, u16 val)
{ {
int ret; int ret;
...@@ -1104,7 +1110,7 @@ static int _mv88e6xxx_phy_write_indirect(struct mv88e6xxx_priv_state *ps, ...@@ -1104,7 +1110,7 @@ static int _mv88e6xxx_phy_write_indirect(struct mv88e6xxx_priv_state *ps,
GLOBAL2_SMI_OP_22_WRITE | (addr << 5) | GLOBAL2_SMI_OP_22_WRITE | (addr << 5) |
regnum); regnum);
return _mv88e6xxx_phy_wait(ps); return mv88e6xxx_mdio_wait(ps);
} }
static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
...@@ -1118,7 +1124,7 @@ static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, ...@@ -1118,7 +1124,7 @@ static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
reg = _mv88e6xxx_phy_read_indirect(ps, port, 16); reg = mv88e6xxx_mdio_read_indirect(ps, port, 16);
if (reg < 0) if (reg < 0)
goto out; goto out;
...@@ -1149,7 +1155,7 @@ static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, ...@@ -1149,7 +1155,7 @@ static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
ret = _mv88e6xxx_phy_read_indirect(ps, port, 16); ret = mv88e6xxx_mdio_read_indirect(ps, port, 16);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -1159,7 +1165,7 @@ static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, ...@@ -1159,7 +1165,7 @@ static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
if (e->tx_lpi_enabled) if (e->tx_lpi_enabled)
reg |= 0x0100; reg |= 0x0100;
ret = _mv88e6xxx_phy_write_indirect(ps, port, 16, reg); ret = mv88e6xxx_mdio_write_indirect(ps, port, 16, reg);
out: out:
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
...@@ -1322,7 +1328,7 @@ static int _mv88e6xxx_port_state(struct mv88e6xxx_priv_state *ps, int port, ...@@ -1322,7 +1328,7 @@ static int _mv88e6xxx_port_state(struct mv88e6xxx_priv_state *ps, int port,
if (ret) if (ret)
return ret; return ret;
netdev_dbg(ds->ports[port], "PortState %s (was %s)\n", netdev_dbg(ds->ports[port].netdev, "PortState %s (was %s)\n",
mv88e6xxx_port_state_names[state], mv88e6xxx_port_state_names[state],
mv88e6xxx_port_state_names[oldstate]); mv88e6xxx_port_state_names[oldstate]);
} }
...@@ -1400,7 +1406,8 @@ static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, ...@@ -1400,7 +1406,8 @@ static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
if (err) if (err)
netdev_err(ds->ports[port], "failed to update state to %s\n", netdev_err(ds->ports[port].netdev,
"failed to update state to %s\n",
mv88e6xxx_port_state_names[stp_state]); mv88e6xxx_port_state_names[stp_state]);
} }
...@@ -1426,8 +1433,8 @@ static int _mv88e6xxx_port_pvid(struct mv88e6xxx_priv_state *ps, int port, ...@@ -1426,8 +1433,8 @@ static int _mv88e6xxx_port_pvid(struct mv88e6xxx_priv_state *ps, int port,
if (ret < 0) if (ret < 0)
return ret; return ret;
netdev_dbg(ds->ports[port], "DefaultVID %d (was %d)\n", *new, netdev_dbg(ds->ports[port].netdev,
pvid); "DefaultVID %d (was %d)\n", *new, pvid);
} }
if (old) if (old)
...@@ -1842,7 +1849,8 @@ static int _mv88e6xxx_port_fid(struct mv88e6xxx_priv_state *ps, int port, ...@@ -1842,7 +1849,8 @@ static int _mv88e6xxx_port_fid(struct mv88e6xxx_priv_state *ps, int port,
if (ret < 0) if (ret < 0)
return ret; return ret;
netdev_dbg(ds->ports[port], "FID %d (was %d)\n", *new, fid); netdev_dbg(ds->ports[port].netdev,
"FID %d (was %d)\n", *new, fid);
} }
if (old) if (old)
...@@ -2023,7 +2031,7 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, ...@@ -2023,7 +2031,7 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
ps->ports[port].bridge_dev) ps->ports[port].bridge_dev)
break; /* same bridge, check next VLAN */ break; /* same bridge, check next VLAN */
netdev_warn(ds->ports[port], netdev_warn(ds->ports[port].netdev,
"hardware VLAN %d already used by %s\n", "hardware VLAN %d already used by %s\n",
vlan.vid, vlan.vid,
netdev_name(ps->ports[i].bridge_dev)); netdev_name(ps->ports[i].bridge_dev));
...@@ -2073,7 +2081,7 @@ static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, ...@@ -2073,7 +2081,7 @@ static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
if (ret < 0) if (ret < 0)
goto unlock; goto unlock;
netdev_dbg(ds->ports[port], "802.1Q Mode %s (was %s)\n", netdev_dbg(ds->ports[port].netdev, "802.1Q Mode %s (was %s)\n",
mv88e6xxx_port_8021q_mode_names[new], mv88e6xxx_port_8021q_mode_names[new],
mv88e6xxx_port_8021q_mode_names[old]); mv88e6xxx_port_8021q_mode_names[old]);
} }
...@@ -2142,11 +2150,12 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, ...@@ -2142,11 +2150,12 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
if (_mv88e6xxx_port_vlan_add(ps, port, vid, untagged)) if (_mv88e6xxx_port_vlan_add(ps, port, vid, untagged))
netdev_err(ds->ports[port], "failed to add VLAN %d%c\n", netdev_err(ds->ports[port].netdev,
"failed to add VLAN %d%c\n",
vid, untagged ? 'u' : 't'); vid, untagged ? 'u' : 't');
if (pvid && _mv88e6xxx_port_pvid_set(ps, port, vlan->vid_end)) if (pvid && _mv88e6xxx_port_pvid_set(ps, port, vlan->vid_end))
netdev_err(ds->ports[port], "failed to set PVID %d\n", netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",
vlan->vid_end); vlan->vid_end);
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
...@@ -2331,7 +2340,8 @@ static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, ...@@ -2331,7 +2340,8 @@ static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
if (_mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, state)) if (_mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, state))
netdev_err(ds->ports[port], "failed to load MAC address\n"); netdev_err(ds->ports[port].netdev,
"failed to load MAC address\n");
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
} }
...@@ -2532,39 +2542,40 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port) ...@@ -2532,39 +2542,40 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
for (i = 0; i < ps->info->num_ports; ++i) for (i = 0; i < ps->info->num_ports; ++i)
if (i == port || ps->ports[i].bridge_dev == bridge) if (i == port || ps->ports[i].bridge_dev == bridge)
if (_mv88e6xxx_port_based_vlan_map(ps, i)) if (_mv88e6xxx_port_based_vlan_map(ps, i))
netdev_warn(ds->ports[i], "failed to remap\n"); netdev_warn(ds->ports[i].netdev,
"failed to remap\n");
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
} }
static int _mv88e6xxx_phy_page_write(struct mv88e6xxx_priv_state *ps, static int _mv88e6xxx_mdio_page_write(struct mv88e6xxx_priv_state *ps,
int port, int page, int reg, int val) int port, int page, int reg, int val)
{ {
int ret; int ret;
ret = _mv88e6xxx_phy_write_indirect(ps, port, 0x16, page); ret = mv88e6xxx_mdio_write_indirect(ps, port, 0x16, page);
if (ret < 0) if (ret < 0)
goto restore_page_0; goto restore_page_0;
ret = _mv88e6xxx_phy_write_indirect(ps, port, reg, val); ret = mv88e6xxx_mdio_write_indirect(ps, port, reg, val);
restore_page_0: restore_page_0:
_mv88e6xxx_phy_write_indirect(ps, port, 0x16, 0x0); mv88e6xxx_mdio_write_indirect(ps, port, 0x16, 0x0);
return ret; return ret;
} }
static int _mv88e6xxx_phy_page_read(struct mv88e6xxx_priv_state *ps, static int _mv88e6xxx_mdio_page_read(struct mv88e6xxx_priv_state *ps,
int port, int page, int reg) int port, int page, int reg)
{ {
int ret; int ret;
ret = _mv88e6xxx_phy_write_indirect(ps, port, 0x16, page); ret = mv88e6xxx_mdio_write_indirect(ps, port, 0x16, page);
if (ret < 0) if (ret < 0)
goto restore_page_0; goto restore_page_0;
ret = _mv88e6xxx_phy_read_indirect(ps, port, reg); ret = mv88e6xxx_mdio_read_indirect(ps, port, reg);
restore_page_0: restore_page_0:
_mv88e6xxx_phy_write_indirect(ps, port, 0x16, 0x0); mv88e6xxx_mdio_write_indirect(ps, port, 0x16, 0x0);
return ret; return ret;
} }
...@@ -2635,16 +2646,16 @@ static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps) ...@@ -2635,16 +2646,16 @@ static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps)
{ {
int ret; int ret;
ret = _mv88e6xxx_phy_page_read(ps, REG_FIBER_SERDES, PAGE_FIBER_SERDES, ret = _mv88e6xxx_mdio_page_read(ps, REG_FIBER_SERDES,
MII_BMCR); PAGE_FIBER_SERDES, MII_BMCR);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (ret & BMCR_PDOWN) { if (ret & BMCR_PDOWN) {
ret &= ~BMCR_PDOWN; ret &= ~BMCR_PDOWN;
ret = _mv88e6xxx_phy_page_write(ps, REG_FIBER_SERDES, ret = _mv88e6xxx_mdio_page_write(ps, REG_FIBER_SERDES,
PAGE_FIBER_SERDES, MII_BMCR, PAGE_FIBER_SERDES, MII_BMCR,
ret); ret);
} }
return ret; return ret;
...@@ -2715,11 +2726,8 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_priv_state *ps, int port) ...@@ -2715,11 +2726,8 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_priv_state *ps, int port)
if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
mv88e6xxx_6320_family(ps)) { mv88e6xxx_6320_family(ps)) {
if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA) reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA |
reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA; PORT_CONTROL_FORWARD_UNKNOWN |
else
reg |= PORT_CONTROL_FRAME_MODE_DSA;
reg |= PORT_CONTROL_FORWARD_UNKNOWN |
PORT_CONTROL_FORWARD_UNKNOWN_MC; PORT_CONTROL_FORWARD_UNKNOWN_MC;
} }
...@@ -2727,7 +2735,6 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_priv_state *ps, int port) ...@@ -2727,7 +2735,6 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_priv_state *ps, int port)
mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) || mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) ||
mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps)) { mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps)) {
if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
reg |= PORT_CONTROL_EGRESS_ADD_TAG; reg |= PORT_CONTROL_EGRESS_ADD_TAG;
} }
} }
...@@ -3014,9 +3021,8 @@ static int mv88e6xxx_setup_global(struct mv88e6xxx_priv_state *ps) ...@@ -3014,9 +3021,8 @@ static int mv88e6xxx_setup_global(struct mv88e6xxx_priv_state *ps)
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++) {
int nexthop = 0x1f; int nexthop = 0x1f;
if (ps->ds->cd->rtable && if (i != ds->index && i < DSA_MAX_SWITCHES)
i != ps->ds->index && i < ps->ds->dst->pd->nr_chips) nexthop = ds->rtable[i] & 0x1f;
nexthop = ps->ds->cd->rtable[i] & 0x1f;
err = _mv88e6xxx_reg_write( err = _mv88e6xxx_reg_write(
ps, REG_GLOBAL2, ps, REG_GLOBAL2,
...@@ -3125,13 +3131,11 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) ...@@ -3125,13 +3131,11 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
int i; int i;
ps->ds = ds; ps->ds = ds;
ds->slave_mii_bus = ps->mdio_bus;
if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM)) if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
mutex_init(&ps->eeprom_mutex); mutex_init(&ps->eeprom_mutex);
if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
mv88e6xxx_ppu_state_init(ps);
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
err = mv88e6xxx_switch_reset(ps); err = mv88e6xxx_switch_reset(ps);
...@@ -3154,43 +3158,43 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) ...@@ -3154,43 +3158,43 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
return err; return err;
} }
int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg) int mv88e6xxx_mdio_page_read(struct dsa_switch *ds, int port, int page, int reg)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret; int ret;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
ret = _mv88e6xxx_phy_page_read(ps, port, page, reg); ret = _mv88e6xxx_mdio_page_read(ps, port, page, reg);
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
return ret; return ret;
} }
int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page, int mv88e6xxx_mdio_page_write(struct dsa_switch *ds, int port, int page,
int reg, int val) int reg, int val)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret; int ret;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
ret = _mv88e6xxx_phy_page_write(ps, port, page, reg, val); ret = _mv88e6xxx_mdio_page_write(ps, port, page, reg, val);
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
return ret; return ret;
} }
static int mv88e6xxx_port_to_phy_addr(struct mv88e6xxx_priv_state *ps, static int mv88e6xxx_port_to_mdio_addr(struct mv88e6xxx_priv_state *ps,
int port) int port)
{ {
if (port >= 0 && port < ps->info->num_ports) if (port >= 0 && port < ps->info->num_ports)
return port; return port;
return -EINVAL; return -EINVAL;
} }
static int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum) static int mv88e6xxx_mdio_read(struct mii_bus *bus, int port, int regnum)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = bus->priv;
int addr = mv88e6xxx_port_to_phy_addr(ps, port); int addr = mv88e6xxx_port_to_mdio_addr(ps, port);
int ret; int ret;
if (addr < 0) if (addr < 0)
...@@ -3199,21 +3203,21 @@ static int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum) ...@@ -3199,21 +3203,21 @@ static int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU)) if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
ret = mv88e6xxx_phy_read_ppu(ps, addr, regnum); ret = mv88e6xxx_mdio_read_ppu(ps, addr, regnum);
else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY)) else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
ret = _mv88e6xxx_phy_read_indirect(ps, addr, regnum); ret = mv88e6xxx_mdio_read_indirect(ps, addr, regnum);
else else
ret = _mv88e6xxx_phy_read(ps, addr, regnum); ret = mv88e6xxx_mdio_read_direct(ps, addr, regnum);
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
return ret; return ret;
} }
static int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, static int mv88e6xxx_mdio_write(struct mii_bus *bus, int port, int regnum,
u16 val) u16 val)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = bus->priv;
int addr = mv88e6xxx_port_to_phy_addr(ps, port); int addr = mv88e6xxx_port_to_mdio_addr(ps, port);
int ret; int ret;
if (addr < 0) if (addr < 0)
...@@ -3222,16 +3226,76 @@ static int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, ...@@ -3222,16 +3226,76 @@ static int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum,
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU)) if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
ret = mv88e6xxx_phy_write_ppu(ps, addr, regnum, val); ret = mv88e6xxx_mdio_write_ppu(ps, addr, regnum, val);
else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY)) else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
ret = _mv88e6xxx_phy_write_indirect(ps, addr, regnum, val); ret = mv88e6xxx_mdio_write_indirect(ps, addr, regnum, val);
else else
ret = _mv88e6xxx_phy_write(ps, addr, regnum, val); ret = mv88e6xxx_mdio_write_direct(ps, addr, regnum, val);
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
return ret; return ret;
} }
static int mv88e6xxx_mdio_register(struct mv88e6xxx_priv_state *ps,
struct device_node *np)
{
static int index;
struct mii_bus *bus;
int err;
if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
mv88e6xxx_ppu_state_init(ps);
if (np)
ps->mdio_np = of_get_child_by_name(np, "mdio");
bus = devm_mdiobus_alloc(ps->dev);
if (!bus)
return -ENOMEM;
bus->priv = (void *)ps;
if (np) {
bus->name = np->full_name;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name);
} else {
bus->name = "mv88e6xxx SMI";
snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++);
}
bus->read = mv88e6xxx_mdio_read;
bus->write = mv88e6xxx_mdio_write;
bus->parent = ps->dev;
if (ps->mdio_np)
err = of_mdiobus_register(bus, ps->mdio_np);
else
err = mdiobus_register(bus);
if (err) {
dev_err(ps->dev, "Cannot register MDIO bus (%d)\n", err);
goto out;
}
ps->mdio_bus = bus;
return 0;
out:
if (ps->mdio_np)
of_node_put(ps->mdio_np);
return err;
}
static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_priv_state *ps)
{
struct mii_bus *bus = ps->mdio_bus;
mdiobus_unregister(bus);
if (ps->mdio_np)
of_node_put(ps->mdio_np);
}
#ifdef CONFIG_NET_DSA_HWMON #ifdef CONFIG_NET_DSA_HWMON
static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp) static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
...@@ -3244,37 +3308,37 @@ static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp) ...@@ -3244,37 +3308,37 @@ static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
ret = _mv88e6xxx_phy_write(ps, 0x0, 0x16, 0x6); ret = mv88e6xxx_mdio_write_direct(ps, 0x0, 0x16, 0x6);
if (ret < 0) if (ret < 0)
goto error; goto error;
/* Enable temperature sensor */ /* Enable temperature sensor */
ret = _mv88e6xxx_phy_read(ps, 0x0, 0x1a); ret = mv88e6xxx_mdio_read_direct(ps, 0x0, 0x1a);
if (ret < 0) if (ret < 0)
goto error; goto error;
ret = _mv88e6xxx_phy_write(ps, 0x0, 0x1a, ret | (1 << 5)); ret = mv88e6xxx_mdio_write_direct(ps, 0x0, 0x1a, ret | (1 << 5));
if (ret < 0) if (ret < 0)
goto error; goto error;
/* Wait for temperature to stabilize */ /* Wait for temperature to stabilize */
usleep_range(10000, 12000); usleep_range(10000, 12000);
val = _mv88e6xxx_phy_read(ps, 0x0, 0x1a); val = mv88e6xxx_mdio_read_direct(ps, 0x0, 0x1a);
if (val < 0) { if (val < 0) {
ret = val; ret = val;
goto error; goto error;
} }
/* Disable temperature sensor */ /* Disable temperature sensor */
ret = _mv88e6xxx_phy_write(ps, 0x0, 0x1a, ret & ~(1 << 5)); ret = mv88e6xxx_mdio_write_direct(ps, 0x0, 0x1a, ret & ~(1 << 5));
if (ret < 0) if (ret < 0)
goto error; goto error;
*temp = ((val & 0x1f) - 5) * 5; *temp = ((val & 0x1f) - 5) * 5;
error: error:
_mv88e6xxx_phy_write(ps, 0x0, 0x16, 0x0); mv88e6xxx_mdio_write_direct(ps, 0x0, 0x16, 0x0);
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
return ret; return ret;
} }
...@@ -3287,7 +3351,7 @@ static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp) ...@@ -3287,7 +3351,7 @@ static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
*temp = 0; *temp = 0;
ret = mv88e6xxx_phy_page_read(ds, phy, 6, 27); ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 27);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -3320,7 +3384,7 @@ static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp) ...@@ -3320,7 +3384,7 @@ static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
*temp = 0; *temp = 0;
ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26); ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -3338,12 +3402,12 @@ static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp) ...@@ -3338,12 +3402,12 @@ static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT)) if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
return -EOPNOTSUPP; return -EOPNOTSUPP;
ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26); ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
if (ret < 0) if (ret < 0)
return ret; return ret;
temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f); temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
return mv88e6xxx_phy_page_write(ds, phy, 6, 26, return mv88e6xxx_mdio_page_write(ds, phy, 6, 26,
(ret & 0xe0ff) | (temp << 8)); (ret & 0xe0ff) | (temp << 8));
} }
static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm) static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
...@@ -3357,7 +3421,7 @@ static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm) ...@@ -3357,7 +3421,7 @@ static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
*alarm = false; *alarm = false;
ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26); ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -3544,6 +3608,7 @@ static const char *mv88e6xxx_drv_probe(struct device *dsa_dev, ...@@ -3544,6 +3608,7 @@ static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
struct mii_bus *bus; struct mii_bus *bus;
const char *name; const char *name;
int id, prod_num, rev; int id, prod_num, rev;
int err;
bus = dsa_host_dev_to_mii_bus(host_dev); bus = dsa_host_dev_to_mii_bus(host_dev);
if (!bus) if (!bus)
...@@ -3570,8 +3635,13 @@ static const char *mv88e6xxx_drv_probe(struct device *dsa_dev, ...@@ -3570,8 +3635,13 @@ static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
ps->bus = bus; ps->bus = bus;
ps->sw_addr = sw_addr; ps->sw_addr = sw_addr;
ps->info = info; ps->info = info;
ps->dev = dsa_dev;
mutex_init(&ps->smi_mutex); mutex_init(&ps->smi_mutex);
err = mv88e6xxx_mdio_register(ps, NULL);
if (err)
return NULL;
*priv = ps; *priv = ps;
dev_info(&ps->bus->dev, "switch 0x%x probed: %s, revision %u\n", dev_info(&ps->bus->dev, "switch 0x%x probed: %s, revision %u\n",
...@@ -3585,8 +3655,6 @@ struct dsa_switch_driver mv88e6xxx_switch_driver = { ...@@ -3585,8 +3655,6 @@ struct dsa_switch_driver mv88e6xxx_switch_driver = {
.probe = mv88e6xxx_drv_probe, .probe = mv88e6xxx_drv_probe,
.setup = mv88e6xxx_setup, .setup = mv88e6xxx_setup,
.set_addr = mv88e6xxx_set_addr, .set_addr = mv88e6xxx_set_addr,
.phy_read = mv88e6xxx_phy_read,
.phy_write = mv88e6xxx_phy_write,
.adjust_link = mv88e6xxx_adjust_link, .adjust_link = mv88e6xxx_adjust_link,
.get_strings = mv88e6xxx_get_strings, .get_strings = mv88e6xxx_get_strings,
.get_ethtool_stats = mv88e6xxx_get_ethtool_stats, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
...@@ -3672,8 +3740,20 @@ int mv88e6xxx_probe(struct mdio_device *mdiodev) ...@@ -3672,8 +3740,20 @@ int mv88e6xxx_probe(struct mdio_device *mdiodev)
!of_property_read_u32(np, "eeprom-length", &eeprom_len)) !of_property_read_u32(np, "eeprom-length", &eeprom_len))
ps->eeprom_len = eeprom_len; ps->eeprom_len = eeprom_len;
err = mv88e6xxx_mdio_register(ps, mdiodev->dev.of_node);
if (err)
return err;
ds->slave_mii_bus = ps->mdio_bus;
dev_set_drvdata(dev, ds); dev_set_drvdata(dev, ds);
err = dsa_register_switch(ds, mdiodev->dev.of_node);
if (err) {
mv88e6xxx_mdio_unregister(ps);
return err;
}
dev_info(dev, "switch 0x%x probed: %s, revision %u\n", dev_info(dev, "switch 0x%x probed: %s, revision %u\n",
prod_num, ps->info->name, rev); prod_num, ps->info->name, rev);
...@@ -3685,7 +3765,10 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev) ...@@ -3685,7 +3765,10 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev)
struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
dsa_unregister_switch(ds);
put_device(&ps->bus->dev); put_device(&ps->bus->dev);
mv88e6xxx_mdio_unregister(ps);
} }
static const struct of_device_id mv88e6xxx_of_match[] = { static const struct of_device_id mv88e6xxx_of_match[] = {
......
...@@ -600,6 +600,12 @@ struct mv88e6xxx_priv_state { ...@@ -600,6 +600,12 @@ struct mv88e6xxx_priv_state {
/* set to size of eeprom if supported by the switch */ /* set to size of eeprom if supported by the switch */
int eeprom_len; int eeprom_len;
/* Device node for the MDIO bus */
struct device_node *mdio_np;
/* And the MDIO bus itself */
struct mii_bus *mdio_bus;
}; };
enum stat_type { enum stat_type {
......
...@@ -26,6 +26,7 @@ enum dsa_tag_protocol { ...@@ -26,6 +26,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_TRAILER, DSA_TAG_PROTO_TRAILER,
DSA_TAG_PROTO_EDSA, DSA_TAG_PROTO_EDSA,
DSA_TAG_PROTO_BRCM, DSA_TAG_PROTO_BRCM,
DSA_TAG_LAST, /* MUST BE LAST */
}; };
#define DSA_MAX_SWITCHES 4 #define DSA_MAX_SWITCHES 4
...@@ -58,12 +59,11 @@ struct dsa_chip_data { ...@@ -58,12 +59,11 @@ struct dsa_chip_data {
struct device_node *port_dn[DSA_MAX_PORTS]; struct device_node *port_dn[DSA_MAX_PORTS];
/* /*
* An array (with nr_chips elements) of which element [a] * An array of which element [a] indicates which port on this
* indicates which port on this switch should be used to * switch should be used to send packets to that are destined
* send packets to that are destined for switch a. Can be * for switch a. Can be NULL if there is only one switch chip.
* NULL if there is only one switch chip.
*/ */
s8 *rtable; s8 rtable[DSA_MAX_SWITCHES];
}; };
struct dsa_platform_data { struct dsa_platform_data {
...@@ -85,6 +85,17 @@ struct dsa_platform_data { ...@@ -85,6 +85,17 @@ struct dsa_platform_data {
struct packet_type; struct packet_type;
struct dsa_switch_tree { struct dsa_switch_tree {
struct list_head list;
/* Tree identifier */
u32 tree;
/* Number of switches attached to this tree */
struct kref refcount;
/* Has this tree been applied to the hardware? */
bool applied;
/* /*
* Configuration data for the platform device that owns * Configuration data for the platform device that owns
* this dsa switch tree instance. * this dsa switch tree instance.
...@@ -100,7 +111,6 @@ struct dsa_switch_tree { ...@@ -100,7 +111,6 @@ struct dsa_switch_tree {
struct net_device *dev, struct net_device *dev,
struct packet_type *pt, struct packet_type *pt,
struct net_device *orig_dev); struct net_device *orig_dev);
enum dsa_tag_protocol tag_protocol;
/* /*
* Original copy of the master netdev ethtool_ops * Original copy of the master netdev ethtool_ops
...@@ -117,6 +127,17 @@ struct dsa_switch_tree { ...@@ -117,6 +127,17 @@ struct dsa_switch_tree {
* Data for the individual switch chips. * Data for the individual switch chips.
*/ */
struct dsa_switch *ds[DSA_MAX_SWITCHES]; struct dsa_switch *ds[DSA_MAX_SWITCHES];
/*
* Tagging protocol operations for adding and removing an
* encapsulation tag.
*/
const struct dsa_device_ops *tag_ops;
};
struct dsa_port {
struct net_device *netdev;
struct device_node *dn;
}; };
struct dsa_switch { struct dsa_switch {
...@@ -144,6 +165,13 @@ struct dsa_switch { ...@@ -144,6 +165,13 @@ struct dsa_switch {
*/ */
struct dsa_switch_driver *drv; struct dsa_switch_driver *drv;
/*
* An array of which element [a] indicates which port on this
* switch should be used to send packets to that are destined
* for switch a. Can be NULL if there is only one switch chip.
*/
s8 rtable[DSA_MAX_SWITCHES];
#ifdef CONFIG_NET_DSA_HWMON #ifdef CONFIG_NET_DSA_HWMON
/* /*
* Hardware monitoring information * Hardware monitoring information
...@@ -152,14 +180,20 @@ struct dsa_switch { ...@@ -152,14 +180,20 @@ struct dsa_switch {
struct device *hwmon_dev; struct device *hwmon_dev;
#endif #endif
/*
* The lower device this switch uses to talk to the host
*/
struct net_device *master_netdev;
/* /*
* Slave mii_bus and devices for the individual ports. * Slave mii_bus and devices for the individual ports.
*/ */
u32 dsa_port_mask; u32 dsa_port_mask;
u32 cpu_port_mask;
u32 enabled_port_mask; u32 enabled_port_mask;
u32 phys_mii_mask; u32 phys_mii_mask;
struct dsa_port ports[DSA_MAX_PORTS];
struct mii_bus *slave_mii_bus; struct mii_bus *slave_mii_bus;
struct net_device *ports[DSA_MAX_PORTS];
}; };
static inline bool dsa_is_cpu_port(struct dsa_switch *ds, int p) static inline bool dsa_is_cpu_port(struct dsa_switch *ds, int p)
...@@ -174,7 +208,7 @@ static inline bool dsa_is_dsa_port(struct dsa_switch *ds, int p) ...@@ -174,7 +208,7 @@ static inline bool dsa_is_dsa_port(struct dsa_switch *ds, int p)
static inline bool dsa_is_port_initialized(struct dsa_switch *ds, int p) static inline bool dsa_is_port_initialized(struct dsa_switch *ds, int p)
{ {
return ds->enabled_port_mask & (1 << p) && ds->ports[p]; return ds->enabled_port_mask & (1 << p) && ds->ports[p].netdev;
} }
static inline u8 dsa_upstream_port(struct dsa_switch *ds) static inline u8 dsa_upstream_port(struct dsa_switch *ds)
...@@ -190,7 +224,7 @@ static inline u8 dsa_upstream_port(struct dsa_switch *ds) ...@@ -190,7 +224,7 @@ static inline u8 dsa_upstream_port(struct dsa_switch *ds)
if (dst->cpu_switch == ds->index) if (dst->cpu_switch == ds->index)
return dst->cpu_port; return dst->cpu_port;
else else
return ds->cd->rtable[dst->cpu_switch]; return ds->rtable[dst->cpu_switch];
} }
struct switchdev_trans; struct switchdev_trans;
...@@ -344,4 +378,7 @@ static inline bool dsa_uses_tagged_protocol(struct dsa_switch_tree *dst) ...@@ -344,4 +378,7 @@ static inline bool dsa_uses_tagged_protocol(struct dsa_switch_tree *dst)
{ {
return dst->rcv != NULL; return dst->rcv != NULL;
} }
void dsa_unregister_switch(struct dsa_switch *ds);
int dsa_register_switch(struct dsa_switch *ds, struct device_node *np);
#endif #endif
# the core # the core
obj-$(CONFIG_NET_DSA) += dsa_core.o obj-$(CONFIG_NET_DSA) += dsa_core.o
dsa_core-y += dsa.o slave.o dsa_core-y += dsa.o slave.o dsa2.o
# tagging formats # tagging formats
dsa_core-$(CONFIG_NET_DSA_TAG_BRCM) += tag_brcm.o dsa_core-$(CONFIG_NET_DSA_TAG_BRCM) += tag_brcm.o
......
...@@ -29,6 +29,33 @@ ...@@ -29,6 +29,33 @@
char dsa_driver_version[] = "0.1"; char dsa_driver_version[] = "0.1";
static struct sk_buff *dsa_slave_notag_xmit(struct sk_buff *skb,
struct net_device *dev)
{
/* Just return the original SKB */
return skb;
}
static const struct dsa_device_ops none_ops = {
.xmit = dsa_slave_notag_xmit,
.rcv = NULL,
};
const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = {
#ifdef CONFIG_NET_DSA_TAG_DSA
[DSA_TAG_PROTO_DSA] = &dsa_netdev_ops,
#endif
#ifdef CONFIG_NET_DSA_TAG_EDSA
[DSA_TAG_PROTO_EDSA] = &edsa_netdev_ops,
#endif
#ifdef CONFIG_NET_DSA_TAG_TRAILER
[DSA_TAG_PROTO_TRAILER] = &trailer_netdev_ops,
#endif
#ifdef CONFIG_NET_DSA_TAG_BRCM
[DSA_TAG_PROTO_BRCM] = &brcm_netdev_ops,
#endif
[DSA_TAG_PROTO_NONE] = &none_ops,
};
/* switch driver registration ***********************************************/ /* switch driver registration ***********************************************/
static DEFINE_MUTEX(dsa_switch_drivers_mutex); static DEFINE_MUTEX(dsa_switch_drivers_mutex);
...@@ -180,41 +207,65 @@ __ATTRIBUTE_GROUPS(dsa_hwmon); ...@@ -180,41 +207,65 @@ __ATTRIBUTE_GROUPS(dsa_hwmon);
#endif /* CONFIG_NET_DSA_HWMON */ #endif /* CONFIG_NET_DSA_HWMON */
/* basic switch operations **************************************************/ /* basic switch operations **************************************************/
static int dsa_cpu_dsa_setup(struct dsa_switch *ds, struct net_device *master) int dsa_cpu_dsa_setup(struct dsa_switch *ds, struct device *dev,
struct device_node *port_dn, int port)
{ {
struct dsa_chip_data *cd = ds->cd;
struct device_node *port_dn;
struct phy_device *phydev; struct phy_device *phydev;
int ret, port, mode; int ret, mode;
if (of_phy_is_fixed_link(port_dn)) {
ret = of_phy_register_fixed_link(port_dn);
if (ret) {
dev_err(dev, "failed to register fixed PHY\n");
return ret;
}
phydev = of_phy_find_device(port_dn);
mode = of_get_phy_mode(port_dn);
if (mode < 0)
mode = PHY_INTERFACE_MODE_NA;
phydev->interface = mode;
genphy_config_init(phydev);
genphy_read_status(phydev);
if (ds->drv->adjust_link)
ds->drv->adjust_link(ds, port, phydev);
}
return 0;
}
static int dsa_cpu_dsa_setups(struct dsa_switch *ds, struct device *dev)
{
struct device_node *port_dn;
int ret, port;
for (port = 0; port < DSA_MAX_PORTS; port++) { for (port = 0; port < DSA_MAX_PORTS; port++) {
if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)))
continue; continue;
port_dn = cd->port_dn[port]; port_dn = ds->ports[port].dn;
if (of_phy_is_fixed_link(port_dn)) { ret = dsa_cpu_dsa_setup(ds, dev, port_dn, port);
ret = of_phy_register_fixed_link(port_dn); if (ret)
if (ret) { return ret;
netdev_err(master,
"failed to register fixed PHY\n");
return ret;
}
phydev = of_phy_find_device(port_dn);
mode = of_get_phy_mode(port_dn);
if (mode < 0)
mode = PHY_INTERFACE_MODE_NA;
phydev->interface = mode;
genphy_config_init(phydev);
genphy_read_status(phydev);
if (ds->drv->adjust_link)
ds->drv->adjust_link(ds, port, phydev);
}
} }
return 0; return 0;
} }
const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol)
{
const struct dsa_device_ops *ops;
if (tag_protocol >= DSA_TAG_LAST)
return ERR_PTR(-EINVAL);
ops = dsa_device_ops[tag_protocol];
if (!ops)
return ERR_PTR(-ENOPROTOOPT);
return ops;
}
static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent) static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)
{ {
struct dsa_switch_driver *drv = ds->drv; struct dsa_switch_driver *drv = ds->drv;
...@@ -243,6 +294,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent) ...@@ -243,6 +294,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)
} }
dst->cpu_switch = index; dst->cpu_switch = index;
dst->cpu_port = i; dst->cpu_port = i;
ds->cpu_port_mask |= 1 << i;
} else if (!strcmp(name, "dsa")) { } else if (!strcmp(name, "dsa")) {
ds->dsa_port_mask |= 1 << i; ds->dsa_port_mask |= 1 << i;
} else { } else {
...@@ -267,37 +319,17 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent) ...@@ -267,37 +319,17 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)
* switch. * switch.
*/ */
if (dst->cpu_switch == index) { if (dst->cpu_switch == index) {
switch (drv->tag_protocol) { dst->tag_ops = dsa_resolve_tag_protocol(drv->tag_protocol);
#ifdef CONFIG_NET_DSA_TAG_DSA if (IS_ERR(dst->tag_ops)) {
case DSA_TAG_PROTO_DSA: ret = PTR_ERR(dst->tag_ops);
dst->rcv = dsa_netdev_ops.rcv;
break;
#endif
#ifdef CONFIG_NET_DSA_TAG_EDSA
case DSA_TAG_PROTO_EDSA:
dst->rcv = edsa_netdev_ops.rcv;
break;
#endif
#ifdef CONFIG_NET_DSA_TAG_TRAILER
case DSA_TAG_PROTO_TRAILER:
dst->rcv = trailer_netdev_ops.rcv;
break;
#endif
#ifdef CONFIG_NET_DSA_TAG_BRCM
case DSA_TAG_PROTO_BRCM:
dst->rcv = brcm_netdev_ops.rcv;
break;
#endif
case DSA_TAG_PROTO_NONE:
break;
default:
ret = -ENOPROTOOPT;
goto out; goto out;
} }
dst->tag_protocol = drv->tag_protocol; dst->rcv = dst->tag_ops->rcv;
} }
memcpy(ds->rtable, cd->rtable, sizeof(ds->rtable));
/* /*
* Do basic register setup. * Do basic register setup.
*/ */
...@@ -309,22 +341,25 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent) ...@@ -309,22 +341,25 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)
if (ret < 0) if (ret < 0)
goto out; goto out;
ds->slave_mii_bus = devm_mdiobus_alloc(parent); if (!ds->slave_mii_bus && drv->phy_read) {
if (ds->slave_mii_bus == NULL) { ds->slave_mii_bus = devm_mdiobus_alloc(parent);
ret = -ENOMEM; if (!ds->slave_mii_bus) {
goto out; ret = -ENOMEM;
} goto out;
dsa_slave_mii_bus_init(ds); }
dsa_slave_mii_bus_init(ds);
ret = mdiobus_register(ds->slave_mii_bus);
if (ret < 0)
goto out;
ret = mdiobus_register(ds->slave_mii_bus);
if (ret < 0)
goto out;
}
/* /*
* Create network devices for physical switch ports. * Create network devices for physical switch ports.
*/ */
for (i = 0; i < DSA_MAX_PORTS; i++) { for (i = 0; i < DSA_MAX_PORTS; i++) {
ds->ports[i].dn = cd->port_dn[i];
if (!(ds->enabled_port_mask & (1 << i))) if (!(ds->enabled_port_mask & (1 << i)))
continue; continue;
...@@ -337,7 +372,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent) ...@@ -337,7 +372,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)
} }
/* Perform configuration of the CPU and DSA ports */ /* Perform configuration of the CPU and DSA ports */
ret = dsa_cpu_dsa_setup(ds, dst->master_netdev); ret = dsa_cpu_dsa_setups(ds, parent);
if (ret < 0) { if (ret < 0) {
netdev_err(dst->master_netdev, "[%d] : can't configure CPU and DSA ports\n", netdev_err(dst->master_netdev, "[%d] : can't configure CPU and DSA ports\n",
index); index);
...@@ -420,11 +455,21 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, ...@@ -420,11 +455,21 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,
return ds; return ds;
} }
static void dsa_switch_destroy(struct dsa_switch *ds) void dsa_cpu_dsa_destroy(struct device_node *port_dn)
{ {
struct device_node *port_dn;
struct phy_device *phydev; struct phy_device *phydev;
struct dsa_chip_data *cd = ds->cd;
if (of_phy_is_fixed_link(port_dn)) {
phydev = of_phy_find_device(port_dn);
if (phydev) {
phy_device_free(phydev);
fixed_phy_unregister(phydev);
}
}
}
static void dsa_switch_destroy(struct dsa_switch *ds)
{
int port; int port;
#ifdef CONFIG_NET_DSA_HWMON #ifdef CONFIG_NET_DSA_HWMON
...@@ -437,26 +482,25 @@ static void dsa_switch_destroy(struct dsa_switch *ds) ...@@ -437,26 +482,25 @@ static void dsa_switch_destroy(struct dsa_switch *ds)
if (!(ds->enabled_port_mask & (1 << port))) if (!(ds->enabled_port_mask & (1 << port)))
continue; continue;
if (!ds->ports[port]) if (!ds->ports[port].netdev)
continue; continue;
dsa_slave_destroy(ds->ports[port]); dsa_slave_destroy(ds->ports[port].netdev);
} }
/* Remove any fixed link PHYs */ /* Disable configuration of the CPU and DSA ports */
for (port = 0; port < DSA_MAX_PORTS; port++) { for (port = 0; port < DSA_MAX_PORTS; port++) {
port_dn = cd->port_dn[port]; if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)))
if (of_phy_is_fixed_link(port_dn)) { continue;
phydev = of_phy_find_device(port_dn); dsa_cpu_dsa_destroy(ds->ports[port].dn);
if (phydev) {
phy_device_free(phydev); /* Clearing a bit which is not set does no harm */
of_node_put(port_dn); ds->cpu_port_mask |= ~(1 << port);
fixed_phy_unregister(phydev); ds->dsa_port_mask |= ~(1 << port);
}
}
} }
mdiobus_unregister(ds->slave_mii_bus); if (ds->slave_mii_bus && ds->drv->phy_read)
mdiobus_unregister(ds->slave_mii_bus);
} }
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
...@@ -469,7 +513,7 @@ static int dsa_switch_suspend(struct dsa_switch *ds) ...@@ -469,7 +513,7 @@ static int dsa_switch_suspend(struct dsa_switch *ds)
if (!dsa_is_port_initialized(ds, i)) if (!dsa_is_port_initialized(ds, i))
continue; continue;
ret = dsa_slave_suspend(ds->ports[i]); ret = dsa_slave_suspend(ds->ports[i].netdev);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -495,7 +539,7 @@ static int dsa_switch_resume(struct dsa_switch *ds) ...@@ -495,7 +539,7 @@ static int dsa_switch_resume(struct dsa_switch *ds)
if (!dsa_is_port_initialized(ds, i)) if (!dsa_is_port_initialized(ds, i))
continue; continue;
ret = dsa_slave_resume(ds->ports[i]); ret = dsa_slave_resume(ds->ports[i].netdev);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -587,17 +631,6 @@ static int dsa_of_setup_routing_table(struct dsa_platform_data *pd, ...@@ -587,17 +631,6 @@ static int dsa_of_setup_routing_table(struct dsa_platform_data *pd,
if (link_sw_addr >= pd->nr_chips) if (link_sw_addr >= pd->nr_chips)
return -EINVAL; return -EINVAL;
/* First time routing table allocation */
if (!cd->rtable) {
cd->rtable = kmalloc_array(pd->nr_chips, sizeof(s8),
GFP_KERNEL);
if (!cd->rtable)
return -ENOMEM;
/* default to no valid uplink/downlink */
memset(cd->rtable, -1, pd->nr_chips * sizeof(s8));
}
cd->rtable[link_sw_addr] = port_index; cd->rtable[link_sw_addr] = port_index;
return 0; return 0;
...@@ -639,7 +672,6 @@ static void dsa_of_free_platform_data(struct dsa_platform_data *pd) ...@@ -639,7 +672,6 @@ static void dsa_of_free_platform_data(struct dsa_platform_data *pd)
kfree(pd->chip[i].port_names[port_index]); kfree(pd->chip[i].port_names[port_index]);
port_index++; port_index++;
} }
kfree(pd->chip[i].rtable);
/* Drop our reference to the MDIO bus device */ /* Drop our reference to the MDIO bus device */
if (pd->chip[i].host_dev) if (pd->chip[i].host_dev)
......
/*
* net/dsa/dsa2.c - Hardware switch handling, binding version 2
* Copyright (c) 2008-2009 Marvell Semiconductor
* Copyright (c) 2013 Florian Fainelli <florian@openwrt.org>
* Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/rtnetlink.h>
#include <net/dsa.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include "dsa_priv.h"
static LIST_HEAD(dsa_switch_trees);
static DEFINE_MUTEX(dsa2_mutex);
static struct dsa_switch_tree *dsa_get_dst(u32 tree)
{
struct dsa_switch_tree *dst;
list_for_each_entry(dst, &dsa_switch_trees, list)
if (dst->tree == tree)
return dst;
return NULL;
}
static void dsa_free_dst(struct kref *ref)
{
struct dsa_switch_tree *dst = container_of(ref, struct dsa_switch_tree,
refcount);
list_del(&dst->list);
kfree(dst);
}
static void dsa_put_dst(struct dsa_switch_tree *dst)
{
kref_put(&dst->refcount, dsa_free_dst);
}
static struct dsa_switch_tree *dsa_add_dst(u32 tree)
{
struct dsa_switch_tree *dst;
dst = kzalloc(sizeof(*dst), GFP_KERNEL);
if (!dst)
return NULL;
dst->tree = tree;
dst->cpu_switch = -1;
INIT_LIST_HEAD(&dst->list);
list_add_tail(&dsa_switch_trees, &dst->list);
kref_init(&dst->refcount);
return dst;
}
static void dsa_dst_add_ds(struct dsa_switch_tree *dst,
struct dsa_switch *ds, u32 index)
{
kref_get(&dst->refcount);
dst->ds[index] = ds;
}
static void dsa_dst_del_ds(struct dsa_switch_tree *dst,
struct dsa_switch *ds, u32 index)
{
dst->ds[index] = NULL;
kref_put(&dst->refcount, dsa_free_dst);
}
static bool dsa_port_is_dsa(struct device_node *port)
{
const char *name;
name = of_get_property(port, "label", NULL);
if (!name)
return false;
if (!strcmp(name, "dsa"))
return true;
return false;
}
static bool dsa_port_is_cpu(struct device_node *port)
{
const char *name;
name = of_get_property(port, "label", NULL);
if (!name)
return false;
if (!strcmp(name, "cpu"))
return true;
return false;
}
static bool dsa_ds_find_port(struct dsa_switch *ds,
struct device_node *port)
{
u32 index;
for (index = 0; index < DSA_MAX_PORTS; index++)
if (ds->ports[index].dn == port)
return true;
return false;
}
static struct dsa_switch *dsa_dst_find_port(struct dsa_switch_tree *dst,
struct device_node *port)
{
struct dsa_switch *ds;
u32 index;
for (index = 0; index < DSA_MAX_SWITCHES; index++) {
ds = dst->ds[index];
if (!ds)
continue;
if (dsa_ds_find_port(ds, port))
return ds;
}
return NULL;
}
static int dsa_port_complete(struct dsa_switch_tree *dst,
struct dsa_switch *src_ds,
struct device_node *port,
u32 src_port)
{
struct device_node *link;
int index;
struct dsa_switch *dst_ds;
for (index = 0;; index++) {
link = of_parse_phandle(port, "link", index);
if (!link)
break;
dst_ds = dsa_dst_find_port(dst, link);
of_node_put(link);
if (!dst_ds)
return 1;
src_ds->rtable[dst_ds->index] = src_port;
}
return 0;
}
/* A switch is complete if all the DSA ports phandles point to ports
* known in the tree. A return value of 1 means the tree is not
* complete. This is not an error condition. A value of 0 is
* success.
*/
static int dsa_ds_complete(struct dsa_switch_tree *dst, struct dsa_switch *ds)
{
struct device_node *port;
u32 index;
int err;
for (index = 0; index < DSA_MAX_PORTS; index++) {
port = ds->ports[index].dn;
if (!port)
continue;
if (!dsa_port_is_dsa(port))
continue;
err = dsa_port_complete(dst, ds, port, index);
if (err != 0)
return err;
ds->dsa_port_mask |= BIT(index);
}
return 0;
}
/* A tree is complete if all the DSA ports phandles point to ports
* known in the tree. A return value of 1 means the tree is not
* complete. This is not an error condition. A value of 0 is
* success.
*/
static int dsa_dst_complete(struct dsa_switch_tree *dst)
{
struct dsa_switch *ds;
u32 index;
int err;
for (index = 0; index < DSA_MAX_SWITCHES; index++) {
ds = dst->ds[index];
if (!ds)
continue;
err = dsa_ds_complete(dst, ds);
if (err != 0)
return err;
}
return 0;
}
static int dsa_dsa_port_apply(struct device_node *port, u32 index,
struct dsa_switch *ds)
{
int err;
err = dsa_cpu_dsa_setup(ds, ds->dev, port, index);
if (err) {
dev_warn(ds->dev, "Failed to setup dsa port %d: %d\n",
index, err);
return err;
}
return 0;
}
static void dsa_dsa_port_unapply(struct device_node *port, u32 index,
struct dsa_switch *ds)
{
dsa_cpu_dsa_destroy(port);
}
static int dsa_cpu_port_apply(struct device_node *port, u32 index,
struct dsa_switch *ds)
{
int err;
err = dsa_cpu_dsa_setup(ds, ds->dev, port, index);
if (err) {
dev_warn(ds->dev, "Failed to setup cpu port %d: %d\n",
index, err);
return err;
}
ds->cpu_port_mask |= BIT(index);
return 0;
}
static void dsa_cpu_port_unapply(struct device_node *port, u32 index,
struct dsa_switch *ds)
{
dsa_cpu_dsa_destroy(port);
ds->cpu_port_mask &= ~BIT(index);
}
static int dsa_user_port_apply(struct device_node *port, u32 index,
struct dsa_switch *ds)
{
const char *name;
int err;
name = of_get_property(port, "label", NULL);
err = dsa_slave_create(ds, ds->dev, index, name);
if (err) {
dev_warn(ds->dev, "Failed to create slave %d: %d\n",
index, err);
return err;
}
return 0;
}
static void dsa_user_port_unapply(struct device_node *port, u32 index,
struct dsa_switch *ds)
{
if (ds->ports[index].netdev) {
dsa_slave_destroy(ds->ports[index].netdev);
ds->ports[index].netdev = NULL;
}
}
static int dsa_ds_apply(struct dsa_switch_tree *dst, struct dsa_switch *ds)
{
struct device_node *port;
u32 index;
int err;
err = ds->drv->setup(ds);
if (err < 0)
return err;
err = ds->drv->set_addr(ds, dst->master_netdev->dev_addr);
if (err < 0)
return err;
err = ds->drv->set_addr(ds, dst->master_netdev->dev_addr);
if (err < 0)
return err;
for (index = 0; index < DSA_MAX_PORTS; index++) {
port = ds->ports[index].dn;
if (!port)
continue;
if (dsa_port_is_dsa(port)) {
err = dsa_dsa_port_apply(port, index, ds);
if (err)
return err;
continue;
}
if (dsa_port_is_cpu(port)) {
err = dsa_cpu_port_apply(port, index, ds);
if (err)
return err;
continue;
}
err = dsa_user_port_apply(port, index, ds);
if (err)
continue;
}
return 0;
}
static void dsa_ds_unapply(struct dsa_switch_tree *dst, struct dsa_switch *ds)
{
struct device_node *port;
u32 index;
for (index = 0; index < DSA_MAX_PORTS; index++) {
port = ds->ports[index].dn;
if (!port)
continue;
if (dsa_port_is_dsa(port)) {
dsa_dsa_port_unapply(port, index, ds);
continue;
}
if (dsa_port_is_cpu(port)) {
dsa_cpu_port_unapply(port, index, ds);
continue;
}
dsa_user_port_unapply(port, index, ds);
}
}
static int dsa_dst_apply(struct dsa_switch_tree *dst)
{
struct dsa_switch *ds;
u32 index;
int err;
for (index = 0; index < DSA_MAX_SWITCHES; index++) {
ds = dst->ds[index];
if (!ds)
continue;
err = dsa_ds_apply(dst, ds);
if (err)
return err;
}
/* If we use a tagging format that doesn't have an ethertype
* field, make sure that all packets from this point on get
* sent to the tag format's receive function.
*/
wmb();
dst->master_netdev->dsa_ptr = (void *)dst;
dst->applied = true;
return 0;
}
static void dsa_dst_unapply(struct dsa_switch_tree *dst)
{
struct dsa_switch *ds;
u32 index;
if (!dst->applied)
return;
dst->master_netdev->dsa_ptr = NULL;
/* If we used a tagging format that doesn't have an ethertype
* field, make sure that all packets from this point get sent
* without the tag and go through the regular receive path.
*/
wmb();
for (index = 0; index < DSA_MAX_SWITCHES; index++) {
ds = dst->ds[index];
if (!ds)
continue;
dsa_ds_unapply(dst, ds);
}
pr_info("DSA: tree %d unapplied\n", dst->tree);
dst->applied = false;
}
static int dsa_cpu_parse(struct device_node *port, u32 index,
struct dsa_switch_tree *dst,
struct dsa_switch *ds)
{
struct net_device *ethernet_dev;
struct device_node *ethernet;
ethernet = of_parse_phandle(port, "ethernet", 0);
if (!ethernet)
return -EINVAL;
ethernet_dev = of_find_net_device_by_node(ethernet);
if (!ethernet_dev)
return -EPROBE_DEFER;
if (!ds->master_netdev)
ds->master_netdev = ethernet_dev;
if (!dst->master_netdev)
dst->master_netdev = ethernet_dev;
if (dst->cpu_switch == -1) {
dst->cpu_switch = ds->index;
dst->cpu_port = index;
}
dst->tag_ops = dsa_resolve_tag_protocol(ds->drv->tag_protocol);
if (IS_ERR(dst->tag_ops)) {
dev_warn(ds->dev, "No tagger for this switch\n");
return PTR_ERR(dst->tag_ops);
}
dst->rcv = dst->tag_ops->rcv;
return 0;
}
static int dsa_ds_parse(struct dsa_switch_tree *dst, struct dsa_switch *ds)
{
struct device_node *port;
u32 index;
int err;
for (index = 0; index < DSA_MAX_PORTS; index++) {
port = ds->ports[index].dn;
if (!port)
continue;
if (dsa_port_is_cpu(port)) {
err = dsa_cpu_parse(port, index, dst, ds);
if (err)
return err;
}
}
pr_info("DSA: switch %d %d parsed\n", dst->tree, ds->index);
return 0;
}
static int dsa_dst_parse(struct dsa_switch_tree *dst)
{
struct dsa_switch *ds;
u32 index;
int err;
for (index = 0; index < DSA_MAX_SWITCHES; index++) {
ds = dst->ds[index];
if (!ds)
continue;
err = dsa_ds_parse(dst, ds);
if (err)
return err;
}
if (!dst->master_netdev) {
pr_warn("Tree has no master device\n");
return -EINVAL;
}
pr_info("DSA: tree %d parsed\n", dst->tree);
return 0;
}
static int dsa_parse_ports_dn(struct device_node *ports, struct dsa_switch *ds)
{
struct device_node *port;
int err;
u32 reg;
for_each_available_child_of_node(ports, port) {
err = of_property_read_u32(port, "reg", &reg);
if (err)
return err;
if (reg >= DSA_MAX_PORTS)
return -EINVAL;
ds->ports[reg].dn = port;
}
return 0;
}
static int dsa_parse_member(struct device_node *np, u32 *tree, u32 *index)
{
int err;
*tree = *index = 0;
err = of_property_read_u32_index(np, "dsa,member", 0, tree);
if (err) {
/* Does not exist, but it is optional */
if (err == -EINVAL)
return 0;
return err;
}
err = of_property_read_u32_index(np, "dsa,member", 1, index);
if (err)
return err;
if (*index >= DSA_MAX_SWITCHES)
return -EINVAL;
return 0;
}
static struct device_node *dsa_get_ports(struct dsa_switch *ds,
struct device_node *np)
{
struct device_node *ports;
ports = of_get_child_by_name(np, "ports");
if (!ports) {
dev_err(ds->dev, "no ports child node found\n");
return ERR_PTR(-EINVAL);
}
return ports;
}
static int _dsa_register_switch(struct dsa_switch *ds, struct device_node *np)
{
struct device_node *ports = dsa_get_ports(ds, np);
struct dsa_switch_tree *dst;
u32 tree, index;
int err;
err = dsa_parse_member(np, &tree, &index);
if (err)
return err;
if (IS_ERR(ports))
return PTR_ERR(ports);
err = dsa_parse_ports_dn(ports, ds);
if (err)
return err;
dst = dsa_get_dst(tree);
if (!dst) {
dst = dsa_add_dst(tree);
if (!dst)
return -ENOMEM;
}
if (dst->ds[index]) {
err = -EBUSY;
goto out;
}
ds->dst = dst;
ds->index = index;
dsa_dst_add_ds(dst, ds, index);
err = dsa_dst_complete(dst);
if (err < 0)
goto out_del_dst;
if (err == 1) {
/* Not all switches registered yet */
err = 0;
goto out;
}
if (dst->applied) {
pr_info("DSA: Disjoint trees?\n");
return -EINVAL;
}
err = dsa_dst_parse(dst);
if (err)
goto out_del_dst;
err = dsa_dst_apply(dst);
if (err) {
dsa_dst_unapply(dst);
goto out_del_dst;
}
dsa_put_dst(dst);
return 0;
out_del_dst:
dsa_dst_del_ds(dst, ds, ds->index);
out:
dsa_put_dst(dst);
return err;
}
int dsa_register_switch(struct dsa_switch *ds, struct device_node *np)
{
int err;
mutex_lock(&dsa2_mutex);
err = _dsa_register_switch(ds, np);
mutex_unlock(&dsa2_mutex);
return err;
}
EXPORT_SYMBOL_GPL(dsa_register_switch);
void _dsa_unregister_switch(struct dsa_switch *ds)
{
struct dsa_switch_tree *dst = ds->dst;
dsa_dst_unapply(dst);
dsa_dst_del_ds(dst, ds, ds->index);
}
void dsa_unregister_switch(struct dsa_switch *ds)
{
mutex_lock(&dsa2_mutex);
_dsa_unregister_switch(ds);
mutex_unlock(&dsa2_mutex);
}
EXPORT_SYMBOL_GPL(dsa_unregister_switch);
...@@ -50,12 +50,16 @@ struct dsa_slave_priv { ...@@ -50,12 +50,16 @@ struct dsa_slave_priv {
/* dsa.c */ /* dsa.c */
extern char dsa_driver_version[]; extern char dsa_driver_version[];
int dsa_cpu_dsa_setup(struct dsa_switch *ds, struct device *dev,
struct device_node *port_dn, int port);
void dsa_cpu_dsa_destroy(struct device_node *port_dn);
const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol);
/* slave.c */ /* slave.c */
extern const struct dsa_device_ops notag_netdev_ops; extern const struct dsa_device_ops notag_netdev_ops;
void dsa_slave_mii_bus_init(struct dsa_switch *ds); void dsa_slave_mii_bus_init(struct dsa_switch *ds);
int dsa_slave_create(struct dsa_switch *ds, struct device *parent, int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
int port, char *name); int port, const char *name);
void dsa_slave_destroy(struct net_device *slave_dev); void dsa_slave_destroy(struct net_device *slave_dev);
int dsa_slave_suspend(struct net_device *slave_dev); int dsa_slave_suspend(struct net_device *slave_dev);
int dsa_slave_resume(struct net_device *slave_dev); int dsa_slave_resume(struct net_device *slave_dev);
......
...@@ -49,8 +49,7 @@ void dsa_slave_mii_bus_init(struct dsa_switch *ds) ...@@ -49,8 +49,7 @@ void dsa_slave_mii_bus_init(struct dsa_switch *ds)
ds->slave_mii_bus->name = "dsa slave smi"; ds->slave_mii_bus->name = "dsa slave smi";
ds->slave_mii_bus->read = dsa_slave_phy_read; ds->slave_mii_bus->read = dsa_slave_phy_read;
ds->slave_mii_bus->write = dsa_slave_phy_write; ds->slave_mii_bus->write = dsa_slave_phy_write;
snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d:%.2x", snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d", ds->index);
ds->index, ds->cd->sw_addr);
ds->slave_mii_bus->parent = ds->dev; ds->slave_mii_bus->parent = ds->dev;
ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask; ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask;
} }
...@@ -522,14 +521,6 @@ static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -522,14 +521,6 @@ static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
static struct sk_buff *dsa_slave_notag_xmit(struct sk_buff *skb,
struct net_device *dev)
{
/* Just return the original SKB */
return skb;
}
/* ethtool operations *******************************************************/ /* ethtool operations *******************************************************/
static int static int
dsa_slave_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) dsa_slave_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
...@@ -615,7 +606,7 @@ static int dsa_slave_get_eeprom_len(struct net_device *dev) ...@@ -615,7 +606,7 @@ static int dsa_slave_get_eeprom_len(struct net_device *dev)
struct dsa_slave_priv *p = netdev_priv(dev); struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_switch *ds = p->parent; struct dsa_switch *ds = p->parent;
if (ds->cd->eeprom_len) if (ds->cd && ds->cd->eeprom_len)
return ds->cd->eeprom_len; return ds->cd->eeprom_len;
if (ds->drv->get_eeprom_len) if (ds->drv->get_eeprom_len)
...@@ -999,13 +990,12 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p, ...@@ -999,13 +990,12 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
struct net_device *slave_dev) struct net_device *slave_dev)
{ {
struct dsa_switch *ds = p->parent; struct dsa_switch *ds = p->parent;
struct dsa_chip_data *cd = ds->cd;
struct device_node *phy_dn, *port_dn; struct device_node *phy_dn, *port_dn;
bool phy_is_fixed = false; bool phy_is_fixed = false;
u32 phy_flags = 0; u32 phy_flags = 0;
int mode, ret; int mode, ret;
port_dn = cd->port_dn[p->port]; port_dn = ds->ports[p->port].dn;
mode = of_get_phy_mode(port_dn); mode = of_get_phy_mode(port_dn);
if (mode < 0) if (mode < 0)
mode = PHY_INTERFACE_MODE_NA; mode = PHY_INTERFACE_MODE_NA;
...@@ -1109,14 +1099,18 @@ int dsa_slave_resume(struct net_device *slave_dev) ...@@ -1109,14 +1099,18 @@ int dsa_slave_resume(struct net_device *slave_dev)
} }
int dsa_slave_create(struct dsa_switch *ds, struct device *parent, int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
int port, char *name) int port, const char *name)
{ {
struct net_device *master = ds->dst->master_netdev;
struct dsa_switch_tree *dst = ds->dst; struct dsa_switch_tree *dst = ds->dst;
struct net_device *master;
struct net_device *slave_dev; struct net_device *slave_dev;
struct dsa_slave_priv *p; struct dsa_slave_priv *p;
int ret; int ret;
master = ds->dst->master_netdev;
if (ds->master_netdev)
master = ds->master_netdev;
slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name, slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name,
NET_NAME_UNKNOWN, ether_setup); NET_NAME_UNKNOWN, ether_setup);
if (slave_dev == NULL) if (slave_dev == NULL)
...@@ -1147,49 +1141,24 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, ...@@ -1147,49 +1141,24 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
NULL); NULL);
SET_NETDEV_DEV(slave_dev, parent); SET_NETDEV_DEV(slave_dev, parent);
slave_dev->dev.of_node = ds->cd->port_dn[port]; slave_dev->dev.of_node = ds->ports[port].dn;
slave_dev->vlan_features = master->vlan_features; slave_dev->vlan_features = master->vlan_features;
p = netdev_priv(slave_dev); p = netdev_priv(slave_dev);
p->parent = ds; p->parent = ds;
p->port = port; p->port = port;
p->xmit = dst->tag_ops->xmit;
switch (ds->dst->tag_protocol) {
#ifdef CONFIG_NET_DSA_TAG_DSA
case DSA_TAG_PROTO_DSA:
p->xmit = dsa_netdev_ops.xmit;
break;
#endif
#ifdef CONFIG_NET_DSA_TAG_EDSA
case DSA_TAG_PROTO_EDSA:
p->xmit = edsa_netdev_ops.xmit;
break;
#endif
#ifdef CONFIG_NET_DSA_TAG_TRAILER
case DSA_TAG_PROTO_TRAILER:
p->xmit = trailer_netdev_ops.xmit;
break;
#endif
#ifdef CONFIG_NET_DSA_TAG_BRCM
case DSA_TAG_PROTO_BRCM:
p->xmit = brcm_netdev_ops.xmit;
break;
#endif
default:
p->xmit = dsa_slave_notag_xmit;
break;
}
p->old_pause = -1; p->old_pause = -1;
p->old_link = -1; p->old_link = -1;
p->old_duplex = -1; p->old_duplex = -1;
ds->ports[port] = slave_dev; ds->ports[port].netdev = slave_dev;
ret = register_netdev(slave_dev); ret = register_netdev(slave_dev);
if (ret) { if (ret) {
netdev_err(master, "error %d registering interface %s\n", netdev_err(master, "error %d registering interface %s\n",
ret, slave_dev->name); ret, slave_dev->name);
ds->ports[port] = NULL; ds->ports[port].netdev = NULL;
free_netdev(slave_dev); free_netdev(slave_dev);
return ret; return ret;
} }
......
...@@ -127,7 +127,7 @@ static int brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -127,7 +127,7 @@ static int brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev,
source_port = brcm_tag[3] & BRCM_EG_PID_MASK; source_port = brcm_tag[3] & BRCM_EG_PID_MASK;
/* Validate port against switch setup, either the port is totally */ /* Validate port against switch setup, either the port is totally */
if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL) if (source_port >= DSA_MAX_PORTS || !ds->ports[source_port].netdev)
goto out_drop; goto out_drop;
/* Remove Broadcom tag and update checksum */ /* Remove Broadcom tag and update checksum */
...@@ -140,7 +140,7 @@ static int brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -140,7 +140,7 @@ static int brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev,
skb_push(skb, ETH_HLEN); skb_push(skb, ETH_HLEN);
skb->pkt_type = PACKET_HOST; skb->pkt_type = PACKET_HOST;
skb->dev = ds->ports[source_port]; skb->dev = ds->ports[source_port].netdev;
skb->protocol = eth_type_trans(skb, skb->dev); skb->protocol = eth_type_trans(skb, skb->dev);
skb->dev->stats.rx_packets++; skb->dev->stats.rx_packets++;
......
...@@ -107,10 +107,14 @@ static int dsa_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -107,10 +107,14 @@ static int dsa_rcv(struct sk_buff *skb, struct net_device *dev,
* Check that the source device exists and that the source * Check that the source device exists and that the source
* port is a registered DSA port. * port is a registered DSA port.
*/ */
if (source_device >= dst->pd->nr_chips) if (source_device >= DSA_MAX_SWITCHES)
goto out_drop; goto out_drop;
ds = dst->ds[source_device]; ds = dst->ds[source_device];
if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL) if (!ds)
goto out_drop;
if (source_port >= DSA_MAX_PORTS || !ds->ports[source_port].netdev)
goto out_drop; goto out_drop;
/* /*
...@@ -159,7 +163,7 @@ static int dsa_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -159,7 +163,7 @@ static int dsa_rcv(struct sk_buff *skb, struct net_device *dev,
2 * ETH_ALEN); 2 * ETH_ALEN);
} }
skb->dev = ds->ports[source_port]; skb->dev = ds->ports[source_port].netdev;
skb_push(skb, ETH_HLEN); skb_push(skb, ETH_HLEN);
skb->pkt_type = PACKET_HOST; skb->pkt_type = PACKET_HOST;
skb->protocol = eth_type_trans(skb, skb->dev); skb->protocol = eth_type_trans(skb, skb->dev);
......
...@@ -120,10 +120,14 @@ static int edsa_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -120,10 +120,14 @@ static int edsa_rcv(struct sk_buff *skb, struct net_device *dev,
* Check that the source device exists and that the source * Check that the source device exists and that the source
* port is a registered DSA port. * port is a registered DSA port.
*/ */
if (source_device >= dst->pd->nr_chips) if (source_device >= DSA_MAX_SWITCHES)
goto out_drop; goto out_drop;
ds = dst->ds[source_device]; ds = dst->ds[source_device];
if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL) if (!ds)
goto out_drop;
if (source_port >= DSA_MAX_PORTS || !ds->ports[source_port].netdev)
goto out_drop; goto out_drop;
/* /*
...@@ -178,7 +182,7 @@ static int edsa_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -178,7 +182,7 @@ static int edsa_rcv(struct sk_buff *skb, struct net_device *dev,
2 * ETH_ALEN); 2 * ETH_ALEN);
} }
skb->dev = ds->ports[source_port]; skb->dev = ds->ports[source_port].netdev;
skb_push(skb, ETH_HLEN); skb_push(skb, ETH_HLEN);
skb->pkt_type = PACKET_HOST; skb->pkt_type = PACKET_HOST;
skb->protocol = eth_type_trans(skb, skb->dev); skb->protocol = eth_type_trans(skb, skb->dev);
......
...@@ -82,12 +82,12 @@ static int trailer_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -82,12 +82,12 @@ static int trailer_rcv(struct sk_buff *skb, struct net_device *dev,
goto out_drop; goto out_drop;
source_port = trailer[1] & 7; source_port = trailer[1] & 7;
if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL) if (source_port >= DSA_MAX_PORTS || !ds->ports[source_port].netdev)
goto out_drop; goto out_drop;
pskb_trim_rcsum(skb, skb->len - 4); pskb_trim_rcsum(skb, skb->len - 4);
skb->dev = ds->ports[source_port]; skb->dev = ds->ports[source_port].netdev;
skb_push(skb, ETH_HLEN); skb_push(skb, ETH_HLEN);
skb->pkt_type = PACKET_HOST; skb->pkt_type = PACKET_HOST;
skb->protocol = eth_type_trans(skb, skb->dev); skb->protocol = eth_type_trans(skb, skb->dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment