diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/dsa/mv88e6352.c | 222 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx.h | 5 |
2 files changed, 224 insertions, 3 deletions
diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c index 744e6fac8023..8a956f9364a2 100644 --- a/drivers/net/dsa/mv88e6352.c +++ b/drivers/net/dsa/mv88e6352.c | |||
@@ -22,18 +22,18 @@ | |||
22 | #include <net/dsa.h> | 22 | #include <net/dsa.h> |
23 | #include "mv88e6xxx.h" | 23 | #include "mv88e6xxx.h" |
24 | 24 | ||
25 | static int mv88e6352_phy_wait(struct dsa_switch *ds) | 25 | static int mv88e6352_wait(struct dsa_switch *ds, int reg, u16 mask) |
26 | { | 26 | { |
27 | unsigned long timeout = jiffies + HZ / 10; | 27 | unsigned long timeout = jiffies + HZ / 10; |
28 | 28 | ||
29 | while (time_before(jiffies, timeout)) { | 29 | while (time_before(jiffies, timeout)) { |
30 | int ret; | 30 | int ret; |
31 | 31 | ||
32 | ret = REG_READ(REG_GLOBAL2, 0x18); | 32 | ret = REG_READ(REG_GLOBAL2, reg); |
33 | if (ret < 0) | 33 | if (ret < 0) |
34 | return ret; | 34 | return ret; |
35 | 35 | ||
36 | if (!(ret & 0x8000)) | 36 | if (!(ret & mask)) |
37 | return 0; | 37 | return 0; |
38 | 38 | ||
39 | usleep_range(1000, 2000); | 39 | usleep_range(1000, 2000); |
@@ -41,6 +41,21 @@ static int mv88e6352_phy_wait(struct dsa_switch *ds) | |||
41 | return -ETIMEDOUT; | 41 | return -ETIMEDOUT; |
42 | } | 42 | } |
43 | 43 | ||
44 | static inline int mv88e6352_phy_wait(struct dsa_switch *ds) | ||
45 | { | ||
46 | return mv88e6352_wait(ds, 0x18, 0x8000); | ||
47 | } | ||
48 | |||
49 | static inline int mv88e6352_eeprom_load_wait(struct dsa_switch *ds) | ||
50 | { | ||
51 | return mv88e6352_wait(ds, 0x14, 0x0800); | ||
52 | } | ||
53 | |||
54 | static inline int mv88e6352_eeprom_busy_wait(struct dsa_switch *ds) | ||
55 | { | ||
56 | return mv88e6352_wait(ds, 0x14, 0x8000); | ||
57 | } | ||
58 | |||
44 | static int __mv88e6352_phy_read(struct dsa_switch *ds, int addr, int regnum) | 59 | static int __mv88e6352_phy_read(struct dsa_switch *ds, int addr, int regnum) |
45 | { | 60 | { |
46 | int ret; | 61 | int ret; |
@@ -429,6 +444,7 @@ static int mv88e6352_setup(struct dsa_switch *ds) | |||
429 | mutex_init(&ps->smi_mutex); | 444 | mutex_init(&ps->smi_mutex); |
430 | mutex_init(&ps->stats_mutex); | 445 | mutex_init(&ps->stats_mutex); |
431 | mutex_init(&ps->phy_mutex); | 446 | mutex_init(&ps->phy_mutex); |
447 | mutex_init(&ps->eeprom_mutex); | ||
432 | 448 | ||
433 | ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0; | 449 | ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0; |
434 | 450 | ||
@@ -525,6 +541,204 @@ static struct mv88e6xxx_hw_stat mv88e6352_hw_stats[] = { | |||
525 | { "hist_1024_max_bytes", 4, 0x0d, }, | 541 | { "hist_1024_max_bytes", 4, 0x0d, }, |
526 | }; | 542 | }; |
527 | 543 | ||
544 | static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr) | ||
545 | { | ||
546 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
547 | int ret; | ||
548 | |||
549 | mutex_lock(&ps->eeprom_mutex); | ||
550 | |||
551 | ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, 0x14, | ||
552 | 0xc000 | (addr & 0xff)); | ||
553 | if (ret < 0) | ||
554 | goto error; | ||
555 | |||
556 | ret = mv88e6352_eeprom_busy_wait(ds); | ||
557 | if (ret < 0) | ||
558 | goto error; | ||
559 | |||
560 | ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, 0x15); | ||
561 | error: | ||
562 | mutex_unlock(&ps->eeprom_mutex); | ||
563 | return ret; | ||
564 | } | ||
565 | |||
566 | static int mv88e6352_get_eeprom(struct dsa_switch *ds, | ||
567 | struct ethtool_eeprom *eeprom, u8 *data) | ||
568 | { | ||
569 | int offset; | ||
570 | int len; | ||
571 | int ret; | ||
572 | |||
573 | offset = eeprom->offset; | ||
574 | len = eeprom->len; | ||
575 | eeprom->len = 0; | ||
576 | |||
577 | eeprom->magic = 0xc3ec4951; | ||
578 | |||
579 | ret = mv88e6352_eeprom_load_wait(ds); | ||
580 | if (ret < 0) | ||
581 | return ret; | ||
582 | |||
583 | if (offset & 1) { | ||
584 | int word; | ||
585 | |||
586 | word = mv88e6352_read_eeprom_word(ds, offset >> 1); | ||
587 | if (word < 0) | ||
588 | return word; | ||
589 | |||
590 | *data++ = (word >> 8) & 0xff; | ||
591 | |||
592 | offset++; | ||
593 | len--; | ||
594 | eeprom->len++; | ||
595 | } | ||
596 | |||
597 | while (len >= 2) { | ||
598 | int word; | ||
599 | |||
600 | word = mv88e6352_read_eeprom_word(ds, offset >> 1); | ||
601 | if (word < 0) | ||
602 | return word; | ||
603 | |||
604 | *data++ = word & 0xff; | ||
605 | *data++ = (word >> 8) & 0xff; | ||
606 | |||
607 | offset += 2; | ||
608 | len -= 2; | ||
609 | eeprom->len += 2; | ||
610 | } | ||
611 | |||
612 | if (len) { | ||
613 | int word; | ||
614 | |||
615 | word = mv88e6352_read_eeprom_word(ds, offset >> 1); | ||
616 | if (word < 0) | ||
617 | return word; | ||
618 | |||
619 | *data++ = word & 0xff; | ||
620 | |||
621 | offset++; | ||
622 | len--; | ||
623 | eeprom->len++; | ||
624 | } | ||
625 | |||
626 | return 0; | ||
627 | } | ||
628 | |||
629 | static int mv88e6352_eeprom_is_readonly(struct dsa_switch *ds) | ||
630 | { | ||
631 | int ret; | ||
632 | |||
633 | ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, 0x14); | ||
634 | if (ret < 0) | ||
635 | return ret; | ||
636 | |||
637 | if (!(ret & 0x0400)) | ||
638 | return -EROFS; | ||
639 | |||
640 | return 0; | ||
641 | } | ||
642 | |||
643 | static int mv88e6352_write_eeprom_word(struct dsa_switch *ds, int addr, | ||
644 | u16 data) | ||
645 | { | ||
646 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
647 | int ret; | ||
648 | |||
649 | mutex_lock(&ps->eeprom_mutex); | ||
650 | |||
651 | ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, 0x15, data); | ||
652 | if (ret < 0) | ||
653 | goto error; | ||
654 | |||
655 | ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, 0x14, | ||
656 | 0xb000 | (addr & 0xff)); | ||
657 | if (ret < 0) | ||
658 | goto error; | ||
659 | |||
660 | ret = mv88e6352_eeprom_busy_wait(ds); | ||
661 | error: | ||
662 | mutex_unlock(&ps->eeprom_mutex); | ||
663 | return ret; | ||
664 | } | ||
665 | |||
666 | static int mv88e6352_set_eeprom(struct dsa_switch *ds, | ||
667 | struct ethtool_eeprom *eeprom, u8 *data) | ||
668 | { | ||
669 | int offset; | ||
670 | int ret; | ||
671 | int len; | ||
672 | |||
673 | if (eeprom->magic != 0xc3ec4951) | ||
674 | return -EINVAL; | ||
675 | |||
676 | ret = mv88e6352_eeprom_is_readonly(ds); | ||
677 | if (ret) | ||
678 | return ret; | ||
679 | |||
680 | offset = eeprom->offset; | ||
681 | len = eeprom->len; | ||
682 | eeprom->len = 0; | ||
683 | |||
684 | ret = mv88e6352_eeprom_load_wait(ds); | ||
685 | if (ret < 0) | ||
686 | return ret; | ||
687 | |||
688 | if (offset & 1) { | ||
689 | int word; | ||
690 | |||
691 | word = mv88e6352_read_eeprom_word(ds, offset >> 1); | ||
692 | if (word < 0) | ||
693 | return word; | ||
694 | |||
695 | word = (*data++ << 8) | (word & 0xff); | ||
696 | |||
697 | ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word); | ||
698 | if (ret < 0) | ||
699 | return ret; | ||
700 | |||
701 | offset++; | ||
702 | len--; | ||
703 | eeprom->len++; | ||
704 | } | ||
705 | |||
706 | while (len >= 2) { | ||
707 | int word; | ||
708 | |||
709 | word = *data++; | ||
710 | word |= *data++ << 8; | ||
711 | |||
712 | ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word); | ||
713 | if (ret < 0) | ||
714 | return ret; | ||
715 | |||
716 | offset += 2; | ||
717 | len -= 2; | ||
718 | eeprom->len += 2; | ||
719 | } | ||
720 | |||
721 | if (len) { | ||
722 | int word; | ||
723 | |||
724 | word = mv88e6352_read_eeprom_word(ds, offset >> 1); | ||
725 | if (word < 0) | ||
726 | return word; | ||
727 | |||
728 | word = (word & 0xff00) | *data++; | ||
729 | |||
730 | ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word); | ||
731 | if (ret < 0) | ||
732 | return ret; | ||
733 | |||
734 | offset++; | ||
735 | len--; | ||
736 | eeprom->len++; | ||
737 | } | ||
738 | |||
739 | return 0; | ||
740 | } | ||
741 | |||
528 | static void | 742 | static void |
529 | mv88e6352_get_strings(struct dsa_switch *ds, int port, uint8_t *data) | 743 | mv88e6352_get_strings(struct dsa_switch *ds, int port, uint8_t *data) |
530 | { | 744 | { |
@@ -562,6 +776,8 @@ struct dsa_switch_driver mv88e6352_switch_driver = { | |||
562 | .set_temp_limit = mv88e6352_set_temp_limit, | 776 | .set_temp_limit = mv88e6352_set_temp_limit, |
563 | .get_temp_alarm = mv88e6352_get_temp_alarm, | 777 | .get_temp_alarm = mv88e6352_get_temp_alarm, |
564 | #endif | 778 | #endif |
779 | .get_eeprom = mv88e6352_get_eeprom, | ||
780 | .set_eeprom = mv88e6352_set_eeprom, | ||
565 | }; | 781 | }; |
566 | 782 | ||
567 | MODULE_ALIAS("platform:mv88e6352"); | 783 | MODULE_ALIAS("platform:mv88e6352"); |
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index c0ce133c756b..29feed02f484 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h | |||
@@ -43,6 +43,11 @@ struct mv88e6xxx_priv_state { | |||
43 | */ | 43 | */ |
44 | struct mutex phy_mutex; | 44 | struct mutex phy_mutex; |
45 | 45 | ||
46 | /* This mutex serializes eeprom access for chips with | ||
47 | * eeprom support. | ||
48 | */ | ||
49 | struct mutex eeprom_mutex; | ||
50 | |||
46 | int id; /* switch product id */ | 51 | int id; /* switch product id */ |
47 | }; | 52 | }; |
48 | 53 | ||