18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Marvell 88E6xxx System Management Interface (SMI) support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2008 Marvell Semiconductor
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (c) 2019 Vivien Didelot <vivien.didelot@gmail.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "chip.h"
118c2ecf20Sopenharmony_ci#include "smi.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/* The switch ADDR[4:1] configuration pins define the chip SMI device address
148c2ecf20Sopenharmony_ci * (ADDR[0] is always zero, thus only even SMI addresses can be strapped).
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it
178c2ecf20Sopenharmony_ci * is the only device connected to the SMI master. In this mode it responds to
188c2ecf20Sopenharmony_ci * all 32 possible SMI addresses, and thus maps directly the internal devices.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing
218c2ecf20Sopenharmony_ci * multiple devices to share the SMI interface. In this mode it responds to only
228c2ecf20Sopenharmony_ci * 2 registers, used to indirectly access the internal SMI devices.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * Some chips use a different scheme: Only the ADDR4 pin is used for
258c2ecf20Sopenharmony_ci * configuration, and the device responds to 16 of the 32 SMI
268c2ecf20Sopenharmony_ci * addresses, allowing two to coexist on the same SMI interface.
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int mv88e6xxx_smi_direct_read(struct mv88e6xxx_chip *chip,
308c2ecf20Sopenharmony_ci				     int dev, int reg, u16 *data)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	int ret;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	ret = mdiobus_read_nested(chip->bus, dev, reg);
358c2ecf20Sopenharmony_ci	if (ret < 0)
368c2ecf20Sopenharmony_ci		return ret;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	*data = ret & 0xffff;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	return 0;
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int mv88e6xxx_smi_direct_write(struct mv88e6xxx_chip *chip,
448c2ecf20Sopenharmony_ci				      int dev, int reg, u16 data)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	int ret;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	ret = mdiobus_write_nested(chip->bus, dev, reg, data);
498c2ecf20Sopenharmony_ci	if (ret < 0)
508c2ecf20Sopenharmony_ci		return ret;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	return 0;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int mv88e6xxx_smi_direct_wait(struct mv88e6xxx_chip *chip,
568c2ecf20Sopenharmony_ci				     int dev, int reg, int bit, int val)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	u16 data;
598c2ecf20Sopenharmony_ci	int err;
608c2ecf20Sopenharmony_ci	int i;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++) {
638c2ecf20Sopenharmony_ci		err = mv88e6xxx_smi_direct_read(chip, dev, reg, &data);
648c2ecf20Sopenharmony_ci		if (err)
658c2ecf20Sopenharmony_ci			return err;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci		if (!!(data & BIT(bit)) == !!val)
688c2ecf20Sopenharmony_ci			return 0;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic const struct mv88e6xxx_bus_ops mv88e6xxx_smi_direct_ops = {
778c2ecf20Sopenharmony_ci	.read = mv88e6xxx_smi_direct_read,
788c2ecf20Sopenharmony_ci	.write = mv88e6xxx_smi_direct_write,
798c2ecf20Sopenharmony_ci};
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic int mv88e6xxx_smi_dual_direct_read(struct mv88e6xxx_chip *chip,
828c2ecf20Sopenharmony_ci					  int dev, int reg, u16 *data)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	return mv88e6xxx_smi_direct_read(chip, chip->sw_addr + dev, reg, data);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int mv88e6xxx_smi_dual_direct_write(struct mv88e6xxx_chip *chip,
888c2ecf20Sopenharmony_ci					   int dev, int reg, u16 data)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	return mv88e6xxx_smi_direct_write(chip, chip->sw_addr + dev, reg, data);
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic const struct mv88e6xxx_bus_ops mv88e6xxx_smi_dual_direct_ops = {
948c2ecf20Sopenharmony_ci	.read = mv88e6xxx_smi_dual_direct_read,
958c2ecf20Sopenharmony_ci	.write = mv88e6xxx_smi_dual_direct_write,
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/* Offset 0x00: SMI Command Register
998c2ecf20Sopenharmony_ci * Offset 0x01: SMI Data Register
1008c2ecf20Sopenharmony_ci */
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int mv88e6xxx_smi_indirect_read(struct mv88e6xxx_chip *chip,
1038c2ecf20Sopenharmony_ci				       int dev, int reg, u16 *data)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	int err;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
1088c2ecf20Sopenharmony_ci					MV88E6XXX_SMI_CMD, 15, 0);
1098c2ecf20Sopenharmony_ci	if (err)
1108c2ecf20Sopenharmony_ci		return err;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,
1138c2ecf20Sopenharmony_ci					 MV88E6XXX_SMI_CMD,
1148c2ecf20Sopenharmony_ci					 MV88E6XXX_SMI_CMD_BUSY |
1158c2ecf20Sopenharmony_ci					 MV88E6XXX_SMI_CMD_MODE_22 |
1168c2ecf20Sopenharmony_ci					 MV88E6XXX_SMI_CMD_OP_22_READ |
1178c2ecf20Sopenharmony_ci					 (dev << 5) | reg);
1188c2ecf20Sopenharmony_ci	if (err)
1198c2ecf20Sopenharmony_ci		return err;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
1228c2ecf20Sopenharmony_ci					MV88E6XXX_SMI_CMD, 15, 0);
1238c2ecf20Sopenharmony_ci	if (err)
1248c2ecf20Sopenharmony_ci		return err;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return mv88e6xxx_smi_direct_read(chip, chip->sw_addr,
1278c2ecf20Sopenharmony_ci					 MV88E6XXX_SMI_DATA, data);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int mv88e6xxx_smi_indirect_write(struct mv88e6xxx_chip *chip,
1318c2ecf20Sopenharmony_ci					int dev, int reg, u16 data)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	int err;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
1368c2ecf20Sopenharmony_ci					MV88E6XXX_SMI_CMD, 15, 0);
1378c2ecf20Sopenharmony_ci	if (err)
1388c2ecf20Sopenharmony_ci		return err;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,
1418c2ecf20Sopenharmony_ci					 MV88E6XXX_SMI_DATA, data);
1428c2ecf20Sopenharmony_ci	if (err)
1438c2ecf20Sopenharmony_ci		return err;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,
1468c2ecf20Sopenharmony_ci					 MV88E6XXX_SMI_CMD,
1478c2ecf20Sopenharmony_ci					 MV88E6XXX_SMI_CMD_BUSY |
1488c2ecf20Sopenharmony_ci					 MV88E6XXX_SMI_CMD_MODE_22 |
1498c2ecf20Sopenharmony_ci					 MV88E6XXX_SMI_CMD_OP_22_WRITE |
1508c2ecf20Sopenharmony_ci					 (dev << 5) | reg);
1518c2ecf20Sopenharmony_ci	if (err)
1528c2ecf20Sopenharmony_ci		return err;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	return mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
1558c2ecf20Sopenharmony_ci					 MV88E6XXX_SMI_CMD, 15, 0);
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic const struct mv88e6xxx_bus_ops mv88e6xxx_smi_indirect_ops = {
1598c2ecf20Sopenharmony_ci	.read = mv88e6xxx_smi_indirect_read,
1608c2ecf20Sopenharmony_ci	.write = mv88e6xxx_smi_indirect_write,
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ciint mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
1648c2ecf20Sopenharmony_ci		       struct mii_bus *bus, int sw_addr)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	if (chip->info->dual_chip)
1678c2ecf20Sopenharmony_ci		chip->smi_ops = &mv88e6xxx_smi_dual_direct_ops;
1688c2ecf20Sopenharmony_ci	else if (sw_addr == 0)
1698c2ecf20Sopenharmony_ci		chip->smi_ops = &mv88e6xxx_smi_direct_ops;
1708c2ecf20Sopenharmony_ci	else if (chip->info->multi_chip)
1718c2ecf20Sopenharmony_ci		chip->smi_ops = &mv88e6xxx_smi_indirect_ops;
1728c2ecf20Sopenharmony_ci	else
1738c2ecf20Sopenharmony_ci		return -EINVAL;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	chip->bus = bus;
1768c2ecf20Sopenharmony_ci	chip->sw_addr = sw_addr;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci}
180