2023-01-08 22:00:40

by Sudip Mukherjee

[permalink] [raw]
Subject: [PATCH 1/7] parport_pc: Remove stale `parport_pc_ecp_read_block_pio' reference

From: "Maciej W. Rozycki" <[email protected]>

Complement commit 991214386dee ("parport: remove unused dead code from
lowlevel drivers") and remove a stale piece of commented-out code that
refers to a function removed with said commit.

Signed-off-by: Maciej W. Rozycki <[email protected]>
Signed-off-by: Sudip Mukherjee <[email protected]>
---
drivers/parport/parport_pc.c | 2 --
1 file changed, 2 deletions(-)

diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index 5784dc20fb38..de7afbea96cd 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -2119,8 +2119,6 @@ struct parport *parport_pc_probe_port(unsigned long int base,
p->ops->compat_write_data = parport_pc_compat_write_block_pio;
#ifdef CONFIG_PARPORT_1284
p->ops->ecp_write_data = parport_pc_ecp_write_block_pio;
- /* currently broken, but working on it.. (FB) */
- /* p->ops->ecp_read_data = parport_pc_ecp_read_block_pio; */
#endif /* IEEE 1284 support */
if (p->dma != PARPORT_DMA_NONE) {
pr_cont(", dma %d", p->dma);
--
2.30.2


2023-01-08 22:04:01

by Sudip Mukherjee

[permalink] [raw]
Subject: [PATCH 6/7] parport_pc: Set up mode and ECR masks for Oxford Semiconductor devices

From: "Maciej W. Rozycki" <[email protected]>

No Oxford Semiconductor PCI or PCIe parallel port device supports the
Parallel Port FIFO mode. All support the PS/2 Parallel Port mode and
the Enhanced Parallel Port mode via the ECR register. The original 5V
PCI OX16PCI954 device does not support the Extended Capabilities Port
mode, the Test mode or the Configuration mode, but all the other OxSemi
devices do, including in particular the 3.3V PCI OXmPCI954 device and
the universal voltage PCI OXuPCI954 device. All the unsupported modes
are marked reserved in the relevant datasheets.

Accordingly enable the `base_hi' BAR for the 954 devices to enable PS2
and EPP mode support via the ECR register, however mask the COMPAT mode
and, until we have a way to determine what chip variant it is that we
poke at, also the ECP mode, and mask the COMPAT mode only for all the
remaining OxSemi devices, fixing errors like:

parport0: FIFO is stuck
FIFO write timed out

and a non-functional port when the Parallel Port FIFO mode is selected.

Complementing the fix apply an ECR mask for all these devices, which are
documented to only permit writing to the mode field of the ECR register
with a bit pattern of 00001 required to be written to bits 4:0 on mode
field writes. No nFault or service interrupts are implemented, which
will therefore never have to be enabled, though bit 2 does report the
FIFO threshold status to be polled for in the ECP mode where supported.

We have a documented case of writing 1 to bit 2 causing a lock-up with
at least one OX12PCI840 device (from old drivers/parport/ChangeLog):

2001-10-10 Tim Waugh <[email protected]>

* parport_pc.c: Support for OX12PCI840 PCI card (reported by
[email protected]). Lock-ups diagnosed by Ronnie Arosa (and now we
just don't trust its ECR).

which commit adbd321a17cc ("parport_pc: add base_hi BAR for oxsemi_840")
must have broken and by applying an ECR mask here we prevent the lock-up
from triggering. This could have been the reason for requiring 00001 to
be written to bits 4:0 of ECR.

Update the inline comment accordingly; it has come from Linux 2.4.12
back in 2001 and predates the introduction of OXmPCI954 and OXuPCI954
devices that do support ECP.

References:

[1] "OX16PCI954 Integrated Quad UART and PCI interface", Oxford
Semiconductor Ltd., Data Sheet Revision 1.3, Feb. 1999, Chapter 9
"Bidirectional Parallel Port", pp. 53-55

[2] "OX16PCI952 Data Sheet, Integrated High Performance Dual UARTs,
Parallel Port and 5.0v PCI interface", Oxford Semiconductor Ltd.,
DS_B008A_00, Datasheet rev 1.1, June 2001, Chapter 8 "Bi-directional
Parallel Port", pp. 52-56

[3] "OXmPCI954 DATA SHEET Integrated High Performance Quad UARTs, 8-bit
Local Bus/Parallel Port. 3.3v PCI/miniPCI interface.", Oxford
Semiconductor Ltd., DS-0019, June 2005, Chapter 10 "Bidirectional
Parallel Port", pp. 86-90

[4] "OXmPCI952 Data Sheet, Integrated High Performance Dual UARTs, 8-bit
Local Bus/Parallel Port. 3.3v PCI/miniPCI interface.", Oxford
Semiconductor Ltd., DS-0020, June 2005, Chapter 8 "Bidirectional
Parallel Port", pp. 73-77

[5] "OX12PCI840 Integrated Parallel Port and PCI interface", Oxford
Semiconductor Ltd., DS-0021, Jun 2005, Chapter 5 "Bi-directional
Parallel Port", pp. 18-21

[6] "OXPCIe952 PCI Express Bridge to Dual Serial & Parallel Port",
Oxford Semiconductor, Inc., DS-0046, Mar 06 08, Chapter "Parallel
Port Function", pp. 59-62

[7] "OXPCIe840 PCI Express Bridge to Parallel Port", Oxford
Semiconductor, Inc., DS-0049, Mar 06 08, Chapter "Parallel Port
Function", pp. 15-18

[8] "OXuPCI954 Data Sheet, Integrated High Performance Quad UARTs, 8-bit
Local Bus/Parallel Port, 3.3 V and 5 V (Universal Voltage) PCI
Interface.", Oxford Semiconductor, Inc., DS-0058, 26 Jan 2009,
Chapter 8 "Bidirectional Parallel Port", pp. 62-65

[9] "OXuPCI952 Data Sheet, Integrated High Performance Dual UARTs, 8-bit
Local Bus/Parallel Port, 3.3 V and 5.0 V Universal Voltage PCI
Interface.", Oxford Semiconductor, Inc., DS-0059, Sep 2007, Chapter
8 "Bidirectional Parallel Port", pp. 61-64

Signed-off-by: Maciej W. Rozycki <[email protected]>
Signed-off-by: Sudip Mukherjee <[email protected]>
---
drivers/parport/parport_pc.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index d7e64f6dfe90..9b4fe9a2b549 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -2696,12 +2696,19 @@ static struct parport_pc_pci {
/* titan_010l */ { 1, { { 3, -1 }, } },
/* avlab_1p */ { 1, { { 0, 1}, } },
/* avlab_2p */ { 2, { { 0, 1}, { 2, 3 },} },
- /* The Oxford Semi cards are unusual: 954 doesn't support ECP,
- * and 840 locks up if you write 1 to bit 2! */
- /* oxsemi_952 */ { 1, { { 0, 1 }, } },
- /* oxsemi_954 */ { 1, { { 0, -1 }, } },
- /* oxsemi_840 */ { 1, { { 0, 1 }, } },
- /* oxsemi_pcie_pport */ { 1, { { 0, 1 }, } },
+ /* The Oxford Semi cards are unusual: older variants of 954 don't
+ * support ECP, and 840 locks up if you write 1 to bit 2! None
+ * implement nFault or service interrupts and all require 00001
+ * bit pattern to be used for bits 4:0 with ECR writes. */
+ /* oxsemi_952 */ { 1, { { 0, 1 }, },
+ PARPORT_MODE_COMPAT, ECR_MODE_MASK },
+ /* oxsemi_954 */ { 1, { { 0, 1 }, },
+ PARPORT_MODE_ECP |
+ PARPORT_MODE_COMPAT, ECR_MODE_MASK },
+ /* oxsemi_840 */ { 1, { { 0, 1 }, },
+ PARPORT_MODE_COMPAT, ECR_MODE_MASK },
+ /* oxsemi_pcie_pport */ { 1, { { 0, 1 }, },
+ PARPORT_MODE_COMPAT, ECR_MODE_MASK },
/* aks_0100 */ { 1, { { 0, -1 }, } },
/* mobility_pp */ { 1, { { 0, 1 }, } },
/* netmos_9900 */ { 1, { { 0, -1 }, } },
--
2.30.2

2023-01-08 22:04:16

by Sudip Mukherjee

[permalink] [raw]
Subject: [PATCH 7/7] parport_pc: Limit the number of PCI BAR pairs to 2

From: "Maciej W. Rozycki" <[email protected]>

Decrease the number of PCI BAR pair slots allocated for port subdrivers
from 4 to 2 as none wants more than 2 at this time, reducing the memory
footprint a little. No functional change.

Signed-off-by: Maciej W. Rozycki <[email protected]>
Signed-off-by: Sudip Mukherjee <[email protected]>
---
drivers/parport/parport_pc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index 9b4fe9a2b549..88e125e36230 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -2657,7 +2657,7 @@ static struct parport_pc_pci {
int lo;
int hi;
/* -1 if not there, >6 for offset-method (max BAR is 6) */
- } addr[4];
+ } addr[2];

/* Bit field of parport modes to exclude. */
unsigned int mode_mask;
--
2.30.2

2023-01-08 22:08:05

by Sudip Mukherjee

[permalink] [raw]
Subject: [PATCH 4/7] parport_pc: Add a mode mask field for PCI devices

From: "Maciej W. Rozycki" <[email protected]>

Add a mode mask field for PCI devices and use `__parport_pc_probe_port'
in place of `parport_pc_probe_port' to apply it.

Signed-off-by: Maciej W. Rozycki <[email protected]>
Signed-off-by: Sudip Mukherjee <[email protected]>
---
drivers/parport/parport_pc.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index ad49fd356c7b..2928f36e05ff 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -2659,6 +2659,9 @@ static struct parport_pc_pci {
/* -1 if not there, >6 for offset-method (max BAR is 6) */
} addr[4];

+ /* Bit field of parport modes to exclude. */
+ unsigned int mode_mask;
+
/* If set, this is called immediately after pci_enable_device.
* If it returns non-zero, no probing will take place and the
* ports will not be used. */
@@ -2862,9 +2865,10 @@ static int parport_pc_pci_probe(struct pci_dev *dev,
id->vendor, id->device, io_lo, io_hi, irq);
}
data->ports[count] =
- parport_pc_probe_port(io_lo, io_hi, irq,
- PARPORT_DMA_NONE, &dev->dev,
- IRQF_SHARED);
+ __parport_pc_probe_port(io_lo, io_hi, irq,
+ PARPORT_DMA_NONE, &dev->dev,
+ IRQF_SHARED,
+ cards[i].mode_mask, 0);
if (data->ports[count])
count++;
}
--
2.30.2

2023-01-08 22:26:25

by Sudip Mukherjee

[permalink] [raw]
Subject: [PATCH 5/7] parport_pc: Add an ECR mask field for PCI devices

From: "Maciej W. Rozycki" <[email protected]>

Add a bitmask field specifying writable ECR bits for PCI devices and
apply it via `__parport_pc_probe_port'.

Signed-off-by: Maciej W. Rozycki <[email protected]>
Signed-off-by: Sudip Mukherjee <[email protected]>
---
drivers/parport/parport_pc.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index 2928f36e05ff..d7e64f6dfe90 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -2662,6 +2662,10 @@ static struct parport_pc_pci {
/* Bit field of parport modes to exclude. */
unsigned int mode_mask;

+ /* If non-zero, sets the bitmask of writable ECR bits. In that
+ * case additionally bit 0 will be forcibly set on writes. */
+ unsigned char ecr_writable;
+
/* If set, this is called immediately after pci_enable_device.
* If it returns non-zero, no probing will take place and the
* ports will not be used. */
@@ -2868,7 +2872,8 @@ static int parport_pc_pci_probe(struct pci_dev *dev,
__parport_pc_probe_port(io_lo, io_hi, irq,
PARPORT_DMA_NONE, &dev->dev,
IRQF_SHARED,
- cards[i].mode_mask, 0);
+ cards[i].mode_mask,
+ cards[i].ecr_writable);
if (data->ports[count])
count++;
}
--
2.30.2

2023-01-08 22:26:50

by Sudip Mukherjee

[permalink] [raw]
Subject: [PATCH 3/7] parport_pc: Let chipset drivers mask ECR bits on writes

From: "Maciej W. Rozycki" <[email protected]>

Provide an `ecr_writable' parameter to `__parport_pc_probe_port' so that
callers can specify a mask of bits to modify on ECR writes.

To avoid the need for separate bit set and bit clear masks always set
bit 0 whenever a non-zero mask has been set, as all the currently known
cases where a mask is required, that is Oxford Semiconductor devices, do
require this bit to be set. If further cases are discovered where the
bit is required to be clear, we can update code accordingly, but chances
are very low as the bit is supposed to be read-only[1].

Skip ECR probing, which can be problematic as the Oxford Semiconductor
OX12PCI840 part has been reported to lock up on setting bit 2, whenever
a non-zero mask has been requested by a port subdriver, assuming that
the ECR must be there if the subdriver has requested a specific way to
access it.

References:

[1] "Extended Capabilities Port Protocol and ISA Interface Standard",
Microsoft Corporation, Revision: 1.14, July 14, 1993, Table 14
"Extended Control Register"

Signed-off-by: Maciej W. Rozycki <[email protected]>
Signed-off-by: Sudip Mukherjee <[email protected]>
---
drivers/parport/parport_pc.c | 46 +++++++++++++++++++++++-------------
include/linux/parport_pc.h | 3 +++
2 files changed, 32 insertions(+), 17 deletions(-)

diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index 9daaaaa305e6..ad49fd356c7b 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -106,15 +106,22 @@ static int pnp_registered_parport;
static void frob_econtrol(struct parport *pb, unsigned char m,
unsigned char v)
{
+ const struct parport_pc_private *priv = pb->physport->private_data;
+ unsigned char ecr_writable = priv->ecr_writable;
unsigned char ectr = 0;
+ unsigned char new;

if (m != 0xff)
ectr = inb(ECONTROL(pb));

- pr_debug("frob_econtrol(%02x,%02x): %02x -> %02x\n",
- m, v, ectr, (ectr & ~m) ^ v);
+ new = (ectr & ~m) ^ v;
+ if (ecr_writable)
+ /* All known users of the ECR mask require bit 0 to be set. */
+ new = (new & ecr_writable) | 1;

- outb((ectr & ~m) ^ v, ECONTROL(pb));
+ pr_debug("frob_econtrol(%02x,%02x): %02x -> %02x\n", m, v, ectr, new);
+
+ outb(new, ECONTROL(pb));
}

static inline void frob_set_mode(struct parport *p, int mode)
@@ -1479,21 +1486,24 @@ static int parport_ECR_present(struct parport *pb)
struct parport_pc_private *priv = pb->private_data;
unsigned char r = 0xc;

- outb(r, CONTROL(pb));
- if ((inb(ECONTROL(pb)) & 0x3) == (r & 0x3)) {
- outb(r ^ 0x2, CONTROL(pb)); /* Toggle bit 1 */
+ if (!priv->ecr_writable) {
+ outb(r, CONTROL(pb));
+ if ((inb(ECONTROL(pb)) & 0x3) == (r & 0x3)) {
+ outb(r ^ 0x2, CONTROL(pb)); /* Toggle bit 1 */

- r = inb(CONTROL(pb));
- if ((inb(ECONTROL(pb)) & 0x2) == (r & 0x2))
- goto no_reg; /* Sure that no ECR register exists */
- }
+ r = inb(CONTROL(pb));
+ if ((inb(ECONTROL(pb)) & 0x2) == (r & 0x2))
+ /* Sure that no ECR register exists */
+ goto no_reg;
+ }

- if ((inb(ECONTROL(pb)) & 0x3) != 0x1)
- goto no_reg;
+ if ((inb(ECONTROL(pb)) & 0x3) != 0x1)
+ goto no_reg;

- ECR_WRITE(pb, 0x34);
- if (inb(ECONTROL(pb)) != 0x35)
- goto no_reg;
+ ECR_WRITE(pb, 0x34);
+ if (inb(ECONTROL(pb)) != 0x35)
+ goto no_reg;
+ }

priv->ecr = 1;
outb(0xc, CONTROL(pb));
@@ -2005,7 +2015,8 @@ static struct parport *__parport_pc_probe_port(unsigned long int base,
int irq, int dma,
struct device *dev,
int irqflags,
- unsigned int mode_mask)
+ unsigned int mode_mask,
+ unsigned char ecr_writable)
{
struct parport_pc_private *priv;
struct parport_operations *ops;
@@ -2054,6 +2065,7 @@ static struct parport *__parport_pc_probe_port(unsigned long int base,
priv->ctr = 0xc;
priv->ctr_writable = ~0x10;
priv->ecr = 0;
+ priv->ecr_writable = ecr_writable;
priv->fifo_depth = 0;
priv->dma_buf = NULL;
priv->dma_handle = 0;
@@ -2256,7 +2268,7 @@ struct parport *parport_pc_probe_port(unsigned long int base,
int irqflags)
{
return __parport_pc_probe_port(base, base_hi, irq, dma,
- dev, irqflags, 0);
+ dev, irqflags, 0, 0);
}
EXPORT_SYMBOL(parport_pc_probe_port);

diff --git a/include/linux/parport_pc.h b/include/linux/parport_pc.h
index 3d6fc576d6a1..f1ec5c10c3b3 100644
--- a/include/linux/parport_pc.h
+++ b/include/linux/parport_pc.h
@@ -26,6 +26,9 @@ struct parport_pc_private {
/* Whether or not there's an ECR. */
int ecr;

+ /* Bitmask of writable ECR bits. */
+ unsigned char ecr_writable;
+
/* Number of PWords that FIFO will hold. */
int fifo_depth;

--
2.30.2

2023-01-08 22:28:08

by Sudip Mukherjee

[permalink] [raw]
Subject: [PATCH 2/7] parport_pc: Let chipset drivers mask unsupported modes

From: "Maciej W. Rozycki" <[email protected]>

Rename `parport_pc_probe_port' to `__parport_pc_probe_port' and add a
`mode_mask' parameter so that callers can specify a mask of unsupported
modes to exclude even if mode probing seems to indicate otherwise. Add
a `parport_pc_probe_port' wrapper with an implicit mask of 0 for the
current callers to use.

No functional change at this point, but the configuration of data write
handlers is now no longer intertwined with determination and reporting
of available modes.

Signed-off-by: Maciej W. Rozycki <[email protected]>
Signed-off-by: Sudip Mukherjee <[email protected]>
---
drivers/parport/parport_pc.c | 45 ++++++++++++++++++++++++++----------
1 file changed, 33 insertions(+), 12 deletions(-)

diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index de7afbea96cd..9daaaaa305e6 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -2000,11 +2000,12 @@ static int parport_dma_probe(struct parport *p)
static LIST_HEAD(ports_list);
static DEFINE_SPINLOCK(ports_lock);

-struct parport *parport_pc_probe_port(unsigned long int base,
- unsigned long int base_hi,
- int irq, int dma,
- struct device *dev,
- int irqflags)
+static struct parport *__parport_pc_probe_port(unsigned long int base,
+ unsigned long int base_hi,
+ int irq, int dma,
+ struct device *dev,
+ int irqflags,
+ unsigned int mode_mask)
{
struct parport_pc_private *priv;
struct parport_operations *ops;
@@ -2116,18 +2117,28 @@ struct parport *parport_pc_probe_port(unsigned long int base,
p->dma != PARPORT_DMA_NOFIFO &&
priv->fifo_depth > 0 && p->irq != PARPORT_IRQ_NONE) {
p->modes |= PARPORT_MODE_ECP | PARPORT_MODE_COMPAT;
+ if (p->dma != PARPORT_DMA_NONE)
+ p->modes |= PARPORT_MODE_DMA;
+ } else
+ /* We can't use the DMA channel after all. */
+ p->dma = PARPORT_DMA_NONE;
+#endif /* Allowed to use FIFO/DMA */
+
+ p->modes &= ~mode_mask;
+
+#ifdef CONFIG_PARPORT_PC_FIFO
+ if ((p->modes & PARPORT_MODE_COMPAT) != 0)
p->ops->compat_write_data = parport_pc_compat_write_block_pio;
#ifdef CONFIG_PARPORT_1284
+ if ((p->modes & PARPORT_MODE_ECP) != 0)
p->ops->ecp_write_data = parport_pc_ecp_write_block_pio;
-#endif /* IEEE 1284 support */
- if (p->dma != PARPORT_DMA_NONE) {
+#endif
+ if ((p->modes & (PARPORT_MODE_ECP | PARPORT_MODE_COMPAT)) != 0) {
+ if ((p->modes & PARPORT_MODE_DMA) != 0)
pr_cont(", dma %d", p->dma);
- p->modes |= PARPORT_MODE_DMA;
- } else
+ else
pr_cont(", using FIFO");
- } else
- /* We can't use the DMA channel after all. */
- p->dma = PARPORT_DMA_NONE;
+ }
#endif /* Allowed to use FIFO/DMA */

pr_cont(" [");
@@ -2237,6 +2248,16 @@ do { \
platform_device_unregister(pdev);
return NULL;
}
+
+struct parport *parport_pc_probe_port(unsigned long int base,
+ unsigned long int base_hi,
+ int irq, int dma,
+ struct device *dev,
+ int irqflags)
+{
+ return __parport_pc_probe_port(base, base_hi, irq, dma,
+ dev, irqflags, 0);
+}
EXPORT_SYMBOL(parport_pc_probe_port);

void parport_pc_unregister_port(struct parport *p)
--
2.30.2