blob: f18e40a2feaadd04379221fbf915a96306f9b20c [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Andy Fleming60ca78b2011-04-07 21:56:05 -05002/*
3 * Micrel PHY drivers
4 *
Andy Fleming60ca78b2011-04-07 21:56:05 -05005 * Copyright 2010-2011 Freescale Semiconductor, Inc.
6 * author Andy Fleming
David Andreyf0d83c42013-02-06 22:18:37 +01007 * (C) 2012 NetModule AG, David Andrey, added KSZ9031
Alexandru Gagniuc757bb672017-07-07 11:36:57 -07008 * (C) Copyright 2017 Adaptrum, Inc.
9 * Written by Alexandru Gagniuc <alex.g@adaptrum.com> for Adaptrum, Inc.
Andy Fleming60ca78b2011-04-07 21:56:05 -050010 */
Troy Kisky80b6b092012-02-07 14:08:48 +000011#include <common.h>
Marek Vasut1005ce52015-12-05 17:41:58 +010012#include <dm.h>
13#include <errno.h>
Troy Kisky80b6b092012-02-07 14:08:48 +000014#include <micrel.h>
Andy Fleming60ca78b2011-04-07 21:56:05 -050015#include <phy.h>
16
Pavel Machek5f022112014-09-09 14:26:51 +020017/*
David Andreyf0d83c42013-02-06 22:18:37 +010018 * KSZ9021 - KSZ9031 common
19 */
20
21#define MII_KSZ90xx_PHY_CTL 0x1f
22#define MIIM_KSZ90xx_PHYCTL_1000 (1 << 6)
23#define MIIM_KSZ90xx_PHYCTL_100 (1 << 5)
24#define MIIM_KSZ90xx_PHYCTL_10 (1 << 4)
25#define MIIM_KSZ90xx_PHYCTL_DUPLEX (1 << 3)
26
Alexandru Gagniuc757bb672017-07-07 11:36:57 -070027/* KSZ9021 PHY Registers */
28#define MII_KSZ9021_EXTENDED_CTRL 0x0b
29#define MII_KSZ9021_EXTENDED_DATAW 0x0c
30#define MII_KSZ9021_EXTENDED_DATAR 0x0d
31
32#define CTRL1000_PREFER_MASTER (1 << 10)
33#define CTRL1000_CONFIG_MASTER (1 << 11)
34#define CTRL1000_MANUAL_CONFIG (1 << 12)
35
James Byrne457107f2019-03-04 17:40:33 +000036#define KSZ9021_PS_TO_REG 120
37
Alexandru Gagniuc757bb672017-07-07 11:36:57 -070038/* KSZ9031 PHY Registers */
39#define MII_KSZ9031_MMD_ACCES_CTRL 0x0d
40#define MII_KSZ9031_MMD_REG_DATA 0x0e
41
James Byrne457107f2019-03-04 17:40:33 +000042#define KSZ9031_PS_TO_REG 60
43
David Andreyf0d83c42013-02-06 22:18:37 +010044static int ksz90xx_startup(struct phy_device *phydev)
45{
46 unsigned phy_ctl;
Michal Simek5ff89662016-05-18 12:46:12 +020047 int ret;
48
49 ret = genphy_update_link(phydev);
50 if (ret)
51 return ret;
52
David Andreyf0d83c42013-02-06 22:18:37 +010053 phy_ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ90xx_PHY_CTL);
54
55 if (phy_ctl & MIIM_KSZ90xx_PHYCTL_DUPLEX)
56 phydev->duplex = DUPLEX_FULL;
57 else
58 phydev->duplex = DUPLEX_HALF;
59
60 if (phy_ctl & MIIM_KSZ90xx_PHYCTL_1000)
61 phydev->speed = SPEED_1000;
62 else if (phy_ctl & MIIM_KSZ90xx_PHYCTL_100)
63 phydev->speed = SPEED_100;
64 else if (phy_ctl & MIIM_KSZ90xx_PHYCTL_10)
65 phydev->speed = SPEED_10;
66 return 0;
67}
David Andreyf0d83c42013-02-06 22:18:37 +010068
Marek Vasut1005ce52015-12-05 17:41:58 +010069/* Common OF config bits for KSZ9021 and KSZ9031 */
Marek Vasut1005ce52015-12-05 17:41:58 +010070#ifdef CONFIG_DM_ETH
71struct ksz90x1_reg_field {
72 const char *name;
73 const u8 size; /* Size of the bitfield, in bits */
74 const u8 off; /* Offset from bit 0 */
75 const u8 dflt; /* Default value */
76};
77
78struct ksz90x1_ofcfg {
79 const u16 reg;
80 const u16 devad;
81 const struct ksz90x1_reg_field *grp;
82 const u16 grpsz;
83};
84
85static const struct ksz90x1_reg_field ksz90x1_rxd_grp[] = {
86 { "rxd0-skew-ps", 4, 0, 0x7 }, { "rxd1-skew-ps", 4, 4, 0x7 },
87 { "rxd2-skew-ps", 4, 8, 0x7 }, { "rxd3-skew-ps", 4, 12, 0x7 }
88};
89
90static const struct ksz90x1_reg_field ksz90x1_txd_grp[] = {
91 { "txd0-skew-ps", 4, 0, 0x7 }, { "txd1-skew-ps", 4, 4, 0x7 },
92 { "txd2-skew-ps", 4, 8, 0x7 }, { "txd3-skew-ps", 4, 12, 0x7 },
93};
94
Alexandru Gagniuc757bb672017-07-07 11:36:57 -070095static const struct ksz90x1_reg_field ksz9021_clk_grp[] = {
96 { "txen-skew-ps", 4, 0, 0x7 }, { "txc-skew-ps", 4, 4, 0x7 },
97 { "rxdv-skew-ps", 4, 8, 0x7 }, { "rxc-skew-ps", 4, 12, 0x7 },
98};
99
100static const struct ksz90x1_reg_field ksz9031_ctl_grp[] = {
101 { "txen-skew-ps", 4, 0, 0x7 }, { "rxdv-skew-ps", 4, 4, 0x7 }
102};
103
104static const struct ksz90x1_reg_field ksz9031_clk_grp[] = {
105 { "rxc-skew-ps", 5, 0, 0xf }, { "txc-skew-ps", 5, 5, 0xf }
106};
107
Marek Vasut1005ce52015-12-05 17:41:58 +0100108static int ksz90x1_of_config_group(struct phy_device *phydev,
James Byrne457107f2019-03-04 17:40:33 +0000109 struct ksz90x1_ofcfg *ofcfg,
110 int ps_to_regval)
Marek Vasut1005ce52015-12-05 17:41:58 +0100111{
112 struct udevice *dev = phydev->dev;
113 struct phy_driver *drv = phydev->drv;
Marek Vasut1005ce52015-12-05 17:41:58 +0100114 int val[4];
115 int i, changed = 0, offset, max;
116 u16 regval = 0;
James Byrne9e791442019-03-04 17:40:34 +0000117 ofnode node;
Marek Vasut1005ce52015-12-05 17:41:58 +0100118
119 if (!drv || !drv->writeext)
120 return -EOPNOTSUPP;
121
James Byrne9e791442019-03-04 17:40:34 +0000122 /* Look for a PHY node under the Ethernet node */
123 node = dev_read_subnode(dev, "ethernet-phy");
124 if (!ofnode_valid(node)) {
125 /* No node found, look in the Ethernet node */
126 node = dev_ofnode(dev);
127 }
128
Marek Vasut1005ce52015-12-05 17:41:58 +0100129 for (i = 0; i < ofcfg->grpsz; i++) {
James Byrne9e791442019-03-04 17:40:34 +0000130 val[i] = ofnode_read_u32_default(node, ofcfg->grp[i].name, ~0);
Marek Vasut1005ce52015-12-05 17:41:58 +0100131 offset = ofcfg->grp[i].off;
132 if (val[i] == -1) {
133 /* Default register value for KSZ9021 */
134 regval |= ofcfg->grp[i].dflt << offset;
135 } else {
136 changed = 1; /* Value was changed in OF */
137 /* Calculate the register value and fix corner cases */
Andreas Pretzschf7c689d2018-11-29 20:04:53 +0100138 max = (1 << ofcfg->grp[i].size) - 1;
139 if (val[i] > ps_to_regval * max) {
Marek Vasut1005ce52015-12-05 17:41:58 +0100140 regval |= max << offset;
141 } else {
142 regval |= (val[i] / ps_to_regval) << offset;
143 }
144 }
145 }
146
147 if (!changed)
148 return 0;
149
150 return drv->writeext(phydev, 0, ofcfg->devad, ofcfg->reg, regval);
151}
Marek Vasut1005ce52015-12-05 17:41:58 +0100152
153static int ksz9021_of_config(struct phy_device *phydev)
154{
155 struct ksz90x1_ofcfg ofcfg[] = {
156 { MII_KSZ9021_EXT_RGMII_RX_DATA_SKEW, 0, ksz90x1_rxd_grp, 4 },
157 { MII_KSZ9021_EXT_RGMII_TX_DATA_SKEW, 0, ksz90x1_txd_grp, 4 },
158 { MII_KSZ9021_EXT_RGMII_CLOCK_SKEW, 0, ksz9021_clk_grp, 4 },
159 };
160 int i, ret = 0;
161
Marek Vasut0c766302016-11-14 15:08:42 +0100162 for (i = 0; i < ARRAY_SIZE(ofcfg); i++) {
James Byrne457107f2019-03-04 17:40:33 +0000163 ret = ksz90x1_of_config_group(phydev, &ofcfg[i],
164 KSZ9021_PS_TO_REG);
Marek Vasut1005ce52015-12-05 17:41:58 +0100165 if (ret)
166 return ret;
Marek Vasut0c766302016-11-14 15:08:42 +0100167 }
Marek Vasut1005ce52015-12-05 17:41:58 +0100168
169 return 0;
170}
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700171
172static int ksz9031_of_config(struct phy_device *phydev)
173{
174 struct ksz90x1_ofcfg ofcfg[] = {
175 { MII_KSZ9031_EXT_RGMII_CTRL_SIG_SKEW, 2, ksz9031_ctl_grp, 2 },
176 { MII_KSZ9031_EXT_RGMII_RX_DATA_SKEW, 2, ksz90x1_rxd_grp, 4 },
177 { MII_KSZ9031_EXT_RGMII_TX_DATA_SKEW, 2, ksz90x1_txd_grp, 4 },
178 { MII_KSZ9031_EXT_RGMII_CLOCK_SKEW, 2, ksz9031_clk_grp, 2 },
179 };
180 int i, ret = 0;
181
182 for (i = 0; i < ARRAY_SIZE(ofcfg); i++) {
James Byrne457107f2019-03-04 17:40:33 +0000183 ret = ksz90x1_of_config_group(phydev, &ofcfg[i],
184 KSZ9031_PS_TO_REG);
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700185 if (ret)
186 return ret;
187 }
188
189 return 0;
190}
191
192static int ksz9031_center_flp_timing(struct phy_device *phydev)
193{
194 struct phy_driver *drv = phydev->drv;
195 int ret = 0;
196
197 if (!drv || !drv->writeext)
198 return -EOPNOTSUPP;
199
200 ret = drv->writeext(phydev, 0, 0, MII_KSZ9031_FLP_BURST_TX_LO, 0x1A80);
201 if (ret)
202 return ret;
203
204 ret = drv->writeext(phydev, 0, 0, MII_KSZ9031_FLP_BURST_TX_HI, 0x6);
205 return ret;
206}
207
208#else /* !CONFIG_DM_ETH */
Marek Vasut1005ce52015-12-05 17:41:58 +0100209static int ksz9021_of_config(struct phy_device *phydev)
210{
211 return 0;
212}
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700213
214static int ksz9031_of_config(struct phy_device *phydev)
215{
216 return 0;
217}
218
219static int ksz9031_center_flp_timing(struct phy_device *phydev)
220{
221 return 0;
222}
Marek Vasut1005ce52015-12-05 17:41:58 +0100223#endif
224
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700225/*
226 * KSZ9021
227 */
Troy Kisky80b6b092012-02-07 14:08:48 +0000228int ksz9021_phy_extended_write(struct phy_device *phydev, int regnum, u16 val)
229{
230 /* extended registers */
231 phy_write(phydev, MDIO_DEVAD_NONE,
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700232 MII_KSZ9021_EXTENDED_CTRL, regnum | 0x8000);
Troy Kisky80b6b092012-02-07 14:08:48 +0000233 return phy_write(phydev, MDIO_DEVAD_NONE,
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700234 MII_KSZ9021_EXTENDED_DATAW, val);
Troy Kisky80b6b092012-02-07 14:08:48 +0000235}
236
237int ksz9021_phy_extended_read(struct phy_device *phydev, int regnum)
238{
239 /* extended registers */
240 phy_write(phydev, MDIO_DEVAD_NONE, MII_KSZ9021_EXTENDED_CTRL, regnum);
241 return phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ9021_EXTENDED_DATAR);
242}
243
Stefano Babica8aa2992013-09-02 15:42:31 +0200244
245static int ksz9021_phy_extread(struct phy_device *phydev, int addr, int devaddr,
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700246 int regnum)
Stefano Babica8aa2992013-09-02 15:42:31 +0200247{
248 return ksz9021_phy_extended_read(phydev, regnum);
249}
250
251static int ksz9021_phy_extwrite(struct phy_device *phydev, int addr,
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700252 int devaddr, int regnum, u16 val)
Stefano Babica8aa2992013-09-02 15:42:31 +0200253{
254 return ksz9021_phy_extended_write(phydev, regnum, val);
255}
256
Troy Kisky80b6b092012-02-07 14:08:48 +0000257static int ksz9021_config(struct phy_device *phydev)
258{
259 unsigned ctrl1000 = 0;
260 const unsigned master = CTRL1000_PREFER_MASTER |
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700261 CTRL1000_CONFIG_MASTER | CTRL1000_MANUAL_CONFIG;
Troy Kisky80b6b092012-02-07 14:08:48 +0000262 unsigned features = phydev->drv->features;
Marek Vasut1005ce52015-12-05 17:41:58 +0100263 int ret;
264
265 ret = ksz9021_of_config(phydev);
266 if (ret)
267 return ret;
Troy Kisky80b6b092012-02-07 14:08:48 +0000268
Simon Glass64b723f2017-08-03 12:22:12 -0600269 if (env_get("disable_giga"))
Troy Kisky80b6b092012-02-07 14:08:48 +0000270 features &= ~(SUPPORTED_1000baseT_Half |
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700271 SUPPORTED_1000baseT_Full);
Troy Kisky80b6b092012-02-07 14:08:48 +0000272 /* force master mode for 1000BaseT due to chip errata */
273 if (features & SUPPORTED_1000baseT_Half)
274 ctrl1000 |= ADVERTISE_1000HALF | master;
275 if (features & SUPPORTED_1000baseT_Full)
276 ctrl1000 |= ADVERTISE_1000FULL | master;
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700277 phydev->advertising = features;
278 phydev->supported = features;
Troy Kisky80b6b092012-02-07 14:08:48 +0000279 phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, ctrl1000);
280 genphy_config_aneg(phydev);
281 genphy_restart_aneg(phydev);
282 return 0;
283}
284
Troy Kisky80b6b092012-02-07 14:08:48 +0000285static struct phy_driver ksz9021_driver = {
286 .name = "Micrel ksz9021",
287 .uid = 0x221610,
James Byrnebc292c22019-03-06 12:48:27 +0000288 .mask = 0xfffffe,
Troy Kisky80b6b092012-02-07 14:08:48 +0000289 .features = PHY_GBIT_FEATURES,
290 .config = &ksz9021_config,
David Andreyf0d83c42013-02-06 22:18:37 +0100291 .startup = &ksz90xx_startup,
Troy Kisky80b6b092012-02-07 14:08:48 +0000292 .shutdown = &genphy_shutdown,
Stefano Babica8aa2992013-09-02 15:42:31 +0200293 .writeext = &ksz9021_phy_extwrite,
294 .readext = &ksz9021_phy_extread,
Troy Kisky80b6b092012-02-07 14:08:48 +0000295};
296
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700297/*
David Andreyf0d83c42013-02-06 22:18:37 +0100298 * KSZ9031
299 */
SARTRE Leoeaf68ac2013-04-30 16:57:25 +0200300int ksz9031_phy_extended_write(struct phy_device *phydev,
301 int devaddr, int regnum, u16 mode, u16 val)
302{
303 /*select register addr for mmd*/
304 phy_write(phydev, MDIO_DEVAD_NONE,
305 MII_KSZ9031_MMD_ACCES_CTRL, devaddr);
306 /*select register for mmd*/
307 phy_write(phydev, MDIO_DEVAD_NONE,
308 MII_KSZ9031_MMD_REG_DATA, regnum);
309 /*setup mode*/
310 phy_write(phydev, MDIO_DEVAD_NONE,
311 MII_KSZ9031_MMD_ACCES_CTRL, (mode | devaddr));
312 /*write the value*/
313 return phy_write(phydev, MDIO_DEVAD_NONE,
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700314 MII_KSZ9031_MMD_REG_DATA, val);
SARTRE Leoeaf68ac2013-04-30 16:57:25 +0200315}
316
317int ksz9031_phy_extended_read(struct phy_device *phydev, int devaddr,
318 int regnum, u16 mode)
319{
320 phy_write(phydev, MDIO_DEVAD_NONE,
321 MII_KSZ9031_MMD_ACCES_CTRL, devaddr);
322 phy_write(phydev, MDIO_DEVAD_NONE,
323 MII_KSZ9031_MMD_REG_DATA, regnum);
324 phy_write(phydev, MDIO_DEVAD_NONE,
325 MII_KSZ9031_MMD_ACCES_CTRL, (devaddr | mode));
326 return phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZ9031_MMD_REG_DATA);
327}
328
Stefano Babica8aa2992013-09-02 15:42:31 +0200329static int ksz9031_phy_extread(struct phy_device *phydev, int addr, int devaddr,
330 int regnum)
331{
332 return ksz9031_phy_extended_read(phydev, devaddr, regnum,
333 MII_KSZ9031_MOD_DATA_NO_POST_INC);
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700334}
Stefano Babica8aa2992013-09-02 15:42:31 +0200335
336static int ksz9031_phy_extwrite(struct phy_device *phydev, int addr,
337 int devaddr, int regnum, u16 val)
338{
339 return ksz9031_phy_extended_write(phydev, devaddr, regnum,
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700340 MII_KSZ9031_MOD_DATA_POST_INC_RW, val);
341}
Stefano Babica8aa2992013-09-02 15:42:31 +0200342
Marek Vasut1005ce52015-12-05 17:41:58 +0100343static int ksz9031_config(struct phy_device *phydev)
344{
345 int ret;
Sebastien Bourdelin3a6e0332017-07-28 15:59:22 -0400346
Marek Vasut1005ce52015-12-05 17:41:58 +0100347 ret = ksz9031_of_config(phydev);
348 if (ret)
349 return ret;
Ash Charles3f55bb62016-10-21 17:31:33 -0400350 ret = ksz9031_center_flp_timing(phydev);
351 if (ret)
352 return ret;
Sebastien Bourdelin3a6e0332017-07-28 15:59:22 -0400353
354 /* add an option to disable the gigabit feature of this PHY */
Simon Glass64b723f2017-08-03 12:22:12 -0600355 if (env_get("disable_giga")) {
Sebastien Bourdelin3a6e0332017-07-28 15:59:22 -0400356 unsigned features;
357 unsigned bmcr;
358
359 /* disable speed 1000 in features supported by the PHY */
360 features = phydev->drv->features;
361 features &= ~(SUPPORTED_1000baseT_Half |
362 SUPPORTED_1000baseT_Full);
363 phydev->advertising = phydev->supported = features;
364
365 /* disable speed 1000 in Basic Control Register */
366 bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
367 bmcr &= ~(1 << 6);
368 phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr);
369
370 /* disable speed 1000 in 1000Base-T Control Register */
371 phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, 0);
372
373 /* start autoneg */
374 genphy_config_aneg(phydev);
375 genphy_restart_aneg(phydev);
376
377 return 0;
378 }
379
Marek Vasut1005ce52015-12-05 17:41:58 +0100380 return genphy_config(phydev);
381}
Stefano Babica8aa2992013-09-02 15:42:31 +0200382
David Andreyf0d83c42013-02-06 22:18:37 +0100383static struct phy_driver ksz9031_driver = {
384 .name = "Micrel ksz9031",
385 .uid = 0x221620,
Stefano Babicd9e36ad2013-09-02 15:42:29 +0200386 .mask = 0xfffff0,
David Andreyf0d83c42013-02-06 22:18:37 +0100387 .features = PHY_GBIT_FEATURES,
Marek Vasut1005ce52015-12-05 17:41:58 +0100388 .config = &ksz9031_config,
David Andreyf0d83c42013-02-06 22:18:37 +0100389 .startup = &ksz90xx_startup,
390 .shutdown = &genphy_shutdown,
Stefano Babica8aa2992013-09-02 15:42:31 +0200391 .writeext = &ksz9031_phy_extwrite,
392 .readext = &ksz9031_phy_extread,
David Andreyf0d83c42013-02-06 22:18:37 +0100393};
394
Alexandru Gagniuc757bb672017-07-07 11:36:57 -0700395int phy_micrel_ksz90x1_init(void)
Andy Fleming60ca78b2011-04-07 21:56:05 -0500396{
Troy Kisky80b6b092012-02-07 14:08:48 +0000397 phy_register(&ksz9021_driver);
David Andreyf0d83c42013-02-06 22:18:37 +0100398 phy_register(&ksz9031_driver);
Andy Fleming60ca78b2011-04-07 21:56:05 -0500399 return 0;
400}