/*-
* 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.
*/
/*
* The CIO sits eight bit wide on the top byte lane of
* 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);
}
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); /* Give the CIO time to set itself up */
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 information strings from the card ROM */
aprint_normal(": CSI Hurdler II Centronics\n");
/* Attach CIO timers 1+2 as timecounter */
if (sc->sc_options & CPI_CTC12_IS_TIMECOUNTER) {
cpi_tc_initclock(sc);
}
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("Not in initial state (%x).\n",
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 = kmem_alloc(CPI_BUFSIZE, KM_SLEEP);
sc->sc_bufbytes = 0;
sc->sc_lpstate = LP_OPEN;
/* Send data to printer, a line buffer full at a time */
while (uio->uio_resid > 0) {
numbytes = uimin(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);
/*
* 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;
/*
* 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);
/* 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);
}
bst = ((struct cpi_softc *)tc->tc_priv)->sc_bst;
bsh = ((struct cpi_softc *)tc->tc_priv)->sc_bsh;
/*
* We run CIO counters 1 and 2 in an internally coupled mode,
* where the output of counter 1 (LSW) clocks counter 2 (MSW).
* The counters are buffered, and the buffers have to be
* locked before we can read out a consistent counter
* value. Reading the LSB releases the buffer lock.
*
* Unfortunately, there is no such mechanism between MSW and
* LSW of the coupled counter. To ensure a consistent
* read-out, we read the MSW, then the LSW, then re-read the
* MSW and compare with the old value. If we find that the MSW
* has just been incremented, we re-read the LSW. This avoids
* a race that could leave us with a new (just wrapped) LSW
* and an old MSW value.
*
* For simplicity, we roll the procedure into a loop - the
* rollover case is rare.
*/
do {