Skip to content

Commit 09bbeda

Browse files
elkablodavem330
authored andcommitted
net: phy: mdio-i2c: support I2C MDIO protocol for RollBall SFP modules
Some multigig SFPs from RollBall and Hilink do not expose functional MDIO access to the internal PHY of the SFP via I2C address 0x56 (although there seems to be read-only clause 22 access on this address). Instead these SFPs PHY can be accessed via I2C via the SFP Enhanced Digital Diagnostic Interface - I2C address 0x51. The SFP_PAGE has to be selected to 3 and the password must be filled with 0xff bytes for this PHY communication to work. This extends the mdio-i2c driver to support this protocol by adding a special parameter to mdio_i2c_alloc function via which this RollBall protocol can be selected. Signed-off-by: Marek Behún <[email protected]> Cc: Andrew Lunn <[email protected]> Cc: Russell King <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent e85b134 commit 09bbeda

File tree

3 files changed

+313
-7
lines changed

3 files changed

+313
-7
lines changed

drivers/net/mdio/mdio-i2c.c

Lines changed: 305 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* MDIO I2C bridge
44
*
55
* Copyright (C) 2015-2016 Russell King
6+
* Copyright (C) 2021 Marek Behun
67
*
78
* Network PHYs can appear on I2C buses when they are part of SFP module.
89
* This driver exposes these PHYs to the networking PHY code, allowing
@@ -12,6 +13,7 @@
1213
#include <linux/i2c.h>
1314
#include <linux/mdio/mdio-i2c.h>
1415
#include <linux/phy.h>
16+
#include <linux/sfp.h>
1517

1618
/*
1719
* I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is
@@ -28,7 +30,7 @@ static unsigned int i2c_mii_phy_addr(int phy_id)
2830
return phy_id + 0x40;
2931
}
3032

31-
static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg)
33+
static int i2c_mii_read_default(struct mii_bus *bus, int phy_id, int reg)
3234
{
3335
struct i2c_adapter *i2c = bus->priv;
3436
struct i2c_msg msgs[2];
@@ -62,7 +64,8 @@ static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg)
6264
return data[0] << 8 | data[1];
6365
}
6466

65-
static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
67+
static int i2c_mii_write_default(struct mii_bus *bus, int phy_id, int reg,
68+
u16 val)
6669
{
6770
struct i2c_adapter *i2c = bus->priv;
6871
struct i2c_msg msg;
@@ -91,9 +94,288 @@ static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
9194
return ret < 0 ? ret : 0;
9295
}
9396

94-
struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c)
97+
/* RollBall SFPs do not access internal PHY via I2C address 0x56, but
98+
* instead via address 0x51, when SFP page is set to 0x03 and password to
99+
* 0xffffffff.
100+
*
101+
* address size contents description
102+
* ------- ---- -------- -----------
103+
* 0x80 1 CMD 0x01/0x02/0x04 for write/read/done
104+
* 0x81 1 DEV Clause 45 device
105+
* 0x82 2 REG Clause 45 register
106+
* 0x84 2 VAL Register value
107+
*/
108+
#define ROLLBALL_PHY_I2C_ADDR 0x51
109+
110+
#define ROLLBALL_PASSWORD (SFP_VSL + 3)
111+
112+
#define ROLLBALL_CMD_ADDR 0x80
113+
#define ROLLBALL_DATA_ADDR 0x81
114+
115+
#define ROLLBALL_CMD_WRITE 0x01
116+
#define ROLLBALL_CMD_READ 0x02
117+
#define ROLLBALL_CMD_DONE 0x04
118+
119+
#define SFP_PAGE_ROLLBALL_MDIO 3
120+
121+
static int __i2c_transfer_err(struct i2c_adapter *i2c, struct i2c_msg *msgs,
122+
int num)
123+
{
124+
int ret;
125+
126+
ret = __i2c_transfer(i2c, msgs, num);
127+
if (ret < 0)
128+
return ret;
129+
else if (ret != num)
130+
return -EIO;
131+
else
132+
return 0;
133+
}
134+
135+
static int __i2c_rollball_get_page(struct i2c_adapter *i2c, int bus_addr,
136+
u8 *page)
137+
{
138+
struct i2c_msg msgs[2];
139+
u8 addr = SFP_PAGE;
140+
141+
msgs[0].addr = bus_addr;
142+
msgs[0].flags = 0;
143+
msgs[0].len = 1;
144+
msgs[0].buf = &addr;
145+
146+
msgs[1].addr = bus_addr;
147+
msgs[1].flags = I2C_M_RD;
148+
msgs[1].len = 1;
149+
msgs[1].buf = page;
150+
151+
return __i2c_transfer_err(i2c, msgs, 2);
152+
}
153+
154+
static int __i2c_rollball_set_page(struct i2c_adapter *i2c, int bus_addr,
155+
u8 page)
156+
{
157+
struct i2c_msg msg;
158+
u8 buf[2];
159+
160+
buf[0] = SFP_PAGE;
161+
buf[1] = page;
162+
163+
msg.addr = bus_addr;
164+
msg.flags = 0;
165+
msg.len = 2;
166+
msg.buf = buf;
167+
168+
return __i2c_transfer_err(i2c, &msg, 1);
169+
}
170+
171+
/* In order to not interfere with other SFP code (which possibly may manipulate
172+
* SFP_PAGE), for every transfer we do this:
173+
* 1. lock the bus
174+
* 2. save content of SFP_PAGE
175+
* 3. set SFP_PAGE to 3
176+
* 4. do the transfer
177+
* 5. restore original SFP_PAGE
178+
* 6. unlock the bus
179+
* Note that one might think that steps 2 to 5 could be theoretically done all
180+
* in one call to i2c_transfer (by constructing msgs array in such a way), but
181+
* unfortunately tests show that this does not work :-( Changed SFP_PAGE does
182+
* not take into account until i2c_transfer() is done.
183+
*/
184+
static int i2c_transfer_rollball(struct i2c_adapter *i2c,
185+
struct i2c_msg *msgs, int num)
186+
{
187+
int ret, main_err = 0;
188+
u8 saved_page;
189+
190+
i2c_lock_bus(i2c, I2C_LOCK_SEGMENT);
191+
192+
/* save original page */
193+
ret = __i2c_rollball_get_page(i2c, msgs->addr, &saved_page);
194+
if (ret)
195+
goto unlock;
196+
197+
/* change to RollBall MDIO page */
198+
ret = __i2c_rollball_set_page(i2c, msgs->addr, SFP_PAGE_ROLLBALL_MDIO);
199+
if (ret)
200+
goto unlock;
201+
202+
/* do the transfer; we try to restore original page if this fails */
203+
ret = __i2c_transfer_err(i2c, msgs, num);
204+
if (ret)
205+
main_err = ret;
206+
207+
/* restore original page */
208+
ret = __i2c_rollball_set_page(i2c, msgs->addr, saved_page);
209+
210+
unlock:
211+
i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT);
212+
213+
return main_err ? : ret;
214+
}
215+
216+
static int i2c_rollball_mii_poll(struct mii_bus *bus, int bus_addr, u8 *buf,
217+
size_t len)
218+
{
219+
struct i2c_adapter *i2c = bus->priv;
220+
struct i2c_msg msgs[2];
221+
u8 cmd_addr, tmp, *res;
222+
int i, ret;
223+
224+
cmd_addr = ROLLBALL_CMD_ADDR;
225+
226+
res = buf ? buf : &tmp;
227+
len = buf ? len : 1;
228+
229+
msgs[0].addr = bus_addr;
230+
msgs[0].flags = 0;
231+
msgs[0].len = 1;
232+
msgs[0].buf = &cmd_addr;
233+
234+
msgs[1].addr = bus_addr;
235+
msgs[1].flags = I2C_M_RD;
236+
msgs[1].len = len;
237+
msgs[1].buf = res;
238+
239+
/* By experiment it takes up to 70 ms to access a register for these
240+
* SFPs. Sleep 20ms between iterations and try 10 times.
241+
*/
242+
i = 10;
243+
do {
244+
msleep(20);
245+
246+
ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs));
247+
if (ret)
248+
return ret;
249+
250+
if (*res == ROLLBALL_CMD_DONE)
251+
return 0;
252+
} while (i-- > 0);
253+
254+
dev_dbg(&bus->dev, "poll timed out\n");
255+
256+
return -ETIMEDOUT;
257+
}
258+
259+
static int i2c_rollball_mii_cmd(struct mii_bus *bus, int bus_addr, u8 cmd,
260+
u8 *data, size_t len)
261+
{
262+
struct i2c_adapter *i2c = bus->priv;
263+
struct i2c_msg msgs[2];
264+
u8 cmdbuf[2];
265+
266+
cmdbuf[0] = ROLLBALL_CMD_ADDR;
267+
cmdbuf[1] = cmd;
268+
269+
msgs[0].addr = bus_addr;
270+
msgs[0].flags = 0;
271+
msgs[0].len = len;
272+
msgs[0].buf = data;
273+
274+
msgs[1].addr = bus_addr;
275+
msgs[1].flags = 0;
276+
msgs[1].len = sizeof(cmdbuf);
277+
msgs[1].buf = cmdbuf;
278+
279+
return i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs));
280+
}
281+
282+
static int i2c_mii_read_rollball(struct mii_bus *bus, int phy_id, int reg)
283+
{
284+
u8 buf[4], res[6];
285+
int bus_addr, ret;
286+
u16 val;
287+
288+
if (!(reg & MII_ADDR_C45))
289+
return -EOPNOTSUPP;
290+
291+
bus_addr = i2c_mii_phy_addr(phy_id);
292+
if (bus_addr != ROLLBALL_PHY_I2C_ADDR)
293+
return 0xffff;
294+
295+
buf[0] = ROLLBALL_DATA_ADDR;
296+
buf[1] = (reg >> 16) & 0x1f;
297+
buf[2] = (reg >> 8) & 0xff;
298+
buf[3] = reg & 0xff;
299+
300+
ret = i2c_rollball_mii_cmd(bus, bus_addr, ROLLBALL_CMD_READ, buf,
301+
sizeof(buf));
302+
if (ret < 0)
303+
return ret;
304+
305+
ret = i2c_rollball_mii_poll(bus, bus_addr, res, sizeof(res));
306+
if (ret == -ETIMEDOUT)
307+
return 0xffff;
308+
else if (ret < 0)
309+
return ret;
310+
311+
val = res[4] << 8 | res[5];
312+
313+
return val;
314+
}
315+
316+
static int i2c_mii_write_rollball(struct mii_bus *bus, int phy_id, int reg,
317+
u16 val)
318+
{
319+
int bus_addr, ret;
320+
u8 buf[6];
321+
322+
if (!(reg & MII_ADDR_C45))
323+
return -EOPNOTSUPP;
324+
325+
bus_addr = i2c_mii_phy_addr(phy_id);
326+
if (bus_addr != ROLLBALL_PHY_I2C_ADDR)
327+
return 0;
328+
329+
buf[0] = ROLLBALL_DATA_ADDR;
330+
buf[1] = (reg >> 16) & 0x1f;
331+
buf[2] = (reg >> 8) & 0xff;
332+
buf[3] = reg & 0xff;
333+
buf[4] = val >> 8;
334+
buf[5] = val & 0xff;
335+
336+
ret = i2c_rollball_mii_cmd(bus, bus_addr, ROLLBALL_CMD_WRITE, buf,
337+
sizeof(buf));
338+
if (ret < 0)
339+
return ret;
340+
341+
ret = i2c_rollball_mii_poll(bus, bus_addr, NULL, 0);
342+
if (ret < 0)
343+
return ret;
344+
345+
return 0;
346+
}
347+
348+
static int i2c_mii_init_rollball(struct i2c_adapter *i2c)
349+
{
350+
struct i2c_msg msg;
351+
u8 pw[5];
352+
int ret;
353+
354+
pw[0] = ROLLBALL_PASSWORD;
355+
pw[1] = 0xff;
356+
pw[2] = 0xff;
357+
pw[3] = 0xff;
358+
pw[4] = 0xff;
359+
360+
msg.addr = ROLLBALL_PHY_I2C_ADDR;
361+
msg.flags = 0;
362+
msg.len = sizeof(pw);
363+
msg.buf = pw;
364+
365+
ret = i2c_transfer(i2c, &msg, 1);
366+
if (ret < 0)
367+
return ret;
368+
else if (ret != 1)
369+
return -EIO;
370+
else
371+
return 0;
372+
}
373+
374+
struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c,
375+
enum mdio_i2c_proto protocol)
95376
{
96377
struct mii_bus *mii;
378+
int ret;
97379

98380
if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
99381
return ERR_PTR(-EINVAL);
@@ -104,10 +386,28 @@ struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c)
104386

105387
snprintf(mii->id, MII_BUS_ID_SIZE, "i2c:%s", dev_name(parent));
106388
mii->parent = parent;
107-
mii->read = i2c_mii_read;
108-
mii->write = i2c_mii_write;
109389
mii->priv = i2c;
110390

391+
switch (protocol) {
392+
case MDIO_I2C_ROLLBALL:
393+
ret = i2c_mii_init_rollball(i2c);
394+
if (ret < 0) {
395+
dev_err(parent,
396+
"Cannot initialize RollBall MDIO I2C protocol: %d\n",
397+
ret);
398+
mdiobus_free(mii);
399+
return ERR_PTR(ret);
400+
}
401+
402+
mii->read = i2c_mii_read_rollball;
403+
mii->write = i2c_mii_write_rollball;
404+
break;
405+
default:
406+
mii->read = i2c_mii_read_default;
407+
mii->write = i2c_mii_write_default;
408+
break;
409+
}
410+
111411
return mii;
112412
}
113413
EXPORT_SYMBOL_GPL(mdio_i2c_alloc);

drivers/net/phy/sfp.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ static int sfp_i2c_mdiobus_create(struct sfp *sfp)
546546
struct mii_bus *i2c_mii;
547547
int ret;
548548

549-
i2c_mii = mdio_i2c_alloc(sfp->dev, sfp->i2c);
549+
i2c_mii = mdio_i2c_alloc(sfp->dev, sfp->i2c, sfp->mdio_protocol);
550550
if (IS_ERR(i2c_mii))
551551
return PTR_ERR(i2c_mii);
552552

@@ -1720,6 +1720,10 @@ static int sfp_sm_probe_for_phy(struct sfp *sfp)
17201720
case MDIO_I2C_C45:
17211721
err = sfp_sm_probe_phy(sfp, true);
17221722
break;
1723+
1724+
case MDIO_I2C_ROLLBALL:
1725+
err = -EOPNOTSUPP;
1726+
break;
17231727
}
17241728

17251729
return err;

include/linux/mdio/mdio-i2c.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ enum mdio_i2c_proto {
1515
MDIO_I2C_NONE,
1616
MDIO_I2C_MARVELL_C22,
1717
MDIO_I2C_C45,
18+
MDIO_I2C_ROLLBALL,
1819
};
1920

20-
struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c);
21+
struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c,
22+
enum mdio_i2c_proto protocol);
2123

2224
#endif

0 commit comments

Comments
 (0)