|
@@ -16,6 +16,7 @@
|
|
#include <linux/platform_device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/phy_fixed.h>
|
|
#include <linux/phy_fixed.h>
|
|
|
|
+#include <linux/phylink.h>
|
|
#include <linux/mii.h>
|
|
#include <linux/mii.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_irq.h>
|
|
@@ -306,7 +307,8 @@ static int bcm_sf2_sw_mdio_write(struct mii_bus *bus, int addr, int regnum,
|
|
|
|
|
|
static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
|
|
static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
|
|
{
|
|
{
|
|
- struct bcm_sf2_priv *priv = dev_id;
|
|
|
|
|
|
+ struct dsa_switch *ds = dev_id;
|
|
|
|
+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
|
|
|
|
|
|
priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) &
|
|
priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) &
|
|
~priv->irq0_mask;
|
|
~priv->irq0_mask;
|
|
@@ -317,16 +319,21 @@ static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
|
|
|
|
|
|
static irqreturn_t bcm_sf2_switch_1_isr(int irq, void *dev_id)
|
|
static irqreturn_t bcm_sf2_switch_1_isr(int irq, void *dev_id)
|
|
{
|
|
{
|
|
- struct bcm_sf2_priv *priv = dev_id;
|
|
|
|
|
|
+ struct dsa_switch *ds = dev_id;
|
|
|
|
+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
|
|
|
|
|
|
priv->irq1_stat = intrl2_1_readl(priv, INTRL2_CPU_STATUS) &
|
|
priv->irq1_stat = intrl2_1_readl(priv, INTRL2_CPU_STATUS) &
|
|
~priv->irq1_mask;
|
|
~priv->irq1_mask;
|
|
intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR);
|
|
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;
|
|
|
|
|
|
+ if (priv->irq1_stat & P_LINK_UP_IRQ(P7_IRQ_OFF)) {
|
|
|
|
+ priv->port_sts[7].link = true;
|
|
|
|
+ dsa_port_phylink_mac_change(ds, 7, true);
|
|
|
|
+ }
|
|
|
|
+ if (priv->irq1_stat & P_LINK_DOWN_IRQ(P7_IRQ_OFF)) {
|
|
|
|
+ priv->port_sts[7].link = false;
|
|
|
|
+ dsa_port_phylink_mac_change(ds, 7, false);
|
|
|
|
+ }
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
return IRQ_HANDLED;
|
|
}
|
|
}
|
|
@@ -620,6 +627,192 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
|
|
status->pause = 1;
|
|
status->pause = 1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port,
|
|
|
|
+ unsigned long *supported,
|
|
|
|
+ struct phylink_link_state *state)
|
|
|
|
+{
|
|
|
|
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
|
|
|
|
+
|
|
|
|
+ if (!phy_interface_mode_is_rgmii(state->interface) &&
|
|
|
|
+ state->interface != PHY_INTERFACE_MODE_MII &&
|
|
|
|
+ state->interface != PHY_INTERFACE_MODE_REVMII &&
|
|
|
|
+ state->interface != PHY_INTERFACE_MODE_GMII &&
|
|
|
|
+ state->interface != PHY_INTERFACE_MODE_INTERNAL &&
|
|
|
|
+ state->interface != PHY_INTERFACE_MODE_MOCA) {
|
|
|
|
+ bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
|
|
+ dev_err(ds->dev,
|
|
|
|
+ "Unsupported interface: %d\n", state->interface);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Allow all the expected bits */
|
|
|
|
+ phylink_set(mask, Autoneg);
|
|
|
|
+ phylink_set_port_modes(mask);
|
|
|
|
+ phylink_set(mask, Pause);
|
|
|
|
+ phylink_set(mask, Asym_Pause);
|
|
|
|
+
|
|
|
|
+ /* With the exclusion of MII and Reverse MII, we support Gigabit,
|
|
|
|
+ * including Half duplex
|
|
|
|
+ */
|
|
|
|
+ if (state->interface != PHY_INTERFACE_MODE_MII &&
|
|
|
|
+ state->interface != PHY_INTERFACE_MODE_REVMII) {
|
|
|
|
+ phylink_set(mask, 1000baseT_Full);
|
|
|
|
+ phylink_set(mask, 1000baseT_Half);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ phylink_set(mask, 10baseT_Half);
|
|
|
|
+ phylink_set(mask, 10baseT_Full);
|
|
|
|
+ phylink_set(mask, 100baseT_Half);
|
|
|
|
+ phylink_set(mask, 100baseT_Full);
|
|
|
|
+
|
|
|
|
+ bitmap_and(supported, supported, mask,
|
|
|
|
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
|
|
+ bitmap_and(state->advertising, state->advertising, mask,
|
|
|
|
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port,
|
|
|
|
+ unsigned int mode,
|
|
|
|
+ const struct phylink_link_state *state)
|
|
|
|
+{
|
|
|
|
+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
|
|
|
|
+ u32 id_mode_dis = 0, port_mode;
|
|
|
|
+ u32 reg, offset;
|
|
|
|
+
|
|
|
|
+ if (priv->type == BCM7445_DEVICE_ID)
|
|
|
|
+ offset = CORE_STS_OVERRIDE_GMIIP_PORT(port);
|
|
|
|
+ else
|
|
|
|
+ offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port);
|
|
|
|
+
|
|
|
|
+ switch (state->interface) {
|
|
|
|
+ case PHY_INTERFACE_MODE_RGMII:
|
|
|
|
+ id_mode_dis = 1;
|
|
|
|
+ /* fallthrough */
|
|
|
|
+ case PHY_INTERFACE_MODE_RGMII_TXID:
|
|
|
|
+ port_mode = EXT_GPHY;
|
|
|
|
+ break;
|
|
|
|
+ case PHY_INTERFACE_MODE_MII:
|
|
|
|
+ port_mode = EXT_EPHY;
|
|
|
|
+ break;
|
|
|
|
+ case PHY_INTERFACE_MODE_REVMII:
|
|
|
|
+ port_mode = EXT_REVMII;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ /* all other PHYs: internal and MoCA */
|
|
|
|
+ goto force_link;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Clear id_mode_dis bit, and the existing port mode, let
|
|
|
|
+ * RGMII_MODE_EN bet set by mac_link_{up,down}
|
|
|
|
+ */
|
|
|
|
+ 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;
|
|
|
|
+ if (id_mode_dis)
|
|
|
|
+ reg |= ID_MODE_DIS;
|
|
|
|
+
|
|
|
|
+ if (state->pause & MLO_PAUSE_TXRX_MASK) {
|
|
|
|
+ if (state->pause & MLO_PAUSE_TX)
|
|
|
|
+ reg |= TX_PAUSE_EN;
|
|
|
|
+ reg |= RX_PAUSE_EN;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ reg_writel(priv, reg, REG_RGMII_CNTRL_P(port));
|
|
|
|
+
|
|
|
|
+force_link:
|
|
|
|
+ /* Force link settings detected from the PHY */
|
|
|
|
+ reg = SW_OVERRIDE;
|
|
|
|
+ switch (state->speed) {
|
|
|
|
+ case SPEED_1000:
|
|
|
|
+ reg |= SPDSTS_1000 << SPEED_SHIFT;
|
|
|
|
+ break;
|
|
|
|
+ case SPEED_100:
|
|
|
|
+ reg |= SPDSTS_100 << SPEED_SHIFT;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (state->link)
|
|
|
|
+ reg |= LINK_STS;
|
|
|
|
+ if (state->duplex == DUPLEX_FULL)
|
|
|
|
+ reg |= DUPLX_MODE;
|
|
|
|
+
|
|
|
|
+ core_writel(priv, reg, offset);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void bcm_sf2_sw_mac_link_set(struct dsa_switch *ds, int port,
|
|
|
|
+ phy_interface_t interface, bool link)
|
|
|
|
+{
|
|
|
|
+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
|
|
|
|
+ u32 reg;
|
|
|
|
+
|
|
|
|
+ if (!phy_interface_mode_is_rgmii(interface) &&
|
|
|
|
+ interface != PHY_INTERFACE_MODE_MII &&
|
|
|
|
+ interface != PHY_INTERFACE_MODE_REVMII)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ /* If the link is down, just disable the interface to conserve power */
|
|
|
|
+ reg = reg_readl(priv, REG_RGMII_CNTRL_P(port));
|
|
|
|
+ if (link)
|
|
|
|
+ reg |= RGMII_MODE_EN;
|
|
|
|
+ else
|
|
|
|
+ reg &= ~RGMII_MODE_EN;
|
|
|
|
+ reg_writel(priv, reg, REG_RGMII_CNTRL_P(port));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port,
|
|
|
|
+ unsigned int mode,
|
|
|
|
+ phy_interface_t interface)
|
|
|
|
+{
|
|
|
|
+ bcm_sf2_sw_mac_link_set(ds, port, interface, false);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port,
|
|
|
|
+ unsigned int mode,
|
|
|
|
+ phy_interface_t interface,
|
|
|
|
+ struct phy_device *phydev)
|
|
|
|
+{
|
|
|
|
+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
|
|
|
|
+ struct ethtool_eee *p = &priv->dev->ports[port].eee;
|
|
|
|
+
|
|
|
|
+ bcm_sf2_sw_mac_link_set(ds, port, interface, true);
|
|
|
|
+
|
|
|
|
+ if (mode == MLO_AN_PHY && phydev)
|
|
|
|
+ p->eee_enabled = b53_eee_init(ds, port, phydev);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void bcm_sf2_sw_fixed_state(struct dsa_switch *ds, int port,
|
|
|
|
+ struct phylink_link_state *status)
|
|
|
|
+{
|
|
|
|
+ struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
|
|
|
|
+
|
|
|
|
+ status->link = false;
|
|
|
|
+
|
|
|
|
+ /* MoCA port 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.
|
|
|
|
+ *
|
|
|
|
+ * For the other ports, we just force the link status, since this is
|
|
|
|
+ * a fixed PHY device.
|
|
|
|
+ */
|
|
|
|
+ if (port == priv->moca_port) {
|
|
|
|
+ status->link = priv->port_sts[port].link;
|
|
|
|
+ /* For MoCA interfaces, also force a link down notification
|
|
|
|
+ * since some version of the user-space daemon (mocad) use
|
|
|
|
+ * cmd->autoneg to force the link, which messes up the PHY
|
|
|
|
+ * state machine and make it go in PHY_FORCING state instead.
|
|
|
|
+ */
|
|
|
|
+ if (!status->link)
|
|
|
|
+ netif_carrier_off(ds->ports[port].slave);
|
|
|
|
+ status->duplex = DUPLEX_FULL;
|
|
|
|
+ } else {
|
|
|
|
+ status->link = true;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
static void bcm_sf2_enable_acb(struct dsa_switch *ds)
|
|
static void bcm_sf2_enable_acb(struct dsa_switch *ds)
|
|
{
|
|
{
|
|
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
|
|
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
|
|
@@ -863,6 +1056,11 @@ static const struct dsa_switch_ops bcm_sf2_ops = {
|
|
.get_phy_flags = bcm_sf2_sw_get_phy_flags,
|
|
.get_phy_flags = bcm_sf2_sw_get_phy_flags,
|
|
.adjust_link = bcm_sf2_sw_adjust_link,
|
|
.adjust_link = bcm_sf2_sw_adjust_link,
|
|
.fixed_link_update = bcm_sf2_sw_fixed_link_update,
|
|
.fixed_link_update = bcm_sf2_sw_fixed_link_update,
|
|
|
|
+ .phylink_validate = bcm_sf2_sw_validate,
|
|
|
|
+ .phylink_mac_config = bcm_sf2_sw_mac_config,
|
|
|
|
+ .phylink_mac_link_down = bcm_sf2_sw_mac_link_down,
|
|
|
|
+ .phylink_mac_link_up = bcm_sf2_sw_mac_link_up,
|
|
|
|
+ .phylink_fixed_state = bcm_sf2_sw_fixed_state,
|
|
.suspend = bcm_sf2_sw_suspend,
|
|
.suspend = bcm_sf2_sw_suspend,
|
|
.resume = bcm_sf2_sw_resume,
|
|
.resume = bcm_sf2_sw_resume,
|
|
.get_wol = bcm_sf2_sw_get_wol,
|
|
.get_wol = bcm_sf2_sw_get_wol,
|
|
@@ -1065,14 +1263,14 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)
|
|
bcm_sf2_intr_disable(priv);
|
|
bcm_sf2_intr_disable(priv);
|
|
|
|
|
|
ret = devm_request_irq(&pdev->dev, priv->irq0, bcm_sf2_switch_0_isr, 0,
|
|
ret = devm_request_irq(&pdev->dev, priv->irq0, bcm_sf2_switch_0_isr, 0,
|
|
- "switch_0", priv);
|
|
|
|
|
|
+ "switch_0", ds);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
pr_err("failed to request switch_0 IRQ\n");
|
|
pr_err("failed to request switch_0 IRQ\n");
|
|
goto out_mdio;
|
|
goto out_mdio;
|
|
}
|
|
}
|
|
|
|
|
|
ret = devm_request_irq(&pdev->dev, priv->irq1, bcm_sf2_switch_1_isr, 0,
|
|
ret = devm_request_irq(&pdev->dev, priv->irq1, bcm_sf2_switch_1_isr, 0,
|
|
- "switch_1", priv);
|
|
|
|
|
|
+ "switch_1", ds);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
pr_err("failed to request switch_1 IRQ\n");
|
|
pr_err("failed to request switch_1 IRQ\n");
|
|
goto out_mdio;
|
|
goto out_mdio;
|