xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/smi.c (revision 2169e6da)
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  * Some chips use a different scheme: Only the ADDR4 pin is used for
25  * configuration, and the device responds to 16 of the 32 SMI
26  * addresses, allowing two to coexist on the same SMI interface.
27  */
28 
29 static 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 
43 static 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 
55 static int mv88e6xxx_smi_direct_wait(struct mv88e6xxx_chip *chip,
56 				     int dev, int reg, int bit, int val)
57 {
58 	u16 data;
59 	int err;
60 	int i;
61 
62 	for (i = 0; i < 16; i++) {
63 		err = mv88e6xxx_smi_direct_read(chip, dev, reg, &data);
64 		if (err)
65 			return err;
66 
67 		if (!!(data >> bit) == !!val)
68 			return 0;
69 	}
70 
71 	return -ETIMEDOUT;
72 }
73 
74 static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_direct_ops = {
75 	.read = mv88e6xxx_smi_direct_read,
76 	.write = mv88e6xxx_smi_direct_write,
77 };
78 
79 static int mv88e6xxx_smi_dual_direct_read(struct mv88e6xxx_chip *chip,
80 					  int dev, int reg, u16 *data)
81 {
82 	return mv88e6xxx_smi_direct_read(chip, chip->sw_addr + dev, reg, data);
83 }
84 
85 static int mv88e6xxx_smi_dual_direct_write(struct mv88e6xxx_chip *chip,
86 					   int dev, int reg, u16 data)
87 {
88 	return mv88e6xxx_smi_direct_write(chip, chip->sw_addr + dev, reg, data);
89 }
90 
91 static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_dual_direct_ops = {
92 	.read = mv88e6xxx_smi_dual_direct_read,
93 	.write = mv88e6xxx_smi_dual_direct_write,
94 };
95 
96 /* Offset 0x00: SMI Command Register
97  * Offset 0x01: SMI Data Register
98  */
99 
100 static int mv88e6xxx_smi_indirect_read(struct mv88e6xxx_chip *chip,
101 				       int dev, int reg, u16 *data)
102 {
103 	int err;
104 
105 	err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
106 					MV88E6XXX_SMI_CMD, 15, 0);
107 	if (err)
108 		return err;
109 
110 	err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,
111 					 MV88E6XXX_SMI_CMD,
112 					 MV88E6XXX_SMI_CMD_BUSY |
113 					 MV88E6XXX_SMI_CMD_MODE_22 |
114 					 MV88E6XXX_SMI_CMD_OP_22_READ |
115 					 (dev << 5) | reg);
116 	if (err)
117 		return err;
118 
119 	err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
120 					MV88E6XXX_SMI_CMD, 15, 0);
121 	if (err)
122 		return err;
123 
124 	return mv88e6xxx_smi_direct_read(chip, chip->sw_addr,
125 					 MV88E6XXX_SMI_DATA, data);
126 }
127 
128 static int mv88e6xxx_smi_indirect_write(struct mv88e6xxx_chip *chip,
129 					int dev, int reg, u16 data)
130 {
131 	int err;
132 
133 	err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
134 					MV88E6XXX_SMI_CMD, 15, 0);
135 	if (err)
136 		return err;
137 
138 	err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,
139 					 MV88E6XXX_SMI_DATA, data);
140 	if (err)
141 		return err;
142 
143 	err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,
144 					 MV88E6XXX_SMI_CMD,
145 					 MV88E6XXX_SMI_CMD_BUSY |
146 					 MV88E6XXX_SMI_CMD_MODE_22 |
147 					 MV88E6XXX_SMI_CMD_OP_22_WRITE |
148 					 (dev << 5) | reg);
149 	if (err)
150 		return err;
151 
152 	return mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
153 					 MV88E6XXX_SMI_CMD, 15, 0);
154 }
155 
156 static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_indirect_ops = {
157 	.read = mv88e6xxx_smi_indirect_read,
158 	.write = mv88e6xxx_smi_indirect_write,
159 };
160 
161 int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
162 		       struct mii_bus *bus, int sw_addr)
163 {
164 	if (chip->info->dual_chip)
165 		chip->smi_ops = &mv88e6xxx_smi_dual_direct_ops;
166 	else if (sw_addr == 0)
167 		chip->smi_ops = &mv88e6xxx_smi_direct_ops;
168 	else if (chip->info->multi_chip)
169 		chip->smi_ops = &mv88e6xxx_smi_indirect_ops;
170 	else
171 		return -EINVAL;
172 
173 	chip->bus = bus;
174 	chip->sw_addr = sw_addr;
175 
176 	return 0;
177 }
178