2015-03-03 00:12:58 +03:00
/*
* intel_soc_dts_iosf . c
* Copyright ( c ) 2015 , Intel Corporation .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope 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 .
*
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <asm/iosf_mbi.h>
# include "intel_soc_dts_iosf.h"
# define SOC_DTS_OFFSET_ENABLE 0xB0
# define SOC_DTS_OFFSET_TEMP 0xB1
# define SOC_DTS_OFFSET_PTPS 0xB2
# define SOC_DTS_OFFSET_PTTS 0xB3
# define SOC_DTS_OFFSET_PTTSS 0xB4
# define SOC_DTS_OFFSET_PTMC 0x80
# define SOC_DTS_TE_AUX0 0xB5
# define SOC_DTS_TE_AUX1 0xB6
# define SOC_DTS_AUX0_ENABLE_BIT BIT(0)
# define SOC_DTS_AUX1_ENABLE_BIT BIT(1)
# define SOC_DTS_CPU_MODULE0_ENABLE_BIT BIT(16)
# define SOC_DTS_CPU_MODULE1_ENABLE_BIT BIT(17)
# define SOC_DTS_TE_SCI_ENABLE BIT(9)
# define SOC_DTS_TE_SMI_ENABLE BIT(10)
# define SOC_DTS_TE_MSI_ENABLE BIT(11)
# define SOC_DTS_TE_APICA_ENABLE BIT(14)
# define SOC_DTS_PTMC_APIC_DEASSERT_BIT BIT(4)
/* DTS encoding for TJ MAX temperature */
# define SOC_DTS_TJMAX_ENCODING 0x7F
/* Only 2 out of 4 is allowed for OSPM */
# define SOC_MAX_DTS_TRIPS 2
/* Mask for two trips in status bits */
# define SOC_DTS_TRIP_MASK 0x03
/* DTS0 and DTS 1 */
# define SOC_MAX_DTS_SENSORS 2
static int get_tj_max ( u32 * tj_max )
{
u32 eax , edx ;
u32 val ;
int err ;
err = rdmsr_safe ( MSR_IA32_TEMPERATURE_TARGET , & eax , & edx ) ;
if ( err )
goto err_ret ;
else {
val = ( eax > > 16 ) & 0xff ;
if ( val )
* tj_max = val * 1000 ;
else {
err = - EINVAL ;
goto err_ret ;
}
}
return 0 ;
err_ret :
* tj_max = 0 ;
return err ;
}
static int sys_get_trip_temp ( struct thermal_zone_device * tzd , int trip ,
2015-07-24 09:12:54 +03:00
int * temp )
2015-03-03 00:12:58 +03:00
{
int status ;
u32 out ;
struct intel_soc_dts_sensor_entry * dts ;
struct intel_soc_dts_sensors * sensors ;
dts = tzd - > devdata ;
sensors = dts - > sensors ;
mutex_lock ( & sensors - > dts_update_lock ) ;
2015-11-11 20:59:29 +03:00
status = iosf_mbi_read ( BT_MBI_UNIT_PMC , MBI_REG_READ ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_PTPS , & out ) ;
mutex_unlock ( & sensors - > dts_update_lock ) ;
if ( status )
return status ;
out = ( out > > ( trip * 8 ) ) & SOC_DTS_TJMAX_ENCODING ;
if ( ! out )
* temp = 0 ;
else
* temp = sensors - > tj_max - out * 1000 ;
return 0 ;
}
static int update_trip_temp ( struct intel_soc_dts_sensor_entry * dts ,
2015-07-24 09:12:54 +03:00
int thres_index , int temp ,
2015-03-03 00:12:58 +03:00
enum thermal_trip_type trip_type )
{
int status ;
u32 temp_out ;
u32 out ;
u32 store_ptps ;
u32 store_ptmc ;
u32 store_te_out ;
u32 te_out ;
u32 int_enable_bit = SOC_DTS_TE_APICA_ENABLE ;
struct intel_soc_dts_sensors * sensors = dts - > sensors ;
if ( sensors - > intr_type = = INTEL_SOC_DTS_INTERRUPT_MSI )
int_enable_bit | = SOC_DTS_TE_MSI_ENABLE ;
temp_out = ( sensors - > tj_max - temp ) / 1000 ;
2015-11-11 20:59:29 +03:00
status = iosf_mbi_read ( BT_MBI_UNIT_PMC , MBI_REG_READ ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_PTPS , & store_ptps ) ;
if ( status )
return status ;
out = ( store_ptps & ~ ( 0xFF < < ( thres_index * 8 ) ) ) ;
out | = ( temp_out & 0xFF ) < < ( thres_index * 8 ) ;
2015-11-11 20:59:29 +03:00
status = iosf_mbi_write ( BT_MBI_UNIT_PMC , MBI_REG_WRITE ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_PTPS , out ) ;
if ( status )
return status ;
pr_debug ( " update_trip_temp PTPS = %x \n " , out ) ;
2015-11-11 20:59:29 +03:00
status = iosf_mbi_read ( BT_MBI_UNIT_PMC , MBI_REG_READ ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_PTMC , & out ) ;
if ( status )
goto err_restore_ptps ;
store_ptmc = out ;
2015-11-11 20:59:29 +03:00
status = iosf_mbi_read ( BT_MBI_UNIT_PMC , MBI_REG_READ ,
2015-03-03 00:12:58 +03:00
SOC_DTS_TE_AUX0 + thres_index ,
& te_out ) ;
if ( status )
goto err_restore_ptmc ;
store_te_out = te_out ;
/* Enable for CPU module 0 and module 1 */
out | = ( SOC_DTS_CPU_MODULE0_ENABLE_BIT |
SOC_DTS_CPU_MODULE1_ENABLE_BIT ) ;
if ( temp ) {
if ( thres_index )
out | = SOC_DTS_AUX1_ENABLE_BIT ;
else
out | = SOC_DTS_AUX0_ENABLE_BIT ;
te_out | = int_enable_bit ;
} else {
if ( thres_index )
out & = ~ SOC_DTS_AUX1_ENABLE_BIT ;
else
out & = ~ SOC_DTS_AUX0_ENABLE_BIT ;
te_out & = ~ int_enable_bit ;
}
2015-11-11 20:59:29 +03:00
status = iosf_mbi_write ( BT_MBI_UNIT_PMC , MBI_REG_WRITE ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_PTMC , out ) ;
if ( status )
goto err_restore_te_out ;
2015-11-11 20:59:29 +03:00
status = iosf_mbi_write ( BT_MBI_UNIT_PMC , MBI_REG_WRITE ,
2015-03-03 00:12:58 +03:00
SOC_DTS_TE_AUX0 + thres_index ,
te_out ) ;
if ( status )
goto err_restore_te_out ;
dts - > trip_types [ thres_index ] = trip_type ;
return 0 ;
err_restore_te_out :
2015-11-11 20:59:29 +03:00
iosf_mbi_write ( BT_MBI_UNIT_PMC , MBI_REG_WRITE ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_PTMC , store_te_out ) ;
err_restore_ptmc :
2015-11-11 20:59:29 +03:00
iosf_mbi_write ( BT_MBI_UNIT_PMC , MBI_REG_WRITE ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_PTMC , store_ptmc ) ;
err_restore_ptps :
2015-11-11 20:59:29 +03:00
iosf_mbi_write ( BT_MBI_UNIT_PMC , MBI_REG_WRITE ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_PTPS , store_ptps ) ;
/* Nothing we can do if restore fails */
return status ;
}
static int sys_set_trip_temp ( struct thermal_zone_device * tzd , int trip ,
2015-07-24 09:12:54 +03:00
int temp )
2015-03-03 00:12:58 +03:00
{
struct intel_soc_dts_sensor_entry * dts = tzd - > devdata ;
struct intel_soc_dts_sensors * sensors = dts - > sensors ;
int status ;
if ( temp > sensors - > tj_max )
return - EINVAL ;
mutex_lock ( & sensors - > dts_update_lock ) ;
status = update_trip_temp ( tzd - > devdata , trip , temp ,
dts - > trip_types [ trip ] ) ;
mutex_unlock ( & sensors - > dts_update_lock ) ;
return status ;
}
static int sys_get_trip_type ( struct thermal_zone_device * tzd ,
int trip , enum thermal_trip_type * type )
{
struct intel_soc_dts_sensor_entry * dts ;
dts = tzd - > devdata ;
* type = dts - > trip_types [ trip ] ;
return 0 ;
}
static int sys_get_curr_temp ( struct thermal_zone_device * tzd ,
2015-07-24 09:12:54 +03:00
int * temp )
2015-03-03 00:12:58 +03:00
{
int status ;
u32 out ;
struct intel_soc_dts_sensor_entry * dts ;
struct intel_soc_dts_sensors * sensors ;
dts = tzd - > devdata ;
sensors = dts - > sensors ;
2015-11-11 20:59:29 +03:00
status = iosf_mbi_read ( BT_MBI_UNIT_PMC , MBI_REG_READ ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_TEMP , & out ) ;
if ( status )
return status ;
out = ( out & dts - > temp_mask ) > > dts - > temp_shift ;
out - = SOC_DTS_TJMAX_ENCODING ;
* temp = sensors - > tj_max - out * 1000 ;
return 0 ;
}
static struct thermal_zone_device_ops tzone_ops = {
. get_temp = sys_get_curr_temp ,
. get_trip_temp = sys_get_trip_temp ,
. get_trip_type = sys_get_trip_type ,
. set_trip_temp = sys_set_trip_temp ,
} ;
static int soc_dts_enable ( int id )
{
u32 out ;
int ret ;
2015-11-11 20:59:29 +03:00
ret = iosf_mbi_read ( BT_MBI_UNIT_PMC , MBI_REG_READ ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_ENABLE , & out ) ;
if ( ret )
return ret ;
if ( ! ( out & BIT ( id ) ) ) {
out | = BIT ( id ) ;
2015-11-11 20:59:29 +03:00
ret = iosf_mbi_write ( BT_MBI_UNIT_PMC , MBI_REG_WRITE ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_ENABLE , out ) ;
if ( ret )
return ret ;
}
return ret ;
}
static void remove_dts_thermal_zone ( struct intel_soc_dts_sensor_entry * dts )
{
if ( dts ) {
2015-11-11 20:59:29 +03:00
iosf_mbi_write ( BT_MBI_UNIT_PMC , MBI_REG_WRITE ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_ENABLE , dts - > store_status ) ;
thermal_zone_device_unregister ( dts - > tzone ) ;
}
}
static int add_dts_thermal_zone ( int id , struct intel_soc_dts_sensor_entry * dts ,
bool notification_support , int trip_cnt ,
int read_only_trip_cnt )
{
char name [ 10 ] ;
int trip_count = 0 ;
int trip_mask = 0 ;
u32 store_ptps ;
int ret ;
int i ;
/* Store status to restor on exit */
2015-11-11 20:59:29 +03:00
ret = iosf_mbi_read ( BT_MBI_UNIT_PMC , MBI_REG_READ ,
SOC_DTS_OFFSET_ENABLE , & dts - > store_status ) ;
2015-03-03 00:12:58 +03:00
if ( ret )
goto err_ret ;
dts - > id = id ;
dts - > temp_mask = 0x00FF < < ( id * 8 ) ;
dts - > temp_shift = id * 8 ;
if ( notification_support ) {
trip_count = min ( SOC_MAX_DTS_TRIPS , trip_cnt ) ;
trip_mask = BIT ( trip_count - read_only_trip_cnt ) - 1 ;
}
/* Check if the writable trip we provide is not used by BIOS */
2015-11-11 20:59:29 +03:00
ret = iosf_mbi_read ( BT_MBI_UNIT_PMC , MBI_REG_READ ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_PTPS , & store_ptps ) ;
if ( ret )
trip_mask = 0 ;
else {
for ( i = 0 ; i < trip_count ; + + i ) {
if ( trip_mask & BIT ( i ) )
if ( store_ptps & ( 0xff < < ( i * 8 ) ) )
trip_mask & = ~ BIT ( i ) ;
}
}
dts - > trip_mask = trip_mask ;
dts - > trip_count = trip_count ;
snprintf ( name , sizeof ( name ) , " soc_dts%d " , id ) ;
dts - > tzone = thermal_zone_device_register ( name ,
trip_count ,
trip_mask ,
dts , & tzone_ops ,
NULL , 0 , 0 ) ;
if ( IS_ERR ( dts - > tzone ) ) {
ret = PTR_ERR ( dts - > tzone ) ;
goto err_ret ;
}
ret = soc_dts_enable ( id ) ;
if ( ret )
goto err_enable ;
return 0 ;
err_enable :
thermal_zone_device_unregister ( dts - > tzone ) ;
err_ret :
return ret ;
}
int intel_soc_dts_iosf_add_read_only_critical_trip (
struct intel_soc_dts_sensors * sensors , int critical_offset )
{
int i , j ;
for ( i = 0 ; i < SOC_MAX_DTS_SENSORS ; + + i ) {
for ( j = 0 ; j < sensors - > soc_dts [ i ] . trip_count ; + + j ) {
if ( ! ( sensors - > soc_dts [ i ] . trip_mask & BIT ( j ) ) ) {
2015-04-29 04:10:16 +03:00
return update_trip_temp ( & sensors - > soc_dts [ i ] , j ,
2015-03-03 00:12:58 +03:00
sensors - > tj_max - critical_offset ,
THERMAL_TRIP_CRITICAL ) ;
}
}
}
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( intel_soc_dts_iosf_add_read_only_critical_trip ) ;
void intel_soc_dts_iosf_interrupt_handler ( struct intel_soc_dts_sensors * sensors )
{
u32 sticky_out ;
int status ;
u32 ptmc_out ;
unsigned long flags ;
spin_lock_irqsave ( & sensors - > intr_notify_lock , flags ) ;
2015-11-11 20:59:29 +03:00
status = iosf_mbi_read ( BT_MBI_UNIT_PMC , MBI_REG_READ ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_PTMC , & ptmc_out ) ;
ptmc_out | = SOC_DTS_PTMC_APIC_DEASSERT_BIT ;
2015-11-11 20:59:29 +03:00
status = iosf_mbi_write ( BT_MBI_UNIT_PMC , MBI_REG_WRITE ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_PTMC , ptmc_out ) ;
2015-11-11 20:59:29 +03:00
status = iosf_mbi_read ( BT_MBI_UNIT_PMC , MBI_REG_READ ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_PTTSS , & sticky_out ) ;
pr_debug ( " status %d PTTSS %x \n " , status , sticky_out ) ;
if ( sticky_out & SOC_DTS_TRIP_MASK ) {
int i ;
/* reset sticky bit */
2015-11-11 20:59:29 +03:00
status = iosf_mbi_write ( BT_MBI_UNIT_PMC , MBI_REG_WRITE ,
2015-03-03 00:12:58 +03:00
SOC_DTS_OFFSET_PTTSS , sticky_out ) ;
spin_unlock_irqrestore ( & sensors - > intr_notify_lock , flags ) ;
for ( i = 0 ; i < SOC_MAX_DTS_SENSORS ; + + i ) {
pr_debug ( " TZD update for zone %d \n " , i ) ;
thermal_zone_device_update ( sensors - > soc_dts [ i ] . tzone ) ;
}
} else
spin_unlock_irqrestore ( & sensors - > intr_notify_lock , flags ) ;
}
EXPORT_SYMBOL_GPL ( intel_soc_dts_iosf_interrupt_handler ) ;
struct intel_soc_dts_sensors * intel_soc_dts_iosf_init (
enum intel_soc_dts_interrupt_type intr_type , int trip_count ,
int read_only_trip_count )
{
struct intel_soc_dts_sensors * sensors ;
bool notification ;
u32 tj_max ;
int ret ;
int i ;
if ( ! iosf_mbi_available ( ) )
return ERR_PTR ( - ENODEV ) ;
if ( ! trip_count | | read_only_trip_count > trip_count )
return ERR_PTR ( - EINVAL ) ;
if ( get_tj_max ( & tj_max ) )
return ERR_PTR ( - EINVAL ) ;
sensors = kzalloc ( sizeof ( * sensors ) , GFP_KERNEL ) ;
if ( ! sensors )
return ERR_PTR ( - ENOMEM ) ;
spin_lock_init ( & sensors - > intr_notify_lock ) ;
mutex_init ( & sensors - > dts_update_lock ) ;
sensors - > intr_type = intr_type ;
sensors - > tj_max = tj_max ;
if ( intr_type = = INTEL_SOC_DTS_INTERRUPT_NONE )
notification = false ;
else
notification = true ;
for ( i = 0 ; i < SOC_MAX_DTS_SENSORS ; + + i ) {
sensors - > soc_dts [ i ] . sensors = sensors ;
ret = add_dts_thermal_zone ( i , & sensors - > soc_dts [ i ] ,
notification , trip_count ,
read_only_trip_count ) ;
if ( ret )
goto err_free ;
}
for ( i = 0 ; i < SOC_MAX_DTS_SENSORS ; + + i ) {
ret = update_trip_temp ( & sensors - > soc_dts [ i ] , 0 , 0 ,
THERMAL_TRIP_PASSIVE ) ;
if ( ret )
goto err_remove_zone ;
ret = update_trip_temp ( & sensors - > soc_dts [ i ] , 1 , 0 ,
THERMAL_TRIP_PASSIVE ) ;
if ( ret )
goto err_remove_zone ;
}
return sensors ;
err_remove_zone :
for ( i = 0 ; i < SOC_MAX_DTS_SENSORS ; + + i )
remove_dts_thermal_zone ( & sensors - > soc_dts [ i ] ) ;
err_free :
kfree ( sensors ) ;
return ERR_PTR ( ret ) ;
}
EXPORT_SYMBOL_GPL ( intel_soc_dts_iosf_init ) ;
void intel_soc_dts_iosf_exit ( struct intel_soc_dts_sensors * sensors )
{
int i ;
for ( i = 0 ; i < SOC_MAX_DTS_SENSORS ; + + i ) {
update_trip_temp ( & sensors - > soc_dts [ i ] , 0 , 0 , 0 ) ;
update_trip_temp ( & sensors - > soc_dts [ i ] , 1 , 0 , 0 ) ;
remove_dts_thermal_zone ( & sensors - > soc_dts [ i ] ) ;
}
kfree ( sensors ) ;
}
EXPORT_SYMBOL_GPL ( intel_soc_dts_iosf_exit ) ;
MODULE_LICENSE ( " GPL v2 " ) ;