230 lines
6.0 KiB
Diff
230 lines
6.0 KiB
Diff
Index: drivers/net/dsa/mv88e6xxx/chip.c
|
|
===================================================================
|
|
--- drivers/net/dsa/mv88e6xxx/chip.c
|
|
+++ linux-imx/drivers/net/dsa/mv88e6xxx/chip.c
|
|
@@ -3936,6 +3936,212 @@ static void mv88e6xxx_unregister_switch(
|
|
dsa_unregister_switch(chip->ds);
|
|
}
|
|
|
|
+/*
|
|
+ * Marvell switch mv88e6xxx sysfs interface
|
|
+ */
|
|
+static int mv88e6xxx_port;
|
|
+static u16 mv88e6xxx_reg;
|
|
+static ssize_t sysfs_reg_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ return scnprintf(buf, PAGE_SIZE, "0x%x", mv88e6xxx_reg);
|
|
+}
|
|
+
|
|
+static ssize_t sysfs_reg_store(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ if (kstrtou16(buf, 0, &mv88e6xxx_reg)) {
|
|
+ dev_err(dev, "Invalid reg\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ return (ssize_t)count;
|
|
+}
|
|
+
|
|
+static ssize_t sysfs_port_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ return scnprintf(buf, PAGE_SIZE, "0x%x", mv88e6xxx_port);
|
|
+}
|
|
+
|
|
+static ssize_t sysfs_port_store(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ if (kstrtoint(buf, 0, &mv88e6xxx_port)) {
|
|
+ dev_err(dev, "Invalid port \n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ return (ssize_t)count;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read the specified register of a specified port
|
|
+ */
|
|
+static ssize_t sysfs_port_reg_val_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct dsa_switch *ds = (struct dsa_switch *)dev_get_drvdata(dev);
|
|
+ struct mv88e6xxx_chip *chip = ds->priv;
|
|
+ u16 reg;
|
|
+ int err;
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+ err = mv88e6xxx_port_read(chip, mv88e6xxx_port, mv88e6xxx_reg, ®);
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ if (err) {
|
|
+ dev_err(dev, "Port %d register %x failed\n", mv88e6xxx_port, mv88e6xxx_reg);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ return scnprintf(buf, PAGE_SIZE, "0x%x\n", reg);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Purge the switch ATU cache
|
|
+ */
|
|
+static ssize_t sysfs_atu_flush(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct dsa_switch *ds = (struct dsa_switch *)dev_get_drvdata(dev);
|
|
+ struct mv88e6xxx_chip *chip = ds->priv;
|
|
+ int err;
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+ err = mv88e6xxx_g1_atu_flush(chip, 0, true);
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ if (err) {
|
|
+ dev_err(dev, "ATU flush failed\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ return (ssize_t)count;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Returns the ATU cache content
|
|
+ */
|
|
+static ssize_t sysfs_atu_dump(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct dsa_switch *ds = (struct dsa_switch *)dev_get_drvdata(dev);
|
|
+ struct mv88e6xxx_chip *chip = ds->priv;
|
|
+ struct mv88e6xxx_atu_entry addr;
|
|
+ u16 fid = 0;
|
|
+ ssize_t n = 0;
|
|
+ int err;
|
|
+
|
|
+ addr.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED;
|
|
+ eth_broadcast_addr(addr.mac);
|
|
+
|
|
+ n += scnprintf(buf + n, PAGE_SIZE - n,
|
|
+ "\nATU List:\n");
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+ while (1) {
|
|
+ err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr);
|
|
+ if (err)
|
|
+ break;
|
|
+
|
|
+ if (addr.state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED)
|
|
+ break;
|
|
+ n += scnprintf(buf + n, PAGE_SIZE - n,
|
|
+ "(%02x:%02x:%02x:%02x:%02x:%02x), PortVec %#x, State %#x\n",
|
|
+ addr.mac[0], addr.mac[1], addr.mac[2],
|
|
+ addr.mac[3], addr.mac[4], addr.mac[5],
|
|
+ addr.portvec,
|
|
+ addr.state);
|
|
+ }
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+ n += scnprintf(buf + n, PAGE_SIZE - n,"\n");
|
|
+ if (err)
|
|
+ return 0;
|
|
+ else
|
|
+ return n;
|
|
+}
|
|
+
|
|
+static ssize_t sysfs_atu_uc_static_entry_store(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct dsa_switch *ds = (struct dsa_switch *)dev_get_drvdata(dev);
|
|
+ struct mv88e6xxx_chip *chip = ds->priv;
|
|
+ const char *name = attr->attr.name;
|
|
+ struct mv88e6xxx_atu_entry entry;
|
|
+ u8 mac[ETH_ALEN];
|
|
+ u8 state;
|
|
+ int values[ETH_ALEN], dpv;
|
|
+ int i, err;
|
|
+
|
|
+ if(7 != sscanf(buf, "%x:%x:%x:%x:%x:%x %x%*c",
|
|
+ &values[0], &values[1], &values[2],
|
|
+ &values[3], &values[4], &values[5],
|
|
+ &dpv)) {
|
|
+ dev_err(dev, "Invalid input %s\n", buf);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i<ETH_ALEN; i++) {
|
|
+ if (values[i] < 0 || values[i] > 255) {
|
|
+ dev_err(dev, "Invalid mac %s\n", buf);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ mac[i] = (u8)values[i];
|
|
+ }
|
|
+
|
|
+ if (!strcmp(name, "atu_uc_static_entry_add"))
|
|
+ state = MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC;
|
|
+ else
|
|
+ state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED;
|
|
+
|
|
+ memset(&entry, 0, sizeof(entry));
|
|
+ ether_addr_copy(entry.mac, mac);
|
|
+ entry.portvec = dpv;
|
|
+ entry.state = state;
|
|
+
|
|
+ mutex_lock(&chip->reg_lock);
|
|
+ err = mv88e6xxx_g1_atu_loadpurge(chip, 0, &entry);
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
+
|
|
+ if (err) {
|
|
+ dev_err(dev, "Load static entry failed\n");
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ return (ssize_t)count;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(reg, S_IWUSR | S_IRUSR,
|
|
+ sysfs_reg_show, sysfs_reg_store);
|
|
+static DEVICE_ATTR(port, S_IWUSR | S_IRUSR,
|
|
+ sysfs_port_show, sysfs_port_store);
|
|
+static DEVICE_ATTR(port_reg_val, S_IWUSR | S_IRUSR,
|
|
+ sysfs_port_reg_val_show, NULL);
|
|
+static DEVICE_ATTR(atu, S_IWUSR | S_IRUSR,
|
|
+ sysfs_atu_dump, sysfs_atu_flush);
|
|
+static DEVICE_ATTR(atu_uc_static_entry_add, S_IWUSR | S_IRUSR,
|
|
+ NULL, sysfs_atu_uc_static_entry_store);
|
|
+static DEVICE_ATTR(atu_uc_static_entry_del, S_IWUSR | S_IRUSR,
|
|
+ NULL, sysfs_atu_uc_static_entry_store);
|
|
+
|
|
+static struct attribute *mv88e6xxx_sysfs_entries[] = {
|
|
+ &dev_attr_reg.attr,
|
|
+ &dev_attr_port.attr,
|
|
+ &dev_attr_port_reg_val.attr,
|
|
+ &dev_attr_atu.attr,
|
|
+ &dev_attr_atu_uc_static_entry_add.attr,
|
|
+ &dev_attr_atu_uc_static_entry_del.attr,
|
|
+ NULL
|
|
+};
|
|
+
|
|
+static struct attribute_group mv88e6xxx_attribute_group = {
|
|
+ .name = "config",
|
|
+ .attrs = mv88e6xxx_sysfs_entries,
|
|
+};
|
|
+
|
|
static int mv88e6xxx_probe(struct mdio_device *mdiodev)
|
|
{
|
|
struct device *dev = &mdiodev->dev;
|
|
@@ -4013,6 +4219,11 @@ static int mv88e6xxx_probe(struct mdio_d
|
|
if (err)
|
|
goto out_mdio;
|
|
|
|
+ /* register sysfs files */
|
|
+ err = sysfs_create_group(&dev->kobj, &mv88e6xxx_attribute_group);
|
|
+ if (err)
|
|
+ dev_err(chip->dev, "sysfs_create_group failed\n");
|
|
+
|
|
return 0;
|
|
|
|
out_mdio:
|