2006-09-20 15:58:25 +02:00
/*
* linux / drivers / s390 / crypto / ap_bus . c
*
* Copyright ( C ) 2006 IBM Corporation
* Author ( s ) : Cornelia Huck < cornelia . huck @ de . ibm . com >
* Martin Schwidefsky < schwidefsky @ de . ibm . com >
* Ralph Wuerthner < rwuerthn @ de . ibm . com >
2008-12-25 13:38:41 +01:00
* Felix Beck < felix . beck @ de . ibm . com >
2006-09-20 15:58:25 +02:00
*
* Adjunct processor bus .
*
* 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 , or ( at your option )
* any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2008-12-25 13:39:46 +01:00
# define KMSG_COMPONENT "ap"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2006-09-20 15:58:25 +02:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/interrupt.h>
# include <linux/workqueue.h>
# include <linux/notifier.h>
# include <linux/kthread.h>
# include <linux/mutex.h>
2006-12-08 15:54:07 +01:00
# include <asm/reset.h>
2008-12-25 13:38:41 +01:00
# include <asm/airq.h>
# include <asm/atomic.h>
# include <asm/system.h>
# include <asm/isc.h>
2008-07-14 09:59:08 +02:00
# include <linux/hrtimer.h>
# include <linux/ktime.h>
2006-09-20 15:58:25 +02:00
# include "ap_bus.h"
/* Some prototypes. */
2006-12-06 19:18:20 +00:00
static void ap_scan_bus ( struct work_struct * ) ;
2006-09-20 15:58:25 +02:00
static void ap_poll_all ( unsigned long ) ;
2008-07-14 09:59:08 +02:00
static enum hrtimer_restart ap_poll_timeout ( struct hrtimer * ) ;
2006-09-20 15:58:25 +02:00
static int ap_poll_thread_start ( void ) ;
static void ap_poll_thread_stop ( void ) ;
2007-07-10 11:24:19 +02:00
static void ap_request_timeout ( unsigned long ) ;
2008-12-25 13:38:41 +01:00
static inline void ap_schedule_poll_timer ( void ) ;
2009-06-22 12:08:16 +02:00
static int __ap_poll_device ( struct ap_device * ap_dev , unsigned long * flags ) ;
static int ap_device_remove ( struct device * dev ) ;
static int ap_device_probe ( struct device * dev ) ;
static void ap_interrupt_handler ( void * unused1 , void * unused2 ) ;
static void ap_reset ( struct ap_device * ap_dev ) ;
static void ap_config_timeout ( unsigned long ptr ) ;
2006-09-20 15:58:25 +02:00
2008-04-17 07:46:28 +02:00
/*
2006-09-20 15:58:25 +02:00
* Module description .
*/
MODULE_AUTHOR ( " IBM Corporation " ) ;
MODULE_DESCRIPTION ( " Adjunct Processor Bus driver, "
" Copyright 2006 IBM Corporation " ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-04-17 07:46:28 +02:00
/*
2006-09-20 15:58:25 +02:00
* Module parameter
*/
int ap_domain_index = - 1 ; /* Adjunct Processor Domain Index */
module_param_named ( domain , ap_domain_index , int , 0000 ) ;
MODULE_PARM_DESC ( domain , " domain index for ap devices " ) ;
EXPORT_SYMBOL ( ap_domain_index ) ;
2008-02-09 18:24:30 +01:00
static int ap_thread_flag = 0 ;
2006-09-20 15:58:25 +02:00
module_param_named ( poll_thread , ap_thread_flag , int , 0000 ) ;
2008-02-09 18:24:30 +01:00
MODULE_PARM_DESC ( poll_thread , " Turn on/off poll thread, default is 0 (off). " ) ;
2006-09-20 15:58:25 +02:00
static struct device * ap_root_device = NULL ;
2008-12-25 13:38:42 +01:00
static DEFINE_SPINLOCK ( ap_device_list_lock ) ;
2007-03-19 13:19:14 +01:00
static LIST_HEAD ( ap_device_list ) ;
2006-09-20 15:58:25 +02:00
2008-04-17 07:46:28 +02:00
/*
2006-09-20 15:58:25 +02:00
* Workqueue & timer for bus rescan .
*/
static struct workqueue_struct * ap_work_queue ;
static struct timer_list ap_config_timer ;
static int ap_config_time = AP_CONFIG_TIME ;
2006-12-06 19:18:20 +00:00
static DECLARE_WORK ( ap_config_work , ap_scan_bus ) ;
2006-09-20 15:58:25 +02:00
2008-04-17 07:46:28 +02:00
/*
2008-12-25 13:38:41 +01:00
* Tasklet & timer for AP request polling and interrupts
2006-09-20 15:58:25 +02:00
*/
static DECLARE_TASKLET ( ap_tasklet , ap_poll_all , 0 ) ;
static atomic_t ap_poll_requests = ATOMIC_INIT ( 0 ) ;
static DECLARE_WAIT_QUEUE_HEAD ( ap_poll_wait ) ;
static struct task_struct * ap_poll_kthread = NULL ;
static DEFINE_MUTEX ( ap_poll_thread_mutex ) ;
2008-12-25 13:38:41 +01:00
static void * ap_interrupt_indicator ;
2008-07-14 09:59:08 +02:00
static struct hrtimer ap_poll_timer ;
/* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds.
* If z / VM change to 1500000 nanoseconds to adjust to z / VM polling . */
static unsigned long long poll_timeout = 250000 ;
2006-09-20 15:58:25 +02:00
2009-06-22 12:08:16 +02:00
/* Suspend flag */
static int ap_suspend_flag ;
static struct bus_type ap_bus_type ;
2008-12-25 13:38:41 +01:00
/**
* ap_using_interrupts ( ) - Returns non - zero if interrupt support is
* available .
*/
static inline int ap_using_interrupts ( void )
{
return ap_interrupt_indicator ! = NULL ;
}
2006-09-20 15:58:25 +02:00
/**
2008-04-17 07:46:28 +02:00
* ap_intructions_available ( ) - Test if AP instructions are available .
2006-09-20 15:58:25 +02:00
*
2008-04-17 07:46:28 +02:00
* Returns 0 if the AP instructions are installed .
2006-09-20 15:58:25 +02:00
*/
static inline int ap_instructions_available ( void )
{
register unsigned long reg0 asm ( " 0 " ) = AP_MKQID ( 0 , 0 ) ;
register unsigned long reg1 asm ( " 1 " ) = - ENODEV ;
register unsigned long reg2 asm ( " 2 " ) = 0UL ;
asm volatile (
" .long 0xb2af0000 \n " /* PQAP(TAPQ) */
" 0: la %1,0 \n "
" 1: \n "
EX_TABLE ( 0 b , 1 b )
: " +d " ( reg0 ) , " +d " ( reg1 ) , " +d " ( reg2 ) : : " cc " ) ;
return reg1 ;
}
2008-12-25 13:38:41 +01:00
/**
* ap_interrupts_available ( ) : Test if AP interrupts are available .
*
* Returns 1 if AP interrupts are available .
*/
static int ap_interrupts_available ( void )
{
unsigned long long facility_bits [ 2 ] ;
if ( stfle ( facility_bits , 2 ) < = 1 )
return 0 ;
if ( ! ( facility_bits [ 0 ] & ( 1ULL < < 61 ) ) | |
! ( facility_bits [ 1 ] & ( 1ULL < < 62 ) ) )
return 0 ;
return 1 ;
}
2006-09-20 15:58:25 +02:00
/**
2008-04-17 07:46:28 +02:00
* ap_test_queue ( ) : Test adjunct processor queue .
* @ qid : The AP queue number
* @ queue_depth : Pointer to queue depth value
* @ device_type : Pointer to device type value
2006-09-20 15:58:25 +02:00
*
2008-04-17 07:46:28 +02:00
* Returns AP queue status structure .
2006-09-20 15:58:25 +02:00
*/
static inline struct ap_queue_status
ap_test_queue ( ap_qid_t qid , int * queue_depth , int * device_type )
{
register unsigned long reg0 asm ( " 0 " ) = qid ;
register struct ap_queue_status reg1 asm ( " 1 " ) ;
register unsigned long reg2 asm ( " 2 " ) = 0UL ;
asm volatile ( " .long 0xb2af0000 " /* PQAP(TAPQ) */
: " +d " ( reg0 ) , " =d " ( reg1 ) , " +d " ( reg2 ) : : " cc " ) ;
* device_type = ( int ) ( reg2 > > 24 ) ;
* queue_depth = ( int ) ( reg2 & 0xff ) ;
return reg1 ;
}
/**
2008-04-17 07:46:28 +02:00
* ap_reset_queue ( ) : Reset adjunct processor queue .
* @ qid : The AP queue number
2006-09-20 15:58:25 +02:00
*
2008-04-17 07:46:28 +02:00
* Returns AP queue status structure .
2006-09-20 15:58:25 +02:00
*/
static inline struct ap_queue_status ap_reset_queue ( ap_qid_t qid )
{
register unsigned long reg0 asm ( " 0 " ) = qid | 0x01000000UL ;
register struct ap_queue_status reg1 asm ( " 1 " ) ;
register unsigned long reg2 asm ( " 2 " ) = 0UL ;
asm volatile (
" .long 0xb2af0000 " /* PQAP(RAPQ) */
: " +d " ( reg0 ) , " =d " ( reg1 ) , " +d " ( reg2 ) : : " cc " ) ;
return reg1 ;
}
2008-12-25 13:38:41 +01:00
# ifdef CONFIG_64BIT
/**
* ap_queue_interruption_control ( ) : Enable interruption for a specific AP .
* @ qid : The AP queue number
* @ ind : The notification indicator byte
*
* Returns AP queue status .
*/
static inline struct ap_queue_status
ap_queue_interruption_control ( ap_qid_t qid , void * ind )
{
register unsigned long reg0 asm ( " 0 " ) = qid | 0x03000000UL ;
register unsigned long reg1_in asm ( " 1 " ) = 0x0000800000000000UL | AP_ISC ;
register struct ap_queue_status reg1_out asm ( " 1 " ) ;
register void * reg2 asm ( " 2 " ) = ind ;
asm volatile (
" .long 0xb2af0000 " /* PQAP(RAPQ) */
: " +d " ( reg0 ) , " +d " ( reg1_in ) , " =d " ( reg1_out ) , " +d " ( reg2 )
:
: " cc " ) ;
return reg1_out ;
}
# endif
/**
* ap_queue_enable_interruption ( ) : Enable interruption on an AP .
* @ qid : The AP queue number
* @ ind : the notification indicator byte
*
* Enables interruption on AP queue via ap_queue_interruption_control ( ) . Based
* on the return value it waits a while and tests the AP queue if interrupts
* have been switched on using ap_test_queue ( ) .
*/
static int ap_queue_enable_interruption ( ap_qid_t qid , void * ind )
{
# ifdef CONFIG_64BIT
struct ap_queue_status status ;
int t_depth , t_device_type , rc , i ;
rc = - EBUSY ;
status = ap_queue_interruption_control ( qid , ind ) ;
for ( i = 0 ; i < AP_MAX_RESET ; i + + ) {
switch ( status . response_code ) {
case AP_RESPONSE_NORMAL :
if ( status . int_enabled )
return 0 ;
break ;
case AP_RESPONSE_RESET_IN_PROGRESS :
case AP_RESPONSE_BUSY :
break ;
case AP_RESPONSE_Q_NOT_AVAIL :
case AP_RESPONSE_DECONFIGURED :
case AP_RESPONSE_CHECKSTOPPED :
case AP_RESPONSE_INVALID_ADDRESS :
return - ENODEV ;
case AP_RESPONSE_OTHERWISE_CHANGED :
if ( status . int_enabled )
return 0 ;
break ;
default :
break ;
}
if ( i < AP_MAX_RESET - 1 ) {
udelay ( 5 ) ;
status = ap_test_queue ( qid , & t_depth , & t_device_type ) ;
}
}
return rc ;
# else
return - EINVAL ;
# endif
}
2006-09-20 15:58:25 +02:00
/**
2008-04-17 07:46:28 +02:00
* __ap_send ( ) : Send message to adjunct processor queue .
* @ qid : The AP queue number
* @ psmid : The program supplied message identifier
* @ msg : The message text
* @ length : The message length
2006-09-20 15:58:25 +02:00
*
2008-04-17 07:46:28 +02:00
* Returns AP queue status structure .
2006-09-20 15:58:25 +02:00
* Condition code 1 on NQAP can ' t happen because the L bit is 1.
* Condition code 2 on NQAP also means the send is incomplete ,
* because a segment boundary was reached . The NQAP is repeated .
*/
static inline struct ap_queue_status
__ap_send ( ap_qid_t qid , unsigned long long psmid , void * msg , size_t length )
{
typedef struct { char _ [ length ] ; } msgblock ;
register unsigned long reg0 asm ( " 0 " ) = qid | 0x40000000UL ;
register struct ap_queue_status reg1 asm ( " 1 " ) ;
register unsigned long reg2 asm ( " 2 " ) = ( unsigned long ) msg ;
register unsigned long reg3 asm ( " 3 " ) = ( unsigned long ) length ;
register unsigned long reg4 asm ( " 4 " ) = ( unsigned int ) ( psmid > > 32 ) ;
register unsigned long reg5 asm ( " 5 " ) = ( unsigned int ) psmid ;
asm volatile (
" 0: .long 0xb2ad0042 \n " /* DQAP */
" brc 2,0b "
: " +d " ( reg0 ) , " =d " ( reg1 ) , " +d " ( reg2 ) , " +d " ( reg3 )
: " d " ( reg4 ) , " d " ( reg5 ) , " m " ( * ( msgblock * ) msg )
: " cc " ) ;
return reg1 ;
}
int ap_send ( ap_qid_t qid , unsigned long long psmid , void * msg , size_t length )
{
struct ap_queue_status status ;
status = __ap_send ( qid , psmid , msg , length ) ;
switch ( status . response_code ) {
case AP_RESPONSE_NORMAL :
return 0 ;
case AP_RESPONSE_Q_FULL :
2007-07-10 11:24:19 +02:00
case AP_RESPONSE_RESET_IN_PROGRESS :
2006-09-20 15:58:25 +02:00
return - EBUSY ;
default : /* Device is gone. */
return - ENODEV ;
}
}
EXPORT_SYMBOL ( ap_send ) ;
2008-04-17 07:46:28 +02:00
/**
* __ap_recv ( ) : Receive message from adjunct processor queue .
* @ qid : The AP queue number
* @ psmid : Pointer to program supplied message identifier
* @ msg : The message text
* @ length : The message length
2006-09-20 15:58:25 +02:00
*
2008-04-17 07:46:28 +02:00
* Returns AP queue status structure .
2006-09-20 15:58:25 +02:00
* Condition code 1 on DQAP means the receive has taken place
* but only partially . The response is incomplete , hence the
* DQAP is repeated .
* Condition code 2 on DQAP also means the receive is incomplete ,
* this time because a segment boundary was reached . Again , the
* DQAP is repeated .
* Note that gpr2 is used by the DQAP instruction to keep track of
* any ' residual ' length , in case the instruction gets interrupted .
* Hence it gets zeroed before the instruction .
*/
static inline struct ap_queue_status
__ap_recv ( ap_qid_t qid , unsigned long long * psmid , void * msg , size_t length )
{
typedef struct { char _ [ length ] ; } msgblock ;
register unsigned long reg0 asm ( " 0 " ) = qid | 0x80000000UL ;
register struct ap_queue_status reg1 asm ( " 1 " ) ;
register unsigned long reg2 asm ( " 2 " ) = 0UL ;
register unsigned long reg4 asm ( " 4 " ) = ( unsigned long ) msg ;
register unsigned long reg5 asm ( " 5 " ) = ( unsigned long ) length ;
register unsigned long reg6 asm ( " 6 " ) = 0UL ;
register unsigned long reg7 asm ( " 7 " ) = 0UL ;
asm volatile (
" 0: .long 0xb2ae0064 \n "
" brc 6,0b \n "
: " +d " ( reg0 ) , " =d " ( reg1 ) , " +d " ( reg2 ) ,
" +d " ( reg4 ) , " +d " ( reg5 ) , " +d " ( reg6 ) , " +d " ( reg7 ) ,
" =m " ( * ( msgblock * ) msg ) : : " cc " ) ;
* psmid = ( ( ( unsigned long long ) reg6 ) < < 32 ) + reg7 ;
return reg1 ;
}
int ap_recv ( ap_qid_t qid , unsigned long long * psmid , void * msg , size_t length )
{
struct ap_queue_status status ;
status = __ap_recv ( qid , psmid , msg , length ) ;
switch ( status . response_code ) {
case AP_RESPONSE_NORMAL :
return 0 ;
case AP_RESPONSE_NO_PENDING_REPLY :
if ( status . queue_empty )
return - ENOENT ;
return - EBUSY ;
2007-07-10 11:24:19 +02:00
case AP_RESPONSE_RESET_IN_PROGRESS :
return - EBUSY ;
2006-09-20 15:58:25 +02:00
default :
return - ENODEV ;
}
}
EXPORT_SYMBOL ( ap_recv ) ;
/**
2008-04-17 07:46:28 +02:00
* ap_query_queue ( ) : Check if an AP queue is available .
* @ qid : The AP queue number
* @ queue_depth : Pointer to queue depth value
* @ device_type : Pointer to device type value
*
* The test is repeated for AP_MAX_RESET times .
2006-09-20 15:58:25 +02:00
*/
static int ap_query_queue ( ap_qid_t qid , int * queue_depth , int * device_type )
{
struct ap_queue_status status ;
int t_depth , t_device_type , rc , i ;
rc = - EBUSY ;
for ( i = 0 ; i < AP_MAX_RESET ; i + + ) {
status = ap_test_queue ( qid , & t_depth , & t_device_type ) ;
switch ( status . response_code ) {
case AP_RESPONSE_NORMAL :
* queue_depth = t_depth + 1 ;
* device_type = t_device_type ;
rc = 0 ;
break ;
case AP_RESPONSE_Q_NOT_AVAIL :
rc = - ENODEV ;
break ;
case AP_RESPONSE_RESET_IN_PROGRESS :
break ;
case AP_RESPONSE_DECONFIGURED :
rc = - ENODEV ;
break ;
case AP_RESPONSE_CHECKSTOPPED :
rc = - ENODEV ;
break ;
2008-12-25 13:38:41 +01:00
case AP_RESPONSE_INVALID_ADDRESS :
rc = - ENODEV ;
break ;
case AP_RESPONSE_OTHERWISE_CHANGED :
break ;
2006-09-20 15:58:25 +02:00
case AP_RESPONSE_BUSY :
break ;
default :
BUG ( ) ;
}
if ( rc ! = - EBUSY )
break ;
if ( i < AP_MAX_RESET - 1 )
udelay ( 5 ) ;
}
return rc ;
}
/**
2008-04-17 07:46:28 +02:00
* ap_init_queue ( ) : Reset an AP queue .
* @ qid : The AP queue number
*
2006-09-20 15:58:25 +02:00
* Reset an AP queue and wait for it to become available again .
*/
static int ap_init_queue ( ap_qid_t qid )
{
struct ap_queue_status status ;
int rc , dummy , i ;
rc = - ENODEV ;
status = ap_reset_queue ( qid ) ;
for ( i = 0 ; i < AP_MAX_RESET ; i + + ) {
switch ( status . response_code ) {
case AP_RESPONSE_NORMAL :
if ( status . queue_empty )
rc = 0 ;
break ;
case AP_RESPONSE_Q_NOT_AVAIL :
case AP_RESPONSE_DECONFIGURED :
case AP_RESPONSE_CHECKSTOPPED :
i = AP_MAX_RESET ; /* return with -ENODEV */
break ;
case AP_RESPONSE_RESET_IN_PROGRESS :
2007-07-10 11:24:19 +02:00
rc = - EBUSY ;
2006-09-20 15:58:25 +02:00
case AP_RESPONSE_BUSY :
default :
break ;
}
2007-07-10 11:24:19 +02:00
if ( rc ! = - ENODEV & & rc ! = - EBUSY )
2006-09-20 15:58:25 +02:00
break ;
if ( i < AP_MAX_RESET - 1 ) {
udelay ( 5 ) ;
status = ap_test_queue ( qid , & dummy , & dummy ) ;
}
}
2008-12-25 13:38:41 +01:00
if ( rc = = 0 & & ap_using_interrupts ( ) ) {
rc = ap_queue_enable_interruption ( qid , ap_interrupt_indicator ) ;
/* If interruption mode is supported by the machine,
* but an AP can not be enabled for interruption then
* the AP will be discarded . */
if ( rc )
pr_err ( " Registering adapter interrupts for "
" AP %d failed \n " , AP_QID_DEVICE ( qid ) ) ;
}
2006-09-20 15:58:25 +02:00
return rc ;
}
2007-07-10 11:24:19 +02:00
/**
2008-04-17 07:46:28 +02:00
* ap_increase_queue_count ( ) : Arm request timeout .
* @ ap_dev : Pointer to an AP device .
*
* Arm request timeout if an AP device was idle and a new request is submitted .
2007-07-10 11:24:19 +02:00
*/
static void ap_increase_queue_count ( struct ap_device * ap_dev )
{
int timeout = ap_dev - > drv - > request_timeout ;
ap_dev - > queue_count + + ;
if ( ap_dev - > queue_count = = 1 ) {
mod_timer ( & ap_dev - > timeout , jiffies + timeout ) ;
ap_dev - > reset = AP_RESET_ARMED ;
}
}
/**
2008-04-17 07:46:28 +02:00
* ap_decrease_queue_count ( ) : Decrease queue count .
* @ ap_dev : Pointer to an AP device .
*
* If AP device is still alive , re - schedule request timeout if there are still
2007-07-10 11:24:19 +02:00
* pending requests .
*/
static void ap_decrease_queue_count ( struct ap_device * ap_dev )
{
int timeout = ap_dev - > drv - > request_timeout ;
ap_dev - > queue_count - - ;
if ( ap_dev - > queue_count > 0 )
mod_timer ( & ap_dev - > timeout , jiffies + timeout ) ;
else
2008-04-17 07:46:28 +02:00
/*
2007-07-10 11:24:19 +02:00
* The timeout timer should to be disabled now - since
* del_timer_sync ( ) is very expensive , we just tell via the
* reset flag to ignore the pending timeout timer .
*/
ap_dev - > reset = AP_RESET_IGNORE ;
}
2008-04-17 07:46:28 +02:00
/*
2006-09-20 15:58:25 +02:00
* AP device related attributes .
*/
static ssize_t ap_hwtype_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct ap_device * ap_dev = to_ap_dev ( dev ) ;
return snprintf ( buf , PAGE_SIZE , " %d \n " , ap_dev - > device_type ) ;
}
2008-12-25 13:38:42 +01:00
static DEVICE_ATTR ( hwtype , 0444 , ap_hwtype_show , NULL ) ;
2006-09-20 15:58:25 +02:00
static ssize_t ap_depth_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct ap_device * ap_dev = to_ap_dev ( dev ) ;
return snprintf ( buf , PAGE_SIZE , " %d \n " , ap_dev - > queue_depth ) ;
}
2008-12-25 13:38:42 +01:00
static DEVICE_ATTR ( depth , 0444 , ap_depth_show , NULL ) ;
2006-09-20 15:58:25 +02:00
static ssize_t ap_request_count_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct ap_device * ap_dev = to_ap_dev ( dev ) ;
int rc ;
spin_lock_bh ( & ap_dev - > lock ) ;
rc = snprintf ( buf , PAGE_SIZE , " %d \n " , ap_dev - > total_request_count ) ;
spin_unlock_bh ( & ap_dev - > lock ) ;
return rc ;
}
static DEVICE_ATTR ( request_count , 0444 , ap_request_count_show , NULL ) ;
static ssize_t ap_modalias_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return sprintf ( buf , " ap:t%02X " , to_ap_dev ( dev ) - > device_type ) ;
}
static DEVICE_ATTR ( modalias , 0444 , ap_modalias_show , NULL ) ;
static struct attribute * ap_dev_attrs [ ] = {
& dev_attr_hwtype . attr ,
& dev_attr_depth . attr ,
& dev_attr_request_count . attr ,
& dev_attr_modalias . attr ,
NULL
} ;
static struct attribute_group ap_dev_attr_group = {
. attrs = ap_dev_attrs
} ;
/**
2008-04-17 07:46:28 +02:00
* ap_bus_match ( )
* @ dev : Pointer to device
* @ drv : Pointer to device_driver
*
2006-09-20 15:58:25 +02:00
* AP bus driver registration / unregistration .
*/
static int ap_bus_match ( struct device * dev , struct device_driver * drv )
{
struct ap_device * ap_dev = to_ap_dev ( dev ) ;
struct ap_driver * ap_drv = to_ap_drv ( drv ) ;
struct ap_device_id * id ;
2008-04-17 07:46:28 +02:00
/*
2006-09-20 15:58:25 +02:00
* Compare device type of the device with the list of
* supported types of the device_driver .
*/
for ( id = ap_drv - > ids ; id - > match_flags ; id + + ) {
if ( ( id - > match_flags & AP_DEVICE_ID_MATCH_DEVICE_TYPE ) & &
( id - > dev_type ! = ap_dev - > device_type ) )
continue ;
return 1 ;
}
return 0 ;
}
/**
2008-04-17 07:46:28 +02:00
* ap_uevent ( ) : Uevent function for AP devices .
* @ dev : Pointer to device
* @ env : Pointer to kobj_uevent_env
*
* It sets up a single environment variable DEV_TYPE which contains the
* hardware device type .
2006-09-20 15:58:25 +02:00
*/
2007-08-14 15:15:12 +02:00
static int ap_uevent ( struct device * dev , struct kobj_uevent_env * env )
2006-09-20 15:58:25 +02:00
{
struct ap_device * ap_dev = to_ap_dev ( dev ) ;
2007-08-14 15:15:12 +02:00
int retval = 0 ;
2006-09-20 15:58:25 +02:00
if ( ! ap_dev )
return - ENODEV ;
/* Set up DEV_TYPE environment variable. */
2007-08-14 15:15:12 +02:00
retval = add_uevent_var ( env , " DEV_TYPE=%04X " , ap_dev - > device_type ) ;
2007-03-30 22:23:12 -07:00
if ( retval )
return retval ;
2006-12-04 15:40:10 +01:00
/* Add MODALIAS= */
2007-08-14 15:15:12 +02:00
retval = add_uevent_var ( env , " MODALIAS=ap:t%02X " , ap_dev - > device_type ) ;
2007-03-30 22:23:12 -07:00
return retval ;
2006-09-20 15:58:25 +02:00
}
2009-06-22 12:08:16 +02:00
static int ap_bus_suspend ( struct device * dev , pm_message_t state )
{
struct ap_device * ap_dev = to_ap_dev ( dev ) ;
unsigned long flags ;
if ( ! ap_suspend_flag ) {
ap_suspend_flag = 1 ;
/* Disable scanning for devices, thus we do not want to scan
* for them after removing .
*/
del_timer_sync ( & ap_config_timer ) ;
if ( ap_work_queue ! = NULL ) {
destroy_workqueue ( ap_work_queue ) ;
ap_work_queue = NULL ;
}
tasklet_disable ( & ap_tasklet ) ;
}
/* Poll on the device until all requests are finished. */
do {
flags = 0 ;
__ap_poll_device ( ap_dev , & flags ) ;
} while ( ( flags & 1 ) | | ( flags & 2 ) ) ;
ap_device_remove ( dev ) ;
return 0 ;
}
static int ap_bus_resume ( struct device * dev )
{
int rc = 0 ;
struct ap_device * ap_dev = to_ap_dev ( dev ) ;
if ( ap_suspend_flag ) {
ap_suspend_flag = 0 ;
if ( ! ap_interrupts_available ( ) )
ap_interrupt_indicator = NULL ;
ap_device_probe ( dev ) ;
ap_reset ( ap_dev ) ;
setup_timer ( & ap_dev - > timeout , ap_request_timeout ,
( unsigned long ) ap_dev ) ;
ap_scan_bus ( NULL ) ;
init_timer ( & ap_config_timer ) ;
ap_config_timer . function = ap_config_timeout ;
ap_config_timer . data = 0 ;
ap_config_timer . expires = jiffies + ap_config_time * HZ ;
add_timer ( & ap_config_timer ) ;
ap_work_queue = create_singlethread_workqueue ( " kapwork " ) ;
if ( ! ap_work_queue )
return - ENOMEM ;
tasklet_enable ( & ap_tasklet ) ;
if ( ! ap_using_interrupts ( ) )
ap_schedule_poll_timer ( ) ;
else
tasklet_schedule ( & ap_tasklet ) ;
if ( ap_thread_flag )
rc = ap_poll_thread_start ( ) ;
} else {
ap_device_probe ( dev ) ;
ap_reset ( ap_dev ) ;
setup_timer ( & ap_dev - > timeout , ap_request_timeout ,
( unsigned long ) ap_dev ) ;
}
return rc ;
}
2006-09-20 15:58:25 +02:00
static struct bus_type ap_bus_type = {
. name = " ap " ,
. match = & ap_bus_match ,
. uevent = & ap_uevent ,
2009-06-22 12:08:16 +02:00
. suspend = ap_bus_suspend ,
. resume = ap_bus_resume
2006-09-20 15:58:25 +02:00
} ;
static int ap_device_probe ( struct device * dev )
{
struct ap_device * ap_dev = to_ap_dev ( dev ) ;
struct ap_driver * ap_drv = to_ap_drv ( dev - > driver ) ;
int rc ;
ap_dev - > drv = ap_drv ;
rc = ap_drv - > probe ? ap_drv - > probe ( ap_dev ) : - ENODEV ;
2008-03-05 12:37:13 +01:00
if ( ! rc ) {
2008-12-25 13:38:42 +01:00
spin_lock_bh ( & ap_device_list_lock ) ;
2008-03-05 12:37:13 +01:00
list_add ( & ap_dev - > list , & ap_device_list ) ;
2008-12-25 13:38:42 +01:00
spin_unlock_bh ( & ap_device_list_lock ) ;
2008-03-05 12:37:13 +01:00
}
2006-09-20 15:58:25 +02:00
return rc ;
}
/**
2008-04-17 07:46:28 +02:00
* __ap_flush_queue ( ) : Flush requests .
* @ ap_dev : Pointer to the AP device
*
2006-09-20 15:58:25 +02:00
* Flush all requests from the request / pending queue of an AP device .
*/
2007-02-05 21:18:53 +01:00
static void __ap_flush_queue ( struct ap_device * ap_dev )
2006-09-20 15:58:25 +02:00
{
struct ap_message * ap_msg , * next ;
list_for_each_entry_safe ( ap_msg , next , & ap_dev - > pendingq , list ) {
list_del_init ( & ap_msg - > list ) ;
ap_dev - > pendingq_count - - ;
ap_dev - > drv - > receive ( ap_dev , ap_msg , ERR_PTR ( - ENODEV ) ) ;
}
list_for_each_entry_safe ( ap_msg , next , & ap_dev - > requestq , list ) {
list_del_init ( & ap_msg - > list ) ;
ap_dev - > requestq_count - - ;
ap_dev - > drv - > receive ( ap_dev , ap_msg , ERR_PTR ( - ENODEV ) ) ;
}
}
void ap_flush_queue ( struct ap_device * ap_dev )
{
spin_lock_bh ( & ap_dev - > lock ) ;
__ap_flush_queue ( ap_dev ) ;
spin_unlock_bh ( & ap_dev - > lock ) ;
}
EXPORT_SYMBOL ( ap_flush_queue ) ;
static int ap_device_remove ( struct device * dev )
{
struct ap_device * ap_dev = to_ap_dev ( dev ) ;
struct ap_driver * ap_drv = ap_dev - > drv ;
2006-10-04 20:02:05 +02:00
ap_flush_queue ( ap_dev ) ;
2007-07-10 11:24:19 +02:00
del_timer_sync ( & ap_dev - > timeout ) ;
2008-12-25 13:38:42 +01:00
spin_lock_bh ( & ap_device_list_lock ) ;
2007-03-19 13:19:14 +01:00
list_del_init ( & ap_dev - > list ) ;
2008-12-25 13:38:42 +01:00
spin_unlock_bh ( & ap_device_list_lock ) ;
2008-03-05 12:37:13 +01:00
if ( ap_drv - > remove )
ap_drv - > remove ( ap_dev ) ;
2007-03-26 20:42:43 +02:00
spin_lock_bh ( & ap_dev - > lock ) ;
atomic_sub ( ap_dev - > queue_count , & ap_poll_requests ) ;
spin_unlock_bh ( & ap_dev - > lock ) ;
2006-09-20 15:58:25 +02:00
return 0 ;
}
int ap_driver_register ( struct ap_driver * ap_drv , struct module * owner ,
char * name )
{
struct device_driver * drv = & ap_drv - > driver ;
drv - > bus = & ap_bus_type ;
drv - > probe = ap_device_probe ;
drv - > remove = ap_device_remove ;
drv - > owner = owner ;
drv - > name = name ;
return driver_register ( drv ) ;
}
EXPORT_SYMBOL ( ap_driver_register ) ;
void ap_driver_unregister ( struct ap_driver * ap_drv )
{
driver_unregister ( & ap_drv - > driver ) ;
}
EXPORT_SYMBOL ( ap_driver_unregister ) ;
2008-04-17 07:46:28 +02:00
/*
2006-09-20 15:58:25 +02:00
* AP bus attributes .
*/
static ssize_t ap_domain_show ( struct bus_type * bus , char * buf )
{
return snprintf ( buf , PAGE_SIZE , " %d \n " , ap_domain_index ) ;
}
static BUS_ATTR ( ap_domain , 0444 , ap_domain_show , NULL ) ;
static ssize_t ap_config_time_show ( struct bus_type * bus , char * buf )
{
return snprintf ( buf , PAGE_SIZE , " %d \n " , ap_config_time ) ;
}
2008-12-25 13:38:41 +01:00
static ssize_t ap_interrupts_show ( struct bus_type * bus , char * buf )
{
return snprintf ( buf , PAGE_SIZE , " %d \n " ,
ap_using_interrupts ( ) ? 1 : 0 ) ;
}
static BUS_ATTR ( ap_interrupts , 0444 , ap_interrupts_show , NULL ) ;
2006-09-20 15:58:25 +02:00
static ssize_t ap_config_time_store ( struct bus_type * bus ,
const char * buf , size_t count )
{
int time ;
if ( sscanf ( buf , " %d \n " , & time ) ! = 1 | | time < 5 | | time > 120 )
return - EINVAL ;
ap_config_time = time ;
if ( ! timer_pending ( & ap_config_timer ) | |
! mod_timer ( & ap_config_timer , jiffies + ap_config_time * HZ ) ) {
ap_config_timer . expires = jiffies + ap_config_time * HZ ;
add_timer ( & ap_config_timer ) ;
}
return count ;
}
static BUS_ATTR ( config_time , 0644 , ap_config_time_show , ap_config_time_store ) ;
static ssize_t ap_poll_thread_show ( struct bus_type * bus , char * buf )
{
return snprintf ( buf , PAGE_SIZE , " %d \n " , ap_poll_kthread ? 1 : 0 ) ;
}
static ssize_t ap_poll_thread_store ( struct bus_type * bus ,
const char * buf , size_t count )
{
int flag , rc ;
if ( sscanf ( buf , " %d \n " , & flag ) ! = 1 )
return - EINVAL ;
if ( flag ) {
rc = ap_poll_thread_start ( ) ;
if ( rc )
return rc ;
}
else
ap_poll_thread_stop ( ) ;
return count ;
}
static BUS_ATTR ( poll_thread , 0644 , ap_poll_thread_show , ap_poll_thread_store ) ;
2008-07-14 09:59:08 +02:00
static ssize_t poll_timeout_show ( struct bus_type * bus , char * buf )
{
return snprintf ( buf , PAGE_SIZE , " %llu \n " , poll_timeout ) ;
}
static ssize_t poll_timeout_store ( struct bus_type * bus , const char * buf ,
size_t count )
{
unsigned long long time ;
ktime_t hr_time ;
/* 120 seconds = maximum poll interval */
2008-12-25 13:38:41 +01:00
if ( sscanf ( buf , " %llu \n " , & time ) ! = 1 | | time < 1 | |
time > 120000000000ULL )
2008-07-14 09:59:08 +02:00
return - EINVAL ;
poll_timeout = time ;
hr_time = ktime_set ( 0 , poll_timeout ) ;
if ( ! hrtimer_is_queued ( & ap_poll_timer ) | |
2008-09-01 15:20:30 -07:00
! hrtimer_forward ( & ap_poll_timer , hrtimer_get_expires ( & ap_poll_timer ) , hr_time ) ) {
hrtimer_set_expires ( & ap_poll_timer , hr_time ) ;
hrtimer_start_expires ( & ap_poll_timer , HRTIMER_MODE_ABS ) ;
2008-07-14 09:59:08 +02:00
}
return count ;
}
static BUS_ATTR ( poll_timeout , 0644 , poll_timeout_show , poll_timeout_store ) ;
2006-09-20 15:58:25 +02:00
static struct bus_attribute * const ap_bus_attrs [ ] = {
& bus_attr_ap_domain ,
& bus_attr_config_time ,
& bus_attr_poll_thread ,
2008-12-25 13:38:41 +01:00
& bus_attr_ap_interrupts ,
2008-07-14 09:59:08 +02:00
& bus_attr_poll_timeout ,
NULL ,
2006-09-20 15:58:25 +02:00
} ;
/**
2008-04-17 07:46:28 +02:00
* ap_select_domain ( ) : Select an AP domain .
*
* Pick one of the 16 AP domains .
2006-09-20 15:58:25 +02:00
*/
2007-02-05 21:18:53 +01:00
static int ap_select_domain ( void )
2006-09-20 15:58:25 +02:00
{
int queue_depth , device_type , count , max_count , best_domain ;
int rc , i , j ;
2008-04-17 07:46:28 +02:00
/*
2006-09-20 15:58:25 +02:00
* We want to use a single domain . Either the one specified with
* the " domain= " parameter or the domain with the maximum number
* of devices .
*/
if ( ap_domain_index > = 0 & & ap_domain_index < AP_DOMAINS )
/* Domain has already been selected. */
return 0 ;
best_domain = - 1 ;
max_count = 0 ;
for ( i = 0 ; i < AP_DOMAINS ; i + + ) {
count = 0 ;
for ( j = 0 ; j < AP_DEVICES ; j + + ) {
ap_qid_t qid = AP_MKQID ( j , i ) ;
rc = ap_query_queue ( qid , & queue_depth , & device_type ) ;
if ( rc )
continue ;
count + + ;
}
if ( count > max_count ) {
max_count = count ;
best_domain = i ;
}
}
if ( best_domain > = 0 ) {
ap_domain_index = best_domain ;
return 0 ;
}
return - ENODEV ;
}
/**
2008-04-17 07:46:28 +02:00
* ap_probe_device_type ( ) : Find the device type of an AP .
2006-09-20 15:58:25 +02:00
* @ ap_dev : pointer to the AP device .
2008-04-17 07:46:28 +02:00
*
* Find the device type if query queue returned a device type of 0.
2006-09-20 15:58:25 +02:00
*/
static int ap_probe_device_type ( struct ap_device * ap_dev )
{
static unsigned char msg [ ] = {
0x00 , 0x06 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x58 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x01 , 0x00 , 0x43 , 0x43 , 0x41 , 0x2d , 0x41 , 0x50 ,
0x50 , 0x4c , 0x20 , 0x20 , 0x20 , 0x01 , 0x01 , 0x01 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x50 , 0x4b , 0x00 , 0x00 ,
0x00 , 0x00 , 0x01 , 0x1c , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x05 , 0xb8 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x70 , 0x00 , 0x41 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x54 , 0x32 , 0x01 , 0x00 , 0xa0 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0xb8 , 0x05 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x0a , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x08 , 0x00 ,
0x49 , 0x43 , 0x53 , 0x46 , 0x20 , 0x20 , 0x20 , 0x20 ,
0x50 , 0x4b , 0x0a , 0x00 , 0x50 , 0x4b , 0x43 , 0x53 ,
0x2d , 0x31 , 0x2e , 0x32 , 0x37 , 0x00 , 0x11 , 0x22 ,
0x33 , 0x44 , 0x55 , 0x66 , 0x77 , 0x88 , 0x99 , 0x00 ,
0x11 , 0x22 , 0x33 , 0x44 , 0x55 , 0x66 , 0x77 , 0x88 ,
0x99 , 0x00 , 0x11 , 0x22 , 0x33 , 0x44 , 0x55 , 0x66 ,
0x77 , 0x88 , 0x99 , 0x00 , 0x11 , 0x22 , 0x33 , 0x44 ,
0x55 , 0x66 , 0x77 , 0x88 , 0x99 , 0x00 , 0x11 , 0x22 ,
0x33 , 0x44 , 0x55 , 0x66 , 0x77 , 0x88 , 0x99 , 0x00 ,
0x11 , 0x22 , 0x33 , 0x5d , 0x00 , 0x5b , 0x00 , 0x77 ,
0x88 , 0x1e , 0x00 , 0x00 , 0x57 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x04 , 0x00 , 0x00 , 0x4f , 0x00 , 0x00 , 0x00 ,
0x03 , 0x02 , 0x00 , 0x00 , 0x40 , 0x01 , 0x00 , 0x01 ,
0xce , 0x02 , 0x68 , 0x2d , 0x5f , 0xa9 , 0xde , 0x0c ,
0xf6 , 0xd2 , 0x7b , 0x58 , 0x4b , 0xf9 , 0x28 , 0x68 ,
0x3d , 0xb4 , 0xf4 , 0xef , 0x78 , 0xd5 , 0xbe , 0x66 ,
0x63 , 0x42 , 0xef , 0xf8 , 0xfd , 0xa4 , 0xf8 , 0xb0 ,
0x8e , 0x29 , 0xc2 , 0xc9 , 0x2e , 0xd8 , 0x45 , 0xb8 ,
0x53 , 0x8c , 0x6f , 0x4e , 0x72 , 0x8f , 0x6c , 0x04 ,
0x9c , 0x88 , 0xfc , 0x1e , 0xc5 , 0x83 , 0x55 , 0x57 ,
0xf7 , 0xdd , 0xfd , 0x4f , 0x11 , 0x36 , 0x95 , 0x5d ,
} ;
struct ap_queue_status status ;
unsigned long long psmid ;
char * reply ;
int rc , i ;
reply = ( void * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! reply ) {
rc = - ENOMEM ;
goto out ;
}
status = __ap_send ( ap_dev - > qid , 0x0102030405060708ULL ,
msg , sizeof ( msg ) ) ;
if ( status . response_code ! = AP_RESPONSE_NORMAL ) {
rc = - ENODEV ;
goto out_free ;
}
/* Wait for the test message to complete. */
for ( i = 0 ; i < 6 ; i + + ) {
mdelay ( 300 ) ;
status = __ap_recv ( ap_dev - > qid , & psmid , reply , 4096 ) ;
if ( status . response_code = = AP_RESPONSE_NORMAL & &
psmid = = 0x0102030405060708ULL )
break ;
}
if ( i < 6 ) {
/* Got an answer. */
if ( reply [ 0 ] = = 0x00 & & reply [ 1 ] = = 0x86 )
ap_dev - > device_type = AP_DEVICE_TYPE_PCICC ;
else
ap_dev - > device_type = AP_DEVICE_TYPE_PCICA ;
rc = 0 ;
} else
rc = - ENODEV ;
out_free :
free_page ( ( unsigned long ) reply ) ;
out :
return rc ;
}
2008-12-25 13:38:41 +01:00
static void ap_interrupt_handler ( void * unused1 , void * unused2 )
{
tasklet_schedule ( & ap_tasklet ) ;
}
2006-09-20 15:58:25 +02:00
/**
2008-04-17 07:46:28 +02:00
* __ap_scan_bus ( ) : Scan the AP bus .
* @ dev : Pointer to device
* @ data : Pointer to data
*
* Scan the AP bus for new devices .
2006-09-20 15:58:25 +02:00
*/
static int __ap_scan_bus ( struct device * dev , void * data )
{
return to_ap_dev ( dev ) - > qid = = ( ap_qid_t ) ( unsigned long ) data ;
}
static void ap_device_release ( struct device * dev )
{
struct ap_device * ap_dev = to_ap_dev ( dev ) ;
kfree ( ap_dev ) ;
}
2006-12-06 19:18:20 +00:00
static void ap_scan_bus ( struct work_struct * unused )
2006-09-20 15:58:25 +02:00
{
struct ap_device * ap_dev ;
struct device * dev ;
ap_qid_t qid ;
int queue_depth , device_type ;
int rc , i ;
if ( ap_select_domain ( ) ! = 0 )
return ;
for ( i = 0 ; i < AP_DEVICES ; i + + ) {
qid = AP_MKQID ( i , ap_domain_index ) ;
dev = bus_find_device ( & ap_bus_type , NULL ,
( void * ) ( unsigned long ) qid ,
__ap_scan_bus ) ;
2006-10-27 12:39:26 +02:00
rc = ap_query_queue ( qid , & queue_depth , & device_type ) ;
2007-03-26 20:42:42 +02:00
if ( dev ) {
2007-07-10 11:24:19 +02:00
if ( rc = = - EBUSY ) {
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
schedule_timeout ( AP_RESET_TIMEOUT ) ;
rc = ap_query_queue ( qid , & queue_depth ,
& device_type ) ;
}
2007-03-26 20:42:42 +02:00
ap_dev = to_ap_dev ( dev ) ;
spin_lock_bh ( & ap_dev - > lock ) ;
if ( rc | | ap_dev - > unregistered ) {
spin_unlock_bh ( & ap_dev - > lock ) ;
device_unregister ( dev ) ;
2007-07-10 11:24:19 +02:00
put_device ( dev ) ;
2007-03-26 20:42:42 +02:00
continue ;
2007-07-10 11:24:19 +02:00
}
spin_unlock_bh ( & ap_dev - > lock ) ;
2006-09-20 15:58:25 +02:00
put_device ( dev ) ;
continue ;
}
if ( rc )
continue ;
rc = ap_init_queue ( qid ) ;
if ( rc )
continue ;
ap_dev = kzalloc ( sizeof ( * ap_dev ) , GFP_KERNEL ) ;
if ( ! ap_dev )
break ;
ap_dev - > qid = qid ;
ap_dev - > queue_depth = queue_depth ;
2006-10-04 20:02:05 +02:00
ap_dev - > unregistered = 1 ;
2006-09-20 15:58:25 +02:00
spin_lock_init ( & ap_dev - > lock ) ;
INIT_LIST_HEAD ( & ap_dev - > pendingq ) ;
INIT_LIST_HEAD ( & ap_dev - > requestq ) ;
2007-03-19 13:19:14 +01:00
INIT_LIST_HEAD ( & ap_dev - > list ) ;
2007-07-10 11:24:19 +02:00
setup_timer ( & ap_dev - > timeout , ap_request_timeout ,
( unsigned long ) ap_dev ) ;
2006-09-20 15:58:25 +02:00
if ( device_type = = 0 )
ap_probe_device_type ( ap_dev ) ;
else
ap_dev - > device_type = device_type ;
ap_dev - > device . bus = & ap_bus_type ;
ap_dev - > device . parent = ap_root_device ;
2008-10-10 21:33:10 +02:00
dev_set_name ( & ap_dev - > device , " card%02x " ,
AP_QID_DEVICE ( ap_dev - > qid ) ) ;
2006-09-20 15:58:25 +02:00
ap_dev - > device . release = ap_device_release ;
rc = device_register ( & ap_dev - > device ) ;
if ( rc ) {
kfree ( ap_dev ) ;
continue ;
}
/* Add device attributes. */
rc = sysfs_create_group ( & ap_dev - > device . kobj ,
& ap_dev_attr_group ) ;
2006-10-04 20:02:05 +02:00
if ( ! rc ) {
spin_lock_bh ( & ap_dev - > lock ) ;
ap_dev - > unregistered = 0 ;
spin_unlock_bh ( & ap_dev - > lock ) ;
}
else
2006-09-20 15:58:25 +02:00
device_unregister ( & ap_dev - > device ) ;
}
}
static void
ap_config_timeout ( unsigned long ptr )
{
queue_work ( ap_work_queue , & ap_config_work ) ;
ap_config_timer . expires = jiffies + ap_config_time * HZ ;
add_timer ( & ap_config_timer ) ;
}
/**
2008-04-17 07:46:28 +02:00
* ap_schedule_poll_timer ( ) : Schedule poll timer .
*
2006-09-20 15:58:25 +02:00
* Set up the timer to run the poll tasklet
*/
static inline void ap_schedule_poll_timer ( void )
{
2009-07-24 12:39:53 +02:00
ktime_t hr_time ;
2009-06-22 12:08:16 +02:00
if ( ap_using_interrupts ( ) | | ap_suspend_flag )
2008-12-25 13:38:41 +01:00
return ;
2008-07-14 09:59:08 +02:00
if ( hrtimer_is_queued ( & ap_poll_timer ) )
2006-09-20 15:58:25 +02:00
return ;
2009-07-24 12:39:53 +02:00
if ( ktime_to_ns ( hrtimer_expires_remaining ( & ap_poll_timer ) ) < = 0 ) {
hr_time = ktime_set ( 0 , poll_timeout ) ;
hrtimer_forward_now ( & ap_poll_timer , hr_time ) ;
hrtimer_restart ( & ap_poll_timer ) ;
}
return ;
2006-09-20 15:58:25 +02:00
}
/**
2008-04-17 07:46:28 +02:00
* ap_poll_read ( ) : Receive pending reply messages from an AP device .
2006-09-20 15:58:25 +02:00
* @ ap_dev : pointer to the AP device
* @ flags : pointer to control flags , bit 2 ^ 0 is set if another poll is
* required , bit 2 ^ 1 is set if the poll timer needs to get armed
2008-04-17 07:46:28 +02:00
*
2006-09-20 15:58:25 +02:00
* Returns 0 if the device is still present , - ENODEV if not .
*/
2007-02-05 21:18:53 +01:00
static int ap_poll_read ( struct ap_device * ap_dev , unsigned long * flags )
2006-09-20 15:58:25 +02:00
{
struct ap_queue_status status ;
struct ap_message * ap_msg ;
if ( ap_dev - > queue_count < = 0 )
return 0 ;
status = __ap_recv ( ap_dev - > qid , & ap_dev - > reply - > psmid ,
ap_dev - > reply - > message , ap_dev - > reply - > length ) ;
switch ( status . response_code ) {
case AP_RESPONSE_NORMAL :
atomic_dec ( & ap_poll_requests ) ;
2007-07-10 11:24:19 +02:00
ap_decrease_queue_count ( ap_dev ) ;
2006-09-20 15:58:25 +02:00
list_for_each_entry ( ap_msg , & ap_dev - > pendingq , list ) {
if ( ap_msg - > psmid ! = ap_dev - > reply - > psmid )
continue ;
list_del_init ( & ap_msg - > list ) ;
ap_dev - > pendingq_count - - ;
ap_dev - > drv - > receive ( ap_dev , ap_msg , ap_dev - > reply ) ;
break ;
}
if ( ap_dev - > queue_count > 0 )
* flags | = 1 ;
break ;
case AP_RESPONSE_NO_PENDING_REPLY :
if ( status . queue_empty ) {
/* The card shouldn't forget requests but who knows. */
2007-03-26 20:42:43 +02:00
atomic_sub ( ap_dev - > queue_count , & ap_poll_requests ) ;
2006-09-20 15:58:25 +02:00
ap_dev - > queue_count = 0 ;
list_splice_init ( & ap_dev - > pendingq , & ap_dev - > requestq ) ;
ap_dev - > requestq_count + = ap_dev - > pendingq_count ;
ap_dev - > pendingq_count = 0 ;
} else
* flags | = 2 ;
break ;
default :
return - ENODEV ;
}
return 0 ;
}
/**
2008-04-17 07:46:28 +02:00
* ap_poll_write ( ) : Send messages from the request queue to an AP device .
2006-09-20 15:58:25 +02:00
* @ ap_dev : pointer to the AP device
* @ flags : pointer to control flags , bit 2 ^ 0 is set if another poll is
* required , bit 2 ^ 1 is set if the poll timer needs to get armed
2008-04-17 07:46:28 +02:00
*
2006-09-20 15:58:25 +02:00
* Returns 0 if the device is still present , - ENODEV if not .
*/
2007-02-05 21:18:53 +01:00
static int ap_poll_write ( struct ap_device * ap_dev , unsigned long * flags )
2006-09-20 15:58:25 +02:00
{
struct ap_queue_status status ;
struct ap_message * ap_msg ;
if ( ap_dev - > requestq_count < = 0 | |
ap_dev - > queue_count > = ap_dev - > queue_depth )
return 0 ;
/* Start the next request on the queue. */
ap_msg = list_entry ( ap_dev - > requestq . next , struct ap_message , list ) ;
status = __ap_send ( ap_dev - > qid , ap_msg - > psmid ,
ap_msg - > message , ap_msg - > length ) ;
switch ( status . response_code ) {
case AP_RESPONSE_NORMAL :
atomic_inc ( & ap_poll_requests ) ;
2007-07-10 11:24:19 +02:00
ap_increase_queue_count ( ap_dev ) ;
2006-09-20 15:58:25 +02:00
list_move_tail ( & ap_msg - > list , & ap_dev - > pendingq ) ;
ap_dev - > requestq_count - - ;
ap_dev - > pendingq_count + + ;
if ( ap_dev - > queue_count < ap_dev - > queue_depth & &
ap_dev - > requestq_count > 0 )
* flags | = 1 ;
* flags | = 2 ;
break ;
case AP_RESPONSE_Q_FULL :
2007-07-10 11:24:19 +02:00
case AP_RESPONSE_RESET_IN_PROGRESS :
2006-09-20 15:58:25 +02:00
* flags | = 2 ;
break ;
case AP_RESPONSE_MESSAGE_TOO_BIG :
return - EINVAL ;
default :
return - ENODEV ;
}
return 0 ;
}
/**
2008-04-17 07:46:28 +02:00
* ap_poll_queue ( ) : Poll AP device for pending replies and send new messages .
2006-09-20 15:58:25 +02:00
* @ ap_dev : pointer to the bus device
* @ flags : pointer to control flags , bit 2 ^ 0 is set if another poll is
* required , bit 2 ^ 1 is set if the poll timer needs to get armed
2008-04-17 07:46:28 +02:00
*
* Poll AP device for pending replies and send new messages . If either
* ap_poll_read or ap_poll_write returns - ENODEV unregister the device .
2006-09-20 15:58:25 +02:00
* Returns 0.
*/
static inline int ap_poll_queue ( struct ap_device * ap_dev , unsigned long * flags )
{
int rc ;
rc = ap_poll_read ( ap_dev , flags ) ;
if ( rc )
return rc ;
return ap_poll_write ( ap_dev , flags ) ;
}
/**
2008-04-17 07:46:28 +02:00
* __ap_queue_message ( ) : Queue a message to a device .
2006-09-20 15:58:25 +02:00
* @ ap_dev : pointer to the AP device
* @ ap_msg : the message to be queued
2008-04-17 07:46:28 +02:00
*
* Queue a message to a device . Returns 0 if successful .
2006-09-20 15:58:25 +02:00
*/
static int __ap_queue_message ( struct ap_device * ap_dev , struct ap_message * ap_msg )
{
struct ap_queue_status status ;
if ( list_empty ( & ap_dev - > requestq ) & &
ap_dev - > queue_count < ap_dev - > queue_depth ) {
status = __ap_send ( ap_dev - > qid , ap_msg - > psmid ,
ap_msg - > message , ap_msg - > length ) ;
switch ( status . response_code ) {
case AP_RESPONSE_NORMAL :
list_add_tail ( & ap_msg - > list , & ap_dev - > pendingq ) ;
atomic_inc ( & ap_poll_requests ) ;
ap_dev - > pendingq_count + + ;
2007-07-10 11:24:19 +02:00
ap_increase_queue_count ( ap_dev ) ;
2006-09-20 15:58:25 +02:00
ap_dev - > total_request_count + + ;
break ;
case AP_RESPONSE_Q_FULL :
2007-07-10 11:24:19 +02:00
case AP_RESPONSE_RESET_IN_PROGRESS :
2006-09-20 15:58:25 +02:00
list_add_tail ( & ap_msg - > list , & ap_dev - > requestq ) ;
ap_dev - > requestq_count + + ;
ap_dev - > total_request_count + + ;
return - EBUSY ;
case AP_RESPONSE_MESSAGE_TOO_BIG :
ap_dev - > drv - > receive ( ap_dev , ap_msg , ERR_PTR ( - EINVAL ) ) ;
return - EINVAL ;
default : /* Device is gone. */
ap_dev - > drv - > receive ( ap_dev , ap_msg , ERR_PTR ( - ENODEV ) ) ;
return - ENODEV ;
}
} else {
list_add_tail ( & ap_msg - > list , & ap_dev - > requestq ) ;
ap_dev - > requestq_count + + ;
ap_dev - > total_request_count + + ;
return - EBUSY ;
}
ap_schedule_poll_timer ( ) ;
return 0 ;
}
void ap_queue_message ( struct ap_device * ap_dev , struct ap_message * ap_msg )
{
unsigned long flags ;
int rc ;
spin_lock_bh ( & ap_dev - > lock ) ;
if ( ! ap_dev - > unregistered ) {
/* Make room on the queue by polling for finished requests. */
rc = ap_poll_queue ( ap_dev , & flags ) ;
if ( ! rc )
rc = __ap_queue_message ( ap_dev , ap_msg ) ;
if ( ! rc )
wake_up ( & ap_poll_wait ) ;
2006-10-04 20:02:05 +02:00
if ( rc = = - ENODEV )
ap_dev - > unregistered = 1 ;
2006-09-20 15:58:25 +02:00
} else {
ap_dev - > drv - > receive ( ap_dev , ap_msg , ERR_PTR ( - ENODEV ) ) ;
2007-03-26 20:42:42 +02:00
rc = - ENODEV ;
2006-09-20 15:58:25 +02:00
}
spin_unlock_bh ( & ap_dev - > lock ) ;
if ( rc = = - ENODEV )
device_unregister ( & ap_dev - > device ) ;
}
EXPORT_SYMBOL ( ap_queue_message ) ;
/**
2008-04-17 07:46:28 +02:00
* ap_cancel_message ( ) : Cancel a crypto request .
* @ ap_dev : The AP device that has the message queued
* @ ap_msg : The message that is to be removed
*
2006-09-20 15:58:25 +02:00
* Cancel a crypto request . This is done by removing the request
2008-04-17 07:46:28 +02:00
* from the device pending or request queue . Note that the
2006-09-20 15:58:25 +02:00
* request stays on the AP queue . When it finishes the message
* reply will be discarded because the psmid can ' t be found .
*/
void ap_cancel_message ( struct ap_device * ap_dev , struct ap_message * ap_msg )
{
struct ap_message * tmp ;
spin_lock_bh ( & ap_dev - > lock ) ;
if ( ! list_empty ( & ap_msg - > list ) ) {
list_for_each_entry ( tmp , & ap_dev - > pendingq , list )
if ( tmp - > psmid = = ap_msg - > psmid ) {
ap_dev - > pendingq_count - - ;
goto found ;
}
ap_dev - > requestq_count - - ;
found :
list_del_init ( & ap_msg - > list ) ;
}
spin_unlock_bh ( & ap_dev - > lock ) ;
}
EXPORT_SYMBOL ( ap_cancel_message ) ;
/**
2008-04-17 07:46:28 +02:00
* ap_poll_timeout ( ) : AP receive polling for finished AP requests .
2008-07-14 09:59:08 +02:00
* @ unused : Unused pointer .
2008-04-17 07:46:28 +02:00
*
2008-07-14 09:59:08 +02:00
* Schedules the AP tasklet using a high resolution timer .
2006-09-20 15:58:25 +02:00
*/
2008-07-14 09:59:08 +02:00
static enum hrtimer_restart ap_poll_timeout ( struct hrtimer * unused )
2006-09-20 15:58:25 +02:00
{
tasklet_schedule ( & ap_tasklet ) ;
2008-07-14 09:59:08 +02:00
return HRTIMER_NORESTART ;
2006-09-20 15:58:25 +02:00
}
2007-07-10 11:24:19 +02:00
/**
2008-04-17 07:46:28 +02:00
* ap_reset ( ) : Reset a not responding AP device .
* @ ap_dev : Pointer to the AP device
*
2007-07-10 11:24:19 +02:00
* Reset a not responding AP device and move all requests from the
* pending queue to the request queue .
*/
static void ap_reset ( struct ap_device * ap_dev )
{
int rc ;
ap_dev - > reset = AP_RESET_IGNORE ;
atomic_sub ( ap_dev - > queue_count , & ap_poll_requests ) ;
ap_dev - > queue_count = 0 ;
list_splice_init ( & ap_dev - > pendingq , & ap_dev - > requestq ) ;
ap_dev - > requestq_count + = ap_dev - > pendingq_count ;
ap_dev - > pendingq_count = 0 ;
rc = ap_init_queue ( ap_dev - > qid ) ;
if ( rc = = - ENODEV )
ap_dev - > unregistered = 1 ;
}
2008-12-25 13:38:42 +01:00
static int __ap_poll_device ( struct ap_device * ap_dev , unsigned long * flags )
2006-09-20 15:58:25 +02:00
{
spin_lock ( & ap_dev - > lock ) ;
if ( ! ap_dev - > unregistered ) {
2007-03-26 20:42:42 +02:00
if ( ap_poll_queue ( ap_dev , flags ) )
2006-10-04 20:02:05 +02:00
ap_dev - > unregistered = 1 ;
2007-07-10 11:24:19 +02:00
if ( ap_dev - > reset = = AP_RESET_DO )
ap_reset ( ap_dev ) ;
2007-03-26 20:42:42 +02:00
}
2006-09-20 15:58:25 +02:00
spin_unlock ( & ap_dev - > lock ) ;
return 0 ;
}
2008-04-17 07:46:28 +02:00
/**
* ap_poll_all ( ) : Poll all AP devices .
* @ dummy : Unused variable
*
* Poll all AP devices on the bus in a round robin fashion . Continue
* polling until bit 2 ^ 0 of the control flags is not set . If bit 2 ^ 1
* of the control flags has been set arm the poll timer .
*/
2006-09-20 15:58:25 +02:00
static void ap_poll_all ( unsigned long dummy )
{
unsigned long flags ;
2007-03-19 13:19:14 +01:00
struct ap_device * ap_dev ;
2006-09-20 15:58:25 +02:00
2008-12-25 13:38:41 +01:00
/* Reset the indicator if interrupts are used. Thus new interrupts can
* be received . Doing it in the beginning of the tasklet is therefor
* important that no requests on any AP get lost .
*/
if ( ap_using_interrupts ( ) )
xchg ( ( u8 * ) ap_interrupt_indicator , 0 ) ;
2006-09-20 15:58:25 +02:00
do {
flags = 0 ;
2008-12-25 13:38:42 +01:00
spin_lock ( & ap_device_list_lock ) ;
2007-03-19 13:19:14 +01:00
list_for_each_entry ( ap_dev , & ap_device_list , list ) {
2008-12-25 13:38:42 +01:00
__ap_poll_device ( ap_dev , & flags ) ;
2007-03-19 13:19:14 +01:00
}
2008-12-25 13:38:42 +01:00
spin_unlock ( & ap_device_list_lock ) ;
2006-09-20 15:58:25 +02:00
} while ( flags & 1 ) ;
if ( flags & 2 )
ap_schedule_poll_timer ( ) ;
}
/**
2008-04-17 07:46:28 +02:00
* ap_poll_thread ( ) : Thread that polls for finished requests .
* @ data : Unused pointer
*
2006-09-20 15:58:25 +02:00
* AP bus poll thread . The purpose of this thread is to poll for
* finished requests in a loop if there is a " free " cpu - that is
* a cpu that doesn ' t have anything better to do . The polling stops
* as soon as there is another task or if all messages have been
* delivered .
*/
static int ap_poll_thread ( void * data )
{
DECLARE_WAITQUEUE ( wait , current ) ;
unsigned long flags ;
int requests ;
2007-03-19 13:19:14 +01:00
struct ap_device * ap_dev ;
2006-09-20 15:58:25 +02:00
2006-10-06 16:38:22 +02:00
set_user_nice ( current , 19 ) ;
2006-09-20 15:58:25 +02:00
while ( 1 ) {
2009-06-22 12:08:16 +02:00
if ( ap_suspend_flag )
return 0 ;
2006-09-20 15:58:25 +02:00
if ( need_resched ( ) ) {
schedule ( ) ;
continue ;
}
add_wait_queue ( & ap_poll_wait , & wait ) ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
if ( kthread_should_stop ( ) )
break ;
requests = atomic_read ( & ap_poll_requests ) ;
if ( requests < = 0 )
schedule ( ) ;
set_current_state ( TASK_RUNNING ) ;
remove_wait_queue ( & ap_poll_wait , & wait ) ;
flags = 0 ;
2008-12-25 13:38:42 +01:00
spin_lock_bh ( & ap_device_list_lock ) ;
2007-03-19 13:19:14 +01:00
list_for_each_entry ( ap_dev , & ap_device_list , list ) {
2008-12-25 13:38:42 +01:00
__ap_poll_device ( ap_dev , & flags ) ;
2007-03-19 13:19:14 +01:00
}
2008-12-25 13:38:42 +01:00
spin_unlock_bh ( & ap_device_list_lock ) ;
2006-09-20 15:58:25 +02:00
}
set_current_state ( TASK_RUNNING ) ;
remove_wait_queue ( & ap_poll_wait , & wait ) ;
return 0 ;
}
static int ap_poll_thread_start ( void )
{
int rc ;
2009-06-22 12:08:16 +02:00
if ( ap_using_interrupts ( ) | | ap_suspend_flag )
2008-12-25 13:38:41 +01:00
return 0 ;
2006-09-20 15:58:25 +02:00
mutex_lock ( & ap_poll_thread_mutex ) ;
if ( ! ap_poll_kthread ) {
ap_poll_kthread = kthread_run ( ap_poll_thread , NULL , " appoll " ) ;
rc = IS_ERR ( ap_poll_kthread ) ? PTR_ERR ( ap_poll_kthread ) : 0 ;
if ( rc )
ap_poll_kthread = NULL ;
}
else
rc = 0 ;
mutex_unlock ( & ap_poll_thread_mutex ) ;
return rc ;
}
static void ap_poll_thread_stop ( void )
{
mutex_lock ( & ap_poll_thread_mutex ) ;
if ( ap_poll_kthread ) {
kthread_stop ( ap_poll_kthread ) ;
ap_poll_kthread = NULL ;
}
mutex_unlock ( & ap_poll_thread_mutex ) ;
}
2007-07-10 11:24:19 +02:00
/**
2008-04-17 07:46:28 +02:00
* ap_request_timeout ( ) : Handling of request timeouts
* @ data : Holds the AP device .
*
* Handles request timeouts .
2007-07-10 11:24:19 +02:00
*/
static void ap_request_timeout ( unsigned long data )
{
struct ap_device * ap_dev = ( struct ap_device * ) data ;
2008-12-25 13:38:41 +01:00
if ( ap_dev - > reset = = AP_RESET_ARMED ) {
2007-07-10 11:24:19 +02:00
ap_dev - > reset = AP_RESET_DO ;
2008-12-25 13:38:41 +01:00
if ( ap_using_interrupts ( ) )
tasklet_schedule ( & ap_tasklet ) ;
}
2007-07-10 11:24:19 +02:00
}
2006-12-15 17:18:17 +01:00
static void ap_reset_domain ( void )
{
int i ;
2007-10-12 16:11:29 +02:00
if ( ap_domain_index ! = - 1 )
for ( i = 0 ; i < AP_DEVICES ; i + + )
ap_reset_queue ( AP_MKQID ( i , ap_domain_index ) ) ;
2006-12-15 17:18:17 +01:00
}
static void ap_reset_all ( void )
2006-12-08 15:54:07 +01:00
{
int i , j ;
for ( i = 0 ; i < AP_DOMAINS ; i + + )
for ( j = 0 ; j < AP_DEVICES ; j + + )
ap_reset_queue ( AP_MKQID ( j , i ) ) ;
}
static struct reset_call ap_reset_call = {
2006-12-15 17:18:17 +01:00
. fn = ap_reset_all ,
2006-12-08 15:54:07 +01:00
} ;
2006-09-20 15:58:25 +02:00
/**
2008-04-17 07:46:28 +02:00
* ap_module_init ( ) : The module initialization code .
*
* Initializes the module .
2006-09-20 15:58:25 +02:00
*/
int __init ap_module_init ( void )
{
int rc , i ;
if ( ap_domain_index < - 1 | | ap_domain_index > = AP_DOMAINS ) {
2008-12-25 13:39:46 +01:00
pr_warning ( " %d is not a valid cryptographic domain \n " ,
ap_domain_index ) ;
2006-09-20 15:58:25 +02:00
return - EINVAL ;
}
if ( ap_instructions_available ( ) ! = 0 ) {
2008-12-25 13:39:46 +01:00
pr_warning ( " The hardware system does not support "
" AP instructions \n " ) ;
2006-09-20 15:58:25 +02:00
return - ENODEV ;
}
2008-12-25 13:38:41 +01:00
if ( ap_interrupts_available ( ) ) {
isc_register ( AP_ISC ) ;
ap_interrupt_indicator = s390_register_adapter_interrupt (
& ap_interrupt_handler , NULL , AP_ISC ) ;
if ( IS_ERR ( ap_interrupt_indicator ) ) {
ap_interrupt_indicator = NULL ;
isc_unregister ( AP_ISC ) ;
}
}
2006-12-08 15:54:07 +01:00
register_reset_call ( & ap_reset_call ) ;
2006-09-20 15:58:25 +02:00
/* Create /sys/bus/ap. */
rc = bus_register ( & ap_bus_type ) ;
if ( rc )
goto out ;
for ( i = 0 ; ap_bus_attrs [ i ] ; i + + ) {
rc = bus_create_file ( & ap_bus_type , ap_bus_attrs [ i ] ) ;
if ( rc )
goto out_bus ;
}
/* Create /sys/devices/ap. */
2008-12-15 12:58:29 +00:00
ap_root_device = root_device_register ( " ap " ) ;
2006-09-20 15:58:25 +02:00
rc = IS_ERR ( ap_root_device ) ? PTR_ERR ( ap_root_device ) : 0 ;
if ( rc )
goto out_bus ;
ap_work_queue = create_singlethread_workqueue ( " kapwork " ) ;
if ( ! ap_work_queue ) {
rc = - ENOMEM ;
goto out_root ;
}
if ( ap_select_domain ( ) = = 0 )
ap_scan_bus ( NULL ) ;
2008-04-17 07:46:28 +02:00
/* Setup the AP bus rescan timer. */
2006-09-20 15:58:25 +02:00
init_timer ( & ap_config_timer ) ;
ap_config_timer . function = ap_config_timeout ;
ap_config_timer . data = 0 ;
ap_config_timer . expires = jiffies + ap_config_time * HZ ;
add_timer ( & ap_config_timer ) ;
2008-07-14 09:59:08 +02:00
/* Setup the high resultion poll timer.
* If we are running under z / VM adjust polling to z / VM polling rate .
*/
if ( MACHINE_IS_VM )
poll_timeout = 1500000 ;
hrtimer_init ( & ap_poll_timer , CLOCK_MONOTONIC , HRTIMER_MODE_ABS ) ;
ap_poll_timer . function = ap_poll_timeout ;
2006-09-20 15:58:25 +02:00
/* Start the low priority AP bus poll thread. */
if ( ap_thread_flag ) {
rc = ap_poll_thread_start ( ) ;
if ( rc )
goto out_work ;
}
return 0 ;
out_work :
del_timer_sync ( & ap_config_timer ) ;
2008-07-14 09:59:08 +02:00
hrtimer_cancel ( & ap_poll_timer ) ;
2006-09-20 15:58:25 +02:00
destroy_workqueue ( ap_work_queue ) ;
out_root :
2008-12-15 12:58:29 +00:00
root_device_unregister ( ap_root_device ) ;
2006-09-20 15:58:25 +02:00
out_bus :
while ( i - - )
bus_remove_file ( & ap_bus_type , ap_bus_attrs [ i ] ) ;
bus_unregister ( & ap_bus_type ) ;
out :
2006-12-08 15:54:07 +01:00
unregister_reset_call ( & ap_reset_call ) ;
2008-12-25 13:38:41 +01:00
if ( ap_using_interrupts ( ) ) {
s390_unregister_adapter_interrupt ( ap_interrupt_indicator , AP_ISC ) ;
isc_unregister ( AP_ISC ) ;
}
2006-09-20 15:58:25 +02:00
return rc ;
}
static int __ap_match_all ( struct device * dev , void * data )
{
return 1 ;
}
/**
2008-04-17 07:46:28 +02:00
* ap_modules_exit ( ) : The module termination code
*
* Terminates the module .
2006-09-20 15:58:25 +02:00
*/
void ap_module_exit ( void )
{
int i ;
struct device * dev ;
2006-12-15 17:18:17 +01:00
ap_reset_domain ( ) ;
2006-09-20 15:58:25 +02:00
ap_poll_thread_stop ( ) ;
del_timer_sync ( & ap_config_timer ) ;
2008-07-14 09:59:08 +02:00
hrtimer_cancel ( & ap_poll_timer ) ;
2006-09-20 15:58:25 +02:00
destroy_workqueue ( ap_work_queue ) ;
2006-12-15 17:18:17 +01:00
tasklet_kill ( & ap_tasklet ) ;
2008-12-15 12:58:29 +00:00
root_device_unregister ( ap_root_device ) ;
2006-09-20 15:58:25 +02:00
while ( ( dev = bus_find_device ( & ap_bus_type , NULL , NULL ,
__ap_match_all ) ) )
{
device_unregister ( dev ) ;
put_device ( dev ) ;
}
for ( i = 0 ; ap_bus_attrs [ i ] ; i + + )
bus_remove_file ( & ap_bus_type , ap_bus_attrs [ i ] ) ;
bus_unregister ( & ap_bus_type ) ;
2006-12-08 15:54:07 +01:00
unregister_reset_call ( & ap_reset_call ) ;
2008-12-25 13:38:41 +01:00
if ( ap_using_interrupts ( ) ) {
s390_unregister_adapter_interrupt ( ap_interrupt_indicator , AP_ISC ) ;
isc_unregister ( AP_ISC ) ;
}
2006-09-20 15:58:25 +02:00
}
# ifndef CONFIG_ZCRYPT_MONOLITHIC
module_init ( ap_module_init ) ;
module_exit ( ap_module_exit ) ;
# endif