# CONFIG_NET_VENDOR_RACAL is not set
# CONFIG_DEPCA is not set
# CONFIG_NET_ISA is not set
-CONFIG_NET_EISA=y
+CONFIG_NET_PCI=y
# CONFIG_PCNET32 is not set
# CONFIG_APRICOT is not set
# CONFIG_CS89x0 is not set
# CONFIG_DGRS is not set
CONFIG_EEXPRESS_PRO100=y
# CONFIG_NE2K_PCI is not set
+# CONFIG_8139TOO is not set
# CONFIG_SIS900 is not set
# CONFIG_TLAN is not set
# CONFIG_VIA_RHINE is not set
# CONFIG_SYSV_FS is not set
# CONFIG_SYSV_FS_WRITE is not set
# CONFIG_UDF_FS is not set
-# CONFIG_UDF_RW is not set
# CONFIG_UFS_FS is not set
# CONFIG_UFS_FS_WRITE is not set
comment 'SCSI support type (disk, tape, CD-ROM)'
dep_tristate ' SCSI disk support' CONFIG_BLK_DEV_SD $CONFIG_SCSI
+ if [ "$CONFIG_BLK_DEV_SD" != "n" ]; then
+ int 'Maximum number of SCSI disks that can be loaded as modules' CONFIG_SD_EXTRA_DEVS 40
+ fi
dep_tristate ' SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI
+ if [ "$CONFIG_BLK_DEV_ST" != "n" ]; then
+ int 'Maximum number of SCSI tapes that can be loaded as modules' CONFIG_ST_EXTRA_DEVS 2
+ fi
dep_tristate ' SCSI CD-ROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI
if [ "$CONFIG_BLK_DEV_SR" != "n" ]; then
bool ' Enable vendor-specific extensions (for SCSI CDROM)' CONFIG_BLK_DEV_SR_VENDOR
+ int 'Maximum number of CDROM devices that can be loaded as modules' CONFIG_SR_EXTRA_DEVS 2
fi
dep_tristate ' SCSI generic support' CONFIG_CHR_DEV_SG $CONFIG_SCSI
tristate ' Apollo 3c505 support' CONFIG_APOLLO_ELPLUS
fi
if [ "$CONFIG_MAC" = "y" ]; then
- bool ' Mac NS 8390 based ethernet cards' CONFIG_DAYNAPORT
-# bool ' Macintosh (AV) onboard MACE ethernet' CONFIG_MACMACE
- bool ' Macintosh (Quadra) onboard SONIC ethernet' CONFIG_MACSONIC
+ bool ' Macintosh NS 8390 based ethernet cards' CONFIG_MAC8390
+ tristate ' Macintosh SONIC based ethernet (onboard, NuBus, LC, CS)' CONFIG_MACSONIC
+ tristate ' Macintosh SMC 9194 based ethernet cards' CONFIG_SMC9194
+ tristate ' Macintosh CS89x0 based ethernet cards' CONFIG_MAC89x0
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool ' Macintosh (AV) onboard MACE ethernet (EXPERIMENTAL)' CONFIG_MACMACE
+ fi
fi
if [ "$CONFIG_VME" = "y" -a "$CONFIG_MVME147" = "y" ]; then
tristate ' MVME147 (Lance) Ethernet support' CONFIG_MVME147_NET
define_bool CONFIG_BUSMOUSE y
fi
fi
-if [ "$CONFIG_MAC" = "y" ]; then
- bool 'Mac ADB mouse support' CONFIG_ADBMOUSE
- if [ "$CONFIG_ADBMOUSE" != "n" ]; then
- define_bool CONFIG_BUSMOUSE y
- fi
-fi
if [ "$CONFIG_ATARI" = "y" ]; then
tristate 'Atari MFP serial support' CONFIG_ATARI_MFPSER
tristate 'Atari SCC serial support' CONFIG_ATARI_SCC
fi
fi
if [ "$CONFIG_MAC" = "y" ]; then
- bool 'Mac SCC serial support' CONFIG_MAC_SCC
+ tristate 'Macintosh serial support' CONFIG_MAC_SCC
+ bool 'Apple Desktop Bus (ADB) support' CONFIG_ADB
+ if [ "$CONFIG_ADB" = "y" ]; then
+ bool ' Support for ADB keyboard' CONFIG_ADB_KEYBOARD
+ bool ' Support for ADB mouse' CONFIG_ADBMOUSE
+ bool ' Include Mac II ADB driver' CONFIG_ADB_MACII
+ bool ' Include Mac IIsi ADB driver' CONFIG_ADB_MACIISI
+ bool ' Include CUDA ADB driver' CONFIG_ADB_CUDA
+ bool ' Include IOP (IIfx/Quadra 9x0) ADB driver' CONFIG_ADB_IOP
+ bool ' Include PMU (Powerbook) ADB driver' CONFIG_ADB_PMU68K
+ fi
+ if [ "$CONFIG_ADBMOUSE" = "y" ]; then
+ define_bool CONFIG_BUSMOUSE y
+ fi
fi
if [ "$CONFIG_HP300" = "y" -a "$CONFIG_DIO" = "y" ]; then
tristate 'HP DCA serial support' CONFIG_HPDCA
head.o: head.S m68k_defs.h
+entry.o: entry.S m68k_defs.h
+
sun3-head.o: sun3-head.S m68k_defs.h
m68k_defs.h: m68k_defs.c m68k_defs.head
#endif
/* check if we need to do software interrupts */
- movel SYMBOL_NAME(bh_active),%d0
- andl SYMBOL_NAME(bh_mask),%d0
+ movel SYMBOL_NAME(softirq_state),%d0
+ andl SYMBOL_NAME(softirq_state)+4,%d0
jeq SYMBOL_NAME(ret_from_exception)
pea SYMBOL_NAME(ret_from_exception)
- jra SYMBOL_NAME(do_bottom_half)
+ jra SYMBOL_NAME(do_softirq)
/* Handler for uninitialized and spurious interrupts */
#include <asm/checksum.h>
#include <asm/hardirq.h>
#include <asm/softirq.h>
-#include <asm/m68kserial.h>
asmlinkage long long __ashrdi3 (long long, int);
asmlinkage long long __lshrdi3 (long long, int);
O_TARGET := mac.o
OX_OBJS := mac_ksyms.o
O_OBJS := config.o bootparse.o macints.o iop.o via.o oss.o psc.o \
- macboing.o debug.o misc.o
+ baboon.o macboing.o debug.o misc.o
include $(TOPDIR)/Rules.make
+++ /dev/null
-/*
- * MACII ADB keyboard handler.
- * Copyright (c) 1997 Alan Cox
- *
- * Derived from code
- * Copyright (C) 1996 Paul Mackerras.
- *
- * MSch (9/97) Partial rewrite of interrupt handler to MacII style
- * ADB handshake, based on:
- * - Guide to Mac Hardware
- * - Guido Koerber's session with a logic analyzer
- *
- * MSch (1/98) Integrated start of IIsi driver by Robert Thompson
- */
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/miscdevice.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <linux/mm.h>
-#include "via6522.h"
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/adb.h>
-#include <asm/system.h>
-#include <asm/segment.h>
-#include <asm/setup.h>
-#include <asm/macintosh.h>
-#include <asm/macints.h>
-
-
-#define MACII /* For now - will be a switch */
-
-/* Bits in B data register: all active low */
-#define TREQ 0x08 /* Transfer request (input) */
-#define TACK 0x10 /* Transfer acknowledge (output) */
-#define TIP 0x20 /* Transfer in progress (output) */
-
-/* Bits in B data register: ADB transaction states MacII */
-#define ST_MASK 0x30 /* mask for selecting ADB state bits */
-/* ADB transaction states according to GMHW */
-#define ST_CMD 0x00 /* ADB state: command byte */
-#define ST_EVEN 0x10 /* ADB state: even data byte */
-#define ST_ODD 0x20 /* ADB state: odd data byte */
-#define ST_IDLE 0x30 /* ADB state: idle, nothing to send */
-
-/* Bits in ACR */
-#define SR_CTRL 0x1c /* Shift register control bits */
-#ifdef USE_ORIG
-#define SR_EXT 0x1c /* Shift on external clock */
-#else
-#define SR_EXT 0x0c /* Shift on external clock */
-#endif
-#define SR_OUT 0x10 /* Shift out if 1 */
-
-/* Bits in IFR and IER */
-#define IER_SET 0x80 /* set bits in IER */
-#define IER_CLR 0 /* clear bits in IER */
-#define SR_INT 0x04 /* Shift register full/empty */
-#define SR_DATA 0x08 /* Shift register data */
-#define SR_CLOCK 0x10 /* Shift register clock */
-
-/* JRT */
-#define ADB_DELAY 150
-
-static struct adb_handler {
- void (*handler)(unsigned char *, int, struct pt_regs *);
-} adb_handler[16];
-
-static enum adb_state {
- idle,
- sent_first_byte,
- sending,
- reading,
- read_done,
- awaiting_reply
-} adb_state;
-
-static struct adb_request *current_req;
-static struct adb_request *last_req;
-static unsigned char cuda_rbuf[16];
-static unsigned char *reply_ptr;
-static int reply_len;
-static int reading_reply;
-static int data_index;
-static int first_byte;
-static int prefix_len;
-
-static int status = ST_IDLE|TREQ;
-static int last_status;
-
-static int driver_running = 0;
-
-/*static int adb_delay;*/
-int in_keybinit = 1;
-
-static void adb_start(void);
-extern void adb_interrupt(int irq, void *arg, struct pt_regs *regs);
-extern void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs);
-extern void adb_clock_interrupt(int irq, void *arg, struct pt_regs *regs);
-extern void adb_data_interrupt(int irq, void *arg, struct pt_regs *regs);
-static void adb_input(unsigned char *buf, int nb, struct pt_regs *regs);
-
-static void adb_hw_setup_IIsi(void);
-static void adb_hw_setup_cuda(void);
-
-/*
- * debug level 10 required for ADB logging (should be && debug_adb, ideally)
- */
-
-extern int console_loglevel;
-
-/*
- * Misc. defines for testing - should go to header :-(
- */
-
-#define ADBDEBUG_STATUS (1)
-#define ADBDEBUG_STATE (2)
-#define ADBDEBUG_READ (4)
-#define ADBDEBUG_WRITE (8)
-#define ADBDEBUG_START (16)
-#define ADBDEBUG_RETRY (32)
-#define ADBDEBUG_POLL (64)
-#define ADBDEBUG_INT (128)
-#define ADBDEBUG_PROT (256)
-#define ADBDEBUG_SRQ (512)
-#define ADBDEBUG_REQUEST (1024)
-#define ADBDEBUG_INPUT (2048)
-#define ADBDEBUG_DEVICE (4096)
-
-#define ADBDEBUG_IISI (8192)
-
-
-/*#define DEBUG_ADB*/
-
-#ifdef DEBUG_ADB
-#define ADBDEBUG (ADBDEBUG_INPUT | ADBDEBUG_READ | ADBDEBUG_START | ADBDEBUG_WRITE | ADBDEBUG_SRQ | ADBDEBUG_REQUEST)
-#else
-#define ADBDEBUG (0)
-#endif
-
-#define TRY_CUDA
-
-void adb_bus_init(void)
-{
- unsigned long flags;
- unsigned char c, i;
-
- save_flags(flags);
- cli();
-
- /*
- * Setup ADB
- */
-
- switch(macintosh_config->adb_type)
- {
-
- case MAC_ADB_II:
- printk("adb: MacII style keyboard/mouse driver.\n");
- /* Set the lines up. We want TREQ as input TACK|TIP as output */
- via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ));
- /*
- * Docs suggest TREQ should be output - that seems nuts
- * BSD agrees here :-)
- * Setup vPCR ??
- */
-
-#ifdef USE_ORIG
- /* Lower the bus signals (MacII is active low it seems ???) */
- via_write(via1, vBufB, via_read(via1, vBufB)&~TACK);
-#else
- /* Corresponding state: idle (clear state bits) */
- via_write(via1, vBufB, (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
- last_status = (via_read(via1, vBufB)&~ST_MASK);
-#endif
- /* Shift register on input */
- c=via_read(via1, vACR);
- c&=~SR_CTRL; /* Clear shift register bits */
- c|=SR_EXT; /* Shift on external clock; out or in? */
- via_write(via1, vACR, c);
- /* Wipe any pending data and int */
- via_read(via1, vSR);
-
- /* This is interrupts on enable SR for keyboard */
- via_write(via1, vIER, IER_SET|SR_INT);
- /* This clears the interrupt bit */
- via_write(via1, vIFR, SR_INT);
-
- /*
- * Ok we probably ;) have a ready to use adb bus. Its also
- * hopefully idle (Im assuming the mac didnt leave a half
- * complete transaction on booting us).
- */
-
- request_irq(IRQ_MAC_ADB, adb_interrupt, IRQ_FLG_LOCK,
- "adb interrupt", adb_interrupt);
- adb_state = idle;
- break;
- /*
- * Unsupported; but later code doesn't notice !!
- */
- case MAC_ADB_CUDA:
- printk("adb: CUDA interface.\n");
-#if 0
- /* don't know what to set up here ... */
- adb_state = idle;
- /* Set the lines up. We want TREQ as input TACK|TIP as output */
- via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ));
-#endif
- adb_hw_setup_cuda();
- adb_state = idle;
- request_irq(IRQ_MAC_ADB, adb_cuda_interrupt, IRQ_FLG_LOCK,
- "adb CUDA interrupt", adb_cuda_interrupt);
- break;
- case MAC_ADB_IISI:
- printk("adb: Using IIsi hardware.\n");
- printk("\tDEBUG_JRT\n");
- /* Set the lines up. We want TREQ as input TACK|TIP as output */
- via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ));
-
- /*
- * MSch: I'm pretty sure the setup is mildly wrong
- * for the IIsi.
- */
- /* Initial state: idle (clear state bits) */
- via_write(via1, vBufB, (via_read(via1, vBufB) & ~(TIP|TACK)) );
- last_status = (via_read(via1, vBufB)&~ST_MASK);
- /* Shift register on input */
- c=via_read(via1, vACR);
- c&=~SR_CTRL; /* Clear shift register bits */
- c|=SR_EXT; /* Shift on external clock; out or in? */
- via_write(via1, vACR, c);
- /* Wipe any pending data and int */
- via_read(via1, vSR);
-
- /* This is interrupts on enable SR for keyboard */
- via_write(via1, vIER, IER_SET|SR_INT);
- /* This clears the interrupt bit */
- via_write(via1, vIFR, SR_INT);
-
- /* get those pesky clock ticks we missed while booting */
- for ( i = 0; i < 60; i++) {
- udelay(ADB_DELAY);
- adb_hw_setup_IIsi();
- udelay(ADB_DELAY);
- if (via_read(via1, vBufB) & TREQ)
- break;
- }
- if (i == 60)
- printk("adb_IIsi: maybe bus jammed ??\n");
-
- /*
- * Ok we probably ;) have a ready to use adb bus. Its also
- */
- request_irq(IRQ_MAC_ADB, adb_cuda_interrupt, IRQ_FLG_LOCK,
- "adb interrupt", adb_cuda_interrupt);
- adb_state = idle;
- break;
- default:
- printk("adb: Unknown hardware interface.\n");
- nosupp:
- printk("adb: Interface unsupported.\n");
- restore_flags(flags);
- return;
- }
-
- /*
- * XXX: interrupt only registered if supported HW !!
- * -> unsupported HW will just time out on keyb_init!
- */
-#if 0
- request_irq(IRQ_MAC_ADB, adb_interrupt, IRQ_FLG_LOCK,
- "adb interrupt", adb_interrupt);
-#endif
-#ifdef DEBUG_ADB_INTS
- request_irq(IRQ_MAC_ADB_CL, adb_clock_interrupt, IRQ_FLG_LOCK,
- "adb clock interrupt", adb_clock_interrupt);
- request_irq(IRQ_MAC_ADB_SD, adb_data_interrupt, IRQ_FLG_LOCK,
- "adb data interrupt", adb_data_interrupt);
-#endif
-
- printk("adb: init done.\n");
- restore_flags(flags);
-}
-
-void adb_hw_setup_cuda(void)
-{
- int x;
- unsigned long flags;
-
- printk("CUDA: HW Setup:");
-
- save_flags(flags);
- cli();
-
- if (console_loglevel == 10)
- printk(" 1,");
-
- /* Set the direction of the cuda signals, TIP+TACK are output TREQ is an input */
- via_write( via1, vDirB, via_read( via1, vDirB ) | TIP | TACK );
- via_write( via1, vDirB, via_read( via1, vDirB ) & ~TREQ );
-
- if (console_loglevel == 10)
- printk("2,");
-
- /* Set the clock control. Set to shift data in by external clock CB1 */
- via_write( via1, vACR, ( via_read(via1, vACR ) | SR_EXT ) & ~SR_OUT );
-
- if (console_loglevel == 10)
- printk("3,");
-
- /* Clear any possible Cuda interrupt */
- x = via_read( via1, vSR );
-
- if (console_loglevel == 10)
- printk("4,");
-
- /* Terminate transaction and set idle state */
- via_write( via1, vBufB, via_read( via1, vBufB ) | TIP | TACK );
-
- if (console_loglevel == 10)
- printk("5,");
-
- /* Delay 4 mS for ADB reset to complete */
- udelay(4000);
-
- if (console_loglevel == 10)
- printk("6,");
-
- /* Clear pending interrupts... */
- x = via_read( via1, vSR );
-
- if (console_loglevel == 10)
- printk("7,");
- /* Issue a sync transaction, TACK asserted while TIP negated */
- via_write( via1, vBufB, via_read( via1, vBufB ) & ~TACK );
-
- if (console_loglevel == 10)
- printk("8,");
-
- /* Wait for the sync acknowledgement, Cuda to assert TREQ */
- while( ( via_read( via1, vBufB ) & TREQ ) != 0 )
- barrier();
-
- if (console_loglevel == 10)
- printk("9,");
-
- /* Wait for the sync acknowledment interrupt */
- while( ( via_read( via1, vIFR ) & SR_INT ) == 0 )
- barrier();
-
- if (console_loglevel == 10)
- printk("10,");
-
- /* Clear pending interrupts... */
- x = via_read( via1, vSR );
-
- if (console_loglevel == 10)
- printk("11,");
-
- /* Terminate the sync cycle by negating TACK */
- via_write( via1, vBufB, via_read( via1, vBufB ) | TACK );
-
- if (console_loglevel == 10)
- printk("12,");
-
- /* Wait for the sync termination acknowledgement, Cuda to negate TREQ */
- while( ( via_read( via1, vBufB ) & TREQ ) == 0 )
- barrier();
-
- if (console_loglevel == 10)
- printk("13,");
-
- /* Wait for the sync termination acknowledment interrupt */
- while( ( via_read( via1, vIFR ) & SR_INT ) == 0 )
- barrier();
-
- if (console_loglevel == 10)
- printk("14,");
-
- /* Terminate transaction and set idle state, TIP+TACK negate */
- via_write( via1, vBufB, via_read( via1, vBufB ) | TIP );
-
- if (console_loglevel == 10)
- printk("15 !");
-
- /* Clear pending interrupts... */
- x = via_read( via1, vSR );
-
- restore_flags(flags);
-
- printk("\nCUDA: HW Setup done!\n");
-}
-
-void adb_hw_setup_IIsi(void)
-{
- int dummy;
- long poll_timeout;
-
- printk("adb_IIsi: cleanup!\n");
-
- /* ??? */
- udelay(ADB_DELAY);
-
- /* disable SR int. */
- via_write(via1, vIER, IER_CLR|SR_INT);
- /* set SR to shift in */
- via_write(via1, vACR, via_read(via1, vACR ) & ~SR_OUT);
-
- /* this is required, especially on faster machines */
- udelay(ADB_DELAY);
-
- if (!(via_read(via1, vBufB) & TREQ)) { /* IRQ on */
- /* start frame */
- via_write(via1, vBufB,via_read(via1,vBufB) | TIP);
-
- while (1) {
- /* poll for ADB interrupt and watch for timeout */
- /* if time out, keep going in hopes of not hanging the
- * ADB chip - I think */
- poll_timeout = ADB_DELAY * 5;
- while ( !(via_read(via1, vIFR) & SR_INT)
- && (poll_timeout-- > 0) )
- dummy = via_read(via1, vBufB);
-
- dummy = via_read(via1, vSR); /* reset interrupt flag */
-
- /* perhaps put in a check here that ignores all data
- * after the first ADB_MAX_MSG_LENGTH bytes ??? */
-
- /* end of frame reached ?? */
- if (via_read(via1, vBufB) & TREQ)
- break;
-
- /* set ACK */
- via_write(via1,vBufB,via_read(via1, vBufB) | TACK);
- /* delay */
- udelay(ADB_DELAY);
- /* clear ACK */
- via_write(via1,vBufB,via_read(via1, vBufB) & ~TACK);
- }
- /* end frame */
- via_write(via1, vBufB,via_read(via1,vBufB) & ~TIP);
- /* probably don't need to delay this long */
- udelay(ADB_DELAY);
- }
- /* re-enable SR int. */
- via_write(via1, vIER, IER_SET|SR_INT);
-}
-
-#define WAIT_FOR(cond, what) \
- do { \
- for (x = 1000; !(cond); --x) { \
- if (x == 0) { \
- printk("Timeout waiting for " what); \
- return 0; \
- } \
- __delay(100*160); \
- } \
- } while (0)
-
-/*
- * Construct and send an adb request
- * This function is the main entry point into the ADB driver from
- * kernel code; it takes the request data supplied and populates the
- * adb_request structure.
- * In order to keep this interface independent from any assumption about
- * the underlying ADB hardware, we take requests in CUDA format here,
- * the ADB packet 'prefixed' with a packet type code.
- * Non-CUDA hardware is confused by this, so we strip the packet type
- * here depending on hardware type ...
- */
-int adb_request(struct adb_request *req, void (*done)(struct adb_request *),
- int nbytes, ...)
-{
- va_list list;
- int i, start;
-
- va_start(list, nbytes);
-
- /*
- * skip first byte if not CUDA
- */
- if (macintosh_config->adb_type == MAC_ADB_II) {
- start = va_arg(list, int);
- nbytes--;
- }
- req->nbytes = nbytes;
- req->done = done;
-#if (ADBDEBUG & ADBDEBUG_REQUEST)
- if (console_loglevel == 10)
- printk("adb_request, data bytes: ");
-#endif
- for (i = 0; i < nbytes; ++i) {
- req->data[i] = va_arg(list, int);
-#if (ADBDEBUG & ADBDEBUG_REQUEST)
- if (console_loglevel == 10)
- printk("%x ", req->data[i]);
-#endif
- }
-#if (ADBDEBUG & ADBDEBUG_REQUEST)
- if (console_loglevel == 10)
- printk(" !\n");
-#endif
- va_end(list);
- /*
- * XXX: This might be fatal if no reply is generated (i.e. Listen) !
- * Currently, the interrupt handler 'fakes' a reply on non-TALK
- * commands for this reason.
- * Also, we need a CUDA_AUTOPOLL emulation here for non-CUDA
- * Macs, and some mechanism to remember the last issued TALK
- * request for resending it repeatedly on timeout!
- */
- req->reply_expected = 1;
- return adb_send_request(req);
-}
-
-/*
- * Construct an adb request for later sending
- * This function only populates the adb_request structure, without
- * actually queueing it.
- * Reason: Poll requests and Talk requests need to be handled in a way
- * different from 'user' requests; no reply_expected is set and
- * Poll requests need to be placed at the head of the request queue.
- * Using adb_request results in implicit queueing at the tail of the
- * request queue (duplicating the Poll) with reply_expected set.
- * No adjustment of packet data is necessary, as this mechanisnm is not
- * used by CUDA hardware (Autopoll used instead).
- */
-int adb_build_request(struct adb_request *req, void (*done)(struct adb_request *),
- int nbytes, ...)
-{
- va_list list;
- int i;
-
- req->nbytes = nbytes;
- req->done = done;
- va_start(list, nbytes);
-#if (ADBDEBUG & ADBDEBUG_REQUEST)
- if (console_loglevel == 10)
- printk("adb__build_request, data bytes: ");
-#endif
- /*
- * skip first byte if not CUDA ?
- */
- for (i = 0; i < nbytes; ++i) {
- req->data[i] = va_arg(list, int);
-#if (ADBDEBUG & ADBDEBUG_REQUEST)
- if (console_loglevel == 10)
- printk("%x ", req->data[i]);
-#endif
- }
-#if (ADBDEBUG & ADBDEBUG_REQUEST)
- if (console_loglevel == 10)
- printk(" !\n");
-#endif
- va_end(list);
-
- req->reply_expected = 0;
- return 0;
-}
-
-/*
- * Send an ADB poll (Talk, tagged on the front of the request queue)
- */
-void adb_queue_poll(void)
-{
- static int pod=0;
- static int in_poll=0;
- static struct adb_request r;
- unsigned long flags;
-
- if(in_poll)
- printk("Double poll!\n");
-
- in_poll++;
- pod++;
- if(pod>7) /* 15 */
- pod=0;
-
-#if (ADBDEBUG & ADBDEBUG_POLL)
- if (console_loglevel == 10)
- printk("adb: Polling %d\n",pod);
-#endif
-
- if (macintosh_config->adb_type == MAC_ADB_II)
- /* XXX: that's a TALK, register 0, MacII version */
- adb_build_request(&r,NULL, 1, (pod<<4|0xC));
- else
- /* CUDA etc. version */
- adb_build_request(&r,NULL, 2, 0, (pod<<4|0xC));
-
- r.reply_expected=0;
- r.done=NULL;
- r.sent=0;
- r.got_reply=0;
- r.reply_len=0;
- save_flags(flags);
- cli();
- /* Poll inserted at head of queue ... */
- r.next=current_req;
- current_req=&r;
- restore_flags(flags);
- adb_start();
- in_poll--;
-}
-
-/*
- * Send an ADB retransmit (Talk, appended to the request queue)
- */
-void adb_retransmit(int device)
-{
- static int in_retransmit=0;
- static struct adb_request rt;
- unsigned long flags;
-
- if(in_retransmit)
- printk("Double retransmit!\n");
-
- in_retransmit++;
-
-#if (ADBDEBUG & ADBDEBUG_POLL)
- if (console_loglevel == 10)
- printk("adb: Sending retransmit: %d\n", device);
-#endif
-
- /* MacII version */
- adb_build_request(&rt,NULL, 1, (device<<4|0xC));
-
- rt.reply_expected = 0;
- rt.done = NULL;
- rt.sent = 0;
- rt.got_reply = 0;
- rt.reply_len = 0;
- rt.next = NULL;
-
- save_flags(flags);
- cli();
-
- /* Retransmit inserted at tail of queue ... */
-
- if (current_req != NULL)
- {
- last_req->next = &rt;
- last_req = &rt;
- }
- else
- {
- current_req = &rt;
- last_req = &rt;
- }
-
- /* always restart driver (send_retransmit used in place of adb_start!)*/
-
- if (adb_state == idle)
- adb_start();
-
- restore_flags(flags);
- in_retransmit--;
-}
-
-/*
- * Queue an ADB request; start ADB transfer if necessary
- */
-int adb_send_request(struct adb_request *req)
-{
- unsigned long flags;
-
- req->next = 0;
- req->sent = 0;
- req->got_reply = 0;
- req->reply_len = 0;
- save_flags(flags);
- cli();
-
- if (current_req != NULL)
- {
- last_req->next = req;
- last_req = req;
- }
- else
- {
- current_req = req;
- last_req = req;
- if (adb_state == idle)
- adb_start();
- }
-
- restore_flags(flags);
- return 0;
-}
-
-static int nclock, ndata;
-
-static int need_poll = 0;
-static int command_byte = 0;
-static int last_reply = 0;
-static int last_active = 0;
-
-static struct adb_request *retry_req;
-
-/*
- * Start sending ADB packet
- */
-static void adb_start(void)
-{
- unsigned long flags;
- struct adb_request *req;
-
- /*
- * We get here on three 'sane' conditions:
- * 1) called from send_adb_request, if adb_state == idle
- * 2) called from within adb_interrupt, if adb_state == idle
- * (after receiving, or after sending a LISTEN)
- * 3) called from within adb_interrupt, if adb_state == sending
- * and no reply is expected (immediate next command).
- * Maybe we get here on SRQ as well ??
- */
-
- /* get the packet to send */
- req = current_req;
- /* assert adb_state == idle */
- if (adb_state != idle) {
- printk("ADB: adb_start called while driver busy (%p %x %x)!\n",
- req, adb_state, via_read(via1, vBufB)&(ST_MASK|TREQ));
- return;
- }
- if (req == 0)
- return;
- save_flags(flags);
- cli();
-
-#if (ADBDEBUG & ADBDEBUG_START)
- if (console_loglevel == 10)
- printk("adb_start: request %p ", req);
-#endif
-
- nclock = 0;
- ndata = 0;
-
- /*
- * IRQ signaled ?? (means ADB controller wants to send, or might
- * be end of packet if we were reading)
- */
- if ((via_read(via1, vBufB) & TREQ) == 0)
- {
- switch(macintosh_config->adb_type)
- {
- /*
- * FIXME - we need to restart this on a timer
- * or a collision at boot hangs us.
- * Never set adb_state to idle here, or adb_start
- * won't be called again from send_request!
- * (need to re-check other cases ...)
- */
- case MAC_ADB_CUDA:
- /* printk("device busy - fail\n"); */
- restore_flags(flags);
- /* a byte is coming in from the CUDA */
- return;
- case MAC_ADB_IISI:
- printk("adb_start: device busy - fail\n");
- retry_req = req;
- restore_flags(flags);
- return;
- case MAC_ADB_II:
- /*
- * if the interrupt handler set the need_poll
- * flag, it's hopefully a SRQ poll or re-Talk
- * so we try to send here anyway
- */
- if (!need_poll) {
- printk("device busy - retry %p state %d status %x!\n",
- req, adb_state, via_read(via1, vBufB)&(ST_MASK|TREQ));
- retry_req = req;
- /* set ADB status here ? */
- restore_flags(flags);
- return;
- } else {
-#if (ADBDEBUG & ADBDEBUG_START)
- if (console_loglevel == 10)
- printk("device busy - polling; state %d status %x!\n",
- adb_state, via_read(via1, vBufB)&(ST_MASK|TREQ));
-#endif
- need_poll = 0;
- break;
- }
- }
- }
-
-#if 0
- /*
- * Bus idle ?? Not sure about this one; SRQ might need ST_CMD here!
- * OTOH: setting ST_CMD in the interrupt routine would make the
- * ADB contoller shift in before this routine starts shifting out ...
- */
- if ((via_read(via1, vBufB)&ST_MASK) != ST_IDLE)
- {
-#if (ADBDEBUG & ADBDEBUG_STATE)
- if (console_loglevel == 10)
- printk("ADB bus not idle (%x), retry later!\n",
- via_read(via1, vBufB)&(ST_MASK|TREQ));
-#endif
- retry_req = req;
- restore_flags(flags);
- return;
- }
-#endif
-
- /*
- * Another retry pending? (sanity check)
- */
- if (retry_req) {
-#if (ADBDEBUG & ADBDEBUG_RETRY)
- if (console_loglevel == 10)
- if (retry_req == req)
- /* new requests are appended at tail of request queue */
- printk("adb_start: retry %p pending ! \n", req);
- else
- /* poll requests are added to the head of queue */
- printk("adb_start: retry %p pending, req %p (poll?) current! \n",
- retry_req, req);
-#endif
- retry_req = NULL;
- }
-
- /*
- * Seems OK, go for it!
- */
- switch(macintosh_config->adb_type)
- {
- case MAC_ADB_CUDA:
- /* store command byte (first byte is 'type' byte) */
- command_byte = req->data[1];
- /* set the shift register to shift out and send a byte */
- via_write(via1, vACR, via_read(via1, vACR)|SR_OUT);
- via_write(via1, vSR, req->data[0]);
- via_write(via1, vBufB, via_read(via1, vBufB)&~TIP);
- break;
- case MAC_ADB_IISI:
- /* store command byte (first byte is 'type' byte) */
- command_byte = req->data[1];
- /* set ADB state to 'active' */
- via_write(via1, vBufB, via_read(via1, vBufB) | TIP);
- /* switch ACK off (in case it was left on) */
- via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
- /* set the shift register to shift out and send a byte */
- via_write(via1, vACR, via_read(via1, vACR) | SR_OUT);
- via_write(via1, vSR, req->data[0]);
- /* signal 'byte ready' */
- via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
- break;
- case MAC_ADB_II:
- /* store command byte */
- command_byte = req->data[0];
- /* Output mode */
- via_write(via1, vACR, via_read(via1, vACR)|SR_OUT);
- /* Load data */
- via_write(via1, vSR, req->data[0]);
-#ifdef USE_ORIG
- /* Turn off TIP/TACK - this should tell the external logic to
- start the external shift clock */
-/* via_write(via1, vBufB, via_read(via1, vBufB)&~(TIP|TACK));*/
- via_write(via1, vBufB, via_read(via1, vBufB)|(TIP|TACK));
-#else
- /* set ADB state to 'command' */
- via_write(via1, vBufB, (via_read(via1, vBufB)&~ST_MASK)|ST_CMD);
-#endif
- break;
- }
- adb_state = sent_first_byte;
- data_index = 1;
-#if (ADBDEBUG & ADBDEBUG_START)
- if (console_loglevel == 10)
- printk("sent first byte of %d: %x, (%x %x) ... ",
- req->nbytes, req->data[0], adb_state,
- (via_read(via1, vBufB) & (ST_MASK|TREQ)) );
-#endif
- restore_flags(flags);
-}
-
-/*
- * Poll the ADB state (maybe obsolete now that interrupt-driven ADB runs)
- */
-void adb_poll(void)
-{
- unsigned char c;
- unsigned long flags;
- save_flags(flags);
- cli();
- c=via_read(via1, vIFR);
-#if (ADBDEBUG & ADBDEBUG_POLL)
-#ifdef DEBUG_ADB_INTS
- if (console_loglevel == 10) {
- printk("adb_poll: IFR %x state %x cl %d dat %d ",
- c, adb_state, nclock, ndata);
- if (c & (SR_CLOCK|SR_DATA)) {
- if (c & SR_CLOCK)
- printk("adb clock event ");
- if (c & SR_DATA)
- printk("adb data event ");
- }
- }
-#else
- if (console_loglevel == 10)
- printk("adb_poll: IFR %x state %x ",
- c, adb_state);
-#endif
- if (console_loglevel == 10)
- printk("\r");
-#endif
- if (c & SR_INT)
- {
-#if (ADBDEBUG & ADBDEBUG_POLL)
- if (console_loglevel == 10)
- printk("adb_poll: adb interrupt event\n");
-#endif
- adb_interrupt(0, 0, 0);
- }
- restore_flags(flags);
-}
-
-/*
- * Debugging gimmicks
- */
-void adb_clock_interrupt(int irq, void *arg, struct pt_regs *regs)
-{
- nclock++;
-}
-
-void adb_data_interrupt(int irq, void *arg, struct pt_regs *regs)
-{
- ndata++;
-}
-
-/*
- * The notorious ADB interrupt handler - does all of the protocol handling,
- * except for starting new send operations. Relies heavily on the ADB
- * controller sending and receiving data, thereby generating SR interrupts
- * for us. This means there has to be always activity on the ADB bus, otherwise
- * the whole process dies and has to be re-kicked by sending TALK requests ...
- * CUDA-based Macs seem to solve this with the autopoll option, for MacII-type
- * ADB the problem isn't solved yet (retransmit of the latest active TALK seems
- * a good choice; either on timeout or on a timer interrupt).
- *
- * The basic ADB state machine was left unchanged from the original MacII code
- * by Alan Cox, which was based on the CUDA driver for PowerMac.
- * The syntax of the ADB status lines seems to be totally different on MacII,
- * though. MacII uses the states Command -> Even -> Odd -> Even ->...-> Idle for
- * sending, and Idle -> Even -> Odd -> Even ->...-> Idle for receiving. Start
- * and end of a receive packet are signaled by asserting /IRQ on the interrupt
- * line. Timeouts are signaled by a sequence of 4 0xFF, with /IRQ asserted on
- * every other byte. SRQ is probably signaled by 3 or more 0xFF tacked on the
- * end of a packet. (Thanks to Guido Koerber for eavesdropping on the ADB
- * protocol with a logic analyzer!!)
- * CUDA seems to use /TIP -> /TIP | TACK -> /TIP -> /TIP | TACK ... -> TIP|TACK
- * for sending, and /TIP -> /TIP | TACK -> /TIP -> /TIP | TACK ... -> TIP for
- * receiving. No clue how timeouts are handled; SRQ seems to be sent as a
- * separate packet. Quite a few changes have been made outside the handshake
- * code, so I don't know if the CUDA code still behaves as before.
- *
- * Note: As of 21/10/97, the MacII ADB part works including timeout detection
- * and retransmit (Talk to the last active device). Cleanup of code and
- * testing of the CUDA functionality is required, though.
- * Note2: As of 13/12/97, CUDA support is definitely broken ...
- * Note3: As of 21/12/97, CUDA works on a P475. What was broken? The assumption
- * that Q700 and Q800 use CUDA :-(
- *
- * 27/01/98: IIsi driver implemented (thanks to Robert Thompson for the
- * initial bits). See adb_cuda_interrupts ...
- *
- * Next TODO: implementation of IIsi ADB protocol (maybe the USE_ORIG
- * conditionals can be a start?)
- */
-void adb_interrupt(int irq, void *arg, struct pt_regs *regs)
-{
- int x, adbdir;
- unsigned long flags;
- struct adb_request *req;
-
- last_status = status;
-
- /* prevent races due to SCSI enabling ints */
- save_flags(flags);
- cli();
-
- if (driver_running) {
- restore_flags(flags);
- return;
- }
-
- driver_running = 1;
-
-#ifdef USE_ORIG
- status = (~via_read(via1, vBufB) & (TIP|TREQ)) | (via_read(via1, vACR) & SR_OUT);
-#else
- if (macintosh_config->adb_type==MAC_ADB_CUDA)
- status = (~via_read(via1, vBufB) & (TIP|TREQ)) | (via_read(via1, vACR) & SR_OUT);
- else
- /* status bits (0x8->0x20) and direction (0x10 ??) CLASH !! */
- status = (via_read(via1, vBufB) & (ST_MASK|TREQ));
-#endif
- adbdir = (via_read(via1, vACR) & SR_OUT);
-#if (ADBDEBUG & ADBDEBUG_INT)
- if (console_loglevel == 10)
- printk("adb_interrupt: state=%d status=%x last=%x direction=%x\n",
- adb_state, status, last_status, adbdir);
-#endif
-
- switch (adb_state)
- {
- case idle:
- if(macintosh_config->adb_type==MAC_ADB_CUDA)
- {
- /* CUDA has sent us the first byte of data - unsolicited */
- if (status != TREQ)
- printk("cuda: state=idle, status=%x\n", status);
- x = via_read(via1, vSR);
- via_write(via1, vBufB, via_read(via1,vBufB)&~TIP);
- }
- else if(macintosh_config->adb_type==MAC_ADB_IISI)
- {
- udelay(150);
- /* set SR to IN (??? no byte received else) */
- via_write(via1, vACR,via_read(via1, vACR)&~SR_OUT);
- /* signal start of frame */
- via_write(via1, vBufB, via_read(via1, vBufB) | TIP);
- /* read first byte */
- x = via_read(via1, vSR);
- first_byte = x;
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk("adb_macIIsi : receiving unsol. packet: %x (%x %x) ",
- x, adb_state, status);
-#endif
- /* ACK adb chip */
- via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
- udelay(150);
- via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
- }
- else if(macintosh_config->adb_type==MAC_ADB_II)
- {
-#if (ADBDEBUG & ADBDEBUG_STATUS)
- if (status == TREQ && !adbdir)
- /* that's: not IRQ, idle, input -> weird */
- printk("adb_macII: idle, status=%x dir=%x\n",
- status, adbdir);
-#endif
- x = via_read(via1, vSR);
- first_byte = x;
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk("adb_macII: receiving unsol. packet: %x (%x %x) ",
- x, adb_state, status);
-#endif
- /* set ADB state = even for first data byte */
- via_write(via1, vBufB, (via_read(via1, vBufB)&~ST_MASK)|ST_EVEN);
- }
- adb_state = reading;
- reply_ptr = cuda_rbuf;
- reply_len = 0;
- reading_reply = 0;
- prefix_len = 0;
- if (macintosh_config->adb_type==MAC_ADB_II) {
- *reply_ptr++ = ADB_PACKET;
- *reply_ptr++ = first_byte;
- *reply_ptr++ = command_byte; /*first_byte;*/
- reply_len = 3;
- prefix_len = 3;
- }
- break;
-
- case awaiting_reply:
- if(macintosh_config->adb_type==MAC_ADB_CUDA)
- {
- /* CUDA has sent us the first byte of data of a reply */
- if (status != TREQ)
- printk("cuda: state=awaiting_reply, status=%x\n", status);
- x = via_read(via1, vSR);
- via_write(via1,vBufB,
- via_read(via1, vBufB)&~TIP);
- }
- else if(macintosh_config->adb_type==MAC_ADB_IISI)
- {
- /* set SR to IN */
- via_write(via1, vACR,via_read(via1, vACR)&~SR_OUT);
- /* signal start of frame */
- via_write(via1, vBufB, via_read(via1, vBufB) | TIP);
- /* read first byte */
- x = via_read(via1, vSR);
- first_byte = x;
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk("adb_macIIsi: reading reply: %x (%x %x) ",
- x, adb_state, status);
-#endif
-#if 0
- if( via_read(via1,vBufB) & TREQ)
- ending = 1;
- else
- ending = 0;
-#endif
- /* ACK adb chip */
- via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
- udelay(150);
- via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
- }
- else if(macintosh_config->adb_type==MAC_ADB_II)
- {
- /* handshake etc. for II ?? */
- x = via_read(via1, vSR);
- first_byte = x;
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk("adb_macII: reading reply: %x (%x %x) ",
- x, adb_state, status);
-#endif
- /* set ADB state = even for first data byte */
- via_write(via1, vBufB, (via_read(via1, vBufB)&~ST_MASK)|ST_EVEN);
- }
- adb_state = reading;
- reply_ptr = current_req->reply;
- reading_reply = 1;
- reply_len = 0;
- prefix_len = 0;
- if (macintosh_config->adb_type==MAC_ADB_II) {
- *reply_ptr++ = ADB_PACKET;
- *reply_ptr++ = first_byte;
- *reply_ptr++ = first_byte; /* should be command byte */
- reply_len = 3;
- prefix_len = 3;
- }
- break;
-
- case sent_first_byte:
-#if (ADBDEBUG & ADBDEBUG_WRITE)
- if (console_loglevel == 10)
- printk(" sending: %x (%x %x) ",
- current_req->data[1], adb_state, status);
-#endif
- if(macintosh_config->adb_type==MAC_ADB_CUDA)
- {
- if (status == TREQ + TIP + SR_OUT)
- {
- /* collision */
- via_write(via1, vACR,
- via_read(via1, vACR)&~SR_OUT);
- x = via_read(via1, vSR);
- via_write(via1, vBufB,
- via_read(via1,vBufB)|TIP|TACK);
- adb_state = idle;
- }
- else
- {
- /* assert status == TIP + SR_OUT */
- if (status != TIP + SR_OUT)
- printk("cuda: state=sent_first_byte status=%x\n", status);
- via_write(via1,vSR,current_req->data[1]);
- via_write(via1, vBufB,
- via_read(via1, vBufB)^TACK);
- data_index = 2;
- adb_state = sending;
- }
- }
- else if(macintosh_config->adb_type==MAC_ADB_IISI)
- {
- /* switch ACK off */
- via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
- if ( !(via_read(via1, vBufB) & TREQ) )
- {
- /* collision */
-#if (ADBDEBUG & ADBDEBUG_WRITE)
- if (console_loglevel == 10)
- printk("adb_macIIsi: send collison, aborting!\n");
-#endif
- /* set shift in */
- via_write(via1, vACR,
- via_read(via1, vACR)&~SR_OUT);
- /* clear SR int. */
- x = via_read(via1, vSR);
- /* set ADB state to 'idle' */
- via_write(via1, vBufB,
- via_read(via1,vBufB) & ~(TIP|TACK));
- adb_state = idle;
- }
- else
- {
- /* delay */
- udelay(ADB_DELAY);
- /* set the shift register to shift out and send a byte */
-#if 0
- via_write(via1, vACR, via_read(via1, vACR) | SR_OUT);
-#endif
- via_write(via1, vSR, current_req->data[1]);
- /* signal 'byte ready' */
- via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
- data_index=2;
- adb_state = sending;
- }
- }
- else if(macintosh_config->adb_type==MAC_ADB_II)
- {
- /* how to detect a collision here ?? */
- /* maybe we're already done (Talk, or Poll)? */
- if (data_index >= current_req->nbytes)
- {
- /* assert it's a Talk ?? */
- if ( (command_byte&0xc) != 0xc
- && console_loglevel == 10 )
- printk("ADB: single byte command, no Talk: %x!\n",
- command_byte);
-#if (ADBDEBUG & ADBDEBUG_WRITE)
- if (console_loglevel == 10)
- printk(" -> end (%d of %d) (%x %x)!\n",
- data_index, current_req->nbytes, adb_state, status);
-#endif
- current_req->sent = 1;
- if (current_req->reply_expected)
- {
-#if (ADBDEBUG & ADBDEBUG_WRITE)
- if (console_loglevel == 10)
- printk("ADB: reply expected on poll!\n");
-#endif
- adb_state = awaiting_reply;
- reading_reply = 0;
- } else {
-#if (ADBDEBUG & ADBDEBUG_WRITE)
- if (console_loglevel == 10)
- printk("ADB: no reply for poll, not calling done()!\n");
-#endif
- req = current_req;
- current_req = req->next;
-#if 0 /* XXX Not sure about that one ... probably better enabled */
- if (req->done)
- (*req->done)(req);
-#endif
- adb_state = idle;
- reading_reply = 0;
- }
- /* set to shift in */
- via_write(via1, vACR,
- via_read(via1, vACR) & ~SR_OUT);
- x=via_read(via1, vSR);
- /* set ADB state idle - might get SRQ */
- via_write(via1, vBufB,
- (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
- break;
- }
-#if (ADBDEBUG & ADBDEBUG_STATUS)
- if(!(status==(ST_CMD|TREQ) && adbdir == SR_OUT))
- printk("adb_macII: sent_first_byte, weird status=%x dir=%x\n",
- status, adbdir);
-#endif
- /* SR already set to shift out; send byte */
- via_write(via1, vSR, current_req->data[1]);
- /* set state to ST_EVEN (first byte was: ST_CMD) */
- via_write(via1, vBufB,
- (via_read(via1, vBufB)&~ST_MASK)|ST_EVEN);
- data_index=2;
- adb_state = sending;
- }
- break;
-
- case sending:
- req = current_req;
- if (data_index >= req->nbytes)
- {
-#if (ADBDEBUG & ADBDEBUG_WRITE)
- if (console_loglevel == 10)
- printk(" -> end (%d of %d) (%x %x)!\n",
- data_index-1, req->nbytes, adb_state, status);
-#endif
- /* end of packet */
- if(macintosh_config->adb_type==MAC_ADB_CUDA)
- {
- via_write(via1, vACR,
- via_read(via1, vACR)&~SR_OUT);
- x = via_read(via1, vSR);
- via_write(via1, vBufB,
- via_read(via1,vBufB)|TACK|TIP);
- }
- else if(macintosh_config->adb_type==MAC_ADB_IISI)
- {
- /* XXX maybe clear ACK here ??? */
- /* switch ACK off */
- via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
- /* delay */
- udelay(ADB_DELAY);
- /* set the shift register to shift in */
- via_write(via1, vACR, via_read(via1, vACR)|SR_OUT);
- /* clear SR int. */
- x = via_read(via1, vSR);
- /* set ADB state 'idle' (end of frame) */
- via_write(via1, vBufB,
- via_read(via1,vBufB) & ~(TACK|TIP));
- }
- else if(macintosh_config->adb_type==MAC_ADB_II)
- {
- /*
- * XXX Not sure: maybe only switch to
- * input mode on Talk ??
- */
- /* set to shift in */
- via_write(via1, vACR,
- via_read(via1, vACR) & ~SR_OUT);
- x=via_read(via1, vSR);
- /* set ADB state idle - might get SRQ */
- via_write(via1, vBufB,
- (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
- }
- req->sent = 1;
- if (req->reply_expected)
- {
- /*
- * maybe fake a reply here on Listen ??
- * Otherwise, a Listen hangs on success
- */
- if ( macintosh_config->adb_type==MAC_ADB_II
- && ((req->data[0]&0xc) == 0xc) )
- adb_state = awaiting_reply;
- else if ( macintosh_config->adb_type != MAC_ADB_II
- && ( req->data[0] == 0x0)
- && ((req->data[1]&0xc) == 0xc) )
- adb_state = awaiting_reply;
- else {
- /*
- * Reply expected, but none
- * possible -> fake reply.
- * Problem: sending next command
- * should probably be done
- * without setting bus to 'idle'!
- * (except if no more commands)
- */
-#if (ADBDEBUG & ADBDEBUG_PROT)
- printk("ADB: reply expected on Listen, faking reply\n");
-#endif
- /* make it look weird */
- /* XXX: return reply_len -1? */
- /* XXX: fake ADB header? */
- req->reply[0] = req->reply[1] = req->reply[2] = 0xFF;
- req->reply_len = 3;
- req->got_reply = 1;
- current_req = req->next;
- if (req->done)
- (*req->done)(req);
- /*
- * ready with this one, run
- * next command or repeat last
- * Talk (=idle on II)
- */
- /* set state to idle !! */
- adb_state = idle;
- if (current_req || retry_req)
- adb_start();
- }
- }
- else
- {
- current_req = req->next;
- if (req->done)
- (*req->done)(req);
- /* not sure about this */
- /*
- * MS: Must set idle, no new request
- * started else !
- */
- adb_state = idle;
- /*
- * requires setting ADB state to idle,
- * maybe read a byte ! (done above)
- */
- if (current_req || retry_req)
- adb_start();
- }
- }
- else
- {
-#if (ADBDEBUG & ADBDEBUG_WRITE)
- if (console_loglevel == 10)
- printk(" %x (%x %x) ",
- req->data[data_index], adb_state, status);
-#endif
- if(macintosh_config->adb_type==MAC_ADB_CUDA)
- {
- via_write(via1, vSR, req->data[data_index++]);
- via_write(via1, vBufB,
- via_read(via1, vBufB)^TACK);
- }
- else if(macintosh_config->adb_type==MAC_ADB_IISI)
- {
- /* switch ACK off */
- via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
- /* delay */
- udelay(ADB_DELAY);
- /* XXX: need to check for collision?? */
- /* set the shift register to shift out and send a byte */
-#if 0
- via_write(via1, vACR, via_read(via1, vACR)|SR_OUT);
-#endif
- via_write(via1, vSR, req->data[data_index++]);
- /* signal 'byte ready' */
- via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
- }
- else if(macintosh_config->adb_type==MAC_ADB_II)
- {
- via_write(via1, vSR, req->data[data_index++]);
- /* invert state bits, toggle ODD/EVEN */
- x = via_read(via1, vBufB);
- via_write(via1, vBufB,
- (x&~ST_MASK)|~(x&ST_MASK));
- }
- }
- break;
-
- case reading:
-
- /* timeout / SRQ handling for II hw */
-#ifdef POLL_ON_TIMEOUT
- if((reply_len-prefix_len)==3 && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0)
-#else
- if( (first_byte == 0xFF && (reply_len-prefix_len)==2
- && memcmp(reply_ptr-2,"\xFF\xFF",2)==0) ||
- ((reply_len-prefix_len)==3
- && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0))
-#endif
- {
- /*
- * possible timeout (in fact, most probably a
- * timeout, since SRQ can't be signaled without
- * transfer on the bus).
- * The last three bytes seen were FF, together
- * with the starting byte (in case we started
- * on 'idle' or 'awaiting_reply') this probably
- * makes four. So this is mostl likely #5!
- * The timeout signal is a pattern 1 0 1 0 0..
- * on /INT, meaning we missed it :-(
- */
- x = via_read(via1, vSR);
- if (x != 0xFF)
- printk("ADB: mistaken timeout/SRQ!\n");
-
- /*
- * ADB status bits: either even or odd.
- * adb_state: need to set 'idle' here.
- * Maybe saner: set 'need_poll' or
- * 'need_resend' here, fall through to
- * read_done ??
- */
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk(" -> read aborted: %x (%x %x)!\n",
- x, adb_state, status);
-#endif
-
-#if 0 /* XXX leave status unchanged!! - need to check this again! */
- /* XXX Only touch status on II !!! */
- /* set ADB state to idle (required by adb_start()) */
- via_write(via1, vBufB,
- (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
-#endif
-
- /*
- * What if the timeout happens on reading a
- * reply ?? Assemble error reply and call
- * current_request->done()? Keep request
- * on queue?
- */
-
- /* prevent 'busy' in adb_start() */
- need_poll = 1;
-
- /*
- * Timeout: /IRQ alternates high/low during
- * 4 'FF' bytes (1 0 1 0 0...)
- * We're on byte 5, so we need one
- * more backlog here (TBI) ....
- */
- if ((status&TREQ) != (last_status&TREQ)) {
-#if (ADBDEBUG & ADBDEBUG_SRQ)
- if (console_loglevel == 10)
- printk("ADB: reply timeout, resending!\n");
-#endif
- /*
- * first byte received should be the
- * command byte timing out !!
- */
- if (first_byte != 0xff)
- command_byte = first_byte;
-
- /*
- * compute target for retransmit: if
- * last_active is set, use that one,
- * else use command_byte
- */
- if (last_active == -1)
- last_active = (command_byte&0xf0)>>4;
- adb_state = idle;
- /* resend if TALK, don't poll! */
- if (current_req)
- adb_start();
- else
- /*
- * XXX: need to count the timeouts ??
- * restart last active TALK ??
- * If no current_req, reuse old one!
- */
- adb_retransmit(last_active);
-
- } else {
- /*
- * SRQ: NetBSD suggests /IRQ is asserted!?
- */
- if (status&TREQ)
- printk("ADB: SRQ signature w/o /INT!\n");
-#if (ADBDEBUG & ADBDEBUG_SRQ)
- if (console_loglevel == 10)
- printk("ADB: empty SRQ packet!\n");
-#endif
- /* Terminate the SRQ packet and poll */
- adb_state = idle;
- adb_queue_poll();
- }
- /*
- * Leave ADB status lines unchanged (means /IRQ
- * will still be low when entering adb_start!)
- */
- break;
- }
- /* end timeout / SRQ handling for II hw. */
- if((reply_len-prefix_len)>3 && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0)
- {
- /* SRQ tacked on data packet */
- /* Check /IRQ here ?? */
-#if (ADBDEBUG & ADBDEBUG_SRQ)
- if (console_loglevel == 10)
- printk("\nADB: Packet with SRQ!\n");
-#endif
- /* Terminate the packet (SRQ never ends) */
- x = via_read(via1, vSR);
- adb_state = read_done;
- reply_len -= 3;
- reply_ptr -= 3;
- need_poll = 1;
- /* need to continue; next byte not seen else */
- /*
- * XXX: not at all sure here; maybe need to
- * send away the reply and poll immediately?
- */
- } else {
- /* Sanity check */
- if(reply_len>15)
- reply_len=0;
- /* read byte */
- *reply_ptr = via_read(via1, vSR);
- x = *reply_ptr;
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk(" %x (%x %x) ",
- *reply_ptr, adb_state, status);
-#endif
- reply_ptr++;
- reply_len++;
- }
- /* The usual handshake ... */
- if (macintosh_config->adb_type==MAC_ADB_CUDA)
- {
- if (status == TIP)
- {
- /* that's all folks */
- via_write(via1, vBufB,
- via_read(via1, vBufB)|TACK|TIP);
- adb_state = read_done;
- }
- else
- {
- /* assert status == TIP | TREQ */
- if (status != TIP + TREQ)
- printk("cuda: state=reading status=%x\n", status);
- via_write(via1, vBufB,
- via_read(via1, vBufB)^TACK);
- }
- }
- else if (macintosh_config->adb_type==MAC_ADB_IISI)
- {
- /* ACK adb chip (maybe check for end first?) */
- via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
- udelay(150);
- via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
- /* end of frame?? */
- if (status & TREQ)
- {
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk("adb_IIsi: end of frame!\n");
-#endif
- /* that's all folks */
- via_write(via1, vBufB,
- via_read(via1, vBufB) & ~(TACK|TIP));
- adb_state = read_done;
- /* maybe process read_done here?? Handshake anyway?? */
- }
- }
- else if (macintosh_config->adb_type==MAC_ADB_II)
- {
- /*
- * NetBSD hints that the next to last byte
- * is sent with IRQ !!
- * Guido found out it's the last one (0x0),
- * but IRQ should be asserted already.
- * Problem with timeout detection: First
- * transition to /IRQ might be second
- * byte of timeout packet!
- * Timeouts are signaled by 4x FF.
- */
- if(!(status&TREQ) && x == 0x00) /* != 0xFF */
- {
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk(" -> read done!\n");
-#endif
-#if 0 /* XXX: we take one more byte (why?), so handshake! */
- /* set ADB state to idle */
- via_write(via1, vBufB,
- (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
-#else
- /* invert state bits, toggle ODD/EVEN */
- x = via_read(via1, vBufB);
- via_write(via1, vBufB,
- (x&~ST_MASK)|~(x&ST_MASK));
-#endif
- /* adjust packet length */
- reply_len--;
- reply_ptr--;
- adb_state = read_done;
- }
- else
- {
-#if (ADBDEBUG & ADBDEBUG_STATUS)
- if(status!=TIP+TREQ)
- printk("macII_adb: state=reading status=%x\n", status);
-#endif
- /* not caught: ST_CMD */
- /* required for re-entry 'reading'! */
- if ((status&ST_MASK) == ST_IDLE) {
- /* (in)sanity check - set even */
- via_write(via1, vBufB,
- (via_read(via1, vBufB)&~ST_MASK)|ST_EVEN);
- } else {
- /* invert state bits, toggle ODD/EVEN */
- x = via_read(via1, vBufB);
- via_write(via1, vBufB,
- (x&~ST_MASK)|~(x&ST_MASK));
- }
- }
- }
- break;
-
- case read_done:
- x = via_read(via1, vSR);
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk("ADB: read done: %x (%x %x)!\n",
- x, adb_state, status);
-#endif
- if (reading_reply)
- {
- req = current_req;
- req->reply_len = reply_ptr - req->reply;
- req->got_reply = 1;
- current_req = req->next;
- if (req->done)
- (*req->done)(req);
- }
- else
- {
- adb_input(cuda_rbuf, reply_ptr - cuda_rbuf, regs);
- }
-
- /*
- * remember this device ID; it's the latest we got a
- * reply from!
- */
- last_reply = command_byte;
- last_active = (command_byte&0xf0)>>4;
-
- /*
- * Assert status = ST_IDLE ??
- */
- /*
- * SRQ seen before, initiate poll now
- */
- if (need_poll) {
-#if (ADBDEBUG & ADBDEBUG_POLL)
- if (console_loglevel == 10)
- printk("ADB: initiate poll!\n");
-#endif
- adb_state = idle;
- /*
- * set ADB status bits?? (unchanged above!)
- */
- adb_queue_poll();
- need_poll = 0;
- /* hope this is ok; queue_poll runs adb_start */
- break;
- }
-
- /*
- * /IRQ seen, so the ADB controller has data for us
- */
- if (!(status&TREQ))
- {
- /* set ADB state to idle */
- via_write(via1, vBufB,
- (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
-
- adb_state = reading;
- reply_ptr = cuda_rbuf;
- reply_len = 0;
- prefix_len = 0;
- if (macintosh_config->adb_type==MAC_ADB_II) {
- *reply_ptr++ = ADB_PACKET;
- *reply_ptr++ = command_byte;
- reply_len = 2;
- prefix_len = 2;
- }
- reading_reply = 0;
- }
- else
- {
- /*
- * no IRQ, send next packet or wait
- */
- adb_state = idle;
- if (current_req)
- adb_start();
- else
- adb_retransmit(last_active);
- }
- break;
-
- default:
-#if (ADBDEBUG & ADBDEBUG_STATE)
- printk("adb_interrupt: unknown adb_state %d?\n", adb_state);
-#endif
- }
- /* reset mutex and interrupts */
- driver_running = 0;
- restore_flags(flags);
-}
-
-/*
- * Restart of CUDA support: please modify this interrupt handler while
- * working at the Quadra etc. ADB driver. We can try to merge them later, or
- * remove the CUDA stuff from the MacII handler
- *
- * MSch 27/01/98: Implemented IIsi driver based on initial code by Robert
- * Thompson and hints from the NetBSD driver. CUDA and IIsi seem more closely
- * related than to the MacII code, so merging all three might be a bad
- * idea.
- */
-
-void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
-{
- int x, status;
- struct adb_request *req;
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- if(macintosh_config->adb_type==MAC_ADB_CUDA)
- status = (~via_read(via1, vBufB) & (TIP|TREQ)) | (via_read(via1, vACR) & SR_OUT);
- else
- status = via_read(via1, vBufB) & (TIP|TREQ);
-
-#if (ADBDEBUG & ADBDEBUG_INT)
- if (console_loglevel == 10)
- printk("adb_interrupt: state=%d status=%x\n", adb_state, status);
-#endif
-
- switch (adb_state)
- {
- case idle:
- first_byte = 0;
- if(macintosh_config->adb_type==MAC_ADB_CUDA)
- {
-#if (ADBDEBUG & ADBDEBUG_STATUS)
- /* CUDA has sent us the first byte of data - unsolicited */
- if (status != TREQ)
- printk("cuda: state=idle, status=%x want=%x\n",
- status, TREQ);
-#endif
- x = via_read(via1, vSR);
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk("adb_cuda: receiving unsol. packet: %x (%x %x) ",
- x, adb_state, status);
-#endif
- via_write(via1, vBufB, via_read(via1,vBufB)&~TIP);
- }
- else if(macintosh_config->adb_type==MAC_ADB_IISI)
- {
- udelay(150);
- /* set SR to IN */
- via_write(via1, vACR,via_read(via1, vACR)&~SR_OUT);
- /* signal start of frame */
- via_write(via1, vBufB, via_read(via1, vBufB) | TIP);
- /* read first byte */
- x = via_read(via1, vSR);
- first_byte = x;
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk("adb_IIsi : receiving unsol. packet: %x (%x %x) ",
- x, adb_state, status);
-#endif
- /* ACK adb chip */
- via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
- udelay(150);
- via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
- }
- else if(macintosh_config->adb_type==MAC_ADB_II)
- {
- if (status != TREQ)
- printk("adb_macII: state=idle status=%x want=%x\n",
- status, TREQ);
- x = via_read(via1, vSR);
- via_write(via1, vBufB, via_read(via1, vBufB)&~(TIP|TACK));
- }
- adb_state = reading;
- reply_ptr = cuda_rbuf;
- reply_len = 0;
- reading_reply = 0;
- break;
-
- case awaiting_reply:
- if(macintosh_config->adb_type==MAC_ADB_CUDA)
- {
- /* CUDA has sent us the first byte of data of a reply */
-#if (ADBDEBUG & ADBDEBUG_STATUS)
- if (status != TREQ)
- printk("cuda: state=awaiting_reply, status=%x want=%x\n",
- status, TREQ);
-#endif
- x = via_read(via1, vSR);
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk("adb_cuda: reading reply: %x (%x %x) ",
- x, adb_state, status);
-#endif
- via_write(via1,vBufB,
- via_read(via1, vBufB)&~TIP);
- }
- else if(macintosh_config->adb_type==MAC_ADB_IISI)
- {
- /* udelay(150);*/
- /* set SR to IN */
- via_write(via1, vACR,via_read(via1, vACR)&~SR_OUT);
- /* signal start of frame */
- via_write(via1, vBufB, via_read(via1, vBufB) | TIP);
- /* read first byte */
- x = via_read(via1, vSR);
- first_byte = x;
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk("adb_IIsi: reading reply: %x (%x %x) ",
- x, adb_state, status);
-#endif
-#if 0
- if( via_read(via1,vBufB) & TREQ)
- ending = 1;
- else
- ending = 0;
-#endif
- /* ACK adb chip */
- via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
- udelay(150);
- via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
- }
- adb_state = reading;
- reply_ptr = current_req->reply;
- reading_reply = 1;
- reply_len = 0;
- break;
-
- case sent_first_byte:
- if(macintosh_config->adb_type==MAC_ADB_CUDA)
- {
-#if (ADBDEBUG & ADBDEBUG_WRITE)
- if (console_loglevel == 10)
- printk(" sending: %x (%x %x) ",
- current_req->data[1], adb_state, status);
-#endif
- if (status == TREQ + TIP + SR_OUT)
- {
- /* collision */
- if (console_loglevel == 10)
- printk("adb_cuda: send collision!\n");
- via_write(via1, vACR,
- via_read(via1, vACR)&~SR_OUT);
- x = via_read(via1, vSR);
- via_write(via1, vBufB,
- via_read(via1,vBufB)|TIP|TACK);
- adb_state = idle;
- }
- else
- {
- /* assert status == TIP + SR_OUT */
-#if (ADBDEBUG & ADBDEBUG_STATUS)
- if (status != TIP + SR_OUT)
- printk("adb_cuda: state=sent_first_byte status=%x want=%x\n",
- status, TIP + SR_OUT);
-#endif
- via_write(via1,vSR,current_req->data[1]);
- via_write(via1, vBufB,
- via_read(via1, vBufB)^TACK);
- data_index = 2;
- adb_state = sending;
- }
- }
- else if(macintosh_config->adb_type==MAC_ADB_IISI)
- {
- /* switch ACK off */
- via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
- if ( !(via_read(via1, vBufB) & TREQ) )
- {
- /* collision */
-#if (ADBDEBUG & ADBDEBUG_WRITE)
- if (console_loglevel == 10)
- printk("adb_macIIsi: send collison, aborting!\n");
-#endif
- /* set shift in */
- via_write(via1, vACR,
- via_read(via1, vACR)&~SR_OUT);
- /* clear SR int. */
- x = via_read(via1, vSR);
- /* set ADB state to 'idle' */
- via_write(via1, vBufB,
- via_read(via1,vBufB) & ~(TIP|TACK));
- adb_state = idle;
- }
- else
- {
- /* delay */
- udelay(ADB_DELAY);
- /* set the shift register to shift out and send a byte */
-#if 0
- via_write(via1, vACR, via_read(via1, vACR) | SR_OUT);
-#endif
- via_write(via1, vSR, current_req->data[1]);
- /* signal 'byte ready' */
- via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
- data_index=2;
- adb_state = sending;
- }
- }
- else if(macintosh_config->adb_type==MAC_ADB_II)
- {
- if(status!=TIP+SR_OUT)
- printk("adb_macII: state=send_first_byte status=%x want=%x\n",
- status, TIP+SR_OUT);
- via_write(via1, vSR, current_req->data[1]);
- via_write(via1, vBufB,
- via_read(via1, vBufB)^TACK);
- data_index=2;
- adb_state = sending;
- }
- break;
-
- case sending:
- req = current_req;
- if (data_index >= req->nbytes)
- {
-#if (ADBDEBUG & ADBDEBUG_WRITE)
- if (console_loglevel == 10)
- printk(" -> end (%d of %d) (%x %x)!\n",
- data_index-1, req->nbytes, adb_state, status);
-#endif
- if(macintosh_config->adb_type==MAC_ADB_CUDA)
- {
- via_write(via1, vACR,
- via_read(via1, vACR)&~SR_OUT);
- x = via_read(via1, vSR);
- via_write(via1, vBufB,
- via_read(via1,vBufB)|TACK|TIP);
- }
- else if(macintosh_config->adb_type==MAC_ADB_IISI)
- {
- /* XXX maybe clear ACK here ??? */
- /* switch ACK off */
- via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
- /* delay */
- udelay(ADB_DELAY);
- /* set the shift register to shift in */
- via_write(via1, vACR, via_read(via1, vACR)|SR_OUT);
- /* clear SR int. */
- x = via_read(via1, vSR);
- /* set ADB state 'idle' (end of frame) */
- via_write(via1, vBufB,
- via_read(via1,vBufB) & ~(TACK|TIP));
- }
- else if(macintosh_config->adb_type==MAC_ADB_II)
- {
- via_write(via1, vACR,
- via_read(via1, vACR) & ~SR_OUT);
- x=via_read(via1, vSR);
- via_write(via1, vBufB,
- via_read(via1, vBufB)|TACK|TIP);
- }
- req->sent = 1;
- if (req->reply_expected)
- {
- /*
- * maybe fake a reply here on Listen ??
- * Otherwise, a Listen hangs on success
- * CUDA+IIsi: only ADB Talk considered
- * RTC/PRAM read (0x1 0x3) to follow.
- */
- if ( (req->data[0] == 0x0) && ((req->data[1]&0xc) == 0xc) )
- adb_state = awaiting_reply;
- else {
- /*
- * Reply expected, but none
- * possible -> fake reply.
- */
-#if (ADBDEBUG & ADBDEBUG_PROT)
- printk("ADB: reply expected on Listen, faking reply\n");
-#endif
- /* make it look weird */
- /* XXX: return reply_len -1? */
- /* XXX: fake ADB header? */
- req->reply[0] = req->reply[1] = req->reply[2] = 0xFF;
- req->reply_len = 3;
- req->got_reply = 1;
- current_req = req->next;
- if (req->done)
- (*req->done)(req);
- /*
- * ready with this one, run
- * next command !
- */
- /* set state to idle !! */
- adb_state = idle;
- if (current_req || retry_req)
- adb_start();
- }
- }
- else
- {
- current_req = req->next;
- if (req->done)
- (*req->done)(req);
- /* not sure about this */
- adb_state = idle;
- adb_start();
- }
- }
- else
- {
-#if (ADBDEBUG & ADBDEBUG_WRITE)
- if (console_loglevel == 10)
- printk(" %x (%x %x) ",
- req->data[data_index], adb_state, status);
-#endif
- if(macintosh_config->adb_type==MAC_ADB_CUDA)
- {
- via_write(via1, vSR, req->data[data_index++]);
- via_write(via1, vBufB,
- via_read(via1, vBufB)^TACK);
- }
- else if(macintosh_config->adb_type==MAC_ADB_IISI)
- {
- /* switch ACK off */
- via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
- /* delay */
- udelay(ADB_DELAY);
- /* XXX: need to check for collision?? */
- /* set the shift register to shift out and send a byte */
-#if 0
- via_write(via1, vACR, via_read(via1, vACR)|SR_OUT);
-#endif
- via_write(via1, vSR, req->data[data_index++]);
- /* signal 'byte ready' */
- via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
- }
- else if(macintosh_config->adb_type==MAC_ADB_II)
- {
- via_write(via1, vSR, req->data[data_index++]);
- via_write(via1, vBufB,
- via_read(via1, vBufB)^TACK);
- }
- }
- break;
-
- case reading:
- if(reply_len==3 && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0)
- {
- /* Terminate the SRQ packet */
-#if (ADBDEBUG & ADBDEBUG_SRQ)
- if (console_loglevel == 10)
- printk("adb: Got an SRQ\n");
-#endif
- adb_state = idle;
- adb_queue_poll();
- break;
- }
- /* Sanity check - botched in orig. code! */
- if(reply_len>15) {
- printk("adb_cuda: reply buffer overrun!\n");
- /* wrap buffer */
- reply_len=0;
- if (reading_reply)
- reply_ptr = current_req->reply;
- else
- reply_ptr = cuda_rbuf;
- }
- *reply_ptr = via_read(via1, vSR);
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk(" %x (%x %x) ",
- *reply_ptr, adb_state, status);
-#endif
- reply_ptr++;
- reply_len++;
- if(macintosh_config->adb_type==MAC_ADB_CUDA)
- {
- if (status == TIP)
- {
- /* that's all folks */
- via_write(via1, vBufB,
- via_read(via1, vBufB)|TACK|TIP);
- adb_state = read_done;
- }
- else
- {
- /* assert status == TIP | TREQ */
-#if (ADBDEBUG & ADBDEBUG_STATUS)
- if (status != TIP + TREQ)
- printk("cuda: state=reading status=%x want=%x\n",
- status, TIP + TREQ);
-#endif
- via_write(via1, vBufB,
- via_read(via1, vBufB)^TACK);
- }
- }
- else if (macintosh_config->adb_type==MAC_ADB_IISI)
- {
- /* ACK adb chip (maybe check for end first?) */
- via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
- udelay(150);
- via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
- /* end of frame?? */
- if (status & TREQ)
- {
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk("adb_IIsi: end of frame!\n");
-#endif
- /* that's all folks */
- via_write(via1, vBufB,
- via_read(via1, vBufB) & ~(TACK|TIP));
- adb_state = read_done;
- /* XXX maybe process read_done here??
- Handshake anyway?? */
- }
- }
- if(macintosh_config->adb_type==MAC_ADB_II)
- {
- if( status == TIP)
- {
- via_write(via1, vBufB,
- via_read(via1, vBufB)|TACK|TIP);
- adb_state = read_done;
- }
- else
- {
-#if (ADBDEBUG & ADBDEBUG_STATUS)
- if(status!=TIP+TREQ)
- printk("macII_adb: state=reading status=%x\n", status);
-#endif
- via_write(via1, vBufB,
- via_read(via1, vBufB)^TACK);
- }
- }
- /* fall through for IIsi on end of frame */
- if (macintosh_config->adb_type != MAC_ADB_IISI
- || adb_state != read_done)
- break;
-
- case read_done:
- x = via_read(via1, vSR);
-#if (ADBDEBUG & ADBDEBUG_READ)
- if (console_loglevel == 10)
- printk("adb: read done: %x (%x %x)!\n",
- x, adb_state, status);
-#endif
- if (reading_reply)
- {
- req = current_req;
- req->reply_len = reply_ptr - req->reply;
- req->got_reply = 1;
- current_req = req->next;
- if (req->done)
- (*req->done)(req);
- }
- else
- {
- adb_input(cuda_rbuf, reply_ptr - cuda_rbuf, regs);
- }
-
- if (macintosh_config->adb_type==MAC_ADB_CUDA
- && status & TREQ)
- {
- via_write(via1, vBufB,
- via_read(via1, vBufB)&~TIP);
- adb_state = reading;
- reply_ptr = cuda_rbuf;
- reading_reply = 0;
- }
- else if (macintosh_config->adb_type==MAC_ADB_IISI
- && !(status & TREQ))
- {
- udelay(150);
- via_write(via1, vBufB,
- via_read(via1, vBufB) | TIP);
- adb_state = reading;
- reply_ptr = cuda_rbuf;
- reading_reply = 0;
- }
- else
- {
- adb_state = idle;
- adb_start();
- }
- break;
-
- default:
- printk("adb_cuda_interrupt: unknown adb_state %d?\n", adb_state);
- }
-
- restore_flags(flags);
-
-}
-
-/*
- * The 'reply delivery' routine; determines which device sent the
- * request and calls the appropriate handler.
- * Reply data are expected in CUDA format (again, argh...) so we need
- * to fake this in the interrupt handler for MacII.
- * Only one handler per device ID is currently possible.
- * XXX: is the ID field here representing the default or real ID?
- */
-static void adb_input(unsigned char *buf, int nb, struct pt_regs *regs)
-{
- int i, id;
-
- switch (buf[0])
- {
- case ADB_PACKET:
- /* what's in buf[1] ?? */
- id = buf[2] >> 4;
-#if 0
- xmon_printf("adb packet: ");
- for (i = 0; i < nb; ++i)
- xmon_printf(" %x", buf[i]);
- xmon_printf(", id = %d\n", id);
-#endif
-#if (ADBDEBUG & ADBDEBUG_INPUT)
- if (console_loglevel == 10) {
- printk("adb_input: adb packet ");
- for (i = 0; i < nb; ++i)
- printk(" %x", buf[i]);
- printk(", id = %d\n", id);
- }
-#endif
- if (adb_handler[id].handler != 0)
- {
- (*adb_handler[id].handler)(buf, nb, regs);
- }
- break;
-
- default:
-#if (ADBDEBUG & ADBDEBUG_INPUT)
- if (console_loglevel == 10) {
- printk("adb_input: data from via (%d bytes):", nb);
- for (i = 0; i < nb; ++i)
- printk(" %.2x", buf[i]);
- printk("\n");
- }
-#endif
- }
-}
-
-/* Ultimately this should return the number of devices with
- the given default id. */
-
-int adb_register(int default_id,
- void (*handler)(unsigned char *, int, struct pt_regs *))
-{
- if (adb_handler[default_id].handler != 0)
- panic("Two handlers for ADB device %d\n", default_id);
- adb_handler[default_id].handler = handler;
- return 1;
-}
-
-/*
- * /dev/adb device driver.
- */
-
-#define ADB_MAJOR 56 /* major number for /dev/adb */
-
-#define ADB_MAX_MINOR 64 /* range of ADB minors */
-#define ADB_TYPE_SHIFT 4 /* # bits for device ID/type in subdevices */
-
-#define ADB_TYPE_RAW 0 /* raw device; unbuffered */
-#define ADB_TYPE_BUFF 1 /* raw device; buffered */
-#define ADB_TYPE_COOKED 2 /* 'cooked' device */
-
-
-extern void adbdev_init(void);
-
-struct adbdev_state {
- struct adb_request req;
-};
-
-static DECLARE_WAIT_QUEUE_HEAD(adb_wait);
-
-static int adb_wait_reply(struct adbdev_state *state, struct file *file)
-{
- int ret = 0;
- DECLARE_WAITQUEUE(wait,current);
-
- __set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue(&adb_wait, &wait);
-
- while (!state->req.got_reply) {
- if (file->f_flags & O_NONBLOCK) {
- ret = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
- schedule();
- }
-
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(&adb_wait, &wait);
-
- return ret;
-}
-
-static void adb_write_done(struct adb_request *req)
-{
- if (!req->got_reply) {
- req->reply_len = 0;
- req->got_reply = 1;
- }
- wake_up_interruptible(&adb_wait);
-}
-
-struct file_operations *adb_raw[16];
-struct file_operations *adb_buffered[16];
-struct file_operations *adb_cooked[16];
-
-static int adb_open(struct inode *inode, struct file *file)
-{
- int adb_type, adb_subtype;
- struct adbdev_state *state;
-
- if (MINOR(inode->i_rdev) > ADB_MAX_MINOR)
- return -ENXIO;
-
- switch (MINOR(inode->i_rdev) >> ADB_TYPE_SHIFT) {
- case ADB_TYPE_RAW:
- /* see code below */
- break;
- case ADB_TYPE_BUFF:
- /* TBI */
- return -ENXIO;
- case ADB_TYPE_COOKED:
- /* subtypes such as kbd, mouse, ... */
- adb_subtype = MINOR(inode->i_rdev) & ~ADB_TYPE_SHIFT;
- if ((file->f_op = adb_cooked[adb_subtype]))
- return file->f_op->open(inode,file);
- else
- return -ENODEV;
- }
-
- state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL);
- if (state == 0)
- return -ENOMEM;
- file->private_data = state;
- state->req.reply_expected = 0;
- return 0;
-}
-
-static void adb_release(struct inode *inode, struct file *file)
-{
- struct adbdev_state *state = file->private_data;
-
- if (state) {
- file->private_data = NULL;
- if (state->req.reply_expected && !state->req.got_reply)
- if (adb_wait_reply(state, file))
- return;
- kfree(state);
- }
- return;
-}
-
-static int adb_lseek(struct inode *inode, struct file *file,
- off_t offset, int origin)
-{
- return -ESPIPE;
-}
-
-static int adb_read(struct inode *inode, struct file *file,
- char *buf, int count)
-{
- int ret;
- struct adbdev_state *state = file->private_data;
-
- if (count < 2)
- return -EINVAL;
- if (count > sizeof(state->req.reply))
- count = sizeof(state->req.reply);
- ret = verify_area(VERIFY_WRITE, buf, count);
- if (ret)
- return ret;
-
- if (!state->req.reply_expected)
- return 0;
-
- ret = adb_wait_reply(state, file);
- if (ret)
- return ret;
-
- state->req.reply_expected = 0;
- ret = state->req.reply_len;
- copy_to_user(buf, state->req.reply, ret);
-
- return ret;
-}
-
-static int adb_write(struct inode *inode, struct file *file,
- const char *buf, int count)
-{
- int ret, i;
- struct adbdev_state *state = file->private_data;
-
- if (count < 2 || count > sizeof(state->req.data))
- return -EINVAL;
- ret = verify_area(VERIFY_READ, buf, count);
- if (ret)
- return ret;
-
- if (state->req.reply_expected && !state->req.got_reply) {
- /* A previous request is still being processed.
- Wait for it to finish. */
- ret = adb_wait_reply(state, file);
- if (ret)
- return ret;
- }
-
- state->req.nbytes = count;
- state->req.done = adb_write_done;
- state->req.got_reply = 0;
- copy_from_user(state->req.data, buf, count);
-#if 0
- switch (adb_hardware) {
- case ADB_NONE:
- return -ENXIO;
- case ADB_VIACUDA:
- state->req.reply_expected = 1;
- cuda_send_request(&state->req);
- break;
- default:
-#endif
- if (state->req.data[0] != ADB_PACKET)
- return -EINVAL;
- for (i = 1; i < state->req.nbytes; ++i)
- state->req.data[i] = state->req.data[i+1];
- state->req.reply_expected =
- ((state->req.data[0] & 0xc) == 0xc);
- adb_send_request(&state->req);
-#if 0
- break;
- }
-#endif
-
- return count;
-}
-
-static struct file_operations adb_fops = {
- llseek: adb_lseek,
- read: adb_read,
- write: adb_write,
- open: adb_open,
- release: adb_release,
-};
-
-int adbdev_register(int subtype, struct file_operations *fops)
-{
- if (subtype < 0 || subtype > 15)
- return -EINVAL;
- if (adb_cooked[subtype])
- return -EBUSY;
- adb_cooked[subtype] = fops;
- return 0;
-}
-
-int adbdev_unregister(int subtype)
-{
- if (subtype < 0 || subtype > 15)
- return -EINVAL;
- if (!adb_cooked[subtype])
- return -ENODEV;
- adb_cooked[subtype] = NULL;
- return 0;
-}
-
-void adbdev_init()
-{
- if (register_chrdev(ADB_MAJOR, "adb", &adb_fops))
- printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR);
-}
-
-
-#if 0 /* old ADB device */
-
-/*
- * Here are the file operations we export for /dev/adb.
- */
-
-#define ADB_MINOR 140 /* /dev/adb is c 10 140 */
-
-extern void adbdev_inits(void);
-
-struct adbdev_state {
- struct adb_request req;
-};
-
-static DECLARE_WAIT_QUEUE_HEAD(adb_wait);
-
-static int adb_wait_reply(struct adbdev_state *state, struct file *file)
-{
- int ret = 0;
- DECLARE_WAITQUEUE(wait, current);
-
-#if (ADBDEBUG & ADBDEBUG_DEVICE)
- printk("ADB request: wait_reply (blocking ... \n");
-#endif
-
- __set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue(&adb_wait, &wait);
-
- while (!state->req.got_reply) {
- if (file->f_flags & O_NONBLOCK) {
- ret = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
- schedule();
- }
-
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(&adb_wait, &wait);
-
- return ret;
-}
-
-static void adb_write_done(struct adb_request *req)
-{
- if (!req->got_reply) {
- req->reply_len = 0;
- req->got_reply = 1;
- }
- wake_up_interruptible(&adb_wait);
-}
-
-static int adb_open(struct inode *inode, struct file *file)
-{
- struct adbdev_state *state;
-
- state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL);
- if (state == 0)
- return -ENOMEM;
- file->private_data = state;
- state->req.reply_expected = 0;
- return 0;
-}
-
-static void adb_release(struct inode *inode, struct file *file)
-{
- struct adbdev_state *state = file->private_data;
-
- if (state) {
- file->private_data = NULL;
- if (state->req.reply_expected && !state->req.got_reply)
- if (adb_wait_reply(state, file))
- return;
- kfree(state);
- }
- return;
-}
-
-static int adb_lseek(struct inode *inode, struct file *file,
- off_t offset, int origin)
-{
- return -ESPIPE;
-}
-
-static int adb_read(struct inode *inode, struct file *file,
- char *buf, int count)
-{
- int ret;
- struct adbdev_state *state = file->private_data;
-
- if (count < 2)
- return -EINVAL;
- if (count > sizeof(state->req.reply))
- count = sizeof(state->req.reply);
- ret = verify_area(VERIFY_WRITE, buf, count);
- if (ret)
- return ret;
-
- if (!state->req.reply_expected)
- return 0;
-
- ret = adb_wait_reply(state, file);
- if (ret)
- return ret;
-
- ret = state->req.reply_len;
- memcpy_tofs(buf, state->req.reply, ret);
- state->req.reply_expected = 0;
-
- return ret;
-}
-
-static int adb_write(struct inode *inode, struct file *file,
- const char *buf, int count)
-{
- int ret;
- struct adbdev_state *state = file->private_data;
-
- if (count < 2 || count > sizeof(state->req.data))
- return -EINVAL;
- ret = verify_area(VERIFY_READ, buf, count);
- if (ret)
- return ret;
-
- if (state->req.reply_expected && !state->req.got_reply) {
- /* A previous request is still being processed.
- Wait for it to finish. */
- ret = adb_wait_reply(state, file);
- if (ret)
- return ret;
- }
-
- state->req.nbytes = count;
- state->req.done = adb_write_done;
- memcpy_fromfs(state->req.data, buf, count);
- state->req.reply_expected = 1;
- state->req.got_reply = 0;
- adb_send_request(&state->req);
-
- return count;
-}
-
-static struct file_operations adb_fops = {
- llseek: adb_lseek,
- read: adb_read,
- write: adb_write,
- open: adb_open,
- release: adb_release,
-};
-
-static struct miscdevice adb_dev = {
- ADB_MINOR,
- "adb",
- &adb_fops
-};
-
-void adbdev_init(void)
-{
- misc_register(&adb_dev);
-}
-
-#endif /* old ADB device */
--- /dev/null
+/*
+ * Baboon Custom IC Managment
+ *
+ * The Baboon custom IC controls the IDE, PCMCIA and media bay on the
+ * PowerBook 190. It multiplexes multiple interrupt sources onto the
+ * Nubus slot $C interrupt.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/traps.h>
+#include <asm/bootinfo.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_baboon.h>
+
+/* #define DEBUG_BABOON /**/
+/* #define DEBUG_IRQS /**/
+
+int baboon_present,baboon_active;
+volatile struct baboon *baboon;
+
+void baboon_irq(int, void *, struct pt_regs *);
+
+extern int console_loglevel;
+
+extern int macide_ack_intr(ide_hwif_t *);
+
+/*
+ * Baboon initialization.
+ */
+
+void __init baboon_init(void)
+{
+ if (macintosh_config->ident != MAC_MODEL_PB190) {
+ baboon = NULL;
+ baboon_present = 0;
+ return;
+ }
+
+ baboon = (struct baboon *) BABOON_BASE;
+ baboon_present = 1;
+ baboon_active = 0;
+
+ printk("Baboon detected at %p\n", baboon);
+}
+
+/*
+ * Register the Baboon interrupt dispatcher on nubus slot $C.
+ */
+
+void __init baboon_register_interrupts(void)
+{
+ request_irq(IRQ_NUBUS_C, baboon_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+ "baboon", (void *) baboon);
+}
+
+/*
+ * Baboon interrupt handler. This works a lot like a VIA.
+ */
+
+void baboon_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ int irq_bit,i;
+ unsigned char events;
+
+#ifdef DEBUG_IRQS
+ printk("baboon_irq: mb_control %02X mb_ifr %02X mb_status %02X active %02X\n",
+ (uint) baboon->mb_control, (uint) baboon->mb_ifr,
+ (uint) baboon->mb_status, baboon_active);
+#endif
+
+ if (!(events = baboon->mb_ifr & 0x07)) return;
+
+ for (i = 0, irq_bit = 1 ; i < 3 ; i++, irq_bit <<= 1) {
+ if (events & irq_bit/* & baboon_active*/) {
+ baboon_active &= ~irq_bit;
+ mac_do_irq_list(IRQ_BABOON_0 + i, regs);
+ baboon_active |= irq_bit;
+ baboon->mb_ifr &= ~irq_bit;
+ }
+ }
+#if 0
+ if (baboon->mb_ifr & 0x02) macide_ack_intr(NULL);
+ /* for now we need to smash all interrupts */
+ baboon->mb_ifr &= ~events;
+#endif
+}
+
+void baboon_irq_enable(int irq) {
+ int irq_idx = IRQ_IDX(irq);
+
+#ifdef DEBUG_IRQUSE
+ printk("baboon_irq_enable(%d)\n", irq);
+#endif
+ baboon_active |= (1 << irq_idx);
+}
+
+void baboon_irq_disable(int irq) {
+ int irq_idx = IRQ_IDX(irq);
+
+#ifdef DEBUG_IRQUSE
+ printk("baboon_irq_disable(%d)\n", irq);
+#endif
+ baboon_active &= ~(1 << irq_idx);
+}
+
+void baboon_irq_clear(int irq) {
+ int irq_idx = IRQ_IDX(irq);
+
+ baboon->mb_ifr &= ~(1 << irq_idx);
+}
+
+int baboon_irq_pending(int irq)
+{
+ int irq_idx = IRQ_IDX(irq);
+
+ return baboon->mb_ifr & (1 << irq_idx);
+}
#include <asm/mac_oss.h>
#include <asm/mac_psc.h>
-/* Offset between Unix time (1970-based) and Mac time (1904-based) */
-
-#define MAC_TIME_OFFSET 2082844800
-
-/*
- * hardware reset vector
- */
-
-static void (*rom_reset)(void);
-
/* Mac bootinfo struct */
struct mac_booter_data mac_bi_data = {0,};
extern void mackbd_leds(unsigned int leds);
/* Mac specific timer functions */
+extern void mac_gettod (int *, int *, int *, int *, int *, int *);
extern unsigned long mac_gettimeoffset (void);
-static void mac_gettod (int *, int *, int *, int *, int *, int *);
-static int mac_hwclk (int, struct hwclk_time *);
-static int mac_set_clock_mmss (unsigned long);
+extern int mac_hwclk (int, struct hwclk_time *);
+extern int mac_set_clock_mmss (unsigned long);
+extern int mac_get_irq_list(char *);
extern void iop_preinit(void);
extern void iop_init(void);
extern void via_init(void);
extern void via_flush_cache(void);
extern void oss_init(void);
extern void psc_init(void);
+extern void baboon_init(void);
extern void (*kd_mksound)(unsigned int, unsigned int);
extern void mac_mksound(unsigned int, unsigned int);
extern void mac_debug_init(void);
extern void mac_debugging_long(int, long);
-/* poweroff functions */
-extern void via_poweroff(void);
-extern void oss_poweroff(void);
-extern void adb_poweroff(void);
-extern void adb_hwreset(void);
-
-/* pram functions */
-extern __u32 via_read_time(void);
-extern void via_write_time(__u32);
-extern __u32 adb_read_time(void);
-extern void adb_write_time(__u32);
-
#ifdef CONFIG_MAGIC_SYSRQ
static char mac_sysrq_xlate[128] =
"\000sdfghzxcv\000bqwer" /* 0x00 - 0x0f */
extern int console_loglevel;
-/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
- * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
- * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
- *
- * [For the Julian calendar (which was used in Russia before 1917,
- * Britain & colonies before 1752, anywhere else before 1582,
- * and is still in use by some communities) leave out the
- * -year/100+year/400 terms, and add 10.]
- *
- * This algorithm was first published by Gauss (I think).
- *
- * WARNING: this function will overflow on 2106-02-07 06:28:16 on
- * machines were long is 32-bit! (However, as time_t is signed, we
- * will already get problems at other places on 2038-01-19 03:14:08)
- */
-static unsigned long mktime(unsigned int year, unsigned int mon,
- unsigned int day, unsigned int hour,
- unsigned int min, unsigned int sec)
-{
- if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */
- mon += 12; /* Puts Feb last since it has leap day */
- year -= 1;
- }
- return (((
- (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
- year*365 - 719499
- )*24 + hour /* now have hours */
- )*60 + min /* now have minutes */
- )*60 + sec; /* finally seconds */
-}
-
-/*
- * This function translates seconds since 1970 into a proper date.
- *
- * Algorithm cribbed from glibc2.1, __offtime().
- */
-#define SECS_PER_MINUTE (60)
-#define SECS_PER_HOUR (SECS_PER_MINUTE * 60)
-#define SECS_PER_DAY (SECS_PER_HOUR * 24)
-
-static void unmktime(unsigned long time, long offset,
- int *yearp, int *monp, int *dayp,
- int *hourp, int *minp, int *secp)
-{
- /* How many days come before each month (0-12). */
- static const unsigned short int __mon_yday[2][13] =
- {
- /* Normal years. */
- { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
- /* Leap years. */
- { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
- };
- long int days, rem, y, wday, yday;
- const unsigned short int *ip;
-
- days = time / SECS_PER_DAY;
- rem = time % SECS_PER_DAY;
- rem += offset;
- while (rem < 0) {
- rem += SECS_PER_DAY;
- --days;
- }
- while (rem >= SECS_PER_DAY) {
- rem -= SECS_PER_DAY;
- ++days;
- }
- *hourp = rem / SECS_PER_HOUR;
- rem %= SECS_PER_HOUR;
- *minp = rem / SECS_PER_MINUTE;
- *secp = rem % SECS_PER_MINUTE;
- /* January 1, 1970 was a Thursday. */
- wday = (4 + days) % 7; /* Day in the week. Not currently used */
- if (wday < 0) wday += 7;
- y = 1970;
-
-#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
-#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
-#define __isleap(year) \
- ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
-
- while (days < 0 || days >= (__isleap (y) ? 366 : 365))
- {
- /* Guess a corrected year, assuming 365 days per year. */
- long int yg = y + days / 365 - (days % 365 < 0);
-
- /* Adjust DAYS and Y to match the guessed year. */
- days -= ((yg - y) * 365
- + LEAPS_THRU_END_OF (yg - 1)
- - LEAPS_THRU_END_OF (y - 1));
- y = yg;
- }
- *yearp = y - 1900;
- yday = days; /* day in the year. Not currently used. */
- ip = __mon_yday[__isleap(y)];
- for (y = 11; days < (long int) ip[y]; --y)
- continue;
- days -= ip[y];
- *monp = y;
- *dayp = days + 1; /* day in the month */
- return;
-}
-
-/*
- * Return the boot time for use in initializing the kernel clock.
- *
- * I'd like to read the hardware clock here but many machines read
- * the PRAM through ADB, and interrupts aren't initialized when this
- * is called so ADB obviously won't work.
- */
-
-static void mac_gettod(int *yearp, int *monp, int *dayp,
- int *hourp, int *minp, int *secp)
-{
- /* Yes the GMT bias is backwards. It looks like Penguin is
- screwing up the boottime it gives us... This works for me
- in Canada/Eastern but it might be wrong everywhere else. */
- unmktime(mac_bi_data.boottime, -mac_bi_data.gmtbias * 60,
- yearp, monp, dayp, hourp, minp, secp);
- /* For some reason this is off by one */
- *monp = *monp + 1;
-}
-
-/*
- * Read/write the hardware clock.
- */
-
-static int mac_hwclk(int op, struct hwclk_time *t)
-{
- unsigned long now;
-
- if (!op) { /* read */
- if (macintosh_config->adb_type == MAC_ADB_II) {
- now = via_read_time();
- } else if ((macintosh_config->adb_type == MAC_ADB_IISI) ||
- (macintosh_config->adb_type == MAC_ADB_CUDA)) {
- now = adb_read_time();
- } else if (macintosh_config->adb_type == MAC_ADB_IOP) {
- now = via_read_time();
- } else {
- now = MAC_TIME_OFFSET;
- }
-
- now -= MAC_TIME_OFFSET;
-
- t->wday = 0;
- unmktime(now, 0,
- &t->year, &t->mon, &t->day,
- &t->hour, &t->min, &t->sec);
- } else { /* write */
- now = mktime(t->year + 1900, t->mon + 1, t->day,
- t->hour, t->min, t->sec) + MAC_TIME_OFFSET;
-
- if (macintosh_config->adb_type == MAC_ADB_II) {
- via_write_time(now);
- } else if ((macintosh_config->adb_type == MAC_ADB_IISI) ||
- (macintosh_config->adb_type == MAC_ADB_CUDA)) {
- adb_write_time(now);
- } else if (macintosh_config->adb_type == MAC_ADB_IOP) {
- via_write_time(now);
- }
- }
- return 0;
-}
-
-/*
- * Set minutes/seconds in the hardware clock
- */
-
-static int mac_set_clock_mmss (unsigned long nowtime)
-{
- struct hwclk_time now;
-
- mac_hwclk(0, &now);
- now.sec = nowtime % 60;
- now.min = (nowtime / 60) % 60;
- mac_hwclk(1, &now);
-
- return 0;
-}
-
#if 0
void mac_waitbut (void)
{
extern struct consw fb_con;
extern struct fb_info *mac_fb_init(long *);
- /*
- * Parse a Macintosh-specific record in the bootinfo
- */
+extern void mac_default_handler(int, void *, struct pt_regs *);
+
+void (*mac_handlers[8])(int, void *, struct pt_regs *)=
+{
+ mac_default_handler,
+ mac_default_handler,
+ mac_default_handler,
+ mac_default_handler,
+ mac_default_handler,
+ mac_default_handler,
+ mac_default_handler,
+ mac_default_handler
+};
+
+/*
+ * Parse a Macintosh-specific record in the bootinfo
+ */
int __init mac_parse_bootinfo(const struct bi_record *record)
{
}
/*
- * Flip into 24bit mode for an instant - flushes the L2 cache card. We
- * have to disable interrupts for this. Our IRQ handlers will crap
- * themselves if they take an IRQ in 24bit mode!
+ * Flip into 24bit mode for an instant - flushes the L2 cache card. We
+ * have to disable interrupts for this. Our IRQ handlers will crap
+ * themselves if they take an IRQ in 24bit mode!
*/
static void mac_cache_card_flush(int writeback)
enable_irq = mac_enable_irq;
disable_irq = mac_disable_irq;
mach_get_model = mac_get_model;
+ mach_default_handler = &mac_handlers;
+ mach_get_irq_list = mac_get_irq_list;
mach_gettimeoffset = mac_gettimeoffset;
mach_gettod = mac_gettod;
mach_hwclk = mac_hwclk;
static struct mac_model mac_data_table[]=
{
/*
- * The default machine, in case we get an unsupported one
* We'll pretend to be a Macintosh II, that's pretty safe.
*/
* Weirdified MacII hardware - all subtley different. Gee thanks
* Apple. All these boxes seem to have VIA2 in a different place to
* the MacII (+1A000 rather than +4000)
- *
- * The IIfx apparently has different ADB hardware, and stuff
- * so zany nobody knows how to drive it.
- * Even so, with Marten's help we'll try to deal with it :-)
* CSA: see http://developer.apple.com/technotes/hw/hw_09.html
*/
{ MAC_MODEL_C660, "Centris 660AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_MACE, MAC_NUBUS},
/*
- * Power books - seem similar to early Quadras ? (most have 030 though)
+ * The PowerBooks all the same "Combo" custom IC for SCSI and SCC
+ * and a PMU (in two variations?) for ADB. Most of them use the
+ * Quadra-style VIAs. A few models also have IDE from hell.
*/
- { MAC_MODEL_PB140, "PowerBook 140", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
- { MAC_MODEL_PB145, "PowerBook 145", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
- /* The PB150 has IDE, and IIci style VIA */
- { MAC_MODEL_PB150, "PowerBook 150", MAC_ADB_PB1, MAC_VIA_IIci, MAC_SCSI_NONE, MAC_IDE_PB, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
- { MAC_MODEL_PB160, "PowerBook 160", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
- { MAC_MODEL_PB165, "PowerBook 165", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
- { MAC_MODEL_PB165C, "PowerBook 165c", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
- { MAC_MODEL_PB170, "PowerBook 170", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
- { MAC_MODEL_PB180, "PowerBook 180", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
- { MAC_MODEL_PB180C, "PowerBook 180c", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
- { MAC_MODEL_PB190, "PowerBook 190cs", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_PB, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
- /* These have onboard SONIC */
- { MAC_MODEL_PB520, "PowerBook 520", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS},
+ { MAC_MODEL_PB140, "PowerBook 140", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
+ { MAC_MODEL_PB145, "PowerBook 145", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
+ { MAC_MODEL_PB150, "PowerBook 150", MAC_ADB_PB1, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_PB, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
+ { MAC_MODEL_PB160, "PowerBook 160", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
+ { MAC_MODEL_PB165, "PowerBook 165", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
+ { MAC_MODEL_PB165C, "PowerBook 165c", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
+ { MAC_MODEL_PB170, "PowerBook 170", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
+ { MAC_MODEL_PB180, "PowerBook 180", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
+ { MAC_MODEL_PB180C, "PowerBook 180c", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
+ { MAC_MODEL_PB190, "PowerBook 190", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_BABOON, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
+ { MAC_MODEL_PB520, "PowerBook 520", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS},
/*
- * Power book Duos - similar to Power books, I hope
+ * PowerBook Duos are pretty much like normal PowerBooks
+ * All of these probably have onboard SONIC in the Dock which
+ * means we'll have to probe for it eventually.
+ *
+ * Are these reallly MAC_VIA_IIci? The developer notes for the
+ * Duos show pretty much the same custom parts as in most of
+ * the other PowerBooks which would imply MAC_VIA_QUADRA.
*/
- /* All of these might have onboard SONIC in the Dock but I'm not quite sure */
{ MAC_MODEL_PB210, "PowerBook Duo 210", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
{ MAC_MODEL_PB230, "PowerBook Duo 230", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
{ MAC_MODEL_PB250, "PowerBook Duo 250", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
via_init();
oss_init();
psc_init();
+ baboon_init();
}
void mac_report_hardware(void)
strcpy(str,"Macintosh ");
strcat(str, macintosh_config->name);
}
-
-/*
- * The power switch - yes it's software!
- */
-
-void mac_poweroff(void)
-{
- /*
- * MAC_ADB_IISI may need to be moved up here if it doesn't actually
- * work using the ADB packet method. --David Kilzer
- */
-
- if (oss_present) {
- oss_poweroff();
- } else if (macintosh_config->adb_type == MAC_ADB_II) {
- via_poweroff();
- } else {
- adb_poweroff();
- }
-}
-
-/*
- * Not all Macs support software power down; for the rest, just
- * try the ROM reset vector ...
- */
-void mac_reset(void)
-{
- /*
- * MAC_ADB_IISI may need to be moved up here if it doesn't actually
- * work using the ADB packet method. --David Kilzer
- */
-
- if (macintosh_config->adb_type == MAC_ADB_II) {
- unsigned long cpu_flags;
-
- /* need ROMBASE in booter */
- /* indeed, plus need to MAP THE ROM !! */
-
- if (mac_bi_data.rombase == 0)
- mac_bi_data.rombase = 0x40800000;
-
- /* works on some */
- rom_reset = (void *) (mac_bi_data.rombase + 0xa);
-
- if (macintosh_config->ident == MAC_MODEL_SE30) {
- /*
- * MSch: Machines known to crash on ROM reset ...
- */
- printk("System halted.\n");
- while(1);
- } else {
- save_flags(cpu_flags);
- cli();
-
- rom_reset();
-
- restore_flags(cpu_flags);
- }
-
- /* We never make it this far... it usually panics above. */
- printk ("Restart failed. Please restart manually.\n");
-
- /* XXX - delay do we need to spin here ? */
- while(1); /* Just in case .. */
- } else if (macintosh_config->adb_type == MAC_ADB_IISI
- || macintosh_config->adb_type == MAC_ADB_CUDA) {
- adb_hwreset();
- } else if (CPU_IS_030) {
-
- /* 030-specific reset routine. The idea is general, but the
- * specific registers to reset are '030-specific. Until I
- * have a non-030 machine, I can't test anything else.
- * -- C. Scott Ananian <cananian@alumni.princeton.edu>
- */
-
- unsigned long rombase = 0x40000000;
-
- /* make a 1-to-1 mapping, using the transparent tran. reg. */
- unsigned long virt = (unsigned long) mac_reset;
- unsigned long phys = virt_to_phys(mac_reset);
- unsigned long offset = phys-virt;
- cli(); /* lets not screw this up, ok? */
- __asm__ __volatile__(".chip 68030\n\t"
- "pmove %0,%/tt0\n\t"
- ".chip 68k"
- : : "m" ((phys&0xFF000000)|0x8777));
- /* Now jump to physical address so we can disable MMU */
- __asm__ __volatile__(
- ".chip 68030\n\t"
- "lea %/pc@(1f),%/a0\n\t"
- "addl %0,%/a0\n\t"/* fixup target address and stack ptr */
- "addl %0,%/sp\n\t"
- "pflusha\n\t"
- "jmp %/a0@\n\t" /* jump into physical memory */
- "0:.long 0\n\t" /* a constant zero. */
- /* OK. Now reset everything and jump to reset vector. */
- "1:\n\t"
- "lea %/pc@(0b),%/a0\n\t"
- "pmove %/a0@, %/tc\n\t" /* disable mmu */
- "pmove %/a0@, %/tt0\n\t" /* disable tt0 */
- "pmove %/a0@, %/tt1\n\t" /* disable tt1 */
- "movel #0, %/a0\n\t"
- "movec %/a0, %/vbr\n\t" /* clear vector base register */
- "movec %/a0, %/cacr\n\t" /* disable caches */
- "movel #0x0808,%/a0\n\t"
- "movec %/a0, %/cacr\n\t" /* flush i&d caches */
- "movew #0x2700,%/sr\n\t" /* set up status register */
- "movel %1@(0x0),%/a0\n\t"/* load interrupt stack pointer */
- "movec %/a0, %/isp\n\t"
- "movel %1@(0x4),%/a0\n\t" /* load reset vector */
- "reset\n\t" /* reset external devices */
- "jmp %/a0@\n\t" /* jump to the reset vector */
- ".chip 68k"
- : : "r" (offset), "a" (rombase) : "a0");
-
- /* should never get here */
- sti(); /* sure, why not */
- printk ("030 Restart failed. Please restart manually.\n");
- while(1);
- } else {
- /* We never make it here... The above shoule handle all cases. */
- printk ("Restart failed. Please restart manually.\n");
-
- /* XXX - delay do we need to spin here ? */
- while(1); /* Just in case .. */
- }
-}
-
-/*
- * Local variables:
- * c-indent-level: 4
- * tab-width: 8
- * End:
- */
{
if (iop_ism_present) {
if (oss_present) {
- request_irq(OSS_IRQLEV_IOPISM, iop_ism_irq,
+ sys_request_irq(OSS_IRQLEV_IOPISM, iop_ism_irq,
IRQ_FLG_LOCK, "ISM IOP",
(void *) IOP_NUM_ISM);
oss_irq_enable(IRQ_MAC_ADB);
* 3 - unused (?)
*
* 4 - SCC (slot number determined by reading RR3 on the SSC itself)
- * - slot 0: SCC channel A
- * - slot 1: SCC channel B
+ * - slot 1: SCC channel A
+ * - slot 2: SCC channel B
*
* 5 - unused (?)
* [serial errors or special conditions seem to raise level 6
* - slot 5: Slot $E
*
* 4 - SCC IOP
- * - slot 0: SCC channel A
- * - slot 1: SCC channel B
+ * - slot 1: SCC channel A
+ * - slot 2: SCC channel B
*
* 5 - ISM IOP (ADB?)
*
* bits. The handlers for this new machspec interrupt number are then
* called. This puts Nubus interrupts into the range 56-62.
*
+ * - The Baboon interrupts (used on some PowerBooks) are an even more special
+ * case. They're hidden behind the Nubus slot $C interrupt thus adding a
+ * third layer of indirection. Why oh why did the Apple engineers do that?
+ *
* - We support "fast" and "slow" handlers, just like the Amiga port. The
* fast handlers are called first and with all interrupts disabled. They
* are expected to execute quickly (hence the name). The slow handlers are
* called last with interrupts enabled and the interrupt level restored.
* They must therefore be reentrant.
*
- * - Drivers should never try to request autovector interrupt numbers. It
- * won't work.
- *
* TODO:
*
- * o Perhaps build some intelligence into mac_SCC_handler(); we could check
- * the SCC ourselves and only call the handler for the appopriate channel.
*/
#include <linux/types.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/traps.h>
+#include <asm/bootinfo.h>
#include <asm/machw.h>
#include <asm/macintosh.h>
#include <asm/mac_via.h>
#include <asm/macints.h>
+/*
+ * The mac_irq_list array is an array of linked lists of irq_node_t nodes.
+ * Each node contains one handler to be called whenever the interrupt
+ * occurs, with fast handlers listed before slow handlers.
+ */
+
+irq_node_t *mac_irq_list[NUM_MAC_SOURCES];
+
+/* SCC interrupt mask */
+
+static int scc_mask;
+
/*
* VIA/RBV hooks
*/
extern void iop_register_interrupts(void);
+/*
+ * Baboon hooks
+ */
+
+extern int baboon_present;
+
+extern void baboon_init(void);
+extern void baboon_register_interrupts(void);
+extern void baboon_irq_enable(int);
+extern void baboon_irq_disable(int);
+extern void baboon_irq_clear(int);
+extern int baboon_irq_pending(int);
+
+/*
+ * SCC interrupt routines
+ */
+
+static void scc_irq_enable(int);
+static void scc_irq_disable(int);
+
/*
* console_loglevel determines NMI handler function
*/
extern void mac_bang(int, void *, struct pt_regs *);
void mac_nmi_handler(int, void *, struct pt_regs *);
-void mac_SCC_handler(int, void *, struct pt_regs *);
+void mac_debug_handler(int, void *, struct pt_regs *);
/* #define DEBUG_MACINTS */
void mac_init_IRQ(void)
{
+ int i;
+
#ifdef DEBUG_MACINTS
printk("mac_init_IRQ(): Setting things up...\n");
#endif
+ /* Initialize the IRQ handler lists. Initially each list is empty, */
+
+ for (i = 0; i < NUM_MAC_SOURCES; i++) {
+ mac_irq_list[i] = NULL;
+ }
+
+ scc_mask = 0;
+
/*
- * Register the handlers for the the master IRQ handlers
+ * Now register the handlers for the the master IRQ handlers
* at levels 1-7. Most of the work is done elsewhere.
*/
via_register_interrupts();
}
if (psc_present) psc_register_interrupts();
+ if (baboon_present) baboon_register_interrupts();
iop_register_interrupts();
- request_irq(7, mac_nmi_handler, IRQ_FLG_LOCK, "NMI", mac_nmi_handler);
+ sys_request_irq(7, mac_nmi_handler, IRQ_FLG_LOCK, "NMI", mac_nmi_handler);
#ifdef DEBUG_MACINTS
printk("mac_init_IRQ(): Done!\n");
#endif
void mac_do_irq_list(int irq, struct pt_regs *fp)
{
- irq_node_t *node, *slow_nodes, **list = NULL;
+ irq_node_t *node, *slow_nodes;
unsigned long cpu_flags;
kstat.irqs[0][irq]++;
- if (irq < VIA1_SOURCE_BASE) {
- list = &autoirq_list[irq];
- } else if (irq < NUM_MAC_SOURCES) {
- list = &userirq_list[irq - VIA1_SOURCE_BASE];
- }
- if (!list) return;
-
#ifdef DEBUG_SPURIOUS
- if (!*list && (console_loglevel > 7)) {
+ if (!mac_irq_list[irq] && (console_loglevel > 7)) {
printk("mac_do_irq_list: spurious interrupt %d!\n", irq);
return;
}
#endif
/* serve first fast and normal handlers */
- for (node = *list;
+ for (node = mac_irq_list[irq];
node && (!(node->flags & IRQ_FLG_SLOW));
node = node->next)
node->handler(irq, node->dev_id, fp);
void mac_enable_irq (unsigned int irq)
{
- switch(IRQ_SRC(irq)) {
+ int irq_src = IRQ_SRC(irq);
+
+ switch(irq_src) {
case 1: via_irq_enable(irq);
break;
case 2:
psc_irq_enable(irq);
} else if (oss_present) {
oss_irq_enable(irq);
+ } else if (irq_src == 4) {
+ scc_irq_enable(irq);
+ }
+ break;
+ case 8: if (baboon_present) {
+ baboon_irq_enable(irq);
}
break;
}
void mac_disable_irq (unsigned int irq)
{
- switch(IRQ_SRC(irq)) {
+ int irq_src = IRQ_SRC(irq);
+
+ switch(irq_src) {
case 1: via_irq_disable(irq);
break;
case 2:
- case 7:
- if (oss_present) {
+ case 7: if (oss_present) {
oss_irq_disable(irq);
} else {
via_irq_disable(irq);
psc_irq_disable(irq);
} else if (oss_present) {
oss_irq_disable(irq);
+ } else if (irq_src == 4) {
+ scc_irq_disable(irq);
+ }
+ break;
+ case 8: if (baboon_present) {
+ baboon_irq_disable(irq);
}
break;
}
oss_irq_clear(irq);
}
break;
+ case 8: if (baboon_present) {
+ baboon_irq_clear(irq);
+ }
+ break;
}
}
void (*handler)(int, void *, struct pt_regs *),
unsigned long flags, const char *devname, void *dev_id)
{
- int vec, ret;
+ irq_node_t *node;
#ifdef DEBUG_MACINTS
printk ("%s: irq %d requested for %s\n", __FUNCTION__, irq, devname);
#endif
if (irq < VIA1_SOURCE_BASE) {
- vec = VEC_SPUR + irq;
- } else if (irq < NUM_MAC_SOURCES) {
- vec = VEC_USER + irq - VIA1_SOURCE_BASE;
- } else {
+ return sys_request_irq(irq, handler, flags, devname, dev_id);
+ }
+
+ if (irq >= NUM_MAC_SOURCES) {
printk ("%s: unknown irq %d requested by %s\n",
__FUNCTION__, irq, devname);
- return -EAGAIN;
}
- ret = sys_request_listirq(vec, handler, flags, devname, dev_id);
- if (!ret) {
- vectors[vec] = autoirq_listhandler;
- mac_enable_irq(irq);
- }
+ /* Get a node and stick it onto the right list */
+
+ if (!(node = new_irq_node())) return -ENOMEM;
+
+ node->handler = handler;
+ node->flags = flags;
+ node->dev_id = dev_id;
+ node->devname = devname;
+ node->next = NULL;
+ mac_insert_irq(&mac_irq_list[irq], node);
+
+ /* Now enable the IRQ source */
+
+ mac_enable_irq(irq);
- return ret;
+ return 0;
}
/*
void mac_free_irq(unsigned int irq, void *dev_id)
{
- irq_node_t **list = NULL;
- int vec = 0;
-
#ifdef DEBUG_MACINTS
printk ("%s: irq %d freed by %p\n", __FUNCTION__, irq, dev_id);
#endif
if (irq < VIA1_SOURCE_BASE) {
- vec = VEC_SPUR + irq;
- list = &autoirq_list[irq];
- } else if (irq < NUM_MAC_SOURCES) {
- vec = VEC_USER + irq - VIA1_SOURCE_BASE;
- list = &userirq_list[irq - VIA1_SOURCE_BASE];
+ return sys_free_irq(irq, dev_id);
+ }
+
+ if (irq >= NUM_MAC_SOURCES) {
+ printk ("%s: unknown irq %d freed\n",
+ __FUNCTION__, irq);
+ return;
}
- if (!list) return;
- sys_free_irq(vec, dev_id);
+ mac_delete_irq(&mac_irq_list[irq], dev_id);
/* If the list for this interrupt is */
/* empty then disable the source. */
- if (!*list) {
+ if (!mac_irq_list[irq]) {
mac_disable_irq(irq);
- vectors[vec] = bad_interrupt;
+ }
+}
+
+/*
+ * Generate a pretty listing for /proc/interrupts
+ *
+ * By the time we're called the autovector interrupt list has already been
+ * generated, so we just need to do the machspec interrupts.
+ *
+ * 990506 (jmt) - rewritten to handle chained machspec interrupt handlers.
+ * Also removed display of num_spurious it is already
+ * displayed for us as autovector irq 0.
+ */
+
+int mac_get_irq_list (char *buf)
+{
+ int i, len = 0;
+ irq_node_t *node;
+ char *base;
+
+ /* Don't do Nubus interrupts in this loop; we do them separately */
+ /* below so that we can print slot numbers instead of IRQ numbers */
+
+ for (i = VIA1_SOURCE_BASE ; i < NUM_MAC_SOURCES ; ++i) {
+
+ /* Nonexistant interrupt or nothing registered; skip it. */
+
+ if ((node = mac_irq_list[i]) == NULL) continue;
+ if (node->flags & IRQ_FLG_STD) continue;
+
+ base = "";
+ switch(IRQ_SRC(i)) {
+ case 1: base = "via1";
+ break;
+ case 2: if (oss_present) {
+ base = "oss";
+ } else {
+ base = "via2";
+ }
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6: if (psc_present) {
+ base = "psc";
+ } else if (oss_present) {
+ base = "oss";
+ } else {
+ if (IRQ_SRC(i) == 4) base = "scc";
+ }
+ break;
+ case 7: base = "nbus";
+ break;
+ case 8: base = "bbn";
+ break;
+ }
+ len += sprintf(buf+len, "%4s %2d: %10u ",
+ base, i, kstat.irqs[0][i]);
+
+ do {
+ if (node->flags & IRQ_FLG_FAST) {
+ len += sprintf(buf+len, "F ");
+ } else if (node->flags & IRQ_FLG_SLOW) {
+ len += sprintf(buf+len, "S ");
+ } else {
+ len += sprintf(buf+len, " ");
+ }
+ len += sprintf(buf+len, "%s\n", node->devname);
+ if ((node = node->next)) {
+ len += sprintf(buf+len, " ");
+ }
+ } while(node);
+
+ }
+ return len;
+}
+
+void mac_default_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+#ifdef DEBUG_SPURIOUS
+ if (console_loglevel > 6) {
+ printk("Unexpected IRQ %d on device %p\n", irq, dev_id);
+ }
+#endif
+}
+
+static int num_debug[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+void mac_debug_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ if (num_debug[irq] < 10) {
+ printk("DEBUG: Unexpected IRQ %d\n", irq);
+ num_debug[irq]++;
}
}
}
/*
- * SCC master interrupt handler; sole purpose: pass the registered
- * async struct to the SCC handler proper.
+ * Simple routines for masking and unmasking
+ * SCC interrupts in cases where this can't be
+ * done in hardware (only the PSC can do that.)
+ */
+
+static void scc_irq_enable(int irq) {
+ int irq_idx = IRQ_IDX(irq);
+
+ scc_mask |= (1 << irq_idx);
+}
+
+static void scc_irq_disable(int irq) {
+ int irq_idx = IRQ_IDX(irq);
+
+ scc_mask &= ~(1 << irq_idx);
+}
+
+/*
+ * SCC master interrupt handler. We have to do a bit of magic here
+ * to figure out what channel gave us the interrupt; putting this
+ * here is cleaner than hacking it into drivers/char/macserial.c.
*/
-void mac_SCC_handler(int irq, void *dev_id, struct pt_regs *regs)
+void mac_scc_dispatch(int irq, void *dev_id, struct pt_regs *regs)
{
- mac_do_irq_list(IRQ_SCCA, regs);
- mac_do_irq_list(IRQ_SCCB, regs);
+ volatile unsigned char *scc = (unsigned char *) mac_bi_data.sccbase + 2;
+ unsigned char reg;
+ unsigned long cpu_flags;
+
+ /* Read RR3 from the chip. Always do this on channel A */
+ /* This must be an atomic operation so disable irqs. */
+
+ save_flags(cpu_flags); cli();
+ *scc = 3;
+ reg = *scc;
+ restore_flags(cpu_flags);
+
+ /* Now dispatch. Bits 0-2 are for channel B and */
+ /* bits 3-5 are for channel A. We can safely */
+ /* ignore the remaining bits here. */
+ /* */
+ /* Note that we're ignoring scc_mask for now. */
+ /* If we actually mask the ints then we tend to */
+ /* get hammered by very persistant SCC irqs, */
+ /* and since they're autovector interrupts they */
+ /* pretty much kill the system. */
+
+ if (reg & 0x38) mac_do_irq_list(IRQ_SCCA, regs);
+ if (reg & 0x07) mac_do_irq_list(IRQ_SCCB, regs);
}
+++ /dev/null
-/*
- * linux/arch/m68k/mac/mackeyb.c
- *
- * Keyboard driver for Macintosh computers.
- *
- * Adapted from drivers/macintosh/key_mac.c and arch/m68k/atari/akakeyb.c
- * (see that file for its authors and contributors).
- *
- * Copyright (C) 1997 Michael Schmitz.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive
- * for more details.
- */
-
-/*
- * misc. keyboard stuff (everything not in adb-bus.c or keyb_m68k.c)
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/mm.h>
-#include <linux/kd.h>
-#include <linux/tty.h>
-#include <linux/console.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-/* keyb */
-#include <linux/keyboard.h>
-#include <linux/random.h>
-#include <linux/delay.h>
-/* keyb */
-
-#include <asm/setup.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/pgtable.h>
-#include <asm/machdep.h>
-
-#include <asm/macintosh.h>
-#include <asm/macints.h>
-/* for keyboard_input stuff */
-#include <asm/adb.h>
-#define KEYB_KEYREG 0 /* register # for key up/down data */
-#define KEYB_LEDREG 2 /* register # for leds on ADB keyboard */
-#define MOUSE_DATAREG 0 /* reg# for movement/button codes from mouse */
-/* end keyboard_input stuff */
-
-#include <linux/kbd_kern.h>
-#include <linux/kbd_ll.h>
-
-static void kbd_repeat(unsigned long);
-static struct timer_list repeat_timer = { NULL, NULL, 0, 0, kbd_repeat };
-static int last_keycode;
-
-static void input_keycode(int, int);
-
-extern struct kbd_struct kbd_table[];
-
-extern void adb_bus_init(void);
-extern void handle_scancode(unsigned char, int);
-extern void put_queue(int);
-
-/* keyb */
-static void mac_leds_done(struct adb_request *);
-static void keyboard_input(unsigned char *, int, struct pt_regs *);
-static void mouse_input(unsigned char *, int, struct pt_regs *);
-
-#ifdef CONFIG_ADBMOUSE
-/* XXX: Hook for mouse driver */
-void (*adb_mouse_interrupt_hook)(unsigned char *, int);
-int adb_emulate_buttons = 0;
-int adb_button2_keycode = 0x7d; /* right control key */
-int adb_button3_keycode = 0x7c; /* right option key */
-#endif
-
-/* The mouse driver - for debugging */
-extern void adb_mouse_interrupt(char *, int);
-/* end keyb */
-
-/* this map indicates which keys shouldn't autorepeat. */
-static unsigned char dont_repeat[128] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* esc...option */
- 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* num lock */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, /* scroll lock */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-};
-
-/*
- * Mac private key maps
- */
-u_short mac_plain_map[NR_KEYS] __initdata = {
- 0xfb61, 0xfb73, 0xfb64, 0xfb66, 0xfb68, 0xfb67, 0xfb7a, 0xfb78,
- 0xfb63, 0xfb76, 0xf200, 0xfb62, 0xfb71, 0xfb77, 0xfb65, 0xfb72,
- 0xfb79, 0xfb74, 0xf031, 0xf032, 0xf033, 0xf034, 0xf036, 0xf035,
- 0xf03d, 0xf039, 0xf037, 0xf02d, 0xf038, 0xf030, 0xf05d, 0xfb6f,
- 0xfb75, 0xf05b, 0xfb69, 0xfb70, 0xf201, 0xfb6c, 0xfb6a, 0xf027,
- 0xfb6b, 0xf03b, 0xf05c, 0xf02c, 0xf02f, 0xfb6e, 0xfb6d, 0xf02e,
- 0xf009, 0xf020, 0xf060, 0xf07f, 0xf200, 0xf01b, 0xf702, 0xf703,
- 0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
- 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
- 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
- 0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
- 0xf306, 0xf307, 0xfb61, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
- 0xf104, 0xf105, 0xf106, 0xf102, 0xf107, 0xf108, 0xf200, 0xf10a,
- 0xf200, 0xf10c, 0xf200, 0xf209, 0xf200, 0xf109, 0xf200, 0xf10b,
- 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf103, 0xf117,
- 0xf101, 0xf119, 0xf100, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short mac_shift_map[NR_KEYS] __initdata = {
- 0xfb41, 0xfb53, 0xfb44, 0xfb46, 0xfb48, 0xfb47, 0xfb5a, 0xfb58,
- 0xfb43, 0xfb56, 0xf200, 0xfb42, 0xfb51, 0xfb57, 0xfb45, 0xfb52,
- 0xfb59, 0xfb54, 0xf021, 0xf040, 0xf023, 0xf024, 0xf05e, 0xf025,
- 0xf02b, 0xf028, 0xf026, 0xf05f, 0xf02a, 0xf029, 0xf07d, 0xfb4f,
- 0xfb55, 0xf07b, 0xfb49, 0xfb50, 0xf201, 0xfb4c, 0xfb4a, 0xf022,
- 0xfb4b, 0xf03a, 0xf07c, 0xf03c, 0xf03f, 0xfb4e, 0xfb4d, 0xf03e,
- 0xf009, 0xf020, 0xf07e, 0xf07f, 0xf200, 0xf01b, 0xf702, 0xf703,
- 0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
- 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
- 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
- 0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
- 0xf306, 0xf307, 0xfb41, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
- 0xf10e, 0xf10f, 0xf110, 0xf10c, 0xf111, 0xf112, 0xf200, 0xf10a,
- 0xf200, 0xf10c, 0xf200, 0xf203, 0xf200, 0xf113, 0xf200, 0xf10b,
- 0xf200, 0xf11d, 0xf115, 0xf114, 0xf20b, 0xf116, 0xf10d, 0xf117,
- 0xf10b, 0xf20a, 0xf10a, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short mac_altgr_map[NR_KEYS] __initdata = {
- 0xf914, 0xfb73, 0xf917, 0xf919, 0xfb68, 0xfb67, 0xfb7a, 0xfb78,
- 0xf916, 0xfb76, 0xf200, 0xf915, 0xfb71, 0xfb77, 0xf918, 0xfb72,
- 0xfb79, 0xfb74, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200,
- 0xf200, 0xf05d, 0xf07b, 0xf05c, 0xf05b, 0xf07d, 0xf07e, 0xfb6f,
- 0xfb75, 0xf200, 0xfb69, 0xfb70, 0xf201, 0xfb6c, 0xfb6a, 0xf200,
- 0xfb6b, 0xf200, 0xf200, 0xf200, 0xf200, 0xfb6e, 0xfb6d, 0xf200,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf703,
- 0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
- 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
- 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
- 0xf200, 0xf200, 0xf90a, 0xf90b, 0xf90c, 0xf90d, 0xf90e, 0xf90f,
- 0xf910, 0xf911, 0xf914, 0xf912, 0xf913, 0xf200, 0xf200, 0xf200,
- 0xf510, 0xf511, 0xf512, 0xf50e, 0xf513, 0xf514, 0xf200, 0xf516,
- 0xf200, 0xf10c, 0xf200, 0xf202, 0xf200, 0xf515, 0xf200, 0xf517,
- 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf50f, 0xf117,
- 0xf50d, 0xf119, 0xf50c, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short mac_ctrl_map[NR_KEYS] __initdata = {
- 0xf001, 0xf013, 0xf004, 0xf006, 0xf008, 0xf007, 0xf01a, 0xf018,
- 0xf003, 0xf016, 0xf200, 0xf002, 0xf011, 0xf017, 0xf005, 0xf012,
- 0xf019, 0xf014, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01e, 0xf01d,
- 0xf200, 0xf200, 0xf01f, 0xf01f, 0xf07f, 0xf200, 0xf01d, 0xf00f,
- 0xf015, 0xf01b, 0xf009, 0xf010, 0xf201, 0xf00c, 0xf00a, 0xf007,
- 0xf00b, 0xf200, 0xf01c, 0xf200, 0xf07f, 0xf00e, 0xf00d, 0xf20e,
- 0xf200, 0xf000, 0xf000, 0xf008, 0xf200, 0xf200, 0xf702, 0xf703,
- 0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
- 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
- 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
- 0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
- 0xf306, 0xf307, 0xf001, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
- 0xf104, 0xf105, 0xf106, 0xf102, 0xf107, 0xf108, 0xf200, 0xf10a,
- 0xf200, 0xf10c, 0xf200, 0xf204, 0xf200, 0xf109, 0xf200, 0xf10b,
- 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf103, 0xf117,
- 0xf101, 0xf119, 0xf100, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short mac_shift_ctrl_map[NR_KEYS] __initdata = {
- 0xf001, 0xf013, 0xf004, 0xf006, 0xf008, 0xf007, 0xf01a, 0xf018,
- 0xf003, 0xf016, 0xf200, 0xf002, 0xf011, 0xf017, 0xf005, 0xf012,
- 0xf019, 0xf014, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, 0xf00f,
- 0xf015, 0xf200, 0xf009, 0xf010, 0xf201, 0xf00c, 0xf00a, 0xf200,
- 0xf00b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf00e, 0xf00d, 0xf200,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf703,
- 0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
- 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
- 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
- 0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
- 0xf306, 0xf307, 0xf001, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf200, 0xf10c, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf200, 0xf117,
- 0xf200, 0xf119, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf20c,
-};
-
-u_short mac_alt_map[NR_KEYS] __initdata = {
- 0xf861, 0xf873, 0xf864, 0xf866, 0xf868, 0xf867, 0xf87a, 0xf878,
- 0xf863, 0xf876, 0xf200, 0xf862, 0xf871, 0xf877, 0xf865, 0xf872,
- 0xf879, 0xf874, 0xf831, 0xf832, 0xf833, 0xf834, 0xf836, 0xf835,
- 0xf83d, 0xf839, 0xf837, 0xf82d, 0xf838, 0xf830, 0xf85d, 0xf86f,
- 0xf875, 0xf85b, 0xf869, 0xf870, 0xf80d, 0xf86c, 0xf86a, 0xf827,
- 0xf86b, 0xf83b, 0xf85c, 0xf82c, 0xf82f, 0xf86e, 0xf86d, 0xf82e,
- 0xf809, 0xf820, 0xf860, 0xf87f, 0xf200, 0xf81b, 0xf702, 0xf703,
- 0xf700, 0xf207, 0xf701, 0xf210, 0xf211, 0xf600, 0xf603, 0xf200,
- 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
- 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
- 0xf200, 0xf200, 0xf900, 0xf901, 0xf902, 0xf903, 0xf904, 0xf905,
- 0xf906, 0xf907, 0xf861, 0xf908, 0xf909, 0xf200, 0xf200, 0xf200,
- 0xf504, 0xf505, 0xf506, 0xf502, 0xf507, 0xf508, 0xf200, 0xf50a,
- 0xf200, 0xf10c, 0xf200, 0xf209, 0xf200, 0xf509, 0xf200, 0xf50b,
- 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf503, 0xf117,
- 0xf501, 0xf119, 0xf500, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short mac_ctrl_alt_map[NR_KEYS] __initdata = {
- 0xf801, 0xf813, 0xf804, 0xf806, 0xf808, 0xf807, 0xf81a, 0xf818,
- 0xf803, 0xf816, 0xf200, 0xf802, 0xf811, 0xf817, 0xf805, 0xf812,
- 0xf819, 0xf814, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf80f,
- 0xf815, 0xf200, 0xf809, 0xf810, 0xf201, 0xf80c, 0xf80a, 0xf200,
- 0xf80b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf80e, 0xf80d, 0xf200,
- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf703,
- 0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
- 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
- 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
- 0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
- 0xf306, 0xf307, 0xf801, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
- 0xf504, 0xf505, 0xf506, 0xf502, 0xf507, 0xf508, 0xf200, 0xf50a,
- 0xf200, 0xf10c, 0xf200, 0xf200, 0xf200, 0xf509, 0xf200, 0xf50b,
- 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf503, 0xf117,
- 0xf501, 0xf119, 0xf500, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-extern unsigned int keymap_count;
-
-/*
- * Misc. defines for testing
- */
-
-extern int console_loglevel;
-
-static struct adb_request led_request;
-extern int in_keybinit;
-
-/*
- * machdep keyboard routines, interface and key repeat method modeled after
- * drivers/macintosh/keyb_mac.c
- */
-
-int mac_kbd_translate(unsigned char keycode, unsigned char *keycodep,
- char raw_mode)
-{
- if (!raw_mode) {
- /*
- * Convert R-shift/control/option to L version.
- * Remap keycode 0 (A) to the unused keycode 0x5a.
- * Other parts of the system assume 0 is not a valid keycode.
- */
- switch (keycode) {
- case 0x7b: keycode = 0x38; break; /* R-shift */
- case 0x7c: keycode = 0x3a; break; /* R-option */
- case 0x7d: keycode = 0x36; break; /* R-control */
- case 0: keycode = 0x5a; break; /* A */
- }
- }
- *keycodep = keycode;
- return 1;
-}
-
-int mac_kbd_unexpected_up(unsigned char keycode)
-{
- return 0x80;
-}
-
-static void
-keyboard_input(unsigned char *data, int nb, struct pt_regs *regs)
-{
- /* first check this is from register 0 */
- if (nb != 5 || (data[2] & 3) != KEYB_KEYREG)
- return; /* ignore it */
- kbd_pt_regs = regs;
- input_keycode(data[3], 0);
- if (!(data[4] == 0xff || (data[4] == 0x7f && data[3] == 0x7f)))
- input_keycode(data[4], 0);
-}
-
-static void
-input_keycode(int keycode, int repeat)
-{
- struct kbd_struct *kbd;
- int up_flag;
-
- kbd = kbd_table + fg_console;
- up_flag = (keycode & 0x80);
- keycode &= 0x7f;
-
- if (!repeat)
- del_timer(&repeat_timer);
-
-#ifdef CONFIG_ADBMOUSE
- /*
- * XXX: Add mouse button 2+3 fake codes here if mouse open.
- * As we only report up/down events, keep track of faked buttons.
- * Really messy; might need to check if keyboard is in
- * VC_RAW mode for X?.
- * Might also want to know how many buttons need to be emulated.
- * -> hide this as function in arch/m68k/mac ?
- * Current emulation buttons: right alt/option and control
- * (wanted: command and alt/option, or KP= and KP( ...)
- * Debug version; might be rewritten to be faster on normal keys.
- */
- if (adb_emulate_buttons
- && (adb_mouse_interrupt_hook || console_loglevel >= 8)) {
- unsigned char button, button2, button3, fake_event;
- static unsigned char button2state=0, button3state=0; /* up */
- /* faked ADB packet */
- static unsigned char data[4] = { 0, 0x80, 0x80, 0x80 };
-
- button = 0;
- fake_event = 0;
- if (keycode == adb_button2_keycode) { /* which 'button' ? */
- /* R-option */
- button2 = (!up_flag); /* new state */
- if (button2 != button2state) /* change ? */
- button = 2;
- button2state = button2; /* save state */
- fake_event = 2;
- } else if (keycode == adb_button3_keycode) {
- /* R-control */
- button3 = (!up_flag); /* new state */
- if (button3 != button3state) /* change ? */
- button = 3;
- button3state = button3; /* save state */
- fake_event = 3;
- }
-#ifdef DEBUG_ADBMOUSE
- if (fake_event && console_loglevel >= 8)
- printk("fake event: button2 %d button3 %d button %d\n",
- button2state, button3state, button);
-#endif
- if (button) { /* there's been a button state change */
- /* fake a mouse packet : send all bytes, change one! */
- data[button] = (up_flag ? 0x80 : 0);
- if (adb_mouse_interrupt_hook)
- adb_mouse_interrupt_hook(data, 4);
-#ifdef DEBUG_ADBMOUSE
- else
- printk("mouse_fake: data %2x %2x %2x buttons %2x \n",
- data[1], data[2], data[3],
- ~( (data[1] & 0x80 ? 0 : 4)
- | (data[2] & 0x80 ? 0 : 1)
- | (data[3] & 0x80 ? 0 : 2) )&7 );
-#endif
- }
- /*
- * for mouse 3-button emulation: don't process 'fake' keys!
- * Keys might autorepeat, and console state gets generally messed
- * up enough so that selection stops working.
- */
- if (fake_event)
- return;
- }
-#endif /* CONFIG_ADBMOUSE */
-
- /*
- * Convert R-shift/control/option to L version.
- */
- switch (keycode) {
- case 0x7b: keycode = 0x38; break; /* R-shift */
- case 0x7c: keycode = 0x3a; break; /* R-option */
- case 0x7d: keycode = 0x36; break; /* R-control */
- case 0x0: if (kbd->kbdmode != VC_RAW)
- keycode = 0x5a; /* A; keycode 0 deprecated */
- break;
- }
-
- if (kbd->kbdmode != VC_RAW) {
- if (!up_flag && !dont_repeat[keycode]) {
- last_keycode = keycode;
- repeat_timer.expires = jiffies + (repeat? HZ/15: HZ/2);
- add_timer(&repeat_timer);
- }
-
- /*
- * XXX fix caps-lock behaviour by turning the key-up
- * transition into a key-down transition.
- * MSch: need to turn each caps-lock event into a down-up
- * double event (keyboard code assumes caps-lock is a toggle)
- * 981127: fix LED behavior (kudos atong!)
- */
- switch (keycode) {
- case 0x39:
- handle_scancode(keycode, 1); /* down */
- up_flag = 0x80; /* see below ... */
- mark_bh(KEYBOARD_BH);
- break;
- case 0x47:
- mark_bh(KEYBOARD_BH);
- break;
- }
- }
-
- handle_scancode(keycode, !up_flag);
-}
-
-static void
-kbd_repeat(unsigned long xxx)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
- input_keycode(last_keycode, 1);
- restore_flags(flags);
-}
-
- /* [ACA:23-Mar-97] Three button mouse support. This is designed to
- function with MkLinux DR-2.1 style X servers. It only works with
- three-button mice that conform to Apple's multi-button mouse
- protocol. */
-
- /*
- The X server for MkLinux DR2.1 uses the following unused keycodes to
- read the mouse:
-
- 0x7e This indicates that the next two keycodes should be interpreted
- as mouse information. The first following byte's high bit
- represents the state of the left button. The lower seven bits
- represent the x-axis acceleration. The lower seven bits of the
- second byte represent y-axis acceleration.
-
- 0x3f The x server interprets this keycode as a middle button
- release.
-
- 0xbf The x server interprets this keycode as a middle button
- depress.
-
- 0x40 The x server interprets this keycode as a right button
- release.
-
- 0xc0 The x server interprets this keycode as a right button
- depress.
-
- NOTES: There should be a better way of handling mice in the X server.
- The MOUSE_ESCAPE code (0x7e) should be followed by three bytes instead
- of two. The three mouse buttons should then, in the X server, be read
- as the high-bits of all three bytes. The x and y motions can still be
- in the first two bytes. Maybe I'll do this...
- */
-
- /*
- Handler 4 -- Apple Extended mouse protocol.
-
- For Apple's 3-button mouse protocol the data array will contain the
- following values:
-
- BITS COMMENTS
- data[0] = 0000 0000 ADB packet identifer.
- data[1] = 0100 0000 Extended protocol register.
- Bits 6-7 are the device id, which should be 1.
- Bits 4-5 are resolution which is in "units/inch".
- The Logitech MouseMan returns these bits clear but it has
- 200/300cpi resolution.
- Bits 0-3 are unique vendor id.
- data[2] = 0011 1100 Bits 0-1 should be zero for a mouse device.
- Bits 2-3 should be 8 + 4.
- Bits 4-7 should be 3 for a mouse device.
- data[3] = bxxx xxxx Left button and x-axis motion.
- data[4] = byyy yyyy Second button and y-axis motion.
- data[5] = byyy bxxx Third button and fourth button. Y is additional
- high bits of y-axis motion. XY is additional
- high bits of x-axis motion.
-
- NOTE: data[0] and data[2] are confirmed by the parent function and
- need not be checked here.
- */
-
- /*
- Handler 1 -- 100cpi original Apple mouse protocol.
- Handler 2 -- 200cpi original Apple mouse protocol.
-
- For Apple's standard one-button mouse protocol the data array will
- contain the following values:
-
- BITS COMMENTS
- data[0] = 0000 0000 ADB packet identifer.
- data[1] = ???? ???? (?)
- data[2] = ???? ??00 Bits 0-1 should be zero for a mouse device.
- data[3] = bxxx xxxx First button and x-axis motion.
- data[4] = byyy yyyy Second button and y-axis motion.
-
- NOTE: data[0] is confirmed by the parent function and need not be
- checked here.
- */
-
-static void
-mouse_input(unsigned char *data, int nb, struct pt_regs *regs)
-{
- struct kbd_struct *kbd;
- int i;
-
- if (nb < 5 || nb > 6 || (data[2] & 3) != MOUSE_DATAREG) {
- printk("data from mouse:");
- for (i = 0; i < nb; ++i)
- printk(" %x", data[i]);
- printk("\n");
- return;
- }
-
- if (adb_mouse_interrupt_hook) {
- adb_mouse_interrupt_hook(data+2, nb-2);
- /*
- * passing the mouse data to i.e. the X server as done for
- * Xpmac will confuse applications on a sane X server :-)
- */
- return;
- }
-#ifdef DEBUG_ADBMOUSE
- else
- if (console_loglevel >= 8)
- printk("mouse_input: data %x %x %x buttons %x dx %d dy %d \n",
- data[3], data[4], data[5],
- ~((data[3] & 0x80 ? 0 : 4)
- | (data[4] & 0x80 ? 0 : 1)
- | (data[5] & 0x80 ? 0 : 2))&7,
- ((data[4]&0x7f) < 64 ? (data[4]&0x7f) : (data[4]&0x7f)-128 ),
- ((data[3]&0x7f) < 64 ? -(data[3]&0x7f) : 128-(data[3]&0x7f) ) );
-#endif
-
-
- kbd = kbd_table + fg_console;
-
-#if 0 /* The entirely insane way of MkLinux handling mouse input */
- /* Requires put_queue which is static in keyboard.c :-( */
- /* Only send mouse codes when keyboard is in raw mode. */
- if (kbd->kbdmode == VC_RAW) {
- static unsigned char uch_ButtonStateSecond = 0;
- unsigned char uchButtonSecond;
-
- /* Send first button, second button and movement. */
- put_queue( 0x7e );
- put_queue( data[3] );
- put_queue( data[4] );
-
- /* [ACA: Are there any two-button ADB mice that use handler 1 or 2?] */
-
- /* Store the button state. */
- uchButtonSecond = (data[4] & 0x80);
-
- /* Send second button. */
- if (uchButtonSecond != uch_ButtonStateSecond) {
- put_queue( 0x3f | uchButtonSecond );
- uch_ButtonStateSecond = uchButtonSecond;
- }
-
- /* Macintosh 3-button mouse (handler 4). */
- if ((nb == 6) && (data[1] & 0x40)) {
- static unsigned char uch_ButtonStateThird = 0;
- unsigned char uchButtonThird;
-
- /* Store the button state for speed. */
- uchButtonThird = (data[5] & 0x80);
-
- /* Send third button. */
- if (uchButtonThird != uch_ButtonStateThird) {
- put_queue( 0x40 | uchButtonThird );
- uch_ButtonStateThird = uchButtonThird;
- }
- }
- }
-#endif /* insane MkLinux mouse hack */
-}
-
-/* Map led flags as defined in kbd_kern.h to bits for Apple keyboard. */
-static unsigned char mac_ledmap[8] = {
- 0, /* none */
- 4, /* scroll lock */
- 1, /* num lock */
- 5, /* scroll + num lock */
- 2, /* caps lock */
- 6, /* caps + scroll lock */
- 3, /* caps + num lock */
- 7, /* caps + num + scroll lock */
-};
-
-static int leds_pending;
-
-void mac_kbd_leds(unsigned int leds)
-{
- if (led_request.got_reply) {
-#ifdef DEBUG_ADB
- if (console_loglevel == 10)
- printk("mac_kbd_leds: got reply, sending request!\n");
-#endif
- adb_request(&led_request, mac_leds_done, 4, ADB_PACKET,
- ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG),
- 0xff, ~mac_ledmap[leds]);
- } else
- leds_pending = leds | 0x100;
-}
-
-static void mac_leds_done(struct adb_request *req)
-{
- int leds;
-
- if (leds_pending) {
- leds = leds_pending & 0xff;
- leds_pending = 0;
- mac_kbd_leds(leds);
- }
- mark_bh(KEYBOARD_BH);
-}
-
-int mac_kbdrate(struct kbd_repeat *k)
-{
- return 0;
-}
-
-int __init mac_keyb_init(void)
-{
- static struct adb_request autopoll_req, confcod_req, mouse_req, readkey_req;
- volatile int ct;
-
- /* setup key map */
- memcpy(key_maps[0], mac_plain_map, sizeof(plain_map));
- memcpy(key_maps[1], mac_shift_map, sizeof(plain_map));
- memcpy(key_maps[2], mac_altgr_map, sizeof(plain_map));
- memcpy(key_maps[4], mac_ctrl_map, sizeof(plain_map));
- memcpy(key_maps[5], mac_shift_ctrl_map, sizeof(plain_map));
- memcpy(key_maps[8], mac_alt_map, sizeof(plain_map));
- memcpy(key_maps[12], mac_ctrl_alt_map, sizeof(plain_map));
-
- /* initialize mouse interrupt hook */
- adb_mouse_interrupt_hook = NULL;
-
- /*
- * Might put that someplace else, possibly ....
- */
- adb_bus_init();
-
- /* the input functions ... */
- adb_register(ADB_KEYBOARD, keyboard_input);
- adb_register(ADB_MOUSE, mouse_input);
-
- /* turn on ADB auto-polling in the CUDA */
-
- /*
- * Older boxes don't support CUDA_* targets and CUDA commands
- * instead we emulate them in the adb_request hook to make
- * the code interfaces saner.
- *
- * Note XXX: the Linux PMac and this code both assume the
- * devices are at their primary ids and do not do device
- * assignment. This isn't ideal. We should fix it to follow
- * the reassignment specs.
- */
-
- if (macintosh_config->adb_type == MAC_ADB_CUDA) {
- printk("CUDA autopoll on ...\n");
- adb_request(&autopoll_req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1);
- ct=0;
- while (!autopoll_req.got_reply && ++ct<1000)
- {
- udelay(10);
- }
- if(ct==1000) {
- printk("Keyboard timed out.\n");
- autopoll_req.got_reply = 1;
- }
- }
-
- /*
- * XXX: all ADB requests now in CUDA format; adb_request takes
- * care of that for other Macs.
- */
-
- printk("Configuring keyboard:\n");
-
- udelay(3000);
-
- /*
- * turn on all leds - the keyboard driver will turn them back off
- * via mac_kbd_leds if everything works ok!
- */
- printk("leds on ...\n");
- adb_request(&led_request, NULL, 4, ADB_PACKET,
- ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG), 0xff, ~7);
-
- /*
- * The polling stuff should go away as soon as the ADB driver is stable
- */
- ct = 0;
- while (!led_request.got_reply && ++ct<1000)
- {
- udelay(10);
- }
- if(ct==1000) {
- printk("keyboard timed out.\n");
- led_request.got_reply = 1;
- }
-
-#if 1
- printk("configuring coding mode ...\n");
-
- udelay(3000);
-
- /*
- * get the keyboard to send separate codes for
- * left and right shift, control, option keys.
- */
- adb_request(&confcod_req, NULL, 4, ADB_PACKET,
- ADB_WRITEREG(ADB_KEYBOARD, 3), 0, 3);
-
- ct=0;
- while (!confcod_req.got_reply && ++ct<1000)
- {
- udelay(10);
- }
- if(ct==1000) {
- printk("keyboard timed out.\n");
- confcod_req.got_reply = 1;
- }
-#endif
-
-#if 0 /* seems to hurt, at least Geert's Mac */
- printk("Configuring mouse (3-button mode) ...\n");
-
- udelay(3000);
-
- /*
- * XXX: taken from the PPC driver again ...
- * Try to switch the mouse (id 3) to handler 4, for three-button
- * mode. (0x20 is Service Request Enable, 0x03 is Device ID).
- */
- adb_request(&mouse_req, NULL, 4, ADB_PACKET,
- ADB_WRITEREG(ADB_MOUSE, 3), 0x23, 4 );
-
- ct=0;
- while (!mouse_req.got_reply && ++ct<1000)
- {
- udelay(10);
- }
- if(ct==1000)
- printk("Mouse timed out.\n");
-#endif
-
-#if 0
- printk("Start polling keyboard ...\n");
-
- /*
- * get the keyboard to send data back, via the adb_input hook
- * XXX: was never used properly, and the driver takes care
- * of polling and timeout retransmits now.
- * Might be of use if we want to start talking to a specific
- * device here...
- */
- adb_request(&readkey_req, NULL, 2, ADB_PACKET,
- ADB_READREG(ADB_KEYBOARD, KEYB_KEYREG));
-#endif
-
- in_keybinit = 0;
- printk("keyboard init done\n");
-
- return 0;
-}
--- /dev/null
+/*
+ * Miscellaneous Mac68K-specific stuff
+ */
+
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/kd.h>
+#include <linux/mm.h>
+
+#include <linux/adb.h>
+#include <linux/cuda.h>
+#include <linux/pmu.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/setup.h>
+#include <asm/macintosh.h>
+#include <asm/mac_via.h>
+#include <asm/mac_oss.h>
+
+#define BOOTINFO_COMPAT_1_0
+#include <asm/setup.h>
+#include <asm/bootinfo.h>
+#include <asm/machdep.h>
+
+/* Offset between Unix time (1970-based) and Mac time (1904-based) */
+
+#define RTC_OFFSET 2082844800
+
+extern struct mac_booter_data mac_bi_data;
+static void (*rom_reset)(void);
+
+/*
+ * Return the current time as the number of seconds since January 1, 1904.
+ */
+
+static long adb_read_time(void)
+{
+ volatile struct adb_request req;
+ long time;
+
+ adb_request((struct adb_request *) &req, NULL,
+ ADBREQ_RAW|ADBREQ_SYNC,
+ 2, CUDA_PACKET, CUDA_GET_TIME);
+
+ time = (req.reply[3] << 24) | (req.reply[4] << 16)
+ | (req.reply[5] << 8) | req.reply[6];
+ return time - RTC_OFFSET;
+}
+
+/*
+ * Set the current system time
+ */
+
+static void adb_write_time(long data)
+{
+ volatile struct adb_request req;
+
+ data += RTC_OFFSET;
+
+ adb_request((struct adb_request *) &req, NULL,
+ ADBREQ_RAW|ADBREQ_SYNC,
+ 6, CUDA_PACKET, CUDA_SET_TIME,
+ (data >> 24) & 0xFF, (data >> 16) & 0xFF,
+ (data >> 8) & 0xFF, data & 0xFF);
+}
+
+/*
+ * Get a byte from the NVRAM
+ */
+
+static __u8 adb_read_pram(int offset)
+{
+ volatile struct adb_request req;
+
+ adb_request((struct adb_request *) &req, NULL,
+ ADBREQ_RAW|ADBREQ_SYNC,
+ 4, CUDA_PACKET, CUDA_GET_PRAM,
+ (offset >> 8) & 0xFF, offset & 0xFF);
+ return req.reply[3];
+}
+
+/*
+ * Write a byte to the NVRAM
+ */
+
+static void adb_write_pram(int offset, __u8 data)
+{
+ volatile struct adb_request req;
+
+ adb_request((struct adb_request *) &req, NULL,
+ ADBREQ_RAW|ADBREQ_SYNC,
+ 5, CUDA_PACKET, CUDA_SET_PRAM,
+ (offset >> 8) & 0xFF, offset & 0xFF,
+ data);
+}
+
+/*
+ * VIA PRAM/RTC access routines
+ *
+ * Must be called with interrupts disabled and
+ * the RTC should be enabled.
+ */
+
+static __u8 via_pram_readbyte(void)
+{
+ int i,reg;
+ __u8 data;
+
+ reg = via1[vBufB] & ~VIA1B_vRTCClk;
+
+ /* Set the RTC data line to be an input. */
+
+ via1[vDirB] &= ~VIA1B_vRTCData;
+
+ /* The bits of the byte come out in MSB order */
+
+ data = 0;
+ for (i = 0 ; i < 8 ; i++) {
+ via1[vBufB] = reg;
+ via1[vBufB] = reg | VIA1B_vRTCClk;
+ data = (data << 1) | (via1[vBufB] & VIA1B_vRTCData);
+ }
+
+ /* Return RTC data line to output state */
+
+ via1[vDirB] |= VIA1B_vRTCData;
+
+ return data;
+}
+
+static void via_pram_writebyte(__u8 data)
+{
+ int i,reg,bit;
+
+ reg = via1[vBufB] & ~(VIA1B_vRTCClk | VIA1B_vRTCData);
+
+ /* The bits of the byte go in in MSB order */
+
+ for (i = 0 ; i < 8 ; i++) {
+ bit = data & 0x80? 1 : 0;
+ data <<= 1;
+ via1[vBufB] = reg | bit;
+ via1[vBufB] = reg | bit | VIA1B_vRTCClk;
+ }
+}
+
+/*
+ * Execute a VIA PRAM/RTC command. For read commands
+ * data should point to a one-byte buffer for the
+ * resulting data. For write commands it should point
+ * to the data byte to for the command.
+ *
+ * This function disables all interrupts while running.
+ */
+
+static void via_pram_command(int command, __u8 *data)
+{
+ unsigned long cpu_flags;
+ int is_read;
+
+ save_flags(cpu_flags);
+ cli();
+
+ /* Enable the RTC and make sure the strobe line is high */
+
+ via1[vBufB] = (via1[vBufB] | VIA1B_vRTCClk) & ~VIA1B_vRTCEnb;
+
+ if (command & 0xFF00) { /* extended (two-byte) command */
+ via_pram_writebyte((command & 0xFF00) >> 8);
+ via_pram_writebyte(command & 0xFF);
+ is_read = command & 0x8000;
+ } else { /* one-byte command */
+ via_pram_writebyte(command);
+ is_read = command & 0x80;
+ }
+ if (is_read) {
+ *data = via_pram_readbyte();
+ } else {
+ via_pram_writebyte(*data);
+ }
+
+ /* All done, disable the RTC */
+
+ via1[vBufB] |= VIA1B_vRTCEnb;
+
+ restore_flags(cpu_flags);
+}
+
+static __u8 via_read_pram(int offset)
+{
+ return 0;
+}
+
+static void via_write_pram(int offset, __u8 data)
+{
+}
+
+/*
+ * Return the current time in seconds since January 1, 1904.
+ *
+ * This only works on machines with the VIA-based PRAM/RTC, which
+ * is basically any machine with Mac II-style ADB.
+ */
+
+static long via_read_time(void)
+{
+ union {
+ __u8 cdata[4];
+ long idata;
+ } result, last_result;
+ int ct;
+
+ /*
+ * The NetBSD guys say to loop until you get the same reading
+ * twice in a row.
+ */
+
+ ct = 0;
+ do {
+ if (++ct > 10) {
+ printk("via_read_time: couldn't get valid time, "
+ "last read = 0x%08X and 0x%08X\n", last_result.idata,
+ result.idata);
+ break;
+ }
+
+ last_result.idata = result.idata;
+ result.idata = 0;
+
+ via_pram_command(0x81, &result.cdata[3]);
+ via_pram_command(0x85, &result.cdata[2]);
+ via_pram_command(0x89, &result.cdata[1]);
+ via_pram_command(0x8D, &result.cdata[0]);
+ } while (result.idata != last_result.idata);
+
+ return result.idata - RTC_OFFSET;
+}
+
+/*
+ * Set the current time to a number of seconds since January 1, 1904.
+ *
+ * This only works on machines with the VIA-based PRAM/RTC, which
+ * is basically any machine with Mac II-style ADB.
+ */
+
+static void via_write_time(long time)
+{
+ union {
+ __u8 cdata[4];
+ long idata;
+ } data;
+ __u8 temp;
+
+ /* Clear the write protect bit */
+
+ temp = 0x55;
+ via_pram_command(0x35, &temp);
+
+ data.idata = time + RTC_OFFSET;
+ via_pram_command(0x01, &data.cdata[3]);
+ via_pram_command(0x05, &data.cdata[2]);
+ via_pram_command(0x09, &data.cdata[1]);
+ via_pram_command(0x0D, &data.cdata[0]);
+
+ /* Set the write protect bit */
+
+ temp = 0xD5;
+ via_pram_command(0x35, &temp);
+}
+
+static void via_shutdown(void)
+{
+ if (rbv_present) {
+ via2[rBufB] &= ~0x04;
+ } else {
+ /* Direction of vDirB is output */
+ via2[vDirB] |= 0x04;
+ /* Send a value of 0 on that line */
+ via2[vBufB] &= ~0x04;
+ mdelay(1000);
+ }
+}
+
+/*
+ * FIXME: not sure how this is supposed to work exactly...
+ */
+
+static void oss_shutdown(void)
+{
+ oss->rom_ctrl = OSS_POWEROFF;
+}
+
+#ifdef CONFIG_ADB_CUDA
+
+static void cuda_restart(void)
+{
+ adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+ 2, CUDA_PACKET, CUDA_RESET_SYSTEM);
+}
+
+static void cuda_shutdown(void)
+{
+ adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+ 2, CUDA_PACKET, CUDA_POWERDOWN);
+}
+
+#endif /* CONFIG_ADB_CUDA */
+
+#ifdef CONFIG_ADB_PMU
+
+void pmu_restart(void)
+{
+ adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+ 3, PMU_PACKET, PMU_SET_INTR_MASK,
+ PMU_INT_ADB|PMU_INT_TICK);
+
+ adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+ 2, PMU_PACKET, PMU_RESET);
+}
+
+void pmu_shutdown(void)
+{
+ adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+ 3, PMU_PACKET, PMU_SET_INTR_MASK,
+ PMU_INT_ADB|PMU_INT_TICK);
+
+ adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+ 6, PMU_PACKET, PMU_SHUTDOWN,
+ 'M', 'A', 'T', 'T');
+}
+
+#endif /* CONFIG_ADB_PMU */
+
+/*
+ *-------------------------------------------------------------------
+ * Below this point are the generic routines; they'll dispatch to the
+ * correct routine for the hardware on which we're running.
+ *-------------------------------------------------------------------
+ */
+
+void mac_pram_read(int offset, __u8 *buffer, int len)
+{
+ __u8 (*func)(int) = NULL;
+ int i;
+
+ if (macintosh_config->adb_type == MAC_ADB_IISI ||
+ macintosh_config->adb_type == MAC_ADB_PB1 ||
+ macintosh_config->adb_type == MAC_ADB_PB2 ||
+ macintosh_config->adb_type == MAC_ADB_CUDA) {
+ func = adb_read_pram;
+ } else {
+ func = via_read_pram;
+ }
+ for (i = 0 ; i < len ; i++) {
+ buffer[i] = (*func)(offset++);
+ }
+}
+
+void mac_pram_write(int offset, __u8 *buffer, int len)
+{
+ void (*func)(int, __u8) = NULL;
+ int i;
+
+ if (macintosh_config->adb_type == MAC_ADB_IISI ||
+ macintosh_config->adb_type == MAC_ADB_PB1 ||
+ macintosh_config->adb_type == MAC_ADB_PB2 ||
+ macintosh_config->adb_type == MAC_ADB_CUDA) {
+ func = adb_write_pram;
+ } else {
+ func = via_write_pram;
+ }
+ for (i = 0 ; i < len ; i++) {
+ (*func)(offset++, buffer[i]);
+ }
+}
+
+void mac_poweroff(void)
+{
+ /*
+ * MAC_ADB_IISI may need to be moved up here if it doesn't actually
+ * work using the ADB packet method. --David Kilzer
+ */
+
+ if (oss_present) {
+ oss_shutdown();
+ } else if (macintosh_config->adb_type == MAC_ADB_II) {
+ via_shutdown();
+#ifdef CONFIG_ADB_CUDA
+ } else if (macintosh_config->adb_type == MAC_ADB_CUDA) {
+ cuda_shutdown();
+#endif
+#ifdef CONFIG_ADB_PMU
+ } else if (macintosh_config->adb_type == MAC_ADB_PB1
+ || macintosh_config->adb_type == MAC_ADB_PB2) {
+ pmu_shutdown();
+#endif
+ }
+ sti();
+ printk("It is now safe to turn off your Macintosh.\n");
+ while(1);
+}
+
+void mac_reset(void)
+{
+ if (macintosh_config->adb_type == MAC_ADB_II) {
+ unsigned long cpu_flags;
+
+ /* need ROMBASE in booter */
+ /* indeed, plus need to MAP THE ROM !! */
+
+ if (mac_bi_data.rombase == 0)
+ mac_bi_data.rombase = 0x40800000;
+
+ /* works on some */
+ rom_reset = (void *) (mac_bi_data.rombase + 0xa);
+
+ if (macintosh_config->ident == MAC_MODEL_SE30) {
+ /*
+ * MSch: Machines known to crash on ROM reset ...
+ */
+ } else {
+ save_flags(cpu_flags);
+ cli();
+
+ rom_reset();
+
+ restore_flags(cpu_flags);
+ }
+#ifdef CONFIG_ADB_CUDA
+ } else if (macintosh_config->adb_type == MAC_ADB_CUDA) {
+ cuda_restart();
+#endif
+#ifdef CONFIG_ADB_PMU
+ } else if (macintosh_config->adb_type == MAC_ADB_PB1
+ || macintosh_config->adb_type == MAC_ADB_PB2) {
+ pmu_restart();
+#endif
+ } else if (CPU_IS_030) {
+
+ /* 030-specific reset routine. The idea is general, but the
+ * specific registers to reset are '030-specific. Until I
+ * have a non-030 machine, I can't test anything else.
+ * -- C. Scott Ananian <cananian@alumni.princeton.edu>
+ */
+
+ unsigned long rombase = 0x40000000;
+
+ /* make a 1-to-1 mapping, using the transparent tran. reg. */
+ unsigned long virt = (unsigned long) mac_reset;
+ unsigned long phys = virt_to_phys(mac_reset);
+ unsigned long offset = phys-virt;
+ cli(); /* lets not screw this up, ok? */
+ __asm__ __volatile__(".chip 68030\n\t"
+ "pmove %0,%/tt0\n\t"
+ ".chip 68k"
+ : : "m" ((phys&0xFF000000)|0x8777));
+ /* Now jump to physical address so we can disable MMU */
+ __asm__ __volatile__(
+ ".chip 68030\n\t"
+ "lea %/pc@(1f),%/a0\n\t"
+ "addl %0,%/a0\n\t"/* fixup target address and stack ptr */
+ "addl %0,%/sp\n\t"
+ "pflusha\n\t"
+ "jmp %/a0@\n\t" /* jump into physical memory */
+ "0:.long 0\n\t" /* a constant zero. */
+ /* OK. Now reset everything and jump to reset vector. */
+ "1:\n\t"
+ "lea %/pc@(0b),%/a0\n\t"
+ "pmove %/a0@, %/tc\n\t" /* disable mmu */
+ "pmove %/a0@, %/tt0\n\t" /* disable tt0 */
+ "pmove %/a0@, %/tt1\n\t" /* disable tt1 */
+ "movel #0, %/a0\n\t"
+ "movec %/a0, %/vbr\n\t" /* clear vector base register */
+ "movec %/a0, %/cacr\n\t" /* disable caches */
+ "movel #0x0808,%/a0\n\t"
+ "movec %/a0, %/cacr\n\t" /* flush i&d caches */
+ "movew #0x2700,%/sr\n\t" /* set up status register */
+ "movel %1@(0x0),%/a0\n\t"/* load interrupt stack pointer */
+ "movec %/a0, %/isp\n\t"
+ "movel %1@(0x4),%/a0\n\t" /* load reset vector */
+ "reset\n\t" /* reset external devices */
+ "jmp %/a0@\n\t" /* jump to the reset vector */
+ ".chip 68k"
+ : : "r" (offset), "a" (rombase) : "a0");
+ }
+
+ /* should never get here */
+ sti();
+ printk ("Restart failed. Please restart manually.\n");
+ while(1);
+}
+
+/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
+ * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
+ * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
+ *
+ * [For the Julian calendar (which was used in Russia before 1917,
+ * Britain & colonies before 1752, anywhere else before 1582,
+ * and is still in use by some communities) leave out the
+ * -year/100+year/400 terms, and add 10.]
+ *
+ * This algorithm was first published by Gauss (I think).
+ *
+ * WARNING: this function will overflow on 2106-02-07 06:28:16 on
+ * machines were long is 32-bit! (However, as time_t is signed, we
+ * will already get problems at other places on 2038-01-19 03:14:08)
+ */
+static unsigned long mktime(unsigned int year, unsigned int mon,
+ unsigned int day, unsigned int hour,
+ unsigned int min, unsigned int sec)
+{
+ if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */
+ mon += 12; /* Puts Feb last since it has leap day */
+ year -= 1;
+ }
+ return (((
+ (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
+ year*365 - 719499
+ )*24 + hour /* now have hours */
+ )*60 + min /* now have minutes */
+ )*60 + sec; /* finally seconds */
+}
+
+/*
+ * This function translates seconds since 1970 into a proper date.
+ *
+ * Algorithm cribbed from glibc2.1, __offtime().
+ */
+#define SECS_PER_MINUTE (60)
+#define SECS_PER_HOUR (SECS_PER_MINUTE * 60)
+#define SECS_PER_DAY (SECS_PER_HOUR * 24)
+
+static void unmktime(unsigned long time, long offset,
+ int *yearp, int *monp, int *dayp,
+ int *hourp, int *minp, int *secp)
+{
+ /* How many days come before each month (0-12). */
+ static const unsigned short int __mon_yday[2][13] =
+ {
+ /* Normal years. */
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ /* Leap years. */
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+ };
+ long int days, rem, y, wday, yday;
+ const unsigned short int *ip;
+
+ days = time / SECS_PER_DAY;
+ rem = time % SECS_PER_DAY;
+ rem += offset;
+ while (rem < 0) {
+ rem += SECS_PER_DAY;
+ --days;
+ }
+ while (rem >= SECS_PER_DAY) {
+ rem -= SECS_PER_DAY;
+ ++days;
+ }
+ *hourp = rem / SECS_PER_HOUR;
+ rem %= SECS_PER_HOUR;
+ *minp = rem / SECS_PER_MINUTE;
+ *secp = rem % SECS_PER_MINUTE;
+ /* January 1, 1970 was a Thursday. */
+ wday = (4 + days) % 7; /* Day in the week. Not currently used */
+ if (wday < 0) wday += 7;
+ y = 1970;
+
+#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
+#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
+#define __isleap(year) \
+ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
+
+ while (days < 0 || days >= (__isleap (y) ? 366 : 365))
+ {
+ /* Guess a corrected year, assuming 365 days per year. */
+ long int yg = y + days / 365 - (days % 365 < 0);
+
+ /* Adjust DAYS and Y to match the guessed year. */
+ days -= ((yg - y) * 365
+ + LEAPS_THRU_END_OF (yg - 1)
+ - LEAPS_THRU_END_OF (y - 1));
+ y = yg;
+ }
+ *yearp = y - 1900;
+ yday = days; /* day in the year. Not currently used. */
+ ip = __mon_yday[__isleap(y)];
+ for (y = 11; days < (long int) ip[y]; --y)
+ continue;
+ days -= ip[y];
+ *monp = y;
+ *dayp = days + 1; /* day in the month */
+ return;
+}
+
+/*
+ * Return the boot time for use in initializing the kernel clock.
+ *
+ * I'd like to read the hardware clock here but many machines read
+ * the PRAM through ADB, and interrupts aren't initialized when this
+ * is called so ADB obviously won't work.
+ */
+
+void mac_gettod(int *yearp, int *monp, int *dayp,
+ int *hourp, int *minp, int *secp)
+{
+ /* Yes the GMT bias is backwards. It looks like Penguin is
+ screwing up the boottime it gives us... This works for me
+ in Canada/Eastern but it might be wrong everywhere else. */
+ unmktime(mac_bi_data.boottime, -mac_bi_data.gmtbias * 60,
+ yearp, monp, dayp, hourp, minp, secp);
+ /* For some reason this is off by one */
+ *monp = *monp + 1;
+}
+
+/*
+ * Read/write the hardware clock.
+ */
+
+int mac_hwclk(int op, struct hwclk_time *t)
+{
+ unsigned long now;
+
+ if (!op) { /* read */
+ if (macintosh_config->adb_type == MAC_ADB_II) {
+ now = via_read_time();
+ } else if ((macintosh_config->adb_type == MAC_ADB_IISI) ||
+ (macintosh_config->adb_type == MAC_ADB_PB1) ||
+ (macintosh_config->adb_type == MAC_ADB_PB2) ||
+ (macintosh_config->adb_type == MAC_ADB_CUDA)) {
+ now = adb_read_time();
+ } else if (macintosh_config->adb_type == MAC_ADB_IOP) {
+ now = via_read_time();
+ } else {
+ now = 0;
+ }
+
+ t->wday = 0;
+ unmktime(now, 0,
+ &t->year, &t->mon, &t->day,
+ &t->hour, &t->min, &t->sec);
+ printk("mac_hwclk: read %04d-%02d-%-2d %02d:%02d:%02d\n",
+ t->year + 1900, t->mon + 1, t->day, t->hour, t->min, t->sec);
+ } else { /* write */
+ printk("mac_hwclk: tried to write %04d-%02d-%-2d %02d:%02d:%02d\n",
+ t->year + 1900, t->mon + 1, t->day, t->hour, t->min, t->sec);
+
+#if 0 /* it trashes my rtc */
+ now = mktime(t->year + 1900, t->mon + 1, t->day,
+ t->hour, t->min, t->sec);
+
+ if (macintosh_config->adb_type == MAC_ADB_II) {
+ via_write_time(now);
+ } else if ((macintosh_config->adb_type == MAC_ADB_IISI) ||
+ (macintosh_config->adb_type == MAC_ADB_PB1) ||
+ (macintosh_config->adb_type == MAC_ADB_PB2) ||
+ (macintosh_config->adb_type == MAC_ADB_CUDA)) {
+ adb_write_time(now);
+ } else if (macintosh_config->adb_type == MAC_ADB_IOP) {
+ via_write_time(now);
+ }
+#endif
+ }
+ return 0;
+}
+
+/*
+ * Set minutes/seconds in the hardware clock
+ */
+
+int mac_set_clock_mmss (unsigned long nowtime)
+{
+ struct hwclk_time now;
+
+ mac_hwclk(0, &now);
+ now.sec = nowtime % 60;
+ now.min = (nowtime / 60) % 60;
+ mac_hwclk(1, &now);
+
+ return 0;
+}
void oss_nubus_irq(int, void *, struct pt_regs *);
extern void via1_irq(int, void *, struct pt_regs *);
-extern void mac_SCC_handler(int, void *, struct pt_regs *);
+extern void mac_scc_dispatch(int, void *, struct pt_regs *);
extern int console_loglevel;
/*
void __init oss_register_interrupts(void)
{
- request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK,
- "OSS SCSI Dispatch", (void *) oss);
- request_irq(OSS_IRQLEV_IOPSCC, mac_SCC_handler, IRQ_FLG_LOCK,
- "SCC Dispatch", mac_SCC_handler);
- request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK,
- "Nubus Dispatch", (void *) oss);
- request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK,
- "OSS Sound Dispatch", (void *) oss);
- request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK,
- "VIA1 Dispatch", (void *) via1);
+ sys_request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK,
+ "scsi", (void *) oss);
+ sys_request_irq(OSS_IRQLEV_IOPSCC, mac_scc_dispatch, IRQ_FLG_LOCK,
+ "scc", mac_scc_dispatch);
+ sys_request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK,
+ "nubus", (void *) oss);
+ sys_request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK,
+ "sound", (void *) oss);
+ sys_request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK,
+ "via1", (void *) via1);
}
/*
{
}
-/*
- * Turn off the power via the ROM control register
- *
- * FIXME: not sure how this is supposed to work exactly...
- */
-
-void oss_poweroff(void)
-{
- oss->rom_ctrl = OSS_POWEROFF;
-
- /* We should never make it this far... */
-
- printk ("It is now safe to switch off your machine.\n");
- while(1);
-}
-
/*
* Handle miscellaneous OSS interrupts. Right now that's just sound
* and SCSI; everything else is routed to its own autovector IRQ.
void __init psc_register_interrupts(void)
{
- request_irq(3, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch",
- (void *) 0x30);
- request_irq(4, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch",
- (void *) 0x40);
- request_irq(5, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch",
- (void *) 0x50);
- request_irq(6, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch",
- (void *) 0x60);
+ sys_request_irq(3, psc_irq, IRQ_FLG_LOCK, "psc3",
+ (void *) 0x30);
+ sys_request_irq(4, psc_irq, IRQ_FLG_LOCK, "psc4",
+ (void *) 0x40);
+ sys_request_irq(5, psc_irq, IRQ_FLG_LOCK, "psc5",
+ (void *) 0x50);
+ sys_request_irq(6, psc_irq, IRQ_FLG_LOCK, "psc6",
+ (void *) 0x60);
}
/*
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/ide.h>
+
#include <asm/traps.h>
#include <asm/bootinfo.h>
#include <asm/macintosh.h>
#define MAC_CLOCK_LOW (MAC_CLOCK_TICK&0xFF)
#define MAC_CLOCK_HIGH (MAC_CLOCK_TICK>>8)
-static int nubus_active;
+static int nubus_active = 0;
void via_debug_dump(void);
void via1_irq(int, void *, struct pt_regs *);
void via_irq_clear(int irq);
extern void mac_bang(int, void *, struct pt_regs *);
-extern void mac_SCC_handler(int, void *, struct pt_regs *);
+extern void mac_scc_dispatch(int, void *, struct pt_regs *);
extern int console_loglevel;
extern int oss_present;
void __init via_register_interrupts(void)
{
if (via_alt_mapping) {
- request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
- "Software IRQ", (void *) via1);
- request_irq(IRQ_AUTO_6, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
- "VIA1 Dispatch", (void *) via1);
+ sys_request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+ "software", (void *) via1);
+ sys_request_irq(IRQ_AUTO_6, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+ "via1", (void *) via1);
} else {
- request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
- "VIA1 Dispatch", (void *) via1);
+ sys_request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+ "via1", (void *) via1);
#if 0 /* interferes with serial on some machines */
if (!psc_present) {
- request_irq(IRQ_AUTO_6, mac_bang, IRQ_FLG_LOCK,
+ sys_request_irq(IRQ_AUTO_6, mac_bang, IRQ_FLG_LOCK,
"Off Switch", mac_bang);
}
#endif
}
- request_irq(IRQ_AUTO_2, via2_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
- "VIA2 Dispatch", (void *) via2);
+ sys_request_irq(IRQ_AUTO_2, via2_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+ "via2", (void *) via2);
if (!psc_present) {
- request_irq(IRQ_AUTO_4, mac_SCC_handler, IRQ_FLG_LOCK,
- "SCC Dispatch", mac_SCC_handler);
+ sys_request_irq(IRQ_AUTO_4, mac_scc_dispatch, IRQ_FLG_LOCK,
+ "scc", mac_scc_dispatch);
}
request_irq(IRQ_MAC_NUBUS, via_nubus_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
- "Nubus Dispatch", (void *) via2);
+ "nubus", (void *) via2);
}
/*
return (int) via2[gBufB] & VIA2B_vCDis;
}
-/*
- * VIA-based power switch, for machines that support it.
- */
-
-void via_poweroff(void)
-{
- if (rbv_present) {
- via2[rBufB] &= ~0x04;
- } else {
- /* Direction of vDirB is output */
- via2[vDirB] |= 0x04;
- /* Send a value of 0 on that line */
- via2[vBufB] &= ~0x04;
- /* Otherwise it prints "It is now.." then shuts off */
- mdelay(1000);
- }
-
- /* We should never make it this far... */
- printk ("It is now safe to switch off your machine.\n");
- while(1);
-}
-
/*
* Initialize VIA2 for Nubus access
*/
void __init via_nubus_init(void)
{
- nubus_active = 0;
+ /* don't set nubus_active = 0 here, it kills the Baboon */
+ /* interrupt that we've already registered. */
/* unlock nubus transactions */
/* set the line to be an output on non-RBV machines */
via2[vDirB] |= 0x02;
}
- via2[gBufB] |= 0x02;
+
+ /* this seems to be an ADB bit on PMU machines */
+ /* according to MkLinux. -- jmt */
+
+ if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
+ (macintosh_config->adb_type != MAC_ADB_PB2)) {
+ via2[gBufB] |= 0x02;
+ }
/* disable nubus slot interrupts. */
if (rbv_present) {
- via2[rSIER] = 0x7F; /* like VIA; bit 7=clr,set */
+ via2[rSIER] = 0x7F;
+ via2[rSIER] = nubus_active | 0x80;
} else {
- via2[vBufA] = 0xFF; /* active low irqs, force high */
- via2[vDirA] = 0xFF; /* ddr to output. */
+ via2[vBufA] = 0xFF;
+ via2[vDirA] = ~nubus_active;
}
}
int irq_bit, i;
unsigned char events;
- if (!(events = ~via2[gBufA] & nubus_active)) return;
+ if (!(events = ~via2[gBufA] & nubus_active)) return;
for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1) {
if (events & irq_bit) {
}
return 0;
}
-
-void via_scsi_clear(void)
-{
- volatile unsigned char deep_magic;
-
-#ifdef DEBUG_IRQUSE
- printk("via_scsi_clear()\n");
-#endif
-
- /* We handle this in oss.c , but this gets called in mac_scsinew.c */
- if(oss_present) return;
-
- if (rbv_present) {
- via2[rIFR] = (1<<3) | (1<<0) | rbv_clear;
- deep_magic = via2[rBufB];
- } else {
- deep_magic = via2[vBufB];
- }
- mac_enable_irq(IRQ_MAC_SCSI);
-}
-
-/*
- * PRAM/RTC access routines
- *
- * Must be called with interrupts disabled and
- * the RTC should be enabled.
- */
-
-static __u8 via_pram_readbyte(void)
-{
- int i,reg;
- __u8 data;
-
- reg = via1[vBufB] & ~VIA1B_vRTCClk;
-
- /* Set the RTC data line to be an input. */
-
- via1[vDirB] &= ~VIA1B_vRTCData;
-
- /* The bits of the byte come out in MSB order */
-
- data = 0;
- for (i = 0 ; i < 8 ; i++) {
- via1[vBufB] = reg;
- via1[vBufB] = reg | VIA1B_vRTCClk;
- data = (data << 1) | (via1[vBufB] & VIA1B_vRTCData);
- }
-
- /* Return RTC data line to output state */
-
- via1[vDirB] |= VIA1B_vRTCData;
-
- return data;
-}
-
-static void via_pram_writebyte(__u8 data)
-{
- int i,reg,bit;
-
- reg = via1[vBufB] & ~(VIA1B_vRTCClk | VIA1B_vRTCData);
-
- /* The bits of the byte go in in MSB order */
-
- for (i = 0 ; i < 8 ; i++) {
- bit = data & 0x80? 1 : 0;
- data <<= 1;
- via1[vBufB] = reg | bit;
- via1[vBufB] = reg | bit | VIA1B_vRTCClk;
- }
-}
-
-/*
- * Execute a PRAM/RTC command. For read commands
- * data should point to a one-byte buffer for the
- * resulting data. For write commands it should point
- * to the data byte to for the command.
- *
- * This function disables all interrupts while running.
- */
-
-void via_pram_command(int command, __u8 *data)
-{
- unsigned long cpu_flags;
- int is_read;
-
- save_flags(cpu_flags);
- cli();
-
- /* Enable the RTC and make sure the strobe line is high */
-
- via1[vBufB] = (via1[vBufB] | VIA1B_vRTCClk) & ~VIA1B_vRTCEnb;
-
- if (command & 0xFF00) { /* extended (two-byte) command */
- via_pram_writebyte((command & 0xFF00) >> 8);
- via_pram_writebyte(command & 0xFF);
- is_read = command & 0x8000;
- } else { /* one-byte command */
- via_pram_writebyte(command);
- is_read = command & 0x80;
- }
- if (is_read) {
- *data = via_pram_readbyte();
- } else {
- via_pram_writebyte(*data);
- }
-
- /* All done, disable the RTC */
-
- via1[vBufB] |= VIA1B_vRTCEnb;
-
- restore_flags(cpu_flags);
-}
-
-/*
- * Return the current time in seconds since January 1, 1904.
- *
- * This only works on machines with the VIA-based PRAM/RTC, which
- * is basically any machine with Mac II-style ADB.
- */
-
-__u32 via_read_time(void)
-{
- union {
- __u8 cdata[4];
- __u32 idata;
- } result, last_result;
- int ct;
-
- /*
- * The NetBSD guys say to loop until you get the same reading
- * twice in a row.
- */
-
- ct = 0;
- do {
- if (++ct > 10) {
- printk("via_read_time: couldn't get valid time, "
- "last read = 0x%08X and 0x%08X\n", last_result.idata,
- result.idata);
- break;
- }
-
- last_result.idata = result.idata;
- result.idata = 0;
-
- via_pram_command(0x81, &result.cdata[3]);
- via_pram_command(0x85, &result.cdata[2]);
- via_pram_command(0x89, &result.cdata[1]);
- via_pram_command(0x8D, &result.cdata[0]);
- } while (result.idata != last_result.idata);
-
- return result.idata;
-}
-
-/*
- * Set the current time to a number of seconds since January 1, 1904.
- *
- * This only works on machines with the VIA-based PRAM/RTC, which
- * is basically any machine with Mac II-style ADB.
- */
-
-void via_write_time(__u32 time)
-{
- union {
- __u8 cdata[4];
- __u32 idata;
- } data;
- __u8 temp;
-
- /* Clear the write protect bit */
-
- temp = 0x55;
- via_pram_command(0x35, &temp);
-
- data.idata = time;
- via_pram_command(0x01, &data.cdata[3]);
- via_pram_command(0x05, &data.cdata[2]);
- via_pram_command(0x09, &data.cdata[1]);
- via_pram_command(0x0D, &data.cdata[0]);
-
- /* Set the write protect bit */
-
- temp = 0xD5;
- via_pram_command(0x35, &temp);
-}
{
int chunk;
unsigned long mem_avail = 0;
- unsigned int zones_size[3] = { 0, };
+ unsigned long zones_size[3] = { 0, };
#ifdef DEBUG
{
extern void mmu_emu_init (void);
-extern unsigned long free_area_init(unsigned long, unsigned long);
+extern unsigned long free_area_init(unsigned long *zones_size);
const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n";
SERIAL =serial.o
ifeq ($(ARCH),m68k)
- KEYBD =
- SERIAL =
+ ifdef CONFIG_AMIGA
+ KEYBD = amikeyb.o
+ else
+ KEYBD =
+ endif
+ SERIAL =
endif
ifeq ($(ARCH),arm)
SERIAL =
endif
+
obj-$(CONFIG_VT) += vt.o vc_screen.o consolemap.o consolemap_deftbl.o $(CONSOLE) selection.o
obj-$(CONFIG_SERIAL) += $(SERIAL)
obj-$(CONFIG_SERIAL_21285) += serial_21285.o
obj-$(CONFIG_SYNCLINK) += synclink.o
obj-$(CONFIG_N_HDLC) += n_hdlc.o
obj-$(CONFIG_SPECIALIX) += specialix.o
+obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o
ifeq ($(CONFIG_SX),y)
obj-y += sx.o generic_serial.o
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/keyboard.h>
+#include <linux/kd.h>
+#include <linux/kbd_ll.h>
#include <linux/delay.h>
#include <linux/timer.h>
-#include <linux/kd.h>
#include <linux/random.h>
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/kbd_ll.h>
+#include <linux/kbd_kern.h>
#include <asm/amigaints.h>
#include <asm/amigahw.h>
/* switch CIA serial port to input mode */
ciaa.cra &= ~0x40;
- mark_bh(KEYBOARD_BH);
+ tasklet_schedule(&keyboard_tasklet);
/* rotate scan code to get up/down bit in proper position */
scancode = ((scancode >> 1) & 0x7f) | ((scancode << 7) & 0x80);
--- /dev/null
+/*
+ * linux/drivers/char/amiserial.c
+ *
+ * Serial driver for the amiga builtin port.
+ *
+ * This code was created by taking serial.c version 4.30 from kernel
+ * release 2.3.22, replacing all hardware related stuff with the
+ * corresponding amiga hardware actions, and removing all irrelevant
+ * code. As a consequence, it uses many of the constants and names
+ * associated with the registers and bits of 16550 compatible UARTS -
+ * but only to keep track of status, etc in the state variables. It
+ * was done this was to make it easier to keep the code in line with
+ * (non hardware specific) changes to serial.c.
+ *
+ * The port is registered with the tty driver as minor device 64, and
+ * therefore other ports should should only use 65 upwards.
+ *
+ * Richard Lucock 28/12/99
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997,
+ * 1998, 1999 Theodore Ts'o
+ *
+ */
+
+/*
+ * Serial driver configuration section. Here are the various options:
+ *
+ * SERIAL_PARANOIA_CHECK
+ * Check the magic number for the async_structure where
+ * ever possible.
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#undef SERIAL_PARANOIA_CHECK
+#define SERIAL_DO_RESTART
+
+/* Set of debugging defines */
+
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+
+/* Sanity checks */
+
+#define SERIAL_INLINE
+
+#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
+#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
+ kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s)
+#else
+#define DBG_CNT(s)
+#endif
+
+/*
+ * End of serial driver configuration section.
+ */
+
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/serial.h>
+#include <linux/serialP.h>
+#include <linux/serial_reg.h>
+static char *serial_version = "4.30";
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#ifdef CONFIG_AMIGA
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#endif
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+
+#ifdef SERIAL_INLINE
+#define _INLINE_ inline
+#endif
+
+static char *serial_name = "Amiga-builtin serial driver";
+
+static DECLARE_TASK_QUEUE(tq_serial);
+
+static struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* serial subtype definitions */
+#ifndef SERIAL_TYPE_NORMAL
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+#endif
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+static struct async_struct *IRQ_ports;
+
+static unsigned char current_ctl_bits;
+
+static void change_speed(struct async_struct *info, struct termios *old);
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
+
+
+static struct serial_state rs_table[1];
+
+#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state))
+
+
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write. We need to
+ * lock it in case the copy_from_user blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf;
+static DECLARE_MUTEX(tmp_buf_sem);
+
+#include <asm/uaccess.h>
+
+#define serial_isroot() (capable(CAP_SYS_ADMIN))
+
+
+static inline int serial_paranoia_check(struct async_struct *info,
+ kdev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+ static const char *badmagic =
+ "Warning: bad magic number for serial struct (%s) in %s\n";
+ static const char *badinfo =
+ "Warning: null async_struct for (%s) in %s\n";
+
+ if (!info) {
+ printk(badinfo, kdevname(device), routine);
+ return 1;
+ }
+ if (info->magic != SERIAL_MAGIC) {
+ printk(badmagic, kdevname(device), routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/* some serial hardware definitions */
+#define SDR_OVRUN (1<<15)
+#define SDR_RBF (1<<14)
+#define SDR_TBE (1<<13)
+#define SDR_TSRE (1<<12)
+
+#define SERPER_PARENB (1<<15)
+
+#define AC_SETCLR (1<<15)
+#define AC_UARTBRK (1<<11)
+
+#define SER_DTR (1<<7)
+#define SER_RTS (1<<6)
+#define SER_DCD (1<<5)
+#define SER_CTS (1<<4)
+#define SER_DSR (1<<3)
+
+static __inline__ void rtsdtr_ctrl(int bits)
+{
+ ciab.pra = ((bits & (SER_RTS | SER_DTR)) ^ (SER_RTS | SER_DTR)) | (ciab.pra & ~(SER_RTS | SER_DTR));
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_stop"))
+ return;
+
+ save_flags(flags); cli();
+ if (info->IER & UART_IER_THRI) {
+ info->IER &= ~UART_IER_THRI;
+ /* disable Tx interrupt and remove any pending interrupts */
+ custom.intena = IF_TBE;
+ mb();
+ custom.intreq = IF_TBE;
+ mb();
+ }
+ restore_flags(flags);
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_start"))
+ return;
+
+ save_flags(flags); cli();
+ if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) {
+ info->IER |= UART_IER_THRI;
+ custom.intena = IF_SETCLR | IF_TBE;
+ mb();
+ /* set a pending Tx Interrupt, transmitter should restart now */
+ custom.intreq = IF_SETCLR | IF_TBE;
+ mb();
+ }
+ restore_flags(flags);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines. All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt(). They were separated out for readability's sake.
+ *
+ * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off. People who may want to modify
+ * rs_interrupt() should try to keep the interrupt handler as fast as
+ * possible. After you are done making modifications, it is not a bad
+ * idea to do:
+ *
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void rs_sched_event(struct async_struct *info,
+ int event)
+{
+ info->event |= 1 << event;
+ queue_task(&info->tqueue, &tq_serial);
+ mark_bh(SERIAL_BH);
+}
+
+static _INLINE_ void receive_chars(struct async_struct *info)
+{
+ int status;
+ int serdatr;
+ struct tty_struct *tty = info->tty;
+ unsigned char ch;
+ struct async_icount *icount;
+
+ icount = &info->state->icount;
+
+ status = UART_LSR_DR; /* We obviously have a character! */
+ serdatr = custom.serdatr;
+ mb();
+ custom.intreq = IF_RBF;
+ mb();
+
+ if((serdatr & 0x1ff) == 0)
+ status |= UART_LSR_BI;
+ if(serdatr & SDR_OVRUN)
+ status |= UART_LSR_OE;
+
+ ch = serdatr & 0xff;
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+ goto ignore_char;
+ *tty->flip.char_buf_ptr = ch;
+ icount->rx++;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("DR%02x:%02x...", ch, status);
+#endif
+ *tty->flip.flag_buf_ptr = 0;
+
+ /*
+ * We don't handle parity or frame errors - but I have left
+ * the code in, since I'm not sure that the errors can't be
+ * detected.
+ */
+
+ if (status & (UART_LSR_BI | UART_LSR_PE |
+ UART_LSR_FE | UART_LSR_OE)) {
+ /*
+ * For statistics only
+ */
+ if (status & UART_LSR_BI) {
+ status &= ~(UART_LSR_FE | UART_LSR_PE);
+ icount->brk++;
+ } else if (status & UART_LSR_PE)
+ icount->parity++;
+ else if (status & UART_LSR_FE)
+ icount->frame++;
+ if (status & UART_LSR_OE)
+ icount->overrun++;
+
+ /*
+ * Now check to see if character should be
+ * ignored, and mask off conditions which
+ * should be ignored.
+ */
+ if (status & info->ignore_status_mask)
+ goto ignore_char;
+
+ status &= info->read_status_mask;
+
+ if (status & (UART_LSR_BI)) {
+#ifdef SERIAL_DEBUG_INTR
+ printk("handling break....");
+#endif
+ *tty->flip.flag_buf_ptr = TTY_BREAK;
+ if (info->flags & ASYNC_SAK)
+ do_SAK(tty);
+ } else if (status & UART_LSR_PE)
+ *tty->flip.flag_buf_ptr = TTY_PARITY;
+ else if (status & UART_LSR_FE)
+ *tty->flip.flag_buf_ptr = TTY_FRAME;
+ if (status & UART_LSR_OE) {
+ /*
+ * Overrun is special, since it's
+ * reported immediately, and doesn't
+ * affect the current character
+ */
+ if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+ tty->flip.count++;
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ *tty->flip.flag_buf_ptr = TTY_OVERRUN;
+ }
+ }
+ }
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ tty->flip.count++;
+ ignore_char:
+
+ tty_flip_buffer_push(tty);
+}
+
+static _INLINE_ void transmit_chars(struct async_struct *info)
+{
+ custom.intreq = IF_TBE;
+ mb();
+ if (info->x_char) {
+ custom.serdat = info->x_char | 0x100;
+ mb();
+ info->state->icount.tx++;
+ info->x_char = 0;
+ return;
+ }
+ if ((info->xmit_cnt <= 0) || info->tty->stopped ||
+ info->tty->hw_stopped) {
+ info->IER &= ~UART_IER_THRI;
+ custom.intena = IF_TBE;
+ mb();
+ return;
+ }
+
+ custom.serdat = info->xmit_buf[info->xmit_tail++] | 0x100;
+ mb();
+ info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+ info->state->icount.tx++;
+ --info->xmit_cnt;
+
+ if (info->xmit_cnt < WAKEUP_CHARS)
+ rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("THRE...");
+#endif
+ if (info->xmit_cnt <= 0) {
+ custom.intena = IF_TBE;
+ mb();
+ info->IER &= ~UART_IER_THRI;
+ }
+}
+
+static _INLINE_ void check_modem_status(struct async_struct *info)
+{
+ unsigned char status = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);
+ unsigned char dstatus;
+ struct async_icount *icount;
+
+ /* Determine bits that have changed */
+ dstatus = status ^ current_ctl_bits;
+ current_ctl_bits = status;
+
+ if (dstatus) {
+ icount = &info->state->icount;
+ /* update input line counters */
+ if (dstatus & SER_DSR)
+ icount->dsr++;
+ if (dstatus & SER_DCD) {
+ icount->dcd++;
+#ifdef CONFIG_HARD_PPS
+ if ((info->flags & ASYNC_HARDPPS_CD) &&
+ !(status & SER_DCD))
+ hardpps();
+#endif
+ }
+ if (dstatus & SER_CTS)
+ icount->cts++;
+ wake_up_interruptible(&info->delta_msr_wait);
+ }
+
+ if ((info->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) {
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+ printk("ttyS%02d CD now %s...", info->line,
+ (!(status & SER_DCD)) ? "on" : "off");
+#endif
+ if (!(status & SER_DCD))
+ wake_up_interruptible(&info->open_wait);
+ else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("doing serial hangup...");
+#endif
+ if (info->tty)
+ tty_hangup(info->tty);
+ }
+ }
+ if (info->flags & ASYNC_CTS_FLOW) {
+ if (info->tty->hw_stopped) {
+ if (!(status & SER_CTS)) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+ printk("CTS tx start...");
+#endif
+ info->tty->hw_stopped = 0;
+ info->IER |= UART_IER_THRI;
+ custom.intena = IF_SETCLR | IF_TBE;
+ mb();
+ /* set a pending Tx Interrupt, transmitter should restart now */
+ custom.intreq = IF_SETCLR | IF_TBE;
+ mb();
+ rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+ return;
+ }
+ } else {
+ if ((status & SER_CTS)) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+ printk("CTS tx stop...");
+#endif
+ info->tty->hw_stopped = 1;
+ info->IER &= ~UART_IER_THRI;
+ /* disable Tx interrupt and remove any pending interrupts */
+ custom.intena = IF_TBE;
+ mb();
+ custom.intreq = IF_TBE;
+ mb();
+ }
+ }
+ }
+}
+
+static void ser_vbl_int( int irq, void *data, struct pt_regs *regs)
+{
+ /* vbl is just a periodic interrupt we tie into to update modem status */
+ struct async_struct * info = IRQ_ports;
+ /*
+ * TBD - is it better to unregister from this interrupt or to
+ * ignore it if MSI is clear ?
+ */
+ if(info->IER & UART_IER_MSI)
+ check_modem_status(info);
+}
+
+static void ser_rx_int(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct async_struct * info;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("ser_rx_int...");
+#endif
+
+ info = IRQ_ports;
+ if (!info || !info->tty)
+ return;
+
+ receive_chars(info);
+ info->last_active = jiffies;
+#ifdef SERIAL_DEBUG_INTR
+ printk("end.\n");
+#endif
+}
+
+static void ser_tx_int(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct async_struct * info;
+
+ if (custom.serdatr & SDR_TBE) {
+#ifdef SERIAL_DEBUG_INTR
+ printk("ser_tx_int...");
+#endif
+
+ info = IRQ_ports;
+ if (!info || !info->tty)
+ return;
+
+ transmit_chars(info);
+ info->last_active = jiffies;
+#ifdef SERIAL_DEBUG_INTR
+ printk("end.\n");
+#endif
+ }
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+ run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private_)
+{
+ struct async_struct *info = (struct async_struct *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+ }
+}
+
+/*
+ * ---------------------------------------------------------------
+ * Low level utility subroutines for the serial driver: routines to
+ * figure out the appropriate timeout for an interrupt chain, routines
+ * to initialize and startup a serial port, and routines to shutdown a
+ * serial port. Useful stuff like that.
+ * ---------------------------------------------------------------
+ */
+
+static int startup(struct async_struct * info)
+{
+ unsigned long flags;
+ int retval=0;
+ unsigned long page;
+
+ page = get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ save_flags(flags); cli();
+
+ if (info->flags & ASYNC_INITIALIZED) {
+ free_page(page);
+ goto errout;
+ }
+
+ if (info->xmit_buf)
+ free_page(page);
+ else
+ info->xmit_buf = (unsigned char *) page;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("starting up ttys%d ...", info->line);
+#endif
+
+ /* Clear anything in the input buffer */
+
+ custom.intreq = IF_RBF;
+ mb();
+
+ retval = request_irq(IRQ_AMIGA_VERTB, ser_vbl_int, 0, "serial status", info);
+ if (retval) {
+ if (serial_isroot()) {
+ if (info->tty)
+ set_bit(TTY_IO_ERROR,
+ &info->tty->flags);
+ retval = 0;
+ }
+ goto errout;
+ }
+
+ /* enable both Rx and Tx interrupts */
+ custom.intena = IF_SETCLR | IF_RBF | IF_TBE;
+ mb();
+ info->IER = UART_IER_MSI;
+
+ /* remember current state of the DCD and CTS bits */
+ current_ctl_bits = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);
+
+ IRQ_ports = info;
+
+ info->MCR = 0;
+ if (info->tty->termios->c_cflag & CBAUD)
+ info->MCR = SER_DTR | SER_RTS;
+ rtsdtr_ctrl(info->MCR);
+
+ if (info->tty)
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+ /*
+ * Set up the tty->alt_speed kludge
+ */
+ if (info->tty) {
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ info->tty->alt_speed = 57600;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ info->tty->alt_speed = 115200;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+ info->tty->alt_speed = 230400;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+ info->tty->alt_speed = 460800;
+ }
+
+ /*
+ * and set the speed of the serial port
+ */
+ change_speed(info, 0);
+
+ info->flags |= ASYNC_INITIALIZED;
+ restore_flags(flags);
+ return 0;
+
+errout:
+ restore_flags(flags);
+ return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct async_struct * info)
+{
+ unsigned long flags;
+ struct serial_state *state;
+
+ if (!(info->flags & ASYNC_INITIALIZED))
+ return;
+
+ state = info->state;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("Shutting down serial port %d ....\n", info->line);
+#endif
+
+ save_flags(flags); cli(); /* Disable interrupts */
+
+ /*
+ * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+ * here so the queue might never be waken up
+ */
+ wake_up_interruptible(&info->delta_msr_wait);
+
+ IRQ_ports = NULL;
+
+ /*
+ * Free the IRQ, if necessary
+ */
+ free_irq(IRQ_AMIGA_VERTB, info);
+
+ if (info->xmit_buf) {
+ free_page((unsigned long) info->xmit_buf);
+ info->xmit_buf = 0;
+ }
+
+ info->IER = 0;
+ custom.intena = IF_RBF | IF_TBE;
+ mb();
+
+ /* disable break condition */
+ custom.adkcon = AC_UARTBRK;
+ mb();
+
+ if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
+ info->MCR &= ~(SER_DTR|SER_RTS);
+ rtsdtr_ctrl(info->MCR);
+
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ info->flags &= ~ASYNC_INITIALIZED;
+ restore_flags(flags);
+}
+
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct async_struct *info,
+ struct termios *old_termios)
+{
+ int quot = 0, baud_base, baud;
+ unsigned cflag, cval = 0;
+ int bits;
+ unsigned long flags;
+
+ if (!info->tty || !info->tty->termios)
+ return;
+ cflag = info->tty->termios->c_cflag;
+
+ /* Byte size is always 8 bits plus parity bit if requested */
+
+ cval = 3; bits = 10;
+ if (cflag & CSTOPB) {
+ cval |= 0x04;
+ bits++;
+ }
+ if (cflag & PARENB) {
+ cval |= UART_LCR_PARITY;
+ bits++;
+ }
+ if (!(cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+ if (cflag & CMSPAR)
+ cval |= UART_LCR_SPAR;
+#endif
+
+ /* Determine divisor based on baud rate */
+ baud = tty_get_baud_rate(info->tty);
+ if (!baud)
+ baud = 9600; /* B0 transition handled in rs_set_termios */
+ baud_base = info->state->baud_base;
+ if (baud == 38400 &&
+ ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
+ quot = info->state->custom_divisor;
+ else {
+ if (baud == 134)
+ /* Special case since 134 is really 134.5 */
+ quot = (2*baud_base / 269);
+ else if (baud)
+ quot = baud_base / baud;
+ }
+ /* If the quotient is zero refuse the change */
+ if (!quot && old_termios) {
+ info->tty->termios->c_cflag &= ~CBAUD;
+ info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
+ baud = tty_get_baud_rate(info->tty);
+ if (!baud)
+ baud = 9600;
+ if (baud == 38400 &&
+ ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
+ quot = info->state->custom_divisor;
+ else {
+ if (baud == 134)
+ /* Special case since 134 is really 134.5 */
+ quot = (2*baud_base / 269);
+ else if (baud)
+ quot = baud_base / baud;
+ }
+ }
+ /* As a last resort, if the quotient is zero, default to 9600 bps */
+ if (!quot)
+ quot = baud_base / 9600;
+ info->quot = quot;
+ info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
+ info->timeout += HZ/50; /* Add .02 seconds of slop */
+
+ /* CTS flow control flag and modem status interrupts */
+ info->IER &= ~UART_IER_MSI;
+ if (info->flags & ASYNC_HARDPPS_CD)
+ info->IER |= UART_IER_MSI;
+ if (cflag & CRTSCTS) {
+ info->flags |= ASYNC_CTS_FLOW;
+ info->IER |= UART_IER_MSI;
+ } else
+ info->flags &= ~ASYNC_CTS_FLOW;
+ if (cflag & CLOCAL)
+ info->flags &= ~ASYNC_CHECK_CD;
+ else {
+ info->flags |= ASYNC_CHECK_CD;
+ info->IER |= UART_IER_MSI;
+ }
+ /* TBD:
+ * Does clearing IER_MSI imply that we should disbale the VBL interrupt ?
+ */
+
+ /*
+ * Set up parity check flag
+ */
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+ info->read_status_mask = UART_LSR_OE | UART_LSR_DR;
+ if (I_INPCK(info->tty))
+ info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+ if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+ info->read_status_mask |= UART_LSR_BI;
+
+ /*
+ * Characters to ignore
+ */
+ info->ignore_status_mask = 0;
+ if (I_IGNPAR(info->tty))
+ info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+ if (I_IGNBRK(info->tty)) {
+ info->ignore_status_mask |= UART_LSR_BI;
+ /*
+ * If we're ignore parity and break indicators, ignore
+ * overruns too. (For real raw support).
+ */
+ if (I_IGNPAR(info->tty))
+ info->ignore_status_mask |= UART_LSR_OE;
+ }
+ /*
+ * !!! ignore all characters if CREAD is not set
+ */
+ if ((cflag & CREAD) == 0)
+ info->ignore_status_mask |= UART_LSR_DR;
+ save_flags(flags); cli();
+
+ {
+ short serper;
+
+ /* Set up the baud rate */
+ serper = quot - 1;
+
+ /* Enable or disable parity bit */
+
+ if(cval & UART_LCR_PARITY)
+ serper |= (SERPER_PARENB);
+
+ custom.serper = serper;
+ mb();
+ }
+
+ info->LCR = cval; /* Save LCR */
+ restore_flags(flags);
+}
+
+static void rs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_put_char"))
+ return;
+
+ if (!tty || !info->xmit_buf)
+ return;
+
+ save_flags(flags); cli();
+ if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+ restore_flags(flags);
+ return;
+ }
+
+ info->xmit_buf[info->xmit_head++] = ch;
+ info->xmit_head &= SERIAL_XMIT_SIZE-1;
+ info->xmit_cnt++;
+ restore_flags(flags);
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
+ return;
+
+ if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+ !info->xmit_buf)
+ return;
+
+ save_flags(flags); cli();
+ info->IER |= UART_IER_THRI;
+ custom.intena = IF_SETCLR | IF_TBE;
+ mb();
+ /* set a pending Tx Interrupt, transmitter should restart now */
+ custom.intreq = IF_SETCLR | IF_TBE;
+ mb();
+ restore_flags(flags);
+}
+
+static int rs_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ int c, ret = 0;
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_write"))
+ return 0;
+
+ if (!tty || !info->xmit_buf || !tmp_buf)
+ return 0;
+
+ save_flags(flags);
+ if (from_user) {
+ down(&tmp_buf_sem);
+ while (1) {
+ c = MIN(count,
+ MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ if (c <= 0)
+ break;
+
+ c -= copy_from_user(tmp_buf, buf, c);
+ if (!c) {
+ if (!ret)
+ ret = -EFAULT;
+ break;
+ }
+ cli();
+ c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+ info->xmit_head = ((info->xmit_head + c) &
+ (SERIAL_XMIT_SIZE-1));
+ info->xmit_cnt += c;
+ restore_flags(flags);
+ buf += c;
+ count -= c;
+ ret += c;
+ }
+ up(&tmp_buf_sem);
+ } else {
+ while (1) {
+ cli();
+ c = MIN(count,
+ MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ if (c <= 0) {
+ restore_flags(flags);
+ break;
+ }
+ memcpy(info->xmit_buf + info->xmit_head, buf, c);
+ info->xmit_head = ((info->xmit_head + c) &
+ (SERIAL_XMIT_SIZE-1));
+ info->xmit_cnt += c;
+ restore_flags(flags);
+ buf += c;
+ count -= c;
+ ret += c;
+ }
+ }
+ if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+ !(info->IER & UART_IER_THRI)) {
+ info->IER |= UART_IER_THRI;
+ cli();
+ custom.intena = IF_SETCLR | IF_TBE;
+ mb();
+ /* set a pending Tx Interrupt, transmitter should restart now */
+ custom.intreq = IF_SETCLR | IF_TBE;
+ mb();
+ restore_flags(flags);
+ }
+ return ret;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ int ret;
+
+ if (serial_paranoia_check(info, tty->device, "rs_write_room"))
+ return 0;
+ ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+ if (ret < 0)
+ ret = 0;
+ return ret;
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
+ return 0;
+ return info->xmit_cnt;
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
+ return;
+ save_flags(flags); cli();
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ restore_flags(flags);
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void rs_send_xchar(struct tty_struct *tty, char ch)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_send_char"))
+ return;
+
+ info->x_char = ch;
+ if (ch) {
+ /* Make sure transmit interrupts are on */
+
+ /* Check this ! */
+ save_flags(flags);
+ cli();
+ if(!(custom.intenar & IF_TBE)) {
+ custom.intena = IF_SETCLR | IF_TBE;
+ mb();
+ /* set a pending Tx Interrupt, transmitter should restart now */
+ custom.intreq = IF_SETCLR | IF_TBE;
+ mb();
+ }
+ restore_flags(flags);
+
+ info->IER |= UART_IER_THRI;
+ }
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("throttle %s: %d....\n", tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "rs_throttle"))
+ return;
+
+ if (I_IXOFF(tty))
+ rs_send_xchar(tty, STOP_CHAR(tty));
+
+ if (tty->termios->c_cflag & CRTSCTS)
+ info->MCR &= ~SER_RTS;
+
+ save_flags(flags); cli();
+ rtsdtr_ctrl(info->MCR);
+ restore_flags(flags);
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("unthrottle %s: %d....\n", tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
+ return;
+
+ if (I_IXOFF(tty)) {
+ if (info->x_char)
+ info->x_char = 0;
+ else
+ rs_send_xchar(tty, START_CHAR(tty));
+ }
+ if (tty->termios->c_cflag & CRTSCTS)
+ info->MCR |= SER_RTS;
+ save_flags(flags); cli();
+ rtsdtr_ctrl(info->MCR);
+ restore_flags(flags);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct async_struct * info,
+ struct serial_struct * retinfo)
+{
+ struct serial_struct tmp;
+ struct serial_state *state = info->state;
+
+ if (!retinfo)
+ return -EFAULT;
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.type = state->type;
+ tmp.line = state->line;
+ tmp.port = state->port;
+ tmp.irq = state->irq;
+ tmp.flags = state->flags;
+ tmp.xmit_fifo_size = state->xmit_fifo_size;
+ tmp.baud_base = state->baud_base;
+ tmp.close_delay = state->close_delay;
+ tmp.closing_wait = state->closing_wait;
+ tmp.custom_divisor = state->custom_divisor;
+ if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+ return -EFAULT;
+ return 0;
+}
+
+static int set_serial_info(struct async_struct * info,
+ struct serial_struct * new_info)
+{
+ struct serial_struct new_serial;
+ struct serial_state old_state, *state;
+ unsigned int change_irq,change_port;
+ int retval = 0;
+
+ if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+ return -EFAULT;
+ state = info->state;
+ old_state = *state;
+
+ change_irq = new_serial.irq != state->irq;
+ change_port = (new_serial.port != state->port);
+ if(change_irq || change_port || (new_serial.xmit_fifo_size != state->xmit_fifo_size))
+ return -EINVAL;
+
+ if (!serial_isroot()) {
+ if ((new_serial.baud_base != state->baud_base) ||
+ (new_serial.close_delay != state->close_delay) ||
+ (new_serial.xmit_fifo_size != state->xmit_fifo_size) ||
+ ((new_serial.flags & ~ASYNC_USR_MASK) !=
+ (state->flags & ~ASYNC_USR_MASK)))
+ return -EPERM;
+ state->flags = ((state->flags & ~ASYNC_USR_MASK) |
+ (new_serial.flags & ASYNC_USR_MASK));
+ info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+ (new_serial.flags & ASYNC_USR_MASK));
+ state->custom_divisor = new_serial.custom_divisor;
+ goto check_and_exit;
+ }
+
+ if (new_serial.baud_base < 9600)
+ return -EINVAL;
+
+ /*
+ * OK, past this point, all the error checking has been done.
+ * At this point, we start making changes.....
+ */
+
+ state->baud_base = new_serial.baud_base;
+ state->flags = ((state->flags & ~ASYNC_FLAGS) |
+ (new_serial.flags & ASYNC_FLAGS));
+ info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) |
+ (info->flags & ASYNC_INTERNAL_FLAGS));
+ state->custom_divisor = new_serial.custom_divisor;
+ state->close_delay = new_serial.close_delay * HZ/100;
+ state->closing_wait = new_serial.closing_wait * HZ/100;
+ info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+check_and_exit:
+ if (info->flags & ASYNC_INITIALIZED) {
+ if (((old_state.flags & ASYNC_SPD_MASK) !=
+ (state->flags & ASYNC_SPD_MASK)) ||
+ (old_state.custom_divisor != state->custom_divisor)) {
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ info->tty->alt_speed = 57600;
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ info->tty->alt_speed = 115200;
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+ info->tty->alt_speed = 230400;
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+ info->tty->alt_speed = 460800;
+ change_speed(info, 0);
+ }
+ } else
+ retval = startup(info);
+ return retval;
+}
+
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * is emptied. On bus types like RS485, the transmitter must
+ * release the bus after transmitting. This must be done when
+ * the transmit shift register is empty, not be done when the
+ * transmit holding register is empty. This functionality
+ * allows an RS485 driver to be written in user space.
+ */
+static int get_lsr_info(struct async_struct * info, unsigned int *value)
+{
+ unsigned char status;
+ unsigned int result;
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ status = custom.serdatr;
+ mb();
+ restore_flags(flags);
+ result = ((status & SDR_TSRE) ? TIOCSER_TEMT : 0);
+ if (copy_to_user(value, &result, sizeof(int)))
+ return -EFAULT;
+ return 0;
+}
+
+
+static int get_modem_info(struct async_struct * info, unsigned int *value)
+{
+ unsigned char control, status;
+ unsigned int result;
+ unsigned long flags;
+
+ control = info->MCR;
+ save_flags(flags); cli();
+ status = ciab.pra;
+ restore_flags(flags);
+ result = ((control & SER_RTS) ? TIOCM_RTS : 0)
+ | ((control & SER_DTR) ? TIOCM_DTR : 0)
+ | (!(status & SER_DCD) ? TIOCM_CAR : 0)
+ | (!(status & SER_DSR) ? TIOCM_DSR : 0)
+ | (!(status & SER_CTS) ? TIOCM_CTS : 0);
+ if (copy_to_user(value, &result, sizeof(int)))
+ return -EFAULT;
+ return 0;
+}
+
+static int set_modem_info(struct async_struct * info, unsigned int cmd,
+ unsigned int *value)
+{
+ unsigned int arg;
+ unsigned long flags;
+
+ if (copy_from_user(&arg, value, sizeof(int)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS)
+ info->MCR |= SER_RTS;
+ if (arg & TIOCM_DTR)
+ info->MCR |= SER_DTR;
+ break;
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS)
+ info->MCR &= ~SER_RTS;
+ if (arg & TIOCM_DTR)
+ info->MCR &= ~SER_DTR;
+ break;
+ case TIOCMSET:
+ info->MCR = ((info->MCR & ~(SER_RTS | SER_DTR))
+ | ((arg & TIOCM_RTS) ? SER_RTS : 0)
+ | ((arg & TIOCM_DTR) ? SER_DTR : 0));
+ break;
+ default:
+ return -EINVAL;
+ }
+ save_flags(flags); cli();
+ rtsdtr_ctrl(info->MCR);
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * rs_break() --- routine which turns the break handling on or off
+ */
+static void rs_break(struct tty_struct *tty, int break_state)
+{
+ struct async_struct * info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_break"))
+ return;
+
+ save_flags(flags); cli();
+ if (break_state == -1)
+ custom.adkcon = AC_SETCLR | AC_UARTBRK;
+ else
+ custom.adkcon = AC_UARTBRK;
+ mb();
+ restore_flags(flags);
+}
+
+
+static int rs_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct async_struct * info = (struct async_struct *)tty->driver_data;
+ struct async_icount cprev, cnow; /* kernel counter temps */
+ struct serial_icounter_struct icount;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
+ return -ENODEV;
+
+ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
+ (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+ }
+
+ switch (cmd) {
+ case TIOCMGET:
+ return get_modem_info(info, (unsigned int *) arg);
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ return set_modem_info(info, cmd, (unsigned int *) arg);
+ case TIOCGSERIAL:
+ return get_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSSERIAL:
+ return set_serial_info(info,
+ (struct serial_struct *) arg);
+ case TIOCSERCONFIG:
+ return 0;
+
+ case TIOCSERGETLSR: /* Get line status register */
+ return get_lsr_info(info, (unsigned int *) arg);
+
+ case TIOCSERGSTRUCT:
+ if (copy_to_user((struct async_struct *) arg,
+ info, sizeof(struct async_struct)))
+ return -EFAULT;
+ return 0;
+
+ /*
+ * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+ * - mask passed in arg for lines of interest
+ * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+ * Caller should use TIOCGICOUNT to see which one it was
+ */
+ case TIOCMIWAIT:
+ save_flags(flags); cli();
+ /* note the counters on entry */
+ cprev = info->state->icount;
+ restore_flags(flags);
+ while (1) {
+ interruptible_sleep_on(&info->delta_msr_wait);
+ /* see if a signal did it */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ save_flags(flags); cli();
+ cnow = info->state->icount; /* atomic copy */
+ restore_flags(flags);
+ if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+ cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+ return -EIO; /* no change => error */
+ if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+ ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+ ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
+ ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
+ return 0;
+ }
+ cprev = cnow;
+ }
+ /* NOTREACHED */
+
+ /*
+ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+ * Return: write counters to the user passed counter struct
+ * NB: both 1->0 and 0->1 transitions are counted except for
+ * RI where only 0->1 is counted.
+ */
+ case TIOCGICOUNT:
+ save_flags(flags); cli();
+ cnow = info->state->icount;
+ restore_flags(flags);
+ icount.cts = cnow.cts;
+ icount.dsr = cnow.dsr;
+ icount.rng = cnow.rng;
+ icount.dcd = cnow.dcd;
+ icount.rx = cnow.rx;
+ icount.tx = cnow.tx;
+ icount.frame = cnow.frame;
+ icount.overrun = cnow.overrun;
+ icount.parity = cnow.parity;
+ icount.brk = cnow.brk;
+ icount.buf_overrun = cnow.buf_overrun;
+
+ if (copy_to_user((void *)arg, &icount, sizeof(icount)))
+ return -EFAULT;
+ return 0;
+ case TIOCSERGWILD:
+ case TIOCSERSWILD:
+ /* "setserial -W" is called in Debian boot */
+ printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+ unsigned int cflag = tty->termios->c_cflag;
+
+ if ( (cflag == old_termios->c_cflag)
+ && ( RELEVANT_IFLAG(tty->termios->c_iflag)
+ == RELEVANT_IFLAG(old_termios->c_iflag)))
+ return;
+
+ change_speed(info, old_termios);
+
+ /* Handle transition to B0 status */
+ if ((old_termios->c_cflag & CBAUD) &&
+ !(cflag & CBAUD)) {
+ info->MCR &= ~(SER_DTR|SER_RTS);
+ save_flags(flags); cli();
+ rtsdtr_ctrl(info->MCR);
+ restore_flags(flags);
+ }
+
+ /* Handle transition away from B0 status */
+ if (!(old_termios->c_cflag & CBAUD) &&
+ (cflag & CBAUD)) {
+ info->MCR |= SER_DTR;
+ if (!(tty->termios->c_cflag & CRTSCTS) ||
+ !test_bit(TTY_THROTTLED, &tty->flags)) {
+ info->MCR |= SER_RTS;
+ }
+ save_flags(flags); cli();
+ rtsdtr_ctrl(info->MCR);
+ restore_flags(flags);
+ }
+
+ /* Handle turning off CRTSCTS */
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ rs_start(tty);
+ }
+
+#if 0
+ /*
+ * No need to wake up processes in open wait, since they
+ * sample the CLOCAL flag once, and don't recheck it.
+ * XXX It's not clear whether the current behavior is correct
+ * or not. Hence, this may change.....
+ */
+ if (!(old_termios->c_cflag & CLOCAL) &&
+ (tty->termios->c_cflag & CLOCAL))
+ wake_up_interruptible(&info->open_wait);
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed. First, we
+ * wait for the last remaining data to be sent. Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+ struct async_struct * info = (struct async_struct *)tty->driver_data;
+ struct serial_state *state;
+ unsigned long flags;
+
+ if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
+ return;
+
+ state = info->state;
+
+ save_flags(flags); cli();
+
+ if (tty_hung_up_p(filp)) {
+ DBG_CNT("before DEC-hung");
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+ return;
+ }
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_close ttys%d, count = %d\n", info->line, state->count);
+#endif
+ if ((tty->count == 1) && (state->count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. state->count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ printk("rs_close: bad serial port count; tty->count is 1, "
+ "state->count is %d\n", state->count);
+ state->count = 1;
+ }
+ if (--state->count < 0) {
+ printk("rs_close: bad serial port count for ttys%d: %d\n",
+ info->line, state->count);
+ state->count = 0;
+ }
+ if (state->count) {
+ DBG_CNT("before DEC-2");
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+ return;
+ }
+ info->flags |= ASYNC_CLOSING;
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (info->flags & ASYNC_NORMAL_ACTIVE)
+ info->state->normal_termios = *tty->termios;
+ if (info->flags & ASYNC_CALLOUT_ACTIVE)
+ info->state->callout_termios = *tty->termios;
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, info->closing_wait);
+ /*
+ * At this point we stop accepting input. To do this, we
+ * disable the receive line status interrupts, and tell the
+ * interrupt driver to stop checking the data ready bit in the
+ * line status register.
+ */
+ info->read_status_mask &= ~UART_LSR_DR;
+ if (info->flags & ASYNC_INITIALIZED) {
+ /* disable receive interrupts */
+ custom.intena = IF_RBF;
+ mb();
+ /* clear any pending receive interrupt */
+ custom.intreq = IF_RBF;
+ mb();
+
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+ rs_wait_until_sent(tty, info->timeout);
+ }
+ shutdown(info);
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+ tty->closing = 0;
+ info->event = 0;
+ info->tty = 0;
+ if (info->blocked_open) {
+ if (info->close_delay) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(info->close_delay);
+ }
+ wake_up_interruptible(&info->open_wait);
+ }
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+ ASYNC_CLOSING);
+ wake_up_interruptible(&info->close_wait);
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+}
+
+/*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ struct async_struct * info = (struct async_struct *)tty->driver_data;
+ unsigned long orig_jiffies, char_time;
+ int lsr;
+
+ if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent"))
+ return;
+
+ if (info->xmit_fifo_size == 0)
+ return; /* Just in case.... */
+
+ orig_jiffies = jiffies;
+ /*
+ * Set the check interval to be 1/5 of the estimated time to
+ * send a single character, and make it at least 1. The check
+ * interval should also be less than the timeout.
+ *
+ * Note: we have to use pretty tight timings here to satisfy
+ * the NIST-PCTS.
+ */
+ char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
+ char_time = char_time / 5;
+ if (char_time == 0)
+ char_time = 1;
+ if (timeout)
+ char_time = MIN(char_time, timeout);
+ /*
+ * If the transmitter hasn't cleared in twice the approximate
+ * amount of time to send the entire FIFO, it probably won't
+ * ever clear. This assumes the UART isn't doing flow
+ * control, which is currently the case. Hence, if it ever
+ * takes longer than info->timeout, this is probably due to a
+ * UART bug of some kind. So, we clamp the timeout parameter at
+ * 2*info->timeout.
+ */
+ if (!timeout || timeout > 2*info->timeout)
+ timeout = 2*info->timeout;
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
+ printk("jiff=%lu...", jiffies);
+#endif
+ while(!((lsr = custom.serdatr) & SDR_TSRE)) {
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("serdatr = %d (jiff=%lu)...", lsr, jiffies);
+#endif
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(char_time);
+ if (signal_pending(current))
+ break;
+ if (timeout && time_after(jiffies, orig_jiffies + timeout))
+ break;
+ }
+ current->state = TASK_RUNNING;
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+#endif
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void rs_hangup(struct tty_struct *tty)
+{
+ struct async_struct * info = (struct async_struct *)tty->driver_data;
+ struct serial_state *state = info->state;
+
+ if (serial_paranoia_check(info, tty->device, "rs_hangup"))
+ return;
+
+ state = info->state;
+
+ rs_flush_buffer(tty);
+ shutdown(info);
+ info->event = 0;
+ state->count = 0;
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+ info->tty = 0;
+ wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+ struct async_struct *info)
+{
+#ifdef DECLARE_WAITQUEUE
+ DECLARE_WAITQUEUE(wait, current);
+#else
+ struct wait_queue wait = { current, NULL };
+#endif
+ struct serial_state *state = info->state;
+ int retval;
+ int do_clocal = 0, extra_count = 0;
+ unsigned long flags;
+
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (tty_hung_up_p(filp) ||
+ (info->flags & ASYNC_CLOSING)) {
+ if (info->flags & ASYNC_CLOSING)
+ interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ return ((info->flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+ if (info->flags & ASYNC_NORMAL_ACTIVE)
+ return -EBUSY;
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_SESSION_LOCKOUT) &&
+ (info->session != current->session))
+ return -EBUSY;
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_PGRP_LOCKOUT) &&
+ (info->pgrp != current->pgrp))
+ return -EBUSY;
+ info->flags |= ASYNC_CALLOUT_ACTIVE;
+ return 0;
+ }
+
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
+ if (info->flags & ASYNC_CALLOUT_ACTIVE)
+ return -EBUSY;
+ info->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+ if (state->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (tty->termios->c_cflag & CLOCAL)
+ do_clocal = 1;
+ }
+
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, state->count is dropped by one, so that
+ * rs_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+ retval = 0;
+ add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready before block: ttys%d, count = %d\n",
+ state->line, state->count);
+#endif
+ save_flags(flags); cli();
+ if (!tty_hung_up_p(filp)) {
+ extra_count = 1;
+ state->count--;
+ }
+ restore_flags(flags);
+ info->blocked_open++;
+ while (1) {
+ save_flags(flags); cli();
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (tty->termios->c_cflag & CBAUD))
+ rtsdtr_ctrl(SER_DTR|SER_RTS);
+ restore_flags(flags);
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (tty_hung_up_p(filp) ||
+ !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ASYNC_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+#else
+ retval = -EAGAIN;
+#endif
+ break;
+ }
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ !(info->flags & ASYNC_CLOSING) &&
+ (do_clocal || (!(ciab.pra & SER_DCD)) ))
+ break;
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready blocking: ttys%d, count = %d\n",
+ info->line, state->count);
+#endif
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&info->open_wait, &wait);
+ if (extra_count)
+ state->count++;
+ info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready after blocking: ttys%d, count = %d\n",
+ info->line, state->count);
+#endif
+ if (retval)
+ return retval;
+ info->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+}
+
+static int get_async_struct(int line, struct async_struct **ret_info)
+{
+ struct async_struct *info;
+ struct serial_state *sstate;
+
+ sstate = rs_table + line;
+ sstate->count++;
+ if (sstate->info) {
+ *ret_info = sstate->info;
+ return 0;
+ }
+ info = kmalloc(sizeof(struct async_struct), GFP_KERNEL);
+ if (!info) {
+ sstate->count--;
+ return -ENOMEM;
+ }
+ memset(info, 0, sizeof(struct async_struct));
+#ifdef DECLARE_WAITQUEUE
+ init_waitqueue_head(&info->open_wait);
+ init_waitqueue_head(&info->close_wait);
+ init_waitqueue_head(&info->delta_msr_wait);
+#endif
+ info->magic = SERIAL_MAGIC;
+ info->port = sstate->port;
+ info->flags = sstate->flags;
+ info->xmit_fifo_size = sstate->xmit_fifo_size;
+ info->line = line;
+ info->tqueue.routine = do_softint;
+ info->tqueue.data = info;
+ info->state = sstate;
+ if (sstate->info) {
+ kfree_s(info, sizeof(struct async_struct));
+ *ret_info = sstate->info;
+ return 0;
+ }
+ *ret_info = sstate->info = info;
+ return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened. It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain. It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int rs_open(struct tty_struct *tty, struct file * filp)
+{
+ struct async_struct *info;
+ int retval, line;
+ unsigned long page;
+
+ MOD_INC_USE_COUNT;
+ line = MINOR(tty->device) - tty->driver.minor_start;
+ if ((line < 0) || (line >= NR_PORTS)) {
+ MOD_DEC_USE_COUNT;
+ return -ENODEV;
+ }
+ retval = get_async_struct(line, &info);
+ if (retval) {
+ MOD_DEC_USE_COUNT;
+ return retval;
+ }
+ tty->driver_data = info;
+ info->tty = tty;
+ if (serial_paranoia_check(info, tty->device, "rs_open"))
+ return -ENODEV;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
+ info->state->count);
+#endif
+ info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+ if (!tmp_buf) {
+ page = get_free_page(GFP_KERNEL);
+ if (!page) {
+ return -ENOMEM;
+ }
+ if (tmp_buf)
+ free_page(page);
+ else
+ tmp_buf = (unsigned char *) page;
+ }
+
+ /*
+ * If the port is the middle of closing, bail out now
+ */
+ if (tty_hung_up_p(filp) ||
+ (info->flags & ASYNC_CLOSING)) {
+ if (info->flags & ASYNC_CLOSING)
+ interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ return ((info->flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * Start up serial port
+ */
+ retval = startup(info);
+ if (retval) {
+ return retval;
+ }
+
+ retval = block_til_ready(tty, filp, info);
+ if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open returning after block_til_ready with %d\n",
+ retval);
+#endif
+ return retval;
+ }
+
+ if ((info->state->count == 1) &&
+ (info->flags & ASYNC_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+ *tty->termios = info->state->normal_termios;
+ else
+ *tty->termios = info->state->callout_termios;
+ change_speed(info, 0);
+ }
+ info->session = current->session;
+ info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open ttys%d successful...", info->line);
+#endif
+ return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static inline int line_info(char *buf, struct serial_state *state)
+{
+ struct async_struct *info = state->info, scr_info;
+ char stat_buf[30], control, status;
+ int ret;
+ unsigned long flags;
+
+ ret = sprintf(buf, "%d: uart:amiga_builtin",state->line);
+
+ /*
+ * Figure out the current RS-232 lines
+ */
+ if (!info) {
+ info = &scr_info; /* This is just for serial_{in,out} */
+
+ info->magic = SERIAL_MAGIC;
+ info->flags = state->flags;
+ info->quot = 0;
+ info->tty = 0;
+ }
+ save_flags(flags); cli();
+ status = ciab.pra;
+ control = info ? info->MCR : status;
+ restore_flags(flags);
+
+ stat_buf[0] = 0;
+ stat_buf[1] = 0;
+ if(!(control & SER_RTS))
+ strcat(stat_buf, "|RTS");
+ if(!(status & SER_CTS))
+ strcat(stat_buf, "|CTS");
+ if(!(control & SER_DTR))
+ strcat(stat_buf, "|DTR");
+ if(!(status & SER_DSR))
+ strcat(stat_buf, "|DSR");
+ if(!(status & SER_DCD))
+ strcat(stat_buf, "|CD");
+
+ if (info->quot) {
+ ret += sprintf(buf+ret, " baud:%d",
+ state->baud_base / info->quot);
+ }
+
+ ret += sprintf(buf+ret, " tx:%d rx:%d",
+ state->icount.tx, state->icount.rx);
+
+ if (state->icount.frame)
+ ret += sprintf(buf+ret, " fe:%d", state->icount.frame);
+
+ if (state->icount.parity)
+ ret += sprintf(buf+ret, " pe:%d", state->icount.parity);
+
+ if (state->icount.brk)
+ ret += sprintf(buf+ret, " brk:%d", state->icount.brk);
+
+ if (state->icount.overrun)
+ ret += sprintf(buf+ret, " oe:%d", state->icount.overrun);
+
+ /*
+ * Last thing is the RS-232 status lines
+ */
+ ret += sprintf(buf+ret, " %s\n", stat_buf+1);
+ return ret;
+}
+
+int rs_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ int len = 0, l;
+ off_t begin = 0;
+
+ len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version);
+ l = line_info(page + len, &rs_table[0]);
+ len += l;
+ if (len+begin > off+count)
+ goto done;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ *eof = 1;
+done:
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * rs_init() and friends
+ *
+ * rs_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static _INLINE_ void show_serial_version(void)
+{
+ printk(KERN_INFO "%s version %s\n", serial_name, serial_version);
+}
+
+
+int register_serial(struct serial_struct *req);
+void unregister_serial(int line);
+
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+int __init rs_init(void)
+{
+ unsigned long flags;
+ struct serial_state * state;
+
+ if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_SERIAL))
+ return -ENODEV;
+
+ init_bh(SERIAL_BH, do_serial_bh);
+
+ IRQ_ports = NULL;
+
+ show_serial_version();
+
+ /* Initialize the tty_driver structure */
+
+ memset(&serial_driver, 0, sizeof(struct tty_driver));
+ serial_driver.magic = TTY_DRIVER_MAGIC;
+ serial_driver.driver_name = "amiserial";
+ serial_driver.name = "ttyS";
+ serial_driver.major = TTY_MAJOR;
+ serial_driver.minor_start = 64;
+ serial_driver.num = 1;
+ serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ serial_driver.subtype = SERIAL_TYPE_NORMAL;
+ serial_driver.init_termios = tty_std_termios;
+ serial_driver.init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ serial_driver.flags = TTY_DRIVER_REAL_RAW;
+ serial_driver.refcount = &serial_refcount;
+ serial_driver.table = serial_table;
+ serial_driver.termios = serial_termios;
+ serial_driver.termios_locked = serial_termios_locked;
+
+ serial_driver.open = rs_open;
+ serial_driver.close = rs_close;
+ serial_driver.write = rs_write;
+ serial_driver.put_char = rs_put_char;
+ serial_driver.flush_chars = rs_flush_chars;
+ serial_driver.write_room = rs_write_room;
+ serial_driver.chars_in_buffer = rs_chars_in_buffer;
+ serial_driver.flush_buffer = rs_flush_buffer;
+ serial_driver.ioctl = rs_ioctl;
+ serial_driver.throttle = rs_throttle;
+ serial_driver.unthrottle = rs_unthrottle;
+ serial_driver.set_termios = rs_set_termios;
+ serial_driver.stop = rs_stop;
+ serial_driver.start = rs_start;
+ serial_driver.hangup = rs_hangup;
+ serial_driver.break_ctl = rs_break;
+ serial_driver.send_xchar = rs_send_xchar;
+ serial_driver.wait_until_sent = rs_wait_until_sent;
+ serial_driver.read_proc = rs_read_proc;
+
+ /*
+ * The callout device is just like normal device except for
+ * major number and the subtype code.
+ */
+ callout_driver = serial_driver;
+ callout_driver.name = "cua";
+ callout_driver.major = TTYAUX_MAJOR;
+ callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+ callout_driver.read_proc = 0;
+ callout_driver.proc_entry = 0;
+
+ if (tty_register_driver(&serial_driver))
+ panic("Couldn't register serial driver\n");
+ if (tty_register_driver(&callout_driver))
+ panic("Couldn't register callout driver\n");
+
+ state = rs_table;
+ state->magic = SSTATE_MAGIC;
+ state->port = (int)&custom.serdatr; /* Just to give it a value */
+ state->line = 0;
+ state->custom_divisor = 0;
+ state->close_delay = 5*HZ/10;
+ state->closing_wait = 30*HZ;
+ state->callout_termios = callout_driver.init_termios;
+ state->normal_termios = serial_driver.init_termios;
+ state->icount.cts = state->icount.dsr =
+ state->icount.rng = state->icount.dcd = 0;
+ state->icount.rx = state->icount.tx = 0;
+ state->icount.frame = state->icount.parity = 0;
+ state->icount.overrun = state->icount.brk = 0;
+ /*
+ if(state->port && check_region(state->port,REGION_LENGTH(state)))
+ continue;
+ */
+
+ printk(KERN_INFO "ttyS%02d is the amiga builtin serial port\n",
+ state->line);
+
+ /* Hardware set up */
+
+ state->baud_base = amiga_colorclock;
+ state->xmit_fifo_size = 1;
+
+ save_flags (flags);
+ cli();
+
+ /* set ISRs, and then disable the rx interrupts */
+ request_irq(IRQ_AMIGA_TBE, ser_tx_int, 0, "serial TX", state);
+ request_irq(IRQ_AMIGA_RBF, ser_rx_int, SA_INTERRUPT, "serial RX", state);
+
+ /* turn off Rx and Tx interrupts */
+ custom.intena = IF_RBF | IF_TBE;
+ mb();
+
+ /* clear any pending interrupt */
+ custom.intreq = IF_RBF | IF_TBE;
+ mb();
+
+ restore_flags (flags);
+
+ /*
+ * set the appropriate directions for the modem control flags,
+ * and clear RTS and DTR
+ */
+ ciab.ddra |= (SER_DTR | SER_RTS); /* outputs */
+ ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR); /* inputs */
+
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return rs_init();
+}
+
+void cleanup_module(void)
+{
+ unsigned long flags;
+ int e1, e2;
+ struct async_struct *info;
+
+ /* printk("Unloading %s: version %s\n", serial_name, serial_version); */
+ save_flags(flags);
+ cli();
+ remove_bh(SERIAL_BH);
+ if ((e1 = tty_unregister_driver(&serial_driver)))
+ printk("SERIAL: failed to unregister serial driver (%d)\n",
+ e1);
+ if ((e2 = tty_unregister_driver(&callout_driver)))
+ printk("SERIAL: failed to unregister callout driver (%d)\n",
+ e2);
+ restore_flags(flags);
+
+ info = rs_table[0].info;
+ if (info) {
+ rs_table[0].info = NULL;
+ kfree_s(info, sizeof(struct async_struct));
+ }
+
+ if (tmp_buf) {
+ free_page((unsigned long) tmp_buf);
+ tmp_buf = NULL;
+ }
+}
+#endif /* MODULE */
+
+/*
+ Local variables:
+ compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -D__SMP__ -pipe -fno-strength-reduce -DCPU=686 -march=i686 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c"
+ End:
+*/
+++ /dev/null
-/*
- * mac_SCC.c: m68k version of
- *
- * macserial.c: Serial port driver for Power Macintoshes.
- * Extended for the 68K mac by Alan Cox.
- * Rewritten to m68k serial design by Michael Schmitz
- *
- * Derived from drivers/sbus/char/sunserial.c by Paul Mackerras.
- *
- * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- */
-
-/*
- * Design note for the m68k rewrite:
- * The structure of the m68k serial code requires separation of the low-level
- * functions that talk directly to the hardware from the Linux serial driver
- * code interfacing to the tty layer. The reason for this separation is simply
- * the fact that the m68k serial hardware is, unlike the i386, based on a
- * variety of chips, and the rs_* serial routines need to be shared.
- *
- * I've tried to make consistent use of the async_struct info populated in the
- * midlevel code, and introduced an async_private struct to hold the Macintosh
- * SCC internals (this was added to the async_struct for the PowerMac driver).
- * Exception: the console and kgdb hooks still use the zs_soft[] data, and this
- * is still filled in by the probe_sccs() routine, which provides some data
- * for mac_SCC_init as well. Interrupts are registered in mac_SCC_init, so
- * the console/kgdb stuff probably won't work before proper serial init, and
- * I have to rely on keeping info and zs_soft consistent at least for the
- * console/kgdb port.
- *
- * Update (16-11-97): The SCC interrupt handling was suffering from the problem
- * that the autovector SCC interrupt was registered only once, hence only one
- * async_struct was passed to the interrupt function and only interrupts from
- * the corresponding channel could be handled (yes, major design flaw).
- * The autovector interrupt is now registered by the main interrupt initfunc,
- * and uses a handler that will call the registered SCC specific interrupts in
- * turn. The SCC init has to register these as machspec interrupts now, as is
- * done for the VIA interrupts elsewhere.
- */
-
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/config.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-
-#include <asm/uaccess.h>
-#include <asm/setup.h>
-#include <asm/bootinfo.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/macints.h>
-#ifndef CONFIG_MAC
-#include <asm/prom.h>
-#endif
-#include <asm/system.h>
-#include <asm/segment.h>
-#include <asm/bitops.h>
-#include <asm/hwtest.h>
-
-#include "mac_SCC.h"
-
-/*
- * It would be nice to dynamically allocate everything that
- * depends on NUM_SERIAL, so we could support any number of
- * Z8530s, but for now...
- */
-#define NUM_SERIAL 2 /* Max number of ZS chips supported */
-#define NUM_CHANNELS (NUM_SERIAL * 2) /* 2 channels per chip */
-
-#ifdef CONFIG_MAC
-/*
- * All the Macintosh 68K boxes that have an MMU also have hardware
- * recovery delays.
- */
-#define RECOVERY_DELAY
-#else
-/* On PowerMacs, the hardware takes care of the SCC recovery time,
- but we need the eieio to make sure that the accesses occur
- in the order we want. */
-#define RECOVERY_DELAY eieio()
-#endif
-
-struct mac_zschannel *zs_kgdbchan;
-struct mac_zschannel zs_channels[NUM_CHANNELS];
-
-struct m68k_async_struct zs_soft[NUM_CHANNELS];
-struct m68k_async_private zs_soft_private[NUM_CHANNELS];
-int zs_channels_found;
-struct m68k_async_struct *zs_chain; /* list of all channels */
-
-struct tty_struct zs_ttys[NUM_CHANNELS];
-/** struct tty_struct *zs_constty; **/
-
-/* Console hooks... */
-static int zs_cons_chanout = 0;
-static int zs_cons_chanin = 0;
-struct m68k_async_struct *zs_consinfo = 0;
-struct mac_zschannel *zs_conschan;
-
-static unsigned char kgdb_regs[16] = {
- 0, 0, 0, /* write 0, 1, 2 */
- (Rx8 | RxENABLE), /* write 3 */
- (X16CLK | SB1 | PAR_EVEN), /* write 4 */
- (Tx8 | TxENAB), /* write 5 */
- 0, 0, 0, /* write 6, 7, 8 */
- (NV), /* write 9 */
- (NRZ), /* write 10 */
- (TCBR | RCBR), /* write 11 */
- 1, 0, /* 38400 baud divisor, write 12 + 13 */
- (BRENABL), /* write 14 */
- (DCDIE) /* write 15 */
-};
-
-#define ZS_CLOCK 3686400 /* Z8530 RTxC input clock rate */
-
-/* Debugging... DEBUG_INTR is bad to use when one of the zs
- * lines is your console ;(
- */
-#undef SERIAL_DEBUG_INTR
-#undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_FLOW
-
-#define RS_STROBE_TIME 10
-#define RS_ISR_PASS_LIMIT 256
-
-#define _INLINE_ inline
-
-static void probe_sccs(void);
-
-#ifndef MIN
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-
-
-/***************************** Prototypes *****************************/
-
-static void SCC_init_port( struct m68k_async_struct *info, int type, int channel );
-#if 0
-#ifdef MODULE
-static void SCC_deinit_port( struct m68k_async_struct *info, int channel );
-#endif
-#endif
-
-/* FIXME !!! Currently, only autovector interrupt used! */
-#if 0
-static void SCC_rx_int (int irq, void *data, struct pt_regs *fp);
-static void SCC_spcond_int (int irq, void *data, struct pt_regs *fp);
-static void SCC_tx_int (int irq, void *data, struct pt_regs *fp);
-static void SCC_stat_int (int irq, void *data, struct pt_regs *fp);
-static void SCC_ri_int (int irq, void *data, struct pt_regs *fp);
-#endif
-
-static int SCC_check_open( struct m68k_async_struct *info, struct tty_struct
- *tty, struct file *file );
-static void SCC_init( struct m68k_async_struct *info );
-static void SCC_deinit( struct m68k_async_struct *info, int leave_dtr );
-static void SCC_enab_tx_int( struct m68k_async_struct *info, int enab_flag );
-static int SCC_check_custom_divisor( struct m68k_async_struct *info, int baud_base,
- int divisor );
-static void SCC_change_speed( struct m68k_async_struct *info );
-#if 0
-static int SCC_clocksrc( unsigned baud_base, unsigned channel );
-#endif
-static void SCC_throttle( struct m68k_async_struct *info, int status );
-static void SCC_set_break( struct m68k_async_struct *info, int break_flag );
-static void SCC_get_serial_info( struct m68k_async_struct *info, struct
- serial_struct *retinfo );
-static unsigned int SCC_get_modem_info( struct m68k_async_struct *info );
-static int SCC_set_modem_info( struct m68k_async_struct *info, int new_dtr, int
- new_rts );
-static int SCC_ioctl( struct tty_struct *tty, struct file *file, struct
- m68k_async_struct *info, unsigned int cmd, unsigned long arg );
-static void SCC_stop_receive (struct m68k_async_struct *info);
-static int SCC_trans_empty (struct m68k_async_struct *info);
-
-/************************* End of Prototypes **************************/
-
-
-static SERIALSWITCH SCC_switch = {
- SCC_init, SCC_deinit, SCC_enab_tx_int,
- SCC_check_custom_divisor, SCC_change_speed,
- SCC_throttle, SCC_set_break,
- SCC_get_serial_info, SCC_get_modem_info,
- SCC_set_modem_info, SCC_ioctl, SCC_stop_receive, SCC_trans_empty,
- SCC_check_open
-};
-
-/*
- * This is used to figure out the divisor speeds and the timeouts
- */
-static int baud_table[] = {
- 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
- 9600, 19200, 38400, 57600, 115200, 0 };
-
-/*
- * Reading and writing Z8530 registers.
- */
-static inline unsigned char read_zsreg(struct mac_zschannel *channel,
- unsigned char reg)
-{
- unsigned char retval;
-
- if (reg != 0) {
- *channel->control = reg;
- RECOVERY_DELAY;
- }
- retval = *channel->control;
- RECOVERY_DELAY;
- return retval;
-}
-
-static inline void write_zsreg(struct mac_zschannel *channel,
- unsigned char reg, unsigned char value)
-{
- if (reg != 0) {
- *channel->control = reg;
- RECOVERY_DELAY;
- }
- *channel->control = value;
- RECOVERY_DELAY;
- return;
-}
-
-static inline unsigned char read_zsdata(struct mac_zschannel *channel)
-{
- unsigned char retval;
-
- retval = *channel->data;
- RECOVERY_DELAY;
- return retval;
-}
-
-static inline void write_zsdata(struct mac_zschannel *channel,
- unsigned char value)
-{
- *channel->data = value;
- RECOVERY_DELAY;
- return;
-}
-
-static inline void load_zsregs(struct mac_zschannel *channel,
- unsigned char *regs)
-{
- ZS_CLEARERR(channel);
- ZS_CLEARFIFO(channel);
- /* Load 'em up */
- write_zsreg(channel, R4, regs[R4]);
- write_zsreg(channel, R10, regs[R10]);
- write_zsreg(channel, R3, regs[R3] & ~RxENABLE);
- write_zsreg(channel, R5, regs[R5] & ~TxENAB);
- write_zsreg(channel, R1, regs[R1]);
- write_zsreg(channel, R9, regs[R9]);
- write_zsreg(channel, R11, regs[R11]);
- write_zsreg(channel, R12, regs[R12]);
- write_zsreg(channel, R13, regs[R13]);
- write_zsreg(channel, R14, regs[R14]);
- write_zsreg(channel, R15, regs[R15]);
- write_zsreg(channel, R3, regs[R3]);
- write_zsreg(channel, R5, regs[R5]);
- return;
-}
-
-/* Sets or clears DTR/RTS on the requested line */
-static inline void zs_rtsdtr(struct m68k_async_struct *ss, int set)
-{
- if (set)
- ss->private->curregs[5] |= (RTS | DTR);
- else
- ss->private->curregs[5] &= ~(RTS | DTR);
- write_zsreg(ss->private->zs_channel, 5, ss->private->curregs[5]);
- return;
-}
-
-static inline void kgdb_chaninit(struct m68k_async_struct *ss, int intson, int bps)
-{
- int brg;
-
- if (intson) {
- kgdb_regs[R1] = INT_ALL_Rx;
- kgdb_regs[R9] |= MIE;
- } else {
- kgdb_regs[R1] = 0;
- kgdb_regs[R9] &= ~MIE;
- }
- brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
- kgdb_regs[R12] = brg;
- kgdb_regs[R13] = brg >> 8;
- load_zsregs(ss->private->zs_channel, kgdb_regs);
-}
-
-/* Utility routines for the Zilog */
-static inline int get_zsbaud(struct m68k_async_struct *ss)
-{
- struct mac_zschannel *channel = ss->private->zs_channel;
- int brg;
-
- /* The baud rate is split up between two 8-bit registers in
- * what is termed 'BRG time constant' format in my docs for
- * the chip, it is a function of the clk rate the chip is
- * receiving which happens to be constant.
- */
- brg = (read_zsreg(channel, 13) << 8);
- brg |= read_zsreg(channel, 12);
- return BRG_TO_BPS(brg, (ZS_CLOCK/(ss->private->clk_divisor)));
-}
-
-/* On receive, this clears errors and the receiver interrupts */
-static inline void SCC_recv_clear(struct mac_zschannel *zsc)
-{
- write_zsreg(zsc, 0, ERR_RES);
- write_zsreg(zsc, 0, RES_H_IUS); /* XXX this is unnecessary */
-}
-
-/*
- * ----------------------------------------------------------------------
- *
- * Here starts the interrupt handling routines. All of the following
- * subroutines are declared as inline and are folded into
- * rs_interrupt(). They were separated out for readability's sake.
- *
- * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
- * -----------------------------------------------------------------------
- */
-
-extern void breakpoint(void); /* For the KGDB frame character */
-
-static /*_INLINE_*/ void receive_chars(struct m68k_async_struct *info,
- struct pt_regs *regs)
-{
- struct tty_struct *tty = info->tty;
- unsigned char ch, stat, flag;
-
- while ((read_zsreg(info->private->zs_channel, 0) & Rx_CH_AV) != 0) {
-
- stat = read_zsreg(info->private->zs_channel, R1);
- ch = read_zsdata(info->private->zs_channel);
-
-#ifdef SCC_DEBUG
- printk("mac_SCC: receive_chars stat=%X char=%X \n", stat, ch);
-#endif
-
-#if 0 /* KGDB not yet supported */
- /* Look for kgdb 'stop' character, consult the gdb documentation
- * for remote target debugging and arch/sparc/kernel/sparc-stub.c
- * to see how all this works.
- */
- if ((info->kgdb_channel) && (ch =='\003')) {
- breakpoint();
- continue;
- }
-#endif
-
- if (!tty)
- continue;
-
- if (tty->flip.count >= TTY_FLIPBUF_SIZE)
- tty_flip_buffer_push(tty);
-
- if (stat & Rx_OVR) {
- flag = TTY_OVERRUN;
- /* reset the error indication */
- write_zsreg(info->private->zs_channel, 0, ERR_RES);
- } else if (stat & FRM_ERR) {
- /* this error is not sticky */
- flag = TTY_FRAME;
- } else if (stat & PAR_ERR) {
- flag = TTY_PARITY;
- /* reset the error indication */
- write_zsreg(info->private->zs_channel, 0, ERR_RES);
- } else
- flag = 0;
-
- if (tty->flip.buf_num
- && tty->flip.count >= TTY_FLIPBUF_SIZE) {
-#ifdef SCC_DEBUG_OVERRUN
- printk("mac_SCC: flip buffer overrun!\n");
-#endif
- return;
- }
-
- if (!tty->flip.buf_num
- && tty->flip.count >= 2*TTY_FLIPBUF_SIZE) {
- printk("mac_SCC: double flip buffer overrun!\n");
- return;
- }
-
- tty->flip.count++;
- *tty->flip.flag_buf_ptr++ = flag;
- *tty->flip.char_buf_ptr++ = ch;
- info->icount.rx++;
- tty_flip_buffer_push(tty);
- }
-#if 0
-clear_and_exit:
- SCC_recv_clear(info->private->zs_channel);
-#endif
-}
-
-/* that's SCC_enable_tx_int, basically */
-
-static void transmit_chars(struct m68k_async_struct *info)
-{
- if ((read_zsreg(info->private->zs_channel, 0) & Tx_BUF_EMP) == 0)
- return;
- info->private->tx_active = 0;
-
- if (info->x_char) {
- /* Send next char */
- write_zsdata(info->private->zs_channel, info->x_char);
- info->x_char = 0;
- info->private->tx_active = 1;
- return;
- }
-
- if ((info->xmit_cnt <= 0) || info->tty->stopped
- || info->private->tx_stopped) {
- write_zsreg(info->private->zs_channel, 0, RES_Tx_P);
- return;
- }
-
- /* Send char */
- write_zsdata(info->private->zs_channel, info->xmit_buf[info->xmit_tail++]);
- info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
- info->icount.tx++;
- info->xmit_cnt--;
- info->private->tx_active = 1;
-
- if (info->xmit_cnt < WAKEUP_CHARS)
- rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
-}
-
-static /*_INLINE_*/ void status_handle(struct m68k_async_struct *info)
-{
- unsigned char status;
-
- /* Get status from Read Register 0 */
- status = read_zsreg(info->private->zs_channel, 0);
-
- /* Check for DCD transitions */
- if (((status ^ info->private->read_reg_zero) & DCD) != 0
- && info->tty && C_CLOCAL(info->tty)) {
- if (status & DCD) {
- wake_up_interruptible(&info->open_wait);
- } else if (!(info->flags & ZILOG_CALLOUT_ACTIVE)) {
- if (info->tty)
- tty_hangup(info->tty);
- }
- }
-
- /* Check for CTS transitions */
- if (info->tty && C_CRTSCTS(info->tty)) {
- /*
- * For some reason, on the Power Macintosh,
- * it seems that the CTS bit is 1 when CTS is
- * *negated* and 0 when it is asserted.
- * The DCD bit doesn't seem to be inverted
- * like this.
- */
- if ((status & CTS) == 0) {
- if (info->private->tx_stopped) {
- info->private->tx_stopped = 0;
- if (!info->private->tx_active)
- transmit_chars(info);
- }
- } else {
- info->private->tx_stopped = 1;
- }
- }
-
- /* Clear status condition... */
- write_zsreg(info->private->zs_channel, 0, RES_EXT_INT);
- info->private->read_reg_zero = status;
-}
-
-/*
- * This is the serial driver's generic interrupt routine
- */
-void mac_SCC_interrupt(int irq, void *dev_id, struct pt_regs * regs)
-{
- struct m68k_async_struct *info = (struct m68k_async_struct *) dev_id;
- unsigned char zs_intreg;
- int shift;
-
- /* NOTE: The read register 3, which holds the irq status,
- * does so for both channels on each chip. Although
- * the status value itself must be read from the A
- * channel and is only valid when read from channel A.
- * Yes... broken hardware...
- */
-#define CHAN_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT)
-
-#ifdef SCC_DEBUG
- printk("mac_SCC: interrupt; port: %lx channel: %lx \n",
- info->port, info->private->zs_channel);
-#endif
-
- if (info->private->zs_chan_a == info->private->zs_channel)
- shift = 3; /* Channel A */
- else
- shift = 0; /* Channel B */
-
- for (;;) {
- zs_intreg = read_zsreg(info->private->zs_chan_a, 3);
-#ifdef SCC_DEBUG
- printk("mac_SCC: status %x shift %d shifted %x \n",
- zs_intreg, shift, zs_intreg >> shift);
-#endif
- zs_intreg = zs_intreg >> shift;
- if ((zs_intreg & CHAN_IRQMASK) == 0)
- break;
-
- if (zs_intreg & CHBRxIP)
- receive_chars(info, regs);
- if (zs_intreg & CHBTxIP)
- transmit_chars(info);
- if (zs_intreg & CHBEXT)
- status_handle(info);
- }
-}
-
-/*
- * -------------------------------------------------------------------
- * Here ends the serial interrupt routines.
- * -------------------------------------------------------------------
- */
-
-/*
- * ------------------------------------------------------------
- * rs_stop() and rs_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * ------------------------------------------------------------
- */
-
-static void SCC_enab_tx_int( struct m68k_async_struct *info, int enab_flag )
-{
- unsigned long flags;
-
- if (enab_flag) {
-#if 0
- save_flags(flags); cli();
- if (info->private->curregs[5] & TxENAB) {
- info->private->curregs[5] &= ~TxENAB;
- info->private->pendregs[5] &= ~TxENAB;
- write_zsreg(info->private->zs_channel, 5,
- info->private->curregs[5]);
- }
- restore_flags(flags);
-#endif
- /* FIXME: should call transmit_chars here ??? */
- transmit_chars(info);
- } else {
- save_flags(flags); cli();
-#if 0
- if ( info->xmit_cnt && info->xmit_buf &&
- !(info->private->curregs[5] & TxENAB)) {
- info->private->curregs[5] |= TxENAB;
- info->private->pendregs[5] = info->private->curregs[5];
- write_zsreg(info->private->zs_channel, 5,
- info->private->curregs[5]);
- }
-#else
- if ( info->xmit_cnt && info->xmit_buf &&
- !info->private->tx_active) {
- transmit_chars(info);
- }
-#endif
- restore_flags(flags);
- }
-
-}
-
-#if 0
-/*
- * leftover from original driver ...
- */
-static int SCC_startup(struct m68k_async_struct * info)
-{
- unsigned long flags;
-
- save_flags(flags); cli();
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("starting up ttyS%d (irq %d)...", info->line, info->irq);
-#endif
-
- /*
- * Clear the receive FIFO.
- */
- ZS_CLEARFIFO(info->private->zs_channel);
- info->xmit_fifo_size = 1;
-
- /*
- * Clear the interrupt registers.
- */
- write_zsreg(info->private->zs_channel, 0, ERR_RES);
- write_zsreg(info->private->zs_channel, 0, RES_H_IUS);
-
- /*
- * Turn on RTS and DTR.
- */
- zs_rtsdtr(info, 1);
-
- /*
- * Finally, enable sequencing and interrupts
- */
- info->private->curregs[1] = (info->private->curregs[1] & ~0x18)
- | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
- info->private->pendregs[1] = info->private->curregs[1];
- info->private->curregs[3] |= (RxENABLE | Rx8);
- info->private->pendregs[3] = info->private->curregs[3];
- info->private->curregs[5] |= (TxENAB | Tx8);
- info->private->pendregs[5] = info->private->curregs[5];
- info->private->curregs[9] |= (NV | MIE);
- info->private->pendregs[9] = info->private->curregs[9];
- write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]);
- write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);
- write_zsreg(info->private->zs_channel, 9, info->private->curregs[9]);
-
- /*
- * Set the speed of the serial port
- */
- SCC_change_speed(info);
-
- /* Save the current value of RR0 */
- info->private->read_reg_zero = read_zsreg(info->private->zs_channel, 0);
-
- restore_flags(flags);
- return 0;
-}
-#endif
-
-/* FIXME: are these required ?? */
-static int SCC_check_open( struct m68k_async_struct *info, struct tty_struct *tty,
- struct file *file )
-{
- /* check on the basis of info->whatever ?? */
- if (info->private->kgdb_channel || info->private->is_cons)
- return -EBUSY;
- return( 0 );
-}
-
-static void SCC_init( struct m68k_async_struct *info )
-{
- /* FIXME: init currently done in probe_sccs() */
-
- /* BUT: startup part needs to be done here! */
-
-#ifdef SCC_DEBUG
- printk("mac_SCC: init, info %lx, info->port %lx \n", info, info->port);
-#endif
- /*
- * Clear the receive FIFO.
- */
- ZS_CLEARFIFO(info->private->zs_channel);
- info->xmit_fifo_size = 1;
-
- /*
- * Clear the interrupt registers.
- */
- write_zsreg(info->private->zs_channel, 0, ERR_RES);
- write_zsreg(info->private->zs_channel, 0, RES_H_IUS);
-
- /*
- * Turn on RTS and DTR.
- */
- zs_rtsdtr(info, 1);
-
- /*
- * Finally, enable sequencing and interrupts
- */
- info->private->curregs[1] = (info->private->curregs[1] & ~0x18)
- | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
- info->private->pendregs[1] = info->private->curregs[1];
- info->private->curregs[3] |= (RxENABLE | Rx8);
- info->private->pendregs[3] = info->private->curregs[3];
- info->private->curregs[5] |= (TxENAB | Tx8);
- info->private->pendregs[5] = info->private->curregs[5];
- info->private->curregs[9] |= (NV | MIE);
- info->private->pendregs[9] = info->private->curregs[9];
- write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]);
- write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);
- write_zsreg(info->private->zs_channel, 9, info->private->curregs[9]);
-
- /*
- * Set the speed of the serial port - done in startup() !!
- */
-#if 0
- SCC_change_speed(info);
-#endif
-
- /* Save the current value of RR0 */
- info->private->read_reg_zero = read_zsreg(info->private->zs_channel, 0);
-
-}
-
-static void SCC_init_port( struct m68k_async_struct *info, int type, int channel )
-{
- static int got_autovector = 0;
-
-#ifdef SCC_DEBUG
- printk("mac_SCC: init_port, info %x \n", info);
-#endif
- info->sw = &SCC_switch;
- info->private = &zs_soft_private[channel];
- info->private->zs_channel = &zs_channels[channel];
- info->irq = IRQ4;
- info->private->clk_divisor = 16;
- info->private->zs_baud = get_zsbaud(info);
- info->port = (int) info->private->zs_channel->control;
-
- /*
- * MSch: Extended interrupt scheme:
- * The generic m68k interrupt code can't use multiple handlers for
- * the same interrupt source (no chained interrupts).
- * We have to plug in a 'master' interrupt handler instead, calling
- * mac_SCC_interrupt with the proper arguments ...
- */
-
- if (!got_autovector) {
- if(sys_request_irq(IRQ4, mac_SCC_handler, 0, "SCC master", info))
- panic("macserial: can't get irq %d", IRQ4);
-#ifdef SCC_DEBUG
- printk("mac_SCC: got SCC master interrupt %d, channel %d info %p\n",
- IRQ4, channel, info);
-#endif
- got_autovector = 1;
- }
-
- if (info->private->zs_chan_a == info->private->zs_channel) {
- /* Channel A */
- if (request_irq(IRQ_SCCA, mac_SCC_interrupt, 0, "SCC A", info))
- panic("mac_SCC: can't get irq %d", IRQ_SCCA);
-#ifdef SCC_DEBUG
- printk("mac_SCC: got SCC A interrupt %d, channel %d info %p\n",
- IRQ_SCCA, channel, info);
-#endif
- } else {
- /* Channel B */
- if (request_irq(IRQ_SCCB, mac_SCC_interrupt, 0, "SCC B", info))
- panic("mac_SCC: can't get irq %d", IRQ_SCCB);
-#ifdef SCC_DEBUG
- printk("mac_SCC: got SCC B interrupt %d, channel %d info %p\n",
- IRQ_SCCB, channel, info);
-#endif
- }
-
- /* If console serial line, then enable interrupts. */
- if (info->private->is_cons) {
- printk("mac_SCC: console line %d; enabling interrupt!\n", info->line);
- write_zsreg(info->private->zs_channel, R1,
- (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB));
- write_zsreg(info->private->zs_channel, R9, (NV | MIE));
- write_zsreg(info->private->zs_channel, R10, (NRZ));
- write_zsreg(info->private->zs_channel, R3, (Rx8 | RxENABLE));
- write_zsreg(info->private->zs_channel, R5, (Tx8 | TxENAB));
- }
- /* If this is the kgdb line, enable interrupts because we
- * now want to receive the 'control-c' character from the
- * client attached to us asynchronously.
- */
- if (info->private->kgdb_channel) {
- printk("mac_SCC: kgdb line %d; enabling interrupt!\n", info->line);
- kgdb_chaninit(info, 1, info->private->zs_baud);
- }
- /* Report settings (in m68kserial.c) */
-#ifndef CONFIG_MAC
- printk("ttyS%d at 0x%08x (irq = %d)", info->line,
- info->port, info->irq);
- printk(" is a Z8530 SCC\n");
-#endif
-
-}
-
-/*
- * This routine will shutdown a serial port; interrupts are disabled, and
- * DTR is dropped if the hangup on close termio flag is on.
- */
-static void SCC_deinit(struct m68k_async_struct * info, int leave_dtr)
-{
- unsigned long flags;
-
- save_flags(flags); cli(); /* Disable interrupts */
-
- info->private->pendregs[1] = info->private->curregs[1] = 0;
- write_zsreg(info->private->zs_channel, 1, 0); /* no interrupts */
-
- info->private->curregs[3] &= ~RxENABLE;
- info->private->pendregs[3] = info->private->curregs[3];
- write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]);
-
- info->private->curregs[5] &= ~TxENAB;
-
- if (!leave_dtr)
- info->private->curregs[5] &= ~(DTR | RTS);
- else
- info->private->curregs[5] &= ~(RTS);
-
- info->private->pendregs[5] = info->private->curregs[5];
- write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);
-
- restore_flags(flags);
-}
-
-/* FIXME !!! */
-static int SCC_check_custom_divisor( struct m68k_async_struct *info,
- int baud_base, int divisor )
-{
- return 0;
-}
-
-/*
- * This routine is called to set the UART divisor registers to match
- * the specified baud rate for a serial port.
- */
-static void SCC_change_speed(struct m68k_async_struct *info)
-{
- unsigned short port;
- unsigned cflag;
- int i;
- int brg;
- unsigned long flags;
-
- if (!info->tty || !info->tty->termios)
- return;
- cflag = info->tty->termios->c_cflag;
- if (!(port = info->port))
- return;
- i = cflag & CBAUD;
-
- if (i == 0 && !(info->flags & ASYNC_SPD_MASK)) {
- /* speed == 0 -> drop DTR */
- save_flags(flags);
- cli();
- info->private->curregs[5] &= ~(DTR | RTS);
- write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);
- restore_flags(flags);
- return;
- }
-
-
- if (i & CBAUDEX) {
- /* XXX CBAUDEX is not obeyed.
- * It is impossible at a 32bits PPC. XXX??
- * But we have to report this to user ... someday.
- */
- i = B9600;
- }
-
- save_flags(flags); cli();
- info->private->zs_baud = baud_table[i];
- info->private->clk_divisor = 16;
-
- info->private->curregs[4] = X16CLK;
- info->private->curregs[11] = TCBR | RCBR;
- brg = BPS_TO_BRG(info->private->zs_baud,
- ZS_CLOCK/info->private->clk_divisor);
- info->private->curregs[12] = (brg & 255);
- info->private->curregs[13] = ((brg >> 8) & 255);
- info->private->curregs[14] = BRENABL;
-
- /* byte size and parity */
- info->private->curregs[3] &= ~RxNBITS_MASK;
- info->private->curregs[5] &= ~TxNBITS_MASK;
- switch (cflag & CSIZE) {
- case CS5:
- info->private->curregs[3] |= Rx5;
- info->private->curregs[5] |= Tx5;
- break;
- case CS6:
- info->private->curregs[3] |= Rx6;
- info->private->curregs[5] |= Tx6;
- break;
- case CS7:
- info->private->curregs[3] |= Rx7;
- info->private->curregs[5] |= Tx7;
- break;
- case CS8:
- default: /* defaults to 8 bits */
- info->private->curregs[3] |= Rx8;
- info->private->curregs[5] |= Tx8;
- break;
- }
- info->private->pendregs[3] = info->private->curregs[3];
- info->private->pendregs[5] = info->private->curregs[5];
-
- info->private->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN);
- if (cflag & CSTOPB) {
- info->private->curregs[4] |= SB2;
- } else {
- info->private->curregs[4] |= SB1;
- }
- if (cflag & PARENB) {
- info->private->curregs[4] |= PAR_ENA;
- }
- if (!(cflag & PARODD)) {
- info->private->curregs[4] |= PAR_EVEN;
- }
- info->private->pendregs[4] = info->private->curregs[4];
-
- info->private->curregs[15] &= ~(DCDIE | CTSIE);
- if (!(cflag & CLOCAL)) {
- info->private->curregs[15] |= DCDIE;
- }
- if (cflag & CRTSCTS) {
- info->private->curregs[15] |= CTSIE;
- if ((read_zsreg(info->private->zs_channel, 0) & CTS) != 0)
- info->private->tx_stopped = 1;
- } else
- info->private->tx_stopped = 0;
- info->private->pendregs[15] = info->private->curregs[15];
-
- /* Load up the new values */
- load_zsregs(info->private->zs_channel, info->private->curregs);
-
- restore_flags(flags);
-}
-
-/* This is for console output over ttya/ttyb */
-static void SCC_put_char(char ch)
-{
- struct mac_zschannel *chan = zs_conschan;
- int loops = 0;
- unsigned long flags;
-
- if(!chan)
- return;
-
- save_flags(flags); cli();
- while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0 && loops < 10000) {
- loops++;
- udelay(5);
- }
- write_zsdata(chan, ch);
- restore_flags(flags);
-}
-
-/* These are for receiving and sending characters under the kgdb
- * source level kernel debugger.
- */
-void putDebugChar(char kgdb_char)
-{
- struct mac_zschannel *chan = zs_kgdbchan;
-
- while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0)
- udelay(5);
- write_zsdata(chan, kgdb_char);
-}
-
-char getDebugChar(void)
-{
- struct mac_zschannel *chan = zs_kgdbchan;
-
- while ((read_zsreg(chan, 0) & Rx_CH_AV) == 0)
- udelay(5);
- return read_zsdata(chan);
-}
-
-/*
- * Fair output driver allows a process to speak.
- */
-static void SCC_fair_output(void)
-{
- int left; /* Output no more than that */
- unsigned long flags;
- struct m68k_async_struct *info = zs_consinfo;
- char c;
-
- if (info == 0) return;
- if (info->xmit_buf == 0) return;
-
- save_flags(flags); cli();
- left = info->xmit_cnt;
- while (left != 0) {
- c = info->xmit_buf[info->xmit_tail];
- info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
- info->xmit_cnt--;
- restore_flags(flags);
-
- SCC_put_char(c);
-
- save_flags(flags); cli();
- left = MIN(info->xmit_cnt, left-1);
- }
-
- restore_flags(flags);
- return;
-}
-
-/*
- * zs_console_print is registered for printk.
- */
-static void zs_console_print(const char *p)
-{
- char c;
-
- while ((c = *(p++)) != 0) {
- if (c == '\n')
- SCC_put_char('\r');
- SCC_put_char(c);
- }
-
- /* Comment this if you want to have a strict interrupt-driven output */
- SCC_fair_output();
-}
-
-/* FIXME: check with SCC_enab_tx_int!! */
-#if 0
-static void rs_flush_chars(struct tty_struct *tty)
-{
- struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
- return;
-
- if (info->xmit_cnt <= 0 || tty->stopped || info->private->tx_stopped ||
- !info->xmit_buf)
- return;
-
- /* Enable transmitter */
- save_flags(flags); cli();
- transmit_chars(info);
- restore_flags(flags);
-}
-
-static int rs_write(struct tty_struct * tty, int from_user,
- const unsigned char *buf, int count)
-{
- int c, total = 0;
- struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->device, "rs_write"))
- return 0;
-
- if (!tty || !info->xmit_buf)
- return 0;
-
- save_flags(flags);
- while (1) {
- cli();
- c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
- SERIAL_XMIT_SIZE - info->xmit_head));
- if (c <= 0)
- break;
-
- if (from_user) {
- down(&tmp_buf_sem);
- memcpy_fromfs(tmp_buf, buf, c);
- c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
- SERIAL_XMIT_SIZE - info->xmit_head));
- memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
- up(&tmp_buf_sem);
- } else
- memcpy(info->xmit_buf + info->xmit_head, buf, c);
- info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
- info->xmit_cnt += c;
- restore_flags(flags);
- buf += c;
- count -= c;
- total += c;
- }
- if (info->xmit_cnt && !tty->stopped && !info->tx_stopped
- && !info->tx_active)
- transmit_chars(info);
- restore_flags(flags);
- return total;
-}
-#endif
-
-/*
- * ------------------------------------------------------------
- * rs_throttle()
- *
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled.
- * ------------------------------------------------------------
- */
-static void SCC_throttle(struct m68k_async_struct *info, int status)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- if (status) {
- /*
- * Here we want to turn off the RTS line. On Macintoshes,
- * we only get the DTR line, which goes to both DTR and
- * RTS on the modem. RTS doesn't go out to the serial
- * port socket. So you should make sure your modem is
- * set to ignore DTR if you're using CRTSCTS.
- */
- info->private->curregs[5] &= ~(DTR | RTS);
- info->private->pendregs[5] &= ~(DTR | RTS);
- write_zsreg(info->private->zs_channel, 5,
- info->private->curregs[5]);
- } else {
- /* Assert RTS and DTR lines */
- info->private->curregs[5] |= DTR | RTS;
- info->private->pendregs[5] |= DTR | RTS;
- write_zsreg(info->private->zs_channel, 5,
- info->private->curregs[5]);
- }
-
- restore_flags(flags);
-
-}
-
-/*
- * ------------------------------------------------------------
- * rs_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-static void SCC_get_serial_info(struct m68k_async_struct * info,
- struct serial_struct * retinfo)
-{
- retinfo->baud_base = info->baud_base;
- retinfo->custom_divisor = info->custom_divisor;
-}
-
-/* FIXME: set_serial_info needs check_custom_divisor !!! */
-
-/*
- * get_lsr_info - get line status register info
- *
- * Purpose: Let user call ioctl() to get info when the UART physically
- * is emptied. On bus types like RS485, the transmitter must
- * release the bus after transmitting. This must be done when
- * the transmit shift register is empty, not be done when the
- * transmit holding register is empty. This functionality
- * allows an RS485 driver to be written in user space.
- */
-static int SCC_get_lsr_info(struct m68k_async_struct * info, unsigned int *value)
-{
- unsigned char status;
-
- cli();
- status = read_zsreg(info->private->zs_channel, 0);
- sti();
- return status;
-}
-
-static unsigned int SCC_get_modem_info(struct m68k_async_struct *info)
-{
- unsigned char control, status;
- unsigned int result;
-
- cli();
- control = info->private->curregs[5];
- status = read_zsreg(info->private->zs_channel, 0);
- sti();
- result = ((control & RTS) ? TIOCM_RTS: 0)
- | ((control & DTR) ? TIOCM_DTR: 0)
- | ((status & DCD) ? TIOCM_CAR: 0)
- | ((status & CTS) ? 0: TIOCM_CTS);
- return result;
-}
-
-/* FIXME: zs_setdtr was used in rs_open ... */
-
-static int SCC_set_modem_info(struct m68k_async_struct *info,
- int new_dtr, int new_rts)
-{
- unsigned int bits;
-
- bits = (new_rts ? RTS: 0) + (new_dtr ? DTR: 0);
- info->private->curregs[5] = (info->private->curregs[5] & ~(DTR | RTS)) | bits;
- info->private->pendregs[5] = info->private->curregs[5];
- write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);
- sti();
- return 0;
-}
-
-/*
- * This routine sends a break character out the serial port.
- */
-static void SCC_set_break(struct m68k_async_struct * info, int break_flag)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- if (break_flag) {
- info->private->curregs[5] |= SND_BRK;
- write_zsreg(info->private->zs_channel, 5,
- info->private->curregs[5]);
- } else {
- info->private->curregs[5] &= ~SND_BRK;
- write_zsreg(info->private->zs_channel, 5,
- info->private->curregs[5]);
- }
-
- restore_flags(flags);
-}
-
-/* FIXME: these have to be enabled in rs_ioctl !! */
-
-static int SCC_ioctl(struct tty_struct *tty, struct file * file,
- struct m68k_async_struct * info, unsigned int cmd,
- unsigned long arg)
-{
- int error;
-
- switch (cmd) {
- case TIOCSERGETLSR: /* Get line status register */
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned int));
- if (error)
- return error;
- else
- return SCC_get_lsr_info(info, (unsigned int *) arg);
-
- case TIOCSERGSTRUCT:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(struct m68k_async_struct));
- if (error)
- return error;
- copy_to_user((struct m68k_async_struct *) arg,
- info, sizeof(struct m68k_async_struct));
- return 0;
-
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
-}
-
-static void SCC_stop_receive (struct m68k_async_struct *info)
-{
- /* disable Rx */
- info->private->curregs[3] &= ~RxENABLE;
- info->private->pendregs[3] = info->private->curregs[3];
- write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]);
- /* disable Rx interrupts */
- info->private->curregs[1] &= ~(0x18); /* disable any rx ints */
- info->private->pendregs[1] = info->private->curregs[1];
- write_zsreg(info->private->zs_channel, 1, info->private->curregs[1]);
- ZS_CLEARFIFO(info->private->zs_channel);
-}
-
-static int SCC_trans_empty (struct m68k_async_struct *info)
-{
- return (read_zsreg(info->private->zs_channel, 1) & ALL_SNT) != 0;
-}
-
-/* Finally, routines used to initialize the serial driver. */
-
-#ifdef CONFIG_MAC
-
-/*
- * Mac: use boot_info data; assume 2 channels
- */
-
-static void probe_sccs(void)
-{
- int n;
-
-#define ZS_CONTROL 0x50F04000
-#define ZS_DATA (ZS_CONTROL+4)
-#define ZS_IRQ 5
-#define ZS_MOVE -2
-#define ZS_DATA_MOVE 4
-#define ZS_CH_A_FIRST 2
-
- /* last-ditch fixup for NetBSD booter case */
- if (mac_bi_data.sccbase == 0)
- mac_bi_data.sccbase = ZS_CONTROL;
-
- /* testing: fix up broken 24 bit addresses (ClassicII) */
- if ((mac_bi_data.sccbase & 0x00FFFFFF) == mac_bi_data.sccbase)
- mac_bi_data.sccbase |= 0x50000000;
-
- if ( !hwreg_present((void *)mac_bi_data.sccbase))
- {
- printk(KERN_WARNING "z8530: Serial devices not accessible. Check serial switch.\n");
- return;
- }
-
- for(n=0;n<2;n++)
- {
-#if 0
- zs_channels[n].control = (volatile unsigned char *)
- ZS_CONTROL+ZS_MOVE*n;
- zs_channels[n].data = (volatile unsigned char *)ZS_DATA+ZS_MOVE*n;
-#else
- zs_channels[n].control = (volatile unsigned char *) /* 2, 0 */
- (mac_bi_data.sccbase+ZS_CH_A_FIRST)+ZS_MOVE*n;
- zs_channels[n].data = (volatile unsigned char *) /* 6, 4 */
- (mac_bi_data.sccbase+ZS_CH_A_FIRST+ZS_DATA_MOVE)+ZS_MOVE*n;
-#endif
- zs_soft[n].private = &zs_soft_private[n];
- zs_soft[n].private->zs_channel = &zs_channels[n];
- zs_soft[n].irq = IRQ4;
-#if 0
- if (request_irq(ch->intrs[0], rs_interrupt, 0,
- "SCC", &zs_soft[n]))
- panic("macserial: can't get irq %d",
- ch->intrs[0]);
-#endif
- if (n & 1)
- zs_soft[n].private->zs_chan_a = &zs_channels[n-1];
- else
- zs_soft[n].private->zs_chan_a = &zs_channels[n];
- }
-
- zs_channels_found=2;
-}
-
-#else
-
-/*
- * PowerMAC - query the PROM
- */
-
-static void show_serial_version(void)
-{
- printk("PowerMac Z8530 serial driver version 1.00\n");
-}
-
-/* Ask the PROM how many Z8530s we have and initialize their zs_channels */
-static void
-probe_sccs()
-{
- struct device_node *dev, *ch;
- struct m68k_async_struct **pp;
- int n;
-
- n = 0;
- pp = &zs_chain;
- for (dev = find_devices("escc"); dev != 0; dev = dev->next) {
- if (n >= NUM_CHANNELS) {
- printk("Sorry, can't use %s: no more channels\n",
- dev->full_name);
- continue;
- }
- for (ch = dev->child; ch != 0; ch = ch->sibling) {
- if (ch->n_addrs < 1 || ch ->n_intrs < 1) {
- printk("Can't use %s: %d addrs %d intrs\n",
- ch->full_name, ch->n_addrs, ch->n_intrs);
- continue;
- }
- zs_channels[n].control = (volatile unsigned char *)
- ch->addrs[0].address;
- zs_channels[n].data = zs_channels[n].control
- + ch->addrs[0].size / 2;
- zs_soft[n].private = &zs_soft_private[n];
- zs_soft[n].private->zs_channel = &zs_channels[n];
- zs_soft[n].irq = ch->intrs[0];
- if (request_irq(ch->intrs[0], mac_SCC_interrupt, 0,
- "SCC", &zs_soft[n]))
- panic("macserial: can't get irq %d",
- ch->intrs[0]);
- /* XXX this assumes the prom puts chan A before B */
- if (n & 1)
- zs_soft[n].private->zs_chan_a = &zs_channels[n-1];
- else
- zs_soft[n].private->zs_chan_a = &zs_channels[n];
-
- *pp = &zs_soft[n];
- pp = &zs_soft[n].private->zs_next;
- ++n;
- }
- }
- *pp = 0;
- zs_channels_found = n;
-}
-
-#endif
-
-extern void register_console(void (*proc)(const char *));
-
-static inline void
-rs_cons_check(struct m68k_async_struct *ss, int channel)
-{
- int i, o, io;
- static int consout_registered = 0;
- static int msg_printed = 0;
-
- i = o = io = 0;
-
- /* Is this one of the serial console lines? */
- if ((zs_cons_chanout != channel) &&
- (zs_cons_chanin != channel))
- return;
- zs_conschan = ss->private->zs_channel;
- zs_consinfo = ss;
-
- /* Register the console output putchar, if necessary */
- if (zs_cons_chanout == channel) {
- o = 1;
- /* double whee.. */
- if (!consout_registered) {
- register_console(zs_console_print);
- consout_registered = 1;
- }
- }
-
- if (zs_cons_chanin == channel) {
- i = 1;
- }
- if (o && i)
- io = 1;
- if (ss->private->zs_baud != 9600)
- panic("Console baud rate weirdness");
-
- /* Set flag variable for this port so that it cannot be
- * opened for other uses by accident.
- */
- ss->private->is_cons = 1;
-
- if (io) {
- if(!msg_printed) {
- printk("zs%d: console I/O\n", ((channel>>1)&1));
- msg_printed = 1;
- }
- } else {
- printk("zs%d: console %s\n", ((channel>>1)&1),
- (i==1 ? "input" : (o==1 ? "output" : "WEIRD")));
- }
-
- /* FIXME : register interrupt here??? */
-}
-
-volatile int test_done;
-
-/* rs_init inits the driver */
-int mac_SCC_init(void)
-{
- int channel, line, nr = 0;
- unsigned long flags;
- struct serial_struct req;
-
- printk("Mac68K Z8530 serial driver version 1.01\n");
-
- /* SCC present at all? */
- if (!MACH_IS_MAC)
- return( -ENODEV );
-
- if (zs_chain == 0)
- probe_sccs();
-
- save_flags(flags);
- cli();
-
- /*
- * FIXME: init of rs_table entry and register_serial now done,
- * but possible clash of zs_soft[channel] and rs_table[channel]!!
- * zs_soft initialized in probe_sccs(), some settings copied to
- * info = &rs_table[channel], which is used by the mid-level code.
- * The info->private part is shared among both!
- */
-
- for (channel = 0; channel < zs_channels_found; ++channel) {
- req.line = channel;
- req.type = SER_SCC_MAC;
- req.port = (int) zs_soft[channel].private->zs_channel->control;
-
- if ((line = register_serial( &req )) >= 0) {
- SCC_init_port( &rs_table[line], req.type, line );
- ++nr;
- }
- else
- printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );
- }
-
- restore_flags(flags);
-
- return( nr > 0 ? 0 : -ENODEV );
-}
-
-/* Hooks for running a serial console. con_init() calls this if the
- * console is being run over one of the serial ports.
- * 'channel' is decoded as 0=modem 1=printer, 'chip' is ignored.
- */
-void
-rs_cons_hook(int chip, int out, int channel)
-{
- if (zs_chain == 0)
- probe_sccs();
- zs_soft[channel].private->clk_divisor = 16;
- zs_soft[channel].private->zs_baud = get_zsbaud(&zs_soft[channel]);
- rs_cons_check(&zs_soft[channel], channel);
- if (out)
- zs_cons_chanout = channel;
- else
- zs_cons_chanin = channel;
-
- /* FIXME : register interrupt here??? */
-}
-
-/* This is called at boot time to prime the kgdb serial debugging
- * serial line. The 'tty_num' argument is 0 for /dev/ttyS0 and 1
- * for /dev/ttyS1 which is determined in setup_arch() from the
- * boot command line flags.
- */
-void
-rs_kgdb_hook(int tty_num)
-{
- if (zs_chain == 0)
- probe_sccs();
- zs_kgdbchan = zs_soft[tty_num].private->zs_channel;
- zs_soft[tty_num].private->clk_divisor = 16;
- zs_soft[tty_num].private->zs_baud = get_zsbaud(&zs_soft[tty_num]);
- zs_soft[tty_num].private->kgdb_channel = 1; /* This runs kgdb */
- zs_soft[tty_num ^ 1].private->kgdb_channel = 0; /* This does not */
- /* Turn on transmitter/receiver at 8-bits/char */
- kgdb_chaninit(&zs_soft[tty_num], 0, 9600);
- ZS_CLEARERR(zs_kgdbchan);
- ZS_CLEARFIFO(zs_kgdbchan);
-
- /* FIXME : register interrupt here??? */
-}
-
+++ /dev/null
-/*
- * macserial.h: Definitions for the Macintosh Z8530 serial driver.
- *
- * Adapted from drivers/sbus/char/sunserial.h by Paul Mackerras.
- *
- * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- */
-#ifndef _MAC_SCC_H
-#define _MAC_SCC_H
-
-/*
- * For the close wait times, 0 means wait forever for serial port to
- * flush its output. 65535 means don't wait at all.
- */
-#define ZILOG_CLOSING_WAIT_INF 0
-#define ZILOG_CLOSING_WAIT_NONE 65535
-
-/*
- * Definitions for ZILOG_struct (and serial_struct) flags field
- */
-#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
- on the callout port */
-#define ZILOG_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
-#define ZILOG_SAK 0x0004 /* Secure Attention Key (Orange book) */
-#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
-
-#define ZILOG_SPD_MASK 0x0030
-#define ZILOG_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */
-
-#define ZILOG_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */
-#define ZILOG_SPD_CUST 0x0030 /* Use user-specified divisor */
-
-#define ZILOG_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
-#define ZILOG_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
-#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
-#define ZILOG_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
-#define ZILOG_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
-
-#define ZILOG_FLAGS 0x0FFF /* Possible legal ZILOG flags */
-#define ZILOG_USR_MASK 0x0430 /* Legal flags that non-privileged
- * users can set or reset */
-
-/* Internal flags used only by kernel/chr_drv/serial.c */
-#define ZILOG_INITIALIZED 0x80000000 /* Serial port was initialized */
-#define ZILOG_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
-#define ZILOG_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
-#define ZILOG_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
-#define ZILOG_CLOSING 0x08000000 /* Serial port is closing */
-#define ZILOG_CTS_FLOW 0x04000000 /* Do CTS flow control */
-#define ZILOG_CHECK_CD 0x02000000 /* i.e., CLOCAL */
-
-/* Software state per channel */
-
-#ifdef __KERNEL__
-/*
- * This is our internal structure for each serial port's state.
- *
- * Many fields are paralleled by the structure used by the serial_struct
- * structure.
- *
- * For definitions of the flags field, see tty.h
- */
-
-/* MSch: gone to <asm/serial.h> */
-
-#define SERIAL_MAGIC 0x5301
-
-/*
- * The size of the serial xmit buffer is 1 page, or 4096 bytes
- */
-#define SERIAL_XMIT_SIZE 4096
-
-/*
- * Events are used to schedule things to happen at timer-interrupt
- * time, instead of at rs interrupt time.
- */
-#define RS_EVENT_WRITE_WAKEUP 0
-
-#endif /* __KERNEL__ */
-
-/* Conversion routines to/from brg time constants from/to bits
- * per second.
- */
-#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
-#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
-
-/* The Zilog register set */
-
-#define FLAG 0x7e
-
-/* Write Register 0 */
-#define R0 0 /* Register selects */
-#define R1 1
-#define R2 2
-#define R3 3
-#define R4 4
-#define R5 5
-#define R6 6
-#define R7 7
-#define R8 8
-#define R9 9
-#define R10 10
-#define R11 11
-#define R12 12
-#define R13 13
-#define R14 14
-#define R15 15
-
-#define NULLCODE 0 /* Null Code */
-#define POINT_HIGH 0x8 /* Select upper half of registers */
-#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */
-#define SEND_ABORT 0x18 /* HDLC Abort */
-#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */
-#define RES_Tx_P 0x28 /* Reset TxINT Pending */
-#define ERR_RES 0x30 /* Error Reset */
-#define RES_H_IUS 0x38 /* Reset highest IUS */
-
-#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */
-#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */
-#define RES_EOM_L 0xC0 /* Reset EOM latch */
-
-/* Write Register 1 */
-
-#define EXT_INT_ENAB 0x1 /* Ext Int Enable */
-#define TxINT_ENAB 0x2 /* Tx Int Enable */
-#define PAR_SPEC 0x4 /* Parity is special condition */
-
-#define RxINT_DISAB 0 /* Rx Int Disable */
-#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */
-#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
-#define INT_ERR_Rx 0x18 /* Int on error only */
-
-#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
-#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
-#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
-
-/* Write Register #2 (Interrupt Vector) */
-
-/* Write Register 3 */
-
-#define RxENABLE 0x1 /* Rx Enable */
-#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
-#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
-#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
-#define ENT_HM 0x10 /* Enter Hunt Mode */
-#define AUTO_ENAB 0x20 /* Auto Enables */
-#define Rx5 0x0 /* Rx 5 Bits/Character */
-#define Rx7 0x40 /* Rx 7 Bits/Character */
-#define Rx6 0x80 /* Rx 6 Bits/Character */
-#define Rx8 0xc0 /* Rx 8 Bits/Character */
-#define RxNBITS_MASK 0xc0
-
-/* Write Register 4 */
-
-#define PAR_ENA 0x1 /* Parity Enable */
-#define PAR_EVEN 0x2 /* Parity Even/Odd* */
-
-#define SYNC_ENAB 0 /* Sync Modes Enable */
-#define SB1 0x4 /* 1 stop bit/char */
-#define SB15 0x8 /* 1.5 stop bits/char */
-#define SB2 0xc /* 2 stop bits/char */
-#define SB_MASK 0xc
-
-#define MONSYNC 0 /* 8 Bit Sync character */
-#define BISYNC 0x10 /* 16 bit sync character */
-#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */
-#define EXTSYNC 0x30 /* External Sync Mode */
-
-#define X1CLK 0x0 /* x1 clock mode */
-#define X16CLK 0x40 /* x16 clock mode */
-#define X32CLK 0x80 /* x32 clock mode */
-#define X64CLK 0xC0 /* x64 clock mode */
-#define XCLK_MASK 0xC0
-
-/* Write Register 5 */
-
-#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
-#define RTS 0x2 /* RTS */
-#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
-#define TxENAB 0x8 /* Tx Enable */
-#define SND_BRK 0x10 /* Send Break */
-#define Tx5 0x0 /* Tx 5 bits (or less)/character */
-#define Tx7 0x20 /* Tx 7 bits/character */
-#define Tx6 0x40 /* Tx 6 bits/character */
-#define Tx8 0x60 /* Tx 8 bits/character */
-#define TxNBITS_MASK 0x60
-#define DTR 0x80 /* DTR */
-
-/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
-
-/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
-
-/* Write Register 8 (transmit buffer) */
-
-/* Write Register 9 (Master interrupt control) */
-#define VIS 1 /* Vector Includes Status */
-#define NV 2 /* No Vector */
-#define DLC 4 /* Disable Lower Chain */
-#define MIE 8 /* Master Interrupt Enable */
-#define STATHI 0x10 /* Status high */
-#define NORESET 0 /* No reset on write to R9 */
-#define CHRB 0x40 /* Reset channel B */
-#define CHRA 0x80 /* Reset channel A */
-#define FHWRES 0xc0 /* Force hardware reset */
-
-/* Write Register 10 (misc control bits) */
-#define BIT6 1 /* 6 bit/8bit sync */
-#define LOOPMODE 2 /* SDLC Loop mode */
-#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */
-#define MARKIDLE 8 /* Mark/flag on idle */
-#define GAOP 0x10 /* Go active on poll */
-#define NRZ 0 /* NRZ mode */
-#define NRZI 0x20 /* NRZI mode */
-#define FM1 0x40 /* FM1 (transition = 1) */
-#define FM0 0x60 /* FM0 (transition = 0) */
-#define CRCPS 0x80 /* CRC Preset I/O */
-
-/* Write Register 11 (Clock Mode control) */
-#define TRxCXT 0 /* TRxC = Xtal output */
-#define TRxCTC 1 /* TRxC = Transmit clock */
-#define TRxCBR 2 /* TRxC = BR Generator Output */
-#define TRxCDP 3 /* TRxC = DPLL output */
-#define TRxCOI 4 /* TRxC O/I */
-#define TCRTxCP 0 /* Transmit clock = RTxC pin */
-#define TCTRxCP 8 /* Transmit clock = TRxC pin */
-#define TCBR 0x10 /* Transmit clock = BR Generator output */
-#define TCDPLL 0x18 /* Transmit clock = DPLL output */
-#define RCRTxCP 0 /* Receive clock = RTxC pin */
-#define RCTRxCP 0x20 /* Receive clock = TRxC pin */
-#define RCBR 0x40 /* Receive clock = BR Generator output */
-#define RCDPLL 0x60 /* Receive clock = DPLL output */
-#define RTxCX 0x80 /* RTxC Xtal/No Xtal */
-
-/* Write Register 12 (lower byte of baud rate generator time constant) */
-
-/* Write Register 13 (upper byte of baud rate generator time constant) */
-
-/* Write Register 14 (Misc control bits) */
-#define BRENABL 1 /* Baud rate generator enable */
-#define BRSRC 2 /* Baud rate generator source */
-#define DTRREQ 4 /* DTR/Request function */
-#define AUTOECHO 8 /* Auto Echo */
-#define LOOPBAK 0x10 /* Local loopback */
-#define SEARCH 0x20 /* Enter search mode */
-#define RMC 0x40 /* Reset missing clock */
-#define DISDPLL 0x60 /* Disable DPLL */
-#define SSBR 0x80 /* Set DPLL source = BR generator */
-#define SSRTxC 0xa0 /* Set DPLL source = RTxC */
-#define SFMM 0xc0 /* Set FM mode */
-#define SNRZI 0xe0 /* Set NRZI mode */
-
-/* Write Register 15 (external/status interrupt control) */
-#define ZCIE 2 /* Zero count IE */
-#define DCDIE 8 /* DCD IE */
-#define SYNCIE 0x10 /* Sync/hunt IE */
-#define CTSIE 0x20 /* CTS IE */
-#define TxUIE 0x40 /* Tx Underrun/EOM IE */
-#define BRKIE 0x80 /* Break/Abort IE */
-
-
-/* Read Register 0 */
-#define Rx_CH_AV 0x1 /* Rx Character Available */
-#define ZCOUNT 0x2 /* Zero count */
-#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
-#define DCD 0x8 /* DCD */
-#define SYNC_HUNT 0x10 /* Sync/hunt */
-#define CTS 0x20 /* CTS */
-#define TxEOM 0x40 /* Tx underrun */
-#define BRK_ABRT 0x80 /* Break/Abort */
-
-/* Read Register 1 */
-#define ALL_SNT 0x1 /* All sent */
-/* Residue Data for 8 Rx bits/char programmed */
-#define RES3 0x8 /* 0/3 */
-#define RES4 0x4 /* 0/4 */
-#define RES5 0xc /* 0/5 */
-#define RES6 0x2 /* 0/6 */
-#define RES7 0xa /* 0/7 */
-#define RES8 0x6 /* 0/8 */
-#define RES18 0xe /* 1/8 */
-#define RES28 0x0 /* 2/8 */
-/* Special Rx Condition Interrupts */
-#define PAR_ERR 0x10 /* Parity error */
-#define Rx_OVR 0x20 /* Rx Overrun Error */
-#define FRM_ERR 0x40 /* CRC/Framing Error */
-#define END_FR 0x80 /* End of Frame (SDLC) */
-
-/* Read Register 2 (channel b only) - Interrupt vector */
-
-/* Read Register 3 (interrupt pending register) ch a only */
-#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
-#define CHBTxIP 0x2 /* Channel B Tx IP */
-#define CHBRxIP 0x4 /* Channel B Rx IP */
-#define CHAEXT 0x8 /* Channel A Ext/Stat IP */
-#define CHATxIP 0x10 /* Channel A Tx IP */
-#define CHARxIP 0x20 /* Channel A Rx IP */
-
-/* Read Register 8 (receive data register) */
-
-/* Read Register 10 (misc status bits) */
-#define ONLOOP 2 /* On loop */
-#define LOOPSEND 0x10 /* Loop sending */
-#define CLK2MIS 0x40 /* Two clocks missing */
-#define CLK1MIS 0x80 /* One clock missing */
-
-/* Read Register 12 (lower byte of baud rate generator constant) */
-
-/* Read Register 13 (upper byte of baud rate generator constant) */
-
-/* Read Register 15 (value of WR 15) */
-
-/* Misc macros */
-#define ZS_CLEARERR(channel) (write_zsreg(channel, 0, ERR_RES))
-#define ZS_CLEARFIFO(channel) do { volatile unsigned char garbage; \
- garbage = read_zsdata(channel); \
- garbage = read_zsdata(channel); \
- garbage = read_zsdata(channel); \
- } while(0)
-
-#endif /* !(_MAC_SCC_H) */
L_OBJS += via-pmu.o
endif
+ifdef CONFIG_ADB_PMU68K
+ L_OBJS += via-pmu68k.o
+endif
+
ifdef CONFIG_ADB_MACIO
L_OBJS += macio-adb.o
endif
--- /dev/null
+/*
+ * I/O Processor (IOP) ADB Driver
+ * Written and (C) 1999 by Joshua M. Thompson (funaho@jurai.org)
+ * Based on via-cuda.c by Paul Mackerras.
+ *
+ * 1999-07-01 (jmt) - First implementation for new driver architecture.
+ *
+ * 1999-07-31 (jmt) - First working version.
+ *
+ * TODO:
+ *
+ * o Implement SRQ handling.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+
+#include <asm/bootinfo.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_iop.h>
+#include <asm/mac_oss.h>
+#include <asm/adb_iop.h>
+
+#include <linux/adb.h>
+
+/*#define DEBUG_ADB_IOP*/
+
+extern void iop_ism_irq(int, void *, struct pt_regs *);
+
+static struct adb_request *current_req;
+static struct adb_request *last_req;
+#if 0
+static unsigned char reply_buff[16];
+static unsigned char *reply_ptr;
+#endif
+
+static enum adb_iop_state {
+ idle,
+ sending,
+ awaiting_reply
+} adb_iop_state;
+
+static void adb_iop_start(void);
+static int adb_iop_probe(void);
+static int adb_iop_init(void);
+static int adb_iop_send_request(struct adb_request *, int);
+static int adb_iop_write(struct adb_request *);
+static int adb_iop_autopoll(int);
+static void adb_iop_poll(void);
+static int adb_iop_reset_bus(void);
+
+struct adb_driver adb_iop_driver = {
+ "ISM IOP",
+ adb_iop_probe,
+ adb_iop_init,
+ adb_iop_send_request,
+ adb_iop_autopoll,
+ adb_iop_poll,
+ adb_iop_reset_bus
+};
+
+static void adb_iop_end_req(struct adb_request *req, int state)
+{
+ req->complete = 1;
+ current_req = req->next;
+ if (req->done) (*req->done)(req);
+ adb_iop_state = state;
+}
+
+/*
+ * Completion routine for ADB commands sent to the IOP.
+ *
+ * This will be called when a packet has been successfully sent.
+ */
+
+static void adb_iop_complete(struct iop_msg *msg, struct pt_regs *regs)
+{
+ struct adb_request *req;
+ uint flags;
+
+ save_flags(flags);
+ cli();
+
+ req = current_req;
+ if ((adb_iop_state == sending) && req && req->reply_expected) {
+ adb_iop_state = awaiting_reply;
+ }
+
+ restore_flags(flags);
+}
+
+/*
+ * Listen for ADB messages from the IOP.
+ *
+ * This will be called when unsolicited messages (usually replies to TALK
+ * commands or autopoll packets) are received.
+ */
+
+static void adb_iop_listen(struct iop_msg *msg, struct pt_regs *regs)
+{
+ struct adb_iopmsg *amsg = (struct adb_iopmsg *) msg->message;
+ struct adb_request *req;
+ uint flags;
+
+ save_flags(flags);
+ cli();
+
+ req = current_req;
+
+#ifdef DEBUG_ADB_IOP
+ printk("adb_iop_listen: rcvd packet, %d bytes: %02X %02X",
+ (uint) amsg->count + 2, (uint) amsg->flags, (uint) amsg->cmd);
+ i = 0;
+ while (i < amsg->count) {
+ printk(" %02X", (uint) amsg->data[i++]);
+ }
+ printk("\n");
+#endif
+
+ /* Handle a timeout. Timeout packets seem to occur even after */
+ /* we've gotten a valid reply to a TALK, so I'm assuming that */
+ /* a "timeout" is actually more like an "end-of-data" signal. */
+ /* We need to send back a timeout packet to the IOP to shut */
+ /* it up, plus complete the current request, if any. */
+
+ if (amsg->flags & ADB_IOP_TIMEOUT) {
+ msg->reply[0] = ADB_IOP_TIMEOUT | ADB_IOP_AUTOPOLL;
+ msg->reply[1] = 0;
+ msg->reply[2] = 0;
+ if (req && (adb_iop_state != idle)) {
+ adb_iop_end_req(req, idle);
+ }
+ } else {
+ /* TODO: is it possible for more tha one chunk of data */
+ /* to arrive before the timeout? If so we need to */
+ /* use reply_ptr here like the other drivers do. */
+ if ((adb_iop_state == awaiting_reply) &&
+ (amsg->flags & ADB_IOP_EXPLICIT)) {
+ req->reply_len = amsg->count + 1;
+ memcpy(req->reply, &amsg->cmd, req->reply_len);
+ } else {
+ adb_input(&amsg->cmd, amsg->count + 1, regs,
+ amsg->flags & ADB_IOP_AUTOPOLL);
+ }
+ memcpy(msg->reply, msg->message, IOP_MSG_LEN);
+ }
+ iop_complete_message(msg);
+ restore_flags(flags);
+}
+
+/*
+ * Start sending an ADB packet, IOP style
+ *
+ * There isn't much to do other than hand the packet over to the IOP
+ * after encapsulating it in an adb_iopmsg.
+ */
+
+static void adb_iop_start(void)
+{
+ unsigned long flags;
+ struct adb_request *req;
+ struct adb_iopmsg amsg;
+
+ /* get the packet to send */
+ req = current_req;
+ if (!req) return;
+
+ save_flags(flags);
+ cli();
+
+#ifdef DEBUG_ADB_IOP
+ printk("adb_iop_start: sending packet, %d bytes:", req->nbytes);
+ for (i = 0 ; i < req->nbytes ; i++)
+ printk(" %02X", (uint) req->data[i]);
+ printk("\n");
+#endif
+
+ /* The IOP takes MacII-style packets, so */
+ /* strip the initial ADB_PACKET byte. */
+
+ amsg.flags = ADB_IOP_EXPLICIT;
+ amsg.count = req->nbytes - 2;
+
+ /* amsg.data immediately follows amsg.cmd, effectively making */
+ /* amsg.cmd a pointer to the beginning of a full ADB packet. */
+ memcpy(&amsg.cmd, req->data + 1, req->nbytes - 1);
+
+ req->sent = 1;
+ adb_iop_state = sending;
+ restore_flags(flags);
+
+ /* Now send it. The IOP manager will call adb_iop_complete */
+ /* when the packet has been sent. */
+
+ iop_send_message(ADB_IOP, ADB_CHAN, req,
+ sizeof(amsg), (__u8 *) &amsg, adb_iop_complete);
+}
+
+int adb_iop_probe(void)
+{
+ if (!iop_ism_present) return -ENODEV;
+ return 0;
+}
+
+int adb_iop_init(void)
+{
+ printk("adb: IOP ISM driver v0.4 for Unified ADB.\n");
+ iop_listen(ADB_IOP, ADB_CHAN, adb_iop_listen, "ADB");
+ return 0;
+}
+
+int adb_iop_send_request(struct adb_request *req, int sync)
+{
+ int err;
+
+ err = adb_iop_write(req);
+ if (err) return err;
+
+ if (sync) {
+ while (!req->complete) adb_iop_poll();
+ }
+ return 0;
+}
+
+static int adb_iop_write(struct adb_request *req)
+{
+ unsigned long flags;
+
+ if ((req->nbytes < 2) || (req->data[0] != ADB_PACKET)) {
+ req->complete = 1;
+ return -EINVAL;
+ }
+
+ save_flags(flags);
+ cli();
+
+ req->next = 0;
+ req->sent = 0;
+ req->complete = 0;
+ req->reply_len = 0;
+
+ if (current_req != 0) {
+ last_req->next = req;
+ last_req = req;
+ } else {
+ current_req = req;
+ last_req = req;
+ }
+
+ restore_flags(flags);
+ if (adb_iop_state == idle) adb_iop_start();
+ return 0;
+}
+
+int adb_iop_autopoll(int devs)
+{
+ /* TODO: how do we enable/disable autopoll? */
+ return 0;
+}
+
+void adb_iop_poll(void)
+{
+ if (adb_iop_state == idle) adb_iop_start();
+ iop_ism_irq(0, (void *) ADB_IOP, NULL);
+}
+
+int adb_iop_reset_bus(void)
+{
+ struct adb_request req;
+
+ req.reply_expected = 0;
+ req.nbytes = 2;
+ req.data[0] = ADB_PACKET;
+ req.data[1] = 0; /* RESET */
+ adb_iop_write(&req);
+ while (!req.complete) adb_iop_poll();
+ return 0;
+}
--- /dev/null
+/*
+ * Device driver for the via ADB on (many) Mac II-class machines
+ *
+ * Based on the original ADB keyboard handler Copyright (c) 1997 Alan Cox
+ * Also derived from code Copyright (C) 1996 Paul Mackerras.
+ *
+ * With various updates provided over the years by Michael Schmitz,
+ * Guideo Koerber and others.
+ *
+ * Rewrite for Unified ADB by Joshua M. Thompson (funaho@jurai.org)
+ *
+ * 1999-08-02 (jmt) - Initial rewrite for Unified ADB.
+ */
+
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/adb.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+#include <asm/mac_via.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/init.h>
+
+static volatile unsigned char *via;
+
+/* VIA registers - spaced 0x200 bytes apart */
+#define RS 0x200 /* skip between registers */
+#define B 0 /* B-side data */
+#define A RS /* A-side data */
+#define DIRB (2*RS) /* B-side direction (1=output) */
+#define DIRA (3*RS) /* A-side direction (1=output) */
+#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */
+#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */
+#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */
+#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */
+#define T2CL (8*RS) /* Timer 2 ctr/latch (low 8 bits) */
+#define T2CH (9*RS) /* Timer 2 counter (high 8 bits) */
+#define SR (10*RS) /* Shift register */
+#define ACR (11*RS) /* Auxiliary control register */
+#define PCR (12*RS) /* Peripheral control register */
+#define IFR (13*RS) /* Interrupt flag register */
+#define IER (14*RS) /* Interrupt enable register */
+#define ANH (15*RS) /* A-side data, no handshake */
+
+/* Bits in B data register: all active low */
+#define TREQ 0x08 /* Transfer request (input) */
+#define TACK 0x10 /* Transfer acknowledge (output) */
+#define TIP 0x20 /* Transfer in progress (output) */
+#define ST_MASK 0x30 /* mask for selecting ADB state bits */
+
+/* Bits in ACR */
+#define SR_CTRL 0x1c /* Shift register control bits */
+#define SR_EXT 0x0c /* Shift on external clock */
+#define SR_OUT 0x10 /* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET 0x80 /* set bits in IER */
+#define IER_CLR 0 /* clear bits in IER */
+#define SR_INT 0x04 /* Shift register full/empty */
+#define SR_DATA 0x08 /* Shift register data */
+#define SR_CLOCK 0x10 /* Shift register clock */
+
+/* ADB transaction states according to GMHW */
+#define ST_CMD 0x00 /* ADB state: command byte */
+#define ST_EVEN 0x10 /* ADB state: even data byte */
+#define ST_ODD 0x20 /* ADB state: odd data byte */
+#define ST_IDLE 0x30 /* ADB state: idle, nothing to send */
+
+static int macii_init_via(void);
+static void macii_start(void);
+static void macii_interrupt(int irq, void *arg, struct pt_regs *regs);
+static void macii_retransmit(int);
+static void macii_queue_poll(void);
+
+static int macii_probe(void);
+static int macii_init(void);
+static int macii_send_request(struct adb_request *req, int sync);
+static int macii_write(struct adb_request *req);
+static int macii_autopoll(int devs);
+static void macii_poll(void);
+static int macii_reset_bus(void);
+
+struct adb_driver via_macii_driver = {
+ "Mac II",
+ macii_probe,
+ macii_init,
+ macii_send_request,
+ macii_autopoll,
+ macii_poll,
+ macii_reset_bus
+};
+
+static enum macii_state {
+ idle,
+ sent_first_byte,
+ sending,
+ reading,
+ read_done,
+ awaiting_reply
+} macii_state;
+
+static int need_poll = 0;
+static int command_byte = 0;
+static int last_reply = 0;
+static int last_active = 0;
+
+static struct adb_request *current_req;
+static struct adb_request *last_req;
+static struct adb_request *retry_req;
+static unsigned char reply_buf[16];
+static unsigned char *reply_ptr;
+static int reply_len;
+static int reading_reply;
+static int data_index;
+static int first_byte;
+static int prefix_len;
+static int status = ST_IDLE|TREQ;
+static int last_status;
+static int driver_running = 0;
+
+/* debug level 10 required for ADB logging (should be && debug_adb, ideally) */
+extern int console_loglevel;
+
+/* Check for MacII style ADB */
+static int macii_probe(void)
+{
+ if (macintosh_config->adb_type != MAC_ADB_II) return -ENODEV;
+
+ via = via1;
+
+ printk("adb: Mac II ADB Driver v0.4 for Unified ADB\n");
+ return 0;
+}
+
+/* Initialize the driver */
+int macii_init(void)
+{
+ unsigned long flags;
+ int err;
+
+ save_flags(flags);
+ cli();
+
+ err = macii_init_via();
+ if (err) return err;
+
+ err = request_irq(IRQ_MAC_ADB, macii_interrupt, IRQ_FLG_LOCK, "ADB",
+ macii_interrupt);
+ if (err) return err;
+
+ macii_state = idle;
+ restore_flags(flags);
+ return 0;
+}
+
+/* initialize the hardware */
+static int macii_init_via(void)
+{
+ unsigned char x;
+
+ /* Set the lines up. We want TREQ as input TACK|TIP as output */
+ via[DIRB] = (via[DIRB] | TACK | TIP) & ~TREQ;
+
+ /* Set up state: idle */
+ via[B] |= ST_IDLE;
+ last_status = via[B] & (ST_MASK|TREQ);
+
+ /* Shift register on input */
+ via[ACR] = (via[ACR] & ~SR_CTRL) | SR_EXT;
+
+ /* Wipe any pending data and int */
+ x = via[SR];
+
+ return 0;
+}
+
+/* Send an ADB poll (Talk Register 0 command, tagged on the front of the request queue) */
+static void macii_queue_poll(void)
+{
+ static int device = 0;
+ static int in_poll=0;
+ static struct adb_request req;
+ unsigned long flags;
+
+ if (in_poll) printk("macii_queue_poll: double poll!\n");
+
+ in_poll++;
+ if (++device > 15) device = 1;
+
+ adb_request(&req, NULL, ADBREQ_REPLY|ADBREQ_NOSEND, 1,
+ ADB_READREG(device, 0));
+
+ save_flags(flags);
+ cli();
+
+ req.next = current_req;
+ current_req = &req;
+
+ restore_flags(flags);
+ macii_start();
+ in_poll--;
+}
+
+/* Send an ADB retransmit (Talk, appended to the request queue) */
+static void macii_retransmit(int device)
+{
+ static int in_retransmit = 0;
+ static struct adb_request rt;
+ unsigned long flags;
+
+ if (in_retransmit) printk("macii_retransmit: double retransmit!\n");
+
+ in_retransmit++;
+
+ adb_request(&rt, NULL, ADBREQ_REPLY|ADBREQ_NOSEND, 1,
+ ADB_READREG(device, 0));
+
+ save_flags(flags);
+ cli();
+
+ if (current_req != NULL) {
+ last_req->next = &rt;
+ last_req = &rt;
+ } else {
+ current_req = &rt;
+ last_req = &rt;
+ }
+
+ if (macii_state == idle) macii_start();
+
+ restore_flags(flags);
+ in_retransmit--;
+}
+
+/* Send an ADB request; if sync, poll out the reply 'till it's done */
+static int macii_send_request(struct adb_request *req, int sync)
+{
+ int i;
+
+ i = macii_write(req);
+ if (i) return i;
+
+ if (sync) {
+ while (!req->complete) macii_poll();
+ }
+ return 0;
+}
+
+/* Send an ADB request */
+static int macii_write(struct adb_request *req)
+{
+ unsigned long flags;
+
+ if (req->nbytes < 2 || req->data[0] != ADB_PACKET) {
+ req->complete = 1;
+ return -EINVAL;
+ }
+
+ req->next = 0;
+ req->sent = 0;
+ req->complete = 0;
+ req->reply_len = 0;
+
+ save_flags(flags); cli();
+
+ if (current_req != NULL) {
+ last_req->next = req;
+ last_req = req;
+ } else {
+ current_req = req;
+ last_req = req;
+ if (macii_state == idle) macii_start();
+ }
+
+ restore_flags(flags);
+ return 0;
+}
+
+/* Start auto-polling */
+static int macii_autopoll(int devs)
+{
+ /* Just ping a random default address */
+ if (!(current_req || retry_req))
+ macii_retransmit( (last_active < 16 && last_active > 0) ? last_active : 3);
+ return 0;
+}
+
+/* Prod the chip without interrupts */
+static void macii_poll(void)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ if (via[IFR] & SR_INT) macii_interrupt(0, 0, 0);
+ restore_flags(flags);
+}
+
+/* Reset the bus */
+static int macii_reset_bus(void)
+{
+ static struct adb_request req;
+
+ /* Command = 0, Address = ignored */
+ adb_request(&req, NULL, 0, 1, ADB_BUSRESET);
+
+ return 0;
+}
+
+/* Start sending ADB packet */
+static void macii_start(void)
+{
+ unsigned long flags;
+ struct adb_request *req;
+
+ req = current_req;
+ if (!req) return;
+
+ /* assert macii_state == idle */
+ if (macii_state != idle) {
+ printk("macii_start: called while driver busy (%p %x %x)!\n",
+ req, macii_state, (uint) via1[B] & (ST_MASK|TREQ));
+ return;
+ }
+
+ save_flags(flags); cli();
+
+ /*
+ * IRQ signaled ?? (means ADB controller wants to send, or might
+ * be end of packet if we were reading)
+ */
+ if ((via[B] & TREQ) == 0) {
+ /*
+ * FIXME - we need to restart this on a timer
+ * or a collision at boot hangs us.
+ * Never set macii_state to idle here, or macii_start
+ * won't be called again from send_request!
+ * (need to re-check other cases ...)
+ */
+ /*
+ * if the interrupt handler set the need_poll
+ * flag, it's hopefully a SRQ poll or re-Talk
+ * so we try to send here anyway
+ */
+ if (!need_poll) {
+ if (console_loglevel == 10)
+ printk("macii_start: device busy - retry %p state %d status %x!\n",
+ req, macii_state,
+ (uint) via[B] & (ST_MASK|TREQ));
+ retry_req = req;
+ /* set ADB status here ? */
+ restore_flags(flags);
+ return;
+ } else {
+ need_poll = 0;
+ }
+ }
+ /*
+ * Another retry pending? (sanity check)
+ */
+ if (retry_req) {
+ retry_req = NULL;
+ }
+
+ /* Now send it. Be careful though, that first byte of the request */
+ /* is actually ADB_PACKET; the real data begins at index 1! */
+
+ /* store command byte */
+ command_byte = req->data[1];
+ /* Output mode */
+ via[ACR] |= SR_OUT;
+ /* Load data */
+ via[SR] = req->data[1];
+ /* set ADB state to 'command' */
+ via[B] = (via[B] & ~ST_MASK) | ST_CMD;
+
+ macii_state = sent_first_byte;
+ data_index = 2;
+
+ restore_flags(flags);
+}
+
+/*
+ * The notorious ADB interrupt handler - does all of the protocol handling,
+ * except for starting new send operations. Relies heavily on the ADB
+ * controller sending and receiving data, thereby generating SR interrupts
+ * for us. This means there has to be always activity on the ADB bus, otherwise
+ * the whole process dies and has to be re-kicked by sending TALK requests ...
+ * CUDA-based Macs seem to solve this with the autopoll option, for MacII-type
+ * ADB the problem isn't solved yet (retransmit of the latest active TALK seems
+ * a good choice; either on timeout or on a timer interrupt).
+ *
+ * The basic ADB state machine was left unchanged from the original MacII code
+ * by Alan Cox, which was based on the CUDA driver for PowerMac.
+ * The syntax of the ADB status lines seems to be totally different on MacII,
+ * though. MacII uses the states Command -> Even -> Odd -> Even ->...-> Idle for
+ * sending, and Idle -> Even -> Odd -> Even ->...-> Idle for receiving. Start
+ * and end of a receive packet are signaled by asserting /IRQ on the interrupt
+ * line. Timeouts are signaled by a sequence of 4 0xFF, with /IRQ asserted on
+ * every other byte. SRQ is probably signaled by 3 or more 0xFF tacked on the
+ * end of a packet. (Thanks to Guido Koerber for eavesdropping on the ADB
+ * protocol with a logic analyzer!!)
+ *
+ * Note: As of 21/10/97, the MacII ADB part works including timeout detection
+ * and retransmit (Talk to the last active device).
+ */
+void macii_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+ int x, adbdir;
+ unsigned long flags;
+ struct adb_request *req;
+
+ last_status = status;
+
+ /* prevent races due to SCSI enabling ints */
+ save_flags(flags); cli();
+
+ if (driver_running) {
+ restore_flags(flags);
+ return;
+ }
+
+ driver_running = 1;
+
+ status = via[B] & (ST_MASK|TREQ);
+ adbdir = via[ACR] & SR_OUT;
+
+ switch (macii_state) {
+ case idle:
+ x = via[SR];
+ first_byte = x;
+ /* set ADB state = even for first data byte */
+ via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
+
+ reply_buf[0] = first_byte; /* was command_byte?? */
+ reply_ptr = reply_buf + 1;
+ reply_len = 1;
+ prefix_len = 1;
+ reading_reply = 0;
+
+ macii_state = reading;
+ break;
+
+ case awaiting_reply:
+ /* handshake etc. for II ?? */
+ x = via[SR];
+ first_byte = x;
+ /* set ADB state = even for first data byte */
+ via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
+
+ current_req->reply[0] = first_byte;
+ reply_ptr = current_req->reply + 1;
+ reply_len = 1;
+ prefix_len = 1;
+ reading_reply = 1;
+
+ macii_state = reading;
+ break;
+
+ case sent_first_byte:
+ req = current_req;
+ /* maybe we're already done (Talk, or Poll)? */
+ if (data_index >= req->nbytes) {
+ /* reset to shift in */
+ /* If it's a Listen command and we're done, someone's doing weird stuff. */
+ if (((command_byte & 0x0C) == 0x08)
+ && (console_loglevel == 10))
+ printk("macii_interrupt: listen command with no data: %x!\n",
+ command_byte);
+ /* reset to shift in */
+ via[ACR] &= ~SR_OUT;
+ x = via[SR];
+ /* set ADB state idle - might get SRQ */
+ via[B] = (via[B] & ~ST_MASK) | ST_IDLE;
+
+ req->sent = 1;
+
+ if (req->reply_expected) {
+ macii_state = awaiting_reply;
+ } else {
+ req->complete = 1;
+ current_req = req->next;
+ if (req->done) (*req->done)(req);
+ macii_state = idle;
+ if (current_req || retry_req)
+ macii_start();
+ else
+ macii_retransmit((command_byte & 0xF0) >> 4);
+ }
+ } else {
+ /* SR already set to shift out; send byte */
+ via[SR] = current_req->data[data_index++];
+ /* set state to ST_EVEN (first byte was: ST_CMD) */
+ via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
+ macii_state = sending;
+ }
+ break;
+
+ case sending:
+ req = current_req;
+ if (data_index >= req->nbytes) {
+ /* reset to shift in */
+ via[ACR] &= ~SR_OUT;
+ x = via[SR];
+ /* set ADB state idle - might get SRQ */
+ via[B] = (via[B] & ~ST_MASK) | ST_IDLE;
+
+ req->sent = 1;
+
+ if (req->reply_expected) {
+ macii_state = awaiting_reply;
+ } else {
+ req->complete = 1;
+ current_req = req->next;
+ if (req->done) (*req->done)(req);
+ macii_state = idle;
+ if (current_req || retry_req)
+ macii_start();
+ else
+ macii_retransmit((command_byte & 0xF0) >> 4);
+ }
+ } else {
+ via[SR] = req->data[data_index++];
+
+ /* invert state bits, toggle ODD/EVEN */
+ via[B] ^= ST_MASK;
+ }
+ break;
+
+ case reading:
+
+ /* timeout / SRQ handling for II hw */
+ if( (first_byte == 0xFF && (reply_len-prefix_len)==2
+ && memcmp(reply_ptr-2,"\xFF\xFF",2)==0) ||
+ ((reply_len-prefix_len)==3
+ && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0))
+ {
+ /*
+ * possible timeout (in fact, most probably a
+ * timeout, since SRQ can't be signaled without
+ * transfer on the bus).
+ * The last three bytes seen were FF, together
+ * with the starting byte (in case we started
+ * on 'idle' or 'awaiting_reply') this probably
+ * makes four. So this is mostl likely #5!
+ * The timeout signal is a pattern 1 0 1 0 0..
+ * on /INT, meaning we missed it :-(
+ */
+ x = via[SR];
+ if (x != 0xFF) printk("macii_interrupt: mistaken timeout/SRQ!\n");
+
+ if ((status & TREQ) == (last_status & TREQ)) {
+ /* Not a timeout. Unsolicited SRQ? weird. */
+ /* Terminate the SRQ packet and poll */
+ need_poll = 1;
+ }
+ /* There's no packet to get, so reply is blank */
+ via[B] ^= ST_MASK;
+ reply_ptr -= (reply_len-prefix_len);
+ reply_len = prefix_len;
+ macii_state = read_done;
+ break;
+ } /* end timeout / SRQ handling for II hw. */
+
+ if((reply_len-prefix_len)>3
+ && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0)
+ {
+ /* SRQ tacked on data packet */
+ /* Terminate the packet (SRQ never ends) */
+ x = via[SR];
+ macii_state = read_done;
+ reply_len -= 3;
+ reply_ptr -= 3;
+ need_poll = 1;
+ /* need to continue; next byte not seen else */
+ } else {
+ /* Sanity check */
+ if (reply_len > 15) reply_len = 0;
+ /* read byte */
+ x = via[SR];
+ *reply_ptr = x;
+ reply_ptr++;
+ reply_len++;
+ }
+ /* The usual handshake ... */
+
+ /*
+ * NetBSD hints that the next to last byte
+ * is sent with IRQ !!
+ * Guido found out it's the last one (0x0),
+ * but IRQ should be asserted already.
+ * Problem with timeout detection: First
+ * transition to /IRQ might be second
+ * byte of timeout packet!
+ * Timeouts are signaled by 4x FF.
+ */
+ if (!(status & TREQ) && (x == 0x00)) { /* != 0xFF */
+ /* invert state bits, toggle ODD/EVEN */
+ via[B] ^= ST_MASK;
+
+ /* adjust packet length */
+ reply_len--;
+ reply_ptr--;
+ macii_state = read_done;
+ } else {
+ /* not caught: ST_CMD */
+ /* required for re-entry 'reading'! */
+ if ((status & ST_MASK) == ST_IDLE) {
+ /* (in)sanity check - set even */
+ via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
+ } else {
+ /* invert state bits */
+ via[B] ^= ST_MASK;
+ }
+ }
+ break;
+
+ case read_done:
+ x = via[SR];
+ if (reading_reply) {
+ req = current_req;
+ req->reply_len = reply_ptr - req->reply;
+ req->complete = 1;
+ current_req = req->next;
+ if (req->done) (*req->done)(req);
+ } else {
+ adb_input(reply_buf, reply_ptr - reply_buf,
+ regs, 0);
+ }
+
+ /*
+ * remember this device ID; it's the latest we got a
+ * reply from!
+ */
+ last_reply = command_byte;
+ last_active = (command_byte & 0xF0) >> 4;
+
+ /* SRQ seen before, initiate poll now */
+ if (need_poll) {
+ macii_state = idle;
+ macii_queue_poll();
+ need_poll = 0;
+ break;
+ }
+
+ /* /IRQ seen, so the ADB controller has data for us */
+ if (!(status & TREQ)) {
+ /* set ADB state to idle */
+ via[B] = (via[B] & ~ST_MASK) | ST_IDLE;
+
+ macii_state = reading;
+
+ reply_buf[0] = command_byte;
+ reply_ptr = reply_buf + 1;
+ reply_len = 1;
+ prefix_len = 1;
+ reading_reply = 0;
+ } else {
+ /* no IRQ, send next packet or wait */
+ macii_state = idle;
+ if (current_req)
+ macii_start();
+ else
+ macii_retransmit(last_active);
+ }
+ break;
+
+ default:
+ break;
+ }
+ /* reset mutex and interrupts */
+ driver_running = 0;
+ restore_flags(flags);
+}
--- /dev/null
+/*
+ * Device driver for the IIsi-style ADB on some Mac LC and II-class machines
+ *
+ * Based on via-cuda.c and via-macii.c, as well as the original
+ * adb-bus.c, which in turn is somewhat influenced by (but uses no
+ * code from) the NetBSD HWDIRECT ADB code. Original IIsi driver work
+ * was done by Robert Thompson and integrated into the old style
+ * driver by Michael Schmitz.
+ *
+ * Original sources (c) Alan Cox, Paul Mackerras, and others.
+ *
+ * Rewritten for Unified ADB by David Huggins-Daines <dhd@debian.org> */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/adb.h>
+#include <linux/cuda.h>
+#include <linux/delay.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+#include <asm/mac_via.h>
+
+static volatile unsigned char *via;
+
+/* VIA registers - spaced 0x200 bytes apart - only the ones we actually use */
+#define RS 0x200 /* skip between registers */
+#define B 0 /* B-side data */
+#define A RS /* A-side data */
+#define DIRB (2*RS) /* B-side direction (1=output) */
+#define DIRA (3*RS) /* A-side direction (1=output) */
+#define SR (10*RS) /* Shift register */
+#define ACR (11*RS) /* Auxiliary control register */
+#define IFR (13*RS) /* Interrupt flag register */
+#define IER (14*RS) /* Interrupt enable register */
+
+/* Bits in B data register: all active low */
+#define TREQ 0x08 /* Transfer request (input) */
+#define TACK 0x10 /* Transfer acknowledge (output) */
+#define TIP 0x20 /* Transfer in progress (output) */
+#define ST_MASK 0x30 /* mask for selecting ADB state bits */
+
+/* Bits in ACR */
+#define SR_CTRL 0x1c /* Shift register control bits */
+#define SR_EXT 0x0c /* Shift on external clock */
+#define SR_OUT 0x10 /* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET 0x80 /* set bits in IER */
+#define IER_CLR 0 /* clear bits in IER */
+#define SR_INT 0x04 /* Shift register full/empty */
+#define SR_DATA 0x08 /* Shift register data */
+#define SR_CLOCK 0x10 /* Shift register clock */
+
+#define ADB_DELAY 150
+
+static struct adb_request* current_req = NULL;
+static struct adb_request* last_req = NULL;
+static unsigned char maciisi_rbuf[16];
+static unsigned char *reply_ptr = NULL;
+static int data_index;
+static int reading_reply;
+static int reply_len;
+
+static enum maciisi_state {
+ idle,
+ sending,
+ reading,
+} maciisi_state;
+
+static int maciisi_probe(void);
+static int maciisi_init(void);
+static int maciisi_send_request(struct adb_request* req, int sync);
+static int maciisi_write(struct adb_request* req);
+static void maciisi_interrupt(int irq, void* arg, struct pt_regs* regs);
+static void maciisi_input(unsigned char *buf, int nb, struct pt_regs *regs);
+static int maciisi_init_via(void);
+static void maciisi_poll(void);
+static void maciisi_start(void);
+
+struct adb_driver via_maciisi_driver = {
+ "Mac IIsi",
+ maciisi_probe,
+ maciisi_init,
+ maciisi_send_request,
+ NULL, /* maciisi_adb_autopoll, */
+ maciisi_poll,
+ NULL /* maciisi_reset_adb_bus */
+};
+
+static int
+maciisi_probe(void)
+{
+ if (macintosh_config->adb_type != MAC_ADB_IISI)
+ return -ENODEV;
+
+ via = via1;
+ return 0;
+}
+
+static int
+maciisi_init(void)
+{
+ int err;
+
+ if (via == NULL)
+ return -ENODEV;
+
+ if ((err = maciisi_init_via())) {
+ printk(KERN_ERR "maciisi_init: maciisi_init_via() failed, code %d\n", err);
+ via = NULL;
+ return err;
+ }
+
+ if (request_irq(IRQ_MAC_ADB, maciisi_interrupt, IRQ_FLG_LOCK,
+ "ADB", maciisi_interrupt)) {
+ printk(KERN_ERR "maciisi_init: can't get irq %d\n", IRQ_MAC_ADB);
+ return -EAGAIN;
+ }
+
+ printk("adb: Mac IIsi driver v0.1 for Unified ADB.\n");
+ return 0;
+}
+
+static void
+maciisi_stfu(void)
+{
+ int status = via[B] & (TIP|TREQ);
+
+ if (status & TREQ) {
+ printk (KERN_DEBUG "maciisi_stfu called with TREQ high!\n");
+ return;
+ }
+
+ /* start transfer */
+ via[B] |= TIP;
+ while (!(status & TREQ)) {
+ int poll_timeout = ADB_DELAY * 5;
+ /* Poll for SR interrupt */
+ while (!(via[IFR] & SR_INT) && poll_timeout-- > 0)
+ status = via[B] & (TIP|TREQ);
+ via[SR]; /* Clear shift register */
+ printk(KERN_DEBUG "maciisi_stfu: status %x timeout %d\n",
+ status, poll_timeout);
+
+ /* ACK on-off */
+ via[B] |= TACK;
+ udelay(ADB_DELAY);
+ via[B] &= ~TACK;
+ }
+ /* end frame */
+ via[B] &= ~TIP;
+}
+
+/* All specifically VIA-related initialization goes here */
+static int
+maciisi_init_via(void)
+{
+ /* Set the lines up. We want TREQ as input TACK|TIP as output */
+ via[DIRB] = (via[DIRB] | TACK | TIP) & ~TREQ;
+ /* Shift register on input */
+ via[ACR] = (via[ACR] & ~SR_CTRL) | SR_EXT;
+ printk(KERN_DEBUG "maciisi_init_via: initial status %x\n", via[B] & (TIP|TREQ));
+ /* Set initial state: idle */
+ via[B] &= ~(TACK|TIP);
+ /* Wipe any pending data and int */
+ via[SR];
+ if (!(via[B] & TREQ))
+ maciisi_stfu();
+ via[IER] = IER_SET | SR_INT;
+ maciisi_state = idle;
+ return 0;
+}
+
+/* Send a request, possibly waiting for a reply */
+static int
+maciisi_send_request(struct adb_request* req, int sync)
+{
+ int i;
+ static int dump_packet = 1;
+
+ if (via == NULL) {
+ req->complete = 1;
+ return -ENXIO;
+ }
+
+ if (dump_packet) {
+ printk(KERN_DEBUG "maciisi_send_request:");
+ for (i = 0; i < req->nbytes; i++) {
+ printk(" %.2x", req->data[i]);
+ }
+ printk("\n");
+ }
+ req->reply_expected = 1;
+
+ i = maciisi_write(req);
+ if (i)
+ return i;
+
+ if (sync) {
+ while (!req->complete) {
+ maciisi_poll();
+ }
+ }
+ return 0;
+}
+
+/* Enqueue a request, and run the queue if possible */
+static int
+maciisi_write(struct adb_request* req)
+{
+ unsigned long flags;
+
+ printk(KERN_DEBUG "maciisi_write called, state=%d ifr=%x\n", maciisi_state, via[IFR]);
+ /* We will accept CUDA packets - the VIA sends them to us, so
+ it figures that we should be able to send them to it */
+ if (req->nbytes < 2 || req->data[0] > CUDA_PACKET) {
+ printk(KERN_ERR "maciisi_write: packet too small or not an ADB or CUDA packet\n");
+ req->complete = 1;
+ return -EINVAL;
+ }
+ req->next = 0;
+ req->sent = 0;
+ req->complete = 0;
+ req->reply_len = 0;
+ save_flags(flags); cli();
+
+ if (current_req) {
+ last_req->next = req;
+ last_req = req;
+ } else {
+ current_req = req;
+ last_req = req;
+ }
+ if (maciisi_state == idle)
+ maciisi_start();
+ else
+ printk(KERN_DEBUG "maciisi_write: would start, but state is %d\n", maciisi_state);
+
+ restore_flags(flags);
+ return 0;
+}
+
+static void
+maciisi_start(void)
+{
+ struct adb_request* req;
+ int status;
+
+ printk(KERN_DEBUG "maciisi_start called, state=%d, ifr=%x\n", maciisi_state, via[IFR]);
+ if (maciisi_state != idle) {
+ /* shouldn't happen */
+ printk(KERN_ERR "maciisi_start: maciisi_start called when driver busy!\n");
+ return;
+ }
+
+ req = current_req;
+ if (req == NULL)
+ return;
+
+ status = via[B] & (TIP|TREQ);
+ if (!(status & TREQ)) {
+ /* Bus is busy, set up for reading */
+ printk(KERN_DEBUG "maciisi_start: bus busy - aborting\n");
+ return;
+ }
+
+ /* Okay, send */
+ printk(KERN_DEBUG "maciisi_start: sending\n");
+ /* Set state to active */
+ via[B] |= TIP;
+ /* ACK off */
+ via[B] &= ~TACK;
+ /* Shift out and send */
+ via[ACR] |= SR_OUT;
+ via[SR] = req->data[0];
+ data_index = 1;
+ /* ACK on */
+ via[B] |= TACK;
+ maciisi_state = sending;
+}
+
+void
+maciisi_poll(void)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (via[IFR] & SR_INT) {
+ maciisi_interrupt(0, 0, 0);
+ }
+ restore_flags(flags);
+}
+
+/* Shift register interrupt - this is *supposed* to mean that the
+ register is either full or empty. In practice, I have no idea what
+ it means :( */
+static void
+maciisi_interrupt(int irq, void* arg, struct pt_regs* regs)
+{
+ int status;
+ struct adb_request *req;
+ static int dump_reply = 1;
+
+ if (!(via[IFR] & SR_INT)) {
+ /* Shouldn't happen, we hope */
+ printk(KERN_DEBUG "maciisi_interrupt: called without interrupt flag set\n");
+ return;
+ }
+
+ status = via[B] & (TIP|TREQ);
+ printk(KERN_DEBUG "state %d status %x ifr %x\n", maciisi_state, status, via[IFR]);
+
+ switch_start:
+ switch (maciisi_state) {
+ case idle:
+ printk(KERN_DEBUG "maciisi_interrupt: state=idle, status %x\n", status);
+ if (status & TIP)
+ printk(KERN_DEBUG "maciisi_interrupt: state is idle but TIP asserted!\n");
+
+ udelay(ADB_DELAY);
+ /* Shift in */
+ via[ACR] &= ~SR_OUT;
+ /* Signal start of frame */
+ via[B] |= TIP;
+ /* Clear the interrupt (throw this value on the floor, it's useless) */
+ via[SR];
+ /* ACK adb chip, high-low */
+ via[B] |= TACK;
+ udelay(ADB_DELAY);
+ via[B] &= ~TACK;
+ reply_len = 0;
+ maciisi_state = reading;
+ if (reading_reply) {
+ reply_ptr = current_req->reply;
+ } else {
+ printk(KERN_DEBUG "maciisi_interrupt: received unsolicited packet\n");
+ reply_ptr = maciisi_rbuf;
+ }
+ break;
+
+ case sending:
+ printk(KERN_DEBUG "maciisi_interrupt: state=sending, status=%x\n", status);
+ /* Clear interrupt */
+ via[SR];
+ /* Set ACK off */
+ via[B] &= ~TACK;
+ req = current_req;
+
+ if (!(status & TREQ)) {
+ /* collision */
+ printk(KERN_DEBUG "maciisi_interrupt: send collision\n");
+ /* Set idle and input */
+ via[B] &= ~TIP;
+ via[ACR] &= ~SR_OUT;
+ /* Must re-send */
+ reading_reply = 0;
+ reply_len = 0;
+ maciisi_state = idle;
+ /* process this now, because the IFR has been cleared */
+ goto switch_start;
+ }
+
+ if (data_index >= req->nbytes) {
+ /* Sent the whole packet, put the bus back in idle state */
+ /* Shift in, we are about to read a reply (hopefully) */
+ via[ACR] &= ~SR_OUT;
+ /* End of frame */
+ via[B] &= ~TIP;
+ req->sent = 1;
+ maciisi_state = idle;
+ if (req->reply_expected) {
+ /* Note: only set this once we've
+ successfully sent the packet */
+ reading_reply = 1;
+ } else {
+ current_req = req->next;
+ if (req->done)
+ (*req->done)(req);
+ }
+ } else {
+ /* Sending more stuff */
+ /* Shift out */
+ via[ACR] |= SR_OUT;
+ /* Delay */
+ udelay(ADB_DELAY);
+ /* Write */
+ via[SR] = req->data[data_index++];
+ /* Signal 'byte ready' */
+ via[B] |= TACK;
+ }
+ break;
+
+ case reading:
+ printk(KERN_DEBUG "maciisi_interrupt: state=reading, status=%x\n", status);
+ /* Shift in */
+ via[ACR] &= ~SR_OUT;
+ if (reply_len++ > 16) {
+ printk(KERN_ERR "maciisi_interrupt: reply too long, aborting read\n");
+ via[B] |= TACK;
+ udelay(ADB_DELAY);
+ via[B] &= ~(TACK|TIP);
+ maciisi_state = idle;
+ maciisi_start();
+ break;
+ }
+ *reply_ptr++ = via[SR];
+ status = via[B] & (TIP|TREQ);
+ /* ACK on/off */
+ via[B] |= TACK;
+ udelay(ADB_DELAY);
+ via[B] &= ~TACK;
+ if (!(status & TREQ))
+ break; /* more stuff to deal with */
+
+ /* end of frame */
+ via[B] &= ~TIP;
+
+ /* end of packet, deal with it */
+ if (reading_reply) {
+ req = current_req;
+ req->reply_len = reply_ptr - req->reply;
+ if (req->data[0] == ADB_PACKET) {
+ /* Have to adjust the reply from ADB commands */
+ if (req->reply_len <= 2 || (req->reply[1] & 2) != 0) {
+ /* the 0x2 bit indicates no response */
+ req->reply_len = 0;
+ } else {
+ /* leave just the command and result bytes in the reply */
+ req->reply_len -= 2;
+ memmove(req->reply, req->reply + 2, req->reply_len);
+ }
+ }
+ if (dump_reply) {
+ int i;
+ printk(KERN_DEBUG "maciisi_interrupt: reply is ");
+ for (i = 0; i < req->reply_len; ++i)
+ printk(" %.2x", req->reply[i]);
+ printk("\n");
+ }
+ req->complete = 1;
+ current_req = req->next;
+ if (req->done)
+ (*req->done)(req);
+ /* Obviously, we got it */
+ reading_reply = 0;
+ } else {
+ maciisi_input(maciisi_rbuf, reply_ptr - maciisi_rbuf, regs);
+ }
+ maciisi_state = idle;
+ status = via[B] & (TIP|TREQ);
+ if (!(status & TREQ)) {
+ /* Timeout?! */
+ printk(KERN_DEBUG "extra data after packet: status %x ifr %x\n",
+ status, via[IFR]);
+ maciisi_stfu();
+ }
+ /* Do any queued requests now if possible */
+ maciisi_start();
+ break;
+
+ default:
+ printk("maciisi_interrupt: unknown maciisi_state %d?\n", maciisi_state);
+ }
+}
+
+static void
+maciisi_input(unsigned char *buf, int nb, struct pt_regs *regs)
+{
+ int i;
+
+ switch (buf[0]) {
+ case ADB_PACKET:
+ adb_input(buf+2, nb-2, regs, buf[1] & 0x40);
+ break;
+ default:
+ printk(KERN_DEBUG "data from IIsi ADB (%d bytes):", nb);
+ for (i = 0; i < nb; ++i)
+ printk(" %.2x", buf[i]);
+ printk("\n");
+
+ }
+}
--- /dev/null
+/*
+ * Device driver for the PMU on 68K-based Apple PowerBooks
+ *
+ * The VIA (versatile interface adapter) interfaces to the PMU,
+ * a 6805 microprocessor core whose primary function is to control
+ * battery charging and system power on the PowerBooks.
+ * The PMU also controls the ADB (Apple Desktop Bus) which connects
+ * to the keyboard and mouse, as well as the non-volatile RAM
+ * and the RTC (real time clock) chip.
+ *
+ * Adapted for 68K PMU by Joshua M. Thompson
+ *
+ * Based largely on the PowerMac PMU code by Paul Mackerras and
+ * Fabio Riccardi.
+ *
+ * Also based on the PMU driver from MkLinux by Apple Computer, Inc.
+ * and the Open Software Foundation, Inc.
+ */
+
+#include <stdarg.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/blkdev.h>
+#include <linux/pci.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <linux/cuda.h>
+
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+#include <asm/mac_via.h>
+
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* Misc minor number allocated for /dev/pmu */
+#define PMU_MINOR 154
+
+/* VIA registers - spaced 0x200 bytes apart */
+#define RS 0x200 /* skip between registers */
+#define B 0 /* B-side data */
+#define A RS /* A-side data */
+#define DIRB (2*RS) /* B-side direction (1=output) */
+#define DIRA (3*RS) /* A-side direction (1=output) */
+#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */
+#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */
+#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */
+#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */
+#define T2CL (8*RS) /* Timer 2 ctr/latch (low 8 bits) */
+#define T2CH (9*RS) /* Timer 2 counter (high 8 bits) */
+#define SR (10*RS) /* Shift register */
+#define ACR (11*RS) /* Auxiliary control register */
+#define PCR (12*RS) /* Peripheral control register */
+#define IFR (13*RS) /* Interrupt flag register */
+#define IER (14*RS) /* Interrupt enable register */
+#define ANH (15*RS) /* A-side data, no handshake */
+
+/* Bits in B data register: both active low */
+#define TACK 0x02 /* Transfer acknowledge (input) */
+#define TREQ 0x04 /* Transfer request (output) */
+
+/* Bits in ACR */
+#define SR_CTRL 0x1c /* Shift register control bits */
+#define SR_EXT 0x0c /* Shift on external clock */
+#define SR_OUT 0x10 /* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define SR_INT 0x04 /* Shift register full/empty */
+#define CB1_INT 0x10 /* transition on CB1 input */
+
+static enum pmu_state {
+ idle,
+ sending,
+ intack,
+ reading,
+ reading_intr,
+} pmu_state;
+
+static struct adb_request *current_req;
+static struct adb_request *last_req;
+static struct adb_request *req_awaiting_reply;
+static unsigned char interrupt_data[32];
+static unsigned char *reply_ptr;
+static int data_index;
+static int data_len;
+static int adb_int_pending;
+static int pmu_adb_flags;
+static int adb_dev_map = 0;
+static struct adb_request bright_req_1, bright_req_2, bright_req_3;
+static int pmu_kind = PMU_UNKNOWN;
+static int pmu_fully_inited = 0;
+
+int asleep;
+struct notifier_block *sleep_notifier_list;
+
+static int pmu_probe(void);
+static int pmu_init(void);
+static void pmu_start(void);
+static void pmu_interrupt(int irq, void *arg, struct pt_regs *regs);
+static int pmu_send_request(struct adb_request *req, int sync);
+static int pmu_autopoll(int devs);
+void pmu_poll(void);
+static int pmu_reset_bus(void);
+static int pmu_queue_request(struct adb_request *req);
+
+static void pmu_start(void);
+static void send_byte(int x);
+static void recv_byte(void);
+static void pmu_done(struct adb_request *req);
+static void pmu_handle_data(unsigned char *data, int len,
+ struct pt_regs *regs);
+static void set_volume(int level);
+
+struct adb_driver via_pmu_driver = {
+ "68K PMU",
+ pmu_probe,
+ pmu_init,
+ pmu_send_request,
+ pmu_autopoll,
+ pmu_poll,
+ pmu_reset_bus
+};
+
+/*
+ * This table indicates for each PMU opcode:
+ * - the number of data bytes to be sent with the command, or -1
+ * if a length byte should be sent,
+ * - the number of response bytes which the PMU will return, or
+ * -1 if it will send a length byte.
+ */
+static s8 pmu_data_len[256][2] = {
+/* 0 1 2 3 4 5 6 7 */
+/*00*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*08*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*10*/ { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*18*/ { 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0, 0},
+/*20*/ {-1, 0},{ 0, 0},{ 2, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*28*/ { 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0,-1},
+/*30*/ { 4, 0},{20, 0},{-1, 0},{ 3, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*38*/ { 0, 4},{ 0,20},{ 2,-1},{ 2, 1},{ 3,-1},{-1,-1},{-1,-1},{ 4, 0},
+/*40*/ { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*48*/ { 0, 1},{ 0, 1},{-1,-1},{ 1, 0},{ 1, 0},{-1,-1},{-1,-1},{-1,-1},
+/*50*/ { 1, 0},{ 0, 0},{ 2, 0},{ 2, 0},{-1, 0},{ 1, 0},{ 3, 0},{ 1, 0},
+/*58*/ { 0, 1},{ 1, 0},{ 0, 2},{ 0, 2},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},
+/*60*/ { 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*68*/ { 0, 3},{ 0, 3},{ 0, 2},{ 0, 8},{ 0,-1},{ 0,-1},{-1,-1},{-1,-1},
+/*70*/ { 1, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*78*/ { 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{ 5, 1},{ 4, 1},{ 4, 1},
+/*80*/ { 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*88*/ { 0, 5},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*90*/ { 1, 0},{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*98*/ { 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*a0*/ { 2, 0},{ 2, 0},{ 2, 0},{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},
+/*a8*/ { 1, 1},{ 1, 0},{ 3, 0},{ 2, 0},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*b0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*b8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*c0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*c8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*d0*/ { 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*d8*/ { 1, 1},{ 1, 1},{-1,-1},{-1,-1},{ 0, 1},{ 0,-1},{-1,-1},{-1,-1},
+/*e0*/ {-1, 0},{ 4, 0},{ 0, 1},{-1, 0},{-1, 0},{ 4, 0},{-1, 0},{-1, 0},
+/*e8*/ { 3,-1},{-1,-1},{ 0, 1},{-1,-1},{ 0,-1},{-1,-1},{-1,-1},{ 0, 0},
+/*f0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*f8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+};
+
+int pmu_probe()
+{
+ if (macintosh_config->adb_type == MAC_ADB_PB1) {
+ pmu_kind = PMU_68K_V1;
+ } else if (macintosh_config->adb_type == MAC_ADB_PB2) {
+ pmu_kind = PMU_68K_V2;
+ } else {
+ return -ENODEV;
+ }
+
+ pmu_state = idle;
+
+ return 0;
+}
+
+static int
+pmu_init(void)
+{
+ int timeout;
+ volatile struct adb_request req;
+
+ via2[B] |= TREQ; /* negate TREQ */
+ via2[DIRB] = (via2[DIRB] | TREQ) & ~TACK; /* TACK in, TREQ out */
+
+ pmu_request((struct adb_request *) &req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB);
+ timeout = 100000;
+ while (!req.complete) {
+ if (--timeout < 0) {
+ printk(KERN_ERR "pmu_init: no response from PMU\n");
+ return -EAGAIN;
+ }
+ udelay(10);
+ pmu_poll();
+ }
+
+ /* ack all pending interrupts */
+ timeout = 100000;
+ interrupt_data[0] = 1;
+ while (interrupt_data[0] || pmu_state != idle) {
+ if (--timeout < 0) {
+ printk(KERN_ERR "pmu_init: timed out acking intrs\n");
+ return -EAGAIN;
+ }
+ if (pmu_state == idle) {
+ adb_int_pending = 1;
+ pmu_interrupt(0, NULL, NULL);
+ }
+ pmu_poll();
+ udelay(10);
+ }
+
+ pmu_request((struct adb_request *) &req, NULL, 2, PMU_SET_INTR_MASK,
+ PMU_INT_ADB_AUTO|PMU_INT_SNDBRT|PMU_INT_ADB);
+ timeout = 100000;
+ while (!req.complete) {
+ if (--timeout < 0) {
+ printk(KERN_ERR "pmu_init: no response from PMU\n");
+ return -EAGAIN;
+ }
+ udelay(10);
+ pmu_poll();
+ }
+
+ bright_req_1.complete = 1;
+ bright_req_2.complete = 1;
+ bright_req_3.complete = 1;
+
+ if (request_irq(IRQ_MAC_ADB_SR, pmu_interrupt, 0, "pmu-shift",
+ pmu_interrupt)) {
+ printk(KERN_ERR "pmu_init: can't get irq %d\n",
+ IRQ_MAC_ADB_SR);
+ return -EAGAIN;
+ }
+ if (request_irq(IRQ_MAC_ADB_CL, pmu_interrupt, 0, "pmu-clock",
+ pmu_interrupt)) {
+ printk(KERN_ERR "pmu_init: can't get irq %d\n",
+ IRQ_MAC_ADB_CL);
+ free_irq(IRQ_MAC_ADB_SR, pmu_interrupt);
+ return -EAGAIN;
+ }
+
+ pmu_fully_inited = 1;
+
+ /* Enable backlight */
+ pmu_enable_backlight(1);
+
+ printk("adb: PMU 68K driver v0.5 for Unified ADB.\n");
+
+ return 0;
+}
+
+int
+pmu_get_model(void)
+{
+ return pmu_kind;
+}
+
+/* Send an ADB command */
+static int
+pmu_send_request(struct adb_request *req, int sync)
+{
+ int i, ret;
+
+ if (!pmu_fully_inited)
+ {
+ req->complete = 1;
+ return -ENXIO;
+ }
+
+ ret = -EINVAL;
+
+ switch (req->data[0]) {
+ case PMU_PACKET:
+ for (i = 0; i < req->nbytes - 1; ++i)
+ req->data[i] = req->data[i+1];
+ --req->nbytes;
+ if (pmu_data_len[req->data[0]][1] != 0) {
+ req->reply[0] = ADB_RET_OK;
+ req->reply_len = 1;
+ } else
+ req->reply_len = 0;
+ ret = pmu_queue_request(req);
+ break;
+ case CUDA_PACKET:
+ switch (req->data[1]) {
+ case CUDA_GET_TIME:
+ if (req->nbytes != 2)
+ break;
+ req->data[0] = PMU_READ_RTC;
+ req->nbytes = 1;
+ req->reply_len = 3;
+ req->reply[0] = CUDA_PACKET;
+ req->reply[1] = 0;
+ req->reply[2] = CUDA_GET_TIME;
+ ret = pmu_queue_request(req);
+ break;
+ case CUDA_SET_TIME:
+ if (req->nbytes != 6)
+ break;
+ req->data[0] = PMU_SET_RTC;
+ req->nbytes = 5;
+ for (i = 1; i <= 4; ++i)
+ req->data[i] = req->data[i+1];
+ req->reply_len = 3;
+ req->reply[0] = CUDA_PACKET;
+ req->reply[1] = 0;
+ req->reply[2] = CUDA_SET_TIME;
+ ret = pmu_queue_request(req);
+ break;
+ case CUDA_GET_PRAM:
+ if (req->nbytes != 4)
+ break;
+ req->data[0] = PMU_READ_NVRAM;
+ req->data[1] = req->data[2];
+ req->data[2] = req->data[3];
+ req->nbytes = 3;
+ req->reply_len = 3;
+ req->reply[0] = CUDA_PACKET;
+ req->reply[1] = 0;
+ req->reply[2] = CUDA_GET_PRAM;
+ ret = pmu_queue_request(req);
+ break;
+ case CUDA_SET_PRAM:
+ if (req->nbytes != 5)
+ break;
+ req->data[0] = PMU_WRITE_NVRAM;
+ req->data[1] = req->data[2];
+ req->data[2] = req->data[3];
+ req->data[3] = req->data[4];
+ req->nbytes = 4;
+ req->reply_len = 3;
+ req->reply[0] = CUDA_PACKET;
+ req->reply[1] = 0;
+ req->reply[2] = CUDA_SET_PRAM;
+ ret = pmu_queue_request(req);
+ break;
+ }
+ break;
+ case ADB_PACKET:
+ for (i = req->nbytes - 1; i > 1; --i)
+ req->data[i+2] = req->data[i];
+ req->data[3] = req->nbytes - 2;
+ req->data[2] = pmu_adb_flags;
+ /*req->data[1] = req->data[1];*/
+ req->data[0] = PMU_ADB_CMD;
+ req->nbytes += 2;
+ req->reply_expected = 1;
+ req->reply_len = 0;
+ ret = pmu_queue_request(req);
+ break;
+ }
+ if (ret)
+ {
+ req->complete = 1;
+ return ret;
+ }
+
+ if (sync) {
+ while (!req->complete)
+ pmu_poll();
+ }
+
+ return 0;
+}
+
+/* Enable/disable autopolling */
+static int
+pmu_autopoll(int devs)
+{
+ struct adb_request req;
+
+ if (!pmu_fully_inited) return -ENXIO;
+
+ if (devs) {
+ adb_dev_map = devs;
+ pmu_request(&req, NULL, 5, PMU_ADB_CMD, 0, 0x86,
+ adb_dev_map >> 8, adb_dev_map);
+ pmu_adb_flags = 2;
+ } else {
+ pmu_request(&req, NULL, 1, PMU_ADB_POLL_OFF);
+ pmu_adb_flags = 0;
+ }
+ while (!req.complete)
+ pmu_poll();
+ return 0;
+}
+
+/* Reset the ADB bus */
+static int
+pmu_reset_bus(void)
+{
+ struct adb_request req;
+ long timeout;
+ int save_autopoll = adb_dev_map;
+
+ if (!pmu_fully_inited) return -ENXIO;
+
+ /* anyone got a better idea?? */
+ pmu_autopoll(0);
+
+ req.nbytes = 5;
+ req.done = NULL;
+ req.data[0] = PMU_ADB_CMD;
+ req.data[1] = 0;
+ req.data[2] = 3; /* ADB_BUSRESET ??? */
+ req.data[3] = 0;
+ req.data[4] = 0;
+ req.reply_len = 0;
+ req.reply_expected = 1;
+ if (pmu_queue_request(&req) != 0)
+ {
+ printk(KERN_ERR "pmu_adb_reset_bus: pmu_queue_request failed\n");
+ return -EIO;
+ }
+ while (!req.complete)
+ pmu_poll();
+ timeout = 100000;
+ while (!req.complete) {
+ if (--timeout < 0) {
+ printk(KERN_ERR "pmu_adb_reset_bus (reset): no response from PMU\n");
+ return -EIO;
+ }
+ udelay(10);
+ pmu_poll();
+ }
+
+ if (save_autopoll != 0)
+ pmu_autopoll(save_autopoll);
+
+ return 0;
+}
+
+/* Construct and send a pmu request */
+int
+pmu_request(struct adb_request *req, void (*done)(struct adb_request *),
+ int nbytes, ...)
+{
+ va_list list;
+ int i;
+
+ if (nbytes < 0 || nbytes > 32) {
+ printk(KERN_ERR "pmu_request: bad nbytes (%d)\n", nbytes);
+ req->complete = 1;
+ return -EINVAL;
+ }
+ req->nbytes = nbytes;
+ req->done = done;
+ va_start(list, nbytes);
+ for (i = 0; i < nbytes; ++i)
+ req->data[i] = va_arg(list, int);
+ va_end(list);
+ if (pmu_data_len[req->data[0]][1] != 0) {
+ req->reply[0] = ADB_RET_OK;
+ req->reply_len = 1;
+ } else
+ req->reply_len = 0;
+ req->reply_expected = 0;
+ return pmu_queue_request(req);
+}
+
+static int
+pmu_queue_request(struct adb_request *req)
+{
+ unsigned long flags;
+ int nsend;
+
+ if (req->nbytes <= 0) {
+ req->complete = 1;
+ return 0;
+ }
+ nsend = pmu_data_len[req->data[0]][0];
+ if (nsend >= 0 && req->nbytes != nsend + 1) {
+ req->complete = 1;
+ return -EINVAL;
+ }
+
+ req->next = 0;
+ req->sent = 0;
+ req->complete = 0;
+ save_flags(flags); cli();
+
+ if (current_req != 0) {
+ last_req->next = req;
+ last_req = req;
+ } else {
+ current_req = req;
+ last_req = req;
+ if (pmu_state == idle)
+ pmu_start();
+ }
+
+ restore_flags(flags);
+ return 0;
+}
+
+static void
+send_byte(int x)
+{
+ via1[ACR] |= SR_CTRL;
+ via1[SR] = x;
+ via2[B] &= ~TREQ; /* assert TREQ */
+}
+
+static void
+recv_byte()
+{
+ char c;
+
+ via1[ACR] = (via1[ACR] | SR_EXT) & ~SR_OUT;
+ c = via1[SR]; /* resets SR */
+ via2[B] &= ~TREQ;
+}
+
+static void
+pmu_start()
+{
+ unsigned long flags;
+ struct adb_request *req;
+
+ /* assert pmu_state == idle */
+ /* get the packet to send */
+ save_flags(flags); cli();
+ req = current_req;
+ if (req == 0 || pmu_state != idle
+ || (req->reply_expected && req_awaiting_reply))
+ goto out;
+
+ pmu_state = sending;
+ data_index = 1;
+ data_len = pmu_data_len[req->data[0]][0];
+
+ /* set the shift register to shift out and send a byte */
+ send_byte(req->data[0]);
+
+out:
+ restore_flags(flags);
+}
+
+void
+pmu_poll()
+{
+ unsigned long cpu_flags;
+
+ save_flags(cpu_flags);
+ cli();
+ if (via1[IFR] & SR_INT) {
+ via1[IFR] = SR_INT;
+ pmu_interrupt(IRQ_MAC_ADB_SR, NULL, NULL);
+ }
+ if (via1[IFR] & CB1_INT) {
+ via1[IFR] = CB1_INT;
+ pmu_interrupt(IRQ_MAC_ADB_CL, NULL, NULL);
+ }
+ restore_flags(cpu_flags);
+}
+
+static void
+pmu_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct adb_request *req;
+ int timeout, bite = 0; /* to prevent compiler warning */
+
+#if 0
+ printk("pmu_interrupt: irq %d state %d acr %02X, b %02X data_index %d/%d adb_int_pending %d\n",
+ irq, pmu_state, (uint) via1[ACR], (uint) via2[B], data_index, data_len, adb_int_pending);
+#endif
+
+ if (irq == IRQ_MAC_ADB_CL) { /* CB1 interrupt */
+ adb_int_pending = 1;
+ } else if (irq == IRQ_MAC_ADB_SR) { /* SR interrupt */
+ if (via2[B] & TACK) {
+ printk(KERN_DEBUG "PMU: SR_INT but ack still high! (%x)\n", via2[B]);
+ }
+
+ /* if reading grab the byte */
+ if ((via1[ACR] & SR_OUT) == 0) bite = via1[SR];
+
+ /* reset TREQ and wait for TACK to go high */
+ via2[B] |= TREQ;
+ timeout = 3200;
+ while (!(via2[B] & TACK)) {
+ if (--timeout < 0) {
+ printk(KERN_ERR "PMU not responding (!ack)\n");
+ goto finish;
+ }
+ udelay(10);
+ }
+
+ switch (pmu_state) {
+ case sending:
+ req = current_req;
+ if (data_len < 0) {
+ data_len = req->nbytes - 1;
+ send_byte(data_len);
+ break;
+ }
+ if (data_index <= data_len) {
+ send_byte(req->data[data_index++]);
+ break;
+ }
+ req->sent = 1;
+ data_len = pmu_data_len[req->data[0]][1];
+ if (data_len == 0) {
+ pmu_state = idle;
+ current_req = req->next;
+ if (req->reply_expected)
+ req_awaiting_reply = req;
+ else
+ pmu_done(req);
+ } else {
+ pmu_state = reading;
+ data_index = 0;
+ reply_ptr = req->reply + req->reply_len;
+ recv_byte();
+ }
+ break;
+
+ case intack:
+ data_index = 0;
+ data_len = -1;
+ pmu_state = reading_intr;
+ reply_ptr = interrupt_data;
+ recv_byte();
+ break;
+
+ case reading:
+ case reading_intr:
+ if (data_len == -1) {
+ data_len = bite;
+ if (bite > 32)
+ printk(KERN_ERR "PMU: bad reply len %d\n",
+ bite);
+ } else {
+ reply_ptr[data_index++] = bite;
+ }
+ if (data_index < data_len) {
+ recv_byte();
+ break;
+ }
+
+ if (pmu_state == reading_intr) {
+ pmu_handle_data(interrupt_data, data_index, regs);
+ } else {
+ req = current_req;
+ current_req = req->next;
+ req->reply_len += data_index;
+ pmu_done(req);
+ }
+ pmu_state = idle;
+
+ break;
+
+ default:
+ printk(KERN_ERR "pmu_interrupt: unknown state %d?\n",
+ pmu_state);
+ }
+ }
+finish:
+ if (pmu_state == idle) {
+ if (adb_int_pending) {
+ pmu_state = intack;
+ send_byte(PMU_INT_ACK);
+ adb_int_pending = 0;
+ } else if (current_req) {
+ pmu_start();
+ }
+ }
+
+#if 0
+ printk("pmu_interrupt: exit state %d acr %02X, b %02X data_index %d/%d adb_int_pending %d\n",
+ pmu_state, (uint) via1[ACR], (uint) via2[B], data_index, data_len, adb_int_pending);
+#endif
+}
+
+static void
+pmu_done(struct adb_request *req)
+{
+ req->complete = 1;
+ if (req->done)
+ (*req->done)(req);
+}
+
+/* Interrupt data could be the result data from an ADB cmd */
+static void
+pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs)
+{
+ static int show_pmu_ints = 1;
+
+ asleep = 0;
+ if (len < 1) {
+ adb_int_pending = 0;
+ return;
+ }
+ if (data[0] & PMU_INT_ADB) {
+ if ((data[0] & PMU_INT_ADB_AUTO) == 0) {
+ struct adb_request *req = req_awaiting_reply;
+ if (req == 0) {
+ printk(KERN_ERR "PMU: extra ADB reply\n");
+ return;
+ }
+ req_awaiting_reply = 0;
+ if (len <= 2)
+ req->reply_len = 0;
+ else {
+ memcpy(req->reply, data + 1, len - 1);
+ req->reply_len = len - 1;
+ }
+ pmu_done(req);
+ } else {
+ adb_input(data+1, len-1, regs, 1);
+ }
+ } else {
+ if (data[0] == 0x08 && len == 3) {
+ /* sound/brightness buttons pressed */
+ pmu_set_brightness(data[1] >> 3);
+ set_volume(data[2]);
+ } else if (show_pmu_ints
+ && !(data[0] == PMU_INT_TICK && len == 1)) {
+ int i;
+ printk(KERN_DEBUG "pmu intr");
+ for (i = 0; i < len; ++i)
+ printk(" %.2x", data[i]);
+ printk("\n");
+ }
+ }
+}
+
+int backlight_level = -1;
+int backlight_enabled = 0;
+
+#define LEVEL_TO_BRIGHT(lev) ((lev) < 1? 0x7f: 0x4a - ((lev) << 1))
+
+void
+pmu_enable_backlight(int on)
+{
+ struct adb_request req;
+
+ if (on) {
+ /* first call: get current backlight value */
+ if (backlight_level < 0) {
+ switch(pmu_kind) {
+ case PMU_68K_V1:
+ case PMU_68K_V2:
+ pmu_request(&req, NULL, 3, PMU_READ_NVRAM, 0x14, 0xe);
+ while (!req.complete)
+ pmu_poll();
+ printk(KERN_DEBUG "pmu: nvram returned bright: %d\n", (int)req.reply[1]);
+ backlight_level = req.reply[1];
+ break;
+ default:
+ backlight_enabled = 0;
+ return;
+ }
+ }
+ pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT,
+ LEVEL_TO_BRIGHT(backlight_level));
+ while (!req.complete)
+ pmu_poll();
+ }
+ pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
+ PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF));
+ while (!req.complete)
+ pmu_poll();
+ backlight_enabled = on;
+}
+
+void
+pmu_set_brightness(int level)
+{
+ int bright;
+
+ backlight_level = level;
+ bright = LEVEL_TO_BRIGHT(level);
+ if (!backlight_enabled)
+ return;
+ if (bright_req_1.complete)
+ pmu_request(&bright_req_1, NULL, 2, PMU_BACKLIGHT_BRIGHT,
+ bright);
+ if (bright_req_2.complete)
+ pmu_request(&bright_req_2, NULL, 2, PMU_POWER_CTRL,
+ PMU_POW_BACKLIGHT | (bright < 0x7f ? PMU_POW_ON : PMU_POW_OFF));
+}
+
+void
+pmu_enable_irled(int on)
+{
+ struct adb_request req;
+
+ pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_IRLED |
+ (on ? PMU_POW_ON : PMU_POW_OFF));
+ while (!req.complete)
+ pmu_poll();
+}
+
+static void
+set_volume(int level)
+{
+}
+
+int
+pmu_present(void)
+{
+ return (pmu_kind != PMU_UNKNOWN);
+}
+
+#if 0 /* needs some work for 68K */
+
+/*
+ * This struct is used to store config register values for
+ * PCI devices which may get powered off when we sleep.
+ */
+static struct pci_save {
+ u16 command;
+ u16 cache_lat;
+ u16 intr;
+} *pbook_pci_saves;
+static int n_pbook_pci_saves;
+
+static inline void __openfirmware
+pbook_pci_save(void)
+{
+ int npci;
+ struct pci_dev *pd;
+ struct pci_save *ps;
+
+ npci = 0;
+ for (pd = pci_devices; pd != NULL; pd = pd->next)
+ ++npci;
+ n_pbook_pci_saves = npci;
+ if (npci == 0)
+ return;
+ ps = (struct pci_save *) kmalloc(npci * sizeof(*ps), GFP_KERNEL);
+ pbook_pci_saves = ps;
+ if (ps == NULL)
+ return;
+
+ for (pd = pci_devices; pd != NULL && npci != 0; pd = pd->next) {
+ pci_read_config_word(pd, PCI_COMMAND, &ps->command);
+ pci_read_config_word(pd, PCI_CACHE_LINE_SIZE, &ps->cache_lat);
+ pci_read_config_word(pd, PCI_INTERRUPT_LINE, &ps->intr);
+ ++ps;
+ --npci;
+ }
+}
+
+static inline void __openfirmware
+pbook_pci_restore(void)
+{
+ u16 cmd;
+ struct pci_save *ps = pbook_pci_saves;
+ struct pci_dev *pd;
+ int j;
+
+ for (pd = pci_devices; pd != NULL; pd = pd->next, ++ps) {
+ if (ps->command == 0)
+ continue;
+ pci_read_config_word(pd, PCI_COMMAND, &cmd);
+ if ((ps->command & ~cmd) == 0)
+ continue;
+ switch (pd->hdr_type) {
+ case PCI_HEADER_TYPE_NORMAL:
+ for (j = 0; j < 6; ++j)
+ pci_write_config_dword(pd,
+ PCI_BASE_ADDRESS_0 + j*4,
+ pd->resource[j].start);
+ pci_write_config_dword(pd, PCI_ROM_ADDRESS,
+ pd->resource[PCI_ROM_RESOURCE].start);
+ pci_write_config_word(pd, PCI_CACHE_LINE_SIZE,
+ ps->cache_lat);
+ pci_write_config_word(pd, PCI_INTERRUPT_LINE,
+ ps->intr);
+ pci_write_config_word(pd, PCI_COMMAND, ps->command);
+ break;
+ /* other header types not restored at present */
+ }
+ }
+}
+
+/*
+ * Put the powerbook to sleep.
+ */
+#define IRQ_ENABLE ((unsigned int *)0xf3000024)
+#define MEM_CTRL ((unsigned int *)0xf8000070)
+
+int __openfirmware powerbook_sleep(void)
+{
+ int ret, i, x;
+ static int save_backlight;
+ static unsigned int save_irqen;
+ unsigned long msr;
+ unsigned int hid0;
+ unsigned long p, wait;
+ struct adb_request sleep_req;
+
+ /* Notify device drivers */
+ ret = notifier_call_chain(&sleep_notifier_list, PBOOK_SLEEP, NULL);
+ if (ret & NOTIFY_STOP_MASK)
+ return -EBUSY;
+
+ /* Sync the disks. */
+ /* XXX It would be nice to have some way to ensure that
+ * nobody is dirtying any new buffers while we wait. */
+ fsync_dev(0);
+
+ /* Turn off the display backlight */
+ save_backlight = backlight_enabled;
+ if (save_backlight)
+ pmu_enable_backlight(0);
+
+ /* Give the disks a little time to actually finish writing */
+ for (wait = jiffies + (HZ/4); time_before(jiffies, wait); )
+ mb();
+
+ /* Disable all interrupts except pmu */
+ save_irqen = in_le32(IRQ_ENABLE);
+ for (i = 0; i < 32; ++i)
+ if (i != vias->intrs[0].line && (save_irqen & (1 << i)))
+ disable_irq(i);
+ asm volatile("mtdec %0" : : "r" (0x7fffffff));
+
+ /* Save the state of PCI config space for some slots */
+ pbook_pci_save();
+
+ /* Set the memory controller to keep the memory refreshed
+ while we're asleep */
+ for (i = 0x403f; i >= 0x4000; --i) {
+ out_be32(MEM_CTRL, i);
+ do {
+ x = (in_be32(MEM_CTRL) >> 16) & 0x3ff;
+ } while (x == 0);
+ if (x >= 0x100)
+ break;
+ }
+
+ /* Ask the PMU to put us to sleep */
+ pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+ while (!sleep_req.complete)
+ mb();
+ /* displacement-flush the L2 cache - necessary? */
+ for (p = KERNELBASE; p < KERNELBASE + 0x100000; p += 0x1000)
+ i = *(volatile int *)p;
+ asleep = 1;
+
+ /* Put the CPU into sleep mode */
+ asm volatile("mfspr %0,1008" : "=r" (hid0) :);
+ hid0 = (hid0 & ~(HID0_NAP | HID0_DOZE)) | HID0_SLEEP;
+ asm volatile("mtspr 1008,%0" : : "r" (hid0));
+ save_flags(msr);
+ msr |= MSR_POW | MSR_EE;
+ restore_flags(msr);
+ udelay(10);
+
+ /* OK, we're awake again, start restoring things */
+ out_be32(MEM_CTRL, 0x3f);
+ pbook_pci_restore();
+
+ /* wait for the PMU interrupt sequence to complete */
+ while (asleep)
+ mb();
+
+ /* reenable interrupts */
+ for (i = 0; i < 32; ++i)
+ if (i != vias->intrs[0].line && (save_irqen & (1 << i)))
+ enable_irq(i);
+
+ /* Notify drivers */
+ notifier_call_chain(&sleep_notifier_list, PBOOK_WAKE, NULL);
+
+ /* reenable ADB autopoll */
+ pmu_adb_autopoll(adb_dev_map);
+
+ /* Turn on the screen backlight, if it was on before */
+ if (save_backlight)
+ pmu_enable_backlight(1);
+
+ /* Wait for the hard disk to spin up */
+
+ return 0;
+}
+
+/*
+ * Support for /dev/pmu device
+ */
+static int __openfirmware pmu_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static ssize_t __openfirmware pmu_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ return 0;
+}
+
+static ssize_t __openfirmware pmu_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ return 0;
+}
+
+/* Note: removed __openfirmware here since it causes link errors */
+static int /*__openfirmware*/ pmu_ioctl(struct inode * inode, struct file *filp,
+ u_int cmd, u_long arg)
+{
+ int error;
+ __u32 value;
+
+ switch (cmd) {
+ case PMU_IOC_SLEEP:
+ return -ENOSYS;
+ case PMU_IOC_GET_BACKLIGHT:
+ return put_user(backlight_level, (__u32 *)arg);
+ case PMU_IOC_SET_BACKLIGHT:
+ error = get_user(value, (__u32 *)arg);
+ if (!error)
+ pmu_set_brightness(value);
+ return error;
+ case PMU_IOC_GET_MODEL:
+ return put_user(pmu_kind, (__u32 *)arg);
+ }
+ return -EINVAL;
+}
+
+static struct file_operations pmu_device_fops = {
+ NULL, /* no seek */
+ pmu_read,
+ pmu_write,
+ NULL, /* no readdir */
+ NULL, /* no poll yet */
+ pmu_ioctl,
+ NULL, /* no mmap */
+ pmu_open,
+ NULL, /* flush */
+ NULL /* no release */
+};
+
+static struct miscdevice pmu_device = {
+ PMU_MINOR, "pmu", &pmu_device_fops
+};
+
+void pmu_device_init(void)
+{
+ if (via)
+ misc_register(&pmu_device);
+}
+#endif /* CONFIG_PMAC_PBOOK */
+
continue;
}
strcpy(z->name, "Ariadne Ethernet Card and Parallel Ports");
+
+ dev = init_etherdev(NULL, sizeof(struct ariadne_private));
+
+ if (dev == NULL) {
+ release_mem_region(base_addr, sizeof(struct Am79C960));
+ release_mem_region(ram_start, ARIADNE_RAM_SIZE);
+ return -ENOMEM;
+ }
+ memset(dev->priv, 0, sizeof(struct ariadne_private));
+
dev->dev_addr[0] = 0x00;
dev->dev_addr[1] = 0x60;
dev->dev_addr[2] = 0x30;
dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
- init_etherdev(dev, 0);
-
- dev->priv = kmalloc(sizeof(struct ariadne_private), GFP_KERNEL);
- if (dev->priv == NULL) {
- release_mem_region(base_addr, sizeof(struct Am79C960));
- release_mem_region(ram_start, ARIADNE_RAM_SIZE);
- return -ENOMEM;
- }
- memset(dev->priv, 0, sizeof(struct ariadne_private));
-
dev->base_addr = ZTWO_VADDR(base_addr);
dev->mem_start = ZTWO_VADDR(ram_start);
dev->mem_end = dev->mem_start+ARIADNE_RAM_SIZE;
}
strcpy(z->name, "Hydra Ethernet Card");
+ dev = init_etherdev(NULL, sizeof(struct hydra_private));
+ memset(dev->priv, 0, sizeof(struct hydra_private));
+
for(j = 0; j < ETHER_ADDR_LEN; j++)
dev->dev_addr[j] = *((u8 *)ZTWO_VADDR(board + HYDRA_ADDRPROM + 2*j));
printk("%s: hydra at 0x%08x, address %02x:%02x:%02x:%02x:%02x:%02x (hydra.c " HYDRA_VERSION ")\n",
dev->name, (int)board, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
- init_etherdev(dev, 0);
-
- dev->priv = kmalloc(sizeof(struct hydra_private), GFP_KERNEL);
- memset(dev->priv, 0, sizeof(struct hydra_private));
-
+
dev->base_addr = ZTWO_VADDR(base_addr);
dev->mem_start = ZTWO_VADDR(board);
dev->mem_end = dev->mem_start+0x4000;
esp->irq = IRQ_MAC_SCSI;
request_irq(IRQ_MAC_SCSI, esp_intr, 0, "Mac ESP SCSI", esp);
+#if 0 /* conflicts with IOP ADB */
request_irq(IRQ_MAC_SCSIDRQ, fake_drq, 0, "Mac ESP DRQ", esp);
+#endif
if (macintosh_config->scsi_type == MAC_SCSI_QUADRA) {
esp->cfreq = 16500000;
} else { /* chipnum == 1 */
esp->irq = IRQ_MAC_SCSIDRQ;
-
+#if 0 /* conflicts with IOP ADB */
request_irq(IRQ_MAC_SCSIDRQ, esp_intr, 0, "Mac ESP SCSI 2", esp);
+#endif
esp->cfreq = 25000000;
#define NDEBUG (NDEBUG_ABORT)
#endif
-#define USE_WRAPPER
#define RESET_BOOT
#define DRIVER_SETUP
#undef DRIVER_SETUP
#endif
-/*
- * Need to define this to make SCSI work on RBV machines; leave undefined
- * to enable interrupts a bit more on other machines
- * Changes method of SCSI interrupt disable from software mask to VIA IER!
- * (don't know if that's essential)
- *
- * 990502 (jmt) - not needed (and won't work) on new irq architecture
- */
-/* #define RBV_HACK */
-
-#ifdef RBV_HACK
-#define ENABLE_IRQ() mac_turnon_irq( IRQ_MAC_SCSI );
-#define DISABLE_IRQ() mac_turnoff_irq( IRQ_MAC_SCSI );
-#else
#define ENABLE_IRQ() mac_enable_irq( IRQ_MAC_SCSI );
#define DISABLE_IRQ() mac_disable_irq( IRQ_MAC_SCSI );
-#endif
-
-#define mac_turnon_irq(x) mac_enable_irq(x)
-#define mac_turnoff_irq(x) mac_disable_irq(x)
-extern void via_scsi_clear(void);
-
-static void scsi_mac_intr(int irq, void *dummy, struct pt_regs *fp);
#ifdef RESET_BOOT
static void mac_scsi_reset_boot(struct Scsi_Host *instance);
#endif
if (macintosh_config->scsi_type != MAC_SCSI_OLD)
return( 0 );
- tpnt->proc_name = "Mac 5380 SCSI";
+ tpnt->proc_name = "mac5380";
/* setup variables */
tpnt->can_queue =
((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0;
if (instance->irq != IRQ_NONE)
-#ifdef USE_WRAPPER
- if (request_irq(instance->irq, scsi_mac_intr, IRQ_FLG_SLOW, "MacSCSI-5380", NULL)) {
-#else
- if (request_irq(instance->irq, macscsi_intr, IRQ_FLG_SLOW, "MacSCSI-5380", NULL)) {
-#endif
+ if (request_irq(instance->irq, NCR5380_intr, IRQ_FLG_SLOW, "ncr5380", NCR5380_intr)) {
printk("scsi%d: IRQ%d not free, interrupts disabled\n",
instance->host_no, instance->irq);
instance->irq = IRQ_NONE;
int macscsi_release (struct Scsi_Host *shpnt)
{
if (shpnt->irq != IRQ_NONE)
- free_irq (shpnt->irq, NULL);
+ free_irq (shpnt->irq, NCR5380_intr);
return 0;
}
printk( "Macintosh SCSI: resetting the SCSI bus..." );
/* switch off SCSI IRQ - catch an interrupt without IRQ bit set else */
- mac_turnoff_irq( IRQ_MAC_SCSI );
+ mac_disable_irq(IRQ_MAC_SCSI);
/* get in phase */
NCR5380_write( TARGET_COMMAND_REG,
barrier();
/* switch on SCSI IRQ again */
- mac_turnon_irq( IRQ_MAC_SCSI );
+ mac_enable_irq(IRQ_MAC_SCSI);
printk( " done\n" );
}
restore_flags(flags);
}
-#ifdef USE_WRAPPER
-/*
- * SCSI interrupt wrapper - just to make sure it's the proper irq, and
- * that we leave the handler in a clean state
- */
-
-static void scsi_mac_intr (int irq, void *dev_id, struct pt_regs *fp)
-{
-#ifndef RBV_HACK
- unsigned long flags;
-#endif
-
-#ifdef RBV_HACK
- mac_turnoff_irq( IRQ_MAC_SCSI );
-#else
- mac_disable_irq( IRQ_MAC_SCSI );
-#endif
-
- if ( irq == IRQ_MAC_SCSI ) {
-#ifndef RBV_HACK
- save_flags(flags);
- restore_irq(fp);
-#endif
- NCR5380_intr (irq, dev_id, fp);
-#ifndef RBV_HACK
- restore_flags(flags);
-#endif
- }
-
- /* To be sure the int is not masked */
-#ifdef RBV_HACK
- mac_turnon_irq( IRQ_MAC_SCSI );
-#else
- mac_enable_irq( IRQ_MAC_SCSI );
-#endif
-
-#if 1 /* ??? 0 worked */
- /* Clear the IRQ */
- via_scsi_clear();
-#endif
-}
-#endif
-
/*
* pseudo-DMA transfer functions, copied and modified from Russel King's
* ARM 5380 driver (cumana_1)
printk("SCSI poll\n");
save_flags(flags);
cli();
-#ifdef USE_WRAPPER
- scsi_mac_intr(IRQ_MAC_SCSI, instance, NULL);
-#else
- macscsi_intr(IRQ_MAC_SCSI, instance, NULL);
-#endif
+ NCR5380_intr(IRQ_MAC_SCSI, instance, NULL);
restore_flags(flags);
}
#if 0
if (called)
return 0;
- tpnt->proc_name = "MVME16x"
+ tpnt->proc_name = "MVME16x";
options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT;
--- /dev/null
+/* sun3_NCR5380.c -- adapted from atari_NCR5380.c for the sun3 by
+ Sam Creasey. */
+/*
+ * NCR 5380 generic driver routines. These should make it *trivial*
+ * to implement 5380 SCSI drivers under Linux with a non-trantor
+ * architecture.
+ *
+ * Note that these routines also work with NR53c400 family chips.
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * DISTRIBUTION RELEASE 6.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * ++roman: To port the 5380 driver to the Atari, I had to do some changes in
+ * this file, too:
+ *
+ * - Some of the debug statements were incorrect (undefined variables and the
+ * like). I fixed that.
+ *
+ * - In information_transfer(), I think a #ifdef was wrong. Looking at the
+ * possible DMA transfer size should also happen for REAL_DMA. I added this
+ * in the #if statement.
+ *
+ * - When using real DMA, information_transfer() should return in a DATAOUT
+ * phase after starting the DMA. It has nothing more to do.
+ *
+ * - The interrupt service routine should run main after end of DMA, too (not
+ * only after RESELECTION interrupts). Additionally, it should _not_ test
+ * for more interrupts after running main, since a DMA process may have
+ * been started and interrupts are turned on now. The new int could happen
+ * inside the execution of NCR5380_intr(), leading to recursive
+ * calls.
+ *
+ * - I've added a function merge_contiguous_buffers() that tries to
+ * merge scatter-gather buffers that are located at contiguous
+ * physical addresses and can be processed with the same DMA setup.
+ * Since most scatter-gather operations work on a page (4K) of
+ * 4 buffers (1K), in more than 90% of all cases three interrupts and
+ * DMA setup actions are saved.
+ *
+ * - I've deleted all the stuff for AUTOPROBE_IRQ, REAL_DMA_POLL, PSEUDO_DMA
+ * and USLEEP, because these were messing up readability and will never be
+ * needed for Atari SCSI.
+ *
+ * - I've revised the NCR5380_main() calling scheme (relax the 'main_running'
+ * stuff), and 'main' is executed in a bottom half if awoken by an
+ * interrupt.
+ *
+ * - The code was quite cluttered up by "#if (NDEBUG & NDEBUG_*) printk..."
+ * constructs. In my eyes, this made the source rather unreadable, so I
+ * finally replaced that by the *_PRINTK() macros.
+ *
+ */
+
+/*
+ * Further development / testing that should be done :
+ * 1. Test linked command handling code after Eric is ready with
+ * the high level code.
+ */
+
+#if (NDEBUG & NDEBUG_LISTS)
+#define LIST(x,y) \
+ { printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); \
+ if ((x)==(y)) udelay(5); }
+#define REMOVE(w,x,y,z) \
+ { printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, \
+ (void*)(w), (void*)(x), (void*)(y), (void*)(z)); \
+ if ((x)==(y)) udelay(5); }
+#else
+#define LIST(x,y)
+#define REMOVE(w,x,y,z)
+#endif
+
+#ifndef notyet
+#undef LINKED
+#endif
+
+/*
+ * Design
+ * Issues :
+ *
+ * The other Linux SCSI drivers were written when Linux was Intel PC-only,
+ * and specifically for each board rather than each chip. This makes their
+ * adaptation to platforms like the Mac (Some of which use NCR5380's)
+ * more difficult than it has to be.
+ *
+ * Also, many of the SCSI drivers were written before the command queuing
+ * routines were implemented, meaning their implementations of queued
+ * commands were hacked on rather than designed in from the start.
+ *
+ * When I designed the Linux SCSI drivers I figured that
+ * while having two different SCSI boards in a system might be useful
+ * for debugging things, two of the same type wouldn't be used.
+ * Well, I was wrong and a number of users have mailed me about running
+ * multiple high-performance SCSI boards in a server.
+ *
+ * Finally, when I get questions from users, I have no idea what
+ * revision of my driver they are running.
+ *
+ * This driver attempts to address these problems :
+ * This is a generic 5380 driver. To use it on a different platform,
+ * one simply writes appropriate system specific macros (ie, data
+ * transfer - some PC's will use the I/O bus, 68K's must use
+ * memory mapped) and drops this file in their 'C' wrapper.
+ *
+ * As far as command queueing, two queues are maintained for
+ * each 5380 in the system - commands that haven't been issued yet,
+ * and commands that are currently executing. This means that an
+ * unlimited number of commands may be queued, letting
+ * more commands propagate from the higher driver levels giving higher
+ * throughput. Note that both I_T_L and I_T_L_Q nexuses are supported,
+ * allowing multiple commands to propagate all the way to a SCSI-II device
+ * while a command is already executing.
+ *
+ * To solve the multiple-boards-in-the-same-system problem,
+ * there is a separate instance structure for each instance
+ * of a 5380 in the system. So, multiple NCR5380 drivers will
+ * be able to coexist with appropriate changes to the high level
+ * SCSI code.
+ *
+ * A NCR5380_PUBLIC_REVISION macro is provided, with the release
+ * number (updated for each public release) printed by the
+ * NCR5380_print_options command, which should be called from the
+ * wrapper detect function, so that I know what release of the driver
+ * users are using.
+ *
+ * Issues specific to the NCR5380 :
+ *
+ * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead
+ * piece of hardware that requires you to sit in a loop polling for
+ * the REQ signal as long as you are connected. Some devices are
+ * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect
+ * while doing long seek operations.
+ *
+ * The workaround for this is to keep track of devices that have
+ * disconnected. If the device hasn't disconnected, for commands that
+ * should disconnect, we do something like
+ *
+ * while (!REQ is asserted) { sleep for N usecs; poll for M usecs }
+ *
+ * Some tweaking of N and M needs to be done. An algorithm based
+ * on "time to data" would give the best results as long as short time
+ * to datas (ie, on the same track) were considered, however these
+ * broken devices are the exception rather than the rule and I'd rather
+ * spend my time optimizing for the normal case.
+ *
+ * Architecture :
+ *
+ * At the heart of the design is a coroutine, NCR5380_main,
+ * which is started when not running by the interrupt handler,
+ * timer, and queue command function. It attempts to establish
+ * I_T_L or I_T_L_Q nexuses by removing the commands from the
+ * issue queue and calling NCR5380_select() if a nexus
+ * is not established.
+ *
+ * Once a nexus is established, the NCR5380_information_transfer()
+ * phase goes through the various phases as instructed by the target.
+ * if the target goes into MSG IN and sends a DISCONNECT message,
+ * the command structure is placed into the per instance disconnected
+ * queue, and NCR5380_main tries to find more work. If USLEEP
+ * was defined, and the target is idle for too long, the system
+ * will try to sleep.
+ *
+ * If a command has disconnected, eventually an interrupt will trigger,
+ * calling NCR5380_intr() which will in turn call NCR5380_reselect
+ * to reestablish a nexus. This will run main if necessary.
+ *
+ * On command termination, the done function will be called as
+ * appropriate.
+ *
+ * SCSI pointers are maintained in the SCp field of SCSI command
+ * structures, being initialized after the command is connected
+ * in NCR5380_select, and set as appropriate in NCR5380_information_transfer.
+ * Note that in violation of the standard, an implicit SAVE POINTERS operation
+ * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS.
+ */
+
+/*
+ * Using this file :
+ * This file a skeleton Linux SCSI driver for the NCR 5380 series
+ * of chips. To use it, you write an architecture specific functions
+ * and macros and include this file in your driver.
+ *
+ * These macros control options :
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ * for commands that return with a CHECK CONDITION status.
+ *
+ * LINKED - if defined, linked commands are supported.
+ *
+ * REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
+ *
+ * SUPPORT_TAGS - if defined, SCSI-2 tagged queuing is used where possible
+ *
+ * These macros MUST be defined :
+ *
+ * NCR5380_read(register) - read from the specified register
+ *
+ * NCR5380_write(register, value) - write to the specific register
+ *
+ * Either real DMA *or* pseudo DMA may be implemented
+ * REAL functions :
+ * NCR5380_REAL_DMA should be defined if real DMA is to be used.
+ * Note that the DMA setup functions should return the number of bytes
+ * that they were able to program the controller for.
+ *
+ * Also note that generic i386/PC versions of these macros are
+ * available as NCR5380_i386_dma_write_setup,
+ * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
+ *
+ * NCR5380_dma_write_setup(instance, src, count) - initialize
+ * NCR5380_dma_read_setup(instance, dst, count) - initialize
+ * NCR5380_dma_residual(instance); - residual count
+ *
+ * PSEUDO functions :
+ * NCR5380_pwrite(instance, src, count)
+ * NCR5380_pread(instance, dst, count);
+ *
+ * If nothing specific to this implementation needs doing (ie, with external
+ * hardware), you must also define
+ *
+ * NCR5380_queue_command
+ * NCR5380_reset
+ * NCR5380_abort
+ * NCR5380_proc_info
+ *
+ * to be the global entry points into the specific driver, ie
+ * #define NCR5380_queue_command t128_queue_command.
+ *
+ * If this is not done, the routines will be defined as static functions
+ * with the NCR5380* names and the user must provide a globally
+ * accessible wrapper function.
+ *
+ * The generic driver is initialized by calling NCR5380_init(instance),
+ * after setting the appropriate host specific fields and ID. If the
+ * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance,
+ * possible) function may be used. Before the specific driver initialization
+ * code finishes, NCR5380_print_options should be called.
+ */
+
+static struct Scsi_Host *first_instance = NULL;
+static Scsi_Host_Template *the_template = NULL;
+
+/* Macros ease life... :-) */
+#define SETUP_HOSTDATA(in) \
+ struct NCR5380_hostdata *hostdata = \
+ (struct NCR5380_hostdata *)(in)->hostdata
+#define HOSTDATA(in) ((struct NCR5380_hostdata *)(in)->hostdata)
+
+#define NEXT(cmd) ((Scsi_Cmnd *)((cmd)->host_scribble))
+#define NEXTADDR(cmd) ((Scsi_Cmnd **)&((cmd)->host_scribble))
+
+#define HOSTNO instance->host_no
+#define H_NO(cmd) (cmd)->host->host_no
+
+#ifdef SUPPORT_TAGS
+
+/*
+ * Functions for handling tagged queuing
+ * =====================================
+ *
+ * ++roman (01/96): Now I've implemented SCSI-2 tagged queuing. Some notes:
+ *
+ * Using consecutive numbers for the tags is no good idea in my eyes. There
+ * could be wrong re-usings if the counter (8 bit!) wraps and some early
+ * command has been preempted for a long time. My solution: a bitfield for
+ * remembering used tags.
+ *
+ * There's also the problem that each target has a certain queue size, but we
+ * cannot know it in advance :-( We just see a QUEUE_FULL status being
+ * returned. So, in this case, the driver internal queue size assumption is
+ * reduced to the number of active tags if QUEUE_FULL is returned by the
+ * target. The command is returned to the mid-level, but with status changed
+ * to BUSY, since --as I've seen-- the mid-level can't handle QUEUE_FULL
+ * correctly.
+ *
+ * We're also not allowed running tagged commands as long as an untagged
+ * command is active. And REQUEST SENSE commands after a contingent allegiance
+ * condition _must_ be untagged. To keep track whether an untagged command has
+ * been issued, the host->busy array is still employed, as it is without
+ * support for tagged queuing.
+ *
+ * One could suspect that there are possible race conditions between
+ * is_lun_busy(), cmd_get_tag() and cmd_free_tag(). But I think this isn't the
+ * case: is_lun_busy() and cmd_get_tag() are both called from NCR5380_main(),
+ * which already guaranteed to be running at most once. It is also the only
+ * place where tags/LUNs are allocated. So no other allocation can slip
+ * between that pair, there could only happen a reselection, which can free a
+ * tag, but that doesn't hurt. Only the sequence in cmd_free_tag() becomes
+ * important: the tag bit must be cleared before 'nr_allocated' is decreased.
+ */
+
+/* -1 for TAG_NONE is not possible with unsigned char cmd->tag */
+#undef TAG_NONE
+#define TAG_NONE 0xff
+
+/* For the m68k, the number of bits in 'allocated' must be a multiple of 32! */
+#if (MAX_TAGS % 32) != 0
+#error "MAX_TAGS must be a multiple of 32!"
+#endif
+
+typedef struct {
+ char allocated[MAX_TAGS/8];
+ int nr_allocated;
+ int queue_size;
+} TAG_ALLOC;
+
+static TAG_ALLOC TagAlloc[8][8]; /* 8 targets and 8 LUNs */
+
+
+static void __init init_tags( void )
+{
+ int target, lun;
+ TAG_ALLOC *ta;
+
+ if (!setup_use_tagged_queuing)
+ return;
+
+ for( target = 0; target < 8; ++target ) {
+ for( lun = 0; lun < 8; ++lun ) {
+ ta = &TagAlloc[target][lun];
+ memset( &ta->allocated, 0, MAX_TAGS/8 );
+ ta->nr_allocated = 0;
+ /* At the beginning, assume the maximum queue size we could
+ * support (MAX_TAGS). This value will be decreased if the target
+ * returns QUEUE_FULL status.
+ */
+ ta->queue_size = MAX_TAGS;
+ }
+ }
+}
+
+
+/* Check if we can issue a command to this LUN: First see if the LUN is marked
+ * busy by an untagged command. If the command should use tagged queuing, also
+ * check that there is a free tag and the target's queue won't overflow. This
+ * function should be called with interrupts disabled to avoid race
+ * conditions.
+ */
+
+static int is_lun_busy( Scsi_Cmnd *cmd, int should_be_tagged )
+{
+ SETUP_HOSTDATA(cmd->host);
+
+ if (hostdata->busy[cmd->target] & (1 << cmd->lun))
+ return( 1 );
+ if (!should_be_tagged ||
+ !setup_use_tagged_queuing || !cmd->device->tagged_supported)
+ return( 0 );
+ if (TagAlloc[cmd->target][cmd->lun].nr_allocated >=
+ TagAlloc[cmd->target][cmd->lun].queue_size ) {
+ TAG_PRINTK( "scsi%d: target %d lun %d: no free tags\n",
+ H_NO(cmd), cmd->target, cmd->lun );
+ return( 1 );
+ }
+ return( 0 );
+}
+
+
+/* Allocate a tag for a command (there are no checks anymore, check_lun_busy()
+ * must be called before!), or reserve the LUN in 'busy' if the command is
+ * untagged.
+ */
+
+static void cmd_get_tag( Scsi_Cmnd *cmd, int should_be_tagged )
+{
+ SETUP_HOSTDATA(cmd->host);
+
+ /* If we or the target don't support tagged queuing, allocate the LUN for
+ * an untagged command.
+ */
+ if (!should_be_tagged ||
+ !setup_use_tagged_queuing || !cmd->device->tagged_supported) {
+ cmd->tag = TAG_NONE;
+ hostdata->busy[cmd->target] |= (1 << cmd->lun);
+ TAG_PRINTK( "scsi%d: target %d lun %d now allocated by untagged "
+ "command\n", H_NO(cmd), cmd->target, cmd->lun );
+ }
+ else {
+ TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun];
+
+ cmd->tag = find_first_zero_bit( &ta->allocated, MAX_TAGS );
+ set_bit( cmd->tag, &ta->allocated );
+ ta->nr_allocated++;
+ TAG_PRINTK( "scsi%d: using tag %d for target %d lun %d "
+ "(now %d tags in use)\n",
+ H_NO(cmd), cmd->tag, cmd->target, cmd->lun,
+ ta->nr_allocated );
+ }
+}
+
+
+/* Mark the tag of command 'cmd' as free, or in case of an untagged command,
+ * unlock the LUN.
+ */
+
+static void cmd_free_tag( Scsi_Cmnd *cmd )
+{
+ SETUP_HOSTDATA(cmd->host);
+
+ if (cmd->tag == TAG_NONE) {
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+ TAG_PRINTK( "scsi%d: target %d lun %d untagged cmd finished\n",
+ H_NO(cmd), cmd->target, cmd->lun );
+ }
+ else if (cmd->tag >= MAX_TAGS) {
+ printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n",
+ H_NO(cmd), cmd->tag );
+ }
+ else {
+ TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun];
+ clear_bit( cmd->tag, &ta->allocated );
+ ta->nr_allocated--;
+ TAG_PRINTK( "scsi%d: freed tag %d for target %d lun %d\n",
+ H_NO(cmd), cmd->tag, cmd->target, cmd->lun );
+ }
+}
+
+
+static void free_all_tags( void )
+{
+ int target, lun;
+ TAG_ALLOC *ta;
+
+ if (!setup_use_tagged_queuing)
+ return;
+
+ for( target = 0; target < 8; ++target ) {
+ for( lun = 0; lun < 8; ++lun ) {
+ ta = &TagAlloc[target][lun];
+ memset( &ta->allocated, 0, MAX_TAGS/8 );
+ ta->nr_allocated = 0;
+ }
+ }
+}
+
+#endif /* SUPPORT_TAGS */
+
+
+/*
+ * Function: void merge_contiguous_buffers( Scsi_Cmnd *cmd )
+ *
+ * Purpose: Try to merge several scatter-gather requests into one DMA
+ * transfer. This is possible if the scatter buffers lie on
+ * physical contiguous addresses.
+ *
+ * Parameters: Scsi_Cmnd *cmd
+ * The command to work on. The first scatter buffer's data are
+ * assumed to be already transfered into ptr/this_residual.
+ */
+
+static void merge_contiguous_buffers( Scsi_Cmnd *cmd )
+{
+ unsigned long endaddr;
+#if (NDEBUG & NDEBUG_MERGING)
+ unsigned long oldlen = cmd->SCp.this_residual;
+ int cnt = 1;
+#endif
+
+ for (endaddr = virt_to_phys(cmd->SCp.ptr + cmd->SCp.this_residual - 1) + 1;
+ cmd->SCp.buffers_residual &&
+ virt_to_phys(cmd->SCp.buffer[1].address) == endaddr; ) {
+
+ MER_PRINTK("VTOP(%p) == %08lx -> merging\n",
+ cmd->SCp.buffer[1].address, endaddr);
+#if (NDEBUG & NDEBUG_MERGING)
+ ++cnt;
+#endif
+ ++cmd->SCp.buffer;
+ --cmd->SCp.buffers_residual;
+ cmd->SCp.this_residual += cmd->SCp.buffer->length;
+ endaddr += cmd->SCp.buffer->length;
+ }
+#if (NDEBUG & NDEBUG_MERGING)
+ if (oldlen != cmd->SCp.this_residual)
+ MER_PRINTK("merged %d buffers from %p, new length %08x\n",
+ cnt, cmd->SCp.ptr, cmd->SCp.this_residual);
+#endif
+}
+
+/*
+ * Function : void initialize_SCp(Scsi_Cmnd *cmd)
+ *
+ * Purpose : initialize the saved data pointers for cmd to point to the
+ * start of the buffer.
+ *
+ * Inputs : cmd - Scsi_Cmnd structure to have pointers reset.
+ */
+
+static __inline__ void initialize_SCp(Scsi_Cmnd *cmd)
+{
+ /*
+ * Initialize the Scsi Pointer field so that all of the commands in the
+ * various queues are valid.
+ */
+
+ if (cmd->use_sg) {
+ cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
+ cmd->SCp.buffers_residual = cmd->use_sg - 1;
+ cmd->SCp.ptr = (char *) cmd->SCp.buffer->address;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ /* ++roman: Try to merge some scatter-buffers if they are at
+ * contiguous physical addresses.
+ */
+ merge_contiguous_buffers( cmd );
+ } else {
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *) cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ }
+
+}
+
+#include <linux/config.h>
+#include <linux/delay.h>
+
+#if 1
+static struct {
+ unsigned char mask;
+ const char * name;}
+signals[] = {{ SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" },
+ { SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD, "CD" }, { SR_IO, "IO" },
+ { SR_SEL, "SEL" }, {0, NULL}},
+basrs[] = {{BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}},
+icrs[] = {{ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"},
+ {ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"},
+ {ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"},
+ {0, NULL}},
+mrs[] = {{MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"},
+ {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR,
+ "MODE PARITY INTR"}, {MR_ENABLE_EOP_INTR,"MODE EOP INTR"},
+ {MR_MONITOR_BSY, "MODE MONITOR BSY"},
+ {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"},
+ {0, NULL}};
+
+/*
+ * Function : void NCR5380_print(struct Scsi_Host *instance)
+ *
+ * Purpose : print the SCSI bus signals for debugging purposes
+ *
+ * Input : instance - which NCR5380
+ */
+
+static void NCR5380_print(struct Scsi_Host *instance) {
+ unsigned char status, data, basr, mr, icr, i;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ data = NCR5380_read(CURRENT_SCSI_DATA_REG);
+ status = NCR5380_read(STATUS_REG);
+ mr = NCR5380_read(MODE_REG);
+ icr = NCR5380_read(INITIATOR_COMMAND_REG);
+ basr = NCR5380_read(BUS_AND_STATUS_REG);
+ restore_flags(flags);
+ printk("STATUS_REG: %02x ", status);
+ for (i = 0; signals[i].mask ; ++i)
+ if (status & signals[i].mask)
+ printk(",%s", signals[i].name);
+ printk("\nBASR: %02x ", basr);
+ for (i = 0; basrs[i].mask ; ++i)
+ if (basr & basrs[i].mask)
+ printk(",%s", basrs[i].name);
+ printk("\nICR: %02x ", icr);
+ for (i = 0; icrs[i].mask; ++i)
+ if (icr & icrs[i].mask)
+ printk(",%s", icrs[i].name);
+ printk("\nMODE: %02x ", mr);
+ for (i = 0; mrs[i].mask; ++i)
+ if (mr & mrs[i].mask)
+ printk(",%s", mrs[i].name);
+ printk("\n");
+}
+
+static struct {
+ unsigned char value;
+ const char *name;
+} phases[] = {
+ {PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
+ {PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
+ {PHASE_UNKNOWN, "UNKNOWN"}};
+
+/*
+ * Function : void NCR5380_print_phase(struct Scsi_Host *instance)
+ *
+ * Purpose : print the current SCSI phase for debugging purposes
+ *
+ * Input : instance - which NCR5380
+ */
+
+static void NCR5380_print_phase(struct Scsi_Host *instance)
+{
+ unsigned char status;
+ int i;
+
+ status = NCR5380_read(STATUS_REG);
+ if (!(status & SR_REQ))
+ printk(KERN_DEBUG "scsi%d: REQ not asserted, phase unknown.\n", HOSTNO);
+ else {
+ for (i = 0; (phases[i].value != PHASE_UNKNOWN) &&
+ (phases[i].value != (status & PHASE_MASK)); ++i);
+ printk(KERN_DEBUG "scsi%d: phase %s\n", HOSTNO, phases[i].name);
+ }
+}
+
+#else /* !NDEBUG */
+
+/* dummies... */
+__inline__ void NCR5380_print(struct Scsi_Host *instance) { };
+__inline__ void NCR5380_print_phase(struct Scsi_Host *instance) { };
+
+#endif
+
+/*
+ * ++roman: New scheme of calling NCR5380_main()
+ *
+ * If we're not in an interrupt, we can call our main directly, it cannot be
+ * already running. Else, we queue it on a task queue, if not 'main_running'
+ * tells us that a lower level is already executing it. This way,
+ * 'main_running' needs not be protected in a special way.
+ *
+ * queue_main() is a utility function for putting our main onto the task
+ * queue, if main_running is false. It should be called only from a
+ * interrupt or bottom half.
+ */
+
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+
+static volatile int main_running = 0;
+static struct tq_struct NCR5380_tqueue = {
+ NULL, /* next */
+ 0, /* sync */
+ (void (*)(void*))NCR5380_main, /* routine, must have (void *) arg... */
+ NULL /* data */
+};
+
+static __inline__ void queue_main(void)
+{
+ if (!main_running) {
+ /* If in interrupt and NCR5380_main() not already running,
+ queue it on the 'immediate' task queue, to be processed
+ immediately after the current interrupt processing has
+ finished. */
+ queue_task(&NCR5380_tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ }
+ /* else: nothing to do: the running NCR5380_main() will pick up
+ any newly queued command. */
+}
+
+
+static inline void NCR5380_all_init (void)
+{
+ static int done = 0;
+ if (!done) {
+ INI_PRINTK("scsi : NCR5380_all_init()\n");
+ done = 1;
+ }
+}
+
+
+/*
+ * Function : void NCR58380_print_options (struct Scsi_Host *instance)
+ *
+ * Purpose : called by probe code indicating the NCR5380 driver
+ * options that were selected.
+ *
+ * Inputs : instance, pointer to this instance. Unused.
+ */
+
+static void __init NCR5380_print_options (struct Scsi_Host *instance)
+{
+ printk(" generic options"
+#ifdef AUTOSENSE
+ " AUTOSENSE"
+#endif
+#ifdef REAL_DMA
+ " REAL DMA"
+#endif
+#ifdef PARITY
+ " PARITY"
+#endif
+#ifdef SUPPORT_TAGS
+ " SCSI-2 TAGGED QUEUING"
+#endif
+ );
+ printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
+}
+
+/*
+ * Function : void NCR5380_print_status (struct Scsi_Host *instance)
+ *
+ * Purpose : print commands in the various queues, called from
+ * NCR5380_abort and NCR5380_debug to aid debugging.
+ *
+ * Inputs : instance, pointer to this instance.
+ */
+
+static void NCR5380_print_status (struct Scsi_Host *instance)
+{
+ char *pr_bfr;
+ char *start;
+ int len;
+
+ NCR_PRINT(NDEBUG_ANY);
+ NCR_PRINT_PHASE(NDEBUG_ANY);
+
+ pr_bfr = (char *) __get_free_page(GFP_ATOMIC);
+ if (!pr_bfr) {
+ printk("NCR5380_print_status: no memory for print buffer\n");
+ return;
+ }
+ len = NCR5380_proc_info(pr_bfr, &start, 0, PAGE_SIZE, HOSTNO, 0);
+ pr_bfr[len] = 0;
+ printk("\n%s\n", pr_bfr);
+ free_page((unsigned long) pr_bfr);
+}
+
+
+/******************************************/
+/*
+ * /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED]
+ *
+ * *buffer: I/O buffer
+ * **start: if inout == FALSE pointer into buffer where user read should start
+ * offset: current offset
+ * length: length of buffer
+ * hostno: Scsi_Host host_no
+ * inout: TRUE - user is writing; FALSE - user is reading
+ *
+ * Return the number of bytes read from or written
+*/
+
+#undef SPRINTF
+#define SPRINTF(fmt,args...) \
+ do { if (pos + strlen(fmt) + 20 /* slop */ < buffer + length) \
+ pos += sprintf(pos, fmt , ## args); } while(0)
+static
+char *lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length);
+
+#ifndef NCR5380_proc_info
+static
+#endif
+int NCR5380_proc_info (char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout)
+{
+ char *pos = buffer;
+ struct Scsi_Host *instance;
+ struct NCR5380_hostdata *hostdata;
+ Scsi_Cmnd *ptr;
+ unsigned long flags;
+ off_t begin = 0;
+#define check_offset() \
+ do { \
+ if (pos - buffer < offset - begin) { \
+ begin += pos - buffer; \
+ pos = buffer; \
+ } \
+ } while (0)
+
+ for (instance = first_instance; instance && HOSTNO != hostno;
+ instance = instance->next)
+ ;
+ if (!instance)
+ return(-ESRCH);
+ hostdata = (struct NCR5380_hostdata *)instance->hostdata;
+
+ if (inout) { /* Has data been written to the file ? */
+ return(-ENOSYS); /* Currently this is a no-op */
+ }
+ SPRINTF("NCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE);
+ check_offset();
+ save_flags(flags);
+ cli();
+ SPRINTF("NCR5380: coroutine is%s running.\n", main_running ? "" : "n't");
+ check_offset();
+ if (!hostdata->connected)
+ SPRINTF("scsi%d: no currently connected command\n", HOSTNO);
+ else
+ pos = lprint_Scsi_Cmnd ((Scsi_Cmnd *) hostdata->connected,
+ pos, buffer, length);
+ SPRINTF("scsi%d: issue_queue\n", HOSTNO);
+ check_offset();
+ for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = NEXT(ptr)) {
+ pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
+ check_offset();
+ }
+
+ SPRINTF("scsi%d: disconnected_queue\n", HOSTNO);
+ check_offset();
+ for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
+ ptr = NEXT(ptr)) {
+ pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
+ check_offset();
+ }
+
+ restore_flags(flags);
+ *start = buffer + (offset - begin);
+ if (pos - buffer < offset - begin)
+ return 0;
+ else if (pos - buffer - (offset - begin) < length)
+ return pos - buffer - (offset - begin);
+ return length;
+}
+
+static char *
+lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length)
+{
+ int i, s;
+ unsigned char *command;
+ SPRINTF("scsi%d: destination target %d, lun %d\n",
+ H_NO(cmd), cmd->target, cmd->lun);
+ SPRINTF(" command = ");
+ command = cmd->cmnd;
+ SPRINTF("%2d (0x%02x)", command[0], command[0]);
+ for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
+ SPRINTF(" %02x", command[i]);
+ SPRINTF("\n");
+ return pos;
+}
+
+
+/*
+ * Function : void NCR5380_init (struct Scsi_Host *instance)
+ *
+ * Purpose : initializes *instance and corresponding 5380 chip.
+ *
+ * Inputs : instance - instantiation of the 5380 driver.
+ *
+ * Notes : I assume that the host, hostno, and id bits have been
+ * set correctly. I don't care about the irq and other fields.
+ *
+ */
+
+static void __init NCR5380_init (struct Scsi_Host *instance, int flags)
+{
+ int i;
+ SETUP_HOSTDATA(instance);
+
+ NCR5380_all_init();
+
+ hostdata->aborted = 0;
+ hostdata->id_mask = 1 << instance->this_id;
+ hostdata->id_higher_mask = 0;
+ for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
+ if (i > hostdata->id_mask)
+ hostdata->id_higher_mask |= i;
+ for (i = 0; i < 8; ++i)
+ hostdata->busy[i] = 0;
+#ifdef SUPPORT_TAGS
+ init_tags();
+#endif
+#if defined (REAL_DMA)
+ hostdata->dma_len = 0;
+#endif
+ hostdata->targets_present = 0;
+ hostdata->connected = NULL;
+ hostdata->issue_queue = NULL;
+ hostdata->disconnected_queue = NULL;
+ hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT;
+
+ if (!the_template) {
+ the_template = instance->hostt;
+ first_instance = instance;
+ }
+
+
+#ifndef AUTOSENSE
+ if ((instance->cmd_per_lun > 1) || (instance->can_queue > 1))
+ printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n"
+ " without AUTOSENSE option, contingent allegiance conditions may\n"
+ " be incorrectly cleared.\n", HOSTNO);
+#endif /* def AUTOSENSE */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+}
+
+/*
+ * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd,
+ * void (*done)(Scsi_Cmnd *))
+ *
+ * Purpose : enqueues a SCSI command
+ *
+ * Inputs : cmd - SCSI command, done - function called on completion, with
+ * a pointer to the command descriptor.
+ *
+ * Returns : 0
+ *
+ * Side effects :
+ * cmd is added to the per instance issue_queue, with minor
+ * twiddling done to the host specific fields of cmd. If the
+ * main coroutine is not running, it is restarted.
+ *
+ */
+
+/* Only make static if a wrapper function is used */
+#ifndef NCR5380_queue_command
+static
+#endif
+int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+ SETUP_HOSTDATA(cmd->host);
+ Scsi_Cmnd *tmp;
+ unsigned long flags;
+ extern int update_timeout(Scsi_Cmnd * SCset, int timeout);
+
+#if (NDEBUG & NDEBUG_NO_WRITE)
+ switch (cmd->cmnd[0]) {
+ case WRITE_6:
+ case WRITE_10:
+ printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n",
+ H_NO(cmd));
+ cmd->result = (DID_ERROR << 16);
+ done(cmd);
+ return 0;
+ }
+#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
+
+
+#ifdef NCR5380_STATS
+# if 0
+ if (!hostdata->connected && !hostdata->issue_queue &&
+ !hostdata->disconnected_queue) {
+ hostdata->timebase = jiffies;
+ }
+# endif
+# ifdef NCR5380_STAT_LIMIT
+ if (cmd->request_bufflen > NCR5380_STAT_LIMIT)
+# endif
+ switch (cmd->cmnd[0])
+ {
+ case WRITE:
+ case WRITE_6:
+ case WRITE_10:
+ hostdata->time_write[cmd->target] -= (jiffies - hostdata->timebase);
+ hostdata->bytes_write[cmd->target] += cmd->request_bufflen;
+ hostdata->pendingw++;
+ break;
+ case READ:
+ case READ_6:
+ case READ_10:
+ hostdata->time_read[cmd->target] -= (jiffies - hostdata->timebase);
+ hostdata->bytes_read[cmd->target] += cmd->request_bufflen;
+ hostdata->pendingr++;
+ break;
+ }
+#endif
+
+ /*
+ * We use the host_scribble field as a pointer to the next command
+ * in a queue
+ */
+
+ NEXT(cmd) = NULL;
+ cmd->scsi_done = done;
+
+ cmd->result = 0;
+
+
+ /*
+ * Insert the cmd into the issue queue. Note that REQUEST SENSE
+ * commands are added to the head of the queue since any command will
+ * clear the contingent allegiance condition that exists and the
+ * sense data is only guaranteed to be valid while the condition exists.
+ */
+
+ save_flags(flags);
+ cli();
+ /* ++guenther: now that the issue queue is being set up, we can lock ST-DMA.
+ * Otherwise a running NCR5380_main may steal the lock.
+ * Lock before actually inserting due to fairness reasons explained in
+ * atari_scsi.c. If we insert first, then it's impossible for this driver
+ * to release the lock.
+ * Stop timer for this command while waiting for the lock, or timeouts
+ * may happen (and they really do), and it's no good if the command doesn't
+ * appear in any of the queues.
+ * ++roman: Just disabling the NCR interrupt isn't sufficient here,
+ * because also a timer int can trigger an abort or reset, which would
+ * alter queues and touch the lock.
+ */
+ if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+ LIST(cmd, hostdata->issue_queue);
+ NEXT(cmd) = hostdata->issue_queue;
+ hostdata->issue_queue = cmd;
+ } else {
+ for (tmp = (Scsi_Cmnd *)hostdata->issue_queue;
+ NEXT(tmp); tmp = NEXT(tmp))
+ ;
+ LIST(cmd, tmp);
+ NEXT(tmp) = cmd;
+ }
+
+ restore_flags(flags);
+
+ QU_PRINTK("scsi%d: command added to %s of queue\n", H_NO(cmd),
+ (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
+
+ /* If queue_command() is called from an interrupt (real one or bottom
+ * half), we let queue_main() do the job of taking care about main. If it
+ * is already running, this is a no-op, else main will be queued.
+ *
+ * If we're not in an interrupt, we can call NCR5380_main()
+ * unconditionally, because it cannot be already running.
+ */
+ if (in_interrupt() || ((flags >> 8) & 7) >= 6)
+ queue_main();
+ else
+ NCR5380_main();
+ return 0;
+}
+
+/*
+ * Function : NCR5380_main (void)
+ *
+ * Purpose : NCR5380_main is a coroutine that runs as long as more work can
+ * be done on the NCR5380 host adapters in a system. Both
+ * NCR5380_queue_command() and NCR5380_intr() will try to start it
+ * in case it is not running.
+ *
+ * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should
+ * reenable them. This prevents reentrancy and kernel stack overflow.
+ */
+
+static void NCR5380_main (void)
+{
+ Scsi_Cmnd *tmp, *prev;
+ struct Scsi_Host *instance = first_instance;
+ struct NCR5380_hostdata *hostdata = HOSTDATA(instance);
+ int done;
+ unsigned long flags;
+
+ /*
+ * We run (with interrupts disabled) until we're sure that none of
+ * the host adapters have anything that can be done, at which point
+ * we set main_running to 0 and exit.
+ *
+ * Interrupts are enabled before doing various other internal
+ * instructions, after we've decided that we need to run through
+ * the loop again.
+ *
+ * this should prevent any race conditions.
+ *
+ * ++roman: Just disabling the NCR interrupt isn't sufficient here,
+ * because also a timer int can trigger an abort or reset, which can
+ * alter queues and touch the Falcon lock.
+ */
+
+ /* Tell int handlers main() is now already executing. Note that
+ no races are possible here. If an int comes in before
+ 'main_running' is set here, and queues/executes main via the
+ task queue, it doesn't do any harm, just this instance of main
+ won't find any work left to do. */
+ if (main_running)
+ return;
+ main_running = 1;
+
+ save_flags(flags);
+ do {
+ cli(); /* Freeze request queues */
+ done = 1;
+
+ if (!hostdata->connected) {
+ MAIN_PRINTK( "scsi%d: not connected\n", HOSTNO );
+ /*
+ * Search through the issue_queue for a command destined
+ * for a target that's not busy.
+ */
+#if (NDEBUG & NDEBUG_LISTS)
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL;
+ tmp && (tmp != prev); prev = tmp, tmp = NEXT(tmp))
+ ;
+ if ((tmp == prev) && tmp) printk(" LOOP\n");/* else printk("\n");*/
+#endif
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue,
+ prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp) ) {
+
+#if (NDEBUG & NDEBUG_LISTS)
+ if (prev != tmp)
+ printk("MAIN tmp=%p target=%d busy=%d lun=%d\n",
+ tmp, tmp->target, hostdata->busy[tmp->target],
+ tmp->lun);
+#endif
+ /* When we find one, remove it from the issue queue. */
+ /* ++guenther: possible race with Falcon locking */
+ if (
+#ifdef SUPPORT_TAGS
+ !is_lun_busy( tmp, tmp->cmnd[0] != REQUEST_SENSE)
+#else
+ !(hostdata->busy[tmp->target] & (1 << tmp->lun))
+#endif
+ ) {
+ cli(); /* ++guenther: just to be sure, this must be atomic */
+ if (prev) {
+ REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
+ NEXT(prev) = NEXT(tmp);
+ } else {
+ REMOVE(-1, hostdata->issue_queue, tmp, NEXT(tmp));
+ hostdata->issue_queue = NEXT(tmp);
+ }
+ NEXT(tmp) = NULL;
+
+ /* reenable interrupts after finding one */
+ restore_flags(flags);
+
+ /*
+ * Attempt to establish an I_T_L nexus here.
+ * On success, instance->hostdata->connected is set.
+ * On failure, we must add the command back to the
+ * issue queue so we can keep trying.
+ */
+ MAIN_PRINTK("scsi%d: main(): command for target %d "
+ "lun %d removed from issue_queue\n",
+ HOSTNO, tmp->target, tmp->lun);
+ /*
+ * REQUEST SENSE commands are issued without tagged
+ * queueing, even on SCSI-II devices because the
+ * contingent allegiance condition exists for the
+ * entire unit.
+ */
+ /* ++roman: ...and the standard also requires that
+ * REQUEST SENSE command are untagged.
+ */
+
+#ifdef SUPPORT_TAGS
+ cmd_get_tag( tmp, tmp->cmnd[0] != REQUEST_SENSE );
+#endif
+ if (!NCR5380_select(instance, tmp,
+ (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE :
+ TAG_NEXT)) {
+ break;
+ } else {
+ cli();
+ LIST(tmp, hostdata->issue_queue);
+ NEXT(tmp) = hostdata->issue_queue;
+ hostdata->issue_queue = tmp;
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( tmp );
+#endif
+ restore_flags(flags);
+ MAIN_PRINTK("scsi%d: main(): select() failed, "
+ "returned to issue_queue\n", HOSTNO);
+ if (hostdata->connected)
+ break;
+ }
+ } /* if target/lun/target queue is not busy */
+ } /* for issue_queue */
+ } /* if (!hostdata->connected) */
+ if (hostdata->connected
+#ifdef REAL_DMA
+ && !hostdata->dma_len
+#endif
+ ) {
+ restore_flags(flags);
+ MAIN_PRINTK("scsi%d: main: performing information transfer\n",
+ HOSTNO);
+ NCR5380_information_transfer(instance);
+ MAIN_PRINTK("scsi%d: main: done set false\n", HOSTNO);
+ done = 0;
+ }
+ } while (!done);
+
+ /* Better allow ints _after_ 'main_running' has been cleared, else
+ an interrupt could believe we'll pick up the work it left for
+ us, but we won't see it anymore here... */
+ main_running = 0;
+ restore_flags(flags);
+}
+
+
+#ifdef REAL_DMA
+/*
+ * Function : void NCR5380_dma_complete (struct Scsi_Host *instance)
+ *
+ * Purpose : Called by interrupt handler when DMA finishes or a phase
+ * mismatch occurs (which would finish the DMA transfer).
+ *
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ */
+
+static void NCR5380_dma_complete( struct Scsi_Host *instance )
+{
+ SETUP_HOSTDATA(instance);
+ int transfered;
+ unsigned char **data;
+ volatile int *count;
+
+ if (!hostdata->connected) {
+ printk(KERN_WARNING "scsi%d: received end of DMA interrupt with "
+ "no connected cmd\n", HOSTNO);
+ return;
+ }
+
+ DMA_PRINTK("scsi%d: real DMA transfer complete, basr 0x%X, sr 0x%X\n",
+ HOSTNO, NCR5380_read(BUS_AND_STATUS_REG),
+ NCR5380_read(STATUS_REG));
+
+ if((sun3scsi_dma_finish())) {
+ printk("scsi%d: overrun in UDC counter -- not prepared to deal with this!\n", HOSTNO);
+ printk("please e-mail sammy@oh.verio.com with a description of how this\n");
+ printk("error was produced.\n");
+ machine_halt();
+ }
+
+ /* make sure we're not stuck in a data phase */
+ if((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH |
+ BASR_ACK)) ==
+ (BASR_PHASE_MATCH | BASR_ACK)) {
+ printk("scsi%d: BASR %02x\n", HOSTNO, NCR5380_read(BUS_AND_STATUS_REG));
+ printk("scsi%d: bus stuck in data phase -- probably a
+ single byte overrun!\n", HOSTNO);
+ printk("not prepared for this error!\n");
+ printk("please e-mail sammy@oh.verio.com with a description of how this\n");
+ printk("error was produced.\n");
+ machine_halt();
+ }
+
+
+
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ transfered = hostdata->dma_len - NCR5380_dma_residual(instance);
+ hostdata->dma_len = 0;
+
+ data = (unsigned char **) &(hostdata->connected->SCp.ptr);
+ count = &(hostdata->connected->SCp.this_residual);
+ *data += transfered;
+ *count -= transfered;
+
+}
+#endif /* REAL_DMA */
+
+
+/*
+ * Function : void NCR5380_intr (int irq)
+ *
+ * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
+ * from the disconnected queue, and restarting NCR5380_main()
+ * as required.
+ *
+ * Inputs : int irq, irq that caused this interrupt.
+ *
+ */
+
+static void NCR5380_intr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct Scsi_Host *instance = first_instance;
+ int done = 1;
+ unsigned char basr;
+
+ INT_PRINTK("scsi%d: NCR5380 irq triggered\n", HOSTNO);
+
+ /* Look for pending interrupts */
+ basr = NCR5380_read(BUS_AND_STATUS_REG);
+ INT_PRINTK("scsi%d: BASR=%02x\n", HOSTNO, basr);
+ /* dispatch to appropriate routine if found and done=0 */
+ if (basr & BASR_IRQ) {
+ NCR_PRINT(NDEBUG_INTR);
+ if ((NCR5380_read(STATUS_REG) & (SR_SEL|SR_IO)) == (SR_SEL|SR_IO)) {
+ done = 0;
+ ENABLE_IRQ();
+ INT_PRINTK("scsi%d: SEL interrupt\n", HOSTNO);
+ NCR5380_reselect(instance);
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ }
+ else if (basr & BASR_PARITY_ERROR) {
+ INT_PRINTK("scsi%d: PARITY interrupt\n", HOSTNO);
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ }
+ else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) {
+ INT_PRINTK("scsi%d: RESET interrupt\n", HOSTNO);
+ (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ }
+ else {
+ /*
+ * The rest of the interrupt conditions can occur only during a
+ * DMA transfer
+ */
+
+#if defined(REAL_DMA)
+ /*
+ * We should only get PHASE MISMATCH and EOP interrupts if we have
+ * DMA enabled, so do a sanity check based on the current setting
+ * of the MODE register.
+ */
+
+ if ((NCR5380_read(MODE_REG) & MR_DMA_MODE) &&
+ ((basr & BASR_END_DMA_TRANSFER) ||
+ !(basr & BASR_PHASE_MATCH))) {
+
+ INT_PRINTK("scsi%d: PHASE MISM or EOP interrupt\n", HOSTNO);
+ NCR5380_dma_complete( instance );
+ done = 0;
+ ENABLE_IRQ();
+ } else
+#endif /* REAL_DMA */
+ {
+/* MS: Ignore unknown phase mismatch interrupts (caused by EOP interrupt) */
+ if (basr & BASR_PHASE_MATCH)
+ printk(KERN_NOTICE "scsi%d: unknown interrupt, "
+ "BASR 0x%x, MR 0x%x, SR 0x%x\n",
+ HOSTNO, basr, NCR5380_read(MODE_REG),
+ NCR5380_read(STATUS_REG));
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ }
+ } /* if !(SELECTION || PARITY) */
+ } /* BASR & IRQ */
+ else {
+
+ printk(KERN_NOTICE "scsi%d: interrupt without IRQ bit set in BASR, "
+ "BASR 0x%X, MR 0x%X, SR 0x%x\n", HOSTNO, basr,
+ NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG));
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ }
+
+ if (!done) {
+ INT_PRINTK("scsi%d: in int routine, calling main\n", HOSTNO);
+ /* Put a call to NCR5380_main() on the queue... */
+ queue_main();
+ }
+}
+
+#ifdef NCR5380_STATS
+static void collect_stats(struct NCR5380_hostdata* hostdata, Scsi_Cmnd* cmd)
+{
+# ifdef NCR5380_STAT_LIMIT
+ if (cmd->request_bufflen > NCR5380_STAT_LIMIT)
+# endif
+ switch (cmd->cmnd[0])
+ {
+ case WRITE:
+ case WRITE_6:
+ case WRITE_10:
+ hostdata->time_write[cmd->target] += (jiffies - hostdata->timebase);
+ /*hostdata->bytes_write[cmd->target] += cmd->request_bufflen;*/
+ hostdata->pendingw--;
+ break;
+ case READ:
+ case READ_6:
+ case READ_10:
+ hostdata->time_read[cmd->target] += (jiffies - hostdata->timebase);
+ /*hostdata->bytes_read[cmd->target] += cmd->request_bufflen;*/
+ hostdata->pendingr--;
+ break;
+ }
+}
+#endif
+
+/*
+ * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+ * int tag);
+ *
+ * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
+ * including ARBITRATION, SELECTION, and initial message out for
+ * IDENTIFY and queue messages.
+ *
+ * Inputs : instance - instantiation of the 5380 driver on which this
+ * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for
+ * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for
+ * the command that is presently connected.
+ *
+ * Returns : -1 if selection could not execute for some reason,
+ * 0 if selection succeeded or failed because the target
+ * did not respond.
+ *
+ * Side effects :
+ * If bus busy, arbitration failed, etc, NCR5380_select() will exit
+ * with registers as they should have been on entry - ie
+ * SELECT_ENABLE will be set appropriately, the NCR5380
+ * will cease to drive any SCSI bus signals.
+ *
+ * If successful : I_T_L or I_T_L_Q nexus will be established,
+ * instance->connected will be set to cmd.
+ * SELECT interrupt will be disabled.
+ *
+ * If failed (no target) : cmd->scsi_done() will be called, and the
+ * cmd->result host byte set to DID_BAD_TARGET.
+ */
+
+static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
+{
+ SETUP_HOSTDATA(instance);
+ unsigned char tmp[3], phase;
+ unsigned char *data;
+ int len;
+ unsigned long timeout;
+ unsigned long flags;
+
+ hostdata->restart_select = 0;
+ NCR_PRINT(NDEBUG_ARBITRATION);
+ ARB_PRINTK("scsi%d: starting arbitration, id = %d\n", HOSTNO,
+ instance->this_id);
+
+ /*
+ * Set the phase bits to 0, otherwise the NCR5380 won't drive the
+ * data bus during SELECTION.
+ */
+
+ save_flags(flags);
+ cli();
+ if (hostdata->connected) {
+ restore_flags(flags);
+ return -1;
+ }
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+
+ /*
+ * Start arbitration.
+ */
+
+ NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
+ NCR5380_write(MODE_REG, MR_ARBITRATE);
+
+ restore_flags(flags);
+
+ /* Wait for arbitration logic to complete */
+#if NCR_TIMEOUT
+ {
+ unsigned long timeout = jiffies + 2*NCR_TIMEOUT;
+
+ while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
+ && time_before(jiffies, timeout) && !hostdata->connected)
+ ;
+ if (time_after_eq(jiffies, timeout))
+ {
+ printk("scsi : arbitration timeout at %d\n", __LINE__);
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+ }
+#else /* NCR_TIMEOUT */
+ while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
+ && !hostdata->connected);
+#endif
+
+ ARB_PRINTK("scsi%d: arbitration complete\n", HOSTNO);
+
+ if (hostdata->connected) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ return -1;
+ }
+ /*
+ * The arbitration delay is 2.2us, but this is a minimum and there is
+ * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate
+ * the integral nature of udelay().
+ *
+ */
+
+ udelay(3);
+
+ /* Check for lost arbitration */
+ if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+ (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) ||
+ (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+ hostdata->connected) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ ARB_PRINTK("scsi%d: lost arbitration, deasserting MR_ARBITRATE\n",
+ HOSTNO);
+ return -1;
+ }
+
+ /* after/during arbitration, BSY should be asserted.
+ IBM DPES-31080 Version S31Q works now */
+ /* Tnx to Thomas_Roesch@m2.maus.de for finding this! (Roman) */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL |
+ ICR_ASSERT_BSY ) ;
+
+ if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+ hostdata->connected) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ ARB_PRINTK("scsi%d: lost arbitration, deasserting ICR_ASSERT_SEL\n",
+ HOSTNO);
+ return -1;
+ }
+
+ /*
+ * Again, bus clear + bus settle time is 1.2us, however, this is
+ * a minimum so we'll udelay ceil(1.2)
+ */
+
+#ifdef CONFIG_ATARI_SCSI_TOSHIBA_DELAY
+ /* ++roman: But some targets (see above :-) seem to need a bit more... */
+ udelay(15);
+#else
+ udelay(2);
+#endif
+
+ if (hostdata->connected) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ return -1;
+ }
+
+ ARB_PRINTK("scsi%d: won arbitration\n", HOSTNO);
+
+ /*
+ * Now that we have won arbitration, start Selection process, asserting
+ * the host and target ID's on the SCSI bus.
+ */
+
+ NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->target)));
+
+ /*
+ * Raise ATN while SEL is true before BSY goes false from arbitration,
+ * since this is the only way to guarantee that we'll get a MESSAGE OUT
+ * phase immediately after selection.
+ */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL ));
+ NCR5380_write(MODE_REG, MR_BASE);
+
+ /*
+ * Reselect interrupts must be turned off prior to the dropping of BSY,
+ * otherwise we will trigger an interrupt.
+ */
+
+ if (hostdata->connected) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ return -1;
+ }
+
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+
+ /*
+ * The initiator shall then wait at least two deskew delays and release
+ * the BSY signal.
+ */
+ udelay(1); /* wingel -- wait two bus deskew delay >2*45ns */
+
+ /* Reset BSY */
+ NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA |
+ ICR_ASSERT_ATN | ICR_ASSERT_SEL));
+
+ /*
+ * Something weird happens when we cease to drive BSY - looks
+ * like the board/chip is letting us do another read before the
+ * appropriate propagation delay has expired, and we're confusing
+ * a BSY signal from ourselves as the target's response to SELECTION.
+ *
+ * A small delay (the 'C++' frontend breaks the pipeline with an
+ * unnecessary jump, making it work on my 386-33/Trantor T128, the
+ * tighter 'C' code breaks and requires this) solves the problem -
+ * the 1 us delay is arbitrary, and only used because this delay will
+ * be the same on other platforms and since it works here, it should
+ * work there.
+ *
+ * wingel suggests that this could be due to failing to wait
+ * one deskew delay.
+ */
+
+ udelay(1);
+
+ SEL_PRINTK("scsi%d: selecting target %d\n", HOSTNO, cmd->target);
+
+ /*
+ * The SCSI specification calls for a 250 ms timeout for the actual
+ * selection.
+ */
+
+ timeout = jiffies + 25;
+
+ /*
+ * XXX very interesting - we're seeing a bounce where the BSY we
+ * asserted is being reflected / still asserted (propagation delay?)
+ * and it's detecting as true. Sigh.
+ */
+
+#if 0
+ /* ++roman: If a target conformed to the SCSI standard, it wouldn't assert
+ * IO while SEL is true. But again, there are some disks out the in the
+ * world that do that nevertheless. (Somebody claimed that this announces
+ * reselection capability of the target.) So we better skip that test and
+ * only wait for BSY... (Famous german words: Der Klügere gibt nach :-)
+ */
+
+ while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) &
+ (SR_BSY | SR_IO)));
+
+ if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) ==
+ (SR_SEL | SR_IO)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ NCR5380_reselect(instance);
+ printk (KERN_ERR "scsi%d: reselection after won arbitration?\n",
+ HOSTNO);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+#else
+ while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY));
+#endif
+
+ /*
+ * No less than two deskew delays after the initiator detects the
+ * BSY signal is true, it shall release the SEL signal and may
+ * change the DATA BUS. -wingel
+ */
+
+ udelay(1);
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+ if (!(NCR5380_read(STATUS_REG) & SR_BSY)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ if (hostdata->targets_present & (1 << cmd->target)) {
+ printk(KERN_ERR "scsi%d: weirdness\n", HOSTNO);
+ if (hostdata->restart_select)
+ printk(KERN_NOTICE "\trestart select\n");
+ NCR_PRINT(NDEBUG_ANY);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+ cmd->result = DID_BAD_TARGET << 16;
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( cmd );
+#endif
+ cmd->scsi_done(cmd);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ SEL_PRINTK("scsi%d: target did not respond within 250ms\n", HOSTNO);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return 0;
+ }
+
+ hostdata->targets_present |= (1 << cmd->target);
+
+ /*
+ * Since we followed the SCSI spec, and raised ATN while SEL
+ * was true but before BSY was false during selection, the information
+ * transfer phase should be a MESSAGE OUT phase so that we can send the
+ * IDENTIFY message.
+ *
+ * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG
+ * message (2 bytes) with a tag ID that we increment with every command
+ * until it wraps back to 0.
+ *
+ * XXX - it turns out that there are some broken SCSI-II devices,
+ * which claim to support tagged queuing but fail when more than
+ * some number of commands are issued at once.
+ */
+
+ /* Wait for start of REQ/ACK handshake */
+ while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+
+ SEL_PRINTK("scsi%d: target %d selected, going into MESSAGE OUT phase.\n",
+ HOSTNO, cmd->target);
+ tmp[0] = IDENTIFY(1, cmd->lun);
+
+#ifdef SUPPORT_TAGS
+ if (cmd->tag != TAG_NONE) {
+ tmp[1] = hostdata->last_message = SIMPLE_QUEUE_TAG;
+ tmp[2] = cmd->tag;
+ len = 3;
+ } else
+ len = 1;
+#else
+ len = 1;
+ cmd->tag=0;
+#endif /* SUPPORT_TAGS */
+
+ /* Send message(s) */
+ data = tmp;
+ phase = PHASE_MSGOUT;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ SEL_PRINTK("scsi%d: nexus established.\n", HOSTNO);
+ /* XXX need to handle errors here */
+ hostdata->connected = cmd;
+#ifndef SUPPORT_TAGS
+ hostdata->busy[cmd->target] |= (1 << cmd->lun);
+#endif
+
+ initialize_SCp(cmd);
+
+
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance,
+ * unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using polled I/O
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to
+ * what phase is expected, *count - pointer to number of
+ * bytes to transfer, **data - pointer to data pointer.
+ *
+ * Returns : -1 when different phase is entered without transferring
+ * maximum number of bytes, 0 if all bytes are transfered or exit
+ * is in same phase.
+ *
+ * Also, *phase, *count, *data are modified in place.
+ *
+ * XXX Note : handling for bus free may be useful.
+ */
+
+/*
+ * Note : this code is not as quick as it could be, however it
+ * IS 100% reliable, and for the actual data transfer where speed
+ * counts, we will always do a pseudo DMA or DMA transfer.
+ */
+
+static int NCR5380_transfer_pio( struct Scsi_Host *instance,
+ unsigned char *phase, int *count,
+ unsigned char **data)
+{
+ register unsigned char p = *phase, tmp;
+ register int c = *count;
+ register unsigned char *d = *data;
+
+ /*
+ * The NCR5380 chip will only drive the SCSI bus when the
+ * phase specified in the appropriate bits of the TARGET COMMAND
+ * REGISTER match the STATUS REGISTER
+ */
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
+
+ do {
+ /*
+ * Wait for assertion of REQ, after which the phase bits will be
+ * valid
+ */
+ while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ));
+
+ HSH_PRINTK("scsi%d: REQ detected\n", HOSTNO);
+
+ /* Check for phase mismatch */
+ if ((tmp & PHASE_MASK) != p) {
+ PIO_PRINTK("scsi%d: phase mismatch\n", HOSTNO);
+ NCR_PRINT_PHASE(NDEBUG_PIO);
+ break;
+ }
+
+ /* Do actual transfer from SCSI bus to / from memory */
+ if (!(p & SR_IO))
+ NCR5380_write(OUTPUT_DATA_REG, *d);
+ else
+ *d = NCR5380_read(CURRENT_SCSI_DATA_REG);
+
+ ++d;
+
+ /*
+ * The SCSI standard suggests that in MSGOUT phase, the initiator
+ * should drop ATN on the last byte of the message phase
+ * after REQ has been asserted for the handshake but before
+ * the initiator raises ACK.
+ */
+
+ if (!(p & SR_IO)) {
+ if (!((p & SR_MSG) && c > 1)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA);
+ NCR_PRINT(NDEBUG_PIO);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ACK);
+ } else {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN);
+ NCR_PRINT(NDEBUG_PIO);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
+ }
+ } else {
+ NCR_PRINT(NDEBUG_PIO);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
+ }
+
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+
+ HSH_PRINTK("scsi%d: req false, handshake complete\n", HOSTNO);
+
+/*
+ * We have several special cases to consider during REQ/ACK handshaking :
+ * 1. We were in MSGOUT phase, and we are on the last byte of the
+ * message. ATN must be dropped as ACK is dropped.
+ *
+ * 2. We are in a MSGIN phase, and we are on the last byte of the
+ * message. We must exit with ACK asserted, so that the calling
+ * code may raise ATN before dropping ACK to reject the message.
+ *
+ * 3. ACK and ATN are clear and the target may proceed as normal.
+ */
+ if (!(p == PHASE_MSGIN && c == 1)) {
+ if (p == PHASE_MSGOUT && c > 1)
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ else
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ }
+ } while (--c);
+
+ PIO_PRINTK("scsi%d: residual %d\n", HOSTNO, c);
+
+ *count = c;
+ *data = d;
+ tmp = NCR5380_read(STATUS_REG);
+ /* The phase read from the bus is valid if either REQ is (already)
+ * asserted or if ACK hasn't been released yet. The latter is the case if
+ * we're in MSGIN and all wanted bytes have been received. */
+ if ((tmp & SR_REQ) || (p == PHASE_MSGIN && c == 0))
+ *phase = tmp & PHASE_MASK;
+ else
+ *phase = PHASE_UNKNOWN;
+
+ if (!c || (*phase == p))
+ return 0;
+ else
+ return -1;
+}
+
+/*
+ * Function : do_abort (Scsi_Host *host)
+ *
+ * Purpose : abort the currently established nexus. Should only be
+ * called from a routine which can drop into a
+ *
+ * Returns : 0 on success, -1 on failure.
+ */
+
+static int do_abort (struct Scsi_Host *host)
+{
+ unsigned char tmp, *msgptr, phase;
+ int len;
+
+ /* Request message out phase */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+ /*
+ * Wait for the target to indicate a valid phase by asserting
+ * REQ. Once this happens, we'll have either a MSGOUT phase
+ * and can immediately send the ABORT message, or we'll have some
+ * other phase and will have to source/sink data.
+ *
+ * We really don't care what value was on the bus or what value
+ * the target sees, so we just handshake.
+ */
+
+ while (!(tmp = NCR5380_read(STATUS_REG)) & SR_REQ);
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+ if ((tmp & PHASE_MASK) != PHASE_MSGOUT) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
+ ICR_ASSERT_ACK);
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ }
+
+ tmp = ABORT;
+ msgptr = &tmp;
+ len = 1;
+ phase = PHASE_MSGOUT;
+ NCR5380_transfer_pio (host, &phase, &len, &msgptr);
+
+ /*
+ * If we got here, and the command completed successfully,
+ * we're about to go into bus free state.
+ */
+
+ return len ? -1 : 0;
+}
+
+#if defined(REAL_DMA)
+/*
+ * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance,
+ * unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using either real
+ * or pseudo DMA.
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to
+ * what phase is expected, *count - pointer to number of
+ * bytes to transfer, **data - pointer to data pointer.
+ *
+ * Returns : -1 when different phase is entered without transferring
+ * maximum number of bytes, 0 if all bytes or transfered or exit
+ * is in same phase.
+ *
+ * Also, *phase, *count, *data are modified in place.
+ *
+ */
+
+
+static int NCR5380_transfer_dma( struct Scsi_Host *instance,
+ unsigned char *phase, int *count,
+ unsigned char **data)
+{
+ SETUP_HOSTDATA(instance);
+ register int c = *count;
+ register unsigned char p = *phase;
+ unsigned long flags;
+
+ /* sanity check */
+ if(!sun3_dma_setup_done) {
+ printk("scsi%d: transfer_dma without setup!\n", HOSTNO);
+ machine_halt();
+ }
+
+ hostdata->dma_len = c;
+
+ DMA_PRINTK("scsi%d: initializing DMA for %s, %d bytes %s %p\n",
+ HOSTNO, (p & SR_IO) ? "reading" : "writing",
+ c, (p & SR_IO) ? "to" : "from", d);
+
+ /* netbsd turns off ints here, why not be safe and do it too */
+ save_flags(flags);
+ cli();
+
+ /* send start chain */
+ sun3_udc_write(UDC_CHN_START, UDC_CSR);
+
+ if (p & SR_IO) {
+ NCR5380_write(TARGET_COMMAND_REG, 1);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ NCR5380_write(INITIATOR_COMMAND_REG, 0);
+ NCR5380_write(MODE_REG, (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR));
+ NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
+ } else {
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_DATA);
+ NCR5380_write(MODE_REG, (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR));
+ NCR5380_write(START_DMA_SEND_REG, 0);
+ }
+
+ restore_flags(flags);
+
+ sun3_dma_active = 1;
+ return 0;
+}
+#endif /* defined(REAL_DMA) */
+
+/*
+ * Function : NCR5380_information_transfer (struct Scsi_Host *instance)
+ *
+ * Purpose : run through the various SCSI phases and do as the target
+ * directs us to. Operates on the currently connected command,
+ * instance->connected.
+ *
+ * Inputs : instance, instance for which we are doing commands
+ *
+ * Side effects : SCSI things happen, the disconnected queue will be
+ * modified if a command disconnects, *instance->connected will
+ * change.
+ *
+ * XXX Note : we need to watch for bus free or a reset condition here
+ * to recover from an unexpected bus free condition.
+ */
+
+static void NCR5380_information_transfer (struct Scsi_Host *instance)
+{
+ SETUP_HOSTDATA(instance);
+ unsigned long flags;
+ unsigned char msgout = NOP;
+ int sink = 0;
+ int len;
+#if defined(REAL_DMA)
+ int transfersize;
+#endif
+ unsigned char *data;
+ unsigned char phase, tmp, extended_msg[10], old_phase=0xff;
+ Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
+
+ while (1) {
+ tmp = NCR5380_read(STATUS_REG);
+ /* We only have a valid SCSI phase when REQ is asserted */
+ if (tmp & SR_REQ) {
+ phase = (tmp & PHASE_MASK);
+ if (phase != old_phase) {
+ old_phase = phase;
+ NCR_PRINT_PHASE(NDEBUG_INFORMATION);
+ }
+
+ if(phase == PHASE_CMDOUT) {
+ void *d;
+ unsigned long count;
+
+ if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+ count = cmd->SCp.buffer->length;
+ d = cmd->SCp.buffer->address;
+ } else {
+ count = cmd->SCp.this_residual;
+ d = cmd->SCp.ptr;
+ }
+#ifdef REAL_DMA
+ /* this command setup for dma yet? */
+ if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done
+ != cmd))
+ {
+ sun3scsi_dma_setup(d, count,
+ cmd->request.cmd);
+ sun3_dma_setup_done = cmd;
+ }
+#endif
+ }
+
+
+ if (sink && (phase != PHASE_MSGOUT)) {
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
+ ICR_ASSERT_ACK);
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
+ sink = 0;
+ continue;
+ }
+
+ switch (phase) {
+ case PHASE_DATAOUT:
+#if (NDEBUG & NDEBUG_NO_DATAOUT)
+ printk("scsi%d: NDEBUG_NO_DATAOUT set, attempted DATAOUT "
+ "aborted\n", HOSTNO);
+ sink = 1;
+ do_abort(instance);
+ cmd->result = DID_ERROR << 16;
+ cmd->done(cmd);
+ return;
+#endif
+ case PHASE_DATAIN:
+ /*
+ * If there is no room left in the current buffer in the
+ * scatter-gather list, move onto the next one.
+ */
+ if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+ ++cmd->SCp.buffer;
+ --cmd->SCp.buffers_residual;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr = cmd->SCp.buffer->address;
+
+ /* ++roman: Try to merge some scatter-buffers if
+ * they are at contiguous physical addresses.
+ */
+// merge_contiguous_buffers( cmd );
+ INF_PRINTK("scsi%d: %d bytes and %d buffers left\n",
+ HOSTNO, cmd->SCp.this_residual,
+ cmd->SCp.buffers_residual);
+ }
+
+ /*
+ * The preferred transfer method is going to be
+ * PSEUDO-DMA for systems that are strictly PIO,
+ * since we can let the hardware do the handshaking.
+ *
+ * For this to work, we need to know the transfersize
+ * ahead of time, since the pseudo-DMA code will sit
+ * in an unconditional loop.
+ */
+
+/* ++roman: I suggest, this should be
+ * #if def(REAL_DMA)
+ * instead of leaving REAL_DMA out.
+ */
+
+#if defined(REAL_DMA)
+// if (!cmd->device->borken &&
+ if((transfersize =
+ NCR5380_dma_xfer_len(instance,cmd,phase)) > SUN3_DMA_MINSIZE) {
+ len = transfersize;
+ cmd->SCp.phase = phase;
+
+ if (NCR5380_transfer_dma(instance, &phase,
+ &len, (unsigned char **) &cmd->SCp.ptr)) {
+ /*
+ * If the watchdog timer fires, all future
+ * accesses to this device will use the
+ * polled-IO. */
+ printk(KERN_NOTICE "scsi%d: switching target %d "
+ "lun %d to slow handshake\n", HOSTNO,
+ cmd->target, cmd->lun);
+ cmd->device->borken = 1;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
+ sink = 1;
+ do_abort(instance);
+ cmd->result = DID_ERROR << 16;
+ cmd->done(cmd);
+ /* XXX - need to source or sink data here, as appropriate */
+ } else {
+#ifdef REAL_DMA
+ /* ++roman: When using real DMA,
+ * information_transfer() should return after
+ * starting DMA since it has nothing more to
+ * do.
+ */
+ return;
+#else
+ cmd->SCp.this_residual -= transfersize - len;
+#endif
+ }
+ } else
+#endif /* defined(REAL_DMA) */
+ NCR5380_transfer_pio(instance, &phase,
+ (int *) &cmd->SCp.this_residual, (unsigned char **)
+ &cmd->SCp.ptr);
+#ifdef REAL_DMA
+ /* if we had intended to dma that command clear it */
+ if(sun3_dma_setup_done == cmd)
+ sun3_dma_setup_done = NULL;
+#endif
+
+ break;
+ case PHASE_MSGIN:
+ len = 1;
+ data = &tmp;
+ NCR5380_write(SELECT_ENABLE_REG, 0); /* disable reselects */
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ cmd->SCp.Message = tmp;
+
+ switch (tmp) {
+ /*
+ * Linking lets us reduce the time required to get the
+ * next command out to the device, hopefully this will
+ * mean we don't waste another revolution due to the delays
+ * required by ARBITRATION and another SELECTION.
+ *
+ * In the current implementation proposal, low level drivers
+ * merely have to start the next command, pointed to by
+ * next_link, done() is called as with unlinked commands.
+ */
+#ifdef LINKED
+ case LINKED_CMD_COMPLETE:
+ case LINKED_FLG_CMD_COMPLETE:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ LNK_PRINTK("scsi%d: target %d lun %d linked command "
+ "complete.\n", HOSTNO, cmd->target, cmd->lun);
+
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ /*
+ * Sanity check : A linked command should only terminate
+ * with one of these messages if there are more linked
+ * commands available.
+ */
+
+ if (!cmd->next_link) {
+ printk(KERN_NOTICE "scsi%d: target %d lun %d "
+ "linked command complete, no next_link\n",
+ HOSTNO, cmd->target, cmd->lun);
+ sink = 1;
+ do_abort (instance);
+ return;
+ }
+
+ initialize_SCp(cmd->next_link);
+ /* The next command is still part of this process; copy it
+ * and don't free it! */
+ cmd->next_link->tag = cmd->tag;
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ LNK_PRINTK("scsi%d: target %d lun %d linked request "
+ "done, calling scsi_done().\n",
+ HOSTNO, cmd->target, cmd->lun);
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+ cmd->scsi_done(cmd);
+ cmd = hostdata->connected;
+ break;
+#endif /* def LINKED */
+ case ABORT:
+ case COMMAND_COMPLETE:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ hostdata->connected = NULL;
+ QU_PRINTK("scsi%d: command for target %d, lun %d "
+ "completed\n", HOSTNO, cmd->target, cmd->lun);
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( cmd );
+ if (status_byte(cmd->SCp.Status) == QUEUE_FULL) {
+ /* Turn a QUEUE FULL status into BUSY, I think the
+ * mid level cannot handle QUEUE FULL :-( (The
+ * command is retried after BUSY). Also update our
+ * queue size to the number of currently issued
+ * commands now.
+ */
+ /* ++Andreas: the mid level code knows about
+ QUEUE_FULL now. */
+ TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun];
+ TAG_PRINTK("scsi%d: target %d lun %d returned "
+ "QUEUE_FULL after %d commands\n",
+ HOSTNO, cmd->target, cmd->lun,
+ ta->nr_allocated);
+ if (ta->queue_size > ta->nr_allocated)
+ ta->nr_allocated = ta->queue_size;
+ }
+#else
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+#endif
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+
+ /*
+ * I'm not sure what the correct thing to do here is :
+ *
+ * If the command that just executed is NOT a request
+ * sense, the obvious thing to do is to set the result
+ * code to the values of the stored parameters.
+ *
+ * If it was a REQUEST SENSE command, we need some way to
+ * differentiate between the failure code of the original
+ * and the failure code of the REQUEST sense - the obvious
+ * case is success, where we fall through and leave the
+ * result code unchanged.
+ *
+ * The non-obvious place is where the REQUEST SENSE failed
+ */
+
+ if (cmd->cmnd[0] != REQUEST_SENSE)
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ else if (status_byte(cmd->SCp.Status) != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+
+#ifdef AUTOSENSE
+ if ((cmd->cmnd[0] != REQUEST_SENSE) &&
+ (status_byte(cmd->SCp.Status) == CHECK_CONDITION)) {
+ ASEN_PRINTK("scsi%d: performing request sense\n",
+ HOSTNO);
+ cmd->cmnd[0] = REQUEST_SENSE;
+ cmd->cmnd[1] &= 0xe0;
+ cmd->cmnd[2] = 0;
+ cmd->cmnd[3] = 0;
+ cmd->cmnd[4] = sizeof(cmd->sense_buffer);
+ cmd->cmnd[5] = 0;
+ cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
+
+ cmd->use_sg = 0;
+ /* this is initialized from initialize_SCp
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ */
+ cmd->request_buffer = (char *) cmd->sense_buffer;
+ cmd->request_bufflen = sizeof(cmd->sense_buffer);
+
+ save_flags(flags);
+ cli();
+ LIST(cmd,hostdata->issue_queue);
+ NEXT(cmd) = hostdata->issue_queue;
+ hostdata->issue_queue = (Scsi_Cmnd *) cmd;
+ restore_flags(flags);
+ QU_PRINTK("scsi%d: REQUEST SENSE added to head of "
+ "issue queue\n", H_NO(cmd));
+ } else
+#endif /* def AUTOSENSE */
+ {
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+ cmd->scsi_done(cmd);
+ }
+
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ /*
+ * Restore phase bits to 0 so an interrupted selection,
+ * arbitration can resume.
+ */
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+ barrier();
+
+ return;
+ case MESSAGE_REJECT:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ switch (hostdata->last_message) {
+ case HEAD_OF_QUEUE_TAG:
+ case ORDERED_QUEUE_TAG:
+ case SIMPLE_QUEUE_TAG:
+ /* The target obviously doesn't support tagged
+ * queuing, even though it announced this ability in
+ * its INQUIRY data ?!? (maybe only this LUN?) Ok,
+ * clear 'tagged_supported' and lock the LUN, since
+ * the command is treated as untagged further on.
+ */
+ cmd->device->tagged_supported = 0;
+ hostdata->busy[cmd->target] |= (1 << cmd->lun);
+ cmd->tag = TAG_NONE;
+ TAG_PRINTK("scsi%d: target %d lun %d rejected "
+ "QUEUE_TAG message; tagged queuing "
+ "disabled\n",
+ HOSTNO, cmd->target, cmd->lun);
+ break;
+ }
+ break;
+ case DISCONNECT:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ save_flags(flags);
+ cli();
+ cmd->device->disconnect = 1;
+ LIST(cmd,hostdata->disconnected_queue);
+ NEXT(cmd) = hostdata->disconnected_queue;
+ hostdata->connected = NULL;
+ hostdata->disconnected_queue = cmd;
+ restore_flags(flags);
+ QU_PRINTK("scsi%d: command for target %d lun %d was "
+ "moved from connected to the "
+ "disconnected_queue\n", HOSTNO,
+ cmd->target, cmd->lun);
+ /*
+ * Restore phase bits to 0 so an interrupted selection,
+ * arbitration can resume.
+ */
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ /* Wait for bus free to avoid nasty timeouts */
+ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+ barrier();
+ return;
+ /*
+ * The SCSI data pointer is *IMPLICITLY* saved on a disconnect
+ * operation, in violation of the SCSI spec so we can safely
+ * ignore SAVE/RESTORE pointers calls.
+ *
+ * Unfortunately, some disks violate the SCSI spec and
+ * don't issue the required SAVE_POINTERS message before
+ * disconnecting, and we have to break spec to remain
+ * compatible.
+ */
+ case SAVE_POINTERS:
+ case RESTORE_POINTERS:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ break;
+ case EXTENDED_MESSAGE:
+/*
+ * Extended messages are sent in the following format :
+ * Byte
+ * 0 EXTENDED_MESSAGE == 1
+ * 1 length (includes one byte for code, doesn't
+ * include first two bytes)
+ * 2 code
+ * 3..length+1 arguments
+ *
+ * Start the extended message buffer with the EXTENDED_MESSAGE
+ * byte, since print_msg() wants the whole thing.
+ */
+ extended_msg[0] = EXTENDED_MESSAGE;
+ /* Accept first byte by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ EXT_PRINTK("scsi%d: receiving extended message\n", HOSTNO);
+
+ len = 2;
+ data = extended_msg + 1;
+ phase = PHASE_MSGIN;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ EXT_PRINTK("scsi%d: length=%d, code=0x%02x\n", HOSTNO,
+ (int)extended_msg[1], (int)extended_msg[2]);
+
+ if (!len && extended_msg[1] <=
+ (sizeof (extended_msg) - 1)) {
+ /* Accept third byte by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ len = extended_msg[1] - 1;
+ data = extended_msg + 3;
+ phase = PHASE_MSGIN;
+
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ EXT_PRINTK("scsi%d: message received, residual %d\n",
+ HOSTNO, len);
+
+ switch (extended_msg[2]) {
+ case EXTENDED_SDTR:
+ case EXTENDED_WDTR:
+ case EXTENDED_MODIFY_DATA_POINTER:
+ case EXTENDED_EXTENDED_IDENTIFY:
+ tmp = 0;
+ }
+ } else if (len) {
+ printk(KERN_NOTICE "scsi%d: error receiving "
+ "extended message\n", HOSTNO);
+ tmp = 0;
+ } else {
+ printk(KERN_NOTICE "scsi%d: extended message "
+ "code %02x length %d is too long\n",
+ HOSTNO, extended_msg[2], extended_msg[1]);
+ tmp = 0;
+ }
+ /* Fall through to reject message */
+
+ /*
+ * If we get something weird that we aren't expecting,
+ * reject it.
+ */
+ default:
+ if (!tmp) {
+ printk(KERN_DEBUG "scsi%d: rejecting message ", HOSTNO);
+ print_msg (extended_msg);
+ printk("\n");
+ } else if (tmp != EXTENDED_MESSAGE)
+ printk(KERN_DEBUG "scsi%d: rejecting unknown "
+ "message %02x from target %d, lun %d\n",
+ HOSTNO, tmp, cmd->target, cmd->lun);
+ else
+ printk(KERN_DEBUG "scsi%d: rejecting unknown "
+ "extended message "
+ "code %02x, length %d from target %d, lun %d\n",
+ HOSTNO, extended_msg[1], extended_msg[0],
+ cmd->target, cmd->lun);
+
+
+ msgout = MESSAGE_REJECT;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
+ break;
+ } /* switch (tmp) */
+ break;
+ case PHASE_MSGOUT:
+ len = 1;
+ data = &msgout;
+ hostdata->last_message = msgout;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ if (msgout == ABORT) {
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( cmd );
+#else
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+#endif
+ hostdata->connected = NULL;
+ cmd->result = DID_ERROR << 16;
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+ cmd->scsi_done(cmd);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return;
+ }
+ msgout = NOP;
+ break;
+ case PHASE_CMDOUT:
+ len = cmd->cmd_len;
+ data = cmd->cmnd;
+ /*
+ * XXX for performance reasons, on machines with a
+ * PSEUDO-DMA architecture we should probably
+ * use the dma transfer function.
+ */
+ NCR5380_transfer_pio(instance, &phase, &len,
+ &data);
+ break;
+ case PHASE_STATIN:
+ len = 1;
+ data = &tmp;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ cmd->SCp.Status = tmp;
+ break;
+ default:
+ printk("scsi%d: unknown phase\n", HOSTNO);
+ NCR_PRINT(NDEBUG_ANY);
+ } /* switch(phase) */
+ } /* if (tmp * SR_REQ) */
+ } /* while (1) */
+}
+
+/*
+ * Function : void NCR5380_reselect (struct Scsi_Host *instance)
+ *
+ * Purpose : does reselection, initializing the instance->connected
+ * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q
+ * nexus has been reestablished,
+ *
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ */
+
+/* it might eventually prove necessary to do a dma setup on
+ reselection, but it doesn't seem to be needed now -- sam */
+
+static void NCR5380_reselect (struct Scsi_Host *instance)
+{
+ SETUP_HOSTDATA(instance);
+ unsigned char target_mask;
+ unsigned char lun;
+#ifdef SUPPORT_TAGS
+ unsigned char tag;
+#endif
+ unsigned char msg[3];
+ Scsi_Cmnd *tmp = NULL, *prev;
+/* unsigned long flags; */
+
+ /*
+ * Disable arbitration, etc. since the host adapter obviously
+ * lost, and tell an interrupted NCR5380_select() to restart.
+ */
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ hostdata->restart_select = 1;
+
+ target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask);
+
+ RSL_PRINTK("scsi%d: reselect\n", HOSTNO);
+
+ /*
+ * At this point, we have detected that our SCSI ID is on the bus,
+ * SEL is true and BSY was false for at least one bus settle delay
+ * (400 ns).
+ *
+ * We must assert BSY ourselves, until the target drops the SEL
+ * signal.
+ */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY);
+
+ while (NCR5380_read(STATUS_REG) & SR_SEL);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ /*
+ * Wait for target to go into MSGIN.
+ */
+
+ while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+
+#if 1
+ // acknowledge toggle to MSGIN
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(PHASE_MSGIN));
+
+ // peek at the byte without really hitting the bus
+ msg[0] = NCR5380_read(CURRENT_SCSI_DATA_REG);
+#endif
+
+ if (!msg[0] & 0x80) {
+ printk(KERN_DEBUG "scsi%d: expecting IDENTIFY message, got ", HOSTNO);
+ print_msg(msg);
+ do_abort(instance);
+ return;
+ }
+ lun = (msg[0] & 0x07);
+
+ /*
+ * Find the command corresponding to the I_T_L or I_T_L_Q nexus we
+ * just reestablished, and remove it from the disconnected queue.
+ */
+
+ for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL;
+ tmp; prev = tmp, tmp = NEXT(tmp) ) {
+ if ((target_mask == (1 << tmp->target)) && (lun == tmp->lun)
+#ifdef SUPPORT_TAGS
+ && (tag == tmp->tag)
+#endif
+ ) {
+ if (prev) {
+ REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
+ NEXT(prev) = NEXT(tmp);
+ } else {
+ REMOVE(-1, hostdata->disconnected_queue, tmp, NEXT(tmp));
+ hostdata->disconnected_queue = NEXT(tmp);
+ }
+ NEXT(tmp) = NULL;
+ break;
+ }
+ }
+
+ if (!tmp) {
+ printk(KERN_WARNING "scsi%d: warning: target bitmask %02x lun %d "
+#ifdef SUPPORT_TAGS
+ "tag %d "
+#endif
+ "not in disconnected_queue.\n",
+ HOSTNO, target_mask, lun
+#ifdef SUPPORT_TAGS
+ , tag
+#endif
+ );
+ /*
+ * Since we have an established nexus that we can't do anything
+ * with, we must abort it.
+ */
+ do_abort(instance);
+ return;
+ }
+#if 1
+ /* engage dma setup for the command we just saw */
+ {
+ void *d;
+ unsigned long count;
+
+ if (!tmp->SCp.this_residual && tmp->SCp.buffers_residual) {
+ count = tmp->SCp.buffer->length;
+ d = tmp->SCp.buffer->address;
+ } else {
+ count = tmp->SCp.this_residual;
+ d = tmp->SCp.ptr;
+ }
+#ifdef REAL_DMA
+ /* setup this command for dma if not already */
+ if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done
+ != tmp))
+ {
+ sun3scsi_dma_setup(d, count,
+ tmp->request.cmd);
+ sun3_dma_setup_done = tmp;
+ }
+#endif
+ }
+#endif
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+#ifdef SUPPORT_TAGS
+ /* If the phase is still MSGIN, the target wants to send some more
+ * messages. In case it supports tagged queuing, this is probably a
+ * SIMPLE_QUEUE_TAG for the I_T_L_Q nexus.
+ */
+ tag = TAG_NONE;
+ if (phase == PHASE_MSGIN && setup_use_tagged_queuing) {
+ /* Accept previous IDENTIFY message by clearing ACK */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
+ len = 2;
+ data = msg+1;
+ if (!NCR5380_transfer_pio(instance, &phase, &len, &data) &&
+ msg[1] == SIMPLE_QUEUE_TAG)
+ tag = msg[2];
+ TAG_PRINTK("scsi%d: target mask %02x, lun %d sent tag %d at "
+ "reselection\n", HOSTNO, target_mask, lun, tag);
+ }
+#endif
+
+ hostdata->connected = tmp;
+ RSL_PRINTK("scsi%d: nexus established, target = %d, lun = %d, tag = %d\n",
+ HOSTNO, tmp->target, tmp->lun, tmp->tag);
+}
+
+
+/*
+ * Function : int NCR5380_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : abort a command
+ *
+ * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
+ * host byte of the result field to, if zero DID_ABORTED is
+ * used.
+ *
+ * Returns : 0 - success, -1 on failure.
+ *
+ * XXX - there is no way to abort the command that is currently
+ * connected, you have to wait for it to complete. If this is
+ * a problem, we could implement longjmp() / setjmp(), setjmp()
+ * called where the loop started in NCR5380_main().
+ */
+
+#ifndef NCR5380_abort
+static
+#endif
+int NCR5380_abort (Scsi_Cmnd *cmd)
+{
+ struct Scsi_Host *instance = cmd->host;
+ SETUP_HOSTDATA(instance);
+ Scsi_Cmnd *tmp, **prev;
+ unsigned long flags;
+
+ printk(KERN_NOTICE "scsi%d: aborting command\n", HOSTNO);
+ print_Scsi_Cmnd (cmd);
+
+ NCR5380_print_status (instance);
+
+ save_flags(flags);
+ cli();
+
+ ABRT_PRINTK("scsi%d: abort called basr 0x%02x, sr 0x%02x\n", HOSTNO,
+ NCR5380_read(BUS_AND_STATUS_REG),
+ NCR5380_read(STATUS_REG));
+
+#if 1
+/*
+ * Case 1 : If the command is the currently executing command,
+ * we'll set the aborted flag and return control so that
+ * information transfer routine can exit cleanly.
+ */
+
+ if (hostdata->connected == cmd) {
+
+ ABRT_PRINTK("scsi%d: aborting connected command\n", HOSTNO);
+/*
+ * We should perform BSY checking, and make sure we haven't slipped
+ * into BUS FREE.
+ */
+
+/* NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN); */
+/*
+ * Since we can't change phases until we've completed the current
+ * handshake, we have to source or sink a byte of data if the current
+ * phase is not MSGOUT.
+ */
+
+/*
+ * Return control to the executing NCR drive so we can clear the
+ * aborted flag and get back into our main loop.
+ */
+
+ if (do_abort(instance) == 0) {
+ hostdata->aborted = 1;
+ hostdata->connected = NULL;
+ cmd->result = DID_ABORT << 16;
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( cmd );
+#else
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+#endif
+ restore_flags(flags);
+ cmd->scsi_done(cmd);
+ return SCSI_ABORT_SUCCESS;
+ } else {
+/* restore_flags(flags); */
+ printk("scsi%d: abort of connected command failed!\n", HOSTNO);
+ return SCSI_ABORT_ERROR;
+ }
+ }
+#endif
+
+/*
+ * Case 2 : If the command hasn't been issued yet, we simply remove it
+ * from the issue queue.
+ */
+ for (prev = (Scsi_Cmnd **) &(hostdata->issue_queue),
+ tmp = (Scsi_Cmnd *) hostdata->issue_queue;
+ tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) )
+ if (cmd == tmp) {
+ REMOVE(5, *prev, tmp, NEXT(tmp));
+ (*prev) = NEXT(tmp);
+ NEXT(tmp) = NULL;
+ tmp->result = DID_ABORT << 16;
+ restore_flags(flags);
+ ABRT_PRINTK("scsi%d: abort removed command from issue queue.\n",
+ HOSTNO);
+ /* Tagged queuing note: no tag to free here, hasn't been assigned
+ * yet... */
+ tmp->scsi_done(tmp);
+ return SCSI_ABORT_SUCCESS;
+ }
+
+/*
+ * Case 3 : If any commands are connected, we're going to fail the abort
+ * and let the high level SCSI driver retry at a later time or
+ * issue a reset.
+ *
+ * Timeouts, and therefore aborted commands, will be highly unlikely
+ * and handling them cleanly in this situation would make the common
+ * case of noresets less efficient, and would pollute our code. So,
+ * we fail.
+ */
+
+ if (hostdata->connected) {
+ restore_flags(flags);
+ ABRT_PRINTK("scsi%d: abort failed, command connected.\n", HOSTNO);
+ return SCSI_ABORT_SNOOZE;
+ }
+
+/*
+ * Case 4: If the command is currently disconnected from the bus, and
+ * there are no connected commands, we reconnect the I_T_L or
+ * I_T_L_Q nexus associated with it, go into message out, and send
+ * an abort message.
+ *
+ * This case is especially ugly. In order to reestablish the nexus, we
+ * need to call NCR5380_select(). The easiest way to implement this
+ * function was to abort if the bus was busy, and let the interrupt
+ * handler triggered on the SEL for reselect take care of lost arbitrations
+ * where necessary, meaning interrupts need to be enabled.
+ *
+ * When interrupts are enabled, the queues may change - so we
+ * can't remove it from the disconnected queue before selecting it
+ * because that could cause a failure in hashing the nexus if that
+ * device reselected.
+ *
+ * Since the queues may change, we can't use the pointers from when we
+ * first locate it.
+ *
+ * So, we must first locate the command, and if NCR5380_select()
+ * succeeds, then issue the abort, relocate the command and remove
+ * it from the disconnected queue.
+ */
+
+ for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp;
+ tmp = NEXT(tmp))
+ if (cmd == tmp) {
+ restore_flags(flags);
+ ABRT_PRINTK("scsi%d: aborting disconnected command.\n", HOSTNO);
+
+ if (NCR5380_select (instance, cmd, (int) cmd->tag))
+ return SCSI_ABORT_BUSY;
+
+ ABRT_PRINTK("scsi%d: nexus reestablished.\n", HOSTNO);
+
+ do_abort (instance);
+
+ save_flags(flags);
+ cli();
+ for (prev = (Scsi_Cmnd **) &(hostdata->disconnected_queue),
+ tmp = (Scsi_Cmnd *) hostdata->disconnected_queue;
+ tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) )
+ if (cmd == tmp) {
+ REMOVE(5, *prev, tmp, NEXT(tmp));
+ *prev = NEXT(tmp);
+ NEXT(tmp) = NULL;
+ tmp->result = DID_ABORT << 16;
+ /* We must unlock the tag/LUN immediately here, since the
+ * target goes to BUS FREE and doesn't send us another
+ * message (COMMAND_COMPLETE or the like)
+ */
+#ifdef SUPPORT_TAGS
+ cmd_free_tag( tmp );
+#else
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+#endif
+ restore_flags(flags);
+ tmp->scsi_done(tmp);
+ return SCSI_ABORT_SUCCESS;
+ }
+ }
+
+/*
+ * Case 5 : If we reached this point, the command was not found in any of
+ * the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case something really
+ * broke.
+ */
+
+ restore_flags(flags);
+ printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully\n"
+ KERN_INFO " before abortion\n", HOSTNO);
+
+ return SCSI_ABORT_NOT_RUNNING;
+}
+
+
+/*
+ * Function : int NCR5380_reset (Scsi_Cmnd *cmd, unsigned int reset_flags)
+ *
+ * Purpose : reset the SCSI bus.
+ *
+ * Returns : SCSI_RESET_WAKEUP
+ *
+ */
+
+int NCR5380_reset( Scsi_Cmnd *cmd, unsigned int reset_flags)
+{
+ SETUP_HOSTDATA(cmd->host);
+ int i;
+ unsigned long flags;
+#if 1
+ Scsi_Cmnd *connected, *disconnected_queue;
+#endif
+
+
+ NCR5380_print_status (cmd->host);
+
+ /* get in phase */
+ NCR5380_write( TARGET_COMMAND_REG,
+ PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) ));
+ /* assert RST */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST );
+ udelay (40);
+ /* reset NCR registers */
+ NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
+ NCR5380_write( MODE_REG, MR_BASE );
+ NCR5380_write( TARGET_COMMAND_REG, 0 );
+ NCR5380_write( SELECT_ENABLE_REG, 0 );
+ /* ++roman: reset interrupt condition! otherwise no interrupts don't get
+ * through anymore ... */
+ (void)NCR5380_read( RESET_PARITY_INTERRUPT_REG );
+
+#if 1 /* XXX Should now be done by midlevel code, but it's broken XXX */
+ /* XXX see below XXX */
+
+ /* MSch: old-style reset: actually abort all command processing here */
+
+ /* After the reset, there are no more connected or disconnected commands
+ * and no busy units; to avoid problems with re-inserting the commands
+ * into the issue_queue (via scsi_done()), the aborted commands are
+ * remembered in local variables first.
+ */
+ save_flags(flags);
+ cli();
+ connected = (Scsi_Cmnd *)hostdata->connected;
+ hostdata->connected = NULL;
+ disconnected_queue = (Scsi_Cmnd *)hostdata->disconnected_queue;
+ hostdata->disconnected_queue = NULL;
+#ifdef SUPPORT_TAGS
+ free_all_tags();
+#endif
+ for( i = 0; i < 8; ++i )
+ hostdata->busy[i] = 0;
+#ifdef REAL_DMA
+ hostdata->dma_len = 0;
+#endif
+ restore_flags(flags);
+
+ /* In order to tell the mid-level code which commands were aborted,
+ * set the command status to DID_RESET and call scsi_done() !!!
+ * This ultimately aborts processing of these commands in the mid-level.
+ */
+
+ if ((cmd = connected)) {
+ ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd));
+ cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
+ cmd->scsi_done( cmd );
+ }
+
+ for (i = 0; (cmd = disconnected_queue); ++i) {
+ disconnected_queue = NEXT(cmd);
+ NEXT(cmd) = NULL;
+ cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
+ cmd->scsi_done( cmd );
+ }
+ if (i > 0)
+ ABRT_PRINTK("scsi: reset aborted %d disconnected command(s)\n", i);
+
+
+ /* since all commands have been explicitly terminated, we need to tell
+ * the midlevel code that the reset was SUCCESSFUL, and there is no
+ * need to 'wake up' the commands by a request_sense
+ */
+ return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
+#else /* 1 */
+
+ /* MSch: new-style reset handling: let the mid-level do what it can */
+
+ /* ++guenther: MID-LEVEL IS STILL BROKEN.
+ * Mid-level is supposed to requeue all commands that were active on the
+ * various low-level queues. In fact it does this, but that's not enough
+ * because all these commands are subject to timeout. And if a timeout
+ * happens for any removed command, *_abort() is called but all queues
+ * are now empty. Abort then gives up the falcon lock, which is fatal,
+ * since the mid-level will queue more commands and must have the lock
+ * (it's all happening inside timer interrupt handler!!).
+ * Even worse, abort will return NOT_RUNNING for all those commands not
+ * on any queue, so they won't be retried ...
+ *
+ * Conclusion: either scsi.c disables timeout for all resetted commands
+ * immediately, or we loose! As of linux-2.0.20 it doesn't.
+ */
+
+ /* After the reset, there are no more connected or disconnected commands
+ * and no busy units; so clear the low-level status here to avoid
+ * conflicts when the mid-level code tries to wake up the affected
+ * commands!
+ */
+
+ if (hostdata->issue_queue)
+ ABRT_PRINTK("scsi%d: reset aborted issued command(s)\n", H_NO(cmd));
+ if (hostdata->connected)
+ ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd));
+ if (hostdata->disconnected_queue)
+ ABRT_PRINTK("scsi%d: reset aborted disconnected command(s)\n", H_NO(cmd));
+
+ save_flags(flags);
+ cli();
+ hostdata->issue_queue = NULL;
+ hostdata->connected = NULL;
+ hostdata->disconnected_queue = NULL;
+#ifdef SUPPORT_TAGS
+ free_all_tags();
+#endif
+ for( i = 0; i < 8; ++i )
+ hostdata->busy[i] = 0;
+#ifdef REAL_DMA
+ hostdata->dma_len = 0;
+#endif
+ restore_flags(flags);
+
+ /* we did no complete reset of all commands, so a wakeup is required */
+ return SCSI_RESET_WAKEUP | SCSI_RESET_BUS_RESET;
+#endif /* 1 */
+}
+
+/* Local Variables: */
+/* tab-width: 8 */
+/* End: */
/*
* Sun3 SCSI stuff by Erik Verbruggen (erik@bigmama.xtdnet.nl)
*
+ * Sun3 DMA routines added by Sam Creasey (sammy@oh.verio.com)
+ *
* Adapted from mac_scsinew.c:
*/
/*
*/
/*
- * $Log: mac_NCR5380.c,v $
+ * $Log: sun3_NCR5380.c,v $
*/
#define AUTOSENSE
-#if 0
-#define PSEUDO_DMA
-#endif
#include <linux/types.h>
#include <linux/stddef.h>
#include <asm/system.h>
#include <asm/sun3ints.h>
+#include <asm/dvma.h>
+/* dma on! */
+#define REAL_DMA
#include "scsi.h"
#include "hosts.h"
#include "NCR5380.h"
#include "constants.h"
-#if 0
-#define NDEBUG (NDEBUG_INTR | NDEBUG_PSEUDO_DMA | NDEBUG_ARBITRATION | NDEBUG_SELECTION | NDEBUG_RESELECTION)
-#define NCR_TIMEOUT 100
-#else
-#define NDEBUG (NDEBUG_ABORT)
-#endif
-
#define USE_WRAPPER
#define RESET_BOOT
#define DRIVER_SETUP
+#define NDEBUG 0
+
/*
* BUG can be used to trigger a strange code-size related hang on 2.1 kernels
*/
#undef DRIVER_SETUP
#endif
-#define ENABLE_IRQ() sun3_enable_irq( IRQ_SUN3_SCSI );
-#define DISABLE_IRQ() sun3_enable_irq( IRQ_SUN3_SCSI );
+#undef SUPPORT_TAGS
+
+#define ENABLE_IRQ() enable_irq( IRQ_SUN3_SCSI );
-/* extern void via_scsi_clear(void); */
static void scsi_sun3_intr(int irq, void *dummy, struct pt_regs *fp);
-static char sun3scsi_read(struct Scsi_Host *instance, int reg);
-static void sun3scsi_write(struct Scsi_Host *instance, int reg, int value);
+static inline unsigned char sun3scsi_read(int reg);
+static inline void sun3scsi_write(int reg, int value);
static int setup_can_queue = -1;
static int setup_cmd_per_lun = -1;
#endif
static int setup_hostid = -1;
-static int polled_scsi_on = 0;
+static Scsi_Cmnd *sun3_dma_setup_done = NULL;
#define AFTER_RESET_DELAY (HZ/2)
-static volatile unsigned char *sun3_scsi_regp = IOBASE_SUN3_SCSI;
-/*
-static volatile unsigned char *sun3_scsi_drq = NULL;
-static volatile unsigned char *sun3_scsi_nodrq = NULL;
-*/
+/* ms to wait after hitting dma regs */
+#define SUN3_DMA_DELAY 5
+
+/* dvma buffer to allocate -- 32k should hopefully be more than sufficient */
+#define SUN3_DVMA_BUFSIZE 0xe000
+
+/* minimum number of bytes to to dma on */
+#define SUN3_DMA_MINSIZE 128
+
+static struct proc_dir_entry proc_scsi_sun3_5380 = {
+ PROC_SCSI_MAC, 13, "Sun3 5380 SCSI", S_IFDIR | S_IRUGO, S_IXUGO, 2
+};
+
+static volatile unsigned char *sun3_scsi_regp;
+static volatile struct sun3_dma_regs *dregs;
+static unsigned char *dmabuf = NULL; /* dma memory buffer */
+static struct sun3_udc_regs *udc_regs = NULL;
+static void *sun3_dma_orig_addr = NULL;
+static unsigned long sun3_dma_orig_count = 0;
+static int sun3_dma_active = 0;
/*
- * Function : sun3_scsi_setup(char *str, int *ints)
- *
- * Purpose : booter command line initialization of the overrides array,
- *
- * Inputs : str - unused, ints - array of integer parameters with ints[0]
- * equal to the number of ints.
- *
- * TODO: make it actually work!
- *
+ * NCR 5380 register access functions
*/
-void sun3_scsi_setup(char *str, int *ints) {
- printk("sun3_scsi_setup() called\n");
- setup_can_queue = -1;
- setup_cmd_per_lun = -1;
- setup_sg_tablesize = -1;
- setup_hostid = -1;
-#ifdef SUPPORT_TAGS
- setup_use_tagged_queuing = -1;
-#endif
- printk("sun3_scsi_setup() done\n");
+static inline unsigned char sun3scsi_read(int reg)
+{
+ return( sun3_scsi_regp[reg] );
+}
+
+static inline void sun3scsi_write(int reg, int value)
+{
+ sun3_scsi_regp[reg] = value;
+}
+
+/* dma controller register access functions */
+
+static inline unsigned short sun3_udc_read(unsigned char reg)
+{
+ unsigned short ret;
+
+ dregs->udc_addr = UDC_CSR;
+ udelay(SUN3_DMA_DELAY);
+ ret = dregs->udc_data;
+ udelay(SUN3_DMA_DELAY);
+
+ return ret;
+}
+
+static inline void sun3_udc_write(unsigned short val, unsigned char reg)
+{
+ dregs->udc_addr = reg;
+ udelay(SUN3_DMA_DELAY);
+ dregs->udc_data = val;
+ udelay(SUN3_DMA_DELAY);
}
/*
int sun3scsi_detect(Scsi_Host_Template * tpnt)
{
unsigned long ioaddr, iopte;
- unsigned short *ioptr;
int count = 0;
static int called = 0;
struct Scsi_Host *instance;
if(called)
return 0;
-printk("sun3scsi_detect(0x%p)\n",tpnt);
-
- tpnt->proc_name = "Sun3 5380 SCSI"; /* Could you spell "ewww..."? */
+ tpnt->proc_dir = &proc_scsi_sun3_5380;
/* setup variables */
tpnt->can_queue =
if(((iopte & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT) ==
IOBASE_SUN3_SCSI) {
count = 1;
-printk("Found ioaddr in pmeg\n");
break;
}
}
return 0;
}
- sun3_scsi_regp = ioaddr;
-
- /* doing some stuff like resetting DVMA: */
- ioptr = ioaddr;
- *(ioptr+8) = 0;
- udelay(10);
- *(ioptr+9) = 0;
- udelay(10);
- *(ioptr+12) = 0;
- udelay(10);
- *(ioptr+12) = 0x7;
- udelay(10);
- printk("SCSI status reg = %x\n", *(ioptr+12));
- udelay(10);
- *(ioptr+13) = 0;
- udelay(10);
+ sun3_scsi_regp = (unsigned char *)ioaddr;
+ dregs = (struct sun3_dma_regs *)(((unsigned char *)ioaddr) + 8);
+
+ if((dmabuf = sun3_dvma_malloc(SUN3_DVMA_BUFSIZE)) == NULL) {
+ printk("SUN3 Scsi couldn't allocate DVMA memory!\n");
+ return 0;
+ }
+
+ if((udc_regs = sun3_dvma_malloc(sizeof(struct sun3_udc_regs)))
+ == NULL) {
+ printk("SUN3 Scsi couldn't allocate DVMA memory!\n");
+ return 0;
+ }
#ifdef SUPPORT_TAGS
if (setup_use_tagged_queuing < 0)
instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
default_instance = instance;
-/*
- if (macintosh_config->ident == MAC_MODEL_IIFX) {
- mac_scsi_regp = via1_regp+0x8000;
- mac_scsi_drq = via1_regp+0x6000;
- mac_scsi_nodrq = via1_regp+0x12000;
- } else {
- mac_scsi_regp = via1_regp+0x10000;
- mac_scsi_drq = via1_regp+0x6000;
- mac_scsi_nodrq = via1_regp+0x12000;
- }
-*/
-
instance->io_port = (unsigned long) ioaddr;
instance->irq = IRQ_SUN3_SCSI;
((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0;
- if (instance->irq != IRQ_NONE)
- if (sun3_request_irq(instance->irq, sun3scsi_intr,
- 0, "Sun3SCSI-5380", NULL)) {
- printk("scsi%d: IRQ%d not free, interrupts disabled\n",
- instance->host_no, instance->irq);
- instance->irq = IRQ_NONE;
- }
-
- printk("scsi%d: generic 5380 at port %lX irq", instance->host_no, instance->io_port);
+ if (request_irq(instance->irq, scsi_sun3_intr,
+ 0, "Sun3SCSI-5380", NULL)) {
+#ifndef REAL_DMA
+ printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+ instance->host_no, instance->irq);
+ instance->irq = IRQ_NONE;
+#else
+ printk("scsi%d: IRQ%d not free, bailing out\n",
+ instance->host_no, instance->irq);
+ return 0;
+#endif
+ }
+
+ printk("scsi%d: Sun3 5380 at port %lX irq", instance->host_no, instance->io_port);
if (instance->irq == IRQ_NONE)
printk ("s disabled");
else
NCR5380_print_options(instance);
printk("\n");
+ dregs->csr = 0;
+ udelay(SUN3_DMA_DELAY);
+ dregs->csr = CSR_SCSI | CSR_FIFO | CSR_INTR;
+ udelay(SUN3_DMA_DELAY);
+ dregs->fifo_count = 0;
+
called = 1;
return 1;
}
return "";
}
+// safe bits for the CSR
+#define CSR_GOOD 0x060f
-/*
- * NCR 5380 register access functions
- */
-
-static char sun3scsi_read(struct Scsi_Host *instance, int reg)
+static void scsi_sun3_intr(int irq, void *dummy, struct pt_regs *fp)
{
-/*
-printk("sun3scsi_read(instance=0x%p, reg=0x%x): @0x%p= %d\n",instance,reg,sun3_scsi_regp,sun3_scsi_regp[reg]);
-*/
- return( sun3_scsi_regp[reg] );
-}
+ unsigned short csr = dregs->csr;
-static void sun3scsi_write(struct Scsi_Host *instance, int reg, int value)
-{
-/*
- printk("sun3scsi_write(instance=0x%p, reg=0x%x, value=0x%x)\n", instance, reg, value);
-*/
- sun3_scsi_regp[reg] = value;
-}
+ if(csr & ~CSR_GOOD) {
+ if(csr & CSR_DMA_BUSERR) {
+ printk("scsi%d: bus error in dma\n", default_instance->host_no);
+ }
-#include "NCR5380.c"
+ if(csr & CSR_DMA_CONFLICT) {
+ printk("scsi%d: dma conflict\n", default_instance->host_no);
+ }
+ }
+
+ if(csr & (CSR_SDB_INT | CSR_DMA_INT))
+ NCR5380_intr(irq, dummy, fp);
+}
/*
* Debug stuff - to be called on NMI, or sysrq key. Use at your own risk;
* reentering NCR5380_print_status seems to have ugly side effects
*/
+/* this doesn't seem to get used at all -- sam */
+#if 0
void sun3_sun3_debug (void)
{
unsigned long flags;
NCR5380_print_status(default_instance);
restore_flags(flags);
}
-#if 0
- polled_scsi_on = 1;
-#endif
}
-/*
- * Helper function for interrupt trouble. More ugly side effects here.
- */
+#endif
+
-void scsi_sun3_polled (void)
+/* sun3scsi_dma_setup() -- initialize the dma controller for a read/write */
+static unsigned long sun3scsi_dma_setup(void *data, unsigned long count, int write_flag)
{
- unsigned long flags;
- NCR5380_local_declare();
- struct Scsi_Host *instance;
+ if(write_flag)
+ memcpy(dmabuf, data, count);
+ else {
+ sun3_dma_orig_addr = data;
+ sun3_dma_orig_count = count;
+ }
+
+ dregs->fifo_count = 0;
+ sun3_udc_write(UDC_RESET, UDC_CSR);
- instance = default_instance;
- NCR5380_setup(instance);
- if(NCR5380_read(BUS_AND_STATUS_REG)&BASR_IRQ)
- {
- printk("SCSI poll\n");
- save_flags(flags);
- cli();
- sun3scsi_intr(IRQ_SUN3_SCSI, instance, NULL);
- restore_flags(flags);
+ /* reset fifo */
+ dregs->csr &= ~CSR_FIFO;
+ dregs->csr |= CSR_FIFO;
+
+ /* set direction */
+ if(write_flag)
+ dregs->csr |= CSR_SEND;
+ else
+ dregs->csr &= ~CSR_SEND;
+
+ /* byte count for fifo */
+ dregs->fifo_count = count;
+
+ sun3_udc_write(UDC_RESET, UDC_CSR);
+
+ /* reset fifo */
+ dregs->csr &= ~CSR_FIFO;
+ dregs->csr |= CSR_FIFO;
+
+
+ if(dregs->fifo_count != count) {
+ printk("scsi%d: fifo_mismatch %04x not %04x\n",
+ default_instance->host_no, dregs->fifo_count,
+ (unsigned int) count);
+ NCR5380_print(default_instance);
+ }
+
+ /* setup udc */
+ udc_regs->addr_hi = ((sun3_dvma_vtop(dmabuf) & 0xff0000) >> 8);
+ udc_regs->addr_lo = (sun3_dvma_vtop(dmabuf) & 0xffff);
+ udc_regs->count = count/2; /* count in words */
+ udc_regs->mode_hi = UDC_MODE_HIWORD;
+ if(write_flag) {
+ if(count & 1)
+ udc_regs->count++;
+ udc_regs->mode_lo = UDC_MODE_LSEND;
+ udc_regs->rsel = UDC_RSEL_SEND;
+ } else {
+ udc_regs->mode_lo = UDC_MODE_LRECV;
+ udc_regs->rsel = UDC_RSEL_RECV;
}
+
+ /* announce location of regs block */
+ sun3_udc_write(((sun3_dvma_vtop(udc_regs) & 0xff0000) >> 8),
+ UDC_CHN_HI);
+
+ sun3_udc_write((sun3_dvma_vtop(udc_regs) & 0xffff), UDC_CHN_LO);
+
+ /* set dma master on */
+ sun3_udc_write(0xd, UDC_MODE);
+
+ /* interrupt enable */
+ sun3_udc_write(UDC_INT_ENABLE, UDC_CSR);
+
+ return count;
+
+}
+
+static inline unsigned long sun3scsi_dma_residual(struct Scsi_Host *instance)
+{
+ unsigned short resid;
+
+ dregs->udc_addr = 0x32;
+ udelay(SUN3_DMA_DELAY);
+ resid = dregs->udc_data;
+ udelay(SUN3_DMA_DELAY);
+ resid *= 2;
+
+ return (unsigned long) resid;
+}
+
+static inline unsigned long sun3scsi_dma_xfer_len(unsigned long wanted, Scsi_Cmnd *cmd,
+ int write_flag)
+{
+ return wanted;
}
+/* clean up after our dma is done */
+static int sun3scsi_dma_finish(void)
+{
+ unsigned short count;
+ int ret = 0;
+
+ count = sun3scsi_dma_residual(default_instance);
+
+ sun3_dma_active = 0;
+
+ /* if we've finished a read, copy out the data we read */
+ if(sun3_dma_orig_addr) {
+ /* check for residual bytes after dma end */
+ if(count && (NCR5380_read(BUS_AND_STATUS_REG) &
+ (BASR_PHASE_MATCH | BASR_ACK))) {
+ printk("scsi%d: sun3_scsi_finish: read overrun baby... ", default_instance->host_no);
+ printk("basr now %02x\n", NCR5380_read(BUS_AND_STATUS_REG));
+ ret = count;
+ }
+
+ /* copy in what we dma'd no matter what */
+ memcpy(sun3_dma_orig_addr, dmabuf, sun3_dma_orig_count);
+ sun3_dma_orig_addr = NULL;
+
+ }
+
+ sun3_udc_write(UDC_RESET, UDC_CSR);
+
+ dregs->csr &= ~CSR_SEND;
+
+ /* reset fifo */
+ dregs->csr &= ~CSR_FIFO;
+ dregs->csr |= CSR_FIFO;
+
+ sun3_dma_setup_done = NULL;
+
+ return ret;
+
+}
+
+#include "sun3_NCR5380.c"
#ifdef MODULE
#include "scsi_module.c"
#endif
+
/*
* Sun3 SCSI stuff by Erik Verbruggen (erik@bigmama.xtdnet.nl)
*
+ * Sun3 DMA additions by Sam Creasey (sammy@oh.verio.com)
+ *
* Adapted from mac_scsinew.h:
*/
/*
#define NCR5380_setup(instance) \
_instance = instance
-#define NCR5380_read(reg) sun3scsi_read(_instance, reg)
-#define NCR5380_write(reg, value) sun3scsi_write(_instance, reg, value)
+#define NCR5380_read(reg) sun3scsi_read(reg)
+#define NCR5380_write(reg, value) sun3scsi_write(reg, value)
#define NCR5380_intr sun3scsi_intr
#define NCR5380_queue_command sun3scsi_queue_command
-#define NCR5380_abort sun3scsi_abort
#define NCR5380_reset sun3scsi_reset
+#define NCR5380_abort sun3scsi_abort
#define NCR5380_proc_info sun3scsi_proc_info
+#define NCR5380_dma_xfer_len(i, cmd, phase) \
+ sun3scsi_dma_xfer_len(cmd->SCp.this_residual,cmd,((phase) & SR_IO) ? 0 : 1)
+
+#define NCR5380_dma_write_setup(instance, data, count) sun3scsi_dma_setup(data, count, 1)
+#define NCR5380_dma_read_setup(instance, data, count) sun3scsi_dma_setup(data, count, 0)
+#define NCR5380_dma_residual sun3scsi_dma_residual
#define BOARD_NORMAL 0
#define BOARD_NCR53C400 1
+/* additional registers - mainly DMA control regs */
+/* these start at regbase + 8 -- directly after the NCR regs */
+struct sun3_dma_regs {
+ unsigned short vmeregs[4]; /* unimpl vme stuff */
+ unsigned short udc_data; /* udc dma data reg */
+ unsigned short udc_addr; /* uda dma addr reg */
+ unsigned short fifo_data; /* fifo data reg, holds extra byte on
+ odd dma reads */
+ unsigned short fifo_count;
+ unsigned short csr; /* control/status reg */
+};
+
+/* ucd chip specific regs - live in dvma space */
+struct sun3_udc_regs {
+ unsigned short rsel; /* select regs to load */
+ unsigned short addr_hi; /* high word of addr */
+ unsigned short addr_lo; /* low word */
+ unsigned short count; /* words to be xfer'd */
+ unsigned short mode_hi; /* high word of channel mode */
+ unsigned short mode_lo; /* low word of channel mode */
+};
+
+/* addresses of the udc registers */
+#define UDC_MODE 0x38
+#define UDC_CSR 0x2e /* command/status */
+#define UDC_CHN_HI 0x26 /* chain high word */
+#define UDC_CHN_LO 0x22 /* chain lo word */
+#define UDC_CURA_HI 0x1a /* cur reg A high */
+#define UDC_CURA_LO 0x0a /* cur reg A low */
+#define UDC_CURB_HI 0x12 /* cur reg B high */
+#define UDC_CURB_LO 0x02 /* cur reg B low */
+#define UDC_MODE_HI 0x56 /* mode reg high */
+#define UDC_MODE_LO 0x52 /* mode reg low */
+#define UDC_COUNT 0x32 /* words to xfer */
+
+/* some udc commands */
+#define UDC_RESET 0
+#define UDC_CHN_START 0xa0 /* start chain */
+#define UDC_INT_ENABLE 0x32 /* channel 1 int on */
+
+/* udc mode words */
+#define UDC_MODE_HIWORD 0x40
+#define UDC_MODE_LSEND 0xc2
+#define UDC_MODE_LRECV 0xd2
+
+/* udc reg selections */
+#define UDC_RSEL_SEND 0x282
+#define UDC_RSEL_RECV 0x182
+
+/* bits in csr reg */
+#define CSR_DMA_ACTIVE 0x8000
+#define CSR_DMA_CONFLICT 0x4000
+#define CSR_DMA_BUSERR 0x2000
+
+#define CSR_FIFO_EMPTY 0x400 /* fifo flushed? */
+#define CSR_SDB_INT 0x200 /* sbc interrupt pending */
+#define CSR_DMA_INT 0x100 /* dma interrupt pending */
+
+#define CSR_SEND 0x8 /* 1 = send 0 = recv */
+#define CSR_FIFO 0x2 /* reset fifo */
+#define CSR_INTR 0x4 /* interrupt enable */
+#define CSR_SCSI 0x1
+
+// debugging printk's, taken from atari_scsi.h
+/* Debugging printk definitions:
+ *
+ * ARB -> arbitration
+ * ASEN -> auto-sense
+ * DMA -> DMA
+ * HSH -> PIO handshake
+ * INF -> information transfer
+ * INI -> initialization
+ * INT -> interrupt
+ * LNK -> linked commands
+ * MAIN -> NCR5380_main() control flow
+ * NDAT -> no data-out phase
+ * NWR -> no write commands
+ * PIO -> PIO transfers
+ * PDMA -> pseudo DMA (unused on Atari)
+ * QU -> queues
+ * RSL -> reselections
+ * SEL -> selections
+ * USL -> usleep cpde (unused on Atari)
+ * LBS -> last byte sent (unused on Atari)
+ * RSS -> restarting of selections
+ * EXT -> extended messages
+ * ABRT -> aborting and resetting
+ * TAG -> queue tag handling
+ * MER -> merging of consec. buffers
+ *
+ */
+
+
+
+#if NDEBUG & NDEBUG_ARBITRATION
+#define ARB_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define ARB_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_AUTOSENSE
+#define ASEN_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define ASEN_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_DMA
+#define DMA_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define DMA_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_HANDSHAKE
+#define HSH_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define HSH_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_INFORMATION
+#define INF_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define INF_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_INIT
+#define INI_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define INI_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_INTR
+#define INT_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define INT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_LINKED
+#define LNK_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define LNK_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_MAIN
+#define MAIN_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define MAIN_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_NO_DATAOUT
+#define NDAT_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define NDAT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_NO_WRITE
+#define NWR_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define NWR_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_PIO
+#define PIO_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define PIO_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_PSEUDO_DMA
+#define PDMA_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define PDMA_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_QUEUES
+#define QU_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define QU_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_RESELECTION
+#define RSL_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define RSL_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_SELECTION
+#define SEL_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define SEL_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_USLEEP
+#define USL_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define USL_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_LAST_BYTE_SENT
+#define LBS_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define LBS_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_RESTART_SELECT
+#define RSS_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define RSS_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_EXTENDED
+#define EXT_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define EXT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_ABORT
+#define ABRT_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define ABRT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_TAGS
+#define TAG_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define TAG_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_MERGING
+#define MER_PRINTK(format, args...) \
+ printk(KERN_DEBUG format , ## args)
+#else
+#define MER_PRINTK(format, args...)
+#endif
+
+/* conditional macros for NCR5380_print_{,phase,status} */
+
+#define NCR_PRINT(mask) \
+ ((NDEBUG & (mask)) ? NCR5380_print(instance) : (void)0)
+
+#define NCR_PRINT_PHASE(mask) \
+ ((NDEBUG & (mask)) ? NCR5380_print_phase(instance) : (void)0)
+
+#define NCR_PRINT_STATUS(mask) \
+ ((NDEBUG & (mask)) ? NCR5380_print_status(instance) : (void)0)
+
+#define NDEBUG_ANY 0xffffffff
+
+
+
#endif /* ndef HOSTS_C */
#endif /* SUN3_NCR5380_H */
fi
if [ "$CONFIG_MAC" = "y" ]; then
define_bool CONFIG_FB_MAC y
+ bool ' Apple "valkyrie" display support' CONFIG_FB_VALKYRIE
+# bool ' Apple DAFB display support' CONFIG_FB_DAFB
fi
if [ "$CONFIG_HP300" = "y" ]; then
define_bool CONFIG_FB_HP300 y
tristate ' SGI Visual Workstation framebuffer support' CONFIG_FB_SGIVW
define_bool CONFIG_BUS_I2C y
fi
+ if [ "$CONFIG_SUN3" = "y" -o "$CONFIG_SUN3X" = "y" ]; then
+ bool 'Sun3 framebuffer support' CONFIG_FB_SUN3
+ if [ "$CONFIG_FB_SUN3" != "n" ]; then
+ bool ' BWtwo support' CONFIG_FB_BWTWO
+ bool ' CGsix (GX,TurboGX) support' CONFIG_FB_CGSIX
+ fi
+ fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
if [ "$CONFIG_PCI" != "n" ]; then
tristate ' Matrox acceleration (EXPERIMENTAL)' CONFIG_FB_MATROX
obj-$(CONFIG_FB_SGIVW) += sgivwfb.o
obj-$(CONFIG_FB_RIVA) += rivafb.o riva_hw.o
obj-$(CONFIG_FB_3DFX) += tdfxfb.o
-obj-$(CONFIG_FB_MAC) += macfb.o
+obj-$(CONFIG_FB_MAC) += macfb.o macmodes.o
obj-$(CONFIG_FB_HP300) += hpfb.o
obj-$(CONFIG_FB_OF) += offb.o macmodes.o
obj-$(CONFIG_FB_IMSTT) += imsttfb.o
obj-$(CONFIG_FB_P9100) += p9100fb.o sbusfb.o
obj-$(CONFIG_FB_LEO) += leofb.o sbusfb.o
obj-$(CONFIG_FB_MATROX) += matroxfb.o
+obj-$(CONFIG_FB_SUN3) += sun3fb.o
+obj-$(CONFIG_FB_BWTWO) += bwtwofb.o
obj-$(CONFIG_FB_VIRTUAL) += vfb.o
# Generic Low Level Drivers
#include <video/sbusfb.h>
#include <asm/io.h>
-#ifndef __sparc_v9__
+#if !defined(__sparc_v9__) && !defined(__mc68000__)
#include <asm/sun4paddr.h>
#endif
case BWTWO_SR_ID_NOCONN:
return NULL;
default:
+#ifndef CONFIG_FB_SUN3
prom_printf("bw2: can't handle SR %02x\n",
status);
prom_halt();
+#endif
return NULL; /* fool gcc. */
}
for ( ; *p; p += 2) {
u16 *dest16, pix16;
u32 *dest32, pix32;
- if (pixel_x < 0 || pixel_y < 0 || pixel_x >= 832 || pixel_y >= 624) {
+ /* There *are* 68k Macs that support more than 832x624, you know :-) */
+ if (pixel_x < 0 || pixel_y < 0 || pixel_x >= p->var.xres || pixel_y >= p->var.yres) {
int cnt;
printk ("ERROR: pixel_x == %d, pixel_y == %d", pixel_x, pixel_y);
for(cnt = 0; cnt < 100000; cnt++)
extern int fm2fb_init(void);
extern int fm2fb_setup(char*);
extern int q40fb_init(void);
+extern int sun3fb_init(void);
+extern int sun3fb_setup(char *);
extern int sgivwfb_init(void);
extern int sgivwfb_setup(char*);
extern int rivafb_init(void);
#ifdef CONFIG_FB_FM2
{ "fm2fb", fm2fb_init, fm2fb_setup },
#endif
+#ifdef CONFIG_FB_SUN3
+ { "sun3", sun3fb_init, sun3fb_setup },
+#endif
#ifdef CONFIG_GSP_RESOLVER
/* Not a real frame buffer device... */
{ "resolver", NULL, resolver_video_setup },
vma->vm_flags |= VM_IO;
#else
#if defined(__mc68000__)
+#if defined(CONFIG_SUN3)
+ pgprot_val(vma->vm_page_prot) |= SUN3_PAGE_NOCACHE;
+#else
if (CPU_IS_020_OR_030)
pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE030;
if (CPU_IS_040_OR_060) {
/* Use no-cache mode, serialized */
pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE_S;
}
+#endif
#elif defined(__powerpc__)
pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE|_PAGE_GUARDED;
#elif defined(__alpha__)
-/*
- * We've been given MAC frame buffer info by the booter. Now go set it up
- */
+/* macfb.c: Generic framebuffer for Macs whose colourmaps/modes we
+ don't know how to set */
+
+/* (c) 1999 David Huggins-Daines <dhd@debian.org>
+
+ Primarily based on vesafb.c, by Gerd Knorr
+ (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
+
+ Also uses information and code from:
+
+ The original macfb.c from Linux/mac68k 2.0, by Alan Cox, Juergen
+ Mellinger, Mikael Forselius, Michael Schmitz, and others.
+
+ valkyriefb.c, by Martin Costabel, Kevin Schoedel, Barry Nathan, Dan
+ Jacobowitz, Paul Mackerras, Fabio Riccardi, and Geert Uytterhoeven.
+
+ This code is free software. You may copy, modify, and distribute
+ it subject to the terms and conditions of the GNU General Public
+ License, version 2, or any later version, at your convenience. */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/nubus.h>
#include <linux/init.h>
+#include <linux/fb.h>
#include <asm/setup.h>
#include <asm/bootinfo.h>
#include <asm/pgtable.h>
#include <asm/irq.h>
#include <asm/macintosh.h>
-#include <linux/fb.h>
+#include <asm/io.h>
+#include <asm/machw.h>
#include <video/fbcon.h>
-/* conditionalize these ?? */
#include <video/fbcon-mfb.h>
#include <video/fbcon-cfb2.h>
#include <video/fbcon-cfb4.h>
#include <video/fbcon-cfb8.h>
+#include <video/fbcon-cfb16.h>
+#include <video/fbcon-cfb24.h>
+#include <video/fbcon-cfb32.h>
+
+#if defined(FBCON_HAS_CFB8) || defined(FBCON_HAS_CFB4) || defined(FBCON_HAS_CFB2)
+
+/* Common DAC base address for the LC, RBV, Valkyrie, and IIvx */
+#define DAC_BASE 0x50f24000
+
+/* Some addresses for the DAFB */
+#define DAFB_BASE 0xf9800200
+
+/* Address for the built-in Civic framebuffer in Quadra AVs */
+#define CIVIC_BASE 0x50f30800 /* Only tested on 660AV! */
+
+/* GSC (Gray Scale Controller) base address */
+#define GSC_BASE 0x50F20000
+
+/* CSC (Color Screen Controller) base address */
+#define CSC_BASE 0x50F20000
+
+static int (*macfb_setpalette) (unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue) = NULL;
+static int valkyrie_setpalette (unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue);
+static int dafb_setpalette (unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue);
+static int rbv_setpalette (unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue);
+static int mdc_setpalette (unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue);
+static int toby_setpalette (unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue);
+static int civic_setpalette (unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue);
+static int csc_setpalette (unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue);
+
+static volatile struct {
+ unsigned char addr;
+ /* Note: word-aligned */
+ char pad[3];
+ unsigned char lut;
+} *valkyrie_cmap_regs;
+
+static volatile struct {
+ unsigned char addr;
+ unsigned char lut;
+} *v8_brazil_cmap_regs;
+
+static volatile struct {
+ unsigned char addr;
+ char pad1[3]; /* word aligned */
+ unsigned char lut;
+ char pad2[3]; /* word aligned */
+ unsigned char cntl; /* a guess as to purpose */
+} *rbv_cmap_regs;
+
+static volatile struct {
+ unsigned long reset;
+ unsigned long pad1[3];
+ unsigned char pad2[3];
+ unsigned char lut;
+} *dafb_cmap_regs;
+
+static volatile struct {
+ unsigned char addr; /* OFFSET: 0x00 */
+ unsigned char pad1[15];
+ unsigned char lut; /* OFFSET: 0x10 */
+ unsigned char pad2[15];
+ unsigned char status; /* OFFSET: 0x20 */
+ unsigned char pad3[7];
+ unsigned long vbl_addr; /* OFFSET: 0x28 */
+ unsigned int status2; /* OFFSET: 0x2C */
+} *civic_cmap_regs;
+
+static volatile struct {
+ char pad1[0x40];
+ unsigned char clut_waddr; /* 0x40 */
+ char pad2;
+ unsigned char clut_data; /* 0x42 */
+ char pad3[0x3];
+ unsigned char clut_raddr; /* 0x46 */
+} *csc_cmap_regs;
+
+/* We will leave these the way they are for the time being */
+struct mdc_cmap_regs {
+ char pad1[0x200200];
+ unsigned char addr;
+ char pad2[6];
+ unsigned char lut;
+};
+
+struct toby_cmap_regs {
+ char pad1[0x90018];
+ unsigned char lut; /* TFBClutWDataReg, offset 0x90018 */
+ char pad2[3];
+ unsigned char addr; /* TFBClutAddrReg, offset 0x9001C */
+};
+
+struct jet_cmap_regs {
+ char pad1[0xe0e000];
+ unsigned char addr;
+ unsigned char lut;
+};
+
+#endif
+
+#define PIXEL_TO_MM(a) (((a)*10)/28) /* width in mm at 72 dpi */
+
+static char* video_base;
+static int video_size;
+static char* video_vbase; /* mapped */
-#define arraysize(x) (sizeof(x)/sizeof(*(x)))
+/* mode */
+static int video_bpp;
+static int video_width;
+static int video_height;
+static int video_type = FB_TYPE_PACKED_PIXELS;
+static int video_visual;
+static int video_linelength;
+static int video_cmap_len;
+static int video_slot = 0;
static struct fb_var_screeninfo macfb_defined={
0,0,0,0, /* W,H, W, H (virtual) load xres,xres_virtual*/
{0,0,0}, /* transparency */
0, /* standard pixel format */
FB_ACTIVATE_NOW,
- 274,195, /* 14" monitor *Mikael Nykvist's anyway* */
- 0, /* The only way to accelerate a mac is .. */
+ -1, -1,
+ FB_ACCEL_NONE, /* The only way to accelerate a mac is .. */
0L,0L,0L,0L,0L,
0L,0L,0, /* No sync info */
FB_VMODE_NONINTERLACED,
{0,0,0,0,0,0}
};
-#define NUM_TOTAL_MODES 1
-#define NUM_PREDEF_MODES 1
-
static struct display disp;
static struct fb_info fb_info;
+static struct { u_short blue, green, red, pad; } palette[256];
+static union {
+#ifdef FBCON_HAS_CFB16
+ u16 cfb16[16];
+#endif
+#ifdef FBCON_HAS_CFB24
+ u32 cfb24[16];
+#endif
+#ifdef FBCON_HAS_CFB32
+ u32 cfb32[16];
+#endif
+} fbcon_cmap;
-static int inverse = 0;
-
-struct macfb_par
-{
- void *unused;
-};
-
-static int currcon = 0;
-static int current_par_valid = 0;
-struct macfb_par current_par;
-
-static int mac_xres,mac_yres,mac_depth, mac_xbytes, mac_vxres;
-static unsigned long mac_videobase;
-static unsigned long mac_videosize;
+static int inverse = 0;
+static int vidtest = 0;
+static int currcon = 0;
- /*
- * Open/Release the frame buffer device
- */
+/*
+ * Open/Release the frame buffer device
+ */
static int macfb_open(struct fb_info *info, int user)
{
return(0);
}
-static void macfb_encode_var(struct fb_var_screeninfo *var,
- struct macfb_par *par)
-{
- memset(var, 0, sizeof(struct fb_var_screeninfo));
- var->xres=mac_xres;
- var->yres=mac_yres;
- var->xres_virtual=mac_vxres;
- var->yres_virtual=var->yres;
- var->xoffset=0;
- var->yoffset=0;
- var->bits_per_pixel = mac_depth;
- var->grayscale=0;
- var->transp.offset=0;
- var->transp.length=0;
- var->transp.msb_right=0;
- var->nonstd=0;
- var->activate=0;
- var->height= -1;
- var->width= -1;
- var->vmode=FB_VMODE_NONINTERLACED;
- var->pixclock=0;
- var->sync=0;
- var->left_margin=0;
- var->right_margin=0;
- var->upper_margin=0;
- var->lower_margin=0;
- var->hsync_len=0;
- var->vsync_len=0;
- return;
-}
-
-
-static void macfb_get_par(struct macfb_par *par)
-{
- *par=current_par;
-}
-
-static void macfb_set_par(struct macfb_par *par)
-{
- current_par_valid=1;
-}
-
-static int fb_update_var(int con, struct fb_info *info)
+static int macfb_update_var(int con, struct fb_info *info)
{
return 0;
}
-static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
-{
- struct macfb_par par;
-
- macfb_get_par(&par);
- macfb_encode_var(var, &par);
- return 0;
-}
-
-extern int console_loglevel;
-
-static void macfb_encode_fix(struct fb_fix_screeninfo *fix,
- struct macfb_par *par)
-{
- memset(fix, 0, sizeof(struct fb_fix_screeninfo));
- strcpy(fix->id,"Macintosh");
-
- /*
- * fbmem.c accepts non page aligned mappings now!
- */
- fix->smem_start=mac_videobase;
- fix->smem_len=mac_videosize;
- fix->type = FB_TYPE_PACKED_PIXELS;
- if (mac_depth == 1)
- fix->visual = FB_VISUAL_MONO01;
- else
- fix->visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
- fix->xpanstep=0;
- fix->ypanstep=0;
- fix->ywrapstep=0;
- fix->line_length=mac_xbytes;
- return;
-}
-
static int macfb_get_fix(struct fb_fix_screeninfo *fix, int con,
struct fb_info *info)
{
- struct macfb_par par;
- macfb_get_par(&par);
- macfb_encode_fix(fix, &par);
+ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+ strcpy(fix->id, "Mac Generic");
+
+ fix->smem_start = video_base;
+ fix->smem_len = video_size;
+ fix->type = video_type;
+ fix->visual = video_visual;
+ fix->xpanstep = 0;
+ fix->ypanstep = 0;
+ fix->line_length=video_linelength;
return 0;
}
static int macfb_get_var(struct fb_var_screeninfo *var, int con,
struct fb_info *info)
{
- struct macfb_par par;
if(con==-1)
- {
- macfb_get_par(&par);
- macfb_encode_var(var, &par);
- }
+ memcpy(var, &macfb_defined, sizeof(struct fb_var_screeninfo));
else
*var=fb_display[con].var;
return 0;
else
display = &disp; /* used during initialization */
- macfb_get_fix(&fix, con, 0);
+ macfb_get_fix(&fix, con, &fb_info);
- display->screen_base = fix.smem_start;
+ memset(display, 0, sizeof(struct display));
+ display->screen_base = video_vbase;
display->visual = fix.visual;
display->type = fix.type;
display->type_aux = fix.type_aux;
display->line_length = fix.line_length;
display->next_line = fix.line_length;
display->can_soft_blank = 0;
- display->inverse =
- (fix.visual == FB_VISUAL_MONO01 ? !inverse : inverse);
+ display->inverse = inverse;
+ display->scrollmode = SCROLL_YREDRAW;
+ macfb_get_var(&display->var, -1, &fb_info);
- switch (mac_depth) {
+ switch (video_bpp) {
#ifdef FBCON_HAS_MFB
- case 1:
+ case 1:
display->dispsw = &fbcon_mfb;
break;
#endif
#ifdef FBCON_HAS_CFB2
- case 2:
+ case 2:
display->dispsw = &fbcon_cfb2;
break;
#endif
#ifdef FBCON_HAS_CFB4
- case 4:
+ case 4:
display->dispsw = &fbcon_cfb4;
break;
#endif
#ifdef FBCON_HAS_CFB8
- case 8:
+ case 8:
display->dispsw = &fbcon_cfb8;
break;
#endif
- default:
- display->dispsw = &fbcon_dummy;
+#ifdef FBCON_HAS_CFB16
+ case 15:
+ case 16:
+ display->dispsw = &fbcon_cfb16;
+ display->dispsw_data = fbcon_cmap.cfb16;
+ break;
+#endif
+#ifdef FBCON_HAS_CFB24
+ case 24:
+ display->dispsw = &fbcon_cfb24;
+ display->dispsw_data = fbcon_cmap.cfb24;
break;
+#endif
+#ifdef FBCON_HAS_CFB32
+ case 32:
+ display->dispsw = &fbcon_cfb32;
+ display->dispsw_data = fbcon_cmap.cfb32;
+ break;
+#endif
+ default:
+ display->dispsw = &fbcon_dummy;
+ return;
}
}
static int macfb_set_var(struct fb_var_screeninfo *var, int con,
struct fb_info *info)
{
- int err;
+ static int first = 1;
+
+ if (var->xres != macfb_defined.xres ||
+ var->yres != macfb_defined.yres ||
+ var->xres_virtual != macfb_defined.xres_virtual ||
+ var->yres_virtual != macfb_defined.yres ||
+ var->xoffset ||
+ var->bits_per_pixel != macfb_defined.bits_per_pixel ||
+ var->nonstd) {
+ if (first) {
+ printk("macfb does not support changing the video mode\n");
+ first = 0;
+ }
+ return -EINVAL;
+ }
+
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST)
+ return 0;
+
+ if (var->yoffset)
+ return -EINVAL;
+ return 0;
+}
+
+#if defined(FBCON_HAS_CFB8) || defined(FBCON_HAS_CFB4) || defined(FBCON_HAS_CFB2)
+static int valkyrie_setpalette (unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue)
+{
+ unsigned long flags;
+
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+
+ save_flags(flags);
+ cli();
+
+ /* tell clut which address to fill */
+ writeb(regno, &valkyrie_cmap_regs->addr);
+ nop();
+
+ /* send one color channel at a time */
+ writeb(red, &valkyrie_cmap_regs->lut);
+ nop();
+ writeb(green, &valkyrie_cmap_regs->lut);
+ nop();
+ writeb(blue, &valkyrie_cmap_regs->lut);
+
+ restore_flags(flags);
+
+ return 0;
+}
+
+/* Unlike the Valkyrie, the DAFB cannot set individual colormap
+ registers. Therefore, we do what the MacOS driver does (no
+ kidding!) and simply set them one by one until we hit the one we
+ want. */
+static int dafb_setpalette (unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue)
+{
+ /* FIXME: really, really need to use ioremap() here,
+ phys_to_virt() doesn't work anymore */
+ static int lastreg = -1;
+ unsigned long flags;
+
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+
+ save_flags(flags);
+ cli();
+
+ /* fbcon will set an entire colourmap, but X won't. Hopefully
+ this should accomodate both of them */
+ if (regno != lastreg+1) {
+ int i;
+
+ /* Stab in the dark trying to reset the CLUT pointer */
+ writel(0, &dafb_cmap_regs->reset);
+ nop();
+
+ /* Loop until we get to the register we want */
+ for (i = 0; i < regno; i++) {
+ writeb(palette[i].red >> 8, &dafb_cmap_regs->lut);
+ nop();
+ writeb(palette[i].green >> 8, &dafb_cmap_regs->lut);
+ nop();
+ writeb(palette[i].blue >> 8, &dafb_cmap_regs->lut);
+ nop();
+ }
+ }
+
+ writeb(red, &dafb_cmap_regs->lut);
+ nop();
+ writeb(green, &dafb_cmap_regs->lut);
+ nop();
+ writeb(blue, &dafb_cmap_regs->lut);
+
+ restore_flags(flags);
+
+ lastreg = regno;
+ return 0;
+}
+
+/* V8 and Brazil seem to use the same DAC. Sonora does as well. */
+static int v8_brazil_setpalette (unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue)
+{
+ unsigned char _red =red>>8;
+ unsigned char _green=green>>8;
+ unsigned char _blue =blue>>8;
+ unsigned char _regno;
+ unsigned long flags;
+
+ if (video_bpp>8) return 1; /* failsafe */
+
+ save_flags(flags);
+ cli();
+
+ /* On these chips, the CLUT register numbers are spread out
+ across the register space. Thus:
+
+ In 8bpp, all regnos are valid.
+
+ In 4bpp, the regnos are 0x0f, 0x1f, 0x2f, etc, etc
+
+ In 2bpp, the regnos are 0x3f, 0x7f, 0xbf, 0xff */
+ _regno = (regno<<(8-video_bpp)) | (0xFF>>video_bpp);
+ writeb(_regno, &v8_brazil_cmap_regs->addr); nop();
+
+ /* send one color channel at a time */
+ writeb(_red, &v8_brazil_cmap_regs->lut); nop();
+ writeb(_green, &v8_brazil_cmap_regs->lut); nop();
+ writeb(_blue, &v8_brazil_cmap_regs->lut);
+
+ restore_flags(flags);
+
+ return 0;
+}
+
+static int rbv_setpalette (unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue)
+{
+ /* use MSBs */
+ unsigned char _red =red>>8;
+ unsigned char _green=green>>8;
+ unsigned char _blue =blue>>8;
+ unsigned char _regno;
+ unsigned long flags;
+
+ if (video_bpp>8) return 1; /* failsafe */
+
+ save_flags(flags);
+ cli();
+
+ /* From the VideoToolbox driver. Seems to be saying that
+ * regno #254 and #255 are the important ones for 1-bit color,
+ * regno #252-255 are the important ones for 2-bit color, etc.
+ */
+ _regno = regno + (256-(1<<video_bpp));
+
+ /* reset clut? (VideoToolbox sez "not necessary") */
+ writeb(0xFF, &rbv_cmap_regs->cntl); nop();
+
+ /* tell clut which address to use. */
+ writeb(_regno, &rbv_cmap_regs->addr); nop();
+
+ /* send one color channel at a time. */
+ writeb(_red, &rbv_cmap_regs->lut); nop();
+ writeb(_green, &rbv_cmap_regs->lut); nop();
+ writeb(_blue, &rbv_cmap_regs->lut);
+
+ restore_flags(flags);
+ /* done. */
+ return 0;
+}
+
+/* Macintosh Display Card (8x24) */
+static int mdc_setpalette(unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue)
+{
+ volatile struct mdc_cmap_regs *cmap_regs =
+ nubus_slot_addr(video_slot);
+ /* use MSBs */
+ unsigned char _red =red>>8;
+ unsigned char _green=green>>8;
+ unsigned char _blue =blue>>8;
+ unsigned char _regno=regno;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ /* the nop's are there to order writes. */
+ writeb(_regno, &cmap_regs->addr); nop();
+ writeb(_red, &cmap_regs->lut); nop();
+ writeb(_green, &cmap_regs->lut); nop();
+ writeb(_blue, &cmap_regs->lut);
+
+ restore_flags(flags);
+ return 0;
+}
+
+/* Toby frame buffer */
+static int toby_setpalette(unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue)
+{
+ volatile struct toby_cmap_regs *cmap_regs =
+ nubus_slot_addr(video_slot);
+ /* use MSBs */
+ unsigned char _red =~(red>>8);
+ unsigned char _green=~(green>>8);
+ unsigned char _blue =~(blue>>8);
+ unsigned char _regno = (regno<<(8-video_bpp)) | (0xFF>>video_bpp);
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
- if ((err=do_fb_set_var(var, 1)))
- return err;
+ writeb(_regno, &cmap_regs->addr); nop();
+ writeb(_red, &cmap_regs->lut); nop();
+ writeb(_green, &cmap_regs->lut); nop();
+ writeb(_blue, &cmap_regs->lut);
+
+ restore_flags(flags);
+ return 0;
+}
+
+/* Jet frame buffer */
+static int jet_setpalette(unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue)
+{
+ volatile struct jet_cmap_regs *cmap_regs =
+ nubus_slot_addr(video_slot);
+ /* use MSBs */
+ unsigned char _red = (red>>8);
+ unsigned char _green = (green>>8);
+ unsigned char _blue = (blue>>8);
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ writeb(regno, &cmap_regs->addr); nop();
+ writeb(_red, &cmap_regs->lut); nop();
+ writeb(_green, &cmap_regs->lut); nop();
+ writeb(_blue, &cmap_regs->lut);
+
+ restore_flags(flags);
return 0;
}
/*
- * Color map handling - hardcoded maps!!
+ * Civic framebuffer -- Quadra AV built-in video. A chip
+ * called Sebastian holds the actual color palettes, and
+ * apparently, there are two different banks of 512K RAM
+ * which can act as separate framebuffers for doing video
+ * input and viewing the screen at the same time! The 840AV
+ * Can add another 1MB RAM to give the two framebuffers
+ * 1MB RAM apiece.
*
- * 2.0 color map primitives, copied from atafb.c
- */
-
-/*
- * should be kmalloc'ed on request
+ * FIXME: this doesn't seem to work anymore.
*/
-static short red256[256], green256[256], blue256[256];
-
-static short red16[]=
- { 0x0000,0x0000,0x0000,0x0000,0x8080,0x8080,0x8080,0xc0c0,
- 0x8080,0x0000,0x0000,0x0000,0xffff,0xffff,0xffff,0xffff};
-static short green16[]=
- { 0x0000,0x0000,0x8080,0x8080,0x0000,0x0000,0x8080,0xc0c0,
- 0x8080,0x0000,0xffff,0xffff,0x0000,0x0000,0xffff,0xffff};
-static short blue16[]=
- { 0x0000,0x8080,0x0000,0x8080,0x0000,0x8080,0x0000,0xc0c0,
- 0x8080,0xffff,0x0000,0xffff,0x0000,0xffff,0x0000,0xffff};
-
-static short red4[]=
- { 0x0000,0x8080,0xffff,0xffff};
-static short green4[]=
- { 0x0000,0x8080,0x0000,0xffff};
-static short blue4[]=
- { 0x0000,0x8080,0x0000,0xffff};
-
-static short red2[]=
- { 0x0000,0xffff};
-static short green2[]=
- { 0x0000,0xffff};
-static short blue2[]=
- { 0x0000,0xffff};
-
-struct fb_cmap default_256_colors = { 0, 256, red256, green256, blue256, NULL };
-struct fb_cmap default_16_colors = { 0, 16, red16, green16, blue16, NULL };
-struct fb_cmap default_4_colors = { 0, 4, red4, green4, blue4, NULL };
-struct fb_cmap default_2_colors = { 0, 2, red2, green2, blue2, NULL };
-
-static int mac_set_cmap256(struct fb_cmap* cmap)
+static int civic_setpalette (unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue)
{
- int i,start;
- unsigned short *red,*green,*blue;
- unsigned short cval[] = {0xffff, 0xcccc, 0x9999,
- 0x6666, 0x3333, 0x0000 };
- unsigned short gval[] = {0x0a0a, 0x1414, 0x1e1e,
- 0x2828, 0x3232, 0x3c3c,
- 0x4646, 0x5050, 0x5a5a,
- 0x6464, 0x6e6e, 0x7878,
- 0x8282, 0x8c8c, 0x9696,
- 0xa0a0, 0xaaaa, 0xb4b4,
- 0xbebe, 0xc8c8, 0xd2d2,
- 0xdcdc, 0xe6e6, 0xf0f0};
-
- red=cmap->red;
- green=cmap->green;
- blue=cmap->blue;
- start=cmap->start;
-
- if (start < 0)
- return -EINVAL;
- if (cmap->len < 255)
- return -EINVAL;
- /* 16 ANSI colors */
- for (i=0 ; i < 16 ; i++) {
- *red++ = red16[i];
- *green++ = green16[i];
- *blue++ = blue16[i];
+ static int lastreg = -1;
+ unsigned long flags;
+ int clut_status;
+
+ if (video_bpp > 8) return 1; /* failsafe */
+
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+
+ save_flags(flags);
+ cli();
+
+ /*
+ * Set the register address
+ */
+ writeb(regno, &civic_cmap_regs->addr); nop();
+
+ /*
+ * Wait for VBL interrupt here;
+ * They're usually not enabled from Penguin, so we won't check
+ */
+#if 0
+ {
+#define CIVIC_VBL_OFFSET 0x120
+ volatile unsigned long *vbl = readl(civic_cmap_regs->vbl_addr + CIVIC_VBL_OFFSET);
+ /* do interrupt setup stuff here? */
+ *vbl = 0L; nop(); /* clear */
+ *vbl = 1L; nop(); /* set */
+ while (*vbl != 0L) /* wait for next vbl */
+ {
+ usleep(10); /* needed? */
+ }
+ /* do interrupt shutdown stuff here? */
}
- /* 216 colors (6x6x6) map) */
- for (i=16 ; i < 232 ; i++) {
- *red++ = cval[(i-16)/36];
- *green++ = cval[((i-16)/6)%6];
- *blue++ = cval[(i-16)%6];
+#endif
+
+ /*
+ * Grab a status word and do some checking;
+ * Then finally write the clut!
+ */
+ clut_status = readb(&civic_cmap_regs->status2);
+
+ if ((clut_status & 0x0008) == 0)
+ {
+#if 0
+ if ((clut_status & 0x000D) != 0)
+ {
+ writeb(0x00, &civic_cmap_regs->lut); nop();
+ writeb(0x00, &civic_cmap_regs->lut); nop();
+ }
+#endif
+
+ writeb( red, &civic_cmap_regs->lut); nop();
+ writeb(green, &civic_cmap_regs->lut); nop();
+ writeb( blue, &civic_cmap_regs->lut); nop();
+ writeb( 0x00, &civic_cmap_regs->lut); nop();
}
- /* 24 grays */
- for (i=232 ; i < 256 ; i++) {
- *red = *green = *blue = gval[i-232];
- red++;
- green++;
- blue++;
+ else
+ {
+ unsigned char junk;
+
+ junk = readb(&civic_cmap_regs->lut); nop();
+ junk = readb(&civic_cmap_regs->lut); nop();
+ junk = readb(&civic_cmap_regs->lut); nop();
+ junk = readb(&civic_cmap_regs->lut); nop();
+
+ if ((clut_status & 0x000D) != 0)
+ {
+ writeb(0x00, &civic_cmap_regs->lut); nop();
+ writeb(0x00, &civic_cmap_regs->lut); nop();
+ }
+
+ writeb( red, &civic_cmap_regs->lut); nop();
+ writeb(green, &civic_cmap_regs->lut); nop();
+ writeb( blue, &civic_cmap_regs->lut); nop();
+ writeb( junk, &civic_cmap_regs->lut); nop();
}
+
+ restore_flags(flags);
+
+ lastreg = regno;
return 0;
}
-static struct fb_cmap * mac_get_default_cmap(int bpp)
+/*
+ * The CSC is the framebuffer on the PowerBook 190 series
+ * (and the 5300 too, but that's a PowerMac). This function
+ * brought to you in part by the ECSC driver for MkLinux.
+ */
+
+static int csc_setpalette (unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue)
{
- if (bpp == 1)
- return &default_2_colors;
- if (bpp == 2)
- return &default_4_colors;
- if (bpp == 4)
- return &default_16_colors;
- return &default_256_colors;
+ mdelay(1);
+ csc_cmap_regs->clut_waddr = regno;
+ csc_cmap_regs->clut_data = red;
+ csc_cmap_regs->clut_data = green;
+ csc_cmap_regs->clut_data = blue;
+ return 0;
}
-static void memcpy_fs(int fsfromto, void *to, void *from, int len)
+#endif /* FBCON_HAS_CFB8 || FBCON_HAS_CFB4 || FBCON_HAS_CFB2 */
+
+static int macfb_getcolreg(unsigned regno, unsigned *red, unsigned *green,
+ unsigned *blue, unsigned *transp,
+ struct fb_info *fb_info)
{
- switch (fsfromto) {
- case 0:
- memcpy(to, from, len);
- return;
- case 1:
- copy_from_user(to, from, len);
- return;
- case 2:
- copy_to_user(to, from, len);
- return;
- }
+ /*
+ * Read a single color register and split it into colors/transparent.
+ * Return != 0 for invalid regno.
+ */
+
+ if (regno >= video_cmap_len)
+ return 1;
+
+ *red = palette[regno].red;
+ *green = palette[regno].green;
+ *blue = palette[regno].blue;
+ *transp = 0;
+ return 0;
}
-static void copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto)
+static int macfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *fb_info)
{
- int size;
- int tooff=0, fromoff=0;
+ /*
+ * Set a single color register. The values supplied are
+ * already rounded down to the hardware's capabilities
+ * (according to the entries in the `var' structure). Return
+ * != 0 for invalid regno.
+ */
+
+ if (regno >= video_cmap_len)
+ return 1;
- if (to->start > from->start)
- fromoff=to->start-from->start;
- else
- tooff=from->start-to->start;
- size=to->len-tooff;
- if (size > from->len-fromoff)
- size=from->len-fromoff;
- if (size < 0)
+ palette[regno].red = red;
+ palette[regno].green = green;
+ palette[regno].blue = blue;
+
+ switch (video_bpp) {
+#ifdef FBCON_HAS_MFB
+ case 1:
+ /* We shouldn't get here */
+ break;
+#endif
+#ifdef FBCON_HAS_CFB2
+ case 2:
+ if (macfb_setpalette)
+ macfb_setpalette(regno, red, green, blue);
+ else
+ return 1;
+ break;
+#endif
+#ifdef FBCON_HAS_CFB4
+ case 4:
+ if (macfb_setpalette)
+ macfb_setpalette(regno, red, green, blue);
+ else
+ return 1;
+ break;
+#endif
+#ifdef FBCON_HAS_CFB8
+ case 8:
+ if (macfb_setpalette)
+ macfb_setpalette(regno, red, green, blue);
+ else
+ return 1;
+ break;
+#endif
+#ifdef FBCON_HAS_CFB16
+ case 15:
+ case 16:
+ /* 1:5:5:5 */
+ fbcon_cmap.cfb16[regno] =
+ ((red & 0xf800) >> 1) |
+ ((green & 0xf800) >> 6) |
+ ((blue & 0xf800) >> 11) |
+ ((transp != 0) << 15);
+ break;
+#endif
+ /* I'm pretty sure that one or the other of these
+ doesn't exist on 68k Macs */
+#ifdef FBCON_HAS_CFB24
+ case 24:
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+ fbcon_cmap.cfb24[regno] =
+ (red << macfb_defined.red.offset) |
+ (green << macfb_defined.green.offset) |
+ (blue << macfb_defined.blue.offset);
+ break;
+#endif
+#ifdef FBCON_HAS_CFB32
+ case 32:
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+ fbcon_cmap.cfb32[regno] =
+ (red << macfb_defined.red.offset) |
+ (green << macfb_defined.green.offset) |
+ (blue << macfb_defined.blue.offset);
+ break;
+#endif
+ }
+ return 0;
+}
+
+static void do_install_cmap(int con, struct fb_info *info)
+{
+ if (con != currcon)
return;
- size*=sizeof(unsigned short);
- memcpy_fs(fsfromto, to->red+tooff, from->red+fromoff, size);
- memcpy_fs(fsfromto, to->green+tooff, from->green+fromoff, size);
- memcpy_fs(fsfromto, to->blue+tooff, from->blue+fromoff, size);
- if (from->transp && to->transp)
- memcpy_fs(fsfromto, to->transp+tooff, from->transp+fromoff, size);
+ if (fb_display[con].cmap.len)
+ fb_set_cmap(&fb_display[con].cmap, 1, macfb_setcolreg, info);
+ else
+ fb_set_cmap(fb_default_cmap(video_cmap_len), 1,
+ macfb_setcolreg, info);
}
-
+
static int macfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
struct fb_info *info)
{
-#if 0
- printk("macfb_get_cmap: not supported!\n");
- /* interferes with X11 */
- if (console_loglevel < 7)
- return -EINVAL;
if (con == currcon) /* current console? */
- return fb_get_cmap(cmap, kspc, 0 /*offb_getcolreg*/, info);
+ return fb_get_cmap(cmap, kspc, macfb_getcolreg, info);
else if (fb_display[con].cmap.len) /* non default colormap? */
fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
else
- fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+ fb_copy_cmap(fb_default_cmap(video_cmap_len),
cmap, kspc ? 0 : 2);
-#endif
- copy_cmap(mac_get_default_cmap(fb_display[con].var.bits_per_pixel),
- cmap, kspc ? 0 : 2);
return 0;
-
}
static int macfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
struct fb_info *info)
{
-#if 0
- printk("macfb_set_cmap: not supported!\n");
- if (console_loglevel < 7)
- return -EINVAL;
+ int err;
+
if (!fb_display[con].cmap.len) { /* no colormap allocated? */
- if ((err = fb_alloc_cmap(&fb_display[con].cmap,
- 1<<fb_display[con].var.bits_per_pixel, 0)))
- return err;
+ err = fb_alloc_cmap(&fb_display[con].cmap,video_cmap_len,0);
+ if (err)
+ return err;
}
if (con == currcon) /* current console? */
- return fb_set_cmap(cmap, kspc, 1 /*offb_setcolreg*/, info);
+ return fb_set_cmap(cmap, kspc, macfb_setcolreg, info);
else
fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-#endif
return 0;
}
-static int macfb_pan_display(struct fb_var_screeninfo *var, int con,
- struct fb_info *info)
+static int macfb_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg, int con,
+ struct fb_info *info)
{
- /* no panning */
- printk("macfb_pan: not supported!\n");
return -EINVAL;
}
-static int macfb_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg, int con,
- struct fb_info *info)
+static int macfb_pan_display(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info)
{
- printk("macfb_ioctl: not supported!\n");
return -EINVAL;
}
macfb_get_cmap,
macfb_set_cmap,
macfb_pan_display,
- macfb_ioctl
+ macfb_ioctl,
+ NULL
};
-int __init macfb_setup(char *options)
+void macfb_setup(char *options, int *ints)
{
- char *this_opt;
-
- fb_info.fontname[0] = '\0';
-
- if (!options || !*options)
- return 0;
-
- for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) {
- if (!*this_opt) continue;
-
- if (! strcmp(this_opt, "inverse"))
- inverse=1;
- else if (!strncmp(this_opt, "font:", 5)) {
- strcpy(fb_info.fontname, this_opt+5);
- printk("macfb_setup: option %s\n", this_opt);
+ char *this_opt;
+
+ fb_info.fontname[0] = '\0';
+
+ if (!options || !*options)
+ return;
+
+ for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) {
+ if (!*this_opt) continue;
+
+ if (! strcmp(this_opt, "inverse"))
+ inverse=1;
+ else if (!strncmp(this_opt, "font:", 5))
+ strcpy(fb_info.fontname, this_opt+5);
+ /* This means "turn on experimental CLUT code" */
+ else if (!strcmp(this_opt, "vidtest"))
+ vidtest=1;
}
- }
- return 0;
}
static int macfb_switch(int con, struct fb_info *info)
{
- do_fb_set_var(&fb_display[con].var,1);
- currcon=con;
- return 0;
+ /* Do we have to save the colormap? */
+ if (fb_display[currcon].cmap.len)
+ fb_get_cmap(&fb_display[currcon].cmap, 1, macfb_getcolreg,
+ info);
+
+ currcon = con;
+ /* Install new colormap */
+ do_install_cmap(con, info);
+ macfb_update_var(con, info);
+ return 1;
}
-/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
-
static void macfb_blank(int blank, struct fb_info *info)
{
/* Not supported */
}
-/*
- * Nubus call back. This will give us our board identity and also
- * other useful info we need later
- */
-
-static int nubus_video_card(struct nubus_device_specifier *ns, int slot, struct nubus_type *nt)
+void __init macfb_init(void)
{
- if(nt->category==NUBUS_CAT_DISPLAY)
- return 0;
- /* Claim all video cards. We don't yet do driver specifics though. */
- return -ENODEV;
-}
-
-static struct nubus_device_specifier nb_video={
- nubus_video_card,
- NULL
-};
-
-int __init macfb_init(void)
-{
- /* nubus_remap the video .. */
+ struct nubus_dev* ndev = NULL;
+ int video_is_nubus = 0;
if (!MACH_IS_MAC)
- return -ENXIO;
-
- mac_xres=mac_bi_data.dimensions&0xFFFF;
- mac_yres=(mac_bi_data.dimensions&0xFFFF0000)>>16;
- mac_depth=mac_bi_data.videodepth;
- mac_xbytes=mac_bi_data.videorow;
- mac_vxres = (mac_xbytes/mac_depth)*8;
- mac_videosize=mac_xbytes*mac_yres;
- mac_videobase=mac_bi_data.videoaddr;
-
- printk("macfb_init: xres %d yres %d bpp %d addr %lx size %ld \n",
- mac_xres, mac_yres, mac_depth, mac_videobase, mac_videosize);
+ return;
+ /* There can only be one internal video controller anyway so
+ we're not too worried about this */
+ video_width = mac_bi_data.dimensions & 0xFFFF;
+ video_height = mac_bi_data.dimensions >> 16;
+ video_bpp = mac_bi_data.videodepth;
+ video_linelength = mac_bi_data.videorow;
+ video_size = video_linelength * video_height;
+ /* Note: physical address (since 2.1.127) */
+ video_base = (void*) mac_bi_data.videoaddr;
+ /* This is actually redundant with the initial mappings.
+ However, there are some non-obvious aspects to the way
+ those mappings are set up, so this is in fact the safest
+ way to ensure that this driver will work on every possible
+ Mac */
+ video_vbase = ioremap(mac_bi_data.videoaddr, video_size);
+
+ printk("macfb: framebuffer at 0x%p, mapped to 0x%p, size %dk\n",
+ video_base, video_vbase, video_size/1024);
+ printk("macfb: mode is %dx%dx%d, linelength=%d\n",
+ video_width, video_height, video_bpp, video_linelength);
+
/*
* Fill in the available video resolution
*/
- macfb_defined.xres=mac_xres;
- macfb_defined.yres=mac_yres;
- macfb_defined.xres_virtual=mac_vxres;
- macfb_defined.yres_virtual=mac_yres;
- macfb_defined.bits_per_pixel=mac_depth;
-
-
- /*
- * Let there be consoles..
- */
- strcpy(fb_info.modename, "Macintosh Builtin ");
- fb_info.changevar = NULL;
- fb_info.node = -1;
- fb_info.fbops = &macfb_ops;
- fb_info.disp=&disp;
- fb_info.switch_con=&macfb_switch;
- fb_info.updatevar=&fb_update_var;
- fb_info.blank=&macfb_blank;
- fb_info.flags = FBINFO_FLAG_DEFAULT;
- do_fb_set_var(&macfb_defined,1);
-
- macfb_get_var(&disp.var, -1, &fb_info);
- macfb_set_disp(-1);
-
- /*
- * Fill in the 8 bit color table if required
- */
- if (mac_depth == 8)
- mac_set_cmap256(&default_256_colors);
-
- /*
- * Register the nubus hook
+ macfb_defined.xres = video_width;
+ macfb_defined.yres = video_height;
+ macfb_defined.xres_virtual = video_width;
+ macfb_defined.yres_virtual = video_height;
+ macfb_defined.bits_per_pixel = video_bpp;
+ macfb_defined.height = PIXEL_TO_MM(macfb_defined.yres);
+ macfb_defined.width = PIXEL_TO_MM(macfb_defined.xres);
+
+ printk("macfb: scrolling: redraw\n");
+ macfb_defined.yres_virtual = video_height;
+
+ /* some dummy values for timing to make fbset happy */
+ macfb_defined.pixclock = 10000000 / video_width * 1000 / video_height;
+ macfb_defined.left_margin = (video_width / 8) & 0xf8;
+ macfb_defined.right_margin = 32;
+ macfb_defined.upper_margin = 16;
+ macfb_defined.lower_margin = 4;
+ macfb_defined.hsync_len = (video_width / 8) & 0xf8;
+ macfb_defined.vsync_len = 4;
+
+ switch (video_bpp) {
+ case 1:
+ /* XXX: I think this will catch any program that tries
+ to do FBIO_PUTCMAP when the visual is monochrome */
+ video_cmap_len = 0;
+ video_visual = FB_VISUAL_MONO01;
+ break;
+ case 2:
+ case 4:
+ case 8:
+ macfb_defined.red.length = video_bpp;
+ macfb_defined.green.length = video_bpp;
+ macfb_defined.blue.length = video_bpp;
+ video_cmap_len = 1 << video_bpp;
+ video_visual = FB_VISUAL_PSEUDOCOLOR;
+ break;
+ case 16:
+ macfb_defined.transp.offset = 15;
+ macfb_defined.transp.length = 1;
+ macfb_defined.red.offset = 10;
+ macfb_defined.red.length = 5;
+ macfb_defined.green.offset = 5;
+ macfb_defined.green.length = 5;
+ macfb_defined.blue.offset = 0;
+ macfb_defined.blue.length = 5;
+ printk("macfb: directcolor: "
+ "size=1:5:5:5, shift=15:10:5:0\n");
+ video_cmap_len = 16;
+ /* Should actually be FB_VISUAL_DIRECTCOLOR, but this
+ works too */
+ video_visual = FB_VISUAL_TRUECOLOR;
+ break;
+ case 24:
+ case 32:
+ /* XXX: have to test these... can any 68k Macs
+ actually do this on internal video? */
+ macfb_defined.red.offset = 16;
+ macfb_defined.red.length = 8;
+ macfb_defined.green.offset = 8;
+ macfb_defined.green.length = 8;
+ macfb_defined.blue.offset = 0;
+ macfb_defined.blue.length = 8;
+ printk("macfb: truecolor: "
+ "size=0:8:8:8, shift=0:16:8:0\n");
+ video_cmap_len = 16;
+ video_visual = FB_VISUAL_TRUECOLOR;
+ default:
+ video_cmap_len = 0;
+ video_visual = FB_VISUAL_MONO01;
+ printk("macfb: unknown or unsupported bit depth: %d\n", video_bpp);
+ break;
+ }
+
+ /* Hardware dependent stuff */
+ /* We take a wild guess that if the video physical address is
+ * in nubus slot space, that the nubus card is driving video.
+ * Penguin really ought to tell us whether we are using internal
+ * video or not.
*/
-
- register_nubus_device(&nb_video);
+ /* Hopefully we only find one of them. Otherwise our NuBus
+ code is really broken :-) */
- if (register_framebuffer(&fb_info) < 0)
+ while ((ndev = nubus_find_type(NUBUS_CAT_DISPLAY, NUBUS_TYPE_VIDEO, ndev))
+ != NULL)
{
- return -EINVAL;
+ if (!(mac_bi_data.videoaddr >= ndev->board->slot_addr
+ && (mac_bi_data.videoaddr <
+ (unsigned long)nubus_slot_addr(ndev->board->slot+1))))
+ continue;
+ video_is_nubus = 1;
+ /* We should probably just use the slot address... */
+ video_slot = ndev->board->slot;
+
+ switch(ndev->dr_hw) {
+ case NUBUS_DRHW_APPLE_MDC:
+ strcpy( fb_info.modename, "Macintosh Display Card" );
+ macfb_setpalette = mdc_setpalette;
+ macfb_defined.activate = FB_ACTIVATE_NOW;
+ break;
+ case NUBUS_DRHW_APPLE_TFB:
+ strcpy( fb_info.modename, "Toby" );
+ macfb_setpalette = toby_setpalette;
+ macfb_defined.activate = FB_ACTIVATE_NOW;
+ break;
+ case NUBUS_DRHW_APPLE_JET:
+ strcpy(fb_info.modename, "Jet");
+ macfb_setpalette = jet_setpalette;
+ macfb_defined.activate = FB_ACTIVATE_NOW;
+ break;
+ default:
+ strcpy( fb_info.modename, "Generic NuBus" );
+ break;
+ }
}
- printk("fb%d: %s frame buffer device using %ldK of video memory\n",
- GET_FB_IDX(fb_info.node), fb_info.modename, mac_videosize>>10);
- return 0;
-}
+ /* If it's not a NuBus card, it must be internal video */
+ /* FIXME: this function is getting way too big. (this driver
+ is too...) */
+ if (!video_is_nubus)
+ switch( mac_bi_data.id )
+ {
+ /* These don't have onboard video. Eventually, we may
+ be able to write separate framebuffer drivers for
+ them (tobyfb.c, hiresfb.c, etc, etc) */
+ case MAC_MODEL_II:
+ case MAC_MODEL_IIX:
+ case MAC_MODEL_IICX:
+ case MAC_MODEL_IIFX:
+ strcpy( fb_info.modename, "Generic NuBus" );
+ break;
+
+ /* Valkyrie Quadras */
+ case MAC_MODEL_Q630:
+ /* I'm not sure about this one */
+ case MAC_MODEL_P588:
+ strcpy( fb_info.modename, "Valkyrie built-in" );
+ macfb_setpalette = valkyrie_setpalette;
+ macfb_defined.activate = FB_ACTIVATE_NOW;
+ valkyrie_cmap_regs = ioremap(DAC_BASE, 0x1000);
+ break;
+
+ /* DAFB Quadras */
+ /* Note: these first four have the v7 DAFB, which is
+ known to be rather unlike the ones used in the
+ other models */
+ case MAC_MODEL_P475:
+ case MAC_MODEL_P475F:
+ case MAC_MODEL_P575:
+ case MAC_MODEL_Q605:
+
+ case MAC_MODEL_Q800:
+ case MAC_MODEL_Q650:
+ case MAC_MODEL_Q610:
+ case MAC_MODEL_C650:
+ case MAC_MODEL_C610:
+ case MAC_MODEL_Q700:
+ case MAC_MODEL_Q900:
+ case MAC_MODEL_Q950:
+ strcpy( fb_info.modename, "DAFB built-in" );
+ macfb_setpalette = dafb_setpalette;
+ macfb_defined.activate = FB_ACTIVATE_NOW;
+ dafb_cmap_regs = ioremap(DAFB_BASE, 0x1000);
+ break;
+
+ /* LC II uses the V8 framebuffer */
+ case MAC_MODEL_LCII:
+ strcpy( fb_info.modename, "V8 built-in" );
+ macfb_setpalette = v8_brazil_setpalette;
+ macfb_defined.activate = FB_ACTIVATE_NOW;
+ v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000);
+ break;
+
+ /* IIvi, IIvx use the "Brazil" framebuffer (which is
+ very much like the V8, it seems, and probably uses
+ the same DAC) */
+ case MAC_MODEL_IIVI:
+ case MAC_MODEL_IIVX:
+ case MAC_MODEL_P600:
+ strcpy( fb_info.modename, "Brazil built-in" );
+ macfb_setpalette = v8_brazil_setpalette;
+ macfb_defined.activate = FB_ACTIVATE_NOW;
+ v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000);
+ break;
+
+ /* LC III (and friends) use the Sonora framebuffer */
+ /* Incidentally this is also used in the non-AV models
+ of the x100 PowerMacs */
+ /* These do in fact seem to use the same DAC interface
+ as the LC II. */
+ case MAC_MODEL_LCIII:
+ case MAC_MODEL_P520:
+ case MAC_MODEL_P550:
+ case MAC_MODEL_P460:
+ macfb_setpalette = v8_brazil_setpalette;
+ macfb_defined.activate = FB_ACTIVATE_NOW;
+ strcpy( fb_info.modename, "Sonora built-in" );
+ v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000);
+ break;
+
+ /* IIci and IIsi use the infamous RBV chip
+ (the IIsi is just a rebadged and crippled
+ IIci in a different case, BTW) */
+ case MAC_MODEL_IICI:
+ case MAC_MODEL_IISI:
+ macfb_setpalette = rbv_setpalette;
+ macfb_defined.activate = FB_ACTIVATE_NOW;
+ strcpy( fb_info.modename, "RBV built-in" );
+ rbv_cmap_regs = ioremap(DAC_BASE, 0x1000);
+ break;
+
+ /* AVs use the Civic framebuffer */
+ case MAC_MODEL_Q840:
+ case MAC_MODEL_C660:
+ macfb_setpalette = civic_setpalette;
+ macfb_defined.activate = FB_ACTIVATE_NOW;
+ strcpy( fb_info.modename, "Civic built-in" );
+ civic_cmap_regs = ioremap(CIVIC_BASE, 0x1000);
+ break;
+
+
+ /* Write a setpalette function for your machine, then
+ you can add something similar here. These are
+ grouped by classes of video chipsets. Some of this
+ information is from the VideoToolbox "Bugs" web
+ page at
+ http://rajsky.psych.nyu.edu/Tips/VideoBugs.html */
+
+ /* Assorted weirdos */
+ /* We think this may be like the LC II */
+ case MAC_MODEL_LC:
+ if (vidtest) {
+ macfb_setpalette = v8_brazil_setpalette;
+ macfb_defined.activate = FB_ACTIVATE_NOW;
+ v8_brazil_cmap_regs =
+ ioremap(DAC_BASE, 0x1000);
+ }
+ strcpy( fb_info.modename, "LC built-in" );
+ break;
+ /* We think this may be like the LC II */
+ case MAC_MODEL_CCL:
+ if (vidtest) {
+ macfb_setpalette = v8_brazil_setpalette;
+ macfb_defined.activate = FB_ACTIVATE_NOW;
+ v8_brazil_cmap_regs =
+ ioremap(DAC_BASE, 0x1000);
+ }
+ strcpy( fb_info.modename, "Color Classic built-in" );
+ break;
+
+ /* And we *do* mean "weirdos" */
+ case MAC_MODEL_TV:
+ strcpy( fb_info.modename, "Mac TV built-in" );
+ break;
+
+ /* These don't have colour, so no need to worry */
+ case MAC_MODEL_SE30:
+ case MAC_MODEL_CLII:
+ strcpy( fb_info.modename, "Monochrome built-in" );
+ break;
+
+ /* Powerbooks are particularly difficult. Many of
+ them have separate framebuffers for external and
+ internal video, which is admittedly pretty cool,
+ but will be a bit of a headache to support here.
+ Also, many of them are grayscale, and we don't
+ really support that. */
+
+ case MAC_MODEL_PB140:
+ case MAC_MODEL_PB145:
+ case MAC_MODEL_PB170:
+ strcpy( fb_info.modename, "DDC built-in" );
+ break;
+
+ /* Internal is GSC, External (if present) is ViSC */
+ case MAC_MODEL_PB150: /* no external video */
+ case MAC_MODEL_PB160:
+ case MAC_MODEL_PB165:
+ case MAC_MODEL_PB180:
+ case MAC_MODEL_PB210:
+ case MAC_MODEL_PB230:
+ strcpy( fb_info.modename, "GSC built-in" );
+ break;
+
+ /* Internal is TIM, External is ViSC */
+ case MAC_MODEL_PB165C:
+ case MAC_MODEL_PB180C:
+ strcpy( fb_info.modename, "TIM built-in" );
+ break;
+
+ /* Internal is CSC, External is Keystone+Ariel. */
+ case MAC_MODEL_PB190: /* external video is optional */
+ case MAC_MODEL_PB520:
+ case MAC_MODEL_PB250:
+ case MAC_MODEL_PB270C:
+ case MAC_MODEL_PB280:
+ case MAC_MODEL_PB280C:
+ macfb_setpalette = csc_setpalette;
+ macfb_defined.activate = FB_ACTIVATE_NOW;
+ strcpy( fb_info.modename, "CSC built-in" );
+ csc_cmap_regs = ioremap(CSC_BASE, 0x1000);
+ break;
+
+ default:
+ strcpy( fb_info.modename, "Unknown/Unsupported built-in" );
+ break;
+ }
+
+ fb_info.changevar = NULL;
+ fb_info.node = -1;
+ fb_info.fbops = &macfb_ops;
+ fb_info.disp = &disp;
+ fb_info.switch_con = &macfb_switch;
+ fb_info.updatevar = &macfb_update_var;
+ fb_info.blank = &macfb_blank;
+ fb_info.flags = FBINFO_FLAG_DEFAULT;
+ macfb_set_disp(-1);
+ do_install_cmap(0, &fb_info);
+
+ if (register_framebuffer(&fb_info) < 0)
+ return;
-#if 0
-/*
- * These two auxiliary debug functions should go away ASAP. Only usage:
- * before the console output is up (after head.S come some other crucial
- * setup routines :-)
- *
- * Now in debug.c ...
- */
-#endif
+ printk("fb%d: %s frame buffer device\n",
+ GET_FB_IDX(fb_info.node), fb_info.modename);
+}
--- /dev/null
+/*
+ * linux/drivers/video/sun3fb.c -- Frame buffer driver for Sun3
+ *
+ * (C) 1998 Thomas Bogendoerfer
+ *
+ * This driver is bases on sbusfb.c, which is
+ *
+ * Copyright (C) 1998 Jakub Jelinek
+ *
+ * This driver is partly based on the Open Firmware console driver
+ *
+ * Copyright (C) 1997 Geert Uytterhoeven
+ *
+ * and SPARC console subsystem
+ *
+ * Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su)
+ * Copyright (C) 1995-1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995-1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
+ * Copyright (C) 1996-1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/selection.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/kd.h>
+#include <linux/vt_kern.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h> /* io_remap_page_range() */
+
+#ifdef CONFIG_SUN3
+#include <asm/oplib.h>
+#endif
+#ifdef CONFIG_SUN3X
+#include <asm/sun3x.h>
+#endif
+#include <video/sbusfb.h>
+
+#define DEFAULT_CURSOR_BLINK_RATE (2*HZ/5)
+
+#define CURSOR_SHAPE 1
+#define CURSOR_BLINK 2
+
+ /*
+ * Interface used by the world
+ */
+
+int sun3fb_init(void);
+int sun3fb_setup(char *options);
+
+static int currcon;
+static char fontname[40] __initdata = { 0 };
+static int curblink __initdata = 1;
+
+static int sun3fb_open(struct fb_info *info, int user);
+static int sun3fb_release(struct fb_info *info, int user);
+static int sun3fb_get_fix(struct fb_fix_screeninfo *fix, int con,
+ struct fb_info *info);
+static int sun3fb_get_var(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info);
+static int sun3fb_set_var(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info);
+static int sun3fb_pan_display(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info);
+static int sun3fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info);
+static int sun3fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info);
+static int sun3fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+ u_long arg, int con, struct fb_info *info);
+static void sun3fb_cursor(struct display *p, int mode, int x, int y);
+static void sun3fb_clear_margin(struct display *p, int s);
+
+
+ /*
+ * Interface to the low level console driver
+ */
+
+static int sun3fbcon_switch(int con, struct fb_info *info);
+static int sun3fbcon_updatevar(int con, struct fb_info *info);
+static void sun3fbcon_blank(int blank, struct fb_info *info);
+
+
+ /*
+ * Internal routines
+ */
+
+static int sun3fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp, struct fb_info *info);
+static int sun3fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info);
+static void do_install_cmap(int con, struct fb_info *info);
+
+static struct fb_ops sun3fb_ops = {
+ sun3fb_open, sun3fb_release, sun3fb_get_fix, sun3fb_get_var, sun3fb_set_var,
+ sun3fb_get_cmap, sun3fb_set_cmap, sun3fb_pan_display, sun3fb_ioctl
+};
+
+
+ /*
+ * Open/Release the frame buffer device
+ */
+
+static int sun3fb_open(struct fb_info *info, int user)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int sun3fb_release(struct fb_info *info, int user)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static void sun3fb_clear_margin(struct display *p, int s)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfod(p);
+
+ if (fb->switch_from_graph)
+ (*fb->switch_from_graph)(fb);
+ if (fb->fill) {
+ unsigned short rects [16];
+
+ rects [0] = 0;
+ rects [1] = 0;
+ rects [2] = fb->var.xres_virtual;
+ rects [3] = fb->y_margin;
+ rects [4] = 0;
+ rects [5] = fb->y_margin;
+ rects [6] = fb->x_margin;
+ rects [7] = fb->var.yres_virtual;
+ rects [8] = fb->var.xres_virtual - fb->x_margin;
+ rects [9] = fb->y_margin;
+ rects [10] = fb->var.xres_virtual;
+ rects [11] = fb->var.yres_virtual;
+ rects [12] = fb->x_margin;
+ rects [13] = fb->var.yres_virtual - fb->y_margin;
+ rects [14] = fb->var.xres_virtual - fb->x_margin;
+ rects [15] = fb->var.yres_virtual;
+ (*fb->fill)(fb, p, s, 4, rects);
+ } else {
+ unsigned char *fb_base = p->screen_base, *q;
+ int skip_bytes = fb->y_margin * fb->var.xres_virtual;
+ int scr_size = fb->var.xres_virtual * fb->var.yres_virtual;
+ int h, he, incr, size;
+
+ he = fb->var.yres;
+ if (fb->var.bits_per_pixel == 1) {
+ fb_base -= (skip_bytes + fb->x_margin) / 8;
+ skip_bytes /= 8;
+ scr_size /= 8;
+ mymemset (fb_base, skip_bytes - fb->x_margin / 8);
+ mymemset (fb_base + scr_size - skip_bytes + fb->x_margin / 8, skip_bytes - fb->x_margin / 8);
+ incr = fb->var.xres_virtual / 8;
+ size = fb->x_margin / 8 * 2;
+ for (q = fb_base + skip_bytes - fb->x_margin / 8, h = 0;
+ h <= he; q += incr, h++)
+ mymemset (q, size);
+ } else {
+ fb_base -= (skip_bytes + fb->x_margin);
+ memset (fb_base, attr_bgcol(p,s), skip_bytes - fb->x_margin);
+ memset (fb_base + scr_size - skip_bytes + fb->x_margin, attr_bgcol(p,s), skip_bytes - fb->x_margin);
+ incr = fb->var.xres_virtual;
+ size = fb->x_margin * 2;
+ for (q = fb_base + skip_bytes - fb->x_margin, h = 0;
+ h <= he; q += incr, h++)
+ memset (q, attr_bgcol(p,s), size);
+ }
+ }
+}
+
+static void sun3fb_disp_setup(struct display *p)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfod(p);
+
+ if (fb->setup)
+ fb->setup(p);
+ sun3fb_clear_margin(p, 0);
+}
+
+ /*
+ * Get the Fixed Part of the Display
+ */
+
+static int sun3fb_get_fix(struct fb_fix_screeninfo *fix, int con,
+ struct fb_info *info)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ memcpy(fix, &fb->fix, sizeof(struct fb_fix_screeninfo));
+ return 0;
+}
+
+ /*
+ * Get the User Defined Part of the Display
+ */
+
+static int sun3fb_get_var(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ memcpy(var, &fb->var, sizeof(struct fb_var_screeninfo));
+ return 0;
+}
+
+ /*
+ * Set the User Defined Part of the Display
+ */
+
+static int sun3fb_set_var(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ if (var->xres > fb->var.xres || var->yres > fb->var.yres ||
+ var->xres_virtual > fb->var.xres_virtual ||
+ var->yres_virtual > fb->var.yres_virtual ||
+ var->bits_per_pixel != fb->var.bits_per_pixel ||
+ var->nonstd ||
+ (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
+ return -EINVAL;
+ memcpy(var, &fb->var, sizeof(struct fb_var_screeninfo));
+ return 0;
+}
+
+ /*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ */
+
+static int sun3fb_pan_display(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info)
+{
+ if (var->xoffset || var->yoffset)
+ return -EINVAL;
+ else
+ return 0;
+}
+
+ /*
+ * Hardware cursor
+ */
+
+static unsigned char hw_cursor_cmap[2] = { 0, 0xff };
+
+static void
+sun3fb_cursor_timer_handler(unsigned long dev_addr)
+{
+ struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)dev_addr;
+
+ if (!fb->setcursor) return;
+
+ if (fb->cursor.mode & CURSOR_BLINK) {
+ fb->cursor.enable ^= 1;
+ fb->setcursor(fb);
+ }
+
+ fb->cursor.timer.expires = jiffies + fb->cursor.blink_rate;
+ add_timer(&fb->cursor.timer);
+}
+
+static void sun3fb_cursor(struct display *p, int mode, int x, int y)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfod(p);
+
+ switch (mode) {
+ case CM_ERASE:
+ fb->cursor.mode &= ~CURSOR_BLINK;
+ fb->cursor.enable = 0;
+ (*fb->setcursor)(fb);
+ break;
+
+ case CM_MOVE:
+ case CM_DRAW:
+ if (fb->cursor.mode & CURSOR_SHAPE) {
+ fb->cursor.size.fbx = fontwidth(p);
+ fb->cursor.size.fby = fontheight(p);
+ fb->cursor.chot.fbx = 0;
+ fb->cursor.chot.fby = 0;
+ fb->cursor.enable = 1;
+ memset (fb->cursor.bits, 0, sizeof (fb->cursor.bits));
+ fb->cursor.bits[0][fontheight(p) - 2] = (0xffffffff << (32 - fontwidth(p)));
+ fb->cursor.bits[1][fontheight(p) - 2] = (0xffffffff << (32 - fontwidth(p)));
+ fb->cursor.bits[0][fontheight(p) - 1] = (0xffffffff << (32 - fontwidth(p)));
+ fb->cursor.bits[1][fontheight(p) - 1] = (0xffffffff << (32 - fontwidth(p)));
+ (*fb->setcursormap) (fb, hw_cursor_cmap, hw_cursor_cmap, hw_cursor_cmap);
+ (*fb->setcurshape) (fb);
+ }
+ fb->cursor.mode = CURSOR_BLINK;
+ if (fontwidthlog(p))
+ fb->cursor.cpos.fbx = (x << fontwidthlog(p)) + fb->x_margin;
+ else
+ fb->cursor.cpos.fbx = (x * fontwidth(p)) + fb->x_margin;
+ if (fontheightlog(p))
+ fb->cursor.cpos.fby = (y << fontheightlog(p)) + fb->y_margin;
+ else
+ fb->cursor.cpos.fby = (y * fontheight(p)) + fb->y_margin;
+ (*fb->setcursor)(fb);
+ break;
+ }
+}
+
+ /*
+ * Get the Colormap
+ */
+
+static int sun3fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info)
+{
+ if (con == currcon) /* current console? */
+ return fb_get_cmap(cmap, kspc, sun3fb_getcolreg, info);
+ else if (fb_display[con].cmap.len) /* non default colormap? */
+ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+ else
+ fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2);
+ return 0;
+}
+
+ /*
+ * Set the Colormap
+ */
+
+static int sun3fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info)
+{
+ int err;
+
+ if (!fb_display[con].cmap.len) { /* no colormap allocated? */
+ if ((err = fb_alloc_cmap(&fb_display[con].cmap, 1<<fb_display[con].var.bits_per_pixel, 0)))
+ return err;
+ }
+ if (con == currcon) { /* current console? */
+ err = fb_set_cmap(cmap, kspc, sun3fb_setcolreg, info);
+ if (!err) {
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ if (fb->loadcmap)
+ (*fb->loadcmap)(fb, &fb_display[con], cmap->start, cmap->len);
+ }
+ return err;
+ } else
+ fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+ return 0;
+}
+
+static int sun3fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+ u_long arg, int con, struct fb_info *info)
+{
+ return -EINVAL;
+}
+
+ /*
+ * Setup: parse used options
+ */
+
+__initfunc(void sun3fb_setup(char *options))
+{
+ char *p;
+
+ for (p = options;;) {
+ if (!strncmp(p, "font=", 5)) {
+ int i;
+
+ for (i = 0; i < sizeof(fontname) - 1; i++)
+ if (p[i+5] == ' ' || !p[i+5])
+ break;
+ memcpy(fontname, p+5, i);
+ fontname[i] = 0;
+ } else if (!strncmp(p, "noblink", 7))
+ curblink = 0;
+ while (*p && *p != ' ' && *p != ',') p++;
+ if (*p != ',') break;
+ p++;
+ }
+
+ return 0;
+}
+
+static int sun3fbcon_switch(int con, struct fb_info *info)
+{
+ int x_margin, y_margin;
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+ int lastconsole;
+
+ /* Do we have to save the colormap? */
+ if (fb_display[currcon].cmap.len)
+ fb_get_cmap(&fb_display[currcon].cmap, 1, sun3fb_getcolreg, info);
+
+ if (info->display_fg) {
+ lastconsole = info->display_fg->vc_num;
+ if (lastconsole != con &&
+ (fontwidth(&fb_display[lastconsole]) != fontwidth(&fb_display[con]) ||
+ fontheight(&fb_display[lastconsole]) != fontheight(&fb_display[con])))
+ fb->cursor.mode |= CURSOR_SHAPE;
+ }
+ x_margin = (fb_display[con].var.xres_virtual - fb_display[con].var.xres) / 2;
+ y_margin = (fb_display[con].var.yres_virtual - fb_display[con].var.yres) / 2;
+ if (fb->margins)
+ fb->margins(fb, &fb_display[con], x_margin, y_margin);
+ if (fb->graphmode || fb->x_margin != x_margin || fb->y_margin != y_margin) {
+ fb->x_margin = x_margin; fb->y_margin = y_margin;
+ sun3fb_clear_margin(&fb_display[con], 0);
+ }
+ currcon = con;
+ /* Install new colormap */
+ do_install_cmap(con, info);
+ return 0;
+}
+
+ /*
+ * Update the `var' structure (called by fbcon.c)
+ */
+
+static int sun3fbcon_updatevar(int con, struct fb_info *info)
+{
+ /* Nothing */
+ return 0;
+}
+
+ /*
+ * Blank the display.
+ */
+
+static void sun3fbcon_blank(int blank, struct fb_info *info)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ if (blank && fb->blank)
+ return fb->blank(fb);
+ else if (!blank && fb->unblank)
+ return fb->unblank(fb);
+}
+
+ /*
+ * Read a single color register and split it into
+ * colors/transparent. Return != 0 for invalid regno.
+ */
+
+static int sun3fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp, struct fb_info *info)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ if (!fb->color_map || regno > 255)
+ return 1;
+ *red = (fb->color_map CM(regno, 0)<<8) | fb->color_map CM(regno, 0);
+ *green = (fb->color_map CM(regno, 1)<<8) | fb->color_map CM(regno, 1);
+ *blue = (fb->color_map CM(regno, 2)<<8) | fb->color_map CM(regno, 2);
+ *transp = 0;
+ return 0;
+}
+
+
+ /*
+ * Set a single color register. The values supplied are already
+ * rounded down to the hardware's capabilities (according to the
+ * entries in the var structure). Return != 0 for invalid regno.
+ */
+
+static int sun3fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ if (!fb->color_map || regno > 255)
+ return 1;
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+ fb->color_map CM(regno, 0) = red;
+ fb->color_map CM(regno, 1) = green;
+ fb->color_map CM(regno, 2) = blue;
+ return 0;
+}
+
+
+static void do_install_cmap(int con, struct fb_info *info)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ if (con != currcon)
+ return;
+ if (fb_display[con].cmap.len)
+ fb_set_cmap(&fb_display[con].cmap, 1, sun3fb_setcolreg, info);
+ else
+ fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+ 1, sun3fb_setcolreg, info);
+ if (fb->loadcmap)
+ (*fb->loadcmap)(fb, &fb_display[con], 0, 256);
+}
+
+static int sun3fb_set_font(struct display *p, int width, int height)
+{
+ int w = p->var.xres_virtual, h = p->var.yres_virtual;
+ int depth = p->var.bits_per_pixel;
+ struct fb_info_sbusfb *fb = sbusfbinfod(p);
+ int x_margin, y_margin;
+
+ if (depth > 8) depth = 8;
+ x_margin = (w % width) / 2;
+ y_margin = (h % height) / 2;
+
+ p->var.xres = w - 2*x_margin;
+ p->var.yres = h - 2*y_margin;
+
+ fb->cursor.mode |= CURSOR_SHAPE;
+
+ if (fb->margins)
+ fb->margins(fb, p, x_margin, y_margin);
+ if (fb->x_margin != x_margin || fb->y_margin != y_margin) {
+ fb->x_margin = x_margin; fb->y_margin = y_margin;
+ sun3fb_clear_margin(p, 0);
+ }
+
+ return 1;
+}
+
+void sun3fb_palette(int enter)
+{
+ int i;
+ struct display *p;
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ p = &fb_display[i];
+ if (p->dispsw && p->dispsw->setup == sun3fb_disp_setup &&
+ p->fb_info->display_fg &&
+ p->fb_info->display_fg->vc_num == i) {
+ struct fb_info_sbusfb *fb = sbusfbinfod(p);
+
+ if (fb->restore_palette) {
+ if (enter)
+ fb->restore_palette(fb);
+ else if (vt_cons[i]->vc_mode != KD_GRAPHICS)
+ vc_cons[i].d->vc_sw->con_set_palette(vc_cons[i].d, color_table);
+ }
+ }
+ }
+}
+
+ /*
+ * Initialisation
+ */
+__initfunc(static void sun3fb_init_fb(int fbtype, unsigned long addr))
+{
+ static struct linux_sbus_device sdb;
+ struct fb_fix_screeninfo *fix;
+ struct fb_var_screeninfo *var;
+ struct display *disp;
+ struct fb_info_sbusfb *fb;
+ struct fbtype *type;
+ int linebytes, w, h, depth;
+ char *p = NULL;
+
+ fb = kmalloc(sizeof(struct fb_info_sbusfb), GFP_ATOMIC);
+ if (!fb)
+ return -ENOMEM;
+
+ memset(fb, 0, sizeof(struct fb_info_sbusfb));
+ fix = &fb->fix;
+ var = &fb->var;
+ disp = &fb->disp;
+ type = &fb->type;
+
+ sdb.reg_addrs[0].phys_addr = addr;
+ fb->sbdp = &sdb;
+
+ type->fb_type = fbtype;
+
+ type->fb_height = h = 900;
+ type->fb_width = w = 1152;
+sizechange:
+ type->fb_depth = depth = (fbtype == FBTYPE_SUN2BW) ? 1 : 8;
+ linebytes = w * depth / 8;
+ type->fb_size = PAGE_ALIGN((linebytes) * h);
+
+ fb->x_margin = (w & 7) / 2;
+ fb->y_margin = (h & 15) / 2;
+
+ var->xres_virtual = w;
+ var->yres_virtual = h;
+ var->xres = w - 2*fb->x_margin;
+ var->yres = h - 2*fb->y_margin;
+
+ var->bits_per_pixel = depth;
+ var->height = var->width = -1;
+ var->pixclock = 10000;
+ var->vmode = FB_VMODE_NONINTERLACED;
+ var->red.length = var->green.length = var->blue.length = 8;
+
+ fix->line_length = linebytes;
+ fix->smem_len = type->fb_size;
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+
+ fb->info.node = -1;
+ fb->info.fbops = &sun3fb_ops;
+ fb->info.disp = disp;
+ strcpy(fb->info.fontname, fontname);
+ fb->info.changevar = NULL;
+ fb->info.switch_con = &sun3fbcon_switch;
+ fb->info.updatevar = &sun3fbcon_updatevar;
+ fb->info.blank = &sun3fbcon_blank;
+ fb->info.flags = FBINFO_FLAG_DEFAULT;
+
+ fb->cursor.hwsize.fbx = 32;
+ fb->cursor.hwsize.fby = 32;
+
+ if (depth > 1 && !fb->color_map)
+ fb->color_map = kmalloc(256 * 3, GFP_ATOMIC);
+
+ switch(fbtype) {
+#ifdef CONFIG_FB_CGSIX
+ case FBTYPE_SUNFAST_COLOR:
+ p = cgsixfb_init(fb); break;
+#endif
+#ifdef CONFIG_FB_BWTWO
+ case FBTYPE_SUN2BW:
+ p = bwtwofb_init(fb); break;
+#endif
+ }
+ fix->smem_start = fb->disp.screen_base;
+
+ if (!p) {
+ kfree(fb);
+ -ENODEV;
+ }
+
+ if (p == SBUSFBINIT_SIZECHANGE)
+ goto sizechange;
+
+ disp->dispsw = &fb->dispsw;
+ if (fb->setcursor) {
+ fb->dispsw.cursor = sun3fb_cursor;
+ if (curblink) {
+ fb->cursor.blink_rate = DEFAULT_CURSOR_BLINK_RATE;
+ init_timer(&fb->cursor.timer);
+ fb->cursor.timer.expires = jiffies + fb->cursor.blink_rate;
+ fb->cursor.timer.data = (unsigned long)fb;
+ fb->cursor.timer.function = sun3fb_cursor_timer_handler;
+ add_timer(&fb->cursor.timer);
+ }
+ }
+ fb->cursor.mode = CURSOR_SHAPE;
+ fb->dispsw.set_font = sun3fb_set_font;
+ fb->setup = fb->dispsw.setup;
+ fb->dispsw.setup = sun3fb_disp_setup;
+ fb->dispsw.clear_margins = NULL;
+
+ disp->var = *var;
+ disp->visual = fix->visual;
+ disp->type = fix->type;
+ disp->type_aux = fix->type_aux;
+ disp->line_length = fix->line_length;
+
+ if (fb->blank)
+ disp->can_soft_blank = 1;
+
+ sun3fb_set_var(var, -1, &fb->info);
+
+ if (register_framebuffer(&fb->info) < 0) {
+ kfree(fb);
+ return -EINVAL;
+ }
+ printk("fb%d: %s\n", GET_FB_IDX(fb->info.node), p);
+
+ return 0;
+}
+
+
+__initfunc(int sun3fb_init(void))
+{
+ extern int con_is_present(void);
+ unsigned long addr;
+ char p4id;
+
+ if (!con_is_present()) return;
+ printk("sun3fb_init()\n");
+#ifdef CONFIG_SUN3
+ addr = 0xfe20000;
+ switch(*(romvec->pv_fbtype))
+ {
+ case FBTYPE_SUN2BW:
+ return sun3fb_init_fb(FBTYPE_SUN2BW, addr);
+ case FBTYPE_SUN3COLOR:
+ printk("cg3 detected but not supported\n");
+ return -EINVAL;
+ }
+#else
+ addr = SUN3X_VIDEO_BASE;
+ p4id = *(char *)SUN3X_VIDEO_P4ID;
+
+ p4id = (p4id == 0x45) ? p4id : (p4id & 0xf0);
+ switch (p4id) {
+ case 0x00:
+ return sun3fb_init_fb(FBTYPE_SUN2BW, addr);
+#if 0 /* not yet */
+ case 0x40:
+ sun3fb_init_fb(FBTYPE_SUN4COLOR, addr);
+ break;
+ case 0x45:
+ sun3fb_init_fb(FBTYPE_SUN8COLOR, addr);
+ break;
+#endif
+ case 0x60:
+ return sun3fb_init_fb(FBTYPE_SUNFAST_COLOR, addr);
+ }
+#endif
+}
#define flush_cache_page(vma, vmaddr) do { } while (0)
#define flush_page_to_ram(page) do { } while (0)
#define flush_icache_range(start, end) do { } while (0)
+#define flush_icache_page(vma,pg) do { } while (0)
#define __flush_tlb() \
do { \
--- /dev/null
+/*
+ * ADB through the IOP
+ * Written by Joshua M. Thompson
+ */
+
+/* IOP number and channel number for ADB */
+
+#define ADB_IOP IOP_NUM_ISM
+#define ADB_CHAN 2
+
+/* From the A/UX headers...maybe important, maybe not */
+
+#define ADB_IOP_LISTEN 0x01
+#define ADB_IOP_TALK 0x02
+#define ADB_IOP_EXISTS 0x04
+#define ADB_IOP_FLUSH 0x08
+#define ADB_IOP_RESET 0x10
+#define ADB_IOP_INT 0x20
+#define ADB_IOP_POLL 0x40
+#define ADB_IOP_UNINT 0x80
+
+#define AIF_RESET 0x00
+#define AIF_FLUSH 0x01
+#define AIF_LISTEN 0x08
+#define AIF_TALK 0x0C
+
+/* Flag bits in struct adb_iopmsg */
+
+#define ADB_IOP_EXPLICIT 0x80 /* nonzero if explicit command */
+#define ADB_IOP_AUTOPOLL 0x40 /* auto/SRQ polling enabled */
+#define ADB_IOP_SRQ 0x04 /* SRQ detected */
+#define ADB_IOP_TIMEOUT 0x02 /* nonzero if timeout */
+
+#ifndef __ASSEMBLY__
+
+struct adb_iopmsg {
+ __u8 flags; /* ADB flags */
+ __u8 count; /* no. of data bytes */
+ __u8 cmd; /* ADB command */
+ __u8 data[8]; /* ADB data */
+ __u8 spare[21]; /* spare */
+};
+
+#endif /* __ASSEMBLY__ */
#define in_interrupt() (local_irq_count[smp_processor_id()] + local_bh_count[smp_processor_id()] != 0)
+#define in_irq() (local_irq_count[smp_processor_id()] != 0)
+
#define hardirq_trylock(cpu) (local_irq_count[cpu] == 0)
#define hardirq_endlock(cpu) do { } while (0)
* Currently the Atari has 72 and the Amiga 24, but if both are
* supported in the kernel it is better to make room for 72.
*/
-#if defined(CONFIG_ATARI)
+#if defined(CONFIG_ATARI) || defined(CONFIG_MAC)
#define NR_IRQS (72+SYS_IRQS)
#else
#define NR_IRQS (24+SYS_IRQS)
--- /dev/null
+/*
+ * Definitions for the "Baboon" custom IC on the PowerBook 190.
+ */
+
+#define BABOON_BASE (0x50F1A000) /* same as IDE controller base */
+
+#ifndef __ASSEMBLY__
+
+struct baboon {
+ char pad1[208]; /* generic IDE registers, not used here */
+ short mb_control; /* Control register:
+ * bit 5 : slot 2 power control
+ * bit 6 : slot 1 power control
+ */
+ char pad2[2];
+ short mb_status; /* (0xD4) media bay status register:
+ *
+ * bit 0: ????
+ * bit 1: IDE interrupt active?
+ * bit 2: bay status, 0 = full, 1 = empty
+ * bit 3: ????
+ */
+ char pad3[2]; /* (0xD6) not used */
+ short mb_ifr; /* (0xD8) media bay interrupt flags register:
+ *
+ * bit 0: ????
+ * bit 1: IDE controller interrupt
+ * bit 2: media bay status change interrupt
+ */
+};
+
+extern volatile struct baboon *baboon;
+
+#endif /* __ASSEMBLY **/
#define MAC_IDE_NONE 0
#define MAC_IDE_QUADRA 1
#define MAC_IDE_PB 2
+#define MAC_IDE_BABOON 3
#define MAC_SCC_II 1
#define MAC_SCC_QUADRA 2
#define PSC5_SOURCE_BASE 40
#define PSC6_SOURCE_BASE 48
#define NUBUS_SOURCE_BASE 56
+#define BABOON_SOURCE_BASE 64
/*
- * Maximum IRQ number is NUBUS_SOURCE_BASE + 7,
- * giving us IRQs up through 63.
+ * Maximum IRQ number is BABOON_SOURCE_BASE + 7,
+ * giving us IRQs up through 71
*/
-#define NUM_MAC_SOURCES 64
+#define NUM_MAC_SOURCES 72
/*
* clean way to separate IRQ into its source and index
#define IRQ_NUBUS_E (61)
#define IRQ_NUBUS_F (62)
+/* Baboon interrupts (cascaded to nubus slot $C) */
+#define IRQ_BABOON_0 (64)
+#define IRQ_BABOON_1 (65)
+#define IRQ_BABOON_2 (66)
+#define IRQ_BABOON_3 (67)
+
#define SLOT2IRQ(x) (x + 47)
#define IRQ2SLOT(x) (x - 47)
#include <asm/atomic.h>
-#define get_active_bhs() (bh_mask & bh_active)
-#define clear_active_bhs(x) atomic_clear_mask((x),&bh_active)
-
-extern inline void init_bh(int nr, void (*routine)(void))
-{
- bh_base[nr] = routine;
- atomic_set(&bh_mask_count[nr], 0);
- bh_mask |= 1 << nr;
-}
-
-extern inline void mark_bh(int nr)
-{
- set_bit(nr, &bh_active);
-}
-
-/*
- * These use a mask count to correctly handle
- * nested disable/enable calls
- */
-extern inline void disable_bh(int nr)
-{
- bh_mask &= ~(1 << nr);
- atomic_inc(&bh_mask_count[nr]);
-}
-
-extern inline void enable_bh(int nr)
-{
- if (atomic_dec_and_test(&bh_mask_count[nr]))
- bh_mask |= 1 << nr;
-}
-
-extern inline void remove_bh(int nr)
-{
- bh_base[nr] = NULL;
- bh_mask &= ~(1 << nr);
-}
-
extern unsigned int local_bh_count[NR_CPUS];
#define local_bh_disable() (local_bh_count[smp_processor_id()]++)
#define local_bh_enable() (local_bh_count[smp_processor_id()]--)
-extern inline void start_bh_atomic(void)
-{
- local_bh_count[smp_processor_id()]++;
- barrier();
-}
-
-extern inline void end_bh_atomic(void)
-{
- barrier();
- local_bh_count[smp_processor_id()]--;
-}
+#define in_softirq() (local_bh_count != 0)
/* These are for the irq's testing the lock */
#define softirq_trylock(cpu) (local_bh_count[cpu] ? 0 : (local_bh_count[cpu]=1))