2019-05-29 07:18:02 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2014-09-03 15:13:30 +08:00
/*
* ACPI INT3403 thermal driver
* Copyright ( c ) 2013 , Intel Corporation .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/acpi.h>
# include <linux/thermal.h>
# include <linux/platform_device.h>
2015-01-16 15:59:05 -08:00
# include "int340x_thermal_zone.h"
2014-09-03 15:13:30 +08:00
# define INT3403_TYPE_SENSOR 0x03
# define INT3403_TYPE_CHARGER 0x0B
# define INT3403_TYPE_BATTERY 0x0C
# define INT3403_PERF_CHANGED_EVENT 0x80
2016-08-26 16:21:19 -07:00
# define INT3403_PERF_TRIP_POINT_CHANGED 0x81
2014-09-03 15:13:30 +08:00
# define INT3403_THERMAL_EVENT 0x90
2015-01-16 15:59:05 -08:00
/* Preserved structure for future expandbility */
2014-09-03 15:13:30 +08:00
struct int3403_sensor {
2015-01-16 15:59:05 -08:00
struct int34x_thermal_zone * int340x_zone ;
2014-09-03 15:13:30 +08:00
} ;
struct int3403_performance_state {
u64 performance ;
u64 power ;
u64 latency ;
u64 linear ;
u64 control ;
u64 raw_performace ;
char * raw_unit ;
int reserved ;
} ;
struct int3403_cdev {
struct thermal_cooling_device * cdev ;
unsigned long max_state ;
} ;
struct int3403_priv {
struct platform_device * pdev ;
struct acpi_device * adev ;
unsigned long long type ;
void * priv ;
} ;
static void int3403_notify ( acpi_handle handle ,
u32 event , void * data )
{
struct int3403_priv * priv = data ;
struct int3403_sensor * obj ;
if ( ! priv )
return ;
obj = priv - > priv ;
if ( priv - > type ! = INT3403_TYPE_SENSOR | | ! obj )
return ;
switch ( event ) {
case INT3403_PERF_CHANGED_EVENT :
break ;
case INT3403_THERMAL_EVENT :
2016-08-26 16:21:18 -07:00
int340x_thermal_zone_device_update ( obj - > int340x_zone ,
THERMAL_TRIP_VIOLATED ) ;
2014-09-03 15:13:30 +08:00
break ;
2016-08-26 16:21:19 -07:00
case INT3403_PERF_TRIP_POINT_CHANGED :
int340x_thermal_read_trips ( obj - > int340x_zone ) ;
int340x_thermal_zone_device_update ( obj - > int340x_zone ,
THERMAL_TRIP_CHANGED ) ;
break ;
2014-09-03 15:13:30 +08:00
default :
dev_err ( & priv - > pdev - > dev , " Unsupported event [0x%x] \n " , event ) ;
break ;
}
}
static int int3403_sensor_add ( struct int3403_priv * priv )
{
int result = 0 ;
struct int3403_sensor * obj ;
obj = devm_kzalloc ( & priv - > pdev - > dev , sizeof ( * obj ) , GFP_KERNEL ) ;
if ( ! obj )
return - ENOMEM ;
priv - > priv = obj ;
2015-01-16 15:59:05 -08:00
obj - > int340x_zone = int340x_thermal_zone_add ( priv - > adev , NULL ) ;
if ( IS_ERR ( obj - > int340x_zone ) )
return PTR_ERR ( obj - > int340x_zone ) ;
2014-09-03 15:13:30 +08:00
result = acpi_install_notify_handler ( priv - > adev - > handle ,
ACPI_DEVICE_NOTIFY , int3403_notify ,
( void * ) priv ) ;
if ( result )
goto err_free_obj ;
return 0 ;
err_free_obj :
2015-01-16 15:59:05 -08:00
int340x_thermal_zone_remove ( obj - > int340x_zone ) ;
2014-09-03 15:13:30 +08:00
return result ;
}
static int int3403_sensor_remove ( struct int3403_priv * priv )
{
struct int3403_sensor * obj = priv - > priv ;
2014-12-23 15:23:36 -08:00
acpi_remove_notify_handler ( priv - > adev - > handle ,
ACPI_DEVICE_NOTIFY , int3403_notify ) ;
2015-01-16 15:59:05 -08:00
int340x_thermal_zone_remove ( obj - > int340x_zone ) ;
2014-09-03 15:13:30 +08:00
return 0 ;
}
/* INT3403 Cooling devices */
static int int3403_get_max_state ( struct thermal_cooling_device * cdev ,
unsigned long * state )
{
struct int3403_priv * priv = cdev - > devdata ;
struct int3403_cdev * obj = priv - > priv ;
* state = obj - > max_state ;
return 0 ;
}
static int int3403_get_cur_state ( struct thermal_cooling_device * cdev ,
unsigned long * state )
{
struct int3403_priv * priv = cdev - > devdata ;
unsigned long long level ;
acpi_status status ;
status = acpi_evaluate_integer ( priv - > adev - > handle , " PPPC " , NULL , & level ) ;
if ( ACPI_SUCCESS ( status ) ) {
* state = level ;
return 0 ;
} else
return - EINVAL ;
}
static int
int3403_set_cur_state ( struct thermal_cooling_device * cdev , unsigned long state )
{
struct int3403_priv * priv = cdev - > devdata ;
acpi_status status ;
status = acpi_execute_simple_method ( priv - > adev - > handle , " SPPC " , state ) ;
if ( ACPI_SUCCESS ( status ) )
return 0 ;
else
return - EINVAL ;
}
static const struct thermal_cooling_device_ops int3403_cooling_ops = {
. get_max_state = int3403_get_max_state ,
. get_cur_state = int3403_get_cur_state ,
. set_cur_state = int3403_set_cur_state ,
} ;
static int int3403_cdev_add ( struct int3403_priv * priv )
{
int result = 0 ;
acpi_status status ;
struct int3403_cdev * obj ;
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * p ;
obj = devm_kzalloc ( & priv - > pdev - > dev , sizeof ( * obj ) , GFP_KERNEL ) ;
if ( ! obj )
return - ENOMEM ;
status = acpi_evaluate_object ( priv - > adev - > handle , " PPSS " , NULL , & buf ) ;
if ( ACPI_FAILURE ( status ) )
return - ENODEV ;
p = buf . pointer ;
if ( ! p | | ( p - > type ! = ACPI_TYPE_PACKAGE ) ) {
printk ( KERN_WARNING " Invalid PPSS data \n " ) ;
2014-12-23 15:23:34 -08:00
kfree ( buf . pointer ) ;
2014-09-03 15:13:30 +08:00
return - EFAULT ;
}
2018-04-22 19:56:17 +02:00
priv - > priv = obj ;
2014-09-03 15:13:30 +08:00
obj - > max_state = p - > package . count - 1 ;
obj - > cdev =
thermal_cooling_device_register ( acpi_device_bid ( priv - > adev ) ,
priv , & int3403_cooling_ops ) ;
if ( IS_ERR ( obj - > cdev ) )
result = PTR_ERR ( obj - > cdev ) ;
2014-12-23 15:23:34 -08:00
kfree ( buf . pointer ) ;
2014-09-03 15:13:30 +08:00
/* TODO: add ACPI notification support */
return result ;
}
static int int3403_cdev_remove ( struct int3403_priv * priv )
{
struct int3403_cdev * obj = priv - > priv ;
thermal_cooling_device_unregister ( obj - > cdev ) ;
return 0 ;
}
static int int3403_add ( struct platform_device * pdev )
{
struct int3403_priv * priv ;
int result = 0 ;
2019-04-21 16:45:11 +08:00
unsigned long long tmp ;
2014-09-03 15:13:30 +08:00
acpi_status status ;
priv = devm_kzalloc ( & pdev - > dev , sizeof ( struct int3403_priv ) ,
GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > pdev = pdev ;
priv - > adev = ACPI_COMPANION ( & ( pdev - > dev ) ) ;
if ( ! priv - > adev ) {
result = - EINVAL ;
goto err ;
}
2017-06-06 16:00:41 -07:00
2019-04-21 16:45:11 +08:00
status = acpi_evaluate_integer ( priv - > adev - > handle , " _TMP " ,
NULL , & tmp ) ;
if ( ACPI_FAILURE ( status ) ) {
status = acpi_evaluate_integer ( priv - > adev - > handle , " PTYP " ,
NULL , & priv - > type ) ;
2017-06-06 16:00:41 -07:00
if ( ACPI_FAILURE ( status ) ) {
result = - EINVAL ;
goto err ;
}
2019-04-21 16:45:11 +08:00
} else {
priv - > type = INT3403_TYPE_SENSOR ;
2014-09-03 15:13:30 +08:00
}
platform_set_drvdata ( pdev , priv ) ;
switch ( priv - > type ) {
case INT3403_TYPE_SENSOR :
result = int3403_sensor_add ( priv ) ;
break ;
case INT3403_TYPE_CHARGER :
case INT3403_TYPE_BATTERY :
result = int3403_cdev_add ( priv ) ;
break ;
default :
result = - EINVAL ;
}
if ( result )
goto err ;
return result ;
err :
return result ;
}
static int int3403_remove ( struct platform_device * pdev )
{
struct int3403_priv * priv = platform_get_drvdata ( pdev ) ;
switch ( priv - > type ) {
case INT3403_TYPE_SENSOR :
int3403_sensor_remove ( priv ) ;
break ;
case INT3403_TYPE_CHARGER :
case INT3403_TYPE_BATTERY :
int3403_cdev_remove ( priv ) ;
break ;
default :
break ;
}
return 0 ;
}
static const struct acpi_device_id int3403_device_ids [ ] = {
{ " INT3403 " , 0 } ,
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , int3403_device_ids ) ;
static struct platform_driver int3403_driver = {
. probe = int3403_add ,
. remove = int3403_remove ,
. driver = {
. name = " int3403 thermal " ,
. acpi_match_table = int3403_device_ids ,
} ,
} ;
module_platform_driver ( int3403_driver ) ;
MODULE_AUTHOR ( " Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " ACPI INT3403 thermal driver " ) ;