1d397f7c4SAlexandru Gagniuc /*
2d397f7c4SAlexandru Gagniuc * Micrel PHY drivers
3d397f7c4SAlexandru Gagniuc *
4d397f7c4SAlexandru Gagniuc * SPDX-License-Identifier: GPL-2.0+
5d397f7c4SAlexandru Gagniuc *
6d397f7c4SAlexandru Gagniuc * Copyright 2010-2011 Freescale Semiconductor, Inc.
7d397f7c4SAlexandru Gagniuc * author Andy Fleming
8d397f7c4SAlexandru Gagniuc * (C) 2012 NetModule AG, David Andrey, added KSZ9031
9d397f7c4SAlexandru Gagniuc * (C) Copyright 2017 Adaptrum, Inc.
10d397f7c4SAlexandru Gagniuc * Written by Alexandru Gagniuc <alex.g@adaptrum.com> for Adaptrum, Inc.
11d397f7c4SAlexandru Gagniuc */
12*8fb9fe4dSPhilipp Tomsich
13d397f7c4SAlexandru Gagniuc #include <config.h>
14d397f7c4SAlexandru Gagniuc #include <common.h>
15d397f7c4SAlexandru Gagniuc #include <dm.h>
16d397f7c4SAlexandru Gagniuc #include <errno.h>
17d397f7c4SAlexandru Gagniuc #include <micrel.h>
18d397f7c4SAlexandru Gagniuc #include <phy.h>
19d397f7c4SAlexandru Gagniuc
20d397f7c4SAlexandru Gagniuc DECLARE_GLOBAL_DATA_PTR;
21d397f7c4SAlexandru Gagniuc
22d397f7c4SAlexandru Gagniuc /*
23d397f7c4SAlexandru Gagniuc * KSZ9021 - KSZ9031 common
24d397f7c4SAlexandru Gagniuc */
25d397f7c4SAlexandru Gagniuc
26d397f7c4SAlexandru Gagniuc #define MII_KSZ90xx_PHY_CTL 0x1f
27d397f7c4SAlexandru Gagniuc #define MIIM_KSZ90xx_PHYCTL_1000 (1 << 6)
28d397f7c4SAlexandru Gagniuc #define MIIM_KSZ90xx_PHYCTL_100 (1 << 5)
29d397f7c4SAlexandru Gagniuc #define MIIM_KSZ90xx_PHYCTL_10 (1 << 4)
30d397f7c4SAlexandru Gagniuc #define MIIM_KSZ90xx_PHYCTL_DUPLEX (1 << 3)
31d397f7c4SAlexandru Gagniuc
32d397f7c4SAlexandru Gagniuc /* KSZ9021 PHY Registers */
33d397f7c4SAlexandru Gagniuc #define MII_KSZ9021_EXTENDED_CTRL 0x0b
34d397f7c4SAlexandru Gagniuc #define MII_KSZ9021_EXTENDED_DATAW 0x0c
35d397f7c4SAlexandru Gagniuc #define MII_KSZ9021_EXTENDED_DATAR 0x0d
36d397f7c4SAlexandru Gagniuc
37d397f7c4SAlexandru Gagniuc #define CTRL1000_PREFER_MASTER (1 << 10)
38d397f7c4SAlexandru Gagniuc #define CTRL1000_CONFIG_MASTER (1 << 11)
39d397f7c4SAlexandru Gagniuc #define CTRL1000_MANUAL_CONFIG (1 << 12)
40d397f7c4SAlexandru Gagniuc
41d397f7c4SAlexandru Gagniuc /* KSZ9031 PHY Registers */
42d397f7c4SAlexandru Gagniuc #define MII_KSZ9031_MMD_ACCES_CTRL 0x0d
43d397f7c4SAlexandru Gagniuc #define MII_KSZ9031_MMD_REG_DATA 0x0e
44d397f7c4SAlexandru Gagniuc
ksz90xx_startup(struct phy_device * phydev)45d397f7c4SAlexandru Gagniuc static int ksz90xx_startup(struct phy_device *phydev)
46d397f7c4SAlexandru Gagniuc {
47d397f7c4SAlexandru Gagniuc unsigned phy_ctl;
48d397f7c4SAlexandru Gagniuc int ret;
49d397f7c4SAlexandru Gagniuc
50d397f7c4SAlexandru Gagniuc ret = genphy_update_link(phydev);
51d397f7c4SAlexandru Gagniuc if (ret)
52d397f7c4SAlexandru Gagniuc return ret;
53d397f7c4SAlexandru Gagniuc
54d397f7c4SAlexandru Gagniuc phy_ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ90xx_PHY_CTL);
55d397f7c4SAlexandru Gagniuc
56d397f7c4SAlexandru Gagniuc if (phy_ctl & MIIM_KSZ90xx_PHYCTL_DUPLEX)
57d397f7c4SAlexandru Gagniuc phydev->duplex = DUPLEX_FULL;
58d397f7c4SAlexandru Gagniuc else
59d397f7c4SAlexandru Gagniuc phydev->duplex = DUPLEX_HALF;
60d397f7c4SAlexandru Gagniuc
61d397f7c4SAlexandru Gagniuc if (phy_ctl & MIIM_KSZ90xx_PHYCTL_1000)
62d397f7c4SAlexandru Gagniuc phydev->speed = SPEED_1000;
63d397f7c4SAlexandru Gagniuc else if (phy_ctl & MIIM_KSZ90xx_PHYCTL_100)
64d397f7c4SAlexandru Gagniuc phydev->speed = SPEED_100;
65d397f7c4SAlexandru Gagniuc else if (phy_ctl & MIIM_KSZ90xx_PHYCTL_10)
66d397f7c4SAlexandru Gagniuc phydev->speed = SPEED_10;
67d397f7c4SAlexandru Gagniuc return 0;
68d397f7c4SAlexandru Gagniuc }
69d397f7c4SAlexandru Gagniuc
70d397f7c4SAlexandru Gagniuc /* Common OF config bits for KSZ9021 and KSZ9031 */
71d397f7c4SAlexandru Gagniuc #ifdef CONFIG_DM_ETH
72d397f7c4SAlexandru Gagniuc struct ksz90x1_reg_field {
73d397f7c4SAlexandru Gagniuc const char *name;
74d397f7c4SAlexandru Gagniuc const u8 size; /* Size of the bitfield, in bits */
75d397f7c4SAlexandru Gagniuc const u8 off; /* Offset from bit 0 */
76d397f7c4SAlexandru Gagniuc const u8 dflt; /* Default value */
77d397f7c4SAlexandru Gagniuc };
78d397f7c4SAlexandru Gagniuc
79d397f7c4SAlexandru Gagniuc struct ksz90x1_ofcfg {
80d397f7c4SAlexandru Gagniuc const u16 reg;
81d397f7c4SAlexandru Gagniuc const u16 devad;
82d397f7c4SAlexandru Gagniuc const struct ksz90x1_reg_field *grp;
83d397f7c4SAlexandru Gagniuc const u16 grpsz;
84d397f7c4SAlexandru Gagniuc };
85d397f7c4SAlexandru Gagniuc
86d397f7c4SAlexandru Gagniuc static const struct ksz90x1_reg_field ksz90x1_rxd_grp[] = {
87d397f7c4SAlexandru Gagniuc { "rxd0-skew-ps", 4, 0, 0x7 }, { "rxd1-skew-ps", 4, 4, 0x7 },
88d397f7c4SAlexandru Gagniuc { "rxd2-skew-ps", 4, 8, 0x7 }, { "rxd3-skew-ps", 4, 12, 0x7 }
89d397f7c4SAlexandru Gagniuc };
90d397f7c4SAlexandru Gagniuc
91d397f7c4SAlexandru Gagniuc static const struct ksz90x1_reg_field ksz90x1_txd_grp[] = {
92d397f7c4SAlexandru Gagniuc { "txd0-skew-ps", 4, 0, 0x7 }, { "txd1-skew-ps", 4, 4, 0x7 },
93d397f7c4SAlexandru Gagniuc { "txd2-skew-ps", 4, 8, 0x7 }, { "txd3-skew-ps", 4, 12, 0x7 },
94d397f7c4SAlexandru Gagniuc };
95d397f7c4SAlexandru Gagniuc
96d397f7c4SAlexandru Gagniuc static const struct ksz90x1_reg_field ksz9021_clk_grp[] = {
97d397f7c4SAlexandru Gagniuc { "txen-skew-ps", 4, 0, 0x7 }, { "txc-skew-ps", 4, 4, 0x7 },
98d397f7c4SAlexandru Gagniuc { "rxdv-skew-ps", 4, 8, 0x7 }, { "rxc-skew-ps", 4, 12, 0x7 },
99d397f7c4SAlexandru Gagniuc };
100d397f7c4SAlexandru Gagniuc
101d397f7c4SAlexandru Gagniuc static const struct ksz90x1_reg_field ksz9031_ctl_grp[] = {
102d397f7c4SAlexandru Gagniuc { "txen-skew-ps", 4, 0, 0x7 }, { "rxdv-skew-ps", 4, 4, 0x7 }
103d397f7c4SAlexandru Gagniuc };
104d397f7c4SAlexandru Gagniuc
105d397f7c4SAlexandru Gagniuc static const struct ksz90x1_reg_field ksz9031_clk_grp[] = {
106d397f7c4SAlexandru Gagniuc { "rxc-skew-ps", 5, 0, 0xf }, { "txc-skew-ps", 5, 5, 0xf }
107d397f7c4SAlexandru Gagniuc };
108d397f7c4SAlexandru Gagniuc
ksz90x1_of_config_group(struct phy_device * phydev,struct ksz90x1_ofcfg * ofcfg)109d397f7c4SAlexandru Gagniuc static int ksz90x1_of_config_group(struct phy_device *phydev,
110d397f7c4SAlexandru Gagniuc struct ksz90x1_ofcfg *ofcfg)
111d397f7c4SAlexandru Gagniuc {
112d397f7c4SAlexandru Gagniuc struct udevice *dev = phydev->dev;
113d397f7c4SAlexandru Gagniuc struct phy_driver *drv = phydev->drv;
114d397f7c4SAlexandru Gagniuc const int ps_to_regval = 60;
115d397f7c4SAlexandru Gagniuc int val[4];
116d397f7c4SAlexandru Gagniuc int i, changed = 0, offset, max;
117d397f7c4SAlexandru Gagniuc u16 regval = 0;
118d397f7c4SAlexandru Gagniuc
119d397f7c4SAlexandru Gagniuc if (!drv || !drv->writeext)
120d397f7c4SAlexandru Gagniuc return -EOPNOTSUPP;
121d397f7c4SAlexandru Gagniuc
122d397f7c4SAlexandru Gagniuc for (i = 0; i < ofcfg->grpsz; i++) {
123*8fb9fe4dSPhilipp Tomsich val[i] = dev_read_u32_default(dev, ofcfg->grp[i].name, ~0);
124d397f7c4SAlexandru Gagniuc offset = ofcfg->grp[i].off;
125d397f7c4SAlexandru Gagniuc if (val[i] == -1) {
126d397f7c4SAlexandru Gagniuc /* Default register value for KSZ9021 */
127d397f7c4SAlexandru Gagniuc regval |= ofcfg->grp[i].dflt << offset;
128d397f7c4SAlexandru Gagniuc } else {
129d397f7c4SAlexandru Gagniuc changed = 1; /* Value was changed in OF */
130d397f7c4SAlexandru Gagniuc /* Calculate the register value and fix corner cases */
131d397f7c4SAlexandru Gagniuc if (val[i] > ps_to_regval * 0xf) {
132d397f7c4SAlexandru Gagniuc max = (1 << ofcfg->grp[i].size) - 1;
133d397f7c4SAlexandru Gagniuc regval |= max << offset;
134d397f7c4SAlexandru Gagniuc } else {
135d397f7c4SAlexandru Gagniuc regval |= (val[i] / ps_to_regval) << offset;
136d397f7c4SAlexandru Gagniuc }
137d397f7c4SAlexandru Gagniuc }
138d397f7c4SAlexandru Gagniuc }
139d397f7c4SAlexandru Gagniuc
140d397f7c4SAlexandru Gagniuc if (!changed)
141d397f7c4SAlexandru Gagniuc return 0;
142d397f7c4SAlexandru Gagniuc
143d397f7c4SAlexandru Gagniuc return drv->writeext(phydev, 0, ofcfg->devad, ofcfg->reg, regval);
144d397f7c4SAlexandru Gagniuc }
145d397f7c4SAlexandru Gagniuc
ksz9021_of_config(struct phy_device * phydev)146d397f7c4SAlexandru Gagniuc static int ksz9021_of_config(struct phy_device *phydev)
147d397f7c4SAlexandru Gagniuc {
148d397f7c4SAlexandru Gagniuc struct ksz90x1_ofcfg ofcfg[] = {
149d397f7c4SAlexandru Gagniuc { MII_KSZ9021_EXT_RGMII_RX_DATA_SKEW, 0, ksz90x1_rxd_grp, 4 },
150d397f7c4SAlexandru Gagniuc { MII_KSZ9021_EXT_RGMII_TX_DATA_SKEW, 0, ksz90x1_txd_grp, 4 },
151d397f7c4SAlexandru Gagniuc { MII_KSZ9021_EXT_RGMII_CLOCK_SKEW, 0, ksz9021_clk_grp, 4 },
152d397f7c4SAlexandru Gagniuc };
153d397f7c4SAlexandru Gagniuc int i, ret = 0;
154d397f7c4SAlexandru Gagniuc
155d397f7c4SAlexandru Gagniuc for (i = 0; i < ARRAY_SIZE(ofcfg); i++) {
156d397f7c4SAlexandru Gagniuc ret = ksz90x1_of_config_group(phydev, &(ofcfg[i]));
157d397f7c4SAlexandru Gagniuc if (ret)
158d397f7c4SAlexandru Gagniuc return ret;
159d397f7c4SAlexandru Gagniuc }
160d397f7c4SAlexandru Gagniuc
161d397f7c4SAlexandru Gagniuc return 0;
162d397f7c4SAlexandru Gagniuc }
163d397f7c4SAlexandru Gagniuc
ksz9031_of_config(struct phy_device * phydev)164d397f7c4SAlexandru Gagniuc static int ksz9031_of_config(struct phy_device *phydev)
165d397f7c4SAlexandru Gagniuc {
166d397f7c4SAlexandru Gagniuc struct ksz90x1_ofcfg ofcfg[] = {
167d397f7c4SAlexandru Gagniuc { MII_KSZ9031_EXT_RGMII_CTRL_SIG_SKEW, 2, ksz9031_ctl_grp, 2 },
168d397f7c4SAlexandru Gagniuc { MII_KSZ9031_EXT_RGMII_RX_DATA_SKEW, 2, ksz90x1_rxd_grp, 4 },
169d397f7c4SAlexandru Gagniuc { MII_KSZ9031_EXT_RGMII_TX_DATA_SKEW, 2, ksz90x1_txd_grp, 4 },
170d397f7c4SAlexandru Gagniuc { MII_KSZ9031_EXT_RGMII_CLOCK_SKEW, 2, ksz9031_clk_grp, 2 },
171d397f7c4SAlexandru Gagniuc };
172d397f7c4SAlexandru Gagniuc int i, ret = 0;
173d397f7c4SAlexandru Gagniuc
174d397f7c4SAlexandru Gagniuc for (i = 0; i < ARRAY_SIZE(ofcfg); i++) {
175d397f7c4SAlexandru Gagniuc ret = ksz90x1_of_config_group(phydev, &(ofcfg[i]));
176d397f7c4SAlexandru Gagniuc if (ret)
177d397f7c4SAlexandru Gagniuc return ret;
178d397f7c4SAlexandru Gagniuc }
179d397f7c4SAlexandru Gagniuc
180d397f7c4SAlexandru Gagniuc return 0;
181d397f7c4SAlexandru Gagniuc }
182d397f7c4SAlexandru Gagniuc
ksz9031_center_flp_timing(struct phy_device * phydev)183d397f7c4SAlexandru Gagniuc static int ksz9031_center_flp_timing(struct phy_device *phydev)
184d397f7c4SAlexandru Gagniuc {
185d397f7c4SAlexandru Gagniuc struct phy_driver *drv = phydev->drv;
186d397f7c4SAlexandru Gagniuc int ret = 0;
187d397f7c4SAlexandru Gagniuc
188d397f7c4SAlexandru Gagniuc if (!drv || !drv->writeext)
189d397f7c4SAlexandru Gagniuc return -EOPNOTSUPP;
190d397f7c4SAlexandru Gagniuc
191d397f7c4SAlexandru Gagniuc ret = drv->writeext(phydev, 0, 0, MII_KSZ9031_FLP_BURST_TX_LO, 0x1A80);
192d397f7c4SAlexandru Gagniuc if (ret)
193d397f7c4SAlexandru Gagniuc return ret;
194d397f7c4SAlexandru Gagniuc
195d397f7c4SAlexandru Gagniuc ret = drv->writeext(phydev, 0, 0, MII_KSZ9031_FLP_BURST_TX_HI, 0x6);
196d397f7c4SAlexandru Gagniuc return ret;
197d397f7c4SAlexandru Gagniuc }
198d397f7c4SAlexandru Gagniuc
199d397f7c4SAlexandru Gagniuc #else /* !CONFIG_DM_ETH */
ksz9021_of_config(struct phy_device * phydev)200d397f7c4SAlexandru Gagniuc static int ksz9021_of_config(struct phy_device *phydev)
201d397f7c4SAlexandru Gagniuc {
202d397f7c4SAlexandru Gagniuc return 0;
203d397f7c4SAlexandru Gagniuc }
204d397f7c4SAlexandru Gagniuc
ksz9031_of_config(struct phy_device * phydev)205d397f7c4SAlexandru Gagniuc static int ksz9031_of_config(struct phy_device *phydev)
206d397f7c4SAlexandru Gagniuc {
207d397f7c4SAlexandru Gagniuc return 0;
208d397f7c4SAlexandru Gagniuc }
209d397f7c4SAlexandru Gagniuc
ksz9031_center_flp_timing(struct phy_device * phydev)210d397f7c4SAlexandru Gagniuc static int ksz9031_center_flp_timing(struct phy_device *phydev)
211d397f7c4SAlexandru Gagniuc {
212d397f7c4SAlexandru Gagniuc return 0;
213d397f7c4SAlexandru Gagniuc }
214d397f7c4SAlexandru Gagniuc #endif
215d397f7c4SAlexandru Gagniuc
216d397f7c4SAlexandru Gagniuc /*
217d397f7c4SAlexandru Gagniuc * KSZ9021
218d397f7c4SAlexandru Gagniuc */
ksz9021_phy_extended_write(struct phy_device * phydev,int regnum,u16 val)219d397f7c4SAlexandru Gagniuc int ksz9021_phy_extended_write(struct phy_device *phydev, int regnum, u16 val)
220d397f7c4SAlexandru Gagniuc {
221d397f7c4SAlexandru Gagniuc /* extended registers */
222d397f7c4SAlexandru Gagniuc phy_write(phydev, MDIO_DEVAD_NONE,
223d397f7c4SAlexandru Gagniuc MII_KSZ9021_EXTENDED_CTRL, regnum | 0x8000);
224d397f7c4SAlexandru Gagniuc return phy_write(phydev, MDIO_DEVAD_NONE,
225d397f7c4SAlexandru Gagniuc MII_KSZ9021_EXTENDED_DATAW, val);
226d397f7c4SAlexandru Gagniuc }
227d397f7c4SAlexandru Gagniuc
ksz9021_phy_extended_read(struct phy_device * phydev,int regnum)228d397f7c4SAlexandru Gagniuc int ksz9021_phy_extended_read(struct phy_device *phydev, int regnum)
229d397f7c4SAlexandru Gagniuc {
230d397f7c4SAlexandru Gagniuc /* extended registers */
231d397f7c4SAlexandru Gagniuc phy_write(phydev, MDIO_DEVAD_NONE, MII_KSZ9021_EXTENDED_CTRL, regnum);
232d397f7c4SAlexandru Gagniuc return phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ9021_EXTENDED_DATAR);
233d397f7c4SAlexandru Gagniuc }
234d397f7c4SAlexandru Gagniuc
235d397f7c4SAlexandru Gagniuc
ksz9021_phy_extread(struct phy_device * phydev,int addr,int devaddr,int regnum)236d397f7c4SAlexandru Gagniuc static int ksz9021_phy_extread(struct phy_device *phydev, int addr, int devaddr,
237d397f7c4SAlexandru Gagniuc int regnum)
238d397f7c4SAlexandru Gagniuc {
239d397f7c4SAlexandru Gagniuc return ksz9021_phy_extended_read(phydev, regnum);
240d397f7c4SAlexandru Gagniuc }
241d397f7c4SAlexandru Gagniuc
ksz9021_phy_extwrite(struct phy_device * phydev,int addr,int devaddr,int regnum,u16 val)242d397f7c4SAlexandru Gagniuc static int ksz9021_phy_extwrite(struct phy_device *phydev, int addr,
243d397f7c4SAlexandru Gagniuc int devaddr, int regnum, u16 val)
244d397f7c4SAlexandru Gagniuc {
245d397f7c4SAlexandru Gagniuc return ksz9021_phy_extended_write(phydev, regnum, val);
246d397f7c4SAlexandru Gagniuc }
247d397f7c4SAlexandru Gagniuc
ksz9021_config(struct phy_device * phydev)248d397f7c4SAlexandru Gagniuc static int ksz9021_config(struct phy_device *phydev)
249d397f7c4SAlexandru Gagniuc {
250d397f7c4SAlexandru Gagniuc unsigned ctrl1000 = 0;
251d397f7c4SAlexandru Gagniuc const unsigned master = CTRL1000_PREFER_MASTER |
252d397f7c4SAlexandru Gagniuc CTRL1000_CONFIG_MASTER | CTRL1000_MANUAL_CONFIG;
253d397f7c4SAlexandru Gagniuc unsigned features = phydev->drv->features;
254d397f7c4SAlexandru Gagniuc int ret;
255d397f7c4SAlexandru Gagniuc
256d397f7c4SAlexandru Gagniuc ret = ksz9021_of_config(phydev);
257d397f7c4SAlexandru Gagniuc if (ret)
258d397f7c4SAlexandru Gagniuc return ret;
259d397f7c4SAlexandru Gagniuc
26000caae6dSSimon Glass if (env_get("disable_giga"))
261d397f7c4SAlexandru Gagniuc features &= ~(SUPPORTED_1000baseT_Half |
262d397f7c4SAlexandru Gagniuc SUPPORTED_1000baseT_Full);
263d397f7c4SAlexandru Gagniuc /* force master mode for 1000BaseT due to chip errata */
264d397f7c4SAlexandru Gagniuc if (features & SUPPORTED_1000baseT_Half)
265d397f7c4SAlexandru Gagniuc ctrl1000 |= ADVERTISE_1000HALF | master;
266d397f7c4SAlexandru Gagniuc if (features & SUPPORTED_1000baseT_Full)
267d397f7c4SAlexandru Gagniuc ctrl1000 |= ADVERTISE_1000FULL | master;
268d397f7c4SAlexandru Gagniuc phydev->advertising = features;
269d397f7c4SAlexandru Gagniuc phydev->supported = features;
270d397f7c4SAlexandru Gagniuc phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, ctrl1000);
271d397f7c4SAlexandru Gagniuc genphy_config_aneg(phydev);
272d397f7c4SAlexandru Gagniuc genphy_restart_aneg(phydev);
273d397f7c4SAlexandru Gagniuc return 0;
274d397f7c4SAlexandru Gagniuc }
275d397f7c4SAlexandru Gagniuc
276d397f7c4SAlexandru Gagniuc static struct phy_driver ksz9021_driver = {
277d397f7c4SAlexandru Gagniuc .name = "Micrel ksz9021",
278d397f7c4SAlexandru Gagniuc .uid = 0x221610,
279d397f7c4SAlexandru Gagniuc .mask = 0xfffff0,
280d397f7c4SAlexandru Gagniuc .features = PHY_GBIT_FEATURES,
281d397f7c4SAlexandru Gagniuc .config = &ksz9021_config,
282d397f7c4SAlexandru Gagniuc .startup = &ksz90xx_startup,
283d397f7c4SAlexandru Gagniuc .shutdown = &genphy_shutdown,
284d397f7c4SAlexandru Gagniuc .writeext = &ksz9021_phy_extwrite,
285d397f7c4SAlexandru Gagniuc .readext = &ksz9021_phy_extread,
286d397f7c4SAlexandru Gagniuc };
287d397f7c4SAlexandru Gagniuc
288d397f7c4SAlexandru Gagniuc /*
289d397f7c4SAlexandru Gagniuc * KSZ9031
290d397f7c4SAlexandru Gagniuc */
ksz9031_phy_extended_write(struct phy_device * phydev,int devaddr,int regnum,u16 mode,u16 val)291d397f7c4SAlexandru Gagniuc int ksz9031_phy_extended_write(struct phy_device *phydev,
292d397f7c4SAlexandru Gagniuc int devaddr, int regnum, u16 mode, u16 val)
293d397f7c4SAlexandru Gagniuc {
294d397f7c4SAlexandru Gagniuc /*select register addr for mmd*/
295d397f7c4SAlexandru Gagniuc phy_write(phydev, MDIO_DEVAD_NONE,
296d397f7c4SAlexandru Gagniuc MII_KSZ9031_MMD_ACCES_CTRL, devaddr);
297d397f7c4SAlexandru Gagniuc /*select register for mmd*/
298d397f7c4SAlexandru Gagniuc phy_write(phydev, MDIO_DEVAD_NONE,
299d397f7c4SAlexandru Gagniuc MII_KSZ9031_MMD_REG_DATA, regnum);
300d397f7c4SAlexandru Gagniuc /*setup mode*/
301d397f7c4SAlexandru Gagniuc phy_write(phydev, MDIO_DEVAD_NONE,
302d397f7c4SAlexandru Gagniuc MII_KSZ9031_MMD_ACCES_CTRL, (mode | devaddr));
303d397f7c4SAlexandru Gagniuc /*write the value*/
304d397f7c4SAlexandru Gagniuc return phy_write(phydev, MDIO_DEVAD_NONE,
305d397f7c4SAlexandru Gagniuc MII_KSZ9031_MMD_REG_DATA, val);
306d397f7c4SAlexandru Gagniuc }
307d397f7c4SAlexandru Gagniuc
ksz9031_phy_extended_read(struct phy_device * phydev,int devaddr,int regnum,u16 mode)308d397f7c4SAlexandru Gagniuc int ksz9031_phy_extended_read(struct phy_device *phydev, int devaddr,
309d397f7c4SAlexandru Gagniuc int regnum, u16 mode)
310d397f7c4SAlexandru Gagniuc {
311d397f7c4SAlexandru Gagniuc phy_write(phydev, MDIO_DEVAD_NONE,
312d397f7c4SAlexandru Gagniuc MII_KSZ9031_MMD_ACCES_CTRL, devaddr);
313d397f7c4SAlexandru Gagniuc phy_write(phydev, MDIO_DEVAD_NONE,
314d397f7c4SAlexandru Gagniuc MII_KSZ9031_MMD_REG_DATA, regnum);
315d397f7c4SAlexandru Gagniuc phy_write(phydev, MDIO_DEVAD_NONE,
316d397f7c4SAlexandru Gagniuc MII_KSZ9031_MMD_ACCES_CTRL, (devaddr | mode));
317d397f7c4SAlexandru Gagniuc return phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ9031_MMD_REG_DATA);
318d397f7c4SAlexandru Gagniuc }
319d397f7c4SAlexandru Gagniuc
ksz9031_phy_extread(struct phy_device * phydev,int addr,int devaddr,int regnum)320d397f7c4SAlexandru Gagniuc static int ksz9031_phy_extread(struct phy_device *phydev, int addr, int devaddr,
321d397f7c4SAlexandru Gagniuc int regnum)
322d397f7c4SAlexandru Gagniuc {
323d397f7c4SAlexandru Gagniuc return ksz9031_phy_extended_read(phydev, devaddr, regnum,
324d397f7c4SAlexandru Gagniuc MII_KSZ9031_MOD_DATA_NO_POST_INC);
325d397f7c4SAlexandru Gagniuc }
326d397f7c4SAlexandru Gagniuc
ksz9031_phy_extwrite(struct phy_device * phydev,int addr,int devaddr,int regnum,u16 val)327d397f7c4SAlexandru Gagniuc static int ksz9031_phy_extwrite(struct phy_device *phydev, int addr,
328d397f7c4SAlexandru Gagniuc int devaddr, int regnum, u16 val)
329d397f7c4SAlexandru Gagniuc {
330d397f7c4SAlexandru Gagniuc return ksz9031_phy_extended_write(phydev, devaddr, regnum,
331d397f7c4SAlexandru Gagniuc MII_KSZ9031_MOD_DATA_POST_INC_RW, val);
332d397f7c4SAlexandru Gagniuc }
333d397f7c4SAlexandru Gagniuc
ksz9031_config(struct phy_device * phydev)334d397f7c4SAlexandru Gagniuc static int ksz9031_config(struct phy_device *phydev)
335d397f7c4SAlexandru Gagniuc {
336d397f7c4SAlexandru Gagniuc int ret;
337ef1f61aaSSebastien Bourdelin
338d397f7c4SAlexandru Gagniuc ret = ksz9031_of_config(phydev);
339d397f7c4SAlexandru Gagniuc if (ret)
340d397f7c4SAlexandru Gagniuc return ret;
341d397f7c4SAlexandru Gagniuc ret = ksz9031_center_flp_timing(phydev);
342d397f7c4SAlexandru Gagniuc if (ret)
343d397f7c4SAlexandru Gagniuc return ret;
344ef1f61aaSSebastien Bourdelin
345ef1f61aaSSebastien Bourdelin /* add an option to disable the gigabit feature of this PHY */
34600caae6dSSimon Glass if (env_get("disable_giga")) {
347ef1f61aaSSebastien Bourdelin unsigned features;
348ef1f61aaSSebastien Bourdelin unsigned bmcr;
349ef1f61aaSSebastien Bourdelin
350ef1f61aaSSebastien Bourdelin /* disable speed 1000 in features supported by the PHY */
351ef1f61aaSSebastien Bourdelin features = phydev->drv->features;
352ef1f61aaSSebastien Bourdelin features &= ~(SUPPORTED_1000baseT_Half |
353ef1f61aaSSebastien Bourdelin SUPPORTED_1000baseT_Full);
354ef1f61aaSSebastien Bourdelin phydev->advertising = phydev->supported = features;
355ef1f61aaSSebastien Bourdelin
356ef1f61aaSSebastien Bourdelin /* disable speed 1000 in Basic Control Register */
357ef1f61aaSSebastien Bourdelin bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
358ef1f61aaSSebastien Bourdelin bmcr &= ~(1 << 6);
359ef1f61aaSSebastien Bourdelin phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr);
360ef1f61aaSSebastien Bourdelin
361ef1f61aaSSebastien Bourdelin /* disable speed 1000 in 1000Base-T Control Register */
362ef1f61aaSSebastien Bourdelin phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, 0);
363ef1f61aaSSebastien Bourdelin
364ef1f61aaSSebastien Bourdelin /* start autoneg */
365ef1f61aaSSebastien Bourdelin genphy_config_aneg(phydev);
366ef1f61aaSSebastien Bourdelin genphy_restart_aneg(phydev);
367ef1f61aaSSebastien Bourdelin
368ef1f61aaSSebastien Bourdelin return 0;
369ef1f61aaSSebastien Bourdelin }
370ef1f61aaSSebastien Bourdelin
371d397f7c4SAlexandru Gagniuc return genphy_config(phydev);
372d397f7c4SAlexandru Gagniuc }
373d397f7c4SAlexandru Gagniuc
374d397f7c4SAlexandru Gagniuc static struct phy_driver ksz9031_driver = {
375d397f7c4SAlexandru Gagniuc .name = "Micrel ksz9031",
376d397f7c4SAlexandru Gagniuc .uid = 0x221620,
377d397f7c4SAlexandru Gagniuc .mask = 0xfffff0,
378d397f7c4SAlexandru Gagniuc .features = PHY_GBIT_FEATURES,
379d397f7c4SAlexandru Gagniuc .config = &ksz9031_config,
380d397f7c4SAlexandru Gagniuc .startup = &ksz90xx_startup,
381d397f7c4SAlexandru Gagniuc .shutdown = &genphy_shutdown,
382d397f7c4SAlexandru Gagniuc .writeext = &ksz9031_phy_extwrite,
383d397f7c4SAlexandru Gagniuc .readext = &ksz9031_phy_extread,
384d397f7c4SAlexandru Gagniuc };
385d397f7c4SAlexandru Gagniuc
phy_micrel_ksz90x1_init(void)386d397f7c4SAlexandru Gagniuc int phy_micrel_ksz90x1_init(void)
387d397f7c4SAlexandru Gagniuc {
388d397f7c4SAlexandru Gagniuc phy_register(&ksz9021_driver);
389d397f7c4SAlexandru Gagniuc phy_register(&ksz9031_driver);
390d397f7c4SAlexandru Gagniuc return 0;
391d397f7c4SAlexandru Gagniuc }
392