xref: /freebsd/sys/dev/etherswitch/micrel/ksz8995ma.c (revision 2e6a8c1ae3ce1efe5510eef495829b2b5c47f16f)
1d3bafe1dSMichael Zhilin /*-
2d3bafe1dSMichael Zhilin  * Copyright (c) 2016 Hiroki Mori
3d3bafe1dSMichael Zhilin  * Copyright (c) 2013 Luiz Otavio O Souza.
4d3bafe1dSMichael Zhilin  * Copyright (c) 2011-2012 Stefan Bethke.
5d3bafe1dSMichael Zhilin  * Copyright (c) 2012 Adrian Chadd.
6d3bafe1dSMichael Zhilin  * All rights reserved.
7d3bafe1dSMichael Zhilin  *
8d3bafe1dSMichael Zhilin  * Redistribution and use in source and binary forms, with or without
9d3bafe1dSMichael Zhilin  * modification, are permitted provided that the following conditions
10d3bafe1dSMichael Zhilin  * are met:
11d3bafe1dSMichael Zhilin  * 1. Redistributions of source code must retain the above copyright
12d3bafe1dSMichael Zhilin  *    notice, this list of conditions and the following disclaimer.
13d3bafe1dSMichael Zhilin  * 2. Redistributions in binary form must reproduce the above copyright
14d3bafe1dSMichael Zhilin  *    notice, this list of conditions and the following disclaimer in the
15d3bafe1dSMichael Zhilin  *    documentation and/or other materials provided with the distribution.
16d3bafe1dSMichael Zhilin  *
17d3bafe1dSMichael Zhilin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18d3bafe1dSMichael Zhilin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19d3bafe1dSMichael Zhilin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20d3bafe1dSMichael Zhilin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21d3bafe1dSMichael Zhilin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22d3bafe1dSMichael Zhilin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23d3bafe1dSMichael Zhilin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24d3bafe1dSMichael Zhilin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25d3bafe1dSMichael Zhilin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26d3bafe1dSMichael Zhilin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27d3bafe1dSMichael Zhilin  * SUCH DAMAGE.
28d3bafe1dSMichael Zhilin  *
29d3bafe1dSMichael Zhilin  * $FreeBSD$
30d3bafe1dSMichael Zhilin  */
31d3bafe1dSMichael Zhilin 
32d3bafe1dSMichael Zhilin /*
33d3bafe1dSMichael Zhilin  * This is Micrel KSZ8995MA driver code. KSZ8995MA use SPI bus on control.
34d3bafe1dSMichael Zhilin  * This code development on @SRCHACK's ksz8995ma board and FON2100 with
35d3bafe1dSMichael Zhilin  * gpiospi.
36d3bafe1dSMichael Zhilin  * etherswitchcfg command port option support addtag, ingress, striptag,
37d3bafe1dSMichael Zhilin  * dropuntagged.
38d3bafe1dSMichael Zhilin  */
39d3bafe1dSMichael Zhilin 
40d3bafe1dSMichael Zhilin #include <sys/param.h>
41d3bafe1dSMichael Zhilin #include <sys/bus.h>
42d3bafe1dSMichael Zhilin #include <sys/errno.h>
43d3bafe1dSMichael Zhilin #include <sys/kernel.h>
44d3bafe1dSMichael Zhilin #include <sys/lock.h>
45d3bafe1dSMichael Zhilin #include <sys/malloc.h>
46d3bafe1dSMichael Zhilin #include <sys/module.h>
47d3bafe1dSMichael Zhilin #include <sys/mutex.h>
48d3bafe1dSMichael Zhilin #include <sys/socket.h>
49d3bafe1dSMichael Zhilin #include <sys/sockio.h>
50d3bafe1dSMichael Zhilin #include <sys/sysctl.h>
51d3bafe1dSMichael Zhilin #include <sys/systm.h>
52d3bafe1dSMichael Zhilin 
53d3bafe1dSMichael Zhilin #include <net/if.h>
54d3bafe1dSMichael Zhilin #include <net/if_var.h>
55d3bafe1dSMichael Zhilin #include <net/ethernet.h>
56d3bafe1dSMichael Zhilin #include <net/if_media.h>
57d3bafe1dSMichael Zhilin #include <net/if_types.h>
58d3bafe1dSMichael Zhilin 
59d3bafe1dSMichael Zhilin #include <machine/bus.h>
60d3bafe1dSMichael Zhilin #include <dev/mii/mii.h>
61d3bafe1dSMichael Zhilin #include <dev/mii/miivar.h>
62d3bafe1dSMichael Zhilin 
63d3bafe1dSMichael Zhilin #include <dev/etherswitch/etherswitch.h>
64d3bafe1dSMichael Zhilin 
65d3bafe1dSMichael Zhilin #include <dev/spibus/spi.h>
66d3bafe1dSMichael Zhilin 
67d3bafe1dSMichael Zhilin #include "spibus_if.h"
68d3bafe1dSMichael Zhilin #include "miibus_if.h"
69d3bafe1dSMichael Zhilin #include "etherswitch_if.h"
70d3bafe1dSMichael Zhilin 
71d3bafe1dSMichael Zhilin #define	KSZ8995MA_SPI_READ		0x03
72d3bafe1dSMichael Zhilin #define	KSZ8995MA_SPI_WRITE		0x02
73d3bafe1dSMichael Zhilin 
74d3bafe1dSMichael Zhilin #define	KSZ8995MA_CID0			0x00
75d3bafe1dSMichael Zhilin #define	KSZ8995MA_CID1			0x01
76d3bafe1dSMichael Zhilin 
77d3bafe1dSMichael Zhilin #define	KSZ8995MA_GC0			0x02
78d3bafe1dSMichael Zhilin #define	KSZ8995MA_GC1			0x03
79d3bafe1dSMichael Zhilin #define	KSZ8995MA_GC2			0x04
80d3bafe1dSMichael Zhilin #define	KSZ8995MA_GC3			0x05
81d3bafe1dSMichael Zhilin 
82d3bafe1dSMichael Zhilin #define	KSZ8995MA_PORT_SIZE		0x10
83d3bafe1dSMichael Zhilin 
84d3bafe1dSMichael Zhilin #define	KSZ8995MA_PC0_BASE		0x10
85d3bafe1dSMichael Zhilin #define	KSZ8995MA_PC1_BASE		0x11
86d3bafe1dSMichael Zhilin #define	KSZ8995MA_PC2_BASE		0x12
87d3bafe1dSMichael Zhilin #define	KSZ8995MA_PC3_BASE		0x13
88d3bafe1dSMichael Zhilin #define	KSZ8995MA_PC4_BASE		0x14
89d3bafe1dSMichael Zhilin #define	KSZ8995MA_PC5_BASE		0x15
90d3bafe1dSMichael Zhilin #define	KSZ8995MA_PC6_BASE		0x16
91d3bafe1dSMichael Zhilin #define	KSZ8995MA_PC7_BASE		0x17
92d3bafe1dSMichael Zhilin #define	KSZ8995MA_PC8_BASE		0x18
93d3bafe1dSMichael Zhilin #define	KSZ8995MA_PC9_BASE		0x19
94d3bafe1dSMichael Zhilin #define	KSZ8995MA_PC10_BASE		0x1a
95d3bafe1dSMichael Zhilin #define	KSZ8995MA_PC11_BASE		0x1b
96d3bafe1dSMichael Zhilin #define	KSZ8995MA_PC12_BASE		0x1c
97d3bafe1dSMichael Zhilin #define	KSZ8995MA_PC13_BASE		0x1d
98d3bafe1dSMichael Zhilin 
99d3bafe1dSMichael Zhilin #define	KSZ8995MA_PS0_BASE		0x1e
100d3bafe1dSMichael Zhilin 
101d3bafe1dSMichael Zhilin #define	KSZ8995MA_PC14_BASE		0x1f
102d3bafe1dSMichael Zhilin 
103d3bafe1dSMichael Zhilin #define	KSZ8995MA_IAC0			0x6e
104d3bafe1dSMichael Zhilin #define	KSZ8995MA_IAC1			0x6f
105d3bafe1dSMichael Zhilin #define	KSZ8995MA_IDR8			0x70
106d3bafe1dSMichael Zhilin #define	KSZ8995MA_IDR7			0x71
107d3bafe1dSMichael Zhilin #define	KSZ8995MA_IDR6			0x72
108d3bafe1dSMichael Zhilin #define	KSZ8995MA_IDR5			0x73
109d3bafe1dSMichael Zhilin #define	KSZ8995MA_IDR4			0x74
110d3bafe1dSMichael Zhilin #define	KSZ8995MA_IDR3			0x75
111d3bafe1dSMichael Zhilin #define	KSZ8995MA_IDR2			0x76
112d3bafe1dSMichael Zhilin #define	KSZ8995MA_IDR1			0x77
113d3bafe1dSMichael Zhilin #define	KSZ8995MA_IDR0			0x78
114d3bafe1dSMichael Zhilin 
115d3bafe1dSMichael Zhilin #define	KSZ8995MA_FAMILI_ID		0x95
116d3bafe1dSMichael Zhilin #define	KSZ8995MA_CHIP_ID		0x00
117d3bafe1dSMichael Zhilin #define	KSZ8995MA_CHIP_ID_MASK		0xf0
118d3bafe1dSMichael Zhilin #define	KSZ8995MA_START			0x01
119d3bafe1dSMichael Zhilin #define	KSZ8995MA_VLAN_ENABLE		0x80
120d3bafe1dSMichael Zhilin #define	KSZ8995MA_TAG_INS		0x04
121d3bafe1dSMichael Zhilin #define	KSZ8995MA_TAG_RM		0x02
122d3bafe1dSMichael Zhilin #define	KSZ8995MA_INGR_FILT		0x40
123d3bafe1dSMichael Zhilin #define	KSZ8995MA_DROP_NONPVID		0x20
124d3bafe1dSMichael Zhilin 
125d3bafe1dSMichael Zhilin #define	KSZ8995MA_PDOWN			0x08
126d3bafe1dSMichael Zhilin #define	KSZ8995MA_STARTNEG		0x20
127d3bafe1dSMichael Zhilin 
128d3bafe1dSMichael Zhilin #define	KSZ8995MA_MII_STAT		0x7808
129d3bafe1dSMichael Zhilin #define	KSZ8995MA_MII_PHYID_H		0x0022
130d3bafe1dSMichael Zhilin #define	KSZ8995MA_MII_PHYID_L		0x1450
131d3bafe1dSMichael Zhilin #define	KSZ8995MA_MII_AA		0x0401
132d3bafe1dSMichael Zhilin 
133d3bafe1dSMichael Zhilin #define	KSZ8995MA_VLAN_TABLE_VALID	0x20
134d3bafe1dSMichael Zhilin #define	KSZ8995MA_VLAN_TABLE_READ	0x14
135d3bafe1dSMichael Zhilin #define	KSZ8995MA_VLAN_TABLE_WRITE	0x04
136d3bafe1dSMichael Zhilin 
137d3bafe1dSMichael Zhilin #define	KSZ8995MA_MAX_PORT		5
138d3bafe1dSMichael Zhilin 
139d3bafe1dSMichael Zhilin MALLOC_DECLARE(M_KSZ8995MA);
140d3bafe1dSMichael Zhilin MALLOC_DEFINE(M_KSZ8995MA, "ksz8995ma", "ksz8995ma data structures");
141d3bafe1dSMichael Zhilin 
142d3bafe1dSMichael Zhilin struct ksz8995ma_softc {
143d3bafe1dSMichael Zhilin 	struct mtx	sc_mtx;		/* serialize access to softc */
144d3bafe1dSMichael Zhilin 	device_t	sc_dev;
145d3bafe1dSMichael Zhilin 	int		vlan_mode;
146d3bafe1dSMichael Zhilin 	int		media;		/* cpu port media */
147d3bafe1dSMichael Zhilin 	int		cpuport;	/* which PHY is connected to the CPU */
148d3bafe1dSMichael Zhilin 	int		phymask;	/* PHYs we manage */
149d3bafe1dSMichael Zhilin 	int		numports;	/* number of ports */
150d3bafe1dSMichael Zhilin 	int		ifpport[KSZ8995MA_MAX_PORT];
151d3bafe1dSMichael Zhilin 	int		*portphy;
152d3bafe1dSMichael Zhilin 	char		**ifname;
153d3bafe1dSMichael Zhilin 	device_t	**miibus;
154*2e6a8c1aSJustin Hibbits 	if_t *ifp;
155d3bafe1dSMichael Zhilin 	struct callout	callout_tick;
156d3bafe1dSMichael Zhilin 	etherswitch_info_t	info;
157d3bafe1dSMichael Zhilin };
158d3bafe1dSMichael Zhilin 
159d3bafe1dSMichael Zhilin #define	KSZ8995MA_LOCK(_sc)			\
160d3bafe1dSMichael Zhilin 	    mtx_lock(&(_sc)->sc_mtx)
161d3bafe1dSMichael Zhilin #define	KSZ8995MA_UNLOCK(_sc)			\
162d3bafe1dSMichael Zhilin 	    mtx_unlock(&(_sc)->sc_mtx)
163d3bafe1dSMichael Zhilin #define	KSZ8995MA_LOCK_ASSERT(_sc, _what)	\
164d3bafe1dSMichael Zhilin 	    mtx_assert(&(_sc)->sc_mtx, (_what))
165d3bafe1dSMichael Zhilin #define	KSZ8995MA_TRYLOCK(_sc)			\
166d3bafe1dSMichael Zhilin 	    mtx_trylock(&(_sc)->sc_mtx)
167d3bafe1dSMichael Zhilin 
168d3bafe1dSMichael Zhilin #if defined(DEBUG)
169d3bafe1dSMichael Zhilin #define	DPRINTF(dev, args...) device_printf(dev, args)
170d3bafe1dSMichael Zhilin #else
171d3bafe1dSMichael Zhilin #define	DPRINTF(dev, args...)
172d3bafe1dSMichael Zhilin #endif
173d3bafe1dSMichael Zhilin 
174d3bafe1dSMichael Zhilin static inline int ksz8995ma_portforphy(struct ksz8995ma_softc *, int);
175d3bafe1dSMichael Zhilin static void ksz8995ma_tick(void *);
176*2e6a8c1aSJustin Hibbits static int ksz8995ma_ifmedia_upd(if_t );
177*2e6a8c1aSJustin Hibbits static void ksz8995ma_ifmedia_sts(if_t , struct ifmediareq *);
178d3bafe1dSMichael Zhilin static int ksz8995ma_readreg(device_t dev, int addr);
179d3bafe1dSMichael Zhilin static int ksz8995ma_writereg(device_t dev, int addr, int value);
180d3bafe1dSMichael Zhilin static void ksz8995ma_portvlanreset(device_t dev);
181d3bafe1dSMichael Zhilin 
182d3bafe1dSMichael Zhilin static int
183d3bafe1dSMichael Zhilin ksz8995ma_probe(device_t dev)
184d3bafe1dSMichael Zhilin {
185d3bafe1dSMichael Zhilin 	int id0, id1;
186d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc *sc;
187d3bafe1dSMichael Zhilin 
188d3bafe1dSMichael Zhilin 	sc = device_get_softc(dev);
189d3bafe1dSMichael Zhilin 	bzero(sc, sizeof(*sc));
190d3bafe1dSMichael Zhilin 
191d3bafe1dSMichael Zhilin 	id0 = ksz8995ma_readreg(dev, KSZ8995MA_CID0);
192d3bafe1dSMichael Zhilin 	id1 = ksz8995ma_readreg(dev, KSZ8995MA_CID1);
193d3bafe1dSMichael Zhilin 	if (bootverbose)
194d3bafe1dSMichael Zhilin 		device_printf(dev,"Chip Identifier Register %x %x\n", id0, id1);
195d3bafe1dSMichael Zhilin 
196d3bafe1dSMichael Zhilin 	/* check Product Code */
197d3bafe1dSMichael Zhilin 	if (id0 != KSZ8995MA_FAMILI_ID || (id1 & KSZ8995MA_CHIP_ID_MASK) !=
198d3bafe1dSMichael Zhilin 	    KSZ8995MA_CHIP_ID) {
199d3bafe1dSMichael Zhilin 		return (ENXIO);
200d3bafe1dSMichael Zhilin 	}
201d3bafe1dSMichael Zhilin 
202d3bafe1dSMichael Zhilin 	device_set_desc_copy(dev, "Micrel KSZ8995MA SPI switch driver");
203d3bafe1dSMichael Zhilin 	return (BUS_PROBE_DEFAULT);
204d3bafe1dSMichael Zhilin }
205d3bafe1dSMichael Zhilin 
206d3bafe1dSMichael Zhilin static int
207d3bafe1dSMichael Zhilin ksz8995ma_attach_phys(struct ksz8995ma_softc *sc)
208d3bafe1dSMichael Zhilin {
209d3bafe1dSMichael Zhilin 	int phy, port, err;
210d3bafe1dSMichael Zhilin 	char name[IFNAMSIZ];
211d3bafe1dSMichael Zhilin 
212d3bafe1dSMichael Zhilin 	port = 0;
213d3bafe1dSMichael Zhilin 	err = 0;
214d3bafe1dSMichael Zhilin 	/* PHYs need an interface, so we generate a dummy one */
215d3bafe1dSMichael Zhilin 	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
216d3bafe1dSMichael Zhilin 	for (phy = 0; phy < sc->numports; phy++) {
217d3bafe1dSMichael Zhilin 		if (phy == sc->cpuport)
218d3bafe1dSMichael Zhilin 			continue;
219d3bafe1dSMichael Zhilin 		if (((1 << phy) & sc->phymask) == 0)
220d3bafe1dSMichael Zhilin 			continue;
221d3bafe1dSMichael Zhilin 		sc->ifpport[phy] = port;
222d3bafe1dSMichael Zhilin 		sc->portphy[port] = phy;
223d3bafe1dSMichael Zhilin 		sc->ifp[port] = if_alloc(IFT_ETHER);
2240774131eSMichael Zhilin 		if (sc->ifp[port] == NULL) {
2250774131eSMichael Zhilin 			device_printf(sc->sc_dev, "couldn't allocate ifnet structure\n");
2260774131eSMichael Zhilin 			err = ENOMEM;
2270774131eSMichael Zhilin 			break;
2280774131eSMichael Zhilin 		}
2290774131eSMichael Zhilin 
230d3bafe1dSMichael Zhilin 		sc->ifp[port]->if_softc = sc;
231d3bafe1dSMichael Zhilin 		sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST |
232d3bafe1dSMichael Zhilin 		    IFF_DRV_RUNNING | IFF_SIMPLEX;
233d3bafe1dSMichael Zhilin 		if_initname(sc->ifp[port], name, port);
234d3bafe1dSMichael Zhilin 		sc->miibus[port] = malloc(sizeof(device_t), M_KSZ8995MA,
235d3bafe1dSMichael Zhilin 		    M_WAITOK | M_ZERO);
236d3bafe1dSMichael Zhilin 		if (sc->miibus[port] == NULL) {
237d3bafe1dSMichael Zhilin 			err = ENOMEM;
238d3bafe1dSMichael Zhilin 			goto failed;
239d3bafe1dSMichael Zhilin 		}
240d3bafe1dSMichael Zhilin 		err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port],
241d3bafe1dSMichael Zhilin 		    ksz8995ma_ifmedia_upd, ksz8995ma_ifmedia_sts, \
242d3bafe1dSMichael Zhilin 		    BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
243d3bafe1dSMichael Zhilin 		DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
244d3bafe1dSMichael Zhilin 		    device_get_nameunit(*sc->miibus[port]),
245d3bafe1dSMichael Zhilin 		    sc->ifp[port]->if_xname);
246d3bafe1dSMichael Zhilin 		if (err != 0) {
247d3bafe1dSMichael Zhilin 			device_printf(sc->sc_dev,
248d3bafe1dSMichael Zhilin 			    "attaching PHY %d failed\n",
249d3bafe1dSMichael Zhilin 			    phy);
250d3bafe1dSMichael Zhilin 			goto failed;
251d3bafe1dSMichael Zhilin 		}
252d3bafe1dSMichael Zhilin 		++port;
253d3bafe1dSMichael Zhilin 	}
254d3bafe1dSMichael Zhilin 	sc->info.es_nports = port;
255d3bafe1dSMichael Zhilin 	if (sc->cpuport != -1) {
256d3bafe1dSMichael Zhilin 		/* cpu port is MAC5 on ksz8995ma */
257d3bafe1dSMichael Zhilin 		sc->ifpport[sc->cpuport] = port;
258d3bafe1dSMichael Zhilin 		sc->portphy[port] = sc->cpuport;
259d3bafe1dSMichael Zhilin 		++sc->info.es_nports;
260d3bafe1dSMichael Zhilin 	}
261d3bafe1dSMichael Zhilin 
262d3bafe1dSMichael Zhilin 	return (0);
263d3bafe1dSMichael Zhilin 
264d3bafe1dSMichael Zhilin failed:
265d3bafe1dSMichael Zhilin 	for (phy = 0; phy < sc->numports; phy++) {
266d3bafe1dSMichael Zhilin 		if (((1 << phy) & sc->phymask) == 0)
267d3bafe1dSMichael Zhilin 			continue;
268d3bafe1dSMichael Zhilin 		port = ksz8995ma_portforphy(sc, phy);
269d3bafe1dSMichael Zhilin 		if (sc->miibus[port] != NULL)
270d3bafe1dSMichael Zhilin 			device_delete_child(sc->sc_dev, (*sc->miibus[port]));
271d3bafe1dSMichael Zhilin 		if (sc->ifp[port] != NULL)
272d3bafe1dSMichael Zhilin 			if_free(sc->ifp[port]);
273d3bafe1dSMichael Zhilin 		if (sc->ifname[port] != NULL)
274d3bafe1dSMichael Zhilin 			free(sc->ifname[port], M_KSZ8995MA);
275d3bafe1dSMichael Zhilin 		if (sc->miibus[port] != NULL)
276d3bafe1dSMichael Zhilin 			free(sc->miibus[port], M_KSZ8995MA);
277d3bafe1dSMichael Zhilin 	}
278d3bafe1dSMichael Zhilin 	return (err);
279d3bafe1dSMichael Zhilin }
280d3bafe1dSMichael Zhilin 
281d3bafe1dSMichael Zhilin static int
282d3bafe1dSMichael Zhilin ksz8995ma_attach(device_t dev)
283d3bafe1dSMichael Zhilin {
284d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc	*sc;
285d3bafe1dSMichael Zhilin 	int			 err, reg;
286d3bafe1dSMichael Zhilin 
287d3bafe1dSMichael Zhilin 	err = 0;
288d3bafe1dSMichael Zhilin 	sc = device_get_softc(dev);
289d3bafe1dSMichael Zhilin 
290d3bafe1dSMichael Zhilin 	sc->sc_dev = dev;
291d3bafe1dSMichael Zhilin 	mtx_init(&sc->sc_mtx, "ksz8995ma", NULL, MTX_DEF);
292d3bafe1dSMichael Zhilin 	strlcpy(sc->info.es_name, device_get_desc(dev),
293d3bafe1dSMichael Zhilin 	    sizeof(sc->info.es_name));
294d3bafe1dSMichael Zhilin 
295d3bafe1dSMichael Zhilin 	/* KSZ8995MA Defaults */
296d3bafe1dSMichael Zhilin 	sc->numports = KSZ8995MA_MAX_PORT;
297d3bafe1dSMichael Zhilin 	sc->phymask = (1 << (KSZ8995MA_MAX_PORT + 1)) - 1;
298d3bafe1dSMichael Zhilin 	sc->cpuport = -1;
299d3bafe1dSMichael Zhilin 	sc->media = 100;
300d3bafe1dSMichael Zhilin 
301d3bafe1dSMichael Zhilin 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
302d3bafe1dSMichael Zhilin 	    "cpuport", &sc->cpuport);
303d3bafe1dSMichael Zhilin 
304d3bafe1dSMichael Zhilin 	sc->info.es_nvlangroups = 16;
305d3bafe1dSMichael Zhilin 	sc->info.es_vlan_caps = ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOT1Q;
306d3bafe1dSMichael Zhilin 
307*2e6a8c1aSJustin Hibbits 	sc->ifp = malloc(sizeof(if_t ) * sc->numports, M_KSZ8995MA,
308d3bafe1dSMichael Zhilin 	    M_WAITOK | M_ZERO);
309d3bafe1dSMichael Zhilin 	sc->ifname = malloc(sizeof(char *) * sc->numports, M_KSZ8995MA,
310d3bafe1dSMichael Zhilin 	    M_WAITOK | M_ZERO);
311d3bafe1dSMichael Zhilin 	sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_KSZ8995MA,
312d3bafe1dSMichael Zhilin 	    M_WAITOK | M_ZERO);
313d3bafe1dSMichael Zhilin 	sc->portphy = malloc(sizeof(int) * sc->numports, M_KSZ8995MA,
314d3bafe1dSMichael Zhilin 	    M_WAITOK | M_ZERO);
315d3bafe1dSMichael Zhilin 
316d3bafe1dSMichael Zhilin 	if (sc->ifp == NULL || sc->ifname == NULL || sc->miibus == NULL ||
317d3bafe1dSMichael Zhilin 	    sc->portphy == NULL) {
318d3bafe1dSMichael Zhilin 		err = ENOMEM;
319d3bafe1dSMichael Zhilin 		goto failed;
320d3bafe1dSMichael Zhilin 	}
321d3bafe1dSMichael Zhilin 
322d3bafe1dSMichael Zhilin 	/*
323d3bafe1dSMichael Zhilin 	 * Attach the PHYs and complete the bus enumeration.
324d3bafe1dSMichael Zhilin 	 */
325d3bafe1dSMichael Zhilin 	err = ksz8995ma_attach_phys(sc);
326d3bafe1dSMichael Zhilin 	if (err != 0)
327d3bafe1dSMichael Zhilin 		goto failed;
328d3bafe1dSMichael Zhilin 
329d3bafe1dSMichael Zhilin 	bus_generic_probe(dev);
330d3bafe1dSMichael Zhilin 	bus_enumerate_hinted_children(dev);
331d3bafe1dSMichael Zhilin 	err = bus_generic_attach(dev);
332d3bafe1dSMichael Zhilin 	if (err != 0)
333d3bafe1dSMichael Zhilin 		goto failed;
334d3bafe1dSMichael Zhilin 
335d3bafe1dSMichael Zhilin 	callout_init(&sc->callout_tick, 0);
336d3bafe1dSMichael Zhilin 
337d3bafe1dSMichael Zhilin 	ksz8995ma_tick(sc);
338d3bafe1dSMichael Zhilin 
339d3bafe1dSMichael Zhilin 	/* start switch */
340d3bafe1dSMichael Zhilin 	sc->vlan_mode = 0;
341d3bafe1dSMichael Zhilin 	reg = ksz8995ma_readreg(dev, KSZ8995MA_GC3);
342d3bafe1dSMichael Zhilin 	ksz8995ma_writereg(dev, KSZ8995MA_GC3,
343d3bafe1dSMichael Zhilin 	    reg & ~KSZ8995MA_VLAN_ENABLE);
344d3bafe1dSMichael Zhilin 	ksz8995ma_portvlanreset(dev);
345d3bafe1dSMichael Zhilin 	ksz8995ma_writereg(dev, KSZ8995MA_CID1, KSZ8995MA_START);
346d3bafe1dSMichael Zhilin 
347d3bafe1dSMichael Zhilin 	return (0);
348d3bafe1dSMichael Zhilin 
349d3bafe1dSMichael Zhilin failed:
350d3bafe1dSMichael Zhilin 	if (sc->portphy != NULL)
351d3bafe1dSMichael Zhilin 		free(sc->portphy, M_KSZ8995MA);
352d3bafe1dSMichael Zhilin 	if (sc->miibus != NULL)
353d3bafe1dSMichael Zhilin 		free(sc->miibus, M_KSZ8995MA);
354d3bafe1dSMichael Zhilin 	if (sc->ifname != NULL)
355d3bafe1dSMichael Zhilin 		free(sc->ifname, M_KSZ8995MA);
356d3bafe1dSMichael Zhilin 	if (sc->ifp != NULL)
357d3bafe1dSMichael Zhilin 		free(sc->ifp, M_KSZ8995MA);
358d3bafe1dSMichael Zhilin 
359d3bafe1dSMichael Zhilin 	return (err);
360d3bafe1dSMichael Zhilin }
361d3bafe1dSMichael Zhilin 
362d3bafe1dSMichael Zhilin static int
363d3bafe1dSMichael Zhilin ksz8995ma_detach(device_t dev)
364d3bafe1dSMichael Zhilin {
365d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc	*sc;
366d3bafe1dSMichael Zhilin 	int			 i, port;
367d3bafe1dSMichael Zhilin 
368d3bafe1dSMichael Zhilin 	sc = device_get_softc(dev);
369d3bafe1dSMichael Zhilin 
370d3bafe1dSMichael Zhilin 	callout_drain(&sc->callout_tick);
371d3bafe1dSMichael Zhilin 
372d3bafe1dSMichael Zhilin 	for (i = 0; i < KSZ8995MA_MAX_PORT; i++) {
373d3bafe1dSMichael Zhilin 		if (((1 << i) & sc->phymask) == 0)
374d3bafe1dSMichael Zhilin 			continue;
375d3bafe1dSMichael Zhilin 		port = ksz8995ma_portforphy(sc, i);
376d3bafe1dSMichael Zhilin 		if (sc->miibus[port] != NULL)
377d3bafe1dSMichael Zhilin 			device_delete_child(dev, (*sc->miibus[port]));
378d3bafe1dSMichael Zhilin 		if (sc->ifp[port] != NULL)
379d3bafe1dSMichael Zhilin 			if_free(sc->ifp[port]);
380d3bafe1dSMichael Zhilin 		free(sc->ifname[port], M_KSZ8995MA);
381d3bafe1dSMichael Zhilin 		free(sc->miibus[port], M_KSZ8995MA);
382d3bafe1dSMichael Zhilin 	}
383d3bafe1dSMichael Zhilin 
384d3bafe1dSMichael Zhilin 	free(sc->portphy, M_KSZ8995MA);
385d3bafe1dSMichael Zhilin 	free(sc->miibus, M_KSZ8995MA);
386d3bafe1dSMichael Zhilin 	free(sc->ifname, M_KSZ8995MA);
387d3bafe1dSMichael Zhilin 	free(sc->ifp, M_KSZ8995MA);
388d3bafe1dSMichael Zhilin 
389d3bafe1dSMichael Zhilin 	bus_generic_detach(dev);
390d3bafe1dSMichael Zhilin 	mtx_destroy(&sc->sc_mtx);
391d3bafe1dSMichael Zhilin 
392d3bafe1dSMichael Zhilin 	return (0);
393d3bafe1dSMichael Zhilin }
394d3bafe1dSMichael Zhilin 
395d3bafe1dSMichael Zhilin /*
396d3bafe1dSMichael Zhilin  * Convert PHY number to port number.
397d3bafe1dSMichael Zhilin  */
398d3bafe1dSMichael Zhilin static inline int
399d3bafe1dSMichael Zhilin ksz8995ma_portforphy(struct ksz8995ma_softc *sc, int phy)
400d3bafe1dSMichael Zhilin {
401d3bafe1dSMichael Zhilin 
402d3bafe1dSMichael Zhilin 	return (sc->ifpport[phy]);
403d3bafe1dSMichael Zhilin }
404d3bafe1dSMichael Zhilin 
405d3bafe1dSMichael Zhilin static inline struct mii_data *
406d3bafe1dSMichael Zhilin ksz8995ma_miiforport(struct ksz8995ma_softc *sc, int port)
407d3bafe1dSMichael Zhilin {
408d3bafe1dSMichael Zhilin 
409d3bafe1dSMichael Zhilin 	if (port < 0 || port > sc->numports)
410d3bafe1dSMichael Zhilin 		return (NULL);
411d3bafe1dSMichael Zhilin 	if (port == sc->cpuport)
412d3bafe1dSMichael Zhilin 		return (NULL);
413d3bafe1dSMichael Zhilin 	return (device_get_softc(*sc->miibus[port]));
414d3bafe1dSMichael Zhilin }
415d3bafe1dSMichael Zhilin 
416*2e6a8c1aSJustin Hibbits static inline if_t
417d3bafe1dSMichael Zhilin ksz8995ma_ifpforport(struct ksz8995ma_softc *sc, int port)
418d3bafe1dSMichael Zhilin {
419d3bafe1dSMichael Zhilin 
420d3bafe1dSMichael Zhilin 	if (port < 0 || port > sc->numports)
421d3bafe1dSMichael Zhilin 		return (NULL);
422d3bafe1dSMichael Zhilin 	return (sc->ifp[port]);
423d3bafe1dSMichael Zhilin }
424d3bafe1dSMichael Zhilin 
425d3bafe1dSMichael Zhilin /*
426d3bafe1dSMichael Zhilin  * Poll the status for all PHYs.
427d3bafe1dSMichael Zhilin  */
428d3bafe1dSMichael Zhilin static void
429d3bafe1dSMichael Zhilin ksz8995ma_miipollstat(struct ksz8995ma_softc *sc)
430d3bafe1dSMichael Zhilin {
431d3bafe1dSMichael Zhilin 	int i, port;
432d3bafe1dSMichael Zhilin 	struct mii_data *mii;
433d3bafe1dSMichael Zhilin 	struct mii_softc *miisc;
434d3bafe1dSMichael Zhilin 
435d3bafe1dSMichael Zhilin 	KSZ8995MA_LOCK_ASSERT(sc, MA_NOTOWNED);
436d3bafe1dSMichael Zhilin 
437d3bafe1dSMichael Zhilin 	for (i = 0; i < KSZ8995MA_MAX_PORT; i++) {
438d3bafe1dSMichael Zhilin 		if (i == sc->cpuport)
439d3bafe1dSMichael Zhilin 			continue;
440d3bafe1dSMichael Zhilin 		if (((1 << i) & sc->phymask) == 0)
441d3bafe1dSMichael Zhilin 			continue;
442d3bafe1dSMichael Zhilin 		port = ksz8995ma_portforphy(sc, i);
443d3bafe1dSMichael Zhilin 		if ((*sc->miibus[port]) == NULL)
444d3bafe1dSMichael Zhilin 			continue;
445d3bafe1dSMichael Zhilin 		mii = device_get_softc(*sc->miibus[port]);
446d3bafe1dSMichael Zhilin 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
447d3bafe1dSMichael Zhilin 			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
448d3bafe1dSMichael Zhilin 			    miisc->mii_inst)
449d3bafe1dSMichael Zhilin 				continue;
450d3bafe1dSMichael Zhilin 			ukphy_status(miisc);
451d3bafe1dSMichael Zhilin 			mii_phy_update(miisc, MII_POLLSTAT);
452d3bafe1dSMichael Zhilin 		}
453d3bafe1dSMichael Zhilin 	}
454d3bafe1dSMichael Zhilin }
455d3bafe1dSMichael Zhilin 
456d3bafe1dSMichael Zhilin static void
457d3bafe1dSMichael Zhilin ksz8995ma_tick(void *arg)
458d3bafe1dSMichael Zhilin {
459d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc *sc;
460d3bafe1dSMichael Zhilin 
461d3bafe1dSMichael Zhilin 	sc = arg;
462d3bafe1dSMichael Zhilin 
463d3bafe1dSMichael Zhilin 	ksz8995ma_miipollstat(sc);
464d3bafe1dSMichael Zhilin 	callout_reset(&sc->callout_tick, hz, ksz8995ma_tick, sc);
465d3bafe1dSMichael Zhilin }
466d3bafe1dSMichael Zhilin 
467d3bafe1dSMichael Zhilin static void
468d3bafe1dSMichael Zhilin ksz8995ma_lock(device_t dev)
469d3bafe1dSMichael Zhilin {
470d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc *sc;
471d3bafe1dSMichael Zhilin 
472d3bafe1dSMichael Zhilin 	sc = device_get_softc(dev);
473d3bafe1dSMichael Zhilin 
474d3bafe1dSMichael Zhilin 	KSZ8995MA_LOCK_ASSERT(sc, MA_NOTOWNED);
475d3bafe1dSMichael Zhilin 	KSZ8995MA_LOCK(sc);
476d3bafe1dSMichael Zhilin }
477d3bafe1dSMichael Zhilin 
478d3bafe1dSMichael Zhilin static void
479d3bafe1dSMichael Zhilin ksz8995ma_unlock(device_t dev)
480d3bafe1dSMichael Zhilin {
481d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc *sc;
482d3bafe1dSMichael Zhilin 
483d3bafe1dSMichael Zhilin 	sc = device_get_softc(dev);
484d3bafe1dSMichael Zhilin 
485d3bafe1dSMichael Zhilin 	KSZ8995MA_LOCK_ASSERT(sc, MA_OWNED);
486d3bafe1dSMichael Zhilin 	KSZ8995MA_UNLOCK(sc);
487d3bafe1dSMichael Zhilin }
488d3bafe1dSMichael Zhilin 
489d3bafe1dSMichael Zhilin static etherswitch_info_t *
490d3bafe1dSMichael Zhilin ksz8995ma_getinfo(device_t dev)
491d3bafe1dSMichael Zhilin {
492d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc *sc;
493d3bafe1dSMichael Zhilin 
494d3bafe1dSMichael Zhilin 	sc = device_get_softc(dev);
495d3bafe1dSMichael Zhilin 
496d3bafe1dSMichael Zhilin 	return (&sc->info);
497d3bafe1dSMichael Zhilin }
498d3bafe1dSMichael Zhilin 
499d3bafe1dSMichael Zhilin static int
500d3bafe1dSMichael Zhilin ksz8995ma_getport(device_t dev, etherswitch_port_t *p)
501d3bafe1dSMichael Zhilin {
502d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc *sc;
503d3bafe1dSMichael Zhilin 	struct mii_data *mii;
504d3bafe1dSMichael Zhilin 	struct ifmediareq *ifmr;
505d3bafe1dSMichael Zhilin 	int phy, err;
506d3bafe1dSMichael Zhilin 	int tag1, tag2, portreg;
507d3bafe1dSMichael Zhilin 
508d3bafe1dSMichael Zhilin 	sc = device_get_softc(dev);
509d3bafe1dSMichael Zhilin 	ifmr = &p->es_ifmr;
510d3bafe1dSMichael Zhilin 
511d3bafe1dSMichael Zhilin 	if (p->es_port < 0 || p->es_port >= sc->numports)
512d3bafe1dSMichael Zhilin 		return (ENXIO);
513d3bafe1dSMichael Zhilin 
514d3bafe1dSMichael Zhilin 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
515d3bafe1dSMichael Zhilin 		tag1 = ksz8995ma_readreg(dev, KSZ8995MA_PC3_BASE +
516d3bafe1dSMichael Zhilin 		    KSZ8995MA_PORT_SIZE * p->es_port);
517d3bafe1dSMichael Zhilin 		tag2 = ksz8995ma_readreg(dev, KSZ8995MA_PC4_BASE +
518d3bafe1dSMichael Zhilin 		    KSZ8995MA_PORT_SIZE * p->es_port);
519d3bafe1dSMichael Zhilin 		p->es_pvid = (tag1 & 0x0f) << 8 | tag2;
520d3bafe1dSMichael Zhilin 
521d3bafe1dSMichael Zhilin 		portreg = ksz8995ma_readreg(dev, KSZ8995MA_PC0_BASE +
522d3bafe1dSMichael Zhilin 		    KSZ8995MA_PORT_SIZE * p->es_port);
523d3bafe1dSMichael Zhilin 		if (portreg & KSZ8995MA_TAG_INS)
524d3bafe1dSMichael Zhilin 			p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
525d3bafe1dSMichael Zhilin 		if (portreg & KSZ8995MA_TAG_RM)
526d3bafe1dSMichael Zhilin 			p->es_flags |= ETHERSWITCH_PORT_STRIPTAG;
527d3bafe1dSMichael Zhilin 
528d3bafe1dSMichael Zhilin 		portreg = ksz8995ma_readreg(dev, KSZ8995MA_PC2_BASE +
529d3bafe1dSMichael Zhilin 		    KSZ8995MA_PORT_SIZE * p->es_port);
530d3bafe1dSMichael Zhilin 		if (portreg & KSZ8995MA_DROP_NONPVID)
531d3bafe1dSMichael Zhilin 			p->es_flags |= ETHERSWITCH_PORT_DROPUNTAGGED;
532d3bafe1dSMichael Zhilin 		if (portreg & KSZ8995MA_INGR_FILT)
533d3bafe1dSMichael Zhilin 			p->es_flags |= ETHERSWITCH_PORT_INGRESS;
534d3bafe1dSMichael Zhilin 	}
535d3bafe1dSMichael Zhilin 
536d3bafe1dSMichael Zhilin 	phy = sc->portphy[p->es_port];
537d3bafe1dSMichael Zhilin 	mii = ksz8995ma_miiforport(sc, p->es_port);
538d3bafe1dSMichael Zhilin 	if (sc->cpuport != -1 && phy == sc->cpuport) {
539d3bafe1dSMichael Zhilin 		/* fill in fixed values for CPU port */
540d3bafe1dSMichael Zhilin 		p->es_flags |= ETHERSWITCH_PORT_CPU;
541d3bafe1dSMichael Zhilin 		ifmr->ifm_count = 0;
542d3bafe1dSMichael Zhilin 		if (sc->media == 100)
543d3bafe1dSMichael Zhilin 			ifmr->ifm_current = ifmr->ifm_active =
544d3bafe1dSMichael Zhilin 			    IFM_ETHER | IFM_100_TX | IFM_FDX;
545d3bafe1dSMichael Zhilin 		else
546d3bafe1dSMichael Zhilin 			ifmr->ifm_current = ifmr->ifm_active =
547d3bafe1dSMichael Zhilin 			    IFM_ETHER | IFM_1000_T | IFM_FDX;
548d3bafe1dSMichael Zhilin 		ifmr->ifm_mask = 0;
549d3bafe1dSMichael Zhilin 		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
550d3bafe1dSMichael Zhilin 	} else if (mii != NULL) {
551d3bafe1dSMichael Zhilin 		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
552d3bafe1dSMichael Zhilin 		    &mii->mii_media, SIOCGIFMEDIA);
553d3bafe1dSMichael Zhilin 		if (err)
554d3bafe1dSMichael Zhilin 			return (err);
555d3bafe1dSMichael Zhilin 	} else {
556d3bafe1dSMichael Zhilin 		return (ENXIO);
557d3bafe1dSMichael Zhilin 	}
558d3bafe1dSMichael Zhilin 
559d3bafe1dSMichael Zhilin 	return (0);
560d3bafe1dSMichael Zhilin }
561d3bafe1dSMichael Zhilin 
562d3bafe1dSMichael Zhilin static int
563d3bafe1dSMichael Zhilin ksz8995ma_setport(device_t dev, etherswitch_port_t *p)
564d3bafe1dSMichael Zhilin {
565d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc *sc;
566d3bafe1dSMichael Zhilin 	struct mii_data *mii;
567d3bafe1dSMichael Zhilin         struct ifmedia *ifm;
568*2e6a8c1aSJustin Hibbits         if_t ifp;
569d3bafe1dSMichael Zhilin 	int phy, err;
570d3bafe1dSMichael Zhilin 	int portreg;
571d3bafe1dSMichael Zhilin 
572d3bafe1dSMichael Zhilin 	sc = device_get_softc(dev);
573d3bafe1dSMichael Zhilin 
574d3bafe1dSMichael Zhilin 	if (p->es_port < 0 || p->es_port >= sc->numports)
575d3bafe1dSMichael Zhilin 		return (ENXIO);
576d3bafe1dSMichael Zhilin 
577d3bafe1dSMichael Zhilin 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
578d3bafe1dSMichael Zhilin 		ksz8995ma_writereg(dev, KSZ8995MA_PC4_BASE +
579d3bafe1dSMichael Zhilin 		    KSZ8995MA_PORT_SIZE * p->es_port, p->es_pvid & 0xff);
580d3bafe1dSMichael Zhilin 		portreg = ksz8995ma_readreg(dev, KSZ8995MA_PC3_BASE +
581d3bafe1dSMichael Zhilin 		    KSZ8995MA_PORT_SIZE * p->es_port);
582d3bafe1dSMichael Zhilin 		ksz8995ma_writereg(dev, KSZ8995MA_PC3_BASE +
583d3bafe1dSMichael Zhilin 		    KSZ8995MA_PORT_SIZE * p->es_port,
584d3bafe1dSMichael Zhilin 		    (portreg & 0xf0) | ((p->es_pvid >> 8) & 0x0f));
585d3bafe1dSMichael Zhilin 
586d3bafe1dSMichael Zhilin 		portreg = ksz8995ma_readreg(dev, KSZ8995MA_PC0_BASE +
587d3bafe1dSMichael Zhilin 		    KSZ8995MA_PORT_SIZE * p->es_port);
588d3bafe1dSMichael Zhilin 		if (p->es_flags & ETHERSWITCH_PORT_ADDTAG)
589d3bafe1dSMichael Zhilin 			portreg |= KSZ8995MA_TAG_INS;
590d3bafe1dSMichael Zhilin 		else
591d3bafe1dSMichael Zhilin 			portreg &= ~KSZ8995MA_TAG_INS;
592d3bafe1dSMichael Zhilin 		if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG)
593d3bafe1dSMichael Zhilin 			portreg |= KSZ8995MA_TAG_RM;
594d3bafe1dSMichael Zhilin 		else
595d3bafe1dSMichael Zhilin 			portreg &= ~KSZ8995MA_TAG_RM;
596d3bafe1dSMichael Zhilin 		ksz8995ma_writereg(dev, KSZ8995MA_PC0_BASE +
597d3bafe1dSMichael Zhilin 		    KSZ8995MA_PORT_SIZE * p->es_port, portreg);
598d3bafe1dSMichael Zhilin 
599d3bafe1dSMichael Zhilin 		portreg = ksz8995ma_readreg(dev, KSZ8995MA_PC2_BASE +
600d3bafe1dSMichael Zhilin 		    KSZ8995MA_PORT_SIZE * p->es_port);
601d3bafe1dSMichael Zhilin 		if (p->es_flags & ETHERSWITCH_PORT_DROPUNTAGGED)
602d3bafe1dSMichael Zhilin 			portreg |= KSZ8995MA_DROP_NONPVID;
603d3bafe1dSMichael Zhilin 		else
604d3bafe1dSMichael Zhilin 			portreg &= ~KSZ8995MA_DROP_NONPVID;
605d3bafe1dSMichael Zhilin 		if (p->es_flags & ETHERSWITCH_PORT_INGRESS)
606d3bafe1dSMichael Zhilin 			portreg |= KSZ8995MA_INGR_FILT;
607d3bafe1dSMichael Zhilin 		else
608d3bafe1dSMichael Zhilin 			portreg &= ~KSZ8995MA_INGR_FILT;
609d3bafe1dSMichael Zhilin 		ksz8995ma_writereg(dev, KSZ8995MA_PC2_BASE +
610d3bafe1dSMichael Zhilin 		    KSZ8995MA_PORT_SIZE * p->es_port, portreg);
611d3bafe1dSMichael Zhilin 	}
612d3bafe1dSMichael Zhilin 
613d3bafe1dSMichael Zhilin 	phy = sc->portphy[p->es_port];
614d3bafe1dSMichael Zhilin 	mii = ksz8995ma_miiforport(sc, p->es_port);
615d3bafe1dSMichael Zhilin 	if (phy != sc->cpuport) {
616d3bafe1dSMichael Zhilin 		if (mii == NULL)
617d3bafe1dSMichael Zhilin 			return (ENXIO);
618d3bafe1dSMichael Zhilin 		ifp = ksz8995ma_ifpforport(sc, p->es_port);
619d3bafe1dSMichael Zhilin 		ifm = &mii->mii_media;
620d3bafe1dSMichael Zhilin 		err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA);
621d3bafe1dSMichael Zhilin 	}
622d3bafe1dSMichael Zhilin 	return (0);
623d3bafe1dSMichael Zhilin }
624d3bafe1dSMichael Zhilin 
625d3bafe1dSMichael Zhilin static int
626d3bafe1dSMichael Zhilin ksz8995ma_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
627d3bafe1dSMichael Zhilin {
628d3bafe1dSMichael Zhilin 	int data0, data1, data2;
629d3bafe1dSMichael Zhilin 	int vlantab;
630d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc *sc;
631d3bafe1dSMichael Zhilin 
632d3bafe1dSMichael Zhilin 	sc = device_get_softc(dev);
633d3bafe1dSMichael Zhilin 
634d3bafe1dSMichael Zhilin 	if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
635d3bafe1dSMichael Zhilin 		if (vg->es_vlangroup < sc->numports) {
636d3bafe1dSMichael Zhilin 			vg->es_vid = ETHERSWITCH_VID_VALID;
637d3bafe1dSMichael Zhilin 			vg->es_vid |= vg->es_vlangroup;
638d3bafe1dSMichael Zhilin 			data0 = ksz8995ma_readreg(dev, KSZ8995MA_PC1_BASE +
639d3bafe1dSMichael Zhilin 			    KSZ8995MA_PORT_SIZE * vg->es_vlangroup);
640d3bafe1dSMichael Zhilin 			vg->es_member_ports = data0 & 0x1f;
641d3bafe1dSMichael Zhilin 			vg->es_untagged_ports = vg->es_member_ports;
642d3bafe1dSMichael Zhilin 			vg->es_fid = 0;
643d3bafe1dSMichael Zhilin 		} else {
644d3bafe1dSMichael Zhilin 			vg->es_vid = 0;
645d3bafe1dSMichael Zhilin 		}
646d3bafe1dSMichael Zhilin 	} else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
647d3bafe1dSMichael Zhilin 		ksz8995ma_writereg(dev, KSZ8995MA_IAC0,
648d3bafe1dSMichael Zhilin 		    KSZ8995MA_VLAN_TABLE_READ);
649d3bafe1dSMichael Zhilin 		ksz8995ma_writereg(dev, KSZ8995MA_IAC1, vg->es_vlangroup);
650d3bafe1dSMichael Zhilin 		data2 = ksz8995ma_readreg(dev, KSZ8995MA_IDR2);
651d3bafe1dSMichael Zhilin 		data1 = ksz8995ma_readreg(dev, KSZ8995MA_IDR1);
652d3bafe1dSMichael Zhilin 		data0 = ksz8995ma_readreg(dev, KSZ8995MA_IDR0);
653d3bafe1dSMichael Zhilin 		vlantab = data2 << 16 | data1 << 8 | data0;
654d3bafe1dSMichael Zhilin 		if (data2 & KSZ8995MA_VLAN_TABLE_VALID) {
655d3bafe1dSMichael Zhilin 			vg->es_vid = ETHERSWITCH_VID_VALID;
656d3bafe1dSMichael Zhilin 			vg->es_vid |= vlantab & 0xfff;
657d3bafe1dSMichael Zhilin 			vg->es_member_ports = (vlantab >> 16) & 0x1f;
658d3bafe1dSMichael Zhilin 			vg->es_untagged_ports = vg->es_member_ports;
659d3bafe1dSMichael Zhilin 			vg->es_fid = (vlantab >> 12) & 0x0f;
660d3bafe1dSMichael Zhilin 		} else {
661d3bafe1dSMichael Zhilin 			vg->es_fid = 0;
662d3bafe1dSMichael Zhilin 		}
663d3bafe1dSMichael Zhilin 	}
664d3bafe1dSMichael Zhilin 
665d3bafe1dSMichael Zhilin 	return (0);
666d3bafe1dSMichael Zhilin }
667d3bafe1dSMichael Zhilin 
668d3bafe1dSMichael Zhilin static int
669d3bafe1dSMichael Zhilin ksz8995ma_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
670d3bafe1dSMichael Zhilin {
671d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc *sc;
672d3bafe1dSMichael Zhilin 	int data0;
673d3bafe1dSMichael Zhilin 
674d3bafe1dSMichael Zhilin 	sc = device_get_softc(dev);
675d3bafe1dSMichael Zhilin 
676d3bafe1dSMichael Zhilin 	if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
677d3bafe1dSMichael Zhilin 		data0 = ksz8995ma_readreg(dev, KSZ8995MA_PC1_BASE +
678d3bafe1dSMichael Zhilin 		    KSZ8995MA_PORT_SIZE * vg->es_vlangroup);
679d3bafe1dSMichael Zhilin 		ksz8995ma_writereg(dev, KSZ8995MA_PC1_BASE +
680d3bafe1dSMichael Zhilin 		    KSZ8995MA_PORT_SIZE * vg->es_vlangroup,
681d3bafe1dSMichael Zhilin 		    (data0 & 0xe0) | (vg->es_member_ports & 0x1f));
682d3bafe1dSMichael Zhilin 	} else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
683d3bafe1dSMichael Zhilin 		if (vg->es_member_ports != 0) {
684d3bafe1dSMichael Zhilin 			ksz8995ma_writereg(dev, KSZ8995MA_IDR2,
685d3bafe1dSMichael Zhilin 			    KSZ8995MA_VLAN_TABLE_VALID |
686d3bafe1dSMichael Zhilin 			    (vg->es_member_ports & 0x1f));
687d3bafe1dSMichael Zhilin 			ksz8995ma_writereg(dev, KSZ8995MA_IDR1,
688d3bafe1dSMichael Zhilin 			    vg->es_fid << 4 | vg->es_vid >> 8);
689d3bafe1dSMichael Zhilin 			ksz8995ma_writereg(dev, KSZ8995MA_IDR0,
690d3bafe1dSMichael Zhilin 			    vg->es_vid & 0xff);
691d3bafe1dSMichael Zhilin 		} else {
692d3bafe1dSMichael Zhilin 			ksz8995ma_writereg(dev, KSZ8995MA_IDR2, 0);
693d3bafe1dSMichael Zhilin 			ksz8995ma_writereg(dev, KSZ8995MA_IDR1, 0);
694d3bafe1dSMichael Zhilin 			ksz8995ma_writereg(dev, KSZ8995MA_IDR0, 0);
695d3bafe1dSMichael Zhilin 		}
696d3bafe1dSMichael Zhilin 		ksz8995ma_writereg(dev, KSZ8995MA_IAC0,
697d3bafe1dSMichael Zhilin 		    KSZ8995MA_VLAN_TABLE_WRITE);
698d3bafe1dSMichael Zhilin 		ksz8995ma_writereg(dev, KSZ8995MA_IAC1, vg->es_vlangroup);
699d3bafe1dSMichael Zhilin 	}
700d3bafe1dSMichael Zhilin 
701d3bafe1dSMichael Zhilin 	return (0);
702d3bafe1dSMichael Zhilin }
703d3bafe1dSMichael Zhilin 
704d3bafe1dSMichael Zhilin static int
705d3bafe1dSMichael Zhilin ksz8995ma_getconf(device_t dev, etherswitch_conf_t *conf)
706d3bafe1dSMichael Zhilin {
707d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc *sc;
708d3bafe1dSMichael Zhilin 
709d3bafe1dSMichael Zhilin 	sc = device_get_softc(dev);
710d3bafe1dSMichael Zhilin 
711d3bafe1dSMichael Zhilin 	/* Return the VLAN mode. */
712d3bafe1dSMichael Zhilin 	conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
713d3bafe1dSMichael Zhilin 	conf->vlan_mode = sc->vlan_mode;
714d3bafe1dSMichael Zhilin 
715d3bafe1dSMichael Zhilin 	return (0);
716d3bafe1dSMichael Zhilin }
717d3bafe1dSMichael Zhilin 
718d3bafe1dSMichael Zhilin static void
719d3bafe1dSMichael Zhilin ksz8995ma_portvlanreset(device_t dev)
720d3bafe1dSMichael Zhilin {
721d3bafe1dSMichael Zhilin 	int i, data;
722d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc *sc;
723d3bafe1dSMichael Zhilin 
724d3bafe1dSMichael Zhilin 	sc = device_get_softc(dev);
725d3bafe1dSMichael Zhilin 
726d3bafe1dSMichael Zhilin 	for (i = 0; i < sc->numports; ++i) {
727d3bafe1dSMichael Zhilin 		data = ksz8995ma_readreg(dev, KSZ8995MA_PC1_BASE +
728d3bafe1dSMichael Zhilin 		    KSZ8995MA_PORT_SIZE * i);
729d3bafe1dSMichael Zhilin 		ksz8995ma_writereg(dev, KSZ8995MA_PC1_BASE +
730d3bafe1dSMichael Zhilin 		    KSZ8995MA_PORT_SIZE * i, (data & 0xe0) | 0x1f);
731d3bafe1dSMichael Zhilin 	}
732d3bafe1dSMichael Zhilin }
733d3bafe1dSMichael Zhilin 
734d3bafe1dSMichael Zhilin static int
735d3bafe1dSMichael Zhilin ksz8995ma_setconf(device_t dev, etherswitch_conf_t *conf)
736d3bafe1dSMichael Zhilin {
737d3bafe1dSMichael Zhilin 	int reg;
738d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc *sc;
739d3bafe1dSMichael Zhilin 
740d3bafe1dSMichael Zhilin 	sc = device_get_softc(dev);
741d3bafe1dSMichael Zhilin 
742d3bafe1dSMichael Zhilin 	if ((conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) == 0)
743d3bafe1dSMichael Zhilin 		return (0);
744d3bafe1dSMichael Zhilin 
745d3bafe1dSMichael Zhilin 	if (conf->vlan_mode == ETHERSWITCH_VLAN_PORT) {
746d3bafe1dSMichael Zhilin 		sc->vlan_mode = ETHERSWITCH_VLAN_PORT;
747d3bafe1dSMichael Zhilin 		reg = ksz8995ma_readreg(dev, KSZ8995MA_GC3);
748d3bafe1dSMichael Zhilin 		ksz8995ma_writereg(dev, KSZ8995MA_GC3,
749d3bafe1dSMichael Zhilin 		    reg & ~KSZ8995MA_VLAN_ENABLE);
750d3bafe1dSMichael Zhilin 		ksz8995ma_portvlanreset(dev);
751d3bafe1dSMichael Zhilin 	} else if (conf->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
752d3bafe1dSMichael Zhilin 		sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
753d3bafe1dSMichael Zhilin 		reg = ksz8995ma_readreg(dev, KSZ8995MA_GC3);
754d3bafe1dSMichael Zhilin 		ksz8995ma_writereg(dev, KSZ8995MA_GC3,
755d3bafe1dSMichael Zhilin 		    reg | KSZ8995MA_VLAN_ENABLE);
756d3bafe1dSMichael Zhilin 	} else {
757d3bafe1dSMichael Zhilin 		sc->vlan_mode = 0;
758d3bafe1dSMichael Zhilin 		reg = ksz8995ma_readreg(dev, KSZ8995MA_GC3);
759d3bafe1dSMichael Zhilin 		ksz8995ma_writereg(dev, KSZ8995MA_GC3,
760d3bafe1dSMichael Zhilin 		    reg & ~KSZ8995MA_VLAN_ENABLE);
761d3bafe1dSMichael Zhilin 		ksz8995ma_portvlanreset(dev);
762d3bafe1dSMichael Zhilin 	}
763d3bafe1dSMichael Zhilin 	return (0);
764d3bafe1dSMichael Zhilin }
765d3bafe1dSMichael Zhilin 
766d3bafe1dSMichael Zhilin static void
767d3bafe1dSMichael Zhilin ksz8995ma_statchg(device_t dev)
768d3bafe1dSMichael Zhilin {
769d3bafe1dSMichael Zhilin 
770d3bafe1dSMichael Zhilin 	DPRINTF(dev, "%s\n", __func__);
771d3bafe1dSMichael Zhilin }
772d3bafe1dSMichael Zhilin 
773d3bafe1dSMichael Zhilin static int
774*2e6a8c1aSJustin Hibbits ksz8995ma_ifmedia_upd(if_t ifp)
775d3bafe1dSMichael Zhilin {
776d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc *sc;
777d3bafe1dSMichael Zhilin 	struct mii_data *mii;
778d3bafe1dSMichael Zhilin 
779*2e6a8c1aSJustin Hibbits 	sc = if_getsoftc(ifp);
780*2e6a8c1aSJustin Hibbits 	mii = ksz8995ma_miiforport(sc, ifp->if_dunit); /* XXX - DRVAPI */
781d3bafe1dSMichael Zhilin 
782d3bafe1dSMichael Zhilin 	DPRINTF(sc->sc_dev, "%s\n", __func__);
783d3bafe1dSMichael Zhilin 	if (mii == NULL)
784d3bafe1dSMichael Zhilin 		return (ENXIO);
785d3bafe1dSMichael Zhilin 	mii_mediachg(mii);
786d3bafe1dSMichael Zhilin 	return (0);
787d3bafe1dSMichael Zhilin }
788d3bafe1dSMichael Zhilin 
789d3bafe1dSMichael Zhilin static void
790*2e6a8c1aSJustin Hibbits ksz8995ma_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
791d3bafe1dSMichael Zhilin {
792d3bafe1dSMichael Zhilin 	struct ksz8995ma_softc *sc;
793d3bafe1dSMichael Zhilin 	struct mii_data *mii;
794d3bafe1dSMichael Zhilin 
795*2e6a8c1aSJustin Hibbits 	sc = if_getsoftc(ifp);
796*2e6a8c1aSJustin Hibbits 	mii = ksz8995ma_miiforport(sc, ifp->if_dunit); /* XXX - DRVAPI */
797d3bafe1dSMichael Zhilin 
798d3bafe1dSMichael Zhilin 	DPRINTF(sc->sc_dev, "%s\n", __func__);
799d3bafe1dSMichael Zhilin 
800d3bafe1dSMichael Zhilin 	if (mii == NULL)
801d3bafe1dSMichael Zhilin 		return;
802d3bafe1dSMichael Zhilin 	mii_pollstat(mii);
803d3bafe1dSMichael Zhilin 	ifmr->ifm_active = mii->mii_media_active;
804d3bafe1dSMichael Zhilin 	ifmr->ifm_status = mii->mii_media_status;
805d3bafe1dSMichael Zhilin }
806d3bafe1dSMichael Zhilin 
807d3bafe1dSMichael Zhilin static int
808d3bafe1dSMichael Zhilin ksz8995ma_readphy(device_t dev, int phy, int reg)
809d3bafe1dSMichael Zhilin {
810d3bafe1dSMichael Zhilin int portreg;
811d3bafe1dSMichael Zhilin 
812d3bafe1dSMichael Zhilin 	/*
813d3bafe1dSMichael Zhilin 	 * This is no mdio/mdc connection code.
814d3bafe1dSMichael Zhilin          * simulate MIIM Registers via the SPI interface
815d3bafe1dSMichael Zhilin 	 */
816d3bafe1dSMichael Zhilin 	if (reg == MII_BMSR) {
817d3bafe1dSMichael Zhilin 		portreg = ksz8995ma_readreg(dev, KSZ8995MA_PS0_BASE +
818d3bafe1dSMichael Zhilin 			KSZ8995MA_PORT_SIZE * phy);
819d3bafe1dSMichael Zhilin 		return (KSZ8995MA_MII_STAT |
820d3bafe1dSMichael Zhilin 		    (portreg & 0x20 ? BMSR_LINK : 0x00) |
821d3bafe1dSMichael Zhilin 		    (portreg & 0x40 ? BMSR_ACOMP : 0x00));
822d3bafe1dSMichael Zhilin 	} else if (reg == MII_PHYIDR1) {
823d3bafe1dSMichael Zhilin 		return (KSZ8995MA_MII_PHYID_H);
824d3bafe1dSMichael Zhilin 	} else if (reg == MII_PHYIDR2) {
825d3bafe1dSMichael Zhilin 		return (KSZ8995MA_MII_PHYID_L);
826d3bafe1dSMichael Zhilin 	} else if (reg == MII_ANAR) {
827d3bafe1dSMichael Zhilin 		portreg = ksz8995ma_readreg(dev, KSZ8995MA_PC12_BASE +
828d3bafe1dSMichael Zhilin 			KSZ8995MA_PORT_SIZE * phy);
829d3bafe1dSMichael Zhilin 		return (KSZ8995MA_MII_AA | (portreg & 0x0f) << 5);
830d3bafe1dSMichael Zhilin 	} else if (reg == MII_ANLPAR) {
831d3bafe1dSMichael Zhilin 		portreg = ksz8995ma_readreg(dev, KSZ8995MA_PS0_BASE +
832d3bafe1dSMichael Zhilin 			KSZ8995MA_PORT_SIZE * phy);
833d3bafe1dSMichael Zhilin 		return (((portreg & 0x0f) << 5) | 0x01);
834d3bafe1dSMichael Zhilin 	}
835d3bafe1dSMichael Zhilin 
836d3bafe1dSMichael Zhilin 	return (0);
837d3bafe1dSMichael Zhilin }
838d3bafe1dSMichael Zhilin 
839d3bafe1dSMichael Zhilin static int
840d3bafe1dSMichael Zhilin ksz8995ma_writephy(device_t dev, int phy, int reg, int data)
841d3bafe1dSMichael Zhilin {
842d3bafe1dSMichael Zhilin int portreg;
843d3bafe1dSMichael Zhilin 
844d3bafe1dSMichael Zhilin 	/*
845d3bafe1dSMichael Zhilin 	 * This is no mdio/mdc connection code.
846d3bafe1dSMichael Zhilin          * simulate MIIM Registers via the SPI interface
847d3bafe1dSMichael Zhilin 	 */
848d3bafe1dSMichael Zhilin 	if (reg == MII_BMCR) {
849d3bafe1dSMichael Zhilin 		portreg = ksz8995ma_readreg(dev, KSZ8995MA_PC13_BASE +
850d3bafe1dSMichael Zhilin 			KSZ8995MA_PORT_SIZE * phy);
851d3bafe1dSMichael Zhilin 		if (data & BMCR_PDOWN)
852d3bafe1dSMichael Zhilin 			portreg |= KSZ8995MA_PDOWN;
853d3bafe1dSMichael Zhilin 		else
854d3bafe1dSMichael Zhilin 			portreg &= ~KSZ8995MA_PDOWN;
855d3bafe1dSMichael Zhilin 		if (data & BMCR_STARTNEG)
856d3bafe1dSMichael Zhilin 			portreg |= KSZ8995MA_STARTNEG;
857d3bafe1dSMichael Zhilin 		else
858d3bafe1dSMichael Zhilin 			portreg &= ~KSZ8995MA_STARTNEG;
859d3bafe1dSMichael Zhilin 		ksz8995ma_writereg(dev, KSZ8995MA_PC13_BASE +
860d3bafe1dSMichael Zhilin 			KSZ8995MA_PORT_SIZE * phy, portreg);
861d3bafe1dSMichael Zhilin 	} else if (reg == MII_ANAR) {
862d3bafe1dSMichael Zhilin 		portreg = ksz8995ma_readreg(dev, KSZ8995MA_PC12_BASE +
863d3bafe1dSMichael Zhilin 			KSZ8995MA_PORT_SIZE * phy);
864d3bafe1dSMichael Zhilin 		portreg &= 0xf;
865d3bafe1dSMichael Zhilin 		portreg |= ((data >> 5) & 0x0f);
866d3bafe1dSMichael Zhilin 		ksz8995ma_writereg(dev, KSZ8995MA_PC12_BASE +
867d3bafe1dSMichael Zhilin 			KSZ8995MA_PORT_SIZE * phy, portreg);
868d3bafe1dSMichael Zhilin 	}
869d3bafe1dSMichael Zhilin 	return (0);
870d3bafe1dSMichael Zhilin }
871d3bafe1dSMichael Zhilin 
872d3bafe1dSMichael Zhilin static int
873d3bafe1dSMichael Zhilin ksz8995ma_readreg(device_t dev, int addr)
874d3bafe1dSMichael Zhilin {
875d3bafe1dSMichael Zhilin 	uint8_t txBuf[8], rxBuf[8];
876d3bafe1dSMichael Zhilin 	struct spi_command cmd;
877d3bafe1dSMichael Zhilin 	int err;
878d3bafe1dSMichael Zhilin 
879d3bafe1dSMichael Zhilin 	memset(&cmd, 0, sizeof(cmd));
880d3bafe1dSMichael Zhilin 	memset(txBuf, 0, sizeof(txBuf));
881d3bafe1dSMichael Zhilin 	memset(rxBuf, 0, sizeof(rxBuf));
882d3bafe1dSMichael Zhilin 
883d3bafe1dSMichael Zhilin 	/* read spi */
884d3bafe1dSMichael Zhilin 	txBuf[0] = KSZ8995MA_SPI_READ;
885d3bafe1dSMichael Zhilin 	txBuf[1] = addr;
886d3bafe1dSMichael Zhilin 	cmd.tx_cmd = &txBuf;
887d3bafe1dSMichael Zhilin 	cmd.rx_cmd = &rxBuf;
888d3bafe1dSMichael Zhilin 	cmd.tx_cmd_sz = 3;
889d3bafe1dSMichael Zhilin 	cmd.rx_cmd_sz = 3;
890d3bafe1dSMichael Zhilin         err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
891d3bafe1dSMichael Zhilin 	if (err)
892d3bafe1dSMichael Zhilin 		return(0);
893d3bafe1dSMichael Zhilin 
894d3bafe1dSMichael Zhilin 	return (rxBuf[2]);
895d3bafe1dSMichael Zhilin }
896d3bafe1dSMichael Zhilin 
897d3bafe1dSMichael Zhilin static int
898d3bafe1dSMichael Zhilin ksz8995ma_writereg(device_t dev, int addr, int value)
899d3bafe1dSMichael Zhilin {
900d3bafe1dSMichael Zhilin 	uint8_t txBuf[8], rxBuf[8];
901d3bafe1dSMichael Zhilin 	struct spi_command cmd;
902d3bafe1dSMichael Zhilin 	int err;
903d3bafe1dSMichael Zhilin 
904d3bafe1dSMichael Zhilin 	memset(&cmd, 0, sizeof(cmd));
905d3bafe1dSMichael Zhilin 	memset(txBuf, 0, sizeof(txBuf));
906d3bafe1dSMichael Zhilin 	memset(rxBuf, 0, sizeof(rxBuf));
907d3bafe1dSMichael Zhilin 
908d3bafe1dSMichael Zhilin 	/* write spi */
909d3bafe1dSMichael Zhilin 	txBuf[0] = KSZ8995MA_SPI_WRITE;
910d3bafe1dSMichael Zhilin 	txBuf[1] = addr;
911d3bafe1dSMichael Zhilin 	txBuf[2] = value;
912d3bafe1dSMichael Zhilin 	cmd.tx_cmd = &txBuf;
913d3bafe1dSMichael Zhilin 	cmd.rx_cmd = &rxBuf;
914d3bafe1dSMichael Zhilin 	cmd.tx_cmd_sz = 3;
915d3bafe1dSMichael Zhilin 	cmd.rx_cmd_sz = 3;
916d3bafe1dSMichael Zhilin         err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd);
917d3bafe1dSMichael Zhilin 	if (err)
918d3bafe1dSMichael Zhilin 		return(0);
919d3bafe1dSMichael Zhilin 
920d3bafe1dSMichael Zhilin 	return (0);
921d3bafe1dSMichael Zhilin }
922d3bafe1dSMichael Zhilin 
923d3bafe1dSMichael Zhilin static device_method_t ksz8995ma_methods[] = {
924d3bafe1dSMichael Zhilin 	/* Device interface */
925d3bafe1dSMichael Zhilin 	DEVMETHOD(device_probe,			ksz8995ma_probe),
926d3bafe1dSMichael Zhilin 	DEVMETHOD(device_attach,		ksz8995ma_attach),
927d3bafe1dSMichael Zhilin 	DEVMETHOD(device_detach,		ksz8995ma_detach),
928d3bafe1dSMichael Zhilin 
929d3bafe1dSMichael Zhilin 	/* bus interface */
930d3bafe1dSMichael Zhilin 	DEVMETHOD(bus_add_child,		device_add_child_ordered),
931d3bafe1dSMichael Zhilin 
932d3bafe1dSMichael Zhilin 	/* MII interface */
933d3bafe1dSMichael Zhilin 	DEVMETHOD(miibus_readreg,		ksz8995ma_readphy),
934d3bafe1dSMichael Zhilin 	DEVMETHOD(miibus_writereg,		ksz8995ma_writephy),
935d3bafe1dSMichael Zhilin 	DEVMETHOD(miibus_statchg,		ksz8995ma_statchg),
936d3bafe1dSMichael Zhilin 
937d3bafe1dSMichael Zhilin 	/* etherswitch interface */
938d3bafe1dSMichael Zhilin 	DEVMETHOD(etherswitch_lock,		ksz8995ma_lock),
939d3bafe1dSMichael Zhilin 	DEVMETHOD(etherswitch_unlock,		ksz8995ma_unlock),
940d3bafe1dSMichael Zhilin 	DEVMETHOD(etherswitch_getinfo,		ksz8995ma_getinfo),
941d3bafe1dSMichael Zhilin 	DEVMETHOD(etherswitch_readreg,		ksz8995ma_readreg),
942d3bafe1dSMichael Zhilin 	DEVMETHOD(etherswitch_writereg,		ksz8995ma_writereg),
943d3bafe1dSMichael Zhilin 	DEVMETHOD(etherswitch_readphyreg,	ksz8995ma_readphy),
944d3bafe1dSMichael Zhilin 	DEVMETHOD(etherswitch_writephyreg,	ksz8995ma_writephy),
945d3bafe1dSMichael Zhilin 	DEVMETHOD(etherswitch_getport,		ksz8995ma_getport),
946d3bafe1dSMichael Zhilin 	DEVMETHOD(etherswitch_setport,		ksz8995ma_setport),
947d3bafe1dSMichael Zhilin 	DEVMETHOD(etherswitch_getvgroup,	ksz8995ma_getvgroup),
948d3bafe1dSMichael Zhilin 	DEVMETHOD(etherswitch_setvgroup,	ksz8995ma_setvgroup),
949d3bafe1dSMichael Zhilin 	DEVMETHOD(etherswitch_setconf,		ksz8995ma_setconf),
950d3bafe1dSMichael Zhilin 	DEVMETHOD(etherswitch_getconf,		ksz8995ma_getconf),
951d3bafe1dSMichael Zhilin 
952d3bafe1dSMichael Zhilin 	DEVMETHOD_END
953d3bafe1dSMichael Zhilin };
954d3bafe1dSMichael Zhilin 
955d3bafe1dSMichael Zhilin DEFINE_CLASS_0(ksz8995ma, ksz8995ma_driver, ksz8995ma_methods,
956d3bafe1dSMichael Zhilin     sizeof(struct ksz8995ma_softc));
957d3bafe1dSMichael Zhilin 
95842726c2fSJohn Baldwin DRIVER_MODULE(ksz8995ma, spibus, ksz8995ma_driver, 0, 0);
9593e38757dSJohn Baldwin DRIVER_MODULE(miibus, ksz8995ma, miibus_driver, 0, 0);
960829a13faSJohn Baldwin DRIVER_MODULE(etherswitch, ksz8995ma, etherswitch_driver, 0, 0);
961d3bafe1dSMichael Zhilin MODULE_VERSION(ksz8995ma, 1);
962d3bafe1dSMichael Zhilin MODULE_DEPEND(ksz8995ma, spibus, 1, 1, 1); /* XXX which versions? */
963d3bafe1dSMichael Zhilin MODULE_DEPEND(ksz8995ma, miibus, 1, 1, 1); /* XXX which versions? */
964d3bafe1dSMichael Zhilin MODULE_DEPEND(ksz8995ma, etherswitch, 1, 1, 1); /* XXX which versions? */
965