0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/i2c.h>
0013 #include <linux/mdio/mdio-i2c.h>
0014 #include <linux/phy.h>
0015
0016
0017
0018
0019
0020
0021 static bool i2c_mii_valid_phy_id(int phy_id)
0022 {
0023 return phy_id != 0x10 && phy_id != 0x11;
0024 }
0025
0026 static unsigned int i2c_mii_phy_addr(int phy_id)
0027 {
0028 return phy_id + 0x40;
0029 }
0030
0031 static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg)
0032 {
0033 struct i2c_adapter *i2c = bus->priv;
0034 struct i2c_msg msgs[2];
0035 u8 addr[3], data[2], *p;
0036 int bus_addr, ret;
0037
0038 if (!i2c_mii_valid_phy_id(phy_id))
0039 return 0xffff;
0040
0041 p = addr;
0042 if (reg & MII_ADDR_C45) {
0043 *p++ = 0x20 | ((reg >> 16) & 31);
0044 *p++ = reg >> 8;
0045 }
0046 *p++ = reg;
0047
0048 bus_addr = i2c_mii_phy_addr(phy_id);
0049 msgs[0].addr = bus_addr;
0050 msgs[0].flags = 0;
0051 msgs[0].len = p - addr;
0052 msgs[0].buf = addr;
0053 msgs[1].addr = bus_addr;
0054 msgs[1].flags = I2C_M_RD;
0055 msgs[1].len = sizeof(data);
0056 msgs[1].buf = data;
0057
0058 ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs));
0059 if (ret != ARRAY_SIZE(msgs))
0060 return 0xffff;
0061
0062 return data[0] << 8 | data[1];
0063 }
0064
0065 static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
0066 {
0067 struct i2c_adapter *i2c = bus->priv;
0068 struct i2c_msg msg;
0069 int ret;
0070 u8 data[5], *p;
0071
0072 if (!i2c_mii_valid_phy_id(phy_id))
0073 return 0;
0074
0075 p = data;
0076 if (reg & MII_ADDR_C45) {
0077 *p++ = (reg >> 16) & 31;
0078 *p++ = reg >> 8;
0079 }
0080 *p++ = reg;
0081 *p++ = val >> 8;
0082 *p++ = val;
0083
0084 msg.addr = i2c_mii_phy_addr(phy_id);
0085 msg.flags = 0;
0086 msg.len = p - data;
0087 msg.buf = data;
0088
0089 ret = i2c_transfer(i2c, &msg, 1);
0090
0091 return ret < 0 ? ret : 0;
0092 }
0093
0094 struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c)
0095 {
0096 struct mii_bus *mii;
0097
0098 if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
0099 return ERR_PTR(-EINVAL);
0100
0101 mii = mdiobus_alloc();
0102 if (!mii)
0103 return ERR_PTR(-ENOMEM);
0104
0105 snprintf(mii->id, MII_BUS_ID_SIZE, "i2c:%s", dev_name(parent));
0106 mii->parent = parent;
0107 mii->read = i2c_mii_read;
0108 mii->write = i2c_mii_write;
0109 mii->priv = i2c;
0110
0111 return mii;
0112 }
0113 EXPORT_SYMBOL_GPL(mdio_i2c_alloc);
0114
0115 MODULE_AUTHOR("Russell King");
0116 MODULE_DESCRIPTION("MDIO I2C bridge library");
0117 MODULE_LICENSE("GPL v2");