xref: /freebsd/sys/dev/vnic/thunder_mdio.c (revision b73763fc10d8000ec5c5682abccf6c8489eee8e3)
1441d6780SZbigniew Bodek /*-
2441d6780SZbigniew Bodek  * Copyright (c) 2015 The FreeBSD Foundation
3441d6780SZbigniew Bodek  *
4441d6780SZbigniew Bodek  * This software was developed by Semihalf under
5441d6780SZbigniew Bodek  * the sponsorship of the FreeBSD Foundation.
6441d6780SZbigniew Bodek  *
7441d6780SZbigniew Bodek  * Redistribution and use in source and binary forms, with or without
8441d6780SZbigniew Bodek  * modification, are permitted provided that the following conditions
9441d6780SZbigniew Bodek  * are met:
10441d6780SZbigniew Bodek  * 1. Redistributions of source code must retain the above copyright
11441d6780SZbigniew Bodek  *    notice, this list of conditions and the following disclaimer.
12441d6780SZbigniew Bodek  * 2. Redistributions in binary form must reproduce the above copyright
13441d6780SZbigniew Bodek  *    notice, this list of conditions and the following disclaimer in the
14441d6780SZbigniew Bodek  *    documentation and/or other materials provided with the distribution.
15441d6780SZbigniew Bodek  *
16441d6780SZbigniew Bodek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17441d6780SZbigniew Bodek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18441d6780SZbigniew Bodek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19441d6780SZbigniew Bodek  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20441d6780SZbigniew Bodek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21441d6780SZbigniew Bodek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22441d6780SZbigniew Bodek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23441d6780SZbigniew Bodek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24441d6780SZbigniew Bodek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25441d6780SZbigniew Bodek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26441d6780SZbigniew Bodek  * SUCH DAMAGE.
27441d6780SZbigniew Bodek  */
28441d6780SZbigniew Bodek 
29441d6780SZbigniew Bodek #include <sys/cdefs.h>
30441d6780SZbigniew Bodek __FBSDID("$FreeBSD$");
31441d6780SZbigniew Bodek 
32441d6780SZbigniew Bodek #include <sys/param.h>
33441d6780SZbigniew Bodek #include <sys/systm.h>
34441d6780SZbigniew Bodek #include <sys/bus.h>
35441d6780SZbigniew Bodek #include <sys/kernel.h>
36441d6780SZbigniew Bodek #include <sys/module.h>
37441d6780SZbigniew Bodek #include <sys/resource.h>
38441d6780SZbigniew Bodek #include <sys/rman.h>
39441d6780SZbigniew Bodek #include <sys/socket.h>
40441d6780SZbigniew Bodek #include <sys/queue.h>
41441d6780SZbigniew Bodek 
42441d6780SZbigniew Bodek #include <machine/bus.h>
43441d6780SZbigniew Bodek #include <machine/resource.h>
44441d6780SZbigniew Bodek 
45441d6780SZbigniew Bodek #include <net/if.h>
46441d6780SZbigniew Bodek #include <net/if_media.h>
47441d6780SZbigniew Bodek #include <net/if_types.h>
48441d6780SZbigniew Bodek #include <net/if_var.h>
49441d6780SZbigniew Bodek 
50441d6780SZbigniew Bodek #include <dev/mii/mii.h>
51441d6780SZbigniew Bodek #include <dev/mii/miivar.h>
52441d6780SZbigniew Bodek 
53441d6780SZbigniew Bodek #include "thunder_mdio_var.h"
54441d6780SZbigniew Bodek 
55441d6780SZbigniew Bodek #include "lmac_if.h"
56441d6780SZbigniew Bodek #include "miibus_if.h"
57441d6780SZbigniew Bodek 
58441d6780SZbigniew Bodek #define	REG_BASE_RID	0
59441d6780SZbigniew Bodek 
60441d6780SZbigniew Bodek #define	SMI_CMD				0x00
61441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_REG_ADR_SHIFT	(0)
62441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_REG_ADR_MASK	(0x1FUL << SMI_CMD_PHY_REG_ADR_SHIFT)
63441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_ADR_SHIFT		(8)
64441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_ADR_MASK		(0x1FUL << SMI_CMD_PHY_ADR_SHIFT)
65441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_OP_MASK		(0x3UL << 16)
66441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_OP_C22_READ	(0x1UL << 16)
67441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_OP_C22_WRITE	(0x0UL << 16)
68441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_OP_C45_READ	(0x3UL << 16)
69441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_OP_C45_WRITE	(0x1UL << 16)
70441d6780SZbigniew Bodek #define	 SMI_CMD_PHY_OP_C45_ADDR	(0x0UL << 16)
71441d6780SZbigniew Bodek 
72441d6780SZbigniew Bodek #define	SMI_WR_DAT			0x08
73441d6780SZbigniew Bodek #define	 SMI_WR_DAT_PENDING		(1UL << 17)
74441d6780SZbigniew Bodek #define	 SMI_WR_DAT_VAL			(1UL << 16)
75441d6780SZbigniew Bodek #define	 SMI_WR_DAT_DAT_MASK		(0xFFFFUL << 0)
76441d6780SZbigniew Bodek 
77441d6780SZbigniew Bodek #define	SMI_RD_DAT			0x10
78441d6780SZbigniew Bodek #define	 SMI_RD_DAT_PENDING		(1UL << 17)
79441d6780SZbigniew Bodek #define	 SMI_RD_DAT_VAL			(1UL << 16)
80441d6780SZbigniew Bodek #define	 SMI_RD_DAT_DAT_MASK		(0xFFFFUL << 0)
81441d6780SZbigniew Bodek 
82441d6780SZbigniew Bodek #define	SMI_CLK				0x18
83441d6780SZbigniew Bodek #define	 SMI_CLK_PREAMBLE		(1UL << 12)
84441d6780SZbigniew Bodek #define	 SMI_CLK_MODE			(1UL << 24)
85441d6780SZbigniew Bodek 
86441d6780SZbigniew Bodek #define	SMI_EN				0x20
87*b73763fcSGordon Bergling #define	 SMI_EN_EN			(1UL << 0)	/* Enable interface */
88441d6780SZbigniew Bodek 
89441d6780SZbigniew Bodek #define	SMI_DRV_CTL			0x28
90441d6780SZbigniew Bodek 
91441d6780SZbigniew Bodek static int thunder_mdio_detach(device_t);
92441d6780SZbigniew Bodek 
93441d6780SZbigniew Bodek static int thunder_mdio_read(device_t, int, int);
94441d6780SZbigniew Bodek static int thunder_mdio_write(device_t, int, int, int);
95441d6780SZbigniew Bodek 
96b9545c57SJustin Hibbits static int thunder_ifmedia_change_stub(if_t);
97b9545c57SJustin Hibbits static void thunder_ifmedia_status_stub(if_t, struct ifmediareq *);
98441d6780SZbigniew Bodek 
99441d6780SZbigniew Bodek static int thunder_mdio_media_status(device_t, int, int *, int *, int *);
100441d6780SZbigniew Bodek static int thunder_mdio_media_change(device_t, int, int, int, int);
101441d6780SZbigniew Bodek static int thunder_mdio_phy_connect(device_t, int, int);
102441d6780SZbigniew Bodek static int thunder_mdio_phy_disconnect(device_t, int, int);
103441d6780SZbigniew Bodek 
104441d6780SZbigniew Bodek static device_method_t thunder_mdio_methods[] = {
105441d6780SZbigniew Bodek 	/* Device interface */
106441d6780SZbigniew Bodek 	DEVMETHOD(device_detach,	thunder_mdio_detach),
107441d6780SZbigniew Bodek 	/* LMAC interface */
108441d6780SZbigniew Bodek 	DEVMETHOD(lmac_media_status,	thunder_mdio_media_status),
109441d6780SZbigniew Bodek 	DEVMETHOD(lmac_media_change,	thunder_mdio_media_change),
110441d6780SZbigniew Bodek 	DEVMETHOD(lmac_phy_connect,	thunder_mdio_phy_connect),
111441d6780SZbigniew Bodek 	DEVMETHOD(lmac_phy_disconnect,	thunder_mdio_phy_disconnect),
112441d6780SZbigniew Bodek 	/* MII interface */
113441d6780SZbigniew Bodek 	DEVMETHOD(miibus_readreg,	thunder_mdio_read),
114441d6780SZbigniew Bodek 	DEVMETHOD(miibus_writereg,	thunder_mdio_write),
115441d6780SZbigniew Bodek 
116441d6780SZbigniew Bodek 	/* End */
117441d6780SZbigniew Bodek 	DEVMETHOD_END
118441d6780SZbigniew Bodek };
119441d6780SZbigniew Bodek 
120441d6780SZbigniew Bodek DEFINE_CLASS_0(thunder_mdio, thunder_mdio_driver, thunder_mdio_methods,
121441d6780SZbigniew Bodek     sizeof(struct thunder_mdio_softc));
122441d6780SZbigniew Bodek 
1233e38757dSJohn Baldwin DRIVER_MODULE(miibus, thunder_mdio, miibus_driver, 0, 0);
124f4aafb9eSWojciech Macek MODULE_VERSION(thunder_mdio, 1);
125441d6780SZbigniew Bodek MODULE_DEPEND(thunder_mdio, ether, 1, 1, 1);
126441d6780SZbigniew Bodek MODULE_DEPEND(thunder_mdio, miibus, 1, 1, 1);
127f4aafb9eSWojciech Macek MODULE_DEPEND(thunder_mdio, mrmlbus, 1, 1, 1);
128441d6780SZbigniew Bodek 
129441d6780SZbigniew Bodek MALLOC_DEFINE(M_THUNDER_MDIO, "ThunderX MDIO",
130441d6780SZbigniew Bodek     "Cavium ThunderX MDIO dynamic memory");
131441d6780SZbigniew Bodek 
132441d6780SZbigniew Bodek #define	MDIO_LOCK_INIT(sc, name)			\
133441d6780SZbigniew Bodek     mtx_init(&(sc)->mtx, name, NULL, MTX_DEF)
134441d6780SZbigniew Bodek 
135441d6780SZbigniew Bodek #define	MDIO_LOCK_DESTROY(sc)				\
136441d6780SZbigniew Bodek     mtx_destroy(&(sc)->mtx)
137441d6780SZbigniew Bodek 
138441d6780SZbigniew Bodek #define	MDIO_LOCK(sc)	mtx_lock(&(sc)->mtx)
139441d6780SZbigniew Bodek #define	MDIO_UNLOCK(sc)	mtx_unlock(&(sc)->mtx)
140441d6780SZbigniew Bodek 
141441d6780SZbigniew Bodek #define	MDIO_LOCK_ASSERT(sc)				\
142441d6780SZbigniew Bodek     mtx_assert(&(sc)->mtx, MA_OWNED)
143441d6780SZbigniew Bodek 
144441d6780SZbigniew Bodek #define	mdio_reg_read(sc, reg)				\
145441d6780SZbigniew Bodek     bus_read_8((sc)->reg_base, (reg))
146441d6780SZbigniew Bodek 
147441d6780SZbigniew Bodek #define	mdio_reg_write(sc, reg, val)			\
148441d6780SZbigniew Bodek     bus_write_8((sc)->reg_base, (reg), (val))
149441d6780SZbigniew Bodek 
150441d6780SZbigniew Bodek int
151441d6780SZbigniew Bodek thunder_mdio_attach(device_t dev)
152441d6780SZbigniew Bodek {
153441d6780SZbigniew Bodek 	struct thunder_mdio_softc *sc;
154441d6780SZbigniew Bodek 	int rid;
155441d6780SZbigniew Bodek 
156441d6780SZbigniew Bodek 	sc = device_get_softc(dev);
157441d6780SZbigniew Bodek 	sc->dev = dev;
158441d6780SZbigniew Bodek 
159441d6780SZbigniew Bodek 	/* Allocate memory resources */
160441d6780SZbigniew Bodek 	rid = REG_BASE_RID;
161441d6780SZbigniew Bodek 	sc->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
162441d6780SZbigniew Bodek 	    RF_ACTIVE);
163441d6780SZbigniew Bodek 	if (sc->reg_base == NULL) {
164441d6780SZbigniew Bodek 		device_printf(dev, "Could not allocate memory\n");
165441d6780SZbigniew Bodek 		return (ENXIO);
166441d6780SZbigniew Bodek 	}
167441d6780SZbigniew Bodek 
168441d6780SZbigniew Bodek 	TAILQ_INIT(&sc->phy_desc_head);
169441d6780SZbigniew Bodek 	MDIO_LOCK_INIT(sc, "ThunderX MDIO lock");
170441d6780SZbigniew Bodek 
171441d6780SZbigniew Bodek 	/* Enable SMI/MDIO interface */
172441d6780SZbigniew Bodek 	mdio_reg_write(sc, SMI_EN, SMI_EN_EN);
173441d6780SZbigniew Bodek 
174441d6780SZbigniew Bodek 	return (0);
175441d6780SZbigniew Bodek }
176441d6780SZbigniew Bodek 
177441d6780SZbigniew Bodek static int
178441d6780SZbigniew Bodek thunder_mdio_detach(device_t dev)
179441d6780SZbigniew Bodek {
180441d6780SZbigniew Bodek 	struct thunder_mdio_softc *sc;
181441d6780SZbigniew Bodek 
182441d6780SZbigniew Bodek 	sc = device_get_softc(dev);
183441d6780SZbigniew Bodek 
184441d6780SZbigniew Bodek 	if (sc->reg_base != NULL) {
185441d6780SZbigniew Bodek 		bus_release_resource(dev, SYS_RES_MEMORY, REG_BASE_RID,
186441d6780SZbigniew Bodek 		    sc->reg_base);
187441d6780SZbigniew Bodek 	}
188441d6780SZbigniew Bodek 
189441d6780SZbigniew Bodek 	return (0);
190441d6780SZbigniew Bodek }
191441d6780SZbigniew Bodek 
192441d6780SZbigniew Bodek static __inline void
193441d6780SZbigniew Bodek thunder_mdio_set_mode(struct thunder_mdio_softc *sc,
194441d6780SZbigniew Bodek     enum thunder_mdio_mode mode)
195441d6780SZbigniew Bodek {
196441d6780SZbigniew Bodek 	uint64_t smi_clk;
197441d6780SZbigniew Bodek 
198441d6780SZbigniew Bodek 	if (sc->mode == mode)
199441d6780SZbigniew Bodek 		return;
200441d6780SZbigniew Bodek 
201441d6780SZbigniew Bodek 	/* Set mode, IEEE CLAUSE 22 or IEEE CAUSE 45 */
202441d6780SZbigniew Bodek 	smi_clk = mdio_reg_read(sc, SMI_CLK);
203441d6780SZbigniew Bodek 	if (mode == MODE_IEEE_C22)
204441d6780SZbigniew Bodek 		smi_clk &= ~SMI_CLK_MODE;
205441d6780SZbigniew Bodek 	else
206441d6780SZbigniew Bodek 		smi_clk |= SMI_CLK_MODE;
207441d6780SZbigniew Bodek 	/* Enable sending 32 bit preable on SMI transactions */
208441d6780SZbigniew Bodek 	smi_clk |= SMI_CLK_PREAMBLE;
209fafb1c57SGordon Bergling 	/* Saved settings */
210441d6780SZbigniew Bodek 	mdio_reg_write(sc, SMI_CLK, smi_clk);
211441d6780SZbigniew Bodek 	sc->mode = mode;
212441d6780SZbigniew Bodek }
213441d6780SZbigniew Bodek 
214441d6780SZbigniew Bodek static int
215441d6780SZbigniew Bodek thunder_mdio_c45_addr(struct thunder_mdio_softc *sc, int phy, int reg)
216441d6780SZbigniew Bodek {
217441d6780SZbigniew Bodek 	uint64_t smi_cmd, smi_wr_dat;
218441d6780SZbigniew Bodek 	ssize_t timeout;
219441d6780SZbigniew Bodek 
220441d6780SZbigniew Bodek 	thunder_mdio_set_mode(sc, MODE_IEEE_C45);
221441d6780SZbigniew Bodek 
222441d6780SZbigniew Bodek 	/* Prepare data for transmission */
223441d6780SZbigniew Bodek 	mdio_reg_write(sc, SMI_WR_DAT, reg & SMI_WR_DAT_DAT_MASK);
224441d6780SZbigniew Bodek 	/*
225441d6780SZbigniew Bodek 	 * Assemble command
226441d6780SZbigniew Bodek 	 */
227441d6780SZbigniew Bodek 	smi_cmd = 0;
228441d6780SZbigniew Bodek 	/* Set opcode */
229441d6780SZbigniew Bodek 	smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE;
230441d6780SZbigniew Bodek 
231441d6780SZbigniew Bodek 	/* Set PHY address */
232441d6780SZbigniew Bodek 	smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK);
233441d6780SZbigniew Bodek 	/* Set PHY register offset */
234441d6780SZbigniew Bodek 	smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) &
235441d6780SZbigniew Bodek 	    SMI_CMD_PHY_REG_ADR_MASK);
236441d6780SZbigniew Bodek 
237441d6780SZbigniew Bodek 	mdio_reg_write(sc, SMI_CMD, smi_cmd);
238441d6780SZbigniew Bodek 	for (timeout = 1000; timeout > 0; timeout--) {
239441d6780SZbigniew Bodek 		smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT);
240441d6780SZbigniew Bodek 		if (smi_wr_dat & SMI_WR_DAT_PENDING)
241441d6780SZbigniew Bodek 			DELAY(1000);
242441d6780SZbigniew Bodek 		else
243441d6780SZbigniew Bodek 			break;
244441d6780SZbigniew Bodek 	}
245441d6780SZbigniew Bodek 
246441d6780SZbigniew Bodek 	if (timeout <= 0)
247441d6780SZbigniew Bodek 		return (EIO);
248441d6780SZbigniew Bodek 	else {
249441d6780SZbigniew Bodek 		/* Return 0 on success */
250441d6780SZbigniew Bodek 		return (0);
251441d6780SZbigniew Bodek 	}
252441d6780SZbigniew Bodek }
253441d6780SZbigniew Bodek 
254441d6780SZbigniew Bodek static int
255441d6780SZbigniew Bodek thunder_mdio_read(device_t dev, int phy, int reg)
256441d6780SZbigniew Bodek {
257441d6780SZbigniew Bodek 	struct thunder_mdio_softc *sc;
258441d6780SZbigniew Bodek 	uint64_t smi_cmd, smi_rd_dat;
259441d6780SZbigniew Bodek 	ssize_t timeout;
260441d6780SZbigniew Bodek 	int err;
261441d6780SZbigniew Bodek 
262441d6780SZbigniew Bodek 	sc = device_get_softc(dev);
263441d6780SZbigniew Bodek 
264441d6780SZbigniew Bodek 	/* XXX Always C22 - for <= 1Gbps only */
265441d6780SZbigniew Bodek 	thunder_mdio_set_mode(sc, MODE_IEEE_C22);
266441d6780SZbigniew Bodek 
267441d6780SZbigniew Bodek 	/*
268441d6780SZbigniew Bodek 	 * Assemble command
269441d6780SZbigniew Bodek 	 */
270441d6780SZbigniew Bodek 	smi_cmd = 0;
271441d6780SZbigniew Bodek 	/* Set opcode */
272441d6780SZbigniew Bodek 	if (sc->mode == MODE_IEEE_C22)
273441d6780SZbigniew Bodek 		smi_cmd |= SMI_CMD_PHY_OP_C22_READ;
274441d6780SZbigniew Bodek 	else {
275441d6780SZbigniew Bodek 		smi_cmd |= SMI_CMD_PHY_OP_C45_READ;
276441d6780SZbigniew Bodek 		err = thunder_mdio_c45_addr(sc, phy, reg);
277441d6780SZbigniew Bodek 		if (err != 0)
278441d6780SZbigniew Bodek 			return (err);
279441d6780SZbigniew Bodek 
280441d6780SZbigniew Bodek 		reg = (reg >> 16) & 0x1F;
281441d6780SZbigniew Bodek 	}
282441d6780SZbigniew Bodek 
283441d6780SZbigniew Bodek 	/* Set PHY address */
284441d6780SZbigniew Bodek 	smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK);
285441d6780SZbigniew Bodek 	/* Set PHY register offset */
286441d6780SZbigniew Bodek 	smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) &
287441d6780SZbigniew Bodek 	    SMI_CMD_PHY_REG_ADR_MASK);
288441d6780SZbigniew Bodek 
289441d6780SZbigniew Bodek 	mdio_reg_write(sc, SMI_CMD, smi_cmd);
290441d6780SZbigniew Bodek 	for (timeout = 1000; timeout > 0; timeout--) {
291441d6780SZbigniew Bodek 		smi_rd_dat = mdio_reg_read(sc, SMI_RD_DAT);
292441d6780SZbigniew Bodek 		if (smi_rd_dat & SMI_RD_DAT_PENDING)
293441d6780SZbigniew Bodek 			DELAY(1000);
294441d6780SZbigniew Bodek 		else
295441d6780SZbigniew Bodek 			break;
296441d6780SZbigniew Bodek 	}
297441d6780SZbigniew Bodek 
298441d6780SZbigniew Bodek 	if (smi_rd_dat & SMI_RD_DAT_VAL)
299441d6780SZbigniew Bodek 		return (smi_rd_dat & SMI_RD_DAT_DAT_MASK);
300441d6780SZbigniew Bodek 	else {
301441d6780SZbigniew Bodek 		/* Return 0 on error */
302441d6780SZbigniew Bodek 		return (0);
303441d6780SZbigniew Bodek 	}
304441d6780SZbigniew Bodek }
305441d6780SZbigniew Bodek 
306441d6780SZbigniew Bodek static int
307441d6780SZbigniew Bodek thunder_mdio_write(device_t dev, int phy, int reg, int data)
308441d6780SZbigniew Bodek {
309441d6780SZbigniew Bodek 	struct thunder_mdio_softc *sc;
310441d6780SZbigniew Bodek 	uint64_t smi_cmd, smi_wr_dat;
311441d6780SZbigniew Bodek 	ssize_t timeout;
312441d6780SZbigniew Bodek 
313441d6780SZbigniew Bodek 	sc = device_get_softc(dev);
314441d6780SZbigniew Bodek 
315441d6780SZbigniew Bodek 	/* XXX Always C22 - for <= 1Gbps only */
316441d6780SZbigniew Bodek 	thunder_mdio_set_mode(sc, MODE_IEEE_C22);
317441d6780SZbigniew Bodek 
318441d6780SZbigniew Bodek 	/* Prepare data for transmission */
319441d6780SZbigniew Bodek 	mdio_reg_write(sc, SMI_WR_DAT, data & SMI_WR_DAT_DAT_MASK);
320441d6780SZbigniew Bodek 	/*
321441d6780SZbigniew Bodek 	 * Assemble command
322441d6780SZbigniew Bodek 	 */
323441d6780SZbigniew Bodek 	smi_cmd = 0;
324441d6780SZbigniew Bodek 	/* Set opcode */
325441d6780SZbigniew Bodek 	if (sc->mode == MODE_IEEE_C22)
326441d6780SZbigniew Bodek 		smi_cmd |= SMI_CMD_PHY_OP_C22_WRITE;
327441d6780SZbigniew Bodek 	else
328441d6780SZbigniew Bodek 		smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE;
329441d6780SZbigniew Bodek 
330441d6780SZbigniew Bodek 	/* Set PHY address */
331441d6780SZbigniew Bodek 	smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK);
332441d6780SZbigniew Bodek 	/* Set PHY register offset */
333441d6780SZbigniew Bodek 	smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) &
334441d6780SZbigniew Bodek 	    SMI_CMD_PHY_REG_ADR_MASK);
335441d6780SZbigniew Bodek 
336441d6780SZbigniew Bodek 	mdio_reg_write(sc, SMI_CMD, smi_cmd);
337441d6780SZbigniew Bodek 	for (timeout = 1000; timeout > 0; timeout--) {
338441d6780SZbigniew Bodek 		smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT);
339441d6780SZbigniew Bodek 		if (smi_wr_dat & SMI_WR_DAT_PENDING)
340441d6780SZbigniew Bodek 			DELAY(1000);
341441d6780SZbigniew Bodek 		else
342441d6780SZbigniew Bodek 			break;
343441d6780SZbigniew Bodek 	}
344441d6780SZbigniew Bodek 
345441d6780SZbigniew Bodek 	if (timeout <= 0)
346441d6780SZbigniew Bodek 		return (EIO);
347441d6780SZbigniew Bodek 	else {
348441d6780SZbigniew Bodek 		/* Return 0 on success */
349441d6780SZbigniew Bodek 		return (0);
350441d6780SZbigniew Bodek 	}
351441d6780SZbigniew Bodek }
352441d6780SZbigniew Bodek 
353441d6780SZbigniew Bodek static int
354b9545c57SJustin Hibbits thunder_ifmedia_change_stub(if_t ifp __unused)
355441d6780SZbigniew Bodek {
356441d6780SZbigniew Bodek 	/* Will never be called by if_media */
357441d6780SZbigniew Bodek 	return (0);
358441d6780SZbigniew Bodek }
359441d6780SZbigniew Bodek 
360441d6780SZbigniew Bodek static void
361b9545c57SJustin Hibbits thunder_ifmedia_status_stub(if_t ifp __unused, struct ifmediareq
362441d6780SZbigniew Bodek     *ifmr __unused)
363441d6780SZbigniew Bodek {
364441d6780SZbigniew Bodek 	/* Will never be called by if_media */
365441d6780SZbigniew Bodek }
366441d6780SZbigniew Bodek 
367441d6780SZbigniew Bodek static __inline struct phy_desc *
368441d6780SZbigniew Bodek get_phy_desc(struct thunder_mdio_softc *sc, int lmacid)
369441d6780SZbigniew Bodek {
370441d6780SZbigniew Bodek 	struct phy_desc *pd = NULL;
371441d6780SZbigniew Bodek 
372441d6780SZbigniew Bodek 	MDIO_LOCK_ASSERT(sc);
373441d6780SZbigniew Bodek 	TAILQ_FOREACH(pd, &sc->phy_desc_head, phy_desc_list) {
374441d6780SZbigniew Bodek 		if (pd->lmacid == lmacid)
375441d6780SZbigniew Bodek 			break;
376441d6780SZbigniew Bodek 	}
377441d6780SZbigniew Bodek 
378441d6780SZbigniew Bodek 	return (pd);
379441d6780SZbigniew Bodek }
380441d6780SZbigniew Bodek static int
381441d6780SZbigniew Bodek thunder_mdio_media_status(device_t dev, int lmacid, int *link, int *duplex,
382441d6780SZbigniew Bodek     int *speed)
383441d6780SZbigniew Bodek {
384441d6780SZbigniew Bodek 	struct thunder_mdio_softc *sc;
385441d6780SZbigniew Bodek 	struct mii_data *mii_sc;
386441d6780SZbigniew Bodek 	struct phy_desc *pd;
387441d6780SZbigniew Bodek 
388441d6780SZbigniew Bodek 	sc = device_get_softc(dev);
389441d6780SZbigniew Bodek 
390441d6780SZbigniew Bodek 	MDIO_LOCK(sc);
391441d6780SZbigniew Bodek 	pd = get_phy_desc(sc, lmacid);
392441d6780SZbigniew Bodek 	if (pd == NULL) {
393441d6780SZbigniew Bodek 		/* Panic when invariants are enabled, fail otherwise. */
394441d6780SZbigniew Bodek 		KASSERT(0, ("%s: no PHY descriptor for LMAC%d",
395441d6780SZbigniew Bodek 		    __func__, lmacid));
396441d6780SZbigniew Bodek 		MDIO_UNLOCK(sc);
397441d6780SZbigniew Bodek 		return (ENXIO);
398441d6780SZbigniew Bodek 	}
399441d6780SZbigniew Bodek 	mii_sc = device_get_softc(pd->miibus);
400441d6780SZbigniew Bodek 
401441d6780SZbigniew Bodek 	mii_tick(mii_sc);
402441d6780SZbigniew Bodek 	if ((mii_sc->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
403441d6780SZbigniew Bodek 	    (IFM_ACTIVE | IFM_AVALID)) {
404441d6780SZbigniew Bodek 		/* Link is up */
405441d6780SZbigniew Bodek 		*link = 1;
406441d6780SZbigniew Bodek 	} else
407441d6780SZbigniew Bodek 		*link = 0;
408441d6780SZbigniew Bodek 
409441d6780SZbigniew Bodek 	switch (IFM_SUBTYPE(mii_sc->mii_media_active)) {
410441d6780SZbigniew Bodek 	case IFM_10_T:
411441d6780SZbigniew Bodek 		*speed = 10;
412441d6780SZbigniew Bodek 		break;
413441d6780SZbigniew Bodek 	case IFM_100_TX:
414441d6780SZbigniew Bodek 		*speed = 100;
415441d6780SZbigniew Bodek 		break;
416441d6780SZbigniew Bodek 	case IFM_1000_T:
417441d6780SZbigniew Bodek 		*speed = 1000;
418441d6780SZbigniew Bodek 		break;
419441d6780SZbigniew Bodek 	default:
420441d6780SZbigniew Bodek 		/* IFM_NONE */
421441d6780SZbigniew Bodek 		*speed = 0;
422441d6780SZbigniew Bodek 	}
423441d6780SZbigniew Bodek 
424441d6780SZbigniew Bodek 	if ((IFM_OPTIONS(mii_sc->mii_media_active) & IFM_FDX) != 0)
425441d6780SZbigniew Bodek 		*duplex = 1;
426441d6780SZbigniew Bodek 	else
427441d6780SZbigniew Bodek 		*duplex = 0;
428441d6780SZbigniew Bodek 
429441d6780SZbigniew Bodek 	MDIO_UNLOCK(sc);
430441d6780SZbigniew Bodek 
431441d6780SZbigniew Bodek 	return (0);
432441d6780SZbigniew Bodek }
433441d6780SZbigniew Bodek 
434441d6780SZbigniew Bodek static int
435441d6780SZbigniew Bodek thunder_mdio_media_change(device_t dev, int lmacid, int link, int duplex,
436441d6780SZbigniew Bodek     int speed)
437441d6780SZbigniew Bodek {
438441d6780SZbigniew Bodek 
439441d6780SZbigniew Bodek 	return (EIO);
440441d6780SZbigniew Bodek }
441441d6780SZbigniew Bodek 
442441d6780SZbigniew Bodek static int
443441d6780SZbigniew Bodek thunder_mdio_phy_connect(device_t dev, int lmacid, int phy)
444441d6780SZbigniew Bodek {
445441d6780SZbigniew Bodek 	struct thunder_mdio_softc *sc;
446441d6780SZbigniew Bodek 	struct phy_desc *pd;
447441d6780SZbigniew Bodek 	int err;
448441d6780SZbigniew Bodek 
449441d6780SZbigniew Bodek 	sc = device_get_softc(dev);
450441d6780SZbigniew Bodek 
451441d6780SZbigniew Bodek 	MDIO_LOCK(sc);
452441d6780SZbigniew Bodek 	pd = get_phy_desc(sc, lmacid);
453441d6780SZbigniew Bodek 	MDIO_UNLOCK(sc);
454441d6780SZbigniew Bodek 	if (pd == NULL) {
455441d6780SZbigniew Bodek 		pd = malloc(sizeof(*pd), M_THUNDER_MDIO, (M_NOWAIT | M_ZERO));
456441d6780SZbigniew Bodek 		if (pd == NULL)
457441d6780SZbigniew Bodek 			return (ENOMEM);
458441d6780SZbigniew Bodek 		pd->ifp = if_alloc(IFT_ETHER);
459441d6780SZbigniew Bodek 		if (pd->ifp == NULL) {
460441d6780SZbigniew Bodek 			free(pd, M_THUNDER_MDIO);
461441d6780SZbigniew Bodek 			return (ENOMEM);
462441d6780SZbigniew Bodek 		}
463441d6780SZbigniew Bodek 		pd->lmacid = lmacid;
464441d6780SZbigniew Bodek 	}
465441d6780SZbigniew Bodek 
466441d6780SZbigniew Bodek 	err = mii_attach(dev, &pd->miibus, pd->ifp,
467441d6780SZbigniew Bodek 	    thunder_ifmedia_change_stub, thunder_ifmedia_status_stub,
468441d6780SZbigniew Bodek 	    BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
469441d6780SZbigniew Bodek 
470441d6780SZbigniew Bodek 	if (err != 0) {
471441d6780SZbigniew Bodek 		device_printf(dev, "Could not attach PHY%d\n", phy);
472441d6780SZbigniew Bodek 		if_free(pd->ifp);
473441d6780SZbigniew Bodek 		free(pd, M_THUNDER_MDIO);
474441d6780SZbigniew Bodek 		return (ENXIO);
475441d6780SZbigniew Bodek 	}
476441d6780SZbigniew Bodek 
477441d6780SZbigniew Bodek 	MDIO_LOCK(sc);
478441d6780SZbigniew Bodek 	TAILQ_INSERT_TAIL(&sc->phy_desc_head, pd, phy_desc_list);
479441d6780SZbigniew Bodek 	MDIO_UNLOCK(sc);
480441d6780SZbigniew Bodek 
481441d6780SZbigniew Bodek 	return (0);
482441d6780SZbigniew Bodek }
483441d6780SZbigniew Bodek 
484441d6780SZbigniew Bodek static int
485441d6780SZbigniew Bodek thunder_mdio_phy_disconnect(device_t dev, int lmacid, int phy)
486441d6780SZbigniew Bodek {
487441d6780SZbigniew Bodek 	struct thunder_mdio_softc *sc;
488441d6780SZbigniew Bodek 	struct phy_desc *pd;
489441d6780SZbigniew Bodek 
490441d6780SZbigniew Bodek 	sc = device_get_softc(dev);
491441d6780SZbigniew Bodek 	MDIO_LOCK(sc);
492441d6780SZbigniew Bodek 
493441d6780SZbigniew Bodek 	pd = get_phy_desc(sc, lmacid);
494441d6780SZbigniew Bodek 	if (pd == NULL) {
495441d6780SZbigniew Bodek 		MDIO_UNLOCK(sc);
496441d6780SZbigniew Bodek 		return (EINVAL);
497441d6780SZbigniew Bodek 	}
498441d6780SZbigniew Bodek 
499441d6780SZbigniew Bodek 	/* Remove this PHY descriptor from the list */
500441d6780SZbigniew Bodek 	TAILQ_REMOVE(&sc->phy_desc_head, pd, phy_desc_list);
501441d6780SZbigniew Bodek 
502441d6780SZbigniew Bodek 	/* Detach miibus */
503441d6780SZbigniew Bodek 	bus_generic_detach(dev);
504441d6780SZbigniew Bodek 	device_delete_child(dev, pd->miibus);
505441d6780SZbigniew Bodek 	/* Free fake ifnet */
506441d6780SZbigniew Bodek 	if_free(pd->ifp);
507441d6780SZbigniew Bodek 	/* Free memory under phy descriptor */
508441d6780SZbigniew Bodek 	free(pd, M_THUNDER_MDIO);
509441d6780SZbigniew Bodek 	MDIO_UNLOCK(sc);
510441d6780SZbigniew Bodek 
511441d6780SZbigniew Bodek 	return (0);
512441d6780SZbigniew Bodek }
513