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(bus: chip->bus, addr: dev, regnum: 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(bus: chip->bus, addr: dev, regnum: reg, val: 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 | const unsigned long timeout = jiffies + msecs_to_jiffies(m: 50); |
59 | u16 data; |
60 | int err; |
61 | int i; |
62 | |
63 | /* Even if the initial poll takes longer than 50ms, always do |
64 | * 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: &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 | else |
77 | usleep_range(min: 1000, max: 2000); |
78 | } |
79 | |
80 | return -ETIMEDOUT; |
81 | } |
82 | |
83 | static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_direct_ops = { |
84 | .read = mv88e6xxx_smi_direct_read, |
85 | .write = mv88e6xxx_smi_direct_write, |
86 | }; |
87 | |
88 | static 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, dev: chip->sw_addr + dev, reg, data); |
92 | } |
93 | |
94 | static 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, dev: chip->sw_addr + dev, reg, data); |
98 | } |
99 | |
100 | static 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 Register |
106 | * Offset 0x01: SMI Data Register |
107 | */ |
108 | |
109 | static 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, dev: 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, dev: chip->sw_addr, |
124 | MV88E6XXX_SMI_CMD, bit: 15, val: 0); |
125 | if (err) |
126 | return err; |
127 | |
128 | return mv88e6xxx_smi_direct_read(chip, dev: chip->sw_addr, |
129 | MV88E6XXX_SMI_DATA, data); |
130 | } |
131 | |
132 | static 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, dev: chip->sw_addr, |
138 | MV88E6XXX_SMI_DATA, data); |
139 | if (err) |
140 | return err; |
141 | |
142 | err = mv88e6xxx_smi_direct_write(chip, dev: 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, dev: chip->sw_addr, |
152 | MV88E6XXX_SMI_CMD, bit: 15, val: 0); |
153 | } |
154 | |
155 | static int mv88e6xxx_smi_indirect_init(struct mv88e6xxx_chip *chip) |
156 | { |
157 | /* Ensure that the chip starts out in the ready state. As both |
158 | * reads and writes always ensure this on return, they can |
159 | * safely depend on the chip not being busy on entry. |
160 | */ |
161 | return mv88e6xxx_smi_direct_wait(chip, dev: chip->sw_addr, |
162 | MV88E6XXX_SMI_CMD, bit: 15, val: 0); |
163 | } |
164 | |
165 | static 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 | |
171 | int 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 | else |
181 | 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 | |