/* $Id: parport.c,v 1.8 2001/09/26 11:51:52 bjornw Exp $ * * Elinux parallel port driver * NOTE! * Since par0 shares DMA with ser2 and par 1 shares DMA with ser3 * this should be handled if both are enabled at the same time. * THIS IS NOT HANDLED YET! * * Copyright (c) 2001 Axis Communications AB * * Author: Fredrik Hugosson * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef DEBUG #ifdef DEBUG #define DPRINTK printk #else static inline int DPRINTK(void *nothing, ...) {return 0;} #endif /* * Etrax100 DMAchannels: * Par0 out : DMA2 * Par0 in : DMA3 * Par1 out : DMA4 * Par1 in : DMA5 * NOTE! par0 is shared with ser2 and par1 is shared with ser3 regarding * DMA and DMA irq */ //#define CONFIG_PAR0_INT 1 //#define CONFIG_PAR1_INT 1 #define SETF(var, reg, field, val) \ var = (var & ~IO_MASK(##reg##, field)) | IO_FIELD(##reg##, field, val) #define SETS(var, reg, field, val) \ var = (var & ~IO_MASK(##reg##, field)) | IO_STATE(##reg##, field, val) struct etrax100par_struct { /* parallell port control */ volatile u32 *reg_ctrl_data; /* R_PARx_CTRL_DATA */ const volatile u32 *reg_status_data; /* R_PARx_STATUS_DATA */ volatile u32 *reg_config; /* R_PARx_CONFIG */ volatile u32 *reg_delay; /* R_PARx_DELAY */ /* DMA control */ int odma; unsigned long dma_irq; /* bitnr in R_IRQ_MASK2 for dmaX_descr */ volatile char *oclrintradr; /* adr to R_DMA_CHx_CLR_INTR, output */ volatile u32 *ofirstadr; /* adr to R_DMA_CHx_FIRST, output */ volatile char *ocmdadr; /* adr to R_DMA_CHx_CMD, output */ volatile char *iclrintradr; /* adr to R_DMA_CHx_CLR_INTR, input */ volatile u32 *ifirstadr; /* adr to R_DMA_CHx_FIRST, input */ volatile char *icmdadr; /* adr to R_DMA_CHx_CMD, input */ /* Non DMA interrupt stuff */ unsigned long int_irq; /* R_VECT_MASK_RD */ const volatile u32 *irq_mask_rd; /* R_IRQ_MASKX_RD */ volatile u32 *irq_mask_clr; /* R_IRQ_MASKX_RD */ const volatile u32 *irq_read; /* R_IRQ_READX */ volatile u32 *irq_mask_set; /* R_IRQ_MASKX_SET */ unsigned long irq_mask_tx; /* bitmask in R_IRQ_ for tx (ready) int */ unsigned long irq_mask_rx; /* bitmask in R_IRQ_ for rx (data) int */ unsigned long irq_mask_ecp_cmd; /* mask in R_IRQ_ for ecp_cmd int */ unsigned long irq_mask_peri; /* bitmask in R_IRQ_ for peri int */ int portnr; /* ----- end of fields initialised in port_table[] below ----- */ struct parport *port; /* Shadow registers */ volatile unsigned long reg_ctrl_data_shadow; /* for R_PARx_CTRL_DATA */ volatile unsigned long reg_config_shadow; /* for R_PARx_CONFIG */ volatile unsigned long reg_delay_shadow; /* for R_PARx_DELAY */ }; /* Always have the complete structs here, even if the port is not used! * (that way we can index this by the port number) */ static struct etrax100par_struct port_table[] = { { R_PAR0_CTRL_DATA, R_PAR0_STATUS_DATA, R_PAR0_CONFIG, R_PAR0_DELAY, /* DMA interrupt stuff */ 2, 1U << 4, /* uses DMA 2 and 3 */ R_DMA_CH2_CLR_INTR, R_DMA_CH2_FIRST, R_DMA_CH2_CMD, R_DMA_CH3_CLR_INTR, R_DMA_CH3_FIRST, R_DMA_CH3_CMD, /* Non DMA interrupt stuff */ IO_BITNR(R_VECT_MASK_RD, par0), R_IRQ_MASK0_RD, R_IRQ_MASK0_CLR, R_IRQ_READ0, R_IRQ_MASK0_SET, IO_FIELD(R_IRQ_MASK0_RD, par0_ready, 1U), /* tx (ready)*/ IO_FIELD(R_IRQ_MASK0_RD, par0_data, 1U), /* rx (data)*/ IO_FIELD(R_IRQ_MASK0_RD, par0_ecp_cmd, 1U), /* ecp_cmd */ IO_FIELD(R_IRQ_MASK0_RD, par0_peri, 1U), /* peri */ 0 }, { R_PAR1_CTRL_DATA, R_PAR1_STATUS_DATA, R_PAR1_CONFIG, R_PAR1_DELAY, /* DMA interrupt stuff */ 4, 1U << 8, /* uses DMA 4 and 5 */ R_DMA_CH4_CLR_INTR, R_DMA_CH4_FIRST, R_DMA_CH4_CMD, R_DMA_CH5_CLR_INTR, R_DMA_CH5_FIRST, R_DMA_CH5_CMD, /* Non DMA interrupt stuff */ IO_BITNR(R_VECT_MASK_RD, par1), R_IRQ_MASK1_RD, R_IRQ_MASK1_CLR, R_IRQ_READ1, R_IRQ_MASK1_SET, IO_FIELD(R_IRQ_MASK1_RD, par1_ready, 1U), /* tx (ready)*/ IO_FIELD(R_IRQ_MASK1_RD, par1_data, 1U), /* rx (data)*/ IO_FIELD(R_IRQ_MASK1_RD, par1_ecp_cmd, 1U), /* ecp_cmd */ IO_FIELD(R_IRQ_MASK1_RD, par1_peri, 1U), /* peri */ 1 } }; #define NR_PORTS (sizeof(port_table)/sizeof(struct etrax100par_struct)) static void parport_etrax_write_data(struct parport *p, unsigned char value) { struct etrax100par_struct *info = (struct etrax100par_struct *)p->private_data; DPRINTK("* E100 PP %d: etrax_write_data %02X\n", p->portnum, value); SETF(info->reg_ctrl_data_shadow, R_PAR0_CTRL_DATA, data, value); *info->reg_ctrl_data = info->reg_ctrl_data_shadow; } static unsigned char parport_etrax_read_data(struct parport *p) { unsigned char ret; struct etrax100par_struct *info = (struct etrax100par_struct *)p->private_data; ret = IO_EXTRACT(R_PAR0_STATUS_DATA, data, *info->reg_status_data); DPRINTK("* E100 PP %d: etrax_read_data %02X\n", p->portnum, ret); return ret; } static void parport_etrax_write_control(struct parport *p, unsigned char control) { struct etrax100par_struct *info = (struct etrax100par_struct *)p->private_data; DPRINTK("* E100 PP %d: etrax_write_control %02x\n", p->portnum, control); SETF(info->reg_ctrl_data_shadow, R_PAR0_CTRL_DATA, strb, (control & PARPORT_CONTROL_STROBE) > 0); SETF(info->reg_ctrl_data_shadow, R_PAR0_CTRL_DATA, autofd, (control & PARPORT_CONTROL_AUTOFD) > 0); SETF(info->reg_ctrl_data_shadow, R_PAR0_CTRL_DATA, init, (control & PARPORT_CONTROL_INIT) > 0); SETF(info->reg_ctrl_data_shadow, R_PAR0_CTRL_DATA, seli, (control & PARPORT_CONTROL_SELECT) > 0); *info->reg_ctrl_data = info->reg_ctrl_data_shadow; } static unsigned char parport_etrax_read_control( struct parport *p) { unsigned char ret = 0; struct etrax100par_struct *info = (struct etrax100par_struct *)p->private_data; if (IO_EXTRACT(R_PAR0_CTRL_DATA, strb, info->reg_ctrl_data_shadow)) ret |= PARPORT_CONTROL_STROBE; if (IO_EXTRACT(R_PAR0_CTRL_DATA, autofd, info->reg_ctrl_data_shadow)) ret |= PARPORT_CONTROL_AUTOFD; if (IO_EXTRACT(R_PAR0_CTRL_DATA, init, info->reg_ctrl_data_shadow)) ret |= PARPORT_CONTROL_INIT; if (IO_EXTRACT(R_PAR0_CTRL_DATA, seli, info->reg_ctrl_data_shadow)) ret |= PARPORT_CONTROL_SELECT; DPRINTK("* E100 PP %d: etrax_read_control %02x\n", p->portnum, ret); return ret; } static unsigned char parport_etrax_frob_control(struct parport *p, unsigned char mask, unsigned char val) { unsigned char old; DPRINTK("* E100 PP %d: frob_control mask %02x, value %02x\n", p->portnum, mask, val); old = parport_etrax_read_control(p); parport_etrax_write_control(p, (old & ~mask) ^ val); return old; } static unsigned char parport_etrax_read_status(struct parport *p) { unsigned char ret = 0; struct etrax100par_struct *info = (struct etrax100par_struct *)p->private_data; if (IO_EXTRACT(R_PAR0_STATUS_DATA, fault, *info->reg_status_data)) ret |= PARPORT_STATUS_ERROR; if (IO_EXTRACT(R_PAR0_STATUS_DATA, sel, *info->reg_status_data)) ret |= PARPORT_STATUS_SELECT; if (!IO_EXTRACT(R_PAR0_STATUS_DATA, perr, *info->reg_status_data)) ret |= PARPORT_STATUS_PAPEROUT; if (IO_EXTRACT(R_PAR0_STATUS_DATA, ack, *info->reg_status_data)) ret |= PARPORT_STATUS_ACK; if (!IO_EXTRACT(R_PAR0_STATUS_DATA, busy, *info->reg_status_data)) ret |= PARPORT_STATUS_BUSY; DPRINTK("* E100 PP %d: status register %04x\n", p->portnum, *info->reg_status_data); DPRINTK("* E100 PP %d: read_status %02x\n", p->portnum, ret); return ret; } static void parport_etrax_enable_irq(struct parport *p) { struct etrax100par_struct *info = (struct etrax100par_struct *)p->private_data; *info->irq_mask_set = info->irq_mask_tx; DPRINTK("* E100 PP %d: enable irq\n", p->portnum); } static void parport_etrax_disable_irq(struct parport *p) { struct etrax100par_struct *info = (struct etrax100par_struct *)p->private_data; *info->irq_mask_clr = info->irq_mask_tx; DPRINTK("* E100 PP %d: disable irq\n", p->portnum); } static void parport_etrax_data_forward(struct parport *p) { struct etrax100par_struct *info = (struct etrax100par_struct *)p->private_data; DPRINTK("* E100 PP %d: forward mode\n", p->portnum); SETS(info->reg_ctrl_data_shadow, R_PAR0_CTRL_DATA, oe, enable); *info->reg_ctrl_data = info->reg_ctrl_data_shadow; } static void parport_etrax_data_reverse(struct parport *p) { struct etrax100par_struct *info = (struct etrax100par_struct *)p->private_data; DPRINTK("* E100 PP %d: reverse mode\n", p->portnum); SETS(info->reg_ctrl_data_shadow, R_PAR0_CTRL_DATA, oe, disable); *info->reg_ctrl_data = info->reg_ctrl_data_shadow; } static void parport_etrax_init_state(struct pardevice *dev, struct parport_state *s) { DPRINTK("* E100 PP: parport_etrax_init_state\n"); } static void parport_etrax_save_state(struct parport *p, struct parport_state *s) { DPRINTK("* E100 PP: parport_etrax_save_state\n"); } static void parport_etrax_restore_state(struct parport *p, struct parport_state *s) { DPRINTK("* E100 PP: parport_etrax_restore_state\n"); } static void parport_etrax_inc_use_count(void) { MOD_INC_USE_COUNT; } static void parport_etrax_dec_use_count(void) { MOD_DEC_USE_COUNT; } static struct parport_operations pp_etrax_ops = { parport_etrax_write_data, parport_etrax_read_data, parport_etrax_write_control, parport_etrax_read_control, parport_etrax_frob_control, parport_etrax_read_status, parport_etrax_enable_irq, parport_etrax_disable_irq, parport_etrax_data_forward, parport_etrax_data_reverse, parport_etrax_init_state, parport_etrax_save_state, parport_etrax_restore_state, parport_etrax_inc_use_count, parport_etrax_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 parport_etrax_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct etrax100par_struct *info = (struct etrax100par_struct *) ((struct parport *)dev_id)->private_data; DPRINTK("* E100 PP %d: Interrupt received\n", ((struct parport *)dev_id)->portnum); *info->irq_mask_clr = info->irq_mask_tx; parport_generic_irq(irq, (struct parport *)dev_id, regs); } /* ----------- Initialisation code --------------------------------- */ static void __init parport_etrax_show_parallel_version(void) { printk("ETRAX 100LX parallel port driver v1.0, (c) 2001 Axis Communications AB\n"); } #ifdef CONFIG_ETRAX_PAR0_DMA #define PAR0_USE_DMA 1 #else #define PAR0_USE_DMA 0 #endif #ifdef CONFIG_ETRAX_PAR1_DMA #define PAR1_USE_DMA 1 #else #define PAR1_USE_DMA 0 #endif static void __init parport_etrax_init_registers(void) { struct etrax100par_struct *info; int i; for (i = 0, info = port_table; i < 2; i++, info++) { #ifndef CONFIG_ETRAX_PARALLEL_PORT0 if (i == 0) continue; #endif #ifndef CONFIG_ETRAX_PARALLEL_PORT1 if (i == 1) continue; #endif info->reg_config_shadow = IO_STATE(R_PAR0_CONFIG, iseli, inv) | IO_STATE(R_PAR0_CONFIG, iautofd, inv) | IO_STATE(R_PAR0_CONFIG, istrb, inv) | IO_STATE(R_PAR0_CONFIG, iinit, inv) | IO_STATE(R_PAR0_CONFIG, rle_in, disable) | IO_STATE(R_PAR0_CONFIG, rle_out, disable) | IO_STATE(R_PAR0_CONFIG, enable, on) | IO_STATE(R_PAR0_CONFIG, force, off) | IO_STATE(R_PAR0_CONFIG, ign_ack, wait) | IO_STATE(R_PAR0_CONFIG, oe_ack, wait_oe) | IO_STATE(R_PAR0_CONFIG, mode, manual); if ((i == 0 && PAR0_USE_DMA) || (i == 1 && PAR1_USE_DMA)) info->reg_config_shadow |= IO_STATE(R_PAR0_CONFIG, dma, enable); else info->reg_config_shadow |= IO_STATE(R_PAR0_CONFIG, dma, disable); *info->reg_config = info->reg_config_shadow; info->reg_ctrl_data_shadow = IO_STATE(R_PAR0_CTRL_DATA, peri_int, nop) | IO_STATE(R_PAR0_CTRL_DATA, oe, enable) | IO_STATE(R_PAR0_CTRL_DATA, seli, inactive) | IO_STATE(R_PAR0_CTRL_DATA, autofd, inactive) | IO_STATE(R_PAR0_CTRL_DATA, strb, inactive) | IO_STATE(R_PAR0_CTRL_DATA, init, inactive) | IO_STATE(R_PAR0_CTRL_DATA, ecp_cmd, data) | IO_FIELD(R_PAR0_CTRL_DATA, data, 0); *info->reg_ctrl_data = info->reg_ctrl_data_shadow; /* Clear peri int without setting shadow */ *info->reg_ctrl_data = info->reg_ctrl_data_shadow | IO_STATE(R_PAR0_CTRL_DATA, peri_int, ack); info->reg_delay_shadow = IO_FIELD(R_PAR0_DELAY, setup, 5) | IO_FIELD(R_PAR0_DELAY, strobe, 5) | IO_FIELD(R_PAR0_DELAY, hold, 5); *info->reg_delay = info->reg_delay_shadow; } #ifdef CONFIG_ETRAX_PARALLEL_PORT0 #ifdef CONFIG_ETRAX_PAR0_DMA RESET_DMA(PAR0_TX_DMA_NBR); WAIT_DMA(PAR0_TX_DMA_NBR); #ifdef CONFIG_ETRAX_SERIAL_PORT2 printk(" Warning - DMA clash with ser2!\n"); #endif /* SERIAL_PORT2 */ #endif /* DMA */ #endif /* PORT0 */ #ifdef CONFIG_ETRAX_PARALLEL_PORT1 #ifdef CONFIG_ETRAX_PAR1_DMA RESET_DMA(PAR1_TX_DMA_NBR); WAIT_DMA(PAR1_TX_DMA_NBR); #ifdef CONFIG_ETRAX_SERIAL_PORT3 printk(" Warning - DMA clash with ser3!\n"); #endif /* SERIAL_PORT3 */ #endif /* DMA */ #endif /* PORT1 */ } int __init parport_etrax_init(void) { struct parport *p; int port_exists = 0; int i; struct etrax100par_struct *info; const char *names[] = { "parallel 0 tx+rx", "parallel 1 tx+rx" }; parport_etrax_show_parallel_version(); parport_etrax_init_registers(); for (i = 0, info = port_table; i < NR_PORTS; i++, info++) { #ifndef CONFIG_ETRAX_PARALLEL_PORT0 if (i == 0) continue; #endif #ifndef CONFIG_ETRAX_PARALLEL_PORT1 if (i == 1) continue; #endif p = parport_register_port((unsigned long)0, info->int_irq, PARPORT_DMA_NONE, &pp_etrax_ops); if (!p) continue; info->port = p; p->private_data = info; /* Axis FIXME: Set mode flags. */ /* p->modes = PARPORT_MODE_TRISTATE | PARPORT_MODE_SAFEININT; */ if(request_irq(info->int_irq, parport_etrax_interrupt, SA_SHIRQ, names[i], p)) { parport_unregister_port (p); continue; } printk(KERN_INFO "%s: ETRAX 100LX port %d using irq\n", p->name, i); parport_proc_register(p); parport_announce_port(p); port_exists = 1; } return port_exists; } void __exit parport_etrax_exit(void) { int i; struct etrax100par_struct *info; for (i = 0, info = port_table; i < NR_PORTS; i++, info++) { #ifndef CONFIG_ETRAX_PARALLEL_PORT0 if (i == 0) continue; #endif #ifndef CONFIG_ETRAX_PARALLEL_PORT1 if (i == 1) continue; #endif if (info->int_irq != PARPORT_IRQ_NONE) free_irq(info->int_irq, info->port); parport_proc_unregister(info->port); parport_unregister_port(info->port); } }