2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
* acpi_fan . c - ACPI Fan Driver ( $ Revision : 29 $ )
*
* Copyright ( C ) 2001 , 2002 Andy Grover < andrew . grover @ intel . com >
* Copyright ( C ) 2001 , 2002 Paul Diefenbaugh < paul . s . diefenbaugh @ intel . com >
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
2014-08-28 17:47:19 +04:00
# include <linux/uaccess.h>
2008-01-17 10:51:24 +03:00
# include <linux/thermal.h>
2013-12-03 04:49:16 +04:00
# include <linux/acpi.h>
2013-11-19 12:59:20 +04:00
# include <linux/platform_device.h>
2014-04-01 11:50:27 +04:00
# include <linux/sort.h>
2005-04-17 02:20:36 +04:00
2007-02-13 06:42:12 +03:00
MODULE_AUTHOR ( " Paul Diefenbaugh " ) ;
2007-02-13 07:50:02 +03:00
MODULE_DESCRIPTION ( " ACPI Fan Driver " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
2013-11-19 12:59:20 +04:00
static int acpi_fan_probe ( struct platform_device * pdev ) ;
static int acpi_fan_remove ( struct platform_device * pdev ) ;
2005-04-17 02:20:36 +04:00
2007-07-23 16:44:41 +04:00
static const struct acpi_device_id fan_device_ids [ ] = {
{ " PNP0C0B " , 0 } ,
2014-04-01 11:54:05 +04:00
{ " INT3404 " , 0 } ,
2007-07-23 16:44:41 +04:00
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , fan_device_ids ) ;
2012-08-10 01:00:02 +04:00
# ifdef CONFIG_PM_SLEEP
2012-06-28 01:26:07 +04:00
static int acpi_fan_suspend ( struct device * dev ) ;
static int acpi_fan_resume ( struct device * dev ) ;
2016-02-11 02:23:27 +03:00
static const struct dev_pm_ops acpi_fan_pm = {
2014-02-19 08:16:48 +04:00
. resume = acpi_fan_resume ,
. freeze = acpi_fan_suspend ,
. thaw = acpi_fan_resume ,
. restore = acpi_fan_resume ,
} ;
# define FAN_PM_OPS_PTR (&acpi_fan_pm)
2014-02-13 07:19:08 +04:00
# else
2014-02-19 08:16:48 +04:00
# define FAN_PM_OPS_PTR NULL
2012-08-10 01:00:02 +04:00
# endif
2012-06-28 01:26:07 +04:00
2014-04-01 11:50:27 +04:00
struct acpi_fan_fps {
u64 control ;
u64 trip_point ;
u64 speed ;
u64 noise_level ;
u64 power ;
} ;
struct acpi_fan_fif {
u64 revision ;
u64 fine_grain_ctrl ;
u64 step_size ;
u64 low_speed_notification ;
} ;
struct acpi_fan {
bool acpi4 ;
struct acpi_fan_fif fif ;
struct acpi_fan_fps * fps ;
int fps_count ;
struct thermal_cooling_device * cdev ;
} ;
2013-11-19 12:59:20 +04:00
static struct platform_driver acpi_fan_driver = {
. probe = acpi_fan_probe ,
. remove = acpi_fan_remove ,
. driver = {
. name = " acpi-fan " ,
. acpi_match_table = fan_device_ids ,
. pm = FAN_PM_OPS_PTR ,
} ,
2005-04-17 02:20:36 +04:00
} ;
2008-01-17 10:51:24 +03:00
/* thermal cooling device callbacks */
2008-11-27 20:48:13 +03:00
static int fan_get_max_state ( struct thermal_cooling_device * cdev , unsigned long
* state )
2008-01-17 10:51:24 +03:00
{
2014-04-01 11:50:27 +04:00
struct acpi_device * device = cdev - > devdata ;
struct acpi_fan * fan = acpi_driver_data ( device ) ;
if ( fan - > acpi4 )
* state = fan - > fps_count - 1 ;
else
* state = 1 ;
2008-11-27 20:48:13 +03:00
return 0 ;
2008-01-17 10:51:24 +03:00
}
2014-04-01 11:50:27 +04:00
static int fan_get_state_acpi4 ( struct acpi_device * device , unsigned long * state )
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
struct acpi_fan * fan = acpi_driver_data ( device ) ;
union acpi_object * obj ;
acpi_status status ;
int control , i ;
status = acpi_evaluate_object ( device - > handle , " _FST " , NULL , & buffer ) ;
if ( ACPI_FAILURE ( status ) ) {
dev_err ( & device - > dev , " Get fan state failed \n " ) ;
return status ;
}
obj = buffer . pointer ;
if ( ! obj | | obj - > type ! = ACPI_TYPE_PACKAGE | |
obj - > package . count ! = 3 | |
obj - > package . elements [ 1 ] . type ! = ACPI_TYPE_INTEGER ) {
dev_err ( & device - > dev , " Invalid _FST data \n " ) ;
status = - EINVAL ;
goto err ;
}
control = obj - > package . elements [ 1 ] . integer . value ;
for ( i = 0 ; i < fan - > fps_count ; i + + ) {
2016-10-04 23:51:40 +03:00
/*
* When Fine Grain Control is set , return the state
* corresponding to maximum fan - > fps [ i ] . control
* value compared to the current speed . Here the
* fan - > fps [ ] is sorted array with increasing speed .
*/
if ( fan - > fif . fine_grain_ctrl & & control < fan - > fps [ i ] . control ) {
i = ( i > 0 ) ? i - 1 : 0 ;
2014-04-01 11:50:27 +04:00
break ;
2016-10-04 23:51:40 +03:00
} else if ( control = = fan - > fps [ i ] . control ) {
break ;
}
2014-04-01 11:50:27 +04:00
}
if ( i = = fan - > fps_count ) {
dev_dbg ( & device - > dev , " Invalid control value returned \n " ) ;
status = - EINVAL ;
goto err ;
}
* state = i ;
err :
kfree ( obj ) ;
return status ;
}
static int fan_get_state ( struct acpi_device * device , unsigned long * state )
2008-01-17 10:51:24 +03:00
{
int result ;
2013-07-01 18:21:00 +04:00
int acpi_state = ACPI_STATE_D0 ;
2008-01-17 10:51:24 +03:00
2013-11-19 11:43:52 +04:00
result = acpi_device_update_power ( device , & acpi_state ) ;
2008-01-17 10:51:24 +03:00
if ( result )
return result ;
2015-05-16 02:55:35 +03:00
* state = acpi_state = = ACPI_STATE_D3_COLD
| | acpi_state = = ACPI_STATE_D3_HOT ?
0 : ( acpi_state = = ACPI_STATE_D0 ? 1 : - 1 ) ;
2008-11-27 20:48:13 +03:00
return 0 ;
2008-01-17 10:51:24 +03:00
}
2014-04-01 11:50:27 +04:00
static int fan_get_cur_state ( struct thermal_cooling_device * cdev , unsigned long
* state )
2008-01-17 10:51:24 +03:00
{
struct acpi_device * device = cdev - > devdata ;
2014-04-01 11:50:27 +04:00
struct acpi_fan * fan = acpi_driver_data ( device ) ;
2008-01-17 10:51:24 +03:00
2014-04-01 11:50:27 +04:00
if ( fan - > acpi4 )
return fan_get_state_acpi4 ( device , state ) ;
else
return fan_get_state ( device , state ) ;
}
2008-01-17 10:51:24 +03:00
2014-04-01 11:50:27 +04:00
static int fan_set_state ( struct acpi_device * device , unsigned long state )
{
if ( state ! = 0 & & state ! = 1 )
2008-01-17 10:51:24 +03:00
return - EINVAL ;
2014-04-01 11:50:27 +04:00
return acpi_device_set_power ( device ,
state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD ) ;
}
2008-01-17 10:51:24 +03:00
2014-04-01 11:50:27 +04:00
static int fan_set_state_acpi4 ( struct acpi_device * device , unsigned long state )
{
struct acpi_fan * fan = acpi_driver_data ( device ) ;
acpi_status status ;
if ( state > = fan - > fps_count )
return - EINVAL ;
status = acpi_execute_simple_method ( device - > handle , " _FSL " ,
fan - > fps [ state ] . control ) ;
if ( ACPI_FAILURE ( status ) ) {
dev_dbg ( & device - > dev , " Failed to set state by _FSL \n " ) ;
return status ;
}
return 0 ;
2008-01-17 10:51:24 +03:00
}
2014-04-01 11:50:27 +04:00
static int
fan_set_cur_state ( struct thermal_cooling_device * cdev , unsigned long state )
{
struct acpi_device * device = cdev - > devdata ;
struct acpi_fan * fan = acpi_driver_data ( device ) ;
if ( fan - > acpi4 )
return fan_set_state_acpi4 ( device , state ) ;
else
return fan_set_state ( device , state ) ;
2018-03-22 01:09:32 +03:00
}
2014-04-01 11:50:27 +04:00
2011-06-25 21:07:52 +04:00
static const struct thermal_cooling_device_ops fan_cooling_ops = {
2008-01-17 10:51:24 +03:00
. get_max_state = fan_get_max_state ,
. get_cur_state = fan_get_cur_state ,
. set_cur_state = fan_set_cur_state ,
} ;
2005-04-17 02:20:36 +04:00
/* --------------------------------------------------------------------------
2014-08-28 17:47:19 +04:00
* Driver Interface
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
2005-04-17 02:20:36 +04:00
2014-04-01 11:50:27 +04:00
static bool acpi_fan_is_acpi4 ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2014-04-01 11:50:27 +04:00
return acpi_has_method ( device - > handle , " _FIF " ) & &
acpi_has_method ( device - > handle , " _FPS " ) & &
acpi_has_method ( device - > handle , " _FSL " ) & &
acpi_has_method ( device - > handle , " _FST " ) ;
}
2005-04-17 02:20:36 +04:00
2014-04-01 11:50:27 +04:00
static int acpi_fan_get_fif ( struct acpi_device * device )
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
struct acpi_fan * fan = acpi_driver_data ( device ) ;
struct acpi_buffer format = { sizeof ( " NNNN " ) , " NNNN " } ;
struct acpi_buffer fif = { sizeof ( fan - > fif ) , & fan - > fif } ;
union acpi_object * obj ;
acpi_status status ;
status = acpi_evaluate_object ( device - > handle , " _FIF " , NULL , & buffer ) ;
if ( ACPI_FAILURE ( status ) )
return status ;
obj = buffer . pointer ;
if ( ! obj | | obj - > type ! = ACPI_TYPE_PACKAGE ) {
dev_err ( & device - > dev , " Invalid _FIF data \n " ) ;
status = - EINVAL ;
goto err ;
}
2005-04-17 02:20:36 +04:00
2014-04-01 11:50:27 +04:00
status = acpi_extract_package ( obj , & format , & fif ) ;
if ( ACPI_FAILURE ( status ) ) {
dev_err ( & device - > dev , " Invalid _FIF element \n " ) ;
status = - EINVAL ;
}
2005-04-17 02:20:36 +04:00
2014-04-01 11:50:27 +04:00
err :
kfree ( obj ) ;
return status ;
}
static int acpi_fan_speed_cmp ( const void * a , const void * b )
{
const struct acpi_fan_fps * fps1 = a ;
const struct acpi_fan_fps * fps2 = b ;
return fps1 - > speed - fps2 - > speed ;
}
static int acpi_fan_get_fps ( struct acpi_device * device )
{
struct acpi_fan * fan = acpi_driver_data ( device ) ;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * obj ;
acpi_status status ;
int i ;
status = acpi_evaluate_object ( device - > handle , " _FPS " , NULL , & buffer ) ;
if ( ACPI_FAILURE ( status ) )
return status ;
obj = buffer . pointer ;
if ( ! obj | | obj - > type ! = ACPI_TYPE_PACKAGE | | obj - > package . count < 2 ) {
dev_err ( & device - > dev , " Invalid _FPS data \n " ) ;
status = - EINVAL ;
goto err ;
}
fan - > fps_count = obj - > package . count - 1 ; /* minus revision field */
treewide: devm_kzalloc() -> devm_kcalloc()
The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc().
This patch replaces cases of:
devm_kzalloc(handle, a * b, gfp)
with:
devm_kcalloc(handle, a * b, gfp)
as well as handling cases of:
devm_kzalloc(handle, a * b * c, gfp)
with:
devm_kzalloc(handle, array3_size(a, b, c), gfp)
as it's slightly less ugly than:
devm_kcalloc(handle, array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
devm_kzalloc(handle, 4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
Some manual whitespace fixes were needed in this patch, as Coccinelle
really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...".
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
expression HANDLE;
type TYPE;
expression THING, E;
@@
(
devm_kzalloc(HANDLE,
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
devm_kzalloc(HANDLE,
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression HANDLE;
expression COUNT;
typedef u8;
typedef __u8;
@@
(
devm_kzalloc(HANDLE,
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
expression HANDLE;
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
expression HANDLE;
identifier SIZE, COUNT;
@@
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression HANDLE;
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression HANDLE;
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
expression HANDLE;
identifier STRIDE, SIZE, COUNT;
@@
(
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression HANDLE;
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE,
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression HANDLE;
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, sizeof(THING) * C2, ...)
|
devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...)
|
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE, C1 * C2, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * E2
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * (E2)
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 00:07:58 +03:00
fan - > fps = devm_kcalloc ( & device - > dev ,
fan - > fps_count , sizeof ( struct acpi_fan_fps ) ,
2014-04-01 11:50:27 +04:00
GFP_KERNEL ) ;
if ( ! fan - > fps ) {
dev_err ( & device - > dev , " Not enough memory \n " ) ;
status = - ENOMEM ;
goto err ;
}
for ( i = 0 ; i < fan - > fps_count ; i + + ) {
struct acpi_buffer format = { sizeof ( " NNNNN " ) , " NNNNN " } ;
struct acpi_buffer fps = { sizeof ( fan - > fps [ i ] ) , & fan - > fps [ i ] } ;
status = acpi_extract_package ( & obj - > package . elements [ i + 1 ] ,
& format , & fps ) ;
if ( ACPI_FAILURE ( status ) ) {
dev_err ( & device - > dev , " Invalid _FPS element \n " ) ;
break ;
}
}
/* sort the state array according to fan speed in increase order */
sort ( fan - > fps , fan - > fps_count , sizeof ( * fan - > fps ) ,
acpi_fan_speed_cmp , NULL ) ;
err :
kfree ( obj ) ;
return status ;
}
2013-11-19 12:59:20 +04:00
static int acpi_fan_probe ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
2008-01-17 10:51:24 +03:00
struct thermal_cooling_device * cdev ;
2014-04-01 11:50:27 +04:00
struct acpi_fan * fan ;
2013-11-19 12:59:20 +04:00
struct acpi_device * device = ACPI_COMPANION ( & pdev - > dev ) ;
2014-12-10 03:15:40 +03:00
char * name ;
2005-04-17 02:20:36 +04:00
2014-04-01 11:50:27 +04:00
fan = devm_kzalloc ( & pdev - > dev , sizeof ( * fan ) , GFP_KERNEL ) ;
if ( ! fan ) {
dev_err ( & device - > dev , " No memory for fan \n " ) ;
return - ENOMEM ;
}
device - > driver_data = fan ;
platform_set_drvdata ( pdev , fan ) ;
if ( acpi_fan_is_acpi4 ( device ) ) {
if ( acpi_fan_get_fif ( device ) | | acpi_fan_get_fps ( device ) )
goto end ;
fan - > acpi4 = true ;
} else {
result = acpi_device_update_power ( device , NULL ) ;
if ( result ) {
2016-01-14 01:25:19 +03:00
dev_err ( & device - > dev , " Failed to set initial power state \n " ) ;
2014-04-01 11:50:27 +04:00
goto end ;
}
2005-04-17 02:20:36 +04:00
}
2014-12-10 03:15:40 +03:00
if ( ! strncmp ( pdev - > name , " PNP0C0B " , strlen ( " PNP0C0B " ) ) )
name = " Fan " ;
else
name = acpi_device_bid ( device ) ;
cdev = thermal_cooling_device_register ( name , device ,
2008-01-17 10:51:24 +03:00
& fan_cooling_ops ) ;
2008-02-15 09:01:52 +03:00
if ( IS_ERR ( cdev ) ) {
result = PTR_ERR ( cdev ) ;
goto end ;
}
2008-04-11 06:09:24 +04:00
2013-11-19 12:59:20 +04:00
dev_dbg ( & pdev - > dev , " registered as cooling_device%d \n " , cdev - > id ) ;
2008-04-11 06:09:24 +04:00
2014-04-01 11:50:27 +04:00
fan - > cdev = cdev ;
2013-11-19 12:59:20 +04:00
result = sysfs_create_link ( & pdev - > dev . kobj ,
2008-04-11 06:09:24 +04:00
& cdev - > device . kobj ,
" thermal_cooling " ) ;
if ( result )
2014-10-24 22:21:43 +04:00
dev_err ( & pdev - > dev , " Failed to create sysfs link 'thermal_cooling' \n " ) ;
2008-04-11 06:09:24 +04:00
result = sysfs_create_link ( & cdev - > device . kobj ,
2013-11-19 12:59:20 +04:00
& pdev - > dev . kobj ,
2008-04-11 06:09:24 +04:00
" device " ) ;
if ( result )
2014-10-24 22:21:43 +04:00
dev_err ( & pdev - > dev , " Failed to create sysfs link 'device' \n " ) ;
2005-04-17 02:20:36 +04:00
2013-08-31 01:16:05 +04:00
end :
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
}
2013-11-19 12:59:20 +04:00
static int acpi_fan_remove ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
2014-04-01 11:50:27 +04:00
struct acpi_fan * fan = platform_get_drvdata ( pdev ) ;
2005-04-17 02:20:36 +04:00
2013-11-19 12:59:20 +04:00
sysfs_remove_link ( & pdev - > dev . kobj , " thermal_cooling " ) ;
2014-04-01 11:50:27 +04:00
sysfs_remove_link ( & fan - > cdev - > device . kobj , " device " ) ;
thermal_cooling_device_unregister ( fan - > cdev ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2012-08-10 01:00:02 +04:00
# ifdef CONFIG_PM_SLEEP
2012-06-28 01:26:07 +04:00
static int acpi_fan_suspend ( struct device * dev )
2008-01-24 06:41:20 +03:00
{
2014-04-01 11:50:27 +04:00
struct acpi_fan * fan = dev_get_drvdata ( dev ) ;
if ( fan - > acpi4 )
return 0 ;
2008-01-24 06:41:20 +03:00
2013-11-19 12:59:20 +04:00
acpi_device_set_power ( ACPI_COMPANION ( dev ) , ACPI_STATE_D0 ) ;
2008-01-24 06:41:20 +03:00
return AE_OK ;
}
2012-06-28 01:26:07 +04:00
static int acpi_fan_resume ( struct device * dev )
2008-01-24 06:41:20 +03:00
{
2010-11-25 02:11:24 +03:00
int result ;
2014-04-01 11:50:27 +04:00
struct acpi_fan * fan = dev_get_drvdata ( dev ) ;
2008-01-24 06:41:20 +03:00
2014-04-01 11:50:27 +04:00
if ( fan - > acpi4 )
return 0 ;
2008-01-24 06:41:20 +03:00
2013-11-19 12:59:20 +04:00
result = acpi_device_update_power ( ACPI_COMPANION ( dev ) , NULL ) ;
2010-11-25 02:11:24 +03:00
if ( result )
2014-08-28 17:47:19 +04:00
dev_err ( dev , " Error updating fan power state \n " ) ;
2008-01-24 06:41:20 +03:00
return result ;
}
2012-08-10 01:00:02 +04:00
# endif
2008-01-24 06:41:20 +03:00
2013-11-19 12:59:20 +04:00
module_platform_driver ( acpi_fan_driver ) ;