diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig index 394ca8678d2b..c0a951599cdf 100644 --- a/drivers/net/dsa/microchip/Kconfig +++ b/drivers/net/dsa/microchip/Kconfig @@ -39,3 +39,10 @@ config NET_DSA_MICROCHIP_KSZ8863_SMI help Select to enable support for registering switches configured through Microchip SMI. It supports the KSZ8863 and KSZ8873 switch. + +config NET_DSA_MICROCHIP_KSZ9477_SYSFS + tristate "SYSFS interface for userspace KSZ register access" + depends on NET_DSA_MICROCHIP_KSZ_COMMON + help + Select to enable support for accessing KSZ registers in userspace + through the sysfs interface. diff --git a/drivers/net/dsa/microchip/Makefile b/drivers/net/dsa/microchip/Makefile index 1b0d4ec4c1c8..ab64e470221f 100644 --- a/drivers/net/dsa/microchip/Makefile +++ b/drivers/net/dsa/microchip/Makefile @@ -15,3 +15,4 @@ ksz_switch-objs += ksz_devlink.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C) += ksz9477_i2c.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_SPI) += ksz_spi.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8863_SMI) += ksz8863_smi.o +obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_SYSFS) += ksz9477_sysfs.o diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 59134d117846..581913032510 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -18,6 +18,7 @@ #include "ksz9477_reg.h" #include "ksz_common.h" #include "ksz9477.h" +#include "ksz9477_sysfs.h" static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set) { @@ -1185,6 +1186,10 @@ int ksz9477_switch_init(struct ksz_device *dev) if (ret) return ret; +#ifdef CONFIG_NET_DSA_MICROCHIP_KSZ9477_SYSFS + ksz_sysfs_register(dev); +#endif + return 0; } diff --git a/drivers/net/dsa/microchip/ksz9477_sysfs.c b/drivers/net/dsa/microchip/ksz9477_sysfs.c new file mode 100644 index 000000000000..208a3d27314e --- /dev/null +++ b/drivers/net/dsa/microchip/ksz9477_sysfs.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * KSZ9477 Sysfs Register Access + * + */ + +#include + +#include "ksz_common.h" +#include "ksz9477_reg.h" +#include "ksz9477_sysfs.h" + +#define to_kszdev(d) container_of(d, struct ksz_device, dev) + +struct ksz_device *g_kdev = NULL; + +static ssize_t ksz_read_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 reg, value; + int ret, bit; + char end; + + if (!g_kdev) + return -EINVAL; + + /* Parse remaining parameters, reject extra parameters */ + ret = sscanf(buf, "%d %x%c", &bit, ®, &end); + if (ret < 1) { + dev_err(dev, "%s: Can't parse REG address\n", __func__); + return -EINVAL; + } + if (ret > 1 && end != '\n') { + dev_err(dev, "%s: Extra parameters\n", __func__); + return -EINVAL; + } + + switch(bit) { + case 8: + ret = ksz_read8(g_kdev, reg, (u8*)&value); + break; + case 16: + ret = ksz_read16(g_kdev, reg, (u16*)&value); + break; + case 32: + ret = ksz_read32(g_kdev, reg, &value); + break; + + } + + if (ret < 0) { + dev_err(dev, "%s: read failed.\n", __func__); + return ret; + } + + dev_info(dev, "%d-bit read: reg 0x%04x: 0x%08x.\n", bit, reg, value); + + return count; +} +static DEVICE_ATTR_WO(ksz_read); + +static ssize_t ksz_write_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 reg, value; + int ret, bit; + char end; + + if (!g_kdev) + return -EINVAL; + + /* Parse remaining parameters, reject extra parameters */ + ret = sscanf(buf, "%d %x %x%c", &bit, ®, &value, &end); + if (ret < 1) { + dev_err(dev, "%s: Can't parse register command\n", __func__); + return -EINVAL; + } + if (ret > 1 && end != '\n') { + dev_err(dev, "%s: Extra parameters\n", __func__); + return -EINVAL; + } + + switch(bit) { + case 8: + ret = ksz_write8(g_kdev, reg, (u8)value); + break; + case 16: + ret = ksz_write16(g_kdev, reg, (u16)value); + break; + case 32: + ret = ksz_write32(g_kdev, reg, value); + break; + + } + + if (ret < 0) { + dev_err(dev, "%s: read failed.\n", __func__); + return ret; + } + + dev_info(dev, "%d-bit write: reg 0x%04x value 0x%08x.\n", + bit, reg, value); + + return count; +} +static DEVICE_ATTR_WO(ksz_write); + +int ksz_sysfs_register(struct ksz_device *kdev) +{ + struct device *dev = kdev->dev; + int ret; + + if (!kdev) + return -EINVAL; + + ret = device_create_file(dev, &dev_attr_ksz_read); + if (ret) + dev_err(dev, "Could not create <%s> attribute.\n", "ksz_read"); + + ret = device_create_file(dev, &dev_attr_ksz_write); + if (ret) + dev_err(dev, "Could not create <%s> attribute.\n", "ksz_write"); + + g_kdev = kdev; + + return ret; +} + +void ksz_sysfs_unregister(struct ksz_device *kdev) +{ + struct device *dev = kdev->dev; + + g_kdev = NULL; + + device_remove_file(dev, &dev_attr_ksz_read); + device_remove_file(dev, &dev_attr_ksz_write); +} diff --git a/drivers/net/dsa/microchip/ksz9477_sysfs.h b/drivers/net/dsa/microchip/ksz9477_sysfs.h new file mode 100644 index 000000000000..8e4f88ddca21 --- /dev/null +++ b/drivers/net/dsa/microchip/ksz9477_sysfs.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef KSZ9477_SYSFS_H_ +#define KSZ9477_SYSFS_H_ + +int ksz_sysfs_register(struct ksz_device *kdev); +void ksz_sysfs_unregister(struct ksz_device *kdev); + +#endif /* KSZ9477_SYSFS_H_ */