2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
* eisa_enumerator . c - provide support for EISA adapters in PA - RISC machines
*
* Copyright ( c ) 2002 Daniel Engstrom < 5116 @ telia . com >
*/
# include <linux/ioport.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <asm/io.h>
2016-12-24 22:46:01 +03:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
# include <asm/byteorder.h>
# include <asm/eisa_bus.h>
# include <asm/eisa_eeprom.h>
/*
* Todo :
*
* PORT init with MASK attr and other size than byte
* MEMORY with other decode than 20 bit
* CRC stuff
* FREEFORM stuff
*/
# define EPI 0xc80
# define NUM_SLOT 16
# define SLOT2PORT(x) (x<<12)
/* macros to handle unaligned accesses and
* byte swapping . The data in the EEPROM is
* little - endian on the big - endian PAROSC */
# define get_8(x) (*(u_int8_t*)(x))
static inline u_int16_t get_16 ( const unsigned char * x )
{
return ( x [ 1 ] < < 8 ) | x [ 0 ] ;
}
static inline u_int32_t get_32 ( const unsigned char * x )
{
return ( x [ 3 ] < < 24 ) | ( x [ 2 ] < < 16 ) | ( x [ 1 ] < < 8 ) | x [ 0 ] ;
}
static inline u_int32_t get_24 ( const unsigned char * x )
{
return ( x [ 2 ] < < 24 ) | ( x [ 1 ] < < 16 ) | ( x [ 0 ] < < 8 ) ;
}
static void print_eisa_id ( char * s , u_int32_t id )
{
char vendor [ 4 ] ;
int rev ;
int device ;
rev = id & 0xff ;
id > > = 8 ;
device = id & 0xff ;
id > > = 8 ;
vendor [ 3 ] = ' \0 ' ;
vendor [ 2 ] = ' @ ' + ( id & 0x1f ) ;
id > > = 5 ;
vendor [ 1 ] = ' @ ' + ( id & 0x1f ) ;
id > > = 5 ;
vendor [ 0 ] = ' @ ' + ( id & 0x1f ) ;
id > > = 5 ;
sprintf ( s , " %s%02X%02X " , vendor , device , rev ) ;
}
static int configure_memory ( const unsigned char * buf ,
struct resource * mem_parent ,
char * name )
{
int len ;
u_int8_t c ;
int i ;
struct resource * res ;
len = 0 ;
for ( i = 0 ; i < HPEE_MEMORY_MAX_ENT ; i + + ) {
c = get_8 ( buf + len ) ;
2016-01-26 23:57:25 +03:00
if ( NULL ! = ( res = kzalloc ( sizeof ( struct resource ) , GFP_KERNEL ) ) ) {
2005-04-17 02:20:36 +04:00
int result ;
res - > name = name ;
res - > start = mem_parent - > start + get_24 ( buf + len + 2 ) ;
res - > end = res - > start + get_16 ( buf + len + 5 ) * 1024 ;
res - > flags = IORESOURCE_MEM ;
2019-09-05 17:37:38 +03:00
pr_cont ( " memory %pR " , res ) ;
2005-04-17 02:20:36 +04:00
result = request_resource ( mem_parent , res ) ;
if ( result < 0 ) {
2009-07-07 00:05:40 +04:00
printk ( KERN_ERR " EISA Enumerator: failed to claim EISA Bus address space! \n " ) ;
2005-04-17 02:20:36 +04:00
return result ;
}
}
len + = 7 ;
if ( ! ( c & HPEE_MEMORY_MORE ) ) {
break ;
}
}
return len ;
}
static int configure_irq ( const unsigned char * buf )
{
int len ;
u_int8_t c ;
int i ;
len = 0 ;
for ( i = 0 ; i < HPEE_IRQ_MAX_ENT ; i + + ) {
c = get_8 ( buf + len ) ;
2019-09-05 17:37:38 +03:00
pr_cont ( " IRQ %d " , c & HPEE_IRQ_CHANNEL_MASK ) ;
2005-04-17 02:20:36 +04:00
if ( c & HPEE_IRQ_TRIG_LEVEL ) {
eisa_make_irq_level ( c & HPEE_IRQ_CHANNEL_MASK ) ;
} else {
eisa_make_irq_edge ( c & HPEE_IRQ_CHANNEL_MASK ) ;
}
len + = 2 ;
/* hpux seems to allow for
* two bytes of irq data but only defines one of
* them , I think */
if ( ! ( c & HPEE_IRQ_MORE ) ) {
break ;
}
}
return len ;
}
static int configure_dma ( const unsigned char * buf )
{
int len ;
u_int8_t c ;
int i ;
len = 0 ;
for ( i = 0 ; i < HPEE_DMA_MAX_ENT ; i + + ) {
c = get_8 ( buf + len ) ;
2019-09-05 17:37:38 +03:00
pr_cont ( " DMA %d " , c & HPEE_DMA_CHANNEL_MASK ) ;
2005-04-17 02:20:36 +04:00
/* fixme: maybe initialize the dma channel withthe timing ? */
len + = 2 ;
if ( ! ( c & HPEE_DMA_MORE ) ) {
break ;
}
}
return len ;
}
static int configure_port ( const unsigned char * buf , struct resource * io_parent ,
char * board )
{
int len ;
u_int8_t c ;
int i ;
struct resource * res ;
int result ;
len = 0 ;
for ( i = 0 ; i < HPEE_PORT_MAX_ENT ; i + + ) {
c = get_8 ( buf + len ) ;
2016-01-26 23:57:25 +03:00
if ( NULL ! = ( res = kzalloc ( sizeof ( struct resource ) , GFP_KERNEL ) ) ) {
2005-04-17 02:20:36 +04:00
res - > name = board ;
res - > start = get_16 ( buf + len + 1 ) ;
res - > end = get_16 ( buf + len + 1 ) + ( c & HPEE_PORT_SIZE_MASK ) + 1 ;
res - > flags = IORESOURCE_IO ;
2019-09-05 17:37:38 +03:00
pr_cont ( " ioports %pR " , res ) ;
2005-04-17 02:20:36 +04:00
result = request_resource ( io_parent , res ) ;
if ( result < 0 ) {
2009-07-07 00:05:40 +04:00
printk ( KERN_ERR " EISA Enumerator: failed to claim EISA Bus address space! \n " ) ;
2005-04-17 02:20:36 +04:00
return result ;
}
}
len + = 3 ;
if ( ! ( c & HPEE_PORT_MORE ) ) {
break ;
}
}
return len ;
}
/* byte 1 and 2 is the port number to write
* and at byte 3 the value to write starts .
* I assume that there are and - and or - masks
* here when HPEE_PORT_INIT_MASK is set but I have
* not yet encountered this . */
static int configure_port_init ( const unsigned char * buf )
{
int len = 0 ;
u_int8_t c ;
while ( len < HPEE_PORT_INIT_MAX_LEN ) {
int s = 0 ;
c = get_8 ( buf + len ) ;
switch ( c & HPEE_PORT_INIT_WIDTH_MASK ) {
case HPEE_PORT_INIT_WIDTH_BYTE :
s = 1 ;
if ( c & HPEE_PORT_INIT_MASK ) {
2009-07-07 00:05:40 +04:00
printk ( KERN_WARNING " port_init: unverified mask attribute \n " ) ;
2005-04-17 02:20:36 +04:00
outb ( ( inb ( get_16 ( buf + len + 1 ) &
get_8 ( buf + len + 3 ) ) |
get_8 ( buf + len + 4 ) ) , get_16 ( buf + len + 1 ) ) ;
} else {
outb ( get_8 ( buf + len + 3 ) , get_16 ( buf + len + 1 ) ) ;
}
break ;
case HPEE_PORT_INIT_WIDTH_WORD :
s = 2 ;
if ( c & HPEE_PORT_INIT_MASK ) {
printk ( KERN_WARNING " port_init: unverified mask attribute \n " ) ;
outw ( ( inw ( get_16 ( buf + len + 1 ) ) &
get_16 ( buf + len + 3 ) ) |
get_16 ( buf + len + 5 ) ,
get_16 ( buf + len + 1 ) ) ;
} else {
outw ( cpu_to_le16 ( get_16 ( buf + len + 3 ) ) , get_16 ( buf + len + 1 ) ) ;
}
break ;
case HPEE_PORT_INIT_WIDTH_DWORD :
s = 4 ;
if ( c & HPEE_PORT_INIT_MASK ) {
2009-07-07 00:05:40 +04:00
printk ( KERN_WARNING " port_init: unverified mask attribute \n " ) ;
2005-04-17 02:20:36 +04:00
outl ( ( inl ( get_16 ( buf + len + 1 ) &
get_32 ( buf + len + 3 ) ) |
get_32 ( buf + len + 7 ) ) , get_16 ( buf + len + 1 ) ) ;
} else {
outl ( cpu_to_le32 ( get_32 ( buf + len + 3 ) ) , get_16 ( buf + len + 1 ) ) ;
}
break ;
default :
2009-07-07 00:05:40 +04:00
printk ( KERN_ERR " Invalid port init word %02x \n " , c ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
if ( c & HPEE_PORT_INIT_MASK ) {
s * = 2 ;
}
len + = s + 3 ;
if ( ! ( c & HPEE_PORT_INIT_MORE ) ) {
break ;
}
}
return len ;
}
static int configure_choise ( const unsigned char * buf , u_int8_t * info )
{
int len ;
/* theis record contain the value of the functions
* configuration choises and an info byte which
* describes which other records to expect in this
* function */
len = get_8 ( buf ) ;
* info = get_8 ( buf + len + 1 ) ;
return len + 2 ;
}
static int configure_type_string ( const unsigned char * buf )
{
int len ;
/* just skip past the type field */
len = get_8 ( buf ) ;
if ( len > 80 ) {
2009-07-07 00:05:40 +04:00
printk ( KERN_ERR " eisa_enumerator: type info field too long (%d, max is 80) \n " , len ) ;
2005-04-17 02:20:36 +04:00
}
return 1 + len ;
}
static int configure_function ( const unsigned char * buf , int * more )
{
/* the init field seems to be a two-byte field
* which is non - zero if there are an other function following
* I think it is the length of the function def
*/
* more = get_16 ( buf ) ;
return 2 ;
}
static int parse_slot_config ( int slot ,
const unsigned char * buf ,
struct eeprom_eisa_slot_info * es ,
struct resource * io_parent ,
struct resource * mem_parent )
{
int res = 0 ;
int function_len ;
unsigned int pos = 0 ;
unsigned int maxlen ;
int num_func = 0 ;
u_int8_t flags ;
int p0 ;
char * board ;
int id_string_used = 0 ;
if ( NULL = = ( board = kmalloc ( 8 , GFP_KERNEL ) ) ) {
return - 1 ;
}
print_eisa_id ( board , es - > eisa_slot_id ) ;
printk ( KERN_INFO " EISA slot %d: %s %s " ,
slot , board , es - > flags & HPEE_FLAG_BOARD_IS_ISA ? " ISA " : " EISA " ) ;
maxlen = es - > config_data_length < HPEE_MAX_LENGTH ?
es - > config_data_length : HPEE_MAX_LENGTH ;
while ( ( pos < maxlen ) & & ( num_func < = es - > num_functions ) ) {
pos + = configure_function ( buf + pos , & function_len ) ;
if ( ! function_len ) {
break ;
}
num_func + + ;
p0 = pos ;
pos + = configure_choise ( buf + pos , & flags ) ;
if ( flags & HPEE_FUNCTION_INFO_F_DISABLED ) {
/* function disabled, skip silently */
pos = p0 + function_len ;
continue ;
}
if ( flags & HPEE_FUNCTION_INFO_CFG_FREE_FORM ) {
/* I have no idea how to handle this */
2015-02-27 17:52:31 +03:00
printk ( " function %d have free-form configuration, skipping " ,
2005-04-17 02:20:36 +04:00
num_func ) ;
pos = p0 + function_len ;
continue ;
}
/* the ordering of the sections need
* more investigation .
* Currently I think that memory comaed before IRQ
* I assume the order is LSB to MSB in the
* info flags
* eg type , memory , irq , dma , port , HPEE_PORT_init
*/
if ( flags & HPEE_FUNCTION_INFO_HAVE_TYPE ) {
pos + = configure_type_string ( buf + pos ) ;
}
if ( flags & HPEE_FUNCTION_INFO_HAVE_MEMORY ) {
id_string_used = 1 ;
pos + = configure_memory ( buf + pos , mem_parent , board ) ;
}
if ( flags & HPEE_FUNCTION_INFO_HAVE_IRQ ) {
pos + = configure_irq ( buf + pos ) ;
}
if ( flags & HPEE_FUNCTION_INFO_HAVE_DMA ) {
pos + = configure_dma ( buf + pos ) ;
}
if ( flags & HPEE_FUNCTION_INFO_HAVE_PORT ) {
id_string_used = 1 ;
pos + = configure_port ( buf + pos , io_parent , board ) ;
}
if ( flags & HPEE_FUNCTION_INFO_HAVE_PORT_INIT ) {
pos + = configure_port_init ( buf + pos ) ;
}
if ( p0 + function_len < pos ) {
2009-07-07 00:05:40 +04:00
printk ( KERN_ERR " eisa_enumerator: function %d length mis-match "
2005-04-17 02:20:36 +04:00
" got %d, expected %d \n " ,
num_func , pos - p0 , function_len ) ;
res = - 1 ;
break ;
}
pos = p0 + function_len ;
}
2019-09-05 17:37:38 +03:00
pr_cont ( " \n " ) ;
2005-04-17 02:20:36 +04:00
if ( ! id_string_used ) {
kfree ( board ) ;
}
if ( pos ! = es - > config_data_length ) {
printk ( KERN_ERR " eisa_enumerator: config data length mis-match got %d, expected %d \n " ,
pos , es - > config_data_length ) ;
res = - 1 ;
}
if ( num_func ! = es - > num_functions ) {
printk ( KERN_ERR " eisa_enumerator: number of functions mis-match got %d, expected %d \n " ,
num_func , es - > num_functions ) ;
res = - 2 ;
}
return res ;
}
static int init_slot ( int slot , struct eeprom_eisa_slot_info * es )
{
unsigned int id ;
char id_string [ 8 ] ;
if ( ! ( es - > slot_info & HPEE_SLOT_INFO_NO_READID ) ) {
/* try to read the id of the board in the slot */
id = le32_to_cpu ( inl ( SLOT2PORT ( slot ) + EPI ) ) ;
if ( 0xffffffff = = id ) {
/* Maybe we didn't expect a card to be here... */
if ( es - > eisa_slot_id = = 0xffffffff )
return - 1 ;
/* this board is not here or it does not
* support readid
*/
printk ( KERN_ERR " EISA slot %d a configured board was not detected ( " ,
slot ) ;
print_eisa_id ( id_string , es - > eisa_slot_id ) ;
printk ( " expected %s) \n " , id_string ) ;
return - 1 ;
}
if ( es - > eisa_slot_id ! = id ) {
print_eisa_id ( id_string , id ) ;
printk ( KERN_ERR " EISA slot %d id mis-match: got %s " ,
slot , id_string ) ;
print_eisa_id ( id_string , es - > eisa_slot_id ) ;
2010-02-06 20:47:14 +03:00
printk ( " expected %s \n " , id_string ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
}
/* now: we need to enable the board if
* it supports enabling and run through
* the port init sction if present
* and finally record any interrupt polarity
*/
if ( es - > slot_features & HPEE_SLOT_FEATURES_ENABLE ) {
/* enable board */
outb ( 0x01 | inb ( SLOT2PORT ( slot ) + EPI + 4 ) ,
SLOT2PORT ( slot ) + EPI + 4 ) ;
}
return 0 ;
}
int eisa_enumerator ( unsigned long eeprom_addr ,
struct resource * io_parent , struct resource * mem_parent )
{
int i ;
struct eeprom_header * eh ;
static char eeprom_buf [ HPEE_MAX_LENGTH ] ;
for ( i = 0 ; i < HPEE_MAX_LENGTH ; i + + ) {
eeprom_buf [ i ] = gsc_readb ( eeprom_addr + i ) ;
}
printk ( KERN_INFO " Enumerating EISA bus \n " ) ;
eh = ( struct eeprom_header * ) ( eeprom_buf ) ;
for ( i = 0 ; i < eh - > num_slots ; i + + ) {
struct eeprom_eisa_slot_info * es ;
es = ( struct eeprom_eisa_slot_info * )
( & eeprom_buf [ HPEE_SLOT_INFO ( i ) ] ) ;
if ( - 1 = = init_slot ( i + 1 , es ) ) {
continue ;
}
if ( es - > config_data_offset < HPEE_MAX_LENGTH ) {
if ( parse_slot_config ( i + 1 , & eeprom_buf [ es - > config_data_offset ] ,
es , io_parent , mem_parent ) ) {
return - 1 ;
}
} else {
printk ( KERN_WARNING " EISA EEPROM offset 0x%x out of range \n " , es - > config_data_offset ) ;
return - 1 ;
}
}
return eh - > num_slots ;
}