aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/dsa/mv88e6352.c222
-rw-r--r--drivers/net/dsa/mv88e6xxx.h5
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
25static int mv88e6352_phy_wait(struct dsa_switch *ds) 25static 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
44static inline int mv88e6352_phy_wait(struct dsa_switch *ds)
45{
46 return mv88e6352_wait(ds, 0x18, 0x8000);
47}
48
49static inline int mv88e6352_eeprom_load_wait(struct dsa_switch *ds)
50{
51 return mv88e6352_wait(ds, 0x14, 0x0800);
52}
53
54static inline int mv88e6352_eeprom_busy_wait(struct dsa_switch *ds)
55{
56 return mv88e6352_wait(ds, 0x14, 0x8000);
57}
58
44static int __mv88e6352_phy_read(struct dsa_switch *ds, int addr, int regnum) 59static 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
544static 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);
561error:
562 mutex_unlock(&ps->eeprom_mutex);
563 return ret;
564}
565
566static 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
629static 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
643static 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);
661error:
662 mutex_unlock(&ps->eeprom_mutex);
663 return ret;
664}
665
666static 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
528static void 742static void
529mv88e6352_get_strings(struct dsa_switch *ds, int port, uint8_t *data) 743mv88e6352_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
567MODULE_ALIAS("platform:mv88e6352"); 783MODULE_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