2007-02-07 20:19:37 +03:00
/*
* Provides ACPI support for IDE drives .
*
* Copyright ( C ) 2005 Intel Corp .
* Copyright ( C ) 2005 Randy Dunlap
* Copyright ( C ) 2006 SUSE Linux Products GmbH
* Copyright ( C ) 2006 Hannes Reinecke
*/
# include <linux/ata.h>
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <acpi/acpi.h>
# include <linux/ide.h>
# include <linux/pci.h>
2008-01-11 01:03:42 +03:00
# include <linux/dmi.h>
2007-02-07 20:19:37 +03:00
# include <acpi/acpi_bus.h>
# define REGS_PER_GTF 7
struct taskfile_array {
u8 tfa [ REGS_PER_GTF ] ; /* regs. 0x1f1 - 0x1f7 */
} ;
struct GTM_buffer {
u32 PIO_speed0 ;
u32 DMA_speed0 ;
u32 PIO_speed1 ;
u32 DMA_speed1 ;
u32 GTM_flags ;
} ;
struct ide_acpi_drive_link {
acpi_handle obj_handle ;
u8 idbuff [ 512 ] ;
} ;
struct ide_acpi_hwif_link {
ide_hwif_t * hwif ;
acpi_handle obj_handle ;
struct GTM_buffer gtm ;
struct ide_acpi_drive_link master ;
struct ide_acpi_drive_link slave ;
} ;
# undef DEBUGGING
/* note: adds function name and KERN_DEBUG */
# ifdef DEBUGGING
# define DEBPRINT(fmt, args...) \
2008-04-27 00:25:20 +04:00
printk ( KERN_DEBUG " %s: " fmt , __func__ , # # args )
2007-02-07 20:19:37 +03:00
# else
# define DEBPRINT(fmt, args...) do {} while (0)
# endif /* DEBUGGING */
2008-07-15 23:21:47 +04:00
static int ide_noacpi ;
2008-04-27 17:38:30 +04:00
module_param_named ( noacpi , ide_noacpi , bool , 0 ) ;
MODULE_PARM_DESC ( noacpi , " disable IDE ACPI support " ) ;
2008-07-15 23:21:47 +04:00
static int ide_acpigtf ;
2008-04-27 17:38:30 +04:00
module_param_named ( acpigtf , ide_acpigtf , bool , 0 ) ;
MODULE_PARM_DESC ( acpigtf , " enable IDE ACPI _GTF support " ) ;
2008-07-15 23:21:47 +04:00
static int ide_acpionboot ;
2008-04-27 17:38:30 +04:00
module_param_named ( acpionboot , ide_acpionboot , bool , 0 ) ;
MODULE_PARM_DESC ( acpionboot , " call IDE ACPI methods on boot " ) ;
2007-02-07 20:19:37 +03:00
2008-01-11 01:03:42 +03:00
static bool ide_noacpi_psx ;
static int no_acpi_psx ( const struct dmi_system_id * id )
{
ide_noacpi_psx = true ;
printk ( KERN_NOTICE " %s detected - disable ACPI _PSx. \n " , id - > ident ) ;
return 0 ;
}
static const struct dmi_system_id ide_acpi_dmi_table [ ] = {
/* Bug 9673. */
/* We should check if this is because ACPI NVS isn't save/restored. */
{
. callback = no_acpi_psx ,
. ident = " HP nx9005 " ,
. matches = {
DMI_MATCH ( DMI_BIOS_VENDOR , " Phoenix Technologies Ltd. " ) ,
DMI_MATCH ( DMI_BIOS_VERSION , " KAM1.60 " )
} ,
} ,
2008-01-11 07:59:18 +03:00
{ } /* terminate list */
2008-01-11 01:03:42 +03:00
} ;
static int ide_acpi_blacklist ( void )
{
static int done ;
if ( done )
return 0 ;
done = 1 ;
dmi_check_system ( ide_acpi_dmi_table ) ;
return 0 ;
}
2007-02-07 20:19:37 +03:00
/**
* ide_get_dev_handle - finds acpi_handle and PCI device . function
* @ dev : device to locate
* @ handle : returned acpi_handle for @ dev
* @ pcidevfn : return PCI device . func for @ dev
*
* Returns the ACPI object handle to the corresponding PCI device .
*
* Returns 0 on success , < 0 on error .
*/
static int ide_get_dev_handle ( struct device * dev , acpi_handle * handle ,
acpi_integer * pcidevfn )
{
struct pci_dev * pdev = to_pci_dev ( dev ) ;
unsigned int bus , devnum , func ;
acpi_integer addr ;
acpi_handle dev_handle ;
struct acpi_buffer buffer = { . length = ACPI_ALLOCATE_BUFFER ,
. pointer = NULL } ;
acpi_status status ;
struct acpi_device_info * dinfo = NULL ;
int ret = - ENODEV ;
bus = pdev - > bus - > number ;
devnum = PCI_SLOT ( pdev - > devfn ) ;
func = PCI_FUNC ( pdev - > devfn ) ;
/* ACPI _ADR encoding for PCI bus: */
addr = ( acpi_integer ) ( devnum < < 16 | func ) ;
DEBPRINT ( " ENTER: pci %02x:%02x.%01x \n " , bus , devnum , func ) ;
dev_handle = DEVICE_ACPI_HANDLE ( dev ) ;
if ( ! dev_handle ) {
DEBPRINT ( " no acpi handle for device \n " ) ;
goto err ;
}
status = acpi_get_object_info ( dev_handle , & buffer ) ;
if ( ACPI_FAILURE ( status ) ) {
DEBPRINT ( " get_object_info for device failed \n " ) ;
goto err ;
}
dinfo = buffer . pointer ;
if ( dinfo & & ( dinfo - > valid & ACPI_VALID_ADR ) & &
dinfo - > address = = addr ) {
* pcidevfn = addr ;
* handle = dev_handle ;
} else {
DEBPRINT ( " get_object_info for device has wrong "
" address: %llu, should be %u \n " ,
dinfo ? ( unsigned long long ) dinfo - > address : - 1ULL ,
( unsigned int ) addr ) ;
goto err ;
}
DEBPRINT ( " for dev=0x%x.%x, addr=0x%llx, *handle=0x%p \n " ,
devnum , func , ( unsigned long long ) addr , * handle ) ;
ret = 0 ;
err :
kfree ( dinfo ) ;
return ret ;
}
/**
* ide_acpi_hwif_get_handle - Get ACPI object handle for a given hwif
* @ hwif : device to locate
*
* Retrieves the object handle for a given hwif .
*
* Returns handle on success , 0 on error .
*/
static acpi_handle ide_acpi_hwif_get_handle ( ide_hwif_t * hwif )
{
struct device * dev = hwif - > gendev . parent ;
2008-02-06 04:57:49 +03:00
acpi_handle uninitialized_var ( dev_handle ) ;
2007-02-07 20:19:37 +03:00
acpi_integer pcidevfn ;
acpi_handle chan_handle ;
int err ;
DEBPRINT ( " ENTER: device %s \n " , hwif - > name ) ;
if ( ! dev ) {
DEBPRINT ( " no PCI device for %s \n " , hwif - > name ) ;
return NULL ;
}
err = ide_get_dev_handle ( dev , & dev_handle , & pcidevfn ) ;
if ( err < 0 ) {
DEBPRINT ( " ide_get_dev_handle failed (%d) \n " , err ) ;
return NULL ;
}
/* get child objects of dev_handle == channel objects,
* + _their_ children = = drive objects */
/* channel is hwif->channel */
chan_handle = acpi_get_child ( dev_handle , hwif - > channel ) ;
DEBPRINT ( " chan adr=%d: handle=0x%p \n " ,
hwif - > channel , chan_handle ) ;
return chan_handle ;
}
/**
* ide_acpi_drive_get_handle - Get ACPI object handle for a given drive
* @ drive : device to locate
*
* Retrieves the object handle of a given drive . According to the ACPI
* spec the drive is a child of the hwif .
*
* Returns handle on success , 0 on error .
*/
static acpi_handle ide_acpi_drive_get_handle ( ide_drive_t * drive )
{
2009-01-06 19:20:52 +03:00
ide_hwif_t * hwif = drive - > hwif ;
2007-02-07 20:19:37 +03:00
int port ;
acpi_handle drive_handle ;
if ( ! hwif - > acpidata )
return NULL ;
if ( ! hwif - > acpidata - > obj_handle )
return NULL ;
port = hwif - > channel ? drive - > dn - 2 : drive - > dn ;
DEBPRINT ( " ENTER: %s at channel#: %d port#: %d \n " ,
drive - > name , hwif - > channel , port ) ;
/* TBD: could also check ACPI object VALID bits */
drive_handle = acpi_get_child ( hwif - > acpidata - > obj_handle , port ) ;
DEBPRINT ( " drive %s handle 0x%p \n " , drive - > name , drive_handle ) ;
return drive_handle ;
}
/**
* do_drive_get_GTF - get the drive bootup default taskfile settings
* @ drive : the drive for which the taskfile settings should be retrieved
* @ gtf_length : number of bytes of _GTF data returned at @ gtf_address
* @ gtf_address : buffer containing _GTF taskfile arrays
*
* The _GTF method has no input parameters .
* It returns a variable number of register set values ( registers
* hex 1F 1. .1F 7 , taskfiles ) .
* The < variable number > is not known in advance , so have ACPI - CA
* allocate the buffer as needed and return it , then free it later .
*
* The returned @ gtf_length and @ gtf_address are only valid if the
* function return value is 0.
*/
static int do_drive_get_GTF ( ide_drive_t * drive ,
unsigned int * gtf_length , unsigned long * gtf_address ,
unsigned long * obj_loc )
{
acpi_status status ;
struct acpi_buffer output ;
union acpi_object * out_obj ;
2009-01-06 19:20:52 +03:00
ide_hwif_t * hwif = drive - > hwif ;
2007-02-07 20:19:37 +03:00
struct device * dev = hwif - > gendev . parent ;
int err = - ENODEV ;
int port ;
* gtf_length = 0 ;
* gtf_address = 0UL ;
* obj_loc = 0UL ;
if ( ide_noacpi )
return 0 ;
if ( ! dev ) {
DEBPRINT ( " no PCI device for %s \n " , hwif - > name ) ;
goto out ;
}
if ( ! hwif - > acpidata ) {
DEBPRINT ( " no ACPI data for %s \n " , hwif - > name ) ;
goto out ;
}
port = hwif - > channel ? drive - > dn - 2 : drive - > dn ;
DEBPRINT ( " ENTER: %s at %s, port#: %d, hard_port#: %d \n " ,
2009-02-02 22:12:21 +03:00
hwif - > name , dev_name ( dev ) , port , hwif - > channel ) ;
2007-02-07 20:19:37 +03:00
2008-10-13 23:39:36 +04:00
if ( ( drive - > dev_flags & IDE_DFLAG_PRESENT ) = = 0 ) {
2007-02-07 20:19:37 +03:00
DEBPRINT ( " %s drive %d:%d not present \n " ,
hwif - > name , hwif - > channel , port ) ;
goto out ;
}
/* Get this drive's _ADR info. if not already known. */
if ( ! drive - > acpidata - > obj_handle ) {
drive - > acpidata - > obj_handle = ide_acpi_drive_get_handle ( drive ) ;
if ( ! drive - > acpidata - > obj_handle ) {
DEBPRINT ( " No ACPI object found for %s \n " ,
drive - > name ) ;
goto out ;
}
}
/* Setting up output buffer */
output . length = ACPI_ALLOCATE_BUFFER ;
output . pointer = NULL ; /* ACPI-CA sets this; save/free it later */
/* _GTF has no input parameters */
err = - EIO ;
status = acpi_evaluate_object ( drive - > acpidata - > obj_handle , " _GTF " ,
NULL , & output ) ;
if ( ACPI_FAILURE ( status ) ) {
printk ( KERN_DEBUG
" %s: Run _GTF error: status = 0x%x \n " ,
2008-04-27 00:25:20 +04:00
__func__ , status ) ;
2007-02-07 20:19:37 +03:00
goto out ;
}
if ( ! output . length | | ! output . pointer ) {
DEBPRINT ( " Run _GTF: "
" length or ptr is NULL (0x%llx, 0x%p) \n " ,
( unsigned long long ) output . length ,
output . pointer ) ;
goto out ;
}
out_obj = output . pointer ;
if ( out_obj - > type ! = ACPI_TYPE_BUFFER ) {
DEBPRINT ( " Run _GTF: error: "
" expected object type of ACPI_TYPE_BUFFER, "
" got 0x%x \n " , out_obj - > type ) ;
err = - ENOENT ;
kfree ( output . pointer ) ;
goto out ;
}
if ( ! out_obj - > buffer . length | | ! out_obj - > buffer . pointer | |
out_obj - > buffer . length % REGS_PER_GTF ) {
printk ( KERN_ERR
" %s: unexpected GTF length (%d) or addr (0x%p) \n " ,
2008-04-27 00:25:20 +04:00
__func__ , out_obj - > buffer . length ,
2007-02-07 20:19:37 +03:00
out_obj - > buffer . pointer ) ;
err = - ENOENT ;
kfree ( output . pointer ) ;
goto out ;
}
* gtf_length = out_obj - > buffer . length ;
* gtf_address = ( unsigned long ) out_obj - > buffer . pointer ;
* obj_loc = ( unsigned long ) out_obj ;
DEBPRINT ( " returning gtf_length=%d, gtf_address=0x%lx, obj_loc=0x%lx \n " ,
* gtf_length , * gtf_address , * obj_loc ) ;
err = 0 ;
out :
return err ;
}
/**
* taskfile_load_raw - send taskfile registers to drive
* @ drive : drive to which output is sent
* @ gtf : raw ATA taskfile register set ( 0x1f1 - 0x1f7 )
*
* Outputs IDE taskfile to the drive .
*/
static int taskfile_load_raw ( ide_drive_t * drive ,
const struct taskfile_array * gtf )
{
ide_task_t args ;
int err = 0 ;
DEBPRINT ( " (0x1f1-1f7): hex: "
" %02x %02x %02x %02x %02x %02x %02x \n " ,
gtf - > tfa [ 0 ] , gtf - > tfa [ 1 ] , gtf - > tfa [ 2 ] ,
gtf - > tfa [ 3 ] , gtf - > tfa [ 4 ] , gtf - > tfa [ 5 ] , gtf - > tfa [ 6 ] ) ;
memset ( & args , 0 , sizeof ( ide_task_t ) ) ;
/* convert gtf to IDE Taskfile */
2008-01-26 00:17:06 +03:00
memcpy ( & args . tf_array [ 7 ] , & gtf - > tfa , 7 ) ;
2008-01-26 22:13:10 +03:00
args . tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE ;
2007-02-07 20:19:37 +03:00
2008-04-27 17:38:30 +04:00
if ( ! ide_acpigtf ) {
2007-02-07 20:19:37 +03:00
DEBPRINT ( " _GTF execution disabled \n " ) ;
return err ;
}
2008-01-26 00:17:07 +03:00
err = ide_no_data_taskfile ( drive , & args ) ;
2007-02-07 20:19:37 +03:00
if ( err )
2008-01-26 00:17:07 +03:00
printk ( KERN_ERR " %s: ide_no_data_taskfile failed: %u \n " ,
2008-04-27 00:25:20 +04:00
__func__ , err ) ;
2007-02-07 20:19:37 +03:00
return err ;
}
/**
* do_drive_set_taskfiles - write the drive taskfile settings from _GTF
* @ drive : the drive to which the taskfile command should be sent
* @ gtf_length : total number of bytes of _GTF taskfiles
* @ gtf_address : location of _GTF taskfile arrays
*
* Write { gtf_address , length gtf_length } in groups of
* REGS_PER_GTF bytes .
*/
static int do_drive_set_taskfiles ( ide_drive_t * drive ,
unsigned int gtf_length ,
unsigned long gtf_address )
{
int rc = - ENODEV , err ;
int gtf_count = gtf_length / REGS_PER_GTF ;
int ix ;
struct taskfile_array * gtf ;
if ( ide_noacpi )
return 0 ;
DEBPRINT ( " ENTER: %s, hard_port#: %d \n " , drive - > name , drive - > dn ) ;
2008-10-13 23:39:36 +04:00
if ( ( drive - > dev_flags & IDE_DFLAG_PRESENT ) = = 0 )
2007-02-07 20:19:37 +03:00
goto out ;
2008-10-13 23:39:36 +04:00
2007-02-07 20:19:37 +03:00
if ( ! gtf_count ) /* shouldn't be here */
goto out ;
DEBPRINT ( " total GTF bytes=%u (0x%x), gtf_count=%d, addr=0x%lx \n " ,
gtf_length , gtf_length , gtf_count , gtf_address ) ;
if ( gtf_length % REGS_PER_GTF ) {
printk ( KERN_ERR " %s: unexpected GTF length (%d) \n " ,
2008-04-27 00:25:20 +04:00
__func__ , gtf_length ) ;
2007-02-07 20:19:37 +03:00
goto out ;
}
rc = 0 ;
for ( ix = 0 ; ix < gtf_count ; ix + + ) {
gtf = ( struct taskfile_array * )
( gtf_address + ix * REGS_PER_GTF ) ;
/* send all TaskFile registers (0x1f1-0x1f7) *in*that*order* */
err = taskfile_load_raw ( drive , gtf ) ;
if ( err )
rc = err ;
}
out :
return rc ;
}
/**
* ide_acpi_exec_tfs - get then write drive taskfile settings
* @ drive : the drive for which the taskfile settings should be
* written .
*
* According to the ACPI spec this should be called after _STM
* has been evaluated for the interface . Some ACPI vendors interpret
* that as a hard requirement and modify the taskfile according
* to the Identify Drive information passed down with _STM .
* So one should really make sure to call this only after _STM has
* been executed .
*/
int ide_acpi_exec_tfs ( ide_drive_t * drive )
{
int ret ;
unsigned int gtf_length ;
unsigned long gtf_address ;
unsigned long obj_loc ;
if ( ide_noacpi )
return 0 ;
DEBPRINT ( " call get_GTF, drive=%s port=%d \n " , drive - > name , drive - > dn ) ;
ret = do_drive_get_GTF ( drive , & gtf_length , & gtf_address , & obj_loc ) ;
if ( ret < 0 ) {
DEBPRINT ( " get_GTF error (%d) \n " , ret ) ;
return ret ;
}
DEBPRINT ( " call set_taskfiles, drive=%s \n " , drive - > name ) ;
ret = do_drive_set_taskfiles ( drive , gtf_length , gtf_address ) ;
kfree ( ( void * ) obj_loc ) ;
if ( ret < 0 ) {
DEBPRINT ( " set_taskfiles error (%d) \n " , ret ) ;
}
DEBPRINT ( " ret=%d \n " , ret ) ;
return ret ;
}
/**
* ide_acpi_get_timing - get the channel ( controller ) timings
* @ hwif : target IDE interface ( channel )
*
* This function executes the _GTM ACPI method for the target channel .
*
*/
void ide_acpi_get_timing ( ide_hwif_t * hwif )
{
acpi_status status ;
struct acpi_buffer output ;
union acpi_object * out_obj ;
if ( ide_noacpi )
return ;
DEBPRINT ( " ENTER: \n " ) ;
if ( ! hwif - > acpidata ) {
DEBPRINT ( " no ACPI data for %s \n " , hwif - > name ) ;
return ;
}
/* Setting up output buffer for _GTM */
output . length = ACPI_ALLOCATE_BUFFER ;
output . pointer = NULL ; /* ACPI-CA sets this; save/free it later */
/* _GTM has no input parameters */
status = acpi_evaluate_object ( hwif - > acpidata - > obj_handle , " _GTM " ,
NULL , & output ) ;
DEBPRINT ( " _GTM status: %d, outptr: 0x%p, outlen: 0x%llx \n " ,
status , output . pointer ,
( unsigned long long ) output . length ) ;
if ( ACPI_FAILURE ( status ) ) {
DEBPRINT ( " Run _GTM error: status = 0x%x \n " , status ) ;
return ;
}
if ( ! output . length | | ! output . pointer ) {
DEBPRINT ( " Run _GTM: length or ptr is NULL (0x%llx, 0x%p) \n " ,
( unsigned long long ) output . length ,
output . pointer ) ;
kfree ( output . pointer ) ;
return ;
}
out_obj = output . pointer ;
if ( out_obj - > type ! = ACPI_TYPE_BUFFER ) {
kfree ( output . pointer ) ;
DEBPRINT ( " Run _GTM: error: "
" expected object type of ACPI_TYPE_BUFFER, "
" got 0x%x \n " , out_obj - > type ) ;
return ;
}
if ( ! out_obj - > buffer . length | | ! out_obj - > buffer . pointer | |
out_obj - > buffer . length ! = sizeof ( struct GTM_buffer ) ) {
kfree ( output . pointer ) ;
printk ( KERN_ERR
2007-02-07 20:19:42 +03:00
" %s: unexpected _GTM length (0x%x)[should be 0x%zx] or "
" addr (0x%p) \n " ,
2008-04-27 00:25:20 +04:00
__func__ , out_obj - > buffer . length ,
2007-02-07 20:19:42 +03:00
sizeof ( struct GTM_buffer ) , out_obj - > buffer . pointer ) ;
2007-02-07 20:19:37 +03:00
return ;
}
memcpy ( & hwif - > acpidata - > gtm , out_obj - > buffer . pointer ,
sizeof ( struct GTM_buffer ) ) ;
DEBPRINT ( " _GTM info: ptr: 0x%p, len: 0x%x, exp.len: 0x%Zx \n " ,
out_obj - > buffer . pointer , out_obj - > buffer . length ,
sizeof ( struct GTM_buffer ) ) ;
DEBPRINT ( " _GTM fields: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x \n " ,
hwif - > acpidata - > gtm . PIO_speed0 ,
hwif - > acpidata - > gtm . DMA_speed0 ,
hwif - > acpidata - > gtm . PIO_speed1 ,
hwif - > acpidata - > gtm . DMA_speed1 ,
hwif - > acpidata - > gtm . GTM_flags ) ;
kfree ( output . pointer ) ;
}
/**
* ide_acpi_push_timing - set the channel ( controller ) timings
* @ hwif : target IDE interface ( channel )
*
* This function executes the _STM ACPI method for the target channel .
*
* _STM requires Identify Drive data , which has to passed as an argument .
2008-10-11 00:39:19 +04:00
* Unfortunately drive - > id is a mangled version which we can ' t readily
2007-02-07 20:19:37 +03:00
* use ; hence we ' ll get the information afresh .
*/
void ide_acpi_push_timing ( ide_hwif_t * hwif )
{
acpi_status status ;
struct acpi_object_list input ;
union acpi_object in_params [ 3 ] ;
struct ide_acpi_drive_link * master = & hwif - > acpidata - > master ;
struct ide_acpi_drive_link * slave = & hwif - > acpidata - > slave ;
if ( ide_noacpi )
return ;
DEBPRINT ( " ENTER: \n " ) ;
if ( ! hwif - > acpidata ) {
DEBPRINT ( " no ACPI data for %s \n " , hwif - > name ) ;
return ;
}
/* Give the GTM buffer + drive Identify data to the channel via the
* _STM method : */
/* setup input parameters buffer for _STM */
input . count = 3 ;
input . pointer = in_params ;
in_params [ 0 ] . type = ACPI_TYPE_BUFFER ;
in_params [ 0 ] . buffer . length = sizeof ( struct GTM_buffer ) ;
in_params [ 0 ] . buffer . pointer = ( u8 * ) & hwif - > acpidata - > gtm ;
in_params [ 1 ] . type = ACPI_TYPE_BUFFER ;
2008-12-29 22:27:29 +03:00
in_params [ 1 ] . buffer . length = ATA_ID_WORDS * 2 ;
2007-02-07 20:19:37 +03:00
in_params [ 1 ] . buffer . pointer = ( u8 * ) & master - > idbuff ;
in_params [ 2 ] . type = ACPI_TYPE_BUFFER ;
2008-12-29 22:27:29 +03:00
in_params [ 2 ] . buffer . length = ATA_ID_WORDS * 2 ;
2007-02-07 20:19:37 +03:00
in_params [ 2 ] . buffer . pointer = ( u8 * ) & slave - > idbuff ;
/* Output buffer: _STM has no output */
status = acpi_evaluate_object ( hwif - > acpidata - > obj_handle , " _STM " ,
& input , NULL ) ;
if ( ACPI_FAILURE ( status ) ) {
DEBPRINT ( " Run _STM error: status = 0x%x \n " , status ) ;
}
DEBPRINT ( " _STM status: %d \n " , status ) ;
}
2007-10-12 01:53:58 +04:00
/**
* ide_acpi_set_state - set the channel power state
* @ hwif : target IDE interface
* @ on : state , on / off
*
* This function executes the _PS0 / _PS3 ACPI method to set the power state .
* ACPI spec requires _PS0 when IDE power on and _PS3 when power off
*/
void ide_acpi_set_state ( ide_hwif_t * hwif , int on )
{
2009-01-06 19:20:56 +03:00
ide_drive_t * drive ;
int i ;
2007-10-12 01:53:58 +04:00
2008-01-11 01:03:42 +03:00
if ( ide_noacpi | | ide_noacpi_psx )
2007-10-12 01:53:58 +04:00
return ;
DEBPRINT ( " ENTER: \n " ) ;
if ( ! hwif - > acpidata ) {
DEBPRINT ( " no ACPI data for %s \n " , hwif - > name ) ;
return ;
}
/* channel first and then drives for power on and verse versa for power off */
if ( on )
acpi_bus_set_power ( hwif - > acpidata - > obj_handle , ACPI_STATE_D0 ) ;
2009-01-06 19:20:56 +03:00
ide_port_for_each_dev ( i , drive , hwif ) {
2007-10-12 01:53:58 +04:00
if ( ! drive - > acpidata - > obj_handle )
drive - > acpidata - > obj_handle = ide_acpi_drive_get_handle ( drive ) ;
2008-10-13 23:39:36 +04:00
if ( drive - > acpidata - > obj_handle & &
( drive - > dev_flags & IDE_DFLAG_PRESENT ) ) {
2007-10-12 01:53:58 +04:00
acpi_bus_set_power ( drive - > acpidata - > obj_handle ,
on ? ACPI_STATE_D0 : ACPI_STATE_D3 ) ;
}
}
if ( ! on )
acpi_bus_set_power ( hwif - > acpidata - > obj_handle , ACPI_STATE_D3 ) ;
}
2007-02-07 20:19:37 +03:00
/**
* ide_acpi_init - initialize the ACPI link for an IDE interface
* @ hwif : target IDE interface ( channel )
*
* The ACPI spec is not quite clear when the drive identify buffer
* should be obtained . Calling IDENTIFY DEVICE during shutdown
* is not the best of ideas as the drive might already being put to
* sleep . And obviously we can ' t call it during resume .
* So we get the information during startup ; but this means that
* any changes during run - time will be lost after resume .
*/
void ide_acpi_init ( ide_hwif_t * hwif )
{
2008-01-11 01:03:42 +03:00
ide_acpi_blacklist ( ) ;
2007-02-07 20:19:37 +03:00
hwif - > acpidata = kzalloc ( sizeof ( struct ide_acpi_hwif_link ) , GFP_KERNEL ) ;
if ( ! hwif - > acpidata )
return ;
hwif - > acpidata - > obj_handle = ide_acpi_hwif_get_handle ( hwif ) ;
if ( ! hwif - > acpidata - > obj_handle ) {
DEBPRINT ( " no ACPI object for %s found \n " , hwif - > name ) ;
kfree ( hwif - > acpidata ) ;
hwif - > acpidata = NULL ;
}
2008-02-02 21:56:43 +03:00
}
void ide_acpi_port_init_devices ( ide_hwif_t * hwif )
{
ide_drive_t * drive ;
int i , err ;
if ( hwif - > acpidata = = NULL )
return ;
2007-02-07 20:19:37 +03:00
/*
* The ACPI spec mandates that we send information
* for both drives , regardless whether they are connected
* or not .
*/
2009-01-06 19:20:56 +03:00
hwif - > devices [ 0 ] - > acpidata = & hwif - > acpidata - > master ;
hwif - > devices [ 1 ] - > acpidata = & hwif - > acpidata - > slave ;
2007-02-07 20:19:37 +03:00
/*
* Send IDENTIFY for each drive
*/
2009-01-06 19:20:56 +03:00
ide_port_for_each_dev ( i , drive , hwif ) {
2008-10-13 23:39:36 +04:00
if ( ( drive - > dev_flags & IDE_DFLAG_PRESENT ) = = 0 )
2008-02-02 21:56:43 +03:00
continue ;
2007-02-07 20:19:37 +03:00
2008-02-02 21:56:43 +03:00
err = taskfile_lib_get_identify ( drive , drive - > acpidata - > idbuff ) ;
if ( err )
2007-02-07 20:19:37 +03:00
DEBPRINT ( " identify device %s failed (%d) \n " ,
2008-02-02 21:56:43 +03:00
drive - > name , err ) ;
2007-02-07 20:19:37 +03:00
}
2008-04-27 17:38:30 +04:00
if ( ! ide_acpionboot ) {
2007-02-07 20:19:37 +03:00
DEBPRINT ( " ACPI methods disabled on boot \n " ) ;
return ;
}
2007-10-12 01:53:58 +04:00
/* ACPI _PS0 before _STM */
ide_acpi_set_state ( hwif , 1 ) ;
2007-02-07 20:19:37 +03:00
/*
* ACPI requires us to call _STM on startup
*/
ide_acpi_get_timing ( hwif ) ;
ide_acpi_push_timing ( hwif ) ;
2009-01-06 19:20:56 +03:00
ide_port_for_each_dev ( i , drive , hwif ) {
2008-10-13 23:39:36 +04:00
if ( drive - > dev_flags & IDE_DFLAG_PRESENT )
2007-02-07 20:19:37 +03:00
/* Execute ACPI startup code */
ide_acpi_exec_tfs ( drive ) ;
}
}