xref: /freebsd/sys/dev/vnic/thunder_mdio.c (revision 441d67800c37883bc6433e00b39d6114695a7905)
1*441d6780SZbigniew Bodek /*-
2*441d6780SZbigniew Bodek  * Copyright (c) 2015 The FreeBSD Foundation
3*441d6780SZbigniew Bodek  * All rights reserved.
4*441d6780SZbigniew Bodek  *
5*441d6780SZbigniew Bodek  * This software was developed by Semihalf under
6*441d6780SZbigniew Bodek  * the sponsorship of the FreeBSD Foundation.
7*441d6780SZbigniew Bodek  *
8*441d6780SZbigniew Bodek  * Redistribution and use in source and binary forms, with or without
9*441d6780SZbigniew Bodek  * modification, are permitted provided that the following conditions
10*441d6780SZbigniew Bodek  * are met:
11*441d6780SZbigniew Bodek  * 1. Redistributions of source code must retain the above copyright
12*441d6780SZbigniew Bodek  *    notice, this list of conditions and the following disclaimer.
13*441d6780SZbigniew Bodek  * 2. Redistributions in binary form must reproduce the above copyright
14*441d6780SZbigniew Bodek  *    notice, this list of conditions and the following disclaimer in the
15*441d6780SZbigniew Bodek  *    documentation and/or other materials provided with the distribution.
16*441d6780SZbigniew Bodek  *
17*441d6780SZbigniew Bodek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18*441d6780SZbigniew Bodek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*441d6780SZbigniew Bodek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*441d6780SZbigniew Bodek  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21*441d6780SZbigniew Bodek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22*441d6780SZbigniew Bodek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23*441d6780SZbigniew Bodek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24*441d6780SZbigniew Bodek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25*441d6780SZbigniew Bodek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26*441d6780SZbigniew Bodek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27*441d6780SZbigniew Bodek  * SUCH DAMAGE.
28*441d6780SZbigniew Bodek  */
29*441d6780SZbigniew Bodek 
30*441d6780SZbigniew Bodek #include <sys/cdefs.h>
31*441d6780SZbigniew Bodek __FBSDID("$FreeBSD$");
32*441d6780SZbigniew Bodek 
33*441d6780SZbigniew Bodek #include <sys/param.h>
34*441d6780SZbigniew Bodek #include <sys/systm.h>
35*441d6780SZbigniew Bodek #include <sys/bus.h>
36*441d6780SZbigniew Bodek #include <sys/kernel.h>
37*441d6780SZbigniew Bodek #include <sys/module.h>
38*441d6780SZbigniew Bodek #include <sys/resource.h>
39*441d6780SZbigniew Bodek #include <sys/rman.h>
40*441d6780SZbigniew Bodek #include <sys/socket.h>
41*441d6780SZbigniew Bodek #include <sys/queue.h>
42*441d6780SZbigniew Bodek 
43*441d6780SZbigniew Bodek #include <machine/bus.h>
44*441d6780SZbigniew Bodek #include <machine/resource.h>
45*441d6780SZbigniew Bodek 
46*441d6780SZbigniew Bodek #include <net/if.h>
47*441d6780SZbigniew Bodek #include <net/if_media.h>
48*441d6780SZbigniew Bodek #include <net/if_types.h>
49*441d6780SZbigniew Bodek #include <net/if_var.h>
50*441d6780SZbigniew Bodek 
51*441d6780SZbigniew Bodek #include <dev/mii/mii.h>
52*441d6780SZbigniew Bodek #include <dev/mii/miivar.h>
53*441d6780SZbigniew Bodek 
54*441d6780SZbigniew Bodek #include "thunder_mdio_var.h"
55*441d6780SZbigniew Bodek 
56*441d6780SZbigniew Bodek #include "lmac_if.h"
57*441d6780SZbigniew Bodek #include "miibus_if.h"
58*441d6780SZbigniew Bodek 
59*441d6780SZbigniew Bodek #define	REG_BASE_RID	0
60*441d6780SZbigniew Bodek 
61*441d6780SZbigniew Bodek #define	SMI_CMD				0x00
62*441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_REG_ADR_SHIFT	(0)
63*441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_REG_ADR_MASK	(0x1FUL << SMI_CMD_PHY_REG_ADR_SHIFT)
64*441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_ADR_SHIFT		(8)
65*441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_ADR_MASK		(0x1FUL << SMI_CMD_PHY_ADR_SHIFT)
66*441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_OP_MASK		(0x3UL << 16)
67*441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_OP_C22_READ	(0x1UL << 16)
68*441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_OP_C22_WRITE	(0x0UL << 16)
69*441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_OP_C45_READ	(0x3UL << 16)
70*441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_OP_C45_WRITE	(0x1UL << 16)
71*441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_OP_C45_ADDR	(0x0UL << 16)
72*441d6780SZbigniew Bodek 
73*441d6780SZbigniew Bodek #define	SMI_WR_DAT			0x08
74*441d6780SZbigniew Bodek #define	 SMI_WR_DAT_PENDING		(1UL << 17)
75*441d6780SZbigniew Bodek #define	 SMI_WR_DAT_VAL			(1UL << 16)
76*441d6780SZbigniew Bodek #define	 SMI_WR_DAT_DAT_MASK		(0xFFFFUL << 0)
77*441d6780SZbigniew Bodek 
78*441d6780SZbigniew Bodek #define	SMI_RD_DAT			0x10
79*441d6780SZbigniew Bodek #define	 SMI_RD_DAT_PENDING		(1UL << 17)
80*441d6780SZbigniew Bodek #define	 SMI_RD_DAT_VAL			(1UL << 16)
81*441d6780SZbigniew Bodek #define	 SMI_RD_DAT_DAT_MASK		(0xFFFFUL << 0)
82*441d6780SZbigniew Bodek 
83*441d6780SZbigniew Bodek #define	SMI_CLK				0x18
84*441d6780SZbigniew Bodek #define	 SMI_CLK_PREAMBLE		(1UL << 12)
85*441d6780SZbigniew Bodek #define	 SMI_CLK_MODE			(1UL << 24)
86*441d6780SZbigniew Bodek 
87*441d6780SZbigniew Bodek #define	SMI_EN				0x20
88*441d6780SZbigniew Bodek #define	 SMI_EN_EN			(1UL << 0)	/* Enabele interface */
89*441d6780SZbigniew Bodek 
90*441d6780SZbigniew Bodek #define	SMI_DRV_CTL			0x28
91*441d6780SZbigniew Bodek 
92*441d6780SZbigniew Bodek static int thunder_mdio_detach(device_t);
93*441d6780SZbigniew Bodek 
94*441d6780SZbigniew Bodek static int thunder_mdio_read(device_t, int, int);
95*441d6780SZbigniew Bodek static int thunder_mdio_write(device_t, int, int, int);
96*441d6780SZbigniew Bodek 
97*441d6780SZbigniew Bodek static int thunder_ifmedia_change_stub(struct ifnet *);
98*441d6780SZbigniew Bodek static void thunder_ifmedia_status_stub(struct ifnet *, struct ifmediareq *);
99*441d6780SZbigniew Bodek 
100*441d6780SZbigniew Bodek static int thunder_mdio_media_status(device_t, int, int *, int *, int *);
101*441d6780SZbigniew Bodek static int thunder_mdio_media_change(device_t, int, int, int, int);
102*441d6780SZbigniew Bodek static int thunder_mdio_phy_connect(device_t, int, int);
103*441d6780SZbigniew Bodek static int thunder_mdio_phy_disconnect(device_t, int, int);
104*441d6780SZbigniew Bodek 
105*441d6780SZbigniew Bodek static device_method_t thunder_mdio_methods[] = {
106*441d6780SZbigniew Bodek 	/* Device interface */
107*441d6780SZbigniew Bodek 	DEVMETHOD(device_detach,	thunder_mdio_detach),
108*441d6780SZbigniew Bodek 	/* LMAC interface */
109*441d6780SZbigniew Bodek 	DEVMETHOD(lmac_media_status,	thunder_mdio_media_status),
110*441d6780SZbigniew Bodek 	DEVMETHOD(lmac_media_change,	thunder_mdio_media_change),
111*441d6780SZbigniew Bodek 	DEVMETHOD(lmac_phy_connect,	thunder_mdio_phy_connect),
112*441d6780SZbigniew Bodek 	DEVMETHOD(lmac_phy_disconnect,	thunder_mdio_phy_disconnect),
113*441d6780SZbigniew Bodek 	/* MII interface */
114*441d6780SZbigniew Bodek 	DEVMETHOD(miibus_readreg,	thunder_mdio_read),
115*441d6780SZbigniew Bodek 	DEVMETHOD(miibus_writereg,	thunder_mdio_write),
116*441d6780SZbigniew Bodek 
117*441d6780SZbigniew Bodek 	/* End */
118*441d6780SZbigniew Bodek 	DEVMETHOD_END
119*441d6780SZbigniew Bodek };
120*441d6780SZbigniew Bodek 
121*441d6780SZbigniew Bodek DEFINE_CLASS_0(thunder_mdio, thunder_mdio_driver, thunder_mdio_methods,
122*441d6780SZbigniew Bodek     sizeof(struct thunder_mdio_softc));
123*441d6780SZbigniew Bodek 
124*441d6780SZbigniew Bodek DRIVER_MODULE(miibus, thunder_mdio, miibus_driver, miibus_devclass, 0, 0);
125*441d6780SZbigniew Bodek MODULE_DEPEND(thunder_mdio, ether, 1, 1, 1);
126*441d6780SZbigniew Bodek MODULE_DEPEND(thunder_mdio, miibus, 1, 1, 1);
127*441d6780SZbigniew Bodek 
128*441d6780SZbigniew Bodek MALLOC_DEFINE(M_THUNDER_MDIO, "ThunderX MDIO",
129*441d6780SZbigniew Bodek     "Cavium ThunderX MDIO dynamic memory");
130*441d6780SZbigniew Bodek 
131*441d6780SZbigniew Bodek #define	MDIO_LOCK_INIT(sc, name)			\
132*441d6780SZbigniew Bodek     mtx_init(&(sc)->mtx, name, NULL, MTX_DEF)
133*441d6780SZbigniew Bodek 
134*441d6780SZbigniew Bodek #define	MDIO_LOCK_DESTROY(sc)				\
135*441d6780SZbigniew Bodek     mtx_destroy(&(sc)->mtx)
136*441d6780SZbigniew Bodek 
137*441d6780SZbigniew Bodek #define	MDIO_LOCK(sc)	mtx_lock(&(sc)->mtx)
138*441d6780SZbigniew Bodek #define	MDIO_UNLOCK(sc)	mtx_unlock(&(sc)->mtx)
139*441d6780SZbigniew Bodek 
140*441d6780SZbigniew Bodek #define	MDIO_LOCK_ASSERT(sc)				\
141*441d6780SZbigniew Bodek     mtx_assert(&(sc)->mtx, MA_OWNED)
142*441d6780SZbigniew Bodek 
143*441d6780SZbigniew Bodek 
144*441d6780SZbigniew Bodek #define	mdio_reg_read(sc, reg)				\
145*441d6780SZbigniew Bodek     bus_read_8((sc)->reg_base, (reg))
146*441d6780SZbigniew Bodek 
147*441d6780SZbigniew Bodek #define	mdio_reg_write(sc, reg, val)			\
148*441d6780SZbigniew Bodek     bus_write_8((sc)->reg_base, (reg), (val))
149*441d6780SZbigniew Bodek 
150*441d6780SZbigniew Bodek int
151*441d6780SZbigniew Bodek thunder_mdio_attach(device_t dev)
152*441d6780SZbigniew Bodek {
153*441d6780SZbigniew Bodek 	struct thunder_mdio_softc *sc;
154*441d6780SZbigniew Bodek 	int rid;
155*441d6780SZbigniew Bodek 
156*441d6780SZbigniew Bodek 	sc = device_get_softc(dev);
157*441d6780SZbigniew Bodek 	sc->dev = dev;
158*441d6780SZbigniew Bodek 
159*441d6780SZbigniew Bodek 	/* Allocate memory resources */
160*441d6780SZbigniew Bodek 	rid = REG_BASE_RID;
161*441d6780SZbigniew Bodek 	sc->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
162*441d6780SZbigniew Bodek 	    RF_ACTIVE);
163*441d6780SZbigniew Bodek 	if (sc->reg_base == NULL) {
164*441d6780SZbigniew Bodek 		device_printf(dev, "Could not allocate memory\n");
165*441d6780SZbigniew Bodek 		return (ENXIO);
166*441d6780SZbigniew Bodek 	}
167*441d6780SZbigniew Bodek 
168*441d6780SZbigniew Bodek 	TAILQ_INIT(&sc->phy_desc_head);
169*441d6780SZbigniew Bodek 	MDIO_LOCK_INIT(sc, "ThunderX MDIO lock");
170*441d6780SZbigniew Bodek 
171*441d6780SZbigniew Bodek 	/* Enable SMI/MDIO interface */
172*441d6780SZbigniew Bodek 	mdio_reg_write(sc, SMI_EN, SMI_EN_EN);
173*441d6780SZbigniew Bodek 
174*441d6780SZbigniew Bodek 	return (0);
175*441d6780SZbigniew Bodek }
176*441d6780SZbigniew Bodek 
177*441d6780SZbigniew Bodek static int
178*441d6780SZbigniew Bodek thunder_mdio_detach(device_t dev)
179*441d6780SZbigniew Bodek {
180*441d6780SZbigniew Bodek 	struct thunder_mdio_softc *sc;
181*441d6780SZbigniew Bodek 
182*441d6780SZbigniew Bodek 	sc = device_get_softc(dev);
183*441d6780SZbigniew Bodek 
184*441d6780SZbigniew Bodek 	if (sc->reg_base != NULL) {
185*441d6780SZbigniew Bodek 		bus_release_resource(dev, SYS_RES_MEMORY, REG_BASE_RID,
186*441d6780SZbigniew Bodek 		    sc->reg_base);
187*441d6780SZbigniew Bodek 	}
188*441d6780SZbigniew Bodek 
189*441d6780SZbigniew Bodek 	return (0);
190*441d6780SZbigniew Bodek }
191*441d6780SZbigniew Bodek 
192*441d6780SZbigniew Bodek static __inline void
193*441d6780SZbigniew Bodek thunder_mdio_set_mode(struct thunder_mdio_softc *sc,
194*441d6780SZbigniew Bodek     enum thunder_mdio_mode mode)
195*441d6780SZbigniew Bodek {
196*441d6780SZbigniew Bodek 	uint64_t smi_clk;
197*441d6780SZbigniew Bodek 
198*441d6780SZbigniew Bodek 	if (sc->mode == mode)
199*441d6780SZbigniew Bodek 		return;
200*441d6780SZbigniew Bodek 
201*441d6780SZbigniew Bodek 	/* Set mode, IEEE CLAUSE 22 or IEEE CAUSE 45 */
202*441d6780SZbigniew Bodek 	smi_clk = mdio_reg_read(sc, SMI_CLK);
203*441d6780SZbigniew Bodek 	if (mode == MODE_IEEE_C22)
204*441d6780SZbigniew Bodek 		smi_clk &= ~SMI_CLK_MODE;
205*441d6780SZbigniew Bodek 	else
206*441d6780SZbigniew Bodek 		smi_clk |= SMI_CLK_MODE;
207*441d6780SZbigniew Bodek 	/* Enable sending 32 bit preable on SMI transactions */
208*441d6780SZbigniew Bodek 	smi_clk |= SMI_CLK_PREAMBLE;
209*441d6780SZbigniew Bodek 	/* Saved setings */
210*441d6780SZbigniew Bodek 	mdio_reg_write(sc, SMI_CLK, smi_clk);
211*441d6780SZbigniew Bodek 	sc->mode = mode;
212*441d6780SZbigniew Bodek }
213*441d6780SZbigniew Bodek 
214*441d6780SZbigniew Bodek static int
215*441d6780SZbigniew Bodek thunder_mdio_c45_addr(struct thunder_mdio_softc *sc, int phy, int reg)
216*441d6780SZbigniew Bodek {
217*441d6780SZbigniew Bodek 	uint64_t smi_cmd, smi_wr_dat;
218*441d6780SZbigniew Bodek 	ssize_t timeout;
219*441d6780SZbigniew Bodek 
220*441d6780SZbigniew Bodek 	thunder_mdio_set_mode(sc, MODE_IEEE_C45);
221*441d6780SZbigniew Bodek 
222*441d6780SZbigniew Bodek 	/* Prepare data for transmission */
223*441d6780SZbigniew Bodek 	mdio_reg_write(sc, SMI_WR_DAT, reg & SMI_WR_DAT_DAT_MASK);
224*441d6780SZbigniew Bodek 	/*
225*441d6780SZbigniew Bodek 	 * Assemble command
226*441d6780SZbigniew Bodek 	 */
227*441d6780SZbigniew Bodek 	smi_cmd = 0;
228*441d6780SZbigniew Bodek 	/* Set opcode */
229*441d6780SZbigniew Bodek 	smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE;
230*441d6780SZbigniew Bodek 
231*441d6780SZbigniew Bodek 	/* Set PHY address */
232*441d6780SZbigniew Bodek 	smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK);
233*441d6780SZbigniew Bodek 	/* Set PHY register offset */
234*441d6780SZbigniew Bodek 	smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) &
235*441d6780SZbigniew Bodek 	    SMI_CMD_PHY_REG_ADR_MASK);
236*441d6780SZbigniew Bodek 
237*441d6780SZbigniew Bodek 	mdio_reg_write(sc, SMI_CMD, smi_cmd);
238*441d6780SZbigniew Bodek 	for (timeout = 1000; timeout > 0; timeout--) {
239*441d6780SZbigniew Bodek 		smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT);
240*441d6780SZbigniew Bodek 		if (smi_wr_dat & SMI_WR_DAT_PENDING)
241*441d6780SZbigniew Bodek 			DELAY(1000);
242*441d6780SZbigniew Bodek 		else
243*441d6780SZbigniew Bodek 			break;
244*441d6780SZbigniew Bodek 	}
245*441d6780SZbigniew Bodek 
246*441d6780SZbigniew Bodek 	if (timeout <= 0)
247*441d6780SZbigniew Bodek 		return (EIO);
248*441d6780SZbigniew Bodek 	else {
249*441d6780SZbigniew Bodek 		/* Return 0 on success */
250*441d6780SZbigniew Bodek 		return (0);
251*441d6780SZbigniew Bodek 	}
252*441d6780SZbigniew Bodek }
253*441d6780SZbigniew Bodek 
254*441d6780SZbigniew Bodek static int
255*441d6780SZbigniew Bodek thunder_mdio_read(device_t dev, int phy, int reg)
256*441d6780SZbigniew Bodek {
257*441d6780SZbigniew Bodek 	struct thunder_mdio_softc *sc;
258*441d6780SZbigniew Bodek 	uint64_t smi_cmd, smi_rd_dat;
259*441d6780SZbigniew Bodek 	ssize_t timeout;
260*441d6780SZbigniew Bodek 	int err;
261*441d6780SZbigniew Bodek 
262*441d6780SZbigniew Bodek 	sc = device_get_softc(dev);
263*441d6780SZbigniew Bodek 
264*441d6780SZbigniew Bodek 	/* XXX Always C22 - for <= 1Gbps only */
265*441d6780SZbigniew Bodek 	thunder_mdio_set_mode(sc, MODE_IEEE_C22);
266*441d6780SZbigniew Bodek 
267*441d6780SZbigniew Bodek 	/*
268*441d6780SZbigniew Bodek 	 * Assemble command
269*441d6780SZbigniew Bodek 	 */
270*441d6780SZbigniew Bodek 	smi_cmd = 0;
271*441d6780SZbigniew Bodek 	/* Set opcode */
272*441d6780SZbigniew Bodek 	if (sc->mode == MODE_IEEE_C22)
273*441d6780SZbigniew Bodek 		smi_cmd |= SMI_CMD_PHY_OP_C22_READ;
274*441d6780SZbigniew Bodek 	else {
275*441d6780SZbigniew Bodek 		smi_cmd |= SMI_CMD_PHY_OP_C45_READ;
276*441d6780SZbigniew Bodek 		err = thunder_mdio_c45_addr(sc, phy, reg);
277*441d6780SZbigniew Bodek 		if (err != 0)
278*441d6780SZbigniew Bodek 			return (err);
279*441d6780SZbigniew Bodek 
280*441d6780SZbigniew Bodek 		reg = (reg >> 16) & 0x1F;
281*441d6780SZbigniew Bodek 	}
282*441d6780SZbigniew Bodek 
283*441d6780SZbigniew Bodek 	/* Set PHY address */
284*441d6780SZbigniew Bodek 	smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK);
285*441d6780SZbigniew Bodek 	/* Set PHY register offset */
286*441d6780SZbigniew Bodek 	smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) &
287*441d6780SZbigniew Bodek 	    SMI_CMD_PHY_REG_ADR_MASK);
288*441d6780SZbigniew Bodek 
289*441d6780SZbigniew Bodek 	mdio_reg_write(sc, SMI_CMD, smi_cmd);
290*441d6780SZbigniew Bodek 	for (timeout = 1000; timeout > 0; timeout--) {
291*441d6780SZbigniew Bodek 		smi_rd_dat = mdio_reg_read(sc, SMI_RD_DAT);
292*441d6780SZbigniew Bodek 		if (smi_rd_dat & SMI_RD_DAT_PENDING)
293*441d6780SZbigniew Bodek 			DELAY(1000);
294*441d6780SZbigniew Bodek 		else
295*441d6780SZbigniew Bodek 			break;
296*441d6780SZbigniew Bodek 	}
297*441d6780SZbigniew Bodek 
298*441d6780SZbigniew Bodek 	if (smi_rd_dat & SMI_RD_DAT_VAL)
299*441d6780SZbigniew Bodek 		return (smi_rd_dat & SMI_RD_DAT_DAT_MASK);
300*441d6780SZbigniew Bodek 	else {
301*441d6780SZbigniew Bodek 		/* Return 0 on error */
302*441d6780SZbigniew Bodek 		return (0);
303*441d6780SZbigniew Bodek 	}
304*441d6780SZbigniew Bodek }
305*441d6780SZbigniew Bodek 
306*441d6780SZbigniew Bodek static int
307*441d6780SZbigniew Bodek thunder_mdio_write(device_t dev, int phy, int reg, int data)
308*441d6780SZbigniew Bodek {
309*441d6780SZbigniew Bodek 	struct thunder_mdio_softc *sc;
310*441d6780SZbigniew Bodek 	uint64_t smi_cmd, smi_wr_dat;
311*441d6780SZbigniew Bodek 	ssize_t timeout;
312*441d6780SZbigniew Bodek 
313*441d6780SZbigniew Bodek 	sc = device_get_softc(dev);
314*441d6780SZbigniew Bodek 
315*441d6780SZbigniew Bodek 	/* XXX Always C22 - for <= 1Gbps only */
316*441d6780SZbigniew Bodek 	thunder_mdio_set_mode(sc, MODE_IEEE_C22);
317*441d6780SZbigniew Bodek 
318*441d6780SZbigniew Bodek 	/* Prepare data for transmission */
319*441d6780SZbigniew Bodek 	mdio_reg_write(sc, SMI_WR_DAT, data & SMI_WR_DAT_DAT_MASK);
320*441d6780SZbigniew Bodek 	/*
321*441d6780SZbigniew Bodek 	 * Assemble command
322*441d6780SZbigniew Bodek 	 */
323*441d6780SZbigniew Bodek 	smi_cmd = 0;
324*441d6780SZbigniew Bodek 	/* Set opcode */
325*441d6780SZbigniew Bodek 	if (sc->mode == MODE_IEEE_C22)
326*441d6780SZbigniew Bodek 		smi_cmd |= SMI_CMD_PHY_OP_C22_WRITE;
327*441d6780SZbigniew Bodek 	else
328*441d6780SZbigniew Bodek 		smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE;
329*441d6780SZbigniew Bodek 
330*441d6780SZbigniew Bodek 	/* Set PHY address */
331*441d6780SZbigniew Bodek 	smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK);
332*441d6780SZbigniew Bodek 	/* Set PHY register offset */
333*441d6780SZbigniew Bodek 	smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) &
334*441d6780SZbigniew Bodek 	    SMI_CMD_PHY_REG_ADR_MASK);
335*441d6780SZbigniew Bodek 
336*441d6780SZbigniew Bodek 	mdio_reg_write(sc, SMI_CMD, smi_cmd);
337*441d6780SZbigniew Bodek 	for (timeout = 1000; timeout > 0; timeout--) {
338*441d6780SZbigniew Bodek 		smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT);
339*441d6780SZbigniew Bodek 		if (smi_wr_dat & SMI_WR_DAT_PENDING)
340*441d6780SZbigniew Bodek 			DELAY(1000);
341*441d6780SZbigniew Bodek 		else
342*441d6780SZbigniew Bodek 			break;
343*441d6780SZbigniew Bodek 	}
344*441d6780SZbigniew Bodek 
345*441d6780SZbigniew Bodek 	if (timeout <= 0)
346*441d6780SZbigniew Bodek 		return (EIO);
347*441d6780SZbigniew Bodek 	else {
348*441d6780SZbigniew Bodek 		/* Return 0 on success */
349*441d6780SZbigniew Bodek 		return (0);
350*441d6780SZbigniew Bodek 	}
351*441d6780SZbigniew Bodek }
352*441d6780SZbigniew Bodek 
353*441d6780SZbigniew Bodek static int
354*441d6780SZbigniew Bodek thunder_ifmedia_change_stub(struct ifnet *ifp __unused)
355*441d6780SZbigniew Bodek {
356*441d6780SZbigniew Bodek 	/* Will never be called by if_media */
357*441d6780SZbigniew Bodek 	return (0);
358*441d6780SZbigniew Bodek }
359*441d6780SZbigniew Bodek 
360*441d6780SZbigniew Bodek static void
361*441d6780SZbigniew Bodek thunder_ifmedia_status_stub(struct ifnet *ifp __unused, struct ifmediareq
362*441d6780SZbigniew Bodek     *ifmr __unused)
363*441d6780SZbigniew Bodek {
364*441d6780SZbigniew Bodek 	/* Will never be called by if_media */
365*441d6780SZbigniew Bodek }
366*441d6780SZbigniew Bodek 
367*441d6780SZbigniew Bodek static __inline struct phy_desc *
368*441d6780SZbigniew Bodek get_phy_desc(struct thunder_mdio_softc *sc, int lmacid)
369*441d6780SZbigniew Bodek {
370*441d6780SZbigniew Bodek 	struct phy_desc *pd = NULL;
371*441d6780SZbigniew Bodek 
372*441d6780SZbigniew Bodek 	MDIO_LOCK_ASSERT(sc);
373*441d6780SZbigniew Bodek 	TAILQ_FOREACH(pd, &sc->phy_desc_head, phy_desc_list) {
374*441d6780SZbigniew Bodek 		if (pd->lmacid == lmacid)
375*441d6780SZbigniew Bodek 			break;
376*441d6780SZbigniew Bodek 	}
377*441d6780SZbigniew Bodek 
378*441d6780SZbigniew Bodek 	return (pd);
379*441d6780SZbigniew Bodek }
380*441d6780SZbigniew Bodek static int
381*441d6780SZbigniew Bodek thunder_mdio_media_status(device_t dev, int lmacid, int *link, int *duplex,
382*441d6780SZbigniew Bodek     int *speed)
383*441d6780SZbigniew Bodek {
384*441d6780SZbigniew Bodek 	struct thunder_mdio_softc *sc;
385*441d6780SZbigniew Bodek 	struct mii_data *mii_sc;
386*441d6780SZbigniew Bodek 	struct phy_desc *pd;
387*441d6780SZbigniew Bodek 
388*441d6780SZbigniew Bodek 	sc = device_get_softc(dev);
389*441d6780SZbigniew Bodek 
390*441d6780SZbigniew Bodek 	MDIO_LOCK(sc);
391*441d6780SZbigniew Bodek 	pd = get_phy_desc(sc, lmacid);
392*441d6780SZbigniew Bodek 	if (pd == NULL) {
393*441d6780SZbigniew Bodek 		/* Panic when invariants are enabled, fail otherwise. */
394*441d6780SZbigniew Bodek 		KASSERT(0, ("%s: no PHY descriptor for LMAC%d",
395*441d6780SZbigniew Bodek 		    __func__, lmacid));
396*441d6780SZbigniew Bodek 		MDIO_UNLOCK(sc);
397*441d6780SZbigniew Bodek 		return (ENXIO);
398*441d6780SZbigniew Bodek 	}
399*441d6780SZbigniew Bodek 	mii_sc = device_get_softc(pd->miibus);
400*441d6780SZbigniew Bodek 
401*441d6780SZbigniew Bodek 	mii_tick(mii_sc);
402*441d6780SZbigniew Bodek 	if ((mii_sc->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
403*441d6780SZbigniew Bodek 	    (IFM_ACTIVE | IFM_AVALID)) {
404*441d6780SZbigniew Bodek 		/* Link is up */
405*441d6780SZbigniew Bodek 		*link = 1;
406*441d6780SZbigniew Bodek 	} else
407*441d6780SZbigniew Bodek 		*link = 0;
408*441d6780SZbigniew Bodek 
409*441d6780SZbigniew Bodek 	switch (IFM_SUBTYPE(mii_sc->mii_media_active)) {
410*441d6780SZbigniew Bodek 	case IFM_10_T:
411*441d6780SZbigniew Bodek 		*speed = 10;
412*441d6780SZbigniew Bodek 		break;
413*441d6780SZbigniew Bodek 	case IFM_100_TX:
414*441d6780SZbigniew Bodek 		*speed = 100;
415*441d6780SZbigniew Bodek 		break;
416*441d6780SZbigniew Bodek 	case IFM_1000_T:
417*441d6780SZbigniew Bodek 		*speed = 1000;
418*441d6780SZbigniew Bodek 		break;
419*441d6780SZbigniew Bodek 	default:
420*441d6780SZbigniew Bodek 		/* IFM_NONE */
421*441d6780SZbigniew Bodek 		*speed = 0;
422*441d6780SZbigniew Bodek 	}
423*441d6780SZbigniew Bodek 
424*441d6780SZbigniew Bodek 	if ((IFM_OPTIONS(mii_sc->mii_media_active) & IFM_FDX) != 0)
425*441d6780SZbigniew Bodek 		*duplex = 1;
426*441d6780SZbigniew Bodek 	else
427*441d6780SZbigniew Bodek 		*duplex = 0;
428*441d6780SZbigniew Bodek 
429*441d6780SZbigniew Bodek 	MDIO_UNLOCK(sc);
430*441d6780SZbigniew Bodek 
431*441d6780SZbigniew Bodek 	return (0);
432*441d6780SZbigniew Bodek }
433*441d6780SZbigniew Bodek 
434*441d6780SZbigniew Bodek static int
435*441d6780SZbigniew Bodek thunder_mdio_media_change(device_t dev, int lmacid, int link, int duplex,
436*441d6780SZbigniew Bodek     int speed)
437*441d6780SZbigniew Bodek {
438*441d6780SZbigniew Bodek 
439*441d6780SZbigniew Bodek 	return (EIO);
440*441d6780SZbigniew Bodek }
441*441d6780SZbigniew Bodek 
442*441d6780SZbigniew Bodek static int
443*441d6780SZbigniew Bodek thunder_mdio_phy_connect(device_t dev, int lmacid, int phy)
444*441d6780SZbigniew Bodek {
445*441d6780SZbigniew Bodek 	struct thunder_mdio_softc *sc;
446*441d6780SZbigniew Bodek 	struct phy_desc *pd;
447*441d6780SZbigniew Bodek 	int err;
448*441d6780SZbigniew Bodek 
449*441d6780SZbigniew Bodek 	sc = device_get_softc(dev);
450*441d6780SZbigniew Bodek 
451*441d6780SZbigniew Bodek 	MDIO_LOCK(sc);
452*441d6780SZbigniew Bodek 	pd = get_phy_desc(sc, lmacid);
453*441d6780SZbigniew Bodek 	MDIO_UNLOCK(sc);
454*441d6780SZbigniew Bodek 	if (pd == NULL) {
455*441d6780SZbigniew Bodek 		pd = malloc(sizeof(*pd), M_THUNDER_MDIO, (M_NOWAIT | M_ZERO));
456*441d6780SZbigniew Bodek 		if (pd == NULL)
457*441d6780SZbigniew Bodek 			return (ENOMEM);
458*441d6780SZbigniew Bodek 		pd->ifp = if_alloc(IFT_ETHER);
459*441d6780SZbigniew Bodek 		if (pd->ifp == NULL) {
460*441d6780SZbigniew Bodek 			free(pd, M_THUNDER_MDIO);
461*441d6780SZbigniew Bodek 			return (ENOMEM);
462*441d6780SZbigniew Bodek 		}
463*441d6780SZbigniew Bodek 		pd->lmacid = lmacid;
464*441d6780SZbigniew Bodek 	}
465*441d6780SZbigniew Bodek 
466*441d6780SZbigniew Bodek 	err = mii_attach(dev, &pd->miibus, pd->ifp,
467*441d6780SZbigniew Bodek 	    thunder_ifmedia_change_stub, thunder_ifmedia_status_stub,
468*441d6780SZbigniew Bodek 	    BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
469*441d6780SZbigniew Bodek 
470*441d6780SZbigniew Bodek 	if (err != 0) {
471*441d6780SZbigniew Bodek 		device_printf(dev, "Could not attach PHY%d\n", phy);
472*441d6780SZbigniew Bodek 		if_free(pd->ifp);
473*441d6780SZbigniew Bodek 		free(pd, M_THUNDER_MDIO);
474*441d6780SZbigniew Bodek 		return (ENXIO);
475*441d6780SZbigniew Bodek 	}
476*441d6780SZbigniew Bodek 
477*441d6780SZbigniew Bodek 	MDIO_LOCK(sc);
478*441d6780SZbigniew Bodek 	TAILQ_INSERT_TAIL(&sc->phy_desc_head, pd, phy_desc_list);
479*441d6780SZbigniew Bodek 	MDIO_UNLOCK(sc);
480*441d6780SZbigniew Bodek 
481*441d6780SZbigniew Bodek 	return (0);
482*441d6780SZbigniew Bodek }
483*441d6780SZbigniew Bodek 
484*441d6780SZbigniew Bodek static int
485*441d6780SZbigniew Bodek thunder_mdio_phy_disconnect(device_t dev, int lmacid, int phy)
486*441d6780SZbigniew Bodek {
487*441d6780SZbigniew Bodek 	struct thunder_mdio_softc *sc;
488*441d6780SZbigniew Bodek 	struct phy_desc *pd;
489*441d6780SZbigniew Bodek 
490*441d6780SZbigniew Bodek 	sc = device_get_softc(dev);
491*441d6780SZbigniew Bodek 	MDIO_LOCK(sc);
492*441d6780SZbigniew Bodek 
493*441d6780SZbigniew Bodek 	pd = get_phy_desc(sc, lmacid);
494*441d6780SZbigniew Bodek 	if (pd == NULL) {
495*441d6780SZbigniew Bodek 		MDIO_UNLOCK(sc);
496*441d6780SZbigniew Bodek 		return (EINVAL);
497*441d6780SZbigniew Bodek 	}
498*441d6780SZbigniew Bodek 
499*441d6780SZbigniew Bodek 	/* Remove this PHY descriptor from the list */
500*441d6780SZbigniew Bodek 	TAILQ_REMOVE(&sc->phy_desc_head, pd, phy_desc_list);
501*441d6780SZbigniew Bodek 
502*441d6780SZbigniew Bodek 	/* Detach miibus */
503*441d6780SZbigniew Bodek 	bus_generic_detach(dev);
504*441d6780SZbigniew Bodek 	device_delete_child(dev, pd->miibus);
505*441d6780SZbigniew Bodek 	/* Free fake ifnet */
506*441d6780SZbigniew Bodek 	if_free(pd->ifp);
507*441d6780SZbigniew Bodek 	/* Free memory under phy descriptor */
508*441d6780SZbigniew Bodek 	free(pd, M_THUNDER_MDIO);
509*441d6780SZbigniew Bodek 	MDIO_UNLOCK(sc);
510*441d6780SZbigniew Bodek 
511*441d6780SZbigniew Bodek 	return (0);
512*441d6780SZbigniew Bodek }
513