2018-08-20 21:42:35 +03:00
// SPDX-License-Identifier: GPL-2.0
2016-12-22 13:38:21 +03:00
/*
* R - Car Gen3 THS thermal sensor driver
* Based on rcar_thermal . c and work from Hien Dang and Khiem Nguyen .
*
* Copyright ( C ) 2016 Renesas Electronics Corporation .
* Copyright ( C ) 2016 Sang Engineering
*/
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/module.h>
2023-07-14 20:50:07 +03:00
# include <linux/of.h>
2016-12-22 13:38:21 +03:00
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/thermal.h>
2019-02-11 22:56:20 +03:00
# include "thermal_hwmon.h"
2017-03-29 21:43:54 +03:00
2016-12-22 13:38:21 +03:00
/* Register offsets */
# define REG_GEN3_IRQSTR 0x04
# define REG_GEN3_IRQMSK 0x08
# define REG_GEN3_IRQCTL 0x0C
# define REG_GEN3_IRQEN 0x10
# define REG_GEN3_IRQTEMP1 0x14
# define REG_GEN3_IRQTEMP2 0x18
# define REG_GEN3_IRQTEMP3 0x1C
# define REG_GEN3_THCTR 0x20
# define REG_GEN3_TEMP 0x28
# define REG_GEN3_THCODE1 0x50
# define REG_GEN3_THCODE2 0x54
# define REG_GEN3_THCODE3 0x58
2021-10-14 13:38:16 +03:00
# define REG_GEN3_PTAT1 0x5c
# define REG_GEN3_PTAT2 0x60
# define REG_GEN3_PTAT3 0x64
# define REG_GEN3_THSCP 0x68
2023-05-11 22:22:19 +03:00
# define REG_GEN4_THSFMON00 0x180
# define REG_GEN4_THSFMON01 0x184
# define REG_GEN4_THSFMON02 0x188
# define REG_GEN4_THSFMON15 0x1BC
# define REG_GEN4_THSFMON16 0x1C0
# define REG_GEN4_THSFMON17 0x1C4
2016-12-22 13:38:21 +03:00
2017-03-29 21:43:54 +03:00
/* IRQ{STR,MSK,EN} bits */
# define IRQ_TEMP1 BIT(0)
# define IRQ_TEMP2 BIT(1)
# define IRQ_TEMP3 BIT(2)
# define IRQ_TEMPD1 BIT(3)
# define IRQ_TEMPD2 BIT(4)
# define IRQ_TEMPD3 BIT(5)
2016-12-22 13:38:21 +03:00
/* THCTR bits */
# define THCTR_PONM BIT(6)
# define THCTR_THSST BIT(0)
2021-10-14 13:38:16 +03:00
/* THSCP bits */
# define THSCP_COR_PARA_VLD (BIT(15) | BIT(14))
2016-12-22 13:38:21 +03:00
# define CTEMP_MASK 0xFFF
# define MCELSIUS(temp) ((temp) * 1000)
# define GEN3_FUSE_MASK 0xFFF
2023-05-11 22:22:19 +03:00
# define GEN4_FUSE_MASK 0xFFF
2016-12-22 13:38:21 +03:00
2021-03-09 19:24:19 +03:00
# define TSC_MAX_NUM 5
2016-12-22 13:38:21 +03:00
/* Structure for thermal temperature calculation */
struct equation_coefs {
int a1 ;
int b1 ;
int a2 ;
int b2 ;
} ;
2023-05-11 22:22:18 +03:00
struct rcar_gen3_thermal_priv ;
2023-05-11 22:22:17 +03:00
struct rcar_thermal_info {
int ths_tj_1 ;
2023-05-11 22:22:18 +03:00
void ( * read_fuses ) ( struct rcar_gen3_thermal_priv * priv ) ;
2023-05-11 22:22:17 +03:00
} ;
2016-12-22 13:38:21 +03:00
struct rcar_gen3_thermal_tsc {
void __iomem * base ;
struct thermal_zone_device * zone ;
struct equation_coefs coef ;
2019-05-13 23:03:54 +03:00
int tj_t ;
2021-10-14 13:38:15 +03:00
int thcode [ 3 ] ;
2016-12-22 13:38:21 +03:00
} ;
struct rcar_gen3_thermal_priv {
struct rcar_gen3_thermal_tsc * tscs [ TSC_MAX_NUM ] ;
2023-02-08 22:03:32 +03:00
struct thermal_zone_device_ops ops ;
2017-03-29 21:43:53 +03:00
unsigned int num_tscs ;
2021-10-14 13:38:15 +03:00
int ptat [ 3 ] ;
2023-05-11 22:22:17 +03:00
const struct rcar_thermal_info * info ;
2016-12-22 13:38:21 +03:00
} ;
static inline u32 rcar_gen3_thermal_read ( struct rcar_gen3_thermal_tsc * tsc ,
u32 reg )
{
return ioread32 ( tsc - > base + reg ) ;
}
static inline void rcar_gen3_thermal_write ( struct rcar_gen3_thermal_tsc * tsc ,
u32 reg , u32 data )
{
iowrite32 ( data , tsc - > base + reg ) ;
}
/*
* Linear approximation for temperature
*
* [ reg ] = [ temp ] * a + b = > [ temp ] = ( [ reg ] - b ) / a
*
* The constants a and b are calculated using two triplets of int values PTAT
* and THCODE . PTAT and THCODE can either be read from hardware or use hard
* coded values from driver . The formula to calculate a and b are taken from
* BSP and sparsely documented and understood .
*
* Examining the linear formula and the formula used to calculate constants a
* and b while knowing that the span for PTAT and THCODE values are between
* 0x000 and 0xfff the largest integer possible is 0xfff * 0xfff = = 0xffe001 .
* Integer also needs to be signed so that leaves 7 bits for binary
* fixed point scaling .
*/
# define FIXPT_SHIFT 7
# define FIXPT_INT(_x) ((_x) << FIXPT_SHIFT)
2017-03-29 21:43:54 +03:00
# define INT_FIXPT(_x) ((_x) >> FIXPT_SHIFT)
2016-12-22 13:38:21 +03:00
# define FIXPT_DIV(_a, _b) DIV_ROUND_CLOSEST(((_a) << FIXPT_SHIFT), (_b))
# define FIXPT_TO_MCELSIUS(_x) ((_x) * 1000 >> FIXPT_SHIFT)
# define RCAR3_THERMAL_GRAN 500 /* mili Celsius */
/* no idea where these constants come from */
# define TJ_3 -41
2021-10-14 13:38:15 +03:00
static void rcar_gen3_thermal_calc_coefs ( struct rcar_gen3_thermal_priv * priv ,
struct rcar_gen3_thermal_tsc * tsc ,
2019-05-13 23:03:53 +03:00
int ths_tj_1 )
2016-12-22 13:38:21 +03:00
{
/* TODO: Find documentation and document constant calculation formula */
/*
* Division is not scaled in BSP and if scaled it might overflow
* the dividend ( 4095 * 4095 < < 14 > INT_MAX ) so keep it unscaled
*/
2021-10-14 13:38:15 +03:00
tsc - > tj_t = ( FIXPT_INT ( ( priv - > ptat [ 1 ] - priv - > ptat [ 2 ] ) * ( ths_tj_1 - TJ_3 ) )
/ ( priv - > ptat [ 0 ] - priv - > ptat [ 2 ] ) ) + FIXPT_INT ( TJ_3 ) ;
2016-12-22 13:38:21 +03:00
2021-10-14 13:38:15 +03:00
tsc - > coef . a1 = FIXPT_DIV ( FIXPT_INT ( tsc - > thcode [ 1 ] - tsc - > thcode [ 2 ] ) ,
2019-05-13 23:03:54 +03:00
tsc - > tj_t - FIXPT_INT ( TJ_3 ) ) ;
2021-10-14 13:38:15 +03:00
tsc - > coef . b1 = FIXPT_INT ( tsc - > thcode [ 2 ] ) - tsc - > coef . a1 * TJ_3 ;
2016-12-22 13:38:21 +03:00
2021-10-14 13:38:15 +03:00
tsc - > coef . a2 = FIXPT_DIV ( FIXPT_INT ( tsc - > thcode [ 1 ] - tsc - > thcode [ 0 ] ) ,
2019-05-13 23:03:54 +03:00
tsc - > tj_t - FIXPT_INT ( ths_tj_1 ) ) ;
2021-10-14 13:38:15 +03:00
tsc - > coef . b2 = FIXPT_INT ( tsc - > thcode [ 0 ] ) - tsc - > coef . a2 * ths_tj_1 ;
2016-12-22 13:38:21 +03:00
}
static int rcar_gen3_thermal_round ( int temp )
{
int result , round_offs ;
round_offs = temp > = 0 ? RCAR3_THERMAL_GRAN / 2 :
- RCAR3_THERMAL_GRAN / 2 ;
result = ( temp + round_offs ) / RCAR3_THERMAL_GRAN ;
return result * RCAR3_THERMAL_GRAN ;
}
2022-08-05 01:43:33 +03:00
static int rcar_gen3_thermal_get_temp ( struct thermal_zone_device * tz , int * temp )
2016-12-22 13:38:21 +03:00
{
2023-03-01 23:14:30 +03:00
struct rcar_gen3_thermal_tsc * tsc = thermal_zone_device_priv ( tz ) ;
2019-05-13 23:03:55 +03:00
int mcelsius , val ;
2020-06-25 14:38:19 +03:00
int reg ;
2016-12-22 13:38:21 +03:00
/* Read register and convert to mili Celsius */
reg = rcar_gen3_thermal_read ( tsc , REG_GEN3_TEMP ) & CTEMP_MASK ;
2021-10-14 13:38:15 +03:00
if ( reg < = tsc - > thcode [ 1 ] )
2019-05-13 23:03:55 +03:00
val = FIXPT_DIV ( FIXPT_INT ( reg ) - tsc - > coef . b1 ,
tsc - > coef . a1 ) ;
else
val = FIXPT_DIV ( FIXPT_INT ( reg ) - tsc - > coef . b2 ,
tsc - > coef . a2 ) ;
mcelsius = FIXPT_TO_MCELSIUS ( val ) ;
2016-12-22 13:38:21 +03:00
2020-01-17 19:05:54 +03:00
/* Guaranteed operating range is -40C to 125C. */
2016-12-22 13:38:21 +03:00
/* Round value to device granularity setting */
* temp = rcar_gen3_thermal_round ( mcelsius ) ;
return 0 ;
}
2021-08-04 12:18:17 +03:00
static int rcar_gen3_thermal_mcelsius_to_temp ( struct rcar_gen3_thermal_tsc * tsc ,
int mcelsius )
{
int celsius , val ;
celsius = DIV_ROUND_CLOSEST ( mcelsius , 1000 ) ;
if ( celsius < = INT_FIXPT ( tsc - > tj_t ) )
val = celsius * tsc - > coef . a1 + tsc - > coef . b1 ;
else
val = celsius * tsc - > coef . a2 + tsc - > coef . b2 ;
return INT_FIXPT ( val ) ;
}
2022-08-05 01:43:33 +03:00
static int rcar_gen3_thermal_set_trips ( struct thermal_zone_device * tz , int low , int high )
2021-08-04 12:18:17 +03:00
{
2023-03-01 23:14:30 +03:00
struct rcar_gen3_thermal_tsc * tsc = thermal_zone_device_priv ( tz ) ;
2021-08-04 12:18:17 +03:00
u32 irqmsk = 0 ;
if ( low ! = - INT_MAX ) {
irqmsk | = IRQ_TEMPD1 ;
rcar_gen3_thermal_write ( tsc , REG_GEN3_IRQTEMP1 ,
rcar_gen3_thermal_mcelsius_to_temp ( tsc , low ) ) ;
}
if ( high ! = INT_MAX ) {
irqmsk | = IRQ_TEMP2 ;
rcar_gen3_thermal_write ( tsc , REG_GEN3_IRQTEMP2 ,
rcar_gen3_thermal_mcelsius_to_temp ( tsc , high ) ) ;
}
rcar_gen3_thermal_write ( tsc , REG_GEN3_IRQMSK , irqmsk ) ;
return 0 ;
}
2023-02-08 22:03:32 +03:00
static const struct thermal_zone_device_ops rcar_gen3_tz_of_ops = {
2016-12-22 13:38:21 +03:00
. get_temp = rcar_gen3_thermal_get_temp ,
2021-08-04 12:18:17 +03:00
. set_trips = rcar_gen3_thermal_set_trips ,
2016-12-22 13:38:21 +03:00
} ;
2021-08-04 12:18:17 +03:00
static irqreturn_t rcar_gen3_thermal_irq ( int irq , void * data )
{
struct rcar_gen3_thermal_priv * priv = data ;
unsigned int i ;
u32 status ;
for ( i = 0 ; i < priv - > num_tscs ; i + + ) {
status = rcar_gen3_thermal_read ( priv - > tscs [ i ] , REG_GEN3_IRQSTR ) ;
rcar_gen3_thermal_write ( priv - > tscs [ i ] , REG_GEN3_IRQSTR , 0 ) ;
2023-02-08 22:03:33 +03:00
if ( status & & priv - > tscs [ i ] - > zone )
2021-08-04 12:18:17 +03:00
thermal_zone_device_update ( priv - > tscs [ i ] - > zone ,
THERMAL_EVENT_UNSPECIFIED ) ;
}
return IRQ_HANDLED ;
}
2023-05-11 22:22:18 +03:00
static void rcar_gen3_thermal_read_fuses_gen3 ( struct rcar_gen3_thermal_priv * priv )
{
unsigned int i ;
/*
* Set the pseudo calibration points with fused values .
* PTAT is shared between all TSCs but only fused for the first
* TSC while THCODEs are fused for each TSC .
*/
priv - > ptat [ 0 ] = rcar_gen3_thermal_read ( priv - > tscs [ 0 ] , REG_GEN3_PTAT1 ) &
GEN3_FUSE_MASK ;
priv - > ptat [ 1 ] = rcar_gen3_thermal_read ( priv - > tscs [ 0 ] , REG_GEN3_PTAT2 ) &
GEN3_FUSE_MASK ;
priv - > ptat [ 2 ] = rcar_gen3_thermal_read ( priv - > tscs [ 0 ] , REG_GEN3_PTAT3 ) &
GEN3_FUSE_MASK ;
for ( i = 0 ; i < priv - > num_tscs ; i + + ) {
struct rcar_gen3_thermal_tsc * tsc = priv - > tscs [ i ] ;
tsc - > thcode [ 0 ] = rcar_gen3_thermal_read ( tsc , REG_GEN3_THCODE1 ) &
GEN3_FUSE_MASK ;
tsc - > thcode [ 1 ] = rcar_gen3_thermal_read ( tsc , REG_GEN3_THCODE2 ) &
GEN3_FUSE_MASK ;
tsc - > thcode [ 2 ] = rcar_gen3_thermal_read ( tsc , REG_GEN3_THCODE3 ) &
GEN3_FUSE_MASK ;
}
}
2023-05-11 22:22:19 +03:00
static void rcar_gen3_thermal_read_fuses_gen4 ( struct rcar_gen3_thermal_priv * priv )
{
unsigned int i ;
/*
* Set the pseudo calibration points with fused values .
* PTAT is shared between all TSCs but only fused for the first
* TSC while THCODEs are fused for each TSC .
*/
priv - > ptat [ 0 ] = rcar_gen3_thermal_read ( priv - > tscs [ 0 ] , REG_GEN4_THSFMON16 ) &
GEN4_FUSE_MASK ;
priv - > ptat [ 1 ] = rcar_gen3_thermal_read ( priv - > tscs [ 0 ] , REG_GEN4_THSFMON17 ) &
GEN4_FUSE_MASK ;
priv - > ptat [ 2 ] = rcar_gen3_thermal_read ( priv - > tscs [ 0 ] , REG_GEN4_THSFMON15 ) &
GEN4_FUSE_MASK ;
for ( i = 0 ; i < priv - > num_tscs ; i + + ) {
struct rcar_gen3_thermal_tsc * tsc = priv - > tscs [ i ] ;
tsc - > thcode [ 0 ] = rcar_gen3_thermal_read ( tsc , REG_GEN4_THSFMON01 ) &
GEN4_FUSE_MASK ;
tsc - > thcode [ 1 ] = rcar_gen3_thermal_read ( tsc , REG_GEN4_THSFMON02 ) &
GEN4_FUSE_MASK ;
tsc - > thcode [ 2 ] = rcar_gen3_thermal_read ( tsc , REG_GEN4_THSFMON00 ) &
GEN4_FUSE_MASK ;
}
}
2021-10-14 13:38:16 +03:00
static bool rcar_gen3_thermal_read_fuses ( struct rcar_gen3_thermal_priv * priv )
{
unsigned int i ;
u32 thscp ;
/* If fuses are not set, fallback to pseudo values. */
thscp = rcar_gen3_thermal_read ( priv - > tscs [ 0 ] , REG_GEN3_THSCP ) ;
2023-05-11 22:22:18 +03:00
if ( ! priv - > info - > read_fuses | |
( thscp & THSCP_COR_PARA_VLD ) ! = THSCP_COR_PARA_VLD ) {
2021-10-14 13:38:16 +03:00
/* Default THCODE values in case FUSEs are not set. */
static const int thcodes [ TSC_MAX_NUM ] [ 3 ] = {
{ 3397 , 2800 , 2221 } ,
{ 3393 , 2795 , 2216 } ,
{ 3389 , 2805 , 2237 } ,
{ 3415 , 2694 , 2195 } ,
{ 3356 , 2724 , 2244 } ,
} ;
priv - > ptat [ 0 ] = 2631 ;
priv - > ptat [ 1 ] = 1509 ;
priv - > ptat [ 2 ] = 435 ;
for ( i = 0 ; i < priv - > num_tscs ; i + + ) {
struct rcar_gen3_thermal_tsc * tsc = priv - > tscs [ i ] ;
tsc - > thcode [ 0 ] = thcodes [ i ] [ 0 ] ;
tsc - > thcode [ 1 ] = thcodes [ i ] [ 1 ] ;
tsc - > thcode [ 2 ] = thcodes [ i ] [ 2 ] ;
}
return false ;
}
2023-05-11 22:22:18 +03:00
priv - > info - > read_fuses ( priv ) ;
2021-10-14 13:38:16 +03:00
return true ;
}
2023-02-08 22:03:33 +03:00
static void rcar_gen3_thermal_init ( struct rcar_gen3_thermal_priv * priv ,
struct rcar_gen3_thermal_tsc * tsc )
2016-12-22 13:38:21 +03:00
{
u32 reg_val ;
reg_val = rcar_gen3_thermal_read ( tsc , REG_GEN3_THCTR ) ;
reg_val & = ~ THCTR_PONM ;
rcar_gen3_thermal_write ( tsc , REG_GEN3_THCTR , reg_val ) ;
usleep_range ( 1000 , 2000 ) ;
2019-03-27 12:03:18 +03:00
rcar_gen3_thermal_write ( tsc , REG_GEN3_IRQCTL , 0 ) ;
2017-03-29 21:43:54 +03:00
rcar_gen3_thermal_write ( tsc , REG_GEN3_IRQMSK , 0 ) ;
2023-02-08 22:03:33 +03:00
if ( priv - > ops . set_trips )
2021-08-04 12:18:17 +03:00
rcar_gen3_thermal_write ( tsc , REG_GEN3_IRQEN ,
IRQ_TEMPD1 | IRQ_TEMP2 ) ;
2017-03-29 21:43:54 +03:00
2016-12-22 13:38:21 +03:00
reg_val = rcar_gen3_thermal_read ( tsc , REG_GEN3_THCTR ) ;
reg_val | = THCTR_THSST ;
rcar_gen3_thermal_write ( tsc , REG_GEN3_THCTR , reg_val ) ;
2017-03-29 21:43:50 +03:00
usleep_range ( 1000 , 2000 ) ;
2016-12-22 13:38:21 +03:00
}
2023-05-11 22:22:17 +03:00
static const struct rcar_thermal_info rcar_m3w_thermal_info = {
. ths_tj_1 = 116 ,
2023-05-11 22:22:18 +03:00
. read_fuses = rcar_gen3_thermal_read_fuses_gen3 ,
2023-05-11 22:22:17 +03:00
} ;
static const struct rcar_thermal_info rcar_gen3_thermal_info = {
. ths_tj_1 = 126 ,
2023-05-11 22:22:18 +03:00
. read_fuses = rcar_gen3_thermal_read_fuses_gen3 ,
2023-05-11 22:22:17 +03:00
} ;
2023-05-11 22:22:19 +03:00
static const struct rcar_thermal_info rcar_gen4_thermal_info = {
. ths_tj_1 = 126 ,
. read_fuses = rcar_gen3_thermal_read_fuses_gen4 ,
} ;
2016-12-22 13:38:21 +03:00
static const struct of_device_id rcar_gen3_thermal_dt_ids [ ] = {
2019-05-13 23:03:53 +03:00
{
. compatible = " renesas,r8a774a1-thermal " ,
2023-05-11 22:22:17 +03:00
. data = & rcar_m3w_thermal_info ,
2019-05-13 23:03:53 +03:00
} ,
2019-09-23 17:25:46 +03:00
{
. compatible = " renesas,r8a774b1-thermal " ,
2023-05-11 22:22:17 +03:00
. data = & rcar_gen3_thermal_info ,
2019-09-23 17:25:46 +03:00
} ,
2020-07-15 14:08:53 +03:00
{
. compatible = " renesas,r8a774e1-thermal " ,
2023-05-11 22:22:17 +03:00
. data = & rcar_gen3_thermal_info ,
2020-07-15 14:08:53 +03:00
} ,
2019-05-13 23:03:53 +03:00
{
. compatible = " renesas,r8a7795-thermal " ,
2023-05-11 22:22:17 +03:00
. data = & rcar_gen3_thermal_info ,
2019-05-13 23:03:53 +03:00
} ,
{
. compatible = " renesas,r8a7796-thermal " ,
2023-05-11 22:22:17 +03:00
. data = & rcar_m3w_thermal_info ,
2019-05-13 23:03:53 +03:00
} ,
2020-03-06 13:55:03 +03:00
{
. compatible = " renesas,r8a77961-thermal " ,
2023-05-11 22:22:17 +03:00
. data = & rcar_m3w_thermal_info ,
2020-03-06 13:55:03 +03:00
} ,
2019-05-13 23:03:53 +03:00
{
. compatible = " renesas,r8a77965-thermal " ,
2023-05-11 22:22:17 +03:00
. data = & rcar_gen3_thermal_info ,
2019-05-13 23:03:53 +03:00
} ,
{
. compatible = " renesas,r8a77980-thermal " ,
2023-05-11 22:22:17 +03:00
. data = & rcar_gen3_thermal_info ,
2019-05-13 23:03:53 +03:00
} ,
2020-11-27 01:30:28 +03:00
{
. compatible = " renesas,r8a779a0-thermal " ,
2023-05-11 22:22:17 +03:00
. data = & rcar_gen3_thermal_info ,
2020-11-27 01:30:28 +03:00
} ,
2022-07-05 22:55:20 +03:00
{
. compatible = " renesas,r8a779f0-thermal " ,
2023-05-11 22:22:19 +03:00
. data = & rcar_gen4_thermal_info ,
2022-07-05 22:55:20 +03:00
} ,
2023-02-09 19:11:54 +03:00
{
. compatible = " renesas,r8a779g0-thermal " ,
2023-05-11 22:22:19 +03:00
. data = & rcar_gen4_thermal_info ,
2023-02-09 19:11:54 +03:00
} ,
2024-03-06 13:56:03 +03:00
{
. compatible = " renesas,r8a779h0-thermal " ,
. data = & rcar_gen4_thermal_info ,
} ,
2016-12-22 13:38:21 +03:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , rcar_gen3_thermal_dt_ids ) ;
2023-09-27 22:37:25 +03:00
static void rcar_gen3_thermal_remove ( struct platform_device * pdev )
2016-12-22 13:38:21 +03:00
{
struct device * dev = & pdev - > dev ;
pm_runtime_put ( dev ) ;
pm_runtime_disable ( dev ) ;
}
2019-02-11 22:56:20 +03:00
static void rcar_gen3_hwmon_action ( void * data )
{
struct thermal_zone_device * zone = data ;
thermal_remove_hwmon_sysfs ( zone ) ;
}
2021-08-04 12:18:17 +03:00
static int rcar_gen3_thermal_request_irqs ( struct rcar_gen3_thermal_priv * priv ,
struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
unsigned int i ;
char * irqname ;
int ret , irq ;
for ( i = 0 ; i < 2 ; i + + ) {
irq = platform_get_irq_optional ( pdev , i ) ;
if ( irq < 0 )
return irq ;
irqname = devm_kasprintf ( dev , GFP_KERNEL , " %s:ch%d " ,
dev_name ( dev ) , i ) ;
if ( ! irqname )
return - ENOMEM ;
ret = devm_request_threaded_irq ( dev , irq , NULL ,
rcar_gen3_thermal_irq ,
IRQF_ONESHOT , irqname , priv ) ;
if ( ret )
return ret ;
}
return 0 ;
}
2016-12-22 13:38:21 +03:00
static int rcar_gen3_thermal_probe ( struct platform_device * pdev )
{
struct rcar_gen3_thermal_priv * priv ;
struct device * dev = & pdev - > dev ;
struct resource * res ;
struct thermal_zone_device * zone ;
2021-08-04 12:18:18 +03:00
unsigned int i ;
int ret ;
2016-12-22 13:38:21 +03:00
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2023-02-08 22:03:32 +03:00
priv - > ops = rcar_gen3_tz_of_ops ;
2017-03-29 21:43:55 +03:00
2023-05-11 22:22:17 +03:00
priv - > info = of_device_get_match_data ( dev ) ;
2016-12-22 13:38:21 +03:00
platform_set_drvdata ( pdev , priv ) ;
2021-08-04 12:18:17 +03:00
if ( rcar_gen3_thermal_request_irqs ( priv , pdev ) )
2023-02-08 22:03:32 +03:00
priv - > ops . set_trips = NULL ;
2021-08-04 12:18:17 +03:00
2016-12-22 13:38:21 +03:00
pm_runtime_enable ( dev ) ;
pm_runtime_get_sync ( dev ) ;
for ( i = 0 ; i < TSC_MAX_NUM ; i + + ) {
struct rcar_gen3_thermal_tsc * tsc ;
2017-03-29 21:43:52 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , i ) ;
if ( ! res )
break ;
2016-12-22 13:38:21 +03:00
tsc = devm_kzalloc ( dev , sizeof ( * tsc ) , GFP_KERNEL ) ;
if ( ! tsc ) {
ret = - ENOMEM ;
goto error_unregister ;
}
tsc - > base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( tsc - > base ) ) {
ret = PTR_ERR ( tsc - > base ) ;
goto error_unregister ;
}
2021-10-14 13:38:15 +03:00
2016-12-22 13:38:21 +03:00
priv - > tscs [ i ] = tsc ;
2021-10-14 13:38:16 +03:00
}
priv - > num_tscs = i ;
if ( ! rcar_gen3_thermal_read_fuses ( priv ) )
dev_info ( dev , " No calibration values fused, fallback to driver values \n " ) ;
for ( i = 0 ; i < priv - > num_tscs ; i + + ) {
struct rcar_gen3_thermal_tsc * tsc = priv - > tscs [ i ] ;
2016-12-22 13:38:21 +03:00
2023-03-07 19:30:34 +03:00
rcar_gen3_thermal_init ( priv , tsc ) ;
2023-05-11 22:22:17 +03:00
rcar_gen3_thermal_calc_coefs ( priv , tsc , priv - > info - > ths_tj_1 ) ;
2023-02-08 22:03:33 +03:00
2023-02-08 22:03:32 +03:00
zone = devm_thermal_of_zone_register ( dev , i , tsc , & priv - > ops ) ;
2016-12-22 13:38:21 +03:00
if ( IS_ERR ( zone ) ) {
2022-06-10 23:04:59 +03:00
dev_err ( dev , " Sensor %u: Can't register thermal zone \n " , i ) ;
2016-12-22 13:38:21 +03:00
ret = PTR_ERR ( zone ) ;
goto error_unregister ;
}
tsc - > zone = zone ;
2017-03-29 21:43:54 +03:00
2019-02-11 22:56:20 +03:00
ret = thermal_add_hwmon_sysfs ( tsc - > zone ) ;
if ( ret )
goto error_unregister ;
2019-07-08 15:34:09 +03:00
ret = devm_add_action_or_reset ( dev , rcar_gen3_hwmon_action , zone ) ;
2020-02-13 01:47:32 +03:00
if ( ret )
2019-02-11 22:56:20 +03:00
goto error_unregister ;
2022-10-03 12:25:48 +03:00
ret = thermal_zone_get_num_trips ( tsc - > zone ) ;
2019-05-09 12:09:17 +03:00
if ( ret < 0 )
goto error_unregister ;
2022-06-10 23:04:59 +03:00
dev_info ( dev , " Sensor %u: Loaded %d trip points \n " , i , ret ) ;
2016-12-22 13:38:21 +03:00
}
2017-03-29 21:43:53 +03:00
if ( ! priv - > num_tscs ) {
ret = - ENODEV ;
goto error_unregister ;
}
2016-12-22 13:38:21 +03:00
return 0 ;
error_unregister :
rcar_gen3_thermal_remove ( pdev ) ;
return ret ;
}
2017-03-29 21:43:56 +03:00
static int __maybe_unused rcar_gen3_thermal_resume ( struct device * dev )
{
struct rcar_gen3_thermal_priv * priv = dev_get_drvdata ( dev ) ;
unsigned int i ;
for ( i = 0 ; i < priv - > num_tscs ; i + + ) {
struct rcar_gen3_thermal_tsc * tsc = priv - > tscs [ i ] ;
2023-03-07 19:30:34 +03:00
rcar_gen3_thermal_init ( priv , tsc ) ;
2017-03-29 21:43:56 +03:00
}
return 0 ;
}
2020-11-27 01:09:23 +03:00
static SIMPLE_DEV_PM_OPS ( rcar_gen3_thermal_pm_ops , NULL ,
2017-03-29 21:43:56 +03:00
rcar_gen3_thermal_resume ) ;
2016-12-22 13:38:21 +03:00
static struct platform_driver rcar_gen3_thermal_driver = {
. driver = {
. name = " rcar_gen3_thermal " ,
2017-03-29 21:43:56 +03:00
. pm = & rcar_gen3_thermal_pm_ops ,
2016-12-22 13:38:21 +03:00
. of_match_table = rcar_gen3_thermal_dt_ids ,
} ,
. probe = rcar_gen3_thermal_probe ,
2023-09-27 22:37:25 +03:00
. remove_new = rcar_gen3_thermal_remove ,
2016-12-22 13:38:21 +03:00
} ;
module_platform_driver ( rcar_gen3_thermal_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " R-Car Gen3 THS thermal sensor driver " ) ;
MODULE_AUTHOR ( " Wolfram Sang <wsa+renesas@sang-engineering.com> " ) ;