diff options
Diffstat (limited to 'sys')
67 files changed, 14400 insertions, 224 deletions
diff --git a/sys/arch/mips64/conf/files.mips64 b/sys/arch/mips64/conf/files.mips64 index def002b4c53..9383b21aadd 100644 --- a/sys/arch/mips64/conf/files.mips64 +++ b/sys/arch/mips64/conf/files.mips64 @@ -1,4 +1,4 @@ -# $OpenBSD: files.mips64,v 1.17 2010/10/24 15:40:03 miod Exp $ +# $OpenBSD: files.mips64,v 1.18 2012/03/28 20:44:23 miod Exp $ file arch/mips64/mips64/arcbios.c arcbios file arch/mips64/mips64/clock.c @@ -15,6 +15,7 @@ file arch/mips64/mips64/trap.c file arch/mips64/mips64/vm_machdep.c file arch/mips64/mips64/cache_loongson2.S cpu_loongson2 +file arch/mips64/mips64/cache_r4k.c cpu_r4000 file arch/mips64/mips64/cache_r5k.S cpu_r5000 | cpu_rm7000 file arch/mips64/mips64/cache_r10k.S cpu_r10000 file arch/mips64/mips64/cache_octeon.c cpu_octeon diff --git a/sys/arch/mips64/include/arcbios.h b/sys/arch/mips64/include/arcbios.h index 51e6f00b014..112bbaf2f2c 100644 --- a/sys/arch/mips64/include/arcbios.h +++ b/sys/arch/mips64/include/arcbios.h @@ -1,4 +1,4 @@ -/* $OpenBSD: arcbios.h,v 1.18 2011/06/05 20:58:46 miod Exp $ */ +/* $OpenBSD: arcbios.h,v 1.19 2012/03/28 20:44:23 miod Exp $ */ /*- * Copyright (c) 1996 M. Warner Losh. All rights reserved. * @@ -419,6 +419,7 @@ typedef struct arc_param_blk_64 #define ARCBIOS_PAGE_SIZE 4096 extern int bios_is_32bit; +extern int bios_consrate; extern char bios_enaddr[20]; extern char bios_console[30]; extern char bios_graphics[6]; diff --git a/sys/arch/mips64/include/cpu.h b/sys/arch/mips64/include/cpu.h index 5a079b54e11..59b0760382b 100644 --- a/sys/arch/mips64/include/cpu.h +++ b/sys/arch/mips64/include/cpu.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.h,v 1.77 2012/03/25 13:52:52 miod Exp $ */ +/* $OpenBSD: cpu.h,v 1.78 2012/03/28 20:44:23 miod Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -560,6 +560,7 @@ uint32_t cp0_get_config_2(void); uint32_t cp0_get_config_3(void); uint32_t cp0_get_prid(void); void cp0_set_compare(u_int); +void cp0_set_config(uint32_t); u_int cp1_get_prid(void); void tlb_set_page_mask(uint32_t); void tlb_set_pid(int); @@ -580,6 +581,8 @@ void save_fpu(void); int fpe_branch_emulate(struct proc *, struct trap_frame *, uint32_t, vaddr_t); +int guarded_read_1(paddr_t, uint8_t *); +int guarded_read_2(paddr_t, uint16_t *); int guarded_read_4(paddr_t, uint32_t *); int guarded_write_4(paddr_t, uint32_t); diff --git a/sys/arch/mips64/mips64/arcbios.c b/sys/arch/mips64/mips64/arcbios.c index e0704b8a504..dac9405f891 100644 --- a/sys/arch/mips64/mips64/arcbios.c +++ b/sys/arch/mips64/mips64/arcbios.c @@ -1,4 +1,4 @@ -/* $OpenBSD: arcbios.c,v 1.31 2012/03/24 20:11:28 miod Exp $ */ +/* $OpenBSD: arcbios.c,v 1.32 2012/03/28 20:44:23 miod Exp $ */ /*- * Copyright (c) 1996 M. Warner Losh. All rights reserved. * Copyright (c) 1996-2004 Opsycon AB. All rights reserved. @@ -50,6 +50,7 @@ int bios_is_32bit; */ char bios_enaddr[20] = "ff:ff:ff:ff:ff:ff"; +int bios_consrate; /* Serial console speed. */ char bios_console[30]; /* Primary console. */ char bios_graphics[6]; /* Graphics state. */ char bios_keyboard[6]; /* Keyboard layout. */ diff --git a/sys/arch/mips64/mips64/cache_r4k.c b/sys/arch/mips64/mips64/cache_r4k.c new file mode 100644 index 00000000000..5d8aec81215 --- /dev/null +++ b/sys/arch/mips64/mips64/cache_r4k.c @@ -0,0 +1,323 @@ +/* $OpenBSD: cache_r4k.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ + +/* + * Copyright (c) 2012 Miodrag Vallat. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> + +#include <mips64/cache.h> +#include <machine/cpu.h> + +#define IndexInvalidate_I 0x00 +#define IndexWBInvalidate_D 0x01 +#define IndexWBInvalidate_S 0x03 + +#define HitInvalidate_D 0x11 +#define HitInvalidate_S 0x13 + +#define HitWBInvalidate_D 0x15 +#define HitWBInvalidate_S 0x17 + +#define cache(op,addr) \ + __asm__ __volatile__ ("cache %0, 0(%1)" :: "i"(op), "r"(addr) : "memory") +#define sync() \ + __asm__ __volatile__ ("sync" ::: "memory"); + +void +Mips4k_ConfigCache(struct cpu_info *ci) +{ + uint32_t cfg; + + cfg = cp0_get_config(); + + if (cfg & (1 << 5)) /* IB */ + ci->ci_l1instcacheline = 32; + else + ci->ci_l1instcacheline = 16; + ci->ci_l1instcachesize = (1 << 12) << ((cfg >> 9) & 0x07); /* IC */ + + if (cfg & (1 << 4)) /* DB */ + ci->ci_l1datacacheline = 32; + else + ci->ci_l1datacacheline = 16; + ci->ci_l1datacachesize = (1 << 12) << ((cfg >> 6) & 0x07); /* DC */ + + /* R4000 and R4400 L1 caches are direct */ + ci->ci_cacheways = 1; + ci->ci_l1instcacheset = ci->ci_l1instcachesize; + ci->ci_l1datacacheset = ci->ci_l1datacachesize; + + CpuCacheAliasMask = (ci->ci_l1instcachesize | ci->ci_l1datacachesize) & + ~PAGE_MASK; + + if ((cfg & (1 << 17)) == 0) { /* SC */ + /* + * We expect the setup code to have set up ci->ci_l2size for + * us. Unfortunately we aren't allowed to panic() there, + * because the console is not available. + */ + + /* fixed 32KB aliasing to avoid VCE */ + CpuCacheAliasMask |= ((1 << 15) - 1) & ~PAGE_MASK; + } else { + ci->ci_cacheconfiguration = 0; + ci->ci_l2size = 0; + } + ci->ci_l3size = 0; + + if (CpuCacheAliasMask != 0) + CpuCacheAliasMask |= PAGE_MASK; + + if ((cfg & 7) != CCA_CACHED) { + void (*fn)(uint32_t); + vaddr_t va; + paddr_t pa; + + va = (vaddr_t)&cp0_set_config; + if (IS_XKPHYS(va)) { + pa = XKPHYS_TO_PHYS(va); + va = PHYS_TO_XKPHYS(pa, CCA_NC); + } else { + pa = CKSEG0_TO_PHYS(va); + va = PHYS_TO_CKSEG1(pa); + } + fn = (void (*)(uint32_t))va; + + cfg = (cfg & ~7) | CCA_CACHED; + (*fn)(cfg); + } +} + +/* + * Writeback and invalidate all caches. + */ +void +Mips4k_SyncCache(struct cpu_info *ci) +{ + vaddr_t sva, eva; + uint64_t line; + + sva = PHYS_TO_XKPHYS(0, CCA_CACHED); + eva = sva + ci->ci_l1instcachesize; + line = ci->ci_l1instcacheline; + while (sva != eva) { + cache(IndexInvalidate_I, sva); + sva += line; + } + + sva = PHYS_TO_XKPHYS(0, CCA_CACHED); + eva = sva + ci->ci_l1datacachesize; + line = ci->ci_l1datacacheline; + while (sva != eva) { + cache(IndexWBInvalidate_D, sva); + sva += line; + } + + if (ci->ci_l2size != 0) { + sva = PHYS_TO_XKPHYS(0, CCA_CACHED); + eva = sva + ci->ci_l2size; + line = ci->ci_cacheconfiguration; /* L2 line size */ + while (sva != eva) { + cache(IndexWBInvalidate_S, sva); + sva += line; + } + } + + sync(); +} + +/* + * Invalidate I$ for the given range. + */ +void +Mips4k_InvalidateICache(struct cpu_info *ci, uint64_t _va, size_t _sz) +{ + vaddr_t va, sva, eva; + vsize_t sz; + uint64_t line; + + line = ci->ci_l1instcacheline; + /* extend the range to integral cache lines */ + if (line == 16) { + va = _va & ~(16UL - 1); + sz = ((_va + _sz + 16 - 1) & ~(16UL - 1)) - _va; + } else { + va = _va & ~(32UL - 1); + sz = ((_va + _sz + 32 - 1) & ~(32UL - 1)) - _va; + } + + sva = PHYS_TO_XKPHYS(0, CCA_CACHED); + /* keep only the index bits */ + sva += va & ((1UL << 15) - 1); + eva = sva + sz; + while (sva != eva) { + cache(IndexInvalidate_I, sva); + sva += line; + } + + sync(); +} + +/* + * Writeback D$ for the given page. + */ +void +Mips4k_SyncDCachePage(struct cpu_info *ci, uint64_t va) +{ + vaddr_t sva, eva; + uint64_t line; + + line = ci->ci_l1datacacheline; + sva = PHYS_TO_XKPHYS(0, CCA_CACHED); + /* keep only the index bits */ + sva += va & ((1UL << 15) - 1); + eva = sva + PAGE_SIZE; + while (sva != eva) { + cache(IndexWBInvalidate_D, sva); + sva += line; + } + + if (ci->ci_l2size != 0) { + line = ci->ci_cacheconfiguration; /* L2 line size */ + sva = PHYS_TO_XKPHYS(0, CCA_CACHED); + /* keep only the index bits */ + sva += va & ((1UL << 22) - 1); /* largest L2 is 4MB */ + eva = sva + PAGE_SIZE; + while (sva != eva) { + cache(IndexWBInvalidate_S, sva); + sva += line; + } + } + + sync(); +} + +/* + * Writeback D$ for the given range. Range is expected to be currently + * mapped, allowing the use of `Hit' operations. This is less aggressive + * than using `Index' operations. + */ +void +Mips4k_HitSyncDCache(struct cpu_info *ci, uint64_t _va, size_t _sz) +{ + vaddr_t va, sva, eva; + vsize_t sz; + uint64_t line; + + line = ci->ci_l1datacacheline; + /* extend the range to integral cache lines */ + if (line == 16) { + va = _va & ~(16UL - 1); + sz = ((_va + _sz + 16 - 1) & ~(16UL - 1)) - va; + } else { + va = _va & ~(32UL - 1); + sz = ((_va + _sz + 32 - 1) & ~(32UL - 1)) - va; + } + + sva = va; + eva = sva + sz; + while (sva != eva) { + cache(HitWBInvalidate_D, sva); + sva += line; + } + + if (ci->ci_l2size != 0) { + line = ci->ci_cacheconfiguration; /* L2 line size */ + /* extend the range to integral cache lines */ + va = _va & ~(line - 1); + sz = ((_va + _sz + line - 1) & ~(line - 1)) - va; + + sva = va; + eva = sva + sz; + while (sva != eva) { + cache(HitWBInvalidate_S, sva); + sva += line; + } + } + + sync(); +} + +/* + * Invalidate D$ for the given range. Range is expected to be currently + * mapped, allowing the use of `Hit' operations. This is less aggressive + * than using `Index' operations. + */ +void +Mips4k_HitInvalidateDCache(struct cpu_info *ci, uint64_t _va, size_t _sz) +{ + vaddr_t va, sva, eva; + vsize_t sz; + uint64_t line; + + line = ci->ci_l1datacacheline; + /* extend the range to integral cache lines */ + if (line == 16) { + va = _va & ~(16UL - 1); + sz = ((_va + _sz + 16 - 1) & ~(16UL - 1)) - _va; + } else { + va = _va & ~(32UL - 1); + sz = ((_va + _sz + 32 - 1) & ~(32UL - 1)) - _va; + } + + sva = va; + eva = sva + sz; + while (sva != eva) { + cache(HitInvalidate_D, sva); + sva += line; + } + + if (ci->ci_l2size != 0) { + line = ci->ci_cacheconfiguration; /* L2 line size */ + /* extend the range to integral cache lines */ + va = _va & ~(line - 1); + sz = ((_va + _sz + line - 1) & ~(line - 1)) - va; + + sva = va; + eva = sva + sz; + while (sva != eva) { + cache(HitInvalidate_S, sva); + sva += line; + } + } + + sync(); +} + +/* + * Backend for bus_dmamap_sync(). Enforce coherency of the given range + * by performing the necessary cache writeback and/or invalidate + * operations. + */ +void +Mips4k_IOSyncDCache(struct cpu_info *ci, uint64_t va, size_t sz, int how) +{ + switch (how) { + case CACHE_SYNC_R: + if (((va | sz) & (ci->ci_l1datacacheline - 1)) == 0) { + Mips4k_HitInvalidateDCache(ci, va, sz); + break; + } + /* FALLTHROUGH */ + case CACHE_SYNC_X: + Mips4k_HitSyncDCache(ci, va, sz); + break; + case CACHE_SYNC_W: + Mips4k_HitSyncDCache(ci, va, sz); + break; + } +} diff --git a/sys/arch/mips64/mips64/cp0access.S b/sys/arch/mips64/mips64/cp0access.S index 32866dce285..7bb04346f0c 100644 --- a/sys/arch/mips64/mips64/cp0access.S +++ b/sys/arch/mips64/mips64/cp0access.S @@ -1,4 +1,4 @@ -/* $OpenBSD: cp0access.S,v 1.14 2010/09/11 11:29:50 syuu Exp $ */ +/* $OpenBSD: cp0access.S,v 1.15 2012/03/28 20:44:23 miod Exp $ */ /* * Copyright (c) 2001-2003 Opsycon AB (www.opsycon.se / www.opsycon.com) @@ -154,6 +154,13 @@ LEAF(cp0_get_config, 0) nop END(cp0_get_config) +/* WARNING! Needs to be invoked from uncached address. */ +LEAF(cp0_set_config, 0) + mtc0 a0, COP_0_CONFIG + j ra + nop +END(cp0_set_config) + LEAF(cp0_get_prid, 0) mfc0 v0, COP_0_PRID j ra diff --git a/sys/arch/mips64/mips64/cpu.c b/sys/arch/mips64/mips64/cpu.c index 37240e67095..2a85adba26e 100644 --- a/sys/arch/mips64/mips64/cpu.c +++ b/sys/arch/mips64/mips64/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.39 2011/04/10 17:16:51 miod Exp $ */ +/* $OpenBSD: cpu.c,v 1.40 2012/03/28 20:44:23 miod Exp $ */ /* * Copyright (c) 1997-2004 Opsycon AB (www.opsycon.se) @@ -112,14 +112,18 @@ cpuattach(struct device *parent, struct device *dev, void *aux) printf(": "); displayver = 1; + fptype = (ch->c1prid >> 8) & 0xff; vers_maj = (ch->c0prid >> 4) & 0x0f; vers_min = ch->c0prid & 0x0f; switch (ch->type) { case MIPS_R4000: - if (ci->ci_l1instcachesize == 16384) - printf("MIPS R4400 CPU"); - else + if (vers_maj < 4) printf("MIPS R4000 CPU"); + else { + vers_maj -= 3; + printf("MIPS R4400 CPU"); + } + fptype = MIPS_R4000; break; case MIPS_R5000: printf("MIPS R5000 CPU"); @@ -184,6 +188,7 @@ cpuattach(struct device *parent, struct device *dev, void *aux) break; case MIPS_OCTEON: printf("Cavium OCTEON CPU"); + fptype = MIPS_SOFT; break; default: printf("Unknown CPU type (0x%x)", ch->type); @@ -194,13 +199,8 @@ cpuattach(struct device *parent, struct device *dev, void *aux) printf(" %d MHz, ", ch->clock / 1000000); displayver = 1; - if (ch->type == MIPS_OCTEON) - fptype = MIPS_SOFT; - else { - fptype = (ch->c1prid >> 8) & 0xff; - vers_maj = (ch->c1prid >> 4) & 0x0f; - vers_min = ch->c1prid & 0x0f; - } + vers_maj = (ch->c1prid >> 4) & 0x0f; + vers_min = ch->c1prid & 0x0f; switch (fptype) { case MIPS_SOFT: printf("Software FP emulation"); @@ -267,7 +267,7 @@ cpuattach(struct device *parent, struct device *dev, void *aux) printf("4 way"); break; default: - printf("1 way"); + printf("direct"); break; } @@ -293,8 +293,10 @@ cpuattach(struct device *parent, struct device *dev, void *aux) printf("\n"); #ifdef DEBUG - printf("cpu%d: Setsize %d:%d\n", cpuno, + printf("cpu%d: L1 set size %d:%d\n", cpuno, ci->ci_l1instcacheset, ci->ci_l1datacacheset); + printf("cpu%d: L1 line size %d:%d\n", cpuno, + ci->ci_l1instcacheline, ci->ci_l1datacacheline); printf("cpu%d: Alias mask %p\n", cpuno, CpuCacheAliasMask); printf("cpu%d: Config Register %08x\n", cpuno, cp0_get_config()); printf("cpu%d: Cache configuration %x\n", diff --git a/sys/arch/mips64/mips64/exception.S b/sys/arch/mips64/mips64/exception.S index a02a560136b..2626cf95a77 100644 --- a/sys/arch/mips64/mips64/exception.S +++ b/sys/arch/mips64/mips64/exception.S @@ -1,4 +1,4 @@ -/* $OpenBSD: exception.S,v 1.32 2010/12/28 18:40:28 miod Exp $ */ +/* $OpenBSD: exception.S,v 1.33 2012/03/28 20:44:23 miod Exp $ */ /* * Copyright (c) 2002-2003 Opsycon AB (www.opsycon.se / www.opsycon.com) @@ -129,6 +129,11 @@ u_exception_table: * to the vector area and must thus be PIC and less than 128 * bytes long to fit. Only k0 and k1 may be used at this time. */ + .globl cache_err +cache_err: +#ifdef CPU_R4000 + nop +#endif .globl exception exception: .set noat diff --git a/sys/arch/mips64/mips64/lcore_access.S b/sys/arch/mips64/mips64/lcore_access.S index 4975d28094e..c48b44ef6fa 100644 --- a/sys/arch/mips64/mips64/lcore_access.S +++ b/sys/arch/mips64/mips64/lcore_access.S @@ -1,4 +1,4 @@ -/* $OpenBSD: lcore_access.S,v 1.19 2010/01/31 19:39:04 miod Exp $ */ +/* $OpenBSD: lcore_access.S,v 1.20 2012/03/28 20:44:23 miod Exp $ */ /* * Copyright (c) 2001-2003 Opsycon AB (www.opsycon.se / www.opsycon.com) @@ -286,10 +286,42 @@ _kcopyerr: /* * Guarded ``memory'' access routines + * int guarded_read_1(paddr_t address, uint8_t *dest); + * int guarded_read_2(paddr_t address, uint16_t *dest); * int guarded_read_4(paddr_t address, uint32_t *dest); * int guarded_write_4(paddr_t address, uint32_t src); */ +LEAF(guarded_read_1, 0) + GET_CPU_INFO(t1, t0) + PTR_L t3, CI_CURPROCPADDR(t1) + li v0, KT_GUARDERR + lw v1, PCB_ONFAULT(t3) + sw v0, PCB_ONFAULT(t3) + + lb v0, 0(a0) + sb v0, 0(a1) + + sw v1, PCB_ONFAULT(t3) + j ra + move v0, zero +END(guarded_read_1) + +LEAF(guarded_read_2, 0) + GET_CPU_INFO(t1, t0) + PTR_L t3, CI_CURPROCPADDR(t1) + li v0, KT_GUARDERR + lw v1, PCB_ONFAULT(t3) + sw v0, PCB_ONFAULT(t3) + + lh v0, 0(a0) + sh v0, 0(a1) + + sw v1, PCB_ONFAULT(t3) + j ra + move v0, zero +END(guarded_read_2) + LEAF(guarded_read_4, 0) GET_CPU_INFO(t1, t0) PTR_L t3, CI_CURPROCPADDR(t1) diff --git a/sys/arch/mips64/mips64/tlbhandler.S b/sys/arch/mips64/mips64/tlbhandler.S index d96e343d765..d78fe8cebc6 100644 --- a/sys/arch/mips64/mips64/tlbhandler.S +++ b/sys/arch/mips64/mips64/tlbhandler.S @@ -1,4 +1,4 @@ -/* $OpenBSD: tlbhandler.S,v 1.32 2010/09/12 12:05:37 syuu Exp $ */ +/* $OpenBSD: tlbhandler.S,v 1.33 2012/03/28 20:44:23 miod Exp $ */ /* * Copyright (c) 1995-2004 Opsycon AB (www.opsycon.se / www.opsycon.com) @@ -54,6 +54,22 @@ */ .set noat +#ifdef CPU_R4000 + .globl xtlb_miss_err_r4k + .ent xtlb_miss_err_r4k, 0 +xtlb_miss_err_r4k: + /* + * R4000 errata: TLB miss exception may be invoked with BadVAddr + * being incorrect. + */ + tlbp + mfc0 k1, COP_0_TLB_INDEX + bltz k1, xtlb_miss # missing! + nop + eret + nop + .end xtlb_miss_err_r4k +#endif /* CPU_R4000 */ #if defined(CPU_R5000) || defined(CPU_RM7000) .globl xtlb_miss_err_r5k .ent xtlb_miss_err_r5k, 0 diff --git a/sys/arch/mips64/mips64/trap.c b/sys/arch/mips64/mips64/trap.c index d906010b49a..73aa524bcf7 100644 --- a/sys/arch/mips64/mips64/trap.c +++ b/sys/arch/mips64/mips64/trap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: trap.c,v 1.77 2011/11/16 20:50:19 deraadt Exp $ */ +/* $OpenBSD: trap.c,v 1.78 2012/03/28 20:44:23 miod Exp $ */ /* * Copyright (c) 1988 University of Utah. @@ -763,6 +763,36 @@ printf("SIG-BUSB @%p pc %p, ra %p\n", trapframe->badvaddr, trapframe->pc, trapfr } goto err; +#ifdef CPU_R4000 + case T_VCEI: + case T_VCEI+T_USER: + { + vaddr_t va = trapframe->badvaddr & + ~((vaddr_t)ci->ci_l1instcacheline - 1); +#ifdef DEBUG + printf("VCEI trap, badvaddr %p\n", trapframe->badvaddr); +#endif + /* HitWBInvalidate_S */ + __asm__ __volatile__ ("cache 0x17, 0(%0)" :: "r"(va)); + /* HitInvalidate_I */ + __asm__ __volatile__ ("cache 0x10, 0(%0)" :: "r"(va)); + } + return; + case T_VCED: + case T_VCED+T_USER: + { + vaddr_t va = trapframe->badvaddr & + ~((vaddr_t)ci->ci_l1datacacheline - 1); +#ifdef DEBUG + printf("VCED trap, badvaddr %p\n", trapframe->badvaddr); +#endif + /* HitWBInvalidate_S */ + __asm__ __volatile__ ("cache 0x17, 0(%0)" :: "r"(va)); + /* HitInvalidate_D */ + __asm__ __volatile__ ("cache 0x11, 0(%0)" :: "r"(va)); + } + return; +#endif /* CPU_R4000 */ default: err: disableintr(); diff --git a/sys/arch/sgi/compile/.cvsignore b/sys/arch/sgi/compile/.cvsignore index 8a0ebe82e75..db6b8d0fedd 100644 --- a/sys/arch/sgi/compile/.cvsignore +++ b/sys/arch/sgi/compile/.cvsignore @@ -1,7 +1,10 @@ +GENERIC-IP22 GENERIC-IP27 +GENERIC-IP27.MP GENERIC-IP30 GENERIC-IP30.MP GENERIC-IP32 +RAMDISK-IP22 RAMDISK-IP27 RAMDISK-IP30 RAMDISK-IP32 diff --git a/sys/arch/sgi/conf/GENERIC-IP22 b/sys/arch/sgi/conf/GENERIC-IP22 new file mode 100644 index 00000000000..c062b6c4cb4 --- /dev/null +++ b/sys/arch/sgi/conf/GENERIC-IP22 @@ -0,0 +1,85 @@ +# $OpenBSD: GENERIC-IP22,v 1.1 2012/03/28 20:44:23 miod Exp $ +# +# THIS KERNEL IS FOR INDIGO (IP20), INDY (IP22) AND INDIGO2 (IP24) SYSTEMS ONLY. +# +# For further information on compiling OpenBSD kernels, see the config(8) +# man page. +# +# For further information on hardware support for this architecture, see +# the intro(4) man page. For further information about kernel options +# for this architecture, see the options(4) man page. For an explanation +# of each device driver in this file see the section 4 man page for the +# device. + +machine sgi mips64 +include "../../../conf/GENERIC" +maxusers 32 # Estimated number of users + +# Make options +makeoption LINK_ADDRESS="0xffffffff88100000" +# Force use of 16KB pages. The R5000 Indy, which has the infamous XKPHYS +# coherency bug wrt ll/sc instructions, can not have more than 256MB of +# physical memory, all of it fitting within CKSEG0. +option PAGE_SHIFT="14" + +#option WSDISPLAY_COMPAT_RAWKBD # Provide raw scancodes; needed for X11 + +# Define what targets to support +option TGT_INDIGO # R4x00 Indigo (IP20) +option TGT_INDIGO2 # Indigo2 (IP22/IP26/IP28) +option TGT_INDY # Indy (IP24) +option ARCBIOS # mandatory +option CPU_R4000 # R4000/R4400 support (IP20/IP22/IP24) +#option CPU_R4600 # R4600 support (IP22/IP24) +option CPU_R5000 # R5000 support (IP24) + +config bsd swap generic + +# +# Definition of system +# +mainbus0 at root +cpu* at mainbus0 +clock0 at mainbus0 + +int0 at mainbus0 # Interrupt Controller +imc0 at mainbus0 # Memory Controller +gio0 at imc0 +#eisa0 at imc0 + +hpc0 at gio? addr 0x1fb80000 +hpc1 at gio? addr 0x1fb00000 +hpc2 at gio? addr 0x1f980000 + +dpclock0 at hpc0 # IP20 +dsclock0 at hpc0 # IP22/24 +sq* at hpc? # On-board Ethernet or E++ adapter +wdsc* at hpc? # On-board SCSI or GIO32 SCSI adapter +#haltwo* at hpc? # Indy/Indigo2 Audio +#panel* at hpc? # Indy front panel buttons +#pckbc* at hpc? # Indy/Indigo2 keyboard and mouse + +zs* at hpc? +zstty* at zs? # Serial ports + +#newport* at gio? # Indy Newport graphics +#wsdisplay* at newport? + +#grtwo* at gio? # Express (GR2) graphics +#wsdisplay* at grtwo? + +#light* at gio? # Light/Starter/Entry (LG1/LG2) graphics +#wsdisplay* at light? + +scsibus* at scsi? +sd* at scsibus? +st* at scsibus? +cd* at scsibus? +ch* at scsibus? +safte* at scsibus? +ses* at scsibus? +uk* at scsibus? + +pseudo-device crypto 1 +#pseudo-device hotplug 1 # devices hot plugging +#pseudo-device wsmux 2 # Mouse and keyboard multiplexor diff --git a/sys/arch/sgi/conf/RAMDISK-IP22 b/sys/arch/sgi/conf/RAMDISK-IP22 new file mode 100644 index 00000000000..91e2e00f390 --- /dev/null +++ b/sys/arch/sgi/conf/RAMDISK-IP22 @@ -0,0 +1,95 @@ +# $OpenBSD: RAMDISK-IP22,v 1.1 2012/03/28 20:44:23 miod Exp $ +# +# THIS KERNEL IS FOR INDIGO (IP20), INDY (IP22) AND INDIGO2 (IP24) SYSTEMS ONLY. + +machine sgi mips64 + +maxusers 4 +option TIMEZONE=0 # minutes west of GMT (for) +option DST=0 # use daylight savings rules + +# Make options +makeoption LINK_ADDRESS="0xffffffff88100000" +# Force use of 16KB pages. The R5000 Indy, which has the infamous XKPHYS +# coherency bug wrt ll/sc instructions, can not have more than 256MB of +# physical memory, all of it fitting within CKSEG0. +option PAGE_SHIFT="14" + +option DDB + +# Filesystem options +option CD9660 # ISO 9660 + Rock Ridge file system +option FIFO # POSIX fifo support (in all filesystems) +option FFS # fast filesystem +#option MSDOSFS # Ability to read write MS-Dos filesystem +option NFSCLIENT # Sun NFS-compatible filesystem (client) + +# Networking options +option INET # IP + ICMP + TCP + UDP +option INET6 # IPv6 (needs INET) + +# RAMDISK stuff +option MINIROOTSIZE=10240 +option RAMDISK_HOOKS + +# Define what targets to support +option TGT_INDIGO # R4x00 Indigo (IP20) +option TGT_INDIGO2 # Indigo2 (IP22/IP26/IP28) +option TGT_INDY # Indy (IP24) +option ARCBIOS # mandatory +option CPU_R4000 # R4000/R4400 support (IP20/IP22/IP24) +#option CPU_R4600 # R4600 support (IP22/IP24) +option CPU_R5000 # R5000 support (IP24) + +# Specify storage configuration using ramdisk +config bsd root on rd0a swap on rd0b + +# +# Definition of system +# +mainbus0 at root +cpu* at mainbus0 +clock0 at mainbus0 + +int0 at mainbus0 # Interrupt Controller +imc0 at mainbus0 # Memory Controller +gio0 at imc0 +#eisa0 at imc0 + +hpc0 at gio? addr 0x1fb80000 +hpc1 at gio? addr 0x1fb00000 +hpc2 at gio? addr 0x1f980000 + +dpclock0 at hpc0 # IP20 +dsclock0 at hpc0 # IP22/24 +sq* at hpc? # On-board Ethernet or E++ adapter +wdsc* at hpc? # On-board SCSI or GIO32 SCSI adapter +#pckbc* at hpc? # Indy/Indigo2 keyboard and mouse + +zs* at hpc? +zstty* at zs? # Serial ports + +#newport* at gio? # Indy Newport graphics +#wsdisplay* at newport? + +#grtwo* at gio? # Express (GR2) graphics +#wsdisplay* at grtwo? + +#light* at gio? # Light/Starter/Entry (LG1/LG2) graphics +#wsdisplay* at light? + +scsibus* at scsi? +sd* at scsibus? +st* at scsibus? +cd* at scsibus? +#ch* at scsibus? +#uk* at scsibus? + +#### PSEUDO Devices +pseudo-device loop 1 # network loopback +pseudo-device bpfilter 1 # packet filter ports + +pseudo-device rd 1 # Ram disk. +pseudo-device bio 1 # ioctl multiplexing device + +option BOOT_CONFIG # add support for boot -c diff --git a/sys/arch/sgi/conf/files.sgi b/sys/arch/sgi/conf/files.sgi index 83953010ed9..a13ff36f3c7 100644 --- a/sys/arch/sgi/conf/files.sgi +++ b/sys/arch/sgi/conf/files.sgi @@ -1,4 +1,4 @@ -# $OpenBSD: files.sgi,v 1.46 2011/11/15 22:27:53 deraadt Exp $ +# $OpenBSD: files.sgi,v 1.47 2012/03/28 20:44:23 miod Exp $ # # maxpartitions must be first item in files.${ARCH} # @@ -14,6 +14,8 @@ file arch/sgi/sgi/bus_dma.c file arch/sgi/sgi/conf.c file arch/sgi/sgi/disksubr.c disk file arch/sgi/sgi/lock_machdep.c multiprocessor +file arch/sgi/sgi/ip22_machdep.c tgt_indigo | tgt_indigo2 | + tgt_indy file arch/sgi/sgi/ip27_machdep.c tgt_origin file arch/sgi/sgi/ip30_machdep.c tgt_octane file arch/sgi/sgi/ip30_nmi.S tgt_octane & ddb @@ -56,6 +58,21 @@ device clock attach clock at mainbus # +# IP20/22/24 specific devices +# +define giobus {} +device imc: giobus, eisabus +attach imc at mainbus +file arch/sgi/localbus/imc.c imc + +device int +attach int at mainbus +file arch/sgi/localbus/int.c int + +include "arch/sgi/gio/files.gio" +include "arch/sgi/hpc/files.hpc" + +# # O2 MACE localbus autoconfiguration devices # define macebus {[base = -1]} @@ -87,6 +104,13 @@ major {cd = 3} include "dev/i2o/files.i2o" # +# EISA Bus support +# + +include "dev/eisa/files.eisa" +file arch/sgi/sgi/eisa_machdep.c eisa + +# # PCI Bus support # @@ -132,9 +156,9 @@ file arch/sgi/dev/iockbc.c iockbc | # DS1687 Time-Of-Day calendar device device dsrtc -attach dsrtc at macebus with dsrtc_macebus attach dsrtc at ioc with dsrtc_ioc attach dsrtc at iof with dsrtc_iof +attach dsrtc at macebus with dsrtc_macebus file arch/sgi/dev/dsrtc.c dsrtc # GBE framebuffer @@ -207,4 +231,3 @@ file arch/sgi/dev/owmem_subr.c owmac | owserial # IP35 SPD memory information attach spdmem at mainbus with spdmem_mainbus file arch/sgi/dev/spdmem_mainbus.c spdmem_mainbus - diff --git a/sys/arch/sgi/dev/dsrtc.c b/sys/arch/sgi/dev/dsrtc.c index 7132df8e5fd..8991abfe0bf 100644 --- a/sys/arch/sgi/dev/dsrtc.c +++ b/sys/arch/sgi/dev/dsrtc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dsrtc.c,v 1.11 2009/11/07 14:49:01 miod Exp $ */ +/* $OpenBSD: dsrtc.c,v 1.12 2012/03/28 20:44:23 miod Exp $ */ /* * Copyright (c) 2001-2004 Opsycon AB (www.opsycon.se / www.opsycon.com) @@ -122,7 +122,7 @@ dsrtc_attach_ioc(struct device *parent, struct device *self, void *aux) bus_space_handle_t ih, ih2; /* - * The IOC3 RTC is either a Dallas (now Maxim) DS1386 or compatible + * The IOC3 RTC is either a Dallas (now Maxim) DS1397 or compatible * (likely a more recent DS1687), or a DS1747 or compatible * (itself being a Mostek MK48T35 clone). * diff --git a/sys/arch/sgi/gio/Makefile b/sys/arch/sgi/gio/Makefile new file mode 100644 index 00000000000..130e520dcfb --- /dev/null +++ b/sys/arch/sgi/gio/Makefile @@ -0,0 +1,8 @@ +# $OpenBSD: Makefile,v 1.1 2012/03/28 20:44:23 miod Exp $ +# $NetBSD: Makefile.giodevs,v 1.5 2008/10/19 22:05:21 apb Exp $ + +AWK= awk + +giodevs.h giodevs_data.h: giodevs devlist2h.awk + /bin/rm -f giodevs.h giodevs_data.h + ${AWK} -f devlist2h.awk giodevs diff --git a/sys/arch/sgi/gio/devlist2h.awk b/sys/arch/sgi/gio/devlist2h.awk new file mode 100644 index 00000000000..361a2eb2a25 --- /dev/null +++ b/sys/arch/sgi/gio/devlist2h.awk @@ -0,0 +1,152 @@ +#! /usr/bin/awk -f +# $OpenBSD: devlist2h.awk,v 1.1 2012/03/28 20:44:23 miod Exp $ +# $NetBSD: devlist2h.awk,v 1.5 2008/05/02 18:11:05 martin Exp $ +# +# Copyright (c) 1998 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Christos Zoulas. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# Copyright (c) 1995, 1996 Christopher G. Demetriou +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Christopher G. Demetriou. +# This product includes software developed by Christos Zoulas +# 4. The name of the author(s) may not be used to endorse or promote products +# derived from this software without specific prior written permission +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +function collectline(_f, _line) { + _oparen = 0 + _line = "" + while (_f <= NF) { + if ($_f == "#") { + _line = _line "(" + _oparen = 1 + _f++ + continue + } + if (_oparen) { + _line = _line $_f + if (_f < NF) + _line = _line " " + _f++ + continue + } + _line = _line $_f + if (_f < NF) + _line = _line " " + _f++ + } + if (_oparen) + _line = _line ")" + return _line +} +BEGIN { + nproducts = nvendors = blanklines = 0 + dfile="giodevs_data.h" + hfile="giodevs.h" + line="" +} +NR == 1 { + VERSION = $0 + gsub("\\$", "", VERSION) + + printf("/*\n") > hfile + printf(" * THIS FILE AUTOMATICALLY GENERATED. DO NOT EDIT.\n") \ + > hfile + printf(" *\n") > hfile + printf(" * generated from:\n") > hfile + printf(" *\t%s\n", VERSION) > hfile + printf(" */\n\n") > hfile + + printf("/*\n") > dfile + printf(" * THIS FILE AUTOMATICALLY GENERATED. DO NOT EDIT.\n") \ + > dfile + printf(" *\n") > dfile + printf(" * generated from:\n") > dfile + printf(" *\t%s\n", VERSION) > dfile + printf(" */\n\n") > dfile + + next +} +NF > 0 && $1 == "product" { + nproducts++ + + products[nproducts, 1] = $2; + products[nproducts, 2] = $3 + products[nproducts, 3] = collectline(4, line) + + next +} +{ + if ($0 == "") + blanklines++ + if (blanklines < 2) + print $0 > dfile +} +END { + # print out the match tables + + printf("\n") > dfile + + printf("struct gio_knowndev {\n") > dfile + printf("\tint productid;\n") > dfile + printf("\tconst char *product;\n") > dfile + printf("};\n") > dfile + printf("\nstruct gio_knowndev gio_knowndevs[] = {\n") > dfile + + printf("\n") > hfile + for (i = 1; i <= nproducts; i++) { + printf("#define %s\t%s\t/* %s */\n", products[i, 1], products[i,2], products[i, 3]) > hfile + + printf("\t{ %s, \"%s\" },\n", + products[i, 2], products[i, 3]) > dfile + } + printf("\t{ 0, NULL }\n") > dfile + printf("};\n") > dfile + close(dfile) + close(hfile) +} diff --git a/sys/arch/sgi/gio/files.gio b/sys/arch/sgi/gio/files.gio new file mode 100644 index 00000000000..fe393862625 --- /dev/null +++ b/sys/arch/sgi/gio/files.gio @@ -0,0 +1,31 @@ +# $OpenBSD: files.gio,v 1.1 2012/03/28 20:44:23 miod Exp $ +# $NetBSD: files.gio,v 1.11 2009/02/12 06:33:57 rumble Exp $ + +device gio {[slot = -1], [addr = -1]} +attach gio at giobus + +file arch/sgi/gio/gio.c gio needs-flag + +device hpc {[offset = -1]}: smc93cx6 +attach hpc at gio +file arch/sgi/hpc/hpc.c hpc + +# XL graphics +device newport: wsemuldisplaydev, rasops8 +attach newport at gio +file arch/sgi/gio/newport.c newport needs-flag + +# GR2 graphics +device grtwo: wsemuldisplaydev +attach grtwo at gio +file arch/sgi/gio/grtwo.c grtwo needs-flag + +# LG1/LG2 graphics +device light: wsemuldisplaydev +attach light at gio +file arch/sgi/gio/light.c light needs-flag + +# PCI cards glued to the GIO bus +device giopci: pcibus +attach giopci at gio +file arch/sgi/gio/pci_gio.c giopci diff --git a/sys/arch/sgi/gio/gio.c b/sys/arch/sgi/gio/gio.c new file mode 100644 index 00000000000..b201adab5b9 --- /dev/null +++ b/sys/arch/sgi/gio/gio.c @@ -0,0 +1,518 @@ +/* $OpenBSD: gio.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: gio.c,v 1.32 2011/07/01 18:53:46 dyoung Exp $ */ + +/* + * Copyright (c) 2000 Soren S. Jorvang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the + * NetBSD Project. See http://www.NetBSD.org/ for + * information about NetBSD. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <mips64/archtype.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> + +#include <sgi/gio/gioreg.h> +#include <sgi/gio/giovar.h> +#include <sgi/gio/giodevs_data.h> + +#include <sgi/localbus/imcvar.h> +#include <sgi/localbus/intvar.h> +#include <sgi/sgi/ip22.h> + +int gio_match(struct device *, void *, void *); +void gio_attach(struct device *, struct device *, void *); +int gio_print(void *, const char *); +int gio_print_fb(void *, const char *); +int gio_search(struct device *, void *, void *); +int gio_submatch(struct device *, void *, void *); +uint32_t gio_id(paddr_t, int); + +struct gio_softc { + struct device sc_dev; + + bus_space_tag_t sc_iot; + bus_dma_tag_t sc_dmat; +}; + +const struct cfattach gio_ca = { + sizeof(struct gio_softc), gio_match, gio_attach +}; + +struct cfdriver gio_cd = { + NULL, "gio", DV_DULL +}; + +struct gio_probe { + uint32_t slot; + uint64_t base; + uint32_t mach_type; + uint32_t mach_subtype; +}; + +/* + * Expansion Slot Base Addresses + * + * IP12, IP20 and IP24 have two GIO connectors: GIO_SLOT_EXP0 and + * GIO_SLOT_EXP1. + * + * On IP24 these slots exist on the graphics board or the IOPLUS + * "mezzanine" on Indy and Challenge S, respectively. The IOPLUS or + * graphics board connects to the mainboard via a single GIO64 connector. + * + * IP22 has either three or four physical connectors, but only two + * electrically distinct slots: GIO_SLOT_GFX and GIO_SLOT_EXP0. + * + * It should also be noted that DMA is (mostly) not supported in Challenge S's + * GIO_SLOT_EXP1. See gio(4) for the story. + */ +static const struct gio_probe slot_bases[] = { + { GIO_SLOT_GFX, 0x1f000000, SGI_IP22, IP22_INDIGO2 }, + + { GIO_SLOT_EXP0, 0x1f400000, SGI_IP20, -1 }, + { GIO_SLOT_EXP0, 0x1f400000, SGI_IP22, -1 }, + + { GIO_SLOT_EXP1, 0x1f600000, SGI_IP20, -1 }, + { GIO_SLOT_EXP1, 0x1f600000, SGI_IP22, IP22_INDY }, + + { 0, 0, 0, 0 } +}; + +/* + * Graphic Board Base Addresses + * + * Graphics boards are not treated like expansion slot cards. Their base + * addresses do not necessarily correspond to GIO slot addresses and they + * do not contain product identification words. + */ +static const struct gio_probe gfx_bases[] = { + /* grtwo, and newport on IP22 */ + { -1, 0x1f000000, SGI_IP20, -1 }, + { -1, 0x1f000000, SGI_IP22, -1 }, + + /* light */ + { -1, 0x1f3f0000, SGI_IP20, -1 }, + + /* light (dual headed) */ + { -1, 0x1f3f8000, SGI_IP20, -1 }, + + /* grtwo, and newport on IP22 */ + { -1, 0x1f400000, SGI_IP20, -1 }, + { -1, 0x1f400000, SGI_IP22, -1 }, + + /* grtwo */ + { -1, 0x1f600000, SGI_IP20, -1 }, + { -1, 0x1f600000, SGI_IP22, -1 }, + + /* newport */ + { -1, 0x1f800000, SGI_IP22, IP22_INDIGO2 }, + + /* newport */ + { -1, 0x1fc00000, SGI_IP22, IP22_INDIGO2 }, + + { 0, 0, 0, 0 } +}; + +/* maximum number of graphics boards possible (arbitrarily large estimate) */ +#define MAXGFX (nitems(gfx_bases) - 1) + +int +gio_match(struct device *parent, void *match, void *aux) +{ + struct imc_attach_args *iaa = aux; + + if (strcmp(iaa->iaa_name, gio_cd.cd_name) != 0) + return 0; + + return 1; +} + +void +gio_attach(struct device *parent, struct device *self, void *aux) +{ + struct gio_softc *sc = (struct gio_softc *)self; + struct imc_attach_args *iaa = (struct imc_attach_args *)aux; + struct gio_attach_args ga; + uint32_t gfx[MAXGFX]; + uint i, j, ngfx; + + printf("\n"); + + sc->sc_iot = iaa->iaa_st; + sc->sc_dmat = iaa->iaa_dmat; + + ngfx = 0; + memset(gfx, 0, sizeof(gfx)); + + /* + * Try and attach graphics devices first. + * Unfortunately, they - not being GIO devices after all - do not + * contain a Product Identification Word, nor have a slot number. + * + * Record addresses to which graphics devices attach so that + * we do not confuse them with expansion slots, should the + * addresses coincide. + * + * Unfortunately graphics devices for which we have no configured + * driver, which address matches a regular slot number, will show + * up as rogue devices attached to real slots. + * + * If only the ARCBios component tree would be so kind as to give + * us the address of the frame buffer components... + */ + for (i = 0; gfx_bases[i].base != 0; i++) { + /* skip slots that don't apply to us */ + if (gfx_bases[i].mach_type != sys_config.system_type) + continue; + + if (gfx_bases[i].mach_subtype != -1 && + gfx_bases[i].mach_subtype != sys_config.system_subtype) + continue; + + ga.ga_slot = -1; + ga.ga_addr = gfx_bases[i].base; + ga.ga_iot = sc->sc_iot; + ga.ga_ioh = PHYS_TO_XKPHYS(ga.ga_addr, CCA_NC); + ga.ga_dmat = sc->sc_dmat; + ga.ga_product = -1; + + if (gio_id(ga.ga_ioh, 1) == 0) + continue; + + if (config_found_sm(self, &ga, gio_print_fb, gio_submatch)) { + gfx[ngfx++] = gfx_bases[i].base; + } + } + + /* + * Now attach any GIO expansion cards. + * + * Be sure to skip any addresses to which a graphics device has + * already been attached. + */ + for (i = 0; slot_bases[i].base != 0; i++) { + int skip = 0; + + /* skip slots that don't apply to us */ + if (slot_bases[i].mach_type != sys_config.system_type) + continue; + + if (slot_bases[i].mach_subtype != -1 && + slot_bases[i].mach_subtype != sys_config.system_subtype) + continue; + + for (j = 0; j < ngfx; j++) { + if (slot_bases[i].base == gfx[j]) { + skip = 1; + break; + } + } + if (skip) + continue; + + ga.ga_slot = slot_bases[i].slot; + ga.ga_addr = slot_bases[i].base; + ga.ga_iot = sc->sc_iot; + ga.ga_ioh = PHYS_TO_XKPHYS(ga.ga_addr, CCA_NC); + ga.ga_dmat = sc->sc_dmat; + + if (gio_id(ga.ga_ioh, 0) == 0) + continue; + + ga.ga_product = bus_space_read_4(ga.ga_iot, ga.ga_ioh, 0); + + config_found_sm(self, &ga, gio_print, gio_submatch); + } + + config_search(gio_search, self, aux); +} + +/* + * Try and figure out whether there is a device at the given slot address. + */ +uint32_t +gio_id(paddr_t pa, int maybe_gfx) +{ + uint32_t id32; + uint16_t id16 = 0; + uint8_t id8 = 0; + + if (guarded_read_4(pa, &id32) != 0) + return 0; + + id16 = id32 ^ 0xffff; + (void)guarded_read_2(pa | 2, &id16); + id8 = id16 ^ 0xff; + (void)guarded_read_1(pa | 3, &id8); + + /* + * If there is a real GIO device at this address (as opposed to + * a graphics card), then the low-order 8 bits of each read need + * to be consistent... + */ + if (id8 == (id16 & 0xff) && id8 == (id32 & 0xff)) { + if (GIO_PRODUCT_32BIT_ID(id8)) { + if (id16 == (id32 & 0xffff)) + return id32; + } else { + if (id8 != 0) + return id32; + } + } + + if (maybe_gfx) + return 1; + + return 0; +} + +int +gio_print(void *aux, const char *pnp) +{ + struct gio_attach_args *ga = aux; + const char *descr; + int product, revision; + uint i; + + product = GIO_PRODUCT_PRODUCTID(ga->ga_product); + if (GIO_PRODUCT_32BIT_ID(ga->ga_product)) + revision = GIO_PRODUCT_REVISION(ga->ga_product); + else + revision = 0; + + descr = "unknown GIO card"; + for (i = 0; gio_knowndevs[i].productid != 0; i++) { + if (gio_knowndevs[i].productid == product) { + descr = gio_knowndevs[i].product; + break; + } + } + + if (pnp != NULL) { + printf("%s", descr); + if (ga->ga_product != -1) + printf(" (product 0x%02x revision 0x%02x)", + product, revision); + printf(" at %s", pnp); + } + + if (ga->ga_slot != -1) + printf(" slot %d", ga->ga_slot); + printf(" addr 0x%lx", ga->ga_addr); + + return UNCONF; +} + +int +gio_print_fb(void *aux, const char *pnp) +{ +#if 0 /* once we can know for sure there really is a frame buffer here */ + if (pnp != NULL) + printf("framebuffer at %s", pnp); + + if (ga->ga_addr != (uint64_t)-1) + printf(" addr 0x%lx", ga->ga_addr); + + return UNCONF; +#else + return QUIET; +#endif +} + +int +gio_search(struct device *parent, void *vcf, void *aux) +{ + struct gio_softc *sc = (struct gio_softc *)parent; + struct cfdata *cf = (struct cfdata *)vcf; + struct gio_attach_args ga; + + /* Handled by direct configuration, so skip here */ + if (cf->cf_loc[1 /*GIOCF_ADDR*/] == -1) + return 0; + + ga.ga_product = -1; + ga.ga_slot = cf->cf_loc[0 /*GIOCF_SLOT*/]; + ga.ga_addr = (uint64_t)cf->cf_loc[1 /*GIOCF_ADDR*/]; + ga.ga_iot = sc->sc_iot; + ga.ga_ioh = PHYS_TO_XKPHYS(ga.ga_addr, CCA_NC); + ga.ga_dmat = sc->sc_dmat; + + if ((*cf->cf_attach->ca_match)(parent, cf, &ga) == 0) + return 0; + + config_attach(parent, cf, &ga, gio_print); + + return 1; +} + +int +gio_submatch(struct device *parent, void *vcf, void *aux) +{ + struct cfdata *cf = (struct cfdata *)vcf; + struct gio_attach_args *ga = (struct gio_attach_args *)aux; + + if (cf->cf_loc[0 /*GIOCF_SLOT*/] != -1 && + cf->cf_loc[0 /*GIOCF_SLOT*/] != ga->ga_slot) + return 0; + + if (cf->cf_loc[1 /*GIOCF_ADDR*/] != -1 && + (uint64_t)cf->cf_loc[1 /*GIOCF_ADDR*/] != ga->ga_addr) + return 0; + + return (*cf->cf_attach->ca_match)(parent, cf, aux); +} + +#if 0 /* XXX involve wscons_machdep somehow */ +int +gio_cnattach(void) +{ + extern struct machine_bus_dma_tag imc_bus_dma_tag; /* XXX */ + extern bus_space_t imcbus_tag; /* XXX */ + struct gio_attach_args ga; + uint32_t dummy; + int i; + + for (i = 0; gfx_bases[i].base != 0; i++) { + /* skip bases that don't apply to us */ + if (gfx_bases[i].mach_type != sys_config.system_type) + continue; + + if (gfx_bases[i].mach_subtype != -1 && + gfx_bases[i].mach_subtype != sys_config.system_subtype) + continue; + + ga.ga_slot = -1; + ga.ga_addr = gfx_bases[i].base; + ga.ga_iot = &imcbus_tag; /* XXX */ + ga.ga_ioh = PHYS_TO_XKPHYS(ga.ga_addr, CCA_NC); + ga.ga_dmat = &imc_bus_dma_tag; /* XXX */ + ga.ga_product = -1; + + if (gio_id(ga.ga_ioh, 1) == 0) + continue; + +#if NGRTWO > 0 + if (grtwo_cnattach(&ga) == 0) + return 0; +#endif + +#if NLIGHT > 0 + if (light_cnattach(&ga) == 0) + return 0; +#endif + +#if NNEWPORT > 0 + if (newport_cnattach(&ga) == 0) + return 0; +#endif + + } + + return ENXIO; +} +#endif + +/* + * Devices living in the expansion slots must enable or disable some + * GIO arbiter settings. This is accomplished via imc(4) registers. + */ +int +gio_arb_config(int slot, uint32_t flags) +{ + if (flags == 0) + return (EINVAL); + + if (flags & ~(GIO_ARB_RT | GIO_ARB_LB | GIO_ARB_MST | GIO_ARB_SLV | + GIO_ARB_PIPE | GIO_ARB_NOPIPE | GIO_ARB_32BIT | GIO_ARB_64BIT | + GIO_ARB_HPC2_32BIT | GIO_ARB_HPC2_64BIT)) + return (EINVAL); + + if (((flags & GIO_ARB_RT) && (flags & GIO_ARB_LB)) || + ((flags & GIO_ARB_MST) && (flags & GIO_ARB_SLV)) || + ((flags & GIO_ARB_PIPE) && (flags & GIO_ARB_NOPIPE)) || + ((flags & GIO_ARB_32BIT) && (flags & GIO_ARB_64BIT)) || + ((flags & GIO_ARB_HPC2_32BIT) && (flags & GIO_ARB_HPC2_64BIT))) + return (EINVAL); + + return (imc_gio64_arb_config(slot, flags)); +} + +/* + * Establish an interrupt handler for the specified slot. + * + * Indy and Challenge S have an interrupt per GIO slot. Indigo and Indigo2 + * share a single interrupt, however. + */ +void * +gio_intr_establish(int slot, int level, int (*func)(void *), void *arg, + const char *what) +{ + int intr; + + switch (sys_config.system_type) { + case SGI_IP20: + if (slot == GIO_SLOT_GFX) + return NULL; + intr = 6; + break; + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + if (sys_config.system_subtype == IP22_INDIGO2) { + if (slot == GIO_SLOT_EXP1) + return NULL; + intr = 6; + } else { + if (slot == GIO_SLOT_GFX) + return NULL; + intr = (slot == GIO_SLOT_EXP0) ? 22 : 23; + } + break; + default: + return NULL; + } + + return int2_intr_establish(intr, level, func, arg, what); +} + +const char * +gio_product_string(int prid) +{ + int i; + + for (i = 0; gio_knowndevs[i].product != NULL; i++) + if (gio_knowndevs[i].productid == prid) + return (gio_knowndevs[i].product); + + return (NULL); +} diff --git a/sys/arch/sgi/gio/giodevs b/sys/arch/sgi/gio/giodevs new file mode 100644 index 00000000000..8189affed20 --- /dev/null +++ b/sys/arch/sgi/gio/giodevs @@ -0,0 +1,21 @@ +$OpenBSD: giodevs,v 1.1 2012/03/28 20:44:23 miod Exp $ +/* $NetBSD: giodevs,v 1.8 2007/02/19 04:46:33 rumble Exp $ */ + +product XPI 0x01 XPI low cost FDDI +product GTR 0x02 GTR TokenRing +product ISDN 0x04 Synchronous ISDN +product CANON 0x06 Canon Interface +product JPEG_D 0x08 JPEG (Double Wide) +product JPEG_S 0x09 JPEG (Single Wide) +product XPI_M0 0x0a XPI mez. FDDI device 0 +product XPI_M1 0x0b XPI mez. FDDI device 1 +product EP 0x0e E-Plex 8-port Ethernet +product IVAS 0x30 Lyon Lamb IVAS +product PHOBOS_G160 0x35 Phobos G160 10/100 Ethernet +product PHOBOS_G130 0x36 Phobos G130 10/100 Ethernet +product PHOBOS_G100 0x37 Phobos G100 100baseTX Fast Ethernet +product SETENG_GFE 0x38 Set Engineering GFE 10/100 Ethernet +product ATM 0x85 ATM board +product SCSI 0x87 16 bit SCSI Card +product SMPTE 0x8c SMPTE 259M Video +product BABBLE 0x8d Babblefish Compression diff --git a/sys/arch/sgi/gio/gioreg.h b/sys/arch/sgi/gio/gioreg.h new file mode 100644 index 00000000000..a5bf76444e3 --- /dev/null +++ b/sys/arch/sgi/gio/gioreg.h @@ -0,0 +1,68 @@ +/* $OpenBSD: gioreg.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: gioreg.h,v 1.4 2006/08/31 00:01:10 rumble Exp $ */ + +/* + * Copyright (c) 2003 Ilpo Ruotsalainen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * <<Id: LICENSE_GC,v 1.1 2001/10/01 23:24:05 cgd Exp>> + */ + +/* + * The GIO Product Identification Word is the first word (1 or 4 bytes) + * in each GIO device's address space. It is the same format for GIO32, + * GIO32-bis, and GIO64 devices. The macros below extract the following + * fields: + * + * Bits: + * 0-6 Product ID Code + * 7 Product Identification Word size (0: 8 bits, 1: 32 bits) + * 8-15 Product Revision + * 16 GIO Interface Size (0: 32, 1: 64; NB: GIO64 devices may be 32) + * 17 Rom Present (1: present) + * 18-31 Manufacturer-specific Code + * + * The upper three bytes containing the Product Revision, GIO Interface + * Size, Rom Presence indicator, and Manufacturer-specific Code are only + * valid if bit 7 is set in the Product ID Word. If it is not set, all + * values default to 0. + * + * If the Rom Present bit is set, the three words after the Product ID are + * reserved for three ROM registers: + * Board Serial Number Register (base_address + 0x4) + * ROM Index Register (base_address + 0x8) + * ROM Read Register (base_address + 0xc) + * + * The ROM Index Register is initialised by the CPU to 0 and incremented by + * 4 on each read from the ROM Read Register. The Board Serial Number + * Register contains a manufacturer-specific serial number. + */ + +#define GIO_PRODUCT_32BIT_ID(x) ((x) & 0x80) +#define GIO_PRODUCT_PRODUCTID(x) ((x) & 0x7f) +#define GIO_PRODUCT_REVISION(x) (((x) >> 8) & 0xff) +#define GIO_PRODUCT_IS_64BIT(x) (!!((x) & 0x8000)) +#define GIO_PRODUCT_HAS_ROM(x) (!!((x) & 0x10000)) +#define GIO_PRODUCT_MANUCODE(x) ((x) >> 18) diff --git a/sys/arch/sgi/gio/giovar.h b/sys/arch/sgi/gio/giovar.h new file mode 100644 index 00000000000..6c35c7561da --- /dev/null +++ b/sys/arch/sgi/gio/giovar.h @@ -0,0 +1,75 @@ +/* $OpenBSD: giovar.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: giovar.h,v 1.10 2011/07/01 18:53:46 dyoung Exp $ */ + +/* + * Copyright (c) 2000 Soren S. Jorvang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the + * NetBSD Project. See http://www.NetBSD.org/ for + * information about NetBSD. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * GIO 32/32-bis/64 bus + */ + +struct gio_attach_args { + bus_space_tag_t ga_iot; + bus_space_handle_t ga_ioh; + bus_dma_tag_t ga_dmat; + + int ga_slot; /* not valid if graphics */ + u_int64_t ga_addr; + + u_int32_t ga_product; /* not valid if graphics */ +}; + + +#define GIO_SLOT_GFX 0 +#define GIO_SLOT_EXP0 1 +#define GIO_SLOT_EXP1 2 + +#define GIO_ARB_RT 0x001 /* real-time device */ +#define GIO_ARB_LB 0x002 /* long-burst device */ + +#define GIO_ARB_MST 0x004 /* bus master enable */ +#define GIO_ARB_SLV 0x008 /* slave */ + +#define GIO_ARB_PIPE 0x010 /* pipelining enable */ +#define GIO_ARB_NOPIPE 0x020 /* pipelining disable */ + +#define GIO_ARB_32BIT 0x040 /* 32-bit transfers */ +#define GIO_ARB_64BIT 0x080 /* 64-bit transfers */ + +#define GIO_ARB_HPC2_32BIT 0x100 /* 32-bit secondary HPC (ignores slot)*/ +#define GIO_ARB_HPC2_64BIT 0x200 /* 64-bit secondary HPC (ignores slot)*/ + +int gio_cnattach(void); +int gio_arb_config(int, uint32_t); +void *gio_intr_establish(int, int, int (*)(void *), void *, + const char *); +const char *gio_product_string(int); diff --git a/sys/arch/sgi/hpc/dpclock.c b/sys/arch/sgi/hpc/dpclock.c new file mode 100644 index 00000000000..600c3ede567 --- /dev/null +++ b/sys/arch/sgi/hpc/dpclock.c @@ -0,0 +1,312 @@ +/* $OpenBSD: dpclock.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: dpclock.c,v 1.3 2011/07/01 18:53:46 dyoung Exp $ */ + +/* + * Copyright (c) 2012 Miodrag Vallat. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * Copyright (c) 2001 Erik Reid + * Copyright (c) 2001 Rafal K. Boni + * Copyright (c) 2001 Christopher Sekiya + * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Portions of this code are derived from software contributed to The + * NetBSD Foundation by Jason R. Thorpe of the Numerical Aerospace + * Simulation Facility, NASA Ames Research Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1982, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: Utah $Hdr: clock.c 1.18 91/01/21$ + * + * @(#)clock.c 8.2 (Berkeley) 1/12/94 + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> + +#include <mips64/archtype.h> +#include <mips64/dev/clockvar.h> + +#include <dev/ic/dp8573areg.h> +#include <sgi/hpc/hpcvar.h> + +#define IRIX_BASE_YEAR 1940 + +struct dpclock_softc { + struct device sc_dev; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +int dpclock_match(struct device *, void *, void *); +void dpclock_attach(struct device *, struct device *, void *); + +struct cfdriver dpclock_cd = { + NULL, "dpclock", DV_DULL +}; + +const struct cfattach dpclock_ca = { + sizeof(struct dpclock_softc), dpclock_match, dpclock_attach +}; + +#define dpclock_read(sc,r) \ + bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, ((r) << 2) + 3) +#define dpclock_write(sc,r,v) \ + bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, ((r) << 2) + 3, (v)) + +static inline int frombcd(int); +static inline int tobcd(int); +static inline int leapyear(int year); + +static inline int +frombcd(int x) +{ + return (x >> 4) * 10 + (x & 0xf); +} +static inline int +tobcd(int x) +{ + return (x / 10 * 16) + (x % 10); +} +/* + * This inline avoids some unnecessary modulo operations + * as compared with the usual macro: + * ( ((year % 4) == 0 && + * (year % 100) != 0) || + * ((year % 400) == 0) ) + * It is otherwise equivalent. + * (borrowed from kern/clock_subr.c) + */ +static inline int +leapyear(int year) +{ + int rv = 0; + + if ((year & 3) == 0) { + rv = 1; + if ((year % 100) == 0) { + rv = 0; + if ((year % 400) == 0) + rv = 1; + } + } + return (rv); +} + +void dpclock_gettime(void *, time_t, struct tod_time *); +void dpclock_settime(void *, struct tod_time *); + +int +dpclock_match(struct device *parent, void *vcf, void *aux) +{ + struct hpc_attach_args *haa = aux; + + if (strcmp(haa->ha_name, dpclock_cd.cd_name) != 0) + return 0; + + return 1; +} + +void +dpclock_attach(struct device *parent, struct device *self, void *aux) +{ + struct dpclock_softc *sc = (void *)self; + struct hpc_attach_args *haa = aux; + uint8_t st, r; + + sc->sc_iot = haa->ha_st; + if (bus_space_subregion(haa->ha_st, haa->ha_sh, haa->ha_devoff, + 4 * DP8573A_NREG, &sc->sc_ioh) != 0) { + printf(": can't map registers\n"); + return; + } + + st = dpclock_read(sc, DP8573A_STATUS); + dpclock_write(sc, DP8573A_STATUS, st | DP8573A_STATUS_REGSEL); + r = dpclock_read(sc, DP8573A_RT_MODE); + if ((r & DP8573A_RT_MODE_CLKSS) == 0) { + printf(": clock stopped"); + dpclock_write(sc, DP8573A_RT_MODE, r | DP8573A_RT_MODE_CLKSS); + dpclock_write(sc, DP8573A_INT0_CTL, 0); + dpclock_write(sc, DP8573A_INT1_CTL, DP8573A_INT1_CTL_PWRINT); + } + dpclock_write(sc, DP8573A_STATUS, st & ~DP8573A_STATUS_REGSEL); + r = dpclock_read(sc, DP8573A_PFLAG); + if (r & DP8573A_PFLAG_TESTMODE) { + dpclock_write(sc, DP8573A_RAM_1F, 0); + dpclock_write(sc, DP8573A_PFLAG, r & ~DP8573A_PFLAG_TESTMODE); + } + + printf("\n"); + + sys_tod.tod_get = dpclock_gettime; + sys_tod.tod_set = dpclock_settime; + sys_tod.tod_cookie = self; +} + +/* + * Get the time of day, based on the clock's value and/or the base value. + */ +void +dpclock_gettime(void *cookie, time_t base, struct tod_time *ct) +{ + struct dpclock_softc *sc = (void *)cookie; + uint i; + uint8_t regs[DP8573A_NREG]; + + i = dpclock_read(sc, DP8573A_TIMESAVE_CTL); + dpclock_write(sc, DP8573A_TIMESAVE_CTL, i | DP8573A_TIMESAVE_CTL_EN); + dpclock_write(sc, DP8573A_TIMESAVE_CTL, i); + + for (i = 0; i < DP8573A_NREG; i++) + regs[i] = dpclock_read(sc, i); + + ct->sec = frombcd(regs[DP8573A_SAVE_SEC]); + ct->min = frombcd(regs[DP8573A_SAVE_MIN]); + + if (regs[DP8573A_RT_MODE] & DP8573A_RT_MODE_1224) { + ct->hour = frombcd(regs[DP8573A_SAVE_HOUR] & + DP8573A_HOUR_12HR_MASK) + + ((regs[DP8573A_SAVE_HOUR] & DP8573A_RT_MODE_1224) ? 0 : 12); + + /* + * In AM/PM mode, hour range is 01-12, so adding in 12 hours + * for PM gives us 01-24, whereas we want 00-23, so map hour + * 24 to hour 0. + */ + if (ct->hour == 24) + ct->hour = 0; + } else { + ct->hour = frombcd(regs[DP8573A_SAVE_HOUR] & + DP8573A_HOUR_24HR_MASK); + } + + ct->day = frombcd(regs[DP8573A_SAVE_DOM]); + ct->mon = frombcd(regs[DP8573A_SAVE_MONTH]); + ct->year = frombcd(regs[DP8573A_YEAR]) + (IRIX_BASE_YEAR - 1900); +} + +/* + * Reset the TODR based on the time value. + */ +void +dpclock_settime(void *cookie, struct tod_time *ct) +{ + struct dpclock_softc *sc = (void *)cookie; + uint i; + uint st, r, delta; + uint8_t regs[DP8573A_NREG]; + + r = dpclock_read(sc, DP8573A_TIMESAVE_CTL); + dpclock_write(sc, DP8573A_TIMESAVE_CTL, r | DP8573A_TIMESAVE_CTL_EN); + dpclock_write(sc, DP8573A_TIMESAVE_CTL, r); + + for (i = 0; i < DP8573A_NREG; i++) + regs[i] = dpclock_read(sc, i); + + regs[DP8573A_SUBSECOND] = 0; + regs[DP8573A_SECOND] = tobcd(ct->sec); + regs[DP8573A_MINUTE] = tobcd(ct->min); + regs[DP8573A_HOUR] = tobcd(ct->hour) & DP8573A_HOUR_24HR_MASK; + regs[DP8573A_DOW] = tobcd(ct->dow); + regs[DP8573A_DOM] = tobcd(ct->day); + regs[DP8573A_MONTH] = tobcd(ct->mon); + regs[DP8573A_YEAR] = tobcd(ct->year - (IRIX_BASE_YEAR - 1900)); + + st = dpclock_read(sc, DP8573A_STATUS); + dpclock_write(sc, DP8573A_STATUS, st | DP8573A_STATUS_REGSEL); + r = dpclock_read(sc, DP8573A_RT_MODE); + dpclock_write(sc, DP8573A_RT_MODE, r & ~DP8573A_RT_MODE_CLKSS); + + for (i = 0; i < 10; i++) + dpclock_write(sc, DP8573A_COUNTERS + i, + regs[DP8573A_COUNTERS + i]); + + /* + * We now need to set the leap year counter to the correct value. + * Unfortunately it is only two bits wide, while eight years can + * happen between two leap years. Skirting this is left as an + * exercise to the reader with an Indigo in working condition + * by year 2100. + */ + delta = 0; + while (delta < 3 && !leapyear(ct->year - delta)) + delta++; + + r &= ~(DP8573A_RT_MODE_LYLSB | DP8573A_RT_MODE_LYMSB); + dpclock_write(sc, DP8573A_RT_MODE, r | delta | DP8573A_RT_MODE_CLKSS); + + dpclock_write(sc, DP8573A_STATUS, st & ~DP8573A_STATUS_REGSEL); +} diff --git a/sys/arch/sgi/hpc/dsclock.c b/sys/arch/sgi/hpc/dsclock.c new file mode 100644 index 00000000000..396d68ec9d9 --- /dev/null +++ b/sys/arch/sgi/hpc/dsclock.c @@ -0,0 +1,192 @@ +/* $OpenBSD: dsclock.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: dsclock.c,v 1.5 2011/07/01 18:53:46 dyoung Exp $ */ + +/* + * Copyright (c) 2001 Rafal K. Boni + * Copyright (c) 2001 Christopher Sekiya + * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Portions of this code are derived from software contributed to The + * NetBSD Foundation by Jason R. Thorpe of the Numerical Aerospace + * Simulation Facility, NASA Ames Research Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> + +#include <mips64/archtype.h> +#include <mips64/dev/clockvar.h> + +#include <dev/ic/ds1286reg.h> +#include <sgi/hpc/hpcvar.h> + +#define IRIX_BASE_YEAR 1940 + +struct dsclock_softc { + struct device sc_dev; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +int dsclock_match(struct device *, void *, void *); +void dsclock_attach(struct device *, struct device *, void *); + +struct cfdriver dsclock_cd = { + NULL, "dsclock", DV_DULL +}; + +const struct cfattach dsclock_ca = { + sizeof(struct dsclock_softc), dsclock_match, dsclock_attach +}; + +#define ds1286_read(sc,r) \ + bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, ((r) << 2) + 3) +#define ds1286_write(sc,r,v) \ + bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, ((r) << 2) + 3, (v)) + +static inline int frombcd(int); +static inline int tobcd(int); + +static inline int +frombcd(int x) +{ + return (x >> 4) * 10 + (x & 0xf); +} +static inline int +tobcd(int x) +{ + return (x / 10 * 16) + (x % 10); +} + +void dsclock_gettime(void *, time_t, struct tod_time *); +void dsclock_settime(void *, struct tod_time *); + +int +dsclock_match(struct device *parent, void *vcf, void *aux) +{ + struct hpc_attach_args *haa = aux; + + if (strcmp(haa->ha_name, dsclock_cd.cd_name) != 0) + return 0; + + return 1; +} + +void +dsclock_attach(struct device *parent, struct device *self, void *aux) +{ + struct dsclock_softc *sc = (void *)self; + struct hpc_attach_args *haa = aux; + + sc->sc_iot = haa->ha_st; + if (bus_space_subregion(haa->ha_st, haa->ha_sh, haa->ha_devoff, + 4 * 8192, &sc->sc_ioh) != 0) { + printf(": can't map registers\n"); + return; + } + + printf("\n"); + + sys_tod.tod_get = dsclock_gettime; + sys_tod.tod_set = dsclock_settime; + sys_tod.tod_cookie = self; +} + +/* + * Get the time of day, based on the clock's value and/or the base value. + */ +void +dsclock_gettime(void *cookie, time_t base, struct tod_time *ct) +{ + struct dsclock_softc *sc = (void *)cookie; + ds1286_todregs regs; + int s; + + s = splhigh(); + DS1286_GETTOD(sc, ®s) + splx(s); + + ct->sec = frombcd(regs[DS1286_SEC]); + ct->min = frombcd(regs[DS1286_MIN]); + + if (regs[DS1286_HOUR] & DS1286_HOUR_12MODE) { + ct->hour = frombcd(regs[DS1286_HOUR] & DS1286_HOUR_12HR_MASK) + + ((regs[DS1286_HOUR] & DS1286_HOUR_12HR_PM) ? 12 : 0); + + /* + * In AM/PM mode, hour range is 01-12, so adding in 12 hours + * for PM gives us 01-24, whereas we want 00-23, so map hour + * 24 to hour 0. + */ + if (ct->hour == 24) + ct->hour = 0; + } else { + ct->hour = frombcd(regs[DS1286_HOUR] & DS1286_HOUR_24HR_MASK); + } + + ct->day = frombcd(regs[DS1286_DOM]); + ct->mon = frombcd(regs[DS1286_MONTH] & DS1286_MONTH_MASK); + ct->year = frombcd(regs[DS1286_YEAR]) + (IRIX_BASE_YEAR - 1900); +} + +/* + * Reset the TODR based on the time value. + */ +void +dsclock_settime(void *cookie, struct tod_time *ct) +{ + struct dsclock_softc *sc = (void *)cookie; + ds1286_todregs regs; + int s; + + s = splhigh(); + DS1286_GETTOD(sc, ®s); + splx(s); + + regs[DS1286_SUBSEC] = 0; + regs[DS1286_SEC] = tobcd(ct->sec); + regs[DS1286_MIN] = tobcd(ct->min); + regs[DS1286_HOUR] = tobcd(ct->hour) & DS1286_HOUR_24HR_MASK; + regs[DS1286_DOW] = tobcd(ct->dow); + regs[DS1286_DOM] = tobcd(ct->day); + + /* Leave wave-generator bits as set originally */ + regs[DS1286_MONTH] &= ~DS1286_MONTH_MASK; + regs[DS1286_MONTH] |= tobcd(ct->mon) & DS1286_MONTH_MASK; + + regs[DS1286_YEAR] = tobcd(ct->year - (IRIX_BASE_YEAR - 1900)); + + s = splhigh(); + DS1286_PUTTOD(sc, ®s); + splx(s); +} diff --git a/sys/arch/sgi/hpc/files.hpc b/sys/arch/sgi/hpc/files.hpc new file mode 100644 index 00000000000..4463f066a45 --- /dev/null +++ b/sys/arch/sgi/hpc/files.hpc @@ -0,0 +1,56 @@ +# $OpenBSD: files.hpc,v 1.1 2012/03/28 20:44:23 miod Exp $ +# $NetBSD: files.hpc,v 1.14 2009/05/14 01:10:19 macallan Exp $ + +# IP20 RTC +device dpclock +attach dpclock at hpc +file arch/sgi/hpc/dpclock.c dpclock + +# IP22/24 RTC +device dsclock +attach dsclock at hpc +file arch/sgi/hpc/dsclock.c dsclock + +device sq: ether, ifnet +attach sq at hpc +file arch/sgi/hpc/if_sq.c sq + +define hpcdma +file arch/sgi/hpc/hpcdma.c hpcdma + +device wdsc: wd33c93, scsi, hpcdma +attach wdsc at hpc +file arch/sgi/hpc/wdsc.c wdsc + +device haltwo: audio, auconv, mulaw +attach haltwo at hpc +file arch/sgi/hpc/haltwo.c haltwo + +device zs {[channel = -1]} +attach zs at hpc with zs_hpc +file arch/sgi/hpc/zs.c zs needs-flag +file arch/sgi/hpc/z8530sc.c zs + +device zstty: tty +attach zstty at zs +file arch/sgi/hpc/z8530tty.c zstty needs-flag + +device zskbd: wskbddev +attach zskbd at zs +file arch/sgi/hpc/zs_kbd.c zskbd needs-flag +file arch/sgi/dev/wskbdmap_sgi.c zskbd + +device zsms: wsmousedev +attach zsms at zs +file arch/sgi/hpc/zs_ms.c zsms + +attach pckbc at hpc with pckbc_hpc +file arch/sgi/hpc/pckbc_hpc.c pckbc_hpc + +#device pione +#attach pione at hpc +#file arch/sgi/hpc/pione.c pione + +device panel +attach panel at hpc +file arch/sgi/hpc/panel.c panel diff --git a/sys/arch/sgi/hpc/hpc.c b/sys/arch/sgi/hpc/hpc.c new file mode 100644 index 00000000000..ca2c31c06ed --- /dev/null +++ b/sys/arch/sgi/hpc/hpc.c @@ -0,0 +1,822 @@ +/* $OpenBSD: hpc.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: hpc.c,v 1.66 2011/07/01 18:53:46 dyoung Exp $ */ +/* $NetBSD: ioc.c,v 1.9 2011/07/01 18:53:47 dyoung Exp $ */ + +/* + * Copyright (c) 2003 Christopher Sekiya + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the + * NetBSD Project. See http://www.NetBSD.org/ for + * information about NetBSD. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 2000 Soren S. Jorvang + * Copyright (c) 2001 Rafal K. Boni + * Copyright (c) 2001 Jason R. Thorpe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the + * NetBSD Project. See http://www.NetBSD.org/ for + * information about NetBSD. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Combined driver for the HPC (High performance Peripheral Controller) + * and IOC2 (I/O Controller) chips. + * + * It would theoretically be better to attach an IOC driver to HPC on + * IOC systems (IP22/24/26/28), and attach the few onboard devices + * which attach directly to HPC on IP20, to IOC. But since IOC depends + * too much on HPC, the complexity this would introduce is not worth + * the hassle. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/timeout.h> + +#include <mips64/archtype.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> +#include <machine/cpu.h> + +#include <sgi/gio/gioreg.h> +#include <sgi/gio/giovar.h> + +#include <sgi/hpc/hpcvar.h> +#include <sgi/hpc/hpcreg.h> +#include <sgi/hpc/iocreg.h> +#include <sgi/sgi/ip22.h> + +#include <dev/ic/smc93cx6var.h> + +struct hpc_device { + const char *hd_name; + bus_addr_t hd_base; + bus_addr_t hd_devoff; + bus_addr_t hd_dmaoff; + int hd_irq; + int hd_sysmask; +}; + +static const struct hpc_device hpc1_devices[] = { + /* probe order is important for IP20 zs */ + + { "zs", /* Indigo serial 0/1 duart 1 */ + HPC_BASE_ADDRESS_0, + 0x0d10, 0, + 5, + HPCDEV_IP20 }, + + { "zs", /* Indigo kbd/ms duart 0 */ + HPC_BASE_ADDRESS_0, + 0x0d00, 0, + 5, + HPCDEV_IP20 }, + + { "sq", /* Indigo onboard ethernet */ + HPC_BASE_ADDRESS_0, + HPC1_ENET_DEVREGS, HPC1_ENET_REGS, + 3, + HPCDEV_IP20 }, + + { "sq", /* E++ GIO adapter slot 0 (Indigo) */ + HPC_BASE_ADDRESS_1, + HPC1_ENET_DEVREGS, HPC1_ENET_REGS, + 6, + HPCDEV_IP20 }, + + { "sq", /* E++ GIO adapter slot 0 (Indy) */ + HPC_BASE_ADDRESS_1, + HPC1_ENET_DEVREGS, HPC1_ENET_REGS, + 16 + 6, + HPCDEV_IP24 }, + + { "sq", /* E++ GIO adapter slot 1 (Indigo) */ + HPC_BASE_ADDRESS_2, + HPC1_ENET_DEVREGS, HPC1_ENET_REGS, + 6, + HPCDEV_IP20 }, + + { "sq", /* E++ GIO adapter slot 1 (Indy/Challenge S) */ + HPC_BASE_ADDRESS_2, + HPC1_ENET_DEVREGS, HPC1_ENET_REGS, + 16 + 7, + HPCDEV_IP24 }, + + { "wdsc", /* Indigo onboard SCSI */ + HPC_BASE_ADDRESS_0, + HPC1_SCSI0_DEVREGS, HPC1_SCSI0_REGS, + 2, + HPCDEV_IP20 }, + + { "wdsc", /* GIO32 SCSI adapter slot 0 (Indigo) */ + HPC_BASE_ADDRESS_1, + HPC1_SCSI0_DEVREGS, HPC1_SCSI0_REGS, + 6, + HPCDEV_IP20 }, + + { "wdsc", /* GIO32 SCSI adapter slot 0 (Indy) */ + HPC_BASE_ADDRESS_1, + HPC1_SCSI0_DEVREGS, HPC1_SCSI0_REGS, + 16 + 6, + HPCDEV_IP24 }, + + { "wdsc", /* GIO32 SCSI adapter slot 1 (Indigo) */ + HPC_BASE_ADDRESS_2, + HPC1_SCSI0_DEVREGS, HPC1_SCSI0_REGS, + 6, + HPCDEV_IP20 }, + + { "wdsc", /* GIO32 SCSI adapter slot 1 (Indy/Challenge S) */ + HPC_BASE_ADDRESS_2, + HPC1_SCSI0_DEVREGS, HPC1_SCSI0_REGS, + 16 + 7, + HPCDEV_IP24 }, + + { NULL, + 0, + 0, 0, + 0, + 0 + } +}; + +static const struct hpc_device hpc3_devices[] = { + { "zs", /* serial 0/1 duart 0 */ + HPC_BASE_ADDRESS_0, + /* XXX Magic numbers */ + HPC3_PBUS_CH6_DEVREGS + IOC_SERIAL_REGS, 0, + 24 + 5, + HPCDEV_IP22 | HPCDEV_IP24 }, + + { "pckbc", /* Indigo2/Indy ps2 keyboard/mouse controller */ + HPC_BASE_ADDRESS_0, + HPC3_PBUS_CH6_DEVREGS + IOC_KB_REGS, 0, + 24 + 4, + HPCDEV_IP22 | HPCDEV_IP24 }, + + { "sq", /* Indigo2/Indy/Challenge S/Challenge M onboard enet */ + HPC_BASE_ADDRESS_0, + HPC3_ENET_DEVREGS, HPC3_ENET_REGS, + 3, + HPCDEV_IP22 | HPCDEV_IP24 }, + + { "sq", /* Challenge S IOPLUS secondary ethernet */ + HPC_BASE_ADDRESS_1, + HPC3_ENET_DEVREGS, HPC3_ENET_REGS, + 0, + HPCDEV_IP24 }, + + { "wdsc", /* Indigo2/Indy/Challenge S/Challenge M onboard SCSI */ + HPC_BASE_ADDRESS_0, + HPC3_SCSI0_DEVREGS, HPC3_SCSI0_REGS, + 1, + HPCDEV_IP22 | HPCDEV_IP24 }, + + { "wdsc", /* Indigo2/Challenge M secondary onboard SCSI */ + HPC_BASE_ADDRESS_0, + HPC3_SCSI1_DEVREGS, HPC3_SCSI1_REGS, + 2, + HPCDEV_IP22 }, + + { "haltwo", /* Indigo2/Indy onboard audio */ + HPC_BASE_ADDRESS_0, + HPC3_PBUS_CH0_DEVREGS, HPC3_PBUS_DMAREGS, + 8 + 4, /* really the HPC DMA complete interrupt */ + HPCDEV_IP22 | HPCDEV_IP24 }, + + { "pione", /* Indigo2/Indy/Challenge S/Challenge M onboard pport */ + HPC_BASE_ADDRESS_0, + HPC3_PBUS_CH6_DEVREGS + IOC_PLP_REGS, 0, + 5, + HPCDEV_IP22 | HPCDEV_IP24 }, + + { "panel", /* Indy front panel */ + HPC_BASE_ADDRESS_0, + HPC3_PBUS_CH6_DEVREGS + IOC_PANEL, 0, + 9, + HPCDEV_IP24 }, + + { NULL, + 0, + 0, 0, + 0, + 0 + } +}; + +struct hpc_softc { + struct device sc_dev; + + bus_addr_t sc_base; + + bus_space_tag_t sc_ct; + bus_space_handle_t sc_ch; + bus_dma_tag_t sc_dmat; + + struct timeout sc_blink_tmo; +}; + +static struct hpc_values hpc1_values = { + .revision = 1, + .scsi0_regs = HPC1_SCSI0_REGS, + .scsi0_regs_size = HPC1_SCSI0_REGS_SIZE, + .scsi0_cbp = HPC1_SCSI0_CBP, + .scsi0_ndbp = HPC1_SCSI0_NDBP, + .scsi0_bc = HPC1_SCSI0_BC, + .scsi0_ctl = HPC1_SCSI0_CTL, + .scsi0_gio = HPC1_SCSI0_GIO, + .scsi0_dev = HPC1_SCSI0_DEV, + .scsi0_dmacfg = HPC1_SCSI0_DMACFG, + .scsi0_piocfg = HPC1_SCSI0_PIOCFG, + .scsi1_regs = 0, + .scsi1_regs_size = 0, + .scsi1_cbp = 0, + .scsi1_ndbp = 0, + .scsi1_bc = 0, + .scsi1_ctl = 0, + .scsi1_gio = 0, + .scsi1_dev = 0, + .scsi1_dmacfg = 0, + .scsi1_piocfg = 0, + .enet_regs = HPC1_ENET_REGS, + .enet_regs_size = HPC1_ENET_REGS_SIZE, + .enet_intdelay = HPC1_ENET_INTDELAY, + .enet_intdelayval = HPC1_ENET_INTDELAY_OFF, + .enetr_cbp = HPC1_ENETR_CBP, + .enetr_ndbp = HPC1_ENETR_NDBP, + .enetr_bc = HPC1_ENETR_BC, + .enetr_ctl = HPC1_ENETR_CTL, + .enetr_ctl_active = HPC1_ENETR_CTL_ACTIVE, + .enetr_reset = HPC1_ENETR_RESET, + .enetr_dmacfg = 0, + .enetr_piocfg = 0, + .enetx_cbp = HPC1_ENETX_CBP, + .enetx_ndbp = HPC1_ENETX_NDBP, + .enetx_bc = HPC1_ENETX_BC, + .enetx_ctl = HPC1_ENETX_CTL, + .enetx_ctl_active = HPC1_ENETX_CTL_ACTIVE, + .enetx_dev = 0, + .enetr_fifo = HPC1_ENETR_FIFO, + .enetr_fifo_size = HPC1_ENETR_FIFO_SIZE, + .enetx_fifo = HPC1_ENETX_FIFO, + .enetx_fifo_size = HPC1_ENETX_FIFO_SIZE, + .enet_dma_boundary = 4096, + .enet_devregs = HPC1_ENET_DEVREGS, + .enet_devregs_size = HPC1_ENET_DEVREGS_SIZE, + .pbus_fifo = 0, + .pbus_fifo_size = 0, + .pbus_bbram = 0, +#define MAX_SCSI_XFER (roundup(MAXPHYS, PAGE_SIZE)) + .scsi_dma_segs = (MAX_SCSI_XFER / 4096), + .scsi_dma_segs_size = 4096, + .scsi_dma_datain_cmd = (HPC1_SCSI_DMACTL_ACTIVE | HPC1_SCSI_DMACTL_DIR), + .scsi_dma_dataout_cmd = HPC1_SCSI_DMACTL_ACTIVE, + .scsi_dmactl_flush = HPC1_SCSI_DMACTL_FLUSH, + .scsi_dmactl_active = HPC1_SCSI_DMACTL_ACTIVE, + .scsi_dmactl_reset = HPC1_SCSI_DMACTL_RESET +}; + +static struct hpc_values hpc3_values = { + .revision = 3, + .scsi0_regs = HPC3_SCSI0_REGS, + .scsi0_regs_size = HPC3_SCSI0_REGS_SIZE, + .scsi0_cbp = HPC3_SCSI0_CBP, + .scsi0_ndbp = HPC3_SCSI0_NDBP, + .scsi0_bc = HPC3_SCSI0_BC, + .scsi0_ctl = HPC3_SCSI0_CTL, + .scsi0_gio = HPC3_SCSI0_GIO, + .scsi0_dev = HPC3_SCSI0_DEV, + .scsi0_dmacfg = HPC3_SCSI0_DMACFG, + .scsi0_piocfg = HPC3_SCSI0_PIOCFG, + .scsi1_regs = HPC3_SCSI1_REGS, + .scsi1_regs_size = HPC3_SCSI1_REGS_SIZE, + .scsi1_cbp = HPC3_SCSI1_CBP, + .scsi1_ndbp = HPC3_SCSI1_NDBP, + .scsi1_bc = HPC3_SCSI1_BC, + .scsi1_ctl = HPC3_SCSI1_CTL, + .scsi1_gio = HPC3_SCSI1_GIO, + .scsi1_dev = HPC3_SCSI1_DEV, + .scsi1_dmacfg = HPC3_SCSI1_DMACFG, + .scsi1_piocfg = HPC3_SCSI1_PIOCFG, + .enet_regs = HPC3_ENET_REGS, + .enet_regs_size = HPC3_ENET_REGS_SIZE, + .enet_intdelay = 0, + .enet_intdelayval = 0, + .enetr_cbp = HPC3_ENETR_CBP, + .enetr_ndbp = HPC3_ENETR_NDBP, + .enetr_bc = HPC3_ENETR_BC, + .enetr_ctl = HPC3_ENETR_CTL, + .enetr_ctl_active = HPC3_ENETR_CTL_ACTIVE, + .enetr_reset = HPC3_ENETR_RESET, + .enetr_dmacfg = HPC3_ENETR_DMACFG, + .enetr_piocfg = HPC3_ENETR_PIOCFG, + .enetx_cbp = HPC3_ENETX_CBP, + .enetx_ndbp = HPC3_ENETX_NDBP, + .enetx_bc = HPC3_ENETX_BC, + .enetx_ctl = HPC3_ENETX_CTL, + .enetx_ctl_active = HPC3_ENETX_CTL_ACTIVE, + .enetx_dev = HPC3_ENETX_DEV, + .enetr_fifo = HPC3_ENETR_FIFO, + .enetr_fifo_size = HPC3_ENETR_FIFO_SIZE, + .enetx_fifo = HPC3_ENETX_FIFO, + .enetx_fifo_size = HPC3_ENETX_FIFO_SIZE, + .enet_dma_boundary = 8192, + .enet_devregs = HPC3_ENET_DEVREGS, + .enet_devregs_size = HPC3_ENET_DEVREGS_SIZE, + .pbus_fifo = HPC3_PBUS_FIFO, + .pbus_fifo_size = HPC3_PBUS_FIFO_SIZE, + .pbus_bbram = HPC3_PBUS_BBRAM, + .scsi_dma_segs = (MAX_SCSI_XFER / 8192), + .scsi_dma_segs_size = 8192, + .scsi_dma_datain_cmd = HPC3_SCSI_DMACTL_ACTIVE, + .scsi_dma_dataout_cmd =(HPC3_SCSI_DMACTL_ACTIVE | HPC3_SCSI_DMACTL_DIR), + .scsi_dmactl_flush = HPC3_SCSI_DMACTL_FLUSH, + .scsi_dmactl_active = HPC3_SCSI_DMACTL_ACTIVE, + .scsi_dmactl_reset = HPC3_SCSI_DMACTL_RESET +}; + +int hpc_match(struct device *, void *, void *); +void hpc_attach(struct device *, struct device *, void *); +int hpc_print(void *, const char *); + +int hpc_revision(struct hpc_softc *, struct gio_attach_args *); +int hpc_submatch(struct device *, void *, void *); +int hpc_power_intr(void *); +void hpc_blink(void *); +void hpc_blink_ioc(void *); +int hpc_read_eeprom(int, bus_space_tag_t, bus_space_handle_t, uint8_t *, + size_t); + +const struct cfattach hpc_ca = { + sizeof(struct hpc_softc), hpc_match, hpc_attach +}; + +struct cfdriver hpc_cd = { + NULL, "hpc", DV_DULL +}; + +uint8_t hpc_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t); +uint16_t hpc_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t); +void hpc_read_raw_2(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + uint8_t *, bus_size_t); +void hpc_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint8_t); +void hpc_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint16_t); +void hpc_write_raw_2(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + const uint8_t *, bus_size_t); +uint32_t hpc_read_4(bus_space_tag_t, bus_space_handle_t, bus_size_t); +uint64_t hpc_read_8(bus_space_tag_t, bus_space_handle_t, bus_size_t); +void hpc_write_4(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint32_t); +void hpc_write_8(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint64_t); +void hpc_read_raw_4(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + uint8_t *, bus_size_t); +void hpc_write_raw_4(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + const uint8_t *, bus_size_t); +void hpc_read_raw_8(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + uint8_t *, bus_size_t); +void hpc_write_raw_8(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + const uint8_t *, bus_size_t); +int hpc_space_map(bus_space_tag_t, bus_addr_t, bus_size_t, int, + bus_space_handle_t *); +void hpc_space_unmap(bus_space_tag_t, bus_space_handle_t, bus_size_t); +int hpc_space_region(bus_space_tag_t, bus_space_handle_t, bus_size_t, + bus_size_t, bus_space_handle_t *); +void *hpc_space_vaddr(bus_space_tag_t, bus_space_handle_t); +void hpc_space_barrier(bus_space_tag_t, bus_space_handle_t, bus_size_t, + bus_size_t, int); + +int +hpc_match(struct device *parent, void *vcf, void *aux) +{ + struct gio_attach_args* ga = aux; + uint32_t dummy; + + /* Make sure it's actually there and readable */ + if (guarded_read_4(PHYS_TO_XKPHYS(ga->ga_addr, CCA_NC), &dummy) == 0) + return 1; + + return 0; +} + +void +hpc_attach(struct device *parent, struct device *self, void *aux) +{ + struct hpc_softc *sc = (struct hpc_softc *)self; + struct gio_attach_args* ga = aux; + struct hpc_attach_args ha; + const struct hpc_device *hd; + uint32_t dummy; + uint32_t hpctype; + int isonboard; + int isioplus; + int sysmask = 0; + + sc->sc_base = ga->ga_addr; + sc->sc_ct = ga->ga_iot; + sc->sc_ch = PHYS_TO_XKPHYS(sc->sc_base, CCA_NC); + sc->sc_dmat = ga->ga_dmat; + + switch (sys_config.system_type) { + case SGI_IP20: + sysmask = HPCDEV_IP20; + break; + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + if (sys_config.system_subtype == IP22_INDIGO2) + sysmask = HPCDEV_IP22; + else + sysmask = HPCDEV_IP24; + break; + }; + + if ((hpctype = hpc_revision(sc, ga)) == 0) { + printf(": could not identify HPC revision\n"); + return; + } + + /* force big-endian mode */ + if (hpctype == 15) + bus_space_write_4(sc->sc_ct, sc->sc_ch, HPC1_BIGENDIAN, 0); + + /* + * All machines have only one HPC on the mainboard itself. ''Extra'' + * HPCs require bus arbiter and other magic to run happily. + */ + isonboard = (sc->sc_base == HPC_BASE_ADDRESS_0); + isioplus = (sc->sc_base == HPC_BASE_ADDRESS_1 && hpctype == 3 && + sysmask == HPCDEV_IP24); + + printf(": SGI HPC%d%s (%s)\n", (hpctype == 3) ? 3 : 1, + (hpctype == 15) ? ".5" : "", (isonboard) ? "onboard" : + (isioplus) ? "IOPLUS mezzanine" : "GIO slot"); + + /* + * Configure the IOC. + */ + if (isonboard && sys_config.system_type != SGI_IP20) { + /* Reset IOC */ + bus_space_write_4(sc->sc_ct, sc->sc_ch, IOC_BASE + IOC_RESET, + IOC_RESET_PARALLEL | IOC_RESET_PCKBC | IOC_RESET_EISA | + IOC_RESET_ISDN | IOC_RESET_LED_GREEN ); + + /* + * Set the 10BaseT port to use UTP cable, set autoselect mode + * for the Ethernet interface (AUI vs. TP), set the two serial + * ports to PC mode. + */ + bus_space_write_4(sc->sc_ct, sc->sc_ch, IOC_BASE + IOC_WRITE, + IOC_WRITE_ENET_AUTO | IOC_WRITE_ENET_UTP | + IOC_WRITE_PC_UART2 | IOC_WRITE_PC_UART1); + + /* XXX: the firmware should have taken care of this already */ +#if 0 + if (sys_config.system_subtype == IP22_INDY) { + bus_space_write_4(sc->sc_ct, sc->sc_ch, + IOC_BASE + IOC_GCSEL, 0xff); + bus_space_write_4(sc->sc_ct, sc->sc_ch, + IOC_BASE + IOC_GCREG, 0xff); + } +#endif + } + + /* + * Configure the bus arbiter appropriately. + * + * In the case of Challenge S, we must tell the IOPLUS board which + * DMA channel to use (we steal it from one of the slots). SGI allows + * an HPC1.5 in slot 1, in which case IOPLUS must use EXP0, or any + * other DMA-capable board in slot 0, which leaves us to use EXP1. Of + * course, this means that only one GIO board may use DMA. + * + * Note that this never happens on Indigo2. + */ + if (isioplus) { + int arb_slot; + + if (guarded_read_4(PHYS_TO_XKPHYS(HPC_BASE_ADDRESS_2, CCA_NC), + &dummy) != 0) + arb_slot = GIO_SLOT_EXP1; + else + arb_slot = GIO_SLOT_EXP0; + + if (gio_arb_config(arb_slot, GIO_ARB_LB | GIO_ARB_MST | + GIO_ARB_64BIT | GIO_ARB_HPC2_64BIT)) { + printf("%s: failed to configure GIO bus arbiter\n", + sc->sc_dev.dv_xname); + return; + } + + printf("%s: using EXP%d's DMA channel\n", + sc->sc_dev.dv_xname, + (arb_slot == GIO_SLOT_EXP0) ? 0 : 1); + + bus_space_write_4(sc->sc_ct, sc->sc_ch, + HPC3_PBUS_CFGPIO_REGS, 0x0003ffff); + + if (arb_slot == GIO_SLOT_EXP0) + bus_space_write_4(sc->sc_ct, sc->sc_ch, + HPC3_PBUS_CH0_DEVREGS, 0x20202020); + else + bus_space_write_4(sc->sc_ct, sc->sc_ch, + HPC3_PBUS_CH0_DEVREGS, 0x30303030); + } else if (!isonboard) { + int arb_slot; + + arb_slot = (sc->sc_base == HPC_BASE_ADDRESS_1) ? + GIO_SLOT_EXP0 : GIO_SLOT_EXP1; + + if (gio_arb_config(arb_slot, GIO_ARB_RT | GIO_ARB_MST)) { + printf("%s: failed to configure GIO bus arbiter\n", + sc->sc_dev.dv_xname); + return; + } + } + + hpc_read_eeprom(hpctype, sc->sc_ct, sc->sc_ch, + ha.hpc_eeprom, sizeof(ha.hpc_eeprom)); + + hd = hpctype == 3 ? hpc3_devices : hpc1_devices; + for (; hd->hd_name != NULL; hd++) { + if (!(hd->hd_sysmask & sysmask) || hd->hd_base != sc->sc_base) + continue; + + ha.ha_name = hd->hd_name; + ha.ha_devoff = hd->hd_devoff; + ha.ha_dmaoff = hd->hd_dmaoff; + ha.ha_irq = hd->hd_irq; + + ha.ha_st = sc->sc_ct; + ha.ha_sh = sc->sc_ch; + ha.ha_dmat = sc->sc_dmat; + if (hpctype == 3) + ha.hpc_regs = &hpc3_values; + else + ha.hpc_regs = &hpc1_values; + ha.hpc_regs->revision = hpctype; + + /* + * XXX On hpc@gio boards such as the E++, this will cause + * XXX `wdsc not configured' messages (or sq on SCSI + * XXX boards. We need to move some device detection + * XXX in there, or figure out if there is a way to know + * XXX what is really connected. + */ + config_found_sm(self, &ha, hpc_print, hpc_submatch); + } + + /* + * Attach the clock chip as well if on hpc0. + */ + if (isonboard) { + if (sys_config.system_type == SGI_IP20) { + ha.ha_name = "dpclock"; + ha.ha_devoff = HPC1_PBUS_BBRAM; + } else { + ha.ha_name = "dsclock"; + ha.ha_devoff = HPC3_PBUS_BBRAM; + } + ha.ha_dmaoff = 0; + ha.ha_irq = -1; + ha.ha_st = sc->sc_ct; + ha.ha_sh = sc->sc_ch; + ha.ha_dmat = sc->sc_dmat; + ha.hpc_regs = NULL; + + config_found_sm(self, &ha, hpc_print, hpc_submatch); + + if (sys_config.system_type == SGI_IP20) { + timeout_set(&sc->sc_blink_tmo, hpc_blink, sc); + hpc_blink(sc); + } else { + timeout_set(&sc->sc_blink_tmo, hpc_blink_ioc, sc); + hpc_blink_ioc(sc); + } + } +} + +/* + * HPC revision detection isn't as simple as it should be. Devices probe + * differently depending on their slots, but luckily there is only one + * instance in which we have to decide the major revision (HPC1 vs HPC3). + * + * The HPC is found in the following configurations: + * o Indigo R4k + * One on-board HPC1 or HPC1.5. + * Up to two additional HPC1.5's in GIO slots 0 and 1. + * o Indy + * One on-board HPC3. + * Up to two additional HPC1.5's in GIO slots 0 and 1. + * o Challenge S + * One on-board HPC3. + * Up to one additional HPC3 on the IOPLUS board (if installed). + * Up to one additional HPC1.5 in slot 1 of the IOPLUS board. + * o Indigo2, Challenge M + * One on-board HPC3. + * + * All we really have to worry about is the IP22 case. + */ +int +hpc_revision(struct hpc_softc *sc, struct gio_attach_args *ga) +{ + uint32_t reg; + + /* No hardware ever supported the last hpc base address. */ + if (ga->ga_addr == HPC_BASE_ADDRESS_3) + return 0; + + switch (sys_config.system_type) { + case SGI_IP20: + if (guarded_read_4(PHYS_TO_XKPHYS(ga->ga_addr + HPC1_BIGENDIAN, + CCA_NC), ®) != 0) { + if (((reg >> HPC1_REVSHIFT) & HPC1_REVMASK) == + HPC1_REV15) + return 15; + else + return 1; + } + return 1; + + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + /* + * If IP22, probe slot 0 to determine if HPC1.5 or HPC3. Slot 1 + * must be HPC1.5. + */ + if (ga->ga_addr == HPC_BASE_ADDRESS_0) + return 3; + + if (ga->ga_addr == HPC_BASE_ADDRESS_2) + return 15; + + /* + * Probe for it. We use one of the PBUS registers. Note + * that this probe succeeds with my E++ adapter in slot 1 + * (bad), but it appears to always do the right thing in + * slot 0 (good!) and we're only worried about that one + * anyhow. + */ + if (guarded_read_4(PHYS_TO_XKPHYS(ga->ga_addr + + HPC3_PBUS_CH7_BP, CCA_NC), ®) != 0) + return 15; + else + return 3; + } + + return 0; +} + +int +hpc_submatch(struct device *parent, void *vcf, void *aux) +{ + struct cfdata *cf = (struct cfdata *)vcf; + struct hpc_attach_args *ha = (struct hpc_attach_args *)aux; + + if (cf->cf_loc[0 /*HPCCF_OFFSET*/] != -1 && + (bus_addr_t)cf->cf_loc[0 /*HPCCF_OFFSET*/] != ha->ha_devoff) + return 0; + + return (*cf->cf_attach->ca_match)(parent, cf, aux); +} + +int +hpc_print(void *aux, const char *pnp) +{ + struct hpc_attach_args *ha = aux; + + if (pnp) + printf("%s at %s", ha->ha_name, pnp); + + printf(" offset 0x%08lx", ha->ha_devoff); + if (ha->ha_irq >= 0) + printf(" irq %d", ha->ha_irq); + + return UNCONF; +} + +void +hpc_blink(void *arg) +{ + struct hpc_softc *sc = arg; + + bus_space_write_1(sc->sc_ct, sc->sc_ch, HPC1_AUX_REGS, + bus_space_read_1(sc->sc_ct, sc->sc_ch, HPC1_AUX_REGS) ^ + HPC1_AUX_CONSLED); + + timeout_add(&sc->sc_blink_tmo, + (((averunnable.ldavg[0] + FSCALE) * hz) >> (FSHIFT + 1))); +} + +void +hpc_blink_ioc(void *arg) +{ + struct hpc_softc *sc = arg; + uint32_t value; + + /* This is a bit odd. To strobe the green LED, we have to toggle the + red control bit. */ + value = bus_space_read_4(sc->sc_ct, sc->sc_ch, IOC_BASE + IOC_RESET) & + 0xff; + value ^= IOC_RESET_LED_RED; + bus_space_write_4(sc->sc_ct, sc->sc_ch, IOC_BASE + IOC_RESET, value); + + timeout_add(&sc->sc_blink_tmo, + (((averunnable.ldavg[0] + FSCALE) * hz) >> (FSHIFT + 1))); +} + +/* + * Read the eeprom associated with one of the HPC's. + * + * NB: An eeprom is not always present, but the HPC should be able to + * handle this gracefully. Any consumers should validate the data to + * ensure it's reasonable. + */ +int +hpc_read_eeprom(int hpctype, bus_space_tag_t t, bus_space_handle_t h, + uint8_t *buf, size_t len) +{ + struct seeprom_descriptor sd; + bus_space_handle_t bsh; + bus_size_t offset; + + if (!len || len & 0x1) + return (1); + + offset = (hpctype == 3) ? HPC3_EEPROM_DATA : HPC1_AUX_REGS; + + if (bus_space_subregion(t, h, offset, 1, &bsh) != 0) + return (1); + + sd.sd_chip = C56_66; + sd.sd_tag = t; + sd.sd_bsh = bsh; + sd.sd_regsize = 1; + sd.sd_control_offset = 0; + sd.sd_status_offset = 0; + sd.sd_dataout_offset = 0; + sd.sd_DI = 0x10; /* EEPROM -> CPU */ + sd.sd_DO = 0x08; /* CPU -> EEPROM */ + sd.sd_CK = 0x04; + sd.sd_CS = 0x02; + sd.sd_MS = 0; + sd.sd_RDY = 0; + + if (read_seeprom(&sd, (uint16_t *)buf, 0, len / 2) != 1) + return (1); + + bus_space_unmap(t, bsh, 1); + + return 0; +} diff --git a/sys/arch/sgi/hpc/hpcdma.c b/sys/arch/sgi/hpc/hpcdma.c new file mode 100644 index 00000000000..53dc86994ce --- /dev/null +++ b/sys/arch/sgi/hpc/hpcdma.c @@ -0,0 +1,208 @@ +/* $OpenBSD: hpcdma.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: hpcdma.c,v 1.21 2011/07/01 18:53:46 dyoung Exp $ */ + +/* + * Copyright (c) 2001 Wayne Knowles + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Wayne Knowles + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Support for SCSI DMA provided by the HPC. + * + * Note: We use SCSI0 offsets, etc. here. Since the layout of SCSI0 + * and SCSI1 are the same, this is no problem. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/buf.h> + +#include <uvm/uvm_extern.h> + +#include <machine/bus.h> + +#include <sgi/hpc/hpcreg.h> +#include <sgi/hpc/hpcvar.h> +#include <sgi/hpc/hpcdma.h> + +/* + * Allocate DMA Chain descriptor list + */ +void +hpcdma_init(struct hpc_attach_args *haa, struct hpc_dma_softc *sc, int ndesc) +{ + bus_dma_segment_t seg; + int rseg, allocsz; + + sc->sc_bst = haa->ha_st; + sc->sc_dmat = haa->ha_dmat; + sc->sc_ndesc = ndesc; + sc->sc_flags = 0; + + if (bus_space_subregion(haa->ha_st, haa->ha_sh, haa->ha_dmaoff, + sc->hpc->scsi0_regs_size, &sc->sc_bsh) != 0) { + printf(": can't map DMA registers\n"); + return; + } + + /* Alloc 1 additional descriptor - needed for DMA bug fix */ + allocsz = sizeof(struct hpc_dma_desc) * (ndesc + 1); + + /* + * Allocate a block of memory for dma chaining pointers + */ + if (bus_dmamem_alloc(sc->sc_dmat, allocsz, 0, 0, &seg, 1, &rseg, + BUS_DMA_NOWAIT)) { + printf(": can't allocate sglist\n"); + return; + } + /* Map pages into kernel memory */ + if (bus_dmamem_map(sc->sc_dmat, &seg, rseg, allocsz, + (caddr_t *)&sc->sc_desc_kva, BUS_DMA_NOWAIT)) { + printf(": can't map sglist\n"); + bus_dmamem_free(sc->sc_dmat, &seg, rseg); + return; + } + + if (bus_dmamap_create(sc->sc_dmat, allocsz, 1 /*seg*/, allocsz, 0, + BUS_DMA_WAITOK, &sc->sc_dmamap) != 0) { + printf(": failed to create dmamap\n"); + return; + } + + if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, + sc->sc_desc_kva, allocsz, NULL, BUS_DMA_NOWAIT)) { + printf(": can't load sglist\n"); + return; + } + + sc->sc_desc_pa = sc->sc_dmamap->dm_segs[0].ds_addr; +} + + +void +hpcdma_sglist_create(struct hpc_dma_softc *sc, bus_dmamap_t dmamap) +{ + struct hpc_dma_desc *hva; + bus_addr_t hpa; + bus_dma_segment_t *segp; + int i; + + KASSERT(dmamap->dm_nsegs <= sc->sc_ndesc); + + hva = sc->sc_desc_kva; + hpa = sc->sc_desc_pa; + segp = dmamap->dm_segs; + +#ifdef DMA_DEBUG + printf("DMA_SGLIST<"); +#endif + for (i = dmamap->dm_nsegs; i; i--) { +#ifdef DMA_DEBUG + printf("%p:%ld, ", (void *)segp->ds_addr, segp->ds_len); +#endif + hpa += sizeof(struct hpc_dma_desc); /* next chain desc */ + if (sc->hpc->revision == 3) { + hva->hpc3_hdd_bufptr = segp->ds_addr; + hva->hpc3_hdd_ctl = segp->ds_len; + hva->hdd_descptr = hpa; + } else /* HPC 1/1.5 */ { + /* + * there doesn't seem to be any good way of doing this + * via an abstraction layer + */ + hva->hpc1_hdd_bufptr = segp->ds_addr; + hva->hpc1_hdd_ctl = segp->ds_len; + hva->hdd_descptr = hpa; + } + ++hva; + ++segp; + } + + /* Work around HPC3 DMA bug */ + if (sc->hpc->revision == 3) { + hva->hpc3_hdd_bufptr = 0; + hva->hpc3_hdd_ctl = HPC3_HDD_CTL_EOCHAIN; + hva->hdd_descptr = 0; + } else { + hva--; + hva->hpc1_hdd_bufptr |= HPC1_HDD_CTL_EOCHAIN; + hva->hdd_descptr = 0; + } + +#ifdef DMA_DEBUG + printf(">\n"); +#endif + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, + 0, sizeof(struct hpc_dma_desc) * (dmamap->dm_nsegs + 1), + BUS_DMASYNC_PREWRITE); + + /* Load DMA Descriptor list */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ndbp, + sc->sc_desc_pa); +} + +void +hpcdma_cntl(struct hpc_dma_softc *sc, uint32_t mode) +{ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl, mode); +} + +void +hpcdma_reset(struct hpc_dma_softc *sc) +{ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl, + sc->hpc->scsi_dmactl_reset); + delay(100); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl, 0); + delay(1000); +} + +void +hpcdma_flush(struct hpc_dma_softc *sc) +{ + uint32_t mode; + + mode = bus_space_read_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, + sc->hpc->scsi0_ctl, mode | sc->hpc->scsi_dmactl_flush); + + /* Wait for Active bit to drop */ + while (bus_space_read_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl) & + sc->hpc->scsi_dmactl_active) { + bus_space_barrier(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl, 4, + BUS_SPACE_BARRIER_READ); + } +} diff --git a/sys/arch/sgi/hpc/hpcdma.h b/sys/arch/sgi/hpc/hpcdma.h new file mode 100644 index 00000000000..bca5fc5b171 --- /dev/null +++ b/sys/arch/sgi/hpc/hpcdma.h @@ -0,0 +1,63 @@ +/* $OpenBSD: hpcdma.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: hpcdma.h,v 1.11 2011/07/01 18:53:46 dyoung Exp $ */ + +/* + * Copyright (c) 2001 Wayne Knowles + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Wayne Knowles + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +struct hpc_dma_softc { + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + bus_dma_tag_t sc_dmat; + + uint32_t sc_flags; +#define HPCDMA_READ 0x20 /* direction of transfer */ +#define HPCDMA_LOADED 0x40 /* bus_dmamap loaded */ +#define HPCDMA_ACTIVE 0x80 /* DMA engine is busy */ + uint32_t sc_dmacmd; + int sc_ndesc; + bus_dmamap_t sc_dmamap; + struct hpc_dma_desc *sc_desc_kva; /* Virtual address */ + bus_addr_t sc_desc_pa; /* DMA address */ + ssize_t sc_dlen; /* number of bytes transfered */ + struct hpc_values *hpc; /* constants for HPC1/3 */ +}; + + +void hpcdma_init(struct hpc_attach_args *, struct hpc_dma_softc *, int); +void hpcdma_sglist_create(struct hpc_dma_softc *, bus_dmamap_t); +void hpcdma_cntl(struct hpc_dma_softc *, uint32_t); +void hpcdma_reset(struct hpc_dma_softc *); +void hpcdma_flush(struct hpc_dma_softc *); diff --git a/sys/arch/sgi/hpc/hpcreg.h b/sys/arch/sgi/hpc/hpcreg.h new file mode 100644 index 00000000000..bce6a2d2245 --- /dev/null +++ b/sys/arch/sgi/hpc/hpcreg.h @@ -0,0 +1,450 @@ +/* $OpenBSD: hpcreg.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: hpcreg.h,v 1.20 2011/01/25 12:21:04 tsutsui Exp $ */ + +/* + * Copyright (c) 2001 Rafal K. Boni + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * HPC locations are identical across all HPC-supported + * platforms. + */ +#define HPC_BASE_ADDRESS_0 0x1fb80000 /* Primary onboard */ +#define HPC_BASE_ADDRESS_1 0x1fb00000 +#define HPC_BASE_ADDRESS_2 0x1f980000 +#define HPC_BASE_ADDRESS_3 0x1f900000 /* NB: Never supported in h/w */ + +/* + * HPC3 descriptor layout. + */ +struct hpc_dma_desc { + uint32_t hdd_bufptr; /* Physical address of buffer */ + uint32_t hdd_ctl; /* Control flags and byte count */ + uint32_t hdd_descptr; /* Physical address of next descr. */ + uint32_t hdd_pad; /* Pad out to quadword alignment */ +}; + +/* + * The hdd_bufptr and hdd_ctl fields are swapped between HPC1 and + * HPC3. These fields are referenced by macro for readability. + */ +#define hpc1_hdd_ctl hdd_bufptr +#define hpc1_hdd_bufptr hdd_ctl +#define hpc3_hdd_ctl hdd_ctl +#define hpc3_hdd_bufptr hdd_bufptr + +/* + * Control flags + */ +#define HPC3_HDD_CTL_EOCHAIN 0x80000000 /* End of descriptor chain */ +#define HPC3_HDD_CTL_EOPACKET 0x40000000 /* Ethernet: end of packet */ +#define HPC3_HDD_CTL_INTR 0x20000000 /* Interrupt when finished */ +#define HPC3_HDD_CTL_XMITDONE 0x00008000 /* Ethernet transmit done */ +#define HPC3_HDD_CTL_OWN 0x00004000 /* CPU owns this frame */ + +#define HPC3_HDD_CTL_BYTECNT(x) ((x) & 0x3fff) /* Byte count: for ethernet + * rcv channel also doubles as + * length of packet received + */ + +/* + * HPC memory map, as offsets from HPC base + * + * XXXrkb: should each section be used as a base and have the specific + * registers offset from there?? + * + * XXX: define register values as well as their offsets. + * + */ +#define HPC3_PBUS_DMAREGS 0x00000000 /* DMA registers for PBus */ +#define HPC3_PBUS_DMAREGS_SIZE 0x0000ffff /* channels 0 - 7 */ + +#define HPC3_PBUS_CH0_BP 0x00000000 /* Chan 0 Buffer Ptr */ +#define HPC3_PBUS_CH0_DP 0x00000004 /* Chan 0 Descriptor Ptr */ +#define HPC3_PBUS_CH0_CTL 0x00001000 /* Chan 0 Control Register */ + +#define HPC3_PBUS_CH1_BP 0x00002000 /* Chan 1 Buffer Ptr */ +#define HPC3_PBUS_CH1_DP 0x00002004 /* Chan 1 Descriptor Ptr */ +#define HPC3_PBUS_CH1_CTL 0x00003000 /* Chan 1 Control Register */ + +#define HPC3_PBUS_CH2_BP 0x00004000 /* Chan 2 Buffer Ptr */ +#define HPC3_PBUS_CH2_DP 0x00004004 /* Chan 2 Descriptor Ptr */ +#define HPC3_PBUS_CH2_CTL 0x00005000 /* Chan 2 Control Register */ + +#define HPC3_PBUS_CH3_BP 0x00006000 /* Chan 3 Buffer Ptr */ +#define HPC3_PBUS_CH3_DP 0x00006004 /* Chan 3 Descriptor Ptr */ +#define HPC3_PBUS_CH3_CTL 0x00007000 /* Chan 3 Control Register */ + +#define HPC3_PBUS_CH4_BP 0x00008000 /* Chan 4 Buffer Ptr */ +#define HPC3_PBUS_CH4_DP 0x00008004 /* Chan 4 Descriptor Ptr */ +#define HPC3_PBUS_CH4_CTL 0x00009000 /* Chan 4 Control Register */ + +#define HPC3_PBUS_CH5_BP 0x0000a000 /* Chan 5 Buffer Ptr */ +#define HPC3_PBUS_CH5_DP 0x0000a004 /* Chan 5 Descriptor Ptr */ +#define HPC3_PBUS_CH5_CTL 0x0000b000 /* Chan 5 Control Register */ + +#define HPC3_PBUS_CH6_BP 0x0000c000 /* Chan 6 Buffer Ptr */ +#define HPC3_PBUS_CH6_DP 0x0000c004 /* Chan 6 Descriptor Ptr */ +#define HPC3_PBUS_CH6_CTL 0x0000d000 /* Chan 6 Control Register */ + +#define HPC3_PBUS_CH7_BP 0x0000e000 /* Chan 7 Buffer Ptr */ +#define HPC3_PBUS_CH7_DP 0x0000e004 /* Chan 7 Descriptor Ptr */ +#define HPC3_PBUS_CH7_CTL 0x0000f000 /* Chan 7 Control Register */ + +#define HPC3_SCSI0_REGS 0x00010000 /* SCSI channel 0 registers */ +#define HPC3_SCSI0_REGS_SIZE 0x00001fff + +#define HPC3_SCSI0_CBP 0x00000000 /* Current buffer ptr */ +#define HPC3_SCSI0_NDBP 0x00000004 /* Next descriptor ptr */ + +#define HPC3_SCSI0_BC 0x00001000 /* DMA byte count & flags */ +#define HPC3_SCSI0_CTL 0x00001004 /* DMA control flags */ +#define HPC3_SCSI0_GIO 0x00001008 /* GIO DMA FIFO pointer */ +#define HPC3_SCSI0_DEV 0x0000100c /* Device DMA FIFO pointer */ +#define HPC3_SCSI0_DMACFG 0x00001010 /* DMA configuration */ +#define HPC3_SCSI0_PIOCFG 0x00001014 /* PIO configuration */ + +#define HPC3_SCSI1_REGS 0x00012000 /* SCSI channel 1 registers */ +#define HPC3_SCSI1_REGS_SIZE 0x00001fff + +#define HPC3_SCSI1_CBP 0x00000000 /* Current buffer ptr */ +#define HPC3_SCSI1_NDBP 0x00000004 /* Next descriptor ptr */ + +#define HPC3_SCSI1_BC 0x00001000 /* DMA byte count & flags */ +#define HPC3_SCSI1_CTL 0x00001004 /* DMA control flags */ +#define HPC3_SCSI1_GIO 0x00001008 /* GIO DMA FIFO pointer */ +#define HPC3_SCSI1_DEV 0x0000100c /* Device DMA FIFO pointer */ +#define HPC3_SCSI1_DMACFG 0x00001010 /* DMA configuration */ +#define HPC3_SCSI1_PIOCFG 0x00001014 /* PIO configuration */ + +/* HPC3_SCSIx_CTL "SCSI control register" flags: */ +#define HPC3_SCSI_DMACTL_IRQ 0x01 /* IRQ asserted, dma done or parity */ +#define HPC3_SCSI_DMACTL_ENDIAN 0x02 /* DMA endian mode, 0=BE, 1=LE */ +#define HPC3_SCSI_DMACTL_DIR 0x04 /* DMA direction, 0=dev->mem, 1=mem->dev */ +#define HPC3_SCSI_DMACTL_FLUSH 0x08 /* Flush DMA FIFO's */ +#define HPC3_SCSI_DMACTL_ACTIVE 0x10 /* DMA channel is active */ +#define HPC3_SCSI_DMACTL_AMASK 0x20 /* DMA active inhibits PIO */ +#define HPC3_SCSI_DMACTL_RESET 0x40 /* Reset dma channel and ext. controller */ +#define HPC3_SCSI_DMACTL_PERR 0x80 /* Parity error: interface to controller */ + +/* HPC_PBUS_CHx_CTL read: */ +#define HPC3_PBUS_DMACTL_IRQ 0x01 /* IRQ asserted, DMA done */ +#define HPC3_PBUS_DMACTL_ISACT 0x02 /* DMA channel is active */ + +/* HPC_PBUS_CHx_CTL write: */ +#define HPC3_PBUS_DMACTL_ENDIAN 0x02 /* DMA endianness, 0=BE 1=LE */ +#define HPC3_PBUS_DMACTL_RECEIVE 0x04 /* DMA direction, 1=dev->mem, 0=mem->dev*/ +#define HPC3_PBUS_DMACTL_FLUSH 0x08 /* Flush DMA FIFO */ +#define HPC3_PBUS_DMACTL_ACT 0x10 /* Activate DMA channel */ +#define HPC3_PBUS_DMACTL_ACT_LD 0x20 /* Load enable for ACT */ +#define HPC3_PBUS_DMACTL_RT 0x40 /* Enable real time GIO service for DMA */ +#define HPC3_PBUS_DMACTL_HIGHWATER_SHIFT 8 +#define HPC3_PBUS_DMACTL_FIFOBEG_SHIFT 16 +#define HPC3_PBUS_DMACTL_FIFOEND_SHIFT 24 + +#define HPC3_ENET_REGS 0x00014000 /* Ethernet registers */ +#define HPC3_ENET_REGS_SIZE 0x00003fff + +#define HPC3_ENETR_CBP 0x00000000 /* Recv: Current buffer ptr */ +#define HPC3_ENETR_NDBP 0x00000004 /* Recv: Next descriptor ptr */ + +#define HPC3_ENETR_BC 0x00001000 /* Recv: DMA byte cnt/flags */ +#define HPC3_ENETR_CTL 0x00001004 /* Recv: DMA control flags */ + +#define HPC3_ENETR_CTL_STAT_5_0 0x003f /* Seeq irq status: bits 0-5 */ +#define HPC3_ENETR_CTL_STAT_6 0x0040 /* Irq status: late_rxdc */ +#define HPC3_ENETR_CTL_STAT_7 0x0080 /* Irq status: old/new bit */ +#define HPC3_ENETR_CTL_LENDIAN 0x0100 /* DMA channel endian mode */ +#define HPC3_ENETR_CTL_ACTIVE 0x0200 /* DMA channel active? */ +#define HPC3_ENETR_CTL_ACTIVE_MSK 0x0400 /* DMA channel active? */ +#define HPC3_ENETR_CTL_RBO 0x0800 /* Recv buffer overflow */ + +#define HPC3_ENETR_GIO 0x00001008 /* Recv: GIO DMA FIFO ptr */ +#define HPC3_ENETR_DEV 0x0000100c /* Recv: Device DMA FIFO ptr */ +#define HPC3_ENETR_RESET 0x00001014 /* Recv: Ethernet chip reset */ + +#define HPC3_ENETR_RESET_CH 0x0001 /* Reset controller & chan */ +#define HPC3_ENETR_RESET_CLRINT 0x0002 /* Clear channel interrupt */ +#define HPC3_ENETR_RESET_LOOPBK 0x0004 /* External loopback enable */ +#define HPC3_ENETR_RESET_CLRRBO 0x0008 /* Clear RBO condition (??) */ + +#define HPC3_ENETR_DMACFG 0x00001018 /* Recv: DMA configuration */ + +#define HPC3_ENETR_DMACFG_D1(_x) (((_x) << 0) & 0x000f) /* D1 gio_clk cycles */ +#define HPC3_ENETR_DMACFG_D2(_x) (((_x) << 4) & 0x00f0) /* D2 gio_clk cycles */ +#define HPC3_ENETR_DMACFG_D3(_x) (((_x) << 8) & 0x0f00) /* D3 gio_clk cycles */ +#define HPC3_ENETR_DMACFG_WRCTL 0x01000 /* Enable IPG write */ + +/* + * The following three bits work around bugs in the Seeq 8003; if you + * don't set them, the Seeq gets wonky pretty often. + */ +#define HPC3_ENETR_DMACFG_FIX_RXDC 0x02000 /* Clear EOP bits on RXDC */ +#define HPC3_ENETR_DMACFG_FIX_EOP 0x04000 /* Enable rxintr timeout */ +#define HPC3_ENETR_DMACFG_FIX_INTR 0x08000 /* Enable EOP timeout */ +#define HPC3_ENETR_DMACFG_TIMEOUT 0x30000 /* Timeout value for above two*/ + +#define HPC3_ENETR_PIOCFG 0x0000101c /* Recv: PIO configuration */ + +#define HPC3_ENETR_PIOCFG_P1(_x) (((_x) << 0) & 0x000f) /* P1 gio_clk cycles */ +#define HPC3_ENETR_PIOCFG_P2(_x) (((_x) << 4) & 0x00f0) /* P2 gio_clk cycles */ +#define HPC3_ENETR_PIOCFG_P3(_x) (((_x) << 8) & 0x0f00) /* P3 gio_clk cycles */ + +#define HPC3_ENETX_CBP 0x00002000 /* Xmit: Current buffer ptr */ +#define HPC3_ENETX_NDBP 0x00002004 /* Xmit: Next descriptor ptr */ + +#define HPC3_ENETX_BC 0x00003000 /* Xmit: DMA byte cnt/flags */ +#define HPC3_ENETX_CTL 0x00003004 /* Xmit: DMA control flags */ + +#define HPC3_ENETX_CTL_STAT_5_0 0x003f /* Seeq irq status: bits 0-5 */ +#define HPC3_ENETX_CTL_STAT_6 0x0040 /* Irq status: late_rxdc */ +#define HPC3_ENETX_CTL_STAT_7 0x0080 /* Irq status: old/new bit */ +#define HPC3_ENETX_CTL_LENDIAN 0x0100 /* DMA channel endian mode */ +#define HPC3_ENETX_CTL_ACTIVE 0x0200 /* DMA channel active? */ +#define HPC3_ENETX_CTL_ACTIVE_MSK 0x0400 /* DMA channel active? */ +#define HPC3_ENETX_CTL_RBO 0x0800 /* Recv buffer overflow */ + +#define HPC3_ENETX_GIO 0x00003008 /* Xmit: GIO DMA FIFO ptr */ +#define HPC3_ENETX_DEV 0x0000300c /* Xmit: Device DMA FIFO ptr */ + +#define HPC3_PBUS_FIFO 0x00020000 /* PBus DMA FIFO */ +#define HPC3_PBUS_FIFO_SIZE 0x00007fff /* PBus DMA FIFO size */ + +#define HPC3_SCSI0_FIFO 0x00028000 /* SCSI0 DMA FIFO */ +#define HPC3_SCSI0_FIFO_SIZE 0x00001fff /* SCSI0 DMA FIFO size */ + +#define HPC3_SCSI1_FIFO 0x0002a000 /* SCSI1 DMA FIFO */ +#define HPC3_SCSI1_FIFO_SIZE 0x00001fff /* SCSI1 DMA FIFO size */ + +#define HPC3_ENETR_FIFO 0x0002c000 /* Ether recv DMA FIFO */ +#define HPC3_ENETR_FIFO_SIZE 0x00001fff /* Ether recv DMA FIFO size */ + +#define HPC3_ENETX_FIFO 0x0002e000 /* Ether xmit DMA FIFO */ +#define HPC3_ENETX_FIFO_SIZE 0x00001fff /* Ether xmit DMA FIFO size */ + +/* + * HPCBUG: The interrupt status is split amongst two registers, and they're + * not even consecutive in the HPC address space. This is documented as a + * bug by SGI. + */ +#define HPC3_INTRSTAT_40 0x00030000 /* Interrupt stat, bits 4:0 */ +#define HPC3_INTRSTAT_95 0x0003000c /* Interrupt stat, bits 9:5 */ + +#define HPC3_GIO_MISC 0x00030004 /* GIO64 misc register */ + +#define HPC3_EEPROM_DATA 0x0003000b /* Serial EEPROM data reg. */ + /* (byte) */ + +#define HPC3_GIO_BUSERR 0x00030010 /* GIO64 bus error intr stat */ + +#define HPC3_SCSI0_DEVREGS 0x00044000 /* SCSI channel 0 chip regs */ +#define HPC3_SCSI1_DEVREGS 0x0004c000 /* SCSI channel 1 chip regs */ + +#define HPC3_ENET_DEVREGS 0x00054000 /* Ethernet chip registers */ +#define HPC3_ENET_DEVREGS_SIZE 0x000004ff /* Size of chip registers */ + +#define HPC3_PBUS_DEVREGS 0x00054000 /* PBus PIO chip registers */ +#define HPC3_PBUS_DEVREGS_SIZE 0x000003ff /* PBus PIO chip registers */ + +#define HPC3_PBUS_CH0_DEVREGS 0x00058000 /* PBus ch. 0 chip registers */ +#define HPC3_PBUS_CH0_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH1_DEVREGS 0x00058400 /* PBus ch. 1 chip registers */ +#define HPC3_PBUS_CH1_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH2_DEVREGS 0x00058800 /* PBus ch. 2 chip registers */ +#define HPC3_PBUS_CH2_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH3_DEVREGS 0x00058c00 /* PBus ch. 3 chip registers */ +#define HPC3_PBUS_CH3_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH4_DEVREGS 0x00059000 /* PBus ch. 4 chip registers */ +#define HPC3_PBUS_CH4_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH5_DEVREGS 0x00059400 /* PBus ch. 5 chip registers */ +#define HPC3_PBUS_CH5_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH6_DEVREGS 0x00059800 /* PBus ch. 6 chip registers */ +#define HPC3_PBUS_CH6_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH7_DEVREGS 0x00059c00 /* PBus ch. 7 chip registers */ +#define HPC3_PBUS_CH7_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH8_DEVREGS 0x0005a000 /* PBus ch. 8 chip registers */ +#define HPC3_PBUS_CH8_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH9_DEVREGS 0x0005a400 /* PBus ch. 9 chip registers */ +#define HPC3_PBUS_CH9_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH8_DEVREGS_2 0x0005a800 /* PBus ch. 8 chip registers */ +#define HPC3_PBUS_CH8_DEVREGS_2_SIZE 0x03ff + +#define HPC3_PBUS_CH9_DEVREGS_2 0x0005ac00 /* PBus ch. 9 chip registers */ +#define HPC3_PBUS_CH9_DEVREGS_2_SIZE 0x03ff + +#define HPC3_PBUS_CH8_DEVREGS_3 0x0005b000 /* PBus ch. 8 chip registers */ +#define HPC3_PBUS_CH8_DEVREGS_3_SIZE 0x03ff + +#define HPC3_PBUS_CH9_DEVREGS_3 0x0005b400 /* PBus ch. 9 chip registers */ +#define HPC3_PBUS_CH9_DEVREGS_3_SIZE 0x03ff + +#define HPC3_PBUS_CH8_DEVREGS_4 0x0005b800 /* PBus ch. 8 chip registers */ +#define HPC3_PBUS_CH8_DEVREGS_4_SIZE 0x03ff + +#define HPC3_PBUS_CH9_DEVREGS_4 0x0005bc00 /* PBus ch. 9 chip registers */ +#define HPC3_PBUS_CH9_DEVREGS_4_SIZE 0x03ff + +#define HPC3_PBUS_CFGDMA_REGS 0x0005c000 /* PBus DMA config registers */ +#define HPC3_PBUS_CFGDMA_REGS_SIZE 0x0fff + +#define HPC3_PBUS_CH0_CFGDMA 0x0005c000 /* PBus Ch. 0 DMA config */ +#define HPC3_PBUS_CH0_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CH1_CFGDMA 0x0005c200 /* PBus Ch. 1 DMA config */ +#define HPC3_PBUS_CH1_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CH2_CFGDMA 0x0005c400 /* PBus Ch. 2 DMA config */ +#define HPC3_PBUS_CH2_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CH3_CFGDMA 0x0005c600 /* PBus Ch. 3 DMA config */ +#define HPC3_PBUS_CH3_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CH4_CFGDMA 0x0005c800 /* PBus Ch. 4 DMA config */ +#define HPC3_PBUS_CH4_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CH5_CFGDMA 0x0005ca00 /* PBus Ch. 5 DMA config */ +#define HPC3_PBUS_CH5_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CH6_CFGDMA 0x0005cc00 /* PBus Ch. 6 DMA config */ +#define HPC3_PBUS_CH6_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CH7_CFGDMA 0x0005ce00 /* PBus Ch. 7 DMA config */ +#define HPC3_PBUS_CH7_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CFGPIO_REGS 0x0005d000 /* PBus PIO config registers */ +#define HPC3_PBUS_CFGPIO_REGS_SIZE 0x0fff + +#define HPC3_PBUS_CH0_CFGPIO 0x0005d000 /* PBus Ch. 0 PIO config */ +#define HPC3_PBUS_CH1_CFGPIO 0x0005d100 /* PBus Ch. 1 PIO config */ +#define HPC3_PBUS_CH2_CFGPIO 0x0005d200 /* PBus Ch. 2 PIO config */ +#define HPC3_PBUS_CH3_CFGPIO 0x0005d300 /* PBus Ch. 3 PIO config */ +#define HPC3_PBUS_CH4_CFGPIO 0x0005d400 /* PBus Ch. 4 PIO config */ +#define HPC3_PBUS_CH5_CFGPIO 0x0005d500 /* PBus Ch. 5 PIO config */ +#define HPC3_PBUS_CH6_CFGPIO 0x0005d600 /* PBus Ch. 6 PIO config */ +#define HPC3_PBUS_CH7_CFGPIO 0x0005d700 /* PBus Ch. 7 PIO config */ +#define HPC3_PBUS_CH8_CFGPIO 0x0005d800 /* PBus Ch. 8 PIO config */ +#define HPC3_PBUS_CH9_CFGPIO 0x0005d900 /* PBus Ch. 9 PIO config */ +#define HPC3_PBUS_CH8_CFGPIO_2 0x0005da00 /* PBus Ch. 8 PIO config */ +#define HPC3_PBUS_CH9_CFGPIO_2 0x0005db00 /* PBus Ch. 9 PIO config */ +#define HPC3_PBUS_CH8_CFGPIO_3 0x0005dc00 /* PBus Ch. 8 PIO config */ +#define HPC3_PBUS_CH9_CFGPIO_3 0x0005dd00 /* PBus Ch. 9 PIO config */ +#define HPC3_PBUS_CH8_CFGPIO_4 0x0005de00 /* PBus Ch. 8 PIO config */ +#define HPC3_PBUS_CH9_CFGPIO_4 0x0005df00 /* PBus Ch. 9 PIO config */ + +#define HPC3_PBUS_PROM_WE 0x0005e000 /* PBus boot-prom write + * enable register + */ + +#define HPC3_PBUS_PROM_SWAP 0x0005e800 /* PBus boot-prom chip-select + * swap register + */ + +#define HPC3_PBUS_GEN_OUT 0x0005f000 /* PBus general-purpose output + * register + */ + +#define HPC3_PBUS_BBRAM 0x00060000 /* PBus battery-backed RAM + * external registers + */ + +/* HPC1/HPC1.5 differs from HPC3 in several details. */ + +#define HPC1_HDD_CTL_EOCHAIN 0x80000000 /* End of descriptor chain */ +#define HPC1_HDD_CTL_EOPACKET 0x80000000 /* Ethernet: end of packet */ +#define HPC1_HDD_CTL_INTR 0x00008000 /* Interrupt when finished */ +#define HPC1_HDD_CTL_OWN 0x40000000 /* CPU owns this frame */ +#define HPC1_HDD_CTL_BYTECNT(x) ((x) & 0x1fff) /* Byte count: for ethernet */ +#define HPC1_BIGENDIAN 0x000000c0 /* Endianness:5 revision:2 */ +#define HPC1_REVSHIFT 0x00000006 /* Revision rshft */ +#define HPC1_REVMASK 0x00000003 /* Revision mask */ +#define HPC1_REV15 0x00000001 /* HPC Revision 1.5 */ +#define HPC1_SCSI0_REGS 0x00000088 +#define HPC1_SCSI0_REGS_SIZE 0x00000018 +#define HPC1_SCSI0_CBP 0x00000004 /* Current buffer ptr */ +#define HPC1_SCSI0_NDBP 0x00000008 /* Next descriptor ptr */ +#define HPC1_SCSI0_BC 0x00000000 /* DMA byte count & flags */ +#define HPC1_SCSI0_CTL 0x0000000c /* DMA control flags */ +#define HPC1_SCSI0_DEV 0x00000014 /* Device DMA FIFO pointer */ +#define HPC1_SCSI0_DMACFG 0x00000010 /* DMA configuration */ +#define HPC1_SCSI0_GIO 0x00001008 /* GIO DMA FIFO pointer */ +#define HPC1_SCSI0_PIOCFG 0x00001014 /* PIO configuration */ +#define HPC1_SCSI_DMACTL_RESET 0x01 /* Reset dma channel and ext. controller */ +#define HPC1_SCSI_DMACTL_FLUSH 0x02 /* Flush DMA FIFO's */ +#define HPC1_SCSI_DMACTL_DIR 0x10 /* DMA direction: 1=dev->mem, 0=mem->dev */ +#define HPC1_SCSI_DMACTL_ACTIVE 0x80 /* DMA channel is active */ +#define HPC1_ENET_REGS 0x00000000 /* Ethernet registers */ +#define HPC1_ENET_REGS_SIZE 0x00000100 +#define HPC1_ENET_INTDELAY 0x0000002c /* Interrupt Delay Count */ +#define HPC1_ENET_INTDELAY_OFF 0x01000000 /* Disable Interrupt Delay */ +#define HPC1_ENETR_CBP 0x00000054 /* Recv: Current buffer ptr */ +#define HPC1_ENETR_NDBP 0x00000050 /* Recv: Next descriptor ptr */ +#define HPC1_ENETR_BC 0x00000048 /* Recv: DMA byte cnt/flags */ +#define HPC1_ENETR_CTL 0x00000038 /* Recv: DMA control flags */ +#define HPC1_ENETR_CTL_ACTIVE 0x00004000 /* DMA channel active? */ +#define HPC1_ENETR_RESET 0x0000003c /* Recv: Ethernet chip reset */ +#define HPC1_ENETR_RESET_CH 0x0001 /* Reset controller & chan */ +#define HPC1_ENETR_RESET_CLRINT 0x0002 /* Clear channel interrupt */ +#define HPC1_ENETR_RESET_LOOPBK 0x0004 /* External loopback enable */ +#define HPC1_ENETR_RESET_CLRRBO 0x0008 /* Clear RBO condition (??) */ +#define HPC1_ENETX_CBP 0x00000020 /* Xmit: Current buffer ptr */ +#define HPC1_ENETX_NDBP 0x00000010 /* Xmit: Next descriptor ptr */ +#define HPC1_ENETX_CFXBP 0x00000024 /* Xmit: Current first buf */ +#define HPC1_ENETX_PFXBP 0x00000028 /* Xmit: Prev. first buf */ +#define HPC1_ENETX_BC 0x00000014 /* Xmit: DMA byte cnt/flags */ +#define HPC1_ENETX_CTL 0x00000034 /* Xmit: DMA control flags */ +#define HPC1_ENETX_CTL_ACTIVE 0x00400000 +#define HPC1_ENETR_FIFO 0x0002c000 /* Ether recv DMA FIFO */ +#define HPC1_ENETR_FIFO_SIZE 0x00001fff /* Ether recv DMA FIFO size */ +#define HPC1_ENETX_FIFO 0x0002e000 /* Ether xmit DMA FIFO */ +#define HPC1_ENETX_FIFO_SIZE 0x00001fff /* Ether xmit DMA FIFO size */ +#define HPC1_SCSI0_DEVREGS 0x0000011f /* (actually 122) */ +#define HPC1_ENET_DEVREGS 0x00000100 /* Ethernet chip registers */ +#define HPC1_ENET_DEVREGS_SIZE 0x00000020 /* Size of chip registers */ +#define HPC1_PBUS_BBRAM 0x00000e00 /* PBus battery-backed RAM */ +#define HPC1_LPT_REGS 0x000000a8 /* LPT HPC Registers */ +#define HPC1_LPT_REGS_SIZE 0x00000018 +#define HPC1_LPT_BC 0x00000000 /* Byte Count */ +#define HPC1_LPT_CBP 0x00000004 /* Current Buffer Ptr */ +#define HPC1_LPT_NDBP 0x00000008 /* Next Buffer Ptr */ +#define HPC1_LPT_CTL 0x0000000c /* DMA Control Flags */ +#define HPC1_LPT_DEV 0x00000010 /* DMA Fifo Ptr */ +#define HPC1_LPT_DMACFG 0x00000014 /* DMA Configuration */ +#define HPC1_LPT_DEVREGS 0x00000132 /* Ext. Parallel Registers */ +#define HPC1_LPT_DEVREGS_SIZE 0x00000001 /* Size of External Registers */ + +/* AUX regs on the primary HPC */ +#define HPC1_AUX_REGS 0x000001bf /* EEPROM/LED Control (byte) */ +#define HPC1_AUX_CONSLED 0x01 /* Console LED */ diff --git a/sys/arch/sgi/hpc/hpcvar.h b/sys/arch/sgi/hpc/hpcvar.h new file mode 100644 index 00000000000..c79f284ff87 --- /dev/null +++ b/sys/arch/sgi/hpc/hpcvar.h @@ -0,0 +1,109 @@ +/* $OpenBSD: hpcvar.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: hpcvar.h,v 1.12 2011/01/25 12:21:04 tsutsui Exp $ */ + +/* + * Copyright (c) 2001 Rafal K. Boni + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define HPCDEV_IP20 (1U << 1) /* Indigo R4k */ +#define HPCDEV_IP22 (1U << 2) /* Indigo2 */ +#define HPCDEV_IP24 (1U << 3) /* Indy */ + +/* HPC 1.5/3 differ a bit, thus we need an abstraction layer */ + +struct hpc_values { + int revision; + uint32_t scsi0_regs; + uint32_t scsi0_regs_size; + uint32_t scsi0_cbp; + uint32_t scsi0_ndbp; + uint32_t scsi0_bc; + uint32_t scsi0_ctl; + uint32_t scsi0_gio; + uint32_t scsi0_dev; + uint32_t scsi0_dmacfg; + uint32_t scsi0_piocfg; + uint32_t scsi1_regs; + uint32_t scsi1_regs_size; + uint32_t scsi1_cbp; + uint32_t scsi1_ndbp; + uint32_t scsi1_bc; + uint32_t scsi1_ctl; + uint32_t scsi1_gio; + uint32_t scsi1_dev; + uint32_t scsi1_dmacfg; + uint32_t scsi1_piocfg; + uint32_t enet_regs; + uint32_t enet_regs_size; + uint32_t enet_intdelay; + uint32_t enet_intdelayval; + uint32_t enetr_cbp; + uint32_t enetr_ndbp; + uint32_t enetr_bc; + uint32_t enetr_ctl; + uint32_t enetr_ctl_active; + uint32_t enetr_reset; + uint32_t enetr_dmacfg; + uint32_t enetr_piocfg; + uint32_t enetx_cbp; + uint32_t enetx_ndbp; + uint32_t enetx_bc; + uint32_t enetx_ctl; + uint32_t enetx_ctl_active; + uint32_t enetx_dev; + uint32_t enetr_fifo; + uint32_t enetr_fifo_size; + uint32_t enetx_fifo; + uint32_t enetx_fifo_size; + uint32_t enet_dma_boundary; + uint32_t enet_devregs; + uint32_t enet_devregs_size; + uint32_t pbus_fifo; + uint32_t pbus_fifo_size; + uint32_t pbus_bbram; + uint32_t scsi_dma_segs; + uint32_t scsi_dma_segs_size; + uint32_t scsi_dma_datain_cmd; + uint32_t scsi_dma_dataout_cmd; + uint32_t scsi_dmactl_flush; + uint32_t scsi_dmactl_active; + uint32_t scsi_dmactl_reset; +}; + +struct hpc_attach_args { + const char *ha_name; /* name of device */ + bus_addr_t ha_devoff; /* offset of device */ + bus_addr_t ha_dmaoff; /* offset of DMA regs */ + int ha_irq; /* interrupt line */ + + bus_space_tag_t ha_st; /* HPC space tag */ + bus_space_handle_t ha_sh; /* HPC space handle XXX */ + bus_dma_tag_t ha_dmat; /* HPC DMA tag */ + + struct hpc_values *hpc_regs; /* HPC register definitions */ + + uint8_t hpc_eeprom[256];/* HPC eeprom contents */ +}; diff --git a/sys/arch/sgi/hpc/if_sq.c b/sys/arch/sgi/hpc/if_sq.c new file mode 100644 index 00000000000..44521062c38 --- /dev/null +++ b/sys/arch/sgi/hpc/if_sq.c @@ -0,0 +1,1348 @@ +/* $OpenBSD: if_sq.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: if_sq.c,v 1.42 2011/07/01 18:53:47 dyoung Exp $ */ + +/* + * Copyright (c) 2001 Rafal K. Boni + * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Portions of this code are derived from software contributed to The + * NetBSD Foundation by Jason R. Thorpe of the Numerical Aerospace + * Simulation Facility, NASA Ames Research Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/timeout.h> +#include <sys/mbuf.h> +#include <sys/pool.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/syslog.h> + +#include <uvm/uvm_extern.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#endif + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#include <netinet/if_ether.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> +#include <machine/cpu.h> /* guarded_read_4 */ +#include <machine/intr.h> +#include <mips64/arcbios.h> /* bios_enaddr */ +#include <sgi/localbus/intvar.h> + +#include <dev/ic/seeq8003reg.h> + +#include <sgi/hpc/hpcvar.h> +#include <sgi/hpc/hpcreg.h> +#include <sgi/hpc/if_sqvar.h> + +/* + * Short TODO list: + * (1) Do counters for bad-RX packets. + * (2) Allow multi-segment transmits, instead of copying to a single, + * contiguous mbuf. + * (3) Verify sq_stop() turns off enough stuff; I was still getting + * seeq interrupts after sq_stop(). + * (4) Implement EDLC modes: especially packet auto-pad and simplex + * mode. + * (5) Should the driver filter out its own transmissions in non-EDLC + * mode? + * (6) Multicast support -- multicast filter, address management, ... + * (7) Deal with RB0 (recv buffer overflow) on reception. Will need + * to figure out if RB0 is read-only as stated in one spot in the + * HPC spec or read-write (ie, is the 'write a one to clear it') + * the correct thing? + */ + +#if defined(SQ_DEBUG) +int sq_debug = 0; +#define SQ_DPRINTF(x) do { if (sq_debug) printf x; } while (0) +#else +#define SQ_DPRINTF(x) do { } while (0) +#endif + +int sq_match(struct device *, void *, void *); +void sq_attach(struct device *, struct device *, void *); +int sq_init(struct ifnet *); +void sq_start(struct ifnet *); +void sq_stop(struct ifnet *); +void sq_watchdog(struct ifnet *); +int sq_ioctl(struct ifnet *, u_long, caddr_t); + +void sq_set_filter(struct sq_softc *); +int sq_intr(void *); +void sq_rxintr(struct sq_softc *); +void sq_txintr(struct sq_softc *); +void sq_txring_hpc1(struct sq_softc *); +void sq_txring_hpc3(struct sq_softc *); +void sq_reset(struct sq_softc *); +int sq_add_rxbuf(struct sq_softc *, int); +#ifdef SQ_DEBUG +void sq_trace_dump(struct sq_softc *); +#endif + +const struct cfattach sq_ca = { + sizeof(struct sq_softc), sq_match, sq_attach +}; + +struct cfdriver sq_cd = { + NULL, "sq", DV_IFNET +}; + +/* XXX these values should be moved to <net/if_ether.h> ? */ +#define ETHER_PAD_LEN (ETHER_MIN_LEN - ETHER_CRC_LEN) + +#define sq_seeq_read(sc, off) \ + bus_space_read_1(sc->sc_regt, sc->sc_regh, ((off) << 2) | 3) +#define sq_seeq_write(sc, off, val) \ + bus_space_write_1(sc->sc_regt, sc->sc_regh, ((off) << 2) | 3, val) + +#define sq_hpc_read(sc, off) \ + bus_space_read_4(sc->sc_hpct, sc->sc_hpch, off) +#define sq_hpc_write(sc, off, val) \ + bus_space_write_4(sc->sc_hpct, sc->sc_hpch, off, val) + +/* MAC address offset for non-onboard implementations */ +#define SQ_HPC_EEPROM_ENADDR 250 + +#define SGI_OUI_0 0x08 +#define SGI_OUI_1 0x00 +#define SGI_OUI_2 0x69 + +int +sq_match(struct device *parent, void *vcf, void *aux) +{ + struct hpc_attach_args *ha = aux; + struct cfdata *cf = vcf; + vaddr_t reset, txstat; + uint32_t dummy; + + if (strcmp(ha->ha_name, cf->cf_driver->cd_name) != 0) + return 0; + + reset = PHYS_TO_XKPHYS(ha->ha_sh + ha->ha_dmaoff + + ha->hpc_regs->enetr_reset, CCA_NC); + txstat = PHYS_TO_XKPHYS(ha->ha_sh + ha->ha_devoff + (SEEQ_TXSTAT << 2), + CCA_NC); + + if (guarded_read_4(reset, &dummy) != 0) + return 0; + + *(volatile uint32_t *)reset = 0x1; + delay(20); + *(volatile uint32_t *)reset = 0x0; + + if (guarded_read_4(txstat, &dummy) != 0) + return 0; + + if ((*(volatile uint32_t *)txstat & 0xff) != TXSTAT_OLDNEW) + return 0; + + return 1; +} + +void +sq_attach(struct device *parent, struct device *self, void *aux) +{ + struct sq_softc *sc = (struct sq_softc *)self; + struct hpc_attach_args *haa = aux; + struct ifnet *ifp = &sc->sc_ac.ac_if; + int i, rc; + + sc->sc_hpct = haa->ha_st; + sc->hpc_regs = haa->hpc_regs; /* HPC register definitions */ + + if ((rc = bus_space_subregion(haa->ha_st, haa->ha_sh, + haa->ha_dmaoff, sc->hpc_regs->enet_regs_size, + &sc->sc_hpch)) != 0) { + printf(": can't HPC DMA registers, error = %d\n", rc); + goto fail_0; + } + + sc->sc_regt = haa->ha_st; + if ((rc = bus_space_subregion(haa->ha_st, haa->ha_sh, + haa->ha_devoff, sc->hpc_regs->enet_devregs_size, + &sc->sc_regh)) != 0) { + printf(": can't map Seeq registers, error = %d\n", rc); + goto fail_0; + } + + sc->sc_dmat = haa->ha_dmat; + + if ((rc = bus_dmamem_alloc(sc->sc_dmat, sizeof(struct sq_control), + sc->hpc_regs->enet_dma_boundary, + sc->hpc_regs->enet_dma_boundary, &sc->sc_cdseg, 1, + &sc->sc_ncdseg, BUS_DMA_NOWAIT)) != 0) { + printf(": unable to allocate control data, error = %d\n", rc); + goto fail_0; + } + + if ((rc = bus_dmamem_map(sc->sc_dmat, &sc->sc_cdseg, sc->sc_ncdseg, + sizeof(struct sq_control), (caddr_t *)&sc->sc_control, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) { + printf(": unable to map control data, error = %d\n", rc); + goto fail_1; + } + + if ((rc = bus_dmamap_create(sc->sc_dmat, + sizeof(struct sq_control), 1, sizeof(struct sq_control), + sc->hpc_regs->enet_dma_boundary, BUS_DMA_NOWAIT, + &sc->sc_cdmap)) != 0) { + printf(": unable to create DMA map for control data, error " + "= %d\n", rc); + goto fail_2; + } + + if ((rc = bus_dmamap_load(sc->sc_dmat, sc->sc_cdmap, + sc->sc_control, sizeof(struct sq_control), NULL, + BUS_DMA_NOWAIT)) != 0) { + printf(": unable to load DMA map for control data, error " + "= %d\n", rc); + goto fail_3; + } + + memset(sc->sc_control, 0, sizeof(struct sq_control)); + + /* Create transmit buffer DMA maps */ + for (i = 0; i < SQ_NTXDESC; i++) { + if ((rc = bus_dmamap_create(sc->sc_dmat, + MCLBYTES, 1, MCLBYTES, 0, + BUS_DMA_NOWAIT, &sc->sc_txmap[i])) != 0) { + printf(": unable to create tx DMA map %d, error = %d\n", + i, rc); + goto fail_4; + } + } + + /* Create receive buffer DMA maps */ + for (i = 0; i < SQ_NRXDESC; i++) { + if ((rc = bus_dmamap_create(sc->sc_dmat, + MCLBYTES, 1, MCLBYTES, 0, + BUS_DMA_NOWAIT, &sc->sc_rxmap[i])) != 0) { + printf(": unable to create rx DMA map %d, error = %d\n", + i, rc); + goto fail_5; + } + } + + /* Pre-allocate the receive buffers. */ + for (i = 0; i < SQ_NRXDESC; i++) { + if ((rc = sq_add_rxbuf(sc, i)) != 0) { + printf(": unable to allocate or map rx buffer %d\n," + " error = %d\n", i, rc); + goto fail_6; + } + } + + bcopy(&haa->hpc_eeprom[SQ_HPC_EEPROM_ENADDR], sc->sc_ac.ac_enaddr, + ETHER_ADDR_LEN); + + /* + * If our mac address is bogus, obtain it from ARCBIOS. This will + * be true of the onboard HPC3 on IP22, since there is no eeprom, + * but rather the DS1386 RTC's battery-backed ram is used. + */ + if (sc->sc_ac.ac_enaddr[0] != SGI_OUI_0 || + sc->sc_ac.ac_enaddr[1] != SGI_OUI_1 || + sc->sc_ac.ac_enaddr[2] != SGI_OUI_2) + enaddr_aton(bios_enaddr, sc->sc_ac.ac_enaddr); + + if ((int2_intr_establish(haa->ha_irq, IPL_NET, sq_intr, sc, + self->dv_xname)) == NULL) { + printf(": unable to establish interrupt!\n"); + goto fail_6; + } + + /* Reset the chip to a known state. */ + sq_reset(sc); + + /* + * Determine if we're an 8003 or 80c03 by setting the first + * MAC address register to non-zero, and then reading it back. + * If it's zero, we have an 80c03, because we will have read + * the TxCollLSB register. + */ + sq_seeq_write(sc, SEEQ_TXCOLLS0, 0xa5); + if (sq_seeq_read(sc, SEEQ_TXCOLLS0) == 0) + sc->sc_type = SQ_TYPE_80C03; + else + sc->sc_type = SQ_TYPE_8003; + sq_seeq_write(sc, SEEQ_TXCOLLS0, 0x00); + + printf(": Seeq %s, address %s\n", + sc->sc_type == SQ_TYPE_80C03 ? "80c03" : "8003", + ether_sprintf(sc->sc_ac.ac_enaddr)); + + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_mtu = ETHERMTU; + ifp->if_start = sq_start; + ifp->if_ioctl = sq_ioctl; + ifp->if_watchdog = sq_watchdog; + ifp->if_flags = IFF_BROADCAST | IFF_NOTRAILERS | IFF_MULTICAST; + IFQ_SET_READY(&ifp->if_snd); + + if_attach(ifp); + IFQ_SET_MAXLEN(&ifp->if_snd, SQ_NTXDESC - 1); + ether_ifattach(ifp); + + /* Done! */ + return; + + /* + * Free any resources we've allocated during the failed attach + * attempt. Do this in reverse order and fall through. + */ + fail_6: + for (i = 0; i < SQ_NRXDESC; i++) { + if (sc->sc_rxmbuf[i] != NULL) { + bus_dmamap_unload(sc->sc_dmat, sc->sc_rxmap[i]); + m_freem(sc->sc_rxmbuf[i]); + } + } + fail_5: + for (i = 0; i < SQ_NRXDESC; i++) { + if (sc->sc_rxmap[i] != NULL) + bus_dmamap_destroy(sc->sc_dmat, sc->sc_rxmap[i]); + } + fail_4: + for (i = 0; i < SQ_NTXDESC; i++) { + if (sc->sc_txmap[i] != NULL) + bus_dmamap_destroy(sc->sc_dmat, sc->sc_txmap[i]); + } + bus_dmamap_unload(sc->sc_dmat, sc->sc_cdmap); + fail_3: + bus_dmamap_destroy(sc->sc_dmat, sc->sc_cdmap); + fail_2: + bus_dmamem_unmap(sc->sc_dmat, + (void *)sc->sc_control, sizeof(struct sq_control)); + fail_1: + bus_dmamem_free(sc->sc_dmat, &sc->sc_cdseg, sc->sc_ncdseg); + fail_0: + return; +} + +/* Set up data to get the interface up and running. */ +int +sq_init(struct ifnet *ifp) +{ + struct sq_softc *sc = ifp->if_softc; + int i; + + /* Cancel any in-progress I/O */ + sq_stop(ifp); + + sc->sc_nextrx = 0; + + sc->sc_nfreetx = SQ_NTXDESC; + sc->sc_nexttx = sc->sc_prevtx = 0; + + SQ_TRACE(SQ_RESET, sc, 0, 0); + + /* Set into 8003 mode, bank 0 to program ethernet address */ + sq_seeq_write(sc, SEEQ_TXCMD, TXCMD_BANK0); + + /* Now write the address */ + for (i = 0; i < ETHER_ADDR_LEN; i++) + sq_seeq_write(sc, i, sc->sc_ac.ac_enaddr[i]); + + sc->sc_rxcmd = + RXCMD_IE_CRC | + RXCMD_IE_DRIB | + RXCMD_IE_SHORT | + RXCMD_IE_END | + RXCMD_IE_GOOD; + + /* + * Set the receive filter -- this will add some bits to the + * prototype RXCMD register. Do this before setting the + * transmit config register, since we might need to switch + * banks. + */ + sq_set_filter(sc); + + /* Set up Seeq transmit command register */ + sq_seeq_write(sc, SEEQ_TXCMD, + TXCMD_IE_UFLOW | TXCMD_IE_COLL | TXCMD_IE_16COLL | TXCMD_IE_GOOD); + + /* Now write the receive command register. */ + sq_seeq_write(sc, SEEQ_RXCMD, sc->sc_rxcmd); + + /* + * Set up HPC ethernet PIO and DMA configurations. + * + * The PROM appears to do most of this for the onboard HPC3, but + * not for the Challenge S's IOPLUS chip. We copy how the onboard + * chip is configured and assume that it's correct for both. + */ + if (sc->hpc_regs->revision == 3) { + uint32_t dmareg, pioreg; + + pioreg = + HPC3_ENETR_PIOCFG_P1(1) | + HPC3_ENETR_PIOCFG_P2(6) | + HPC3_ENETR_PIOCFG_P3(1); + + dmareg = + HPC3_ENETR_DMACFG_D1(6) | + HPC3_ENETR_DMACFG_D2(2) | + HPC3_ENETR_DMACFG_D3(0) | + HPC3_ENETR_DMACFG_FIX_RXDC | + HPC3_ENETR_DMACFG_FIX_INTR | + HPC3_ENETR_DMACFG_FIX_EOP | + HPC3_ENETR_DMACFG_TIMEOUT; + + sq_hpc_write(sc, HPC3_ENETR_PIOCFG, pioreg); + sq_hpc_write(sc, HPC3_ENETR_DMACFG, dmareg); + } + + /* Pass the start of the receive ring to the HPC */ + sq_hpc_write(sc, sc->hpc_regs->enetr_ndbp, SQ_CDRXADDR(sc, 0)); + + /* And turn on the HPC ethernet receive channel */ + sq_hpc_write(sc, sc->hpc_regs->enetr_ctl, + sc->hpc_regs->enetr_ctl_active); + + /* + * Turn off delayed receive interrupts on HPC1. + * (see Hollywood HPC Specification 2.1.4.3) + */ + if (sc->hpc_regs->revision != 3) + sq_hpc_write(sc, HPC1_ENET_INTDELAY, HPC1_ENET_INTDELAY_OFF); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + sq_start(ifp); + + return 0; +} + +void +sq_set_filter(struct sq_softc *sc) +{ + struct arpcom *ac = &sc->sc_ac; + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct ether_multi *enm; + struct ether_multistep step; + + /* + * Check for promiscuous mode. Also implies + * all-multicast. + */ + if (ifp->if_flags & IFF_PROMISC) { + sc->sc_rxcmd |= RXCMD_REC_ALL; + ifp->if_flags |= IFF_ALLMULTI; + return; + } + + /* + * The 8003 has no hash table. If we have any multicast + * addresses on the list, enable reception of all multicast + * frames. + * + * XXX The 80c03 has a hash table. We should use it. + */ + + ETHER_FIRST_MULTI(step, ac, enm); + + if (enm == NULL) { + sc->sc_rxcmd &= ~RXCMD_REC_MASK; + sc->sc_rxcmd |= RXCMD_REC_BROAD; + + ifp->if_flags &= ~IFF_ALLMULTI; + return; + } + + sc->sc_rxcmd |= RXCMD_REC_MULTI; + ifp->if_flags |= IFF_ALLMULTI; +} + +int +sq_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct sq_softc *sc = ifp->if_softc; + struct ifaddr *ifa = (struct ifaddr *)data; + int s, error = 0; + + SQ_TRACE(SQ_IOCTL, sc, 0, 0); + + s = splnet(); + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + if (!(ifp->if_flags & IFF_RUNNING)) + sq_init(ifp); +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) + arp_ifinit(&sc->sc_ac, ifa); +#endif + break; + + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING) + error = ENETRESET; + else + sq_init(ifp); + } else { + if (ifp->if_flags & IFF_RUNNING) + sq_stop(ifp); + } + break; + + default: + error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); + break; + } + + if (error == ENETRESET) { + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + if (ifp->if_flags & IFF_RUNNING) + error = sq_init(ifp); + else + error = 0; + } + + splx(s); + return error; +} + +void +sq_start(struct ifnet *ifp) +{ + struct sq_softc *sc = ifp->if_softc; + uint32_t status; + struct mbuf *m0, *m; + bus_dmamap_t dmamap; + int err, len, totlen, nexttx, firsttx, lasttx = -1, ofree, seg; + + if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING) + return; + + /* + * Remember the previous number of free descriptors and + * the first descriptor we'll use. + */ + ofree = sc->sc_nfreetx; + firsttx = sc->sc_nexttx; + + /* + * Loop through the send queue, setting up transmit descriptors + * until we drain the queue, or use up all available transmit + * descriptors. + */ + while (sc->sc_nfreetx != 0) { + /* + * Grab a packet off the queue. + */ + IFQ_POLL(&ifp->if_snd, m0); + if (m0 == NULL) + break; + m = NULL; + + dmamap = sc->sc_txmap[sc->sc_nexttx]; + + /* + * Load the DMA map. If this fails, the packet either + * didn't fit in the alloted number of segments, or we were + * short on resources. In this case, we'll copy and try + * again. + * Also copy it if we need to pad, so that we are sure there + * is room for the pad buffer. + * XXX the right way of doing this is to use a static buffer + * for padding and adding it to the transmit descriptor (see + * sys/dev/pci/if_tl.c for example). We can't do this here yet + * because we can't send packets with more than one fragment. + */ + len = m0->m_pkthdr.len; + if (len < ETHER_PAD_LEN || + bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m0, + BUS_DMA_NOWAIT) != 0) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + printf("%s: unable to allocate Tx mbuf\n", + sc->sc_dev.dv_xname); + break; + } + if (len > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + printf("%s: unable to allocate Tx " + "cluster\n", + sc->sc_dev.dv_xname); + m_freem(m); + break; + } + } + + m_copydata(m0, 0, len, mtod(m, void *)); + if (len < ETHER_PAD_LEN) { + memset(mtod(m, char *) + len, 0, + ETHER_PAD_LEN - len); + len = ETHER_PAD_LEN; + } + m->m_pkthdr.len = m->m_len = len; + + if ((err = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, + m, BUS_DMA_NOWAIT)) != 0) { + printf("%s: unable to load Tx buffer, " + "error = %d\n", + sc->sc_dev.dv_xname, err); + break; + } + } + + /* + * Ensure we have enough descriptors free to describe + * the packet. + */ + if (dmamap->dm_nsegs > sc->sc_nfreetx) { + /* + * Not enough free descriptors to transmit this + * packet. We haven't committed to anything yet, + * so just unload the DMA map, put the packet + * back on the queue, and punt. Notify the upper + * layer that there are no more slots left. + * + * XXX We could allocate an mbuf and copy, but + * XXX it is worth it? + */ + ifp->if_flags |= IFF_OACTIVE; + bus_dmamap_unload(sc->sc_dmat, dmamap); + if (m != NULL) + m_freem(m); + break; + } + + IFQ_DEQUEUE(&ifp->if_snd, m0); +#if NBPFILTER > 0 + /* + * Pass the packet to any BPF listeners. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT); +#endif + if (m != NULL) { + m_freem(m0); + m0 = m; + } + + /* + * WE ARE NOW COMMITTED TO TRANSMITTING THE PACKET. + */ + + SQ_TRACE(SQ_ENQUEUE, sc, sc->sc_nexttx, 0); + + /* Sync the DMA map. */ + bus_dmamap_sync(sc->sc_dmat, dmamap, 0, dmamap->dm_mapsize, + BUS_DMASYNC_PREWRITE); + + /* + * Initialize the transmit descriptors. + */ + for (nexttx = sc->sc_nexttx, seg = 0, totlen = 0; + seg < dmamap->dm_nsegs; + seg++, nexttx = SQ_NEXTTX(nexttx)) { + if (sc->hpc_regs->revision == 3) { + sc->sc_txdesc[nexttx].hpc3_hdd_bufptr = + dmamap->dm_segs[seg].ds_addr; + sc->sc_txdesc[nexttx].hpc3_hdd_ctl = + dmamap->dm_segs[seg].ds_len; + } else { + sc->sc_txdesc[nexttx].hpc1_hdd_bufptr = + dmamap->dm_segs[seg].ds_addr; + sc->sc_txdesc[nexttx].hpc1_hdd_ctl = + dmamap->dm_segs[seg].ds_len; + } + sc->sc_txdesc[nexttx].hdd_descptr = + SQ_CDTXADDR(sc, SQ_NEXTTX(nexttx)); + lasttx = nexttx; + totlen += dmamap->dm_segs[seg].ds_len; + } + + /* Last descriptor gets end-of-packet */ + KASSERT(lasttx != -1); + if (sc->hpc_regs->revision == 3) + sc->sc_txdesc[lasttx].hpc3_hdd_ctl |= + HPC3_HDD_CTL_EOPACKET; + else + sc->sc_txdesc[lasttx].hpc1_hdd_ctl |= + HPC1_HDD_CTL_EOPACKET; + + SQ_DPRINTF(("%s: transmit %d-%d, len %d\n", + sc->sc_dev.dv_xname, sc->sc_nexttx, lasttx, totlen)); + + if (ifp->if_flags & IFF_DEBUG) { + printf(" transmit chain:\n"); + for (seg = sc->sc_nexttx;; seg = SQ_NEXTTX(seg)) { + printf(" descriptor %d:\n", seg); + printf(" hdd_bufptr: 0x%08x\n", + (sc->hpc_regs->revision == 3) ? + sc->sc_txdesc[seg].hpc3_hdd_bufptr : + sc->sc_txdesc[seg].hpc1_hdd_bufptr); + printf(" hdd_ctl: 0x%08x\n", + (sc->hpc_regs->revision == 3) ? + sc->sc_txdesc[seg].hpc3_hdd_ctl: + sc->sc_txdesc[seg].hpc1_hdd_ctl); + printf(" hdd_descptr: 0x%08x\n", + sc->sc_txdesc[seg].hdd_descptr); + + if (seg == lasttx) + break; + } + } + + /* Sync the descriptors we're using. */ + SQ_CDTXSYNC(sc, sc->sc_nexttx, dmamap->dm_nsegs, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* Store a pointer to the packet so we can free it later */ + sc->sc_txmbuf[sc->sc_nexttx] = m0; + + /* Advance the tx pointer. */ + sc->sc_nfreetx -= dmamap->dm_nsegs; + sc->sc_nexttx = nexttx; + } + + /* All transmit descriptors used up, let upper layers know */ + if (sc->sc_nfreetx == 0) + ifp->if_flags |= IFF_OACTIVE; + + if (sc->sc_nfreetx != ofree) { + SQ_DPRINTF(("%s: %d packets enqueued, first %d, INTR on %d\n", + sc->sc_dev.dv_xname, lasttx - firsttx + 1, + firsttx, lasttx)); + + /* + * Cause a transmit interrupt to happen on the + * last packet we enqueued, mark it as the last + * descriptor. + * + * HPC1_HDD_CTL_INTR will generate an interrupt on + * HPC1. HPC3 requires HPC3_HDD_CTL_EOPACKET in + * addition to HPC3_HDD_CTL_INTR to interrupt. + */ + KASSERT(lasttx != -1); + if (sc->hpc_regs->revision == 3) { + sc->sc_txdesc[lasttx].hpc3_hdd_ctl |= + HPC3_HDD_CTL_INTR | HPC3_HDD_CTL_EOCHAIN; + } else { + sc->sc_txdesc[lasttx].hpc1_hdd_ctl |= HPC1_HDD_CTL_INTR; + sc->sc_txdesc[lasttx].hpc1_hdd_bufptr |= + HPC1_HDD_CTL_EOCHAIN; + } + + SQ_CDTXSYNC(sc, lasttx, 1, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* + * There is a potential race condition here if the HPC + * DMA channel is active and we try and either update + * the 'next descriptor' pointer in the HPC PIO space + * or the 'next descriptor' pointer in a previous desc- + * riptor. + * + * To avoid this, if the channel is active, we rely on + * the transmit interrupt routine noticing that there + * are more packets to send and restarting the HPC DMA + * engine, rather than mucking with the DMA state here. + */ + status = sq_hpc_read(sc, sc->hpc_regs->enetx_ctl); + + if ((status & sc->hpc_regs->enetx_ctl_active) != 0) { + SQ_TRACE(SQ_ADD_TO_DMA, sc, firsttx, status); + + /* + * NB: hpc3_hdd_ctl == hpc1_hdd_bufptr, and + * HPC1_HDD_CTL_EOCHAIN == HPC3_HDD_CTL_EOCHAIN + */ + sc->sc_txdesc[SQ_PREVTX(firsttx)].hpc3_hdd_ctl &= + ~HPC3_HDD_CTL_EOCHAIN; + + if (sc->hpc_regs->revision != 3) + sc->sc_txdesc[SQ_PREVTX(firsttx)].hpc1_hdd_ctl + &= ~HPC1_HDD_CTL_INTR; + + SQ_CDTXSYNC(sc, SQ_PREVTX(firsttx), 1, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + } else if (sc->hpc_regs->revision == 3) { + SQ_TRACE(SQ_START_DMA, sc, firsttx, status); + + sq_hpc_write(sc, HPC3_ENETX_NDBP, SQ_CDTXADDR(sc, + firsttx)); + + /* Kick DMA channel into life */ + sq_hpc_write(sc, HPC3_ENETX_CTL, HPC3_ENETX_CTL_ACTIVE); + } else { + /* + * In the HPC1 case where transmit DMA is + * inactive, we can either kick off if + * the ring was previously empty, or call + * our transmit interrupt handler to + * figure out if the ring stopped short + * and restart at the right place. + */ + if (ofree == SQ_NTXDESC) { + SQ_TRACE(SQ_START_DMA, sc, firsttx, status); + + sq_hpc_write(sc, HPC1_ENETX_NDBP, + SQ_CDTXADDR(sc, firsttx)); + sq_hpc_write(sc, HPC1_ENETX_CFXBP, + SQ_CDTXADDR(sc, firsttx)); + sq_hpc_write(sc, HPC1_ENETX_CBP, + SQ_CDTXADDR(sc, firsttx)); + + /* Kick DMA channel into life */ + sq_hpc_write(sc, HPC1_ENETX_CTL, + HPC1_ENETX_CTL_ACTIVE); + } else + sq_txring_hpc1(sc); + } + + /* Set a watchdog timer in case the chip flakes out. */ + ifp->if_timer = 5; + } +} + +void +sq_stop(struct ifnet *ifp) +{ + struct sq_softc *sc = ifp->if_softc; + int i; + + ifp->if_timer = 0; + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + for (i = 0; i < SQ_NTXDESC; i++) { + if (sc->sc_txmbuf[i] != NULL) { + bus_dmamap_unload(sc->sc_dmat, sc->sc_txmap[i]); + m_freem(sc->sc_txmbuf[i]); + sc->sc_txmbuf[i] = NULL; + } + } + + /* Clear Seeq transmit/receive command registers */ + sq_seeq_write(sc, SEEQ_TXCMD, 0); + sq_seeq_write(sc, SEEQ_RXCMD, 0); + + sq_reset(sc); +} + +/* Device timeout/watchdog routine. */ +void +sq_watchdog(struct ifnet *ifp) +{ + struct sq_softc *sc = ifp->if_softc; + uint32_t status; + + status = sq_hpc_read(sc, sc->hpc_regs->enetx_ctl); + log(LOG_ERR, "%s: device timeout (prev %d, next %d, free %d, " + "status %08x)\n", sc->sc_dev.dv_xname, sc->sc_prevtx, + sc->sc_nexttx, sc->sc_nfreetx, status); + +#ifdef SQ_DEBUG + sq_trace_dump(sc); +#endif + + ++ifp->if_oerrors; + + sq_init(ifp); +} + +#ifdef SQ_DEBUG +void +sq_trace_dump(struct sq_softc *sc) +{ + int i; + const char *act; + + for (i = 0; i < sc->sq_trace_idx; i++) { + switch (sc->sq_trace[i].action) { + case SQ_RESET: act = "SQ_RESET"; break; + case SQ_ADD_TO_DMA: act = "SQ_ADD_TO_DMA"; break; + case SQ_START_DMA: act = "SQ_START_DMA"; break; + case SQ_DONE_DMA: act = "SQ_DONE_DMA"; break; + case SQ_RESTART_DMA: act = "SQ_RESTART_DMA"; break; + case SQ_TXINTR_ENTER: act = "SQ_TXINTR_ENTER"; break; + case SQ_TXINTR_EXIT: act = "SQ_TXINTR_EXIT"; break; + case SQ_TXINTR_BUSY: act = "SQ_TXINTR_BUSY"; break; + case SQ_IOCTL: act = "SQ_IOCTL"; break; + case SQ_ENQUEUE: act = "SQ_ENQUEUE"; break; + default: act = "UNKNOWN"; + } + + printf("%s: [%03d] action %-16s buf %03d free %03d " + "status %08x line %d\n", sc->sc_dev.dv_xname, i, act, + sc->sq_trace[i].bufno, sc->sq_trace[i].freebuf, + sc->sq_trace[i].status, sc->sq_trace[i].line); + } + + memset(&sc->sq_trace, 0, sizeof(sc->sq_trace)); + sc->sq_trace_idx = 0; +} +#endif + +int +sq_intr(void *arg) +{ + struct sq_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ac.ac_if; + uint32_t stat; + + stat = sq_hpc_read(sc, sc->hpc_regs->enetr_reset); + + if ((stat & 2) == 0) { + SQ_DPRINTF(("%s: Unexpected interrupt!\n", + sc->sc_dev.dv_xname)); + } else + sq_hpc_write(sc, sc->hpc_regs->enetr_reset, (stat | 2)); + + /* + * If the interface isn't running, the interrupt couldn't + * possibly have come from us. + */ + if ((ifp->if_flags & IFF_RUNNING) == 0) + return 0; + + /* Always check for received packets */ + sq_rxintr(sc); + + /* Only handle transmit interrupts if we actually sent something */ + if (sc->sc_nfreetx < SQ_NTXDESC) + sq_txintr(sc); + + /* + * XXX Always claim the interrupt, even if we did nothing. + * XXX There seem to be extra interrupts when the receiver becomes + * XXX idle. + */ + return 1; +} + +void +sq_rxintr(struct sq_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct mbuf* m; + int i, framelen; + uint8_t pktstat; + uint32_t status; + uint32_t ctl_reg; + int new_end, orig_end; + + for (i = sc->sc_nextrx; ; i = SQ_NEXTRX(i)) { + SQ_CDRXSYNC(sc, i, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + /* + * If this is a CPU-owned buffer, we're at the end of the list. + */ + if (sc->hpc_regs->revision == 3) + ctl_reg = + sc->sc_rxdesc[i].hpc3_hdd_ctl & HPC3_HDD_CTL_OWN; + else + ctl_reg = + sc->sc_rxdesc[i].hpc1_hdd_ctl & HPC1_HDD_CTL_OWN; + + if (ctl_reg) { +#if defined(SQ_DEBUG) + uint32_t reg; + + reg = sq_hpc_read(sc, sc->hpc_regs->enetr_ctl); + SQ_DPRINTF(("%s: rxintr: done at %d (ctl %08x)\n", + sc->sc_dev.dv_xname, i, reg)); +#endif + break; + } + + m = sc->sc_rxmbuf[i]; + framelen = m->m_ext.ext_size - 3; + if (sc->hpc_regs->revision == 3) + framelen -= + HPC3_HDD_CTL_BYTECNT(sc->sc_rxdesc[i].hpc3_hdd_ctl); + else + framelen -= + HPC1_HDD_CTL_BYTECNT(sc->sc_rxdesc[i].hpc1_hdd_ctl); + + /* Now sync the actual packet data */ + bus_dmamap_sync(sc->sc_dmat, sc->sc_rxmap[i], 0, + sc->sc_rxmap[i]->dm_mapsize, BUS_DMASYNC_POSTREAD); + + pktstat = *((uint8_t *)m->m_data + framelen + 2); + + if ((pktstat & RXSTAT_GOOD) == 0) { + ifp->if_ierrors++; + + if (pktstat & RXSTAT_OFLOW) + printf("%s: receive FIFO overflow\n", + sc->sc_dev.dv_xname); + + bus_dmamap_sync(sc->sc_dmat, sc->sc_rxmap[i], 0, + sc->sc_rxmap[i]->dm_mapsize, BUS_DMASYNC_PREREAD); + SQ_INIT_RXDESC(sc, i); + SQ_DPRINTF(("%s: sq_rxintr: buf %d no RXSTAT_GOOD\n", + sc->sc_dev.dv_xname, i)); + continue; + } + + if (sq_add_rxbuf(sc, i) != 0) { + ifp->if_ierrors++; + bus_dmamap_sync(sc->sc_dmat, sc->sc_rxmap[i], 0, + sc->sc_rxmap[i]->dm_mapsize, BUS_DMASYNC_PREREAD); + SQ_INIT_RXDESC(sc, i); + SQ_DPRINTF(("%s: sq_rxintr: buf %d sq_add_rxbuf() " + "failed\n", sc->sc_dev.dv_xname, i)); + continue; + } + + + m->m_data += 2; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = framelen; + + ifp->if_ipackets++; + + SQ_DPRINTF(("%s: sq_rxintr: buf %d len %d\n", + sc->sc_dev.dv_xname, i, framelen)); + +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN); +#endif + + ether_input_mbuf(ifp, m); + } + + + /* If anything happened, move ring start/end pointers to new spot */ + if (i != sc->sc_nextrx) { + /* + * NB: hpc3_hdd_ctl == hpc1_hdd_bufptr, and + * HPC1_HDD_CTL_EOCHAIN == HPC3_HDD_CTL_EOCHAIN + */ + + new_end = SQ_PREVRX(i); + sc->sc_rxdesc[new_end].hpc3_hdd_ctl |= HPC3_HDD_CTL_EOCHAIN; + SQ_CDRXSYNC(sc, new_end, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + orig_end = SQ_PREVRX(sc->sc_nextrx); + sc->sc_rxdesc[orig_end].hpc3_hdd_ctl &= ~HPC3_HDD_CTL_EOCHAIN; + SQ_CDRXSYNC(sc, orig_end, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + sc->sc_nextrx = i; + } + + status = sq_hpc_read(sc, sc->hpc_regs->enetr_ctl); + + /* If receive channel is stopped, restart it... */ + if ((status & sc->hpc_regs->enetr_ctl_active) == 0) { + /* Pass the start of the receive ring to the HPC */ + sq_hpc_write(sc, sc->hpc_regs->enetr_ndbp, + SQ_CDRXADDR(sc, sc->sc_nextrx)); + + /* And turn on the HPC ethernet receive channel */ + sq_hpc_write(sc, sc->hpc_regs->enetr_ctl, + sc->hpc_regs->enetr_ctl_active); + } +} + +void +sq_txintr(struct sq_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + uint shift = 0; + uint32_t status, tmp; + + if (sc->hpc_regs->revision != 3) + shift = 16; + + status = sq_hpc_read(sc, sc->hpc_regs->enetx_ctl) >> shift; + + SQ_TRACE(SQ_TXINTR_ENTER, sc, sc->sc_prevtx, status); + + tmp = (sc->hpc_regs->enetx_ctl_active >> shift) | TXSTAT_GOOD; + if ((status & tmp) == 0) { + if (status & TXSTAT_COLL) + ifp->if_collisions++; + + if (status & TXSTAT_UFLOW) { + printf("%s: transmit underflow\n", + sc->sc_dev.dv_xname); + ifp->if_oerrors++; +#ifdef SQ_DEBUG + sq_trace_dump(sc); +#endif + sq_init(ifp); + return; + } + + if (status & TXSTAT_16COLL) { + printf("%s: max collisions reached\n", + sc->sc_dev.dv_xname); + ifp->if_oerrors++; + ifp->if_collisions += 16; + } + } + + /* prevtx now points to next xmit packet not yet finished */ + if (sc->hpc_regs->revision == 3) + sq_txring_hpc3(sc); + else + sq_txring_hpc1(sc); + + /* If we have buffers free, let upper layers know */ + if (sc->sc_nfreetx > 0) + ifp->if_flags &= ~IFF_OACTIVE; + + /* If all packets have left the coop, cancel watchdog */ + if (sc->sc_nfreetx == SQ_NTXDESC) + ifp->if_timer = 0; + + SQ_TRACE(SQ_TXINTR_EXIT, sc, sc->sc_prevtx, status); + sq_start(ifp); +} + +/* + * Reclaim used transmit descriptors and restart the transmit DMA + * engine if necessary. + */ +void +sq_txring_hpc1(struct sq_softc *sc) +{ + /* + * HPC1 doesn't tag transmitted descriptors, however, + * the NDBP register points to the next descriptor that + * has not yet been processed. If DMA is not in progress, + * we can safely reclaim all descriptors up to NDBP, and, + * if necessary, restart DMA at NDBP. Otherwise, if DMA + * is active, we can only safely reclaim up to CBP. + * + * For now, we'll only reclaim on inactive DMA and assume + * that a sufficiently large ring keeps us out of trouble. + */ + struct ifnet *ifp = &sc->sc_ac.ac_if; + uint32_t reclaimto, status; + int reclaimall, i = sc->sc_prevtx; + + status = sq_hpc_read(sc, HPC1_ENETX_CTL); + if (status & HPC1_ENETX_CTL_ACTIVE) { + SQ_TRACE(SQ_TXINTR_BUSY, sc, i, status); + return; + } else + reclaimto = sq_hpc_read(sc, HPC1_ENETX_NDBP); + + if (sc->sc_nfreetx == 0 && SQ_CDTXADDR(sc, i) == reclaimto) + reclaimall = 1; + else + reclaimall = 0; + + while (sc->sc_nfreetx < SQ_NTXDESC) { + if (SQ_CDTXADDR(sc, i) == reclaimto && !reclaimall) + break; + + SQ_CDTXSYNC(sc, i, sc->sc_txmap[i]->dm_nsegs, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + /* Sync the packet data, unload DMA map, free mbuf */ + bus_dmamap_sync(sc->sc_dmat, sc->sc_txmap[i], + 0, sc->sc_txmap[i]->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, sc->sc_txmap[i]); + m_freem(sc->sc_txmbuf[i]); + sc->sc_txmbuf[i] = NULL; + + ifp->if_opackets++; + sc->sc_nfreetx++; + + SQ_TRACE(SQ_DONE_DMA, sc, i, status); + + i = SQ_NEXTTX(i); + } + + if (sc->sc_nfreetx < SQ_NTXDESC) { + SQ_TRACE(SQ_RESTART_DMA, sc, i, status); + + KASSERT(reclaimto == SQ_CDTXADDR(sc, i)); + + sq_hpc_write(sc, HPC1_ENETX_CFXBP, reclaimto); + sq_hpc_write(sc, HPC1_ENETX_CBP, reclaimto); + + /* Kick DMA channel into life */ + sq_hpc_write(sc, HPC1_ENETX_CTL, HPC1_ENETX_CTL_ACTIVE); + + /* + * Set a watchdog timer in case the chip + * flakes out. + */ + ifp->if_timer = 5; + } + + sc->sc_prevtx = i; +} + +/* + * Reclaim used transmit descriptors and restart the transmit DMA + * engine if necessary. + */ +void +sq_txring_hpc3(struct sq_softc *sc) +{ + /* + * HPC3 tags descriptors with a bit once they've been + * transmitted. We need only free each XMITDONE'd + * descriptor, and restart the DMA engine if any + * descriptors are left over. + */ + struct ifnet *ifp = &sc->sc_ac.ac_if; + int i; + uint32_t status = 0; + + i = sc->sc_prevtx; + while (sc->sc_nfreetx < SQ_NTXDESC) { + /* + * Check status first so we don't end up with a case of + * the buffer not being finished while the DMA channel + * has gone idle. + */ + status = sq_hpc_read(sc, HPC3_ENETX_CTL); + + SQ_CDTXSYNC(sc, i, sc->sc_txmap[i]->dm_nsegs, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + /* Check for used descriptor and restart DMA chain if needed */ + if ((sc->sc_txdesc[i].hpc3_hdd_ctl & + HPC3_HDD_CTL_XMITDONE) == 0) { + if ((status & HPC3_ENETX_CTL_ACTIVE) == 0) { + SQ_TRACE(SQ_RESTART_DMA, sc, i, status); + + sq_hpc_write(sc, HPC3_ENETX_NDBP, + SQ_CDTXADDR(sc, i)); + + /* Kick DMA channel into life */ + sq_hpc_write(sc, HPC3_ENETX_CTL, + HPC3_ENETX_CTL_ACTIVE); + + /* + * Set a watchdog timer in case the chip + * flakes out. + */ + ifp->if_timer = 5; + } else + SQ_TRACE(SQ_TXINTR_BUSY, sc, i, status); + break; + } + + /* Sync the packet data, unload DMA map, free mbuf */ + bus_dmamap_sync(sc->sc_dmat, sc->sc_txmap[i], + 0, sc->sc_txmap[i]->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, sc->sc_txmap[i]); + m_freem(sc->sc_txmbuf[i]); + sc->sc_txmbuf[i] = NULL; + + ifp->if_opackets++; + sc->sc_nfreetx++; + + SQ_TRACE(SQ_DONE_DMA, sc, i, status); + i = SQ_NEXTTX(i); + } + + sc->sc_prevtx = i; +} + +void +sq_reset(struct sq_softc *sc) +{ + /* Stop HPC dma channels */ + sq_hpc_write(sc, sc->hpc_regs->enetr_ctl, 0); + sq_hpc_write(sc, sc->hpc_regs->enetx_ctl, 0); + + sq_hpc_write(sc, sc->hpc_regs->enetr_reset, 3); + delay(20); + sq_hpc_write(sc, sc->hpc_regs->enetr_reset, 0); +} + +/* sq_add_rxbuf: Add a receive buffer to the indicated descriptor. */ +int +sq_add_rxbuf(struct sq_softc *sc, int idx) +{ + int err; + struct mbuf *m; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return ENOBUFS; + + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + return ENOBUFS; + } + + if (sc->sc_rxmbuf[idx] != NULL) + bus_dmamap_unload(sc->sc_dmat, sc->sc_rxmap[idx]); + + sc->sc_rxmbuf[idx] = m; + + if ((err = bus_dmamap_load(sc->sc_dmat, sc->sc_rxmap[idx], + m->m_ext.ext_buf, m->m_ext.ext_size, NULL, BUS_DMA_NOWAIT)) != 0) { + printf("%s: can't load rx DMA map %d, error = %d\n", + sc->sc_dev.dv_xname, idx, err); + panic("sq_add_rxbuf"); /* XXX */ + } + + bus_dmamap_sync(sc->sc_dmat, sc->sc_rxmap[idx], + 0, sc->sc_rxmap[idx]->dm_mapsize, BUS_DMASYNC_PREREAD); + + SQ_INIT_RXDESC(sc, idx); + + return 0; +} diff --git a/sys/arch/sgi/hpc/if_sqvar.h b/sys/arch/sgi/hpc/if_sqvar.h new file mode 100644 index 00000000000..657025771f7 --- /dev/null +++ b/sys/arch/sgi/hpc/if_sqvar.h @@ -0,0 +1,200 @@ +/* $OpenBSD: if_sqvar.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: sqvar.h,v 1.12 2011/01/25 13:12:39 tsutsui Exp $ */ + +/* + * Copyright (c) 2001 Rafal K. Boni + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Note, these must be powers of two for the magic NEXT/PREV macros to work */ +#define SQ_NRXDESC 64 +#define SQ_NTXDESC 64 + +#define SQ_NRXDESC_MASK (SQ_NRXDESC - 1) +#define SQ_NEXTRX(x) ((x + 1) & SQ_NRXDESC_MASK) +#define SQ_PREVRX(x) ((x - 1) & SQ_NRXDESC_MASK) + +#define SQ_NTXDESC_MASK (SQ_NTXDESC - 1) +#define SQ_NEXTTX(x) ((x + 1) & SQ_NTXDESC_MASK) +#define SQ_PREVTX(x) ((x - 1) & SQ_NTXDESC_MASK) + +/* + * We pack all DMA control structures into one container so we can alloc just + * one chunk of DMA-safe memory and pack them into it. Otherwise, we'd have to + * allocate a page for each descriptor, since the bus_dmamem_alloc() interface + * does not allow us to allocate smaller chunks. + */ +struct sq_control { + /* Receive descriptors */ + struct hpc_dma_desc rx_desc[SQ_NRXDESC]; + + /* Transmit descriptors */ + struct hpc_dma_desc tx_desc[SQ_NTXDESC]; +}; + +#define SQ_CDOFF(x) offsetof(struct sq_control, x) +#define SQ_CDTXOFF(x) SQ_CDOFF(tx_desc[(x)]) +#define SQ_CDRXOFF(x) SQ_CDOFF(rx_desc[(x)]) + +#define SQ_TYPE_8003 0 +#define SQ_TYPE_80C03 1 + +/* Trace Actions */ +#define SQ_RESET 1 +#define SQ_ADD_TO_DMA 2 +#define SQ_START_DMA 3 +#define SQ_DONE_DMA 4 +#define SQ_RESTART_DMA 5 +#define SQ_TXINTR_ENTER 6 +#define SQ_TXINTR_EXIT 7 +#define SQ_TXINTR_BUSY 8 +#define SQ_IOCTL 9 +#define SQ_ENQUEUE 10 + +struct sq_action_trace { + int action; + int line; + int bufno; + int status; + int freebuf; +}; + +#ifdef SQ_DEBUG +#define SQ_TRACEBUF_SIZE 100 + +#define SQ_TRACE(act, sc, buf, stat) do { \ + (sc)->sq_trace[(sc)->sq_trace_idx].action = (act); \ + (sc)->sq_trace[(sc)->sq_trace_idx].line = __LINE__; \ + (sc)->sq_trace[(sc)->sq_trace_idx].bufno = (buf); \ + (sc)->sq_trace[(sc)->sq_trace_idx].status = (stat); \ + (sc)->sq_trace[(sc)->sq_trace_idx].freebuf = (sc)->sc_nfreetx; \ + if (++(sc)->sq_trace_idx == SQ_TRACEBUF_SIZE) \ + (sc)->sq_trace_idx = 0; \ +} while (/* CONSTCOND */0) +#else +#define SQ_TRACE(act, sc, buf, stat) do { } while (/* CONSTCOND */0) +#endif + +struct sq_softc { + struct device sc_dev; + + /* HPC registers */ + bus_space_tag_t sc_hpct; + bus_space_handle_t sc_hpch; + + /* HPC external Ethernet registers: aka Seeq 8003 registers */ + bus_space_tag_t sc_regt; + bus_space_handle_t sc_regh; + + bus_dma_tag_t sc_dmat; + + struct arpcom sc_ac; + uint8_t sc_enaddr[ETHER_ADDR_LEN]; + + int sc_type; + + struct sq_control* sc_control; +#define sc_rxdesc sc_control->rx_desc +#define sc_txdesc sc_control->tx_desc + + /* DMA structures for control data (DMA RX/TX descriptors) */ + int sc_ncdseg; + bus_dma_segment_t sc_cdseg; + bus_dmamap_t sc_cdmap; +#define sc_cddma sc_cdmap->dm_segs[0].ds_addr + + int sc_nextrx; + + /* DMA structures for RX packet data */ + bus_dma_segment_t sc_rxseg[SQ_NRXDESC]; + bus_dmamap_t sc_rxmap[SQ_NRXDESC]; + struct mbuf* sc_rxmbuf[SQ_NRXDESC]; + + int sc_nexttx; + int sc_prevtx; + int sc_nfreetx; + + /* DMA structures for TX packet data */ + bus_dma_segment_t sc_txseg[SQ_NTXDESC]; + bus_dmamap_t sc_txmap[SQ_NTXDESC]; + struct mbuf* sc_txmbuf[SQ_NTXDESC]; + + uint8_t sc_rxcmd; /* prototype rxcmd */ + + struct hpc_values *hpc_regs; /* HPC register definitions */ + +#ifdef SQ_DEBUG + int sq_trace_idx; + struct sq_action_trace sq_trace[SQ_TRACEBUF_SIZE]; +#endif +}; + +#define SQ_CDTXADDR(sc, x) ((sc)->sc_cddma + SQ_CDTXOFF((x))) +#define SQ_CDRXADDR(sc, x) ((sc)->sc_cddma + SQ_CDRXOFF((x))) + +static inline void +SQ_CDTXSYNC(struct sq_softc *sc, int __x, int __n, int ops) +{ + /* If it will wrap around, sync to the end of the ring. */ + if ((__x + __n) > SQ_NTXDESC) { + bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cdmap, + SQ_CDTXOFF(__x), sizeof(struct hpc_dma_desc) * + (SQ_NTXDESC - __x), (ops)); + __n -= (SQ_NTXDESC - __x); + __x = 0; + } + + /* Now sync whatever is left. */ + bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cdmap, + SQ_CDTXOFF(__x), sizeof(struct hpc_dma_desc) * __n, (ops)); +} + +#define SQ_CDRXSYNC(sc, x, ops) \ + bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cdmap, \ + SQ_CDRXOFF((x)), sizeof(struct hpc_dma_desc), (ops)) + +static inline void +SQ_INIT_RXDESC(struct sq_softc *sc, unsigned int x) +{ + struct hpc_dma_desc* __rxd = &(sc)->sc_rxdesc[(x)]; + struct mbuf *__m = (sc)->sc_rxmbuf[(x)]; + + __m->m_data = __m->m_ext.ext_buf; + if (sc->hpc_regs->revision == 3) { + __rxd->hpc3_hdd_bufptr = + (sc)->sc_rxmap[(x)]->dm_segs[0].ds_addr; + __rxd->hpc3_hdd_ctl = __m->m_ext.ext_size | HPC3_HDD_CTL_OWN | + HPC3_HDD_CTL_INTR | HPC3_HDD_CTL_EOPACKET | + ((x) == (SQ_NRXDESC - 1) ? HPC3_HDD_CTL_EOCHAIN : 0); + } else { + __rxd->hpc1_hdd_bufptr = + (sc)->sc_rxmap[(x)]->dm_segs[0].ds_addr | + ((x) == (SQ_NRXDESC - 1) ? HPC1_HDD_CTL_EOCHAIN : 0); + __rxd->hpc1_hdd_ctl = __m->m_ext.ext_size | HPC1_HDD_CTL_OWN | + HPC1_HDD_CTL_INTR | HPC1_HDD_CTL_EOPACKET; + } + __rxd->hdd_descptr = SQ_CDRXADDR((sc), SQ_NEXTRX((x))); + SQ_CDRXSYNC((sc), (x), BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); +} diff --git a/sys/arch/sgi/hpc/iocreg.h b/sys/arch/sgi/hpc/iocreg.h new file mode 100644 index 00000000000..274454e4ddf --- /dev/null +++ b/sys/arch/sgi/hpc/iocreg.h @@ -0,0 +1,122 @@ +/* $OpenBSD: iocreg.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: iocreg.h,v 1.2 2005/12/11 12:18:53 christos Exp $ */ + +/* + * Copyright (c) 2003 Christopher Sekiya + * Copyright (c) 2001 Rafal K. Boni + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * IOC1/2 memory map. + * + * The IOC1/2 is connected to the HPC#0, PBus channel 6, so these registers + * are based from the external register window for PBus channel 6 on HPC#0. + * + */ + +#define IOC_BASE HPC3_PBUS_CH6_DEVREGS + +#define IOC_PLP_REGS 0x00 /* Parallel port registers */ +#define IOC_PLP_REGS_SIZE 0x2c + +#define IOC_PLP_DATA 0x00 /* Data register */ +#define IOC_PLP_CTL 0x04 /* Control register */ +#define IOC_PLP_STAT 0x08 /* Status register */ +#define IOC_PLP_DMACTL 0x0c /* DMA control register */ +#define IOC_PLP_INTSTAT 0x10 /* Interrupt status register */ +#define IOC_PLP_INTMASK 0x14 /* Interrupt mask register */ +#define IOC_PLP_TIMER1 0x18 /* Timer 1 register */ +#define IOC_PLP_TIMER2 0x1c /* Timer 2 register */ +#define IOC_PLP_TIMER3 0x20 /* Timer 3 register */ +#define IOC_PLP_TIMER4 0x24 /* Timer 4 register */ + +#define IOC_SERIAL_REGS 0x30 /* Serial port registers */ +#define IOC_SERIAL_REGS_SIZE 0x0c + +#define IOC_SERIAL_PORT1_CMD 0x00 /* Port 1 command transfer */ +#define IOC_SERIAL_PORT1_DATA 0x04 /* Port 1 data transfer */ +#define IOC_SERIAL_PORT2_CMD 0x08 /* Port 2 command transfer */ +#define IOC_SERIAL_PORT2_DATA 0x0c /* Port 2 data transfer */ + +#define IOC_KB_REGS 0x40 /* Keyboard/mouse registers */ +#define IOC_KB_REGS_SIZE 0x08 + +/* Miscellaneous registers */ + +#define IOC_MISC_REGS 0x48 /* Misc. IOC regs */ +#define IOC_MISC_REGS_SIZE 0x34 + +#define IOC_GCSEL 0x48 /* General select register */ + +#define IOC_GCREG 0x4c /* General control register */ + +#define IOC_PANEL 0x50 /* Front Panel register */ +#define IOC_PANEL_POWER_STATE 0x01 +#define IOC_PANEL_POWER_IRQ 0x02 +#define IOC_PANEL_VDOWN_IRQ 0x10 +#define IOC_PANEL_VDOWN_HOLD 0x20 +#define IOC_PANEL_VUP_IRQ 0x40 +#define IOC_PANEL_VUP_HOLD 0x80 + +#define IOC_SYSID 0x58 /* System ID register */ +#define IOC_SYSID_SYSTYPE 0x01 /* 0: Sapphire, 1: Full House */ +#define IOC_SYSID_BOARDREV 0x1e +#define IOC_SYSID_BOARDREV_SHIFT 1 +#define IOC_SYSID_CHIPREV 0xe0 +#define IOC_SYSID_CHIPREV_SHIFT 5 + +#define IOC_READ 0x60 /* Read register */ +#define IOC_READ_SCSI0_POWER 0x10 +#define IOC_READ_SCSI1_POWER 0x20 +#define IOC_READ_ENET_POWER 0x40 +#define IOC_READ_ENET_LINK 0x80 + +#define IOC_DMASEL 0x68 /* DMA select register */ +#define IOC_DMASEL_ISDN_B 0x01 +#define IOC_DMASEL_ISDN_A 0x02 +#define IOC_DMASEL_PARALLEL 0x04 +#define IOC_DMASEL_SERIAL_10MHZ 0x00 +#define IOC_DMASEL_SERIAL_6MHZ 0x10 +#define IOC_DMASEL_SERIAL_EXTERNAL 0x20 + +#define IOC_RESET 0x70 /* Reset register */ +#define IOC_RESET_PARALLEL 0x01 +#define IOC_RESET_PCKBC 0x02 +#define IOC_RESET_EISA 0x04 +#define IOC_RESET_ISDN 0x08 +#define IOC_RESET_LED_GREEN 0x10 +#define IOC_RESET_LED_RED 0x20 +#define IOC_RESET_LED_ORANGE 0x40 + +#define IOC_WRITE 0x78 /* Write register */ +#define IOC_WRITE_ENET_NTH 0x01 +#define IOC_WRITE_ENET_UTP 0x02 +#define IOC_WRITE_ENET_AUI 0x04 +#define IOC_WRITE_ENET_AUTO 0x08 +#define IOC_WRITE_PC_UART2 0x10 +#define IOC_WRITE_PC_UART1 0x20 +#define IOC_WRITE_MARGIN_LOW 0x40 +#define IOC_WRITE_MARGIN_HIGH 0x80 diff --git a/sys/arch/sgi/hpc/wdsc.c b/sys/arch/sgi/hpc/wdsc.c new file mode 100644 index 00000000000..101b5178819 --- /dev/null +++ b/sys/arch/sgi/hpc/wdsc.c @@ -0,0 +1,290 @@ +/* $OpenBSD: wdsc.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: wdsc.c,v 1.32 2011/07/01 18:53:47 dyoung Exp $ */ + +/* + * Copyright (c) 2001 Wayne Knowles + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Wayne Knowles + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/buf.h> +#include <sys/timeout.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <mips64/archtype.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> +#include <machine/cpu.h> +#include <sgi/localbus/intvar.h> + +#include <sgi/hpc/hpcreg.h> +#include <sgi/hpc/hpcvar.h> +#include <sgi/hpc/hpcdma.h> + +#include <dev/ic/wd33c93reg.h> +#include <dev/ic/wd33c93var.h> + +struct wdsc_softc { + struct wd33c93_softc sc_wd33c93; /* Must be first */ + bus_dma_tag_t sc_dmat; + bus_dmamap_t sc_dmamap; + int sc_flags; +#define WDSC_DMA_ACTIVE 0x1 +#define WDSC_DMA_MAPLOADED 0x2 + struct hpc_dma_softc sc_hpcdma; +}; + +int wdsc_match(struct device *, void *, void *); +void wdsc_attach(struct device *, struct device *, void *); + +const struct cfattach wdsc_ca = { + sizeof(struct wdsc_softc), wdsc_match, wdsc_attach +}; + +struct cfdriver wdsc_cd = { + NULL, "wdsc", DV_DULL +}; + +int wdsc_dmasetup(struct wd33c93_softc *, void ** ,size_t *, int, size_t *); +int wdsc_dmago(struct wd33c93_softc *); +void wdsc_dmastop(struct wd33c93_softc *); +void wdsc_reset(struct wd33c93_softc *); + +struct scsi_adapter wdsc_switch = { + wd33c93_scsi_cmd, + scsi_minphys, + NULL, + NULL, +}; + +/* + * Match for SCSI devices on the onboard and GIO32 adapter WD33C93 chips + */ +int +wdsc_match(struct device *parent, void *vcf, void *aux) +{ + struct hpc_attach_args *haa = aux; + struct cfdata *cf = vcf; + vaddr_t reset, asr; + uint32_t dummy; + uint8_t reg; + + if (strcmp(haa->ha_name, cf->cf_driver->cd_name) != 0) + return 0; + + reset = PHYS_TO_XKPHYS(haa->ha_sh + haa->ha_dmaoff + + haa->hpc_regs->scsi0_ctl, CCA_NC); + if (guarded_read_4(reset, &dummy) != 0) + return 0; + *(volatile uint32_t *)reset = haa->hpc_regs->scsi_dmactl_reset; + delay(1000); + *(volatile uint32_t *)reset = 0x0; + delay(1000); + + asr = PHYS_TO_XKPHYS(haa->ha_sh + haa->ha_devoff + 3, CCA_NC); + if (guarded_read_1(asr, ®) != 0) + return 0; + if ((reg & 0xff) != SBIC_ASR_INT) + return 0; + + return 1; +} + +/* + * Attach the wdsc driver + */ +void +wdsc_attach(struct device *parent, struct device *self, void *aux) +{ + struct wdsc_softc *wsc = (struct wdsc_softc *)self; + struct wd33c93_softc *sc = &wsc->sc_wd33c93; + struct hpc_attach_args *haa = aux; + int err; + + sc->sc_regt = haa->ha_st; + wsc->sc_dmat = haa->ha_dmat; + + wsc->sc_hpcdma.hpc = haa->hpc_regs; + + if ((err = bus_space_subregion(haa->ha_st, haa->ha_sh, + haa->ha_devoff + 3, 1, &sc->sc_asr_regh)) != 0) { + printf(": unable to map asr reg, err=%d\n", err); + return; + } + + if ((err = bus_space_subregion(haa->ha_st, haa->ha_sh, + haa->ha_devoff + 3 + 4, 1, &sc->sc_data_regh)) != 0) { + printf(": unable to map asr reg, err=%d\n", err); + return; + } + + if (bus_dmamap_create(wsc->sc_dmat, MAXPHYS, + wsc->sc_hpcdma.hpc->scsi_dma_segs, + wsc->sc_hpcdma.hpc->scsi_dma_segs_size, + wsc->sc_hpcdma.hpc->scsi_dma_segs_size, + BUS_DMA_WAITOK, &wsc->sc_dmamap) != 0) { + printf(": failed to create dmamap\n"); + return; + } + + sc->sc_dmasetup = wdsc_dmasetup; + sc->sc_dmago = wdsc_dmago; + sc->sc_dmastop = wdsc_dmastop; + sc->sc_reset = wdsc_reset; + + sc->sc_id = 0; /* Host ID = 0 */ + sc->sc_clkfreq = 200; /* 20MHz */ + sc->sc_dmamode = SBIC_CTL_BURST_DMA; + + if (int2_intr_establish(haa->ha_irq, IPL_BIO, + wd33c93_intr, wsc, self->dv_xname) == NULL) { + printf(": unable to establish interrupt!\n"); + return; + } + + hpcdma_init(haa, &wsc->sc_hpcdma, wsc->sc_hpcdma.hpc->scsi_dma_segs); + wd33c93_attach(sc, &wdsc_switch); +} + +/* + * Prime the hardware for a DMA transfer + * + * Requires splbio() interrupts to be disabled by the caller + */ +int +wdsc_dmasetup(struct wd33c93_softc *sc, void **addr, size_t *len, int datain, + size_t *dmasize) +{ + struct wdsc_softc *wsc = (struct wdsc_softc *)sc; + struct hpc_dma_softc *dsc = &wsc->sc_hpcdma; + int count, err; + void *vaddr; + + KASSERT((wsc->sc_flags & WDSC_DMA_ACTIVE) == 0); + + vaddr = *addr; + count = dsc->sc_dlen = *len; + if (count) { + KASSERT((wsc->sc_flags & WDSC_DMA_MAPLOADED) == 0); + + /* Build list of physical addresses for this transfer */ + if ((err = bus_dmamap_load(wsc->sc_dmat, wsc->sc_dmamap, + vaddr, count, NULL /* kernel address */, + BUS_DMA_NOWAIT)) != 0) + panic("%s: bus_dmamap_load err=%d", + sc->sc_dev.dv_xname, err); + + hpcdma_sglist_create(dsc, wsc->sc_dmamap); + wsc->sc_flags |= WDSC_DMA_MAPLOADED; + + if (datain) { + dsc->sc_dmacmd = + wsc->sc_hpcdma.hpc->scsi_dma_datain_cmd; + dsc->sc_flags |= HPCDMA_READ; + } else { + dsc->sc_dmacmd = + wsc->sc_hpcdma.hpc->scsi_dma_dataout_cmd; + dsc->sc_flags &= ~HPCDMA_READ; + } + } + return count; +} + +/* + * Prime the hardware for the next DMA transfer + */ +int +wdsc_dmago(struct wd33c93_softc *sc) +{ + struct wdsc_softc *wsc = (struct wdsc_softc *)sc; + struct hpc_dma_softc *dsc = &wsc->sc_hpcdma; + + if (dsc->sc_dlen == 0) + return 0; + + KASSERT((wsc->sc_flags & WDSC_DMA_ACTIVE) == 0); + KASSERT((wsc->sc_flags & WDSC_DMA_MAPLOADED)); + + wsc->sc_flags |= WDSC_DMA_ACTIVE; + + bus_dmamap_sync(wsc->sc_dmat, wsc->sc_dmamap, + 0, wsc->sc_dmamap->dm_mapsize, + (dsc->sc_flags & HPCDMA_READ) ? + BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + + hpcdma_cntl(dsc, dsc->sc_dmacmd); /* Thunderbirds are go! */ + + return wsc->sc_dmamap->dm_mapsize; +} + +/* + * Stop DMA and unload active DMA maps + */ +void +wdsc_dmastop(struct wd33c93_softc *sc) +{ + struct wdsc_softc *wsc = (struct wdsc_softc *)sc; + struct hpc_dma_softc *dsc = &wsc->sc_hpcdma; + + if (wsc->sc_flags & WDSC_DMA_ACTIVE) { + if (dsc->sc_flags & HPCDMA_READ) + hpcdma_flush(dsc); + hpcdma_cntl(dsc, 0); /* Stop DMA */ + bus_dmamap_sync(wsc->sc_dmat, wsc->sc_dmamap, + 0, wsc->sc_dmamap->dm_mapsize, + (dsc->sc_flags & HPCDMA_READ) ? + BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); + } + if (wsc->sc_flags & WDSC_DMA_MAPLOADED) + bus_dmamap_unload(wsc->sc_dmat, wsc->sc_dmamap); + wsc->sc_flags &= ~(WDSC_DMA_ACTIVE | WDSC_DMA_MAPLOADED); +} + +/* + * Reset the controller. + */ +void +wdsc_reset(struct wd33c93_softc *sc) +{ + struct wdsc_softc *wsc = (struct wdsc_softc *)sc; + struct hpc_dma_softc *dsc = &wsc->sc_hpcdma; + + hpcdma_reset(dsc); +} diff --git a/sys/arch/sgi/hpc/z8530sc.c b/sys/arch/sgi/hpc/z8530sc.c new file mode 100644 index 00000000000..fbec53846b4 --- /dev/null +++ b/sys/arch/sgi/hpc/z8530sc.c @@ -0,0 +1,409 @@ +/* $OpenBSD: z8530sc.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: z8530sc.c,v 1.30 2009/05/22 03:51:30 mrg Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)zs.c 8.1 (Berkeley) 7/19/93 + */ + +/* + * Copyright (c) 1994 Gordon W. Ross + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)zs.c 8.1 (Berkeley) 7/19/93 + */ + +/* + * Zilog Z8530 Dual UART driver (common part) + * + * This file contains the machine-independent parts of the + * driver common to tty and keyboard/mouse sub-drivers. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/device.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/tty.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/syslog.h> + +#include <dev/ic/z8530reg.h> +#include <machine/z8530var.h> + +void +zs_break(struct zs_chanstate *cs, int set) +{ + + if (set) { + cs->cs_preg[5] |= ZSWR5_BREAK; + cs->cs_creg[5] |= ZSWR5_BREAK; + } else { + cs->cs_preg[5] &= ~ZSWR5_BREAK; + cs->cs_creg[5] &= ~ZSWR5_BREAK; + } + zs_write_reg(cs, 5, cs->cs_creg[5]); +} + + +/* + * drain on-chip fifo + */ +void +zs_iflush(struct zs_chanstate *cs) +{ + uint8_t c, rr0, rr1; + int i; + + /* + * Count how many times we loop. Some systems, such as some + * Apple PowerBooks, claim to have SCC's which they really don't. + */ + for (i = 0; i < 32; i++) { + /* Is there input available? */ + rr0 = zs_read_csr(cs); + if ((rr0 & ZSRR0_RX_READY) == 0) + break; + + /* + * First read the status, because reading the data + * destroys the status of this char. + */ + rr1 = zs_read_reg(cs, 1); + c = zs_read_data(cs); + + if (rr1 & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) { + /* Clear the receive error. */ + zs_write_csr(cs, ZSWR0_RESET_ERRORS); + } + } +} + + +/* + * Write the given register set to the given zs channel in the proper order. + * The channel must not be transmitting at the time. The receiver will + * be disabled for the time it takes to write all the registers. + * Call this with interrupts disabled. + */ +void +zs_loadchannelregs(struct zs_chanstate *cs) +{ + uint8_t *reg, v; + + zs_write_csr(cs, ZSM_RESET_ERR); /* XXX: reset error condition */ + +#if 1 + /* + * XXX: Is this really a good idea? + * XXX: Should go elsewhere! -gwr + */ + zs_iflush(cs); /* XXX */ +#endif + + if (cs->cs_ctl_chan != NULL) + v = ((cs->cs_ctl_chan->cs_creg[5] & (ZSWR5_RTS | ZSWR5_DTR)) != + (cs->cs_ctl_chan->cs_preg[5] & (ZSWR5_RTS | ZSWR5_DTR))); + else + v = 0; + + if (memcmp((void *)cs->cs_preg, (void *)cs->cs_creg, 16) == 0 && !v) + return; /* only change if values are different */ + + /* Copy "pending" regs to "current" */ + memcpy((void *)cs->cs_creg, (void *)cs->cs_preg, 16); + reg = cs->cs_creg; /* current regs */ + + /* disable interrupts */ + zs_write_reg(cs, 1, reg[1] & ~ZSWR1_IMASK); + + /* baud clock divisor, stop bits, parity */ + zs_write_reg(cs, 4, reg[4]); + + /* misc. TX/RX control bits */ + zs_write_reg(cs, 10, reg[10]); + + /* char size, enable (RX/TX) */ + zs_write_reg(cs, 3, reg[3] & ~ZSWR3_RX_ENABLE); + zs_write_reg(cs, 5, reg[5] & ~ZSWR5_TX_ENABLE); + + /* synchronous mode stuff */ + zs_write_reg(cs, 6, reg[6]); + zs_write_reg(cs, 7, reg[7]); + +#if 0 + /* + * Registers 2 and 9 are special because they are + * actually common to both channels, but must be + * programmed through channel A. The "zsc" attach + * function takes care of setting these registers + * and they should not be touched thereafter. + */ + /* interrupt vector */ + zs_write_reg(cs, 2, reg[2]); + /* master interrupt control */ + zs_write_reg(cs, 9, reg[9]); +#endif + + /* Shut down the BRG */ + zs_write_reg(cs, 14, reg[14] & ~ZSWR14_BAUD_ENA); + +#ifdef ZS_MD_SETCLK + /* Let the MD code setup any external clock. */ + ZS_MD_SETCLK(cs); +#endif /* ZS_MD_SETCLK */ + + /* clock mode control */ + zs_write_reg(cs, 11, reg[11]); + + /* baud rate (lo/hi) */ + zs_write_reg(cs, 12, reg[12]); + zs_write_reg(cs, 13, reg[13]); + + /* Misc. control bits */ + zs_write_reg(cs, 14, reg[14]); + + /* which lines cause status interrupts */ + zs_write_reg(cs, 15, reg[15]); + + /* + * Zilog docs recommend resetting external status twice at this + * point. Mainly as the status bits are latched, and the first + * interrupt clear might unlatch them to new values, generating + * a second interrupt request. + */ + zs_write_csr(cs, ZSM_RESET_STINT); + zs_write_csr(cs, ZSM_RESET_STINT); + + /* char size, enable (RX/TX)*/ + zs_write_reg(cs, 3, reg[3]); + zs_write_reg(cs, 5, reg[5]); + + /* Write the status bits on the alternate channel also. */ + if (cs->cs_ctl_chan != NULL) { + v = cs->cs_ctl_chan->cs_preg[5]; + cs->cs_ctl_chan->cs_creg[5] = v; + zs_write_reg(cs->cs_ctl_chan, 5, v); + } + + /* interrupt enables: RX, TX, STATUS */ + zs_write_reg(cs, 1, reg[1]); +} + +/* + * ZS hardware interrupt. Scan all ZS channels. NB: we know here that + * channels are kept in (A,B) pairs. + * + * Do just a little, then get out; set a software interrupt if more + * work is needed. + * + * We deliberately ignore the vectoring Zilog gives us, and match up + * only the number of `reset interrupt under service' operations, not + * the order. + */ +int +zsc_intr_hard(void *arg) +{ + struct zsc_softc *zsc = arg; + struct zs_chanstate *cs0, *cs1; + int handled; + uint8_t rr3; + + handled = 0; + + /* First look at channel A. */ + cs0 = zsc->zsc_cs[0]; + cs1 = zsc->zsc_cs[1]; + + /* + * We have to clear interrupt first to avoid a race condition, + * but it will be done in each MD handler. + */ + for (;;) { + /* Note: only channel A has an RR3 */ + rr3 = zs_read_reg(cs0, 3); + + if ((rr3 & (ZSRR3_IP_A_RX | ZSRR3_IP_A_TX | ZSRR3_IP_A_STAT | + ZSRR3_IP_B_RX | ZSRR3_IP_B_TX | ZSRR3_IP_B_STAT)) == 0) { + break; + } + handled = 1; + + /* First look at channel A. */ + if (rr3 & (ZSRR3_IP_A_RX | ZSRR3_IP_A_TX | ZSRR3_IP_A_STAT)) + zs_write_csr(cs0, ZSWR0_CLR_INTR); + + if (rr3 & ZSRR3_IP_A_RX) + (*cs0->cs_ops->zsop_rxint)(cs0); + if (rr3 & ZSRR3_IP_A_STAT) + (*cs0->cs_ops->zsop_stint)(cs0, 0); + if (rr3 & ZSRR3_IP_A_TX) + (*cs0->cs_ops->zsop_txint)(cs0); + + /* Now look at channel B. */ + if (rr3 & (ZSRR3_IP_B_RX | ZSRR3_IP_B_TX | ZSRR3_IP_B_STAT)) + zs_write_csr(cs1, ZSWR0_CLR_INTR); + + if (rr3 & ZSRR3_IP_B_RX) + (*cs1->cs_ops->zsop_rxint)(cs1); + if (rr3 & ZSRR3_IP_B_STAT) + (*cs1->cs_ops->zsop_stint)(cs1, 0); + if (rr3 & ZSRR3_IP_B_TX) + (*cs1->cs_ops->zsop_txint)(cs1); + } + + /* Note: caller will check cs_x->cs_softreq and DTRT. */ + return handled; +} + + +/* + * ZS software interrupt. Scan all channels for deferred interrupts. + */ +int +zsc_intr_soft(void *arg) +{ + struct zsc_softc *zsc = arg; + struct zs_chanstate *cs; + int rval, chan; + + rval = 0; + for (chan = 0; chan < 2; chan++) { + cs = zsc->zsc_cs[chan]; + + /* + * The softint flag can be safely cleared once + * we have decided to call the softint routine. + * (No need to do splzs() first.) + */ + if (cs->cs_softreq) { + cs->cs_softreq = 0; + (*cs->cs_ops->zsop_softint)(cs); + rval++; + } + } + return (rval); +} + +/* + * Provide a null zs "ops" vector. + */ + +static void zsnull_rxint (struct zs_chanstate *); +static void zsnull_stint (struct zs_chanstate *, int); +static void zsnull_txint (struct zs_chanstate *); +static void zsnull_softint(struct zs_chanstate *); + +static void +zsnull_rxint(struct zs_chanstate *cs) +{ + + /* Ask for softint() call. */ + cs->cs_softreq = 1; +} + +static void +zsnull_stint(struct zs_chanstate *cs, int force) +{ + + /* Ask for softint() call. */ + cs->cs_softreq = 1; +} + +static void +zsnull_txint(struct zs_chanstate *cs) +{ + + /* Ask for softint() call. */ + cs->cs_softreq = 1; +} + +static void +zsnull_softint(struct zs_chanstate *cs) +{ + + zs_write_reg(cs, 1, 0); + zs_write_reg(cs, 15, 0); +} + +struct zsops zsops_null = { + zsnull_rxint, /* receive char available */ + zsnull_stint, /* external/status */ + zsnull_txint, /* xmit buffer empty */ + zsnull_softint, /* process software interrupt */ +}; diff --git a/sys/arch/sgi/hpc/z8530sc.h b/sys/arch/sgi/hpc/z8530sc.h new file mode 100644 index 00000000000..d4b707ad518 --- /dev/null +++ b/sys/arch/sgi/hpc/z8530sc.h @@ -0,0 +1,199 @@ +/* $OpenBSD: z8530sc.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: z8530sc.h,v 1.26 2009/05/22 03:51:30 mrg Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)zsvar.h 8.1 (Berkeley) 6/11/93 + */ + +/* + * Copyright (c) 1994 Gordon W. Ross + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)zsvar.h 8.1 (Berkeley) 6/11/93 + */ + +/* + * Function vector - per channel + */ +struct zs_chanstate; +struct zsops { + void (*zsop_rxint)(struct zs_chanstate *); + /* receive char available */ + void (*zsop_stint)(struct zs_chanstate *, int); + /* external/status */ + void (*zsop_txint)(struct zs_chanstate *); + /* xmit buffer empty */ + void (*zsop_softint)(struct zs_chanstate *); + /* process software interrupt */ +}; + +extern struct zsops zsops_null; + + +/* + * Software state, per zs channel. + */ +struct zs_chanstate { + + /* Pointers to the device registers. */ + volatile uint8_t *cs_reg_csr; /* ctrl, status, and reg. number. */ + volatile uint8_t *cs_reg_data; /* data or numbered register */ + + int cs_channel; /* sub-unit number */ + void *cs_private; /* sub-driver data pointer */ + struct zsops *cs_ops; + + int cs_brg_clk; /* BAUD Rate Generator clock + * (usually PCLK / 16) */ + int cs_defspeed; /* default baud rate */ + int cs_defcflag; /* default cflag */ + + /* + * We must keep a copy of the write registers as they are + * mostly write-only and we sometimes need to set and clear + * individual bits (e.g., in WR3). Not all of these are + * needed but 16 bytes is cheap and this makes the addressing + * simpler. Unfortunately, we can only write to some registers + * when the chip is not actually transmitting, so whenever + * we are expecting a `transmit done' interrupt the preg array + * is allowed to `get ahead' of the current values. In a + * few places we must change the current value of a register, + * rather than (or in addition to) the pending value; for these + * cs_creg[] contains the current value. + */ + uint8_t cs_creg[16]; /* current values */ + uint8_t cs_preg[16]; /* pending values */ + int cs_heldchange; /* change pending (creg != preg) */ + + uint8_t cs_rr0; /* last rr0 processed */ + uint8_t cs_rr0_delta; /* rr0 changes at status intr. */ + uint8_t cs_rr0_mask; /* rr0 bits that stop output */ + uint8_t cs_rr0_dcd; /* which bit to read as DCD */ + uint8_t cs_rr0_cts; /* which bit to read as CTS */ + uint8_t cs_rr0_pps; /* which bit to use for PPS */ + /* the above is set only while CRTSCTS is enabled. */ + + uint8_t cs_wr5_dtr; /* which bit to write as DTR */ + uint8_t cs_wr5_rts; /* which bit to write as RTS */ + /* the above is set only while CRTSCTS is enabled. */ + + volatile uint8_t cs_softreq; /* need soft interrupt call */ + char cs_cua; /* CUA mode flag */ + + /* + * For strange systems that have oddly wired serial ports, we + * provide a pointer to the channel state of the port that has + * our status lines on it. + */ + struct zs_chanstate *cs_ctl_chan; + + /* power management hooks */ + int (*enable)(struct zs_chanstate *); + void (*disable)(struct zs_chanstate *); + int enabled; + + /* MD code might define a larger variant of this. */ +}; + +struct consdev; +struct zsc_attach_args { + int channel; /* two serial channels per zsc */ + int hwflags; /* see definitions below */ + /* `consdev' is only valid if ZS_HWFLAG_USE_CONSDEV is set */ + struct consdev *consdev; +}; + +/* In case of split console devices, use these: */ +#define ZS_HWFLAG_CONSOLE_INPUT 1 +#define ZS_HWFLAG_CONSOLE_OUTPUT 2 +#define ZS_HWFLAG_CONSOLE \ + (ZS_HWFLAG_CONSOLE_INPUT | ZS_HWFLAG_CONSOLE_OUTPUT) +#define ZS_HWFLAG_NO_DCD 4 /* Ignore the DCD bit */ +#define ZS_HWFLAG_NO_CTS 8 /* Ignore the CTS bit */ +#define ZS_HWFLAG_RAW 16 /* advise raw mode */ +#define ZS_HWFLAG_USE_CONSDEV 32 /* Use console ops from `consdev' */ +#define ZS_HWFLAG_NORESET 64 /* Don't reset at attach time */ + +extern int zs_major; + +int zsc_intr_soft(void *); +int zsc_intr_hard(void *); + +void zs_abort(struct zs_chanstate *); +void zs_break(struct zs_chanstate *, int); +void zs_iflush(struct zs_chanstate *); +void zs_loadchannelregs(struct zs_chanstate *); +int zs_set_speed(struct zs_chanstate *, int); +int zs_set_modes(struct zs_chanstate *, int); diff --git a/sys/arch/sgi/hpc/z8530tty.c b/sys/arch/sgi/hpc/z8530tty.c new file mode 100644 index 00000000000..4108ed6ea10 --- /dev/null +++ b/sys/arch/sgi/hpc/z8530tty.c @@ -0,0 +1,1663 @@ +/* $OpenBSD: z8530tty.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: z8530tty.c,v 1.128 2011/04/24 16:27:00 rmind Exp $ */ + +/*- + * Copyright (c) 1993, 1994, 1995, 1996, 1997, 1998, 1999 + * Charles M. Hannum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Charles M. Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)zs.c 8.1 (Berkeley) 7/19/93 + */ + +/* + * Copyright (c) 1994 Gordon W. Ross + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)zs.c 8.1 (Berkeley) 7/19/93 + */ + +/* + * Zilog Z8530 Dual UART driver (tty interface) + * + * This is the "slave" driver that will be attached to + * the "zsc" driver for plain "tty" async. serial lines. + * + * Credits, history: + * + * The original version of this code was the sparc/dev/zs.c driver + * as distributed with the Berkeley 4.4 Lite release. Since then, + * Gordon Ross reorganized the code into the current parent/child + * driver scheme, separating the Sun keyboard and mouse support + * into independent child drivers. + * + * RTS/CTS flow-control support was a collaboration of: + * Gordon Ross <gwr@NetBSD.org>, + * Bill Studenmund <wrstuden@loki.stanford.edu> + * Ian Dall <Ian.Dall@dsto.defence.gov.au> + * + * The driver was massively overhauled in November 1997 by Charles Hannum, + * fixing *many* bugs, and substantially improving performance. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/device.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> +#include <sys/tty.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/syslog.h> + +#include <dev/ic/z8530reg.h> +#include <machine/z8530var.h> + +#include <dev/cons.h> + +/* + * How many input characters we can buffer. + * The port-specific var.h may override this. + * Note: must be a power of two! + */ +#ifndef ZSTTY_RING_SIZE +#define ZSTTY_RING_SIZE 2048 +#endif + +struct cfdriver zstty_cd = { + NULL, "zstty", DV_TTY +}; + +/* + * Make this an option variable one can patch. + * But be warned: this must be a power of 2! + */ +u_int zstty_rbuf_size = ZSTTY_RING_SIZE; + +/* Stop input when 3/4 of the ring is full; restart when only 1/4 is full. */ +u_int zstty_rbuf_hiwat = (ZSTTY_RING_SIZE * 1) / 4; +u_int zstty_rbuf_lowat = (ZSTTY_RING_SIZE * 3) / 4; + +struct zstty_softc { + struct device zst_dev; /* required first: base device */ + struct tty *zst_tty; + struct zs_chanstate *zst_cs; + + struct timeout zst_diag_ch; + + u_int zst_overflows, + zst_floods, + zst_errors; + + int zst_hwflags, /* see z8530var.h */ + zst_swflags; /* TIOCFLAG_SOFTCAR, ... <ttycom.h> */ + + u_int zst_r_hiwat, + zst_r_lowat; + uint8_t *volatile zst_rbget, + *volatile zst_rbput; + volatile u_int zst_rbavail; + uint8_t *zst_rbuf, + *zst_ebuf; + + /* + * The transmit byte count and address are used for pseudo-DMA + * output in the hardware interrupt code. PDMA can be suspended + * to get pending changes done; heldtbc is used for this. It can + * also be stopped for ^S; this sets TS_TTSTOP in tp->t_state. + */ + uint8_t *zst_tba; /* transmit buffer address */ + u_int zst_tbc, /* transmit byte count */ + zst_heldtbc; /* held tbc while xmission stopped */ + + /* Flags to communicate with zstty_softint() */ + volatile uint8_t zst_rx_flags, /* receiver blocked */ +#define RX_TTY_BLOCKED 0x01 +#define RX_TTY_OVERFLOWED 0x02 +#define RX_IBUF_BLOCKED 0x04 +#define RX_IBUF_OVERFLOWED 0x08 +#define RX_ANY_BLOCK 0x0f + zst_tx_busy, /* working on an output chunk */ + zst_tx_done, /* done with one output chunk */ + zst_tx_stopped, /* H/W level stop (lost CTS) */ + zst_st_check, /* got a status interrupt */ + zst_rx_ready; + + /* PPS signal on DCD, with or without inkernel clock disciplining */ + uint8_t zst_ppsmask; /* pps signal mask */ + uint8_t zst_ppsassert; /* pps leading edge */ + uint8_t zst_ppsclear; /* pps trailing edge */ +}; + +/* Definition of the driver for autoconfig. */ +int zstty_match(struct device *, void *, void *); +void zstty_attach(struct device *, struct device *, void *); + +const struct cfattach zstty_ca = { + sizeof(struct zstty_softc), zstty_match, zstty_attach +}; + +cdev_decl(zs); + +struct zsops zsops_tty; + +void zs_shutdown(struct zstty_softc *); +void zsstart(struct tty *); +int zsparam(struct tty *, struct termios *); +void zs_modem(struct zstty_softc *, int); +void tiocm_to_zs(struct zstty_softc *, u_long, int); +int zs_to_tiocm(struct zstty_softc *); +int zshwiflow(struct tty *, int); +void zs_hwiflow(struct zstty_softc *); +void zs_maskintr(struct zstty_softc *); + +struct zstty_softc *zs_device_lookup(struct cfdriver *, int); + +/* Low-level routines. */ +void zstty_rxint (struct zs_chanstate *); +void zstty_stint (struct zs_chanstate *, int); +void zstty_txint (struct zs_chanstate *); +void zstty_softint(struct zs_chanstate *); +void zstty_diag(void *); + +#define ZSUNIT(x) (minor(x) & 0x7f) +#define ZSDIALOUT(x) (minor(x) & 0x80) + +struct zstty_softc * +zs_device_lookup(struct cfdriver *cf, int unit) +{ + return (struct zstty_softc *)device_lookup(cf, unit); +} + +/* + * zstty_match: how is this zs channel configured? + */ +int +zstty_match(struct device *parent, void *vcf, void *aux) +{ + struct cfdata *cf = vcf; + struct zsc_attach_args *args = aux; + + /* Exact match is better than wildcard. */ + if (cf->cf_loc[0] == args->channel) + return 2; + + /* This driver accepts wildcard. */ + if (cf->cf_loc[0] == -1) + return 1; + + return 0; +} + +void +zstty_attach(struct device *parent, struct device *self, void *aux) +{ + struct zsc_softc *zsc = (struct zsc_softc *)parent; + struct zstty_softc *zst = (struct zstty_softc *)self; + struct cfdata *cf = self->dv_cfdata; + struct zsc_attach_args *args = aux; + struct zs_chanstate *cs; + struct tty *tp; + int channel, s, tty_unit; + dev_t dev; + const char *i, *o; + int dtr_on; + int resetbit; + + timeout_set(&zst->zst_diag_ch, zstty_diag, zst); + + tty_unit = zst->zst_dev.dv_unit; + channel = args->channel; + cs = zsc->zsc_cs[channel]; + cs->cs_private = zst; + cs->cs_ops = &zsops_tty; + + zst->zst_cs = cs; + zst->zst_swflags = cf->cf_flags; /* softcar, etc. */ + zst->zst_hwflags = args->hwflags; + dev = makedev(zs_major, tty_unit); + + if (zst->zst_swflags) + printf(" flags 0x%x", zst->zst_swflags); + + /* + * Check whether we serve as a console device. + * XXX - split console input/output channels aren't + * supported yet on /dev/console + */ + i = o = NULL; + if ((zst->zst_hwflags & ZS_HWFLAG_CONSOLE_INPUT) != 0) { + i = " input"; + if ((args->hwflags & ZS_HWFLAG_USE_CONSDEV) != 0) { + args->consdev->cn_dev = dev; + cn_tab->cn_pollc = args->consdev->cn_pollc; + cn_tab->cn_getc = args->consdev->cn_getc; + } + cn_tab->cn_dev = dev; + } + if ((zst->zst_hwflags & ZS_HWFLAG_CONSOLE_OUTPUT) != 0) { + o = " output"; + if ((args->hwflags & ZS_HWFLAG_USE_CONSDEV) != 0) { + cn_tab->cn_putc = args->consdev->cn_putc; + } + cn_tab->cn_dev = dev; + } + if (i != NULL || o != NULL) + printf(": console%s", i ? (o ? "" : i) : o); + +#ifdef KGDB + if (zs_check_kgdb(cs, dev)) { + /* + * Allow kgdb to "take over" this port. Returns true + * if this serial port is in-use by kgdb. + */ + printf(" (kgdb)\n"); + /* + * This is the kgdb port (exclusive use) + * so skip the normal attach code. + */ + return; + } +#endif + + printf("\n"); + + tp = ttymalloc(0); + tp->t_dev = dev; + tp->t_oproc = zsstart; + tp->t_param = zsparam; + tp->t_hwiflow = zshwiflow; + + zst->zst_tty = tp; + zst->zst_rbuf = malloc(zstty_rbuf_size << 1, M_DEVBUF, M_WAITOK); + zst->zst_ebuf = zst->zst_rbuf + (zstty_rbuf_size << 1); + /* Disable the high water mark. */ + zst->zst_r_hiwat = 0; + zst->zst_r_lowat = 0; + zst->zst_rbget = zst->zst_rbput = zst->zst_rbuf; + zst->zst_rbavail = zstty_rbuf_size; + + /* if there are no enable/disable functions, assume the device + is always enabled */ + if (!cs->enable) + cs->enabled = 1; + + /* + * Hardware init + */ + dtr_on = 0; + resetbit = 0; + if (ISSET(zst->zst_hwflags, ZS_HWFLAG_CONSOLE)) { + /* Call zsparam similar to open. */ + struct termios t; + + /* Wait a while for previous console output to complete */ + DELAY(10000); + + /* Setup the "new" parameters in t. */ + t.c_ispeed = 0; + t.c_ospeed = cs->cs_defspeed; + t.c_cflag = cs->cs_defcflag; + + s = splzs(); + + /* + * Turn on receiver and status interrupts. + * We defer the actual write of the register to zsparam(), + * but we must make sure status interrupts are turned on by + * the time zsparam() reads the initial rr0 state. + */ + SET(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_SIE); + + splx(s); + + /* Make sure zsparam will see changes. */ + tp->t_ospeed = 0; + (void)zsparam(tp, &t); + + /* Make sure DTR is on now. */ + dtr_on = 1; + } else if (!ISSET(zst->zst_hwflags, ZS_HWFLAG_NORESET)) { + /* Not the console; may need reset. */ + resetbit = (channel == 0) ? ZSWR9_A_RESET : ZSWR9_B_RESET; + } + + s = splzs(); + if (resetbit) + zs_write_reg(cs, 9, resetbit); + zs_modem(zst, dtr_on); + splx(s); +} + + +/* + * Return pointer to our tty. + */ +struct tty * +zstty(dev_t dev) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev)); + + return (zst->zst_tty); +} + + +void +zs_shutdown(struct zstty_softc *zst) +{ + struct zs_chanstate *cs = zst->zst_cs; + struct tty *tp = zst->zst_tty; + int s; + + s = splzs(); + + /* If we were asserting flow control, then deassert it. */ + SET(zst->zst_rx_flags, RX_IBUF_BLOCKED); + zs_hwiflow(zst); + + /* Clear any break condition set with TIOCSBRK. */ + zs_break(cs, 0); + + /* Turn off PPS capture on last close. */ + zst->zst_ppsmask = 0; + + /* + * Hang up if necessary. Wait a bit, so the other side has time to + * notice even if we immediately open the port again. + */ + if (ISSET(tp->t_cflag, HUPCL) || ISSET(tp->t_state, TS_WOPEN)) { + zs_modem(zst, 0); + /* hold low for 1 second */ + (void)tsleep(cs, TTIPRI, ttclos, hz); + } + + /* Turn off interrupts if not the console. */ + if (!ISSET(zst->zst_hwflags, ZS_HWFLAG_CONSOLE)) { + CLR(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_TIE | ZSWR1_SIE); + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + } + + /* Call the power management hook. */ + if (cs->disable) { +#ifdef DIAGNOSTIC + if (!cs->enabled) + panic("%s: not enabled?", __func__); +#endif + (*cs->disable)(zst->zst_cs); + } + + splx(s); +} + +/* + * Open a zs serial (tty) port. + */ +int +zsopen(dev_t dev, int flags, int mode, struct proc *p) +{ + struct zstty_softc *zst; + struct zs_chanstate *cs; + struct tty *tp; + int s, s2; + int error; + + zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev)); + if (zst == NULL) + return (ENXIO); + + tp = zst->zst_tty; + cs = zst->zst_cs; + + /* If KGDB took the line, then tp==NULL */ + if (tp == NULL) + return (EBUSY); + + if (ISSET(tp->t_state, TS_ISOPEN) && + ISSET(tp->t_state, TS_XCLUDE) && + suser(p, 0) != 0) + return (EBUSY); + + s = spltty(); + + /* + * Do the following iff this is a first open. + */ + if (!ISSET(tp->t_state, TS_ISOPEN)) { + struct termios t; + + tp->t_dev = dev; + + /* Call the power management hook. */ + if (cs->enable) { + if ((*cs->enable)(cs)) { + splx(s); + printf("%s: device enable failed\n", + zst->zst_dev.dv_xname); + return (EIO); + } + } + + /* + * Initialize the termios status to the defaults. Add in the + * sticky bits from TIOCSFLAGS. + */ + t.c_ispeed = 0; + t.c_ospeed = cs->cs_defspeed; + t.c_cflag = cs->cs_defcflag; + if (ISSET(zst->zst_swflags, TIOCFLAG_CLOCAL)) + SET(t.c_cflag, CLOCAL); + if (ISSET(zst->zst_swflags, TIOCFLAG_CRTSCTS)) + SET(t.c_cflag, CRTSCTS); + if (ISSET(zst->zst_swflags, TIOCFLAG_MDMBUF)) + SET(t.c_cflag, MDMBUF); + + s2 = splzs(); + + /* + * Turn on receiver and status interrupts. + * We defer the actual write of the register to zsparam(), + * but we must make sure status interrupts are turned on by + * the time zsparam() reads the initial rr0 state. + */ + SET(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_SIE); + + /* Clear PPS capture state on first open. */ + zst->zst_ppsmask = 0; + + splx(s2); + + /* Make sure zsparam will see changes. */ + tp->t_ospeed = 0; + (void)zsparam(tp, &t); + + /* + * Note: zsparam has done: cflag, ispeed, ospeed + * so we just need to do: iflag, oflag, lflag, cc + * For "raw" mode, just leave all zeros. + */ + if (!ISSET(zst->zst_hwflags, ZS_HWFLAG_RAW)) { + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_lflag = TTYDEF_LFLAG; + } else { + tp->t_iflag = 0; + tp->t_oflag = 0; + tp->t_lflag = 0; + } + ttychars(tp); + ttsetwater(tp); + + if (ZSDIALOUT(dev)) + SET(tp->t_state, TS_CARR_ON); + else + CLR(tp->t_state, TS_CARR_ON); + + s2 = splzs(); + + /* Clear the input ring, and unblock. */ + zst->zst_rbget = zst->zst_rbput = zst->zst_rbuf; + zst->zst_rbavail = zstty_rbuf_size; + zs_iflush(cs); + CLR(zst->zst_rx_flags, RX_ANY_BLOCK); + zs_hwiflow(zst); + + splx(s2); + } + + if (ZSDIALOUT(dev)) { + if (ISSET(tp->t_state, TS_ISOPEN)) { + /* someone already is dialed in... */ + splx(s); + return EBUSY; + } + cs->cs_cua = 1; + } + + error = 0; + /* wait for carrier if necessary */ + if (ISSET(flags, O_NONBLOCK)) { + if (!ZSDIALOUT(dev) && cs->cs_cua) { + /* Opening TTY non-blocking... but the CUA is busy */ + error = EBUSY; + } + } else + while (cs->cs_cua || + (!ISSET(tp->t_cflag, CLOCAL) && !ISSET(tp->t_state, TS_CARR_ON))) { + int rr0; + + error = 0; + SET(tp->t_state, TS_WOPEN); + + if (!ZSDIALOUT(dev) && !cs->cs_cua) { + /* + * Turn on DTR. We must always do this on non-CUA + * devices, even if carrier is not present, because + * otherwise we'd have to use TIOCSDTR immediately + * after setting CLOCAL, which applications do not + * expect. We always assert DTR while the device is + * open unless explicitly requested to deassert it. + */ + s2 = splzs(); + zs_modem(zst, 1); + rr0 = zs_read_csr(cs); + splx(s2); + + /* loop, turning on the device, until carrier present */ + if (ISSET(rr0, ZSRR0_DCD) || + ISSET(zst->zst_swflags, TIOCFLAG_SOFTCAR)) + SET(tp->t_state, TS_CARR_ON); + } + + if ((ISSET(tp->t_cflag, CLOCAL) || + ISSET(tp->t_state, TS_CARR_ON)) && !cs->cs_cua) + break; + + error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH, + ttopen, 0); + + if (!ZSDIALOUT(dev) && cs->cs_cua && error == EINTR) { + error = 0; + continue; + } + + if (error) { + if (!ISSET(tp->t_state, TS_ISOPEN)) { + s2 = splzs(); + zs_modem(zst, 0); + splx(s2); + CLR(tp->t_state, TS_WOPEN); + ttwakeup(tp); + } + if (ZSDIALOUT(dev)) + cs->cs_cua = 0; + CLR(tp->t_state, TS_WOPEN); + break; + } + if (!ZSDIALOUT(dev) && cs->cs_cua) + continue; + } + + splx(s); + + if (error == 0) + error = ((*linesw[tp->t_line].l_open)(dev, tp, p)); + if (error) + goto bad; + + return (0); + +bad: + if (!ISSET(tp->t_state, TS_ISOPEN)) { + /* + * We failed to open the device, and nobody else had it opened. + * Clean up the state as appropriate. + */ + zs_shutdown(zst); + } + + return (error); +} + +/* + * Close a zs serial port. + */ +int +zsclose(dev_t dev, int flags, int mode, struct proc *p) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev)); + struct zs_chanstate *cs = zst->zst_cs; + struct tty *tp = zst->zst_tty; + int s; + + /* XXX This is for cons.c. */ + if (!ISSET(tp->t_state, TS_ISOPEN)) + return 0; + + (*linesw[tp->t_line].l_close)(tp, flags, p); + + s = spltty(); + cs->cs_cua = 0; + ttyclose(tp); + splx(s); + + if (!ISSET(tp->t_state, TS_ISOPEN)) { + /* + * Although we got a last close, the device may still be in + * use; e.g. if this was the dialout node, and there are still + * processes waiting for carrier on the non-dialout node. + */ + zs_shutdown(zst); + } + + return (0); +} + +/* + * Read/write zs serial port. + */ +int +zsread(dev_t dev, struct uio *uio, int flags) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev)); + struct tty *tp = zst->zst_tty; + + return (*linesw[tp->t_line].l_read)(tp, uio, flags); +} + +int +zswrite(dev_t dev, struct uio *uio, int flags) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev)); + struct tty *tp = zst->zst_tty; + + return (*linesw[tp->t_line].l_write)(tp, uio, flags); +} + +int +zsioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev)); + struct zs_chanstate *cs = zst->zst_cs; + struct tty *tp = zst->zst_tty; + int error; + int s; + + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + error = ttioctl(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + +#ifdef ZS_MD_IOCTL + error = ZS_MD_IOCTL; + if (error >= 0) + return (error); +#endif /* ZS_MD_IOCTL */ + + error = 0; + + s = splzs(); + + switch (cmd) { + case TIOCSBRK: + zs_break(cs, 1); + break; + + case TIOCCBRK: + zs_break(cs, 0); + break; + + case TIOCGFLAGS: + *(int *)data = zst->zst_swflags; + break; + + case TIOCSFLAGS: + error = suser(p, 0); + if (error) + break; + zst->zst_swflags = *(int *)data; + break; + + case TIOCSDTR: + zs_modem(zst, 1); + break; + + case TIOCCDTR: + zs_modem(zst, 0); + break; + + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + tiocm_to_zs(zst, cmd, *(int *)data); + break; + + case TIOCMGET: + *(int *)data = zs_to_tiocm(zst); + break; + + default: + error = ENOTTY; + break; + } + + splx(s); + + return (error); +} + +/* + * Start or restart transmission. + */ +void +zsstart(struct tty *tp) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(tp->t_dev)); + struct zs_chanstate *cs = zst->zst_cs; + u_char *tba; + int tbc; + int s; + + s = spltty(); + if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) + goto out; + if (zst->zst_tx_stopped) + goto out; + + ttwakeupwr(tp); + if (tp->t_outq.c_cc == 0) + goto out; + + /* Grab the first contiguous region of buffer space. */ + tba = tp->t_outq.c_cf; + tbc = ndqb(&tp->t_outq, 0); + + (void)splzs(); + + zst->zst_tba = tba; + zst->zst_tbc = tbc; + SET(tp->t_state, TS_BUSY); + zst->zst_tx_busy = 1; + + /* Enable transmit completion interrupts if necessary. */ + if (!ISSET(cs->cs_preg[1], ZSWR1_TIE)) { + SET(cs->cs_preg[1], ZSWR1_TIE); + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + } + + /* Output the first character of the contiguous buffer. */ + zs_write_data(cs, *zst->zst_tba); + zst->zst_tbc--; + zst->zst_tba++; + +out: + splx(s); +} + +/* + * Stop output, e.g., for ^S or output flush. + */ +int +zsstop(struct tty *tp, int flag) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(tp->t_dev)); + int s; + + s = splzs(); + if (ISSET(tp->t_state, TS_BUSY)) { + /* Stop transmitting at the next chunk. */ + zst->zst_tbc = 0; + zst->zst_heldtbc = 0; + if (!ISSET(tp->t_state, TS_TTSTOP)) + SET(tp->t_state, TS_FLUSH); + } + splx(s); + return 0; +} + +/* + * Set ZS tty parameters from termios. + * XXX - Should just copy the whole termios after + * making sure all the changes could be done. + */ +int +zsparam(struct tty *tp, struct termios *t) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(tp->t_dev)); + struct zs_chanstate *cs = zst->zst_cs; + int ospeed; + tcflag_t cflag; + uint8_t tmp3, tmp4, tmp5; + int s, error; + + ospeed = t->c_ospeed; + cflag = t->c_cflag; + + /* Check requested parameters. */ + if (ospeed < 0) + return (EINVAL); + if (t->c_ispeed && t->c_ispeed != ospeed) + return (EINVAL); + + /* + * For the console, always force CLOCAL and !HUPCL, so that the port + * is always active. + */ + if (ISSET(zst->zst_swflags, TIOCFLAG_SOFTCAR) || + ISSET(zst->zst_hwflags, ZS_HWFLAG_CONSOLE)) { + SET(cflag, CLOCAL); + CLR(cflag, HUPCL); + } + + /* + * Only whack the UART when params change. + * Some callers need to clear tp->t_ospeed + * to make sure initialization gets done. + */ + if (tp->t_ospeed == ospeed && + tp->t_cflag == cflag) + return (0); + + /* + * Call MD functions to deal with changed + * clock modes or H/W flow control modes. + * The BRG divisor is set now. (reg 12,13) + */ + error = zs_set_speed(cs, ospeed); + if (error) + return (error); + error = zs_set_modes(cs, cflag); + if (error) + return (error); + + /* + * Block interrupts so that state will not + * be altered until we are done setting it up. + * + * Initial values in cs_preg are set before + * our attach routine is called. The master + * interrupt enable is handled by zsc.c + * + */ + s = splzs(); + + /* + * Recalculate which status ints to enable. + */ + zs_maskintr(zst); + + /* Recompute character size bits. */ + tmp3 = cs->cs_preg[3]; + tmp5 = cs->cs_preg[5]; + CLR(tmp3, ZSWR3_RXSIZE); + CLR(tmp5, ZSWR5_TXSIZE); + switch (ISSET(cflag, CSIZE)) { + case CS5: + SET(tmp3, ZSWR3_RX_5); + SET(tmp5, ZSWR5_TX_5); + break; + case CS6: + SET(tmp3, ZSWR3_RX_6); + SET(tmp5, ZSWR5_TX_6); + break; + case CS7: + SET(tmp3, ZSWR3_RX_7); + SET(tmp5, ZSWR5_TX_7); + break; + case CS8: + SET(tmp3, ZSWR3_RX_8); + SET(tmp5, ZSWR5_TX_8); + break; + } + cs->cs_preg[3] = tmp3; + cs->cs_preg[5] = tmp5; + + /* + * Recompute the stop bits and parity bits. Note that + * zs_set_speed() may have set clock selection bits etc. + * in wr4, so those must preserved. + */ + tmp4 = cs->cs_preg[4]; + CLR(tmp4, ZSWR4_SBMASK | ZSWR4_PARMASK); + if (ISSET(cflag, CSTOPB)) + SET(tmp4, ZSWR4_TWOSB); + else + SET(tmp4, ZSWR4_ONESB); + if (!ISSET(cflag, PARODD)) + SET(tmp4, ZSWR4_EVENP); + if (ISSET(cflag, PARENB)) + SET(tmp4, ZSWR4_PARENB); + cs->cs_preg[4] = tmp4; + + /* And copy to tty. */ + tp->t_ispeed = 0; + tp->t_ospeed = ospeed; + tp->t_cflag = cflag; + + /* + * If nothing is being transmitted, set up new current values, + * else mark them as pending. + */ + if (!cs->cs_heldchange) { + if (zst->zst_tx_busy) { + zst->zst_heldtbc = zst->zst_tbc; + zst->zst_tbc = 0; + cs->cs_heldchange = 1; + } else + zs_loadchannelregs(cs); + } + + /* + * If hardware flow control is disabled, turn off the buffer water + * marks and unblock any soft flow control state. Otherwise, enable + * the water marks. + */ + if (!ISSET(cflag, CHWFLOW)) { + zst->zst_r_hiwat = 0; + zst->zst_r_lowat = 0; + if (ISSET(zst->zst_rx_flags, RX_TTY_OVERFLOWED)) { + CLR(zst->zst_rx_flags, RX_TTY_OVERFLOWED); + zst->zst_rx_ready = 1; + cs->cs_softreq = 1; + } + if (ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED|RX_IBUF_BLOCKED)) { + CLR(zst->zst_rx_flags, RX_TTY_BLOCKED|RX_IBUF_BLOCKED); + zs_hwiflow(zst); + } + } else { + zst->zst_r_hiwat = zstty_rbuf_hiwat; + zst->zst_r_lowat = zstty_rbuf_lowat; + } + + /* + * Force a recheck of the hardware carrier and flow control status, + * since we may have changed which bits we're looking at. + */ + zstty_stint(cs, 1); + + splx(s); + + /* + * If hardware flow control is disabled, unblock any hard flow control + * state. + */ + if (!ISSET(cflag, CHWFLOW)) { + if (zst->zst_tx_stopped) { + zst->zst_tx_stopped = 0; + zsstart(tp); + } + } + + zstty_softint(cs); + + return (0); +} + +/* + * Compute interrupt enable bits and set in the pending bits. Called both + * in zsparam() and when PPS (pulse per second timing) state changes. + * Must be called at splzs(). + */ +void +zs_maskintr(struct zstty_softc *zst) +{ + struct zs_chanstate *cs = zst->zst_cs; + uint8_t tmp15; + + cs->cs_rr0_mask = cs->cs_rr0_cts | cs->cs_rr0_dcd; + if (zst->zst_ppsmask != 0) + cs->cs_rr0_mask |= cs->cs_rr0_pps; + tmp15 = cs->cs_preg[15]; + if (ISSET(cs->cs_rr0_mask, ZSRR0_DCD)) + SET(tmp15, ZSWR15_DCD_IE); + else + CLR(tmp15, ZSWR15_DCD_IE); + if (ISSET(cs->cs_rr0_mask, ZSRR0_CTS)) + SET(tmp15, ZSWR15_CTS_IE); + else + CLR(tmp15, ZSWR15_CTS_IE); + cs->cs_preg[15] = tmp15; +} + + +/* + * Raise or lower modem control (DTR/RTS) signals. If a character is + * in transmission, the change is deferred. + * Called at splzs(). + */ +void +zs_modem(struct zstty_softc *zst, int onoff) +{ + struct zs_chanstate *cs = zst->zst_cs, *ccs; + + if (cs->cs_wr5_dtr == 0) + return; + + ccs = (cs->cs_ctl_chan != NULL ? cs->cs_ctl_chan : cs); + + if (onoff) + SET(ccs->cs_preg[5], cs->cs_wr5_dtr); + else + CLR(ccs->cs_preg[5], cs->cs_wr5_dtr); + + if (!cs->cs_heldchange) { + if (zst->zst_tx_busy) { + zst->zst_heldtbc = zst->zst_tbc; + zst->zst_tbc = 0; + cs->cs_heldchange = 1; + } else + zs_loadchannelregs(cs); + } +} + +/* + * Set modem bits. + * Called at splzs(). + */ +void +tiocm_to_zs(struct zstty_softc *zst, u_long how, int ttybits) +{ + struct zs_chanstate *cs = zst->zst_cs, *ccs; + uint8_t zsbits; + + ccs = (cs->cs_ctl_chan != NULL ? cs->cs_ctl_chan : cs); + + zsbits = 0; + if (ISSET(ttybits, TIOCM_DTR)) + SET(zsbits, ZSWR5_DTR); + if (ISSET(ttybits, TIOCM_RTS)) + SET(zsbits, ZSWR5_RTS); + + switch (how) { + case TIOCMBIC: + CLR(ccs->cs_preg[5], zsbits); + break; + + case TIOCMBIS: + SET(ccs->cs_preg[5], zsbits); + break; + + case TIOCMSET: + CLR(ccs->cs_preg[5], ZSWR5_RTS | ZSWR5_DTR); + SET(ccs->cs_preg[5], zsbits); + break; + } + + if (!cs->cs_heldchange) { + if (zst->zst_tx_busy) { + zst->zst_heldtbc = zst->zst_tbc; + zst->zst_tbc = 0; + cs->cs_heldchange = 1; + } else + zs_loadchannelregs(cs); + } +} + +/* + * Get modem bits. + * Called at splzs(). + */ +int +zs_to_tiocm(struct zstty_softc *zst) +{ + struct zs_chanstate *cs = zst->zst_cs, *ccs; + uint8_t zsbits; + int ttybits = 0; + + ccs = (cs->cs_ctl_chan != NULL ? cs->cs_ctl_chan : cs); + + zsbits = ccs->cs_preg[5]; + if (ISSET(zsbits, ZSWR5_DTR)) + SET(ttybits, TIOCM_DTR); + if (ISSET(zsbits, ZSWR5_RTS)) + SET(ttybits, TIOCM_RTS); + + zsbits = cs->cs_rr0; + if (ISSET(zsbits, ZSRR0_DCD)) + SET(ttybits, TIOCM_CD); + if (ISSET(zsbits, ZSRR0_CTS)) + SET(ttybits, TIOCM_CTS); + + return (ttybits); +} + +/* + * Try to block or unblock input using hardware flow-control. + * This is called by kern/tty.c if MDMBUF|CRTSCTS is set, and + * if this function returns non-zero, the TS_TBLOCK flag will + * be set or cleared according to the "block" arg passed. + */ +int +zshwiflow(struct tty *tp, int block) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(tp->t_dev)); + struct zs_chanstate *cs = zst->zst_cs; + int s; + + if (cs->cs_wr5_rts == 0) + return (0); + + s = splzs(); + if (block) { + if (!ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED)) { + SET(zst->zst_rx_flags, RX_TTY_BLOCKED); + zs_hwiflow(zst); + } + } else { + if (ISSET(zst->zst_rx_flags, RX_TTY_OVERFLOWED)) { + CLR(zst->zst_rx_flags, RX_TTY_OVERFLOWED); + zst->zst_rx_ready = 1; + cs->cs_softreq = 1; + } + if (ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED)) { + CLR(zst->zst_rx_flags, RX_TTY_BLOCKED); + zs_hwiflow(zst); + } + } + splx(s); + return (1); +} + +/* + * Internal version of zshwiflow + * Called at splzs() + */ +void +zs_hwiflow(struct zstty_softc *zst) +{ + struct zs_chanstate *cs = zst->zst_cs, *ccs; + + if (cs->cs_wr5_rts == 0) + return; + + ccs = (cs->cs_ctl_chan != NULL ? cs->cs_ctl_chan : cs); + + if (ISSET(zst->zst_rx_flags, RX_ANY_BLOCK)) { + CLR(ccs->cs_preg[5], cs->cs_wr5_rts); + CLR(ccs->cs_creg[5], cs->cs_wr5_rts); + } else { + SET(ccs->cs_preg[5], cs->cs_wr5_rts); + SET(ccs->cs_creg[5], cs->cs_wr5_rts); + } + zs_write_reg(ccs, 5, ccs->cs_creg[5]); +} + + +/**************************************************************** + * Interface to the lower layer (zscc) + ****************************************************************/ + +#define integrate static inline +integrate void zstty_rxsoft(struct zstty_softc *, struct tty *); +integrate void zstty_txsoft(struct zstty_softc *, struct tty *); +integrate void zstty_stsoft(struct zstty_softc *, struct tty *); +void zstty_diag(void *); + +/* + * Receiver Ready interrupt. + * Called at splzs(). + */ +void +zstty_rxint(struct zs_chanstate *cs) +{ + struct zstty_softc *zst = cs->cs_private; + uint8_t *put, *end; + u_int cc; + uint8_t rr0, rr1, c; + + end = zst->zst_ebuf; + put = zst->zst_rbput; + cc = zst->zst_rbavail; + + while (cc > 0) { + /* + * First read the status, because reading the received char + * destroys the status of this char. + */ + rr1 = zs_read_reg(cs, 1); + c = zs_read_data(cs); + + if (ISSET(rr1, ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) { + /* Clear the receive error. */ + zs_write_csr(cs, ZSWR0_RESET_ERRORS); + } + + put[0] = c; + put[1] = rr1; + put += 2; + if (put >= end) + put = zst->zst_rbuf; + cc--; + + rr0 = zs_read_csr(cs); + if (!ISSET(rr0, ZSRR0_RX_READY)) + break; + } + + /* + * Current string of incoming characters ended because + * no more data was available or we ran out of space. + * Schedule a receive event if any data was received. + * If we're out of space, turn off receive interrupts. + */ + zst->zst_rbput = put; + zst->zst_rbavail = cc; + if (!ISSET(zst->zst_rx_flags, RX_TTY_OVERFLOWED)) { + zst->zst_rx_ready = 1; + cs->cs_softreq = 1; + } + + /* + * See if we are in danger of overflowing a buffer. If + * so, use hardware flow control to ease the pressure. + */ + if (!ISSET(zst->zst_rx_flags, RX_IBUF_BLOCKED) && + cc < zst->zst_r_hiwat) { + SET(zst->zst_rx_flags, RX_IBUF_BLOCKED); + zs_hwiflow(zst); + } + + /* + * If we're out of space, disable receive interrupts + * until the queue has drained a bit. + */ + if (!cc) { + SET(zst->zst_rx_flags, RX_IBUF_OVERFLOWED); + CLR(cs->cs_preg[1], ZSWR1_RIE); + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + } +} + +/* + * Transmitter Ready interrupt. + * Called at splzs(). + */ +void +zstty_txint(struct zs_chanstate *cs) +{ + struct zstty_softc *zst = cs->cs_private; + + zs_write_csr(cs, ZSWR0_RESET_TXINT); + + /* + * If we've delayed a parameter change, do it now, and restart + * output. + */ + if (cs->cs_heldchange) { + zs_loadchannelregs(cs); + cs->cs_heldchange = 0; + zst->zst_tbc = zst->zst_heldtbc; + zst->zst_heldtbc = 0; + } + + /* Output the next character in the buffer, if any. */ + if (zst->zst_tbc > 0) { + zs_write_data(cs, *zst->zst_tba); + zst->zst_tbc--; + zst->zst_tba++; + } else { + /* Disable transmit completion interrupts if necessary. */ + if (ISSET(cs->cs_preg[1], ZSWR1_TIE)) { + CLR(cs->cs_preg[1], ZSWR1_TIE); + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + } + if (zst->zst_tx_busy) { + zst->zst_tx_busy = 0; + zst->zst_tx_done = 1; + cs->cs_softreq = 1; + } + } +} + +#ifdef DDB +#include <ddb/db_var.h> +#define DB_CONSOLE db_console +#else +#define DB_CONSOLE 1 +#endif + +/* + * Status Change interrupt. + * Called at splzs(). + */ +void +zstty_stint(struct zs_chanstate *cs, int force) +{ + struct zstty_softc *zst = cs->cs_private; + struct tty *tp = zst->zst_tty; + uint8_t rr0, delta; + + rr0 = zs_read_csr(cs); + zs_write_csr(cs, ZSWR0_RESET_STATUS); + + /* + * Check here for console break, so that we can abort + * even when interrupts are locking up the machine. + */ + if ((zst->zst_hwflags & ZS_HWFLAG_CONSOLE_INPUT) && + ISSET(rr0, ZSRR0_BREAK) && DB_CONSOLE) + zs_abort(cs); + + if (!force) + delta = rr0 ^ cs->cs_rr0; + else + delta = cs->cs_rr0_mask; + + ttytstamp(tp, cs->cs_rr0 & ZSRR0_CTS, rr0 & ZSRR0_CTS, + cs->cs_rr0 & ZSRR0_DCD, rr0 & ZSRR0_DCD); + + cs->cs_rr0 = rr0; + + if (ISSET(delta, cs->cs_rr0_mask)) { + SET(cs->cs_rr0_delta, delta); + + /* + * Stop output immediately if we lose the output + * flow control signal or carrier detect. + */ + if (ISSET(~rr0, cs->cs_rr0_mask)) { + zst->zst_tbc = 0; + zst->zst_heldtbc = 0; + } + + zst->zst_st_check = 1; + cs->cs_softreq = 1; + } +} + +void +zstty_diag(void *arg) +{ + struct zstty_softc *zst = arg; + int overflows, floods; + int s; + + s = splzs(); + overflows = zst->zst_overflows; + zst->zst_overflows = 0; + floods = zst->zst_floods; + zst->zst_floods = 0; + zst->zst_errors = 0; + splx(s); + + log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf flood%s\n", + zst->zst_dev.dv_xname, + overflows, overflows == 1 ? "" : "s", + floods, floods == 1 ? "" : "s"); +} + +integrate void +zstty_rxsoft(struct zstty_softc *zst, struct tty *tp) +{ + struct zs_chanstate *cs = zst->zst_cs; + int (*rint)(int, struct tty *) = linesw[tp->t_line].l_rint; + uint8_t *get, *end; + u_int cc, scc; + uint8_t rr1; + int code; + int s; + + end = zst->zst_ebuf; + get = zst->zst_rbget; + scc = cc = zstty_rbuf_size - zst->zst_rbavail; + + if (cc == zstty_rbuf_size) { + zst->zst_floods++; + if (zst->zst_errors++ == 0) + timeout_add_sec(&zst->zst_diag_ch, 60); + } + + /* If not yet open, drop the entire buffer content here */ + if (!ISSET(tp->t_state, TS_ISOPEN)) { + get += cc << 1; + if (get >= end) + get -= zstty_rbuf_size << 1; + cc = 0; + } + while (cc) { + code = get[0]; + rr1 = get[1]; + if (ISSET(rr1, ZSRR1_DO | ZSRR1_FE | ZSRR1_PE)) { + if (ISSET(rr1, ZSRR1_DO)) { + zst->zst_overflows++; + if (zst->zst_errors++ == 0) + timeout_add_sec(&zst->zst_diag_ch, 60); + } + if (ISSET(rr1, ZSRR1_FE)) + SET(code, TTY_FE); + if (ISSET(rr1, ZSRR1_PE)) + SET(code, TTY_PE); + } + if ((*rint)(code, tp) == -1) { + /* + * The line discipline's buffer is out of space. + */ + if (!ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED)) { + /* + * We're either not using flow control, or the + * line discipline didn't tell us to block for + * some reason. Either way, we have no way to + * know when there's more space available, so + * just drop the rest of the data. + */ + get += cc << 1; + if (get >= end) + get -= zstty_rbuf_size << 1; + cc = 0; + } else { + /* + * Don't schedule any more receive processing + * until the line discipline tells us there's + * space available (through comhwiflow()). + * Leave the rest of the data in the input + * buffer. + */ + SET(zst->zst_rx_flags, RX_TTY_OVERFLOWED); + } + break; + } + get += 2; + if (get >= end) + get = zst->zst_rbuf; + cc--; + } + + if (cc != scc) { + zst->zst_rbget = get; + s = splzs(); + cc = zst->zst_rbavail += scc - cc; + /* Buffers should be ok again, release possible block. */ + if (cc >= zst->zst_r_lowat) { + if (ISSET(zst->zst_rx_flags, RX_IBUF_OVERFLOWED)) { + CLR(zst->zst_rx_flags, RX_IBUF_OVERFLOWED); + SET(cs->cs_preg[1], ZSWR1_RIE); + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + } + if (ISSET(zst->zst_rx_flags, RX_IBUF_BLOCKED)) { + CLR(zst->zst_rx_flags, RX_IBUF_BLOCKED); + zs_hwiflow(zst); + } + } + splx(s); + } +} + +integrate void +zstty_txsoft(struct zstty_softc *zst, struct tty *tp) +{ + int s; + + CLR(tp->t_state, TS_BUSY); + if (ISSET(tp->t_state, TS_FLUSH)) + CLR(tp->t_state, TS_FLUSH); + else { + s = splzs(); + ndflush(&tp->t_outq, (int)(zst->zst_tba - tp->t_outq.c_cf)); + splx(s); + } + (*linesw[tp->t_line].l_start)(tp); +} + +integrate void +zstty_stsoft(struct zstty_softc *zst, struct tty *tp) +{ + struct zs_chanstate *cs = zst->zst_cs; + uint8_t rr0, delta; + int s; + + s = splzs(); + rr0 = cs->cs_rr0; + delta = cs->cs_rr0_delta; + cs->cs_rr0_delta = 0; + splx(s); + + if (ISSET(delta, cs->cs_rr0_dcd)) { + /* + * Inform the tty layer that carrier detect changed. + */ + (void)(*linesw[tp->t_line].l_modem)(tp, ISSET(rr0, ZSRR0_DCD)); + } + + if (ISSET(delta, cs->cs_rr0_cts)) { + /* Block or unblock output according to flow control. */ + if (ISSET(rr0, cs->cs_rr0_cts)) { + zst->zst_tx_stopped = 0; + (*linesw[tp->t_line].l_start)(tp); + } else { + zst->zst_tx_stopped = 1; + } + } +} + +/* + * Software interrupt. Called at zssoft + * + * The main job to be done here is to empty the input ring + * by passing its contents up to the tty layer. The ring is + * always emptied during this operation, therefore the ring + * must not be larger than the space after "high water" in + * the tty layer, or the tty layer might drop our input. + * + * Note: an "input blockage" condition is assumed to exist if + * EITHER the TS_TBLOCK flag or zst_rx_blocked flag is set. + */ +void +zstty_softint(struct zs_chanstate *cs) +{ + struct zstty_softc *zst = cs->cs_private; + struct tty *tp = zst->zst_tty; + int s; + + s = spltty(); + + if (zst->zst_rx_ready) { + zst->zst_rx_ready = 0; + zstty_rxsoft(zst, tp); + } + + if (zst->zst_st_check) { + zst->zst_st_check = 0; + zstty_stsoft(zst, tp); + } + + if (zst->zst_tx_done) { + zst->zst_tx_done = 0; + zstty_txsoft(zst, tp); + } + + splx(s); +} + +struct zsops zsops_tty = { + zstty_rxint, /* receive char available */ + zstty_stint, /* external/status */ + zstty_txint, /* xmit buffer empty */ + zstty_softint, /* process software interrupt */ +}; diff --git a/sys/arch/sgi/hpc/zs.c b/sys/arch/sgi/hpc/zs.c new file mode 100644 index 00000000000..c8ce8b74102 --- /dev/null +++ b/sys/arch/sgi/hpc/zs.c @@ -0,0 +1,712 @@ +/* $OpenBSD: zs.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: zs.c,v 1.37 2011/02/20 07:59:50 matt Exp $ */ + +/*- + * Copyright (c) 1996, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Gordon W. Ross and Wayne Knowles + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Zilog Z8530 Dual UART driver (machine-dependent part) + * + * Runs two serial lines per chip using slave drivers. + * Plain tty/async lines use the zs_async slave. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/device.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/tty.h> +#include <sys/time.h> +#include <sys/syslog.h> + +#include <mips64/archtype.h> +#include <mips64/arcbios.h> + +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <machine/z8530var.h> + +#include <dev/cons.h> + +#include <dev/ic/z8530reg.h> + +#include <sgi/hpc/hpcvar.h> +#include <sgi/hpc/hpcreg.h> +#include <sgi/localbus/intvar.h> + +/* + * Some warts needed by z8530tty.c - + * The default parity REALLY needs to be the same as the PROM uses, + * or you can not see messages done with printf during boot-up... + */ +int zs_def_cflag = (CREAD | CS8 | HUPCL); +int zs_major = 19; + +#define PCLK 3672000 /* PCLK pin input clock rate */ + +#ifndef ZS_DEFSPEED +#define ZS_DEFSPEED 9600 +#endif + +/* + * Define interrupt levels. + */ +#define ZSHARD_PRI 64 + +/* SGI shouldn't need ZS_DELAY() as recovery time is done in hardware? */ +#define ZS_DELAY() delay(3) + +/* The layout of this is hardware-dependent (padding, order). */ +struct zschan { + uint8_t pad1[3]; + volatile uint8_t zc_csr; /* ctrl,status, and indirect access */ + uint8_t pad2[3]; + volatile uint8_t zc_data; /* data */ +}; + +struct zsdevice { + struct zschan zs_chan_b; + struct zschan zs_chan_a; +}; + +/* Return the byte offset of element within a structure */ +#define OFFSET(struct_def, el) ((size_t)&((struct_def *)0)->el) + +#define ZS_CHAN_A OFFSET(struct zsdevice, zs_chan_a) +#define ZS_CHAN_B OFFSET(struct zsdevice, zs_chan_b) +#define ZS_REG_CSR 3 +#define ZS_REG_DATA 7 +static int zs_chan_offset[] = {ZS_CHAN_A, ZS_CHAN_B}; + +cons_decl(zs); +struct consdev zs_cn = { + zscnprobe, + zscninit, + zscngetc, + zscnputc, + zscnpollc, + NULL +}; + + +/* Flags from cninit() */ +static int zs_consunit = -1; +static int zs_conschan = -1; + +/* Default speed for all channels */ +static int zs_defspeed = ZS_DEFSPEED; + +static uint8_t zs_init_reg[16] = { + 0, /* 0: CMD (reset, etc.) */ + 0, /* 1: No interrupts yet. */ + ZSHARD_PRI, /* 2: IVECT */ + ZSWR3_RX_8 | ZSWR3_RX_ENABLE, + ZSWR4_CLK_X16 | ZSWR4_ONESB, + ZSWR5_TX_8 | ZSWR5_TX_ENABLE, + 0, /* 6: TXSYNC/SYNCLO */ + 0, /* 7: RXSYNC/SYNCHI */ + 0, /* 8: alias for data port */ + ZSWR9_MASTER_IE, + 0, /*10: Misc. TX/RX control bits */ + ZSWR11_TXCLK_BAUD | ZSWR11_RXCLK_BAUD | ZSWR11_TRXC_OUT_ENA, + BPS_TO_TCONST(PCLK/16, ZS_DEFSPEED), /*12: BAUDLO (default=9600) */ + 0, /*13: BAUDHI (default=9600) */ + ZSWR14_BAUD_ENA, + ZSWR15_BREAK_IE, +}; + + +/**************************************************************** + * Autoconfig + ****************************************************************/ + +/* Definition of the driver for autoconfig. */ +int zs_hpc_match(struct device *, void *, void *); +void zs_hpc_attach(struct device *, struct device *, void *); +int zs_print(void *, const char *name); + +struct cfdriver zs_cd = { + NULL, "zs", DV_TTY +}; + +struct cfattach zs_hpc_ca = { + sizeof(struct zsc_softc), zs_hpc_match, zs_hpc_attach +}; + +int zshard(void *); +void zssoft(void *); +struct zschan *zs_get_chan_addr(int, int); +int zs_getc(void *); +void zs_putc(void *, int); + +/* + * Is the zs chip present? + */ +int +zs_hpc_match(struct device *parent, void *vcf, void *aux) +{ + struct cfdata *cf = vcf; + struct hpc_attach_args *ha = aux; + + if (strcmp(ha->ha_name, cf->cf_driver->cd_name) == 0) + return (1); + + return (0); +} + +/* + * Attach a found zs. + * + * Match slave number to zs unit number, so that misconfiguration will + * not set up the keyboard as ttya, etc. + */ +void +zs_hpc_attach(struct device *parent, struct device *self, void *aux) +{ + struct zsc_softc *zsc = (void *)self; + struct hpc_attach_args *haa = aux; + struct zsc_attach_args zsc_args; + struct zs_chanstate *cs; + struct zs_channel *ch; + int zs_unit, channel, err, s; + + zsc->zsc_bustag = haa->ha_st; + if ((err = bus_space_subregion(haa->ha_st, haa->ha_sh, + haa->ha_devoff, 0x10, + &zsc->zsc_base)) != 0) { + printf(": unable to map 85c30 registers, error = %d\n", + err); + return; + } + + zs_unit = zsc->zsc_dev.dv_unit; + printf("\n"); + + /* + * Initialize software state for each channel. + * + * Done in reverse order of channels since the first serial port + * is actually attached to the *second* channel, and vice versa. + * Doing it this way should force a 'zstty*' to attach zstty0 to + * channel 1 and zstty1 to channel 0. They couldn't have wired + * it up in a more sensible fashion, could they? + */ + for (channel = 1; channel >= 0; channel--) { + zsc_args.channel = channel; + ch = &zsc->zsc_cs_store[channel]; + cs = zsc->zsc_cs[channel] = (struct zs_chanstate *)ch; + + cs->cs_reg_csr = NULL; + cs->cs_reg_data = NULL; + cs->cs_channel = channel; + cs->cs_private = NULL; + cs->cs_ops = &zsops_null; + cs->cs_brg_clk = PCLK / 16; + + if (bus_space_subregion(zsc->zsc_bustag, zsc->zsc_base, + zs_chan_offset[channel], + sizeof(struct zschan), + &ch->cs_regs) != 0) { + printf("%s: cannot map regs\n", self->dv_xname); + return; + } + ch->cs_bustag = zsc->zsc_bustag; + + memcpy(cs->cs_creg, zs_init_reg, 16); + memcpy(cs->cs_preg, zs_init_reg, 16); + + /* If console, don't stomp speed, let zstty know */ + if (zs_unit == zs_consunit && channel == zs_conschan) { + zsc_args.consdev = &zs_cn; + zsc_args.hwflags = ZS_HWFLAG_CONSOLE; + cs->cs_defspeed = bios_consrate; + } else { + zsc_args.consdev = NULL; + zsc_args.hwflags = 0; + cs->cs_defspeed = zs_defspeed; + } + + cs->cs_defcflag = zs_def_cflag; + + /* Make these correspond to cs_defcflag (-crtscts) */ + cs->cs_rr0_dcd = ZSRR0_DCD; + cs->cs_rr0_cts = 0; + cs->cs_wr5_dtr = ZSWR5_DTR | ZSWR5_RTS; + cs->cs_wr5_rts = 0; + + /* + * Clear the master interrupt enable. + * The INTENA is common to both channels, + * so just do it on the A channel. + */ + if (channel == 0) { + zs_write_reg(cs, 9, 0); + } + /* + * Look for a child driver for this channel. + * The child attach will setup the hardware. + */ + if (!config_found(self, (void *)&zsc_args, zs_print)) { + /* No sub-driver. Just reset it. */ + uint8_t reset = (channel == 0) ? + ZSWR9_A_RESET : ZSWR9_B_RESET; + + s = splhigh(); + zs_write_reg(cs, 9, reset); + splx(s); + } + } + + + zsc->sc_si = softintr_establish(SI_SOFTTTY, zssoft, zsc); + int2_intr_establish(haa->ha_irq, IPL_TTY, zshard, zsc, self->dv_xname); + + /* + * Set the master interrupt enable and interrupt vector. + * (common to both channels, do it on A) + */ + cs = zsc->zsc_cs[0]; + s = splhigh(); + /* interrupt vector */ + zs_write_reg(cs, 2, zs_init_reg[2]); + /* master interrupt control (enable) */ + zs_write_reg(cs, 9, zs_init_reg[9]); + splx(s); +} + +int +zs_print(void *aux, const char *name) +{ + struct zsc_attach_args *args = aux; + + if (name != NULL) + printf("%s: ", name); + + if (args->channel != -1) + printf(" channel %d", args->channel); + + return UNCONF; +} + +/* + * Our ZS chips all share a common, autovectored interrupt, + * so we have to look at all of them on each interrupt. + */ +int +zshard(void *arg) +{ + struct zsc_softc *zsc = arg; + int rr3, rval; + + rval = 0; + while ((rr3 = zsc_intr_hard(zsc))) { + rval |= rr3; + } + + if (zsc->zsc_cs[0]->cs_softreq || + zsc->zsc_cs[1]->cs_softreq) + softintr_schedule(zsc->sc_si); + + return rval; +} + +/* + * Similar scheme as for zshard (look at all of them) + */ +void +zssoft(void *arg) +{ + struct zsc_softc *zsc = arg; + int s; + + /* Make sure we call the tty layer at spltty. */ + s = spltty(); + (void) zsc_intr_soft(zsc); + splx(s); +} + + +/* + * MD functions for setting the baud rate and control modes. + */ +int +zs_set_speed(struct zs_chanstate *cs, int bps) +{ + int tconst, real_bps; + + if (bps == 0) + return (0); + +#ifdef DIAGNOSTIC + if (cs->cs_brg_clk == 0) + panic("zs_set_speed"); +#endif + + tconst = BPS_TO_TCONST(cs->cs_brg_clk, bps); + if (tconst < 0) + return (EINVAL); + + /* Convert back to make sure we can do it. */ + real_bps = TCONST_TO_BPS(cs->cs_brg_clk, tconst); + +#if 0 /* PCLK is too small, 9600bps really yields 9562 */ + /* XXX - Allow some tolerance here? */ + if (real_bps != bps) + return (EINVAL); +#endif + + cs->cs_preg[12] = tconst; + cs->cs_preg[13] = tconst >> 8; + + /* Caller will stuff the pending registers. */ + return (0); +} + +int +zs_set_modes(struct zs_chanstate *cs, int cflag) +{ + int s; + + /* + * Output hardware flow control on the chip is horrendous: + * if carrier detect drops, the receiver is disabled, and if + * CTS drops, the transmitter is stoped IN MID CHARACTER! + * Therefore, NEVER set the HFC bit, and instead use the + * status interrupt to detect CTS changes. + */ + s = splzs(); + cs->cs_rr0_pps = 0; + if ((cflag & (CLOCAL | MDMBUF)) != 0) { + cs->cs_rr0_dcd = 0; + if ((cflag & MDMBUF) == 0) + cs->cs_rr0_pps = ZSRR0_DCD; + } else + cs->cs_rr0_dcd = ZSRR0_DCD; + if ((cflag & CRTSCTS) != 0) { + cs->cs_wr5_dtr = ZSWR5_DTR; + cs->cs_wr5_rts = ZSWR5_RTS; + cs->cs_rr0_cts = ZSRR0_CTS; + } else if ((cflag & MDMBUF) != 0) { + cs->cs_wr5_dtr = 0; + cs->cs_wr5_rts = ZSWR5_DTR; + cs->cs_rr0_cts = ZSRR0_DCD; + } else { + cs->cs_wr5_dtr = ZSWR5_DTR | ZSWR5_RTS; + cs->cs_wr5_rts = 0; + cs->cs_rr0_cts = 0; + } + splx(s); + + /* Caller will stuff the pending registers. */ + return (0); +} + + +/* + * Read or write the chip with suitable delays. + */ + +uint8_t +zs_read_reg(struct zs_chanstate *cs, uint8_t reg) +{ + uint8_t val; + struct zs_channel *zsc = (struct zs_channel *)cs; + + bus_space_write_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_CSR, reg); + ZS_DELAY(); + val = bus_space_read_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_CSR); + ZS_DELAY(); + return val; +} + +void +zs_write_reg(struct zs_chanstate *cs, uint8_t reg, uint8_t val) +{ + struct zs_channel *zsc = (struct zs_channel *)cs; + + bus_space_write_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_CSR, reg); + ZS_DELAY(); + bus_space_write_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_CSR, val); + ZS_DELAY(); +} + +uint8_t +zs_read_csr(struct zs_chanstate *cs) +{ + struct zs_channel *zsc = (struct zs_channel *)cs; + uint8_t val; + + val = bus_space_read_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_CSR); + ZS_DELAY(); + return val; +} + +void +zs_write_csr(struct zs_chanstate *cs, uint8_t val) +{ + struct zs_channel *zsc = (struct zs_channel *)cs; + + bus_space_write_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_CSR, val); + ZS_DELAY(); +} + +uint8_t +zs_read_data(struct zs_chanstate *cs) +{ + struct zs_channel *zsc = (struct zs_channel *)cs; + uint8_t val; + + val = bus_space_read_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_DATA); + ZS_DELAY(); + return val; +} + +void +zs_write_data(struct zs_chanstate *cs, uint8_t val) +{ + struct zs_channel *zsc = (struct zs_channel *)cs; + + bus_space_write_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_DATA, val); + ZS_DELAY(); +} + +void +zs_abort(struct zs_chanstate *cs) +{ +#if defined(KGDB) + zskgdb(cs); +#elif defined(DDB) + Debugger(); +#endif +} + + +/*********************************************************/ +/* Polled character I/O functions for console and KGDB */ +/*********************************************************/ + +struct zschan * +zs_get_chan_addr(int zs_unit, int channel) +{ +#if 0 + static int dumped_addr = 0; +#endif + struct zsdevice *addr = NULL; + struct zschan *zc; + + switch (sys_config.system_type) { + case SGI_IP20: + switch (zs_unit) { + case 0: + addr = (struct zsdevice *) + PHYS_TO_XKPHYS(0x1fb80d00, CCA_NC); + break; + case 1: + addr = (struct zsdevice *) + PHYS_TO_XKPHYS(0x1fb80d10, CCA_NC); + break; + } + break; + + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + if (zs_unit == 0) + addr = (struct zsdevice *) + PHYS_TO_XKPHYS(0x1fbd9830, CCA_NC); + break; + } + if (addr == NULL) + panic("zs_get_chan_addr: bad zs_unit %d\n", zs_unit); + + /* + * We need to swap serial ports to match reality on + * non-keyboard channels. + */ + if (sys_config.system_type != SGI_IP20) { + if (channel == 0) + zc = &addr->zs_chan_b; + else + zc = &addr->zs_chan_a; + } else { + if (zs_unit == 0) { + if (channel == 0) + zc = &addr->zs_chan_a; + else + zc = &addr->zs_chan_b; + } else { + if (channel == 0) + zc = &addr->zs_chan_b; + else + zc = &addr->zs_chan_a; + } + } + +#if 0 + if (dumped_addr == 0) { + dumped_addr++; + printf("zs unit %d, channel %d had address %p\n", + zs_unit, channel, zc); + } +#endif + + return (zc); +} + +int +zs_getc(void *arg) +{ + register volatile struct zschan *zc = arg; + register int s, c, rr0; + + s = splzs(); + /* Wait for a character to arrive. */ + do { + rr0 = zc->zc_csr; + ZS_DELAY(); + } while ((rr0 & ZSRR0_RX_READY) == 0); + + c = zc->zc_data; + ZS_DELAY(); + splx(s); + + return (c); +} + +/* + * Polled output char. + */ +void +zs_putc(void *arg, int c) +{ + register volatile struct zschan *zc = arg; + register int s, rr0; + + s = splzs(); + /* Wait for transmitter to become ready. */ + do { + rr0 = zc->zc_csr; + ZS_DELAY(); + } while ((rr0 & ZSRR0_TX_READY) == 0); + + zc->zc_data = c; + __asm__ __volatile__ ("sync" ::: "memory"); /* wbflush(); */ + ZS_DELAY(); + splx(s); +} + +/***************************************************************/ + +static int cons_port; + +void +zscnprobe(struct consdev *cp) +{ + cp->cn_dev = makedev(zs_major, 0); + cp->cn_pri = CN_DEAD; + + switch (sys_config.system_type) { + case SGI_IP20: + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + if (strlen(bios_console) == 9 && + strncmp(bios_console, "serial", 6) == 0) + cp->cn_pri = CN_FORCED; + else + cp->cn_pri = CN_MIDPRI; + break; + } +} + +void +zscninit(struct consdev *cn) +{ + if (strlen(bios_console) == 9 && + strncmp(bios_console, "serial", 6) != 0) + cons_port = bios_console[7] - '0'; + + /* Mark this unit as the console */ + zs_consunit = 0; + + /* SGI hardware wires serial port 1 to channel B, port 2 to A */ + if (cons_port == 0) + zs_conschan = 1; + else + zs_conschan = 0; +} + +int +zscngetc(dev_t dev) +{ + struct zschan *zs; + + switch (sys_config.system_type) { + case SGI_IP20: + zs = zs_get_chan_addr(1, cons_port); + break; + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + default: + zs = zs_get_chan_addr(0, cons_port); + break; + } + + return zs_getc(zs); +} + +void +zscnputc(dev_t dev, int c) +{ + struct zschan *zs; + + switch (sys_config.system_type) { + case SGI_IP20: + zs = zs_get_chan_addr(1, cons_port); + break; + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + default: + zs = zs_get_chan_addr(0, cons_port); + break; + } + + zs_putc(zs, c); +} + +void +zscnpollc(dev_t dev, int on) +{ +} diff --git a/sys/arch/sgi/include/autoconf.h b/sys/arch/sgi/include/autoconf.h index b71366edba6..d3db14d94e5 100644 --- a/sys/arch/sgi/include/autoconf.h +++ b/sys/arch/sgi/include/autoconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: autoconf.h,v 1.32 2012/03/25 13:52:52 miod Exp $ */ +/* $OpenBSD: autoconf.h,v 1.33 2012/03/28 20:44:23 miod Exp $ */ /* * Copyright (c) 2001-2003 Opsycon AB (www.opsycon.se / www.opsycon.com) @@ -93,6 +93,7 @@ void arcs_device_register(struct device *, void *); void dksc_device_register(struct device *, void *); extern void (*_device_register)(struct device *, void *); +void ip22_setup(void); void ip27_setup(void); void ip27_autoconf(struct device *); void ip30_setup(void); diff --git a/sys/arch/sgi/include/eisa_machdep.h b/sys/arch/sgi/include/eisa_machdep.h new file mode 100644 index 00000000000..93ba8c4c4e8 --- /dev/null +++ b/sys/arch/sgi/include/eisa_machdep.h @@ -0,0 +1,68 @@ +/* $OpenBSD: eisa_machdep.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: eisa_machdep.h,v 1.4 1997/06/06 23:12:52 thorpej Exp $ */ + +/* + * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou + * for the NetBSD Project. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Machine-specific definitions for EISA autoconfiguration. + */ + +/* + * EISA spaces memory layout + */ + +#define EISA_IO_BASE 0x00080000 +#define EISA_IO_END 0x00090000 + +#define EISA_MEM0_BASE 0x00100000 +#define EISA_MEM0_END 0x08000000 + +#define EISA_MEM1_BASE 0x80000000 +#define EISA_MEM1_END 0x100000000UL + +/* + * Types provided to machine-independent EISA code. + */ +typedef void *eisa_chipset_tag_t; +typedef int eisa_intr_handle_t; + +/* + * Functions provided to machine-independent EISA code. + */ +void eisa_attach_hook(struct device *, struct device *, + struct eisabus_attach_args *); +int eisa_maxslots(eisa_chipset_tag_t); +int eisa_intr_map(eisa_chipset_tag_t, u_int, + eisa_intr_handle_t *); +const char *eisa_intr_string(eisa_chipset_tag_t, eisa_intr_handle_t); +void *eisa_intr_establish(eisa_chipset_tag_t, eisa_intr_handle_t, + int, int, int (*)(void *), void *, char *); +void eisa_intr_disestablish(eisa_chipset_tag_t, void *); diff --git a/sys/arch/sgi/include/z8530var.h b/sys/arch/sgi/include/z8530var.h new file mode 100644 index 00000000000..5fab516b720 --- /dev/null +++ b/sys/arch/sgi/include/z8530var.h @@ -0,0 +1,121 @@ +/* $OpenBSD: z8530var.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: z8530var.h,v 1.10 2011/07/01 21:00:21 dyoung Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)zsvar.h 8.1 (Berkeley) 6/11/93 + */ + +/* + * Copyright (c) 1994 Gordon W. Ross + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)zsvar.h 8.1 (Berkeley) 6/11/93 + */ + +#include <machine/bus.h> +#include <sgi/hpc/z8530sc.h> + +struct zs_channel { + struct zs_chanstate cs_zscs; /* Required: soft state */ + bus_space_tag_t cs_bustag; /* Machine-dependent */ + bus_space_handle_t cs_regs; +}; + +struct zsc_softc { + struct device zsc_dev; /* required: base device */ + struct zs_chanstate *zsc_cs[2]; /* channel soft state */ + struct zs_channel zsc_cs_store[2]; + /* Machine-dependent part follows... */ + bus_space_tag_t zsc_bustag; /* Bus type */ + bus_space_handle_t zsc_base; /* Device base address */ + void *sc_si; /* Softinterrupt handle */ +}; + +/* + * Functions to read and write individual registers in a channel. + * The SCC chip requires 3-4 PCLK cycles recovery time between accesses + */ + +uint8_t zs_read_reg(struct zs_chanstate *, uint8_t); +uint8_t zs_read_csr(struct zs_chanstate *); +uint8_t zs_read_data(struct zs_chanstate *); + +void zs_write_reg(struct zs_chanstate *, uint8_t, uint8_t); +void zs_write_csr(struct zs_chanstate *, uint8_t); +void zs_write_data(struct zs_chanstate *, uint8_t); + +/* Zilog Serial hardware interrupts (level 0) */ +#define splzs() spltty() +#define IPL_ZS IPL_TTY diff --git a/sys/arch/sgi/localbus/imc.c b/sys/arch/sgi/localbus/imc.c new file mode 100644 index 00000000000..c8544aa8788 --- /dev/null +++ b/sys/arch/sgi/localbus/imc.c @@ -0,0 +1,826 @@ +/* $OpenBSD: imc.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: imc.c,v 1.32 2011/07/01 18:53:46 dyoung Exp $ */ + +/* + * Copyright (c) 2012 Miodrag Vallat. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * Copyright (c) 2001 Rafal K. Boni + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/systm.h> + +#include <mips64/archtype.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> +#include <machine/cpu.h> + +#include <sgi/sgi/ip22.h> +#include <sgi/localbus/imcreg.h> +#include <sgi/localbus/imcvar.h> + +#include <sgi/hpc/hpcreg.h> +#include <sgi/gio/giovar.h> + +#include "eisa.h" + +#if NEISA > 0 +#include <dev/eisa/eisavar.h> +#endif + +int imc_match(struct device *, void *, void *); +void imc_attach(struct device *, struct device *, void *); +int imc_print(void *, const char *); + +const struct cfattach imc_ca = { + sizeof(struct device), imc_match, imc_attach +}; + +struct cfdriver imc_cd = { + NULL, "imc", DV_DULL +}; + +uint32_t imc_bus_error(uint32_t, struct trap_frame *); +void imc_bus_reset(void); +int imc_watchdog_cb(void *, int); + +uint8_t imc_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t); +uint16_t imc_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t); +void imc_read_raw_2(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + uint8_t *, bus_size_t); +void imc_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint8_t); +void imc_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint16_t); +void imc_write_raw_2(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + const uint8_t *, bus_size_t); +uint32_t imc_read_4(bus_space_tag_t, bus_space_handle_t, bus_size_t); +uint64_t imc_read_8(bus_space_tag_t, bus_space_handle_t, bus_size_t); +void imc_write_4(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint32_t); +void imc_write_8(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint64_t); +void imc_read_raw_4(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + uint8_t *, bus_size_t); +void imc_write_raw_4(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + const uint8_t *, bus_size_t); +void imc_read_raw_8(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + uint8_t *, bus_size_t); +void imc_write_raw_8(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + const uint8_t *, bus_size_t); +int imc_space_map(bus_space_tag_t, bus_addr_t, bus_size_t, int, + bus_space_handle_t *); +void imc_space_unmap(bus_space_tag_t, bus_space_handle_t, bus_size_t); +int imc_space_region(bus_space_tag_t, bus_space_handle_t, bus_size_t, + bus_size_t, bus_space_handle_t *); +void *imc_space_vaddr(bus_space_tag_t, bus_space_handle_t); +void imc_space_barrier(bus_space_tag_t, bus_space_handle_t, bus_size_t, + bus_size_t, int); + +bus_space_t imcbus_tag = {/* not static for gio_cnattch() */ + PHYS_TO_XKPHYS(0, CCA_NC), + NULL, + imc_read_1, imc_write_1, + imc_read_2, imc_write_2, + imc_read_4, imc_write_4, + imc_read_8, imc_write_8, + imc_read_raw_2, imc_write_raw_2, + imc_read_raw_4, imc_write_raw_4, + imc_read_raw_8, imc_write_raw_8, + imc_space_map, imc_space_unmap, imc_space_region, + imc_space_vaddr, imc_space_barrier +}; + +#if NEISA > 0 +int imc_eisa_io_map(bus_space_tag_t, bus_addr_t, bus_size_t, int, + bus_space_handle_t *); +int imc_eisa_io_region(bus_space_tag_t, bus_space_handle_t, bus_size_t, + bus_size_t, bus_space_handle_t *); +int imc_eisa_mem_map(bus_space_tag_t, bus_addr_t, bus_size_t, int, + bus_space_handle_t *); +int imc_eisa_mem_region(bus_space_tag_t, bus_space_handle_t, bus_size_t, + bus_size_t, bus_space_handle_t *); + +static bus_space_t imcbus_eisa_io_tag = { + PHYS_TO_XKPHYS(EISA_IO_BASE, CCA_NC), + NULL, + imc_read_1, imc_write_1, + imc_read_2, imc_write_2, + imc_read_4, imc_write_4, + imc_read_8, imc_write_8, + imc_read_raw_2, imc_write_raw_2, + imc_read_raw_4, imc_write_raw_4, + imc_read_raw_8, imc_write_raw_8, + imc_eisa_io_map, imc_space_unmap, imc_eisa_io_region, + imc_space_vaddr, imc_space_barrier +}; +static bus_space_t imcbus_eisa_mem_tag = { + PHYS_TO_XKPHYS(0, CCA_NC), + NULL, + imc_read_1, imc_write_1, + imc_read_2, imc_write_2, + imc_read_4, imc_write_4, + imc_read_8, imc_write_8, + imc_read_raw_2, imc_write_raw_2, + imc_read_raw_4, imc_write_raw_4, + imc_read_raw_8, imc_write_raw_8, + imc_eisa_mem_map, imc_space_unmap, imc_eisa_mem_region, + imc_space_vaddr, imc_space_barrier +}; +#endif + +bus_addr_t imc_pa_to_device(paddr_t); +paddr_t imc_device_to_pa(bus_addr_t); + +struct machine_bus_dma_tag imc_bus_dma_tag = {/* not static for gio_cnattch() */ + NULL, /* _cookie */ + _dmamap_create, + _dmamap_destroy, + _dmamap_load, + _dmamap_load_mbuf, + _dmamap_load_uio, + _dmamap_load_raw, + _dmamap_load_buffer, + _dmamap_unload, + _dmamap_sync, + _dmamem_alloc, + _dmamem_free, + _dmamem_map, + _dmamem_unmap, + _dmamem_mmap, + imc_pa_to_device, + imc_device_to_pa, + 0 +}; + +/* + * Bus access primitives. + */ + +uint8_t +imc_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) +{ + return *(volatile uint8_t *)(h + o); +} + +uint16_t +imc_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) +{ + return *(volatile uint16_t *)(h + o); +} + +uint32_t +imc_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) +{ + return *(volatile uint32_t *)(h + o); +} + +uint64_t +imc_read_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) +{ + return *(volatile uint64_t *)(h + o); +} + +void +imc_write_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint8_t v) +{ + *(volatile uint8_t *)(h + o) = v; +} + +void +imc_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint16_t v) +{ + *(volatile uint16_t *)(h + o) = v; +} + +void +imc_write_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint32_t v) +{ + *(volatile uint32_t *)(h + o) = v; +} + +void +imc_write_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint64_t v) +{ + *(volatile uint64_t *)(h + o) = v; +} + +void +imc_read_raw_2(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o, + uint8_t *buf, bus_size_t len) +{ + volatile uint16_t *addr = (volatile uint16_t *)(h + o); + len >>= 1; + while (len-- != 0) { + *(uint16_t *)buf = *addr; + buf += 2; + } +} + +void +imc_write_raw_2(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o, + const uint8_t *buf, bus_size_t len) +{ + volatile uint16_t *addr = (volatile uint16_t *)(h + o); + len >>= 1; + while (len-- != 0) { + *addr = *(uint16_t *)buf; + buf += 2; + } +} + +void +imc_read_raw_4(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o, + uint8_t *buf, bus_size_t len) +{ + volatile uint32_t *addr = (volatile uint32_t *)(h + o); + len >>= 2; + while (len-- != 0) { + *(uint32_t *)buf = *addr; + buf += 4; + } +} + +void +imc_write_raw_4(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o, + const uint8_t *buf, bus_size_t len) +{ + volatile uint32_t *addr = (volatile uint32_t *)(h + o); + len >>= 2; + while (len-- != 0) { + *addr = *(uint32_t *)buf; + buf += 4; + } +} + +void +imc_read_raw_8(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o, + uint8_t *buf, bus_size_t len) +{ + volatile uint64_t *addr = (volatile uint64_t *)(h + o); + len >>= 3; + while (len-- != 0) { + *(uint64_t *)buf = *addr; + buf += 8; + } +} + +void +imc_write_raw_8(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o, + const uint8_t *buf, bus_size_t len) +{ + volatile uint64_t *addr = (volatile uint64_t *)(h + o); + len >>= 3; + while (len-- != 0) { + *addr = *(uint64_t *)buf; + buf += 8; + } +} + +int +imc_space_map(bus_space_tag_t t, bus_addr_t offs, bus_size_t size, + int flags, bus_space_handle_t *bshp) +{ + *bshp = t->bus_base + offs; + return 0; +} + +void +imc_space_unmap(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size) +{ +} + +int +imc_space_region(bus_space_tag_t t, bus_space_handle_t bsh, + bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp) +{ + *nbshp = bsh + offset; + return 0; +} + +void * +imc_space_vaddr(bus_space_tag_t t, bus_space_handle_t h) +{ + return (void *)h; +} + +void +imc_space_barrier(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offs, + bus_size_t len, int flags) +{ + __asm__ __volatile__ ("sync" ::: "memory"); +} + +#if NEISA > 0 +int +imc_eisa_io_map(bus_space_tag_t t, bus_addr_t offs, bus_size_t size, int flags, + bus_space_handle_t *bshp) +{ + if (offs + size > EISA_IO_END - EISA_IO_BASE) + return EINVAL; + + *bshp = t->bus_base + offs; + return 0; +} + +int +imc_eisa_io_region(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset, + bus_size_t size, bus_space_handle_t *nbshp) +{ + if ((bsh - t->bus_base) + offset + size > EISA_IO_END - EISA_IO_BASE) + return EINVAL; + + *nbshp = bsh + offset; + return 0; +} + +int +imc_eisa_mem_map(bus_space_tag_t t, bus_addr_t offs, bus_size_t size, int flags, + bus_space_handle_t *bshp) +{ + if ((offs >= EISA_MEM0_BASE && offs + size <= EISA_MEM0_END) || + (offs >= EISA_MEM1_BASE && offs + size <= EISA_MEM1_END)) { + *bshp = t->bus_base + offs; + return 0; + } + + return EINVAL; +} + +int +imc_eisa_mem_region(bus_space_tag_t t, bus_space_handle_t bsh, + bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp) +{ + bus_addr_t orig = bsh - t->bus_base; + + if ((orig >= EISA_MEM0_BASE && orig + offset + size <= EISA_MEM0_END) || + (orig >= EISA_MEM1_BASE && orig + offset + size <= EISA_MEM1_END)) { + *nbshp = t->bus_base + offset; + return 0; + } + + return EINVAL; +} +#endif + +bus_addr_t +imc_pa_to_device(paddr_t pa) +{ + return (bus_addr_t)pa; +} + +paddr_t +imc_device_to_pa(bus_addr_t addr) +{ + return (paddr_t)addr; +} + +/* + * Autoconf glue. + */ + +int +imc_match(struct device *parent, void *match, void *aux) +{ + struct mainbus_attach_args *maa = (void *)aux; + + switch (sys_config.system_type) { + case SGI_IP20: + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + break; + default: + return 0; + } + + return strcmp(maa->maa_name, imc_cd.cd_name) == 0; +} + +void +imc_attach(struct device *parent, struct device *self, void *aux) +{ + struct imc_attach_args iaa; +#if NEISA > 0 + struct eisabus_attach_args eba; +#endif + uint32_t reg; + uint32_t id, rev; + int have_eisa; + + id = imc_read(IMC_SYSID); + rev = id & IMC_SYSID_REVMASK; + + /* EISA exists on Indigo2 only */ + if (sys_config.system_type != SGI_IP20 && + sys_config.system_subtype == IP22_INDIGO2) + have_eisa = (id & IMC_SYSID_HAVEISA) != 0; + else + have_eisa = 0; + + printf(": revision %d\n", rev); + + /* Clear CPU/GIO error status registers to clear any leftover bits. */ + imc_bus_reset(); + + /* Disable watchdog if leftover from previous reboot */ + imc_watchdog_cb(self, 0); + + /* Hook the bus error handler into the ISR */ + set_intr(INTPRI_BUSERR, CR_INT_4, imc_bus_error); + + /* + * Enable parity reporting on GIO/main memory transactions. + * Disable parity checking on CPU bus transactions (as turning + * it on seems to cause spurious bus errors), but enable parity + * checking on CPU reads from main memory (note that this bit + * has the opposite sense... Turning it on turns the checks off!). + * Finally, turn on interrupt writes to the CPU from the MC. + */ + reg = imc_read(IMC_CPUCTRL0); + reg &= ~IMC_CPUCTRL0_NCHKMEMPAR; + reg |= (IMC_CPUCTRL0_GPR | IMC_CPUCTRL0_MPR | IMC_CPUCTRL0_INTENA); + imc_write(IMC_CPUCTRL0, reg); + + /* Setup the MC write buffer depth */ + reg = imc_read(IMC_CPUCTRL1); + reg = (reg & ~IMC_CPUCTRL1_MCHWMSK) | 13; + + /* + * Force endianness on the onboard HPC and both slots. + * This should be safe for Fullhouse, but leave it conditional + * for now. + */ + switch (sys_config.system_type) { + case SGI_IP22: + if (sys_config.system_subtype != IP22_INDY) + break; + /* FALLTHROUGH */ + case SGI_IP20: + reg |= IMC_CPUCTRL1_HPCFX; + reg |= IMC_CPUCTRL1_EXP0FX; + reg |= IMC_CPUCTRL1_EXP1FX; + reg &= ~IMC_CPUCTRL1_HPCLITTLE; + reg &= ~IMC_CPUCTRL1_EXP0LITTLE; + reg &= ~IMC_CPUCTRL1_EXP1LITTLE; + break; + } + imc_write(IMC_CPUCTRL1, reg); + + /* + * Set GIO64 arbitrator configuration register: + * + * Preserve PROM-set graphics-related bits, as they seem to depend + * on the graphics variant present and I'm not sure how to figure + * that out or 100% sure what the correct settings are for each. + */ + reg = imc_read(IMC_GIO64ARB); + reg &= (IMC_GIO64ARB_GRX64 | IMC_GIO64ARB_GRXRT | IMC_GIO64ARB_GRXMST); + + /* + * Rest of settings are machine/board dependent + * XXX I wonder if this even works as advertized. The logic apparently + * XXX comes from Linux, but the EISA settings look horribly broken to + * XXX me -- miod + */ + switch (sys_config.system_type) { + case SGI_IP20: + reg |= IMC_GIO64ARB_ONEGIO; + reg |= IMC_GIO64ARB_EXP0RT | IMC_GIO64ARB_EXP1RT; + reg |= IMC_GIO64ARB_EXP0MST | IMC_GIO64ARB_EXP1MST; + reg &= ~(IMC_GIO64ARB_HPC64 | + IMC_GIO64ARB_HPCEXP64 | IMC_GIO64ARB_EISA64 | + IMC_GIO64ARB_EXP064 | IMC_GIO64ARB_EXP164 | + IMC_GIO64ARB_EXP0PIPE | IMC_GIO64ARB_EXP1PIPE); + break; + default: + /* + * GIO64 invariant for all IP22 platforms: one GIO bus, + * HPC1 @ 64 + */ + reg |= IMC_GIO64ARB_ONEGIO | IMC_GIO64ARB_HPC64; + + switch (sys_config.system_subtype) { + default: + case IP22_INDY: + /* XXX is MST mutually exclusive? */ + reg |= IMC_GIO64ARB_EXP0RT | IMC_GIO64ARB_EXP1RT; + reg |= IMC_GIO64ARB_EXP0MST | IMC_GIO64ARB_EXP1MST; + + /* EISA can bus-master, is 64-bit */ + reg |= IMC_GIO64ARB_EISAMST | IMC_GIO64ARB_EISA64; + break; + + case IP22_INDIGO2: + /* + * All Fullhouse boards have a 64-bit HPC2 and pipelined + * EXP0 slot. + */ + reg |= IMC_GIO64ARB_HPCEXP64 | IMC_GIO64ARB_EXP0PIPE; + + if (rev < 2) { + /* EXP0 realtime, EXP1 can master */ + reg |= IMC_GIO64ARB_EXP0RT | + IMC_GIO64ARB_EXP1MST; + } else { + /* EXP1 pipelined as well, EISA masters */ + reg |= IMC_GIO64ARB_EXP1PIPE | + IMC_GIO64ARB_EISAMST; + } + break; + } + } + + imc_write(IMC_GIO64ARB, reg); + +#if NEISA > 0 + if (have_eisa) { + memset(&eba, 0, sizeof(eba)); + eba.eba_busname = "eisa"; + eba.eba_iot = &imcbus_eisa_io_tag; + eba.eba_memt = &imcbus_eisa_mem_tag; + eba.eba_dmat = &imc_bus_dma_tag; + eba.eba_ec = NULL; + config_found(self, &eba, imc_print); + } +#endif + + memset(&iaa, 0, sizeof(iaa)); + iaa.iaa_name = "gio"; + iaa.iaa_st = &imcbus_tag; + iaa.iaa_dmat = &imc_bus_dma_tag; + config_found(self, &iaa, imc_print); + + /* Clear CPU/GIO error status registers to clear any leftover bits. */ + imc_bus_reset(); + + /* Register watchdog */ + wdog_register(self, imc_watchdog_cb); +} + +int +imc_print(void *aux, const char *name) +{ + struct imc_attach_args *iaa = aux; + + if (name != NULL) + printf("%s at %s", iaa->iaa_name, name); + + return UNCONF; +} + +void +imc_bus_reset() +{ + imc_write(IMC_CPU_ERRSTAT, 0); + imc_write(IMC_GIO_ERRSTAT, 0); +} + +uint32_t +imc_bus_error(uint32_t hwpend, struct trap_frame *tf) +{ + printf("bus error: cpu_stat %08x addr %08x, gio_stat %08x addr %08x\n", + imc_read(IMC_CPU_ERRSTAT), + imc_read(IMC_CPU_ERRADDR), + imc_read(IMC_GIO_ERRSTAT), + imc_read(IMC_GIO_ERRADDR)); + imc_bus_reset(); + + return hwpend; +} + +int +imc_watchdog_cb(void *v, int period) +{ + uint32_t reg; + + if (period == 0) { + /* reset... */ + imc_write(IMC_WDOG, 0); + /* ...and disable */ + reg = imc_read(IMC_CPUCTRL0); + reg &= ~(IMC_CPUCTRL0_WDOG); + imc_write(IMC_CPUCTRL0, reg); + + return 0; + } else { + /* enable... */ + reg = imc_read(IMC_CPUCTRL0); + reg |= IMC_CPUCTRL0_WDOG; + imc_write(IMC_CPUCTRL0, reg); + /* ...and reset */ + imc_write(IMC_WDOG, 0); + + /* + * The watchdog period is not controllable; it will fire + * when the 20 bit counter, running on a 64 usec clock, + * overflows. + */ + return (64 << 20) / 1000000; + } +} + +/* intended to be called from gio/gio.c only */ +int +imc_gio64_arb_config(int slot, uint32_t flags) +{ + uint32_t reg; + + if (sys_config.system_type == SGI_IP20 || + sys_config.system_subtype != IP22_INDIGO2) { + /* GIO_SLOT_GFX is only usable on Fullhouse */ + if (slot == GIO_SLOT_GFX) + return EINVAL; + } else { + /* GIO_SLOT_EXP1 is unusable on Fullhouse */ + if (slot == GIO_SLOT_EXP1) + return EINVAL; + } + + /* GIO_SLOT_GFX is always pipelined */ + if (slot == GIO_SLOT_GFX && (flags & GIO_ARB_NOPIPE)) + return EINVAL; + + /* IP20 does not support pipelining (XXX what about Indy?) */ + if (((flags & GIO_ARB_PIPE) || (flags & GIO_ARB_NOPIPE)) && + sys_config.system_type == SGI_IP20) + return EINVAL; + + reg = imc_read(IMC_GIO64ARB); + + if (flags & GIO_ARB_RT) { + if (slot == GIO_SLOT_EXP0) + reg |= IMC_GIO64ARB_EXP0RT; + else if (slot == GIO_SLOT_EXP1) + reg |= IMC_GIO64ARB_EXP1RT; + else if (slot == GIO_SLOT_GFX) + reg |= IMC_GIO64ARB_GRXRT; + } + + if (flags & GIO_ARB_MST) { + if (slot == GIO_SLOT_EXP0) + reg |= IMC_GIO64ARB_EXP0MST; + else if (slot == GIO_SLOT_EXP1) + reg |= IMC_GIO64ARB_EXP1MST; + else if (slot == GIO_SLOT_GFX) + reg |= IMC_GIO64ARB_GRXMST; + } + + if (flags & GIO_ARB_PIPE) { + if (slot == GIO_SLOT_EXP0) + reg |= IMC_GIO64ARB_EXP0PIPE; + else if (slot == GIO_SLOT_EXP1) + reg |= IMC_GIO64ARB_EXP1PIPE; + } + + if (flags & GIO_ARB_LB) { + if (slot == GIO_SLOT_EXP0) + reg &= ~IMC_GIO64ARB_EXP0RT; + else if (slot == GIO_SLOT_EXP1) + reg &= ~IMC_GIO64ARB_EXP1RT; + else if (slot == GIO_SLOT_GFX) + reg &= ~IMC_GIO64ARB_GRXRT; + } + + if (flags & GIO_ARB_SLV) { + if (slot == GIO_SLOT_EXP0) + reg &= ~IMC_GIO64ARB_EXP0MST; + else if (slot == GIO_SLOT_EXP1) + reg &= ~IMC_GIO64ARB_EXP1MST; + else if (slot == GIO_SLOT_GFX) + reg &= ~IMC_GIO64ARB_GRXMST; + } + + if (flags & GIO_ARB_NOPIPE) { + if (slot == GIO_SLOT_EXP0) + reg &= ~IMC_GIO64ARB_EXP0PIPE; + else if (slot == GIO_SLOT_EXP1) + reg &= ~IMC_GIO64ARB_EXP1PIPE; + } + + if (flags & GIO_ARB_32BIT) { + if (slot == GIO_SLOT_EXP0) + reg &= ~IMC_GIO64ARB_EXP064; + else if (slot == GIO_SLOT_EXP1) + reg &= ~IMC_GIO64ARB_EXP164; + } + + if (flags & GIO_ARB_64BIT) { + if (slot == GIO_SLOT_EXP0) + reg |= IMC_GIO64ARB_EXP064; + else if (slot == GIO_SLOT_EXP1) + reg |= IMC_GIO64ARB_EXP164; + } + + if (flags & GIO_ARB_HPC2_32BIT) + reg &= ~IMC_GIO64ARB_HPCEXP64; + + if (flags & GIO_ARB_HPC2_64BIT) + reg |= IMC_GIO64ARB_HPCEXP64; + + imc_write(IMC_GIO64ARB, reg); + + return 0; +} + +/* + * According to chapter 19 of the "IRIX Device Driver Programmer's Guide", + * some GIO devices, which do not drive all data lines, may cause false + * memory read parity errors on the SysAD bus. The workaround is to disable + * parity checking. + */ +void +imc_disable_sysad_parity(void) +{ + uint32_t reg; + + switch (sys_config.system_type) { + case SGI_IP20: + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + break; + default: + return; + } + + reg = imc_read(IMC_CPUCTRL0); + reg |= IMC_CPUCTRL0_NCHKMEMPAR; + imc_write(IMC_CPUCTRL0, reg); +} + +void +imc_enable_sysad_parity(void) +{ + uint32_t reg; + + switch (sys_config.system_type) { + case SGI_IP20: + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + break; + default: + return; + } + + reg = imc_read(IMC_CPUCTRL0); + reg &= ~IMC_CPUCTRL0_NCHKMEMPAR; + imc_write(IMC_CPUCTRL0, reg); +} + +#if 0 +int +imc_is_sysad_parity_enabled(void) +{ + uint32_t reg; + + switch (sys_config.system_type) { + case SGI_IP20: + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + break; + default: + return 0; + } + + reg = imc_read(IMC_CPUCTRL0); + + return reg & IMC_CPUCTRL0_NCHKMEMPAR; +} +#endif diff --git a/sys/arch/sgi/localbus/imcreg.h b/sys/arch/sgi/localbus/imcreg.h new file mode 100644 index 00000000000..fd27ff4c9be --- /dev/null +++ b/sys/arch/sgi/localbus/imcreg.h @@ -0,0 +1,143 @@ +/* $OpenBSD: imcreg.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: imcreg.h,v 1.4 2005/12/11 12:18:52 christos Exp $ */ + +/* + * Copyright (c) 2001 Rafal K. Boni + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define IMC_BASE 0x1fa00000 + +#define IMC_CPUCTRL0 0x04 /* CPU control, register 0 */ + +#define IMC_CPUCTRL0_REFMASK 0x000f /* # lines to refresh */ +#define IMC_CPUCTRL0_RFE 0x0010 /* refresh enable */ +#define IMC_CPUCTRL0_GPR 0x0020 /* GIO parity enable */ +#define IMC_CPUCTRL0_MPR 0x0040 /* memory parity enable */ +#define IMC_CPUCTRL0_CPR 0x0080 /* cpu bus parity enable */ +#define IMC_CPUCTRL0_WDOG 0x0100 /* watchdog enable */ +#define IMC_CPUCTRL0_SIN 0x0200 /* reset system */ +#define IMC_CPUCTRL0_GRR 0x0400 /* graphics reset */ +#define IMC_CPUCTRL0_ENLOCK 0x0800 /* enable EISA memory lock */ +#define IMC_CPUCTRL0_CMDPAR 0x1000 /* SysCmd parity enable */ +#define IMC_CPUCTRL0_INTENA 0x2000 /* enable CPU interrupts */ +#define IMC_CPUCTRL0_SNOOPENA 0x4000 /* enable gfx DMA snoop */ +#define IMC_CPUCTRL0_PROM_WRENA 0x8000 /* disable buserr on PROM + * writes */ +#define IMC_CPUCTRL0_WRST 0x00010000 /* warm restart (reset cpu) */ +/* Bit 17 reserved 0x00020000 */ +#define IMC_CPUCTRL0_LITTLE 0x00040000 /* MC little-endian toggle */ +#define IMC_CPUCTRL0_WRRST 0x00080000 /* cpu warm reset */ +#define IMC_CPUCTRL0_MUXHWMSK 0x01f00000 /* MUX fifo high-water mask */ +#define IMC_CPUCTRL0_BADPAR 0x02000000 /* generate bad parity on + * CPU->memory writes */ +#define IMC_CPUCTRL0_NCHKMEMPAR 0x04000000 /* disable CPU parity check + * on memory reads. */ +#define IMC_CPUCTRL0_BACK2 0x08000000 /* enable back2back GIO wrt */ +#define IMC_CPUCTRL0_BUSRTMSK 0xf0000000 /* stall cycle for berr data */ + +#define IMC_CPUCTRL1 0x0c /* CPU control, register 1 */ +#define IMC_CPUCTRL1_MCHWMSK 0x0000000f /* MC FIFO high water mask */ +#define IMC_CPUCTRL1_ABORTEN 0x00000010 /* Enable GIO bus timeouts */ +/* Bits 5 - 11 reserved 0x00000fe0 */ +#define IMC_CPUCTRL1_HPCFX 0x00001000 /* HPC endian fix */ +#define IMC_CPUCTRL1_HPCLITTLE 0x00002000 /* HPC DMA is little-endian */ +#define IMC_CPUCTRL1_EXP0FX 0x00004000 /* EXP0 endian fix */ +#define IMC_CPUCTRL1_EXP0LITTLE 0x00008000 /* EXP0 DMA is little-endian */ +#define IMC_CPUCTRL1_EXP1FX 0x00010000 /* EXP1 endian fix */ +#define IMC_CPUCTRL1_EXP1LITTLE 0x00020000 /* EXP1 DMA is little-endian */ + +#define IMC_WDOG 0x14 /* Watchdog counter */ +#define IMC_WDOG_MASK 0x001fffff /* counter mask */ + +#define IMC_SYSID 0x1c /* MC revision register */ +#define IMC_SYSID_REVMASK 0x0000000f /* MC revision mask */ +#define IMC_SYSID_HAVEISA 0x00000010 /* EISA present */ + +#define IMC_RPSSDIV 0x2c /* RPSS divider */ +#define IMC_RPSSDIV_DIVMSK 0x000000ff /* RPC divider mask */ +#define IMC_RPSSDIV_INCMSK 0x0000ff00 /* RPC increment mask */ + +#define IMC_EEPROM 0x34 /* EEPROM serial interface */ +/* Bit 1 is reserved 0x00000001 */ +#define IMC_EEPROM_CS 0x00000002 /* EEPROM chip select */ +#define IMC_EEPROM_SCK 0x00000004 /* EEPROM serial clock */ +#define IMC_EEPROM_SO 0x00000008 /* Serial data to EEPROM */ +#define IMC_EEPROM_SI 0x00000010 /* Serial data from EEPROM */ + +#define IMC_CTRLD 0x44 /* Refresh counter preload */ +#define IMC_CTRLD_MSK 0x000000ff /* Counter preload mask */ + +#define IMC_REFCTR 0x4c /* Refresh counter */ +#define IMC_REFCTR_MSK 0x000000ff /* Refresh counter mask */ + +#define IMC_GIO64ARB 0x84 /* GIO64 arbitration params */ +#define IMC_GIO64ARB_HPC64 0x00000001 /* HPC addr size (32/64bit) */ +#define IMC_GIO64ARB_GRX64 0x00000002 /* Gfx addr size (32/64bit) */ +#define IMC_GIO64ARB_EXP064 0x00000004 /* EXP0 addr size (32/64bit) */ +#define IMC_GIO64ARB_EXP164 0x00000008 /* EXP0 addr size (32/64bit) */ +#define IMC_GIO64ARB_EISA64 0x00000010 /* EISA addr size (32/64bit) */ +#define IMC_GIO64ARB_HPCEXP64 0x00000020 /* HPC2 addr size (32/64bit) */ +#define IMC_GIO64ARB_GRXRT 0x00000040 /* Gfx is realtime device */ +#define IMC_GIO64ARB_EXP0RT 0x00000080 /* EXP0 is realtime device */ +#define IMC_GIO64ARB_EXP1RT 0x00000100 /* EXP1 is realtime device */ +#define IMC_GIO64ARB_EISAMST 0x00000200 /* EISA can be busmaster */ +#define IMC_GIO64ARB_ONEGIO 0x00000400 /* Only one GIO64 bus */ +#define IMC_GIO64ARB_GRXMST 0x00000800 /* Gfx can be busmaster */ +#define IMC_GIO64ARB_EXP0MST 0x00001000 /* EXP0 can be busmaster */ +#define IMC_GIO64ARB_EXP1MST 0x00002000 /* EXP1 can be busmaster */ +#define IMC_GIO64ARB_EXP0PIPE 0x00004000 /* EXP0 is pipelined */ +#define IMC_GIO64ARB_EXP1PIPE 0x00008000 /* EXP1 is pipelined */ + +#define IMC_CPUTIME 0x8c /* Arbiter CPU time period */ + +#define IMC_LBTIME 0x9c /* Arbiter long-burst time */ + +#define IMC_MEMCFG0 0xc4 /* Mem config, register 0 */ +#define IMC_MEMCFG1 0xcc /* Mem config, register 1 */ +#define IMC_MEMC_BANK_MASK 0x0000ffff +#define IMC_MEMC_BANK_SHIFT 16 +#define IMC_MEMC_ADDR_MASK 0x00ff +#define IMC_MEMC_ADDR_SHIFT 0 +#define IMC_MEMC_SIZE_MASK 0x1f00 +#define IMC_MEMC_SIZE_SHIFT 8 +#define IMC_MEMC_LSHIFT 22 /* 4MB units */ +#define IMC_MEMC_LSHIFT_HUGE 24 /* 16MB units */ +#define IMC_MEMC_VALID 0x2000 +#define IMC_MEMC_SUBBANKS 0x4000 + +#define IMC_CPU_MEMACC 0xd4 /* CPU mem access config */ + +#define IMC_GIO_MEMACC 0xdc /* GIO mem access config */ + +#define IMC_CPU_ERRADDR 0xe4 /* CPU error address */ + +#define IMC_CPU_ERRSTAT 0xec /* CPU error status */ + +#define IMC_GIO_ERRADDR 0xf4 /* GIO error address */ + +#define IMC_GIO_ERRSTAT 0xfc /* GIO error status */ + +#define IMC_RPSS 0x1004 /* RPSS counter */ diff --git a/sys/arch/sgi/localbus/imcvar.h b/sys/arch/sgi/localbus/imcvar.h new file mode 100644 index 00000000000..651d23f9d3e --- /dev/null +++ b/sys/arch/sgi/localbus/imcvar.h @@ -0,0 +1,42 @@ +/* $OpenBSD: imcvar.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: imcvar.h,v 1.1 2006/08/30 23:44:52 rumble Exp $ */ + +/* + * Copyright (c) 2006 Stephen M. Rumble + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +struct imc_attach_args { + const char *iaa_name; + bus_space_tag_t iaa_st; + bus_dma_tag_t iaa_dmat; +}; + +int imc_gio64_arb_config(int, uint32_t); +void imc_disable_sysad_parity(void); +void imc_enable_sysad_parity(void); +int imc_is_sysad_parity_enabled(void); + +#define imc_read(o) \ + *(volatile uint32_t *)PHYS_TO_XKPHYS(IMC_BASE + (o), CCA_NC) +#define imc_write(o,v) \ + *(volatile uint32_t *)PHYS_TO_XKPHYS(IMC_BASE + (o), CCA_NC) = (v) diff --git a/sys/arch/sgi/localbus/int.c b/sys/arch/sgi/localbus/int.c new file mode 100644 index 00000000000..01e8b2a5dde --- /dev/null +++ b/sys/arch/sgi/localbus/int.c @@ -0,0 +1,369 @@ +/* $OpenBSD: int.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: int.c,v 1.24 2011/07/01 18:53:46 dyoung Exp $ */ + +/* + * Copyright (c) 2009 Stephen M. Rumble + * Copyright (c) 2004 Christopher SEKIYA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * INT2 (IP20, IP22) /INT3 (IP24) interrupt controllers + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/proc.h> + +#include <mips64/archtype.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> +#include <machine/cpu.h> +#include <machine/intr.h> + +#include <dev/ic/i8253reg.h> + +#include <sgi/localbus/intreg.h> +#include <sgi/localbus/intvar.h> +#include <sgi/sgi/ip22.h> + +int int2_match(struct device *, void *, void *); +void int2_attach(struct device *, struct device *, void *); +int int2_mappable_intr(void *); + +const struct cfattach int_ca = { + sizeof(struct device), int2_match, int2_attach +}; + +struct cfdriver int_cd = { + NULL, "int", DV_DULL +}; + +paddr_t int2_base; + +#define int2_read(r) *(volatile uint8_t *)(int2_base + (r)) +#define int2_write(r, v) *(volatile uint8_t *)(int2_base + (r)) = (v) + +/* + * INT2 Interrupt handling declarations: 16 local sources on 2 levels. + * (we don't use the i8254 timer interrupts) + * + * In addition to this, INT3 provides 8 so-called mappable interrupts, which + * are cascaded to either one of the unused two INT2 VME interrupts. + * To make things easier from a software viewpoint, we pretend there are + * 16 of them - one set of 8 per cascaded interrupt. This allows for + * faster recognition on where to connect these interrupts - as long as + * interrupt vector assignment makes sure no mappable interrupt is + * registered on both cascaded interrupts. + */ + +#define INT2_NINTS (8 + 8 + 2 * 8) +struct intrhand *int2_intrhand[INT2_NINTS]; + +uint32_t int2_intem; +uint8_t int2_l0imask[NIPLS], int2_l1imask[NIPLS]; + +void int2_splx(int); +uint32_t int2_l0intr(uint32_t, struct trap_frame *); +void int2_l0makemasks(void); +uint32_t int2_l1intr(uint32_t, struct trap_frame *); +void int2_l1makemasks(void); + +/* + * Level 0 interrupt handler. + */ + +uint32_t save_l0imr, save_l0isr, save_l0ipl; +#define INTR_FUNCTIONNAME int2_l0intr +#define MASK_FUNCTIONNAME int2_l0makemasks + +#define INTR_LOCAL_DECLS +#define MASK_LOCAL_DECLS +#define INTR_GETMASKS \ +do { \ + isr = int2_read(INT2_LOCAL0_STATUS); \ + imr = int2_read(INT2_LOCAL0_MASK); \ + bit = 7; \ +save_l0isr = isr; save_l0imr = imr; save_l0ipl = frame->ipl; \ +} while (0) +#define INTR_MASKPENDING \ + int2_write(INT2_LOCAL0_MASK, imr & ~isr) +#define INTR_IMASK(ipl) int2_l0imask[ipl] +#define INTR_HANDLER(bit) int2_intrhand[bit + 0] +#define INTR_SPURIOUS(bit) \ +do { \ + printf("spurious int2 interrupt %d\n", bit); \ +} while (0) +#define INTR_MASKRESTORE \ + int2_write(INT2_LOCAL0_MASK, imr) +#define INTR_MASKSIZE 8 + +#include <sgi/sgi/intr_template.c> + +/* + * Level 1 interrupt handler. + */ + +uint32_t save_l1imr, save_l1isr, save_l1ipl; +#define INTR_FUNCTIONNAME int2_l1intr +#define MASK_FUNCTIONNAME int2_l1makemasks + +#define INTR_LOCAL_DECLS +#define MASK_LOCAL_DECLS +#define INTR_GETMASKS \ +do { \ + isr = int2_read(INT2_LOCAL1_STATUS); \ + imr = int2_read(INT2_LOCAL1_MASK); \ + bit = 7; \ +save_l1isr = isr; save_l1imr = imr; save_l1ipl = frame->ipl; \ +} while (0) +#define INTR_MASKPENDING \ + int2_write(INT2_LOCAL1_MASK, imr & ~isr) +#define INTR_IMASK(ipl) int2_l1imask[ipl] +#define INTR_HANDLER(bit) int2_intrhand[bit + 8] +#define INTR_SPURIOUS(bit) \ +do { \ + printf("spurious int2 interrupt %d\n", bit + 8); \ +} while (0) +#define INTR_MASKRESTORE \ + int2_write(INT2_LOCAL1_MASK, imr) +#define INTR_MASKSIZE 8 + +#include <sgi/sgi/intr_template.c> + +void * +int2_intr_establish(int irq, int level, int (*ih_fun) (void *), + void *ih_arg, const char *ih_what) +{ + struct intrhand **p, *q, *ih; + int s; + +#ifdef DIAGNOSTIC + if (irq < 0 || irq >= INT2_NINTS) + panic("int2_intr_establish: illegal irq %d", irq); +#endif + + ih = malloc(sizeof *ih, M_DEVBUF, M_NOWAIT); + if (ih == NULL) + return NULL; + + ih->ih_next = NULL; + ih->ih_fun = ih_fun; + ih->ih_arg = ih_arg; + ih->ih_level = level; + ih->ih_irq = irq; + if (ih_what != NULL) + evcount_attach(&ih->ih_count, ih_what, &ih->ih_irq); + + s = splhigh(); + + for (p = &int2_intrhand[irq]; (q = *p) != NULL; p = &q->ih_next) + ; + *p = ih; + + int2_intem |= 1 << irq; + switch (irq >> 3) { + case 0: + int2_l0makemasks(); + break; + case 1: + int2_l1makemasks(); + break; + /* + * We do not maintain masks for mappable interrupts. They are + * masked as a whole, by the level 0 or 1 interrupt they cascade to. + */ + case 2: + int2_write(INT2_MAP_MASK0, + int2_read(INT2_MAP_MASK0) | (1 << (irq & 7))); + break; + case 3: + int2_write(INT2_MAP_MASK1, + int2_read(INT2_MAP_MASK1) | (1 << (irq & 7))); + break; + } + + splx(s); /* will cause hardware mask update */ + + return ih; +} + +void +int2_splx(int newipl) +{ + struct cpu_info *ci = curcpu(); + uint32_t sr; + + __asm__ ("\t.set noreorder\n"); + ci->ci_ipl = newipl; + __asm__ ("sync\n\t.set reorder\n"); + + sr = disableintr(); /* XXX overkill? */ + int2_write(INT2_LOCAL1_MASK, (int2_intem >> 8) & ~int2_l1imask[newipl]); + int2_write(INT2_LOCAL0_MASK, int2_intem & ~int2_l0imask[newipl]); + setsr(sr); + + if (ci->ci_softpending != 0 && newipl < IPL_SOFTINT) + setsoftintr0(); +} + +/* + * Mappable interrupts handler. + */ + +int +int2_mappable_intr(void *arg) +{ + uint which = (unsigned long)arg; + uint64_t imr, isr; + uint i, intnum; + struct intrhand *ih; + int rc, ret; + + isr = int2_read(INT2_MAP_STATUS); + imr = int2_read(INT2_MAP_MASK0 + (which << 2)); + + isr &= imr; + if (isr == 0) + return 0; /* not for us */ + + /* + * Don't bother masking sources here - all mappable interrupts are + * tied to either a level 1 or level 0 interrupt, and the dispatcher + * is registered at IPL_TTY, so we can safely assume we are running + * at IPL_TTY now. + */ + + for (i = 0; i < 8; i++) { + intnum = i + 16 + (which << 3); + if (isr & (1 << i)) { + rc = 0; + for (ih = int2_intrhand[intnum]; ih != NULL; + ih = ih->ih_next) { + ret = (*ih->ih_fun)(ih->ih_arg); + if (ret != 0) { + rc = 1; + atomic_add_uint64(&ih->ih_count.ec_count, + 1); + } + if (ret == 1) + break; + } + if (rc == 0) + printf("spurious int2 mapped interrupt %d\n", + i); + } + } + + return 1; +} + +int +int2_match(struct device *parent, void *match, void *aux) +{ + struct mainbus_attach_args *maa = (void *)aux; + + switch (sys_config.system_type) { + case SGI_IP20: + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + break; + default: + return 0; + } + + return !strcmp(maa->maa_name, int_cd.cd_name); +} + +void +int2_attach(struct device *parent, struct device *self, void *aux) +{ + uint32_t address; + + switch (sys_config.system_type) { + case SGI_IP20: + address = INT2_IP20; + break; + default: + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + if (sys_config.system_subtype == IP22_INDIGO2) + address = INT2_IP22; + else + address = INT2_IP24; + break; + } + + printf(" addr 0x%x\n", address); + int2_base = PHYS_TO_XKPHYS((uint64_t)address, CCA_NC); + + /* Clean out interrupt masks */ + int2_write(INT2_LOCAL0_MASK, 0); + int2_write(INT2_LOCAL1_MASK, 0); + int2_write(INT2_MAP_MASK0, 0); + int2_write(INT2_MAP_MASK1, 0); + + /* Reset timer interrupts */ + int2_write(INT2_TIMER_CONTROL, + TIMER_SEL0 | TIMER_16BIT | TIMER_SWSTROBE); + int2_write(INT2_TIMER_CONTROL, + TIMER_SEL1 | TIMER_16BIT | TIMER_SWSTROBE); + int2_write(INT2_TIMER_CONTROL, + TIMER_SEL2 | TIMER_16BIT | TIMER_SWSTROBE); + __asm__ __volatile__ ("sync" ::: "memory"); + delay(4); + int2_write(INT2_TIMER_CLEAR, 0x03); + + set_intr(INTPRI_L1, CR_INT_1, int2_l1intr); + set_intr(INTPRI_L0, CR_INT_0, int2_l0intr); + register_splx_handler(int2_splx); + + if (sys_config.system_type != SGI_IP20) { + /* Wire interrupts 7, 11 to mappable interrupt 0,1 handlers */ + int2_intr_establish(7, IPL_TTY, int2_mappable_intr, + (void *)0, NULL); + int2_intr_establish(8 + 3, IPL_TTY, int2_mappable_intr, + (void *)1, NULL); + } +} + +/* + * Wait for the FIFO Full interrupt condition (Local 0 bit 0) to clear. + */ +void +int2_wait_fifo(uint32_t flag) +{ + if (int2_base == 0) + delay(5000); /* XXX */ + else + while (int2_read(INT2_LOCAL0_STATUS) & flag) + ; +} diff --git a/sys/arch/sgi/localbus/intreg.h b/sys/arch/sgi/localbus/intreg.h new file mode 100644 index 00000000000..c6deca4e933 --- /dev/null +++ b/sys/arch/sgi/localbus/intreg.h @@ -0,0 +1,51 @@ +/* $OpenBSD: intreg.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: int2reg.h,v 1.5 2009/02/12 06:33:57 rumble Exp $ */ + +/* + * Copyright (c) 2004 Christopher SEKIYA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* The INT has known locations on all SGI machines */ +#define INT2_IP20 0x1fb801c0 +#define INT2_IP22 0x1fbd9000 +#define INT2_IP24 0x1fbd9880 + +/* The following registers are all 8 bit. */ +#define INT2_LOCAL0_STATUS 0x03 +#define INT2_LOCAL0_STATUS_FIFO 0x01 +#define INT2_LOCAL0_MASK 0x07 +#define INT2_LOCAL1_STATUS 0x0b +#define INT2_LOCAL1_MASK 0x0f +#define INT2_MAP_STATUS 0x13 +#define INT2_MAP_MASK0 0x17 +#define INT2_MAP_MASK1 0x1b +#define INT2_MAP_POL 0x1f +#define INT2_TIMER_CLEAR 0x23 +#define INT2_ERROR_STATUS 0x27 +#define INT2_TIMER_0 0x33 +#define INT2_TIMER_1 0x37 +#define INT2_TIMER_2 0x3b +#define INT2_TIMER_CONTROL 0x3f diff --git a/sys/arch/sgi/localbus/intvar.h b/sys/arch/sgi/localbus/intvar.h new file mode 100644 index 00000000000..c8f6a133862 --- /dev/null +++ b/sys/arch/sgi/localbus/intvar.h @@ -0,0 +1,34 @@ +/* $OpenBSD: intvar.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: int2var.h,v 1.3 2008/08/23 17:25:54 tsutsui Exp $ */ + +/* + * Copyright (c) 2004 Christopher SEKIYA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +void *int2_intr_establish(int, int, int (*)(void *), + void *, const char *); + +void int2_wait_fifo(uint32_t); diff --git a/sys/arch/sgi/sgi/autoconf.c b/sys/arch/sgi/sgi/autoconf.c index 6416fa757d7..0c18a9b1e0a 100644 --- a/sys/arch/sgi/sgi/autoconf.c +++ b/sys/arch/sgi/sgi/autoconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: autoconf.c,v 1.33 2011/05/30 22:25:22 oga Exp $ */ +/* $OpenBSD: autoconf.c,v 1.34 2012/03/28 20:44:23 miod Exp $ */ /* * Copyright (c) 2009, 2010 Miodrag Vallat. * @@ -238,7 +238,8 @@ device_register(struct device *dev, void *aux) (*_device_register)(dev, aux); } -#if defined(TGT_O2) || defined(TGT_OCTANE) +#if defined(TGT_INDIGO) || defined(TGT_INDY) || defined(TGT_INDIGO2) || \ + defined(TGT_O2) || defined(TGT_OCTANE) /* * ARCS boot path traversal @@ -324,6 +325,7 @@ arcs_device_register(struct device *dev, void *aux) { static struct device *lastparent = NULL; static struct device *pciparent = NULL; + static struct device *wdscparent = NULL; static int component_pos = 0; struct device *parent = dev->dv_parent; @@ -409,22 +411,45 @@ arcs_device_register(struct device *dev, void *aux) if (parent == lastparent) goto found_advance; + if (component_pos == 0) + switch (sys_config.system_type) { #ifdef TGT_O2 /* * On O2, the pci(0) component may be omitted from * the bootpath, in which case we fake the missing * pci(0) component. */ - if (sys_config.system_type == SGI_O2 && - component_pos == 0) { + case SGI_O2: if (parent->dv_parent != NULL && strcmp(parent->dv_parent->dv_cfdata->cf_driver->cd_name, "pci") == 0) { pciparent = parent->dv_parent; goto found_advance; } - } + break; #endif +#if defined(TGT_INDIGO) || defined(TGT_INDY) || defined(TGT_INDIGO2) + /* + * On Ind{igo,y,i^2} systems, the bootpath + * starts at scsi(). + */ + case SGI_IP20: + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + if (strcmp(parent->dv_cfdata->cf_driver->cd_name, + "wdsc") == 0 && + parent->dv_parent != NULL && + strcmp(parent->dv_parent->dv_cfdata->cf_driver->cd_name, + "hpc") == 0) { + wdscparent = parent; + goto found_advance; + } + break; +#endif + default: + break; + } } if (parent == lastparent) { @@ -434,6 +459,12 @@ arcs_device_register(struct device *dev, void *aux) if (unit == paa->pa_device - (sys_config.system_type == SGI_O2 ? 1 : 0)) goto found; + } else + if (parent == wdscparent) { + /* XXX is there any better information to use + XXX than the attachment number? */ + if (unit == parent->dv_unit) + goto found; } /* * in case scsi() can follow something else then @@ -473,7 +504,7 @@ found: lastparent = dev; } -#endif /* defined(TGT_O2) || defined(TGT_OCTANE) */ +#endif /* IP20/22/24/26/28/30/32 */ #ifdef TGT_ORIGIN @@ -647,7 +678,7 @@ dksc_device_register(struct device *dev, void *aux) } } -#endif +#endif /* IP27/35 */ struct nam2blk nam2blk[] = { { "sd", 0 }, diff --git a/sys/arch/sgi/sgi/conf.c b/sys/arch/sgi/sgi/conf.c index 9a229de9161..0da5395e126 100644 --- a/sys/arch/sgi/sgi/conf.c +++ b/sys/arch/sgi/sgi/conf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.c,v 1.29 2011/10/06 20:49:28 deraadt Exp $ */ +/* $OpenBSD: conf.c,v 1.30 2012/03/28 20:44:23 miod Exp $ */ /* * Copyright (c) 1992, 1993 @@ -97,6 +97,8 @@ cdev_decl(fd); #include "tun.h" #include "com.h" cdev_decl(com); +#include "zs.h" +cdev_decl(zs); #include "lpt.h" cdev_decl(lpt); #include "ch.h" @@ -154,7 +156,7 @@ struct cdevsw cdevsw[] = cdev_lpt_init(NLPT,lpt), /* 16: Parallel printer interface */ cdev_tty_init(NCOM,com), /* 17: 16C450 serial interface */ cdev_disk_init(NWD,wd), /* 18: ST506/ESDI/IDE disk */ - cdev_notdef(), /* 19: */ + cdev_tty_init(NZS,zs), /* 19: Z8530 serial interface */ cdev_notdef(), /* 20: */ cdev_notdef(), /* 21: */ cdev_disk_init(NRD,rd), /* 22: ramdisk device */ @@ -294,8 +296,9 @@ int nchrtoblktbl = nitems(chrtoblktbl); #include <dev/cons.h> -cons_decl(ws); cons_decl(com); +cons_decl(ws); +cons_decl(zs); struct consdev constab[] = { #if NWSDISPLAY > 0 @@ -304,5 +307,8 @@ struct consdev constab[] = { #if NCOM > 0 cons_init(com), #endif +#if NZS > 0 + cons_init(zs), +#endif { 0 }, }; diff --git a/sys/arch/sgi/sgi/ip22.h b/sys/arch/sgi/sgi/ip22.h new file mode 100644 index 00000000000..9fd00054f77 --- /dev/null +++ b/sys/arch/sgi/sgi/ip22.h @@ -0,0 +1,32 @@ +/* $OpenBSD: ip22.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ + +/* + * Copyright (c) 2012 Miodrag Vallat. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * IP20/IP22/IP24 definitions + */ + +/* IP22 system types */ + +#define IP22_INDY 0 /* Indy, Challenge S */ +#define IP22_INDIGO2 1 /* Indigo 2 */ + +/* Interrupt handling priority */ + +#define INTPRI_BUSERR (INTPRI_CLOCK + 1) +#define INTPRI_L1 (INTPRI_BUSERR + 1) +#define INTPRI_L0 (INTPRI_L1 + 1) diff --git a/sys/arch/sgi/sgi/ip22_machdep.c b/sys/arch/sgi/sgi/ip22_machdep.c new file mode 100644 index 00000000000..dbe7ccbd330 --- /dev/null +++ b/sys/arch/sgi/sgi/ip22_machdep.c @@ -0,0 +1,277 @@ +/* $OpenBSD: ip22_machdep.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ + +/* + * Copyright (c) 2012 Miodrag Vallat. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> + +#include <uvm/uvm_extern.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> +#include <machine/cpu.h> +#include <machine/memconf.h> + +#include <mips64/arcbios.h> +#include <mips64/archtype.h> + +#include <sgi/sgi/ip22.h> +#include <sgi/localbus/imcreg.h> +#include <sgi/localbus/imcvar.h> +#include <sgi/hpc/hpcreg.h> +#include <sgi/hpc/iocreg.h> + +extern char *hw_prod; + +void ip22_arcbios_walk(void); +int ip22_arcbios_walk_component(arc_config_t *); +void ip22_memory_setup(void); + +/* + * Walk the ARCBios component tree to get L2 cache information. + * This is the only way we can get the L2 cache size. + */ + +int +ip22_arcbios_walk_component(arc_config_t *cf) +{ + struct cpu_info *ci = curcpu(); + arc_config_t *child; + + /* + * Split secondary caches are not supported. + * No IP22 processor module uses them anyway. + */ + if (cf->class == arc_CacheClass && cf->type == arc_SecondaryCache) { + /* + * Secondary cache information is encoded as WWLLSSSS, where + * WW is the number of ways (should be 01) + * LL is Log2(line size) (should be 04 or 05) + * SS is Log2(cache size in 4KB units) (should be 0007) + */ + ci->ci_l2size = (1 << 12) << (cf->key & 0x0000ffff); + /* L2 line size */ + ci->ci_cacheconfiguration = 1 << ((cf->key >> 16) & 0xff); + return 0; /* abort walk */ + } + + /* + * It is safe to assume we have a 32-bit ARCBios, until + * IP26 and IP28 support is added, hence unconditional + * use of arc_config_t. + */ + for (child = (arc_config_t *)Bios_GetChild(cf); child != NULL; + child = (arc_config_t *)Bios_GetPeer(child)) { + if (ip22_arcbios_walk_component(child) == 0) + return 0; + } + + return 1; /* continue walk */ +} + +void +ip22_arcbios_walk() +{ + (void)ip22_arcbios_walk_component((arc_config_t *)Bios_GetChild(NULL)); +} + +#define IMC_NREGION 3 + +void +ip22_memory_setup() +{ + uint i, bank, shift; + uint32_t memc0, memc1; + uint32_t memc; + paddr_t base[IMC_NREGION], size[IMC_NREGION], limit; + paddr_t start0, end0, start1, end1; + struct phys_mem_desc *mem; + + /* + * Figure out the top of memory, as reported by ARCBios. + */ + + limit = 0; + for (i = 0, mem = mem_layout; i < MAXMEMSEGS; i++, mem++) { + if (mem->mem_last_page > limit) + limit = mem->mem_last_page; + } + limit = ptoa(limit); + + /* + * Figure out where the memory controller has put memory. + */ + + memc0 = imc_read(IMC_MEMCFG0); + memc1 = imc_read(IMC_MEMCFG1); + + shift = IMC_MEMC_LSHIFT; + /* Revision D onwards uses larger units, to allow for more memory */ + if ((imc_read(IMC_SYSID) & IMC_SYSID_REVMASK) >= 5) + shift = IMC_MEMC_LSHIFT_HUGE; + + for (bank = 0; bank < IMC_NREGION; bank++) { + memc = (bank & 2) ? memc1 : memc0; + if ((bank & 1) == 0) + memc >>= IMC_MEMC_BANK_SHIFT; + memc &= IMC_MEMC_BANK_MASK; + + if ((memc & IMC_MEMC_VALID) == 0) { + base[bank] = size[bank] = 0; + continue; + } + + base[bank] = (memc & IMC_MEMC_ADDR_MASK) >> IMC_MEMC_ADDR_SHIFT; + base[bank] <<= shift; + + size[bank] = (memc & IMC_MEMC_SIZE_MASK) >> IMC_MEMC_SIZE_SHIFT; + size[bank]++; + size[bank] <<= shift; + } + + /* + * Perform sanity checks on the above data.. + */ + + /* memory should not start below 128MB */ + for (bank = 0; bank < IMC_NREGION; bank++) + if (size[bank] != 0 && base[bank] < (1ULL << 27)) + goto dopanic; + + /* banks should not overlap */ + for (bank = 1; bank < IMC_NREGION; bank++) { + if (size[bank] == 0) + continue; + start0 = base[bank]; + end0 = base[bank] + size[bank]; + for (i = 0; i < bank; i++) { + if (size[i] == 0) + continue; + start1 = base[i]; + end1 = base[i] + size[i]; + if (end0 > start1 && start0 < end1) + goto dopanic; + } + } + + /* + * Now register all the memory beyond what ARCBios stopped at. + */ + + for (bank = 0; bank < IMC_NREGION; bank++) { + if (size[bank] == 0) + continue; + + start0 = base[bank]; + end0 = base[bank] + size[bank]; + if (end0 <= limit) + continue; + + if (start0 < limit) + start0 = limit; + + memrange_register(atop(start0), atop(end0), 0); + } + + return; + +dopanic: + bios_printf("** UNEXPECTED MEMORY CONFIGURATION **\n"); + bios_printf("MEMC0 %08x MEMC1 %08x\n", memc0, memc1); + bios_printf("Please contact <sgi@openbsd.org>\n" + "Halting system.\n"); + Bios_Halt(); + for (;;) ; +} + +void +ip22_setup() +{ + u_long cpuspeed; + volatile uint32_t *sysid; + + /* + * Get CPU information. + */ + bootcpu_hwinfo.c0prid = cp0_get_prid(); + bootcpu_hwinfo.c1prid = cp1_get_prid(); + cpuspeed = bios_getenvint("cpufreq"); + if (sys_config.system_type == SGI_IP20) + cpuspeed <<= 1; + if (cpuspeed < 100) + cpuspeed = 100; /* reasonable default */ + bootcpu_hwinfo.clock = cpuspeed * 1000000; + bootcpu_hwinfo.type = (bootcpu_hwinfo.c0prid >> 8) & 0xff; + + /* + * Figure out what critter we are running on. + */ + switch (sys_config.system_type) { + case SGI_IP20: + hw_prod = "Indigo"; + break; + case SGI_IP22: + sysid = (volatile uint32_t *) + PHYS_TO_XKPHYS(HPC_BASE_ADDRESS_0 + IOC_BASE + IOC_SYSID, + CCA_NC); + if (*sysid & 0x01) { + sys_config.system_subtype = IP22_INDIGO2; + hw_prod = "Indigo2"; + } else { + sys_config.system_subtype = IP22_INDY; + hw_prod = "Indy"; + } + break; + case SGI_IP26: + sys_config.system_subtype = IP22_INDIGO2; + hw_prod = "POWER Indigo2 R8000"; + break; + case SGI_IP28: + sys_config.system_subtype = IP22_INDIGO2; + hw_prod = "POWER Indigo2 R10000"; + break; + } + + /* + * Figure out how many TLB entries are available. + */ + switch (bootcpu_hwinfo.type) { +#ifdef CPU_R10000 + case MIPS_R10000: + bootcpu_hwinfo.tlbsize = 64; + break; +#endif + default: /* R4x00, R5000 */ + bootcpu_hwinfo.tlbsize = 48; + break; + } + + /* + * Compute memory layout. ARCBios may not report all memory (on + * Indigo, it seems to only report up to 128MB, and on Indigo2, + * up to 256MB). + */ + ip22_memory_setup(); + + /* + * Scan ARCBios component list for L2 cache information. + */ + ip22_arcbios_walk(); + + _device_register = arcs_device_register; +} diff --git a/sys/arch/sgi/sgi/ip30_machdep.c b/sys/arch/sgi/sgi/ip30_machdep.c index 4a2d5dc02e2..c5077d31438 100644 --- a/sys/arch/sgi/sgi/ip30_machdep.c +++ b/sys/arch/sgi/sgi/ip30_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip30_machdep.c,v 1.50 2012/03/25 13:52:52 miod Exp $ */ +/* $OpenBSD: ip30_machdep.c,v 1.51 2012/03/28 20:44:23 miod Exp $ */ /* * Copyright (c) 2008, 2009 Miodrag Vallat. @@ -168,9 +168,7 @@ ip30_setup() comconsaddr = IOC3_UARTA_BASE; comconsfreq = 22000000 / 3; comconsiot = &sys_config.console_io; - comconsrate = bios_getenvint("dbaud"); - if (comconsrate < 50 || comconsrate > 115200) - comconsrate = 9600; + comconsrate = bios_consrate; #ifdef DDB /* diff --git a/sys/arch/sgi/sgi/ip32_machdep.c b/sys/arch/sgi/sgi/ip32_machdep.c index e3760cbd031..611f3f2d058 100644 --- a/sys/arch/sgi/sgi/ip32_machdep.c +++ b/sys/arch/sgi/sgi/ip32_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip32_machdep.c,v 1.17 2012/03/15 18:57:22 miod Exp $ */ +/* $OpenBSD: ip32_machdep.c,v 1.18 2012/03/28 20:44:23 miod Exp $ */ /* * Copyright (c) 2003-2004 Opsycon AB (www.opsycon.se / www.opsycon.com) @@ -198,9 +198,7 @@ ip32_setup() comconsaddr = MACE_ISA_SER1_OFFS; comconsfreq = 1843200; comconsiot = &macebus_tag; - comconsrate = bios_getenvint("dbaud"); - if (comconsrate < 50 || comconsrate > 115200) - comconsrate = 9600; + comconsrate = bios_consrate; } /* not sure if there is a way to tell O2 and O2+ apart */ diff --git a/sys/arch/sgi/sgi/machdep.c b/sys/arch/sgi/sgi/machdep.c index 0d4f8811c2a..52405982621 100644 --- a/sys/arch/sgi/sgi/machdep.c +++ b/sys/arch/sgi/sgi/machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: machdep.c,v 1.114 2012/03/25 13:52:52 miod Exp $ */ +/* $OpenBSD: machdep.c,v 1.115 2012/03/28 20:44:23 miod Exp $ */ /* * Copyright (c) 2003-2004 Opsycon AB (www.opsycon.se / www.opsycon.com) @@ -141,11 +141,11 @@ caddr_t mips_init(int argc, void *argv, caddr_t boot_esym) { char *cp; - int i; + int i, guessed; u_int cputype; vaddr_t xtlb_handler; extern char start[], edata[], end[]; - extern char exception[], e_exception[]; + extern char cache_err[], exception[], e_exception[]; extern char *hw_vendor; #ifdef MULTIPROCESSOR @@ -222,6 +222,34 @@ mips_init(int argc, void *argv, caddr_t boot_esym) */ hw_vendor = "SGI"; switch (sys_config.system_type) { +#ifdef TGT_INDIGO + case SGI_IP20: + bios_printf("Found SGI-IP20, setting up.\n"); + /* IP22 is intentional, we use the same kernel */ + strlcpy(cpu_model, "IP22", sizeof(cpu_model)); + ip22_setup(); + break; +#endif +#if defined(TGT_INDY) || defined(TGT_INDIGO2) + case SGI_IP22: + bios_printf("Found SGI-IP22, setting up.\n"); + strlcpy(cpu_model, "IP22", sizeof(cpu_model)); + ip22_setup(); + break; +#endif +#ifdef TGT_INDIGO2 + case SGI_IP26: + bios_printf("Found SGI-IP26, setting up.\n"); + /* IP28 is intentional, we will probably use the same kernel */ + strlcpy(cpu_model, "IP28", sizeof(cpu_model)); + ip22_setup(); + break; + case SGI_IP28: + bios_printf("Found SGI-IP28, setting up.\n"); + strlcpy(cpu_model, "IP28", sizeof(cpu_model)); + ip22_setup(); + break; +#endif #ifdef TGT_O2 case SGI_O2: bios_printf("Found SGI-IP32, setting up.\n"); @@ -253,10 +281,14 @@ mips_init(int argc, void *argv, caddr_t boot_esym) break; #endif default: - bios_printf("Kernel doesn't support this system type!\n"); + bios_printf("There is no support for this system type " + "(%02x) in this kernel.\n" + "Are you sure you have booted the right kernel " + "for this machine?\n", + sys_config.system_type); bios_printf("Halting system.\n"); Bios_Halt(); - while(1); + for (;;) ; } /* @@ -277,20 +309,16 @@ mips_init(int argc, void *argv, caddr_t boot_esym) "The kernel might not be able to find out its root device.\n"); /* - * Read platform-specific environment variables. + * Read platform-specific environment variables from ARCBios. + * (Note these may not exist on all systems) */ - switch (sys_config.system_type) { -#ifdef TGT_O2 - case SGI_O2: - /* Get Ethernet address from ARCBIOS. */ - cp = Bios_GetEnvironmentVariable("eaddr"); - if (cp != NULL && strlen(cp) > 0) - strlcpy(bios_enaddr, cp, sizeof bios_enaddr); - break; -#endif - default: - break; - } + /* onboard Ethernet address (does not exist on IP27/IP35) */ + cp = Bios_GetEnvironmentVariable("eaddr"); + if (cp != NULL && strlen(cp) > 0) + strlcpy(bios_enaddr, cp, sizeof bios_enaddr); + bios_consrate = bios_getenvint("dbaud"); + if (bios_consrate < 50 || bios_consrate > 115200) + bios_consrate = 9600; /* sane default */ /* * Set pagesize to enable use of page macros and functions. @@ -345,6 +373,7 @@ mips_init(int argc, void *argv, caddr_t boot_esym) /* * Configure cache. */ + guessed = 0; switch (bootcpu_hwinfo.type) { #ifdef CPU_R10000 case MIPS_R10000: @@ -353,6 +382,16 @@ mips_init(int argc, void *argv, caddr_t boot_esym) cputype = MIPS_R10000; break; #endif +#ifdef CPU_R4000 + case MIPS_R4000: + cputype = MIPS_R4000; + break; +#endif +#ifdef CPU_R4600 + case MIPS_R4600: + cputype = MIPS_R5000; + break; +#endif #ifdef CPU_R5000 case MIPS_R5000: case MIPS_RM52X0: @@ -369,24 +408,52 @@ mips_init(int argc, void *argv, caddr_t boot_esym) /* * If we can't identify the cpu type, it must be * r10k-compatible on Octane and Origin families, and - * it is likely to be r5k-compatible on O2. + * it is likely to be r5k-compatible on O2 and + * r4k-compatible on Ind{igo*,y}. */ + guessed = 1; switch (sys_config.system_type) { + case SGI_IP20: + case SGI_IP22: + bios_printf("Unrecognized processor type, assuming" + " R4000 compatible\n"); + cputype = MIPS_R4000; + break; case SGI_O2: + bios_printf("Unrecognized processor type, assuming" + " R5000 compatible\n"); cputype = MIPS_R5000; break; + case SGI_IP26: + bios_printf("Unrecognized processor type, assuming" + " R8000 compatible\n"); + cputype = MIPS_R8000; + break; default: - case SGI_OCTANE: case SGI_IP27: + case SGI_IP28: + case SGI_OCTANE: case SGI_IP35: + bios_printf("Unrecognized processor type, assuming" + " R10000 compatible\n"); cputype = MIPS_R10000; break; } break; } switch (cputype) { - default: -#if defined(CPU_R5000) || defined(CPU_RM7000) +#ifdef CPU_R4000 + case MIPS_R4000: + Mips4k_ConfigCache(curcpu()); + sys_config._SyncCache = Mips4k_SyncCache; + sys_config._InvalidateICache = Mips4k_InvalidateICache; + sys_config._SyncDCachePage = Mips4k_SyncDCachePage; + sys_config._HitSyncDCache = Mips4k_HitSyncDCache; + sys_config._IOSyncDCache = Mips4k_IOSyncDCache; + sys_config._HitInvalidateDCache = Mips4k_HitInvalidateDCache; + break; +#endif +#if defined(CPU_R4600) || defined(CPU_R5000) || defined(CPU_RM7000) case MIPS_R5000: Mips5k_ConfigCache(curcpu()); sys_config._SyncCache = Mips5k_SyncCache; @@ -408,6 +475,20 @@ mips_init(int argc, void *argv, caddr_t boot_esym) sys_config._HitInvalidateDCache = Mips10k_HitInvalidateDCache; break; #endif + default: + if (guessed) { + bios_printf("There is no support for this processor " + "family in this kernel.\n" + "Are you sure you have booted the right kernel " + "for this machine?\n"); + } else { + bios_printf("There is no support for this processor " + "family (%02x) in this kernel.\n", cputype); + } + bios_printf("Halting system.\n"); + Bios_Halt(); + for (;;) ; + break; } /* @@ -449,13 +530,21 @@ mips_init(int argc, void *argv, caddr_t boot_esym) /* * Copy down exception vector code. */ - bcopy(exception, (char *)CACHE_ERR_EXC_VEC, e_exception - exception); + bcopy(exception, (char *)CACHE_ERR_EXC_VEC, e_exception - cache_err); bcopy(exception, (char *)GEN_EXC_VEC, e_exception - exception); /* * Build proper TLB refill handler trampolines. */ switch (cputype) { +#ifdef CPU_R4000 + case MIPS_R4000: + { + extern void xtlb_miss_err_r4k; + xtlb_handler = (vaddr_t)&xtlb_miss_err_r4k; + } + break; +#endif #if defined(CPU_R5000) || defined(CPU_RM7000) case MIPS_R5000: { @@ -744,9 +833,17 @@ arcbios_halt(int howto) #endif if (howto & RB_HALT) { - if (howto & RB_POWERDOWN) + if (howto & RB_POWERDOWN) { +#ifdef TGT_INDY + /* + * ARCBios needs to use the FPU on Indy during + * shutdown. + */ + if (sys_config.system_type == SGI_IP22) + setsr(getsr() | SR_COP_1_BIT); +#endif Bios_PowerDown(); - else + } else Bios_EnterInteractiveMode(); } else Bios_Reboot(); diff --git a/sys/arch/sgi/sgi/mainbus.c b/sys/arch/sgi/sgi/mainbus.c index 5d8e411d0d1..018bbd372a4 100644 --- a/sys/arch/sgi/sgi/mainbus.c +++ b/sys/arch/sgi/sgi/mainbus.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mainbus.c,v 1.8 2010/01/09 20:33:16 miod Exp $ */ +/* $OpenBSD: mainbus.c,v 1.9 2012/03/28 20:44:23 miod Exp $ */ /* * Copyright (c) 2001-2003 Opsycon AB (www.opsycon.se / www.opsycon.com) @@ -89,9 +89,9 @@ mbattach(struct device *parent, struct device *self, void *aux) } /* - * On other systems, attach the CPU we are running on early; - * other processors, if any, will get attached as they are - * discovered. + * On other systems, attach the CPU, its clock, and the + * mainbus here. + * XXX Would be worth doing as ipXX_autoconf() too. */ bzero(&caa, sizeof caa); @@ -103,6 +103,19 @@ mbattach(struct device *parent, struct device *self, void *aux) config_found(self, &caa.caa_maa, mbprint); switch (sys_config.system_type) { +#if defined(TGT_INDIGO) || defined(TGT_INDY) || defined(TGT_INDIGO2) + case SGI_IP20: + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + /* Interrupt Controller */ + caa.caa_maa.maa_name = "int"; + config_found(self, &caa.caa_maa, mbprint); + /* Memory Controller */ + caa.caa_maa.maa_name = "imc"; + config_found(self, &caa.caa_maa, mbprint); + break; +#endif #ifdef TGT_O2 case SGI_O2: caa.caa_maa.maa_name = "macebus"; diff --git a/sys/arch/sgi/stand/Makefile b/sys/arch/sgi/stand/Makefile index 62a19908f9c..d18503c5228 100644 --- a/sys/arch/sgi/stand/Makefile +++ b/sys/arch/sgi/stand/Makefile @@ -1,11 +1,10 @@ -# $OpenBSD: Makefile,v 1.7 2012/03/19 17:38:29 miod Exp $ +# $OpenBSD: Makefile,v 1.8 2012/03/28 20:44:23 miod Exp $ SUBDIR= sgivol .if ${MACHINE} == "sgi" SUBDIR+= libsa libsa32 libz libz32 -SUBDIR+= boot64 boot32 -#SUBDIR+= bootecoff +SUBDIR+= boot64 boot32 bootecoff .endif .include <bsd.subdir.mk> diff --git a/sys/conf/files b/sys/conf/files index 379b1e2cdb8..dad448cd616 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.534 2012/03/09 13:01:28 ariane Exp $ +# $OpenBSD: files,v 1.535 2012/03/28 20:44:23 miod Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -234,6 +234,10 @@ file dev/ic/osiop.c osiop & (osiop_gsc | osiop_eisa | osiop_pcctwo) device oosiop: scsi file dev/ic/oosiop.c oosiop +# Western Digital WD33C93 SCSI controllers +define wd33c93 +file dev/ic/wd33c93.c wd33c93 + # 3Com Etherlink-III Ethernet controller device ep: ether, ifnet, ifmedia, mii file dev/ic/elink3.c ep diff --git a/sys/dev/eisa/eisavar.h b/sys/dev/eisa/eisavar.h index d8d5b59f84c..e48cfa8f561 100644 --- a/sys/dev/eisa/eisavar.h +++ b/sys/dev/eisa/eisavar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: eisavar.h,v 1.13 2010/05/23 14:50:31 deraadt Exp $ */ +/* $OpenBSD: eisavar.h,v 1.14 2012/03/28 20:44:23 miod Exp $ */ /* $NetBSD: eisavar.h,v 1.11 1997/06/06 23:30:07 thorpej Exp $ */ /* @@ -54,17 +54,12 @@ struct eisabus_attach_args; /* * Machine-dependent definitions. */ -#if (__alpha__ + __i386__ + __hppa__ != 1) -#error COMPILING FOR UNSUPPORTED MACHINE, OR MORE THAN ONE. -#endif -#if __alpha__ +#if defined(__alpha__) #include <alpha/eisa/eisa_machdep.h> -#endif -#if __i386__ +#elif defined(__i386__) #include <i386/eisa/eisa_machdep.h> -#endif -#if __hppa__ -#include <hppa/include/eisa_machdep.h> +#else +#include <machine/eisa_machdep.h> #endif typedef int eisa_slot_t; /* really only needs to be 4 bits */ diff --git a/sys/dev/ic/dp8573areg.h b/sys/dev/ic/dp8573areg.h new file mode 100644 index 00000000000..bf66e70bf1e --- /dev/null +++ b/sys/dev/ic/dp8573areg.h @@ -0,0 +1,121 @@ +/* $OpenBSD: dp8573areg.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: dp8573areg.h,v 1.1 2009/02/12 06:33:57 rumble Exp $ */ + +/* + * Copyright (c) 2003 Steve Rumble + * Copyright (c) 2001 Erik Reid + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * National Semiconductor DP8573A Real Time Clock + */ + +/* Control and Status Register Offsets and Masks */ +#define DP8573A_STATUS 0x00 /* Main Status */ +#define DP8573A_STATUS_INTSTAT 0x01 /* Interrupt Status */ +#define DP8573A_STATUS_PWRFAIL 0x02 /* Power Fail Interrupt */ +#define DP8573A_STATUS_PERINT 0x04 /* Period Interrupt */ +#define DP8573A_STATUS_ALMINT 0x08 /* Alarm Interrupt */ +#define DP8573A_STATUS_REGSEL 0x40 /* Register Select */ + +/* Register Select = 0 */ +#define DP8573A_PFLAG 0x03 /* Periodic Flag */ +#define DP8573A_PFLAG_MIN 0x01 /* Minutes */ +#define DP8573A_PFLAG_10SEC 0x02 /* Ten Second */ +#define DP8573A_PFLAG_SEC 0x04 /* Seconds */ +#define DP8573A_PFLAG_100MIL 0x08 /* 100 Millisecond */ +#define DP8573A_PFLAG_10MIL 0x10 /* 10 Millisecond */ +#define DP8573A_PFLAG_MIL 0x20 /* Milliseconds */ +#define DP8573A_PFLAG_OFSS 0x40 /* Oscillator Fail/Single Supply */ +#define DP8573A_PFLAG_TESTMODE 0x80 /* Test Mode Enable */ + +#define DP8573A_TIMESAVE_CTL 0x04 /* Time Save Control */ +#define DP8573A_TIMESAVE_CTL_EN 0x80 /* Time Save Enable */ + +/* Register Select = 1 */ +#define DP8573A_RT_MODE 0x01 /* Real Time Mode */ +#define DP8573A_RT_MODE_LYLSB 0x01 /* Leap Year LSB */ +#define DP8573A_RT_MODE_LYMSB 0x02 /* Leap Year MSB */ +#define DP8573A_RT_MODE_1224 0x04 /* 12(low)/24(high) Hour Mode */ +#define DP8573A_RT_MODE_CLKSS 0x08 /* Clock Start(high)/Stop(low) */ +#define DP8573A_RT_MODE_INTPFOP 0x10 /* Interrupt PF Operation */ + +#define DP8573A_OUT_MODE 0x02 /* Output Mode */ +#define DP8573A_OUT_MODE_MFOPO 0x80 /* MFO Pin as Oscillator */ + +#define DP8573A_INT0_CTL 0x03 /* Interrupt Control 0 */ +#define DP8573A_INT0_CTL_MIN 0x01 /* Minutes Enable */ +#define DP8573A_INT0_CTL_10SEC 0x02 /* 10 Second Enable */ +#define DP8573A_INT0_CTL_SEC 0x04 /* Seconds Enable */ +#define DP8573A_INT0_CTL_100MIL 0x08 /* 100 Millisecond Enable */ +#define DP8573A_INT0_CTL_10MIL 0x10 /* 10 Millisecond Enable */ +#define DP8573A_INT0_CTL_MIL 0x20 /* Millisecond Enable */ + +#define DP8573A_INT1_CTL 0x04 /* Interrupt Control 1 */ +#define DP8573A_INT1_CTL_SECC 0x01 /* Second Compare Enable */ +#define DP8573A_INT1_CTL_MINC 0x02 /* Minute Compare Enable */ +#define DP8573A_INT1_CTL_HOURC 0x04 /* Hour Compare Enable */ +#define DP8573A_INT1_CTL_DOMC 0x08 /* Day of Month Compare Enable */ +#define DP8573A_INT1_CTL_MONTHC 0x10 /* Month Compare Enable */ +#define DP8573A_INT1_CTL_DOWC 0x20 /* Day of Week Compare Enable */ +#define DP8573A_INT1_CTL_ALMINT 0x40 /* Alarm Interrupt Enable */ +#define DP8573A_INT1_CTL_PWRINT 0x80 /* Power Fail Interrupt Enable */ + +/* Clock Counter Offsets */ +#define DP8573A_COUNTERS 0x05 /* Start of Clock Counters */ +#define DP8573A_SUBSECOND 0x05 /* 1/100 Second */ +#define DP8573A_SECOND 0x06 /* Seconds */ +#define DP8573A_MINUTE 0x07 /* Minutes */ +#define DP8573A_HOUR 0x08 /* Hours */ +#define DP8573A_DOM 0x09 /* Day of Month */ +#define DP8573A_MONTH 0x0a /* Months */ +#define DP8573A_YEAR 0x0b /* Years */ +#define DP8573A_DOW 0x0e /* Day of Week */ + +/* Comparsion Registers */ +#define DP8573A_CMP_SEC 0x13 /* Seconds */ +#define DP8573A_CMP_MIN 0x14 /* Minutes */ +#define DP8573A_CMP_HOUR 0x15 /* Hours */ +#define DP8573A_CMP_DOM 0x16 /* Day of Month */ +#define DP8573A_CMP_MONTH 0x17 /* Months */ +#define DP8573A_CMP_DOW 0x18 /* Day of Week */ + +/* Time Save Registers */ +#define DP8573A_SAVE_SEC 0x19 /* Seconds */ +#define DP8573A_SAVE_MIN 0x1a /* Minutes */ +#define DP8573A_SAVE_HOUR 0x1b /* Hours */ +#define DP8573A_SAVE_DOM 0x1c /* Day of Month */ +#define DP8573A_SAVE_MONTH 0x1d /* Months */ + +/* RAM Registers */ +#define DP8573A_RAM_0C 0x0c /* RAM */ +#define DP8573A_RAM_1E 0x1e /* RAM */ +#define DP8573A_RAM_1F 0x1f /* RAM */ + +/* 12/24 Hour Masks */ +#define DP8573A_HOUR_12HR_MASK 0x1f +#define DP8573A_HOUR_24HR_MASK 0x3f + +#define DP8573A_NREG 0x20 diff --git a/sys/dev/ic/dp857xreg.h b/sys/dev/ic/dp857xreg.h deleted file mode 100644 index 9164c574718..00000000000 --- a/sys/dev/ic/dp857xreg.h +++ /dev/null @@ -1,133 +0,0 @@ -/* $OpenBSD: dp857xreg.h,v 1.4 2012/03/07 18:15:25 miod Exp $ */ - -/* - * Copyright (c) 1996 Per Fogelstrom - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ - -#if !defined(_DP857X_H) -#define _DP857X_H - -/* - * Definition of Real Time Clock address space. - * - * Clock is a National DP8570A RTC - */ -#define BSIZE 1 /* No of Bytes for Address Spacing */ -#define MAIN_STATUS 0x00 /* Main status register */ -/* - * Registers selected with BS=0 RS=0 - */ -#define TIMER0_CTRL 0x01 /* Timer 0 control register */ -#define TIMER1_CTRL 0x02 /* Timer 0 control register */ -#define PERIODIC_FLAGS 0x03 /* Timer periodic flag register */ -#define INTERRUPT_ROUT 0x04 /* Interrupt routing register */ -/* - * Registers selected with BS=0 RS=1 - */ -#define REAL_TIME_MODE 0x01 /* Real Time Mode register */ -#define OUTPUT_MODE 0x02 /* Output mode register */ -#define INTERRUPT_CTRL0 0x03 /* Interrupt control register 0 */ -#define INTERRUPT_CTRL1 0x04 /* Interrupt control register 1 */ -/* - * Clock and timer registers when BS=0 - */ -#define CLK_SUBSECONDS 0x05 /* 1/100 seconds register */ -#define CLK_SECONDS 0x06 /* Seconds */ -#define CLK_MINUTES 0x07 /* Minutes */ -#define CLK_HOURS 0x08 /* Hours */ -#define CLK_DAY 0x09 /* Day of month */ -#define CLK_MONTH 0x0a /* Month */ -#define CLK_YEAR 0x0b /* Year */ -#define CLK_JULIAN_L 0x0c /* Lsb of Julian date */ -#define CLK_JULIAN_H 0x0d /* Msb of Julian date */ -#define CLK_WEEKDAY 0x0e /* Day of week */ -#define TIMER0_LSB 0x0f /* Timer 0 lsb */ -#define TIMER0_MSB 0x10 /* Timer 0 msb */ -#define TIMER1_LSB 0x11 /* Timer 1 lsb */ -#define TIMER1_MSB 0x12 /* Timer 1 msb */ -#define CMP_SECONDS 0x13 /* Seconds compare */ -#define CMP_MINUTES 0x14 /* Minutes compare */ -#define CMP_HOUR 0x15 /* Hours compare */ -#define CMP_DAY 0x16 /* Day of month compare */ -#define CMP_MONTH 0x17 /* Month compare */ -#define CMP_WEEKDAY 0x18 /* Day of week compare */ -#define SAVE_SECONDS 0x19 /* Seconds time save */ -#define SAVE_MINUTES 0x1a /* Minutes time save */ -#define SAVE_HOUR 0x1b /* Hours time save */ -#define SAVE_DAY 0x1c /* Day of month time save */ -#define SAVE_MONTH 0x1d /* Month time save */ -#define RAM_1E 0x1e /* Ram location 1e */ -#define RAM_1F 0x1f /* Ram location 1f */ -#define SIZE_DP857X 0x20 /* Size of dp address map */ - -#define DP_FIRSTTODREG CLK_SUBSECONDS -#define DP_LASTTODREG CLK_WEEKDAY - -typedef u_int dp_todregs[SIZE_DP857X]; -u_int dp857x_read(void *sc, u_int reg); -void dp857x_write(void *sc, u_int reg, u_int datum); - -/* - * Get all of the TOD/Alarm registers - * Must be called at splhigh(), and with the RTC properly set up. - */ -#define DP857X_GETTOD(sc, regs) \ - do { \ - int i; \ - \ - /* make sure clock regs are selected */ \ - dp857x_write(sc, MAIN_STATUS, 0); \ - /* try read until no rollover */ \ - do { \ - /* read all of the tod/alarm regs */ \ - for (i = DP_FIRSTTODREG; i < SIZE_DP857X; i++) \ - (*regs)[i] = dp857x_read(sc, i); \ - } while(dp857x_read(sc, PERIODIC_FLAGS) & 7); \ - } while (0); - -/* - * Set all of the TOD/Alarm registers - * Must be called at splhigh(), and with the RTC properly set up. - */ -#define DP857X_PUTTOD(sc, regs) \ - do { \ - int i; \ - \ - /* stop updates while setting, eg clear start bit */ \ - dp857x_write(sc, MAIN_STATUS, 0x40); \ - dp857x_write(sc, REAL_TIME_MODE, \ - dp857x_read(sc, REAL_TIME_MODE) & 0xF7); \ - \ - /* write all of the tod/alarm regs */ \ - for (i = DP_FIRSTTODREG; i <= DP_LASTTODREG; i++) \ - dp857x_write(sc, i, (*regs)[i]); \ - \ - /* reenable updates, eg set clock start bit */ \ - dp857x_write(sc, REAL_TIME_MODE, \ - dp857x_read(sc, REAL_TIME_MODE) | 0x08); \ - } while (0); - -#endif /*_DP857X_H*/ - diff --git a/sys/dev/ic/ds1286reg.h b/sys/dev/ic/ds1286reg.h new file mode 100644 index 00000000000..2b625fc181f --- /dev/null +++ b/sys/dev/ic/ds1286reg.h @@ -0,0 +1,182 @@ +/* $OpenBSD: ds1286reg.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: ds1286reg.h,v 1.8 2005/12/11 12:21:26 christos Exp $ */ + +/* + * Copyright (c) 2001 Rafal K. Boni + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Originally based on mc146818reg.h, with the following license: + * + * Copyright (c) 1995 Carnegie-Mellon University. + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software and + * its documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +/* + * Definitions for the Dallas Semiconductor DS1286/DS1386 Real Time Clock. + * + * Plucked right from the Dallas Semicomductor specs available at + * http://pdfserv.maxim-ic.com/arpdf/DS1286.pdf and + * http://pdfserv.maxim-ic.com/arpdf/DS1386-DS1386P.pdf + * + * The DS1286 and 1386 have 14 clock-related registers and some amount + * of user registers (50 for the 1286, 8K or 32K for the 1386). The + * first eleven registers contain time-of-day and alarm data, the rest + * contain various control bits and the watchdog timer functionality. + * + * Since the locations of these ports and the method used to access + * them can be machine-dependent, the low-level details of reading + * and writing the RTC's registers are handled by machine-specific + * functions. + * + * The DS1286/DS1386 chips always store time-of-day and alarm data in + * BCD. The "hour" time-of-year and alarm fields can either be stored + * in AM/PM format, or in 24-hour format. If AM/PM format is chosen, + * the hour fields can have the values: 1-12 (for AM) and 21-32 (for + * PM). If the 24-hour format is chosen, they can have the values 0 + * to 23. The hour format is selectable separately for the time and + * alarm fields, and is controller by bit 6 of the respective register. + */ + +/* + * The registers, and the bits within each register. + */ + +#define DS1286_SUBSEC 0x0 /* Time of year: hundredths of seconds (0-99) */ +#define DS1286_SEC 0x1 /* Time of year: seconds (0-59) */ +#define DS1286_MIN 0x2 /* Time of year: minutes (0-59) */ +#define DS1286_AMIN 0x3 /* Alarm: minutes */ +#define DS1286_HOUR 0x4 /* Time of year: hour (see above) */ + +#define DS1286_HOUR_12MODE 0x40 /* Hour mode: 12-hour (on), 24 (off) */ +#define DS1286_HOUR_12HR_PM 0x20 /* AM/PM in 12-hour mode: on = PM */ +#define DS1286_HOUR_12HR_MASK 0x1f /* Mask for hours in 12hour mode */ +#define DS1286_HOUR_24HR_MASK 0x3f /* Mask for hours in 24hour mode */ + +#define DS1286_AHOUR 0x5 /* Alarm: hour */ +#define DS1286_DOW 0x6 /* Time of year: day of week (1-7) */ +#define DS1286_ADOW 0x7 /* Alarm: day of week (1-7) */ +#define DS1286_DOM 0x8 /* Time of year: day of month (1-31) */ +#define DS1286_MONTH 0x9 /* Time of year: month (1-12), wave generator */ + +#define DS1286_MONTH_MASK 0x3f /* Mask to extract month */ +#define DS1286_WAVEGEN_MASK 0xc0 /* Mask to extract wave bits */ + +#define DS1286_YEAR 0xA /* Time of year: year in century (0-99) */ + +#define DS1286_CONTROL 0xB /* Control register A */ + +#define DS1286_TE 0x80 /* Update in progress (on == disable update) */ +#define DS1286_INTSWAP 0x40 /* Swap INTA, INTB outputs */ +#define DS1286_INTBSRC 0x20 /* INTB source (on) or sink (off) current */ +#define DS1286_INTAPLS 0x10 /* INTA pulse (on) or level (off) mode */ +#define DS1286_WAM 0x08 /* Watchdog alarm mask */ +#define DS1286_TDM 0x04 /* Time-of-day alarm mask */ +#define DS1286_WAF 0x02 /* Watchdog alarm flag */ +#define DS1286_TDF 0x01 /* Time-of-day alarm flag */ + +#define DS1286_NREGS 0xd /* 14 registers; CMOS follows */ +#define DS1286_NTODREGS 0xb /* 11 of those regs are for TOD and alarm */ + +#define DS1286_NVRAM_START 0xe /* start of NVRAM: offset 14 */ + +/* NVRAM size depends on the chip -- the 1286 only has 50 bytes, whereas + * the 1386 can have 8K or 32K + */ +#define DS1286_NVRAM_SIZE 50 /* 50 bytes of NVRAM */ + +/* + * RTC register/NVRAM read and write functions -- machine-dependent. + * Appropriately manipulate RTC registers to get/put data values. + */ +u_int ds1286_read(void *, u_int); +void ds1286_write(void *, u_int, u_int); + +/* + * A collection of TOD/Alarm registers. + */ +typedef u_int ds1286_todregs[DS1286_NTODREGS]; + +/* + * Get all of the TOD/Alarm registers + * Must be called at splhigh(), and with the RTC properly set up. + */ +#define DS1286_GETTOD(sc, regs) \ + do { \ + int i; \ + u_int ctl; \ + \ + /* turn off update for now */ \ + ctl = ds1286_read(sc, DS1286_CONTROL); \ + ds1286_write(sc, DS1286_CONTROL, ctl | DS1286_TE); \ + \ + /* read all of the tod/alarm regs */ \ + for (i = 0; i < DS1286_NTODREGS; i++) \ + (*regs)[i] = ds1286_read(sc, i); \ + \ + /* turn update back on */ \ + ds1286_write(sc, DS1286_CONTROL, ctl); \ + } while (0); + +/* + * Set all of the TOD/Alarm registers + * Must be called at splhigh(), and with the RTC properly set up. + */ +#define DS1286_PUTTOD(sc, regs) \ + do { \ + int i; \ + u_int ctl; \ + \ + /* turn off update for now */ \ + ctl = ds1286_read(sc, DS1286_CONTROL); \ + ds1286_write(sc, DS1286_CONTROL, ctl | DS1286_TE); \ + \ + /* write all of the tod/alarm regs */ \ + for (i = 0; i < DS1286_NTODREGS; i++) \ + ds1286_write(sc, i, (*regs)[i]); \ + \ + /* turn update back on */ \ + ds1286_write(sc, DS1286_CONTROL, ctl); \ + } while (0); diff --git a/sys/dev/ic/seeq8003reg.h b/sys/dev/ic/seeq8003reg.h new file mode 100644 index 00000000000..c0978c67508 --- /dev/null +++ b/sys/dev/ic/seeq8003reg.h @@ -0,0 +1,134 @@ +/* $OpenBSD: seeq8003reg.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: seeq8003reg.h,v 1.3 2001/06/07 05:19:26 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Soren S. Jorvang. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Register definitions for the Seeq 8003 and 80C03 ethernet controllers + * + * Based on documentation available at + * http://www.lsilogic.com/techlib/techdocs/networking/eol/80c03.pdf . + */ + +#define SEEQ_ADDR0 0 /* Station Address Byte 0 */ +#define SEEQ_ADDR1 1 /* Station Address Byte 1 */ +#define SEEQ_ADDR2 2 /* Station Address Byte 2 */ +#define SEEQ_ADDR3 3 /* Station Address Byte 3 */ +#define SEEQ_ADDR4 4 /* Station Address Byte 4 */ +#define SEEQ_ADDR5 5 /* Station Address Byte 5 */ + +#define SEEQ_TXCOLLS0 0 /* Transmit Collision Counter LSB */ +#define SEEQ_TXCOLLS1 1 /* Transmit Collision Counter MSB */ +#define SEEQ_ALLCOLL0 2 /* Total Collision Counter LSB */ +#define SEEQ_ALLCOLL1 3 /* Total Collision Counter MSB */ + +#define SEEQ_TEST 4 /* "For Test Only" - Do Not Use */ + +#define SEEQ_SQE 5 /* SQE / No Carrier */ +#define SQE_FLAG 0x01 /* SQE Flag */ +#define SQE_NOCARR 0x02 /* No Carrier Flag */ + +#define SEEQ_RXCMD 6 /* Rx Command */ +#define RXCMD_IE_OFLOW 0x01 /* Interrupt on Overflow Error */ +#define RXCMD_IE_CRC 0x02 /* Interrupt on CRC Error */ +#define RXCMD_IE_DRIB 0x04 /* Interrupt on Dribble Error */ +#define RXCMD_IE_SHORT 0x08 /* Interrupt on Short Frame */ +#define RXCMD_IE_END 0x10 /* Interrupt on End of Frame */ +#define RXCMD_IE_GOOD 0x20 /* Interrupt on Good Frame */ +#define RXCMD_REC_MASK 0xc0 /* Receiver Match Mode Mask */ +#define RXCMD_REC_NONE 0x00 /* Receiver Disabled */ +#define RXCMD_REC_ALL 0x40 /* Receive All Frames */ +#define RXCMD_REC_BROAD 0x80 /* Receive Station/Broadcast Frames */ +#define RXCMD_REC_MULTI 0xc0 /* Station/Broadcast/Multicast */ + +#define SEEQ_RXSTAT 6 /* Rx Status */ +#define RXSTAT_OFLOW 0x01 /* Frame Overflow Error */ +#define RXSTAT_CRC 0x02 /* Frame CRC Error */ +#define RXSTAT_DRIB 0x04 /* Frame Dribble Error */ +#define RXSTAT_SHORT 0x08 /* Received Short Frame */ +#define RXSTAT_END 0x10 /* Received End of Frame */ +#define RXSTAT_GOOD 0x20 /* Received Good Frame */ +#define RXSTAT_OLDNEW 0x80 /* Old/New Status */ + +#define SEEQ_TXCMD 7 /* Tx Command */ +#define TXCMD_IE_UFLOW 0x01 /* Interrupt on Transmit Underflow */ +#define TXCMD_IE_COLL 0x02 /* Interrupt on Transmit Collision */ +#define TXCMD_IE_16COLL 0x04 /* Interrupt on 16 Collisions */ +#define TXCMD_IE_GOOD 0x08 /* Interrupt on Transmit Succes */ +#define TXCMD_ENABLE_C 0xf0 /* (80C03) Enable 80C03 Mode */ +#define TXCMD_BANK_MASK 0x60 /* (80C03) Register Bank Mask */ +#define TXCMD_BANK0 0x00 /* (80C03) Register Bank 0 (8003) */ +#define TXCMD_BANK1 0x20 /* (80C03) Register Bank 1 (Writes) */ +#define TXCMD_BANK2 0x40 /* (80C03) Register Bank 2 (Writes) */ + +#define SEEQ_TXSTAT 7 /* Tx Status */ +#define TXSTAT_UFLOW 0x01 /* Transmit Underflow */ +#define TXSTAT_COLL 0x02 /* Transmit Collision */ +#define TXSTAT_16COLL 0x04 /* 16 Collisions */ +#define TXSTAT_GOOD 0x08 /* Transmit Success */ +#define TXSTAT_OLDNEW 0x80 /* Old/New Status */ + +/* + * 80C03 Mode Register Bank 1 + */ + +#define SEEQ_MC_HASH0 0 /* Multicast Filter Byte 0 (LSB) */ +#define SEEQ_MC_HASH1 1 /* Multicast Filter Byte 1 */ +#define SEEQ_MC_HASH2 2 /* Multicast Filter Byte 2 */ +#define SEEQ_MC_HASH3 3 /* Multicast Filter Byte 3 */ +#define SEEQ_MC_HASH4 4 /* Multicast Filter Byte 4 */ +#define SEEQ_MC_HASH5 5 /* Multicast Filter Byte 5 */ + +/* + * 80C03 Mode Register Bank 2 + */ + +#define SEEQ_MC_HASH6 0 /* Multicast Filter Byte 6 */ +#define SEEQ_MC_HASH7 1 /* Multicast Filter Byte 7 (MSB) */ + +#define SEEQ_RESERVED0 2 /* Reserved (Set to All Zeroes) */ + +#define SEEQ_TXCTRL 3 /* Tx Control */ +#define TXCTRL_TXCOLL 0x01 /* Clear/Enable Tx Collision Counter */ +#define TXCTRL_COLL 0x02 /* Clear/Enable Collision Counter */ +#define TXCTRL_SQE 0x04 /* Clear/Enable SQE Flag */ +#define TXCTRL_HASH 0x08 /* Enable Multicast Hash Filter */ +#define TXCTRL_SHORT 0x10 /* Receive Short (<13 Bytes) Frames */ +#define TXCTRL_NOCARR 0x20 /* Clear/Enable No Carrier Flag */ + +#define SEEQ_CFG 4 /* Transmit/Receive Configuration */ +#define CFG_RX_GRPADDR 0x01 /* Ignore Last 4 Bits of Address */ +#define CFG_TX_AUTOPAD 0x02 /* Automatically Pad to 60 Bytes */ +#define CFG_TX_NOPRE 0x04 /* Do Not Add Preamble Pattern */ +#define CFG_RX_NOOWN 0x08 /* Do Not Receive Own Packets */ +#define CFG_TX_NOCRC 0x10 /* No Not Append CRC */ +#define CFG_TX_DUPLEX 0x20 /* AutoDUPLEX - Ignore Carrier */ +#define CFG_RX_CRCFIFO 0x40 /* Write CRC to FIFO */ +#define CFG_RX_FASTDISC 0x80 /* Fast Receive Discard Mode */ + +#define SEEQ_RESERVED1 5 /* Reserved */ +#define SEEQ_RESERVED2 6 /* Reserved */ +#define SEEQ_RESERVED3 7 /* Reserved */ diff --git a/sys/dev/ic/wd33c93.c b/sys/dev/ic/wd33c93.c new file mode 100644 index 00000000000..2c6fabc6660 --- /dev/null +++ b/sys/dev/ic/wd33c93.c @@ -0,0 +1,2337 @@ +/* $OpenBSD: wd33c93.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: wd33c93.c,v 1.24 2010/11/13 13:52:02 uebayasi Exp $ */ + +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)scsi.c 7.5 (Berkeley) 5/4/91 + */ + +/* + * Changes Copyright (c) 2001 Wayne Knowles + * Changes Copyright (c) 1996 Steve Woodford + * Original Copyright (c) 1994 Christian E. Hopps + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)scsi.c 7.5 (Berkeley) 5/4/91 + */ + +/* + * This version of the driver is pretty well generic, so should work with + * any flavour of WD33C93 chip. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kernel.h> /* For hz */ +#include <sys/malloc.h> +#include <sys/pool.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <scsi/scsi_message.h> + +#include <machine/bus.h> + +#include <dev/ic/wd33c93reg.h> +#include <dev/ic/wd33c93var.h> + +/* + * SCSI delays + * In u-seconds, primarily for state changes on the SPC. + */ +#define SBIC_CMD_WAIT 200000 /* wait per step of 'immediate' cmds */ +#define SBIC_DATA_WAIT 200000 /* wait per data in/out step */ +#define SBIC_INIT_WAIT 200000 /* wait per step (both) during init */ + +#define STATUS_UNKNOWN 0xff /* uninitialized status */ + +/* + * Convenience macro for waiting for a particular wd33c93 event + */ +#define SBIC_WAIT(regs, until, timeo) wd33c93_wait(regs, until, timeo, __LINE__) + +void wd33c93_init(struct wd33c93_softc *); +void wd33c93_reset(struct wd33c93_softc *); +int wd33c93_go(struct wd33c93_softc *, struct wd33c93_acb *); +int wd33c93_dmaok(struct wd33c93_softc *, struct scsi_xfer *); +int wd33c93_wait(struct wd33c93_softc *, u_char, int , int); +u_char wd33c93_selectbus(struct wd33c93_softc *, struct wd33c93_acb *); +int wd33c93_xfout(struct wd33c93_softc *, int, void *); +int wd33c93_xfin(struct wd33c93_softc *, int, void *); +int wd33c93_poll(struct wd33c93_softc *, struct wd33c93_acb *); +int wd33c93_nextstate(struct wd33c93_softc *, struct wd33c93_acb *, + u_char, u_char); +int wd33c93_abort(struct wd33c93_softc *, struct wd33c93_acb *, + const char *); +void wd33c93_xferdone(struct wd33c93_softc *); +void wd33c93_error(struct wd33c93_softc *, struct wd33c93_acb *); +void wd33c93_scsidone(struct wd33c93_softc *, struct wd33c93_acb *, int); +void wd33c93_sched(struct wd33c93_softc *); +void wd33c93_dequeue(struct wd33c93_softc *, struct wd33c93_acb *); +void wd33c93_dma_stop(struct wd33c93_softc *); +void wd33c93_dma_setup(struct wd33c93_softc *, int); +int wd33c93_msgin_phase(struct wd33c93_softc *, int); +void wd33c93_msgin(struct wd33c93_softc *, u_char *, int); +void wd33c93_reselect(struct wd33c93_softc *, int, int, int, int); +void wd33c93_sched_msgout(struct wd33c93_softc *, u_short); +void wd33c93_msgout(struct wd33c93_softc *); +void wd33c93_timeout(void *arg); +void wd33c93_watchdog(void *arg); +u_char wd33c93_stp2syn(struct wd33c93_softc *, struct wd33c93_tinfo *); +void wd33c93_setsync(struct wd33c93_softc *, struct wd33c93_tinfo *); + +struct pool wd33c93_pool; /* Adapter Control Blocks */ +int wd33c93_pool_initialized = 0; + +/* + * Timeouts + */ +int wd33c93_cmd_wait = SBIC_CMD_WAIT; +int wd33c93_data_wait = SBIC_DATA_WAIT; +int wd33c93_init_wait = SBIC_INIT_WAIT; + +int wd33c93_nodma = 0; /* Use polled IO transfers */ +int wd33c93_nodisc = 0; /* Allow command queues */ +int wd33c93_notags = 0; /* No Tags */ + +/* + * Some useful stuff for debugging purposes + */ +#ifdef SBICDEBUG + +#define QPRINTF(a) SBIC_DEBUG(MISC, a) + +int wd33c93_debug = 0; /* Debug flags */ + +void wd33c93_print_csr (u_char); +void wd33c93_hexdump (u_char *, int); + +#else +#define QPRINTF(a) /* */ +#endif + +static const char *wd33c93_chip_names[] = SBIC_CHIP_LIST; + +/* + * Attach instance of driver and probe for sub devices + */ +void +wd33c93_attach(struct wd33c93_softc *sc, struct scsi_adapter *adapter) +{ + struct scsibus_attach_args saa; + + sc->sc_cfflags = sc->sc_dev.dv_cfdata->cf_flags; + timeout_set(&sc->sc_watchdog, wd33c93_watchdog, sc); + wd33c93_init(sc); + + printf(": %s, %d.%d MHz, %s\n", + wd33c93_chip_names[sc->sc_chip], + sc->sc_clkfreq / 10, sc->sc_clkfreq % 10, + (sc->sc_dmamode == SBIC_CTL_DMA) ? "DMA" : + (sc->sc_dmamode == SBIC_CTL_DBA_DMA) ? "DBA" : + (sc->sc_dmamode == SBIC_CTL_BURST_DMA) ? "burst DMA" : "PIO"); + if (sc->sc_chip == SBIC_CHIP_WD33C93B) { + printf("%s: microcode revision 0x%02x", + sc->sc_dev.dv_xname, sc->sc_rev); + if (sc->sc_minsyncperiod < 50) + printf(", fast SCSI"); + printf("\n"); + } + + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter_target = sc->sc_id; + sc->sc_link.adapter_buswidth = SBIC_NTARG; + sc->sc_link.adapter = adapter; + sc->sc_link.openings = 2; + sc->sc_link.luns = SBIC_NLUN; + + bzero(&saa, sizeof(saa)); + saa.saa_sc_link = &sc->sc_link; + + config_found(&sc->sc_dev, &saa, scsiprint); + timeout_add_sec(&sc->sc_watchdog, 60); +} + +/* + * Initialize driver-private structures + */ +void +wd33c93_init(struct wd33c93_softc *sc) +{ + u_int i; + + timeout_del(&sc->sc_watchdog); + + if (!wd33c93_pool_initialized) { + /* All instances share the same pool */ + pool_init(&wd33c93_pool, sizeof(struct wd33c93_acb), 0, 0, 0, + "wd33c93_acb", NULL); + ++wd33c93_pool_initialized; + } + + if (sc->sc_state == 0) { + TAILQ_INIT(&sc->ready_list); + + sc->sc_nexus = NULL; + sc->sc_disc = 0; + memset(sc->sc_tinfo, 0, sizeof(sc->sc_tinfo)); + } else { + /* XXX cancel all active commands */ + panic("wd33c93: reinitializing driver!"); + } + + sc->sc_flags = 0; + sc->sc_state = SBIC_IDLE; + wd33c93_reset(sc); + + for (i = 0; i < SBIC_NTARG; i++) { + struct wd33c93_tinfo *ti = &sc->sc_tinfo[i]; + /* + * cf_flags = 0xTTSSRR + * + * TT = Bitmask to disable Tagged Queues + * SS = Bitmask to disable Sync negotiation + * RR = Bitmask to disable disconnect/reselect + */ + ti->flags = T_NEED_RESET; + if (CFFLAGS_NOSYNC(sc->sc_cfflags, i)) + ti->flags |= T_NOSYNC; + if (CFFLAGS_NODISC(sc->sc_cfflags, i) || wd33c93_nodisc) + ti->flags |= T_NODISC; + ti->period = sc->sc_minsyncperiod; + ti->offset = 0; + } +} + +void +wd33c93_reset(struct wd33c93_softc *sc) +{ + u_int my_id, s, div, i; + u_char csr, reg; + + SET_SBIC_cmd(sc, SBIC_CMD_ABORT); + WAIT_CIP(sc); + + s = splbio(); + + if (sc->sc_reset != NULL) + (*sc->sc_reset)(sc); + + my_id = sc->sc_link.adapter_target & SBIC_ID_MASK; + + /* Enable advanced features and really(!) advanced features */ +#if 1 + my_id |= (SBIC_ID_EAF | SBIC_ID_RAF); /* XXX - MD Layer */ +#endif + + SET_SBIC_myid(sc, my_id); + + /* Reset the chip */ + SET_SBIC_cmd(sc, SBIC_CMD_RESET); + DELAY(25); + SBIC_WAIT(sc, SBIC_ASR_INT, 0); + + /* Set up various chip parameters */ + SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI); + + GET_SBIC_csr(sc, csr); /* clears interrupt also */ + GET_SBIC_cdb1(sc, sc->sc_rev); /* valid with RAF on wd33c93b */ + + switch (csr) { + case SBIC_CSR_RESET: + sc->sc_chip = SBIC_CHIP_WD33C93; + break; + case SBIC_CSR_RESET_AM: + SET_SBIC_queue_tag(sc, 0x55); + GET_SBIC_queue_tag(sc, reg); + sc->sc_chip = (reg == 0x55) ? + SBIC_CHIP_WD33C93B : SBIC_CHIP_WD33C93A; + SET_SBIC_queue_tag(sc, 0x0); + break; + default: + sc->sc_chip = SBIC_CHIP_UNKNOWN; + } + + /* + * Choose a suitable clock divisor and work out the resulting + * sync transfer periods in 4ns units. + */ + if (sc->sc_clkfreq < 110) { + my_id |= SBIC_ID_FS_8_10; + div = 2; + } else if (sc->sc_clkfreq < 160) { + my_id |= SBIC_ID_FS_12_15; + div = 3; + } else if (sc->sc_clkfreq < 210) { + my_id |= SBIC_ID_FS_16_20; + div = 4; + } else + panic("wd33c93: invalid clock speed %d", sc->sc_clkfreq); + + for (i = 0; i < 7; i++) + sc->sc_syncperiods[i] = + (i + 2) * div * 1250 / sc->sc_clkfreq; + sc->sc_minsyncperiod = sc->sc_syncperiods[0]; + SBIC_DEBUG(SYNC, ("available sync periods: %d %d %d %d %d %d %d\n", + sc->sc_syncperiods[0], sc->sc_syncperiods[1], + sc->sc_syncperiods[2], sc->sc_syncperiods[3], + sc->sc_syncperiods[4], sc->sc_syncperiods[5], + sc->sc_syncperiods[6])); + + if (sc->sc_clkfreq >= 160 && sc->sc_chip == SBIC_CHIP_WD33C93B) { + for (i = 0; i < 3; i++) + sc->sc_fsyncperiods[i] = + (i + 2) * 2 * 1250 / sc->sc_clkfreq; + SBIC_DEBUG(SYNC, ("available fast sync periods: %d %d %d\n", + sc->sc_fsyncperiods[0], sc->sc_fsyncperiods[1], + sc->sc_fsyncperiods[2])); + sc->sc_minsyncperiod = sc->sc_fsyncperiods[0]; + } + + /* Max Sync Offset */ + if (sc->sc_chip == SBIC_CHIP_WD33C93A || + sc->sc_chip == SBIC_CHIP_WD33C93B) + sc->sc_maxoffset = SBIC_SYN_93AB_MAX_OFFSET; + else + sc->sc_maxoffset = SBIC_SYN_93_MAX_OFFSET; + + /* + * don't allow Selection (SBIC_RID_ES) + * until we can handle target mode!! + */ + SET_SBIC_rselid(sc, SBIC_RID_ER); + + /* Asynchronous for now */ + SET_SBIC_syn(sc, 0); + + sc->sc_flags = 0; + sc->sc_state = SBIC_IDLE; + + splx(s); +} + +void +wd33c93_error(struct wd33c93_softc *sc, struct wd33c93_acb *acb) +{ + struct scsi_xfer *xs = acb->xs; + + KASSERT(xs); + + if (xs->flags & SCSI_SILENT) + return; + + sc_print_addr(xs->sc_link); + printf("SCSI Error\n"); +} + +/* + * Determine an appropriate value for the synchronous transfer register + * given the period and offset values in *ti. + */ +u_char +wd33c93_stp2syn(struct wd33c93_softc *sc, struct wd33c93_tinfo *ti) +{ + unsigned i; + + /* see if we can handle fast scsi (100-200ns) first */ + if (ti->period < 50 && sc->sc_minsyncperiod < 50) { + for (i = 0; i < 3; i++) + if (sc->sc_fsyncperiods[i] >= ti->period) + return (SBIC_SYN(ti->offset, i + 2, 1)); + } + + for (i = 0; i < 7; i++) { + if (sc->sc_syncperiods[i] >= ti->period) { + if (i == 6) + return (SBIC_SYN(0, 0, 0)); + else + return (SBIC_SYN(ti->offset, i + 2, 0)); + } + } + + /* XXX - can't handle it; do async */ + return (SBIC_SYN(0, 0, 0)); +} + +/* + * Setup sync mode for given target + */ +void +wd33c93_setsync(struct wd33c93_softc *sc, struct wd33c93_tinfo *ti) +{ + u_char syncreg; + + if (ti->flags & T_SYNCMODE) + syncreg = wd33c93_stp2syn(sc, ti); + else + syncreg = SBIC_SYN(0, 0, 0); + + SBIC_DEBUG(SYNC, ("wd33c93_setsync: sync reg = 0x%02x\n", syncreg)); + SET_SBIC_syn(sc, syncreg); +} + +/* + * Check if current operation can be done using DMA + * + * returns 1 if DMA OK, 0 for polled I/O transfer + */ +int +wd33c93_dmaok(struct wd33c93_softc *sc, struct scsi_xfer *xs) +{ + if (wd33c93_nodma || sc->sc_dmamode == SBIC_CTL_NO_DMA || + (xs->flags & SCSI_POLL) || xs->datalen == 0) + return (0); + return(1); +} + +/* + * Setup for DMA transfer + */ +void +wd33c93_dma_setup(struct wd33c93_softc *sc, int datain) +{ + struct wd33c93_acb *acb = sc->sc_nexus; + int s; + + sc->sc_daddr = acb->daddr; + sc->sc_dleft = acb->dleft; + + s = splbio(); + /* Indicate that we're in DMA mode */ + if (sc->sc_dleft) { + sc->sc_dmasetup(sc, &sc->sc_daddr, &sc->sc_dleft, + datain, &sc->sc_dleft); + } + splx(s); + return; +} + + +/* + * Save DMA pointers. Take into account partial transfer. Shut down DMA. + */ +void +wd33c93_dma_stop(struct wd33c93_softc *sc) +{ + size_t count; + int asr; + + /* Wait until WD chip is idle */ + do { + GET_SBIC_asr(sc, asr); /* XXX */ + if (asr & SBIC_ASR_DBR) { + printf("wd33c93_dma_stop: asr %02x canceled!\n", asr); + break; + } + } while (asr & (SBIC_ASR_BSY|SBIC_ASR_CIP)); + + /* Only need to save pointers if DMA was active */ + if (sc->sc_flags & SBICF_INDMA) { + int s = splbio(); + + /* Shut down DMA and flush FIFO's */ + sc->sc_dmastop(sc); + + /* Fetch the residual count */ + SBIC_TC_GET(sc, count); + + /* Work out how many bytes were actually transferred */ + count = sc->sc_tcnt - count; + + if (sc->sc_dleft < count) + printf("xfer too large: dleft=%zu resid=%zu\n", + sc->sc_dleft, count); + + /* Fixup partial xfers */ + sc->sc_daddr = (char *)sc->sc_daddr + count; + sc->sc_dleft -= count; + sc->sc_tcnt = 0; + sc->sc_flags &= ~SBICF_INDMA; + splx(s); + SBIC_DEBUG(DMA, ("dma_stop\n")); + } + /* + * Ensure the WD chip is back in polled I/O mode, with nothing to + * transfer. + */ + SBIC_TC_PUT(sc, 0); + SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI); +} + + +/* + * Handle new request from scsi layer + */ +void +wd33c93_scsi_cmd(struct scsi_xfer *xs) +{ + struct scsi_link *sc_link = xs->sc_link; + struct wd33c93_softc *sc = sc_link->adapter_softc; + struct wd33c93_acb *acb; + int flags, s; + + SBIC_DEBUG(MISC, ("wd33c93_scsi_cmd\n")); + + flags = xs->flags; + + if (sc->sc_nexus && (flags & SCSI_POLL)) + panic("wd33c93_scsicmd: busy"); + + s = splbio(); + acb = (struct wd33c93_acb *)pool_get(&wd33c93_pool, + PR_NOWAIT | PR_ZERO); + splx(s); + + if (acb == NULL) { + xs->error = XS_NO_CCB; + scsi_done(xs); + return; + } + + acb->flags = ACB_ACTIVE; + acb->xs = xs; + acb->timeout = xs->timeout; + timeout_set(&acb->to, wd33c93_timeout, acb); + + memcpy(&acb->cmd, xs->cmd, xs->cmdlen); + acb->clen = xs->cmdlen; + acb->daddr = xs->data; + acb->dleft = xs->datalen; + +#if 0 + if (flags & SCSI_POLL) { + /* + * Complete currently active command(s) before + * issuing an immediate command + */ + while (sc->sc_nexus) + wd33c93_poll(sc, sc->sc_nexus); + } +#endif + + s = splbio(); + TAILQ_INSERT_TAIL(&sc->ready_list, acb, chain); + acb->flags |= ACB_READY; + + /* If nothing is active, try to start it now. */ + if (sc->sc_state == SBIC_IDLE) + wd33c93_sched(sc); + splx(s); + + if ((flags & SCSI_POLL) == 0) + return; + + if (wd33c93_poll(sc, acb)) { + wd33c93_timeout(acb); + if (wd33c93_poll(sc, acb)) /* 2nd retry for ABORT */ + wd33c93_timeout(acb); + } +} + +/* + * attempt to start the next available command + */ +void +wd33c93_sched(struct wd33c93_softc *sc) +{ + struct scsi_link *sc_link; + struct wd33c93_acb *acb; + struct wd33c93_tinfo *ti; + struct wd33c93_linfo *li; + int lun, tag, flags; + int s; + + if (sc->sc_state != SBIC_IDLE) + return; + + KASSERT(sc->sc_nexus == NULL); + + /* Loop through the ready list looking for work to do... */ + TAILQ_FOREACH(acb, &sc->ready_list, chain) { + sc_link = acb->xs->sc_link; + lun = sc_link->lun; + ti = &sc->sc_tinfo[sc_link->target]; + + KASSERT(acb->flags & ACB_READY); + + /* Select type of tag for this command */ + if ((ti->flags & T_NODISC) != 0) + tag = 0; + else if ((ti->flags & T_TAG) == 0) + tag = 0; + else if ((acb->flags & ACB_SENSE) != 0) + tag = 0; + else if (acb->xs->flags & SCSI_POLL) + tag = 0; /* No tags for polled commands */ + else + tag = MSG_SIMPLE_Q_TAG; + + s = splbio(); + li = TINFO_LUN(ti, lun); + if (li == NULL) { + /* Initialize LUN info and add to list. */ + li = malloc(sizeof(*li), M_DEVBUF, M_NOWAIT | M_ZERO); + if (li == NULL) { + splx(s); + continue; + } + li->lun = lun; + if (lun < SBIC_NLUN) + ti->lun[lun] = li; + } + li->last_used = time_second; + + /* + * We've found a potential command, but is the target/lun busy? + */ + + if (tag == 0 && li->untagged == NULL) + li->untagged = acb; /* Issue untagged */ + + if (li->untagged != NULL) { + tag = 0; + if ((li->state != L_STATE_BUSY) && li->used == 0) { + /* Issue this untagged command now */ + acb = li->untagged; + sc_link = acb->xs->sc_link; + } else{ + /* Not ready yet */ + splx(s); + continue; + } + } + + acb->tag_type = tag; + if (tag != 0) { + int i; + + /* Allocate a tag */ + if (li->used == 255) { + /* no free tags */ + splx(s); + continue; + } + /* Start from the last used location */ + for (i = li->avail; i < 256; i++) { + if (li->queued[i] == NULL) + break; + } + /* Couldn't find one, start again from the beginning */ + if (i == 256) { + for (i = 0; i < 256; i++) { + if (li->queued[i] == NULL) + break; + } + } +#ifdef DIAGNOSTIC + if (i == 256) + panic("%s: tag alloc failure", __func__); +#endif + + /* Save where to start next time. */ + li->avail = i + 1; + li->used++; + li->queued[i] = acb; + acb->tag_id = i; + } + splx(s); + if (li->untagged != NULL && (li->state != L_STATE_BUSY)) { + li->state = L_STATE_BUSY; + break; + } + if (li->untagged == NULL && tag != 0) { + break; + } else + printf("%d:%d busy\n", sc_link->target, sc_link->lun); + } + + if (acb == NULL) { + SBIC_DEBUG(ACBS, ("wd33c93sched: no work\n")); + return; /* did not find an available command */ + } + + SBIC_DEBUG(ACBS, ("wd33c93_sched(%d,%d)\n", sc_link->target, + sc_link->lun)); + + TAILQ_REMOVE(&sc->ready_list, acb, chain); + acb->flags &= ~ACB_READY; + + flags = acb->xs->flags; + if (flags & SCSI_RESET) + wd33c93_reset(sc); + + /* XXX - Implicitly call scsidone on select timeout */ + if (wd33c93_go(sc, acb) != 0 || acb->xs->error == XS_SELTIMEOUT) { + acb->dleft = sc->sc_dleft; + wd33c93_scsidone(sc, acb, sc->sc_status); + return; + } +} + +void +wd33c93_scsidone(struct wd33c93_softc *sc, struct wd33c93_acb *acb, int status) +{ + struct scsi_xfer *xs = acb->xs; + struct scsi_link *sc_link = xs->sc_link; + struct wd33c93_tinfo *ti; + struct wd33c93_linfo *li; + int s; + +#ifdef DIAGNOSTIC + KASSERT(sc->target == sc_link->target); + KASSERT(sc->lun == sc_link->lun); + KASSERT(acb->flags != ACB_FREE); +#endif + + SBIC_DEBUG(ACBS, ("scsidone: (%d,%d)->(%d,%d)%02x\n", + sc_link->target, sc_link->lun, sc->target, sc->lun, status)); + + timeout_del(&acb->to); + + if (xs->error == XS_NOERROR) { + xs->status = status & SCSI_STATUS_MASK; + xs->resid = acb->dleft; + + switch (xs->status) { + case SCSI_CHECK: + case SCSI_TERMINATED: + /* XXX Need to read sense - return busy for now */ + /*FALLTHROUGH*/ + case SCSI_QUEUE_FULL: + case SCSI_BUSY: + xs->error = XS_BUSY; + break; + } + } + + ti = &sc->sc_tinfo[sc_link->target]; + li = TINFO_LUN(ti, sc_link->lun); + ti->cmds++; + if (xs->error == XS_SELTIMEOUT) { + /* Selection timeout -- discard this LUN if empty */ + if (li->untagged == NULL && li->used == 0) { + if (sc_link->lun < SBIC_NLUN) + ti->lun[sc_link->lun] = NULL; + free(li, M_DEVBUF); + } + } + + wd33c93_dequeue(sc, acb); + if (sc->sc_nexus == acb) { + sc->sc_state = SBIC_IDLE; + sc->sc_nexus = NULL; + sc->sc_flags = 0; + + if (!TAILQ_EMPTY(&sc->ready_list)) + wd33c93_sched(sc); + } + + /* place control block back on free list. */ + if ((xs->flags & SCSI_POLL) == 0) { + s = splbio(); + acb->flags = ACB_FREE; + pool_put(&wd33c93_pool, acb); + splx(s); + } + + scsi_done(xs); +} + +void +wd33c93_dequeue(struct wd33c93_softc *sc, struct wd33c93_acb *acb) +{ + struct wd33c93_tinfo *ti = &sc->sc_tinfo[acb->xs->sc_link->target]; + struct wd33c93_linfo *li; + int lun = acb->xs->sc_link->lun; + + li = TINFO_LUN(ti, lun); +#ifdef DIAGNOSTIC + if (li == NULL || li->lun != lun) + panic("wd33c93_dequeue: lun %d for ecb %p does not exist", + lun, acb); +#endif + if (li->untagged == acb) { + li->state = L_STATE_IDLE; + li->untagged = NULL; + } + if (acb->tag_type && li->queued[acb->tag_id] != NULL) { +#ifdef DIAGNOSTIC + if (li->queued[acb->tag_id] != NULL && + (li->queued[acb->tag_id] != acb)) + panic("wd33c93_dequeue: slot %d for lun %d has %p " + "instead of acb %p\n", acb->tag_id, + lun, li->queued[acb->tag_id], acb); +#endif + li->queued[acb->tag_id] = NULL; + li->used--; + } +} + + +int +wd33c93_wait(struct wd33c93_softc *sc, u_char until, int timeo, int line) +{ + u_char val; + + if (timeo == 0) + timeo = 1000000; /* some large value.. */ + GET_SBIC_asr(sc, val); + while ((val & until) == 0) { + if (timeo-- == 0) { + int csr; + GET_SBIC_csr(sc, csr); +#ifdef SBICDEBUG + printf("wd33c93_wait: TIMEO @%d with asr=0x%x csr=0x%x\n", + line, val, csr); +#ifdef DDB + Debugger(); +#endif +#endif + return(val); /* Maybe I should abort */ + break; + } + DELAY(1); + GET_SBIC_asr(sc, val); + } + return(val); +} + +int +wd33c93_abort(struct wd33c93_softc *sc, struct wd33c93_acb *acb, + const char *where) +{ + u_char csr, asr; + + GET_SBIC_asr(sc, asr); + GET_SBIC_csr(sc, csr); + + sc_print_addr(acb->xs->sc_link); + printf("ABORT in %s: csr=0x%02x, asr=0x%02x\n", where, csr, asr); + + acb->timeout = SBIC_ABORT_TIMEOUT; + acb->flags |= ACB_ABORT; + + /* + * Clean up chip itself + */ + if (sc->sc_nexus == acb) { + /* Reschedule timeout. */ + timeout_add_msec(&acb->to, acb->timeout); + + while (asr & SBIC_ASR_DBR) { + /* + * wd33c93 is jammed w/data. need to clear it + * But we don't know what direction it needs to go + */ + GET_SBIC_data(sc, asr); + printf("abort %s: clearing data buffer 0x%02x\n", + where, asr); + GET_SBIC_asr(sc, asr); + if (asr & SBIC_ASR_DBR) /* Not the read direction */ + SET_SBIC_data(sc, asr); + GET_SBIC_asr(sc, asr); + } + + sc_print_addr(acb->xs->sc_link); + printf("sending ABORT command\n"); + + WAIT_CIP(sc); + SET_SBIC_cmd(sc, SBIC_CMD_ABORT); + WAIT_CIP(sc); + + GET_SBIC_asr(sc, asr); + + sc_print_addr(acb->xs->sc_link); + if (asr & (SBIC_ASR_BSY|SBIC_ASR_LCI)) { + /* + * ok, get more drastic.. + */ + printf("Resetting bus\n"); + wd33c93_reset(sc); + } else { + printf("sending DISCONNECT to target\n"); + SET_SBIC_cmd(sc, SBIC_CMD_DISC); + WAIT_CIP(sc); + + do { + SBIC_WAIT (sc, SBIC_ASR_INT, 0); + GET_SBIC_asr(sc, asr); + GET_SBIC_csr(sc, csr); + SBIC_DEBUG(MISC, ("csr: 0x%02x, asr: 0x%02x\n", + csr, asr)); + } while ((csr != SBIC_CSR_DISC) && + (csr != SBIC_CSR_DISC_1) && + (csr != SBIC_CSR_CMD_INVALID)); + } + sc->sc_state = SBIC_ERROR; + sc->sc_flags = 0; + } + return SBIC_STATE_ERROR; +} + + +/* + * select the bus, return when selected or error. + * + * Returns the current CSR following selection and optionally MSG out phase. + * i.e. the returned CSR *should* indicate CMD phase... + * If the return value is 0, some error happened. + */ +u_char +wd33c93_selectbus(struct wd33c93_softc *sc, struct wd33c93_acb *acb) +{ + struct scsi_xfer *xs = acb->xs; + struct scsi_link *sc_link = xs->sc_link; + struct wd33c93_tinfo *ti; + u_char target, lun, asr, csr, id; + + KASSERT(sc->sc_state == SBIC_IDLE); + + target = sc_link->target; + lun = sc_link->lun; + ti = &sc->sc_tinfo[target]; + + sc->sc_state = SBIC_SELECTING; + sc->target = target; + sc->lun = lun; + + SBIC_DEBUG(PHASE, ("wd33c93_selectbus %d: ", target)); + + if ((xs->flags & SCSI_POLL) == 0) + timeout_add_msec(&acb->to, acb->timeout); + + /* + * issue select + */ + SBIC_TC_PUT(sc, 0); + SET_SBIC_selid(sc, target); + SET_SBIC_timeo(sc, SBIC_TIMEOUT(250, sc->sc_clkfreq)); + + GET_SBIC_asr(sc, asr); + if (asr & (SBIC_ASR_INT|SBIC_ASR_BSY)) { + /* This means we got ourselves reselected upon */ + SBIC_DEBUG(PHASE, ("WD busy (reselect?) ASR=%02x\n", asr)); + return 0; + } + + SET_SBIC_cmd(sc, SBIC_CMD_SEL_ATN); + WAIT_CIP(sc); + + /* + * wait for select (merged from separate function may need + * cleanup) + */ + do { + asr = SBIC_WAIT(sc, SBIC_ASR_INT | SBIC_ASR_LCI, 0); + if (asr & SBIC_ASR_LCI) { + QPRINTF(("late LCI: asr %02x\n", asr)); + return 0; + } + + /* Clear interrupt */ + GET_SBIC_csr (sc, csr); + + /* Reselected from under our feet? */ + if (csr == SBIC_CSR_RSLT_NI || csr == SBIC_CSR_RSLT_IFY) { + SBIC_DEBUG(PHASE, ("got reselected, asr %02x\n", asr)); + /* + * We need to handle this now so we don't lock up later + */ + wd33c93_nextstate(sc, acb, csr, asr); + return 0; + } + + /* Whoops! */ + if (csr == SBIC_CSR_SLT || csr == SBIC_CSR_SLT_ATN) { + panic("wd33c93_selectbus: target issued select!"); + return 0; + } + + } while (csr != (SBIC_CSR_MIS_2 | MESG_OUT_PHASE) && + csr != (SBIC_CSR_MIS_2 | CMD_PHASE) && + csr != SBIC_CSR_SEL_TIMEO); + + /* Anyone at home? */ + if (csr == SBIC_CSR_SEL_TIMEO) { + xs->error = XS_SELTIMEOUT; + SBIC_DEBUG(PHASE, ("-- Selection Timeout\n")); + return 0; + } + + SBIC_DEBUG(PHASE, ("Selection Complete\n")); + + /* Assume we're now selected */ + GET_SBIC_selid(sc, id); + if (id != target) { + /* Something went wrong - wrong target was select */ + printf("wd33c93_selectbus: wrong target selected;" + " WANTED %d GOT %d", target, id); + return 0; /* XXX: Need to call nexstate to handle? */ + } + + sc->sc_flags |= SBICF_SELECTED; + sc->sc_state = SBIC_CONNECTED; + + /* setup correct sync mode for this target */ + wd33c93_setsync(sc, ti); + + if (ti->flags & T_NODISC && sc->sc_disc == 0) + SET_SBIC_rselid (sc, 0); /* Not expecting a reselect */ + else + SET_SBIC_rselid (sc, SBIC_RID_ER); + + /* + * We only really need to do anything when the target goes to MSG out + * If the device ignored ATN, it's probably old and brain-dead, + * but we'll try to support it anyhow. + * If it doesn't support message out, it definately doesn't + * support synchronous transfers, so no point in even asking... + */ + if (csr == (SBIC_CSR_MIS_2 | MESG_OUT_PHASE)) { + if (ti->flags & T_NEGOTIATE) { + /* Initiate a SDTR message */ + SBIC_DEBUG(SYNC, ("Sending SDTR to target %d\n", id)); + if (ti->flags & T_WANTSYNC) { + ti->period = sc->sc_minsyncperiod; + ti->offset = sc->sc_maxoffset; + } else { + ti->period = 0; + ti->offset = 0; + } + /* Send Sync negotiation message */ + sc->sc_omsg[0] = MSG_IDENTIFY(lun, 0); /* No Disc */ + sc->sc_omsg[1] = MSG_EXTENDED; + sc->sc_omsg[2] = MSG_EXT_SDTR_LEN; + sc->sc_omsg[3] = MSG_EXT_SDTR; + if (ti->flags & T_WANTSYNC) { + sc->sc_omsg[4] = sc->sc_minsyncperiod; + sc->sc_omsg[5] = sc->sc_maxoffset; + } else { + sc->sc_omsg[4] = 0; + sc->sc_omsg[5] = 0; + } + wd33c93_xfout(sc, 6, sc->sc_omsg); + sc->sc_msgout |= SEND_SDTR; /* may be rejected */ + sc->sc_flags |= SBICF_SYNCNEGO; + } else { + if (sc->sc_nexus->tag_type != 0) { + /* Use TAGS */ + SBIC_DEBUG(TAGS, ("<select %d:%d TAG=%x>\n", + sc->target, sc->lun, + sc->sc_nexus->tag_id)); + sc->sc_omsg[0] = MSG_IDENTIFY(lun, 1); + sc->sc_omsg[1] = sc->sc_nexus->tag_type; + sc->sc_omsg[2] = sc->sc_nexus->tag_id; + wd33c93_xfout(sc, 3, sc->sc_omsg); + sc->sc_msgout |= SEND_TAG; + } else { + int no_disc; + + /* Setup LUN nexus and disconnect privilege */ + no_disc = xs->flags & SCSI_POLL || + ti->flags & T_NODISC; + SEND_BYTE(sc, MSG_IDENTIFY(lun, !no_disc)); + } + } + /* + * There's one interrupt still to come: + * the change to CMD phase... + */ + SBIC_WAIT(sc, SBIC_ASR_INT , 0); + GET_SBIC_csr(sc, csr); + } + + return csr; +} + +/* + * Information Transfer *to* a SCSI Target. + * + * Note: Don't expect there to be an interrupt immediately after all + * the data is transferred out. The WD spec sheet says that the Transfer- + * Info command for non-MSG_IN phases only completes when the target + * next asserts 'REQ'. That is, when the SCSI bus changes to a new state. + * + * This can have a nasty effect on commands which take a relatively long + * time to complete, for example a START/STOP unit command may remain in + * CMD phase until the disk has spun up. Only then will the target change + * to STATUS phase. This is really only a problem for immediate commands + * since we don't allow disconnection for them (yet). + */ +int +wd33c93_xfout(struct wd33c93_softc *sc, int len, void *bp) +{ + int wait = wd33c93_data_wait; + u_char asr, *buf = bp; + + QPRINTF(("wd33c93_xfout {%d} %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x\n", len, buf[0], buf[1], buf[2], + buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9])); + + /* + * sigh.. WD-PROTO strikes again.. sending the command in one go + * causes the chip to lock up if talking to certain (misbehaving?) + * targets. Anyway, this procedure should work for all targets, but + * it's slightly slower due to the overhead + */ + + SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI); + SBIC_TC_PUT (sc, (unsigned)len); + + WAIT_CIP (sc); + SET_SBIC_cmd (sc, SBIC_CMD_XFER_INFO); + + /* + * Loop for each byte transferred + */ + do { + GET_SBIC_asr (sc, asr); + + if (asr & SBIC_ASR_DBR) { + if (len) { + SET_SBIC_data (sc, *buf); + buf++; + len--; + } else { + SET_SBIC_data (sc, 0); + } + wait = wd33c93_data_wait; + } + } while (len && (asr & SBIC_ASR_INT) == 0 && wait-- > 0); + + QPRINTF(("wd33c93_xfout done: %d bytes remaining (wait:%d)\n", len, wait)); + + /* + * Normally, an interrupt will be pending when this routing returns. + */ + return(len); +} + +/* + * Information Transfer *from* a Scsi Target + * returns # bytes left to read + */ +int +wd33c93_xfin(struct wd33c93_softc *sc, int len, void *bp) +{ + int wait = wd33c93_data_wait; + u_char *buf = bp; + u_char asr; +#ifdef SBICDEBUG + u_char *obp = bp; +#endif + SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI); + SBIC_TC_PUT (sc, (unsigned)len); + + WAIT_CIP (sc); + SET_SBIC_cmd (sc, SBIC_CMD_XFER_INFO); + + /* + * Loop for each byte transferred + */ + do { + GET_SBIC_asr (sc, asr); + + if (asr & SBIC_ASR_DBR) { + if (len) { + GET_SBIC_data (sc, *buf); + buf++; + len--; + } else { + u_char foo; + GET_SBIC_data (sc, foo); + } + wait = wd33c93_data_wait; + } + + } while ((asr & SBIC_ASR_INT) == 0 && wait-- > 0); + + QPRINTF(("wd33c93_xfin {%d} %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x\n", len, obp[0], obp[1], obp[2], + obp[3], obp[4], obp[5], obp[6], obp[7], obp[8], obp[9])); + + SBIC_TC_PUT (sc, 0); + + /* + * this leaves with one csr to be read + */ + return len; +} + + +/* + * Finish SCSI xfer command: After the completion interrupt from + * a read/write operation, sequence through the final phases in + * programmed i/o. + */ +void +wd33c93_xferdone(struct wd33c93_softc *sc) +{ + u_char phase, csr; + int s; + + QPRINTF(("{")); + s = splbio(); + + /* + * have the wd33c93 complete on its own + */ + SBIC_TC_PUT(sc, 0); + SET_SBIC_cmd_phase(sc, 0x46); + SET_SBIC_cmd(sc, SBIC_CMD_SEL_ATN_XFER); + + do { + SBIC_WAIT (sc, SBIC_ASR_INT, 0); + GET_SBIC_csr (sc, csr); + QPRINTF(("%02x:", csr)); + } while ((csr != SBIC_CSR_DISC) && + (csr != SBIC_CSR_DISC_1) && + (csr != SBIC_CSR_S_XFERRED)); + + sc->sc_flags &= ~SBICF_SELECTED; + sc->sc_state = SBIC_DISCONNECT; + + GET_SBIC_cmd_phase (sc, phase); + QPRINTF(("}%02x", phase)); + + if (phase == 0x60) + GET_SBIC_tlun(sc, sc->sc_status); + else + wd33c93_error(sc, sc->sc_nexus); + + QPRINTF(("=STS:%02x=\n", sc->sc_status)); + splx(s); +} + + +int +wd33c93_go(struct wd33c93_softc *sc, struct wd33c93_acb *acb) +{ + struct scsi_xfer *xs = acb->xs; + struct scsi_link *sc_link = xs->sc_link; + int i, dmaok; + u_char csr, asr; + + SBIC_DEBUG(ACBS, ("wd33c93_go(%d:%d)\n", sc_link->target, sc_link->lun)); + + sc->sc_nexus = acb; + + sc->target = sc_link->target; + sc->lun = sc_link->lun; + + sc->sc_status = STATUS_UNKNOWN; + sc->sc_daddr = acb->daddr; + sc->sc_dleft = acb->dleft; + + sc->sc_msgpriq = sc->sc_msgout = sc->sc_msgoutq = 0; + sc->sc_flags = 0; + + dmaok = wd33c93_dmaok(sc, xs); + + if (dmaok == 0) + sc->sc_flags |= SBICF_NODMA; + + SBIC_DEBUG(DMA, ("wd33c93_go dmago:%d(tcnt=%zx) dmaok=%d\n", + sc->target, sc->sc_tcnt, dmaok)); + + /* select the SCSI bus (it's an error if bus isn't free) */ + if ((csr = wd33c93_selectbus(sc, acb)) == 0) + return(0); /* Not done: needs to be rescheduled */ + + /* + * Lets cycle a while then let the interrupt handler take over. + */ + GET_SBIC_asr(sc, asr); + do { + QPRINTF(("go[0x%x] ", csr)); + + /* Handle the new phase */ + i = wd33c93_nextstate(sc, acb, csr, asr); + WAIT_CIP(sc); /* XXX */ + if (sc->sc_state == SBIC_CONNECTED) { + + GET_SBIC_asr(sc, asr); + + if (asr & SBIC_ASR_LCI) + printf("wd33c93_go: LCI asr:%02x csr:%02x\n", asr, csr); + + if (asr & SBIC_ASR_INT) + GET_SBIC_csr(sc, csr); + } + + } while (sc->sc_state == SBIC_CONNECTED && + asr & (SBIC_ASR_INT|SBIC_ASR_LCI)); + + QPRINTF(("> done i=%d stat=%02x\n", i, sc->sc_status)); + + if (i == SBIC_STATE_DONE) { + if (sc->sc_status == STATUS_UNKNOWN) { + printf("wd33c93_go: done & stat == UNKNOWN\n"); + return 1; /* Did we really finish that fast? */ + } + } + return 0; +} + + +int +wd33c93_intr(void *v) +{ + struct wd33c93_softc *sc = v; + u_char asr, csr; + int i; + + /* + * pending interrupt? + */ + GET_SBIC_asr (sc, asr); + if ((asr & SBIC_ASR_INT) == 0) + return(0); + + GET_SBIC_csr(sc, csr); + + do { + SBIC_DEBUG(INTS, ("intr[csr=0x%x]", csr)); + + i = wd33c93_nextstate(sc, sc->sc_nexus, csr, asr); + WAIT_CIP(sc); /* XXX */ + if (sc->sc_state == SBIC_CONNECTED) { + GET_SBIC_asr(sc, asr); + + if (asr & SBIC_ASR_LCI) + printf("wd33c93_intr: LCI asr:%02x csr:%02x\n", + asr, csr); + + if (asr & SBIC_ASR_INT) + GET_SBIC_csr(sc, csr); + } + } while (sc->sc_state == SBIC_CONNECTED && + asr & (SBIC_ASR_INT|SBIC_ASR_LCI)); + + SBIC_DEBUG(INTS, ("intr done. state=%d, asr=0x%02x\n", i, asr)); + + return(1); +} + +/* + * Complete current command using polled I/O. Used when interrupt driven + * I/O is not allowed (ie. during boot and shutdown) + * + * Polled I/O is very processor intensive + */ +int +wd33c93_poll(struct wd33c93_softc *sc, struct wd33c93_acb *acb) +{ + u_char asr, csr=0; + int i, count; + struct scsi_xfer *xs = acb->xs; + int s; + + SBIC_WAIT(sc, SBIC_ASR_INT, wd33c93_cmd_wait); + for (count = acb->timeout; count;) { + GET_SBIC_asr(sc, asr); + if (asr & SBIC_ASR_LCI) + printf("wd33c93_poll: LCI; asr:%02x csr:%02x\n", + asr, csr); + if (asr & SBIC_ASR_INT) { + GET_SBIC_csr(sc, csr); + sc->sc_flags |= SBICF_NODMA; + i = wd33c93_nextstate(sc, sc->sc_nexus, csr, asr); + WAIT_CIP(sc); /* XXX */ + } else { + DELAY(1000); + count--; + } + + if ((xs->flags & ITSDONE) != 0) { + s = splbio(); + acb->flags = ACB_FREE; + pool_put(&wd33c93_pool, acb); + splx(s); + + return (0); + } + + if (sc->sc_state == SBIC_IDLE) { + SBIC_DEBUG(ACBS, ("[poll: rescheduling] ")); + wd33c93_sched(sc); + } + } + return (1); +} + +static inline int +__verify_msg_format(u_char *p, int len) +{ + if (len == 1 && IS1BYTEMSG(p[0])) + return 1; + if (len == 2 && IS2BYTEMSG(p[0])) + return 1; + if (len >= 3 && ISEXTMSG(p[0]) && + len == p[1] + 2) + return 1; + return 0; +} + +/* + * Handle message_in phase + */ +int +wd33c93_msgin_phase(struct wd33c93_softc *sc, int reselect) +{ + int len; + u_char asr, csr, *msg; + + GET_SBIC_asr(sc, asr); + + SBIC_DEBUG(MSGS, ("wd33c93msgin asr=%02x\n", asr)); + + GET_SBIC_selid (sc, csr); + SET_SBIC_selid (sc, csr | SBIC_SID_FROM_SCSI); + + SBIC_TC_PUT(sc, 0); + + SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI); + + msg = sc->sc_imsg; + len = 0; + + do { + /* Fetch the next byte of the message */ + RECV_BYTE(sc, *msg++); + len++; + + /* + * get the command completion interrupt, or we + * can't send a new command (LCI) + */ + SBIC_WAIT(sc, SBIC_ASR_INT, 0); + GET_SBIC_csr(sc, csr); + + if (__verify_msg_format(sc->sc_imsg, len)) + break; /* Complete message received */ + + /* + * Clear ACK, and wait for the interrupt + * for the next byte or phase change + */ + SET_SBIC_cmd(sc, SBIC_CMD_CLR_ACK); + SBIC_WAIT(sc, SBIC_ASR_INT, 0); + + GET_SBIC_csr(sc, csr); + } while (len < SBIC_MAX_MSGLEN); + + if (__verify_msg_format(sc->sc_imsg, len)) + wd33c93_msgin(sc, sc->sc_imsg, len); + + /* + * Clear ACK, and wait for the interrupt + * for the phase change + */ + SET_SBIC_cmd(sc, SBIC_CMD_CLR_ACK); + SBIC_WAIT(sc, SBIC_ASR_INT, 0); + + /* Should still have one CSR to read */ + return SBIC_STATE_RUNNING; +} + + +void wd33c93_msgin(struct wd33c93_softc *sc, u_char *msgaddr, int msglen) +{ + struct wd33c93_acb *acb = sc->sc_nexus; + struct wd33c93_tinfo *ti = &sc->sc_tinfo[sc->target]; + struct wd33c93_linfo *li; + u_char asr; + + switch (sc->sc_state) { + case SBIC_CONNECTED: + switch (msgaddr[0]) { + case MSG_MESSAGE_REJECT: + SBIC_DEBUG(MSGS, ("msgin: MSG_REJECT, " + "last msgout=%x\n", sc->sc_msgout)); + switch (sc->sc_msgout) { + case SEND_TAG: + printf("%s: tagged queuing rejected: " + "target %d\n", + sc->sc_dev.dv_xname, sc->target); + ti->flags &= ~T_TAG; + li = TINFO_LUN(ti, sc->lun); + if (acb->tag_type && + li->queued[acb->tag_id] != NULL) { + li->queued[acb->tag_id] = NULL; + li->used--; + } + acb->tag_type = acb->tag_id = 0; + li->untagged = acb; + li->state = L_STATE_BUSY; + break; + + case SEND_SDTR: + printf("%s: sync transfer rejected: target %d\n", + sc->sc_dev.dv_xname, sc->target); + + sc->sc_flags &= ~SBICF_SYNCNEGO; + ti->flags &= ~(T_NEGOTIATE | T_SYNCMODE); + wd33c93_setsync(sc, ti); + + case SEND_INIT_DET_ERR: + goto abort; + + default: + SBIC_DEBUG(MSGS, ("Unexpected MSG_REJECT\n")); + break; + } + sc->sc_msgout = 0; + break; + + case MSG_HEAD_OF_Q_TAG: + case MSG_ORDERED_Q_TAG: + case MSG_SIMPLE_Q_TAG: + printf("-- Out of phase TAG;" + "Nexus=%d:%d Tag=%02x/%02x\n", + sc->target, sc->lun, msgaddr[0], msgaddr[1]); + break; + + case MSG_DISCONNECT: + SBIC_DEBUG(MSGS, ("msgin: DISCONNECT")); + /* + * Mark the fact that all bytes have moved. The + * target may not bother to do a SAVE POINTERS + * at this stage. This flag will set the residual + * count to zero on MSG COMPLETE. + */ + if (sc->sc_dleft == 0) + acb->flags |= ACB_COMPLETE; + + if (acb->xs->flags & SCSI_POLL) + /* Don't allow disconnect in immediate mode */ + goto reject; + else { /* Allow disconnect */ + sc->sc_flags &= ~SBICF_SELECTED; + sc->sc_state = SBIC_DISCONNECT; + } + if ((acb->xs->sc_link->quirks & SDEV_AUTOSAVE) == 0) + break; + /*FALLTHROUGH*/ + + case MSG_SAVEDATAPOINTER: + SBIC_DEBUG(MSGS, ("msgin: SAVEDATAPTR")); + acb->daddr = sc->sc_daddr; + acb->dleft = sc->sc_dleft; + break; + + case MSG_RESTOREPOINTERS: + SBIC_DEBUG(MSGS, ("msgin: RESTOREPTR")); + sc->sc_daddr = acb->daddr; + sc->sc_dleft = acb->dleft; + break; + + case MSG_CMDCOMPLETE: + /* + * !! KLUDGE ALERT !! quite a few drives don't seem to + * really like the current way of sending the + * sync-handshake together with the ident-message, and + * they react by sending command-complete and + * disconnecting right after returning the valid sync + * handshake. So, all I can do is reselect the drive, + * and hope it won't disconnect again. I don't think + * this is valid behavior, but I can't help fixing a + * problem that apparently exists. + * + * Note: we should not get here on `normal' command + * completion, as that condition is handled by the + * high-level sel&xfer resume command used to walk + * thru status/cc-phase. + */ + SBIC_DEBUG(MSGS, ("msgin: CMD_COMPLETE")); + SBIC_DEBUG(SYNC, ("GOT MSG %d! target %d" + " acting weird.." + " waiting for disconnect...\n", + msgaddr[0], sc->target)); + + /* Check to see if wd33c93 is handling this */ + GET_SBIC_asr(sc, asr); + if (asr & SBIC_ASR_BSY) + break; + + /* XXX: Assume it works and set status to 00 */ + sc->sc_status = 0; + sc->sc_state = SBIC_CMDCOMPLETE; + break; + + case MSG_EXTENDED: + switch(msgaddr[2]) { + case MSG_EXT_SDTR: /* Sync negotiation */ + SBIC_DEBUG(MSGS, ("msgin: EXT_SDTR; " + "period %d, offset %d", + msgaddr[3], msgaddr[4])); + if (msgaddr[1] != 3) + goto reject; + + ti->period = + MAX(msgaddr[3], sc->sc_minsyncperiod); + ti->offset = MIN(msgaddr[4], sc->sc_maxoffset); + + /* + * <SGI, IBM DORS-32160, WA6A> will do nothing + * but attempt sync negotiation until it gets + * what it wants. To avoid an infinite loop set + * off by the identify request, oblige them. + */ + if ((sc->sc_flags&SBICF_SYNCNEGO) == 0 && + msgaddr[3] != 0) + ti->flags |= T_WANTSYNC; + + if (!(ti->flags & T_WANTSYNC)) + ti->period = ti->offset = 0; + + ti->flags &= ~T_NEGOTIATE; + + if (ti->offset == 0) + ti->flags &= ~T_SYNCMODE; /* Async */ + else + ti->flags |= T_SYNCMODE; /* Sync */ + + /* target initiated negotiation */ + if ((sc->sc_flags&SBICF_SYNCNEGO) == 0) + wd33c93_sched_msgout(sc, SEND_SDTR); + sc->sc_flags &= ~SBICF_SYNCNEGO; + + SBIC_DEBUG(SYNC, ("msgin(%d): SDTR(o=%d,p=%d)", + sc->target, ti->offset, + ti->period)); + wd33c93_setsync(sc, ti); + break; + + case MSG_EXT_WDTR: + SBIC_DEBUG(MSGS, ("msgin: EXT_WDTR rejected")); + goto reject; + + default: + sc_print_addr(acb->xs->sc_link); + printf("unrecognized MESSAGE EXTENDED;" + " sending REJECT\n"); + goto reject; + } + break; + + default: + sc_print_addr(acb->xs->sc_link); + printf("unrecognized MESSAGE; sending REJECT\n"); + + reject: + /* We don't support whatever this message is... */ + wd33c93_sched_msgout(sc, SEND_REJECT); + break; + } + break; + + case SBIC_IDENTIFIED: + /* + * IDENTIFY message was received and queue tag is expected now + */ + if ((msgaddr[0]!=MSG_SIMPLE_Q_TAG) || (sc->sc_msgify==0)) { + printf("%s: TAG reselect without IDENTIFY;" + " MSG %x; sending DEVICE RESET\n", + sc->sc_dev.dv_xname, msgaddr[0]); + goto reset; + } + SBIC_DEBUG(TAGS, ("TAG %x/%x\n", msgaddr[0], msgaddr[1])); + if (sc->sc_nexus) + printf("*TAG Recv with active nexus!!\n"); + wd33c93_reselect(sc, sc->target, sc->lun, + msgaddr[0], msgaddr[1]); + break; + + case SBIC_RESELECTED: + /* + * IDENTIFY message with target + */ + if (MSG_ISIDENTIFY(msgaddr[0])) { + SBIC_DEBUG(PHASE, ("IFFY[%x] ", msgaddr[0])); + sc->sc_msgify = msgaddr[0]; + } else { + printf("%s: reselect without IDENTIFY;" + " MSG %x;" + " sending DEVICE RESET\n", + sc->sc_dev.dv_xname, msgaddr[0]); + goto reset; + } + break; + + default: + printf("%s: unexpected MESSAGE IN. State=%d - Sending RESET\n", + sc->sc_dev.dv_xname, sc->sc_state); + reset: + wd33c93_sched_msgout(sc, SEND_DEV_RESET); + break; + abort: + wd33c93_sched_msgout(sc, SEND_ABORT); + break; + } +} + +void +wd33c93_sched_msgout(struct wd33c93_softc *sc, u_short msg) +{ + u_char asr; + + SBIC_DEBUG(SYNC,("sched_msgout: %04x\n", msg)); + sc->sc_msgpriq |= msg; + + /* Schedule MSGOUT Phase to send message */ + + WAIT_CIP(sc); + SET_SBIC_cmd(sc, SBIC_CMD_SET_ATN); + WAIT_CIP(sc); + GET_SBIC_asr(sc, asr); + if (asr & SBIC_ASR_LCI) { + printf("MSGOUT Failed!\n"); + } + SET_SBIC_cmd(sc, SBIC_CMD_CLR_ACK); + WAIT_CIP(sc); +} + +/* + * Send the highest priority, scheduled message + */ +void +wd33c93_msgout(struct wd33c93_softc *sc) +{ + struct wd33c93_tinfo *ti; + struct wd33c93_acb *acb = sc->sc_nexus; + + if (acb == NULL) + panic("MSGOUT with no nexus"); + + if (sc->sc_omsglen == 0) { + /* Pick up highest priority message */ + sc->sc_msgout = sc->sc_msgpriq & -sc->sc_msgpriq; + sc->sc_msgoutq |= sc->sc_msgout; + sc->sc_msgpriq &= ~sc->sc_msgout; + sc->sc_omsglen = 1; /* "Default" message len */ + switch (sc->sc_msgout) { + case SEND_SDTR: + ti = &sc->sc_tinfo[acb->xs->sc_link->target]; + sc->sc_omsg[0] = MSG_EXTENDED; + sc->sc_omsg[1] = MSG_EXT_SDTR_LEN; + sc->sc_omsg[2] = MSG_EXT_SDTR; + if (ti->flags & T_WANTSYNC) { + sc->sc_omsg[3] = ti->period; + sc->sc_omsg[4] = ti->offset; + } else { + sc->sc_omsg[3] = 0; + sc->sc_omsg[4] = 0; + } + sc->sc_omsglen = 5; + if ((sc->sc_flags & SBICF_SYNCNEGO) == 0) { + if (ti->flags & T_WANTSYNC) + ti->flags |= T_SYNCMODE; + else + ti->flags &= ~T_SYNCMODE; + wd33c93_setsync(sc, ti); + } + break; + case SEND_IDENTIFY: + if (sc->sc_state != SBIC_CONNECTED) { + printf("%s at line %d: no nexus\n", + sc->sc_dev.dv_xname, __LINE__); + } + sc->sc_omsg[0] = + MSG_IDENTIFY(acb->xs->sc_link->lun, 0); + break; + case SEND_TAG: + if (sc->sc_state != SBIC_CONNECTED) { + printf("%s at line %d: no nexus\n", + sc->sc_dev.dv_xname, __LINE__); + } + sc->sc_omsg[0] = acb->tag_type; + sc->sc_omsg[1] = acb->tag_id; + sc->sc_omsglen = 2; + break; + case SEND_DEV_RESET: + sc->sc_omsg[0] = MSG_BUS_DEV_RESET; + ti = &sc->sc_tinfo[sc->target]; + ti->flags &= ~T_SYNCMODE; + if ((ti->flags & T_NOSYNC) == 0) + /* We can re-start sync negotiation */ + ti->flags |= T_NEGOTIATE; + break; + case SEND_PARITY_ERROR: + sc->sc_omsg[0] = MSG_PARITY_ERROR; + break; + case SEND_ABORT: + sc->sc_flags |= SBICF_ABORTING; + sc->sc_omsg[0] = MSG_ABORT; + break; + case SEND_INIT_DET_ERR: + sc->sc_omsg[0] = MSG_INITIATOR_DET_ERR; + break; + case SEND_REJECT: + sc->sc_omsg[0] = MSG_MESSAGE_REJECT; + break; + default: + /* Wasn't expecting MSGOUT Phase */ + sc->sc_omsg[0] = MSG_NOOP; + break; + } + } + + wd33c93_xfout(sc, sc->sc_omsglen, sc->sc_omsg); +} + + +/* + * wd33c93_nextstate() + * return: + * SBIC_STATE_DONE == done + * SBIC_STATE_RUNNING == working + * SBIC_STATE_DISCONNECT == disconnected + * SBIC_STATE_ERROR == error + */ +int +wd33c93_nextstate(struct wd33c93_softc *sc, struct wd33c93_acb *acb, u_char csr, u_char asr) +{ + SBIC_DEBUG(PHASE, ("next[a=%02x,c=%02x]: ",asr,csr)); + + switch (csr) { + + case SBIC_CSR_XFERRED | CMD_PHASE: + case SBIC_CSR_MIS | CMD_PHASE: + case SBIC_CSR_MIS_1 | CMD_PHASE: + case SBIC_CSR_MIS_2 | CMD_PHASE: + + if (wd33c93_xfout(sc, acb->clen, &acb->cmd)) + goto abort; + break; + + case SBIC_CSR_XFERRED | STATUS_PHASE: + case SBIC_CSR_MIS | STATUS_PHASE: + case SBIC_CSR_MIS_1 | STATUS_PHASE: + case SBIC_CSR_MIS_2 | STATUS_PHASE: + + SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI); + + /* + * this should be the normal i/o completion case. + * get the status & cmd complete msg then let the + * device driver look at what happened. + */ + wd33c93_xferdone(sc); + + wd33c93_dma_stop(sc); + + /* Fixup byte count to be passed to higher layer */ + acb->dleft = (acb->flags & ACB_COMPLETE) ? 0 : + sc->sc_dleft; + + /* + * Indicate to the upper layers that the command is done + */ + wd33c93_scsidone(sc, acb, sc->sc_status); + + return SBIC_STATE_DONE; + + + case SBIC_CSR_XFERRED | DATA_IN_PHASE: + case SBIC_CSR_MIS | DATA_IN_PHASE: + case SBIC_CSR_MIS_1 | DATA_IN_PHASE: + case SBIC_CSR_MIS_2 | DATA_IN_PHASE: + case SBIC_CSR_XFERRED | DATA_OUT_PHASE: + case SBIC_CSR_MIS | DATA_OUT_PHASE: + case SBIC_CSR_MIS_1 | DATA_OUT_PHASE: + case SBIC_CSR_MIS_2 | DATA_OUT_PHASE: + /* + * Verify that we expected to transfer data... + */ + if (acb->dleft <= 0) { + printf("next: DATA phase with xfer count == %zd, asr:0x%02x csr:0x%02x\n", + acb->dleft, asr, csr); + goto abort; + } + + /* + * Should we transfer using PIO or DMA ? + */ + if (acb->xs->flags & SCSI_POLL || + sc->sc_flags & SBICF_NODMA) { + /* Perfrom transfer using PIO */ + int resid; + + SBIC_DEBUG(DMA, ("PIO xfer: %d(%p:%zx)\n", sc->target, + sc->sc_daddr, sc->sc_dleft)); + + if (SBIC_PHASE(csr) == DATA_IN_PHASE) + /* data in */ + resid = wd33c93_xfin(sc, sc->sc_dleft, + sc->sc_daddr); + else /* data out */ + resid = wd33c93_xfout(sc, sc->sc_dleft, + sc->sc_daddr); + + sc->sc_daddr = (char *)sc->sc_daddr + + (acb->dleft - resid); + sc->sc_dleft = resid; + } else { + int datain = SBIC_PHASE(csr) == DATA_IN_PHASE; + + /* Perform transfer using DMA */ + wd33c93_dma_setup(sc, datain); + + SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI | + sc->sc_dmamode); + + SBIC_DEBUG(DMA, ("DMA xfer: %d(%p:%zx)\n", sc->target, + sc->sc_daddr, sc->sc_dleft)); + + /* Setup byte count for transfer */ + SBIC_TC_PUT(sc, (unsigned)sc->sc_dleft); + + /* Start the transfer */ + SET_SBIC_cmd(sc, SBIC_CMD_XFER_INFO); + + /* Start the DMA chip going */ + sc->sc_tcnt = sc->sc_dmago(sc); + + /* Indicate that we're in DMA mode */ + sc->sc_flags |= SBICF_INDMA; + } + break; + + case SBIC_CSR_XFERRED | MESG_IN_PHASE: + case SBIC_CSR_MIS | MESG_IN_PHASE: + case SBIC_CSR_MIS_1 | MESG_IN_PHASE: + case SBIC_CSR_MIS_2 | MESG_IN_PHASE: + + wd33c93_dma_stop(sc); + + /* Handle a single message in... */ + return wd33c93_msgin_phase(sc, 0); + + case SBIC_CSR_MSGIN_W_ACK: + + /* + * We should never see this since it's handled in + * 'wd33c93_msgin_phase()' but just for the sake of paranoia... + */ + SET_SBIC_cmd(sc, SBIC_CMD_CLR_ACK); + + printf("Acking unknown msgin CSR:%02x",csr); + break; + + case SBIC_CSR_XFERRED | MESG_OUT_PHASE: + case SBIC_CSR_MIS | MESG_OUT_PHASE: + case SBIC_CSR_MIS_1 | MESG_OUT_PHASE: + case SBIC_CSR_MIS_2 | MESG_OUT_PHASE: + + /* + * Message out phase. ATN signal has been asserted + */ + wd33c93_dma_stop(sc); + wd33c93_msgout(sc); + return SBIC_STATE_RUNNING; + + case SBIC_CSR_DISC: + case SBIC_CSR_DISC_1: + SBIC_DEBUG(RSEL, ("wd33c93next target %d disconnected\n", + sc->target)); + wd33c93_dma_stop(sc); + + sc->sc_nexus = NULL; + sc->sc_state = SBIC_IDLE; + sc->sc_flags = 0; + + ++sc->sc_tinfo[sc->target].dconns; + ++sc->sc_disc; + + if (acb->xs->flags & SCSI_POLL || wd33c93_nodisc) + return SBIC_STATE_DISCONNECT; + + /* Try to schedule another target */ + wd33c93_sched(sc); + + return SBIC_STATE_DISCONNECT; + + case SBIC_CSR_RSLT_NI: + case SBIC_CSR_RSLT_IFY: + { + /* + * A reselection. + * Note that since we don't enable Advanced Features (assuming + * the WD chip is at least the 'A' revision), we're only ever + * likely to see the 'SBIC_CSR_RSLT_NI' status. But for the + * hell of it, we'll handle it anyway, for all the extra code + * it needs... + */ + u_char newtarget, newlun; + + if (sc->sc_flags & SBICF_INDMA) { + printf("**** RESELECT WHILE DMA ACTIVE!!! ***\n"); + wd33c93_dma_stop(sc); + } + + sc->sc_state = SBIC_RESELECTED; + GET_SBIC_rselid(sc, newtarget); + + /* check SBIC_RID_SIV? */ + newtarget &= SBIC_RID_MASK; + + if (csr == SBIC_CSR_RSLT_IFY) { + /* Read Identify msg to avoid lockup */ + GET_SBIC_data(sc, newlun); + WAIT_CIP(sc); + newlun &= SBIC_TLUN_MASK; + sc->sc_msgify = MSG_IDENTIFY(newlun, 0); + } else { + /* + * Need to read Identify message the hard way, assuming + * the target even sends us one... + */ + for (newlun = 255; newlun; --newlun) { + GET_SBIC_asr(sc, asr); + if (asr & SBIC_ASR_INT) + break; + DELAY(10); + } + + /* If we didn't get an interrupt, somethink's up */ + if ((asr & SBIC_ASR_INT) == 0) { + printf("%s: Reselect without identify? asr %x\n", + sc->sc_dev.dv_xname, asr); + newlun = 0; /* XXXX */ + } else { + /* + * We got an interrupt, verify that it's a + * change to message in phase, and if so + * read the message. + */ + GET_SBIC_csr(sc,csr); + + if (csr == (SBIC_CSR_MIS | MESG_IN_PHASE) || + csr == (SBIC_CSR_MIS_1 | MESG_IN_PHASE) || + csr == (SBIC_CSR_MIS_2 | MESG_IN_PHASE)) { + /* + * Yup, gone to message in. + * Fetch the target LUN + */ + sc->sc_msgify = 0; + wd33c93_msgin_phase(sc, 1); + newlun = sc->sc_msgify & SBIC_TLUN_MASK; + } else { + /* + * Whoops! Target didn't go to msg_in + * phase!! + */ + printf("RSLT_NI - not MESG_IN_PHASE %x\n", csr); + newlun = 0; /* XXXSCW */ + } + } + } + + /* Ok, we have the identity of the reselecting target. */ + SBIC_DEBUG(RSEL, ("wd33c93next: reselect from targ %d lun %d", + newtarget, newlun)); + wd33c93_reselect(sc, newtarget, newlun, 0, 0); + sc->sc_disc--; + + if (csr == SBIC_CSR_RSLT_IFY) + SET_SBIC_cmd(sc, SBIC_CMD_CLR_ACK); + break; + } + + default: + abort: + /* Something unexpected happend -- deal with it. */ + printf("next: aborting asr 0x%02x csr 0x%02x\n", asr, csr); + +#ifdef DDB + Debugger(); +#endif + + SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI); + if (acb->xs) + wd33c93_error(sc, acb); + wd33c93_abort(sc, acb, "next"); + + if (sc->sc_flags & SBICF_INDMA) { + wd33c93_dma_stop(sc); + wd33c93_scsidone(sc, acb, STATUS_UNKNOWN); + } + return SBIC_STATE_ERROR; + } + return SBIC_STATE_RUNNING; +} + + +void +wd33c93_reselect(struct wd33c93_softc *sc, int target, int lun, int tag_type, int tag_id) +{ + + struct wd33c93_tinfo *ti; + struct wd33c93_linfo *li; + struct wd33c93_acb *acb; + + if (sc->sc_nexus) { + /* + * Whoops! We've been reselected with a + * command in progress! + * The best we can do is to put the current + * command back on the ready list and hope + * for the best. + */ + SBIC_DEBUG(RSEL, ("%s: reselect with active command\n", + sc->sc_dev.dv_xname)); + ti = &sc->sc_tinfo[sc->target]; + li = TINFO_LUN(ti, sc->lun); + li->state = L_STATE_IDLE; + + wd33c93_dequeue(sc, sc->sc_nexus); + TAILQ_INSERT_HEAD(&sc->ready_list, sc->sc_nexus, chain); + sc->sc_nexus->flags |= ACB_READY; + + sc->sc_nexus = NULL; + } + + /* Setup state for new nexus */ + acb = NULL; + sc->sc_flags = SBICF_SELECTED; + sc->sc_msgpriq = sc->sc_msgout = sc->sc_msgoutq = 0; + + ti = &sc->sc_tinfo[target]; + li = TINFO_LUN(ti, lun); + + if (li != NULL) { + if (li->untagged != NULL && li->state) + acb = li->untagged; + else if (tag_type != MSG_SIMPLE_Q_TAG) { + /* Wait for tag to come by during MESG_IN Phase */ + sc->target = target; /* setup I_T_L nexus */ + sc->lun = lun; + sc->sc_state = SBIC_IDENTIFIED; + return; + } else if (tag_type) + acb = li->queued[tag_id]; + } + + if (acb == NULL) { + printf("%s: reselect from target %d lun %d tag %x:%x " + "with no nexus; sending ABORT\n", + sc->sc_dev.dv_xname, target, lun, tag_type, tag_id); + goto abort; + } + + sc->target = target; + sc->lun = lun; + sc->sc_nexus = acb; + sc->sc_state = SBIC_CONNECTED; + + if (!wd33c93_dmaok(sc, acb->xs)) + sc->sc_flags |= SBICF_NODMA; + + /* Do an implicit RESTORE POINTERS. */ + sc->sc_daddr = acb->daddr; + sc->sc_dleft = acb->dleft; + + /* Set sync modes for new target */ + wd33c93_setsync(sc, ti); + + if (acb->flags & ACB_RESET) + wd33c93_sched_msgout(sc, SEND_DEV_RESET); + else if (acb->flags & ACB_ABORT) + wd33c93_sched_msgout(sc, SEND_ABORT); + return; + +abort: + wd33c93_sched_msgout(sc, SEND_ABORT); + return; + +} + +void +wd33c93_timeout(void *arg) +{ + struct wd33c93_acb *acb = arg; + struct scsi_xfer *xs = acb->xs; + struct scsi_link *sc_link = xs->sc_link; + struct wd33c93_softc *sc = sc_link->adapter_softc; + int s, asr; + + s = splbio(); + + GET_SBIC_asr(sc, asr); + + sc_print_addr(sc_link); + printf("%s: timed out; asr=0x%02x [acb %p (flags 0x%x, dleft %zx)], " + "<state %d, nexus %p, resid %lx, msg(q %x,o %x)>", + sc->sc_dev.dv_xname, asr, acb, acb->flags, acb->dleft, + sc->sc_state, sc->sc_nexus, (long)sc->sc_dleft, + sc->sc_msgpriq, sc->sc_msgout); + + if (asr & SBIC_ASR_INT) { + /* We need to service a missed IRQ */ + wd33c93_intr(sc); + } else { + (void) wd33c93_abort(sc, sc->sc_nexus, "timeout"); + } + splx(s); +} + + +void +wd33c93_watchdog(void *arg) +{ + struct wd33c93_softc *sc = arg; + struct wd33c93_tinfo *ti; + struct wd33c93_linfo *li; + int t, s, l; + /* scrub LUN's that have not been used in the last 10min. */ + time_t old = time_second - (10 * 60); + + for (t = 0; t < SBIC_NTARG; t++) { + ti = &sc->sc_tinfo[t]; + for (l = 0; l < SBIC_NLUN; l++) { + s = splbio(); + li = TINFO_LUN(ti, l); + if (li && li->last_used < old && + li->untagged == NULL && li->used == 0) { + ti->lun[li->lun] = NULL; + free(li, M_DEVBUF); + } + splx(s); + } + } + timeout_add_sec(&sc->sc_watchdog, 60); +} + + +#ifdef SBICDEBUG +void +wd33c93_hexdump(u_char *buf, int len) +{ + printf("{%d}:", len); + while (len--) + printf(" %02x", *buf++); + printf("\n"); +} + + +void +wd33c93_print_csr(u_char csr) +{ + switch (SCSI_PHASE(csr)) { + case CMD_PHASE: + printf("CMD_PHASE\n"); + break; + + case STATUS_PHASE: + printf("STATUS_PHASE\n"); + break; + + case DATA_IN_PHASE: + printf("DATAIN_PHASE\n"); + break; + + case DATA_OUT_PHASE: + printf("DATAOUT_PHASE\n"); + break; + + case MESG_IN_PHASE: + printf("MESG_IN_PHASE\n"); + break; + + case MESG_OUT_PHASE: + printf("MESG_OUT_PHASE\n"); + break; + + default: + switch (csr) { + case SBIC_CSR_DISC_1: + printf("DISC_1\n"); + break; + + case SBIC_CSR_RSLT_NI: + printf("RESELECT_NO_IFY\n"); + break; + + case SBIC_CSR_RSLT_IFY: + printf("RESELECT_IFY\n"); + break; + + case SBIC_CSR_SLT: + printf("SELECT\n"); + break; + + case SBIC_CSR_SLT_ATN: + printf("SELECT, ATN\n"); + break; + + case SBIC_CSR_UNK_GROUP: + printf("UNK_GROUP\n"); + break; + + default: + printf("UNKNOWN csr=%02x\n", csr); + } + } +} +#endif diff --git a/sys/dev/ic/wd33c93reg.h b/sys/dev/ic/wd33c93reg.h new file mode 100644 index 00000000000..e1f16ad11f9 --- /dev/null +++ b/sys/dev/ic/wd33c93reg.h @@ -0,0 +1,510 @@ +/* $OpenBSD: wd33c93reg.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: wd33c93reg.h,v 1.4 2009/02/12 06:24:45 rumble Exp $ */ + +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)scsireg.h 7.3 (Berkeley) 2/5/91 + */ + +/* + * Copyright (c) 2001 Wayne Knowles + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)scsireg.h 7.3 (Berkeley) 2/5/91 + */ + +/* + * WD33C93 SCSI interface hardware description. + * + * Using parts of the Mach scsi driver for the 33C93 + */ + +#define SBIC_myid 0 +#define SBIC_cdbsize 0 +#define SBIC_control 1 +#define SBIC_timeo 2 +#define SBIC_cdb1 3 +#define SBIC_tsecs 3 +#define SBIC_cdb2 4 +#define SBIC_theads 4 +#define SBIC_cdb3 5 +#define SBIC_tcyl_hi 5 +#define SBIC_cdb4 6 +#define SBIC_tcyl_lo 6 +#define SBIC_cdb5 7 +#define SBIC_addr_hi 7 +#define SBIC_cdb6 8 +#define SBIC_addr_2 8 +#define SBIC_cdb7 9 +#define SBIC_addr_3 9 +#define SBIC_cdb8 10 +#define SBIC_addr_lo 10 +#define SBIC_cdb9 11 +#define SBIC_secno 11 +#define SBIC_cdb10 12 +#define SBIC_headno 12 +#define SBIC_cdb11 13 +#define SBIC_cylno_hi 13 +#define SBIC_cdb12 14 +#define SBIC_cylno_lo 14 +#define SBIC_tlun 15 +#define SBIC_cmd_phase 16 +#define SBIC_syn 17 +#define SBIC_count_hi 18 +#define SBIC_count_med 19 +#define SBIC_count_lo 20 +#define SBIC_selid 21 +#define SBIC_rselid 22 +#define SBIC_csr 23 +#define SBIC_cmd 24 +#define SBIC_data 25 +#define SBIC_queue_tag 26 +#define SBIC_aux_status 27 + +/* wd33c93_asr is addressed directly */ + +/* + * Register defines + */ + +/* + * Auxiliary Status Register + */ + +#define SBIC_ASR_INT 0x80 /* Interrupt pending */ +#define SBIC_ASR_LCI 0x40 /* Last command ignored */ +#define SBIC_ASR_BSY 0x20 /* Busy, only cmd/data/asr readable */ +#define SBIC_ASR_CIP 0x10 /* Busy, cmd unavail also */ +#define SBIC_ASR_xxx 0x0c +#define SBIC_ASR_PE 0x02 /* Parity error (even) */ +#define SBIC_ASR_DBR 0x01 /* Data Buffer Ready */ + +/* + * My ID register, and/or CDB Size + */ + +#define SBIC_ID_FS_8_10 0x00 /* Input clock is 8-10 MHz */ + /* 11 MHz is invalid */ +#define SBIC_ID_FS_12_15 0x40 /* Input clock is 12-15 MHz */ +#define SBIC_ID_FS_16_20 0x80 /* Input clock is 16-20 MHz */ +#define SBIC_ID_RAF 0x20 /* Enable Really Advanced Features */ +#define SBIC_ID_EHP 0x10 /* Enable host parity */ +#define SBIC_ID_EAF 0x08 /* Enable Advanced Features */ +#define SBIC_ID_MASK 0x07 +#define SBIC_ID_CBDSIZE_MASK 0x0f /* if unk SCSI cmd group */ + +/* + * Control register + */ + +#define SBIC_CTL_DMA 0x80 /* Single byte dma */ +#define SBIC_CTL_DBA_DMA 0x40 /* direct buffer access (bus master) */ +#define SBIC_CTL_BURST_DMA 0x20 /* continuous mode (8237) */ +#define SBIC_CTL_NO_DMA 0x00 /* Programmed I/O */ +#define SBIC_CTL_HHP 0x10 /* Halt on host parity error */ +#define SBIC_CTL_EDI 0x08 /* Ending disconnect interrupt */ +#define SBIC_CTL_IDI 0x04 /* Intermediate disconnect interrupt*/ +#define SBIC_CTL_HA 0x02 /* Halt on ATN */ +#define SBIC_CTL_HSP 0x01 /* Halt on SCSI parity error */ + +/* + * Timeout period register + * [val in msecs, input clk in 0.1 MHz] + */ + +#define SBIC_TIMEOUT(val,clk) ((((val) * (clk)) / 800) + 1) + +/* + * CDBn registers, note that + * cdb11 is used for status byte in target mode (send-status-and-cc) + * cdb12 sez if linked command complete, and w/flag if so + */ + +/* + * Target LUN register + * [holds target status when select-and-xfer] + */ + +#define SBIC_TLUN_VALID 0x80 /* did we receive an Identify msg */ +#define SBIC_TLUN_DOK 0x40 /* Disconnect OK */ +#define SBIC_TLUN_xxx 0x38 +#define SBIC_TLUN_MASK 0x07 + +/* + * Command Phase register + */ + +#define SBIC_CPH_MASK 0x7f /* values/restarts are cmd specific */ +#define SBIC_CPH(p) ((p) & SBIC_CPH_MASK) + +/* + * FIFO register + */ + +#define SBIC_FIFO_93_DEPTH 5 +#define SBIC_FIFO_93AB_DEPTH 12 + +/* + * maximum possible size in TC registers. Since this is 24 bit, it's easy + */ +#define SBIC_TC_MAX ((1 << 24) - 1) + +/* + * Synchronous xfer register + * + * NB: SBIC_SYN_FSS only valid on WD33C93B with 16-20MHz clock. + */ + +#define SBIC_SYN_OFF_MASK 0x0f +#define SBIC_SYN_93_MAX_OFFSET (SBIC_FIFO_93_DEPTH - 1) /* 4 is recommended */ +#define SBIC_SYN_93AB_MAX_OFFSET SBIC_FIFO_93AB_DEPTH +#define SBIC_SYN_PER_MASK 0x70 +#define SBIC_SYN_MIN_PERIOD 2 /* upto 8, encoded as 0 */ +#define SBIC_SYN_FSS 0x80 /* Enable Fast SCSI Transfers (10MB/s)*/ + +#define SBIC_SYN(o,p,f) \ + (((o) & SBIC_SYN_OFF_MASK) | (((p) << 4) & SBIC_SYN_PER_MASK) | \ + ((f) ? SBIC_SYN_FSS : 0)) + +/* + * Transfer count register + * optimal access macros depend on addressing + */ + +/* + * Destination ID (selid) register + */ + +#define SBIC_SID_SCC 0x80 /* Select command chaining (tgt) */ +#define SBIC_SID_DPD 0x40 /* Data phase direction (inittor) */ +#define SBIC_SID_FROM_SCSI 0x40 +#define SBIC_SID_TO_SCSI 0x00 +#define SBIC_SID_xxx 0x38 +#define SBIC_SID_IDMASK 0x07 + +/* + * Source ID (rselid) register + */ + +#define SBIC_RID_ER 0x80 /* Enable reselection */ +#define SBIC_RID_ES 0x40 /* Enable selection */ +#define SBIC_RID_DSP 0x20 /* Disable select parity */ +#define SBIC_RID_SIV 0x08 /* Source ID valid */ +#define SBIC_RID_MASK 0x07 + +/* + * Status register + */ + +#define SBIC_CSR_CAUSE 0xf0 +#define SBIC_CSR_RESET 0x00 /* chip was reset */ +#define SBIC_CSR_CMD_DONE 0x10 /* cmd completed */ +#define SBIC_CSR_CMD_STOPPED 0x20 /* interrupted or abrted*/ +#define SBIC_CSR_CMD_ERR 0x40 /* end with error */ +#define SBIC_CSR_BUS_SERVICE 0x80 /* REQ pending on the bus */ + + +#define SBIC_CSR_QUALIFIER 0x0f +/* Reset State Interrupts */ +#define SBIC_CSR_RESET 0x00 /* reset w/advanced features*/ +#define SBIC_CSR_RESET_AM 0x01 /* reset w/advanced features*/ +/* Successful Completion Interrupts */ +#define SBIC_CSR_TARGET 0x10 /* reselect complete */ +#define SBIC_CSR_INITIATOR 0x11 /* select complete */ +#define SBIC_CSR_WO_ATN 0x13 /* tgt mode completion */ +#define SBIC_CSR_W_ATN 0x14 /* ditto */ +#define SBIC_CSR_XLATED 0x15 /* translate address cmd */ +#define SBIC_CSR_S_XFERRED 0x16 /* initiator mode completion*/ +#define SBIC_CSR_XFERRED 0x18 /* phase in low bits */ +/* Paused or Aborted Interrupts */ +#define SBIC_CSR_MSGIN_W_ACK 0x20 /* (I) msgin, ACK asserted*/ +#define SBIC_CSR_SDP 0x21 /* (I) SDP msg received */ +#define SBIC_CSR_SEL_ABRT 0x22 /* sel/resel aborted */ +#define SBIC_CSR_XFR_PAUSED 0x23 /* (T) no ATN */ +#define SBIC_CSR_XFR_PAUSED_ATN 0x24 /* (T) ATN is asserted */ +#define SBIC_CSR_RSLT_AM 0x27 /* (I) lost selection (AM) */ +#define SBIC_CSR_MIS 0x28 /* (I) xfer aborted, ph mis */ +/* Terminated Interrupts */ +#define SBIC_CSR_CMD_INVALID 0x40 +#define SBIC_CSR_DISC 0x41 /* (I) tgt disconnected */ +#define SBIC_CSR_SEL_TIMEO 0x42 +#define SBIC_CSR_PE 0x43 /* parity error */ +#define SBIC_CSR_PE_ATN 0x44 /* ditto, ATN is asserted */ +#define SBIC_CSR_XLATE_TOOBIG 0x45 +#define SBIC_CSR_RSLT_NOAM 0x46 /* (I) lost sel, no AM mode */ +#define SBIC_CSR_BAD_STATUS 0x47 /* status byte was nok */ +#define SBIC_CSR_MIS_1 0x48 /* ph mis, see low bits */ +/* Service Required Interrupts */ +#define SBIC_CSR_RSLT_NI 0x80 /* reselected, no ify msg */ +#define SBIC_CSR_RSLT_IFY 0x81 /* ditto, AM mode, got ify */ +#define SBIC_CSR_SLT 0x82 /* selected, no ATN */ +#define SBIC_CSR_SLT_ATN 0x83 /* selected with ATN */ +#define SBIC_CSR_ATN 0x84 /* (T) ATN asserted */ +#define SBIC_CSR_DISC_1 0x85 /* (I) bus is free */ +#define SBIC_CSR_UNK_GROUP 0x87 /* strange CDB1 */ +#define SBIC_CSR_MIS_2 0x88 /* (I) ph mis, see low bits */ + +#define SBIC_PHASE(csr) SCSI_PHASE(csr) + +/* + * Command register (command codes) + */ + +#define SBIC_CMD_SBT 0x80 /* Single byte xfer qualifier */ +#define SBIC_CMD_MASK 0x7f + + /* Miscellaneous */ +#define SBIC_CMD_RESET 0x00 /* (DTI) lev I */ +#define SBIC_CMD_ABORT 0x01 /* (DTI) lev I */ +#define SBIC_CMD_DISC 0x04 /* ( TI) lev I */ +#define SBIC_CMD_SSCC 0x0d /* ( TI) lev I */ +#define SBIC_CMD_SET_IDI 0x0f /* (DTI) lev I */ +#define SBIC_CMD_XLATE 0x18 /* (DT ) lev II */ + + /* Initiator state */ +#define SBIC_CMD_SET_ATN 0x02 /* ( I) lev I */ +#define SBIC_CMD_CLR_ACK 0x03 /* ( I) lev I */ +#define SBIC_CMD_XFER_PAD 0x19 /* ( I) lev II */ +#define SBIC_CMD_XFER_INFO 0x20 /* ( I) lev II */ + + /* Target state */ +#define SBIC_CMD_SND_DISC 0x0e /* ( T ) lev II */ +#define SBIC_CMD_RCV_CMD 0x10 /* ( T ) lev II */ +#define SBIC_CMD_RCV_DATA 0x11 /* ( T ) lev II */ +#define SBIC_CMD_RCV_MSG_OUT 0x12 /* ( T ) lev II */ +#define SBIC_CMD_RCV 0x13 /* ( T ) lev II */ +#define SBIC_CMD_SND_STATUS 0x14 /* ( T ) lev II */ +#define SBIC_CMD_SND_DATA 0x15 /* ( T ) lev II */ +#define SBIC_CMD_SND_MSG_IN 0x16 /* ( T ) lev II */ +#define SBIC_CMD_SND 0x17 /* ( T ) lev II */ + + /* Disconnected state */ +#define SBIC_CMD_RESELECT 0x05 /* (D ) lev II */ +#define SBIC_CMD_SEL_ATN 0x06 /* (D ) lev II */ +#define SBIC_CMD_SEL 0x07 /* (D ) lev II */ +#define SBIC_CMD_SEL_ATN_XFER 0x08 /* (D I) lev II */ +#define SBIC_CMD_SEL_XFER 0x09 /* (D I) lev II */ +#define SBIC_CMD_RESELECT_RECV 0x0a /* (DT ) lev II */ +#define SBIC_CMD_RESELECT_SEND 0x0b /* (DT ) lev II */ +#define SBIC_CMD_WAIT_SEL_RECV 0x0c /* (DT ) lev II */ + + +#define PHASE_MASK 0x07 /* mask for psns/pctl phase */ +#define DATA_OUT_PHASE 0x00 +#define DATA_IN_PHASE 0x01 +#define CMD_PHASE 0x02 +#define STATUS_PHASE 0x03 +#define BUS_FREE_PHASE 0x04 +#define ARB_SEL_PHASE 0x05 /* Fuji chip combines bus arb with sel. */ +#define MESG_OUT_PHASE 0x06 +#define MESG_IN_PHASE 0x07 + +#define SCSI_PHASE(reg) ((reg) & PHASE_MASK) + +#define SCSI_STATUS_MASK 0x3e /* Mask unused bits in status byte */ + +/* approximate, but we won't do SBT on selects */ +#define wd33c93_isa_select(cmd) (((cmd) > 0x5) && ((cmd) < 0xa)) + +#define PAD(n) char n; +#define SBIC_MACHINE_DMA_MODE SBIC_CTL_DMA + +/* + * WD33C93 has two registers: + * ASR - r : Aux Status Register, w : desired register no + * DATA - rw: register value + * + * We access them via separate handles because some people *cough*SGI*cough* + * like to keep them apart. + */ + +#define wd33c93_read_reg(sc,regno,val) \ + do { \ + bus_space_write_1((sc)->sc_regt,(sc)->sc_asr_regh, 0, (regno)); \ + (val) = bus_space_read_1((sc)->sc_regt,(sc)->sc_data_regh, 0); \ + } while (0) + +#define wd33c93_write_reg(sc,regno,val) \ + do { \ + bus_space_write_1((sc)->sc_regt, (sc)->sc_asr_regh, 0, (regno)); \ + bus_space_write_1((sc)->sc_regt, (sc)->sc_data_regh, 0, (val)); \ + } while (0) + +#define SET_SBIC_myid(sc,val) wd33c93_write_reg(sc,SBIC_myid,val) +#define GET_SBIC_myid(sc,val) wd33c93_read_reg(sc,SBIC_myid,val) +#define SET_SBIC_cdbsize(sc,val) wd33c93_write_reg(sc,SBIC_cdbsize,val) +#define GET_SBIC_cdbsize(sc,val) wd33c93_read_reg(sc,SBIC_cdbsize,val) +#define SET_SBIC_control(sc,val) wd33c93_write_reg(sc,SBIC_control,val) +#define GET_SBIC_control(sc,val) wd33c93_read_reg(sc,SBIC_control,val) +#define SET_SBIC_timeo(sc,val) wd33c93_write_reg(sc,SBIC_timeo,val) +#define GET_SBIC_timeo(sc,val) wd33c93_read_reg(sc,SBIC_timeo,val) +#define SET_SBIC_cdb1(sc,val) wd33c93_write_reg(sc,SBIC_cdb1,val) +#define GET_SBIC_cdb1(sc,val) wd33c93_read_reg(sc,SBIC_cdb1,val) +#define SET_SBIC_cdb2(sc,val) wd33c93_write_reg(sc,SBIC_cdb2,val) +#define GET_SBIC_cdb2(sc,val) wd33c93_read_reg(sc,SBIC_cdb2,val) +#define SET_SBIC_cdb3(sc,val) wd33c93_write_reg(sc,SBIC_cdb3,val) +#define GET_SBIC_cdb3(sc,val) wd33c93_read_reg(sc,SBIC_cdb3,val) +#define SET_SBIC_cdb4(sc,val) wd33c93_write_reg(sc,SBIC_cdb4,val) +#define GET_SBIC_cdb4(sc,val) wd33c93_read_reg(sc,SBIC_cdb4,val) +#define SET_SBIC_cdb5(sc,val) wd33c93_write_reg(sc,SBIC_cdb5,val) +#define GET_SBIC_cdb5(sc,val) wd33c93_read_reg(sc,SBIC_cdb5,val) +#define SET_SBIC_cdb6(sc,val) wd33c93_write_reg(sc,SBIC_cdb6,val) +#define GET_SBIC_cdb6(sc,val) wd33c93_read_reg(sc,SBIC_cdb6,val) +#define SET_SBIC_cdb7(sc,val) wd33c93_write_reg(sc,SBIC_cdb7,val) +#define GET_SBIC_cdb7(sc,val) wd33c93_read_reg(sc,SBIC_cdb7,val) +#define SET_SBIC_cdb8(sc,val) wd33c93_write_reg(sc,SBIC_cdb8,val) +#define GET_SBIC_cdb8(sc,val) wd33c93_read_reg(sc,SBIC_cdb8,val) +#define SET_SBIC_cdb9(sc,val) wd33c93_write_reg(sc,SBIC_cdb9,val) +#define GET_SBIC_cdb9(sc,val) wd33c93_read_reg(sc,SBIC_cdb9,val) +#define SET_SBIC_cdb10(sc,val) wd33c93_write_reg(sc,SBIC_cdb10,val) +#define GET_SBIC_cdb10(sc,val) wd33c93_read_reg(sc,SBIC_cdb10,val) +#define SET_SBIC_cdb11(sc,val) wd33c93_write_reg(sc,SBIC_cdb11,val) +#define GET_SBIC_cdb11(sc,val) wd33c93_read_reg(sc,SBIC_cdb11,val) +#define SET_SBIC_cdb12(sc,val) wd33c93_write_reg(sc,SBIC_cdb12,val) +#define GET_SBIC_cdb12(sc,val) wd33c93_read_reg(sc,SBIC_cdb12,val) +#define SET_SBIC_tlun(sc,val) wd33c93_write_reg(sc,SBIC_tlun,val) +#define GET_SBIC_tlun(sc,val) wd33c93_read_reg(sc,SBIC_tlun,val) +#define SET_SBIC_cmd_phase(sc,val) wd33c93_write_reg(sc,SBIC_cmd_phase,val) +#define GET_SBIC_cmd_phase(sc,val) wd33c93_read_reg(sc,SBIC_cmd_phase,val) +#define SET_SBIC_syn(sc,val) wd33c93_write_reg(sc,SBIC_syn,val) +#define GET_SBIC_syn(sc,val) wd33c93_read_reg(sc,SBIC_syn,val) +#define SET_SBIC_count_hi(sc,val) wd33c93_write_reg(sc,SBIC_count_hi,val) +#define GET_SBIC_count_hi(sc,val) wd33c93_read_reg(sc,SBIC_count_hi,val) +#define SET_SBIC_count_med(sc,val) wd33c93_write_reg(sc,SBIC_count_med,val) +#define GET_SBIC_count_med(sc,val) wd33c93_read_reg(sc,SBIC_count_med,val) +#define SET_SBIC_count_lo(sc,val) wd33c93_write_reg(sc,SBIC_count_lo,val) +#define GET_SBIC_count_lo(sc,val) wd33c93_read_reg(sc,SBIC_count_lo,val) +#define SET_SBIC_selid(sc,val) wd33c93_write_reg(sc,SBIC_selid,val) +#define GET_SBIC_selid(sc,val) wd33c93_read_reg(sc,SBIC_selid,val) +#define SET_SBIC_rselid(sc,val) wd33c93_write_reg(sc,SBIC_rselid,val) +#define GET_SBIC_rselid(sc,val) wd33c93_read_reg(sc,SBIC_rselid,val) +#define SET_SBIC_csr(sc,val) wd33c93_write_reg(sc,SBIC_csr,val) +#define GET_SBIC_csr(sc,val) wd33c93_read_reg(sc,SBIC_csr,val) +#define SET_SBIC_cmd(sc,val) wd33c93_write_reg(sc,SBIC_cmd,val) +#define GET_SBIC_cmd(sc,val) wd33c93_read_reg(sc,SBIC_cmd,val) +#define SET_SBIC_data(sc,val) wd33c93_write_reg(sc,SBIC_data,val) +#define GET_SBIC_data(sc,val) wd33c93_read_reg(sc,SBIC_data,val) +#define SET_SBIC_queue_tag(sc,val) wd33c93_write_reg(sc,SBIC_queue_tag,val) +#define GET_SBIC_queue_tag(sc,val) wd33c93_read_reg(sc,SBIC_queue_tag,val) + +#define SBIC_TC_PUT(sc,val) \ + do { \ + wd33c93_write_reg(sc,SBIC_count_hi,((val)>>16)); \ + bus_space_write_1((sc)->sc_regt, (sc)->sc_data_regh, 0, \ + (val)>>8); \ + bus_space_write_1((sc)->sc_regt, (sc)->sc_data_regh, 0, \ + (val)); \ + } while (0) + +#define SBIC_TC_GET(sc,val) \ + do { \ + wd33c93_read_reg(sc,SBIC_count_hi,(val)); \ + (val) = ((val)<<8) | bus_space_read_1((sc)->sc_regt, \ + (sc)->sc_data_regh, 0); \ + (val) = ((val)<<8) | bus_space_read_1((sc)->sc_regt, \ + (sc)->sc_data_regh, 0); \ + } while (0) + +#define SBIC_LOAD_COMMAND(sc,cmd,cmdsize) \ + do { \ + int n = (cmdsize) - 1; \ + char *ptr = (char *)(cmd); \ + wd33c93_write_reg(regs, SBIC_cdb1, *ptr++); \ + while(n-- > 0) \ + bus_space_write_1((sc)->sc_regt, (sc)->sc_data_regh, \ + 0, *ptr++); /* XXX write_multi */ \ + } while (0) + +#define GET_SBIC_asr(sc,val) \ + do { \ + (val) = bus_space_read_1((sc)->sc_regt,(sc)->sc_asr_regh, 0); \ + } while (0) + + +#define WAIT_CIP(sc) \ + do { \ + while (bus_space_read_1((sc)->sc_regt,(sc)->sc_asr_regh, \ + 0) & SBIC_ASR_CIP) \ + /*nop*/; \ + } while (0) + +/* + * transmit a byte in programmed I/O mode + */ +#define SEND_BYTE(sc, ch) \ + do { \ + WAIT_CIP(sc); \ + SET_SBIC_cmd(sc, SBIC_CMD_SBT | SBIC_CMD_XFER_INFO); \ + SBIC_WAIT(sc, SBIC_ASR_DBR, 0); \ + SET_SBIC_data(sc, ch); \ + } while (0) + +/* + * receive a byte in programmed I/O mode + */ +#define RECV_BYTE(sc, ch) \ + do { \ + WAIT_CIP(sc); \ + SET_SBIC_cmd(sc, SBIC_CMD_SBT | SBIC_CMD_XFER_INFO); \ + SBIC_WAIT(sc, SBIC_ASR_DBR, 0); \ + GET_SBIC_data(sc, ch); \ + } while (0) diff --git a/sys/dev/ic/wd33c93var.h b/sys/dev/ic/wd33c93var.h new file mode 100644 index 00000000000..45a24bcebc0 --- /dev/null +++ b/sys/dev/ic/wd33c93var.h @@ -0,0 +1,262 @@ +/* $OpenBSD: wd33c93var.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: wd33c93var.h,v 1.10 2009/05/12 14:25:18 cegger Exp $ */ + +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)scsivar.h 7.1 (Berkeley) 5/8/90 + */ + +#define SBIC_NTARG 8 +#define SBIC_NLUN 8 +#define SBIC_NTAGS 256 + +#define SBIC_MAX_MSGLEN 8 + +#define SBIC_ABORT_TIMEOUT 2000 /* time to wait for abort */ +#define SBIC_SENSE_TIMEOUT 1000 /* time to wait for sense */ + +/* + * ACB. Holds additional information for each SCSI command Comments: We + * need a separate scsi command block because we may need to overwrite it + * with a request sense command. Basically, we refrain from fiddling with + * the scsi_xfer struct (except do the expected updating of return values). + * We'll generally update: xs->{flags,resid,error,sense,status} and + * occasionally xs->retries. + */ +struct wd33c93_acb { + TAILQ_ENTRY(wd33c93_acb) chain; + struct scsi_xfer *xs; /* SCSI xfer ctrl block from above */ + int flags; /* Status */ +#define ACB_FREE 0x00 +#define ACB_ACTIVE 0x01 +#define ACB_READY 0x02 /* ACB is on ready list */ +#define ACB_DONE 0x04 +#define ACB_SENSE 0x08 /* ACB Requesting sense */ +#define ACB_COMPLETE 0x10 /* Disconnected at end of xfer */ +#define ACB_RESET 0x20 /* Require Reset */ +#define ACB_ABORT 0x40 /* Require Abort */ + int timeout; + struct timeout to; + + struct scsi_generic cmd; /* SCSI command block */ + char *daddr; /* kva for data */ + int clen; + size_t dleft; /* bytes remaining */ + u_char tag_type; /* TAG Type (0x20-0x22, 0=No Tags) */ + u_char tag_id; /* TAG id number */ +}; + +/* + * Some info about each (possible) target on the SCSI bus. This should + * probably have been a "per target+lunit" structure, but we'll leave it at + * this for now. Is there a way to reliably hook it up to sc->fordriver?? + */ + +struct wd33c93_linfo { + int lun; + LIST_ENTRY(wd33c93_linfo) link; + time_t last_used; + u_char used; /* # slots in use */ + u_char avail; /* where to start scanning */ + u_char state; +#define L_STATE_IDLE 0 +#define L_STATE_BUSY 1 +#define L_STATE_ESTAT 2 + struct wd33c93_acb *untagged; + struct wd33c93_acb *queued[SBIC_NTAGS]; +}; + +struct wd33c93_tinfo { + int cmds; /* # of commands processed */ + int dconns; /* # of disconnects */ + + u_char flags; +#define T_NEED_RESET 0x01 /* Should send a BUS_DEV_RESET */ +#define T_NEGOTIATE 0x02 /* (Re)Negotiate synchronous options */ +#define T_BUSY 0x04 /* Target is busy */ +#define T_SYNCMODE 0x08 /* SYNC mode has been negotiated */ +#define T_NOSYNC 0x10 /* Force ASYNC mode */ +#define T_NODISC 0x20 /* Don't allow disconnect */ +#define T_TAG 0x40 /* Turn on TAG QUEUEs */ +#define T_WANTSYNC 0x80 /* Negotiatious should aim for sync */ + u_char period; /* Period suggestion */ + u_char offset; /* Offset suggestion */ + struct wd33c93_linfo *lun[SBIC_NLUN]; /* LUN list for this target */ +}; + +/* Look up a lun in a tinfo */ +#define TINFO_LUN(t, l) ((t)->lun[(l)]) + +struct wd33c93_softc { + struct device sc_dev; + + struct timeout sc_watchdog; + struct scsi_link sc_link; + void *sc_driver; /* driver specific field */ + + int target; /* Currently active target */ + int lun; /* Currently active LUN */ + + /* WD33c93 registers */ + bus_space_tag_t sc_regt; + bus_space_handle_t sc_asr_regh; + bus_space_handle_t sc_data_regh; + + /* Data about the current nexus (updated for every cmd switch) */ + void * sc_daddr; /* Current data pointer */ + size_t sc_dleft; /* Data left to transfer */ + ssize_t sc_tcnt; /* number of bytes transfered */ + + /* Lists of command blocks */ + TAILQ_HEAD(acb_list, wd33c93_acb) ready_list; + + struct wd33c93_acb *sc_nexus; /* current command */ + struct wd33c93_tinfo sc_tinfo[8]; + + u_short sc_state; + u_short sc_status; + int sc_disc; /* current # of active nexus's */ + int sc_flags; + + /* Message stuff */ + u_short sc_msgify; /* Last IDENTIFY message */ + u_short sc_msgout; /* Current message out */ + u_short sc_msgpriq; /* mesg_out queue (bitmap) */ + u_short sc_msgoutq; /* mesg_out queue */ + + u_char sc_imsg[SBIC_MAX_MSGLEN]; + u_char sc_omsg[SBIC_MAX_MSGLEN]; + u_char sc_imsglen; + u_char sc_omsglen; + + /* Static hardware attributes supplied by attachment */ + int sc_id; /* SCSI ID for controller */ + int sc_clkfreq; /* wd33c93 clk freq * 10 MHz */ + uint8_t sc_dmamode; /* One of SBIC_CTL_*DMA */ + + /* Static hardware attributes derived by wd33c93_attach() */ + int sc_chip; /* Chip variation */ + int sc_rev; /* Chip revision */ + int sc_cfflags; /* Copy of config flags */ + int sc_maxxfer; /* Maximum transfer size */ + uint8_t sc_maxoffset; /* Maximum sync offset (bytes) */ + uint8_t sc_minsyncperiod; /* Minimum supported sync xfer period */ + uint8_t sc_syncperiods[7]; /* Sync transfer periods (4ns units) */ + uint8_t sc_fsyncperiods[3]; /* Sync transfer periods for Fast SCSI*/ + + int (*sc_dmasetup)(struct wd33c93_softc *, void **, size_t *, int, + size_t *); + int (*sc_dmago)(struct wd33c93_softc *); + void (*sc_dmastop)(struct wd33c93_softc *); + void (*sc_reset)(struct wd33c93_softc *); +}; + +/* values for sc_flags */ +#define SBICF_SELECTED 0x01 /* bus is in selected state. */ +#define SBICF_NODMA 0x02 /* Polled transfer */ +#define SBICF_INDMA 0x04 /* DMA I/O in progress */ +#define SBICF_SYNCNEGO 0x08 /* Sync negotiation in progress */ +#define SBICF_ABORTING 0x10 /* Aborting */ + +/* values for sc_state */ +#define SBIC_UNINITIALIZED 0 /* Driver not initialized */ +#define SBIC_IDLE 1 /* waiting for something to do */ +#define SBIC_SELECTING 2 /* SCSI command is arbiting */ +#define SBIC_RESELECTED 3 /* Has been reselected */ +#define SBIC_IDENTIFIED 4 /* Has gotten IFY but not TAG */ +#define SBIC_CONNECTED 5 /* Actively using the SCSI bus */ +#define SBIC_DISCONNECT 6 /* MSG_DISCONNECT received */ +#define SBIC_CMDCOMPLETE 7 /* MSG_CMDCOMPLETE received */ +#define SBIC_ERROR 8 /* Error has occurred */ +#define SBIC_SELTIMEOUT 9 /* Select Timeout */ +#define SBIC_CLEANING 10 /* Scrubbing ACB's */ +#define SBIC_BUSRESET 11 /* SCSI RST has been issued */ + +/* values for sc_msgout */ +#define SEND_DEV_RESET 0x0001 +#define SEND_PARITY_ERROR 0x0002 +#define SEND_INIT_DET_ERR 0x0004 +#define SEND_REJECT 0x0008 +#define SEND_IDENTIFY 0x0010 +#define SEND_ABORT 0x0020 +#define SEND_WDTR 0x0040 +#define SEND_SDTR 0x0080 +#define SEND_TAG 0x0100 + +/* WD33c93 chipset revisions - values for sc_rev */ +#define SBIC_CHIP_UNKNOWN 0 +#define SBIC_CHIP_WD33C93 1 +#define SBIC_CHIP_WD33C93A 2 +#define SBIC_CHIP_WD33C93B 3 + +#define SBIC_CHIP_LIST {"UNKNOWN", "WD33C93", "WD33C93A", "WD33C93B"} + +/* macros for sc_cfflags */ +#define CFFLAGS_NODISC(_cf, _t) ((_cf) & (1 << ( 0 + (_t)))) +#define CFFLAGS_NOSYNC(_cf, _t) ((_cf) & (1 << ( 8 + (_t)))) +#define CFFLAGS_NOTAGS(_cf, _t) ((_cf) & (1 << (16 + (_t)))) + +/* + * States returned by our state machine + */ +#define SBIC_STATE_ERROR -1 +#define SBIC_STATE_DONE 0 +#define SBIC_STATE_RUNNING 1 +#define SBIC_STATE_DISCONNECT 2 + +#define DEBUG_ACBS 0x01 +#define DEBUG_INTS 0x02 +#define DEBUG_CMDS 0x04 +#define DEBUG_MISC 0x08 +#define DEBUG_TRAC 0x10 +#define DEBUG_RSEL 0x20 +#define DEBUG_PHASE 0x40 +#define DEBUG_DMA 0x80 +#define DEBUG_CCMDS 0x100 +#define DEBUG_MSGS 0x200 +#define DEBUG_TAGS 0x400 +#define DEBUG_SYNC 0x800 + +#ifdef SBICDEBUG +extern int wd33c93_debug_flags; +#define SBIC_DEBUG(level, str) \ + do { \ + if (wd33c93_debug & __CONCAT(DEBUG_,level)) \ + printf str; \ + } while (0) +#else +#define SBIC_DEBUG(level, str) +#endif + +void wd33c93_scsi_cmd(struct scsi_xfer *); +void wd33c93_attach(struct wd33c93_softc *, struct scsi_adapter *); +int wd33c93_intr(void *); diff --git a/sys/dev/ic/z8530reg.h b/sys/dev/ic/z8530reg.h index b07c6249914..4bf7ca41a4f 100644 --- a/sys/dev/ic/z8530reg.h +++ b/sys/dev/ic/z8530reg.h @@ -1,5 +1,5 @@ -/* $OpenBSD: z8530reg.h,v 1.6 2003/10/21 18:58:50 jmc Exp $ */ -/* $NetBSD: z8530reg.h,v 1.7 1996/10/23 00:32:31 gwr Exp $ */ +/* $OpenBSD: z8530reg.h,v 1.7 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: z8530reg.h,v 1.12 2005/12/11 12:21:29 christos Exp $ */ /* * Copyright (c) 1992, 1993 @@ -162,6 +162,8 @@ #define ZSWR1_TIE 0x02 /* transmit interrupt enable */ #define ZSWR1_SIE 0x01 /* external/status interrupt enable */ +#define ZSWR1_IMASK 0x1F /* mask of all itr. enable bits. */ + /* HSIS compat */ #define ZSWR1_REQ_ENABLE (ZSWR1_REQ_WAIT | ZSWR1_REQ_TX) @@ -184,6 +186,7 @@ #define ZSWR3_HUNT 0x10 /* enter hunt mode */ #define ZSWR3_RXCRC_ENABLE 0x08 /* enable recv crc calculation */ #define ZSWR3_ADDR_SEARCH_MODE 0x04 /* address search mode (SDLC only) */ +#define ZSWR3_SDLC_SHORT_ADDR 0x02 /* short address mode (SDLC only) */ #define ZSWR3_SYNC_LOAD_INH 0x02 /* sync character load inhibit */ #define ZSWR3_RX_ENABLE 0x01 /* receiver enable */ |