/* * Low-level parallel port routines for the SGI Indy/Indigo2 builtin port * * Copyright (c) 2002 Vincent Stehlé * (shamelessly based on other parport_xxx.c) * See the "COPYING" license file at the top-level. * * PI1 registers have the same bits as the PC parallel port registers, but * all PI1 bits are "direct"; i.e. no invertion: A 1 is TTL "high" (5 V), * a 0 is TTL "low" (0 V). There are also some bits on the PI1, which have * no equivalents on the PC. They are for non-SPP modes. * * Here follows a little summary of the signals and their meanings: * * A "/" in front of a signal means that the _bit_ is inverted: i.e. a "1" * bit value means a TTL "low" (0 V) signal. * * A "-" in front of a signal means that it is low active: i.e. a TTL "low" * (0 V) value means that the signal is active. * * Internal only signals are between ()'s. * * Control * ------- * PC SGI * * 7 6 5 4 3 2 1 0 * \ \ \ \ \ \ \ `- /-STROBE -STROBE * \ \ \ \ \ \ `-- /-AUTO FEED -AFD * \ \ \ \ \ `--- -INIT -INIT * \ \ \ \ `---- /-SELECT -SLIN * \ \ \ `----- (IRQ 1=enabled) * \ \ `------ (DIRECTION 0=forward) * \ `------- (SEL ?) * `-------- * * Status * ------ * PC SGI * * 7 6 5 4 3 2 1 0 * \ \ \ \ \ \ \ `- DEVID * \ \ \ \ \ \ `-- " " * \ \ \ \ \ `--- NOINK * \ \ \ \ `---- -ERROR -ERROR * \ \ \ `----- SELECT IN ONLINE * \ \ `------ PAPER END PE * \ `------- -ACK -ACK * `-------- / BUSY BUSY * * Note that some of those information have been "guessed" with a multimeter * and some spare nights :) * * Reminder: all functions await PC-style values, i.e. one should convert * status and control. */ #include #include #include #include #include #include #undef DEBUG #ifdef DEBUG #define DPRINTK printk #else #define DPRINTK(x...) do { } while (0) #endif const unsigned char control_invert = PARPORT_CONTROL_STROBE | PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_SELECT; const unsigned char status_invert = PARPORT_STATUS_BUSY; static struct parport *this_port = NULL; static inline void debug_dump_registers(void) { #ifdef DEBUG printk(KERN_DEBUG "PI1 registers:\n" " data= %02x\n" " control= %02x\n" " status= %02x\n" " dma control= %02x\n" " interrupt status= %02x\n" " interrupt mask= %02x\n" " timer 1= %02x\n" " timer 2= %02x\n" " timer 3= %02x\n" " timer 4= %02x\n" , sgioc->pport.data, sgioc->pport.ctrl, sgioc->pport.status, sgioc->pport.dmactrl, sgioc->pport.intstat, sgioc->pport.intmask, sgioc->pport.timer1, sgioc->pport.timer2, sgioc->pport.timer3, sgioc->pport.timer4 ); #endif } static unsigned char pi1_read_data(struct parport *p) { unsigned char data = sgioc->pport.data; DPRINTK("pi1_read_data: %#x\n", data); return data; } static void pi1_write_data(struct parport *p, unsigned char data) { DPRINTK("pi1_write_data: %#x\n", data); sgioc->pport.data = data; } static unsigned char pi1_read_control(struct parport *p) { const unsigned char mask = PARPORT_CONTROL_STROBE | PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_INIT | PARPORT_CONTROL_SELECT; unsigned char pctrl = sgioc->pport.ctrl, control = ((pctrl & mask) ^ control_invert); DPRINTK("pi1_read_control: %#x, %#x\n", pctrl, control); return control; } static void pi1_write_control(struct parport *p, unsigned char control) { const unsigned char mask = PARPORT_CONTROL_STROBE | PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_INIT | PARPORT_CONTROL_SELECT, /* we enforce some necessary bits */ force = 0x30; unsigned char pctrl = ((control & mask) ^ control_invert) | force; DPRINTK("pi1_write_control: %#x, %#x\n", control, pctrl); sgioc->pport.ctrl = pctrl; } static unsigned char pi1_frob_control(struct parport *p, unsigned char mask, unsigned char val) { unsigned char old; DPRINTK(KERN_DEBUG "pi1_frob_control mask %02x, value %02x\n",mask,val); old = pi1_read_control(p); pi1_write_control(p, (old & ~mask) ^ val); return old; } static unsigned char pi1_read_status(struct parport *p) { const unsigned mask = PARPORT_STATUS_ERROR | PARPORT_STATUS_SELECT | PARPORT_STATUS_PAPEROUT | PARPORT_STATUS_ACK | PARPORT_STATUS_BUSY; unsigned char pstat = sgioc->pport.status, status = ((pstat & mask) ^ status_invert); DPRINTK("pi1_read_status: %#x, %#x\n", pstat, status); return status; } static void pi1_init_state(struct pardevice *d, struct parport_state *s) { DPRINTK("pi1_init_state\n"); } static void pi1_save_state(struct parport *p, struct parport_state *s) { DPRINTK("pi1_save_state\n"); } static void pi1_restore_state(struct parport *p, struct parport_state *s) { DPRINTK("pi1_restore_state\n"); } static void pi1_interrupt(int irq, void *dev_id, struct pt_regs *regs) { DPRINTK("pi1_interrupt\n"); parport_generic_irq(irq, (struct parport *) dev_id, regs); } static void pi1_enable_irq(struct parport *p) { DPRINTK("pi1_enable_irq\n"); } static void pi1_disable_irq(struct parport *p) { DPRINTK("pi1_disable_irq\n"); } static void pi1_data_forward(struct parport *p) { DPRINTK("pi1_data_forward\n"); sgioc->pport.ctrl &= ~PI1_CTRL_DIR; } static void pi1_data_reverse(struct parport *p) { DPRINTK("pi1_data_reverse\n"); sgioc->pport.ctrl |= PI1_CTRL_DIR; } static void pi1_inc_use_count(void) { DPRINTK("pi1_inc_use_count\n"); MOD_INC_USE_COUNT; } static void pi1_dec_use_count(void) { DPRINTK("pi1_dec_use_count\n"); MOD_DEC_USE_COUNT; } static struct parport_operations pi1_ops = { pi1_write_data, pi1_read_data, pi1_write_control, pi1_read_control, pi1_frob_control, pi1_read_status, pi1_enable_irq, pi1_disable_irq, pi1_data_forward, pi1_data_reverse, pi1_init_state, pi1_save_state, pi1_restore_state, pi1_inc_use_count, pi1_dec_use_count, parport_ieee1284_epp_write_data, parport_ieee1284_epp_read_data, parport_ieee1284_epp_write_addr, parport_ieee1284_epp_read_addr, parport_ieee1284_ecp_write_data, parport_ieee1284_ecp_read_data, parport_ieee1284_ecp_write_addr, parport_ieee1284_write_compat, parport_ieee1284_read_nibble, parport_ieee1284_read_byte }; static void init_hardware(void) { sgioc->pport.intmask = 0xfc; sgioc->pport.dmactrl = 1; sgioc->pport.ctrl = 0x3f; sgioc->pport.timer1 = 0; sgioc->pport.timer2 = 0; sgioc->pport.timer3 = 0; sgioc->pport.timer4 = 0; sgioc->pport.data = 0; } static int __init pi1_init(void) { struct parport *p; DPRINTK("pi1_init\n"); p = parport_register_port((unsigned long) &sgioc->pport.data, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pi1_ops); if (!p) return -EBUSY; /* TODO err = request_irq(IRQ_AMIGA_CIAA_FLG, amiga_interrupt, 0, p->name, p); if (err) goto out_irq; */ /* tell what we are capable of */ p->modes = PARPORT_MODE_PCSPP; /* TODO | PARPORT_MODE_TRISTATE;*/ /* remember for exit */ this_port = p; /* put hardware into known initial state */ init_hardware(); printk(KERN_INFO "%s: SGI PI1\n", p->name); parport_proc_register(p); parport_announce_port(p); return 0; } static void __exit pi1_exit(void) { DPRINTK("pi1_exit\n"); if (this_port->irq != PARPORT_IRQ_NONE) free_irq(this_port->irq, this_port); parport_proc_unregister(this_port); parport_unregister_port(this_port); } MODULE_AUTHOR("Vincent Stehle "); MODULE_DESCRIPTION("Driver for SGI Indy/Indigo2 parallel port"); MODULE_SUPPORTED_DEVICE("SGI PI1 parallel port"); MODULE_LICENSE("GPL"); module_init(pi1_init); module_exit(pi1_exit);