2005-04-17 02:20:36 +04:00
/*
* 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/sched.h>
# include <asm/current.h>
# include <asm/uaccess.h>
# undef DEBUG
# ifdef DEBUG
2006-10-04 01:25:14 +04:00
# define DPRINTK(stuff...) printk(stuff)
2005-04-17 02:20:36 +04:00
# 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. */
2006-10-04 01:25:14 +04:00
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 ) ;
2005-04-17 02:20:36 +04:00
/* Add a device to the discovered topology. */
2006-10-04 01:25:14 +04:00
static void add_dev ( int devnum , struct parport * port , int daisy )
2005-04-17 02:20:36 +04:00
{
struct daisydev * newdev , * * p ;
2006-10-04 01:25:14 +04:00
newdev = kmalloc ( sizeof ( struct daisydev ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
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). */
2006-10-04 01:25:14 +04:00
static struct parport * clone_parport ( struct parport * real , int muxport )
2005-04-17 02:20:36 +04:00
{
2006-10-04 01:25:14 +04:00
struct parport * extra = parport_register_port ( real - > base ,
2005-04-17 02:20:36 +04:00
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 . */
2006-10-04 01:25:14 +04:00
int parport_daisy_init ( struct parport * port )
2005-04-17 02:20:36 +04:00
{
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 . */
2006-10-04 01:25:14 +04:00
if ( port - > muxport < 0 & & mux_present ( port ) & &
2005-04-17 02:20:36 +04:00
/* don't be fooled: a mux must have 2 or 4 ports. */
2006-10-04 01:25:14 +04:00
( ( num_ports = num_mux_ports ( port ) ) = = 2 | | num_ports = = 4 ) ) {
2005-04-17 02:20:36 +04:00
/* Leave original as port zero. */
port - > muxport = 0 ;
2006-10-04 01:25:14 +04:00
printk ( KERN_INFO
2005-04-17 02:20:36 +04:00
" %s: 1st (default) port of %d-way multiplexor \n " ,
port - > name , num_ports ) ;
for ( i = 1 ; i < num_ports ; i + + ) {
/* Clone the port. */
2006-10-04 01:25:14 +04:00
struct parport * extra = clone_parport ( port , i ) ;
2005-04-17 02:20:36 +04:00
if ( ! extra ) {
2006-10-04 01:25:14 +04:00
if ( signal_pending ( current ) )
2005-04-17 02:20:36 +04:00
break ;
2006-10-04 01:25:14 +04:00
schedule ( ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
2006-10-04 01:25:14 +04:00
printk ( KERN_INFO
2005-04-17 02:20:36 +04:00
" %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 )
2006-10-04 01:25:14 +04:00
select_port ( port ) ;
2005-04-17 02:20:36 +04:00
2006-10-04 01:25:14 +04:00
parport_daisy_deselect_all ( port ) ;
detected + = assign_addrs ( port ) ;
2005-04-17 02:20:36 +04:00
/* Count the potential legacy device at the end. */
2006-10-04 01:25:14 +04:00
add_dev ( numdevs + + , port , - 1 ) ;
2005-04-17 02:20:36 +04:00
/* Find out the legacy device's IEEE 1284 device ID. */
2006-10-04 01:25:14 +04:00
deviceid = kmalloc ( 1024 , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( deviceid ) {
2006-10-04 01:25:14 +04:00
if ( parport_device_id ( numdevs - 1 , deviceid , 1024 ) > 2 )
2005-04-17 02:20:36 +04:00
detected + + ;
2006-10-04 01:25:14 +04:00
kfree ( deviceid ) ;
2005-04-17 02:20:36 +04:00
}
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 . */
2006-10-04 01:25:14 +04:00
parport_daisy_fini ( port ) ;
parport_write_control ( port , PARPORT_CONTROL_SELECT ) ;
udelay ( 50 ) ;
parport_write_control ( port ,
2005-04-17 02:20:36 +04:00
PARPORT_CONTROL_SELECT |
PARPORT_CONTROL_INIT ) ;
2006-10-04 01:25:14 +04:00
udelay ( 50 ) ;
2005-04-17 02:20:36 +04:00
last_try = 1 ;
goto again ;
}
return detected ;
}
/* Forget about devices on a physical port. */
2006-10-04 01:25:14 +04:00
void parport_daisy_fini ( struct parport * port )
2005-04-17 02:20:36 +04:00
{
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 ( ) .
* */
2007-10-19 10:54:26 +04:00
struct pardevice * parport_open ( int devnum , const char * name )
2005-04-17 02:20:36 +04:00
{
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 ) ;
2007-10-19 10:54:26 +04:00
dev = parport_register_device ( port , name , NULL , NULL , NULL , 0 , NULL ) ;
2005-04-17 02:20:36 +04:00
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 ;
2006-10-04 01:25:14 +04:00
parport_claim_or_block ( dev ) ;
2005-04-17 02:20:36 +04:00
selected = port - > daisy ;
2006-10-04 01:25:14 +04:00
parport_release ( dev ) ;
2005-04-17 02:20:36 +04:00
2006-01-06 11:19:46 +03:00
if ( selected ! = daisy ) {
2005-04-17 02:20:36 +04:00
/* No corresponding device. */
2006-10-04 01:25:14 +04:00
parport_unregister_device ( dev ) ;
2005-04-17 02:20:36 +04:00
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 ( ) .
* */
2006-10-04 01:25:14 +04:00
void parport_close ( struct pardevice * dev )
2005-04-17 02:20:36 +04:00
{
2006-10-04 01:25:14 +04:00
parport_unregister_device ( dev ) ;
2005-04-17 02:20:36 +04:00
}
/* Send a daisy-chain-style CPP command packet. */
2006-10-04 01:25:14 +04:00
static int cpp_daisy ( struct parport * port , int cmd )
2005-04-17 02:20:36 +04:00
{
unsigned char s ;
2006-10-04 01:25:14 +04:00
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
2005-04-17 02:20:36 +04:00
| PARPORT_STATUS_PAPEROUT
| PARPORT_STATUS_SELECT
| PARPORT_STATUS_ERROR ) ;
if ( s ! = ( PARPORT_STATUS_BUSY
| PARPORT_STATUS_PAPEROUT
| PARPORT_STATUS_SELECT
| PARPORT_STATUS_ERROR ) ) {
2006-10-04 01:25:14 +04:00
DPRINTK ( KERN_DEBUG " %s: cpp_daisy: aa5500ff(%02x) \n " ,
2005-04-17 02:20:36 +04:00
port - > name , s ) ;
return - ENXIO ;
}
2006-10-04 01:25:14 +04:00
parport_write_data ( port , 0x87 ) ; udelay ( 2 ) ;
s = parport_read_status ( port ) & ( PARPORT_STATUS_BUSY
2005-04-17 02:20:36 +04:00
| PARPORT_STATUS_PAPEROUT
| PARPORT_STATUS_SELECT
| PARPORT_STATUS_ERROR ) ;
if ( s ! = ( PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR ) ) {
2006-10-04 01:25:14 +04:00
DPRINTK ( KERN_DEBUG " %s: cpp_daisy: aa5500ff87(%02x) \n " ,
2005-04-17 02:20:36 +04:00
port - > name , s ) ;
return - ENXIO ;
}
2006-10-04 01:25:14 +04:00
parport_write_data ( port , 0x78 ) ; udelay ( 2 ) ;
parport_write_data ( port , cmd ) ; udelay ( 2 ) ;
parport_frob_control ( port ,
2005-04-17 02:20:36 +04:00
PARPORT_CONTROL_STROBE ,
PARPORT_CONTROL_STROBE ) ;
2006-10-04 01:25:14 +04:00
udelay ( 1 ) ;
s = parport_read_status ( port ) ;
parport_frob_control ( port , PARPORT_CONTROL_STROBE , 0 ) ;
udelay ( 1 ) ;
parport_write_data ( port , 0xff ) ; udelay ( 2 ) ;
2005-04-17 02:20:36 +04:00
return s ;
}
/* Send a mux-style CPP command packet. */
2006-10-04 01:25:14 +04:00
static int cpp_mux ( struct parport * port , int cmd )
2005-04-17 02:20:36 +04:00
{
unsigned char s ;
int rc ;
2006-10-04 01:25:14 +04:00
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 ) ;
2005-04-17 02:20:36 +04:00
2006-10-04 01:25:14 +04:00
s = parport_read_status ( port ) ;
2005-04-17 02:20:36 +04:00
if ( ! ( s & PARPORT_STATUS_ACK ) ) {
2006-10-04 01:25:14 +04:00
DPRINTK ( KERN_DEBUG " %s: cpp_mux: aa55f00f52ad%02x(%02x) \n " ,
2005-04-17 02:20:36 +04:00
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 ;
}
2006-10-04 01:25:14 +04:00
void parport_daisy_deselect_all ( struct parport * port )
2005-04-17 02:20:36 +04:00
{
2006-10-04 01:25:14 +04:00
cpp_daisy ( port , 0x30 ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 01:25:14 +04:00
int parport_daisy_select ( struct parport * port , int daisy , int mode )
2005-04-17 02:20:36 +04:00
{
switch ( mode )
{
// For these modes we should switch to EPP mode:
case IEEE1284_MODE_EPP :
case IEEE1284_MODE_EPPSL :
case IEEE1284_MODE_EPPSWE :
2006-10-04 01:25:14 +04:00
return ! ( cpp_daisy ( port , 0x20 + daisy ) &
2006-01-06 11:19:46 +03:00
PARPORT_STATUS_ERROR ) ;
2005-04-17 02:20:36 +04:00
// For these modes we should switch to ECP mode:
case IEEE1284_MODE_ECP :
case IEEE1284_MODE_ECPRLE :
case IEEE1284_MODE_ECPSWE :
2006-10-04 01:25:14 +04:00
return ! ( cpp_daisy ( port , 0xd0 + daisy ) &
2006-01-06 11:19:46 +03:00
PARPORT_STATUS_ERROR ) ;
2005-04-17 02:20:36 +04:00
// 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 :
2006-10-04 01:25:14 +04:00
return ! ( cpp_daisy ( port , 0xe0 + daisy ) &
2006-01-06 11:19:46 +03:00
PARPORT_STATUS_ERROR ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-10-04 01:25:14 +04:00
static int mux_present ( struct parport * port )
2005-04-17 02:20:36 +04:00
{
2006-10-04 01:25:14 +04:00
return cpp_mux ( port , 0x51 ) = = 3 ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 01:25:14 +04:00
static int num_mux_ports ( struct parport * port )
2005-04-17 02:20:36 +04:00
{
2006-10-04 01:25:14 +04:00
return cpp_mux ( port , 0x58 ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 01:25:14 +04:00
static int select_port ( struct parport * port )
2005-04-17 02:20:36 +04:00
{
int muxport = port - > muxport ;
2006-10-04 01:25:14 +04:00
return cpp_mux ( port , 0x60 + muxport ) = = muxport ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 01:25:14 +04:00
static int assign_addrs ( struct parport * port )
2005-04-17 02:20:36 +04:00
{
2006-01-06 11:19:45 +03:00
unsigned char s ;
2005-04-17 02:20:36 +04:00
unsigned char daisy ;
int thisdev = numdevs ;
int detected ;
char * deviceid ;
2006-10-04 01:25:14 +04:00
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
2005-04-17 02:20:36 +04:00
| PARPORT_STATUS_PAPEROUT
| PARPORT_STATUS_SELECT
| PARPORT_STATUS_ERROR ) ;
if ( s ! = ( PARPORT_STATUS_BUSY
| PARPORT_STATUS_PAPEROUT
| PARPORT_STATUS_SELECT
| PARPORT_STATUS_ERROR ) ) {
2006-10-04 01:25:14 +04:00
DPRINTK ( KERN_DEBUG " %s: assign_addrs: aa5500ff(%02x) \n " ,
2005-04-17 02:20:36 +04:00
port - > name , s ) ;
return 0 ;
}
2006-10-04 01:25:14 +04:00
parport_write_data ( port , 0x87 ) ; udelay ( 2 ) ;
s = parport_read_status ( port ) & ( PARPORT_STATUS_BUSY
2005-04-17 02:20:36 +04:00
| PARPORT_STATUS_PAPEROUT
| PARPORT_STATUS_SELECT
| PARPORT_STATUS_ERROR ) ;
if ( s ! = ( PARPORT_STATUS_SELECT | PARPORT_STATUS_ERROR ) ) {
2006-10-04 01:25:14 +04:00
DPRINTK ( KERN_DEBUG " %s: assign_addrs: aa5500ff87(%02x) \n " ,
2005-04-17 02:20:36 +04:00
port - > name , s ) ;
return 0 ;
}
2006-10-04 01:25:14 +04:00
parport_write_data ( port , 0x78 ) ; udelay ( 2 ) ;
s = parport_read_status ( port ) ;
2005-04-17 02:20:36 +04:00
2006-01-06 11:19:45 +03:00
for ( daisy = 0 ;
( s & ( PARPORT_STATUS_PAPEROUT | PARPORT_STATUS_SELECT ) )
= = ( PARPORT_STATUS_PAPEROUT | PARPORT_STATUS_SELECT )
& & daisy < 4 ;
+ + daisy ) {
2006-10-04 01:25:14 +04:00
parport_write_data ( port , daisy ) ;
udelay ( 2 ) ;
parport_frob_control ( port ,
2005-04-17 02:20:36 +04:00
PARPORT_CONTROL_STROBE ,
PARPORT_CONTROL_STROBE ) ;
2006-10-04 01:25:14 +04:00
udelay ( 1 ) ;
parport_frob_control ( port , PARPORT_CONTROL_STROBE , 0 ) ;
udelay ( 1 ) ;
2005-04-17 02:20:36 +04:00
2006-10-04 01:25:14 +04:00
add_dev ( numdevs + + , port , daisy ) ;
2005-04-17 02:20:36 +04:00
2006-01-06 11:19:45 +03:00
/* See if this device thought it was the last in the
* chain . */
if ( ! ( s & PARPORT_STATUS_BUSY ) )
break ;
2005-04-17 02:20:36 +04:00
2006-01-06 11:19:45 +03:00
/* 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 . */
2006-10-04 01:25:14 +04:00
s = parport_read_status ( port ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 01:25:14 +04:00
parport_write_data ( port , 0xff ) ; udelay ( 2 ) ;
2005-04-17 02:20:36 +04:00
detected = numdevs - thisdev ;
2006-10-04 01:25:14 +04:00
DPRINTK ( KERN_DEBUG " %s: Found %d daisy-chained devices \n " , port - > name ,
2005-04-17 02:20:36 +04:00
detected ) ;
/* Ask the new devices to introduce themselves. */
2006-10-04 01:25:14 +04:00
deviceid = kmalloc ( 1024 , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! deviceid ) return 0 ;
for ( daisy = 0 ; thisdev < numdevs ; thisdev + + , daisy + + )
2006-10-04 01:25:14 +04:00
parport_device_id ( thisdev , deviceid , 1024 ) ;
2005-04-17 02:20:36 +04:00
2006-10-04 01:25:14 +04:00
kfree ( deviceid ) ;
2005-04-17 02:20:36 +04:00
return detected ;
}