aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/dsa
diff options
context:
space:
mode:
authorGuenter Roeck <linux@roeck-us.net>2014-10-29 13:45:03 -0400
committerDavid S. Miller <davem@davemloft.net>2014-10-30 14:54:11 -0400
commit33b43df40a3827365ff80fae79f7c59c107508d3 (patch)
tree2c8d5e987d9aa5b2bf00e8aebb5d91695291717d /drivers/net/dsa
parent06745729c48e3677a64db63481184cc7aef1ea69 (diff)
net: dsa/mv88e6352: Implement EEPROM access functions
MV88E6352 supports read and write access to its configuration eeprom. There is no means to detect if an EEPROM is connected to the switch. Also, the switch supports EEPROMs with different sizes, but can not detect or report the type or size of connected EEPROMs. Therefore, do not implement the get_eeprom_len callback but depend on platform or devicetree data to provide information about EEPROM presence and size. Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/dsa')
-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