From: ? <uns...@us...> - 2002-05-29 03:37:44
|
Update of /cvsroot/linux-vax/kernel-2.4/drivers/char In directory usw-pr-cvs1:/tmp/cvs-serv31994/drivers/char Modified Files: Makefile tty_io.c Added Files: vxt2694.c sc26c94.c sc26c94.h Log Message: First attempt at a real serial driver for the SC26C94 in the VXT2000. --- NEW FILE --- /* * The platform-specific bits of a driver for the SC26C94 based serial interface * in the VXT2000. * * 2002 Uns Lider */ /* TODO: * Properly configure the I/O output ports * Figure out how to use incoming flow control signals * Hooks for LKxxx keyboard */ /* channel A is the serial port channel B is the unused "J5" channel C is the keyboard (but its I/O ports are borrowed by channel A) channel D is the mouse */ #include <linux/types.h> #include <linux/delay.h> #include <asm/system.h> #include <linux/interrupt.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/tty.h> #include <linux/serial.h> #include <linux/generic_serial.h> #include <linux/console.h> #include <asm/io.h> #include <linux/ioport.h> #include "sc26c94.h" #define VXT2694_PHYS_ADDR 0x200A0000 volatile int *vxt2694_addr = NULL; /* * callbacks for sc26c94.c */ static u8 readreg(struct sc26c94 *quart, int reg) { return vxt2694_addr[reg]; } static void writereg(struct sc26c94 *quart, int reg, u8 value) { vxt2694_addr[reg]=value; } static void set_handshake(struct sc26c94 *quart, int channel, int ready) { if(channel != SC26C94_PORT_A) { return; } sc26c94_write_io(quart, SC26C94_PORT_C, 2, ready); } static int get_handshake(struct sc26c94 *quart, int channel) { if(channel != SC26C94_PORT_A) { return 1; } return 1; /* don't know what port this is on! */ } static void set_flow(struct sc26c94 *quart, int channel, int on) { if(channel != SC26C94_PORT_A) { return; } sc26c94_write_io(quart, SC26C94_PORT_A, 2, on); } static int get_flow(struct sc26c94 *quart, int channel) { if(channel != SC26C94_PORT_A) { return 1; } return sc26c94_read_io(quart, SC26C94_PORT_A, 0); } static int get_cd(struct sc26c94 *quart, int channel) { if(channel != SC26C94_PORT_A) { return 1; } return 1; /* don't know what port this is on! */ } /* since channel B is unused, we are free to program timer A/B to an arbitrary value and use that as the brg */ /* at 3.6864MHz clock, a timer value of 2 (the minimum value) would result in 1 edge per 2/3.6864e6 sec, 1 cycle per 4/3.6864e6 sec. that would result in a bit period of 64/3.6864e6, or a baud rate of 3.6864e6/64=57600 bps. so the baud rate for a given timer value is equal to 115200 divided by the timer value. */ static void set_speed_acr(struct sc26c94 *quart, u8 bit7) { quart->writereg(quart, SC26C94_REG_CSRa, 0xcc); quart->acr[0] &= ~128; quart->acr[0] |= bit7; quart->writereg(quart, SC26C94_REG_ACRab, quart->acr[0]); } static void set_speed_timer(struct sc26c94 *quart, int bps) { u16 ctval; int a, b, c; ctval = 115200 / bps; a = abs(bps - (115200 / (ctval - 1))); b = abs(bps - (115200 / ctval)); c = abs(bps - (115200 / (ctval + 1))); if(a < b) { ctval--; } else if(c < b) { ctval++; } quart->writereg(quart, SC26C94_REG_CSRa, 0xdd); quart->writereg(quart, SC26C94_REG_CTUab, (ctval & 0xff00) >> 8); quart->writereg(quart, SC26C94_REG_CTLab, ctval & 0x00ff); } static int set_speed(struct sc26c94 *quart, int channel, int bps) { if(channel != SC26C94_PORT_A) { return 0; } if(bps > 57600) { switch(bps) { case 115200: set_speed_acr(quart, 1); case 230400: set_speed_acr(quart, 0); default: return -1; } } else { set_speed_timer(quart, bps); } return 0; } struct sc26c94 the_quart = { {{0},{0},{0},{0}}, readreg, writereg, set_handshake, get_handshake, set_flow, get_flow, get_cd, set_speed, NULL }; static int vxt2694_open(struct tty_struct *tty, struct file *filp) { return sc26c94_open(&the_quart, tty, filp); } /* * initialization code */ struct tty_driver tty_driver; struct tty_driver callout_driver; int vxt2694_init() { int irq; int error; #if 0 printk("vxt2694_init\n"); mdelay(1000); /* let TxFIFO drain before messing with UART */ #endif if(vxt2694_addr == NULL) vxt2694_addr = ioremap(VXT2694_PHYS_ADDR, 256); sc26c94_init_chip(&the_quart); /* turn the timer interrupt(?) back on */ sc26c94_set_io_dir(&the_quart, SC26C94_PORT_D, 1, 2); the_quart.acr[1] = 0x60; writereg(&the_quart, SC26C94_REG_ACRcd, 0x60); writereg(&the_quart, SC26C94_REG_CTUcd, 0x48); writereg(&the_quart, SC26C94_REG_CTLcd, 0); (void) readreg(&the_quart, SC26C94_CREG_STARTCTcd); /* turn the serial console port back on */ writereg(&the_quart, SC26C94_REG_CRa, SC26C94_CR_TX_ON); sc26c94_write_mr(&the_quart, SC26C94_PORT_A, 1, SC26C94_MR1_PAR_NONE | SC26C94_MR1_CSIZE_8); /* set up the BRG */ the_quart.acr[0] = 0x60; writereg(&the_quart, SC26C94_REG_ACRab, 0x60); set_speed(&the_quart, SC26C94_PORT_A, 9600); sc26c94_init_chanstructs(&the_quart); writereg(&the_quart, SC26C94_REG_IVR, 0x42); irq = 0x42; if(irq!=0) { request_irq(irq, sc29c94_interrupt, SA_INTERRUPT, "sc29c94", &the_quart); } #if 0 writereg(&the_quart, SC26C94_REG_IMRab, 0xff); #endif sc26c94_init_drivers(&tty_driver, &callout_driver); tty_driver.open = vxt2694_open; #if 1 if ((error = tty_register_driver(&tty_driver))) { printk(KERN_ERR "Couldn't register sc29c94 driver, error = %d\n", error); } if ((error = tty_register_driver(&callout_driver))) { printk(KERN_ERR "Couldn't register sc29c94 callout driver, error = %d\n", error); } #endif printk("sc26c94: interrupt vector %d\n",irq); return 0; } /* * console stuff */ static void vxt_console_write(struct console *co, const char *str, unsigned count) { unsigned long flags; save_flags(flags); cli(); while(count--) { if(*str == '\n') sc26c94_immediate_putc(&the_quart, SC26C94_PORT_A, '\r'); sc26c94_immediate_putc(&the_quart, SC26C94_PORT_A, *str++); } restore_flags(flags); } static int vxt_console_wait_key(struct console *co) { unsigned long flags; int c; save_flags(flags); cli(); c=sc26c94_immediate_getc(&the_quart, SC26C94_PORT_A); restore_flags(flags); return c; } static kdev_t vxt_console_device(struct console *c) { return MKDEV(TTY_MAJOR, 64 + c->index); } static int __init vxt_console_setup(struct console *co, char *options) { #if 0 vxt2694_init(); co->cflag = CREAD | HUPCL | CLOCAL | B9600 | CS8; writereg(&the_quart, SC26C94_REG_CRa, SC26C94_CR_TX_ON); writereg(&the_quart, SC26C94_REG_CRa, SC26C94_CR_RX_ON); #endif /* this is called really early in kernel initialization, so we can't do a full vxt2694_init. but that's ok because all we'll be calling is the _immediate_putc and _immediate_getc */ vxt2694_addr = ioremap(VXT2694_PHYS_ADDR, 256); return 0; } static struct console sercons = { name: "ttyS", write: vxt_console_write, device: vxt_console_device, wait_key: vxt_console_wait_key, setup: vxt_console_setup, flags: CON_PRINTBUFFER, index: 0, }; void __init vxt_serial_console_init(void) { register_console(&sercons); } --- NEW FILE --- /* * The portable bits of a driver for the SC26C94 "QUART" (Quad UART). * * 2002 Uns Lider * some parts are based on vme_scc.c */ /* TODO: * need a way to wake up open() if we're blocking for a status line change */ #include <linux/module.h> #include <linux/types.h> #include <linux/delay.h> #include <asm/system.h> #include <linux/interrupt.h> #include <linux/sched.h> #include <linux/tty.h> #include <linux/serial.h> #include <linux/generic_serial.h> #include "sc26c94.h" static void disable_tx_ints(void *ptr); static void disable_rx_ints(void *ptr); static void modify_imr(struct sc26c94 *quart, int whichimr, u8 andmask, u8 ormask); /* * functions for doing types of register access beyond a basic write/read. */ void sc26c94_command(struct sc26c94 *quart, int channel, int command) { quart->writereg(quart, SC26C94_REGCHAN(SC26C94_REG_CR, channel), (command&15) << 4); udelay(2); /* The standard clock is 3.68MHz. This delay is sufficient for any clock >2.5MHz. */ } /* * sc26c94_read_mr and sc26c94_write_mr read and write a mode register. * Each channel has three mode registers. However, each channel uses only one * address to access all three registers. */ static void sc26c94_seek_mr(struct sc26c94 *quart, int channel, int index) { switch(channel) { case 0: return sc26c94_command(quart, channel, SC26C94_CMD_MR0); case 1: return sc26c94_command(quart, channel, SC26C94_CMD_MR1); case 2: /* There's no SC26C94_CMD_MR2. However, the pointer is incremented automatically every time the MR is accessed. Instead of using SC26C94_CMD_MR1 and then incrementing it, we just assume that it's at MR0 and increment it repeatedly. The pointer will not auto-increment beyond MR2. This way, we avoid the 2uS delay associated with using a command. */ (void)quart->readreg(quart, SC26C94_REGCHAN(SC26C94_REG_MR, channel)); (void)quart->readreg(quart, SC26C94_REGCHAN(SC26C94_REG_MR, channel)); return; } } int sc26c94_read_mr(struct sc26c94 *quart, int channel, int index) { sc26c94_seek_mr(quart, channel, index); return quart->readreg(quart, SC26C94_REGCHAN(SC26C94_REG_MR, channel)); } void sc26c94_write_mr(struct sc26c94 *quart, int channel, int index, int value) { sc26c94_seek_mr(quart, channel, index); quart->writereg(quart, SC26C94_REGCHAN(SC26C94_REG_MR, channel), value); } #if 0 /* * interrupt autoprobing support. * on platforms that make full use of the 2694's interrupt vector features, the * boot rom has programmed a write-only register in the 2694 to tell it what * vector to use. this probing can be used to determine what value has been * programmed. * the machine-specific driver needs to invoke these functions to do its probe. * first it should save any state (baud rates, etc) that it doesn't want * destroyed. * then it should call sc26c94_prepare_interrupt. this will get the chip ready * to send an interrupt; basically everything but actually triggering it. * then, after turning on the interrupt probing, it should call * sc26c94_cause_interrupt, then turn off the interrupt probing. * once it's done that, it can restore the state that was destroyed. * the point of separating _prepare and _cause is to keep the time window of the * actual probe as small as possible, to maximize accuracy. */ /* * The interrupt autoprobing does not work, at least on the VXT. So we don't use * it. * The problem is, the 2694 fires a billion interrupts at the CPU, unless the * interrupt handler clears the interrupt. During the autoprobe, the processor * ends up spending all its time in the stray interrupt handler, and never gets * around to returning to the driver code that would clear the interrupt! */ int sc26c94_prepare_interrupt(struct sc26c94 *quart) { int i; /* Just to be nice, if there are characters in the Tx buffer, wait for them to be printed before we screw everything up. */ for(i = 0; i < 100; i++) { if(quart->readreg(quart, SC26C94_REG_SRa) & SC26C94_SR_TXRDY) break; udelay(10); } /* First disable all the interrupts and turn off all the channels -- wouldn't want _two_ interrupts coming in ;-) */ quart->writereg(quart, SC26C94_REG_IMRab, 0); quart->writereg(quart, SC26C94_REG_IMRcd, 0); /* Set the threshold to allow all interrupts. Use the full IVR value as the interrupt vector. */ quart->writereg(quart, SC26C94_REG_ICR, SC26C94_IVC_IVR); /* Entice the transmitter to set TxRDY */ sc26c94_command(quart, SC26C94_PORT_A, SC26C94_CMD_RESET_TX); sc26c94_write_mr(quart, SC26C94_PORT_A, 2, 0); quart->writereg(quart, SC26C94_REG_CRa, SC26C94_CR_TX_ON); /* Wait for TxRDY to actually go high */ for(i = 0; i < 100; i++) { if(quart->readreg(quart, SC26C94_REG_ISRab) & SC26C94_ISR_TXa) break; udelay(10); } #if 0 printk("SC26C94_REG_ISRab=%x\n",quart->readreg(quart, SC26C94_REG_ISRab)); #endif if(i == 100) return -1; return 0; } int sc26c94_cause_interrupt(struct sc26c94 *quart) { int i; /* OK, TxRDY is high, now enable the interrupt-on-TxRDY! */ quart->writereg(quart, SC26C94_REG_IMRab, SC26C94_ISR_TXa); #if 1 /* for some wonderful reason i don't think this is generating an interrupt, so what the hell, enable all the other interrupts too */ quart->writereg(quart, SC26C94_REG_IMRab, 0xff); #endif /* wait until the interrupt has actually been sent */ for(i = 0; i < 100; i++) { if(SC26C94_CIR_TYPE(quart->readreg(quart, SC26C94_REG_CIR)) != SC26C94_CIR_NONE) break; udelay(10); } /* now disable the interrupt again */ quart->writereg(quart, SC26C94_REG_IMRab, 0); /* clear the interrupt, too (why not?) */ for(i=0;i<100;i++) quart->writereg(quart, SC26C94_CREG_UPDATE_CIR, 0); #if 0 printk("foo %d %x\n",i,quart->readreg(quart, SC26C94_REG_CIR)); mdelay(1000); #endif if(i == 100) return -1; return 0; } #endif /* * immediate putc and getc. the getc is blocking; the putc does not return until * the character has actually been handed to the uart. these are for console use * only; they are way ill-behaved for anything else. the normal, buffered getc * and putc are handled by generic_serial. */ void sc26c94_immediate_putc(struct sc26c94 *quart, int channel, unsigned char c) { unsigned long flags; save_flags(flags); cli(); /* right now, we don't limit the amount of time that we'll spend waiting for the uart to become ready, so we could conceivably sit here forever if the uart state has been corrupted */ while((quart->readreg(quart, (SC26C94_REGCHAN(SC26C94_REG_SR, channel))) & SC26C94_SR_TXRDY) == 0) ; quart->writereg(quart, SC26C94_REGCHAN(SC26C94_REG_THR, channel), c); restore_flags(flags); } unsigned char sc26c94_immediate_getc(struct sc26c94 *quart, int channel) { unsigned long flags; unsigned char c; save_flags(flags); cli(); while((quart->readreg(quart, (SC26C94_REGCHAN(SC26C94_REG_SR, channel))) & SC26C94_SR_RXRDY) == 0) ; c = quart->readreg(quart, SC26C94_REGCHAN(SC26C94_REG_RHR, channel)); restore_flags(flags); return c; } /* * read and write I/O pins */ static u8 io_bits[] = {SC26C94_IO_0a, SC26C94_IO_1a, SC26C94_IO_2a, SC26C94_IO_3a}; int sc26c94_read_io(struct sc26c94 *quart, int channum, int ionum) { int ipr_reg = (channum < SC26C94_PORT_C) ? SC26C94_REG_IPRab : SC26C94_REG_IPRcd; int ipr = quart->readreg(quart, ipr_reg); if((channum & 1) != 0) ipr >>= 2; return (ipr & io_bits[ionum & 3]) ? 1 : 0; } void sc26c94_write_io(struct sc26c94 *quart, int channum, int ionum, int val) { int opr_reg = (channum < SC26C94_PORT_C) ? SC26C94_REG_OPRab : SC26C94_REG_OPRcd; int opr; unsigned long flags; val = val ? 0 : io_bits[ionum & 3]; if((channum & 1) != 0) val <<= 2; save_flags(flags); cli(); opr = quart->readreg(quart, opr_reg); quart->writereg(quart, opr_reg, opr | val); restore_flags(flags); } void sc26c94_set_io_dir(struct sc26c94 *quart, int channum, int ionum, int val) { static int regnum[]={SC26C94_REG_IOPCRa, SC26C94_REG_IOPCRb, SC26C94_REG_IOPCRc, SC26C94_REG_IOPCRd}; /* can't use SC26C94_REGCHAN here */ u8 mask = 3; mask <<= (ionum & 3) * 2; val &= 3; val <<= (ionum & 3) * 2; quart->iopcr[channum & 3] &= ~mask; quart->iopcr[channum & 3] |= val; quart->writereg(quart, regnum[channum & 3], quart->iopcr[channum & 3]); } static void endis_delta_int(struct sc26c94 *quart, int channum, int ionum, int on) { int acrnum = (channum < SC26C94_PORT_C) ? 0 : 1; u8 mask = 1; on &= 1; if(ionum != 0) { on <<= 1; mask <<= 1; } if((channum & 1) != 0) { on <<= 2; mask <<= 2; } quart->acr[acrnum] &= ~mask; quart->acr[acrnum] |= on; quart->writereg(quart, (acrnum == 0) ? SC26C94_REG_ACRab : SC26C94_REG_ACRcd, quart->acr[acrnum]); } void sc26c94_register_delta_int(struct sc26c94 *quart, int channum, int ionum, void (*handler)(struct sc26c94 *quart, void *data, int state), void *data) { endis_delta_int(quart, channum, ionum, 0); quart->delta_handlers[channum & 3][ionum & 1].handler = handler; quart->delta_handlers[channum & 3][ionum & 1].data = data; endis_delta_int(quart, channum, ionum, 1); } void sc26c94_unregister_delta_int(struct sc26c94 *quart, int channum, int ionum) { endis_delta_int(quart, channum, ionum, 0); quart->delta_handlers[channum & 3][ionum & 1].handler = NULL; } /* * interrupt top half * on some platforms we are able to get tx and rx interrupts separately, and * also to get separate interrupts by channel. we don't take advantage of this. * we use a single interrupt vector for all events, and when we get an * interrupt, we take action to clear _all_ outstanding interrupt conditions. * since the quart will tell us what the highest-priority outstanding interrupt * is, we don't have to waste time polling every possible source. */ static inline void interrupt_tx(struct sc26c94 *quart, int channum, int size) { struct sc26c94_chan *chan=&(quart->chans[channum]); int chars, i; /* if we have any xon/xoff characters queued, shove them out first */ if(chan->xchar) { quart->writereg(quart, SC26C94_REG_GTHR, chan->xchar); chan->xchar=0; size--; if(size == 0) return; } chars = chan->gs.xmit_cnt; if(chars > size) chars = size; if(chan->gs.xmit_cnt < 0 || !chan->gs.tty || chan->gs.tty->stopped || chan->gs.tty->hw_stopped) chars = 0; for(i = 0; i < chars; i++) { quart->writereg(quart, SC26C94_REG_GTHR, chan->gs.xmit_buf[chan->gs.xmit_tail]); chan->gs.xmit_tail++; chan->gs.xmit_tail &= (SERIAL_XMIT_SIZE-1); } chan->gs.xmit_cnt-=chars; if(chars == 0 || chan->gs.xmit_cnt == 0) disable_tx_ints(chan); if(chan->gs.tty && chan->gs.xmit_cnt <= chan->gs.wakeup_chars) { if ((chan->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && chan->gs.tty->ldisc.write_wakeup) (chan->gs.tty->ldisc.write_wakeup)(chan->gs.tty); wake_up_interruptible(&chan->gs.tty->write_wait); } } static inline void interrupt_rx(struct sc26c94 *quart, int channum, int size, int e) { struct sc26c94_chan *chan=&(quart->chans[channum]); struct tty_flip_buffer *flip; int chars, i; u8 sr; if(!chan->gs.tty) { disable_rx_ints(chan); return; } flip=&(chan->gs.tty->flip); chars = size; if(chars > TTY_FLIPBUF_SIZE - flip->count) chars = TTY_FLIPBUF_SIZE - flip->count; for(i = 0; i < chars; i++) { if(e) { sr = quart->readreg(quart, SC26C94_REGCHAN(SC26C94_REG_SR, channum)); if(sr & SC26C94_SR_BRK) { *(flip->flag_buf_ptr++) = TTY_BREAK; } else if(sr & SC26C94_SR_FRAME) { *(flip->flag_buf_ptr++) = TTY_FRAME; } else if(sr & SC26C94_SR_PARITY) { *(flip->flag_buf_ptr++) = TTY_PARITY; } else if(sr & SC26C94_SR_OVER) { /* technically this one isn't associated with the fifo character */ *(flip->flag_buf_ptr++) = TTY_OVERRUN; } else { *(flip->flag_buf_ptr++) = 0; } sc26c94_command(quart, channum, SC26C94_CMD_RESET_ERR); } else { *(flip->flag_buf_ptr++) = 0; } *(flip->char_buf_ptr++) = quart->readreg(quart, SC26C94_REG_GRHR); } flip->count+=chars; /* uhh. so what happens at this point if we filled the entire flip buffer but there are still more characters in the fifo? will we loop infinitely? */ tty_flip_buffer_push(chan->gs.tty); } static inline void interrupt_state(struct sc26c94 *quart, int channum) { u8 ipcr = quart->readreg(quart, (channum < SC26C94_PORT_C) ? SC26C94_REG_IPCRab : SC26C94_REG_IPCRcd); u8 deltamask, statemask; int thischan = (channum < SC26C94_PORT_C) ? SC26C94_PORT_B : SC26C94_PORT_D; int thisio = 1; void (*handler)(struct sc26c94 *quart, void *data, int state); void *data; /* at this point, up to 4 pins may have changed state, but we cleared the whole interrupt by reading the ipcr */ for(deltamask = SC26C94_IPCR_DELTA1b, statemask = SC26C94_IPCR_STATE1b; statemask != 0; deltamask >>= 1, statemask >>= 1) { if((ipcr & deltamask) != 0) { handler = quart->delta_handlers[thischan][thisio].handler; data = quart->delta_handlers[thischan][thisio].data; handler(quart, data, (ipcr & statemask) ? 1 : 0); } thisio--; if(thisio < 0) { thisio = 1; thischan--; } } } static inline void interrupt_break(struct sc26c94 *quart, int channum) { printk("sc26c94: unexpected interrupt_break\n"); sc26c94_command(quart, channum, SC26C94_CMD_RESET_BCI); } static inline void interrupt_ct(struct sc26c94 *quart, int channum) { printk("sc26c94: unexpected interrupt_ct\n"); modify_imr(quart, 0, ~SC26C94_ISR_CT, 0); modify_imr(quart, 1, ~SC26C94_ISR_CT, 0); } void sc29c94_interrupt(int vector, void *data, struct pt_regs *fp) { struct sc26c94 *quart = data; u8 cir, size, chan; #if 1 printk("%%"); #endif for(;;) { cir = quart->readreg(quart, SC26C94_REG_CIR); size = SC26C94_CIR_SIZE(cir); chan = SC26C94_CIR_CHAN(cir); switch(SC26C94_CIR_TYPE(cir)) { case SC26C94_CIR_NONE: return; case SC26C94_CIR_TX1: case SC26C94_CIR_TX2: interrupt_tx(quart, chan, size); break; case SC26C94_CIR_RX: interrupt_rx(quart, chan, size, 0); break; case SC26C94_CIR_RXE: interrupt_rx(quart, chan, size, 1); break; case SC26C94_CIR_STATE: interrupt_state(quart, chan); break; case SC26C94_CIR_BRK: interrupt_break(quart, chan); break; case SC26C94_CIR_CT: interrupt_ct(quart, chan); break; default: /* can never happen */ break; } /* we've just finished handing 1 interrupt source. now clear it and go back and see if there are any more. */ quart->writereg(quart, SC26C94_CREG_UPDATE_CIR, 0); } } /* * callbacks for generic_serial */ static void modify_imr(struct sc26c94 *quart, int whichimr, u8 andmask, u8 ormask) { int imr_reg = (whichimr == 0) ? SC26C94_REG_IMRab : SC26C94_REG_IMRcd; quart->imr[whichimr & 1] &= andmask; quart->imr[whichimr & 1] |= ormask; quart->writereg(quart, imr_reg, quart->imr[whichimr & 1]); } static void disable_tx_ints(void *ptr) { struct sc26c94_chan *chan = ptr; struct sc26c94 *quart = chan->quart; u8 imr_bit = ((chan->channel & 1) != 0) ? SC26C94_ISR_TXb : SC26C94_ISR_TXa; modify_imr(quart, (chan->channel < SC26C94_PORT_C) ? 0 : 1, ~imr_bit, 0); } static void enable_tx_ints(void *ptr) { struct sc26c94_chan *chan = ptr; struct sc26c94 *quart = chan->quart; u8 imr_bit = ((chan->channel & 1) != 0) ? SC26C94_ISR_TXb : SC26C94_ISR_TXa; modify_imr(quart, (chan->channel < SC26C94_PORT_C) ? 0 : 1, 255, imr_bit); } static void disable_rx_ints(void *ptr) { struct sc26c94_chan *chan = ptr; struct sc26c94 *quart = chan->quart; u8 imr_bit = ((chan->channel & 1) != 0) ? SC26C94_ISR_RXb : SC26C94_ISR_RXa; modify_imr(quart, (chan->channel < SC26C94_PORT_C) ? 0 : 1, ~imr_bit, 0); } static void enable_rx_ints(void *ptr) { struct sc26c94_chan *chan = ptr; struct sc26c94 *quart = chan->quart; u8 imr_bit = ((chan->channel & 1) != 0) ? SC26C94_ISR_RXb : SC26C94_ISR_RXa; modify_imr(quart, (chan->channel < SC26C94_PORT_C) ? 0 : 1, 255, imr_bit); } static int get_CD(void *ptr) { struct sc26c94_chan *chan = ptr; return chan->quart->get_cd(chan->quart, chan->channel); } static void shutdown_port(void *ptr) { struct sc26c94_chan *chan = ptr; chan->gs.flags &= ~GS_ACTIVE; if (chan->gs.tty && chan->gs.tty->termios->c_cflag & HUPCL) chan->quart->set_handshake(chan->quart, chan->channel, 0); } static int set_real_termios(void *ptr) { struct sc26c94_chan *chan = ptr; unsigned int cflag; unsigned long flags; u8 foo; if (!chan->gs.tty || !chan->gs.tty->termios) return 0; cflag = chan->gs.tty->termios->c_cflag; if(chan->gs.baud == 0) { chan->quart->set_handshake(chan->quart, chan->channel, 0); } else { if(chan->quart->set_speed(chan->quart, chan->channel, chan->gs.baud) != 0) printk("Could not set serial speed\n"); chan->quart->set_handshake(chan->quart, chan->channel, 1); } if (cflag & CLOCAL) { chan->gs.flags &= ~ASYNC_CHECK_CD; } else { chan->gs.flags |= ASYNC_CHECK_CD; } foo = (cflag & CSIZE) >> 4; if((cflag & PARENB) == 0) foo |= SC26C94_MR1_PAR_NONE; if(cflag & PARODD) foo |= SC26C94_MR1_PARODD; save_flags(flags); cli(); sc26c94_write_mr(chan->quart, chan->channel, 1, foo); foo = (cflag & CSTOPB) ? SC26C94_MR2_STOP_2000 : SC26C94_MR2_STOP_1000; sc26c94_write_mr(chan->quart, chan->channel, 2, foo); restore_flags(flags); return 0; } static int chars_in_buffer(void *ptr) { struct sc26c94_chan *chan = ptr; u8 sr; sr=chan->quart->readreg(chan->quart, SC26C94_REGCHAN(SC26C94_REG_SR, chan->channel)); return (sr & SC26C94_SR_TXEMT) ? 0 : 1; } /* uhh. what are hungup and close really for? */ static void hungup(void *ptr) { disable_tx_ints(ptr); disable_rx_ints(ptr); MOD_DEC_USE_COUNT; } static void closefu(void *ptr) { disable_tx_ints(ptr); disable_rx_ints(ptr); } /* * callbacks for tty_driver */ int sc26c94_open(struct sc26c94 *quart, struct tty_struct *tty, struct file *filp) { struct sc26c94_chan *chan; int retval, line; line = MINOR(tty->device); if(line < 0 || line >= 4) return -ENODEV; chan = &(quart->chans[line]); tty->driver_data = chan; chan->gs.tty = tty; if(!chan->gs.count) MOD_INC_USE_COUNT; chan->gs.count++; retval = gs_init_port(&chan->gs); if(retval) { chan->gs.count--; if(!chan->gs.count) MOD_DEC_USE_COUNT; return retval; } chan->gs.flags |= GS_ACTIVE; /* uhh. shouldn't there be a way to open the port without glitching these pins? */ quart->set_handshake(quart, 4, 1); quart->set_flow(quart, 4, 1); retval = gs_block_til_ready(chan, filp); if(retval) return retval; if((chan->gs.count == 1) && (chan->gs.flags & ASYNC_SPLIT_TERMIOS)) { if (tty->driver.subtype == SERIAL_TYPE_NORMAL) { *tty->termios = chan->gs.normal_termios; } else { *tty->termios = chan->gs.callout_termios; } set_real_termios(chan); } chan->gs.session = current->session; chan->gs.pgrp = current->pgrp; return 0; } static void send_xchar(struct sc26c94_chan *chan, char c) { chan->xchar = c; if(c) enable_tx_ints(chan); } static void sc26c94_throttle(struct tty_struct *tty) { struct sc26c94_chan *chan = (struct sc26c94_chan *)tty->driver_data; if(tty->termios->c_cflag & CRTSCTS) chan->quart->set_flow(chan->quart, chan->channel, 0); if(I_IXOFF(tty)) send_xchar(chan, STOP_CHAR(tty)); } static void sc26c94_unthrottle(struct tty_struct *tty) { struct sc26c94_chan *chan = (struct sc26c94_chan *)tty->driver_data; if(tty->termios->c_cflag & CRTSCTS) chan->quart->set_flow(chan->quart, chan->channel, 0); if(I_IXOFF(tty)) send_xchar(chan, START_CHAR(tty)); } static int sc26c94_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { return -ENOIOCTLCMD; } static void sc26c94_break_ctl(struct tty_struct *tty, int break_state) { struct sc26c94_chan *chan = (struct sc26c94_chan *)tty->driver_data; unsigned long flags; save_flags(flags); cli(); sc26c94_command(chan->quart, chan->channel, break_state ? SC26C94_CMD_BREAK_ON : SC26C94_CMD_BREAK_OFF); restore_flags(flags); } /* * initialization code */ static struct real_driver real_driver = { disable_tx_ints, enable_tx_ints, disable_rx_ints, enable_rx_ints, get_CD, shutdown_port, set_real_termios, chars_in_buffer, closefu, hungup, NULL }; /* this stuff is used by the tty_driver somehow */ static struct tty_struct *table[4] = { NULL, }; static struct termios * termios[4]; static struct termios * termios_locked[4]; static int refcount; void sc26c94_init_drivers(struct tty_driver *tty_driver, struct tty_driver *callout_driver) { memset(tty_driver, 0, sizeof(struct tty_driver)); tty_driver->magic = TTY_DRIVER_MAGIC; tty_driver->name = "ttyS"; tty_driver->major = TTY_MAJOR; tty_driver->minor_start = 64; tty_driver->num = 2; tty_driver->type = TTY_DRIVER_TYPE_SERIAL; tty_driver->subtype = SERIAL_TYPE_NORMAL; tty_driver->init_termios = tty_std_termios; tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; tty_driver->flags = TTY_DRIVER_REAL_RAW; tty_driver->refcount = &refcount; tty_driver->table = table; tty_driver->termios = termios; tty_driver->termios_locked = termios_locked; /* tty_driver->open = sc26c94_open; */ tty_driver->close = gs_close; tty_driver->write = gs_write; tty_driver->put_char = gs_put_char; tty_driver->flush_chars = gs_flush_chars; tty_driver->write_room = gs_write_room; tty_driver->chars_in_buffer = gs_chars_in_buffer; tty_driver->flush_buffer = gs_flush_buffer; tty_driver->ioctl = sc26c94_ioctl; tty_driver->throttle = sc26c94_throttle; tty_driver->unthrottle = sc26c94_unthrottle; tty_driver->set_termios = gs_set_termios; tty_driver->stop = gs_stop; tty_driver->start = gs_start; tty_driver->hangup = gs_hangup; tty_driver->break_ctl = sc26c94_break_ctl; memcpy(callout_driver, tty_driver, sizeof(struct tty_driver)); callout_driver->name = "cua"; callout_driver->major = TTYAUX_MAJOR; callout_driver->subtype = SERIAL_TYPE_CALLOUT; /* at this point, tty_driver and callout_driver are ready to be tty_register_driver()'d (after adding .open) */ } void sc26c94_init_chanstructs(struct sc26c94 *quart) { struct sc26c94_chan *chan; int i; for(i = 0; i < 4; i++) { chan = &(quart->chans[i]); chan->quart = quart; chan->channel = i; chan->xchar = 0; chan->gs.callout_termios = tty_std_termios; chan->gs.normal_termios = tty_std_termios; chan->gs.close_delay = HZ/2; chan->gs.closing_wait = 30 * HZ; chan->gs.rd = &real_driver; #ifdef NEW_WRITE_LOCKING chan->gs.port_write_sem = MUTEX; #endif init_waitqueue_head(&chan->gs.open_wait); init_waitqueue_head(&chan->gs.close_wait); } } void sc26c94_init_chip(struct sc26c94 *quart) { int i; modify_imr(quart, 0, 0, 0); modify_imr(quart, 1, 0, 0); for(i = SC26C94_PORT_A; i <= SC26C94_PORT_D; i++) { sc26c94_command(quart, i, SC26C94_CMD_RESET_TX); sc26c94_command(quart, i, SC26C94_CMD_RESET_RX); sc26c94_write_mr(quart, i, 0, 0); sc26c94_write_mr(quart, i, 1, 0); sc26c94_write_mr(quart, i, 2, 0); quart->writereg(quart, SC26C94_REGCHAN(SC26C94_REG_BCR, i), 0); } /* On the VXT, the counter/timers must be stopped AFTER we have done any sc26c94_command()'s we're going to do. for some reason it looks like udelay() will hang if we call it with the second timer stopped */ /* btw, these might not actually stop the C/T, see datasheet p.17 */ (void) quart->readreg(quart, SC26C94_CREG_STOPCTab); (void) quart->readreg(quart, SC26C94_CREG_STOPCTcd); /* IOPCR and BCR registers aren't numbered the same way as the others, so we can't use SC26C94_REGCHAN() with them */ quart->writereg(quart, SC26C94_REG_IOPCRa, 0); quart->writereg(quart, SC26C94_REG_IOPCRb, 0); quart->writereg(quart, SC26C94_REG_IOPCRc, 0); quart->writereg(quart, SC26C94_REG_IOPCRd, 0); quart->iopcr[0] = 0; quart->iopcr[1] = 0; quart->iopcr[2] = 0; quart->iopcr[3] = 0; quart->writereg(quart, SC26C94_REG_BCRa, 0); quart->writereg(quart, SC26C94_REG_BCRb, 0); quart->writereg(quart, SC26C94_REG_BCRc, 0); quart->writereg(quart, SC26C94_REG_BCRd, 0); quart->writereg(quart, SC26C94_REG_ICR, SC26C94_IVC_IVR); quart->writereg(quart, SC26C94_REG_ACRab, 0); quart->writereg(quart, SC26C94_REG_ACRcd, 0); quart->acr[0] = 0; quart->acr[1] = 0; /* Enable the main mask for the I/O port change interrupts. Don't worry, those interrupts aren't actually enabled until they're turned on in the ACR. */ modify_imr(quart, 0, 0, SC26C94_ISR_IO); modify_imr(quart, 1, 0, SC26C94_ISR_IO); } --- NEW FILE --- /* * Registers */ /* channels A and B */ #define SC26C94_REG_MRa 0 /* Mode Register, actually 3 registers (rw) */ #define SC26C94_REG_SRa 1 /* Status Register (r) */ #define SC26C94_REG_CSRa 1 /* Clock Select Register (w) */ #define SC26C94_REG_CRa 2 /* Command Register (w) */ #define SC26C94_REG_RHRa 3 /* Receive Holding Register (RxFIFO) (r) */ #define SC26C94_REG_THRa 3 /* Transmit Holding Register (TxFIFO) (w) */ #define SC26C94_REG_IPCRab 4 /* Input Port Change Register (r) */ #define SC26C94_REG_ACRab 4 /* Auxiliary Control Register (w) */ #define SC26C94_REG_ISRab 5 /* Interrupt Status Register (r) */ #define SC26C94_REG_IMRab 5 /* Interrupt Mask Register (w) */ #define SC26C94_REG_CTUab 6 /* Counter/Timer high-order byte (rw) */ #define SC26C94_REG_CTLab 7 /* Counter/Timer low-order byte (rw) */ #define SC26C94_REG_MRb 8 /* Mode Register, actually 3 registers (rw) */ #define SC26C94_REG_SRb 9 /* Status Register (r) */ #define SC26C94_REG_CSRb 9 /* Clock Select Register (w) */ #define SC26C94_REG_CRb 10 /* Command Register (w) */ #define SC26C94_REG_RHRb 11 /* Receive Holding Register (RxFIFO) (r) */ #define SC26C94_REG_THRb 11 /* Transmit Holding Register (TxFIFO) (w) */ #define SC26C94_REG_OPRab 12 /* Output Port Register (rw) */ #define SC26C94_REG_IPRab 13 /* Input Port Register (r) */ #define SC26C94_REG_IOPCRa 13 /* I/O Port Control Register (w) */ #define SC26C94_CREG_STARTCTab 14 /* Start Counter command (r) */ #define SC26C94_REG_IOPCRb 14 /* I/O Port Control Register (w) */ #define SC26C94_CREG_STOPCTab 15 /* Stop Counter command (r) */ #define SC26C94_REG_BCRa 32 /* interrupt Bidding Control Register (rw) */ #define SC26C94_REG_BCRb 33 /* interrupt Bidding Control Register (rw) */ /* channels C and D */ #define SC26C94_REG_MRc 16 /* Mode Register, actually 3 registers (rw) */ #define SC26C94_REG_SRc 17 /* Status Register (r) */ #define SC26C94_REG_CSRc 17 /* Clock Select Register (w) */ #define SC26C94_REG_CRc 18 /* Command Register (w) */ #define SC26C94_REG_RHRc 19 /* Receive Holding Register (RxFIFO) (r) */ #define SC26C94_REG_THRc 19 /* Transmit Holding Register (TxFIFO) (w) */ #define SC26C94_REG_IPCRcd 20 /* Input Port Change Register (r) */ #define SC26C94_REG_ACRcd 20 /* Auxiliary Control Register (w) */ #define SC26C94_REG_ISRcd 21 /* Interrupt Status Register (r) */ #define SC26C94_REG_IMRcd 21 /* Interrupt Mask Register (w) */ #define SC26C94_REG_CTUcd 22 /* Counter/Timer high-order byte (rw) */ #define SC26C94_REG_CTLcd 23 /* Counter/Timer low-order byte (rw) */ #define SC26C94_REG_MRd 24 /* Mode Register, actually 3 registers (rw) */ #define SC26C94_REG_SRd 25 /* Status Register (r) */ #define SC26C94_REG_CSRd 25 /* Clock Select Register (w) */ #define SC26C94_REG_CRd 26 /* Command Register (w) */ #define SC26C94_REG_RHRd 27 /* Receive Holding Register (RxFIFO) (r) */ #define SC26C94_REG_THRd 27 /* Transmit Holding Register (TxFIFO) (w) */ #define SC26C94_REG_OPRcd 28 /* Output Port Register (rw) */ #define SC26C94_REG_IPRcd 29 /* Input Port Register (r) */ #define SC26C94_REG_IOPCRc 29 /* I/O Port Control Register (w) */ #define SC26C94_CREG_STARTCTcd 30 /* Start Counter command (r) */ #define SC26C94_REG_IOPCRd 30 /* I/O Port Control Register (w) */ #define SC26C94_CREG_STOPCTcd 31 /* Stop Counter command (r) */ #define SC26C94_REG_BCRc 34 /* interrupt Bidding Control Register (rw) */ #define SC26C94_REG_BCRd 35 /* interrupt Bidding Control Register (rw) */ /* global */ #define SC26C94_CREG_POWER_DOWN 36 /* Power Down (w) */ #define SC26C94_CREG_POWER_UP 37 /* Power Up (w) */ #define SC26C94_CREG_DIS_DACKN 38 /* Disable DACKN (w) */ #define SC26C94_CREG_EN_DACKN 39 /* Enable DACKN (w) */ #define SC26C94_REG_CIR 40 /* Current Interrupt Register (r) */ #define SC26C94_REG_GICR 41 /* Global Interrupting Channel Register (r) */ #define SC26C94_REG_IVR 41 /* Interrupt Vector Register (w) */ #define SC26C94_REG_GIBCR 42 /* Global Interrupt Byte Count Register (r) */ #define SC26C94_CREG_UPDATE_CIR 42 /* Update CIR (w) */ #define SC26C94_REG_GRHR 43 /* Global Receive Holding Reg (RxFIFO) (r) */ #define SC26C94_REG_GTHR 43 /* Global Transmit Holding Reg (TxFIFO) (w) */ #define SC26C94_REG_ICR 44 /* Interrupt Control Register (ICR) (rw) */ #define SC26C94_REG_BRGRATE 45 /* BRG Rate (w) */ #define SC26C94_CREG_CLK_DIV2 46 /* Set X1/CLK divide by two (w) */ #define SC26C94_CREG_CLK_NORM 47 /* Set X1/CLK normal (w) */ #define SC26C94_REG_TEST 57 /* Test Mode (rw) */ #define SC26C94_REGCHAN(x,y) (x ## a + ((y & 3) << 3)) /* * Command Register commands */ #define SC26C94_CMD_MR1 1 /* Set MR pointer to MR1 */ #define SC26C94_CMD_RESET_RX 2 /* Reset the receiver */ #define SC26C94_CMD_RESET_TX 3 /* Reset the transmitter */ #define SC26C94_CMD_RESET_ERR 4 /* Clear the error status */ #define SC26C94_CMD_RESET_BCI 5 /* Clear the break detect change ISR bit */ #define SC26C94_CMD_BREAK_ON 6 /* Start sending a break */ #define SC26C94_CMD_BREAK_OFF 7 /* Stop sending a break */ #define SC26C94_CMD_RTSN_ON 8 /* Assert RTSN */ #define SC26C94_CMD_RTSN_OFF 9 /* Negate RTSN */ #define SC26C94_CMD_TIMEOUT_ON 10 /* Enable timeout mode */ #define SC26C94_CMD_MR0 11 /* Set MR pointer to MR0 */ #define SC26C94_CMD_TIMEOUT_OFF 12 /* Disable timeout mode */ /* * register formats */ #define SC26C94_IVC_IVR 0 /* Use the full IVC value as interrupt vector */ #define SC26C94_IVC_CHAN 1 /* Use IVC and channel number to make vector */ #define SC26C94_IVC_TYPE_CHAN 2 /* Use IVC, channel, and type to make vector */ #define SC26C94_IVC_INHIBIT 3 /* Use a vector of 0xff */ #define SC26C94_ISR_IO 128 /* I/O Port Change */ #define SC26C94_ISR_BRKb 64 /* Delta BREAKb */ #define SC26C94_ISR_RXb 32 /* RxRDY/FFULLb */ #define SC26C94_ISR_TXb 16 /* TxRDYb */ #define SC26C94_ISR_CT 8 /* Counter Ready */ #define SC26C94_ISR_BRKa 4 /* Delta BREAKa */ #define SC26C94_ISR_RXa 2 /* RxRDY/FFULLa */ #define SC26C94_ISR_TXa 1 /* TxRDYa */ #define SC26C94_CIR_CHAN(x) (x & 3) #define SC26C94_CIR_TYPE(x) ((x >> 2) & 7) #define SC26C94_CIR_SIZE(x) ((x >> 5) & 7) #define SC26C94_CIR_NONE 0 /* No Interrupt */ #define SC26C94_CIR_STATE 1 /* Change of State */ #define SC26C94_CIR_TX1 2 /* Transmit available */ #define SC26C94_CIR_RX 3 /* Receive available, no error */ #define SC26C94_CIR_BRK 4 /* Receiver break change */ #define SC26C94_CIR_CT 5 /* Counter/Timer */ #define SC26C94_CIR_TX2 6 /* Transmit available (alternate encoding) */ #define SC26C94_CIR_RXE 7 /* Receive available, with errors */ #define SC26C94_SR_BRK 128 /* Received Break */ #define SC26C94_SR_FRAME 64 /* Framing Error */ #define SC26C94_SR_PARITY 32 /* Parity Error */ #define SC26C94_SR_OVER 16 /* Overrun Error */ #define SC26C94_SR_TXEMT 8 /* Tx Empty */ #define SC26C94_SR_TXRDY 4 /* Tx Ready */ #define SC26C94_SR_RXFULL 2 /* Rx Full */ #define SC26C94_SR_RXRDY 1 /* Rx Ready */ #define SC26C94_CR_TX_OFF 8 /* Disable Tx */ #define SC26C94_CR_TX_ON 4 /* Enable Tx */ #define SC26C94_CR_RX_OFF 2 /* Disable Tx */ #define SC26C94_CR_RX_ON 1 /* Enable Rx */ #define SC26C94_MR1_RXRTS 128 /* RxRTS Control */ #define SC26C94_MR1_RXINT1 64 /* RxINT1 Select */ #define SC26C94_MR1_ERRMODE 32 /* Char/Block Error Mode */ #define SC26C94_MR1_PAR_MASK 24 /* Parity Mode */ #define SC26C94_MR1_PAR_WITH 0 /* With Parity */ #define SC26C94_MR1_PAR_FORCE 8 /* Force Parity */ #define SC26C94_MR1_PAR_NONE 16 /* No Parity */ #define SC26C94_MR1_PAR_WAKE 24 /* Wake-Up Mode */ #define SC26C94_MR1_PARODD 4 /* Parity Type (1=odd) */ #define SC26C94_MR1_CSIZE_MASK 1 /*Bits per Character */ #define SC26C94_MR1_CSIZE_5 0 #define SC26C94_MR1_CSIZE_6 1 #define SC26C94_MR1_CSIZE_7 2 #define SC26C94_MR1_CSIZE_8 3 #define SC26C94_MR2_MODE_MASK 192 /* Channel Mode: */ #define SC26C94_MR2_MODE_NORM 0 /* Normal */ #define SC26C94_MR2_MODE_ECHO 64 /* Auto-echo */ #define SC26C94_MR2_MODE_LLOOP 128 /* Local loop */ #define SC26C94_MR2_MODE_RLOOP 192 /* Remote loop */ #define SC26C94_MR2_TXRTS 32 /* TxRTS Control */ #define SC26C94_MR2_TXCTS 16 /* TxCTS Control */ #define SC26C94_MR2_STOP_MASK 15 /*Stop Bit Length: */ #define SC26C94_MR2_STOP_1000 7 /* 1.000 */ #define SC26C94_MR2_STOP_2000 15 /* 2.000 */ #define SC26C94_IO_3b 128 #define SC26C94_IO_2b 64 #define SC26C94_IO_3a 32 #define SC26C94_IO_2a 16 #define SC26C94_IO_1b 8 #define SC26C94_IO_0b 4 #define SC26C94_IO_1a 2 #define SC26C94_IO_0a 1 #define SC26C94_IOPCR_INPUT 0 #define SC26C94_IOPCR_OUTPUT 1 #define SC26C94_IOPCR_CLOCK1 2 /* TxC or RxC, 16x or 1x, or C/T, dep on port */ #define SC26C94_IOPCR_CLOCK2 3 /* TxC or RxC, 16x or 1x, depending on port */ #define SC26C94_IPCR_DELTA1b 128 #define SC26C94_IPCR_DELTA0b 64 #define SC26C94_IPCR_DELTA1a 32 #define SC26C94_IPCR_DELTA0a 16 #define SC26C94_IPCR_STATE1b 8 #define SC26C94_IPCR_STATE0b 4 #define SC26C94_IPCR_STATE1a 2 #define SC26C94_IPCR_STATE0a 1 /* * */ #define SC26C94_PORT_A 0 #define SC26C94_PORT_B 1 #define SC26C94_PORT_C 2 #define SC26C94_PORT_D 3 /* * */ struct sc26c94_chan { struct sc26c94 *quart; int channel; struct gs_port gs; char xchar; }; struct sc26c94 { struct sc26c94_chan chans[4]; /* We do all register I/O through these function calls. It may be a bit slower than doing it directly, but it should be worth it from a portability standpoint. */ u8 (*readreg)(struct sc26c94 *quart, int register); void (*writereg)(struct sc26c94 *quart, int register, u8 value); /* The 2694 does not have dedicated pins for DTR and so forth. It does have generic I/O pins, though. Since different machines may implement (or not implement) this functionality using different I/O pins, we call back to the platform-specific code to handle this. The set_ routines are used to send status to the peer, the get_ routines are used to get the peer's status. The _handshake routines deal with DTR/DSR, the _flow routines deal with RTS/CTS. These routines may be called with interrupts on, so they will need to disable interrupts if appropriate. */ void (*set_handshake)(struct sc26c94 *quart, int channel, int ready); int (*get_handshake)(struct sc26c94 *quart, int channel); void (*set_flow)(struct sc26c94 *quart, int channel, int on); int (*get_flow)(struct sc26c94 *quart, int channel); /* is this used? */ int (*get_cd)(struct sc26c94 *quart, int channel); /* It is not possible on the 2694 to set all the channels to arbitrary baud rates independently of one another. The baud rate chosen for one channel affects the set of available baud rates on other channels. (See the data sheet for full details.) On a given machine, some channels may be dedicated to a keyboard or mouse interface, while others may be unused. By giving these channels a more limited selection of baud rates, the other channels can have a larger selection. So we leave it to the platform-specific code to decide how to manage the baud-rate generators. */ int (*set_speed)(struct sc26c94 *quart, int channel, int bps); /* platform-specific code can point machdep to a struct that stores i/o addresses or whatever */ void *machdep; /* shadow copies of the write-only registers */ u8 iopcr[4]; u8 acr[2]; u8 imr[2]; /* interrupt handlers for the change-of-state detectors for the i/o pins. indexed as delta_handlers[channel][ionum] */ struct { void (*handler)(struct sc26c94 *quart, void *data, int state); void *data; } delta_handlers[4][2]; }; /* * */ void sc26c94_command(struct sc26c94 *quart, int channel, int command); int sc26c94_read_mr(struct sc26c94 *quart, int channel, int index); void sc26c94_write_mr(struct sc26c94 *quart, int channel, int index, int value); int sc26c94_prepare_interrupt(struct sc26c94 *quart); int sc26c94_cause_interrupt(struct sc26c94 *quart); void sc26c94_immediate_putc(struct sc26c94 *quart, int channel, unsigned char c); unsigned char sc26c94_immediate_getc(struct sc26c94 *quart, int channel); int sc26c94_read_io(struct sc26c94 *quart, int channum, int ionum); void sc26c94_write_io(struct sc26c94 *quart, int channum, int ionum, int val); void sc26c94_set_io_dir(struct sc26c94 *quart, int channum, int ionum, int val); void sc26c94_register_delta_int(struct sc26c94 *quart, int channum, int ionum, void (*handler)(struct sc26c94 *quart, void *data, int state), void *data); void sc26c94_unregister_delta_int(struct sc26c94 *quart, int channum, int ionum); void sc29c94_interrupt(int vector, void *data, struct pt_regs *fp); int sc26c94_open(struct sc26c94 *quart, struct tty_struct *tty, struct file *filp); void sc26c94_init_drivers(struct tty_driver *tty_driver, struct tty_driver *callout_driver); void sc26c94_init_chanstructs(struct sc26c94 *quart); void sc26c94_init_chip(struct sc26c94 *quart); Index: Makefile =================================================================== RCS file: /cvsroot/linux-vax/kernel-2.4/drivers/char/Makefile,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- Makefile 11 Apr 2002 13:30:51 -0000 1.2 +++ Makefile 29 May 2002 03:37:39 -0000 1.3 @@ -211,6 +211,7 @@ obj-$(CONFIG_H8) += h8.o obj-$(CONFIG_PPDEV) += ppdev.o obj-$(CONFIG_DZ) += dz.o +obj-$(CONFIG_VXT2694) += sc26c94.o vxt2694.o generic_serial.o obj-$(CONFIG_NWBUTTON) += nwbutton.o obj-$(CONFIG_NWFLASH) += nwflash.o Index: tty_io.c =================================================================== RCS file: /cvsroot/linux-vax/kernel-2.4/drivers/char/tty_io.c,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- tty_io.c 11 Apr 2002 13:30:59 -0000 1.5 +++ tty_io.c 29 May 2002 03:37:39 -0000 1.6 @@ -2378,4 +2378,7 @@ #ifdef CONFIG_A2232 a2232board_init(); #endif +#ifdef CONFIG_VXT2694 + vxt2694_init(); +#endif } |