📄 parport_pc.c
字号:
/* Low-level parallel-port routines for 8255-based PC-style hardware. * * Authors: Phil Blundell <Philip.Blundell@pobox.com> * Tim Waugh <tim@cyberelk.demon.co.uk> * Jose Renau <renau@acm.org> * David Campbell <campbell@torque.net> * Andrea Arcangeli * * based on work by Grant Guenther <grant@torque.net> and Phil Blundell. * * Cleaned up include files - Russell King <linux@arm.uk.linux.org> * DMA support - Bert De Jonghe <bert@sophis.be> * Many ECP bugs fixed. Fred Barnes & Jamie Lokier, 1999 * More PCI support now conditional on CONFIG_PCI, 03/2001, Paul G. * Various hacks, Fred Barnes, 04/2001 *//* This driver should work with any hardware that is broadly compatible * with that in the IBM PC. This applies to the majority of integrated * I/O chipsets that are commonly available. The expected register * layout is: * * base+0 data * base+1 status * base+2 control * * In addition, there are some optional registers: * * base+3 EPP address * base+4 EPP data * base+0x400 ECP config A * base+0x401 ECP config B * base+0x402 ECP control * * All registers are 8 bits wide and read/write. If your hardware differs * only in register addresses (eg because your registers are on 32-bit * word boundaries) then you can alter the constants in parport_pc.h to * accomodate this. * * Note that the ECP registers may not start at offset 0x400 for PCI cards, * but rather will start at port->base_hi. */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/pci.h>#include <linux/sysctl.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/uaccess.h>#include <linux/parport.h>#include <linux/parport_pc.h>#include <asm/parport.h>#define PARPORT_PC_MAX_PORTS PARPORT_MAX/* ECR modes */#define ECR_SPP 00#define ECR_PS2 01#define ECR_PPF 02#define ECR_ECP 03#define ECR_EPP 04#define ECR_VND 05#define ECR_TST 06#define ECR_CNF 07#define ECR_MODE_MASK 0xe0#define ECR_WRITE(p,v) frob_econtrol((p),0xff,(v))#undef DEBUG#ifdef DEBUG#define DPRINTK printk#else#define DPRINTK(stuff...)#endif#define NR_SUPERIOS 3static struct superio_struct { /* For Super-IO chips autodetection */ int io; int irq; int dma;} superios[NR_SUPERIOS] __devinitdata = { {0,},};static int user_specified __devinitdata = 0;#if defined(CONFIG_PARPORT_PC_FIFO) || defined(CONFIG_PARPORT_PC_SUPERIO)static int verbose_probing;#endifstatic int registered_parport;/* frob_control, but for ECR */static void frob_econtrol (struct parport *pb, unsigned char m, unsigned char v){ unsigned char ectr = 0; if (m != 0xff) ectr = inb (ECONTROL (pb)); DPRINTK (KERN_DEBUG "frob_econtrol(%02x,%02x): %02x -> %02x\n", m, v, ectr, (ectr & ~m) ^ v); outb ((ectr & ~m) ^ v, ECONTROL (pb));}static void __inline__ frob_set_mode (struct parport *p, int mode){ frob_econtrol (p, ECR_MODE_MASK, mode << 5);}#ifdef CONFIG_PARPORT_PC_FIFO/* Safely change the mode bits in the ECR Returns: 0 : Success -EBUSY: Could not drain FIFO in some finite amount of time, mode not changed! */static int change_mode(struct parport *p, int m){ const struct parport_pc_private *priv = p->physport->private_data; unsigned char oecr; int mode; DPRINTK(KERN_INFO "parport change_mode ECP-ISA to mode 0x%02x\n",m); if (!priv->ecr) { printk (KERN_DEBUG "change_mode: but there's no ECR!\n"); return 0; } /* Bits <7:5> contain the mode. */ oecr = inb (ECONTROL (p)); mode = (oecr >> 5) & 0x7; if (mode == m) return 0; if (mode >= 2 && !(priv->ctr & 0x20)) { /* This mode resets the FIFO, so we may * have to wait for it to drain first. */ long expire = jiffies + p->physport->cad->timeout; int counter; switch (mode) { case ECR_PPF: /* Parallel Port FIFO mode */ case ECR_ECP: /* ECP Parallel Port mode */ /* Busy wait for 200us */ for (counter = 0; counter < 40; counter++) { if (inb (ECONTROL (p)) & 0x01) break; if (signal_pending (current)) break; udelay (5); } /* Poll slowly. */ while (!(inb (ECONTROL (p)) & 0x01)) { if (time_after_eq (jiffies, expire)) /* The FIFO is stuck. */ return -EBUSY; __set_current_state (TASK_INTERRUPTIBLE); schedule_timeout ((HZ + 99) / 100); if (signal_pending (current)) break; } } } if (mode >= 2 && m >= 2) { /* We have to go through mode 001 */ oecr &= ~(7 << 5); oecr |= ECR_PS2 << 5; ECR_WRITE (p, oecr); } /* Set the mode. */ oecr &= ~(7 << 5); oecr |= m << 5; ECR_WRITE (p, oecr); return 0;}#ifdef CONFIG_PARPORT_1284/* Find FIFO lossage; FIFO is reset */static int get_fifo_residue (struct parport *p){ int residue; int cnfga; const struct parport_pc_private *priv = p->physport->private_data; /* Adjust for the contents of the FIFO. */ for (residue = priv->fifo_depth; ; residue--) { if (inb (ECONTROL (p)) & 0x2) /* Full up. */ break; outb (0, FIFO (p)); } printk (KERN_DEBUG "%s: %d PWords were left in FIFO\n", p->name, residue); /* Reset the FIFO. */ frob_set_mode (p, ECR_PS2); /* Now change to config mode and clean up. FIXME */ frob_set_mode (p, ECR_CNF); cnfga = inb (CONFIGA (p)); printk (KERN_DEBUG "%s: cnfgA contains 0x%02x\n", p->name, cnfga); if (!(cnfga & (1<<2))) { printk (KERN_DEBUG "%s: Accounting for extra byte\n", p->name); residue++; } /* Don't care about partial PWords until support is added for * PWord != 1 byte. */ /* Back to PS2 mode. */ frob_set_mode (p, ECR_PS2); DPRINTK (KERN_DEBUG "*** get_fifo_residue: done residue collecting (ecr = 0x%2.2x)\n", inb (ECONTROL (p))); return residue;}#endif /* IEEE 1284 support */#endif /* FIFO support *//* * Clear TIMEOUT BIT in EPP MODE * * This is also used in SPP detection. */static int clear_epp_timeout(struct parport *pb){ unsigned char r; if (!(parport_pc_read_status(pb) & 0x01)) return 1; /* To clear timeout some chips require double read */ parport_pc_read_status(pb); r = parport_pc_read_status(pb); outb (r | 0x01, STATUS (pb)); /* Some reset by writing 1 */ outb (r & 0xfe, STATUS (pb)); /* Others by writing 0 */ r = parport_pc_read_status(pb); return !(r & 0x01);}/* * Access functions. * * Most of these aren't static because they may be used by the * parport_xxx_yyy macros. extern __inline__ versions of several * of these are in parport_pc.h. */static void parport_pc_interrupt(int irq, void *dev_id, struct pt_regs *regs){ parport_generic_irq(irq, (struct parport *) dev_id, regs);}void parport_pc_write_data(struct parport *p, unsigned char d){ outb (d, DATA (p));}unsigned char parport_pc_read_data(struct parport *p){ return inb (DATA (p));}void parport_pc_write_control(struct parport *p, unsigned char d){ const unsigned char wm = (PARPORT_CONTROL_STROBE | PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_INIT | PARPORT_CONTROL_SELECT); /* Take this out when drivers have adapted to the newer interface. */ if (d & 0x20) { printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n", p->name, p->cad->name); parport_pc_data_reverse (p); } __parport_pc_frob_control (p, wm, d & wm);}unsigned char parport_pc_read_control(struct parport *p){ const unsigned char wm = (PARPORT_CONTROL_STROBE | PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_INIT | PARPORT_CONTROL_SELECT); const struct parport_pc_private *priv = p->physport->private_data; return priv->ctr & wm; /* Use soft copy */}unsigned char parport_pc_frob_control (struct parport *p, unsigned char mask, unsigned char val){ const unsigned char wm = (PARPORT_CONTROL_STROBE | PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_INIT | PARPORT_CONTROL_SELECT); /* Take this out when drivers have adapted to the newer interface. */ if (mask & 0x20) { printk (KERN_DEBUG "%s (%s): use data_%s for this!\n", p->name, p->cad->name, (val & 0x20) ? "reverse" : "forward"); if (val & 0x20) parport_pc_data_reverse (p); else parport_pc_data_forward (p); } /* Restrict mask and val to control lines. */ mask &= wm; val &= wm; return __parport_pc_frob_control (p, mask, val);}unsigned char parport_pc_read_status(struct parport *p){ return inb (STATUS (p));}void parport_pc_disable_irq(struct parport *p){ __parport_pc_frob_control (p, 0x10, 0);}void parport_pc_enable_irq(struct parport *p){ if (p->irq != PARPORT_IRQ_NONE) __parport_pc_frob_control (p, 0x10, 0x10);}void parport_pc_data_forward (struct parport *p){ __parport_pc_frob_control (p, 0x20, 0);}void parport_pc_data_reverse (struct parport *p){ __parport_pc_frob_control (p, 0x20, 0x20);}void parport_pc_init_state(struct pardevice *dev, struct parport_state *s){ s->u.pc.ctr = 0xc; if (dev->irq_func && dev->port->irq != PARPORT_IRQ_NONE) /* Set ackIntEn */ s->u.pc.ctr |= 0x10; s->u.pc.ecr = 0x34; /* NetMos chip can cause problems 0x24; * D.Gruszka VScom */}void parport_pc_save_state(struct parport *p, struct parport_state *s){ const struct parport_pc_private *priv = p->physport->private_data; s->u.pc.ctr = priv->ctr; if (priv->ecr) s->u.pc.ecr = inb (ECONTROL (p));}void parport_pc_restore_state(struct parport *p, struct parport_state *s){ struct parport_pc_private *priv = p->physport->private_data; register unsigned char c = s->u.pc.ctr & priv->ctr_writable; outb (c, CONTROL (p)); priv->ctr = c; if (priv->ecr) ECR_WRITE (p, s->u.pc.ecr);}#ifdef CONFIG_PARPORT_1284static size_t parport_pc_epp_read_data (struct parport *port, void *buf, size_t length, int flags){ size_t got = 0; if (flags & PARPORT_W91284PIC) { unsigned char status; size_t left = length; /* use knowledge about data lines..: * nFault is 0 if there is at least 1 byte in the Warp's FIFO * pError is 1 if there are 16 bytes in the Warp's FIFO */ status = inb (STATUS (port)); while (!(status & 0x08) && (got < length)) { if ((left >= 16) && (status & 0x20) && !(status & 0x08)) { /* can grab 16 bytes from warp fifo */ if (!((long)buf & 0x03)) { insl (EPPDATA (port), buf, 4); } else { insb (EPPDATA (port), buf, 16); } buf += 16; got += 16; left -= 16; } else { /* grab single byte from the warp fifo */ *((char *)buf)++ = inb (EPPDATA (port)); got++; left--; } status = inb (STATUS (port)); if (status & 0x01) { /* EPP timeout should never occur... */ printk (KERN_DEBUG "%s: EPP timeout occured while talking to " "w91284pic (should not have done)\n", port->name); clear_epp_timeout (port); } } return got; } if ((flags & PARPORT_EPP_FAST) && (length > 1)) { if (!(((long)buf | length) & 0x03)) { insl (EPPDATA (port), buf, (length >> 2)); } else { insb (EPPDATA (port), buf, length); } if (inb (STATUS (port)) & 0x01) { clear_epp_timeout (port); return -EIO; } return length; } for (; got < length; got++) { *((char*)buf)++ = inb (EPPDATA(port)); if (inb (STATUS (port)) & 0x01) { /* EPP timeout */ clear_epp_timeout (port); break; } } return got;}static size_t parport_pc_epp_write_data (struct parport *port, const void *buf, size_t length, int flags){ size_t written = 0; if ((flags & PARPORT_EPP_FAST) && (length > 1)) { if (!(((long)buf | length) & 0x03)) { outsl (EPPDATA (port), buf, (length >> 2)); } else { outsb (EPPDATA (port), buf, length); } if (inb (STATUS (port)) & 0x01) { clear_epp_timeout (port); return -EIO; } return length; } for (; written < length; written++) { outb (*((char*)buf)++, EPPDATA(port)); if (inb (STATUS(port)) & 0x01) { clear_epp_timeout (port); break; } } return written;}static size_t parport_pc_epp_read_addr (struct parport *port, void *buf, size_t length, int flags){ size_t got = 0; if ((flags & PARPORT_EPP_FAST) && (length > 1)) { insb (EPPADDR (port), buf, length); if (inb (STATUS (port)) & 0x01) { clear_epp_timeout (port); return -EIO; } return length; } for (; got < length; got++) { *((char*)buf)++ = inb (EPPADDR (port)); if (inb (STATUS (port)) & 0x01) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -