utopia.c revision 116258
1/*
2 * Copyright (c) 2003
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * 	All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * Author: Hartmut Brandt <harti@freebsd.org>
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/dev/utopia/utopia.c 116258 2003-06-12 14:28:32Z harti $");
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/sysctl.h>
42#include <sys/lock.h>
43#include <sys/mutex.h>
44#include <sys/socket.h>
45
46#include <net/if.h>
47#include <net/if_var.h>
48#include <net/if_media.h>
49#include <net/if_atm.h>
50
51#include <dev/utopia/suni.h>
52#include <dev/utopia/idtphy.h>
53#include <dev/utopia/utopia.h>
54
55#define READREGS(UTOPIA, REG, VALP, NP)				\
56    (UTOPIA)->methods->readregs((UTOPIA)->ifatm, REG, VALP, NP)
57#define WRITEREG(UTOPIA, REG, MASK, VAL)			\
58    (UTOPIA)->methods->writereg((UTOPIA)->ifatm, REG, MASK, VAL)
59
60/*
61 * Global list of all registered interfaces
62 */
63static struct mtx utopia_list_mtx;
64static LIST_HEAD(, utopia) utopia_list = LIST_HEAD_INITIALIZER(utopia_list);
65
66#define UTP_RLOCK_LIST()	mtx_lock(&utopia_list_mtx)
67#define UTP_RUNLOCK_LIST()	mtx_unlock(&utopia_list_mtx)
68#define UTP_WLOCK_LIST()	mtx_lock(&utopia_list_mtx)
69#define UTP_WUNLOCK_LIST()	mtx_unlock(&utopia_list_mtx)
70
71#define UTP_LOCK(UTP)		mtx_lock((UTP)->lock)
72#define UTP_UNLOCK(UTP)		mtx_unlock((UTP)->lock)
73#define UTP_LOCK_ASSERT(UTP)	mtx_assert((UTP)->lock, MA_OWNED)
74
75static struct proc *utopia_kproc;
76
77static void utopia_dump(struct utopia *) __unused;
78
79/*
80 * Debugging - dump all registers.
81 */
82static void
83utopia_dump(struct utopia *utp)
84{
85	uint8_t regs[256];
86	u_int n = 256, i;
87	int err;
88
89	if ((err = READREGS(utp, SUNI_REGO_MRESET, regs, &n)) != 0) {
90		printf("SUNI read error %d\n", err);
91		return;
92	}
93	for (i = 0; i < n; i++) {
94		if (i % 16 == 0)
95			printf("%02x:", i);
96		if (i % 16 == 8)
97			printf(" ");
98		printf(" %02x", regs[i]);
99		if (i % 16 == 15)
100			printf("\n");
101	}
102	if (i % 16 != 0)
103		printf("\n");
104}
105
106/*
107 * Update the carrier status
108 */
109static void
110utopia_check_carrier(struct utopia *utp, u_int carr_ok)
111{
112	int old;
113
114	old = utp->carrier;
115	if (carr_ok) {
116		/* carrier */
117		utp->carrier = UTP_CARR_OK;
118		if (old != UTP_CARR_OK) {
119			if_printf(&utp->ifatm->ifnet, "carrier detected\n");
120		}
121	} else {
122		/* no carrier */
123		utp->carrier = UTP_CARR_LOST;
124		if (old == UTP_CARR_OK) {
125			if_printf(&utp->ifatm->ifnet, "carrier lost\n");
126		}
127	}
128}
129
130static int
131utopia_update_carrier_default(struct utopia *utp)
132{
133	int err;
134	uint8_t reg;
135	u_int n = 1;
136
137	if ((err = READREGS(utp, SUNI_REGO_RSOPSIS, &reg, &n)) != 0) {
138		utp->carrier = UTP_CARR_UNKNOWN;
139		return (err);
140	}
141	utopia_check_carrier(utp, !(reg & SUNI_REGM_RSOPSIS_LOSV));
142	return (0);
143}
144
145/*
146 * enable/disable scrambling
147 */
148static int
149utopia_set_noscramb_default(struct utopia *utp, int noscramb)
150{
151	int err;
152
153	if (noscramb) {
154		err = WRITEREG(utp, SUNI_REGO_TACPCTRL,
155		    SUNI_REGM_TACPCTRL_DSCR, SUNI_REGM_TACPCTRL_DSCR);
156		if (err)
157			return (err);
158		err = WRITEREG(utp, SUNI_REGO_RACPCTRL,
159		    SUNI_REGM_RACPCTRL_DDSCR, SUNI_REGM_RACPCTRL_DDSCR);
160		if (err)
161			return (err);
162		utp->state |= UTP_ST_NOSCRAMB;
163	} else {
164		err = WRITEREG(utp, SUNI_REGO_TACPCTRL,
165		    SUNI_REGM_TACPCTRL_DSCR, 0);
166		if (err)
167			return (err);
168		err = WRITEREG(utp, SUNI_REGO_RACPCTRL,
169		    SUNI_REGM_RACPCTRL_DDSCR, 0);
170		if (err)
171			return (err);
172		utp->state &= ~UTP_ST_NOSCRAMB;
173	}
174	return (0);
175}
176
177/*
178 * set SONET/SDH mode
179 */
180static int
181utopia_set_sdh_default(struct utopia *utp, int sdh)
182{
183	int err;
184
185	if (sdh)
186		err = WRITEREG(utp, SUNI_REGO_TPOPAPTR + 1,
187		    SUNI_REGM_TPOPAPTR_S,
188		    SUNI_REGM_SDH << SUNI_REGS_TPOPAPTR_S);
189	else
190		err = WRITEREG(utp, SUNI_REGO_TPOPAPTR + 1,
191		    SUNI_REGM_TPOPAPTR_S,
192		    SUNI_REGM_SONET << SUNI_REGS_TPOPAPTR_S);
193	if (err != 0)
194		return (err);
195
196	utp->state &= ~UTP_ST_SDH;
197	if (sdh)
198		utp->state |= UTP_ST_SDH;
199
200	return (0);
201}
202
203/*
204 * set idle/unassigned cells
205 */
206static int
207utopia_set_unass_default(struct utopia *utp, int unass)
208{
209	int err;
210
211	if (unass)
212		err = WRITEREG(utp, SUNI_REGO_TACPIDLEH,
213		    0xff, (0 << SUNI_REGS_TACPIDLEH_CLP));
214	else
215		err = WRITEREG(utp, SUNI_REGO_TACPIDLEH,
216		    0xff, (1 << SUNI_REGS_TACPIDLEH_CLP));
217	if (err != 0)
218		return (err);
219
220	utp->state &= ~UTP_ST_UNASS;
221	if (unass)
222		utp->state |= UTP_ST_UNASS;
223
224	return (0);
225}
226
227/*
228 * Set loopback mode for the Lite
229 */
230static int
231utopia_set_loopback_lite(struct utopia *utp, u_int mode)
232{
233	int err;
234	uint32_t val;
235	u_int nmode;
236
237	val = 0;
238	nmode = mode;
239	if (mode & UTP_LOOP_TIME) {
240		nmode &= ~UTP_LOOP_TIME;
241		val |= SUNI_REGM_MCTRL_LOOPT;
242	}
243	if (mode & UTP_LOOP_DIAG) {
244		nmode &= ~UTP_LOOP_DIAG;
245		val |= SUNI_REGM_MCTRL_DLE;
246	}
247	if (mode & UTP_LOOP_LINE) {
248		nmode &= ~UTP_LOOP_LINE;
249		if (val & SUNI_REGM_MCTRL_DLE)
250			return (EINVAL);
251		val |= SUNI_REGM_MCTRL_LLE;
252	}
253	if (nmode != 0)
254		return (EINVAL);
255
256	err = WRITEREG(utp, SUNI_REGO_MCTRL,
257	    SUNI_REGM_MCTRL_LLE | SUNI_REGM_MCTRL_DLE | SUNI_REGM_MCTRL_LOOPT,
258	    val);
259	if (err)
260		return (err);
261	utp->loopback = mode;
262
263	return (0);
264}
265
266/*
267 * Set loopback mode for the Ultra
268 */
269static int
270utopia_set_loopback_ultra(struct utopia *utp, u_int mode)
271{
272	int err;
273	uint32_t val;
274	u_int nmode;
275
276	val = 0;
277	nmode = mode;
278	if (mode & UTP_LOOP_TIME) {
279		nmode &= ~UTP_LOOP_TIME;
280		val |= SUNI_REGM_MCTRL_LOOPT;
281	}
282	if (mode & UTP_LOOP_DIAG) {
283		nmode &= ~UTP_LOOP_DIAG;
284		if (val & SUNI_REGM_MCTRL_LOOPT)
285			return (EINVAL);
286		val |= SUNI_REGM_MCTRL_SDLE;
287	}
288	if (mode & UTP_LOOP_LINE) {
289		nmode &= ~UTP_LOOP_LINE;
290		if (val & (SUNI_REGM_MCTRL_LOOPT | SUNI_REGM_MCTRL_SDLE))
291			return (EINVAL);
292		val |= SUNI_REGM_MCTRL_LLE;
293	}
294	if (mode & UTP_LOOP_PARAL) {
295		nmode &= ~UTP_LOOP_PARAL;
296		val |= SUNI_REGM_MCTRL_PDLE;
297	}
298	if (mode & UTP_LOOP_TWIST) {
299		nmode &= ~UTP_LOOP_TWIST;
300		val |= SUNI_REGM_MCTRL_TPLE;
301	}
302	if (nmode != 0)
303		return (EINVAL);
304
305	err = WRITEREG(utp, SUNI_REGO_MCTRL,
306	    SUNI_REGM_MCTRL_LLE | SUNI_REGM_MCTRL_SDLE | SUNI_REGM_MCTRL_LOOPT |
307	    SUNI_REGM_MCTRL_PDLE | SUNI_REGM_MCTRL_TPLE, val);
308	if (err)
309		return (err);
310	utp->loopback = mode;
311
312	return (0);
313}
314
315/*
316 * Set loopback mode for the Ultra
317 */
318static int
319utopia_set_loopback_622(struct utopia *utp, u_int mode)
320{
321	int err;
322	uint32_t val;
323	uint8_t config;
324	int smode;
325	u_int nmode;
326	u_int n = 1;
327
328	val = 0;
329	nmode = mode;
330	if (mode & UTP_LOOP_PATH) {
331		nmode &= ~UTP_LOOP_PATH;
332		val |= SUNI_REGM_MCTRLM_DPLE;
333	}
334
335	err = READREGS(utp, SUNI_REGO_MCONFIG, &config, &n);
336	if (err != 0)
337		return (err);
338	smode = ((config & SUNI_REGM_MCONFIG_TMODE_622) ==
339	    SUNI_REGM_MCONFIG_TMODE_STS1_BIT &&
340	    (config & SUNI_REGM_MCONFIG_RMODE_622) ==
341	    SUNI_REGM_MCONFIG_RMODE_STS1_BIT);
342
343	if (mode & UTP_LOOP_TIME) {
344		if (!smode)
345			return (EINVAL);
346		nmode &= ~UTP_LOOP_TIME;
347		val |= SUNI_REGM_MCTRLM_LOOPT;
348	}
349	if (mode & UTP_LOOP_DIAG) {
350		nmode &= ~UTP_LOOP_DIAG;
351		if (val & SUNI_REGM_MCTRLM_LOOPT)
352			return (EINVAL);
353		val |= SUNI_REGM_MCTRLM_DLE;
354	}
355	if (mode & UTP_LOOP_LINE) {
356		nmode &= ~UTP_LOOP_LINE;
357		if (val & (SUNI_REGM_MCTRLM_LOOPT | SUNI_REGM_MCTRLM_DLE))
358			return (EINVAL);
359		val |= SUNI_REGM_MCTRLM_LLE;
360	}
361	if (nmode != 0)
362		return (EINVAL);
363
364	err = WRITEREG(utp, SUNI_REGO_MCTRLM,
365	    SUNI_REGM_MCTRLM_LLE | SUNI_REGM_MCTRLM_DLE |
366	    SUNI_REGM_MCTRLM_DPLE | SUNI_REGM_MCTRL_LOOPT, val);
367	if (err)
368		return (err);
369	utp->loopback = mode;
370
371	return (0);
372}
373
374/*
375 * Set the SUNI chip to reflect the current state in utopia.
376 * Assume, that the chip has been reset.
377 */
378static int
379utopia_set_chip(struct utopia *utp)
380{
381	int err = 0;
382
383	/* set sonet/sdh */
384	err |= utopia_set_sdh(utp, utp->state & UTP_ST_SDH);
385
386	/* unassigned or idle cells */
387	err |= utopia_set_unass(utp, utp->state & UTP_ST_UNASS);
388	err |= WRITEREG(utp, SUNI_REGO_TACPIDLEP, 0xff, 0x6a);
389
390	/* loopback */
391	err |= utopia_set_loopback(utp, utp->loopback);
392
393	/* update carrier state */
394	err |= utopia_update_carrier(utp);
395
396	/* enable interrupts on LOS */
397	err |= WRITEREG(utp, SUNI_REGO_RSOPCIE,
398	    SUNI_REGM_RSOPCIE_LOSE, SUNI_REGM_RSOPCIE_LOSE);
399
400	return (err ? EIO : 0);
401}
402
403/*
404 * Reset the SUNI chip to reflect the current state of utopia.
405 */
406static int
407utopia_reset_default(struct utopia *utp)
408{
409	int err = 0;
410
411	if (!(utp->flags & UTP_FL_NORESET)) {
412		err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET,
413		    SUNI_REGM_MRESET_RESET);
414		err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET,
415		    0);
416	}
417
418	/* disable test mode */
419	err |= WRITEREG(utp, SUNI_REGO_MTEST, 0xff, 0x00);
420
421	err |= utopia_set_chip(utp);
422
423	return (err ? EIO : 0);
424}
425
426/*
427 * Reset the SUNI chip to reflect the current state of utopia.
428 */
429static int
430utopia_reset_622(struct utopia *utp)
431{
432	int err = 0;
433
434	if (!(utp->flags & UTP_FL_NORESET)) {
435		err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET,
436		    SUNI_REGM_MRESET_RESET);
437		err |= WRITEREG(utp, SUNI_REGO_MRESET, SUNI_REGM_MRESET_RESET,
438		    0);
439	}
440
441	/* disable test mode */
442	err |= WRITEREG(utp, SUNI_REGO_MTEST, 0xff,
443	    SUNI_REGM_MTEST_DS27_53_622);
444
445	err |= utopia_set_chip(utp);
446
447	return (err ? EIO : 0);
448}
449
450/*
451 * Handle interrupt on lite chip
452 */
453static void
454utopia_intr_default(struct utopia *utp)
455{
456	uint8_t regs[SUNI_REGO_MTEST];
457	u_int n = SUNI_REGO_MTEST;
458	int err;
459
460	/* Read all registers. This acks the interrupts */
461	if ((err = READREGS(utp, SUNI_REGO_MRESET, regs, &n)) != 0) {
462		printf("SUNI read error %d\n", err);
463		return;
464	}
465	if (n <= SUNI_REGO_RSOPSIS) {
466		printf("%s: could not read RSOPSIS", __func__);
467		return;
468	}
469	/* check for LOSI (loss of signal) */
470	if ((regs[SUNI_REGO_MISTATUS] & SUNI_REGM_MISTATUS_RSOPI) &&
471	    (regs[SUNI_REGO_RSOPSIS] & SUNI_REGM_RSOPSIS_LOSI))
472		utopia_check_carrier(utp, !(regs[SUNI_REGO_RSOPSIS]
473		    & SUNI_REGM_RSOPSIS_LOSV));
474}
475
476static const struct utopia_chip chip_622 = {
477	UTP_TYPE_SUNI_622,
478	"Suni/622 (PMC-5355)",
479	256,
480	utopia_reset_622,
481	utopia_set_sdh_default,
482	utopia_set_unass_default,
483	utopia_set_noscramb_default,
484	utopia_update_carrier_default,
485	utopia_set_loopback_622,
486	utopia_intr_default,
487};
488static const struct utopia_chip chip_lite = {
489	UTP_TYPE_SUNI_LITE,
490	"Suni/Lite (PMC-5346)",
491	256,
492	utopia_reset_default,
493	utopia_set_sdh_default,
494	utopia_set_unass_default,
495	utopia_set_noscramb_default,
496	utopia_update_carrier_default,
497	utopia_set_loopback_lite,
498	utopia_intr_default,
499};
500static const struct utopia_chip chip_ultra = {
501	UTP_TYPE_SUNI_ULTRA,
502	"Suni/Ultra (PMC-5350)",
503	256,
504	utopia_reset_default,
505	utopia_set_sdh_default,
506	utopia_set_unass_default,
507	utopia_set_noscramb_default,
508	utopia_update_carrier_default,
509	utopia_set_loopback_ultra,
510	utopia_intr_default,
511};
512
513/*
514 * Reset IDT77105. There is really no way to reset this thing by acessing
515 * the registers. Load the registers with default values.
516 */
517static int
518idt77105_reset(struct utopia *utp)
519{
520	int err = 0;
521	u_int n;
522	uint8_t val[2];
523
524	err |= WRITEREG(utp, IDTPHY_REGO_MCR, 0xff,
525	    IDTPHY_REGM_MCR_DRIC | IDTPHY_REGM_MCR_EI);
526	n = 1;
527	err |= READREGS(utp, IDTPHY_REGO_ISTAT, val, &n);
528	err |= WRITEREG(utp, IDTPHY_REGO_DIAG, 0xff, 0);
529	err |= WRITEREG(utp, IDTPHY_REGO_LHEC, 0xff, 0);
530
531	err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_SEC);
532	n = 2;
533	err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n);
534
535	err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_TX);
536	n = 2;
537	err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n);
538
539	err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_RX);
540	n = 2;
541	err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n);
542
543	err |= WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, IDTPHY_REGM_CNTS_HECE);
544	n = 2;
545	err |= READREGS(utp, IDTPHY_REGO_CNT, val, &n);
546
547	err |= WRITEREG(utp, IDTPHY_REGO_MCR, IDTPHY_REGM_MCR_DREC,
548	    IDTPHY_REGM_MCR_DREC);
549	err |= WRITEREG(utp, IDTPHY_REGO_DIAG, IDTPHY_REGM_DIAG_RFLUSH,
550	    IDTPHY_REGM_DIAG_RFLUSH);
551
552	/* loopback */
553	err |= utopia_set_loopback(utp, utp->loopback);
554
555	/* update carrier state */
556	err |= utopia_update_carrier(utp);
557
558	return (err ? EIO : 0);
559}
560
561static int
562unknown_inval(struct utopia *utp, int what __unused)
563{
564	return (EINVAL);
565}
566
567static int
568idt77105_update_carrier(struct utopia *utp)
569{
570	int err;
571	uint8_t reg;
572	u_int n = 1;
573
574	if ((err = READREGS(utp, IDTPHY_REGO_ISTAT, &reg, &n)) != 0) {
575		utp->carrier = UTP_CARR_UNKNOWN;
576		return (err);
577	}
578	utopia_check_carrier(utp, reg & IDTPHY_REGM_ISTAT_GOOD);
579	return (0);
580}
581
582static int
583idt77105_set_loopback(struct utopia *utp, u_int mode)
584{
585	int err;
586
587	switch (mode) {
588	  case UTP_LOOP_NONE:
589		err = WRITEREG(utp, IDTPHY_REGO_DIAG,
590		    IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_NONE);
591		break;
592
593	  case UTP_LOOP_DIAG:
594		err = WRITEREG(utp, IDTPHY_REGO_DIAG,
595		    IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_PHY);
596		break;
597
598	  case UTP_LOOP_LINE:
599		err = WRITEREG(utp, IDTPHY_REGO_DIAG,
600		    IDTPHY_REGM_DIAG_LOOP, IDTPHY_REGM_DIAG_LOOP_LINE);
601		break;
602
603	  default:
604		return (EINVAL);
605	}
606	if (err)
607		return (err);
608	utp->loopback = mode;
609	return (0);
610}
611
612/*
613 * Handle interrupt on IDT77105 chip
614 */
615static void
616idt77105_intr(struct utopia *utp)
617{
618	uint8_t reg;
619	u_int n = 1;
620	int err;
621
622	/* Interrupt status and ack the interrupt */
623	if ((err = READREGS(utp, IDTPHY_REGO_ISTAT, &reg, &n)) != 0) {
624		printf("IDT77105 read error %d\n", err);
625		return;
626	}
627	/* check for signal condition */
628	utopia_check_carrier(utp, reg & IDTPHY_REGM_ISTAT_GOOD);
629}
630
631static const struct utopia_chip chip_idt77105 = {
632	UTP_TYPE_IDT77105,
633	"IDT77105",
634	7,
635	idt77105_reset,
636	unknown_inval,
637	unknown_inval,
638	unknown_inval,
639	idt77105_update_carrier,
640	idt77105_set_loopback,
641	idt77105_intr,
642};
643
644static int
645unknown_reset(struct utopia *utp __unused)
646{
647	return (EIO);
648}
649
650static int
651unknown_update_carrier(struct utopia *utp)
652{
653	utp->carrier = UTP_CARR_UNKNOWN;
654	return (0);
655}
656
657static int
658unknown_set_loopback(struct utopia *utp __unused, u_int mode __unused)
659{
660	return (EINVAL);
661}
662
663static void
664unknown_intr(struct utopia *utp __unused)
665{
666}
667
668static const struct utopia_chip chip_unknown = {
669	UTP_TYPE_UNKNOWN,
670	"unknown",
671	0,
672	unknown_reset,
673	unknown_inval,
674	unknown_inval,
675	unknown_inval,
676	unknown_update_carrier,
677	unknown_set_loopback,
678	unknown_intr,
679};
680
681/*
682 * Callbacks for the ifmedia infrastructure.
683 */
684static int
685utopia_media_change(struct ifnet *ifp)
686{
687	struct ifatm *ifatm = (struct ifatm *)ifp->if_softc;
688	struct utopia *utp = ifatm->phy;
689	int error = 0;
690
691	UTP_LOCK(utp);
692	if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) {
693		if (utp->media->ifm_media & IFM_ATM_SDH) {
694			if (!(utp->state & UTP_ST_SDH))
695				error = utopia_set_sdh(utp, 1);
696		} else {
697			if (utp->state & UTP_ST_SDH)
698				error = utopia_set_sdh(utp, 0);
699		}
700		if (utp->media->ifm_media & IFM_ATM_UNASSIGNED) {
701			if (!(utp->state & UTP_ST_UNASS))
702				error = utopia_set_unass(utp, 1);
703		} else {
704			if (utp->state & UTP_ST_UNASS)
705				error = utopia_set_unass(utp, 0);
706		}
707		if (utp->media->ifm_media & IFM_ATM_NOSCRAMB) {
708			if (!(utp->state & UTP_ST_NOSCRAMB))
709				error = utopia_set_noscramb(utp, 1);
710		} else {
711			if (utp->state & UTP_ST_NOSCRAMB)
712				error = utopia_set_noscramb(utp, 0);
713		}
714	} else
715		error = EIO;
716	UTP_UNLOCK(utp);
717	return (error);
718}
719
720/*
721 * Look at the carrier status.
722 */
723static void
724utopia_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
725{
726	struct utopia *utp = ((struct ifatm *)ifp->if_softc)->phy;
727
728	UTP_LOCK(utp);
729	if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) {
730		ifmr->ifm_active = IFM_ATM | utp->ifatm->mib.media;
731
732		switch (utp->carrier) {
733
734		  case UTP_CARR_OK:
735			ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
736			break;
737
738		  case UTP_CARR_LOST:
739			ifmr->ifm_status = IFM_AVALID;
740			break;
741
742		  default:
743			ifmr->ifm_status = 0;
744			break;
745		}
746		if (utp->state & UTP_ST_SDH) {
747			ifmr->ifm_active |= IFM_ATM_SDH;
748			ifmr->ifm_current |= IFM_ATM_SDH;
749		}
750		if (utp->state & UTP_ST_UNASS) {
751			ifmr->ifm_active |= IFM_ATM_UNASSIGNED;
752			ifmr->ifm_current |= IFM_ATM_UNASSIGNED;
753		}
754		if (utp->state & UTP_ST_NOSCRAMB) {
755			ifmr->ifm_active |= IFM_ATM_NOSCRAMB;
756			ifmr->ifm_current |= IFM_ATM_NOSCRAMB;
757		}
758	} else {
759		ifmr->ifm_active = 0;
760		ifmr->ifm_status = 0;
761	}
762	UTP_UNLOCK(utp);
763}
764
765/*
766 * Initialize media from the mib
767 */
768void
769utopia_init_media(struct utopia *utp)
770{
771
772	ifmedia_removeall(utp->media);
773	ifmedia_add(utp->media, IFM_ATM | utp->ifatm->mib.media, 0, NULL);
774	ifmedia_set(utp->media, IFM_ATM | utp->ifatm->mib.media);
775}
776
777/*
778 * Reset all media
779 */
780void
781utopia_reset_media(struct utopia *utp)
782{
783
784	ifmedia_removeall(utp->media);
785}
786
787/*
788 * This is called by the driver as soon as the SUNI registers are accessible.
789 * This may be either in the attach routine or the init routine of the driver.
790 */
791int
792utopia_start(struct utopia *utp)
793{
794	uint8_t reg;
795	int err;
796	u_int n = 1;
797
798	if ((err = READREGS(utp, SUNI_REGO_MRESET, &reg, &n)) != 0)
799		return (err);
800
801	switch (reg & SUNI_REGM_MRESET_TYPE) {
802
803	  case SUNI_REGM_MRESET_TYPE_622:
804		utp->chip = &chip_622;
805		break;
806
807	  case SUNI_REGM_MRESET_TYPE_LITE:
808		utp->chip = &chip_lite;
809		break;
810
811	  case SUNI_REGM_MRESET_TYPE_ULTRA:
812		utp->chip = &chip_ultra;
813		break;
814
815	  default:
816		if (reg == (IDTPHY_REGM_MCR_DRIC | IDTPHY_REGM_MCR_EI))
817			utp->chip = &chip_idt77105;
818		else {
819			if_printf(&utp->ifatm->ifnet,
820			    "unknown ATM-PHY chip %#x\n", reg);
821			utp->chip = &chip_unknown;
822		}
823		break;
824	}
825	utp->state |= UTP_ST_ACTIVE;
826	return (0);
827}
828
829/*
830 * Stop the chip
831 */
832void
833utopia_stop(struct utopia *utp)
834{
835	utp->state &= ~UTP_ST_ACTIVE;
836}
837
838/*
839 * Handle the sysctls
840 */
841static int
842utopia_sysctl_regs(SYSCTL_HANDLER_ARGS)
843{
844	struct utopia *utp = (struct utopia *)arg1;
845	int error;
846	u_int n;
847	uint8_t *val;
848	uint8_t new[3];
849
850	if ((n = utp->chip->nregs) == 0)
851		return (EIO);
852	val = malloc(sizeof(uint8_t) * n, M_TEMP, M_WAITOK);
853
854	UTP_LOCK(utp);
855	error = READREGS(utp, 0, val, &n);
856	UTP_UNLOCK(utp);
857
858	if (error) {
859		free(val, M_TEMP);
860		return (error);
861	}
862
863	error = SYSCTL_OUT(req, val, sizeof(uint8_t) * n);
864	free(val, M_TEMP);
865	if (error != 0 || req->newptr == NULL)
866		return (error);
867
868	error = SYSCTL_IN(req, new, sizeof(new));
869	if (error)
870		return (error);
871
872	UTP_LOCK(utp);
873	error = WRITEREG(utp, new[0], new[1], new[2]);
874	UTP_UNLOCK(utp);
875
876	return (error);
877}
878
879/*
880 * Handle the loopback sysctl
881 */
882static int
883utopia_sysctl_loopback(SYSCTL_HANDLER_ARGS)
884{
885	struct utopia *utp = (struct utopia *)arg1;
886	int error;
887	u_int loopback;
888
889	error = SYSCTL_OUT(req, &utp->loopback, sizeof(u_int));
890	if (error != 0 || req->newptr == NULL)
891		return (error);
892
893	error = SYSCTL_IN(req, &loopback, sizeof(u_int));
894	if (error)
895		return (error);
896
897	UTP_LOCK(utp);
898	error = utopia_set_loopback(utp, loopback);
899	UTP_UNLOCK(utp);
900
901	return (error);
902}
903
904/*
905 * Handle the type sysctl
906 */
907static int
908utopia_sysctl_type(SYSCTL_HANDLER_ARGS)
909{
910	struct utopia *utp = (struct utopia *)arg1;
911
912	return (SYSCTL_OUT(req, &utp->chip->type, sizeof(utp->chip->type)));
913}
914
915/*
916 * Handle the name sysctl
917 */
918static int
919utopia_sysctl_name(SYSCTL_HANDLER_ARGS)
920{
921	struct utopia *utp = (struct utopia *)arg1;
922
923	return (SYSCTL_OUT(req, utp->chip->name, strlen(utp->chip->name) + 1));
924}
925
926/*
927 * Initialize the state. This is called from the drivers attach
928 * function. The mutex must be already initialized.
929 */
930int
931utopia_attach(struct utopia *utp, struct ifatm *ifatm, struct ifmedia *media,
932    struct mtx *lock, struct sysctl_ctx_list *ctx,
933    struct sysctl_oid_list *children, const struct utopia_methods *m)
934{
935
936	bzero(utp, sizeof(*utp));
937	utp->ifatm = ifatm;
938	utp->methods = m;
939	utp->media = media;
940	utp->lock = lock;
941	utp->chip = &chip_unknown;
942
943	ifmedia_init(media,
944	    IFM_ATM_SDH | IFM_ATM_UNASSIGNED | IFM_ATM_NOSCRAMB,
945	    utopia_media_change, utopia_media_status);
946
947	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_regs",
948	    CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_regs, "S",
949	    "phy registers") == NULL)
950		return (-1);
951
952	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_loopback",
953	    CTLFLAG_RW | CTLTYPE_UINT, utp, 0, utopia_sysctl_loopback, "IU",
954	    "phy loopback mode") == NULL)
955		return (-1);
956
957	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_type",
958	    CTLFLAG_RD | CTLTYPE_UINT, utp, 0, utopia_sysctl_type, "IU",
959	    "phy type") == NULL)
960		return (-1);
961
962	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_name",
963	    CTLFLAG_RD | CTLTYPE_STRING, utp, 0, utopia_sysctl_name, "A",
964	    "phy name") == NULL)
965		return (-1);
966
967	UTP_WLOCK_LIST();
968	LIST_INSERT_HEAD(&utopia_list, utp, link);
969	UTP_WUNLOCK_LIST();
970
971	utp->state |= UTP_ST_ATTACHED;
972	return (0);
973}
974
975/*
976 * Detach. We set a flag here, wakeup the daemon and let him do it.
977 * Here we need the lock for synchronisation with the daemon.
978 */
979void
980utopia_detach(struct utopia *utp)
981{
982
983	UTP_LOCK_ASSERT(utp);
984	if (utp->state & UTP_ST_ATTACHED) {
985		utp->state |= UTP_ST_DETACH;
986		while (utp->state & UTP_ST_DETACH) {
987			wakeup(&utopia_list);
988			msleep(utp, utp->lock, PZERO, "utopia_detach", hz);
989		}
990	}
991}
992
993/*
994 * The carrier state kernel proc for those adapters that do not interrupt.
995 *
996 * We assume, that utopia_attach can safely add a new utopia while we are going
997 * through the list without disturbing us (we lock the list while getting
998 * the address of the first element, adding is always done at the head).
999 * Removing is entirely handled here.
1000 */
1001static void
1002utopia_daemon(void *arg __unused)
1003{
1004	struct utopia *utp, *next;
1005
1006	UTP_RLOCK_LIST();
1007	while (utopia_kproc != NULL) {
1008		utp = LIST_FIRST(&utopia_list);
1009		UTP_RUNLOCK_LIST();
1010
1011		while (utp != NULL) {
1012			mtx_lock(&Giant);	/* XXX depend on MPSAFE */
1013			UTP_LOCK(utp);
1014			next = LIST_NEXT(utp, link);
1015			if (utp->state & UTP_ST_DETACH) {
1016				LIST_REMOVE(utp, link);
1017				utp->state &= ~UTP_ST_DETACH;
1018				wakeup_one(utp);
1019			} else if ((utp->state & UTP_ST_ACTIVE) &&
1020			    (utp->flags & UTP_FL_POLL_CARRIER)) {
1021				utopia_update_carrier(utp);
1022			}
1023			UTP_UNLOCK(utp);
1024			mtx_unlock(&Giant);	/* XXX depend on MPSAFE */
1025			utp = next;
1026		}
1027
1028		UTP_RLOCK_LIST();
1029		msleep(&utopia_list, &utopia_list_mtx, PZERO, "utopia", hz);
1030	}
1031	wakeup_one(&utopia_list);
1032	UTP_RUNLOCK_LIST();
1033	mtx_lock(&Giant);
1034	kthread_exit(0);
1035}
1036
1037/*
1038 * Module initialisation
1039 */
1040static int
1041utopia_mod_init(module_t mod, int what, void *arg)
1042{
1043	int err;
1044	struct proc *kp;
1045
1046	switch (what) {
1047
1048	  case MOD_LOAD:
1049		mtx_init(&utopia_list_mtx, "utopia list mutex", NULL, MTX_DEF);
1050		err = kthread_create(utopia_daemon, NULL, &utopia_kproc,
1051		    RFHIGHPID, 0, "utopia");
1052		if (err != 0) {
1053			printf("cannot created utopia thread %d\n", err);
1054			return (err);
1055		}
1056		break;
1057
1058	  case MOD_UNLOAD:
1059		UTP_WLOCK_LIST();
1060		if ((kp = utopia_kproc) != NULL) {
1061			utopia_kproc = NULL;
1062			wakeup_one(&utopia_list);
1063			PROC_LOCK(kp);
1064			UTP_WUNLOCK_LIST();
1065			msleep(kp, &kp->p_mtx, PWAIT, "utopia_destroy", 0);
1066			PROC_UNLOCK(kp);
1067		} else
1068			UTP_WUNLOCK_LIST();
1069		mtx_destroy(&utopia_list_mtx);
1070		break;
1071	}
1072	return (0);
1073}
1074
1075static moduledata_t utopia_mod = {
1076        "utopia",
1077        utopia_mod_init,
1078        0
1079};
1080
1081DECLARE_MODULE(utopia, utopia_mod, SI_SUB_INIT_IF, SI_ORDER_ANY);
1082MODULE_VERSION(utopia, 1);
1083