📄 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 *//* 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/malloc.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#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;/* frob_control, but for ECR */static void frob_econtrol (struct parport *pb, unsigned char m, unsigned char v){ unsigned char 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));}#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; int ecr = ECONTROL(p); unsigned char oecr; int mode; DPRINTK("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 (ecr); 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; outb (oecr, ecr); } /* Set the mode. */ oecr &= ~(7 << 5); oecr |= m << 5; outb (oecr, ecr); 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_econtrol (p, 0xe0, ECR_PS2 << 5); /* Now change to config mode and clean up. FIXME */ frob_econtrol (p, 0xe0, ECR_CNF << 5); 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_econtrol (p, 0xe0, ECR_PS2 << 5); 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){ __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 | (dev->irq_func ? 0x10 : 0x0); s->u.pc.ecr = 0x24;}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 = inb (CONTROL (p)); if (priv->ecr) s->u.pc.ecr = inb (ECONTROL (p));}void parport_pc_restore_state(struct parport *p, struct parport_state *s){ const struct parport_pc_private *priv = p->physport->private_data; outb (s->u.pc.ctr, CONTROL (p)); if (priv->ecr) outb (s->u.pc.ecr, ECONTROL (p));}#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; for (; got < length; got++) { *((char*)buf)++ = inb (EPPDATA(port)); if (inb (STATUS(port)) & 0x01) { 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; 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; for (; got < length; got++) { *((char*)buf)++ = inb (EPPADDR (port)); if (inb (STATUS (port)) & 0x01) { clear_epp_timeout (port); break; } } return got;}static size_t parport_pc_epp_write_addr (struct parport *port, const void *buf, size_t length, int flags){ size_t written = 0; for (; written < length; written++) { outb (*((char*)buf)++, EPPADDR (port)); if (inb (STATUS (port)) & 0x01) { clear_epp_timeout (port); break; } } return written;}static size_t parport_pc_ecpepp_read_data (struct parport *port, void *buf, size_t length, int flags){ size_t got; frob_econtrol (port, 0xe0, ECR_EPP << 5); parport_pc_data_reverse (port); parport_pc_write_control (port, 0x4); got = parport_pc_epp_read_data (port, buf, length, flags); frob_econtrol (port, 0xe0, ECR_PS2 << 5); return got;}static size_t parport_pc_ecpepp_write_data (struct parport *port, const void *buf, size_t length, int flags){ size_t written; frob_econtrol (port, 0xe0, ECR_EPP << 5); parport_pc_write_control (port, 0x4); parport_pc_data_forward (port); written = parport_pc_epp_write_data (port, buf, length, flags); frob_econtrol (port, 0xe0, ECR_PS2 << 5); return written;}static size_t parport_pc_ecpepp_read_addr (struct parport *port, void *buf, size_t length, int flags){ size_t got; frob_econtrol (port, 0xe0, ECR_EPP << 5); parport_pc_data_reverse (port); parport_pc_write_control (port, 0x4); got = parport_pc_epp_read_addr (port, buf, length, flags); frob_econtrol (port, 0xe0, ECR_PS2 << 5); return got;}static size_t parport_pc_ecpepp_write_addr (struct parport *port, const void *buf, size_t length, int flags){ size_t written; frob_econtrol (port, 0xe0, ECR_EPP << 5); parport_pc_write_control (port, 0x4); parport_pc_data_forward (port); written = parport_pc_epp_write_addr (port, buf, length, flags); frob_econtrol (port, 0xe0, ECR_PS2 << 5); return written;}#endif /* IEEE 1284 support */#ifdef CONFIG_PARPORT_PC_FIFOstatic size_t parport_pc_fifo_write_block_pio (struct parport *port, const void *buf, size_t length){ int ret = 0; const unsigned char *bufp = buf; size_t left = length; long expire = jiffies + port->physport->cad->timeout;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -