2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2014-07-16 19:46:42 +04:00
/*
* pwm - fan . c - Hwmon driver for fans connected to PWM lines .
*
* Copyright ( c ) 2014 Samsung Electronics Co . , Ltd .
*
* Author : Kamil Debski < k . debski @ samsung . com >
*/
# include <linux/hwmon.h>
2019-04-11 16:30:11 +03:00
# include <linux/interrupt.h>
2014-07-16 19:46:42 +04:00
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/pwm.h>
2019-02-22 16:45:24 +03:00
# include <linux/regulator/consumer.h>
2014-07-16 19:46:42 +04:00
# include <linux/sysfs.h>
2015-02-26 16:59:37 +03:00
# include <linux/thermal.h>
2019-04-11 16:30:11 +03:00
# include <linux/timer.h>
2014-07-16 19:46:42 +04:00
# define MAX_PWM 255
2020-12-12 22:50:07 +03:00
struct pwm_fan_tach {
int irq ;
atomic_t pulses ;
unsigned int rpm ;
u8 pulses_per_revolution ;
} ;
2022-09-14 18:31:37 +03:00
enum pwm_fan_enable_mode {
pwm_off_reg_off ,
pwm_disable_reg_enable ,
pwm_enable_reg_enable ,
pwm_disable_reg_disable ,
} ;
2014-07-16 19:46:42 +04:00
struct pwm_fan_ctx {
2022-09-14 18:31:34 +03:00
struct device * dev ;
2014-07-16 19:46:42 +04:00
struct mutex lock ;
struct pwm_device * pwm ;
2021-01-12 22:13:14 +03:00
struct pwm_state pwm_state ;
2019-02-22 16:45:24 +03:00
struct regulator * reg_en ;
2022-09-14 18:31:37 +03:00
enum pwm_fan_enable_mode enable_mode ;
2022-09-14 18:31:35 +03:00
bool regulator_enabled ;
2022-09-14 18:31:34 +03:00
bool enabled ;
2019-04-11 16:30:11 +03:00
2020-12-12 22:50:08 +03:00
int tach_count ;
struct pwm_fan_tach * tachs ;
2019-04-11 16:30:11 +03:00
ktime_t sample_start ;
struct timer_list rpm_timer ;
2015-02-26 16:59:36 +03:00
unsigned int pwm_value ;
unsigned int pwm_fan_state ;
unsigned int pwm_fan_max_state ;
unsigned int * pwm_fan_cooling_levels ;
2015-02-26 16:59:37 +03:00
struct thermal_cooling_device * cdev ;
2020-11-28 20:49:09 +03:00
struct hwmon_chip_info info ;
2020-12-12 22:50:08 +03:00
struct hwmon_channel_info fan_channel ;
2020-11-28 20:49:09 +03:00
} ;
2019-04-11 16:30:11 +03:00
/* This handler assumes self resetting edge triggered interrupt. */
static irqreturn_t pulse_handler ( int irq , void * dev_id )
{
2020-12-12 22:50:07 +03:00
struct pwm_fan_tach * tach = dev_id ;
2019-04-11 16:30:11 +03:00
2020-12-12 22:50:07 +03:00
atomic_inc ( & tach - > pulses ) ;
2019-04-11 16:30:11 +03:00
return IRQ_HANDLED ;
}
static void sample_timer ( struct timer_list * t )
{
struct pwm_fan_ctx * ctx = from_timer ( ctx , t , rpm_timer ) ;
2020-11-11 19:46:43 +03:00
unsigned int delta = ktime_ms_delta ( ktime_get ( ) , ctx - > sample_start ) ;
2020-12-12 22:50:08 +03:00
int i ;
2019-04-11 16:30:11 +03:00
2020-11-11 19:46:43 +03:00
if ( delta ) {
2020-12-12 22:50:08 +03:00
for ( i = 0 ; i < ctx - > tach_count ; i + + ) {
struct pwm_fan_tach * tach = & ctx - > tachs [ i ] ;
int pulses ;
pulses = atomic_read ( & tach - > pulses ) ;
atomic_sub ( pulses , & tach - > pulses ) ;
tach - > rpm = ( unsigned int ) ( pulses * 1000 * 60 ) /
( tach - > pulses_per_revolution * delta ) ;
}
2020-11-11 19:46:43 +03:00
ctx - > sample_start = ktime_get ( ) ;
}
2019-04-11 16:30:11 +03:00
mod_timer ( & ctx - > rpm_timer , jiffies + HZ ) ;
}
2022-09-14 18:31:37 +03:00
static void pwm_fan_enable_mode_2_state ( int enable_mode ,
struct pwm_state * state ,
bool * enable_regulator )
{
switch ( enable_mode ) {
case pwm_disable_reg_enable :
/* disable pwm, keep regulator enabled */
state - > enabled = false ;
* enable_regulator = true ;
break ;
case pwm_enable_reg_enable :
/* keep pwm and regulator enabled */
state - > enabled = true ;
* enable_regulator = true ;
break ;
case pwm_off_reg_off :
case pwm_disable_reg_disable :
/* disable pwm and regulator */
state - > enabled = false ;
* enable_regulator = false ;
}
}
2022-09-14 18:31:35 +03:00
static int pwm_fan_switch_power ( struct pwm_fan_ctx * ctx , bool on )
{
int ret = 0 ;
if ( ! ctx - > reg_en )
return ret ;
if ( ! ctx - > regulator_enabled & & on ) {
ret = regulator_enable ( ctx - > reg_en ) ;
if ( ret = = 0 )
ctx - > regulator_enabled = true ;
} else if ( ctx - > regulator_enabled & & ! on ) {
ret = regulator_disable ( ctx - > reg_en ) ;
if ( ret = = 0 )
ctx - > regulator_enabled = false ;
}
return ret ;
}
2022-09-14 18:31:33 +03:00
static int pwm_fan_power_on ( struct pwm_fan_ctx * ctx )
2014-07-16 19:46:42 +04:00
{
2022-09-14 18:31:33 +03:00
struct pwm_state * state = & ctx - > pwm_state ;
int ret ;
2022-09-14 18:31:34 +03:00
if ( ctx - > enabled )
return 0 ;
2022-09-14 18:31:37 +03:00
ret = pwm_fan_switch_power ( ctx , true ) ;
if ( ret < 0 ) {
dev_err ( ctx - > dev , " failed to enable power supply \n " ) ;
return ret ;
}
2022-09-14 18:31:33 +03:00
state - > enabled = true ;
ret = pwm_apply_state ( ctx - > pwm , state ) ;
2022-09-14 18:31:34 +03:00
if ( ret ) {
dev_err ( ctx - > dev , " failed to enable PWM \n " ) ;
2022-09-14 18:31:37 +03:00
goto disable_regulator ;
2022-09-14 18:31:34 +03:00
}
ctx - > enabled = true ;
2022-09-14 18:31:33 +03:00
2022-09-14 18:31:37 +03:00
return 0 ;
disable_regulator :
pwm_fan_switch_power ( ctx , false ) ;
2022-09-14 18:31:33 +03:00
return ret ;
}
static int pwm_fan_power_off ( struct pwm_fan_ctx * ctx )
{
2021-01-12 22:13:14 +03:00
struct pwm_state * state = & ctx - > pwm_state ;
2022-09-14 18:31:37 +03:00
bool enable_regulator = false ;
2022-09-14 18:31:34 +03:00
int ret ;
if ( ! ctx - > enabled )
return 0 ;
2016-04-14 22:17:24 +03:00
2022-09-14 18:31:37 +03:00
pwm_fan_enable_mode_2_state ( ctx - > enable_mode ,
state ,
& enable_regulator ) ;
2022-09-14 18:31:33 +03:00
state - > enabled = false ;
state - > duty_cycle = 0 ;
2022-09-14 18:31:34 +03:00
ret = pwm_apply_state ( ctx - > pwm , state ) ;
if ( ret ) {
dev_err ( ctx - > dev , " failed to disable PWM \n " ) ;
return ret ;
}
2022-09-14 18:31:37 +03:00
pwm_fan_switch_power ( ctx , enable_regulator ) ;
2022-09-14 18:31:34 +03:00
ctx - > enabled = false ;
2022-09-14 18:31:33 +03:00
return 0 ;
}
static int __set_pwm ( struct pwm_fan_ctx * ctx , unsigned long pwm )
{
2022-09-14 18:31:34 +03:00
struct pwm_state * state = & ctx - > pwm_state ;
unsigned long period ;
2022-09-14 18:31:33 +03:00
int ret = 0 ;
2022-09-14 18:31:34 +03:00
if ( pwm > 0 ) {
2022-09-14 18:31:37 +03:00
if ( ctx - > enable_mode = = pwm_off_reg_off )
/* pwm-fan hard disabled */
return 0 ;
2022-09-14 18:31:34 +03:00
period = state - > period ;
state - > duty_cycle = DIV_ROUND_UP ( pwm * ( period - 1 ) , MAX_PWM ) ;
ret = pwm_apply_state ( ctx - > pwm , state ) ;
if ( ret )
2022-09-14 18:31:36 +03:00
return ret ;
2022-09-14 18:31:33 +03:00
ret = pwm_fan_power_on ( ctx ) ;
2022-09-14 18:31:34 +03:00
} else {
2022-09-14 18:31:33 +03:00
ret = pwm_fan_power_off ( ctx ) ;
2022-09-14 18:31:34 +03:00
}
2017-04-24 16:13:17 +03:00
if ( ! ret )
ctx - > pwm_value = pwm ;
2022-09-14 18:31:33 +03:00
2022-09-14 18:31:36 +03:00
return ret ;
}
static int set_pwm ( struct pwm_fan_ctx * ctx , unsigned long pwm )
{
int ret ;
mutex_lock ( & ctx - > lock ) ;
ret = __set_pwm ( ctx , pwm ) ;
2014-07-16 19:46:42 +04:00
mutex_unlock ( & ctx - > lock ) ;
2022-09-14 18:31:36 +03:00
2014-07-16 19:46:42 +04:00
return ret ;
}
2015-02-26 16:59:37 +03:00
static void pwm_fan_update_state ( struct pwm_fan_ctx * ctx , unsigned long pwm )
{
int i ;
for ( i = 0 ; i < ctx - > pwm_fan_max_state ; + + i )
if ( pwm < ctx - > pwm_fan_cooling_levels [ i + 1 ] )
break ;
ctx - > pwm_fan_state = i ;
}
2022-09-14 18:31:37 +03:00
static int pwm_fan_update_enable ( struct pwm_fan_ctx * ctx , long val )
{
int ret = 0 ;
int old_val ;
mutex_lock ( & ctx - > lock ) ;
if ( ctx - > enable_mode = = val )
goto out ;
old_val = ctx - > enable_mode ;
ctx - > enable_mode = val ;
if ( val = = 0 ) {
/* Disable pwm-fan unconditionally */
ret = __set_pwm ( ctx , 0 ) ;
if ( ret )
ctx - > enable_mode = old_val ;
pwm_fan_update_state ( ctx , 0 ) ;
} else {
/*
* Change PWM and / or regulator state if currently disabled
* Nothing to do if currently enabled
*/
if ( ! ctx - > enabled ) {
struct pwm_state * state = & ctx - > pwm_state ;
bool enable_regulator = false ;
state - > duty_cycle = 0 ;
pwm_fan_enable_mode_2_state ( val ,
state ,
& enable_regulator ) ;
pwm_apply_state ( ctx - > pwm , state ) ;
pwm_fan_switch_power ( ctx , enable_regulator ) ;
pwm_fan_update_state ( ctx , 0 ) ;
}
}
out :
mutex_unlock ( & ctx - > lock ) ;
return ret ;
}
2020-11-28 20:49:09 +03:00
static int pwm_fan_write ( struct device * dev , enum hwmon_sensor_types type ,
u32 attr , int channel , long val )
2015-02-26 16:59:35 +03:00
{
struct pwm_fan_ctx * ctx = dev_get_drvdata ( dev ) ;
int ret ;
2022-09-14 18:31:37 +03:00
switch ( attr ) {
case hwmon_pwm_input :
if ( val < 0 | | val > MAX_PWM )
return - EINVAL ;
ret = set_pwm ( ctx , val ) ;
if ( ret )
return ret ;
pwm_fan_update_state ( ctx , val ) ;
break ;
case hwmon_pwm_enable :
if ( val < 0 | | val > 3 )
ret = - EINVAL ;
else
ret = pwm_fan_update_enable ( ctx , val ) ;
2015-02-26 16:59:35 +03:00
return ret ;
2022-09-14 18:31:37 +03:00
default :
return - EOPNOTSUPP ;
}
2015-02-26 16:59:35 +03:00
2020-11-28 20:49:09 +03:00
return 0 ;
2015-02-26 16:59:35 +03:00
}
2020-11-28 20:49:09 +03:00
static int pwm_fan_read ( struct device * dev , enum hwmon_sensor_types type ,
u32 attr , int channel , long * val )
2014-07-16 19:46:42 +04:00
{
struct pwm_fan_ctx * ctx = dev_get_drvdata ( dev ) ;
2020-11-28 20:49:09 +03:00
switch ( type ) {
case hwmon_pwm :
2022-09-14 18:31:37 +03:00
switch ( attr ) {
case hwmon_pwm_input :
* val = ctx - > pwm_value ;
return 0 ;
case hwmon_pwm_enable :
* val = ctx - > enable_mode ;
return 0 ;
}
return - EOPNOTSUPP ;
2020-11-28 20:49:09 +03:00
case hwmon_fan :
2020-12-12 22:50:08 +03:00
* val = ctx - > tachs [ channel ] . rpm ;
2020-11-28 20:49:09 +03:00
return 0 ;
2019-04-11 16:30:11 +03:00
2020-11-28 20:49:09 +03:00
default :
return - ENOTSUPP ;
}
2019-04-11 16:30:11 +03:00
}
2014-07-16 19:46:42 +04:00
2020-11-28 20:49:09 +03:00
static umode_t pwm_fan_is_visible ( const void * data ,
enum hwmon_sensor_types type ,
u32 attr , int channel )
2019-04-11 16:30:11 +03:00
{
2020-11-28 20:49:09 +03:00
switch ( type ) {
case hwmon_pwm :
return 0644 ;
2019-04-11 16:30:11 +03:00
2020-11-28 20:49:09 +03:00
case hwmon_fan :
return 0444 ;
2019-04-11 16:30:11 +03:00
2020-11-28 20:49:09 +03:00
default :
return 0 ;
}
2019-04-11 16:30:11 +03:00
}
2020-11-28 20:49:09 +03:00
static const struct hwmon_ops pwm_fan_hwmon_ops = {
. is_visible = pwm_fan_is_visible ,
. read = pwm_fan_read ,
. write = pwm_fan_write ,
2019-04-11 16:30:11 +03:00
} ;
2014-07-16 19:46:42 +04:00
2015-02-26 16:59:37 +03:00
/* thermal cooling device callbacks */
static int pwm_fan_get_max_state ( struct thermal_cooling_device * cdev ,
unsigned long * state )
{
struct pwm_fan_ctx * ctx = cdev - > devdata ;
if ( ! ctx )
return - EINVAL ;
* state = ctx - > pwm_fan_max_state ;
return 0 ;
}
static int pwm_fan_get_cur_state ( struct thermal_cooling_device * cdev ,
unsigned long * state )
{
struct pwm_fan_ctx * ctx = cdev - > devdata ;
if ( ! ctx )
return - EINVAL ;
* state = ctx - > pwm_fan_state ;
return 0 ;
}
static int
pwm_fan_set_cur_state ( struct thermal_cooling_device * cdev , unsigned long state )
{
struct pwm_fan_ctx * ctx = cdev - > devdata ;
int ret ;
if ( ! ctx | | ( state > ctx - > pwm_fan_max_state ) )
return - EINVAL ;
if ( state = = ctx - > pwm_fan_state )
return 0 ;
2022-09-14 18:31:36 +03:00
ret = set_pwm ( ctx , ctx - > pwm_fan_cooling_levels [ state ] ) ;
2015-02-26 16:59:37 +03:00
if ( ret ) {
dev_err ( & cdev - > device , " Cannot set pwm! \n " ) ;
return ret ;
}
ctx - > pwm_fan_state = state ;
return ret ;
}
static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = {
. get_max_state = pwm_fan_get_max_state ,
. get_cur_state = pwm_fan_get_cur_state ,
. set_cur_state = pwm_fan_set_cur_state ,
} ;
2015-03-04 20:51:05 +03:00
static int pwm_fan_of_get_cooling_data ( struct device * dev ,
struct pwm_fan_ctx * ctx )
2015-02-26 16:59:36 +03:00
{
struct device_node * np = dev - > of_node ;
int num , i , ret ;
if ( ! of_find_property ( np , " cooling-levels " , NULL ) )
return 0 ;
ret = of_property_count_u32_elems ( np , " cooling-levels " ) ;
if ( ret < = 0 ) {
dev_err ( dev , " Wrong data! \n " ) ;
return ret ? : - EINVAL ;
}
num = ret ;
treewide: devm_kzalloc() -> devm_kcalloc()
The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc().
This patch replaces cases of:
devm_kzalloc(handle, a * b, gfp)
with:
devm_kcalloc(handle, a * b, gfp)
as well as handling cases of:
devm_kzalloc(handle, a * b * c, gfp)
with:
devm_kzalloc(handle, array3_size(a, b, c), gfp)
as it's slightly less ugly than:
devm_kcalloc(handle, array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
devm_kzalloc(handle, 4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
Some manual whitespace fixes were needed in this patch, as Coccinelle
really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...".
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
expression HANDLE;
type TYPE;
expression THING, E;
@@
(
devm_kzalloc(HANDLE,
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
devm_kzalloc(HANDLE,
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression HANDLE;
expression COUNT;
typedef u8;
typedef __u8;
@@
(
devm_kzalloc(HANDLE,
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
expression HANDLE;
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
expression HANDLE;
identifier SIZE, COUNT;
@@
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression HANDLE;
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression HANDLE;
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
expression HANDLE;
identifier STRIDE, SIZE, COUNT;
@@
(
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression HANDLE;
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE,
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression HANDLE;
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, sizeof(THING) * C2, ...)
|
devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...)
|
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE, C1 * C2, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * E2
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * (E2)
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 00:07:58 +03:00
ctx - > pwm_fan_cooling_levels = devm_kcalloc ( dev , num , sizeof ( u32 ) ,
2015-02-26 16:59:36 +03:00
GFP_KERNEL ) ;
if ( ! ctx - > pwm_fan_cooling_levels )
return - ENOMEM ;
ret = of_property_read_u32_array ( np , " cooling-levels " ,
ctx - > pwm_fan_cooling_levels , num ) ;
if ( ret ) {
dev_err ( dev , " Property 'cooling-levels' cannot be read! \n " ) ;
return ret ;
}
for ( i = 0 ; i < num ; i + + ) {
if ( ctx - > pwm_fan_cooling_levels [ i ] > MAX_PWM ) {
dev_err ( dev , " PWM fan state[%d]:%d > %d \n " , i ,
ctx - > pwm_fan_cooling_levels [ i ] , MAX_PWM ) ;
return - EINVAL ;
}
}
ctx - > pwm_fan_max_state = num - 1 ;
return 0 ;
}
2022-09-14 18:31:37 +03:00
static void pwm_fan_cleanup ( void * __ctx )
2019-04-18 22:58:20 +03:00
{
2019-05-16 17:56:57 +03:00
struct pwm_fan_ctx * ctx = __ctx ;
2021-01-12 22:13:14 +03:00
2019-05-16 17:56:57 +03:00
del_timer_sync ( & ctx - > rpm_timer ) ;
2022-09-14 18:31:37 +03:00
/* Switch off everything */
ctx - > enable_mode = pwm_disable_reg_disable ;
pwm_fan_power_off ( ctx ) ;
2019-04-18 22:58:20 +03:00
}
2014-07-16 19:46:42 +04:00
static int pwm_fan_probe ( struct platform_device * pdev )
{
2015-02-26 16:59:37 +03:00
struct thermal_cooling_device * cdev ;
2019-04-18 22:58:20 +03:00
struct device * dev = & pdev - > dev ;
2014-07-16 19:46:42 +04:00
struct pwm_fan_ctx * ctx ;
2015-02-26 16:59:37 +03:00
struct device * hwmon ;
2014-07-16 19:46:42 +04:00
int ret ;
2020-11-28 20:49:09 +03:00
const struct hwmon_channel_info * * channels ;
2020-12-12 22:50:08 +03:00
u32 * fan_channel_config ;
int channel_count = 1 ; /* We always have a PWM channel. */
int i ;
2014-07-16 19:46:42 +04:00
2019-04-18 22:58:20 +03:00
ctx = devm_kzalloc ( dev , sizeof ( * ctx ) , GFP_KERNEL ) ;
2014-07-16 19:46:42 +04:00
if ( ! ctx )
return - ENOMEM ;
mutex_init ( & ctx - > lock ) ;
2022-09-14 18:31:34 +03:00
ctx - > dev = & pdev - > dev ;
2022-08-26 20:26:40 +03:00
ctx - > pwm = devm_pwm_get ( dev , NULL ) ;
2020-08-17 10:34:33 +03:00
if ( IS_ERR ( ctx - > pwm ) )
return dev_err_probe ( dev , PTR_ERR ( ctx - > pwm ) , " Could not get PWM \n " ) ;
2014-07-16 19:46:42 +04:00
platform_set_drvdata ( pdev , ctx ) ;
2019-04-18 22:58:20 +03:00
ctx - > reg_en = devm_regulator_get_optional ( dev , " fan " ) ;
2019-02-22 16:45:24 +03:00
if ( IS_ERR ( ctx - > reg_en ) ) {
if ( PTR_ERR ( ctx - > reg_en ) ! = - ENODEV )
return PTR_ERR ( ctx - > reg_en ) ;
ctx - > reg_en = NULL ;
}
2021-01-12 22:13:14 +03:00
pwm_init_state ( ctx - > pwm , & ctx - > pwm_state ) ;
2020-12-15 12:20:30 +03:00
/*
2022-09-14 18:31:36 +03:00
* set_pwm assumes that MAX_PWM * ( period - 1 ) fits into an unsigned
2020-12-15 12:20:30 +03:00
* long . Check this here to prevent the fan running at a too low
* frequency .
*/
2021-01-12 22:13:14 +03:00
if ( ctx - > pwm_state . period > ULONG_MAX / MAX_PWM + 1 ) {
2020-12-15 12:20:30 +03:00
dev_err ( dev , " Configured period too big \n " ) ;
return - EINVAL ;
}
2022-09-14 18:31:37 +03:00
ctx - > enable_mode = pwm_disable_reg_enable ;
/*
* Set duty cycle to maximum allowed and enable PWM output as well as
* the regulator . In case of error nothing is changed
*/
2022-09-14 18:31:36 +03:00
ret = set_pwm ( ctx , MAX_PWM ) ;
2014-07-16 19:46:42 +04:00
if ( ret ) {
2019-05-16 17:56:57 +03:00
dev_err ( dev , " Failed to configure PWM: %d \n " , ret ) ;
2019-04-18 22:58:20 +03:00
return ret ;
2014-07-16 19:46:42 +04:00
}
2019-04-11 16:30:11 +03:00
timer_setup ( & ctx - > rpm_timer , sample_timer , 0 ) ;
2022-09-14 18:31:37 +03:00
ret = devm_add_action_or_reset ( dev , pwm_fan_cleanup , ctx ) ;
2019-06-07 23:27:06 +03:00
if ( ret )
return ret ;
2019-04-11 16:30:11 +03:00
2020-12-12 22:50:08 +03:00
ctx - > tach_count = platform_irq_count ( pdev ) ;
if ( ctx - > tach_count < 0 )
return dev_err_probe ( dev , ctx - > tach_count ,
2020-11-26 20:44:07 +03:00
" Could not get number of fan tachometer inputs \n " ) ;
2020-12-12 22:50:08 +03:00
dev_dbg ( dev , " %d fan tachometer inputs \n " , ctx - > tach_count ) ;
2020-11-26 20:44:07 +03:00
2020-12-12 22:50:08 +03:00
if ( ctx - > tach_count ) {
channel_count + + ; /* We also have a FAN channel. */
ctx - > tachs = devm_kcalloc ( dev , ctx - > tach_count ,
sizeof ( struct pwm_fan_tach ) ,
GFP_KERNEL ) ;
if ( ! ctx - > tachs )
return - ENOMEM ;
ctx - > fan_channel . type = hwmon_fan ;
fan_channel_config = devm_kcalloc ( dev , ctx - > tach_count + 1 ,
sizeof ( u32 ) , GFP_KERNEL ) ;
if ( ! fan_channel_config )
return - ENOMEM ;
ctx - > fan_channel . config = fan_channel_config ;
}
channels = devm_kcalloc ( dev , channel_count + 1 ,
2020-11-28 20:49:09 +03:00
sizeof ( struct hwmon_channel_info * ) , GFP_KERNEL ) ;
if ( ! channels )
return - ENOMEM ;
2022-09-14 18:31:37 +03:00
channels [ 0 ] = HWMON_CHANNEL_INFO ( pwm , HWMON_PWM_INPUT | HWMON_PWM_ENABLE ) ;
2020-11-28 20:49:09 +03:00
2020-12-12 22:50:08 +03:00
for ( i = 0 ; i < ctx - > tach_count ; i + + ) {
struct pwm_fan_tach * tach = & ctx - > tachs [ i ] ;
2020-11-26 20:44:07 +03:00
u32 ppr = 2 ;
2020-12-12 22:50:08 +03:00
tach - > irq = platform_get_irq ( pdev , i ) ;
2020-12-12 22:50:07 +03:00
if ( tach - > irq = = - EPROBE_DEFER )
return tach - > irq ;
if ( tach - > irq > 0 ) {
ret = devm_request_irq ( dev , tach - > irq , pulse_handler , 0 ,
pdev - > name , tach ) ;
2020-11-26 20:44:07 +03:00
if ( ret ) {
dev_err ( dev ,
" Failed to request interrupt: %d \n " ,
ret ) ;
return ret ;
}
}
2019-04-11 16:30:11 +03:00
2020-12-12 22:50:08 +03:00
of_property_read_u32_index ( dev - > of_node ,
" pulses-per-revolution " ,
i ,
& ppr ) ;
2020-12-12 22:50:07 +03:00
tach - > pulses_per_revolution = ppr ;
if ( ! tach - > pulses_per_revolution ) {
2020-11-26 20:44:07 +03:00
dev_err ( dev , " pulses-per-revolution can't be zero. \n " ) ;
return - EINVAL ;
2019-04-11 16:30:11 +03:00
}
2020-11-26 20:44:07 +03:00
2020-12-12 22:50:08 +03:00
fan_channel_config [ i ] = HWMON_F_INPUT ;
dev_dbg ( dev , " tach%d: irq=%d, pulses_per_revolution=%d \n " ,
i , tach - > irq , tach - > pulses_per_revolution ) ;
}
2020-11-26 20:44:07 +03:00
2020-12-12 22:50:08 +03:00
if ( ctx - > tach_count > 0 ) {
2019-04-11 16:30:11 +03:00
ctx - > sample_start = ktime_get ( ) ;
mod_timer ( & ctx - > rpm_timer , jiffies + HZ ) ;
2020-11-28 20:49:09 +03:00
2020-12-12 22:50:08 +03:00
channels [ 1 ] = & ctx - > fan_channel ;
2019-04-11 16:30:11 +03:00
}
2020-11-28 20:49:09 +03:00
ctx - > info . ops = & pwm_fan_hwmon_ops ;
ctx - > info . info = channels ;
hwmon = devm_hwmon_device_register_with_info ( dev , " pwmfan " ,
ctx , & ctx - > info , NULL ) ;
2014-07-16 19:46:42 +04:00
if ( IS_ERR ( hwmon ) ) {
2019-04-18 22:58:20 +03:00
dev_err ( dev , " Failed to register hwmon device \n " ) ;
return PTR_ERR ( hwmon ) ;
2014-07-16 19:46:42 +04:00
}
2015-02-26 16:59:36 +03:00
2019-04-18 22:58:20 +03:00
ret = pwm_fan_of_get_cooling_data ( dev , ctx ) ;
2015-02-26 16:59:36 +03:00
if ( ret )
return ret ;
2015-02-26 16:59:37 +03:00
ctx - > pwm_fan_state = ctx - > pwm_fan_max_state ;
if ( IS_ENABLED ( CONFIG_THERMAL ) ) {
2019-04-18 22:58:20 +03:00
cdev = devm_thermal_of_cooling_device_register ( dev ,
dev - > of_node , " pwm-fan " , ctx , & pwm_fan_cooling_ops ) ;
2015-02-26 16:59:37 +03:00
if ( IS_ERR ( cdev ) ) {
2017-04-24 16:13:17 +03:00
ret = PTR_ERR ( cdev ) ;
2019-04-18 22:58:20 +03:00
dev_err ( dev ,
2019-04-12 17:05:23 +03:00
" Failed to register pwm-fan as cooling device: %d \n " ,
ret ) ;
2019-05-16 17:56:57 +03:00
return ret ;
2015-02-26 16:59:37 +03:00
}
ctx - > cdev = cdev ;
}
2014-07-16 19:46:42 +04:00
return 0 ;
}
2020-01-20 18:32:24 +03:00
static void pwm_fan_shutdown ( struct platform_device * pdev )
{
2022-09-14 18:31:37 +03:00
struct pwm_fan_ctx * ctx = platform_get_drvdata ( pdev ) ;
pwm_fan_cleanup ( ctx ) ;
2020-01-20 18:32:24 +03:00
}
static int pwm_fan_suspend ( struct device * dev )
{
2022-09-14 18:31:37 +03:00
struct pwm_fan_ctx * ctx = dev_get_drvdata ( dev ) ;
return pwm_fan_power_off ( ctx ) ;
2020-01-20 18:32:24 +03:00
}
2014-07-16 19:46:42 +04:00
static int pwm_fan_resume ( struct device * dev )
{
struct pwm_fan_ctx * ctx = dev_get_drvdata ( dev ) ;
2014-11-03 17:42:55 +03:00
2022-09-14 18:31:37 +03:00
return set_pwm ( ctx , ctx - > pwm_value ) ;
2014-07-16 19:46:42 +04:00
}
2022-09-25 20:27:54 +03:00
static DEFINE_SIMPLE_DEV_PM_OPS ( pwm_fan_pm , pwm_fan_suspend , pwm_fan_resume ) ;
2014-07-16 19:46:42 +04:00
2015-03-16 22:54:36 +03:00
static const struct of_device_id of_pwm_fan_match [ ] = {
2014-07-16 19:46:42 +04:00
{ . compatible = " pwm-fan " , } ,
{ } ,
} ;
2015-09-17 19:09:55 +03:00
MODULE_DEVICE_TABLE ( of , of_pwm_fan_match ) ;
2014-07-16 19:46:42 +04:00
static struct platform_driver pwm_fan_driver = {
. probe = pwm_fan_probe ,
2020-01-20 18:32:24 +03:00
. shutdown = pwm_fan_shutdown ,
2014-07-16 19:46:42 +04:00
. driver = {
. name = " pwm-fan " ,
2022-09-25 20:27:54 +03:00
. pm = pm_sleep_ptr ( & pwm_fan_pm ) ,
2014-07-16 19:46:42 +04:00
. of_match_table = of_pwm_fan_match ,
} ,
} ;
module_platform_driver ( pwm_fan_driver ) ;
MODULE_AUTHOR ( " Kamil Debski <k.debski@samsung.com> " ) ;
MODULE_ALIAS ( " platform:pwm-fan " ) ;
MODULE_DESCRIPTION ( " PWM FAN driver " ) ;
MODULE_LICENSE ( " GPL " ) ;