return 0;
}
-int rtl8366_set_vlan(struct rtl8366_smi *smi, int vid, u32 member, u32 untag,
- u32 fid)
+static int rtl8366_set_vlan(struct rtl8366_smi *smi, int vid, u32 member,
+ u32 untag, u32 fid)
{
struct rtl8366_vlan_4k vlan4k;
int err;
return err;
}
-EXPORT_SYMBOL_GPL(rtl8366_set_vlan);
static int rtl8366_get_pvid(struct rtl8366_smi *smi, int port, int *val)
{
return -ENOSPC;
}
+int rtl8366_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+ int err;
+
+ err = smi->ops->enable_vlan(smi, enable);
+ if (err)
+ return err;
+
+ smi->vlan_enabled = enable;
+
+ if (!enable) {
+ smi->vlan4k_enabled = 0;
+ err = smi->ops->enable_vlan4k(smi, enable);
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtl8366_enable_vlan);
+
+static int rtl8366_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+ int err;
+
+ if (enable) {
+ err = smi->ops->enable_vlan(smi, enable);
+ if (err)
+ return err;
+
+ smi->vlan_enabled = enable;
+ }
+
+ err = smi->ops->enable_vlan4k(smi, enable);
+ if (err)
+ return err;
+
+ smi->vlan4k_enabled = enable;
+ return 0;
+}
+
+int rtl8366_enable_all_ports(struct rtl8366_smi *smi, int enable)
+{
+ int port;
+ int err;
+
+ for (port = 0; port < smi->num_ports; port++) {
+ err = smi->ops->enable_port(smi, port, enable);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_enable_all_ports);
+
int rtl8366_reset_vlan(struct rtl8366_smi *smi)
{
struct rtl8366_vlan_mc vlanmc;
int err;
int i;
+ rtl8366_enable_vlan(smi, 0);
+ rtl8366_enable_vlan4k(smi, 0);
+
/* clear VLAN member configurations */
vlanmc.vid = 0;
vlanmc.priority = 0;
return err;
}
- for (i = 0; i < smi->num_ports; i++) {
- if (i == smi->cpu_port)
- continue;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_reset_vlan);
+
+static int rtl8366_init_vlan(struct rtl8366_smi *smi)
+{
+ int port;
+ int err;
- err = rtl8366_set_vlan(smi, (i + 1),
- (1 << i) | (1 << smi->cpu_port),
- (1 << i) | (1 << smi->cpu_port),
- 0);
+ err = rtl8366_reset_vlan(smi);
+ if (err)
+ return err;
+
+ for (port = 0; port < smi->num_ports; port++) {
+ u32 mask;
+
+ if (port == smi->cpu_port)
+ mask = (1 << smi->num_ports) - 1;
+ else
+ mask = (1 << port) | (1 << smi->cpu_port);
+
+ err = rtl8366_set_vlan(smi, (port + 1), mask, mask, 0);
if (err)
return err;
- err = rtl8366_set_pvid(smi, i, (i + 1));
+ err = rtl8366_set_pvid(smi, port, (port + 1));
if (err)
return err;
}
- return 0;
+ return rtl8366_enable_vlan(smi, 1);
}
-EXPORT_SYMBOL_GPL(rtl8366_reset_vlan);
#ifdef CONFIG_RTL8366S_PHY_DEBUG_FS
int rtl8366_debugfs_open(struct inode *inode, struct file *file)
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
+#define RTL8366_VLAN4K_PAGE_SIZE 64
+#define RTL8366_VLAN4K_NUM_PAGES (4096 / RTL8366_VLAN4K_PAGE_SIZE)
+
+static ssize_t rtl8366_read_debugfs_vlan_4k(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+ int i, len = 0;
+ int offset;
+ char *buf = smi->buf;
+
+ if (smi->dbg_vlan_4k_page >= RTL8366_VLAN4K_NUM_PAGES) {
+ len += snprintf(buf + len, sizeof(smi->buf) - len,
+ "invalid page: %u\n", smi->dbg_vlan_4k_page);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ }
+
+ len += snprintf(buf + len, sizeof(smi->buf) - len,
+ "%4s %6s %6s %3s\n",
+ "vid", "member", "untag", "fid");
+
+ offset = RTL8366_VLAN4K_PAGE_SIZE * smi->dbg_vlan_4k_page;
+ for (i = 0; i < RTL8366_VLAN4K_PAGE_SIZE; i++) {
+ struct rtl8366_vlan_4k vlan4k;
+
+ smi->ops->get_vlan_4k(smi, offset + i, &vlan4k);
+
+ len += snprintf(buf + len, sizeof(smi->buf) - len,
+ "%4d 0x%04x 0x%04x %3d\n",
+ vlan4k.vid, vlan4k.member,
+ vlan4k.untag, vlan4k.fid);
+ }
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t rtl8366_read_debugfs_pvid(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+ char *buf = smi->buf;
+ int len = 0;
+ int i;
+
+ len += snprintf(buf + len, sizeof(smi->buf) - len, "%4s %4s\n",
+ "port", "pvid");
+
+ for (i = 0; i < smi->num_ports; i++) {
+ int pvid;
+ int err;
+
+ err = rtl8366_get_pvid(smi, i, &pvid);
+ if (err)
+ len += snprintf(buf + len, sizeof(smi->buf) - len,
+ "%4d error\n", i);
+ else
+ len += snprintf(buf + len, sizeof(smi->buf) - len,
+ "%4d %4d\n", i, pvid);
+ }
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
static ssize_t rtl8366_read_debugfs_reg(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
.owner = THIS_MODULE
};
+static const struct file_operations fops_rtl8366_vlan_4k = {
+ .read = rtl8366_read_debugfs_vlan_4k,
+ .open = rtl8366_debugfs_open,
+ .owner = THIS_MODULE
+};
+
+static const struct file_operations fops_rtl8366_pvid = {
+ .read = rtl8366_read_debugfs_pvid,
+ .open = rtl8366_debugfs_open,
+ .owner = THIS_MODULE
+};
+
static const struct file_operations fops_rtl8366_mibs = {
.read = rtl8366_read_debugfs_mibs,
.open = rtl8366_debugfs_open,
return;
}
+ node = debugfs_create_u8("vlan_4k_page", S_IRUGO | S_IWUSR, root,
+ &smi->dbg_vlan_4k_page);
+ if (!node) {
+ dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+ "vlan_4k_page");
+ return;
+ }
+
+ node = debugfs_create_file("vlan_4k", S_IRUSR, root, smi,
+ &fops_rtl8366_vlan_4k);
+ if (!node) {
+ dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+ "vlan_4k");
+ return;
+ }
+
+ node = debugfs_create_file("pvid", S_IRUSR, root, smi,
+ &fops_rtl8366_pvid);
+ if (!node) {
+ dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+ "pvid");
+ return;
+ }
+
node = debugfs_create_file("mibs", S_IRUSR, smi->debugfs_root, smi,
&fops_rtl8366_mibs);
if (!node)
}
EXPORT_SYMBOL_GPL(rtl8366_sw_set_port_pvid);
+int rtl8366_sw_get_port_mib(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+ int i, len = 0;
+ unsigned long long counter = 0;
+ char *buf = smi->buf;
+
+ if (val->port_vlan >= smi->num_ports)
+ return -EINVAL;
+
+ len += snprintf(buf + len, sizeof(smi->buf) - len,
+ "Port %d MIB counters\n",
+ val->port_vlan);
+
+ for (i = 0; i < smi->num_mib_counters; ++i) {
+ len += snprintf(buf + len, sizeof(smi->buf) - len,
+ "%-36s: ", smi->mib_counters[i].name);
+ if (!smi->ops->get_mib_counter(smi, i, val->port_vlan,
+ &counter))
+ len += snprintf(buf + len, sizeof(smi->buf) - len,
+ "%llu\n", counter);
+ else
+ len += snprintf(buf + len, sizeof(smi->buf) - len,
+ "%s\n", "error");
+ }
+
+ val->value.s = buf;
+ val->len = len;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_mib);
+
+int rtl8366_sw_get_vlan_info(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ int i;
+ u32 len = 0;
+ struct rtl8366_vlan_4k vlan4k;
+ struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+ char *buf = smi->buf;
+ int err;
+
+ if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+ return -EINVAL;
+
+ memset(buf, '\0', sizeof(smi->buf));
+
+ err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+ if (err)
+ return err;
+
+ len += snprintf(buf + len, sizeof(smi->buf) - len,
+ "VLAN %d: Ports: '", vlan4k.vid);
+
+ for (i = 0; i < smi->num_ports; i++) {
+ if (!(vlan4k.member & (1 << i)))
+ continue;
+
+ len += snprintf(buf + len, sizeof(smi->buf) - len, "%d%s", i,
+ (vlan4k.untag & (1 << i)) ? "" : "t");
+ }
+
+ len += snprintf(buf + len, sizeof(smi->buf) - len,
+ "', members=%04x, untag=%04x, fid=%u",
+ vlan4k.member, vlan4k.untag, vlan4k.fid);
+
+ val->value.s = buf;
+ val->len = len;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_info);
+
+int rtl8366_sw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+ struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+ struct switch_port *port;
+ struct rtl8366_vlan_4k vlan4k;
+ int i;
+
+ if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+ return -EINVAL;
+
+ smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+
+ port = &val->value.ports[0];
+ val->len = 0;
+ for (i = 0; i < smi->num_ports; i++) {
+ if (!(vlan4k.member & BIT(i)))
+ continue;
+
+ port->id = i;
+ port->flags = (vlan4k.untag & BIT(i)) ?
+ 0 : BIT(SWITCH_PORT_FLAG_TAGGED);
+ val->len++;
+ port++;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_ports);
+
+int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+ struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+ struct switch_port *port;
+ u32 member = 0;
+ u32 untag = 0;
+ int err;
+ int i;
+
+ if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+ return -EINVAL;
+
+ port = &val->value.ports[0];
+ for (i = 0; i < val->len; i++, port++) {
+ member |= BIT(port->id);
+
+ if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED)))
+ untag |= BIT(port->id);
+
+ /*
+ * To ensure that we have a valid MC entry for this VLAN,
+ * initialize the port VLAN ID here.
+ */
+ err = rtl8366_set_pvid(smi, port->id, val->port_vlan);
+ if (err < 0)
+ return err;
+ }
+
+ return rtl8366_set_vlan(smi, val->port_vlan, member, untag, 0);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_ports);
+
+int rtl8366_sw_get_vlan_fid(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rtl8366_vlan_4k vlan4k;
+ struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+ int err;
+
+ if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+ return -EINVAL;
+
+ err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+ if (err)
+ return err;
+
+ val->value.i = vlan4k.fid;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_fid);
+
+int rtl8366_sw_set_vlan_fid(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rtl8366_vlan_4k vlan4k;
+ struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+ int err;
+
+ if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+ return -EINVAL;
+
+ if (val->value.i < 0 || val->value.i > attr->max)
+ return -EINVAL;
+
+ err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+ if (err)
+ return err;
+
+ return rtl8366_set_vlan(smi, val->port_vlan,
+ vlan4k.member,
+ vlan4k.untag,
+ val->value.i);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_fid);
+
+int rtl8366_sw_get_vlan_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+ if (attr->ofs > 2)
+ return -EINVAL;
+
+ if (attr->ofs == 1)
+ val->value.i = smi->vlan_enabled;
+ else
+ val->value.i = smi->vlan4k_enabled;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_enable);
+
+int rtl8366_sw_set_vlan_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+ int err;
+
+ if (attr->ofs > 2)
+ return -EINVAL;
+
+ if (attr->ofs == 1)
+ err = rtl8366_enable_vlan(smi, val->value.i);
+ else
+ err = rtl8366_enable_vlan4k(smi, val->value.i);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_enable);
+
struct rtl8366_smi *rtl8366_smi_alloc(struct device *parent)
{
struct rtl8366_smi *smi;
goto err_free_sck;
}
+ err = rtl8366_init_vlan(smi);
+ if (err) {
+ dev_err(smi->parent, "VLAN initialization failed, err=%d\n",
+ err);
+ goto err_disable_hw;
+ }
+
+ err = rtl8366_enable_all_ports(smi, 1);
+ if (err)
+ goto err_disable_hw;
+
err = rtl8366_smi_mii_init(smi);
if (err)
goto err_free_sck;