123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * Marvell 88E6xxx System Management Interface (SMI) support
- *
- * Copyright (c) 2008 Marvell Semiconductor
- *
- * Copyright (c) 2019 Vivien Didelot <[email protected]>
- */
- #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.
- *
- * Some chips use a different scheme: Only the ADDR4 pin is used for
- * configuration, and the device responds to 16 of the 32 SMI
- * addresses, allowing two to coexist on the same SMI interface.
- */
- 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)
- {
- const unsigned long timeout = jiffies + msecs_to_jiffies(50);
- u16 data;
- int err;
- int i;
- /* Even if the initial poll takes longer than 50ms, always do
- * at least one more attempt.
- */
- for (i = 0; time_before(jiffies, timeout) || (i < 2); i++) {
- err = mv88e6xxx_smi_direct_read(chip, dev, reg, &data);
- if (err)
- return err;
- if (!!(data & BIT(bit)) == !!val)
- return 0;
- if (i < 2)
- cpu_relax();
- else
- usleep_range(1000, 2000);
- }
- return -ETIMEDOUT;
- }
- static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_direct_ops = {
- .read = mv88e6xxx_smi_direct_read,
- .write = mv88e6xxx_smi_direct_write,
- };
- static int mv88e6xxx_smi_dual_direct_read(struct mv88e6xxx_chip *chip,
- int dev, int reg, u16 *data)
- {
- return mv88e6xxx_smi_direct_read(chip, chip->sw_addr + dev, reg, data);
- }
- static int mv88e6xxx_smi_dual_direct_write(struct mv88e6xxx_chip *chip,
- int dev, int reg, u16 data)
- {
- return mv88e6xxx_smi_direct_write(chip, chip->sw_addr + dev, reg, data);
- }
- static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_dual_direct_ops = {
- .read = mv88e6xxx_smi_dual_direct_read,
- .write = mv88e6xxx_smi_dual_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_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_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 int mv88e6xxx_smi_indirect_init(struct mv88e6xxx_chip *chip)
- {
- /* Ensure that the chip starts out in the ready state. As both
- * reads and writes always ensure this on return, they can
- * safely depend on the chip not being busy on entry.
- */
- 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,
- .init = mv88e6xxx_smi_indirect_init,
- };
- int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
- struct mii_bus *bus, int sw_addr)
- {
- if (chip->info->dual_chip)
- chip->smi_ops = &mv88e6xxx_smi_dual_direct_ops;
- else 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;
- if (chip->smi_ops->init)
- return chip->smi_ops->init(chip);
- return 0;
- }
|