1/*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 1997, 1998, 1999
5 *	Bill Paul <wpaul@ee.columbia.edu>.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by Bill Paul.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36/*
37 * Pseudo-driver for internal NWAY support on DEC 21143 and workalike
38 * controllers.  Technically we're abusing the miibus code to handle
39 * media selection and NWAY support here since there is no MII
40 * interface.  However the logical operations are roughly the same,
41 * and the alternative is to create a fake MII interface in the driver,
42 * which is harder to do.
43 */
44
45#include <sys/param.h>
46#include <sys/systm.h>
47#include <sys/kernel.h>
48#include <sys/socket.h>
49#include <sys/errno.h>
50#include <sys/lock.h>
51#include <sys/module.h>
52#include <sys/mutex.h>
53#include <sys/bus.h>
54
55#include <net/if.h>
56#include <net/if_var.h>
57#include <net/if_arp.h>
58#include <net/if_media.h>
59
60#include <dev/mii/mii.h>
61#include <dev/mii/miivar.h>
62#include "miidevs.h"
63
64#include <machine/bus.h>
65#include <machine/resource.h>
66
67#include <dev/pci/pcivar.h>
68
69#include <dev/dc/if_dcreg.h>
70
71#include "miibus_if.h"
72
73#define DC_SETBIT(sc, reg, x)                           \
74        CSR_WRITE_4(sc, reg,                            \
75                CSR_READ_4(sc, reg) | x)
76
77#define DC_CLRBIT(sc, reg, x)                           \
78        CSR_WRITE_4(sc, reg,                            \
79                CSR_READ_4(sc, reg) & ~x)
80
81#define MIIF_AUTOTIMEOUT	0x0004
82
83/*
84 * This is the subsystem ID for the built-in 21143 ethernet
85 * in several Compaq Presario systems.  Apparently these are
86 * 10Mbps only, so we need to treat them specially.
87 */
88#define COMPAQ_PRESARIO_ID	0xb0bb0e11
89
90static int dcphy_probe(device_t);
91static int dcphy_attach(device_t);
92
93static device_method_t dcphy_methods[] = {
94	/* device interface */
95	DEVMETHOD(device_probe,		dcphy_probe),
96	DEVMETHOD(device_attach,	dcphy_attach),
97	DEVMETHOD(device_detach,	mii_phy_detach),
98	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
99	DEVMETHOD_END
100};
101
102static driver_t dcphy_driver = {
103	"dcphy",
104	dcphy_methods,
105	sizeof(struct mii_softc)
106};
107
108DRIVER_MODULE(dcphy, miibus, dcphy_driver, 0, 0);
109
110static int	dcphy_service(struct mii_softc *, struct mii_data *, int);
111static void	dcphy_status(struct mii_softc *);
112static void	dcphy_reset(struct mii_softc *);
113static int	dcphy_auto(struct mii_softc *);
114
115static const struct mii_phy_funcs dcphy_funcs = {
116	dcphy_service,
117	dcphy_status,
118	dcphy_reset
119};
120
121static int
122dcphy_probe(device_t dev)
123{
124	struct mii_attach_args *ma;
125
126	ma = device_get_ivars(dev);
127
128	/*
129	 * The dc driver will report the 21143 vendor and device
130	 * ID to let us know that it wants us to attach.
131	 */
132	if (ma->mii_id1 != DC_VENDORID_DEC ||
133	    ma->mii_id2 != DC_DEVICEID_21143)
134		return (ENXIO);
135
136	device_set_desc(dev, "Intel 21143 NWAY media interface");
137
138	return (BUS_PROBE_DEFAULT);
139}
140
141static int
142dcphy_attach(device_t dev)
143{
144	struct mii_softc *sc;
145	struct dc_softc		*dc_sc;
146	device_t brdev;
147
148	sc = device_get_softc(dev);
149
150	mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE,
151	    &dcphy_funcs, 0);
152
153	/*PHY_RESET(sc);*/
154	dc_sc = if_getsoftc(sc->mii_pdata->mii_ifp);
155	CSR_WRITE_4(dc_sc, DC_10BTSTAT, 0);
156	CSR_WRITE_4(dc_sc, DC_10BTCTRL, 0);
157
158	brdev = device_get_parent(sc->mii_dev);
159	switch (pci_get_subdevice(brdev) << 16 | pci_get_subvendor(brdev)) {
160	case COMPAQ_PRESARIO_ID:
161		/* Example of how to only allow 10Mbps modes. */
162		sc->mii_capabilities = BMSR_ANEG | BMSR_10TFDX | BMSR_10THDX;
163		break;
164	default:
165		if (dc_sc->dc_pmode == DC_PMODE_SIA)
166			sc->mii_capabilities =
167			    BMSR_ANEG | BMSR_10TFDX | BMSR_10THDX;
168		else
169			sc->mii_capabilities =
170			    BMSR_ANEG | BMSR_100TXFDX | BMSR_100TXHDX |
171			    BMSR_10TFDX | BMSR_10THDX;
172		break;
173	}
174
175	sc->mii_capabilities &= sc->mii_capmask;
176	device_printf(dev, " ");
177	mii_phy_add_media(sc);
178	printf("\n");
179
180	MIIBUS_MEDIAINIT(sc->mii_dev);
181	return (0);
182}
183
184static int
185dcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
186{
187	struct dc_softc		*dc_sc;
188	struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
189	int reg;
190	u_int32_t		mode;
191
192	dc_sc = if_getsoftc(mii->mii_ifp);
193
194	switch (cmd) {
195	case MII_POLLSTAT:
196		break;
197
198	case MII_MEDIACHG:
199		/*
200		 * If the interface is not up, don't do anything.
201		 */
202		if ((if_getflags(mii->mii_ifp) & IFF_UP) == 0)
203			break;
204
205		mii->mii_media_active = IFM_NONE;
206		mode = CSR_READ_4(dc_sc, DC_NETCFG);
207		mode &= ~(DC_NETCFG_FULLDUPLEX | DC_NETCFG_PORTSEL |
208		    DC_NETCFG_PCS | DC_NETCFG_SCRAMBLER | DC_NETCFG_SPEEDSEL);
209
210		switch (IFM_SUBTYPE(ife->ifm_media)) {
211		case IFM_AUTO:
212			/*PHY_RESET(sc);*/
213			(void)dcphy_auto(sc);
214			break;
215		case IFM_100_TX:
216			PHY_RESET(sc);
217			DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
218			mode |= DC_NETCFG_PORTSEL | DC_NETCFG_PCS |
219			    DC_NETCFG_SCRAMBLER;
220			if ((ife->ifm_media & IFM_FDX) != 0)
221				mode |= DC_NETCFG_FULLDUPLEX;
222			else
223				mode &= ~DC_NETCFG_FULLDUPLEX;
224			CSR_WRITE_4(dc_sc, DC_NETCFG, mode);
225			break;
226		case IFM_10_T:
227			DC_CLRBIT(dc_sc, DC_SIARESET, DC_SIA_RESET);
228			DC_CLRBIT(dc_sc, DC_10BTCTRL, 0xFFFF);
229			if ((ife->ifm_media & IFM_FDX) != 0)
230				DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3D);
231			else
232				DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3F);
233			DC_SETBIT(dc_sc, DC_SIARESET, DC_SIA_RESET);
234			DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
235			mode &= ~DC_NETCFG_PORTSEL;
236			mode |= DC_NETCFG_SPEEDSEL;
237			if ((ife->ifm_media & IFM_FDX) != 0)
238				mode |= DC_NETCFG_FULLDUPLEX;
239			else
240				mode &= ~DC_NETCFG_FULLDUPLEX;
241			CSR_WRITE_4(dc_sc, DC_NETCFG, mode);
242			break;
243		default:
244			return (EINVAL);
245		}
246		break;
247
248	case MII_TICK:
249		/*
250		 * Is the interface even up?
251		 */
252		if ((if_getflags(mii->mii_ifp) & IFF_UP) == 0)
253			return (0);
254
255		/*
256		 * Only used for autonegotiation.
257		 */
258		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
259			break;
260
261		reg = CSR_READ_4(dc_sc, DC_10BTSTAT);
262		if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100))
263			break;
264
265                /*
266                 * Only retry autonegotiation every 5 seconds.
267		 *
268		 * Otherwise, fall through to calling dcphy_status()
269		 * since real Intel 21143 chips don't show valid link
270		 * status until autonegotiation is switched off, and
271		 * that only happens in dcphy_status().  Without this,
272		 * successful autonegotiation is never recognised on
273		 * these chips.
274                 */
275                if (++sc->mii_ticks <= 50)
276			break;
277
278		sc->mii_ticks = 0;
279		dcphy_auto(sc);
280
281		break;
282	}
283
284	/* Update the media status. */
285	PHY_STATUS(sc);
286
287	/* Callback if something changed. */
288	mii_phy_update(sc, cmd);
289	return (0);
290}
291
292static void
293dcphy_status(struct mii_softc *sc)
294{
295	struct mii_data *mii = sc->mii_pdata;
296	int anlpar, tstat;
297	struct dc_softc		*dc_sc;
298
299	dc_sc = if_getsoftc(mii->mii_ifp);
300
301	mii->mii_media_status = IFM_AVALID;
302	mii->mii_media_active = IFM_ETHER;
303
304	if ((if_getflags(mii->mii_ifp) & IFF_UP) == 0)
305		return;
306
307	tstat = CSR_READ_4(dc_sc, DC_10BTSTAT);
308	if (!(tstat & DC_TSTAT_LS10) || !(tstat & DC_TSTAT_LS100))
309		mii->mii_media_status |= IFM_ACTIVE;
310
311	if (CSR_READ_4(dc_sc, DC_10BTCTRL) & DC_TCTL_AUTONEGENBL) {
312		/* Erg, still trying, I guess... */
313		if ((tstat & DC_TSTAT_ANEGSTAT) != DC_ASTAT_AUTONEGCMP) {
314			if ((DC_IS_MACRONIX(dc_sc) || DC_IS_PNICII(dc_sc)) &&
315			    (tstat & DC_TSTAT_ANEGSTAT) == DC_ASTAT_DISABLE)
316				goto skip;
317			mii->mii_media_active |= IFM_NONE;
318			return;
319		}
320
321		if (tstat & DC_TSTAT_LP_CAN_NWAY) {
322			anlpar = tstat >> 16;
323			if (anlpar & ANLPAR_TX_FD &&
324			    sc->mii_capabilities & BMSR_100TXFDX)
325				mii->mii_media_active |= IFM_100_TX | IFM_FDX;
326			else if (anlpar & ANLPAR_T4 &&
327			    sc->mii_capabilities & BMSR_100T4)
328				mii->mii_media_active |= IFM_100_T4 | IFM_HDX;
329			else if (anlpar & ANLPAR_TX &&
330			    sc->mii_capabilities & BMSR_100TXHDX)
331				mii->mii_media_active |= IFM_100_TX | IFM_HDX;
332			else if (anlpar & ANLPAR_10_FD)
333				mii->mii_media_active |= IFM_10_T | IFM_FDX;
334			else if (anlpar & ANLPAR_10)
335				mii->mii_media_active |= IFM_10_T | IFM_HDX;
336			else
337				mii->mii_media_active |= IFM_NONE;
338			if (DC_IS_INTEL(dc_sc))
339				DC_CLRBIT(dc_sc, DC_10BTCTRL,
340				    DC_TCTL_AUTONEGENBL);
341			return;
342		}
343
344		/*
345		 * If the other side doesn't support NWAY, then the
346		 * best we can do is determine if we have a 10Mbps or
347		 * 100Mbps link.  There's no way to know if the link
348		 * is full or half duplex, so we default to half duplex
349		 * and hope that the user is clever enough to manually
350		 * change the media settings if we're wrong.
351		 */
352		if (!(tstat & DC_TSTAT_LS100))
353			mii->mii_media_active |= IFM_100_TX | IFM_HDX;
354		else if (!(tstat & DC_TSTAT_LS10))
355			mii->mii_media_active |= IFM_10_T | IFM_HDX;
356		else
357			mii->mii_media_active |= IFM_NONE;
358		if (DC_IS_INTEL(dc_sc))
359			DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
360		return;
361	}
362
363skip:
364	if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SPEEDSEL)
365		mii->mii_media_active |= IFM_10_T;
366	else
367		mii->mii_media_active |= IFM_100_TX;
368	if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX)
369		mii->mii_media_active |= IFM_FDX;
370	else
371		mii->mii_media_active |= IFM_HDX;
372}
373
374static int
375dcphy_auto(struct mii_softc *mii)
376{
377	struct dc_softc		*sc;
378
379	sc = if_getsoftc(mii->mii_pdata->mii_ifp);
380
381	DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
382	DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX);
383	DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET);
384	if (mii->mii_capabilities & BMSR_100TXHDX)
385		CSR_WRITE_4(sc, DC_10BTCTRL, 0x3FFFF);
386	else
387		CSR_WRITE_4(sc, DC_10BTCTRL, 0xFFFF);
388	DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
389	DC_SETBIT(sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL);
390	DC_SETBIT(sc, DC_10BTSTAT, DC_ASTAT_TXDISABLE);
391
392	return (EJUSTRETURN);
393}
394
395static void
396dcphy_reset(struct mii_softc *mii)
397{
398	struct dc_softc		*sc;
399
400	sc = if_getsoftc(mii->mii_pdata->mii_ifp);
401
402	DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET);
403	DELAY(1000);
404	DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
405}
406