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