mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-17 09:14:19 +08: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
@ -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
|
||||
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:
|
||||
|
||||
dsa@0 {
|
||||
@ -58,6 +74,7 @@ Example:
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "lan1";
|
||||
phy-handle = <&phy0>;
|
||||
};
|
||||
|
||||
port@1 {
|
||||
|
@ -36,4 +36,15 @@ config NET_DSA_MV88E6123_61_65
|
||||
This enables support for the Marvell 88E6123/6161/6165
|
||||
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
|
||||
|
@ -7,3 +7,4 @@ endif
|
||||
ifdef CONFIG_NET_DSA_MV88E6131
|
||||
mv88e6xxx_drv-y += mv88e6131.o
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
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_MOXART) += mdio-moxart.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
|
||||
}
|
||||
|
||||
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)
|
||||
return dsa_uses_dsa_tags(dev->dsa_ptr);
|
||||
return dsa_uses_tagged_protocol(dev->dsa_ptr);
|
||||
#endif
|
||||
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1933,6 +1922,13 @@ struct udp_offload {
|
||||
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) */
|
||||
struct pcpu_sw_netstats {
|
||||
u64 rx_packets;
|
||||
|
@ -18,6 +18,9 @@ extern int fixed_phy_register(unsigned int irq,
|
||||
struct fixed_phy_status *status,
|
||||
struct device_node *np);
|
||||
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
|
||||
static inline int fixed_phy_add(unsigned int irq, int phy_id,
|
||||
struct fixed_phy_status *status)
|
||||
@ -34,14 +37,12 @@ static inline int fixed_phy_del(int phy_addr)
|
||||
{
|
||||
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 */
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
|
@ -15,6 +15,14 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/timer.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_PORTS 12
|
||||
@ -26,6 +34,12 @@ struct dsa_chip_data {
|
||||
struct device *mii_bus;
|
||||
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
|
||||
* 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.
|
||||
*/
|
||||
char *port_names[DSA_MAX_PORTS];
|
||||
struct device_node *port_dn[DSA_MAX_PORTS];
|
||||
|
||||
/*
|
||||
* 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_device_ops;
|
||||
|
||||
struct dsa_switch_tree {
|
||||
/*
|
||||
* Configuration data for the platform device that owns
|
||||
@ -71,6 +88,7 @@ struct dsa_switch_tree {
|
||||
* protocol to use.
|
||||
*/
|
||||
struct net_device *master_netdev;
|
||||
const struct dsa_device_ops *ops;
|
||||
__be16 tag_protocol;
|
||||
|
||||
/*
|
||||
@ -119,6 +137,7 @@ struct dsa_switch {
|
||||
*/
|
||||
u32 dsa_port_mask;
|
||||
u32 phys_port_mask;
|
||||
u32 phys_mii_mask;
|
||||
struct mii_bus *slave_mii_bus;
|
||||
struct net_device *ports[DSA_MAX_PORTS];
|
||||
};
|
||||
@ -169,6 +188,14 @@ struct dsa_switch_driver {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@ -186,21 +213,9 @@ static inline void *ds_to_priv(struct dsa_switch *ds)
|
||||
return (void *)(ds + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
static inline bool dsa_uses_tagged_protocol(struct dsa_switch_tree *dst)
|
||||
{
|
||||
return !!(dst->tag_protocol == htons(ETH_P_DSA));
|
||||
}
|
||||
|
||||
static inline bool dsa_uses_trailer_tags(struct dsa_switch_tree *dst)
|
||||
{
|
||||
return !!(dst->tag_protocol == htons(ETH_P_TRAILER));
|
||||
return dst->tag_protocol != 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -128,6 +128,7 @@
|
||||
#define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */
|
||||
#define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */
|
||||
#define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF protocol */
|
||||
#define ETH_P_XDSA 0x00F8 /* Multiplexed DSA protocol */
|
||||
|
||||
/*
|
||||
* This is an Ethernet frame header.
|
||||
|
@ -12,6 +12,9 @@ config NET_DSA
|
||||
if NET_DSA
|
||||
|
||||
# tagging formats
|
||||
config NET_DSA_TAG_BRCM
|
||||
bool
|
||||
|
||||
config NET_DSA_TAG_DSA
|
||||
bool
|
||||
|
||||
|
@ -3,6 +3,7 @@ obj-$(CONFIG_NET_DSA) += dsa_core.o
|
||||
dsa_core-y += dsa.o slave.o
|
||||
|
||||
# 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_EDSA) += tag_edsa.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;
|
||||
}
|
||||
|
||||
/* 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
|
||||
* tagging protocol to the preferred tagging format of this
|
||||
@ -410,6 +415,7 @@ static int dsa_of_probe(struct platform_device *pdev)
|
||||
chip_index++;
|
||||
cd = &pd->chip[chip_index];
|
||||
|
||||
cd->of_node = child;
|
||||
cd->mii_bus = &mdio_bus->dev;
|
||||
|
||||
sw_addr = of_get_property(child, "reg", NULL);
|
||||
@ -431,6 +437,8 @@ static int dsa_of_probe(struct platform_device *pdev)
|
||||
if (!port_name)
|
||||
continue;
|
||||
|
||||
cd->port_dn[port_index] = port;
|
||||
|
||||
cd->port_names[port_index] = kstrdup(port_name,
|
||||
GFP_KERNEL);
|
||||
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[] = {
|
||||
{ .compatible = "brcm,bcm7445-switch-v4.0" },
|
||||
{ .compatible = "marvell,dsa", },
|
||||
{}
|
||||
};
|
||||
@ -633,30 +660,15 @@ static int __init dsa_init_module(void)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
#ifdef CONFIG_NET_DSA_TAG_DSA
|
||||
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
|
||||
dev_add_pack(&dsa_pack_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(dsa_init_module);
|
||||
|
||||
static void __exit dsa_cleanup_module(void)
|
||||
{
|
||||
#ifdef CONFIG_NET_DSA_TAG_TRAILER
|
||||
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
|
||||
dev_remove_pack(&dsa_pack_type);
|
||||
platform_driver_unregister(&dsa_driver);
|
||||
}
|
||||
module_exit(dsa_cleanup_module);
|
||||
|
@ -33,6 +33,10 @@ struct dsa_slave_priv {
|
||||
* to this port.
|
||||
*/
|
||||
struct phy_device *phy;
|
||||
phy_interface_t phy_interface;
|
||||
int old_link;
|
||||
int old_pause;
|
||||
int old_duplex;
|
||||
};
|
||||
|
||||
/* dsa.c */
|
||||
@ -45,16 +49,16 @@ struct net_device *dsa_slave_create(struct dsa_switch *ds,
|
||||
int port, char *name);
|
||||
|
||||
/* tag_dsa.c */
|
||||
netdev_tx_t dsa_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
extern struct packet_type dsa_packet_type;
|
||||
extern const struct dsa_device_ops dsa_netdev_ops;
|
||||
|
||||
/* tag_edsa.c */
|
||||
netdev_tx_t edsa_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
extern struct packet_type edsa_packet_type;
|
||||
extern const struct dsa_device_ops edsa_netdev_ops;
|
||||
|
||||
/* tag_trailer.c */
|
||||
netdev_tx_t trailer_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
extern struct packet_type trailer_packet_type;
|
||||
extern const struct dsa_device_ops trailer_netdev_ops;
|
||||
|
||||
/* tag_brcm.c */
|
||||
extern const struct dsa_device_ops brcm_netdev_ops;
|
||||
|
||||
|
||||
#endif
|
||||
|
165
net/dsa/slave.c
165
net/dsa/slave.c
@ -12,6 +12,8 @@
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include "dsa_priv.h"
|
||||
|
||||
/* 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;
|
||||
|
||||
if (ds->phys_port_mask & (1 << addr))
|
||||
if (ds->phys_mii_mask & (1 << addr))
|
||||
return ds->drv->phy_read(ds, addr, reg);
|
||||
|
||||
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;
|
||||
|
||||
if (ds->phys_port_mask & (1 << addr))
|
||||
if (ds->phys_mii_mask & (1 << addr))
|
||||
return ds->drv->phy_write(ds, addr, reg, val);
|
||||
|
||||
return 0;
|
||||
@ -171,6 +173,25 @@ static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
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 *******************************************************/
|
||||
static int
|
||||
@ -293,44 +314,107 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
|
||||
.get_sset_count = dsa_slave_get_sset_count,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_NET_DSA_TAG_DSA
|
||||
static const struct net_device_ops dsa_netdev_ops = {
|
||||
static const struct net_device_ops dsa_slave_netdev_ops = {
|
||||
.ndo_init = dsa_slave_init,
|
||||
.ndo_open = dsa_slave_open,
|
||||
.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_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_EDSA
|
||||
static const struct net_device_ops edsa_netdev_ops = {
|
||||
.ndo_init = dsa_slave_init,
|
||||
.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,
|
||||
|
||||
static const struct dsa_device_ops notag_netdev_ops = {
|
||||
.xmit = dsa_slave_notag_xmit,
|
||||
.rcv = NULL,
|
||||
};
|
||||
#endif
|
||||
#ifdef CONFIG_NET_DSA_TAG_TRAILER
|
||||
static const struct net_device_ops trailer_netdev_ops = {
|
||||
.ndo_init = dsa_slave_init,
|
||||
.ndo_open = dsa_slave_open,
|
||||
.ndo_stop = dsa_slave_close,
|
||||
.ndo_start_xmit = trailer_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
|
||||
|
||||
static void dsa_slave_adjust_link(struct net_device *dev)
|
||||
{
|
||||
struct dsa_slave_priv *p = netdev_priv(dev);
|
||||
struct dsa_switch *ds = p->parent;
|
||||
unsigned int status_changed = 0;
|
||||
|
||||
if (p->old_link != p->phy->link) {
|
||||
status_changed = 1;
|
||||
p->old_link = p->phy->link;
|
||||
}
|
||||
|
||||
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 *******************************************************/
|
||||
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 *
|
||||
dsa_slave_create(struct dsa_switch *ds, struct device *parent,
|
||||
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;
|
||||
eth_hw_addr_inherit(slave_dev, master);
|
||||
slave_dev->tx_queue_len = 0;
|
||||
slave_dev->netdev_ops = &dsa_slave_netdev_ops;
|
||||
|
||||
switch (ds->dst->tag_protocol) {
|
||||
#ifdef CONFIG_NET_DSA_TAG_DSA
|
||||
case htons(ETH_P_DSA):
|
||||
slave_dev->netdev_ops = &dsa_netdev_ops;
|
||||
ds->dst->ops = &dsa_netdev_ops;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_NET_DSA_TAG_EDSA
|
||||
case htons(ETH_P_EDSA):
|
||||
slave_dev->netdev_ops = &edsa_netdev_ops;
|
||||
ds->dst->ops = &edsa_netdev_ops;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_NET_DSA_TAG_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;
|
||||
#endif
|
||||
default:
|
||||
BUG();
|
||||
ds->dst->ops = ¬ag_netdev_ops;
|
||||
break;
|
||||
}
|
||||
|
||||
SET_NETDEV_DEV(slave_dev, parent);
|
||||
slave_dev->dev.of_node = ds->pd->port_dn[port];
|
||||
slave_dev->vlan_features = master->vlan_features;
|
||||
|
||||
p = netdev_priv(slave_dev);
|
||||
p->dev = slave_dev;
|
||||
p->parent = ds;
|
||||
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);
|
||||
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
|
||||
|
||||
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);
|
||||
u8 *dsa_header;
|
||||
@ -186,7 +186,7 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct packet_type dsa_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_DSA),
|
||||
.func = dsa_rcv,
|
||||
const struct dsa_device_ops dsa_netdev_ops = {
|
||||
.xmit = dsa_xmit,
|
||||
.rcv = dsa_rcv,
|
||||
};
|
||||
|
@ -17,7 +17,7 @@
|
||||
#define DSA_HLEN 4
|
||||
#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);
|
||||
u8 *edsa_header;
|
||||
@ -205,7 +205,7 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct packet_type edsa_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_EDSA),
|
||||
.func = edsa_rcv,
|
||||
const struct dsa_device_ops edsa_netdev_ops = {
|
||||
.xmit = edsa_xmit,
|
||||
.rcv = edsa_rcv,
|
||||
};
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include <linux/slab.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 sk_buff *nskb;
|
||||
@ -114,7 +114,7 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct packet_type trailer_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_TRAILER),
|
||||
.func = trailer_rcv,
|
||||
const struct dsa_device_ops trailer_netdev_ops = {
|
||||
.xmit = trailer_xmit,
|
||||
.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,
|
||||
* and if so, set skb->protocol without looking at the packet.
|
||||
*/
|
||||
if (unlikely(netdev_uses_dsa_tags(dev)))
|
||||
return htons(ETH_P_DSA);
|
||||
|
||||
if (unlikely(netdev_uses_trailer_tags(dev)))
|
||||
return htons(ETH_P_TRAILER);
|
||||
if (unlikely(netdev_uses_dsa(dev)))
|
||||
return htons(ETH_P_XDSA);
|
||||
|
||||
if (likely(ntohs(eth->h_proto) >= ETH_P_802_3_MIN))
|
||||
return eth->h_proto;
|
||||
|
Loading…
Reference in New Issue
Block a user