bpp.c revision b0b35ace1e62aef432dcf5587270182d6c65cade
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Source code for the bidirectional parallel port
* driver for the Zebra SBus card, and the parallel
* port in the DMA2P and MACHIO.
*
*/
/* #includes below */
/* 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 */
};
/* static variable declarations below */
/* array of pointers to unit structs */
static int sbus_clock = 0; /* sbus clock freq prop in MHz */
static int sbus_cycle = 0; /* sbus clock prop period in nsec */
static void *bpp_state_head; /* opaque handle top of state structs */
static ddi_dma_lim_t bpp_limits = {
0x00000000, /* lower range limit */
((1<<24)-1), /* inclusive upper bound of */
/* bpp dma address counter */
/* lower 24 bits are a counter, */
/* upper 8 bits are registered */
DEFAULT_BURSTSIZE, /* encoded burstsizes */
0 /* dma speed - don't care */
};
#ifndef BPP_DEBUG
#define BPP_DEBUG 0
#endif /* BPP_DEBUG */
#if BPP_DEBUG > 0
#else
#endif /* BPP_DEBUG */
/* private procedure declarations below */
/* Autoconfig Declarations */
/* Driver function Declarations */
/* Utility Function Declarations */
static int check_bpp_registers(int unit_no);
register enum handshake_t handshake);
static void check_for_active_pins(int unit_no);
static void bpp_transfer_timeout(void *unit_no_arg);
static void bpp_transfer_failed(int unit_no);
/*
* The bpp_cb_ops struct enables the kernel to find the
* rest of the driver entry points.
*/
static struct cb_ops bpp_cb_ops = {
bpp_open, /* driver open routine */
bpp_close, /* driver close routine */
nulldev, /* driver strategy routine - block devs only */
nodev, /* driver print routine */
nodev, /* driver dump routine */
bpp_read, /* driver read routine */
bpp_write, /* driver write routine */
bpp_ioctl, /* driver ioctl routine */
nodev, /* driver devmap routine */
nulldev, /* driver mmap routine */
nulldev, /* driver segmap routine */
nochpoll, /* driver chpoll routine */
ddi_prop_op, /* driver prop_op routine */
0, /* driver cb_str - STREAMS only */
};
/*
* The bpp_ops struct enables the kernel to find the
* bpp loadable module routines.
*/
{
DEVO_REV, /* revision number */
0, /* device reference count */
bpp_getinfo, /* driver get_dev_info routine */
nulldev, /* confirm device ID */
nulldev, /* device probe for non-self-id */
bpp_attach, /* attach routine of driver */
bpp_detach, /* device detach routine */
nodev, /* device reset routine */
&bpp_cb_ops, /* device operations struct */
(struct bus_ops *)0, /* bus operations */
};
/*
* The bpp_drv structure provides the linkage between the vd driver
* (for loadable drivers) and the dev_ops structure for this driver
* (bpp_ops).
*/
&mod_driverops, /* type of module - driver */
"pport driver: bpp %I% %E%", /* name of module */
&bpp_ops /* *Drv_dev_ops */
};
static struct modlinkage modlinkage = {
&modldrv,
};
/* Autoconfig Support Functions */
/*
* bpp_attach()
*
* Allocate unit structures.
* 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.
*/
static int
{
int unit_no; /* attaching unit's number */
int sbus_frequency; /* sbus clock frequency (in cycles) */
int burst_sizes; /* sbus burst sizes, encoded */
/* unit's state struct */
volatile struct bpp_regs *bpp_regs_p;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_FAILURE);
return (DDI_FAILURE);
}
goto initialise;
default:
return (DDI_FAILURE);
}
/* Make sure we're not in a slave-only slot */
"bpp unit %d: NOT used - SBus slot is slave only.",
unit_no);
return (DDI_FAILURE);
}
/*
* Allocate a unit structure for this unit.
* Each bpp_unit struct is allocated as zeroed memory.
* Store away its address for future use.
*/
unit_no));
return (DDI_FAILURE);
/* assign a pointer to this unit's state struct */
/*
* Initialize the unit structures. The unit structure for
* each unit is initialized when bpp_attach is called for that unit.
*/
/*
* 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.
*/
(ddi_idevice_cookie_t *)0, bpp_intr,
"bpp_attach unit %d: cannot add interrupt!", unit_no);
return (DDI_FAILURE);
}
/*
* Initialize the bpp mutex.
* This mutex is used for all operations outside of attach.
*/
(void *)bpp_p->bpp_block_cookie);
/*
* Save the devinfo pointer for this unit.
* Initialize the interupt cookie.
* Inhibit opens on this unit until initialization is successful.
*/
/*
* Initialize the transfer parameters structure for this unit.
*/
/*
* Initialize the control pins structure for this unit.
*/
/*
* Initialize the error status structure for this unit.
* Initialize the timeout status byte for this unit.
* Initialize the timeout idents for this unit.
*/
/*
* Check that the clock-frequency property 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.
*/
"clock-frequency", 1000000);
"clock-frequency prop is: %d\n", sbus_frequency));
/* calculate clock period (in nsec) */
} else {
return (DDI_FAILURE);
}
/*
* Map in any device registers. The zebra parallel section
* has only one register area.
*/
/*
* Map the structure into kernel virtual space.
*/
0, sizeof (struct bpp_regs)) != DDI_SUCCESS) {
"bpp_attach unit %d: ddi_map_regs failed!", unit_no);
return (DDI_FAILURE);
}
"bpp_attach unit %d: register check failed!", unit_no);
0, sizeof (struct bpp_regs));
return (DDI_FAILURE);
}
/* The driver is now commited - all sanity checks done */
unit_no);
0, sizeof (struct bpp_regs));
return (DDI_FAILURE);
}
}
/*
* The burst-sizes property encodes which SBus burst sizes this
* cpu will support. Each binary digit represents a power of
* two. Thus 0x37 would indicate 1,2,4,16, and 32-byte bursts
* are supported.
* Use this info to program the P_BURST_SIZE bits of the
* P_CSR.
*/
"^burst-sizes prop is: 0x%x\n", burst_sizes));
/*
* Starting with the DMA2P, the bpp lives with a DMA controller
* which can support different burst sizes. Determine the
* SBus burst size and program the register.
* Be sure to always look for largest-possible burst-size first.
* On an HIOD (zebra card) there is no active register at that
* location.
*/
#if BPP_DEBUG
/*
* HIOD DMA engine only supports 4-word (default) bursts, no
* programmable register.
*/
"bpp_attach: devid field indicates HIOD bpp DMA\n"));
/* no register, so do nothing here */
} else
#endif /* BPP_DEBUG */
"bpp_attach: devid field indicates DMA2P bpp DMA\n"));
if ((burst_sizes == 0xff) ||
(!((burst_sizes & BURST16) ||
(burst_sizes & BURST32)))) {
"bad burst-sizes 0x%x, setting to %x\n",
"Setting P_BURST_SIZE for 8-word bursts\n"));
} else if (burst_sizes & BURST16) {
"Setting P_BURST_SIZE for 4-word bursts\n"));
}
"bpp_attach: undefined bpp DMA"));
"using 0x%x for bursts.\n", BPP_BURST_DEFAULT));
}
/*
* Perform device initialization.
*/
/*
* 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_regs_p->int_cntl));
/* SLCT+ means off-line */
/* clear any stray interrupts */
bpp_regs_p->int_cntl));
/* Turn on interrupts */
& BPP_VERSATEC_INTERLOCK) { /* versatec connector absent */
/* block versatec handshake modes */
} else {
/* allow versatec handshake modes */
}
return (DDI_SUCCESS);
}
/*
* xx_getinfo is called from the framework to determine the devinfo pointer
* or instance number corresponding to a given dev_info_t.
*/
/*ARGSUSED*/
static int
{
register int error;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
error = DDI_FAILURE;
} else {
error = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
/*
* _init is called by the autoloading code when the special file is
* first opened, or by modload().
*/
int
_init(void)
{
register int error;
sizeof (struct bpp_unit), 1)) != 0) {
return (error);
}
return (error);
}
/*
* _info is called by modinfo().
*/
int
{
}
/*
* _fini is called by
* modunload() just before the driver is unloaded from system memory.
*/
int
_fini(void)
{
int status;
return (status);
return (status);
}
/*
* Turn off interrupts, remove registration of all interrupt vectors,
* and release all memory.
* This routine does the reverse of the attach routine.
*
*/
static int
{
/* unit's state struct */
volatile struct bpp_regs *bpp_regs_p;
int unit_no;
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (DDI_FAILURE);
return (DDI_FAILURE);
}
/* XXX - Need to Wait for pending ops to finish */
return (DDI_SUCCESS);
default:
"Invalid detach cmd 0x%x in bpp_detach\n", cmd));
goto detach_failed;
}
/* Turn off interrupts for this unit. */
/* was transferring */
"ERROR: bpp unload of unit %d while DMA active!",
unit_no);
/* turn off DMA and byte count */
/* Reset PP state machine */
/* flush the cache */
}
/*
* 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->int_cntl &=
/*
* XXX This comment no longer applies to 5.x
*
* 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.
*/
/* Remove the minor node created in attach */
/*
* Unmap register area from kernel memory.
*/
0, sizeof (struct bpp_regs));
/*
* Remove interrupt registry
*/
}
/* Destroy the per-unit mutex. */
/* Free the memory allocated for this unit's state struct */
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/* Normal Device Driver routines */
/*
* Open the device.
*/
/*ARGSUSED*/
static int
{
int unit_no;
/* unit's state struct */
/*
* Assure that the device is being opened as a character device.
*/
"bpp%d attempted open as non-character device!",
unit_no);
goto out;
}
/*
* Check for allocation of unit structures.
*/
"bpp%d unit pointer is NULL!", unit_no);
goto out;
}
/*
* Only allow a single open. If this device has
* already been opened, return an error.
*/
goto out;
}
/*
* Mark the bpp as opened.
*/
/*
* Initialize the transfer parameters structure
* and initialize the control pins structure
* for this unit.
*/
out:
return (retval);
}
/*
* Close the device.
*/
/*ARGSUSED*/
static int
{
int unit_no;
/* unit's state struct */
timeout_id_t tid = 0;
"Entering bpp_close, unit number %d.\n", unit_no));
}
}
}
/*
* Mark unit closed.
*/
if (tid)
return (0);
}
/*
* Read system call.
*/
/*ARGSUSED*/
static int
{
int unit_no;
volatile struct bpp_regs *bpp_regs_p;
struct bpp_transfer_parms *bpp_transfer_parms_p;
/* to change from data sink */
/* to source */
/* unit's state struct */
unit_no));
return (EINTR);
}
/*
*/
}
/*
* Set the handshake bits
*/
/*
* make sure the memory clear operation is turned off
*/
switch (bpp_transfer_parms_p->read_handshake) {
case BPP_NO_HS:
break;
case BPP_ACK_HS:
bpp_regs_p->op_config |=
break;
case BPP_BUSY_HS:
case BPP_HSCAN_HS:
break;
case BPP_ACK_BUSY_HS:
bpp_regs_p->op_config |=
break;
case BPP_XSCAN_HS:
/*
* reads with the Xerox use ACK handshake
* and unidirectional operation
*/
bpp_regs_p->op_config &=
break;
case BPP_CLEAR_MEM:
break;
case BPP_SET_MEM:
break;
}
/*
* The direction should not be marked until after the handshake
* bits have been set.
*/
/* set the dss and dsw values */
/*
*/
/* The HP Scanjet uses AFX */
} else {
}
}
"Leaving bpp_read, unit %d: errno %d.\n",
return (retval);
}
/*
* Write system call.
*/
/*ARGSUSED*/
static int
{
int unit_no;
register volatile struct bpp_regs *bpp_regs_p;
register struct bpp_transfer_parms *bpp_transfer_parms_p;
/* unit's state struct */
"Entering bpp_write, unit number %d.\n", unit_no));
return (EINTR);
}
/* clear any old error status */
/*
* 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_regs_p->int_cntl));
/* SLCT+ is off-line */
bpp_regs_p->int_cntl));
/*
* 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.
*/
/*
*/
/* The HP Scanjet uses AFX */
} else {
}
} else {
if ((bpp_errorstat_p->pin_status &
/* printer error - no transfer allowed */
"In bpp_write, pending error pin condition\n"));
goto out;
}
}
/* mark the transfer direction in the hardware */
/*
* make sure the memory clear operation is turned off
*/
/*
* Set the handshake bits
*/
}
/* Make sure that ACK and BUSY are unidirectional */
/* set the dss and dsw values */
set_dss_dsw(unit_no, 0);
out:
return (retval);
}
/*
* Limit transfer size to the smaller of
* - system minphys size
* - 16 MB limit in HIOD address register
*/
static void
{
}
/*
* Check to see if any of the control pins are active.
*/
static void
{
/* unit's state struct */
register volatile struct bpp_regs *bpp_regs_p;
"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_regs_p->in_pins));
bpp_regs_p->int_cntl));
"In ck_active_pins, pending ERR condition\n"));
}
"In ck_active_pins, pending SLCT condition\n"));
}
"In check_active_pins, pending PE condition\n"));
}
"Leaving check_for_active_pins, unit number %d.\n", unit_no));
}
/*
* Setup and start a transfer on the device.
*/
/*ARGSUSED*/
static int
{
/* unit's state struct */
int unit_no;
int timeout_value; /* read or write timeout in secs */
register struct bpp_transfer_parms *bpp_transfer_parms_p;
register volatile struct bpp_regs *bpp_regs_p;
int flags; /* flags to pass to ddi_dma_buf_setup */
"bpp%d:Entering bpp_strategy: length 0x%x.\n",
/*
* Use the unit number to locate our data structures.
*/
}
/* Clear the unit error status struct */
/* Set DMA request flags based on struct buf flags */
"Before dma_buf_setup, b_addr = 0x%p, b_bcount = 0x%x\n",
/*
* Get dvma bus resource, sleeping if necessary.
*/
(caddr_t)0, &bpp_limits,
&bpp_p->bpp_dma_handle)) != 0) {
"ERROR: bpp%d: ddi_dma_buf_setup failed mapping",
unit_no);
}
"After dma_buf_setup, b_addr = 0x%p, b_bcount = 0x%x\n",
/*
* Convert the dma_handle into an actual virtual
* DVMA address which can be put into the DMA engine.
*/
&dma_cookie) != 0) {
"ERROR: bpp%d: ddi_dma_htoc failed to fill in DMA cookie!",
unit_no);
}
"After dma_htoc, start_address = 0x%x\n", start_address));
/*
* Write the dma start address to the hardware.
* Write the transfer byte count to the hardware.
*/
start_address, size));
"bpp_strategy: Transfer %d bytes starting at 0x%x.\n",
"before enabling interrupts, dma csr=0x%x, int_cntl=0x%x.\n",
/*
* 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.
*/
/*
* Do not close critical section until timeouts have been
* enabled, otherwise we might get an untimeout before
* the timeout has been set!
*/
"bp->b_flags indicates READ mode\n"));
} else {
"bp->b_flags indicates WRITE mode\n"));
}
"after enable error int. int cntl contains 0x%x.\n",
bpp_regs_p->int_cntl));
}
"after enabling interrupts, dma csr = 0x%x, int_cntl = 0x%x.\n",
"Setting timeout to call bpp_transfer_timeout in %d sec\n",
return (0);
}
/*
* Handle special control requests
*/
/*ARGSUSED*/
static int
{
int unit_no;
ushort_t read_retval = 0;
ushort_t write_retval = 0;
/* unit's state struct */
volatile struct bpp_regs *bpp_regs_p;
struct bpp_transfer_parms *bpp_transfer_parms_p;
struct bpp_transfer_parms temp_parms;
register enum handshake_t write_handshake;
register enum handshake_t read_handshake;
"Entering bpp_ioctl, unit number %d.\n", unit_no));
}
switch (cmd) {
case BPPIOC_SETPARMS: /* set transfer parameters */
/* copy passed parms to temporary storage */
"Checking read parameters.\n"));
}
"Checking write parameters.\n"));
}
if (read_retval || write_retval) {
} else { /* valid parameters */
}
break;
case BPPIOC_GETPARMS: /* get transfer parameters */
retval = 0;
break;
case BPPIOC_SETOUTPINS: /* set output pins */
/* copy passed parms to temporary storage */
sizeof (struct bpp_pins));
bpp_pins_p = &temp_pins;
"Checking read pins.\n"));
if (read_retval == 0) { /* valid pins */
}
}
"Checking write pins.\n"));
if (write_retval == 0) { /* valid pins */
}
}
if (read_retval || write_retval) {
} else { /* All is well, write the registers */
/* the previous line will not cstyle */
}
break;
case BPPIOC_GETOUTPINS: /* get output pins */
/* read the current pin state into the struct */
retval = 0;
break;
case BPPIOC_GETERR: /* get error block status */
retval = 0;
break;
case BPPIOC_TESTIO: /* test transfer readiness */
retval = 0;
/* clear any old error status */
/* if any active pins were found, return -1 */
if (bpp_errorstat_p->pin_status &
"In TESTIO, found error pin condition\n"));
} else
retval = 0;
break;
/* TEST - request partial fake transfer */
case BPPIOC_SETBC:
break;
/* TEST - get DMA_BCNT from last data transfer */
case BPPIOC_GETBC:
break;
/* TEST - get contents of device registers */
case BPPIOC_GETREGS:
break;
/* TEST - set special fakeout error code to simulate errs */
case BPPIOC_SETERRCODE:
break;
/* TEST - get pointer to fakeout transferred data */
case BPPIOC_GETFAKEBUF:
break;
default:
break;
}
return (retval);
}
/*
* Handle an interrupt or interrupts that may or may not be from
* one or more of the bpp units.
*/
static uint_t
{
int bpp_unit_no;
register volatile struct bpp_regs *bpp_regs_p;
/* unit's state struct */
/*
* Check that this unit is indeed interrupting.
*/
bpp_unit_no));
/*
* Mark that we found an interrupting device.
* Call the interrupt service routine.
*/
}
/*
* 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_DEBUG
}
#endif /* BPP_DEBUG */
/* 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.
*/
return (DDI_INTR_CLAIMED);
} else {
/* will make return value -1 */
}
/*
* capture the error status in the error_stat struct
* so the application can get it with the GETERR
* ioctl later.
*/
/* Mark the error. */
/* clear the error interrupt */
;
/* wait here */
/* cannot assert FLUSH till cache drains */
/* spin on draining bit */
}
bpp_regs_p->dma_csr));
bpp_regs_p->int_cntl));
/* TC case - terminal count */
0)) {
"Terminal count interrupt found.\n"));
/* mask this interrupt */
/* and clear the interrupting condition */
/* Mask the error interrupt conditions */
bpp_regs_p->int_cntl &=
bpp_regs_p->dma_csr));
bpp_regs_p->int_cntl));
}
/* ERR_IRQ case - error pin interrupt */
"Error pin interrupt found.\n"));
/* was transferring */
}
/* clear interrupting condition */
}
/* SLCT_IRQ case - select pin interrupt */
"Select pin interrupt found.\n"));
/* was transferring */
}
/* clear interrupting condition */
}
/* PE_IRQ case - paper error pin interrupt */
"Paper error pin interrupt found.\n"));
/* was transferring */
}
/* clear interrupting condition */
}
/*
* 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 */
"Busy pin interrupt found.\n"));
/* for pio only */
/* clear interrupting condition */
}
/* ACK_IRQ case - acknowledge pin interrupt */
"Acknowledge pin interrupt found.\n"));
/* for pio only */
/* clear interrupting condition */
}
/* DS_IRQ case - data strobe pin interrupt */
"Data strobe pin interrupt found.\n"));
/* for pio only */
/* clear interrupting condition */
}
} /* end of INT_PEND check */
/* Clear the transfer timeout */
"In bpp_intr, Clearing transfer timeout.\n"));
/*
* Release the dvma bus resource.
*/
"bpp_intr, unit %d, Calling biodone.\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.
*/
if (tid)
} else {
}
}
int_serviced));
return (int_serviced);
}
/*
* A transfer has failed for some reason.
* Mark the bp struct to indicate how much happened,
* and turn off the transfer and its interrupts.
*/
static void
{
register volatile struct bpp_regs *bpp_regs_p;
/* unit's state struct */
/*
* 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 */
/* If the DMA state machines are not idle, reset them. */
"In bpp_strategy, resetting PP state machine\n"));
/*
* we have not received the acknowledge for the last
* byte transferred, so the byte counter was never
* decremented for it.
*/
} else {
}
/* flush the local cache */
/* 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->int_cntl &=
/* Check for any of the input pins active */
}
/*
* 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 biodone to wake up strategy.
*/
static void
bpp_transfer_timeout(void *unit_no_arg)
{
/* unit's state struct */
register volatile struct bpp_regs *bpp_regs_p;
unit_no));
"In bpp_transfer_timeout, Timeout block is 0x%x.\n",
if (bpp_p->bpp_transfer_timeout_ident == 0) {
return;
}
/*
* Use the unit number to locate our data structures.
*/
/*
* If we're talking to the
* Ricoh scanner, handle it's special "hold busy"
* reset the parallel port
*/
/* read mode - partial reads will time out */
/* 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_PE_IRQ_EN);
/* 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.
*/
}
}
/* mark this timeout as no longer pending */
/* mark the error status structure */
"In bpp_transfer_timeout, Timeout blk is 0x%x.\n",
/*
* Release the dvma bus resource.
*/
"bpp_transfer_timeout, unit %d, Calling biodone.\n", unit_no));
unit_no));
}
/* Utility Functions */
/*
* 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
{
int dss_temp; /* tentative dss value */
int dsw_temp; /* tentative dsw value */
/* unit's state struct */
register struct bpp_transfer_parms *bpp_transfer_parms_p;
"Entering set_dss_dsw, unit:%d, read_mode = %x.\n",
if (read_mode) {
dss_temp =
dss_temp ++; /* round up */
dsw_temp =
dsw_temp ++; /* round up */
} else {
dss_temp =
dss_temp ++; /* round up */
dsw_temp =
dsw_temp ++; /* round up */
}
"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 ushort_t
{
static int max_setup = 0; /* maximum setup time allowed (ns) */
static int max_width = 0; /* maximum width time allowed (ns) */
/* unit's state struct */
retval = 0;
"Entering check_write_params, parms_p = %x.\n", parms_p));
#ifndef lint
/* better rangechecking will be added later */
/* check for legal range */
goto out;
}
/* the handshake values overlap. Check for read handshakes */
"Handshake out of legal write range!\n"));
goto out;
}
/* versatec handshakes illegal in read-write mode */
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 */
"No versatec handshakes allowed yet!\n"));
goto out;
}
#endif /* lint */
/* check range of setup time and strobe width here */
if ((parms_p->write_setup_time < 0) ||
"Write setup time out of legal range!\n"));
goto out;
}
if ((parms_p->write_strobe_width < 0) ||
"Write strobe width out of legal range!\n"));
goto out;
}
/* check range of write timeout */
if ((parms_p->write_timeout < 0) ||
"Write timeout out of legal range!\n"));
goto out;
}
out:
"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 ushort_t
{
static int max_setup = 0; /* maximum setup time allowed (ns) */
static int max_width = 0; /* maximum width time allowed (ns) */
/* unit's state struct */
retval = 0;
"Entering check_read_params, parms_p = %x.\n", parms_p));
"read_hs %d, read_time %d, read_width %d, timeout %d.\n",
#ifndef lint
/* check for legal range */
goto out;
}
#endif /* lint */
/* check range of setup time and strobe width here */
if ((parms_p->read_setup_time < 0) ||
"Read setup time out of legal range!\n"));
goto out;
}
if ((parms_p->read_strobe_width < 0) ||
"Read strobe width out of legal range!\n"));
goto out;
}
/* check range of read timeout */
if ((parms_p->read_timeout < 0) ||
"Read timeout out of legal range!\n"));
goto out;
}
out:
"Leaving check_read_params, retval = %d.\n", retval));
return (retval);
}
/*ARGSUSED*/
static ushort_t
register enum handshake_t handshake)
{
/* unit's state struct */
"Entering check_read_pins, pins_p = 0x%x \n", pins_p));
pins_p->input_reg_pins));
/* check for bogus bits turned on */
"Check pins : Bogus bit in bpp pins structure!\n"));
goto out;
}
out:
"Leaving check_read_pins, retval = %d.\n", retval));
return (retval);
}
/*ARGSUSED*/
static ushort_t
register enum handshake_t handshake)
{
/* unit's state struct */
"Entering check_write_pins, pins_p = 0x%x, \n", pins_p));
/* check for bogus bits turned on */
"Check pins : Bogus bit in bpp pins structure!\n"));
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)) {
"No versatec handshakes allowed yet!\n"));
/*
* really, need to check for one bit only of remote
* pins set.
*/
goto out;
}
#endif /* lint */
out:
"Leaving check_write_pins, retval = %d.\n", retval));
return (retval);
}
/* ARGSUSED */
static void
{
/* unit's state struct */
"Entering read_outpins, unit = %d, flags = 0x%x, \n",
#if BPP_DEBUG > 0
if (handshake > BPP_BUSY_HS) {
"No versatec handshakes allowed yet!\n"));
}
#endif /* BPP_DEBUG */
}
}
/*
* 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
{
/* unit's state struct */
unit_no));
/* check the 32-bit dma registers */
/* dma csr */
"ck_bpp_registers: peek failed dma csr, address %x\n",
l_reg_addr));
return (1);
}
/* dma addr */
"ck_bpp_registers: peek failed dma addr, address %x\n",
l_reg_addr));
return (1);
}
/* dma bcnt */
"ck_bpp_registers: peek failed dma bcnt, address %x\n",
l_reg_addr));
return (1);
}
/* short hardware registers */
/* hw_config */
"ck_bpp_registers: peek failed hw_config, address %x\n",
s_reg_addr));
return (1);
}
"ck_bpp_registers: poke failed hw_config, address %x\n",
s_reg_addr));
return (1);
}
/* op_config */
"ck_bpp_registers: peek failed op_config, address %x\n",
s_reg_addr));
return (1);
}
/* int_cntl */
"ck_bpp_registers: peek failed int_cntl, address %x\n",
s_reg_addr));
return (1);
}
/* char hardware registers */
/* data */
"ck_bpp_registers: peek failed data, address %x\n",
c_reg_addr));
return (1);
}
/* trans_cntl */
"ck_bpp_registers:peek failed trans_cntl, address %x\n",
c_reg_addr));
return (1);
}
/* out_pins */
"ck_bpp_registers: peek failed out_pins, address %x\n",
c_reg_addr));
return (1);
}
/* in_pins */
"ck_bpp_registers: peek failed in_pins, address %x\n",
c_reg_addr));
return (1);
}
"Leaving check_bpp_registers, unit %d.\n", unit_no));
return (0);
}