xref: /openbsd/sys/dev/mii/ytphy.c (revision 9d7a05f9)
1*9d7a05f9Skettenis /*	$OpenBSD: ytphy.c,v 1.6 2024/03/12 16:26:46 kettenis Exp $	*/
2b3fbd974Skettenis /*
3b3fbd974Skettenis  * Copyright (c) 2001 Theo de Raadt
48b04391dSkettenis  * Copyright (c) 2023 Mark Kettenis <kettenis@openbsd.org>
5b3fbd974Skettenis  *
6b3fbd974Skettenis  * Permission to use, copy, modify, and distribute this software for any
7b3fbd974Skettenis  * purpose with or without fee is hereby granted, provided that the above
8b3fbd974Skettenis  * copyright notice and this permission notice appear in all copies.
9b3fbd974Skettenis  *
10b3fbd974Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11b3fbd974Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12b3fbd974Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13b3fbd974Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14b3fbd974Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15b3fbd974Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16b3fbd974Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17b3fbd974Skettenis  */
18b3fbd974Skettenis 
19b3fbd974Skettenis #include <sys/param.h>
20b3fbd974Skettenis #include <sys/systm.h>
21b3fbd974Skettenis #include <sys/device.h>
22b3fbd974Skettenis #include <sys/socket.h>
23b3fbd974Skettenis #include <sys/errno.h>
24b3fbd974Skettenis 
25b3fbd974Skettenis #include <net/if.h>
26b3fbd974Skettenis #include <net/if_var.h>
27b3fbd974Skettenis #include <net/if_media.h>
28b3fbd974Skettenis 
29b3fbd974Skettenis #include <dev/mii/mii.h>
30b3fbd974Skettenis #include <dev/mii/miivar.h>
312b94700cSkettenis #include <dev/mii/miidevs.h>
322b94700cSkettenis 
332b94700cSkettenis #ifdef __HAVE_FDT
342b94700cSkettenis #include <machine/fdt.h>
352b94700cSkettenis #include <dev/ofw/openfirm.h>
362b94700cSkettenis #endif
372b94700cSkettenis 
382b94700cSkettenis #define MII_MODEL_MOTORCOMM_YT8511	0x10
392b94700cSkettenis #define MII_MODEL_MOTORCOMM_YT8521	0x11
40b3fbd974Skettenis 
41b3fbd974Skettenis #define YT8511_REG_ADDR		0x1e
42b3fbd974Skettenis #define YT8511_REG_DATA		0x1f
43b3fbd974Skettenis 
44b3fbd974Skettenis #define YT8511_EXT_CLK_GATE		0x0c
45b3fbd974Skettenis #define  YT8511_TX_CLK_DELAY_SEL_MASK	(0xf << 4)
46b3fbd974Skettenis #define  YT8511_TX_CLK_DELAY_SEL_EN	(0xf << 4)
47b3fbd974Skettenis #define  YT8511_TX_CLK_DELAY_SEL_DIS	(0x2 << 4)
48b3fbd974Skettenis #define  YT8511_CLK_25M_SEL_MASK	(0x3 << 1)
49b3fbd974Skettenis #define  YT8511_CLK_25M_SEL_125M	(0x3 << 1)
50b3fbd974Skettenis #define  YT8511_RX_CLK_DELAY_EN		(1 << 0)
51b3fbd974Skettenis #define YT8511_EXT_DELAY_DRIVE		0x0d
52b3fbd974Skettenis #define  YT8511_TXC_DELAY_SEL_FE_MASK	(0xf << 12)
53b3fbd974Skettenis #define  YT8511_TXC_DELAY_SEL_FE_EN	(0xf << 12)
54b3fbd974Skettenis #define  YT8511_TXC_DELAY_SEL_FE_DIS	(0x2 << 12)
55b3fbd974Skettenis #define YT8511_EXT_SLEEP_CTRL		0x27
56b3fbd974Skettenis #define  YT8511_PLL_ON_IN_SLEEP		(1 << 14)
57b3fbd974Skettenis 
582b94700cSkettenis #define YT8521_EXT_CHIP_CONFIG		0xa001
592b94700cSkettenis #define  YT8521_RXC_DLY_EN		(1 << 8)
60863a04a5Skettenis #define  YT8521_CFG_LDO_MASK		(0x3 << 4)
61b3a435bdSjsg #define  YT8521_CFG_LDO_3V3		(0x0 << 4)
62b3a435bdSjsg #define  YT8521_CFG_LDO_2V5		(0x1 << 4)
63b3a435bdSjsg #define  YT8521_CFG_LDO_1V8		(0x2 << 4)	/* or 0x3 */
642b94700cSkettenis #define YT8521_EXT_RGMII_CONFIG1	0xa003
652b94700cSkettenis #define  YT8521_TX_CLK_SEL		(1 << 14)
662b94700cSkettenis #define  YT8521_RX_DELAY_SEL_MASK	(0xf << 10)
672b94700cSkettenis #define  YT8521_RX_DELAY_SEL_SHIFT	10
682b94700cSkettenis #define  YT8521_TX_DELAY_SEL_MASK	(0xf << 0)
692b94700cSkettenis #define  YT8521_TX_DELAY_SEL_SHIFT	0
70863a04a5Skettenis #define YT8521_EXT_PAD_DRIVE_STRENGTH	0xa010
71863a04a5Skettenis #define  YT8531_RGMII_RXC_DS_MASK	(0x7 << 13)
72863a04a5Skettenis #define  YT8531_RGMII_RXC_DS_SHIFT	13
73863a04a5Skettenis #define  YT8531_RGMII_RXD_DS_MASK	((0x1 << 12) | (0x3 << 4))
74863a04a5Skettenis #define  YT8531_RGMII_RXD_DS_LOW(x)	(((x) & 0x3) << 4)
75863a04a5Skettenis #define  YT8531_RGMII_RXD_DS_HIGH(x)	(((x) >> 2) << 12)
76*9d7a05f9Skettenis #define YT8521_EXT_SYNCE_CFG		0xa012
77*9d7a05f9Skettenis #define  YT8531_EN_SYNC_E		(1 << 6)
78*9d7a05f9Skettenis #define  YT8531_CLK_FRE_SEL_125M	(1 << 4)
79*9d7a05f9Skettenis #define  YT8531_CLK_SRC_SEL_MASK	(0x7 << 1)
80*9d7a05f9Skettenis #define  YT8531_CLK_SRC_SEL_PLL_125M	(0x0 << 1)
81*9d7a05f9Skettenis #define  YT8531_CLK_SRC_SEL_REF_25M	(0x4 << 1)
822b94700cSkettenis 
83b3fbd974Skettenis int	ytphy_match(struct device *, void *, void *);
84b3fbd974Skettenis void	ytphy_attach(struct device *, struct device *, void *);
85b3fbd974Skettenis 
86b3fbd974Skettenis const struct cfattach ytphy_ca = {
87b3fbd974Skettenis 	sizeof(struct mii_softc), ytphy_match, ytphy_attach, mii_phy_detach
88b3fbd974Skettenis };
89b3fbd974Skettenis 
90b3fbd974Skettenis struct cfdriver ytphy_cd = {
91b3fbd974Skettenis 	NULL, "ytphy", DV_DULL
92b3fbd974Skettenis };
93b3fbd974Skettenis 
94b3fbd974Skettenis int	ytphy_service(struct mii_softc *, struct mii_data *, int);
952b94700cSkettenis void	ytphy_yt8511_init(struct mii_softc *);
962b94700cSkettenis void	ytphy_yt8521_init(struct mii_softc *);
972b94700cSkettenis void	ytphy_yt8521_update(struct mii_softc *);
98863a04a5Skettenis void	ytphy_yt8531_init(struct mii_softc *);
99b3fbd974Skettenis 
100b3fbd974Skettenis const struct mii_phy_funcs ytphy_funcs = {
101b3fbd974Skettenis 	ytphy_service, ukphy_status, mii_phy_reset,
102b3fbd974Skettenis };
103b3fbd974Skettenis 
1042b94700cSkettenis static const struct mii_phydesc ytphys[] = {
1052b94700cSkettenis 	{ MII_OUI_MOTORCOMM,	MII_MODEL_MOTORCOMM_YT8531,
1062b94700cSkettenis 	  MII_STR_MOTORCOMM_YT8531 },
1072b94700cSkettenis 	{ 0,			0,
1082b94700cSkettenis 	  NULL },
1092b94700cSkettenis };
1102b94700cSkettenis 
111b3fbd974Skettenis int
ytphy_match(struct device * parent,void * match,void * aux)112b3fbd974Skettenis ytphy_match(struct device *parent, void *match, void *aux)
113b3fbd974Skettenis {
114b3fbd974Skettenis 	struct mii_attach_args *ma = aux;
115b3fbd974Skettenis 
116b3fbd974Skettenis 	/*
1172b94700cSkettenis 	 * The MotorComm YT8511 and TY8521 have bogus MII OUIs, so
1182b94700cSkettenis 	 * match the complete ID including the rev.
119b3fbd974Skettenis 	 */
120b3fbd974Skettenis 	if (ma->mii_id1 == 0x0000 && ma->mii_id2 == 0x010a)
121b3fbd974Skettenis 		return (10);
1222b94700cSkettenis 	if (ma->mii_id1 == 0x0000 && ma->mii_id2 == 0x011a)
1232b94700cSkettenis 		return (10);
1242b94700cSkettenis 
1252b94700cSkettenis 	if (mii_phy_match(ma, ytphys) != NULL)
1262b94700cSkettenis 		return (10);
127b3fbd974Skettenis 
128b3fbd974Skettenis 	return (0);
129b3fbd974Skettenis }
130b3fbd974Skettenis 
131b3fbd974Skettenis void
ytphy_attach(struct device * parent,struct device * self,void * aux)132b3fbd974Skettenis ytphy_attach(struct device *parent, struct device *self, void *aux)
133b3fbd974Skettenis {
134b3fbd974Skettenis 	struct mii_softc *sc = (struct mii_softc *)self;
135b3fbd974Skettenis 	struct mii_attach_args *ma = aux;
136b3fbd974Skettenis 	struct mii_data *mii = ma->mii_data;
1372b94700cSkettenis 	const struct mii_phydesc *mpd;
138b3fbd974Skettenis 
1392b94700cSkettenis 	mpd = mii_phy_match(ma, ytphys);
1402b94700cSkettenis 
1412b94700cSkettenis 	if (mpd)
1422b94700cSkettenis 		printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2));
1432b94700cSkettenis 	else if (ma->mii_id1 == 0x0000 && ma->mii_id2 == 0x010a)
144b3fbd974Skettenis 		printf(": YT8511 10/100/1000 PHY\n");
1452b94700cSkettenis 	else
1462b94700cSkettenis 		printf(": YT8521 10/100/1000 PHY\n");
147b3fbd974Skettenis 
148b3fbd974Skettenis 	sc->mii_inst = mii->mii_instance;
149b3fbd974Skettenis 	sc->mii_phy = ma->mii_phyno;
150b3fbd974Skettenis 	sc->mii_funcs = &ytphy_funcs;
151b3fbd974Skettenis 	sc->mii_oui = MII_OUI(ma->mii_id1, ma->mii_id2);
152b3fbd974Skettenis 	sc->mii_model = MII_MODEL(ma->mii_id2);
153b3fbd974Skettenis 	sc->mii_pdata = mii;
154b3fbd974Skettenis 	sc->mii_flags = ma->mii_flags;
155b3fbd974Skettenis 
1562b94700cSkettenis 	if (ma->mii_id1 == 0x0000 && ma->mii_id2 == 0x010a)
1572b94700cSkettenis 		ytphy_yt8511_init(sc);
158863a04a5Skettenis 	else if (ma->mii_id1 == 0x0000 && ma->mii_id2 == 0x011a)
1592b94700cSkettenis 		ytphy_yt8521_init(sc);
160863a04a5Skettenis 	else
161863a04a5Skettenis 		ytphy_yt8531_init(sc);
162b3fbd974Skettenis 
163b3fbd974Skettenis 	sc->mii_capabilities =
164b3fbd974Skettenis 	    PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
165b3fbd974Skettenis 	if (sc->mii_capabilities & BMSR_EXTSTAT)
166b3fbd974Skettenis 		sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR);
167b3fbd974Skettenis 	if ((sc->mii_capabilities & BMSR_MEDIAMASK) ||
168b3fbd974Skettenis 	    (sc->mii_extcapabilities & EXTSR_MEDIAMASK))
169b3fbd974Skettenis 		mii_phy_add_media(sc);
170b3fbd974Skettenis 
171b3fbd974Skettenis 	PHY_RESET(sc);
172b3fbd974Skettenis }
173b3fbd974Skettenis 
174b3fbd974Skettenis int
ytphy_service(struct mii_softc * sc,struct mii_data * mii,int cmd)175b3fbd974Skettenis ytphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
176b3fbd974Skettenis {
177b3fbd974Skettenis 	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
178b3fbd974Skettenis 	int reg;
179b3fbd974Skettenis 
180b3fbd974Skettenis 	if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0)
181b3fbd974Skettenis 		return (ENXIO);
182b3fbd974Skettenis 
183b3fbd974Skettenis 	switch (cmd) {
184b3fbd974Skettenis 	case MII_POLLSTAT:
185b3fbd974Skettenis 		/*
186b3fbd974Skettenis 		 * If we're not polling our PHY instance, just return.
187b3fbd974Skettenis 		 */
188b3fbd974Skettenis 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
189b3fbd974Skettenis 			return (0);
190b3fbd974Skettenis 		break;
191b3fbd974Skettenis 
192b3fbd974Skettenis 	case MII_MEDIACHG:
193b3fbd974Skettenis 		/*
194b3fbd974Skettenis 		 * If the media indicates a different PHY instance,
195b3fbd974Skettenis 		 * isolate ourselves.
196b3fbd974Skettenis 		 */
197b3fbd974Skettenis 		if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
198b3fbd974Skettenis 			reg = PHY_READ(sc, MII_BMCR);
199b3fbd974Skettenis 			PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
200b3fbd974Skettenis 			return (0);
201b3fbd974Skettenis 		}
202b3fbd974Skettenis 
203b3fbd974Skettenis 		/*
204b3fbd974Skettenis 		 * If the interface is not up, don't do anything.
205b3fbd974Skettenis 		 */
206b3fbd974Skettenis 		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
207b3fbd974Skettenis 			break;
208b3fbd974Skettenis 
209b3fbd974Skettenis 		mii_phy_setmedia(sc);
210b3fbd974Skettenis 		break;
211b3fbd974Skettenis 
212b3fbd974Skettenis 	case MII_TICK:
213b3fbd974Skettenis 		/*
214b3fbd974Skettenis 		 * If we're not currently selected, just return.
215b3fbd974Skettenis 		 */
216b3fbd974Skettenis 		if (IFM_INST(ife->ifm_media) != sc->mii_inst)
217b3fbd974Skettenis 			return (0);
218b3fbd974Skettenis 
219b3fbd974Skettenis 		if (mii_phy_tick(sc) == EJUSTRETURN)
220b3fbd974Skettenis 			return (0);
221b3fbd974Skettenis 		break;
222b3fbd974Skettenis 
223b3fbd974Skettenis 	case MII_DOWN:
224b3fbd974Skettenis 		mii_phy_down(sc);
225b3fbd974Skettenis 		return (0);
226b3fbd974Skettenis 	}
227b3fbd974Skettenis 
228b3fbd974Skettenis 	/* Update the media status. */
229b3fbd974Skettenis 	mii_phy_status(sc);
230b3fbd974Skettenis 
231b3fbd974Skettenis 	/* Callback if something changed. */
2322b94700cSkettenis 	if (sc->mii_model != MII_MODEL_MOTORCOMM_YT8511)
2332b94700cSkettenis 		ytphy_yt8521_update(sc);
234b3fbd974Skettenis 	mii_phy_update(sc, cmd);
235b3fbd974Skettenis 	return (0);
236b3fbd974Skettenis }
2372b94700cSkettenis 
2382b94700cSkettenis void
ytphy_yt8511_init(struct mii_softc * sc)2392b94700cSkettenis ytphy_yt8511_init(struct mii_softc *sc)
2402b94700cSkettenis {
2412b94700cSkettenis 	uint16_t tx_clk_delay_sel;
2422b94700cSkettenis 	uint16_t rx_clk_delay_en;
2432b94700cSkettenis 	uint16_t txc_delay_sel_fe;
2442b94700cSkettenis 	uint16_t addr, data;
2452b94700cSkettenis 
2462b94700cSkettenis 	if (sc->mii_flags & MIIF_RXID)
2472b94700cSkettenis 		rx_clk_delay_en = YT8511_RX_CLK_DELAY_EN;
2482b94700cSkettenis 	else
2492b94700cSkettenis 		rx_clk_delay_en = 0;
2502b94700cSkettenis 
2512b94700cSkettenis 	if (sc->mii_flags & MIIF_TXID) {
2522b94700cSkettenis 		tx_clk_delay_sel = YT8511_TX_CLK_DELAY_SEL_EN;
2532b94700cSkettenis 		txc_delay_sel_fe = YT8511_TXC_DELAY_SEL_FE_EN;
2542b94700cSkettenis 	} else {
2552b94700cSkettenis 		tx_clk_delay_sel = YT8511_TX_CLK_DELAY_SEL_DIS;
2562b94700cSkettenis 		txc_delay_sel_fe = YT8511_TXC_DELAY_SEL_FE_DIS;
2572b94700cSkettenis 	}
2582b94700cSkettenis 
2592b94700cSkettenis 	/* Save address register. */
2602b94700cSkettenis 	addr = PHY_READ(sc, YT8511_REG_ADDR);
2612b94700cSkettenis 
2622b94700cSkettenis 	PHY_WRITE(sc, YT8511_REG_ADDR, YT8511_EXT_CLK_GATE);
2632b94700cSkettenis 	data = PHY_READ(sc, YT8511_REG_DATA);
2642b94700cSkettenis 	data &= ~YT8511_TX_CLK_DELAY_SEL_MASK;
2652b94700cSkettenis 	data &= ~YT8511_RX_CLK_DELAY_EN;
2662b94700cSkettenis 	data &= ~YT8511_CLK_25M_SEL_MASK;
2672b94700cSkettenis 	data |= tx_clk_delay_sel;
2682b94700cSkettenis 	data |= rx_clk_delay_en;
2692b94700cSkettenis 	data |= YT8511_CLK_25M_SEL_125M;
2702b94700cSkettenis 	PHY_WRITE(sc, YT8511_REG_DATA, data);
2712b94700cSkettenis 
2722b94700cSkettenis 	PHY_WRITE(sc, YT8511_REG_ADDR, YT8511_EXT_DELAY_DRIVE);
2732b94700cSkettenis 	data = PHY_READ(sc, YT8511_REG_DATA);
2742b94700cSkettenis 	data &= ~YT8511_TXC_DELAY_SEL_FE_MASK;
2752b94700cSkettenis 	data |= txc_delay_sel_fe;
2762b94700cSkettenis 	PHY_WRITE(sc, YT8511_REG_DATA, data);
2772b94700cSkettenis 
2782b94700cSkettenis 	PHY_WRITE(sc, YT8511_REG_ADDR, YT8511_EXT_SLEEP_CTRL);
2792b94700cSkettenis 	data = PHY_READ(sc, YT8511_REG_DATA);
2802b94700cSkettenis 	data |= YT8511_PLL_ON_IN_SLEEP;
2812b94700cSkettenis 	PHY_WRITE(sc, YT8511_REG_DATA, data);
2822b94700cSkettenis 
2832b94700cSkettenis 	/* Restore address register. */
2842b94700cSkettenis 	PHY_WRITE(sc, YT8511_REG_ADDR, addr);
2852b94700cSkettenis }
2862b94700cSkettenis 
2872b94700cSkettenis void
ytphy_yt8521_init(struct mii_softc * sc)2882b94700cSkettenis ytphy_yt8521_init(struct mii_softc *sc)
2892b94700cSkettenis {
2902b94700cSkettenis 	uint32_t rx_delay = 1950;
2912b94700cSkettenis 	uint32_t tx_delay = 1950;
2922b94700cSkettenis 	int rx_delay_en = 0;
2932b94700cSkettenis 	uint16_t addr, data;
2942b94700cSkettenis 
2952b94700cSkettenis #ifdef __HAVE_FDT
2962b94700cSkettenis 	if (sc->mii_pdata->mii_node) {
2972b94700cSkettenis 		rx_delay = OF_getpropint(sc->mii_pdata->mii_node,
2982b94700cSkettenis 		    "rx-internal-delay-ps", rx_delay);
2992b94700cSkettenis 		tx_delay = OF_getpropint(sc->mii_pdata->mii_node,
3002b94700cSkettenis 		    "tx-internal-delay-ps", tx_delay);
3012b94700cSkettenis 	}
3022b94700cSkettenis #endif
3032b94700cSkettenis 
3042b94700cSkettenis 	/* Save address register. */
3052b94700cSkettenis 	addr = PHY_READ(sc, YT8511_REG_ADDR);
3062b94700cSkettenis 
3072b94700cSkettenis 	if ((sc->mii_flags & MIIF_RXID) == 0)
3082b94700cSkettenis 		rx_delay = 0;
3092b94700cSkettenis 	if ((sc->mii_flags & MIIF_TXID) == 0)
3102b94700cSkettenis 		tx_delay = 0;
3112b94700cSkettenis 
3122b94700cSkettenis 	if (rx_delay >= 1900 && ((rx_delay - 1900) % 150) == 0) {
3132b94700cSkettenis 		rx_delay -= 1900;
3142b94700cSkettenis 		rx_delay_en = 1;
3152b94700cSkettenis 	}
3162b94700cSkettenis 
3172b94700cSkettenis 	PHY_WRITE(sc, YT8511_REG_ADDR, YT8521_EXT_CHIP_CONFIG);
3182b94700cSkettenis 	data = PHY_READ(sc, YT8511_REG_DATA);
3192b94700cSkettenis 	if (rx_delay_en)
3202b94700cSkettenis 		data |= YT8521_RXC_DLY_EN;
3212b94700cSkettenis 	else
3222b94700cSkettenis 		data &= ~YT8521_RXC_DLY_EN;
3232b94700cSkettenis 	PHY_WRITE(sc, YT8511_REG_DATA, data);
3242b94700cSkettenis 
3252b94700cSkettenis 	PHY_WRITE(sc, YT8511_REG_ADDR, YT8521_EXT_RGMII_CONFIG1);
3262b94700cSkettenis 	data = PHY_READ(sc, YT8511_REG_DATA);
3272b94700cSkettenis 	data &= ~YT8521_RX_DELAY_SEL_MASK;
3282b94700cSkettenis 	data |= (((rx_delay + 75) / 150) << YT8521_RX_DELAY_SEL_SHIFT);
3292b94700cSkettenis 	data &= ~YT8521_TX_DELAY_SEL_MASK;
3302b94700cSkettenis 	data |= (((tx_delay + 75) / 150) << YT8521_TX_DELAY_SEL_SHIFT);
3312b94700cSkettenis 	PHY_WRITE(sc, YT8511_REG_DATA, data);
3322b94700cSkettenis 
3332b94700cSkettenis 	/* Restore address register. */
3342b94700cSkettenis 	PHY_WRITE(sc, YT8511_REG_ADDR, addr);
3352b94700cSkettenis }
3362b94700cSkettenis 
3372b94700cSkettenis void
ytphy_yt8521_update(struct mii_softc * sc)3382b94700cSkettenis ytphy_yt8521_update(struct mii_softc *sc)
3392b94700cSkettenis {
3402b94700cSkettenis #ifdef __HAVE_FDT
3412b94700cSkettenis 	struct mii_data *mii = sc->mii_pdata;
3422b94700cSkettenis 	int tx_clk_adj_en;
3432b94700cSkettenis 	int tx_clk_inv = 0;
3442b94700cSkettenis 	uint16_t addr, data;
3452b94700cSkettenis 
3462b94700cSkettenis 	if (sc->mii_media_active == mii->mii_media_active)
3472b94700cSkettenis 		return;
3482b94700cSkettenis 
3492b94700cSkettenis 	tx_clk_adj_en = OF_getpropbool(mii->mii_node,
3502b94700cSkettenis 	    "motorcomm,tx-clk-adj-enabled");
3512b94700cSkettenis 	if (!tx_clk_adj_en)
3522b94700cSkettenis 		return;
3532b94700cSkettenis 
3542b94700cSkettenis 	switch (IFM_SUBTYPE(mii->mii_media_active)) {
3552b94700cSkettenis 	case IFM_1000_T:
3562b94700cSkettenis 		tx_clk_inv = OF_getpropbool(mii->mii_node,
3572b94700cSkettenis 		    "motorcomm,tx-clk-1000-inverted");
3582b94700cSkettenis 		break;
3592b94700cSkettenis 	case IFM_100_TX:
3602b94700cSkettenis 		tx_clk_inv = OF_getpropbool(mii->mii_node,
3612b94700cSkettenis 		    "motorcomm,tx-clk-100-inverted");
3622b94700cSkettenis 		break;
3632b94700cSkettenis 	case IFM_10_T:
3642b94700cSkettenis 		tx_clk_inv = OF_getpropbool(mii->mii_node,
3652b94700cSkettenis 		    "motorcomm,tx-clk-10-inverted");
3662b94700cSkettenis 		break;
3672b94700cSkettenis 	}
3682b94700cSkettenis 
3692b94700cSkettenis 	/* Save address register. */
3702b94700cSkettenis 	addr = PHY_READ(sc, YT8511_REG_ADDR);
3712b94700cSkettenis 
3722b94700cSkettenis 	PHY_WRITE(sc, YT8511_REG_ADDR, YT8521_EXT_RGMII_CONFIG1);
3732b94700cSkettenis 	data = PHY_READ(sc, YT8511_REG_DATA);
3742b94700cSkettenis 	if (tx_clk_inv)
3752b94700cSkettenis 		data |= YT8521_TX_CLK_SEL;
3762b94700cSkettenis 	else
3772b94700cSkettenis 		data &= ~YT8521_TX_CLK_SEL;
3782b94700cSkettenis 	PHY_WRITE(sc, YT8511_REG_DATA, data);
3792b94700cSkettenis 
3802b94700cSkettenis 	/* Restore address register. */
3812b94700cSkettenis 	PHY_WRITE(sc, YT8511_REG_ADDR, addr);
3822b94700cSkettenis #endif
3832b94700cSkettenis }
384863a04a5Skettenis 
385863a04a5Skettenis /* The RGMII drive strength depends on the voltage level. */
386863a04a5Skettenis 
387863a04a5Skettenis struct ytphy_ds_map {
388863a04a5Skettenis 	uint32_t volt;		/* mV */
389863a04a5Skettenis 	uint16_t amp;		/* uA */
390863a04a5Skettenis 	uint16_t ds;
391863a04a5Skettenis };
392863a04a5Skettenis 
393863a04a5Skettenis struct ytphy_ds_map ytphy_yt8531_ds_map[] = {
394863a04a5Skettenis 	{ 1800, 1200, 0 },
395863a04a5Skettenis 	{ 1800, 2100, 1 },
396863a04a5Skettenis 	{ 1800, 2700, 2 },
397863a04a5Skettenis 	{ 1800, 2910, 3 },
398863a04a5Skettenis 	{ 1800, 3110, 4 },
399863a04a5Skettenis 	{ 1800, 3600, 5 },
400863a04a5Skettenis 	{ 1800, 3970, 6 },
401863a04a5Skettenis 	{ 1800, 4350, 7 },
402863a04a5Skettenis 	{ 3300, 3070, 0 },
403863a04a5Skettenis 	{ 3300, 4080, 1 },
404863a04a5Skettenis 	{ 3300, 4370, 2 },
405863a04a5Skettenis 	{ 3300, 4680, 3 },
406863a04a5Skettenis 	{ 3300, 5020, 4 },
407863a04a5Skettenis 	{ 3300, 5450, 5 },
408863a04a5Skettenis 	{ 3300, 5740, 6 },
409863a04a5Skettenis 	{ 3300, 6140, 7 },
410863a04a5Skettenis };
411863a04a5Skettenis 
412863a04a5Skettenis uint32_t
ytphy_yt8531_ds(struct mii_softc * sc,uint32_t volt,uint32_t amp)413863a04a5Skettenis ytphy_yt8531_ds(struct mii_softc *sc, uint32_t volt, uint32_t amp)
414863a04a5Skettenis {
415863a04a5Skettenis 	int i;
416863a04a5Skettenis 
417863a04a5Skettenis 	for (i = 0; i < nitems(ytphy_yt8531_ds_map); i++) {
418863a04a5Skettenis 		if (ytphy_yt8531_ds_map[i].volt == volt &&
419863a04a5Skettenis 		    ytphy_yt8531_ds_map[i].amp == amp)
420863a04a5Skettenis 			return ytphy_yt8531_ds_map[i].ds;
421863a04a5Skettenis 	}
422863a04a5Skettenis 
423863a04a5Skettenis 	if (amp) {
424863a04a5Skettenis 		printf("%s: unknown drive strength (%d uA at %d mV)\n",
425863a04a5Skettenis 		    sc->mii_dev.dv_xname, amp, volt);
426863a04a5Skettenis 	}
427863a04a5Skettenis 
428863a04a5Skettenis 	/* Default drive strength. */
429863a04a5Skettenis 	return 3;
430863a04a5Skettenis }
431863a04a5Skettenis 
432863a04a5Skettenis void
ytphy_yt8531_init(struct mii_softc * sc)433863a04a5Skettenis ytphy_yt8531_init(struct mii_softc *sc)
434863a04a5Skettenis {
435863a04a5Skettenis 	uint32_t rx_clk_drv = 0;
436863a04a5Skettenis 	uint32_t rx_data_drv = 0;
437*9d7a05f9Skettenis 	uint32_t clk_out_freq = 0;
438863a04a5Skettenis 	uint16_t addr, data;
439863a04a5Skettenis 	uint16_t volt;
440863a04a5Skettenis 
441863a04a5Skettenis 	ytphy_yt8521_init(sc);
442863a04a5Skettenis 
443863a04a5Skettenis #ifdef __HAVE_FDT
444863a04a5Skettenis 	if (sc->mii_pdata->mii_node) {
445863a04a5Skettenis 		rx_clk_drv = OF_getpropint(sc->mii_pdata->mii_node,
446863a04a5Skettenis 		    "motorcomm,rx-clk-drv-microamp", 0);
447863a04a5Skettenis 		rx_data_drv = OF_getpropint(sc->mii_pdata->mii_node,
448863a04a5Skettenis 		    "motorcomm,rx-data-drv-microamp", 0);
449*9d7a05f9Skettenis 		clk_out_freq = OF_getpropint(sc->mii_pdata->mii_node,
450*9d7a05f9Skettenis 		    "motorcomm,clk-out-frequency-hz", 0);
451863a04a5Skettenis 	}
452863a04a5Skettenis #endif
453863a04a5Skettenis 
454863a04a5Skettenis 	/* Save address register. */
455863a04a5Skettenis 	addr = PHY_READ(sc, YT8511_REG_ADDR);
456863a04a5Skettenis 
457863a04a5Skettenis 	PHY_WRITE(sc, YT8511_REG_ADDR, YT8521_EXT_CHIP_CONFIG);
458863a04a5Skettenis 	data = PHY_READ(sc, YT8511_REG_DATA);
459863a04a5Skettenis 	if ((data & YT8521_CFG_LDO_MASK) == YT8521_CFG_LDO_3V3)
460863a04a5Skettenis 		volt = 3300;
461863a04a5Skettenis 	else if ((data & YT8521_CFG_LDO_MASK) == YT8521_CFG_LDO_2V5)
462863a04a5Skettenis 		volt = 2500;
463863a04a5Skettenis 	else
464863a04a5Skettenis 		volt = 1800;
465863a04a5Skettenis 
466863a04a5Skettenis 	rx_clk_drv = ytphy_yt8531_ds(sc, volt, rx_clk_drv);
467863a04a5Skettenis 	rx_data_drv = ytphy_yt8531_ds(sc, volt, rx_data_drv);
468863a04a5Skettenis 
469863a04a5Skettenis 	PHY_WRITE(sc, YT8511_REG_ADDR, YT8521_EXT_PAD_DRIVE_STRENGTH);
470863a04a5Skettenis 	data = PHY_READ(sc, YT8511_REG_DATA);
471863a04a5Skettenis 	data &= ~YT8531_RGMII_RXC_DS_MASK;
472863a04a5Skettenis 	data |= rx_clk_drv << YT8531_RGMII_RXC_DS_SHIFT;
473863a04a5Skettenis 	data &= ~YT8531_RGMII_RXD_DS_MASK;
474863a04a5Skettenis 	data |= YT8531_RGMII_RXD_DS_LOW(rx_data_drv);
475863a04a5Skettenis 	data |= YT8531_RGMII_RXD_DS_HIGH(rx_data_drv);
476863a04a5Skettenis 	PHY_WRITE(sc, YT8511_REG_DATA, data);
477863a04a5Skettenis 
478*9d7a05f9Skettenis 	PHY_WRITE(sc, YT8511_REG_ADDR, YT8521_EXT_SYNCE_CFG);
479*9d7a05f9Skettenis 	data = PHY_READ(sc, YT8511_REG_DATA);
480*9d7a05f9Skettenis 	switch (clk_out_freq) {
481*9d7a05f9Skettenis 	case 125000000:
482*9d7a05f9Skettenis 		data |= YT8531_EN_SYNC_E;
483*9d7a05f9Skettenis 		data |= YT8531_CLK_FRE_SEL_125M;
484*9d7a05f9Skettenis 		data &= ~YT8531_CLK_SRC_SEL_MASK;
485*9d7a05f9Skettenis 		data |= YT8531_CLK_SRC_SEL_PLL_125M;
486*9d7a05f9Skettenis 		break;
487*9d7a05f9Skettenis 	case 25000000:
488*9d7a05f9Skettenis 		data |= YT8531_EN_SYNC_E;
489*9d7a05f9Skettenis 		data &= ~YT8531_CLK_FRE_SEL_125M;
490*9d7a05f9Skettenis 		data &= ~YT8531_CLK_SRC_SEL_MASK;
491*9d7a05f9Skettenis 		data |= YT8531_CLK_SRC_SEL_REF_25M;
492*9d7a05f9Skettenis 		break;
493*9d7a05f9Skettenis 	default:
494*9d7a05f9Skettenis 		data &= ~YT8531_EN_SYNC_E;
495*9d7a05f9Skettenis 		break;
496*9d7a05f9Skettenis 	}
497*9d7a05f9Skettenis 	PHY_WRITE(sc, YT8511_REG_DATA, data);
498*9d7a05f9Skettenis 
499863a04a5Skettenis 	/* Restore address register. */
500863a04a5Skettenis 	PHY_WRITE(sc, YT8511_REG_ADDR, addr);
501863a04a5Skettenis }
502