2014-04-08 00:57:15 +04:00
/*
* intel_soc_dts_thermal . c
* Copyright ( c ) 2014 , 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/interrupt.h>
# include <asm/cpu_device_id.h>
2016-06-03 03:19:52 +03:00
# include <asm/intel-family.h>
2015-03-03 00:12:59 +03:00
# include "intel_soc_dts_iosf.h"
2014-04-08 00:57:15 +04:00
# define CRITICAL_OFFSET_FROM_TJ_MAX 5000
static int crit_offset = CRITICAL_OFFSET_FROM_TJ_MAX ;
module_param ( crit_offset , int , 0644 ) ;
MODULE_PARM_DESC ( crit_offset ,
" Critical Temperature offset from tj max in millidegree Celsius. " ) ;
2015-03-03 00:12:59 +03:00
/* IRQ 86 is a fixed APIC interrupt for BYT DTS Aux threshold notifications */
# define BYT_SOC_DTS_APIC_IRQ 86
2014-04-08 00:57:15 +04:00
2015-03-03 00:12:59 +03:00
static int soc_dts_thres_irq ;
static struct intel_soc_dts_sensors * soc_dts ;
2014-04-08 00:57:15 +04:00
static irqreturn_t soc_irq_thread_fn ( int irq , void * dev_data )
{
pr_debug ( " proc_thermal_interrupt \n " ) ;
2015-03-03 00:12:59 +03:00
intel_soc_dts_iosf_interrupt_handler ( soc_dts ) ;
2014-04-08 00:57:15 +04:00
return IRQ_HANDLED ;
}
static const struct x86_cpu_id soc_thermal_ids [ ] = {
2016-06-03 03:19:52 +03:00
{ X86_VENDOR_INTEL , 6 , INTEL_FAM6_ATOM_SILVERMONT1 , 0 ,
BYT_SOC_DTS_APIC_IRQ } ,
2014-04-08 00:57:15 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( x86cpu , soc_thermal_ids ) ;
static int __init intel_soc_thermal_init ( void )
{
int err = 0 ;
const struct x86_cpu_id * match_cpu ;
match_cpu = x86_match_cpu ( soc_thermal_ids ) ;
if ( ! match_cpu )
return - ENODEV ;
2015-03-03 00:12:59 +03:00
/* Create a zone with 2 trips with marked as read only */
soc_dts = intel_soc_dts_iosf_init ( INTEL_SOC_DTS_INTERRUPT_APIC , 2 , 1 ) ;
if ( IS_ERR ( soc_dts ) ) {
err = PTR_ERR ( soc_dts ) ;
return err ;
2014-04-08 00:57:15 +04:00
}
2015-03-03 00:12:59 +03:00
soc_dts_thres_irq = ( int ) match_cpu - > driver_data ;
2014-04-08 00:57:15 +04:00
2015-01-28 22:48:02 +03:00
if ( soc_dts_thres_irq ) {
err = request_threaded_irq ( soc_dts_thres_irq , NULL ,
soc_irq_thread_fn ,
IRQF_TRIGGER_RISING | IRQF_ONESHOT ,
" soc_dts " , soc_dts ) ;
if ( err ) {
2017-04-13 00:07:17 +03:00
/*
* Do not just error out because the user space thermal
* daemon such as DPTF may use polling instead of being
* interrupt driven .
*/
pr_warn ( " request_threaded_irq ret %d \n " , err ) ;
2015-01-28 22:48:02 +03:00
}
2014-04-08 00:57:15 +04:00
}
2015-03-03 00:12:59 +03:00
err = intel_soc_dts_iosf_add_read_only_critical_trip ( soc_dts ,
crit_offset ) ;
if ( err )
goto error_trips ;
2014-04-08 00:57:15 +04:00
return 0 ;
2015-03-03 00:12:59 +03:00
error_trips :
2015-01-28 22:48:02 +03:00
if ( soc_dts_thres_irq )
free_irq ( soc_dts_thres_irq , soc_dts ) ;
2015-03-03 00:12:59 +03:00
intel_soc_dts_iosf_exit ( soc_dts ) ;
2014-04-08 00:57:15 +04:00
return err ;
}
static void __exit intel_soc_thermal_exit ( void )
{
2015-01-28 22:48:02 +03:00
if ( soc_dts_thres_irq )
free_irq ( soc_dts_thres_irq , soc_dts ) ;
2015-03-03 00:12:59 +03:00
intel_soc_dts_iosf_exit ( soc_dts ) ;
2014-04-08 00:57:15 +04:00
}
module_init ( intel_soc_thermal_init )
module_exit ( intel_soc_thermal_exit )
MODULE_DESCRIPTION ( " Intel SoC DTS Thermal Driver " ) ;
MODULE_AUTHOR ( " Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;