/* * Keystone GBE and XGBE sysfs driver code * * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ * * Authors: Sandeep Nair * Sandeep Paulraj * Cyril Chemparathy * Santosh Shilimkar * Wingman Kwok * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation version 2. * * This program is distributed "as is" WITHOUT ANY WARRANTY of any * kind, whether express or implied; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "netcp_ethss.h" #define to_gbe_dev(obj) container_of(obj, struct gbe_priv, kobj) #define tx_pri_to_gbe_dev(obj) container_of(obj, struct gbe_priv, tx_pri_kobj) #define pvlan_to_gbe_dev(obj) container_of(obj, struct gbe_priv, pvlan_kobj) #define stats_to_gbe_dev(obj) container_of(obj, struct gbe_priv, stats_kobj) #define gbe_sw_mod_info_field_val(r, i) \ ((r & BITMASK(i->bits, i->shift)) >> i->shift) #define __GBE_SW_ATTR_FULL(_name, _mode, _show, _store, _info, \ _info_size, _ctxt) \ { \ .attr = {.name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ .info = _info, \ .info_size = _info_size, \ .context = (_ctxt), \ } #define __GBE_SW_ATTR(_name, _mode, _show, _store, _info) \ __GBE_SW_ATTR_FULL(_name, _mode, _show, _store, _info, \ (ARRAY_SIZE(_info)), NULL) #define __GBE_SW_CTXT_ATTR(_name, _mode, _show, _store, _info, _ctxt) \ __GBE_SW_ATTR_FULL(_name, _mode, _show, _store, _info, \ (ARRAY_SIZE(_info)), _ctxt) #define BITS(x) (BIT(x) - 1) #define BITMASK(n, s) (BITS(n) << (s)) enum gbe_sysfs_sw_entry { GBE_SYSFS_SW_CONTROL, GBE_SYSFS_SW_TX_PRIO, GBE_SYSFS_SW_VLAN, GBE_SYSFS_SW_STATS, GBE_SYSFS_SW_MAX }; struct gbe_sw_mod_info { const char *name; int shift; int bits; }; struct gbe_sw_parse_result { int control; int port; u32 value; }; struct gbe_attribute { struct attribute attr; ssize_t (*show)(struct gbe_priv *gbe_dev, struct gbe_attribute *attr, char *buf); ssize_t (*store)(struct gbe_priv *gbe_dev, struct gbe_attribute *attr, const char *, size_t); const struct gbe_sw_mod_info *info; ssize_t info_size; void *context; }; #define to_gbe_attr(_attr) container_of(_attr, struct gbe_attribute, attr) static struct gbe_slave *gbe_port_num_get_slave(struct gbe_priv *gbe_dev, int port) { struct gbe_intf *gbe_intf; for_each_intf(gbe_intf, gbe_dev) { if (gbe_intf->slave->port_num == port) return gbe_intf->slave; } return NULL; } static ssize_t gbe_sw_version_show(struct gbe_priv *gbe_dev, struct gbe_attribute *attr, char *buf) { u32 reg; reg = readl(GBE_REG_ADDR(gbe_dev, switch_regs, id_ver)); return snprintf(buf, PAGE_SIZE, "\nGBE Switch Version %d.%d (%d) Identification value 0x%x\n", GBE_MAJOR_VERSION(reg), GBE_MINOR_VERSION(reg), GBE_RTL_VERSION(reg), GBE_IDENT(reg)); } static struct gbe_attribute gbe_sw_version_attribute = __ATTR(version, S_IRUGO, gbe_sw_version_show, NULL); static const struct gbe_sw_mod_info gbe_sw_ver14_controls[] = { { .name = "fifo_loopback", .shift = 0, .bits = 1, }, { .name = "vlan_aware", .shift = 1, .bits = 1, }, { .name = "p0_enable", .shift = 2, .bits = 1, }, { .name = "p0_pass_pri_tagged", .shift = 3, .bits = 1, }, { .name = "p1_pass_pri_tagged", .shift = 4, .bits = 1, }, { .name = "p2_pass_pri_tagged", .shift = 5, .bits = 1, }, { .name = "p3_pass_pri_tagged", .shift = 7, .bits = 1, }, { .name = "p4_pass_pri_tagged", .shift = 8, .bits = 1, }, }; static const struct gbe_sw_mod_info gbe_sw_xge_controls[] = { { .name = "fifo_loopback", .shift = 0, .bits = 1, }, { .name = "vlan_aware", .shift = 1, .bits = 1, }, { .name = "p0_enable", .shift = 2, .bits = 1, }, { .name = "p0_pass_pri_tagged", .shift = 3, .bits = 1, }, { .name = "p1_pass_pri_tagged", .shift = 4, .bits = 1, }, { .name = "p2_pass_pri_tagged", .shift = 5, .bits = 1, }, { .name = "p0_tx_crc_type", .shift = 12, .bits = 1, }, }; static const struct gbe_sw_mod_info gbe_sw_nu_controls[] = { { .name = "vlan_aware", .shift = 1, .bits = 1, }, { .name = "p0_enable", .shift = 2, .bits = 1, }, { .name = "p0_pass_pri_tagged", .shift = 3, .bits = 1, }, { .name = "p1_pass_pri_tagged", .shift = 4, .bits = 1, }, { .name = "p2_pass_pri_tagged", .shift = 5, .bits = 1, }, { .name = "p3_pass_pri_tagged", .shift = 6, .bits = 1, }, { .name = "p4_pass_pri_tagged", .shift = 7, .bits = 1, }, { .name = "p5_pass_pri_tagged", .shift = 8, .bits = 1, }, { .name = "p6_pass_pri_tagged", .shift = 9, .bits = 1, }, { .name = "p7_pass_pri_tagged", .shift = 10, .bits = 1, }, { .name = "p8_pass_pri_tagged", .shift = 11, .bits = 1, }, { .name = "p0_tx_crc_type", .shift = 12, .bits = 1, }, { .name = "p0_rx_pad", .shift = 14, .bits = 1, }, { .name = "p0_rx_pass_crc_err", .shift = 15, .bits = 1, }, }; static inline void gbe_sw_info_set_reg_field(void __iomem *reg, const struct gbe_sw_mod_info *info, int val) { u32 rv; rv = readl(reg); rv = ((rv & ~BITMASK(info->bits, info->shift)) | (val << info->shift)); writel(rv, reg); } static ssize_t gbe_sw_attr_parse_set_command(struct gbe_priv *gbe_dev, struct gbe_attribute *attr, const char *buf, size_t count, struct gbe_sw_parse_result *res) { char ctrl_str[33], tmp_str[9]; int port = -1, value, len, control; unsigned long end; const struct gbe_sw_mod_info *info = attr->info; len = strcspn(buf, ".="); if (len >= 32) return -ENOMEM; strncpy(ctrl_str, buf, len); ctrl_str[len] = '\0'; buf += len; if (*buf == '.') { ++buf; len = strcspn(buf, "="); if (len >= 8) return -ENOMEM; strncpy(tmp_str, buf, len); tmp_str[len] = '\0'; if (kstrtoul(tmp_str, 0, &end)) return -EINVAL; port = (int)end; buf += len; } if (*buf != '=') return -EINVAL; if (kstrtoul(buf + 1, 0, &end)) return -EINVAL; value = (int)end; for (control = 0; control < attr->info_size; control++) if (strcmp(ctrl_str, info[control].name) == 0) break; if (control >= attr->info_size) return -ENOENT; res->control = control; res->port = port; res->value = value; dev_info(gbe_dev->dev, "parsed command %s.%d=%d\n", attr->info[control].name, port, value); return 0; } static ssize_t gbe_sw_attr_info_show(const struct gbe_sw_mod_info *info, int info_size, u32 reg_val, char *buf) { int i, len = 0; for (i = 0; i < info_size; i++, info++) { len += snprintf(buf + len, PAGE_SIZE - len, "%s=%d\n", info->name, (int)gbe_sw_mod_info_field_val(reg_val, info)); } return len; } static ssize_t gbe_sw_control_show(struct gbe_priv *gbe_dev, struct gbe_attribute *attr, char *buf) { u32 reg_val = readl(GBE_REG_ADDR(gbe_dev, switch_regs, control)); return gbe_sw_attr_info_show(attr->info, attr->info_size, reg_val, buf); } static ssize_t gbe_sw_control_store(struct gbe_priv *gbe_dev, struct gbe_attribute *attr, const char *buf, size_t count) { const struct gbe_sw_mod_info *info; struct gbe_sw_parse_result res; int ret; ret = gbe_sw_attr_parse_set_command(gbe_dev, attr, buf, count, &res); if (ret) return ret; info = &attr->info[res.control]; gbe_sw_info_set_reg_field(GBE_REG_ADDR(gbe_dev, switch_regs, control), info, res.value); return count; } static struct gbe_attribute gbe_sw_ver14_control_attribute = __GBE_SW_ATTR(control, S_IRUGO | S_IWUSR, gbe_sw_control_show, gbe_sw_control_store, gbe_sw_ver14_controls); static struct gbe_attribute gbe_sw_xge_control_attribute = __GBE_SW_ATTR(control, S_IRUGO | S_IWUSR, gbe_sw_control_show, gbe_sw_control_store, gbe_sw_xge_controls); static struct gbe_attribute gbe_sw_nu_control_attribute = __GBE_SW_ATTR(control, S_IRUGO | S_IWUSR, gbe_sw_control_show, gbe_sw_control_store, gbe_sw_nu_controls); static const struct gbe_sw_mod_info gbe_sw_ver14_ptypes[] = { { .name = "escalate_pri_load_val", .shift = 0, .bits = 5, }, { .name = "port0_pri_type_escalate", .shift = 8, .bits = 1, }, { .name = "port1_pri_type_escalate", .shift = 9, .bits = 1, }, { .name = "port2_pri_type_escalate", .shift = 10, .bits = 1, }, { .name = "port3_pri_type_escalate", .shift = 11, .bits = 1, }, { .name = "port4_pri_type_escalate", .shift = 12, .bits = 1, }, }; static const struct gbe_sw_mod_info gbe_sw_xge_ptypes[] = { { .name = "escalate_pri_load_val", .shift = 0, .bits = 5, }, { .name = "port0_pri_type_escalate", .shift = 8, .bits = 1, }, { .name = "port1_pri_type_escalate", .shift = 9, .bits = 1, }, { .name = "port2_pri_type_escalate", .shift = 10, .bits = 1, }, }; static const struct gbe_sw_mod_info gbe_sw_nu_ptypes[] = { { .name = "escalate_pri_load_val", .shift = 0, .bits = 5, }, { .name = "port0_pri_type_escalate", .shift = 8, .bits = 1, }, { .name = "port1_pri_type_escalate", .shift = 9, .bits = 1, }, { .name = "port2_pri_type_escalate", .shift = 10, .bits = 1, }, { .name = "port3_pri_type_escalate", .shift = 11, .bits = 1, }, { .name = "port4_pri_type_escalate", .shift = 12, .bits = 1, }, { .name = "port5_pri_type_escalate", .shift = 13, .bits = 1, }, { .name = "port6_pri_type_escalate", .shift = 14, .bits = 1, }, { .name = "port7_pri_type_escalate", .shift = 15, .bits = 1, }, { .name = "port8_pri_type_escalate", .shift = 16, .bits = 1, }, }; static ssize_t gbe_sw_pri_type_show(struct gbe_priv *gbe_dev, struct gbe_attribute *attr, char *buf) { u32 reg_val = readl(GBE_REG_ADDR(gbe_dev, switch_regs, ptype)); return gbe_sw_attr_info_show(attr->info, attr->info_size, reg_val, buf); } static ssize_t gbe_sw_pri_type_store(struct gbe_priv *gbe_dev, struct gbe_attribute *attr, const char *buf, size_t count) { const struct gbe_sw_mod_info *info; struct gbe_sw_parse_result res; int ret; ret = gbe_sw_attr_parse_set_command(gbe_dev, attr, buf, count, &res); if (ret) return ret; info = &attr->info[res.control]; gbe_sw_info_set_reg_field(GBE_REG_ADDR(gbe_dev, switch_regs, ptype), info, res.value); return count; } static struct gbe_attribute gbe_sw_ver14_pri_type_attribute = __GBE_SW_ATTR(priority_type, S_IRUGO | S_IWUSR, gbe_sw_pri_type_show, gbe_sw_pri_type_store, gbe_sw_ver14_ptypes); static struct gbe_attribute gbe_sw_xge_pri_type_attribute = __GBE_SW_ATTR(priority_type, S_IRUGO | S_IWUSR, gbe_sw_pri_type_show, gbe_sw_pri_type_store, gbe_sw_xge_ptypes); static struct gbe_attribute gbe_sw_nu_pri_type_attribute = __GBE_SW_ATTR(priority_type, S_IRUGO | S_IWUSR, gbe_sw_pri_type_show, gbe_sw_pri_type_store, gbe_sw_nu_ptypes); static const struct gbe_sw_mod_info gbe_sw_ver14_flow_controls[] = { { .name = "port0_flow_control_en", .shift = 0, .bits = 1, }, { .name = "port1_flow_control_en", .shift = 1, .bits = 1, }, { .name = "port2_flow_control_en", .shift = 2, .bits = 1, }, { .name = "port3_flow_control_en", .shift = 3, .bits = 1, }, { .name = "port4_flow_control_en", .shift = 4, .bits = 1, }, }; static const struct gbe_sw_mod_info gbe_sw_xge_flow_controls[] = { { .name = "port0_flow_control_en", .shift = 0, .bits = 1, }, { .name = "port1_flow_control_en", .shift = 1, .bits = 1, }, { .name = "port2_flow_control_en", .shift = 2, .bits = 1, }, }; static ssize_t gbe_sw_flow_control_show(struct gbe_priv *gbe_dev, struct gbe_attribute *attr, char *buf) { u32 reg_val = readl(GBE_REG_ADDR(gbe_dev, switch_regs, flow_control)); return gbe_sw_attr_info_show(attr->info, attr->info_size, reg_val, buf); } static ssize_t gbe_sw_flow_control_store(struct gbe_priv *gbe_dev, struct gbe_attribute *attr, const char *buf, size_t count) { const struct gbe_sw_mod_info *info; struct gbe_sw_parse_result res; int ret; ret = gbe_sw_attr_parse_set_command(gbe_dev, attr, buf, count, &res); if (ret) return ret; info = &attr->info[res.control]; gbe_sw_info_set_reg_field(GBE_REG_ADDR(gbe_dev, switch_regs, flow_control), info, res.value); return count; } static struct gbe_attribute gbe_sw_ver14_flow_control_attribute = __GBE_SW_ATTR(flow_control, S_IRUGO | S_IWUSR, gbe_sw_flow_control_show, gbe_sw_flow_control_store, gbe_sw_ver14_flow_controls); static struct gbe_attribute gbe_sw_xge_flow_control_attribute = __GBE_SW_ATTR(flow_control, S_IRUGO | S_IWUSR, gbe_sw_flow_control_show, gbe_sw_flow_control_store, gbe_sw_xge_flow_controls); static struct attribute *gbe_sw_ver14_default_attrs[] = { &gbe_sw_version_attribute.attr, &gbe_sw_ver14_control_attribute.attr, &gbe_sw_ver14_pri_type_attribute.attr, &gbe_sw_ver14_flow_control_attribute.attr, NULL }; static struct attribute *gbe_sw_xge_default_attrs[] = { &gbe_sw_version_attribute.attr, &gbe_sw_xge_control_attribute.attr, &gbe_sw_xge_pri_type_attribute.attr, &gbe_sw_xge_flow_control_attribute.attr, NULL }; static struct attribute *gbe_sw_nu_default_attrs[] = { &gbe_sw_version_attribute.attr, &gbe_sw_nu_control_attribute.attr, &gbe_sw_nu_pri_type_attribute.attr, NULL }; static const struct gbe_sw_mod_info gbe_sw_port_tx_pri_maps[] = { { .name = "port_tx_pri_0", .shift = 0, .bits = 3, }, { .name = "port_tx_pri_1", .shift = 4, .bits = 3, }, { .name = "port_tx_pri_2", .shift = 8, .bits = 3, }, { .name = "port_tx_pri_3", .shift = 12, .bits = 3, }, { .name = "port_tx_pri_4", .shift = 16, .bits = 3, }, { .name = "port_tx_pri_5", .shift = 20, .bits = 3, }, { .name = "port_tx_pri_6", .shift = 24, .bits = 3, }, { .name = "port_tx_pri_7", .shift = 28, .bits = 3, }, }; static ssize_t gbe_sw_port_tx_pri_map_show(struct gbe_priv *gbe_dev, struct gbe_attribute *attr, char *buf) { int len = 0, total_len = 0, port; struct gbe_slave *slave; u32 reg_val; port = (int)(attr->context); slave = gbe_port_num_get_slave(gbe_dev, port); if (!slave) return 0; reg_val = readl(GBE_REG_ADDR(slave, port_regs, tx_pri_map)); len = gbe_sw_attr_info_show(attr->info, attr->info_size, reg_val, buf); return (total_len += len); } static ssize_t gbe_sw_port_tx_pri_map_store(struct gbe_priv *gbe_dev, struct gbe_attribute *attr, const char *buf, size_t count) { const struct gbe_sw_mod_info *info; struct gbe_sw_parse_result res; void __iomem *reg = NULL; struct gbe_slave *slave; int ret, port; port = (int)(attr->context); slave = gbe_port_num_get_slave(gbe_dev, port); if (!slave) return -EINVAL; ret = gbe_sw_attr_parse_set_command(gbe_dev, attr, buf, count, &res); if (ret) return ret; info = &attr->info[res.control]; reg = GBE_REG_ADDR(slave, port_regs, tx_pri_map); if (!reg) return -ENOENT; gbe_sw_info_set_reg_field(reg, info, res.value); return count; } static struct gbe_attribute gbe_sw_tx_pri_0_attribute = __GBE_SW_CTXT_ATTR(0, S_IRUGO | S_IWUSR, gbe_sw_port_tx_pri_map_show, gbe_sw_port_tx_pri_map_store, gbe_sw_port_tx_pri_maps, (void *)0); static struct gbe_attribute gbe_sw_tx_pri_1_attribute = __GBE_SW_CTXT_ATTR(1, S_IRUGO | S_IWUSR, gbe_sw_port_tx_pri_map_show, gbe_sw_port_tx_pri_map_store, gbe_sw_port_tx_pri_maps, (void *)1); static struct gbe_attribute gbe_sw_tx_pri_2_attribute = __GBE_SW_CTXT_ATTR(2, S_IRUGO | S_IWUSR, gbe_sw_port_tx_pri_map_show, gbe_sw_port_tx_pri_map_store, gbe_sw_port_tx_pri_maps, (void *)2); static struct gbe_attribute gbe_sw_tx_pri_3_attribute = __GBE_SW_CTXT_ATTR(3, S_IRUGO | S_IWUSR, gbe_sw_port_tx_pri_map_show, gbe_sw_port_tx_pri_map_store, gbe_sw_port_tx_pri_maps, (void *)3); static struct gbe_attribute gbe_sw_tx_pri_4_attribute = __GBE_SW_CTXT_ATTR(4, S_IRUGO | S_IWUSR, gbe_sw_port_tx_pri_map_show, gbe_sw_port_tx_pri_map_store, gbe_sw_port_tx_pri_maps, (void *)4); static struct gbe_attribute gbe_sw_tx_pri_5_attribute = __GBE_SW_CTXT_ATTR(5, S_IRUGO | S_IWUSR, gbe_sw_port_tx_pri_map_show, gbe_sw_port_tx_pri_map_store, gbe_sw_port_tx_pri_maps, (void *)5); static struct gbe_attribute gbe_sw_tx_pri_6_attribute = __GBE_SW_CTXT_ATTR(6, S_IRUGO | S_IWUSR, gbe_sw_port_tx_pri_map_show, gbe_sw_port_tx_pri_map_store, gbe_sw_port_tx_pri_maps, (void *)6); static struct gbe_attribute gbe_sw_tx_pri_7_attribute = __GBE_SW_CTXT_ATTR(7, S_IRUGO | S_IWUSR, gbe_sw_port_tx_pri_map_show, gbe_sw_port_tx_pri_map_store, gbe_sw_port_tx_pri_maps, (void *)7); static struct gbe_attribute gbe_sw_tx_pri_8_attribute = __GBE_SW_CTXT_ATTR(8, S_IRUGO | S_IWUSR, gbe_sw_port_tx_pri_map_show, gbe_sw_port_tx_pri_map_store, gbe_sw_port_tx_pri_maps, (void *)8); static struct attribute *gbe_sw_ver14_tx_pri_default_attrs[] = { &gbe_sw_tx_pri_1_attribute.attr, &gbe_sw_tx_pri_2_attribute.attr, &gbe_sw_tx_pri_3_attribute.attr, &gbe_sw_tx_pri_4_attribute.attr, NULL }; static struct attribute *gbe_sw_xge_tx_pri_default_attrs[] = { &gbe_sw_tx_pri_0_attribute.attr, &gbe_sw_tx_pri_1_attribute.attr, &gbe_sw_tx_pri_2_attribute.attr, NULL }; static struct attribute *gbe_sw_nu_tx_pri_default_attrs[] = { &gbe_sw_tx_pri_1_attribute.attr, &gbe_sw_tx_pri_2_attribute.attr, &gbe_sw_tx_pri_3_attribute.attr, &gbe_sw_tx_pri_4_attribute.attr, &gbe_sw_tx_pri_5_attribute.attr, &gbe_sw_tx_pri_6_attribute.attr, &gbe_sw_tx_pri_7_attribute.attr, &gbe_sw_tx_pri_8_attribute.attr, NULL }; static ssize_t gbe_sw_tx_pri_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct gbe_attribute *attribute = to_gbe_attr(attr); struct gbe_priv *gbe_dev = tx_pri_to_gbe_dev(kobj); if (!attribute->show) return -EIO; return attribute->show(gbe_dev, attribute, buf); } static ssize_t gbe_sw_tx_pri_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct gbe_attribute *attribute = to_gbe_attr(attr); struct gbe_priv *gbe_dev = tx_pri_to_gbe_dev(kobj); if (!attribute->store) return -EIO; return attribute->store(gbe_dev, attribute, buf, count); } static const struct sysfs_ops gbe_sw_tx_pri_sysfs_ops = { .show = gbe_sw_tx_pri_attr_show, .store = gbe_sw_tx_pri_attr_store, }; static struct kobj_type gbe_sw_ver14_tx_pri_ktype = { .sysfs_ops = &gbe_sw_tx_pri_sysfs_ops, .default_attrs = gbe_sw_ver14_tx_pri_default_attrs, }; static struct kobj_type gbe_sw_xge_tx_pri_ktype = { .sysfs_ops = &gbe_sw_tx_pri_sysfs_ops, .default_attrs = gbe_sw_xge_tx_pri_default_attrs, }; static struct kobj_type gbe_sw_nu_tx_pri_ktype = { .sysfs_ops = &gbe_sw_tx_pri_sysfs_ops, .default_attrs = gbe_sw_nu_tx_pri_default_attrs, }; static const struct gbe_sw_mod_info gbe_sw_port_vlans[] = { { .name = "port_vlan_id", .shift = 0, .bits = 12, }, { .name = "port_cfi", .shift = 12, .bits = 1, }, { .name = "port_vlan_pri", .shift = 13, .bits = 3, }, }; static ssize_t gbe_sw_port_vlan_show(struct gbe_priv *gbe_dev, struct gbe_attribute *attr, char *buf) { int len = 0, total_len = 0, port; struct gbe_slave *slave; u32 reg_val; port = (int)(attr->context); if (port == gbe_dev->host_port) { /* Host port */ reg_val = readl(GBE_REG_ADDR(gbe_dev, host_port_regs, port_vlan)); len = gbe_sw_attr_info_show(attr->info, attr->info_size, reg_val, buf); return len; } slave = gbe_port_num_get_slave(gbe_dev, port); if (!slave) return 0; reg_val = readl(GBE_REG_ADDR(slave, port_regs, port_vlan)); len = gbe_sw_attr_info_show(attr->info, attr->info_size, reg_val, buf); return (total_len += len); } static ssize_t gbe_sw_port_vlan_store(struct gbe_priv *gbe_dev, struct gbe_attribute *attr, const char *buf, size_t count) { const struct gbe_sw_mod_info *info; struct gbe_sw_parse_result res; struct gbe_slave *slave; void __iomem *reg = NULL; int ret, port; port = (int)(attr->context); ret = gbe_sw_attr_parse_set_command(gbe_dev, attr, buf, count, &res); if (ret) return ret; info = &attr->info[res.control]; /* Host port */ if (port == gbe_dev->host_port) { reg = GBE_REG_ADDR(gbe_dev, host_port_regs, port_vlan); goto set; } slave = gbe_port_num_get_slave(gbe_dev, port); if (!slave) return -EINVAL; /* Slave port */ reg = GBE_REG_ADDR(slave, port_regs, port_vlan); if (!reg) return -ENOENT; set: gbe_sw_info_set_reg_field(reg, info, res.value); return count; } static struct gbe_attribute gbe_sw_pvlan_0_attribute = __GBE_SW_CTXT_ATTR(0, S_IRUGO | S_IWUSR, gbe_sw_port_vlan_show, gbe_sw_port_vlan_store, gbe_sw_port_vlans, (void *)0); static struct gbe_attribute gbe_sw_pvlan_1_attribute = __GBE_SW_CTXT_ATTR(1, S_IRUGO | S_IWUSR, gbe_sw_port_vlan_show, gbe_sw_port_vlan_store, gbe_sw_port_vlans, (void *)1); static struct gbe_attribute gbe_sw_pvlan_2_attribute = __GBE_SW_CTXT_ATTR(2, S_IRUGO | S_IWUSR, gbe_sw_port_vlan_show, gbe_sw_port_vlan_store, gbe_sw_port_vlans, (void *)2); static struct gbe_attribute gbe_sw_pvlan_3_attribute = __GBE_SW_CTXT_ATTR(3, S_IRUGO | S_IWUSR, gbe_sw_port_vlan_show, gbe_sw_port_vlan_store, gbe_sw_port_vlans, (void *)3); static struct gbe_attribute gbe_sw_pvlan_4_attribute = __GBE_SW_CTXT_ATTR(4, S_IRUGO | S_IWUSR, gbe_sw_port_vlan_show, gbe_sw_port_vlan_store, gbe_sw_port_vlans, (void *)4); static struct gbe_attribute gbe_sw_pvlan_5_attribute = __GBE_SW_CTXT_ATTR(5, S_IRUGO | S_IWUSR, gbe_sw_port_vlan_show, gbe_sw_port_vlan_store, gbe_sw_port_vlans, (void *)5); static struct gbe_attribute gbe_sw_pvlan_6_attribute = __GBE_SW_CTXT_ATTR(6, S_IRUGO | S_IWUSR, gbe_sw_port_vlan_show, gbe_sw_port_vlan_store, gbe_sw_port_vlans, (void *)6); static struct gbe_attribute gbe_sw_pvlan_7_attribute = __GBE_SW_CTXT_ATTR(7, S_IRUGO | S_IWUSR, gbe_sw_port_vlan_show, gbe_sw_port_vlan_store, gbe_sw_port_vlans, (void *)7); static struct gbe_attribute gbe_sw_pvlan_8_attribute = __GBE_SW_CTXT_ATTR(8, S_IRUGO | S_IWUSR, gbe_sw_port_vlan_show, gbe_sw_port_vlan_store, gbe_sw_port_vlans, (void *)8); static struct attribute *gbe_sw_ver14_pvlan_default_attrs[] = { &gbe_sw_pvlan_0_attribute.attr, &gbe_sw_pvlan_1_attribute.attr, &gbe_sw_pvlan_2_attribute.attr, &gbe_sw_pvlan_3_attribute.attr, &gbe_sw_pvlan_4_attribute.attr, NULL }; static struct attribute *gbe_sw_xge_pvlan_default_attrs[] = { &gbe_sw_pvlan_0_attribute.attr, &gbe_sw_pvlan_1_attribute.attr, &gbe_sw_pvlan_2_attribute.attr, NULL }; static struct attribute *gbe_sw_nu_pvlan_default_attrs[] = { &gbe_sw_pvlan_0_attribute.attr, &gbe_sw_pvlan_1_attribute.attr, &gbe_sw_pvlan_2_attribute.attr, &gbe_sw_pvlan_3_attribute.attr, &gbe_sw_pvlan_4_attribute.attr, &gbe_sw_pvlan_5_attribute.attr, &gbe_sw_pvlan_6_attribute.attr, &gbe_sw_pvlan_7_attribute.attr, &gbe_sw_pvlan_8_attribute.attr, NULL }; static ssize_t gbe_sw_pvlan_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct gbe_attribute *attribute = to_gbe_attr(attr); struct gbe_priv *gbe_dev = pvlan_to_gbe_dev(kobj); if (!attribute->show) return -EIO; return attribute->show(gbe_dev, attribute, buf); } static ssize_t gbe_sw_pvlan_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct gbe_attribute *attribute = to_gbe_attr(attr); struct gbe_priv *gbe_dev = pvlan_to_gbe_dev(kobj); if (!attribute->store) return -EIO; return attribute->store(gbe_dev, attribute, buf, count); } static const struct sysfs_ops gbe_sw_pvlan_sysfs_ops = { .show = gbe_sw_pvlan_attr_show, .store = gbe_sw_pvlan_attr_store, }; static struct kobj_type gbe_sw_ver14_pvlan_ktype = { .sysfs_ops = &gbe_sw_pvlan_sysfs_ops, .default_attrs = gbe_sw_ver14_pvlan_default_attrs, }; static struct kobj_type gbe_sw_xge_pvlan_ktype = { .sysfs_ops = &gbe_sw_pvlan_sysfs_ops, .default_attrs = gbe_sw_xge_pvlan_default_attrs, }; static struct kobj_type gbe_sw_nu_pvlan_ktype = { .sysfs_ops = &gbe_sw_pvlan_sysfs_ops, .default_attrs = gbe_sw_nu_pvlan_default_attrs, }; static ssize_t gbe_sw_stats_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct gbe_attribute *attribute = to_gbe_attr(attr); struct gbe_priv *gbe_dev = stats_to_gbe_dev(kobj); if (!attribute->store) return -EIO; return attribute->store(gbe_dev, attribute, buf, count); } static const struct sysfs_ops gbe_sw_stats_sysfs_ops = { .store = gbe_sw_stats_attr_store, }; static ssize_t gbe_sw_stats_mod_store(struct gbe_priv *gbe_dev, struct gbe_attribute *attr, const char *buf, size_t count) { int stat_mod, max_ports; unsigned long end; if (kstrtoul(buf, 0, &end) != 0 || (end != 0)) return -EINVAL; stat_mod = (int)(attr->context); /* We have stats blocks for only slave ports on GBE_SS_VERSION_14 * but also for host port for other variations. So check this * value accordingly */ max_ports = (gbe_dev->ss_version == GBE_SS_VERSION_14) ? gbe_dev->max_num_slaves : gbe_dev->max_num_ports; if (stat_mod >= max_ports) return -EINVAL; spin_lock_bh(&gbe_dev->hw_stats_lock); if (gbe_dev->ss_version == GBE_SS_VERSION_14) gbe_reset_mod_stats_ver14(gbe_dev, stat_mod); else gbe_reset_mod_stats(gbe_dev, stat_mod); spin_unlock_bh(&gbe_dev->hw_stats_lock); return count; } static struct gbe_attribute gbe_sw_stats_a_attribute = __GBE_SW_ATTR_FULL(A, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)GBE_STATSA_MODULE); static struct gbe_attribute gbe_sw_stats_b_attribute = __GBE_SW_ATTR_FULL(B, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)GBE_STATSB_MODULE); static struct gbe_attribute gbe_sw_stats_c_attribute = __GBE_SW_ATTR_FULL(C, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)GBE_STATSC_MODULE); static struct gbe_attribute gbe_sw_stats_d_attribute = __GBE_SW_ATTR_FULL(D, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)GBE_STATSD_MODULE); static struct attribute *gbe_sw_ver14_stats_default_attrs[] = { &gbe_sw_stats_a_attribute.attr, &gbe_sw_stats_b_attribute.attr, &gbe_sw_stats_c_attribute.attr, &gbe_sw_stats_d_attribute.attr, NULL }; static struct kobj_type gbe_sw_ver14_stats_ktype = { .sysfs_ops = &gbe_sw_stats_sysfs_ops, .default_attrs = gbe_sw_ver14_stats_default_attrs, }; static struct gbe_attribute gbe_sw_xge_stats_0_attribute = __GBE_SW_ATTR_FULL(0, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)XGBE_STATS0_MODULE); static struct gbe_attribute gbe_sw_xge_stats_1_attribute = __GBE_SW_ATTR_FULL(1, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)XGBE_STATS1_MODULE); static struct gbe_attribute gbe_sw_xge_stats_2_attribute = __GBE_SW_ATTR_FULL(2, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)XGBE_STATS2_MODULE); static struct attribute *gbe_sw_xge_stats_default_attrs[] = { &gbe_sw_xge_stats_0_attribute.attr, &gbe_sw_xge_stats_1_attribute.attr, &gbe_sw_xge_stats_2_attribute.attr, NULL }; static struct kobj_type gbe_sw_xge_stats_ktype = { .sysfs_ops = &gbe_sw_stats_sysfs_ops, .default_attrs = gbe_sw_xge_stats_default_attrs, }; static struct gbe_attribute gbe_sw_stats_0_attribute = __GBE_SW_ATTR_FULL(0, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)GBENU_STATS0_MODULE); static struct gbe_attribute gbe_sw_stats_1_attribute = __GBE_SW_ATTR_FULL(1, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)GBENU_STATS1_MODULE); static struct gbe_attribute gbe_sw_stats_2_attribute = __GBE_SW_ATTR_FULL(2, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)GBENU_STATS2_MODULE); static struct gbe_attribute gbe_sw_stats_3_attribute = __GBE_SW_ATTR_FULL(3, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)GBENU_STATS3_MODULE); static struct gbe_attribute gbe_sw_stats_4_attribute = __GBE_SW_ATTR_FULL(4, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)GBENU_STATS4_MODULE); static struct gbe_attribute gbe_sw_stats_5_attribute = __GBE_SW_ATTR_FULL(5, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)GBENU_STATS5_MODULE); static struct gbe_attribute gbe_sw_stats_6_attribute = __GBE_SW_ATTR_FULL(6, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)GBENU_STATS6_MODULE); static struct gbe_attribute gbe_sw_stats_7_attribute = __GBE_SW_ATTR_FULL(7, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)GBENU_STATS7_MODULE); static struct gbe_attribute gbe_sw_stats_8_attribute = __GBE_SW_ATTR_FULL(8, S_IWUSR, NULL, gbe_sw_stats_mod_store, NULL, 0, (void *)GBENU_STATS8_MODULE); static struct attribute *gbe_sw_nu_stats_default_attrs[] = { &gbe_sw_stats_0_attribute.attr, &gbe_sw_stats_1_attribute.attr, &gbe_sw_stats_2_attribute.attr, &gbe_sw_stats_3_attribute.attr, &gbe_sw_stats_4_attribute.attr, &gbe_sw_stats_5_attribute.attr, &gbe_sw_stats_6_attribute.attr, &gbe_sw_stats_7_attribute.attr, &gbe_sw_stats_8_attribute.attr, NULL }; static struct kobj_type gbe_sw_nu_stats_ktype = { .sysfs_ops = &gbe_sw_stats_sysfs_ops, .default_attrs = gbe_sw_nu_stats_default_attrs, }; static ssize_t gbe_sw_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct gbe_attribute *attribute = to_gbe_attr(attr); struct gbe_priv *gbe_dev = to_gbe_dev(kobj); if (!attribute->show) return -EIO; return attribute->show(gbe_dev, attribute, buf); } static ssize_t gbe_sw_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct gbe_attribute *attribute = to_gbe_attr(attr); struct gbe_priv *gbe_dev = to_gbe_dev(kobj); if (!attribute->store) return -EIO; return attribute->store(gbe_dev, attribute, buf, count); } static const struct sysfs_ops gbe_sw_sysfs_ops = { .show = gbe_sw_attr_show, .store = gbe_sw_attr_store, }; static struct kobj_type gbe_sw_ver14_ktype = { .sysfs_ops = &gbe_sw_sysfs_ops, .default_attrs = gbe_sw_ver14_default_attrs, }; static struct kobj_type gbe_sw_xge_ktype = { .sysfs_ops = &gbe_sw_sysfs_ops, .default_attrs = gbe_sw_xge_default_attrs, }; static struct kobj_type gbe_sw_nu_ktype = { .sysfs_ops = &gbe_sw_sysfs_ops, .default_attrs = gbe_sw_nu_default_attrs, }; /* for ver14 switch */ static struct kobj_type *gbe_sw_ver14_kobjs[GBE_SYSFS_SW_MAX] = { &gbe_sw_ver14_ktype, &gbe_sw_ver14_tx_pri_ktype, &gbe_sw_ver14_pvlan_ktype, &gbe_sw_ver14_stats_ktype, }; /* for xge switch */ static struct kobj_type *gbe_sw_xge_kobjs[GBE_SYSFS_SW_MAX] = { &gbe_sw_xge_ktype, &gbe_sw_xge_tx_pri_ktype, &gbe_sw_xge_pvlan_ktype, &gbe_sw_xge_stats_ktype, }; /* for NU switch */ static struct kobj_type *gbe_sw_nu_kobjs[GBE_SYSFS_SW_MAX] = { &gbe_sw_nu_ktype, &gbe_sw_nu_tx_pri_ktype, &gbe_sw_nu_pvlan_ktype, &gbe_sw_nu_stats_ktype, }; int gbe_create_sysfs_entries(struct gbe_priv *gbe_dev) { struct device *dev = gbe_dev->dev; static struct kobj_type **kobjs; int ret; switch (gbe_dev->ss_version) { case XGBE_SS_VERSION_10: kobjs = &gbe_sw_xge_kobjs[0]; break; case GBE_SS_VERSION_14: kobjs = &gbe_sw_ver14_kobjs[0]; break; default: kobjs = &gbe_sw_nu_kobjs[0]; } ret = kobject_init_and_add(&gbe_dev->kobj, kobjs[GBE_SYSFS_SW_CONTROL], &dev->kobj, "gbe_sw"); if (ret) { dev_err(dev, "failed to create gbe sw sysfs entry\n"); return ret; } ret = kobject_init_and_add(&gbe_dev->tx_pri_kobj, kobjs[GBE_SYSFS_SW_TX_PRIO], &gbe_dev->kobj, "port_tx_pri_map"); if (ret) { dev_err(dev, "failed to create sysfs port_tx_pri_map entry\n"); goto clean_tx_pri_kobj; } ret = kobject_init_and_add(&gbe_dev->pvlan_kobj, kobjs[GBE_SYSFS_SW_VLAN], &gbe_dev->kobj, "port_vlan"); if (ret) { dev_err(dev, "failed to create sysfs port_vlan entry\n"); goto clean_pvlan_kobj; } ret = kobject_init_and_add(&gbe_dev->stats_kobj, kobjs[GBE_SYSFS_SW_STATS], &gbe_dev->kobj, "stats"); if (ret) { dev_err(dev, "failed to create sysfs stats entry\n"); goto clean_stats_kobj; } return ret; clean_stats_kobj: kobject_put(&gbe_dev->pvlan_kobj); clean_pvlan_kobj: kobject_put(&gbe_dev->tx_pri_kobj); clean_tx_pri_kobj: kobject_put(&gbe_dev->kobj); return ret; } void gbe_remove_sysfs_entries(struct gbe_priv *gbe_dev) { kobject_put(&gbe_dev->stats_kobj); kobject_put(&gbe_dev->pvlan_kobj); kobject_put(&gbe_dev->tx_pri_kobj); kobject_put(&gbe_dev->kobj); }