aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Lunn <andrew@lunn.ch>2018-08-09 09:38:48 -0400
committerDavid S. Miller <davem@davemloft.net>2018-08-09 14:08:20 -0400
commitefd1ba6af93ff63d40f92515a83405133145c028 (patch)
tree46e5072b99ad19c2987c57815224674aecc50d1d
parent7b898469b91ea08e42dbed52acf8dfcb4f5914d0 (diff)
net: dsa: mv88e6xxx: Add SERDES phydev_mac_change up for 6390
phylink wants to know when the MAC layers notices a change in the link. For the 6390 family, this is a change in the SERDES state. Add interrupt support for the SERDES interface used to implement SGMII/1000Base-X/2500Base-X. This is currently limited to ports 9 and 10. Support for the 10G SERDES and other ports will be added later, building on this basic framework. Signed-off-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.c22
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.h5
-rw-r--r--drivers/net/dsa/mv88e6xxx/serdes.c179
-rw-r--r--drivers/net/dsa/mv88e6xxx/serdes.h16
4 files changed, 222 insertions, 0 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 5845cbf7f096..17752316ab10 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -2337,7 +2337,12 @@ static int mv88e6xxx_port_enable(struct dsa_switch *ds, int port,
2337 int err; 2337 int err;
2338 2338
2339 mutex_lock(&chip->reg_lock); 2339 mutex_lock(&chip->reg_lock);
2340
2340 err = mv88e6xxx_serdes_power(chip, port, true); 2341 err = mv88e6xxx_serdes_power(chip, port, true);
2342
2343 if (!err && chip->info->ops->serdes_irq_setup)
2344 err = chip->info->ops->serdes_irq_setup(chip, port);
2345
2341 mutex_unlock(&chip->reg_lock); 2346 mutex_unlock(&chip->reg_lock);
2342 2347
2343 return err; 2348 return err;
@@ -2349,8 +2354,13 @@ static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port,
2349 struct mv88e6xxx_chip *chip = ds->priv; 2354 struct mv88e6xxx_chip *chip = ds->priv;
2350 2355
2351 mutex_lock(&chip->reg_lock); 2356 mutex_lock(&chip->reg_lock);
2357
2358 if (chip->info->ops->serdes_irq_free)
2359 chip->info->ops->serdes_irq_free(chip, port);
2360
2352 if (mv88e6xxx_serdes_power(chip, port, false)) 2361 if (mv88e6xxx_serdes_power(chip, port, false))
2353 dev_err(chip->dev, "failed to power off SERDES\n"); 2362 dev_err(chip->dev, "failed to power off SERDES\n");
2363
2354 mutex_unlock(&chip->reg_lock); 2364 mutex_unlock(&chip->reg_lock);
2355} 2365}
2356 2366
@@ -3225,6 +3235,8 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
3225 .vtu_getnext = mv88e6390_g1_vtu_getnext, 3235 .vtu_getnext = mv88e6390_g1_vtu_getnext,
3226 .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, 3236 .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
3227 .serdes_power = mv88e6390_serdes_power, 3237 .serdes_power = mv88e6390_serdes_power,
3238 .serdes_irq_setup = mv88e6390_serdes_irq_setup,
3239 .serdes_irq_free = mv88e6390_serdes_irq_free,
3228 .gpio_ops = &mv88e6352_gpio_ops, 3240 .gpio_ops = &mv88e6352_gpio_ops,
3229 .phylink_validate = mv88e6390_phylink_validate, 3241 .phylink_validate = mv88e6390_phylink_validate,
3230}; 3242};
@@ -3265,6 +3277,8 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
3265 .vtu_getnext = mv88e6390_g1_vtu_getnext, 3277 .vtu_getnext = mv88e6390_g1_vtu_getnext,
3266 .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, 3278 .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
3267 .serdes_power = mv88e6390x_serdes_power, 3279 .serdes_power = mv88e6390x_serdes_power,
3280 .serdes_irq_setup = mv88e6390_serdes_irq_setup,
3281 .serdes_irq_free = mv88e6390_serdes_irq_free,
3268 .gpio_ops = &mv88e6352_gpio_ops, 3282 .gpio_ops = &mv88e6352_gpio_ops,
3269 .phylink_validate = mv88e6390x_phylink_validate, 3283 .phylink_validate = mv88e6390x_phylink_validate,
3270}; 3284};
@@ -3305,6 +3319,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
3305 .vtu_getnext = mv88e6390_g1_vtu_getnext, 3319 .vtu_getnext = mv88e6390_g1_vtu_getnext,
3306 .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, 3320 .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
3307 .serdes_power = mv88e6390_serdes_power, 3321 .serdes_power = mv88e6390_serdes_power,
3322 .serdes_irq_setup = mv88e6390_serdes_irq_setup,
3323 .serdes_irq_free = mv88e6390_serdes_irq_free,
3308 .avb_ops = &mv88e6390_avb_ops, 3324 .avb_ops = &mv88e6390_avb_ops,
3309 .ptp_ops = &mv88e6352_ptp_ops, 3325 .ptp_ops = &mv88e6352_ptp_ops,
3310 .phylink_validate = mv88e6390_phylink_validate, 3326 .phylink_validate = mv88e6390_phylink_validate,
@@ -3393,6 +3409,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
3393 .vtu_getnext = mv88e6390_g1_vtu_getnext, 3409 .vtu_getnext = mv88e6390_g1_vtu_getnext,
3394 .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, 3410 .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
3395 .serdes_power = mv88e6390_serdes_power, 3411 .serdes_power = mv88e6390_serdes_power,
3412 .serdes_irq_setup = mv88e6390_serdes_irq_setup,
3413 .serdes_irq_free = mv88e6390_serdes_irq_free,
3396 .gpio_ops = &mv88e6352_gpio_ops, 3414 .gpio_ops = &mv88e6352_gpio_ops,
3397 .avb_ops = &mv88e6390_avb_ops, 3415 .avb_ops = &mv88e6390_avb_ops,
3398 .ptp_ops = &mv88e6352_ptp_ops, 3416 .ptp_ops = &mv88e6352_ptp_ops,
@@ -3694,6 +3712,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
3694 .vtu_getnext = mv88e6390_g1_vtu_getnext, 3712 .vtu_getnext = mv88e6390_g1_vtu_getnext,
3695 .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, 3713 .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
3696 .serdes_power = mv88e6390_serdes_power, 3714 .serdes_power = mv88e6390_serdes_power,
3715 .serdes_irq_setup = mv88e6390_serdes_irq_setup,
3716 .serdes_irq_free = mv88e6390_serdes_irq_free,
3697 .gpio_ops = &mv88e6352_gpio_ops, 3717 .gpio_ops = &mv88e6352_gpio_ops,
3698 .avb_ops = &mv88e6390_avb_ops, 3718 .avb_ops = &mv88e6390_avb_ops,
3699 .ptp_ops = &mv88e6352_ptp_ops, 3719 .ptp_ops = &mv88e6352_ptp_ops,
@@ -3739,6 +3759,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
3739 .vtu_getnext = mv88e6390_g1_vtu_getnext, 3759 .vtu_getnext = mv88e6390_g1_vtu_getnext,
3740 .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, 3760 .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
3741 .serdes_power = mv88e6390x_serdes_power, 3761 .serdes_power = mv88e6390x_serdes_power,
3762 .serdes_irq_setup = mv88e6390_serdes_irq_setup,
3763 .serdes_irq_free = mv88e6390_serdes_irq_free,
3742 .gpio_ops = &mv88e6352_gpio_ops, 3764 .gpio_ops = &mv88e6352_gpio_ops,
3743 .avb_ops = &mv88e6390_avb_ops, 3765 .avb_ops = &mv88e6390_avb_ops,
3744 .ptp_ops = &mv88e6352_ptp_ops, 3766 .ptp_ops = &mv88e6352_ptp_ops,
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index 577398fe36df..f9ecb7872d32 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -200,6 +200,7 @@ struct mv88e6xxx_port {
200 u64 vtu_member_violation; 200 u64 vtu_member_violation;
201 u64 vtu_miss_violation; 201 u64 vtu_miss_violation;
202 u8 cmode; 202 u8 cmode;
203 int serdes_irq;
203}; 204};
204 205
205struct mv88e6xxx_chip { 206struct mv88e6xxx_chip {
@@ -434,6 +435,10 @@ struct mv88e6xxx_ops {
434 /* Power on/off a SERDES interface */ 435 /* Power on/off a SERDES interface */
435 int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, bool on); 436 int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, bool on);
436 437
438 /* SERDES interrupt handling */
439 int (*serdes_irq_setup)(struct mv88e6xxx_chip *chip, int port);
440 void (*serdes_irq_free)(struct mv88e6xxx_chip *chip, int port);
441
437 /* Statistics from the SERDES interface */ 442 /* Statistics from the SERDES interface */
438 int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port); 443 int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port);
439 int (*serdes_get_strings)(struct mv88e6xxx_chip *chip, int port, 444 int (*serdes_get_strings)(struct mv88e6xxx_chip *chip, int port,
diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c
index 064d0bb8fe02..519346b81b87 100644
--- a/drivers/net/dsa/mv88e6xxx/serdes.c
+++ b/drivers/net/dsa/mv88e6xxx/serdes.c
@@ -11,6 +11,8 @@
11 * (at your option) any later version. 11 * (at your option) any later version.
12 */ 12 */
13 13
14#include <linux/interrupt.h>
15#include <linux/irqdomain.h>
14#include <linux/mii.h> 16#include <linux/mii.h>
15 17
16#include "chip.h" 18#include "chip.h"
@@ -399,6 +401,183 @@ int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
399 return 0; 401 return 0;
400} 402}
401 403
404static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
405 int port, int lane)
406{
407 struct dsa_switch *ds = chip->ds;
408 u16 status;
409 bool up;
410
411 mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
412 MV88E6390_SGMII_STATUS, &status);
413
414 /* Status must be read twice in order to give the current link
415 * status. Otherwise the change in link status since the last
416 * read of the register is returned.
417 */
418 mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
419 MV88E6390_SGMII_STATUS, &status);
420 up = status & MV88E6390_SGMII_STATUS_LINK;
421
422 dsa_port_phylink_mac_change(ds, port, up);
423}
424
425static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip,
426 int lane)
427{
428 return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
429 MV88E6390_SGMII_INT_ENABLE,
430 MV88E6390_SGMII_INT_LINK_DOWN |
431 MV88E6390_SGMII_INT_LINK_UP);
432}
433
434static int mv88e6390_serdes_irq_disable_sgmii(struct mv88e6xxx_chip *chip,
435 int lane)
436{
437 return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
438 MV88E6390_SGMII_INT_ENABLE, 0);
439}
440
441int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port,
442 int lane)
443{
444 u8 cmode = chip->ports[port].cmode;
445 int err = 0;
446
447 switch (cmode) {
448 case MV88E6XXX_PORT_STS_CMODE_SGMII:
449 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
450 case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
451 err = mv88e6390_serdes_irq_enable_sgmii(chip, lane);
452 }
453
454 return err;
455}
456
457int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port,
458 int lane)
459{
460 u8 cmode = chip->ports[port].cmode;
461 int err = 0;
462
463 switch (cmode) {
464 case MV88E6XXX_PORT_STS_CMODE_SGMII:
465 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
466 case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
467 err = mv88e6390_serdes_irq_disable_sgmii(chip, lane);
468 }
469
470 return err;
471}
472
473static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip,
474 int lane, u16 *status)
475{
476 int err;
477
478 err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
479 MV88E6390_SGMII_INT_STATUS, status);
480
481 return err;
482}
483
484static irqreturn_t mv88e6390_serdes_thread_fn(int irq, void *dev_id)
485{
486 struct mv88e6xxx_port *port = dev_id;
487 struct mv88e6xxx_chip *chip = port->chip;
488 irqreturn_t ret = IRQ_NONE;
489 u8 cmode = port->cmode;
490 u16 status;
491 int lane;
492 int err;
493
494 lane = mv88e6390x_serdes_get_lane(chip, port->port);
495
496 mutex_lock(&chip->reg_lock);
497
498 switch (cmode) {
499 case MV88E6XXX_PORT_STS_CMODE_SGMII:
500 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
501 case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
502 err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status);
503 if (err)
504 goto out;
505 if (status && (MV88E6390_SGMII_INT_LINK_DOWN ||
506 MV88E6390_SGMII_INT_LINK_UP)) {
507 ret = IRQ_HANDLED;
508 mv88e6390_serdes_irq_link_sgmii(chip, port->port, lane);
509 }
510 }
511out:
512 mutex_unlock(&chip->reg_lock);
513
514 return ret;
515}
516
517int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port)
518{
519 int lane;
520 int err;
521
522 /* Only support ports 9 and 10 at the moment */
523 if (port < 9)
524 return 0;
525
526 lane = mv88e6390x_serdes_get_lane(chip, port);
527
528 if (lane == -ENODEV)
529 return 0;
530
531 if (lane < 0)
532 return lane;
533
534 chip->ports[port].serdes_irq = irq_find_mapping(chip->g2_irq.domain,
535 port);
536 if (chip->ports[port].serdes_irq < 0) {
537 dev_err(chip->dev, "Unable to map SERDES irq: %d\n",
538 chip->ports[port].serdes_irq);
539 return chip->ports[port].serdes_irq;
540 }
541
542 /* Requesting the IRQ will trigger irq callbacks. So we cannot
543 * hold the reg_lock.
544 */
545 mutex_unlock(&chip->reg_lock);
546 err = request_threaded_irq(chip->ports[port].serdes_irq, NULL,
547 mv88e6390_serdes_thread_fn,
548 IRQF_ONESHOT, "mv88e6xxx-serdes",
549 &chip->ports[port]);
550 mutex_lock(&chip->reg_lock);
551
552 if (err) {
553 dev_err(chip->dev, "Unable to request SERDES interrupt: %d\n",
554 err);
555 return err;
556 }
557
558 return mv88e6390_serdes_irq_enable(chip, port, lane);
559}
560
561void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port)
562{
563 int lane = mv88e6390x_serdes_get_lane(chip, port);
564
565 if (port < 9)
566 return;
567
568 if (lane < 0)
569 return;
570
571 mv88e6390_serdes_irq_disable(chip, port, lane);
572
573 /* Freeing the IRQ will trigger irq callbacks. So we cannot
574 * hold the reg_lock.
575 */
576 mutex_unlock(&chip->reg_lock);
577 free_irq(chip->ports[port].serdes_irq, &chip->ports[port]);
578 mutex_lock(&chip->reg_lock);
579}
580
402int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) 581int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
403{ 582{
404 u8 cmode = chip->ports[port].cmode; 583 u8 cmode = chip->ports[port].cmode;
diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h
index a64ca1974988..09da08cb5261 100644
--- a/drivers/net/dsa/mv88e6xxx/serdes.h
+++ b/drivers/net/dsa/mv88e6xxx/serdes.h
@@ -42,11 +42,27 @@
42#define MV88E6390_SGMII_CONTROL_RESET BIT(15) 42#define MV88E6390_SGMII_CONTROL_RESET BIT(15)
43#define MV88E6390_SGMII_CONTROL_LOOPBACK BIT(14) 43#define MV88E6390_SGMII_CONTROL_LOOPBACK BIT(14)
44#define MV88E6390_SGMII_CONTROL_PDOWN BIT(11) 44#define MV88E6390_SGMII_CONTROL_PDOWN BIT(11)
45#define MV88E6390_SGMII_STATUS 0x2001
46#define MV88E6390_SGMII_STATUS_AN_DONE BIT(5)
47#define MV88E6390_SGMII_STATUS_REMOTE_FAULT BIT(4)
48#define MV88E6390_SGMII_STATUS_LINK BIT(2)
49#define MV88E6390_SGMII_INT_ENABLE 0xa001
50#define MV88E6390_SGMII_INT_SPEED_CHANGE BIT(14)
51#define MV88E6390_SGMII_INT_DUPLEX_CHANGE BIT(13)
52#define MV88E6390_SGMII_INT_PAGE_RX BIT(12)
53#define MV88E6390_SGMII_INT_AN_COMPLETE BIT(11)
54#define MV88E6390_SGMII_INT_LINK_DOWN BIT(10)
55#define MV88E6390_SGMII_INT_LINK_UP BIT(9)
56#define MV88E6390_SGMII_INT_SYMBOL_ERROR BIT(8)
57#define MV88E6390_SGMII_INT_FALSE_CARRIER BIT(7)
58#define MV88E6390_SGMII_INT_STATUS 0xa002
45 59
46int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); 60int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
47int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); 61int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
48int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); 62int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
49int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); 63int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
64int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port);
65void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port);
50int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port); 66int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port);
51int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, 67int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
52 int port, uint8_t *data); 68 int port, uint8_t *data);