162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * MDIO I2C bridge 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015-2016 Russell King 662306a36Sopenharmony_ci * Copyright (C) 2021 Marek Behun 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Network PHYs can appear on I2C buses when they are part of SFP module. 962306a36Sopenharmony_ci * This driver exposes these PHYs to the networking PHY code, allowing 1062306a36Sopenharmony_ci * our PHY drivers access to these PHYs, and so allowing configuration 1162306a36Sopenharmony_ci * of their settings. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci#include <linux/i2c.h> 1462306a36Sopenharmony_ci#include <linux/mdio/mdio-i2c.h> 1562306a36Sopenharmony_ci#include <linux/phy.h> 1662306a36Sopenharmony_ci#include <linux/sfp.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is 2062306a36Sopenharmony_ci * specified to be present in SFP modules. These correspond with PHY 2162306a36Sopenharmony_ci * addresses 16 and 17. Disallow access to these "phy" addresses. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_cistatic bool i2c_mii_valid_phy_id(int phy_id) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci return phy_id != 0x10 && phy_id != 0x11; 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic unsigned int i2c_mii_phy_addr(int phy_id) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci return phy_id + 0x40; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int i2c_mii_read_default_c45(struct mii_bus *bus, int phy_id, int devad, 3462306a36Sopenharmony_ci int reg) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct i2c_adapter *i2c = bus->priv; 3762306a36Sopenharmony_ci struct i2c_msg msgs[2]; 3862306a36Sopenharmony_ci u8 addr[3], data[2], *p; 3962306a36Sopenharmony_ci int bus_addr, ret; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (!i2c_mii_valid_phy_id(phy_id)) 4262306a36Sopenharmony_ci return 0xffff; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci p = addr; 4562306a36Sopenharmony_ci if (devad >= 0) { 4662306a36Sopenharmony_ci *p++ = 0x20 | devad; 4762306a36Sopenharmony_ci *p++ = reg >> 8; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci *p++ = reg; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci bus_addr = i2c_mii_phy_addr(phy_id); 5262306a36Sopenharmony_ci msgs[0].addr = bus_addr; 5362306a36Sopenharmony_ci msgs[0].flags = 0; 5462306a36Sopenharmony_ci msgs[0].len = p - addr; 5562306a36Sopenharmony_ci msgs[0].buf = addr; 5662306a36Sopenharmony_ci msgs[1].addr = bus_addr; 5762306a36Sopenharmony_ci msgs[1].flags = I2C_M_RD; 5862306a36Sopenharmony_ci msgs[1].len = sizeof(data); 5962306a36Sopenharmony_ci msgs[1].buf = data; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs)); 6262306a36Sopenharmony_ci if (ret != ARRAY_SIZE(msgs)) 6362306a36Sopenharmony_ci return 0xffff; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return data[0] << 8 | data[1]; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int i2c_mii_write_default_c45(struct mii_bus *bus, int phy_id, 6962306a36Sopenharmony_ci int devad, int reg, u16 val) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct i2c_adapter *i2c = bus->priv; 7262306a36Sopenharmony_ci struct i2c_msg msg; 7362306a36Sopenharmony_ci int ret; 7462306a36Sopenharmony_ci u8 data[5], *p; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (!i2c_mii_valid_phy_id(phy_id)) 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci p = data; 8062306a36Sopenharmony_ci if (devad >= 0) { 8162306a36Sopenharmony_ci *p++ = devad; 8262306a36Sopenharmony_ci *p++ = reg >> 8; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci *p++ = reg; 8562306a36Sopenharmony_ci *p++ = val >> 8; 8662306a36Sopenharmony_ci *p++ = val; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci msg.addr = i2c_mii_phy_addr(phy_id); 8962306a36Sopenharmony_ci msg.flags = 0; 9062306a36Sopenharmony_ci msg.len = p - data; 9162306a36Sopenharmony_ci msg.buf = data; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci ret = i2c_transfer(i2c, &msg, 1); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return ret < 0 ? ret : 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int i2c_mii_read_default_c22(struct mii_bus *bus, int phy_id, int reg) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci return i2c_mii_read_default_c45(bus, phy_id, -1, reg); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int i2c_mii_write_default_c22(struct mii_bus *bus, int phy_id, int reg, 10462306a36Sopenharmony_ci u16 val) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci return i2c_mii_write_default_c45(bus, phy_id, -1, reg, val); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* RollBall SFPs do not access internal PHY via I2C address 0x56, but 11062306a36Sopenharmony_ci * instead via address 0x51, when SFP page is set to 0x03 and password to 11162306a36Sopenharmony_ci * 0xffffffff. 11262306a36Sopenharmony_ci * 11362306a36Sopenharmony_ci * address size contents description 11462306a36Sopenharmony_ci * ------- ---- -------- ----------- 11562306a36Sopenharmony_ci * 0x80 1 CMD 0x01/0x02/0x04 for write/read/done 11662306a36Sopenharmony_ci * 0x81 1 DEV Clause 45 device 11762306a36Sopenharmony_ci * 0x82 2 REG Clause 45 register 11862306a36Sopenharmony_ci * 0x84 2 VAL Register value 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ci#define ROLLBALL_PHY_I2C_ADDR 0x51 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define ROLLBALL_PASSWORD (SFP_VSL + 3) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#define ROLLBALL_CMD_ADDR 0x80 12562306a36Sopenharmony_ci#define ROLLBALL_DATA_ADDR 0x81 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define ROLLBALL_CMD_WRITE 0x01 12862306a36Sopenharmony_ci#define ROLLBALL_CMD_READ 0x02 12962306a36Sopenharmony_ci#define ROLLBALL_CMD_DONE 0x04 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#define SFP_PAGE_ROLLBALL_MDIO 3 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int __i2c_transfer_err(struct i2c_adapter *i2c, struct i2c_msg *msgs, 13462306a36Sopenharmony_ci int num) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci int ret; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci ret = __i2c_transfer(i2c, msgs, num); 13962306a36Sopenharmony_ci if (ret < 0) 14062306a36Sopenharmony_ci return ret; 14162306a36Sopenharmony_ci else if (ret != num) 14262306a36Sopenharmony_ci return -EIO; 14362306a36Sopenharmony_ci else 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int __i2c_rollball_get_page(struct i2c_adapter *i2c, int bus_addr, 14862306a36Sopenharmony_ci u8 *page) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct i2c_msg msgs[2]; 15162306a36Sopenharmony_ci u8 addr = SFP_PAGE; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci msgs[0].addr = bus_addr; 15462306a36Sopenharmony_ci msgs[0].flags = 0; 15562306a36Sopenharmony_ci msgs[0].len = 1; 15662306a36Sopenharmony_ci msgs[0].buf = &addr; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci msgs[1].addr = bus_addr; 15962306a36Sopenharmony_ci msgs[1].flags = I2C_M_RD; 16062306a36Sopenharmony_ci msgs[1].len = 1; 16162306a36Sopenharmony_ci msgs[1].buf = page; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return __i2c_transfer_err(i2c, msgs, 2); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int __i2c_rollball_set_page(struct i2c_adapter *i2c, int bus_addr, 16762306a36Sopenharmony_ci u8 page) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct i2c_msg msg; 17062306a36Sopenharmony_ci u8 buf[2]; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci buf[0] = SFP_PAGE; 17362306a36Sopenharmony_ci buf[1] = page; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci msg.addr = bus_addr; 17662306a36Sopenharmony_ci msg.flags = 0; 17762306a36Sopenharmony_ci msg.len = 2; 17862306a36Sopenharmony_ci msg.buf = buf; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return __i2c_transfer_err(i2c, &msg, 1); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* In order to not interfere with other SFP code (which possibly may manipulate 18462306a36Sopenharmony_ci * SFP_PAGE), for every transfer we do this: 18562306a36Sopenharmony_ci * 1. lock the bus 18662306a36Sopenharmony_ci * 2. save content of SFP_PAGE 18762306a36Sopenharmony_ci * 3. set SFP_PAGE to 3 18862306a36Sopenharmony_ci * 4. do the transfer 18962306a36Sopenharmony_ci * 5. restore original SFP_PAGE 19062306a36Sopenharmony_ci * 6. unlock the bus 19162306a36Sopenharmony_ci * Note that one might think that steps 2 to 5 could be theoretically done all 19262306a36Sopenharmony_ci * in one call to i2c_transfer (by constructing msgs array in such a way), but 19362306a36Sopenharmony_ci * unfortunately tests show that this does not work :-( Changed SFP_PAGE does 19462306a36Sopenharmony_ci * not take into account until i2c_transfer() is done. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_cistatic int i2c_transfer_rollball(struct i2c_adapter *i2c, 19762306a36Sopenharmony_ci struct i2c_msg *msgs, int num) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci int ret, main_err = 0; 20062306a36Sopenharmony_ci u8 saved_page; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci i2c_lock_bus(i2c, I2C_LOCK_SEGMENT); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* save original page */ 20562306a36Sopenharmony_ci ret = __i2c_rollball_get_page(i2c, msgs->addr, &saved_page); 20662306a36Sopenharmony_ci if (ret) 20762306a36Sopenharmony_ci goto unlock; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* change to RollBall MDIO page */ 21062306a36Sopenharmony_ci ret = __i2c_rollball_set_page(i2c, msgs->addr, SFP_PAGE_ROLLBALL_MDIO); 21162306a36Sopenharmony_ci if (ret) 21262306a36Sopenharmony_ci goto unlock; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* do the transfer; we try to restore original page if this fails */ 21562306a36Sopenharmony_ci ret = __i2c_transfer_err(i2c, msgs, num); 21662306a36Sopenharmony_ci if (ret) 21762306a36Sopenharmony_ci main_err = ret; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* restore original page */ 22062306a36Sopenharmony_ci ret = __i2c_rollball_set_page(i2c, msgs->addr, saved_page); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ciunlock: 22362306a36Sopenharmony_ci i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return main_err ? : ret; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int i2c_rollball_mii_poll(struct mii_bus *bus, int bus_addr, u8 *buf, 22962306a36Sopenharmony_ci size_t len) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct i2c_adapter *i2c = bus->priv; 23262306a36Sopenharmony_ci struct i2c_msg msgs[2]; 23362306a36Sopenharmony_ci u8 cmd_addr, tmp, *res; 23462306a36Sopenharmony_ci int i, ret; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci cmd_addr = ROLLBALL_CMD_ADDR; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci res = buf ? buf : &tmp; 23962306a36Sopenharmony_ci len = buf ? len : 1; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci msgs[0].addr = bus_addr; 24262306a36Sopenharmony_ci msgs[0].flags = 0; 24362306a36Sopenharmony_ci msgs[0].len = 1; 24462306a36Sopenharmony_ci msgs[0].buf = &cmd_addr; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci msgs[1].addr = bus_addr; 24762306a36Sopenharmony_ci msgs[1].flags = I2C_M_RD; 24862306a36Sopenharmony_ci msgs[1].len = len; 24962306a36Sopenharmony_ci msgs[1].buf = res; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* By experiment it takes up to 70 ms to access a register for these 25262306a36Sopenharmony_ci * SFPs. Sleep 20ms between iterations and try 10 times. 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_ci i = 10; 25562306a36Sopenharmony_ci do { 25662306a36Sopenharmony_ci msleep(20); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs)); 25962306a36Sopenharmony_ci if (ret) 26062306a36Sopenharmony_ci return ret; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (*res == ROLLBALL_CMD_DONE) 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci } while (i-- > 0); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci dev_dbg(&bus->dev, "poll timed out\n"); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return -ETIMEDOUT; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int i2c_rollball_mii_cmd(struct mii_bus *bus, int bus_addr, u8 cmd, 27262306a36Sopenharmony_ci u8 *data, size_t len) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct i2c_adapter *i2c = bus->priv; 27562306a36Sopenharmony_ci struct i2c_msg msgs[2]; 27662306a36Sopenharmony_ci u8 cmdbuf[2]; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci cmdbuf[0] = ROLLBALL_CMD_ADDR; 27962306a36Sopenharmony_ci cmdbuf[1] = cmd; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci msgs[0].addr = bus_addr; 28262306a36Sopenharmony_ci msgs[0].flags = 0; 28362306a36Sopenharmony_ci msgs[0].len = len; 28462306a36Sopenharmony_ci msgs[0].buf = data; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci msgs[1].addr = bus_addr; 28762306a36Sopenharmony_ci msgs[1].flags = 0; 28862306a36Sopenharmony_ci msgs[1].len = sizeof(cmdbuf); 28962306a36Sopenharmony_ci msgs[1].buf = cmdbuf; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs)); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int i2c_mii_read_rollball(struct mii_bus *bus, int phy_id, int devad, 29562306a36Sopenharmony_ci int reg) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci u8 buf[4], res[6]; 29862306a36Sopenharmony_ci int bus_addr, ret; 29962306a36Sopenharmony_ci u16 val; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci bus_addr = i2c_mii_phy_addr(phy_id); 30262306a36Sopenharmony_ci if (bus_addr != ROLLBALL_PHY_I2C_ADDR) 30362306a36Sopenharmony_ci return 0xffff; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci buf[0] = ROLLBALL_DATA_ADDR; 30662306a36Sopenharmony_ci buf[1] = devad; 30762306a36Sopenharmony_ci buf[2] = (reg >> 8) & 0xff; 30862306a36Sopenharmony_ci buf[3] = reg & 0xff; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci ret = i2c_rollball_mii_cmd(bus, bus_addr, ROLLBALL_CMD_READ, buf, 31162306a36Sopenharmony_ci sizeof(buf)); 31262306a36Sopenharmony_ci if (ret < 0) 31362306a36Sopenharmony_ci return ret; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci ret = i2c_rollball_mii_poll(bus, bus_addr, res, sizeof(res)); 31662306a36Sopenharmony_ci if (ret == -ETIMEDOUT) 31762306a36Sopenharmony_ci return 0xffff; 31862306a36Sopenharmony_ci else if (ret < 0) 31962306a36Sopenharmony_ci return ret; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci val = res[4] << 8 | res[5]; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return val; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int i2c_mii_write_rollball(struct mii_bus *bus, int phy_id, int devad, 32762306a36Sopenharmony_ci int reg, u16 val) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci int bus_addr, ret; 33062306a36Sopenharmony_ci u8 buf[6]; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci bus_addr = i2c_mii_phy_addr(phy_id); 33362306a36Sopenharmony_ci if (bus_addr != ROLLBALL_PHY_I2C_ADDR) 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci buf[0] = ROLLBALL_DATA_ADDR; 33762306a36Sopenharmony_ci buf[1] = devad; 33862306a36Sopenharmony_ci buf[2] = (reg >> 8) & 0xff; 33962306a36Sopenharmony_ci buf[3] = reg & 0xff; 34062306a36Sopenharmony_ci buf[4] = val >> 8; 34162306a36Sopenharmony_ci buf[5] = val & 0xff; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci ret = i2c_rollball_mii_cmd(bus, bus_addr, ROLLBALL_CMD_WRITE, buf, 34462306a36Sopenharmony_ci sizeof(buf)); 34562306a36Sopenharmony_ci if (ret < 0) 34662306a36Sopenharmony_ci return ret; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci ret = i2c_rollball_mii_poll(bus, bus_addr, NULL, 0); 34962306a36Sopenharmony_ci if (ret < 0) 35062306a36Sopenharmony_ci return ret; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int i2c_mii_init_rollball(struct i2c_adapter *i2c) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct i2c_msg msg; 35862306a36Sopenharmony_ci u8 pw[5]; 35962306a36Sopenharmony_ci int ret; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci pw[0] = ROLLBALL_PASSWORD; 36262306a36Sopenharmony_ci pw[1] = 0xff; 36362306a36Sopenharmony_ci pw[2] = 0xff; 36462306a36Sopenharmony_ci pw[3] = 0xff; 36562306a36Sopenharmony_ci pw[4] = 0xff; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci msg.addr = ROLLBALL_PHY_I2C_ADDR; 36862306a36Sopenharmony_ci msg.flags = 0; 36962306a36Sopenharmony_ci msg.len = sizeof(pw); 37062306a36Sopenharmony_ci msg.buf = pw; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci ret = i2c_transfer(i2c, &msg, 1); 37362306a36Sopenharmony_ci if (ret < 0) 37462306a36Sopenharmony_ci return ret; 37562306a36Sopenharmony_ci else if (ret != 1) 37662306a36Sopenharmony_ci return -EIO; 37762306a36Sopenharmony_ci else 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistruct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c, 38262306a36Sopenharmony_ci enum mdio_i2c_proto protocol) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct mii_bus *mii; 38562306a36Sopenharmony_ci int ret; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) 38862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci mii = mdiobus_alloc(); 39162306a36Sopenharmony_ci if (!mii) 39262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci snprintf(mii->id, MII_BUS_ID_SIZE, "i2c:%s", dev_name(parent)); 39562306a36Sopenharmony_ci mii->parent = parent; 39662306a36Sopenharmony_ci mii->priv = i2c; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci switch (protocol) { 39962306a36Sopenharmony_ci case MDIO_I2C_ROLLBALL: 40062306a36Sopenharmony_ci ret = i2c_mii_init_rollball(i2c); 40162306a36Sopenharmony_ci if (ret < 0) { 40262306a36Sopenharmony_ci dev_err(parent, 40362306a36Sopenharmony_ci "Cannot initialize RollBall MDIO I2C protocol: %d\n", 40462306a36Sopenharmony_ci ret); 40562306a36Sopenharmony_ci mdiobus_free(mii); 40662306a36Sopenharmony_ci return ERR_PTR(ret); 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci mii->read_c45 = i2c_mii_read_rollball; 41062306a36Sopenharmony_ci mii->write_c45 = i2c_mii_write_rollball; 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci default: 41362306a36Sopenharmony_ci mii->read = i2c_mii_read_default_c22; 41462306a36Sopenharmony_ci mii->write = i2c_mii_write_default_c22; 41562306a36Sopenharmony_ci mii->read_c45 = i2c_mii_read_default_c45; 41662306a36Sopenharmony_ci mii->write_c45 = i2c_mii_write_default_c45; 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return mii; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mdio_i2c_alloc); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ciMODULE_AUTHOR("Russell King"); 42562306a36Sopenharmony_ciMODULE_DESCRIPTION("MDIO I2C bridge library"); 42662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 427