aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarek Behún <kabel@kernel.org>2021-05-31 22:02:45 +0200
committerMarek Behún <kabel@kernel.org>2021-11-10 19:09:46 +0100
commitc87c6f80c5fd6c1175c9c513933f95fdd0ad9959 (patch)
tree83f3b96d9e1df5b61ba4299ac25ddec8de8f686f
parent1202b721efac79bc264c27ad5879561647a28472 (diff)
net: mdiobus: add support to access PHY registers via debugfs [*not for mainline*]net-queue-work
This adds config option CONFIG_MDIO_BUS_DEBUGFS which, when enabled, adds support to communicate with the devices connected to the MDIO via debugfs. For every MDIO bus this creates directory /sys/kernel/debug/mdio_bus/MDIO_BUS_NAME with files "addr", "reg" and "val". User can write device address to the "addr" file and register number to the "reg" file, and then can read the value of the register from the "val" file, or can write new value by writing to the "val" file. This is useful when debugging various PHYs or switches. Signed-off-by: Marek Behún <kabel@kernel.org>
-rw-r--r--drivers/net/mdio/Kconfig8
-rw-r--r--drivers/net/phy/Makefile2
-rw-r--r--drivers/net/phy/mdio_bus.c32
-rw-r--r--drivers/net/phy/mdio_debugfs.c95
-rw-r--r--drivers/net/phy/mdio_debugfs.h36
-rw-r--r--include/linux/phy.h5
6 files changed, 173 insertions, 5 deletions
diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig
index 6da1fcb25847e..205569faa0e2e 100644
--- a/drivers/net/mdio/Kconfig
+++ b/drivers/net/mdio/Kconfig
@@ -43,6 +43,14 @@ config ACPI_MDIO
if MDIO_BUS
+config MDIO_BUS_DEBUGFS
+ bool "MDIO bus debugfs support"
+ depends on DEBUG_FS
+ help
+ This adds support to communicate via the MDIO bus via files in
+ debugfs. Note that using this on a PHY device that is being handled by
+ a driver can break the state of the PHY.
+
config MDIO_DEVRES
tristate
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 48733fb58dcb3..56c4a5f244273 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -5,6 +5,8 @@ libphy-y := phy.o phy-c45.o phy-core.o phy_device.o \
linkmode.o
mdio-bus-y += mdio_bus.o mdio_device.o
+obj-$(CONFIG_MDIO_BUS_DEBUGFS) += mdio_debugfs.o
+
ifdef CONFIG_MDIO_DEVICE
obj-y += mdio-boardinfo.o
endif
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 4638d73759438..1ed404bc32795 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -39,6 +39,7 @@
#include <trace/events/mdio.h>
#include "mdio-boardinfo.h"
+#include "mdio_debugfs.h"
static int mdiobus_register_gpiod(struct mdio_device *mdiodev)
{
@@ -588,6 +589,12 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner)
}
}
+ err = mdiobus_register_debugfs(bus);
+ if (err) {
+ dev_err(&bus->dev, "mii_bus %s couldn't create debugfs entries\n", bus->id);
+ goto error;
+ }
+
mdiobus_setup_mdiodev_from_board_info(bus, mdiobus_create_device);
bus->state = MDIOBUS_REGISTERED;
@@ -622,6 +629,8 @@ void mdiobus_unregister(struct mii_bus *bus)
return;
bus->state = MDIOBUS_UNREGISTERED;
+ mdiobus_unregister_debugfs(bus);
+
for (i = 0; i < PHY_MAX_ADDR; i++) {
mdiodev = bus->mdio_map[i];
if (!mdiodev)
@@ -1051,11 +1060,23 @@ int __init mdio_bus_init(void)
int ret;
ret = class_register(&mdio_bus_class);
- if (!ret) {
- ret = bus_register(&mdio_bus_type);
- if (ret)
- class_unregister(&mdio_bus_class);
- }
+ if (ret)
+ return ret;
+
+ ret = bus_register(&mdio_bus_type);
+ if (ret)
+ goto err_class;
+
+ ret = mdiobus_debugfs_init();
+ if (ret)
+ goto err_bus;
+
+ return 0;
+
+err_bus:
+ bus_unregister(&mdio_bus_type);
+err_class:
+ class_unregister(&mdio_bus_class);
return ret;
}
@@ -1064,6 +1085,7 @@ EXPORT_SYMBOL_GPL(mdio_bus_init);
#if IS_ENABLED(CONFIG_PHYLIB)
void mdio_bus_exit(void)
{
+ mdiobus_debugfs_exit();
class_unregister(&mdio_bus_class);
bus_unregister(&mdio_bus_type);
}
diff --git a/drivers/net/phy/mdio_debugfs.c b/drivers/net/phy/mdio_debugfs.c
new file mode 100644
index 0000000000000..ad795a25b0483
--- /dev/null
+++ b/drivers/net/phy/mdio_debugfs.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* MDIO Bus debugfs support
+ *
+ * Copyright (c) 2020 Marek Behun <marek.behun@nic.cz>
+ */
+
+#include <linux/debugfs.h>
+#include <linux/phy.h>
+
+static int val_get(void *data, u64 *ptr)
+{
+ struct mii_bus *bus = data;
+ int val;
+
+ val = mdiobus_read(bus, bus->debug_addr, bus->debug_reg);
+ if (val < 0)
+ return val;
+
+ *ptr = val;
+
+ return 0;
+}
+
+static int val_set(void *data, u64 val)
+{
+ struct mii_bus *bus = data;
+ int res;
+
+ if (val > 0xffff)
+ return -EINVAL;
+
+ res = mdiobus_write(bus, bus->debug_addr, bus->debug_reg, val);
+ if (res < 0)
+ return res;
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(val_fops, val_get, val_set, "0x%04llx\n");
+
+static struct dentry *mdiobus_dentry;
+
+int mdiobus_register_debugfs(struct mii_bus *bus)
+{
+ struct dentry *dir, *entry;
+
+ dir = debugfs_create_dir(dev_name(&bus->dev), mdiobus_dentry);
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
+
+ debugfs_create_u32("addr", 0600, dir, &bus->debug_addr);
+ debugfs_create_u32("reg", 0600, dir, &bus->debug_reg);
+
+ entry = debugfs_create_file_unsafe("val", 0600, dir, bus, &val_fops);
+ if (IS_ERR(entry))
+ goto fail;
+
+ return 0;
+
+fail:
+ debugfs_remove_recursive(dir);
+ return PTR_ERR(entry);
+}
+
+void mdiobus_unregister_debugfs(struct mii_bus *bus)
+{
+ struct dentry *dir;
+
+ dir = debugfs_lookup(dev_name(&bus->dev), mdiobus_dentry);
+ if (!dir)
+ return;
+
+ debugfs_remove_recursive(dir);
+}
+
+int mdiobus_debugfs_init(void)
+{
+ struct dentry *dentry;
+
+ dentry = debugfs_create_dir("mdio_bus", NULL);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ mdiobus_dentry = dentry;
+
+ return 0;
+}
+
+void mdiobus_debugfs_exit(void)
+{
+ if (mdiobus_dentry) {
+ debugfs_remove(mdiobus_dentry);
+ mdiobus_dentry = NULL;
+ }
+}
diff --git a/drivers/net/phy/mdio_debugfs.h b/drivers/net/phy/mdio_debugfs.h
new file mode 100644
index 0000000000000..5faba96fc586a
--- /dev/null
+++ b/drivers/net/phy/mdio_debugfs.h
@@ -0,0 +1,36 @@
+#ifndef MDIOBUS_DEBUGFS_H
+#define MDIOBUS_DEBUGFS_H
+
+#include <linux/kernel.h>
+#include <linux/phy.h>
+
+#if IS_ENABLED(CONFIG_MDIO_BUS_DEBUGFS)
+
+int mdiobus_register_debugfs(struct mii_bus *bus);
+void mdiobus_unregister_debugfs(struct mii_bus *bus);
+int mdiobus_debugfs_init(void);
+void mdiobus_debugfs_exit(void);
+
+#else
+
+static inline int mdiobus_register_debugfs(struct mii_bus *bus)
+{
+ return 0;
+}
+
+static inline void mdiobus_unregister_debugfs(struct mii_bus *bus)
+{
+}
+
+static inline int mdiobus_debugfs_init(void)
+{
+ return 0;
+}
+
+static inline void mdiobus_debugfs_exit(void)
+{
+}
+
+#endif
+
+#endif
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 987892fb81cc7..23107089b41f8 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -423,6 +423,11 @@ struct mii_bus {
/** @shared: shared state across different PHYs */
struct phy_package_shared *shared[PHY_MAX_ADDR];
+
+#if IS_ENABLED(CONFIG_MDIO_BUS_DEBUGFS)
+ /* address and regnum for debugfs */
+ u32 debug_addr, debug_reg;
+#endif
};
#define to_mii_bus(d) container_of(d, struct mii_bus, dev)