1 From d66d78e0b8b56ac0dd9eb9aacb53a95d67e49710 Mon Sep 17 00:00:00 2001 2 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> 3 Date: Mon, 11 Jan 2021 05:36:26 +0100 4 Subject: [PATCH net-next 11/12] net: phy: mdio-i2c: support I2C MDIO protocol 5 for RollBall SFP modules 6 MIME-Version: 1.0 7 Content-Type: text/plain; charset=UTF-8 8 Content-Transfer-Encoding: 8bit 9 10 Some multigig SFPs from RollBall and Hilink do not expose functional 11 MDIO access to the internal PHY of the SFP via I2C address 0x56 12 (although there seems to be read-only clause 22 access on this address). 13 14 Instead these SFPs PHY can be accessed via I2C via the SFP Enhanced 15 Digital Diagnostic Interface - I2C address 0x51. The SFP_PAGE has to be 16 selected to 3 and the password must be filled with 0xff bytes for this 17 PHY communication to work. 18 19 This extends the mdio-i2c driver to support this protocol by adding a 20 special parameter to mdio_i2c_alloc function via which this RollBall 21 protocol can be selected. 22 23 Signed-off-by: Marek BehĂșn <kabel@kernel.org> 24 Cc: Andrew Lunn <andrew@lunn.ch> 25 Cc: Russell King <rmk+kernel@armlinux.org.uk> 26 --- 27 drivers/net/mdio/mdio-i2c.c | 310 +++++++++++++++++++++++++++++++++- 28 drivers/net/phy/sfp.c | 6 +- 29 include/linux/mdio/mdio-i2c.h | 4 +- 30 3 files changed, 313 insertions(+), 7 deletions(-) 31 32 diff --git a/drivers/net/mdio/mdio-i2c.c b/drivers/net/mdio/mdio-i2c.c 33 index 09200a70b315..bf8bf5e20faf 100644 34 --- a/drivers/net/mdio/mdio-i2c.c 35 +++ b/drivers/net/mdio/mdio-i2c.c 36 @@ -3,6 +3,7 @@ 37 * MDIO I2C bridge 38 * 39 * Copyright (C) 2015-2016 Russell King 40 + * Copyright (C) 2021 Marek Behun 41 * 42 * Network PHYs can appear on I2C buses when they are part of SFP module. 43 * This driver exposes these PHYs to the networking PHY code, allowing 44 @@ -12,6 +13,7 @@ 45 #include <linux/i2c.h> 46 #include <linux/mdio/mdio-i2c.h> 47 #include <linux/phy.h> 48 +#include <linux/sfp.h> 49 50 /* 51 * I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is 52 @@ -28,7 +30,7 @@ static unsigned int i2c_mii_phy_addr(int phy_id) 53 return phy_id + 0x40; 54 } 55 56 -static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg) 57 +static int i2c_mii_read_default(struct mii_bus *bus, int phy_id, int reg) 58 { 59 struct i2c_adapter *i2c = bus->priv; 60 struct i2c_msg msgs[2]; 61 @@ -62,7 +64,8 @@ static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg) 62 return data[0] << 8 | data[1]; 63 } 64 65 -static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val) 66 +static int i2c_mii_write_default(struct mii_bus *bus, int phy_id, int reg, 67 + u16 val) 68 { 69 struct i2c_adapter *i2c = bus->priv; 70 struct i2c_msg msg; 71 @@ -91,9 +94,288 @@ static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val) 72 return ret < 0 ? ret : 0; 73 } 74 75 -struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c) 76 +/* RollBall SFPs do not access internal PHY via I2C address 0x56, but 77 + * instead via address 0x51, when SFP page is set to 0x03 and password to 78 + * 0xffffffff. 79 + * 80 + * address size contents description 81 + * ------- ---- -------- ----------- 82 + * 0x80 1 CMD 0x01/0x02/0x04 for write/read/done 83 + * 0x81 1 DEV Clause 45 device 84 + * 0x82 2 REG Clause 45 register 85 + * 0x84 2 VAL Register value 86 + */ 87 +#define ROLLBALL_PHY_I2C_ADDR 0x51 88 + 89 +#define ROLLBALL_PASSWORD (SFP_VSL + 3) 90 + 91 +#define ROLLBALL_CMD_ADDR 0x80 92 +#define ROLLBALL_DATA_ADDR 0x81 93 + 94 +#define ROLLBALL_CMD_WRITE 0x01 95 +#define ROLLBALL_CMD_READ 0x02 96 +#define ROLLBALL_CMD_DONE 0x04 97 + 98 +#define SFP_PAGE_ROLLBALL_MDIO 3 99 + 100 +static int __i2c_transfer_err(struct i2c_adapter *i2c, struct i2c_msg *msgs, 101 + int num) 102 +{ 103 + int ret; 104 + 105 + ret = __i2c_transfer(i2c, msgs, num); 106 + if (ret < 0) 107 + return ret; 108 + else if (ret != num) 109 + return -EIO; 110 + else 111 + return 0; 112 +} 113 + 114 +static int __i2c_rollball_get_page(struct i2c_adapter *i2c, int bus_addr, 115 + u8 *page) 116 +{ 117 + struct i2c_msg msgs[2]; 118 + u8 addr = SFP_PAGE; 119 + 120 + msgs[0].addr = bus_addr; 121 + msgs[0].flags = 0; 122 + msgs[0].len = 1; 123 + msgs[0].buf = &addr; 124 + 125 + msgs[1].addr = bus_addr; 126 + msgs[1].flags = I2C_M_RD; 127 + msgs[1].len = 1; 128 + msgs[1].buf = page; 129 + 130 + return __i2c_transfer_err(i2c, msgs, 2); 131 +} 132 + 133 +static int __i2c_rollball_set_page(struct i2c_adapter *i2c, int bus_addr, 134 + u8 page) 135 +{ 136 + struct i2c_msg msg; 137 + u8 buf[2]; 138 + 139 + buf[0] = SFP_PAGE; 140 + buf[1] = page; 141 + 142 + msg.addr = bus_addr; 143 + msg.flags = 0; 144 + msg.len = 2; 145 + msg.buf = buf; 146 + 147 + return __i2c_transfer_err(i2c, &msg, 1); 148 +} 149 + 150 +/* In order to not interfere with other SFP code (which possibly may manipulate 151 + * SFP_PAGE), for every transfer we do this: 152 + * 1. lock the bus 153 + * 2. save content of SFP_PAGE 154 + * 3. set SFP_PAGE to 3 155 + * 4. do the transfer 156 + * 5. restore original SFP_PAGE 157 + * 6. unlock the bus 158 + * Note that one might think that steps 2 to 5 could be theoretically done all 159 + * in one call to i2c_transfer (by constructing msgs array in such a way), but 160 + * unfortunately tests show that this does not work :-( Changed SFP_PAGE does 161 + * not take into account until i2c_transfer() is done. 162 + */ 163 +static int i2c_transfer_rollball(struct i2c_adapter *i2c, 164 + struct i2c_msg *msgs, int num) 165 +{ 166 + int ret, main_err = 0; 167 + u8 saved_page; 168 + 169 + i2c_lock_bus(i2c, I2C_LOCK_SEGMENT); 170 + 171 + /* save original page */ 172 + ret = __i2c_rollball_get_page(i2c, msgs->addr, &saved_page); 173 + if (ret) 174 + goto unlock; 175 + 176 + /* change to RollBall MDIO page */ 177 + ret = __i2c_rollball_set_page(i2c, msgs->addr, SFP_PAGE_ROLLBALL_MDIO); 178 + if (ret) 179 + goto unlock; 180 + 181 + /* do the transfer; we try to restore original page if this fails */ 182 + ret = __i2c_transfer_err(i2c, msgs, num); 183 + if (ret) 184 + main_err = ret; 185 + 186 + /* restore original page */ 187 + ret = __i2c_rollball_set_page(i2c, msgs->addr, saved_page); 188 + 189 +unlock: 190 + i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT); 191 + 192 + return main_err ? : ret; 193 +} 194 + 195 +static int i2c_rollball_mii_poll(struct mii_bus *bus, int bus_addr, u8 *buf, 196 + size_t len) 197 +{ 198 + struct i2c_adapter *i2c = bus->priv; 199 + struct i2c_msg msgs[2]; 200 + u8 cmd_addr, tmp, *res; 201 + int i, ret; 202 + 203 + cmd_addr = ROLLBALL_CMD_ADDR; 204 + 205 + res = buf ? buf : &tmp; 206 + len = buf ? len : 1; 207 + 208 + msgs[0].addr = bus_addr; 209 + msgs[0].flags = 0; 210 + msgs[0].len = 1; 211 + msgs[0].buf = &cmd_addr; 212 + 213 + msgs[1].addr = bus_addr; 214 + msgs[1].flags = I2C_M_RD; 215 + msgs[1].len = len; 216 + msgs[1].buf = res; 217 + 218 + /* By experiment it takes up to 70 ms to access a register for these 219 + * SFPs. Sleep 20ms between iterations and try 10 times. 220 + */ 221 + i = 10; 222 + do { 223 + msleep(20); 224 + 225 + ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs)); 226 + if (ret) 227 + return ret; 228 + 229 + if (*res == ROLLBALL_CMD_DONE) 230 + return 0; 231 + } while (i-- > 0); 232 + 233 + dev_dbg(&bus->dev, "poll timed out\n"); 234 + 235 + return -ETIMEDOUT; 236 +} 237 + 238 +static int i2c_rollball_mii_cmd(struct mii_bus *bus, int bus_addr, u8 cmd, 239 + u8 *data, size_t len) 240 +{ 241 + struct i2c_adapter *i2c = bus->priv; 242 + struct i2c_msg msgs[2]; 243 + u8 cmdbuf[2]; 244 + 245 + cmdbuf[0] = ROLLBALL_CMD_ADDR; 246 + cmdbuf[1] = cmd; 247 + 248 + msgs[0].addr = bus_addr; 249 + msgs[0].flags = 0; 250 + msgs[0].len = len; 251 + msgs[0].buf = data; 252 + 253 + msgs[1].addr = bus_addr; 254 + msgs[1].flags = 0; 255 + msgs[1].len = sizeof(cmdbuf); 256 + msgs[1].buf = cmdbuf; 257 + 258 + return i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs)); 259 +} 260 + 261 +static int i2c_mii_read_rollball(struct mii_bus *bus, int phy_id, int reg) 262 +{ 263 + u8 buf[4], res[6]; 264 + int bus_addr, ret; 265 + u16 val; 266 + 267 + if (!(reg & MII_ADDR_C45)) 268 + return -EOPNOTSUPP; 269 + 270 + bus_addr = i2c_mii_phy_addr(phy_id); 271 + if (bus_addr != ROLLBALL_PHY_I2C_ADDR) 272 + return 0xffff; 273 + 274 + buf[0] = ROLLBALL_DATA_ADDR; 275 + buf[1] = (reg >> 16) & 0x1f; 276 + buf[2] = (reg >> 8) & 0xff; 277 + buf[3] = reg & 0xff; 278 + 279 + ret = i2c_rollball_mii_cmd(bus, bus_addr, ROLLBALL_CMD_READ, buf, 280 + sizeof(buf)); 281 + if (ret < 0) 282 + return ret; 283 + 284 + ret = i2c_rollball_mii_poll(bus, bus_addr, res, sizeof(res)); 285 + if (ret == -ETIMEDOUT) 286 + return 0xffff; 287 + else if (ret < 0) 288 + return ret; 289 + 290 + val = res[4] << 8 | res[5]; 291 + 292 + return val; 293 +} 294 + 295 +static int i2c_mii_write_rollball(struct mii_bus *bus, int phy_id, int reg, 296 + u16 val) 297 +{ 298 + int bus_addr, ret; 299 + u8 buf[6]; 300 + 301 + if (!(reg & MII_ADDR_C45)) 302 + return -EOPNOTSUPP; 303 + 304 + bus_addr = i2c_mii_phy_addr(phy_id); 305 + if (bus_addr != ROLLBALL_PHY_I2C_ADDR) 306 + return 0; 307 + 308 + buf[0] = ROLLBALL_DATA_ADDR; 309 + buf[1] = (reg >> 16) & 0x1f; 310 + buf[2] = (reg >> 8) & 0xff; 311 + buf[3] = reg & 0xff; 312 + buf[4] = val >> 8; 313 + buf[5] = val & 0xff; 314 + 315 + ret = i2c_rollball_mii_cmd(bus, bus_addr, ROLLBALL_CMD_WRITE, buf, 316 + sizeof(buf)); 317 + if (ret < 0) 318 + return ret; 319 + 320 + ret = i2c_rollball_mii_poll(bus, bus_addr, NULL, 0); 321 + if (ret < 0) 322 + return ret; 323 + 324 + return 0; 325 +} 326 + 327 +static int i2c_mii_init_rollball(struct i2c_adapter *i2c) 328 +{ 329 + struct i2c_msg msg; 330 + u8 pw[5]; 331 + int ret; 332 + 333 + pw[0] = ROLLBALL_PASSWORD; 334 + pw[1] = 0xff; 335 + pw[2] = 0xff; 336 + pw[3] = 0xff; 337 + pw[4] = 0xff; 338 + 339 + msg.addr = ROLLBALL_PHY_I2C_ADDR; 340 + msg.flags = 0; 341 + msg.len = sizeof(pw); 342 + msg.buf = pw; 343 + 344 + ret = i2c_transfer(i2c, &msg, 1); 345 + if (ret < 0) 346 + return ret; 347 + else if (ret != 1) 348 + return -EIO; 349 + else 350 + return 0; 351 +} 352 + 353 +struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c, 354 + enum mdio_i2c_proto protocol) 355 { 356 struct mii_bus *mii; 357 + int ret; 358 359 if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) 360 return ERR_PTR(-EINVAL); 361 @@ -104,10 +386,28 @@ struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c) 362 363 snprintf(mii->id, MII_BUS_ID_SIZE, "i2c:%s", dev_name(parent)); 364 mii->parent = parent; 365 - mii->read = i2c_mii_read; 366 - mii->write = i2c_mii_write; 367 mii->priv = i2c; 368 369 + switch (protocol) { 370 + case MDIO_I2C_ROLLBALL: 371 + ret = i2c_mii_init_rollball(i2c); 372 + if (ret < 0) { 373 + dev_err(parent, 374 + "Cannot initialize RollBall MDIO I2C protocol: %d\n", 375 + ret); 376 + mdiobus_free(mii); 377 + return ERR_PTR(ret); 378 + } 379 + 380 + mii->read = i2c_mii_read_rollball; 381 + mii->write = i2c_mii_write_rollball; 382 + break; 383 + default: 384 + mii->read = i2c_mii_read_default; 385 + mii->write = i2c_mii_write_default; 386 + break; 387 + } 388 + 389 return mii; 390 } 391 EXPORT_SYMBOL_GPL(mdio_i2c_alloc); 392 diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c 393 index ccd7710685f2..20f48464a06a 100644 394 --- a/drivers/net/phy/sfp.c 395 +++ b/drivers/net/phy/sfp.c 396 @@ -546,7 +546,7 @@ static int sfp_i2c_mdiobus_create(struct sfp *sfp) 397 struct mii_bus *i2c_mii; 398 int ret; 399 400 - i2c_mii = mdio_i2c_alloc(sfp->dev, sfp->i2c); 401 + i2c_mii = mdio_i2c_alloc(sfp->dev, sfp->i2c, sfp->mdio_protocol); 402 if (IS_ERR(i2c_mii)) 403 return PTR_ERR(i2c_mii); 404 405 @@ -1720,6 +1720,10 @@ static int sfp_sm_probe_for_phy(struct sfp *sfp) 406 case MDIO_I2C_C45: 407 err = sfp_sm_probe_phy(sfp, true); 408 break; 409 + 410 + case MDIO_I2C_ROLLBALL: 411 + err = -EOPNOTSUPP; 412 + break; 413 } 414 415 return err; 416 diff --git a/include/linux/mdio/mdio-i2c.h b/include/linux/mdio/mdio-i2c.h 417 index 3bde1a555a49..65b550a6fc32 100644 418 --- a/include/linux/mdio/mdio-i2c.h 419 +++ b/include/linux/mdio/mdio-i2c.h 420 @@ -15,8 +15,10 @@ enum mdio_i2c_proto { 421 MDIO_I2C_NONE, 422 MDIO_I2C_MARVELL_C22, 423 MDIO_I2C_C45, 424 + MDIO_I2C_ROLLBALL, 425 }; 426 427 -struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c); 428 +struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c, 429 + enum mdio_i2c_proto protocol); 430 431 #endif 432 -- 433 2.35.1 434