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>
47257184Sglebius#include <sys/taskqueue.h>	/* XXXGL: if_rlreg.h contamination */
48165782Sticso
49165782Sticso#include <net/if.h>
50165782Sticso#include <net/if_arp.h>
51165782Sticso#include <net/if_media.h>
52165782Sticso
53165782Sticso#include <dev/mii/mii.h>
54165782Sticso#include <dev/mii/miivar.h>
55165782Sticso#include "miidevs.h"
56165782Sticso
57165782Sticso#include <machine/bus.h>
58271864Sglebius#include <dev/rl/if_rlreg.h>
59165782Sticso
60165782Sticso#include "miibus_if.h"
61165782Sticso
62165782Sticso//#define RL_DEBUG
63165782Sticso#define RL_VLAN
64165782Sticso
65165782Sticsostatic int rlswitch_probe(device_t);
66165782Sticsostatic int rlswitch_attach(device_t);
67165782Sticso
68165782Sticsostatic device_method_t rlswitch_methods[] = {
69165782Sticso	/* device interface */
70165782Sticso	DEVMETHOD(device_probe,		rlswitch_probe),
71165782Sticso	DEVMETHOD(device_attach,	rlswitch_attach),
72165782Sticso	DEVMETHOD(device_detach,	mii_phy_detach),
73165782Sticso	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
74227908Smarius	DEVMETHOD_END
75165782Sticso};
76165782Sticso
77165782Sticsostatic devclass_t rlswitch_devclass;
78165782Sticso
79165782Sticsostatic driver_t rlswitch_driver = {
80165782Sticso	"rlswitch",
81165782Sticso	rlswitch_methods,
82165782Sticso	sizeof(struct mii_softc)
83165782Sticso};
84165782Sticso
85165782SticsoDRIVER_MODULE(rlswitch, miibus, rlswitch_driver, rlswitch_devclass, 0, 0);
86165782Sticso
87165782Sticsostatic int	rlswitch_service(struct mii_softc *, struct mii_data *, int);
88165782Sticsostatic void	rlswitch_status(struct mii_softc *);
89165782Sticso
90165782Sticso#ifdef RL_DEBUG
91165782Sticsostatic void	rlswitch_phydump(device_t dev);
92165782Sticso#endif
93165782Sticso
94165782Sticsostatic const struct mii_phydesc rlswitches[] = {
95221407Smarius	MII_PHY_DESC(REALTEK, RTL8305SC),
96165782Sticso	MII_PHY_END
97165782Sticso};
98165782Sticso
99221407Smariusstatic const struct mii_phy_funcs rlswitch_funcs = {
100221407Smarius	rlswitch_service,
101221407Smarius	rlswitch_status,
102221407Smarius	mii_phy_reset
103221407Smarius};
104221407Smarius
105165782Sticsostatic int
106165782Sticsorlswitch_probe(device_t dev)
107165782Sticso{
108165782Sticso	int rv;
109165782Sticso
110165782Sticso	rv = mii_phy_dev_probe(dev, rlswitches, BUS_PROBE_DEFAULT);
111165782Sticso	if (rv <= 0)
112165782Sticso		return (rv);
113165782Sticso
114165782Sticso	return (ENXIO);
115165782Sticso}
116165782Sticso
117165782Sticsostatic int
118165782Sticsorlswitch_attach(device_t dev)
119165782Sticso{
120165782Sticso	struct mii_softc	*sc;
121165782Sticso
122165782Sticso	sc = device_get_softc(dev);
123165782Sticso
124213364Smarius	/*
125213364Smarius	 * We handle all pseudo PHYs in a single instance.
126213364Smarius	 */
127221407Smarius	mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE,
128221407Smarius	    &rlswitch_funcs, 0);
129165782Sticso
130221407Smarius	sc->mii_capabilities = BMSR_100TXFDX & sc->mii_capmask;
131165782Sticso	device_printf(dev, " ");
132165782Sticso	mii_phy_add_media(sc);
133165782Sticso	printf("\n");
134165782Sticso#ifdef RL_DEBUG
135165782Sticso	rlswitch_phydump(dev);
136165782Sticso#endif
137165782Sticso
138165782Sticso#ifdef RL_VLAN
139165782Sticso	int val;
140165782Sticso
141165782Sticso	/* Global Control 0 */
142165782Sticso	val = 0;
143165782Sticso	val |= 0 << 10;		/* enable 802.1q VLAN Tag support */
144165782Sticso	val |= 0 << 9;		/* enable VLAN ingress filtering */
145165782Sticso	val |= 1 << 8;		/* disable VLAN tag admit control */
146165782Sticso	val |= 1 << 6;		/* internal use */
147165782Sticso	val |= 1 << 5;		/* internal use */
148165782Sticso	val |= 1 << 4;		/* internal use */
149165782Sticso	val |= 1 << 3;		/* internal use */
150165782Sticso	val |= 1 << 1;		/* reserved */
151165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 0, 16, val);
152165782Sticso
153165782Sticso	/* Global Control 2 */
154165782Sticso	val = 0;
155165782Sticso	val |= 1 << 15;		/* reserved */
156165782Sticso	val |= 0 << 14;		/* enable 1552 Bytes support */
157165782Sticso	val |= 1 << 13;		/* enable broadcast input drop */
158165782Sticso	val |= 1 << 12;		/* forward reserved control frames */
159165782Sticso	val |= 1 << 11;		/* disable forwarding unicast frames to other VLAN's */
160165782Sticso	val |= 1 << 10;		/* disable forwarding ARP broadcasts to other VLAN's */
161165782Sticso	val |= 1 << 9;		/* enable 48 pass 1 */
162165782Sticso	val |= 0 << 8;		/* enable VLAN */
163165782Sticso	val |= 1 << 7;		/* reserved */
164165782Sticso	val |= 1 << 6;		/* enable defer */
165165782Sticso	val |= 1 << 5;		/* 43ms LED blink time */
166165782Sticso	val |= 3 << 3;		/* 16:1 queue weight */
167165782Sticso	val |= 1 << 2;		/* disable broadcast storm control */
168165782Sticso	val |= 1 << 1;		/* enable power-on LED blinking */
169165782Sticso	val |= 1 << 0;		/* reserved */
170165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 0, 18, val);
171165782Sticso
172165782Sticso	/* Port 0 Control Register 0 */
173165782Sticso	val = 0;
174165782Sticso	val |= 1 << 15;		/* reserved */
175165782Sticso	val |= 1 << 11;		/* drop received packets with wrong VLAN tag */
176165782Sticso	val |= 1 << 10;		/* disable 802.1p priority classification */
177165782Sticso	val |= 1 << 9;		/* disable diffserv priority classification */
178165782Sticso	val |= 1 << 6;		/* internal use */
179165782Sticso	val |= 3 << 4;		/* internal use */
180165782Sticso	val |= 1 << 3;		/* internal use */
181165782Sticso	val |= 1 << 2;		/* internal use */
182165782Sticso	val |= 1 << 0;		/* remove VLAN tags on output */
183165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 0, 22, val);
184165782Sticso
185165782Sticso	/* Port 1 Control Register 0 */
186165782Sticso	val = 0;
187165782Sticso	val |= 1 << 15;		/* reserved */
188165782Sticso	val |= 1 << 11;		/* drop received packets with wrong VLAN tag */
189165782Sticso	val |= 1 << 10;		/* disable 802.1p priority classification */
190165782Sticso	val |= 1 << 9;		/* disable diffserv priority classification */
191165782Sticso	val |= 1 << 6;		/* internal use */
192165782Sticso	val |= 3 << 4;		/* internal use */
193165782Sticso	val |= 1 << 3;		/* internal use */
194165782Sticso	val |= 1 << 2;		/* internal use */
195165782Sticso	val |= 1 << 0;		/* remove VLAN tags on output */
196165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 1, 22, val);
197165782Sticso
198165782Sticso	/* Port 2 Control Register 0 */
199165782Sticso	val = 0;
200165782Sticso	val |= 1 << 15;		/* reserved */
201165782Sticso	val |= 1 << 11;		/* drop received packets with wrong VLAN tag */
202165782Sticso	val |= 1 << 10;		/* disable 802.1p priority classification */
203165782Sticso	val |= 1 << 9;		/* disable diffserv priority classification */
204165782Sticso	val |= 1 << 6;		/* internal use */
205165782Sticso	val |= 3 << 4;		/* internal use */
206165782Sticso	val |= 1 << 3;		/* internal use */
207165782Sticso	val |= 1 << 2;		/* internal use */
208165782Sticso	val |= 1 << 0;		/* remove VLAN tags on output */
209165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 2, 22, val);
210165782Sticso
211165782Sticso	/* Port 3 Control Register 0 */
212165782Sticso	val = 0;
213165782Sticso	val |= 1 << 15;		/* reserved */
214165782Sticso	val |= 1 << 11;		/* drop received packets with wrong VLAN tag */
215165782Sticso	val |= 1 << 10;		/* disable 802.1p priority classification */
216165782Sticso	val |= 1 << 9;		/* disable diffserv priority classification */
217165782Sticso	val |= 1 << 6;		/* internal use */
218165782Sticso	val |= 3 << 4;		/* internal use */
219165782Sticso	val |= 1 << 3;		/* internal use */
220165782Sticso	val |= 1 << 2;		/* internal use */
221165782Sticso	val |= 1 << 0;		/* remove VLAN tags on output */
222165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 3, 22, val);
223165782Sticso
224165782Sticso	/* Port 4 (system port) Control Register 0 */
225165782Sticso	val = 0;
226165782Sticso	val |= 1 << 15;		/* reserved */
227165782Sticso	val |= 0 << 11;		/* don't drop received packets with wrong VLAN tag */
228165782Sticso	val |= 1 << 10;		/* disable 802.1p priority classification */
229165782Sticso	val |= 1 << 9;		/* disable diffserv priority classification */
230165782Sticso	val |= 1 << 6;		/* internal use */
231165782Sticso	val |= 3 << 4;		/* internal use */
232165782Sticso	val |= 1 << 3;		/* internal use */
233165782Sticso	val |= 1 << 2;		/* internal use */
234165782Sticso	val |= 2 << 0;		/* add VLAN tags for untagged packets on output */
235165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 4, 22, val);
236165782Sticso
237165782Sticso	/* Port 0 Control Register 1 and VLAN A */
238165782Sticso	val = 0;
239165782Sticso	val |= 0x0 << 12;	/* Port 0 VLAN Index */
240165782Sticso	val |= 1 << 11;		/* internal use */
241165782Sticso	val |= 1 << 10;		/* internal use */
242165782Sticso	val |= 1 << 9;		/* internal use */
243165782Sticso	val |= 1 << 7;		/* internal use */
244165782Sticso	val |= 1 << 6;		/* internal use */
245165782Sticso	val |= 0x11 << 0;	/* VLAN A membership */
246165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 0, 24, val);
247165782Sticso
248165782Sticso	/* Port 0 Control Register 2 and VLAN A */
249165782Sticso	val = 0;
250165782Sticso	val |= 1 << 15;		/* internal use */
251165782Sticso	val |= 1 << 14;		/* internal use */
252165782Sticso	val |= 1 << 13;		/* internal use */
253165782Sticso	val |= 1 << 12;		/* internal use */
254165782Sticso	val |= 0x100 << 0;	/* VLAN A ID */
255165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 0, 25, val);
256165782Sticso
257165782Sticso	/* Port 1 Control Register 1 and VLAN B */
258165782Sticso	val = 0;
259165782Sticso	val |= 0x1 << 12;	/* Port 1 VLAN Index */
260165782Sticso	val |= 1 << 11;		/* internal use */
261165782Sticso	val |= 1 << 10;		/* internal use */
262165782Sticso	val |= 1 << 9;		/* internal use */
263165782Sticso	val |= 1 << 7;		/* internal use */
264165782Sticso	val |= 1 << 6;		/* internal use */
265165782Sticso	val |= 0x12 << 0;	/* VLAN B membership */
266165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 1, 24, val);
267165782Sticso
268165782Sticso	/* Port 1 Control Register 2 and VLAN B */
269165782Sticso	val = 0;
270165782Sticso	val |= 1 << 15;		/* internal use */
271165782Sticso	val |= 1 << 14;		/* internal use */
272165782Sticso	val |= 1 << 13;		/* internal use */
273165782Sticso	val |= 1 << 12;		/* internal use */
274165782Sticso	val |= 0x101 << 0;	/* VLAN B ID */
275165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 1, 25, val);
276165782Sticso
277165782Sticso	/* Port 2 Control Register 1 and VLAN C */
278165782Sticso	val = 0;
279165782Sticso	val |= 0x2 << 12;	/* Port 2 VLAN Index */
280165782Sticso	val |= 1 << 11;		/* internal use */
281165782Sticso	val |= 1 << 10;		/* internal use */
282165782Sticso	val |= 1 << 9;		/* internal use */
283165782Sticso	val |= 1 << 7;		/* internal use */
284165782Sticso	val |= 1 << 6;		/* internal use */
285165782Sticso	val |= 0x14 << 0;	/* VLAN C membership */
286165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 2, 24, val);
287165782Sticso
288165782Sticso	/* Port 2 Control Register 2 and VLAN C */
289165782Sticso	val = 0;
290165782Sticso	val |= 1 << 15;		/* internal use */
291165782Sticso	val |= 1 << 14;		/* internal use */
292165782Sticso	val |= 1 << 13;		/* internal use */
293165782Sticso	val |= 1 << 12;		/* internal use */
294165782Sticso	val |= 0x102 << 0;	/* VLAN C ID */
295165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 2, 25, val);
296165782Sticso
297165782Sticso	/* Port 3 Control Register 1 and VLAN D */
298165782Sticso	val = 0;
299165782Sticso	val |= 0x3 << 12;	/* Port 3 VLAN Index */
300165782Sticso	val |= 1 << 11;		/* internal use */
301165782Sticso	val |= 1 << 10;		/* internal use */
302165782Sticso	val |= 1 << 9;		/* internal use */
303165782Sticso	val |= 1 << 7;		/* internal use */
304165782Sticso	val |= 1 << 6;		/* internal use */
305165782Sticso	val |= 0x18 << 0;	/* VLAN D membership */
306165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 3, 24, val);
307165782Sticso
308165782Sticso	/* Port 3 Control Register 2 and VLAN D */
309165782Sticso	val = 0;
310165782Sticso	val |= 1 << 15;		/* internal use */
311165782Sticso	val |= 1 << 14;		/* internal use */
312165782Sticso	val |= 1 << 13;		/* internal use */
313165782Sticso	val |= 1 << 12;		/* internal use */
314165782Sticso	val |= 0x103 << 0;	/* VLAN D ID */
315165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 3, 25, val);
316165782Sticso
317165782Sticso	/* Port 4 Control Register 1 and VLAN E */
318165782Sticso	val = 0;
319165782Sticso	val |= 0x0 << 12;	/* Port 4 VLAN Index */
320165782Sticso	val |= 1 << 11;		/* internal use */
321165782Sticso	val |= 1 << 10;		/* internal use */
322165782Sticso	val |= 1 << 9;		/* internal use */
323165782Sticso	val |= 1 << 7;		/* internal use */
324165782Sticso	val |= 1 << 6;		/* internal use */
325165782Sticso	val |= 0 << 0;		/* VLAN E membership */
326165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 4, 24, val);
327165782Sticso
328165782Sticso	/* Port 4 Control Register 2 and VLAN E */
329165782Sticso	val = 0;
330165782Sticso	val |= 1 << 15;		/* internal use */
331165782Sticso	val |= 1 << 14;		/* internal use */
332165782Sticso	val |= 1 << 13;		/* internal use */
333165782Sticso	val |= 1 << 12;		/* internal use */
334165782Sticso	val |= 0x104 << 0;	/* VLAN E ID */
335165782Sticso	MIIBUS_WRITEREG(sc->mii_dev, 4, 25, val);
336165782Sticso#endif
337165782Sticso
338165782Sticso#ifdef RL_DEBUG
339165782Sticso	rlswitch_phydump(dev);
340165782Sticso#endif
341165782Sticso	MIIBUS_MEDIAINIT(sc->mii_dev);
342165782Sticso	return (0);
343165782Sticso}
344165782Sticso
345165782Sticsostatic int
346165782Sticsorlswitch_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
347165782Sticso{
348165782Sticso
349165782Sticso	switch (cmd) {
350165782Sticso	case MII_POLLSTAT:
351165782Sticso		break;
352165782Sticso
353165782Sticso	case MII_MEDIACHG:
354165782Sticso		break;
355165782Sticso
356165782Sticso	case MII_TICK:
357165782Sticso		break;
358165782Sticso	}
359165782Sticso
360165782Sticso	/* Update the media status. */
361221407Smarius	PHY_STATUS(sc);
362165782Sticso
363165782Sticso	/* Callback if something changed. */
364165782Sticso	// mii_phy_update(sc, cmd);
365165782Sticso	return (0);
366165782Sticso}
367165782Sticso
368165782Sticsostatic void
369165782Sticsorlswitch_status(struct mii_softc *phy)
370165782Sticso{
371165782Sticso	struct mii_data *mii = phy->mii_pdata;
372165782Sticso
373165782Sticso	mii->mii_media_status = IFM_AVALID;
374165782Sticso	mii->mii_media_active = IFM_ETHER;
375165782Sticso	mii->mii_media_status |= IFM_ACTIVE;
376221407Smarius	mii->mii_media_active |=
377221407Smarius	    IFM_100_TX | IFM_FDX | mii_phy_flowstatus(phy);
378165782Sticso}
379165782Sticso
380165782Sticso#ifdef RL_DEBUG
381165782Sticsostatic void
382165782Sticsorlswitch_phydump(device_t dev) {
383165782Sticso	int phy, reg, val;
384165782Sticso	struct mii_softc *sc;
385165782Sticso
386165782Sticso	sc = device_get_softc(dev);
387165782Sticso	device_printf(dev, "rlswitchphydump\n");
388165782Sticso	for (phy = 0; phy <= 5; phy++) {
389165782Sticso		printf("PHY%i:", phy);
390165782Sticso		for (reg = 0; reg <= 31; reg++) {
391165782Sticso			val = MIIBUS_READREG(sc->mii_dev, phy, reg);
392165782Sticso			printf(" 0x%x", val);
393165782Sticso		}
394165782Sticso		printf("\n");
395165782Sticso	}
396165782Sticso}
397165782Sticso#endif
398