mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-26 00:51:02 +07:00
6ab3d5624e
Signed-off-by: Jörn Engel <joern@wohnheim.fh-wedel.de> Signed-off-by: Adrian Bunk <bunk@stusta.de>
915 lines
23 KiB
C
915 lines
23 KiB
C
/* IEEE-1284 operations for parport.
|
|
*
|
|
* This file is for generic IEEE 1284 operations. The idea is that
|
|
* they are used by the low-level drivers. If they have a special way
|
|
* of doing something, they can provide their own routines (and put
|
|
* the function pointers in port->ops); if not, they can just use these
|
|
* as a fallback.
|
|
*
|
|
* Note: Make no assumptions about hardware or architecture in this file!
|
|
*
|
|
* Author: Tim Waugh <tim@cyberelk.demon.co.uk>
|
|
* Fixed AUTOFD polarity in ecp_forward_to_reverse(). Fred Barnes, 1999
|
|
* Software emulated EPP fixes, Fred Barnes, 04/2001.
|
|
*/
|
|
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/parport.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/sched.h>
|
|
#include <asm/uaccess.h>
|
|
|
|
#undef DEBUG /* undef me for production */
|
|
|
|
#ifdef CONFIG_LP_CONSOLE
|
|
#undef DEBUG /* Don't want a garbled console */
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
#define DPRINTK(stuff...) printk (stuff)
|
|
#else
|
|
#define DPRINTK(stuff...)
|
|
#endif
|
|
|
|
/*** *
|
|
* One-way data transfer functions. *
|
|
* ***/
|
|
|
|
/* Compatibility mode. */
|
|
size_t parport_ieee1284_write_compat (struct parport *port,
|
|
const void *buffer, size_t len,
|
|
int flags)
|
|
{
|
|
int no_irq = 1;
|
|
ssize_t count = 0;
|
|
const unsigned char *addr = buffer;
|
|
unsigned char byte;
|
|
struct pardevice *dev = port->physport->cad;
|
|
unsigned char ctl = (PARPORT_CONTROL_SELECT
|
|
| PARPORT_CONTROL_INIT);
|
|
|
|
if (port->irq != PARPORT_IRQ_NONE) {
|
|
parport_enable_irq (port);
|
|
no_irq = 0;
|
|
}
|
|
|
|
port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
|
|
parport_write_control (port, ctl);
|
|
parport_data_forward (port);
|
|
while (count < len) {
|
|
unsigned long expire = jiffies + dev->timeout;
|
|
long wait = msecs_to_jiffies(10);
|
|
unsigned char mask = (PARPORT_STATUS_ERROR
|
|
| PARPORT_STATUS_BUSY);
|
|
unsigned char val = (PARPORT_STATUS_ERROR
|
|
| PARPORT_STATUS_BUSY);
|
|
|
|
/* Wait until the peripheral's ready */
|
|
do {
|
|
/* Is the peripheral ready yet? */
|
|
if (!parport_wait_peripheral (port, mask, val))
|
|
/* Skip the loop */
|
|
goto ready;
|
|
|
|
/* Is the peripheral upset? */
|
|
if ((parport_read_status (port) &
|
|
(PARPORT_STATUS_PAPEROUT |
|
|
PARPORT_STATUS_SELECT |
|
|
PARPORT_STATUS_ERROR))
|
|
!= (PARPORT_STATUS_SELECT |
|
|
PARPORT_STATUS_ERROR))
|
|
/* If nFault is asserted (i.e. no
|
|
* error) and PAPEROUT and SELECT are
|
|
* just red herrings, give the driver
|
|
* a chance to check it's happy with
|
|
* that before continuing. */
|
|
goto stop;
|
|
|
|
/* Have we run out of time? */
|
|
if (!time_before (jiffies, expire))
|
|
break;
|
|
|
|
/* Yield the port for a while. If this is the
|
|
first time around the loop, don't let go of
|
|
the port. This way, we find out if we have
|
|
our interrupt handler called. */
|
|
if (count && no_irq) {
|
|
parport_release (dev);
|
|
schedule_timeout_interruptible(wait);
|
|
parport_claim_or_block (dev);
|
|
}
|
|
else
|
|
/* We must have the device claimed here */
|
|
parport_wait_event (port, wait);
|
|
|
|
/* Is there a signal pending? */
|
|
if (signal_pending (current))
|
|
break;
|
|
|
|
/* Wait longer next time. */
|
|
wait *= 2;
|
|
} while (time_before (jiffies, expire));
|
|
|
|
if (signal_pending (current))
|
|
break;
|
|
|
|
DPRINTK (KERN_DEBUG "%s: Timed out\n", port->name);
|
|
break;
|
|
|
|
ready:
|
|
/* Write the character to the data lines. */
|
|
byte = *addr++;
|
|
parport_write_data (port, byte);
|
|
udelay (1);
|
|
|
|
/* Pulse strobe. */
|
|
parport_write_control (port, ctl | PARPORT_CONTROL_STROBE);
|
|
udelay (1); /* strobe */
|
|
|
|
parport_write_control (port, ctl);
|
|
udelay (1); /* hold */
|
|
|
|
/* Assume the peripheral received it. */
|
|
count++;
|
|
|
|
/* Let another process run if it needs to. */
|
|
if (time_before (jiffies, expire))
|
|
if (!parport_yield_blocking (dev)
|
|
&& need_resched())
|
|
schedule ();
|
|
}
|
|
stop:
|
|
port->physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
|
|
|
|
return count;
|
|
}
|
|
|
|
/* Nibble mode. */
|
|
size_t parport_ieee1284_read_nibble (struct parport *port,
|
|
void *buffer, size_t len,
|
|
int flags)
|
|
{
|
|
#ifndef CONFIG_PARPORT_1284
|
|
return 0;
|
|
#else
|
|
unsigned char *buf = buffer;
|
|
int i;
|
|
unsigned char byte = 0;
|
|
|
|
len *= 2; /* in nibbles */
|
|
for (i=0; i < len; i++) {
|
|
unsigned char nibble;
|
|
|
|
/* Does the error line indicate end of data? */
|
|
if (((i & 1) == 0) &&
|
|
(parport_read_status(port) & PARPORT_STATUS_ERROR)) {
|
|
goto end_of_data;
|
|
}
|
|
|
|
/* Event 7: Set nAutoFd low. */
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_AUTOFD,
|
|
PARPORT_CONTROL_AUTOFD);
|
|
|
|
/* Event 9: nAck goes low. */
|
|
port->ieee1284.phase = IEEE1284_PH_REV_DATA;
|
|
if (parport_wait_peripheral (port,
|
|
PARPORT_STATUS_ACK, 0)) {
|
|
/* Timeout -- no more data? */
|
|
DPRINTK (KERN_DEBUG
|
|
"%s: Nibble timeout at event 9 (%d bytes)\n",
|
|
port->name, i/2);
|
|
parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
|
|
break;
|
|
}
|
|
|
|
|
|
/* Read a nibble. */
|
|
nibble = parport_read_status (port) >> 3;
|
|
nibble &= ~8;
|
|
if ((nibble & 0x10) == 0)
|
|
nibble |= 8;
|
|
nibble &= 0xf;
|
|
|
|
/* Event 10: Set nAutoFd high. */
|
|
parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
|
|
|
|
/* Event 11: nAck goes high. */
|
|
if (parport_wait_peripheral (port,
|
|
PARPORT_STATUS_ACK,
|
|
PARPORT_STATUS_ACK)) {
|
|
/* Timeout -- no more data? */
|
|
DPRINTK (KERN_DEBUG
|
|
"%s: Nibble timeout at event 11\n",
|
|
port->name);
|
|
break;
|
|
}
|
|
|
|
if (i & 1) {
|
|
/* Second nibble */
|
|
byte |= nibble << 4;
|
|
*buf++ = byte;
|
|
} else
|
|
byte = nibble;
|
|
}
|
|
|
|
if (i == len) {
|
|
/* Read the last nibble without checking data avail. */
|
|
if (parport_read_status (port) & PARPORT_STATUS_ERROR) {
|
|
end_of_data:
|
|
DPRINTK (KERN_DEBUG
|
|
"%s: No more nibble data (%d bytes)\n",
|
|
port->name, i/2);
|
|
|
|
/* Go to reverse idle phase. */
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_AUTOFD,
|
|
PARPORT_CONTROL_AUTOFD);
|
|
port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE;
|
|
}
|
|
else
|
|
port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL;
|
|
}
|
|
|
|
return i/2;
|
|
#endif /* IEEE1284 support */
|
|
}
|
|
|
|
/* Byte mode. */
|
|
size_t parport_ieee1284_read_byte (struct parport *port,
|
|
void *buffer, size_t len,
|
|
int flags)
|
|
{
|
|
#ifndef CONFIG_PARPORT_1284
|
|
return 0;
|
|
#else
|
|
unsigned char *buf = buffer;
|
|
ssize_t count = 0;
|
|
|
|
for (count = 0; count < len; count++) {
|
|
unsigned char byte;
|
|
|
|
/* Data available? */
|
|
if (parport_read_status (port) & PARPORT_STATUS_ERROR) {
|
|
goto end_of_data;
|
|
}
|
|
|
|
/* Event 14: Place data bus in high impedance state. */
|
|
parport_data_reverse (port);
|
|
|
|
/* Event 7: Set nAutoFd low. */
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_AUTOFD,
|
|
PARPORT_CONTROL_AUTOFD);
|
|
|
|
/* Event 9: nAck goes low. */
|
|
port->physport->ieee1284.phase = IEEE1284_PH_REV_DATA;
|
|
if (parport_wait_peripheral (port,
|
|
PARPORT_STATUS_ACK,
|
|
0)) {
|
|
/* Timeout -- no more data? */
|
|
parport_frob_control (port, PARPORT_CONTROL_AUTOFD,
|
|
0);
|
|
DPRINTK (KERN_DEBUG "%s: Byte timeout at event 9\n",
|
|
port->name);
|
|
break;
|
|
}
|
|
|
|
byte = parport_read_data (port);
|
|
*buf++ = byte;
|
|
|
|
/* Event 10: Set nAutoFd high */
|
|
parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
|
|
|
|
/* Event 11: nAck goes high. */
|
|
if (parport_wait_peripheral (port,
|
|
PARPORT_STATUS_ACK,
|
|
PARPORT_STATUS_ACK)) {
|
|
/* Timeout -- no more data? */
|
|
DPRINTK (KERN_DEBUG "%s: Byte timeout at event 11\n",
|
|
port->name);
|
|
break;
|
|
}
|
|
|
|
/* Event 16: Set nStrobe low. */
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_STROBE,
|
|
PARPORT_CONTROL_STROBE);
|
|
udelay (5);
|
|
|
|
/* Event 17: Set nStrobe high. */
|
|
parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
|
|
}
|
|
|
|
if (count == len) {
|
|
/* Read the last byte without checking data avail. */
|
|
if (parport_read_status (port) & PARPORT_STATUS_ERROR) {
|
|
end_of_data:
|
|
DPRINTK (KERN_DEBUG
|
|
"%s: No more byte data (%Zd bytes)\n",
|
|
port->name, count);
|
|
|
|
/* Go to reverse idle phase. */
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_AUTOFD,
|
|
PARPORT_CONTROL_AUTOFD);
|
|
port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE;
|
|
}
|
|
else
|
|
port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL;
|
|
}
|
|
|
|
return count;
|
|
#endif /* IEEE1284 support */
|
|
}
|
|
|
|
/*** *
|
|
* ECP Functions. *
|
|
* ***/
|
|
|
|
#ifdef CONFIG_PARPORT_1284
|
|
|
|
static inline
|
|
int ecp_forward_to_reverse (struct parport *port)
|
|
{
|
|
int retval;
|
|
|
|
/* Event 38: Set nAutoFd low */
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_AUTOFD,
|
|
PARPORT_CONTROL_AUTOFD);
|
|
parport_data_reverse (port);
|
|
udelay (5);
|
|
|
|
/* Event 39: Set nInit low to initiate bus reversal */
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_INIT,
|
|
0);
|
|
|
|
/* Event 40: PError goes low */
|
|
retval = parport_wait_peripheral (port,
|
|
PARPORT_STATUS_PAPEROUT, 0);
|
|
|
|
if (!retval) {
|
|
DPRINTK (KERN_DEBUG "%s: ECP direction: reverse\n",
|
|
port->name);
|
|
port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
|
|
} else {
|
|
DPRINTK (KERN_DEBUG "%s: ECP direction: failed to reverse\n",
|
|
port->name);
|
|
port->ieee1284.phase = IEEE1284_PH_ECP_DIR_UNKNOWN;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static inline
|
|
int ecp_reverse_to_forward (struct parport *port)
|
|
{
|
|
int retval;
|
|
|
|
/* Event 47: Set nInit high */
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_INIT
|
|
| PARPORT_CONTROL_AUTOFD,
|
|
PARPORT_CONTROL_INIT
|
|
| PARPORT_CONTROL_AUTOFD);
|
|
|
|
/* Event 49: PError goes high */
|
|
retval = parport_wait_peripheral (port,
|
|
PARPORT_STATUS_PAPEROUT,
|
|
PARPORT_STATUS_PAPEROUT);
|
|
|
|
if (!retval) {
|
|
parport_data_forward (port);
|
|
DPRINTK (KERN_DEBUG "%s: ECP direction: forward\n",
|
|
port->name);
|
|
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
|
|
} else {
|
|
DPRINTK (KERN_DEBUG
|
|
"%s: ECP direction: failed to switch forward\n",
|
|
port->name);
|
|
port->ieee1284.phase = IEEE1284_PH_ECP_DIR_UNKNOWN;
|
|
}
|
|
|
|
|
|
return retval;
|
|
}
|
|
|
|
#endif /* IEEE1284 support */
|
|
|
|
/* ECP mode, forward channel, data. */
|
|
size_t parport_ieee1284_ecp_write_data (struct parport *port,
|
|
const void *buffer, size_t len,
|
|
int flags)
|
|
{
|
|
#ifndef CONFIG_PARPORT_1284
|
|
return 0;
|
|
#else
|
|
const unsigned char *buf = buffer;
|
|
size_t written;
|
|
int retry;
|
|
|
|
port = port->physport;
|
|
|
|
if (port->ieee1284.phase != IEEE1284_PH_FWD_IDLE)
|
|
if (ecp_reverse_to_forward (port))
|
|
return 0;
|
|
|
|
port->ieee1284.phase = IEEE1284_PH_FWD_DATA;
|
|
|
|
/* HostAck high (data, not command) */
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_AUTOFD
|
|
| PARPORT_CONTROL_STROBE
|
|
| PARPORT_CONTROL_INIT,
|
|
PARPORT_CONTROL_INIT);
|
|
for (written = 0; written < len; written++, buf++) {
|
|
unsigned long expire = jiffies + port->cad->timeout;
|
|
unsigned char byte;
|
|
|
|
byte = *buf;
|
|
try_again:
|
|
parport_write_data (port, byte);
|
|
parport_frob_control (port, PARPORT_CONTROL_STROBE,
|
|
PARPORT_CONTROL_STROBE);
|
|
udelay (5);
|
|
for (retry = 0; retry < 100; retry++) {
|
|
if (!parport_wait_peripheral (port,
|
|
PARPORT_STATUS_BUSY, 0))
|
|
goto success;
|
|
|
|
if (signal_pending (current)) {
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_STROBE,
|
|
0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Time for Host Transfer Recovery (page 41 of IEEE1284) */
|
|
DPRINTK (KERN_DEBUG "%s: ECP transfer stalled!\n", port->name);
|
|
|
|
parport_frob_control (port, PARPORT_CONTROL_INIT,
|
|
PARPORT_CONTROL_INIT);
|
|
udelay (50);
|
|
if (parport_read_status (port) & PARPORT_STATUS_PAPEROUT) {
|
|
/* It's buggered. */
|
|
parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
|
|
break;
|
|
}
|
|
|
|
parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
|
|
udelay (50);
|
|
if (!(parport_read_status (port) & PARPORT_STATUS_PAPEROUT))
|
|
break;
|
|
|
|
DPRINTK (KERN_DEBUG "%s: Host transfer recovered\n",
|
|
port->name);
|
|
|
|
if (time_after_eq (jiffies, expire)) break;
|
|
goto try_again;
|
|
success:
|
|
parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
|
|
udelay (5);
|
|
if (parport_wait_peripheral (port,
|
|
PARPORT_STATUS_BUSY,
|
|
PARPORT_STATUS_BUSY))
|
|
/* Peripheral hasn't accepted the data. */
|
|
break;
|
|
}
|
|
|
|
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
|
|
|
|
return written;
|
|
#endif /* IEEE1284 support */
|
|
}
|
|
|
|
/* ECP mode, reverse channel, data. */
|
|
size_t parport_ieee1284_ecp_read_data (struct parport *port,
|
|
void *buffer, size_t len, int flags)
|
|
{
|
|
#ifndef CONFIG_PARPORT_1284
|
|
return 0;
|
|
#else
|
|
struct pardevice *dev = port->cad;
|
|
unsigned char *buf = buffer;
|
|
int rle_count = 0; /* shut gcc up */
|
|
unsigned char ctl;
|
|
int rle = 0;
|
|
ssize_t count = 0;
|
|
|
|
port = port->physport;
|
|
|
|
if (port->ieee1284.phase != IEEE1284_PH_REV_IDLE)
|
|
if (ecp_forward_to_reverse (port))
|
|
return 0;
|
|
|
|
port->ieee1284.phase = IEEE1284_PH_REV_DATA;
|
|
|
|
/* Set HostAck low to start accepting data. */
|
|
ctl = parport_read_control (port);
|
|
ctl &= ~(PARPORT_CONTROL_STROBE | PARPORT_CONTROL_INIT |
|
|
PARPORT_CONTROL_AUTOFD);
|
|
parport_write_control (port,
|
|
ctl | PARPORT_CONTROL_AUTOFD);
|
|
while (count < len) {
|
|
unsigned long expire = jiffies + dev->timeout;
|
|
unsigned char byte;
|
|
int command;
|
|
|
|
/* Event 43: Peripheral sets nAck low. It can take as
|
|
long as it wants. */
|
|
while (parport_wait_peripheral (port, PARPORT_STATUS_ACK, 0)) {
|
|
/* The peripheral hasn't given us data in
|
|
35ms. If we have data to give back to the
|
|
caller, do it now. */
|
|
if (count)
|
|
goto out;
|
|
|
|
/* If we've used up all the time we were allowed,
|
|
give up altogether. */
|
|
if (!time_before (jiffies, expire))
|
|
goto out;
|
|
|
|
/* Yield the port for a while. */
|
|
if (count && dev->port->irq != PARPORT_IRQ_NONE) {
|
|
parport_release (dev);
|
|
schedule_timeout_interruptible(msecs_to_jiffies(40));
|
|
parport_claim_or_block (dev);
|
|
}
|
|
else
|
|
/* We must have the device claimed here. */
|
|
parport_wait_event (port, msecs_to_jiffies(40));
|
|
|
|
/* Is there a signal pending? */
|
|
if (signal_pending (current))
|
|
goto out;
|
|
}
|
|
|
|
/* Is this a command? */
|
|
if (rle)
|
|
/* The last byte was a run-length count, so
|
|
this can't be as well. */
|
|
command = 0;
|
|
else
|
|
command = (parport_read_status (port) &
|
|
PARPORT_STATUS_BUSY) ? 1 : 0;
|
|
|
|
/* Read the data. */
|
|
byte = parport_read_data (port);
|
|
|
|
/* If this is a channel command, rather than an RLE
|
|
command or a normal data byte, don't accept it. */
|
|
if (command) {
|
|
if (byte & 0x80) {
|
|
DPRINTK (KERN_DEBUG "%s: stopping short at "
|
|
"channel command (%02x)\n",
|
|
port->name, byte);
|
|
goto out;
|
|
}
|
|
else if (port->ieee1284.mode != IEEE1284_MODE_ECPRLE)
|
|
DPRINTK (KERN_DEBUG "%s: device illegally "
|
|
"using RLE; accepting anyway\n",
|
|
port->name);
|
|
|
|
rle_count = byte + 1;
|
|
|
|
/* Are we allowed to read that many bytes? */
|
|
if (rle_count > (len - count)) {
|
|
DPRINTK (KERN_DEBUG "%s: leaving %d RLE bytes "
|
|
"for next time\n", port->name,
|
|
rle_count);
|
|
break;
|
|
}
|
|
|
|
rle = 1;
|
|
}
|
|
|
|
/* Event 44: Set HostAck high, acknowledging handshake. */
|
|
parport_write_control (port, ctl);
|
|
|
|
/* Event 45: The peripheral has 35ms to set nAck high. */
|
|
if (parport_wait_peripheral (port, PARPORT_STATUS_ACK,
|
|
PARPORT_STATUS_ACK)) {
|
|
/* It's gone wrong. Return what data we have
|
|
to the caller. */
|
|
DPRINTK (KERN_DEBUG "ECP read timed out at 45\n");
|
|
|
|
if (command)
|
|
printk (KERN_WARNING
|
|
"%s: command ignored (%02x)\n",
|
|
port->name, byte);
|
|
|
|
break;
|
|
}
|
|
|
|
/* Event 46: Set HostAck low and accept the data. */
|
|
parport_write_control (port,
|
|
ctl | PARPORT_CONTROL_AUTOFD);
|
|
|
|
/* If we just read a run-length count, fetch the data. */
|
|
if (command)
|
|
continue;
|
|
|
|
/* If this is the byte after a run-length count, decompress. */
|
|
if (rle) {
|
|
rle = 0;
|
|
memset (buf, byte, rle_count);
|
|
buf += rle_count;
|
|
count += rle_count;
|
|
DPRINTK (KERN_DEBUG "%s: decompressed to %d bytes\n",
|
|
port->name, rle_count);
|
|
} else {
|
|
/* Normal data byte. */
|
|
*buf = byte;
|
|
buf++, count++;
|
|
}
|
|
}
|
|
|
|
out:
|
|
port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
|
|
return count;
|
|
#endif /* IEEE1284 support */
|
|
}
|
|
|
|
/* ECP mode, forward channel, commands. */
|
|
size_t parport_ieee1284_ecp_write_addr (struct parport *port,
|
|
const void *buffer, size_t len,
|
|
int flags)
|
|
{
|
|
#ifndef CONFIG_PARPORT_1284
|
|
return 0;
|
|
#else
|
|
const unsigned char *buf = buffer;
|
|
size_t written;
|
|
int retry;
|
|
|
|
port = port->physport;
|
|
|
|
if (port->ieee1284.phase != IEEE1284_PH_FWD_IDLE)
|
|
if (ecp_reverse_to_forward (port))
|
|
return 0;
|
|
|
|
port->ieee1284.phase = IEEE1284_PH_FWD_DATA;
|
|
|
|
/* HostAck low (command, not data) */
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_AUTOFD
|
|
| PARPORT_CONTROL_STROBE
|
|
| PARPORT_CONTROL_INIT,
|
|
PARPORT_CONTROL_AUTOFD
|
|
| PARPORT_CONTROL_INIT);
|
|
for (written = 0; written < len; written++, buf++) {
|
|
unsigned long expire = jiffies + port->cad->timeout;
|
|
unsigned char byte;
|
|
|
|
byte = *buf;
|
|
try_again:
|
|
parport_write_data (port, byte);
|
|
parport_frob_control (port, PARPORT_CONTROL_STROBE,
|
|
PARPORT_CONTROL_STROBE);
|
|
udelay (5);
|
|
for (retry = 0; retry < 100; retry++) {
|
|
if (!parport_wait_peripheral (port,
|
|
PARPORT_STATUS_BUSY, 0))
|
|
goto success;
|
|
|
|
if (signal_pending (current)) {
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_STROBE,
|
|
0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Time for Host Transfer Recovery (page 41 of IEEE1284) */
|
|
DPRINTK (KERN_DEBUG "%s: ECP transfer stalled!\n", port->name);
|
|
|
|
parport_frob_control (port, PARPORT_CONTROL_INIT,
|
|
PARPORT_CONTROL_INIT);
|
|
udelay (50);
|
|
if (parport_read_status (port) & PARPORT_STATUS_PAPEROUT) {
|
|
/* It's buggered. */
|
|
parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
|
|
break;
|
|
}
|
|
|
|
parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
|
|
udelay (50);
|
|
if (!(parport_read_status (port) & PARPORT_STATUS_PAPEROUT))
|
|
break;
|
|
|
|
DPRINTK (KERN_DEBUG "%s: Host transfer recovered\n",
|
|
port->name);
|
|
|
|
if (time_after_eq (jiffies, expire)) break;
|
|
goto try_again;
|
|
success:
|
|
parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
|
|
udelay (5);
|
|
if (parport_wait_peripheral (port,
|
|
PARPORT_STATUS_BUSY,
|
|
PARPORT_STATUS_BUSY))
|
|
/* Peripheral hasn't accepted the data. */
|
|
break;
|
|
}
|
|
|
|
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
|
|
|
|
return written;
|
|
#endif /* IEEE1284 support */
|
|
}
|
|
|
|
/*** *
|
|
* EPP functions. *
|
|
* ***/
|
|
|
|
/* EPP mode, forward channel, data. */
|
|
size_t parport_ieee1284_epp_write_data (struct parport *port,
|
|
const void *buffer, size_t len,
|
|
int flags)
|
|
{
|
|
unsigned char *bp = (unsigned char *) buffer;
|
|
size_t ret = 0;
|
|
|
|
/* set EPP idle state (just to make sure) with strobe low */
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_STROBE |
|
|
PARPORT_CONTROL_AUTOFD |
|
|
PARPORT_CONTROL_SELECT |
|
|
PARPORT_CONTROL_INIT,
|
|
PARPORT_CONTROL_STROBE |
|
|
PARPORT_CONTROL_INIT);
|
|
port->ops->data_forward (port);
|
|
for (; len > 0; len--, bp++) {
|
|
/* Event 62: Write data and set autofd low */
|
|
parport_write_data (port, *bp);
|
|
parport_frob_control (port, PARPORT_CONTROL_AUTOFD,
|
|
PARPORT_CONTROL_AUTOFD);
|
|
|
|
/* Event 58: wait for busy (nWait) to go high */
|
|
if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY, 0, 10))
|
|
break;
|
|
|
|
/* Event 63: set nAutoFd (nDStrb) high */
|
|
parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
|
|
|
|
/* Event 60: wait for busy (nWait) to go low */
|
|
if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY,
|
|
PARPORT_STATUS_BUSY, 5))
|
|
break;
|
|
|
|
ret++;
|
|
}
|
|
|
|
/* Event 61: set strobe (nWrite) high */
|
|
parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* EPP mode, reverse channel, data. */
|
|
size_t parport_ieee1284_epp_read_data (struct parport *port,
|
|
void *buffer, size_t len,
|
|
int flags)
|
|
{
|
|
unsigned char *bp = (unsigned char *) buffer;
|
|
unsigned ret = 0;
|
|
|
|
/* set EPP idle state (just to make sure) with strobe high */
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_STROBE |
|
|
PARPORT_CONTROL_AUTOFD |
|
|
PARPORT_CONTROL_SELECT |
|
|
PARPORT_CONTROL_INIT,
|
|
PARPORT_CONTROL_INIT);
|
|
port->ops->data_reverse (port);
|
|
for (; len > 0; len--, bp++) {
|
|
/* Event 67: set nAutoFd (nDStrb) low */
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_AUTOFD,
|
|
PARPORT_CONTROL_AUTOFD);
|
|
/* Event 58: wait for Busy to go high */
|
|
if (parport_wait_peripheral (port, PARPORT_STATUS_BUSY, 0)) {
|
|
break;
|
|
}
|
|
|
|
*bp = parport_read_data (port);
|
|
|
|
/* Event 63: set nAutoFd (nDStrb) high */
|
|
parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
|
|
|
|
/* Event 60: wait for Busy to go low */
|
|
if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY,
|
|
PARPORT_STATUS_BUSY, 5)) {
|
|
break;
|
|
}
|
|
|
|
ret++;
|
|
}
|
|
port->ops->data_forward (port);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* EPP mode, forward channel, addresses. */
|
|
size_t parport_ieee1284_epp_write_addr (struct parport *port,
|
|
const void *buffer, size_t len,
|
|
int flags)
|
|
{
|
|
unsigned char *bp = (unsigned char *) buffer;
|
|
size_t ret = 0;
|
|
|
|
/* set EPP idle state (just to make sure) with strobe low */
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_STROBE |
|
|
PARPORT_CONTROL_AUTOFD |
|
|
PARPORT_CONTROL_SELECT |
|
|
PARPORT_CONTROL_INIT,
|
|
PARPORT_CONTROL_STROBE |
|
|
PARPORT_CONTROL_INIT);
|
|
port->ops->data_forward (port);
|
|
for (; len > 0; len--, bp++) {
|
|
/* Event 56: Write data and set nAStrb low. */
|
|
parport_write_data (port, *bp);
|
|
parport_frob_control (port, PARPORT_CONTROL_SELECT,
|
|
PARPORT_CONTROL_SELECT);
|
|
|
|
/* Event 58: wait for busy (nWait) to go high */
|
|
if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY, 0, 10))
|
|
break;
|
|
|
|
/* Event 59: set nAStrb high */
|
|
parport_frob_control (port, PARPORT_CONTROL_SELECT, 0);
|
|
|
|
/* Event 60: wait for busy (nWait) to go low */
|
|
if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY,
|
|
PARPORT_STATUS_BUSY, 5))
|
|
break;
|
|
|
|
ret++;
|
|
}
|
|
|
|
/* Event 61: set strobe (nWrite) high */
|
|
parport_frob_control (port, PARPORT_CONTROL_STROBE, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* EPP mode, reverse channel, addresses. */
|
|
size_t parport_ieee1284_epp_read_addr (struct parport *port,
|
|
void *buffer, size_t len,
|
|
int flags)
|
|
{
|
|
unsigned char *bp = (unsigned char *) buffer;
|
|
unsigned ret = 0;
|
|
|
|
/* Set EPP idle state (just to make sure) with strobe high */
|
|
parport_frob_control (port,
|
|
PARPORT_CONTROL_STROBE |
|
|
PARPORT_CONTROL_AUTOFD |
|
|
PARPORT_CONTROL_SELECT |
|
|
PARPORT_CONTROL_INIT,
|
|
PARPORT_CONTROL_INIT);
|
|
port->ops->data_reverse (port);
|
|
for (; len > 0; len--, bp++) {
|
|
/* Event 64: set nSelectIn (nAStrb) low */
|
|
parport_frob_control (port, PARPORT_CONTROL_SELECT,
|
|
PARPORT_CONTROL_SELECT);
|
|
|
|
/* Event 58: wait for Busy to go high */
|
|
if (parport_wait_peripheral (port, PARPORT_STATUS_BUSY, 0)) {
|
|
break;
|
|
}
|
|
|
|
*bp = parport_read_data (port);
|
|
|
|
/* Event 59: set nSelectIn (nAStrb) high */
|
|
parport_frob_control (port, PARPORT_CONTROL_SELECT,
|
|
PARPORT_CONTROL_SELECT);
|
|
|
|
/* Event 60: wait for Busy to go low */
|
|
if (parport_poll_peripheral (port, PARPORT_STATUS_BUSY,
|
|
PARPORT_STATUS_BUSY, 5))
|
|
break;
|
|
|
|
ret++;
|
|
}
|
|
port->ops->data_forward (port);
|
|
|
|
return ret;
|
|
}
|
|
|
|
EXPORT_SYMBOL(parport_ieee1284_ecp_write_data);
|
|
EXPORT_SYMBOL(parport_ieee1284_ecp_read_data);
|
|
EXPORT_SYMBOL(parport_ieee1284_ecp_write_addr);
|
|
EXPORT_SYMBOL(parport_ieee1284_write_compat);
|
|
EXPORT_SYMBOL(parport_ieee1284_read_nibble);
|
|
EXPORT_SYMBOL(parport_ieee1284_read_byte);
|
|
EXPORT_SYMBOL(parport_ieee1284_epp_write_data);
|
|
EXPORT_SYMBOL(parport_ieee1284_epp_read_data);
|
|
EXPORT_SYMBOL(parport_ieee1284_epp_write_addr);
|
|
EXPORT_SYMBOL(parport_ieee1284_epp_read_addr);
|