3105 lines
86 KiB
C
3105 lines
86 KiB
C
|
|
/*
|
|
* Copyright (c) 1990 by Sun Microsystems, Inc.
|
|
* @(#)bpp.c 1.1 94/10/31 SMI
|
|
*
|
|
* Source code for the bidirectional parallel port
|
|
* driver for the Zebra SBus card.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* These switches are needed to provide full source code compatibility
|
|
* for both the modload (loadable) case and the config (linked in) case.
|
|
* Also, some header files do not include some kernel information unless
|
|
* KERNEL is defined.
|
|
*/
|
|
#ifndef KERNEL /* predefined in the config case*/
|
|
# define KERNEL
|
|
#else KERNEL
|
|
# define LINKED_IN /* prevent errors in make depend*/
|
|
#endif KERNEL
|
|
|
|
/* #includes below */
|
|
/*
|
|
#include <stdio.h>
|
|
*/
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/file.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/uio.h>
|
|
#include <machine/psl.h>
|
|
#include <sun/openprom.h>
|
|
#include <sun/vddrv.h>
|
|
#include <sys/map.h>
|
|
#include <sys/debug.h>
|
|
#include <sundev/mbvar.h>
|
|
#include <sun/autoconf.h>
|
|
#include "bpp_io.h"
|
|
#include "bpp_reg.h"
|
|
#include "bpp_var.h"
|
|
|
|
|
|
/* structure definitions below */
|
|
|
|
static struct bpp_transfer_parms bpp_default_transfer_parms = {
|
|
BPP_ACK_BUSY_HS, /* read_handshake */
|
|
1000, /* read_setup_time - 1 us */
|
|
1000, /* read_strobe_width - 1 us */
|
|
60, /* read_timeout - 1 minute */
|
|
BPP_ACK_HS, /* write_handshake */
|
|
1000, /* write_setup_time - 1 us */
|
|
1000, /* write_strobe_width - 1 us */
|
|
60, /* write_timeout - 1 minute */
|
|
};
|
|
|
|
static struct bpp_pins bpp_default_pins = {
|
|
0, /* output pins */
|
|
0, /* input pins */
|
|
};
|
|
|
|
static struct bpp_error_status bpp_default_error_stat = {
|
|
0, /* no timeout */
|
|
0, /* no bus error */
|
|
0, /* no pin status set */
|
|
};
|
|
|
|
#ifdef NO_BPP_HW
|
|
/* buffer to simulate hardware */
|
|
static u_char bpp_hard_buf[FAKE_BUF_SIZE];
|
|
/* hardware mimicing registers */
|
|
static struct bpp_regs bpp_fake_regs[MAX_NBPP];
|
|
/* default register values */
|
|
static struct bpp_regs bpp_def_regs;
|
|
#endif NO_BPP_HW
|
|
|
|
|
|
/* static variable declarations below */
|
|
|
|
/* array of pointers to unit structs */
|
|
static struct bpp_unit *bpp_units[MAX_NBPP];
|
|
static u_char nbpp = 0; /* number of units in system */
|
|
static int sys_clock = 0; /* system clock freq in MHz */
|
|
static int clock_cycle = 0; /* system clock period in nsec */
|
|
static int sbus_clock = 0; /* sbus clock freq prop in MHz */
|
|
static int sbus_cycle = 0; /* sbus clock prop period in nsec */
|
|
static int sbus_clock_cycle = 0; /* actual sbus clock period in nsec */
|
|
extern int hz; /* clock ticks per second */
|
|
extern struct map *dvmamap; /* pointer to DVMA resource map */
|
|
|
|
/* Declarations for bpp_fakeout hardware simulation support */
|
|
static u_int sim_byte_count = 0; /* bytes desired to be transferred */
|
|
/* - this will be set by bpp_ioctl() */
|
|
/* for testing error conditions */
|
|
static int err_code = 0; /* error code for simulating */
|
|
/* hardware errors */
|
|
static enum trans_type { /* state variable for last transfer */
|
|
read_trans,
|
|
write_trans
|
|
} last_trans;
|
|
|
|
/*
|
|
* bpp_printlevel controls what level of messages seen
|
|
* on the console. LOG_DEBUG is the most verbose.
|
|
* Do not set bpp_printlevel below LOG_CRIT or you
|
|
* will not even see error messages from the bpp driver.
|
|
* bpp_print depends on these levels being legal values to pass
|
|
* to the kernel log() function.
|
|
* These are level defs from <sys/syslog.h>, and are included
|
|
* here for reference.
|
|
*
|
|
* int bpp_printlevel = LOG_ERR; error conditions
|
|
* int bpp_printlevel = LOG_WARNING; warning conditions
|
|
* int bpp_printlevel = LOG_NOTICE; normal but significant condition
|
|
* int bpp_printlevel = LOG_INFO; informational
|
|
* int bpp_printlevel = LOG_DEBUG; debug-level messages
|
|
*/
|
|
|
|
#ifdef BPP_DEBUG
|
|
int bpp_printlevel = LOG_DEBUG; /* debug-level messages */
|
|
#else BPP_DEBUG
|
|
int bpp_printlevel = LOG_ERR; /* error conditions only */
|
|
#endif BPP_DEBUG
|
|
|
|
/* private procedure declarations below */
|
|
/* Autoconfig Declarations */
|
|
static int bpp_identify();
|
|
static int bpp_attach();
|
|
#ifdef notdef
|
|
int bpp_init(); /* bpp_init MUST be global or modload will fail */
|
|
#endif notdef
|
|
/* Driver function Declarations */
|
|
/*
|
|
* bpp_open, bpp_close, bpp_read, bpp_write and
|
|
* bpp_ioctl MUST be global to build this driver into a kernel
|
|
*/
|
|
int bpp_open();
|
|
int bpp_close();
|
|
int bpp_read();
|
|
int bpp_write();
|
|
int bpp_ioctl();
|
|
static int bpp_poll();
|
|
static void bpp_intr();
|
|
static bpp_strategy();
|
|
static void bpp_minphys();
|
|
|
|
/* Utility Function Declarations */
|
|
static bpp_transfer_timeout();
|
|
static void see_devinfo();
|
|
static char * get_property();
|
|
static int get_num_property();
|
|
static int check_bpp_registers();
|
|
static void set_dss_dsw();
|
|
static u_short check_write_params();
|
|
static u_short check_read_params();
|
|
static u_short check_read_pins();
|
|
static u_short check_write_pins();
|
|
static void read_outpins();
|
|
#ifdef notdef
|
|
static void bpp_decommission();
|
|
#endif notdef
|
|
static void start_critical();
|
|
static void end_critical();
|
|
static int bpp_transfer_failed();
|
|
#ifdef NO_BPP_HW
|
|
static void bpp_fakeout();
|
|
static void bpp_stuff_fregs();
|
|
#endif NO_BPP_HW
|
|
static void bpp_print_regs();
|
|
static void bpp_print();
|
|
|
|
caddr_t kmem_zalloc();
|
|
|
|
/*
|
|
* The bpp_ops struct enables the kernel to find the
|
|
* bpp_identify and bpp_attach routines. Note
|
|
* that the name of this structure is REQUIRED to be "<drivername>_ops"
|
|
* if the driver is to be configured into the kernel.
|
|
*/
|
|
struct dev_ops bpp_ops =
|
|
{
|
|
1, /* revision number */
|
|
bpp_identify, /* confirm device ID */
|
|
bpp_attach, /* attach routine of driver */
|
|
};
|
|
|
|
#ifdef notdef
|
|
/*
|
|
* Character device entry points for this driver.
|
|
* These are in cdevsw[] in sun/conf.c for preconfigured drivers.
|
|
*/
|
|
struct cdevsw bpp_cdevsw =
|
|
{
|
|
bpp_open, bpp_close, bpp_read, bpp_write,
|
|
bpp_ioctl, nulldev, seltrue, 0, 0
|
|
};
|
|
|
|
/*
|
|
* The bpp_drv structure provides the linkage between the vd driver
|
|
* (for loadable drivers) and the dev_ops structure for this driver
|
|
* (bpp_ops).
|
|
*/
|
|
struct vdldrv bpp_drv = {
|
|
VDMAGIC_DRV, /* Drv_magic */
|
|
#ifndef BPP_DEBUG
|
|
"bpp 1.1 @(#) 1.1 94/10/31", /* *Drv_name */
|
|
#else BPP_DEBUG
|
|
"bpp 1.1 Debug @(#) 1.1 94/10/31", /* *Drv_name */
|
|
#endif BPP_DEBUG
|
|
&bpp_ops, /* *Drv_dev_ops */
|
|
NULL, /* *Drv_bdevsw */
|
|
&bpp_cdevsw, /* *Drv_cdevsw */
|
|
0, /* Drv_blockmajor */
|
|
0 /* Drv_charmajor */
|
|
};
|
|
#endif notdef
|
|
|
|
/* Autoconfig Support Functions */
|
|
|
|
/*
|
|
* bpp_identify()
|
|
* Determine whether this is the right driver to work with the named device.
|
|
* Count the number of times a match occurs - this is the number of
|
|
* devices actually in the system.
|
|
*/
|
|
static int
|
|
bpp_identify(name)
|
|
char *name;
|
|
{
|
|
bpp_print(LOG_INFO, "Entering bpp_identify, name: %s\n",
|
|
name);
|
|
if (strcmp(name, BPP_PROM_NAME) == 0)
|
|
{
|
|
/*
|
|
* Sanity check, perhaps paranoid. Limit the number of
|
|
* units that we accept. Any not accepted cannot be
|
|
* attached, and therefore cannot be serviced.
|
|
*/
|
|
if (nbpp < MAX_NBPP)
|
|
{
|
|
nbpp++;
|
|
bpp_print(LOG_INFO,
|
|
"Leaving bpp_identify, ACCEPTED (#%d)\n",
|
|
nbpp);
|
|
return (1); /* Good match; device accepted. */
|
|
} else {
|
|
bpp_print(LOG_ERR,
|
|
"bpp_identify: too many bpps (%d).\n",
|
|
nbpp);
|
|
}
|
|
}
|
|
bpp_print(LOG_INFO, "Leaving bpp_identify, (REJECTED)\n");
|
|
return (0); /* No match or over limit; the device rejected. */
|
|
}
|
|
|
|
/*
|
|
* bpp_attach()
|
|
*
|
|
* Map the bpp device registers into kernel virtual memory.
|
|
* Add the bpp driver to the level 2 interrupt chain.
|
|
* Initialize the bpp portion of the zebra card.
|
|
* Turn on the interrupts.
|
|
* Allocate unit structures based on the number of units (nbpp).
|
|
*/
|
|
static int
|
|
bpp_attach(devinfo_p)
|
|
register struct dev_info *devinfo_p;
|
|
{
|
|
struct dev_reg *dev_reg_p;
|
|
struct dev_intr *dev_intr_p;
|
|
u_int counter;
|
|
int myslot;
|
|
u_int unit_no; /* attaching unit's number */
|
|
int cpu_frequency; /* system clock frequency (in cycles) */
|
|
int sbus_frequency; /* sbus clock frequency (in cycles) */
|
|
int interrupt_level; /* real interrupt level used */
|
|
int i;
|
|
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
struct bpp_regs *bpp_regs_p;
|
|
|
|
static int unit_count = 0; /*
|
|
* The static variable unit_count
|
|
* is the source of unit numbers to
|
|
* assign to the devinfo struct.
|
|
* It is the responsibility of the
|
|
* attach routine to fill in the value
|
|
* of devinfo_p->devi_unit.
|
|
*/
|
|
|
|
bpp_print(LOG_INFO, "Entering bpp_attach: devinfo_p: 0x%x\n",
|
|
devinfo_p);
|
|
|
|
/* Initialize bpp_units struct before allocating any unit structs */
|
|
bpp_print(LOG_INFO, "Initializing units array.\n");
|
|
if (unit_count == 0) { /* first time attach is called */
|
|
for (i = 0; i < MAX_NBPP; i++)
|
|
bpp_units[i] = NULL;
|
|
}
|
|
|
|
if (devinfo_p == NULL) {
|
|
/*
|
|
* Make the attach fail. A physical device should
|
|
* have been found by the prom, and therefore have a
|
|
* devinfo pointer. The code below requires it.
|
|
* This is a "can't happen".
|
|
*/
|
|
bpp_print(LOG_ERR,
|
|
"bpp_attach: devinfo_p is NULL!\n");
|
|
goto attach_failed;
|
|
}
|
|
|
|
|
|
#ifdef notdef
|
|
OLD METHOD
|
|
/*
|
|
* Allocate the bpp_units array based on the number of
|
|
* units (nbpp) counted by bpp_identify.
|
|
* The unit structs are allocated as zeroed memory.
|
|
*/
|
|
if (bpp_units == NULL) { /* not allocated yet - 1st time */
|
|
bpp_units =
|
|
(struct bpp_unit *)kmem_zalloc(nbpp * sizeof (struct bpp_unit));
|
|
}
|
|
#endif notdef
|
|
|
|
if (bpp_units[unit_count] != NULL) {
|
|
/* Massive error - unit_no is out of sync? */
|
|
bpp_print(LOG_ERR,
|
|
"bpp_attch: unit %d struct pointer not NULL!\n",
|
|
unit_count);
|
|
goto attach_failed;
|
|
}
|
|
|
|
/*
|
|
* Allocate a unit structure for this unit.
|
|
* Each bpp_unit struct is allocated as zeroed memory.
|
|
* Store away its address for future use.
|
|
*/
|
|
bpp_print(LOG_INFO, "Allocating unit struct for unit %d.\n",
|
|
unit_count);
|
|
bpp_units[unit_count] =
|
|
(struct bpp_unit *)kmem_zalloc(sizeof (struct bpp_unit));
|
|
/*
|
|
* Initialize the unit structures. The unit structure for
|
|
* each unit is initialized when bpp_attach is called for that unit.
|
|
*/
|
|
|
|
/*
|
|
* Assign a unit structure and fill in the value of
|
|
* devinfo_p->devi_unit.
|
|
*/
|
|
unit_no = unit_count++;
|
|
devinfo_p->devi_unit = unit_no;
|
|
|
|
/* Make sure we're not in a slave-only slot */
|
|
if ((myslot = slaveslot(devinfo_p->devi_reg->reg_addr)) >= 0) {
|
|
bpp_print(LOG_ERR,
|
|
"bpp unit %d: NOT used - SBus slot %d is slave only.\n",
|
|
unit_no, myslot);
|
|
goto attach_failed;
|
|
}
|
|
|
|
/* assign a pointer to this unit's state struct */
|
|
bpp_p = bpp_units[unit_no];
|
|
/*
|
|
* Store the devinfo pointer in the unit structure. We will need
|
|
* it for decommissioning. Initialize the saved spl in the
|
|
* unit structure for assertion checking for critical sections.
|
|
* Initialize the pointer to the transfer parameters structure
|
|
* for this unit.
|
|
*/
|
|
bpp_p->devinfo_p = devinfo_p;
|
|
bpp_p->saved_spl = (-1);
|
|
|
|
/*
|
|
* Initialize the transfer parameters structure for this unit.
|
|
*/
|
|
bpp_p->transfer_parms = bpp_default_transfer_parms;
|
|
|
|
/*
|
|
* Initialize the control pins structure for this unit.
|
|
*/
|
|
bpp_p->pins = bpp_default_pins;
|
|
|
|
/*
|
|
* Initialize the error status structure for this unit.
|
|
* Initialize the timeout status byte for this unit.
|
|
*/
|
|
bpp_p->error_stat = bpp_default_error_stat;
|
|
bpp_p->timeouts = NO_TIMEOUTS;
|
|
bpp_print(LOG_INFO, "Timeout block is 0x%x.\n", bpp_p->timeouts);
|
|
/* clear the buf struct before using it */
|
|
bzero ((char*)&(bpp_p->buf), sizeof (struct buf));
|
|
/*
|
|
* Look at the properties in the devinfo struct. Demonstrate
|
|
* how to get other properties by getting the value of the
|
|
* "name" property the hard way.
|
|
*/
|
|
#ifdef DEBUG
|
|
(void) see_devinfo(devinfo_p);
|
|
#endif DEBUG
|
|
(void) get_property(devinfo_p, "name", "NO NAME PROPERTY?");
|
|
/*
|
|
* Check that the clock frequency in the boot prom is in
|
|
* a sensible range. If it isn't, the math used when setting
|
|
* the transfer parameters strobe and width times will
|
|
* fail. Flag the future problem here rather than in the ioctl.
|
|
*/
|
|
/*
|
|
* Sparcstation and its clones only have a clock-frequency
|
|
* property associated with the top_devinfo node. This
|
|
* means that the system clock and sbus clock are the
|
|
* same frequency. Calvin runs the sbus at a slower
|
|
* speed than the cpu. Try to find a clock-frequency
|
|
* property in my parent (sbus) node. If none there,
|
|
* then try the cpu node (top_devinfo).
|
|
*/
|
|
sbus_frequency = get_num_property(devinfo_p->devi_parent,
|
|
"clock-frequency", 1000000);
|
|
sbus_clock = sbus_frequency/1000000;
|
|
if (sbus_clock >= 10 && sbus_clock <= 25) {
|
|
bpp_print(LOG_INFO, "Sbus clock is %d MHz.\n", sbus_clock);
|
|
/* calculate clock period (in nsec) */
|
|
sbus_cycle = (1000 / sbus_clock);
|
|
sbus_clock_cycle = sbus_cycle;
|
|
} else {
|
|
bpp_print(LOG_WARNING, "Sbus frequency out of range.\n");
|
|
/* try for cpu clock frequency instead */
|
|
cpu_frequency = get_num_property(top_devinfo,
|
|
"clock-frequency", 1000000);
|
|
sys_clock = cpu_frequency/1000000;
|
|
if (sys_clock >= 10 && sys_clock <= 25) {
|
|
bpp_print(LOG_WARNING,
|
|
"Using CPU clock property for SBus.\n");
|
|
bpp_print(LOG_INFO,
|
|
"Sbus clock is %d MHz.\n", sys_clock);
|
|
/* calculate clock period (in nsec) */
|
|
clock_cycle = (1000 / sys_clock);
|
|
sbus_clock_cycle = clock_cycle;
|
|
} else {
|
|
bpp_print(LOG_ERR,
|
|
"Warning! System Clock freq out of range!\n");
|
|
goto attach_failed;
|
|
}
|
|
}
|
|
bpp_print(LOG_INFO, "Sbus Clock period is %d nsec.\n",
|
|
sbus_clock_cycle);
|
|
|
|
/*
|
|
* Map in any device registers. The zebra parallel section
|
|
* has only one register area. I'm assuming devinfo_p->nreg = 1.
|
|
*/
|
|
dev_reg_p = devinfo_p->devi_reg;
|
|
/*
|
|
* dev_reg_p may be NULL, but only if devi_nreg is zero.
|
|
*/
|
|
if (dev_reg_p == NULL)
|
|
{
|
|
/*
|
|
* A "can't happen".
|
|
*/
|
|
bpp_print(LOG_ERR,
|
|
"bpp_attach: dev_reg_p is NULL!\n");
|
|
goto attach_failed;
|
|
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
#ifndef NO_BPP_HW
|
|
/* The "real" hardware case */
|
|
/*
|
|
* Map the structure into kernel virtual space.
|
|
*/
|
|
bpp_p->bpp_regs_p =
|
|
(struct bpp_regs *) map_regs(dev_reg_p->reg_addr,
|
|
sizeof (struct bpp_regs),
|
|
dev_reg_p->reg_bustype);
|
|
bpp_regs_p = bpp_p->bpp_regs_p;
|
|
bpp_print(LOG_INFO, "After mapping registers, int_cntl = 0x%x\n",
|
|
bpp_regs_p->int_cntl);
|
|
/*
|
|
* Check that the size of the register set is the same as the
|
|
* size of the structure that overlays it.
|
|
*/
|
|
if (sizeof (struct bpp_regs) != dev_reg_p->reg_size)
|
|
{
|
|
bpp_print(LOG_ERR,
|
|
"bpp_attach, unit %d, structure size mismatch!\n", unit_no);
|
|
bpp_print(LOG_ERR,
|
|
"Struct (%d bytes) != reg (%d bytes)!\n",
|
|
sizeof (struct bpp_regs), dev_reg_p->reg_size);
|
|
goto attach_failed;
|
|
}
|
|
|
|
|
|
#else NO_BPP_HW
|
|
/* use allocated memory instead of reg_addr if no real hardware */
|
|
/* The "faked" hardware case */
|
|
/* bustype SPO_VIRTUAL */
|
|
/*
|
|
* Map the (fake) structure into kernel virtual space.
|
|
*/
|
|
bpp_p->bpp_regs_p =
|
|
(struct bpp_regs *) map_regs(&bpp_fake_regs[unit_no],
|
|
sizeof (struct bpp_regs),
|
|
SPO_VIRTUAL);
|
|
bpp_print(LOG_INFO,
|
|
"Mapped fake registers: %x mapped to %x\n",
|
|
&bpp_fake_regs[unit_no], bpp_p->bpp_regs_p);
|
|
/* stuff initial vals into fake registers */
|
|
bpp_stuff_fregs(unit_no);
|
|
#endif NO_BPP_HW
|
|
|
|
if (bpp_p->bpp_regs_p == NULL) {
|
|
bpp_print(LOG_ERR,
|
|
"bpp_attach unit %d: map_regs failed!\n", unit_no);
|
|
goto attach_failed;
|
|
}
|
|
if (check_bpp_registers(unit_no)) { /* registers don't seem right */
|
|
bpp_print(LOG_ERR,
|
|
"bpp_attach unit %d: register check failed!\n", unit_no);
|
|
goto attach_failed;
|
|
}
|
|
|
|
/*
|
|
* For devices that issue interrupts, the driver must install
|
|
* itself on the interrupt chain for each level that the hardware
|
|
* can interrupt at.
|
|
* This must be done before expecting to receive any interrupts.
|
|
*/
|
|
dev_intr_p = devinfo_p->devi_intr;
|
|
/*
|
|
* dev_intr_p may be NULL, but only if devi_nintr is zero.
|
|
*/
|
|
for (counter = devinfo_p->devi_nintr; counter > 0;
|
|
--counter, ++dev_intr_p)
|
|
{
|
|
if (dev_intr_p == NULL)
|
|
{
|
|
/*
|
|
* A "can't happen".
|
|
*/
|
|
bpp_print(LOG_ERR,
|
|
"bpp_attach: dev_intr_p is NULL!\n");
|
|
goto attach_failed;
|
|
|
|
/*NOTREACHED*/
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Convert the hardware interrupt priority level (0-15)
|
|
* into a psr for critical sections of code. If there
|
|
* is more than 1 interrupt level, use the highest one.
|
|
* Note that the units may differ.
|
|
*/
|
|
interrupt_level = dev_intr_p->int_pri;
|
|
if (ipltospl(interrupt_level) >
|
|
bpp_p->interrupt_pri)
|
|
{
|
|
bpp_p->interrupt_pri =
|
|
ipltospl(interrupt_level);
|
|
bpp_print(LOG_INFO,
|
|
"Set interrupt_pri to 0x%x for unit %d.\n",
|
|
bpp_p->interrupt_pri, unit_no);
|
|
}
|
|
|
|
|
|
/*
|
|
* If the device can do dma, then the dma routines need
|
|
* to know the highest priority that the device may
|
|
* interrupt at. The routine adddma is used to gather
|
|
* this information.
|
|
*/
|
|
adddma(interrupt_level);
|
|
|
|
#ifndef NO_BPP_HW
|
|
|
|
start_critical(unit_no);
|
|
|
|
/*
|
|
* Addintr() does the registration. Duplicate
|
|
* registrations (such as from several units) are
|
|
* ignored except for data collection for vmstat.
|
|
* Addintr() panics if this registration would make
|
|
* too many devices on the interrupt chain. Sorry.
|
|
*/
|
|
(void) addintr(interrupt_level, bpp_poll,
|
|
devinfo_p->devi_name, devinfo_p->devi_unit);
|
|
|
|
end_critical(unit_no);
|
|
|
|
bpp_print(LOG_INFO,
|
|
"Installed bpp_poll: unit %d at SBus priority 0x%x\n",
|
|
unit_no, interrupt_level);
|
|
#endif NO_BPP_HW
|
|
}
|
|
|
|
/*
|
|
* The routine report_dev() writes a line on the console and system
|
|
* log file to announce the attachment of the driver. This will
|
|
* happen either at boot or modload time, one line for each unit.
|
|
* It is nice to call report_dev before contacting the device
|
|
* if possible so that if something horrible happens there is
|
|
* a record of what was being attempted.
|
|
*/
|
|
report_dev(devinfo_p);
|
|
|
|
/*
|
|
* Perform device initialization.
|
|
*/
|
|
|
|
bpp_regs_p->dma_csr |= BPP_RESET_BPP;
|
|
bpp_regs_p->dma_csr &= ~BPP_RESET_BPP;
|
|
bpp_regs_p->dma_csr |= BPP_TC_INTR_DISABLE;
|
|
|
|
/*
|
|
* Set up the polarities for the ERR, SLCT PE, and BUSY interrupts.
|
|
* Changing the polarities could cause a stray interrupt,
|
|
* so clear them here.
|
|
* These polarities are handshake dependent.
|
|
* This setup corresponds to the default handshakes.
|
|
*/
|
|
bpp_print(LOG_INFO, "Before setting polarities, int_cntl = 0x%x\n",
|
|
bpp_regs_p->int_cntl);
|
|
bpp_regs_p->int_cntl |= BPP_ERR_IRP; /* ERR rising edge */
|
|
bpp_regs_p->int_cntl |= BPP_SLCT_IRP; /* SLCT rising edge */
|
|
/* SLCT+ means off-line */
|
|
bpp_regs_p->int_cntl &= ~BPP_PE_IRP; /* PE falling edge */
|
|
bpp_regs_p->int_cntl |= BPP_BUSY_IRP; /* BUSY rising edge */
|
|
/* clear any stray interrupts */
|
|
bpp_regs_p->int_cntl |= BPP_ALL_IRQS;
|
|
bpp_print(LOG_INFO, "After setting polarities, int_cntl = 0x%x\n",
|
|
bpp_regs_p->int_cntl);
|
|
|
|
/* Last of all, turn on interrupts */
|
|
bpp_p->bpp_regs_p->dma_csr |= BPP_INT_EN;
|
|
|
|
if (bpp_p->bpp_regs_p->op_config
|
|
& BPP_VERSATEC_INTERLOCK) { /* versatec connector absent */
|
|
/* block versatec handshake modes */
|
|
bpp_p->versatec_allowed = 0;
|
|
bpp_print(LOG_INFO, "Versatec connector absent.\n");
|
|
} else {
|
|
/* allow versatec handshake modes */
|
|
bpp_p->versatec_allowed = 1;
|
|
bpp_print(LOG_INFO, "Versatec connector present.\n");
|
|
}
|
|
bpp_print_regs (unit_no, "At end of attach:\n");
|
|
bpp_print(LOG_INFO, "Leaving bpp_attach: unit %d\n", unit_no);
|
|
|
|
bpp_print(LOG_INFO, "ATTACH SUCCEEDED.\n");
|
|
return (0);
|
|
|
|
attach_failed:
|
|
bpp_print(LOG_INFO, "ATTACH FAILED.\n");
|
|
bpp_p->open_inhibit = 1; /* prevent open */
|
|
return (-1);
|
|
}
|
|
|
|
|
|
/*
|
|
* bpp_init -- Support for a loadable device driver.
|
|
* Required by the utilities modload modstat & modunload.
|
|
* The VDLOAD case is called (indirectly) from modload.
|
|
*
|
|
* The VDUNLOAD case is called (indirectly) from modunload.
|
|
*
|
|
* The VDSTAT case is called (indirectly) from modstat.
|
|
*
|
|
*/
|
|
|
|
#ifdef notdef
|
|
int
|
|
bpp_init(command, vdp, vdi, vds)
|
|
int command; /* Identify why module called */
|
|
struct vddrv *vdp; /* Pointer to kernel's structure*/
|
|
struct vdioctl_load *vdi; /* pointer to struct vdioctl_* */
|
|
struct vdstat *vds; /* ID and status information */
|
|
{
|
|
struct vdconf *vdconf_p;
|
|
char *string_p;
|
|
u_int bpp_unit_no;
|
|
int status = 0;
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
|
|
bpp_print(LOG_INFO,
|
|
"-----------------(%d)--------------- \n", vdp->vdd_id);
|
|
bpp_print(LOG_INFO,
|
|
"Entering bpp_init, vdp 0x%x, vdi 0x%x, vds 0x%x.\n",
|
|
vdp, vdi, vds);
|
|
switch (command)
|
|
{
|
|
case VDLOAD:
|
|
bpp_print(LOG_DEBUG, "Command is VDLOAD.\n");
|
|
/*
|
|
* For the driver of an SBus device there are generally
|
|
* only two things to do in the VDLOAD step. The first is
|
|
* to get any available configuration information that may
|
|
* have been passed in from a modload configuration file.
|
|
* The second is to fill in the linkage pointer to the
|
|
* vdldrv structure. Most initialization of the driver or
|
|
* it's devices must wait until the driver's attach()
|
|
* routine is called because this is when (1) the count
|
|
* of physical devices is known as all calls to the
|
|
* driver's identify() routine have taken place, and
|
|
* (2) the dev_info pointer and therefore the devices
|
|
* themselves become available.
|
|
*/
|
|
|
|
/*
|
|
* Get the configuration info from the vd driver if any
|
|
* was passed in from modload. Some of this code cannot
|
|
* be useful to the bpp driver. It is included for
|
|
* illustration.
|
|
*/
|
|
if (vdi != NULL)
|
|
{
|
|
bpp_print(LOG_DEBUG, "vdi_id: %d\n",
|
|
vdi->vdi_id);
|
|
bpp_print(LOG_DEBUG, "vdi_status: %d\n",
|
|
vdi->vdi_status);
|
|
bpp_print(LOG_DEBUG, "vdi_mmapaddr: 0x%x\n",
|
|
vdi->vdi_mmapaddr);
|
|
bpp_print(LOG_DEBUG, "vdi_symtabsize: %d\n",
|
|
vdi->vdi_symtabsize);
|
|
bpp_print(LOG_DEBUG, "vdi_userconf: 0x%x\n",
|
|
vdi->vdi_userconf);
|
|
if (vdi->vdi_userconf != NULL)
|
|
{
|
|
vdconf_p = vdi->vdi_userconf;
|
|
if (vdconf_p->vdc_type == VDCEND)
|
|
{
|
|
bpp_print(LOG_DEBUG,
|
|
"No config info available.\n");
|
|
}
|
|
|
|
while (vdconf_p->vdc_type != VDCEND)
|
|
{
|
|
switch (vdconf_p->vdc_type)
|
|
{
|
|
case VDCCONTROLLER:
|
|
|
|
string_p =
|
|
"(VDCCONTROLLER)";
|
|
break;
|
|
|
|
case VDCDEVICE:
|
|
string_p =
|
|
"(VDCDEVICE)";
|
|
break;
|
|
|
|
case VDCBLOCKMAJOR:
|
|
string_p =
|
|
"(VDCBLOCKMAJOR)";
|
|
break;
|
|
|
|
case VDCCHARMAJOR:
|
|
string_p =
|
|
"(VDCCHARMAJOR)";
|
|
break;
|
|
|
|
case VDCSYSCALLNUM:
|
|
string_p =
|
|
"(VDCSYSCALLNUM)";
|
|
break;
|
|
|
|
default:
|
|
bpp_print(LOG_INFO,
|
|
"vdconf switch error!\n");
|
|
string_p =
|
|
"(SWITCH ERROR!)";
|
|
break;
|
|
}
|
|
|
|
bpp_print(LOG_DEBUG,
|
|
"vdc_type: %d %s %s 0x%x\n",
|
|
vdconf_p->vdc_type,
|
|
string_p, "vdc_data:",
|
|
vdconf_p->vdc_data);
|
|
|
|
/* Move to next element of array */
|
|
vdconf_p++;
|
|
}
|
|
} else {
|
|
bpp_print(LOG_INFO,
|
|
"No config info, vdi_userconf is NULL\n");
|
|
}
|
|
|
|
} else {
|
|
bpp_print(LOG_INFO,
|
|
"No config info, vdi is NULL\n");
|
|
}
|
|
|
|
/*
|
|
* Configure the bpp driver from the available
|
|
* information. If no information was passed in from
|
|
* modload, then use the default info from this file.
|
|
*/
|
|
|
|
/*
|
|
* Fill in pointer to the vdldrv structure,
|
|
* which must be already initialized at this point.
|
|
*/
|
|
vdp->vdd_vdtab = (struct vdlinkage *)&bpp_drv;
|
|
|
|
bpp_print(LOG_DEBUG, "Done VDLOAD.\n");
|
|
break;
|
|
|
|
case VDUNLOAD:
|
|
bpp_print(LOG_DEBUG, "Command is VDUNLOAD.\n");
|
|
/*
|
|
* Check if the driver is in a condition to allow safe
|
|
* unloading. If it is, then cleanly shut the driver down,
|
|
* clean up, and release all dynamically allocated space.
|
|
* BE SURE to remove the driver from any interrupt chains
|
|
* that it is on.
|
|
*/
|
|
for (bpp_unit_no = 0; bpp_unit_no < nbpp;
|
|
bpp_unit_no++)
|
|
{
|
|
bpp_p = bpp_units[bpp_unit_no];
|
|
/*
|
|
* Inhibit further opens on the unit.
|
|
*/
|
|
bpp_p->open_inhibit = 1;
|
|
|
|
/*
|
|
* If any any unit is open, cannot unload.
|
|
*/
|
|
if (bpp_p->unit_open != 0)
|
|
{
|
|
status = EBUSY;
|
|
}
|
|
/*
|
|
* should check that the DMA engine
|
|
* is idle before allowing unloading.
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* If it looks good so far, turn off interrupts, remove
|
|
* registration of interrupt vector, release all memory,
|
|
* and permit unload.
|
|
*/
|
|
if (status == 0)
|
|
{
|
|
(void) bpp_decommission();
|
|
} else {
|
|
/*
|
|
* Refuse unload, return to business as usual.
|
|
*/
|
|
for (bpp_unit_no = 0; bpp_unit_no < nbpp;
|
|
bpp_unit_no++)
|
|
{
|
|
bpp_p = bpp_units[bpp_unit_no];
|
|
/*
|
|
* Release the open inhibits.
|
|
*/
|
|
bpp_p->open_inhibit = 0;
|
|
}
|
|
}
|
|
|
|
bpp_print(LOG_DEBUG, "Done VDUNLOAD.\n");
|
|
break;
|
|
|
|
case VDSTAT:
|
|
bpp_print(LOG_DEBUG, "Command is VDSTAT.\n");
|
|
|
|
/* DEVICE SPECIFIC */
|
|
|
|
bpp_print(LOG_DEBUG, "Done VDSTAT.\n");
|
|
break;
|
|
|
|
default:
|
|
bpp_print(LOG_ERR,
|
|
"bpp_init: unknown command 0x%x\n", command);
|
|
status = EINVAL;
|
|
break;
|
|
}
|
|
|
|
bpp_print(LOG_INFO,
|
|
"Leaving bpp_init, status (errno): %d.\n", status);
|
|
return (status);
|
|
}
|
|
/*
|
|
* Turn off interrupts, remove registration of all interrupt vectors,
|
|
* and release all memory. Note that the info for the interrupt levels
|
|
* came from the Fcode prom on each device, and therefore may vary from
|
|
* unit to unit. Be sure to remove all interrupt vectors, or the kernel
|
|
* will bus error if the device interrupts while the driver is unloaded.
|
|
*
|
|
*/
|
|
static void
|
|
bpp_decommission()
|
|
{
|
|
u_int bpp_unit_no;
|
|
int i;
|
|
int counter;
|
|
struct dev_intr *dev_intr_p;
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
struct bpp_regs *bpp_regs_p;
|
|
|
|
bpp_print(LOG_INFO, "Entering bpp_decommission.\n");
|
|
|
|
/*
|
|
* Turn off interrupts for each unit.
|
|
*/
|
|
for (bpp_unit_no = 0; bpp_unit_no < nbpp; bpp_unit_no++) {
|
|
bpp_p = bpp_units[bpp_unit_no];
|
|
bpp_regs_p = bpp_p->bpp_regs_p;
|
|
if (bpp_regs_p->dma_csr & BPP_ENABLE_DMA) {
|
|
/* was transferring */
|
|
bpp_print(LOG_ERR,
|
|
"ERROR: bpp unload of unit %d while DMA active!\n",
|
|
bpp_unit_no);
|
|
/* turn off DMA and byte count */
|
|
bpp_regs_p->dma_csr &= ~BPP_ENABLE_DMA;
|
|
bpp_regs_p->dma_csr &= ~BPP_ENABLE_BCNT;
|
|
/* Reset PP state machine */
|
|
bpp_regs_p->op_config |= BPP_SRST;
|
|
bpp_regs_p->op_config &= ~BPP_SRST;
|
|
/* flush the cache */
|
|
bpp_regs_p->dma_csr |= BPP_FLUSH;
|
|
}
|
|
/*
|
|
* Disable the TC interrupts.
|
|
* Mask the error interrupts too.
|
|
* These shouldn't be on if we weren't transferring
|
|
* at the time, but it's safest to just turn
|
|
* them off anyway.
|
|
*/
|
|
bpp_regs_p->dma_csr |= BPP_TC_INTR_DISABLE;
|
|
bpp_regs_p->int_cntl &=
|
|
~(BPP_ERR_IRQ_EN | BPP_SLCT_IRQ_EN | BPP_PE_IRQ_EN);
|
|
/* ALL FIELDS CLEARED, idle state */
|
|
/*
|
|
* To be safer, I really should free the buf which
|
|
* was being used to do the transfer, and wait on
|
|
* a semaphore that tells me that bpp_read or
|
|
* bpp_write have returned the partial error.
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* Everything that we need to know is in the unit structures.
|
|
* From there we access the devinfo struct to find the interrupt
|
|
* levels that we are (might be) installed on. Since duplicate
|
|
* registrations with addintr() are ignored, duplicate
|
|
* unregistrations can not be considered an error.
|
|
* March through and do everything meticulously.
|
|
* Do not take short cuts or you die.
|
|
*/
|
|
for (bpp_unit_no = 0; bpp_unit_no < nbpp; bpp_unit_no++)
|
|
{
|
|
bpp_p = bpp_units[bpp_unit_no];
|
|
start_critical(bpp_unit_no);
|
|
|
|
dev_intr_p = bpp_p->devinfo_p->devi_intr;
|
|
/*
|
|
* dev_intr_p may be NULL, but only if devi_nintr is zero.
|
|
*/
|
|
for (counter = bpp_p->devinfo_p->devi_nintr; counter > 0;
|
|
--counter, ++dev_intr_p)
|
|
{
|
|
if (dev_intr_p == NULL)
|
|
{
|
|
/*
|
|
* A "can't happen".
|
|
*/
|
|
bpp_print(LOG_ERR,
|
|
"bpp_decommission ASSERT FAILED.\n");
|
|
break;
|
|
}
|
|
/*
|
|
* In the multiple-unit case,
|
|
* on the first remintr really removes the
|
|
* interrupt routine from the poll chain.
|
|
* All subsequent attempts will generate
|
|
* a message. This is normal.
|
|
*/
|
|
#ifdef VDDRV
|
|
(void) remintr(dev_intr_p->int_pri, bpp_poll);
|
|
#endif VDDRV
|
|
}
|
|
|
|
end_critical(bpp_unit_no);
|
|
}
|
|
|
|
#ifdef notdef
|
|
OLD METHOD
|
|
/*
|
|
* Release the memory allocated for the unit structures.
|
|
*/
|
|
if ((bpp_units != NULL) && (nbpp > 0))
|
|
{
|
|
(void) kmem_free ((caddr_t)bpp_units,
|
|
(u_long)(nbpp * sizeof (struct bpp_unit)));
|
|
bpp_units = NULL;
|
|
}
|
|
#endif notdef
|
|
/*
|
|
* Release the memory allocated for the unit structures.
|
|
*/
|
|
if ((bpp_units[0] != NULL) && (nbpp > 0)) {
|
|
for (i = 0; i < nbpp; i++) {
|
|
(void) kmem_free ((caddr_t)bpp_units[i],
|
|
(u_int)(sizeof (struct bpp_unit)));
|
|
bpp_units[i] = NULL;
|
|
}
|
|
}
|
|
|
|
bpp_print(LOG_INFO, "Leaving bpp_decommission.\n");
|
|
|
|
return;
|
|
}
|
|
#endif notdef
|
|
/* Normal Device Driver routines */
|
|
|
|
/*
|
|
* Open the device.
|
|
*/
|
|
int
|
|
bpp_open(dev, openflags)
|
|
dev_t dev;
|
|
int openflags;
|
|
{
|
|
u_char unit_no;
|
|
u_short retval = 0; /* return value (errno) for system call */
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
|
|
unit_no = BPP_UNIT(dev);
|
|
bpp_p = bpp_units[unit_no];
|
|
bpp_print(LOG_INFO,
|
|
"bpp%d: Entering bpp_open, flags %d.\n", unit_no, openflags);
|
|
/*
|
|
* Check for how many are allocated. At this point
|
|
* nbpp must be set.
|
|
*/
|
|
if (unit_no >= nbpp)
|
|
{
|
|
retval = ENXIO;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Check for allocation of unit structures.
|
|
*/
|
|
if (bpp_units[unit_no] == NULL)
|
|
{
|
|
retval = ENXIO; /* attach failed ?? */
|
|
goto out;
|
|
}
|
|
|
|
start_critical(unit_no);
|
|
/*
|
|
* Only allow a single open. If this device has
|
|
* already been opened, return an error.
|
|
*/
|
|
if (bpp_p->unit_open != 0) {
|
|
bpp_print(LOG_NOTICE, "bpp%d already opened.\n", unit_no);
|
|
retval = EBUSY;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Check if the open mode requested is permissible.
|
|
*/
|
|
if (bpp_p->open_inhibit != 0) {
|
|
bpp_print(LOG_NOTICE,
|
|
"bpp%d open inhibit.\n", unit_no);
|
|
retval = ENXIO;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Mark the bpp as opened.
|
|
*/
|
|
bpp_p->unit_open = 1;
|
|
end_critical(unit_no);
|
|
|
|
/*
|
|
* Initialize the transfer parameters structure
|
|
* and initialize the control pins structure
|
|
* for this unit.
|
|
*/
|
|
bpp_p->transfer_parms = bpp_default_transfer_parms;
|
|
bpp_p->pins = bpp_default_pins;
|
|
bpp_p->openflags = openflags; /* record the open mode */
|
|
|
|
|
|
out:
|
|
bpp_print(LOG_INFO, "Leaving bpp_open, unit %d: errno %d.\n",
|
|
unit_no, retval);
|
|
return (retval);
|
|
|
|
}
|
|
|
|
/*
|
|
* Close the device.
|
|
*/
|
|
/*ARGSUSED*/
|
|
int
|
|
bpp_close(dev, openflags)
|
|
dev_t dev;
|
|
int openflags;
|
|
{
|
|
u_char unit_no;
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
|
|
unit_no = BPP_UNIT(dev);
|
|
bpp_p = bpp_units[unit_no];
|
|
bpp_print(LOG_INFO,
|
|
"Entering bpp_close, unit number %d.\n", unit_no);
|
|
|
|
/*
|
|
* Implement the close.
|
|
*/
|
|
bpp_print(LOG_INFO, "In bpp_close, Timeout block is 0x%x.\n",
|
|
bpp_p->timeouts);
|
|
if (bpp_p->timeouts) { /* any timeouts pending? */
|
|
bpp_print(LOG_INFO, "Some timeouts still pending.\n");
|
|
if (bpp_p->timeouts & TRANSFER_TIMEOUT) {
|
|
bpp_print(LOG_INFO, "Clearing transfer timeout.\n");
|
|
untimeout(bpp_transfer_timeout, (caddr_t)unit_no);
|
|
}
|
|
if (bpp_p->timeouts & FAKEOUT_TIMEOUT) {
|
|
#ifdef NO_BPP_HW
|
|
bpp_print(LOG_INFO, "Clearing fakeout timeout.\n");
|
|
untimeout(bpp_fakeout, (caddr_t) 0);
|
|
#else NO_BPP_HW
|
|
bpp_print(LOG_WARNING, "BOGUS fakeout timeout.\n");
|
|
#endif NO_BPP_HW
|
|
}
|
|
}
|
|
bpp_p->timeouts = NO_TIMEOUTS;
|
|
bpp_print(LOG_INFO, "At end of bpp_close, Timeout block is 0x%x.\n",
|
|
bpp_p->timeouts);
|
|
|
|
/*
|
|
* Mark unit closed.
|
|
*/
|
|
bpp_p->unit_open = 0;
|
|
|
|
bpp_print(LOG_INFO, "Leaving bpp_close, unit %d:\n", unit_no);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Read system call.
|
|
*/
|
|
/*ARGSUSED*/
|
|
int
|
|
bpp_read(dev, uio)
|
|
dev_t dev;
|
|
struct uio *uio;
|
|
{
|
|
u_char unit_no;
|
|
u_short retval = 0; /* return value (errno) for system call */
|
|
struct bpp_regs *bpp_regs_p;
|
|
struct bpp_transfer_parms *bpp_transfer_parms_p;
|
|
/* time to allow the scanner to change from data sink to source */
|
|
static int scan_turnaround = 1000;
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
|
|
unit_no = BPP_UNIT(dev);
|
|
bpp_p = bpp_units[unit_no];
|
|
bpp_print(LOG_INFO, "Entering bpp_read, unit number %d.\n",
|
|
unit_no);
|
|
|
|
bpp_print_regs(unit_no, "in bpp_read");
|
|
|
|
bpp_regs_p = bpp_p->bpp_regs_p;
|
|
bpp_transfer_parms_p = &bpp_p->transfer_parms;
|
|
|
|
/*
|
|
* delay to allow for scanning write/read turnaround
|
|
*/
|
|
|
|
if (last_trans == write_trans) {
|
|
DELAY(scan_turnaround);
|
|
}
|
|
|
|
/*
|
|
* Set the handshake bits
|
|
*/
|
|
|
|
start_critical(unit_no);
|
|
/*
|
|
* make sure the memory clear operation is turned off
|
|
*/
|
|
bpp_regs_p->op_config &= ~BPP_EN_MEM_CLR;
|
|
|
|
switch (bpp_transfer_parms_p->read_handshake) {
|
|
case BPP_NO_HS:
|
|
bpp_print(LOG_INFO, "BPP_NO_HS case\n");
|
|
bpp_regs_p->op_config &= ~(BPP_ACK_OP | BPP_BUSY_OP);
|
|
bpp_regs_p->op_config |= (BPP_DS_BIDIR | BPP_BUSY_BIDIR);
|
|
break;
|
|
case BPP_ACK_HS:
|
|
bpp_print(LOG_INFO, "BPP_ACK_HS case\n");
|
|
bpp_regs_p->op_config &= ~BPP_BUSY_OP;
|
|
bpp_regs_p->op_config |= BPP_ACK_OP;
|
|
bpp_regs_p->op_config |=
|
|
(BPP_DS_BIDIR | BPP_ACK_BIDIR | BPP_BUSY_BIDIR);
|
|
break;
|
|
case BPP_BUSY_HS:
|
|
case BPP_HSCAN_HS:
|
|
bpp_print(LOG_INFO, "BPP_BUSY_HS case\n");
|
|
bpp_regs_p->op_config |= BPP_BUSY_OP;
|
|
bpp_regs_p->op_config &= ~BPP_ACK_OP;
|
|
bpp_regs_p->op_config |= (BPP_DS_BIDIR | BPP_BUSY_BIDIR);
|
|
break;
|
|
case BPP_ACK_BUSY_HS:
|
|
bpp_print(LOG_INFO, "BPP_ACK_BUSY_HS case\n");
|
|
bpp_regs_p->op_config |= (BPP_BUSY_OP | BPP_ACK_OP);
|
|
bpp_regs_p->op_config |=
|
|
(BPP_DS_BIDIR | BPP_ACK_BIDIR | BPP_BUSY_BIDIR);
|
|
break;
|
|
case BPP_XSCAN_HS:
|
|
/*
|
|
*reads with the Xerox use ACK handshake
|
|
* and unidirectional operation
|
|
*/
|
|
bpp_print(LOG_INFO, "BPP_XSCAN_HS case\n");
|
|
bpp_regs_p->op_config &= ~BPP_BUSY_OP;
|
|
bpp_regs_p->op_config |= BPP_ACK_OP;
|
|
bpp_regs_p->op_config &=
|
|
~(BPP_DS_BIDIR | BPP_BUSY_BIDIR | BPP_ACK_BIDIR);
|
|
break;
|
|
case BPP_CLEAR_MEM:
|
|
bpp_print(LOG_INFO, "BPP_CLEAR_MEM case\n");
|
|
bpp_regs_p->op_config &= ~BPP_DMA_DATA;
|
|
bpp_regs_p->op_config |= BPP_EN_MEM_CLR;
|
|
break;
|
|
case BPP_SET_MEM:
|
|
bpp_print(LOG_INFO, "BPP_SET_MEM case\n");
|
|
bpp_regs_p->op_config |= BPP_DMA_DATA;
|
|
bpp_regs_p->op_config |= BPP_EN_MEM_CLR;
|
|
break;
|
|
}
|
|
/*
|
|
* The direction should not be marked until after the handshake
|
|
* bits have been set.
|
|
*/
|
|
bpp_regs_p->trans_cntl |= BPP_DIRECTION;
|
|
end_critical(unit_no);
|
|
|
|
/* set the dss and dsw values */
|
|
set_dss_dsw(unit_no, 1);
|
|
|
|
/*
|
|
* If we're opened for read/write,
|
|
* toggle the scan/print line for scanning
|
|
*/
|
|
if ((bpp_p->openflags & FREAD) &&
|
|
(bpp_p->openflags & FWRITE)) {
|
|
start_critical(unit_no);
|
|
if (bpp_transfer_parms_p->read_handshake == BPP_HSCAN_HS) {
|
|
/* The HP Scanjet uses AFX */
|
|
bpp_regs_p->out_pins |= BPP_AFX_PIN;
|
|
} else {
|
|
bpp_regs_p->out_pins |= BPP_SLCTIN_PIN;
|
|
}
|
|
end_critical(unit_no);
|
|
}
|
|
bpp_print_regs(unit_no, "after switch, before physio:");
|
|
|
|
retval = physio(bpp_strategy, &bpp_p->buf, dev,
|
|
B_READ, bpp_minphys, uio);
|
|
|
|
bpp_print(LOG_INFO,
|
|
"Leaving bpp_read, unit %d: errno %d.\n",
|
|
unit_no, retval);
|
|
last_trans = read_trans; /* set last transfer type */
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
* Write data to the bpp. Block until the write is finished.
|
|
*/
|
|
int
|
|
bpp_write(dev, uio)
|
|
dev_t dev;
|
|
struct uio *uio;
|
|
{
|
|
u_char unit_no;
|
|
u_short retval = 0; /* return value (errno) for system call */
|
|
register struct bpp_regs *bpp_regs_p;
|
|
register struct bpp_transfer_parms *bpp_transfer_parms_p;
|
|
|
|
register struct bpp_error_status *bpp_errorstat_p; /* error stat */
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
|
|
unit_no = BPP_UNIT(dev);
|
|
bpp_print(LOG_INFO,
|
|
"Entering bpp_write, unit number %d.\n", unit_no);
|
|
|
|
bpp_p = bpp_units[unit_no];
|
|
bpp_regs_p = bpp_p->bpp_regs_p;
|
|
bpp_transfer_parms_p = &bpp_p->transfer_parms;
|
|
bpp_errorstat_p = &bpp_p->error_stat;
|
|
|
|
/* clear any old error status */
|
|
*bpp_errorstat_p = bpp_default_error_stat;
|
|
|
|
/*
|
|
* Set up the polarities for the ERR, SLCT PE, and BUSY interrupts.
|
|
* Changing the polarities could cause a stray interrupt,
|
|
* so clear them here.
|
|
* These polarities are handshake dependent.
|
|
* This setup corresponds to the default handshakes.
|
|
*/
|
|
bpp_print(LOG_INFO, "Before setting polarities, int_cntl = 0x%x\n",
|
|
bpp_regs_p->int_cntl);
|
|
start_critical(unit_no);
|
|
bpp_regs_p->int_cntl |= BPP_ERR_IRP; /* ERR rising edge */
|
|
bpp_regs_p->int_cntl |= BPP_SLCT_IRP; /* SLCT rising edge */
|
|
/* SLCT+ is off-line */
|
|
bpp_regs_p->int_cntl &= ~BPP_PE_IRP; /* PE falling edge */
|
|
|
|
bpp_regs_p->int_cntl |= (BPP_ERR_IRQ | BPP_SLCT_IRQ | BPP_PE_IRQ);
|
|
end_critical(unit_no);
|
|
|
|
bpp_print(LOG_INFO, "After setting polarities, int_cntl = 0x%x\n",
|
|
bpp_regs_p->int_cntl);
|
|
|
|
start_critical(unit_no);
|
|
check_for_active_pins(unit_no);
|
|
end_critical(unit_no);
|
|
|
|
/*
|
|
* if any active pins were found, don't attempt the transfer,
|
|
* unless we're in scanner mode (read-write), scanners use the PE line
|
|
* to get the host's attention.
|
|
*/
|
|
if ((bpp_p->openflags & (FREAD | FWRITE)) == (FREAD | FWRITE)) {
|
|
/*
|
|
* Toggle the scan/print line for scanning
|
|
*/
|
|
start_critical(unit_no);
|
|
if (bpp_transfer_parms_p->read_handshake == BPP_HSCAN_HS) {
|
|
/* The HP Scanjet uses AFX */
|
|
bpp_regs_p->out_pins &= ~BPP_AFX_PIN;
|
|
} else {
|
|
bpp_regs_p->out_pins &= ~BPP_SLCTIN_PIN;
|
|
}
|
|
end_critical(unit_no);
|
|
} else {
|
|
if ((bpp_errorstat_p->pin_status &
|
|
(BPP_ERR_ERR | BPP_SLCT_ERR | BPP_PE_ERR))) {
|
|
/* printer error - no transfer allowed */
|
|
bpp_print(LOG_INFO,
|
|
"In bpp_write, pending error pin condition\n");
|
|
retval = ENXIO;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
start_critical(unit_no);
|
|
/* mark the transfer direction in the hardware */
|
|
bpp_regs_p->trans_cntl &= ~BPP_DIRECTION;
|
|
|
|
/*
|
|
* make sure the memory clear operation is turned off
|
|
*/
|
|
bpp_regs_p->op_config &= ~BPP_EN_MEM_CLR;
|
|
|
|
/*
|
|
* Set the handshake bits
|
|
*/
|
|
if (bpp_transfer_parms_p->write_handshake == BPP_NO_HS) {
|
|
bpp_print(LOG_INFO, "BPP_NO_HS case\n");
|
|
bpp_regs_p->op_config &= ~(BPP_ACK_OP | BPP_BUSY_OP);
|
|
} else if (bpp_transfer_parms_p->write_handshake == BPP_ACK_HS) {
|
|
bpp_print(LOG_INFO, "BPP_ACK_HS case\n");
|
|
bpp_regs_p->op_config &= ~BPP_BUSY_OP;
|
|
bpp_regs_p->op_config |= BPP_ACK_OP;
|
|
} else if (bpp_transfer_parms_p->write_handshake == BPP_BUSY_HS) {
|
|
bpp_print(LOG_INFO, "BPP_BUSY_HS case\n");
|
|
bpp_regs_p->op_config |= BPP_BUSY_OP;
|
|
bpp_regs_p->op_config &= ~BPP_ACK_OP;
|
|
}
|
|
|
|
/* Make sure that ACK and BUSY are unidirectional */
|
|
bpp_regs_p->op_config &= ~(BPP_ACK_BIDIR | BPP_BUSY_BIDIR);
|
|
end_critical(unit_no);
|
|
|
|
/* set the dss and dsw values */
|
|
set_dss_dsw(unit_no, 0);
|
|
|
|
retval = physio(bpp_strategy, &bpp_p->buf, dev,
|
|
B_WRITE, bpp_minphys, uio);
|
|
|
|
out:
|
|
bpp_print(LOG_INFO,
|
|
"Leaving bpp_write, unit %d: errno %d.\n", unit_no, retval);
|
|
last_trans = write_trans; /* set last transfer type */
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
* Limit transfer size to the smaller of
|
|
* - system minphys size
|
|
* - 16 MB limit in HIOD address register
|
|
*/
|
|
static void
|
|
bpp_minphys(bp)
|
|
struct buf *bp;
|
|
{
|
|
minphys(bp);
|
|
if (bp->b_bcount > BPP_MAX_DMA)
|
|
bp->b_bcount = BPP_MAX_DMA;
|
|
}
|
|
|
|
/*
|
|
* Check to see if any of the control pins are active.
|
|
* NOTE: if this routine is called from the top half,
|
|
* it should be bracketed by start_critical and end_critical.
|
|
*/
|
|
int
|
|
check_for_active_pins(unit_no)
|
|
u_char unit_no;
|
|
{
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
register struct bpp_regs *bpp_regs_p;
|
|
register struct bpp_error_status *bpp_errorstat_p; /* error stat */
|
|
|
|
bpp_p = bpp_units[unit_no];
|
|
bpp_regs_p = bpp_p->bpp_regs_p;
|
|
bpp_errorstat_p = &bpp_p->error_stat;
|
|
|
|
bpp_print(LOG_INFO,
|
|
"Entering check_for_active_pins, unit number %d.\n", unit_no);
|
|
/*
|
|
* Check that there are no pending ERR, SLCT or PE error
|
|
* conditions. If there are, do not attempt the transfer.
|
|
*/
|
|
bpp_print(LOG_INFO, "check_active_pins: in_pins = 0x%x\n",
|
|
bpp_regs_p->in_pins);
|
|
bpp_print(LOG_INFO, "check_active_pins: int_cntl = 0x%x\n",
|
|
bpp_regs_p->int_cntl);
|
|
|
|
if (((bpp_regs_p->in_pins & BPP_ERR_PIN) &&
|
|
(bpp_regs_p->int_cntl & BPP_ERR_IRP)) ||
|
|
((~bpp_regs_p->in_pins & BPP_ERR_PIN)&&
|
|
(~bpp_regs_p->int_cntl & BPP_ERR_IRP))) { /* ERR active */
|
|
bpp_print(LOG_INFO,
|
|
"In ck_active_pins, pending ERR condition\n");
|
|
bpp_errorstat_p->pin_status |= BPP_ERR_ERR;
|
|
}
|
|
|
|
if (((bpp_regs_p->in_pins & BPP_SLCT_PIN) &&
|
|
(bpp_regs_p->int_cntl & BPP_SLCT_IRP)) ||
|
|
((~bpp_regs_p->in_pins & BPP_SLCT_PIN)&&
|
|
(~bpp_regs_p->int_cntl & BPP_SLCT_IRP))) { /* SLCT active */
|
|
bpp_print(LOG_INFO,
|
|
"In ck_active_pins, pending SLCT condition\n");
|
|
bpp_errorstat_p->pin_status |= BPP_SLCT_ERR;
|
|
}
|
|
|
|
if (((bpp_regs_p->in_pins & BPP_PE_PIN) &&
|
|
(bpp_regs_p->int_cntl & BPP_PE_IRP)) ||
|
|
((~bpp_regs_p->in_pins & BPP_PE_PIN)&&
|
|
(~bpp_regs_p->int_cntl & BPP_PE_IRP))) { /* PE active */
|
|
bpp_print(LOG_INFO,
|
|
"In check_active_pins, pending PE condition\n");
|
|
bpp_errorstat_p->pin_status |= BPP_PE_ERR;
|
|
}
|
|
bpp_print(LOG_INFO,
|
|
"Leaving check_for_active_pins, unit number %d.\n", unit_no);
|
|
}
|
|
|
|
/*
|
|
* Setup and start a transfer on the device.
|
|
*/
|
|
/*ARGSUSED*/
|
|
static
|
|
bpp_strategy(bp)
|
|
register struct buf *bp;
|
|
{
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
u_int unit_no;
|
|
int timeout_value; /* read or write timeout in secs */
|
|
u_long start_address; /* kernel virt. DVMA start address */
|
|
|
|
#ifdef notdef
|
|
u_long end_address; /* kernel virt. DVMA end address */
|
|
#endif notdef
|
|
|
|
u_long size; /* size of DVMA transfer */
|
|
register struct bpp_transfer_parms *bpp_transfer_parms_p;
|
|
register struct bpp_regs *bpp_regs_p;
|
|
|
|
|
|
unit_no = BPP_UNIT(bp->b_dev);
|
|
bpp_print(LOG_INFO,
|
|
"bpp%d:Entering bpp_strategy: length 0x%x.\n",
|
|
unit_no, bp->b_bcount);
|
|
|
|
/*
|
|
* Use the unit number to locate our data structures.
|
|
*/
|
|
bpp_p = bpp_units[unit_no];
|
|
bpp_regs_p = bpp_p->bpp_regs_p;
|
|
bpp_transfer_parms_p = &bpp_p->transfer_parms;
|
|
|
|
/* Clear the unit error status struct */
|
|
bpp_p->error_stat = bpp_default_error_stat;
|
|
bpp_print_regs(unit_no, "Start strategy, before checking for bpp_idle");
|
|
|
|
|
|
|
|
start_critical(unit_no);
|
|
/*
|
|
* Get dvma bus resource, sleeping if necessary.
|
|
*/
|
|
bp->b_mbinfo =
|
|
mb_mapalloc(dvmamap, bp, 0, ((int (*)())0), (caddr_t) 0);
|
|
#ifdef DEBUG
|
|
bpp_print(LOG_INFO,
|
|
"In bpp_strategy, *(&bp->b_mbinfo) = %x\n", *(&bp->b_mbinfo));
|
|
#endif DEBUG
|
|
|
|
#ifdef notdef
|
|
/* FIXME: This code has never been tested properly */
|
|
|
|
/*
|
|
* The bpp hardware will not properly transfer across
|
|
* a 16 MB boundary. If the transfer will cross the boundary,
|
|
* I trim the size I pass to the DMA engine, so
|
|
* that the resulting end address is just short of
|
|
* the boundary.
|
|
*/
|
|
|
|
size = bp->b_bcount;
|
|
start_address = (u_long) MBI_ADDR(bp->b_mbinfo + DVMA);
|
|
end_address = start_address + size;
|
|
|
|
if ((start_address % BPP_MAX_DMA) >
|
|
(end_address % BPP_MAX_DMA)) {
|
|
/* transfer would cross a boundary */
|
|
bpp_print(LOG_DEBUG,
|
|
"bpp_strategy, unit %d, transfer crosses boundary.\n",
|
|
unit_no);
|
|
size = BPP_MAX_DMA - (start_address % BPP_MAX_DMA);
|
|
bpp_p->transfer_remainder = (bp->b_bcount - size);
|
|
} else {
|
|
size = bp->b_bcount;
|
|
bpp_p->transfer_remainder = 0;
|
|
}
|
|
#else notdef
|
|
|
|
size = bp->b_bcount;
|
|
start_address = (u_long) MBI_ADDR(bp->b_mbinfo + DVMA);
|
|
|
|
#ifdef notdef
|
|
end_address = start_address + size;
|
|
#endif notdef
|
|
|
|
bpp_p->transfer_remainder = 0;
|
|
|
|
#endif notdef
|
|
|
|
/*
|
|
* Write the dma start address to the hardware.
|
|
* Write the transfer byte count to the hardware.
|
|
*/
|
|
bpp_regs_p->dma_addr = start_address;
|
|
|
|
bpp_regs_p->dma_bcnt = size;
|
|
end_critical(unit_no);
|
|
|
|
bpp_print(LOG_INFO,
|
|
"bpp_strategy: Transfer %d bytes starting at 0x%x.\n",
|
|
bpp_regs_p->dma_bcnt, bpp_regs_p->dma_addr);
|
|
bpp_print(LOG_INFO,
|
|
"before enabling interrupts, dma csr=0x%x, int_cntl=0x%x.\n",
|
|
bpp_regs_p->dma_csr, bpp_regs_p->int_cntl);
|
|
|
|
/*
|
|
* Enable byte-counter during DVMA.
|
|
* Enable TC interrupts so we will know when the DVMA is done.
|
|
* Start the DVMA.
|
|
* Enable the peripheral error interrupts.
|
|
*/
|
|
start_critical(unit_no);
|
|
/*
|
|
* Do not close critical section until timeouts have been
|
|
* enabled, otherwise we might get an untimeout before
|
|
* the timeout has been set!
|
|
*/
|
|
bpp_regs_p->dma_csr |= BPP_ENABLE_BCNT;
|
|
bpp_regs_p->dma_csr &= ~BPP_TC_INTR_DISABLE;
|
|
bpp_regs_p->dma_csr |= BPP_ENABLE_DMA;
|
|
|
|
if (bp->b_flags & B_READ) {
|
|
bpp_print(LOG_INFO,
|
|
"bp->b_flags indicates READ mode\n");
|
|
timeout_value = bpp_transfer_parms_p->read_timeout;
|
|
} else {
|
|
bpp_print(LOG_INFO,
|
|
"bp->b_flags indicates WRITE mode\n");
|
|
if (!(bpp_p->openflags & FREAD &&
|
|
bpp_p->openflags & FWRITE)) {
|
|
bpp_regs_p->int_cntl |= (BPP_ERR_IRQ_EN |
|
|
BPP_SLCT_IRQ_EN | BPP_PE_IRQ_EN);
|
|
}
|
|
bpp_print(LOG_INFO,
|
|
"after enable error int. int cntl contains 0x%x.\n",
|
|
bpp_regs_p->int_cntl);
|
|
timeout_value = bpp_transfer_parms_p->write_timeout;
|
|
}
|
|
|
|
bpp_print(LOG_INFO,
|
|
"after enabling interrupts, dma csr = 0x%x, int_cntl = 0x%x.\n",
|
|
bpp_regs_p->dma_csr, bpp_regs_p->int_cntl);
|
|
bpp_print(LOG_INFO,
|
|
"Setting timeout to call bpp_transfer_timeout in %d sec\n",
|
|
timeout_value);
|
|
timeout(bpp_transfer_timeout, (caddr_t)unit_no, (timeout_value*hz));
|
|
bpp_p->timeouts |= TRANSFER_TIMEOUT;
|
|
bpp_print(LOG_INFO, "In bpp_strategy, Timeout block is 0x%x.\n",
|
|
bpp_p->timeouts);
|
|
end_critical(unit_no);
|
|
|
|
|
|
#ifdef NO_BPP_HW
|
|
bpp_print(LOG_INFO,
|
|
"Setting timeout to call bpp_fakeout in 2 sec\n");
|
|
timeout(bpp_fakeout, unit_no, 2*hz); /* 2 seconds */
|
|
bpp_p->timeouts |= FAKEOUT_TIMEOUT;
|
|
bpp_print(LOG_INFO, "In bpp_strategy, Timeout block is 0x%x.\n",
|
|
bpp_p->timeouts);
|
|
#endif NO_BPP_HW
|
|
|
|
bpp_print_regs(unit_no, "end of strategy");
|
|
bpp_print(LOG_INFO, "Leaving bpp_strategy.\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Handle special control requests
|
|
*/
|
|
/*ARGSUSED*/
|
|
int
|
|
bpp_ioctl(dev, cmd, arg, flag)
|
|
dev_t dev;
|
|
int cmd;
|
|
caddr_t arg;
|
|
int flag;
|
|
{
|
|
u_char unit_no;
|
|
u_short retval = 0; /* return value (errno) for system call */
|
|
u_short read_retval = 0;
|
|
u_short write_retval = 0;
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
register struct bpp_regs *bpp_regs_p;
|
|
register struct bpp_transfer_parms *bpp_transfer_parms_p;
|
|
register struct bpp_error_status *bpp_errorstat_p; /* error stat */
|
|
register struct bpp_pins *bpp_pins_p; /* error stat */
|
|
|
|
register enum handshake_t write_handshake;
|
|
register enum handshake_t read_handshake;
|
|
|
|
unit_no = BPP_UNIT(dev);
|
|
bpp_p = bpp_units[unit_no];
|
|
bpp_regs_p = bpp_p->bpp_regs_p;
|
|
bpp_transfer_parms_p = &bpp_p->transfer_parms;
|
|
bpp_errorstat_p = &bpp_p->error_stat;
|
|
bpp_pins_p = &bpp_p->pins;
|
|
|
|
write_handshake = bpp_transfer_parms_p->write_handshake;
|
|
read_handshake = bpp_transfer_parms_p->read_handshake;
|
|
|
|
bpp_print(LOG_INFO,
|
|
"Entering bpp_ioctl, unit number %d.\n", unit_no);
|
|
|
|
switch (cmd) {
|
|
case BPPIOC_SETPARMS: /* set transfer parameters */
|
|
bpp_print(LOG_INFO, "BPPIOC_SETPARMS case.\n");
|
|
bpp_transfer_parms_p = (struct bpp_transfer_parms *)arg;
|
|
if (flag & FREAD) {
|
|
bpp_print(LOG_INFO,
|
|
"Checking read parameters.\n");
|
|
read_retval =
|
|
check_read_params(bpp_transfer_parms_p,
|
|
flag);
|
|
}
|
|
if (flag & FWRITE) {
|
|
bpp_print(LOG_INFO,
|
|
"Checking write parameters.\n");
|
|
write_retval =
|
|
check_write_params(bpp_transfer_parms_p,
|
|
flag);
|
|
}
|
|
if (read_retval || write_retval) {
|
|
retval = EINVAL;
|
|
} else { /* valid parameters */
|
|
bpp_p->transfer_parms =
|
|
*(struct bpp_transfer_parms *)arg;
|
|
}
|
|
break;
|
|
case BPPIOC_GETPARMS: /* get transfer parameters */
|
|
bpp_print(LOG_INFO, "BPPIOC_GETPARMS case.\n");
|
|
*(struct bpp_transfer_parms *)arg =
|
|
bpp_p->transfer_parms;
|
|
retval = 0;
|
|
break;
|
|
case BPPIOC_SETOUTPINS: /* set output pins */
|
|
bpp_print(LOG_INFO, "BPPIOC_SETOUTPINS case.\n");
|
|
bpp_pins_p = (struct bpp_pins *)arg;
|
|
if (flag & FREAD) {
|
|
bpp_print(LOG_INFO,
|
|
"Checking read pins.\n");
|
|
read_retval = check_read_pins(bpp_pins_p,
|
|
flag, read_handshake);
|
|
if (read_retval == 0) { /* valid pins */
|
|
bpp_p->pins =
|
|
*(struct bpp_pins *)arg;
|
|
|
|
}
|
|
}
|
|
if (flag & FWRITE) {
|
|
bpp_print(LOG_INFO,
|
|
"Checking write pins.\n");
|
|
write_retval = check_write_pins(bpp_pins_p,
|
|
flag, write_handshake);
|
|
if (write_retval == 0) { /* valid pins */
|
|
bpp_p->pins =
|
|
*(struct bpp_pins *)arg;
|
|
|
|
}
|
|
}
|
|
if (read_retval || write_retval) {
|
|
retval = EINVAL;
|
|
} else { /* All is well, write the registers */
|
|
start_critical(unit_no);
|
|
bpp_regs_p->out_pins =
|
|
bpp_p->pins.output_reg_pins;
|
|
/* the previous line will not cstyle */
|
|
bpp_regs_p->in_pins =
|
|
bpp_p->pins.input_reg_pins;
|
|
end_critical(unit_no);
|
|
}
|
|
break;
|
|
case BPPIOC_GETOUTPINS: /* get output pins */
|
|
bpp_print(LOG_INFO, "BPPIOC_GETOUTPINS case.\n");
|
|
/* read the current pin state into the struct */
|
|
read_outpins(unit_no, flag, write_handshake);
|
|
*(struct bpp_pins *)arg =
|
|
bpp_p->pins;
|
|
retval = 0;
|
|
break;
|
|
case BPPIOC_GETERR: /* get error block status */
|
|
bpp_print(LOG_INFO, "BPPIOC_GETERR case.\n");
|
|
*(struct bpp_error_status *)arg =
|
|
bpp_p->error_stat;
|
|
retval = 0;
|
|
break;
|
|
case BPPIOC_TESTIO: /* test transfer readiness */
|
|
bpp_print(LOG_INFO, "BPPIOC_TESTIO case.\n");
|
|
bpp_errorstat_p = &bpp_p->error_stat;
|
|
retval = 0;
|
|
/* clear any old error status */
|
|
*bpp_errorstat_p = bpp_default_error_stat;
|
|
start_critical(unit_no);
|
|
check_for_active_pins(unit_no);
|
|
end_critical(unit_no);
|
|
/* if any active pins were found, return -1 */
|
|
if (bpp_errorstat_p->pin_status &
|
|
(BPP_ERR_ERR | BPP_SLCT_ERR | BPP_PE_ERR)) {
|
|
bpp_print(LOG_INFO,
|
|
"In TESTIO, found error pin condition\n");
|
|
retval = EIO;
|
|
} else
|
|
retval = 0;
|
|
break;
|
|
/* TEST - request partial fake transfer */
|
|
case BPPIOC_SETBC:
|
|
bpp_print(LOG_INFO, "BPPIOC_SETBC case.\n");
|
|
sim_byte_count = *(u_int *)arg;
|
|
retval = 0;
|
|
break;
|
|
/* TEST - get DMA_BCNT from last data transfer */
|
|
case BPPIOC_GETBC:
|
|
bpp_print(LOG_INFO, "BPPIOC_GETBC case.\n");
|
|
retval = 0;
|
|
break;
|
|
/* TEST - get contents of device registers */
|
|
case BPPIOC_GETREGS:
|
|
bpp_print(LOG_INFO, "BPPIOC_GETREGS case.\n");
|
|
*(struct bpp_regs *)arg =
|
|
*(bpp_p->bpp_regs_p);
|
|
retval = 0;
|
|
break;
|
|
/* TEST - set special fakeout error code to simulate errs */
|
|
case BPPIOC_SETERRCODE:
|
|
bpp_print(LOG_INFO, "BPPIOC_SETERRCODE case.\n");
|
|
err_code = *(int *)arg;
|
|
retval = 0;
|
|
break;
|
|
/* TEST - get pointer to fakeout transferred data */
|
|
case BPPIOC_GETFAKEBUF:
|
|
bpp_print(LOG_INFO, "BPPIOC_GETFAKEBUF case.\n");
|
|
#ifdef NO_BPP_HW
|
|
bpp_print(LOG_INFO, "bpp_hard_buf = 0x%x\n",
|
|
bpp_hard_buf);
|
|
bpp_print(LOG_INFO, "arg = 0x%x\n", arg);
|
|
bpp_print(LOG_INFO, "*arg = 0x%x\n", *arg);
|
|
{
|
|
u_char **argpp;
|
|
|
|
argpp = (u_char **)arg;
|
|
retval = (copyout(&bpp_hard_buf[0],
|
|
*argpp, FAKE_BUF_SIZE));
|
|
bpp_print(LOG_INFO, "argpp = 0x%x\n", argpp);
|
|
bpp_print(LOG_INFO, "*argpp = 0x%x\n", *argpp);
|
|
}
|
|
#else NO_BPP_HW
|
|
bpp_print(LOG_WARNING, "Bad ioctl call!\n");
|
|
#endif NO_BPP_HW
|
|
retval = ENOTTY;
|
|
break;
|
|
default:
|
|
bpp_print(LOG_NOTICE, "Error in bpp_ioctl switch!\n");
|
|
retval = ENOTTY;
|
|
break;
|
|
}
|
|
|
|
bpp_print(LOG_INFO,
|
|
"Leaving bpp_ioctl, unit %d: errno %d.\n",
|
|
unit_no, retval);
|
|
return (retval);
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle an interrupt or interrupts that may or may not be from
|
|
* one or more of the bpp units. This routine is safely declared
|
|
* static because it is explicitly registered by address.
|
|
*/
|
|
static int
|
|
bpp_poll()
|
|
{
|
|
u_int bpp_unit_no;
|
|
u_int int_serviced = 0;
|
|
register struct bpp_regs *bpp_regs_p;
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
bpp_print(LOG_INFO, "Entering bpp_poll.\n");
|
|
|
|
/*
|
|
* March through the units, checking for an interrupt pending.
|
|
*/
|
|
for (bpp_unit_no = 0; bpp_unit_no < nbpp; ++bpp_unit_no) {
|
|
bpp_p = bpp_units[bpp_unit_no];
|
|
bpp_regs_p = bpp_p->bpp_regs_p;
|
|
if ((bpp_regs_p->dma_csr & BPP_INT_PEND) ||
|
|
(bpp_regs_p->dma_csr & BPP_ERR_PEND)) {
|
|
bpp_print(LOG_INFO, "Interrupt found, unit #%d.\n",
|
|
bpp_unit_no);
|
|
/*
|
|
* Mark that we found an interrupting device.
|
|
* Call the interrupt service routine.
|
|
*/
|
|
int_serviced = 1;
|
|
(void) bpp_intr(bpp_unit_no);
|
|
}
|
|
}
|
|
bpp_print(LOG_INFO, "Leaving bpp_poll, int_serviced = 0x%x.\n",
|
|
int_serviced);
|
|
return (int_serviced);
|
|
}
|
|
/*
|
|
* Interrupt routine for the bpp hardware. This routine is always called
|
|
* from bpp_poll with the unit number. When this routine is called,
|
|
* we know that this unit issued an interrupt that needs service.
|
|
*/
|
|
static void
|
|
bpp_intr(unit_no)
|
|
u_int unit_no;
|
|
{
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
register struct bpp_regs *bpp_regs_p;
|
|
register struct bpp_error_status *bpp_errorstat_p; /* error stat */
|
|
register struct buf *bp;
|
|
|
|
bpp_print(LOG_INFO, "Entering bpp_intr, unit %d.\n",
|
|
unit_no);
|
|
/*
|
|
* Set pointer to dma registers.
|
|
* Set pointer to error stat struct.
|
|
* Set pointer to buf structure.
|
|
*/
|
|
bpp_p = bpp_units[unit_no];
|
|
bpp_regs_p = bpp_p->bpp_regs_p;
|
|
bpp_errorstat_p = &bpp_p->error_stat;
|
|
bp = &bpp_p->buf;
|
|
|
|
|
|
bpp_print(LOG_INFO, "dma csr contains 0x%x.\n",
|
|
bpp_regs_p->dma_csr);
|
|
bpp_print(LOG_INFO, "int cntl contains 0x%x.\n",
|
|
bpp_regs_p->int_cntl);
|
|
|
|
/*
|
|
* The bpp hardware can interrupt for errors, or
|
|
* for several transfer conditions. These can happen
|
|
* at the same time.
|
|
* I process any errors first, checking to see
|
|
* if a transfer was in process.
|
|
* In the future, I may want to count how many times I've
|
|
* tried to flush the cache, and eventually give up and
|
|
* reset the hardware.
|
|
* Then I process "normal" conditions.
|
|
*/
|
|
|
|
/*
|
|
* Check for an error, recover if possible.
|
|
*/
|
|
if (bpp_regs_p->dma_csr & BPP_ERR_PEND) {
|
|
bpp_print(LOG_INFO, "Error interrupt detected\n");
|
|
if (bpp_regs_p->dma_csr & BPP_SLAVE_ERR) {
|
|
bpp_print(LOG_INFO, "Slave error detected\n");
|
|
}
|
|
if (bpp_regs_p->dma_csr & BPP_ENABLE_DMA) {
|
|
/* was transferring */
|
|
/*
|
|
* The transfer has failed. Notify the application
|
|
* how many bytes got out, and that there was
|
|
* an IO error, and turn off the transfer.
|
|
*/
|
|
bpp_transfer_failed(unit_no);
|
|
return;
|
|
} else {
|
|
/* will make return value -1 */
|
|
bp->b_flags |= B_ERROR;
|
|
bp->b_resid = bp->b_bcount;
|
|
}
|
|
/*
|
|
* capture the error status in the error_stat struct
|
|
* so the application can get it with the GETERR
|
|
* ioctl later.
|
|
*/
|
|
bpp_errorstat_p->bus_error = 1;
|
|
|
|
/* Mark the error. */
|
|
bp->b_error = EIO;
|
|
|
|
/* clear the error interrupt */
|
|
while (bp->b_flags & B_READ)
|
|
/* cannot assert FLUSH till cache drains */
|
|
/* spin on draining bit */
|
|
bpp_regs_p->dma_csr |= BPP_FLUSH;
|
|
}
|
|
if (bpp_regs_p->dma_csr & BPP_INT_PEND) {
|
|
bpp_print(LOG_INFO, "Interrupt pending found.\n");
|
|
bpp_print(LOG_INFO, "dma csr contains 0x%x.\n",
|
|
bpp_regs_p->dma_csr);
|
|
bpp_print(LOG_INFO, "int cntl contains 0x%x.\n",
|
|
bpp_regs_p->int_cntl);
|
|
/* TC case - terminal count */
|
|
if (bpp_regs_p->dma_csr & BPP_TERMINAL_CNT &&
|
|
((bpp_regs_p->dma_csr & BPP_TC_INTR_DISABLE) == 0)) {
|
|
bpp_print(LOG_INFO,
|
|
"Terminal count interrupt found.\n");
|
|
/* mask this interrupt */
|
|
bpp_regs_p->dma_csr |= BPP_TC_INTR_DISABLE;
|
|
bpp_regs_p->dma_csr &= ~BPP_ENABLE_BCNT;
|
|
/* and clear the interrupting condition */
|
|
bpp_regs_p->dma_csr |= BPP_TERMINAL_CNT;
|
|
bpp_regs_p->dma_csr &= ~BPP_ENABLE_DMA;
|
|
bp->b_resid = bpp_p->transfer_remainder;
|
|
/* Mask the error interrupt conditions */
|
|
bpp_regs_p->int_cntl &=
|
|
~(BPP_ERR_IRQ_EN | BPP_SLCT_IRQ_EN |
|
|
BPP_PE_IRQ_EN);
|
|
bpp_print(LOG_INFO, "dma csr contains 0x%x.\n",
|
|
bpp_regs_p->dma_csr);
|
|
bpp_print(LOG_INFO, "int cntl contains 0x%x.\n",
|
|
bpp_regs_p->int_cntl);
|
|
}
|
|
/* ERR_IRQ case - error pin interrupt */
|
|
if ((bpp_regs_p->int_cntl & BPP_ERR_IRQ) &&
|
|
(bpp_regs_p->int_cntl & BPP_ERR_IRQ_EN)) {
|
|
bpp_print(LOG_INFO,
|
|
"Error pin interrupt found.\n");
|
|
bpp_errorstat_p->pin_status |= BPP_ERR_ERR;
|
|
if (bpp_regs_p->dma_csr & BPP_ENABLE_DMA) {
|
|
/* was transferring */
|
|
bpp_transfer_failed(unit_no);
|
|
}
|
|
/* clear interrupting condition */
|
|
bpp_regs_p->int_cntl |= BPP_ERR_IRQ;
|
|
}
|
|
/* SLCT_IRQ case - select pin interrupt */
|
|
if ((bpp_regs_p->int_cntl & BPP_SLCT_IRQ) &&
|
|
(bpp_regs_p->int_cntl & BPP_SLCT_IRQ_EN)) {
|
|
bpp_print(LOG_INFO,
|
|
"Select pin interrupt found.\n");
|
|
bpp_errorstat_p->pin_status |= BPP_SLCT_ERR;
|
|
if (bpp_regs_p->dma_csr & BPP_ENABLE_DMA) {
|
|
/* was transferring */
|
|
bpp_transfer_failed(unit_no);
|
|
}
|
|
/* clear interrupting condition */
|
|
bpp_regs_p->int_cntl |= BPP_SLCT_IRQ;
|
|
}
|
|
/* PE_IRQ case - paper error pin interrupt */
|
|
if ((bpp_regs_p->int_cntl & BPP_PE_IRQ) &&
|
|
(bpp_regs_p->int_cntl & BPP_PE_IRQ_EN)) {
|
|
bpp_print(LOG_INFO,
|
|
"Paper error pin interrupt found.\n");
|
|
bpp_errorstat_p->pin_status |= BPP_PE_ERR;
|
|
if (bpp_regs_p->dma_csr & BPP_ENABLE_DMA) {
|
|
/* was transferring */
|
|
bpp_transfer_failed(unit_no);
|
|
}
|
|
/* clear interrupting condition */
|
|
bpp_regs_p->int_cntl |= BPP_PE_IRQ;
|
|
}
|
|
/*
|
|
* The interrupts below (BUSY, ACK, and DS)
|
|
* are available in the hardware, but are not
|
|
* being used for anything now.
|
|
*/
|
|
/* BUSY_IRQ case - busy pin interrupt */
|
|
if ((bpp_regs_p->int_cntl & BPP_BUSY_IRQ) &&
|
|
(bpp_regs_p->int_cntl & BPP_BUSY_IRQ_EN)) {
|
|
bpp_print(LOG_INFO,
|
|
"Busy pin interrupt found.\n");
|
|
/* for pio only */
|
|
/* clear interrupting condition */
|
|
bpp_regs_p->int_cntl |= BPP_BUSY_IRQ;
|
|
}
|
|
/* ACK_IRQ case - acknowledge pin interrupt */
|
|
if ((bpp_regs_p->int_cntl & BPP_ACK_IRQ) &&
|
|
(bpp_regs_p->int_cntl & BPP_ACK_IRQ_EN)) {
|
|
bpp_print(LOG_INFO,
|
|
"Acknowledge pin interrupt found.\n");
|
|
/* for pio only */
|
|
/* clear interrupting condition */
|
|
bpp_regs_p->int_cntl |= BPP_ACK_IRQ;
|
|
}
|
|
/* DS_IRQ case - data strobe pin interrupt */
|
|
if ((bpp_regs_p->int_cntl & BPP_DS_IRQ) &&
|
|
(bpp_regs_p->int_cntl & BPP_DS_IRQ_EN)) {
|
|
bpp_print(LOG_INFO,
|
|
"Data strobe pin interrupt found.\n");
|
|
/* for pio only */
|
|
/* clear interrupting condition */
|
|
bpp_regs_p->int_cntl |= BPP_DS_IRQ;
|
|
}
|
|
} /* end of INT_PEND check */
|
|
|
|
bpp_print(LOG_INFO, "dma csr contains 0x%x.\n", bpp_regs_p->dma_csr);
|
|
bpp_print(LOG_INFO, "int cntl contains 0x%x.\n", bpp_regs_p->int_cntl);
|
|
|
|
/* Clear the transfer timeout */
|
|
bpp_print(LOG_INFO, "In bpp_intr, Clearing transfer timeout.\n");
|
|
untimeout(bpp_transfer_timeout, (caddr_t)unit_no);
|
|
bpp_p->timeouts &= ~TRANSFER_TIMEOUT;
|
|
bpp_print(LOG_INFO, "In bpp_intr, Timeout block is 0x%x.\n",
|
|
bpp_p->timeouts);
|
|
/*
|
|
* Release the dvma bus resource.
|
|
*/
|
|
#ifdef DEBUG
|
|
bpp_print(LOG_INFO,
|
|
"In bpp_intr, *(&bp->b_mbinfo) = %x\n", *(&bp->b_mbinfo));
|
|
#endif DEBUG
|
|
(void) mb_mapfree(dvmamap, &bp->b_mbinfo);
|
|
|
|
bpp_print(LOG_DEBUG, "bpp_intr, unit %d, Calling iodone.\n", unit_no);
|
|
/*
|
|
* Mark the io on the buf as finished, with the side effect
|
|
* of waking up others who want to use the buf.
|
|
*/
|
|
(void) iodone(bp);
|
|
|
|
bpp_print(LOG_INFO, "Leaving bpp_intr.\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* A transfer has failed for some reason.
|
|
* Mark the bp struct to indicate how much happened,
|
|
* and turn off the transfer and its interrupts.
|
|
* NOTE: if this routine is called from the top half,
|
|
* it should be bracketed by start_critical and end_critical.
|
|
*/
|
|
static int
|
|
bpp_transfer_failed(unit_no)
|
|
u_int unit_no; /* unit which had transfer failure */
|
|
{
|
|
struct buf *bp;
|
|
register struct bpp_regs *bpp_regs_p;
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
|
|
bpp_p = bpp_units[unit_no];
|
|
bp = (struct buf *)(&bpp_p->buf),
|
|
bpp_regs_p = bpp_p->bpp_regs_p;
|
|
|
|
bpp_print(LOG_INFO, "Entering bpp_transfer_failed.\n");
|
|
bpp_print(LOG_WARNING, "ERROR: bpp%d transfer failed!\n", unit_no);
|
|
|
|
/*
|
|
* The transfer has failed. Notify the application
|
|
* how many bytes got out, and turn off the transfer.
|
|
* NOTE: don't set B_ERROR in b_flags else the return from
|
|
* write() will be -1. See syscall().
|
|
* NOTE: the kernel ignores the the b_error field
|
|
* in the short-write case - errno is always set to zero.
|
|
*/
|
|
/* Disable the DMA first for safety */
|
|
bpp_regs_p->dma_csr &= ~BPP_ENABLE_DMA;
|
|
|
|
bpp_print_regs(unit_no, "Just after disable DMA in transfer_failed");
|
|
|
|
/* If the DMA state machines are not idle, reset them. */
|
|
if (!(bpp_regs_p->op_config & BPP_IDLE)) {
|
|
bpp_print(LOG_INFO, "Warning: DMA is not IDLE!\n");
|
|
bpp_regs_p->dma_csr &= ~BPP_ENABLE_BCNT;
|
|
bpp_print(LOG_INFO,
|
|
"In bpp_strategy, resetting PP state machine\n");
|
|
bpp_regs_p->op_config |= BPP_SRST;
|
|
bpp_regs_p->op_config &= ~BPP_SRST;
|
|
|
|
/*
|
|
* we have not received the acknowledge for the last
|
|
* byte transferred, so the byte counter was never
|
|
* decremented for it.
|
|
*/
|
|
bp->b_resid = ((bpp_regs_p->dma_bcnt - 1) +
|
|
bpp_p->transfer_remainder);
|
|
} else {
|
|
bpp_regs_p->dma_csr &= ~BPP_ENABLE_BCNT;
|
|
bp->b_resid = (bpp_regs_p->dma_bcnt +
|
|
bpp_p->transfer_remainder);
|
|
}
|
|
/* flush the cache */
|
|
bpp_regs_p->dma_csr |= BPP_FLUSH;
|
|
|
|
bpp_print(LOG_INFO,
|
|
"In bpp_transfer_failed, Residual is %d.\n", bp->b_resid);
|
|
|
|
/* make sure the DMA doesn't start again. */
|
|
bpp_regs_p->dma_bcnt = 0;
|
|
/*
|
|
* Disable the TC interrupts.
|
|
* Mask the error interrupts too.
|
|
*/
|
|
bpp_regs_p->dma_csr |= BPP_TC_INTR_DISABLE;
|
|
bpp_regs_p->int_cntl &=
|
|
~(BPP_ERR_IRQ_EN | BPP_SLCT_IRQ_EN | BPP_PE_IRQ_EN);
|
|
|
|
/* Check for any of the input pins active */
|
|
/*
|
|
* Already inside a critical section here,
|
|
* thus this routine is not bracketed with
|
|
* start_critical and end_critical calls.
|
|
*/
|
|
check_for_active_pins(unit_no);
|
|
bpp_print_regs(unit_no, "At end of bpp_transfer_failed");
|
|
bpp_print(LOG_INFO, "Leaving bpp_transfer_failed.\n");
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* This routine is called when the DVMA does not complete
|
|
* and generate a TC interrupt.
|
|
* I mark the bp struct to indicate that the transfer failed,
|
|
* and turn off the transfer. I then call iodone to wake up strategy.
|
|
*/
|
|
static
|
|
bpp_transfer_timeout(unit_no)
|
|
u_int unit_no;
|
|
{
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
register struct buf *bp;
|
|
register struct bpp_regs *bpp_regs_p;
|
|
|
|
bpp_print(LOG_INFO, "Entering bpp_transfer_timeout, unit #%d.\n",
|
|
unit_no);
|
|
|
|
bpp_p = bpp_units[unit_no];
|
|
bpp_print(LOG_INFO, "In bpp_transfer_timeout, Timeout block is 0x%x.\n",
|
|
bpp_p->timeouts);
|
|
ASSERT(bpp_p->timeouts != 0);
|
|
/*
|
|
* Use the unit number to locate our data structures.
|
|
*/
|
|
bp = (struct buf *)(&bpp_p->buf),
|
|
bpp_regs_p = bpp_p->bpp_regs_p;
|
|
|
|
/*
|
|
* If we're talking to the
|
|
* Ricoh scanner, handle it's special "hold busy"
|
|
* protocol -- Toggle the print/scan (PR/SC) bit and
|
|
* reset the parallel port
|
|
*/
|
|
bpp_print(LOG_INFO, "byte count is %d.\n", bpp_regs_p->dma_bcnt);
|
|
bpp_print(LOG_INFO, "write_timeout is %d.\n",
|
|
bpp_p->transfer_parms.write_timeout);
|
|
if (bpp_regs_p->trans_cntl & BPP_DIRECTION) {
|
|
/* read mode - partial reads will time out */
|
|
bpp_print(LOG_DEBUG, "read timeout, clearing registers.\n");
|
|
start_critical(unit_no);
|
|
bp->b_resid = (bpp_regs_p->dma_bcnt +
|
|
bpp_p->transfer_remainder);
|
|
/* make sure the DMA doesn't start again. */
|
|
bpp_regs_p->dma_bcnt = 0;
|
|
/*
|
|
* Disable the byte counting, and the TC interrupts.
|
|
* Mask the error interrupts too.
|
|
*/
|
|
bpp_regs_p->dma_csr |= BPP_TC_INTR_DISABLE;
|
|
bpp_regs_p->dma_csr &= ~BPP_ENABLE_BCNT;
|
|
bpp_regs_p->dma_csr &= ~BPP_ENABLE_DMA;
|
|
bpp_regs_p->int_cntl &= ~(BPP_ERR_IRQ_EN | BPP_SLCT_IRQ_EN
|
|
| BPP_PE_IRQ_EN);
|
|
end_critical(unit_no);
|
|
} else if ((bpp_regs_p->trans_cntl & BPP_DIRECTION) == 0) {
|
|
/* other write cases (read can time out w/no error) */
|
|
start_critical(unit_no);
|
|
bpp_transfer_failed(unit_no);
|
|
/* Mark the error. */
|
|
/*
|
|
* bp->b_resid will be set to indicate the number
|
|
* of bytes actually transferred by bpp_transfer_failed().
|
|
* If no bytes were transferred, set B_ERROR
|
|
* so that -1 is returned, and set the b_error value.
|
|
*/
|
|
if (!(bp->b_resid)) { /* not a partial transfer */
|
|
bp->b_flags |= B_ERROR;
|
|
bp->b_error = EIO;
|
|
}
|
|
end_critical(unit_no);
|
|
}
|
|
|
|
/* mark the timeout as no longer pending */
|
|
bpp_p->timeouts &= ~TRANSFER_TIMEOUT;
|
|
/* mark the error status structure */
|
|
bpp_p->error_stat.timeout_occurred = 1;
|
|
bpp_print(LOG_INFO, "In bpp_transfer_timeout, Timeout blk is 0x%x.\n",
|
|
bpp_p->timeouts);
|
|
|
|
/*
|
|
* Release the dvma bus resource.
|
|
*/
|
|
start_critical(unit_no);
|
|
(void) mb_mapfree(dvmamap, &bp->b_mbinfo);
|
|
end_critical(unit_no);
|
|
#ifdef DEBUG
|
|
bpp_print(LOG_INFO,
|
|
"In bpp_transfer_timeout, *(&bp->b_mbinfo) = %x\n",
|
|
*(&bp->b_mbinfo));
|
|
#endif DEBUG
|
|
|
|
bpp_print(LOG_DEBUG, "bpp_transfer_timeout, unit %d, Calling iodone.\n",
|
|
unit_no);
|
|
(void) iodone(bp);
|
|
bpp_print(LOG_INFO, "Leaving bpp_transfer_timeout, unit #%d.\n",
|
|
unit_no);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/*<<<<<<<<<<<<<<<<<<<<< PRIVATE SUPPORT ROUTINES >>>>>>>>>>>>>>>>>>>>>*/
|
|
|
|
/*
|
|
* Protect a critical section of code from interruption by the unit whose
|
|
* data structures are currently being serviced. In BPP_DEBUG mode,
|
|
* assertion checking is done to assure the proper pairing of
|
|
* start_critical() and end_critical() calls.
|
|
*
|
|
* ARGS: unit_no The current unit number, an index
|
|
* into the bpp_units array.
|
|
*
|
|
* RETURN VALUE: None.
|
|
*/
|
|
static void
|
|
start_critical(unit_no)
|
|
register u_int unit_no;
|
|
{
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
register int temp_saved_spl;
|
|
|
|
bpp_print(LOG_INFO, "Entering start_critical: unit %d\n", unit_no);
|
|
|
|
bpp_p = bpp_units[unit_no];
|
|
temp_saved_spl = splr((int)(bpp_p->interrupt_pri));
|
|
bpp_print(LOG_DEBUG, "in start_critical, tmp_svd_spl = %x\n",
|
|
temp_saved_spl);
|
|
bpp_print(LOG_DEBUG, "in start_critical, svd_spl = %x\n",
|
|
bpp_p->saved_spl);
|
|
#ifdef BPP_DEBUG
|
|
/*
|
|
* Do assertion checking. This is a critical section in itself.
|
|
* If saved_spl is something other than -1, you are calling
|
|
* start_critical for the second time in a row.
|
|
* No nested critical sections are allowed!
|
|
*/
|
|
if (bpp_p->saved_spl != (-1))
|
|
{
|
|
bpp_print(LOG_INFO,
|
|
"start_critical: ASSERT failed, saved_spl: %d\n",
|
|
bpp_p->saved_spl);
|
|
}
|
|
#endif BPP_DEBUG
|
|
|
|
bpp_p->saved_spl = temp_saved_spl;
|
|
|
|
bpp_print(LOG_INFO, "Leaving start_critical: unit %d\n",
|
|
unit_no);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* End the protection of code from interruption by the unit whose
|
|
* data structures are currently being serviced. In BPP_DEBUG mode,
|
|
* assertion checking is done to assure the proper pairing of
|
|
* start_critical() and end_critical() calls.
|
|
*
|
|
* ARGS: unit_no The current unit number, an index
|
|
* into the bpp_units array.
|
|
*
|
|
* RETURN VALUE: None.
|
|
*/
|
|
static void
|
|
end_critical(unit_no)
|
|
register u_int unit_no;
|
|
{
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
register int temp_saved_spl;
|
|
|
|
bpp_print(LOG_INFO, "Entering end_critical: unit %d\n", unit_no);
|
|
bpp_p = bpp_units[unit_no];
|
|
temp_saved_spl = bpp_p->saved_spl;
|
|
bpp_print(LOG_DEBUG, "in end_critical, tmp_svd_spl = %x\n",
|
|
temp_saved_spl);
|
|
bpp_print(LOG_DEBUG, "in end_critical, svd_spl = %x\n",
|
|
bpp_p->saved_spl);
|
|
|
|
#ifdef BPP_DEBUG
|
|
/*
|
|
* Do assertion checking.
|
|
* If saved_spl is -1, you called end_critical without
|
|
* calling start_critical first.
|
|
*/
|
|
if (bpp_p->saved_spl == (-1))
|
|
{
|
|
bpp_print(LOG_INFO,
|
|
"end_critical: ASSERT failed, saved_spl: %d\n",
|
|
bpp_p->saved_spl);
|
|
}
|
|
|
|
bpp_p->saved_spl = (-1);
|
|
#endif BPP_DEBUG
|
|
|
|
/*
|
|
* Release the critical section last.
|
|
* The above is a critical section in itself.
|
|
*/
|
|
(void) splx(temp_saved_spl);
|
|
|
|
bpp_print(LOG_INFO, "Leaving end_critical: unit %d\n",
|
|
unit_no);
|
|
return;
|
|
}
|
|
|
|
/* Utility Functions */
|
|
|
|
/*
|
|
* see_devinfo:
|
|
* Show interesting information stored in this node of the devinfo tree
|
|
* (for development and debugging).
|
|
*/
|
|
static void
|
|
see_devinfo(devinfo_p)
|
|
register struct dev_info *devinfo_p;
|
|
{
|
|
struct dev_reg *dev_reg_p;
|
|
struct dev_intr *dev_intr_p;
|
|
u_int counter;
|
|
|
|
bpp_print(LOG_INFO, "Entering see_devinfo: devinfo_p 0x%x\n",
|
|
devinfo_p);
|
|
bpp_print(LOG_DEBUG, "devinfo_p->devi_name: %s\n",
|
|
devinfo_p->devi_name);
|
|
bpp_print(LOG_DEBUG, "devinfo_p->devi_unit: %d\n",
|
|
devinfo_p->devi_unit);
|
|
|
|
bpp_print(LOG_DEBUG, "devinfo_p->devi_nreg: %d\n",
|
|
devinfo_p->devi_nreg);
|
|
bpp_print(LOG_DEBUG, "devinfo_p->devi_reg: 0x%x\n",
|
|
devinfo_p->devi_reg);
|
|
/*
|
|
* Show all the device registers.
|
|
*/
|
|
dev_reg_p = devinfo_p->devi_reg;
|
|
/*
|
|
* dev_reg_p may be NULL, but only if devi_nreg is zero.
|
|
*/
|
|
for (counter = 1; counter <= devinfo_p->devi_nreg;
|
|
++counter, ++dev_reg_p)
|
|
{
|
|
if (dev_reg_p == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
bpp_print(LOG_DEBUG,
|
|
"REGISTER #%d: devi_reg->reg_bustype: 0x%x\n",
|
|
counter, dev_reg_p->reg_bustype);
|
|
bpp_print(LOG_DEBUG,
|
|
"REGISTER #%d: devi_reg->reg_addr: 0x%x\n",
|
|
counter, dev_reg_p->reg_addr);
|
|
bpp_print(LOG_DEBUG,
|
|
"REGISTER #%d: devi_reg->reg_size: 0x%x\n",
|
|
counter, dev_reg_p->reg_size);
|
|
}
|
|
|
|
bpp_print(LOG_DEBUG, "devinfo_p->devi_nintr: %d\n",
|
|
devinfo_p->devi_nintr);
|
|
bpp_print(LOG_DEBUG, "devinfo_p->devi_intr: 0x%x\n",
|
|
devinfo_p->devi_intr);
|
|
|
|
/*
|
|
* Show all the interrupt data.
|
|
*/
|
|
dev_intr_p = devinfo_p->devi_intr;
|
|
/*
|
|
* dev_intr_p may be NULL, but only if devi_nintr is zero.
|
|
*/
|
|
for (counter = 1; counter <= devinfo_p->devi_nintr;
|
|
++counter, ++dev_intr_p)
|
|
{
|
|
if (dev_intr_p == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
bpp_print(LOG_DEBUG,
|
|
"INTERRUPT #%d: devi_intr->int_pri: 0x%x\n",
|
|
counter, dev_intr_p->int_pri);
|
|
bpp_print(LOG_DEBUG,
|
|
"INTERRUPT #%d: devi_intr->int_vec: 0x%x\n",
|
|
counter, dev_intr_p->int_vec);
|
|
}
|
|
|
|
bpp_print(LOG_DEBUG, "devinfo_p->devi_data: 0x%x\n",
|
|
devinfo_p->devi_data);
|
|
bpp_print(LOG_DEBUG, "devinfo_p->devi_nodeid: 0x%x\n",
|
|
devinfo_p->devi_nodeid);
|
|
|
|
bpp_print(LOG_INFO, "Leaving see_devinfo: devinfo_p 0x%x\n",
|
|
devinfo_p);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Get a property from the fcode prom on the SBus card, or if the property
|
|
* is not available, substitute the assigned default. Only string valued
|
|
* properties are handled here. You have to know the name of a property
|
|
* that you want to get. There is no way to get all defined properties.
|
|
*
|
|
* ARGS: propname A pointer to string which is the assigned
|
|
* name of the desired property.
|
|
*
|
|
* defaultvalue A pointer to string which is the default
|
|
* value to use if the named property is not
|
|
* defined in the devinfo structure.
|
|
*
|
|
* RETURN VALUE: A pointer to the value string selected.
|
|
* This pointer may have any value that was
|
|
* passed in for the default, including
|
|
* a NULL string or a NULL pointer.
|
|
*/
|
|
static char *
|
|
get_property(devinfo_p, propname, defaultvalue)
|
|
struct dev_info *devinfo_p;
|
|
char *propname;
|
|
char *defaultvalue;
|
|
{
|
|
char *ret_getlongprop;
|
|
char *propval;
|
|
char *msg_string;
|
|
|
|
bpp_print(LOG_INFO, "Entering get_property: devinfo_p: 0x%x\n",
|
|
devinfo_p);
|
|
|
|
ret_getlongprop = (char *) getlongprop(devinfo_p->devi_nodeid,
|
|
propname);
|
|
if (ret_getlongprop == NULL)
|
|
{
|
|
propval = defaultvalue;
|
|
msg_string = "default";
|
|
} else {
|
|
propval = ret_getlongprop;
|
|
msg_string = "from prom";
|
|
}
|
|
|
|
bpp_print(LOG_INFO, "Leaving get_property: %s=%s (%s)\n",
|
|
propname, propval, msg_string);
|
|
}
|
|
|
|
/*
|
|
* Get a numeric property from the fcode prom on the SBus card,
|
|
* or if the property
|
|
* is not available, substitute the assigned default. Only numeric valued
|
|
* properties are handled here. You have to know the name of a property
|
|
* that you want to get. There is no way to get all defined properties.
|
|
*
|
|
* ARGS: propname A pointer to string which is the assigned
|
|
* name of the desired property.
|
|
*
|
|
* defaultvalue A pointer to string which is the default
|
|
* value to use if the named property is not
|
|
* defined in the devinfo structure.
|
|
*
|
|
* RETURN VALUE: The numeric property from the prom,
|
|
* or the default value.
|
|
*/
|
|
static int
|
|
get_num_property(devinfo_p, propname, defaultvalue)
|
|
struct dev_info *devinfo_p;
|
|
char *propname;
|
|
int defaultvalue;
|
|
{
|
|
addr_t ret_getprop;
|
|
int propval;
|
|
char *msg_string;
|
|
|
|
bpp_print(LOG_INFO, "Entering get_num_property: devinfo_p: 0x%x\n",
|
|
devinfo_p);
|
|
|
|
ret_getprop = (addr_t)getprop(devinfo_p->devi_nodeid,
|
|
propname, defaultvalue);
|
|
propval = (int)ret_getprop;
|
|
if (propval == defaultvalue)
|
|
{
|
|
msg_string = "default";
|
|
} else {
|
|
msg_string = "from prom";
|
|
}
|
|
|
|
bpp_print(LOG_INFO, "Leaving get_num_property: %s=%d (%s)\n",
|
|
propname, propval, msg_string);
|
|
return (propval);
|
|
}
|
|
|
|
/*
|
|
* The values of read_setup_time and read_strobe_width
|
|
* have already been bounds-checked. Convert the requested times
|
|
* (in nanoseconds) to SBus clock cycles for the dss and dsw registers.
|
|
* Always round the requested setup time up to the next clock
|
|
* cycle boundary.
|
|
*/
|
|
static void
|
|
set_dss_dsw(unit_no, read_mode)
|
|
u_char unit_no;
|
|
int read_mode; /* 1 if called from read */
|
|
{
|
|
int dss_temp; /* tentative dss value */
|
|
int dsw_temp; /* tentative dsw value */
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
register struct bpp_transfer_parms *bpp_transfer_parms_p;
|
|
|
|
bpp_print(LOG_INFO,
|
|
"Entering set_dss_dsw, unit:%d, read_mode = %x.\n",
|
|
unit_no, read_mode);
|
|
|
|
bpp_p = bpp_units[unit_no];
|
|
bpp_transfer_parms_p = &bpp_p->transfer_parms;
|
|
|
|
if (read_mode) {
|
|
dss_temp =
|
|
bpp_transfer_parms_p->read_setup_time / sbus_clock_cycle;
|
|
if (bpp_transfer_parms_p->read_setup_time % sbus_clock_cycle)
|
|
dss_temp ++; /* round up */
|
|
dsw_temp =
|
|
bpp_transfer_parms_p->read_strobe_width / sbus_clock_cycle;
|
|
if (bpp_transfer_parms_p->read_strobe_width % sbus_clock_cycle)
|
|
dsw_temp ++; /* round up */
|
|
} else {
|
|
dss_temp =
|
|
bpp_transfer_parms_p->write_setup_time / sbus_clock_cycle;
|
|
if (bpp_transfer_parms_p->write_setup_time % sbus_clock_cycle)
|
|
dss_temp ++; /* round up */
|
|
dsw_temp =
|
|
bpp_transfer_parms_p->write_strobe_width / sbus_clock_cycle;
|
|
if (bpp_transfer_parms_p->write_strobe_width % sbus_clock_cycle)
|
|
dsw_temp ++; /* round up */
|
|
}
|
|
|
|
bpp_print(LOG_INFO, "dss = 0x%x, dsw = 0x%x\n", dss_temp, dsw_temp);
|
|
start_critical(unit_no);
|
|
bpp_p->bpp_regs_p->hw_config =
|
|
((((u_char)dsw_temp) << 8) | ((u_char)dss_temp));
|
|
end_critical(unit_no);
|
|
bpp_print(LOG_INFO,
|
|
"Leaving set_dss_dsw, unit:%d.\n", unit_no);
|
|
}
|
|
/*
|
|
* Check the values of the write parameters in the passed bpp_transfer_parms
|
|
* structure. If all the parameters are in range, 0 is returned.
|
|
* If there is an out-of-range parameter, EINVAL is returned.
|
|
*/
|
|
/*ARGSUSED*/
|
|
static u_short
|
|
check_write_params(parms_p, flags)
|
|
struct bpp_transfer_parms *parms_p;
|
|
int flags; /* this unit's (read-write) open flags */
|
|
{
|
|
u_short retval = 0; /* return value (errno) for system call */
|
|
static int max_setup = 0; /* maximum setup time allowed (ns) */
|
|
static int max_width = 0; /* maximum width time allowed (ns) */
|
|
|
|
retval = 0;
|
|
bpp_print(LOG_INFO,
|
|
"Entering check_write_params, parms_p = %x.\n", parms_p);
|
|
bpp_print(LOG_DEBUG, "write_hs %d, write_time %d, ",
|
|
parms_p->write_handshake, parms_p->write_setup_time);
|
|
bpp_print(LOG_DEBUG, "write_width %d, timeout %d.\n",
|
|
parms_p->write_strobe_width, parms_p->write_timeout);
|
|
#ifndef lint
|
|
/* better rangechecking will be added later */
|
|
/* check for legal range */
|
|
if ((parms_p->write_handshake < BPP_NO_HS) ||
|
|
(parms_p->write_handshake > BPP_VPLOT_HS)) {
|
|
bpp_print(LOG_WARNING, "Handshake out of legal range!\n");
|
|
retval = EINVAL;
|
|
goto out;
|
|
}
|
|
/* the handshake values overlap. Check for read handshakes */
|
|
if ((parms_p->write_handshake > BPP_BUSY_HS) &&
|
|
(parms_p->write_handshake < BPP_VPRINT_HS)) {
|
|
bpp_print(LOG_WARNING, "Handshake out of legal write range!\n");
|
|
retval = EINVAL;
|
|
goto out;
|
|
}
|
|
/* versatec handshakes illegal in read-write mode */
|
|
if ((flags & FREAD) && (parms_p->write_handshake > BPP_BUSY_HS)) {
|
|
bpp_print(LOG_WARNING, "No versatec handshakes in read md!\n");
|
|
retval = EINVAL;
|
|
goto out;
|
|
}
|
|
/*
|
|
* Originally there was a plan to support a versatec mode.
|
|
* The decision was made not to support it in software.
|
|
* However, the hooks are still there in the hardware.
|
|
* I leave the versatec fragments in case the decision is ever
|
|
* reversed.
|
|
*/
|
|
/* versatec handshakes not implemented in current code */
|
|
if ((parms_p->write_handshake > BPP_BUSY_HS)) {
|
|
bpp_print(LOG_WARNING, "No versatec handshakes allowed yet!\n");
|
|
retval = EINVAL;
|
|
goto out;
|
|
}
|
|
#endif lint
|
|
/* check range of setup time and strobe width here */
|
|
max_setup = BPP_DSS_SIZE * sbus_clock_cycle;
|
|
max_width = BPP_DSW_SIZE * sbus_clock_cycle;
|
|
|
|
if ((parms_p->write_setup_time < 0) ||
|
|
(parms_p->write_setup_time > max_setup)) {
|
|
bpp_print(LOG_WARNING,
|
|
"Write setup time out of legal range!\n");
|
|
retval = EINVAL;
|
|
goto out;
|
|
}
|
|
if ((parms_p->write_strobe_width < 0) ||
|
|
(parms_p->write_strobe_width > max_width)) {
|
|
bpp_print(LOG_WARNING,
|
|
"Write strobe width out of legal range!\n");
|
|
retval = EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/* check range of write timeout */
|
|
if ((parms_p->write_timeout < 0) ||
|
|
(parms_p->write_timeout > MAX_TIMEOUT)) {
|
|
bpp_print(LOG_WARNING,
|
|
"Write timeout out of legal range!\n");
|
|
retval = EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
bpp_print(LOG_INFO,
|
|
"Leaving check_write_params, retval = %d.\n", retval);
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
* Check the values of the read parameters in the passed bpp_transfer_parms
|
|
* structure. If all the parameters are in range, 0 is returned.
|
|
* If there is an out-of-range parameter, EINVAL is returned.
|
|
*/
|
|
/*ARGSUSED*/
|
|
static u_short
|
|
check_read_params(parms_p, flags)
|
|
struct bpp_transfer_parms *parms_p;
|
|
int flags; /* this unit's (read-write) open flags */
|
|
{
|
|
u_short retval = 0; /* return value (errno) for system call */
|
|
static int max_setup = 0; /* maximum setup time allowed (ns) */
|
|
static int max_width = 0; /* maximum width time allowed (ns) */
|
|
|
|
retval = 0;
|
|
bpp_print(LOG_INFO,
|
|
"Entering check_read_params, parms_p = %x.\n", parms_p);
|
|
bpp_print(LOG_DEBUG,
|
|
"read_hs %d, read_time %d, read_width %d, timeout %d.\n",
|
|
parms_p->read_handshake, parms_p->read_setup_time,
|
|
parms_p->read_strobe_width, parms_p->read_timeout);
|
|
#ifndef lint
|
|
/* check for legal range */
|
|
if ((parms_p->read_handshake < BPP_NO_HS) ||
|
|
(parms_p->read_handshake > BPP_SET_MEM)) {
|
|
bpp_print(LOG_WARNING, "Handshake out of legal range!\n");
|
|
retval = EINVAL;
|
|
goto out;
|
|
}
|
|
#endif lint
|
|
/* check range of setup time and strobe width here */
|
|
max_setup = BPP_DSS_SIZE * sbus_clock_cycle;
|
|
max_width = BPP_DSW_SIZE * sbus_clock_cycle;
|
|
|
|
if ((parms_p->read_setup_time < 0) ||
|
|
(parms_p->read_setup_time > max_setup)) {
|
|
bpp_print(LOG_WARNING,
|
|
"Read setup time out of legal range!\n");
|
|
retval = EINVAL;
|
|
goto out;
|
|
}
|
|
if ((parms_p->read_strobe_width < 0) ||
|
|
(parms_p->read_strobe_width > max_width)) {
|
|
bpp_print(LOG_WARNING,
|
|
"Read strobe width out of legal range!\n");
|
|
retval = EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/* check range of read timeout */
|
|
if ((parms_p->read_timeout < 0) ||
|
|
(parms_p->read_timeout > MAX_TIMEOUT)) {
|
|
bpp_print(LOG_WARNING,
|
|
"Read timeout out of legal range!\n");
|
|
retval = EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
bpp_print(LOG_INFO,
|
|
"Leaving check_read_params, retval = %d.\n", retval);
|
|
return (retval);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static u_short
|
|
check_read_pins(pins_p, flags, handshake)
|
|
struct bpp_pins *pins_p;
|
|
int flags; /* this unit's (read-write) open flags */
|
|
/* this unit's read handshake */
|
|
register enum handshake_t handshake;
|
|
{
|
|
u_short retval = 0; /* return value (errno) for system call */
|
|
bpp_print(LOG_INFO,
|
|
"Entering check_read_pins, pins_p = 0x%x ", pins_p);
|
|
bpp_print(LOG_DEBUG,
|
|
"outpins = 0x%x, inpins = 0x%x.\n", pins_p->output_reg_pins,
|
|
pins_p->input_reg_pins);
|
|
/* check for bogus bits turned on */
|
|
if ((pins_p->output_reg_pins & ~BPP_ALL_OUT_PINS) ||
|
|
(pins_p->input_reg_pins & ~BPP_ALL_IN_PINS)) {
|
|
bpp_print(LOG_WARNING,
|
|
"Check pins : Bogus bit in bpp pins structure!\n");
|
|
retval = EINVAL;
|
|
goto out;
|
|
}
|
|
out:
|
|
bpp_print(LOG_INFO,
|
|
"Leaving check_read_pins, retval = %d.\n", retval);
|
|
return (retval);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static u_short
|
|
check_write_pins(pins_p, flags, handshake)
|
|
struct bpp_pins *pins_p;
|
|
int flags; /* this unit's (read-write) open flags */
|
|
/* this unit's write handshake */
|
|
register enum handshake_t handshake;
|
|
{
|
|
u_short retval = 0; /* return value (errno) for system call */
|
|
bpp_print(LOG_INFO,
|
|
"Entering check_write_pins, pins_p = 0x%x, ", pins_p);
|
|
bpp_print(LOG_DEBUG, "outpins = 0x%x, inpins = 0x%x.\n",
|
|
pins_p->output_reg_pins, pins_p->input_reg_pins);
|
|
/* check for bogus bits turned on */
|
|
if ((pins_p->output_reg_pins & ~BPP_ALL_OUT_PINS) ||
|
|
(pins_p->input_reg_pins & ~BPP_ALL_IN_PINS)) {
|
|
bpp_print(LOG_WARNING,
|
|
"Check pins : Bogus bit in bpp pins structure!\n");
|
|
retval = EINVAL;
|
|
goto out;
|
|
}
|
|
/*
|
|
* Originally there was a plan to support a versatec mode.
|
|
* The decision was made not to support it in software.
|
|
* However, the hooks are still there in the hardware.
|
|
* I leave the versatec fragments in case the decision is ever
|
|
* reversed.
|
|
*/
|
|
#ifndef lint
|
|
/* versatec handshakes not implemented in current code */
|
|
if ((handshake > BPP_BUSY_HS)) {
|
|
bpp_print(LOG_WARNING, "No versatec handshakes allowed yet!\n");
|
|
/*
|
|
* really, need to check for one bit only of remote
|
|
* pins set.
|
|
*/
|
|
retval = EINVAL;
|
|
goto out;
|
|
}
|
|
#endif lint
|
|
out:
|
|
bpp_print(LOG_INFO,
|
|
"Leaving check_write_pins, retval = %d.\n", retval);
|
|
return (retval);
|
|
}
|
|
|
|
static void
|
|
read_outpins(unit_no, flags, handshake)
|
|
u_char unit_no;
|
|
int flags; /* this unit's (read-write) open flags */
|
|
/* this unit's write handshake */
|
|
register enum handshake_t handshake;
|
|
{
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
u_char temppins;
|
|
bpp_print(LOG_INFO,
|
|
"Entering read_outpins, unit = %d, flags = 0x%x, ",
|
|
unit_no, flags);
|
|
bpp_p = bpp_units[unit_no];
|
|
|
|
bpp_print(LOG_DEBUG, "handshake = %d.\n", handshake);
|
|
if (flags & FWRITE) {
|
|
|
|
#ifndef lint
|
|
if ((handshake > BPP_BUSY_HS)) {
|
|
bpp_print(LOG_WARNING,
|
|
"No versatec handshakes allowed yet!\n");
|
|
}
|
|
#endif lint
|
|
temppins = bpp_p->bpp_regs_p->out_pins &
|
|
(BPP_SLCTIN_PIN | BPP_AFX_PIN | BPP_INIT_PIN);
|
|
bpp_p->pins.output_reg_pins |= temppins;
|
|
}
|
|
|
|
bpp_print(LOG_INFO, "Leaving read_outpins.\n");
|
|
}
|
|
/*
|
|
* bpp_stuff_fregs()
|
|
*
|
|
* This routine will simulate the device registers for the bpp DMA
|
|
* registers of the SBus-HIOD
|
|
*
|
|
*/
|
|
|
|
/*ARGSUSED*/
|
|
static void
|
|
bpp_stuff_fregs(unit_no)
|
|
u_int unit_no; /* attaching unit's number */
|
|
{
|
|
|
|
#ifdef NO_BPP_HW
|
|
/*
|
|
* set up default registers to the expected (inital) values
|
|
*/
|
|
|
|
bpp_def_regs.dma_csr = 0x40000100; /* device id = 0100 */
|
|
bpp_def_regs.dma_addr = 0;
|
|
bpp_def_regs.dma_bcnt = 0;
|
|
bpp_def_regs.unused = 0;
|
|
bpp_def_regs.hw_config = 0;
|
|
bpp_def_regs.op_config = BPP_DS_BIDIR;
|
|
bpp_def_regs.data = 0;
|
|
bpp_def_regs.trans_cntl = BPP_DIRECTION | 0x40;
|
|
bpp_def_regs.out_pins = 0;
|
|
bpp_def_regs.in_pins = 0;
|
|
bpp_def_regs.int_cntl = 0;
|
|
|
|
/*
|
|
* now copy default registers into mimic struct
|
|
*/
|
|
|
|
bpp_fake_regs[unit_no] = bpp_def_regs;
|
|
#endif NO_BPP_HW
|
|
}
|
|
|
|
/*
|
|
* Peek at the bpp registers to make sure that they really
|
|
* exist. Also check initial conditions. If any of this
|
|
* fails, return a non-zero value.
|
|
*/
|
|
static int
|
|
check_bpp_registers(unit_no)
|
|
u_int unit_no; /* attaching unit's number */
|
|
{
|
|
u_long *l_reg_addr; /* address of a 32-bit register */
|
|
u_long l_reg_contents; /* contents of a 32-bit register */
|
|
u_short *s_reg_addr; /* address of a 16-bit register */
|
|
u_short s_reg_contents; /* contents of a 16-bit register */
|
|
u_char *c_reg_addr; /* address of a 8-bit register */
|
|
u_short c_reg_contents; /* contents of a 8-bit register */
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
|
|
bpp_print(LOG_INFO, "Entering check_bpp_registers, unit %d.\n",
|
|
unit_no);
|
|
bpp_p = bpp_units[unit_no];
|
|
/* check the long dma registers */
|
|
/* dma csr */
|
|
l_reg_addr = &(bpp_p->bpp_regs_p->dma_csr);
|
|
if (peekl((long *)l_reg_addr, (long *)&l_reg_contents) == -1) {
|
|
bpp_print(LOG_WARNING,
|
|
"ck_bpp_registers: peek failed\n",
|
|
"dma csr, address %x, contents %x\n",
|
|
l_reg_addr, l_reg_contents);
|
|
return (1);
|
|
}
|
|
|
|
|
|
else
|
|
bpp_print(LOG_INFO, "dma_csr contains %x\n", l_reg_contents);
|
|
|
|
/* dma addr */
|
|
l_reg_addr = &(bpp_p->bpp_regs_p->dma_addr);
|
|
if (peekl((long *)l_reg_addr, (long *)&l_reg_contents) == -1) {
|
|
bpp_print(LOG_WARNING,
|
|
"ck_bpp_registers: peek failed dma addr, address %x\n",
|
|
l_reg_addr, l_reg_contents);
|
|
return (1);
|
|
} else
|
|
bpp_print(LOG_INFO, "dma_addr contains %x\n", l_reg_contents);
|
|
|
|
/* dma bcnt */
|
|
l_reg_addr = &(bpp_p->bpp_regs_p->dma_bcnt);
|
|
if (peekl((long *)l_reg_addr, (long *)&l_reg_contents) == -1) {
|
|
bpp_print(LOG_WARNING,
|
|
"ck_bpp_registers: peek failed dma bcnt, address %x\n",
|
|
l_reg_addr);
|
|
return (1);
|
|
} else
|
|
bpp_print(LOG_INFO, "dma_bcnt contains %x\n", l_reg_contents);
|
|
|
|
/* short hardware registers */
|
|
/* hw_config */
|
|
s_reg_addr = &(bpp_p->bpp_regs_p->hw_config);
|
|
if ((s_reg_contents = peek((short *)s_reg_addr)) == (u_short)-1) {
|
|
bpp_print(LOG_WARNING,
|
|
"ck_bpp_registers: peek failed hw_config, address %x\n",
|
|
s_reg_addr);
|
|
return (1);
|
|
}
|
|
if (poke((short *)s_reg_addr, (short)s_reg_contents) != 0) {
|
|
bpp_print(LOG_WARNING,
|
|
"ck_bpp_registers: poke failed hw_config, address %x\n",
|
|
s_reg_addr);
|
|
return (1);
|
|
} else
|
|
bpp_print(LOG_INFO, "hw_config contains %x\n", s_reg_contents);
|
|
|
|
/* op_config */
|
|
s_reg_addr = &(bpp_p->bpp_regs_p->op_config);
|
|
if ((s_reg_contents = peek((short *)s_reg_addr)) == (u_short)-1) {
|
|
bpp_print(LOG_WARNING,
|
|
"ck_bpp_registers: peek failed op_config, address %x\n",
|
|
s_reg_addr);
|
|
return (1);
|
|
} else
|
|
bpp_print(LOG_INFO, "op_config contains %x\n", s_reg_contents);
|
|
|
|
/* int_cntl */
|
|
s_reg_addr = &(bpp_p->bpp_regs_p->int_cntl);
|
|
if ((s_reg_contents = peek((short *)s_reg_addr)) == (u_short)-1) {
|
|
bpp_print(LOG_WARNING,
|
|
"ck_bpp_registers: peek failed int_cntl, address %x\n",
|
|
s_reg_addr);
|
|
return (1);
|
|
} else
|
|
bpp_print(LOG_INFO, "int_cntl contains %x\n", s_reg_contents);
|
|
|
|
/* char hardware registers */
|
|
/* data */
|
|
c_reg_addr = &(bpp_p->bpp_regs_p->data);
|
|
if ((c_reg_contents = peekc((char *)c_reg_addr)) == (u_short)-1) {
|
|
bpp_print(LOG_WARNING,
|
|
"ck_bpp_registers: peek failed data, address %x\n",
|
|
c_reg_addr);
|
|
return (1);
|
|
} else
|
|
bpp_print(LOG_INFO, "data contains %x\n", c_reg_contents);
|
|
|
|
/* trans_cntl */
|
|
c_reg_addr = &(bpp_p->bpp_regs_p->trans_cntl);
|
|
if ((c_reg_contents = peekc((char *)c_reg_addr)) == (u_short)-1) {
|
|
bpp_print(LOG_WARNING,
|
|
"ck_bpp_registers:peek failed trans_cntl, address %x\n",
|
|
c_reg_addr);
|
|
return (1);
|
|
} else
|
|
bpp_print(LOG_INFO, "trans_cntl contains %x\n", c_reg_contents);
|
|
|
|
/* out_pins */
|
|
c_reg_addr = &(bpp_p->bpp_regs_p->out_pins);
|
|
if ((c_reg_contents = peekc((char *)c_reg_addr)) == (u_short)-1) {
|
|
bpp_print(LOG_WARNING,
|
|
"ck_bpp_registers: peek failed out_pins, address %x\n",
|
|
c_reg_addr);
|
|
return (1);
|
|
} else
|
|
bpp_print(LOG_INFO, "out_pins contains %x\n", c_reg_contents);
|
|
|
|
/* in_pins */
|
|
c_reg_addr = &(bpp_p->bpp_regs_p->in_pins);
|
|
if ((c_reg_contents = peekc((char *)c_reg_addr)) == (u_short)-1) {
|
|
bpp_print(LOG_WARNING,
|
|
"ck_bpp_registers: peek failed in_pins, address %x\n",
|
|
c_reg_addr);
|
|
return (1);
|
|
} else
|
|
bpp_print(LOG_DEBUG, "in_pins contains %x\n", c_reg_contents);
|
|
bpp_print(LOG_INFO, "Leaving check_bpp_registers, unit %d.\n", unit_no);
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
bpp_print_regs (unit_no, msg)
|
|
u_int unit_no;
|
|
char *msg;
|
|
{
|
|
register struct bpp_regs *bpp_regs_p;
|
|
register struct bpp_unit *bpp_p; /* will point to this */
|
|
/* unit's state struct */
|
|
|
|
bpp_p = bpp_units[unit_no];
|
|
bpp_regs_p = bpp_p->bpp_regs_p;
|
|
bpp_print(LOG_INFO, "%s, dma_csr = 0x%x, dma_addr = 0x%x.\n",
|
|
msg, bpp_regs_p->dma_csr, bpp_regs_p->dma_addr);
|
|
bpp_print(LOG_INFO, "dma_bcnt = %d, hw_config = 0x%x.\n",
|
|
bpp_regs_p->dma_bcnt, bpp_regs_p->hw_config);
|
|
bpp_print(LOG_INFO, "op_config = 0x%x, data = 0x%x.\n",
|
|
bpp_regs_p->op_config, bpp_regs_p->data);
|
|
bpp_print(LOG_INFO, "trans_cntl = 0x%x, out_pins = 0x%x.\n",
|
|
bpp_regs_p->trans_cntl, bpp_regs_p->out_pins);
|
|
bpp_print(LOG_INFO, "in_pins = 0x%x, int_cntl = 0x%x.\n",
|
|
bpp_regs_p->in_pins, bpp_regs_p->int_cntl);
|
|
}
|
|
|
|
|
|
/* VARARGS */
|
|
static void
|
|
bpp_print(level, fmt, a, b, c, d, e, f, g, h)
|
|
int level;
|
|
char *fmt;
|
|
int a, b, c, d, e, f, g, h;
|
|
{
|
|
if (level <= bpp_printlevel) {
|
|
log(level, fmt, a, b, c, d, e, f, g, h);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef NO_BPP_HW
|
|
#include "bpp_test_code.c"
|
|
#endif NO_BPP_HW
|