patch-2.1.68 linux/drivers/misc/parport_ax.c
Next file: linux/drivers/misc/parport_ieee1284.c
Previous file: linux/drivers/misc/Makefile
Back to the patch index
Back to the overall index
- Lines: 555
- Date:
Sat Nov 29 16:19:40 1997
- Orig file:
v2.1.67/linux/drivers/misc/parport_ax.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.67/linux/drivers/misc/parport_ax.c linux/drivers/misc/parport_ax.c
@@ -0,0 +1,554 @@
+/* $Id: parport_ax.c,v 1.2 1997/10/25 17:27:03 philip Exp $
+ * Parallel-port routines for Sun Ultra/AX architecture
+ *
+ * Author: Eddie C. Dost <ecd@skynet.be>
+ *
+ * based on work by:
+ * Phil Blundell <Philip.Blundell@pobox.com>
+ * Tim Waugh <tim@cyberelk.demon.co.uk>
+ * Jose Renau <renau@acm.org>
+ * David Campbell <campbell@tirian.che.curtin.edu.au>
+ * Grant Guenther <grant@torque.net>
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include <linux/parport.h>
+
+#include <asm/ptrace.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/ebus.h>
+#include <asm/ns87303.h>
+
+
+/*
+ * Define this if you have Devices which don't support short
+ * host read/write cycles.
+ */
+#undef HAVE_SLOW_DEVICES
+
+
+#define DATA 0x00
+#define STATUS 0x01
+#define CONTROL 0x02
+
+#define CFIFO 0x400
+#define DFIFO 0x400
+#define TFIFO 0x400
+#define CNFA 0x400
+#define CNFB 0x401
+#define ECR 0x402
+
+static void
+ax_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+ /* NULL function - Does nothing */
+ return;
+}
+
+#if 0
+static unsigned int
+ax_read_configb(struct parport *p)
+{
+ return (unsigned int)inb(p->base + CNFB);
+}
+#endif
+
+static void
+ax_write_data(struct parport *p, unsigned int d)
+{
+ outb(d, p->base + DATA);
+}
+
+static unsigned int
+ax_read_data(struct parport *p)
+{
+ return (unsigned int)inb(p->base + DATA);
+}
+
+static void
+ax_write_control(struct parport *p, unsigned int d)
+{
+ outb(d, p->base + CONTROL);
+}
+
+static unsigned int
+ax_read_control(struct parport *p)
+{
+ return (unsigned int)inb(p->base + CONTROL);
+}
+
+static unsigned int
+ax_frob_control(struct parport *p, unsigned int mask, unsigned int val)
+{
+ unsigned int old = (unsigned int)inb(p->base + CONTROL);
+ outb(((old & ~mask) ^ val), p->base + CONTROL);
+ return old;
+}
+
+static void
+ax_write_status(struct parport *p, unsigned int d)
+{
+ outb(d, p->base + STATUS);
+}
+
+static unsigned int
+ax_read_status(struct parport *p)
+{
+ return (unsigned int)inb(p->base + STATUS);
+}
+
+static void
+ax_write_econtrol(struct parport *p, unsigned int d)
+{
+ outb(d, p->base + ECR);
+}
+
+static unsigned int
+ax_read_econtrol(struct parport *p)
+{
+ return (unsigned int)inb(p->base + ECR);
+}
+
+static unsigned int
+ax_frob_econtrol(struct parport *p, unsigned int mask, unsigned int val)
+{
+ unsigned int old = (unsigned int)inb(p->base + ECR);
+ outb(((old & ~mask) ^ val), p->base + ECR);
+ return old;
+}
+
+static void
+ax_change_mode(struct parport *p, int m)
+{
+ ax_frob_econtrol(p, 0xe0, m << 5);
+}
+
+static void
+ax_write_fifo(struct parport *p, unsigned int v)
+{
+ outb(v, p->base + DFIFO);
+}
+
+static unsigned int
+ax_read_fifo(struct parport *p)
+{
+ return inb(p->base + DFIFO);
+}
+
+static void
+ax_disable_irq(struct parport *p)
+{
+ struct linux_ebus_dma *dma = p->private_data;
+ unsigned int dcsr;
+
+ dcsr = readl((unsigned long)&dma->dcsr);
+ dcsr &= ~(EBUS_DCSR_INT_EN);
+ writel(dcsr, (unsigned long)&dma->dcsr);
+}
+
+static void
+ax_enable_irq(struct parport *p)
+{
+ struct linux_ebus_dma *dma = p->private_data;
+ unsigned int dcsr;
+
+ dcsr = readl((unsigned long)&dma->dcsr);
+ dcsr |= EBUS_DCSR_INT_EN;
+ writel(dcsr, (unsigned long)&dma->dcsr);
+}
+
+static void
+ax_release_resources(struct parport *p)
+{
+ if (p->irq != PARPORT_IRQ_NONE) {
+ ax_disable_irq(p);
+ free_irq(p->irq, p);
+ }
+ release_region(p->base, p->size);
+ if (p->modes & PARPORT_MODE_PCECR)
+ release_region(p->base+0x400, 3);
+ release_region((unsigned long)p->private_data,
+ sizeof(struct linux_ebus_dma));
+}
+
+static int
+ax_claim_resources(struct parport *p)
+{
+ /* FIXME check that resources are free */
+ if (p->irq != PARPORT_IRQ_NONE) {
+ request_irq(p->irq, ax_null_intr_func, 0, p->name, p);
+ ax_enable_irq(p);
+ }
+ request_region(p->base, p->size, p->name);
+ if (p->modes & PARPORT_MODE_PCECR)
+ request_region(p->base+0x400, 3, p->name);
+ request_region((unsigned long)p->private_data,
+ sizeof(struct linux_ebus_dma), p->name);
+ return 0;
+}
+
+static void
+ax_save_state(struct parport *p, struct parport_state *s)
+{
+ s->u.pc.ctr = ax_read_control(p);
+ s->u.pc.ecr = ax_read_econtrol(p);
+}
+
+static void
+ax_restore_state(struct parport *p, struct parport_state *s)
+{
+ ax_write_control(p, s->u.pc.ctr);
+ ax_write_econtrol(p, s->u.pc.ecr);
+}
+
+static unsigned int
+ax_epp_read_block(struct parport *p, void *buf, unsigned int length)
+{
+ return 0; /* FIXME */
+}
+
+static unsigned int
+ax_epp_write_block(struct parport *p, void *buf, unsigned int length)
+{
+ return 0; /* FIXME */
+}
+
+static unsigned int
+ax_ecp_read_block(struct parport *p, void *buf, unsigned int length,
+ void (*fn)(struct parport *, void *, unsigned int),
+ void *handle)
+{
+ return 0; /* FIXME */
+}
+
+static unsigned int
+ax_ecp_write_block(struct parport *p, void *buf, unsigned int length,
+ void (*fn)(struct parport *, void *, unsigned int),
+ void *handle)
+{
+ return 0; /* FIXME */
+}
+
+static int
+ax_examine_irq(struct parport *p)
+{
+ return 0; /* FIXME */
+}
+
+static void
+ax_inc_use_count(void)
+{
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+}
+
+static void
+ax_dec_use_count(void)
+{
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+}
+
+static struct parport_operations ax_ops =
+{
+ ax_write_data,
+ ax_read_data,
+
+ ax_write_control,
+ ax_read_control,
+ ax_frob_control,
+
+ ax_write_econtrol,
+ ax_read_econtrol,
+ ax_frob_econtrol,
+
+ ax_write_status,
+ ax_read_status,
+
+ ax_write_fifo,
+ ax_read_fifo,
+
+ ax_change_mode,
+
+ ax_release_resources,
+ ax_claim_resources,
+
+ ax_epp_write_block,
+ ax_epp_read_block,
+
+ ax_ecp_write_block,
+ ax_ecp_read_block,
+
+ ax_save_state,
+ ax_restore_state,
+
+ ax_enable_irq,
+ ax_disable_irq,
+ ax_examine_irq,
+
+ ax_inc_use_count,
+ ax_dec_use_count
+};
+
+
+/******************************************************
+ * MODE detection section:
+ */
+
+/* Check for ECP
+ *
+ * Old style XT ports alias io ports every 0x400, hence accessing ECR
+ * on these cards actually accesses the CTR.
+ *
+ * Modern cards don't do this but reading from ECR will return 0xff
+ * regardless of what is written here if the card does NOT support
+ * ECP.
+ *
+ * We will write 0x2c to ECR and 0xcc to CTR since both of these
+ * values are "safe" on the CTR since bits 6-7 of CTR are unused.
+ */
+static int parport_ECR_present(struct parport *pb)
+{
+ unsigned int r, octr = pb->ops->read_control(pb),
+ oecr = pb->ops->read_econtrol(pb);
+
+ r = pb->ops->read_control(pb);
+ if ((pb->ops->read_econtrol(pb) & 0x3) == (r & 0x3)) {
+ pb->ops->write_control(pb, r ^ 0x2 ); /* Toggle bit 1 */
+
+ r = pb->ops->read_control(pb);
+ if ((pb->ops->read_econtrol(pb) & 0x2) == (r & 0x2)) {
+ pb->ops->write_control(pb, octr);
+ return 0; /* Sure that no ECR register exists */
+ }
+ }
+
+ if ((pb->ops->read_econtrol(pb) & 0x3 ) != 0x1)
+ return 0;
+
+ pb->ops->write_econtrol(pb, 0x34);
+ if (pb->ops->read_econtrol(pb) != 0x35)
+ return 0;
+
+ pb->ops->write_econtrol(pb, oecr);
+ pb->ops->write_control(pb, octr);
+
+ return PARPORT_MODE_PCECR;
+}
+
+static int parport_ECP_supported(struct parport *pb)
+{
+ int i, oecr = pb->ops->read_econtrol(pb);
+
+ /* If there is no ECR, we have no hope of supporting ECP. */
+ if (!(pb->modes & PARPORT_MODE_PCECR))
+ return 0;
+
+ /*
+ * Using LGS chipset it uses ECR register, but
+ * it doesn't support ECP or FIFO MODE
+ */
+
+ pb->ops->write_econtrol(pb, 0xc0); /* TEST FIFO */
+ for (i=0; i < 1024 && (pb->ops->read_econtrol(pb) & 0x01); i++)
+ pb->ops->write_fifo(pb, 0xaa);
+
+ pb->ops->write_econtrol(pb, oecr);
+ return (i == 1024) ? 0 : PARPORT_MODE_PCECP;
+}
+
+/* Detect PS/2 support.
+ *
+ * Bit 5 (0x20) sets the PS/2 data direction; setting this high
+ * allows us to read data from the data lines. In theory we would get back
+ * 0xff but any peripheral attached to the port may drag some or all of the
+ * lines down to zero. So if we get back anything that isn't the contents
+ * of the data register we deem PS/2 support to be present.
+ *
+ * Some SPP ports have "half PS/2" ability - you can't turn off the line
+ * drivers, but an external peripheral with sufficiently beefy drivers of
+ * its own can overpower them and assert its own levels onto the bus, from
+ * where they can then be read back as normal. Ports with this property
+ * and the right type of device attached are likely to fail the SPP test,
+ * (as they will appear to have stuck bits) and so the fact that they might
+ * be misdetected here is rather academic.
+ */
+
+static int parport_PS2_supported(struct parport *pb)
+{
+ int ok = 0, octr = pb->ops->read_control(pb);
+
+ pb->ops->write_control(pb, octr | 0x20); /* try to tri-state buffer */
+
+ pb->ops->write_data(pb, 0x55);
+ if (pb->ops->read_data(pb) != 0x55) ok++;
+
+ pb->ops->write_data(pb, 0xaa);
+ if (pb->ops->read_data(pb) != 0xaa) ok++;
+
+ pb->ops->write_control(pb, octr); /* cancel input mode */
+
+ return ok ? PARPORT_MODE_PCPS2 : 0;
+}
+
+static int parport_ECPPS2_supported(struct parport *pb)
+{
+ int mode, oecr = pb->ops->read_econtrol(pb);
+
+ if (!(pb->modes & PARPORT_MODE_PCECR))
+ return 0;
+
+ pb->ops->write_econtrol(pb, 0x20);
+
+ mode = parport_PS2_supported(pb);
+
+ pb->ops->write_econtrol(pb, oecr);
+ return mode ? PARPORT_MODE_PCECPPS2 : 0;
+}
+
+#define printmode(x) \
+{ \
+ if (p->modes & PARPORT_MODE_PC##x) { \
+ printk("%s%s", f ? "," : "", #x); \
+ f++; \
+ } \
+}
+
+int
+init_one_port(struct linux_ebus_device *dev)
+{
+ struct parport tmpport, *p;
+ unsigned long base;
+ unsigned long config;
+ unsigned char tmp;
+ int irq, dma;
+
+ /* Pointer to NS87303 Configuration Registers */
+ config = dev->base_address[1];
+
+ /* Setup temporary access to Device operations */
+ tmpport.base = dev->base_address[0];
+ tmpport.ops = &ax_ops;
+
+ /* Enable ECP mode, set bit 2 of the CTR first */
+ tmpport.ops->write_control(&tmpport, 0x04);
+ tmp = ns87303_readb(config, PCR);
+ tmp |= (PCR_EPP_IEEE | PCR_ECP_ENABLE | PCR_ECP_CLK_ENA);
+ ns87303_writeb(config, PCR, tmp);
+
+ /* LPT CTR bit 5 controls direction of parallel port */
+ tmp = ns87303_readb(config, PTR);
+ tmp |= PTR_LPT_REG_DIR;
+ ns87303_writeb(config, PTR, tmp);
+
+ /* Configure IRQ to Push Pull, Level Low */
+ tmp = ns87303_readb(config, PCR);
+ tmp &= ~(PCR_IRQ_ODRAIN);
+ tmp |= PCR_IRQ_POLAR;
+ ns87303_writeb(config, PCR, tmp);
+
+#ifndef HAVE_SLOW_DEVICES
+ /* Enable Zero Wait State for ECP */
+ tmp = ns87303_readb(config, FCR);
+ tmp |= FCR_ZWS_ENA;
+ ns87303_writeb(config, FCR, tmp);
+#endif
+
+ /*
+ * Now continue initializing the port
+ */
+ base = dev->base_address[0];
+ irq = dev->irqs[0];
+ dma = PARPORT_DMA_AUTO;
+
+ if (!(p = parport_register_port(base, irq, dma, &ax_ops)))
+ return 0;
+
+ /* Safe away pointer to our EBus DMA */
+ p->private_data = (void *)dev->base_address[2];
+
+ p->modes = PARPORT_MODE_PCSPP | parport_PS2_supported(p);
+ if (!check_region(p->base + 0x400, 3)) {
+ p->modes |= parport_ECR_present(p);
+ p->modes |= parport_ECP_supported(p);
+ p->modes |= parport_ECPPS2_supported(p);
+ }
+ p->size = 3;
+
+ if (p->dma == PARPORT_DMA_AUTO)
+ p->dma = (p->modes & PARPORT_MODE_PCECP) ? 0 : PARPORT_DMA_NONE;
+
+ printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
+ if (p->irq != PARPORT_IRQ_NONE)
+ printk(", irq %x", (unsigned int)p->irq);
+ if (p->dma != PARPORT_DMA_NONE)
+ printk(", dma %d", p->dma);
+ printk(" [");
+ {
+ int f = 0;
+ printmode(SPP);
+ printmode(PS2);
+ printmode(ECP);
+ printmode(ECPPS2);
+ }
+ printk("]\n");
+ parport_proc_register(p);
+ p->flags |= PARPORT_FLAG_COMA;
+
+ ax_write_control(p, 0x0c);
+ ax_write_data(p, 0);
+
+ if (parport_probe_hook)
+ (*parport_probe_hook)(p);
+
+ return 1;
+}
+
+int
+parport_ax_init(void)
+{
+ struct linux_ebus *ebus;
+ struct linux_ebus_device *edev;
+ int count = 0;
+
+ for_all_ebusdev(edev, ebus)
+ if (!strcmp(edev->prom_name, "ecpp"))
+ count += init_one_port(edev);
+ return count;
+}
+
+#ifdef MODULE
+
+int
+init_module(void)
+{
+ return (parport_ax_init() ? 0 : 1);
+}
+
+void
+cleanup_module(void)
+{
+ struct parport *p = parport_enumerate(), *tmp;
+ while (p) {
+ tmp = p->next;
+ if (p->modes & PARPORT_MODE_PCSPP) {
+ if (!(p->flags & PARPORT_FLAG_COMA))
+ parport_quiesce(p);
+ parport_proc_unregister(p);
+ parport_unregister_port(p);
+ }
+ p = tmp;
+ }
+}
+#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov