2013-04-16 14:14:44 +02:00
/*
* s390 specific pci instructions
*
* Copyright IBM Corp . 2013
*/
# include <linux/export.h>
# include <linux/errno.h>
# include <linux/delay.h>
# include <asm/pci_insn.h>
2015-08-18 19:39:27 +02:00
# include <asm/pci_debug.h>
2013-04-16 14:16:14 +02:00
# include <asm/processor.h>
2013-04-16 14:14:44 +02:00
# define ZPCI_INSN_BUSY_DELAY 1 /* 1 microsecond */
2015-08-18 19:39:27 +02:00
static inline void zpci_err_insn ( u8 cc , u8 status , u64 req , u64 offset )
{
struct {
u64 req ;
u64 offset ;
2015-10-09 11:07:06 +02:00
u8 cc ;
u8 status ;
} __packed data = { req , offset , cc , status } ;
2015-08-18 19:39:27 +02:00
zpci_err_hex ( & data , sizeof ( data ) ) ;
}
2013-04-16 14:14:44 +02:00
/* Modify PCI Function Controls */
static inline u8 __mpcifc ( u64 req , struct zpci_fib * fib , u8 * status )
{
u8 cc ;
asm volatile (
" .insn rxy,0xe300000000d0,%[req],%[fib] \n "
" ipm %[cc] \n "
" srl %[cc],28 \n "
: [ cc ] " =d " ( cc ) , [ req ] " +d " ( req ) , [ fib ] " +Q " ( * fib )
: : " cc " ) ;
* status = req > > 24 & 0xff ;
return cc ;
}
2013-06-25 14:52:23 +02:00
int zpci_mod_fc ( u64 req , struct zpci_fib * fib )
2013-04-16 14:14:44 +02:00
{
u8 cc , status ;
do {
cc = __mpcifc ( req , fib , & status ) ;
if ( cc = = 2 )
msleep ( ZPCI_INSN_BUSY_DELAY ) ;
} while ( cc = = 2 ) ;
if ( cc )
2015-08-18 19:39:27 +02:00
zpci_err_insn ( cc , status , req , 0 ) ;
2013-04-16 14:14:44 +02:00
return ( cc ) ? - EIO : 0 ;
}
/* Refresh PCI Translations */
static inline u8 __rpcit ( u64 fn , u64 addr , u64 range , u8 * status )
{
register u64 __addr asm ( " 2 " ) = addr ;
register u64 __range asm ( " 3 " ) = range ;
u8 cc ;
asm volatile (
" .insn rre,0xb9d30000,%[fn],%[addr] \n "
" ipm %[cc] \n "
" srl %[cc],28 \n "
: [ cc ] " =d " ( cc ) , [ fn ] " +d " ( fn )
: [ addr ] " d " ( __addr ) , " d " ( __range )
: " cc " ) ;
* status = fn > > 24 & 0xff ;
return cc ;
}
2013-06-25 14:52:23 +02:00
int zpci_refresh_trans ( u64 fn , u64 addr , u64 range )
2013-04-16 14:14:44 +02:00
{
u8 cc , status ;
do {
cc = __rpcit ( fn , addr , range , & status ) ;
if ( cc = = 2 )
udelay ( ZPCI_INSN_BUSY_DELAY ) ;
} while ( cc = = 2 ) ;
if ( cc )
2015-08-18 19:39:27 +02:00
zpci_err_insn ( cc , status , addr , range ) ;
2013-04-16 14:14:44 +02:00
return ( cc ) ? - EIO : 0 ;
}
/* Set Interruption Controls */
2013-06-25 14:52:23 +02:00
void zpci_set_irq_ctrl ( u16 ctl , char * unused , u8 isc )
2013-04-16 14:14:44 +02:00
{
asm volatile (
" .insn rsy,0xeb00000000d1,%[ctl],%[isc],%[u] \n "
: : [ ctl ] " d " ( ctl ) , [ isc ] " d " ( isc < < 27 ) , [ u ] " Q " ( * unused ) ) ;
}
/* PCI Load */
2013-04-16 14:16:14 +02:00
static inline int __pcilg ( u64 * data , u64 req , u64 offset , u8 * status )
2013-04-16 14:14:44 +02:00
{
register u64 __req asm ( " 2 " ) = req ;
register u64 __offset asm ( " 3 " ) = offset ;
2013-04-16 14:16:14 +02:00
int cc = - ENXIO ;
2013-04-16 14:14:44 +02:00
u64 __data ;
asm volatile (
" .insn rre,0xb9d20000,%[data],%[req] \n "
2013-04-16 14:16:14 +02:00
" 0: ipm %[cc] \n "
2013-04-16 14:14:44 +02:00
" srl %[cc],28 \n "
2013-04-16 14:16:14 +02:00
" 1: \n "
EX_TABLE ( 0 b , 1 b )
: [ cc ] " +d " ( cc ) , [ data ] " =d " ( __data ) , [ req ] " +d " ( __req )
2013-04-16 14:14:44 +02:00
: " d " ( __offset )
: " cc " ) ;
* status = __req > > 24 & 0xff ;
2013-04-16 14:17:15 +02:00
if ( ! cc )
* data = __data ;
2013-04-16 14:14:44 +02:00
return cc ;
}
2013-06-25 14:52:23 +02:00
int zpci_load ( u64 * data , u64 req , u64 offset )
2013-04-16 14:14:44 +02:00
{
2013-04-16 14:16:14 +02:00
u8 status ;
int cc ;
2013-04-16 14:14:44 +02:00
do {
cc = __pcilg ( data , req , offset , & status ) ;
if ( cc = = 2 )
udelay ( ZPCI_INSN_BUSY_DELAY ) ;
} while ( cc = = 2 ) ;
2013-04-16 14:16:14 +02:00
if ( cc )
2015-08-18 19:39:27 +02:00
zpci_err_insn ( cc , status , req , offset ) ;
2013-04-16 14:16:14 +02:00
return ( cc > 0 ) ? - EIO : cc ;
2013-04-16 14:14:44 +02:00
}
2013-06-25 14:52:23 +02:00
EXPORT_SYMBOL_GPL ( zpci_load ) ;
2013-04-16 14:14:44 +02:00
/* PCI Store */
2013-04-16 14:16:14 +02:00
static inline int __pcistg ( u64 data , u64 req , u64 offset , u8 * status )
2013-04-16 14:14:44 +02:00
{
register u64 __req asm ( " 2 " ) = req ;
register u64 __offset asm ( " 3 " ) = offset ;
2013-04-16 14:16:14 +02:00
int cc = - ENXIO ;
2013-04-16 14:14:44 +02:00
asm volatile (
" .insn rre,0xb9d00000,%[data],%[req] \n "
2013-04-16 14:16:14 +02:00
" 0: ipm %[cc] \n "
2013-04-16 14:14:44 +02:00
" srl %[cc],28 \n "
2013-04-16 14:16:14 +02:00
" 1: \n "
EX_TABLE ( 0 b , 1 b )
: [ cc ] " +d " ( cc ) , [ req ] " +d " ( __req )
2013-04-16 14:14:44 +02:00
: " d " ( __offset ) , [ data ] " d " ( data )
: " cc " ) ;
* status = __req > > 24 & 0xff ;
return cc ;
}
2013-06-25 14:52:23 +02:00
int zpci_store ( u64 data , u64 req , u64 offset )
2013-04-16 14:14:44 +02:00
{
2013-04-16 14:16:14 +02:00
u8 status ;
int cc ;
2013-04-16 14:14:44 +02:00
do {
cc = __pcistg ( data , req , offset , & status ) ;
if ( cc = = 2 )
udelay ( ZPCI_INSN_BUSY_DELAY ) ;
} while ( cc = = 2 ) ;
if ( cc )
2015-08-18 19:39:27 +02:00
zpci_err_insn ( cc , status , req , offset ) ;
2013-04-16 14:16:14 +02:00
return ( cc > 0 ) ? - EIO : cc ;
2013-04-16 14:14:44 +02:00
}
2013-06-25 14:52:23 +02:00
EXPORT_SYMBOL_GPL ( zpci_store ) ;
2013-04-16 14:14:44 +02:00
/* PCI Store Block */
2013-04-16 14:16:14 +02:00
static inline int __pcistb ( const u64 * data , u64 req , u64 offset , u8 * status )
2013-04-16 14:14:44 +02:00
{
2013-04-16 14:16:14 +02:00
int cc = - ENXIO ;
2013-04-16 14:14:44 +02:00
asm volatile (
" .insn rsy,0xeb00000000d0,%[req],%[offset],%[data] \n "
2013-04-16 14:16:14 +02:00
" 0: ipm %[cc] \n "
2013-04-16 14:14:44 +02:00
" srl %[cc],28 \n "
2013-04-16 14:16:14 +02:00
" 1: \n "
EX_TABLE ( 0 b , 1 b )
: [ cc ] " +d " ( cc ) , [ req ] " +d " ( req )
2013-04-16 14:14:44 +02:00
: [ offset ] " d " ( offset ) , [ data ] " Q " ( * data )
: " cc " ) ;
* status = req > > 24 & 0xff ;
return cc ;
}
2013-06-25 14:52:23 +02:00
int zpci_store_block ( const u64 * data , u64 req , u64 offset )
2013-04-16 14:14:44 +02:00
{
2013-04-16 14:16:14 +02:00
u8 status ;
int cc ;
2013-04-16 14:14:44 +02:00
do {
cc = __pcistb ( data , req , offset , & status ) ;
if ( cc = = 2 )
udelay ( ZPCI_INSN_BUSY_DELAY ) ;
} while ( cc = = 2 ) ;
if ( cc )
2015-08-18 19:39:27 +02:00
zpci_err_insn ( cc , status , req , offset ) ;
2013-04-16 14:16:14 +02:00
return ( cc > 0 ) ? - EIO : cc ;
2013-04-16 14:14:44 +02:00
}
2013-06-25 14:52:23 +02:00
EXPORT_SYMBOL_GPL ( zpci_store_block ) ;