utopia.c revision 147256
1/*-
2 * Copyright (c) 2003
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * 	All rights reserved.
5 *
6 * Author: Hartmut Brandt <harti@freebsd.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/dev/utopia/utopia.c 147256 2005-06-10 16:49:24Z brooks $");
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/unistd.h>
36#include <sys/kernel.h>
37#include <sys/kthread.h>
38#include <sys/proc.h>
39#include <sys/bus.h>
40#include <sys/malloc.h>
41#include <sys/module.h>
42#include <sys/sysctl.h>
43#include <sys/lock.h>
44#include <sys/mutex.h>
45#include <sys/socket.h>
46
47#include <net/if.h>
48#include <net/if_var.h>
49#include <net/if_media.h>
50#include <net/if_atm.h>
51
52#include <dev/utopia/suni.h>
53#include <dev/utopia/idtphy.h>
54#include <dev/utopia/utopia.h>
55#include <dev/utopia/utopia_priv.h>
56
57/* known chips */
58extern const struct utopia_chip utopia_chip_idt77155;
59extern const struct utopia_chip utopia_chip_idt77105;
60extern const struct utopia_chip utopia_chip_lite;
61extern const struct utopia_chip utopia_chip_ultra;
62extern const struct utopia_chip utopia_chip_622;
63
64/*
65 * Global list of all registered interfaces
66 */
67static struct mtx utopia_list_mtx;
68static LIST_HEAD(, utopia) utopia_list = LIST_HEAD_INITIALIZER(utopia_list);
69
70#define UTP_RLOCK_LIST()	mtx_lock(&utopia_list_mtx)
71#define UTP_RUNLOCK_LIST()	mtx_unlock(&utopia_list_mtx)
72#define UTP_WLOCK_LIST()	mtx_lock(&utopia_list_mtx)
73#define UTP_WUNLOCK_LIST()	mtx_unlock(&utopia_list_mtx)
74
75#define UTP_LOCK(UTP)		mtx_lock((UTP)->lock)
76#define UTP_UNLOCK(UTP)		mtx_unlock((UTP)->lock)
77#define UTP_LOCK_ASSERT(UTP)	mtx_assert((UTP)->lock, MA_OWNED)
78
79static struct proc *utopia_kproc;
80
81static void utopia_dump(struct utopia *) __unused;
82
83/*
84 * Read a multi-register value.
85 */
86uint32_t
87utopia_update(struct utopia *utp, u_int reg, u_int nreg, uint32_t mask)
88{
89	int err;
90	u_int n;
91	uint8_t regs[4];
92	uint32_t val;
93
94	n = nreg;
95	if ((err = UTP_READREGS(utp, reg, regs, &n)) != 0) {
96#ifdef DIAGNOSTIC
97		printf("%s: register read error %s(%u,%u): %d\n", __func__,
98		    utp->chip->name, reg, nreg, err);
99#endif
100		return (0);
101	}
102	if (n < nreg) {
103#ifdef DIAGNOSTIC
104		printf("%s: got only %u regs %s(%u,%u): %d\n", __func__, n,
105		    utp->chip->name, reg, nreg, err);
106#endif
107		return (0);
108	}
109	val = 0;
110	for (n = nreg; n > 0; n--) {
111		val <<= 8;
112		val |= regs[n - 1];
113	}
114	return (val & mask);
115}
116
117/*
118 * Debugging - dump all registers.
119 */
120static void
121utopia_dump(struct utopia *utp)
122{
123	uint8_t regs[256];
124	u_int n = 256, i;
125	int err;
126
127	if ((err = UTP_READREGS(utp, 0, regs, &n)) != 0) {
128		printf("UTOPIA reg read error %d\n", err);
129		return;
130	}
131	for (i = 0; i < n; i++) {
132		if (i % 16 == 0)
133			printf("%02x:", i);
134		if (i % 16 == 8)
135			printf(" ");
136		printf(" %02x", regs[i]);
137		if (i % 16 == 15)
138			printf("\n");
139	}
140	if (i % 16 != 0)
141		printf("\n");
142}
143
144/*
145 * Update the carrier status
146 */
147void
148utopia_check_carrier(struct utopia *utp, u_int carr_ok)
149{
150	int old;
151
152	old = utp->carrier;
153	if (carr_ok) {
154		/* carrier */
155		utp->carrier = UTP_CARR_OK;
156		if (old != UTP_CARR_OK) {
157			if_printf(utp->ifatm->ifp, "carrier detected\n");
158			ATMEV_SEND_IFSTATE_CHANGED(utp->ifatm, 1);
159		}
160	} else {
161		/* no carrier */
162		utp->carrier = UTP_CARR_LOST;
163		if (old == UTP_CARR_OK) {
164			if_printf(utp->ifatm->ifp, "carrier lost\n");
165			ATMEV_SEND_IFSTATE_CHANGED(utp->ifatm, 0);
166		}
167	}
168}
169
170static int
171unknown_inval(struct utopia *utp, int what __unused)
172{
173
174	return (EINVAL);
175}
176
177static int
178unknown_reset(struct utopia *utp __unused)
179{
180	return (EIO);
181}
182
183static int
184unknown_update_carrier(struct utopia *utp)
185{
186	utp->carrier = UTP_CARR_UNKNOWN;
187	return (0);
188}
189
190static int
191unknown_set_loopback(struct utopia *utp __unused, u_int mode __unused)
192{
193	return (EINVAL);
194}
195
196static void
197unknown_intr(struct utopia *utp __unused)
198{
199}
200
201static void
202unknown_update_stats(struct utopia *utp __unused)
203{
204}
205
206static const struct utopia_chip utopia_chip_unknown = {
207	UTP_TYPE_UNKNOWN,
208	"unknown",
209	0,
210	unknown_reset,
211	unknown_inval,
212	unknown_inval,
213	unknown_inval,
214	unknown_update_carrier,
215	unknown_set_loopback,
216	unknown_intr,
217	unknown_update_stats,
218};
219
220/*
221 * Callbacks for the ifmedia infrastructure.
222 */
223static int
224utopia_media_change(struct ifnet *ifp)
225{
226	struct ifatm *ifatm = (struct ifatm *)ifp->if_softc;
227	struct utopia *utp = ifatm->phy;
228	int error = 0;
229
230	UTP_LOCK(utp);
231	if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) {
232		if (utp->media->ifm_media & IFM_ATM_SDH) {
233			if (!(utp->state & UTP_ST_SDH))
234				error = utopia_set_sdh(utp, 1);
235		} else {
236			if (utp->state & UTP_ST_SDH)
237				error = utopia_set_sdh(utp, 0);
238		}
239		if (utp->media->ifm_media & IFM_ATM_UNASSIGNED) {
240			if (!(utp->state & UTP_ST_UNASS))
241				error = utopia_set_unass(utp, 1);
242		} else {
243			if (utp->state & UTP_ST_UNASS)
244				error = utopia_set_unass(utp, 0);
245		}
246		if (utp->media->ifm_media & IFM_ATM_NOSCRAMB) {
247			if (!(utp->state & UTP_ST_NOSCRAMB))
248				error = utopia_set_noscramb(utp, 1);
249		} else {
250			if (utp->state & UTP_ST_NOSCRAMB)
251				error = utopia_set_noscramb(utp, 0);
252		}
253	} else
254		error = EIO;
255	UTP_UNLOCK(utp);
256	return (error);
257}
258
259/*
260 * Look at the carrier status.
261 */
262static void
263utopia_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
264{
265	struct utopia *utp = ((struct ifatm *)ifp->if_softc)->phy;
266
267	UTP_LOCK(utp);
268	if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) {
269		ifmr->ifm_active = IFM_ATM | utp->ifatm->mib.media;
270
271		switch (utp->carrier) {
272
273		  case UTP_CARR_OK:
274			ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
275			break;
276
277		  case UTP_CARR_LOST:
278			ifmr->ifm_status = IFM_AVALID;
279			break;
280
281		  default:
282			ifmr->ifm_status = 0;
283			break;
284		}
285		if (utp->state & UTP_ST_SDH) {
286			ifmr->ifm_active |= IFM_ATM_SDH;
287			ifmr->ifm_current |= IFM_ATM_SDH;
288		}
289		if (utp->state & UTP_ST_UNASS) {
290			ifmr->ifm_active |= IFM_ATM_UNASSIGNED;
291			ifmr->ifm_current |= IFM_ATM_UNASSIGNED;
292		}
293		if (utp->state & UTP_ST_NOSCRAMB) {
294			ifmr->ifm_active |= IFM_ATM_NOSCRAMB;
295			ifmr->ifm_current |= IFM_ATM_NOSCRAMB;
296		}
297	} else {
298		ifmr->ifm_active = 0;
299		ifmr->ifm_status = 0;
300	}
301	UTP_UNLOCK(utp);
302}
303
304/*
305 * Initialize media from the mib
306 */
307void
308utopia_init_media(struct utopia *utp)
309{
310
311	ifmedia_removeall(utp->media);
312	ifmedia_add(utp->media, IFM_ATM | utp->ifatm->mib.media, 0, NULL);
313	ifmedia_set(utp->media, IFM_ATM | utp->ifatm->mib.media);
314}
315
316/*
317 * Reset all media
318 */
319void
320utopia_reset_media(struct utopia *utp)
321{
322
323	ifmedia_removeall(utp->media);
324}
325
326/*
327 * This is called by the driver as soon as the SUNI registers are accessible.
328 * This may be either in the attach routine or the init routine of the driver.
329 */
330int
331utopia_start(struct utopia *utp)
332{
333	uint8_t reg;
334	int err;
335	u_int n = 1;
336
337	/*
338	 * Try to find out what chip we have
339	 */
340	if ((err = UTP_READREGS(utp, SUNI_REGO_MRESET, &reg, &n)) != 0)
341		return (err);
342
343	switch (reg & SUNI_REGM_MRESET_TYPE) {
344
345	  case SUNI_REGM_MRESET_TYPE_622:
346		utp->chip = &utopia_chip_622;
347		break;
348
349	  case SUNI_REGM_MRESET_TYPE_LITE:
350		/* this may be either a SUNI LITE or a IDT77155 *
351		 * Read register 0x70. The SUNI doesn't have it */
352		n = 1;
353		if ((err = UTP_READREGS(utp, IDTPHY_REGO_RBER, &reg, &n)) != 0)
354			return (err);
355		if ((reg & ~IDTPHY_REGM_RBER_RESV) ==
356		    (IDTPHY_REGM_RBER_FAIL | IDTPHY_REGM_RBER_WARN))
357			utp->chip = &utopia_chip_idt77155;
358		else
359			utp->chip = &utopia_chip_lite;
360		break;
361
362	  case SUNI_REGM_MRESET_TYPE_ULTRA:
363		utp->chip = &utopia_chip_ultra;
364		break;
365
366	  default:
367		if (reg == (IDTPHY_REGM_MCR_DRIC | IDTPHY_REGM_MCR_EI))
368			utp->chip = &utopia_chip_idt77105;
369		else {
370			if_printf(utp->ifatm->ifp,
371			    "unknown ATM-PHY chip %#x\n", reg);
372			utp->chip = &utopia_chip_unknown;
373		}
374		break;
375	}
376	utp->state |= UTP_ST_ACTIVE;
377	return (0);
378}
379
380/*
381 * Stop the chip
382 */
383void
384utopia_stop(struct utopia *utp)
385{
386	utp->state &= ~UTP_ST_ACTIVE;
387}
388
389/*
390 * Handle the sysctls
391 */
392static int
393utopia_sysctl_regs(SYSCTL_HANDLER_ARGS)
394{
395	struct utopia *utp = (struct utopia *)arg1;
396	int error;
397	u_int n;
398	uint8_t *val;
399	uint8_t new[3];
400
401	if ((n = utp->chip->nregs) == 0)
402		return (EIO);
403	val = malloc(sizeof(uint8_t) * n, M_TEMP, M_WAITOK);
404
405	UTP_LOCK(utp);
406	error = UTP_READREGS(utp, 0, val, &n);
407	UTP_UNLOCK(utp);
408
409	if (error) {
410		free(val, M_TEMP);
411		return (error);
412	}
413
414	error = SYSCTL_OUT(req, val, sizeof(uint8_t) * n);
415	free(val, M_TEMP);
416	if (error != 0 || req->newptr == NULL)
417		return (error);
418
419	error = SYSCTL_IN(req, new, sizeof(new));
420	if (error)
421		return (error);
422
423	UTP_LOCK(utp);
424	error = UTP_WRITEREG(utp, new[0], new[1], new[2]);
425	UTP_UNLOCK(utp);
426
427	return (error);
428}
429
430static int
431utopia_sysctl_stats(SYSCTL_HANDLER_ARGS)
432{
433	struct utopia *utp = (struct utopia *)arg1;
434	void *val;
435	int error;
436
437	val = malloc(sizeof(utp->stats), M_TEMP, M_WAITOK);
438
439	UTP_LOCK(utp);
440	bcopy(&utp->stats, val, sizeof(utp->stats));
441	if (req->newptr != NULL)
442		bzero((char *)&utp->stats + sizeof(utp->stats.version),
443		    sizeof(utp->stats) - sizeof(utp->stats.version));
444	UTP_UNLOCK(utp);
445
446	error = SYSCTL_OUT(req, val, sizeof(utp->stats));
447	free(val, M_TEMP);
448
449	if (error && req->newptr != NULL)
450		bcopy(val, &utp->stats, sizeof(utp->stats));
451
452	/* ignore actual new value */
453
454	return (error);
455}
456
457/*
458 * Handle the loopback sysctl
459 */
460static int
461utopia_sysctl_loopback(SYSCTL_HANDLER_ARGS)
462{
463	struct utopia *utp = (struct utopia *)arg1;
464	int error;
465	u_int loopback;
466
467	error = SYSCTL_OUT(req, &utp->loopback, sizeof(u_int));
468	if (error != 0 || req->newptr == NULL)
469		return (error);
470
471	error = SYSCTL_IN(req, &loopback, sizeof(u_int));
472	if (error)
473		return (error);
474
475	UTP_LOCK(utp);
476	error = utopia_set_loopback(utp, loopback);
477	UTP_UNLOCK(utp);
478
479	return (error);
480}
481
482/*
483 * Handle the type sysctl
484 */
485static int
486utopia_sysctl_type(SYSCTL_HANDLER_ARGS)
487{
488	struct utopia *utp = (struct utopia *)arg1;
489
490	return (SYSCTL_OUT(req, &utp->chip->type, sizeof(utp->chip->type)));
491}
492
493/*
494 * Handle the name sysctl
495 */
496static int
497utopia_sysctl_name(SYSCTL_HANDLER_ARGS)
498{
499	struct utopia *utp = (struct utopia *)arg1;
500
501	return (SYSCTL_OUT(req, utp->chip->name, strlen(utp->chip->name) + 1));
502}
503
504/*
505 * Initialize the state. This is called from the drivers attach
506 * function. The mutex must be already initialized.
507 */
508int
509utopia_attach(struct utopia *utp, struct ifatm *ifatm, struct ifmedia *media,
510    struct mtx *lock, struct sysctl_ctx_list *ctx,
511    struct sysctl_oid_list *children, const struct utopia_methods *m)
512{
513
514	bzero(utp, sizeof(*utp));
515	utp->ifatm = ifatm;
516	utp->methods = m;
517	utp->media = media;
518	utp->lock = lock;
519	utp->chip = &utopia_chip_unknown;
520	utp->stats.version = 1;
521
522	ifmedia_init(media,
523	    IFM_ATM_SDH | IFM_ATM_UNASSIGNED | IFM_ATM_NOSCRAMB,
524	    utopia_media_change, utopia_media_status);
525
526	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_regs",
527	    CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_regs, "S",
528	    "phy registers") == NULL)
529		return (-1);
530
531	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_loopback",
532	    CTLFLAG_RW | CTLTYPE_UINT, utp, 0, utopia_sysctl_loopback, "IU",
533	    "phy loopback mode") == NULL)
534		return (-1);
535
536	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_type",
537	    CTLFLAG_RD | CTLTYPE_UINT, utp, 0, utopia_sysctl_type, "IU",
538	    "phy type") == NULL)
539		return (-1);
540
541	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_name",
542	    CTLFLAG_RD | CTLTYPE_STRING, utp, 0, utopia_sysctl_name, "A",
543	    "phy name") == NULL)
544		return (-1);
545
546	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_stats",
547	    CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_stats, "S",
548	    "phy statistics") == NULL)
549		return (-1);
550
551	if (SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "phy_state",
552	    CTLFLAG_RD, &utp->state, 0, "phy state") == NULL)
553		return (-1);
554
555	if (SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "phy_carrier",
556	    CTLFLAG_RD, &utp->carrier, 0, "phy carrier") == NULL)
557		return (-1);
558
559	UTP_WLOCK_LIST();
560	LIST_INSERT_HEAD(&utopia_list, utp, link);
561	UTP_WUNLOCK_LIST();
562
563	utp->state |= UTP_ST_ATTACHED;
564	return (0);
565}
566
567/*
568 * Detach. We set a flag here, wakeup the daemon and let him do it.
569 * Here we need the lock for synchronisation with the daemon.
570 */
571void
572utopia_detach(struct utopia *utp)
573{
574
575	UTP_LOCK_ASSERT(utp);
576	if (utp->state & UTP_ST_ATTACHED) {
577		utp->state |= UTP_ST_DETACH;
578		while (utp->state & UTP_ST_DETACH) {
579			wakeup(&utopia_list);
580			msleep(utp, utp->lock, PZERO, "utopia_detach", hz);
581		}
582	}
583}
584
585/*
586 * The carrier state kernel proc for those adapters that do not interrupt.
587 *
588 * We assume, that utopia_attach can safely add a new utopia while we are going
589 * through the list without disturbing us (we lock the list while getting
590 * the address of the first element, adding is always done at the head).
591 * Removing is entirely handled here.
592 */
593static void
594utopia_daemon(void *arg __unused)
595{
596	struct utopia *utp, *next;
597
598	UTP_RLOCK_LIST();
599	while (utopia_kproc != NULL) {
600		utp = LIST_FIRST(&utopia_list);
601		UTP_RUNLOCK_LIST();
602
603		while (utp != NULL) {
604			mtx_lock(&Giant);	/* XXX depend on MPSAFE */
605			UTP_LOCK(utp);
606			next = LIST_NEXT(utp, link);
607			if (utp->state & UTP_ST_DETACH) {
608				LIST_REMOVE(utp, link);
609				utp->state &= ~UTP_ST_DETACH;
610				wakeup_one(utp);
611			} else if (utp->state & UTP_ST_ACTIVE) {
612				if (utp->flags & UTP_FL_POLL_CARRIER)
613					utopia_update_carrier(utp);
614				utopia_update_stats(utp);
615			}
616			UTP_UNLOCK(utp);
617			mtx_unlock(&Giant);	/* XXX depend on MPSAFE */
618			utp = next;
619		}
620
621		UTP_RLOCK_LIST();
622		msleep(&utopia_list, &utopia_list_mtx, PZERO, "*idle*", hz);
623	}
624	wakeup_one(&utopia_list);
625	UTP_RUNLOCK_LIST();
626	kthread_exit(0);
627}
628
629/*
630 * Module initialisation
631 */
632static int
633utopia_mod_init(module_t mod, int what, void *arg)
634{
635	int err;
636	struct proc *kp;
637
638	switch (what) {
639
640	  case MOD_LOAD:
641		mtx_init(&utopia_list_mtx, "utopia list mutex", NULL, MTX_DEF);
642		err = kthread_create(utopia_daemon, NULL, &utopia_kproc,
643		    RFHIGHPID, 0, "utopia");
644		if (err != 0) {
645			printf("cannot created utopia thread %d\n", err);
646			return (err);
647		}
648		break;
649
650	  case MOD_UNLOAD:
651		UTP_WLOCK_LIST();
652		if ((kp = utopia_kproc) != NULL) {
653			utopia_kproc = NULL;
654			wakeup_one(&utopia_list);
655			PROC_LOCK(kp);
656			UTP_WUNLOCK_LIST();
657			msleep(kp, &kp->p_mtx, PWAIT, "utopia_destroy", 0);
658			PROC_UNLOCK(kp);
659		} else
660			UTP_WUNLOCK_LIST();
661		mtx_destroy(&utopia_list_mtx);
662		break;
663	  default:
664		return (EOPNOTSUPP);
665	}
666	return (0);
667}
668
669static moduledata_t utopia_mod = {
670        "utopia",
671        utopia_mod_init,
672        0
673};
674
675DECLARE_MODULE(utopia, utopia_mod, SI_SUB_INIT_IF, SI_ORDER_ANY);
676MODULE_VERSION(utopia, 1);
677