utopia.c revision 117546
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 117546 2003-07-14 12:12:50Z 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
644/*
645 * Update the carrier status
646 */
647static int
648idt77155_update_carrier(struct utopia *utp)
649{
650	int err;
651	uint8_t reg;
652	u_int n = 1;
653
654	if ((err = READREGS(utp, IDTPHY_REGO_RSOS, &reg, &n)) != 0) {
655		utp->carrier = UTP_CARR_UNKNOWN;
656		return (err);
657	}
658	utopia_check_carrier(utp, !(reg & IDTPHY_REGM_RSOS_LOS));
659	return (0);
660}
661
662/*
663 * Handle interrupt on IDT77155 chip
664 */
665static void
666idt77155_intr(struct utopia *utp)
667{
668	uint8_t reg;
669	u_int n = 1;
670	int err;
671
672	if ((err = READREGS(utp, IDTPHY_REGO_RSOS, &reg, &n)) != 0) {
673		printf("IDT77105 read error %d\n", err);
674		return;
675	}
676	utopia_check_carrier(utp, !(reg & IDTPHY_REGM_RSOS_LOS));
677}
678
679/*
680 * set SONET/SDH mode
681 */
682static int
683idt77155_set_sdh(struct utopia *utp, int sdh)
684{
685	int err;
686
687	if (sdh)
688		err = WRITEREG(utp, IDTPHY_REGO_PTRM,
689		    IDTPHY_REGM_PTRM_SS, IDTPHY_REGM_PTRM_SDH);
690	else
691		err = WRITEREG(utp, IDTPHY_REGO_PTRM,
692		    IDTPHY_REGM_PTRM_SS, IDTPHY_REGM_PTRM_SONET);
693	if (err != 0)
694		return (err);
695
696	utp->state &= ~UTP_ST_SDH;
697	if (sdh)
698		utp->state |= UTP_ST_SDH;
699
700	return (0);
701}
702
703/*
704 * set idle/unassigned cells
705 */
706static int
707idt77155_set_unass(struct utopia *utp, int unass)
708{
709	int err;
710
711	if (unass)
712		err = WRITEREG(utp, IDTPHY_REGO_TCHP, 0xff, 0);
713	else
714		err = WRITEREG(utp, IDTPHY_REGO_TCHP, 0xff, 1);
715	if (err != 0)
716		return (err);
717
718	utp->state &= ~UTP_ST_UNASS;
719	if (unass)
720		utp->state |= UTP_ST_UNASS;
721
722	return (0);
723}
724
725/*
726 * enable/disable scrambling
727 */
728static int
729idt77155_set_noscramb(struct utopia *utp, int noscramb)
730{
731	int err;
732
733	if (noscramb) {
734		err = WRITEREG(utp, IDTPHY_REGO_TCC,
735		    IDTPHY_REGM_TCC_DSCR, IDTPHY_REGM_TCC_DSCR);
736		if (err)
737			return (err);
738		err = WRITEREG(utp, IDTPHY_REGO_RCC,
739		    IDTPHY_REGM_RCC_DSCR, IDTPHY_REGM_RCC_DSCR);
740		if (err)
741			return (err);
742		utp->state |= UTP_ST_NOSCRAMB;
743	} else {
744		err = WRITEREG(utp, IDTPHY_REGO_TCC,
745		    IDTPHY_REGM_TCC_DSCR, 0);
746		if (err)
747			return (err);
748		err = WRITEREG(utp, IDTPHY_REGO_RCC,
749		    IDTPHY_REGM_RCC_DSCR, 0);
750		if (err)
751			return (err);
752		utp->state &= ~UTP_ST_NOSCRAMB;
753	}
754	return (0);
755}
756
757/*
758 * Set loopback mode for the 77155
759 */
760static int
761idt77155_set_loopback(struct utopia *utp, u_int mode)
762{
763	int err;
764	uint32_t val;
765	u_int nmode;
766
767	val = 0;
768	nmode = mode;
769	if (mode & UTP_LOOP_TIME) {
770		nmode &= ~UTP_LOOP_TIME;
771		val |= IDTPHY_REGM_MCTL_TLOOP;
772	}
773	if (mode & UTP_LOOP_DIAG) {
774		nmode &= ~UTP_LOOP_DIAG;
775		val |= IDTPHY_REGM_MCTL_DLOOP;
776	}
777	if (mode & UTP_LOOP_LINE) {
778		nmode &= ~UTP_LOOP_LINE;
779		val |= IDTPHY_REGM_MCTL_LLOOP;
780	}
781	if (nmode != 0)
782		return (EINVAL);
783
784	err = WRITEREG(utp, IDTPHY_REGO_MCTL, IDTPHY_REGM_MCTL_TLOOP |
785	    IDTPHY_REGM_MCTL_DLOOP | IDTPHY_REGM_MCTL_LLOOP, val);
786	if (err)
787		return (err);
788	utp->loopback = mode;
789
790	return (0);
791}
792
793/*
794 * Set the chip to reflect the current state in utopia.
795 * Assume, that the chip has been reset.
796 */
797static int
798idt77155_set_chip(struct utopia *utp)
799{
800	int err = 0;
801
802	/* set sonet/sdh */
803	err |= idt77155_set_sdh(utp, utp->state & UTP_ST_SDH);
804
805	/* unassigned or idle cells */
806	err |= idt77155_set_unass(utp, utp->state & UTP_ST_UNASS);
807
808	/* loopback */
809	err |= idt77155_set_loopback(utp, utp->loopback);
810
811	/* update carrier state */
812	err |= idt77155_update_carrier(utp);
813
814	/* enable interrupts on LOS */
815	err |= WRITEREG(utp, IDTPHY_REGO_INT,
816	    IDTPHY_REGM_INT_RXSOHI, IDTPHY_REGM_INT_RXSOHI);
817	err |= WRITEREG(utp, IDTPHY_REGO_RSOC,
818	    IDTPHY_REGM_RSOC_LOSI, IDTPHY_REGM_RSOC_LOSI);
819
820	return (err ? EIO : 0);
821}
822
823/*
824 * Reset the chip to reflect the current state of utopia.
825 */
826static int
827idt77155_reset(struct utopia *utp)
828{
829	int err = 0;
830
831	if (!(utp->flags & UTP_FL_NORESET)) {
832		err |= WRITEREG(utp, IDTPHY_REGO_MRID, IDTPHY_REGM_MRID_RESET,
833		    IDTPHY_REGM_MRID_RESET);
834		err |= WRITEREG(utp, IDTPHY_REGO_MRID, IDTPHY_REGM_MRID_RESET,
835		    0);
836	}
837
838	err |= idt77155_set_chip(utp);
839
840	return (err ? EIO : 0);
841}
842
843static const struct utopia_chip chip_idt77155 = {
844	UTP_TYPE_IDT77155,
845	"IDT77155",
846	0x80,
847	idt77155_reset,
848	idt77155_set_sdh,
849	idt77155_set_unass,
850	idt77155_set_noscramb,
851	idt77155_update_carrier,
852	idt77155_set_loopback,
853	idt77155_intr,
854};
855
856static int
857unknown_reset(struct utopia *utp __unused)
858{
859	return (EIO);
860}
861
862static int
863unknown_update_carrier(struct utopia *utp)
864{
865	utp->carrier = UTP_CARR_UNKNOWN;
866	return (0);
867}
868
869static int
870unknown_set_loopback(struct utopia *utp __unused, u_int mode __unused)
871{
872	return (EINVAL);
873}
874
875static void
876unknown_intr(struct utopia *utp __unused)
877{
878}
879
880static const struct utopia_chip chip_unknown = {
881	UTP_TYPE_UNKNOWN,
882	"unknown",
883	0,
884	unknown_reset,
885	unknown_inval,
886	unknown_inval,
887	unknown_inval,
888	unknown_update_carrier,
889	unknown_set_loopback,
890	unknown_intr,
891};
892
893/*
894 * Callbacks for the ifmedia infrastructure.
895 */
896static int
897utopia_media_change(struct ifnet *ifp)
898{
899	struct ifatm *ifatm = (struct ifatm *)ifp->if_softc;
900	struct utopia *utp = ifatm->phy;
901	int error = 0;
902
903	UTP_LOCK(utp);
904	if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) {
905		if (utp->media->ifm_media & IFM_ATM_SDH) {
906			if (!(utp->state & UTP_ST_SDH))
907				error = utopia_set_sdh(utp, 1);
908		} else {
909			if (utp->state & UTP_ST_SDH)
910				error = utopia_set_sdh(utp, 0);
911		}
912		if (utp->media->ifm_media & IFM_ATM_UNASSIGNED) {
913			if (!(utp->state & UTP_ST_UNASS))
914				error = utopia_set_unass(utp, 1);
915		} else {
916			if (utp->state & UTP_ST_UNASS)
917				error = utopia_set_unass(utp, 0);
918		}
919		if (utp->media->ifm_media & IFM_ATM_NOSCRAMB) {
920			if (!(utp->state & UTP_ST_NOSCRAMB))
921				error = utopia_set_noscramb(utp, 1);
922		} else {
923			if (utp->state & UTP_ST_NOSCRAMB)
924				error = utopia_set_noscramb(utp, 0);
925		}
926	} else
927		error = EIO;
928	UTP_UNLOCK(utp);
929	return (error);
930}
931
932/*
933 * Look at the carrier status.
934 */
935static void
936utopia_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
937{
938	struct utopia *utp = ((struct ifatm *)ifp->if_softc)->phy;
939
940	UTP_LOCK(utp);
941	if (utp->chip->type != UTP_TYPE_UNKNOWN && utp->state & UTP_ST_ACTIVE) {
942		ifmr->ifm_active = IFM_ATM | utp->ifatm->mib.media;
943
944		switch (utp->carrier) {
945
946		  case UTP_CARR_OK:
947			ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
948			break;
949
950		  case UTP_CARR_LOST:
951			ifmr->ifm_status = IFM_AVALID;
952			break;
953
954		  default:
955			ifmr->ifm_status = 0;
956			break;
957		}
958		if (utp->state & UTP_ST_SDH) {
959			ifmr->ifm_active |= IFM_ATM_SDH;
960			ifmr->ifm_current |= IFM_ATM_SDH;
961		}
962		if (utp->state & UTP_ST_UNASS) {
963			ifmr->ifm_active |= IFM_ATM_UNASSIGNED;
964			ifmr->ifm_current |= IFM_ATM_UNASSIGNED;
965		}
966		if (utp->state & UTP_ST_NOSCRAMB) {
967			ifmr->ifm_active |= IFM_ATM_NOSCRAMB;
968			ifmr->ifm_current |= IFM_ATM_NOSCRAMB;
969		}
970	} else {
971		ifmr->ifm_active = 0;
972		ifmr->ifm_status = 0;
973	}
974	UTP_UNLOCK(utp);
975}
976
977/*
978 * Initialize media from the mib
979 */
980void
981utopia_init_media(struct utopia *utp)
982{
983
984	ifmedia_removeall(utp->media);
985	ifmedia_add(utp->media, IFM_ATM | utp->ifatm->mib.media, 0, NULL);
986	ifmedia_set(utp->media, IFM_ATM | utp->ifatm->mib.media);
987}
988
989/*
990 * Reset all media
991 */
992void
993utopia_reset_media(struct utopia *utp)
994{
995
996	ifmedia_removeall(utp->media);
997}
998
999/*
1000 * This is called by the driver as soon as the SUNI registers are accessible.
1001 * This may be either in the attach routine or the init routine of the driver.
1002 */
1003int
1004utopia_start(struct utopia *utp)
1005{
1006	uint8_t reg;
1007	int err;
1008	u_int n = 1;
1009
1010	if ((err = READREGS(utp, SUNI_REGO_MRESET, &reg, &n)) != 0)
1011		return (err);
1012
1013	switch (reg & SUNI_REGM_MRESET_TYPE) {
1014
1015	  case SUNI_REGM_MRESET_TYPE_622:
1016		utp->chip = &chip_622;
1017		break;
1018
1019	  case SUNI_REGM_MRESET_TYPE_LITE:
1020		/* this may be either a SUNI LITE or a IDT77155 *
1021		 * Read register 0x70. The SUNI doesn't have it */
1022		n = 1;
1023		if ((err = READREGS(utp, IDTPHY_REGO_RBER, &reg, &n)) != 0)
1024			return (err);
1025		if ((reg & ~IDTPHY_REGM_RBER_RESV) ==
1026		    (IDTPHY_REGM_RBER_FAIL | IDTPHY_REGM_RBER_WARN))
1027			utp->chip = &chip_idt77155;
1028		else
1029			utp->chip = &chip_lite;
1030		break;
1031
1032	  case SUNI_REGM_MRESET_TYPE_ULTRA:
1033		utp->chip = &chip_ultra;
1034		break;
1035
1036	  default:
1037		if (reg == (IDTPHY_REGM_MCR_DRIC | IDTPHY_REGM_MCR_EI))
1038			utp->chip = &chip_idt77105;
1039		else {
1040			if_printf(&utp->ifatm->ifnet,
1041			    "unknown ATM-PHY chip %#x\n", reg);
1042			utp->chip = &chip_unknown;
1043		}
1044		break;
1045	}
1046	utp->state |= UTP_ST_ACTIVE;
1047	return (0);
1048}
1049
1050/*
1051 * Stop the chip
1052 */
1053void
1054utopia_stop(struct utopia *utp)
1055{
1056	utp->state &= ~UTP_ST_ACTIVE;
1057}
1058
1059/*
1060 * Handle the sysctls
1061 */
1062static int
1063utopia_sysctl_regs(SYSCTL_HANDLER_ARGS)
1064{
1065	struct utopia *utp = (struct utopia *)arg1;
1066	int error;
1067	u_int n;
1068	uint8_t *val;
1069	uint8_t new[3];
1070
1071	if ((n = utp->chip->nregs) == 0)
1072		return (EIO);
1073	val = malloc(sizeof(uint8_t) * n, M_TEMP, M_WAITOK);
1074
1075	UTP_LOCK(utp);
1076	error = READREGS(utp, 0, val, &n);
1077	UTP_UNLOCK(utp);
1078
1079	if (error) {
1080		free(val, M_TEMP);
1081		return (error);
1082	}
1083
1084	error = SYSCTL_OUT(req, val, sizeof(uint8_t) * n);
1085	free(val, M_TEMP);
1086	if (error != 0 || req->newptr == NULL)
1087		return (error);
1088
1089	error = SYSCTL_IN(req, new, sizeof(new));
1090	if (error)
1091		return (error);
1092
1093	UTP_LOCK(utp);
1094	error = WRITEREG(utp, new[0], new[1], new[2]);
1095	UTP_UNLOCK(utp);
1096
1097	return (error);
1098}
1099
1100/*
1101 * Handle the loopback sysctl
1102 */
1103static int
1104utopia_sysctl_loopback(SYSCTL_HANDLER_ARGS)
1105{
1106	struct utopia *utp = (struct utopia *)arg1;
1107	int error;
1108	u_int loopback;
1109
1110	error = SYSCTL_OUT(req, &utp->loopback, sizeof(u_int));
1111	if (error != 0 || req->newptr == NULL)
1112		return (error);
1113
1114	error = SYSCTL_IN(req, &loopback, sizeof(u_int));
1115	if (error)
1116		return (error);
1117
1118	UTP_LOCK(utp);
1119	error = utopia_set_loopback(utp, loopback);
1120	UTP_UNLOCK(utp);
1121
1122	return (error);
1123}
1124
1125/*
1126 * Handle the type sysctl
1127 */
1128static int
1129utopia_sysctl_type(SYSCTL_HANDLER_ARGS)
1130{
1131	struct utopia *utp = (struct utopia *)arg1;
1132
1133	return (SYSCTL_OUT(req, &utp->chip->type, sizeof(utp->chip->type)));
1134}
1135
1136/*
1137 * Handle the name sysctl
1138 */
1139static int
1140utopia_sysctl_name(SYSCTL_HANDLER_ARGS)
1141{
1142	struct utopia *utp = (struct utopia *)arg1;
1143
1144	return (SYSCTL_OUT(req, utp->chip->name, strlen(utp->chip->name) + 1));
1145}
1146
1147/*
1148 * Initialize the state. This is called from the drivers attach
1149 * function. The mutex must be already initialized.
1150 */
1151int
1152utopia_attach(struct utopia *utp, struct ifatm *ifatm, struct ifmedia *media,
1153    struct mtx *lock, struct sysctl_ctx_list *ctx,
1154    struct sysctl_oid_list *children, const struct utopia_methods *m)
1155{
1156
1157	bzero(utp, sizeof(*utp));
1158	utp->ifatm = ifatm;
1159	utp->methods = m;
1160	utp->media = media;
1161	utp->lock = lock;
1162	utp->chip = &chip_unknown;
1163
1164	ifmedia_init(media,
1165	    IFM_ATM_SDH | IFM_ATM_UNASSIGNED | IFM_ATM_NOSCRAMB,
1166	    utopia_media_change, utopia_media_status);
1167
1168	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_regs",
1169	    CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_regs, "S",
1170	    "phy registers") == NULL)
1171		return (-1);
1172
1173	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_loopback",
1174	    CTLFLAG_RW | CTLTYPE_UINT, utp, 0, utopia_sysctl_loopback, "IU",
1175	    "phy loopback mode") == NULL)
1176		return (-1);
1177
1178	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_type",
1179	    CTLFLAG_RD | CTLTYPE_UINT, utp, 0, utopia_sysctl_type, "IU",
1180	    "phy type") == NULL)
1181		return (-1);
1182
1183	if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_name",
1184	    CTLFLAG_RD | CTLTYPE_STRING, utp, 0, utopia_sysctl_name, "A",
1185	    "phy name") == NULL)
1186		return (-1);
1187
1188	UTP_WLOCK_LIST();
1189	LIST_INSERT_HEAD(&utopia_list, utp, link);
1190	UTP_WUNLOCK_LIST();
1191
1192	utp->state |= UTP_ST_ATTACHED;
1193	return (0);
1194}
1195
1196/*
1197 * Detach. We set a flag here, wakeup the daemon and let him do it.
1198 * Here we need the lock for synchronisation with the daemon.
1199 */
1200void
1201utopia_detach(struct utopia *utp)
1202{
1203
1204	UTP_LOCK_ASSERT(utp);
1205	if (utp->state & UTP_ST_ATTACHED) {
1206		utp->state |= UTP_ST_DETACH;
1207		while (utp->state & UTP_ST_DETACH) {
1208			wakeup(&utopia_list);
1209			msleep(utp, utp->lock, PZERO, "utopia_detach", hz);
1210		}
1211	}
1212}
1213
1214/*
1215 * The carrier state kernel proc for those adapters that do not interrupt.
1216 *
1217 * We assume, that utopia_attach can safely add a new utopia while we are going
1218 * through the list without disturbing us (we lock the list while getting
1219 * the address of the first element, adding is always done at the head).
1220 * Removing is entirely handled here.
1221 */
1222static void
1223utopia_daemon(void *arg __unused)
1224{
1225	struct utopia *utp, *next;
1226
1227	UTP_RLOCK_LIST();
1228	while (utopia_kproc != NULL) {
1229		utp = LIST_FIRST(&utopia_list);
1230		UTP_RUNLOCK_LIST();
1231
1232		while (utp != NULL) {
1233			mtx_lock(&Giant);	/* XXX depend on MPSAFE */
1234			UTP_LOCK(utp);
1235			next = LIST_NEXT(utp, link);
1236			if (utp->state & UTP_ST_DETACH) {
1237				LIST_REMOVE(utp, link);
1238				utp->state &= ~UTP_ST_DETACH;
1239				wakeup_one(utp);
1240			} else if ((utp->state & UTP_ST_ACTIVE) &&
1241			    (utp->flags & UTP_FL_POLL_CARRIER)) {
1242				utopia_update_carrier(utp);
1243			}
1244			UTP_UNLOCK(utp);
1245			mtx_unlock(&Giant);	/* XXX depend on MPSAFE */
1246			utp = next;
1247		}
1248
1249		UTP_RLOCK_LIST();
1250		msleep(&utopia_list, &utopia_list_mtx, PZERO, "utopia", hz);
1251	}
1252	wakeup_one(&utopia_list);
1253	UTP_RUNLOCK_LIST();
1254	mtx_lock(&Giant);
1255	kthread_exit(0);
1256}
1257
1258/*
1259 * Module initialisation
1260 */
1261static int
1262utopia_mod_init(module_t mod, int what, void *arg)
1263{
1264	int err;
1265	struct proc *kp;
1266
1267	switch (what) {
1268
1269	  case MOD_LOAD:
1270		mtx_init(&utopia_list_mtx, "utopia list mutex", NULL, MTX_DEF);
1271		err = kthread_create(utopia_daemon, NULL, &utopia_kproc,
1272		    RFHIGHPID, 0, "utopia");
1273		if (err != 0) {
1274			printf("cannot created utopia thread %d\n", err);
1275			return (err);
1276		}
1277		break;
1278
1279	  case MOD_UNLOAD:
1280		UTP_WLOCK_LIST();
1281		if ((kp = utopia_kproc) != NULL) {
1282			utopia_kproc = NULL;
1283			wakeup_one(&utopia_list);
1284			PROC_LOCK(kp);
1285			UTP_WUNLOCK_LIST();
1286			msleep(kp, &kp->p_mtx, PWAIT, "utopia_destroy", 0);
1287			PROC_UNLOCK(kp);
1288		} else
1289			UTP_WUNLOCK_LIST();
1290		mtx_destroy(&utopia_list_mtx);
1291		break;
1292	}
1293	return (0);
1294}
1295
1296static moduledata_t utopia_mod = {
1297        "utopia",
1298        utopia_mod_init,
1299        0
1300};
1301
1302DECLARE_MODULE(utopia, utopia_mod, SI_SUB_INIT_IF, SI_ORDER_ANY);
1303MODULE_VERSION(utopia, 1);
1304