2005-04-17 02:20:36 +04:00
/* 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 ;
}
#if 0 /* currently unused */
static unsigned char status_pc_to_mfc3 ( unsigned char status )
{
unsigned char ret = 1 ;
if ( status & PARPORT_STATUS_BUSY ) /* Busy */
ret & = ~ 1 ;
if ( status & PARPORT_STATUS_ACK ) /* Ack */
ret | = 8 ;
if ( status & PARPORT_STATUS_PAPEROUT ) /* PaperOut */
ret | = 2 ;
if ( status & PARPORT_STATUS_SELECT ) /* select */
ret | = 4 ;
if ( status & PARPORT_STATUS_ERROR ) /* error */
ret | = 16 ;
return ret ;
}
# endif
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 ;
}
#if 0 /* currently unused */
static void mfc3_write_status ( struct parport * p , unsigned char status )
{
DPRINTK ( KERN_DEBUG " write_status %02x \n " , status ) ;
pia ( p ) - > ppra = ( pia ( p ) - > ppra & 0xe0 ) | status_pc_to_mfc3 ( status ) ;
}
# endif
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 ;
}
#if 0 /* currently unused */
static void mfc3_change_mode ( struct parport * p , int m )
{
/* XXX: This port only has one mode, and I am
not sure about the corresponding PC - style mode */
}
# endif
static int use_cnt = 0 ;
static irqreturn_t mfc3_interrupt ( int irq , void * dev_id , struct pt_regs * regs )
{
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 ( irq , this_port [ i ] , regs ) ;
}
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 = ( struct pia * ) 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 )
2006-07-02 06:29:38 +04:00
if ( request_irq ( IRQ_AMIGA_PORTS , mfc3_interrupt , IRQF_SHARED , p - > name , & pp_mfc3_ops ) )
2005-04-17 02:20:36 +04:00
goto out_irq ;
}
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 Paralllel Port " ) ;
MODULE_SUPPORTED_DEVICE ( " Multiface 3 Parallel Port " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( parport_mfc3_init )
module_exit ( parport_mfc3_exit )