761 lines
19 KiB
C
761 lines
19 KiB
C
/* $NetBSD: cpi_nubus.c,v 1.5 2011/05/13 22:35:50 rmind Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2008 Hauke Fath
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: cpi_nubus.c,v 1.5 2011/05/13 22:35:50 rmind Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/device.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/event.h>
|
|
#include <sys/callout.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/file.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/tty.h>
|
|
#include <sys/time.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/errno.h>
|
|
|
|
#include <machine/intr.h>
|
|
#include <machine/bus.h>
|
|
#include <machine/viareg.h>
|
|
|
|
#include <dev/ic/z8536reg.h>
|
|
#include <mac68k/nubus/nubus.h>
|
|
#include <mac68k/nubus/cpi_nubusvar.h>
|
|
|
|
#include "ioconf.h"
|
|
|
|
#ifdef DEBUG
|
|
#define CPI_DEBUG
|
|
#endif
|
|
|
|
/*
|
|
* Stuff taken from Egan/Teixeira ch 8: 'if(TRACE_FOO)' debug output
|
|
* statements don't break indentation, and when DEBUG is not defined,
|
|
* the compiler code optimizer drops them as dead code.
|
|
*/
|
|
#ifdef CPI_DEBUG
|
|
#define M_TRACE_CONFIG 0x0001
|
|
#define M_TRACE_OPEN 0x0002
|
|
#define M_TRACE_CLOSE 0x0004
|
|
#define M_TRACE_READ 0x0008
|
|
#define M_TRACE_WRITE 0x0010
|
|
#define M_TRACE_IOCTL 0x0020
|
|
#define M_TRACE_STATUS 0x0040
|
|
#define M_TRACE_ALL 0xFFFF
|
|
#define M_TRACE_NONE 0x0000
|
|
|
|
#define TRACE_CONFIG (cpi_debug_mask & M_TRACE_CONFIG)
|
|
#define TRACE_OPEN (cpi_debug_mask & M_TRACE_OPEN)
|
|
#define TRACE_CLOSE (cpi_debug_mask & M_TRACE_CLOSE)
|
|
#define TRACE_READ (cpi_debug_mask & M_TRACE_READ)
|
|
#define TRACE_WRITE (cpi_debug_mask & M_TRACE_WRITE)
|
|
#define TRACE_IOCTL (cpi_debug_mask & M_TRACE_IOCTL)
|
|
#define TRACE_STATUS (cpi_debug_mask & M_TRACE_STATUS)
|
|
#define TRACE_ALL (cpi_debug_mask & M_TRACE_ALL)
|
|
#define TRACE_NONE (cpi_debug_mask & M_TRACE_NONE)
|
|
|
|
uint32_t cpi_debug_mask = M_TRACE_NONE /* | M_TRACE_WRITE */ ;
|
|
|
|
#else
|
|
#define TRACE_CONFIG 0
|
|
#define TRACE_OPEN 0
|
|
#define TRACE_CLOSE 0
|
|
#define TRACE_READ 0
|
|
#define TRACE_WRITE 0
|
|
#define TRACE_IOCTL 0
|
|
#define TRACE_STATUS 0
|
|
#define TRACE_ALL 0
|
|
#define TRACE_NONE 0
|
|
#endif
|
|
|
|
#undef USE_CIO_TIMERS /* TBD */
|
|
|
|
/* autoconf interface */
|
|
int cpi_nubus_match(device_t, cfdata_t, void *);
|
|
void cpi_nubus_attach(device_t, device_t, void *);
|
|
void cpi_nubus_intr(void *);
|
|
|
|
CFATTACH_DECL(cpi, sizeof(struct cpi_softc),
|
|
cpi_nubus_match, cpi_nubus_attach, NULL, NULL);
|
|
|
|
dev_type_open(cpi_open);
|
|
dev_type_close(cpi_close);
|
|
dev_type_read(cpi_read);
|
|
dev_type_write(cpi_write);
|
|
dev_type_ioctl(cpi_ioctl);
|
|
|
|
const struct cdevsw cpi_cdevsw = {
|
|
cpi_open, cpi_close, noread, cpi_write, cpi_ioctl,
|
|
nostop, notty, nopoll, nommap, nokqfilter, D_OTHER
|
|
};
|
|
|
|
/* prototypes */
|
|
static void cpi_lpreset(struct cpi_softc *);
|
|
static int cpi_notready(struct cpi_softc *);
|
|
static void cpi_wakeup(void *);
|
|
static int cpi_flush(struct cpi_softc *);
|
|
static void cpi_intr(void *);
|
|
|
|
#ifdef USE_CIO_TIMERS
|
|
static void cpi_initclock(struct cpi_softc *);
|
|
static u_int cpi_get_timecount(struct timecounter *);
|
|
#endif
|
|
|
|
static inline void z8536_reg_set(bus_space_tag_t, bus_space_handle_t,
|
|
uint8_t, uint8_t);
|
|
static inline uint8_t z8536_reg_get(bus_space_tag_t, bus_space_handle_t,
|
|
uint8_t);
|
|
|
|
|
|
const uint8_t cio_reset[] = {
|
|
/* register value */
|
|
Z8536_MICR, 0x00,
|
|
Z8536_MICR, MICR_RESET,
|
|
Z8536_MICR, 0x00
|
|
};
|
|
|
|
const uint8_t cio_init[] = {
|
|
/* register value */
|
|
|
|
/* Interrupt vectors - clear all */
|
|
Z8536_IVRA, 0x00,
|
|
Z8536_IVRB, 0x00,
|
|
Z8536_IVRCT, 0x20 /* ??? Do we use this? */,
|
|
|
|
/*
|
|
* Port A specification - bit port, single buffered,
|
|
* latched output, pulsed handshake, all bits non-inverting
|
|
* non-special I/O
|
|
*/
|
|
Z8536_PMSRA, PMSR_PTS_OUT | PMSR_LPM,
|
|
Z8536_PHSRA, PHSR_HTS_PUL,
|
|
Z8536_DPPRA, 0x00,
|
|
Z8536_DDRA, 0x00,
|
|
Z8536_SIOCRA, 0x00,
|
|
|
|
/*
|
|
* Port B specification - bit port, transparent output,
|
|
* pulsed handshake, all bits non-inverting
|
|
* bits 0, 4 output; bits 1-3, 5-8 input,
|
|
* non-special I/O
|
|
* Pattern matching: Bit 6 (BUSY) matching "1"
|
|
* Alternatively: Bit 3 (/ACK) matching "0"
|
|
*/
|
|
Z8536_PMSRB, PMSR_PMS_OR_PEV,
|
|
Z8536_PHSRB, 0x00,
|
|
Z8536_DPPRB, 0x00,
|
|
Z8536_DDRB, 0xee /*11101110b*/,
|
|
Z8536_SIOCRB, 0x00,
|
|
Z8536_PPRB, 0x00,
|
|
Z8536_PTRB, 0x00,
|
|
Z8536_PMRB, 0x40 /*01000000b = PB6 */,
|
|
|
|
Z8536_PDRB, 0xFE, /* Assign printer -RESET */
|
|
Z8536_PCSRA, 0x00, /* Clear port A interrupt bits */
|
|
|
|
/*
|
|
* Port C specification - bit 3 out, bits 0-2 in,
|
|
* all 4 non-inverting, non-special I/O
|
|
*/
|
|
Z8536_DDRC, 0x07 /*00000111b*/,
|
|
Z8536_DPPRC, 0x00,
|
|
Z8536_SIOCRC, 0x00,
|
|
|
|
#ifdef USE_CIO_TIMERS
|
|
/*
|
|
* Counter/Timers 1+2 are joined to form a free-running
|
|
* 32 bit timecounter
|
|
*/
|
|
Z8536_CTMSR1, CTMS_CSC,
|
|
Z8536_CTTCR1_MSB, 0x00,
|
|
Z8536_CTTCR1_LSB, 0x00,
|
|
Z8536_CTMSR2, CTMS_CSC,
|
|
Z8536_CTTCR2_MSB, 0x00,
|
|
Z8536_CTTCR2_LSB, 0x00,
|
|
#endif /* USE_CIO_TIMERS */
|
|
|
|
/*
|
|
* We need Timer 3 for running port A in strobed mode.
|
|
*
|
|
* Counter/Timer 3 specification -- clear IP & IUS, trigger +
|
|
* gate command bit, one-shot operation
|
|
*/
|
|
Z8536_CTCSR3, CTCS_CLR_IP_IUS | CTCS_GCB | CTCS_TCB,
|
|
Z8536_CTMSR3, CTMS_DCS_ONESHOT,
|
|
Z8536_CTTCR3_MSB, 0x00,
|
|
Z8536_CTTCR3_LSB, 0x03,
|
|
|
|
/*
|
|
* Enable ports A+B+C+CT3
|
|
* Set timer 1 to clock timer 2, but not yet enabled.
|
|
*/
|
|
Z8536_MCCR, MCCR_PAE | MCCR_PBE | MCCR_CT1CT2 | MCCR_PC_CT3E,
|
|
/* Master Interrupt Enable, Disable Lower Chain,
|
|
* No Vector, port A+B+CT vectors include status */
|
|
Z8536_MICR, MICR_MIE | MICR_DLC | MICR_NV | MICR_PAVIS |
|
|
MICR_PBVIS | MICR_CTVIS,
|
|
Z8536_PDRB, 0xFE, /* Clear printer -RESET */
|
|
};
|
|
|
|
|
|
/*
|
|
* Look for Creative Systems Inc. "Hurdler Centronics Parallel Interface"
|
|
*/
|
|
int
|
|
cpi_nubus_match(device_t parent, cfdata_t cf, void *aux)
|
|
{
|
|
struct nubus_attach_args *na;
|
|
|
|
na = aux;
|
|
if ((na->category == NUBUS_CATEGORY_COMMUNICATIONS) &&
|
|
(na->type == NUBUS_TYPE_CENTRONICS) &&
|
|
(na->drsw == NUBUS_DRSW_CPI) &&
|
|
(na->drhw == NUBUS_DRHW_CPI))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
cpi_nubus_attach(device_t parent, device_t self, void *aux)
|
|
{
|
|
struct cpi_softc *sc;
|
|
struct nubus_attach_args *na;
|
|
int err, ii;
|
|
|
|
sc = device_private(self);
|
|
na = aux;
|
|
sc->sc_bst = na->na_tag;
|
|
memcpy(&sc->sc_slot, na->fmt, sizeof(nubus_slot));
|
|
sc->sc_basepa = (bus_addr_t)NUBUS_SLOT2PA(na->slot);
|
|
|
|
/*
|
|
* The CIO sits on the MSB (top byte lane) of the 32 bit
|
|
* Nubus, so map 16 byte.
|
|
*/
|
|
if (TRACE_CONFIG) {
|
|
printf("\n");
|
|
printf("\tcpi_nubus_attach() mapping 8536 CIO at 0x%lx.\n",
|
|
sc->sc_basepa + CIO_BASE_OFFSET);
|
|
}
|
|
|
|
err = bus_space_map(sc->sc_bst, sc->sc_basepa + CIO_BASE_OFFSET,
|
|
(Z8536_IOSIZE << 4), 0, &sc->sc_bsh);
|
|
if (err) {
|
|
aprint_normal(": failed to map memory space.\n");
|
|
return;
|
|
}
|
|
|
|
sc->sc_lpstate = LP_INITIAL;
|
|
sc->sc_intcount = 0;
|
|
sc->sc_bytestoport = 0;
|
|
|
|
if (TRACE_CONFIG)
|
|
printf("\tcpi_nubus_attach() about to set up 8536 CIO.\n");
|
|
|
|
for (ii = 0; ii < sizeof(cio_reset); ii += 2)
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, cio_reset[ii],
|
|
cio_reset[ii + 1]);
|
|
|
|
delay(1000); /* Just in case */
|
|
for (ii = 0; ii < sizeof(cio_init); ii += 2) {
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, cio_init[ii],
|
|
cio_init[ii + 1]);
|
|
}
|
|
|
|
if (TRACE_CONFIG)
|
|
printf("\tcpi_nubus_attach() done with 8536 CIO setup.\n");
|
|
|
|
/* XXX Get the information strings from the card's ROM */
|
|
aprint_normal(": CSI Hurdler II Centronics\n");
|
|
|
|
#ifdef USE_CIO_TIMERS
|
|
/* Attach CIO timers as timecounters */
|
|
if (TRACE_CONFIG)
|
|
printf("\tcpi_nubus_attach() about to attach timers\n");
|
|
|
|
cpi_initclock(sc);
|
|
#endif /* USE_CIO_TIMERS */
|
|
|
|
callout_init(&sc->sc_wakeupchan, 0); /* XXX */
|
|
|
|
/* make sure interrupts are vectored to us */
|
|
add_nubus_intr(na->slot, cpi_nubus_intr, sc);
|
|
}
|
|
|
|
void
|
|
cpi_nubus_intr(void *arg)
|
|
{
|
|
struct cpi_softc *sc;
|
|
int s;
|
|
|
|
sc = (struct cpi_softc *)arg;
|
|
|
|
s = spltty();
|
|
|
|
sc->sc_intcount++;
|
|
|
|
/* Check for interrupt source, and clear interrupt */
|
|
|
|
/*
|
|
* Clear port A interrupt
|
|
* Interrupt from register A, clear "pending"
|
|
* and set "under service"
|
|
*/
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_CLR_IE);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_CLR_IP);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_SET_IUS);
|
|
|
|
cpi_intr(sc);
|
|
|
|
/* Interrupt from register A, mark serviced */
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_CLR_IUS);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_SET_IE);
|
|
|
|
splx(s);
|
|
}
|
|
|
|
|
|
/* cpi nuts and bolts */
|
|
|
|
int
|
|
cpi_open(dev_t device, int flag, int mode, struct lwp *l)
|
|
{
|
|
int err, ii, s;
|
|
struct cpi_softc *sc;
|
|
|
|
if (TRACE_OPEN)
|
|
printf("\tcpi_open() called...\n");
|
|
|
|
/* Consistency checks: Valid unit number, softc, device state */
|
|
sc = device_lookup_private(&cpi_cd, CPI_UNIT(device));
|
|
if (NULL == sc) {
|
|
if (TRACE_OPEN)
|
|
printf("Tried to cpi_open() with NULL softc\n");
|
|
return ENXIO;
|
|
}
|
|
if (sc->sc_lpstate != LP_INITIAL) {
|
|
if (TRACE_OPEN)
|
|
printf("%s not in initial state (%x).\n",
|
|
sc->sc_dev.dv_xname, sc->sc_lpstate);
|
|
return EBUSY;
|
|
}
|
|
sc->sc_lpstate = LP_OPENING;
|
|
|
|
if (TRACE_OPEN)
|
|
printf("\tcpi_open() resetting the printer...\n");
|
|
cpi_lpreset(sc);
|
|
|
|
if (TRACE_OPEN)
|
|
printf("\tcpi_open() waiting for printer ready...\n");
|
|
|
|
/* Wait max 15 sec for printer to get ready */
|
|
for (ii = 15; cpi_notready(sc); ii--) {
|
|
if (0 == ii) {
|
|
sc->sc_lpstate = LP_INITIAL;
|
|
return EBUSY;
|
|
}
|
|
/* sleep for a second, unless we get a signal */
|
|
err = tsleep(sc, PZERO | PCATCH, "cpi_open", hz);
|
|
if (err != EWOULDBLOCK) {
|
|
sc->sc_lpstate = LP_INITIAL;
|
|
return err;
|
|
}
|
|
}
|
|
if (TRACE_OPEN)
|
|
printf("\tcpi_open() allocating printer buffer...\n");
|
|
|
|
/* Allocate the driver's line buffer */
|
|
sc->sc_printbuf = malloc(CPI_BUFSIZE, M_DEVBUF, M_WAITOK);
|
|
sc->sc_bufbytes = 0;
|
|
sc->sc_lpstate = LP_OPEN;
|
|
|
|
/* Statistics */
|
|
sc->sc_intcount = 0;
|
|
sc->sc_bytestoport = 0;
|
|
|
|
/* Kick off transfer */
|
|
cpi_wakeup(sc);
|
|
|
|
/*
|
|
* Reset "interrupt {pending, under service}" bits, then
|
|
* enable Port A interrupts
|
|
*/
|
|
s = spltty();
|
|
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_CLR_IP_IUS);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_SET_IE);
|
|
splx(s);
|
|
|
|
if (TRACE_OPEN)
|
|
printf("\tcpi_open() done...\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cpi_close(dev_t device, int flag, int mode, struct lwp *l)
|
|
{
|
|
struct cpi_softc *sc;
|
|
|
|
sc = device_lookup_private(&cpi_cd, CPI_UNIT(device));
|
|
|
|
if (TRACE_CLOSE)
|
|
printf("\tcpi_close() called (%lu hard, %lu bytes to port)\n",
|
|
sc->sc_intcount, sc->sc_bytestoport);
|
|
|
|
/* Flush the remaining buffer content, ignoring any errors */
|
|
if (0 < sc->sc_bufbytes)
|
|
(void)cpi_flush(sc);
|
|
|
|
callout_stop(&sc->sc_wakeupchan);
|
|
|
|
/* Disable Port A interrupts */
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_CLR_IE);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PCSRA, PCSR_CLR_IP_IUS);
|
|
|
|
sc->sc_lpstate = LP_INITIAL;
|
|
free(sc->sc_printbuf, M_DEVBUF);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cpi_write(dev_t device, struct uio *uio, int flags)
|
|
{
|
|
int err;
|
|
size_t numbytes;
|
|
struct cpi_softc *sc;
|
|
|
|
err = 0;
|
|
|
|
if (TRACE_WRITE)
|
|
printf("\tcpi_write() called for %u bytes\n", uio->uio_resid);
|
|
|
|
sc = device_lookup_private(&cpi_cd, CPI_UNIT(device));
|
|
|
|
/* Send data to printer, a line buffer full at a time */
|
|
while (uio->uio_resid > 0) {
|
|
numbytes = min(CPI_BUFSIZE, uio->uio_resid);
|
|
sc->sc_cp = sc->sc_printbuf;
|
|
uiomove(sc->sc_cp, numbytes, uio);
|
|
sc->sc_bufbytes = numbytes;
|
|
|
|
if (TRACE_WRITE)
|
|
printf("\tQueuing %u bytes\n", numbytes);
|
|
err = cpi_flush(sc);
|
|
if (err) {
|
|
/* Failure; adjust residual counter */
|
|
if (TRACE_WRITE)
|
|
printf("\tQueuing failed with %d\n", err);
|
|
uio->uio_resid += sc->sc_bufbytes;
|
|
sc->sc_bufbytes = 0;
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int
|
|
cpi_ioctl(dev_t device, unsigned long cmd, void *data,
|
|
int flag, struct lwp *l)
|
|
{
|
|
int err;
|
|
|
|
err = 0;
|
|
|
|
if (TRACE_IOCTL)
|
|
printf("\tcpi_ioctl() called with %ld...\n", cmd);
|
|
|
|
switch (cmd) {
|
|
default:
|
|
if (TRACE_IOCTL)
|
|
printf("\tcpi_ioctl() unknown ioctl %ld\n", cmd);
|
|
err = ENODEV;
|
|
break;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Flush the print buffer that our top half uses to provide data to
|
|
* our bottom, interrupt-driven half.
|
|
*/
|
|
static int
|
|
cpi_flush(struct cpi_softc *sc)
|
|
{
|
|
int err, s;
|
|
|
|
err = 0;
|
|
while (0 < sc->sc_bufbytes) {
|
|
/* Feed the printer a char, if it's ready */
|
|
if ( !cpi_notready(sc)) {
|
|
if (TRACE_WRITE)
|
|
printf("\tcpi_flush() writes %u bytes "
|
|
"(%lu hard, %lu bytes to port)\n",
|
|
sc->sc_bufbytes, sc->sc_intcount,
|
|
sc->sc_bytestoport);
|
|
s = spltty();
|
|
cpi_intr(sc);
|
|
splx(s);
|
|
}
|
|
/* XXX Sure we want to wait forever for the printer? */
|
|
err = tsleep((void *)sc, PZERO | PCATCH,
|
|
"cpi_flush", (60 * hz));
|
|
}
|
|
return err;
|
|
}
|
|
|
|
|
|
static void
|
|
cpi_wakeup(void *param)
|
|
{
|
|
struct cpi_softc *sc;
|
|
int s;
|
|
|
|
sc = param;
|
|
|
|
s = spltty();
|
|
cpi_intr(sc);
|
|
splx(s);
|
|
|
|
callout_reset(&sc->sc_wakeupchan, hz, cpi_wakeup, sc);
|
|
}
|
|
|
|
|
|
static void
|
|
cpi_lpreset(struct cpi_softc *sc)
|
|
{
|
|
uint8_t portb; /* Centronics -RESET is on port B, bit 0 */
|
|
#ifdef DIRECT_PORT_ACCESS
|
|
int s;
|
|
|
|
s = spltty();
|
|
|
|
portb = bus_space_read_1(sc->sc_bst, sc->sc_bsh, CIO_PORTB);
|
|
bus_space_write_1(sc->sc_bst, sc->sc_bsh,
|
|
CIO_PORTB, portb & ~CPI_RESET);
|
|
delay(100);
|
|
portb = bus_space_read_1(sc->sc_bst, sc->sc_bsh, CIO_PORTB);
|
|
bus_space_write_1(sc->sc_bst, sc->sc_bsh,
|
|
CIO_PORTB, portb | CPI_RESET);
|
|
|
|
splx(s);
|
|
#else
|
|
portb = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_PDRB);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PDRB, portb & ~CPI_RESET);
|
|
delay(100);
|
|
portb = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_PDRB);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_PDRB, portb | CPI_RESET);
|
|
#endif /* DIRECT_PORT_ACCESS */
|
|
}
|
|
|
|
|
|
/*
|
|
* Centronics BUSY is on port B, bit 6
|
|
* SELECT is on Port B, bit 5
|
|
* /FAULT is on Port B, bit 1
|
|
* PAPER EMPTY is on Port C, bit 1
|
|
*/
|
|
static int
|
|
cpi_notready(struct cpi_softc *sc)
|
|
{
|
|
uint8_t portb, portc;
|
|
int is_busy, is_select, is_fault, is_paper_empty;
|
|
|
|
if (TRACE_STATUS)
|
|
printf("\tcpi_notready() checking printer status...\n");
|
|
|
|
portb = bus_space_read_1(sc->sc_bst, sc->sc_bsh, CIO_PORTB);
|
|
if (TRACE_STATUS)
|
|
printf("\tPort B has 0x0%X\n", portb);
|
|
|
|
is_busy = CPI_BUSY & portb;
|
|
if (TRACE_STATUS)
|
|
printf("\t\tBUSY = %d\n", is_busy);
|
|
|
|
is_select = CPI_SELECT & portb;
|
|
if (TRACE_STATUS)
|
|
printf("\t\tSELECT = %d\n", is_select);
|
|
|
|
is_fault = CPI_FAULT & portb;
|
|
if (TRACE_STATUS)
|
|
printf("\t\t/FAULT = %d\n", is_fault);
|
|
|
|
portc = bus_space_read_1(sc->sc_bst, sc->sc_bsh, CIO_PORTC);
|
|
if (TRACE_STATUS)
|
|
printf("\tPort C has 0x0%X\n", portc);
|
|
|
|
is_paper_empty = CPI_PAPER_EMPTY & portc;
|
|
if (TRACE_STATUS)
|
|
printf("\t\tPAPER EMPTY = %d\n", is_paper_empty);
|
|
|
|
return (is_busy || !is_select || !is_fault || is_paper_empty);
|
|
}
|
|
|
|
static void
|
|
cpi_intr(void *arg)
|
|
{
|
|
struct cpi_softc *sc;
|
|
|
|
sc = arg;
|
|
|
|
/* Printer ready for output? */
|
|
if (cpi_notready(sc))
|
|
return;
|
|
|
|
if (0 && TRACE_WRITE)
|
|
printf("\tcpi_soft_intr() has %u bytes.\n", sc->sc_bufbytes);
|
|
|
|
/* Anything to print? */
|
|
if (sc->sc_bufbytes) {
|
|
/* Data byte */
|
|
bus_space_write_1(sc->sc_bst, sc->sc_bsh,
|
|
CIO_PORTA, *sc->sc_cp++);
|
|
sc->sc_bufbytes--;
|
|
sc->sc_bytestoport++;
|
|
}
|
|
if (0 == sc->sc_bufbytes)
|
|
/* line buffer empty, wake up our top half */
|
|
wakeup((void *)sc);
|
|
}
|
|
|
|
#ifdef USE_CIO_TIMERS
|
|
/*
|
|
* Z8536 CIO timers 1 + 2 used for timecounter(9) support
|
|
*/
|
|
static void
|
|
cpi_initclock(struct cpi_softc *sc)
|
|
{
|
|
static struct timecounter cpi_timecounter = {
|
|
.tc_get_timecount = cpi_get_timecount,
|
|
.tc_poll_pps = 0,
|
|
.tc_counter_mask = 0x0ffffu,
|
|
.tc_frequency = CLK_FREQ,
|
|
.tc_name = "CPI Z8536 CIO",
|
|
.tc_quality = 50,
|
|
.tc_priv = NULL,
|
|
.tc_next = NULL
|
|
};
|
|
|
|
/*
|
|
* Set up timers A and B as a single, free-running 32 bit counter
|
|
*/
|
|
|
|
/* Disable counters A and B */
|
|
reg = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_MCCR);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_MCCR,
|
|
reg & ~(MCCR_CT1E | MCCR_CT2E));
|
|
|
|
/* Make sure interrupt enable bits are cleared */
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTCSR1,
|
|
CTCS_CLR_IE);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTCSR2,
|
|
CTCS_CLR_IE);
|
|
|
|
/* Initialise counter start values, and set to continuous cycle */
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTMSR1, CTMS_CSC);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTTCR1_MSB, 0x00);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTTCR1_LSB, 0x00);
|
|
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTMSR2, CTMS_CSC);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTTCR2_MSB, 0x00);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTTCR2_LSB, 0x00);
|
|
|
|
/* Re-enable counters A and B */
|
|
reg = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_MCCR);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_MCCR,
|
|
reg | MCCR_CT1E | MCCR_CT2E | MCCR_CT1CT2);
|
|
|
|
/* Start counters A and B */
|
|
reg = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_CTCSR1);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTCSR1,
|
|
reg | CTCS_TCB);
|
|
reg = z8536_reg_get(sc->sc_bst, sc->sc_bsh, Z8536_CTCSR2);
|
|
z8536_reg_set(sc->sc_bst, sc->sc_bsh, Z8536_CTCSR2,
|
|
reg | CTCS_TCB);
|
|
|
|
tc_init(&cpi_timecounter);
|
|
}
|
|
|
|
static u_int
|
|
cpi_get_timecount(struct timecounter *tc)
|
|
{
|
|
uint8_t high, high2, low;
|
|
int s;
|
|
|
|
/*
|
|
* Make the timer access atomic
|
|
*
|
|
* XXX How expensive is this? And is it really necessary?
|
|
*/
|
|
s = splhigh();
|
|
|
|
/* TBD */
|
|
|
|
}
|
|
#endif /* USE_CIO_TIMERS */
|
|
|
|
|
|
/*
|
|
* Z8536 CIO nuts and bolts
|
|
*/
|
|
|
|
static inline void
|
|
z8536_reg_set(bus_space_tag_t bspace, bus_space_handle_t bhandle,
|
|
uint8_t reg, uint8_t val)
|
|
{
|
|
int s;
|
|
|
|
s = splhigh();
|
|
bus_space_write_1(bspace, bhandle, CIO_CTRL, reg);
|
|
delay(1);
|
|
bus_space_write_1(bspace, bhandle, CIO_CTRL, val);
|
|
splx(s);
|
|
}
|
|
|
|
static inline uint8_t
|
|
z8536_reg_get(bus_space_tag_t bspace, bus_space_handle_t bhandle, uint8_t reg)
|
|
{
|
|
int s;
|
|
uint8_t val;
|
|
|
|
s = splhigh();
|
|
bus_space_write_1(bspace, bhandle, CIO_CTRL, reg);
|
|
delay(1);
|
|
val = bus_space_read_1(bspace, bhandle, CIO_CTRL);
|
|
splx(s);
|
|
|
|
return val;
|
|
}
|