xref: /openbmc/linux/drivers/net/mdio/mdio-i2c.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1a9770eacSAndrew Lunn // SPDX-License-Identifier: GPL-2.0
2a9770eacSAndrew Lunn /*
3a9770eacSAndrew Lunn  * MDIO I2C bridge
4a9770eacSAndrew Lunn  *
5a9770eacSAndrew Lunn  * Copyright (C) 2015-2016 Russell King
609bbedacSMarek Behún  * Copyright (C) 2021 Marek Behun
7a9770eacSAndrew Lunn  *
8a9770eacSAndrew Lunn  * Network PHYs can appear on I2C buses when they are part of SFP module.
9a9770eacSAndrew Lunn  * This driver exposes these PHYs to the networking PHY code, allowing
10a9770eacSAndrew Lunn  * our PHY drivers access to these PHYs, and so allowing configuration
11a9770eacSAndrew Lunn  * of their settings.
12a9770eacSAndrew Lunn  */
13a9770eacSAndrew Lunn #include <linux/i2c.h>
14a9770eacSAndrew Lunn #include <linux/mdio/mdio-i2c.h>
15a9770eacSAndrew Lunn #include <linux/phy.h>
1609bbedacSMarek Behún #include <linux/sfp.h>
17a9770eacSAndrew Lunn 
18a9770eacSAndrew Lunn /*
19a9770eacSAndrew Lunn  * I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is
20a9770eacSAndrew Lunn  * specified to be present in SFP modules.  These correspond with PHY
21a9770eacSAndrew Lunn  * addresses 16 and 17.  Disallow access to these "phy" addresses.
22a9770eacSAndrew Lunn  */
i2c_mii_valid_phy_id(int phy_id)23a9770eacSAndrew Lunn static bool i2c_mii_valid_phy_id(int phy_id)
24a9770eacSAndrew Lunn {
25a9770eacSAndrew Lunn 	return phy_id != 0x10 && phy_id != 0x11;
26a9770eacSAndrew Lunn }
27a9770eacSAndrew Lunn 
i2c_mii_phy_addr(int phy_id)28a9770eacSAndrew Lunn static unsigned int i2c_mii_phy_addr(int phy_id)
29a9770eacSAndrew Lunn {
30a9770eacSAndrew Lunn 	return phy_id + 0x40;
31a9770eacSAndrew Lunn }
32a9770eacSAndrew Lunn 
i2c_mii_read_default_c45(struct mii_bus * bus,int phy_id,int devad,int reg)3387e3bee0SAndrew Lunn static int i2c_mii_read_default_c45(struct mii_bus *bus, int phy_id, int devad,
3487e3bee0SAndrew Lunn 				    int reg)
35a9770eacSAndrew Lunn {
36a9770eacSAndrew Lunn 	struct i2c_adapter *i2c = bus->priv;
37a9770eacSAndrew Lunn 	struct i2c_msg msgs[2];
38a9770eacSAndrew Lunn 	u8 addr[3], data[2], *p;
39a9770eacSAndrew Lunn 	int bus_addr, ret;
40a9770eacSAndrew Lunn 
41a9770eacSAndrew Lunn 	if (!i2c_mii_valid_phy_id(phy_id))
42a9770eacSAndrew Lunn 		return 0xffff;
43a9770eacSAndrew Lunn 
44a9770eacSAndrew Lunn 	p = addr;
4587e3bee0SAndrew Lunn 	if (devad >= 0) {
4687e3bee0SAndrew Lunn 		*p++ = 0x20 | devad;
47a9770eacSAndrew Lunn 		*p++ = reg >> 8;
48a9770eacSAndrew Lunn 	}
49a9770eacSAndrew Lunn 	*p++ = reg;
50a9770eacSAndrew Lunn 
51a9770eacSAndrew Lunn 	bus_addr = i2c_mii_phy_addr(phy_id);
52a9770eacSAndrew Lunn 	msgs[0].addr = bus_addr;
53a9770eacSAndrew Lunn 	msgs[0].flags = 0;
54a9770eacSAndrew Lunn 	msgs[0].len = p - addr;
55a9770eacSAndrew Lunn 	msgs[0].buf = addr;
56a9770eacSAndrew Lunn 	msgs[1].addr = bus_addr;
57a9770eacSAndrew Lunn 	msgs[1].flags = I2C_M_RD;
58a9770eacSAndrew Lunn 	msgs[1].len = sizeof(data);
59a9770eacSAndrew Lunn 	msgs[1].buf = data;
60a9770eacSAndrew Lunn 
61a9770eacSAndrew Lunn 	ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs));
62a9770eacSAndrew Lunn 	if (ret != ARRAY_SIZE(msgs))
63a9770eacSAndrew Lunn 		return 0xffff;
64a9770eacSAndrew Lunn 
65a9770eacSAndrew Lunn 	return data[0] << 8 | data[1];
66a9770eacSAndrew Lunn }
67a9770eacSAndrew Lunn 
i2c_mii_write_default_c45(struct mii_bus * bus,int phy_id,int devad,int reg,u16 val)6887e3bee0SAndrew Lunn static int i2c_mii_write_default_c45(struct mii_bus *bus, int phy_id,
6987e3bee0SAndrew Lunn 				     int devad, int reg, u16 val)
70a9770eacSAndrew Lunn {
71a9770eacSAndrew Lunn 	struct i2c_adapter *i2c = bus->priv;
72a9770eacSAndrew Lunn 	struct i2c_msg msg;
73a9770eacSAndrew Lunn 	int ret;
74a9770eacSAndrew Lunn 	u8 data[5], *p;
75a9770eacSAndrew Lunn 
76a9770eacSAndrew Lunn 	if (!i2c_mii_valid_phy_id(phy_id))
77a9770eacSAndrew Lunn 		return 0;
78a9770eacSAndrew Lunn 
79a9770eacSAndrew Lunn 	p = data;
8087e3bee0SAndrew Lunn 	if (devad >= 0) {
8187e3bee0SAndrew Lunn 		*p++ = devad;
82a9770eacSAndrew Lunn 		*p++ = reg >> 8;
83a9770eacSAndrew Lunn 	}
84a9770eacSAndrew Lunn 	*p++ = reg;
85a9770eacSAndrew Lunn 	*p++ = val >> 8;
86a9770eacSAndrew Lunn 	*p++ = val;
87a9770eacSAndrew Lunn 
88a9770eacSAndrew Lunn 	msg.addr = i2c_mii_phy_addr(phy_id);
89a9770eacSAndrew Lunn 	msg.flags = 0;
90a9770eacSAndrew Lunn 	msg.len = p - data;
91a9770eacSAndrew Lunn 	msg.buf = data;
92a9770eacSAndrew Lunn 
93a9770eacSAndrew Lunn 	ret = i2c_transfer(i2c, &msg, 1);
94a9770eacSAndrew Lunn 
95a9770eacSAndrew Lunn 	return ret < 0 ? ret : 0;
96a9770eacSAndrew Lunn }
97a9770eacSAndrew Lunn 
i2c_mii_read_default_c22(struct mii_bus * bus,int phy_id,int reg)9887e3bee0SAndrew Lunn static int i2c_mii_read_default_c22(struct mii_bus *bus, int phy_id, int reg)
9987e3bee0SAndrew Lunn {
10087e3bee0SAndrew Lunn 	return i2c_mii_read_default_c45(bus, phy_id, -1, reg);
10187e3bee0SAndrew Lunn }
10287e3bee0SAndrew Lunn 
i2c_mii_write_default_c22(struct mii_bus * bus,int phy_id,int reg,u16 val)10387e3bee0SAndrew Lunn static int i2c_mii_write_default_c22(struct mii_bus *bus, int phy_id, int reg,
10487e3bee0SAndrew Lunn 				     u16 val)
10587e3bee0SAndrew Lunn {
10687e3bee0SAndrew Lunn 	return i2c_mii_write_default_c45(bus, phy_id, -1, reg, val);
10787e3bee0SAndrew Lunn }
10887e3bee0SAndrew Lunn 
10909bbedacSMarek Behún /* RollBall SFPs do not access internal PHY via I2C address 0x56, but
11009bbedacSMarek Behún  * instead via address 0x51, when SFP page is set to 0x03 and password to
11109bbedacSMarek Behún  * 0xffffffff.
11209bbedacSMarek Behún  *
11309bbedacSMarek Behún  * address  size  contents  description
11409bbedacSMarek Behún  * -------  ----  --------  -----------
11509bbedacSMarek Behún  * 0x80     1     CMD       0x01/0x02/0x04 for write/read/done
11609bbedacSMarek Behún  * 0x81     1     DEV       Clause 45 device
11709bbedacSMarek Behún  * 0x82     2     REG       Clause 45 register
11809bbedacSMarek Behún  * 0x84     2     VAL       Register value
11909bbedacSMarek Behún  */
12009bbedacSMarek Behún #define ROLLBALL_PHY_I2C_ADDR		0x51
12109bbedacSMarek Behún 
12209bbedacSMarek Behún #define ROLLBALL_PASSWORD		(SFP_VSL + 3)
12309bbedacSMarek Behún 
12409bbedacSMarek Behún #define ROLLBALL_CMD_ADDR		0x80
12509bbedacSMarek Behún #define ROLLBALL_DATA_ADDR		0x81
12609bbedacSMarek Behún 
12709bbedacSMarek Behún #define ROLLBALL_CMD_WRITE		0x01
12809bbedacSMarek Behún #define ROLLBALL_CMD_READ		0x02
12909bbedacSMarek Behún #define ROLLBALL_CMD_DONE		0x04
13009bbedacSMarek Behún 
13109bbedacSMarek Behún #define SFP_PAGE_ROLLBALL_MDIO		3
13209bbedacSMarek Behún 
__i2c_transfer_err(struct i2c_adapter * i2c,struct i2c_msg * msgs,int num)13309bbedacSMarek Behún static int __i2c_transfer_err(struct i2c_adapter *i2c, struct i2c_msg *msgs,
13409bbedacSMarek Behún 			      int num)
13509bbedacSMarek Behún {
13609bbedacSMarek Behún 	int ret;
13709bbedacSMarek Behún 
13809bbedacSMarek Behún 	ret = __i2c_transfer(i2c, msgs, num);
13909bbedacSMarek Behún 	if (ret < 0)
14009bbedacSMarek Behún 		return ret;
14109bbedacSMarek Behún 	else if (ret != num)
14209bbedacSMarek Behún 		return -EIO;
14309bbedacSMarek Behún 	else
14409bbedacSMarek Behún 		return 0;
14509bbedacSMarek Behún }
14609bbedacSMarek Behún 
__i2c_rollball_get_page(struct i2c_adapter * i2c,int bus_addr,u8 * page)14709bbedacSMarek Behún static int __i2c_rollball_get_page(struct i2c_adapter *i2c, int bus_addr,
14809bbedacSMarek Behún 				   u8 *page)
14909bbedacSMarek Behún {
15009bbedacSMarek Behún 	struct i2c_msg msgs[2];
15109bbedacSMarek Behún 	u8 addr = SFP_PAGE;
15209bbedacSMarek Behún 
15309bbedacSMarek Behún 	msgs[0].addr = bus_addr;
15409bbedacSMarek Behún 	msgs[0].flags = 0;
15509bbedacSMarek Behún 	msgs[0].len = 1;
15609bbedacSMarek Behún 	msgs[0].buf = &addr;
15709bbedacSMarek Behún 
15809bbedacSMarek Behún 	msgs[1].addr = bus_addr;
15909bbedacSMarek Behún 	msgs[1].flags = I2C_M_RD;
16009bbedacSMarek Behún 	msgs[1].len = 1;
16109bbedacSMarek Behún 	msgs[1].buf = page;
16209bbedacSMarek Behún 
16309bbedacSMarek Behún 	return __i2c_transfer_err(i2c, msgs, 2);
16409bbedacSMarek Behún }
16509bbedacSMarek Behún 
__i2c_rollball_set_page(struct i2c_adapter * i2c,int bus_addr,u8 page)16609bbedacSMarek Behún static int __i2c_rollball_set_page(struct i2c_adapter *i2c, int bus_addr,
16709bbedacSMarek Behún 				   u8 page)
16809bbedacSMarek Behún {
16909bbedacSMarek Behún 	struct i2c_msg msg;
17009bbedacSMarek Behún 	u8 buf[2];
17109bbedacSMarek Behún 
17209bbedacSMarek Behún 	buf[0] = SFP_PAGE;
17309bbedacSMarek Behún 	buf[1] = page;
17409bbedacSMarek Behún 
17509bbedacSMarek Behún 	msg.addr = bus_addr;
17609bbedacSMarek Behún 	msg.flags = 0;
17709bbedacSMarek Behún 	msg.len = 2;
17809bbedacSMarek Behún 	msg.buf = buf;
17909bbedacSMarek Behún 
18009bbedacSMarek Behún 	return __i2c_transfer_err(i2c, &msg, 1);
18109bbedacSMarek Behún }
18209bbedacSMarek Behún 
18309bbedacSMarek Behún /* In order to not interfere with other SFP code (which possibly may manipulate
18409bbedacSMarek Behún  * SFP_PAGE), for every transfer we do this:
18509bbedacSMarek Behún  *   1. lock the bus
18609bbedacSMarek Behún  *   2. save content of SFP_PAGE
18709bbedacSMarek Behún  *   3. set SFP_PAGE to 3
18809bbedacSMarek Behún  *   4. do the transfer
18909bbedacSMarek Behún  *   5. restore original SFP_PAGE
19009bbedacSMarek Behún  *   6. unlock the bus
19109bbedacSMarek Behún  * Note that one might think that steps 2 to 5 could be theoretically done all
19209bbedacSMarek Behún  * in one call to i2c_transfer (by constructing msgs array in such a way), but
19309bbedacSMarek Behún  * unfortunately tests show that this does not work :-( Changed SFP_PAGE does
19409bbedacSMarek Behún  * not take into account until i2c_transfer() is done.
19509bbedacSMarek Behún  */
i2c_transfer_rollball(struct i2c_adapter * i2c,struct i2c_msg * msgs,int num)19609bbedacSMarek Behún static int i2c_transfer_rollball(struct i2c_adapter *i2c,
19709bbedacSMarek Behún 				 struct i2c_msg *msgs, int num)
19809bbedacSMarek Behún {
19909bbedacSMarek Behún 	int ret, main_err = 0;
20009bbedacSMarek Behún 	u8 saved_page;
20109bbedacSMarek Behún 
20209bbedacSMarek Behún 	i2c_lock_bus(i2c, I2C_LOCK_SEGMENT);
20309bbedacSMarek Behún 
20409bbedacSMarek Behún 	/* save original page */
20509bbedacSMarek Behún 	ret = __i2c_rollball_get_page(i2c, msgs->addr, &saved_page);
20609bbedacSMarek Behún 	if (ret)
20709bbedacSMarek Behún 		goto unlock;
20809bbedacSMarek Behún 
20909bbedacSMarek Behún 	/* change to RollBall MDIO page */
21009bbedacSMarek Behún 	ret = __i2c_rollball_set_page(i2c, msgs->addr, SFP_PAGE_ROLLBALL_MDIO);
21109bbedacSMarek Behún 	if (ret)
21209bbedacSMarek Behún 		goto unlock;
21309bbedacSMarek Behún 
21409bbedacSMarek Behún 	/* do the transfer; we try to restore original page if this fails */
21509bbedacSMarek Behún 	ret = __i2c_transfer_err(i2c, msgs, num);
21609bbedacSMarek Behún 	if (ret)
21709bbedacSMarek Behún 		main_err = ret;
21809bbedacSMarek Behún 
21909bbedacSMarek Behún 	/* restore original page */
22009bbedacSMarek Behún 	ret = __i2c_rollball_set_page(i2c, msgs->addr, saved_page);
22109bbedacSMarek Behún 
22209bbedacSMarek Behún unlock:
22309bbedacSMarek Behún 	i2c_unlock_bus(i2c, I2C_LOCK_SEGMENT);
22409bbedacSMarek Behún 
22509bbedacSMarek Behún 	return main_err ? : ret;
22609bbedacSMarek Behún }
22709bbedacSMarek Behún 
i2c_rollball_mii_poll(struct mii_bus * bus,int bus_addr,u8 * buf,size_t len)22809bbedacSMarek Behún static int i2c_rollball_mii_poll(struct mii_bus *bus, int bus_addr, u8 *buf,
22909bbedacSMarek Behún 				 size_t len)
23009bbedacSMarek Behún {
23109bbedacSMarek Behún 	struct i2c_adapter *i2c = bus->priv;
23209bbedacSMarek Behún 	struct i2c_msg msgs[2];
23309bbedacSMarek Behún 	u8 cmd_addr, tmp, *res;
23409bbedacSMarek Behún 	int i, ret;
23509bbedacSMarek Behún 
23609bbedacSMarek Behún 	cmd_addr = ROLLBALL_CMD_ADDR;
23709bbedacSMarek Behún 
23809bbedacSMarek Behún 	res = buf ? buf : &tmp;
23909bbedacSMarek Behún 	len = buf ? len : 1;
24009bbedacSMarek Behún 
24109bbedacSMarek Behún 	msgs[0].addr = bus_addr;
24209bbedacSMarek Behún 	msgs[0].flags = 0;
24309bbedacSMarek Behún 	msgs[0].len = 1;
24409bbedacSMarek Behún 	msgs[0].buf = &cmd_addr;
24509bbedacSMarek Behún 
24609bbedacSMarek Behún 	msgs[1].addr = bus_addr;
24709bbedacSMarek Behún 	msgs[1].flags = I2C_M_RD;
24809bbedacSMarek Behún 	msgs[1].len = len;
24909bbedacSMarek Behún 	msgs[1].buf = res;
25009bbedacSMarek Behún 
25109bbedacSMarek Behún 	/* By experiment it takes up to 70 ms to access a register for these
25209bbedacSMarek Behún 	 * SFPs. Sleep 20ms between iterations and try 10 times.
25309bbedacSMarek Behún 	 */
25409bbedacSMarek Behún 	i = 10;
25509bbedacSMarek Behún 	do {
25609bbedacSMarek Behún 		msleep(20);
25709bbedacSMarek Behún 
25809bbedacSMarek Behún 		ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs));
25909bbedacSMarek Behún 		if (ret)
26009bbedacSMarek Behún 			return ret;
26109bbedacSMarek Behún 
26209bbedacSMarek Behún 		if (*res == ROLLBALL_CMD_DONE)
26309bbedacSMarek Behún 			return 0;
26409bbedacSMarek Behún 	} while (i-- > 0);
26509bbedacSMarek Behún 
26609bbedacSMarek Behún 	dev_dbg(&bus->dev, "poll timed out\n");
26709bbedacSMarek Behún 
26809bbedacSMarek Behún 	return -ETIMEDOUT;
26909bbedacSMarek Behún }
27009bbedacSMarek Behún 
i2c_rollball_mii_cmd(struct mii_bus * bus,int bus_addr,u8 cmd,u8 * data,size_t len)27109bbedacSMarek Behún static int i2c_rollball_mii_cmd(struct mii_bus *bus, int bus_addr, u8 cmd,
27209bbedacSMarek Behún 				u8 *data, size_t len)
27309bbedacSMarek Behún {
27409bbedacSMarek Behún 	struct i2c_adapter *i2c = bus->priv;
27509bbedacSMarek Behún 	struct i2c_msg msgs[2];
27609bbedacSMarek Behún 	u8 cmdbuf[2];
27709bbedacSMarek Behún 
27809bbedacSMarek Behún 	cmdbuf[0] = ROLLBALL_CMD_ADDR;
27909bbedacSMarek Behún 	cmdbuf[1] = cmd;
28009bbedacSMarek Behún 
28109bbedacSMarek Behún 	msgs[0].addr = bus_addr;
28209bbedacSMarek Behún 	msgs[0].flags = 0;
28309bbedacSMarek Behún 	msgs[0].len = len;
28409bbedacSMarek Behún 	msgs[0].buf = data;
28509bbedacSMarek Behún 
28609bbedacSMarek Behún 	msgs[1].addr = bus_addr;
28709bbedacSMarek Behún 	msgs[1].flags = 0;
28809bbedacSMarek Behún 	msgs[1].len = sizeof(cmdbuf);
28909bbedacSMarek Behún 	msgs[1].buf = cmdbuf;
29009bbedacSMarek Behún 
29109bbedacSMarek Behún 	return i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs));
29209bbedacSMarek Behún }
29309bbedacSMarek Behún 
i2c_mii_read_rollball(struct mii_bus * bus,int phy_id,int devad,int reg)294*b48a1864SRussell King (Oracle) static int i2c_mii_read_rollball(struct mii_bus *bus, int phy_id, int devad,
295*b48a1864SRussell King (Oracle) 				 int reg)
29609bbedacSMarek Behún {
29709bbedacSMarek Behún 	u8 buf[4], res[6];
29809bbedacSMarek Behún 	int bus_addr, ret;
29909bbedacSMarek Behún 	u16 val;
30009bbedacSMarek Behún 
30109bbedacSMarek Behún 	bus_addr = i2c_mii_phy_addr(phy_id);
30209bbedacSMarek Behún 	if (bus_addr != ROLLBALL_PHY_I2C_ADDR)
30309bbedacSMarek Behún 		return 0xffff;
30409bbedacSMarek Behún 
30509bbedacSMarek Behún 	buf[0] = ROLLBALL_DATA_ADDR;
306*b48a1864SRussell King (Oracle) 	buf[1] = devad;
30709bbedacSMarek Behún 	buf[2] = (reg >> 8) & 0xff;
30809bbedacSMarek Behún 	buf[3] = reg & 0xff;
30909bbedacSMarek Behún 
31009bbedacSMarek Behún 	ret = i2c_rollball_mii_cmd(bus, bus_addr, ROLLBALL_CMD_READ, buf,
31109bbedacSMarek Behún 				   sizeof(buf));
31209bbedacSMarek Behún 	if (ret < 0)
31309bbedacSMarek Behún 		return ret;
31409bbedacSMarek Behún 
31509bbedacSMarek Behún 	ret = i2c_rollball_mii_poll(bus, bus_addr, res, sizeof(res));
31609bbedacSMarek Behún 	if (ret == -ETIMEDOUT)
31709bbedacSMarek Behún 		return 0xffff;
31809bbedacSMarek Behún 	else if (ret < 0)
31909bbedacSMarek Behún 		return ret;
32009bbedacSMarek Behún 
32109bbedacSMarek Behún 	val = res[4] << 8 | res[5];
32209bbedacSMarek Behún 
32309bbedacSMarek Behún 	return val;
32409bbedacSMarek Behún }
32509bbedacSMarek Behún 
i2c_mii_write_rollball(struct mii_bus * bus,int phy_id,int devad,int reg,u16 val)326*b48a1864SRussell King (Oracle) static int i2c_mii_write_rollball(struct mii_bus *bus, int phy_id, int devad,
327*b48a1864SRussell King (Oracle) 				  int reg, u16 val)
32809bbedacSMarek Behún {
32909bbedacSMarek Behún 	int bus_addr, ret;
33009bbedacSMarek Behún 	u8 buf[6];
33109bbedacSMarek Behún 
33209bbedacSMarek Behún 	bus_addr = i2c_mii_phy_addr(phy_id);
33309bbedacSMarek Behún 	if (bus_addr != ROLLBALL_PHY_I2C_ADDR)
33409bbedacSMarek Behún 		return 0;
33509bbedacSMarek Behún 
33609bbedacSMarek Behún 	buf[0] = ROLLBALL_DATA_ADDR;
337*b48a1864SRussell King (Oracle) 	buf[1] = devad;
33809bbedacSMarek Behún 	buf[2] = (reg >> 8) & 0xff;
33909bbedacSMarek Behún 	buf[3] = reg & 0xff;
34009bbedacSMarek Behún 	buf[4] = val >> 8;
34109bbedacSMarek Behún 	buf[5] = val & 0xff;
34209bbedacSMarek Behún 
34309bbedacSMarek Behún 	ret = i2c_rollball_mii_cmd(bus, bus_addr, ROLLBALL_CMD_WRITE, buf,
34409bbedacSMarek Behún 				   sizeof(buf));
34509bbedacSMarek Behún 	if (ret < 0)
34609bbedacSMarek Behún 		return ret;
34709bbedacSMarek Behún 
34809bbedacSMarek Behún 	ret = i2c_rollball_mii_poll(bus, bus_addr, NULL, 0);
34909bbedacSMarek Behún 	if (ret < 0)
35009bbedacSMarek Behún 		return ret;
35109bbedacSMarek Behún 
35209bbedacSMarek Behún 	return 0;
35309bbedacSMarek Behún }
35409bbedacSMarek Behún 
i2c_mii_init_rollball(struct i2c_adapter * i2c)35509bbedacSMarek Behún static int i2c_mii_init_rollball(struct i2c_adapter *i2c)
35609bbedacSMarek Behún {
35709bbedacSMarek Behún 	struct i2c_msg msg;
35809bbedacSMarek Behún 	u8 pw[5];
35909bbedacSMarek Behún 	int ret;
36009bbedacSMarek Behún 
36109bbedacSMarek Behún 	pw[0] = ROLLBALL_PASSWORD;
36209bbedacSMarek Behún 	pw[1] = 0xff;
36309bbedacSMarek Behún 	pw[2] = 0xff;
36409bbedacSMarek Behún 	pw[3] = 0xff;
36509bbedacSMarek Behún 	pw[4] = 0xff;
36609bbedacSMarek Behún 
36709bbedacSMarek Behún 	msg.addr = ROLLBALL_PHY_I2C_ADDR;
36809bbedacSMarek Behún 	msg.flags = 0;
36909bbedacSMarek Behún 	msg.len = sizeof(pw);
37009bbedacSMarek Behún 	msg.buf = pw;
37109bbedacSMarek Behún 
37209bbedacSMarek Behún 	ret = i2c_transfer(i2c, &msg, 1);
37309bbedacSMarek Behún 	if (ret < 0)
37409bbedacSMarek Behún 		return ret;
37509bbedacSMarek Behún 	else if (ret != 1)
37609bbedacSMarek Behún 		return -EIO;
37709bbedacSMarek Behún 	else
37809bbedacSMarek Behún 		return 0;
37909bbedacSMarek Behún }
38009bbedacSMarek Behún 
mdio_i2c_alloc(struct device * parent,struct i2c_adapter * i2c,enum mdio_i2c_proto protocol)38109bbedacSMarek Behún struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c,
38209bbedacSMarek Behún 			       enum mdio_i2c_proto protocol)
383a9770eacSAndrew Lunn {
384a9770eacSAndrew Lunn 	struct mii_bus *mii;
38509bbedacSMarek Behún 	int ret;
386a9770eacSAndrew Lunn 
387a9770eacSAndrew Lunn 	if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
388a9770eacSAndrew Lunn 		return ERR_PTR(-EINVAL);
389a9770eacSAndrew Lunn 
390a9770eacSAndrew Lunn 	mii = mdiobus_alloc();
391a9770eacSAndrew Lunn 	if (!mii)
392a9770eacSAndrew Lunn 		return ERR_PTR(-ENOMEM);
393a9770eacSAndrew Lunn 
394a9770eacSAndrew Lunn 	snprintf(mii->id, MII_BUS_ID_SIZE, "i2c:%s", dev_name(parent));
395a9770eacSAndrew Lunn 	mii->parent = parent;
396a9770eacSAndrew Lunn 	mii->priv = i2c;
397a9770eacSAndrew Lunn 
39809bbedacSMarek Behún 	switch (protocol) {
39909bbedacSMarek Behún 	case MDIO_I2C_ROLLBALL:
40009bbedacSMarek Behún 		ret = i2c_mii_init_rollball(i2c);
40109bbedacSMarek Behún 		if (ret < 0) {
40209bbedacSMarek Behún 			dev_err(parent,
40309bbedacSMarek Behún 				"Cannot initialize RollBall MDIO I2C protocol: %d\n",
40409bbedacSMarek Behún 				ret);
40509bbedacSMarek Behún 			mdiobus_free(mii);
40609bbedacSMarek Behún 			return ERR_PTR(ret);
40709bbedacSMarek Behún 		}
40809bbedacSMarek Behún 
409*b48a1864SRussell King (Oracle) 		mii->read_c45 = i2c_mii_read_rollball;
410*b48a1864SRussell King (Oracle) 		mii->write_c45 = i2c_mii_write_rollball;
41109bbedacSMarek Behún 		break;
41209bbedacSMarek Behún 	default:
41387e3bee0SAndrew Lunn 		mii->read = i2c_mii_read_default_c22;
41487e3bee0SAndrew Lunn 		mii->write = i2c_mii_write_default_c22;
41587e3bee0SAndrew Lunn 		mii->read_c45 = i2c_mii_read_default_c45;
41687e3bee0SAndrew Lunn 		mii->write_c45 = i2c_mii_write_default_c45;
41709bbedacSMarek Behún 		break;
41809bbedacSMarek Behún 	}
41909bbedacSMarek Behún 
420a9770eacSAndrew Lunn 	return mii;
421a9770eacSAndrew Lunn }
422a9770eacSAndrew Lunn EXPORT_SYMBOL_GPL(mdio_i2c_alloc);
423a9770eacSAndrew Lunn 
424a9770eacSAndrew Lunn MODULE_AUTHOR("Russell King");
425a9770eacSAndrew Lunn MODULE_DESCRIPTION("MDIO I2C bridge library");
426a9770eacSAndrew Lunn MODULE_LICENSE("GPL v2");
427