/* * Mach Operating System * Copyright (c) 1991,1990 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 Mellon * the rights to redistribute these changes. */ /* * File: mapped_scsi.c * Author: Alessandro Forin, Carnegie Mellon University * Date: 9/90 * * In-kernel side of the user-mapped SCSI driver. */ #include #include #define NRZ (NASC+NSII) #if NRZ > 0 #include #include /* spl definitions */ #include #include #include #include #include #include #include #ifdef DECSTATION #define machine_btop mips_btop #define kvctophys(v) K0SEG_TO_PHYS((v)) /* kernel virtual cached */ #define phystokvc(p) PHYS_TO_K0SEG((p)) /* and back */ #define kvutophys(v) K1SEG_TO_PHYS((v)) /* kernel virtual uncached */ #define phystokvu(p) PHYS_TO_K1SEG((p)) /* and back */ #include #include #include #define SII_REG_PHYS(self) kvutophys(self->registers.any) #define SII_RAM_PHYS(self) (SII_REG_PHYS((self))+(KN01_SYS_SII_B_START-KN01_SYS_SII)) #define SII_RAM_SIZE (KN01_SYS_SII_B_END-KN01_SYS_SII_B_START) #define ASC_REG_PHYS(self) kvutophys(self->registers.any) #define ASC_DMAR_PHYS(self) (ASC_REG_PHYS((self))+ ASC_OFFSET_DMAR) #define ASC_RAM_PHYS(self) (ASC_REG_PHYS((self))+ ASC_OFFSET_RAM) #define PAD_7061(n) short n #define PAD_53C94(n) char n[3] #endif /*DECSTATION*/ #ifdef VAXSTATION #define machine_btop vax_btop #endif /*VAXSTATION*/ #ifdef P40 #define machine_btop mips_btop #define kvctophys(v) K0SEG_TO_PHYS((v)) /* kernel virtual cached */ #define phystokvc(p) PHYS_TO_K0SEG((p)) /* and back */ #define kvutophys(v) K1SEG_TO_PHYS((v)) /* kernel virtual uncached */ #define phystokvu(p) PHYS_TO_K1SEG((p)) /* and back */ #include #define ASC_RAM_SIZE 0 #define ASC_OFFSET_DMAR 0 #define ASC_OFFSET_RAM 0 #define ASC_REG_PHYS(self) kvutophys(self->registers.any) #define ASC_DMAR_PHYS(self) (ASC_REG_PHYS((self))+ ASC_OFFSET_DMAR) #define ASC_RAM_PHYS(self) (ASC_REG_PHYS((self))+ ASC_OFFSET_RAM) #endif /* P40 */ /* * Phys defines for the various supported HBAs */ /* DEC7061 */ #include #ifdef PAD_7061 typedef struct { volatile unsigned short sii_sdb; /* rw: Data bus and parity */ PAD_7061(pad0); volatile unsigned short sii_sc1; /* rw: scsi signals 1 */ PAD_7061(pad1); volatile unsigned short sii_sc2; /* rw: scsi signals 2 */ PAD_7061(pad2); volatile unsigned short sii_csr; /* rw: control and status */ PAD_7061(pad3); volatile unsigned short sii_id; /* rw: scsi bus ID */ PAD_7061(pad4); volatile unsigned short sii_sel_csr; /* rw: selection status */ PAD_7061(pad5); volatile unsigned short sii_destat; /* ro: selection detector status */ PAD_7061(pad6); volatile unsigned short sii_dstmo; /* unsupp: dssi timeout */ PAD_7061(pad7); volatile unsigned short sii_data; /* rw: data register */ PAD_7061(pad8); volatile unsigned short sii_dma_ctrl; /* rw: dma control reg */ PAD_7061(pad9); volatile unsigned short sii_dma_len; /* rw: length of transfer */ PAD_7061(pad10); volatile unsigned short sii_dma_adr_low;/* rw: low address */ PAD_7061(pad11); volatile unsigned short sii_dma_adr_hi; /* rw: high address */ PAD_7061(pad12); volatile unsigned short sii_dma_1st_byte;/* rw: initial byte */ PAD_7061(pad13); volatile unsigned short sii_stlp; /* unsupp: dssi short trgt list ptr */ PAD_7061(pad14); volatile unsigned short sii_ltlp; /* unsupp: dssi long " " " */ PAD_7061(pad15); volatile unsigned short sii_ilp; /* unsupp: dssi initiator list ptr */ PAD_7061(pad16); volatile unsigned short sii_dssi_csr; /* unsupp: dssi control */ PAD_7061(pad17); volatile unsigned short sii_conn_csr; /* rc: connection interrupt control */ PAD_7061(pad18); volatile unsigned short sii_data_csr; /* rc: data interrupt control */ PAD_7061(pad19); volatile unsigned short sii_cmd; /* rw: command register */ PAD_7061(pad20); volatile unsigned short sii_diag_csr; /* rw: disgnostic status */ PAD_7061(pad21); } sii_padded_regmap_t; #else /*!PAD_7061*/ typedef sii_regmap_t sii_padded_regmap_t; #endif /*!PAD_7061*/ /* NCR 53C94 */ #include #ifdef PAD_53C94 typedef struct { volatile unsigned char asc_tc_lsb; /* rw: Transfer Counter LSB */ PAD_53C94(pad0); volatile unsigned char asc_tc_msb; /* rw: Transfer Counter MSB */ PAD_53C94(pad1); volatile unsigned char asc_fifo; /* rw: FIFO top */ PAD_53C94(pad2); volatile unsigned char asc_cmd; /* rw: Command */ PAD_53C94(pad3); volatile unsigned char asc_csr; /* r: Status */ /*#define asc_dbus_id asc_csr /* w: Destination Bus ID */ PAD_53C94(pad4); volatile unsigned char asc_intr; /* r: Interrupt */ /*#define asc_sel_timo asc_intr /* w: (re)select timeout */ PAD_53C94(pad5); volatile unsigned char asc_ss; /* r: Sequence Step */ /*#define asc_syn_p asc_ss /* w: synchronous period */ PAD_53C94(pad6); volatile unsigned char asc_flags; /* r: FIFO flags + seq step */ /*#define asc_syn_o asc_flags /* w: synchronous offset */ PAD_53C94(pad7); volatile unsigned char asc_cnfg1; /* rw: Configuration 1 */ PAD_53C94(pad8); volatile unsigned char asc_ccf; /* w: Clock Conv. Factor */ PAD_53C94(pad9); volatile unsigned char asc_test; /* w: Test Mode */ PAD_53C94(pad10); volatile unsigned char asc_cnfg2; /* rw: Configuration 2 */ PAD_53C94(pad11); volatile unsigned char asc_cnfg3; /* rw: Configuration 3 */ PAD_53C94(pad12); volatile unsigned char asc_rfb; /* w: Reserve FIFO byte */ PAD_53C94(pad13); } asc_padded_regmap_t; #else /* !PAD_53C94 */ typedef asc_regmap_t asc_padded_regmap_t; #endif /* !PAD_53C94 */ /* * Co-existency with in-kernel drivers */ boolean_t rz_use_mapped_interface = FALSE; /* * Status information for all HBAs */ /*static*/ struct RZ_status { union { unsigned long any; asc_padded_regmap_t *asc; sii_padded_regmap_t *sii; } registers; int (*stop)(); vm_offset_t (*mmap)(); mapped_scsi_info_t info; struct evc eventcounter; } RZ_statii[NRZ]; typedef struct RZ_status *RZ_status_t; /* * Probe routine for all HBAs */ RZ_probe(regbase, ui, hba) unsigned long regbase; register struct bus_device *ui; { int unit = ui->unit; vm_offset_t addr; mapped_scsi_info_t info; struct RZ_status *self; printf("[mappable] "); self = &RZ_statii[unit]; self->registers.any = regbase; /* * Grab a page to be mapped later to users */ (void) kmem_alloc_wired(kernel_map, &addr, PAGE_SIZE); /* kseg2 */ bzero(addr, PAGE_SIZE); addr = pmap_extract(pmap_kernel(), addr); /* phys */ info = (mapped_scsi_info_t) (phystokvc(addr)); self->info = info; /* * Set permanent info */ info->interrupt_count = 0; /*XXX*/ info->ram_size = ASC_RAM_SIZE; info->hba_type = hba; evc_init(&self->eventcounter); info->wait_event = self->eventcounter.ev_id; return 1; } /* * Device open procedure */ RZ_open(dev, flag, ior) io_req_t ior; { int unit = dev; register RZ_status_t self = &RZ_statii[unit]; if (unit >= NRZ) return D_NO_SUCH_DEVICE; /* * Silence interface, just in case */ (*self->stop)(unit); /* * Reset eventcounter */ evc_signal(&self->eventcounter); rz_use_mapped_interface = TRUE; /* * Do not turn interrupts on. The user can do it when ready * to take them. */ return 0; } /* * Device close procedure */ RZ_close(dev, flag) { int unit = dev; register RZ_status_t self = &RZ_statii[unit]; if (unit >= NRZ) return D_NO_SUCH_DEVICE; /* * Silence interface, in case user forgot */ (*self->stop)(unit); evc_signal(&self->eventcounter); rz_use_mapped_interface = FALSE; /* XXX rz_kernel_mode(); XXX */ return 0; } /* * Get status procedure. * We need to tell that we are mappable. */ io_return_t RZ_get_status(dev, flavor, status, status_count) int dev; int flavor; dev_status_t status; unsigned int status_count; { return (D_SUCCESS); } /* * Should not refuse this either */ RZ_set_status(dev, flavor, status, status_count) int dev; int flavor; dev_status_t status; unsigned int status_count; { return (D_SUCCESS); } /* * Port death notification routine */ RZ_portdeath(dev, dead_port) { } /* * Page mapping, switch off to HBA-specific for regs&ram */ vm_offset_t RZ_mmap(dev, off, prot) int dev; { int unit = dev; register RZ_status_t self = &RZ_statii[unit]; vm_offset_t page; vm_offset_t addr; io_return_t ret; if (off < SCSI_INFO_SIZE) { addr = kvctophys (self->info) + off; ret = D_SUCCESS; } else ret = (*self->mmap)(self, off, prot, &addr); if (ret != D_SUCCESS) return ret; page = machine_btop(addr); return (page); } /* *--------------------------------------------------------------- * The rest of the file contains HBA-specific routines *--------------------------------------------------------------- */ #if NASC > 0 /* * Routines for the NCR 53C94 */ static ASC_stop(unit) { register RZ_status_t self = &RZ_statii[unit]; register asc_padded_regmap_t *regs = self->registers.asc; int ack; ack = regs->asc_intr; /* Just acknowledge pending interrupts */ } ASC_probe(reg, ui) unsigned long reg; register struct bus_device *ui; { register RZ_status_t self = &RZ_statii[ui->unit]; static vm_offset_t ASC_mmap(); self->stop = ASC_stop; self->mmap = ASC_mmap; return RZ_probe(reg, ui, HBA_NCR_53c94); } ASC_intr(unit,spllevel) spl_t spllevel; { register RZ_status_t self = &RZ_statii[unit]; register asc_padded_regmap_t *regs = self->registers.asc; register csr, intr, seq_step, cmd; /* * Acknowledge interrupt request * * This clobbers some two other registers, therefore * we read them beforehand. It also clears the intr * request bit, silencing the interface for now. */ csr = regs->asc_csr; /* drop spurious interrupts */ if ((csr & ASC_CSR_INT) == 0) return; seq_step = regs->asc_ss; cmd = regs->asc_cmd; intr = regs->asc_intr; /* ack */ splx(spllevel); /* drop priority */ if (self->info) { self->info->interrupt_count++; /* total interrupts */ self->info->saved_regs.asc.csr = csr; self->info->saved_regs.asc.isr = intr; self->info->saved_regs.asc.seq = seq_step; self->info->saved_regs.asc.cmd = cmd; } /* Awake user thread */ evc_signal(&self->eventcounter); } /* * Virtual->physical mapping routine for PMAZ-AA */ static vm_offset_t ASC_mmap(self, off, prot, addr) RZ_status_t self; vm_offset_t off; vm_prot_t prot; vm_offset_t *addr; { /* * The offset (into the VM object) defines the following layout * * off size what * 0 1pg mapping information (csr & #interrupts) * 1pg 1pg ASC registers * 2pg 1pg ASC dma * 3pg 128k ASC ram buffers */ #define ASC_END (ASC_RAM_BASE+ASC_RAM_SIZE) if (off < ASC_DMAR_BASE) *addr = (vm_offset_t) ASC_REG_PHYS(self) + (off - SCSI_INFO_SIZE); else if (off < ASC_RAM_BASE) *addr = (vm_offset_t) ASC_DMAR_PHYS(self) + (off - ASC_REGS_BASE); else if (off < ASC_END) *addr = (vm_offset_t) ASC_RAM_PHYS(self) + (off - ASC_RAM_BASE); else return D_INVALID_SIZE; return D_SUCCESS; } #endif NASC > 0 #if NSII > 0 SII_stop(unit) { register RZ_status_t self = &RZ_statii[unit]; register sii_padded_regmap_t *regs = self->registers.sii; regs->sii_csr &= ~SII_CSR_IE; /* disable interrupts */ /* clear all wtc bits */ regs->sii_conn_csr = regs->sii_conn_csr; regs->sii_data_csr = regs->sii_data_csr; } SII_probe(reg, ui) unsigned long reg; register struct bus_device *ui; { register RZ_status_t self = &RZ_statii[ui->unit]; static vm_offset_t SII_mmap(); self->stop = SII_stop; self->mmap = SII_mmap; return RZ_probe(reg, ui, HBA_DEC_7061); } SII_intr(unit,spllevel) spl_t spllevel; { register RZ_status_t self = &RZ_statii[unit]; register sii_padded_regmap_t *regs = self->registers.sii; register unsigned short conn, data; /* * Disable interrupts, saving cause(s) first. */ conn = regs->sii_conn_csr; data = regs->sii_data_csr; /* drop spurious calls */ if (((conn|data) & (SII_DTR_DI|SII_DTR_CI)) == 0) return; regs->sii_csr &= ~SII_CSR_IE; regs->sii_conn_csr = conn; regs->sii_data_csr = data; splx(spllevel); if (self->info) { self->info->interrupt_count++; /* total interrupts */ self->info->saved_regs.sii.sii_conn_csr = conn; self->info->saved_regs.sii.sii_data_csr = data; } /* Awake user thread */ evc_signal(&self->eventcounter); } static vm_offset_t SII_mmap(self, off, prot, addr) RZ_status_t self; vm_offset_t off; vm_prot_t prot; vm_offset_t *addr; { /* * The offset (into the VM object) defines the following layout * * off size what * 0 1pg mapping information (csr & #interrupts) * 1pg 1pg SII registers * 2pg 128k SII ram buffer */ #define SII_END (SII_RAM_BASE+SII_RAM_SIZE) if (off < SII_RAM_BASE) *addr = (vm_offset_t) SII_REG_PHYS(self) + (off - SCSI_INFO_SIZE); else if (off < SII_END) *addr = (vm_offset_t) SII_RAM_PHYS(self) + (off - SII_RAM_BASE); else return D_INVALID_SIZE; return D_SUCCESS; } #endif NSII > 0 #endif NRZ > 0