diff options
author | Dale Rahn <drahn@cvs.openbsd.org> | 2001-09-01 15:50:01 +0000 |
---|---|---|
committer | Dale Rahn <drahn@cvs.openbsd.org> | 2001-09-01 15:50:01 +0000 |
commit | 5ee20173f2267860f40c27cb85bb1a4092a97d93 (patch) | |
tree | fdc26169eac9a5701793ba9664c4f795eefd8f34 /sys/arch/macppc | |
parent | 65997b0cdeb6b9e16cdbbb314ede217802895ade (diff) |
The "powerpc" port which has supported the newer Apple Macintosh powerpc based
is being renamed to macppc. This is to allow sharing of common code
between different powerpc base platforms.
Most of the work involved in the renaming process was performed by miod@
Files moved from powerpc/mac to macppc/dev
Diffstat (limited to 'sys/arch/macppc')
32 files changed, 14459 insertions, 0 deletions
diff --git a/sys/arch/macppc/dev/abtn.c b/sys/arch/macppc/dev/abtn.c new file mode 100644 index 00000000000..c21a800197e --- /dev/null +++ b/sys/arch/macppc/dev/abtn.c @@ -0,0 +1,130 @@ +/* $OpenBSD: abtn.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: abtn.c,v 1.1 1999/07/12 17:48:26 tsubai Exp $ */ + +/*- + * Copyright (C) 1999 Tsubai Masanari. 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. 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 <sys/param.h> +#include <sys/device.h> +#include <sys/systm.h> + +#include <macppc/mac/adbvar.h> +#include <macppc/mac/pm_direct.h> + +#define NVRAM_BRIGHTNESS 0x140e +#define ABTN_HANDLER_ID 31 + +struct abtn_softc { + struct device sc_dev; + + int origaddr; /* ADB device type */ + int adbaddr; /* current ADB address */ + int handler_id; + + int brightness; /* backlight brightness */ + int volume; /* speaker volume (not yet) */ +}; + +static int abtn_match __P((struct device *, void *, void *)); +static void abtn_attach __P((struct device *, struct device *, void *)); +static void abtn_adbcomplete __P((caddr_t, caddr_t, int)); + +struct cfattach abtn_ca = { + sizeof(struct abtn_softc), abtn_match, abtn_attach +}; +struct cfdriver abtn_cd = { + NULL, "abtn", DV_DULL +}; + +int +abtn_match(parent, cf, aux) + struct device *parent; + void *cf; + void *aux; +{ + struct adb_attach_args *aa = aux; + + if (aa->origaddr == ADBADDR_MISC && + aa->handler_id == ABTN_HANDLER_ID) + return 1; + + return 0; +} + +void +abtn_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct abtn_softc *sc = (struct abtn_softc *)self; + struct adb_attach_args *aa = aux; + ADBSetInfoBlock adbinfo; + int bright; + + printf("brightness/volume button\n"); + + bright = pm_read_nvram(NVRAM_BRIGHTNESS); + pm_set_brightness(bright); + sc->brightness = bright; + + sc->origaddr = aa->origaddr; + sc->adbaddr = aa->adbaddr; + sc->handler_id = aa->handler_id; + + adbinfo.siServiceRtPtr = (Ptr)abtn_adbcomplete; + adbinfo.siDataAreaAddr = (caddr_t)sc; + + SetADBInfo(&adbinfo, sc->adbaddr); +} + +void +abtn_adbcomplete(buffer, data, adb_command) + caddr_t buffer, data; + int adb_command; +{ + struct abtn_softc *sc = (struct abtn_softc *)data; + u_int cmd; + + cmd = buffer[1]; + + switch (cmd) { + case 0x0a: + sc->brightness -= 8; + if (sc->brightness < 8) + sc->brightness = 8; + pm_set_brightness(sc->brightness); + pm_write_nvram(NVRAM_BRIGHTNESS, sc->brightness); + break; + + case 0x09: + sc->brightness += 8; + if (sc->brightness > 0x78) + sc->brightness = 0x78; + pm_set_brightness(sc->brightness); + pm_write_nvram(NVRAM_BRIGHTNESS, sc->brightness); + break; + } +} diff --git a/sys/arch/macppc/dev/adb.c b/sys/arch/macppc/dev/adb.c new file mode 100644 index 00000000000..08e36ece7b4 --- /dev/null +++ b/sys/arch/macppc/dev/adb.c @@ -0,0 +1,255 @@ +/* $OpenBSD: adb.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: adb.c,v 1.6 1999/08/16 06:28:09 tsubai Exp $ */ + +/*- + * Copyright (C) 1994 Bradley A. Grantham + * 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 Bradley A. Grantham. + * 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 <sys/param.h> +#include <sys/device.h> +#include <sys/fcntl.h> +#include <sys/poll.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/signalvar.h> +#include <sys/systm.h> + +#include <machine/autoconf.h> + +#include <macppc/mac/adbvar.h> +#include <macppc/mac/akbdvar.h> +#include <macppc/mac/viareg.h> + +#include "aed.h" + +/* + * Function declarations. + */ +static int adbmatch __P((struct device *, void *, void *)); +static void adbattach __P((struct device *, struct device *, void *)); +static int adbprint __P((void *, const char *)); + +/* + * Global variables. + */ +int adb_polling = 0; /* Are we polling? (Debugger mode) */ +int adb_initted = 0; /* adb_init() has completed successfully */ +#ifdef ADB_DEBUG +int adb_debug = 0; /* Output debugging messages */ +#endif /* ADB_DEBUG */ + +/* + * Driver definition. + */ +struct cfattach adb_ca = { + sizeof(struct adb_softc), adbmatch, adbattach +}; +struct cfdriver adb_cd = { + NULL, "adb", DV_DULL +}; + +extern int adbHardware; + +static int +adbmatch(parent, cf, aux) + struct device *parent; + void *cf; + void *aux; +{ + struct confargs *ca = aux; + + if (ca->ca_nreg < 8) + return 0; + + if (ca->ca_nintr < 4) + return 0; + + if (strcmp(ca->ca_name, "via-cuda") == 0) + return 1; + + if (strcmp(ca->ca_name, "via-pmu") == 0) + return 1; + + return 0; +} + +/* HACK ALERT */ +int adb_read_date_time(unsigned long *time); +int adb_write_date_time(unsigned long time); +int adb_set_date_time(unsigned long time); +typedef int (clock_read_t)(int *sec, int *min, int *hour, int *day, + int *mon, int *yr); +typedef int (time_read_t)(unsigned long *sec); +typedef int (time_write_t)(unsigned long sec); +extern time_read_t *time_read; +extern time_write_t *time_write; +extern clock_read_t *clock_read; + + + +static void +adbattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct adb_softc *sc = (struct adb_softc *)self; + struct confargs *ca = aux; + + ADBDataBlock adbdata; + struct adb_attach_args aa_args; + int totaladbs; + int adbindex, adbaddr; + + extern volatile u_char *Via1Base; + + ca->ca_reg[0] += ca->ca_baseaddr; + + sc->sc_regbase = mapiodev(ca->ca_reg[0], ca->ca_reg[1]); + Via1Base = sc->sc_regbase; + + if (strcmp(ca->ca_name, "via-cuda") == 0) + adbHardware = ADB_HW_CUDA; + else if (strcmp(ca->ca_name, "via-pmu") == 0) + adbHardware = ADB_HW_PB; + + adb_polling = 1; + ADBReInit(); + + mac_intr_establish(parent, ca->ca_intr[0], IST_LEVEL, IPL_HIGH, + adb_intr, sc, "adb"); + + /* init powerpc globals which control RTC functionality */ + clock_read = NULL; + time_read = adb_read_date_time; + time_write = adb_set_date_time; + +#ifdef ADB_DEBUG + if (adb_debug) + printf("adb: done with ADBReInit\n"); +#endif + totaladbs = CountADBs(); + + printf(" irq %d", ca->ca_intr[0]); + printf(": %d targets\n", totaladbs); + +#if NAED > 0 + /* ADB event device for compatibility */ + aa_args.origaddr = 0; + aa_args.adbaddr = 0; + aa_args.handler_id = 0; + (void)config_found(self, &aa_args, adbprint); +#endif + + /* for each ADB device */ + for (adbindex = 1; adbindex <= totaladbs; adbindex++) { + /* Get the ADB information */ + adbaddr = GetIndADB(&adbdata, adbindex); + + aa_args.origaddr = adbdata.origADBAddr; + aa_args.adbaddr = adbaddr; + aa_args.handler_id = adbdata.devType; + + (void)config_found(self, &aa_args, adbprint); + } + + if (adbHardware == ADB_HW_CUDA) + adb_cuda_autopoll(); + adb_polling = 0; +} + +int +adbprint(args, name) + void *args; + const char *name; +{ + struct adb_attach_args *aa_args = (struct adb_attach_args *)args; + int rv = UNCONF; + + if (name) { /* no configured device matched */ + rv = UNSUPP; /* most ADB device types are unsupported */ + + /* print out what kind of ADB device we have found */ + printf("%s addr %d: ", name, aa_args->adbaddr); + switch(aa_args->origaddr) { +#ifdef DIAGNOSTIC + case 0: + printf("ADB event device"); + rv = UNCONF; + break; + case ADBADDR_SECURE: + printf("security dongle (%d)", aa_args->handler_id); + break; +#endif + case ADBADDR_MAP: + printf("mapped device (%d)", aa_args->handler_id); + rv = UNCONF; + break; + case ADBADDR_REL: + printf("relative positioning device (%d)", + aa_args->handler_id); + rv = UNCONF; + break; +#ifdef DIAGNOSTIC + case ADBADDR_ABS: + switch (aa_args->handler_id) { + case ADB_ARTPAD: + printf("WACOM ArtPad II"); + break; + default: + printf("absolute positioning device (%d)", + aa_args->handler_id); + break; + } + break; + case ADBADDR_DATATX: + printf("data transfer device (modem?) (%d)", + aa_args->handler_id); + break; + case ADBADDR_MISC: + switch (aa_args->handler_id) { + case ADB_POWERKEY: + printf("Sophisticated Circuits PowerKey"); + break; + default: + printf("misc. device (remote control?) (%d)", + aa_args->handler_id); + break; + } + break; + default: + printf("unknown type device, (handler %d)", + aa_args->handler_id); + break; +#endif /* DIAGNOSTIC */ + } + } else /* a device matched and was configured */ + printf(" addr %d: ", aa_args->adbaddr); + + return rv; +} diff --git a/sys/arch/macppc/dev/adb_direct.c b/sys/arch/macppc/dev/adb_direct.c new file mode 100644 index 00000000000..4a16ddf59dc --- /dev/null +++ b/sys/arch/macppc/dev/adb_direct.c @@ -0,0 +1,2222 @@ +/* $OpenBSD: adb_direct.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: adb_direct.c,v 1.14 2000/06/08 22:10:45 tsubai Exp $ */ + +/* From: adb_direct.c 2.02 4/18/97 jpw */ + +/* + * 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. + */ + +/* + * This code is rather messy, but I don't have time right now + * to clean it up as much as I would like. + * But it works, so I'm happy. :-) jpw + */ + +/* + * TO DO: + * - We could reduce the time spent in the adb_intr_* routines + * by having them save the incoming and outgoing data directly + * in the adbInbound and adbOutbound queues, as it would reduce + * the number of times we need to copy the data around. It + * would also make the code more readable and easier to follow. + * - (Related to above) Use the header part of adbCommand to + * reduce the number of copies we have to do of the data. + * - (Related to above) Actually implement the adbOutbound queue. + * This is fairly easy once you switch all the intr routines + * over to using adbCommand structs directly. + * - There is a bug in the state machine of adb_intr_cuda + * code that causes hangs, especially on 030 machines, probably + * because of some timing issues. Because I have been unable to + * determine the exact cause of this bug, I used the timeout function + * to check for and recover from this condition. If anyone finds + * the actual cause of this bug, the calls to timeout and the + * adb_cuda_tickle routine can be removed. + */ + +#include <sys/param.h> +#include <sys/cdefs.h> +#include <sys/systm.h> +#include <sys/timeout.h> +#include <sys/device.h> + +#include <machine/param.h> +#include <machine/cpu.h> +#include <machine/adbsys.h> + +#include <macppc/mac/viareg.h> +#include <macppc/mac/adbvar.h> +#include <macppc/mac/adb_direct.h> +#include <macppc/mac/pm_direct.h> + +#define printf_intr printf + +#ifdef DEBUG +#ifndef ADB_DEBUG +#define ADB_DEBUG +#endif +#endif + +/* some misc. leftovers */ +#define vPB 0x0000 +#define vPB3 0x08 +#define vPB4 0x10 +#define vPB5 0x20 +#define vSR_INT 0x04 +#define vSR_OUT 0x10 + +/* the type of ADB action that we are currently preforming */ +#define ADB_ACTION_NOTREADY 0x1 /* has not been initialized yet */ +#define ADB_ACTION_IDLE 0x2 /* the bus is currently idle */ +#define ADB_ACTION_OUT 0x3 /* sending out a command */ +#define ADB_ACTION_IN 0x4 /* receiving data */ +#define ADB_ACTION_POLLING 0x5 /* polling - II only */ + +/* + * These describe the state of the ADB bus itself, although they + * don't necessarily correspond directly to ADB states. + * Note: these are not really used in the IIsi code. + */ +#define ADB_BUS_UNKNOWN 0x1 /* we don't know yet - all models */ +#define ADB_BUS_IDLE 0x2 /* bus is idle - all models */ +#define ADB_BUS_CMD 0x3 /* starting a command - II models */ +#define ADB_BUS_ODD 0x4 /* the "odd" state - II models */ +#define ADB_BUS_EVEN 0x5 /* the "even" state - II models */ +#define ADB_BUS_ACTIVE 0x6 /* active state - IIsi models */ +#define ADB_BUS_ACK 0x7 /* currently ACKing - IIsi models */ + +/* + * Shortcuts for setting or testing the VIA bit states. + * Not all shortcuts are used for every type of ADB hardware. + */ +#define ADB_SET_STATE_IDLE_II() via_reg_or(VIA1, vBufB, (vPB4 | vPB5)) +#define ADB_SET_STATE_IDLE_IISI() via_reg_and(VIA1, vBufB, ~(vPB4 | vPB5)) +#define ADB_SET_STATE_IDLE_CUDA() via_reg_or(VIA1, vBufB, (vPB4 | vPB5)) +#define ADB_SET_STATE_CMD() via_reg_and(VIA1, vBufB, ~(vPB4 | vPB5)) +#define ADB_SET_STATE_EVEN() write_via_reg(VIA1, vBufB, \ + (read_via_reg(VIA1, vBufB) | vPB4) & ~vPB5) +#define ADB_SET_STATE_ODD() write_via_reg(VIA1, vBufB, \ + (read_via_reg(VIA1, vBufB) | vPB5) & ~vPB4 ) +#define ADB_SET_STATE_ACTIVE() via_reg_or(VIA1, vBufB, vPB5) +#define ADB_SET_STATE_INACTIVE() via_reg_and(VIA1, vBufB, ~vPB5) +#define ADB_SET_STATE_TIP() via_reg_and(VIA1, vBufB, ~vPB5) +#define ADB_CLR_STATE_TIP() via_reg_or(VIA1, vBufB, vPB5) +#define ADB_SET_STATE_ACKON() via_reg_or(VIA1, vBufB, vPB4) +#define ADB_SET_STATE_ACKOFF() via_reg_and(VIA1, vBufB, ~vPB4) +#define ADB_TOGGLE_STATE_ACK_CUDA() via_reg_xor(VIA1, vBufB, vPB4) +#define ADB_SET_STATE_ACKON_CUDA() via_reg_and(VIA1, vBufB, ~vPB4) +#define ADB_SET_STATE_ACKOFF_CUDA() via_reg_or(VIA1, vBufB, vPB4) +#define ADB_SET_SR_INPUT() via_reg_and(VIA1, vACR, ~vSR_OUT) +#define ADB_SET_SR_OUTPUT() via_reg_or(VIA1, vACR, vSR_OUT) +#define ADB_SR() read_via_reg(VIA1, vSR) +#define ADB_VIA_INTR_ENABLE() write_via_reg(VIA1, vIER, 0x84) +#define ADB_VIA_INTR_DISABLE() write_via_reg(VIA1, vIER, 0x04) +#define ADB_VIA_CLR_INTR() write_via_reg(VIA1, vIFR, 0x04) +#define ADB_INTR_IS_OFF (vPB3 == (read_via_reg(VIA1, vBufB) & vPB3)) +#define ADB_INTR_IS_ON (0 == (read_via_reg(VIA1, vBufB) & vPB3)) +#define ADB_SR_INTR_IS_OFF (0 == (read_via_reg(VIA1, vIFR) & vSR_INT)) +#define ADB_SR_INTR_IS_ON (vSR_INT == (read_via_reg(VIA1, \ + vIFR) & vSR_INT)) + +/* + * This is the delay that is required (in uS) between certain + * ADB transactions. The actual timing delay for for each uS is + * calculated at boot time to account for differences in machine speed. + */ +#define ADB_DELAY 150 + +/* + * Maximum ADB message length; includes space for data, result, and + * device code - plus a little for safety. + */ +#define ADB_MAX_MSG_LENGTH 16 +#define ADB_MAX_HDR_LENGTH 8 + +#define ADB_QUEUE 32 +#define ADB_TICKLE_TICKS 4 + +/* + * A structure for storing information about each ADB device. + */ +struct ADBDevEntry { + void (*ServiceRtPtr) __P((void)); + void *DataAreaAddr; + int devType; + int origAddr; + int currentAddr; +}; + +/* + * Used to hold ADB commands that are waiting to be sent out. + */ +struct adbCmdHoldEntry { + u_char outBuf[ADB_MAX_MSG_LENGTH]; /* our message */ + u_char *saveBuf; /* buffer to know where to save result */ + u_char *compRout; /* completion routine pointer */ + u_char *data; /* completion routine data pointer */ +}; + +/* + * Eventually used for two separate queues, the queue between + * the upper and lower halves, and the outgoing packet queue. + * TO DO: adbCommand can replace all of adbCmdHoldEntry eventually + */ +struct adbCommand { + u_char header[ADB_MAX_HDR_LENGTH]; /* not used yet */ + u_char data[ADB_MAX_MSG_LENGTH]; /* packet data only */ + u_char *saveBuf; /* where to save result */ + u_char *compRout; /* completion routine pointer */ + u_char *compData; /* completion routine data pointer */ + u_int cmd; /* the original command for this data */ + u_int unsol; /* 1 if packet was unsolicited */ + u_int ack_only; /* 1 for no special processing */ +}; + +/* + * A few variables that we need and their initial values. + */ +int adbHardware = ADB_HW_UNKNOWN; +int adbActionState = ADB_ACTION_NOTREADY; +int adbBusState = ADB_BUS_UNKNOWN; +int adbWaiting = 0; /* waiting for return data from the device */ +int adbWriteDelay = 0; /* working on (or waiting to do) a write */ +int adbOutQueueHasData = 0; /* something in the queue waiting to go out */ +int adbNextEnd = 0; /* the next incoming bute is the last (II) */ +int adbSoftPower = 0; /* machine supports soft power */ + +int adbWaitingCmd = 0; /* ADB command we are waiting for */ +u_char *adbBuffer = (long)0; /* pointer to user data area */ +void *adbCompRout = (long)0; /* pointer to the completion routine */ +void *adbCompData = (long)0; /* pointer to the completion routine data */ +long adbFakeInts = 0; /* keeps track of fake ADB interrupts for + * timeouts (II) */ +int adbStarting = 1; /* doing ADBReInit so do polling differently */ +int adbSendTalk = 0; /* the intr routine is sending the talk, not + * the user (II) */ +int adbPolling = 0; /* we are polling for service request */ +int adbPollCmd = 0; /* the last poll command we sent */ + +u_char adbInputBuffer[ADB_MAX_MSG_LENGTH]; /* data input buffer */ +u_char adbOutputBuffer[ADB_MAX_MSG_LENGTH]; /* data output buffer */ +struct adbCmdHoldEntry adbOutQueue; /* our 1 entry output queue */ + +int adbSentChars = 0; /* how many characters we have sent */ +int adbLastDevice = 0; /* last ADB dev we heard from (II ONLY) */ +int adbLastDevIndex = 0; /* last ADB dev loc in dev table (II ONLY) */ +int adbLastCommand = 0; /* the last ADB command we sent (II) */ + +struct ADBDevEntry ADBDevTable[16]; /* our ADB device table */ +int ADBNumDevices; /* num. of ADB devices found with ADBReInit */ + +struct adbCommand adbInbound[ADB_QUEUE]; /* incoming queue */ +int adbInCount = 0; /* how many packets in in queue */ +int adbInHead = 0; /* head of in queue */ +int adbInTail = 0; /* tail of in queue */ +struct adbCommand adbOutbound[ADB_QUEUE]; /* outgoing queue - not used yet */ +int adbOutCount = 0; /* how many packets in out queue */ +int adbOutHead = 0; /* head of out queue */ +int adbOutTail = 0; /* tail of out queue */ + +int tickle_count = 0; /* how many tickles seen for this packet? */ +int tickle_serial = 0; /* the last packet tickled */ +int adb_cuda_serial = 0; /* the current packet */ +struct timeout adb_cuda_timeout; +struct timeout adb_softintr_timeout; + +volatile u_char *Via1Base; +extern int adb_polling; /* Are we polling? */ + +void pm_setup_adb __P((void)); +void pm_check_adb_devices __P((int)); +void pm_intr __P((void)); +int pm_adb_op __P((u_char *, void *, void *, int)); +void pm_init_adb_device __P((void)); + +/* + * The following are private routines. + */ +#ifdef ADB_DEBUG +void print_single __P((u_char *)); +#endif +void adb_intr_II __P((void)); +void adb_intr_IIsi __P((void)); +void adb_intr_cuda __P((void)); +void adb_soft_intr __P((void)); +int send_adb_II __P((u_char *, u_char *, void *, void *, int)); +int send_adb_IIsi __P((u_char *, u_char *, void *, void *, int)); +int send_adb_cuda __P((u_char *, u_char *, void *, void *, int)); +void adb_intr_cuda_test __P((void)); +void adb_cuda_tickle __P((void)); +void adb_pass_up __P((struct adbCommand *)); +void adb_op_comprout __P((caddr_t, caddr_t, int)); +void adb_reinit __P((void)); +int count_adbs __P((void)); +int get_ind_adb_info __P((ADBDataBlock *, int)); +int get_adb_info __P((ADBDataBlock *, int)); +int set_adb_info __P((ADBSetInfoBlock *, int)); +void adb_setup_hw_type __P((void)); +int adb_op __P((Ptr, Ptr, Ptr, short)); +void adb_read_II __P((u_char *)); +void adb_hw_setup __P((void)); +void adb_hw_setup_IIsi __P((u_char *)); +void adb_comp_exec __P((void)); +int adb_cmd_result __P((u_char *)); +int adb_cmd_extra __P((u_char *)); +int adb_guess_next_device __P((void)); +int adb_prog_switch_enable __P((void)); +int adb_prog_switch_disable __P((void)); +/* we should create this and it will be the public version */ +int send_adb __P((u_char *, void *, void *)); +int setsoftadb __P((void)); + +#ifdef ADB_DEBUG +/* + * print_single + * Diagnostic display routine. Displays the hex values of the + * specified elements of the u_char. The length of the "string" + * is in [0]. + */ +void +print_single(str) + u_char *str; +{ + int x; + + if (str == 0) { + printf_intr("no data - null pointer\n"); + return; + } + if (*str == 0) { + printf_intr("nothing returned\n"); + return; + } + if (*str > 20) { + printf_intr("ADB: ACK > 20 no way!\n"); + *str = 20; + } + printf_intr("(length=0x%x):", *str); + for (x = 1; x <= *str; x++) + printf_intr(" 0x%02x", str[x]); + printf_intr("\n"); +} +#endif + +void +adb_cuda_tickle(void) +{ + volatile int s; + + if (adbActionState == ADB_ACTION_IN) { + if (tickle_serial == adb_cuda_serial) { + if (++tickle_count > 0) { + s = splhigh(); + adbActionState = ADB_ACTION_IDLE; + adbInputBuffer[0] = 0; + ADB_SET_STATE_IDLE_CUDA(); + splx(s); + } + } else { + tickle_serial = adb_cuda_serial; + tickle_count = 0; + } + } else { + tickle_serial = adb_cuda_serial; + tickle_count = 0; + } + + timeout_add(&adb_cuda_timeout, ADB_TICKLE_TICKS); +} + +/* + * called when when an adb interrupt happens + * + * Cuda version of adb_intr + * TO DO: do we want to add some calls to intr_dispatch() here to + * grab serial interrupts? + */ +void +adb_intr_cuda(void) +{ + volatile int i, ending; + volatile unsigned int s; + struct adbCommand packet; + + s = splhigh(); /* can't be too careful - might be called */ + /* from a routine, NOT an interrupt */ + + ADB_VIA_CLR_INTR(); /* clear interrupt */ + ADB_VIA_INTR_DISABLE(); /* disable ADB interrupt on IIs. */ + +switch_start: + switch (adbActionState) { + case ADB_ACTION_IDLE: + /* + * This is an unexpected packet, so grab the first (dummy) + * byte, set up the proper vars, and tell the chip we are + * starting to receive the packet by setting the TIP bit. + */ + adbInputBuffer[1] = ADB_SR(); + adb_cuda_serial++; + if (ADB_INTR_IS_OFF) /* must have been a fake start */ + break; + + ADB_SET_SR_INPUT(); + ADB_SET_STATE_TIP(); + + adbInputBuffer[0] = 1; + adbActionState = ADB_ACTION_IN; +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("idle 0x%02x ", adbInputBuffer[1]); +#endif + break; + + case ADB_ACTION_IN: + adbInputBuffer[++adbInputBuffer[0]] = ADB_SR(); + /* intr off means this is the last byte (end of frame) */ + if (ADB_INTR_IS_OFF) + ending = 1; + else + ending = 0; + + if (1 == ending) { /* end of message? */ +#ifdef ADB_DEBUG + if (adb_debug) { + printf_intr("in end 0x%02x ", + adbInputBuffer[adbInputBuffer[0]]); + print_single(adbInputBuffer); + } +#endif + + /* + * Are we waiting AND does this packet match what we + * are waiting for AND is it coming from either the + * ADB or RTC/PRAM sub-device? This section _should_ + * recognize all ADB and RTC/PRAM type commands, but + * there may be more... NOTE: commands are always at + * [4], even for RTC/PRAM commands. + */ + /* set up data for adb_pass_up */ + memcpy(packet.data, adbInputBuffer, adbInputBuffer[0] + 1); + + if ((adbWaiting == 1) && + (adbInputBuffer[4] == adbWaitingCmd) && + ((adbInputBuffer[2] == 0x00) || + (adbInputBuffer[2] == 0x01))) { + packet.saveBuf = adbBuffer; + packet.compRout = adbCompRout; + packet.compData = adbCompData; + packet.unsol = 0; + packet.ack_only = 0; + adb_pass_up(&packet); + + adbWaitingCmd = 0; /* reset "waiting" vars */ + adbWaiting = 0; + adbBuffer = (long)0; + adbCompRout = (long)0; + adbCompData = (long)0; + } else { + packet.unsol = 1; + packet.ack_only = 0; + adb_pass_up(&packet); + } + + + /* reset vars and signal the end of this frame */ + adbActionState = ADB_ACTION_IDLE; + adbInputBuffer[0] = 0; + ADB_SET_STATE_IDLE_CUDA(); + /*ADB_SET_SR_INPUT();*/ + + /* + * If there is something waiting to be sent out, + * the set everything up and send the first byte. + */ + if (adbWriteDelay == 1) { + delay(ADB_DELAY); /* required */ + adbSentChars = 0; + adbActionState = ADB_ACTION_OUT; + /* + * If the interrupt is on, we were too slow + * and the chip has already started to send + * something to us, so back out of the write + * and start a read cycle. + */ + if (ADB_INTR_IS_ON) { + ADB_SET_SR_INPUT(); + ADB_SET_STATE_IDLE_CUDA(); + adbSentChars = 0; + adbActionState = ADB_ACTION_IDLE; + adbInputBuffer[0] = 0; + break; + } + /* + * If we got here, it's ok to start sending + * so load the first byte and tell the chip + * we want to send. + */ + ADB_SET_STATE_TIP(); + ADB_SET_SR_OUTPUT(); + write_via_reg(VIA1, vSR, adbOutputBuffer[adbSentChars + 1]); + } + } else { + ADB_TOGGLE_STATE_ACK_CUDA(); +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("in 0x%02x ", + adbInputBuffer[adbInputBuffer[0]]); +#endif + } + break; + + case ADB_ACTION_OUT: + i = ADB_SR(); /* reset SR-intr in IFR */ +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("intr out 0x%02x ", i); +#endif + + adbSentChars++; + if (ADB_INTR_IS_ON) { /* ADB intr low during write */ +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("intr was on "); +#endif + ADB_SET_SR_INPUT(); /* make sure SR is set to IN */ + ADB_SET_STATE_IDLE_CUDA(); + adbSentChars = 0; /* must start all over */ + adbActionState = ADB_ACTION_IDLE; /* new state */ + adbInputBuffer[0] = 0; + adbWriteDelay = 1; /* must retry when done with + * read */ + delay(ADB_DELAY); + goto switch_start; /* process next state right + * now */ + break; + } + if (adbOutputBuffer[0] == adbSentChars) { /* check for done */ + if (0 == adb_cmd_result(adbOutputBuffer)) { /* do we expect data + * back? */ + adbWaiting = 1; /* signal waiting for return */ + adbWaitingCmd = adbOutputBuffer[2]; /* save waiting command */ + } else { /* no talk, so done */ + /* set up stuff for adb_pass_up */ + memcpy(packet.data, adbInputBuffer, adbInputBuffer[0] + 1); + packet.saveBuf = adbBuffer; + packet.compRout = adbCompRout; + packet.compData = adbCompData; + packet.cmd = adbWaitingCmd; + packet.unsol = 0; + packet.ack_only = 1; + adb_pass_up(&packet); + + /* reset "waiting" vars, just in case */ + adbWaitingCmd = 0; + adbBuffer = (long)0; + adbCompRout = (long)0; + adbCompData = (long)0; + } + + adbWriteDelay = 0; /* done writing */ + adbActionState = ADB_ACTION_IDLE; /* signal bus is idle */ + ADB_SET_SR_INPUT(); + ADB_SET_STATE_IDLE_CUDA(); +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("write done "); +#endif + } else { + write_via_reg(VIA1, vSR, adbOutputBuffer[adbSentChars + 1]); /* send next byte */ + ADB_TOGGLE_STATE_ACK_CUDA(); /* signal byte ready to + * shift */ +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("toggle "); +#endif + } + break; + + case ADB_ACTION_NOTREADY: +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("adb: not yet initialized\n"); +#endif + break; + + default: +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("intr: unknown ADB state\n"); +#endif + } + + ADB_VIA_INTR_ENABLE(); /* enable ADB interrupt on IIs. */ + + splx(s); /* restore */ + + return; +} /* end adb_intr_cuda */ + + +int +send_adb_cuda(u_char * in, u_char * buffer, void *compRout, void *data, int + command) +{ + int s, len; + +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("SEND\n"); +#endif + + if (adbActionState == ADB_ACTION_NOTREADY) + return 1; + + /* Don't interrupt while we are messing with the ADB */ + s = splhigh(); + + if ((adbActionState == ADB_ACTION_IDLE) && /* ADB available? */ + (ADB_INTR_IS_OFF)) { /* and no incoming interrupt? */ + } else + if (adbWriteDelay == 0) /* it's busy, but is anything waiting? */ + adbWriteDelay = 1; /* if no, then we'll "queue" + * it up */ + else { + splx(s); + return 1; /* really busy! */ + } + +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("QUEUE\n"); +#endif + if ((long)in == (long)0) { /* need to convert? */ + /* + * 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) + */ + if ((command & 0x0c) == 0x08) /* copy addl data ONLY if + * doing a listen! */ + len = buffer[0]; /* length of additional data */ + else + len = 0;/* no additional data */ + + adbOutputBuffer[0] = 2 + len; /* dev. type + command + addl. + * data */ + adbOutputBuffer[1] = 0x00; /* mark as an ADB command */ + adbOutputBuffer[2] = (u_char)command; /* load command */ + + /* copy additional output data, if any */ + memcpy(adbOutputBuffer + 3, buffer + 1, len); + } else + /* if data ready, just copy over */ + memcpy(adbOutputBuffer, in, in[0] + 2); + + adbSentChars = 0; /* nothing sent yet */ + adbBuffer = buffer; /* save buffer to know where to save result */ + adbCompRout = compRout; /* save completion routine pointer */ + adbCompData = data; /* save completion routine data pointer */ + adbWaitingCmd = adbOutputBuffer[2]; /* save wait command */ + + if (adbWriteDelay != 1) { /* start command now? */ +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("out start NOW"); +#endif + delay(ADB_DELAY); + adbActionState = ADB_ACTION_OUT; /* set next state */ + ADB_SET_SR_OUTPUT(); /* set shift register for OUT */ + write_via_reg(VIA1, vSR, adbOutputBuffer[adbSentChars + 1]); /* load byte for output */ + ADB_SET_STATE_ACKOFF_CUDA(); + ADB_SET_STATE_TIP(); /* tell ADB that we want to send */ + } + adbWriteDelay = 1; /* something in the write "queue" */ + + splx(s); + + if ((s & (1 << 18)) || adb_polling) /* XXX were VIA1 interrupts blocked ? */ + /* poll until byte done */ + while ((adbActionState != ADB_ACTION_IDLE) || (ADB_INTR_IS_ON) + || (adbWaiting == 1)) + if (ADB_SR_INTR_IS_ON) { /* wait for "interrupt" */ + adb_intr_cuda(); /* process it */ + adb_soft_intr(); + } + + return 0; +} /* send_adb_cuda */ + + +void +adb_intr_II(void) +{ + panic("adb_intr_II"); +} + + +/* + * send_adb version for II series machines + */ +int +send_adb_II(u_char * in, u_char * buffer, void *compRout, void *data, int command) +{ + panic("send_adb_II"); +} + + +/* + * This routine is called from the II series interrupt routine + * to determine what the "next" device is that should be polled. + */ +int +adb_guess_next_device(void) +{ + int last, i, dummy; + + if (adbStarting) { + /* + * Start polling EVERY device, since we can't be sure there is + * anything in the device table yet + */ + if (adbLastDevice < 1 || adbLastDevice > 15) + adbLastDevice = 1; + if (++adbLastDevice > 15) /* point to next one */ + adbLastDevice = 1; + } else { + /* find the next device using the device table */ + if (adbLastDevice < 1 || adbLastDevice > 15) /* let's be parinoid */ + adbLastDevice = 2; + last = 1; /* default index location */ + + for (i = 1; i < 16; i++) /* find index entry */ + if (ADBDevTable[i].currentAddr == adbLastDevice) { /* look for device */ + last = i; /* found it */ + break; + } + dummy = last; /* index to start at */ + for (;;) { /* find next device in index */ + if (++dummy > 15) /* wrap around if needed */ + dummy = 1; + if (dummy == last) { /* didn't find any other + * device! This can happen if + * there are no devices on the + * bus */ + dummy = 1; + break; + } + /* found the next device */ + if (ADBDevTable[dummy].devType != 0) + break; + } + adbLastDevice = ADBDevTable[dummy].currentAddr; + } + return adbLastDevice; +} + + +/* + * Called when when an adb interrupt happens. + * This routine simply transfers control over to the appropriate + * code for the machine we are running on. + */ +int +adb_intr(void *arg) +{ + switch (adbHardware) { + case ADB_HW_II: + adb_intr_II(); + break; + + case ADB_HW_IISI: + adb_intr_IIsi(); + break; + + case ADB_HW_PB: + pm_intr(); + break; + + case ADB_HW_CUDA: + adb_intr_cuda(); + break; + + case ADB_HW_UNKNOWN: + break; + } + return 1; +} + + +/* + * called when when an adb interrupt happens + * + * IIsi version of adb_intr + * + */ +void +adb_intr_IIsi(void) +{ + panic("adb_intr_IIsi"); +} + + +/***************************************************************************** + * if the device is currently busy, and there is no data waiting to go out, then + * the data is "queued" in the outgoing buffer. If we are already waiting, then + * we return. + * in: if (in == 0) then the command string is built from command and buffer + * if (in != 0) then in is used as the command string + * buffer: additional data to be sent (used only if in == 0) + * this is also where return data is stored + * compRout: the completion routine that is called when then return value + * is received (if a return value is expected) + * data: a data pointer that can be used by the completion routine + * command: an ADB command to be sent (used only if in == 0) + * + */ +int +send_adb_IIsi(u_char * in, u_char * buffer, void *compRout, void *data, int + command) +{ + panic("send_adb_IIsi"); +} + + +/* + * adb_pass_up is called by the interrupt-time routines. + * It takes the raw packet data that was received from the + * device and puts it into the queue that the upper half + * processes. It then signals for a soft ADB interrupt which + * will eventually call the upper half routine (adb_soft_intr). + * + * If in->unsol is 0, then this is either the notification + * that the packet was sent (on a LISTEN, for example), or the + * response from the device (on a TALK). The completion routine + * is called only if the user specified one. + * + * If in->unsol is 1, then this packet was unsolicited and + * so we look up the device in the ADB device table to determine + * what it's default service routine is. + * + * If in->ack_only is 1, then we really only need to call + * the completion routine, so don't do any other stuff. + * + * Note that in->data contains the packet header AND data, + * while adbInbound[]->data contains ONLY data. + * + * Note: Called only at interrupt time. Assumes this. + */ +void +adb_pass_up(struct adbCommand *in) +{ + int start = 0, len = 0, cmd = 0; + ADBDataBlock block; + + /* temp for testing */ + /*u_char *buffer = 0;*/ + /*u_char *compdata = 0;*/ + /*u_char *comprout = 0;*/ + + if (adbInCount >= ADB_QUEUE) { +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("adb: ring buffer overflow\n"); +#endif + return; + } + + if (in->ack_only) { + len = in->data[0]; + cmd = in->cmd; + start = 0; + } else { + switch (adbHardware) { + case ADB_HW_II: + cmd = in->data[1]; + if (in->data[0] < 2) + len = 0; + else + len = in->data[0]-1; + start = 1; + break; + + case ADB_HW_IISI: + case ADB_HW_CUDA: + /* If it's unsolicited, accept only ADB data for now */ + if (in->unsol) + if (0 != in->data[2]) + return; + cmd = in->data[4]; + if (in->data[0] < 5) + len = 0; + else + len = in->data[0]-4; + start = 4; + break; + + case ADB_HW_PB: + cmd = in->data[1]; + if (in->data[0] < 2) + len = 0; + else + len = in->data[0]-1; + start = 1; + break; + + case ADB_HW_UNKNOWN: + return; + } + + /* Make sure there is a valid device entry for this device */ + if (in->unsol) { + /* ignore unsolicited data during adbreinit */ + if (adbStarting) + return; + /* get device's comp. routine and data area */ + if (-1 == get_adb_info(&block, ADB_CMDADDR(cmd))) + return; + } + } + + /* + * If this is an unsolicited packet, we need to fill in + * some info so adb_soft_intr can process this packet + * properly. If it's not unsolicited, then use what + * the caller sent us. + */ + if (in->unsol) { + adbInbound[adbInTail].compRout = (void *)block.dbServiceRtPtr; + adbInbound[adbInTail].compData = (void *)block.dbDataAreaAddr; + adbInbound[adbInTail].saveBuf = (void *)adbInbound[adbInTail].data; + } else { + adbInbound[adbInTail].compRout = (void *)in->compRout; + adbInbound[adbInTail].compData = (void *)in->compData; + adbInbound[adbInTail].saveBuf = (void *)in->saveBuf; + } + +#ifdef ADB_DEBUG + if (adb_debug && in->data[1] == 2) + printf_intr("adb: caught error\n"); +#endif + + /* copy the packet data over */ + /* + * TO DO: If the *_intr routines fed their incoming data + * directly into an adbCommand struct, which is passed to + * this routine, then we could eliminate this copy. + */ + memcpy(adbInbound[adbInTail].data + 1, in->data + start + 1, len); + adbInbound[adbInTail].data[0] = len; + adbInbound[adbInTail].cmd = cmd; + + adbInCount++; + if (++adbInTail >= ADB_QUEUE) + adbInTail = 0; + + /* + * If the debugger is running, call upper half manually. + * Otherwise, trigger a soft interrupt to handle the rest later. + */ + if (adb_polling) + adb_soft_intr(); + else + setsoftadb(); + + return; +} + + +/* + * Called to process the packets after they have been + * placed in the incoming queue. + * + */ +void +adb_soft_intr(void) +{ + int s; + int cmd = 0; + u_char *buffer = 0; + u_char *comprout = 0; + u_char *compdata = 0; + +#if 0 + s = splhigh(); + printf_intr("sr: %x\n", (s & 0x0700)); + splx(s); +#endif + +/*delay(2*ADB_DELAY);*/ + + while (adbInCount) { +#ifdef ADB_DEBUG + if (adb_debug & 0x80) + printf_intr("%x %x %x ", + adbInCount, adbInHead, adbInTail); +#endif + /* get the data we need from the queue */ + buffer = adbInbound[adbInHead].saveBuf; + comprout = adbInbound[adbInHead].compRout; + compdata = adbInbound[adbInHead].compData; + cmd = adbInbound[adbInHead].cmd; + + /* copy over data to data area if it's valid */ + /* + * Note that for unsol packets we don't want to copy the + * data anywhere, so buffer was already set to 0. + * For ack_only buffer was set to 0, so don't copy. + */ + if (buffer) + memcpy(buffer, adbInbound[adbInHead].data, + adbInbound[adbInHead].data[0] + 1); + +#ifdef ADB_DEBUG + if (adb_debug & 0x80) { + printf_intr("%p %p %p %x ", + buffer, comprout, compdata, (short)cmd); + printf_intr("buf: "); + print_single(adbInbound[adbInHead].data); + } +#endif + + /* call default completion routine if it's valid */ + if (comprout) { + ((int (*) __P((u_char *, u_char*, int))) comprout) + (buffer, compdata, cmd); +#if 0 +#ifdef __NetBSD__ + asm(" movml #0xffff,sp@- | save all registers + movl %0,a2 | compdata + movl %1,a1 | comprout + movl %2,a0 | buffer + movl %3,d0 | cmd + jbsr a1@ | go call the routine + movml sp@+,#0xffff | restore all registers" + : + : "g"(compdata), "g"(comprout), + "g"(buffer), "g"(cmd) + : "d0", "a0", "a1", "a2"); +#else /* for macos based testing */ + asm + { + movem.l a0/a1/a2/d0, -(a7) + move.l compdata, a2 + move.l comprout, a1 + move.l buffer, a0 + move.w cmd, d0 + jsr(a1) + movem.l(a7)+, d0/a2/a1/a0 + } +#endif +#endif + } + + s = splhigh(); + adbInCount--; + if (++adbInHead >= ADB_QUEUE) + adbInHead = 0; + splx(s); + + } + return; +} + + +/* + * This is my version of the ADBOp routine. It mainly just calls the + * hardware-specific routine. + * + * data : pointer to data area to be used by compRout + * compRout : completion routine + * buffer : for LISTEN: points to data to send - MAX 8 data bytes, + * byte 0 = # of bytes + * : for TALK: points to place to save return data + * command : the adb command to send + * result : 0 = success + * : -1 = could not complete + */ +int +adb_op(Ptr buffer, Ptr compRout, Ptr data, short command) +{ + int result; + + switch (adbHardware) { + case ADB_HW_II: + result = send_adb_II((u_char *)0, (u_char *)buffer, + (void *)compRout, (void *)data, (int)command); + if (result == 0) + return 0; + else + return -1; + break; + + case ADB_HW_IISI: + result = send_adb_IIsi((u_char *)0, (u_char *)buffer, + (void *)compRout, (void *)data, (int)command); + /* + * I wish I knew why this delay is needed. It usually needs to + * be here when several commands are sent in close succession, + * especially early in device probes when doing collision + * detection. It must be some race condition. Sigh. - jpw + */ + delay(100); + if (result == 0) + return 0; + else + return -1; + break; + + case ADB_HW_PB: + result = pm_adb_op((u_char *)buffer, (void *)compRout, + (void *)data, (int)command); + + if (result == 0) + return 0; + else + return -1; + break; + + case ADB_HW_CUDA: + result = send_adb_cuda((u_char *)0, (u_char *)buffer, + (void *)compRout, (void *)data, (int)command); + if (result == 0) + return 0; + else + return -1; + break; + + case ADB_HW_UNKNOWN: + default: + return -1; + } +} + + +/* + * adb_hw_setup + * This routine sets up the possible machine specific hardware + * config (mainly VIA settings) for the various models. + */ +void +adb_hw_setup(void) +{ + volatile int i; + u_char send_string[ADB_MAX_MSG_LENGTH]; + + switch (adbHardware) { + case ADB_HW_II: + via_reg_or(VIA1, vDirB, 0x30); /* register B bits 4 and 5: + * outputs */ + via_reg_and(VIA1, vDirB, 0xf7); /* register B bit 3: input */ + via_reg_and(VIA1, vACR, ~vSR_OUT); /* make sure SR is set + * to IN (II, IIsi) */ + adbActionState = ADB_ACTION_IDLE; /* used by all types of + * hardware (II, IIsi) */ + adbBusState = ADB_BUS_IDLE; /* this var. used in II-series + * code only */ + write_via_reg(VIA1, vIER, 0x84);/* make sure VIA interrupts + * are on (II, IIsi) */ + ADB_SET_STATE_IDLE_II(); /* set ADB bus state to idle */ + + ADB_VIA_CLR_INTR(); /* clear interrupt */ + break; + + case ADB_HW_IISI: + via_reg_or(VIA1, vDirB, 0x30); /* register B bits 4 and 5: + * outputs */ + via_reg_and(VIA1, vDirB, 0xf7); /* register B bit 3: input */ + via_reg_and(VIA1, vACR, ~vSR_OUT); /* make sure SR is set + * to IN (II, IIsi) */ + adbActionState = ADB_ACTION_IDLE; /* used by all types of + * hardware (II, IIsi) */ + adbBusState = ADB_BUS_IDLE; /* this var. used in II-series + * code only */ + write_via_reg(VIA1, vIER, 0x84);/* make sure VIA interrupts + * are on (II, IIsi) */ + ADB_SET_STATE_IDLE_IISI(); /* set ADB bus state to idle */ + + /* get those pesky clock ticks we missed while booting */ + for (i = 0; i < 30; i++) { + delay(ADB_DELAY); + adb_hw_setup_IIsi(send_string); +#ifdef ADB_DEBUG + if (adb_debug) { + printf_intr("adb: cleanup: "); + print_single(send_string); + } +#endif + delay(ADB_DELAY); + if (ADB_INTR_IS_OFF) + break; + } + break; + + case ADB_HW_PB: + /* + * XXX - really PM_VIA_CLR_INTR - should we put it in + * pm_direct.h? + */ + write_via_reg(VIA1, vIFR, 0x90); /* clear interrupt */ + break; + + case ADB_HW_CUDA: + via_reg_or(VIA1, vDirB, 0x30); /* register B bits 4 and 5: + * outputs */ + via_reg_and(VIA1, vDirB, 0xf7); /* register B bit 3: input */ + via_reg_and(VIA1, vACR, ~vSR_OUT); /* make sure SR is set + * to IN */ + write_via_reg(VIA1, vACR, (read_via_reg(VIA1, vACR) | 0x0c) & ~0x10); + adbActionState = ADB_ACTION_IDLE; /* used by all types of + * hardware */ + adbBusState = ADB_BUS_IDLE; /* this var. used in II-series + * code only */ + write_via_reg(VIA1, vIER, 0x84);/* make sure VIA interrupts + * are on */ + ADB_SET_STATE_IDLE_CUDA(); /* set ADB bus state to idle */ + + /* sort of a device reset */ + i = ADB_SR(); /* clear interrupt */ + ADB_VIA_INTR_DISABLE(); /* no interrupts while clearing */ + ADB_SET_STATE_IDLE_CUDA(); /* reset state to idle */ + delay(ADB_DELAY); + ADB_SET_STATE_TIP(); /* signal start of frame */ + delay(ADB_DELAY); + ADB_TOGGLE_STATE_ACK_CUDA(); + delay(ADB_DELAY); + ADB_CLR_STATE_TIP(); + delay(ADB_DELAY); + ADB_SET_STATE_IDLE_CUDA(); /* back to idle state */ + i = ADB_SR(); /* clear interrupt */ + ADB_VIA_INTR_ENABLE(); /* ints ok now */ + break; + + case ADB_HW_UNKNOWN: + default: + write_via_reg(VIA1, vIER, 0x04);/* turn interrupts off - TO + * DO: turn PB ints off? */ + return; + break; + } +} + + +/* + * adb_hw_setup_IIsi + * This is sort of a "read" routine that forces the adb hardware through a read cycle + * if there is something waiting. This helps "clean up" any commands that may have gotten + * stuck or stopped during the boot process. + * + */ +void +adb_hw_setup_IIsi(u_char * buffer) +{ + panic("adb_hw_setup_IIsi"); +} + + +/* + * adb_reinit sets up the adb stuff + * + */ +void +adb_reinit(void) +{ + u_char send_string[ADB_MAX_MSG_LENGTH]; + ADBDataBlock data; /* temp. holder for getting device info */ + volatile int i, x; + int s; + int command; + int result; + int saveptr; /* point to next free relocation address */ + int device; + int nonewtimes; /* times thru loop w/o any new devices */ + + /* Make sure we are not interrupted while building the table. */ + if (adbHardware != ADB_HW_PB) /* ints must be on for PB? */ + s = splhigh(); + + ADBNumDevices = 0; /* no devices yet */ + + /* Let intr routines know we are running reinit */ + adbStarting = 1; + + /* + * Initialize the ADB table. For now, we'll always use the same table + * that is defined at the beginning of this file - no mallocs. + */ + for (i = 0; i < 16; i++) + ADBDevTable[i].devType = 0; + + adb_setup_hw_type(); /* setup hardware type */ + + adb_hw_setup(); /* init the VIA bits and hard reset ADB */ + + delay(1000); + + /* send an ADB reset first */ + adb_op_sync((Ptr)0, (Ptr)0, (Ptr)0, (short)0x00); + delay(200000); + + /* + * Probe for ADB devices. Probe devices 1-15 quickly to determine + * which device addresses are in use and which are free. For each + * address that is in use, move the device at that address to a higher + * free address. Continue doing this at that address until no device + * responds at that address. Then move the last device that was moved + * back to the original address. Do this for the remaining addresses + * that we determined were in use. + * + * When finished, do this entire process over again with the updated + * list of in use addresses. Do this until no new devices have been + * found in 20 passes though the in use address list. (This probably + * seems long and complicated, but it's the best way to detect multiple + * devices at the same address - sometimes it takes a couple of tries + * before the collision is detected.) + */ + + /* initial scan through the devices */ + for (i = 1; i < 16; i++) { + send_string[0] = 0; + command = ADBTALK(i, 3); + result = adb_op_sync((Ptr)send_string, (Ptr)0, + (Ptr)0, (short)command); + + if (send_string[0] != 0) { + /* check for valid device handler */ + switch (send_string[2]) { + case 0: + case 0xfd: + case 0xfe: + case 0xff: + continue; /* invalid, skip */ + } + + /* found a device */ + ++ADBNumDevices; + KASSERT(ADBNumDevices < 16); + ADBDevTable[ADBNumDevices].devType = + (int)send_string[2]; + ADBDevTable[ADBNumDevices].origAddr = i; + ADBDevTable[ADBNumDevices].currentAddr = i; + ADBDevTable[ADBNumDevices].DataAreaAddr = + (long)0; + ADBDevTable[ADBNumDevices].ServiceRtPtr = (void *)0; + pm_check_adb_devices(i); /* tell pm driver device + * is here */ + } + } + + /* find highest unused address */ + for (saveptr = 15; saveptr > 0; saveptr--) + if (-1 == get_adb_info(&data, saveptr)) + break; + +#ifdef ADB_DEBUG + if (adb_debug & 0x80) { + printf_intr("first free is: 0x%02x\n", saveptr); + printf_intr("devices: %i\n", ADBNumDevices); + } +#endif + + nonewtimes = 0; /* no loops w/o new devices */ + while (saveptr > 0 && nonewtimes++ < 11) { + for (i = 1; i <= ADBNumDevices; i++) { + device = ADBDevTable[i].currentAddr; +#ifdef ADB_DEBUG + if (adb_debug & 0x80) + printf_intr("moving device 0x%02x to 0x%02x " + "(index 0x%02x) ", device, saveptr, i); +#endif + + /* send TALK R3 to address */ + command = ADBTALK(device, 3); + adb_op_sync((Ptr)send_string, (Ptr)0, + (Ptr)0, (short)command); + + /* move device to higher address */ + command = ADBLISTEN(device, 3); + send_string[0] = 2; + send_string[1] = (u_char)(saveptr | 0x60); + send_string[2] = 0xfe; + adb_op_sync((Ptr)send_string, (Ptr)0, + (Ptr)0, (short)command); + delay(500); + + /* send TALK R3 - anything at new address? */ + command = ADBTALK(saveptr, 3); + adb_op_sync((Ptr)send_string, (Ptr)0, + (Ptr)0, (short)command); + delay(500); + + if (send_string[0] == 0) { +#ifdef ADB_DEBUG + if (adb_debug & 0x80) + printf_intr("failed, continuing\n"); +#endif + continue; + } + + /* send TALK R3 - anything at old address? */ + command = ADBTALK(device, 3); + result = adb_op_sync((Ptr)send_string, (Ptr)0, + (Ptr)0, (short)command); + if (send_string[0] != 0) { + /* check for valid device handler */ + switch (send_string[2]) { + case 0: + case 0xfd: + case 0xfe: + case 0xff: + continue; /* invalid, skip */ + } + + /* new device found */ + /* update data for previously moved device */ + ADBDevTable[i].currentAddr = saveptr; +#ifdef ADB_DEBUG + if (adb_debug & 0x80) + printf_intr("old device at index %i\n",i); +#endif + /* add new device in table */ +#ifdef ADB_DEBUG + if (adb_debug & 0x80) + printf_intr("new device found\n"); +#endif + if (saveptr > ADBNumDevices) { + ++ADBNumDevices; + KASSERT(ADBNumDevices < 16); + } + ADBDevTable[ADBNumDevices].devType = + (int)send_string[2]; + ADBDevTable[ADBNumDevices].origAddr = device; + ADBDevTable[ADBNumDevices].currentAddr = device; + /* These will be set correctly in adbsys.c */ + /* Until then, unsol. data will be ignored. */ + ADBDevTable[ADBNumDevices].DataAreaAddr = + (long)0; + ADBDevTable[ADBNumDevices].ServiceRtPtr = + (void *)0; + /* find next unused address */ + for (x = saveptr; x > 0; x--) { + if (-1 == get_adb_info(&data, x)) { + saveptr = x; + break; + } + } + if (x == 0) + saveptr = 0; +#ifdef ADB_DEBUG + if (adb_debug & 0x80) + printf_intr("new free is 0x%02x\n", + saveptr); +#endif + nonewtimes = 0; + /* tell pm driver device is here */ + pm_check_adb_devices(device); + } else { +#ifdef ADB_DEBUG + if (adb_debug & 0x80) + printf_intr("moving back...\n"); +#endif + /* move old device back */ + command = ADBLISTEN(saveptr, 3); + send_string[0] = 2; + send_string[1] = (u_char)(device | 0x60); + send_string[2] = 0xfe; + adb_op_sync((Ptr)send_string, (Ptr)0, + (Ptr)0, (short)command); + delay(1000); + } + } + } + +#ifdef ADB_DEBUG + if (adb_debug) { + for (i = 1; i <= ADBNumDevices; i++) { + x = get_ind_adb_info(&data, i); + if (x != -1) + printf_intr("index 0x%x, addr 0x%x, type 0x%x\n", + i, x, data.devType); + } + } +#endif + +#ifndef MRG_ADB + /* enable the programmer's switch, if we have one */ + adb_prog_switch_enable(); +#endif + +#ifdef ADB_DEBUG + if (adb_debug) { + if (0 == ADBNumDevices) /* tell user if no devices found */ + printf_intr("adb: no devices found\n"); + } +#endif + + adbStarting = 0; /* not starting anymore */ +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("adb: ADBReInit complete\n"); +#endif + + if (adbHardware == ADB_HW_CUDA) { + timeout_set(&adb_cuda_timeout, (void *)adb_cuda_tickle, NULL); + timeout_add(&adb_cuda_timeout, ADB_TICKLE_TICKS); + } + + if (adbHardware != ADB_HW_PB) /* ints must be on for PB? */ + splx(s); +} + + +#if 0 +/* + * adb_comp_exec + * This is a general routine that calls the completion routine if there is one. + * NOTE: This routine is now only used by pm_direct.c + * All the code in this file (adb_direct.c) uses + * the adb_pass_up routine now. + */ +void +adb_comp_exec(void) +{ + if ((long)0 != adbCompRout) /* don't call if empty return location */ +#ifdef __NetBSD__ + asm(" movml #0xffff,sp@- | save all registers + movl %0,a2 | adbCompData + movl %1,a1 | adbCompRout + movl %2,a0 | adbBuffer + movl %3,d0 | adbWaitingCmd + jbsr a1@ | go call the routine + movml sp@+,#0xffff | restore all registers" + : + : "g"(adbCompData), "g"(adbCompRout), + "g"(adbBuffer), "g"(adbWaitingCmd) + : "d0", "a0", "a1", "a2"); +#else /* for Mac OS-based testing */ + asm { + movem.l a0/a1/a2/d0, -(a7) + move.l adbCompData, a2 + move.l adbCompRout, a1 + move.l adbBuffer, a0 + move.w adbWaitingCmd, d0 + jsr(a1) + movem.l(a7) +, d0/a2/a1/a0 + } +#endif +} +#endif + + +/* + * adb_cmd_result + * + * This routine lets the caller know whether the specified adb command string + * should expect a returned result, such as a TALK command. + * + * returns: 0 if a result should be expected + * 1 if a result should NOT be expected + */ +int +adb_cmd_result(u_char *in) +{ + switch (adbHardware) { + case ADB_HW_II: + /* was it an ADB talk command? */ + if ((in[1] & 0x0c) == 0x0c) + return 0; + return 1; + + case ADB_HW_IISI: + case ADB_HW_CUDA: + /* was it an ADB talk command? */ + if ((in[1] == 0x00) && ((in[2] & 0x0c) == 0x0c)) + return 0; + /* was it an RTC/PRAM read date/time? */ + if ((in[1] == 0x01) && (in[2] == 0x03)) + return 0; + return 1; + + case ADB_HW_PB: + return 1; + + case ADB_HW_UNKNOWN: + default: + return 1; + } +} + + +/* + * adb_cmd_extra + * + * This routine lets the caller know whether the specified adb command string + * may have extra data appended to the end of it, such as a LISTEN command. + * + * returns: 0 if extra data is allowed + * 1 if extra data is NOT allowed + */ +int +adb_cmd_extra(u_char *in) +{ + switch (adbHardware) { + case ADB_HW_II: + if ((in[1] & 0x0c) == 0x08) /* was it a listen command? */ + return 0; + return 1; + + case ADB_HW_IISI: + case ADB_HW_CUDA: + /* + * TO DO: support needs to be added to recognize RTC and PRAM + * commands + */ + if ((in[2] & 0x0c) == 0x08) /* was it a listen command? */ + return 0; + /* add others later */ + return 1; + + case ADB_HW_PB: + return 1; + + case ADB_HW_UNKNOWN: + default: + return 1; + } +} + +/* + * adb_op_sync + * + * This routine does exactly what the adb_op routine does, except that after + * the adb_op is called, it waits until the return value is present before + * returning. + * + * NOTE: The user specified compRout is ignored, since this routine specifies + * it's own to adb_op, which is why you really called this in the first place + * anyway. + */ +int +adb_op_sync(Ptr buffer, Ptr compRout, Ptr data, short command) +{ + int tmout; + int result; + volatile int flag = 0; + + result = adb_op(buffer, (void *)adb_op_comprout, + (void *)&flag, command); /* send command */ + if (result == 0) { /* send ok? */ + /* + * Total time to wait is calculated as follows: + * - Tlt (stop to start time): 260 usec + * - start bit: 100 usec + * - up to 8 data bytes: 64 * 100 usec = 6400 usec + * - stop bit (with SRQ): 140 usec + * Total: 6900 usec + * + * This is the total time allowed by the specification. Any + * device that doesn't conform to this will fail to operate + * properly on some Apple systems. In spite of this we + * double the time to wait; some Cuda-based apparently + * queues some commands and allows the main CPU to continue + * processing (radical concept, eh?). To be safe, allow + * time for two complete ADB transactions to occur. + */ + for (tmout = 13800; !flag && tmout >= 10; tmout -= 10) + delay(10); + if (!flag && tmout > 0) + delay(tmout); + + if (!flag) + result = -2; + } + + return result; +} + + +/* + * adb_op_comprout + * + * This function is used by the adb_op_sync routine so it knows when the + * function is done. + */ +void +adb_op_comprout(buffer, compdata, cmd) + caddr_t buffer, compdata; + int cmd; +{ + short *p = (short *)compdata; + + *p = 1; +} + +void +adb_setup_hw_type(void) +{ + switch (adbHardware) { + case ADB_HW_CUDA: + adbSoftPower = 1; + return; + + case ADB_HW_PB: + adbSoftPower = 1; + pm_setup_adb(); + return; + + default: + panic("unknown adb hardware"); + } +#if 0 + response = 0; /*mac68k_machine.machineid;*/ + + /* + * Determine what type of ADB hardware we are running on. + */ + switch (response) { + 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 */ + adbHardware = ADB_HW_II; +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("adb: using II series hardware support\n"); +#endif + break; + + case MACH_MACCLASSICII: /* Classic II */ + case MACH_MACLCII: /* LC II, Performa 400/405/430 */ + case MACH_MACLCIII: /* LC III, Performa 450 */ + case MACH_MACIISI: /* IIsi */ + case MACH_MACIIVI: /* IIvi */ + case MACH_MACIIVX: /* IIvx */ + case MACH_MACP460: /* Performa 460/465/467 */ + case MACH_MACP600: /* Performa 600 */ + adbHardware = ADB_HW_IISI; +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("adb: using IIsi series hardware support\n"); +#endif + break; + + case MACH_MACPB140: /* PowerBook 140 */ + case MACH_MACPB145: /* PowerBook 145 */ + case MACH_MACPB150: /* PowerBook 150 */ + case MACH_MACPB160: /* PowerBook 160 */ + case MACH_MACPB165: /* PowerBook 165 */ + case MACH_MACPB165C: /* PowerBook 165c */ + case MACH_MACPB170: /* PowerBook 170 */ + case MACH_MACPB180: /* PowerBook 180 */ + case MACH_MACPB180C: /* PowerBook 180c */ + adbHardware = ADB_HW_PB; + pm_setup_adb(); +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("adb: using PowerBook 100-series hardware support\n"); +#endif + break; + + case MACH_MACPB210: /* PowerBook Duo 210 */ + case MACH_MACPB230: /* PowerBook Duo 230 */ + case MACH_MACPB250: /* PowerBook Duo 250 */ + case MACH_MACPB270: /* PowerBook Duo 270 */ + case MACH_MACPB280: /* PowerBook Duo 280 */ + case MACH_MACPB280C: /* PowerBook Duo 280c */ + case MACH_MACPB500: /* PowerBook 500 series */ + adbHardware = ADB_HW_PB; + pm_setup_adb(); +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("adb: using PowerBook Duo-series and PowerBook 500-series hardware support\n"); +#endif + break; + + case MACH_MACC660AV: /* Centris 660AV */ + case MACH_MACCCLASSIC: /* Color Classic */ + case MACH_MACCCLASSICII: /* Color Classic II */ + case MACH_MACLC475: /* LC 475, Performa 475/476 */ + case MACH_MACLC475_33: /* Clock-chipped 47x */ + case MACH_MACLC520: /* LC 520 */ + case MACH_MACLC575: /* LC 575, Performa 575/577/578 */ + case MACH_MACP550: /* LC 550, Performa 550 */ + case MACH_MACP580: /* Performa 580/588 */ + case MACH_MACQ605: /* Quadra 605 */ + case MACH_MACQ605_33: /* Clock-chipped Quadra 605 */ + case MACH_MACQ630: /* LC 630, Performa 630, Quadra 630 */ + case MACH_MACQ840AV: /* Quadra 840AV */ + adbHardware = ADB_HW_CUDA; +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("adb: using Cuda series hardware support\n"); +#endif + break; + default: + adbHardware = ADB_HW_UNKNOWN; +#ifdef ADB_DEBUG + if (adb_debug) { + printf_intr("adb: hardware type unknown for this machine\n"); + printf_intr("adb: ADB support is disabled\n"); + } +#endif + break; + } + + /* + * Determine whether this machine has ADB based soft power. + */ + switch (response) { + case MACH_MACCCLASSIC: /* Color Classic */ + case MACH_MACCCLASSICII: /* Color Classic II */ + case MACH_MACIISI: /* IIsi */ + case MACH_MACIIVI: /* IIvi */ + case MACH_MACIIVX: /* IIvx */ + case MACH_MACLC520: /* LC 520 */ + case MACH_MACLC575: /* LC 575, Performa 575/577/578 */ + case MACH_MACP550: /* LC 550, Performa 550 */ + case MACH_MACP600: /* Performa 600 */ + case MACH_MACQ630: /* LC 630, Performa 630, Quadra 630 */ + case MACH_MACQ840AV: /* Quadra 840AV */ + adbSoftPower = 1; + break; + } +#endif +} + +int +count_adbs(void) +{ + int i; + int found; + + found = 0; + + for (i = 1; i < 16; i++) + if (0 != ADBDevTable[i].devType) + found++; + + return found; +} + +int +get_ind_adb_info(ADBDataBlock * info, int index) +{ + if ((index < 1) || (index > 15)) /* check range 1-15 */ + return (-1); + +#ifdef ADB_DEBUG + if (adb_debug & 0x80) + printf_intr("index 0x%x devType is: 0x%x\n", index, + ADBDevTable[index].devType); +#endif + if (0 == ADBDevTable[index].devType) /* make sure it's a valid entry */ + return (-1); + + info->devType = ADBDevTable[index].devType; + info->origADBAddr = ADBDevTable[index].origAddr; + info->dbServiceRtPtr = (Ptr)ADBDevTable[index].ServiceRtPtr; + info->dbDataAreaAddr = (Ptr)ADBDevTable[index].DataAreaAddr; + + return (ADBDevTable[index].currentAddr); +} + +int +get_adb_info(ADBDataBlock * info, int adbAddr) +{ + int i; + + if ((adbAddr < 1) || (adbAddr > 15)) /* check range 1-15 */ + return (-1); + + for (i = 1; i < 15; i++) + if (ADBDevTable[i].currentAddr == adbAddr) { + info->devType = ADBDevTable[i].devType; + info->origADBAddr = ADBDevTable[i].origAddr; + info->dbServiceRtPtr = (Ptr)ADBDevTable[i].ServiceRtPtr; + info->dbDataAreaAddr = ADBDevTable[i].DataAreaAddr; + return 0; /* found */ + } + + return (-1); /* not found */ +} + +int +set_adb_info(ADBSetInfoBlock * info, int adbAddr) +{ + int i; + + if ((adbAddr < 1) || (adbAddr > 15)) /* check range 1-15 */ + return (-1); + + for (i = 1; i < 15; i++) + if (ADBDevTable[i].currentAddr == adbAddr) { + ADBDevTable[i].ServiceRtPtr = + (void *)(info->siServiceRtPtr); + ADBDevTable[i].DataAreaAddr = info->siDataAreaAddr; + return 0; /* found */ + } + + return (-1); /* not found */ + +} + +#ifndef MRG_ADB + +/* caller should really use machine-independant version: getPramTime */ +/* this version does pseudo-adb access only */ +int +adb_read_date_time(unsigned long *time) +{ + u_char output[ADB_MAX_MSG_LENGTH]; + int result; + int retcode; + volatile int flag = 0; + + switch (adbHardware) { + case ADB_HW_II: + retcode = -1; + break; + + case ADB_HW_IISI: + output[0] = 0x02; /* 2 byte message */ + output[1] = 0x01; /* to pram/rtc device */ + output[2] = 0x03; /* read date/time */ + result = send_adb_IIsi((u_char *)output, (u_char *)output, + (void *)adb_op_comprout, (int *)&flag, (int)0); + if (result != 0) { /* exit if not sent */ + retcode = -1; + break; + } + + while (0 == flag) /* wait for result */ + ; + + *time = (long)(*(long *)(output + 1)); + retcode = 0; + break; + + case ADB_HW_PB: + pm_read_date_time(time); + retcode = 0; + break; + + case ADB_HW_CUDA: + output[0] = 0x02; /* 2 byte message */ + output[1] = 0x01; /* to pram/rtc device */ + output[2] = 0x03; /* read date/time */ + result = send_adb_cuda((u_char *)output, (u_char *)output, + (void *)adb_op_comprout, (void *)&flag, (int)0); + if (result != 0) { /* exit if not sent */ + retcode = -1; + break; + } + + while (0 == flag) /* wait for result */ + ; + + delay(20); /* completion occurs too soon? */ + memcpy(time, output + 1, 4); + retcode = 0; + break; + + case ADB_HW_UNKNOWN: + default: + retcode = -1; + break; + } + if (retcode == 0) { +#define DIFF19041970 2082844800 + *time -= DIFF19041970; + + } else { + *time = 0; + } + return retcode; +} + +/* caller should really use machine-independant version: setPramTime */ +/* this version does pseudo-adb access only */ +int +adb_set_date_time(unsigned long time) +{ + u_char output[ADB_MAX_MSG_LENGTH]; + int result; + volatile int flag = 0; + + time += DIFF19041970; + switch (adbHardware) { + + case ADB_HW_CUDA: + output[0] = 0x06; /* 6 byte message */ + output[1] = 0x01; /* to pram/rtc device */ + output[2] = 0x09; /* set date/time */ + output[3] = (u_char)(time >> 24); + output[4] = (u_char)(time >> 16); + output[5] = (u_char)(time >> 8); + output[6] = (u_char)(time); + result = send_adb_cuda((u_char *)output, (u_char *)0, + (void *)adb_op_comprout, (void *)&flag, (int)0); + if (result != 0) /* exit if not sent */ + return -1; + + while (0 == flag) /* wait for send to finish */ + ; + + return 0; + + case ADB_HW_PB: + pm_set_date_time(time); + return 0; + + case ADB_HW_II: + case ADB_HW_IISI: + case ADB_HW_UNKNOWN: + default: + return -1; + } +} + + +int +adb_poweroff(void) +{ + u_char output[ADB_MAX_MSG_LENGTH]; + int result; + + if (!adbSoftPower) + return -1; + + adb_polling = 1; + + switch (adbHardware) { + case ADB_HW_IISI: + output[0] = 0x02; /* 2 byte message */ + output[1] = 0x01; /* to pram/rtc/soft-power device */ + output[2] = 0x0a; /* set date/time */ + result = send_adb_IIsi((u_char *)output, (u_char *)0, + (void *)0, (void *)0, (int)0); + if (result != 0) /* exit if not sent */ + return -1; + + for (;;); /* wait for power off */ + + return 0; + + case ADB_HW_PB: + pm_adb_poweroff(); + + for (;;); /* wait for power off */ + + return 0; + + case ADB_HW_CUDA: + output[0] = 0x02; /* 2 byte message */ + output[1] = 0x01; /* to pram/rtc/soft-power device */ + output[2] = 0x0a; /* set date/time */ + result = send_adb_cuda((u_char *)output, (u_char *)0, + (void *)0, (void *)0, (int)0); + if (result != 0) /* exit if not sent */ + return -1; + + for (;;); /* wait for power off */ + + return 0; + + case ADB_HW_II: /* II models don't do ADB soft power */ + case ADB_HW_UNKNOWN: + default: + return -1; + } +} + +int +adb_prog_switch_enable(void) +{ + u_char output[ADB_MAX_MSG_LENGTH]; + int result; + volatile int flag = 0; + + switch (adbHardware) { + case ADB_HW_IISI: + output[0] = 0x03; /* 3 byte message */ + output[1] = 0x01; /* to pram/rtc/soft-power device */ + output[2] = 0x1c; /* prog. switch control */ + output[3] = 0x01; /* enable */ + result = send_adb_IIsi((u_char *)output, (u_char *)0, + (void *)adb_op_comprout, (void *)&flag, (int)0); + if (result != 0) /* exit if not sent */ + return -1; + + while (0 == flag) /* wait for send to finish */ + ; + + return 0; + + case ADB_HW_PB: + return -1; + + case ADB_HW_II: /* II models don't do prog. switch */ + case ADB_HW_CUDA: /* cuda doesn't do prog. switch TO DO: verify this */ + case ADB_HW_UNKNOWN: + default: + return -1; + } +} + +int +adb_prog_switch_disable(void) +{ + u_char output[ADB_MAX_MSG_LENGTH]; + int result; + volatile int flag = 0; + + switch (adbHardware) { + case ADB_HW_IISI: + output[0] = 0x03; /* 3 byte message */ + output[1] = 0x01; /* to pram/rtc/soft-power device */ + output[2] = 0x1c; /* prog. switch control */ + output[3] = 0x01; /* disable */ + result = send_adb_IIsi((u_char *)output, (u_char *)0, + (void *)adb_op_comprout, (void *)&flag, (int)0); + if (result != 0) /* exit if not sent */ + return -1; + + while (0 == flag) /* wait for send to finish */ + ; + + return 0; + + case ADB_HW_PB: + return -1; + + case ADB_HW_II: /* II models don't do prog. switch */ + case ADB_HW_CUDA: /* cuda doesn't do prog. switch */ + case ADB_HW_UNKNOWN: + default: + return -1; + } +} + +int +CountADBs(void) +{ + return (count_adbs()); +} + +void +ADBReInit(void) +{ + adb_reinit(); +} + +int +GetIndADB(ADBDataBlock * info, int index) +{ + return (get_ind_adb_info(info, index)); +} + +int +GetADBInfo(ADBDataBlock * info, int adbAddr) +{ + return (get_adb_info(info, adbAddr)); +} + +int +SetADBInfo(ADBSetInfoBlock * info, int adbAddr) +{ + return (set_adb_info(info, adbAddr)); +} + +int +ADBOp(Ptr buffer, Ptr compRout, Ptr data, short commandNum) +{ + return (adb_op(buffer, compRout, data, commandNum)); +} + +#endif + +int +setsoftadb() +{ + if (!timeout_initialized(&adb_softintr_timeout)) + timeout_set(&adb_softintr_timeout, (void *)adb_soft_intr, NULL); + timeout_add(&adb_softintr_timeout, 1); + return 0; +} + +void +adb_cuda_autopoll() +{ + volatile int flag = 0; + int result; + u_char output[16]; + + output[0] = 0x03; /* 3-byte message */ + output[1] = 0x01; /* to pram/rtc device */ + output[2] = 0x01; /* cuda autopoll */ + output[3] = 0x01; + result = send_adb_cuda(output, output, adb_op_comprout, + (void *)&flag, 0); + if (result != 0) /* exit if not sent */ + return; + + while (flag == 0); /* wait for result */ +} + +void +adb_restart() +{ + int result; + u_char output[16]; + + adb_polling = 1; + + switch (adbHardware) { + case ADB_HW_CUDA: + output[0] = 0x02; /* 2 byte message */ + output[1] = 0x01; /* to pram/rtc/soft-power device */ + output[2] = 0x11; /* restart */ + result = send_adb_cuda((u_char *)output, (u_char *)0, + (void *)0, (void *)0, (int)0); + if (result != 0) /* exit if not sent */ + return; + while (1); /* not return */ + + case ADB_HW_PB: + pm_adb_restart(); + while (1); /* not return */ + } +} diff --git a/sys/arch/macppc/dev/adb_direct.h b/sys/arch/macppc/dev/adb_direct.h new file mode 100644 index 00000000000..007d2fc9d61 --- /dev/null +++ b/sys/arch/macppc/dev/adb_direct.h @@ -0,0 +1,55 @@ +/* $OpenBSD: adb_direct.h,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: adb_direct.h,v 1.1 1998/05/15 10:15:47 tsubai Exp $ */ + +/* + * Copyright (C) 1996 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. + */ +/* From: adb_direct.h 1.4 10/23/96 jpw */ + +/* + * These are public declarations that other routines may need. + */ + +/* types of adb hardware that we (will eventually) support */ +#define ADB_HW_UNKNOWN 0x01 /* don't know */ +#define ADB_HW_II 0x02 /* Mac II series */ +#define ADB_HW_IISI 0x03 /* Mac IIsi series */ +#define ADB_HW_PB 0x04 /* PowerBook series */ +#define ADB_HW_CUDA 0x05 /* Machines with a Cuda chip */ + +int adb_poweroff __P((void)); +int CountADBs __P((void)); +void ADBReInit __P((void)); +int GetIndADB __P((ADBDataBlock *info, int index)); +int GetADBInfo __P((ADBDataBlock *info, int adbAddr)); +int SetADBInfo __P((ADBSetInfoBlock *info, int adbAddr)); +int ADBOp __P((Ptr buffer, Ptr compRout, Ptr data, short commandNum)); +int adb_read_date_time __P((unsigned long *)); +int adb_set_date_time __P((unsigned long)); +int adb_op_sync __P((Ptr, Ptr, Ptr, short)); diff --git a/sys/arch/macppc/dev/adbvar.h b/sys/arch/macppc/dev/adbvar.h new file mode 100644 index 00000000000..04bd6dc9f70 --- /dev/null +++ b/sys/arch/macppc/dev/adbvar.h @@ -0,0 +1,132 @@ +/* $OpenBSD: adbvar.h,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: adbvar.h,v 1.3 2000/06/08 22:10:46 tsubai Exp $ */ + +/*- + * Copyright (C) 1994 Bradley A. Grantham + * 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 Bradley A. Grantham. + * 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 <machine/adbsys.h> + +/* + * Arguments used to attach a device to the Apple Desktop Bus + */ +struct adb_attach_args { + int origaddr; + int adbaddr; + int handler_id; +}; + +#define ADB_MAXTRACE (NBPG / sizeof(int) - 1) +extern int adb_traceq[ADB_MAXTRACE]; +extern int adb_traceq_tail; +extern int adb_traceq_len; + +typedef struct adb_trace_xlate_s { + int params; + char *string; +} adb_trace_xlate_t; + +extern adb_trace_xlate_t adb_trace_xlations[]; + +#ifdef DEBUG +#ifndef ADB_DEBUG +#define ADB_DEBUG +#endif +#endif + +#ifdef ADB_DEBUG +extern int adb_debug; +#endif + +typedef caddr_t Ptr; +typedef caddr_t *Handle; + +/* ADB Manager */ +typedef struct { + Ptr siServiceRtPtr; + Ptr siDataAreaAddr; +} ADBSetInfoBlock; +typedef struct { + unsigned char devType; + unsigned char origADBAddr; + Ptr dbServiceRtPtr; + Ptr dbDataAreaAddr; +} ADBDataBlock; + +struct adb_softc { + struct device sc_dev; + char *sc_regbase; +}; + + +/* adb.c */ +void adb_enqevent __P((adb_event_t *event)); +void adb_handoff __P((adb_event_t *event)); +void adb_autorepeat __P((void *keyp)); +void adb_dokeyupdown __P((adb_event_t *event)); +void adb_keymaybemouse __P((adb_event_t *event)); +void adb_processevent __P((adb_event_t *event)); +int adbopen __P((dev_t dev, int flag, int mode, struct proc *p)); +int adbclose __P((dev_t dev, int flag, int mode, struct proc *p)); +int adbread __P((dev_t dev, struct uio *uio, int flag)); +int adbwrite __P((dev_t dev, struct uio *uio, int flag)); +int adbioctl __P((dev_t , int , caddr_t , int , struct proc *)); +int adbpoll __P((dev_t dev, int events, struct proc *p)); + +/* adbsys.c */ +void adb_complete __P((caddr_t buffer, caddr_t data_area, int adb_command)); +void adb_msa3_complete __P((caddr_t buffer, caddr_t data_area, int adb_command)); +void adb_mm_nonemp_complete __P((caddr_t buffer, caddr_t data_area, int adb_command)); +void extdms_init __P((int)); +void extdms_complete __P((caddr_t, caddr_t, int)); + +/* types of adb hardware that we (will eventually) support */ +#define ADB_HW_UNKNOWN 0x01 /* don't know */ +#define ADB_HW_II 0x02 /* Mac II series */ +#define ADB_HW_IISI 0x03 /* Mac IIsi series */ +#define ADB_HW_PB 0x04 /* PowerBook series */ +#define ADB_HW_CUDA 0x05 /* Machines with a Cuda chip */ + +#define ADB_CMDADDR(cmd) ((u_int8_t)((cmd) & 0xf0) >> 4) +#define ADBFLUSH(dev) ((((u_int8_t)(dev) & 0x0f) << 4) | 0x01) +#define ADBLISTEN(dev, reg) ((((u_int8_t)(dev) & 0x0f) << 4) | 0x08 | (reg)) +#define ADBTALK(dev, reg) ((((u_int8_t)(dev) & 0x0f) << 4) | 0x0c | (reg)) + +/* adb_direct.c */ +int adb_poweroff __P((void)); +int CountADBs __P((void)); +void ADBReInit __P((void)); +int GetIndADB __P((ADBDataBlock * info, int index)); +int GetADBInfo __P((ADBDataBlock * info, int adbAddr)); +int SetADBInfo __P((ADBSetInfoBlock * info, int adbAddr)); +int ADBOp __P((Ptr buffer, Ptr compRout, Ptr data, short commandNum)); +int adb_read_date_time __P((unsigned long *t)); +int adb_set_date_time __P((unsigned long t)); +int adb_intr __P((void *arg)); +void adb_cuda_autopoll __P((void)); diff --git a/sys/arch/macppc/dev/aed.c b/sys/arch/macppc/dev/aed.c new file mode 100644 index 00000000000..12e2268de86 --- /dev/null +++ b/sys/arch/macppc/dev/aed.c @@ -0,0 +1,617 @@ +/* $OpenBSD: aed.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: aed.c,v 1.5 2000/03/23 06:40:33 thorpej Exp $ */ + +/* + * Copyright (C) 1994 Bradley A. Grantham + * 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 Bradley A. Grantham. + * 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 <sys/param.h> +#include <sys/device.h> +#include <sys/fcntl.h> +#include <sys/poll.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/signalvar.h> +#include <sys/systm.h> + +#include <machine/autoconf.h> +#include <machine/cpu.h> + +#include <macppc/mac/keyboard.h> +#include <macppc/mac/adbvar.h> +#include <macppc/mac/aedvar.h> +#include <macppc/mac/akbdvar.h> + +#define spladb splhigh + +/* + * Function declarations. + */ +#ifdef __NetBSD__ +static int aedmatch __P((struct device *, struct cfdata *, void *)); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ +static int aedmatch __P((struct device *, void *, void *)); +#endif /* __OpenBSD__ */ +static void aedattach __P((struct device *, struct device *, void *)); +static void aed_emulate_mouse __P((adb_event_t *event)); +static void aed_kbdrpt __P((void *kstate)); +static void aed_dokeyupdown __P((adb_event_t *event)); +static void aed_handoff __P((adb_event_t *event)); +static void aed_enqevent __P((adb_event_t *event)); + +/* + * Global variables. + */ +extern int adb_polling; /* Are we polling? (Debugger mode) */ + +/* + * Local variables. + */ +static struct aed_softc *aed_sc = NULL; +static int aed_options = 0; /* | AED_MSEMUL; */ + +/* Driver definition */ +struct cfdriver aed_cd = { + NULL, "aed", DV_DULL +}; +/* Driver definition */ +struct cfattach aed_ca = { + sizeof(struct aed_softc), aedmatch, aedattach +}; + +extern struct cfdriver aed_cd; + +static int +aedmatch(parent, cf, aux) + struct device *parent; +#ifdef __NetBSD__ + struct cfdata *cf; +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + void *cf; +#endif /* __OpenBSD__ */ + void *aux; +{ + struct adb_attach_args *aa_args = (struct adb_attach_args *)aux; + static int aed_matched = 0; + + /* Allow only one instance. */ + if ((aa_args->origaddr == 0) && (!aed_matched)) { + aed_matched = 1; + return (1); + } else + return (0); +} + +static void +aedattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct adb_attach_args *aa_args = (struct adb_attach_args *)aux; + struct aed_softc *sc = (struct aed_softc *)self; + + aed_sc = sc; + + timeout_set(&sc->sc_repeat_ch, aed_kbdrpt, aed_sc); + + sc->origaddr = aa_args->origaddr; + sc->adbaddr = aa_args->adbaddr; + sc->handler_id = aa_args->handler_id; + + sc->sc_evq_tail = 0; + sc->sc_evq_len = 0; + + sc->sc_rptdelay = 20; + sc->sc_rptinterval = 6; + sc->sc_repeating = -1; /* not repeating */ + + /* Pull in the options flags. */ + sc->sc_options = (sc->sc_dev.dv_cfdata->cf_flags | aed_options); + + sc->sc_ioproc = NULL; + + sc->sc_buttons = 0; + + sc->sc_open = 0; + + + printf("ADB Event device\n"); + + return; +} + +/* + * Given a keyboard ADB event, record the keycode and call the key + * repeat handler, optionally passing the event through the mouse + * button emulation handler first. Pass mouse events directly to + * the handoff function. + */ +void +aed_input(event) + adb_event_t *event; +{ + adb_event_t new_event = *event; + + switch (event->def_addr) { + case ADBADDR_KBD: + if (aed_sc->sc_options & AED_MSEMUL) + aed_emulate_mouse(&new_event); + else + aed_dokeyupdown(&new_event); + break; + case ADBADDR_MS: + new_event.u.m.buttons |= aed_sc->sc_buttons; + aed_handoff(&new_event); + break; + default: /* God only knows. */ +#ifdef DIAGNOSTIC + panic("aed: received event from unsupported device!\n"); +#endif + break; + } + +} + +/* + * Handles mouse button emulation via the keyboard. If the emulation + * modifier key is down, left and right arrows will generate 2nd and + * 3rd mouse button events while the 1, 2, and 3 keys will generate + * the corresponding mouse button event. + */ +static void +aed_emulate_mouse(event) + adb_event_t *event; +{ + static int emulmodkey_down = 0; + adb_event_t new_event; + + if (event->u.k.key == ADBK_KEYDOWN(ADBK_OPTION)) { + emulmodkey_down = 1; + } else if (event->u.k.key == ADBK_KEYUP(ADBK_OPTION)) { + /* key up */ + emulmodkey_down = 0; + if (aed_sc->sc_buttons & 0xfe) { + aed_sc->sc_buttons &= 1; + new_event.def_addr = ADBADDR_MS; + new_event.u.m.buttons = aed_sc->sc_buttons; + new_event.u.m.dx = new_event.u.m.dy = 0; + microtime(&new_event.timestamp); + aed_handoff(&new_event); + } + } else if (emulmodkey_down) { + switch(event->u.k.key) { +#ifdef ALTXBUTTONS + case ADBK_KEYDOWN(ADBK_1): + aed_sc->sc_buttons |= 1; /* left down */ + new_event.def_addr = ADBADDR_MS; + new_event.u.m.buttons = aed_sc->sc_buttons; + new_event.u.m.dx = new_event.u.m.dy = 0; + microtime(&new_event.timestamp); + aed_handoff(&new_event); + break; + case ADBK_KEYUP(ADBK_1): + aed_sc->sc_buttons &= ~1; /* left up */ + new_event.def_addr = ADBADDR_MS; + new_event.u.m.buttons = aed_sc->sc_buttons; + new_event.u.m.dx = new_event.u.m.dy = 0; + microtime(&new_event.timestamp); + aed_handoff(&new_event); + break; +#endif + case ADBK_KEYDOWN(ADBK_LEFT): +#ifdef ALTXBUTTONS + case ADBK_KEYDOWN(ADBK_2): +#endif + aed_sc->sc_buttons |= 2; /* middle down */ + new_event.def_addr = ADBADDR_MS; + new_event.u.m.buttons = aed_sc->sc_buttons; + new_event.u.m.dx = new_event.u.m.dy = 0; + microtime(&new_event.timestamp); + aed_handoff(&new_event); + break; + case ADBK_KEYUP(ADBK_LEFT): +#ifdef ALTXBUTTONS + case ADBK_KEYUP(ADBK_2): +#endif + aed_sc->sc_buttons &= ~2; /* middle up */ + new_event.def_addr = ADBADDR_MS; + new_event.u.m.buttons = aed_sc->sc_buttons; + new_event.u.m.dx = new_event.u.m.dy = 0; + microtime(&new_event.timestamp); + aed_handoff(&new_event); + break; + case ADBK_KEYDOWN(ADBK_RIGHT): +#ifdef ALTXBUTTONS + case ADBK_KEYDOWN(ADBK_3): +#endif + aed_sc->sc_buttons |= 4; /* right down */ + new_event.def_addr = ADBADDR_MS; + new_event.u.m.buttons = aed_sc->sc_buttons; + new_event.u.m.dx = new_event.u.m.dy = 0; + microtime(&new_event.timestamp); + aed_handoff(&new_event); + break; + case ADBK_KEYUP(ADBK_RIGHT): +#ifdef ALTXBUTTONS + case ADBK_KEYUP(ADBK_3): +#endif + aed_sc->sc_buttons &= ~4; /* right up */ + new_event.def_addr = ADBADDR_MS; + new_event.u.m.buttons = aed_sc->sc_buttons; + new_event.u.m.dx = new_event.u.m.dy = 0; + microtime(&new_event.timestamp); + aed_handoff(&new_event); + break; + case ADBK_KEYUP(ADBK_SHIFT): + case ADBK_KEYDOWN(ADBK_SHIFT): + case ADBK_KEYUP(ADBK_CONTROL): + case ADBK_KEYDOWN(ADBK_CONTROL): + case ADBK_KEYUP(ADBK_FLOWER): + case ADBK_KEYDOWN(ADBK_FLOWER): + /* ctrl, shift, cmd */ + aed_dokeyupdown(event); + break; + default: + if (event->u.k.key & 0x80) + /* ignore keyup */ + break; + + /* key down */ + new_event = *event; + + /* send option-down */ + new_event.u.k.key = ADBK_KEYDOWN(ADBK_OPTION); + new_event.bytes[0] = new_event.u.k.key; + microtime(&new_event.timestamp); + aed_dokeyupdown(&new_event); + + /* send key-down */ + new_event.u.k.key = event->bytes[0]; + new_event.bytes[0] = new_event.u.k.key; + microtime(&new_event.timestamp); + aed_dokeyupdown(&new_event); + + /* send key-up */ + new_event.u.k.key = + ADBK_KEYUP(ADBK_KEYVAL(event->bytes[0])); + microtime(&new_event.timestamp); + new_event.bytes[0] = new_event.u.k.key; + aed_dokeyupdown(&new_event); + + /* send option-up */ + new_event.u.k.key = ADBK_KEYUP(ADBK_OPTION); + new_event.bytes[0] = new_event.u.k.key; + microtime(&new_event.timestamp); + aed_dokeyupdown(&new_event); + break; + } + } else { + aed_dokeyupdown(event); + } +} + +/* + * Keyboard autorepeat timeout function. Sends key up/down events + * for the repeating key and schedules the next call at sc_rptinterval + * ticks in the future. + */ +static void +aed_kbdrpt(kstate) + void *kstate; +{ + struct aed_softc *aed_sc = (struct aed_softc *)kstate; + + aed_sc->sc_rptevent.bytes[0] |= 0x80; + microtime(&aed_sc->sc_rptevent.timestamp); + aed_handoff(&aed_sc->sc_rptevent); /* do key up */ + + aed_sc->sc_rptevent.bytes[0] &= 0x7f; + microtime(&aed_sc->sc_rptevent.timestamp); + aed_handoff(&aed_sc->sc_rptevent); /* do key down */ + + if (aed_sc->sc_repeating == aed_sc->sc_rptevent.u.k.key) { + timeout_add(&aed_sc->sc_repeat_ch, aed_sc->sc_rptinterval); + } +} + + +/* + * Cancels the currently repeating key event if there is one, schedules + * a new repeating key event if needed, and hands the event off to the + * appropriate subsystem. + */ +static void +aed_dokeyupdown(event) + adb_event_t *event; +{ + int kbd_key; + + kbd_key = ADBK_KEYVAL(event->u.k.key); + if (ADBK_PRESS(event->u.k.key) && keyboard[kbd_key][0] != 0) { + /* ignore shift & control */ + if (aed_sc->sc_repeating != -1) { + timeout_del(&aed_sc->sc_repeat_ch); + } + aed_sc->sc_rptevent = *event; + aed_sc->sc_repeating = kbd_key; + timeout_add(&aed_sc->sc_repeat_ch, aed_sc->sc_rptdelay); + } else { + if (aed_sc->sc_repeating != -1) { + aed_sc->sc_repeating = -1; + timeout_del(&aed_sc->sc_repeat_ch); + } + aed_sc->sc_rptevent = *event; + } + aed_handoff(event); +} + +/* + * Place the event in the event queue if a requesting device is open + * and we are not polling. + */ +static void +aed_handoff(event) + adb_event_t *event; +{ + if (aed_sc->sc_open && !adb_polling) + aed_enqevent(event); +} + +/* + * Place the event in the event queue and wakeup any waiting processes. + */ +static void +aed_enqevent(event) + adb_event_t *event; +{ + int s; + + s = spladb(); + +#ifdef DIAGNOSTIC + if (aed_sc->sc_evq_tail < 0 || aed_sc->sc_evq_tail >= AED_MAX_EVENTS) + panic("adb: event queue tail is out of bounds"); + + if (aed_sc->sc_evq_len < 0 || aed_sc->sc_evq_len > AED_MAX_EVENTS) + panic("adb: event queue len is out of bounds"); +#endif + + if (aed_sc->sc_evq_len == AED_MAX_EVENTS) { + splx(s); + return; /* Oh, well... */ + } + aed_sc->sc_evq[(aed_sc->sc_evq_len + aed_sc->sc_evq_tail) % + AED_MAX_EVENTS] = *event; + aed_sc->sc_evq_len++; + + selwakeup(&aed_sc->sc_selinfo); + if (aed_sc->sc_ioproc) + psignal(aed_sc->sc_ioproc, SIGIO); + + splx(s); +} + +int +aedopen(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + int unit; + int error = 0; + int s; + + unit = minor(dev); + + if (unit != 0) + return (ENXIO); + + s = spladb(); + if (aed_sc->sc_open) { + splx(s); + return (EBUSY); + } + aed_sc->sc_evq_tail = 0; + aed_sc->sc_evq_len = 0; + aed_sc->sc_open = 1; + aed_sc->sc_ioproc = p; + splx(s); + + return (error); +} + + +int +aedclose(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + int s = spladb(); + + aed_sc->sc_open = 0; + aed_sc->sc_ioproc = NULL; + splx(s); + + return (0); +} + + +int +aedread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + int s, error; + int willfit; + int total; + int firstmove; + int moremove; + + if (uio->uio_resid < sizeof(adb_event_t)) + return (EMSGSIZE); /* close enough. */ + + s = spladb(); + if (aed_sc->sc_evq_len == 0) { + splx(s); + return (0); + } + willfit = howmany(uio->uio_resid, sizeof(adb_event_t)); + total = (aed_sc->sc_evq_len < willfit) ? aed_sc->sc_evq_len : willfit; + + firstmove = (aed_sc->sc_evq_tail + total > AED_MAX_EVENTS) + ? (AED_MAX_EVENTS - aed_sc->sc_evq_tail) : total; + + error = uiomove((caddr_t) & aed_sc->sc_evq[aed_sc->sc_evq_tail], + firstmove * sizeof(adb_event_t), uio); + if (error) { + splx(s); + return (error); + } + moremove = total - firstmove; + + if (moremove > 0) { + error = uiomove((caddr_t) & aed_sc->sc_evq[0], + moremove * sizeof(adb_event_t), uio); + if (error) { + splx(s); + return (error); + } + } + aed_sc->sc_evq_tail = (aed_sc->sc_evq_tail + total) % AED_MAX_EVENTS; + aed_sc->sc_evq_len -= total; + splx(s); + return (0); +} + + +int +aedwrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + return 0; +} + + +int +aedioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + switch (cmd) { + case ADBIOCDEVSINFO: { + adb_devinfo_t *di; + ADBDataBlock adbdata; + int totaldevs; + int adbaddr; + int i; + + di = (void *)data; + + /* Initialize to no devices */ + for (i = 0; i < 16; i++) + di->dev[i].addr = -1; + + totaldevs = CountADBs(); + for (i = 1; i <= totaldevs; i++) { + adbaddr = GetIndADB(&adbdata, i); + di->dev[adbaddr].addr = adbaddr; + di->dev[adbaddr].default_addr = (int)(adbdata.origADBAddr); + di->dev[adbaddr].handler_id = (int)(adbdata.devType); + } + + /* Must call ADB Manager to get devices now */ + break; + } + + case ADBIOCGETREPEAT:{ + adb_rptinfo_t *ri; + + ri = (void *)data; + ri->delay_ticks = aed_sc->sc_rptdelay; + ri->interval_ticks = aed_sc->sc_rptinterval; + break; + } + + case ADBIOCSETREPEAT:{ + adb_rptinfo_t *ri; + + ri = (void *) data; + aed_sc->sc_rptdelay = ri->delay_ticks; + aed_sc->sc_rptinterval = ri->interval_ticks; + break; + } + + case ADBIOCRESET: + /* Do nothing for now */ + break; + + case ADBIOCLISTENCMD:{ + adb_listencmd_t *lc; + + lc = (void *)data; + } + + default: + return (EINVAL); + } + return (0); +} + + +int +aedpoll(dev, events, p) + dev_t dev; + int events; + struct proc *p; +{ + int s, revents; + + revents = events & (POLLOUT | POLLWRNORM); + + if ((events & (POLLIN | POLLRDNORM)) == 0) + return (revents); + + s = spladb(); + if (aed_sc->sc_evq_len > 0) + revents |= events & (POLLIN | POLLRDNORM); + else + selrecord(p, &aed_sc->sc_selinfo); + splx(s); + + return (revents); +} diff --git a/sys/arch/macppc/dev/aedvar.h b/sys/arch/macppc/dev/aedvar.h new file mode 100644 index 00000000000..f53770e0de3 --- /dev/null +++ b/sys/arch/macppc/dev/aedvar.h @@ -0,0 +1,92 @@ +/* $OpenBSD: aedvar.h,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: aedvar.h,v 1.2 2000/03/23 06:40:33 thorpej Exp $ */ + +/* + * Copyright (C) 1994 Bradley A. Grantham + * 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 Bradley A. Grantham. + * 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. + */ + +#ifdef __NetBSD__ +#include <sys/callout.h> +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ +#include <sys/timeout.h> +#endif /* __OpenBSD__ */ +#include <machine/adbsys.h> + +/* Event queue definitions */ +#ifndef AED_MAX_EVENTS +#define AED_MAX_EVENTS 200 /* Maximum events to be kept in queue */ + /* maybe should be higher for slower macs? */ +#endif /* AED_MAX_EVENTS */ + +struct aed_softc { + struct device sc_dev; + +#ifdef __NetBSD__ + struct callout sc_repeat_ch; +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + struct timeout sc_repeat_ch; +#endif /* __OpenBSD__ */ + + /* ADB info */ + u_char origaddr; /* ADB device type (ADBADDR_AED) */ + u_char adbaddr; /* current ADB address */ + u_char handler_id; /* type of device */ + + /* ADB event queue */ + adb_event_t sc_evq[AED_MAX_EVENTS]; /* the queue */ + int sc_evq_tail; /* event queue tail pointer */ + int sc_evq_len; /* event queue length */ + + /* Keyboard repeat state */ + int sc_rptdelay; /* ticks before auto-repeat */ + int sc_rptinterval; /* ticks between auto-repeat */ + int sc_repeating; /* key that is auto-repeating */ + adb_event_t sc_rptevent; /* event to auto-repeat */ + + int sc_buttons; /* mouse button state */ + + struct selinfo sc_selinfo; /* select() info */ + struct proc * sc_ioproc; /* process to wakeup */ + + int sc_open; /* Are we queuing events? */ + int sc_options; /* config options */ +}; + +/* Options */ +#define AED_MSEMUL 0x1 /* emulate mouse buttons */ + +void aed_input __P((adb_event_t *event)); +int aedopen __P((dev_t dev, int flag, int mode, struct proc *p)); +int aedclose __P((dev_t dev, int flag, int mode, struct proc *p)); +int aedread __P((dev_t dev, struct uio *uio, int flag)); +int aedwrite __P((dev_t dev, struct uio *uio, int flag)); +int aedioctl __P((dev_t , int , caddr_t , int , struct proc *)); +int aedpoll __P((dev_t dev, int events, struct proc *p)); diff --git a/sys/arch/macppc/dev/akbd.c b/sys/arch/macppc/dev/akbd.c new file mode 100644 index 00000000000..5974b616e14 --- /dev/null +++ b/sys/arch/macppc/dev/akbd.c @@ -0,0 +1,557 @@ +/* $OpenBSD: akbd.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: akbd.c,v 1.13 2001/01/25 14:08:55 tsubai Exp $ */ + +/* + * Copyright (C) 1998 Colin Wood + * 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 Colin Wood. + * 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 <sys/param.h> +#include <sys/device.h> +#include <sys/fcntl.h> +#include <sys/poll.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/signalvar.h> +#include <sys/systm.h> + +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wskbdvar.h> +#include <dev/wscons/wsksymdef.h> +#include <dev/wscons/wsksymvar.h> + +#include <machine/autoconf.h> +#define KEYBOARD_ARRAY + +#include <macppc/mac/keyboard.h> +#include <macppc/mac/adbvar.h> +#include <macppc/mac/aedvar.h> +#include <macppc/mac/akbdmap.h> +#include <macppc/mac/akbdvar.h> +#include <macppc/mac/amsvar.h> +#include <macppc/mac/adb_direct.h> +#include <macppc/mac/pm_direct.h> + +#include "aed.h" + +/* + * Function declarations. + */ +static int akbdmatch __P((struct device *, void *, void *)); +static void akbdattach __P((struct device *, struct device *, void *)); +void kbd_adbcomplete __P((caddr_t buffer, caddr_t data_area, int adb_command)); +static void kbd_processevent __P((adb_event_t *event, struct akbd_softc *)); +#ifdef notyet +static u_char getleds __P((int)); +static int setleds __P((struct akbd_softc *, u_char)); +static void blinkleds __P((struct akbd_softc *)); +#endif + +/* Driver definition. */ +struct cfattach akbd_ca = { + sizeof(struct akbd_softc), akbdmatch, akbdattach +}; +struct cfdriver akbd_cd = { + NULL, "akbd", DV_DULL +}; + + +extern struct cfdriver akbd_cd; + +int akbd_enable __P((void *, int)); +void akbd_set_leds __P((void *, int)); +int akbd_ioctl __P((void *, u_long, caddr_t, int, struct proc *)); +int akbd_intr __P((adb_event_t *event)); + +struct wskbd_accessops akbd_accessops = { + akbd_enable, + akbd_set_leds, + akbd_ioctl, +}; + +void akbd_cngetc __P((void *, u_int *, int *)); +void akbd_cnpollc __P((void *, int)); + +struct wskbd_consops akbd_consops = { + akbd_cngetc, + akbd_cnpollc, +}; + +struct wskbd_mapdata akbd_keymapdata = { + akbd_keydesctab, +#ifdef AKBD_LAYOUT + AKBD_LAYOUT, +#else + KB_US, +#endif +}; + +static int akbd_is_console; + +static int +akbdmatch(parent, cf, aux) + struct device *parent; + void *cf; + void *aux; +{ + struct adb_attach_args *aa_args = aux; + + if (aa_args->origaddr == ADBADDR_KBD) + return 1; + else + return 0; +} + +static void +akbdattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + ADBSetInfoBlock adbinfo; + struct akbd_softc *sc = (struct akbd_softc *)self; + struct adb_attach_args *aa_args = aux; + int error, kbd_done; + short cmd; + u_char buffer[9]; + struct wskbddev_attach_args a; + + sc->origaddr = aa_args->origaddr; + sc->adbaddr = aa_args->adbaddr; + sc->handler_id = aa_args->handler_id; + + sc->sc_leds = (u_int8_t)0x00; /* initially off */ + + adbinfo.siServiceRtPtr = (Ptr)kbd_adbcomplete; + adbinfo.siDataAreaAddr = (caddr_t)sc; + + switch (sc->handler_id) { + case ADB_STDKBD: + printf("standard keyboard\n"); + break; + case ADB_ISOKBD: + printf("standard keyboard (ISO layout)\n"); + break; + case ADB_EXTKBD: + cmd = ADBTALK(sc->adbaddr, 1); + kbd_done = + (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) == 0); + + /* Ignore Logitech MouseMan/Trackman pseudo keyboard */ + if (kbd_done && buffer[1] == 0x9a && buffer[2] == 0x20) { + printf("Mouseman (non-EMP) pseudo keyboard\n"); + adbinfo.siServiceRtPtr = (Ptr)0; + adbinfo.siDataAreaAddr = (Ptr)0; + } else if (kbd_done && buffer[1] == 0x9a && buffer[2] == 0x21) { + printf("Trackman (non-EMP) pseudo keyboard\n"); + adbinfo.siServiceRtPtr = (Ptr)0; + adbinfo.siDataAreaAddr = (Ptr)0; + } else { + printf("extended keyboard\n"); +#ifdef notyet + blinkleds(sc); +#endif + } + break; + case ADB_EXTISOKBD: + printf("extended keyboard (ISO layout)\n"); +#ifdef notyet + blinkleds(sc); +#endif + break; + case ADB_KBDII: + printf("keyboard II\n"); + break; + case ADB_ISOKBDII: + printf("keyboard II (ISO layout)\n"); + break; + case ADB_PBKBD: + printf("PowerBook keyboard\n"); + break; + case ADB_PBISOKBD: + printf("PowerBook keyboard (ISO layout)\n"); + break; + case ADB_ADJKPD: + printf("adjustable keypad\n"); + break; + case ADB_ADJKBD: + printf("adjustable keyboard\n"); + break; + case ADB_ADJISOKBD: + printf("adjustable keyboard (ISO layout)\n"); + break; + case ADB_ADJJAPKBD: + printf("adjustable keyboard (Japanese layout)\n"); + break; + case ADB_PBEXTISOKBD: + printf("PowerBook extended keyboard (ISO layout)\n"); + break; + case ADB_PBEXTJAPKBD: + printf("PowerBook extended keyboard (Japanese layout)\n"); + break; + case ADB_JPKBDII: + printf("keyboard II (Japanese layout)\n"); + break; + case ADB_PBEXTKBD: + printf("PowerBook extended keyboard\n"); + break; + case ADB_DESIGNKBD: + printf("extended keyboard\n"); +#ifdef notyet + blinkleds(sc); +#endif + break; + case ADB_PBJPKBD: + printf("PowerBook keyboard (Japanese layout)\n"); + break; + case ADB_PBG3JPKBD: + printf("PowerBook G3 keyboard (Japanese layout)\n"); + break; + case ADB_PBG4KBD: + printf("PowerBook G4 keyboard (Inverted T)\n"); + break; + case ADB_IBITISOKBD: + printf("iBook keyboard with inverted T (ISO layout)\n"); + break; + default: + printf("mapped device (%d)\n", sc->handler_id); + break; + } + error = SetADBInfo(&adbinfo, sc->adbaddr); +#ifdef ADB_DEBUG + if (adb_debug) + printf("akbd: returned %d from SetADBInfo\n", error); +#endif + + a.console = akbd_is_console; + a.keymap = &akbd_keymapdata; + a.accessops = &akbd_accessops; + a.accesscookie = sc; + + sc->sc_wskbddev = config_found(self, &a, wskbddevprint); +} + + +/* + * Handle putting the keyboard data received from the ADB into + * an ADB event record. + */ +void +kbd_adbcomplete(buffer, data_area, adb_command) + caddr_t buffer; + caddr_t data_area; + int adb_command; +{ + adb_event_t event; + struct akbd_softc *ksc; + int adbaddr; +#ifdef ADB_DEBUG + int i; + + if (adb_debug) + printf("adb: transaction completion\n"); +#endif + + adbaddr = ADB_CMDADDR(adb_command); + ksc = (struct akbd_softc *)data_area; + + event.addr = adbaddr; + event.hand_id = ksc->handler_id; + event.def_addr = ksc->origaddr; + event.byte_count = buffer[0]; + memcpy(event.bytes, buffer + 1, event.byte_count); + +#ifdef ADB_DEBUG + if (adb_debug) { + printf("akbd: from %d at %d (org %d) %d:", event.addr, + event.hand_id, event.def_addr, buffer[0]); + for (i = 1; i <= buffer[0]; i++) + printf(" %x", buffer[i]); + printf("\n"); + } +#endif + + microtime(&event.timestamp); + + kbd_processevent(&event, ksc); +} + +/* + * Given a keyboard ADB event, record the keycodes and call the key + * repeat handler, optionally passing the event through the mouse + * button emulation handler first. + */ +static void +kbd_processevent(event, ksc) + adb_event_t *event; + struct akbd_softc *ksc; +{ + adb_event_t new_event; + + new_event = *event; + new_event.u.k.key = event->bytes[0]; + new_event.bytes[1] = 0xff; + akbd_intr(&new_event); +#if NAED > 0 + aed_input(&new_event); +#endif + if (event->bytes[1] != 0xff) { + new_event.u.k.key = event->bytes[1]; + new_event.bytes[0] = event->bytes[1]; + new_event.bytes[1] = 0xff; + akbd_intr(&new_event); +#if NAED > 0 + aed_input(&new_event); +#endif + } + +} + +#ifdef notyet +/* + * Get the actual hardware LED state and convert it to softc format. + */ +static u_char +getleds(addr) + int addr; +{ + short cmd; + u_char buffer[9], leds; + + leds = 0x00; /* all off */ + buffer[0] = 0; + + /* talk R2 */ + cmd = ADBTALK(addr, 2); + if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) == 0 && + buffer[0] > 0) + leds = ~(buffer[2]) & 0x07; + + return (leds); +} + +/* + * Set the keyboard LED's. + * + * Automatically translates from ioctl/softc format to the + * actual keyboard register format + */ +static int +setleds(ksc, leds) + struct akbd_softc *ksc; + u_char leds; +{ + int addr; + short cmd; + u_char buffer[9]; + + if ((leds & 0x07) == (ksc->sc_leds & 0x07)) + return (0); + + addr = ksc->adbaddr; + buffer[0] = 0; + + cmd = ADBTALK(addr, 2); + if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) || buffer[0] == 0) + return (EIO); + + leds = ~leds & 0x07; + buffer[2] &= 0xf8; + buffer[2] |= leds; + + cmd = ADBLISTEN(addr, 2); + adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd); + + cmd = ADBTALK(addr, 2); + if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) || buffer[0] == 0) + return (EIO); + + ksc->sc_leds = ~((u_int8_t)buffer[2]) & 0x07; + + if ((buffer[2] & 0xf8) != leds) + return (EIO); + else + return (0); +} + +/* + * Toggle all of the LED's on and off, just for show. + */ +static void +blinkleds(ksc) + struct akbd_softc *ksc; +{ + int addr, i; + u_char blinkleds, origleds; + + addr = ksc->adbaddr; + origleds = getleds(addr); + blinkleds = LED_NUMLOCK | LED_CAPSLOCK | LED_SCROLL_LOCK; + + (void)setleds(ksc, blinkleds); + + for (i = 0; i < 10000; i++) + delay(50); + + /* make sure that we restore the LED settings */ + i = 10; + do { + (void)setleds(ksc, (u_char)0x00); + } while (setleds(ksc, (u_char)0x00) && (i-- > 0)); + + return; +} +#endif + +int +akbd_enable(v, on) + void *v; + int on; +{ + return 0; +} + +void +akbd_set_leds(v, on) + void *v; + int on; +{ +} + +int +akbd_ioctl(v, cmd, data, flag, p) + void *v; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + switch (cmd) { + + case WSKBDIO_GTYPE: + *(int *)data = WSKBD_TYPE_ADB; + return 0; + case WSKBDIO_SETLEDS: + return 0; + case WSKBDIO_GETLEDS: + *(int *)data = 0; + return 0; + } + /* kbdioctl(...); */ + + return -1; +} + +static int polledkey; +extern int adb_polling; + +int +akbd_intr(event) + adb_event_t *event; +{ + int key, press, val; + int type; + + struct akbd_softc *sc = akbd_cd.cd_devs[0]; + + key = event->u.k.key; + press = ADBK_PRESS(key); + val = ADBK_KEYVAL(key); + + type = press ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP; + + switch (key) { + case 57: /* Caps Lock pressed */ + case 185: /* Caps Lock released */ + type = WSCONS_EVENT_KEY_DOWN; + wskbd_input(sc->sc_wskbddev, type, val); + type = WSCONS_EVENT_KEY_UP; + break; +#if 0 + /* not supported... */ + case 245: + pm_eject_pcmcia(0); + break; + case 244: + pm_eject_pcmcia(1); + break; +#endif + } + + if (adb_polling) + polledkey = key; + else + wskbd_input(sc->sc_wskbddev, type, val); + + return 0; +} + +int +akbd_cnattach() +{ + + akbd_is_console = 1; + wskbd_cnattach(&akbd_consops, NULL, &akbd_keymapdata); + return 0; +} + +void +akbd_cngetc(v, type, data) + void *v; + u_int *type; + int *data; +{ + int key, press, val; + int s; + + s = splhigh(); + + polledkey = -1; + adb_polling = 1; + + while (polledkey == -1) { + adb_intr(NULL); /* adb does not use the argument */ + DELAY(10000); /* XXX */ + } + + adb_polling = 0; + splx(s); + + key = polledkey; + press = ADBK_PRESS(key); + val = ADBK_KEYVAL(key); + + *data = val; + *type = press ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP; +} + +void +akbd_cnpollc(v, on) + void *v; + int on; +{ +} diff --git a/sys/arch/macppc/dev/akbdmap.h b/sys/arch/macppc/dev/akbdmap.h new file mode 100644 index 00000000000..1a48af2ae10 --- /dev/null +++ b/sys/arch/macppc/dev/akbdmap.h @@ -0,0 +1,223 @@ +/* $OpenBSD: akbdmap.h,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: akbdmap.h,v 1.3 2000/09/01 16:00:39 tsubai Exp $ */ + +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Juergen Hannken-Illjes. + * + * 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 the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + +/* XXX This list is incomplete. */ + +#define KC(n) KS_KEYCODE(n) + +static const keysym_t akbd_keydesc_us[] = { +/* pos command normal shifted */ + KC(0), KS_a, + KC(1), KS_s, + KC(2), KS_d, + KC(3), KS_f, + KC(4), KS_h, + KC(5), KS_g, + KC(6), KS_z, + KC(7), KS_x, + KC(8), KS_c, + KC(9), KS_v, + + KC(11), KS_b, + KC(12), KS_q, + KC(13), KS_w, + KC(14), KS_e, + KC(15), KS_r, + KC(16), KS_y, + KC(17), KS_t, + KC(18), KS_1, KS_exclam, + KC(19), KS_2, KS_at, + KC(20), KS_3, KS_numbersign, + KC(21), KS_4, KS_dollar, + KC(22), KS_6, KS_asciicircum, + KC(23), KS_5, KS_percent, + KC(24), KS_equal, KS_plus, + KC(25), KS_9, KS_parenleft, + KC(26), KS_7, KS_ampersand, + KC(27), KS_minus, KS_underscore, + KC(28), KS_8, KS_asterisk, + KC(29), KS_0, KS_parenright, + KC(30), KS_bracketright, KS_braceright, + KC(31), KS_o, + KC(32), KS_u, + KC(33), KS_bracketleft, KS_braceleft, + KC(34), KS_i, + KC(35), KS_p, + KC(36), KS_Return, + KC(37), KS_l, + KC(38), KS_j, + KC(39), KS_apostrophe, KS_quotedbl, + KC(40), KS_k, + KC(41), KS_semicolon, KS_colon, + KC(42), KS_backslash, KS_bar, + KC(43), KS_comma, KS_less, + KC(44), KS_slash, KS_question, + KC(45), KS_n, + KC(46), KS_m, + KC(47), KS_period, KS_greater, + KC(48), KS_Tab, + KC(49), KS_space, + KC(50), KS_grave, KS_asciitilde, + KC(51), KS_Delete, + + KC(53), KS_Escape, + KC(54), KS_Control_L, + KC(55), KS_Cmd, /* Command */ + KC(56), KS_Shift_L, + KC(57), KS_Caps_Lock, + KC(58), KS_Cmd1, /* Option */ + KC(59), KS_Left, + KC(60), KS_Right, + KC(61), KS_Down, + KC(62), KS_Up, + + KC(65), KS_KP_Decimal, + KC(67), KS_KP_Multiply, + KC(69), KS_KP_Add, + KC(71), KS_Clear, + KC(75), KS_KP_Divide, + KC(76), KS_KP_Enter, + KC(78), KS_KP_Subtract, + + KC(81), KS_KP_Equal, + KC(82), KS_KP_0, + KC(83), KS_KP_1, + KC(84), KS_KP_2, + KC(85), KS_KP_3, + KC(86), KS_KP_4, + KC(87), KS_KP_5, + KC(88), KS_KP_6, + KC(89), KS_KP_7, + + KC(91), KS_KP_8, + KC(92), KS_KP_9, + + KC(95), KS_comma, /* XXX KS_KP_comma */ + + KC(106), KS_KP_Enter, + + KC(127), KS_Cmd_Debugger, +}; + +static const keysym_t akbd_keydesc_fr[] = { +/* pos normal shifted altgr shift-altgr */ + KC(0), KS_q, + KC(6), KS_w, + KC(10), KS_at, KS_numbersign, + KC(12), KS_a, + KC(13), KS_z, + KC(18), KS_ampersand, KS_1, + KC(19), KS_eacute, KS_2, KS_asciitilde, + KC(20), KS_quotedbl, KS_3, KS_numbersign, + KC(21), KS_apostrophe, KS_4, KS_braceleft, + KC(22), KS_section, KS_6, KS_bar, + KC(23), KS_parenleft, KS_5, KS_bracketleft, KS_braceleft, + KC(24), KS_minus, KS_underscore, KS_braceright, + KC(25), KS_ccedilla, KS_9, KS_asciicircum, + KC(26), KS_egrave, KS_7, KS_grave, + KC(27), KS_parenright, KS_degree, KS_bracketright, KS_braceright, + KC(28), KS_exclam, KS_8, KS_bar, + KC(29), KS_agrave, KS_0, KS_at, + KC(30), KS_dollar, KS_asterisk, + KC(33), KS_dead_circumflex, KS_dead_diaeresis, + KC(39), KS_mu, KS_percent, + KC(40), KS_k, + KC(41), KS_m, + KC(42), KS_grave, KS_sterling, + KC(43), KS_semicolon, KS_period, + KC(44), KS_equal, KS_plus, + KC(46), KS_comma, KS_question, + KC(47), KS_colon, KS_slash, KS_backslash, + KC(50), KS_less, KS_greater, + KC(52), KS_Alt_R, + KC(55), KS_Meta_L, /* Command */ + KC(58), KS_Alt_R, /* Option */ +}; + +#if 0 +static const keysym_t akbd_keydesc_jp[] = { +/* pos command normal shifted */ + KC(42), KS_grave, KS_asciitilde, + KC(93), KS_backslash, KS_bar, +}; +#endif + +static const keysym_t akbd_keydesc_sv[] = { +/* pos normal shifted altgr shift-altgr */ + KC(10), KS_section, KS_degree, + KC(19), KS_2, KS_quotedbl, KS_at, + KC(21), KS_4, KS_dollar, + KC(22), KS_6, KS_ampersand, + KC(24), KS_dead_acute, KS_dead_grave, + KC(25), KS_9, KS_parenright, KS_bracketright, + KC(26), KS_7, KS_slash, KS_braceleft, + KC(27), KS_plus, KS_question, KS_backslash, + KC(28), KS_8, KS_parenleft, KS_bracketleft, + KC(29), KS_0, KS_equal, KS_braceright, + KC(30), KS_dead_diaeresis, KS_dead_circumflex, KS_dead_tilde, + KC(33), KS_aring, + KC(39), KS_adiaeresis, + KC(41), KS_odiaeresis, + KC(42), KS_apostrophe, KS_asterisk, + KC(43), KS_comma, KS_semicolon, + KC(44), KS_minus, KS_underscore, + KC(47), KS_period, KS_colon, + KC(50), KS_less, KS_greater, KS_bar, + KC(52), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t akbd_keydesc_sv_nodead[] = { +/* pos normal shifted altgr shift-altgr */ + KC(24), KS_apostrophe, KS_grave, + KC(30), KS_diaeresis, KS_asciicircum, KS_asciitilde, +}; + +#define KBD_MAP(name, base, map) \ + { name, base, sizeof(map)/sizeof(keysym_t), map } + +static const struct wscons_keydesc akbd_keydesctab[] = { + KBD_MAP(KB_US, 0, akbd_keydesc_us), + KBD_MAP(KB_FR, KB_US, akbd_keydesc_fr), + KBD_MAP(KB_SV, KB_US, akbd_keydesc_sv), + KBD_MAP(KB_SV | KB_NODEAD, KB_SV, akbd_keydesc_sv_nodead), + {0, 0, 0, 0} +}; + +#undef KBD_MAP +#undef KC diff --git a/sys/arch/macppc/dev/akbdvar.h b/sys/arch/macppc/dev/akbdvar.h new file mode 100644 index 00000000000..ff1a56d5365 --- /dev/null +++ b/sys/arch/macppc/dev/akbdvar.h @@ -0,0 +1,62 @@ +/* $OpenBSD: akbdvar.h,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: akbdvar.h,v 1.4 1999/02/17 14:56:56 tsubai Exp $ */ + +/* + * Copyright (C) 1998 Colin Wood + * 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 Colin Wood. + * 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. + */ + +#ifndef _MACPPC_KBDVAR_H_ +#define _MACPPC_KBDVAR_H_ + +#include <machine/adbsys.h> + +/* + * State info, per keyboard instance. + */ +struct akbd_softc { + struct device sc_dev; + + /* ADB info */ + int origaddr; /* ADB device type (ADBADDR_KBD) */ + int adbaddr; /* current ADB address */ + int handler_id; /* type of keyboard */ + + u_int8_t sc_leds; /* current LED state */ + struct device *sc_wskbddev; +}; + +/* LED register bits, inverse of actual register value */ +#define LED_NUMLOCK 0x1 +#define LED_CAPSLOCK 0x2 +#define LED_SCROLL_LOCK 0x4 + +void kbd_adbcomplete __P((caddr_t buffer, caddr_t data_area, int adb_command)); +int akbd_cnattach(void); + +#endif /* _MACPPC_KBDVAR_H_ */ diff --git a/sys/arch/macppc/dev/ams.c b/sys/arch/macppc/dev/ams.c new file mode 100644 index 00000000000..08dd82193aa --- /dev/null +++ b/sys/arch/macppc/dev/ams.c @@ -0,0 +1,554 @@ +/* $OpenBSD: ams.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: ams.c,v 1.11 2000/12/19 03:13:40 tsubai Exp $ */ + +/* + * Copyright (C) 1998 Colin Wood + * 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 Colin Wood. + * 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 <sys/param.h> +#include <sys/device.h> +#include <sys/fcntl.h> +#include <sys/poll.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/signalvar.h> +#include <sys/systm.h> + +#include <machine/autoconf.h> + +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wsmousevar.h> + +#include <macppc/mac/adbvar.h> +#include <macppc/mac/aedvar.h> +#include <macppc/mac/amsvar.h> +#include <macppc/mac/adb_direct.h> + +#include "aed.h" + +/* + * Function declarations. + */ +static int amsmatch __P((struct device *, void *, void *)); +static void amsattach __P((struct device *, struct device *, void *)); +static void ems_init __P((struct ams_softc *)); +static void ms_processevent __P((adb_event_t *event, struct ams_softc *)); + +/* Driver definition. */ +struct cfattach ams_ca = { + sizeof(struct ams_softc), amsmatch, amsattach +}; +/* Driver definition. */ +struct cfdriver ams_cd = { + NULL, "ams", DV_DULL +}; + + +int ams_enable __P((void *)); +int ams_ioctl __P((void *, u_long, caddr_t, int, struct proc *)); +void ams_disable __P((void *)); + +const struct wsmouse_accessops ams_accessops = { + ams_enable, + ams_ioctl, + ams_disable, +}; + +static int +amsmatch(parent, cf, aux) + struct device *parent; + void *cf; + void *aux; +{ + struct adb_attach_args *aa_args = aux; + + if (aa_args->origaddr == ADBADDR_MS) + return 1; + else + return 0; +} + +static void +amsattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + ADBSetInfoBlock adbinfo; + struct ams_softc *sc = (struct ams_softc *)self; + struct adb_attach_args *aa_args = aux; + int error; + struct wsmousedev_attach_args a; + + sc->origaddr = aa_args->origaddr; + sc->adbaddr = aa_args->adbaddr; + sc->handler_id = aa_args->handler_id; + + sc->sc_class = MSCLASS_MOUSE; + sc->sc_buttons = 1; + sc->sc_res = 100; + sc->sc_devid[0] = 0; + sc->sc_devid[4] = 0; + + adbinfo.siServiceRtPtr = (Ptr)ms_adbcomplete; + adbinfo.siDataAreaAddr = (caddr_t)sc; + + ems_init(sc); + + /* print out the type of mouse we have */ + switch (sc->handler_id) { + case ADBMS_100DPI: + printf("%d-button, %d dpi mouse\n", sc->sc_buttons, + (int)(sc->sc_res)); + break; + case ADBMS_200DPI: + sc->sc_res = 200; + printf("%d-button, %d dpi mouse\n", sc->sc_buttons, + (int)(sc->sc_res)); + break; + case ADBMS_MSA3: + printf("Mouse Systems A3 mouse, %d-button, %d dpi\n", + sc->sc_buttons, (int)(sc->sc_res)); + break; + case ADBMS_USPEED: + printf("MicroSpeed mouse, default parameters\n"); + break; + case ADBMS_UCONTOUR: + printf("Contour mouse, default parameters\n"); + break; + case ADBMS_TURBO: + printf("Kensington Turbo Mouse\n"); + break; + case ADBMS_EXTENDED: + if (sc->sc_devid[0] == '\0') { + printf("Logitech "); + switch (sc->sc_class) { + case MSCLASS_MOUSE: + printf("MouseMan (non-EMP) mouse"); + break; + case MSCLASS_TRACKBALL: + printf("TrackMan (non-EMP) trackball"); + break; + default: + printf("non-EMP relative positioning device"); + break; + } + printf("\n"); + } else { + printf("EMP "); + switch (sc->sc_class) { + case MSCLASS_TABLET: + printf("tablet"); + break; + case MSCLASS_MOUSE: + printf("mouse"); + break; + case MSCLASS_TRACKBALL: + printf("trackball"); + break; + case MSCLASS_TRACKPAD: + printf("trackpad"); + break; + default: + printf("unknown device"); + break; + } + printf(" <%s> %d-button, %d dpi\n", sc->sc_devid, + sc->sc_buttons, (int)(sc->sc_res)); + } + break; + default: + printf("relative positioning device (mouse?) (%d)\n", + sc->handler_id); + break; + } + error = SetADBInfo(&adbinfo, sc->adbaddr); +#ifdef ADB_DEBUG + if (adb_debug) + printf("ams: returned %d from SetADBInfo\n", error); +#endif + + a.accessops = &ams_accessops; + a.accesscookie = sc; + sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); +} + + +/* + * Initialize extended mouse support -- probes devices as described + * in Inside Macintosh: Devices, Chapter 5 "ADB Manager". + * + * Extended Mouse Protocol is documented in TechNote HW1: + * "ADB - The Untold Story: Space Aliens Ate My Mouse" + * + * Supports: Extended Mouse Protocol, MicroSpeed Mouse Deluxe, + * Mouse Systems A^3 Mouse, Logitech non-EMP MouseMan + */ +void +ems_init(sc) + struct ams_softc *sc; +{ + int adbaddr; + short cmd; + u_char buffer[9]; + + adbaddr = sc->adbaddr; + if (sc->origaddr != ADBADDR_MS) + return; + if (sc->handler_id == ADBMS_USPEED || + sc->handler_id == ADBMS_UCONTOUR) { + /* Found MicroSpeed Mouse Deluxe Mac or Contour Mouse */ + cmd = ADBLISTEN(adbaddr, 1); + + /* + * To setup the MicroSpeed or the Contour, it appears + * that we can send the following command to the mouse + * and then expect data back in the form: + * buffer[0] = 4 (bytes) + * buffer[1], buffer[2] as std. mouse + * buffer[3] = buffer[4] = 0xff when no buttons + * are down. When button N down, bit N is clear. + * buffer[4]'s locking mask enables a + * click to toggle the button down state--sort of + * like the "Easy Access" shift/control/etc. keys. + * buffer[3]'s alternative speed mask enables using + * different speed when the corr. button is down + */ + buffer[0] = 4; + buffer[1] = 0x00; /* Alternative speed */ + buffer[2] = 0x00; /* speed = maximum */ + buffer[3] = 0x10; /* enable extended protocol, + * lower bits = alt. speed mask + * = 0000b + */ + buffer[4] = 0x07; /* Locking mask = 0000b, + * enable buttons = 0111b + */ + adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd); + + sc->sc_buttons = 3; + sc->sc_res = 200; + return; + } + if (sc->handler_id == ADBMS_TURBO) { + /* Found Kensington Turbo Mouse */ + static u_char data1[] = + { 8, 0xe7, 0x8c, 0, 0, 0, 0xff, 0xff, 0x94 }; + static u_char data2[] = + { 8, 0xa5, 0x14, 0, 0, 0x69, 0xff, 0xff, 0x27 }; + + buffer[0] = 0; + adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, ADBFLUSH(adbaddr)); + + adb_op_sync((Ptr)data1, (Ptr)0, (Ptr)0, ADBLISTEN(adbaddr, 2)); + + buffer[0] = 0; + adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, ADBFLUSH(adbaddr)); + + adb_op_sync((Ptr)data2, (Ptr)0, (Ptr)0, ADBLISTEN(adbaddr, 2)); + return; + } + if ((sc->handler_id == ADBMS_100DPI) || + (sc->handler_id == ADBMS_200DPI)) { + /* found a mouse */ + cmd = ADBTALK(adbaddr, 3); + if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd)) { +#ifdef ADB_DEBUG + if (adb_debug) + printf("adb: ems_init timed out\n"); +#endif + return; + } + + /* Attempt to initialize Extended Mouse Protocol */ + buffer[2] = 4; /* make handler ID 4 */ + cmd = ADBLISTEN(adbaddr, 3); + if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd)) { +#ifdef ADB_DEBUG + if (adb_debug) + printf("adb: ems_init timed out\n"); +#endif + return; + } + + /* + * Check to see if successful, if not + * try to initialize it as other types + */ + cmd = ADBTALK(adbaddr, 3); + if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) == 0 && + buffer[2] == ADBMS_EXTENDED) { + sc->handler_id = ADBMS_EXTENDED; + cmd = ADBTALK(adbaddr, 1); + if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd)) { +#ifdef ADB_DEBUG + if (adb_debug) + printf("adb: ems_init timed out\n"); +#endif + } else if (buffer[0] == 8) { + /* we have a true EMP device */ + sc->sc_class = buffer[7]; + sc->sc_buttons = buffer[8]; + sc->sc_res = (int)*(short *)&buffer[5]; + bcopy(&(buffer[1]), sc->sc_devid, 4); + } else if (buffer[1] == 0x9a && + ((buffer[2] == 0x20) || (buffer[2] == 0x21))) { + /* + * Set up non-EMP Mouseman/Trackman to put + * button bits in 3rd byte instead of sending + * via pseudo keyboard device. + */ + cmd = ADBLISTEN(adbaddr, 1); + buffer[0]=2; + buffer[1]=0x00; + buffer[2]=0x81; + adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd); + + cmd = ADBLISTEN(adbaddr, 1); + buffer[0]=2; + buffer[1]=0x01; + buffer[2]=0x81; + adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd); + + cmd = ADBLISTEN(adbaddr, 1); + buffer[0]=2; + buffer[1]=0x02; + buffer[2]=0x81; + adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd); + + cmd = ADBLISTEN(adbaddr, 1); + buffer[0]=2; + buffer[1]=0x03; + buffer[2]=0x38; + adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd); + + sc->sc_buttons = 3; + sc->sc_res = 400; + if (buffer[2] == 0x21) + sc->sc_class = MSCLASS_TRACKBALL; + else + sc->sc_class = MSCLASS_MOUSE; + } else + /* unknown device? */; + } else { + /* Attempt to initialize as an A3 mouse */ + buffer[2] = 0x03; /* make handler ID 3 */ + cmd = ADBLISTEN(adbaddr, 3); + if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd)) { +#ifdef ADB_DEBUG + if (adb_debug) + printf("adb: ems_init timed out\n"); +#endif + return; + } + + /* + * Check to see if successful, if not + * try to initialize it as other types + */ + cmd = ADBTALK(adbaddr, 3); + if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) == 0 + && buffer[2] == ADBMS_MSA3) { + sc->handler_id = ADBMS_MSA3; + /* Initialize as above */ + cmd = ADBLISTEN(adbaddr, 2); + /* listen 2 */ + buffer[0] = 3; + buffer[1] = 0x00; + /* Irrelevant, buffer has 0x77 */ + buffer[2] = 0x07; + /* + * enable 3 button mode = 0111b, + * speed = normal + */ + adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd); + sc->sc_buttons = 3; + sc->sc_res = 300; + } else { + /* No special support for this mouse */ + } + } + } +} + +/* + * Handle putting the mouse data received from the ADB into + * an ADB event record. + */ +void +ms_adbcomplete(buffer, data_area, adb_command) + caddr_t buffer; + caddr_t data_area; + int adb_command; +{ + adb_event_t event; + struct ams_softc *sc; + int adbaddr; +#ifdef ADB_DEBUG + int i; + + if (adb_debug) + printf("adb: transaction completion\n"); +#endif + + adbaddr = ADB_CMDADDR(adb_command); + sc = (struct ams_softc *)data_area; + + if ((sc->handler_id == ADBMS_EXTENDED) && (sc->sc_devid[0] == 0)) { + /* massage the data to look like EMP data */ + if ((buffer[3] & 0x04) == 0x04) + buffer[1] &= 0x7f; + else + buffer[1] |= 0x80; + if ((buffer[3] & 0x02) == 0x02) + buffer[2] &= 0x7f; + else + buffer[2] |= 0x80; + if ((buffer[3] & 0x01) == 0x01) + buffer[3] = 0x00; + else + buffer[3] = 0x80; + } + + event.addr = adbaddr; + event.hand_id = sc->handler_id; + event.def_addr = sc->origaddr; + event.byte_count = buffer[0]; + memcpy(event.bytes, buffer + 1, event.byte_count); + +#ifdef ADB_DEBUG + if (adb_debug) { + printf("ams: from %d at %d (org %d) %d:", event.addr, + event.hand_id, event.def_addr, buffer[0]); + for (i = 1; i <= buffer[0]; i++) + printf(" %x", buffer[i]); + printf("\n"); + } +#endif + + microtime(&event.timestamp); + + ms_processevent(&event, sc); +} + +/* + * Given a mouse ADB event, record the button settings, calculate the + * x- and y-axis motion, and handoff the event to the appropriate subsystem. + */ +static void +ms_processevent(event, sc) + adb_event_t *event; + struct ams_softc *sc; +{ + adb_event_t new_event; + int i, button_bit, max_byte, mask, buttons; + + new_event = *event; + buttons = 0; + + /* + * This should handle both plain ol' Apple mice and mice + * that claim to support the Extended Apple Mouse Protocol. + */ + max_byte = event->byte_count; + button_bit = 1; + switch (event->hand_id) { + case ADBMS_USPEED: + case ADBMS_UCONTOUR: + /* MicroSpeed mouse and Contour mouse */ + if (max_byte == 4) + buttons = (~event->bytes[2]) & 0xff; + else + buttons = (event->bytes[0] & 0x80) ? 0 : 1; + break; + case ADBMS_MSA3: + /* Mouse Systems A3 mouse */ + if (max_byte == 3) + buttons = (~event->bytes[2]) & 0x07; + else + buttons = (event->bytes[0] & 0x80) ? 0 : 1; + break; + default: + /* Classic Mouse Protocol (up to 2 buttons) */ + for (i = 0; i < 2; i++, button_bit <<= 1) + /* 0 when button down */ + if (!(event->bytes[i] & 0x80)) + buttons |= button_bit; + else + buttons &= ~button_bit; + /* Extended Protocol (up to 6 more buttons) */ + for (mask = 0x80; i < max_byte; + i += (mask == 0x80), button_bit <<= 1) { + /* 0 when button down */ + if (!(event->bytes[i] & mask)) + buttons |= button_bit; + else + buttons &= ~button_bit; + mask = ((mask >> 4) & 0xf) + | ((mask & 0xf) << 4); + } + break; + } + new_event.u.m.buttons = sc->sc_mb | buttons; + new_event.u.m.dx = ((signed int) (event->bytes[1] & 0x3f)) - + ((event->bytes[1] & 0x40) ? 64 : 0); + new_event.u.m.dy = ((signed int) (event->bytes[0] & 0x3f)) - + ((event->bytes[0] & 0x40) ? 64 : 0); + + if (sc->sc_wsmousedev) + wsmouse_input(sc->sc_wsmousedev, new_event.u.m.buttons, + new_event.u.m.dx, -new_event.u.m.dy, 0, + WSMOUSE_INPUT_DELTA); +#if NAED > 0 + aed_input(&new_event); +#endif +} + +int +ams_enable(v) + void *v; +{ + return 0; +} + +int +ams_ioctl(v, cmd, data, flag, p) + void *v; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + return -1; +} + +void +ams_disable(v) + void *v; +{ +} diff --git a/sys/arch/macppc/dev/amsvar.h b/sys/arch/macppc/dev/amsvar.h new file mode 100644 index 00000000000..059131689d6 --- /dev/null +++ b/sys/arch/macppc/dev/amsvar.h @@ -0,0 +1,67 @@ +/* $OpenBSD: amsvar.h,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: amsvar.h,v 1.4 1999/06/17 06:59:05 tsubai Exp $ */ + +/* + * Copyright (C) 1998 Colin Wood + * 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 Colin Wood. + * 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. + */ + +#ifndef _MACPPC_AMSVAR_H_ +#define _MACPPC_AMSVAR_H_ + +/* + * State info, per mouse instance. + */ +struct ams_softc { + struct device sc_dev; + + /* ADB info */ + int origaddr; /* ADB device type (ADBADDR_MS) */ + int adbaddr; /* current ADB address */ + int handler_id; /* type of mouse */ + + /* Extended Mouse Protocol info, faked for non-EMP mice */ + u_int8_t sc_class; /* mouse class (mouse, trackball) */ + u_int8_t sc_buttons; /* number of buttons */ + u_int32_t sc_res; /* mouse resolution (dpi) */ + char sc_devid[5]; /* device indentifier */ + + int sc_mb; /* current button state */ + struct device *sc_wsmousedev; +}; + +/* EMP device classes */ +#define MSCLASS_TABLET 0 +#define MSCLASS_MOUSE 1 +#define MSCLASS_TRACKBALL 2 +#define MSCLASS_TRACKPAD 3 + +void ms_adbcomplete __P((caddr_t buffer, caddr_t data_area, int adb_command)); +void ms_handoff __P((adb_event_t *event, struct ams_softc *)); + +#endif /* _MACPPC_AMSVAR_H_ */ diff --git a/sys/arch/macppc/dev/awacs.c b/sys/arch/macppc/dev/awacs.c new file mode 100644 index 00000000000..cbceac81def --- /dev/null +++ b/sys/arch/macppc/dev/awacs.c @@ -0,0 +1,1065 @@ +/* $OpenBSD: awacs.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: awacs.c,v 1.4 2001/02/26 21:07:51 wiz Exp $ */ + +/*- + * Copyright (c) 2000 Tsubai Masanari. 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. 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 <sys/param.h> +#include <sys/audioio.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/systm.h> +#include <sys/types.h> + +#include <dev/auconv.h> +#include <dev/audio_if.h> +#include <dev/mulaw.h> + +#include <vm/vm.h> +#include <vm/vm_kern.h> +#include <uvm/uvm.h> + +#include <machine/autoconf.h> +#include <machine/pio.h> +#include <macppc/mac/dbdma.h> + +#ifdef AWACS_DEBUG +# define DPRINTF printf +#else +# define DPRINTF while (0) printf +#endif + +struct awacs_softc { + struct device sc_dev; + + void (*sc_ointr)(void *); /* dma completion intr handler */ + void *sc_oarg; /* arg for sc_ointr() */ + int sc_opages; /* # of output pages */ + + void (*sc_iintr)(void *); /* dma completion intr handler */ + void *sc_iarg; /* arg for sc_iintr() */ + + u_int sc_record_source; /* recording source mask */ + u_int sc_output_mask; /* output source mask */ + + char *sc_reg; + u_int sc_codecctl0; + u_int sc_codecctl1; + u_int sc_codecctl2; + u_int sc_codecctl4; + u_int sc_soundctl; + + struct dbdma_regmap *sc_odma; + struct dbdma_regmap *sc_idma; + struct dbdma_command *sc_odmacmd; + struct dbdma_command *sc_idmacmd; +}; + +int awacs_match(struct device *, void *, void *); +void awacs_attach(struct device *, struct device *, void *); +int awacs_intr(void *); +int awacs_tx_intr(void *); +int awacs_rx_intr(void *); + +int awacs_open(void *, int); +void awacs_close(void *); +int awacs_query_encoding(void *, struct audio_encoding *); +int awacs_set_params(void *, int, int, struct audio_params *, + struct audio_params *); +int awacs_round_blocksize(void *, int); +int awacs_trigger_output(void *, void *, void *, int, void (*)(void *), + void *, struct audio_params *); +int awacs_trigger_input(void *, void *, void *, int, void (*)(void *), + void *, struct audio_params *); +int awacs_halt_output(void *); +int awacs_halt_input(void *); +int awacs_getdev(void *, struct audio_device *); +int awacs_set_port(void *, mixer_ctrl_t *); +int awacs_get_port(void *, mixer_ctrl_t *); +int awacs_query_devinfo(void *, mixer_devinfo_t *); +size_t awacs_round_buffersize(void *, int, size_t); +int awacs_mappage(void *, void *, int, int); +int awacs_get_props(void *); + +static inline u_int awacs_read_reg(struct awacs_softc *, int); +static inline void awacs_write_reg(struct awacs_softc *, int, int); +void awacs_write_codec(struct awacs_softc *, int); +void awacs_set_speaker_volume(struct awacs_softc *, int, int); +void awacs_set_ext_volume(struct awacs_softc *, int, int); +int awacs_set_rate(struct awacs_softc *, int); +void awacs_mono16_to_stereo16 __P((void *v, u_char *p, int cc)); + +struct cfattach awacs_ca = { + sizeof(struct awacs_softc), awacs_match, awacs_attach +}; + +struct cfdriver awacs_cd = { + NULL, "awacs", DV_DULL +}; + +struct audio_hw_if awacs_hw_if = { + awacs_open, + awacs_close, + NULL, + awacs_query_encoding, + awacs_set_params, + awacs_round_blocksize, + NULL, + NULL, + NULL, + NULL, + NULL, + awacs_halt_output, + awacs_halt_input, + NULL, + awacs_getdev, + NULL, + awacs_set_port, + awacs_get_port, + awacs_query_devinfo, + NULL, + NULL, + NULL, + awacs_mappage, + awacs_get_props, + awacs_trigger_output, + awacs_trigger_input, + NULL, + awacs_round_buffersize, + +}; + +struct audio_device awacs_device = { + "AWACS", + "", + "awacs" +}; + +/* register offset */ +#define AWACS_SOUND_CTRL 0x00 +#define AWACS_CODEC_CTRL 0x10 +#define AWACS_CODEC_STATUS 0x20 +#define AWACS_CLIP_COUNT 0x30 +#define AWACS_BYTE_SWAP 0x40 + +/* sound control */ +#define AWACS_INPUT_SUBFRAME0 0x00000001 +#define AWACS_INPUT_SUBFRAME1 0x00000002 +#define AWACS_INPUT_SUBFRAME2 0x00000004 +#define AWACS_INPUT_SUBFRAME3 0x00000008 + +#define AWACS_OUTPUT_SUBFRAME0 0x00000010 +#define AWACS_OUTPUT_SUBFRAME1 0x00000020 +#define AWACS_OUTPUT_SUBFRAME2 0x00000040 +#define AWACS_OUTPUT_SUBFRAME3 0x00000080 + +#define AWACS_RATE_44100 0x00000000 +#define AWACS_RATE_29400 0x00000100 +#define AWACS_RATE_22050 0x00000200 +#define AWACS_RATE_17640 0x00000300 +#define AWACS_RATE_14700 0x00000400 +#define AWACS_RATE_11025 0x00000500 +#define AWACS_RATE_8820 0x00000600 +#define AWACS_RATE_7350 0x00000700 +#define AWACS_RATE_MASK 0x00000700 + +#define AWACS_CTL_CNTRLERR (1 << 11) +#define AWACS_CTL_PORTCHG (1 << 12) +#define AWACS_INT_CNTRLERR (1 << 13) +#define AWACS_INT_PORTCHG (1 << 14) + +/* codec control */ +#define AWACS_CODEC_ADDR0 0x00000000 +#define AWACS_CODEC_ADDR1 0x00001000 +#define AWACS_CODEC_ADDR2 0x00002000 +#define AWACS_CODEC_ADDR4 0x00004000 +#define AWACS_CODEC_EMSEL0 0x00000000 +#define AWACS_CODEC_EMSEL1 0x00400000 +#define AWACS_CODEC_EMSEL2 0x00800000 +#define AWACS_CODEC_EMSEL4 0x00c00000 +#define AWACS_CODEC_BUSY 0x01000000 + +/* cc0 */ +#define AWACS_DEFAULT_CD_GAIN 0x000000bb +#define AWACS_INPUT_CD 0x00000200 +#define AWACS_INPUT_LINE 0x00000400 +#define AWACS_INPUT_MICROPHONE 0x00000800 +#define AWACS_INPUT_MASK 0x00000e00 + +/* cc1 */ +#define AWACS_MUTE_SPEAKER 0x00000080 +#define AWACS_MUTE_HEADPHONE 0x00000200 + +int +awacs_match(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct confargs *ca = aux; + + if (strcmp(ca->ca_name, "awacs") != 0 && + strcmp(ca->ca_name, "davbus") != 0) + return 0; + + printf("awacs: matched %s nreg %d nintr %d\n", + ca->ca_name, ca->ca_nreg, ca->ca_nintr); + + if (ca->ca_nreg < 24 || ca->ca_nintr < 12) + return 0; + + /* XXX for now + if (ca->ca_nintr > 12) + return 0; + */ + + return 1; +} + +void +awacs_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct awacs_softc *sc = (struct awacs_softc *)self; + struct confargs *ca = aux; + int cirq, oirq, iirq; + int cirq_type, oirq_type, iirq_type; + + ca->ca_reg[0] += ca->ca_baseaddr; + ca->ca_reg[2] += ca->ca_baseaddr; + ca->ca_reg[4] += ca->ca_baseaddr; + + sc->sc_reg = mapiodev(ca->ca_reg[0], ca->ca_reg[1]); + + sc->sc_odma = mapiodev(ca->ca_reg[2], ca->ca_reg[3]); /* out */ + sc->sc_idma = mapiodev(ca->ca_reg[4], ca->ca_reg[5]); /* in */ + sc->sc_odmacmd = dbdma_alloc(20 * sizeof(struct dbdma_command)); + sc->sc_idmacmd = dbdma_alloc(20 * sizeof(struct dbdma_command)); + + if (ca->ca_nintr == 24) { + cirq = ca->ca_intr[0]; + oirq = ca->ca_intr[2]; + iirq = ca->ca_intr[4]; + cirq_type = ca->ca_intr[1] ? IST_LEVEL : IST_EDGE; + oirq_type = ca->ca_intr[3] ? IST_LEVEL : IST_EDGE; + iirq_type = ca->ca_intr[5] ? IST_LEVEL : IST_EDGE; + } else { + cirq = ca->ca_intr[0]; + oirq = ca->ca_intr[1]; + iirq = ca->ca_intr[2]; + cirq_type = oirq_type = iirq_type = IST_LEVEL; + } + mac_intr_establish(parent, cirq, cirq_type, IPL_AUDIO, awacs_intr, + sc, "awacs"); + mac_intr_establish(parent, oirq, oirq_type, IPL_AUDIO, awacs_tx_intr, + sc, "awacs/tx"); +#if 0 + /* do not use this for now, since both are tied to same freq + * we can service both in the same interrupt, lowering + * interrupt load by half + */ + mac_intr_establish(parent, iirq, irq_type, IPL_AUDIO, awacs_intr, + sc, "awacs/rx"); +#endif + + printf(": irq %d,%d,%d", + cirq, oirq, iirq); + + sc->sc_soundctl = AWACS_INPUT_SUBFRAME0 | AWACS_OUTPUT_SUBFRAME0 | + AWACS_RATE_44100 | AWACS_INT_PORTCHG; + awacs_write_reg(sc, AWACS_SOUND_CTRL, sc->sc_soundctl); + + sc->sc_codecctl0 = AWACS_CODEC_ADDR0 | AWACS_CODEC_EMSEL0; + sc->sc_codecctl1 = AWACS_CODEC_ADDR1 | AWACS_CODEC_EMSEL0; + sc->sc_codecctl2 = AWACS_CODEC_ADDR2 | AWACS_CODEC_EMSEL0; + sc->sc_codecctl4 = AWACS_CODEC_ADDR4 | AWACS_CODEC_EMSEL0; + + sc->sc_codecctl0 |= AWACS_INPUT_CD | AWACS_DEFAULT_CD_GAIN; + awacs_write_codec(sc, sc->sc_codecctl0); + + /* Set initial volume[s] */ + awacs_set_speaker_volume(sc, 80, 80); + awacs_set_ext_volume(sc, 80, 80); + + /* Set loopback (for CD?) */ + /* sc->sc_codecctl1 |= 0x440; */ + sc->sc_codecctl1 |= 0x40; + awacs_write_codec(sc, sc->sc_codecctl1); + + /* check for headphone present */ + if (awacs_read_reg(sc, AWACS_CODEC_STATUS) & 0x8) { + /* default output to speakers */ + printf(" headphones"); + sc->sc_output_mask = 1 << 1; + sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE; + sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER; + awacs_write_codec(sc, sc->sc_codecctl1); + } else { + /* default output to speakers */ + printf(" speaker"); + sc->sc_output_mask = 1 << 0; + sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER; + sc->sc_codecctl1 |= AWACS_MUTE_HEADPHONE; + awacs_write_codec(sc, sc->sc_codecctl1); + } + + /* default input from CD */ + sc->sc_record_source = 1 << 0; + sc->sc_codecctl0 &= ~AWACS_INPUT_MASK; + sc->sc_codecctl0 |= AWACS_INPUT_CD; + awacs_write_codec(sc, sc->sc_codecctl0); + + /* Enable interrupts and looping mode. */ + /* XXX ... */ + awacs_halt_output(sc); + awacs_halt_input(sc); + printf("\n"); + + audio_attach_mi(&awacs_hw_if, sc, &sc->sc_dev); +} + +u_int +awacs_read_reg(sc, reg) + struct awacs_softc *sc; + int reg; +{ + char *addr = sc->sc_reg; + + return in32rb(addr + reg); +} + +void +awacs_write_reg(sc, reg, val) + struct awacs_softc *sc; + int reg, val; +{ + char *addr = sc->sc_reg; + + out32rb(addr + reg, val); +} + +void +awacs_write_codec(sc, value) + struct awacs_softc *sc; + int value; +{ + awacs_write_reg(sc, AWACS_CODEC_CTRL, value); + while (awacs_read_reg(sc, AWACS_CODEC_CTRL) & AWACS_CODEC_BUSY); +} + +int +awacs_intr(v) + void *v; +{ + int reason; + struct awacs_softc *sc = v; + reason = awacs_read_reg(sc, AWACS_SOUND_CTRL); + + if (reason & AWACS_CTL_CNTRLERR) { + /* change outputs ?? */ + printf("should change inputs\n"); + } + if (reason & AWACS_CTL_PORTCHG) { +#ifdef DEBUG + printf("status = %x\n", awacs_read_reg(sc, AWACS_CODEC_STATUS)); +#endif + + if (awacs_read_reg(sc, AWACS_CODEC_STATUS) & 0x8) { + /* default output to speakers */ + sc->sc_output_mask = 1 << 1; + sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE; + sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER; + awacs_write_codec(sc, sc->sc_codecctl1); + } else { + /* default output to speakers */ + sc->sc_output_mask = 1 << 0; + sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER; + sc->sc_codecctl1 |= AWACS_MUTE_HEADPHONE; + awacs_write_codec(sc, sc->sc_codecctl1); + } + } + + awacs_write_reg(sc, AWACS_SOUND_CTRL, reason); /* clear interrupt */ + return 1; +} +int +awacs_tx_intr(v) + void *v; +{ + struct awacs_softc *sc = v; + struct dbdma_command *cmd = sc->sc_odmacmd; + int count = sc->sc_opages; + int status; + + /* Fill used buffer(s). */ + while (count-- > 0) { + /* if DBDMA_INT_ALWAYS */ + if (in16rb(&cmd->d_command) & 0x30) { /* XXX */ + status = in16rb(&cmd->d_status); + cmd->d_status = 0; + if (status) /* status == 0x8400 */ + if (sc->sc_ointr) + (*sc->sc_ointr)(sc->sc_oarg); + } + cmd++; + } + + return 1; +} + +int +awacs_open(h, flags) + void *h; + int flags; +{ + return 0; +} + +/* + * Close function is called at splaudio(). + */ +void +awacs_close(h) + void *h; +{ + struct awacs_softc *sc = h; + + awacs_halt_output(sc); + awacs_halt_input(sc); + + sc->sc_ointr = 0; + sc->sc_iintr = 0; +} + +int +awacs_query_encoding(h, ae) + void *h; + struct audio_encoding *ae; +{ + switch (ae->index) { + case 0: + strcpy(ae->name, AudioEslinear); + ae->encoding = AUDIO_ENCODING_SLINEAR; + ae->precision = 16; + ae->flags = 0; + return 0; + case 1: + strcpy(ae->name, AudioEslinear_be); + ae->encoding = AUDIO_ENCODING_SLINEAR_BE; + ae->precision = 16; + ae->flags = 0; + return 0; + case 2: + strcpy(ae->name, AudioEslinear_le); + ae->encoding = AUDIO_ENCODING_SLINEAR_LE; + ae->precision = 16; + ae->flags = 0; + return 0; + case 3: + strcpy(ae->name, AudioEmulaw); + ae->encoding = AUDIO_ENCODING_ULAW; + ae->precision = 8; + ae->flags = AUDIO_ENCODINGFLAG_EMULATED; + return 0; + case 4: + strcpy(ae->name, AudioEalaw); + ae->encoding = AUDIO_ENCODING_ALAW; + ae->precision = 8; + ae->flags = AUDIO_ENCODINGFLAG_EMULATED; + return 0; + default: + return EINVAL; + } +} + +void +awacs_mono16_to_stereo16(v, p, cc) + void *v; + u_char *p; + int cc; +{ + int x; + int16_t *src, *dst; + + src = (void *)(p + cc); + dst = (void *)(p + cc * 2); + while (cc > 0) { + x = *--src; + *--dst = x; + *--dst = x; + cc -= 2; + } +} + +int +awacs_set_params(h, setmode, usemode, play, rec) + void *h; + int setmode, usemode; + struct audio_params *play, *rec; +{ + struct awacs_softc *sc = h; + struct audio_params *p; + int mode, rate; + + /* + * This device only has one clock, so make the sample rates match. + */ + if (play->sample_rate != rec->sample_rate && + usemode == (AUMODE_PLAY | AUMODE_RECORD)) { + if (setmode == AUMODE_PLAY) { + rec->sample_rate = play->sample_rate; + setmode |= AUMODE_RECORD; + } else if (setmode == AUMODE_RECORD) { + play->sample_rate = rec->sample_rate; + setmode |= AUMODE_PLAY; + } else + return EINVAL; + } + + for (mode = AUMODE_RECORD; mode != -1; + mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) { + if ((setmode & mode) == 0) + continue; + + p = mode == AUMODE_PLAY ? play : rec; + + if (p->sample_rate < 4000 || p->sample_rate > 50000 || + (p->precision != 8 && p->precision != 16) || + (p->channels != 1 && p->channels != 2)) + return EINVAL; + + p->factor = 1; + p->sw_code = 0; + awacs_write_reg(sc, AWACS_BYTE_SWAP, 0); + + switch (p->encoding) { + + case AUDIO_ENCODING_SLINEAR_LE: + awacs_write_reg(sc, AWACS_BYTE_SWAP, 1); + case AUDIO_ENCODING_SLINEAR_BE: + if (p->channels == 1) { + p->factor = 2; + p->sw_code = awacs_mono16_to_stereo16; + break; + } + if (p->precision != 16) + return EINVAL; + /* p->sw_code = change_sign8; */ + break; + + case AUDIO_ENCODING_ULINEAR_LE: + awacs_write_reg(sc, AWACS_BYTE_SWAP, 1); + case AUDIO_ENCODING_ULINEAR_BE: + if (p->precision == 16) + p->sw_code = change_sign16_be; + else + return EINVAL; + break; + + case AUDIO_ENCODING_ULAW: + if (mode == AUMODE_PLAY) { + p->factor = 2; + p->sw_code = mulaw_to_slinear16_be; + } else + p->sw_code = ulinear8_to_mulaw; + break; + + case AUDIO_ENCODING_ALAW: + if (mode == AUMODE_PLAY) { + p->factor = 2; + p->sw_code = alaw_to_slinear16_be; + } else + p->sw_code = ulinear8_to_alaw; + break; + + default: + return EINVAL; + } + } + + /* Set the speed */ + rate = p->sample_rate; + + awacs_set_rate(sc, rate); + + return 0; +} + +int +awacs_round_blocksize(h, size) + void *h; + int size; +{ + if (size < NBPG) + size = NBPG; + return size & ~PGOFSET; +} + +int +awacs_halt_output(h) + void *h; +{ + struct awacs_softc *sc = h; + + dbdma_stop(sc->sc_odma); + dbdma_reset(sc->sc_odma); + dbdma_stop(sc->sc_odma); + return 0; +} + +int +awacs_halt_input(h) + void *h; +{ + struct awacs_softc *sc = h; + + dbdma_stop(sc->sc_idma); + dbdma_reset(sc->sc_idma); + return 0; +} + +int +awacs_getdev(h, retp) + void *h; + struct audio_device *retp; +{ + *retp = awacs_device; + return 0; +} + +enum { + AWACS_OUTPUT_SELECT, + AWACS_VOL_SPEAKER, + AWACS_VOL_HEADPHONE, + AWACS_OUTPUT_CLASS, + AWACS_MONITOR_CLASS, + AWACS_INPUT_SELECT, + AWACS_VOL_INPUT, + AWACS_INPUT_CLASS, + AWACS_RECORD_CLASS, + AWACS_ENUM_LAST +}; + +int +awacs_set_port(h, mc) + void *h; + mixer_ctrl_t *mc; +{ + struct awacs_softc *sc = h; + int l, r; + + DPRINTF("awacs_set_port dev = %d, type = %d\n", mc->dev, mc->type); + + l = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; + r = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; + + switch (mc->dev) { + case AWACS_OUTPUT_SELECT: + /* no change necessary? */ + if (mc->un.mask == sc->sc_output_mask) + return 0; + switch(mc->un.mask) { + case 1<<0: /* speaker */ + sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER; + sc->sc_codecctl1 |= AWACS_MUTE_HEADPHONE; + awacs_write_codec(sc, sc->sc_codecctl1); + break; + case 1<<1: /* headphones */ + sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER; + sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE; + awacs_write_codec(sc, sc->sc_codecctl1); + break; + default: /* invalid argument */ + return -1; + } + sc->sc_output_mask = mc->un.mask; + return 0; + + case AWACS_VOL_SPEAKER: + awacs_set_speaker_volume(sc, l, r); + return 0; + + case AWACS_VOL_HEADPHONE: + awacs_set_ext_volume(sc, l, r); + return 0; + + case AWACS_VOL_INPUT: + sc->sc_codecctl0 &= ~0xff; + sc->sc_codecctl0 |= (l & 0xf0) | (r >> 4); + awacs_write_codec(sc, sc->sc_codecctl0); + return 0; + + case AWACS_INPUT_SELECT: + /* no change necessary? */ + if (mc->un.mask == sc->sc_record_source) + return 0; + switch(mc->un.mask) { + case 1<<0: /* CD */ + sc->sc_codecctl0 &= ~AWACS_INPUT_MASK; + sc->sc_codecctl0 |= AWACS_INPUT_CD; + awacs_write_codec(sc, sc->sc_codecctl0); + break; + case 1<<1: /* microphone */ + sc->sc_codecctl0 &= ~AWACS_INPUT_MASK; + sc->sc_codecctl0 |= AWACS_INPUT_MICROPHONE; + awacs_write_codec(sc, sc->sc_codecctl0); + break; + case 1<<2: /* line in */ + sc->sc_codecctl0 &= ~AWACS_INPUT_MASK; + sc->sc_codecctl0 |= AWACS_INPUT_LINE; + awacs_write_codec(sc, sc->sc_codecctl0); + break; + default: /* invalid argument */ + return -1; + } + sc->sc_record_source = mc->un.mask; + return 0; + } + + return ENXIO; +} + +int +awacs_get_port(h, mc) + void *h; + mixer_ctrl_t *mc; +{ + struct awacs_softc *sc = h; + int vol, l, r; + + DPRINTF("awacs_get_port dev = %d, type = %d\n", mc->dev, mc->type); + + switch (mc->dev) { + case AWACS_OUTPUT_SELECT: + mc->un.mask = sc->sc_output_mask; + return 0; + + case AWACS_VOL_SPEAKER: + vol = sc->sc_codecctl4; + l = (15 - ((vol & 0x3c0) >> 6)) * 16; + r = (15 - (vol & 0x0f)) * 16; + mc->un.mask = 1 << 0; + mc->un.value.num_channels = 2; + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; + return 0; + + case AWACS_VOL_HEADPHONE: + vol = sc->sc_codecctl2; + l = (15 - ((vol & 0x3c0) >> 6)) * 16; + r = (15 - (vol & 0x0f)) * 16; + mc->un.mask = 1 << 1; + mc->un.value.num_channels = 2; + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; + return 0; + + case AWACS_INPUT_SELECT: + mc->un.mask = sc->sc_record_source; + return 0; + + case AWACS_VOL_INPUT: + vol = sc->sc_codecctl0 & 0xff; + l = (vol & 0xf0); + r = (vol & 0x0f) << 4; + mc->un.mask = sc->sc_record_source; + mc->un.value.num_channels = 2; + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; + return 0; + + default: + return ENXIO; + } + + return 0; +} + +int +awacs_query_devinfo(h, dip) + void *h; + mixer_devinfo_t *dip; +{ + + DPRINTF("query_devinfo %d\n", dip->index); + + switch (dip->index) { + + case AWACS_OUTPUT_SELECT: + dip->mixer_class = AWACS_MONITOR_CLASS; + strcpy(dip->label.name, AudioNoutput); + dip->type = AUDIO_MIXER_SET; + dip->prev = dip->next = AUDIO_MIXER_LAST; + dip->un.s.num_mem = 2; + strcpy(dip->un.s.member[0].label.name, AudioNspeaker); + dip->un.s.member[0].mask = 1 << 0; + strcpy(dip->un.s.member[1].label.name, AudioNheadphone); + dip->un.s.member[1].mask = 1 << 1; + return 0; + + case AWACS_VOL_SPEAKER: + dip->mixer_class = AWACS_OUTPUT_CLASS; + strcpy(dip->label.name, AudioNspeaker); + dip->type = AUDIO_MIXER_VALUE; + dip->prev = dip->next = AUDIO_MIXER_LAST; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case AWACS_VOL_HEADPHONE: + dip->mixer_class = AWACS_OUTPUT_CLASS; + strcpy(dip->label.name, AudioNheadphone); + dip->type = AUDIO_MIXER_VALUE; + dip->prev = dip->next = AUDIO_MIXER_LAST; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case AWACS_INPUT_SELECT: + dip->mixer_class = AWACS_MONITOR_CLASS; + strcpy(dip->label.name, AudioNinput); + dip->type = AUDIO_MIXER_SET; + dip->prev = dip->next = AUDIO_MIXER_LAST; + dip->un.s.num_mem = 3; + strcpy(dip->un.s.member[0].label.name, AudioNcd); + dip->un.s.member[0].mask = 1 << 0; + strcpy(dip->un.s.member[1].label.name, AudioNmicrophone); + dip->un.s.member[1].mask = 1 << 1; + strcpy(dip->un.s.member[2].label.name, AudioNline); + dip->un.s.member[2].mask = 1 << 2; + return 0; + + case AWACS_VOL_INPUT: + dip->mixer_class = AWACS_INPUT_CLASS; + strcpy(dip->label.name, AudioNmaster); + dip->type = AUDIO_MIXER_VALUE; + dip->prev = dip->next = AUDIO_MIXER_LAST; + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case AWACS_MONITOR_CLASS: + dip->mixer_class = AWACS_MONITOR_CLASS; + strcpy(dip->label.name, AudioCmonitor); + dip->type = AUDIO_MIXER_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + return 0; + + case AWACS_OUTPUT_CLASS: + dip->mixer_class = AWACS_OUTPUT_CLASS; + strcpy(dip->label.name, AudioCoutputs); + dip->type = AUDIO_MIXER_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + return 0; + + case AWACS_RECORD_CLASS: + dip->mixer_class = AWACS_MONITOR_CLASS; + strcpy(dip->label.name, AudioCrecord); + dip->type = AUDIO_MIXER_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + return 0; + + case AWACS_INPUT_CLASS: + dip->mixer_class = AWACS_INPUT_CLASS; + strcpy(dip->label.name, AudioCinputs); + dip->type = AUDIO_MIXER_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + return 0; + } + + return ENXIO; +} + +size_t +awacs_round_buffersize(h, dir, size) + void *h; + int dir; + size_t size; +{ + if (size > 65536) + size = 65536; + return size; +} + +int +awacs_mappage(h, mem, off, prot) + void *h; + void *mem; + int off; + int prot; +{ + if (off < 0) + return -1; + return -1; /* XXX */ +} + +int +awacs_get_props(h) + void *h; +{ + return AUDIO_PROP_FULLDUPLEX /* | AUDIO_PROP_MMAP */; +} + +int +awacs_trigger_output(h, start, end, bsize, intr, arg, param) + void *h; + void *start, *end; + int bsize; + void (*intr)(void *); + void *arg; + struct audio_params *param; +{ + struct awacs_softc *sc = h; + struct dbdma_command *cmd = sc->sc_odmacmd; + vaddr_t va; + int i, len, intmode; + + DPRINTF("trigger_output %p %p 0x%x\n", start, end, bsize); + + sc->sc_ointr = intr; + sc->sc_oarg = arg; + sc->sc_opages = ((char *)end - (char *)start) / NBPG; + +#ifdef DIAGNOSTIC + if (sc->sc_opages > 16) + panic("awacs_trigger_output"); +#endif + + va = (vaddr_t)start; + len = 0; + for (i = sc->sc_opages; i > 0; i--) { + len += NBPG; + if (len < bsize) + intmode = DBDMA_INT_NEVER; + else { + len = 0; + intmode = DBDMA_INT_ALWAYS; + } + + DBDMA_BUILD(cmd, DBDMA_CMD_OUT_MORE, 0, NBPG, vtophys(va), + intmode, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + va += NBPG; + cmd++; + } + + DBDMA_BUILD(cmd, DBDMA_CMD_NOP, 0, 0, 0, + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_ALWAYS); + dbdma_st32(&cmd->d_cmddep, vtophys((vaddr_t)sc->sc_odmacmd)); + + dbdma_start(sc->sc_odma, sc->sc_odmacmd); + + return 0; +} + +int +awacs_trigger_input(h, start, end, bsize, intr, arg, param) + void *h; + void *start, *end; + int bsize; + void (*intr)(void *); + void *arg; + struct audio_params *param; +{ + printf("awacs_trigger_input called\n"); + + return 1; +} + +void +awacs_set_speaker_volume(sc, left, right) + struct awacs_softc *sc; + int left, right; +{ + int lval = 15 - (left & 0xff) / 16; + int rval = 15 - (right & 0xff) / 16; + + DPRINTF("speaker_volume %d %d\n", lval, rval); + + sc->sc_codecctl4 &= ~0x3cf; + sc->sc_codecctl4 |= (lval << 6) | rval; + awacs_write_codec(sc, sc->sc_codecctl4); +} + +void +awacs_set_ext_volume(sc, left, right) + struct awacs_softc *sc; + int left, right; +{ + int lval = 15 - (left & 0xff) / 16; + int rval = 15 - (right & 0xff) / 16; + + DPRINTF("ext_volume %d %d\n", lval, rval); + + sc->sc_codecctl2 &= ~0x3cf; + sc->sc_codecctl2 |= (lval << 6) | rval; + awacs_write_codec(sc, sc->sc_codecctl2); +} + +int +awacs_set_rate(sc, rate) + struct awacs_softc *sc; + int rate; +{ + int c; + + switch (rate) { + + case 44100: + c = AWACS_RATE_44100; + break; + case 29400: + c = AWACS_RATE_29400; + break; + case 22050: + c = AWACS_RATE_22050; + break; + case 17640: + c = AWACS_RATE_17640; + break; + case 14700: + c = AWACS_RATE_14700; + break; + case 11025: + c = AWACS_RATE_11025; + break; + case 8820: + c = AWACS_RATE_8820; + break; + case 7350: + c = AWACS_RATE_7350; + break; + default: + return -1; + } + + sc->sc_soundctl &= ~AWACS_RATE_MASK; + sc->sc_soundctl |= c; + awacs_write_reg(sc, AWACS_SOUND_CTRL, sc->sc_soundctl); + + return 0; +} diff --git a/sys/arch/macppc/dev/dbdma.c b/sys/arch/macppc/dev/dbdma.c new file mode 100644 index 00000000000..508271c12f1 --- /dev/null +++ b/sys/arch/macppc/dev/dbdma.c @@ -0,0 +1,135 @@ +/* $OpenBSD: dbdma.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: dbdma.c,v 1.2 1998/08/21 16:13:28 tsubai Exp $ */ + +/* + * Copyright 1991-1998 by Open Software Foundation, Inc. + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appears in all copies and + * that both the copyright notice and this permission notice appear in + * supporting documentation. + * + * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, + * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include <sys/param.h> +#include <sys/malloc.h> +#include <sys/systm.h> + +#include <vm/vm.h> + +#include <machine/pio.h> +#include <macppc/mac/dbdma.h> + + + +dbdma_command_t *dbdma_alloc_commands = NULL; + +void +dbdma_start(dmap, commands) + dbdma_regmap_t *dmap; + dbdma_command_t *commands; +{ + u_int32_t addr = vtophys((vaddr_t)commands); + + if (addr & 0xf) + panic("dbdma_start command structure not 16-byte aligned"); + + DBDMA_ST4_ENDIAN(&dmap->d_intselect, DBDMA_CLEAR_CNTRL( (0xffff))); + DBDMA_ST4_ENDIAN(&dmap->d_control, + DBDMA_CLEAR_CNTRL( (DBDMA_CNTRL_ACTIVE | + DBDMA_CNTRL_DEAD | + DBDMA_CNTRL_WAKE | + DBDMA_CNTRL_FLUSH | + DBDMA_CNTRL_PAUSE | + DBDMA_CNTRL_RUN ))); + + do { + delay(10); + } while (DBDMA_LD4_ENDIAN(&dmap->d_status) & DBDMA_CNTRL_ACTIVE); + + + DBDMA_ST4_ENDIAN(&dmap->d_cmdptrhi, 0); /* 64-bit not yet */ + DBDMA_ST4_ENDIAN(&dmap->d_cmdptrlo, addr); + + DBDMA_ST4_ENDIAN(&dmap->d_control, + DBDMA_SET_CNTRL(DBDMA_CNTRL_RUN|DBDMA_CNTRL_WAKE)| + DBDMA_CLEAR_CNTRL(DBDMA_CNTRL_PAUSE|DBDMA_CNTRL_DEAD) ); +} + +void +dbdma_stop(dmap) + dbdma_regmap_t *dmap; +{ + DBDMA_ST4_ENDIAN(&dmap->d_control, DBDMA_CLEAR_CNTRL(DBDMA_CNTRL_RUN) | + DBDMA_SET_CNTRL(DBDMA_CNTRL_FLUSH)); + + while (DBDMA_LD4_ENDIAN(&dmap->d_status) & + (DBDMA_CNTRL_ACTIVE|DBDMA_CNTRL_FLUSH)); +} + +void +dbdma_flush(dmap) + dbdma_regmap_t *dmap; +{ + DBDMA_ST4_ENDIAN(&dmap->d_control, DBDMA_SET_CNTRL(DBDMA_CNTRL_FLUSH)); + + while (DBDMA_LD4_ENDIAN(&dmap->d_status) & (DBDMA_CNTRL_FLUSH)); +} + +void +dbdma_reset(dmap) + dbdma_regmap_t *dmap; +{ + DBDMA_ST4_ENDIAN(&dmap->d_control, + DBDMA_CLEAR_CNTRL( (DBDMA_CNTRL_ACTIVE | + DBDMA_CNTRL_DEAD | + DBDMA_CNTRL_WAKE | + DBDMA_CNTRL_FLUSH | + DBDMA_CNTRL_PAUSE | + DBDMA_CNTRL_RUN ))); + + while (DBDMA_LD4_ENDIAN(&dmap->d_status) & DBDMA_CNTRL_RUN); +} + +void +dbdma_continue(dmap) + dbdma_regmap_t *dmap; +{ + DBDMA_ST4_ENDIAN(&dmap->d_control, + DBDMA_SET_CNTRL(DBDMA_CNTRL_RUN | DBDMA_CNTRL_WAKE) | + DBDMA_CLEAR_CNTRL(DBDMA_CNTRL_PAUSE | DBDMA_CNTRL_DEAD)); +} + +void +dbdma_pause(dmap) + dbdma_regmap_t *dmap; +{ + DBDMA_ST4_ENDIAN(&dmap->d_control,DBDMA_SET_CNTRL(DBDMA_CNTRL_PAUSE)); + + while (DBDMA_LD4_ENDIAN(&dmap->d_status) & DBDMA_CNTRL_ACTIVE) + ; +} + +dbdma_command_t * +dbdma_alloc(size) + int size; +{ + u_int buf; + + buf = (u_int)malloc(size + 0x0f, M_DEVBUF, M_WAITOK); + buf = (buf + 0x0f) & ~0x0f; + + return (dbdma_command_t *)buf; +} diff --git a/sys/arch/macppc/dev/dbdma.h b/sys/arch/macppc/dev/dbdma.h new file mode 100644 index 00000000000..eb386cd1a82 --- /dev/null +++ b/sys/arch/macppc/dev/dbdma.h @@ -0,0 +1,230 @@ +/* $OpenBSD: dbdma.h,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: dbdma.h,v 1.2 1998/08/21 16:13:28 tsubai Exp $ */ + +/* + * Copyright 1991-1998 by Open Software Foundation, Inc. + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appears in all copies and + * that both the copyright notice and this permission notice appear in + * supporting documentation. + * + * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, + * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "machine/pio.h" + +#ifndef _POWERMAC_DBDMA_H_ +#define _POWERMAC_DBDMA_H_ + +#define DBDMA_CMD_OUT_MORE 0 +#define DBDMA_CMD_OUT_LAST 1 +#define DBDMA_CMD_IN_MORE 2 +#define DBDMA_CMD_IN_LAST 3 +#define DBDMA_CMD_STORE_QUAD 4 +#define DBDMA_CMD_LOAD_QUAD 5 +#define DBDMA_CMD_NOP 6 +#define DBDMA_CMD_STOP 7 + +/* Keys */ + +#define DBDMA_KEY_STREAM0 0 +#define DBDMA_KEY_STREAM1 1 +#define DBDMA_KEY_STREAM2 2 +#define DBDMA_KEY_STREAM3 3 + +/* value 4 is reserved */ +#define DBDMA_KEY_REGS 5 +#define DBDMA_KEY_SYSTEM 6 +#define DBDMA_KEY_DEVICE 7 + +#define DBDMA_INT_NEVER 0 +#define DBDMA_INT_IF_TRUE 1 +#define DBDMA_INT_IF_FALSE 2 +#define DBDMA_INT_ALWAYS 3 + +#define DBDMA_BRANCH_NEVER 0 +#define DBDMA_BRANCH_IF_TRUE 1 +#define DBDMA_BRANCH_IF_FALSE 2 +#define DBDMA_BRANCH_ALWAYS 3 + +#define DBDMA_WAIT_NEVER 0 +#define DBDMA_WAIT_IF_TRUE 1 +#define DBDMA_WAIT_IF_FALSE 2 +#define DBDMA_WAIT_ALWAYS 3 + + +/* Channels */ + +#define DBDMA_SCSI0 0x0 +#define DBDMA_CURIO_SCSI DBDMA_SCSI0 +#define DBDMA_FLOPPY 0x1 +#define DBDMA_ETHERNET_TX 0x2 +#define DBDMA_ETHERNET_RV 0x3 +#define DBDMA_SCC_XMIT_A 0x4 +#define DBDMA_SCC_RECV_A 0x5 +#define DBDMA_SCC_XMIT_B 0x6 +#define DBDMA_SCC_RECV_B 0x7 +#define DBDMA_AUDIO_OUT 0x8 +#define DBDMA_AUDIO_IN 0x9 +#define DBDMA_SCSI1 0xA + +/* Control register values (in little endian) */ + +#define DBDMA_STATUS_MASK 0x000000ff /* Status Mask */ +#define DBDMA_CNTRL_BRANCH 0x00000100 + /* 0x200 reserved */ +#define DBDMA_CNTRL_ACTIVE 0x00000400 +#define DBDMA_CNTRL_DEAD 0x00000800 +#define DBDMA_CNTRL_WAKE 0x00001000 +#define DBDMA_CNTRL_FLUSH 0x00002000 +#define DBDMA_CNTRL_PAUSE 0x00004000 +#define DBDMA_CNTRL_RUN 0x00008000 + +#define DBDMA_SET_CNTRL(x) ( ((x) | (x) << 16) ) +#define DBDMA_CLEAR_CNTRL(x) ( (x) << 16) + + +#define DBDMA_REGMAP(channel) \ + (dbdma_regmap_t *)((v_u_char *) POWERMAC_IO(PCI_DMA_BASE_PHYS) \ + + (channel << 8)) + +/* This struct is layout in little endian format */ + +struct dbdma_command { + u_int16_t d_count; + u_int16_t d_command; + u_int32_t d_address; + u_int32_t d_cmddep; + u_int16_t d_resid; + u_int16_t d_status; +}; + +typedef struct dbdma_command dbdma_command_t; + +#define DBDMA_BUILD_CMD(d, cmd, key, interrupt, wait, branch) { \ + dbdma_st16(&(d)->d_command, \ + ((cmd) << 12) | ((key) << 8) | \ + ((interrupt) << 4) | \ + ((branch) << 2) | (wait)); \ + } + +#define DBDMA_BUILD(d, cmd, key, count, address, interrupt, wait, branch) { \ + dbdma_st16(&(d)->d_count, count); \ + dbdma_st32(&(d)->d_address, address); \ + (d)->d_resid = 0; \ + (d)->d_status = 0; \ + (d)->d_cmddep = 0; \ + dbdma_st16(&(d)->d_command, \ + ((cmd) << 12) | ((key) << 8) | \ + ((interrupt) << 4) | \ + ((branch) << 2) | (wait)); \ + } + +#if 0 +static __inline__ void +dbdma_st32(a, x) + volatile u_int32_t *a; + u_int32_t x; +{ + __asm__ volatile + ("stwbrx %0,0,%1" : : "r" (x), "r" (a) : "memory"); + __asm__ volatile ("eieio"); +} + +static __inline__ void +dbdma_st16(a, x) + volatile u_int16_t *a; + u_int16_t x; +{ + __asm__ volatile + ("sthbrx %0,0,%1" : : "r" (x), "r" (a) : "memory"); + __asm__ volatile ("eieio"); +} + +static __inline__ u_int32_t +dbdma_ld32(a) + volatile u_int32_t *a; +{ + u_int32_t swap; + + __asm__ volatile ("eieio"); + __asm__ volatile + ("lwbrx %0,0,%1" : "=r" (swap) : "r" (a)); + + return swap; +} + +static __inline__ u_int16_t +dbdma_ld16(a) + volatile u_int16_t *a; +{ + u_int16_t swap; + + __asm__ volatile ("eieio"); + __asm__ volatile + ("lhbrx %0,0,%1" : "=r" (swap) : "r" (a)); + + return swap; +} + +#define DBDMA_LD4_ENDIAN(a) dbdma_ld32(a) +#define DBDMA_ST4_ENDIAN(a, x) dbdma_st32(a, x) +#else +#define DBDMA_LD4_ENDIAN(a) in32rb(a) +#define DBDMA_ST4_ENDIAN(a, x) out32rb(a, x) +#define dbdma_st16(a,x) out16rb((a),(x)) +#define dbdma_ld16(a) in16rb(a) +#define dbdma_st32(a,x) out32rb((a),(x)) +#define dbdma_ld32(a) in32rb(a) +#endif + + +/* + * DBDMA Channel layout + * + * NOTE - This structure is in little-endian format. + */ + +struct dbdma_regmap { + u_int32_t d_control; /* Control Register */ + u_int32_t d_status; /* DBDMA Status Register */ + u_int32_t d_cmdptrhi; /* MSB of command pointer (not used yet) */ + u_int32_t d_cmdptrlo; /* LSB of command pointer */ + u_int32_t d_intselect; /* Interrupt Select */ + u_int32_t d_branch; /* Branch selection */ + u_int32_t d_wait; /* Wait selection */ + u_int32_t d_transmode; /* Transfer modes */ + u_int32_t d_dataptrhi; /* MSB of Data Pointer */ + u_int32_t d_dataptrlo; /* LSB of Data Pointer */ + u_int32_t d_reserved; /* Reserved for the moment */ + u_int32_t d_branchptrhi; /* MSB of Branch Pointer */ + u_int32_t d_branchptrlo; /* LSB of Branch Pointer */ + /* The remaining fields are undefinied and unimplemented */ +}; + +typedef volatile struct dbdma_regmap dbdma_regmap_t; + +/* DBDMA routines */ + +void dbdma_start(dbdma_regmap_t *channel, dbdma_command_t *commands); +void dbdma_stop(dbdma_regmap_t *channel); +void dbdma_flush(dbdma_regmap_t *channel); +void dbdma_reset(dbdma_regmap_t *channel); +void dbdma_continue(dbdma_regmap_t *channel); +void dbdma_pause(dbdma_regmap_t *channel); + +dbdma_command_t *dbdma_alloc(int); /* Allocate command structures */ + +#endif /* !defined(_POWERMAC_DBDMA_H_) */ diff --git a/sys/arch/macppc/dev/gpio.c b/sys/arch/macppc/dev/gpio.c new file mode 100644 index 00000000000..db4bb579f59 --- /dev/null +++ b/sys/arch/macppc/dev/gpio.c @@ -0,0 +1,187 @@ +/* $OpenBSD: gpio.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: gpio.c,v 1.2 2001/02/27 05:16:33 matt Exp $ */ + +/*- + * Copyright (C) 1998 Internet Research Institute, Inc. + * 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 + * Internet Research Institute, Inc. + * 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 <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/malloc.h> + +#include <dev/ofw/openfirm.h> + +#include <machine/autoconf.h> +#include <machine/pio.h> + +#include "adb.h" + +static void gpio_obio_attach (struct device *, struct device *, void *); +static int gpio_obio_match (struct device *, void *, void *); +static int gpio_obio_print (void *aux, const char *gpio); + +static void gpio_gpio_attach (struct device *, struct device *, void *); +static int gpio_gpio_match (struct device *, void *, void *); +static int gpio_intr (void *); + +struct gpio_softc { + struct device sc_dev; + u_int8_t *sc_port; +}; + +struct cfattach gpio_obio_ca = { + sizeof(struct gpio_softc), gpio_obio_match, gpio_obio_attach +}; + +struct cfattach gpio_gpio_ca = { + sizeof(struct gpio_softc), gpio_gpio_match, gpio_gpio_attach +}; + +struct cfdriver gpio_cd = { + NULL, "gpio_obio", DV_DULL +}; + +int +gpio_obio_match(struct device *parent, void *cf, void *aux) +{ + struct confargs *ca = aux; + + if (strcmp(ca->ca_name, "gpio") != 0) + return 0; + + if (ca->ca_nreg == 0) + return 0; + + return 1; +} + +void +gpio_obio_attach(struct device *parent, struct device *self, void *aux) +{ + struct gpio_softc *sc = (struct gpio_softc *)self; + struct confargs *ca = aux, ca2; + int child; + int namelen; + int intr[6]; + u_int reg[20]; + char name[32]; + + printf("\n"); + + sc->sc_port = mapiodev(ca->ca_baseaddr + ca->ca_reg[0], ca->ca_reg[1]); + + ca2.ca_baseaddr = ca->ca_baseaddr; + for (child = OF_child(ca->ca_node); child; child = OF_peer(child)) { + namelen = OF_getprop(child, "name", name, sizeof(name)); + if (namelen < 0) + continue; + if (namelen >= sizeof(name)) + continue; + + name[namelen] = 0; + ca2.ca_name = name; + ca2.ca_node = child; + + ca2.ca_nreg = OF_getprop(child, "reg", reg, sizeof(reg)); + ca2.ca_nintr = OF_getprop(child, "AAPL,interrupts", intr, + sizeof(intr)); + if (ca2.ca_nintr == -1) + ca2.ca_nintr = OF_getprop(child, "interrupts", intr, + sizeof(intr)); + + ca2.ca_reg = reg; + ca2.ca_intr = intr; + + config_found(self, &ca2, gpio_obio_print); + } +} + +int +gpio_obio_print(void *aux, const char *gpio) +{ + struct confargs *ca = aux; + + if (gpio) + printf("%s at %s", ca->ca_name, gpio); + + if (ca->ca_nreg > 0) + printf(" offset 0x%x", ca->ca_reg[0]); + + return UNCONF; +} + +int +gpio_gpio_match(struct device *parent, void *cf, void *aux) +{ + struct confargs *ca = aux; + + if (strcmp(ca->ca_name, "extint-gpio1") != 0) + return 0; + + if (ca->ca_nintr < 0) + return 0; + + return 1; +} + +void +gpio_gpio_attach(struct device *parent, struct device *self, void *aux) +{ + struct gpio_softc *sc = (struct gpio_softc *)self; + struct confargs *ca = aux; + + + sc->sc_port = ((struct gpio_softc *) parent)->sc_port; + mac_intr_establish(parent, ca->ca_intr[0], IST_LEVEL, IPL_HIGH, + gpio_intr, sc, "gpio/adb"); + + printf(" irq %d\n", ca->ca_intr[0]); +} + +#if NADB > 0 +extern int adb_intr (void *); +extern struct cfdriver adb_cd; +#endif + +int +gpio_intr(void *arg) +{ + int rv = 0; + +#if NADB > 0 + if (adb_cd.cd_devs[0] != NULL) + rv = adb_intr(adb_cd.cd_devs[0]); +#endif + + return rv; +} diff --git a/sys/arch/macppc/dev/if_bm.c b/sys/arch/macppc/dev/if_bm.c new file mode 100644 index 00000000000..61eb8e857b0 --- /dev/null +++ b/sys/arch/macppc/dev/if_bm.c @@ -0,0 +1,1083 @@ +/* $OpenBSD: if_bm.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: if_bm.c,v 1.1 1999/01/01 01:27:52 tsubai Exp $ */ + +/*- + * Copyright (C) 1998, 1999 Tsubai Masanari. 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. 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. + */ + +#ifdef __NetBSD__ +#include "opt_inet.h" +#include "opt_ns.h" +#endif /* __NetBSD__ */ +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/systm.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <net/if_media.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <vm/vm.h> + +#include <dev/ofw/openfirm.h> + +#include <machine/autoconf.h> +#include <machine/pio.h> + +#include <macppc/mac/dbdma.h> +#include <macppc/mac/if_bmreg.h> + +#define BMAC_TXBUFS 2 +#define BMAC_RXBUFS 16 +#define BMAC_BUFLEN 2048 + +struct bmac_softc { + struct device sc_dev; +#ifdef __OpenBSD__ + struct arpcom arpcom; /* per-instance network data */ +#define sc_if arpcom.ac_if +#define sc_enaddr arpcom.ac_enaddr +#else + struct ethercom sc_ethercom; +#define sc_if sc_ethercom.ec_if + u_char sc_enaddr[6]; +#endif + struct ifmedia sc_media; + vaddr_t sc_regs; + dbdma_regmap_t *sc_txdma; + dbdma_regmap_t *sc_rxdma; + dbdma_command_t *sc_txcmd; + dbdma_command_t *sc_rxcmd; + caddr_t sc_txbuf; + caddr_t sc_rxbuf; + int sc_rxlast; + int sc_flags; + int sc_debug; + int txcnt_outstanding; +}; + +#define BMAC_BMACPLUS 0x01 + +extern u_int *heathrow_FCR; + +static __inline int bmac_read_reg __P((struct bmac_softc *, int)); +static __inline void bmac_write_reg __P((struct bmac_softc *, int, int)); +static __inline void bmac_set_bits __P((struct bmac_softc *, int, int)); +static __inline void bmac_reset_bits __P((struct bmac_softc *, int, int)); + +static int bmac_match __P((struct device *, void *, void *)); +static void bmac_attach __P((struct device *, struct device *, void *)); +static void bmac_reset_chip __P((struct bmac_softc *)); +static void bmac_init __P((struct bmac_softc *)); +static void bmac_init_dma __P((struct bmac_softc *)); +static int bmac_intr __P((void *)); +#ifdef WHY_IS_THIS_XXXX +static int bmac_tx_intr __P((void *)); +#endif /* WHY_IS_THIS_XXXX */ +static int bmac_rint __P((void *)); +static void bmac_reset __P((struct bmac_softc *)); +static void bmac_stop __P((struct bmac_softc *)); +static void bmac_start __P((struct ifnet *)); +static void bmac_transmit_packet __P((struct bmac_softc *, void *, int)); +static int bmac_put __P((struct bmac_softc *, caddr_t, struct mbuf *)); +static struct mbuf *bmac_get __P((struct bmac_softc *, caddr_t, int)); +static void bmac_watchdog __P((struct ifnet *)); +static int bmac_ioctl __P((struct ifnet *, u_long, caddr_t)); +static int bmac_mediachange __P((struct ifnet *)); +static void bmac_mediastatus __P((struct ifnet *, struct ifmediareq *)); +static void bmac_setladrf __P((struct bmac_softc *)); +void bmac_init_mif __P((struct bmac_softc *sc)); +u_int bmac_mif_readbits __P((struct bmac_softc *sc, int nb)); +void bmac_mif_writebits __P((struct bmac_softc *sc, u_int val, int nb)); +u_int bmac_mif_read __P((struct bmac_softc *sc, u_int addr)); +void bmac_mif_write __P((struct bmac_softc *sc, u_int addr, u_int val)); + +struct cfattach bm_ca = { + sizeof(struct bmac_softc), bmac_match, bmac_attach +}; + +struct cfdriver bm_cd = { + NULL, "bm", DV_IFNET +}; + +int +bmac_read_reg(sc, off) + struct bmac_softc *sc; + int off; +{ + return in16rb(sc->sc_regs + off); +} + +void +bmac_write_reg(sc, off, val) + struct bmac_softc *sc; + int off, val; +{ + out16rb(sc->sc_regs + off, val); +} + +void +bmac_set_bits(sc, off, val) + struct bmac_softc *sc; + int off, val; +{ + val |= bmac_read_reg(sc, off); + bmac_write_reg(sc, off, val); +} + +void +bmac_reset_bits(sc, off, val) + struct bmac_softc *sc; + int off, val; +{ + bmac_write_reg(sc, off, bmac_read_reg(sc, off) & ~val); +} + +int +bmac_match(parent, cf, aux) + struct device *parent; + void *cf; + void *aux; +{ + struct confargs *ca = aux; + + if (ca->ca_nreg < 24 || ca->ca_nintr < 12) + return 0; + + if (strcmp(ca->ca_name, "bmac") == 0) /* bmac */ + return 1; + if (strcmp(ca->ca_name, "ethernet") == 0) /* bmac+ */ + return 1; + + return 0; +} + +void +bmac_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct confargs *ca = aux; + struct bmac_softc *sc = (void *)self; + struct ifnet *ifp = &sc->sc_if; + u_char laddr[6]; + + sc->sc_flags =0; + if (strcmp(ca->ca_name, "ethernet") == 0) { + sc->sc_flags |= BMAC_BMACPLUS; + } + + ca->ca_reg[0] += ca->ca_baseaddr; + ca->ca_reg[2] += ca->ca_baseaddr; + ca->ca_reg[4] += ca->ca_baseaddr; + + sc->sc_regs = (vaddr_t)mapiodev(ca->ca_reg[0], NBPG); + + bmac_write_reg(sc, INTDISABLE, NoEventsMask); + + if (OF_getprop(ca->ca_node, "local-mac-address", laddr, 6) == -1 && + OF_getprop(ca->ca_node, "mac-address", laddr, 6) == -1) { + printf(": cannot get mac-address\n"); + return; + } + bcopy(laddr, sc->arpcom.ac_enaddr, 6); + + sc->sc_txdma = mapiodev(ca->ca_reg[2], 0x100); + sc->sc_rxdma = mapiodev(ca->ca_reg[4], 0x100); + sc->sc_txcmd = dbdma_alloc(BMAC_TXBUFS * sizeof(dbdma_command_t)); + sc->sc_rxcmd = dbdma_alloc((BMAC_RXBUFS + 1) * sizeof(dbdma_command_t)); + sc->sc_txbuf = malloc(BMAC_BUFLEN * BMAC_TXBUFS, M_DEVBUF, M_NOWAIT); + sc->sc_rxbuf = malloc(BMAC_BUFLEN * BMAC_RXBUFS, M_DEVBUF, M_NOWAIT); + if (sc->sc_txbuf == NULL || sc->sc_rxbuf == NULL || + sc->sc_txcmd == NULL || sc->sc_rxcmd == NULL) { + printf("cannot allocate memory\n"); + return; + } + + printf(" irq %d,%d: address %s\n", ca->ca_intr[0], ca->ca_intr[2], + ether_sprintf(laddr)); + + mac_intr_establish(parent, ca->ca_intr[0], IST_LEVEL, IPL_NET, + bmac_intr, sc, "bmac intr"); +#ifdef WHY_IS_THIS_XXXX + mac_intr_establish(parent, ca->ca_intr[1], IST_LEVEL, IPL_NET, + bmac_tx_intr, sc, "bmac_tx"); +#endif /* WHY_IS_THIS_XXXX */ + mac_intr_establish(parent, ca->ca_intr[2], IST_LEVEL, IPL_NET, + bmac_rint, sc, "bmac rint"); + + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_ioctl = bmac_ioctl; + ifp->if_start = bmac_start; + ifp->if_flags = + IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST; + ifp->if_watchdog = bmac_watchdog; + + ifmedia_init(&sc->sc_media, 0, bmac_mediachange, bmac_mediastatus); + ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_10_T, 0, NULL); + ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_10_T); + + bmac_reset_chip(sc); + + if_attach(ifp); + ether_ifattach(ifp); +} + +/* + * Reset and enable bmac by heathrow FCR. + */ +void +bmac_reset_chip(sc) + struct bmac_softc *sc; +{ + u_int v; + + dbdma_reset(sc->sc_txdma); + dbdma_reset(sc->sc_rxdma); + + v = in32rb(heathrow_FCR); + + v |= EnetEnable; + out32rb(heathrow_FCR, v); + delay(50000); + + /* assert reset */ + v |= ResetEnetCell; + out32rb(heathrow_FCR, v); + delay(70000); + + /* deassert reset */ + v &= ~ResetEnetCell; + out32rb(heathrow_FCR, v); + delay(50000); + + /* enable */ + v |= EnetEnable; + out32rb(heathrow_FCR, v); + delay(50000); + + /* make certain they stay set? */ + out32rb(heathrow_FCR, v); + v = in32rb(heathrow_FCR); +} + +void +bmac_init(sc) + struct bmac_softc *sc; +{ + struct ifnet *ifp = &sc->sc_if; + struct ether_header *eh; + caddr_t data; + int tb; + u_short *p; + + bmac_init_mif(sc); + bmac_reset_chip(sc); + + bmac_write_reg(sc, RXRST, RxResetValue); + bmac_write_reg(sc, TXRST, TxResetBit); + + /* Wait for reset completion. */ + do { + delay(10000); + } while (bmac_read_reg(sc, TXRST) & TxResetBit); + + if (! (sc->sc_flags & BMAC_BMACPLUS)) { + bmac_set_bits(sc, XCVRIF, ClkBit|SerialMode|COLActiveLow); + delay(100); + } + + __asm __volatile ("mftb %0" : "=r"(tb)); + bmac_write_reg(sc, RSEED, tb); + bmac_set_bits(sc, XIFC, TxOutputEnable); + bmac_read_reg(sc, PAREG); + + /* Reset various counters. */ + bmac_write_reg(sc, NCCNT, 0); + bmac_write_reg(sc, NTCNT, 0); + bmac_write_reg(sc, EXCNT, 0); + bmac_write_reg(sc, LTCNT, 0); + bmac_write_reg(sc, FRCNT, 0); + bmac_write_reg(sc, LECNT, 0); + bmac_write_reg(sc, AECNT, 0); + bmac_write_reg(sc, FECNT, 0); + bmac_write_reg(sc, RXCV, 0); + + /* Set tx fifo information. */ + bmac_write_reg(sc, TXTH, 4); /* 4 octets before tx starts */ + + bmac_write_reg(sc, TXFIFOCSR, 0); + bmac_write_reg(sc, TXFIFOCSR, TxFIFOEnable); + + /* Set rx fifo information. */ + bmac_write_reg(sc, RXFIFOCSR, 0); + bmac_write_reg(sc, RXFIFOCSR, RxFIFOEnable); + + /* Clear status register. */ + bmac_read_reg(sc, STATUS); + + bmac_write_reg(sc, HASH3, 0); + bmac_write_reg(sc, HASH2, 0); + bmac_write_reg(sc, HASH1, 0); + bmac_write_reg(sc, HASH0, 0); + + /* Set MAC address. */ + p = (u_short *)sc->sc_enaddr; + bmac_write_reg(sc, MADD0, *p++); + bmac_write_reg(sc, MADD1, *p++); + bmac_write_reg(sc, MADD2, *p); + + bmac_write_reg(sc, RXCFG, + RxCRCEnable | RxHashFilterEnable | RxRejectOwnPackets); + + if (ifp->if_flags & IFF_PROMISC) + bmac_set_bits(sc, RXCFG, RxPromiscEnable); + + bmac_init_dma(sc); + + /* Enable TX/RX */ + bmac_set_bits(sc, RXCFG, RxMACEnable); + bmac_set_bits(sc, TXCFG, TxMACEnable); + + bmac_write_reg(sc, INTDISABLE, NormalIntEvents); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_timer = 0; + + data = sc->sc_txbuf; + eh = (struct ether_header *)data; + + bzero(data, sizeof(eh) + ETHERMIN); + bcopy(sc->sc_enaddr, eh->ether_dhost, ETHER_ADDR_LEN); + bcopy(sc->sc_enaddr, eh->ether_shost, ETHER_ADDR_LEN); + bmac_transmit_packet(sc, data, sizeof(eh) + ETHERMIN); + + bmac_start(ifp); +} + +void +bmac_init_dma(sc) + struct bmac_softc *sc; +{ + dbdma_command_t *cmd = sc->sc_rxcmd; + int i; + + dbdma_reset(sc->sc_txdma); + dbdma_reset(sc->sc_rxdma); + + bzero(sc->sc_txcmd, BMAC_TXBUFS * sizeof(dbdma_command_t)); + bzero(sc->sc_rxcmd, (BMAC_RXBUFS + 1) * sizeof(dbdma_command_t)); + + for (i = 0; i < BMAC_RXBUFS; i++) { + DBDMA_BUILD(cmd, DBDMA_CMD_IN_LAST, 0, BMAC_BUFLEN, + vtophys((vaddr_t)(sc->sc_rxbuf + BMAC_BUFLEN * i)), + DBDMA_INT_ALWAYS, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + cmd++; + } + DBDMA_BUILD(cmd, DBDMA_CMD_NOP, 0, 0, 0, + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_ALWAYS); + dbdma_st32(&cmd->d_cmddep, vtophys((vaddr_t)sc->sc_rxcmd)); + + sc->sc_rxlast = 0; + + dbdma_start(sc->sc_rxdma, sc->sc_rxcmd); +} + +#ifdef WHY_IS_THIS_XXXX +int +bmac_tx_intr(v) + void *v; +{ + struct bmac_softc *sc = v; + + sc->sc_if.if_flags &= ~IFF_OACTIVE; + sc->sc_if.if_timer = 0; + sc->sc_if.if_opackets++; + bmac_start(&sc->sc_if); + +#ifndef BMAC_DEBUG + printf("bmac_tx_intr \n"); +#endif + #if 0 + stat = bmac_read_reg(sc, STATUS); + if (stat == 0) { + printf("tx intr fired, but status 0\n"); + return 0; + } + + + if (stat & IntFrameSent) { + sc->sc_if.if_flags &= ~IFF_OACTIVE; + sc->sc_if.if_timer = 0; + sc->sc_if.if_opackets++; + bmac_start(&sc->sc_if); + } + #endif + return 1; +} +#endif /* WHY_IS_THIS_XXXX */ +int +bmac_intr(v) + void *v; +{ + struct bmac_softc *sc = v; + int stat; + +#ifdef BMAC_DEBUG + printf("bmac_intr called\n"); +#endif + stat = bmac_read_reg(sc, STATUS); + if (stat == 0) + return 0; + +#ifdef BMAC_DEBUG + printf("bmac_intr status = 0x%x\n", stat); +#endif + + if (stat & IntFrameSent) { + sc->sc_if.if_flags &= ~IFF_OACTIVE; + sc->sc_if.if_timer = 0; + sc->sc_if.if_opackets++; + bmac_start(&sc->sc_if); + } + + /* XXX should do more! */ + + return 1; +} + +int +bmac_rint(v) + void *v; +{ + struct bmac_softc *sc = v; + struct ifnet *ifp = &sc->sc_if; + struct mbuf *m; + dbdma_command_t *cmd; + int status, resid, count, datalen; + int i, n; + void *data; +#ifdef BMAC_DEBUG + printf("bmac_rint() called\n"); +#endif + + i = sc->sc_rxlast; + for (n = 0; n < BMAC_RXBUFS; n++, i++) { + if (i == BMAC_RXBUFS) + i = 0; + cmd = &sc->sc_rxcmd[i]; + status = dbdma_ld16(&cmd->d_status); + resid = dbdma_ld16(&cmd->d_resid); + +#ifdef BMAC_DEBUG + if (status != 0 && status != 0x8440 && status != 0x9440) + printf("bmac_rint status = 0x%x\n", status); +#endif + + if ((status & DBDMA_CNTRL_ACTIVE) == 0) /* 0x9440 | 0x8440 */ + continue; + count = dbdma_ld16(&cmd->d_count); + datalen = count - resid; + if (datalen < sizeof(struct ether_header)) { + printf("%s: short packet len = %d\n", + ifp->if_xname, datalen); + goto next; + } + DBDMA_BUILD_CMD(cmd, DBDMA_CMD_STOP, 0, 0, 0, 0); + data = sc->sc_rxbuf + BMAC_BUFLEN * i; + m = bmac_get(sc, data, datalen); + + if (m == NULL) { + ifp->if_ierrors++; + goto next; + } + +#if NBPFILTER > 0 + /* + * Check if there's a BPF listener on this interface. + * If so, hand off the raw packet to BPF. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif +#ifdef __OpenBSD__ + ether_input_mbuf(ifp, m); +#else + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, data, m); +#endif + ifp->if_ipackets++; + +next: + DBDMA_BUILD_CMD(cmd, DBDMA_CMD_IN_LAST, 0, DBDMA_INT_ALWAYS, + DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + + cmd->d_status = 0; + cmd->d_resid = 0; + sc->sc_rxlast = i + 1; + } + dbdma_continue(sc->sc_rxdma); + + return 1; +} + +void +bmac_reset(sc) + struct bmac_softc *sc; +{ + int s; + + s = splnet(); + bmac_init(sc); + splx(s); +} + +void +bmac_stop(sc) + struct bmac_softc *sc; +{ + struct ifnet *ifp = &sc->sc_if; + int s; + + s = splnet(); + + /* Disable TX/RX. */ + bmac_reset_bits(sc, TXCFG, TxMACEnable); + bmac_reset_bits(sc, RXCFG, RxMACEnable); + + /* Disable all interrupts. */ + bmac_write_reg(sc, INTDISABLE, NoEventsMask); + + dbdma_stop(sc->sc_txdma); + dbdma_stop(sc->sc_rxdma); + + ifp->if_flags &= ~(IFF_UP | IFF_RUNNING); + ifp->if_timer = 0; + + splx(s); +} + +void +bmac_start(ifp) + struct ifnet *ifp; +{ + struct bmac_softc *sc = ifp->if_softc; + struct mbuf *m; + int tlen; + + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + return; + + while (1) { + if (ifp->if_flags & IFF_OACTIVE) + return; + + IF_DEQUEUE(&ifp->if_snd, m); + if (m == 0) + break; +#if NBPFILTER > 0 + /* + * If BPF is listening on this interface, let it see the + * packet before we commit it to the wire. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif + + ifp->if_flags |= IFF_OACTIVE; + tlen = bmac_put(sc, sc->sc_txbuf, m); + + /* 5 seconds to watch for failing to transmit */ + ifp->if_timer = 5; + ifp->if_opackets++; /* # of pkts */ + + bmac_transmit_packet(sc, sc->sc_txbuf, tlen); + } +} + +void +bmac_transmit_packet(sc, buff, len) + struct bmac_softc *sc; + void *buff; + int len; +{ + dbdma_command_t *cmd = sc->sc_txcmd; + vaddr_t va = (vaddr_t)buff; + +#ifdef BMAC_DEBUG + if (vtophys(va) + len - 1 != vtophys(va + len - 1)) + panic("bmac_transmit_packet"); +#endif + + DBDMA_BUILD(cmd, DBDMA_CMD_OUT_LAST, 0, len, vtophys(va), + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + cmd++; + DBDMA_BUILD(cmd, DBDMA_CMD_STOP, 0, 0, 0, + DBDMA_INT_ALWAYS, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + + dbdma_start(sc->sc_txdma, sc->sc_txcmd); +} + +int +bmac_put(sc, buff, m) + struct bmac_softc *sc; + caddr_t buff; + struct mbuf *m; +{ + struct mbuf *n; + int len, tlen = 0; + + for (; m; m = n) { + len = m->m_len; + if (len == 0) { + MFREE(m, n); + continue; + } + bcopy(mtod(m, caddr_t), buff, len); + buff += len; + tlen += len; + MFREE(m, n); + } + if (tlen > NBPG) + panic("%s: putpacket packet overflow", sc->sc_dev.dv_xname); + + return tlen; +} + +struct mbuf * +bmac_get(sc, pkt, totlen) + struct bmac_softc *sc; + caddr_t pkt; + int totlen; +{ + struct mbuf *m; + struct mbuf *top, **mp; + int len; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + return 0; + m->m_pkthdr.rcvif = &sc->sc_if; + m->m_pkthdr.len = totlen; + len = MHLEN; + top = 0; + mp = ⊤ + + while (totlen > 0) { + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(top); + return 0; + } + len = MLEN; + } + if (totlen >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + m_freem(top); + return 0; + } + len = MCLBYTES; + } + m->m_len = len = min(totlen, len); + bcopy(pkt, mtod(m, caddr_t), len); + pkt += len; + totlen -= len; + *mp = m; + mp = &m->m_next; + } + + return top; +} + +void +bmac_watchdog(ifp) + struct ifnet *ifp; +{ + struct bmac_softc *sc = ifp->if_softc; + + bmac_reset_bits(sc, RXCFG, RxMACEnable); + bmac_reset_bits(sc, TXCFG, TxMACEnable); + + printf("%s: device timeout\n", ifp->if_xname); + ifp->if_oerrors++; + + bmac_reset(sc); +} + +int +bmac_ioctl(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct bmac_softc *sc = ifp->if_softc; + struct ifaddr *ifa = (struct ifaddr *)data; + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + + s = splnet(); + + switch (cmd) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + bmac_init(sc); +#ifdef __OpenBSD__ + arp_ifinit(&sc->arpcom, ifa); +#else + arp_ifinit(ifp, ifa); +#endif + break; +#endif +#ifdef NS + case AF_NS: + { + struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; + + if (ns_nullhost(*ina)) + ina->x_host = + *(union ns_host *)LLADDR(ifp->if_sadl); + else { + bcopy(ina->x_host.c_host, + LLADDR(ifp->if_sadl), + sizeof(sc->sc_enaddr)); + } + /* Set new address. */ + bmac_init(sc); + break; + } +#endif + default: + bmac_init(sc); + break; + } + break; + + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && + (ifp->if_flags & IFF_RUNNING) != 0) { + /* + * If interface is marked down and it is running, then + * stop it. + */ + bmac_stop(sc); + ifp->if_flags &= ~IFF_RUNNING; + } else if ((ifp->if_flags & IFF_UP) != 0 && + (ifp->if_flags & IFF_RUNNING) == 0) { + /* + * If interface is marked up and it is stopped, then + * start it. + */ + bmac_init(sc); + } else { + /* + * Reset the interface to pick up changes in any other + * flags that affect hardware registers. + */ + /*bmac_stop(sc);*/ + bmac_init(sc); + } +#ifdef BMAC_DEBUG + if (ifp->if_flags & IFF_DEBUG) + sc->sc_debug = 1; + else + sc->sc_debug = 0; +#endif + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: +#if defined(__OpenBSD__) + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->arpcom) : + ether_delmulti(ifr, &sc->arpcom); +#else + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->sc_ethercom) : + ether_delmulti(ifr, &sc->sc_ethercom); +#endif + + if (error == ENETRESET) { + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + bmac_init(sc); + bmac_setladrf(sc); + error = 0; + } + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); + break; + + default: + error = EINVAL; + } + + splx(s); + return error; +} + +int +bmac_mediachange(ifp) + struct ifnet *ifp; +{ + return EINVAL; +} + +void +bmac_mediastatus(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + if ((ifp->if_flags & IFF_UP) == 0) + return; + + ifmr->ifm_status = IFM_AVALID; + ifmr->ifm_status |= IFM_ACTIVE; +} + +#define MC_POLY_BE 0x04c11db7UL /* mcast crc, big endian */ +#define MC_POLY_LE 0xedb88320UL /* mcast crc, little endian */ + +/* + * Set up the logical address filter. + */ +void +bmac_setladrf(sc) + struct bmac_softc *sc; +{ + struct ifnet *ifp = &sc->sc_if; + struct ether_multi *enm; + struct ether_multistep step; + int i, j; + u_int32_t crc; + u_int16_t hash[4]; + u_int8_t octet; + + /* + * Set up multicast address filter by passing all multicast addresses + * through a crc generator, and then using the high order 6 bits as an + * index into the 64 bit logical address filter. The high order bit + * selects the word, while the rest of the bits select the bit within + * the word. + */ + + if (ifp->if_flags & IFF_ALLMULTI) + goto allmulti; + + if (ifp->if_flags & IFF_PROMISC) { + bmac_set_bits(sc, RXCFG, RxPromiscEnable); + goto allmulti; + } + + hash[3] = hash[2] = hash[1] = hash[0] = 0; +#ifdef __OpenBSD__ + ETHER_FIRST_MULTI(step, &sc->arpcom, enm); +#else + ETHER_FIRST_MULTI(step, &sc->sc_ethercom, enm); +#endif + while (enm != NULL) { + if (bcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) { + /* + * We must listen to a range of multicast addresses. + * For now, just accept all multicasts, rather than + * trying to set only those filter bits needed to match + * the range. (At this time, the only use of address + * ranges is for IP multicast routing, for which the + * range is big enough to require all bits set.) + */ + goto allmulti; + } + + crc = 0xffffffff; + for (i = 0; i < ETHER_ADDR_LEN; i++) { + octet = enm->enm_addrlo[i]; + + for (j = 0; j < 8; j++) { + if ((crc & 1) ^ (octet & 1)) { + crc >>= 1; + crc ^= MC_POLY_LE; + } + else + crc >>= 1; + octet >>= 1; + } + } + + /* Just want the 6 most significant bits. */ + crc >>= 26; + + /* Set the corresponding bit in the filter. */ + hash[crc >> 4] |= 1 << (crc & 0xf); + + ETHER_NEXT_MULTI(step, enm); + } + bmac_write_reg(sc, HASH3, hash[3]); + bmac_write_reg(sc, HASH2, hash[2]); + bmac_write_reg(sc, HASH1, hash[1]); + bmac_write_reg(sc, HASH0, hash[0]); + ifp->if_flags &= ~IFF_ALLMULTI; + return; + +allmulti: + ifp->if_flags |= IFF_ALLMULTI; + bmac_write_reg(sc, HASH3, 0xffff); + bmac_write_reg(sc, HASH2, 0xffff); + bmac_write_reg(sc, HASH1, 0xffff); + bmac_write_reg(sc, HASH0, 0xffff); +} + +#define MIFDELAY delay(1) + +u_int +bmac_mif_readbits(sc, nb) + struct bmac_softc *sc; + int nb; +{ + unsigned int val = 0; + + while (--nb >= 0) { + bmac_write_reg(sc, MIFCSR, 0); + MIFDELAY; + if (bmac_read_reg(sc, MIFCSR) & 8) + val |= 1 << nb; + bmac_write_reg(sc, MIFCSR, 1); + MIFDELAY; + } + bmac_write_reg(sc, MIFCSR, 0); + MIFDELAY; + bmac_write_reg(sc, MIFCSR, 1); + MIFDELAY; + return val; +} + +void +bmac_mif_writebits(sc, val, nb) + struct bmac_softc *sc; + u_int val; + int nb; +{ + int b; + + while (--nb >= 0) { + b = (val & (1 << nb))? 6: 4; + bmac_write_reg(sc, MIFCSR, b); + MIFDELAY; + bmac_write_reg(sc, MIFCSR, b|1); + MIFDELAY; + } +} + +u_int +bmac_mif_read(sc, addr) + struct bmac_softc *sc; + u_int addr; +{ + u_int val; + + bmac_write_reg(sc, MIFCSR, 4); + MIFDELAY; + bmac_mif_writebits(sc, ~0U, 32); + bmac_mif_writebits(sc, 6, 4); + bmac_mif_writebits(sc, addr, 10); + bmac_write_reg(sc, MIFCSR, 2); + MIFDELAY; + bmac_write_reg(sc, MIFCSR, 1); + MIFDELAY; + val = bmac_mif_readbits(sc, 17); + bmac_write_reg(sc, MIFCSR, 4); + MIFDELAY; + /* printk(KERN_DEBUG "bmac_mif_read(%x) -> %x\n", addr, val); */ + return val; +} + +void +bmac_mif_write(sc, addr, val) + struct bmac_softc *sc; + u_int addr; + u_int val; +{ + bmac_write_reg(sc, MIFCSR, 4); + MIFDELAY; + bmac_mif_writebits(sc, ~0U, 32); + bmac_mif_writebits(sc, 5, 4); + bmac_mif_writebits(sc, addr, 10); + bmac_mif_writebits(sc, 2, 2); + bmac_mif_writebits(sc, val, 16); + bmac_mif_writebits(sc, 3, 2); +} + +void +bmac_init_mif(sc) + struct bmac_softc *sc; +{ + int id; + if (sc->sc_flags & BMAC_BMACPLUS) { + id = bmac_mif_read(sc,2); + switch (id) { + case 0x7810: + if (bmac_mif_read(sc,4) == 0xa1) { + bmac_mif_write(sc, 0, 0x1000); + } else { + bmac_mif_write(sc, 4, 0xa1); + bmac_mif_write(sc, 0, 0x1200); + } +#if 0 + /* DEBUGGING */ + printf("mif 0 %x\n", bmac_mif_read(sc, 0)); + printf("mif 4 %x\n", bmac_mif_read(sc, 4)); +#endif + break; + default: + printf("bmac mif id %x not regcognized\n", id); + /* nothing */ + } + } + return; +} diff --git a/sys/arch/macppc/dev/if_bmreg.h b/sys/arch/macppc/dev/if_bmreg.h new file mode 100644 index 00000000000..8506808e952 --- /dev/null +++ b/sys/arch/macppc/dev/if_bmreg.h @@ -0,0 +1,141 @@ +/* $OpenBSD: if_bmreg.h,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: if_bmreg.h,v 1.1 1999/01/01 01:27:52 tsubai Exp $ */ + +/* + * Copyright 1991-1998 by Open Software Foundation, Inc. + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appears in all copies and + * that both the copyright notice and this permission notice appear in + * supporting documentation. + * + * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, + * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* -------------------------------------------------------------------- */ +/* Heathrow (F)eature (C)ontrol (R)egister Addresses */ +/* -------------------------------------------------------------------- */ +#define EnetEnable 0x60000000 /* enable Enet Xcvr/Controller */ +#define ResetEnetCell 0x80000000 /* reset Enet cell */ + +/* -------------------------------------------------------------------- */ +/* BigMac Register Numbers & Bit Assignments */ +/* -------------------------------------------------------------------- */ +#define XIFC 0x0000 +#define TxOutputEnable 0x0001 +#define MIILoopbackBits 0x0006 +#define MIIBufferEnable 0x0008 +#define SQETestEnable 0x0010 +#define LinkStatus 0x0100 +#define TXFIFOCSR 0x0100 +#define TxFIFOEnable 0x0001 +#define TxFIFO128 0x0000 +#define TXTH 0x0110 +#define RXFIFOCSR 0x0120 +#define RxFIFOEnable TxFIFOEnable +#define RxFIFO128 TxFIFO128 +#define MEMADD 0x0130 +#define MEMDATAHI 0x0140 +#define MEMDATALO 0x0150 +#define XCVRIF 0x0160 +#define COLActiveLow 0x0002 +#define SerialMode 0x0004 +#define ClkBit 0x0008 +#define CHIPID 0x0170 +#define MIFCSR 0x0180 +#define SROMCSR 0x0190 +#define TXPNTR 0x01A0 +#define RXPNTR 0x01B0 +#define STATUS 0x0200 +#define INTDISABLE 0x0210 +#define IntFrameReceived 0x0001 +#define IntRxFrameCntExp 0x0002 +#define IntRxAlignCntExp 0x0004 +#define IntRxCRCCntExp 0x0008 +#define IntRxLenCntExp 0x0010 +#define IntRxOverFlow 0x0020 +#define IntRxCodeViolation 0x0040 +#define IntSQETestError 0x0080 +#define IntFrameSent 0x0100 +#define IntTxUnderrun 0x0200 +#define IntTxMaxSizeError 0x0400 +#define IntTxNormalCollExp 0x0800 +#define IntTxExcessCollExp 0x1000 +#define IntTxLateCollExp 0x2000 +#define IntTxNetworkCollExp 0x4000 +#define IntTxDeferTimerExp 0x8000 +#define NormalIntEvents ~(IntFrameSent) +#define NoEventsMask 0xFFFF + +#define TxNeverGiveUp 0x0400 +#define TXRST 0x0420 +#define TxResetBit 0x0001 +#define TXCFG 0x0430 +#define TxMACEnable 0x0001 +#define TxThreshold 0x0004 +#define IPG1 0x0440 +#define IPG2 0x0450 +#define ALIMIT 0x0460 +#define SLOT 0x0470 +#define PALEN 0x0480 +#define PAPAT 0x0490 +#define TXSFD 0x04A0 +#define JAM 0x04B0 +#define TXMAX 0x04C0 +#define TXMIN 0x04D0 +#define PAREG 0x04E0 +#define DCNT 0x04F0 +#define NCCNT 0x0500 +#define NTCNT 0x0510 +#define EXCNT 0x0520 +#define LTCNT 0x0530 +#define RSEED 0x0540 +#define TXSM 0x0550 +#define RXRST 0x0620 +#define RxResetValue 0x0000 +#define RXCFG 0x0630 +#define RxMACEnable 0x0001 +#define ReservedValue 0x0004 +#define RxPromiscEnable 0x0040 +#define RxCRCEnable 0x0100 +#define RxRejectOwnPackets 0x0200 +#define RxHashFilterEnable 0x0800 +#define RxAddrFilterEnable 0x1000 +#define RXMAX 0x0640 +#define RXMIN 0x0650 +#define MADD2 0x0660 +#define MADD1 0x0670 +#define MADD0 0x0680 +#define FRCNT 0x0690 +#define LECNT 0x06A0 +#define AECNT 0x06B0 +#define FECNT 0x06C0 +#define RXSM 0x06D0 +#define RXCV 0x06E0 +#define HASH3 0x0700 +#define HASH2 0x0710 +#define HASH1 0x0720 +#define HASH0 0x0730 +#define AFR2 0x0740 +#define AFR1 0x0750 +#define AFR0 0x0760 +#define AFCR 0x0770 +#define EnableAllCompares 0x0fff + +/* -------------------------------------------------------------------- */ +/* Misc. Bit definitions for BMac Status word */ +/* -------------------------------------------------------------------- */ +#define RxAbortBit 0x8000 /* status bit in BMac status for rx packets */ +#define RxLengthMask 0x3FFF /* bits that determine length of rx packets */ + +#define NETWORK_BUFSIZE (ETHERMAXPACKET + ETHERCRC + 2) diff --git a/sys/arch/macppc/dev/if_gm.c b/sys/arch/macppc/dev/if_gm.c new file mode 100644 index 00000000000..11093c25096 --- /dev/null +++ b/sys/arch/macppc/dev/if_gm.c @@ -0,0 +1,1179 @@ +/* $OpenBSD: if_gm.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: if_gm.c,v 1.2 2000/03/04 11:17:00 tsubai Exp $ */ + +/*- + * Copyright (c) 2000 Tsubai Masanari. 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. 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. + */ + +#ifdef __NetBSD__ +#include "opt_inet.h" +#include "opt_ns.h" +#endif /* __NetBSD__ */ +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/kernel.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/systm.h> + +#include <vm/vm.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <net/if_media.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#ifdef __NetBSD__ +#include <netinet/if_inarp.h> +#endif /* __NetBSD__ */ +#endif + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcidevs.h> + +#include <dev/ofw/openfirm.h> +#include <macppc/mac/if_gmreg.h> +#include <machine/pio.h> + +#define NTXBUF 4 +#define NRXBUF 32 + +struct gmac_softc { + struct device sc_dev; +#ifdef __OpenBSD__ + struct arpcom arpcom; /* per-instance network data */ +#define sc_if arpcom.ac_if +#define sc_enaddr arpcom.ac_enaddr +#else + struct ethercom sc_ethercom; +#define sc_if sc_ethercom.ec_if + u_int8_t sc_laddr[6]; +#endif + vaddr_t sc_reg; + bus_space_handle_t gm_bush; + bus_space_tag_t gm_bust; + struct gmac_dma *sc_txlist; + struct gmac_dma *sc_rxlist; + int sc_txnext; + int sc_rxlast; + caddr_t sc_txbuf[NTXBUF]; + caddr_t sc_rxbuf[NRXBUF]; + struct mii_data sc_mii; + struct timeout sc_tmo; +}; + + +int gmac_match __P((struct device *, void *, void *)); +void gmac_attach __P((struct device *, struct device *, void *)); + +static __inline u_int gmac_read_reg __P((struct gmac_softc *, int)); +static __inline void gmac_write_reg __P((struct gmac_softc *, int, u_int)); + +static __inline void gmac_start_txdma __P((struct gmac_softc *)); +static __inline void gmac_start_rxdma __P((struct gmac_softc *)); +static __inline void gmac_stop_txdma __P((struct gmac_softc *)); +static __inline void gmac_stop_rxdma __P((struct gmac_softc *)); + +int gmac_intr __P((void *)); +void gmac_tint __P((struct gmac_softc *)); +void gmac_rint __P((struct gmac_softc *)); +struct mbuf * gmac_get __P((struct gmac_softc *, caddr_t, int)); +void gmac_start __P((struct ifnet *)); +int gmac_put __P((struct gmac_softc *, caddr_t, struct mbuf *)); + +void gmac_stop __P((struct gmac_softc *)); +void gmac_reset __P((struct gmac_softc *)); +void gmac_init __P((struct gmac_softc *)); +void gmac_init_mac __P((struct gmac_softc *)); +void gmac_setladrf __P((struct gmac_softc *)); + +int gmac_ioctl __P((struct ifnet *, u_long, caddr_t)); +void gmac_watchdog __P((struct ifnet *)); +void gmac_enable_hack __P((void)); + +int gmac_mediachange __P((struct ifnet *)); +void gmac_mediastatus __P((struct ifnet *, struct ifmediareq *)); +int gmac_mii_readreg __P((struct device *, int, int)); +void gmac_mii_writereg __P((struct device *, int, int, int)); +void gmac_mii_statchg __P((struct device *)); +void gmac_mii_tick __P((void *)); + +u_int32_t ether_crc32_le(const u_int8_t *buf, size_t len); + +struct cfattach gm_ca = { + sizeof(struct gmac_softc), gmac_match, gmac_attach +}; +struct cfdriver gm_cd = { + NULL, "gm", DV_IFNET +}; + +int +gmac_match(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct pci_attach_args *pa = aux; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_APPLE) + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_APPLE_GMAC: + case PCI_PRODUCT_APPLE_GMAC2: + return 1; + } + + return 0; +} + +void +gmac_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct gmac_softc *sc = (void *)self; + struct pci_attach_args *pa = aux; + pci_chipset_tag_t pc = pa->pa_pc; + struct ifnet *ifp = &sc->sc_if; + struct mii_data *mii = &sc->sc_mii; + const char *intrstr = NULL; + char intrstrbuf[20]; + bus_addr_t membase; + bus_size_t memsize; +#ifdef __NetBSD__ + int node; +#endif + int i; + char *p; + struct gmac_dma *dp; + u_char laddr[6]; + +#ifdef __NetBSD__ + node = pcidev_to_ofdev(pa->pa_pc, pa->pa_tag); + if (node == 0) { + printf(": cannot find gmac node\n"); + return; + } + + OF_getprop(node, "local-mac-address", laddr, sizeof laddr); + OF_getprop(node, "assigned-addresses", reg, sizeof reg); + bcopy(laddr, sc->sc_laddr, sizeof laddr); + sc->sc_reg = reg[2]; +#endif +#ifdef __OpenBSD__ + pci_ether_hw_addr(pc, laddr); + + /* proper pci configuration */ + { + u_int32_t command; + command = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + command |= PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | + PCI_COMMAND_MASTER_ENABLE; + pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, command); + +#ifdef USE_IO + if (pci_io_find(pc, pa->pa_tag, 0x10, &iobase, &iosize, NULL)) { + printf(": can't find I/O space\n"); + return; + } + if (bus_space_map(pa->pa_iot, iobase, iosize, 0, &sc->gm_bush)) + { + printf(": can't map I/O space\n"); + return; + } + sc->gm_bust = pa->pa_iot; +#else /* !USE_IO */ + if (pci_mem_find(pc, pa->pa_tag, 0x10, &membase, &memsize, + NULL)) + { + printf(": can't find MEM space\n"); + return; + } + if (bus_space_map(pa->pa_memt, membase, memsize, 0, + &sc->gm_bush)) + { + printf(": can't map MEM space\n"); + return; + } + sc->gm_bust = pa->pa_memt; +#endif /* !USE_IO */ + + } +#endif + +#if 0 + if (pci_intr_map(pa, &ih)) { + printf(": unable to map interrupt\n"); + return; + } + intrstr = pci_intr_string(pa->pa_pc, ih); + + if (pci_intr_establish(pa->pa_pc, ih, IPL_NET, gmac_intr, sc, "gmac") == NULL) { + printf(": unable to establish interrupt"); + if (intrstr) + printf(" at %s", intrstr); + printf("\n"); + return; + } +#endif +#if 1 + sprintf(intrstrbuf, "irq %d", pa->pa_intrline); + intrstr = intrstrbuf; + /* + if (pci_intr_establish(pa->pa_pc, pa->pa_intrline, IPL_NET, + * Someone explain how to get the interrupt line correctly from the + * pci info? pa_intrline returns 60, not 1 like the hardware expects + * on uni-north G4 system. + */ + if (pci_intr_establish(pa->pa_pc, pa->pa_intrline, IPL_NET, + gmac_intr, sc, "gmac") == NULL) + { + printf(": unable to establish interrupt"); + if (intrstr) + printf(" at %x", pa->pa_intrline); + printf("\n"); + return; + } +#endif + + /* Setup packet buffers and dma descriptors. */ + p = malloc((NRXBUF + NTXBUF) * 2048 + 3 * 0x800, M_DEVBUF, M_NOWAIT); + if (p == NULL) { + printf(": cannot malloc buffers\n"); + return; + } + p = (void *)roundup((vaddr_t)p, 0x800); + bzero(p, 2048 * (NRXBUF + NTXBUF) + 2 * 0x800); + + sc->sc_rxlist = (void *)p; + p += 0x800; + sc->sc_txlist = (void *)p; + p += 0x800; + + dp = sc->sc_rxlist; + for (i = 0; i < NRXBUF; i++) { + sc->sc_rxbuf[i] = p; + dp->address = htole32(vtophys((vaddr_t)p)); + dp->cmd = htole32(GMAC_OWN); + dp++; + p += 2048; + } + + dp = sc->sc_txlist; + for (i = 0; i < NTXBUF; i++) { + sc->sc_txbuf[i] = p; + dp->address = htole32(vtophys((vaddr_t)p)); + dp++; + p += 2048; + } + +#ifdef __OpenBSD__ + bcopy(laddr, sc->sc_enaddr, 6); +#endif /* __OpenBSD__ */ + + printf(": %s, address %s\n", intrstr, ether_sprintf(laddr)); + + gmac_reset(sc); + gmac_init_mac(sc); + + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_ioctl = gmac_ioctl; + ifp->if_start = gmac_start; + ifp->if_watchdog = gmac_watchdog; + ifp->if_flags = + IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST; + ifp->if_flags |= IFF_ALLMULTI; + + mii->mii_ifp = ifp; + mii->mii_readreg = gmac_mii_readreg; + mii->mii_writereg = gmac_mii_writereg; + mii->mii_statchg = gmac_mii_statchg; + timeout_set(&sc->sc_tmo, gmac_mii_tick, sc); + + ifmedia_init(&mii->mii_media, 0, gmac_mediachange, gmac_mediastatus); +#ifdef __NetBSD__ + mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + mii_phy_probe(self, &sc->sc_mii, 0xffffffff); +#endif /* __OpenBSD__ */ + + /* Choose a default media. */ + if (LIST_FIRST(&mii->mii_phys) == NULL) { + ifmedia_add(&mii->mii_media, IFM_ETHER|IFM_NONE, 0, NULL); + ifmedia_set(&mii->mii_media, IFM_ETHER|IFM_NONE); + } else + ifmedia_set(&mii->mii_media, IFM_ETHER|IFM_AUTO); + + if_attach(ifp); +#ifdef __NetBSD__ + ether_ifattach(ifp, laddr); +#else /* !__NetBSD__ */ + ether_ifattach(ifp); +#endif /* !__NetBSD__ */ +} + +u_int +gmac_read_reg(sc, reg) + struct gmac_softc *sc; + int reg; +{ + return bus_space_read_4(sc->gm_bust, sc->gm_bush, reg); +} + +void +gmac_write_reg(sc, reg, val) + struct gmac_softc *sc; + int reg; + u_int val; +{ + bus_space_write_4(sc->gm_bust, sc->gm_bush, reg, val); +} + +void +gmac_start_txdma(sc) + struct gmac_softc *sc; +{ + u_int x; + + x = gmac_read_reg(sc, GMAC_TXDMACONFIG); + x |= 1; + gmac_write_reg(sc, GMAC_TXDMACONFIG, x); + x = gmac_read_reg(sc, GMAC_TXMACCONFIG); + x |= 1; + gmac_write_reg(sc, GMAC_TXMACCONFIG, x); +} + +void +gmac_start_rxdma(sc) + struct gmac_softc *sc; +{ + u_int x; + + x = gmac_read_reg(sc, GMAC_RXDMACONFIG); + x |= 1; + gmac_write_reg(sc, GMAC_RXDMACONFIG, x); + x = gmac_read_reg(sc, GMAC_RXMACCONFIG); + x |= 1; + gmac_write_reg(sc, GMAC_RXMACCONFIG, x); +} + +void +gmac_stop_txdma(sc) + struct gmac_softc *sc; +{ + u_int x; + + x = gmac_read_reg(sc, GMAC_TXDMACONFIG); + x &= ~1; + gmac_write_reg(sc, GMAC_TXDMACONFIG, x); + x = gmac_read_reg(sc, GMAC_TXMACCONFIG); + x &= ~1; + gmac_write_reg(sc, GMAC_TXMACCONFIG, x); +} + +void +gmac_stop_rxdma(sc) + struct gmac_softc *sc; +{ + u_int x; + + x = gmac_read_reg(sc, GMAC_RXDMACONFIG); + x &= ~1; + gmac_write_reg(sc, GMAC_RXDMACONFIG, x); + x = gmac_read_reg(sc, GMAC_RXMACCONFIG); + x &= ~1; + gmac_write_reg(sc, GMAC_RXMACCONFIG, x); +} + +int +gmac_intr(v) + void *v; +{ + struct gmac_softc *sc = v; + u_int status; + + status = gmac_read_reg(sc, GMAC_STATUS) & 0xff; + if (status == 0) { + return 0; + } + + if (status & GMAC_INT_RXDONE) + gmac_rint(sc); + + if (status & GMAC_INT_TXEMPTY) + gmac_tint(sc); + + return 1; +} + +void +gmac_tint(sc) + struct gmac_softc *sc; +{ + struct ifnet *ifp = &sc->sc_if; + + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_timer = 0; + gmac_start(ifp); +} + +void +gmac_rint(sc) + struct gmac_softc *sc; +{ + struct ifnet *ifp = &sc->sc_if; + volatile struct gmac_dma *dp; + struct mbuf *m; + int i, len; + u_int cmd; + + for (i = sc->sc_rxlast;; i++) { + if (i == NRXBUF) + i = 0; + + dp = &sc->sc_rxlist[i]; +#ifdef __OpenBSD__ + cmd = letoh32(dp->cmd); +#else /* !__OpenBSD__ */ + cmd = le32toh(dp->cmd); +#endif /* !__OpenBSD__ */ + if (cmd & GMAC_OWN) + break; + len = (cmd >> 16) & GMAC_LEN_MASK; + len -= 4; /* CRC */ + +#ifdef __OpenBSD__ + if (letoh32(dp->cmd_hi) & 0x40000000) { +#else /* !__OpenBSD__ */ + if (le32toh(dp->cmd_hi) & 0x40000000) { +#endif /* !__OpenBSD__ */ + ifp->if_ierrors++; + goto next; + } + + m = gmac_get(sc, sc->sc_rxbuf[i], len); + if (m == NULL) { + ifp->if_ierrors++; + goto next; + } + +#if NBPFILTER > 0 + /* + * Check if there's a BPF listener on this interface. + * If so, hand off the raw packet to BPF. + */ + if (ifp->if_bpf) + bpf_tap(ifp->if_bpf, sc->sc_rxbuf[i], len); +#endif +#ifdef __OpenBSD__ + ether_input_mbuf(ifp, m); +#else /* !__OpenBSD__ */ + (*ifp->if_input)(ifp, m); +#endif /* !__OpenBSD__ */ + ifp->if_ipackets++; + +next: + dp->cmd_hi = 0; + __asm __volatile ("sync"); + dp->cmd = htole32(GMAC_OWN); + } + sc->sc_rxlast = i; +} + +struct mbuf * +gmac_get(sc, pkt, totlen) + struct gmac_softc *sc; + caddr_t pkt; + int totlen; +{ + struct mbuf *m; + struct mbuf *top, **mp; + int len; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + return 0; + m->m_pkthdr.rcvif = &sc->sc_if; + m->m_pkthdr.len = totlen; + len = MHLEN; + top = 0; + mp = ⊤ + + while (totlen > 0) { + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(top); + return 0; + } + len = MLEN; + } + if (totlen >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + m_freem(top); + return 0; + } + len = MCLBYTES; + } + m->m_len = len = min(totlen, len); + bcopy(pkt, mtod(m, caddr_t), len); + pkt += len; + totlen -= len; + *mp = m; + mp = &m->m_next; + } + + return top; +} + +void +gmac_start(ifp) + struct ifnet *ifp; +{ + struct gmac_softc *sc = ifp->if_softc; + struct mbuf *m; + caddr_t buff; + int i, tlen; + volatile struct gmac_dma *dp; + + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + return; + + for (;;) { + if (ifp->if_flags & IFF_OACTIVE) + break; + + IF_DEQUEUE(&ifp->if_snd, m); + if (m == 0) + break; + + /* 5 seconds to watch for failing to transmit */ + ifp->if_timer = 5; + ifp->if_opackets++; /* # of pkts */ + + i = sc->sc_txnext; + buff = sc->sc_txbuf[i]; + tlen = gmac_put(sc, buff, m); + + dp = &sc->sc_txlist[i]; + dp->cmd_hi = 0; + dp->address_hi = 0; + dp->cmd = htole32(tlen | GMAC_OWN | GMAC_SOP); + + i++; + if (i == NTXBUF) + i = 0; + __asm __volatile ("sync"); + + gmac_write_reg(sc, GMAC_TXDMAKICK, i); + sc->sc_txnext = i; + +#if NBPFILTER > 0 + /* + * If BPF is listening on this interface, let it see the + * packet before we commit it to the wire. + */ + if (ifp->if_bpf) + bpf_tap(ifp->if_bpf, buff, tlen); +#endif + i++; + if (i == NTXBUF) { + i = 0; + } + if (i == gmac_read_reg(sc, GMAC_TXDMACOMPLETE)) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + } +} + +int +gmac_put(sc, buff, m) + struct gmac_softc *sc; + caddr_t buff; + struct mbuf *m; +{ + struct mbuf *n; + int len, tlen = 0; + + for (; m; m = n) { + len = m->m_len; + if (len == 0) { + MFREE(m, n); + continue; + } + bcopy(mtod(m, caddr_t), buff, len); + buff += len; + tlen += len; + MFREE(m, n); + } + if (tlen > 2048) + panic("%s: gmac_put packet overflow", sc->sc_dev.dv_xname); + + return tlen; +} + +void +gmac_reset(sc) + struct gmac_softc *sc; +{ + int i, s; + + s = splnet(); + + gmac_stop_txdma(sc); + gmac_stop_rxdma(sc); + + gmac_write_reg(sc, GMAC_SOFTWARERESET, 3); + for (i = 10; i > 0; i--) { + delay(300000); /* XXX long delay */ + if ((gmac_read_reg(sc, GMAC_SOFTWARERESET) & 3) == 0) + break; + } + if (i == 0) + printf("%s: reset timeout\n", sc->sc_dev.dv_xname); + + sc->sc_txnext = 0; + sc->sc_rxlast = 0; + for (i = 0; i < NRXBUF; i++) + sc->sc_rxlist[i].cmd = htole32(GMAC_OWN); + __asm __volatile ("sync"); + + gmac_write_reg(sc, GMAC_TXDMADESCBASEHI, 0); + gmac_write_reg(sc, GMAC_TXDMADESCBASELO, vtophys((vaddr_t)sc->sc_txlist)); + gmac_write_reg(sc, GMAC_RXDMADESCBASEHI, 0); + gmac_write_reg(sc, GMAC_RXDMADESCBASELO, vtophys((vaddr_t)sc->sc_rxlist)); + gmac_write_reg(sc, GMAC_RXDMAKICK, NRXBUF); + + splx(s); +} + +void +gmac_stop(sc) + struct gmac_softc *sc; +{ + struct ifnet *ifp = &sc->sc_if; + int s; + + s = splnet(); + + timeout_del(&sc->sc_tmo); +#ifndef __OenBSD__ + mii_down(&sc->sc_mii); +#endif + + gmac_stop_txdma(sc); + gmac_stop_rxdma(sc); + + gmac_write_reg(sc, GMAC_INTMASK, 0xffffffff); + + ifp->if_flags &= ~(IFF_UP | IFF_RUNNING); + ifp->if_timer = 0; + + splx(s); +} + +void +gmac_init_mac(sc) + struct gmac_softc *sc; +{ + int i, tb; +#ifdef __NetBSD__ + u_int8_t *laddr = sc->sc_laddr; +#else /* !__NetBSD__ */ + u_int8_t *laddr = sc->sc_enaddr; +#endif + + __asm ("mftb %0" : "=r"(tb)); + gmac_write_reg(sc, GMAC_RANDOMSEED, tb); + + /* init-mii */ + gmac_write_reg(sc, GMAC_DATAPATHMODE, 4); + gmac_mii_writereg(&sc->sc_dev, 0, 0, 0x1000); + + gmac_write_reg(sc, GMAC_TXDMACONFIG, 0xffc00); + gmac_write_reg(sc, GMAC_RXDMACONFIG, 0); + gmac_write_reg(sc, GMAC_MACPAUSE, 0x1bf0); + gmac_write_reg(sc, GMAC_INTERPACKETGAP0, 0); + gmac_write_reg(sc, GMAC_INTERPACKETGAP1, 8); + gmac_write_reg(sc, GMAC_INTERPACKETGAP2, 4); + gmac_write_reg(sc, GMAC_MINFRAMESIZE, ETHER_MIN_LEN); + gmac_write_reg(sc, GMAC_MAXFRAMESIZE, ETHER_MAX_LEN); + gmac_write_reg(sc, GMAC_PASIZE, 7); + gmac_write_reg(sc, GMAC_JAMSIZE, 4); + gmac_write_reg(sc, GMAC_ATTEMPTLIMIT,0x10); + gmac_write_reg(sc, GMAC_MACCNTLTYPE, 0x8808); + + gmac_write_reg(sc, GMAC_MACADDRESS0, (laddr[4] << 8) | laddr[5]); + gmac_write_reg(sc, GMAC_MACADDRESS1, (laddr[2] << 8) | laddr[3]); + gmac_write_reg(sc, GMAC_MACADDRESS2, (laddr[0] << 8) | laddr[1]); + gmac_write_reg(sc, GMAC_MACADDRESS3, 0); + gmac_write_reg(sc, GMAC_MACADDRESS4, 0); + gmac_write_reg(sc, GMAC_MACADDRESS5, 0); + gmac_write_reg(sc, GMAC_MACADDRESS6, 1); + gmac_write_reg(sc, GMAC_MACADDRESS7, 0xc200); + gmac_write_reg(sc, GMAC_MACADDRESS8, 0x0180); + gmac_write_reg(sc, GMAC_MACADDRFILT0, 0); + gmac_write_reg(sc, GMAC_MACADDRFILT1, 0); + gmac_write_reg(sc, GMAC_MACADDRFILT2, 0); + gmac_write_reg(sc, GMAC_MACADDRFILT2_1MASK, 0); + gmac_write_reg(sc, GMAC_MACADDRFILT0MASK, 0); + + for (i = 0; i < 0x6c; i+= 4) + gmac_write_reg(sc, GMAC_HASHTABLE0 + i, 0); + + gmac_write_reg(sc, GMAC_SLOTTIME, 0x40); + + /* XXX */ + gmac_write_reg(sc, GMAC_TXMACCONFIG, 0); + gmac_write_reg(sc, GMAC_XIFCONFIG, 5); + gmac_write_reg(sc, GMAC_MACCTRLCONFIG, 0); + if (IFM_OPTIONS(sc->sc_mii.mii_media_active) & IFM_FDX) { + gmac_write_reg(sc, GMAC_TXMACCONFIG, 6); + gmac_write_reg(sc, GMAC_XIFCONFIG, 1); + } else { + gmac_write_reg(sc, GMAC_TXMACCONFIG, 0); + gmac_write_reg(sc, GMAC_XIFCONFIG, 5); + } + if (0) { /* g-bit? */ + gmac_write_reg(sc, GMAC_MACCTRLCONFIG, 3); + } else { + gmac_write_reg(sc, GMAC_MACCTRLCONFIG, 0); + } +} + +void +gmac_setladrf(sc) + struct gmac_softc *sc; +{ + struct ifnet *ifp = &sc->sc_if; + struct ether_multi *enm; + struct ether_multistep step; +#if defined(__OpenBSD__) + struct arpcom *ec = &sc->arpcom; +#else + struct ethercom *ec = &sc->sc_ethercom; +#endif + u_int32_t crc; + u_int32_t hash[16]; + u_int v; + int i; + + /* Clear hash table */ + for (i = 0; i < 16; i++) + hash[i] = 0; + + /* Get current RX configuration */ + v = gmac_read_reg(sc, GMAC_RXMACCONFIG); + + if ((ifp->if_flags & IFF_PROMISC) != 0) { + /* Turn on promiscuous mode; turn off the hash filter */ + v |= GMAC_RXMAC_PR; + v &= ~GMAC_RXMAC_HEN; + ifp->if_flags |= IFF_ALLMULTI; + goto chipit; + } + + /* Turn off promiscuous mode; turn on the hash filter */ + v &= ~GMAC_RXMAC_PR; + v |= GMAC_RXMAC_HEN; + + /* + * Set up multicast address filter by passing all multicast addresses + * through a crc generator, and then using the high order 8 bits as an + * index into the 256 bit logical address filter. The high order bit + * selects the word, while the rest of the bits select the bit within + * the word. + */ + + ETHER_FIRST_MULTI(step, ec, enm); + while (enm != NULL) { + if (bcmp(enm->enm_addrlo, enm->enm_addrhi, 6)) { + /* + * We must listen to a range of multicast addresses. + * For now, just accept all multicasts, rather than + * trying to set only those filter bits needed to match + * the range. (At this time, the only use of address + * ranges is for IP multicast routing, for which the + * range is big enough to require all bits set.) + */ + for (i = 0; i < 16; i++) + hash[i] = 0xffff; + ifp->if_flags |= IFF_ALLMULTI; + goto chipit; + } + + crc = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN); + + /* Just want the 8 most significant bits. */ + crc >>= 24; + + /* Set the corresponding bit in the filter. */ + hash[crc >> 4] |= 1 << (crc & 0xf); + + ETHER_NEXT_MULTI(step, enm); + } + + ifp->if_flags &= ~IFF_ALLMULTI; + +chipit: + /* Now load the hash table into the chip */ + for (i = 0; i < 16; i++) + gmac_write_reg(sc, GMAC_HASHTABLE0 + i * 4, hash[i]); + + gmac_write_reg(sc, GMAC_RXMACCONFIG, v); +} + +void +gmac_init(sc) + struct gmac_softc *sc; +{ + struct ifnet *ifp = &sc->sc_if; + u_int x; + + gmac_stop_txdma(sc); + gmac_stop_rxdma(sc); + + gmac_init_mac(sc); + gmac_setladrf(sc); + + x = gmac_read_reg(sc, GMAC_RXMACCONFIG); + if (ifp->if_flags & IFF_PROMISC) + x |= GMAC_RXMAC_PR; + else + x &= ~GMAC_RXMAC_PR; + gmac_write_reg(sc, GMAC_RXMACCONFIG, x); + + gmac_start_txdma(sc); + gmac_start_rxdma(sc); + + gmac_write_reg(sc, GMAC_INTMASK, ~(GMAC_INT_TXEMPTY | GMAC_INT_RXDONE)); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_timer = 0; + + timeout_del(&sc->sc_tmo); + timeout_add(&sc->sc_tmo, 1); + + gmac_start(ifp); +} + +int +gmac_ioctl(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct gmac_softc *sc = ifp->if_softc; + struct ifaddr *ifa = (struct ifaddr *)data; + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + + s = splnet(); + + switch (cmd) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + gmac_init(sc); +#ifdef __OpenBSD__ + arp_ifinit(&sc->arpcom, ifa); +#else /* !__OpenBSD__ */ + arp_ifinit(ifp, ifa); +#endif /* !__OpenBSD__ */ + break; +#endif +#ifdef NS + case AF_NS: + { + struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; + + if (ns_nullhost(*ina)) + ina->x_host = + *(union ns_host *)LLADDR(ifp->if_sadl); + else { + bcopy(ina->x_host.c_host, + LLADDR(ifp->if_sadl), + sizeof(sc->sc_enaddr)); + } + /* Set new address. */ + gmac_init(sc); + break; + } +#endif + default: + gmac_init(sc); + break; + } + break; + + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && + (ifp->if_flags & IFF_RUNNING) != 0) { + /* + * If interface is marked down and it is running, then + * stop it. + */ + gmac_stop(sc); + ifp->if_flags &= ~IFF_RUNNING; + } else if ((ifp->if_flags & IFF_UP) != 0 && + (ifp->if_flags & IFF_RUNNING) == 0) { + /* + * If interface is marked up and it is stopped, then + * start it. + */ + gmac_init(sc); + } else { + /* + * Reset the interface to pick up changes in any other + * flags that affect hardware registers. + */ + gmac_reset(sc); + gmac_init(sc); + } +#ifdef GMAC_DEBUG + if (ifp->if_flags & IFF_DEBUG) + sc->sc_flags |= GMAC_DEBUGFLAG; +#endif + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: +#if defined(__OpenBSD__) + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->arpcom) : + ether_delmulti(ifr, &sc->arpcom); +#else + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->sc_ethercom) : + ether_delmulti(ifr, &sc->sc_ethercom); +#endif + + if (error == ENETRESET) { + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + gmac_init(sc); + /* gmac_setladrf(sc); */ + error = 0; + } + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd); + break; + + default: + error = EINVAL; + } + + splx(s); + return error; +} + +void +gmac_watchdog(ifp) + struct ifnet *ifp; +{ + struct gmac_softc *sc = ifp->if_softc; + + printf("%s: device timeout\n", ifp->if_xname); + ifp->if_oerrors++; + + gmac_reset(sc); + gmac_init(sc); +} + +int +gmac_mediachange(ifp) + struct ifnet *ifp; +{ + struct gmac_softc *sc = ifp->if_softc; + + return mii_mediachg(&sc->sc_mii); +} + +void +gmac_mediastatus(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct gmac_softc *sc = ifp->if_softc; + + mii_pollstat(&sc->sc_mii); + + ifmr->ifm_status = sc->sc_mii.mii_media_status; + ifmr->ifm_active = sc->sc_mii.mii_media_active; +} + +int +gmac_mii_readreg(dev, phy, reg) + struct device *dev; + int phy, reg; +{ + struct gmac_softc *sc = (void *)dev; + int i; + + gmac_write_reg(sc, GMAC_MIFFRAMEOUTPUT, + 0x60020000 | (phy << 23) | (reg << 18)); + + for (i = 1000; i >= 0; i -= 10) { + if (gmac_read_reg(sc, GMAC_MIFFRAMEOUTPUT) & 0x10000) + break; + delay(10); + } + if (i < 0) { + printf("%s: gmac_mii_readreg: timeout\n", sc->sc_dev.dv_xname); + return 0; + } + + return gmac_read_reg(sc, GMAC_MIFFRAMEOUTPUT) & 0xffff; +} + +void +gmac_mii_writereg(dev, phy, reg, val) + struct device *dev; + int phy, reg, val; +{ + struct gmac_softc *sc = (void *)dev; + int i; + + gmac_write_reg(sc, GMAC_MIFFRAMEOUTPUT, + 0x50020000 | (phy << 23) | (reg << 18) | (val & 0xffff)); + + for (i = 1000; i >= 0; i -= 10) { + if (gmac_read_reg(sc, GMAC_MIFFRAMEOUTPUT) & 0x10000) + break; + delay(10); + } + if (i < 0) + printf("%s: gmac_mii_writereg: timeout\n", sc->sc_dev.dv_xname); +} + +void +gmac_mii_statchg(dev) + struct device *dev; +{ + struct gmac_softc *sc = (void *)dev; + + gmac_stop_txdma(sc); + gmac_stop_rxdma(sc); + + if (IFM_OPTIONS(sc->sc_mii.mii_media_active) & IFM_FDX) { + gmac_write_reg(sc, GMAC_TXMACCONFIG, 6); + gmac_write_reg(sc, GMAC_XIFCONFIG, 1); + } else { + gmac_write_reg(sc, GMAC_TXMACCONFIG, 0); + gmac_write_reg(sc, GMAC_XIFCONFIG, 5); + } + + if (0) /* g-bit? */ + gmac_write_reg(sc, GMAC_MACCTRLCONFIG, 3); + else + gmac_write_reg(sc, GMAC_MACCTRLCONFIG, 0); + + gmac_start_txdma(sc); + gmac_start_rxdma(sc); +} + +void +gmac_mii_tick(v) + void *v; +{ + struct gmac_softc *sc = v; + int s; + + s = splnet(); + mii_tick(&sc->sc_mii); + splx(s); + + timeout_add(&sc->sc_tmo, hz); +} + +void +gmac_enable_hack() +{ + u_int32_t *paddr; + u_int32_t value; + +#if 1 + paddr = mapiodev(0xf8000020, 0x30); + + value = *paddr; + value |= 0x2; + *paddr = value; + + unmapiodev(paddr,0x30); +#endif + + printf("gmac enabled\n"); +} + +/* HACK, THIS SHOULD NOT BE IN THIS FILE */ +u_int32_t +ether_crc32_le(const u_int8_t *buf, size_t len) +{ + static const u_int32_t crctab[] = { + 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, + 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, + 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c + }; + u_int32_t crc; + int i; + + crc = 0xffffffffU; /* initial value */ + + for (i = 0; i < len; i++) { + crc ^= buf[i]; + crc = (crc >> 4) ^ crctab[crc & 0xf]; + crc = (crc >> 4) ^ crctab[crc & 0xf]; + } + + return (crc); +} diff --git a/sys/arch/macppc/dev/if_gmreg.h b/sys/arch/macppc/dev/if_gmreg.h new file mode 100644 index 00000000000..f7de0e583d1 --- /dev/null +++ b/sys/arch/macppc/dev/if_gmreg.h @@ -0,0 +1,109 @@ +/* $OpenBSD: if_gmreg.h,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: if_gmreg.h,v 1.1 2000/02/27 18:00:55 tsubai Exp $ */ + +/*- + * Copyright (c) 2000 Tsubai Masanari. 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. 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. + */ + +struct gmac_dma { + u_int32_t cmd; + u_int32_t cmd_hi; + u_int32_t address; + u_int32_t address_hi; +}; + +#define GMAC_OWN 0x80000000 +#define GMAC_SOP 0x40000000 /* start of packet? */ +#define GMAC_LEN_MASK 0x00003fff + +#define GMAC_INT_TXEMPTY 0x02 /* TX ring empty */ +#define GMAC_INT_TXDONE 0x04 +#define GMAC_INT_RXDONE 0x10 + +#define GMAC_RXMAC_PR 0x08 /* enable promiscuous mode */ +#define GMAC_RXMAC_HEN 0x10 /* enable the hash filter */ + +/* + * register offset + */ +#define GMAC_STATUS 0x000c +#define GMAC_INTMASK 0x0010 +#define GMAC_SOFTWARERESET 0x1010 + +#define GMAC_TXDMAKICK 0x2000 +#define GMAC_TXDMACONFIG 0x2004 +#define GMAC_TXDMADESCBASELO 0x2008 +#define GMAC_TXDMADESCBASEHI 0x200c +#define GMAC_TXDMACOMPLETE 0x2100 + +#define GMAC_RXDMACONFIG 0x4000 +#define GMAC_RXDMADESCBASELO 0x4004 +#define GMAC_RXDMADESCBASEHI 0x4008 +#define GMAC_RXDMAKICK 0x4100 + +#define GMAC_MACPAUSE 0x6008 +#define GMAC_MACPAUSE 0x6008 +#define GMAC_TXMACSTATUS 0x6010 +#define GMAC_TXMACCONFIG 0x6030 +#define GMAC_RXMACCONFIG 0x6034 +#define GMAC_MACCTRLCONFIG 0x6038 +#define GMAC_XIFCONFIG 0x603c +#define GMAC_INTERPACKETGAP0 0x6040 +#define GMAC_INTERPACKETGAP1 0x6044 +#define GMAC_INTERPACKETGAP2 0x6048 +#define GMAC_SLOTTIME 0x604c +#define GMAC_MINFRAMESIZE 0x6050 +#define GMAC_MAXFRAMESIZE 0x6054 +#define GMAC_PASIZE 0x6058 +#define GMAC_JAMSIZE 0x605c +#define GMAC_ATTEMPTLIMIT 0x6060 /* atemptlimit */ +#define GMAC_MACCNTLTYPE 0x6064 +#define GMAC_MACADDRESS0 0x6080 +#define GMAC_MACADDRESS1 0x6084 +#define GMAC_MACADDRESS2 0x6088 +#define GMAC_MACADDRESS3 0x608c +#define GMAC_MACADDRESS4 0x6090 +#define GMAC_MACADDRESS5 0x6094 +#define GMAC_MACADDRESS6 0x6098 +#define GMAC_MACADDRESS7 0x609c +#define GMAC_MACADDRESS8 0x60a0 +#define GMAC_MACADDRFILT0 0x60a4 +#define GMAC_MACADDRFILT1 0x60a8 +#define GMAC_MACADDRFILT2 0x60ac +#define GMAC_MACADDRFILT2_1MASK 0x60b0 /* macaddressfilter2&1mask */ +#define GMAC_MACADDRFILT0MASK 0x60b4 /* macaddressfilter0mask */ +#define GMAC_HASHTABLE0 0x60c0 + +#define GMAC_RANDOMSEED 0x6130 +#define GMAC_MIFFRAMEOUTPUT 0x620c +#define GMAC_DATAPATHMODE 0x9050 + +#ifndef ETHER_MAX_LEN +#define ETHER_MAX_LEN 1518 +#endif +#ifndef ETHER_MIN_LEN +#define ETHER_MIN_LEN 64 +#endif + diff --git a/sys/arch/macppc/dev/if_wi_obio.c b/sys/arch/macppc/dev/if_wi_obio.c new file mode 100644 index 00000000000..a0788d1cfa0 --- /dev/null +++ b/sys/arch/macppc/dev/if_wi_obio.c @@ -0,0 +1,247 @@ +/* $OpenBSD: if_wi_obio.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ + +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ctr.columbia.edu>. 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 Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``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 Bill Paul OR THE VOICES IN HIS HEAD + * 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. + * + * From: if_wi.c,v 1.7 1999/07/04 14:40:22 wpaul Exp $ + */ + +/* + * Lucent WaveLAN/IEEE 802.11 PCMCIA driver for OpenBSD. + * + * Originally written by Bill Paul <wpaul@ctr.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ + /* + * modified for apple obio by Dale Rahn + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/timeout.h> +#include <sys/socket.h> +#include <sys/device.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_ether.h> +#endif + +#include <net/if_ieee80211.h> + +#include <machine/bus.h> +#include <machine/autoconf.h> + +#include <dev/ic/if_wireg.h> +#include <dev/ic/if_wi_ieee.h> +#include <dev/ic/if_wivar.h> + +int wi_obio_match __P((struct device *, void *, void *)); +void wi_obio_attach __P((struct device *, struct device *, void *)); +int wi_obio_detach __P((struct device *, int)); +int wi_obio_activate __P((struct device *, enum devact)); +void wi_obio_attach __P((struct device *, struct device *, void *)); +int wi_obio_enable(struct wi_softc *sc); +void wi_obio_disable(struct wi_softc *sc); + + +int wi_intr __P((void *)); +int wi_attach __P((struct wi_softc *)); +void wi_init __P((void *)); +void wi_stop __P((struct wi_softc *)); + +struct wi_obio_softc { + struct wi_softc sc_wi; +}; + +struct cfattach wi_obio_ca = { + sizeof (struct wi_obio_softc), wi_obio_match, wi_obio_attach, + wi_obio_detach, wi_obio_activate +}; + + +int +wi_obio_match(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct confargs *ca = aux; + + if (strcmp(ca->ca_name, "radio") == 0) + return (1); + return (0); +} + +void +wi_obio_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct wi_obio_softc *psc = (struct wi_obio_softc *)self; + struct wi_softc *sc = &psc->sc_wi; + struct confargs *ca = aux; + + printf(" irq %d:", ca->ca_intr[0]); + + sc->wi_btag = ca->ca_iot; + ca->ca_reg[0] += ca->ca_baseaddr; + if (bus_space_map(sc->wi_btag, ca->ca_reg[0], ca->ca_reg[1], 0, &sc->wi_bhandle)) { + printf("cant' map i/o space\n"); + } + + /* Establish the interrupt. */ + mac_intr_establish(parent, ca->ca_intr[0], IST_LEVEL, IPL_NET, + wi_intr, psc, "wi_obio"); + + /* Make sure interrupts are disabled. */ + CSR_WRITE_2(sc, WI_INT_EN, 0); + CSR_WRITE_2(sc, WI_EVENT_ACK, 0xffff); + + wi_obio_enable(sc); + + wi_attach(sc); + return; + +} + +int +wi_obio_detach(dev, flags) + struct device *dev; + int flags; +{ + struct wi_obio_softc *psc = (struct wi_obio_softc *)dev; + struct wi_softc *sc = &psc->sc_wi; + struct ifnet *ifp = &sc->arpcom.ac_if; + + /* + obio_io_unmap(psc->sc_pf, psc->sc_io_window); + obio_io_free(psc->sc_pf, &psc->sc_pcioh); + */ + + wi_obio_disable(sc); + ether_ifdetach(ifp); + if_detach(ifp); + + return (0); +} + +int +wi_obio_activate(dev, act) + struct device *dev; + enum devact act; +{ + struct wi_obio_softc *psc = (struct wi_obio_softc *)dev; + struct wi_softc *sc = &psc->sc_wi; + struct ifnet *ifp = &sc->arpcom.ac_if; + int s; + + s = splnet(); + switch (act) { + case DVACT_ACTIVATE: + wi_obio_enable(sc); + printf("%s:", WI_PRT_ARG(sc)); + printf("\n"); + wi_init(sc); + break; + + case DVACT_DEACTIVATE: + ifp->if_timer = 0; + if (ifp->if_flags & IFF_RUNNING) + wi_stop(sc); + wi_obio_disable(sc); + break; + } + splx(s); + return (0); +} + +/* THIS IS CRAP */ + +int +wi_obio_enable(sc) + struct wi_softc *sc; +{ + const u_int keywest = 0x80000000; /* XXX */ + const u_int fcr2 = keywest + 0x40; + const u_int gpio = keywest + 0x6a; + const u_int extint_gpio = keywest + 0x58; + u_int x; + + x = in32rb(fcr2); + x |= 0x4; + out32rb(fcr2, x); + + /* Enable card slot. */ + out8(gpio + 0x0f, 5); + delay(1000); + out8(gpio + 0x0f, 4); + delay(1000); + + x = in32rb(fcr2); + x &= ~0x8000000; + + out32rb(fcr2, x); + /* out8(gpio + 0x10, 4); */ + + out8(extint_gpio + 0x0b, 0); + out8(extint_gpio + 0x0a, 0x28); + out8(extint_gpio + 0x0d, 0x28); + out8(gpio + 0x0d, 0x28); + out8(gpio + 0x0e, 0x28); + out32rb(keywest + 0x1c000, 0); + + /* Initialize the card. */ + out32rb(keywest + 0x1a3e0, 0x41); + x = in32rb(fcr2); + x |= 0x8000000; + out32rb(fcr2, x); + + return 0; +} + +void +wi_obio_disable(sc) + struct wi_softc *sc; +{ + const u_int keywest = 0x80000000; /* XXX */ + const u_int fcr2 = keywest + 0x40; + u_int x; + + x = in32rb(fcr2); + x &= ~0x4; + out32rb(fcr2, x); + /* out8(gpio + 0x10, 0); */ +} diff --git a/sys/arch/macppc/dev/keyboard.h b/sys/arch/macppc/dev/keyboard.h new file mode 100644 index 00000000000..8b6c1d5d36a --- /dev/null +++ b/sys/arch/macppc/dev/keyboard.h @@ -0,0 +1,209 @@ +/* $OpenBSD: keyboard.h,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: keyboard.h,v 1.1 1998/05/15 10:15:54 tsubai Exp $ */ + +/*- + * Copyright (C) 1993 Allen K. Briggs, Chris P. Caputo, + * Michael L. Finch, Bradley A. Grantham, and + * Lawrence A. Kesteloot + * 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 the Alice Group. + * 4. The names of the Alice Group or any of its members may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``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 ALICE GROUP 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. + */ + +#define ADBK_LEFT 0x3B +#define ADBK_RIGHT 0x3C +#define ADBK_UP 0x3E +#define ADBK_DOWN 0x3D +#define ADBK_PGUP 0x74 +#define ADBK_PGDN 0x79 +#define ADBK_HOME 0x73 +#define ADBK_END 0x77 +#define ADBK_CONTROL 0x36 +#define ADBK_FLOWER 0x37 +#define ADBK_SHIFT 0x38 +#define ADBK_CAPSLOCK 0x39 +#define ADBK_OPTION 0x3A +#define ADBK_F 0x03 +#define ADBK_O 0x1F +#define ADBK_P 0x23 +#define ADBK_Q 0x0C +#define ADBK_V 0x09 +#define ADBK_1 0x12 +#define ADBK_2 0x13 +#define ADBK_3 0x14 +#define ADBK_4 0x15 +#define ADBK_5 0x17 +#define ADBK_6 0x16 +#define ADBK_7 0x1A +#define ADBK_8 0x1C +#define ADBK_9 0x19 +#define ADBK_0 0x1D + +#define ADBK_KEYVAL(key) ((key) & 0x7f) +#define ADBK_PRESS(key) (((key) & 0x80) == 0) +#define ADBK_KEYDOWN(key) (key) +#define ADBK_KEYUP(key) ((key) | 0x80) +#define ADBK_MODIFIER(key) ((((key) & 0x7f) == ADBK_SHIFT) || \ + (((key) & 0x7f) == ADBK_CONTROL) || \ + (((key) & 0x7f) == ADBK_FLOWER) || \ + (((key) & 0x7f) == ADBK_OPTION)) + +#ifndef KEYBOARD_ARRAY +extern unsigned char keyboard[128][3]; +#else +unsigned char keyboard[128][3] = { + /* Scan code Normal Shifted Controlled */ + { /* 0x00, */ 'a', 'A', 0x01 }, + { /* 0x01, */ 's', 'S', 0x13 }, + { /* 0x02, */ 'd', 'D', 0x04 }, + { /* 0x03, */ 'f', 'F', 0x06 }, + { /* 0x04, */ 'h', 'H', 0x08 }, + { /* 0x05, */ 'g', 'G', 0x07 }, + { /* 0x06, */ 'z', 'Z', 0x1A }, + { /* 0x07, */ 'x', 'X', 0x18 }, + { /* 0x08, */ 'c', 'C', 0x03 }, + { /* 0x09, */ 'v', 'V', 0x16 }, + { /* 0x0A, */ 0x00, 0x00, 0x00 }, + { /* 0x0B, */ 'b', 'B', 0x02 }, + { /* 0x0C, */ 'q', 'Q', 0x11 }, + { /* 0x0D, */ 'w', 'W', 0x17 }, + { /* 0x0E, */ 'e', 'E', 0x05 }, + { /* 0x0F, */ 'r', 'R', 0x12 }, + { /* 0x10, */ 'y', 'Y', 0x19 }, + { /* 0x11, */ 't', 'T', 0x14 }, + { /* 0x12, */ '1', '!', 0x00 }, + { /* 0x13, */ '2', '@', 0x00 }, + { /* 0x14, */ '3', '#', 0x00 }, + { /* 0x15, */ '4', '$', 0x00 }, + { /* 0x16, */ '6', '^', 0x1E }, + { /* 0x17, */ '5', '%', 0x00 }, + { /* 0x18, */ '=', '+', 0x00 }, + { /* 0x19, */ '9', '(', 0x00 }, + { /* 0x1A, */ '7', '&', 0x00 }, + { /* 0x1B, */ '-', '_', 0x1F }, + { /* 0x1C, */ '8', '*', 0x00 }, + { /* 0x1D, */ '0', ')', 0x00 }, + { /* 0x1E, */ ']', '}', 0x1D }, + { /* 0x1F, */ 'o', 'O', 0x0F }, + { /* 0x20, */ 'u', 'U', 0x15 }, + { /* 0x21, */ '[', '{', 0x1B }, + { /* 0x22, */ 'i', 'I', 0x09 }, + { /* 0x23, */ 'p', 'P', 0x10 }, + { /* 0x24, */ 0x0D, 0x0D, 0x0D }, + { /* 0x25, */ 'l', 'L', 0x0C }, + { /* 0x26, */ 'j', 'J', 0x0A }, + { /* 0x27, */ '\'', '"', 0x00 }, + { /* 0x28, */ 'k', 'K', 0x0B }, + { /* 0x29, */ ';', ':', 0x00 }, + { /* 0x2A, */ '\\', '|', 0x1C }, + { /* 0x2B, */ ',', '<', 0x00 }, + { /* 0x2C, */ '/', '?', 0x00 }, + { /* 0x2D, */ 'n', 'N', 0x0E }, + { /* 0x2E, */ 'm', 'M', 0x0D }, + { /* 0x2F, */ '.', '>', 0x00 }, + { /* 0x30, */ 0x09, 0x09, 0x09 }, + { /* 0x31, */ ' ', ' ', 0x00 }, + { /* 0x32, */ '`', '~', 0x00 }, + { /* 0x33, */ 0x7F, 0x7F, 0x7F }, /* Delete */ + { /* 0x34, */ 0x00, 0x00, 0x00 }, + { /* 0x35, */ 0x1B, 0x1B, 0x1B }, + { /* 0x36, */ 0x00, 0x00, 0x00 }, + { /* 0x37, */ 0x00, 0x00, 0x00 }, + { /* 0x38, */ 0x00, 0x00, 0x00 }, + { /* 0x39, */ 0x00, 0x00, 0x00 }, + { /* 0x3A, */ 0x00, 0x00, 0x00 }, + { /* 0x3B, */ 'h', 0x00, 0x00 }, /* Left */ + { /* 0x3C, */ 'l', 0x00, 0x00 }, /* Right */ + { /* 0x3D, */ 'j', 0x00, 0x00 }, /* Down */ + { /* 0x3E, */ 'k', 0x00, 0x00 }, /* Up */ + { /* 0x3F, */ 0x00, 0x00, 0x00 }, + { /* 0x40, */ 0x00, 0x00, 0x00 }, + { /* 0x41, */ '.', '.', 0x00 }, + { /* 0x42, */ 0x00, 0x00, 0x00 }, + { /* 0x43, */ '*', '*', 0x00 }, + { /* 0x44, */ 0x00, 0x00, 0x00 }, + { /* 0x45, */ '+', '+', 0x00 }, + { /* 0x46, */ 0x00, 0x00, 0x00 }, + { /* 0x47, */ 0x00, 0x00, 0x00 }, + { /* 0x48, */ 0x00, 0x00, 0x00 }, + { /* 0x49, */ 0x00, 0x00, 0x00 }, + { /* 0x4A, */ 0x00, 0x00, 0x00 }, + { /* 0x4B, */ '/', '/', 0x00 }, + { /* 0x4C, */ 0x0D, 0x0D, 0x0D }, + { /* 0x4D, */ 0x00, 0x00, 0x00 }, + { /* 0x4E, */ '-', '-', 0x00 }, + { /* 0x4F, */ 0x00, 0x00, 0x00 }, + { /* 0x50, */ 0x00, 0x00, 0x00 }, + { /* 0x51, */ '=', '=', 0x00 }, + { /* 0x52, */ '0', '0', 0x00 }, + { /* 0x53, */ '1', '1', 0x00 }, + { /* 0x54, */ '2', '2', 0x00 }, + { /* 0x55, */ '3', '3', 0x00 }, + { /* 0x56, */ '4', '4', 0x00 }, + { /* 0x57, */ '5', '5', 0x00 }, + { /* 0x58, */ '6', '6', 0x00 }, + { /* 0x59, */ '7', '7', 0x00 }, + { /* 0x5A, */ 0x00, 0x00, 0x00 }, + { /* 0x5B, */ '8', '8', 0x00 }, + { /* 0x5C, */ '9', '9', 0x00 }, + { /* 0x5D, */ 0x00, 0x00, 0x00 }, + { /* 0x5E, */ 0x00, 0x00, 0x00 }, + { /* 0x5F, */ 0x00, 0x00, 0x00 }, + { /* 0x60, */ 0x00, 0x00, 0x00 }, + { /* 0x61, */ 0x00, 0x00, 0x00 }, + { /* 0x62, */ 0x00, 0x00, 0x00 }, + { /* 0x63, */ 0x00, 0x00, 0x00 }, + { /* 0x64, */ 0x00, 0x00, 0x00 }, + { /* 0x65, */ 0x00, 0x00, 0x00 }, + { /* 0x66, */ 0x00, 0x00, 0x00 }, + { /* 0x67, */ 0x00, 0x00, 0x00 }, + { /* 0x68, */ 0x00, 0x00, 0x00 }, + { /* 0x69, */ 0x00, 0x00, 0x00 }, + { /* 0x6A, */ 0x00, 0x00, 0x00 }, + { /* 0x6B, */ 0x00, 0x00, 0x00 }, + { /* 0x6C, */ 0x00, 0x00, 0x00 }, + { /* 0x6D, */ 0x00, 0x00, 0x00 }, + { /* 0x6E, */ 0x00, 0x00, 0x00 }, + { /* 0x6F, */ 0x00, 0x00, 0x00 }, + { /* 0x70, */ 0x00, 0x00, 0x00 }, + { /* 0x71, */ 0x00, 0x00, 0x00 }, + { /* 0x72, */ 0x00, 0x00, 0x00 }, + { /* 0x73, */ 0x00, 0x00, 0x00 }, + { /* 0x74, */ 0x00, 0x00, 0x00 }, + { /* 0x75, */ 0x00, 0x00, 0x00 }, + { /* 0x76, */ 0x00, 0x00, 0x00 }, + { /* 0x77, */ 0x00, 0x00, 0x00 }, + { /* 0x78, */ 0x00, 0x00, 0x00 }, + { /* 0x79, */ 0x00, 0x00, 0x00 }, + { /* 0x7A, */ 0x00, 0x00, 0x00 }, + { /* 0x7B, */ 0x00, 0x00, 0x00 }, + { /* 0x7C, */ 0x00, 0x00, 0x00 }, + { /* 0x7D, */ 0x00, 0x00, 0x00 }, + { /* 0x7E, */ 0x00, 0x00, 0x00 }, + { /* 0x7F, */ 0x00, 0x00, 0x00 } +}; +#endif /* KEYBOARD_ARRAY */ diff --git a/sys/arch/macppc/dev/macintr.c b/sys/arch/macppc/dev/macintr.c new file mode 100644 index 00000000000..b3f3fbf99fc --- /dev/null +++ b/sys/arch/macppc/dev/macintr.c @@ -0,0 +1,643 @@ +/* $OpenBSD: macintr.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ + +/*- + * Copyright (c) 1995 Per Fogelstrom + * Copyright (c) 1993, 1994 Charles M. Hannum. + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz and Don Ahn. + * + * 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 the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + * + * @(#)isa.c 7.2 (Berkeley) 5/12/91 + */ + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <vm/vm.h> +#include <vm/vm_kern.h> +#include <uvm/uvm.h> + +#include <machine/autoconf.h> +#include <machine/intr.h> +#include <machine/psl.h> +#include <machine/pio.h> +#include <machine/powerpc.h> + +#include <dev/ofw/openfirm.h> + +#define ICU_LEN 64 +#define LEGAL_IRQ(x) ((x >= 0) && (x < ICU_LEN)) + +static int intrtype[ICU_LEN], intrmask[ICU_LEN], intrlevel[ICU_LEN]; +static struct intrhand *intrhand[ICU_LEN]; +static int hwirq[ICU_LEN], virq[64]; +unsigned int imen = 0xffffffff; +int virq_max = 0; + +struct evcnt evirq[ICU_LEN*2]; + +static int fakeintr __P((void *)); +static char *intr_typename __P((int type)); +static void intr_calculatemasks __P((void)); +static void enable_irq __P((int x)); +static __inline int cntlzw __P((int x)); +static int mapirq __P((int irq)); +static int read_irq __P((void)); +static void mac_intr_do_pending_int __P((void)); + +extern u_int32_t *heathrow_FCR; + +#define HWIRQ_MAX 27 +#define HWIRQ_MASK 0x0fffffff + +#define INT_STATE_REG0 (interrupt_reg + 0x20) +#define INT_ENABLE_REG0 (interrupt_reg + 0x24) +#define INT_CLEAR_REG0 (interrupt_reg + 0x28) +#define INT_LEVEL_REG0 (interrupt_reg + 0x2c) +#define INT_STATE_REG1 (INT_STATE_REG0 - 0x10) +#define INT_ENABLE_REG1 (INT_ENABLE_REG0 - 0x10) +#define INT_CLEAR_REG1 (INT_CLEAR_REG0 - 0x10) +#define INT_LEVEL_REG1 (INT_LEVEL_REG0 - 0x10) + +struct macintr_softc { + struct device sc_dev; +}; + +int macintr_match __P((struct device *parent, void *cf, void *aux)); +void macintr_attach __P((struct device *, struct device *, void *)); +void mac_do_pending_int __P((void)); +void mac_ext_intr __P((void)); + +struct cfattach macintr_ca = { + sizeof(struct macintr_softc), + macintr_match, + macintr_attach +}; + +struct cfdriver macintr_cd = { + NULL, "macintr", DV_DULL +}; + +int +macintr_match(parent, cf, aux) + struct device *parent; + void *cf; + void *aux; +{ + struct confargs *ca = aux; + char type[40]; + + /* + * Match entry according to "present" openfirmware entry. + */ + if (strcmp(ca->ca_name, "interrupt-controller") == 0 ) { + OF_getprop(ca->ca_node, "device_type", type, sizeof(type)); + if (strcmp(type, "interrupt-controller") == 0) { + return 1; + } + } + + /* + * Check name for legacy interrupt controller, this is + * faked to allow old firmware which does not have an entry + * to attach to this device. + */ + if (strcmp(ca->ca_name, "legacy-interrupt-controller") == 0 ) { + return 1; + } + return 0; +} + +u_int8_t *interrupt_reg; +typedef void (void_f) (void); +extern void_f *pending_int_f; +int prog_switch (void *arg); + +intr_establish_t macintr_establish; +intr_disestablish_t macintr_disestablish; +extern intr_establish_t *mac_intr_establish_func; +extern intr_disestablish_t *mac_intr_disestablish_func; +void macintr_collect_preconf_intr __P((void)); + +void +macintr_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct confargs *ca = aux; + extern intr_establish_t *intr_establish_func; + extern intr_disestablish_t *intr_disestablish_func; + + interrupt_reg = (void *)ca->ca_baseaddr; /* XXX */ + + install_extint(mac_ext_intr); + pending_int_f = mac_intr_do_pending_int; + intr_establish_func = macintr_establish; + intr_disestablish_func = macintr_disestablish; + mac_intr_establish_func = macintr_establish; + mac_intr_disestablish_func = macintr_disestablish; + + macintr_collect_preconf_intr(); + + mac_intr_establish(parent, 0x14, IST_LEVEL, IPL_HIGH, + prog_switch, (void *)0x14, "prog button"); + + ppc_intr_enable(1); + + printf("\n"); +} +void +macintr_collect_preconf_intr() +{ + int i; + for (i = 0; i < ppc_configed_intr_cnt; i++) { + printf("\n\t%s irq %d level %d fun %x arg %x", + ppc_configed_intr[i].ih_what, + ppc_configed_intr[i].ih_irq, + ppc_configed_intr[i].ih_level, + ppc_configed_intr[i].ih_fun, + ppc_configed_intr[i].ih_arg + ); + macintr_establish(NULL, + ppc_configed_intr[i].ih_irq, + IST_LEVEL, + ppc_configed_intr[i].ih_level, + ppc_configed_intr[i].ih_fun, + ppc_configed_intr[i].ih_arg, + ppc_configed_intr[i].ih_what); + } +} + + +int +prog_switch (void *arg) +{ +#ifdef DDB + Debugger(); +#else + printf("programmer button pressed, debugger not available\n"); +#endif + return 1; +} + +static int +fakeintr(arg) + void *arg; +{ + + return 0; +} + +void nameinterrupt( int replace, char *newstr); + +/* + * Register an interrupt handler. + */ +void * +macintr_establish(lcv, irq, type, level, ih_fun, ih_arg, name) + void * lcv; + int irq; + int type; + int level; + int (*ih_fun) __P((void *)); + void *ih_arg; + char *name; +{ + struct intrhand **p, *q, *ih; + static struct intrhand fakehand; + + fakehand.ih_next = NULL; + fakehand.ih_fun = fakeintr; + +#if 0 +printf("macintr_establish, hI %d L %d ", irq, type); +printf("addr reg0 %x\n", INT_STATE_REG0); +#endif + nameinterrupt(irq, name); + irq = mapirq(irq); +#if 0 +printf("vI %d ", irq); +#endif + + /* no point in sleeping unless someone can free memory. */ + ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); + if (ih == NULL) + panic("intr_establish: can't malloc handler info"); + + if (!LEGAL_IRQ(irq) || type == IST_NONE) + panic("intr_establish: bogus irq or type"); + + switch (intrtype[irq]) { + case IST_NONE: + intrtype[irq] = type; + break; + case IST_EDGE: + case IST_LEVEL: + if (type == intrtype[irq]) + break; + case IST_PULSE: + if (type != IST_NONE) + panic("intr_establish: can't share %s with %s", + intr_typename(intrtype[irq]), + intr_typename(type)); + break; + } + + /* + * Figure out where to put the handler. + * This is O(N^2), but we want to preserve the order, and N is + * generally small. + */ + for (p = &intrhand[irq]; (q = *p) != NULL; p = &q->ih_next) + ; + + /* + * Actually install a fake handler momentarily, since we might be doing + * this with interrupts enabled and DON'T WANt the real routine called + * until masking is set up. + */ + fakehand.ih_level = level; + *p = &fakehand; + + intr_calculatemasks(); + + /* + * Poke the real handler in now. + */ + ih->ih_fun = ih_fun; + ih->ih_arg = ih_arg; + ih->ih_count = 0; + ih->ih_next = NULL; + ih->ih_level = level; + ih->ih_irq = irq; + *p = ih; + + return (ih); +} + +/* + * Deregister an interrupt handler. + */ +void +macintr_disestablish(lcp, arg) + void *lcp; + void *arg; +{ + struct intrhand *ih = arg; + int irq = ih->ih_irq; + struct intrhand **p, *q; + + if (!LEGAL_IRQ(irq)) + panic("intr_disestablish: bogus irq"); + + /* + * Remove the handler from the chain. + * This is O(n^2), too. + */ + for (p = &intrhand[irq]; (q = *p) != NULL && q != ih; p = &q->ih_next) + ; + if (q) + *p = q->ih_next; + else + panic("intr_disestablish: handler not registered"); + free((void *)ih, M_DEVBUF); + + intr_calculatemasks(); + + if (intrhand[irq] == NULL) + intrtype[irq] = IST_NONE; +} + + +static char * +intr_typename(type) + int type; +{ + + switch (type) { + case IST_NONE : + return ("none"); + case IST_PULSE: + return ("pulsed"); + case IST_EDGE: + return ("edge-triggered"); + case IST_LEVEL: + return ("level-triggered"); + default: + panic("intr_typename: invalid type %d", type); +#if 1 /* XXX */ + return ("unknown"); +#endif + } +} +/* + * Recalculate the interrupt masks from scratch. + * We could code special registry and deregistry versions of this function that + * would be faster, but the code would be nastier, and we don't expect this to + * happen very much anyway. + */ +static void +intr_calculatemasks() +{ + int irq, level; + struct intrhand *q; + + /* First, figure out which levels each IRQ uses. */ + for (irq = 0; irq < ICU_LEN; irq++) { + register int levels = 0; + for (q = intrhand[irq]; q; q = q->ih_next) + levels |= 1 << q->ih_level; + intrlevel[irq] = levels; + } + + /* Then figure out which IRQs use each level. */ + for (level = 0; level < 5; level++) { + register int irqs = 0; + for (irq = 0; irq < ICU_LEN; irq++) + if (intrlevel[irq] & (1 << level)) + irqs |= 1 << irq; + imask[level] = irqs | SINT_MASK; + } + + /* + * There are tty, network and disk drivers that use free() at interrupt + * time, so imp > (tty | net | bio). + */ + imask[IPL_IMP] |= imask[IPL_TTY] | imask[IPL_NET] | imask[IPL_BIO]; + + /* + * Enforce a hierarchy that gives slow devices a better chance at not + * dropping data. + */ + imask[IPL_TTY] |= imask[IPL_NET] | imask[IPL_BIO]; + imask[IPL_NET] |= imask[IPL_BIO]; + + /* + * These are pseudo-levels. + */ + imask[IPL_NONE] = 0x00000000; + imask[IPL_HIGH] = 0xffffffff; + + /* And eventually calculate the complete masks. */ + for (irq = 0; irq < ICU_LEN; irq++) { + register int irqs = 1 << irq; + for (q = intrhand[irq]; q; q = q->ih_next) + irqs |= imask[q->ih_level]; + intrmask[irq] = irqs | SINT_MASK; + } + + /* Lastly, determine which IRQs are actually in use. */ + { + register int irqs = 0; + for (irq = 0; irq < ICU_LEN; irq++) { + if (intrhand[irq]) + irqs |= 1 << irq; + } + imen = ~irqs; + enable_irq(~imen); + } +} +static void +enable_irq(x) + int x; +{ + int state0, state1, v; + int irq; + + x &= HWIRQ_MASK; /* XXX Higher bits are software interrupts. */ + + state0 = state1 = 0; + while (x) { + v = 31 - cntlzw(x); + irq = hwirq[v]; + if (irq < 32) { + state0 |= 1 << irq; + } else { + state1 |= 1 << (irq - 32); + } + x &= ~(1 << v); + } + + if (heathrow_FCR) { + out32rb(INT_ENABLE_REG1, state1); + } + out32rb(INT_ENABLE_REG0, state0); +} +/* + * Map 64 irqs into 32 (bits). + */ +static int +mapirq(irq) + int irq; +{ + int v; + + if (irq < 0 || irq >= 64) + panic("invalid irq"); + virq_max++; + v = virq_max; + if (v > HWIRQ_MAX) + panic("virq overflow"); + + hwirq[v] = irq; + virq[irq] = v; +#if 0 +printf("\nmapirq %x to %x\n", irq, v); +#endif + + return v; +} + +/* + * Count leading zeros. + */ +static __inline int +cntlzw(x) + int x; +{ + int a; + + __asm __volatile ("cntlzw %0,%1" : "=r"(a) : "r"(x)); + + return a; +} + +/* + * external interrupt handler + */ +void +mac_ext_intr() +{ + int irq = 0; + int o_imen, r_imen; + int pcpl; + struct intrhand *ih; + volatile unsigned long int_state; + + pcpl = splhigh(); /* Turn off all */ + +#if 0 +printf("mac_intr \n"); +#endif + int_state = read_irq(); + if (int_state == 0) + goto out; + +start: + irq = 31 - cntlzw(int_state); + intrcnt[hwirq[irq]]++; + + o_imen = imen; + r_imen = 1 << irq; + + if ((pcpl & r_imen) != 0) { + ipending |= r_imen; /* Masked! Mark this as pending */ + imen |= r_imen; + enable_irq(~imen); + } else { + ih = intrhand[irq]; + while (ih) { +#if 0 +printf("calling handler %x\n", ih->ih_fun); +#endif + (*ih->ih_fun)(ih->ih_arg); + ih = ih->ih_next; + } + + uvmexp.intrs++; + evirq[hwirq[irq]].ev_count++; + } + int_state &= ~r_imen; + if (int_state) + goto start; + +out: + splx(pcpl); /* Process pendings. */ +} + +void +mac_intr_do_pending_int() +{ + struct intrhand *ih; + int irq; + int pcpl; + int hwpend; + int emsr, dmsr; + static int processing; + + if (processing) + return; + + processing = 1; + pcpl = splhigh(); /* Turn off all */ + asm volatile("mfmsr %0" : "=r"(emsr)); + dmsr = emsr & ~PSL_EE; + asm volatile("mtmsr %0" :: "r"(dmsr)); + + hwpend = ipending & ~pcpl; /* Do now unmasked pendings */ + imen &= ~hwpend; + enable_irq(~imen); + hwpend &= HWIRQ_MASK; + while (hwpend) { + irq = 31 - cntlzw(hwpend); + hwpend &= ~(1L << irq); + ih = intrhand[irq]; + while(ih) { + (*ih->ih_fun)(ih->ih_arg); + ih = ih->ih_next; + } + + evirq[hwirq[irq]].ev_count++; + } + + /*out32rb(INT_ENABLE_REG, ~imen);*/ + + do { + if((ipending & SINT_CLOCK) & ~pcpl) { + ipending &= ~SINT_CLOCK; + softclock(); + } + if((ipending & SINT_NET) & ~pcpl) { + extern int netisr; + int pisr = netisr; + netisr = 0; + ipending &= ~SINT_NET; + softnet(pisr); + } + } while (ipending & (SINT_NET|SINT_CLOCK) & ~cpl); + ipending &= pcpl; + cpl = pcpl; /* Don't use splx... we are here already! */ + asm volatile("mtmsr %0" :: "r"(emsr)); + processing = 0; +} + +static int +read_irq() +{ + int rv = 0; + int state0, state1, p; + int state0save, state1save; + + state0 = in32rb(INT_STATE_REG0); + if (state0) + out32rb(INT_CLEAR_REG0, state0); + state0save = state0; + while (state0) { + p = 31 - cntlzw(state0); + rv |= 1 << virq[p]; + state0 &= ~(1 << p); + } + + if (heathrow_FCR) /* has heathrow? */ + state1 = in32rb(INT_STATE_REG1); + else + state1 = 0; + + if (state1) + out32rb(INT_CLEAR_REG1, state1); + state1save = state1; + while (state1) { + p = 31 - cntlzw(state1); + rv |= 1 << virq[p + 32]; + state1 &= ~(1 << p); + } +#if 0 +printf("mac_intr int_stat 0:%x 1:%x\n", state0save, state1save); +#endif + + /* 1 << 0 is invalid. */ + return rv & ~1; +} diff --git a/sys/arch/macppc/dev/mesh.c b/sys/arch/macppc/dev/mesh.c new file mode 100644 index 00000000000..3b08fdde4e0 --- /dev/null +++ b/sys/arch/macppc/dev/mesh.c @@ -0,0 +1,1181 @@ +/* $OpenBSD: mesh.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: mesh.c,v 1.1 1999/02/19 13:06:03 tsubai Exp $ */ + +/*- + * Copyright (C) 1999 Internet Research Institute, Inc. + * 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 + * Internet Research Institute, Inc. + * 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 <sys/param.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/queue.h> +#include <sys/systm.h> + +#include <vm/vm.h> + + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <scsi/scsi_message.h> +/* +#include "scsipi/scsi_all.h" +#include "scsipi/scsipi_all.h" +#include "scsipi/scsiconf.h" +#include "scsipi/scsi_message.h"*/ + + + +#include <dev/ofw/openfirm.h> + +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <machine/pio.h> + +#include "dbdma.h" +#include "meshreg.h" + +#define T_SYNCMODE 0x01 /* target uses sync mode */ +#define T_SYNCNEGO 0x02 /* sync negotiation done */ + + + +struct mesh_tinfo { + int flags; + int period; + int offset; +}; + +/* scb flags */ +#define MESH_POLL 0x01 +#define MESH_CHECK 0x02 +#define MESH_SENSE 0x04 +#define MESH_READ 0x80 + +struct mesh_scb { + TAILQ_ENTRY(mesh_scb) chain; + int flags; + struct scsi_xfer *xs; + struct scsi_generic cmd; + int cmdlen; + int target; /* target SCSI ID */ + int resid; + vaddr_t daddr; + vsize_t dlen; + int status; +}; + +/* sc_flags value */ +#define MESH_DMA_ACTIVE 0x01 + +struct mesh_softc { + struct device sc_dev; /* us as a device */ + struct scsi_link sc_link; + struct scsi_adapter sc_adapter; + + u_char *sc_reg; /* MESH base address */ + dbdma_regmap_t *sc_dmareg; /* DMA register address */ + dbdma_command_t *sc_dmacmd; /* DMA command area */ + + int sc_flags; + int sc_cfflags; /* copy of config flags */ + int sc_meshid; /* MESH version */ + int sc_minsync; /* minimum sync period */ + int sc_irq; + int sc_freq; /* SCSI bus frequency in MHz */ + int sc_id; /* our SCSI ID */ + struct mesh_tinfo sc_tinfo[8]; /* target information */ + + int sc_nextstate; + int sc_prevphase; + struct mesh_scb *sc_nexus; /* current command */ + + int sc_msgout; + int sc_imsglen; + int sc_omsglen; + u_char sc_imsg[16]; + u_char sc_omsg[16]; + + TAILQ_HEAD(, mesh_scb) free_scb; + TAILQ_HEAD(, mesh_scb) ready_scb; + struct mesh_scb sc_scb[16]; + + struct timeout sc_tmo; +}; + +/* mesh_msgout() values */ +#define SEND_REJECT 1 +#define SEND_IDENTIFY 2 +#define SEND_SDTR 4 + + +#ifdef __OpenBSD__ +#define scsi_print_addr sc_print_addr +#define scsipi_done scsi_done +#endif + +static __inline int mesh_read_reg __P((struct mesh_softc *, int)); +static __inline void mesh_set_reg __P((struct mesh_softc *, int, int)); + +int mesh_match __P((struct device *, struct cfdata *, void *)); +void mesh_attach __P((struct device *, struct device *, void *)); +void mesh_shutdownhook __P((void *)); +int mesh_intr __P((void *)); +void mesh_error __P((struct mesh_softc *, struct mesh_scb *, int, int)); +void mesh_select __P((struct mesh_softc *, struct mesh_scb *)); +void mesh_identify __P((struct mesh_softc *, struct mesh_scb *)); +void mesh_command __P((struct mesh_softc *, struct mesh_scb *)); +void mesh_dma_setup __P((struct mesh_softc *, struct mesh_scb *)); +void mesh_dataio __P((struct mesh_softc *, struct mesh_scb *)); +void mesh_status __P((struct mesh_softc *, struct mesh_scb *)); +void mesh_msgin __P((struct mesh_softc *, struct mesh_scb *)); +void mesh_msgout __P((struct mesh_softc *, int)); +void mesh_bus_reset __P((struct mesh_softc *)); +void mesh_reset __P((struct mesh_softc *)); +int mesh_stp __P((struct mesh_softc *, int)); +void mesh_setsync __P((struct mesh_softc *, struct mesh_tinfo *)); +struct mesh_scb *mesh_get_scb __P((struct mesh_softc *)); +void mesh_free_scb __P((struct mesh_softc *, struct mesh_scb *)); +int mesh_scsi_cmd __P((struct scsi_xfer *)); +void mesh_sched __P((struct mesh_softc *)); +int mesh_poll __P((struct mesh_softc *, struct scsi_xfer *)); +void mesh_done __P((struct mesh_softc *, struct mesh_scb *)); +void mesh_timeout __P((void *)); +void mesh_sense __P((struct mesh_softc *, struct mesh_scb *)); +void mesh_minphys __P((struct buf *)); + + +#define MESH_DATAOUT 0 +#define MESH_DATAIN MESH_STATUS0_IO +#define MESH_COMMAND MESH_STATUS0_CD +#define MESH_STATUS (MESH_STATUS0_CD | MESH_STATUS0_IO) +#define MESH_MSGOUT (MESH_STATUS0_MSG | MESH_STATUS0_CD) +#define MESH_MSGIN (MESH_STATUS0_MSG | MESH_STATUS0_CD | MESH_STATUS0_IO) + +#define MESH_SELECTING 8 +#define MESH_IDENTIFY 9 +#define MESH_COMPLETE 10 +#define MESH_BUSFREE 11 +#define MESH_UNKNOWN -1 + +#define MESH_PHASE_MASK (MESH_STATUS0_MSG | MESH_STATUS0_CD | MESH_STATUS0_IO) + +struct cfattach mesh_ca = { + sizeof(struct mesh_softc),(cfmatch_t)mesh_match, + mesh_attach +}; + + +struct cfdriver mesh_cd = { + NULL, "mesh", DV_DULL +}; + +struct scsi_device mesh_dev = { + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ +}; + +int +mesh_match(parent, cf, aux) + struct device *parent; + struct cfdata *cf; + void *aux; +{ + struct confargs *ca = aux; + printf("MESH_MATCH called ,ca->ca_name= %s\n",ca->ca_name); + + if (strcmp(ca->ca_name, "mesh") != 0) + return 0; + + return 1; +} + +void +mesh_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct mesh_softc *sc = (void *)self; + struct confargs *ca = aux; + int i; + u_int *reg; + + printf("MESH_ATTACH called\n"); + + reg = ca->ca_reg; + reg[0] += ca->ca_baseaddr; + reg[2] += ca->ca_baseaddr; + sc->sc_reg = mapiodev(reg[0], reg[1]); + sc->sc_irq = ca->ca_intr[0]; + sc->sc_dmareg = mapiodev(reg[2], reg[3]); + + sc->sc_cfflags = self->dv_cfdata->cf_flags; + sc->sc_meshid = mesh_read_reg(sc, MESH_MESH_ID) & 0x1f; +#if 0 + if (sc->sc_meshid != (MESH_SIGNATURE & 0x1f) { + printf(": unknown MESH ID (0x%x)\n", sc->sc_meshid); + return; + } +#endif + if (OF_getprop(ca->ca_node, "clock-frequency", &sc->sc_freq, 4) != 4) { + printf(": cannot get clock-frequency\n"); + return; + } + sc->sc_freq /= 1000000; /* in MHz */ + sc->sc_minsync = 25; /* maximum sync rate = 10MB/sec */ + sc->sc_id = 7; + + TAILQ_INIT(&sc->free_scb); + TAILQ_INIT(&sc->ready_scb); + for (i = 0; i < sizeof(sc->sc_scb)/sizeof(sc->sc_scb[0]); i++) + TAILQ_INSERT_TAIL(&sc->free_scb, &sc->sc_scb[i], chain); + + sc->sc_dmacmd = dbdma_alloc(sizeof(dbdma_command_t) * 20); + timeout_set(&sc->sc_tmo, mesh_timeout, scb); + + mesh_reset(sc); + mesh_bus_reset(sc); + + printf(" irq %d: %dMHz, SCSI ID %d\n", + sc->sc_irq, sc->sc_freq, sc->sc_id); + + sc->sc_adapter.scsi_cmd = mesh_scsi_cmd; + sc->sc_adapter.scsi_minphys = mesh_minphys; + +/* sc->sc_link.scsi_scsi.channel = SCSI_CHANNEL_ONLY_ONE;*/ + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter_target = sc->sc_id; + sc->sc_link.adapter = &sc->sc_adapter; + sc->sc_link.device = &mesh_dev; + sc->sc_link.openings = 2; +/* sc->sc_link.scsi_scsi.max_target = 7; + sc->sc_link.scsi_scsi.max_lun = 7; + sc->sc_link.type = BUS_SCSI; +*/ + config_found(&sc->sc_dev, &sc->sc_link, scsiprint); + + printf("in mesh_attach, after config_found,calling mac_intr_establish,sc->sc_irq = %d\n",sc->sc_irq); + + /*intr_establish(sc->sc_irq, IST_LEVEL, IPL_BIO, mesh_intr, sc);*/ + mac_intr_establish(parent,sc->sc_irq,IST_LEVEL, IPL_BIO, mesh_intr, sc,"mesh intr"); + /* Reset SCSI bus when halt. */ + shutdownhook_establish(mesh_shutdownhook, sc); +} + +#define MESH_SET_XFER(sc, count) do { \ + mesh_set_reg(sc, MESH_XFER_COUNT0, count); \ + mesh_set_reg(sc, MESH_XFER_COUNT1, count >> 8); \ +} while (0) + +#define MESH_GET_XFER(sc) ((mesh_read_reg(sc, MESH_XFER_COUNT1) << 8) | \ + mesh_read_reg(sc, MESH_XFER_COUNT0)) + +int +mesh_read_reg(sc, reg) + struct mesh_softc *sc; + int reg; +{ + return in8(sc->sc_reg + reg); +} + +void +mesh_set_reg(sc, reg, val) + struct mesh_softc *sc; + int reg, val; +{ + out8(sc->sc_reg + reg, val); +} + +void +mesh_shutdownhook(arg) + void *arg; +{ + struct mesh_softc *sc = arg; + + /* Set to async mode. */ + mesh_set_reg(sc, MESH_SYNC_PARAM, 2); +} + +int +mesh_intr(arg) + void *arg; +{ + struct mesh_softc *sc = arg; + struct mesh_scb *scb; + u_char intr, exception, error, status0, status1; + int i; + + intr = mesh_read_reg(sc, MESH_INTERRUPT); + +#ifdef MESH_DEBUG + if (intr == 0) { + printf("mesh: stray interrupt\n"); + return 0; + } +#endif + + exception = mesh_read_reg(sc, MESH_EXCEPTION); + error = mesh_read_reg(sc, MESH_ERROR); + status0 = mesh_read_reg(sc, MESH_BUS_STATUS0); + status1 = mesh_read_reg(sc, MESH_BUS_STATUS1); + + /* clear interrupt */ + mesh_set_reg(sc, MESH_INTERRUPT, intr); + + scb = sc->sc_nexus; + if (scb == NULL) { +#ifdef MESH_DEBUG + printf("mesh: NULL nexus\n"); +#endif + return 1; + } + + if (sc->sc_flags & MESH_DMA_ACTIVE) { + dbdma_stop(sc->sc_dmareg); + + sc->sc_flags &= ~MESH_DMA_ACTIVE; + scb->resid = MESH_GET_XFER(sc); + + if (mesh_read_reg(sc, MESH_FIFO_COUNT) != 0) + panic("mesh: FIFO != 0"); /* XXX */ + } + + if (intr & MESH_INTR_ERROR) { + mesh_error(sc, scb, error, 0); + return 1; + } + + if (intr & MESH_INTR_EXCEPTION) { + /* selection timeout */ + if (exception & MESH_EXC_SELTO) { + mesh_error(sc, scb, 0, exception); + return 1; + } + + /* phase mismatch */ + if (exception & MESH_EXC_PHASEMM) { + sc->sc_nextstate = status0 & MESH_PHASE_MASK; +#if 0 + printf("mesh: PHASE MISMATCH cdb ="); + printf(" %02x", scb->cmd.opcode); + for (i = 0; i < 5; i++) { + printf(" %02x", scb->cmd.bytes[i]); + } + printf("\n"); +#endif + } + } + + if (sc->sc_nextstate == MESH_UNKNOWN) + sc->sc_nextstate = status0 & MESH_PHASE_MASK; + + switch (sc->sc_nextstate) { + + case MESH_IDENTIFY: + mesh_identify(sc, scb); + break; + case MESH_COMMAND: + mesh_command(sc, scb); + printf("mesh_intr:case MESH_COMMAND\n"); + break; + case MESH_DATAIN: + case MESH_DATAOUT: + mesh_dataio(sc, scb); + printf("mesh_intr:case MESH_DATAIN or MESH_DATAOUT\n"); + break; + case MESH_STATUS: + mesh_status(sc, scb); + printf("mesh_intr:case MESH_STATUS\n"); + break; + case MESH_MSGIN: + mesh_msgin(sc, scb); + printf("mesh_intr:case MESH_MSGIN\n"); + break; + case MESH_COMPLETE: + printf("mesh_intr:case MESH_COMPLETE\n"); + mesh_done(sc, scb); + break; + + default: + panic("mesh: unknown state (0x%x)", sc->sc_nextstate); + } + + return 1; +} + +void +mesh_error(sc, scb, error, exception) + struct mesh_softc *sc; + struct mesh_scb *scb; + int error, exception; +{ + if (error & MESH_ERR_SCSI_RESET) { + printf("mesh: SCSI RESET\n"); + + /* Wait until the RST signal is deasserted. */ + while (mesh_read_reg(sc, MESH_BUS_STATUS1) & MESH_STATUS1_RST); + mesh_reset(sc); + return; + } + + if (error & MESH_ERR_PARITY_ERR0) { + printf("mesh: parity error\n"); + scb->xs->error = XS_DRIVER_STUFFUP; + } + + if (error & MESH_ERR_DISCONNECT) { + printf("mesh: unexpected disconnect\n"); + if (sc->sc_nextstate != MESH_COMPLETE) + scb->xs->error = XS_DRIVER_STUFFUP; + } + + if (exception & MESH_EXC_SELTO) { + /* XXX should reset bus here? */ + scb->xs->error = XS_DRIVER_STUFFUP; + } + + mesh_done(sc, scb); +} + +void +mesh_select(sc, scb) + struct mesh_softc *sc; + struct mesh_scb *scb; +{ + struct mesh_tinfo *ti = &sc->sc_tinfo[scb->target]; + + mesh_setsync(sc, ti); + MESH_SET_XFER(sc, 0); + + /* arbitration */ + + /* + * MESH mistakenly asserts TARGET ID bit along with its own ID bit + * in arbitration phase (like selection). So we should load + * initiator ID to DestID register temporarily. + */ + mesh_set_reg(sc, MESH_DEST_ID, sc->sc_id); + mesh_set_reg(sc, MESH_INTR_MASK, 0); /* disable intr. */ + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_ARBITRATE); + + while (mesh_read_reg(sc, MESH_INTERRUPT) == 0); + mesh_set_reg(sc, MESH_INTERRUPT, 1); + mesh_set_reg(sc, MESH_INTR_MASK, 7); + + /* selection */ + mesh_set_reg(sc, MESH_DEST_ID, scb->target); + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_SELECT | MESH_SEQ_ATN); + + sc->sc_prevphase = MESH_SELECTING; + sc->sc_nextstate = MESH_IDENTIFY; + + timeout_add(&sc->sc_tmo, 10*hz); +} + +void +mesh_identify(sc, scb) + struct mesh_softc *sc; + struct mesh_scb *scb; +{ + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_FLUSH_FIFO); + mesh_msgout(sc, SEND_IDENTIFY); + + sc->sc_nextstate = MESH_COMMAND; + printf("mesh_identify called\n"); +} + +void +mesh_command(sc, scb) + struct mesh_softc *sc; + struct mesh_scb *scb; +{ + struct mesh_tinfo *ti = &sc->sc_tinfo[scb->target]; + int i; + char *cmdp; + + if ((ti->flags & T_SYNCNEGO) == 0) { + ti->period = sc->sc_minsync; + ti->offset = 15; + mesh_msgout(sc, SEND_SDTR); + sc->sc_prevphase = MESH_COMMAND; + sc->sc_nextstate = MESH_MSGIN; + return; + } + + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_FLUSH_FIFO); + + MESH_SET_XFER(sc, scb->cmdlen); + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_COMMAND); + + cmdp = (char *)&scb->cmd; + for (i = 0; i < scb->cmdlen; i++) + mesh_set_reg(sc, MESH_FIFO, *cmdp++); + + if (scb->resid == 0) + sc->sc_nextstate = MESH_STATUS; /* no data xfer */ + else + sc->sc_nextstate = MESH_DATAIN; +} + +void +mesh_dma_setup(sc, scb) + struct mesh_softc *sc; + struct mesh_scb *scb; +{ + struct scsi_xfer *xs = scb->xs; + int datain = scb->flags & MESH_READ; + dbdma_command_t *cmdp; + u_int cmd; + vaddr_t va; + int count, offset; + + cmdp = sc->sc_dmacmd; + cmd = datain ? DBDMA_CMD_IN_MORE : DBDMA_CMD_OUT_MORE; + + count = scb->dlen; + + if (count / NBPG > 32) + panic("mesh: transfer size >= 128k"); + + va = scb->daddr; + offset = va & PGOFSET; + + /* if va is not page-aligned, setup the first page */ + if (offset != 0) { + int rest = NBPG - offset; /* the rest in the page */ + + if (count > rest) { /* if continues to next page */ + DBDMA_BUILD(cmdp, cmd, 0, rest, vtophys(va), + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, + DBDMA_BRANCH_NEVER); + count -= rest; + va += rest; + cmdp++; + } + } + + /* now va is page-aligned */ + while (count > NBPG) { + DBDMA_BUILD(cmdp, cmd, 0, NBPG, vtophys(va), + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + count -= NBPG; + va += NBPG; + cmdp++; + } + + /* the last page (count <= NBPG here) */ + cmd = datain ? DBDMA_CMD_IN_LAST : DBDMA_CMD_OUT_LAST; + DBDMA_BUILD(cmdp, cmd , 0, count, vtophys(va), + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + cmdp++; + + DBDMA_BUILD(cmdp, DBDMA_CMD_STOP, 0, 0, 0, + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); +} + +void +mesh_dataio(sc, scb) + struct mesh_softc *sc; + struct mesh_scb *scb; +{ + mesh_dma_setup(sc, scb); + + if (scb->dlen == 65536) + MESH_SET_XFER(sc, 0); /* TC = 0 means 64KB transfer */ + else + MESH_SET_XFER(sc, scb->dlen); + + if (scb->flags & MESH_READ) + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_DATAIN | MESH_SEQ_DMA); + else + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_DATAOUT | MESH_SEQ_DMA); + dbdma_start(sc->sc_dmareg, sc->sc_dmacmd); + sc->sc_flags |= MESH_DMA_ACTIVE; + sc->sc_nextstate = MESH_STATUS; +} + +void +mesh_status(sc, scb) + struct mesh_softc *sc; + struct mesh_scb *scb; +{ + if (mesh_read_reg(sc, MESH_FIFO_COUNT) == 0) { /* XXX cheat */ + MESH_SET_XFER(sc, 1); + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_STATUS); + sc->sc_nextstate = MESH_STATUS; + return; + } + + scb->status = mesh_read_reg(sc, MESH_FIFO); + + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_FLUSH_FIFO); + MESH_SET_XFER(sc, 1); + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_MSGIN); + + sc->sc_nextstate = MESH_MSGIN; +} + +#define IS1BYTEMSG(m) (((m) != 1 && (m) < 0x20) || (m) & 0x80) +#define IS2BYTEMSG(m) (((m) & 0xf0) == 0x20) +#define ISEXTMSG(m) ((m) == 1) + +void +mesh_msgin(sc, scb) + struct mesh_softc *sc; + struct mesh_scb *scb; +{ + int i; + + if (mesh_read_reg(sc, MESH_FIFO_COUNT) == 0) { /* XXX cheat */ + MESH_SET_XFER(sc, 1); + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_MSGIN); + sc->sc_imsglen = 0; + sc->sc_nextstate = MESH_MSGIN; + return; + } + + sc->sc_imsg[sc->sc_imsglen++] = mesh_read_reg(sc, MESH_FIFO); + + if (sc->sc_imsglen == 1 && IS1BYTEMSG(sc->sc_imsg[0])) + goto gotit; + if (sc->sc_imsglen == 2 && IS2BYTEMSG(sc->sc_imsg[0])) + goto gotit; + if (sc->sc_imsglen >= 3 && ISEXTMSG(sc->sc_imsg[0]) && + sc->sc_imsglen == sc->sc_imsg[1] + 2) + goto gotit; + + sc->sc_nextstate = MESH_MSGIN; + MESH_SET_XFER(sc, 1); + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_MSGIN); + return; + +gotit: +#ifdef DEBUG + printf("msgin:"); + for (i = 0; i < sc->sc_imsglen; i++) + printf(" 0x%02x", sc->sc_imsg[i]); + printf("\n"); +#endif + + switch (sc->sc_imsg[0]) { + case MSG_CMDCOMPLETE: + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_BUSFREE); + sc->sc_nextstate = MESH_COMPLETE; + sc->sc_imsglen = 0; + return; + + case MSG_MESSAGE_REJECT: + switch (sc->sc_msgout) { + case SEND_SDTR: + printf("SDTR rejected\n"); + printf("using async mode\n"); + sc->sc_tinfo[scb->target].period = 0; + sc->sc_tinfo[scb->target].offset = 0; + mesh_setsync(sc, &sc->sc_tinfo[scb->target]); + break; + } + break; + + case MSG_NOOP: + break; + + case MSG_EXTENDED: + goto extended_msg; + + default: + scsi_print_addr(scb->xs->sc_link); + printf("unrecognized MESSAGE(0x%02x); sending REJECT\n", + sc->sc_imsg[0]); + + reject: + mesh_msgout(sc, SEND_REJECT); + return; + } + goto done; + +extended_msg: + /* process an extended message */ + switch (sc->sc_imsg[2]) { + case MSG_EXT_SDTR: + { + struct mesh_tinfo *ti = &sc->sc_tinfo[scb->target]; + int period = sc->sc_imsg[3]; + int offset = sc->sc_imsg[4]; + int r = 250 / period; + int s = (100*250) / period - 100 * r; + + if (period < sc->sc_minsync) { + ti->period = sc->sc_minsync; + ti->offset = 15; + mesh_msgout(sc, SEND_SDTR); + return; + } + scsi_print_addr(scb->xs->sc_link); + /* XXX if (offset != 0) ... */ + printf("max sync rate %d.%02dMb/s\n", r, s); + ti->period = period; + ti->offset = offset; + ti->flags |= T_SYNCNEGO; + ti->flags |= T_SYNCMODE; + mesh_setsync(sc, ti); + goto done; + } + default: + printf("%s target %d: rejecting extended message 0x%x\n", + sc->sc_dev.dv_xname, scb->target, sc->sc_imsg[0]); + goto reject; + } + +done: + sc->sc_imsglen = 0; + sc->sc_nextstate = MESH_UNKNOWN; + + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_BUSFREE); /* XXX really? */ +} + +void +mesh_msgout(sc, msg) + struct mesh_softc *sc; + int msg; +{ + struct mesh_scb *scb = sc->sc_nexus; + struct mesh_tinfo *ti; + int lun, i; + + switch (msg) { + case SEND_REJECT: + sc->sc_omsglen = 1; + sc->sc_omsg[0] = MSG_MESSAGE_REJECT; + break; + case SEND_IDENTIFY: + lun = scb->xs->sc_link->lun; + sc->sc_omsglen = 1; + sc->sc_omsg[0] = MSG_IDENTIFY(lun, 0); + break; + case SEND_SDTR: + ti = &sc->sc_tinfo[scb->target]; + sc->sc_omsglen = 5; + sc->sc_omsg[0] = MSG_EXTENDED; + sc->sc_omsg[1] = 3; + sc->sc_omsg[2] = MSG_EXT_SDTR; + sc->sc_omsg[3] = ti->period; + sc->sc_omsg[4] = ti->offset; + break; + } + sc->sc_msgout = msg; + + MESH_SET_XFER(sc, sc->sc_omsglen); + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_MSGOUT | MESH_SEQ_ATN); + + for (i = 0; i < sc->sc_omsglen; i++) + mesh_set_reg(sc, MESH_FIFO, sc->sc_omsg[i]); + + sc->sc_nextstate = MESH_UNKNOWN; +} + +void +mesh_bus_reset(sc) + struct mesh_softc *sc; +{ + /* Disable interrupts. */ + mesh_set_reg(sc, MESH_INTR_MASK, 0); + + /* Assert RST line. */ + mesh_set_reg(sc, MESH_BUS_STATUS1, MESH_STATUS1_RST); + delay(50); + mesh_set_reg(sc, MESH_BUS_STATUS1, 0); + + mesh_reset(sc); +} + +void +mesh_reset(sc) + struct mesh_softc *sc; +{ + int i; + + /* Reset DMA first. */ + dbdma_reset(sc->sc_dmareg); + + /* Disable interrupts. */ + mesh_set_reg(sc, MESH_INTR_MASK, 0); + + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_RESET_MESH); + delay(1); + + /* Wait for reset done. */ + while (mesh_read_reg(sc, MESH_INTERRUPT) == 0); + + /* Clear interrupts */ + mesh_set_reg(sc, MESH_INTERRUPT, 0x7); + + /* Set SCSI ID */ + mesh_set_reg(sc, MESH_SOURCE_ID, sc->sc_id); + + /* Set to async mode by default. */ + mesh_set_reg(sc, MESH_SYNC_PARAM, 2); + + /* Set selection timeout to 250ms. */ + mesh_set_reg(sc, MESH_SEL_TIMEOUT, 250 * sc->sc_freq / 500); + + /* Enable parity check. */ + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_ENABLE_PARITY); + + /* Enable all interrupts. */ + mesh_set_reg(sc, MESH_INTR_MASK, 0x7); + + for (i = 0; i < 7; i++) { + struct mesh_tinfo *ti = &sc->sc_tinfo[i]; + + ti->flags = 0; + ti->period = ti->offset = 0; + if (sc->sc_cfflags & (1 << i)) { + ti->flags |= T_SYNCNEGO; + } + } + sc->sc_nexus = NULL; +} + +int +mesh_stp(sc, v) + struct mesh_softc *sc; + int v; +{ + /* + * stp(v) = 5 * clock_period (v == 0) + * = (v + 2) * 2 clock_period (v > 0) + */ + + if (v == 0) + return 5 * 250 / sc->sc_freq; + else + return (v + 2) * 2 * 250 / sc->sc_freq; +} + +void +mesh_setsync(sc, ti) + struct mesh_softc *sc; + struct mesh_tinfo *ti; +{ + int period = ti->period; + int offset = ti->offset; + int v; + + if ((ti->flags & T_SYNCMODE) == 0) + offset = 0; + + if (offset == 0) { /* async mode */ + mesh_set_reg(sc, MESH_SYNC_PARAM, 2); + return; + } + + v = period * sc->sc_freq / 250 / 2 - 2; + if (v < 0) + v = 0; + if (mesh_stp(sc, v) < period) + v++; + if (v > 15) + v = 15; + mesh_set_reg(sc, MESH_SYNC_PARAM, (offset << 4) | v); +} + +struct mesh_scb * +mesh_get_scb(sc) + struct mesh_softc *sc; +{ + struct mesh_scb *scb; + int s; + + s = splbio(); + while ((scb = sc->free_scb.tqh_first) == NULL) + tsleep(&sc->free_scb, PRIBIO, "meshscb", 0); + TAILQ_REMOVE(&sc->free_scb, scb, chain); + splx(s); + + return scb; +} + +void +mesh_free_scb(sc, scb) + struct mesh_softc *sc; + struct mesh_scb *scb; +{ + int s; + + s = splbio(); + TAILQ_INSERT_HEAD(&sc->free_scb, scb, chain); + if (scb->chain.tqe_next == NULL) + wakeup(&sc->free_scb); + splx(s); +} + +int +mesh_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + struct scsi_link *sc_link = xs->sc_link; + struct mesh_softc *sc = sc_link->adapter_softc; + struct mesh_scb *scb; + u_int flags; + int s; + + flags = xs->flags; + + scb = mesh_get_scb(sc); + scb->xs = xs; + scb->flags = 0; + scb->status = 0; + scb->daddr = (vaddr_t)xs->data; + scb->dlen = xs->datalen; + scb->resid = xs->datalen; + bcopy(xs->cmd, &scb->cmd, xs->cmdlen); + scb->cmdlen = xs->cmdlen; + + scb->target = sc_link->target; + sc->sc_imsglen = 0; /* XXX ? */ + + printf("messh_scsi_cmd,scb->target=%d\n",scb->target); + + if (flags & SCSI_POLL){ + printf("mesh_scsi_cmd:flags=SCSI_POLL\n"); + scb->flags |= MESH_POLL; + } + +#if 0 + if (flags & SCSI_DATA_OUT) + scb->flags &= ~MESH_READ; +#endif + if (flags & SCSI_DATA_IN){ + printf("mesh_scsi_cmd:flags=SCSI_DATA_IN\n"); + scb->flags |= MESH_READ; + } + + s = splbio(); + + TAILQ_INSERT_TAIL(&sc->ready_scb, scb, chain); + + if (sc->sc_nexus == NULL){ + printf("mesh_scsi_cmd:sc->sc_nexus == NULL,calling mesh_sched\n"); /* IDLE */ + mesh_sched(sc); + } + + splx(s); + + if ((flags & SCSI_POLL) == 0){ + printf("mesh_scsi_cmd: returning SUCCESSFULLY_QUEUED\n"); + return SUCCESSFULLY_QUEUED; + } + + if (mesh_poll(sc, xs)) { + printf("mesh: timeout\n"); + if (mesh_poll(sc, xs)) + printf("mesh: timeout again\n"); + } + printf("mesh_scsi_cmd: returning COMPLETE\n"); + return COMPLETE; +} + +void +mesh_sched(sc) + struct mesh_softc *sc; +{ + struct scsi_xfer *xs; + struct scsi_link *sc_link; + struct mesh_scb *scb; + + scb = sc->ready_scb.tqh_first; +start: + if (scb == NULL) + return; + + xs = scb->xs; + sc_link = xs->sc_link; + + if (sc->sc_nexus == NULL) { + TAILQ_REMOVE(&sc->ready_scb, scb, chain); + sc->sc_nexus = scb; + mesh_select(sc, scb); + return; + } + + scb = scb->chain.tqe_next; + goto start; +} + +int +mesh_poll(sc, xs) + struct mesh_softc *sc; + struct scsi_xfer *xs; +{ + int count = xs->timeout; + printf("in mesh_poll,timeout=%d\n",xs->timeout); + + + while (count) { + if (mesh_read_reg(sc, MESH_INTERRUPT)) + mesh_intr(sc); + + if (xs->flags & ITSDONE) + return 0; + DELAY(1000); + count--; + }; + return 1; +} + +void +mesh_done(sc, scb) + struct mesh_softc *sc; + struct mesh_scb *scb; +{ + struct scsi_xfer *xs = scb->xs; + +#ifdef MESH_SHOWSTATE + printf("mesh_done\n"); +#endif + + sc->sc_nextstate = MESH_BUSFREE; + sc->sc_nexus = NULL; + + timeout_del(&sc->sc_tmo); + + if (scb->status == SCSI_BUSY) { + xs->error = XS_BUSY; + printf("Target busy\n"); + } + + if (scb->status == SCSI_CHECK) { + if (scb->flags & MESH_SENSE) + panic("SCSI_CHECK && MESH_SENSE?"); + xs->resid = scb->resid; + mesh_sense(sc, scb); + return; + } + + if (xs->error == XS_NOERROR) { + xs->status = scb->status; + if (scb->flags & MESH_SENSE) + xs->error = XS_SENSE; + else + xs->resid = scb->resid; + } + + xs->flags |= ITSDONE; + + mesh_set_reg(sc, MESH_SYNC_PARAM, 2); + + if ((xs->flags & SCSI_POLL) == 0) + mesh_sched(sc); + + scsi_done(xs); + mesh_free_scb(sc, scb); +} + +void +mesh_timeout(arg) + void *arg; +{ + struct mesh_scb *scb = arg; + struct mesh_softc *sc = scb->xs->sc_link->adapter_softc; + int s; + int status0, status1; + int intr, error, exception; + + printf("mesh: timeout state=%x\n", sc->sc_nextstate); + intr = mesh_read_reg(sc, MESH_INTERRUPT); + + + exception = mesh_read_reg(sc, MESH_EXCEPTION); + + error = mesh_read_reg(sc, MESH_ERROR); + + status0 = mesh_read_reg(sc, MESH_BUS_STATUS0); + + status1 = mesh_read_reg(sc, MESH_BUS_STATUS1); + +#if 1 +printf("intr 0x%02x, except 0x%02x, err 0x%02x\n", intr, exception, error); +#endif + + s = splbio(); + + if (sc->sc_flags & MESH_DMA_ACTIVE) { + printf("mesh: resetting dma\n"); + dbdma_reset(sc->sc_dmareg); + } + scb->xs->error = XS_TIMEOUT; + + mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_BUSFREE); + + sc->sc_nextstate = MESH_COMPLETE; + + splx(s); + printf("rerturning from mesh_timeout\n"); +} + +void +mesh_sense(sc, scb) + struct mesh_softc *sc; + struct mesh_scb *scb; +{ + struct scsi_xfer *xs = scb->xs; + struct scsi_link *sc_link = xs->sc_link; + struct scsi_sense *ss = (void *)&scb->cmd; + + bzero(ss, sizeof(*ss)); + ss->opcode = REQUEST_SENSE; + ss->byte2 = sc_link->lun << 5; + ss->length = sizeof(struct scsi_sense_data); + scb->cmdlen = sizeof(*ss); + scb->daddr = (vaddr_t)&xs->sense; + scb->dlen = sizeof(struct scsi_sense_data); + scb->resid = scb->dlen; + bzero((void *)scb->daddr, scb->dlen); + + scb->flags |= MESH_SENSE | MESH_READ; + + TAILQ_INSERT_HEAD(&sc->ready_scb, scb, chain); + if (sc->sc_nexus == NULL) + mesh_sched(sc); +} + +void +mesh_minphys(bp) + struct buf *bp; +{ + if (bp->b_bcount > 64*1024) + bp->b_bcount = 64*1024; + + minphys(bp); +} diff --git a/sys/arch/macppc/dev/meshreg.h b/sys/arch/macppc/dev/meshreg.h new file mode 100644 index 00000000000..d710675b1f4 --- /dev/null +++ b/sys/arch/macppc/dev/meshreg.h @@ -0,0 +1,111 @@ +/* $OpenBSD: meshreg.h,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: meshreg.h,v 1.1 1999/02/19 13:06:03 tsubai Exp $ */ + +/*- + * Copyright (C) 1999 Internet Research Institute, Inc. + * 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 + * Internet Research Institute, Inc. + * 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. + */ + +/* MESH register offsets */ +#define MESH_XFER_COUNT0 0x00 /* transfer count (low) */ +#define MESH_XFER_COUNT1 0x10 /* transfer count (high) */ +#define MESH_FIFO 0x20 /* FIFO (16byte depth) */ +#define MESH_SEQUENCE 0x30 /* command register */ +#define MESH_BUS_STATUS0 0x40 +#define MESH_BUS_STATUS1 0x50 +#define MESH_FIFO_COUNT 0x60 +#define MESH_EXCEPTION 0x70 +#define MESH_ERROR 0x80 +#define MESH_INTR_MASK 0x90 +#define MESH_INTERRUPT 0xa0 +#define MESH_SOURCE_ID 0xb0 +#define MESH_DEST_ID 0xc0 +#define MESH_SYNC_PARAM 0xd0 +#define MESH_MESH_ID 0xe0 /* MESH version */ +#define MESH_SEL_TIMEOUT 0xf0 /* selection timeout delay */ + +#define MESH_SIGNATURE 0xe2 /* XXX wrong! */ + +/* MESH commands */ +#define MESH_CMD_ARBITRATE 0x01 +#define MESH_CMD_SELECT 0x02 +#define MESH_CMD_COMMAND 0x03 +#define MESH_CMD_STATUS 0x04 +#define MESH_CMD_DATAOUT 0x05 +#define MESH_CMD_DATAIN 0x06 +#define MESH_CMD_MSGOUT 0x07 +#define MESH_CMD_MSGIN 0x08 +#define MESH_CMD_BUSFREE 0x09 +#define MESH_CMD_ENABLE_PARITY 0x0A +#define MESH_CMD_DISABLE_PARITY 0x0B +#define MESH_CMD_ENABLE_RESEL 0x0C +#define MESH_CMD_DISABLE_RESEL 0x0D +#define MESH_CMD_RESET_MESH 0x0E +#define MESH_CMD_FLUSH_FIFO 0x0F +#define MESH_SEQ_DMA 0x80 +#define MESH_SEQ_TARGET 0x40 +#define MESH_SEQ_ATN 0x20 +#define MESH_SEQ_ACTNEG 0x10 + +/* INTERRUPT/INTR_MASK register bits */ +#define MESH_INTR_ERROR 0x04 +#define MESH_INTR_EXCEPTION 0x02 +#define MESH_INTR_CMDDONE 0x01 + +/* EXCEPTION register bits */ +#define MESH_EXC_SELATN 0x20 /* selected and ATN asserted (T) */ +#define MESH_EXC_SELECTED 0x10 /* selected (T) */ +#define MESH_EXC_RESEL 0x08 /* reselected */ +#define MESH_EXC_ARBLOST 0x04 /* arbitration lost */ +#define MESH_EXC_PHASEMM 0x02 /* phase mismatch */ +#define MESH_EXC_SELTO 0x01 /* selection timeout */ + +/* ERROR register bits */ +#define MESH_ERR_DISCONNECT 0x40 /* unexpected disconnect */ +#define MESH_ERR_SCSI_RESET 0x20 /* Rst signal asserted */ +#define MESH_ERR_SEQERR 0x10 /* sequence error */ +#define MESH_ERR_PARITY_ERR3 0x08 /* parity error */ +#define MESH_ERR_PARITY_ERR2 0x04 +#define MESH_ERR_PARITY_ERR1 0x02 +#define MESH_ERR_PARITY_ERR0 0x01 + +/* BUS_STATUS0 status bits */ +#define MESH_STATUS0_REQ32 0x80 +#define MESH_STATUS0_ACK32 0x40 +#define MESH_STATUS0_REQ 0x20 +#define MESH_STATUS0_ACK 0x10 +#define MESH_STATUS0_ATN 0x08 +#define MESH_STATUS0_MSG 0x04 +#define MESH_STATUS0_CD 0x02 +#define MESH_STATUS0_IO 0x01 + +/* BUS_STATUS1 status bits */ +#define MESH_STATUS1_RST 0x80 +#define MESH_STATUS1_BSY 0x40 +#define MESH_STATUS1_SEL 0x20 diff --git a/sys/arch/macppc/dev/openpic.c b/sys/arch/macppc/dev/openpic.c new file mode 100644 index 00000000000..85bf783cf5c --- /dev/null +++ b/sys/arch/macppc/dev/openpic.c @@ -0,0 +1,688 @@ +/* $OpenBSD: openpic.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ + +/*- + * Copyright (c) 1995 Per Fogelstrom + * Copyright (c) 1993, 1994 Charles M. Hannum. + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz and Don Ahn. + * + * 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 the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + * + * @(#)isa.c 7.2 (Berkeley) 5/12/91 + */ + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <vm/vm.h> +#include <vm/vm_kern.h> +#include <uvm/uvm.h> + +#include <machine/autoconf.h> +#include <machine/intr.h> +#include <machine/psl.h> +#include <machine/pio.h> +#include <machine/powerpc.h> +#include <macppc/mac/openpicreg.h> +#include <dev/ofw/openfirm.h> + +#define ICU_LEN 128 +#define LEGAL_IRQ(x) ((x >= 0) && (x < ICU_LEN)) + +static int intrtype[ICU_LEN], intrmask[ICU_LEN], intrlevel[ICU_LEN]; +static struct intrhand *intrhand[ICU_LEN] = { 0 }; +static int hwirq[ICU_LEN], virq[ICU_LEN]; +unsigned int imen /* = 0xffffffff */; /* XXX */ +static int virq_max = 0; + +struct evcnt evirq[ICU_LEN]; + +static int fakeintr __P((void *)); +static char *intr_typename __P((int type)); +static void intr_calculatemasks __P((void)); +static __inline int cntlzw __P((int x)); +static int mapirq __P((int irq)); +int prog_switch __P((void *arg)); +void openpic_enable_irq_mask __P((int irq_mask)); + +extern u_int32_t *heathrow_FCR; + +#define HWIRQ_MAX 27 +#define HWIRQ_MASK 0x0fffffff + + +static __inline u_int openpic_read __P((int)); +static __inline void openpic_write __P((int, u_int)); +void openpic_enable_irq __P((int, int)); +void openpic_disable_irq __P((int)); +void openpic_init __P((void)); +void openpic_set_priority __P((int, int)); +static __inline int openpic_read_irq __P((int)); +static __inline void openpic_eoi __P((int)); + +struct openpic_softc { + struct device sc_dev; +}; + +int openpic_match __P((struct device *parent, void *cf, void *aux)); +void openpic_attach __P((struct device *, struct device *, void *)); +void openpic_do_pending_int __P((void)); +void openpic_collect_preconf_intr __P((void)); +void ext_intr_openpic __P((void)); + +struct cfattach openpic_ca = { + sizeof(struct openpic_softc), + openpic_match, + openpic_attach +}; + +struct cfdriver openpic_cd = { + NULL, "openpic", DV_DULL +}; + +int +openpic_match(parent, cf, aux) + struct device *parent; + void *cf; + void *aux; +{ + char type[40]; + struct confargs *ca = aux; + + bzero (type, sizeof(type)); + + if (strcmp(ca->ca_name, "interrupt-controller") == 0 ) { + OF_getprop(ca->ca_node, "device_type", type, sizeof(type)); + if (strcmp(type, "open-pic") == 0) { + return 1; + } + } + return 0; +} + +u_int8_t *interrupt_reg; +typedef void (void_f) (void); +extern void_f *pending_int_f; + +vaddr_t openpic_base; +void * openpic_intr_establish( void * lcv, int irq, int type, int level, + int (*ih_fun) __P((void *)), void *ih_arg, char *name); +void openpic_intr_disestablish( void *lcp, void *arg); +void openpic_collect_preconf_intr __P((void)); + +void +openpic_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct confargs *ca = aux; + extern intr_establish_t *intr_establish_func; + extern intr_disestablish_t *intr_disestablish_func; + extern intr_establish_t *mac_intr_establish_func; + extern intr_disestablish_t *mac_intr_disestablish_func; + + openpic_base = (vaddr_t) mapiodev (ca->ca_baseaddr + + ca->ca_reg[0], 0x22000); + + printf(": version 0x%x", openpic_read(OPENPIC_VENDOR_ID)); + + openpic_init(); + + pending_int_f = openpic_do_pending_int; + intr_establish_func = openpic_intr_establish; + intr_disestablish_func = openpic_intr_disestablish; + mac_intr_establish_func = openpic_intr_establish; + mac_intr_disestablish_func = openpic_intr_disestablish; + install_extint(ext_intr_openpic); + +#if 1 + openpic_collect_preconf_intr(); +#endif + +#if 1 + mac_intr_establish(parent, 0x37, IST_LEVEL, + IPL_HIGH, prog_switch, (void*)0x37, "prog button"); +#endif + ppc_intr_enable(1); + + printf("\n"); +} + +void +openpic_collect_preconf_intr() +{ + int i; + for (i = 0; i < ppc_configed_intr_cnt; i++) { +#ifdef DEBUG + printf("\n\t%s irq %d level %d fun %x arg %x", + ppc_configed_intr[i].ih_what, ppc_configed_intr[i].ih_irq, + ppc_configed_intr[i].ih_level, ppc_configed_intr[i].ih_fun, + ppc_configed_intr[i].ih_arg); +#endif + openpic_intr_establish(NULL, ppc_configed_intr[i].ih_irq, + IST_LEVEL, ppc_configed_intr[i].ih_level, + ppc_configed_intr[i].ih_fun, ppc_configed_intr[i].ih_arg, + ppc_configed_intr[i].ih_what); + } +} + +int +fakeintr(arg) + void *arg; +{ + + return 0; +} + +void nameinterrupt( int replace, char *newstr); + +/* + * Register an interrupt handler. + */ +void * +openpic_intr_establish(lcv, irq, type, level, ih_fun, ih_arg, name) + void * lcv; + int irq; + int type; + int level; + int (*ih_fun) __P((void *)); + void *ih_arg; + char *name; +{ + struct intrhand **p, *q, *ih; + static struct intrhand fakehand; + + fakehand.ih_next = NULL; + fakehand.ih_fun = fakeintr; + +#if 0 +printf("mac_intr_establish, hI %d L %d ", irq, type); +#endif + + nameinterrupt(irq, name); + irq = mapirq(irq); +#if 0 +printf("vI %d ", irq); +#endif + + /* no point in sleeping unless someone can free memory. */ + ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); + if (ih == NULL) + panic("intr_establish: can't malloc handler info"); + + if (!LEGAL_IRQ(irq) || type == IST_NONE) + panic("intr_establish: bogus irq or type"); + + switch (intrtype[irq]) { + case IST_NONE: + intrtype[irq] = type; + break; + case IST_EDGE: + case IST_LEVEL: + if (type == intrtype[irq]) + break; + case IST_PULSE: + if (type != IST_NONE) + panic("intr_establish: can't share %s with %s", + intr_typename(intrtype[irq]), + intr_typename(type)); + break; + } + + /* + * Figure out where to put the handler. + * This is O(N^2), but we want to preserve the order, and N is + * generally small. + */ + for (p = &intrhand[irq]; (q = *p) != NULL; p = &q->ih_next) + ; + + /* + * Actually install a fake handler momentarily, since we might be doing + * this with interrupts enabled and DON'T WANt the real routine called + * until masking is set up. + */ + fakehand.ih_level = level; + *p = &fakehand; + + intr_calculatemasks(); + + /* + * Poke the real handler in now. + */ + ih->ih_fun = ih_fun; + ih->ih_arg = ih_arg; + ih->ih_count = 0; + ih->ih_next = NULL; + ih->ih_level = level; + ih->ih_irq = irq; + *p = ih; + + return (ih); +} + +/* + * Deregister an interrupt handler. + */ +void +openpic_intr_disestablish(lcp, arg) + void *lcp; + void *arg; +{ + struct intrhand *ih = arg; + int irq = ih->ih_irq; + struct intrhand **p, *q; + + if (!LEGAL_IRQ(irq)) + panic("intr_disestablish: bogus irq"); + + /* + * Remove the handler from the chain. + * This is O(n^2), too. + */ + for (p = &intrhand[irq]; (q = *p) != NULL && q != ih; p = &q->ih_next) + ; + if (q) + *p = q->ih_next; + else + panic("intr_disestablish: handler not registered"); + free((void *)ih, M_DEVBUF); + + intr_calculatemasks(); + + if (intrhand[irq] == NULL) + intrtype[irq] = IST_NONE; +} + + +static char * +intr_typename(type) + int type; +{ + + switch (type) { + case IST_NONE : + return ("none"); + case IST_PULSE: + return ("pulsed"); + case IST_EDGE: + return ("edge-triggered"); + case IST_LEVEL: + return ("level-triggered"); + default: + panic("intr_typename: invalid type %d", type); +#if 1 /* XXX */ + return ("unknown"); +#endif + } +} + +/* + * Recalculate the interrupt masks from scratch. + * We could code special registry and deregistry versions of this function that + * would be faster, but the code would be nastier, and we don't expect this to + * happen very much anyway. + */ +static void +intr_calculatemasks() +{ + int irq, level; + struct intrhand *q; + + /* First, figure out which levels each IRQ uses. */ + for (irq = 0; irq < ICU_LEN; irq++) { + register int levels = 0; + for (q = intrhand[irq]; q; q = q->ih_next) + levels |= 1 << q->ih_level; + intrlevel[irq] = levels; + } + + /* Then figure out which IRQs use each level. */ + for (level = 0; level < 5; level++) { + register int irqs = 0; + for (irq = 0; irq < ICU_LEN; irq++) + if (intrlevel[irq] & (1 << level)) + irqs |= 1 << irq; + imask[level] = irqs | SINT_MASK; + } + + /* + * There are tty, network and disk drivers that use free() at interrupt + * time, so imp > (tty | net | bio). + */ + imask[IPL_IMP] |= imask[IPL_TTY] | imask[IPL_NET] | imask[IPL_BIO]; + + /* + * Enforce a hierarchy that gives slow devices a better chance at not + * dropping data. + */ + imask[IPL_TTY] |= imask[IPL_NET] | imask[IPL_BIO]; + imask[IPL_NET] |= imask[IPL_BIO]; + + /* + * These are pseudo-levels. + */ + imask[IPL_NONE] = 0x00000000; + imask[IPL_HIGH] = 0xffffffff; + + /* And eventually calculate the complete masks. */ + for (irq = 0; irq < ICU_LEN; irq++) { + register int irqs = 1 << irq; + for (q = intrhand[irq]; q; q = q->ih_next) + irqs |= imask[q->ih_level]; + intrmask[irq] = irqs | SINT_MASK; + } + + /* Lastly, determine which IRQs are actually in use. */ + { + register int irqs = 0; + for (irq = 0; irq < ICU_LEN; irq++) { + if (intrhand[irq]) { + irqs |= 1 << irq; + openpic_enable_irq(hwirq[irq], intrtype[irq]); + } else { + openpic_disable_irq(hwirq[irq]); + } + } + imen = ~irqs; + } +} +/* + * Map 64 irqs into 32 (bits). + */ +static int +mapirq(irq) + int irq; +{ + int v; + + if (irq < 0 || irq >= ICU_LEN) + panic("invalid irq"); + virq_max++; + v = virq_max; + if (v > HWIRQ_MAX) + panic("virq overflow"); + + hwirq[v] = irq; + virq[irq] = v; +#if 0 +printf("\nmapirq %x to %x\n", irq, v); +#endif + + return v; +} + +/* + * Count leading zeros. + */ +static __inline int +cntlzw(x) + int x; +{ + int a; + + __asm __volatile ("cntlzw %0,%1" : "=r"(a) : "r"(x)); + + return a; +} + + +void +openpic_do_pending_int() +{ + struct intrhand *ih; + int irq; + int pcpl; + int hwpend; + int emsr, dmsr; + static int processing; + + if (processing) + return; + + processing = 1; + pcpl = splhigh(); /* Turn off all */ + asm volatile("mfmsr %0" : "=r"(emsr)); + dmsr = emsr & ~PSL_EE; + asm volatile("mtmsr %0" :: "r"(dmsr)); + + hwpend = ipending & ~pcpl; /* Do now unmasked pendings */ + imen &= ~hwpend; + openpic_enable_irq_mask(~imen); + hwpend &= HWIRQ_MASK; + while (hwpend) { + irq = 31 - cntlzw(hwpend); + hwpend &= ~(1L << irq); + ih = intrhand[irq]; + while(ih) { + (*ih->ih_fun)(ih->ih_arg); + ih = ih->ih_next; + } + + evirq[hwirq[irq]].ev_count++; + } + + /*out32rb(INT_ENABLE_REG, ~imen);*/ + + do { + if((ipending & SINT_CLOCK) & ~pcpl) { + ipending &= ~SINT_CLOCK; + softclock(); + } + if((ipending & SINT_NET) & ~pcpl) { + extern int netisr; + int pisr = netisr; + netisr = 0; + ipending &= ~SINT_NET; + softnet(pisr); + } + } while (ipending & (SINT_NET|SINT_CLOCK) & ~cpl); + ipending &= pcpl; + cpl = pcpl; /* Don't use splx... we are here already! */ + asm volatile("mtmsr %0" :: "r"(emsr)); + processing = 0; +} + +u_int +openpic_read(reg) + int reg; +{ + char *addr = (void *)(openpic_base + reg); + + return in32rb(addr); +} + +void +openpic_write(reg, val) + int reg; + u_int val; +{ + char *addr = (void *)(openpic_base + reg); + + out32rb(addr, val); +} + +void +openpic_enable_irq_mask(irq_mask) +int irq_mask; +{ + int irq; + for ( irq = 0; irq <= virq_max; irq++) { + if (irq_mask & (1 << irq)) { + openpic_enable_irq(hwirq[irq], intrtype[irq]); + } else { + openpic_disable_irq(hwirq[irq]); + } + } +} +void +openpic_enable_irq(irq, type) + int irq; + int type; +{ + u_int x; + + x = openpic_read(OPENPIC_SRC_VECTOR(irq)); + x &= ~(OPENPIC_IMASK|OPENPIC_SENSE_LEVEL|OPENPIC_SENSE_EDGE); + if (type == IST_LEVEL) { + x |= OPENPIC_SENSE_LEVEL; + } else { + x |= OPENPIC_SENSE_EDGE; + } + openpic_write(OPENPIC_SRC_VECTOR(irq), x); +} + +void +openpic_disable_irq(irq) + int irq; +{ + u_int x; + + x = openpic_read(OPENPIC_SRC_VECTOR(irq)); + x |= OPENPIC_IMASK; + openpic_write(OPENPIC_SRC_VECTOR(irq), x); +} + +void +openpic_set_priority(cpu, pri) + int cpu, pri; +{ + u_int x; + + x = openpic_read(OPENPIC_CPU_PRIORITY(cpu)); + x &= ~OPENPIC_CPU_PRIORITY_MASK; + x |= pri; + openpic_write(OPENPIC_CPU_PRIORITY(cpu), x); +} + +int +openpic_read_irq(cpu) + int cpu; +{ + return openpic_read(OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK; +} + +void +openpic_eoi(cpu) + int cpu; +{ + openpic_write(OPENPIC_EOI(cpu), 0); + openpic_read(OPENPIC_EOI(cpu)); +} + +void +ext_intr_openpic() +{ + int irq, realirq; + int r_imen; + int pcpl; + struct intrhand *ih; + + pcpl = splhigh(); /* Turn off all */ + + realirq = openpic_read_irq(0); + + while (realirq != 255) { + irq = virq[realirq]; + intrcnt[realirq]++; + + /* XXX check range */ + + r_imen = 1 << irq; + + if ((pcpl & r_imen) != 0) { + ipending |= r_imen; /* Masked! Mark this as pending */ + openpic_disable_irq(realirq); + } else { + ih = intrhand[irq]; + while (ih) { + (*ih->ih_fun)(ih->ih_arg); + ih = ih->ih_next; + } + + uvmexp.intrs++; + evirq[realirq].ev_count++; + } + + openpic_eoi(0); + + realirq = openpic_read_irq(0); + } + + splx(pcpl); /* Process pendings. */ +} +void +openpic_init() +{ + int irq; + u_int x; + + /* disable all interrupts */ + for (irq = 0; irq < 255; irq++) + openpic_write(OPENPIC_SRC_VECTOR(irq), OPENPIC_IMASK); + openpic_set_priority(0, 15); + + /* we don't need 8259 pass through mode */ + x = openpic_read(OPENPIC_CONFIG); + x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE; + openpic_write(OPENPIC_CONFIG, x); + + /* send all interrupts to cpu 0 */ + for (irq = 0; irq < ICU_LEN; irq++) + openpic_write(OPENPIC_IDEST(irq), 1 << 0); + for (irq = 0; irq < ICU_LEN; irq++) { + x = irq; + x |= OPENPIC_IMASK; + x |= OPENPIC_POLARITY_POSITIVE; + x |= OPENPIC_SENSE_LEVEL; + x |= 8 << OPENPIC_PRIORITY_SHIFT; + openpic_write(OPENPIC_SRC_VECTOR(irq), x); + } + + /* XXX set spurious intr vector */ + + openpic_set_priority(0, 0); + + /* clear all pending interrunts */ + for (irq = 0; irq < ICU_LEN; irq++) { + openpic_read_irq(0); + openpic_eoi(0); + } + + for (irq = 0; irq < ICU_LEN; irq++) + openpic_disable_irq(irq); + + install_extint(ext_intr_openpic); +} diff --git a/sys/arch/macppc/dev/openpicreg.h b/sys/arch/macppc/dev/openpicreg.h new file mode 100644 index 00000000000..e4c0d715cd3 --- /dev/null +++ b/sys/arch/macppc/dev/openpicreg.h @@ -0,0 +1,90 @@ +/* $OpenBSD: openpicreg.h,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: openpicreg.h,v 1.1 2000/02/14 12:45:53 tsubai Exp $ */ + +/*- + * Copyright (c) 2000 Tsubai Masanari. 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. 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. + */ + +/* + * GLOBAL/TIMER register (IDU base + 0x1000) + */ + +/* feature reporting reg 0 */ +#define OPENPIC_FEATURE 0x1000 + +/* global config reg 0 */ +#define OPENPIC_CONFIG 0x1020 +#define OPENPIC_CONFIG_RESET 0x80000000 +#define OPENPIC_CONFIG_8259_PASSTHRU_DISABLE 0x20000000 + +/* vendor ID */ +#define OPENPIC_VENDOR_ID 0x1080 + +/* processor initialization reg */ +#define OPENPIC_PROC_INIT 0x1090 + +/* IPI vector/priority reg */ +#define OPENPIC_IPI_VECTOR(ipi) (0x10a0 + (ipi) * 0x10) + +/* spurious intr. vector */ +#define OPENPIC_SPURIOUS_VECTOR 0x10e0 + + +/* + * INTERRUPT SOURCE register (IDU base + 0x10000) + */ + +/* interrupt vector/priority reg */ +#define OPENPIC_SRC_VECTOR(irq) (0x10000 + (irq) * 0x20) +#define OPENPIC_SENSE_LEVEL 0x00400000 +#define OPENPIC_SENSE_EDGE 0x00000000 +#define OPENPIC_POLARITY_POSITIVE 0x00800000 +#define OPENPIC_POLARITY_NEGATIVE 0x00000000 +#define OPENPIC_IMASK 0x80000000 +#define OPENPIC_ACTIVITY 0x40000000 +#define OPENPIC_PRIORITY_MASK 0x000f0000 +#define OPENPIC_PRIORITY_SHIFT 16 +#define OPENPIC_VECTOR_MASK 0x000000ff + +/* interrupt destination cpu */ +#define OPENPIC_IDEST(irq) (0x10010 + (irq) * 0x20) + + +/* + * PROCESSOR register (IDU base + 0x20000) + */ + +/* IPI command reg */ +#define OPENPIC_IPI(cpu, ipi) (0x20040 + (cpu) * 0x1000 + (ipi)) + +/* current task priority reg */ +#define OPENPIC_CPU_PRIORITY(cpu) (0x20080 + (cpu) * 0x1000) +#define OPENPIC_CPU_PRIORITY_MASK 0x0000000f + +/* interrupt acknowledge reg */ +#define OPENPIC_IACK(cpu) (0x200a0 + (cpu) * 0x1000) + +/* end of interrupt reg */ +#define OPENPIC_EOI(cpu) (0x200b0 + (cpu) * 0x1000) diff --git a/sys/arch/macppc/dev/pm_direct.c b/sys/arch/macppc/dev/pm_direct.c new file mode 100644 index 00000000000..4ec07563fed --- /dev/null +++ b/sys/arch/macppc/dev/pm_direct.c @@ -0,0 +1,1314 @@ +/* $OpenBSD: pm_direct.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: pm_direct.c,v 1.9 2000/06/08 22:10:46 tsubai Exp $ */ + +/* + * Copyright (C) 1997 Takashi Hamada + * 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 Takashi Hamada + * 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. + */ +/* From: pm_direct.c 1.3 03/18/98 Takashi Hamada */ + +#ifdef DEBUG +#ifndef ADB_DEBUG +#define ADB_DEBUG +#endif +#endif + +/* #define PM_GRAB_SI 1 */ + +#include <sys/param.h> +#include <sys/cdefs.h> +#include <sys/device.h> +#include <sys/systm.h> + +#include <machine/adbsys.h> +#include <machine/cpu.h> + +#include <macppc/mac/adbvar.h> +#include <macppc/mac/pm_direct.h> +#include <macppc/mac/viareg.h> + +extern int adb_polling; /* Are we polling? (Debugger mode) */ + +/* hardware dependent values */ +#define ADBDelay 100 /* XXX */ +#define HwCfgFlags3 0x20000 /* XXX */ + +/* define the types of the Power Manager */ +#define PM_HW_UNKNOWN 0x00 /* don't know */ +#define PM_HW_PB1XX 0x01 /* PowerBook 1XX series */ +#define PM_HW_PB5XX 0x02 /* PowerBook Duo and 5XX series */ + +/* useful macros */ +#define PM_SR() read_via_reg(VIA1, vSR) +#define PM_VIA_INTR_ENABLE() write_via_reg(VIA1, vIER, 0x90) +#define PM_VIA_INTR_DISABLE() write_via_reg(VIA1, vIER, 0x10) +#define PM_VIA_CLR_INTR() write_via_reg(VIA1, vIFR, 0x90) +#if 0 +#define PM_SET_STATE_ACKON() via_reg_or(VIA2, vBufB, 0x04) +#define PM_SET_STATE_ACKOFF() via_reg_and(VIA2, vBufB, ~0x04) +#define PM_IS_ON (0x02 == (read_via_reg(VIA2, vBufB) & 0x02)) +#define PM_IS_OFF (0x00 == (read_via_reg(VIA2, vBufB) & 0x02)) +#else +#define PM_SET_STATE_ACKON() via_reg_or(VIA2, vBufB, 0x10) +#define PM_SET_STATE_ACKOFF() via_reg_and(VIA2, vBufB, ~0x10) +#define PM_IS_ON (0x08 == (read_via_reg(VIA2, vBufB) & 0x08)) +#define PM_IS_OFF (0x00 == (read_via_reg(VIA2, vBufB) & 0x08)) +#endif + +/* + * Variables for internal use + */ +int pmHardware = PM_HW_UNKNOWN; +u_short pm_existent_ADB_devices = 0x0; /* each bit expresses the existent ADB device */ +u_int pm_LCD_brightness = 0x0; +u_int pm_LCD_contrast = 0x0; +u_int pm_counter = 0; /* clock count */ + +/* these values shows that number of data returned after 'send' cmd is sent */ +signed char pm_send_cmd_type[] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 0x01, 0x01, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, -1, -1, -1, -1, -1, 0x00, + -1, 0x00, 0x02, 0x01, 0x01, -1, -1, -1, + 0x00, -1, -1, -1, -1, -1, -1, -1, + 0x04, 0x14, -1, 0x03, -1, -1, -1, -1, + 0x00, 0x00, 0x02, 0x02, -1, -1, -1, -1, + 0x01, 0x01, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, -1, -1, 0x01, -1, -1, -1, + 0x01, 0x00, 0x02, 0x02, -1, 0x01, 0x03, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x00, -1, -1, -1, + 0x02, -1, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, -1, + 0x01, 0x01, 0x01, -1, -1, -1, -1, -1, + 0x00, 0x00, -1, -1, -1, -1, 0x04, 0x04, + 0x04, -1, 0x00, -1, -1, -1, -1, -1, + 0x00, -1, -1, -1, -1, -1, -1, -1, + 0x01, 0x02, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, -1, -1, -1, -1, -1, -1, + 0x02, 0x02, 0x02, 0x04, -1, 0x00, -1, -1, + 0x01, 0x01, 0x03, 0x02, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 0x00, -1, -1, -1, -1, -1, -1, -1, + 0x01, 0x01, -1, -1, 0x00, 0x00, -1, -1, + -1, 0x04, 0x00, -1, -1, -1, -1, -1, + 0x03, -1, 0x00, -1, 0x00, -1, -1, 0x00, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +/* these values shows that number of data returned after 'receive' cmd is sent */ +signed char pm_receive_cmd_type[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + -1, -1, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, -1, -1, -1, -1, -1, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + -1, -1, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x15, -1, 0x02, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x03, 0x03, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x04, 0x03, 0x09, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + -1, -1, -1, -1, -1, -1, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, -1, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + -1, -1, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + -1, -1, -1, -1, -1, -1, -1, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, -1, -1, 0x02, -1, -1, -1, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + -1, -1, 0x02, -1, -1, -1, -1, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + + +/* + * Define the private functions + */ + +/* for debugging */ +#ifdef ADB_DEBUG +void pm_printerr __P((char *, int, int, char *)); +#endif + +int pm_wait_busy __P((int)); +int pm_wait_free __P((int)); + +/* these functions are for the PB1XX series */ +int pm_receive_pm1 __P((u_char *)); +int pm_send_pm1 __P((u_char,int)); +int pm_pmgrop_pm1 __P((PMData *)); +void pm_intr_pm1 __P((void)); + +/* these functions are for the PB Duo series and the PB 5XX series */ +int pm_receive_pm2 __P((u_char *)); +int pm_send_pm2 __P((u_char)); +int pm_pmgrop_pm2 __P((PMData *)); +void pm_intr_pm2 __P((void)); + +/* this function is MRG-Based (for testing) */ +int pm_pmgrop_mrg __P((PMData *)); + +/* these functions are called from adb_direct.c */ +void pm_setup_adb __P((void)); +void pm_check_adb_devices __P((int)); +void pm_intr __P((void)); +int pm_adb_op __P((u_char *, void *, void *, int)); + +/* these functions also use the variables of adb_direct.c */ +void pm_adb_get_TALK_result __P((PMData *)); +void pm_adb_get_ADB_data __P((PMData *)); +void pm_adb_poll_next_device_pm1 __P((PMData *)); + + +/* + * These variables are in adb_direct.c. + */ +extern u_char *adbBuffer; /* pointer to user data area */ +extern void *adbCompRout; /* pointer to the completion routine */ +extern void *adbCompData; /* pointer to the completion routine data */ +extern int adbWaiting; /* waiting for return data from the device */ +extern int adbWaitingCmd; /* ADB command we are waiting for */ +extern int adbStarting; /* doing ADB reinit, so do "polling" differently */ + +#define ADB_MAX_MSG_LENGTH 16 +#define ADB_MAX_HDR_LENGTH 8 +struct adbCommand { + u_char header[ADB_MAX_HDR_LENGTH]; /* not used yet */ + u_char data[ADB_MAX_MSG_LENGTH]; /* packet data only */ + u_char *saveBuf; /* where to save result */ + u_char *compRout; /* completion routine pointer */ + u_char *compData; /* completion routine data pointer */ + u_int cmd; /* the original command for this data */ + u_int unsol; /* 1 if packet was unsolicited */ + u_int ack_only; /* 1 for no special processing */ +}; +extern void adb_pass_up __P((struct adbCommand *)); + +#if 0 +/* + * Define the external functions + */ +extern int zshard __P((int)); /* from zs.c */ +#endif + +#ifdef ADB_DEBUG +/* + * This function dumps contents of the PMData + */ +void +pm_printerr(ttl, rval, num, data) + char *ttl; + int rval; + int num; + char *data; +{ + int i; + + printf("pm: %s:%04x %02x ", ttl, rval, num); + for (i = 0; i < num; i++) + printf("%02x ", data[i]); + printf("\n"); +} +#endif + + + +/* + * Check the hardware type of the Power Manager + */ +void +pm_setup_adb() +{ + pmHardware = PM_HW_PB5XX; /* XXX */ +} + + +/* + * Check the existent ADB devices + */ +void +pm_check_adb_devices(id) + int id; +{ + u_short ed = 0x1; + + ed <<= id; + pm_existent_ADB_devices |= ed; +} + + +/* + * Wait until PM IC is busy + */ +int +pm_wait_busy(delay) + int delay; +{ + while (PM_IS_ON) { +#ifdef PM_GRAB_SI +#if 0 + zshard(0); /* grab any serial interrupts */ +#else + (void)intr_dispatch(0x70); +#endif +#endif + if ((--delay) < 0) + return 1; /* timeout */ + } + return 0; +} + + +/* + * Wait until PM IC is free + */ +int +pm_wait_free(delay) + int delay; +{ + while (PM_IS_OFF) { +#ifdef PM_GRAB_SI +#if 0 + zshard(0); /* grab any serial interrupts */ +#else + (void)intr_dispatch(0x70); +#endif +#endif + if ((--delay) < 0) + return 0; /* timeout */ + } + return 1; +} + + + +/* + * Functions for the PB1XX series + */ + +/* + * Receive data from PM for the PB1XX series + */ +int +pm_receive_pm1(data) + u_char *data; +{ +#if 0 + int rval = 0xffffcd34; + + via_reg(VIA2, vDirA) = 0x00; + + switch (1) { + default: + if (pm_wait_busy(0x40) != 0) + break; /* timeout */ + + PM_SET_STATE_ACKOFF(); + *data = via_reg(VIA2, 0x200); + + rval = 0xffffcd33; + if (pm_wait_free(0x40) == 0) + break; /* timeout */ + + rval = 0x00; + break; + } + + PM_SET_STATE_ACKON(); + via_reg(VIA2, vDirA) = 0x00; + + return rval; +#else + panic("pm_receive_pm1"); +#endif +} + + + +/* + * Send data to PM for the PB1XX series + */ +int +pm_send_pm1(data, delay) + u_char data; + int delay; +{ +#if 0 + int rval; + + via_reg(VIA2, vDirA) = 0xff; + via_reg(VIA2, 0x200) = data; + + PM_SET_STATE_ACKOFF(); + if (pm_wait_busy(0x400) != 0) { + PM_SET_STATE_ACKON(); + via_reg(VIA2, vDirA) = 0x00; + + return 0xffffcd36; + } + + rval = 0x0; + PM_SET_STATE_ACKON(); + if (pm_wait_free(0x40) == 0) + rval = 0xffffcd35; + + PM_SET_STATE_ACKON(); + via_reg(VIA2, vDirA) = 0x00; + + return rval; +#else + panic("pm_send_pm1"); +#endif +} + + +/* + * My PMgrOp routine for the PB1XX series + */ +int +pm_pmgrop_pm1(pmdata) + PMData *pmdata; +{ +#if 0 + int i; + int s = 0x81815963; + u_char via1_vIER, via1_vDirA; + int rval = 0; + int num_pm_data = 0; + u_char pm_cmd; + u_char pm_data; + u_char *pm_buf; + + /* disable all inetrrupts but PM */ + via1_vIER = via_reg(VIA1, vIER); + PM_VIA_INTR_DISABLE(); + + via1_vDirA = via_reg(VIA1, vDirA); + + switch (pmdata->command) { + default: + for (i = 0; i < 7; i++) { + via_reg(VIA2, vDirA) = 0x00; + + /* wait until PM is free */ + if (pm_wait_free(ADBDelay) == 0) { /* timeout */ + via_reg(VIA2, vDirA) = 0x00; + /* restore formar value */ + via_reg(VIA1, vDirA) = via1_vDirA; + via_reg(VIA1, vIER) = via1_vIER; + return 0xffffcd38; + } + + switch (mac68k_machine.machineid) { + case MACH_MACPB160: + case MACH_MACPB165: + case MACH_MACPB165C: + case MACH_MACPB180: + case MACH_MACPB180C: + { + int delay = ADBDelay * 16; + + via_reg(VIA2, vDirA) = 0x00; + while ((via_reg(VIA2, 0x200) == 0x7f) && (delay >= 0)) + delay--; + + if (delay < 0) { /* timeout */ + via_reg(VIA2, vDirA) = 0x00; + /* restore formar value */ + via_reg(VIA1, vIER) = via1_vIER; + return 0xffffcd38; + } + } + } /* end switch */ + + s = splhigh(); + + via1_vDirA = via_reg(VIA1, vDirA); + via_reg(VIA1, vDirA) &= 0x7f; + + pm_cmd = (u_char)(pmdata->command & 0xff); + if ((rval = pm_send_pm1(pm_cmd, ADBDelay * 8)) == 0) + break; /* send command succeeded */ + + via_reg(VIA1, vDirA) = via1_vDirA; + splx(s); + } /* end for */ + + /* failed to send a command */ + if (i == 7) { + via_reg(VIA2, vDirA) = 0x00; + /* restore formar value */ + via_reg(VIA1, vDirA) = via1_vDirA; + via_reg(VIA1, vIER) = via1_vIER; + if (s != 0x81815963) + splx(s); + return 0xffffcd38; + } + + /* send # of PM data */ + num_pm_data = pmdata->num_data; + if ((rval = pm_send_pm1((u_char)(num_pm_data & 0xff), ADBDelay * 8)) != 0) + break; /* timeout */ + + /* send PM data */ + pm_buf = (u_char *)pmdata->s_buf; + for (i = 0; i < num_pm_data; i++) + if ((rval = pm_send_pm1(pm_buf[i], ADBDelay * 8)) != 0) + break; /* timeout */ + if ((i != num_pm_data) && (num_pm_data != 0)) + break; /* timeout */ + + /* Will PM IC return data? */ + if ((pm_cmd & 0x08) == 0) { + rval = 0; + break; /* no returned data */ + } + + rval = 0xffffcd37; + if (pm_wait_busy(ADBDelay) != 0) + break; /* timeout */ + + /* receive PM command */ + if ((rval = pm_receive_pm1(&pm_data)) != 0) + break; + + pmdata->command = pm_data; + + /* receive number of PM data */ + if ((rval = pm_receive_pm1(&pm_data)) != 0) + break; /* timeout */ + num_pm_data = pm_data; + pmdata->num_data = num_pm_data; + + /* receive PM data */ + pm_buf = (u_char *)pmdata->r_buf; + for (i = 0; i < num_pm_data; i++) { + if ((rval = pm_receive_pm1(&pm_data)) != 0) + break; /* timeout */ + pm_buf[i] = pm_data; + } + + rval = 0; + } + + via_reg(VIA2, vDirA) = 0x00; + + /* restore formar value */ + via_reg(VIA1, vDirA) = via1_vDirA; + via_reg(VIA1, vIER) = via1_vIER; + if (s != 0x81815963) + splx(s); + + return rval; +#else + panic("pm_pmgrop_pm1"); +#endif +} + + +/* + * My PM interrupt routine for PB1XX series + */ +void +pm_intr_pm1() +{ +#if 0 + int s; + int rval; + PMData pmdata; + + s = splhigh(); + + PM_VIA_CLR_INTR(); /* clear VIA1 interrupt */ + + /* ask PM what happend */ + pmdata.command = 0x78; + pmdata.num_data = 0; + pmdata.data[0] = pmdata.data[1] = 0; + pmdata.s_buf = &pmdata.data[2]; + pmdata.r_buf = &pmdata.data[2]; + rval = pm_pmgrop_pm1(&pmdata); + if (rval != 0) { +#ifdef ADB_DEBUG + if (adb_debug) + printf("pm: PM is not ready. error code=%08x\n", rval); +#endif + splx(s); + return; + } + + if ((pmdata.data[2] & 0x10) == 0x10) { + if ((pmdata.data[2] & 0x0f) == 0) { + /* ADB data that were requested by TALK command */ + pm_adb_get_TALK_result(&pmdata); + } else if ((pmdata.data[2] & 0x08) == 0x8) { + /* PM is requesting to poll */ + pm_adb_poll_next_device_pm1(&pmdata); + } else if ((pmdata.data[2] & 0x04) == 0x4) { + /* ADB device event */ + pm_adb_get_ADB_data(&pmdata); + } + } else { +#ifdef ADB_DEBUG + if (adb_debug) + pm_printerr("driver does not supported this event.", + rval, pmdata.num_data, pmdata.data); +#endif + } + + splx(s); +#else + panic("pm_intr_pm1"); +#endif +} + + + +/* + * Functions for the PB Duo series and the PB 5XX series + */ + +/* + * Receive data from PM for the PB Duo series and the PB 5XX series + */ +int +pm_receive_pm2(data) + u_char *data; +{ + int i; + int rval; + + rval = 0xffffcd34; + + switch (1) { + default: + /* set VIA SR to input mode */ + via_reg_or(VIA1, vACR, 0x0c); + via_reg_and(VIA1, vACR, ~0x10); + i = PM_SR(); + + PM_SET_STATE_ACKOFF(); + if (pm_wait_busy((int)ADBDelay*32) != 0) + break; /* timeout */ + + PM_SET_STATE_ACKON(); + rval = 0xffffcd33; + if (pm_wait_free((int)ADBDelay*32) == 0) + break; /* timeout */ + + *data = PM_SR(); + rval = 0; + + break; + } + + PM_SET_STATE_ACKON(); + via_reg_or(VIA1, vACR, 0x1c); + + return rval; +} + + + +/* + * Send data to PM for the PB Duo series and the PB 5XX series + */ +int +pm_send_pm2(data) + u_char data; +{ + int rval; + + via_reg_or(VIA1, vACR, 0x1c); + write_via_reg(VIA1, vSR, data); /* PM_SR() = data; */ + + PM_SET_STATE_ACKOFF(); + rval = 0xffffcd36; + if (pm_wait_busy((int)ADBDelay*32) != 0) { + PM_SET_STATE_ACKON(); + + via_reg_or(VIA1, vACR, 0x1c); + + return rval; + } + + PM_SET_STATE_ACKON(); + rval = 0xffffcd35; + if (pm_wait_free((int)ADBDelay*32) != 0) + rval = 0; + + PM_SET_STATE_ACKON(); + via_reg_or(VIA1, vACR, 0x1c); + + return rval; +} + + + +/* + * My PMgrOp routine for the PB Duo series and the PB 5XX series + */ +int +pm_pmgrop_pm2(pmdata) + PMData *pmdata; +{ + int i; + int s; + u_char via1_vIER; + int rval = 0; + int num_pm_data = 0; + u_char pm_cmd; + short pm_num_rx_data; + u_char pm_data; + u_char *pm_buf; + + s = splhigh(); + + /* disable all inetrrupts but PM */ + via1_vIER = 0x10; + via1_vIER &= read_via_reg(VIA1, vIER); + write_via_reg(VIA1, vIER, via1_vIER); + if (via1_vIER != 0x0) + via1_vIER |= 0x80; + + switch (pmdata->command) { + default: + /* wait until PM is free */ + pm_cmd = (u_char)(pmdata->command & 0xff); + rval = 0xcd38; + if (pm_wait_free(ADBDelay * 4) == 0) + break; /* timeout */ + + if (HwCfgFlags3 & 0x00200000) { + /* PB 160, PB 165(c), PB 180(c)? */ + int delay = ADBDelay * 16; + + write_via_reg(VIA2, vDirA, 0x00); + while ((read_via_reg(VIA2, 0x200) == 0x07) && + (delay >= 0)) + delay--; + + if (delay < 0) { + rval = 0xffffcd38; + break; /* timeout */ + } + } + + /* send PM command */ + if ((rval = pm_send_pm2((u_char)(pm_cmd & 0xff)))) + break; /* timeout */ + + /* send number of PM data */ + num_pm_data = pmdata->num_data; + if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */ + if (pm_send_cmd_type[pm_cmd] < 0) { + if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0) + break; /* timeout */ + pmdata->command = 0; + } + } else { /* PB 1XX series ? */ + if ((rval = pm_send_pm2((u_char)(num_pm_data & 0xff))) != 0) + break; /* timeout */ + } + /* send PM data */ + pm_buf = (u_char *)pmdata->s_buf; + for (i = 0 ; i < num_pm_data; i++) + if ((rval = pm_send_pm2(pm_buf[i])) != 0) + break; /* timeout */ + if (i != num_pm_data) + break; /* timeout */ + + + /* check if PM will send me data */ + pm_num_rx_data = pm_receive_cmd_type[pm_cmd]; + pmdata->num_data = pm_num_rx_data; + if (pm_num_rx_data == 0) { + rval = 0; + break; /* no return data */ + } + + /* receive PM command */ + pm_data = pmdata->command; + if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */ + pm_num_rx_data--; + if (pm_num_rx_data == 0) + if ((rval = pm_receive_pm2(&pm_data)) != 0) { + rval = 0xffffcd37; + break; + } + pmdata->command = pm_data; + } else { /* PB 1XX series ? */ + if ((rval = pm_receive_pm2(&pm_data)) != 0) { + rval = 0xffffcd37; + break; + } + pmdata->command = pm_data; + } + + /* receive number of PM data */ + if (HwCfgFlags3 & 0x00020000) { /* PB Duo, PB 5XX */ + if (pm_num_rx_data < 0) { + if ((rval = pm_receive_pm2(&pm_data)) != 0) + break; /* timeout */ + num_pm_data = pm_data; + } else + num_pm_data = pm_num_rx_data; + pmdata->num_data = num_pm_data; + } else { /* PB 1XX serias ? */ + if ((rval = pm_receive_pm2(&pm_data)) != 0) + break; /* timeout */ + num_pm_data = pm_data; + pmdata->num_data = num_pm_data; + } + + /* receive PM data */ + pm_buf = (u_char *)pmdata->r_buf; + for (i = 0; i < num_pm_data; i++) { + if ((rval = pm_receive_pm2(&pm_data)) != 0) + break; /* timeout */ + pm_buf[i] = pm_data; + } + + rval = 0; + } + + /* restore former value */ + write_via_reg(VIA1, vIER, via1_vIER); + splx(s); + + return rval; +} + + +/* + * My PM interrupt routine for the PB Duo series and the PB 5XX series + */ +void +pm_intr_pm2() +{ + int s; + int rval; + PMData pmdata; + + s = splhigh(); + + PM_VIA_CLR_INTR(); /* clear VIA1 interrupt */ + /* ask PM what happend */ + pmdata.command = 0x78; + pmdata.num_data = 0; + pmdata.s_buf = &pmdata.data[2]; + pmdata.r_buf = &pmdata.data[2]; + rval = pm_pmgrop_pm2(&pmdata); + if (rval != 0) { +#ifdef ADB_DEBUG + if (adb_debug) + printf("pm: PM is not ready. error code: %08x\n", rval); +#endif + splx(s); + return; + } + + switch ((u_int)(pmdata.data[2] & 0xff)) { + case 0x00: /* 1 sec interrupt? */ + break; + case 0x80: /* 1 sec interrupt? */ + pm_counter++; + break; + case 0x08: /* Brightness/Contrast button on LCD panel */ + /* get brightness and contrast of the LCD */ + pm_LCD_brightness = (u_int)pmdata.data[3] & 0xff; + pm_LCD_contrast = (u_int)pmdata.data[4] & 0xff; +/* + pm_printerr("#08", rval, pmdata.num_data, pmdata.data); + pmdata.command = 0x33; + pmdata.num_data = 1; + pmdata.s_buf = pmdata.data; + pmdata.r_buf = pmdata.data; + pmdata.data[0] = pm_LCD_contrast; + rval = pm_pmgrop_pm2(&pmdata); + pm_printerr("#33", rval, pmdata.num_data, pmdata.data); +*/ + /* this is an experimental code */ + pmdata.command = 0x41; + pmdata.num_data = 1; + pmdata.s_buf = pmdata.data; + pmdata.r_buf = pmdata.data; + pm_LCD_brightness = 0x7f - pm_LCD_brightness / 2; + if (pm_LCD_brightness < 0x08) + pm_LCD_brightness = 0x08; + if (pm_LCD_brightness > 0x78) + pm_LCD_brightness = 0x78; + pmdata.data[0] = pm_LCD_brightness; + rval = pm_pmgrop_pm2(&pmdata); + break; + case 0x10: /* ADB data that were requested by TALK command */ + case 0x14: + pm_adb_get_TALK_result(&pmdata); + break; + case 0x16: /* ADB device event */ + case 0x18: + case 0x1e: + case PMU_INT_WAKEUP: + pm_adb_get_ADB_data(&pmdata); + break; + default: +#ifdef ADB_DEBUG + if (adb_debug) + pm_printerr("driver does not supported this event.", + pmdata.data[2], pmdata.num_data, + pmdata.data); +#endif + break; + } + + splx(s); +} + + +#if 0 +/* + * MRG-based PMgrOp routine + */ +int +pm_pmgrop_mrg(pmdata) + PMData *pmdata; +{ + u_int32_t rval=0; + + asm(" + movl %1, a0 + .word 0xa085 + movl d0, %0" + : "=g" (rval) + : "g" (pmdata) + : "a0", "d0" ); + + return rval; +} +#endif + + +/* + * My PMgrOp routine + */ +int +pmgrop(pmdata) + PMData *pmdata; +{ + switch (pmHardware) { + case PM_HW_PB1XX: + return (pm_pmgrop_pm1(pmdata)); + break; + case PM_HW_PB5XX: + return (pm_pmgrop_pm2(pmdata)); + break; + default: + /* return (pmgrop_mrg(pmdata)); */ + return 1; + } +} + + +/* + * My PM interrupt routine + */ +void +pm_intr() +{ + switch (pmHardware) { + case PM_HW_PB1XX: + pm_intr_pm1(); + break; + case PM_HW_PB5XX: + pm_intr_pm2(); + break; + default: + break; + } +} + + + +/* + * Synchronous ADBOp routine for the Power Manager + */ +int +pm_adb_op(buffer, compRout, data, command) + u_char *buffer; + void *compRout; + void *data; + int command; +{ + int i; + int s; + int rval; + int ndelay; + PMData pmdata; + struct adbCommand packet; + + if (adbWaiting == 1) + return 1; + + s = splhigh(); + write_via_reg(VIA1, vIER, 0x10); + + adbBuffer = buffer; + adbCompRout = compRout; + adbCompData = data; + + pmdata.command = 0x20; + pmdata.s_buf = pmdata.data; + pmdata.r_buf = pmdata.data; + + /* if the command is LISTEN, add number of ADB data to number of PM data */ + if ((command & 0xc) == 0x8) { + if (buffer != (u_char *)0) + pmdata.num_data = buffer[0] + 3; + } else { + pmdata.num_data = 3; + } + + pmdata.data[0] = (u_char)(command & 0xff); + pmdata.data[1] = 0; + if ((command & 0xc) == 0x8) { /* if the command is LISTEN, copy ADB data to PM buffer */ + if ((buffer != (u_char *)0) && (buffer[0] <= 24)) { + pmdata.data[2] = buffer[0]; /* number of data */ + for (i = 0; i < buffer[0]; i++) + pmdata.data[3 + i] = buffer[1 + i]; + } else + pmdata.data[2] = 0; + } else + pmdata.data[2] = 0; + + if ((command & 0xc) != 0xc) { /* if the command is not TALK */ + /* set up stuff for adb_pass_up */ + packet.data[0] = 1 + pmdata.data[2]; + packet.data[1] = command; + for (i = 0; i < pmdata.data[2]; i++) + packet.data[i+2] = pmdata.data[i+3]; + packet.saveBuf = adbBuffer; + packet.compRout = adbCompRout; + packet.compData = adbCompData; + packet.cmd = command; + packet.unsol = 0; + packet.ack_only = 1; + adb_polling = 1; + adb_pass_up(&packet); + adb_polling = 0; + } + + rval = pmgrop(&pmdata); + if (rval != 0) { + splx(s); + return 1; + } + + delay(10000); + + adbWaiting = 1; + adbWaitingCmd = command; + + PM_VIA_INTR_ENABLE(); + + /* wait until the PM interrupt is occured */ + ndelay = 0x80000; + while (adbWaiting == 1) { + if (read_via_reg(VIA1, vIFR) & 0x14) + pm_intr(); +#ifdef PM_GRAB_SI +#if 0 + zshard(0); /* grab any serial interrupts */ +#else + (void)intr_dispatch(0x70); +#endif +#endif + if ((--ndelay) < 0) { + splx(s); + return 1; + } + } + + /* this command enables the interrupt by operating ADB devices */ + if (HwCfgFlags3 & 0x00020000) { /* PB Duo series, PB 5XX series */ + pmdata.command = 0x20; + pmdata.num_data = 4; + pmdata.s_buf = pmdata.data; + pmdata.r_buf = pmdata.data; + pmdata.data[0] = 0x00; + pmdata.data[1] = 0x86; /* magic spell for awaking the PM */ + pmdata.data[2] = 0x00; + pmdata.data[3] = 0x0c; /* each bit may express the existent ADB device */ + } else { /* PB 1XX series */ + pmdata.command = 0x20; + pmdata.num_data = 3; + pmdata.s_buf = pmdata.data; + pmdata.r_buf = pmdata.data; + pmdata.data[0] = (u_char)(command & 0xf0) | 0xc; + pmdata.data[1] = 0x04; + pmdata.data[2] = 0x00; + } + rval = pmgrop(&pmdata); + + splx(s); + return rval; +} + + +void +pm_adb_get_TALK_result(pmdata) + PMData *pmdata; +{ + int i; + struct adbCommand packet; + + /* set up data for adb_pass_up */ + packet.data[0] = pmdata->num_data-1; + packet.data[1] = pmdata->data[3]; + for (i = 0; i <packet.data[0]-1; i++) + packet.data[i+2] = pmdata->data[i+4]; + + packet.saveBuf = adbBuffer; + packet.compRout = adbCompRout; + packet.compData = adbCompData; + packet.unsol = 0; + packet.ack_only = 0; + adb_polling = 1; + adb_pass_up(&packet); + adb_polling = 0; + + adbWaiting = 0; + adbBuffer = (long)0; + adbCompRout = (long)0; + adbCompData = (long)0; +} + + +void +pm_adb_get_ADB_data(pmdata) + PMData *pmdata; +{ + int i; + struct adbCommand packet; + + /* set up data for adb_pass_up */ + packet.data[0] = pmdata->num_data-1; /* number of raw data */ + packet.data[1] = pmdata->data[3]; /* ADB command */ + for (i = 0; i <packet.data[0]-1; i++) + packet.data[i+2] = pmdata->data[i+4]; + packet.unsol = 1; + packet.ack_only = 0; + adb_pass_up(&packet); +} + + +void +pm_adb_poll_next_device_pm1(pmdata) + PMData *pmdata; +{ + int i; + int ndid; + u_short bendid = 0x1; + int rval; + PMData tmp_pmdata; + + /* find another existent ADB device to poll */ + for (i = 1; i < 16; i++) { + ndid = (ADB_CMDADDR(pmdata->data[3]) + i) & 0xf; + bendid <<= ndid; + if ((pm_existent_ADB_devices & bendid) != 0) + break; + } + + /* poll the other device */ + tmp_pmdata.command = 0x20; + tmp_pmdata.num_data = 3; + tmp_pmdata.s_buf = tmp_pmdata.data; + tmp_pmdata.r_buf = tmp_pmdata.data; + tmp_pmdata.data[0] = (u_char)(ndid << 4) | 0xc; + tmp_pmdata.data[1] = 0x04; /* magic spell for awaking the PM */ + tmp_pmdata.data[2] = 0x00; + rval = pmgrop(&tmp_pmdata); +} + +void +pm_adb_restart() +{ + PMData p; + + p.command = PMU_RESET_CPU; + p.num_data = 0; + p.s_buf = p.data; + p.r_buf = p.data; + pmgrop(&p); +} + +void +pm_adb_poweroff() +{ + PMData p; + + p.command = PMU_POWER_OFF; + p.num_data = 4; + p.s_buf = p.data; + p.r_buf = p.data; + strcpy(p.data, "MATT"); + pmgrop(&p); +} + +void +pm_read_date_time(time) + u_long *time; +{ + PMData p; + + p.command = PMU_READ_RTC; + p.num_data = 0; + p.s_buf = p.data; + p.r_buf = p.data; + pmgrop(&p); + + bcopy(p.data, time, 4); +} + +void +pm_set_date_time(time) + u_long time; +{ + PMData p; + + p.command = PMU_SET_RTC; + p.num_data = 4; + p.s_buf = p.r_buf = p.data; + bcopy(&time, p.data, 4); + pmgrop(&p); +} + +int +pm_read_brightness() +{ + PMData p; + + p.command = PMU_READ_BRIGHTNESS; + p.num_data = 1; /* XXX why 1? */ + p.s_buf = p.r_buf = p.data; + p.data[0] = 0; + pmgrop(&p); + + return p.data[0]; +} + +void +pm_set_brightness(val) + int val; +{ + PMData p; + + val = 0x7f - val / 2; + if (val < 0x08) + val = 0x08; + if (val > 0x78) + val = 0x78; + + p.command = PMU_SET_BRIGHTNESS; + p.num_data = 1; + p.s_buf = p.r_buf = p.data; + p.data[0] = val; + pmgrop(&p); +} + +void +pm_init_brightness() +{ + int val; + + val = pm_read_brightness(); + pm_set_brightness(val); +} + +void +pm_eject_pcmcia(slot) + int slot; +{ + PMData p; + + if (slot != 0 && slot != 1) + return; + + p.command = PMU_EJECT_PCMCIA; + p.num_data = 1; + p.s_buf = p.r_buf = p.data; + p.data[0] = 5 + slot; /* XXX */ + pmgrop(&p); +} + +int +pm_read_nvram(addr) + int addr; +{ + PMData p; + + p.command = PMU_READ_NVRAM; + p.num_data = 2; + p.s_buf = p.r_buf = p.data; + p.data[0] = addr >> 8; + p.data[1] = addr; + pmgrop(&p); + + return p.data[0]; +} + +void +pm_write_nvram(addr, val) + int addr, val; +{ + PMData p; + + p.command = PMU_WRITE_NVRAM; + p.num_data = 3; + p.s_buf = p.r_buf = p.data; + p.data[0] = addr >> 8; + p.data[1] = addr; + p.data[2] = val; + pmgrop(&p); +} diff --git a/sys/arch/macppc/dev/pm_direct.h b/sys/arch/macppc/dev/pm_direct.h new file mode 100644 index 00000000000..f84d7f66b11 --- /dev/null +++ b/sys/arch/macppc/dev/pm_direct.h @@ -0,0 +1,111 @@ +/* $OpenBSD: pm_direct.h,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: pm_direct.h,v 1.5 1999/07/12 15:54:55 tsubai Exp $ */ + +/* + * Copyright (C) 1997 Takashi Hamada + * 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 Takashi Hamada. + * 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. + */ +/* From: pm_direct.h 1.0 01/02/97 Takashi Hamada */ + +/* + * Public declarations that other routines may need. + */ + +/* data structure of the command of the Power Manager */ +typedef struct { + short command; /* command of the Power Manager */ + short num_data; /* number of data */ + char *s_buf; /* pointer to buffer for sending */ + char *r_buf; /* pointer to buffer for receiving */ + char data[32]; /* data buffer (is it too much?) */ +} PMData; + +int pmgrop __P((PMData *)); +void pm_adb_restart __P((void)); +void pm_adb_poweroff __P((void)); +void pm_read_date_time __P((u_long *)); +void pm_set_date_time __P((u_long)); +int pm_read_nvram __P((int)); +void pm_write_nvram __P((int, int)); +int pm_read_brightness __P((void)); +void pm_set_brightness __P((int)); +void pm_init_brightness __P((void)); +void pm_eject_pcmcia __P((int)); + +/* PMU commands */ +#define PMU_POWER_OFF 0x7e /* Turn Power off */ +#define PMU_RESET_CPU 0xd0 /* Reset CPU */ + +#define PMU_SET_RTC 0x30 /* Set realtime clock */ +#define PMU_READ_RTC 0x38 /* Read realtime clock */ + +#define PMU_WRITE_PRAM 0x32 /* Write PRAM */ +#define PMU_READ_PRAM 0x3a /* Read PRAM */ + +#define PMU_WRITE_NVRAM 0x33 /* Write NVRAM */ +#define PMU_READ_NVRAM 0x3b /* Read NVRAM */ + +#define PMU_EJECT_PCMCIA 0x4c /* Eject PCMCIA slot */ + +#define PMU_SET_BRIGHTNESS 0x41 /* Set backlight brightness */ +#define PMU_READ_BRIGHTNESS 0xd9 /* Read brightness button position */ + +#define PMU_POWER_EVENTS 0x8f /* Send power-event commands to PMU */ +#define PMU_SYSTEM_READY 0xdf /* tell PMU we are awake */ + +/* Bits in PMU interrupt and interrupt mask bytes */ +#define PMU_INT_ADB_AUTO 0x04 /* ADB autopoll, when PMU_INT_ADB */ +#define PMU_INT_PCEJECT 0x04 /* PC-card eject buttons */ +#define PMU_INT_SNDBRT 0x08 /* sound/brightness up/down buttons */ +#define PMU_INT_ADB 0x10 /* ADB autopoll or reply data */ +#define PMU_INT_BATTERY 0x20 +#define PMU_INT_WAKEUP 0x40 +#define PMU_INT_TICK 0x80 /* 1-second tick interrupt */ + +/* Bits to use with the PMU_POWER_CTRL0 command */ +#define PMU_POW0_ON 0x80 /* OR this to power ON the device */ +#define PMU_POW0_OFF 0x00 /* leave bit 7 to 0 to power it OFF */ + +/* Bits to use with the PMU_POWER_CTRL command */ +#define PMU_POW_ON 0x80 /* OR this to power ON the device */ +#define PMU_POW_OFF 0x00 /* leave bit 7 to 0 to power it OFF */ +#define PMU_POW_BACKLIGHT 0x01 /* backlight power */ +#define PMU_POW_CHARGER 0x02 /* battery charger power */ +#define PMU_POW_IRLED 0x04 /* IR led power (on wallstreet) */ +#define PMU_POW_MEDIABAY 0x08 /* media bay power (wallstreet/lombard ?) */ + +/* PMU PMU_POWER_EVENTS commands */ +enum { + PMU_PWR_GET_POWERUP_EVENTS = 0x00, + PMU_PWR_SET_POWERUP_EVENTS = 0x01, + PMU_PWR_CLR_POWERUP_EVENTS = 0x02, + PMU_PWR_GET_WAKEUP_EVENTS = 0x03, + PMU_PWR_SET_WAKEUP_EVENTS = 0x04, + PMU_PWR_CLR_WAKEUP_EVENTS = 0x05, +}; + diff --git a/sys/arch/macppc/dev/uni_n.c b/sys/arch/macppc/dev/uni_n.c new file mode 100644 index 00000000000..4d3e44efac5 --- /dev/null +++ b/sys/arch/macppc/dev/uni_n.c @@ -0,0 +1,118 @@ +/* $OpenBSD: uni_n.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ + +/* + * Copyright (c) 1998-2001 Dale Rahn. 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 Dale Rahn. + * 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 <sys/param.h> +#include <sys/device.h> +#include <sys/systm.h> + +#include <machine/bus.h> +#include <machine/autoconf.h> + +#include <dev/ofw/openfirm.h> + +static int memcmatch __P((struct device *, void *, void *)); +static void memcattach __P((struct device *, struct device *, void *)); + +struct memc_softc { + struct device sc_dev; + char *baseaddr; +}; +/* Driver definition */ +struct cfdriver memc_cd = { + NULL, "memc", DV_DULL +}; +/* Driver definition */ +struct cfattach memc_ca = { + sizeof(struct memc_softc), memcmatch, memcattach +}; + +void *uni_n_config(int handle); + +int +memcmatch(parent, cf, aux) + struct device *parent; + void *cf; + void *aux; +{ + struct confargs *ca = aux; + static int memc_attached = 0; + + /* allow only one instance */ + if (memc_attached == 0) { + if (0 == strcmp (ca->ca_name, "memc")) { + return 1; + } + } + return 0; +} + +static void +memcattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct confargs *ca = aux; + int len; + char name[64]; + struct memc_softc *sc = (struct memc_softc *)self; + + len = OF_getprop(ca->ca_node, "name", name, sizeof name); + if (len > 0) { + name[len] = 0; + } + if (strcmp (name, "uni-n")== 0) { + sc->baseaddr = uni_n_config(ca->ca_node); + } + printf (": %s\n", name); +} +void * +uni_n_config(int handle) +{ + char name[20]; + char *baseaddr; + int *ctladdr; + u_int32_t address; + + if (OF_getprop(handle, "name", name, sizeof name) > 0) { + /* sanity test */ + if (!strcmp (name, "uni-n")) { + if (OF_getprop(handle, "reg", &address, + sizeof address) > 0) { + baseaddr = mapiodev(address, NBPG); + ctladdr = (void*)(baseaddr + 0x20); + *ctladdr |= 0x02; + } + return baseaddr; + } + } + return 0; +} diff --git a/sys/arch/macppc/dev/viareg.h b/sys/arch/macppc/dev/viareg.h new file mode 100644 index 00000000000..c034f7fb990 --- /dev/null +++ b/sys/arch/macppc/dev/viareg.h @@ -0,0 +1,244 @@ +/* $OpenBSD: viareg.h,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: viareg.h,v 1.2 1998/10/20 14:56:30 tsubai Exp $ */ + +/*- + * Copyright (C) 1993 Allen K. Briggs, Chris P. Caputo, + * Michael L. Finch, Bradley A. Grantham, and + * Lawrence A. Kesteloot + * 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 the Alice Group. + * 4. The names of the Alice Group or any of its members may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``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 ALICE GROUP 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. + * + */ +/* + + Prototype VIA control definitions + + 06/04/92,22:33:57 BG Let's see what I can do. + +*/ + + + /* VIA1 data register A */ +#define DA1I_vSCCWrReq 0x80 +#define DA1O_vPage2 0x40 +#define DA1I_CPU_ID1 0x40 +#define DA1O_vHeadSel 0x20 +#define DA1O_vOverlay 0x10 +#define DA1O_vSync 0x08 +#define DA1O_RESERVED2 0x04 +#define DA1O_RESERVED1 0x02 +#define DA1O_RESERVED0 0x01 + + /* VIA1 data register B */ +#define DB1I_Par_Err 0x80 +#define DB1O_vSndEnb 0x80 +#define DB1O_Par_Enb 0x40 +#define DB1O_vFDesk2 0x20 +#define DB1O_vFDesk1 0x10 +#define DB1I_vFDBInt 0x08 +#define DB1O_rTCEnb 0x04 +#define DB1O_rTCCLK 0x02 +#define DB1O_rTCData 0x01 +#define DB1I_rTCData 0x01 + + /* VIA2 data register A */ +#define DA2O_v2Ram1 0x80 +#define DA2O_v2Ram0 0x40 +#define DA2I_v2IRQ0 0x40 +#define DA2I_v2IRQE 0x20 +#define DA2I_v2IRQD 0x10 +#define DA2I_v2IRQC 0x08 +#define DA2I_v2IRQB 0x04 +#define DA2I_v2IRQA 0x02 +#define DA2I_v2IRQ9 0x01 + + /* VIA2 data register B */ +#define DB2O_v2VBL 0x80 +#define DB2O_Par_Test 0x80 +#define DB2I_v2SNDEXT 0x40 +#define DB2I_v2TM0A 0x20 +#define DB2I_v2TM1A 0x10 +#define DB2I_vFC3 0x08 +#define DB2O_vFC3 0x08 +#define DB2O_v2PowerOff 0x04 +#define DB2O_v2BusLk 0x02 +#define DB2O_vCDis 0x01 +#define DB2O_CEnable 0x01 + +/* + * VIA1 interrupts + */ +#define VIA1_T1 6 +#define VIA1_T2 5 +#define VIA1_ADBCLK 4 +#define VIA1_ADBDATA 3 +#define VIA1_ADBRDY 2 +#define VIA1_VBLNK 1 +#define VIA1_ONESEC 0 + +/* VIA1 interrupt bits */ +#define V1IF_IRQ 0x80 +#define V1IF_T1 (1 << VIA1_T1) +#define V1IF_T2 (1 << VIA1_T2) +#define V1IF_ADBCLK (1 << VIA1_ADBCLK) +#define V1IF_ADBDATA (1 << VIA1_ADBDATA) +#define V1IF_ADBRDY (1 << VIA1_ADBRDY) +#define V1IF_VBLNK (1 << VIA1_VBLNK) +#define V1IF_ONESEC (1 << VIA1_ONESEC) + +/* + * VIA2 interrupts + */ +#define VIA2_T1 6 +#define VIA2_T2 5 +#define VIA2_ASC 4 +#define VIA2_SCSIIRQ 3 +#define VIA2_EXPIRQ 2 +#define VIA2_SLOTINT 1 +#define VIA2_SCSIDRQ 0 + +/* VIA2 interrupt bits */ +#define V2IF_IRQ 0x80 +#define V2IF_T1 (1 << VIA2_T1) +#define V2IF_T2 (1 << VIA2_T2) +#define V2IF_ASC (1 << VIA2_ASC) +#define V2IF_SCSIIRQ (1 << VIA2_SCSIIRQ) +#define V2IF_EXPIRQ (1 << VIA2_EXPIRQ) +#define V2IF_SLOTINT (1 << VIA2_SLOTINT) +#define V2IF_SCSIDRQ (1 << VIA2_SCSIDRQ) + +#define VIA1_INTS (V1IF_T1 | V1IF_ADBRDY) +#define VIA2_INTS (V2IF_T1 | V2IF_ASC | V2IF_SCSIIRQ | V2IF_SLOTINT | \ + V2IF_SCSIDRQ) + +#define RBV_INTS (V2IF_T1 | V2IF_ASC | V2IF_SCSIIRQ | V2IF_SLOTINT | \ + V2IF_SCSIDRQ | V1IF_ADBRDY) + +#define ACR_T1LATCH 0x40 + +extern volatile unsigned char *Via1Base; +#define VIA1_addr Via1Base /* at PA 0x50f00000 */ +#define VIA2OFF 1 /* VIA2 addr = VIA1_addr * 0x2000 */ +#define RBVOFF 0x13 /* RBV addr = VIA1_addr * 0x13000 */ + +#define VIA1 0 +#define VIA2 0 + + /* VIA interface registers */ +#define vBufA 0x1e00 /* register A */ +#define vBufB 0 /* register B */ +#define vDirA 0x0600 /* data direction register */ +#define vDirB 0x0400 /* data direction register */ +#define vT1C 0x0800 +#define vT1CH 0x0a00 +#define vT1L 0x0c00 +#define vT1LH 0x0e00 +#define vT2C 0x1000 +#define vT2CH 0x1200 +#define vSR 0x1400 /* shift register */ +#define vACR 0x1600 /* aux control register */ +#define vPCR 0x1800 /* peripheral control register */ +#define vIFR 0x1a00 /* interrupt flag register */ +#define vIER 0x1c00 /* interrupt enable register */ + + /* RBV interface registers */ +#define rBufB 0 /* register B */ +#define rBufA 2 /* register A */ +#define rIFR 0x3 /* interrupt flag register (writes?) */ +#define rIER 0x13 /* interrupt enable register */ +#define rMonitor 0x10 /* Monitor type */ +#define rSlotInt 0x12 /* Slot interrupt */ + + /* RBV monitor type flags and masks */ +#define RBVDepthMask 0x07 /* depth in bits */ +#define RBVMonitorMask 0x38 /* Type numbers */ +#define RBVOff 0x40 /* monitor turn off */ +#define RBVMonIDNone 0x38 /* What RBV actually has for no video */ +#define RBVMonIDOff 0x0 /* What rbv_vidstatus() returns for no video */ +#define RBVMonID15BWP 0x08 /* BW portrait */ +#define RBVMonIDRGB 0x10 /* color monitor */ +#define RBVMonIDRGB15 0x28 /* 15 inch RGB */ +#define RBVMonIDBW 0x30 /* No internal video */ + +#define via_reg(v, r) (*(Via1Base + (r))) + +#include <machine/pio.h> + +static __inline void +via_reg_and(int ign, int reg, int val) +{ + volatile unsigned char *addr = Via1Base + reg; + + out8(addr, in8(addr) & val); +} + +static __inline void +via_reg_or(int ign, int reg, int val) +{ + volatile unsigned char *addr = Via1Base + reg; + + out8(addr, in8(addr) | val); +} + +static __inline void +via_reg_xor(int ign, int reg, int val) +{ + volatile unsigned char *addr = Via1Base + reg; + + out8(addr, in8(addr) ^ val); +} + +static __inline int +read_via_reg(int ign, int reg) +{ + volatile unsigned char *addr = Via1Base + reg; + + return in8(addr); +} + +static __inline void +write_via_reg(int ign, int reg, int val) +{ + volatile unsigned char *addr = Via1Base + reg; + + out8(addr, val); +} + +#define vDirA_ADBState 0x30 + +void via_init __P((void)); +int rbv_vidstatus __P((void)); +void via_shutdown __P((void)); +void via_set_modem __P((int)); +int add_nubus_intr __P((int, void (*) __P((void *, int)), void *)); +void enable_nubus_intr __P((void)); +void via1_register_irq __P((int, void (*)(void *), void *)); +void via2_register_irq __P((int, void (*)(void *), void *)); + +extern void (*via1itab[7]) __P((void *)); +extern void (*via2itab[7]) __P((void *)); diff --git a/sys/arch/macppc/dev/wdc_obio.c b/sys/arch/macppc/dev/wdc_obio.c new file mode 100644 index 00000000000..f58cdec4372 --- /dev/null +++ b/sys/arch/macppc/dev/wdc_obio.c @@ -0,0 +1,408 @@ +/* $OpenBSD: wdc_obio.c,v 1.1 2001/09/01 15:50:00 drahn Exp $ */ +/* $NetBSD: wdc_obio.c,v 1.4 1999/06/14 08:53:06 tsubai Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum and by Onno van der Linden. + * + * 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 the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> + +#include <vm/vm.h> + +#include <machine/bus.h> +#include <machine/autoconf.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ata/atavar.h> +#include <dev/ata/atareg.h> +#include <dev/ic/wdcvar.h> + +#include <macppc/mac/dbdma.h> + +#define WDC_REG_NPORTS 8 +#define WDC_AUXREG_OFFSET 0x16 +#define WDC_DEFAULT_PIO_IRQ 13 /* XXX */ +#define WDC_DEFAULT_DMA_IRQ 2 /* XXX */ + +#define WDC_OPTIONS_DMA 0x01 + +/* + * XXX This code currently doesn't even try to allow 32-bit data port use. + */ + +struct wdc_obio_softc { + struct wdc_softc sc_wdcdev; + struct channel_softc *wdc_chanptr; + struct channel_softc wdc_channel; + dbdma_regmap_t *sc_dmareg; + dbdma_command_t *sc_dmacmd; +}; + +u_int8_t wdc_obio_read_reg __P((struct channel_softc *, enum wdc_regs)); +void wdc_obio_write_reg __P((struct channel_softc *, enum wdc_regs, u_int8_t)); +void wdc_default_read_raw_multi_2 __P((struct channel_softc *, + void *, unsigned int)); +void wdc_default_write_raw_multi_2 __P((struct channel_softc *, + void *, unsigned int)); +void wdc_default_read_raw_multi_4 __P((struct channel_softc *, + void *, unsigned int)); +void wdc_default_write_raw_multi_4 __P((struct channel_softc *, + void *, unsigned int)); +struct channel_softc_vtbl wdc_obio_vtbl = { + wdc_obio_read_reg, + wdc_obio_write_reg, + wdc_default_read_raw_multi_2, + wdc_default_write_raw_multi_2, + wdc_default_read_raw_multi_4, + wdc_default_write_raw_multi_4 +}; + +int wdc_obio_probe __P((struct device *, void *, void *)); +void wdc_obio_attach __P((struct device *, struct device *, void *)); + +struct cfattach wdc_obio_ca = { + sizeof(struct wdc_obio_softc), wdc_obio_probe, wdc_obio_attach +}; + +#if 0 +struct cfdriver wdc_cd = { + NULL, "wdc", DV_DULL +}; +#endif + + +static int wdc_obio_dma_init __P((void *, int, int, void *, size_t, int)); +static void wdc_obio_dma_start __P((void *, int, int)); +static int wdc_obio_dma_finish __P((void *, int, int)); +static void adjust_timing __P((struct channel_softc *)); + +int +wdc_obio_probe(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct confargs *ca = aux; + char compat[32]; + + /* XXX should not use name */ + if (strcmp(ca->ca_name, "ATA") == 0 || + strncmp(ca->ca_name, "ata", 3) == 0 || + strcmp(ca->ca_name, "ide") == 0) + return 1; + + bzero(compat, sizeof(compat)); + OF_getprop(ca->ca_node, "compatible", compat, sizeof(compat)); + if (strcmp(compat, "heathrow-ata") == 0 || + strcmp(compat, "keylargo-ata") == 0) + return 1; + + return 0; +} + +void +wdc_obio_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct wdc_obio_softc *sc = (void *)self; + struct confargs *ca = aux; + struct channel_softc *chp = &sc->wdc_channel; + int intr; + int use_dma = 1; + bus_addr_t cmdbase; + bus_size_t cmdsize; + + if (sc->sc_wdcdev.sc_dev.dv_cfdata->cf_flags & WDC_OPTIONS_DMA) { + if (ca->ca_nreg >= 16 || ca->ca_nintr == -1) + use_dma = 1; /* XXX Don't work yet. */ + } + + if (ca->ca_nintr >= 4 && ca->ca_nreg >= 8) { + intr = ca->ca_intr[0]; + printf(" irq %d", intr); + } else if (ca->ca_nintr == -1) { + intr = WDC_DEFAULT_PIO_IRQ; + printf(" irq property not found; using %d", intr); + } else { + printf(": couldn't get irq property\n"); + return; + } + + if (use_dma) + printf(": DMA transfer"); + + printf("\n"); + + chp->cmd_iot = chp->ctl_iot = ca->ca_iot; + chp->_vtbl = &wdc_obio_vtbl; + + cmdbase = ca->ca_reg[0]; + cmdsize = ca->ca_reg[1]; + + if (bus_space_map(chp->cmd_iot, cmdbase, cmdsize, 0, &chp->cmd_ioh) || + bus_space_subregion(chp->cmd_iot, chp->cmd_ioh, + /* WDC_AUXREG_OFFSET<<4 */ 0x160, 1, &chp->ctl_ioh)) + { + printf("%s: couldn't map registers\n", + sc->sc_wdcdev.sc_dev.dv_xname); + return; + } + chp->data32iot = chp->cmd_iot; + chp->data32ioh = chp->cmd_ioh; + + mac_intr_establish(parent, intr, IST_LEVEL, IPL_BIO, wdcintr, chp, + "wdc_obio"); + + if (use_dma) { + sc->sc_dmacmd = dbdma_alloc(sizeof(dbdma_command_t) * 20); + sc->sc_dmareg = mapiodev(ca->ca_baseaddr + ca->ca_reg[2], + ca->ca_reg[3]); + sc->sc_wdcdev.cap |= WDC_CAPABILITY_DMA|WDC_CAPABILITY_UDMA; + } + sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA16; + sc->sc_wdcdev.PIO_cap = 0; + sc->wdc_chanptr = chp; + sc->sc_wdcdev.channels = &sc->wdc_chanptr; + sc->sc_wdcdev.nchannels = 1; + sc->sc_wdcdev.dma_arg = sc; + sc->sc_wdcdev.dma_init = wdc_obio_dma_init; + sc->sc_wdcdev.dma_start = wdc_obio_dma_start; + sc->sc_wdcdev.dma_finish = wdc_obio_dma_finish; + chp->channel = 0; + chp->wdc = &sc->sc_wdcdev; + chp->ch_queue = malloc(sizeof(struct channel_queue), + M_DEVBUF, M_NOWAIT); + if (chp->ch_queue == NULL) { + printf("%s: can't allocate memory for command queue", + sc->sc_wdcdev.sc_dev.dv_xname); + return; + } + + wdcattach(chp); + + /* modify DMA access timings */ + if (use_dma) + adjust_timing(chp); + + wdc_print_current_modes(chp); +} + +/* Multiword DMA transfer timings */ +static struct { + int cycle; /* minimum cycle time [ns] */ + int active; /* minimum command active time [ns] */ +} dma_timing[3] = { + { 480, 215 }, /* Mode 0 */ + { 150, 80 }, /* Mode 1 */ + { 120, 70 }, /* Mode 2 */ +}; + +#define TIME_TO_TICK(time) howmany((time), 30) + +#define CONFIG_REG (0x200) /* IDE access timing register */ + +void +adjust_timing(chp) + struct channel_softc *chp; +{ + struct ataparams params; + struct ata_drive_datas *drvp = &chp->ch_drive[0]; /* XXX */ + u_int conf; + int mode; + int cycle, active, min_cycle, min_active; + int cycle_tick, act_tick, inact_tick, half_tick; + + if (ata_get_params(drvp, AT_POLL, ¶ms) != CMD_OK) + return; + + for (mode = 2; mode >= 0; mode--) + if (params.atap_dmamode_act & (1 << mode)) + goto found; + + /* No active DMA mode is found... Do nothing. */ + return; + +found: + min_cycle = dma_timing[mode].cycle; + min_active = dma_timing[mode].active; + +#ifdef notyet + /* Minimum cycle time is 150ns on ohare. */ + if (ohare && params.atap_dmatiming_recom < 150) + params.atap_dmatiming_recom = 150; +#endif + cycle = max(min_cycle, params.atap_dmatiming_recom); + active = min_active + (cycle - min_cycle); /* XXX */ + + cycle_tick = TIME_TO_TICK(cycle); + act_tick = TIME_TO_TICK(active); + inact_tick = cycle_tick - act_tick - 1; + if (inact_tick < 1) + inact_tick = 1; + half_tick = 0; /* XXX */ +#if 0 + conf = bus_space_read_4(chp->cmd_iot, chp->cmd_ioh, CONFIG_REG); + printf("conf = 0x%x, cyc = %d (%d ns), act = %d (%d ns), inact = %d\n", + conf, 0, 0, ((conf >> 11) & 0x1f), 0, ((conf >> 16) & 0x1f)); +#endif + conf = (half_tick << 21) | (inact_tick << 16) | (act_tick << 11); + bus_space_write_4(chp->cmd_iot, chp->cmd_ioh, CONFIG_REG, conf); +#if 0 + printf("conf = 0x%x, cyc = %d (%d ns), act = %d (%d ns), inact = %d\n", + conf, cycle_tick, cycle, act_tick, active, inact_tick); +#endif +} + +static int +wdc_obio_dma_init(v, channel, drive, databuf, datalen, read) + void *v; + void *databuf; + size_t datalen; + int read; +{ + struct wdc_obio_softc *sc = v; + vaddr_t va = (vaddr_t)databuf; + dbdma_command_t *cmdp; + u_int cmd, offset; + + cmdp = sc->sc_dmacmd; + cmd = read ? DBDMA_CMD_IN_MORE : DBDMA_CMD_OUT_MORE; + + offset = va & PGOFSET; + + /* if va is not page-aligned, setup the first page */ + if (offset != 0) { + int rest = NBPG - offset; /* the rest of the page */ + + if (datalen > rest) { /* if continues to next page */ + DBDMA_BUILD(cmdp, cmd, 0, rest, vtophys(va), + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, + DBDMA_BRANCH_NEVER); + datalen -= rest; + va += rest; + cmdp++; + } + } + + /* now va is page-aligned */ + while (datalen > NBPG) { + DBDMA_BUILD(cmdp, cmd, 0, NBPG, vtophys(va), + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + datalen -= NBPG; + va += NBPG; + cmdp++; + } + + /* the last page (datalen <= NBPG here) */ + cmd = read ? DBDMA_CMD_IN_LAST : DBDMA_CMD_OUT_LAST; + DBDMA_BUILD(cmdp, cmd, 0, datalen, vtophys(va), + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + cmdp++; + + DBDMA_BUILD(cmdp, DBDMA_CMD_STOP, 0, 0, 0, + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + + return 0; +} + +static void +wdc_obio_dma_start(v, channel, drive) + void *v; + int channel, drive; +{ + struct wdc_obio_softc *sc = v; + + dbdma_start(sc->sc_dmareg, sc->sc_dmacmd); +} + +static int +wdc_obio_dma_finish(v, channel, drive) + void *v; + int channel, drive; +{ + struct wdc_obio_softc *sc = v; + + dbdma_stop(sc->sc_dmareg); + return 0; +} + +/* read register code + * this allows the registers to be spaced by 0x10, instead of 0x1. + * mac hardware (obio) requires this. + */ + +u_int8_t +wdc_obio_read_reg(chp, reg) + struct channel_softc *chp; + enum wdc_regs reg; +{ +#ifdef DIAGNOSTIC + if (reg & _WDC_WRONLY) { + printf ("wdc_obio_read_reg: reading from a write-only register %d\n", reg); + } +#endif + + if (reg & _WDC_AUX) + return (bus_space_read_1(chp->ctl_iot, chp->ctl_ioh, + (reg & _WDC_REGMASK) << 4)); + else + return (bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, + (reg & _WDC_REGMASK) << 4)); +} + + +void +wdc_obio_write_reg(chp, reg, val) + struct channel_softc *chp; + enum wdc_regs reg; + u_int8_t val; +{ +#ifdef DIAGNOSTIC + if (reg & _WDC_RDONLY) { + printf ("wdc_obio_write_reg: writing to a read-only register %d\n", reg); + } +#endif + + if (reg & _WDC_AUX) + bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, + (reg & _WDC_REGMASK) << 4, val); + else + bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, + (reg & _WDC_REGMASK) << 4, val); +} |