net: phy: Allow pre-declaration of MDIO devices
authorFlorian Fainelli <f.fainelli@gmail.com>
Sat, 4 Feb 2017 21:02:44 +0000 (13:02 -0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 7 Feb 2017 15:51:46 +0000 (10:51 -0500)
Allow board support code to collect pre-declarations for MDIO devices by
registering them with mdiobus_register_board_info(). SPI and I2C buses
have a similar feature, we were missing this for MDIO devices, but this
is particularly useful for e.g: MDIO-connected switches which need to
provide their port layout (often board-specific) to a MDIO Ethernet
switch driver.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/Makefile
drivers/net/phy/mdio-boardinfo.c [new file with mode: 0644]
drivers/net/phy/mdio-boardinfo.h [new file with mode: 0644]
drivers/net/phy/mdio_bus.c
drivers/net/phy/mdio_device.c
include/linux/mdio.h
include/linux/mod_devicetable.h
include/linux/phy.h

index 356859ac7c18be8e41da7b40aad9a71832a8a1e9..407b0b601ea8264b0ac8bf32a609271d43be995a 100644 (file)
@@ -1,6 +1,7 @@
 # Makefile for Linux PHY drivers and MDIO bus drivers
 
-libphy-y                       := phy.o phy_device.o mdio_bus.o mdio_device.o
+libphy-y                       := phy.o phy_device.o mdio_bus.o mdio_device.o \
+                                  mdio-boardinfo.o
 libphy-$(CONFIG_SWPHY)         += swphy.o
 libphy-$(CONFIG_LED_TRIGGER_PHY)       += phy_led_triggers.o
 
diff --git a/drivers/net/phy/mdio-boardinfo.c b/drivers/net/phy/mdio-boardinfo.c
new file mode 100644 (file)
index 0000000..6b988f7
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * mdio-boardinfo - Collect pre-declarations for MDIO devices
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+
+#include "mdio-boardinfo.h"
+
+static LIST_HEAD(mdio_board_list);
+static DEFINE_MUTEX(mdio_board_lock);
+
+/**
+ * mdiobus_setup_mdiodev_from_board_info - create and setup MDIO devices
+ * from pre-collected board specific MDIO information
+ * @mdiodev: MDIO device pointer
+ * Context: can sleep
+ */
+void mdiobus_setup_mdiodev_from_board_info(struct mii_bus *bus)
+{
+       struct mdio_board_entry *be;
+       struct mdio_device *mdiodev;
+       struct mdio_board_info *bi;
+       int ret;
+
+       mutex_lock(&mdio_board_lock);
+       list_for_each_entry(be, &mdio_board_list, list) {
+               bi = &be->board_info;
+
+               if (strcmp(bus->id, bi->bus_id))
+                       continue;
+
+               mdiodev = mdio_device_create(bus, bi->mdio_addr);
+               if (IS_ERR(mdiodev))
+                       continue;
+
+               strncpy(mdiodev->modalias, bi->modalias,
+                       sizeof(mdiodev->modalias));
+               mdiodev->bus_match = mdio_device_bus_match;
+               mdiodev->dev.platform_data = (void *)bi->platform_data;
+
+               ret = mdio_device_register(mdiodev);
+               if (ret) {
+                       mdio_device_free(mdiodev);
+                       continue;
+               }
+       }
+       mutex_unlock(&mdio_board_lock);
+}
+
+/**
+ * mdio_register_board_info - register MDIO devices for a given board
+ * @info: array of devices descriptors
+ * @n: number of descriptors provided
+ * Context: can sleep
+ *
+ * The board info passed can be marked with __initdata but be pointers
+ * such as platform_data etc. are copied as-is
+ */
+int mdiobus_register_board_info(const struct mdio_board_info *info,
+                               unsigned int n)
+{
+       struct mdio_board_entry *be;
+       unsigned int i;
+
+       be = kcalloc(n, sizeof(*be), GFP_KERNEL);
+       if (!be)
+               return -ENOMEM;
+
+       for (i = 0; i < n; i++, be++, info++) {
+               memcpy(&be->board_info, info, sizeof(*info));
+               mutex_lock(&mdio_board_lock);
+               list_add_tail(&be->list, &mdio_board_list);
+               mutex_unlock(&mdio_board_lock);
+       }
+
+       return 0;
+}
diff --git a/drivers/net/phy/mdio-boardinfo.h b/drivers/net/phy/mdio-boardinfo.h
new file mode 100644 (file)
index 0000000..00f9816
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * mdio-boardinfo.h - board info interface internal to the mdio_bus
+ * component
+ */
+
+#ifndef __MDIO_BOARD_INFO_H
+#define __MDIO_BOARD_INFO_H
+
+#include <linux/phy.h>
+#include <linux/mutex.h>
+
+struct mdio_board_entry {
+       struct list_head        list;
+       struct mdio_board_info  board_info;
+};
+
+void mdiobus_setup_mdiodev_from_board_info(struct mii_bus *bus);
+
+#endif /* __MDIO_BOARD_INFO_H */
index 653d076eafe5068672f177d565eec07798a782e3..fa7d51f14869efa8b94ce161e5bd4cb96b4951d3 100644 (file)
@@ -41,6 +41,8 @@
 #define CREATE_TRACE_POINTS
 #include <trace/events/mdio.h>
 
+#include "mdio-boardinfo.h"
+
 int mdiobus_register_device(struct mdio_device *mdiodev)
 {
        if (mdiodev->bus->mdio_map[mdiodev->addr])
@@ -343,6 +345,8 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner)
                }
        }
 
+       mdiobus_setup_mdiodev_from_board_info(bus);
+
        bus->state = MDIOBUS_REGISTERED;
        pr_info("%s: probed\n", bus->name);
        return 0;
index fc3aaaa36b1dbbb9fc83d5243387d09f339c42d7..e24f28924af8953d288601763849cd7eb71cd827 100644 (file)
@@ -34,6 +34,17 @@ static void mdio_device_release(struct device *dev)
        kfree(to_mdio_device(dev));
 }
 
+int mdio_device_bus_match(struct device *dev, struct device_driver *drv)
+{
+       struct mdio_device *mdiodev = to_mdio_device(dev);
+       struct mdio_driver *mdiodrv = to_mdio_driver(drv);
+
+       if (mdiodrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)
+               return 0;
+
+       return strcmp(mdiodev->modalias, drv->name) == 0;
+}
+
 struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr)
 {
        struct mdio_device *mdiodev;
index 55a80d73cfc18b0bac7820e8d4fe071aee4d07e8..ca08ab16ecdc9b78e36e039a381035cafecc660c 100644 (file)
@@ -10,6 +10,7 @@
 #define __LINUX_MDIO_H__
 
 #include <uapi/linux/mdio.h>
+#include <linux/mod_devicetable.h>
 
 struct mii_bus;
 
@@ -29,6 +30,7 @@ struct mdio_device {
 
        const struct dev_pm_ops *pm_ops;
        struct mii_bus *bus;
+       char modalias[MDIO_NAME_SIZE];
 
        int (*bus_match)(struct device *dev, struct device_driver *drv);
        void (*device_free)(struct mdio_device *mdiodev);
@@ -71,6 +73,7 @@ int mdio_device_register(struct mdio_device *mdiodev);
 void mdio_device_remove(struct mdio_device *mdiodev);
 int mdio_driver_register(struct mdio_driver *drv);
 void mdio_driver_unregister(struct mdio_driver *drv);
+int mdio_device_bus_match(struct device *dev, struct device_driver *drv);
 
 static inline bool mdio_phy_id_is_c45(int phy_id)
 {
index 8a57f0b1242d741ef0ebab33cab628773a3e289f..8850fcaf50dba05d9440b958c5d95cd9330e727a 100644 (file)
@@ -501,6 +501,7 @@ struct platform_device_id {
        kernel_ulong_t driver_data;
 };
 
+#define MDIO_NAME_SIZE         32
 #define MDIO_MODULE_PREFIX     "mdio:"
 
 #define MDIO_ID_FMT "%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d"
index 28ae9eafec1913c311415b16175cd584c7a0aca9..d9bdf53e05141f9625df423533fa7c6cef926e53 100644 (file)
@@ -886,6 +886,25 @@ void mdio_bus_exit(void);
 
 extern struct bus_type mdio_bus_type;
 
+struct mdio_board_info {
+       const char      *bus_id;
+       char            modalias[MDIO_NAME_SIZE];
+       int             mdio_addr;
+       const void      *platform_data;
+};
+
+#if IS_ENABLED(CONFIG_PHYLIB)
+int mdiobus_register_board_info(const struct mdio_board_info *info,
+                               unsigned int n);
+#else
+static inline int mdiobus_register_board_info(const struct mdio_board_info *i,
+                                             unsigned int n)
+{
+       return 0;
+}
+#endif
+
+
 /**
  * module_phy_driver() - Helper macro for registering PHY drivers
  * @__phy_drivers: array of PHY drivers to register