patch-2.3.6 linux/drivers/misc/parport_pc.c
Next file: linux/drivers/misc/parport_procfs.c
Previous file: linux/drivers/misc/parport_mfc3.c
Back to the patch index
Back to the overall index
- Lines: 457
- Date:
Mon Jun 7 13:25:18 1999
- Orig file:
v2.3.5/linux/drivers/misc/parport_pc.c
- Orig date:
Mon May 31 22:28:05 1999
diff -u --recursive --new-file v2.3.5/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
@@ -9,6 +9,7 @@
* based on work by Grant Guenther <grant@torque.net> and Phil Blundell.
*
* Cleaned up include files - Russell King <linux@arm.uk.linux.org>
+ * Better EPP probing - Carlos Henrique Bauer <chbauer@acm.org>
*/
/* This driver should work with any hardware that is broadly compatible
@@ -47,6 +48,7 @@
#include <linux/pci.h>
#include <asm/io.h>
+#include <asm/dma.h>
#include <linux/parport.h>
#include <linux/parport_pc.h>
@@ -57,6 +59,26 @@
static int user_specified __initdata = 0;
+/*
+ * Clear TIMEOUT BIT in EPP MODE
+ */
+int parport_pc_epp_clear_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);
+ parport_pc_write_status(pb, r | 0x01); /* Some reset by writing 1 */
+ parport_pc_write_status(pb, r & 0xfe); /* Others by writing 0 */
+ r = parport_pc_read_status(pb);
+
+ return !(r & 0x01);
+}
+
static void parport_pc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
parport_generic_irq(irq, (struct parport *) dev_id, regs);
@@ -179,28 +201,6 @@
parport_pc_frob_control(p, 0x10, 0x10);
}
-void parport_pc_release_resources(struct parport *p)
-{
- if (p->irq != PARPORT_IRQ_NONE)
- free_irq(p->irq, p);
- release_region(p->base, p->size);
- if (p->modes & PARPORT_MODE_PCECR)
- release_region(p->base_hi, 3);
-}
-
-int parport_pc_claim_resources(struct parport *p)
-{
- int err;
- if (p->irq != PARPORT_IRQ_NONE)
- if ((err = request_irq(p->irq, parport_pc_interrupt,
- 0, p->name, p)) != 0)
- return err;
- request_region(p->base, p->size, p->name);
- if (p->modes & PARPORT_MODE_PCECR)
- request_region(p->base_hi, 3, p->name);
- return 0;
-}
-
void parport_pc_init_state(struct parport_state *s)
{
s->u.pc.ctr = 0xc;
@@ -298,9 +298,6 @@
parport_pc_change_mode,
- parport_pc_release_resources,
- parport_pc_claim_resources,
-
parport_pc_write_epp,
parport_pc_read_epp,
parport_pc_write_epp_addr,
@@ -328,26 +325,6 @@
/* --- Mode detection ------------------------------------- */
-/*
- * Clear TIMEOUT BIT in EPP MODE
- */
-int parport_pc_epp_clear_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);
- parport_pc_write_status(pb, r | 0x01); /* Some reset by writing 1 */
- parport_pc_write_status(pb, r & 0xfe); /* Others by writing 0 */
- r = parport_pc_read_status(pb);
-
- return !(r & 0x01);
-}
-
/*
* Checks for port existence, all ports support SPP MODE
@@ -373,12 +350,12 @@
* copy. Some ports _do_ allow reads, so bypass the software
* copy here. In addition, some bits aren't writable. */
r = inb (CONTROL (pb));
- if ((r & 0x3f) == w) {
+ if ((r & 0xf) == w) {
w = 0xe;
parport_pc_write_control (pb, w);
r = inb (CONTROL(pb));
parport_pc_write_control (pb, 0xc);
- if ((r & 0x3f) == w)
+ if ((r & 0xf) == w)
return PARPORT_MODE_PCSPP;
}
@@ -501,6 +478,19 @@
if (!parport_pc_epp_clear_timeout(pb))
return 0; /* No way to clear timeout */
+ /*
+ * Theory:
+ * Bit 0 of STR is the EPP timeout bit, this bit is 0
+ * when EPP is possible and is set high when an EPP timeout
+ * occurs (EPP uses the HALT line to stop the CPU while it does
+ * the byte transfer, an EPP timeout occurs if the attached
+ * device fails to respond after 10 micro seconds).
+ *
+ * This bit is cleared by either reading it (National Semi)
+ * or writing a 1 to the bit (SMC, UMC, WinBond), others ???
+ * This bit is always high in non EPP modes.
+ */
+
parport_pc_write_control(pb, parport_pc_read_control(pb) | 0x20);
parport_pc_write_control(pb, parport_pc_read_control(pb) | 0x10);
parport_pc_epp_clear_timeout(pb);
@@ -513,6 +503,45 @@
return PARPORT_MODE_PCEPP;
}
+ /*
+ * Theory:
+ * Write two values to the EPP address register and
+ * read them back. When the transfer times out, the state of
+ * the EPP register is undefined in some cases (EPP 1.9?) but
+ * in others (EPP 1.7, ECPEPP?) it is possible to read back
+ * its value.
+ */
+ parport_pc_epp_clear_timeout(pb);
+ udelay(30); /* Wait for possible EPP timeout */
+
+ /* Previous test left outputs disabled. */
+ outb (0x55, EPPADDR (pb));
+
+ parport_pc_epp_clear_timeout(pb);
+ udelay(30); /* Wait for possible EPP timeout */
+
+ /* We must enable the outputs to be able to read the address
+ register. */
+ parport_pc_frob_control (pb, 0x20, 0x00);
+
+ if (inb (EPPADDR (pb)) == 0x55) {
+
+ /* wash ... */
+ parport_pc_frob_control (pb, 0x20, 0x20);
+ outb (0xaa, EPPADDR (pb));
+
+ parport_pc_epp_clear_timeout(pb);
+ udelay(30); /* Wait for possible EPP timeout */
+
+ /* ... and repeat */
+ parport_pc_frob_control (pb, 0x20, 0x00);
+
+ if (inb (EPPADDR (pb)) == 0xaa) {
+ parport_pc_epp_clear_timeout (pb);
+ return PARPORT_MODE_PCEPP;
+ }
+ }
+
return 0;
}
@@ -638,14 +667,13 @@
*/
static int __init irq_probe_EPP(struct parport *pb)
{
+#ifndef ADVANCED_DETECT
+ return PARPORT_IRQ_NONE;
+#else
int irqs;
unsigned char octr = parport_pc_read_control(pb);
unsigned char oecr;
-#ifndef ADVANCED_DETECT
- return PARPORT_IRQ_NONE;
-#endif
-
if (pb->modes & PARPORT_MODE_PCECR)
oecr = parport_pc_read_econtrol(pb);
@@ -675,18 +703,19 @@
pb->irq = PARPORT_IRQ_NONE;
return pb->irq;
+#endif /* Advanced detection. */
}
static int __init irq_probe_SPP(struct parport *pb)
{
+#ifndef ADVANCED_DETECT
+ /* Don't even try to do this. */
+ return PARPORT_IRQ_NONE;
+#else
int irqs;
unsigned char octr = parport_pc_read_control(pb);
unsigned char oecr;
-#ifndef ADVANCED_DETECT
- return PARPORT_IRQ_NONE;
-#endif
-
if (pb->modes & PARPORT_MODE_PCECR)
oecr = parport_pc_read_econtrol(pb);
probe_irq_off(probe_irq_on()); /* Clear any interrupts */
@@ -716,6 +745,7 @@
parport_pc_write_econtrol(pb, oecr);
parport_pc_write_control(pb, octr);
return pb->irq;
+#endif /* Advanced detection. */
}
/* We will attempt to share interrupt requests since other devices
@@ -760,44 +790,59 @@
unsigned long int base_hi,
int irq, int dma)
{
- struct parport *p;
+ struct parport_pc_private *priv;
+ struct parport tmp;
+ struct parport *p = &tmp;
int probedirq = PARPORT_IRQ_NONE;
if (check_region(base, 3)) return 0;
- if (!(p = parport_register_port(base, irq, dma, &parport_pc_ops)))
- return 0;
- p->private_data = kmalloc (sizeof (struct parport_pc_private),
- GFP_KERNEL);
- if (!p->private_data) {
- /* Not enough memory. */
+ priv = kmalloc (sizeof (struct parport_pc_private), GFP_KERNEL);
+ if (!priv) {
printk (KERN_DEBUG "parport (0x%lx): no memory!\n", base);
- parport_unregister_port (p);
return 0;
}
- ((struct parport_pc_private *) (p->private_data))->ctr = 0xc;
+ priv->ctr = 0xc;
+ p->base = base;
p->base_hi = base_hi;
+ p->irq = irq;
+ p->dma = dma;
+ p->modes = PARPORT_MODE_PCSPP;
+ p->ops = &parport_pc_ops;
+ p->private_data = priv;
+ if (base_hi && !check_region (base_hi, 3)) {
+ p->modes |= parport_ECR_present (p);
+ p->modes |= parport_ECP_supported (p);
+ p->modes |= parport_ECPPS2_supported (p);
+ }
if (p->base != 0x3bc) {
- if (base_hi && !check_region(base_hi,3)) {
- p->modes |= parport_ECR_present(p);
- p->modes |= parport_ECP_supported(p);
- p->modes |= parport_ECPPS2_supported(p);
- }
if (!check_region(base+0x3, 5)) {
- p->modes |= parport_EPP_supported(p);
- p->modes |= parport_ECPEPP_supported(p);
+ p->modes |= parport_EPP_supported (p);
+ p->modes |= parport_ECPEPP_supported (p);
}
}
if (!parport_SPP_supported(p)) {
/* No port. */
- kfree (p->private_data);
- parport_unregister_port (p);
+ kfree (priv);
+ return 0;
+ }
+
+ p->modes |= parport_PS2_supported(p);
+
+ if (!(p = parport_register_port (base, PARPORT_IRQ_NONE,
+ PARPORT_DMA_NONE, &parport_pc_ops))) {
+ kfree (priv);
return 0;
}
- p->modes |= PARPORT_MODE_PCSPP | parport_PS2_supported(p);
- p->size = (p->modes & (PARPORT_MODE_PCEPP
- | PARPORT_MODE_PCECPEPP))?8:3;
+
+ p->base_hi = base_hi;
+ p->modes = tmp.modes;
+ p->size = (p->modes & PARPORT_MODE_PCEPP) ? 8 : 3;
+ p->private_data = priv;
+
printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
if (p->base_hi && (p->modes & PARPORT_MODE_PCECR))
printk (" (0x%lx)", p->base_hi);
+ p->irq = irq;
+ p->dma = dma;
if (p->irq == PARPORT_IRQ_AUTO) {
p->irq = PARPORT_IRQ_NONE;
parport_irq_probe(p);
@@ -831,7 +876,30 @@
printk("%s: detected irq %d; use procfs to enable interrupt-driven operation.\n", p->name, probedirq);
#endif
parport_proc_register(p);
- p->flags |= PARPORT_FLAG_COMA;
+
+ request_region (p->base, p->size, p->name);
+ if (p->modes & PARPORT_MODE_PCECR)
+ request_region (p->base_hi, 3, p->name);
+
+ if (p->irq != PARPORT_IRQ_NONE) {
+ if (request_irq (p->irq, parport_pc_interrupt,
+ 0, p->name, p)) {
+ printk (KERN_WARNING "%s: irq %d in use, "
+ "resorting to polled operation\n",
+ p->name, p->irq);
+ p->irq = PARPORT_IRQ_NONE;
+ p->dma = PARPORT_DMA_NONE;
+ }
+
+ if (p->dma != PARPORT_DMA_NONE) {
+ if (request_dma (p->dma, p->name)) {
+ printk (KERN_WARNING "%s: dma %d in use, "
+ "resorting to PIO operation\n",
+ p->name, p->dma);
+ p->dma = PARPORT_DMA_NONE;
+ }
+ }
+ }
/* Done probing. Now put the port into a sensible start-up state. */
if (p->modes & PARPORT_MODE_PCECR)
@@ -859,6 +927,34 @@
/* Look for PCI parallel port cards. */
static int __init parport_pc_init_pci (int irq, int dma)
{
+/* These need to go in pci.h: */
+#ifndef PCI_VENDOR_ID_SIIG
+#define PCI_VENDOR_ID_SIIG 0x131f
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_550 0x1010
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_650 0x1011
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_850 0x1012
+#define PCI_DEVICE_ID_SIIG_1P_10x 0x1020
+#define PCI_DEVICE_ID_SIIG_2P_10x 0x1021
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_550 0x1034
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_650 0x1035
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_850 0x1036
+#define PCI_DEVICE_ID_SIIG_1P_20x 0x2020
+#define PCI_DEVICE_ID_SIIG_2P_20x 0x2021
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_550 0x2040
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_650 0x2041
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_850 0x2042
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_550 0x2010
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_650 0x2011
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_850 0x2012
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_550 0x2060
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_650 0x2061
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_850 0x2062
+#define PCI_VENDOR_ID_LAVA 0x1407
+#define PCI_DEVICE_ID_LAVA_PARALLEL 0x8000
+#define PCI_DEVICE_ID_LAVA_DUAL_PAR_A 0x8001 /* The Lava Dual Parallel is */
+#define PCI_DEVICE_ID_LAVA_DUAL_PAR_B 0x8002 /* two PCI devices on a card */
+#endif /* IDs not defined */
+
int count = 0;
#ifdef CONFIG_PCI
int i;
@@ -871,6 +967,50 @@
unsigned int hi; /* -ve if not there */
} addr[4];
} cards[] = {
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, 1,
+ { { 3, 4 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, 1,
+ { { 3, 4 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, 1,
+ { { 3, 4 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_10x, 1,
+ { { 2, 3 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_10x, 2,
+ { { 2, 3 }, { 4, 5 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, 1,
+ { { 4, 5 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, 1,
+ { { 4, 5 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, 1,
+ { { 4, 5 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_20x, 1,
+ { { 0, 1 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_20x, 2,
+ { { 0, 1 }, { 2, 3 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, 2,
+ { { 1, 2 }, { 3, 4 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, 2,
+ { { 1, 2 }, { 3, 4 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, 2,
+ { { 1, 2 }, { 3, 4 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, 1,
+ { { 1, 2 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, 1,
+ { { 1, 2 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, 1,
+ { { 1, 2 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, 1,
+ { { 2, 3 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, 1,
+ { { 2, 3 }, } },
+ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, 1,
+ { { 2, 3 }, } },
+ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PARALLEL, 1,
+ { { 0, -1 }, } },
+ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_A, 1,
+ { { 0, -1 }, } },
+ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B, 1,
+ { { 0, -1 }, } },
{ 0, }
};
@@ -980,9 +1120,14 @@
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);
+ if (p->modes & PARPORT_MODE_PCSPP) {
+ if (p->dma != PARPORT_DMA_NONE)
+ free_dma (p->dma);
+ if (p->irq != PARPORT_IRQ_NONE)
+ free_irq (p->irq, p);
+ release_region (p->base, p->size);
+ if (p->modes & PARPORT_MODE_PCECP)
+ release_region (p->base_hi, 3);
parport_proc_unregister(p);
kfree (p->private_data);
parport_unregister_port(p);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)