Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,9 @@
Currently known (or at least suspected) bugs in parport:
o lp doesn't allow you to read status while printing is in progress (is
this still true?).
o parport_pc_ecp_read_block_pio() is broken. parport will revert to the
software-driven mode in ieee1284_ops.c
See <URL:http://people.redhat.com/twaugh/parport/>.

165
drivers/parport/Kconfig Normal file
View file

@ -0,0 +1,165 @@
#
# For a description of the syntax of this configuration file,
# see Documentation/kbuild/kconfig-language.txt.
#
# Parport configuration.
#
config ARCH_MIGHT_HAVE_PC_PARPORT
bool
help
Select this config option from the architecture Kconfig if
the architecture might have PC parallel port hardware.
menuconfig PARPORT
tristate "Parallel port support"
depends on HAS_IOMEM
---help---
If you want to use devices connected to your machine's parallel port
(the connector at the computer with 25 holes), e.g. printer, ZIP
drive, PLIP link (Parallel Line Internet Protocol is mainly used to
create a mini network by connecting the parallel ports of two local
machines) etc., then you need to say Y here; please read
<file:Documentation/parport.txt> and
<file:drivers/parport/BUGS-parport>.
For extensive information about drivers for many devices attaching
to the parallel port see <http://www.torque.net/linux-pp.html> on
the WWW.
It is possible to share a single parallel port among several devices
and it is safe to compile all the corresponding drivers into the
kernel. To compile parallel port support as a module, choose M here:
the module will be called parport.
If you have more than one parallel port and want to specify which
port and IRQ to be used by this driver at module load time, take a
look at <file:Documentation/parport.txt>.
If unsure, say Y.
if PARPORT
config PARPORT_PC
tristate "PC-style hardware"
depends on ARCH_MIGHT_HAVE_PC_PARPORT
help
You should say Y here if you have a PC-style parallel port. All
IBM PC compatible computers and some Alphas have PC-style
parallel ports. PA-RISC owners should only say Y here if they
have a SuperIO parallel port.
To compile this driver as a module, choose M here: the
module will be called parport_pc.
If unsure, say Y.
config PARPORT_SERIAL
tristate "Multi-IO cards (parallel and serial)"
depends on SERIAL_8250_PCI && PARPORT_PC && PCI
help
This adds support for multi-IO PCI cards that have parallel and
serial ports. You should say Y or M here. If you say M, the module
will be called parport_serial.
config PARPORT_PC_FIFO
bool "Use FIFO/DMA if available"
depends on PARPORT_PC
help
Many parallel port chipsets provide hardware that can speed up
printing. Say Y here if you want to take advantage of that.
As well as actually having a FIFO, or DMA capability, the kernel
will need to know which IRQ the parallel port has. By default,
parallel port interrupts will not be used, and so neither will the
FIFO. See <file:Documentation/parport.txt> to find out how to
specify which IRQ/DMA to use.
config PARPORT_PC_SUPERIO
bool "SuperIO chipset support"
depends on PARPORT_PC && !PARISC
help
Saying Y here enables some probes for Super-IO chipsets in order to
find out things like base addresses, IRQ lines and DMA channels. It
is safe to say N.
config PARPORT_PC_PCMCIA
tristate "Support for PCMCIA management for PC-style ports"
depends on PCMCIA && PARPORT_PC
help
Say Y here if you need PCMCIA support for your PC-style parallel
ports. If unsure, say N.
config PARPORT_IP32
tristate "SGI IP32 builtin port"
depends on SGI_IP32
select PARPORT_NOT_PC
help
Say Y here if you need support for the parallel port on
SGI O2 machines. This code is also available as a module (say M),
called parport_ip32. If in doubt, saying N is the safe plan.
config PARPORT_AMIGA
tristate "Amiga builtin port"
depends on AMIGA
select PARPORT_NOT_PC
help
Say Y here if you need support for the parallel port hardware on
Amiga machines. This code is also available as a module (say M),
called parport_amiga. If in doubt, saying N is the safe plan.
config PARPORT_MFC3
tristate "Multiface III parallel port"
depends on ZORRO
select PARPORT_NOT_PC
help
Say Y here if you need parallel port support for the MFC3 card.
This code is also available as a module (say M), called
parport_mfc3. If in doubt, saying N is the safe plan.
config PARPORT_ATARI
tristate "Atari hardware"
depends on ATARI
select PARPORT_NOT_PC
help
Say Y here if you need support for the parallel port hardware on
Atari machines. This code is also available as a module (say M),
called parport_atari. If in doubt, saying N is the safe plan.
config PARPORT_GSC
tristate
default GSC
select PARPORT_NOT_PC
config PARPORT_SUNBPP
tristate "Sparc hardware"
depends on SBUS
select PARPORT_NOT_PC
help
This driver provides support for the bidirectional parallel port
found on many Sun machines. Note that many of the newer Ultras
actually have pc style hardware instead.
config PARPORT_AX88796
tristate "AX88796 Parallel Port"
select PARPORT_NOT_PC
help
Say Y here if you need support for the parallel port hardware on
the AX88796 network controller chip. This code is also available
as a module (say M), called parport_ax88796.
The driver is not dependent on the AX88796 network driver, and
should not interfere with the networking functions of the chip.
config PARPORT_1284
bool "IEEE 1284 transfer modes"
help
If you have a printer that supports status readback or device ID, or
want to use a device that uses enhanced parallel port transfer modes
such as EPP and ECP, say Y here to enable advanced IEEE 1284
transfer modes. Also say Y if you want device ID information to
appear in /proc/sys/dev/parport/*/autoprobe*. It is safe to say N.
config PARPORT_NOT_PC
bool
endif # PARPORT

21
drivers/parport/Makefile Normal file
View file

@ -0,0 +1,21 @@
#
# Makefile for the kernel Parallel port device drivers.
#
parport-objs := share.o ieee1284.o ieee1284_ops.o procfs.o
ifeq ($(CONFIG_PARPORT_1284),y)
parport-objs += daisy.o probe.o
endif
obj-$(CONFIG_PARPORT) += parport.o
obj-$(CONFIG_PARPORT_PC) += parport_pc.o
obj-$(CONFIG_PARPORT_SERIAL) += parport_serial.o
obj-$(CONFIG_PARPORT_PC_PCMCIA) += parport_cs.o
obj-$(CONFIG_PARPORT_AMIGA) += parport_amiga.o
obj-$(CONFIG_PARPORT_MFC3) += parport_mfc3.o
obj-$(CONFIG_PARPORT_ATARI) += parport_atari.o
obj-$(CONFIG_PARPORT_SUNBPP) += parport_sunbpp.o
obj-$(CONFIG_PARPORT_GSC) += parport_gsc.o
obj-$(CONFIG_PARPORT_AX88796) += parport_ax88796.o
obj-$(CONFIG_PARPORT_IP32) += parport_ip32.o

View file

@ -0,0 +1,20 @@
Things to be done.
0. Fix the bugs (see BUGS-parport).
1. Proper documentation.
2. A better lp.c:
a) ECP support would be nice. This can only work if both the port and
the printer support it.
b) Handle status readback automatically. IEEE1284 printers can post status
bits when they have something to say. We should read out and deal
with (maybe just log) whatever the printer wants to tell the world.
3. Support more hardware (eg m68k, Sun bpp).
4. A better PLIP (make use of bidirectional/ECP/EPP ports).
See <URL:http://people.redhat.com/twaugh/parport/>.

482
drivers/parport/daisy.c Normal file
View file

@ -0,0 +1,482 @@
/*
* IEEE 1284.3 Parallel port daisy chain and multiplexor code
*
* Copyright (C) 1999, 2000 Tim Waugh <tim@cyberelk.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* ??-12-1998: Initial implementation.
* 31-01-1999: Make port-cloning transparent.
* 13-02-1999: Move DeviceID technique from parport_probe.
* 13-03-1999: Get DeviceID from non-IEEE 1284.3 devices too.
* 22-02-2000: Count devices that are actually detected.
*
* Any part of this program may be used in documents licensed under
* the GNU Free Documentation License, Version 1.1 or any later version
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/parport.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <asm/current.h>
#include <asm/uaccess.h>
#undef DEBUG
#ifdef DEBUG
#define DPRINTK(stuff...) printk(stuff)
#else
#define DPRINTK(stuff...)
#endif
static struct daisydev {
struct daisydev *next;
struct parport *port;
int daisy;
int devnum;
} *topology = NULL;
static DEFINE_SPINLOCK(topology_lock);
static int numdevs = 0;
/* Forward-declaration of lower-level functions. */
static int mux_present(struct parport *port);
static int num_mux_ports(struct parport *port);
static int select_port(struct parport *port);
static int assign_addrs(struct parport *port);
/* Add a device to the discovered topology. */
static void add_dev(int devnum, struct parport *port, int daisy)
{
struct daisydev *newdev, **p;
newdev = kmalloc(sizeof(struct daisydev), GFP_KERNEL);
if (newdev) {
newdev->port = port;
newdev->daisy = daisy;
newdev->devnum = devnum;
spin_lock(&topology_lock);
for (p = &topology; *p && (*p)->devnum<devnum; p = &(*p)->next)
;
newdev->next = *p;
*p = newdev;
spin_unlock(&topology_lock);
}
}
/* Clone a parport (actually, make an alias). */
static struct parport *clone_parport(struct parport *real, int muxport)
{
struct parport *extra = parport_register_port(real->base,
real->irq,
real->dma,
real->ops);
if (extra) {
extra->portnum = real->portnum;
extra->physport = real;
extra->muxport = muxport;
real->slaves[muxport-1] = extra;
}
return extra;
}
/* Discover the IEEE1284.3 topology on a port -- muxes and daisy chains.
* Return value is number of devices actually detected. */
int parport_daisy_init(struct parport *port)
{
int detected = 0;
char *deviceid;
static const char *th[] = { /*0*/"th", "st", "nd", "rd", "th" };
int num_ports;
int i;
int last_try = 0;
again:
/* Because this is called before any other devices exist,
* we don't have to claim exclusive access. */
/* If mux present on normal port, need to create new
* parports for each extra port. */
if (port->muxport < 0 && mux_present(port) &&
/* don't be fooled: a mux must have 2 or 4 ports. */
((num_ports = num_mux_ports(port)) == 2 || num_ports == 4)) {
/* Leave original as port zero. */
port->muxport = 0;
printk(KERN_INFO
"%s: 1st (default) port of %d-way multiplexor\n",
port->name, num_ports);
for (i = 1; i < num_ports; i++) {
/* Clone the port. */
struct parport *extra = clone_parport(port, i);
if (!extra) {
if (signal_pending(current))
break;
schedule();
continue;
}
printk(KERN_INFO
"%s: %d%s port of %d-way multiplexor on %s\n",
extra->name, i + 1, th[i + 1], num_ports,
port->name);
/* Analyse that port too. We won't recurse
forever because of the 'port->muxport < 0'
test above. */
parport_daisy_init(extra);
}
}
if (port->muxport >= 0)
select_port(port);
parport_daisy_deselect_all(port);
detected += assign_addrs(port);
/* Count the potential legacy device at the end. */
add_dev(numdevs++, port, -1);
/* Find out the legacy device's IEEE 1284 device ID. */
deviceid = kmalloc(1024, GFP_KERNEL);
if (deviceid) {
if (parport_device_id(numdevs - 1, deviceid, 1024) > 2)
detected++;
kfree(deviceid);
}
if (!detected && !last_try) {
/* No devices were detected. Perhaps they are in some
funny state; let's try to reset them and see if
they wake up. */
parport_daisy_fini(port);
parport_write_control(port, PARPORT_CONTROL_SELECT);
udelay(50);
parport_write_control(port,
PARPORT_CONTROL_SELECT |
PARPORT_CONTROL_INIT);
udelay(50);
last_try = 1;
goto again;
}
return detected;
}
/* Forget about devices on a physical port. */
void parport_daisy_fini(struct parport *port)
{
struct daisydev **p;
spin_lock(&topology_lock);
p = &topology;
while (*p) {
struct daisydev *dev = *p;
if (dev->port != port) {
p = &dev->next;
continue;
}
*p = dev->next;
kfree(dev);
}
/* Gaps in the numbering could be handled better. How should
someone enumerate through all IEEE1284.3 devices in the
topology?. */
if (!topology) numdevs = 0;
spin_unlock(&topology_lock);
return;
}
/**
* parport_open - find a device by canonical device number
* @devnum: canonical device number
* @name: name to associate with the device
*
* This function is similar to parport_register_device(), except
* that it locates a device by its number rather than by the port
* it is attached to.
*
* All parameters except for @devnum are the same as for
* parport_register_device(). The return value is the same as
* for parport_register_device().
**/
struct pardevice *parport_open(int devnum, const char *name)
{
struct daisydev *p = topology;
struct parport *port;
struct pardevice *dev;
int daisy;
spin_lock(&topology_lock);
while (p && p->devnum != devnum)
p = p->next;
if (!p) {
spin_unlock(&topology_lock);
return NULL;
}
daisy = p->daisy;
port = parport_get_port(p->port);
spin_unlock(&topology_lock);
dev = parport_register_device(port, name, NULL, NULL, NULL, 0, NULL);
parport_put_port(port);
if (!dev)
return NULL;
dev->daisy = daisy;
/* Check that there really is a device to select. */
if (daisy >= 0) {
int selected;
parport_claim_or_block(dev);
selected = port->daisy;
parport_release(dev);
if (selected != daisy) {
/* No corresponding device. */
parport_unregister_device(dev);
return NULL;
}
}
return dev;
}
/**
* parport_close - close a device opened with parport_open()
* @dev: device to close
*
* This is to parport_open() as parport_unregister_device() is to
* parport_register_device().
**/
void parport_close(struct pardevice *dev)
{
parport_unregister_device(dev);
}
/* Send a daisy-chain-style CPP command packet. */
static int cpp_daisy(struct parport *port, int cmd)
{
unsigned char s;
parport_data_forward(port);
parport_write_data(port, 0xaa); udelay(2);
parport_write_data(port, 0x55); udelay(2);
parport_write_data(port, 0x00); udelay(2);
parport_write_data(port, 0xff); udelay(2);
s = parport_read_status(port) & (PARPORT_STATUS_BUSY
| PARPORT_STATUS_PAPEROUT
| PARPORT_STATUS_SELECT
| PARPORT_STATUS_ERROR);
if (s != (PARPORT_STATUS_BUSY
| PARPORT_STATUS_PAPEROUT
| PARPORT_STATUS_SELECT
| PARPORT_STATUS_ERROR)) {
DPRINTK(KERN_DEBUG "%s: cpp_daisy: aa5500ff(%02x)\n",
port->name, s);
return -ENXIO;
}
parport_write_data(port, 0x87); udelay(2);
s = parport_read_status(port) & (PARPORT_STATUS_BUSY
| PARPORT_STATUS_PAPEROUT
| PARPORT_STATUS_SELECT
| PARPORT_STATUS_ERROR);
if (s != (PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR)) {
DPRINTK(KERN_DEBUG "%s: cpp_daisy: aa5500ff87(%02x)\n",
port->name, s);
return -ENXIO;
}
parport_write_data(port, 0x78); udelay(2);
parport_write_data(port, cmd); udelay(2);
parport_frob_control(port,
PARPORT_CONTROL_STROBE,
PARPORT_CONTROL_STROBE);
udelay(1);
s = parport_read_status(port);
parport_frob_control(port, PARPORT_CONTROL_STROBE, 0);
udelay(1);
parport_write_data(port, 0xff); udelay(2);
return s;
}
/* Send a mux-style CPP command packet. */
static int cpp_mux(struct parport *port, int cmd)
{
unsigned char s;
int rc;
parport_data_forward(port);
parport_write_data(port, 0xaa); udelay(2);
parport_write_data(port, 0x55); udelay(2);
parport_write_data(port, 0xf0); udelay(2);
parport_write_data(port, 0x0f); udelay(2);
parport_write_data(port, 0x52); udelay(2);
parport_write_data(port, 0xad); udelay(2);
parport_write_data(port, cmd); udelay(2);
s = parport_read_status(port);
if (!(s & PARPORT_STATUS_ACK)) {
DPRINTK(KERN_DEBUG "%s: cpp_mux: aa55f00f52ad%02x(%02x)\n",
port->name, cmd, s);
return -EIO;
}
rc = (((s & PARPORT_STATUS_SELECT ? 1 : 0) << 0) |
((s & PARPORT_STATUS_PAPEROUT ? 1 : 0) << 1) |
((s & PARPORT_STATUS_BUSY ? 0 : 1) << 2) |
((s & PARPORT_STATUS_ERROR ? 0 : 1) << 3));
return rc;
}
void parport_daisy_deselect_all(struct parport *port)
{
cpp_daisy(port, 0x30);
}
int parport_daisy_select(struct parport *port, int daisy, int mode)
{
switch (mode)
{
// For these modes we should switch to EPP mode:
case IEEE1284_MODE_EPP:
case IEEE1284_MODE_EPPSL:
case IEEE1284_MODE_EPPSWE:
return !(cpp_daisy(port, 0x20 + daisy) &
PARPORT_STATUS_ERROR);
// For these modes we should switch to ECP mode:
case IEEE1284_MODE_ECP:
case IEEE1284_MODE_ECPRLE:
case IEEE1284_MODE_ECPSWE:
return !(cpp_daisy(port, 0xd0 + daisy) &
PARPORT_STATUS_ERROR);
// Nothing was told for BECP in Daisy chain specification.
// May be it's wise to use ECP?
case IEEE1284_MODE_BECP:
// Others use compat mode
case IEEE1284_MODE_NIBBLE:
case IEEE1284_MODE_BYTE:
case IEEE1284_MODE_COMPAT:
default:
return !(cpp_daisy(port, 0xe0 + daisy) &
PARPORT_STATUS_ERROR);
}
}
static int mux_present(struct parport *port)
{
return cpp_mux(port, 0x51) == 3;
}
static int num_mux_ports(struct parport *port)
{
return cpp_mux(port, 0x58);
}
static int select_port(struct parport *port)
{
int muxport = port->muxport;
return cpp_mux(port, 0x60 + muxport) == muxport;
}
static int assign_addrs(struct parport *port)
{
unsigned char s;
unsigned char daisy;
int thisdev = numdevs;
int detected;
char *deviceid;
parport_data_forward(port);
parport_write_data(port, 0xaa); udelay(2);
parport_write_data(port, 0x55); udelay(2);
parport_write_data(port, 0x00); udelay(2);
parport_write_data(port, 0xff); udelay(2);
s = parport_read_status(port) & (PARPORT_STATUS_BUSY
| PARPORT_STATUS_PAPEROUT
| PARPORT_STATUS_SELECT
| PARPORT_STATUS_ERROR);
if (s != (PARPORT_STATUS_BUSY
| PARPORT_STATUS_PAPEROUT
| PARPORT_STATUS_SELECT
| PARPORT_STATUS_ERROR)) {
DPRINTK(KERN_DEBUG "%s: assign_addrs: aa5500ff(%02x)\n",
port->name, s);
return 0;
}
parport_write_data(port, 0x87); udelay(2);
s = parport_read_status(port) & (PARPORT_STATUS_BUSY
| PARPORT_STATUS_PAPEROUT
| PARPORT_STATUS_SELECT
| PARPORT_STATUS_ERROR);
if (s != (PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR)) {
DPRINTK(KERN_DEBUG "%s: assign_addrs: aa5500ff87(%02x)\n",
port->name, s);
return 0;
}
parport_write_data(port, 0x78); udelay(2);
s = parport_read_status(port);
for (daisy = 0;
(s & (PARPORT_STATUS_PAPEROUT|PARPORT_STATUS_SELECT))
== (PARPORT_STATUS_PAPEROUT|PARPORT_STATUS_SELECT)
&& daisy < 4;
++daisy) {
parport_write_data(port, daisy);
udelay(2);
parport_frob_control(port,
PARPORT_CONTROL_STROBE,
PARPORT_CONTROL_STROBE);
udelay(1);
parport_frob_control(port, PARPORT_CONTROL_STROBE, 0);
udelay(1);
add_dev(numdevs++, port, daisy);
/* See if this device thought it was the last in the
* chain. */
if (!(s & PARPORT_STATUS_BUSY))
break;
/* We are seeing pass through status now. We see
last_dev from next device or if last_dev does not
work status lines from some non-daisy chain
device. */
s = parport_read_status(port);
}
parport_write_data(port, 0xff); udelay(2);
detected = numdevs - thisdev;
DPRINTK(KERN_DEBUG "%s: Found %d daisy-chained devices\n", port->name,
detected);
/* Ask the new devices to introduce themselves. */
deviceid = kmalloc(1024, GFP_KERNEL);
if (!deviceid) return 0;
for (daisy = 0; thisdev < numdevs; thisdev++, daisy++)
parport_device_id(thisdev, deviceid, 1024);
kfree(deviceid);
return detected;
}

817
drivers/parport/ieee1284.c Normal file
View file

@ -0,0 +1,817 @@
/*
* IEEE-1284 implementation for parport.
*
* Authors: Phil Blundell <philb@gnu.org>
* Carsten Gross <carsten@sol.wohnheim.uni-ulm.de>
* Jose Renau <renau@acm.org>
* Tim Waugh <tim@cyberelk.demon.co.uk> (largely rewritten)
*
* This file is responsible for IEEE 1284 negotiation, and for handing
* read/write requests to low-level drivers.
*
* Any part of this program may be used in documents licensed under
* the GNU Free Documentation License, Version 1.1 or any later version
* published by the Free Software Foundation.
*
* Various hacks, Fred Barnes <frmb2@ukc.ac.uk>, 04/2000
*/
#include <linux/module.h>
#include <linux/threads.h>
#include <linux/parport.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/sched.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
/* Make parport_wait_peripheral wake up.
* It will be useful to call this from an interrupt handler. */
static void parport_ieee1284_wakeup (struct parport *port)
{
up (&port->physport->ieee1284.irq);
}
static struct parport *port_from_cookie[PARPORT_MAX];
static void timeout_waiting_on_port (unsigned long cookie)
{
parport_ieee1284_wakeup (port_from_cookie[cookie % PARPORT_MAX]);
}
/**
* parport_wait_event - wait for an event on a parallel port
* @port: port to wait on
* @timeout: time to wait (in jiffies)
*
* This function waits for up to @timeout jiffies for an
* interrupt to occur on a parallel port. If the port timeout is
* set to zero, it returns immediately.
*
* If an interrupt occurs before the timeout period elapses, this
* function returns zero immediately. If it times out, it returns
* one. An error code less than zero indicates an error (most
* likely a pending signal), and the calling code should finish
* what it's doing as soon as it can.
*/
int parport_wait_event (struct parport *port, signed long timeout)
{
int ret;
struct timer_list timer;
if (!port->physport->cad->timeout)
/* Zero timeout is special, and we can't down() the
semaphore. */
return 1;
init_timer_on_stack(&timer);
timer.expires = jiffies + timeout;
timer.function = timeout_waiting_on_port;
port_from_cookie[port->number % PARPORT_MAX] = port;
timer.data = port->number;
add_timer (&timer);
ret = down_interruptible (&port->physport->ieee1284.irq);
if (!del_timer_sync(&timer) && !ret)
/* Timed out. */
ret = 1;
destroy_timer_on_stack(&timer);
return ret;
}
/**
* parport_poll_peripheral - poll status lines
* @port: port to watch
* @mask: status lines to watch
* @result: desired values of chosen status lines
* @usec: timeout
*
* This function busy-waits until the masked status lines have
* the desired values, or until the timeout period elapses. The
* @mask and @result parameters are bitmasks, with the bits
* defined by the constants in parport.h: %PARPORT_STATUS_BUSY,
* and so on.
*
* This function does not call schedule(); instead it busy-waits
* using udelay(). It currently has a resolution of 5usec.
*
* If the status lines take on the desired values before the
* timeout period elapses, parport_poll_peripheral() returns zero
* immediately. A return value greater than zero indicates
* a timeout. An error code (less than zero) indicates an error,
* most likely a signal that arrived, and the caller should
* finish what it is doing as soon as possible.
*/
int parport_poll_peripheral(struct parport *port,
unsigned char mask,
unsigned char result,
int usec)
{
/* Zero return code is success, >0 is timeout. */
int count = usec / 5 + 2;
int i;
unsigned char status;
for (i = 0; i < count; i++) {
status = parport_read_status (port);
if ((status & mask) == result)
return 0;
if (signal_pending (current))
return -EINTR;
if (need_resched())
break;
if (i >= 2)
udelay (5);
}
return 1;
}
/**
* parport_wait_peripheral - wait for status lines to change in 35ms
* @port: port to watch
* @mask: status lines to watch
* @result: desired values of chosen status lines
*
* This function waits until the masked status lines have the
* desired values, or until 35ms have elapsed (see IEEE 1284-1994
* page 24 to 25 for why this value in particular is hardcoded).
* The @mask and @result parameters are bitmasks, with the bits
* defined by the constants in parport.h: %PARPORT_STATUS_BUSY,
* and so on.
*
* The port is polled quickly to start off with, in anticipation
* of a fast response from the peripheral. This fast polling
* time is configurable (using /proc), and defaults to 500usec.
* If the timeout for this port (see parport_set_timeout()) is
* zero, the fast polling time is 35ms, and this function does
* not call schedule().
*
* If the timeout for this port is non-zero, after the fast
* polling fails it uses parport_wait_event() to wait for up to
* 10ms, waking up if an interrupt occurs.
*/
int parport_wait_peripheral(struct parport *port,
unsigned char mask,
unsigned char result)
{
int ret;
int usec;
unsigned long deadline;
unsigned char status;
usec = port->physport->spintime; /* usecs of fast polling */
if (!port->physport->cad->timeout)
/* A zero timeout is "special": busy wait for the
entire 35ms. */
usec = 35000;
/* Fast polling.
*
* This should be adjustable.
* How about making a note (in the device structure) of how long
* it takes, so we know for next time?
*/
ret = parport_poll_peripheral (port, mask, result, usec);
if (ret != 1)
return ret;
if (!port->physport->cad->timeout)
/* We may be in an interrupt handler, so we can't poll
* slowly anyway. */
return 1;
/* 40ms of slow polling. */
deadline = jiffies + msecs_to_jiffies(40);
while (time_before (jiffies, deadline)) {
if (signal_pending (current))
return -EINTR;
/* Wait for 10ms (or until an interrupt occurs if
* the handler is set) */
if ((ret = parport_wait_event (port, msecs_to_jiffies(10))) < 0)
return ret;
status = parport_read_status (port);
if ((status & mask) == result)
return 0;
if (!ret) {
/* parport_wait_event didn't time out, but the
* peripheral wasn't actually ready either.
* Wait for another 10ms. */
schedule_timeout_interruptible(msecs_to_jiffies(10));
}
}
return 1;
}
#ifdef CONFIG_PARPORT_1284
/* Terminate a negotiated mode. */
static void parport_ieee1284_terminate (struct parport *port)
{
int r;
port = port->physport;
/* EPP terminates differently. */
switch (port->ieee1284.mode) {
case IEEE1284_MODE_EPP:
case IEEE1284_MODE_EPPSL:
case IEEE1284_MODE_EPPSWE:
/* Terminate from EPP mode. */
/* Event 68: Set nInit low */
parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
udelay (50);
/* Event 69: Set nInit high, nSelectIn low */
parport_frob_control (port,
PARPORT_CONTROL_SELECT
| PARPORT_CONTROL_INIT,
PARPORT_CONTROL_SELECT
| PARPORT_CONTROL_INIT);
break;
case IEEE1284_MODE_ECP:
case IEEE1284_MODE_ECPRLE:
case IEEE1284_MODE_ECPSWE:
/* In ECP we can only terminate from fwd idle phase. */
if (port->ieee1284.phase != IEEE1284_PH_FWD_IDLE) {
/* 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 */
r = parport_wait_peripheral (port,
PARPORT_STATUS_PAPEROUT,
PARPORT_STATUS_PAPEROUT);
if (r)
DPRINTK (KERN_INFO "%s: Timeout at event 49\n",
port->name);
parport_data_forward (port);
DPRINTK (KERN_DEBUG "%s: ECP direction: forward\n",
port->name);
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
}
/* fall-though.. */
default:
/* Terminate from all other modes. */
/* Event 22: Set nSelectIn low, nAutoFd high */
parport_frob_control (port,
PARPORT_CONTROL_SELECT
| PARPORT_CONTROL_AUTOFD,
PARPORT_CONTROL_SELECT);
/* Event 24: nAck goes low */
r = parport_wait_peripheral (port, PARPORT_STATUS_ACK, 0);
if (r)
DPRINTK (KERN_INFO "%s: Timeout at event 24\n",
port->name);
/* Event 25: Set nAutoFd low */
parport_frob_control (port,
PARPORT_CONTROL_AUTOFD,
PARPORT_CONTROL_AUTOFD);
/* Event 27: nAck goes high */
r = parport_wait_peripheral (port,
PARPORT_STATUS_ACK,
PARPORT_STATUS_ACK);
if (r)
DPRINTK (KERN_INFO "%s: Timeout at event 27\n",
port->name);
/* Event 29: Set nAutoFd high */
parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
}
port->ieee1284.mode = IEEE1284_MODE_COMPAT;
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
DPRINTK (KERN_DEBUG "%s: In compatibility (forward idle) mode\n",
port->name);
}
#endif /* IEEE1284 support */
/**
* parport_negotiate - negotiate an IEEE 1284 mode
* @port: port to use
* @mode: mode to negotiate to
*
* Use this to negotiate to a particular IEEE 1284 transfer mode.
* The @mode parameter should be one of the constants in
* parport.h starting %IEEE1284_MODE_xxx.
*
* The return value is 0 if the peripheral has accepted the
* negotiation to the mode specified, -1 if the peripheral is not
* IEEE 1284 compliant (or not present), or 1 if the peripheral
* has rejected the negotiation.
*/
int parport_negotiate (struct parport *port, int mode)
{
#ifndef CONFIG_PARPORT_1284
if (mode == IEEE1284_MODE_COMPAT)
return 0;
printk (KERN_ERR "parport: IEEE1284 not supported in this kernel\n");
return -1;
#else
int m = mode & ~IEEE1284_ADDR;
int r;
unsigned char xflag;
port = port->physport;
/* Is there anything to do? */
if (port->ieee1284.mode == mode)
return 0;
/* Is the difference just an address-or-not bit? */
if ((port->ieee1284.mode & ~IEEE1284_ADDR) == (mode & ~IEEE1284_ADDR)){
port->ieee1284.mode = mode;
return 0;
}
/* Go to compatibility forward idle mode */
if (port->ieee1284.mode != IEEE1284_MODE_COMPAT)
parport_ieee1284_terminate (port);
if (mode == IEEE1284_MODE_COMPAT)
/* Compatibility mode: no negotiation. */
return 0;
switch (mode) {
case IEEE1284_MODE_ECPSWE:
m = IEEE1284_MODE_ECP;
break;
case IEEE1284_MODE_EPPSL:
case IEEE1284_MODE_EPPSWE:
m = IEEE1284_MODE_EPP;
break;
case IEEE1284_MODE_BECP:
return -ENOSYS; /* FIXME (implement BECP) */
}
if (mode & IEEE1284_EXT_LINK)
m = 1<<7; /* request extensibility link */
port->ieee1284.phase = IEEE1284_PH_NEGOTIATION;
/* Start off with nStrobe and nAutoFd high, and nSelectIn low */
parport_frob_control (port,
PARPORT_CONTROL_STROBE
| PARPORT_CONTROL_AUTOFD
| PARPORT_CONTROL_SELECT,
PARPORT_CONTROL_SELECT);
udelay(1);
/* Event 0: Set data */
parport_data_forward (port);
parport_write_data (port, m);
udelay (400); /* Shouldn't need to wait this long. */
/* Event 1: Set nSelectIn high, nAutoFd low */
parport_frob_control (port,
PARPORT_CONTROL_SELECT
| PARPORT_CONTROL_AUTOFD,
PARPORT_CONTROL_AUTOFD);
/* Event 2: PError, Select, nFault go high, nAck goes low */
if (parport_wait_peripheral (port,
PARPORT_STATUS_ERROR
| PARPORT_STATUS_SELECT
| PARPORT_STATUS_PAPEROUT
| PARPORT_STATUS_ACK,
PARPORT_STATUS_ERROR
| PARPORT_STATUS_SELECT
| PARPORT_STATUS_PAPEROUT)) {
/* Timeout */
parport_frob_control (port,
PARPORT_CONTROL_SELECT
| PARPORT_CONTROL_AUTOFD,
PARPORT_CONTROL_SELECT);
DPRINTK (KERN_DEBUG
"%s: Peripheral not IEEE1284 compliant (0x%02X)\n",
port->name, parport_read_status (port));
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
return -1; /* Not IEEE1284 compliant */
}
/* Event 3: Set nStrobe low */
parport_frob_control (port,
PARPORT_CONTROL_STROBE,
PARPORT_CONTROL_STROBE);
/* Event 4: Set nStrobe and nAutoFd high */
udelay (5);
parport_frob_control (port,
PARPORT_CONTROL_STROBE
| PARPORT_CONTROL_AUTOFD,
0);
/* Event 6: nAck goes high */
if (parport_wait_peripheral (port,
PARPORT_STATUS_ACK,
PARPORT_STATUS_ACK)) {
/* This shouldn't really happen with a compliant device. */
DPRINTK (KERN_DEBUG
"%s: Mode 0x%02x not supported? (0x%02x)\n",
port->name, mode, port->ops->read_status (port));
parport_ieee1284_terminate (port);
return 1;
}
xflag = parport_read_status (port) & PARPORT_STATUS_SELECT;
/* xflag should be high for all modes other than nibble (0). */
if (mode && !xflag) {
/* Mode not supported. */
DPRINTK (KERN_DEBUG "%s: Mode 0x%02x rejected by peripheral\n",
port->name, mode);
parport_ieee1284_terminate (port);
return 1;
}
/* More to do if we've requested extensibility link. */
if (mode & IEEE1284_EXT_LINK) {
m = mode & 0x7f;
udelay (1);
parport_write_data (port, m);
udelay (1);
/* Event 51: Set nStrobe low */
parport_frob_control (port,
PARPORT_CONTROL_STROBE,
PARPORT_CONTROL_STROBE);
/* Event 52: nAck goes low */
if (parport_wait_peripheral (port, PARPORT_STATUS_ACK, 0)) {
/* This peripheral is _very_ slow. */
DPRINTK (KERN_DEBUG
"%s: Event 52 didn't happen\n",
port->name);
parport_ieee1284_terminate (port);
return 1;
}
/* Event 53: Set nStrobe high */
parport_frob_control (port,
PARPORT_CONTROL_STROBE,
0);
/* Event 55: nAck goes high */
if (parport_wait_peripheral (port,
PARPORT_STATUS_ACK,
PARPORT_STATUS_ACK)) {
/* This shouldn't really happen with a compliant
* device. */
DPRINTK (KERN_DEBUG
"%s: Mode 0x%02x not supported? (0x%02x)\n",
port->name, mode,
port->ops->read_status (port));
parport_ieee1284_terminate (port);
return 1;
}
/* Event 54: Peripheral sets XFlag to reflect support */
xflag = parport_read_status (port) & PARPORT_STATUS_SELECT;
/* xflag should be high. */
if (!xflag) {
/* Extended mode not supported. */
DPRINTK (KERN_DEBUG "%s: Extended mode 0x%02x not "
"supported\n", port->name, mode);
parport_ieee1284_terminate (port);
return 1;
}
/* Any further setup is left to the caller. */
}
/* Mode is supported */
DPRINTK (KERN_DEBUG "%s: In mode 0x%02x\n", port->name, mode);
port->ieee1284.mode = mode;
/* But ECP is special */
if (!(mode & IEEE1284_EXT_LINK) && (m & IEEE1284_MODE_ECP)) {
port->ieee1284.phase = IEEE1284_PH_ECP_SETUP;
/* Event 30: Set nAutoFd low */
parport_frob_control (port,
PARPORT_CONTROL_AUTOFD,
PARPORT_CONTROL_AUTOFD);
/* Event 31: PError goes high. */
r = parport_wait_peripheral (port,
PARPORT_STATUS_PAPEROUT,
PARPORT_STATUS_PAPEROUT);
if (r) {
DPRINTK (KERN_INFO "%s: Timeout at event 31\n",
port->name);
}
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
DPRINTK (KERN_DEBUG "%s: ECP direction: forward\n",
port->name);
} else switch (mode) {
case IEEE1284_MODE_NIBBLE:
case IEEE1284_MODE_BYTE:
port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
break;
default:
port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
}
return 0;
#endif /* IEEE1284 support */
}
/* Acknowledge that the peripheral has data available.
* Events 18-20, in order to get from Reverse Idle phase
* to Host Busy Data Available.
* This will most likely be called from an interrupt.
* Returns zero if data was available.
*/
#ifdef CONFIG_PARPORT_1284
static int parport_ieee1284_ack_data_avail (struct parport *port)
{
if (parport_read_status (port) & PARPORT_STATUS_ERROR)
/* Event 18 didn't happen. */
return -1;
/* Event 20: nAutoFd goes high. */
port->ops->frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
port->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL;
return 0;
}
#endif /* IEEE1284 support */
/* Handle an interrupt. */
void parport_ieee1284_interrupt (void *handle)
{
struct parport *port = handle;
parport_ieee1284_wakeup (port);
#ifdef CONFIG_PARPORT_1284
if (port->ieee1284.phase == IEEE1284_PH_REV_IDLE) {
/* An interrupt in this phase means that data
* is now available. */
DPRINTK (KERN_DEBUG "%s: Data available\n", port->name);
parport_ieee1284_ack_data_avail (port);
}
#endif /* IEEE1284 support */
}
/**
* parport_write - write a block of data to a parallel port
* @port: port to write to
* @buffer: data buffer (in kernel space)
* @len: number of bytes of data to transfer
*
* This will write up to @len bytes of @buffer to the port
* specified, using the IEEE 1284 transfer mode most recently
* negotiated to (using parport_negotiate()), as long as that
* mode supports forward transfers (host to peripheral).
*
* It is the caller's responsibility to ensure that the first
* @len bytes of @buffer are valid.
*
* This function returns the number of bytes transferred (if zero
* or positive), or else an error code.
*/
ssize_t parport_write (struct parport *port, const void *buffer, size_t len)
{
#ifndef CONFIG_PARPORT_1284
return port->ops->compat_write_data (port, buffer, len, 0);
#else
ssize_t retval;
int mode = port->ieee1284.mode;
int addr = mode & IEEE1284_ADDR;
size_t (*fn) (struct parport *, const void *, size_t, int);
/* Ignore the device-ID-request bit and the address bit. */
mode &= ~(IEEE1284_DEVICEID | IEEE1284_ADDR);
/* Use the mode we're in. */
switch (mode) {
case IEEE1284_MODE_NIBBLE:
case IEEE1284_MODE_BYTE:
parport_negotiate (port, IEEE1284_MODE_COMPAT);
case IEEE1284_MODE_COMPAT:
DPRINTK (KERN_DEBUG "%s: Using compatibility mode\n",
port->name);
fn = port->ops->compat_write_data;
break;
case IEEE1284_MODE_EPP:
DPRINTK (KERN_DEBUG "%s: Using EPP mode\n", port->name);
if (addr) {
fn = port->ops->epp_write_addr;
} else {
fn = port->ops->epp_write_data;
}
break;
case IEEE1284_MODE_EPPSWE:
DPRINTK (KERN_DEBUG "%s: Using software-emulated EPP mode\n",
port->name);
if (addr) {
fn = parport_ieee1284_epp_write_addr;
} else {
fn = parport_ieee1284_epp_write_data;
}
break;
case IEEE1284_MODE_ECP:
case IEEE1284_MODE_ECPRLE:
DPRINTK (KERN_DEBUG "%s: Using ECP mode\n", port->name);
if (addr) {
fn = port->ops->ecp_write_addr;
} else {
fn = port->ops->ecp_write_data;
}
break;
case IEEE1284_MODE_ECPSWE:
DPRINTK (KERN_DEBUG "%s: Using software-emulated ECP mode\n",
port->name);
/* The caller has specified that it must be emulated,
* even if we have ECP hardware! */
if (addr) {
fn = parport_ieee1284_ecp_write_addr;
} else {
fn = parport_ieee1284_ecp_write_data;
}
break;
default:
DPRINTK (KERN_DEBUG "%s: Unknown mode 0x%02x\n", port->name,
port->ieee1284.mode);
return -ENOSYS;
}
retval = (*fn) (port, buffer, len, 0);
DPRINTK (KERN_DEBUG "%s: wrote %d/%d bytes\n", port->name, retval, len);
return retval;
#endif /* IEEE1284 support */
}
/**
* parport_read - read a block of data from a parallel port
* @port: port to read from
* @buffer: data buffer (in kernel space)
* @len: number of bytes of data to transfer
*
* This will read up to @len bytes of @buffer to the port
* specified, using the IEEE 1284 transfer mode most recently
* negotiated to (using parport_negotiate()), as long as that
* mode supports reverse transfers (peripheral to host).
*
* It is the caller's responsibility to ensure that the first
* @len bytes of @buffer are available to write to.
*
* This function returns the number of bytes transferred (if zero
* or positive), or else an error code.
*/
ssize_t parport_read (struct parport *port, void *buffer, size_t len)
{
#ifndef CONFIG_PARPORT_1284
printk (KERN_ERR "parport: IEEE1284 not supported in this kernel\n");
return -ENODEV;
#else
int mode = port->physport->ieee1284.mode;
int addr = mode & IEEE1284_ADDR;
size_t (*fn) (struct parport *, void *, size_t, int);
/* Ignore the device-ID-request bit and the address bit. */
mode &= ~(IEEE1284_DEVICEID | IEEE1284_ADDR);
/* Use the mode we're in. */
switch (mode) {
case IEEE1284_MODE_COMPAT:
/* if we can tri-state use BYTE mode instead of NIBBLE mode,
* if that fails, revert to NIBBLE mode -- ought to store somewhere
* the device's ability to do BYTE mode reverse transfers, so we don't
* end up needlessly calling negotiate(BYTE) repeately.. (fb)
*/
if ((port->physport->modes & PARPORT_MODE_TRISTATE) &&
!parport_negotiate (port, IEEE1284_MODE_BYTE)) {
/* got into BYTE mode OK */
DPRINTK (KERN_DEBUG "%s: Using byte mode\n", port->name);
fn = port->ops->byte_read_data;
break;
}
if (parport_negotiate (port, IEEE1284_MODE_NIBBLE)) {
return -EIO;
}
/* fall through to NIBBLE */
case IEEE1284_MODE_NIBBLE:
DPRINTK (KERN_DEBUG "%s: Using nibble mode\n", port->name);
fn = port->ops->nibble_read_data;
break;
case IEEE1284_MODE_BYTE:
DPRINTK (KERN_DEBUG "%s: Using byte mode\n", port->name);
fn = port->ops->byte_read_data;
break;
case IEEE1284_MODE_EPP:
DPRINTK (KERN_DEBUG "%s: Using EPP mode\n", port->name);
if (addr) {
fn = port->ops->epp_read_addr;
} else {
fn = port->ops->epp_read_data;
}
break;
case IEEE1284_MODE_EPPSWE:
DPRINTK (KERN_DEBUG "%s: Using software-emulated EPP mode\n",
port->name);
if (addr) {
fn = parport_ieee1284_epp_read_addr;
} else {
fn = parport_ieee1284_epp_read_data;
}
break;
case IEEE1284_MODE_ECP:
case IEEE1284_MODE_ECPRLE:
DPRINTK (KERN_DEBUG "%s: Using ECP mode\n", port->name);
fn = port->ops->ecp_read_data;
break;
case IEEE1284_MODE_ECPSWE:
DPRINTK (KERN_DEBUG "%s: Using software-emulated ECP mode\n",
port->name);
fn = parport_ieee1284_ecp_read_data;
break;
default:
DPRINTK (KERN_DEBUG "%s: Unknown mode 0x%02x\n", port->name,
port->physport->ieee1284.mode);
return -ENOSYS;
}
return (*fn) (port, buffer, len, 0);
#endif /* IEEE1284 support */
}
/**
* parport_set_timeout - set the inactivity timeout for a device
* @dev: device on a port
* @inactivity: inactivity timeout (in jiffies)
*
* This sets the inactivity timeout for a particular device on a
* port. This affects functions like parport_wait_peripheral().
* The special value 0 means not to call schedule() while dealing
* with this device.
*
* The return value is the previous inactivity timeout.
*
* Any callers of parport_wait_event() for this device are woken
* up.
*/
long parport_set_timeout (struct pardevice *dev, long inactivity)
{
long int old = dev->timeout;
dev->timeout = inactivity;
if (dev->port->physport->cad == dev)
parport_ieee1284_wakeup (dev->port);
return old;
}
/* Exported symbols for modules. */
EXPORT_SYMBOL(parport_negotiate);
EXPORT_SYMBOL(parport_write);
EXPORT_SYMBOL(parport_read);
EXPORT_SYMBOL(parport_wait_peripheral);
EXPORT_SYMBOL(parport_wait_event);
EXPORT_SYMBOL(parport_set_timeout);
EXPORT_SYMBOL(parport_ieee1284_interrupt);

View file

@ -0,0 +1,914 @@
/* 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,
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;
}
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);

View file

@ -0,0 +1,20 @@
#ifndef _MULTIFACE_H_
#define _MULTIFACE_H_
/*
* Defines for SerialMaster, Multiface Card II and Multiface Card III
* The addresses given below are offsets to the board base address
*
* 6.11.95 Joerg Dorchain (dorchain@mpi-sb.mpg.de)
*
*/
#define PIA_REG_PADWIDTH 255
#define DUARTBASE 0x0000
#define PITBASE 0x0100
#define ROMBASE 0x0200
#define PIABASE 0x4000
#endif

View file

@ -0,0 +1,252 @@
/* Low-level parallel port routines for the Amiga built-in port
*
* Author: Joerg Dorchain <joerg@dorchain.net>
*
* This is a complete rewrite of the code, but based heaviy upon the old
* lp_intern. code.
*
* The built-in Amiga parallel port provides one port at a fixed address
* with 8 bidirectional data lines (D0 - D7) and 3 bidirectional status
* lines (BUSY, POUT, SEL), 1 output control line /STROBE (raised automatically
* in hardware when the data register is accessed), and 1 input control line
* /ACK, able to cause an interrupt, but both not directly settable by
* software.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/parport.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <asm/setup.h>
#include <asm/amigahw.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/amigaints.h>
#undef DEBUG
#ifdef DEBUG
#define DPRINTK printk
#else
#define DPRINTK(x...) do { } while (0)
#endif
static void amiga_write_data(struct parport *p, unsigned char data)
{
DPRINTK(KERN_DEBUG "write_data %c\n",data);
/* Triggers also /STROBE. This behavior cannot be changed */
ciaa.prb = data;
mb();
}
static unsigned char amiga_read_data(struct parport *p)
{
/* Triggers also /STROBE. This behavior cannot be changed */
return ciaa.prb;
}
static unsigned char control_amiga_to_pc(unsigned char control)
{
return PARPORT_CONTROL_SELECT |
PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_STROBE;
/* fake value: interrupt enable, select in, no reset,
no autolf, no strobe - seems to be closest the wiring diagram */
}
static void amiga_write_control(struct parport *p, unsigned char control)
{
DPRINTK(KERN_DEBUG "write_control %02x\n",control);
/* No implementation possible */
}
static unsigned char amiga_read_control( struct parport *p)
{
DPRINTK(KERN_DEBUG "read_control \n");
return control_amiga_to_pc(0);
}
static unsigned char amiga_frob_control( struct parport *p, unsigned char mask, unsigned char val)
{
unsigned char old;
DPRINTK(KERN_DEBUG "frob_control mask %02x, value %02x\n",mask,val);
old = amiga_read_control(p);
amiga_write_control(p, (old & ~mask) ^ val);
return old;
}
static unsigned char status_amiga_to_pc(unsigned char status)
{
unsigned char ret = PARPORT_STATUS_BUSY | PARPORT_STATUS_ACK | PARPORT_STATUS_ERROR;
if (status & 1) /* Busy */
ret &= ~PARPORT_STATUS_BUSY;
if (status & 2) /* PaperOut */
ret |= PARPORT_STATUS_PAPEROUT;
if (status & 4) /* Selected */
ret |= PARPORT_STATUS_SELECT;
/* the rest is not connected or handled autonomously in hardware */
return ret;
}
static unsigned char amiga_read_status(struct parport *p)
{
unsigned char status;
status = status_amiga_to_pc(ciab.pra & 7);
DPRINTK(KERN_DEBUG "read_status %02x\n", status);
return status;
}
static void amiga_enable_irq(struct parport *p)
{
enable_irq(IRQ_AMIGA_CIAA_FLG);
}
static void amiga_disable_irq(struct parport *p)
{
disable_irq(IRQ_AMIGA_CIAA_FLG);
}
static void amiga_data_forward(struct parport *p)
{
DPRINTK(KERN_DEBUG "forward\n");
ciaa.ddrb = 0xff; /* all pins output */
mb();
}
static void amiga_data_reverse(struct parport *p)
{
DPRINTK(KERN_DEBUG "reverse\n");
ciaa.ddrb = 0; /* all pins input */
mb();
}
static void amiga_init_state(struct pardevice *dev, struct parport_state *s)
{
s->u.amiga.data = 0;
s->u.amiga.datadir = 255;
s->u.amiga.status = 0;
s->u.amiga.statusdir = 0;
}
static void amiga_save_state(struct parport *p, struct parport_state *s)
{
mb();
s->u.amiga.data = ciaa.prb;
s->u.amiga.datadir = ciaa.ddrb;
s->u.amiga.status = ciab.pra & 7;
s->u.amiga.statusdir = ciab.ddra & 7;
mb();
}
static void amiga_restore_state(struct parport *p, struct parport_state *s)
{
mb();
ciaa.prb = s->u.amiga.data;
ciaa.ddrb = s->u.amiga.datadir;
ciab.pra |= (ciab.pra & 0xf8) | s->u.amiga.status;
ciab.ddra |= (ciab.ddra & 0xf8) | s->u.amiga.statusdir;
mb();
}
static struct parport_operations pp_amiga_ops = {
.write_data = amiga_write_data,
.read_data = amiga_read_data,
.write_control = amiga_write_control,
.read_control = amiga_read_control,
.frob_control = amiga_frob_control,
.read_status = amiga_read_status,
.enable_irq = amiga_enable_irq,
.disable_irq = amiga_disable_irq,
.data_forward = amiga_data_forward,
.data_reverse = amiga_data_reverse,
.init_state = amiga_init_state,
.save_state = amiga_save_state,
.restore_state = amiga_restore_state,
.epp_write_data = parport_ieee1284_epp_write_data,
.epp_read_data = parport_ieee1284_epp_read_data,
.epp_write_addr = parport_ieee1284_epp_write_addr,
.epp_read_addr = parport_ieee1284_epp_read_addr,
.ecp_write_data = parport_ieee1284_ecp_write_data,
.ecp_read_data = parport_ieee1284_ecp_read_data,
.ecp_write_addr = parport_ieee1284_ecp_write_addr,
.compat_write_data = parport_ieee1284_write_compat,
.nibble_read_data = parport_ieee1284_read_nibble,
.byte_read_data = parport_ieee1284_read_byte,
.owner = THIS_MODULE,
};
/* ----------- Initialisation code --------------------------------- */
static int __init amiga_parallel_probe(struct platform_device *pdev)
{
struct parport *p;
int err;
ciaa.ddrb = 0xff;
ciab.ddra &= 0xf8;
mb();
p = parport_register_port((unsigned long)&ciaa.prb, IRQ_AMIGA_CIAA_FLG,
PARPORT_DMA_NONE, &pp_amiga_ops);
if (!p)
return -EBUSY;
err = request_irq(IRQ_AMIGA_CIAA_FLG, parport_irq_handler, 0, p->name,
p);
if (err)
goto out_irq;
printk(KERN_INFO "%s: Amiga built-in port using irq\n", p->name);
/* XXX: set operating mode */
parport_announce_port(p);
platform_set_drvdata(pdev, p);
return 0;
out_irq:
parport_put_port(p);
return err;
}
static int __exit amiga_parallel_remove(struct platform_device *pdev)
{
struct parport *port = platform_get_drvdata(pdev);
parport_remove_port(port);
if (port->irq != PARPORT_IRQ_NONE)
free_irq(IRQ_AMIGA_CIAA_FLG, port);
parport_put_port(port);
return 0;
}
static struct platform_driver amiga_parallel_driver = {
.remove = __exit_p(amiga_parallel_remove),
.driver = {
.name = "amiga-parallel",
.owner = THIS_MODULE,
},
};
module_platform_driver_probe(amiga_parallel_driver, amiga_parallel_probe);
MODULE_AUTHOR("Joerg Dorchain <joerg@dorchain.net>");
MODULE_DESCRIPTION("Parport Driver for Amiga builtin Port");
MODULE_SUPPORTED_DEVICE("Amiga builtin Parallel Port");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:amiga-parallel");

View file

@ -0,0 +1,224 @@
/* Low-level parallel port routines for the Atari builtin port
*
* Author: Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
*
* Based on parport_amiga.c.
*
* The built-in Atari parallel port provides one port at a fixed address
* with 8 output data lines (D0 - D7), 1 output control line (STROBE)
* and 1 input status line (BUSY) able to cause an interrupt.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/parport.h>
#include <linux/interrupt.h>
#include <asm/setup.h>
#include <asm/atarihw.h>
#include <asm/irq.h>
#include <asm/atariints.h>
static struct parport *this_port = NULL;
static unsigned char
parport_atari_read_data(struct parport *p)
{
unsigned long flags;
unsigned char data;
local_irq_save(flags);
sound_ym.rd_data_reg_sel = 15;
data = sound_ym.rd_data_reg_sel;
local_irq_restore(flags);
return data;
}
static void
parport_atari_write_data(struct parport *p, unsigned char data)
{
unsigned long flags;
local_irq_save(flags);
sound_ym.rd_data_reg_sel = 15;
sound_ym.wd_data = data;
local_irq_restore(flags);
}
static unsigned char
parport_atari_read_control(struct parport *p)
{
unsigned long flags;
unsigned char control = 0;
local_irq_save(flags);
sound_ym.rd_data_reg_sel = 14;
if (!(sound_ym.rd_data_reg_sel & (1 << 5)))
control = PARPORT_CONTROL_STROBE;
local_irq_restore(flags);
return control;
}
static void
parport_atari_write_control(struct parport *p, unsigned char control)
{
unsigned long flags;
local_irq_save(flags);
sound_ym.rd_data_reg_sel = 14;
if (control & PARPORT_CONTROL_STROBE)
sound_ym.wd_data = sound_ym.rd_data_reg_sel & ~(1 << 5);
else
sound_ym.wd_data = sound_ym.rd_data_reg_sel | (1 << 5);
local_irq_restore(flags);
}
static unsigned char
parport_atari_frob_control(struct parport *p, unsigned char mask,
unsigned char val)
{
unsigned char old = parport_atari_read_control(p);
parport_atari_write_control(p, (old & ~mask) ^ val);
return old;
}
static unsigned char
parport_atari_read_status(struct parport *p)
{
return ((st_mfp.par_dt_reg & 1 ? 0 : PARPORT_STATUS_BUSY) |
PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR);
}
static void
parport_atari_init_state(struct pardevice *d, struct parport_state *s)
{
}
static void
parport_atari_save_state(struct parport *p, struct parport_state *s)
{
}
static void
parport_atari_restore_state(struct parport *p, struct parport_state *s)
{
}
static void
parport_atari_enable_irq(struct parport *p)
{
enable_irq(IRQ_MFP_BUSY);
}
static void
parport_atari_disable_irq(struct parport *p)
{
disable_irq(IRQ_MFP_BUSY);
}
static void
parport_atari_data_forward(struct parport *p)
{
unsigned long flags;
local_irq_save(flags);
/* Soundchip port B as output. */
sound_ym.rd_data_reg_sel = 7;
sound_ym.wd_data = sound_ym.rd_data_reg_sel | 0x40;
local_irq_restore(flags);
}
static void
parport_atari_data_reverse(struct parport *p)
{
}
static struct parport_operations parport_atari_ops = {
.write_data = parport_atari_write_data,
.read_data = parport_atari_read_data,
.write_control = parport_atari_write_control,
.read_control = parport_atari_read_control,
.frob_control = parport_atari_frob_control,
.read_status = parport_atari_read_status,
.enable_irq = parport_atari_enable_irq,
.disable_irq = parport_atari_disable_irq,
.data_forward = parport_atari_data_forward,
.data_reverse = parport_atari_data_reverse,
.init_state = parport_atari_init_state,
.save_state = parport_atari_save_state,
.restore_state = parport_atari_restore_state,
.epp_write_data = parport_ieee1284_epp_write_data,
.epp_read_data = parport_ieee1284_epp_read_data,
.epp_write_addr = parport_ieee1284_epp_write_addr,
.epp_read_addr = parport_ieee1284_epp_read_addr,
.ecp_write_data = parport_ieee1284_ecp_write_data,
.ecp_read_data = parport_ieee1284_ecp_read_data,
.ecp_write_addr = parport_ieee1284_ecp_write_addr,
.compat_write_data = parport_ieee1284_write_compat,
.nibble_read_data = parport_ieee1284_read_nibble,
.byte_read_data = parport_ieee1284_read_byte,
.owner = THIS_MODULE,
};
static int __init parport_atari_init(void)
{
struct parport *p;
unsigned long flags;
if (MACH_IS_ATARI) {
local_irq_save(flags);
/* Soundchip port A/B as output. */
sound_ym.rd_data_reg_sel = 7;
sound_ym.wd_data = (sound_ym.rd_data_reg_sel & 0x3f) | 0xc0;
/* STROBE high. */
sound_ym.rd_data_reg_sel = 14;
sound_ym.wd_data = sound_ym.rd_data_reg_sel | (1 << 5);
local_irq_restore(flags);
/* MFP port I0 as input. */
st_mfp.data_dir &= ~1;
/* MFP port I0 interrupt on high->low edge. */
st_mfp.active_edge &= ~1;
p = parport_register_port((unsigned long)&sound_ym.wd_data,
IRQ_MFP_BUSY, PARPORT_DMA_NONE,
&parport_atari_ops);
if (!p)
return -ENODEV;
if (request_irq(IRQ_MFP_BUSY, parport_irq_handler,
IRQ_TYPE_SLOW, p->name, p)) {
parport_put_port (p);
return -ENODEV;
}
this_port = p;
printk(KERN_INFO "%s: Atari built-in port using irq\n", p->name);
parport_announce_port (p);
return 0;
}
return -ENODEV;
}
static void __exit parport_atari_exit(void)
{
parport_remove_port(this_port);
if (this_port->irq != PARPORT_IRQ_NONE)
free_irq(IRQ_MFP_BUSY, this_port);
parport_put_port(this_port);
}
MODULE_AUTHOR("Andreas Schwab");
MODULE_DESCRIPTION("Parport Driver for Atari builtin Port");
MODULE_SUPPORTED_DEVICE("Atari builtin Parallel Port");
MODULE_LICENSE("GPL");
module_init(parport_atari_init)
module_exit(parport_atari_exit)

View file

@ -0,0 +1,427 @@
/* linux/drivers/parport/parport_ax88796.c
*
* (c) 2005,2006 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/parport.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/irq.h>
#define AX_SPR_BUSY (1<<7)
#define AX_SPR_ACK (1<<6)
#define AX_SPR_PE (1<<5)
#define AX_SPR_SLCT (1<<4)
#define AX_SPR_ERR (1<<3)
#define AX_CPR_nDOE (1<<5)
#define AX_CPR_SLCTIN (1<<3)
#define AX_CPR_nINIT (1<<2)
#define AX_CPR_ATFD (1<<1)
#define AX_CPR_STRB (1<<0)
struct ax_drvdata {
struct parport *parport;
struct parport_state suspend;
struct device *dev;
struct resource *io;
unsigned char irq_enabled;
void __iomem *base;
void __iomem *spp_data;
void __iomem *spp_spr;
void __iomem *spp_cpr;
};
static inline struct ax_drvdata *pp_to_drv(struct parport *p)
{
return p->private_data;
}
static unsigned char
parport_ax88796_read_data(struct parport *p)
{
struct ax_drvdata *dd = pp_to_drv(p);
return readb(dd->spp_data);
}
static void
parport_ax88796_write_data(struct parport *p, unsigned char data)
{
struct ax_drvdata *dd = pp_to_drv(p);
writeb(data, dd->spp_data);
}
static unsigned char
parport_ax88796_read_control(struct parport *p)
{
struct ax_drvdata *dd = pp_to_drv(p);
unsigned int cpr = readb(dd->spp_cpr);
unsigned int ret = 0;
if (!(cpr & AX_CPR_STRB))
ret |= PARPORT_CONTROL_STROBE;
if (!(cpr & AX_CPR_ATFD))
ret |= PARPORT_CONTROL_AUTOFD;
if (cpr & AX_CPR_nINIT)
ret |= PARPORT_CONTROL_INIT;
if (!(cpr & AX_CPR_SLCTIN))
ret |= PARPORT_CONTROL_SELECT;
return ret;
}
static void
parport_ax88796_write_control(struct parport *p, unsigned char control)
{
struct ax_drvdata *dd = pp_to_drv(p);
unsigned int cpr = readb(dd->spp_cpr);
cpr &= AX_CPR_nDOE;
if (!(control & PARPORT_CONTROL_STROBE))
cpr |= AX_CPR_STRB;
if (!(control & PARPORT_CONTROL_AUTOFD))
cpr |= AX_CPR_ATFD;
if (control & PARPORT_CONTROL_INIT)
cpr |= AX_CPR_nINIT;
if (!(control & PARPORT_CONTROL_SELECT))
cpr |= AX_CPR_SLCTIN;
dev_dbg(dd->dev, "write_control: ctrl=%02x, cpr=%02x\n", control, cpr);
writeb(cpr, dd->spp_cpr);
if (parport_ax88796_read_control(p) != control) {
dev_err(dd->dev, "write_control: read != set (%02x, %02x)\n",
parport_ax88796_read_control(p), control);
}
}
static unsigned char
parport_ax88796_read_status(struct parport *p)
{
struct ax_drvdata *dd = pp_to_drv(p);
unsigned int status = readb(dd->spp_spr);
unsigned int ret = 0;
if (status & AX_SPR_BUSY)
ret |= PARPORT_STATUS_BUSY;
if (status & AX_SPR_ACK)
ret |= PARPORT_STATUS_ACK;
if (status & AX_SPR_ERR)
ret |= PARPORT_STATUS_ERROR;
if (status & AX_SPR_SLCT)
ret |= PARPORT_STATUS_SELECT;
if (status & AX_SPR_PE)
ret |= PARPORT_STATUS_PAPEROUT;
return ret;
}
static unsigned char
parport_ax88796_frob_control(struct parport *p, unsigned char mask,
unsigned char val)
{
struct ax_drvdata *dd = pp_to_drv(p);
unsigned char old = parport_ax88796_read_control(p);
dev_dbg(dd->dev, "frob: mask=%02x, val=%02x, old=%02x\n",
mask, val, old);
parport_ax88796_write_control(p, (old & ~mask) | val);
return old;
}
static void
parport_ax88796_enable_irq(struct parport *p)
{
struct ax_drvdata *dd = pp_to_drv(p);
unsigned long flags;
local_irq_save(flags);
if (!dd->irq_enabled) {
enable_irq(p->irq);
dd->irq_enabled = 1;
}
local_irq_restore(flags);
}
static void
parport_ax88796_disable_irq(struct parport *p)
{
struct ax_drvdata *dd = pp_to_drv(p);
unsigned long flags;
local_irq_save(flags);
if (dd->irq_enabled) {
disable_irq(p->irq);
dd->irq_enabled = 0;
}
local_irq_restore(flags);
}
static void
parport_ax88796_data_forward(struct parport *p)
{
struct ax_drvdata *dd = pp_to_drv(p);
void __iomem *cpr = dd->spp_cpr;
writeb((readb(cpr) & ~AX_CPR_nDOE), cpr);
}
static void
parport_ax88796_data_reverse(struct parport *p)
{
struct ax_drvdata *dd = pp_to_drv(p);
void __iomem *cpr = dd->spp_cpr;
writeb(readb(cpr) | AX_CPR_nDOE, cpr);
}
static void
parport_ax88796_init_state(struct pardevice *d, struct parport_state *s)
{
struct ax_drvdata *dd = pp_to_drv(d->port);
memset(s, 0, sizeof(struct parport_state));
dev_dbg(dd->dev, "init_state: %p: state=%p\n", d, s);
s->u.ax88796.cpr = readb(dd->spp_cpr);
}
static void
parport_ax88796_save_state(struct parport *p, struct parport_state *s)
{
struct ax_drvdata *dd = pp_to_drv(p);
dev_dbg(dd->dev, "save_state: %p: state=%p\n", p, s);
s->u.ax88796.cpr = readb(dd->spp_cpr);
}
static void
parport_ax88796_restore_state(struct parport *p, struct parport_state *s)
{
struct ax_drvdata *dd = pp_to_drv(p);
dev_dbg(dd->dev, "restore_state: %p: state=%p\n", p, s);
writeb(s->u.ax88796.cpr, dd->spp_cpr);
}
static struct parport_operations parport_ax88796_ops = {
.write_data = parport_ax88796_write_data,
.read_data = parport_ax88796_read_data,
.write_control = parport_ax88796_write_control,
.read_control = parport_ax88796_read_control,
.frob_control = parport_ax88796_frob_control,
.read_status = parport_ax88796_read_status,
.enable_irq = parport_ax88796_enable_irq,
.disable_irq = parport_ax88796_disable_irq,
.data_forward = parport_ax88796_data_forward,
.data_reverse = parport_ax88796_data_reverse,
.init_state = parport_ax88796_init_state,
.save_state = parport_ax88796_save_state,
.restore_state = parport_ax88796_restore_state,
.epp_write_data = parport_ieee1284_epp_write_data,
.epp_read_data = parport_ieee1284_epp_read_data,
.epp_write_addr = parport_ieee1284_epp_write_addr,
.epp_read_addr = parport_ieee1284_epp_read_addr,
.ecp_write_data = parport_ieee1284_ecp_write_data,
.ecp_read_data = parport_ieee1284_ecp_read_data,
.ecp_write_addr = parport_ieee1284_ecp_write_addr,
.compat_write_data = parport_ieee1284_write_compat,
.nibble_read_data = parport_ieee1284_read_nibble,
.byte_read_data = parport_ieee1284_read_byte,
.owner = THIS_MODULE,
};
static int parport_ax88796_probe(struct platform_device *pdev)
{
struct device *_dev = &pdev->dev;
struct ax_drvdata *dd;
struct parport *pp = NULL;
struct resource *res;
unsigned long size;
int spacing;
int irq;
int ret;
dd = kzalloc(sizeof(struct ax_drvdata), GFP_KERNEL);
if (dd == NULL) {
dev_err(_dev, "no memory for private data\n");
return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(_dev, "no MEM specified\n");
ret = -ENXIO;
goto exit_mem;
}
size = resource_size(res);
spacing = size / 3;
dd->io = request_mem_region(res->start, size, pdev->name);
if (dd->io == NULL) {
dev_err(_dev, "cannot reserve memory\n");
ret = -ENXIO;
goto exit_mem;
}
dd->base = ioremap(res->start, size);
if (dd->base == NULL) {
dev_err(_dev, "cannot ioremap region\n");
ret = -ENXIO;
goto exit_res;
}
irq = platform_get_irq(pdev, 0);
if (irq <= 0)
irq = PARPORT_IRQ_NONE;
pp = parport_register_port((unsigned long)dd->base, irq,
PARPORT_DMA_NONE,
&parport_ax88796_ops);
if (pp == NULL) {
dev_err(_dev, "failed to register parallel port\n");
ret = -ENOMEM;
goto exit_unmap;
}
pp->private_data = dd;
dd->parport = pp;
dd->dev = _dev;
dd->spp_data = dd->base;
dd->spp_spr = dd->base + (spacing * 1);
dd->spp_cpr = dd->base + (spacing * 2);
/* initialise the port controls */
writeb(AX_CPR_STRB, dd->spp_cpr);
if (irq >= 0) {
/* request irq */
ret = request_irq(irq, parport_irq_handler,
IRQF_TRIGGER_FALLING, pdev->name, pp);
if (ret < 0)
goto exit_port;
dd->irq_enabled = 1;
}
platform_set_drvdata(pdev, pp);
dev_info(_dev, "attached parallel port driver\n");
parport_announce_port(pp);
return 0;
exit_port:
parport_remove_port(pp);
exit_unmap:
iounmap(dd->base);
exit_res:
release_resource(dd->io);
kfree(dd->io);
exit_mem:
kfree(dd);
return ret;
}
static int parport_ax88796_remove(struct platform_device *pdev)
{
struct parport *p = platform_get_drvdata(pdev);
struct ax_drvdata *dd = pp_to_drv(p);
free_irq(p->irq, p);
parport_remove_port(p);
iounmap(dd->base);
release_resource(dd->io);
kfree(dd->io);
kfree(dd);
return 0;
}
#ifdef CONFIG_PM
static int parport_ax88796_suspend(struct platform_device *dev,
pm_message_t state)
{
struct parport *p = platform_get_drvdata(dev);
struct ax_drvdata *dd = pp_to_drv(p);
parport_ax88796_save_state(p, &dd->suspend);
writeb(AX_CPR_nDOE | AX_CPR_STRB, dd->spp_cpr);
return 0;
}
static int parport_ax88796_resume(struct platform_device *dev)
{
struct parport *p = platform_get_drvdata(dev);
struct ax_drvdata *dd = pp_to_drv(p);
parport_ax88796_restore_state(p, &dd->suspend);
return 0;
}
#else
#define parport_ax88796_suspend NULL
#define parport_ax88796_resume NULL
#endif
MODULE_ALIAS("platform:ax88796-pp");
static struct platform_driver axdrv = {
.driver = {
.name = "ax88796-pp",
.owner = THIS_MODULE,
},
.probe = parport_ax88796_probe,
.remove = parport_ax88796_remove,
.suspend = parport_ax88796_suspend,
.resume = parport_ax88796_resume,
};
module_platform_driver(axdrv);
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
MODULE_DESCRIPTION("AX88796 Parport parallel port driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,196 @@
/*======================================================================
A driver for PCMCIA parallel port adapters
(specifically, for the Quatech SPP-100 EPP card: other cards will
probably require driver tweaks)
parport_cs.c 1.29 2002/10/11 06:57:41
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of
the License at http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing
rights and limitations under the License.
The initial developer of the original code is David A. Hinds
<dahinds@users.sourceforge.net>. Portions created by David A. Hinds
are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
Alternatively, the contents of this file may be used under the
terms of the GNU General Public License version 2 (the "GPL"), in
which case the provisions of the GPL are applicable instead of the
above. If you wish to allow the use of your version of this file
only under the terms of the GPL and not to allow others to use
your version of this file under the MPL, indicate your decision
by deleting the provisions above and replace them with the notice
and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this
file under either the MPL or the GPL.
======================================================================*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/major.h>
#include <linux/interrupt.h>
#include <linux/parport.h>
#include <linux/parport_pc.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ciscode.h>
/*====================================================================*/
/* Module parameters */
MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
MODULE_DESCRIPTION("PCMCIA parallel port card driver");
MODULE_LICENSE("Dual MPL/GPL");
#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
INT_MODULE_PARM(epp_mode, 1);
/*====================================================================*/
#define FORCE_EPP_MODE 0x08
typedef struct parport_info_t {
struct pcmcia_device *p_dev;
int ndev;
struct parport *port;
} parport_info_t;
static void parport_detach(struct pcmcia_device *p_dev);
static int parport_config(struct pcmcia_device *link);
static void parport_cs_release(struct pcmcia_device *);
static int parport_probe(struct pcmcia_device *link)
{
parport_info_t *info;
dev_dbg(&link->dev, "parport_attach()\n");
/* Create new parport device */
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) return -ENOMEM;
link->priv = info;
info->p_dev = link;
link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
return parport_config(link);
} /* parport_attach */
static void parport_detach(struct pcmcia_device *link)
{
dev_dbg(&link->dev, "parport_detach\n");
parport_cs_release(link);
kfree(link->priv);
} /* parport_detach */
static int parport_config_check(struct pcmcia_device *p_dev, void *priv_data)
{
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
return pcmcia_request_io(p_dev);
}
static int parport_config(struct pcmcia_device *link)
{
parport_info_t *info = link->priv;
struct parport *p;
int ret;
dev_dbg(&link->dev, "parport_config\n");
if (epp_mode)
link->config_index |= FORCE_EPP_MODE;
ret = pcmcia_loop_config(link, parport_config_check, NULL);
if (ret)
goto failed;
if (!link->irq)
goto failed;
ret = pcmcia_enable_device(link);
if (ret)
goto failed;
p = parport_pc_probe_port(link->resource[0]->start,
link->resource[1]->start,
link->irq, PARPORT_DMA_NONE,
&link->dev, IRQF_SHARED);
if (p == NULL) {
printk(KERN_NOTICE "parport_cs: parport_pc_probe_port() at "
"0x%3x, irq %u failed\n",
(unsigned int) link->resource[0]->start,
link->irq);
goto failed;
}
p->modes |= PARPORT_MODE_PCSPP;
if (epp_mode)
p->modes |= PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP;
info->ndev = 1;
info->port = p;
return 0;
failed:
parport_cs_release(link);
return -ENODEV;
} /* parport_config */
static void parport_cs_release(struct pcmcia_device *link)
{
parport_info_t *info = link->priv;
dev_dbg(&link->dev, "parport_release\n");
if (info->ndev) {
struct parport *p = info->port;
parport_pc_unregister_port(p);
}
info->ndev = 0;
pcmcia_disable_device(link);
} /* parport_cs_release */
static const struct pcmcia_device_id parport_ids[] = {
PCMCIA_DEVICE_FUNC_ID(3),
PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial+Parallel Port: SP230",0x3beb8cf2,0xdb9e58bc),
PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0003),
PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, parport_ids);
static struct pcmcia_driver parport_cs_driver = {
.owner = THIS_MODULE,
.name = "parport_cs",
.probe = parport_probe,
.remove = parport_detach,
.id_table = parport_ids,
};
module_pcmcia_driver(parport_cs_driver);

View file

@ -0,0 +1,431 @@
/*
* Low-level parallel-support for PC-style hardware integrated in the
* LASI-Controller (on GSC-Bus) for HP-PARISC Workstations
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* (C) 1999-2001 by Helge Deller <deller@gmx.de>
*
*
* based on parport_pc.c by
* Grant Guenther <grant@torque.net>
* Phil Blundell <philb@gnu.org>
* Tim Waugh <tim@cyberelk.demon.co.uk>
* Jose Renau <renau@acm.org>
* David Campbell
* Andrea Arcangeli
*/
#undef DEBUG /* undef for production */
#include <linux/module.h>
#include <linux/init.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 <asm/superio.h>
#include <linux/parport.h>
#include <asm/pdc.h>
#include <asm/parisc-device.h>
#include <asm/hardware.h>
#include "parport_gsc.h"
MODULE_AUTHOR("Helge Deller <deller@gmx.de>");
MODULE_DESCRIPTION("HP-PARISC PC-style parallel port driver");
MODULE_SUPPORTED_DEVICE("integrated PC-style parallel port");
MODULE_LICENSE("GPL");
/*
* 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_gsc_read_status(pb) & 0x01))
return 1;
/* To clear timeout some chips require double read */
parport_gsc_read_status(pb);
r = parport_gsc_read_status(pb);
parport_writeb (r | 0x01, STATUS (pb)); /* Some reset by writing 1 */
parport_writeb (r & 0xfe, STATUS (pb)); /* Others by writing 0 */
r = parport_gsc_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_gsc.h.
*/
void parport_gsc_init_state(struct pardevice *dev, struct parport_state *s)
{
s->u.pc.ctr = 0xc | (dev->irq_func ? 0x10 : 0x0);
}
void parport_gsc_save_state(struct parport *p, struct parport_state *s)
{
s->u.pc.ctr = parport_readb (CONTROL (p));
}
void parport_gsc_restore_state(struct parport *p, struct parport_state *s)
{
parport_writeb (s->u.pc.ctr, CONTROL (p));
}
struct parport_operations parport_gsc_ops =
{
.write_data = parport_gsc_write_data,
.read_data = parport_gsc_read_data,
.write_control = parport_gsc_write_control,
.read_control = parport_gsc_read_control,
.frob_control = parport_gsc_frob_control,
.read_status = parport_gsc_read_status,
.enable_irq = parport_gsc_enable_irq,
.disable_irq = parport_gsc_disable_irq,
.data_forward = parport_gsc_data_forward,
.data_reverse = parport_gsc_data_reverse,
.init_state = parport_gsc_init_state,
.save_state = parport_gsc_save_state,
.restore_state = parport_gsc_restore_state,
.epp_write_data = parport_ieee1284_epp_write_data,
.epp_read_data = parport_ieee1284_epp_read_data,
.epp_write_addr = parport_ieee1284_epp_write_addr,
.epp_read_addr = parport_ieee1284_epp_read_addr,
.ecp_write_data = parport_ieee1284_ecp_write_data,
.ecp_read_data = parport_ieee1284_ecp_read_data,
.ecp_write_addr = parport_ieee1284_ecp_write_addr,
.compat_write_data = parport_ieee1284_write_compat,
.nibble_read_data = parport_ieee1284_read_nibble,
.byte_read_data = parport_ieee1284_read_byte,
.owner = THIS_MODULE,
};
/* --- Mode detection ------------------------------------- */
/*
* Checks for port existence, all ports support SPP MODE
*/
static int parport_SPP_supported(struct parport *pb)
{
unsigned char r, w;
/*
* first clear an eventually pending EPP timeout
* I (sailer@ife.ee.ethz.ch) have an SMSC chipset
* that does not even respond to SPP cycles if an EPP
* timeout is pending
*/
clear_epp_timeout(pb);
/* Do a simple read-write test to make sure the port exists. */
w = 0xc;
parport_writeb (w, CONTROL (pb));
/* Is there a control register that we can read from? Some
* ports don't allow reads, so read_control just returns a
* software copy. Some ports _do_ allow reads, so bypass the
* software copy here. In addition, some bits aren't
* writable. */
r = parport_readb (CONTROL (pb));
if ((r & 0xf) == w) {
w = 0xe;
parport_writeb (w, CONTROL (pb));
r = parport_readb (CONTROL (pb));
parport_writeb (0xc, CONTROL (pb));
if ((r & 0xf) == w)
return PARPORT_MODE_PCSPP;
}
/* Try the data register. The data lines aren't tri-stated at
* this stage, so we expect back what we wrote. */
w = 0xaa;
parport_gsc_write_data (pb, w);
r = parport_gsc_read_data (pb);
if (r == w) {
w = 0x55;
parport_gsc_write_data (pb, w);
r = parport_gsc_read_data (pb);
if (r == w)
return PARPORT_MODE_PCSPP;
}
return 0;
}
/* Detect PS/2 support.
*
* Bit 5 (0x20) sets the PS/2 data direction; setting this high
* allows us to read data from the data lines. In theory we would get back
* 0xff but any peripheral attached to the port may drag some or all of the
* lines down to zero. So if we get back anything that isn't the contents
* of the data register we deem PS/2 support to be present.
*
* Some SPP ports have "half PS/2" ability - you can't turn off the line
* drivers, but an external peripheral with sufficiently beefy drivers of
* its own can overpower them and assert its own levels onto the bus, from
* where they can then be read back as normal. Ports with this property
* and the right type of device attached are likely to fail the SPP test,
* (as they will appear to have stuck bits) and so the fact that they might
* be misdetected here is rather academic.
*/
static int parport_PS2_supported(struct parport *pb)
{
int ok = 0;
clear_epp_timeout(pb);
/* try to tri-state the buffer */
parport_gsc_data_reverse (pb);
parport_gsc_write_data(pb, 0x55);
if (parport_gsc_read_data(pb) != 0x55) ok++;
parport_gsc_write_data(pb, 0xaa);
if (parport_gsc_read_data(pb) != 0xaa) ok++;
/* cancel input mode */
parport_gsc_data_forward (pb);
if (ok) {
pb->modes |= PARPORT_MODE_TRISTATE;
} else {
struct parport_gsc_private *priv = pb->private_data;
priv->ctr_writable &= ~0x20;
}
return ok;
}
/* --- Initialisation code -------------------------------- */
struct parport *parport_gsc_probe_port(unsigned long base,
unsigned long base_hi, int irq,
int dma, struct parisc_device *padev)
{
struct parport_gsc_private *priv;
struct parport_operations *ops;
struct parport tmp;
struct parport *p = &tmp;
priv = kzalloc (sizeof (struct parport_gsc_private), GFP_KERNEL);
if (!priv) {
printk (KERN_DEBUG "parport (0x%lx): no memory!\n", base);
return NULL;
}
ops = kmemdup(&parport_gsc_ops, sizeof(struct parport_operations),
GFP_KERNEL);
if (!ops) {
printk (KERN_DEBUG "parport (0x%lx): no memory for ops!\n",
base);
kfree (priv);
return NULL;
}
priv->ctr = 0xc;
priv->ctr_writable = 0xff;
priv->dma_buf = 0;
priv->dma_handle = 0;
p->base = base;
p->base_hi = base_hi;
p->irq = irq;
p->dma = dma;
p->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT;
p->ops = ops;
p->private_data = priv;
p->physport = p;
if (!parport_SPP_supported (p)) {
/* No port. */
kfree (priv);
kfree(ops);
return NULL;
}
parport_PS2_supported (p);
if (!(p = parport_register_port(base, PARPORT_IRQ_NONE,
PARPORT_DMA_NONE, ops))) {
kfree (priv);
kfree (ops);
return NULL;
}
p->dev = &padev->dev;
p->base_hi = base_hi;
p->modes = tmp.modes;
p->size = (p->modes & PARPORT_MODE_EPP)?8:3;
p->private_data = priv;
printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
p->irq = irq;
if (p->irq == PARPORT_IRQ_AUTO) {
p->irq = PARPORT_IRQ_NONE;
}
if (p->irq != PARPORT_IRQ_NONE) {
printk(", irq %d", p->irq);
if (p->dma == PARPORT_DMA_AUTO) {
p->dma = PARPORT_DMA_NONE;
}
}
if (p->dma == PARPORT_DMA_AUTO) /* To use DMA, giving the irq
is mandatory (see above) */
p->dma = PARPORT_DMA_NONE;
printk(" [");
#define printmode(x) {if(p->modes&PARPORT_MODE_##x){printk("%s%s",f?",":"",#x);f++;}}
{
int f = 0;
printmode(PCSPP);
printmode(TRISTATE);
printmode(COMPAT)
printmode(EPP);
// printmode(ECP);
// printmode(DMA);
}
#undef printmode
printk("]\n");
if (p->irq != PARPORT_IRQ_NONE) {
if (request_irq (p->irq, parport_irq_handler,
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;
}
}
/* Done probing. Now put the port into a sensible start-up state. */
parport_gsc_write_data(p, 0);
parport_gsc_data_forward (p);
/* Now that we've told the sharing engine about the port, and
found out its characteristics, let the high-level drivers
know about it. */
parport_announce_port (p);
return p;
}
#define PARPORT_GSC_OFFSET 0x800
static int parport_count;
static int parport_init_chip(struct parisc_device *dev)
{
struct parport *p;
unsigned long port;
if (!dev->irq) {
printk(KERN_WARNING "IRQ not found for parallel device at 0x%llx\n",
(unsigned long long)dev->hpa.start);
return -ENODEV;
}
port = dev->hpa.start + PARPORT_GSC_OFFSET;
/* some older machines with ASP-chip don't support
* the enhanced parport modes.
*/
if (boot_cpu_data.cpu_type > pcxt && !pdc_add_valid(port+4)) {
/* Initialize bidirectional-mode (0x10) & data-tranfer-mode #1 (0x20) */
printk("%s: initialize bidirectional-mode.\n", __func__);
parport_writeb ( (0x10 + 0x20), port + 4);
} else {
printk("%s: enhanced parport-modes not supported.\n", __func__);
}
p = parport_gsc_probe_port(port, 0, dev->irq,
/* PARPORT_IRQ_NONE */ PARPORT_DMA_NONE, dev);
if (p)
parport_count++;
dev_set_drvdata(&dev->dev, p);
return 0;
}
static int parport_remove_chip(struct parisc_device *dev)
{
struct parport *p = dev_get_drvdata(&dev->dev);
if (p) {
struct parport_gsc_private *priv = p->private_data;
struct parport_operations *ops = p->ops;
parport_remove_port(p);
if (p->dma != PARPORT_DMA_NONE)
free_dma(p->dma);
if (p->irq != PARPORT_IRQ_NONE)
free_irq(p->irq, p);
if (priv->dma_buf)
pci_free_consistent(priv->dev, PAGE_SIZE,
priv->dma_buf,
priv->dma_handle);
kfree (p->private_data);
parport_put_port(p);
kfree (ops); /* hope no-one cached it */
}
return 0;
}
static struct parisc_device_id parport_tbl[] = {
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x74 },
{ 0, }
};
MODULE_DEVICE_TABLE(parisc, parport_tbl);
static struct parisc_driver parport_driver = {
.name = "Parallel",
.id_table = parport_tbl,
.probe = parport_init_chip,
.remove = parport_remove_chip,
};
int parport_gsc_init(void)
{
return register_parisc_driver(&parport_driver);
}
static void parport_gsc_exit(void)
{
unregister_parisc_driver(&parport_driver);
}
module_init(parport_gsc_init);
module_exit(parport_gsc_exit);

View file

@ -0,0 +1,222 @@
/*
* Low-level parallel-support for PC-style hardware integrated in the
* LASI-Controller (on GSC-Bus) for HP-PARISC Workstations
*
* (C) 1999-2001 by Helge Deller <deller@gmx.de>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* based on parport_pc.c by
* Grant Guenther <grant@torque.net>
* Phil Blundell <Philip.Blundell@pobox.com>
* Tim Waugh <tim@cyberelk.demon.co.uk>
* Jose Renau <renau@acm.org>
* David Campbell
* Andrea Arcangeli
*/
#ifndef __DRIVERS_PARPORT_PARPORT_GSC_H
#define __DRIVERS_PARPORT_PARPORT_GSC_H
#include <asm/io.h>
#include <linux/delay.h>
#undef DEBUG_PARPORT /* undefine for production */
#define DELAY_TIME 0
#if DELAY_TIME == 0
#define parport_readb gsc_readb
#define parport_writeb gsc_writeb
#else
static __inline__ unsigned char parport_readb( unsigned long port )
{
udelay(DELAY_TIME);
return gsc_readb(port);
}
static __inline__ void parport_writeb( unsigned char value, unsigned long port )
{
gsc_writeb(value,port);
udelay(DELAY_TIME);
}
#endif
/* --- register definitions ------------------------------- */
#define EPPDATA(p) ((p)->base + 0x4)
#define EPPADDR(p) ((p)->base + 0x3)
#define CONTROL(p) ((p)->base + 0x2)
#define STATUS(p) ((p)->base + 0x1)
#define DATA(p) ((p)->base + 0x0)
struct parport_gsc_private {
/* Contents of CTR. */
unsigned char ctr;
/* Bitmask of writable CTR bits. */
unsigned char ctr_writable;
/* Number of bytes per portword. */
int pword;
/* Not used yet. */
int readIntrThreshold;
int writeIntrThreshold;
/* buffer suitable for DMA, if DMA enabled */
char *dma_buf;
dma_addr_t dma_handle;
struct pci_dev *dev;
};
static inline void parport_gsc_write_data(struct parport *p, unsigned char d)
{
#ifdef DEBUG_PARPORT
printk (KERN_DEBUG "parport_gsc_write_data(%p,0x%02x)\n", p, d);
#endif
parport_writeb(d, DATA(p));
}
static inline unsigned char parport_gsc_read_data(struct parport *p)
{
unsigned char val = parport_readb (DATA (p));
#ifdef DEBUG_PARPORT
printk (KERN_DEBUG "parport_gsc_read_data(%p) = 0x%02x\n",
p, val);
#endif
return val;
}
/* __parport_gsc_frob_control differs from parport_gsc_frob_control in that
* it doesn't do any extra masking. */
static inline unsigned char __parport_gsc_frob_control(struct parport *p,
unsigned char mask,
unsigned char val)
{
struct parport_gsc_private *priv = p->physport->private_data;
unsigned char ctr = priv->ctr;
#ifdef DEBUG_PARPORT
printk (KERN_DEBUG
"__parport_gsc_frob_control(%02x,%02x): %02x -> %02x\n",
mask, val, ctr, ((ctr & ~mask) ^ val) & priv->ctr_writable);
#endif
ctr = (ctr & ~mask) ^ val;
ctr &= priv->ctr_writable; /* only write writable bits. */
parport_writeb (ctr, CONTROL (p));
priv->ctr = ctr; /* Update soft copy */
return ctr;
}
static inline void parport_gsc_data_reverse(struct parport *p)
{
__parport_gsc_frob_control (p, 0x20, 0x20);
}
static inline void parport_gsc_data_forward(struct parport *p)
{
__parport_gsc_frob_control (p, 0x20, 0x00);
}
static inline void parport_gsc_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 newer interface. */
if (d & 0x20) {
printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
p->name, p->cad->name);
parport_gsc_data_reverse (p);
}
__parport_gsc_frob_control (p, wm, d & wm);
}
static inline unsigned char parport_gsc_read_control(struct parport *p)
{
const unsigned char rm = (PARPORT_CONTROL_STROBE |
PARPORT_CONTROL_AUTOFD |
PARPORT_CONTROL_INIT |
PARPORT_CONTROL_SELECT);
const struct parport_gsc_private *priv = p->physport->private_data;
return priv->ctr & rm; /* Use soft copy */
}
static inline unsigned char parport_gsc_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 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_gsc_data_reverse (p);
else
parport_gsc_data_forward (p);
}
/* Restrict mask and val to control lines. */
mask &= wm;
val &= wm;
return __parport_gsc_frob_control (p, mask, val);
}
static inline unsigned char parport_gsc_read_status(struct parport *p)
{
return parport_readb (STATUS(p));
}
static inline void parport_gsc_disable_irq(struct parport *p)
{
__parport_gsc_frob_control (p, 0x10, 0x00);
}
static inline void parport_gsc_enable_irq(struct parport *p)
{
__parport_gsc_frob_control (p, 0x10, 0x10);
}
extern void parport_gsc_release_resources(struct parport *p);
extern int parport_gsc_claim_resources(struct parport *p);
extern void parport_gsc_init_state(struct pardevice *, struct parport_state *s);
extern void parport_gsc_save_state(struct parport *p, struct parport_state *s);
extern void parport_gsc_restore_state(struct parport *p, struct parport_state *s);
extern void parport_gsc_inc_use_count(void);
extern void parport_gsc_dec_use_count(void);
extern struct parport *parport_gsc_probe_port(unsigned long base,
unsigned long base_hi,
int irq, int dma,
struct parisc_device *padev);
#endif /* __DRIVERS_PARPORT_PARPORT_GSC_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,370 @@
/* Low-level parallel port routines for the Multiface 3 card
*
* Author: Joerg Dorchain <joerg@dorchain.net>
*
* (C) The elitist m68k Users(TM)
*
* based on the existing parport_amiga and lp_mfc
*
*
* From the MFC3 documentation:
*
* Miscellaneous PIA Details
* -------------------------
*
* The two open-drain interrupt outputs /IRQA and /IRQB are routed to
* /INT2 of the Z2 bus.
*
* The CPU data bus of the PIA (D0-D7) is connected to D8-D15 on the Z2
* bus. This means that any PIA registers are accessed at even addresses.
*
* Centronics Pin Connections for the PIA
* --------------------------------------
*
* The following table shows the connections between the PIA and the
* Centronics interface connector. These connections implement a single, but
* very complete, Centronics type interface. The Pin column gives the pin
* numbers of the PIA. The Centronics pin numbers can be found in the section
* "Parallel Connectors".
*
*
* Pin | PIA | Dir | Centronics Names
* -------+-----+-----+---------------------------------------------------------
* 19 | CB2 | --> | /STROBE (aka /DRDY)
* 10-17 | PBx | <-> | DATA0 - DATA7
* 18 | CB1 | <-- | /ACK
* 40 | CA1 | <-- | BUSY
* 3 | PA1 | <-- | PAPER-OUT (aka POUT)
* 4 | PA2 | <-- | SELECTED (aka SEL)
* 9 | PA7 | --> | /INIT (aka /RESET or /INPUT-PRIME)
* 6 | PA4 | <-- | /ERROR (aka /FAULT)
* 7 | PA5 | --> | DIR (aka /SELECT-IN)
* 8 | PA6 | --> | /AUTO-FEED-XT
* 39 | CA2 | --> | open
* 5 | PA3 | <-- | /ACK (same as CB1!)
* 2 | PA0 | <-- | BUSY (same as CA1!)
* -------+-----+-----+---------------------------------------------------------
*
* Should be enough to understand some of the driver.
*
* Per convention for normal use the port registers are visible.
* If you need the data direction registers, restore the value in the
* control register.
*/
#include "multiface.h"
#include <linux/module.h>
#include <linux/init.h>
#include <linux/parport.h>
#include <linux/delay.h>
#include <linux/mc6821.h>
#include <linux/zorro.h>
#include <linux/interrupt.h>
#include <asm/setup.h>
#include <asm/amigahw.h>
#include <asm/irq.h>
#include <asm/amigaints.h>
/* Maximum Number of Cards supported */
#define MAX_MFC 5
#undef DEBUG
#ifdef DEBUG
#define DPRINTK printk
#else
static inline int DPRINTK(void *nothing, ...) {return 0;}
#endif
static struct parport *this_port[MAX_MFC] = {NULL, };
static volatile int dummy; /* for trigger readds */
#define pia(dev) ((struct pia *)(dev->base))
static struct parport_operations pp_mfc3_ops;
static void mfc3_write_data(struct parport *p, unsigned char data)
{
DPRINTK(KERN_DEBUG "write_data %c\n",data);
dummy = pia(p)->pprb; /* clears irq bit */
/* Triggers also /STROBE.*/
pia(p)->pprb = data;
}
static unsigned char mfc3_read_data(struct parport *p)
{
/* clears interrupt bit. Triggers also /STROBE. */
return pia(p)->pprb;
}
static unsigned char control_pc_to_mfc3(unsigned char control)
{
unsigned char ret = 32|64;
if (control & PARPORT_CONTROL_SELECT) /* XXX: What is SELECP? */
ret &= ~32; /* /SELECT_IN */
if (control & PARPORT_CONTROL_INIT) /* INITP */
ret |= 128;
if (control & PARPORT_CONTROL_AUTOFD) /* AUTOLF */
ret &= ~64;
if (control & PARPORT_CONTROL_STROBE) /* Strobe */
/* Handled directly by hardware */;
return ret;
}
static unsigned char control_mfc3_to_pc(unsigned char control)
{
unsigned char ret = PARPORT_CONTROL_STROBE
| PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_SELECT;
if (control & 128) /* /INITP */
ret |= PARPORT_CONTROL_INIT;
if (control & 64) /* /AUTOLF */
ret &= ~PARPORT_CONTROL_AUTOFD;
if (control & 32) /* /SELECT_IN */
ret &= ~PARPORT_CONTROL_SELECT;
return ret;
}
static void mfc3_write_control(struct parport *p, unsigned char control)
{
DPRINTK(KERN_DEBUG "write_control %02x\n",control);
pia(p)->ppra = (pia(p)->ppra & 0x1f) | control_pc_to_mfc3(control);
}
static unsigned char mfc3_read_control( struct parport *p)
{
DPRINTK(KERN_DEBUG "read_control \n");
return control_mfc3_to_pc(pia(p)->ppra & 0xe0);
}
static unsigned char mfc3_frob_control( struct parport *p, unsigned char mask, unsigned char val)
{
unsigned char old;
DPRINTK(KERN_DEBUG "frob_control mask %02x, value %02x\n",mask,val);
old = mfc3_read_control(p);
mfc3_write_control(p, (old & ~mask) ^ val);
return old;
}
static unsigned char status_mfc3_to_pc(unsigned char status)
{
unsigned char ret = PARPORT_STATUS_BUSY;
if (status & 1) /* Busy */
ret &= ~PARPORT_STATUS_BUSY;
if (status & 2) /* PaperOut */
ret |= PARPORT_STATUS_PAPEROUT;
if (status & 4) /* Selected */
ret |= PARPORT_STATUS_SELECT;
if (status & 8) /* Ack */
ret |= PARPORT_STATUS_ACK;
if (status & 16) /* /ERROR */
ret |= PARPORT_STATUS_ERROR;
return ret;
}
static unsigned char mfc3_read_status(struct parport *p)
{
unsigned char status;
status = status_mfc3_to_pc(pia(p)->ppra & 0x1f);
DPRINTK(KERN_DEBUG "read_status %02x\n", status);
return status;
}
static int use_cnt = 0;
static irqreturn_t mfc3_interrupt(int irq, void *dev_id)
{
int i;
for( i = 0; i < MAX_MFC; i++)
if (this_port[i] != NULL)
if (pia(this_port[i])->crb & 128) { /* Board caused interrupt */
dummy = pia(this_port[i])->pprb; /* clear irq bit */
parport_generic_irq(this_port[i]);
}
return IRQ_HANDLED;
}
static void mfc3_enable_irq(struct parport *p)
{
pia(p)->crb |= PIA_C1_ENABLE_IRQ;
}
static void mfc3_disable_irq(struct parport *p)
{
pia(p)->crb &= ~PIA_C1_ENABLE_IRQ;
}
static void mfc3_data_forward(struct parport *p)
{
DPRINTK(KERN_DEBUG "forward\n");
pia(p)->crb &= ~PIA_DDR; /* make data direction register visible */
pia(p)->pddrb = 255; /* all pins output */
pia(p)->crb |= PIA_DDR; /* make data register visible - default */
}
static void mfc3_data_reverse(struct parport *p)
{
DPRINTK(KERN_DEBUG "reverse\n");
pia(p)->crb &= ~PIA_DDR; /* make data direction register visible */
pia(p)->pddrb = 0; /* all pins input */
pia(p)->crb |= PIA_DDR; /* make data register visible - default */
}
static void mfc3_init_state(struct pardevice *dev, struct parport_state *s)
{
s->u.amiga.data = 0;
s->u.amiga.datadir = 255;
s->u.amiga.status = 0;
s->u.amiga.statusdir = 0xe0;
}
static void mfc3_save_state(struct parport *p, struct parport_state *s)
{
s->u.amiga.data = pia(p)->pprb;
pia(p)->crb &= ~PIA_DDR;
s->u.amiga.datadir = pia(p)->pddrb;
pia(p)->crb |= PIA_DDR;
s->u.amiga.status = pia(p)->ppra;
pia(p)->cra &= ~PIA_DDR;
s->u.amiga.statusdir = pia(p)->pddrb;
pia(p)->cra |= PIA_DDR;
}
static void mfc3_restore_state(struct parport *p, struct parport_state *s)
{
pia(p)->pprb = s->u.amiga.data;
pia(p)->crb &= ~PIA_DDR;
pia(p)->pddrb = s->u.amiga.datadir;
pia(p)->crb |= PIA_DDR;
pia(p)->ppra = s->u.amiga.status;
pia(p)->cra &= ~PIA_DDR;
pia(p)->pddrb = s->u.amiga.statusdir;
pia(p)->cra |= PIA_DDR;
}
static struct parport_operations pp_mfc3_ops = {
.write_data = mfc3_write_data,
.read_data = mfc3_read_data,
.write_control = mfc3_write_control,
.read_control = mfc3_read_control,
.frob_control = mfc3_frob_control,
.read_status = mfc3_read_status,
.enable_irq = mfc3_enable_irq,
.disable_irq = mfc3_disable_irq,
.data_forward = mfc3_data_forward,
.data_reverse = mfc3_data_reverse,
.init_state = mfc3_init_state,
.save_state = mfc3_save_state,
.restore_state = mfc3_restore_state,
.epp_write_data = parport_ieee1284_epp_write_data,
.epp_read_data = parport_ieee1284_epp_read_data,
.epp_write_addr = parport_ieee1284_epp_write_addr,
.epp_read_addr = parport_ieee1284_epp_read_addr,
.ecp_write_data = parport_ieee1284_ecp_write_data,
.ecp_read_data = parport_ieee1284_ecp_read_data,
.ecp_write_addr = parport_ieee1284_ecp_write_addr,
.compat_write_data = parport_ieee1284_write_compat,
.nibble_read_data = parport_ieee1284_read_nibble,
.byte_read_data = parport_ieee1284_read_byte,
.owner = THIS_MODULE,
};
/* ----------- Initialisation code --------------------------------- */
static int __init parport_mfc3_init(void)
{
struct parport *p;
int pias = 0;
struct pia *pp;
struct zorro_dev *z = NULL;
if (!MACH_IS_AMIGA)
return -ENODEV;
while ((z = zorro_find_device(ZORRO_PROD_BSC_MULTIFACE_III, z))) {
unsigned long piabase = z->resource.start+PIABASE;
if (!request_mem_region(piabase, sizeof(struct pia), "PIA"))
continue;
pp = ZTWO_VADDR(piabase);
pp->crb = 0;
pp->pddrb = 255; /* all data pins output */
pp->crb = PIA_DDR|32|8;
dummy = pp->pddrb; /* reading clears interrupt */
pp->cra = 0;
pp->pddra = 0xe0; /* /RESET, /DIR ,/AUTO-FEED output */
pp->cra = PIA_DDR;
pp->ppra = 0; /* reset printer */
udelay(10);
pp->ppra = 128;
p = parport_register_port((unsigned long)pp, IRQ_AMIGA_PORTS,
PARPORT_DMA_NONE, &pp_mfc3_ops);
if (!p)
goto out_port;
if (p->irq != PARPORT_IRQ_NONE) {
if (use_cnt++ == 0)
if (request_irq(IRQ_AMIGA_PORTS, mfc3_interrupt, IRQF_SHARED, p->name, &pp_mfc3_ops))
goto out_irq;
}
p->dev = &z->dev;
this_port[pias++] = p;
printk(KERN_INFO "%s: Multiface III port using irq\n", p->name);
/* XXX: set operating mode */
p->private_data = (void *)piabase;
parport_announce_port (p);
if (pias >= MAX_MFC)
break;
continue;
out_irq:
parport_put_port(p);
out_port:
release_mem_region(piabase, sizeof(struct pia));
}
return pias ? 0 : -ENODEV;
}
static void __exit parport_mfc3_exit(void)
{
int i;
for (i = 0; i < MAX_MFC; i++) {
if (!this_port[i])
continue;
parport_remove_port(this_port[i]);
if (this_port[i]->irq != PARPORT_IRQ_NONE) {
if (--use_cnt == 0)
free_irq(IRQ_AMIGA_PORTS, &pp_mfc3_ops);
}
release_mem_region(ZTWO_PADDR(this_port[i]->private_data), sizeof(struct pia));
parport_put_port(this_port[i]);
}
}
MODULE_AUTHOR("Joerg Dorchain <joerg@dorchain.net>");
MODULE_DESCRIPTION("Parport Driver for Multiface 3 expansion cards Parallel Port");
MODULE_SUPPORTED_DEVICE("Multiface 3 Parallel Port");
MODULE_LICENSE("GPL");
module_init(parport_mfc3_init)
module_exit(parport_mfc3_exit)

3357
drivers/parport/parport_pc.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,715 @@
/*
* Support for common PCI multi-I/O cards (which is most of them)
*
* Copyright (C) 2001 Tim Waugh <twaugh@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*
* Multi-function PCI cards are supposed to present separate logical
* devices on the bus. A common thing to do seems to be to just use
* one logical device with lots of base address registers for both
* parallel ports and serial ports. This driver is for dealing with
* that.
*
*/
#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/parport.h>
#include <linux/parport_pc.h>
#include <linux/8250_pci.h>
enum parport_pc_pci_cards {
titan_110l = 0,
titan_210l,
netmos_9xx5_combo,
netmos_9855,
netmos_9855_2p,
netmos_9900,
netmos_9900_2p,
netmos_99xx_1p,
avlab_1s1p,
avlab_1s2p,
avlab_2s1p,
siig_1s1p_10x,
siig_2s1p_10x,
siig_2p1s_20x,
siig_1s1p_20x,
siig_2s1p_20x,
timedia_4078a,
timedia_4079h,
timedia_4085h,
timedia_4088a,
timedia_4089a,
timedia_4095a,
timedia_4096a,
timedia_4078u,
timedia_4079a,
timedia_4085u,
timedia_4079r,
timedia_4079s,
timedia_4079d,
timedia_4079e,
timedia_4079f,
timedia_9079a,
timedia_9079b,
timedia_9079c,
wch_ch353_1s1p,
wch_ch353_2s1p,
sunix_2s1p,
};
/* each element directly indexed from enum list, above */
struct parport_pc_pci {
int numports;
struct { /* BAR (base address registers) numbers in the config
space header */
int lo;
int hi; /* -1 if not there, >6 for offset-method (max
BAR is 6) */
} addr[4];
/* 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. */
int (*preinit_hook) (struct pci_dev *pdev, struct parport_pc_pci *card,
int autoirq, int autodma);
/* If set, this is called after probing for ports. If 'failed'
* is non-zero we couldn't use any of the ports. */
void (*postinit_hook) (struct pci_dev *pdev,
struct parport_pc_pci *card, int failed);
};
static int netmos_parallel_init(struct pci_dev *dev, struct parport_pc_pci *par,
int autoirq, int autodma)
{
/* the rule described below doesn't hold for this device */
if (dev->device == PCI_DEVICE_ID_NETMOS_9835 &&
dev->subsystem_vendor == PCI_VENDOR_ID_IBM &&
dev->subsystem_device == 0x0299)
return -ENODEV;
if (dev->device == PCI_DEVICE_ID_NETMOS_9912) {
par->numports = 1;
} else {
/*
* Netmos uses the subdevice ID to indicate the number of parallel
* and serial ports. The form is 0x00PS, where <P> is the number of
* parallel ports and <S> is the number of serial ports.
*/
par->numports = (dev->subsystem_device & 0xf0) >> 4;
if (par->numports > ARRAY_SIZE(par->addr))
par->numports = ARRAY_SIZE(par->addr);
}
return 0;
}
static struct parport_pc_pci cards[] = {
/* titan_110l */ { 1, { { 3, -1 }, } },
/* titan_210l */ { 1, { { 3, -1 }, } },
/* netmos_9xx5_combo */ { 1, { { 2, -1 }, }, netmos_parallel_init },
/* netmos_9855 */ { 1, { { 0, -1 }, }, netmos_parallel_init },
/* netmos_9855_2p */ { 2, { { 0, -1 }, { 2, -1 }, } },
/* netmos_9900 */ {1, { { 3, 4 }, }, netmos_parallel_init },
/* netmos_9900_2p */ {2, { { 0, 1 }, { 3, 4 }, } },
/* netmos_99xx_1p */ {1, { { 0, 1 }, } },
/* avlab_1s1p */ { 1, { { 1, 2}, } },
/* avlab_1s2p */ { 2, { { 1, 2}, { 3, 4 },} },
/* avlab_2s1p */ { 1, { { 2, 3}, } },
/* siig_1s1p_10x */ { 1, { { 3, 4 }, } },
/* siig_2s1p_10x */ { 1, { { 4, 5 }, } },
/* siig_2p1s_20x */ { 2, { { 1, 2 }, { 3, 4 }, } },
/* siig_1s1p_20x */ { 1, { { 1, 2 }, } },
/* siig_2s1p_20x */ { 1, { { 2, 3 }, } },
/* timedia_4078a */ { 1, { { 2, -1 }, } },
/* timedia_4079h */ { 1, { { 2, 3 }, } },
/* timedia_4085h */ { 2, { { 2, -1 }, { 4, -1 }, } },
/* timedia_4088a */ { 2, { { 2, 3 }, { 4, 5 }, } },
/* timedia_4089a */ { 2, { { 2, 3 }, { 4, 5 }, } },
/* timedia_4095a */ { 2, { { 2, 3 }, { 4, 5 }, } },
/* timedia_4096a */ { 2, { { 2, 3 }, { 4, 5 }, } },
/* timedia_4078u */ { 1, { { 2, -1 }, } },
/* timedia_4079a */ { 1, { { 2, 3 }, } },
/* timedia_4085u */ { 2, { { 2, -1 }, { 4, -1 }, } },
/* timedia_4079r */ { 1, { { 2, 3 }, } },
/* timedia_4079s */ { 1, { { 2, 3 }, } },
/* timedia_4079d */ { 1, { { 2, 3 }, } },
/* timedia_4079e */ { 1, { { 2, 3 }, } },
/* timedia_4079f */ { 1, { { 2, 3 }, } },
/* timedia_9079a */ { 1, { { 2, 3 }, } },
/* timedia_9079b */ { 1, { { 2, 3 }, } },
/* timedia_9079c */ { 1, { { 2, 3 }, } },
/* wch_ch353_1s1p*/ { 1, { { 1, -1}, } },
/* wch_ch353_2s1p*/ { 1, { { 2, -1}, } },
/* sunix_2s1p */ { 1, { { 3, -1 }, } },
};
#define PCI_VENDOR_ID_SUNIX 0x1fd4
#define PCI_DEVICE_ID_SUNIX_1999 0x1999
static struct pci_device_id parport_serial_pci_tbl[] = {
/* PCI cards */
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_110L,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_110l },
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_210L,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_210l },
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9735,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9745,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9845,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo },
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
0x1000, 0x0020, 0, 0, netmos_9855_2p },
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
0x1000, 0x0022, 0, 0, netmos_9855_2p },
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 },
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
0xA000, 0x3011, 0, 0, netmos_9900 },
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
0xA000, 0x3012, 0, 0, netmos_9900 },
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
0xA000, 0x3020, 0, 0, netmos_9900_2p },
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912,
0xA000, 0x2000, 0, 0, netmos_99xx_1p },
/* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/
{ PCI_VENDOR_ID_AFAVLAB, 0x2110,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
{ PCI_VENDOR_ID_AFAVLAB, 0x2111,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
{ PCI_VENDOR_ID_AFAVLAB, 0x2112,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p },
{ PCI_VENDOR_ID_AFAVLAB, 0x2140,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
{ PCI_VENDOR_ID_AFAVLAB, 0x2141,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
{ PCI_VENDOR_ID_AFAVLAB, 0x2142,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p },
{ PCI_VENDOR_ID_AFAVLAB, 0x2160,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
{ PCI_VENDOR_ID_AFAVLAB, 0x2161,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
{ PCI_VENDOR_ID_AFAVLAB, 0x2162,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x },
/* PCI_VENDOR_ID_TIMEDIA/SUNIX has many differing cards ...*/
{ 0x1409, 0x7168, 0x1409, 0x4078, 0, 0, timedia_4078a },
{ 0x1409, 0x7168, 0x1409, 0x4079, 0, 0, timedia_4079h },
{ 0x1409, 0x7168, 0x1409, 0x4085, 0, 0, timedia_4085h },
{ 0x1409, 0x7168, 0x1409, 0x4088, 0, 0, timedia_4088a },
{ 0x1409, 0x7168, 0x1409, 0x4089, 0, 0, timedia_4089a },
{ 0x1409, 0x7168, 0x1409, 0x4095, 0, 0, timedia_4095a },
{ 0x1409, 0x7168, 0x1409, 0x4096, 0, 0, timedia_4096a },
{ 0x1409, 0x7168, 0x1409, 0x5078, 0, 0, timedia_4078u },
{ 0x1409, 0x7168, 0x1409, 0x5079, 0, 0, timedia_4079a },
{ 0x1409, 0x7168, 0x1409, 0x5085, 0, 0, timedia_4085u },
{ 0x1409, 0x7168, 0x1409, 0x6079, 0, 0, timedia_4079r },
{ 0x1409, 0x7168, 0x1409, 0x7079, 0, 0, timedia_4079s },
{ 0x1409, 0x7168, 0x1409, 0x8079, 0, 0, timedia_4079d },
{ 0x1409, 0x7168, 0x1409, 0x9079, 0, 0, timedia_4079e },
{ 0x1409, 0x7168, 0x1409, 0xa079, 0, 0, timedia_4079f },
{ 0x1409, 0x7168, 0x1409, 0xb079, 0, 0, timedia_9079a },
{ 0x1409, 0x7168, 0x1409, 0xc079, 0, 0, timedia_9079b },
{ 0x1409, 0x7168, 0x1409, 0xd079, 0, 0, timedia_9079c },
/* WCH CARDS */
{ 0x4348, 0x5053, PCI_ANY_ID, PCI_ANY_ID, 0, 0, wch_ch353_1s1p},
{ 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p},
/*
* More SUNIX variations. At least one of these has part number
* '5079A but subdevice 0x102. That board reports 0x0708 as
* its PCI Class.
*/
{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX,
0x0102, 0, 0, sunix_2s1p },
{ 0, } /* terminate list */
};
MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl);
/*
* This table describes the serial "geometry" of these boards. Any
* quirks for these can be found in drivers/serial/8250_pci.c
*
* Cards not tested are marked n/t
* If you have one of these cards and it works for you, please tell me..
*/
static struct pciserial_board pci_parport_serial_boards[] = {
[titan_110l] = {
.flags = FL_BASE1 | FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[titan_210l] = {
.flags = FL_BASE1 | FL_BASE_BARS,
.num_ports = 2,
.base_baud = 921600,
.uart_offset = 8,
},
[netmos_9xx5_combo] = {
.flags = FL_BASE0 | FL_BASE_BARS,
.num_ports = 1,
.base_baud = 115200,
.uart_offset = 8,
},
[netmos_9855] = {
.flags = FL_BASE2 | FL_BASE_BARS,
.num_ports = 1,
.base_baud = 115200,
.uart_offset = 8,
},
[netmos_9855_2p] = {
.flags = FL_BASE4 | FL_BASE_BARS,
.num_ports = 1,
.base_baud = 115200,
.uart_offset = 8,
},
[netmos_9900] = { /* n/t */
.flags = FL_BASE0 | FL_BASE_BARS,
.num_ports = 1,
.base_baud = 115200,
.uart_offset = 8,
},
[netmos_9900_2p] = { /* parallel only */ /* n/t */
.flags = FL_BASE0,
.num_ports = 0,
.base_baud = 115200,
.uart_offset = 8,
},
[netmos_99xx_1p] = { /* parallel only */ /* n/t */
.flags = FL_BASE0,
.num_ports = 0,
.base_baud = 115200,
.uart_offset = 8,
},
[avlab_1s1p] = { /* n/t */
.flags = FL_BASE0 | FL_BASE_BARS,
.num_ports = 1,
.base_baud = 115200,
.uart_offset = 8,
},
[avlab_1s2p] = { /* n/t */
.flags = FL_BASE0 | FL_BASE_BARS,
.num_ports = 1,
.base_baud = 115200,
.uart_offset = 8,
},
[avlab_2s1p] = { /* n/t */
.flags = FL_BASE0 | FL_BASE_BARS,
.num_ports = 2,
.base_baud = 115200,
.uart_offset = 8,
},
[siig_1s1p_10x] = {
.flags = FL_BASE2,
.num_ports = 1,
.base_baud = 460800,
.uart_offset = 8,
},
[siig_2s1p_10x] = {
.flags = FL_BASE2,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[siig_2p1s_20x] = {
.flags = FL_BASE0,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[siig_1s1p_20x] = {
.flags = FL_BASE0,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[siig_2s1p_20x] = {
.flags = FL_BASE0,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_4078a] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_4079h] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_4085h] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_4088a] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_4089a] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_4095a] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_4096a] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_4078u] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_4079a] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_4085u] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_4079r] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_4079s] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_4079d] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_4079e] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_4079f] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_9079a] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_9079b] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[timedia_9079c] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[wch_ch353_1s1p] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 115200,
.uart_offset = 8,
},
[wch_ch353_2s1p] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 2,
.base_baud = 115200,
.uart_offset = 8,
},
[sunix_2s1p] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 2,
.base_baud = 921600,
.uart_offset = 8,
},
};
struct parport_serial_private {
struct serial_private *serial;
int num_par;
struct parport *port[PARPORT_MAX];
struct parport_pc_pci par;
};
/* Register the serial port(s) of a PCI card. */
static int serial_register(struct pci_dev *dev, const struct pci_device_id *id)
{
struct parport_serial_private *priv = pci_get_drvdata (dev);
struct pciserial_board *board;
struct serial_private *serial;
board = &pci_parport_serial_boards[id->driver_data];
if (board->num_ports == 0)
return 0;
serial = pciserial_init_ports(dev, board);
if (IS_ERR(serial))
return PTR_ERR(serial);
priv->serial = serial;
return 0;
}
/* Register the parallel port(s) of a PCI card. */
static int parport_register(struct pci_dev *dev, const struct pci_device_id *id)
{
struct parport_pc_pci *card;
struct parport_serial_private *priv = pci_get_drvdata (dev);
int n, success = 0;
priv->par = cards[id->driver_data];
card = &priv->par;
if (card->preinit_hook &&
card->preinit_hook (dev, card, PARPORT_IRQ_NONE, PARPORT_DMA_NONE))
return -ENODEV;
for (n = 0; n < card->numports; n++) {
struct parport *port;
int lo = card->addr[n].lo;
int hi = card->addr[n].hi;
unsigned long io_lo, io_hi;
int irq;
if (priv->num_par == ARRAY_SIZE (priv->port)) {
printk (KERN_WARNING
"parport_serial: %s: only %zu parallel ports "
"supported (%d reported)\n", pci_name (dev),
ARRAY_SIZE(priv->port), card->numports);
break;
}
io_lo = pci_resource_start (dev, lo);
io_hi = 0;
if ((hi >= 0) && (hi <= 6))
io_hi = pci_resource_start (dev, hi);
else if (hi > 6)
io_lo += hi; /* Reinterpret the meaning of
"hi" as an offset (see SYBA
def.) */
/* TODO: test if sharing interrupts works */
irq = dev->irq;
if (irq == IRQ_NONE) {
dev_dbg(&dev->dev,
"PCI parallel port detected: I/O at %#lx(%#lx)\n",
io_lo, io_hi);
irq = PARPORT_IRQ_NONE;
} else {
dev_dbg(&dev->dev,
"PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n",
io_lo, io_hi, irq);
}
port = parport_pc_probe_port (io_lo, io_hi, irq,
PARPORT_DMA_NONE, &dev->dev, IRQF_SHARED);
if (port) {
priv->port[priv->num_par++] = port;
success = 1;
}
}
if (card->postinit_hook)
card->postinit_hook (dev, card, !success);
return 0;
}
static int parport_serial_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
struct parport_serial_private *priv;
int err;
priv = kzalloc (sizeof *priv, GFP_KERNEL);
if (!priv)
return -ENOMEM;
pci_set_drvdata (dev, priv);
err = pci_enable_device (dev);
if (err) {
kfree (priv);
return err;
}
if (parport_register (dev, id)) {
kfree (priv);
return -ENODEV;
}
if (serial_register (dev, id)) {
int i;
for (i = 0; i < priv->num_par; i++)
parport_pc_unregister_port (priv->port[i]);
kfree (priv);
return -ENODEV;
}
return 0;
}
static void parport_serial_pci_remove(struct pci_dev *dev)
{
struct parport_serial_private *priv = pci_get_drvdata (dev);
int i;
// Serial ports
if (priv->serial)
pciserial_remove_ports(priv->serial);
// Parallel ports
for (i = 0; i < priv->num_par; i++)
parport_pc_unregister_port (priv->port[i]);
kfree (priv);
return;
}
#ifdef CONFIG_PM
static int parport_serial_pci_suspend(struct pci_dev *dev, pm_message_t state)
{
struct parport_serial_private *priv = pci_get_drvdata(dev);
if (priv->serial)
pciserial_suspend_ports(priv->serial);
/* FIXME: What about parport? */
pci_save_state(dev);
pci_set_power_state(dev, pci_choose_state(dev, state));
return 0;
}
static int parport_serial_pci_resume(struct pci_dev *dev)
{
struct parport_serial_private *priv = pci_get_drvdata(dev);
int err;
pci_set_power_state(dev, PCI_D0);
pci_restore_state(dev);
/*
* The device may have been disabled. Re-enable it.
*/
err = pci_enable_device(dev);
if (err) {
printk(KERN_ERR "parport_serial: %s: error enabling "
"device for resume (%d)\n", pci_name(dev), err);
return err;
}
if (priv->serial)
pciserial_resume_ports(priv->serial);
/* FIXME: What about parport? */
return 0;
}
#endif
static struct pci_driver parport_serial_pci_driver = {
.name = "parport_serial",
.id_table = parport_serial_pci_tbl,
.probe = parport_serial_pci_probe,
.remove = parport_serial_pci_remove,
#ifdef CONFIG_PM
.suspend = parport_serial_pci_suspend,
.resume = parport_serial_pci_resume,
#endif
};
static int __init parport_serial_init (void)
{
return pci_register_driver (&parport_serial_pci_driver);
}
static void __exit parport_serial_exit (void)
{
pci_unregister_driver (&parport_serial_pci_driver);
return;
}
MODULE_AUTHOR("Tim Waugh <twaugh@redhat.com>");
MODULE_DESCRIPTION("Driver for common parallel+serial multi-I/O PCI cards");
MODULE_LICENSE("GPL");
module_init(parport_serial_init);
module_exit(parport_serial_exit);

View file

@ -0,0 +1,378 @@
/* parport_sunbpp.c: Parallel-port routines for SBUS
*
* Author: Derrick J. Brashear <shadow@dementia.org>
*
* based on work by:
* Phil Blundell <philb@gnu.org>
* Tim Waugh <tim@cyberelk.demon.co.uk>
* Jose Renau <renau@acm.org>
* David Campbell <campbell@tirian.che.curtin.edu.au>
* Grant Guenther <grant@torque.net>
* Eddie C. Dost <ecd@skynet.be>
* Stephen Williams (steve@icarus.com)
* Gus Baldauf (gbaldauf@ix.netcom.com)
* Peter Zaitcev
* Tom Dyas
*
* Updated to new SBUS device framework: David S. Miller <davem@davemloft.net>
*
*/
#include <linux/string.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/parport.h>
#include <asm/ptrace.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/oplib.h> /* OpenProm Library */
#include <asm/dma.h> /* BPP uses LSI 64854 for DMA */
#include <asm/irq.h>
#include <asm/sunbpp.h>
#undef __SUNBPP_DEBUG
#ifdef __SUNBPP_DEBUG
#define dprintk(x) printk x
#else
#define dprintk(x)
#endif
static void parport_sunbpp_disable_irq(struct parport *p)
{
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
u32 tmp;
tmp = sbus_readl(&regs->p_csr);
tmp &= ~DMA_INT_ENAB;
sbus_writel(tmp, &regs->p_csr);
}
static void parport_sunbpp_enable_irq(struct parport *p)
{
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
u32 tmp;
tmp = sbus_readl(&regs->p_csr);
tmp |= DMA_INT_ENAB;
sbus_writel(tmp, &regs->p_csr);
}
static void parport_sunbpp_write_data(struct parport *p, unsigned char d)
{
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
sbus_writeb(d, &regs->p_dr);
dprintk((KERN_DEBUG "wrote 0x%x\n", d));
}
static unsigned char parport_sunbpp_read_data(struct parport *p)
{
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
return sbus_readb(&regs->p_dr);
}
static unsigned char status_sunbpp_to_pc(struct parport *p)
{
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
unsigned char bits = 0;
unsigned char value_tcr = sbus_readb(&regs->p_tcr);
unsigned char value_ir = sbus_readb(&regs->p_ir);
if (!(value_ir & P_IR_ERR))
bits |= PARPORT_STATUS_ERROR;
if (!(value_ir & P_IR_SLCT))
bits |= PARPORT_STATUS_SELECT;
if (!(value_ir & P_IR_PE))
bits |= PARPORT_STATUS_PAPEROUT;
if (value_tcr & P_TCR_ACK)
bits |= PARPORT_STATUS_ACK;
if (!(value_tcr & P_TCR_BUSY))
bits |= PARPORT_STATUS_BUSY;
dprintk((KERN_DEBUG "tcr 0x%x ir 0x%x\n", value_tcr, value_ir));
dprintk((KERN_DEBUG "read status 0x%x\n", bits));
return bits;
}
static unsigned char control_sunbpp_to_pc(struct parport *p)
{
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
unsigned char bits = 0;
unsigned char value_tcr = sbus_readb(&regs->p_tcr);
unsigned char value_or = sbus_readb(&regs->p_or);
if (!(value_tcr & P_TCR_DS))
bits |= PARPORT_CONTROL_STROBE;
if (!(value_or & P_OR_AFXN))
bits |= PARPORT_CONTROL_AUTOFD;
if (!(value_or & P_OR_INIT))
bits |= PARPORT_CONTROL_INIT;
if (value_or & P_OR_SLCT_IN)
bits |= PARPORT_CONTROL_SELECT;
dprintk((KERN_DEBUG "tcr 0x%x or 0x%x\n", value_tcr, value_or));
dprintk((KERN_DEBUG "read control 0x%x\n", bits));
return bits;
}
static unsigned char parport_sunbpp_read_control(struct parport *p)
{
return control_sunbpp_to_pc(p);
}
static unsigned char parport_sunbpp_frob_control(struct parport *p,
unsigned char mask,
unsigned char val)
{
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
unsigned char value_tcr = sbus_readb(&regs->p_tcr);
unsigned char value_or = sbus_readb(&regs->p_or);
dprintk((KERN_DEBUG "frob1: tcr 0x%x or 0x%x\n",
value_tcr, value_or));
if (mask & PARPORT_CONTROL_STROBE) {
if (val & PARPORT_CONTROL_STROBE) {
value_tcr &= ~P_TCR_DS;
} else {
value_tcr |= P_TCR_DS;
}
}
if (mask & PARPORT_CONTROL_AUTOFD) {
if (val & PARPORT_CONTROL_AUTOFD) {
value_or &= ~P_OR_AFXN;
} else {
value_or |= P_OR_AFXN;
}
}
if (mask & PARPORT_CONTROL_INIT) {
if (val & PARPORT_CONTROL_INIT) {
value_or &= ~P_OR_INIT;
} else {
value_or |= P_OR_INIT;
}
}
if (mask & PARPORT_CONTROL_SELECT) {
if (val & PARPORT_CONTROL_SELECT) {
value_or |= P_OR_SLCT_IN;
} else {
value_or &= ~P_OR_SLCT_IN;
}
}
sbus_writeb(value_or, &regs->p_or);
sbus_writeb(value_tcr, &regs->p_tcr);
dprintk((KERN_DEBUG "frob2: tcr 0x%x or 0x%x\n",
value_tcr, value_or));
return parport_sunbpp_read_control(p);
}
static void parport_sunbpp_write_control(struct parport *p, unsigned char d)
{
const unsigned char wm = (PARPORT_CONTROL_STROBE |
PARPORT_CONTROL_AUTOFD |
PARPORT_CONTROL_INIT |
PARPORT_CONTROL_SELECT);
parport_sunbpp_frob_control (p, wm, d & wm);
}
static unsigned char parport_sunbpp_read_status(struct parport *p)
{
return status_sunbpp_to_pc(p);
}
static void parport_sunbpp_data_forward (struct parport *p)
{
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
unsigned char value_tcr = sbus_readb(&regs->p_tcr);
dprintk((KERN_DEBUG "forward\n"));
value_tcr &= ~P_TCR_DIR;
sbus_writeb(value_tcr, &regs->p_tcr);
}
static void parport_sunbpp_data_reverse (struct parport *p)
{
struct bpp_regs __iomem *regs = (struct bpp_regs __iomem *)p->base;
u8 val = sbus_readb(&regs->p_tcr);
dprintk((KERN_DEBUG "reverse\n"));
val |= P_TCR_DIR;
sbus_writeb(val, &regs->p_tcr);
}
static void parport_sunbpp_init_state(struct pardevice *dev, struct parport_state *s)
{
s->u.pc.ctr = 0xc;
s->u.pc.ecr = 0x0;
}
static void parport_sunbpp_save_state(struct parport *p, struct parport_state *s)
{
s->u.pc.ctr = parport_sunbpp_read_control(p);
}
static void parport_sunbpp_restore_state(struct parport *p, struct parport_state *s)
{
parport_sunbpp_write_control(p, s->u.pc.ctr);
}
static struct parport_operations parport_sunbpp_ops =
{
.write_data = parport_sunbpp_write_data,
.read_data = parport_sunbpp_read_data,
.write_control = parport_sunbpp_write_control,
.read_control = parport_sunbpp_read_control,
.frob_control = parport_sunbpp_frob_control,
.read_status = parport_sunbpp_read_status,
.enable_irq = parport_sunbpp_enable_irq,
.disable_irq = parport_sunbpp_disable_irq,
.data_forward = parport_sunbpp_data_forward,
.data_reverse = parport_sunbpp_data_reverse,
.init_state = parport_sunbpp_init_state,
.save_state = parport_sunbpp_save_state,
.restore_state = parport_sunbpp_restore_state,
.epp_write_data = parport_ieee1284_epp_write_data,
.epp_read_data = parport_ieee1284_epp_read_data,
.epp_write_addr = parport_ieee1284_epp_write_addr,
.epp_read_addr = parport_ieee1284_epp_read_addr,
.ecp_write_data = parport_ieee1284_ecp_write_data,
.ecp_read_data = parport_ieee1284_ecp_read_data,
.ecp_write_addr = parport_ieee1284_ecp_write_addr,
.compat_write_data = parport_ieee1284_write_compat,
.nibble_read_data = parport_ieee1284_read_nibble,
.byte_read_data = parport_ieee1284_read_byte,
.owner = THIS_MODULE,
};
static int bpp_probe(struct platform_device *op)
{
struct parport_operations *ops;
struct bpp_regs __iomem *regs;
int irq, dma, err = 0, size;
unsigned char value_tcr;
void __iomem *base;
struct parport *p;
irq = op->archdata.irqs[0];
base = of_ioremap(&op->resource[0], 0,
resource_size(&op->resource[0]),
"sunbpp");
if (!base)
return -ENODEV;
size = resource_size(&op->resource[0]);
dma = PARPORT_DMA_NONE;
ops = kmemdup(&parport_sunbpp_ops, sizeof(struct parport_operations),
GFP_KERNEL);
if (!ops)
goto out_unmap;
dprintk(("register_port\n"));
if (!(p = parport_register_port((unsigned long)base, irq, dma, ops)))
goto out_free_ops;
p->size = size;
p->dev = &op->dev;
if ((err = request_irq(p->irq, parport_irq_handler,
IRQF_SHARED, p->name, p)) != 0) {
goto out_put_port;
}
parport_sunbpp_enable_irq(p);
regs = (struct bpp_regs __iomem *)p->base;
value_tcr = sbus_readb(&regs->p_tcr);
value_tcr &= ~P_TCR_DIR;
sbus_writeb(value_tcr, &regs->p_tcr);
printk(KERN_INFO "%s: sunbpp at 0x%lx\n", p->name, p->base);
dev_set_drvdata(&op->dev, p);
parport_announce_port(p);
return 0;
out_put_port:
parport_put_port(p);
out_free_ops:
kfree(ops);
out_unmap:
of_iounmap(&op->resource[0], base, size);
return err;
}
static int bpp_remove(struct platform_device *op)
{
struct parport *p = dev_get_drvdata(&op->dev);
struct parport_operations *ops = p->ops;
parport_remove_port(p);
if (p->irq != PARPORT_IRQ_NONE) {
parport_sunbpp_disable_irq(p);
free_irq(p->irq, p);
}
of_iounmap(&op->resource[0], (void __iomem *) p->base, p->size);
parport_put_port(p);
kfree(ops);
dev_set_drvdata(&op->dev, NULL);
return 0;
}
static const struct of_device_id bpp_match[] = {
{
.name = "SUNW,bpp",
},
{},
};
MODULE_DEVICE_TABLE(of, bpp_match);
static struct platform_driver bpp_sbus_driver = {
.driver = {
.name = "bpp",
.owner = THIS_MODULE,
.of_match_table = bpp_match,
},
.probe = bpp_probe,
.remove = bpp_remove,
};
module_platform_driver(bpp_sbus_driver);
MODULE_AUTHOR("Derrick J Brashear");
MODULE_DESCRIPTION("Parport Driver for Sparc bidirectional Port");
MODULE_SUPPORTED_DEVICE("Sparc Bidirectional Parallel Port");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");

282
drivers/parport/probe.c Normal file
View file

@ -0,0 +1,282 @@
/*
* Parallel port device probing code
*
* Authors: Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
* Philip Blundell <philb@gnu.org>
*/
#include <linux/module.h>
#include <linux/parport.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
static const struct {
const char *token;
const char *descr;
} classes[] = {
{ "", "Legacy device" },
{ "PRINTER", "Printer" },
{ "MODEM", "Modem" },
{ "NET", "Network device" },
{ "HDC", "Hard disk" },
{ "PCMCIA", "PCMCIA" },
{ "MEDIA", "Multimedia device" },
{ "FDC", "Floppy disk" },
{ "PORTS", "Ports" },
{ "SCANNER", "Scanner" },
{ "DIGICAM", "Digital camera" },
{ "", "Unknown device" },
{ "", "Unspecified" },
{ "SCSIADAPTER", "SCSI adapter" },
{ NULL, NULL }
};
static void pretty_print(struct parport *port, int device)
{
struct parport_device_info *info = &port->probe_info[device + 1];
printk(KERN_INFO "%s", port->name);
if (device >= 0)
printk (" (addr %d)", device);
printk (": %s", classes[info->class].descr);
if (info->class)
printk(", %s %s", info->mfr, info->model);
printk("\n");
}
static void parse_data(struct parport *port, int device, char *str)
{
char *txt = kmalloc(strlen(str)+1, GFP_KERNEL);
char *p = txt, *q;
int guessed_class = PARPORT_CLASS_UNSPEC;
struct parport_device_info *info = &port->probe_info[device + 1];
if (!txt) {
printk(KERN_WARNING "%s probe: memory squeeze\n", port->name);
return;
}
strcpy(txt, str);
while (p) {
char *sep;
q = strchr(p, ';');
if (q) *q = 0;
sep = strchr(p, ':');
if (sep) {
char *u;
*(sep++) = 0;
/* Get rid of trailing blanks */
u = sep + strlen (sep) - 1;
while (u >= p && *u == ' ')
*u-- = '\0';
u = p;
while (*u) {
*u = toupper(*u);
u++;
}
if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) {
kfree(info->mfr);
info->mfr = kstrdup(sep, GFP_KERNEL);
} else if (!strcmp(p, "MDL") || !strcmp(p, "MODEL")) {
kfree(info->model);
info->model = kstrdup(sep, GFP_KERNEL);
} else if (!strcmp(p, "CLS") || !strcmp(p, "CLASS")) {
int i;
kfree(info->class_name);
info->class_name = kstrdup(sep, GFP_KERNEL);
for (u = sep; *u; u++)
*u = toupper(*u);
for (i = 0; classes[i].token; i++) {
if (!strcmp(classes[i].token, sep)) {
info->class = i;
goto rock_on;
}
}
printk(KERN_WARNING "%s probe: warning, class '%s' not understood.\n", port->name, sep);
info->class = PARPORT_CLASS_OTHER;
} else if (!strcmp(p, "CMD") ||
!strcmp(p, "COMMAND SET")) {
kfree(info->cmdset);
info->cmdset = kstrdup(sep, GFP_KERNEL);
/* if it speaks printer language, it's
probably a printer */
if (strstr(sep, "PJL") || strstr(sep, "PCL"))
guessed_class = PARPORT_CLASS_PRINTER;
} else if (!strcmp(p, "DES") || !strcmp(p, "DESCRIPTION")) {
kfree(info->description);
info->description = kstrdup(sep, GFP_KERNEL);
}
}
rock_on:
if (q)
p = q + 1;
else
p = NULL;
}
/* If the device didn't tell us its class, maybe we have managed to
guess one from the things it did say. */
if (info->class == PARPORT_CLASS_UNSPEC)
info->class = guessed_class;
pretty_print (port, device);
kfree(txt);
}
/* Read up to count-1 bytes of device id. Terminate buffer with
* '\0'. Buffer begins with two Device ID length bytes as given by
* device. */
static ssize_t parport_read_device_id (struct parport *port, char *buffer,
size_t count)
{
unsigned char length[2];
unsigned lelen, belen;
size_t idlens[4];
unsigned numidlens;
unsigned current_idlen;
ssize_t retval;
size_t len;
/* First two bytes are MSB,LSB of inclusive length. */
retval = parport_read (port, length, 2);
if (retval < 0)
return retval;
if (retval != 2)
return -EIO;
if (count < 2)
return 0;
memcpy(buffer, length, 2);
len = 2;
/* Some devices wrongly send LE length, and some send it two
* bytes short. Construct a sorted array of lengths to try. */
belen = (length[0] << 8) + length[1];
lelen = (length[1] << 8) + length[0];
idlens[0] = min(belen, lelen);
idlens[1] = idlens[0]+2;
if (belen != lelen) {
int off = 2;
/* Don't try lengths of 0x100 and 0x200 as 1 and 2 */
if (idlens[0] <= 2)
off = 0;
idlens[off] = max(belen, lelen);
idlens[off+1] = idlens[off]+2;
numidlens = off+2;
}
else {
/* Some devices don't truly implement Device ID, but
* just return constant nibble forever. This catches
* also those cases. */
if (idlens[0] == 0 || idlens[0] > 0xFFF) {
printk (KERN_DEBUG "%s: reported broken Device ID"
" length of %#zX bytes\n",
port->name, idlens[0]);
return -EIO;
}
numidlens = 2;
}
/* Try to respect the given ID length despite all the bugs in
* the ID length. Read according to shortest possible ID
* first. */
for (current_idlen = 0; current_idlen < numidlens; ++current_idlen) {
size_t idlen = idlens[current_idlen];
if (idlen+1 >= count)
break;
retval = parport_read (port, buffer+len, idlen-len);
if (retval < 0)
return retval;
len += retval;
if (port->physport->ieee1284.phase != IEEE1284_PH_HBUSY_DAVAIL) {
if (belen != len) {
printk (KERN_DEBUG "%s: Device ID was %zd bytes"
" while device told it would be %d"
" bytes\n",
port->name, len, belen);
}
goto done;
}
/* This might end reading the Device ID too
* soon. Hopefully the needed fields were already in
* the first 256 bytes or so that we must have read so
* far. */
if (buffer[len-1] == ';') {
printk (KERN_DEBUG "%s: Device ID reading stopped"
" before device told data not available. "
"Current idlen %u of %u, len bytes %02X %02X\n",
port->name, current_idlen, numidlens,
length[0], length[1]);
goto done;
}
}
if (current_idlen < numidlens) {
/* Buffer not large enough, read to end of buffer. */
size_t idlen, len2;
if (len+1 < count) {
retval = parport_read (port, buffer+len, count-len-1);
if (retval < 0)
return retval;
len += retval;
}
/* Read the whole ID since some devices would not
* otherwise give back the Device ID from beginning
* next time when asked. */
idlen = idlens[current_idlen];
len2 = len;
while(len2 < idlen && retval > 0) {
char tmp[4];
retval = parport_read (port, tmp,
min(sizeof tmp, idlen-len2));
if (retval < 0)
return retval;
len2 += retval;
}
}
/* In addition, there are broken devices out there that don't
even finish off with a semi-colon. We do not need to care
about those at this time. */
done:
buffer[len] = '\0';
return len;
}
/* Get Std 1284 Device ID. */
ssize_t parport_device_id (int devnum, char *buffer, size_t count)
{
ssize_t retval = -ENXIO;
struct pardevice *dev = parport_open (devnum, "Device ID probe");
if (!dev)
return -ENXIO;
parport_claim_or_block (dev);
/* Negotiate to compatibility mode, and then to device ID
* mode. (This so that we start form beginning of device ID if
* already in device ID mode.) */
parport_negotiate (dev->port, IEEE1284_MODE_COMPAT);
retval = parport_negotiate (dev->port,
IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID);
if (!retval) {
retval = parport_read_device_id (dev->port, buffer, count);
parport_negotiate (dev->port, IEEE1284_MODE_COMPAT);
if (retval > 2)
parse_data (dev->port, dev->daisy, buffer+2);
}
parport_release (dev);
parport_close (dev);
return retval;
}

608
drivers/parport/procfs.c Normal file
View file

@ -0,0 +1,608 @@
/* Sysctl interface for parport devices.
*
* Authors: David Campbell
* Tim Waugh <tim@cyberelk.demon.co.uk>
* Philip Blundell <philb@gnu.org>
* Andrea Arcangeli
* Riccardo Facchetti <fizban@tin.it>
*
* based on work by Grant Guenther <grant@torque.net>
* and Philip Blundell
*
* Cleaned up include files - Russell King <linux@arm.uk.linux.org>
*/
#include <linux/string.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/parport.h>
#include <linux/ctype.h>
#include <linux/sysctl.h>
#include <asm/uaccess.h>
#if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS)
#define PARPORT_MIN_TIMESLICE_VALUE 1ul
#define PARPORT_MAX_TIMESLICE_VALUE ((unsigned long) HZ)
#define PARPORT_MIN_SPINTIME_VALUE 1
#define PARPORT_MAX_SPINTIME_VALUE 1000
static int do_active_device(struct ctl_table *table, int write,
void __user *result, size_t *lenp, loff_t *ppos)
{
struct parport *port = (struct parport *)table->extra1;
char buffer[256];
struct pardevice *dev;
int len = 0;
if (write) /* can't happen anyway */
return -EACCES;
if (*ppos) {
*lenp = 0;
return 0;
}
for (dev = port->devices; dev ; dev = dev->next) {
if(dev == port->cad) {
len += sprintf(buffer, "%s\n", dev->name);
}
}
if(!len) {
len += sprintf(buffer, "%s\n", "none");
}
if (len > *lenp)
len = *lenp;
else
*lenp = len;
*ppos += len;
return copy_to_user(result, buffer, len) ? -EFAULT : 0;
}
#ifdef CONFIG_PARPORT_1284
static int do_autoprobe(struct ctl_table *table, int write,
void __user *result, size_t *lenp, loff_t *ppos)
{
struct parport_device_info *info = table->extra2;
const char *str;
char buffer[256];
int len = 0;
if (write) /* permissions stop this */
return -EACCES;
if (*ppos) {
*lenp = 0;
return 0;
}
if ((str = info->class_name) != NULL)
len += sprintf (buffer + len, "CLASS:%s;\n", str);
if ((str = info->model) != NULL)
len += sprintf (buffer + len, "MODEL:%s;\n", str);
if ((str = info->mfr) != NULL)
len += sprintf (buffer + len, "MANUFACTURER:%s;\n", str);
if ((str = info->description) != NULL)
len += sprintf (buffer + len, "DESCRIPTION:%s;\n", str);
if ((str = info->cmdset) != NULL)
len += sprintf (buffer + len, "COMMAND SET:%s;\n", str);
if (len > *lenp)
len = *lenp;
else
*lenp = len;
*ppos += len;
return copy_to_user (result, buffer, len) ? -EFAULT : 0;
}
#endif /* IEEE1284.3 support. */
static int do_hardware_base_addr(struct ctl_table *table, int write,
void __user *result,
size_t *lenp, loff_t *ppos)
{
struct parport *port = (struct parport *)table->extra1;
char buffer[20];
int len = 0;
if (*ppos) {
*lenp = 0;
return 0;
}
if (write) /* permissions prevent this anyway */
return -EACCES;
len += sprintf (buffer, "%lu\t%lu\n", port->base, port->base_hi);
if (len > *lenp)
len = *lenp;
else
*lenp = len;
*ppos += len;
return copy_to_user(result, buffer, len) ? -EFAULT : 0;
}
static int do_hardware_irq(struct ctl_table *table, int write,
void __user *result,
size_t *lenp, loff_t *ppos)
{
struct parport *port = (struct parport *)table->extra1;
char buffer[20];
int len = 0;
if (*ppos) {
*lenp = 0;
return 0;
}
if (write) /* permissions prevent this anyway */
return -EACCES;
len += sprintf (buffer, "%d\n", port->irq);
if (len > *lenp)
len = *lenp;
else
*lenp = len;
*ppos += len;
return copy_to_user(result, buffer, len) ? -EFAULT : 0;
}
static int do_hardware_dma(struct ctl_table *table, int write,
void __user *result,
size_t *lenp, loff_t *ppos)
{
struct parport *port = (struct parport *)table->extra1;
char buffer[20];
int len = 0;
if (*ppos) {
*lenp = 0;
return 0;
}
if (write) /* permissions prevent this anyway */
return -EACCES;
len += sprintf (buffer, "%d\n", port->dma);
if (len > *lenp)
len = *lenp;
else
*lenp = len;
*ppos += len;
return copy_to_user(result, buffer, len) ? -EFAULT : 0;
}
static int do_hardware_modes(struct ctl_table *table, int write,
void __user *result,
size_t *lenp, loff_t *ppos)
{
struct parport *port = (struct parport *)table->extra1;
char buffer[40];
int len = 0;
if (*ppos) {
*lenp = 0;
return 0;
}
if (write) /* permissions prevent this anyway */
return -EACCES;
{
#define printmode(x) {if(port->modes&PARPORT_MODE_##x){len+=sprintf(buffer+len,"%s%s",f?",":"",#x);f++;}}
int f = 0;
printmode(PCSPP);
printmode(TRISTATE);
printmode(COMPAT);
printmode(EPP);
printmode(ECP);
printmode(DMA);
#undef printmode
}
buffer[len++] = '\n';
if (len > *lenp)
len = *lenp;
else
*lenp = len;
*ppos += len;
return copy_to_user(result, buffer, len) ? -EFAULT : 0;
}
#define PARPORT_PORT_DIR(CHILD) { .procname = NULL, .mode = 0555, .child = CHILD }
#define PARPORT_PARPORT_DIR(CHILD) { .procname = "parport", \
.mode = 0555, .child = CHILD }
#define PARPORT_DEV_DIR(CHILD) { .procname = "dev", .mode = 0555, .child = CHILD }
#define PARPORT_DEVICES_ROOT_DIR { .procname = "devices", \
.mode = 0555, .child = NULL }
static const unsigned long parport_min_timeslice_value =
PARPORT_MIN_TIMESLICE_VALUE;
static const unsigned long parport_max_timeslice_value =
PARPORT_MAX_TIMESLICE_VALUE;
static const int parport_min_spintime_value =
PARPORT_MIN_SPINTIME_VALUE;
static const int parport_max_spintime_value =
PARPORT_MAX_SPINTIME_VALUE;
struct parport_sysctl_table {
struct ctl_table_header *sysctl_header;
struct ctl_table vars[12];
struct ctl_table device_dir[2];
struct ctl_table port_dir[2];
struct ctl_table parport_dir[2];
struct ctl_table dev_dir[2];
};
static const struct parport_sysctl_table parport_sysctl_template = {
.sysctl_header = NULL,
{
{
.procname = "spintime",
.data = NULL,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = (void*) &parport_min_spintime_value,
.extra2 = (void*) &parport_max_spintime_value
},
{
.procname = "base-addr",
.data = NULL,
.maxlen = 0,
.mode = 0444,
.proc_handler = do_hardware_base_addr
},
{
.procname = "irq",
.data = NULL,
.maxlen = 0,
.mode = 0444,
.proc_handler = do_hardware_irq
},
{
.procname = "dma",
.data = NULL,
.maxlen = 0,
.mode = 0444,
.proc_handler = do_hardware_dma
},
{
.procname = "modes",
.data = NULL,
.maxlen = 0,
.mode = 0444,
.proc_handler = do_hardware_modes
},
PARPORT_DEVICES_ROOT_DIR,
#ifdef CONFIG_PARPORT_1284
{
.procname = "autoprobe",
.data = NULL,
.maxlen = 0,
.mode = 0444,
.proc_handler = do_autoprobe
},
{
.procname = "autoprobe0",
.data = NULL,
.maxlen = 0,
.mode = 0444,
.proc_handler = do_autoprobe
},
{
.procname = "autoprobe1",
.data = NULL,
.maxlen = 0,
.mode = 0444,
.proc_handler = do_autoprobe
},
{
.procname = "autoprobe2",
.data = NULL,
.maxlen = 0,
.mode = 0444,
.proc_handler = do_autoprobe
},
{
.procname = "autoprobe3",
.data = NULL,
.maxlen = 0,
.mode = 0444,
.proc_handler = do_autoprobe
},
#endif /* IEEE 1284 support */
{}
},
{
{
.procname = "active",
.data = NULL,
.maxlen = 0,
.mode = 0444,
.proc_handler = do_active_device
},
{}
},
{
PARPORT_PORT_DIR(NULL),
{}
},
{
PARPORT_PARPORT_DIR(NULL),
{}
},
{
PARPORT_DEV_DIR(NULL),
{}
}
};
struct parport_device_sysctl_table
{
struct ctl_table_header *sysctl_header;
struct ctl_table vars[2];
struct ctl_table device_dir[2];
struct ctl_table devices_root_dir[2];
struct ctl_table port_dir[2];
struct ctl_table parport_dir[2];
struct ctl_table dev_dir[2];
};
static const struct parport_device_sysctl_table
parport_device_sysctl_template = {
.sysctl_header = NULL,
{
{
.procname = "timeslice",
.data = NULL,
.maxlen = sizeof(unsigned long),
.mode = 0644,
.proc_handler = proc_doulongvec_ms_jiffies_minmax,
.extra1 = (void*) &parport_min_timeslice_value,
.extra2 = (void*) &parport_max_timeslice_value
},
},
{
{
.procname = NULL,
.data = NULL,
.maxlen = 0,
.mode = 0555,
.child = NULL
},
{}
},
{
PARPORT_DEVICES_ROOT_DIR,
{}
},
{
PARPORT_PORT_DIR(NULL),
{}
},
{
PARPORT_PARPORT_DIR(NULL),
{}
},
{
PARPORT_DEV_DIR(NULL),
{}
}
};
struct parport_default_sysctl_table
{
struct ctl_table_header *sysctl_header;
struct ctl_table vars[3];
struct ctl_table default_dir[2];
struct ctl_table parport_dir[2];
struct ctl_table dev_dir[2];
};
static struct parport_default_sysctl_table
parport_default_sysctl_table = {
.sysctl_header = NULL,
{
{
.procname = "timeslice",
.data = &parport_default_timeslice,
.maxlen = sizeof(parport_default_timeslice),
.mode = 0644,
.proc_handler = proc_doulongvec_ms_jiffies_minmax,
.extra1 = (void*) &parport_min_timeslice_value,
.extra2 = (void*) &parport_max_timeslice_value
},
{
.procname = "spintime",
.data = &parport_default_spintime,
.maxlen = sizeof(parport_default_spintime),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = (void*) &parport_min_spintime_value,
.extra2 = (void*) &parport_max_spintime_value
},
{}
},
{
{
.procname = "default",
.mode = 0555,
.child = parport_default_sysctl_table.vars
},
{}
},
{
PARPORT_PARPORT_DIR(parport_default_sysctl_table.default_dir),
{}
},
{
PARPORT_DEV_DIR(parport_default_sysctl_table.parport_dir),
{}
}
};
int parport_proc_register(struct parport *port)
{
struct parport_sysctl_table *t;
int i;
t = kmemdup(&parport_sysctl_template, sizeof(*t), GFP_KERNEL);
if (t == NULL)
return -ENOMEM;
t->device_dir[0].extra1 = port;
for (i = 0; i < 5; i++)
t->vars[i].extra1 = port;
t->vars[0].data = &port->spintime;
t->vars[5].child = t->device_dir;
for (i = 0; i < 5; i++)
t->vars[6 + i].extra2 = &port->probe_info[i];
t->port_dir[0].procname = port->name;
t->port_dir[0].child = t->vars;
t->parport_dir[0].child = t->port_dir;
t->dev_dir[0].child = t->parport_dir;
t->sysctl_header = register_sysctl_table(t->dev_dir);
if (t->sysctl_header == NULL) {
kfree(t);
t = NULL;
}
port->sysctl_table = t;
return 0;
}
int parport_proc_unregister(struct parport *port)
{
if (port->sysctl_table) {
struct parport_sysctl_table *t = port->sysctl_table;
port->sysctl_table = NULL;
unregister_sysctl_table(t->sysctl_header);
kfree(t);
}
return 0;
}
int parport_device_proc_register(struct pardevice *device)
{
struct parport_device_sysctl_table *t;
struct parport * port = device->port;
t = kmemdup(&parport_device_sysctl_template, sizeof(*t), GFP_KERNEL);
if (t == NULL)
return -ENOMEM;
t->dev_dir[0].child = t->parport_dir;
t->parport_dir[0].child = t->port_dir;
t->port_dir[0].procname = port->name;
t->port_dir[0].child = t->devices_root_dir;
t->devices_root_dir[0].child = t->device_dir;
t->device_dir[0].procname = device->name;
t->device_dir[0].child = t->vars;
t->vars[0].data = &device->timeslice;
t->sysctl_header = register_sysctl_table(t->dev_dir);
if (t->sysctl_header == NULL) {
kfree(t);
t = NULL;
}
device->sysctl_table = t;
return 0;
}
int parport_device_proc_unregister(struct pardevice *device)
{
if (device->sysctl_table) {
struct parport_device_sysctl_table *t = device->sysctl_table;
device->sysctl_table = NULL;
unregister_sysctl_table(t->sysctl_header);
kfree(t);
}
return 0;
}
static int __init parport_default_proc_register(void)
{
parport_default_sysctl_table.sysctl_header =
register_sysctl_table(parport_default_sysctl_table.dev_dir);
return 0;
}
static void __exit parport_default_proc_unregister(void)
{
if (parport_default_sysctl_table.sysctl_header) {
unregister_sysctl_table(parport_default_sysctl_table.
sysctl_header);
parport_default_sysctl_table.sysctl_header = NULL;
}
}
#else /* no sysctl or no procfs*/
int parport_proc_register(struct parport *pp)
{
return 0;
}
int parport_proc_unregister(struct parport *pp)
{
return 0;
}
int parport_device_proc_register(struct pardevice *device)
{
return 0;
}
int parport_device_proc_unregister(struct pardevice *device)
{
return 0;
}
static int __init parport_default_proc_register (void)
{
return 0;
}
static void __exit parport_default_proc_unregister (void)
{
}
#endif
module_init(parport_default_proc_register)
module_exit(parport_default_proc_unregister)

1032
drivers/parport/share.c Normal file

File diff suppressed because it is too large Load diff