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/param.h>
30441d6780SZbigniew Bodek #include <sys/systm.h>
31441d6780SZbigniew Bodek #include <sys/bus.h>
32441d6780SZbigniew Bodek #include <sys/kernel.h>
33441d6780SZbigniew Bodek #include <sys/module.h>
34441d6780SZbigniew Bodek #include <sys/resource.h>
35441d6780SZbigniew Bodek #include <sys/rman.h>
36441d6780SZbigniew Bodek #include <sys/socket.h>
37441d6780SZbigniew Bodek #include <sys/queue.h>
38441d6780SZbigniew Bodek
39441d6780SZbigniew Bodek #include <machine/bus.h>
40441d6780SZbigniew Bodek #include <machine/resource.h>
41441d6780SZbigniew Bodek
42441d6780SZbigniew Bodek #include <net/if.h>
43441d6780SZbigniew Bodek #include <net/if_media.h>
44441d6780SZbigniew Bodek #include <net/if_types.h>
45441d6780SZbigniew Bodek #include <net/if_var.h>
46441d6780SZbigniew Bodek
47441d6780SZbigniew Bodek #include <dev/mii/mii.h>
48441d6780SZbigniew Bodek #include <dev/mii/miivar.h>
49441d6780SZbigniew Bodek
50441d6780SZbigniew Bodek #include "thunder_mdio_var.h"
51441d6780SZbigniew Bodek
52441d6780SZbigniew Bodek #include "lmac_if.h"
53441d6780SZbigniew Bodek #include "miibus_if.h"
54441d6780SZbigniew Bodek
55441d6780SZbigniew Bodek #define REG_BASE_RID 0
56441d6780SZbigniew Bodek
57441d6780SZbigniew Bodek #define SMI_CMD 0x00
58441d6780SZbigniew Bodek #define SMI_CMD_PHY_REG_ADR_SHIFT (0)
59441d6780SZbigniew Bodek #define SMI_CMD_PHY_REG_ADR_MASK (0x1FUL << SMI_CMD_PHY_REG_ADR_SHIFT)
60441d6780SZbigniew Bodek #define SMI_CMD_PHY_ADR_SHIFT (8)
61441d6780SZbigniew Bodek #define SMI_CMD_PHY_ADR_MASK (0x1FUL << SMI_CMD_PHY_ADR_SHIFT)
62441d6780SZbigniew Bodek #define SMI_CMD_PHY_OP_MASK (0x3UL << 16)
63441d6780SZbigniew Bodek #define SMI_CMD_PHY_OP_C22_READ (0x1UL << 16)
64441d6780SZbigniew Bodek #define SMI_CMD_PHY_OP_C22_WRITE (0x0UL << 16)
65441d6780SZbigniew Bodek #define SMI_CMD_PHY_OP_C45_READ (0x3UL << 16)
66441d6780SZbigniew Bodek #define SMI_CMD_PHY_OP_C45_WRITE (0x1UL << 16)
67441d6780SZbigniew Bodek #define SMI_CMD_PHY_OP_C45_ADDR (0x0UL << 16)
68441d6780SZbigniew Bodek
69441d6780SZbigniew Bodek #define SMI_WR_DAT 0x08
70441d6780SZbigniew Bodek #define SMI_WR_DAT_PENDING (1UL << 17)
71441d6780SZbigniew Bodek #define SMI_WR_DAT_VAL (1UL << 16)
72441d6780SZbigniew Bodek #define SMI_WR_DAT_DAT_MASK (0xFFFFUL << 0)
73441d6780SZbigniew Bodek
74441d6780SZbigniew Bodek #define SMI_RD_DAT 0x10
75441d6780SZbigniew Bodek #define SMI_RD_DAT_PENDING (1UL << 17)
76441d6780SZbigniew Bodek #define SMI_RD_DAT_VAL (1UL << 16)
77441d6780SZbigniew Bodek #define SMI_RD_DAT_DAT_MASK (0xFFFFUL << 0)
78441d6780SZbigniew Bodek
79441d6780SZbigniew Bodek #define SMI_CLK 0x18
80441d6780SZbigniew Bodek #define SMI_CLK_PREAMBLE (1UL << 12)
81441d6780SZbigniew Bodek #define SMI_CLK_MODE (1UL << 24)
82441d6780SZbigniew Bodek
83441d6780SZbigniew Bodek #define SMI_EN 0x20
84*b73763fcSGordon Bergling #define SMI_EN_EN (1UL << 0) /* Enable interface */
85441d6780SZbigniew Bodek
86441d6780SZbigniew Bodek #define SMI_DRV_CTL 0x28
87441d6780SZbigniew Bodek
88441d6780SZbigniew Bodek static int thunder_mdio_detach(device_t);
89441d6780SZbigniew Bodek
90441d6780SZbigniew Bodek static int thunder_mdio_read(device_t, int, int);
91441d6780SZbigniew Bodek static int thunder_mdio_write(device_t, int, int, int);
92441d6780SZbigniew Bodek
93b9545c57SJustin Hibbits static int thunder_ifmedia_change_stub(if_t);
94b9545c57SJustin Hibbits static void thunder_ifmedia_status_stub(if_t, struct ifmediareq *);
95441d6780SZbigniew Bodek
96441d6780SZbigniew Bodek static int thunder_mdio_media_status(device_t, int, int *, int *, int *);
97441d6780SZbigniew Bodek static int thunder_mdio_media_change(device_t, int, int, int, int);
98441d6780SZbigniew Bodek static int thunder_mdio_phy_connect(device_t, int, int);
99441d6780SZbigniew Bodek static int thunder_mdio_phy_disconnect(device_t, int, int);
100441d6780SZbigniew Bodek
101441d6780SZbigniew Bodek static device_method_t thunder_mdio_methods[] = {
102441d6780SZbigniew Bodek /* Device interface */
103441d6780SZbigniew Bodek DEVMETHOD(device_detach, thunder_mdio_detach),
104441d6780SZbigniew Bodek /* LMAC interface */
105441d6780SZbigniew Bodek DEVMETHOD(lmac_media_status, thunder_mdio_media_status),
106441d6780SZbigniew Bodek DEVMETHOD(lmac_media_change, thunder_mdio_media_change),
107441d6780SZbigniew Bodek DEVMETHOD(lmac_phy_connect, thunder_mdio_phy_connect),
108441d6780SZbigniew Bodek DEVMETHOD(lmac_phy_disconnect, thunder_mdio_phy_disconnect),
109441d6780SZbigniew Bodek /* MII interface */
110441d6780SZbigniew Bodek DEVMETHOD(miibus_readreg, thunder_mdio_read),
111441d6780SZbigniew Bodek DEVMETHOD(miibus_writereg, thunder_mdio_write),
112441d6780SZbigniew Bodek
113441d6780SZbigniew Bodek /* End */
114441d6780SZbigniew Bodek DEVMETHOD_END
115441d6780SZbigniew Bodek };
116441d6780SZbigniew Bodek
117441d6780SZbigniew Bodek DEFINE_CLASS_0(thunder_mdio, thunder_mdio_driver, thunder_mdio_methods,
118441d6780SZbigniew Bodek sizeof(struct thunder_mdio_softc));
119441d6780SZbigniew Bodek
1203e38757dSJohn Baldwin DRIVER_MODULE(miibus, thunder_mdio, miibus_driver, 0, 0);
121f4aafb9eSWojciech Macek MODULE_VERSION(thunder_mdio, 1);
122441d6780SZbigniew Bodek MODULE_DEPEND(thunder_mdio, ether, 1, 1, 1);
123441d6780SZbigniew Bodek MODULE_DEPEND(thunder_mdio, miibus, 1, 1, 1);
124f4aafb9eSWojciech Macek MODULE_DEPEND(thunder_mdio, mrmlbus, 1, 1, 1);
125441d6780SZbigniew Bodek
126441d6780SZbigniew Bodek MALLOC_DEFINE(M_THUNDER_MDIO, "ThunderX MDIO",
127441d6780SZbigniew Bodek "Cavium ThunderX MDIO dynamic memory");
128441d6780SZbigniew Bodek
129441d6780SZbigniew Bodek #define MDIO_LOCK_INIT(sc, name) \
130441d6780SZbigniew Bodek mtx_init(&(sc)->mtx, name, NULL, MTX_DEF)
131441d6780SZbigniew Bodek
132441d6780SZbigniew Bodek #define MDIO_LOCK_DESTROY(sc) \
133441d6780SZbigniew Bodek mtx_destroy(&(sc)->mtx)
134441d6780SZbigniew Bodek
135441d6780SZbigniew Bodek #define MDIO_LOCK(sc) mtx_lock(&(sc)->mtx)
136441d6780SZbigniew Bodek #define MDIO_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
137441d6780SZbigniew Bodek
138441d6780SZbigniew Bodek #define MDIO_LOCK_ASSERT(sc) \
139441d6780SZbigniew Bodek mtx_assert(&(sc)->mtx, MA_OWNED)
140441d6780SZbigniew Bodek
141441d6780SZbigniew Bodek #define mdio_reg_read(sc, reg) \
142441d6780SZbigniew Bodek bus_read_8((sc)->reg_base, (reg))
143441d6780SZbigniew Bodek
144441d6780SZbigniew Bodek #define mdio_reg_write(sc, reg, val) \
145441d6780SZbigniew Bodek bus_write_8((sc)->reg_base, (reg), (val))
146441d6780SZbigniew Bodek
147441d6780SZbigniew Bodek int
thunder_mdio_attach(device_t dev)148441d6780SZbigniew Bodek thunder_mdio_attach(device_t dev)
149441d6780SZbigniew Bodek {
150441d6780SZbigniew Bodek struct thunder_mdio_softc *sc;
151441d6780SZbigniew Bodek int rid;
152441d6780SZbigniew Bodek
153441d6780SZbigniew Bodek sc = device_get_softc(dev);
154441d6780SZbigniew Bodek sc->dev = dev;
155441d6780SZbigniew Bodek
156441d6780SZbigniew Bodek /* Allocate memory resources */
157441d6780SZbigniew Bodek rid = REG_BASE_RID;
158441d6780SZbigniew Bodek sc->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
159441d6780SZbigniew Bodek RF_ACTIVE);
160441d6780SZbigniew Bodek if (sc->reg_base == NULL) {
161441d6780SZbigniew Bodek device_printf(dev, "Could not allocate memory\n");
162441d6780SZbigniew Bodek return (ENXIO);
163441d6780SZbigniew Bodek }
164441d6780SZbigniew Bodek
165441d6780SZbigniew Bodek TAILQ_INIT(&sc->phy_desc_head);
166441d6780SZbigniew Bodek MDIO_LOCK_INIT(sc, "ThunderX MDIO lock");
167441d6780SZbigniew Bodek
168441d6780SZbigniew Bodek /* Enable SMI/MDIO interface */
169441d6780SZbigniew Bodek mdio_reg_write(sc, SMI_EN, SMI_EN_EN);
170441d6780SZbigniew Bodek
171441d6780SZbigniew Bodek return (0);
172441d6780SZbigniew Bodek }
173441d6780SZbigniew Bodek
174441d6780SZbigniew Bodek static int
thunder_mdio_detach(device_t dev)175441d6780SZbigniew Bodek thunder_mdio_detach(device_t dev)
176441d6780SZbigniew Bodek {
177441d6780SZbigniew Bodek struct thunder_mdio_softc *sc;
178441d6780SZbigniew Bodek
179441d6780SZbigniew Bodek sc = device_get_softc(dev);
180441d6780SZbigniew Bodek
181441d6780SZbigniew Bodek if (sc->reg_base != NULL) {
182441d6780SZbigniew Bodek bus_release_resource(dev, SYS_RES_MEMORY, REG_BASE_RID,
183441d6780SZbigniew Bodek sc->reg_base);
184441d6780SZbigniew Bodek }
185441d6780SZbigniew Bodek
186441d6780SZbigniew Bodek return (0);
187441d6780SZbigniew Bodek }
188441d6780SZbigniew Bodek
189441d6780SZbigniew Bodek static __inline void
thunder_mdio_set_mode(struct thunder_mdio_softc * sc,enum thunder_mdio_mode mode)190441d6780SZbigniew Bodek thunder_mdio_set_mode(struct thunder_mdio_softc *sc,
191441d6780SZbigniew Bodek enum thunder_mdio_mode mode)
192441d6780SZbigniew Bodek {
193441d6780SZbigniew Bodek uint64_t smi_clk;
194441d6780SZbigniew Bodek
195441d6780SZbigniew Bodek if (sc->mode == mode)
196441d6780SZbigniew Bodek return;
197441d6780SZbigniew Bodek
198441d6780SZbigniew Bodek /* Set mode, IEEE CLAUSE 22 or IEEE CAUSE 45 */
199441d6780SZbigniew Bodek smi_clk = mdio_reg_read(sc, SMI_CLK);
200441d6780SZbigniew Bodek if (mode == MODE_IEEE_C22)
201441d6780SZbigniew Bodek smi_clk &= ~SMI_CLK_MODE;
202441d6780SZbigniew Bodek else
203441d6780SZbigniew Bodek smi_clk |= SMI_CLK_MODE;
204441d6780SZbigniew Bodek /* Enable sending 32 bit preable on SMI transactions */
205441d6780SZbigniew Bodek smi_clk |= SMI_CLK_PREAMBLE;
206fafb1c57SGordon Bergling /* Saved settings */
207441d6780SZbigniew Bodek mdio_reg_write(sc, SMI_CLK, smi_clk);
208441d6780SZbigniew Bodek sc->mode = mode;
209441d6780SZbigniew Bodek }
210441d6780SZbigniew Bodek
211441d6780SZbigniew Bodek static int
thunder_mdio_c45_addr(struct thunder_mdio_softc * sc,int phy,int reg)212441d6780SZbigniew Bodek thunder_mdio_c45_addr(struct thunder_mdio_softc *sc, int phy, int reg)
213441d6780SZbigniew Bodek {
214441d6780SZbigniew Bodek uint64_t smi_cmd, smi_wr_dat;
215441d6780SZbigniew Bodek ssize_t timeout;
216441d6780SZbigniew Bodek
217441d6780SZbigniew Bodek thunder_mdio_set_mode(sc, MODE_IEEE_C45);
218441d6780SZbigniew Bodek
219441d6780SZbigniew Bodek /* Prepare data for transmission */
220441d6780SZbigniew Bodek mdio_reg_write(sc, SMI_WR_DAT, reg & SMI_WR_DAT_DAT_MASK);
221441d6780SZbigniew Bodek /*
222441d6780SZbigniew Bodek * Assemble command
223441d6780SZbigniew Bodek */
224441d6780SZbigniew Bodek smi_cmd = 0;
225441d6780SZbigniew Bodek /* Set opcode */
226441d6780SZbigniew Bodek smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE;
227441d6780SZbigniew Bodek
228441d6780SZbigniew Bodek /* Set PHY address */
229441d6780SZbigniew Bodek smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK);
230441d6780SZbigniew Bodek /* Set PHY register offset */
231441d6780SZbigniew Bodek smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) &
232441d6780SZbigniew Bodek SMI_CMD_PHY_REG_ADR_MASK);
233441d6780SZbigniew Bodek
234441d6780SZbigniew Bodek mdio_reg_write(sc, SMI_CMD, smi_cmd);
235441d6780SZbigniew Bodek for (timeout = 1000; timeout > 0; timeout--) {
236441d6780SZbigniew Bodek smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT);
237441d6780SZbigniew Bodek if (smi_wr_dat & SMI_WR_DAT_PENDING)
238441d6780SZbigniew Bodek DELAY(1000);
239441d6780SZbigniew Bodek else
240441d6780SZbigniew Bodek break;
241441d6780SZbigniew Bodek }
242441d6780SZbigniew Bodek
243441d6780SZbigniew Bodek if (timeout <= 0)
244441d6780SZbigniew Bodek return (EIO);
245441d6780SZbigniew Bodek else {
246441d6780SZbigniew Bodek /* Return 0 on success */
247441d6780SZbigniew Bodek return (0);
248441d6780SZbigniew Bodek }
249441d6780SZbigniew Bodek }
250441d6780SZbigniew Bodek
251441d6780SZbigniew Bodek static int
thunder_mdio_read(device_t dev,int phy,int reg)252441d6780SZbigniew Bodek thunder_mdio_read(device_t dev, int phy, int reg)
253441d6780SZbigniew Bodek {
254441d6780SZbigniew Bodek struct thunder_mdio_softc *sc;
255441d6780SZbigniew Bodek uint64_t smi_cmd, smi_rd_dat;
256441d6780SZbigniew Bodek ssize_t timeout;
257441d6780SZbigniew Bodek int err;
258441d6780SZbigniew Bodek
259441d6780SZbigniew Bodek sc = device_get_softc(dev);
260441d6780SZbigniew Bodek
261441d6780SZbigniew Bodek /* XXX Always C22 - for <= 1Gbps only */
262441d6780SZbigniew Bodek thunder_mdio_set_mode(sc, MODE_IEEE_C22);
263441d6780SZbigniew Bodek
264441d6780SZbigniew Bodek /*
265441d6780SZbigniew Bodek * Assemble command
266441d6780SZbigniew Bodek */
267441d6780SZbigniew Bodek smi_cmd = 0;
268441d6780SZbigniew Bodek /* Set opcode */
269441d6780SZbigniew Bodek if (sc->mode == MODE_IEEE_C22)
270441d6780SZbigniew Bodek smi_cmd |= SMI_CMD_PHY_OP_C22_READ;
271441d6780SZbigniew Bodek else {
272441d6780SZbigniew Bodek smi_cmd |= SMI_CMD_PHY_OP_C45_READ;
273441d6780SZbigniew Bodek err = thunder_mdio_c45_addr(sc, phy, reg);
274441d6780SZbigniew Bodek if (err != 0)
275441d6780SZbigniew Bodek return (err);
276441d6780SZbigniew Bodek
277441d6780SZbigniew Bodek reg = (reg >> 16) & 0x1F;
278441d6780SZbigniew Bodek }
279441d6780SZbigniew Bodek
280441d6780SZbigniew Bodek /* Set PHY address */
281441d6780SZbigniew Bodek smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK);
282441d6780SZbigniew Bodek /* Set PHY register offset */
283441d6780SZbigniew Bodek smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) &
284441d6780SZbigniew Bodek SMI_CMD_PHY_REG_ADR_MASK);
285441d6780SZbigniew Bodek
286441d6780SZbigniew Bodek mdio_reg_write(sc, SMI_CMD, smi_cmd);
287441d6780SZbigniew Bodek for (timeout = 1000; timeout > 0; timeout--) {
288441d6780SZbigniew Bodek smi_rd_dat = mdio_reg_read(sc, SMI_RD_DAT);
289441d6780SZbigniew Bodek if (smi_rd_dat & SMI_RD_DAT_PENDING)
290441d6780SZbigniew Bodek DELAY(1000);
291441d6780SZbigniew Bodek else
292441d6780SZbigniew Bodek break;
293441d6780SZbigniew Bodek }
294441d6780SZbigniew Bodek
295441d6780SZbigniew Bodek if (smi_rd_dat & SMI_RD_DAT_VAL)
296441d6780SZbigniew Bodek return (smi_rd_dat & SMI_RD_DAT_DAT_MASK);
297441d6780SZbigniew Bodek else {
298441d6780SZbigniew Bodek /* Return 0 on error */
299441d6780SZbigniew Bodek return (0);
300441d6780SZbigniew Bodek }
301441d6780SZbigniew Bodek }
302441d6780SZbigniew Bodek
303441d6780SZbigniew Bodek static int
thunder_mdio_write(device_t dev,int phy,int reg,int data)304441d6780SZbigniew Bodek thunder_mdio_write(device_t dev, int phy, int reg, int data)
305441d6780SZbigniew Bodek {
306441d6780SZbigniew Bodek struct thunder_mdio_softc *sc;
307441d6780SZbigniew Bodek uint64_t smi_cmd, smi_wr_dat;
308441d6780SZbigniew Bodek ssize_t timeout;
309441d6780SZbigniew Bodek
310441d6780SZbigniew Bodek sc = device_get_softc(dev);
311441d6780SZbigniew Bodek
312441d6780SZbigniew Bodek /* XXX Always C22 - for <= 1Gbps only */
313441d6780SZbigniew Bodek thunder_mdio_set_mode(sc, MODE_IEEE_C22);
314441d6780SZbigniew Bodek
315441d6780SZbigniew Bodek /* Prepare data for transmission */
316441d6780SZbigniew Bodek mdio_reg_write(sc, SMI_WR_DAT, data & SMI_WR_DAT_DAT_MASK);
317441d6780SZbigniew Bodek /*
318441d6780SZbigniew Bodek * Assemble command
319441d6780SZbigniew Bodek */
320441d6780SZbigniew Bodek smi_cmd = 0;
321441d6780SZbigniew Bodek /* Set opcode */
322441d6780SZbigniew Bodek if (sc->mode == MODE_IEEE_C22)
323441d6780SZbigniew Bodek smi_cmd |= SMI_CMD_PHY_OP_C22_WRITE;
324441d6780SZbigniew Bodek else
325441d6780SZbigniew Bodek smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE;
326441d6780SZbigniew Bodek
327441d6780SZbigniew Bodek /* Set PHY address */
328441d6780SZbigniew Bodek smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK);
329441d6780SZbigniew Bodek /* Set PHY register offset */
330441d6780SZbigniew Bodek smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) &
331441d6780SZbigniew Bodek SMI_CMD_PHY_REG_ADR_MASK);
332441d6780SZbigniew Bodek
333441d6780SZbigniew Bodek mdio_reg_write(sc, SMI_CMD, smi_cmd);
334441d6780SZbigniew Bodek for (timeout = 1000; timeout > 0; timeout--) {
335441d6780SZbigniew Bodek smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT);
336441d6780SZbigniew Bodek if (smi_wr_dat & SMI_WR_DAT_PENDING)
337441d6780SZbigniew Bodek DELAY(1000);
338441d6780SZbigniew Bodek else
339441d6780SZbigniew Bodek break;
340441d6780SZbigniew Bodek }
341441d6780SZbigniew Bodek
342441d6780SZbigniew Bodek if (timeout <= 0)
343441d6780SZbigniew Bodek return (EIO);
344441d6780SZbigniew Bodek else {
345441d6780SZbigniew Bodek /* Return 0 on success */
346441d6780SZbigniew Bodek return (0);
347441d6780SZbigniew Bodek }
348441d6780SZbigniew Bodek }
349441d6780SZbigniew Bodek
350441d6780SZbigniew Bodek static int
thunder_ifmedia_change_stub(if_t ifp __unused)351b9545c57SJustin Hibbits thunder_ifmedia_change_stub(if_t ifp __unused)
352441d6780SZbigniew Bodek {
353441d6780SZbigniew Bodek /* Will never be called by if_media */
354441d6780SZbigniew Bodek return (0);
355441d6780SZbigniew Bodek }
356441d6780SZbigniew Bodek
357441d6780SZbigniew Bodek static void
thunder_ifmedia_status_stub(if_t ifp __unused,struct ifmediareq * ifmr __unused)358b9545c57SJustin Hibbits thunder_ifmedia_status_stub(if_t ifp __unused, struct ifmediareq
359441d6780SZbigniew Bodek *ifmr __unused)
360441d6780SZbigniew Bodek {
361441d6780SZbigniew Bodek /* Will never be called by if_media */
362441d6780SZbigniew Bodek }
363441d6780SZbigniew Bodek
364441d6780SZbigniew Bodek static __inline struct phy_desc *
get_phy_desc(struct thunder_mdio_softc * sc,int lmacid)365441d6780SZbigniew Bodek get_phy_desc(struct thunder_mdio_softc *sc, int lmacid)
366441d6780SZbigniew Bodek {
367441d6780SZbigniew Bodek struct phy_desc *pd = NULL;
368441d6780SZbigniew Bodek
369441d6780SZbigniew Bodek MDIO_LOCK_ASSERT(sc);
370441d6780SZbigniew Bodek TAILQ_FOREACH(pd, &sc->phy_desc_head, phy_desc_list) {
371441d6780SZbigniew Bodek if (pd->lmacid == lmacid)
372441d6780SZbigniew Bodek break;
373441d6780SZbigniew Bodek }
374441d6780SZbigniew Bodek
375441d6780SZbigniew Bodek return (pd);
376441d6780SZbigniew Bodek }
377441d6780SZbigniew Bodek static int
thunder_mdio_media_status(device_t dev,int lmacid,int * link,int * duplex,int * speed)378441d6780SZbigniew Bodek thunder_mdio_media_status(device_t dev, int lmacid, int *link, int *duplex,
379441d6780SZbigniew Bodek int *speed)
380441d6780SZbigniew Bodek {
381441d6780SZbigniew Bodek struct thunder_mdio_softc *sc;
382441d6780SZbigniew Bodek struct mii_data *mii_sc;
383441d6780SZbigniew Bodek struct phy_desc *pd;
384441d6780SZbigniew Bodek
385441d6780SZbigniew Bodek sc = device_get_softc(dev);
386441d6780SZbigniew Bodek
387441d6780SZbigniew Bodek MDIO_LOCK(sc);
388441d6780SZbigniew Bodek pd = get_phy_desc(sc, lmacid);
389441d6780SZbigniew Bodek if (pd == NULL) {
390441d6780SZbigniew Bodek /* Panic when invariants are enabled, fail otherwise. */
391441d6780SZbigniew Bodek KASSERT(0, ("%s: no PHY descriptor for LMAC%d",
392441d6780SZbigniew Bodek __func__, lmacid));
393441d6780SZbigniew Bodek MDIO_UNLOCK(sc);
394441d6780SZbigniew Bodek return (ENXIO);
395441d6780SZbigniew Bodek }
396441d6780SZbigniew Bodek mii_sc = device_get_softc(pd->miibus);
397441d6780SZbigniew Bodek
398441d6780SZbigniew Bodek mii_tick(mii_sc);
399441d6780SZbigniew Bodek if ((mii_sc->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
400441d6780SZbigniew Bodek (IFM_ACTIVE | IFM_AVALID)) {
401441d6780SZbigniew Bodek /* Link is up */
402441d6780SZbigniew Bodek *link = 1;
403441d6780SZbigniew Bodek } else
404441d6780SZbigniew Bodek *link = 0;
405441d6780SZbigniew Bodek
406441d6780SZbigniew Bodek switch (IFM_SUBTYPE(mii_sc->mii_media_active)) {
407441d6780SZbigniew Bodek case IFM_10_T:
408441d6780SZbigniew Bodek *speed = 10;
409441d6780SZbigniew Bodek break;
410441d6780SZbigniew Bodek case IFM_100_TX:
411441d6780SZbigniew Bodek *speed = 100;
412441d6780SZbigniew Bodek break;
413441d6780SZbigniew Bodek case IFM_1000_T:
414441d6780SZbigniew Bodek *speed = 1000;
415441d6780SZbigniew Bodek break;
416441d6780SZbigniew Bodek default:
417441d6780SZbigniew Bodek /* IFM_NONE */
418441d6780SZbigniew Bodek *speed = 0;
419441d6780SZbigniew Bodek }
420441d6780SZbigniew Bodek
421441d6780SZbigniew Bodek if ((IFM_OPTIONS(mii_sc->mii_media_active) & IFM_FDX) != 0)
422441d6780SZbigniew Bodek *duplex = 1;
423441d6780SZbigniew Bodek else
424441d6780SZbigniew Bodek *duplex = 0;
425441d6780SZbigniew Bodek
426441d6780SZbigniew Bodek MDIO_UNLOCK(sc);
427441d6780SZbigniew Bodek
428441d6780SZbigniew Bodek return (0);
429441d6780SZbigniew Bodek }
430441d6780SZbigniew Bodek
431441d6780SZbigniew Bodek static int
thunder_mdio_media_change(device_t dev,int lmacid,int link,int duplex,int speed)432441d6780SZbigniew Bodek thunder_mdio_media_change(device_t dev, int lmacid, int link, int duplex,
433441d6780SZbigniew Bodek int speed)
434441d6780SZbigniew Bodek {
435441d6780SZbigniew Bodek
436441d6780SZbigniew Bodek return (EIO);
437441d6780SZbigniew Bodek }
438441d6780SZbigniew Bodek
439441d6780SZbigniew Bodek static int
thunder_mdio_phy_connect(device_t dev,int lmacid,int phy)440441d6780SZbigniew Bodek thunder_mdio_phy_connect(device_t dev, int lmacid, int phy)
441441d6780SZbigniew Bodek {
442441d6780SZbigniew Bodek struct thunder_mdio_softc *sc;
443441d6780SZbigniew Bodek struct phy_desc *pd;
444441d6780SZbigniew Bodek int err;
445441d6780SZbigniew Bodek
446441d6780SZbigniew Bodek sc = device_get_softc(dev);
447441d6780SZbigniew Bodek
448441d6780SZbigniew Bodek MDIO_LOCK(sc);
449441d6780SZbigniew Bodek pd = get_phy_desc(sc, lmacid);
450441d6780SZbigniew Bodek MDIO_UNLOCK(sc);
451441d6780SZbigniew Bodek if (pd == NULL) {
452441d6780SZbigniew Bodek pd = malloc(sizeof(*pd), M_THUNDER_MDIO, (M_NOWAIT | M_ZERO));
453441d6780SZbigniew Bodek if (pd == NULL)
454441d6780SZbigniew Bodek return (ENOMEM);
455441d6780SZbigniew Bodek pd->ifp = if_alloc(IFT_ETHER);
456441d6780SZbigniew Bodek pd->lmacid = lmacid;
457441d6780SZbigniew Bodek }
458441d6780SZbigniew Bodek
459441d6780SZbigniew Bodek err = mii_attach(dev, &pd->miibus, pd->ifp,
460441d6780SZbigniew Bodek thunder_ifmedia_change_stub, thunder_ifmedia_status_stub,
461441d6780SZbigniew Bodek BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
462441d6780SZbigniew Bodek
463441d6780SZbigniew Bodek if (err != 0) {
464441d6780SZbigniew Bodek device_printf(dev, "Could not attach PHY%d\n", phy);
465441d6780SZbigniew Bodek if_free(pd->ifp);
466441d6780SZbigniew Bodek free(pd, M_THUNDER_MDIO);
467441d6780SZbigniew Bodek return (ENXIO);
468441d6780SZbigniew Bodek }
469441d6780SZbigniew Bodek
470441d6780SZbigniew Bodek MDIO_LOCK(sc);
471441d6780SZbigniew Bodek TAILQ_INSERT_TAIL(&sc->phy_desc_head, pd, phy_desc_list);
472441d6780SZbigniew Bodek MDIO_UNLOCK(sc);
473441d6780SZbigniew Bodek
474441d6780SZbigniew Bodek return (0);
475441d6780SZbigniew Bodek }
476441d6780SZbigniew Bodek
477441d6780SZbigniew Bodek static int
thunder_mdio_phy_disconnect(device_t dev,int lmacid,int phy)478441d6780SZbigniew Bodek thunder_mdio_phy_disconnect(device_t dev, int lmacid, int phy)
479441d6780SZbigniew Bodek {
480441d6780SZbigniew Bodek struct thunder_mdio_softc *sc;
481441d6780SZbigniew Bodek struct phy_desc *pd;
482441d6780SZbigniew Bodek
483441d6780SZbigniew Bodek sc = device_get_softc(dev);
484441d6780SZbigniew Bodek MDIO_LOCK(sc);
485441d6780SZbigniew Bodek
486441d6780SZbigniew Bodek pd = get_phy_desc(sc, lmacid);
487441d6780SZbigniew Bodek if (pd == NULL) {
488441d6780SZbigniew Bodek MDIO_UNLOCK(sc);
489441d6780SZbigniew Bodek return (EINVAL);
490441d6780SZbigniew Bodek }
491441d6780SZbigniew Bodek
492441d6780SZbigniew Bodek /* Remove this PHY descriptor from the list */
493441d6780SZbigniew Bodek TAILQ_REMOVE(&sc->phy_desc_head, pd, phy_desc_list);
494441d6780SZbigniew Bodek
495441d6780SZbigniew Bodek /* Detach miibus */
496441d6780SZbigniew Bodek bus_generic_detach(dev);
497441d6780SZbigniew Bodek device_delete_child(dev, pd->miibus);
498441d6780SZbigniew Bodek /* Free fake ifnet */
499441d6780SZbigniew Bodek if_free(pd->ifp);
500441d6780SZbigniew Bodek /* Free memory under phy descriptor */
501441d6780SZbigniew Bodek free(pd, M_THUNDER_MDIO);
502441d6780SZbigniew Bodek MDIO_UNLOCK(sc);
503441d6780SZbigniew Bodek
504441d6780SZbigniew Bodek return (0);
505441d6780SZbigniew Bodek }
506