mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 09:19:51 +00:00
![Leon Romanovsky](/assets/img/avatar_default.png)
MODULE_SUPPORTED_DEVICE was added in pre-git era and never was implemented. We can safely remove it, because the kernel has grown to have many more reliable mechanisms to determine if device is supported or not. Signed-off-by: Leon Romanovsky <leonro@nvidia.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
365 lines
9.7 KiB
C
365 lines
9.7 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* 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
|
|
|
|
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)
|
|
{
|
|
pr_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)
|
|
{
|
|
pr_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)
|
|
{
|
|
pr_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;
|
|
|
|
pr_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);
|
|
pr_debug("read_status %02x\n", status);
|
|
return status;
|
|
}
|
|
|
|
static int use_cnt;
|
|
|
|
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)
|
|
{
|
|
pr_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)
|
|
{
|
|
pr_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;
|
|
pr_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_LICENSE("GPL");
|
|
|
|
module_init(parport_mfc3_init)
|
|
module_exit(parport_mfc3_exit)
|