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 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: