1326 lines
35 KiB
Diff
1326 lines
35 KiB
Diff
From 3724260d6f3b5e821ce7ead6410416bf02c3fff6 Mon Sep 17 00:00:00 2001
|
|
From: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
|
Date: Thu, 22 Oct 2015 14:31:23 -0400
|
|
Subject: net: dsa: mv88e6xxx: add debugfs interface
|
|
|
|
Add a debugfs directory named mv88e6xxx.X where X is the DSA switch
|
|
index. Mount the debugfs file system with:
|
|
|
|
# mount -t debugfs none /sys/kernel/debug
|
|
|
|
Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
|
[Modified by rmk for current kernels.]
|
|
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
|
|
---
|
|
drivers/net/dsa/mv88e6xxx/chip.c | 7 +
|
|
drivers/net/dsa/mv88e6xxx/chip.h | 2 +
|
|
drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c | 1099 +++++++++++++++++++++++++
|
|
3 files changed, 1108 insertions(+)
|
|
create mode 100644 drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c
|
|
|
|
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
|
|
index 272b0535d946..34b7f057a80d 100644
|
|
--- a/drivers/net/dsa/mv88e6xxx/chip.c
|
|
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
|
|
@@ -3008,8 +3008,13 @@ static int mv88e6390_setup_errata(struct mv88e6xxx_chip *chip)
|
|
return mv88e6xxx_software_reset(chip);
|
|
}
|
|
|
|
+#include "mv88e6xxx_debugfs.c"
|
|
+
|
|
static void mv88e6xxx_teardown(struct dsa_switch *ds)
|
|
{
|
|
+ struct mv88e6xxx_chip *chip = ds->priv;
|
|
+
|
|
+ mv88e6xxx_remove_debugfs(chip);
|
|
mv88e6xxx_teardown_devlink_params(ds);
|
|
dsa_devlink_resources_unregister(ds);
|
|
mv88e6xxx_teardown_devlink_regions(ds);
|
|
@@ -3128,6 +3133,8 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
|
|
if (err)
|
|
goto unlock;
|
|
|
|
+ mv88e6xxx_init_debugfs(chip);
|
|
+
|
|
unlock:
|
|
mv88e6xxx_reg_unlock(chip);
|
|
|
|
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
|
|
index 675b1f3e43b7..850706ed2dad 100644
|
|
--- a/drivers/net/dsa/mv88e6xxx/chip.h
|
|
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
|
|
@@ -377,6 +377,8 @@ struct mv88e6xxx_chip {
|
|
|
|
/* devlink regions */
|
|
struct devlink_region *regions[_MV88E6XXX_REGION_MAX];
|
|
+
|
|
+ struct dentry *dbgfs;
|
|
};
|
|
|
|
struct mv88e6xxx_bus_ops {
|
|
diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c
|
|
new file mode 100644
|
|
index 000000000000..931e769fe9ce
|
|
--- /dev/null
|
|
+++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c
|
|
@@ -0,0 +1,1099 @@
|
|
+#include <linux/debugfs.h>
|
|
+#include <linux/seq_file.h>
|
|
+
|
|
+#define GLOBAL2_PVT_ADDR 0x0b
|
|
+#define GLOBAL2_PVT_ADDR_BUSY BIT(15)
|
|
+#define GLOBAL2_PVT_ADDR_OP_INIT_ONES ((0x01 << 12) | GLOBAL2_PVT_ADDR_BUSY)
|
|
+#define GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN ((0x03 << 12) | GLOBAL2_PVT_ADDR_BUSY)
|
|
+#define GLOBAL2_PVT_ADDR_OP_READ ((0x04 << 12) | GLOBAL2_PVT_ADDR_BUSY)
|
|
+#define GLOBAL2_PVT_DATA 0x0c
|
|
+
|
|
+#define ADDR_GLOBAL2 0x1c
|
|
+
|
|
+static int mv88e6xxx_serdes_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
|
|
+{
|
|
+ return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES,
|
|
+ MV88E6352_SERDES_PAGE_FIBER,
|
|
+ reg, val);
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_serdes_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
|
|
+{
|
|
+ return mv88e6xxx_phy_page_write(chip, MV88E6352_ADDR_SERDES,
|
|
+ MV88E6352_SERDES_PAGE_FIBER,
|
|
+ reg, val);
|
|
+}
|
|
+
|
|
+static int _mv88e6xxx_pvt_wait(struct mv88e6xxx_chip *chip)
|
|
+{
|
|
+ return mv88e6xxx_wait_mask(chip, ADDR_GLOBAL2, GLOBAL2_PVT_ADDR,
|
|
+ GLOBAL2_PVT_ADDR_BUSY, 0);
|
|
+}
|
|
+
|
|
+static int _mv88e6xxx_pvt_cmd(struct mv88e6xxx_chip *chip, int src_dev,
|
|
+ int src_port, u16 op)
|
|
+{
|
|
+ u16 reg = op;
|
|
+ int err;
|
|
+
|
|
+ /* 9-bit Cross-chip PVT pointer: with GLOBAL2_MISC_5_BIT_PORT cleared,
|
|
+ * source device is 5-bit, source port is 4-bit.
|
|
+ */
|
|
+ reg |= (src_dev & 0x1f) << 4;
|
|
+ reg |= (src_port & 0xf);
|
|
+
|
|
+ err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, reg);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ return _mv88e6xxx_pvt_wait(chip);
|
|
+}
|
|
+
|
|
+static int _mv88e6xxx_pvt_read(struct mv88e6xxx_chip *chip, int src_dev,
|
|
+ int src_port, u16 *data)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = _mv88e6xxx_pvt_wait(chip);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ ret = _mv88e6xxx_pvt_cmd(chip, src_dev, src_port,
|
|
+ GLOBAL2_PVT_ADDR_OP_READ);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ return mv88e6xxx_g2_read(chip, GLOBAL2_PVT_DATA, data);
|
|
+}
|
|
+
|
|
+static int _mv88e6xxx_pvt_write(struct mv88e6xxx_chip *chip, int src_dev,
|
|
+ int src_port, u16 data)
|
|
+{
|
|
+ int err;
|
|
+
|
|
+ err = _mv88e6xxx_pvt_wait(chip);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_DATA, data);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ return _mv88e6xxx_pvt_cmd(chip, src_dev, src_port,
|
|
+ GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN);
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_regs_show(struct seq_file *s, void *p)
|
|
+{
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ int port, reg, ret;
|
|
+ u16 data;
|
|
+
|
|
+ seq_puts(s, " GLOBAL GLOBAL2 SERDES ");
|
|
+ for (port = 0; port < mv88e6xxx_num_ports(chip); port++)
|
|
+ seq_printf(s, " %2d ", port);
|
|
+ seq_puts(s, "\n");
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+
|
|
+ for (reg = 0; reg < 32; reg++) {
|
|
+ seq_printf(s, "%2x:", reg);
|
|
+
|
|
+ ret = mv88e6xxx_g1_read(chip, reg, &data);
|
|
+ if (ret < 0)
|
|
+ goto unlock;
|
|
+ seq_printf(s, " %4x ", data);
|
|
+
|
|
+ ret = mv88e6xxx_g2_read(chip, reg, &data);
|
|
+ if (ret < 0)
|
|
+ goto unlock;
|
|
+ seq_printf(s, " %4x ", data);
|
|
+
|
|
+ if (reg != MV88E6XXX_PHY_PAGE) {
|
|
+ ret = mv88e6xxx_serdes_read(chip, reg, &data);
|
|
+ if (ret < 0)
|
|
+ goto unlock;
|
|
+ } else {
|
|
+ data = 0;
|
|
+ }
|
|
+ seq_printf(s, " %4x ", data);
|
|
+
|
|
+ /* Port regs 0x1a-0x1f are reserved in 6185 family */
|
|
+ if (chip->info->family == MV88E6XXX_FAMILY_6185 && reg > 25) {
|
|
+ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port)
|
|
+ seq_printf(s, "%4c ", '-');
|
|
+ seq_puts(s, "\n");
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) {
|
|
+ ret = mv88e6xxx_port_read(chip, port, reg, &data);
|
|
+ if (ret < 0)
|
|
+ goto unlock;
|
|
+
|
|
+ seq_printf(s, "%4x ", data);
|
|
+ }
|
|
+
|
|
+ seq_puts(s, "\n");
|
|
+ }
|
|
+
|
|
+ ret = 0;
|
|
+unlock:
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static ssize_t mv88e6xxx_regs_write(struct file *file, const char __user *buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct seq_file *s = file->private_data;
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ char cmd[32], name[32] = { 0 };
|
|
+ unsigned int port, reg, val;
|
|
+ int ret;
|
|
+
|
|
+ if (count > sizeof(name) - 1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (copy_from_user(cmd, buf, sizeof(cmd)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ ret = sscanf(cmd, "%s %x %x", name, ®, &val);
|
|
+ if (ret != 3)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (reg > 0x1f || val > 0xffff)
|
|
+ return -ERANGE;
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+
|
|
+ if (strcasecmp(name, "GLOBAL") == 0)
|
|
+ ret = mv88e6xxx_g1_write(chip, reg, val);
|
|
+ else if (strcasecmp(name, "GLOBAL2") == 0)
|
|
+ ret = mv88e6xxx_g2_write(chip, reg, val);
|
|
+ else if (strcasecmp(name, "SERDES") == 0)
|
|
+ ret = mv88e6xxx_serdes_write(chip, reg, val);
|
|
+ else if (kstrtouint(name, 10, &port) == 0 && port < mv88e6xxx_num_ports(chip))
|
|
+ ret = mv88e6xxx_port_write(chip, port, reg, val);
|
|
+ else
|
|
+ ret = -EINVAL;
|
|
+
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ return ret < 0 ? ret : count;
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_regs_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, mv88e6xxx_regs_show, inode->i_private);
|
|
+}
|
|
+
|
|
+static const struct file_operations mv88e6xxx_regs_fops = {
|
|
+ .open = mv88e6xxx_regs_open,
|
|
+ .read = seq_read,
|
|
+ .write = mv88e6xxx_regs_write,
|
|
+ .llseek = no_llseek,
|
|
+ .release = single_release,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+static int mv88e6xxx_name_show(struct seq_file *s, void *p)
|
|
+{
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ struct dsa_switch *ds = chip->ds;
|
|
+ struct dsa_switch_tree *dst = ds->dst;
|
|
+ struct dsa_port *dp;
|
|
+ int i;
|
|
+
|
|
+ if (!ds->cd)
|
|
+ return 0;
|
|
+
|
|
+ seq_puts(s, " Port Name\n");
|
|
+
|
|
+ list_for_each_entry(dp, &dst->ports, list) {
|
|
+ if (dp->ds != ds)
|
|
+ continue;
|
|
+
|
|
+ i = dp->index;
|
|
+ if (!ds->cd->port_names[i])
|
|
+ continue;
|
|
+
|
|
+ seq_printf(s, "%4d %s", i, ds->cd->port_names[i]);
|
|
+
|
|
+ if (dp->slave)
|
|
+ seq_printf(s, " (%s)", netdev_name(dp->slave));
|
|
+
|
|
+ seq_puts(s, "\n");
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_name_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, mv88e6xxx_name_show, inode->i_private);
|
|
+}
|
|
+
|
|
+static const struct file_operations mv88e6xxx_name_fops = {
|
|
+ .open = mv88e6xxx_name_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = no_llseek,
|
|
+ .release = single_release,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+static int mv88e6xxx_atu_show(struct seq_file *s, void *p)
|
|
+{
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ struct mv88e6xxx_atu_entry addr;
|
|
+ const char *state;
|
|
+ int fid, i, err;
|
|
+
|
|
+ seq_puts(s, " FID MAC Addr State Trunk? DPV/Trunk ID\n");
|
|
+
|
|
+ for (fid = 0; fid < mv88e6xxx_num_databases(chip); ++fid) {
|
|
+ addr.state = 0;
|
|
+ eth_broadcast_addr(addr.mac);
|
|
+
|
|
+ do {
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+ err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr);
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (addr.state == 0)
|
|
+ break;
|
|
+
|
|
+ /* print ATU entry */
|
|
+ seq_printf(s, "%4d %pM", fid, addr.mac);
|
|
+
|
|
+ if (is_multicast_ether_addr(addr.mac)) {
|
|
+ switch (addr.state) {
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_PO:
|
|
+ state = "MC_STATIC_PO";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT_PO:
|
|
+ state = "MC_STATIC_MGMT_PO";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL_PO:
|
|
+ state = "MC_STATIC_NRL_PO";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_POLICY_PO:
|
|
+ state = "MC_STATIC_POLICY_PO";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC:
|
|
+ state = "MC_STATIC";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT:
|
|
+ state = "MC_STATIC_MGMT";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL:
|
|
+ state = "MC_STATIC_NRL";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_POLICY:
|
|
+ state = "MC_STATIC_POLICY";
|
|
+ break;
|
|
+ case 0xb: case 0xa: case 0x9: case 0x8:
|
|
+ /* Reserved for future use */
|
|
+ case 0x3: case 0x2: case 0x1:
|
|
+ /* Reserved for future use */
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_MC_UNUSED:
|
|
+ default:
|
|
+ state = "???";
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ switch (addr.state) {
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_PO:
|
|
+ state = "UC_STATIC_PO";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC:
|
|
+ state = "UC_STATIC";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT_PO:
|
|
+ state = "UC_STATIC_MGMT_PO";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT:
|
|
+ state = "UC_STATIC_MGMT";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL_PO:
|
|
+ state = "UC_STATIC_NRL_PO";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL:
|
|
+ state = "UC_STATIC_NRL";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_POLICY_PO:
|
|
+ state = "UC_STATIC_POLICY_PO";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_POLICY:
|
|
+ state = "UC_STATIC_POLICY";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_7_NEWEST:
|
|
+ state = "Age 7 (newest)";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_6:
|
|
+ state = "Age 6";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_5:
|
|
+ state = "Age 5";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_4:
|
|
+ state = "Age 4";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_3:
|
|
+ state = "Age 3";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_2:
|
|
+ state = "Age 2";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_1_OLDEST:
|
|
+ state = "Age 1 (oldest)";
|
|
+ break;
|
|
+ case MV88E6XXX_G1_ATU_DATA_STATE_UC_UNUSED:
|
|
+ default:
|
|
+ state = "???";
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ seq_printf(s, " %19s", state);
|
|
+
|
|
+ if (addr.trunk) {
|
|
+ seq_printf(s, " y %d",
|
|
+ addr.portvec);
|
|
+ } else {
|
|
+ seq_puts(s, " n ");
|
|
+ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
|
|
+ seq_printf(s, " %c",
|
|
+ addr.portvec & BIT(i) ?
|
|
+ 48 + i : '-');
|
|
+ }
|
|
+
|
|
+ seq_puts(s, "\n");
|
|
+ } while (!is_broadcast_ether_addr(addr.mac));
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t mv88e6xxx_atu_write(struct file *file, const char __user *buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct seq_file *s = file->private_data;
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ char cmd[64];
|
|
+ unsigned int fid;
|
|
+ int ret;
|
|
+
|
|
+ if (copy_from_user(cmd, buf, sizeof(cmd)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ ret = sscanf(cmd, "%u", &fid);
|
|
+ if (ret != 1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (fid >= mv88e6xxx_num_databases(chip))
|
|
+ return -ERANGE;
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+ ret = mv88e6xxx_g1_atu_flush(chip, fid, true);
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ return ret < 0 ? ret : count;
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_atu_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, mv88e6xxx_atu_show, inode->i_private);
|
|
+}
|
|
+
|
|
+static const struct file_operations mv88e6xxx_atu_fops = {
|
|
+ .open = mv88e6xxx_atu_open,
|
|
+ .read = seq_read,
|
|
+ .write = mv88e6xxx_atu_write,
|
|
+ .llseek = no_llseek,
|
|
+ .release = single_release,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+static int mv88e6xxx_default_vid_show(struct seq_file *s, void *p)
|
|
+{
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ u16 pvid;
|
|
+ int i, err;
|
|
+
|
|
+ seq_puts(s, " Port DefaultVID\n");
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+
|
|
+ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
|
+ err = mv88e6xxx_port_get_pvid(chip, i, &pvid);
|
|
+ if (err)
|
|
+ break;
|
|
+
|
|
+ seq_printf(s, "%4d %d\n", i, pvid);
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static ssize_t mv88e6xxx_default_vid_write(struct file *file,
|
|
+ const char __user *buf, size_t count,
|
|
+ loff_t *ppos)
|
|
+{
|
|
+ struct seq_file *s = file->private_data;
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ char cmd[32];
|
|
+ unsigned int port, pvid;
|
|
+ int ret;
|
|
+
|
|
+ if (copy_from_user(cmd, buf, sizeof(cmd)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ ret = sscanf(cmd, "%u %u", &port, &pvid);
|
|
+ if (ret != 2)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (port >= mv88e6xxx_num_ports(chip) || pvid > 0xfff)
|
|
+ return -ERANGE;
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+ ret = mv88e6xxx_port_set_pvid(chip, port, pvid);
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ return ret < 0 ? ret : count;
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_default_vid_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, mv88e6xxx_default_vid_show, inode->i_private);
|
|
+}
|
|
+
|
|
+static const struct file_operations mv88e6xxx_default_vid_fops = {
|
|
+ .open = mv88e6xxx_default_vid_open,
|
|
+ .read = seq_read,
|
|
+ .write = mv88e6xxx_default_vid_write,
|
|
+ .llseek = no_llseek,
|
|
+ .release = single_release,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+static int mv88e6xxx_fid_show(struct seq_file *s, void *p)
|
|
+{
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ u16 fid;
|
|
+ int i, err;
|
|
+
|
|
+ seq_puts(s, " Port FID\n");
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+
|
|
+ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
|
+ err = mv88e6xxx_port_get_fid(chip, i, &fid);
|
|
+ if (err)
|
|
+ break;
|
|
+
|
|
+ seq_printf(s, "%4d %d\n", i, fid);
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_fid_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, mv88e6xxx_fid_show, inode->i_private);
|
|
+}
|
|
+
|
|
+static const struct file_operations mv88e6xxx_fid_fops = {
|
|
+ .open = mv88e6xxx_fid_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = no_llseek,
|
|
+ .release = single_release,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+static const char * const mv88e6xxx_port_state_names[] = {
|
|
+ [MV88E6XXX_PORT_CTL0_STATE_DISABLED] = "Disabled",
|
|
+ [MV88E6XXX_PORT_CTL0_STATE_BLOCKING] = "Blocking/Listening",
|
|
+ [MV88E6XXX_PORT_CTL0_STATE_LEARNING] = "Learning",
|
|
+ [MV88E6XXX_PORT_CTL0_STATE_FORWARDING] = "Forwarding",
|
|
+};
|
|
+
|
|
+static int mv88e6xxx_state_show(struct seq_file *s, void *p)
|
|
+{
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ int i, ret;
|
|
+ u16 data;
|
|
+
|
|
+ /* header */
|
|
+ seq_puts(s, " Port Mode\n");
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+
|
|
+ /* One line per input port */
|
|
+ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
|
+ seq_printf(s, "%4d ", i);
|
|
+
|
|
+ ret = mv88e6xxx_port_read(chip, i, MV88E6XXX_PORT_CTL0, &data);
|
|
+ if (ret < 0)
|
|
+ goto unlock;
|
|
+
|
|
+ data &= MV88E6XXX_PORT_CTL0_STATE_MASK;
|
|
+ seq_printf(s, " %s\n", mv88e6xxx_port_state_names[data]);
|
|
+ ret = 0;
|
|
+ }
|
|
+
|
|
+unlock:
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_state_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, mv88e6xxx_state_show, inode->i_private);
|
|
+}
|
|
+
|
|
+static const struct file_operations mv88e6xxx_state_fops = {
|
|
+ .open = mv88e6xxx_state_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = no_llseek,
|
|
+ .release = single_release,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+static const char * const mv88e6xxx_port_8021q_mode_names[] = {
|
|
+ [MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED] = "Disabled",
|
|
+ [MV88E6XXX_PORT_CTL2_8021Q_MODE_FALLBACK] = "Fallback",
|
|
+ [MV88E6XXX_PORT_CTL2_8021Q_MODE_CHECK] = "Check",
|
|
+ [MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE] = "Secure",
|
|
+};
|
|
+
|
|
+static int mv88e6xxx_8021q_mode_show(struct seq_file *s, void *p)
|
|
+{
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ int i, ret;
|
|
+ u16 data;
|
|
+
|
|
+ /* header */
|
|
+ seq_puts(s, " Port Mode\n");
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+
|
|
+ /* One line per input port */
|
|
+ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
|
+ seq_printf(s, "%4d ", i);
|
|
+
|
|
+ ret = mv88e6xxx_port_read(chip, i, MV88E6XXX_PORT_CTL2, &data);
|
|
+ if (ret < 0)
|
|
+ goto unlock;
|
|
+
|
|
+ data &= MV88E6XXX_PORT_CTL2_8021Q_MODE_MASK;
|
|
+ seq_printf(s, " %s\n", mv88e6xxx_port_8021q_mode_names[data]);
|
|
+ ret = 0;
|
|
+ }
|
|
+
|
|
+unlock:
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_8021q_mode_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, mv88e6xxx_8021q_mode_show, inode->i_private);
|
|
+}
|
|
+
|
|
+static const struct file_operations mv88e6xxx_8021q_mode_fops = {
|
|
+ .open = mv88e6xxx_8021q_mode_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = no_llseek,
|
|
+ .release = single_release,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+static int mv88e6xxx_vlan_table_show(struct seq_file *s, void *p)
|
|
+{
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ int i, j, ret;
|
|
+ u16 data;
|
|
+
|
|
+ /* header */
|
|
+ seq_puts(s, " Port");
|
|
+ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
|
|
+ seq_printf(s, " %2d", i);
|
|
+ seq_puts(s, "\n");
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+
|
|
+ /* One line per input port */
|
|
+ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
|
+ seq_printf(s, "%4d ", i);
|
|
+
|
|
+ ret = mv88e6xxx_port_read(chip, i, MV88E6XXX_PORT_BASE_VLAN, &data);
|
|
+ if (ret < 0)
|
|
+ goto unlock;
|
|
+
|
|
+ /* One column per output port */
|
|
+ for (j = 0; j < mv88e6xxx_num_ports(chip); ++j)
|
|
+ seq_printf(s, " %c", data & BIT(j) ? '*' : '-');
|
|
+ seq_puts(s, "\n");
|
|
+ }
|
|
+
|
|
+ ret = 0;
|
|
+unlock:
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_vlan_table_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, mv88e6xxx_vlan_table_show, inode->i_private);
|
|
+}
|
|
+
|
|
+static const struct file_operations mv88e6xxx_vlan_table_fops = {
|
|
+ .open = mv88e6xxx_vlan_table_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = no_llseek,
|
|
+ .release = single_release,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+static int mv88e6xxx_pvt_show(struct seq_file *s, void *p)
|
|
+{
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ struct dsa_switch_tree *dst = chip->ds->dst;
|
|
+ int port, src_dev, src_port;
|
|
+ u16 pvlan;
|
|
+ int err = 0;
|
|
+
|
|
+ if (chip->info->family == MV88E6XXX_FAMILY_6185)
|
|
+ return -ENODEV;
|
|
+
|
|
+ /* header */
|
|
+ seq_puts(s, " Dev Port PVLAN");
|
|
+ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port)
|
|
+ seq_printf(s, " %2d", port);
|
|
+ seq_puts(s, "\n");
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+
|
|
+ /* One line per external port */
|
|
+ for (src_dev = 0; src_dev < DSA_MAX_SWITCHES; ++src_dev) {
|
|
+ if (!dst->ds[src_dev])
|
|
+ break;
|
|
+
|
|
+ if (src_dev == chip->ds->index)
|
|
+ continue;
|
|
+
|
|
+ seq_puts(s, "\n");
|
|
+ for (src_port = 0; src_port < 16; ++src_port) {
|
|
+ if (src_port >= DSA_MAX_PORTS)
|
|
+ break;
|
|
+
|
|
+ err = _mv88e6xxx_pvt_read(chip, src_dev, src_port,
|
|
+ &pvlan);
|
|
+ if (err)
|
|
+ goto unlock;
|
|
+
|
|
+ seq_printf(s, " %d %2d %03hhx ", src_dev, src_port,
|
|
+ pvlan);
|
|
+
|
|
+ /* One column per internal output port */
|
|
+ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port)
|
|
+ seq_printf(s, " %c",
|
|
+ pvlan & BIT(port) ? '*' : '-');
|
|
+ seq_puts(s, "\n");
|
|
+ }
|
|
+ }
|
|
+
|
|
+unlock:
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static ssize_t mv88e6xxx_pvt_write(struct file *file, const char __user *buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct seq_file *s = file->private_data;
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ const u16 mask = (1 << mv88e6xxx_num_ports(chip)) - 1;
|
|
+ char cmd[32];
|
|
+ unsigned int src_dev, src_port, pvlan;
|
|
+ int ret;
|
|
+
|
|
+ if (copy_from_user(cmd, buf, sizeof(cmd)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ if (sscanf(cmd, "%d %d %x", &src_dev, &src_port, &pvlan) != 3)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (src_dev >= 32 || src_port >= 16 || pvlan & ~mask)
|
|
+ return -ERANGE;
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+ ret = _mv88e6xxx_pvt_write(chip, src_dev, src_port, pvlan);
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ return ret < 0 ? ret : count;
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_pvt_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, mv88e6xxx_pvt_show, inode->i_private);
|
|
+}
|
|
+
|
|
+static const struct file_operations mv88e6xxx_pvt_fops = {
|
|
+ .open = mv88e6xxx_pvt_open,
|
|
+ .read = seq_read,
|
|
+ .write = mv88e6xxx_pvt_write,
|
|
+ .llseek = no_llseek,
|
|
+ .release = single_release,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+static int mv88e6xxx_vtu_show(struct seq_file *s, void *p)
|
|
+{
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ struct mv88e6xxx_vtu_entry next = { 0 };
|
|
+ int port, ret = 0;
|
|
+
|
|
+ seq_puts(s, " VID FID SID");
|
|
+ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port)
|
|
+ seq_printf(s, " %2d", port);
|
|
+ seq_puts(s, "\n");
|
|
+
|
|
+ if (!chip->info->ops->vtu_getnext)
|
|
+ return 0;
|
|
+
|
|
+ next.vid = chip->info->max_vid; /* first or lowest VID */
|
|
+
|
|
+ do {
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+ ret = chip->info->ops->vtu_getnext(chip, &next);
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+ if (ret < 0)
|
|
+ break;
|
|
+
|
|
+ if (!next.valid)
|
|
+ break;
|
|
+
|
|
+ seq_printf(s, "%4d %4d %2d", next.vid, next.fid, next.sid);
|
|
+ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) {
|
|
+ switch (next.member[port]) {
|
|
+ case MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED:
|
|
+ seq_puts(s, " =");
|
|
+ break;
|
|
+ case MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED:
|
|
+ seq_puts(s, " u");
|
|
+ break;
|
|
+ case MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_TAGGED:
|
|
+ seq_puts(s, " t");
|
|
+ break;
|
|
+ case MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER:
|
|
+ seq_puts(s, " x");
|
|
+ break;
|
|
+ default:
|
|
+ seq_puts(s, " ??");
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ seq_puts(s, "\n");
|
|
+ } while (next.vid < chip->info->max_vid);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static ssize_t mv88e6xxx_vtu_write(struct file *file, const char __user *buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct seq_file *s = file->private_data;
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ struct mv88e6xxx_vtu_entry entry = { 0 };
|
|
+ bool valid = true;
|
|
+ char cmd[64], tags[12]; /* DSA_MAX_PORTS */
|
|
+ int vid, fid, sid, port, ret;
|
|
+
|
|
+ if (!chip->info->ops->vtu_loadpurge)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ if (copy_from_user(cmd, buf, sizeof(cmd)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ /* scan 12 chars instead of num_ports to avoid dynamic scanning... */
|
|
+ ret = sscanf(cmd, "%d %d %d %c %c %c %c %c %c %c %c %c %c %c %c", &vid,
|
|
+ &fid, &sid, &tags[0], &tags[1], &tags[2], &tags[3],
|
|
+ &tags[4], &tags[5], &tags[6], &tags[7], &tags[8], &tags[9],
|
|
+ &tags[10], &tags[11]);
|
|
+ if (ret == 1)
|
|
+ valid = false;
|
|
+ else if (ret != 3 + mv88e6xxx_num_ports(chip))
|
|
+ return -EINVAL;
|
|
+
|
|
+ entry.vid = vid;
|
|
+ entry.valid = valid;
|
|
+
|
|
+ if (valid) {
|
|
+ entry.fid = fid;
|
|
+ entry.sid = sid;
|
|
+ /* Note: The VTU entry pointed by VID will be loaded but not
|
|
+ * considered valid until the STU entry pointed by SID is valid.
|
|
+ */
|
|
+
|
|
+ for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) {
|
|
+ u8 tag;
|
|
+
|
|
+ switch (tags[port]) {
|
|
+ case 'u':
|
|
+ tag = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED;
|
|
+ break;
|
|
+ case 't':
|
|
+ tag = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_TAGGED;
|
|
+ break;
|
|
+ case 'x':
|
|
+ tag = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER;
|
|
+ break;
|
|
+ case '=':
|
|
+ tag = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED;
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ entry.member[port] = tag;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+ ret = chip->info->ops->vtu_loadpurge(chip, &entry);
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ return ret < 0 ? ret : count;
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_vtu_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, mv88e6xxx_vtu_show, inode->i_private);
|
|
+}
|
|
+
|
|
+static const struct file_operations mv88e6xxx_vtu_fops = {
|
|
+ .open = mv88e6xxx_vtu_open,
|
|
+ .read = seq_read,
|
|
+ .write = mv88e6xxx_vtu_write,
|
|
+ .llseek = no_llseek,
|
|
+ .release = single_release,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+#if 0
|
|
+static int mv88e6xxx_stats_show(struct seq_file *s, void *p)
|
|
+{
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ char *strs;
|
|
+ u64 *stats;
|
|
+ int stat, port, num_stats, num_ports;
|
|
+ int err = 0;
|
|
+
|
|
+ num_stats = mv88e6xxx_get_sset_count(chip->ds);
|
|
+ if (num_stats == 0)
|
|
+ return 0;
|
|
+
|
|
+ num_ports = mv88e6xxx_num_ports(chip);
|
|
+
|
|
+ strs = kcalloc(num_stats, ETH_GSTRING_LEN, GFP_KERNEL);
|
|
+ stats = kcalloc(num_stats, num_ports * sizeof(*stats), GFP_KERNEL);
|
|
+ if (!strs || !strs) {
|
|
+ kfree(strs);
|
|
+ kfree(stats);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ mv88e6xxx_get_strings(chip->ds, 0, strs);
|
|
+
|
|
+ for (port = 0; port < num_ports; port++)
|
|
+ mv88e6xxx_get_ethtool_stats(chip->ds, port, stats + (port * num_stats));
|
|
+
|
|
+ seq_puts(s, " Statistic ");
|
|
+ for (port = 0; port < mv88e6xxx_num_ports(chip); port++)
|
|
+ seq_printf(s, " Port %2d ", port);
|
|
+ seq_puts(s, "\n");
|
|
+
|
|
+ for (stat = 0; stat < num_stats; stat++) {
|
|
+ seq_printf(s, "%19s: ", strs + stat * ETH_GSTRING_LEN);
|
|
+ for (port = 0 ; port < num_ports; port++) {
|
|
+ u64 value = stats[stat + port * num_stats];
|
|
+
|
|
+ seq_printf(s, "%8llu ", value);
|
|
+ }
|
|
+ seq_puts(s, "\n");
|
|
+ }
|
|
+
|
|
+ kfree(stats);
|
|
+ kfree(strs);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_stats_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, mv88e6xxx_stats_show, inode->i_private);
|
|
+}
|
|
+
|
|
+static const struct file_operations mv88e6xxx_stats_fops = {
|
|
+ .open = mv88e6xxx_stats_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = no_llseek,
|
|
+ .release = single_release,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+#endif
|
|
+static int mv88e6xxx_device_map_show(struct seq_file *s, void *p)
|
|
+{
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ int target, ret;
|
|
+ u16 data, port_mask;
|
|
+
|
|
+ seq_puts(s, "Target Port\n");
|
|
+
|
|
+ /* FIXME */
|
|
+ port_mask = MV88E6390_G2_DEVICE_MAPPING_PORT_MASK;
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+ for (target = 0; target < 32; target++) {
|
|
+ ret = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_DEVICE_MAPPING,
|
|
+ target << 8 /* MV88E6XXX_G2_DEVICE_MAPPING_DEV_MASK */);
|
|
+ if (ret < 0)
|
|
+ goto out;
|
|
+ ret = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_DEVICE_MAPPING, &data);
|
|
+ if (ret < 0)
|
|
+ goto out;
|
|
+ seq_printf(s, " %2d %2d\n", target, data & port_mask);
|
|
+ }
|
|
+out:
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_device_map_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, mv88e6xxx_device_map_show, inode->i_private);
|
|
+}
|
|
+
|
|
+static const struct file_operations mv88e6xxx_device_map_fops = {
|
|
+ .open = mv88e6xxx_device_map_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = no_llseek,
|
|
+ .release = single_release,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+/* Must be called with SMI lock held */
|
|
+static int _mv88e6xxx_scratch_wait(struct mv88e6xxx_chip *chip)
|
|
+{
|
|
+ return mv88e6xxx_wait_mask(chip, ADDR_GLOBAL2,
|
|
+ MV88E6XXX_G2_SCRATCH_MISC_MISC,
|
|
+ MV88E6XXX_G2_SCRATCH_MISC_UPDATE, 0);
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_scratch_show(struct seq_file *s, void *p)
|
|
+{
|
|
+ struct mv88e6xxx_chip *chip = s->private;
|
|
+ int reg, ret;
|
|
+ u16 data;
|
|
+
|
|
+ seq_puts(s, "Register Value\n");
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+ for (reg = 0; reg < 0x80; reg++) {
|
|
+ ret = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC,
|
|
+ reg << 8 /* MV88E6XXX_G2_SCRATCH_MISC_PTR_MASK */);
|
|
+ if (ret < 0)
|
|
+ goto out;
|
|
+
|
|
+ ret = _mv88e6xxx_scratch_wait(chip);
|
|
+ if (ret < 0)
|
|
+ goto out;
|
|
+
|
|
+ ret = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, &data);
|
|
+ seq_printf(s, " %2x %2x\n", reg,
|
|
+ data & MV88E6XXX_G2_SCRATCH_MISC_DATA_MASK);
|
|
+ }
|
|
+out:
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_scratch_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, mv88e6xxx_scratch_show, inode->i_private);
|
|
+}
|
|
+
|
|
+static const struct file_operations mv88e6xxx_scratch_fops = {
|
|
+ .open = mv88e6xxx_scratch_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = no_llseek,
|
|
+ .release = single_release,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+static void mv88e6xxx_init_debugfs(struct mv88e6xxx_chip *chip)
|
|
+{
|
|
+ char *name;
|
|
+
|
|
+ name = kasprintf(GFP_KERNEL, "mv88e6xxx.%d", chip->ds->index);
|
|
+ chip->dbgfs = debugfs_create_dir(name, NULL);
|
|
+
|
|
+ kfree(name);
|
|
+
|
|
+ debugfs_create_file("regs", S_IRUGO | S_IWUSR, chip->dbgfs, chip,
|
|
+ &mv88e6xxx_regs_fops);
|
|
+
|
|
+ debugfs_create_file("name", S_IRUGO, chip->dbgfs, chip,
|
|
+ &mv88e6xxx_name_fops);
|
|
+
|
|
+ debugfs_create_file("atu", S_IRUGO | S_IWUSR, chip->dbgfs, chip,
|
|
+ &mv88e6xxx_atu_fops);
|
|
+
|
|
+ debugfs_create_file("default_vid", S_IRUGO | S_IWUSR, chip->dbgfs, chip,
|
|
+ &mv88e6xxx_default_vid_fops);
|
|
+
|
|
+ debugfs_create_file("fid", S_IRUGO, chip->dbgfs, chip, &mv88e6xxx_fid_fops);
|
|
+
|
|
+ debugfs_create_file("state", S_IRUGO, chip->dbgfs, chip,
|
|
+ &mv88e6xxx_state_fops);
|
|
+
|
|
+ debugfs_create_file("8021q_mode", S_IRUGO, chip->dbgfs, chip,
|
|
+ &mv88e6xxx_8021q_mode_fops);
|
|
+
|
|
+ debugfs_create_file("vlan_table", S_IRUGO, chip->dbgfs, chip,
|
|
+ &mv88e6xxx_vlan_table_fops);
|
|
+
|
|
+ debugfs_create_file("pvt", S_IRUGO | S_IWUSR, chip->dbgfs, chip,
|
|
+ &mv88e6xxx_pvt_fops);
|
|
+
|
|
+ debugfs_create_file("vtu", S_IRUGO | S_IWUSR, chip->dbgfs, chip,
|
|
+ &mv88e6xxx_vtu_fops);
|
|
+#if 0
|
|
+ debugfs_create_file("stats", S_IRUGO, chip->dbgfs, chip,
|
|
+ &mv88e6xxx_stats_fops);
|
|
+#endif
|
|
+ debugfs_create_file("device_map", S_IRUGO, chip->dbgfs, chip,
|
|
+ &mv88e6xxx_device_map_fops);
|
|
+
|
|
+ debugfs_create_file("scratch", S_IRUGO, chip->dbgfs, chip,
|
|
+ &mv88e6xxx_scratch_fops);
|
|
+}
|
|
+
|
|
+static void mv88e6xxx_remove_debugfs(struct mv88e6xxx_chip *chip)
|
|
+{
|
|
+ debugfs_remove_recursive(chip->dbgfs);
|
|
+}
|
|
--
|
|
cgit v1.2.3
|
|
From ee71c167800c79ed367a6cb8d0efb4e2cfffabf7 Mon Sep 17 00:00:00 2001
|
|
From: Russell King <rmk+kernel@armlinux.org.uk>
|
|
Date: Mon, 27 Jan 2020 14:00:12 +0000
|
|
Subject: net: dsa: mv88e6xxx: debugfs hacks to fix the compile
|
|
|
|
This is the problem with out-of-tree maintained patches; they break,
|
|
sometimes requiring substantial rework. It's all very well promising
|
|
to publish new versions as that happens, but it causes pain when they
|
|
aren't published in a timely manner. Hence this hack.
|
|
|
|
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
|
|
---
|
|
drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c | 5 ++++-
|
|
1 file changed, 4 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c
|
|
index 931e769fe9ce..4005a4760884 100644
|
|
--- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c
|
|
+++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx_debugfs.c
|
|
@@ -668,6 +668,7 @@ static const struct file_operations mv88e6xxx_vlan_table_fops = {
|
|
|
|
static int mv88e6xxx_pvt_show(struct seq_file *s, void *p)
|
|
{
|
|
+#if 0
|
|
struct mv88e6xxx_chip *chip = s->private;
|
|
struct dsa_switch_tree *dst = chip->ds->dst;
|
|
int port, src_dev, src_port;
|
|
@@ -716,8 +717,10 @@ static int mv88e6xxx_pvt_show(struct seq_file *s, void *p)
|
|
|
|
unlock:
|
|
mutex_unlock(&chip->reg_lock);
|
|
-
|
|
return err;
|
|
+#else
|
|
+ return 0;
|
|
+#endif
|
|
}
|
|
|
|
static ssize_t mv88e6xxx_pvt_write(struct file *file, const char __user *buf,
|
|
--
|
|
cgit v1.2.3
|
|
From 04bcd6e51433052a79feb37fe6d02c9c06980d62 Mon Sep 17 00:00:00 2001
|
|
From: Russell King <rmk+kernel@armlinux.org.uk>
|
|
Date: Thu, 28 Sep 2017 12:09:56 +0100
|
|
Subject: Revert "net: dsa: mv88e6xxx: remove LED control register"
|
|
|
|
This reverts commit c56a71a92114e3198e249593841cb744abaadcb7.
|
|
---
|
|
drivers/net/dsa/mv88e6xxx/port.h | 3 +++
|
|
1 file changed, 3 insertions(+)
|
|
|
|
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
|
|
index b10e5aebacf6..bfc37d152f4d 100644
|
|
--- a/drivers/net/dsa/mv88e6xxx/port.h
|
|
+++ b/drivers/net/dsa/mv88e6xxx/port.h
|
|
@@ -282,6 +282,9 @@
|
|
/* Offset 0x13: OutFiltered Counter */
|
|
#define MV88E6XXX_PORT_OUT_FILTERED 0x13
|
|
|
|
+/* Offset 0x16: LED Control */
|
|
+#define MV88E6XXX_PORT_LED_CONTROL 0x16
|
|
+
|
|
/* Offset 0x18: IEEE Priority Mapping Table */
|
|
#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE 0x18
|
|
#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_UPDATE 0x8000
|
|
--
|
|
cgit v1.2.3
|
|
From e43ea2d687d58c0df5ca0334fe99385664b38431 Mon Sep 17 00:00:00 2001
|
|
From: Russell King <rmk+kernel@armlinux.org.uk>
|
|
Date: Sat, 7 Jan 2017 20:47:36 +0000
|
|
Subject: net: dsa: program 6176 LED registers
|
|
|
|
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
|
|
---
|
|
drivers/net/dsa/mv88e6xxx/chip.c | 20 ++++++++++++++++++++
|
|
1 file changed, 20 insertions(+)
|
|
|
|
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
|
|
index 34b7f057a80d..ff4d9cf2fbd1 100644
|
|
--- a/drivers/net/dsa/mv88e6xxx/chip.c
|
|
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
|
|
@@ -2720,6 +2720,20 @@ static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port)
|
|
return 0;
|
|
}
|
|
|
|
+static int mv88e6xxx_setup_led(struct mv88e6xxx_chip *chip, int port)
|
|
+{
|
|
+ int err;
|
|
+
|
|
+ /* LED0 = link/activity, LED1 = 10/100 */
|
|
+ err = mv88e6xxx_wait_bit(chip, chip->info->port_base_addr + port,
|
|
+ MV88E6XXX_PORT_LED_CONTROL, 15, 0);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ return mv88e6xxx_write(chip, chip->info->port_base_addr + port,
|
|
+ MV88E6XXX_PORT_LED_CONTROL, 0x80b3);
|
|
+}
|
|
+
|
|
static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
|
|
{
|
|
struct dsa_switch *ds = chip->ds;
|
|
@@ -2775,6 +2789,12 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
|
|
if (err)
|
|
return err;
|
|
|
|
+ if (chip->info->num_gpio) {
|
|
+ err = mv88e6xxx_setup_led(chip, port);
|
|
+ if (err)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
/* Port Control 2: don't force a good FCS, set the MTU size to
|
|
* 10222 bytes, disable 802.1q tags checking, don't discard tagged or
|
|
* untagged frames on this port, do a destination address lookup on all
|
|
--
|
|
cgit v1.2.3
|
|
From 0732f844ba9e295c78a911016fec9f00b14a8f59 Mon Sep 17 00:00:00 2001
|
|
From: Russell King <rmk+kernel@armlinux.org.uk>
|
|
Date: Wed, 8 Jul 2020 12:31:01 +0100
|
|
Subject: net: dsa/mv88e6xxx: add support for rate-matching PHYs
|
|
|
|
Add basic support for rate-matching 10G PHYs for mv88e6xxx - if we are
|
|
in RXAUI, XAUI or 10GBASE-R mode, the link speed is 10G, even if the
|
|
media is running at a slower speed.
|
|
|
|
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
|
|
---
|
|
drivers/net/dsa/mv88e6xxx/chip.c | 12 ++++++++++++
|
|
1 file changed, 12 insertions(+)
|
|
|
|
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
|
|
index ff4d9cf2fbd1..5925d5402306 100644
|
|
--- a/drivers/net/dsa/mv88e6xxx/chip.c
|
|
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
|
|
@@ -784,6 +784,18 @@ static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,
|
|
if (err)
|
|
goto error;
|
|
|
|
+ /* The link parameters passed in are the media side parameters.
|
|
+ * If in RXAUI, XAUI or 10GBASE-R with a rate matching PHY, we
|
|
+ * need to operate our link at 10G. Only full duplex is
|
|
+ * supported at this speed.
|
|
+ */
|
|
+ if (interface == PHY_INTERFACE_MODE_RXAUI ||
|
|
+ interface == PHY_INTERFACE_MODE_XAUI ||
|
|
+ interface == PHY_INTERFACE_MODE_10GBASER) {
|
|
+ speed = SPEED_10000;
|
|
+ duplex = DUPLEX_FULL;
|
|
+ }
|
|
+
|
|
if (ops->port_set_speed_duplex) {
|
|
err = ops->port_set_speed_duplex(chip, port,
|
|
speed, duplex);
|
|
--
|
|
cgit v1.2.3
|
|
|
|
|