1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Marvell 88E6xxx System Management Interface (SMI) support 4 * 5 * Copyright (c) 2008 Marvell Semiconductor 6 * 7 * Copyright (c) 2019 Vivien Didelot <vivien.didelot@gmail.com> 8 */ 9 10 #include "chip.h" 11 #include "smi.h" 12 13 /* The switch ADDR[4:1] configuration pins define the chip SMI device address 14 * (ADDR[0] is always zero, thus only even SMI addresses can be strapped). 15 * 16 * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it 17 * is the only device connected to the SMI master. In this mode it responds to 18 * all 32 possible SMI addresses, and thus maps directly the internal devices. 19 * 20 * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing 21 * multiple devices to share the SMI interface. In this mode it responds to only 22 * 2 registers, used to indirectly access the internal SMI devices. 23 */ 24 25 static int mv88e6xxx_smi_direct_read(struct mv88e6xxx_chip *chip, 26 int dev, int reg, u16 *data) 27 { 28 int ret; 29 30 ret = mdiobus_read_nested(chip->bus, dev, reg); 31 if (ret < 0) 32 return ret; 33 34 *data = ret & 0xffff; 35 36 return 0; 37 } 38 39 static int mv88e6xxx_smi_direct_write(struct mv88e6xxx_chip *chip, 40 int dev, int reg, u16 data) 41 { 42 int ret; 43 44 ret = mdiobus_write_nested(chip->bus, dev, reg, data); 45 if (ret < 0) 46 return ret; 47 48 return 0; 49 } 50 51 static int mv88e6xxx_smi_direct_wait(struct mv88e6xxx_chip *chip, 52 int dev, int reg, int bit, int val) 53 { 54 u16 data; 55 int err; 56 int i; 57 58 for (i = 0; i < 16; i++) { 59 err = mv88e6xxx_smi_direct_read(chip, dev, reg, &data); 60 if (err) 61 return err; 62 63 if (!!(data >> bit) == !!val) 64 return 0; 65 } 66 67 return -ETIMEDOUT; 68 } 69 70 static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_direct_ops = { 71 .read = mv88e6xxx_smi_direct_read, 72 .write = mv88e6xxx_smi_direct_write, 73 }; 74 75 /* Offset 0x00: SMI Command Register 76 * Offset 0x01: SMI Data Register 77 */ 78 79 static int mv88e6xxx_smi_indirect_read(struct mv88e6xxx_chip *chip, 80 int dev, int reg, u16 *data) 81 { 82 int err; 83 84 err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, 85 MV88E6XXX_SMI_CMD, 15, 0); 86 if (err) 87 return err; 88 89 err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr, 90 MV88E6XXX_SMI_CMD, 91 MV88E6XXX_SMI_CMD_BUSY | 92 MV88E6XXX_SMI_CMD_MODE_22 | 93 MV88E6XXX_SMI_CMD_OP_22_READ | 94 (dev << 5) | reg); 95 if (err) 96 return err; 97 98 err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, 99 MV88E6XXX_SMI_CMD, 15, 0); 100 if (err) 101 return err; 102 103 return mv88e6xxx_smi_direct_read(chip, chip->sw_addr, 104 MV88E6XXX_SMI_DATA, data); 105 } 106 107 static int mv88e6xxx_smi_indirect_write(struct mv88e6xxx_chip *chip, 108 int dev, int reg, u16 data) 109 { 110 int err; 111 112 err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, 113 MV88E6XXX_SMI_CMD, 15, 0); 114 if (err) 115 return err; 116 117 err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr, 118 MV88E6XXX_SMI_DATA, data); 119 if (err) 120 return err; 121 122 err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr, 123 MV88E6XXX_SMI_CMD, 124 MV88E6XXX_SMI_CMD_BUSY | 125 MV88E6XXX_SMI_CMD_MODE_22 | 126 MV88E6XXX_SMI_CMD_OP_22_WRITE | 127 (dev << 5) | reg); 128 if (err) 129 return err; 130 131 return mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, 132 MV88E6XXX_SMI_CMD, 15, 0); 133 } 134 135 static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_indirect_ops = { 136 .read = mv88e6xxx_smi_indirect_read, 137 .write = mv88e6xxx_smi_indirect_write, 138 }; 139 140 int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, 141 struct mii_bus *bus, int sw_addr) 142 { 143 if (sw_addr == 0) 144 chip->smi_ops = &mv88e6xxx_smi_direct_ops; 145 else if (chip->info->multi_chip) 146 chip->smi_ops = &mv88e6xxx_smi_indirect_ops; 147 else 148 return -EINVAL; 149 150 chip->bus = bus; 151 chip->sw_addr = sw_addr; 152 153 return 0; 154 } 155