diff options
Diffstat (limited to 'sys/i386/isa/b004.c')
-rw-r--r-- | sys/i386/isa/b004.c | 669 |
1 files changed, 669 insertions, 0 deletions
diff --git a/sys/i386/isa/b004.c b/sys/i386/isa/b004.c new file mode 100644 index 0000000000000..5a9357da471ee --- /dev/null +++ b/sys/i386/isa/b004.c @@ -0,0 +1,669 @@ +/* + * FreeBSD device driver for B004-compatible Transputer boards. + * + * based on Linux version Copyright (C) 1993 by Christoph Niemann + * + * Rewritten for FreeBSD by + * Luigi Rizzo (luigi@iet.unipi.it) and + * Lorenzo Vicisano (l.vicisano@iet.unipi.it) + * Dipartimento di Ingegneria dell'Informazione + * Universita` di Pisa + * via Diotisalvi 2, 56126 Pisa, ITALY + * 14 september 1994 + * + * 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 Christoph Niemann, + * Luigi Rizzo and Lorenzo Vicisano - Dipartimento di Ingegneria + * dell'Informazione + * 4. The names of these contributors may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 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. + * + * NOTE NOTE NOTE + * The assembler version is still under development. + */ + +/* #define USE_ASM */ + +#include "bqu.h" +#if NBQU > 0 + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/uio.h> +#include <sys/conf.h> +#include <sys/kernel.h> + +#include "opt_devfs.h" + +#ifdef DEVFS +#include <sys/devfsext.h> +#endif /*DEVFS*/ + +#include <machine/clock.h> + +#include <i386/isa/b004.h> +#include <i386/isa/isa_device.h> + +#define IOCTL_OUT(arg, ret) *(int*)arg = ret + +#define B004PRI (PZERO+8) + +#define B004_CHANCE 8 + +/* + * Define these symbols if you want to debug the code. + */ +#undef B004_DEBUG +#undef B004_DEBUG_2 + +#ifdef B004_DEBUG +static u_char d_inb(u_int port); +static void d_outb(u_int port, u_char data); + +#define out(port,data) d_outb(port, data) +#define in(a) d_inb(((u_int)a)) +#else +#define out(port, data) outb(port,data) +#define in(port) inb(((u_int)port)) +#endif B004_DEBUG + +#ifdef B004_DEBUG +#define DEB(x) x +#define NO_DEB(x) /* */ +#else +#define DEB(x) /* */ +#define NO_DEB(x) x +#endif + +#ifdef B004_DEBUG_2 +#define DEB2(x) x +#else +#define DEB2(x) +#endif + +static int bquprobe(struct isa_device *idp); +static int bquattach(struct isa_device *idp); + + +struct isa_driver bqudriver = { + bquprobe, bquattach, "bqu" +}; + +static d_open_t bquopen; +static d_close_t bquclose; +static d_read_t bquread; +static d_write_t bquwrite; +static d_ioctl_t bquioctl; +static d_poll_t bqupoll; + +#define CDEV_MAJOR 8 +static struct cdevsw bqu_cdevsw = + { bquopen, bquclose, bquread, bquwrite, /*8*/ + bquioctl, nostop, nullreset, nodevtotty,/* tputer */ + bqupoll, nommap, NULL, "bqu", NULL, -1 }; + +static int b004_sleep; /* wait address */ + +static struct b004_struct b004_table[NBQU]; + +static int first_time=1; + +/* + * At these addresses the driver will search for B004-compatible boards + */ +static int +b004_base_addresses[B004_CHANCE] = { + /* 0x150, 0x170, 0x190, 0x200, 0x300, 0x320, 0x340, 0x360 */ + 0x150, 0x190, 0, 0, 0, 0, 0, 0 +}; + +#ifdef B004_DEBUG +static void +d_outb(u_int port, u_char data) +{ + + printf("OUT 0x%x TO 0x%x\n",data,port); + outb(port,data); +} + +static u_char +d_inb(u_int port) +{ +u_char ap; + ap=inb(port); + printf("INPUT 0x%x FROM 0x%x\n",ap,port); + return(ap); +} +#endif + +static int +detected(int base) +{ + int i; + for(i=0;i<NBQU;i++) + if ((B004_F(i) & B004_EXIST) && (B004_BASE(i)==base)) return 1; + return (0); +} + +#define b004_delay(a) DELAY(10000) + +/* + * static void bqureset(): reset transputer network. + * + */ + +static void +bqureset( const int dev_min ) +{ + DEB(printf("B004 resetting transputer at link %d.\n", dev_min);) + out(B004_BASE(dev_min)+B004_ANALYSE_OFFSET, B004_DEASSERT_ANALYSE); + b004_delay(dev_min); + + out(B004_BASE(dev_min) + B004_RESET_OFFSET, B004_DEASSERT_RESET); + b004_delay(dev_min); + + out(B004_BASE(dev_min) + B004_RESET_OFFSET, B004_ASSERT_RESET); + b004_delay(dev_min); + + out(B004_BASE(dev_min) + B004_RESET_OFFSET, B004_DEASSERT_RESET); + b004_delay(dev_min); + + DEB(printf("B004 reset done.\n");) +} + +/* + * static void bquanalyse(): switch transputer network to analyse mode. + * + */ + +static void +bquanalyse( const int dev_min ) +{ + DEB(printf("B004 analysing transputer at link %d.\n", dev_min);) + + out(B004_BASE(dev_min) + B004_ANALYSE_OFFSET, B004_DEASSERT_ANALYSE); + b004_delay(dev_min); + + out(B004_BASE(dev_min) + B004_ANALYSE_OFFSET, B004_ASSERT_ANALYSE); + b004_delay(dev_min); + + out(B004_BASE(dev_min) + B004_RESET_OFFSET, B004_ASSERT_RESET); + b004_delay(dev_min); + + out(B004_BASE(dev_min) + B004_RESET_OFFSET, B004_DEASSERT_RESET); + b004_delay(dev_min); + + out(B004_BASE(dev_min) + B004_ANALYSE_OFFSET, B004_DEASSERT_ANALYSE); + b004_delay(dev_min); + + DEB(printf("B004 switching to analyse-mode done.\n");) +} + + +/**************************************************************************** + * + * int bquread() - read bytes from the link interface. + * + * At first, the driver checks if the link-interface is ready to send a byte + * to the PC. If not, this check is repeated up to B004_MAXTRY times. + * If the link-interface is not ready after this loop, the driver sleeps for + * an NO=1 ticks and then checks the link-interface again. + * If the interface is still not ready, repeats as above incrementing NO. + * Once almost one byte is read N0 is set to 1. + * If B004_TIMEOUT != 0 and the link-interface is not ready for more than + * B004_TIMEOUT ticks read aborts returnig with the number of bytes read + * or with an error if no byte was read. + * + * By default, B004_TIMEOUT is = 0 (read is blocking) + * + *****************************************************************************/ + +static int +bquread(dev_t dev, struct uio *uio, int flag) +{ + unsigned int dev_min = minor(dev) & 7; + + int timeout=B004_TIMEOUT(dev_min); + int Timeout=timeout; + int idr=B004_IDR(dev_min); + int isr=B004_ISR(dev_min); + char buffer[B004_MAX_BYTES]; + + if ( uio->uio_resid < 0) { + DEB(printf("B004: invalid count for reading = %d.\n", uio->uio_resid);) + return EINVAL; + } + + while ( uio->uio_resid ) { + int sleep_ticks=0; + char *p, *last, *lim; + int i, end = min(B004_MAX_BYTES,uio->uio_resid); + lim= &buffer[end]; + for (p= buffer; p<lim;) { + last=p; + /*** try to read as much as possible ***/ +#ifdef USE_ASM + /* assembly code uses a very tight loop, with + * BX= data port, DX= address port, CX=count, ES:DI=p, AL=data, AH=1 + * SI=retry counter + */ + __asm__ ( + "movl %1, %%edx\n\t" /* isr */ + "movl %2, %%ebx\n\t" /* idr */ + "movl %3, %%edi\n" /* p */ + "movl %4, %%ecx\n\t" /* lim */ + "subl %%edi, %%ecx\n\t" + + "push %%es\n\t" + "movw %%ss, %%ax\n\t" /** prepare ES, DF for transfer */ + "movw %%ax, %%es\n\t" + "cld\n\t" + "movb $1, %%ah\n\t" + + "1:\tinb %%dx, %%al\n\t" + "testb %%ah, %%al\n\t" + "jz 2f\n\t" + "xchgl %%edx, %%ebx\n\t" + "insb\n\t" + "xchgl %%edx, %%ebx\n" + "2:\tloop 1b\n\t" + + "pop %%es\n\t" + "movl %%edi, %0\n\t" /* store p */ + : /* out */ "=g" (p) + : /* in */ "g" (isr), "g" (idr), "g" (p), "g" (lim) + : /* regs */ "eax", "ebx", "edx", "ecx", "edi"); +#else + for (i=lim - p; i-- ;) + if (inb(isr)&B004_READBYTE) *p++ =(char) inb(idr); +#endif + if (last!=p) { + sleep_ticks = 0; + } else { + /*** no new data read, must sleep ***/ + sleep_ticks= (sleep_ticks<20 ? sleep_ticks+1 : sleep_ticks); + if (Timeout) { + if (timeout <=0) { + DEB2(printf("Read : TIMEOUT OCCURRED XXXXXXXXXXX\n");) + break; + } + if (timeout < sleep_ticks) sleep_ticks=timeout; + timeout -= sleep_ticks; + } + DEB2(printf("Read: SLEEPING FOR %d TICKS XXXXX\n",sleep_ticks);) + if (tsleep((caddr_t)&b004_sleep, B004PRI | PCATCH, + "b004_rd", sleep_ticks)!=EWOULDBLOCK) return 1; + } + } + if (p != buffer) { + uiomove((caddr_t)buffer, p - buffer, uio); + } + if( (Timeout) && (timeout <= 0) ) + break; + } + return 0; +} /* bquread() */ + + +/* + * int bquwrite() - write to the link interface. + */ + +static int +bquwrite(dev_t dev, struct uio *uio, int flag) +{ + unsigned int dev_min = minor(dev) & 7; + + int i, end; + int timeout=B004_TIMEOUT(dev_min); + int Timeout=timeout; + int odr=B004_ODR(dev_min); + int osr=B004_OSR(dev_min); + char buffer[B004_MAX_BYTES]; + + if ( uio->uio_resid < 0) { + DEB(printf("B004 invalid argument for writing: count = %d.\n", uio->uio_resid);) + return EINVAL; + } + + while ( uio->uio_resid ) { + int sleep_ticks=0; + char *p, *last, *lim; + end = min(B004_MAX_BYTES,uio->uio_resid); + uiomove((caddr_t)buffer, end, uio); + + lim= &buffer[end]; + for (p= &buffer[0]; p<lim;) { + last=p; +#ifdef USE_ASM + /* assembly code uses a very tight loop, with + * BX= data port, DX= address port, CX=count, DS:SI=p, AL=data, AH=1 + * DI= retry counter + * Unfortunately, C is almost as fast as this! + */ + __asm__ ( + "movl %1, %%edx\n\t" /* osr */ + "movl %2, %%ebx\n\t" /* odr */ + "movl %3, %%esi\n" /* p */ + "movl %4, %%ecx\n\t" /* lim */ + "subl %%esi, %%ecx\n\t" + + "push %%ds\n\t" + "movw %%ss, %%ax\n\t" /** prepare DS, DF for transfer */ + "movw %%ax, %%ds\n\t" + "cld\n\t" + "movb $1, %%ah\n\t" + "movw $100, %%di\n\t" + + "1:\tinb %%dx, %%al\n\t" + "testb %%ah, %%al\n\t" + "jz 2f\n\t" + "xchgl %%edx, %%ebx\n\t" + "outsb\n\t" + "xchgl %%edx, %%ebx\n\t" + "loop 1b\n\t" + "jmp 3f\n" + + "2:\tdec %%di\n\t" + "jnc 1b\n\t" + + "3:\tpop %%ds\n" + "movl %%esi, %0\n\t" /* store p */ + : /* out */ "=g" (p) + : /* in */ "g" (osr), "g" (odr), "g" (p), "g" (lim) + : /* regs */ "eax", "ebx", "edx", "ecx", "esi", "edi"); +#else + for (i=lim - p; i-- ; ) { + if (inb(osr)&B004_WRITEBYTE) outb(odr, *p++); + } +#endif + if (p != last ) { + sleep_ticks=0; + } else { + sleep_ticks= (sleep_ticks<20 ? sleep_ticks+1 : sleep_ticks); + if (Timeout) { + if (timeout <=0) { + DEB2(printf("Write : TIMEOUT OCCURRED XXXXXXXXXXX\n");) + uio->uio_resid += (lim - p); + break; + } + if (timeout < sleep_ticks) sleep_ticks=timeout; + timeout -= sleep_ticks; + } + DEB2(printf("Write: SLEEPING FOR %d TICKS XXXXXXX\n",sleep_ticks);) + if (tsleep((caddr_t)&b004_sleep, B004PRI | PCATCH, + "b004_rd", sleep_ticks)!=EWOULDBLOCK) return 1; + } + } + if( (Timeout) && (timeout <= 0) ) + break; + } + return 0; +} /* bquwrite() */ + +/* + * int bquopen() -- open the link-device. + * + */ + +static int +bquopen(dev_t dev, int flags, int fmt, struct proc *p) +{ + unsigned int dev_min = minor(dev) & 7; + + if (dev_min >= NBQU) { + DEB(printf("B004 not opened, minor number >= %d.\n", NBQU);) + return ENXIO; + } + if ((B004_F(dev_min) & B004_EXIST) == 0) { + DEB(printf("B004 not opened, board %d does not exist.\n", dev_min);) + return ENXIO; + } + if (B004_F(dev_min) & B004_BUSY) { + DEB(printf("B004 not opened, board busy (minor = %d).\n", dev_min);) + return EBUSY; + } + B004_F(dev_min) |= B004_BUSY; + B004_TIMEOUT(dev_min) = 0; + DEB(printf( "B004 opened, minor = %d.\n", dev_min );) + return 0; +} /* bquopen() */ + + +/* + * int b004close() -- close the link device. + */ + +static int +bquclose(dev_t dev, int flags, int fmt, struct proc *p) +{ + unsigned int dev_min = minor(dev) & 7; + + if (dev_min >= NBQU) { + DEB(printf("B004 not released, minor number >= %d.\n", NBQU);) + return ENXIO; + } + B004_F(dev_min) &= ~B004_BUSY; + DEB(printf("B004(%d) released.\n", dev_min );) + return 0; +} + +static int +bqupoll(dev_t dev, int events, struct proc *p) +{ + /* still unimplemented */ + return(seltrue(dev, events, p)); +} + +/* + * int bquioctl() + * + * Supported functions: + * - reset + * - analyse + * - test error flag + * - set timeout + */ + +static int +bquioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) +{ + unsigned int dev_min = minor(dev) & 7; + int result = 0; + + if (dev_min >= NBQU) { + DEB(printf("B004 ioctl exit, minor >= %d.\n", NBQU );) + return ENODEV; + } + + if ((B004_F(dev_min) & B004_EXIST) == 0) { + DEB(printf("B004 ioctl exit, (B004_F & B004_EXIST) == 0.\n" );) + return ENODEV; + } + + switch ( cmd ) { + case B004RESET: /* reset transputer */ + bqureset(dev_min); + DEB(printf("B004 ioctl B004RESET, done\n" );) + break; + case B004WRITEABLE: /* can we write a byte to the C012 ? */ + IOCTL_OUT (addr, ((in(B004_OSR(dev_min))&B004_WRITEBYTE) != 0 )); + break; + case B004READABLE: /* can we read a byte from C012 ? */ + IOCTL_OUT (addr, ((in(B004_ISR(dev_min)) & B004_READBYTE) != 0 )); + break; + case B004ANALYSE: /* switch transputer to analyse mode */ + bquanalyse(dev_min); + break; + case B004ERROR: /* test error-flag */ + IOCTL_OUT (addr, + ((inb(B004_BASE(dev_min)+B004_ERROR_OFFSET) & + B004_TEST_ERROR) ? 0 : 1)); + break; + case B004TIMEOUT: /* set, retrieve timeout for writing & reading*/ + B004_TIMEOUT(dev_min) = *((int *)addr); + break; + default: result = EINVAL; + } + return result; +} /* bquioctl() */ + + +static int +bquattach(struct isa_device *idp) +{ + int unit = idp->id_unit; + struct b004_struct *bp; + int i; + +#ifdef DEVFS +#define BQU_UID 66 +#define BQU_GID 66 +#define BQU_PERM 0600 + bp = &b004_table[unit]; + for ( i = 0; i < 8; i++) { +#ifdef NOTYET + /* if (we've done all the ports found) break; */ +#endif + bp->devfs_token[i][0]= + devfs_add_devswf(&bqu_cdevsw, i, DV_CHR, BQU_UID, + BQU_GID, BQU_PERM, "ttyba%d", i); + bp->devfs_token[i][0]= + devfs_add_devswf(&bqu_cdevsw, i+64, DV_CHR, BQU_UID, + BQU_GID, BQU_PERM, "ttybb%d", i); + bp->devfs_token[i][0]= + devfs_add_devswf(&bqu_cdevsw, i+128, DV_CHR, BQU_UID, + BQU_GID, BQU_PERM, "ttybc%d", i); + bp->devfs_token[i][0]= + devfs_add_devswf(&bqu_cdevsw, i+192, DV_CHR, BQU_UID, + BQU_GID, BQU_PERM, "ttybd%d", unit); + } +#endif + return 1; +} + +/* + * int bquprobe + * + * Initializes the driver. It tries to detect the hardware + * and sets up all relevant data-structures. + */ + +static int +bquprobe(struct isa_device *idp) +{ + unsigned int test; + unsigned int dev_min = idp->id_unit; + int i,found = 0; + /* After a reset it should be possible to write a byte to + the B004. So let'S do a reset and then test the output status + register + */ +#ifdef undef + printf( + "bquprobe::\nIOBASE 0x%x\nIRQ %d\nDRQ %d\nMSIZE %d\nUNIT %d\nFLAGS" + "x0%x\nALIVE %d\n",idp->id_iobase,idp->id_irq, + idp->id_drq,idp->id_msize,idp->id_unit,idp->id_flags,idp->id_alive); +#endif + if(first_time){ + for(i=0;i<NBQU;i++) B004_F(i) &= ~B004_EXIST; + first_time=0; + } + + if(dev_min >= NBQU) return (0); /* No more descriptors */ + if ((idp->id_iobase < 0x100) || (idp->id_iobase >= 0x1000)) + idp->id_iobase=0; /* Dangerous isa addres ) */ + + for (test = 0; (test < B004_CHANCE); test++) { + if((idp->id_iobase==0)&&((!b004_base_addresses[test])|| + detected(b004_base_addresses[test]))) + continue; + idp->id_iobase=b004_base_addresses[test]; + + DEB(printf("Probing device %d at address 0x%x\n",dev_min, + idp->id_iobase); + ) + b004_delay(test); + B004_F(dev_min) = 0; + B004_TIMEOUT(dev_min) = B004_INIT_TIMEOUT; + B004_BASE(dev_min) = idp->id_iobase; + B004_ODR(dev_min) = B004_BASE(dev_min) + B004_ODR_OFFSET; + B004_ISR(dev_min) = B004_BASE(dev_min) + B004_ISR_OFFSET; + B004_OSR(dev_min) = B004_BASE(dev_min) + B004_OSR_OFFSET; + bqureset(dev_min); + + for (i = 0; i < B004_MAXTRY; i++) + if ( in(B004_OSR(dev_min)) == B004_WRITEBYTE) { + B004_F(dev_min) |= B004_EXIST; + out(B004_BASE(dev_min) + B008_INT_OFFSET, 0); + b004_delay(test); + if (in(B004_BASE(dev_min) + B008_INT_OFFSET) & 0x0f == 0) + B004_BOARDTYPE(dev_min) = B008; + else + B004_BOARDTYPE(dev_min) = B004; + printf("bqu%d at 0x0%x (polling) is a B00%s\n", + dev_min,B004_IDR(dev_min), + (B004_BOARDTYPE(dev_min) == B004) ? "4" : "8"); + found = 1; + break; + } + if(!found) { + idp->id_iobase=0; + } + else break; + + } + + if (!found){ + DEB(printf("b004probe(): no B004-board found.\n")); + return (0); + } + + idp->id_maddr=NULL; + idp->id_irq=0; + if(B004_BOARDTYPE(dev_min) == B004) + return(18); + else + return(20); +} /* bquprobe() */ + + +static bqu_devsw_installed = 0; + +static void +bqu_drvinit(void *unused) +{ + dev_t dev; + + if( ! bqu_devsw_installed ) { + dev = makedev(CDEV_MAJOR, 0); + cdevsw_add(&dev,&bqu_cdevsw, NULL); + bqu_devsw_installed = 1; + } +} + +SYSINIT(bqudev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,bqu_drvinit,NULL) + + +#endif /* NBQU */ |