cregit-Linux how code gets into the kernel

Release 4.7 drivers/macintosh/via-maciisi.c

/*
 * 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>
 * 
 * 7/13/2000- extensive changes by Andrew McPherson <andrew@macduff.dhs.org>
 * Works about 30% of the time now.
 */

#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/adb.h>
#include <linux/cuda.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <asm/macintosh.h>
#include <asm/macints.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


#undef DEBUG_MACIISI_ADB


static struct adb_request* current_req;

static struct adb_request* last_req;

static unsigned char maciisi_rbuf[16];

static unsigned char *reply_ptr;

static int data_index;

static int reading_reply;

static int reply_len;

static int tmp;

static int need_sync;


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 void maciisi_sync(struct adb_request *req);
static int maciisi_write(struct adb_request* req);
static irqreturn_t maciisi_interrupt(int irq, void* arg);
static void maciisi_input(unsigned char *buf, int nb);
static int maciisi_init_via(void);
static void maciisi_poll(void);
static int 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; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git27100.00%1100.00%
Total27100.00%1100.00%


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, 0, "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.2 for Unified ADB.\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git8297.62%133.33%
geert uytterhoevengeert uytterhoeven22.38%266.67%
Total84100.00%3100.00%

/* Flush data from the ADB controller */
static void maciisi_stfu(void) { int status = via[B] & (TIP|TREQ); if (status & TREQ) { #ifdef DEBUG_MACIISI_ADB printk (KERN_DEBUG "maciisi_stfu called with TREQ high!\n"); #endif return; } udelay(ADB_DELAY); via[ACR] &= ~SR_OUT; via[IER] = IER_CLR | SR_INT; udelay(ADB_DELAY); status = via[B] & (TIP|TREQ); if (!(status & TREQ)) { via[B] |= TIP; while(1) { int poll_timeout = ADB_DELAY * 5; /* Poll for SR interrupt */ while (!(via[IFR] & SR_INT) && poll_timeout-- > 0) status = via[B] & (TIP|TREQ); tmp = via[SR]; /* Clear shift register */ #ifdef DEBUG_MACIISI_ADB printk(KERN_DEBUG "maciisi_stfu: status %x timeout %d data %x\n", status, poll_timeout, tmp); #endif if(via[B] & TREQ) break; /* ACK on-off */ via[B] |= TACK; udelay(ADB_DELAY); via[B] &= ~TACK; } /* end frame */ via[B] &= ~TIP; udelay(ADB_DELAY); } via[IER] = IER_SET | SR_INT; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git13660.99%150.00%
geert uytterhoevengeert uytterhoeven8739.01%150.00%
Total223100.00%2100.00%

/* All specifically VIA-related initialization goes here */
static int maciisi_init_via(void) { int i; /* 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; #ifdef DEBUG_MACIISI_ADB printk(KERN_DEBUG "maciisi_init_via: initial status %x\n", via[B] & (TIP|TREQ)); #endif /* Wipe any pending data and int */ tmp = via[SR]; /* Enable keyboard interrupts */ via[IER] = IER_SET | SR_INT; /* Set initial state: idle */ via[B] &= ~(TACK|TIP); /* Clear interrupt bit */ via[IFR] = SR_INT; for(i = 0; i < 60; i++) { udelay(ADB_DELAY); maciisi_stfu(); udelay(ADB_DELAY); if(via[B] & TREQ) break; } if (i == 60) printk(KERN_ERR "maciisi_init_via: bus jam?\n"); maciisi_state = idle; need_sync = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git9756.73%150.00%
geert uytterhoevengeert uytterhoeven7443.27%150.00%
Total171100.00%2100.00%

/* Send a request, possibly waiting for a reply */
static int maciisi_send_request(struct adb_request* req, int sync) { int i; #ifdef DEBUG_MACIISI_ADB static int dump_packet = 0; #endif if (via == NULL) { req->complete = 1; return -ENXIO; } #ifdef DEBUG_MACIISI_ADB if (dump_packet) { printk(KERN_DEBUG "maciisi_send_request:"); for (i = 0; i < req->nbytes; i++) { printk(" %.2x", req->data[i]); } printk(" sync %d\n", sync); } #endif req->reply_expected = 1; i = maciisi_write(req); if (i) { /* Normally, if a packet requires syncing, that happens at the end of * maciisi_send_request. But if the transfer fails, it will be restarted * by maciisi_interrupt(). We use need_sync to tell maciisi_interrupt * when to sync a packet that it sends out. * * Suggestions on a better way to do this are welcome. */ if(i == -EBUSY && sync) need_sync = 1; else need_sync = 0; return i; } if(sync) maciisi_sync(req); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git10871.05%150.00%
geert uytterhoevengeert uytterhoeven4428.95%150.00%
Total152100.00%2100.00%

/* Poll the ADB chip until the request completes */
static void maciisi_sync(struct adb_request *req) { int count = 0; #ifdef DEBUG_MACIISI_ADB printk(KERN_DEBUG "maciisi_sync called\n"); #endif /* If for some reason the ADB chip shuts up on us, we want to avoid an endless loop. */ while (!req->complete && count++ < 50) { maciisi_poll(); } /* This could be BAD... when the ADB controller doesn't respond * for this long, it's probably not coming back :-( */ if (count > 50) /* Hopefully shouldn't happen */ printk(KERN_ERR "maciisi_send_request: poll timed out!\n"); }

Contributors

PersonTokensPropCommitsCommitProp
geert uytterhoevengeert uytterhoeven4474.58%133.33%
pre-gitpre-git1423.73%133.33%
roel kluinroel kluin11.69%133.33%
Total59100.00%3100.00%


int maciisi_request(struct adb_request *req, void (*done)(struct adb_request *), int nbytes, ...) { va_list list; int i; req->nbytes = nbytes; req->done = done; req->reply_expected = 0; va_start(list, nbytes); for (i = 0; i < nbytes; i++) req->data[i++] = va_arg(list, int); va_end(list); return maciisi_send_request(req, 1); }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro98100.00%1100.00%
Total98100.00%1100.00%

/* Enqueue a request, and run the queue if possible */
static int maciisi_write(struct adb_request* req) { unsigned long flags; int i; /* 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 = NULL; req->sent = 0; req->complete = 0; req->reply_len = 0; local_irq_save(flags); if (current_req) { last_req->next = req; last_req = req; } else { current_req = req; last_req = req; } if (maciisi_state == idle) { i = maciisi_start(); if(i != 0) { local_irq_restore(flags); return i; } } else { #ifdef DEBUG_MACIISI_ADB printk(KERN_DEBUG "maciisi_write: would start, but state is %d\n", maciisi_state); #endif local_irq_restore(flags); return -EBUSY; } local_irq_restore(flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git13075.58%125.00%
geert uytterhoevengeert uytterhoeven4123.84%250.00%
al viroal viro10.58%125.00%
Total172100.00%4100.00%


static int maciisi_start(void) { struct adb_request* req; int status; #ifdef DEBUG_MACIISI_ADB status = via[B] & (TIP | TREQ); printk(KERN_DEBUG "maciisi_start called, state=%d, status=%x, ifr=%x\n", maciisi_state, status, via[IFR]); #endif if (maciisi_state != idle) { /* shouldn't happen */ printk(KERN_ERR "maciisi_start: maciisi_start called when driver busy!\n"); return -EBUSY; } req = current_req; if (req == NULL) return -EINVAL; status = via[B] & (TIP|TREQ); if (!(status & TREQ)) { #ifdef DEBUG_MACIISI_ADB printk(KERN_DEBUG "maciisi_start: bus busy - aborting\n"); #endif return -EBUSY; } /* Okay, send */ #ifdef DEBUG_MACIISI_ADB printk(KERN_DEBUG "maciisi_start: sending\n"); #endif /* Set state to active */ via[B] |= TIP; /* ACK off */ via[B] &= ~TACK; /* Delay */ udelay(ADB_DELAY); /* Shift out and send */ via[ACR] |= SR_OUT; via[SR] = req->data[0]; data_index = 1; /* ACK on */ via[B] |= TACK; maciisi_state = sending; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git14272.82%150.00%
geert uytterhoevengeert uytterhoeven5327.18%150.00%
Total195100.00%2100.00%


void maciisi_poll(void) { unsigned long flags; local_irq_save(flags); if (via[IFR] & SR_INT) { maciisi_interrupt(0, NULL); } else /* avoid calling this function too quickly in a loop */ udelay(ADB_DELAY); local_irq_restore(flags); }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git3678.26%125.00%
geert uytterhoevengeert uytterhoeven919.57%250.00%
al viroal viro12.17%125.00%
Total46100.00%4100.00%

/* 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 irqreturn_t maciisi_interrupt(int irq, void* arg) { int status; struct adb_request *req; #ifdef DEBUG_MACIISI_ADB static int dump_reply = 0; #endif int i; unsigned long flags; local_irq_save(flags); status = via[B] & (TIP|TREQ); #ifdef DEBUG_MACIISI_ADB printk(KERN_DEBUG "state %d status %x ifr %x\n", maciisi_state, status, via[IFR]); #endif if (!(via[IFR] & SR_INT)) { /* Shouldn't happen, we hope */ printk(KERN_ERR "maciisi_interrupt: called without interrupt flag set\n"); local_irq_restore(flags); return IRQ_NONE; } /* Clear the interrupt */ /* via[IFR] = SR_INT; */ switch_start: switch (maciisi_state) { case idle: if (status & TIP) printk(KERN_ERR "maciisi_interrupt: state is idle but TIP asserted!\n"); if(!reading_reply) 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) */ tmp = 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 { reply_ptr = maciisi_rbuf; } break; case sending: /* via[SR]; */ /* Set ACK off */ via[B] &= ~TACK; req = current_req; if (!(status & TREQ)) { /* collision */ printk(KERN_ERR "maciisi_interrupt: send collision\n"); /* Set idle and input */ via[ACR] &= ~SR_OUT; tmp = via[SR]; via[B] &= ~TIP; /* Must re-send */ reading_reply = 0; reply_len = 0; maciisi_state = idle; udelay(ADB_DELAY); /* process this now, because the IFR has been cleared */ goto switch_start; } udelay(ADB_DELAY); 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; tmp = via[SR]; /* 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); /* Do any queued requests now */ i = maciisi_start(); if(i == 0 && need_sync) { /* Packet needs to be synced */ maciisi_sync(current_req); } if(i != -EBUSY) need_sync = 0; } } else { /* Sending more stuff */ /* Shift out */ via[ACR] |= SR_OUT; /* Write */ via[SR] = req->data[data_index++]; /* Signal 'byte ready' */ via[B] |= TACK; } break; case reading: /* Shift in */ /* via[ACR] &= ~SR_OUT; */ /* Not in 2.2 */ 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; i = maciisi_start(); if(i == 0 && need_sync) { /* Packet needs to be synced */ maciisi_sync(current_req); } if(i != -EBUSY) need_sync = 0; break; } /* Read data */ *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; tmp = via[SR]; /* That's what happens in 2.2 */ udelay(ADB_DELAY); /* Give controller time to recover */ /* 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); } } #ifdef DEBUG_MACIISI_ADB 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"); } #endif 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); } maciisi_state = idle; status = via[B] & (TIP|TREQ); if (!(status & TREQ)) { /* Timeout?! More likely, another packet coming in already */ #ifdef DEBUG_MACIISI_ADB printk(KERN_DEBUG "extra data after packet: status %x ifr %x\n", status, via[IFR]); #endif #if 0 udelay(ADB_DELAY); via[B] |= TIP; maciisi_state = reading; reading_reply = 0; reply_ptr = maciisi_rbuf; #else /* Process the packet now */ reading_reply = 0; goto switch_start; #endif /* We used to do this... but the controller might actually have data for us */ /* maciisi_stfu(); */ } else { /* Do any queued requests now if possible */ i = maciisi_start(); if(i == 0 && need_sync) { /* Packet needs to be synced */ maciisi_sync(current_req); } if(i != -EBUSY) need_sync = 0; } break; default: printk("maciisi_interrupt: unknown maciisi_state %d?\n", maciisi_state); } local_irq_restore(flags); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git68374.24%125.00%
geert uytterhoevengeert uytterhoeven23725.76%375.00%
Total920100.00%4100.00%


static void maciisi_input(unsigned char *buf, int nb) { #ifdef DEBUG_MACIISI_ADB int i; #endif switch (buf[0]) { case ADB_PACKET: adb_input(buf+2, nb-2, buf[1] & 0x40); break; default: #ifdef DEBUG_MACIISI_ADB printk(KERN_DEBUG "data from IIsi ADB (%d bytes):", nb); for (i = 0; i < nb; ++i) printk(" %.2x", buf[i]); printk("\n"); #endif break; } }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git8588.54%150.00%
geert uytterhoevengeert uytterhoeven1111.46%150.00%
Total96100.00%2100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git184471.58%111.11%
geert uytterhoevengeert uytterhoeven63124.50%555.56%
al viroal viro1003.88%222.22%
roel kluinroel kluin10.04%111.11%
Total2576100.00%9100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}