1 /* $OpenBSD: ytphy.c,v 1.6 2024/03/12 16:26:46 kettenis Exp $ */
2 /*
3 * Copyright (c) 2001 Theo de Raadt
4 * Copyright (c) 2023 Mark Kettenis <kettenis@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/socket.h>
23 #include <sys/errno.h>
24
25 #include <net/if.h>
26 #include <net/if_var.h>
27 #include <net/if_media.h>
28
29 #include <dev/mii/mii.h>
30 #include <dev/mii/miivar.h>
31 #include <dev/mii/miidevs.h>
32
33 #ifdef __HAVE_FDT
34 #include <machine/fdt.h>
35 #include <dev/ofw/openfirm.h>
36 #endif
37
38 #define MII_MODEL_MOTORCOMM_YT8511 0x10
39 #define MII_MODEL_MOTORCOMM_YT8521 0x11
40
41 #define YT8511_REG_ADDR 0x1e
42 #define YT8511_REG_DATA 0x1f
43
44 #define YT8511_EXT_CLK_GATE 0x0c
45 #define YT8511_TX_CLK_DELAY_SEL_MASK (0xf << 4)
46 #define YT8511_TX_CLK_DELAY_SEL_EN (0xf << 4)
47 #define YT8511_TX_CLK_DELAY_SEL_DIS (0x2 << 4)
48 #define YT8511_CLK_25M_SEL_MASK (0x3 << 1)
49 #define YT8511_CLK_25M_SEL_125M (0x3 << 1)
50 #define YT8511_RX_CLK_DELAY_EN (1 << 0)
51 #define YT8511_EXT_DELAY_DRIVE 0x0d
52 #define YT8511_TXC_DELAY_SEL_FE_MASK (0xf << 12)
53 #define YT8511_TXC_DELAY_SEL_FE_EN (0xf << 12)
54 #define YT8511_TXC_DELAY_SEL_FE_DIS (0x2 << 12)
55 #define YT8511_EXT_SLEEP_CTRL 0x27
56 #define YT8511_PLL_ON_IN_SLEEP (1 << 14)
57
58 #define YT8521_EXT_CHIP_CONFIG 0xa001
59 #define YT8521_RXC_DLY_EN (1 << 8)
60 #define YT8521_CFG_LDO_MASK (0x3 << 4)
61 #define YT8521_CFG_LDO_3V3 (0x0 << 4)
62 #define YT8521_CFG_LDO_2V5 (0x1 << 4)
63 #define YT8521_CFG_LDO_1V8 (0x2 << 4) /* or 0x3 */
64 #define YT8521_EXT_RGMII_CONFIG1 0xa003
65 #define YT8521_TX_CLK_SEL (1 << 14)
66 #define YT8521_RX_DELAY_SEL_MASK (0xf << 10)
67 #define YT8521_RX_DELAY_SEL_SHIFT 10
68 #define YT8521_TX_DELAY_SEL_MASK (0xf << 0)
69 #define YT8521_TX_DELAY_SEL_SHIFT 0
70 #define YT8521_EXT_PAD_DRIVE_STRENGTH 0xa010
71 #define YT8531_RGMII_RXC_DS_MASK (0x7 << 13)
72 #define YT8531_RGMII_RXC_DS_SHIFT 13
73 #define YT8531_RGMII_RXD_DS_MASK ((0x1 << 12) | (0x3 << 4))
74 #define YT8531_RGMII_RXD_DS_LOW(x) (((x) & 0x3) << 4)
75 #define YT8531_RGMII_RXD_DS_HIGH(x) (((x) >> 2) << 12)
76 #define YT8521_EXT_SYNCE_CFG 0xa012
77 #define YT8531_EN_SYNC_E (1 << 6)
78 #define YT8531_CLK_FRE_SEL_125M (1 << 4)
79 #define YT8531_CLK_SRC_SEL_MASK (0x7 << 1)
80 #define YT8531_CLK_SRC_SEL_PLL_125M (0x0 << 1)
81 #define YT8531_CLK_SRC_SEL_REF_25M (0x4 << 1)
82
83 int ytphy_match(struct device *, void *, void *);
84 void ytphy_attach(struct device *, struct device *, void *);
85
86 const struct cfattach ytphy_ca = {
87 sizeof(struct mii_softc), ytphy_match, ytphy_attach, mii_phy_detach
88 };
89
90 struct cfdriver ytphy_cd = {
91 NULL, "ytphy", DV_DULL
92 };
93
94 int ytphy_service(struct mii_softc *, struct mii_data *, int);
95 void ytphy_yt8511_init(struct mii_softc *);
96 void ytphy_yt8521_init(struct mii_softc *);
97 void ytphy_yt8521_update(struct mii_softc *);
98 void ytphy_yt8531_init(struct mii_softc *);
99
100 const struct mii_phy_funcs ytphy_funcs = {
101 ytphy_service, ukphy_status, mii_phy_reset,
102 };
103
104 static const struct mii_phydesc ytphys[] = {
105 { MII_OUI_MOTORCOMM, MII_MODEL_MOTORCOMM_YT8531,
106 MII_STR_MOTORCOMM_YT8531 },
107 { 0, 0,
108 NULL },
109 };
110
111 int
ytphy_match(struct device * parent,void * match,void * aux)112 ytphy_match(struct device *parent, void *match, void *aux)
113 {
114 struct mii_attach_args *ma = aux;
115
116 /*
117 * The MotorComm YT8511 and TY8521 have bogus MII OUIs, so
118 * match the complete ID including the rev.
119 */
120 if (ma->mii_id1 == 0x0000 && ma->mii_id2 == 0x010a)
121 return (10);
122 if (ma->mii_id1 == 0x0000 && ma->mii_id2 == 0x011a)
123 return (10);
124
125 if (mii_phy_match(ma, ytphys) != NULL)
126 return (10);
127
128 return (0);
129 }
130
131 void
ytphy_attach(struct device * parent,struct device * self,void * aux)132 ytphy_attach(struct device *parent, struct device *self, void *aux)
133 {
134 struct mii_softc *sc = (struct mii_softc *)self;
135 struct mii_attach_args *ma = aux;
136 struct mii_data *mii = ma->mii_data;
137 const struct mii_phydesc *mpd;
138
139 mpd = mii_phy_match(ma, ytphys);
140
141 if (mpd)
142 printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
143 else if (ma->mii_id1 == 0x0000 && ma->mii_id2 == 0x010a)
144 printf(": YT8511 10/100/1000 PHY\n");
145 else
146 printf(": YT8521 10/100/1000 PHY\n");
147
148 sc->mii_inst = mii->mii_instance;
149 sc->mii_phy = ma->mii_phyno;
150 sc->mii_funcs = &ytphy_funcs;
151 sc->mii_oui = MII_OUI(ma->mii_id1, ma->mii_id2);
152 sc->mii_model = MII_MODEL(ma->mii_id2);
153 sc->mii_pdata = mii;
154 sc->mii_flags = ma->mii_flags;
155
156 if (ma->mii_id1 == 0x0000 && ma->mii_id2 == 0x010a)
157 ytphy_yt8511_init(sc);
158 else if (ma->mii_id1 == 0x0000 && ma->mii_id2 == 0x011a)
159 ytphy_yt8521_init(sc);
160 else
161 ytphy_yt8531_init(sc);
162
163 sc->mii_capabilities =
164 PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
165 if (sc->mii_capabilities & BMSR_EXTSTAT)
166 sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
167 if ((sc->mii_capabilities & BMSR_MEDIAMASK) ||
168 (sc->mii_extcapabilities & EXTSR_MEDIAMASK))
169 mii_phy_add_media(sc);
170
171 PHY_RESET(sc);
172 }
173
174 int
ytphy_service(struct mii_softc * sc,struct mii_data * mii,int cmd)175 ytphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
176 {
177 struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
178 int reg;
179
180 if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
181 return (ENXIO);
182
183 switch (cmd) {
184 case MII_POLLSTAT:
185 /*
186 * If we're not polling our PHY instance, just return.
187 */
188 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
189 return (0);
190 break;
191
192 case MII_MEDIACHG:
193 /*
194 * If the media indicates a different PHY instance,
195 * isolate ourselves.
196 */
197 if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
198 reg = PHY_READ(sc, MII_BMCR);
199 PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
200 return (0);
201 }
202
203 /*
204 * If the interface is not up, don't do anything.
205 */
206 if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
207 break;
208
209 mii_phy_setmedia(sc);
210 break;
211
212 case MII_TICK:
213 /*
214 * If we're not currently selected, just return.
215 */
216 if (IFM_INST(ife->ifm_media) != sc->mii_inst)
217 return (0);
218
219 if (mii_phy_tick(sc) == EJUSTRETURN)
220 return (0);
221 break;
222
223 case MII_DOWN:
224 mii_phy_down(sc);
225 return (0);
226 }
227
228 /* Update the media status. */
229 mii_phy_status(sc);
230
231 /* Callback if something changed. */
232 if (sc->mii_model != MII_MODEL_MOTORCOMM_YT8511)
233 ytphy_yt8521_update(sc);
234 mii_phy_update(sc, cmd);
235 return (0);
236 }
237
238 void
ytphy_yt8511_init(struct mii_softc * sc)239 ytphy_yt8511_init(struct mii_softc *sc)
240 {
241 uint16_t tx_clk_delay_sel;
242 uint16_t rx_clk_delay_en;
243 uint16_t txc_delay_sel_fe;
244 uint16_t addr, data;
245
246 if (sc->mii_flags & MIIF_RXID)
247 rx_clk_delay_en = YT8511_RX_CLK_DELAY_EN;
248 else
249 rx_clk_delay_en = 0;
250
251 if (sc->mii_flags & MIIF_TXID) {
252 tx_clk_delay_sel = YT8511_TX_CLK_DELAY_SEL_EN;
253 txc_delay_sel_fe = YT8511_TXC_DELAY_SEL_FE_EN;
254 } else {
255 tx_clk_delay_sel = YT8511_TX_CLK_DELAY_SEL_DIS;
256 txc_delay_sel_fe = YT8511_TXC_DELAY_SEL_FE_DIS;
257 }
258
259 /* Save address register. */
260 addr = PHY_READ(sc, YT8511_REG_ADDR);
261
262 PHY_WRITE(sc, YT8511_REG_ADDR, YT8511_EXT_CLK_GATE);
263 data = PHY_READ(sc, YT8511_REG_DATA);
264 data &= ~YT8511_TX_CLK_DELAY_SEL_MASK;
265 data &= ~YT8511_RX_CLK_DELAY_EN;
266 data &= ~YT8511_CLK_25M_SEL_MASK;
267 data |= tx_clk_delay_sel;
268 data |= rx_clk_delay_en;
269 data |= YT8511_CLK_25M_SEL_125M;
270 PHY_WRITE(sc, YT8511_REG_DATA, data);
271
272 PHY_WRITE(sc, YT8511_REG_ADDR, YT8511_EXT_DELAY_DRIVE);
273 data = PHY_READ(sc, YT8511_REG_DATA);
274 data &= ~YT8511_TXC_DELAY_SEL_FE_MASK;
275 data |= txc_delay_sel_fe;
276 PHY_WRITE(sc, YT8511_REG_DATA, data);
277
278 PHY_WRITE(sc, YT8511_REG_ADDR, YT8511_EXT_SLEEP_CTRL);
279 data = PHY_READ(sc, YT8511_REG_DATA);
280 data |= YT8511_PLL_ON_IN_SLEEP;
281 PHY_WRITE(sc, YT8511_REG_DATA, data);
282
283 /* Restore address register. */
284 PHY_WRITE(sc, YT8511_REG_ADDR, addr);
285 }
286
287 void
ytphy_yt8521_init(struct mii_softc * sc)288 ytphy_yt8521_init(struct mii_softc *sc)
289 {
290 uint32_t rx_delay = 1950;
291 uint32_t tx_delay = 1950;
292 int rx_delay_en = 0;
293 uint16_t addr, data;
294
295 #ifdef __HAVE_FDT
296 if (sc->mii_pdata->mii_node) {
297 rx_delay = OF_getpropint(sc->mii_pdata->mii_node,
298 "rx-internal-delay-ps", rx_delay);
299 tx_delay = OF_getpropint(sc->mii_pdata->mii_node,
300 "tx-internal-delay-ps", tx_delay);
301 }
302 #endif
303
304 /* Save address register. */
305 addr = PHY_READ(sc, YT8511_REG_ADDR);
306
307 if ((sc->mii_flags & MIIF_RXID) == 0)
308 rx_delay = 0;
309 if ((sc->mii_flags & MIIF_TXID) == 0)
310 tx_delay = 0;
311
312 if (rx_delay >= 1900 && ((rx_delay - 1900) % 150) == 0) {
313 rx_delay -= 1900;
314 rx_delay_en = 1;
315 }
316
317 PHY_WRITE(sc, YT8511_REG_ADDR, YT8521_EXT_CHIP_CONFIG);
318 data = PHY_READ(sc, YT8511_REG_DATA);
319 if (rx_delay_en)
320 data |= YT8521_RXC_DLY_EN;
321 else
322 data &= ~YT8521_RXC_DLY_EN;
323 PHY_WRITE(sc, YT8511_REG_DATA, data);
324
325 PHY_WRITE(sc, YT8511_REG_ADDR, YT8521_EXT_RGMII_CONFIG1);
326 data = PHY_READ(sc, YT8511_REG_DATA);
327 data &= ~YT8521_RX_DELAY_SEL_MASK;
328 data |= (((rx_delay + 75) / 150) << YT8521_RX_DELAY_SEL_SHIFT);
329 data &= ~YT8521_TX_DELAY_SEL_MASK;
330 data |= (((tx_delay + 75) / 150) << YT8521_TX_DELAY_SEL_SHIFT);
331 PHY_WRITE(sc, YT8511_REG_DATA, data);
332
333 /* Restore address register. */
334 PHY_WRITE(sc, YT8511_REG_ADDR, addr);
335 }
336
337 void
ytphy_yt8521_update(struct mii_softc * sc)338 ytphy_yt8521_update(struct mii_softc *sc)
339 {
340 #ifdef __HAVE_FDT
341 struct mii_data *mii = sc->mii_pdata;
342 int tx_clk_adj_en;
343 int tx_clk_inv = 0;
344 uint16_t addr, data;
345
346 if (sc->mii_media_active == mii->mii_media_active)
347 return;
348
349 tx_clk_adj_en = OF_getpropbool(mii->mii_node,
350 "motorcomm,tx-clk-adj-enabled");
351 if (!tx_clk_adj_en)
352 return;
353
354 switch (IFM_SUBTYPE(mii->mii_media_active)) {
355 case IFM_1000_T:
356 tx_clk_inv = OF_getpropbool(mii->mii_node,
357 "motorcomm,tx-clk-1000-inverted");
358 break;
359 case IFM_100_TX:
360 tx_clk_inv = OF_getpropbool(mii->mii_node,
361 "motorcomm,tx-clk-100-inverted");
362 break;
363 case IFM_10_T:
364 tx_clk_inv = OF_getpropbool(mii->mii_node,
365 "motorcomm,tx-clk-10-inverted");
366 break;
367 }
368
369 /* Save address register. */
370 addr = PHY_READ(sc, YT8511_REG_ADDR);
371
372 PHY_WRITE(sc, YT8511_REG_ADDR, YT8521_EXT_RGMII_CONFIG1);
373 data = PHY_READ(sc, YT8511_REG_DATA);
374 if (tx_clk_inv)
375 data |= YT8521_TX_CLK_SEL;
376 else
377 data &= ~YT8521_TX_CLK_SEL;
378 PHY_WRITE(sc, YT8511_REG_DATA, data);
379
380 /* Restore address register. */
381 PHY_WRITE(sc, YT8511_REG_ADDR, addr);
382 #endif
383 }
384
385 /* The RGMII drive strength depends on the voltage level. */
386
387 struct ytphy_ds_map {
388 uint32_t volt; /* mV */
389 uint16_t amp; /* uA */
390 uint16_t ds;
391 };
392
393 struct ytphy_ds_map ytphy_yt8531_ds_map[] = {
394 { 1800, 1200, 0 },
395 { 1800, 2100, 1 },
396 { 1800, 2700, 2 },
397 { 1800, 2910, 3 },
398 { 1800, 3110, 4 },
399 { 1800, 3600, 5 },
400 { 1800, 3970, 6 },
401 { 1800, 4350, 7 },
402 { 3300, 3070, 0 },
403 { 3300, 4080, 1 },
404 { 3300, 4370, 2 },
405 { 3300, 4680, 3 },
406 { 3300, 5020, 4 },
407 { 3300, 5450, 5 },
408 { 3300, 5740, 6 },
409 { 3300, 6140, 7 },
410 };
411
412 uint32_t
ytphy_yt8531_ds(struct mii_softc * sc,uint32_t volt,uint32_t amp)413 ytphy_yt8531_ds(struct mii_softc *sc, uint32_t volt, uint32_t amp)
414 {
415 int i;
416
417 for (i = 0; i < nitems(ytphy_yt8531_ds_map); i++) {
418 if (ytphy_yt8531_ds_map[i].volt == volt &&
419 ytphy_yt8531_ds_map[i].amp == amp)
420 return ytphy_yt8531_ds_map[i].ds;
421 }
422
423 if (amp) {
424 printf("%s: unknown drive strength (%d uA at %d mV)\n",
425 sc->mii_dev.dv_xname, amp, volt);
426 }
427
428 /* Default drive strength. */
429 return 3;
430 }
431
432 void
ytphy_yt8531_init(struct mii_softc * sc)433 ytphy_yt8531_init(struct mii_softc *sc)
434 {
435 uint32_t rx_clk_drv = 0;
436 uint32_t rx_data_drv = 0;
437 uint32_t clk_out_freq = 0;
438 uint16_t addr, data;
439 uint16_t volt;
440
441 ytphy_yt8521_init(sc);
442
443 #ifdef __HAVE_FDT
444 if (sc->mii_pdata->mii_node) {
445 rx_clk_drv = OF_getpropint(sc->mii_pdata->mii_node,
446 "motorcomm,rx-clk-drv-microamp", 0);
447 rx_data_drv = OF_getpropint(sc->mii_pdata->mii_node,
448 "motorcomm,rx-data-drv-microamp", 0);
449 clk_out_freq = OF_getpropint(sc->mii_pdata->mii_node,
450 "motorcomm,clk-out-frequency-hz", 0);
451 }
452 #endif
453
454 /* Save address register. */
455 addr = PHY_READ(sc, YT8511_REG_ADDR);
456
457 PHY_WRITE(sc, YT8511_REG_ADDR, YT8521_EXT_CHIP_CONFIG);
458 data = PHY_READ(sc, YT8511_REG_DATA);
459 if ((data & YT8521_CFG_LDO_MASK) == YT8521_CFG_LDO_3V3)
460 volt = 3300;
461 else if ((data & YT8521_CFG_LDO_MASK) == YT8521_CFG_LDO_2V5)
462 volt = 2500;
463 else
464 volt = 1800;
465
466 rx_clk_drv = ytphy_yt8531_ds(sc, volt, rx_clk_drv);
467 rx_data_drv = ytphy_yt8531_ds(sc, volt, rx_data_drv);
468
469 PHY_WRITE(sc, YT8511_REG_ADDR, YT8521_EXT_PAD_DRIVE_STRENGTH);
470 data = PHY_READ(sc, YT8511_REG_DATA);
471 data &= ~YT8531_RGMII_RXC_DS_MASK;
472 data |= rx_clk_drv << YT8531_RGMII_RXC_DS_SHIFT;
473 data &= ~YT8531_RGMII_RXD_DS_MASK;
474 data |= YT8531_RGMII_RXD_DS_LOW(rx_data_drv);
475 data |= YT8531_RGMII_RXD_DS_HIGH(rx_data_drv);
476 PHY_WRITE(sc, YT8511_REG_DATA, data);
477
478 PHY_WRITE(sc, YT8511_REG_ADDR, YT8521_EXT_SYNCE_CFG);
479 data = PHY_READ(sc, YT8511_REG_DATA);
480 switch (clk_out_freq) {
481 case 125000000:
482 data |= YT8531_EN_SYNC_E;
483 data |= YT8531_CLK_FRE_SEL_125M;
484 data &= ~YT8531_CLK_SRC_SEL_MASK;
485 data |= YT8531_CLK_SRC_SEL_PLL_125M;
486 break;
487 case 25000000:
488 data |= YT8531_EN_SYNC_E;
489 data &= ~YT8531_CLK_FRE_SEL_125M;
490 data &= ~YT8531_CLK_SRC_SEL_MASK;
491 data |= YT8531_CLK_SRC_SEL_REF_25M;
492 break;
493 default:
494 data &= ~YT8531_EN_SYNC_E;
495 break;
496 }
497 PHY_WRITE(sc, YT8511_REG_DATA, data);
498
499 /* Restore address register. */
500 PHY_WRITE(sc, YT8511_REG_ADDR, addr);
501 }
502