/* $Id: adbii.c,v 1.3 2007/12/08 22:44:36 mtrillo Exp $ */ /* $NetBSD: adb_direct.c,v 1.55 2006/11/24 22:04:23 wiz Exp $ */ /* From: adb_direct.c 2.02 4/18/97 jpw */ /* * This is a nadb(4) driver for the Macintosh II ADB controller. * Adapted from the Macintosh II code in adb_direct.c with the * following copyright: * * Copyright (C) 1996, 1997 John P. Wittkoski * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John P. Wittkoski. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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 __KERNEL_RCSID(0, "$Id: adbii.c,v 1.3 2007/12/08 22:44:36 mtrillo Exp $"); #include #include #include #include #include #include #include #include #include #define ADB_MAXMSGLEN 16 struct adbii_softc { struct device sc_dev; struct adb_bus_accessops sc_adbops; void (*sc_adb_handler)(void *, int, uint8_t *); void *sc_adb_cookie; enum {ADB_ACTION_IDLE, ADB_ACTION_OUT, ADB_ACTION_IN} sc_action_state; bool sc_waiting; u_int sc_outq_len; u_char sc_outq[ADB_MAXMSGLEN]; u_int sc_outb_len; u_int sc_outb_sent; u_char *sc_outb; u_char sc_outb_buffer[ADB_MAXMSGLEN]; u_int sc_inb_len; u_char *sc_inb; u_char sc_inb_buffer[ADB_MAXMSGLEN]; u_int sc_devices; u_int sc_last_device; }; #define ACR_SROUT 0x10 /* * Macintosh II ADB controller * * * Transaction states: * To send data: * CMD -> EVEN -> ODD -> EVEN -> ... -> IDLE * To receive data: * EVEN -> ODD -> EVEN -> ... -> IDLE * * Start and end of a packet receive is signaled by the controller * by asserting B_IRQ. */ #define B_CMD 0x00 #define B_EVEN 0x10 #define B_ODD 0x20 #define B_IDLE 0x30 #define B_MASK 0x30 #define B_IRQ 0x08 #define VIA_B_IRQ_ASSERTED(b) (((b) & B_IRQ) == 0) #define VIA_SET_B_STATE(b) (via_reg(VIA1, vBufB) = ((via_reg(VIA1, vBufB) & ~B_MASK) | (b))) #define VIA_INTR_DISABLE() via_reg(VIA1, vIER) = 0x04 #define VIA_INTR_ENABLE() via_reg(VIA1, vIER) = 0x84 #define VIA_INTR_CLEAR() via_reg(VIA1, vIFR) = 0x04 /* Prototypes */ int adbii_set_handler(void *, void (*)(void *, int, uint8_t *), void *); void adbii_init(struct adbii_softc *); int adbii_adb_send(void *, int, int, int, uint8_t *); void adbii_start_sending(struct adbii_softc *); void adbii_adb_poll(void *); void adbii_intr(void *); void adbii_guess_next_device(struct adbii_softc *); void adbii_adb_autopoll(void *, int); void adbii_srq(struct adbii_softc *); int adbii_match(struct device *, struct cfdata *, void *); void adbii_attach(struct device *, struct device *, void *); CFATTACH_DECL(adbii, sizeof(struct adbii_softc), adbii_match, adbii_attach, NULL, NULL); struct adbii_softc *adbii0 = NULL; int adbii_match(self, cf, aux) struct device *self; struct cfdata *cf; void *aux; { if (adbii0 != NULL) return (0); switch (mac68k_machine.machineid) { case MACH_MACC610: /* Centris 610 */ case MACH_MACC650: /* Centris 650 */ case MACH_MACII: /* II */ case MACH_MACIICI: /* IIci */ case MACH_MACIICX: /* IIcx */ case MACH_MACIIX: /* IIx */ case MACH_MACQ610: /* Quadra 610 */ case MACH_MACQ650: /* Quadra 650 */ case MACH_MACQ700: /* Quadra 700 */ case MACH_MACQ800: /* Quadra 800 */ case MACH_MACSE30: /* SE/30 */ return (1); } return (0); } void adbii_attach(parent, self, aux) struct device *parent, *self; void *aux; { struct adbii_softc *sc = device_private(self); adbii0 = sc; printf("\n"); adbii_init(sc); /* * Our internal message buffers don't use * a 'message type' or 'message length' first byte. */ sc->sc_outb = &sc->sc_outb_buffer[1]; sc->sc_inb = &sc->sc_inb_buffer[1]; /* * Attach the ADB bus */ sc->sc_adbops.cookie = sc; sc->sc_adbops.send = adbii_adb_send; sc->sc_adbops.poll = adbii_adb_poll; sc->sc_adbops.autopoll = adbii_adb_autopoll; sc->sc_adbops.set_handler = adbii_set_handler; config_found_ia(self, "adb_bus", &sc->sc_adbops, nadb_print); } int adbii_set_handler(cookie, hdl_f, hdl_arg) void *cookie, *hdl_arg; void (*hdl_f)(void *, int, uint8_t *); { struct adbii_softc *sc = cookie; sc->sc_adb_handler = hdl_f; sc->sc_adb_cookie = hdl_arg; return (0); } /* * Initialize the Macintosh II ADB controller */ void adbii_init(sc) struct adbii_softc *sc; { u_int x; /* * Set the interrupt handler */ via1_register_irq(2, adbii_intr, sc); /* * Set direction for EVEN and ODD to output * and for IRQ to input */ via_reg(VIA1, vDirB) |= B_EVEN | B_ODD; via_reg(VIA1, vDirB) &= ~B_IRQ; /* * Set state to IDLE */ VIA_SET_B_STATE(B_IDLE); sc->sc_action_state = ADB_ACTION_IDLE; via_reg(VIA1, vACR) &= ~ACR_SROUT; x = via_reg(VIA1, vSR); // pending data and intr? /* * Make sure VIA interrupts are on */ VIA_INTR_ENABLE(); VIA_INTR_CLEAR(); } /* * Start sending a queued message */ void adbii_start_sending(sc) struct adbii_softc *sc; { /* start command now */ memcpy(sc->sc_outb, sc->sc_outq, sc->sc_outq_len); sc->sc_outb_len = sc->sc_outq_len; sc->sc_outb_sent = 0; /* nothing sent yet */ sc->sc_action_state = ADB_ACTION_OUT; /* set next state */ via_reg(VIA1, vACR) |= ACR_SROUT; // set shift register for output via_reg(VIA1, vSR) = sc->sc_outb[0]; /* load byte for output */ VIA_SET_B_STATE(B_CMD); sc->sc_outq_len = 0; // nothing in queue now... } /* * Send an ADB message */ int adbii_adb_send(cookie, poll, command, len, data) void *cookie; int poll, command, len; uint8_t *data; { struct adbii_softc *sc = cookie; int s; s = splhigh(); if (sc->sc_outq_len > 0) { splx(s); /* sorry, try again later */ return (1); } /* * Don't need to use adb_cmd_extra here because this section * will be called ONLY when it is an ADB command (no RTC or * PRAM), especially on II series! */ if ((command & 0x0c) != 0x08) len = 0; // only copy additional data on LISTEN commands... sc->sc_outq_len = 1 + len; sc->sc_outq[0] = (u_char) command; /* load command */ /* copy additional output data, if any */ if (len > 0) memcpy(&sc->sc_outq[1], data, len); if (sc->sc_action_state == ADB_ACTION_IDLE && !VIA_B_IRQ_ASSERTED(via_reg(VIA1, vBufB))) { /* * If ADB is available and no incoming * interrupts, then we can start sending * right now. */ adbii_start_sending(sc); } splx(s); if (poll || 0x0100 <= (s & 0x0700)) // were VIA1 interrupts blocked? /* poll until message done */ while (sc->sc_action_state != ADB_ACTION_IDLE || !VIA_B_IRQ_ASSERTED(via_reg(VIA1, vBufB)) || sc->sc_waiting) if (via_reg(VIA1, vIFR) & 0x04) { // wait for interrupt adbii_intr(sc); // go process it } return (0); } void adbii_adb_poll(cookie) void *cookie; { if (via_reg(VIA1, vIFR) & 0x04) adbii_intr(cookie); } void adbii_srq(sc) struct adbii_softc *sc; { adbii_guess_next_device(sc); sc->sc_outb_len = 1; sc->sc_outb[0] = ADBTALK(sc->sc_last_device, 0); sc->sc_outb_sent = 0; /* nothing sent yet */ sc->sc_action_state = ADB_ACTION_IDLE; /* set next state */ via_reg(VIA1, vACR) |= ACR_SROUT; via_reg(VIA1, vSR) = sc->sc_outb[0]; VIA_SET_B_STATE(B_CMD); } /* * Interrupt handler. * * Start and end of a packet receive is signaled by the controller * by asserting the B_IRQ bit. */ void adbii_intr(cookie) void *cookie; { struct adbii_softc *sc = cookie; bool intr_on; int i, s; s = splhigh(); VIA_INTR_CLEAR(); /* clear interrupt */ VIA_INTR_DISABLE(); /* disable ADB interrupt on IIs. */ delay(150); /* yuck (don't remove) */ intr_dispatch(0x70); /* grab any serial interrupts */ intr_on = VIA_B_IRQ_ASSERTED(via_reg(VIA1, vBufB)); // save for later switch (sc->sc_action_state) { case ADB_ACTION_IDLE: if (!intr_on) { i = via_reg(VIA1, vSR); sc->sc_action_state = ADB_ACTION_IDLE; VIA_SET_B_STATE(B_IDLE); } else { sc->sc_inb_len = 1; sc->sc_inb[0] = via_reg(VIA1, vSR); // get first byte via_reg(VIA1, vACR) &= ~ACR_SROUT; // set SR to input sc->sc_action_state = ADB_ACTION_IN; VIA_SET_B_STATE(B_EVEN); } break; case ADB_ACTION_IN: sc->sc_inb[sc->sc_inb_len++] = via_reg(VIA1, vSR); via_reg(VIA1, vACR) &= ~ACR_SROUT; // just in case if (intr_on) { /* * End of packet. */ // XXX if (sc->sc_inb_len != 3) sc->sc_inb_len--; /* minus one */ sc->sc_last_device = ADB_CMDADDR(sc->sc_inb[0]); if (sc->sc_inb_len == 1 && !(sc->sc_waiting)) { // SRQ!! adbii_srq(sc); } else { if (sc->sc_waiting || sc->sc_inb_len > 0) { if (sc->sc_adb_handler) sc->sc_adb_handler( sc->sc_adb_cookie, sc->sc_inb_len + 1, sc->sc_inb_buffer ); sc->sc_waiting = false; } sc->sc_inb_len = 0; /* * Since we are done, check whether there is * any data waiting to do out. * If so, start the sending the data. */ if (sc->sc_outq_len > 0) { adbii_start_sending(sc); } else { adbii_srq(sc); } } } else { /* * The message hasn't ended yet. */ via_reg(VIA1, vBufB) ^= B_EVEN | B_ODD; } break; case ADB_ACTION_OUT: i = via_reg(VIA1, vSR); /* clear interrupt */ sc->sc_outb_sent++; /* * If the outgoing data was a TALK, we must * switch to input mode to get the result. */ if ((sc->sc_outb[0] & 0x0c) == 0x0c) { sc->sc_inb_len = 1; sc->sc_inb[0] = i; sc->sc_action_state = ADB_ACTION_IN; via_reg(VIA1, vACR) &= ~ACR_SROUT; VIA_SET_B_STATE(B_EVEN); /* we want something back */ sc->sc_waiting = true; } else { /* * If it's not a TALK, check whether all data has been * sent. If so, call the completion routine * and clean up. If not, advance to the next state. */ if (sc->sc_outb_len == sc->sc_outb_sent) { // done ? if (sc->sc_adb_handler) sc->sc_adb_handler(sc->sc_adb_cookie, sc->sc_outb_len + 1, sc->sc_outb_buffer); if (sc->sc_outq_len > 0) { adbii_start_sending(sc); } else { adbii_srq(sc); // XXX ? } } else { via_reg(VIA1, vSR) = sc->sc_outb[sc->sc_outb_sent]; if (B_CMD == (via_reg(VIA1, vBufB) & B_MASK)) VIA_SET_B_STATE(B_EVEN); else via_reg(VIA1, vBufB) ^= B_EVEN | B_ODD; } } break; default: panic("adbii_intr"); } VIA_INTR_ENABLE(); /* enable ADB interrupt on IIs */ splx(s); } void adbii_guess_next_device(sc) struct adbii_softc *sc; { if (sc->sc_devices == 0) sc->sc_last_device = (sc->sc_last_device + 1) & 0xF; else { u_int i = sc->sc_last_device; do i = (i + 1) & 0xF; while ((sc->sc_devices & (1 << i)) == 0); sc->sc_last_device = i; } } void adbii_adb_autopoll(cookie, devs) void *cookie; int devs; { struct adbii_softc *sc = cookie; sc->sc_devices = devs & 0xffff; } long mrg_adbintr(void); long mrg_pmintr(void); long mrg_adbintr(void) { if (adbii0) adbii_intr(adbii0); return (1); } long mrg_pmintr(void) { return (1); }