patch-2.4.5 linux/arch/cris/drivers/parport.c
Next file: linux/arch/cris/drivers/serial.c
Previous file: linux/arch/cris/drivers/i2c.h
Back to the patch index
Back to the overall index
- Lines: 592
- Date:
Tue May 1 16:04:56 2001
- Orig file:
v2.4.4/linux/arch/cris/drivers/parport.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.4/linux/arch/cris/drivers/parport.c linux/arch/cris/drivers/parport.c
@@ -0,0 +1,591 @@
+/* $Id: parport.c,v 1.4 2001/04/06 13:04:02 hugo 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/parport.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+
+#include <asm/setup.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <asm/svinto.h>
+
+
+#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 hared 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 */
+ const volatile u8 *istatusadr; /* adr to R_DMA_CHx_STATUS, input */
+ volatile u32 *ihwswadr; /* adr to R_DMA_CHx_HWSW, 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 etrax_dma_descr tr_descr;
+ // unsigned char tr_buf[LP_BUFFER_SIZE];
+ // const unsigned char *tr_buf_curr; /* current char sent */
+ // const unsigned char *tr_buf_last; /* last char in buf */
+
+ // int fifo_magic; /* fifo amount - bytes left in dma buffer */
+ // unsigned char fifo_didmagic; /* a fifo eop has been forced */
+ // volatile int tr_running; /* 1 if output is running */
+
+ 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,
+ R_DMA_CH3_STATUS,
+ R_DMA_CH3_HWSW,
+ /* 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,
+ R_DMA_CH5_STATUS,
+ R_DMA_CH5_HWSW,
+ /* 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
+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
+parport_etrax_init_registers(void)
+{
+ struct etrax100par_struct *info;
+ int i;
+
+ /* The different times below will be (value*160 + 20) ns, */
+ /* i.e. 20ns-4.98us. E.g. if setup is set to 00110 (0x6), */
+ /* the setup time will be (6*160+20) = 980ns. */
+
+ 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(2);
+ WAIT_DMA(2);
+#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(4);
+ WAIT_DMA(4);
+#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);
+ }
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)