brintos

brintos / linux-shallow public Read only

0
0
Text · 4.9 KiB · a990271 Raw
191 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Marvell 88E6xxx System Management Interface (SMI) support4 *5 * Copyright (c) 2008 Marvell Semiconductor6 *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 address14 * (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 it17 * is the only device connected to the SMI master. In this mode it responds to18 * 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, allowing21 * multiple devices to share the SMI interface. In this mode it responds to only22 * 2 registers, used to indirectly access the internal SMI devices.23 *24 * Some chips use a different scheme: Only the ADDR4 pin is used for25 * configuration, and the device responds to 16 of the 32 SMI26 * addresses, allowing two to coexist on the same SMI interface.27 */28 29static int mv88e6xxx_smi_direct_read(struct mv88e6xxx_chip *chip,30				     int dev, int reg, u16 *data)31{32	int ret;33 34	ret = mdiobus_read_nested(chip->bus, dev, reg);35	if (ret < 0)36		return ret;37 38	*data = ret & 0xffff;39 40	return 0;41}42 43static int mv88e6xxx_smi_direct_write(struct mv88e6xxx_chip *chip,44				      int dev, int reg, u16 data)45{46	int ret;47 48	ret = mdiobus_write_nested(chip->bus, dev, reg, data);49	if (ret < 0)50		return ret;51 52	return 0;53}54 55static int mv88e6xxx_smi_direct_wait(struct mv88e6xxx_chip *chip,56				     int dev, int reg, int bit, int val)57{58	const unsigned long timeout = jiffies + msecs_to_jiffies(50);59	u16 data;60	int err;61	int i;62 63	/* Even if the initial poll takes longer than 50ms, always do64	 * at least one more attempt.65	 */66	for (i = 0; time_before(jiffies, timeout) || (i < 2); i++) {67		err = mv88e6xxx_smi_direct_read(chip, dev, reg, &data);68		if (err)69			return err;70 71		if (!!(data & BIT(bit)) == !!val)72			return 0;73 74		if (i < 2)75			cpu_relax();76		else77			usleep_range(1000, 2000);78	}79 80	return -ETIMEDOUT;81}82 83static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_direct_ops = {84	.read = mv88e6xxx_smi_direct_read,85	.write = mv88e6xxx_smi_direct_write,86};87 88static int mv88e6xxx_smi_dual_direct_read(struct mv88e6xxx_chip *chip,89					  int dev, int reg, u16 *data)90{91	return mv88e6xxx_smi_direct_read(chip, chip->sw_addr + dev, reg, data);92}93 94static int mv88e6xxx_smi_dual_direct_write(struct mv88e6xxx_chip *chip,95					   int dev, int reg, u16 data)96{97	return mv88e6xxx_smi_direct_write(chip, chip->sw_addr + dev, reg, data);98}99 100static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_dual_direct_ops = {101	.read = mv88e6xxx_smi_dual_direct_read,102	.write = mv88e6xxx_smi_dual_direct_write,103};104 105/* Offset 0x00: SMI Command Register106 * Offset 0x01: SMI Data Register107 */108 109static int mv88e6xxx_smi_indirect_read(struct mv88e6xxx_chip *chip,110				       int dev, int reg, u16 *data)111{112	int err;113 114	err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,115					 MV88E6XXX_SMI_CMD,116					 MV88E6XXX_SMI_CMD_BUSY |117					 MV88E6XXX_SMI_CMD_MODE_22 |118					 MV88E6XXX_SMI_CMD_OP_22_READ |119					 (dev << 5) | reg);120	if (err)121		return err;122 123	err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,124					MV88E6XXX_SMI_CMD, 15, 0);125	if (err)126		return err;127 128	return mv88e6xxx_smi_direct_read(chip, chip->sw_addr,129					 MV88E6XXX_SMI_DATA, data);130}131 132static int mv88e6xxx_smi_indirect_write(struct mv88e6xxx_chip *chip,133					int dev, int reg, u16 data)134{135	int err;136 137	err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,138					 MV88E6XXX_SMI_DATA, data);139	if (err)140		return err;141 142	err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,143					 MV88E6XXX_SMI_CMD,144					 MV88E6XXX_SMI_CMD_BUSY |145					 MV88E6XXX_SMI_CMD_MODE_22 |146					 MV88E6XXX_SMI_CMD_OP_22_WRITE |147					 (dev << 5) | reg);148	if (err)149		return err;150 151	return mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,152					 MV88E6XXX_SMI_CMD, 15, 0);153}154 155static int mv88e6xxx_smi_indirect_init(struct mv88e6xxx_chip *chip)156{157	/* Ensure that the chip starts out in the ready state. As both158	 * reads and writes always ensure this on return, they can159	 * safely depend on the chip not being busy on entry.160	 */161	return mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,162					 MV88E6XXX_SMI_CMD, 15, 0);163}164 165static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_indirect_ops = {166	.read = mv88e6xxx_smi_indirect_read,167	.write = mv88e6xxx_smi_indirect_write,168	.init = mv88e6xxx_smi_indirect_init,169};170 171int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,172		       struct mii_bus *bus, int sw_addr)173{174	if (chip->info->dual_chip)175		chip->smi_ops = &mv88e6xxx_smi_dual_direct_ops;176	else if (sw_addr == 0)177		chip->smi_ops = &mv88e6xxx_smi_direct_ops;178	else if (chip->info->multi_chip)179		chip->smi_ops = &mv88e6xxx_smi_indirect_ops;180	else181		return -EINVAL;182 183	chip->bus = bus;184	chip->sw_addr = sw_addr;185 186	if (chip->smi_ops->init)187		return chip->smi_ops->init(chip);188 189	return 0;190}191