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$");
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 = IFP2IFATM(ifp);
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 = IFP2IFATM(ifp)->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	if (error && req->newptr != NULL)
448		bcopy(val, &utp->stats, sizeof(utp->stats));
449	free(val, M_TEMP);
450
451	/* ignore actual new value */
452
453	return (error);
454}
455
456/*
457 * Handle the loopback sysctl
458 */
459static int
460utopia_sysctl_loopback(SYSCTL_HANDLER_ARGS)
461{
462	struct utopia *utp = (struct utopia *)arg1;
463	int error;
464	u_int loopback;
465
466	error = SYSCTL_OUT(req, &utp->loopback, sizeof(u_int));
467	if (error != 0 || req->newptr == NULL)
468		return (error);
469
470	error = SYSCTL_IN(req, &loopback, sizeof(u_int));
471	if (error)
472		return (error);
473
474	UTP_LOCK(utp);
475	error = utopia_set_loopback(utp, loopback);
476	UTP_UNLOCK(utp);
477
478	return (error);
479}
480
481/*
482 * Handle the type sysctl
483 */
484static int
485utopia_sysctl_type(SYSCTL_HANDLER_ARGS)
486{
487	struct utopia *utp = (struct utopia *)arg1;
488
489	return (SYSCTL_OUT(req, &utp->chip->type, sizeof(utp->chip->type)));
490}
491
492/*
493 * Handle the name sysctl
494 */
495static int
496utopia_sysctl_name(SYSCTL_HANDLER_ARGS)
497{
498	struct utopia *utp = (struct utopia *)arg1;
499
500	return (SYSCTL_OUT(req, utp->chip->name, strlen(utp->chip->name) + 1));
501}
502
503/*
504 * Initialize the state. This is called from the drivers attach
505 * function. The mutex must be already initialized.
506 */
507int
508utopia_attach(struct utopia *utp, struct ifatm *ifatm, struct ifmedia *media,
509    struct mtx *lock, struct sysctl_ctx_list *ctx,
510    struct sysctl_oid_list *children, const struct utopia_methods *m)
511{
512
513	bzero(utp, sizeof(*utp));
514	utp->ifatm = ifatm;
515	utp->methods = m;
516	utp->media = media;
517	utp->lock = lock;
518	utp->chip = &utopia_chip_unknown;
519	utp->stats.version = 1;
520
521	ifmedia_init(media,
522	    IFM_ATM_SDH | IFM_ATM_UNASSIGNED | IFM_ATM_NOSCRAMB,
523	    utopia_media_change, utopia_media_status);
524
525	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_regs",
526	    CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_regs, "S",
527	    "phy registers") == NULL)
528		return (-1);
529
530	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_loopback",
531	    CTLFLAG_RW | CTLTYPE_UINT, utp, 0, utopia_sysctl_loopback, "IU",
532	    "phy loopback mode") == NULL)
533		return (-1);
534
535	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_type",
536	    CTLFLAG_RD | CTLTYPE_UINT, utp, 0, utopia_sysctl_type, "IU",
537	    "phy type") == NULL)
538		return (-1);
539
540	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_name",
541	    CTLFLAG_RD | CTLTYPE_STRING, utp, 0, utopia_sysctl_name, "A",
542	    "phy name") == NULL)
543		return (-1);
544
545	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_stats",
546	    CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_stats, "S",
547	    "phy statistics") == NULL)
548		return (-1);
549
550	if (SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "phy_state",
551	    CTLFLAG_RD, &utp->state, 0, "phy state") == NULL)
552		return (-1);
553
554	if (SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "phy_carrier",
555	    CTLFLAG_RD, &utp->carrier, 0, "phy carrier") == NULL)
556		return (-1);
557
558	UTP_WLOCK_LIST();
559	LIST_INSERT_HEAD(&utopia_list, utp, link);
560	UTP_WUNLOCK_LIST();
561
562	utp->state |= UTP_ST_ATTACHED;
563	return (0);
564}
565
566/*
567 * Detach. We set a flag here, wakeup the daemon and let him do it.
568 * Here we need the lock for synchronisation with the daemon.
569 */
570void
571utopia_detach(struct utopia *utp)
572{
573
574	UTP_LOCK_ASSERT(utp);
575	if (utp->state & UTP_ST_ATTACHED) {
576		utp->state |= UTP_ST_DETACH;
577		while (utp->state & UTP_ST_DETACH) {
578			wakeup(&utopia_list);
579			msleep(utp, utp->lock, PZERO, "utopia_detach", hz);
580		}
581	}
582}
583
584/*
585 * The carrier state kernel proc for those adapters that do not interrupt.
586 *
587 * We assume, that utopia_attach can safely add a new utopia while we are going
588 * through the list without disturbing us (we lock the list while getting
589 * the address of the first element, adding is always done at the head).
590 * Removing is entirely handled here.
591 */
592static void
593utopia_daemon(void *arg __unused)
594{
595	struct utopia *utp, *next;
596
597	UTP_RLOCK_LIST();
598	while (utopia_kproc != NULL) {
599		utp = LIST_FIRST(&utopia_list);
600		UTP_RUNLOCK_LIST();
601
602		while (utp != NULL) {
603			mtx_lock(&Giant);	/* XXX depend on MPSAFE */
604			UTP_LOCK(utp);
605			next = LIST_NEXT(utp, link);
606			if (utp->state & UTP_ST_DETACH) {
607				LIST_REMOVE(utp, link);
608				utp->state &= ~UTP_ST_DETACH;
609				wakeup_one(utp);
610			} else if (utp->state & UTP_ST_ACTIVE) {
611				if (utp->flags & UTP_FL_POLL_CARRIER)
612					utopia_update_carrier(utp);
613				utopia_update_stats(utp);
614			}
615			UTP_UNLOCK(utp);
616			mtx_unlock(&Giant);	/* XXX depend on MPSAFE */
617			utp = next;
618		}
619
620		UTP_RLOCK_LIST();
621		msleep(&utopia_list, &utopia_list_mtx, PZERO, "*idle*", hz);
622	}
623	wakeup_one(&utopia_list);
624	UTP_RUNLOCK_LIST();
625	kproc_exit(0);
626}
627
628/*
629 * Module initialisation
630 */
631static int
632utopia_mod_init(module_t mod, int what, void *arg)
633{
634	int err;
635	struct proc *kp;
636
637	switch (what) {
638
639	  case MOD_LOAD:
640		mtx_init(&utopia_list_mtx, "utopia list mutex", NULL, MTX_DEF);
641		err = kproc_create(utopia_daemon, NULL, &utopia_kproc,
642		    RFHIGHPID, 0, "utopia");
643		if (err != 0) {
644			printf("cannot created utopia thread %d\n", err);
645			return (err);
646		}
647		break;
648
649	  case MOD_UNLOAD:
650		UTP_WLOCK_LIST();
651		if ((kp = utopia_kproc) != NULL) {
652			utopia_kproc = NULL;
653			wakeup_one(&utopia_list);
654			PROC_LOCK(kp);
655			UTP_WUNLOCK_LIST();
656			msleep(kp, &kp->p_mtx, PWAIT, "utopia_destroy", 0);
657			PROC_UNLOCK(kp);
658		} else
659			UTP_WUNLOCK_LIST();
660		mtx_destroy(&utopia_list_mtx);
661		break;
662	  default:
663		return (EOPNOTSUPP);
664	}
665	return (0);
666}
667
668static moduledata_t utopia_mod = {
669        "utopia",
670        utopia_mod_init,
671        0
672};
673
674DECLARE_MODULE(utopia, utopia_mod, SI_SUB_INIT_IF, SI_ORDER_ANY);
675MODULE_VERSION(utopia, 1);
676