1165782Sticso/*-
2165782Sticso * Copyright (c) 1997, 1998, 1999
3165782Sticso *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
4165782Sticso * Copyright (c) 2006 Bernd Walter.  All rights reserved.
5165782Sticso *
6165782Sticso * Redistribution and use in source and binary forms, with or without
7165782Sticso * modification, are permitted provided that the following conditions
8165782Sticso * are met:
9165782Sticso * 1. Redistributions of source code must retain the above copyright
10165782Sticso *    notice, this list of conditions and the following disclaimer.
11165782Sticso * 2. Redistributions in binary form must reproduce the above copyright
12165782Sticso *    notice, this list of conditions and the following disclaimer in the
13165782Sticso *    documentation and/or other materials provided with the distribution.
14165782Sticso * 3. All advertising materials mentioning features or use of this software
15165782Sticso *    must display the following acknowledgement:
16165782Sticso *	This product includes software developed by Bill Paul.
17165782Sticso * 4. Neither the name of the author nor the names of any co-contributors
18165782Sticso *    may be used to endorse or promote products derived from this software
19165782Sticso *    without specific prior written permission.
20165782Sticso *
21165782Sticso * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
22165782Sticso * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23165782Sticso * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24165782Sticso * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
25165782Sticso * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26165782Sticso * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27165782Sticso * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28165782Sticso * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29165782Sticso * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30165782Sticso * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31165782Sticso * THE POSSIBILITY OF SUCH DAMAGE.
32165782Sticso */
33165782Sticso
34165782Sticso#include <sys/cdefs.h>
35165782Sticso__FBSDID("$FreeBSD$");
36165782Sticso
37165782Sticso/*
38165782Sticso * driver for RealTek 8305 pseudo PHYs
39165782Sticso */
40165782Sticso
41165782Sticso#include <sys/param.h>
42165782Sticso#include <sys/systm.h>
43165782Sticso#include <sys/kernel.h>
44165782Sticso#include <sys/module.h>
45165782Sticso#include <sys/socket.h>
46165782Sticso#include <sys/bus.h>
47165782Sticso
48165782Sticso#include <net/if.h>
49165782Sticso#include <net/if_arp.h>
50165782Sticso#include <net/if_media.h>
51165782Sticso
52165782Sticso#include <dev/mii/mii.h>
53165782Sticso#include <dev/mii/miivar.h>
54165782Sticso#include "miidevs.h"
55165782Sticso
56165782Sticso#include <machine/bus.h>
57165782Sticso#include <pci/if_rlreg.h>
58165782Sticso
59165782Sticso#include "miibus_if.h"
60165782Sticso
61165782Sticso//#define RL_DEBUG
62165782Sticso#define RL_VLAN
63165782Sticso
64165782Sticsostatic int rlswitch_probe(device_t);
65165782Sticsostatic int rlswitch_attach(device_t);
66165782Sticso
67165782Sticsostatic device_method_t rlswitch_methods[] = {
68165782Sticso	/* device interface */
69165782Sticso	DEVMETHOD(device_probe,		rlswitch_probe),
70165782Sticso	DEVMETHOD(device_attach,	rlswitch_attach),
71165782Sticso	DEVMETHOD(device_detach,	mii_phy_detach),
72165782Sticso	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
73229093Shselasky	DEVMETHOD_END
74165782Sticso};
75165782Sticso
76165782Sticsostatic devclass_t rlswitch_devclass;
77165782Sticso
78165782Sticsostatic driver_t rlswitch_driver = {
79165782Sticso	"rlswitch",
80165782Sticso	rlswitch_methods,
81165782Sticso	sizeof(struct mii_softc)
82165782Sticso};
83165782Sticso
84165782SticsoDRIVER_MODULE(rlswitch, miibus, rlswitch_driver, rlswitch_devclass, 0, 0);
85165782Sticso
86165782Sticsostatic int	rlswitch_service(struct mii_softc *, struct mii_data *, int);
87165782Sticsostatic void	rlswitch_status(struct mii_softc *);
88165782Sticso
89165782Sticso#ifdef RL_DEBUG
90165782Sticsostatic void	rlswitch_phydump(device_t dev);
91165782Sticso#endif
92165782Sticso
93165782Sticsostatic const struct mii_phydesc rlswitches[] = {
94221407Smarius	MII_PHY_DESC(REALTEK, RTL8305SC),
95165782Sticso	MII_PHY_END
96165782Sticso};
97165782Sticso
98221407Smariusstatic const struct mii_phy_funcs rlswitch_funcs = {
99221407Smarius	rlswitch_service,
100221407Smarius	rlswitch_status,
101221407Smarius	mii_phy_reset
102221407Smarius};
103221407Smarius
104165782Sticsostatic int
105165782Sticsorlswitch_probe(device_t dev)
106165782Sticso{
107165782Sticso	int rv;
108165782Sticso
109165782Sticso	rv = mii_phy_dev_probe(dev, rlswitches, BUS_PROBE_DEFAULT);
110165782Sticso	if (rv <= 0)
111165782Sticso		return (rv);
112165782Sticso
113165782Sticso	return (ENXIO);
114165782Sticso}
115165782Sticso
116165782Sticsostatic int
117165782Sticsorlswitch_attach(device_t dev)
118165782Sticso{
119165782Sticso	struct mii_softc	*sc;
120165782Sticso
121165782Sticso	sc = device_get_softc(dev);
122165782Sticso
123213364Smarius	/*
124213364Smarius	 * We handle all pseudo PHYs in a single instance.
125213364Smarius	 */
126221407Smarius	mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE,
127221407Smarius	    &rlswitch_funcs, 0);
128165782Sticso
129221407Smarius	sc->mii_capabilities = BMSR_100TXFDX & sc->mii_capmask;
130165782Sticso	device_printf(dev, " ");
131165782Sticso	mii_phy_add_media(sc);
132165782Sticso	printf("\n");
133165782Sticso#ifdef RL_DEBUG
134165782Sticso	rlswitch_phydump(dev);
135165782Sticso#endif
136165782Sticso
137165782Sticso#ifdef RL_VLAN
138165782Sticso	int val;
139165782Sticso
140165782Sticso	/* Global Control 0 */
141165782Sticso	val = 0;
142165782Sticso	val |= 0 << 10;		/* enable 802.1q VLAN Tag support */
143165782Sticso	val |= 0 << 9;		/* enable VLAN ingress filtering */
144165782Sticso	val |= 1 << 8;		/* disable VLAN tag admit control */
145165782Sticso	val |= 1 << 6;		/* internal use */
146165782Sticso	val |= 1 << 5;		/* internal use */
147165782Sticso	val |= 1 << 4;		/* internal use */
148165782Sticso	val |= 1 << 3;		/* internal use */
149165782Sticso	val |= 1 << 1;		/* reserved */
150165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 0, 16, val);
151165782Sticso
152165782Sticso	/* Global Control 2 */
153165782Sticso	val = 0;
154165782Sticso	val |= 1 << 15;		/* reserved */
155165782Sticso	val |= 0 << 14;		/* enable 1552 Bytes support */
156165782Sticso	val |= 1 << 13;		/* enable broadcast input drop */
157165782Sticso	val |= 1 << 12;		/* forward reserved control frames */
158165782Sticso	val |= 1 << 11;		/* disable forwarding unicast frames to other VLAN's */
159165782Sticso	val |= 1 << 10;		/* disable forwarding ARP broadcasts to other VLAN's */
160165782Sticso	val |= 1 << 9;		/* enable 48 pass 1 */
161165782Sticso	val |= 0 << 8;		/* enable VLAN */
162165782Sticso	val |= 1 << 7;		/* reserved */
163165782Sticso	val |= 1 << 6;		/* enable defer */
164165782Sticso	val |= 1 << 5;		/* 43ms LED blink time */
165165782Sticso	val |= 3 << 3;		/* 16:1 queue weight */
166165782Sticso	val |= 1 << 2;		/* disable broadcast storm control */
167165782Sticso	val |= 1 << 1;		/* enable power-on LED blinking */
168165782Sticso	val |= 1 << 0;		/* reserved */
169165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 0, 18, val);
170165782Sticso
171165782Sticso	/* Port 0 Control Register 0 */
172165782Sticso	val = 0;
173165782Sticso	val |= 1 << 15;		/* reserved */
174165782Sticso	val |= 1 << 11;		/* drop received packets with wrong VLAN tag */
175165782Sticso	val |= 1 << 10;		/* disable 802.1p priority classification */
176165782Sticso	val |= 1 << 9;		/* disable diffserv priority classification */
177165782Sticso	val |= 1 << 6;		/* internal use */
178165782Sticso	val |= 3 << 4;		/* internal use */
179165782Sticso	val |= 1 << 3;		/* internal use */
180165782Sticso	val |= 1 << 2;		/* internal use */
181165782Sticso	val |= 1 << 0;		/* remove VLAN tags on output */
182165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 0, 22, val);
183165782Sticso
184165782Sticso	/* Port 1 Control Register 0 */
185165782Sticso	val = 0;
186165782Sticso	val |= 1 << 15;		/* reserved */
187165782Sticso	val |= 1 << 11;		/* drop received packets with wrong VLAN tag */
188165782Sticso	val |= 1 << 10;		/* disable 802.1p priority classification */
189165782Sticso	val |= 1 << 9;		/* disable diffserv priority classification */
190165782Sticso	val |= 1 << 6;		/* internal use */
191165782Sticso	val |= 3 << 4;		/* internal use */
192165782Sticso	val |= 1 << 3;		/* internal use */
193165782Sticso	val |= 1 << 2;		/* internal use */
194165782Sticso	val |= 1 << 0;		/* remove VLAN tags on output */
195165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 1, 22, val);
196165782Sticso
197165782Sticso	/* Port 2 Control Register 0 */
198165782Sticso	val = 0;
199165782Sticso	val |= 1 << 15;		/* reserved */
200165782Sticso	val |= 1 << 11;		/* drop received packets with wrong VLAN tag */
201165782Sticso	val |= 1 << 10;		/* disable 802.1p priority classification */
202165782Sticso	val |= 1 << 9;		/* disable diffserv priority classification */
203165782Sticso	val |= 1 << 6;		/* internal use */
204165782Sticso	val |= 3 << 4;		/* internal use */
205165782Sticso	val |= 1 << 3;		/* internal use */
206165782Sticso	val |= 1 << 2;		/* internal use */
207165782Sticso	val |= 1 << 0;		/* remove VLAN tags on output */
208165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 2, 22, val);
209165782Sticso
210165782Sticso	/* Port 3 Control Register 0 */
211165782Sticso	val = 0;
212165782Sticso	val |= 1 << 15;		/* reserved */
213165782Sticso	val |= 1 << 11;		/* drop received packets with wrong VLAN tag */
214165782Sticso	val |= 1 << 10;		/* disable 802.1p priority classification */
215165782Sticso	val |= 1 << 9;		/* disable diffserv priority classification */
216165782Sticso	val |= 1 << 6;		/* internal use */
217165782Sticso	val |= 3 << 4;		/* internal use */
218165782Sticso	val |= 1 << 3;		/* internal use */
219165782Sticso	val |= 1 << 2;		/* internal use */
220165782Sticso	val |= 1 << 0;		/* remove VLAN tags on output */
221165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 3, 22, val);
222165782Sticso
223165782Sticso	/* Port 4 (system port) Control Register 0 */
224165782Sticso	val = 0;
225165782Sticso	val |= 1 << 15;		/* reserved */
226165782Sticso	val |= 0 << 11;		/* don't drop received packets with wrong VLAN tag */
227165782Sticso	val |= 1 << 10;		/* disable 802.1p priority classification */
228165782Sticso	val |= 1 << 9;		/* disable diffserv priority classification */
229165782Sticso	val |= 1 << 6;		/* internal use */
230165782Sticso	val |= 3 << 4;		/* internal use */
231165782Sticso	val |= 1 << 3;		/* internal use */
232165782Sticso	val |= 1 << 2;		/* internal use */
233165782Sticso	val |= 2 << 0;		/* add VLAN tags for untagged packets on output */
234165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 4, 22, val);
235165782Sticso
236165782Sticso	/* Port 0 Control Register 1 and VLAN A */
237165782Sticso	val = 0;
238165782Sticso	val |= 0x0 << 12;	/* Port 0 VLAN Index */
239165782Sticso	val |= 1 << 11;		/* internal use */
240165782Sticso	val |= 1 << 10;		/* internal use */
241165782Sticso	val |= 1 << 9;		/* internal use */
242165782Sticso	val |= 1 << 7;		/* internal use */
243165782Sticso	val |= 1 << 6;		/* internal use */
244165782Sticso	val |= 0x11 << 0;	/* VLAN A membership */
245165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 0, 24, val);
246165782Sticso
247165782Sticso	/* Port 0 Control Register 2 and VLAN A */
248165782Sticso	val = 0;
249165782Sticso	val |= 1 << 15;		/* internal use */
250165782Sticso	val |= 1 << 14;		/* internal use */
251165782Sticso	val |= 1 << 13;		/* internal use */
252165782Sticso	val |= 1 << 12;		/* internal use */
253165782Sticso	val |= 0x100 << 0;	/* VLAN A ID */
254165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 0, 25, val);
255165782Sticso
256165782Sticso	/* Port 1 Control Register 1 and VLAN B */
257165782Sticso	val = 0;
258165782Sticso	val |= 0x1 << 12;	/* Port 1 VLAN Index */
259165782Sticso	val |= 1 << 11;		/* internal use */
260165782Sticso	val |= 1 << 10;		/* internal use */
261165782Sticso	val |= 1 << 9;		/* internal use */
262165782Sticso	val |= 1 << 7;		/* internal use */
263165782Sticso	val |= 1 << 6;		/* internal use */
264165782Sticso	val |= 0x12 << 0;	/* VLAN B membership */
265165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 1, 24, val);
266165782Sticso
267165782Sticso	/* Port 1 Control Register 2 and VLAN B */
268165782Sticso	val = 0;
269165782Sticso	val |= 1 << 15;		/* internal use */
270165782Sticso	val |= 1 << 14;		/* internal use */
271165782Sticso	val |= 1 << 13;		/* internal use */
272165782Sticso	val |= 1 << 12;		/* internal use */
273165782Sticso	val |= 0x101 << 0;	/* VLAN B ID */
274165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 1, 25, val);
275165782Sticso
276165782Sticso	/* Port 2 Control Register 1 and VLAN C */
277165782Sticso	val = 0;
278165782Sticso	val |= 0x2 << 12;	/* Port 2 VLAN Index */
279165782Sticso	val |= 1 << 11;		/* internal use */
280165782Sticso	val |= 1 << 10;		/* internal use */
281165782Sticso	val |= 1 << 9;		/* internal use */
282165782Sticso	val |= 1 << 7;		/* internal use */
283165782Sticso	val |= 1 << 6;		/* internal use */
284165782Sticso	val |= 0x14 << 0;	/* VLAN C membership */
285165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 2, 24, val);
286165782Sticso
287165782Sticso	/* Port 2 Control Register 2 and VLAN C */
288165782Sticso	val = 0;
289165782Sticso	val |= 1 << 15;		/* internal use */
290165782Sticso	val |= 1 << 14;		/* internal use */
291165782Sticso	val |= 1 << 13;		/* internal use */
292165782Sticso	val |= 1 << 12;		/* internal use */
293165782Sticso	val |= 0x102 << 0;	/* VLAN C ID */
294165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 2, 25, val);
295165782Sticso
296165782Sticso	/* Port 3 Control Register 1 and VLAN D */
297165782Sticso	val = 0;
298165782Sticso	val |= 0x3 << 12;	/* Port 3 VLAN Index */
299165782Sticso	val |= 1 << 11;		/* internal use */
300165782Sticso	val |= 1 << 10;		/* internal use */
301165782Sticso	val |= 1 << 9;		/* internal use */
302165782Sticso	val |= 1 << 7;		/* internal use */
303165782Sticso	val |= 1 << 6;		/* internal use */
304165782Sticso	val |= 0x18 << 0;	/* VLAN D membership */
305165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 3, 24, val);
306165782Sticso
307165782Sticso	/* Port 3 Control Register 2 and VLAN D */
308165782Sticso	val = 0;
309165782Sticso	val |= 1 << 15;		/* internal use */
310165782Sticso	val |= 1 << 14;		/* internal use */
311165782Sticso	val |= 1 << 13;		/* internal use */
312165782Sticso	val |= 1 << 12;		/* internal use */
313165782Sticso	val |= 0x103 << 0;	/* VLAN D ID */
314165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 3, 25, val);
315165782Sticso
316165782Sticso	/* Port 4 Control Register 1 and VLAN E */
317165782Sticso	val = 0;
318165782Sticso	val |= 0x0 << 12;	/* Port 4 VLAN Index */
319165782Sticso	val |= 1 << 11;		/* internal use */
320165782Sticso	val |= 1 << 10;		/* internal use */
321165782Sticso	val |= 1 << 9;		/* internal use */
322165782Sticso	val |= 1 << 7;		/* internal use */
323165782Sticso	val |= 1 << 6;		/* internal use */
324165782Sticso	val |= 0 << 0;		/* VLAN E membership */
325165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 4, 24, val);
326165782Sticso
327165782Sticso	/* Port 4 Control Register 2 and VLAN E */
328165782Sticso	val = 0;
329165782Sticso	val |= 1 << 15;		/* internal use */
330165782Sticso	val |= 1 << 14;		/* internal use */
331165782Sticso	val |= 1 << 13;		/* internal use */
332165782Sticso	val |= 1 << 12;		/* internal use */
333165782Sticso	val |= 0x104 << 0;	/* VLAN E ID */
334165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 4, 25, val);
335165782Sticso#endif
336165782Sticso
337165782Sticso#ifdef RL_DEBUG
338165782Sticso	rlswitch_phydump(dev);
339165782Sticso#endif
340165782Sticso	MIIBUS_MEDIAINIT(sc->mii_dev);
341165782Sticso	return (0);
342165782Sticso}
343165782Sticso
344165782Sticsostatic int
345165782Sticsorlswitch_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
346165782Sticso{
347165782Sticso
348165782Sticso	switch (cmd) {
349165782Sticso	case MII_POLLSTAT:
350165782Sticso		break;
351165782Sticso
352165782Sticso	case MII_MEDIACHG:
353165782Sticso		break;
354165782Sticso
355165782Sticso	case MII_TICK:
356165782Sticso		/*
357165782Sticso		 * Is the interface even up?
358165782Sticso		 */
359165782Sticso		if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
360165782Sticso			return (0);
361165782Sticso		break;
362165782Sticso	}
363165782Sticso
364165782Sticso	/* Update the media status. */
365221407Smarius	PHY_STATUS(sc);
366165782Sticso
367165782Sticso	/* Callback if something changed. */
368165782Sticso	// mii_phy_update(sc, cmd);
369165782Sticso	return (0);
370165782Sticso}
371165782Sticso
372165782Sticsostatic void
373165782Sticsorlswitch_status(struct mii_softc *phy)
374165782Sticso{
375165782Sticso	struct mii_data *mii = phy->mii_pdata;
376165782Sticso
377165782Sticso	mii->mii_media_status = IFM_AVALID;
378165782Sticso	mii->mii_media_active = IFM_ETHER;
379165782Sticso	mii->mii_media_status |= IFM_ACTIVE;
380221407Smarius	mii->mii_media_active |=
381221407Smarius	    IFM_100_TX | IFM_FDX | mii_phy_flowstatus(phy);
382165782Sticso}
383165782Sticso
384165782Sticso#ifdef RL_DEBUG
385165782Sticsostatic void
386165782Sticsorlswitch_phydump(device_t dev) {
387165782Sticso	int phy, reg, val;
388165782Sticso	struct mii_softc *sc;
389165782Sticso
390165782Sticso	sc = device_get_softc(dev);
391165782Sticso	device_printf(dev, "rlswitchphydump\n");
392165782Sticso	for (phy = 0; phy <= 5; phy++) {
393165782Sticso		printf("PHY%i:", phy);
394165782Sticso		for (reg = 0; reg <= 31; reg++) {
395165782Sticso			val = MIIBUS_READREG(sc->mii_dev, phy, reg);
396165782Sticso			printf(" 0x%x", val);
397165782Sticso		}
398165782Sticso		printf("\n");
399165782Sticso	}
400165782Sticso}
401165782Sticso#endif
402