mirror of
https://github.com/torvalds/linux.git
synced 2024-11-17 09:31:50 +00:00
Merge branch 'net-dsa-mv88e6xxx-centralize-SERDES-IRQ-handling'
Vivien Didelot says: ==================== net: dsa: mv88e6xxx: centralize SERDES IRQ handling Following Marek's work on the abstraction of the SERDES lanes mapping, this series trades the .serdes_irq_setup and .serdes_irq_free callbacks for new .serdes_irq_mapping, .serdes_irq_enable and .serdes_irq_status operations. This has the benefit to limit the various SERDES implementations to simple register accesses only; centralize the IRQ handling and mutex locking logic; as well as reducing boilerplate in the driver. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
4bc61b0b16
@ -2054,27 +2054,93 @@ static int mv88e6xxx_setup_egress_floods(struct mv88e6xxx_chip *chip, int port)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t mv88e6xxx_serdes_irq_thread_fn(int irq, void *dev_id)
|
||||
{
|
||||
struct mv88e6xxx_port *mvp = dev_id;
|
||||
struct mv88e6xxx_chip *chip = mvp->chip;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
int port = mvp->port;
|
||||
u8 lane;
|
||||
|
||||
mv88e6xxx_reg_lock(chip);
|
||||
lane = mv88e6xxx_serdes_get_lane(chip, port);
|
||||
if (lane)
|
||||
ret = mv88e6xxx_serdes_irq_status(chip, port, lane);
|
||||
mv88e6xxx_reg_unlock(chip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mv88e6xxx_serdes_irq_request(struct mv88e6xxx_chip *chip, int port,
|
||||
u8 lane)
|
||||
{
|
||||
struct mv88e6xxx_port *dev_id = &chip->ports[port];
|
||||
unsigned int irq;
|
||||
int err;
|
||||
|
||||
/* Nothing to request if this SERDES port has no IRQ */
|
||||
irq = mv88e6xxx_serdes_irq_mapping(chip, port);
|
||||
if (!irq)
|
||||
return 0;
|
||||
|
||||
/* Requesting the IRQ will trigger IRQ callbacks, so release the lock */
|
||||
mv88e6xxx_reg_unlock(chip);
|
||||
err = request_threaded_irq(irq, NULL, mv88e6xxx_serdes_irq_thread_fn,
|
||||
IRQF_ONESHOT, "mv88e6xxx-serdes", dev_id);
|
||||
mv88e6xxx_reg_lock(chip);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dev_id->serdes_irq = irq;
|
||||
|
||||
return mv88e6xxx_serdes_irq_enable(chip, port, lane);
|
||||
}
|
||||
|
||||
static int mv88e6xxx_serdes_irq_free(struct mv88e6xxx_chip *chip, int port,
|
||||
u8 lane)
|
||||
{
|
||||
struct mv88e6xxx_port *dev_id = &chip->ports[port];
|
||||
unsigned int irq = dev_id->serdes_irq;
|
||||
int err;
|
||||
|
||||
/* Nothing to free if no IRQ has been requested */
|
||||
if (!irq)
|
||||
return 0;
|
||||
|
||||
err = mv88e6xxx_serdes_irq_disable(chip, port, lane);
|
||||
|
||||
/* Freeing the IRQ will trigger IRQ callbacks, so release the lock */
|
||||
mv88e6xxx_reg_unlock(chip);
|
||||
free_irq(irq, dev_id);
|
||||
mv88e6xxx_reg_lock(chip);
|
||||
|
||||
dev_id->serdes_irq = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port,
|
||||
bool on)
|
||||
{
|
||||
u8 lane;
|
||||
int err;
|
||||
|
||||
if (!chip->info->ops->serdes_power)
|
||||
lane = mv88e6xxx_serdes_get_lane(chip, port);
|
||||
if (!lane)
|
||||
return 0;
|
||||
|
||||
if (on) {
|
||||
err = chip->info->ops->serdes_power(chip, port, true);
|
||||
err = mv88e6xxx_serdes_power_up(chip, port, lane);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (chip->info->ops->serdes_irq_setup)
|
||||
err = chip->info->ops->serdes_irq_setup(chip, port);
|
||||
err = mv88e6xxx_serdes_irq_request(chip, port, lane);
|
||||
} else {
|
||||
if (chip->info->ops->serdes_irq_free &&
|
||||
chip->ports[port].serdes_irq)
|
||||
chip->info->ops->serdes_irq_free(chip, port);
|
||||
err = mv88e6xxx_serdes_irq_free(chip, port, lane);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = chip->info->ops->serdes_power(chip, port, false);
|
||||
err = mv88e6xxx_serdes_power_down(chip, port, lane);
|
||||
}
|
||||
|
||||
return err;
|
||||
@ -2931,8 +2997,9 @@ static const struct mv88e6xxx_ops mv88e6141_ops = {
|
||||
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6390_serdes_power,
|
||||
.serdes_get_lane = mv88e6341_serdes_get_lane,
|
||||
.serdes_irq_setup = mv88e6390_serdes_irq_setup,
|
||||
.serdes_irq_free = mv88e6390_serdes_irq_free,
|
||||
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
|
||||
.serdes_irq_enable = mv88e6390_serdes_irq_enable,
|
||||
.serdes_irq_status = mv88e6390_serdes_irq_status,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.phylink_validate = mv88e6341_phylink_validate,
|
||||
};
|
||||
@ -3092,6 +3159,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
|
||||
.rmu_disable = mv88e6352_g1_rmu_disable,
|
||||
.vtu_getnext = mv88e6352_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
|
||||
.serdes_get_lane = mv88e6352_serdes_get_lane,
|
||||
.serdes_power = mv88e6352_serdes_power,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.phylink_validate = mv88e6352_phylink_validate,
|
||||
@ -3177,9 +3245,11 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
|
||||
.rmu_disable = mv88e6352_g1_rmu_disable,
|
||||
.vtu_getnext = mv88e6352_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
|
||||
.serdes_get_lane = mv88e6352_serdes_get_lane,
|
||||
.serdes_power = mv88e6352_serdes_power,
|
||||
.serdes_irq_setup = mv88e6352_serdes_irq_setup,
|
||||
.serdes_irq_free = mv88e6352_serdes_irq_free,
|
||||
.serdes_irq_mapping = mv88e6352_serdes_irq_mapping,
|
||||
.serdes_irq_enable = mv88e6352_serdes_irq_enable,
|
||||
.serdes_irq_status = mv88e6352_serdes_irq_status,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.phylink_validate = mv88e6352_phylink_validate,
|
||||
};
|
||||
@ -3261,8 +3331,9 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
|
||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6390_serdes_power,
|
||||
.serdes_get_lane = mv88e6390_serdes_get_lane,
|
||||
.serdes_irq_setup = mv88e6390_serdes_irq_setup,
|
||||
.serdes_irq_free = mv88e6390_serdes_irq_free,
|
||||
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
|
||||
.serdes_irq_enable = mv88e6390_serdes_irq_enable,
|
||||
.serdes_irq_status = mv88e6390_serdes_irq_status,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.phylink_validate = mv88e6390_phylink_validate,
|
||||
};
|
||||
@ -3308,8 +3379,9 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
|
||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6390_serdes_power,
|
||||
.serdes_get_lane = mv88e6390x_serdes_get_lane,
|
||||
.serdes_irq_setup = mv88e6390_serdes_irq_setup,
|
||||
.serdes_irq_free = mv88e6390_serdes_irq_free,
|
||||
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
|
||||
.serdes_irq_enable = mv88e6390_serdes_irq_enable,
|
||||
.serdes_irq_status = mv88e6390_serdes_irq_status,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.phylink_validate = mv88e6390x_phylink_validate,
|
||||
};
|
||||
@ -3355,8 +3427,9 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
|
||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6390_serdes_power,
|
||||
.serdes_get_lane = mv88e6390_serdes_get_lane,
|
||||
.serdes_irq_setup = mv88e6390_serdes_irq_setup,
|
||||
.serdes_irq_free = mv88e6390_serdes_irq_free,
|
||||
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
|
||||
.serdes_irq_enable = mv88e6390_serdes_irq_enable,
|
||||
.serdes_irq_status = mv88e6390_serdes_irq_status,
|
||||
.avb_ops = &mv88e6390_avb_ops,
|
||||
.ptp_ops = &mv88e6352_ptp_ops,
|
||||
.phylink_validate = mv88e6390_phylink_validate,
|
||||
@ -3402,9 +3475,11 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
|
||||
.rmu_disable = mv88e6352_g1_rmu_disable,
|
||||
.vtu_getnext = mv88e6352_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
|
||||
.serdes_get_lane = mv88e6352_serdes_get_lane,
|
||||
.serdes_power = mv88e6352_serdes_power,
|
||||
.serdes_irq_setup = mv88e6352_serdes_irq_setup,
|
||||
.serdes_irq_free = mv88e6352_serdes_irq_free,
|
||||
.serdes_irq_mapping = mv88e6352_serdes_irq_mapping,
|
||||
.serdes_irq_enable = mv88e6352_serdes_irq_enable,
|
||||
.serdes_irq_status = mv88e6352_serdes_irq_status,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.avb_ops = &mv88e6352_avb_ops,
|
||||
.ptp_ops = &mv88e6352_ptp_ops,
|
||||
@ -3492,8 +3567,9 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
|
||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6390_serdes_power,
|
||||
.serdes_get_lane = mv88e6390_serdes_get_lane,
|
||||
.serdes_irq_setup = mv88e6390_serdes_irq_setup,
|
||||
.serdes_irq_free = mv88e6390_serdes_irq_free,
|
||||
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
|
||||
.serdes_irq_enable = mv88e6390_serdes_irq_enable,
|
||||
.serdes_irq_status = mv88e6390_serdes_irq_status,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.avb_ops = &mv88e6390_avb_ops,
|
||||
.ptp_ops = &mv88e6352_ptp_ops,
|
||||
@ -3629,8 +3705,9 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {
|
||||
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6390_serdes_power,
|
||||
.serdes_get_lane = mv88e6341_serdes_get_lane,
|
||||
.serdes_irq_setup = mv88e6390_serdes_irq_setup,
|
||||
.serdes_irq_free = mv88e6390_serdes_irq_free,
|
||||
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
|
||||
.serdes_irq_enable = mv88e6390_serdes_irq_enable,
|
||||
.serdes_irq_status = mv88e6390_serdes_irq_status,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.avb_ops = &mv88e6390_avb_ops,
|
||||
.ptp_ops = &mv88e6352_ptp_ops,
|
||||
@ -3759,9 +3836,11 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
|
||||
.rmu_disable = mv88e6352_g1_rmu_disable,
|
||||
.vtu_getnext = mv88e6352_g1_vtu_getnext,
|
||||
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
|
||||
.serdes_get_lane = mv88e6352_serdes_get_lane,
|
||||
.serdes_power = mv88e6352_serdes_power,
|
||||
.serdes_irq_setup = mv88e6352_serdes_irq_setup,
|
||||
.serdes_irq_free = mv88e6352_serdes_irq_free,
|
||||
.serdes_irq_mapping = mv88e6352_serdes_irq_mapping,
|
||||
.serdes_irq_enable = mv88e6352_serdes_irq_enable,
|
||||
.serdes_irq_status = mv88e6352_serdes_irq_status,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.avb_ops = &mv88e6352_avb_ops,
|
||||
.ptp_ops = &mv88e6352_ptp_ops,
|
||||
@ -3814,8 +3893,9 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
|
||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6390_serdes_power,
|
||||
.serdes_get_lane = mv88e6390_serdes_get_lane,
|
||||
.serdes_irq_setup = mv88e6390_serdes_irq_setup,
|
||||
.serdes_irq_free = mv88e6390_serdes_irq_free,
|
||||
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
|
||||
.serdes_irq_enable = mv88e6390_serdes_irq_enable,
|
||||
.serdes_irq_status = mv88e6390_serdes_irq_status,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.avb_ops = &mv88e6390_avb_ops,
|
||||
.ptp_ops = &mv88e6352_ptp_ops,
|
||||
@ -3865,8 +3945,9 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
|
||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||
.serdes_power = mv88e6390_serdes_power,
|
||||
.serdes_get_lane = mv88e6390x_serdes_get_lane,
|
||||
.serdes_irq_setup = mv88e6390_serdes_irq_setup,
|
||||
.serdes_irq_free = mv88e6390_serdes_irq_free,
|
||||
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
|
||||
.serdes_irq_enable = mv88e6390_serdes_irq_enable,
|
||||
.serdes_irq_status = mv88e6390_serdes_irq_status,
|
||||
.gpio_ops = &mv88e6352_gpio_ops,
|
||||
.avb_ops = &mv88e6390_avb_ops,
|
||||
.ptp_ops = &mv88e6352_ptp_ops,
|
||||
|
@ -199,7 +199,7 @@ struct mv88e6xxx_port {
|
||||
u64 vtu_member_violation;
|
||||
u64 vtu_miss_violation;
|
||||
u8 cmode;
|
||||
int serdes_irq;
|
||||
unsigned int serdes_irq;
|
||||
};
|
||||
|
||||
struct mv88e6xxx_chip {
|
||||
@ -441,14 +441,19 @@ struct mv88e6xxx_ops {
|
||||
int (*mgmt_rsvd2cpu)(struct mv88e6xxx_chip *chip);
|
||||
|
||||
/* Power on/off a SERDES interface */
|
||||
int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, bool on);
|
||||
int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, u8 lane,
|
||||
bool up);
|
||||
|
||||
/* SERDES lane mapping */
|
||||
int (*serdes_get_lane)(struct mv88e6xxx_chip *chip, int port, u8 *lane);
|
||||
u8 (*serdes_get_lane)(struct mv88e6xxx_chip *chip, int port);
|
||||
|
||||
/* SERDES interrupt handling */
|
||||
int (*serdes_irq_setup)(struct mv88e6xxx_chip *chip, int port);
|
||||
void (*serdes_irq_free)(struct mv88e6xxx_chip *chip, int port);
|
||||
unsigned int (*serdes_irq_mapping)(struct mv88e6xxx_chip *chip,
|
||||
int port);
|
||||
int (*serdes_irq_enable)(struct mv88e6xxx_chip *chip, int port, u8 lane,
|
||||
bool enable);
|
||||
irqreturn_t (*serdes_irq_status)(struct mv88e6xxx_chip *chip, int port,
|
||||
u8 lane);
|
||||
|
||||
/* Statistics from the SERDES interface */
|
||||
int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port);
|
||||
|
@ -431,18 +431,15 @@ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
|
||||
if (cmode == chip->ports[port].cmode)
|
||||
return 0;
|
||||
|
||||
err = mv88e6xxx_serdes_get_lane(chip, port, &lane);
|
||||
if (err && err != -ENODEV)
|
||||
return err;
|
||||
|
||||
if (err != -ENODEV) {
|
||||
lane = mv88e6xxx_serdes_get_lane(chip, port);
|
||||
if (lane) {
|
||||
if (chip->ports[port].serdes_irq) {
|
||||
err = mv88e6390_serdes_irq_disable(chip, port, lane);
|
||||
err = mv88e6xxx_serdes_irq_disable(chip, port, lane);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mv88e6390_serdes_power(chip, port, false);
|
||||
err = mv88e6xxx_serdes_power_down(chip, port, lane);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -463,16 +460,16 @@ static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
|
||||
|
||||
chip->ports[port].cmode = cmode;
|
||||
|
||||
err = mv88e6xxx_serdes_get_lane(chip, port, &lane);
|
||||
if (err)
|
||||
return err;
|
||||
lane = mv88e6xxx_serdes_get_lane(chip, port);
|
||||
if (!lane)
|
||||
return -ENODEV;
|
||||
|
||||
err = mv88e6390_serdes_power(chip, port, true);
|
||||
err = mv88e6xxx_serdes_power_up(chip, port, lane);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (chip->ports[port].serdes_irq) {
|
||||
err = mv88e6390_serdes_irq_enable(chip, port, lane);
|
||||
err = mv88e6xxx_serdes_irq_enable(chip, port, lane);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
@ -49,7 +49,8 @@ static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip,
|
||||
return mv88e6xxx_phy_write(chip, lane, reg_c45, val);
|
||||
}
|
||||
|
||||
static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on)
|
||||
int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
|
||||
bool up)
|
||||
{
|
||||
u16 val, new_val;
|
||||
int err;
|
||||
@ -58,7 +59,7 @@ static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (on)
|
||||
if (up)
|
||||
new_val = val & ~BMCR_PDOWN;
|
||||
else
|
||||
new_val = val | BMCR_PDOWN;
|
||||
@ -69,31 +70,27 @@ static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on)
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port)
|
||||
u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
|
||||
{
|
||||
u8 cmode = chip->ports[port].cmode;
|
||||
u8 lane = 0;
|
||||
|
||||
if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASEX) ||
|
||||
(cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX) ||
|
||||
(cmode == MV88E6XXX_PORT_STS_CMODE_SGMII))
|
||||
lane = 0xff; /* Unused */
|
||||
|
||||
return lane;
|
||||
}
|
||||
|
||||
static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port)
|
||||
{
|
||||
if (mv88e6xxx_serdes_get_lane(chip, port))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (mv88e6352_port_has_serdes(chip, port)) {
|
||||
err = mv88e6352_serdes_power_set(chip, on);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mv88e6352_serdes_hw_stat {
|
||||
char string[ETH_GSTRING_LEN];
|
||||
int sizeof_stat;
|
||||
@ -186,254 +183,178 @@ static void mv88e6352_serdes_irq_link(struct mv88e6xxx_chip *chip, int port)
|
||||
struct dsa_switch *ds = chip->ds;
|
||||
u16 status;
|
||||
bool up;
|
||||
int err;
|
||||
|
||||
mv88e6352_serdes_read(chip, MII_BMSR, &status);
|
||||
err = mv88e6352_serdes_read(chip, MII_BMSR, &status);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
/* 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);
|
||||
err = mv88e6352_serdes_read(chip, MII_BMSR, &status);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
up = status & BMSR_LSTATUS;
|
||||
|
||||
dsa_port_phylink_mac_change(ds, port, up);
|
||||
}
|
||||
|
||||
static irqreturn_t mv88e6352_serdes_thread_fn(int irq, void *dev_id)
|
||||
irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
|
||||
u8 lane)
|
||||
{
|
||||
struct mv88e6xxx_port *port = dev_id;
|
||||
struct mv88e6xxx_chip *chip = port->chip;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u16 status;
|
||||
int err;
|
||||
|
||||
mv88e6xxx_reg_lock(chip);
|
||||
|
||||
err = mv88e6352_serdes_read(chip, MV88E6352_SERDES_INT_STATUS, &status);
|
||||
if (err)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
if (status & MV88E6352_SERDES_INT_LINK_CHANGE) {
|
||||
ret = IRQ_HANDLED;
|
||||
mv88e6352_serdes_irq_link(chip, port->port);
|
||||
mv88e6352_serdes_irq_link(chip, port);
|
||||
}
|
||||
out:
|
||||
mv88e6xxx_reg_unlock(chip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip)
|
||||
int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane,
|
||||
bool enable)
|
||||
{
|
||||
return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE,
|
||||
MV88E6352_SERDES_INT_LINK_CHANGE);
|
||||
u16 val = 0;
|
||||
|
||||
if (enable)
|
||||
val |= MV88E6352_SERDES_INT_LINK_CHANGE;
|
||||
|
||||
return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE, val);
|
||||
}
|
||||
|
||||
static int mv88e6352_serdes_irq_disable(struct mv88e6xxx_chip *chip)
|
||||
unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)
|
||||
{
|
||||
return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE, 0);
|
||||
return irq_find_mapping(chip->g2_irq.domain, MV88E6352_SERDES_IRQ);
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
mv88e6xxx_reg_unlock(chip);
|
||||
err = request_threaded_irq(chip->ports[port].serdes_irq, NULL,
|
||||
mv88e6352_serdes_thread_fn,
|
||||
IRQF_ONESHOT, "mv88e6xxx-serdes",
|
||||
&chip->ports[port]);
|
||||
mv88e6xxx_reg_lock(chip);
|
||||
|
||||
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.
|
||||
*/
|
||||
mv88e6xxx_reg_unlock(chip);
|
||||
free_irq(chip->ports[port].serdes_irq, &chip->ports[port]);
|
||||
mv88e6xxx_reg_lock(chip);
|
||||
|
||||
chip->ports[port].serdes_irq = 0;
|
||||
}
|
||||
|
||||
int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port, u8 *lane)
|
||||
u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
|
||||
{
|
||||
u8 cmode = chip->ports[port].cmode;
|
||||
u8 lane = 0;
|
||||
|
||||
if (port != 5)
|
||||
return -ENODEV;
|
||||
|
||||
if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
|
||||
cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
|
||||
cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) {
|
||||
*lane = MV88E6341_PORT5_LANE;
|
||||
return 0;
|
||||
switch (port) {
|
||||
case 5:
|
||||
if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
|
||||
cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
|
||||
cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
|
||||
lane = MV88E6341_PORT5_LANE;
|
||||
break;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
return lane;
|
||||
}
|
||||
|
||||
int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port, u8 *lane)
|
||||
u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
|
||||
{
|
||||
u8 cmode = chip->ports[port].cmode;
|
||||
u8 lane = 0;
|
||||
|
||||
switch (port) {
|
||||
case 9:
|
||||
if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
|
||||
cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
|
||||
cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) {
|
||||
*lane = MV88E6390_PORT9_LANE0;
|
||||
return 0;
|
||||
}
|
||||
cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
|
||||
lane = MV88E6390_PORT9_LANE0;
|
||||
break;
|
||||
case 10:
|
||||
if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
|
||||
cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
|
||||
cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) {
|
||||
*lane = MV88E6390_PORT10_LANE0;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
|
||||
lane = MV88E6390_PORT10_LANE0;
|
||||
break;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
return lane;
|
||||
}
|
||||
|
||||
int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port, u8 *lane)
|
||||
u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
|
||||
{
|
||||
u8 cmode_port9, cmode_port10, cmode_port;
|
||||
|
||||
cmode_port9 = chip->ports[9].cmode;
|
||||
cmode_port10 = chip->ports[10].cmode;
|
||||
cmode_port = chip->ports[port].cmode;
|
||||
u8 cmode_port = chip->ports[port].cmode;
|
||||
u8 cmode_port10 = chip->ports[10].cmode;
|
||||
u8 cmode_port9 = chip->ports[9].cmode;
|
||||
u8 lane = 0;
|
||||
|
||||
switch (port) {
|
||||
case 2:
|
||||
if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) {
|
||||
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) {
|
||||
*lane = MV88E6390_PORT9_LANE1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
|
||||
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
|
||||
lane = MV88E6390_PORT9_LANE1;
|
||||
break;
|
||||
case 3:
|
||||
if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) {
|
||||
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) {
|
||||
*lane = MV88E6390_PORT9_LANE2;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
|
||||
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
|
||||
lane = MV88E6390_PORT9_LANE2;
|
||||
break;
|
||||
case 4:
|
||||
if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) {
|
||||
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) {
|
||||
*lane = MV88E6390_PORT9_LANE3;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
|
||||
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
|
||||
lane = MV88E6390_PORT9_LANE3;
|
||||
break;
|
||||
case 5:
|
||||
if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) {
|
||||
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) {
|
||||
*lane = MV88E6390_PORT10_LANE1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
|
||||
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
|
||||
lane = MV88E6390_PORT10_LANE1;
|
||||
break;
|
||||
case 6:
|
||||
if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) {
|
||||
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) {
|
||||
*lane = MV88E6390_PORT10_LANE2;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
|
||||
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
|
||||
lane = MV88E6390_PORT10_LANE2;
|
||||
break;
|
||||
case 7:
|
||||
if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) {
|
||||
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) {
|
||||
*lane = MV88E6390_PORT10_LANE3;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
|
||||
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
|
||||
lane = MV88E6390_PORT10_LANE3;
|
||||
break;
|
||||
case 9:
|
||||
if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) {
|
||||
*lane = MV88E6390_PORT9_LANE0;
|
||||
return 0;
|
||||
}
|
||||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
|
||||
lane = MV88E6390_PORT9_LANE0;
|
||||
break;
|
||||
case 10:
|
||||
if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) {
|
||||
*lane = MV88E6390_PORT10_LANE0;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
|
||||
lane = MV88E6390_PORT10_LANE0;
|
||||
break;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
return lane;
|
||||
}
|
||||
|
||||
/* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
|
||||
/* Set power up/down for 10GBASE-R and 10GBASE-X4/X2 */
|
||||
static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, u8 lane,
|
||||
bool on)
|
||||
bool up)
|
||||
{
|
||||
u16 val, new_val;
|
||||
int err;
|
||||
@ -444,7 +365,7 @@ static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, u8 lane,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (on)
|
||||
if (up)
|
||||
new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET |
|
||||
MV88E6390_PCS_CONTROL_1_LOOPBACK |
|
||||
MV88E6390_PCS_CONTROL_1_PDOWN);
|
||||
@ -458,9 +379,9 @@ static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, u8 lane,
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Set the power on/off for SGMII and 1000Base-X */
|
||||
/* Set power up/down for SGMII and 1000Base-X */
|
||||
static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, u8 lane,
|
||||
bool on)
|
||||
bool up)
|
||||
{
|
||||
u16 val, new_val;
|
||||
int err;
|
||||
@ -470,7 +391,7 @@ static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, u8 lane,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (on)
|
||||
if (up)
|
||||
new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET |
|
||||
MV88E6390_SGMII_CONTROL_LOOPBACK |
|
||||
MV88E6390_SGMII_CONTROL_PDOWN);
|
||||
@ -484,27 +405,19 @@ static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, u8 lane,
|
||||
return err;
|
||||
}
|
||||
|
||||
int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
|
||||
int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
|
||||
bool up)
|
||||
{
|
||||
u8 cmode = chip->ports[port].cmode;
|
||||
u8 lane;
|
||||
int err;
|
||||
|
||||
err = mv88e6xxx_serdes_get_lane(chip, port, &lane);
|
||||
if (err) {
|
||||
if (err == -ENODEV)
|
||||
err = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (cmode) {
|
||||
case MV88E6XXX_PORT_STS_CMODE_SGMII:
|
||||
case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
|
||||
case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
|
||||
return mv88e6390_serdes_power_sgmii(chip, lane, on);
|
||||
return mv88e6390_serdes_power_sgmii(chip, lane, up);
|
||||
case MV88E6XXX_PORT_STS_CMODE_XAUI:
|
||||
case MV88E6XXX_PORT_STS_CMODE_RXAUI:
|
||||
return mv88e6390_serdes_power_10g(chip, lane, on);
|
||||
return mv88e6390_serdes_power_10g(chip, lane, up);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -578,51 +491,31 @@ static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
|
||||
}
|
||||
|
||||
static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip,
|
||||
u8 lane)
|
||||
u8 lane, bool enable)
|
||||
{
|
||||
u16 val = 0;
|
||||
|
||||
if (enable)
|
||||
val |= MV88E6390_SGMII_INT_LINK_DOWN |
|
||||
MV88E6390_SGMII_INT_LINK_UP;
|
||||
|
||||
return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
|
||||
MV88E6390_SGMII_INT_ENABLE,
|
||||
MV88E6390_SGMII_INT_LINK_DOWN |
|
||||
MV88E6390_SGMII_INT_LINK_UP);
|
||||
MV88E6390_SGMII_INT_ENABLE, val);
|
||||
}
|
||||
|
||||
static int mv88e6390_serdes_irq_disable_sgmii(struct mv88e6xxx_chip *chip,
|
||||
u8 lane)
|
||||
{
|
||||
return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
|
||||
MV88E6390_SGMII_INT_ENABLE, 0);
|
||||
}
|
||||
|
||||
int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port,
|
||||
u8 lane)
|
||||
int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane,
|
||||
bool enable)
|
||||
{
|
||||
u8 cmode = chip->ports[port].cmode;
|
||||
int err = 0;
|
||||
|
||||
switch (cmode) {
|
||||
case MV88E6XXX_PORT_STS_CMODE_SGMII:
|
||||
case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
|
||||
case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
|
||||
err = mv88e6390_serdes_irq_enable_sgmii(chip, lane);
|
||||
return mv88e6390_serdes_irq_enable_sgmii(chip, lane, enable);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port,
|
||||
u8 lane)
|
||||
{
|
||||
u8 cmode = chip->ports[port].cmode;
|
||||
int err = 0;
|
||||
|
||||
switch (cmode) {
|
||||
case MV88E6XXX_PORT_STS_CMODE_SGMII:
|
||||
case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
|
||||
case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
|
||||
err = mv88e6390_serdes_irq_disable_sgmii(chip, lane);
|
||||
}
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip,
|
||||
@ -636,21 +529,13 @@ static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip,
|
||||
return err;
|
||||
}
|
||||
|
||||
static irqreturn_t mv88e6390_serdes_thread_fn(int irq, void *dev_id)
|
||||
irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
|
||||
u8 lane)
|
||||
{
|
||||
struct mv88e6xxx_port *port = dev_id;
|
||||
struct mv88e6xxx_chip *chip = port->chip;
|
||||
u8 cmode = chip->ports[port].cmode;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u8 cmode = port->cmode;
|
||||
u16 status;
|
||||
int err;
|
||||
u8 lane;
|
||||
|
||||
mv88e6xxx_reg_lock(chip);
|
||||
|
||||
err = mv88e6xxx_serdes_get_lane(chip, port->port, &lane);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
switch (cmode) {
|
||||
case MV88E6XXX_PORT_STS_CMODE_SGMII:
|
||||
@ -658,79 +543,18 @@ static irqreturn_t mv88e6390_serdes_thread_fn(int irq, void *dev_id)
|
||||
case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
|
||||
err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status);
|
||||
if (err)
|
||||
goto out;
|
||||
return ret;
|
||||
if (status & (MV88E6390_SGMII_INT_LINK_DOWN |
|
||||
MV88E6390_SGMII_INT_LINK_UP)) {
|
||||
ret = IRQ_HANDLED;
|
||||
mv88e6390_serdes_irq_link_sgmii(chip, port->port, lane);
|
||||
mv88e6390_serdes_irq_link_sgmii(chip, port, lane);
|
||||
}
|
||||
}
|
||||
out:
|
||||
mv88e6xxx_reg_unlock(chip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port)
|
||||
unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)
|
||||
{
|
||||
int err;
|
||||
u8 lane;
|
||||
|
||||
err = mv88e6xxx_serdes_get_lane(chip, port, &lane);
|
||||
if (err) {
|
||||
if (err == -ENODEV)
|
||||
err = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
chip->ports[port].serdes_irq = irq_find_mapping(chip->g2_irq.domain,
|
||||
port);
|
||||
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.
|
||||
*/
|
||||
mv88e6xxx_reg_unlock(chip);
|
||||
err = request_threaded_irq(chip->ports[port].serdes_irq, NULL,
|
||||
mv88e6390_serdes_thread_fn,
|
||||
IRQF_ONESHOT, "mv88e6xxx-serdes",
|
||||
&chip->ports[port]);
|
||||
mv88e6xxx_reg_lock(chip);
|
||||
|
||||
if (err) {
|
||||
dev_err(chip->dev, "Unable to request SERDES interrupt: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return mv88e6390_serdes_irq_enable(chip, port, lane);
|
||||
}
|
||||
|
||||
void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port)
|
||||
{
|
||||
int err;
|
||||
u8 lane;
|
||||
|
||||
err = mv88e6xxx_serdes_get_lane(chip, port, &lane);
|
||||
if (err) {
|
||||
if (err != -ENODEV)
|
||||
dev_err(chip->dev, "Unable to free SERDES irq: %d\n",
|
||||
err);
|
||||
return;
|
||||
}
|
||||
|
||||
mv88e6390_serdes_irq_disable(chip, port, lane);
|
||||
|
||||
/* Freeing the IRQ will trigger irq callbacks. So we cannot
|
||||
* hold the reg_lock.
|
||||
*/
|
||||
mv88e6xxx_reg_unlock(chip);
|
||||
free_irq(chip->ports[port].serdes_irq, &chip->ports[port]);
|
||||
mv88e6xxx_reg_lock(chip);
|
||||
|
||||
chip->ports[port].serdes_irq = 0;
|
||||
return irq_find_mapping(chip->g2_irq.domain, port);
|
||||
}
|
||||
|
@ -74,37 +74,94 @@
|
||||
#define MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID BIT(11)
|
||||
#define MV88E6390_SGMII_PHY_STATUS_LINK BIT(10)
|
||||
|
||||
/* Put the SERDES lane address a port is using into *lane. If a port has
|
||||
* multiple lanes, should put the first lane the port is using. If a port does
|
||||
* not have a lane, return -ENODEV.
|
||||
*/
|
||||
static inline int mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip,
|
||||
int port, u8 *lane)
|
||||
{
|
||||
if (!chip->info->ops->serdes_get_lane)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return chip->info->ops->serdes_get_lane(chip, port, lane);
|
||||
}
|
||||
|
||||
int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port, u8 *lane);
|
||||
int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port, u8 *lane);
|
||||
int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port, u8 *lane);
|
||||
int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
|
||||
int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
|
||||
int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port);
|
||||
void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port);
|
||||
u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
|
||||
u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
|
||||
u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
|
||||
u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
|
||||
unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip,
|
||||
int port);
|
||||
unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip,
|
||||
int port);
|
||||
int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
|
||||
bool on);
|
||||
int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
|
||||
bool on);
|
||||
int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane,
|
||||
bool enable);
|
||||
int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane,
|
||||
bool enable);
|
||||
irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
|
||||
u8 lane);
|
||||
irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
|
||||
u8 lane);
|
||||
int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port);
|
||||
int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
|
||||
int port, uint8_t *data);
|
||||
int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
|
||||
uint64_t *data);
|
||||
int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port,
|
||||
u8 lane);
|
||||
int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port,
|
||||
u8 lane);
|
||||
int mv88e6352_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port);
|
||||
void mv88e6352_serdes_irq_free(struct mv88e6xxx_chip *chip, int port);
|
||||
|
||||
/* Return the (first) SERDES lane address a port is using, 0 otherwise. */
|
||||
static inline u8 mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip,
|
||||
int port)
|
||||
{
|
||||
if (!chip->info->ops->serdes_get_lane)
|
||||
return 0;
|
||||
|
||||
return chip->info->ops->serdes_get_lane(chip, port);
|
||||
}
|
||||
|
||||
static inline int mv88e6xxx_serdes_power_up(struct mv88e6xxx_chip *chip,
|
||||
int port, u8 lane)
|
||||
{
|
||||
if (!chip->info->ops->serdes_power)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return chip->info->ops->serdes_power(chip, port, lane, true);
|
||||
}
|
||||
|
||||
static inline int mv88e6xxx_serdes_power_down(struct mv88e6xxx_chip *chip,
|
||||
int port, u8 lane)
|
||||
{
|
||||
if (!chip->info->ops->serdes_power)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return chip->info->ops->serdes_power(chip, port, lane, false);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
mv88e6xxx_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)
|
||||
{
|
||||
if (!chip->info->ops->serdes_irq_mapping)
|
||||
return 0;
|
||||
|
||||
return chip->info->ops->serdes_irq_mapping(chip, port);
|
||||
}
|
||||
|
||||
static inline int mv88e6xxx_serdes_irq_enable(struct mv88e6xxx_chip *chip,
|
||||
int port, u8 lane)
|
||||
{
|
||||
if (!chip->info->ops->serdes_irq_enable)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return chip->info->ops->serdes_irq_enable(chip, port, lane, true);
|
||||
}
|
||||
|
||||
static inline int mv88e6xxx_serdes_irq_disable(struct mv88e6xxx_chip *chip,
|
||||
int port, u8 lane)
|
||||
{
|
||||
if (!chip->info->ops->serdes_irq_enable)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return chip->info->ops->serdes_irq_enable(chip, port, lane, false);
|
||||
}
|
||||
|
||||
static inline irqreturn_t
|
||||
mv88e6xxx_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, u8 lane)
|
||||
{
|
||||
if (!chip->info->ops->serdes_irq_status)
|
||||
return IRQ_NONE;
|
||||
|
||||
return chip->info->ops->serdes_irq_status(chip, port, lane);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user