ip17x.c revision 250386
1/*-
2 * Copyright (c) 2013 Luiz Otavio O Souza.
3 * Copyright (c) 2011-2012 Stefan Bethke.
4 * Copyright (c) 2012 Adrian Chadd.
5 * 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 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: head/sys/dev/etherswitch/ip17x/ip17x.c 250386 2013-05-08 20:58:41Z adrian $
29 */
30
31#include <sys/param.h>
32#include <sys/bus.h>
33#include <sys/errno.h>
34#include <sys/kernel.h>
35#include <sys/module.h>
36#include <sys/socket.h>
37#include <sys/sockio.h>
38#include <sys/sysctl.h>
39#include <sys/systm.h>
40
41#include <net/if.h>
42#include <net/if_arp.h>
43#include <net/ethernet.h>
44#include <net/if_dl.h>
45#include <net/if_media.h>
46#include <net/if_types.h>
47
48#include <machine/bus.h>
49#include <dev/mii/mii.h>
50#include <dev/mii/miivar.h>
51#include <dev/etherswitch/mdio.h>
52
53#include <dev/etherswitch/etherswitch.h>
54#include <dev/etherswitch/ip17x/ip17x_phy.h>
55#include <dev/etherswitch/ip17x/ip17x_reg.h>
56#include <dev/etherswitch/ip17x/ip17x_var.h>
57#include <dev/etherswitch/ip17x/ip17x_vlans.h>
58#include <dev/etherswitch/ip17x/ip175c.h>
59#include <dev/etherswitch/ip17x/ip175d.h>
60
61#include "mdio_if.h"
62#include "miibus_if.h"
63#include "etherswitch_if.h"
64
65MALLOC_DECLARE(M_IP17X);
66MALLOC_DEFINE(M_IP17X, "ip17x", "ip17x data structures");
67
68static void ip17x_tick(void *);
69static int ip17x_ifmedia_upd(struct ifnet *);
70static void ip17x_ifmedia_sts(struct ifnet *, struct ifmediareq *);
71
72static int
73ip17x_probe(device_t dev)
74{
75	struct ip17x_softc *sc;
76	uint32_t oui, model, phy_id1, phy_id2;
77
78	sc = device_get_softc(dev);
79
80	/* Read ID from PHY 0. */
81	phy_id1 = MDIO_READREG(device_get_parent(dev), 0, MII_PHYIDR1);
82	phy_id2 = MDIO_READREG(device_get_parent(dev), 0, MII_PHYIDR2);
83
84	oui = MII_OUI(phy_id1, phy_id2),
85	model = MII_MODEL(phy_id2);
86	/* We only care about IC+ devices. */
87	if (oui != IP17X_OUI) {
88		device_printf(dev,
89		    "Unsupported IC+ switch. Unknown OUI: %#x\n", oui);
90		return (ENXIO);
91	}
92
93	switch (model) {
94	case IP17X_IP175A:
95		sc->sc_switchtype = IP17X_SWITCH_IP175A;
96		break;
97	case IP17X_IP175C:
98		sc->sc_switchtype = IP17X_SWITCH_IP175C;
99		break;
100	default:
101		device_printf(dev, "Unsupported IC+ switch model: %#x\n",
102		    model);
103		return (ENXIO);
104	}
105
106	/* IP175D has a specific ID register. */
107	model = MDIO_READREG(device_get_parent(dev), IP175D_ID_PHY,
108	    IP175D_ID_REG);
109	if (model == 0x175d)
110		sc->sc_switchtype = IP17X_SWITCH_IP175D;
111	else {
112		/* IP178 has more PHYs.  Try it. */
113		model = MDIO_READREG(device_get_parent(dev), 5, MII_PHYIDR1);
114		if (phy_id1 == model)
115			sc->sc_switchtype = IP17X_SWITCH_IP178C;
116	}
117
118	device_set_desc_copy(dev, "IC+ IP17x switch driver");
119	return (BUS_PROBE_DEFAULT);
120}
121
122static int
123ip17x_attach_phys(struct ip17x_softc *sc)
124{
125	int err, phy, port;
126	char name[IFNAMSIZ];
127
128	port = err = 0;
129
130	/* PHYs need an interface, so we generate a dummy one */
131	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
132	for (phy = 0; phy < MII_NPHY; phy++) {
133		if (((1 << phy) & sc->phymask) == 0)
134			continue;
135		sc->phyport[phy] = port;
136		sc->portphy[port] = phy;
137		sc->ifp[port] = if_alloc(IFT_ETHER);
138		sc->ifp[port]->if_softc = sc;
139		sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST |
140		    IFF_DRV_RUNNING | IFF_SIMPLEX;
141		sc->ifname[port] = malloc(strlen(name)+1, M_IP17X, M_WAITOK);
142		bcopy(name, sc->ifname[port], strlen(name)+1);
143		if_initname(sc->ifp[port], sc->ifname[port], port);
144		sc->miibus[port] = malloc(sizeof(device_t), M_IP17X,
145		    M_WAITOK | M_ZERO);
146		err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port],
147		    ip17x_ifmedia_upd, ip17x_ifmedia_sts, \
148		    BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
149		DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
150		    device_get_nameunit(*sc->miibus[port]),
151		    sc->ifp[port]->if_xname);
152		if (err != 0) {
153			device_printf(sc->sc_dev,
154			    "attaching PHY %d failed\n",
155			    phy);
156			break;
157		}
158		sc->info.es_nports = port + 1;
159		if (++port >= sc->numports)
160			break;
161	}
162	return (err);
163}
164
165static int
166ip17x_attach(device_t dev)
167{
168	struct ip17x_softc *sc;
169	int err;
170
171	sc = device_get_softc(dev);
172
173	sc->sc_dev = dev;
174	mtx_init(&sc->sc_mtx, "ip17x", NULL, MTX_DEF);
175	strlcpy(sc->info.es_name, device_get_desc(dev),
176	    sizeof(sc->info.es_name));
177
178	/* XXX Defaults */
179	sc->phymask = 0x0f;
180	sc->media = 100;
181
182	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
183	    "phymask", &sc->phymask);
184
185	/* Number of vlans supported by the switch. */
186	sc->info.es_nvlangroups = IP17X_MAX_VLANS;
187
188	/* Attach the switch related functions. */
189	if (IP17X_IS_SWITCH(sc, IP175C))
190		ip175c_attach(sc);
191	else if (IP17X_IS_SWITCH(sc, IP175D))
192		ip175d_attach(sc);
193	else
194		/* We don't have support to all the models yet :-/ */
195		return (ENXIO);
196
197	/* Always attach the cpu port. */
198	sc->phymask |= (1 << sc->cpuport);
199
200	sc->ifp = malloc(sizeof(struct ifnet *) * sc->numports, M_IP17X,
201	    M_WAITOK | M_ZERO);
202	sc->pvid = malloc(sizeof(uint32_t) * sc->numports, M_IP17X,
203	    M_WAITOK | M_ZERO);
204	sc->ifname = malloc(sizeof(char *) * sc->numports, M_IP17X,
205	    M_WAITOK | M_ZERO);
206	sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_IP17X,
207	    M_WAITOK | M_ZERO);
208	sc->portphy = malloc(sizeof(int) * sc->numports, M_IP17X,
209	    M_WAITOK | M_ZERO);
210
211	/* Initialize the switch. */
212	sc->hal.ip17x_reset(sc);
213
214	/*
215	 * Attach the PHYs and complete the bus enumeration.
216	 */
217	err = ip17x_attach_phys(sc);
218	if (err != 0)
219		return (err);
220
221	/*
222	 * Set the switch to port based vlans or disabled (if not supported
223	 * on this model).
224	 */
225	sc->hal.ip17x_set_vlan_mode(sc, ETHERSWITCH_VLAN_PORT);
226
227	bus_generic_probe(dev);
228	bus_enumerate_hinted_children(dev);
229	err = bus_generic_attach(dev);
230	if (err != 0)
231		return (err);
232
233	callout_init(&sc->callout_tick, 0);
234
235	ip17x_tick(sc);
236
237	return (0);
238}
239
240static int
241ip17x_detach(device_t dev)
242{
243	struct ip17x_softc *sc;
244	int i, port;
245
246	sc = device_get_softc(dev);
247	callout_drain(&sc->callout_tick);
248
249	for (i=0; i < MII_NPHY; i++) {
250		if (((1 << i) & sc->phymask) == 0)
251			continue;
252		port = sc->phyport[i];
253		if (sc->miibus[port] != NULL)
254			device_delete_child(dev, (*sc->miibus[port]));
255		if (sc->ifp[port] != NULL)
256			if_free(sc->ifp[port]);
257		free(sc->ifname[port], M_IP17X);
258		free(sc->miibus[port], M_IP17X);
259	}
260
261	free(sc->portphy, M_IP17X);
262	free(sc->miibus, M_IP17X);
263	free(sc->ifname, M_IP17X);
264	free(sc->pvid, M_IP17X);
265	free(sc->ifp, M_IP17X);
266
267	/* Reset the switch. */
268	sc->hal.ip17x_reset(sc);
269
270	bus_generic_detach(dev);
271	mtx_destroy(&sc->sc_mtx);
272
273	return (0);
274}
275
276static inline struct mii_data *
277ip17x_miiforport(struct ip17x_softc *sc, int port)
278{
279
280	if (port < 0 || port > sc->numports)
281		return (NULL);
282	return (device_get_softc(*sc->miibus[port]));
283}
284
285static inline struct ifnet *
286ip17x_ifpforport(struct ip17x_softc *sc, int port)
287{
288
289	if (port < 0 || port > sc->numports)
290		return (NULL);
291	return (sc->ifp[port]);
292}
293
294/*
295 * Poll the status for all PHYs.
296 */
297static void
298ip17x_miipollstat(struct ip17x_softc *sc)
299{
300	struct mii_softc *miisc;
301	struct mii_data *mii;
302	int i, port;
303
304	IP17X_LOCK_ASSERT(sc, MA_NOTOWNED);
305
306	for (i = 0; i < MII_NPHY; i++) {
307		if (((1 << i) & sc->phymask) == 0)
308			continue;
309		port = sc->phyport[i];
310		if ((*sc->miibus[port]) == NULL)
311			continue;
312		mii = device_get_softc(*sc->miibus[port]);
313		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
314			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
315			    miisc->mii_inst)
316				continue;
317			ukphy_status(miisc);
318			mii_phy_update(miisc, MII_POLLSTAT);
319		}
320	}
321}
322
323static void
324ip17x_tick(void *arg)
325{
326	struct ip17x_softc *sc;
327
328	sc = arg;
329	ip17x_miipollstat(sc);
330	callout_reset(&sc->callout_tick, hz, ip17x_tick, sc);
331}
332
333static void
334ip17x_lock(device_t dev)
335{
336	struct ip17x_softc *sc;
337
338	sc = device_get_softc(dev);
339	IP17X_LOCK_ASSERT(sc, MA_NOTOWNED);
340	IP17X_LOCK(sc);
341}
342
343static void
344ip17x_unlock(device_t dev)
345{
346	struct ip17x_softc *sc;
347
348	sc = device_get_softc(dev);
349	IP17X_LOCK_ASSERT(sc, MA_OWNED);
350	IP17X_UNLOCK(sc);
351}
352
353static etherswitch_info_t *
354ip17x_getinfo(device_t dev)
355{
356	struct ip17x_softc *sc;
357
358	sc = device_get_softc(dev);
359	return (&sc->info);
360}
361
362static int
363ip17x_getport(device_t dev, etherswitch_port_t *p)
364{
365	struct ip17x_softc *sc;
366	struct ifmediareq *ifmr;
367	struct mii_data *mii;
368	int err, phy;
369
370	sc = device_get_softc(dev);
371	if (p->es_port < 0 || p->es_port >= sc->numports)
372		return (ENXIO);
373
374	phy = sc->portphy[p->es_port];
375
376	/* Retrieve the PVID. */
377	p->es_pvid = sc->pvid[phy];
378
379	/* Port flags. */
380	if (sc->addtag & (1 << phy))
381		p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
382	if (sc->striptag & (1 << phy))
383		p->es_flags |= ETHERSWITCH_PORT_STRIPTAG;
384
385	ifmr = &p->es_ifmr;
386
387	/* No media settings ? */
388	if (p->es_ifmr.ifm_count == 0)
389		return (0);
390
391	mii = ip17x_miiforport(sc, p->es_port);
392	if (mii == NULL)
393		return (ENXIO);
394	if (phy == sc->cpuport) {
395		/* fill in fixed values for CPU port */
396		p->es_flags |= ETHERSWITCH_PORT_CPU;
397		ifmr->ifm_count = 0;
398		if (sc->media == 100)
399			ifmr->ifm_current = ifmr->ifm_active =
400			    IFM_ETHER | IFM_100_TX | IFM_FDX;
401		else
402			ifmr->ifm_current = ifmr->ifm_active =
403			    IFM_ETHER | IFM_1000_T | IFM_FDX;
404		ifmr->ifm_mask = 0;
405		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
406	} else {
407		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
408		    &mii->mii_media, SIOCGIFMEDIA);
409		if (err)
410			return (err);
411	}
412	return (0);
413}
414
415static int
416ip17x_setport(device_t dev, etherswitch_port_t *p)
417{
418	struct ip17x_softc *sc;
419	struct ifmedia *ifm;
420	struct ifnet *ifp;
421	struct mii_data *mii;
422	int phy;
423
424 	sc = device_get_softc(dev);
425	if (p->es_port < 0 || p->es_port >= sc->numports)
426		return (ENXIO);
427
428	phy = sc->portphy[p->es_port];
429	ifp = ip17x_ifpforport(sc, p->es_port);
430	mii = ip17x_miiforport(sc, p->es_port);
431	if (ifp == NULL || mii == NULL)
432		return (ENXIO);
433
434	/* Port flags. */
435	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
436
437		/* Set the PVID. */
438		if (p->es_pvid != 0) {
439			if (IP17X_IS_SWITCH(sc, IP175C) &&
440			    p->es_pvid > IP175C_LAST_VLAN)
441				return (ENXIO);
442			sc->pvid[phy] = p->es_pvid;
443		}
444
445		/* Mutually exclusive. */
446		if (p->es_flags & ETHERSWITCH_PORT_ADDTAG &&
447		    p->es_flags & ETHERSWITCH_PORT_STRIPTAG)
448			return (EINVAL);
449
450		/* Reset the settings for this port. */
451		sc->addtag &= ~(1 << phy);
452		sc->striptag &= ~(1 << phy);
453
454		/* And then set it to the new value. */
455		if (p->es_flags & ETHERSWITCH_PORT_ADDTAG)
456			sc->addtag |= (1 << phy);
457		if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG)
458			sc->striptag |= (1 << phy);
459	}
460
461	/* Update the switch configuration. */
462	if (sc->hal.ip17x_hw_setup(sc))
463		return (ENXIO);
464
465	/* Do not allow media changes on CPU port. */
466	if (phy == sc->cpuport)
467		return (0);
468
469	/* No media settings ? */
470	if (p->es_ifmr.ifm_count == 0)
471		return (0);
472
473	ifm = &mii->mii_media;
474	return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA));
475}
476
477static void
478ip17x_statchg(device_t dev)
479{
480
481	DPRINTF(dev, "%s\n", __func__);
482}
483
484static int
485ip17x_ifmedia_upd(struct ifnet *ifp)
486{
487	struct ip17x_softc *sc;
488	struct mii_data *mii;
489
490	DPRINTF(sc->sc_dev, "%s\n", __func__);
491 	sc = ifp->if_softc;
492 	mii = ip17x_miiforport(sc, ifp->if_dunit);
493	if (mii == NULL)
494		return (ENXIO);
495	mii_mediachg(mii);
496	return (0);
497}
498
499static void
500ip17x_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
501{
502	struct ip17x_softc *sc;
503	struct mii_data *mii;
504
505	DPRINTF(sc->sc_dev, "%s\n", __func__);
506
507 	sc = ifp->if_softc;
508	mii = ip17x_miiforport(sc, ifp->if_dunit);
509	if (mii == NULL)
510		return;
511	mii_pollstat(mii);
512	ifmr->ifm_active = mii->mii_media_active;
513	ifmr->ifm_status = mii->mii_media_status;
514}
515
516static int
517ip17x_readreg(device_t dev, int addr)
518{
519	struct ip17x_softc *sc;
520
521	sc = device_get_softc(dev);
522	IP17X_LOCK_ASSERT(sc, MA_OWNED);
523
524	/* Not supported. */
525	return (0);
526}
527
528static int
529ip17x_writereg(device_t dev, int addr, int value)
530{
531	struct ip17x_softc *sc;
532
533	sc = device_get_softc(dev);
534	IP17X_LOCK_ASSERT(sc, MA_OWNED);
535
536	/* Not supported. */
537	return (0);
538}
539
540static int
541ip17x_getconf(device_t dev, etherswitch_conf_t *conf)
542{
543	struct ip17x_softc *sc;
544
545	sc = device_get_softc(dev);
546
547	/* Return the VLAN mode. */
548	conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
549	conf->vlan_mode = sc->hal.ip17x_get_vlan_mode(sc);
550
551	return (0);
552}
553
554static int
555ip17x_setconf(device_t dev, etherswitch_conf_t *conf)
556{
557	struct ip17x_softc *sc;
558
559	sc = device_get_softc(dev);
560
561	/* Set the VLAN mode. */
562	if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE)
563		sc->hal.ip17x_set_vlan_mode(sc, conf->vlan_mode);
564
565	return (0);
566}
567
568static device_method_t ip17x_methods[] = {
569	/* Device interface */
570	DEVMETHOD(device_probe,		ip17x_probe),
571	DEVMETHOD(device_attach,	ip17x_attach),
572	DEVMETHOD(device_detach,	ip17x_detach),
573
574	/* bus interface */
575	DEVMETHOD(bus_add_child,	device_add_child_ordered),
576
577	/* MII interface */
578	DEVMETHOD(miibus_readreg,	ip17x_readphy),
579	DEVMETHOD(miibus_writereg,	ip17x_writephy),
580	DEVMETHOD(miibus_statchg,	ip17x_statchg),
581
582	/* MDIO interface */
583	DEVMETHOD(mdio_readreg,		ip17x_readphy),
584	DEVMETHOD(mdio_writereg,	ip17x_writephy),
585
586	/* etherswitch interface */
587	DEVMETHOD(etherswitch_lock,	ip17x_lock),
588	DEVMETHOD(etherswitch_unlock,	ip17x_unlock),
589	DEVMETHOD(etherswitch_getinfo,	ip17x_getinfo),
590	DEVMETHOD(etherswitch_readreg,	ip17x_readreg),
591	DEVMETHOD(etherswitch_writereg,	ip17x_writereg),
592	DEVMETHOD(etherswitch_readphyreg,	ip17x_readphy),
593	DEVMETHOD(etherswitch_writephyreg,	ip17x_writephy),
594	DEVMETHOD(etherswitch_getport,	ip17x_getport),
595	DEVMETHOD(etherswitch_setport,	ip17x_setport),
596	DEVMETHOD(etherswitch_getvgroup,	ip17x_getvgroup),
597	DEVMETHOD(etherswitch_setvgroup,	ip17x_setvgroup),
598	DEVMETHOD(etherswitch_getconf,	ip17x_getconf),
599	DEVMETHOD(etherswitch_setconf,	ip17x_setconf),
600
601	DEVMETHOD_END
602};
603
604DEFINE_CLASS_0(ip17x, ip17x_driver, ip17x_methods,
605    sizeof(struct ip17x_softc));
606static devclass_t ip17x_devclass;
607
608DRIVER_MODULE(ip17x, mdio, ip17x_driver, ip17x_devclass, 0, 0);
609DRIVER_MODULE(miibus, ip17x, miibus_driver, miibus_devclass, 0, 0);
610DRIVER_MODULE(mdio, ip17x, mdio_driver, mdio_devclass, 0, 0);
611DRIVER_MODULE(etherswitch, ip17x, etherswitch_driver, etherswitch_devclass, 0, 0);
612MODULE_VERSION(ip17x, 1);
613MODULE_DEPEND(ip17x, miibus, 1, 1, 1); /* XXX which versions? */
614MODULE_DEPEND(ip17x, etherswitch, 1, 1, 1); /* XXX which versions? */
615