mv88e6xxx-objs += port.o
 mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o
 mv88e6xxx-objs += serdes.o
+mv88e6xxx-objs += smi.o
 
 #include "port.h"
 #include "ptp.h"
 #include "serdes.h"
+#include "smi.h"
 
 static void assert_reg_lock(struct mv88e6xxx_chip *chip)
 {
        }
 }
 
-/* The switch ADDR[4:1] configuration pins define the chip SMI device address
- * (ADDR[0] is always zero, thus only even SMI addresses can be strapped).
- *
- * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it
- * is the only device connected to the SMI master. In this mode it responds to
- * all 32 possible SMI addresses, and thus maps directly the internal devices.
- *
- * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing
- * multiple devices to share the SMI interface. In this mode it responds to only
- * 2 registers, used to indirectly access the internal SMI devices.
- */
-
-static int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip,
-                             int addr, int reg, u16 *val)
-{
-       if (!chip->smi_ops)
-               return -EOPNOTSUPP;
-
-       return chip->smi_ops->read(chip, addr, reg, val);
-}
-
-static int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip,
-                              int addr, int reg, u16 val)
-{
-       if (!chip->smi_ops)
-               return -EOPNOTSUPP;
-
-       return chip->smi_ops->write(chip, addr, reg, val);
-}
-
-static int mv88e6xxx_smi_single_chip_read(struct mv88e6xxx_chip *chip,
-                                         int addr, int reg, u16 *val)
-{
-       int ret;
-
-       ret = mdiobus_read_nested(chip->bus, addr, reg);
-       if (ret < 0)
-               return ret;
-
-       *val = ret & 0xffff;
-
-       return 0;
-}
-
-static int mv88e6xxx_smi_single_chip_write(struct mv88e6xxx_chip *chip,
-                                          int addr, int reg, u16 val)
-{
-       int ret;
-
-       ret = mdiobus_write_nested(chip->bus, addr, reg, val);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_single_chip_ops = {
-       .read = mv88e6xxx_smi_single_chip_read,
-       .write = mv88e6xxx_smi_single_chip_write,
-};
-
-static int mv88e6xxx_smi_multi_chip_wait(struct mv88e6xxx_chip *chip)
-{
-       int ret;
-       int i;
-
-       for (i = 0; i < 16; i++) {
-               ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_CMD);
-               if (ret < 0)
-                       return ret;
-
-               if ((ret & SMI_CMD_BUSY) == 0)
-                       return 0;
-       }
-
-       return -ETIMEDOUT;
-}
-
-static int mv88e6xxx_smi_multi_chip_read(struct mv88e6xxx_chip *chip,
-                                        int addr, int reg, u16 *val)
-{
-       int ret;
-
-       /* Wait for the bus to become free. */
-       ret = mv88e6xxx_smi_multi_chip_wait(chip);
-       if (ret < 0)
-               return ret;
-
-       /* Transmit the read command. */
-       ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
-                                  SMI_CMD_OP_22_READ | (addr << 5) | reg);
-       if (ret < 0)
-               return ret;
-
-       /* Wait for the read command to complete. */
-       ret = mv88e6xxx_smi_multi_chip_wait(chip);
-       if (ret < 0)
-               return ret;
-
-       /* Read the data. */
-       ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_DATA);
-       if (ret < 0)
-               return ret;
-
-       *val = ret & 0xffff;
-
-       return 0;
-}
-
-static int mv88e6xxx_smi_multi_chip_write(struct mv88e6xxx_chip *chip,
-                                         int addr, int reg, u16 val)
-{
-       int ret;
-
-       /* Wait for the bus to become free. */
-       ret = mv88e6xxx_smi_multi_chip_wait(chip);
-       if (ret < 0)
-               return ret;
-
-       /* Transmit the data to write. */
-       ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_DATA, val);
-       if (ret < 0)
-               return ret;
-
-       /* Transmit the write command. */
-       ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
-                                  SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
-       if (ret < 0)
-               return ret;
-
-       /* Wait for the write command to complete. */
-       ret = mv88e6xxx_smi_multi_chip_wait(chip);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_multi_chip_ops = {
-       .read = mv88e6xxx_smi_multi_chip_read,
-       .write = mv88e6xxx_smi_multi_chip_write,
-};
-
 int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val)
 {
        int err;
        return chip;
 }
 
-static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
-                             struct mii_bus *bus, int sw_addr)
-{
-       if (sw_addr == 0)
-               chip->smi_ops = &mv88e6xxx_smi_single_chip_ops;
-       else if (chip->info->multi_chip)
-               chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops;
-       else
-               return -EINVAL;
-
-       chip->bus = bus;
-       chip->sw_addr = sw_addr;
-
-       return 0;
-}
-
 static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds,
                                                        int port)
 {
 
 #include <linux/timecounter.h>
 #include <net/dsa.h>
 
-#define SMI_CMD                        0x00
-#define SMI_CMD_BUSY           BIT(15)
-#define SMI_CMD_CLAUSE_22      BIT(12)
-#define SMI_CMD_OP_22_WRITE    ((1 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22)
-#define SMI_CMD_OP_22_READ     ((2 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22)
-#define SMI_CMD_OP_45_WRITE_ADDR       ((0 << 10) | SMI_CMD_BUSY)
-#define SMI_CMD_OP_45_WRITE_DATA       ((1 << 10) | SMI_CMD_BUSY)
-#define SMI_CMD_OP_45_READ_DATA                ((2 << 10) | SMI_CMD_BUSY)
-#define SMI_CMD_OP_45_READ_DATA_INC    ((3 << 10) | SMI_CMD_BUSY)
-#define SMI_DATA               0x01
-
 #define MV88E6XXX_N_FID                4096
 
 /* PVT limits for 4-bit port and 5-bit switch */
 
--- /dev/null
+/*
+ * Marvell 88E6xxx System Management Interface (SMI) support
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2019 Vivien Didelot <vivien.didelot@gmail.com>
+ *
+ * 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 "chip.h"
+#include "smi.h"
+
+/* The switch ADDR[4:1] configuration pins define the chip SMI device address
+ * (ADDR[0] is always zero, thus only even SMI addresses can be strapped).
+ *
+ * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it
+ * is the only device connected to the SMI master. In this mode it responds to
+ * all 32 possible SMI addresses, and thus maps directly the internal devices.
+ *
+ * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing
+ * multiple devices to share the SMI interface. In this mode it responds to only
+ * 2 registers, used to indirectly access the internal SMI devices.
+ */
+
+static int mv88e6xxx_smi_direct_read(struct mv88e6xxx_chip *chip,
+                                    int dev, int reg, u16 *data)
+{
+       int ret;
+
+       ret = mdiobus_read_nested(chip->bus, dev, reg);
+       if (ret < 0)
+               return ret;
+
+       *data = ret & 0xffff;
+
+       return 0;
+}
+
+static int mv88e6xxx_smi_direct_write(struct mv88e6xxx_chip *chip,
+                                     int dev, int reg, u16 data)
+{
+       int ret;
+
+       ret = mdiobus_write_nested(chip->bus, dev, reg, data);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int mv88e6xxx_smi_direct_wait(struct mv88e6xxx_chip *chip,
+                                    int dev, int reg, int bit, int val)
+{
+       u16 data;
+       int err;
+       int i;
+
+       for (i = 0; i < 16; i++) {
+               err = mv88e6xxx_smi_direct_read(chip, dev, reg, &data);
+               if (err)
+                       return err;
+
+               if (!!(data >> bit) == !!val)
+                       return 0;
+       }
+
+       return -ETIMEDOUT;
+}
+
+static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_direct_ops = {
+       .read = mv88e6xxx_smi_direct_read,
+       .write = mv88e6xxx_smi_direct_write,
+};
+
+/* Offset 0x00: SMI Command Register
+ * Offset 0x01: SMI Data Register
+ */
+
+static int mv88e6xxx_smi_indirect_read(struct mv88e6xxx_chip *chip,
+                                      int dev, int reg, u16 *data)
+{
+       int err;
+
+       err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
+                                       MV88E6XXX_SMI_CMD, 15, 0);
+       if (err)
+               return err;
+
+       err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,
+                                        MV88E6XXX_SMI_CMD,
+                                        MV88E6XXX_SMI_CMD_BUSY |
+                                        MV88E6XXX_SMI_CMD_MODE_22 |
+                                        MV88E6XXX_SMI_CMD_OP_22_READ |
+                                        (dev << 5) | reg);
+       if (err)
+               return err;
+
+       err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
+                                       MV88E6XXX_SMI_CMD, 15, 0);
+       if (err)
+               return err;
+
+       return mv88e6xxx_smi_direct_read(chip, chip->sw_addr,
+                                        MV88E6XXX_SMI_DATA, data);
+}
+
+static int mv88e6xxx_smi_indirect_write(struct mv88e6xxx_chip *chip,
+                                       int dev, int reg, u16 data)
+{
+       int err;
+
+       err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
+                                       MV88E6XXX_SMI_CMD, 15, 0);
+       if (err)
+               return err;
+
+       err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,
+                                        MV88E6XXX_SMI_DATA, data);
+       if (err)
+               return err;
+
+       err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,
+                                        MV88E6XXX_SMI_CMD,
+                                        MV88E6XXX_SMI_CMD_BUSY |
+                                        MV88E6XXX_SMI_CMD_MODE_22 |
+                                        MV88E6XXX_SMI_CMD_OP_22_WRITE |
+                                        (dev << 5) | reg);
+       if (err)
+               return err;
+
+       return mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
+                                        MV88E6XXX_SMI_CMD, 15, 0);
+}
+
+static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_indirect_ops = {
+       .read = mv88e6xxx_smi_indirect_read,
+       .write = mv88e6xxx_smi_indirect_write,
+};
+
+int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
+                      struct mii_bus *bus, int sw_addr)
+{
+       if (sw_addr == 0)
+               chip->smi_ops = &mv88e6xxx_smi_direct_ops;
+       else if (chip->info->multi_chip)
+               chip->smi_ops = &mv88e6xxx_smi_indirect_ops;
+       else
+               return -EINVAL;
+
+       chip->bus = bus;
+       chip->sw_addr = sw_addr;
+
+       return 0;
+}
 
--- /dev/null
+/*
+ * Marvell 88E6xxx System Management Interface (SMI) support
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2019 Vivien Didelot <vivien.didelot@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef _MV88E6XXX_SMI_H
+#define _MV88E6XXX_SMI_H
+
+#include "chip.h"
+
+/* Offset 0x00: SMI Command Register */
+#define MV88E6XXX_SMI_CMD                      0x00
+#define MV88E6XXX_SMI_CMD_BUSY                 0x8000
+#define MV88E6XXX_SMI_CMD_MODE_MASK            0x1000
+#define MV88E6XXX_SMI_CMD_MODE_45              0x0000
+#define MV88E6XXX_SMI_CMD_MODE_22              0x1000
+#define MV88E6XXX_SMI_CMD_OP_MASK              0x0c00
+#define MV88E6XXX_SMI_CMD_OP_22_WRITE          0x0400
+#define MV88E6XXX_SMI_CMD_OP_22_READ           0x0800
+#define MV88E6XXX_SMI_CMD_OP_45_WRITE_ADDR     0x0000
+#define MV88E6XXX_SMI_CMD_OP_45_WRITE_DATA     0x0400
+#define MV88E6XXX_SMI_CMD_OP_45_READ_DATA      0x0800
+#define MV88E6XXX_SMI_CMD_OP_45_READ_DATA_INC  0x0c00
+#define MV88E6XXX_SMI_CMD_DEV_ADDR_MASK                0x003e
+#define MV88E6XXX_SMI_CMD_REG_ADDR_MASK                0x001f
+
+/* Offset 0x01: SMI Data Register */
+#define MV88E6XXX_SMI_DATA                     0x01
+
+int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
+                      struct mii_bus *bus, int sw_addr);
+
+static inline int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip,
+                                    int dev, int reg, u16 *data)
+{
+       if (chip->smi_ops && chip->smi_ops->read)
+               return chip->smi_ops->read(chip, dev, reg, data);
+
+       return -EOPNOTSUPP;
+}
+
+static inline int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip,
+                                     int dev, int reg, u16 data)
+{
+       if (chip->smi_ops && chip->smi_ops->write)
+               return chip->smi_ops->write(chip, dev, reg, data);
+
+       return -EOPNOTSUPP;
+}
+
+#endif /* _MV88E6XXX_SMI_H */