Commit 1fcc3fd7 authored by David S. Miller's avatar David S. Miller

Merge branch 'Full-phylink-support-for-mv88e6352'

Andrew Lunn says:

====================
Full phylink support for mv88e6352

These two patches implement full phylink support for the mv88e6352
family, when using an SFP connected to its SERDES interface. This adds
interrupt support to the SERDES, so that we get interrupts on link
up/down, and then make calls phydev_link_change().

The first patch is a minor bug fix, which does not seem to affect any
current features, so i'm not submitting it for stable. It is however
required for configuring SERDES interrupts.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 743e4815 4382172f
...@@ -3160,6 +3160,8 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { ...@@ -3160,6 +3160,8 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
.vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_getnext = mv88e6352_g1_vtu_getnext,
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
.serdes_power = mv88e6352_serdes_power, .serdes_power = mv88e6352_serdes_power,
.serdes_irq_setup = mv88e6352_serdes_irq_setup,
.serdes_irq_free = mv88e6352_serdes_irq_free,
.gpio_ops = &mv88e6352_gpio_ops, .gpio_ops = &mv88e6352_gpio_ops,
.phylink_validate = mv88e6352_phylink_validate, .phylink_validate = mv88e6352_phylink_validate,
}; };
...@@ -3366,6 +3368,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { ...@@ -3366,6 +3368,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
.vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_getnext = mv88e6352_g1_vtu_getnext,
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
.serdes_power = mv88e6352_serdes_power, .serdes_power = mv88e6352_serdes_power,
.serdes_irq_setup = mv88e6352_serdes_irq_setup,
.serdes_irq_free = mv88e6352_serdes_irq_free,
.gpio_ops = &mv88e6352_gpio_ops, .gpio_ops = &mv88e6352_gpio_ops,
.avb_ops = &mv88e6352_avb_ops, .avb_ops = &mv88e6352_avb_ops,
.ptp_ops = &mv88e6352_ptp_ops, .ptp_ops = &mv88e6352_ptp_ops,
...@@ -3664,6 +3668,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { ...@@ -3664,6 +3668,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
.vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_getnext = mv88e6352_g1_vtu_getnext,
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
.serdes_power = mv88e6352_serdes_power, .serdes_power = mv88e6352_serdes_power,
.serdes_irq_setup = mv88e6352_serdes_irq_setup,
.serdes_irq_free = mv88e6352_serdes_irq_free,
.gpio_ops = &mv88e6352_gpio_ops, .gpio_ops = &mv88e6352_gpio_ops,
.avb_ops = &mv88e6352_avb_ops, .avb_ops = &mv88e6352_avb_ops,
.ptp_ops = &mv88e6352_ptp_ops, .ptp_ops = &mv88e6352_ptp_ops,
......
...@@ -110,6 +110,9 @@ int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy, ...@@ -110,6 +110,9 @@ int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy,
err = mv88e6xxx_phy_page_get(chip, phy, page); err = mv88e6xxx_phy_page_get(chip, phy, page);
if (!err) { if (!err) {
err = mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE, page); err = mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE, page);
if (!err)
err = mv88e6xxx_phy_write(chip, phy, reg, val);
mv88e6xxx_phy_page_put(chip, phy); mv88e6xxx_phy_page_put(chip, phy);
} }
......
...@@ -185,6 +185,111 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, ...@@ -185,6 +185,111 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
return ARRAY_SIZE(mv88e6352_serdes_hw_stats); return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
} }
static void mv88e6352_serdes_irq_link(struct mv88e6xxx_chip *chip, int port)
{
struct dsa_switch *ds = chip->ds;
u16 status;
bool up;
mv88e6352_serdes_read(chip, MII_BMSR, &status);
/* Status must be read twice in order to give the current link
* status. Otherwise the change in link status since the last
* read of the register is returned.
*/
mv88e6352_serdes_read(chip, MII_BMSR, &status);
up = status & BMSR_LSTATUS;
dsa_port_phylink_mac_change(ds, port, up);
}
static irqreturn_t mv88e6352_serdes_thread_fn(int irq, void *dev_id)
{
struct mv88e6xxx_port *port = dev_id;
struct mv88e6xxx_chip *chip = port->chip;
irqreturn_t ret = IRQ_NONE;
u16 status;
int err;
mutex_lock(&chip->reg_lock);
err = mv88e6352_serdes_read(chip, MV88E6352_SERDES_INT_STATUS, &status);
if (err)
goto out;
if (status & MV88E6352_SERDES_INT_LINK_CHANGE) {
ret = IRQ_HANDLED;
mv88e6352_serdes_irq_link(chip, port->port);
}
out:
mutex_unlock(&chip->reg_lock);
return ret;
}
static int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip)
{
return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE,
MV88E6352_SERDES_INT_LINK_CHANGE);
}
static int mv88e6352_serdes_irq_disable(struct mv88e6xxx_chip *chip)
{
return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE, 0);
}
int mv88e6352_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port)
{
int err;
if (!mv88e6352_port_has_serdes(chip, port))
return 0;
chip->ports[port].serdes_irq = irq_find_mapping(chip->g2_irq.domain,
MV88E6352_SERDES_IRQ);
if (chip->ports[port].serdes_irq < 0) {
dev_err(chip->dev, "Unable to map SERDES irq: %d\n",
chip->ports[port].serdes_irq);
return chip->ports[port].serdes_irq;
}
/* Requesting the IRQ will trigger irq callbacks. So we cannot
* hold the reg_lock.
*/
mutex_unlock(&chip->reg_lock);
err = request_threaded_irq(chip->ports[port].serdes_irq, NULL,
mv88e6352_serdes_thread_fn,
IRQF_ONESHOT, "mv88e6xxx-serdes",
&chip->ports[port]);
mutex_lock(&chip->reg_lock);
if (err) {
dev_err(chip->dev, "Unable to request SERDES interrupt: %d\n",
err);
return err;
}
return mv88e6352_serdes_irq_enable(chip);
}
void mv88e6352_serdes_irq_free(struct mv88e6xxx_chip *chip, int port)
{
if (!mv88e6352_port_has_serdes(chip, port))
return;
mv88e6352_serdes_irq_disable(chip);
/* Freeing the IRQ will trigger irq callbacks. So we cannot
* hold the reg_lock.
*/
mutex_unlock(&chip->reg_lock);
free_irq(chip->ports[port].serdes_irq, &chip->ports[port]);
mutex_lock(&chip->reg_lock);
chip->ports[port].serdes_irq = 0;
}
/* Return the SERDES lane address a port is using. Only Ports 9 and 10 /* Return the SERDES lane address a port is using. Only Ports 9 and 10
* have SERDES lanes. Returns -ENODEV if a port does not have a lane. * have SERDES lanes. Returns -ENODEV if a port does not have a lane.
*/ */
......
...@@ -18,6 +18,19 @@ ...@@ -18,6 +18,19 @@
#define MV88E6352_ADDR_SERDES 0x0f #define MV88E6352_ADDR_SERDES 0x0f
#define MV88E6352_SERDES_PAGE_FIBER 0x01 #define MV88E6352_SERDES_PAGE_FIBER 0x01
#define MV88E6352_SERDES_IRQ 0x0b
#define MV88E6352_SERDES_INT_ENABLE 0x12
#define MV88E6352_SERDES_INT_SPEED_CHANGE BIT(14)
#define MV88E6352_SERDES_INT_DUPLEX_CHANGE BIT(13)
#define MV88E6352_SERDES_INT_PAGE_RX BIT(12)
#define MV88E6352_SERDES_INT_AN_COMPLETE BIT(11)
#define MV88E6352_SERDES_INT_LINK_CHANGE BIT(10)
#define MV88E6352_SERDES_INT_SYMBOL_ERROR BIT(9)
#define MV88E6352_SERDES_INT_FALSE_CARRIER BIT(8)
#define MV88E6352_SERDES_INT_FIFO_OVER_UNDER BIT(7)
#define MV88E6352_SERDES_INT_FIBRE_ENERGY BIT(4)
#define MV88E6352_SERDES_INT_STATUS 0x13
#define MV88E6341_ADDR_SERDES 0x15 #define MV88E6341_ADDR_SERDES 0x15
...@@ -73,5 +86,8 @@ int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, ...@@ -73,5 +86,8 @@ int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port,
int lane); int lane);
int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port, int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port,
int lane); int lane);
int mv88e6352_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port);
void mv88e6352_serdes_irq_free(struct mv88e6xxx_chip *chip, int port);
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment