+#include <asm/types.h>
+#include <linux/list.h>
+#include <malloc.h>
+#include <net.h>
+
+/* local debug macro */
+#undef MII_DEBUG
+
+#undef debug
+#ifdef MII_DEBUG
+#define debug(fmt, args...) printf(fmt, ##args)
+#else
+#define debug(fmt, args...)
+#endif /* MII_DEBUG */
+
+static struct list_head mii_devs;
+static struct mii_dev *current_mii;
+
+/*
+ * Lookup the mii_dev struct by the registered device name.
+ */
+struct mii_dev *miiphy_get_dev_by_name(const char *devname)
+{
+ struct list_head *entry;
+ struct mii_dev *dev;
+
+ if (!devname) {
+ printf("NULL device name!\n");
+ return NULL;
+ }
+
+ list_for_each(entry, &mii_devs) {
+ dev = list_entry(entry, struct mii_dev, link);
+ if (strcmp(dev->name, devname) == 0)
+ return dev;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************
+ *
+ * Initialize global data. Need to be called before any other miiphy routine.
+ */
+void miiphy_init(void)
+{
+ INIT_LIST_HEAD(&mii_devs);
+ current_mii = NULL;
+}
+
+static int legacy_miiphy_read(struct mii_dev *bus, int addr, int devad, int reg)
+{
+ unsigned short val;
+ int ret;
+ struct legacy_mii_dev *ldev = bus->priv;
+
+ ret = ldev->read(bus->name, addr, reg, &val);
+
+ return ret ? -1 : (int)val;
+}
+
+static int legacy_miiphy_write(struct mii_dev *bus, int addr, int devad,
+ int reg, u16 val)
+{
+ struct legacy_mii_dev *ldev = bus->priv;
+
+ return ldev->write(bus->name, addr, reg, val);
+}
+
+/*****************************************************************************
+ *
+ * Register read and write MII access routines for the device <name>.
+ */
+void miiphy_register(const char *name,
+ int (*read)(const char *devname, unsigned char addr,
+ unsigned char reg, unsigned short *value),
+ int (*write)(const char *devname, unsigned char addr,
+ unsigned char reg, unsigned short value))
+{
+ struct mii_dev *new_dev;
+ struct legacy_mii_dev *ldev;
+
+ BUG_ON(strlen(name) >= MDIO_NAME_LEN);
+
+ /* check if we have unique name */
+ new_dev = miiphy_get_dev_by_name(name);
+ if (new_dev) {
+ printf("miiphy_register: non unique device name '%s'\n", name);
+ return;
+ }
+
+ /* allocate memory */
+ new_dev = mdio_alloc();
+ ldev = malloc(sizeof(*ldev));
+
+ if (new_dev == NULL || ldev == NULL) {
+ printf("miiphy_register: cannot allocate memory for '%s'\n",
+ name);
+ return;
+ }
+
+ /* initalize mii_dev struct fields */
+ new_dev->read = legacy_miiphy_read;
+ new_dev->write = legacy_miiphy_write;
+ strncpy(new_dev->name, name, MDIO_NAME_LEN);
+ new_dev->name[MDIO_NAME_LEN - 1] = 0;
+ ldev->read = read;
+ ldev->write = write;
+ new_dev->priv = ldev;
+
+ debug("miiphy_register: added '%s', read=0x%08lx, write=0x%08lx\n",
+ new_dev->name, ldev->read, ldev->write);
+
+ /* add it to the list */
+ list_add_tail(&new_dev->link, &mii_devs);
+
+ if (!current_mii)
+ current_mii = new_dev;
+}
+
+struct mii_dev *mdio_alloc(void)
+{
+ struct mii_dev *bus;
+
+ bus = malloc(sizeof(*bus));
+ if (!bus)
+ return bus;
+
+ memset(bus, 0, sizeof(*bus));
+
+ /* initalize mii_dev struct fields */
+ INIT_LIST_HEAD(&bus->link);
+
+ return bus;
+}
+
+int mdio_register(struct mii_dev *bus)
+{
+ if (!bus || !bus->name || !bus->read || !bus->write)
+ return -1;
+
+ /* check if we have unique name */
+ if (miiphy_get_dev_by_name(bus->name)) {
+ printf("mdio_register: non unique device name '%s'\n",
+ bus->name);
+ return -1;
+ }
+
+ /* add it to the list */
+ list_add_tail(&bus->link, &mii_devs);
+
+ if (!current_mii)
+ current_mii = bus;
+
+ return 0;
+}
+
+void mdio_list_devices(void)
+{
+ struct list_head *entry;
+
+ list_for_each(entry, &mii_devs) {
+ int i;
+ struct mii_dev *bus = list_entry(entry, struct mii_dev, link);
+
+ printf("%s:\n", bus->name);
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ struct phy_device *phydev = bus->phymap[i];
+
+ if (phydev) {
+ printf("%d - %s", i, phydev->drv->name);
+
+ if (phydev->dev)
+ printf(" <--> %s\n", phydev->dev->name);
+ else
+ printf("\n");
+ }
+ }
+ }
+}
+
+int miiphy_set_current_dev(const char *devname)
+{
+ struct mii_dev *dev;
+
+ dev = miiphy_get_dev_by_name(devname);
+ if (dev) {
+ current_mii = dev;
+ return 0;
+ }
+
+ printf("No such device: %s\n", devname);
+
+ return 1;
+}
+
+struct mii_dev *mdio_get_current_dev(void)
+{
+ return current_mii;
+}
+
+struct phy_device *mdio_phydev_for_ethname(const char *ethname)
+{
+ struct list_head *entry;
+ struct mii_dev *bus;
+
+ list_for_each(entry, &mii_devs) {
+ int i;
+ bus = list_entry(entry, struct mii_dev, link);
+
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ if (!bus->phymap[i] || !bus->phymap[i]->dev)
+ continue;
+
+ if (strcmp(bus->phymap[i]->dev->name, ethname) == 0)
+ return bus->phymap[i];
+ }
+ }
+
+ printf("%s is not a known ethernet\n", ethname);
+ return NULL;
+}
+
+const char *miiphy_get_current_dev(void)
+{
+ if (current_mii)
+ return current_mii->name;
+
+ return NULL;
+}
+
+static struct mii_dev *miiphy_get_active_dev(const char *devname)
+{
+ /* If the current mii is the one we want, return it */
+ if (current_mii)
+ if (strcmp(current_mii->name, devname) == 0)
+ return current_mii;
+
+ /* Otherwise, set the active one to the one we want */
+ if (miiphy_set_current_dev(devname))
+ return NULL;
+ else
+ return current_mii;
+}
+
+/*****************************************************************************
+ *
+ * Read to variable <value> from the PHY attached to device <devname>,
+ * use PHY address <addr> and register <reg>.
+ *
+ * Returns:
+ * 0 on success
+ */
+int miiphy_read(const char *devname, unsigned char addr, unsigned char reg,
+ unsigned short *value)
+{
+ struct mii_dev *bus;
+ int ret;
+
+ bus = miiphy_get_active_dev(devname);
+ if (!bus)
+ return 1;
+
+ ret = bus->read(bus, addr, MDIO_DEVAD_NONE, reg);
+ if (ret < 0)
+ return 1;
+
+ *value = (unsigned short)ret;
+ return 0;
+}
+
+/*****************************************************************************
+ *
+ * Write <value> to the PHY attached to device <devname>,
+ * use PHY address <addr> and register <reg>.
+ *
+ * Returns:
+ * 0 on success
+ */
+int miiphy_write(const char *devname, unsigned char addr, unsigned char reg,
+ unsigned short value)
+{
+ struct mii_dev *bus;
+
+ bus = miiphy_get_active_dev(devname);
+ if (bus)
+ return bus->write(bus, addr, MDIO_DEVAD_NONE, reg, value);
+
+ return 1;
+}
+
+/*****************************************************************************
+ *
+ * Print out list of registered MII capable devices.
+ */
+void miiphy_listdev(void)
+{
+ struct list_head *entry;
+ struct mii_dev *dev;
+
+ puts("MII devices: ");
+ list_for_each(entry, &mii_devs) {
+ dev = list_entry(entry, struct mii_dev, link);
+ printf("'%s' ", dev->name);
+ }
+ puts("\n");
+
+ if (current_mii)
+ printf("Current device: '%s'\n", current_mii->name);
+}