mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-28 09:31:14 +00:00
Merge branch 'sf2'
Florian Fainelli says: ==================== dsa: Broadcom Starfighter 2 switch support This patch series adds support for the Broadcom Starfighter 2 (Roboswitch successor) using the existing DSA infrastructure. This integrated switch is heavily used in Set Top Box, Cable gateways and DSL gateways products from Broadcom, and to a larger extent the new ARM-based Wi-Fi routers although slightly differently. Changes in v5 are the introduction of ETH_P_XDSA as suggested by Alexander to help capture applications see this is a multiplexed DSA approach now. Changes in v4 are the introducing of an indirection level for DSA switch tag protocols receive and transmit functions. I intentionnaly did not address one comment from Alexander who suggested to move port_names and port_dn in a separate structure since that involves touching arch/arm/ and arch/blackfin/ code which I am not yet comfortable doing. Notable changes in v3 is the preliminary patch that reworks the skb->protocol override helpers for non-Ethertype switch tags, based on feedback from Alexander Duyck. The biggest changes from v1 of this patch series are: - use the new fixed PHY helpers - improved the switch driver with more complete features (interrupts, (RG)MII configuration, memory arrays power down/up, port disabling/enable VLAN separation Future work will focus on bringing the upstream driver in feature parity with the current downstream driver, including: - adding Wake-on-LAN support to the switch - adding suspend/resume callbacks for S2/S3 Power Management modes - extending the switch register interface to cover BCM5310X SoCs ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
0ce4866f1e
25 changed files with 1772 additions and 112 deletions
|
@ -0,0 +1,39 @@
|
||||||
|
* Broadcom UniMAC MDIO bus controller
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should one from "brcm,genet-mdio-v1", "brcm,genet-mdio-v2",
|
||||||
|
"brcm,genet-mdio-v3", "brcm,genet-mdio-v4" or "brcm,unimac-mdio"
|
||||||
|
- reg: address and length of the regsiter set for the device, first one is the
|
||||||
|
base register, and the second one is optional and for indirect accesses to
|
||||||
|
larger than 16-bits MDIO transactions
|
||||||
|
- reg-names: name(s) of the register must be "mdio" and optional "mdio_indir_rw"
|
||||||
|
- #size-cells: must be 1
|
||||||
|
- #address-cells: must be 0
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- interrupts: must be one if the interrupt is shared with the Ethernet MAC or
|
||||||
|
Ethernet switch this MDIO block is integrated from, or must be two, if there
|
||||||
|
are two separate interrupts, first one must be "mdio done" and second must be
|
||||||
|
for "mdio error"
|
||||||
|
- interrupt-names: must be "mdio_done_error" when there is a share interrupt fed
|
||||||
|
to this hardware block, or must be "mdio_done" for the first interrupt and
|
||||||
|
"mdio_error" for the second when there are separate interrupts
|
||||||
|
|
||||||
|
Child nodes of this MDIO bus controller node are standard Ethernet PHY device
|
||||||
|
nodes as described in Documentation/devicetree/bindings/net/phy.txt
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
mdio@403c0 {
|
||||||
|
compatible = "brcm,unimac-mdio";
|
||||||
|
reg = <0x403c0 0x8 0x40300 0x18>;
|
||||||
|
reg-names = "mdio", "mdio_indir_rw";
|
||||||
|
#size-cells = <1>;
|
||||||
|
#address-cells = <0>;
|
||||||
|
|
||||||
|
...
|
||||||
|
phy@0 {
|
||||||
|
compatible = "ethernet-phy-ieee802.3-c22";
|
||||||
|
reg = <0>;
|
||||||
|
};
|
||||||
|
};
|
78
Documentation/devicetree/bindings/net/broadcom-sf2.txt
Normal file
78
Documentation/devicetree/bindings/net/broadcom-sf2.txt
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
* Broadcom Starfighter 2 integrated swich
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
|
||||||
|
- compatible: should be "brcm,bcm7445-switch-v4.0"
|
||||||
|
- reg: addresses and length of the register sets for the device, must be 6
|
||||||
|
pairs of register addresses and lengths
|
||||||
|
- interrupts: interrupts for the devices, must be two interrupts
|
||||||
|
- dsa,mii-bus: phandle to the MDIO bus controller, see dsa/dsa.txt
|
||||||
|
- dsa,ethernet: phandle to the CPU network interface controller, see dsa/dsa.txt
|
||||||
|
- #size-cells: must be 0
|
||||||
|
- #address-cells: must be 2, see dsa/dsa.txt
|
||||||
|
|
||||||
|
Subnodes:
|
||||||
|
|
||||||
|
The integrated switch subnode should be specified according to the binding
|
||||||
|
described in dsa/dsa.txt.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
|
||||||
|
- reg-names: litteral names for the device base register addresses, when present
|
||||||
|
must be: "core", "reg", "intrl2_0", "intrl2_1", "fcb", "acb"
|
||||||
|
|
||||||
|
- interrupt-names: litternal names for the device interrupt lines, when present
|
||||||
|
must be: "switch_0" and "switch_1"
|
||||||
|
|
||||||
|
- brcm,num-gphy: specify the maximum number of integrated gigabit PHYs in the
|
||||||
|
switch
|
||||||
|
|
||||||
|
- brcm,num-rgmii-ports: specify the maximum number of RGMII interfaces supported
|
||||||
|
by the switch
|
||||||
|
|
||||||
|
- brcm,fcb-pause-override: boolean property, if present indicates that the switch
|
||||||
|
supports Failover Control Block pause override capability
|
||||||
|
|
||||||
|
- brcm,acb-packets-inflight: boolean property, if present indicates that the switch
|
||||||
|
Admission Control Block supports reporting the number of packets in-flight in a
|
||||||
|
switch queue
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
switch_top@f0b00000 {
|
||||||
|
compatible = "simple-bus";
|
||||||
|
#size-cells = <1>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
ranges = <0 0xf0b00000 0x40804>;
|
||||||
|
|
||||||
|
ethernet_switch@0 {
|
||||||
|
compatible = "brcm,bcm7445-switch-v4.0";
|
||||||
|
#size-cells = <0>;
|
||||||
|
#address-cells = <2>;
|
||||||
|
reg = <0x0 0x40000
|
||||||
|
0x40000 0x110
|
||||||
|
0x40340 0x30
|
||||||
|
0x40380 0x30
|
||||||
|
0x40400 0x34
|
||||||
|
0x40600 0x208>;
|
||||||
|
interrupts = <0 0x18 0
|
||||||
|
0 0x19 0>;
|
||||||
|
brcm,num-gphy = <1>;
|
||||||
|
brcm,num-rgmii-ports = <2>;
|
||||||
|
brcm,fcb-pause-override;
|
||||||
|
brcm,acb-packets-inflight;
|
||||||
|
|
||||||
|
...
|
||||||
|
switch@0 {
|
||||||
|
reg = <0 0>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
#address-cells <1>;
|
||||||
|
|
||||||
|
port@0 {
|
||||||
|
label = "gphy";
|
||||||
|
reg = <0>;
|
||||||
|
};
|
||||||
|
...
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -39,6 +39,22 @@ Optionnal property:
|
||||||
This property is only used when switches are being
|
This property is only used when switches are being
|
||||||
chained/cascaded together.
|
chained/cascaded together.
|
||||||
|
|
||||||
|
- phy-handle : Phandle to a PHY on an external MDIO bus, not the
|
||||||
|
switch internal one. See
|
||||||
|
Documentation/devicetree/bindings/net/ethernet.txt
|
||||||
|
for details.
|
||||||
|
|
||||||
|
- phy-mode : String representing the connection to the designated
|
||||||
|
PHY node specified by the 'phy-handle' property. See
|
||||||
|
Documentation/devicetree/bindings/net/ethernet.txt
|
||||||
|
for details.
|
||||||
|
|
||||||
|
Optional subnodes:
|
||||||
|
- 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:
|
Example:
|
||||||
|
|
||||||
dsa@0 {
|
dsa@0 {
|
||||||
|
@ -58,6 +74,7 @@ Example:
|
||||||
port@0 {
|
port@0 {
|
||||||
reg = <0>;
|
reg = <0>;
|
||||||
label = "lan1";
|
label = "lan1";
|
||||||
|
phy-handle = <&phy0>;
|
||||||
};
|
};
|
||||||
|
|
||||||
port@1 {
|
port@1 {
|
||||||
|
|
|
@ -36,4 +36,15 @@ config NET_DSA_MV88E6123_61_65
|
||||||
This enables support for the Marvell 88E6123/6161/6165
|
This enables support for the Marvell 88E6123/6161/6165
|
||||||
ethernet switch chips.
|
ethernet switch chips.
|
||||||
|
|
||||||
|
config NET_DSA_BCM_SF2
|
||||||
|
tristate "Broadcom Starfighter 2 Ethernet switch support"
|
||||||
|
select NET_DSA
|
||||||
|
select NET_DSA_TAG_BRCM
|
||||||
|
select FIXED_PHY if NET_DSA_BCM_SF2=y
|
||||||
|
select BCM7XXX_PHY
|
||||||
|
select MDIO_BCM_UNIMAC
|
||||||
|
---help---
|
||||||
|
This enables support for the Broadcom Starfighter 2 Ethernet
|
||||||
|
switch chips.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
|
@ -7,3 +7,4 @@ endif
|
||||||
ifdef CONFIG_NET_DSA_MV88E6131
|
ifdef CONFIG_NET_DSA_MV88E6131
|
||||||
mv88e6xxx_drv-y += mv88e6131.o
|
mv88e6xxx_drv-y += mv88e6131.o
|
||||||
endif
|
endif
|
||||||
|
obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm_sf2.o
|
||||||
|
|
626
drivers/net/dsa/bcm_sf2.c
Normal file
626
drivers/net/dsa/bcm_sf2.c
Normal file
|
@ -0,0 +1,626 @@
|
||||||
|
/*
|
||||||
|
* Broadcom Starfighter 2 DSA switch driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014, Broadcom Corporation
|
||||||
|
*
|
||||||
|
* 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/list.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/netdevice.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/phy.h>
|
||||||
|
#include <linux/phy_fixed.h>
|
||||||
|
#include <linux/mii.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <net/dsa.h>
|
||||||
|
|
||||||
|
#include "bcm_sf2.h"
|
||||||
|
#include "bcm_sf2_regs.h"
|
||||||
|
|
||||||
|
/* String, offset, and register size in bytes if different from 4 bytes */
|
||||||
|
static const struct bcm_sf2_hw_stats bcm_sf2_mib[] = {
|
||||||
|
{ "TxOctets", 0x000, 8 },
|
||||||
|
{ "TxDropPkts", 0x020 },
|
||||||
|
{ "TxQPKTQ0", 0x030 },
|
||||||
|
{ "TxBroadcastPkts", 0x040 },
|
||||||
|
{ "TxMulticastPkts", 0x050 },
|
||||||
|
{ "TxUnicastPKts", 0x060 },
|
||||||
|
{ "TxCollisions", 0x070 },
|
||||||
|
{ "TxSingleCollision", 0x080 },
|
||||||
|
{ "TxMultipleCollision", 0x090 },
|
||||||
|
{ "TxDeferredCollision", 0x0a0 },
|
||||||
|
{ "TxLateCollision", 0x0b0 },
|
||||||
|
{ "TxExcessiveCollision", 0x0c0 },
|
||||||
|
{ "TxFrameInDisc", 0x0d0 },
|
||||||
|
{ "TxPausePkts", 0x0e0 },
|
||||||
|
{ "TxQPKTQ1", 0x0f0 },
|
||||||
|
{ "TxQPKTQ2", 0x100 },
|
||||||
|
{ "TxQPKTQ3", 0x110 },
|
||||||
|
{ "TxQPKTQ4", 0x120 },
|
||||||
|
{ "TxQPKTQ5", 0x130 },
|
||||||
|
{ "RxOctets", 0x140, 8 },
|
||||||
|
{ "RxUndersizePkts", 0x160 },
|
||||||
|
{ "RxPausePkts", 0x170 },
|
||||||
|
{ "RxPkts64Octets", 0x180 },
|
||||||
|
{ "RxPkts65to127Octets", 0x190 },
|
||||||
|
{ "RxPkts128to255Octets", 0x1a0 },
|
||||||
|
{ "RxPkts256to511Octets", 0x1b0 },
|
||||||
|
{ "RxPkts512to1023Octets", 0x1c0 },
|
||||||
|
{ "RxPkts1024toMaxPktsOctets", 0x1d0 },
|
||||||
|
{ "RxOversizePkts", 0x1e0 },
|
||||||
|
{ "RxJabbers", 0x1f0 },
|
||||||
|
{ "RxAlignmentErrors", 0x200 },
|
||||||
|
{ "RxFCSErrors", 0x210 },
|
||||||
|
{ "RxGoodOctets", 0x220, 8 },
|
||||||
|
{ "RxDropPkts", 0x240 },
|
||||||
|
{ "RxUnicastPkts", 0x250 },
|
||||||
|
{ "RxMulticastPkts", 0x260 },
|
||||||
|
{ "RxBroadcastPkts", 0x270 },
|
||||||
|
{ "RxSAChanges", 0x280 },
|
||||||
|
{ "RxFragments", 0x290 },
|
||||||
|
{ "RxJumboPkt", 0x2a0 },
|
||||||
|
{ "RxSymblErr", 0x2b0 },
|
||||||
|
{ "InRangeErrCount", 0x2c0 },
|
||||||
|
{ "OutRangeErrCount", 0x2d0 },
|
||||||
|
{ "EEELpiEvent", 0x2e0 },
|
||||||
|
{ "EEELpiDuration", 0x2f0 },
|
||||||
|
{ "RxDiscard", 0x300, 8 },
|
||||||
|
{ "TxQPKTQ6", 0x320 },
|
||||||
|
{ "TxQPKTQ7", 0x330 },
|
||||||
|
{ "TxPkts64Octets", 0x340 },
|
||||||
|
{ "TxPkts65to127Octets", 0x350 },
|
||||||
|
{ "TxPkts128to255Octets", 0x360 },
|
||||||
|
{ "TxPkts256to511Ocets", 0x370 },
|
||||||
|
{ "TxPkts512to1023Ocets", 0x380 },
|
||||||
|
{ "TxPkts1024toMaxPktOcets", 0x390 },
|
||||||
|
};
|
||||||
|
|
||||||
|
#define BCM_SF2_STATS_SIZE ARRAY_SIZE(bcm_sf2_mib)
|
||||||
|
|
||||||
|
static void bcm_sf2_sw_get_strings(struct dsa_switch *ds,
|
||||||
|
int port, uint8_t *data)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < BCM_SF2_STATS_SIZE; i++)
|
||||||
|
memcpy(data + i * ETH_GSTRING_LEN,
|
||||||
|
bcm_sf2_mib[i].string, ETH_GSTRING_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm_sf2_sw_get_ethtool_stats(struct dsa_switch *ds,
|
||||||
|
int port, uint64_t *data)
|
||||||
|
{
|
||||||
|
struct bcm_sf2_priv *priv = ds_to_priv(ds);
|
||||||
|
const struct bcm_sf2_hw_stats *s;
|
||||||
|
unsigned int i;
|
||||||
|
u64 val = 0;
|
||||||
|
u32 offset;
|
||||||
|
|
||||||
|
mutex_lock(&priv->stats_mutex);
|
||||||
|
|
||||||
|
/* Now fetch the per-port counters */
|
||||||
|
for (i = 0; i < BCM_SF2_STATS_SIZE; i++) {
|
||||||
|
s = &bcm_sf2_mib[i];
|
||||||
|
|
||||||
|
/* Do a latched 64-bit read if needed */
|
||||||
|
offset = s->reg + CORE_P_MIB_OFFSET(port);
|
||||||
|
if (s->sizeof_stat == 8)
|
||||||
|
val = core_readq(priv, offset);
|
||||||
|
else
|
||||||
|
val = core_readl(priv, offset);
|
||||||
|
|
||||||
|
data[i] = (u64)val;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&priv->stats_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sf2_sw_get_sset_count(struct dsa_switch *ds)
|
||||||
|
{
|
||||||
|
return BCM_SF2_STATS_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *bcm_sf2_sw_probe(struct mii_bus *bus, int sw_addr)
|
||||||
|
{
|
||||||
|
return "Broadcom Starfighter 2";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port)
|
||||||
|
{
|
||||||
|
struct bcm_sf2_priv *priv = ds_to_priv(ds);
|
||||||
|
unsigned int i;
|
||||||
|
u32 reg, val;
|
||||||
|
|
||||||
|
/* Enable the port memories */
|
||||||
|
reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL);
|
||||||
|
reg &= ~P_TXQ_PSM_VDD(port);
|
||||||
|
core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL);
|
||||||
|
|
||||||
|
/* Enable Broadcast, Multicast, Unicast forwarding to IMP port */
|
||||||
|
reg = core_readl(priv, CORE_IMP_CTL);
|
||||||
|
reg |= (RX_BCST_EN | RX_MCST_EN | RX_UCST_EN);
|
||||||
|
reg &= ~(RX_DIS | TX_DIS);
|
||||||
|
core_writel(priv, reg, CORE_IMP_CTL);
|
||||||
|
|
||||||
|
/* Enable forwarding */
|
||||||
|
core_writel(priv, SW_FWDG_EN, CORE_SWMODE);
|
||||||
|
|
||||||
|
/* Enable IMP port in dumb mode */
|
||||||
|
reg = core_readl(priv, CORE_SWITCH_CTRL);
|
||||||
|
reg |= MII_DUMB_FWDG_EN;
|
||||||
|
core_writel(priv, reg, CORE_SWITCH_CTRL);
|
||||||
|
|
||||||
|
/* Resolve which bit controls the Broadcom tag */
|
||||||
|
switch (port) {
|
||||||
|
case 8:
|
||||||
|
val = BRCM_HDR_EN_P8;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
val = BRCM_HDR_EN_P7;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
val = BRCM_HDR_EN_P5;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
val = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable Broadcom tags for IMP port */
|
||||||
|
reg = core_readl(priv, CORE_BRCM_HDR_CTRL);
|
||||||
|
reg |= val;
|
||||||
|
core_writel(priv, reg, CORE_BRCM_HDR_CTRL);
|
||||||
|
|
||||||
|
/* Enable reception Broadcom tag for CPU TX (switch RX) to
|
||||||
|
* allow us to tag outgoing frames
|
||||||
|
*/
|
||||||
|
reg = core_readl(priv, CORE_BRCM_HDR_RX_DIS);
|
||||||
|
reg &= ~(1 << port);
|
||||||
|
core_writel(priv, reg, CORE_BRCM_HDR_RX_DIS);
|
||||||
|
|
||||||
|
/* Enable transmission of Broadcom tags from the switch (CPU RX) to
|
||||||
|
* allow delivering frames to the per-port net_devices
|
||||||
|
*/
|
||||||
|
reg = core_readl(priv, CORE_BRCM_HDR_TX_DIS);
|
||||||
|
reg &= ~(1 << port);
|
||||||
|
core_writel(priv, reg, CORE_BRCM_HDR_TX_DIS);
|
||||||
|
|
||||||
|
/* Force link status for IMP port */
|
||||||
|
reg = core_readl(priv, CORE_STS_OVERRIDE_IMP);
|
||||||
|
reg |= (MII_SW_OR | LINK_STS);
|
||||||
|
core_writel(priv, reg, CORE_STS_OVERRIDE_IMP);
|
||||||
|
|
||||||
|
/* Enable the IMP Port to be in the same VLAN as the other ports
|
||||||
|
* on a per-port basis such that we only have Port i and IMP in
|
||||||
|
* the same VLAN.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < priv->hw_params.num_ports; i++) {
|
||||||
|
if (!((1 << i) & ds->phys_port_mask))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i));
|
||||||
|
reg |= (1 << port);
|
||||||
|
core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm_sf2_port_setup(struct dsa_switch *ds, int port)
|
||||||
|
{
|
||||||
|
struct bcm_sf2_priv *priv = ds_to_priv(ds);
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
/* Clear the memory power down */
|
||||||
|
reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL);
|
||||||
|
reg &= ~P_TXQ_PSM_VDD(port);
|
||||||
|
core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL);
|
||||||
|
|
||||||
|
/* Clear the Rx and Tx disable bits and set to no spanning tree */
|
||||||
|
core_writel(priv, 0, CORE_G_PCTL_PORT(port));
|
||||||
|
|
||||||
|
/* Enable port 7 interrupts to get notified */
|
||||||
|
if (port == 7)
|
||||||
|
intrl2_1_mask_clear(priv, P_IRQ_MASK(P7_IRQ_OFF));
|
||||||
|
|
||||||
|
/* Set this port, and only this one to be in the default VLAN */
|
||||||
|
reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port));
|
||||||
|
reg &= ~PORT_VLAN_CTRL_MASK;
|
||||||
|
reg |= (1 << port);
|
||||||
|
core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm_sf2_port_disable(struct dsa_switch *ds, int port)
|
||||||
|
{
|
||||||
|
struct bcm_sf2_priv *priv = ds_to_priv(ds);
|
||||||
|
u32 off, reg;
|
||||||
|
|
||||||
|
if (dsa_is_cpu_port(ds, port))
|
||||||
|
off = CORE_IMP_CTL;
|
||||||
|
else
|
||||||
|
off = CORE_G_PCTL_PORT(port);
|
||||||
|
|
||||||
|
reg = core_readl(priv, off);
|
||||||
|
reg |= RX_DIS | TX_DIS;
|
||||||
|
core_writel(priv, reg, off);
|
||||||
|
|
||||||
|
/* Power down the port memory */
|
||||||
|
reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL);
|
||||||
|
reg |= P_TXQ_PSM_VDD(port);
|
||||||
|
core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct bcm_sf2_priv *priv = dev_id;
|
||||||
|
|
||||||
|
priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) &
|
||||||
|
~priv->irq0_mask;
|
||||||
|
intrl2_0_writel(priv, priv->irq0_stat, INTRL2_CPU_CLEAR);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t bcm_sf2_switch_1_isr(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct bcm_sf2_priv *priv = dev_id;
|
||||||
|
|
||||||
|
priv->irq1_stat = intrl2_1_readl(priv, INTRL2_CPU_STATUS) &
|
||||||
|
~priv->irq1_mask;
|
||||||
|
intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR);
|
||||||
|
|
||||||
|
if (priv->irq1_stat & P_LINK_UP_IRQ(P7_IRQ_OFF))
|
||||||
|
priv->port_sts[7].link = 1;
|
||||||
|
if (priv->irq1_stat & P_LINK_DOWN_IRQ(P7_IRQ_OFF))
|
||||||
|
priv->port_sts[7].link = 0;
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sf2_sw_setup(struct dsa_switch *ds)
|
||||||
|
{
|
||||||
|
const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME;
|
||||||
|
struct bcm_sf2_priv *priv = ds_to_priv(ds);
|
||||||
|
struct device_node *dn;
|
||||||
|
void __iomem **base;
|
||||||
|
unsigned int port;
|
||||||
|
unsigned int i;
|
||||||
|
u32 reg, rev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
spin_lock_init(&priv->indir_lock);
|
||||||
|
mutex_init(&priv->stats_mutex);
|
||||||
|
|
||||||
|
/* All the interesting properties are at the parent device_node
|
||||||
|
* level
|
||||||
|
*/
|
||||||
|
dn = ds->pd->of_node->parent;
|
||||||
|
|
||||||
|
priv->irq0 = irq_of_parse_and_map(dn, 0);
|
||||||
|
priv->irq1 = irq_of_parse_and_map(dn, 1);
|
||||||
|
|
||||||
|
base = &priv->core;
|
||||||
|
for (i = 0; i < BCM_SF2_REGS_NUM; i++) {
|
||||||
|
*base = of_iomap(dn, i);
|
||||||
|
if (*base == NULL) {
|
||||||
|
pr_err("unable to find register: %s\n", reg_names[i]);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
base++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable all interrupts and request them */
|
||||||
|
intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET);
|
||||||
|
intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
|
||||||
|
intrl2_0_writel(priv, 0, INTRL2_CPU_MASK_CLEAR);
|
||||||
|
intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET);
|
||||||
|
intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
|
||||||
|
intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR);
|
||||||
|
|
||||||
|
ret = request_irq(priv->irq0, bcm_sf2_switch_0_isr, 0,
|
||||||
|
"switch_0", priv);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("failed to request switch_0 IRQ\n");
|
||||||
|
goto out_unmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = request_irq(priv->irq1, bcm_sf2_switch_1_isr, 0,
|
||||||
|
"switch_1", priv);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("failed to request switch_1 IRQ\n");
|
||||||
|
goto out_free_irq0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset the MIB counters */
|
||||||
|
reg = core_readl(priv, CORE_GMNCFGCFG);
|
||||||
|
reg |= RST_MIB_CNT;
|
||||||
|
core_writel(priv, reg, CORE_GMNCFGCFG);
|
||||||
|
reg &= ~RST_MIB_CNT;
|
||||||
|
core_writel(priv, reg, CORE_GMNCFGCFG);
|
||||||
|
|
||||||
|
/* Get the maximum number of ports for this switch */
|
||||||
|
priv->hw_params.num_ports = core_readl(priv, CORE_IMP0_PRT_ID) + 1;
|
||||||
|
if (priv->hw_params.num_ports > DSA_MAX_PORTS)
|
||||||
|
priv->hw_params.num_ports = DSA_MAX_PORTS;
|
||||||
|
|
||||||
|
/* Assume a single GPHY setup if we can't read that property */
|
||||||
|
if (of_property_read_u32(dn, "brcm,num-gphy",
|
||||||
|
&priv->hw_params.num_gphy))
|
||||||
|
priv->hw_params.num_gphy = 1;
|
||||||
|
|
||||||
|
/* Enable all valid ports and disable those unused */
|
||||||
|
for (port = 0; port < priv->hw_params.num_ports; port++) {
|
||||||
|
/* IMP port receives special treatment */
|
||||||
|
if ((1 << port) & ds->phys_port_mask)
|
||||||
|
bcm_sf2_port_setup(ds, port);
|
||||||
|
else if (dsa_is_cpu_port(ds, port))
|
||||||
|
bcm_sf2_imp_setup(ds, port);
|
||||||
|
else
|
||||||
|
bcm_sf2_port_disable(ds, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Include the pseudo-PHY address and the broadcast PHY address to
|
||||||
|
* divert reads towards our workaround
|
||||||
|
*/
|
||||||
|
ds->phys_mii_mask |= ((1 << 30) | (1 << 0));
|
||||||
|
|
||||||
|
rev = reg_readl(priv, REG_SWITCH_REVISION);
|
||||||
|
priv->hw_params.top_rev = (rev >> SWITCH_TOP_REV_SHIFT) &
|
||||||
|
SWITCH_TOP_REV_MASK;
|
||||||
|
priv->hw_params.core_rev = (rev & SF2_REV_MASK);
|
||||||
|
|
||||||
|
pr_info("Starfighter 2 top: %x.%02x, core: %x.%02x base: 0x%p, IRQs: %d, %d\n",
|
||||||
|
priv->hw_params.top_rev >> 8, priv->hw_params.top_rev & 0xff,
|
||||||
|
priv->hw_params.core_rev >> 8, priv->hw_params.core_rev & 0xff,
|
||||||
|
priv->core, priv->irq0, priv->irq1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_free_irq0:
|
||||||
|
free_irq(priv->irq0, priv);
|
||||||
|
out_unmap:
|
||||||
|
base = &priv->core;
|
||||||
|
for (i = 0; i < BCM_SF2_REGS_NUM; i++) {
|
||||||
|
iounmap(*base);
|
||||||
|
base++;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sf2_sw_set_addr(struct dsa_switch *ds, u8 *addr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sf2_sw_indir_rw(struct dsa_switch *ds, int op, int addr,
|
||||||
|
int regnum, u16 val)
|
||||||
|
{
|
||||||
|
struct bcm_sf2_priv *priv = ds_to_priv(ds);
|
||||||
|
int ret = 0;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = reg_readl(priv, REG_SWITCH_CNTRL);
|
||||||
|
reg |= MDIO_MASTER_SEL;
|
||||||
|
reg_writel(priv, reg, REG_SWITCH_CNTRL);
|
||||||
|
|
||||||
|
/* Page << 8 | offset */
|
||||||
|
reg = 0x70;
|
||||||
|
reg <<= 2;
|
||||||
|
core_writel(priv, addr, reg);
|
||||||
|
|
||||||
|
/* Page << 8 | offset */
|
||||||
|
reg = 0x80 << 8 | regnum << 1;
|
||||||
|
reg <<= 2;
|
||||||
|
|
||||||
|
if (op)
|
||||||
|
ret = core_readl(priv, reg);
|
||||||
|
else
|
||||||
|
core_writel(priv, val, reg);
|
||||||
|
|
||||||
|
reg = reg_readl(priv, REG_SWITCH_CNTRL);
|
||||||
|
reg &= ~MDIO_MASTER_SEL;
|
||||||
|
reg_writel(priv, reg, REG_SWITCH_CNTRL);
|
||||||
|
|
||||||
|
return ret & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sf2_sw_phy_read(struct dsa_switch *ds, int addr, int regnum)
|
||||||
|
{
|
||||||
|
/* Intercept reads from the MDIO broadcast address or Broadcom
|
||||||
|
* pseudo-PHY address
|
||||||
|
*/
|
||||||
|
switch (addr) {
|
||||||
|
case 0:
|
||||||
|
case 30:
|
||||||
|
return bcm_sf2_sw_indir_rw(ds, 1, addr, regnum, 0);
|
||||||
|
default:
|
||||||
|
return 0xffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_sf2_sw_phy_write(struct dsa_switch *ds, int addr, int regnum,
|
||||||
|
u16 val)
|
||||||
|
{
|
||||||
|
/* Intercept writes to the MDIO broadcast address or Broadcom
|
||||||
|
* pseudo-PHY address
|
||||||
|
*/
|
||||||
|
switch (addr) {
|
||||||
|
case 0:
|
||||||
|
case 30:
|
||||||
|
bcm_sf2_sw_indir_rw(ds, 0, addr, regnum, val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port,
|
||||||
|
struct phy_device *phydev)
|
||||||
|
{
|
||||||
|
struct bcm_sf2_priv *priv = ds_to_priv(ds);
|
||||||
|
u32 id_mode_dis = 0, port_mode;
|
||||||
|
const char *str = NULL;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
switch (phydev->interface) {
|
||||||
|
case PHY_INTERFACE_MODE_RGMII:
|
||||||
|
str = "RGMII (no delay)";
|
||||||
|
id_mode_dis = 1;
|
||||||
|
case PHY_INTERFACE_MODE_RGMII_TXID:
|
||||||
|
if (!str)
|
||||||
|
str = "RGMII (TX delay)";
|
||||||
|
port_mode = EXT_GPHY;
|
||||||
|
break;
|
||||||
|
case PHY_INTERFACE_MODE_MII:
|
||||||
|
str = "MII";
|
||||||
|
port_mode = EXT_EPHY;
|
||||||
|
break;
|
||||||
|
case PHY_INTERFACE_MODE_REVMII:
|
||||||
|
str = "Reverse MII";
|
||||||
|
port_mode = EXT_REVMII;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto force_link;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear id_mode_dis bit, and the existing port mode, but
|
||||||
|
* make sure we enable the RGMII block for data to pass
|
||||||
|
*/
|
||||||
|
reg = reg_readl(priv, REG_RGMII_CNTRL_P(port));
|
||||||
|
reg &= ~ID_MODE_DIS;
|
||||||
|
reg &= ~(PORT_MODE_MASK << PORT_MODE_SHIFT);
|
||||||
|
reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN);
|
||||||
|
|
||||||
|
reg |= port_mode | RGMII_MODE_EN;
|
||||||
|
if (id_mode_dis)
|
||||||
|
reg |= ID_MODE_DIS;
|
||||||
|
|
||||||
|
if (phydev->pause) {
|
||||||
|
if (phydev->asym_pause)
|
||||||
|
reg |= TX_PAUSE_EN;
|
||||||
|
reg |= RX_PAUSE_EN;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_writel(priv, reg, REG_RGMII_CNTRL_P(port));
|
||||||
|
|
||||||
|
pr_info("Port %d configured for %s\n", port, str);
|
||||||
|
|
||||||
|
force_link:
|
||||||
|
/* Force link settings detected from the PHY */
|
||||||
|
reg = SW_OVERRIDE;
|
||||||
|
switch (phydev->speed) {
|
||||||
|
case SPEED_1000:
|
||||||
|
reg |= SPDSTS_1000 << SPEED_SHIFT;
|
||||||
|
break;
|
||||||
|
case SPEED_100:
|
||||||
|
reg |= SPDSTS_100 << SPEED_SHIFT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phydev->link)
|
||||||
|
reg |= LINK_STS;
|
||||||
|
if (phydev->duplex == DUPLEX_FULL)
|
||||||
|
reg |= DUPLX_MODE;
|
||||||
|
|
||||||
|
core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
|
||||||
|
struct fixed_phy_status *status)
|
||||||
|
{
|
||||||
|
struct bcm_sf2_priv *priv = ds_to_priv(ds);
|
||||||
|
u32 link, duplex, pause, speed;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
link = core_readl(priv, CORE_LNKSTS);
|
||||||
|
duplex = core_readl(priv, CORE_DUPSTS);
|
||||||
|
pause = core_readl(priv, CORE_PAUSESTS);
|
||||||
|
speed = core_readl(priv, CORE_SPDSTS);
|
||||||
|
|
||||||
|
speed >>= (port * SPDSTS_SHIFT);
|
||||||
|
speed &= SPDSTS_MASK;
|
||||||
|
|
||||||
|
status->link = 0;
|
||||||
|
|
||||||
|
/* Port 7 is special as we do not get link status from CORE_LNKSTS,
|
||||||
|
* which means that we need to force the link at the port override
|
||||||
|
* level to get the data to flow. We do use what the interrupt handler
|
||||||
|
* did determine before.
|
||||||
|
*/
|
||||||
|
if (port == 7) {
|
||||||
|
status->link = priv->port_sts[port].link;
|
||||||
|
reg = core_readl(priv, CORE_STS_OVERRIDE_GMIIP_PORT(7));
|
||||||
|
reg |= SW_OVERRIDE;
|
||||||
|
if (status->link)
|
||||||
|
reg |= LINK_STS;
|
||||||
|
else
|
||||||
|
reg &= ~LINK_STS;
|
||||||
|
core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(7));
|
||||||
|
status->duplex = 1;
|
||||||
|
} else {
|
||||||
|
status->link = !!(link & (1 << port));
|
||||||
|
status->duplex = !!(duplex & (1 << port));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (speed) {
|
||||||
|
case SPDSTS_10:
|
||||||
|
status->speed = SPEED_10;
|
||||||
|
break;
|
||||||
|
case SPDSTS_100:
|
||||||
|
status->speed = SPEED_100;
|
||||||
|
break;
|
||||||
|
case SPDSTS_1000:
|
||||||
|
status->speed = SPEED_1000;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((pause & (1 << port)) &&
|
||||||
|
(pause & (1 << (port + PAUSESTS_TX_PAUSE_SHIFT)))) {
|
||||||
|
status->asym_pause = 1;
|
||||||
|
status->pause = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pause & (1 << port))
|
||||||
|
status->pause = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dsa_switch_driver bcm_sf2_switch_driver = {
|
||||||
|
.tag_protocol = htons(ETH_P_BRCMTAG),
|
||||||
|
.priv_size = sizeof(struct bcm_sf2_priv),
|
||||||
|
.probe = bcm_sf2_sw_probe,
|
||||||
|
.setup = bcm_sf2_sw_setup,
|
||||||
|
.set_addr = bcm_sf2_sw_set_addr,
|
||||||
|
.phy_read = bcm_sf2_sw_phy_read,
|
||||||
|
.phy_write = bcm_sf2_sw_phy_write,
|
||||||
|
.get_strings = bcm_sf2_sw_get_strings,
|
||||||
|
.get_ethtool_stats = bcm_sf2_sw_get_ethtool_stats,
|
||||||
|
.get_sset_count = bcm_sf2_sw_get_sset_count,
|
||||||
|
.adjust_link = bcm_sf2_sw_adjust_link,
|
||||||
|
.fixed_link_update = bcm_sf2_sw_fixed_link_update,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init bcm_sf2_init(void)
|
||||||
|
{
|
||||||
|
register_switch_driver(&bcm_sf2_switch_driver);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
module_init(bcm_sf2_init);
|
||||||
|
|
||||||
|
static void __exit bcm_sf2_exit(void)
|
||||||
|
{
|
||||||
|
unregister_switch_driver(&bcm_sf2_switch_driver);
|
||||||
|
}
|
||||||
|
module_exit(bcm_sf2_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Broadcom Corporation");
|
||||||
|
MODULE_DESCRIPTION("Driver for Broadcom Starfighter 2 ethernet switch chip");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("platform:brcm-sf2");
|
140
drivers/net/dsa/bcm_sf2.h
Normal file
140
drivers/net/dsa/bcm_sf2.h
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
/*
|
||||||
|
* Broadcom Starfighter2 private context
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014, Broadcom Corporation
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __BCM_SF2_H
|
||||||
|
#define __BCM_SF2_H
|
||||||
|
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/mii.h>
|
||||||
|
|
||||||
|
#include <net/dsa.h>
|
||||||
|
|
||||||
|
#include "bcm_sf2_regs.h"
|
||||||
|
|
||||||
|
struct bcm_sf2_hw_params {
|
||||||
|
u16 top_rev;
|
||||||
|
u16 core_rev;
|
||||||
|
u32 num_gphy;
|
||||||
|
u8 num_acb_queue;
|
||||||
|
u8 num_rgmii;
|
||||||
|
u8 num_ports;
|
||||||
|
u8 fcb_pause_override:1;
|
||||||
|
u8 acb_packets_inflight:1;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define BCM_SF2_REGS_NAME {\
|
||||||
|
"core", "reg", "intrl2_0", "intrl2_1", "fcb", "acb" \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BCM_SF2_REGS_NUM 6
|
||||||
|
|
||||||
|
struct bcm_sf2_port_status {
|
||||||
|
unsigned int link;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bcm_sf2_priv {
|
||||||
|
/* Base registers, keep those in order with BCM_SF2_REGS_NAME */
|
||||||
|
void __iomem *core;
|
||||||
|
void __iomem *reg;
|
||||||
|
void __iomem *intrl2_0;
|
||||||
|
void __iomem *intrl2_1;
|
||||||
|
void __iomem *fcb;
|
||||||
|
void __iomem *acb;
|
||||||
|
|
||||||
|
/* spinlock protecting access to the indirect registers */
|
||||||
|
spinlock_t indir_lock;
|
||||||
|
|
||||||
|
int irq0;
|
||||||
|
int irq1;
|
||||||
|
u32 irq0_stat;
|
||||||
|
u32 irq0_mask;
|
||||||
|
u32 irq1_stat;
|
||||||
|
u32 irq1_mask;
|
||||||
|
|
||||||
|
/* Mutex protecting access to the MIB counters */
|
||||||
|
struct mutex stats_mutex;
|
||||||
|
|
||||||
|
struct bcm_sf2_hw_params hw_params;
|
||||||
|
|
||||||
|
struct bcm_sf2_port_status port_sts[DSA_MAX_PORTS];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bcm_sf2_hw_stats {
|
||||||
|
const char *string;
|
||||||
|
u16 reg;
|
||||||
|
u8 sizeof_stat;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SF2_IO_MACRO(name) \
|
||||||
|
static inline u32 name##_readl(struct bcm_sf2_priv *priv, u32 off) \
|
||||||
|
{ \
|
||||||
|
return __raw_readl(priv->name + off); \
|
||||||
|
} \
|
||||||
|
static inline void name##_writel(struct bcm_sf2_priv *priv, \
|
||||||
|
u32 val, u32 off) \
|
||||||
|
{ \
|
||||||
|
__raw_writel(val, priv->name + off); \
|
||||||
|
} \
|
||||||
|
|
||||||
|
/* Accesses to 64-bits register requires us to latch the hi/lo pairs
|
||||||
|
* using the REG_DIR_DATA_{READ,WRITE} ancillary registers. The 'indir_lock'
|
||||||
|
* spinlock is automatically grabbed and released to provide relative
|
||||||
|
* atomiticy with latched reads/writes.
|
||||||
|
*/
|
||||||
|
#define SF2_IO64_MACRO(name) \
|
||||||
|
static inline u64 name##_readq(struct bcm_sf2_priv *priv, u32 off) \
|
||||||
|
{ \
|
||||||
|
u32 indir, dir; \
|
||||||
|
spin_lock(&priv->indir_lock); \
|
||||||
|
indir = reg_readl(priv, REG_DIR_DATA_READ); \
|
||||||
|
dir = __raw_readl(priv->name + off); \
|
||||||
|
spin_unlock(&priv->indir_lock); \
|
||||||
|
return (u64)indir << 32 | dir; \
|
||||||
|
} \
|
||||||
|
static inline void name##_writeq(struct bcm_sf2_priv *priv, u32 off, \
|
||||||
|
u64 val) \
|
||||||
|
{ \
|
||||||
|
spin_lock(&priv->indir_lock); \
|
||||||
|
reg_writel(priv, upper_32_bits(val), REG_DIR_DATA_WRITE); \
|
||||||
|
__raw_writel(lower_32_bits(val), priv->name + off); \
|
||||||
|
spin_unlock(&priv->indir_lock); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SWITCH_INTR_L2(which) \
|
||||||
|
static inline void intrl2_##which##_mask_clear(struct bcm_sf2_priv *priv, \
|
||||||
|
u32 mask) \
|
||||||
|
{ \
|
||||||
|
intrl2_##which##_writel(priv, mask, INTRL2_CPU_MASK_CLEAR); \
|
||||||
|
priv->irq##which##_mask &= ~(mask); \
|
||||||
|
} \
|
||||||
|
static inline void intrl2_##which##_mask_set(struct bcm_sf2_priv *priv, \
|
||||||
|
u32 mask) \
|
||||||
|
{ \
|
||||||
|
intrl2_## which##_writel(priv, mask, INTRL2_CPU_MASK_SET); \
|
||||||
|
priv->irq##which##_mask |= (mask); \
|
||||||
|
} \
|
||||||
|
|
||||||
|
SF2_IO_MACRO(core);
|
||||||
|
SF2_IO_MACRO(reg);
|
||||||
|
SF2_IO64_MACRO(core);
|
||||||
|
SF2_IO_MACRO(intrl2_0);
|
||||||
|
SF2_IO_MACRO(intrl2_1);
|
||||||
|
SF2_IO_MACRO(fcb);
|
||||||
|
SF2_IO_MACRO(acb);
|
||||||
|
|
||||||
|
SWITCH_INTR_L2(0);
|
||||||
|
SWITCH_INTR_L2(1);
|
||||||
|
|
||||||
|
#endif /* __BCM_SF2_H */
|
227
drivers/net/dsa/bcm_sf2_regs.h
Normal file
227
drivers/net/dsa/bcm_sf2_regs.h
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
/*
|
||||||
|
* Broadcom Starfighter 2 switch register defines
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014, Broadcom Corporation
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#ifndef __BCM_SF2_REGS_H
|
||||||
|
#define __BCM_SF2_REGS_H
|
||||||
|
|
||||||
|
/* Register set relative to 'REG' */
|
||||||
|
#define REG_SWITCH_CNTRL 0x00
|
||||||
|
#define MDIO_MASTER_SEL (1 << 0)
|
||||||
|
|
||||||
|
#define REG_SWITCH_STATUS 0x04
|
||||||
|
#define REG_DIR_DATA_WRITE 0x08
|
||||||
|
#define REG_DIR_DATA_READ 0x0C
|
||||||
|
|
||||||
|
#define REG_SWITCH_REVISION 0x18
|
||||||
|
#define SF2_REV_MASK 0xffff
|
||||||
|
#define SWITCH_TOP_REV_SHIFT 16
|
||||||
|
#define SWITCH_TOP_REV_MASK 0xffff
|
||||||
|
|
||||||
|
#define REG_PHY_REVISION 0x1C
|
||||||
|
|
||||||
|
#define REG_SPHY_CNTRL 0x2C
|
||||||
|
#define IDDQ_BIAS (1 << 0)
|
||||||
|
#define EXT_PWR_DOWN (1 << 1)
|
||||||
|
#define FORCE_DLL_EN (1 << 2)
|
||||||
|
#define IDDQ_GLOBAL_PWR (1 << 3)
|
||||||
|
#define CK25_DIS (1 << 4)
|
||||||
|
#define PHY_RESET (1 << 5)
|
||||||
|
#define PHY_PHYAD_SHIFT 8
|
||||||
|
#define PHY_PHYAD_MASK 0x1F
|
||||||
|
|
||||||
|
#define REG_RGMII_0_BASE 0x34
|
||||||
|
#define REG_RGMII_CNTRL 0x00
|
||||||
|
#define REG_RGMII_IB_STATUS 0x04
|
||||||
|
#define REG_RGMII_RX_CLOCK_DELAY_CNTRL 0x08
|
||||||
|
#define REG_RGMII_CNTRL_SIZE 0x0C
|
||||||
|
#define REG_RGMII_CNTRL_P(x) (REG_RGMII_0_BASE + \
|
||||||
|
((x) * REG_RGMII_CNTRL_SIZE))
|
||||||
|
/* Relative to REG_RGMII_CNTRL */
|
||||||
|
#define RGMII_MODE_EN (1 << 0)
|
||||||
|
#define ID_MODE_DIS (1 << 1)
|
||||||
|
#define PORT_MODE_SHIFT 2
|
||||||
|
#define INT_EPHY (0 << PORT_MODE_SHIFT)
|
||||||
|
#define INT_GPHY (1 << PORT_MODE_SHIFT)
|
||||||
|
#define EXT_EPHY (2 << PORT_MODE_SHIFT)
|
||||||
|
#define EXT_GPHY (3 << PORT_MODE_SHIFT)
|
||||||
|
#define EXT_REVMII (4 << PORT_MODE_SHIFT)
|
||||||
|
#define PORT_MODE_MASK 0x7
|
||||||
|
#define RVMII_REF_SEL (1 << 5)
|
||||||
|
#define RX_PAUSE_EN (1 << 6)
|
||||||
|
#define TX_PAUSE_EN (1 << 7)
|
||||||
|
#define TX_CLK_STOP_EN (1 << 8)
|
||||||
|
#define LPI_COUNT_SHIFT 9
|
||||||
|
#define LPI_COUNT_MASK 0x3F
|
||||||
|
|
||||||
|
/* Register set relative to 'INTRL2_0' and 'INTRL2_1' */
|
||||||
|
#define INTRL2_CPU_STATUS 0x00
|
||||||
|
#define INTRL2_CPU_SET 0x04
|
||||||
|
#define INTRL2_CPU_CLEAR 0x08
|
||||||
|
#define INTRL2_CPU_MASK_STATUS 0x0c
|
||||||
|
#define INTRL2_CPU_MASK_SET 0x10
|
||||||
|
#define INTRL2_CPU_MASK_CLEAR 0x14
|
||||||
|
|
||||||
|
/* Shared INTRL2_0 and INTRL2_ interrupt sources macros */
|
||||||
|
#define P_LINK_UP_IRQ(x) (1 << (0 + (x)))
|
||||||
|
#define P_LINK_DOWN_IRQ(x) (1 << (1 + (x)))
|
||||||
|
#define P_ENERGY_ON_IRQ(x) (1 << (2 + (x)))
|
||||||
|
#define P_ENERGY_OFF_IRQ(x) (1 << (3 + (x)))
|
||||||
|
#define P_GPHY_IRQ(x) (1 << (4 + (x)))
|
||||||
|
#define P_NUM_IRQ 5
|
||||||
|
#define P_IRQ_MASK(x) (P_LINK_UP_IRQ((x)) | \
|
||||||
|
P_LINK_DOWN_IRQ((x)) | \
|
||||||
|
P_ENERGY_ON_IRQ((x)) | \
|
||||||
|
P_ENERGY_OFF_IRQ((x)) | \
|
||||||
|
P_GPHY_IRQ((x)))
|
||||||
|
|
||||||
|
/* INTRL2_0 interrupt sources */
|
||||||
|
#define P0_IRQ_OFF 0
|
||||||
|
#define MEM_DOUBLE_IRQ (1 << 5)
|
||||||
|
#define EEE_LPI_IRQ (1 << 6)
|
||||||
|
#define P5_CPU_WAKE_IRQ (1 << 7)
|
||||||
|
#define P8_CPU_WAKE_IRQ (1 << 8)
|
||||||
|
#define P7_CPU_WAKE_IRQ (1 << 9)
|
||||||
|
#define IEEE1588_IRQ (1 << 10)
|
||||||
|
#define MDIO_ERR_IRQ (1 << 11)
|
||||||
|
#define MDIO_DONE_IRQ (1 << 12)
|
||||||
|
#define GISB_ERR_IRQ (1 << 13)
|
||||||
|
#define UBUS_ERR_IRQ (1 << 14)
|
||||||
|
#define FAILOVER_ON_IRQ (1 << 15)
|
||||||
|
#define FAILOVER_OFF_IRQ (1 << 16)
|
||||||
|
#define TCAM_SOFT_ERR_IRQ (1 << 17)
|
||||||
|
|
||||||
|
/* INTRL2_1 interrupt sources */
|
||||||
|
#define P7_IRQ_OFF 0
|
||||||
|
#define P_IRQ_OFF(x) ((6 - (x)) * P_NUM_IRQ)
|
||||||
|
|
||||||
|
/* Register set relative to 'CORE' */
|
||||||
|
#define CORE_G_PCTL_PORT0 0x00000
|
||||||
|
#define CORE_G_PCTL_PORT(x) (CORE_G_PCTL_PORT0 + (x * 0x4))
|
||||||
|
#define CORE_IMP_CTL 0x00020
|
||||||
|
#define RX_DIS (1 << 0)
|
||||||
|
#define TX_DIS (1 << 1)
|
||||||
|
#define RX_BCST_EN (1 << 2)
|
||||||
|
#define RX_MCST_EN (1 << 3)
|
||||||
|
#define RX_UCST_EN (1 << 4)
|
||||||
|
#define G_MISTP_STATE_SHIFT 5
|
||||||
|
#define G_MISTP_NO_STP (0 << G_MISTP_STATE_SHIFT)
|
||||||
|
#define G_MISTP_DIS_STATE (1 << G_MISTP_STATE_SHIFT)
|
||||||
|
#define G_MISTP_BLOCK_STATE (2 << G_MISTP_STATE_SHIFT)
|
||||||
|
#define G_MISTP_LISTEN_STATE (3 << G_MISTP_STATE_SHIFT)
|
||||||
|
#define G_MISTP_LEARN_STATE (4 << G_MISTP_STATE_SHIFT)
|
||||||
|
#define G_MISTP_FWD_STATE (5 << G_MISTP_STATE_SHIFT)
|
||||||
|
#define G_MISTP_STATE_MASK 0x7
|
||||||
|
|
||||||
|
#define CORE_SWMODE 0x0002c
|
||||||
|
#define SW_FWDG_MODE (1 << 0)
|
||||||
|
#define SW_FWDG_EN (1 << 1)
|
||||||
|
#define RTRY_LMT_DIS (1 << 2)
|
||||||
|
|
||||||
|
#define CORE_STS_OVERRIDE_IMP 0x00038
|
||||||
|
#define GMII_SPEED_UP_2G (1 << 6)
|
||||||
|
#define MII_SW_OR (1 << 7)
|
||||||
|
|
||||||
|
#define CORE_NEW_CTRL 0x00084
|
||||||
|
#define IP_MC (1 << 0)
|
||||||
|
#define OUTRANGEERR_DISCARD (1 << 1)
|
||||||
|
#define INRANGEERR_DISCARD (1 << 2)
|
||||||
|
#define CABLE_DIAG_LEN (1 << 3)
|
||||||
|
#define OVERRIDE_AUTO_PD_WAR (1 << 4)
|
||||||
|
#define EN_AUTO_PD_WAR (1 << 5)
|
||||||
|
#define UC_FWD_EN (1 << 6)
|
||||||
|
#define MC_FWD_EN (1 << 7)
|
||||||
|
|
||||||
|
#define CORE_SWITCH_CTRL 0x00088
|
||||||
|
#define MII_DUMB_FWDG_EN (1 << 6)
|
||||||
|
|
||||||
|
#define CORE_SFT_LRN_CTRL 0x000f8
|
||||||
|
#define SW_LEARN_CNTL(x) (1 << (x))
|
||||||
|
|
||||||
|
#define CORE_STS_OVERRIDE_GMIIP_PORT(x) (0x160 + (x) * 4)
|
||||||
|
#define LINK_STS (1 << 0)
|
||||||
|
#define DUPLX_MODE (1 << 1)
|
||||||
|
#define SPEED_SHIFT 2
|
||||||
|
#define SPEED_MASK 0x3
|
||||||
|
#define RXFLOW_CNTL (1 << 4)
|
||||||
|
#define TXFLOW_CNTL (1 << 5)
|
||||||
|
#define SW_OVERRIDE (1 << 6)
|
||||||
|
|
||||||
|
#define CORE_WATCHDOG_CTRL 0x001e4
|
||||||
|
#define SOFTWARE_RESET (1 << 7)
|
||||||
|
#define EN_CHIP_RST (1 << 6)
|
||||||
|
#define EN_SW_RESET (1 << 4)
|
||||||
|
|
||||||
|
#define CORE_LNKSTS 0x00400
|
||||||
|
#define LNK_STS_MASK 0x1ff
|
||||||
|
|
||||||
|
#define CORE_SPDSTS 0x00410
|
||||||
|
#define SPDSTS_10 0
|
||||||
|
#define SPDSTS_100 1
|
||||||
|
#define SPDSTS_1000 2
|
||||||
|
#define SPDSTS_SHIFT 2
|
||||||
|
#define SPDSTS_MASK 0x3
|
||||||
|
|
||||||
|
#define CORE_DUPSTS 0x00420
|
||||||
|
#define CORE_DUPSTS_MASK 0x1ff
|
||||||
|
|
||||||
|
#define CORE_PAUSESTS 0x00428
|
||||||
|
#define PAUSESTS_TX_PAUSE_SHIFT 9
|
||||||
|
|
||||||
|
#define CORE_GMNCFGCFG 0x0800
|
||||||
|
#define RST_MIB_CNT (1 << 0)
|
||||||
|
#define RXBPDU_EN (1 << 1)
|
||||||
|
|
||||||
|
#define CORE_IMP0_PRT_ID 0x0804
|
||||||
|
|
||||||
|
#define CORE_BRCM_HDR_CTRL 0x0080c
|
||||||
|
#define BRCM_HDR_EN_P8 (1 << 0)
|
||||||
|
#define BRCM_HDR_EN_P5 (1 << 1)
|
||||||
|
#define BRCM_HDR_EN_P7 (1 << 2)
|
||||||
|
|
||||||
|
#define CORE_BRCM_HDR_CTRL2 0x0828
|
||||||
|
|
||||||
|
#define CORE_HL_PRTC_CTRL 0x0940
|
||||||
|
#define ARP_EN (1 << 0)
|
||||||
|
#define RARP_EN (1 << 1)
|
||||||
|
#define DHCP_EN (1 << 2)
|
||||||
|
#define ICMPV4_EN (1 << 3)
|
||||||
|
#define ICMPV6_EN (1 << 4)
|
||||||
|
#define ICMPV6_FWD_MODE (1 << 5)
|
||||||
|
#define IGMP_DIP_EN (1 << 8)
|
||||||
|
#define IGMP_RPTLVE_EN (1 << 9)
|
||||||
|
#define IGMP_RTPLVE_FWD_MODE (1 << 10)
|
||||||
|
#define IGMP_QRY_EN (1 << 11)
|
||||||
|
#define IGMP_QRY_FWD_MODE (1 << 12)
|
||||||
|
#define IGMP_UKN_EN (1 << 13)
|
||||||
|
#define IGMP_UKN_FWD_MODE (1 << 14)
|
||||||
|
#define MLD_RPTDONE_EN (1 << 15)
|
||||||
|
#define MLD_RPTDONE_FWD_MODE (1 << 16)
|
||||||
|
#define MLD_QRY_EN (1 << 17)
|
||||||
|
#define MLD_QRY_FWD_MODE (1 << 18)
|
||||||
|
|
||||||
|
#define CORE_RST_MIB_CNT_EN 0x0950
|
||||||
|
|
||||||
|
#define CORE_BRCM_HDR_RX_DIS 0x0980
|
||||||
|
#define CORE_BRCM_HDR_TX_DIS 0x0988
|
||||||
|
|
||||||
|
#define CORE_MEM_PSM_VDD_CTRL 0x2380
|
||||||
|
#define P_TXQ_PSM_VDD_SHIFT 2
|
||||||
|
#define P_TXQ_PSM_VDD_MASK 0x3
|
||||||
|
#define P_TXQ_PSM_VDD(x) (P_TXQ_PSM_VDD_MASK << \
|
||||||
|
((x) * P_TXQ_PSM_VDD_SHIFT))
|
||||||
|
|
||||||
|
#define CORE_P0_MIB_OFFSET 0x8000
|
||||||
|
#define P_MIB_SIZE 0x400
|
||||||
|
#define CORE_P_MIB_OFFSET(x) (CORE_P0_MIB_OFFSET + (x) * P_MIB_SIZE)
|
||||||
|
|
||||||
|
#define CORE_PORT_VLAN_CTL_PORT(x) (0xc400 + ((x) * 0x8))
|
||||||
|
#define PORT_VLAN_CTRL_MASK 0x1ff
|
||||||
|
|
||||||
|
#endif /* __BCM_SF2_REGS_H */
|
|
@ -205,6 +205,14 @@ config MDIO_BUS_MUX_MMIOREG
|
||||||
|
|
||||||
Currently, only 8-bit registers are supported.
|
Currently, only 8-bit registers are supported.
|
||||||
|
|
||||||
|
config MDIO_BCM_UNIMAC
|
||||||
|
tristate "Broadcom UniMAC MDIO bus controller"
|
||||||
|
help
|
||||||
|
This module provides a driver for the Broadcom UniMAC MDIO busses.
|
||||||
|
This hardware can be found in the Broadcom GENET Ethernet MAC
|
||||||
|
controllers as well as some Broadcom Ethernet switches such as the
|
||||||
|
Starfighter 2 switches.
|
||||||
|
|
||||||
endif # PHYLIB
|
endif # PHYLIB
|
||||||
|
|
||||||
config MICREL_KS8995MA
|
config MICREL_KS8995MA
|
||||||
|
|
|
@ -34,3 +34,4 @@ obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o
|
||||||
obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
|
obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
|
||||||
obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o
|
obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o
|
||||||
obj-$(CONFIG_AMD_XGBE_PHY) += amd-xgbe-phy.o
|
obj-$(CONFIG_AMD_XGBE_PHY) += amd-xgbe-phy.o
|
||||||
|
obj-$(CONFIG_MDIO_BCM_UNIMAC) += mdio-bcm-unimac.o
|
||||||
|
|
212
drivers/net/phy/mdio-bcm-unimac.c
Normal file
212
drivers/net/phy/mdio-bcm-unimac.c
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* Broadcom UniMAC MDIO bus controller driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014, Broadcom Corporation
|
||||||
|
*
|
||||||
|
* 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/kernel.h>
|
||||||
|
#include <linux/phy.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_platform.h>
|
||||||
|
#include <linux/of_mdio.h>
|
||||||
|
|
||||||
|
#define MDIO_CMD 0x00
|
||||||
|
#define MDIO_START_BUSY (1 << 29)
|
||||||
|
#define MDIO_READ_FAIL (1 << 28)
|
||||||
|
#define MDIO_RD (2 << 26)
|
||||||
|
#define MDIO_WR (1 << 26)
|
||||||
|
#define MDIO_PMD_SHIFT 21
|
||||||
|
#define MDIO_PMD_MASK 0x1F
|
||||||
|
#define MDIO_REG_SHIFT 16
|
||||||
|
#define MDIO_REG_MASK 0x1F
|
||||||
|
|
||||||
|
#define MDIO_CFG 0x04
|
||||||
|
#define MDIO_C22 (1 << 0)
|
||||||
|
#define MDIO_C45 0
|
||||||
|
#define MDIO_CLK_DIV_SHIFT 4
|
||||||
|
#define MDIO_CLK_DIV_MASK 0x3F
|
||||||
|
#define MDIO_SUPP_PREAMBLE (1 << 12)
|
||||||
|
|
||||||
|
struct unimac_mdio_priv {
|
||||||
|
struct mii_bus *mii_bus;
|
||||||
|
void __iomem *base;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void unimac_mdio_start(struct unimac_mdio_priv *priv)
|
||||||
|
{
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = __raw_readl(priv->base + MDIO_CMD);
|
||||||
|
reg |= MDIO_START_BUSY;
|
||||||
|
__raw_writel(reg, priv->base + MDIO_CMD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int unimac_mdio_busy(struct unimac_mdio_priv *priv)
|
||||||
|
{
|
||||||
|
return __raw_readl(priv->base + MDIO_CMD) & MDIO_START_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
|
||||||
|
{
|
||||||
|
struct unimac_mdio_priv *priv = bus->priv;
|
||||||
|
unsigned int timeout = 1000;
|
||||||
|
u32 cmd;
|
||||||
|
|
||||||
|
/* Prepare the read operation */
|
||||||
|
cmd = MDIO_RD | (phy_id << MDIO_PMD_SHIFT) | (reg << MDIO_REG_SHIFT);
|
||||||
|
__raw_writel(cmd, priv->base + MDIO_CMD);
|
||||||
|
|
||||||
|
/* Start MDIO transaction */
|
||||||
|
unimac_mdio_start(priv);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (!unimac_mdio_busy(priv))
|
||||||
|
break;
|
||||||
|
|
||||||
|
usleep_range(1000, 2000);
|
||||||
|
} while (timeout--);
|
||||||
|
|
||||||
|
if (!timeout)
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
|
||||||
|
cmd = __raw_readl(priv->base + MDIO_CMD);
|
||||||
|
if (cmd & MDIO_READ_FAIL)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
return cmd & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unimac_mdio_write(struct mii_bus *bus, int phy_id,
|
||||||
|
int reg, u16 val)
|
||||||
|
{
|
||||||
|
struct unimac_mdio_priv *priv = bus->priv;
|
||||||
|
unsigned int timeout = 1000;
|
||||||
|
u32 cmd;
|
||||||
|
|
||||||
|
/* Prepare the write operation */
|
||||||
|
cmd = MDIO_WR | (phy_id << MDIO_PMD_SHIFT) |
|
||||||
|
(reg << MDIO_REG_SHIFT) | (0xffff & val);
|
||||||
|
__raw_writel(cmd, priv->base + MDIO_CMD);
|
||||||
|
|
||||||
|
unimac_mdio_start(priv);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (!unimac_mdio_busy(priv))
|
||||||
|
break;
|
||||||
|
|
||||||
|
usleep_range(1000, 2000);
|
||||||
|
} while (timeout--);
|
||||||
|
|
||||||
|
if (!timeout)
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unimac_mdio_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct unimac_mdio_priv *priv;
|
||||||
|
struct device_node *np;
|
||||||
|
struct mii_bus *bus;
|
||||||
|
struct resource *r;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
np = pdev->dev.of_node;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
|
||||||
|
/* Just ioremap, as this MDIO block is usually integrated into an
|
||||||
|
* Ethernet MAC controller register range
|
||||||
|
*/
|
||||||
|
priv->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
|
||||||
|
if (!priv->base) {
|
||||||
|
dev_err(&pdev->dev, "failed to remap register\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->mii_bus = mdiobus_alloc();
|
||||||
|
if (!priv->mii_bus)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
bus = priv->mii_bus;
|
||||||
|
bus->priv = priv;
|
||||||
|
bus->name = "unimac MII bus";
|
||||||
|
bus->parent = &pdev->dev;
|
||||||
|
bus->read = unimac_mdio_read;
|
||||||
|
bus->write = unimac_mdio_write;
|
||||||
|
snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
|
||||||
|
|
||||||
|
bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
|
||||||
|
if (!bus->irq) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out_mdio_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = of_mdiobus_register(bus, np);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "MDIO bus registration failed\n");
|
||||||
|
goto out_mdio_irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, priv);
|
||||||
|
|
||||||
|
dev_info(&pdev->dev, "Broadcom UniMAC MDIO bus at 0x%p\n", priv->base);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_mdio_irq:
|
||||||
|
kfree(bus->irq);
|
||||||
|
out_mdio_free:
|
||||||
|
mdiobus_free(bus);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unimac_mdio_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct unimac_mdio_priv *priv = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
mdiobus_unregister(priv->mii_bus);
|
||||||
|
kfree(priv->mii_bus->irq);
|
||||||
|
mdiobus_free(priv->mii_bus);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct of_device_id unimac_mdio_ids[] = {
|
||||||
|
{ .compatible = "brcm,genet-mdio-v4", },
|
||||||
|
{ .compatible = "brcm,genet-mdio-v3", },
|
||||||
|
{ .compatible = "brcm,genet-mdio-v2", },
|
||||||
|
{ .compatible = "brcm,genet-mdio-v1", },
|
||||||
|
{ .compatible = "brcm,unimac-mdio", },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver unimac_mdio_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "unimac-mdio",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.of_match_table = unimac_mdio_ids,
|
||||||
|
},
|
||||||
|
.probe = unimac_mdio_probe,
|
||||||
|
.remove = unimac_mdio_remove,
|
||||||
|
};
|
||||||
|
module_platform_driver(unimac_mdio_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Broadcom Corporation");
|
||||||
|
MODULE_DESCRIPTION("Broadcom UniMAC MDIO bus controller");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("platform:unimac-mdio");
|
|
@ -1781,24 +1781,13 @@ void dev_net_set(struct net_device *dev, struct net *net)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool netdev_uses_dsa_tags(struct net_device *dev)
|
static inline bool netdev_uses_dsa(struct net_device *dev)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_NET_DSA_TAG_DSA
|
#ifdef CONFIG_NET_DSA
|
||||||
if (dev->dsa_ptr != NULL)
|
if (dev->dsa_ptr != NULL)
|
||||||
return dsa_uses_dsa_tags(dev->dsa_ptr);
|
return dsa_uses_tagged_protocol(dev->dsa_ptr);
|
||||||
#endif
|
#endif
|
||||||
|
return false;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool netdev_uses_trailer_tags(struct net_device *dev)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_NET_DSA_TAG_TRAILER
|
|
||||||
if (dev->dsa_ptr != NULL)
|
|
||||||
return dsa_uses_trailer_tags(dev->dsa_ptr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1933,6 +1922,13 @@ struct udp_offload {
|
||||||
struct offload_callbacks callbacks;
|
struct offload_callbacks callbacks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dsa_device_ops {
|
||||||
|
netdev_tx_t (*xmit)(struct sk_buff *skb, struct net_device *dev);
|
||||||
|
int (*rcv)(struct sk_buff *skb, struct net_device *dev,
|
||||||
|
struct packet_type *pt, struct net_device *orig_dev);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* often modified stats are per cpu, other are shared (netdev->stats) */
|
/* often modified stats are per cpu, other are shared (netdev->stats) */
|
||||||
struct pcpu_sw_netstats {
|
struct pcpu_sw_netstats {
|
||||||
u64 rx_packets;
|
u64 rx_packets;
|
||||||
|
|
|
@ -18,6 +18,9 @@ extern int fixed_phy_register(unsigned int irq,
|
||||||
struct fixed_phy_status *status,
|
struct fixed_phy_status *status,
|
||||||
struct device_node *np);
|
struct device_node *np);
|
||||||
extern void fixed_phy_del(int phy_addr);
|
extern void fixed_phy_del(int phy_addr);
|
||||||
|
extern int fixed_phy_set_link_update(struct phy_device *phydev,
|
||||||
|
int (*link_update)(struct net_device *,
|
||||||
|
struct fixed_phy_status *));
|
||||||
#else
|
#else
|
||||||
static inline int fixed_phy_add(unsigned int irq, int phy_id,
|
static inline int fixed_phy_add(unsigned int irq, int phy_id,
|
||||||
struct fixed_phy_status *status)
|
struct fixed_phy_status *status)
|
||||||
|
@ -34,14 +37,12 @@ static inline int fixed_phy_del(int phy_addr)
|
||||||
{
|
{
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
static inline int fixed_phy_set_link_update(struct phy_device *phydev,
|
||||||
|
int (*link_update)(struct net_device *,
|
||||||
|
struct fixed_phy_status *))
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
#endif /* CONFIG_FIXED_PHY */
|
#endif /* CONFIG_FIXED_PHY */
|
||||||
|
|
||||||
/*
|
|
||||||
* This function issued only by fixed_phy-aware drivers, no need
|
|
||||||
* protect it with #ifdef
|
|
||||||
*/
|
|
||||||
extern int fixed_phy_set_link_update(struct phy_device *phydev,
|
|
||||||
int (*link_update)(struct net_device *,
|
|
||||||
struct fixed_phy_status *));
|
|
||||||
|
|
||||||
#endif /* __PHY_FIXED_H */
|
#endif /* __PHY_FIXED_H */
|
||||||
|
|
|
@ -15,6 +15,14 @@
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/phy.h>
|
||||||
|
#include <linux/phy_fixed.h>
|
||||||
|
|
||||||
|
/* Not an official ethertype value, used only internally for DSA
|
||||||
|
* demultiplexing
|
||||||
|
*/
|
||||||
|
#define ETH_P_BRCMTAG (ETH_P_XDSA + 1)
|
||||||
|
|
||||||
#define DSA_MAX_SWITCHES 4
|
#define DSA_MAX_SWITCHES 4
|
||||||
#define DSA_MAX_PORTS 12
|
#define DSA_MAX_PORTS 12
|
||||||
|
@ -26,6 +34,12 @@ struct dsa_chip_data {
|
||||||
struct device *mii_bus;
|
struct device *mii_bus;
|
||||||
int sw_addr;
|
int sw_addr;
|
||||||
|
|
||||||
|
/* Device tree node pointer for this specific switch chip
|
||||||
|
* used during switch setup in case additional properties
|
||||||
|
* and resources needs to be used
|
||||||
|
*/
|
||||||
|
struct device_node *of_node;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The names of the switch's ports. Use "cpu" to
|
* The names of the switch's ports. Use "cpu" to
|
||||||
* designate the switch port that the cpu is connected to,
|
* designate the switch port that the cpu is connected to,
|
||||||
|
@ -34,6 +48,7 @@ struct dsa_chip_data {
|
||||||
* or any other string to indicate this is a physical port.
|
* or any other string to indicate this is a physical port.
|
||||||
*/
|
*/
|
||||||
char *port_names[DSA_MAX_PORTS];
|
char *port_names[DSA_MAX_PORTS];
|
||||||
|
struct device_node *port_dn[DSA_MAX_PORTS];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* An array (with nr_chips elements) of which element [a]
|
* An array (with nr_chips elements) of which element [a]
|
||||||
|
@ -59,6 +74,8 @@ struct dsa_platform_data {
|
||||||
struct dsa_chip_data *chip;
|
struct dsa_chip_data *chip;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dsa_device_ops;
|
||||||
|
|
||||||
struct dsa_switch_tree {
|
struct dsa_switch_tree {
|
||||||
/*
|
/*
|
||||||
* Configuration data for the platform device that owns
|
* Configuration data for the platform device that owns
|
||||||
|
@ -71,6 +88,7 @@ struct dsa_switch_tree {
|
||||||
* protocol to use.
|
* protocol to use.
|
||||||
*/
|
*/
|
||||||
struct net_device *master_netdev;
|
struct net_device *master_netdev;
|
||||||
|
const struct dsa_device_ops *ops;
|
||||||
__be16 tag_protocol;
|
__be16 tag_protocol;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -119,6 +137,7 @@ struct dsa_switch {
|
||||||
*/
|
*/
|
||||||
u32 dsa_port_mask;
|
u32 dsa_port_mask;
|
||||||
u32 phys_port_mask;
|
u32 phys_port_mask;
|
||||||
|
u32 phys_mii_mask;
|
||||||
struct mii_bus *slave_mii_bus;
|
struct mii_bus *slave_mii_bus;
|
||||||
struct net_device *ports[DSA_MAX_PORTS];
|
struct net_device *ports[DSA_MAX_PORTS];
|
||||||
};
|
};
|
||||||
|
@ -169,6 +188,14 @@ struct dsa_switch_driver {
|
||||||
*/
|
*/
|
||||||
void (*poll_link)(struct dsa_switch *ds);
|
void (*poll_link)(struct dsa_switch *ds);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Link state adjustment (called from libphy)
|
||||||
|
*/
|
||||||
|
void (*adjust_link)(struct dsa_switch *ds, int port,
|
||||||
|
struct phy_device *phydev);
|
||||||
|
void (*fixed_link_update)(struct dsa_switch *ds, int port,
|
||||||
|
struct fixed_phy_status *st);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ethtool hardware statistics.
|
* ethtool hardware statistics.
|
||||||
*/
|
*/
|
||||||
|
@ -186,21 +213,9 @@ static inline void *ds_to_priv(struct dsa_switch *ds)
|
||||||
return (void *)(ds + 1);
|
return (void *)(ds + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static inline bool dsa_uses_tagged_protocol(struct dsa_switch_tree *dst)
|
||||||
* The original DSA tag format and some other tag formats have no
|
|
||||||
* ethertype, which means that we need to add a little hack to the
|
|
||||||
* networking receive path to make sure that received frames get
|
|
||||||
* the right ->protocol assigned to them when one of those tag
|
|
||||||
* formats is in use.
|
|
||||||
*/
|
|
||||||
static inline bool dsa_uses_dsa_tags(struct dsa_switch_tree *dst)
|
|
||||||
{
|
{
|
||||||
return !!(dst->tag_protocol == htons(ETH_P_DSA));
|
return dst->tag_protocol != 0;
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool dsa_uses_trailer_tags(struct dsa_switch_tree *dst)
|
|
||||||
{
|
|
||||||
return !!(dst->tag_protocol == htons(ETH_P_TRAILER));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -128,6 +128,7 @@
|
||||||
#define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */
|
#define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */
|
||||||
#define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */
|
#define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */
|
||||||
#define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF protocol */
|
#define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF protocol */
|
||||||
|
#define ETH_P_XDSA 0x00F8 /* Multiplexed DSA protocol */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is an Ethernet frame header.
|
* This is an Ethernet frame header.
|
||||||
|
|
|
@ -12,6 +12,9 @@ config NET_DSA
|
||||||
if NET_DSA
|
if NET_DSA
|
||||||
|
|
||||||
# tagging formats
|
# tagging formats
|
||||||
|
config NET_DSA_TAG_BRCM
|
||||||
|
bool
|
||||||
|
|
||||||
config NET_DSA_TAG_DSA
|
config NET_DSA_TAG_DSA
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ obj-$(CONFIG_NET_DSA) += dsa_core.o
|
||||||
dsa_core-y += dsa.o slave.o
|
dsa_core-y += dsa.o slave.o
|
||||||
|
|
||||||
# tagging formats
|
# tagging formats
|
||||||
|
dsa_core-$(CONFIG_NET_DSA_TAG_BRCM) += tag_brcm.o
|
||||||
dsa_core-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o
|
dsa_core-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o
|
||||||
dsa_core-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o
|
dsa_core-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o
|
||||||
dsa_core-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
|
dsa_core-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
|
||||||
|
|
|
@ -144,6 +144,11 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Make the built-in MII bus mask match the number of ports,
|
||||||
|
* switch drivers can override this later
|
||||||
|
*/
|
||||||
|
ds->phys_mii_mask = ds->phys_port_mask;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the CPU connects to this switch, set the switch tree
|
* If the CPU connects to this switch, set the switch tree
|
||||||
* tagging protocol to the preferred tagging format of this
|
* tagging protocol to the preferred tagging format of this
|
||||||
|
@ -410,6 +415,7 @@ static int dsa_of_probe(struct platform_device *pdev)
|
||||||
chip_index++;
|
chip_index++;
|
||||||
cd = &pd->chip[chip_index];
|
cd = &pd->chip[chip_index];
|
||||||
|
|
||||||
|
cd->of_node = child;
|
||||||
cd->mii_bus = &mdio_bus->dev;
|
cd->mii_bus = &mdio_bus->dev;
|
||||||
|
|
||||||
sw_addr = of_get_property(child, "reg", NULL);
|
sw_addr = of_get_property(child, "reg", NULL);
|
||||||
|
@ -431,6 +437,8 @@ static int dsa_of_probe(struct platform_device *pdev)
|
||||||
if (!port_name)
|
if (!port_name)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
cd->port_dn[port_index] = port;
|
||||||
|
|
||||||
cd->port_names[port_index] = kstrdup(port_name,
|
cd->port_names[port_index] = kstrdup(port_name,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!cd->port_names[port_index]) {
|
if (!cd->port_names[port_index]) {
|
||||||
|
@ -608,7 +616,26 @@ static void dsa_shutdown(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||||
|
struct packet_type *pt, struct net_device *orig_dev)
|
||||||
|
{
|
||||||
|
struct dsa_switch_tree *dst = dev->dsa_ptr;
|
||||||
|
|
||||||
|
if (unlikely(dst == NULL)) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst->ops->rcv(skb, dev, pt, orig_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct packet_type dsa_pack_type __read_mostly = {
|
||||||
|
.type = cpu_to_be16(ETH_P_XDSA),
|
||||||
|
.func = dsa_switch_rcv,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id dsa_of_match_table[] = {
|
static const struct of_device_id dsa_of_match_table[] = {
|
||||||
|
{ .compatible = "brcm,bcm7445-switch-v4.0" },
|
||||||
{ .compatible = "marvell,dsa", },
|
{ .compatible = "marvell,dsa", },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
@ -633,30 +660,15 @@ static int __init dsa_init_module(void)
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
#ifdef CONFIG_NET_DSA_TAG_DSA
|
dev_add_pack(&dsa_pack_type);
|
||||||
dev_add_pack(&dsa_packet_type);
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_NET_DSA_TAG_EDSA
|
|
||||||
dev_add_pack(&edsa_packet_type);
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_NET_DSA_TAG_TRAILER
|
|
||||||
dev_add_pack(&trailer_packet_type);
|
|
||||||
#endif
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
module_init(dsa_init_module);
|
module_init(dsa_init_module);
|
||||||
|
|
||||||
static void __exit dsa_cleanup_module(void)
|
static void __exit dsa_cleanup_module(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_NET_DSA_TAG_TRAILER
|
dev_remove_pack(&dsa_pack_type);
|
||||||
dev_remove_pack(&trailer_packet_type);
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_NET_DSA_TAG_EDSA
|
|
||||||
dev_remove_pack(&edsa_packet_type);
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_NET_DSA_TAG_DSA
|
|
||||||
dev_remove_pack(&dsa_packet_type);
|
|
||||||
#endif
|
|
||||||
platform_driver_unregister(&dsa_driver);
|
platform_driver_unregister(&dsa_driver);
|
||||||
}
|
}
|
||||||
module_exit(dsa_cleanup_module);
|
module_exit(dsa_cleanup_module);
|
||||||
|
|
|
@ -33,6 +33,10 @@ struct dsa_slave_priv {
|
||||||
* to this port.
|
* to this port.
|
||||||
*/
|
*/
|
||||||
struct phy_device *phy;
|
struct phy_device *phy;
|
||||||
|
phy_interface_t phy_interface;
|
||||||
|
int old_link;
|
||||||
|
int old_pause;
|
||||||
|
int old_duplex;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* dsa.c */
|
/* dsa.c */
|
||||||
|
@ -45,16 +49,16 @@ struct net_device *dsa_slave_create(struct dsa_switch *ds,
|
||||||
int port, char *name);
|
int port, char *name);
|
||||||
|
|
||||||
/* tag_dsa.c */
|
/* tag_dsa.c */
|
||||||
netdev_tx_t dsa_xmit(struct sk_buff *skb, struct net_device *dev);
|
extern const struct dsa_device_ops dsa_netdev_ops;
|
||||||
extern struct packet_type dsa_packet_type;
|
|
||||||
|
|
||||||
/* tag_edsa.c */
|
/* tag_edsa.c */
|
||||||
netdev_tx_t edsa_xmit(struct sk_buff *skb, struct net_device *dev);
|
extern const struct dsa_device_ops edsa_netdev_ops;
|
||||||
extern struct packet_type edsa_packet_type;
|
|
||||||
|
|
||||||
/* tag_trailer.c */
|
/* tag_trailer.c */
|
||||||
netdev_tx_t trailer_xmit(struct sk_buff *skb, struct net_device *dev);
|
extern const struct dsa_device_ops trailer_netdev_ops;
|
||||||
extern struct packet_type trailer_packet_type;
|
|
||||||
|
/* tag_brcm.c */
|
||||||
|
extern const struct dsa_device_ops brcm_netdev_ops;
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
165
net/dsa/slave.c
165
net/dsa/slave.c
|
@ -12,6 +12,8 @@
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
#include <linux/etherdevice.h>
|
#include <linux/etherdevice.h>
|
||||||
#include <linux/phy.h>
|
#include <linux/phy.h>
|
||||||
|
#include <linux/of_net.h>
|
||||||
|
#include <linux/of_mdio.h>
|
||||||
#include "dsa_priv.h"
|
#include "dsa_priv.h"
|
||||||
|
|
||||||
/* slave mii_bus handling ***************************************************/
|
/* slave mii_bus handling ***************************************************/
|
||||||
|
@ -19,7 +21,7 @@ static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg)
|
||||||
{
|
{
|
||||||
struct dsa_switch *ds = bus->priv;
|
struct dsa_switch *ds = bus->priv;
|
||||||
|
|
||||||
if (ds->phys_port_mask & (1 << addr))
|
if (ds->phys_mii_mask & (1 << addr))
|
||||||
return ds->drv->phy_read(ds, addr, reg);
|
return ds->drv->phy_read(ds, addr, reg);
|
||||||
|
|
||||||
return 0xffff;
|
return 0xffff;
|
||||||
|
@ -29,7 +31,7 @@ static int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val)
|
||||||
{
|
{
|
||||||
struct dsa_switch *ds = bus->priv;
|
struct dsa_switch *ds = bus->priv;
|
||||||
|
|
||||||
if (ds->phys_port_mask & (1 << addr))
|
if (ds->phys_mii_mask & (1 << addr))
|
||||||
return ds->drv->phy_write(ds, addr, reg, val);
|
return ds->drv->phy_write(ds, addr, reg, val);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -171,6 +173,25 @@ static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||||
|
struct dsa_switch_tree *dst = p->parent->dst;
|
||||||
|
|
||||||
|
return dst->ops->xmit(skb, dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static netdev_tx_t dsa_slave_notag_xmit(struct sk_buff *skb,
|
||||||
|
struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||||
|
|
||||||
|
skb->dev = p->parent->dst->master_netdev;
|
||||||
|
dev_queue_xmit(skb);
|
||||||
|
|
||||||
|
return NETDEV_TX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ethtool operations *******************************************************/
|
/* ethtool operations *******************************************************/
|
||||||
static int
|
static int
|
||||||
|
@ -293,44 +314,107 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
|
||||||
.get_sset_count = dsa_slave_get_sset_count,
|
.get_sset_count = dsa_slave_get_sset_count,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_NET_DSA_TAG_DSA
|
static const struct net_device_ops dsa_slave_netdev_ops = {
|
||||||
static const struct net_device_ops dsa_netdev_ops = {
|
|
||||||
.ndo_init = dsa_slave_init,
|
.ndo_init = dsa_slave_init,
|
||||||
.ndo_open = dsa_slave_open,
|
.ndo_open = dsa_slave_open,
|
||||||
.ndo_stop = dsa_slave_close,
|
.ndo_stop = dsa_slave_close,
|
||||||
.ndo_start_xmit = dsa_xmit,
|
.ndo_start_xmit = dsa_slave_xmit,
|
||||||
.ndo_change_rx_flags = dsa_slave_change_rx_flags,
|
.ndo_change_rx_flags = dsa_slave_change_rx_flags,
|
||||||
.ndo_set_rx_mode = dsa_slave_set_rx_mode,
|
.ndo_set_rx_mode = dsa_slave_set_rx_mode,
|
||||||
.ndo_set_mac_address = dsa_slave_set_mac_address,
|
.ndo_set_mac_address = dsa_slave_set_mac_address,
|
||||||
.ndo_do_ioctl = dsa_slave_ioctl,
|
.ndo_do_ioctl = dsa_slave_ioctl,
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_NET_DSA_TAG_EDSA
|
static const struct dsa_device_ops notag_netdev_ops = {
|
||||||
static const struct net_device_ops edsa_netdev_ops = {
|
.xmit = dsa_slave_notag_xmit,
|
||||||
.ndo_init = dsa_slave_init,
|
.rcv = NULL,
|
||||||
.ndo_open = dsa_slave_open,
|
|
||||||
.ndo_stop = dsa_slave_close,
|
|
||||||
.ndo_start_xmit = edsa_xmit,
|
|
||||||
.ndo_change_rx_flags = dsa_slave_change_rx_flags,
|
|
||||||
.ndo_set_rx_mode = dsa_slave_set_rx_mode,
|
|
||||||
.ndo_set_mac_address = dsa_slave_set_mac_address,
|
|
||||||
.ndo_do_ioctl = dsa_slave_ioctl,
|
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_NET_DSA_TAG_TRAILER
|
static void dsa_slave_adjust_link(struct net_device *dev)
|
||||||
static const struct net_device_ops trailer_netdev_ops = {
|
{
|
||||||
.ndo_init = dsa_slave_init,
|
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||||
.ndo_open = dsa_slave_open,
|
struct dsa_switch *ds = p->parent;
|
||||||
.ndo_stop = dsa_slave_close,
|
unsigned int status_changed = 0;
|
||||||
.ndo_start_xmit = trailer_xmit,
|
|
||||||
.ndo_change_rx_flags = dsa_slave_change_rx_flags,
|
if (p->old_link != p->phy->link) {
|
||||||
.ndo_set_rx_mode = dsa_slave_set_rx_mode,
|
status_changed = 1;
|
||||||
.ndo_set_mac_address = dsa_slave_set_mac_address,
|
p->old_link = p->phy->link;
|
||||||
.ndo_do_ioctl = dsa_slave_ioctl,
|
}
|
||||||
};
|
|
||||||
#endif
|
if (p->old_duplex != p->phy->duplex) {
|
||||||
|
status_changed = 1;
|
||||||
|
p->old_duplex = p->phy->duplex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p->old_pause != p->phy->pause) {
|
||||||
|
status_changed = 1;
|
||||||
|
p->old_pause = p->phy->pause;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ds->drv->adjust_link && status_changed)
|
||||||
|
ds->drv->adjust_link(ds, p->port, p->phy);
|
||||||
|
|
||||||
|
if (status_changed)
|
||||||
|
phy_print_status(p->phy);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dsa_slave_fixed_link_update(struct net_device *dev,
|
||||||
|
struct fixed_phy_status *status)
|
||||||
|
{
|
||||||
|
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||||
|
struct dsa_switch *ds = p->parent;
|
||||||
|
|
||||||
|
if (ds->drv->fixed_link_update)
|
||||||
|
ds->drv->fixed_link_update(ds, p->port, status);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* slave device setup *******************************************************/
|
/* slave device setup *******************************************************/
|
||||||
|
static void dsa_slave_phy_setup(struct dsa_slave_priv *p,
|
||||||
|
struct net_device *slave_dev)
|
||||||
|
{
|
||||||
|
struct dsa_switch *ds = p->parent;
|
||||||
|
struct dsa_chip_data *cd = ds->pd;
|
||||||
|
struct device_node *phy_dn, *port_dn;
|
||||||
|
bool phy_is_fixed = false;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
port_dn = cd->port_dn[p->port];
|
||||||
|
p->phy_interface = of_get_phy_mode(port_dn);
|
||||||
|
|
||||||
|
phy_dn = of_parse_phandle(port_dn, "phy-handle", 0);
|
||||||
|
if (of_phy_is_fixed_link(port_dn)) {
|
||||||
|
/* In the case of a fixed PHY, the DT node associated
|
||||||
|
* to the fixed PHY is the Port DT node
|
||||||
|
*/
|
||||||
|
ret = of_phy_register_fixed_link(port_dn);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("failed to register fixed PHY\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
phy_is_fixed = true;
|
||||||
|
phy_dn = port_dn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phy_dn)
|
||||||
|
p->phy = of_phy_connect(slave_dev, phy_dn,
|
||||||
|
dsa_slave_adjust_link, 0,
|
||||||
|
p->phy_interface);
|
||||||
|
|
||||||
|
if (p->phy && phy_is_fixed)
|
||||||
|
fixed_phy_set_link_update(p->phy, dsa_slave_fixed_link_update);
|
||||||
|
|
||||||
|
/* We could not connect to a designated PHY, so use the switch internal
|
||||||
|
* MDIO bus instead
|
||||||
|
*/
|
||||||
|
if (!p->phy)
|
||||||
|
p->phy = ds->slave_mii_bus->phy_map[p->port];
|
||||||
|
else
|
||||||
|
pr_info("attached PHY at address %d [%s]\n",
|
||||||
|
p->phy->addr, p->phy->drv->name);
|
||||||
|
}
|
||||||
|
|
||||||
struct net_device *
|
struct net_device *
|
||||||
dsa_slave_create(struct dsa_switch *ds, struct device *parent,
|
dsa_slave_create(struct dsa_switch *ds, struct device *parent,
|
||||||
int port, char *name)
|
int port, char *name)
|
||||||
|
@ -349,35 +433,48 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
|
||||||
slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
|
slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
|
||||||
eth_hw_addr_inherit(slave_dev, master);
|
eth_hw_addr_inherit(slave_dev, master);
|
||||||
slave_dev->tx_queue_len = 0;
|
slave_dev->tx_queue_len = 0;
|
||||||
|
slave_dev->netdev_ops = &dsa_slave_netdev_ops;
|
||||||
|
|
||||||
switch (ds->dst->tag_protocol) {
|
switch (ds->dst->tag_protocol) {
|
||||||
#ifdef CONFIG_NET_DSA_TAG_DSA
|
#ifdef CONFIG_NET_DSA_TAG_DSA
|
||||||
case htons(ETH_P_DSA):
|
case htons(ETH_P_DSA):
|
||||||
slave_dev->netdev_ops = &dsa_netdev_ops;
|
ds->dst->ops = &dsa_netdev_ops;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_NET_DSA_TAG_EDSA
|
#ifdef CONFIG_NET_DSA_TAG_EDSA
|
||||||
case htons(ETH_P_EDSA):
|
case htons(ETH_P_EDSA):
|
||||||
slave_dev->netdev_ops = &edsa_netdev_ops;
|
ds->dst->ops = &edsa_netdev_ops;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_NET_DSA_TAG_TRAILER
|
#ifdef CONFIG_NET_DSA_TAG_TRAILER
|
||||||
case htons(ETH_P_TRAILER):
|
case htons(ETH_P_TRAILER):
|
||||||
slave_dev->netdev_ops = &trailer_netdev_ops;
|
ds->dst->ops = &trailer_netdev_ops;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_NET_DSA_TAG_BRCM
|
||||||
|
case htons(ETH_P_BRCMTAG):
|
||||||
|
ds->dst->ops = &brcm_netdev_ops;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
BUG();
|
ds->dst->ops = ¬ag_netdev_ops;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
SET_NETDEV_DEV(slave_dev, parent);
|
SET_NETDEV_DEV(slave_dev, parent);
|
||||||
|
slave_dev->dev.of_node = ds->pd->port_dn[port];
|
||||||
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->dev = slave_dev;
|
p->dev = slave_dev;
|
||||||
p->parent = ds;
|
p->parent = ds;
|
||||||
p->port = port;
|
p->port = port;
|
||||||
p->phy = ds->slave_mii_bus->phy_map[port];
|
|
||||||
|
p->old_pause = -1;
|
||||||
|
p->old_link = -1;
|
||||||
|
p->old_duplex = -1;
|
||||||
|
|
||||||
|
dsa_slave_phy_setup(p, slave_dev);
|
||||||
|
|
||||||
ret = register_netdev(slave_dev);
|
ret = register_netdev(slave_dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
173
net/dsa/tag_brcm.c
Normal file
173
net/dsa/tag_brcm.c
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* Broadcom tag support
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Broadcom Corporation
|
||||||
|
*
|
||||||
|
* 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/etherdevice.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/netdevice.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include "dsa_priv.h"
|
||||||
|
|
||||||
|
/* This tag length is 4 bytes, older ones were 6 bytes, we do not
|
||||||
|
* handle them
|
||||||
|
*/
|
||||||
|
#define BRCM_TAG_LEN 4
|
||||||
|
|
||||||
|
/* Tag is constructed and desconstructed using byte by byte access
|
||||||
|
* because the tag is placed after the MAC Source Address, which does
|
||||||
|
* not make it 4-bytes aligned, so this might cause unaligned accesses
|
||||||
|
* on most systems where this is used.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Ingress and egress opcodes */
|
||||||
|
#define BRCM_OPCODE_SHIFT 5
|
||||||
|
#define BRCM_OPCODE_MASK 0x7
|
||||||
|
|
||||||
|
/* Ingress fields */
|
||||||
|
/* 1st byte in the tag */
|
||||||
|
#define BRCM_IG_TC_SHIFT 2
|
||||||
|
#define BRCM_IG_TC_MASK 0x7
|
||||||
|
/* 2nd byte in the tag */
|
||||||
|
#define BRCM_IG_TE_MASK 0x3
|
||||||
|
#define BRCM_IG_TS_SHIFT 7
|
||||||
|
/* 3rd byte in the tag */
|
||||||
|
#define BRCM_IG_DSTMAP2_MASK 1
|
||||||
|
#define BRCM_IG_DSTMAP1_MASK 0xff
|
||||||
|
|
||||||
|
/* Egress fields */
|
||||||
|
|
||||||
|
/* 2nd byte in the tag */
|
||||||
|
#define BRCM_EG_CID_MASK 0xff
|
||||||
|
|
||||||
|
/* 3rd byte in the tag */
|
||||||
|
#define BRCM_EG_RC_MASK 0xff
|
||||||
|
#define BRCM_EG_RC_RSVD (3 << 6)
|
||||||
|
#define BRCM_EG_RC_EXCEPTION (1 << 5)
|
||||||
|
#define BRCM_EG_RC_PROT_SNOOP (1 << 4)
|
||||||
|
#define BRCM_EG_RC_PROT_TERM (1 << 3)
|
||||||
|
#define BRCM_EG_RC_SWITCH (1 << 2)
|
||||||
|
#define BRCM_EG_RC_MAC_LEARN (1 << 1)
|
||||||
|
#define BRCM_EG_RC_MIRROR (1 << 0)
|
||||||
|
#define BRCM_EG_TC_SHIFT 5
|
||||||
|
#define BRCM_EG_TC_MASK 0x7
|
||||||
|
#define BRCM_EG_PID_MASK 0x1f
|
||||||
|
|
||||||
|
static netdev_tx_t brcm_tag_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||||
|
u8 *brcm_tag;
|
||||||
|
|
||||||
|
dev->stats.tx_packets++;
|
||||||
|
dev->stats.tx_bytes += skb->len;
|
||||||
|
|
||||||
|
if (skb_cow_head(skb, BRCM_TAG_LEN) < 0)
|
||||||
|
goto out_free;
|
||||||
|
|
||||||
|
skb_push(skb, BRCM_TAG_LEN);
|
||||||
|
|
||||||
|
memmove(skb->data, skb->data + BRCM_TAG_LEN, 2 * ETH_ALEN);
|
||||||
|
|
||||||
|
/* Build the tag after the MAC Source Address */
|
||||||
|
brcm_tag = skb->data + 2 * ETH_ALEN;
|
||||||
|
|
||||||
|
/* Set the ingress opcode, traffic class, tag enforcment is
|
||||||
|
* deprecated
|
||||||
|
*/
|
||||||
|
brcm_tag[0] = (1 << BRCM_OPCODE_SHIFT) |
|
||||||
|
((skb->priority << BRCM_IG_TC_SHIFT) & BRCM_IG_TC_MASK);
|
||||||
|
brcm_tag[1] = 0;
|
||||||
|
brcm_tag[2] = 0;
|
||||||
|
if (p->port == 8)
|
||||||
|
brcm_tag[2] = BRCM_IG_DSTMAP2_MASK;
|
||||||
|
brcm_tag[3] = (1 << p->port) & BRCM_IG_DSTMAP1_MASK;
|
||||||
|
|
||||||
|
/* Queue the SKB for transmission on the parent interface, but
|
||||||
|
* do not modify its EtherType
|
||||||
|
*/
|
||||||
|
skb->protocol = htons(ETH_P_BRCMTAG);
|
||||||
|
skb->dev = p->parent->dst->master_netdev;
|
||||||
|
dev_queue_xmit(skb);
|
||||||
|
|
||||||
|
return NETDEV_TX_OK;
|
||||||
|
|
||||||
|
out_free:
|
||||||
|
kfree_skb(skb);
|
||||||
|
return NETDEV_TX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||||
|
struct packet_type *pt, struct net_device *orig_dev)
|
||||||
|
{
|
||||||
|
struct dsa_switch_tree *dst = dev->dsa_ptr;
|
||||||
|
struct dsa_switch *ds;
|
||||||
|
int source_port;
|
||||||
|
u8 *brcm_tag;
|
||||||
|
|
||||||
|
if (unlikely(dst == NULL))
|
||||||
|
goto out_drop;
|
||||||
|
|
||||||
|
ds = dst->ds[0];
|
||||||
|
|
||||||
|
skb = skb_unshare(skb, GFP_ATOMIC);
|
||||||
|
if (skb == NULL)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (unlikely(!pskb_may_pull(skb, BRCM_TAG_LEN)))
|
||||||
|
goto out_drop;
|
||||||
|
|
||||||
|
/* skb->data points to the EtherType, the tag is right before it */
|
||||||
|
brcm_tag = skb->data - 2;
|
||||||
|
|
||||||
|
/* The opcode should never be different than 0b000 */
|
||||||
|
if (unlikely((brcm_tag[0] >> BRCM_OPCODE_SHIFT) & BRCM_OPCODE_MASK))
|
||||||
|
goto out_drop;
|
||||||
|
|
||||||
|
/* We should never see a reserved reason code without knowing how to
|
||||||
|
* handle it
|
||||||
|
*/
|
||||||
|
WARN_ON(brcm_tag[2] & BRCM_EG_RC_RSVD);
|
||||||
|
|
||||||
|
/* Locate which port this is coming from */
|
||||||
|
source_port = brcm_tag[3] & BRCM_EG_PID_MASK;
|
||||||
|
|
||||||
|
/* Validate port against switch setup, either the port is totally */
|
||||||
|
if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL)
|
||||||
|
goto out_drop;
|
||||||
|
|
||||||
|
/* Remove Broadcom tag and update checksum */
|
||||||
|
skb_pull_rcsum(skb, BRCM_TAG_LEN);
|
||||||
|
|
||||||
|
/* Move the Ethernet DA and SA */
|
||||||
|
memmove(skb->data - ETH_HLEN,
|
||||||
|
skb->data - ETH_HLEN - BRCM_TAG_LEN,
|
||||||
|
2 * ETH_ALEN);
|
||||||
|
|
||||||
|
skb_push(skb, ETH_HLEN);
|
||||||
|
skb->pkt_type = PACKET_HOST;
|
||||||
|
skb->dev = ds->ports[source_port];
|
||||||
|
skb->protocol = eth_type_trans(skb, skb->dev);
|
||||||
|
|
||||||
|
skb->dev->stats.rx_packets++;
|
||||||
|
skb->dev->stats.rx_bytes += skb->len;
|
||||||
|
|
||||||
|
netif_receive_skb(skb);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_drop:
|
||||||
|
kfree_skb(skb);
|
||||||
|
out:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct dsa_device_ops brcm_netdev_ops = {
|
||||||
|
.xmit = brcm_tag_xmit,
|
||||||
|
.rcv = brcm_tag_rcv,
|
||||||
|
};
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
#define DSA_HLEN 4
|
#define DSA_HLEN 4
|
||||||
|
|
||||||
netdev_tx_t dsa_xmit(struct sk_buff *skb, struct net_device *dev)
|
static netdev_tx_t dsa_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct dsa_slave_priv *p = netdev_priv(dev);
|
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||||
u8 *dsa_header;
|
u8 *dsa_header;
|
||||||
|
@ -186,7 +186,7 @@ out:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct packet_type dsa_packet_type __read_mostly = {
|
const struct dsa_device_ops dsa_netdev_ops = {
|
||||||
.type = cpu_to_be16(ETH_P_DSA),
|
.xmit = dsa_xmit,
|
||||||
.func = dsa_rcv,
|
.rcv = dsa_rcv,
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#define DSA_HLEN 4
|
#define DSA_HLEN 4
|
||||||
#define EDSA_HLEN 8
|
#define EDSA_HLEN 8
|
||||||
|
|
||||||
netdev_tx_t edsa_xmit(struct sk_buff *skb, struct net_device *dev)
|
static netdev_tx_t edsa_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct dsa_slave_priv *p = netdev_priv(dev);
|
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||||
u8 *edsa_header;
|
u8 *edsa_header;
|
||||||
|
@ -205,7 +205,7 @@ out:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct packet_type edsa_packet_type __read_mostly = {
|
const struct dsa_device_ops edsa_netdev_ops = {
|
||||||
.type = cpu_to_be16(ETH_P_EDSA),
|
.xmit = edsa_xmit,
|
||||||
.func = edsa_rcv,
|
.rcv = edsa_rcv,
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include "dsa_priv.h"
|
#include "dsa_priv.h"
|
||||||
|
|
||||||
netdev_tx_t trailer_xmit(struct sk_buff *skb, struct net_device *dev)
|
static netdev_tx_t trailer_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct dsa_slave_priv *p = netdev_priv(dev);
|
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||||
struct sk_buff *nskb;
|
struct sk_buff *nskb;
|
||||||
|
@ -114,7 +114,7 @@ out:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct packet_type trailer_packet_type __read_mostly = {
|
const struct dsa_device_ops trailer_netdev_ops = {
|
||||||
.type = cpu_to_be16(ETH_P_TRAILER),
|
.xmit = trailer_xmit,
|
||||||
.func = trailer_rcv,
|
.rcv = trailer_rcv,
|
||||||
};
|
};
|
||||||
|
|
|
@ -181,11 +181,8 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
|
||||||
* variants has been configured on the receiving interface,
|
* variants has been configured on the receiving interface,
|
||||||
* and if so, set skb->protocol without looking at the packet.
|
* and if so, set skb->protocol without looking at the packet.
|
||||||
*/
|
*/
|
||||||
if (unlikely(netdev_uses_dsa_tags(dev)))
|
if (unlikely(netdev_uses_dsa(dev)))
|
||||||
return htons(ETH_P_DSA);
|
return htons(ETH_P_XDSA);
|
||||||
|
|
||||||
if (unlikely(netdev_uses_trailer_tags(dev)))
|
|
||||||
return htons(ETH_P_TRAILER);
|
|
||||||
|
|
||||||
if (likely(ntohs(eth->h_proto) >= ETH_P_802_3_MIN))
|
if (likely(ntohs(eth->h_proto) >= ETH_P_802_3_MIN))
|
||||||
return eth->h_proto;
|
return eth->h_proto;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue