* [RFC net-next 01/11] net: phy: add generic UniMAC MDIO bus driver
2014-05-14 5:00 [RFC net-next 00/11] net: dsa: add support for Broadcom Starfighter 2 Florian Fainelli
@ 2014-05-14 5:00 ` Florian Fainelli
2014-05-14 5:00 ` [RFC net-next 02/11] net: dsa: add Broadcom tag hook Florian Fainelli
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Florian Fainelli @ 2014-05-14 5:00 UTC (permalink / raw)
To: netdev
Cc: davem, Florian Fainelli, nhorman, andy, tgraf, dborkman, ogerlitz,
jesse, pshelar, azhou, ben, stephen, jeffrey.t.kirsher, vyasevic,
xiyou.wangcong, john.r.fastabend, edumazet, jhs, sfeldma, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram
Add a generic UniMAC MDIO bus driver which can be used by the BCMGENET
driver as-is, and with some extend by the Starfighter 2 MDIO bus
controller.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
.../bindings/net/broadcom-mdio-unimac.txt | 33 ++++
drivers/net/phy/Kconfig | 6 +
drivers/net/phy/Makefile | 1 +
drivers/net/phy/mdio-unimac.c | 210 +++++++++++++++++++++
4 files changed, 250 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/broadcom-mdio-unimac.txt
create mode 100644 drivers/net/phy/mdio-unimac.c
diff --git a/Documentation/devicetree/bindings/net/broadcom-mdio-unimac.txt b/Documentation/devicetree/bindings/net/broadcom-mdio-unimac.txt
new file mode 100644
index 000000000000..ac5767552960
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/broadcom-mdio-unimac.txt
@@ -0,0 +1,33 @@
+* Broadcom UniMAC MDIO bus controller
+
+Required properties:
+- compatible: should be "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
+- 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 two interrupts, one for MDIO done, one for MDIO error
+- interrupt-names: must be "mdio_done" for the first interrupt and "mdio_error"
+ for the second
+
+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>;
+ };
+};
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 6a17f92153b3..6ad46e27b8ca 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -199,6 +199,12 @@ config MDIO_BUS_MUX_MMIOREG
Currently, only 8-bit registers are supported.
+config MDIO_UNIMAC
+ tristate "Broadcom UniMAC MDIO bus controller"
+ help
+ This module provides a driver for the Broadcom UniMAC MDIO busses.
+ It is required by the Broadcom GENET Ethernet device drivers.
+
endif # PHYLIB
config MICREL_KS8995MA
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 07d24024863e..0f132ebd16f5 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -33,3 +33,4 @@ obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o
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_MDIO_UNIMAC) += mdio-unimac.o
diff --git a/drivers/net/phy/mdio-unimac.c b/drivers/net/phy/mdio-unimac.c
new file mode 100644
index 000000000000..fb59e8049c41
--- /dev/null
+++ b/drivers/net/phy/mdio-unimac.c
@@ -0,0 +1,210 @@
+/*
+ * 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;
+ unsigned int complete_handler;
+};
+
+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 = kzalloc(sizeof(int) * PHY_MAX_ADDR, 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,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");
--
1.9.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [RFC net-next 02/11] net: dsa: add Broadcom tag hook
2014-05-14 5:00 [RFC net-next 00/11] net: dsa: add support for Broadcom Starfighter 2 Florian Fainelli
2014-05-14 5:00 ` [RFC net-next 01/11] net: phy: add generic UniMAC MDIO bus driver Florian Fainelli
@ 2014-05-14 5:00 ` Florian Fainelli
2014-05-14 5:00 ` [RFC net-next 03/11] net: dsa: provide a switch device device tree node pointer Florian Fainelli
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Florian Fainelli @ 2014-05-14 5:00 UTC (permalink / raw)
To: netdev
Cc: davem, Florian Fainelli, nhorman, andy, tgraf, dborkman, ogerlitz,
jesse, pshelar, azhou, ben, stephen, jeffrey.t.kirsher, vyasevic,
xiyou.wangcong, john.r.fastabend, edumazet, jhs, sfeldma, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram
Register a fake Ethertype for the Broadcom tag to allow us to hook into
a given Ethernet device receive path and parse this Broadcom tag.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
include/linux/netdevice.h | 10 ++++++++++
include/net/dsa.h | 5 +++++
include/uapi/linux/if_ether.h | 1 +
net/ethernet/eth.c | 2 ++
4 files changed, 18 insertions(+)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 2dea98cbbdba..323ba253ce4b 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1675,6 +1675,16 @@ static inline bool netdev_uses_trailer_tags(struct net_device *dev)
return 0;
}
+static inline bool netdev_uses_brcm_tags(struct net_device *dev)
+{
+#ifdef CONFIG_NET_DSA_TAG_BRCM
+ if (dev->dsa_ptr != NULL)
+ return dsa_uses_brcm_tags(dev->dsa_ptr);
+#endif
+
+ return 0;
+}
+
/**
* netdev_priv - access network device private data
* @dev: network device
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 6efce384451e..0ada9c814ff9 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -203,4 +203,9 @@ static inline bool dsa_uses_trailer_tags(struct dsa_switch_tree *dst)
return !!(dst->tag_protocol == htons(ETH_P_TRAILER));
}
+static inline bool dsa_uses_brcm_tags(struct dsa_switch_tree *dst)
+{
+ return !!(dst->tag_protocol == htons(ETH_P_BRCMTAG));
+}
+
#endif
diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
index 0f8210b8e0bc..2e9f13768abd 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -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_BRCMTAG 0x00F8 /* Broadcom tag (4 bytes *) */
/*
* This is an Ethernet frame header.
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index 5dc638cad2e1..a7a02547f9bb 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -186,6 +186,8 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
if (unlikely(netdev_uses_trailer_tags(dev)))
return htons(ETH_P_TRAILER);
+ if (netdev_uses_brcm_tags(dev))
+ return htons(ETH_P_BRCMTAG);
if (likely(ntohs(eth->h_proto) >= ETH_P_802_3_MIN))
return eth->h_proto;
--
1.9.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [RFC net-next 03/11] net: dsa: provide a switch device device tree node pointer
2014-05-14 5:00 [RFC net-next 00/11] net: dsa: add support for Broadcom Starfighter 2 Florian Fainelli
2014-05-14 5:00 ` [RFC net-next 01/11] net: phy: add generic UniMAC MDIO bus driver Florian Fainelli
2014-05-14 5:00 ` [RFC net-next 02/11] net: dsa: add Broadcom tag hook Florian Fainelli
@ 2014-05-14 5:00 ` Florian Fainelli
2014-05-14 5:00 ` [RFC net-next 04/11] net: dsa: retain a per-port device_node pointer Florian Fainelli
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Florian Fainelli @ 2014-05-14 5:00 UTC (permalink / raw)
To: netdev
Cc: davem, Florian Fainelli, nhorman, andy, tgraf, dborkman, ogerlitz,
jesse, pshelar, azhou, ben, stephen, jeffrey.t.kirsher, vyasevic,
xiyou.wangcong, john.r.fastabend, edumazet, jhs, sfeldma, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram
We might need to fetch additional resources from the device tree node
pointer, such as register ranges or other properties. Keep a device_node
pointer around for this.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
include/net/dsa.h | 7 +++++++
net/dsa/dsa.c | 1 +
2 files changed, 8 insertions(+)
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 0ada9c814ff9..9a16c5290738 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -15,6 +15,7 @@
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
+#include <linux/of.h>
#define DSA_MAX_SWITCHES 4
#define DSA_MAX_PORTS 12
@@ -26,6 +27,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,
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 0eb5d5e76dfb..7808dbceff96 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -410,6 +410,7 @@ static int dsa_of_probe(struct platform_device *pdev)
for_each_available_child_of_node(np, child) {
cd = &pd->chip[chip_index];
+ cd->of_node = child;
cd->mii_bus = &mdio_bus->dev;
sw_addr = of_get_property(child, "reg", NULL);
--
1.9.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [RFC net-next 04/11] net: dsa: retain a per-port device_node pointer
2014-05-14 5:00 [RFC net-next 00/11] net: dsa: add support for Broadcom Starfighter 2 Florian Fainelli
` (2 preceding siblings ...)
2014-05-14 5:00 ` [RFC net-next 03/11] net: dsa: provide a switch device device tree node pointer Florian Fainelli
@ 2014-05-14 5:00 ` Florian Fainelli
2014-05-14 5:00 ` [RFC net-next 05/11] net: dsa: allow for more complex PHY setups Florian Fainelli
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Florian Fainelli @ 2014-05-14 5:00 UTC (permalink / raw)
To: netdev
Cc: davem, Florian Fainelli, nhorman, andy, tgraf, dborkman, ogerlitz,
jesse, pshelar, azhou, ben, stephen, jeffrey.t.kirsher, vyasevic,
xiyou.wangcong, john.r.fastabend, edumazet, jhs, sfeldma, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram
We will later use the per-port device_node pointer to fetch a bunch of
port-specific properties.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
include/net/dsa.h | 1 +
net/dsa/dsa.c | 2 ++
2 files changed, 3 insertions(+)
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 9a16c5290738..c435eaa772d5 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -41,6 +41,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]
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 7808dbceff96..ac05d55dfdd3 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -432,6 +432,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]) {
--
1.9.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [RFC net-next 05/11] net: dsa: allow for more complex PHY setups
2014-05-14 5:00 [RFC net-next 00/11] net: dsa: add support for Broadcom Starfighter 2 Florian Fainelli
` (3 preceding siblings ...)
2014-05-14 5:00 ` [RFC net-next 04/11] net: dsa: retain a per-port device_node pointer Florian Fainelli
@ 2014-05-14 5:00 ` Florian Fainelli
2014-05-14 5:00 ` [RFC net-next 06/11] net: dsa: allow switches to working without tagging Florian Fainelli
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Florian Fainelli @ 2014-05-14 5:00 UTC (permalink / raw)
To: netdev
Cc: davem, Florian Fainelli, nhorman, andy, tgraf, dborkman, ogerlitz,
jesse, pshelar, azhou, ben, stephen, jeffrey.t.kirsher, vyasevic,
xiyou.wangcong, john.r.fastabend, edumazet, jhs, sfeldma, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram
Modify the DSA slave interface to be bound to an arbitray PHY, not just
the ones that are available as child PHY devices of the switch MDIO bus.
This allows us for instance to have external PHYs connected to a
separate MDIO bus, but yet also connected to a given switch port.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
net/dsa/dsa_priv.h | 4 ++++
net/dsa/slave.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 62 insertions(+), 1 deletion(-)
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index d4cf5cc747e3..a7f6f0c5fa31 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -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 */
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 64c5af0a10dd..d2dbd271300a 100644
--- a/net/dsa/slave.c
+++ b/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 ***************************************************/
@@ -330,7 +332,60 @@ static const struct net_device_ops trailer_netdev_ops = {
};
#endif
+static void dsa_slave_adjust_link(struct net_device *dev)
+{
+ struct dsa_slave_priv *p = netdev_priv(dev);
+ 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 (status_changed)
+ phy_print_status(p->phy);
+}
+
/* 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;
+
+ p->phy_interface = of_get_phy_mode(cd->port_dn[p->port]);
+
+ phy_dn = of_parse_phandle(cd->port_dn[p->port], "phy-handle", 0);
+ if (phy_dn)
+ p->phy = of_phy_connect(slave_dev, phy_dn,
+ dsa_slave_adjust_link, 0,
+ p->phy_interface);
+ else
+ p->phy = of_phy_connect_fixed_link(slave_dev,
+ dsa_slave_adjust_link,
+ p->phy_interface);
+
+ /* 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)
@@ -370,6 +425,7 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
BUG();
}
+ parent->of_node = ds->pd->port_dn[port];
SET_NETDEV_DEV(slave_dev, parent);
slave_dev->vlan_features = master->vlan_features;
@@ -377,7 +433,8 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
p->dev = slave_dev;
p->parent = ds;
p->port = port;
- p->phy = ds->slave_mii_bus->phy_map[port];
+
+ dsa_slave_phy_setup(p, slave_dev);
ret = register_netdev(slave_dev);
if (ret) {
--
1.9.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [RFC net-next 06/11] net: dsa: allow switches to working without tagging
2014-05-14 5:00 [RFC net-next 00/11] net: dsa: add support for Broadcom Starfighter 2 Florian Fainelli
` (4 preceding siblings ...)
2014-05-14 5:00 ` [RFC net-next 05/11] net: dsa: allow for more complex PHY setups Florian Fainelli
@ 2014-05-14 5:00 ` Florian Fainelli
2014-05-14 5:00 ` [RFC net-next 07/11] net: dsa: allow drivers to do link adjustment Florian Fainelli
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Florian Fainelli @ 2014-05-14 5:00 UTC (permalink / raw)
To: netdev
Cc: davem, Florian Fainelli, nhorman, andy, tgraf, dborkman, ogerlitz,
jesse, pshelar, azhou, ben, stephen, jeffrey.t.kirsher, vyasevic,
xiyou.wangcong, john.r.fastabend, edumazet, jhs, sfeldma, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram
In case switch port tagging is disabled, allow us to continue using the
master net_device operations instead of the tag-specific ones.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
net/dsa/slave.c | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index d2dbd271300a..7b190b6f64e4 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -332,6 +332,27 @@ static const struct net_device_ops trailer_netdev_ops = {
};
#endif
+static netdev_tx_t dsa_slave_dummy_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;
+}
+
+static const struct net_device_ops dummy_netdev_ops = {
+ .ndo_init = dsa_slave_init,
+ .ndo_open = dsa_slave_open,
+ .ndo_stop = dsa_slave_close,
+ .ndo_start_xmit = dsa_slave_dummy_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 void dsa_slave_adjust_link(struct net_device *dev)
{
struct dsa_slave_priv *p = netdev_priv(dev);
@@ -422,7 +443,8 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
break;
#endif
default:
- BUG();
+ slave_dev->netdev_ops = &dummy_netdev_ops;
+ break;
}
parent->of_node = ds->pd->port_dn[port];
--
1.9.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [RFC net-next 07/11] net: dsa: allow drivers to do link adjustment
2014-05-14 5:00 [RFC net-next 00/11] net: dsa: add support for Broadcom Starfighter 2 Florian Fainelli
` (5 preceding siblings ...)
2014-05-14 5:00 ` [RFC net-next 06/11] net: dsa: allow switches to working without tagging Florian Fainelli
@ 2014-05-14 5:00 ` Florian Fainelli
2014-05-14 5:00 ` [RFC net-next 08/11] net: dsa: allow updating fixed PHY link information Florian Fainelli
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Florian Fainelli @ 2014-05-14 5:00 UTC (permalink / raw)
To: netdev
Cc: davem, Florian Fainelli, nhorman, andy, tgraf, dborkman, ogerlitz,
jesse, pshelar, azhou, ben, stephen, jeffrey.t.kirsher, vyasevic,
xiyou.wangcong, john.r.fastabend, edumazet, jhs, sfeldma, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram
Whenever libphy determines that the link status of a given PHY/port has
changed, allow to call into the switch driver link adjustment callback
so proper actions can be taken care of by the switch driver.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
include/net/dsa.h | 7 +++++++
net/dsa/slave.c | 4 ++++
2 files changed, 11 insertions(+)
diff --git a/include/net/dsa.h b/include/net/dsa.h
index c435eaa772d5..a19bda9e260b 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -16,6 +16,7 @@
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/of.h>
+#include <linux/phy.h>
#define DSA_MAX_SWITCHES 4
#define DSA_MAX_PORTS 12
@@ -178,6 +179,12 @@ 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);
+
+ /*
* ethtool hardware statistics.
*/
void (*get_strings)(struct dsa_switch *ds, int port, uint8_t *data);
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 7b190b6f64e4..8ed94dab1867 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -356,6 +356,7 @@ static const struct net_device_ops dummy_netdev_ops = {
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) {
@@ -373,6 +374,9 @@ static void dsa_slave_adjust_link(struct net_device *dev)
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);
}
--
1.9.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [RFC net-next 08/11] net: dsa: allow updating fixed PHY link information
2014-05-14 5:00 [RFC net-next 00/11] net: dsa: add support for Broadcom Starfighter 2 Florian Fainelli
` (6 preceding siblings ...)
2014-05-14 5:00 ` [RFC net-next 07/11] net: dsa: allow drivers to do link adjustment Florian Fainelli
@ 2014-05-14 5:00 ` Florian Fainelli
2014-05-14 5:00 ` [RFC net-next 09/11] net: dsa: add Broadcom SF2 switch driver Florian Fainelli
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Florian Fainelli @ 2014-05-14 5:00 UTC (permalink / raw)
To: netdev
Cc: davem, Florian Fainelli, nhorman, andy, tgraf, dborkman, ogerlitz,
jesse, pshelar, azhou, ben, stephen, jeffrey.t.kirsher, vyasevic,
xiyou.wangcong, john.r.fastabend, edumazet, jhs, sfeldma, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram
Allow switch drivers to hook a PHY link update callback to perform
port-specific link work.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
include/net/dsa.h | 3 +++
net/dsa/slave.c | 18 +++++++++++++++++-
2 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/include/net/dsa.h b/include/net/dsa.h
index a19bda9e260b..1aa40a0491d4 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -17,6 +17,7 @@
#include <linux/workqueue.h>
#include <linux/of.h>
#include <linux/phy.h>
+#include <linux/phy_fixed.h>
#define DSA_MAX_SWITCHES 4
#define DSA_MAX_PORTS 12
@@ -183,6 +184,8 @@ struct dsa_switch_driver {
*/
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.
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 8ed94dab1867..791b0899e7ee 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -381,6 +381,18 @@ static void dsa_slave_adjust_link(struct net_device *dev)
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)
@@ -396,10 +408,14 @@ static void dsa_slave_phy_setup(struct dsa_slave_priv *p,
p->phy = of_phy_connect(slave_dev, phy_dn,
dsa_slave_adjust_link, 0,
p->phy_interface);
- else
+ else {
p->phy = of_phy_connect_fixed_link(slave_dev,
dsa_slave_adjust_link,
p->phy_interface);
+ if (p->phy)
+ 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
--
1.9.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [RFC net-next 09/11] net: dsa: add Broadcom SF2 switch driver
2014-05-14 5:00 [RFC net-next 00/11] net: dsa: add support for Broadcom Starfighter 2 Florian Fainelli
` (7 preceding siblings ...)
2014-05-14 5:00 ` [RFC net-next 08/11] net: dsa: allow updating fixed PHY link information Florian Fainelli
@ 2014-05-14 5:00 ` Florian Fainelli
2014-05-14 5:00 ` [RFC net-next 10/11] net: dsa: add Broadcom tag RX/TX handler Florian Fainelli
2014-05-14 5:00 ` [RFC net-next 11/11] net: dsa: bcm_sf2: enable Broadcom tags Florian Fainelli
10 siblings, 0 replies; 12+ messages in thread
From: Florian Fainelli @ 2014-05-14 5:00 UTC (permalink / raw)
To: netdev
Cc: davem, Florian Fainelli, nhorman, andy, tgraf, dborkman, ogerlitz,
jesse, pshelar, azhou, ben, stephen, jeffrey.t.kirsher, vyasevic,
xiyou.wangcong, john.r.fastabend, edumazet, jhs, sfeldma, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram
Add basic support for the Broadcom Starfigther 2 switch chip using a DSA
driver. For now we just do the basic initialization to put the switch
into a forwarding mode as well as allowing reading its per-port MIB
counters.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/dsa/Kconfig | 10 +
drivers/net/dsa/Makefile | 1 +
drivers/net/dsa/bcm_sf2.c | 482 +++++++++++++++++++++++++++++++++++++++++
drivers/net/dsa/bcm_sf2.h | 135 ++++++++++++
drivers/net/dsa/bcm_sf2_regs.h | 167 ++++++++++++++
5 files changed, 795 insertions(+)
create mode 100644 drivers/net/dsa/bcm_sf2.c
create mode 100644 drivers/net/dsa/bcm_sf2.h
create mode 100644 drivers/net/dsa/bcm_sf2_regs.h
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index b8fe808b7957..9b51960a4227 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -36,4 +36,14 @@ 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 FIXED_PHY
+ select BCM7XXX_PHY
+ select MDIO_UNIMAC
+ ---help---
+ This enables support for the Broadcom Starfighter 2 ethernet
+ switch chips.
+
endmenu
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index f3bda05536cc..dd3cd3b8157f 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -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
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
new file mode 100644
index 000000000000..1f8ac8d45c90
--- /dev/null
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -0,0 +1,482 @@
+/*
+ * 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);
+ u32 reg, val;
+
+ /* 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 and managed mode */
+ core_writel(priv, SW_FWDG_EN | SW_FWDG_MODE, CORE_SWMODE);
+
+ /* 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;
+ }
+
+ /* Disable Broadcom tags for IMP port */
+ reg = core_readl(priv, CORE_BRCM_HDR_CTRL);
+ reg &= ~val;
+ core_writel(priv, reg, CORE_BRCM_HDR_CTRL);
+
+ /* 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);
+}
+
+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 Rx and Tx disable bits and set to no spanning tree */
+ core_writel(priv, 0, CORE_G_PCTL_PORT(port));
+
+ /* Reset port status override */
+ reg = core_readl(priv, CORE_STS_OVERRIDE_GMIIP_PORT(port));
+ reg &= ~SW_OVERRIDE;
+ core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port));
+
+ /* Enable port 7 interrupts to get notified */
+ if (port == 7)
+ intrl2_1_mask_clear(priv, P_IRQ_MASK(P7_IRQ_OFF));
+}
+
+static void bcm_sf2_port_disable(struct dsa_switch *ds, int port)
+{
+ struct bcm_sf2_priv *priv = ds_to_priv(ds);
+
+ /* Disable the port since it is unused */
+ core_writel(priv, RX_DIS | TX_DIS, CORE_G_PCTL_PORT(port));
+}
+
+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_unmap;
+ }
+
+ /* 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);
+
+ /* Enable all valid ports and disable those unused */
+ for (port = 0; port < DSA_MAX_PORTS; port++) {
+ /* IMP port receives special treatment */
+ if (dsa_is_cpu_port(ds, port))
+ bcm_sf2_imp_setup(ds, port);
+ if ((1 << port) & ds->phys_port_mask)
+ bcm_sf2_port_setup(ds, port);
+ else
+ bcm_sf2_port_disable(ds, port);
+ }
+
+ 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("Starfigther 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_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_phy_read(struct dsa_switch *ds, int port, int regnum)
+{
+ return 0;
+}
+
+static int bcm_sf2_sw_phy_write(struct dsa_switch *ds, int port, int regnum,
+ u16 val)
+{
+ 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:
+ return;
+ }
+
+ /* 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);
+}
+
+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));
+ } 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");
diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h
new file mode 100644
index 000000000000..72e165bf229c
--- /dev/null
+++ b/drivers/net/dsa/bcm_sf2.h
@@ -0,0 +1,135 @@
+/*
+ * 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;
+ u8 num_acb_queue;
+ u8 num_gphy;
+ u8 num_rgmii;
+ 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_t indir_lock;
+
+ int irq0;
+ int irq1;
+ u32 irq0_stat;
+ u32 irq0_mask;
+ u32 irq1_stat;
+ u32 irq1_mask;
+
+ 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 */
diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h
new file mode 100644
index 000000000000..e183cac3d7c8
--- /dev/null
+++ b/drivers/net/dsa/bcm_sf2_regs.h
@@ -0,0 +1,167 @@
+/*
+ * Broadcom Starfigther 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 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_STS_OVERRIDE_GMIIP_PORT(x) (0x160 + (x) * 4)
+#define LINK_STS (1 << 0)
+#define DUPLX_MODE (1 << 1)
+#define SPEED_SHIFT (1 << 2)
+#define SPEED_MASK 0x3
+#define RXFLOW_CNTL (1 << 4)
+#define TXFLOW_CNTL (1 << 5)
+#define SW_OVERRIDE (1 << 6)
+
+#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_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_RX_DIS 0x0980
+#define CORE_BRCM_HDR_TX_DIS 0x0988
+
+#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)
+
+#endif /* __BCM_SF2_REGS_H */
--
1.9.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [RFC net-next 10/11] net: dsa: add Broadcom tag RX/TX handler
2014-05-14 5:00 [RFC net-next 00/11] net: dsa: add support for Broadcom Starfighter 2 Florian Fainelli
` (8 preceding siblings ...)
2014-05-14 5:00 ` [RFC net-next 09/11] net: dsa: add Broadcom SF2 switch driver Florian Fainelli
@ 2014-05-14 5:00 ` Florian Fainelli
2014-05-14 5:00 ` [RFC net-next 11/11] net: dsa: bcm_sf2: enable Broadcom tags Florian Fainelli
10 siblings, 0 replies; 12+ messages in thread
From: Florian Fainelli @ 2014-05-14 5:00 UTC (permalink / raw)
To: netdev
Cc: davem, Florian Fainelli, nhorman, andy, tgraf, dborkman, ogerlitz,
jesse, pshelar, azhou, ben, stephen, jeffrey.t.kirsher, vyasevic,
xiyou.wangcong, john.r.fastabend, edumazet, jhs, sfeldma, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram
Add support for the 4-bytes Broadcom tag that built-in switches such as
the Starfighter 2 might insert when receiving packets, or that we need
to insert while targetting specific switch ports.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
net/dsa/Kconfig | 3 +
net/dsa/Makefile | 1 +
net/dsa/dsa.c | 6 ++
net/dsa/dsa_priv.h | 4 ++
net/dsa/slave.c | 17 ++++++
net/dsa/tag_brcm.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 201 insertions(+)
create mode 100644 net/dsa/tag_brcm.c
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index f5eede1d6cb8..a585fd6352eb 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -12,6 +12,9 @@ config NET_DSA
if NET_DSA
# tagging formats
+config NET_DSA_TAG_BRCM
+ bool
+
config NET_DSA_TAG_DSA
bool
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index 7b9fcbbeda5d..da06ed1df620 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -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
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index ac05d55dfdd3..d38570f7c6d8 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -645,6 +645,9 @@ static int __init dsa_init_module(void)
#ifdef CONFIG_NET_DSA_TAG_TRAILER
dev_add_pack(&trailer_packet_type);
#endif
+#ifdef CONFIG_NET_DSA_TAG_BRCM
+ dev_add_pack(&brcm_tag_packet_type);
+#endif
return 0;
}
module_init(dsa_init_module);
@@ -660,6 +663,9 @@ static void __exit dsa_cleanup_module(void)
#ifdef CONFIG_NET_DSA_TAG_DSA
dev_remove_pack(&dsa_packet_type);
#endif
+#ifdef CONFIG_NET_DSA_TAG_BRCM
+ dev_remove_pack(&brcm_tag_packet_type);
+#endif
platform_driver_unregister(&dsa_driver);
}
module_exit(dsa_cleanup_module);
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index a7f6f0c5fa31..8fb71da70dd6 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -60,5 +60,9 @@ extern struct packet_type edsa_packet_type;
netdev_tx_t trailer_xmit(struct sk_buff *skb, struct net_device *dev);
extern struct packet_type trailer_packet_type;
+/* tag_brcm.c */
+netdev_tx_t brcm_tag_xmit(struct sk_buff *skb, struct net_device *dev);
+extern struct packet_type brcm_tag_packet_type;
+
#endif
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 791b0899e7ee..e08d6664e882 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -295,6 +295,18 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
.get_sset_count = dsa_slave_get_sset_count,
};
+#ifdef CONFIG_NET_DSA_TAG_BRCM
+static const struct net_device_ops brcm_netdev_ops = {
+ .ndo_init = dsa_slave_init,
+ .ndo_open = dsa_slave_open,
+ .ndo_stop = dsa_slave_close,
+ .ndo_start_xmit = brcm_tag_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_DSA
static const struct net_device_ops dsa_netdev_ops = {
.ndo_init = dsa_slave_init,
@@ -462,6 +474,11 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
slave_dev->netdev_ops = &trailer_netdev_ops;
break;
#endif
+#ifdef CONFIG_NET_DSA_TAG_BRCM
+ case htons(ETH_P_BRCMTAG):
+ slave_dev->netdev_ops = &brcm_netdev_ops;
+ break;
+#endif
default:
slave_dev->netdev_ops = &dummy_netdev_ops;
break;
diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c
new file mode 100644
index 000000000000..f2a7c9244626
--- /dev/null
+++ b/net/dsa/tag_brcm.c
@@ -0,0 +1,170 @@
+/*
+ * 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
+
+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 = dst->ds[0];
+ int source_port;
+ u8 *brcm_tag;
+
+ if (unlikely(dst == NULL))
+ goto out_drop;
+
+ 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;
+}
+
+struct packet_type brcm_tag_packet_type __read_mostly = {
+ .type = cpu_to_be16(ETH_P_BRCMTAG),
+ .func = brcm_tag_rcv,
+};
--
1.9.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [RFC net-next 11/11] net: dsa: bcm_sf2: enable Broadcom tags
2014-05-14 5:00 [RFC net-next 00/11] net: dsa: add support for Broadcom Starfighter 2 Florian Fainelli
` (9 preceding siblings ...)
2014-05-14 5:00 ` [RFC net-next 10/11] net: dsa: add Broadcom tag RX/TX handler Florian Fainelli
@ 2014-05-14 5:00 ` Florian Fainelli
10 siblings, 0 replies; 12+ messages in thread
From: Florian Fainelli @ 2014-05-14 5:00 UTC (permalink / raw)
To: netdev
Cc: davem, Florian Fainelli, nhorman, andy, tgraf, dborkman, ogerlitz,
jesse, pshelar, azhou, ben, stephen, jeffrey.t.kirsher, vyasevic,
xiyou.wangcong, john.r.fastabend, edumazet, jhs, sfeldma, roopa,
linville, jasowang, ebiederm, nicolas.dichtel, ryazanov.s.a,
buytenh, aviadr, nbd, alexei.starovoitov, Neil.Jerram
Select the Broadcom tag mangler/unmangler and configure the IMP port of
the switch to dispatch the frames to/from the per-port network devices.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/dsa/Kconfig | 1 +
drivers/net/dsa/bcm_sf2.c | 19 +++++++++++++++++--
drivers/net/dsa/bcm_sf2_regs.h | 40 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 58 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 9b51960a4227..d1d049682a8c 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -39,6 +39,7 @@ config NET_DSA_MV88E6123_61_65
config NET_DSA_BCM_SF2
tristate "Broadcom Starfighter 2 Ethernet switch support"
select NET_DSA
+ select NET_DSA_TAG_BRCM
select FIXED_PHY
select BCM7XXX_PHY
select MDIO_UNIMAC
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index 1f8ac8d45c90..110ea7993c3c 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -164,15 +164,30 @@ static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port)
break;
}
- /* Disable Broadcom tags for IMP port */
+ /* Enable Broadcom tags for IMP port */
reg = core_readl(priv, CORE_BRCM_HDR_CTRL);
- reg &= ~val;
+ 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);
+
}
static void bcm_sf2_port_setup(struct dsa_switch *ds, int port)
diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h
index e183cac3d7c8..92a59fb4eec3 100644
--- a/drivers/net/dsa/bcm_sf2_regs.h
+++ b/drivers/net/dsa/bcm_sf2_regs.h
@@ -127,6 +127,19 @@
#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_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)
@@ -152,11 +165,38 @@
#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_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
--
1.9.1
^ permalink raw reply related [flat|nested] 12+ messages in thread