2023-08-17 10:10:06 +08:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Author : zhanghongchen < zhanghongchen @ loongson . cn >
* Yinbo Zhu < zhuyinbo @ loongson . cn >
* Copyright ( C ) 2022 - 2023 Loongson Technology Corporation Limited
*/
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/minmax.h>
2023-10-09 14:45:08 -05:00
# include <linux/mod_devicetable.h>
2023-08-17 10:10:06 +08:00
# include <linux/module.h>
# include <linux/platform_device.h>
2023-10-09 14:45:08 -05:00
# include <linux/property.h>
2023-08-17 10:10:06 +08:00
# include <linux/thermal.h>
# include <linux/units.h>
# include "thermal_hwmon.h"
# define LOONGSON2_MAX_SENSOR_SEL_NUM 3
# define LOONGSON2_THSENS_CTRL_HI_REG 0x0
# define LOONGSON2_THSENS_CTRL_LOW_REG 0x8
# define LOONGSON2_THSENS_STATUS_REG 0x10
# define LOONGSON2_THSENS_OUT_REG 0x14
# define LOONGSON2_THSENS_INT_LO BIT(0)
# define LOONGSON2_THSENS_INT_HIGH BIT(1)
# define LOONGSON2_THSENS_OUT_MASK 0xFF
struct loongson2_thermal_chip_data {
unsigned int thermal_sensor_sel ;
} ;
struct loongson2_thermal_data {
void __iomem * regs ;
const struct loongson2_thermal_chip_data * chip_data ;
} ;
static int loongson2_thermal_set ( struct loongson2_thermal_data * data ,
int low , int high , bool enable )
{
u64 reg_ctrl = 0 ;
int reg_off = data - > chip_data - > thermal_sensor_sel * 2 ;
low = clamp ( - 40 , low , high ) ;
high = clamp ( 125 , low , high ) ;
low + = HECTO ;
high + = HECTO ;
reg_ctrl = low ;
reg_ctrl | = enable ? 0x100 : 0 ;
writew ( reg_ctrl , data - > regs + LOONGSON2_THSENS_CTRL_LOW_REG + reg_off ) ;
reg_ctrl = high ;
reg_ctrl | = enable ? 0x100 : 0 ;
writew ( reg_ctrl , data - > regs + LOONGSON2_THSENS_CTRL_HI_REG + reg_off ) ;
return 0 ;
}
static int loongson2_thermal_get_temp ( struct thermal_zone_device * tz , int * temp )
{
u32 reg_val ;
struct loongson2_thermal_data * data = thermal_zone_device_priv ( tz ) ;
reg_val = readl ( data - > regs + LOONGSON2_THSENS_OUT_REG ) ;
* temp = ( ( reg_val & LOONGSON2_THSENS_OUT_MASK ) - HECTO ) * KILO ;
return 0 ;
}
static irqreturn_t loongson2_thermal_irq_thread ( int irq , void * dev )
{
struct thermal_zone_device * tzd = dev ;
struct loongson2_thermal_data * data = thermal_zone_device_priv ( tzd ) ;
writeb ( LOONGSON2_THSENS_INT_LO | LOONGSON2_THSENS_INT_HIGH , data - > regs +
LOONGSON2_THSENS_STATUS_REG ) ;
thermal_zone_device_update ( tzd , THERMAL_EVENT_UNSPECIFIED ) ;
return IRQ_HANDLED ;
}
static int loongson2_thermal_set_trips ( struct thermal_zone_device * tz , int low , int high )
{
struct loongson2_thermal_data * data = thermal_zone_device_priv ( tz ) ;
return loongson2_thermal_set ( data , low / MILLI , high / MILLI , true ) ;
}
static const struct thermal_zone_device_ops loongson2_of_thermal_ops = {
. get_temp = loongson2_thermal_get_temp ,
. set_trips = loongson2_thermal_set_trips ,
} ;
static int loongson2_thermal_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct loongson2_thermal_data * data ;
struct thermal_zone_device * tzd ;
int ret , irq , i ;
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
data - > chip_data = device_get_match_data ( dev ) ;
data - > regs = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( data - > regs ) )
return PTR_ERR ( data - > regs ) ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 )
return irq ;
writeb ( LOONGSON2_THSENS_INT_LO | LOONGSON2_THSENS_INT_HIGH , data - > regs +
LOONGSON2_THSENS_STATUS_REG ) ;
loongson2_thermal_set ( data , 0 , 0 , false ) ;
for ( i = 0 ; i < = LOONGSON2_MAX_SENSOR_SEL_NUM ; i + + ) {
tzd = devm_thermal_of_zone_register ( dev , i , data ,
& loongson2_of_thermal_ops ) ;
if ( ! IS_ERR ( tzd ) )
break ;
2023-11-24 17:57:45 +08:00
if ( PTR_ERR ( tzd ) ! = - ENODEV )
2023-08-17 10:10:06 +08:00
continue ;
return dev_err_probe ( dev , PTR_ERR ( tzd ) , " failed to register " ) ;
}
ret = devm_request_threaded_irq ( dev , irq , NULL , loongson2_thermal_irq_thread ,
IRQF_ONESHOT , " loongson2_thermal " , tzd ) ;
if ( ret < 0 )
return dev_err_probe ( dev , ret , " failed to request alarm irq \n " ) ;
devm_thermal_add_hwmon_sysfs ( dev , tzd ) ;
return 0 ;
}
static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k1000_data = {
. thermal_sensor_sel = 0 ,
} ;
static const struct of_device_id of_loongson2_thermal_match [ ] = {
{
. compatible = " loongson,ls2k1000-thermal " ,
. data = & loongson2_thermal_ls2k1000_data ,
} ,
{ /* end */ }
} ;
MODULE_DEVICE_TABLE ( of , of_loongson2_thermal_match ) ;
static struct platform_driver loongson2_thermal_driver = {
. driver = {
. name = " loongson2_thermal " ,
. of_match_table = of_loongson2_thermal_match ,
} ,
. probe = loongson2_thermal_probe ,
} ;
module_platform_driver ( loongson2_thermal_driver ) ;
MODULE_DESCRIPTION ( " Loongson2 thermal driver " ) ;
MODULE_LICENSE ( " GPL " ) ;