linux/drivers/net/phy/mdio-i2c.c
<<
>>
Prefs
   1/*
   2 * MDIO I2C bridge
   3 *
   4 * Copyright (C) 2015-2016 Russell King
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 * Network PHYs can appear on I2C buses when they are part of SFP module.
  11 * This driver exposes these PHYs to the networking PHY code, allowing
  12 * our PHY drivers access to these PHYs, and so allowing configuration
  13 * of their settings.
  14 */
  15#include <linux/i2c.h>
  16#include <linux/phy.h>
  17
  18#include "mdio-i2c.h"
  19
  20/*
  21 * I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is
  22 * specified to be present in SFP modules.  These correspond with PHY
  23 * addresses 16 and 17.  Disallow access to these "phy" addresses.
  24 */
  25static bool i2c_mii_valid_phy_id(int phy_id)
  26{
  27        return phy_id != 0x10 && phy_id != 0x11;
  28}
  29
  30static unsigned int i2c_mii_phy_addr(int phy_id)
  31{
  32        return phy_id + 0x40;
  33}
  34
  35static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg)
  36{
  37        struct i2c_adapter *i2c = bus->priv;
  38        struct i2c_msg msgs[2];
  39        u8 data[2], dev_addr = reg;
  40        int bus_addr, ret;
  41
  42        if (!i2c_mii_valid_phy_id(phy_id))
  43                return 0xffff;
  44
  45        bus_addr = i2c_mii_phy_addr(phy_id);
  46        msgs[0].addr = bus_addr;
  47        msgs[0].flags = 0;
  48        msgs[0].len = 1;
  49        msgs[0].buf = &dev_addr;
  50        msgs[1].addr = bus_addr;
  51        msgs[1].flags = I2C_M_RD;
  52        msgs[1].len = sizeof(data);
  53        msgs[1].buf = data;
  54
  55        ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs));
  56        if (ret != ARRAY_SIZE(msgs))
  57                return 0xffff;
  58
  59        return data[0] << 8 | data[1];
  60}
  61
  62static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
  63{
  64        struct i2c_adapter *i2c = bus->priv;
  65        struct i2c_msg msg;
  66        int ret;
  67        u8 data[3];
  68
  69        if (!i2c_mii_valid_phy_id(phy_id))
  70                return 0;
  71
  72        data[0] = reg;
  73        data[1] = val >> 8;
  74        data[2] = val;
  75
  76        msg.addr = i2c_mii_phy_addr(phy_id);
  77        msg.flags = 0;
  78        msg.len = 3;
  79        msg.buf = data;
  80
  81        ret = i2c_transfer(i2c, &msg, 1);
  82
  83        return ret < 0 ? ret : 0;
  84}
  85
  86struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c)
  87{
  88        struct mii_bus *mii;
  89
  90        if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
  91                return ERR_PTR(-EINVAL);
  92
  93        mii = mdiobus_alloc();
  94        if (!mii)
  95                return ERR_PTR(-ENOMEM);
  96
  97        snprintf(mii->id, MII_BUS_ID_SIZE, "i2c:%s", dev_name(parent));
  98        mii->parent = parent;
  99        mii->read = i2c_mii_read;
 100        mii->write = i2c_mii_write;
 101        mii->priv = i2c;
 102
 103        return mii;
 104}
 105EXPORT_SYMBOL_GPL(mdio_i2c_alloc);
 106
 107MODULE_AUTHOR("Russell King");
 108MODULE_DESCRIPTION("MDIO I2C bridge library");
 109MODULE_LICENSE("GPL v2");
 110