2019-10-09 13:28:05 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Fan Control HDL CORE driver
*
* Copyright 2019 Analog Devices Inc .
*/
# include <linux/bits.h>
# include <linux/clk.h>
# include <linux/fpga/adi-axi-common.h>
# include <linux/hwmon.h>
2021-08-11 14:48:53 +03:00
# include <linux/hwmon-sysfs.h>
2019-10-09 13:28:05 +03:00
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
/* register map */
# define ADI_REG_RSTN 0x0080
# define ADI_REG_PWM_WIDTH 0x0084
# define ADI_REG_TACH_PERIOD 0x0088
# define ADI_REG_TACH_TOLERANCE 0x008c
# define ADI_REG_PWM_PERIOD 0x00c0
# define ADI_REG_TACH_MEASUR 0x00c4
# define ADI_REG_TEMPERATURE 0x00c8
2021-08-11 14:48:53 +03:00
# define ADI_REG_TEMP_00_H 0x0100
# define ADI_REG_TEMP_25_L 0x0104
# define ADI_REG_TEMP_25_H 0x0108
# define ADI_REG_TEMP_50_L 0x010c
# define ADI_REG_TEMP_50_H 0x0110
# define ADI_REG_TEMP_75_L 0x0114
# define ADI_REG_TEMP_75_H 0x0118
# define ADI_REG_TEMP_100_L 0x011c
2019-10-09 13:28:05 +03:00
# define ADI_REG_IRQ_MASK 0x0040
# define ADI_REG_IRQ_PENDING 0x0044
# define ADI_REG_IRQ_SRC 0x0048
/* IRQ sources */
# define ADI_IRQ_SRC_PWM_CHANGED BIT(0)
# define ADI_IRQ_SRC_TACH_ERR BIT(1)
# define ADI_IRQ_SRC_TEMP_INCREASE BIT(2)
# define ADI_IRQ_SRC_NEW_MEASUR BIT(3)
# define ADI_IRQ_SRC_MASK GENMASK(3, 0)
# define ADI_IRQ_MASK_OUT_ALL 0xFFFFFFFFU
# define SYSFS_PWM_MAX 255
struct axi_fan_control_data {
void __iomem * base ;
struct device * hdev ;
unsigned long clk_rate ;
int irq ;
/* pulses per revolution */
u32 ppr ;
bool hw_pwm_req ;
bool update_tacho_params ;
u8 fan_fault ;
} ;
static inline void axi_iowrite ( const u32 val , const u32 reg ,
const struct axi_fan_control_data * ctl )
{
iowrite32 ( val , ctl - > base + reg ) ;
}
static inline u32 axi_ioread ( const u32 reg ,
const struct axi_fan_control_data * ctl )
{
return ioread32 ( ctl - > base + reg ) ;
}
2021-08-11 14:48:53 +03:00
/*
* The core calculates the temperature as :
* T = / raw * 509.3140064 / 65535 ) - 280.2308787
*/
static ssize_t axi_fan_control_show ( struct device * dev , struct device_attribute * da , char * buf )
{
struct axi_fan_control_data * ctl = dev_get_drvdata ( dev ) ;
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
u32 temp = axi_ioread ( attr - > index , ctl ) ;
temp = DIV_ROUND_CLOSEST_ULL ( temp * 509314ULL , 65535 ) - 280230 ;
return sprintf ( buf , " %u \n " , temp ) ;
}
static ssize_t axi_fan_control_store ( struct device * dev , struct device_attribute * da ,
const char * buf , size_t count )
{
struct axi_fan_control_data * ctl = dev_get_drvdata ( dev ) ;
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
u32 temp ;
int ret ;
ret = kstrtou32 ( buf , 10 , & temp ) ;
if ( ret )
return ret ;
temp = DIV_ROUND_CLOSEST_ULL ( ( temp + 280230 ) * 65535ULL , 509314 ) ;
axi_iowrite ( temp , attr - > index , ctl ) ;
return count ;
}
2019-10-09 13:28:05 +03:00
static long axi_fan_control_get_pwm_duty ( const struct axi_fan_control_data * ctl )
{
u32 pwm_width = axi_ioread ( ADI_REG_PWM_WIDTH , ctl ) ;
u32 pwm_period = axi_ioread ( ADI_REG_PWM_PERIOD , ctl ) ;
/*
* PWM_PERIOD is a RO register set by the core . It should never be 0.
* For now we are trusting the HW . . .
*/
return DIV_ROUND_CLOSEST ( pwm_width * SYSFS_PWM_MAX , pwm_period ) ;
}
static int axi_fan_control_set_pwm_duty ( const long val ,
struct axi_fan_control_data * ctl )
{
u32 pwm_period = axi_ioread ( ADI_REG_PWM_PERIOD , ctl ) ;
u32 new_width ;
long __val = clamp_val ( val , 0 , SYSFS_PWM_MAX ) ;
new_width = DIV_ROUND_CLOSEST ( __val * pwm_period , SYSFS_PWM_MAX ) ;
axi_iowrite ( new_width , ADI_REG_PWM_WIDTH , ctl ) ;
return 0 ;
}
static long axi_fan_control_get_fan_rpm ( const struct axi_fan_control_data * ctl )
{
const u32 tach = axi_ioread ( ADI_REG_TACH_MEASUR , ctl ) ;
if ( tach = = 0 )
/* should we return error, EAGAIN maybe? */
return 0 ;
/*
* The tacho period should be :
* TACH = 60 / ( ppr * rpm ) , where rpm is revolutions per second
* and ppr is pulses per revolution .
* Given the tacho period , we can multiply it by the input clock
* so that we know how many clocks we need to have this period .
* From this , we can derive the RPM value .
*/
return DIV_ROUND_CLOSEST ( 60 * ctl - > clk_rate , ctl - > ppr * tach ) ;
}
static int axi_fan_control_read_temp ( struct device * dev , u32 attr , long * val )
{
struct axi_fan_control_data * ctl = dev_get_drvdata ( dev ) ;
long raw_temp ;
switch ( attr ) {
case hwmon_temp_input :
raw_temp = axi_ioread ( ADI_REG_TEMPERATURE , ctl ) ;
/*
* The formula for the temperature is :
* T = ( ADC * 501.3743 / 2 ^ bits ) - 273.6777
* It ' s multiplied by 1000 to have millidegrees as
* specified by the hwmon sysfs interface .
*/
* val = ( ( raw_temp * 501374 ) > > 16 ) - 273677 ;
return 0 ;
default :
return - ENOTSUPP ;
}
}
static int axi_fan_control_read_fan ( struct device * dev , u32 attr , long * val )
{
struct axi_fan_control_data * ctl = dev_get_drvdata ( dev ) ;
switch ( attr ) {
case hwmon_fan_fault :
* val = ctl - > fan_fault ;
/* clear it now */
ctl - > fan_fault = 0 ;
return 0 ;
case hwmon_fan_input :
* val = axi_fan_control_get_fan_rpm ( ctl ) ;
return 0 ;
default :
return - ENOTSUPP ;
}
}
static int axi_fan_control_read_pwm ( struct device * dev , u32 attr , long * val )
{
struct axi_fan_control_data * ctl = dev_get_drvdata ( dev ) ;
switch ( attr ) {
case hwmon_pwm_input :
* val = axi_fan_control_get_pwm_duty ( ctl ) ;
return 0 ;
default :
return - ENOTSUPP ;
}
}
static int axi_fan_control_write_pwm ( struct device * dev , u32 attr , long val )
{
struct axi_fan_control_data * ctl = dev_get_drvdata ( dev ) ;
switch ( attr ) {
case hwmon_pwm_input :
return axi_fan_control_set_pwm_duty ( val , ctl ) ;
default :
return - ENOTSUPP ;
}
}
static int axi_fan_control_read_labels ( struct device * dev ,
enum hwmon_sensor_types type ,
u32 attr , int channel , const char * * str )
{
switch ( type ) {
case hwmon_fan :
* str = " FAN " ;
return 0 ;
case hwmon_temp :
* str = " SYSMON4 " ;
return 0 ;
default :
return - ENOTSUPP ;
}
}
static int axi_fan_control_read ( struct device * dev ,
enum hwmon_sensor_types type ,
u32 attr , int channel , long * val )
{
switch ( type ) {
case hwmon_fan :
return axi_fan_control_read_fan ( dev , attr , val ) ;
case hwmon_pwm :
return axi_fan_control_read_pwm ( dev , attr , val ) ;
case hwmon_temp :
return axi_fan_control_read_temp ( dev , attr , val ) ;
default :
return - ENOTSUPP ;
}
}
static int axi_fan_control_write ( struct device * dev ,
enum hwmon_sensor_types type ,
u32 attr , int channel , long val )
{
switch ( type ) {
case hwmon_pwm :
return axi_fan_control_write_pwm ( dev , attr , val ) ;
default :
return - ENOTSUPP ;
}
}
static umode_t axi_fan_control_fan_is_visible ( const u32 attr )
{
switch ( attr ) {
case hwmon_fan_input :
case hwmon_fan_fault :
case hwmon_fan_label :
return 0444 ;
default :
return 0 ;
}
}
static umode_t axi_fan_control_pwm_is_visible ( const u32 attr )
{
switch ( attr ) {
case hwmon_pwm_input :
return 0644 ;
default :
return 0 ;
}
}
static umode_t axi_fan_control_temp_is_visible ( const u32 attr )
{
switch ( attr ) {
case hwmon_temp_input :
case hwmon_temp_label :
return 0444 ;
default :
return 0 ;
}
}
static umode_t axi_fan_control_is_visible ( const void * data ,
enum hwmon_sensor_types type ,
u32 attr , int channel )
{
switch ( type ) {
case hwmon_fan :
return axi_fan_control_fan_is_visible ( attr ) ;
case hwmon_pwm :
return axi_fan_control_pwm_is_visible ( attr ) ;
case hwmon_temp :
return axi_fan_control_temp_is_visible ( attr ) ;
default :
return 0 ;
}
}
/*
* This core has two main ways of changing the PWM duty cycle . It is done ,
* either by a request from userspace ( writing on pwm1_input ) or by the
* core itself . When the change is done by the core , it will use predefined
* parameters to evaluate the tach signal and , on that case we cannot set them .
* On the other hand , when the request is done by the user , with some arbitrary
* value that the core does not now about , we have to provide the tach
* parameters so that , the core can evaluate the signal . On the IRQ handler we
* distinguish this by using the ADI_IRQ_SRC_TEMP_INCREASE interrupt . This tell
* us that the CORE requested a new duty cycle . After this , there is 5 s delay
* on which the core waits for the fan rotation speed to stabilize . After this
* we get ADI_IRQ_SRC_PWM_CHANGED irq where we will decide if we need to set
* the tach parameters or not on the next tach measurement cycle ( corresponding
* already to the ney duty cycle ) based on the % ctl - > hw_pwm_req flag .
*/
static irqreturn_t axi_fan_control_irq_handler ( int irq , void * data )
{
struct axi_fan_control_data * ctl = ( struct axi_fan_control_data * ) data ;
u32 irq_pending = axi_ioread ( ADI_REG_IRQ_PENDING , ctl ) ;
u32 clear_mask ;
2021-08-11 14:48:52 +03:00
if ( irq_pending & ADI_IRQ_SRC_TEMP_INCREASE )
/* hardware requested a new pwm */
ctl - > hw_pwm_req = true ;
2019-10-09 13:28:05 +03:00
if ( irq_pending & ADI_IRQ_SRC_PWM_CHANGED ) {
/*
* if the pwm changes on behalf of software ,
* we need to provide new tacho parameters to the core .
* Wait for the next measurement for that . . .
*/
if ( ! ctl - > hw_pwm_req ) {
ctl - > update_tacho_params = true ;
} else {
ctl - > hw_pwm_req = false ;
2022-03-08 16:54:08 +03:00
hwmon_notify_event ( ctl - > hdev , hwmon_pwm ,
hwmon_pwm_input , 0 ) ;
2019-10-09 13:28:05 +03:00
}
}
2021-08-11 14:48:52 +03:00
if ( irq_pending & ADI_IRQ_SRC_NEW_MEASUR ) {
if ( ctl - > update_tacho_params ) {
u32 new_tach = axi_ioread ( ADI_REG_TACH_MEASUR , ctl ) ;
/* get 25% tolerance */
u32 tach_tol = DIV_ROUND_CLOSEST ( new_tach * 25 , 100 ) ;
/* set new tacho parameters */
axi_iowrite ( new_tach , ADI_REG_TACH_PERIOD , ctl ) ;
axi_iowrite ( tach_tol , ADI_REG_TACH_TOLERANCE , ctl ) ;
ctl - > update_tacho_params = false ;
}
}
2019-10-09 13:28:05 +03:00
if ( irq_pending & ADI_IRQ_SRC_TACH_ERR )
ctl - > fan_fault = 1 ;
/* clear all interrupts */
clear_mask = irq_pending & ADI_IRQ_SRC_MASK ;
axi_iowrite ( clear_mask , ADI_REG_IRQ_PENDING , ctl ) ;
return IRQ_HANDLED ;
}
static int axi_fan_control_init ( struct axi_fan_control_data * ctl ,
const struct device_node * np )
{
int ret ;
/* get fan pulses per revolution */
ret = of_property_read_u32 ( np , " pulses-per-revolution " , & ctl - > ppr ) ;
if ( ret )
return ret ;
/* 1, 2 and 4 are the typical and accepted values */
if ( ctl - > ppr ! = 1 & & ctl - > ppr ! = 2 & & ctl - > ppr ! = 4 )
return - EINVAL ;
/*
* Enable all IRQs
*/
axi_iowrite ( ADI_IRQ_MASK_OUT_ALL &
~ ( ADI_IRQ_SRC_NEW_MEASUR | ADI_IRQ_SRC_TACH_ERR |
ADI_IRQ_SRC_PWM_CHANGED | ADI_IRQ_SRC_TEMP_INCREASE ) ,
ADI_REG_IRQ_MASK , ctl ) ;
/* bring the device out of reset */
axi_iowrite ( 0x01 , ADI_REG_RSTN , ctl ) ;
return ret ;
}
static const struct hwmon_channel_info * axi_fan_control_info [ ] = {
HWMON_CHANNEL_INFO ( pwm , HWMON_PWM_INPUT ) ,
HWMON_CHANNEL_INFO ( fan , HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_LABEL ) ,
HWMON_CHANNEL_INFO ( temp , HWMON_T_INPUT | HWMON_T_LABEL ) ,
NULL
} ;
static const struct hwmon_ops axi_fan_control_hwmon_ops = {
. is_visible = axi_fan_control_is_visible ,
. read = axi_fan_control_read ,
. write = axi_fan_control_write ,
. read_string = axi_fan_control_read_labels ,
} ;
static const struct hwmon_chip_info axi_chip_info = {
. ops = & axi_fan_control_hwmon_ops ,
. info = axi_fan_control_info ,
} ;
2021-08-11 14:48:53 +03:00
/* temperature threshold below which PWM should be 0% */
static SENSOR_DEVICE_ATTR_RW ( pwm1_auto_point1_temp_hyst , axi_fan_control , ADI_REG_TEMP_00_H ) ;
/* temperature threshold above which PWM should be 25% */
static SENSOR_DEVICE_ATTR_RW ( pwm1_auto_point1_temp , axi_fan_control , ADI_REG_TEMP_25_L ) ;
/* temperature threshold below which PWM should be 25% */
static SENSOR_DEVICE_ATTR_RW ( pwm1_auto_point2_temp_hyst , axi_fan_control , ADI_REG_TEMP_25_H ) ;
/* temperature threshold above which PWM should be 50% */
static SENSOR_DEVICE_ATTR_RW ( pwm1_auto_point2_temp , axi_fan_control , ADI_REG_TEMP_50_L ) ;
/* temperature threshold below which PWM should be 50% */
static SENSOR_DEVICE_ATTR_RW ( pwm1_auto_point3_temp_hyst , axi_fan_control , ADI_REG_TEMP_50_H ) ;
/* temperature threshold above which PWM should be 75% */
static SENSOR_DEVICE_ATTR_RW ( pwm1_auto_point3_temp , axi_fan_control , ADI_REG_TEMP_75_L ) ;
/* temperature threshold below which PWM should be 75% */
static SENSOR_DEVICE_ATTR_RW ( pwm1_auto_point4_temp_hyst , axi_fan_control , ADI_REG_TEMP_75_H ) ;
/* temperature threshold above which PWM should be 100% */
static SENSOR_DEVICE_ATTR_RW ( pwm1_auto_point4_temp , axi_fan_control , ADI_REG_TEMP_100_L ) ;
static struct attribute * axi_fan_control_attrs [ ] = {
& sensor_dev_attr_pwm1_auto_point1_temp_hyst . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point1_temp . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point2_temp_hyst . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point2_temp . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point3_temp_hyst . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point3_temp . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point4_temp_hyst . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point4_temp . dev_attr . attr ,
NULL ,
} ;
ATTRIBUTE_GROUPS ( axi_fan_control ) ;
2019-10-09 13:28:05 +03:00
static const u32 version_1_0_0 = ADI_AXI_PCORE_VER ( 1 , 0 , ' a ' ) ;
static const struct of_device_id axi_fan_control_of_match [ ] = {
{ . compatible = " adi,axi-fan-control-1.00.a " ,
. data = ( void * ) & version_1_0_0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , axi_fan_control_of_match ) ;
static int axi_fan_control_probe ( struct platform_device * pdev )
{
struct axi_fan_control_data * ctl ;
struct clk * clk ;
const struct of_device_id * id ;
const char * name = " axi_fan_control " ;
u32 version ;
int ret ;
id = of_match_node ( axi_fan_control_of_match , pdev - > dev . of_node ) ;
if ( ! id )
return - EINVAL ;
ctl = devm_kzalloc ( & pdev - > dev , sizeof ( * ctl ) , GFP_KERNEL ) ;
if ( ! ctl )
return - ENOMEM ;
ctl - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( ctl - > base ) )
return PTR_ERR ( ctl - > base ) ;
2022-08-08 09:06:40 +03:00
clk = devm_clk_get_enabled ( & pdev - > dev , NULL ) ;
2019-10-09 13:28:05 +03:00
if ( IS_ERR ( clk ) ) {
dev_err ( & pdev - > dev , " clk_get failed with %ld \n " , PTR_ERR ( clk ) ) ;
return PTR_ERR ( clk ) ;
}
ctl - > clk_rate = clk_get_rate ( clk ) ;
if ( ! ctl - > clk_rate )
return - EINVAL ;
version = axi_ioread ( ADI_AXI_REG_VERSION , ctl ) ;
if ( ADI_AXI_PCORE_VER_MAJOR ( version ) ! =
ADI_AXI_PCORE_VER_MAJOR ( ( * ( u32 * ) id - > data ) ) ) {
dev_err ( & pdev - > dev , " Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c \n " ,
ADI_AXI_PCORE_VER_MAJOR ( ( * ( u32 * ) id - > data ) ) ,
ADI_AXI_PCORE_VER_MINOR ( ( * ( u32 * ) id - > data ) ) ,
ADI_AXI_PCORE_VER_PATCH ( ( * ( u32 * ) id - > data ) ) ,
ADI_AXI_PCORE_VER_MAJOR ( version ) ,
ADI_AXI_PCORE_VER_MINOR ( version ) ,
ADI_AXI_PCORE_VER_PATCH ( version ) ) ;
return - ENODEV ;
}
ctl - > irq = platform_get_irq ( pdev , 0 ) ;
if ( ctl - > irq < 0 )
return ctl - > irq ;
ret = devm_request_threaded_irq ( & pdev - > dev , ctl - > irq , NULL ,
axi_fan_control_irq_handler ,
IRQF_ONESHOT | IRQF_TRIGGER_HIGH ,
pdev - > driver_override , ctl ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to request an irq, %d " , ret ) ;
return ret ;
}
ret = axi_fan_control_init ( ctl , pdev - > dev . of_node ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to initialize device \n " ) ;
return ret ;
}
ctl - > hdev = devm_hwmon_device_register_with_info ( & pdev - > dev ,
name ,
ctl ,
& axi_chip_info ,
2021-08-11 14:48:53 +03:00
axi_fan_control_groups ) ;
2019-10-09 13:28:05 +03:00
return PTR_ERR_OR_ZERO ( ctl - > hdev ) ;
}
static struct platform_driver axi_fan_control_driver = {
. driver = {
. name = " axi_fan_control_driver " ,
. of_match_table = axi_fan_control_of_match ,
} ,
. probe = axi_fan_control_probe ,
} ;
module_platform_driver ( axi_fan_control_driver ) ;
MODULE_AUTHOR ( " Nuno Sa <nuno.sa@analog.com> " ) ;
MODULE_DESCRIPTION ( " Analog Devices Fan Control HDL CORE driver " ) ;
MODULE_LICENSE ( " GPL " ) ;