aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorReece R. Pollack2013-08-15 17:13:12 -0500
committerMurali Karicheri2014-12-03 10:29:33 -0600
commit2953d2c9838a779b08c9a2eceac6279bd7ec4b3f (patch)
tree393dcef356e341e15615549193506351c6c4caa6 /drivers/net/ethernet/ti/cpsw_ale.c
parente1d4fc9f5555671662456197de5bbfc31823c502 (diff)
downloadlinux-2953d2c9838a779b08c9a2eceac6279bd7ec4b3f.tar.gz
linux-2953d2c9838a779b08c9a2eceac6279bd7ec4b3f.tar.xz
linux-2953d2c9838a779b08c9a2eceac6279bd7ec4b3f.zip
drivers: net: cpsw: Add sysfs attributes "ale_control" and "ale_table"
Add support for two new sysfs attributes "ale_control" and "ale_table" to allow visibility into the contents of the Address Lookup Engine. This is a forward port of code originally written by Sandeep Paulraj and committed under ID 26368250396825790ee31745f7d277d3890f4dcd. Signed-off-by: Reece R. Pollack <x0183204@ti.com>
Diffstat (limited to 'drivers/net/ethernet/ti/cpsw_ale.c')
-rw-r--r--drivers/net/ethernet/ti/cpsw_ale.c178
1 files changed, 178 insertions, 0 deletions
diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c
index 7fa60d6092e..bb50893c2c9 100644
--- a/drivers/net/ethernet/ti/cpsw_ale.c
+++ b/drivers/net/ethernet/ti/cpsw_ale.c
@@ -25,6 +25,9 @@
25#include "cpsw_ale.h" 25#include "cpsw_ale.h"
26 26
27#define BITMASK(bits) (BIT(bits) - 1) 27#define BITMASK(bits) (BIT(bits) - 1)
28#define ADDR_FMT_STR "%02x:%02x:%02x:%02x:%02x:%02x"
29#define ADDR_FMT_ARGS(addr) (addr)[0], (addr)[1], (addr)[2], \
30 (addr)[3], (addr)[4], (addr)[5]
28#define ALE_ENTRY_BITS 68 31#define ALE_ENTRY_BITS 68
29#define ALE_ENTRY_WORDS DIV_ROUND_UP(ALE_ENTRY_BITS, 32) 32#define ALE_ENTRY_WORDS DIV_ROUND_UP(ALE_ENTRY_BITS, 32)
30 33
@@ -677,6 +680,170 @@ int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control)
677 return tmp & BITMASK(info->bits); 680 return tmp & BITMASK(info->bits);
678} 681}
679 682
683static int cpsw_ale_dump_mcast(u32 *ale_entry, char *buf, int len)
684{
685 int outlen = 0;
686 static const char * const str_mcast_state[] = {"f", "blf", "lf", "f"};
687 int mcast_state = cpsw_ale_get_mcast_state(ale_entry);
688 int port_mask = cpsw_ale_get_port_mask(ale_entry);
689 int super = cpsw_ale_get_super(ale_entry);
690
691 outlen += snprintf(buf + outlen, len - outlen,
692 "mcstate: %s(%d), ", str_mcast_state[mcast_state],
693 mcast_state);
694 outlen += snprintf(buf + outlen, len - outlen,
695 "port mask: %x, %ssuper\n", port_mask,
696 super ? "" : "no ");
697 return outlen;
698}
699
700static int cpsw_ale_dump_ucast(u32 *ale_entry, char *buf, int len)
701{
702 int outlen = 0;
703 static const char * const str_ucast_type[] = {"persistant", "untouched",
704 "oui", "touched"};
705 int ucast_type = cpsw_ale_get_ucast_type(ale_entry);
706 int port_num = cpsw_ale_get_port_num(ale_entry);
707 int secure = cpsw_ale_get_secure(ale_entry);
708 int blocked = cpsw_ale_get_blocked(ale_entry);
709
710 outlen += snprintf(buf + outlen, len - outlen,
711 "uctype: %s(%d), ", str_ucast_type[ucast_type],
712 ucast_type);
713 outlen += snprintf(buf + outlen, len - outlen,
714 "port: %d%s%s\n", port_num, secure ? ", Secure" : "",
715 blocked ? ", Blocked" : "");
716 return outlen;
717}
718
719static int cpsw_ale_dump_entry(int idx, u32 *ale_entry, char *buf, int len)
720{
721 int type, outlen = 0;
722 u8 addr[6];
723 static const char * const str_type[] = {"free", "addr",
724 "vlan", "vlan+addr"};
725
726 type = cpsw_ale_get_entry_type(ale_entry);
727 if (type == ALE_TYPE_FREE)
728 return outlen;
729
730 if (idx >= 0) {
731 outlen += snprintf(buf + outlen, len - outlen,
732 "index %d, ", idx);
733 }
734
735 outlen += snprintf(buf + outlen, len - outlen, "raw: %08x %08x %08x, ",
736 ale_entry[0], ale_entry[1], ale_entry[2]);
737
738 outlen += snprintf(buf + outlen, len - outlen,
739 "type: %s(%d), ", str_type[type], type);
740
741 cpsw_ale_get_addr(ale_entry, addr);
742 outlen += snprintf(buf + outlen, len - outlen,
743 "addr: " ADDR_FMT_STR ", ", ADDR_FMT_ARGS(addr));
744
745 if (type == ALE_TYPE_VLAN || type == ALE_TYPE_VLAN_ADDR) {
746 outlen += snprintf(buf + outlen, len - outlen, "vlan: %d, ",
747 cpsw_ale_get_vlan_id(ale_entry));
748 }
749
750 outlen += cpsw_ale_get_mcast(ale_entry) ?
751 cpsw_ale_dump_mcast(ale_entry, buf + outlen, len - outlen) :
752 cpsw_ale_dump_ucast(ale_entry, buf + outlen, len - outlen);
753
754 return outlen;
755}
756
757static ssize_t cpsw_ale_control_show(struct device *dev,
758 struct device_attribute *attr,
759 char *buf)
760{
761 int i, port, len = 0;
762 const struct ale_control_info *info;
763 struct cpsw_ale *ale = control_attr_to_ale(attr);
764
765 for (i = 0, info = ale_controls; i < ALE_NUM_CONTROLS; i++, info++) {
766 /* global controls */
767 if (info->port_shift == 0 && info->port_offset == 0) {
768 len += snprintf(buf + len, SZ_4K - len,
769 "%s=%d\n", info->name,
770 cpsw_ale_control_get(ale, 0, i));
771 continue;
772 }
773
774 /* port specific controls */
775 for (port = 0; port < ale->params.ale_ports; port++) {
776 len += snprintf(buf + len, SZ_4K - len,
777 "%s.%d=%d\n", info->name, port,
778 cpsw_ale_control_get(ale, port, i));
779 }
780 }
781
782 return len;
783}
784
785static ssize_t cpsw_ale_control_store(struct device *dev,
786 struct device_attribute *attr,
787 const char *buf, size_t count)
788{
789 char ctrl_str[33];
790 int port = 0, value, len, ret, control;
791 unsigned long end;
792 struct cpsw_ale *ale = control_attr_to_ale(attr);
793
794 len = strcspn(buf, ".=");
795 if (len >= 32)
796 return -ENOMEM;
797
798 strncpy(ctrl_str, buf, len);
799 ctrl_str[len] = '\0';
800 buf += len;
801
802 if (*buf == '.')
803 port = kstrtoul(buf + 1, 0, &end);
804
805 if (*buf != '=')
806 return -EINVAL;
807
808 value = kstrtoul(buf + 1, 0, NULL);
809
810 for (control = 0; control < ALE_NUM_CONTROLS; control++)
811 if (strcmp(ctrl_str, ale_controls[control].name) == 0)
812 break;
813
814 if (control >= ALE_NUM_CONTROLS)
815 return -ENOENT;
816
817 dev_dbg(ale->params.dev, "processing command %s.%d=%d\n",
818 ale_controls[control].name, port, value);
819
820 ret = cpsw_ale_control_set(ale, port, control, value);
821 if (ret < 0)
822 return ret;
823
824 return count;
825}
826DEVICE_ATTR(ale_control, S_IRUGO | S_IWUSR, cpsw_ale_control_show,
827 cpsw_ale_control_store);
828
829static ssize_t cpsw_ale_table_show(struct device *dev,
830 struct device_attribute *attr,
831 char *buf)
832{
833 int len = SZ_4K, outlen = 0, idx;
834 u32 ale_entry[ALE_ENTRY_WORDS];
835 struct cpsw_ale *ale = table_attr_to_ale(attr);
836
837 for (idx = 0; idx < ale->params.ale_entries; idx++) {
838 cpsw_ale_read(ale, idx, ale_entry);
839 outlen += cpsw_ale_dump_entry(idx, ale_entry, buf + outlen,
840 len - outlen);
841 }
842
843 return outlen;
844}
845DEVICE_ATTR(ale_table, S_IRUGO, cpsw_ale_table_show, NULL);
846
680static void cpsw_ale_timer(unsigned long arg) 847static void cpsw_ale_timer(unsigned long arg)
681{ 848{
682 struct cpsw_ale *ale = (struct cpsw_ale *)arg; 849 struct cpsw_ale *ale = (struct cpsw_ale *)arg;
@@ -703,6 +870,7 @@ int cpsw_ale_set_ageout(struct cpsw_ale *ale, int ageout)
703void cpsw_ale_start(struct cpsw_ale *ale) 870void cpsw_ale_start(struct cpsw_ale *ale)
704{ 871{
705 u32 rev; 872 u32 rev;
873 int ret;
706 874
707 rev = __raw_readl(ale->params.ale_regs + ALE_IDVER); 875 rev = __raw_readl(ale->params.ale_regs + ALE_IDVER);
708 dev_dbg(ale->params.dev, "initialized cpsw ale revision %d.%d\n", 876 dev_dbg(ale->params.dev, "initialized cpsw ale revision %d.%d\n",
@@ -710,6 +878,14 @@ void cpsw_ale_start(struct cpsw_ale *ale)
710 cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1); 878 cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1);
711 cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1); 879 cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
712 880
881 ale->ale_control_attr = dev_attr_ale_control;
882 ret = device_create_file(ale->params.dev, &ale->ale_control_attr);
883 WARN_ON(ret < 0);
884
885 ale->ale_table_attr = dev_attr_ale_table;
886 ret = device_create_file(ale->params.dev, &ale->ale_table_attr);
887 WARN_ON(ret < 0);
888
713 init_timer(&ale->timer); 889 init_timer(&ale->timer);
714 ale->timer.data = (unsigned long)ale; 890 ale->timer.data = (unsigned long)ale;
715 ale->timer.function = cpsw_ale_timer; 891 ale->timer.function = cpsw_ale_timer;
@@ -722,6 +898,8 @@ void cpsw_ale_start(struct cpsw_ale *ale)
722void cpsw_ale_stop(struct cpsw_ale *ale) 898void cpsw_ale_stop(struct cpsw_ale *ale)
723{ 899{
724 del_timer_sync(&ale->timer); 900 del_timer_sync(&ale->timer);
901 device_remove_file(ale->params.dev, &ale->ale_table_attr);
902 device_remove_file(ale->params.dev, &ale->ale_control_attr);
725} 903}
726 904
727struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params) 905struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)