*/
#include <config.h>
#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
#include <micrel.h>
#include <phy.h>
+DECLARE_GLOBAL_DATA_PTR;
+
static struct phy_driver KSZ804_driver = {
.name = "Micrel KSZ804",
.uid = 0x221510,
.shutdown = &genphy_shutdown,
};
+#define MII_KSZPHY_OMSO 0x16
+#define KSZPHY_OMSO_B_CAST_OFF (1 << 9)
+
+static int ksz_genconfig_bcastoff(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZPHY_OMSO);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_KSZPHY_OMSO,
+ ret | KSZPHY_OMSO_B_CAST_OFF);
+ if (ret < 0)
+ return ret;
+
+ return genphy_config(phydev);
+}
+
+static struct phy_driver KSZ8031_driver = {
+ .name = "Micrel KSZ8021/KSZ8031",
+ .uid = 0x221550,
+ .mask = 0xfffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &ksz_genconfig_bcastoff,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+/**
+ * KSZ8051
+ */
+#define MII_KSZ8051_PHY_OMSO 0x16
+#define MII_KSZ8051_PHY_OMSO_NAND_TREE_ON (1 << 5)
+
+static int ksz8051_config(struct phy_device *phydev)
+{
+ unsigned val;
+
+ /* Disable NAND-tree */
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ8051_PHY_OMSO);
+ val &= ~MII_KSZ8051_PHY_OMSO_NAND_TREE_ON;
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_KSZ8051_PHY_OMSO, val);
+
+ return genphy_config(phydev);
+}
+
+static struct phy_driver KSZ8051_driver = {
+ .name = "Micrel KSZ8051",
+ .uid = 0x221550,
+ .mask = 0xfffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &ksz8051_config,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver KSZ8081_driver = {
+ .name = "Micrel KSZ8081",
+ .uid = 0x221560,
+ .mask = 0xfffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &ksz_genconfig_bcastoff,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
/**
* KSZ8895
*/
return 0;
}
+/* Common OF config bits for KSZ9021 and KSZ9031 */
+#if defined(CONFIG_PHY_MICREL_KSZ9021) || defined(CONFIG_PHY_MICREL_KSZ9031)
+#ifdef CONFIG_DM_ETH
+struct ksz90x1_reg_field {
+ const char *name;
+ const u8 size; /* Size of the bitfield, in bits */
+ const u8 off; /* Offset from bit 0 */
+ const u8 dflt; /* Default value */
+};
+
+struct ksz90x1_ofcfg {
+ const u16 reg;
+ const u16 devad;
+ const struct ksz90x1_reg_field *grp;
+ const u16 grpsz;
+};
+
+static const struct ksz90x1_reg_field ksz90x1_rxd_grp[] = {
+ { "rxd0-skew-ps", 4, 0, 0x7 }, { "rxd1-skew-ps", 4, 4, 0x7 },
+ { "rxd2-skew-ps", 4, 8, 0x7 }, { "rxd3-skew-ps", 4, 12, 0x7 }
+};
+
+static const struct ksz90x1_reg_field ksz90x1_txd_grp[] = {
+ { "txd0-skew-ps", 4, 0, 0x7 }, { "txd1-skew-ps", 4, 4, 0x7 },
+ { "txd2-skew-ps", 4, 8, 0x7 }, { "txd3-skew-ps", 4, 12, 0x7 },
+};
+
+static int ksz90x1_of_config_group(struct phy_device *phydev,
+ struct ksz90x1_ofcfg *ofcfg)
+{
+ struct udevice *dev = phydev->dev;
+ struct phy_driver *drv = phydev->drv;
+ const int ps_to_regval = 60;
+ int val[4];
+ int i, changed = 0, offset, max;
+ u16 regval = 0;
+
+ if (!drv || !drv->writeext)
+ return -EOPNOTSUPP;
+
+ for (i = 0; i < ofcfg->grpsz; i++) {
+ val[i] = fdtdec_get_uint(gd->fdt_blob, dev->of_offset,
+ ofcfg->grp[i].name, -1);
+ offset = ofcfg->grp[i].off;
+ if (val[i] == -1) {
+ /* Default register value for KSZ9021 */
+ regval |= ofcfg->grp[i].dflt << offset;
+ } else {
+ changed = 1; /* Value was changed in OF */
+ /* Calculate the register value and fix corner cases */
+ if (val[i] > ps_to_regval * 0xf) {
+ max = (1 << ofcfg->grp[i].size) - 1;
+ regval |= max << offset;
+ } else {
+ regval |= (val[i] / ps_to_regval) << offset;
+ }
+ }
+ }
+
+ if (!changed)
+ return 0;
+
+ return drv->writeext(phydev, 0, ofcfg->devad, ofcfg->reg, regval);
+}
+#endif
+#endif
+
#ifdef CONFIG_PHY_MICREL_KSZ9021
/*
* KSZ9021
#define CTRL1000_CONFIG_MASTER (1 << 11)
#define CTRL1000_MANUAL_CONFIG (1 << 12)
+#if defined(CONFIG_DM_ETH) && (defined(CONFIG_PHY_MICREL_KSZ9021) || \
+ defined(CONFIG_PHY_MICREL_KSZ9031))
+static const struct ksz90x1_reg_field ksz9021_clk_grp[] = {
+ { "txen-skew-ps", 4, 0, 0x7 }, { "txc-skew-ps", 4, 4, 0x7 },
+ { "rxdv-skew-ps", 4, 8, 0x7 }, { "rxc-skew-ps", 4, 12, 0x7 },
+};
+
+static int ksz9021_of_config(struct phy_device *phydev)
+{
+ struct ksz90x1_ofcfg ofcfg[] = {
+ { MII_KSZ9021_EXT_RGMII_RX_DATA_SKEW, 0, ksz90x1_rxd_grp, 4 },
+ { MII_KSZ9021_EXT_RGMII_TX_DATA_SKEW, 0, ksz90x1_txd_grp, 4 },
+ { MII_KSZ9021_EXT_RGMII_CLOCK_SKEW, 0, ksz9021_clk_grp, 4 },
+ };
+ int i, ret = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ofcfg); i++)
+ ret = ksz90x1_of_config_group(phydev, &(ofcfg[i]));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+#else
+static int ksz9021_of_config(struct phy_device *phydev)
+{
+ return 0;
+}
+#endif
+
int ksz9021_phy_extended_write(struct phy_device *phydev, int regnum, u16 val)
{
/* extended registers */
const unsigned master = CTRL1000_PREFER_MASTER |
CTRL1000_CONFIG_MASTER | CTRL1000_MANUAL_CONFIG;
unsigned features = phydev->drv->features;
+ int ret;
+
+ ret = ksz9021_of_config(phydev);
+ if (ret)
+ return ret;
if (getenv("disable_giga"))
features &= ~(SUPPORTED_1000baseT_Half |
#define MII_KSZ9031_MMD_ACCES_CTRL 0x0d
#define MII_KSZ9031_MMD_REG_DATA 0x0e
+#if defined(CONFIG_DM_ETH) && (defined(CONFIG_PHY_MICREL_KSZ9021) || \
+ defined(CONFIG_PHY_MICREL_KSZ9031))
+static const struct ksz90x1_reg_field ksz9031_ctl_grp[] =
+ { { "txen-skew-ps", 4, 0, 0x7 }, { "rxdv-skew-ps", 4, 4, 0x7 } };
+static const struct ksz90x1_reg_field ksz9031_clk_grp[] =
+ { { "rxc-skew-ps", 5, 0, 0xf }, { "txc-skew-ps", 5, 5, 0xf } };
+
+static int ksz9031_of_config(struct phy_device *phydev)
+{
+ struct ksz90x1_ofcfg ofcfg[] = {
+ { MII_KSZ9031_EXT_RGMII_CTRL_SIG_SKEW, 2, ksz9031_ctl_grp, 2 },
+ { MII_KSZ9031_EXT_RGMII_RX_DATA_SKEW, 2, ksz90x1_rxd_grp, 4 },
+ { MII_KSZ9031_EXT_RGMII_TX_DATA_SKEW, 2, ksz90x1_txd_grp, 4 },
+ { MII_KSZ9031_EXT_RGMII_CLOCK_SKEW, 2, ksz9031_clk_grp, 2 },
+ };
+ int i, ret = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ofcfg); i++)
+ ret = ksz90x1_of_config_group(phydev, &(ofcfg[i]));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+#else
+static int ksz9031_of_config(struct phy_device *phydev)
+{
+ return 0;
+}
+#endif
+
/* Accessors to extended registers*/
int ksz9031_phy_extended_write(struct phy_device *phydev,
int devaddr, int regnum, u16 mode, u16 val)
MII_KSZ9031_MOD_DATA_POST_INC_RW, val);
};
+static int ksz9031_config(struct phy_device *phydev)
+{
+ int ret;
+ ret = ksz9031_of_config(phydev);
+ if (ret)
+ return ret;
+ return genphy_config(phydev);
+}
static struct phy_driver ksz9031_driver = {
.name = "Micrel ksz9031",
.uid = 0x221620,
.mask = 0xfffff0,
.features = PHY_GBIT_FEATURES,
- .config = &genphy_config,
+ .config = &ksz9031_config,
.startup = &ksz90xx_startup,
.shutdown = &genphy_shutdown,
.writeext = &ksz9031_phy_extwrite,
int phy_micrel_init(void)
{
phy_register(&KSZ804_driver);
+ phy_register(&KSZ8031_driver);
+ phy_register(&KSZ8051_driver);
+ phy_register(&KSZ8081_driver);
#ifdef CONFIG_PHY_MICREL_KSZ9021
phy_register(&ksz9021_driver);
#else