2010-09-16 15:39:49 +10:00
/*
* Copyright 2010 Red Hat Inc .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE COPYRIGHT HOLDER ( S ) OR AUTHOR ( S ) BE LIABLE FOR ANY CLAIM , DAMAGES OR
* OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE ,
* ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE .
*
* Authors : Ben Skeggs
*/
2010-10-12 12:31:32 +10:00
# ifdef CONFIG_ACPI
# include <linux/acpi.h>
# endif
# include <linux/power_supply.h>
2010-09-22 20:54:22 +02:00
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
2016-05-20 09:22:55 +10:00
# include "nouveau_drv.h"
2013-10-15 09:44:02 +10:00
# include "nouveau_hwmon.h"
2011-09-17 01:42:12 +10:00
2016-02-18 20:10:19 +01:00
# include <nvkm/subdev/iccsense.h>
2015-09-20 14:40:27 +02:00
# include <nvkm/subdev/volt.h>
2011-07-03 19:54:28 +01:00
# if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
2010-09-22 20:54:22 +02:00
2012-11-20 01:14:13 +01:00
static ssize_t
nouveau_hwmon_show_temp1_auto_point1_pwm ( struct device * d ,
struct device_attribute * a , char * buf )
{
return snprintf ( buf , PAGE_SIZE , " %d \n " , 100 ) ;
}
2017-05-18 23:24:38 +02:00
static SENSOR_DEVICE_ATTR ( temp1_auto_point1_pwm , 0444 ,
2012-11-20 01:14:13 +01:00
nouveau_hwmon_show_temp1_auto_point1_pwm , NULL , 0 ) ;
static ssize_t
nouveau_hwmon_temp1_auto_point1_temp ( struct device * d ,
struct device_attribute * a , char * buf )
{
struct drm_device * dev = dev_get_drvdata ( d ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2016-05-18 13:57:42 +10:00
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
2012-11-20 01:14:13 +01:00
return snprintf ( buf , PAGE_SIZE , " %d \n " ,
2015-01-14 15:11:48 +10:00
therm - > attr_get ( therm , NVKM_THERM_ATTR_THRS_FAN_BOOST ) * 1000 ) ;
2012-11-20 01:14:13 +01:00
}
static ssize_t
nouveau_hwmon_set_temp1_auto_point1_temp ( struct device * d ,
struct device_attribute * a ,
const char * buf , size_t count )
{
struct drm_device * dev = dev_get_drvdata ( d ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2016-05-18 13:57:42 +10:00
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
2012-11-20 01:14:13 +01:00
long value ;
2018-07-11 11:02:28 +03:00
if ( kstrtol ( buf , 10 , & value ) )
return - EINVAL ;
2012-11-20 01:14:13 +01:00
2015-01-14 15:11:48 +10:00
therm - > attr_set ( therm , NVKM_THERM_ATTR_THRS_FAN_BOOST ,
2012-11-20 01:14:13 +01:00
value / 1000 ) ;
return count ;
}
2017-05-18 23:24:38 +02:00
static SENSOR_DEVICE_ATTR ( temp1_auto_point1_temp , 0644 ,
2012-11-20 01:14:13 +01:00
nouveau_hwmon_temp1_auto_point1_temp ,
nouveau_hwmon_set_temp1_auto_point1_temp , 0 ) ;
static ssize_t
nouveau_hwmon_temp1_auto_point1_temp_hyst ( struct device * d ,
struct device_attribute * a , char * buf )
{
struct drm_device * dev = dev_get_drvdata ( d ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2016-05-18 13:57:42 +10:00
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
2012-11-20 01:14:13 +01:00
return snprintf ( buf , PAGE_SIZE , " %d \n " ,
2015-01-14 15:11:48 +10:00
therm - > attr_get ( therm , NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST ) * 1000 ) ;
2012-11-20 01:14:13 +01:00
}
static ssize_t
nouveau_hwmon_set_temp1_auto_point1_temp_hyst ( struct device * d ,
struct device_attribute * a ,
const char * buf , size_t count )
{
struct drm_device * dev = dev_get_drvdata ( d ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2016-05-18 13:57:42 +10:00
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
2012-11-20 01:14:13 +01:00
long value ;
2018-07-11 11:02:28 +03:00
if ( kstrtol ( buf , 10 , & value ) )
return - EINVAL ;
2012-11-20 01:14:13 +01:00
2015-01-14 15:11:48 +10:00
therm - > attr_set ( therm , NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST ,
2012-11-20 01:14:13 +01:00
value / 1000 ) ;
return count ;
}
2017-05-18 23:24:38 +02:00
static SENSOR_DEVICE_ATTR ( temp1_auto_point1_temp_hyst , 0644 ,
2012-11-20 01:14:13 +01:00
nouveau_hwmon_temp1_auto_point1_temp_hyst ,
nouveau_hwmon_set_temp1_auto_point1_temp_hyst , 0 ) ;
2010-09-22 20:54:22 +02:00
static ssize_t
2017-05-18 23:24:36 +02:00
nouveau_hwmon_get_pwm1_max ( struct device * d ,
2012-09-04 13:52:00 +02:00
struct device_attribute * a , char * buf )
{
struct drm_device * dev = dev_get_drvdata ( d ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2016-05-18 13:57:42 +10:00
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
2011-11-21 16:41:48 +10:00
int ret ;
2011-08-15 11:10:30 +10:00
2017-05-18 23:24:36 +02:00
ret = therm - > attr_get ( therm , NVKM_THERM_ATTR_FAN_MAX_DUTY ) ;
2011-08-15 11:10:30 +10:00
if ( ret < 0 )
return ret ;
return sprintf ( buf , " %i \n " , ret ) ;
}
static ssize_t
2012-09-04 13:39:40 +02:00
nouveau_hwmon_get_pwm1_min ( struct device * d ,
2011-08-15 11:10:30 +10:00
struct device_attribute * a , char * buf )
{
struct drm_device * dev = dev_get_drvdata ( d ) ;
2012-09-02 02:55:58 +02:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2016-05-18 13:57:42 +10:00
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
2012-09-02 02:55:58 +02:00
int ret ;
2015-01-14 15:11:48 +10:00
ret = therm - > attr_get ( therm , NVKM_THERM_ATTR_FAN_MIN_DUTY ) ;
2012-09-02 02:55:58 +02:00
if ( ret < 0 )
return ret ;
2011-08-15 11:10:30 +10:00
2012-09-02 02:55:58 +02:00
return sprintf ( buf , " %i \n " , ret ) ;
2011-08-15 11:10:30 +10:00
}
static ssize_t
2012-09-04 13:39:40 +02:00
nouveau_hwmon_set_pwm1_min ( struct device * d , struct device_attribute * a ,
2011-08-15 11:10:30 +10:00
const char * buf , size_t count )
{
struct drm_device * dev = dev_get_drvdata ( d ) ;
2012-09-02 02:55:58 +02:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2016-05-18 13:57:42 +10:00
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
2011-08-15 11:10:30 +10:00
long value ;
2012-09-02 02:55:58 +02:00
int ret ;
2011-08-15 11:10:30 +10:00
2018-07-11 11:02:28 +03:00
if ( kstrtol ( buf , 10 , & value ) )
2011-08-15 11:10:30 +10:00
return - EINVAL ;
2015-01-14 15:11:48 +10:00
ret = therm - > attr_set ( therm , NVKM_THERM_ATTR_FAN_MIN_DUTY , value ) ;
2012-09-02 02:55:58 +02:00
if ( ret < 0 )
return ret ;
2011-08-15 11:10:30 +10:00
return count ;
}
2017-05-18 23:24:38 +02:00
static SENSOR_DEVICE_ATTR ( pwm1_min , 0644 ,
2012-09-04 13:39:40 +02:00
nouveau_hwmon_get_pwm1_min ,
nouveau_hwmon_set_pwm1_min , 0 ) ;
2011-08-15 11:10:30 +10:00
static ssize_t
2012-09-04 13:39:40 +02:00
nouveau_hwmon_set_pwm1_max ( struct device * d , struct device_attribute * a ,
2011-08-15 11:10:30 +10:00
const char * buf , size_t count )
{
struct drm_device * dev = dev_get_drvdata ( d ) ;
2012-09-02 02:55:58 +02:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2016-05-18 13:57:42 +10:00
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
2011-08-15 11:10:30 +10:00
long value ;
2012-09-02 02:55:58 +02:00
int ret ;
2011-08-15 11:10:30 +10:00
2018-07-11 11:02:28 +03:00
if ( kstrtol ( buf , 10 , & value ) )
2011-08-15 11:10:30 +10:00
return - EINVAL ;
2015-01-14 15:11:48 +10:00
ret = therm - > attr_set ( therm , NVKM_THERM_ATTR_FAN_MAX_DUTY , value ) ;
2012-09-02 02:55:58 +02:00
if ( ret < 0 )
return ret ;
2011-08-15 11:10:30 +10:00
return count ;
}
2017-05-18 23:24:38 +02:00
static SENSOR_DEVICE_ATTR ( pwm1_max , 0644 ,
2012-09-04 13:39:40 +02:00
nouveau_hwmon_get_pwm1_max ,
nouveau_hwmon_set_pwm1_max , 0 ) ;
2011-08-15 11:10:30 +10:00
2017-05-18 23:24:37 +02:00
static struct attribute * pwm_fan_sensor_attrs [ ] = {
& sensor_dev_attr_pwm1_min . dev_attr . attr ,
& sensor_dev_attr_pwm1_max . dev_attr . attr ,
NULL
} ;
static const struct attribute_group pwm_fan_sensor_group = {
. attrs = pwm_fan_sensor_attrs ,
} ;
static struct attribute * temp1_auto_point_sensor_attrs [ ] = {
& sensor_dev_attr_temp1_auto_point1_pwm . dev_attr . attr ,
& sensor_dev_attr_temp1_auto_point1_temp . dev_attr . attr ,
& sensor_dev_attr_temp1_auto_point1_temp_hyst . dev_attr . attr ,
NULL
} ;
static const struct attribute_group temp1_auto_point_sensor_group = {
. attrs = temp1_auto_point_sensor_attrs ,
} ;
# define N_ATTR_GROUPS 3
2017-05-18 23:24:34 +02:00
static const u32 nouveau_config_chip [ ] = {
HWMON_C_UPDATE_INTERVAL ,
0
} ;
static const u32 nouveau_config_in [ ] = {
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_LABEL ,
0
} ;
static const u32 nouveau_config_temp [ ] = {
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_EMERGENCY |
HWMON_T_EMERGENCY_HYST ,
0
} ;
static const u32 nouveau_config_fan [ ] = {
HWMON_F_INPUT ,
0
} ;
static const u32 nouveau_config_pwm [ ] = {
HWMON_PWM_INPUT | HWMON_PWM_ENABLE ,
0
} ;
static const u32 nouveau_config_power [ ] = {
HWMON_P_INPUT | HWMON_P_CAP_MAX | HWMON_P_CRIT ,
0
} ;
static const struct hwmon_channel_info nouveau_chip = {
. type = hwmon_chip ,
. config = nouveau_config_chip ,
} ;
static const struct hwmon_channel_info nouveau_temp = {
. type = hwmon_temp ,
. config = nouveau_config_temp ,
} ;
static const struct hwmon_channel_info nouveau_fan = {
. type = hwmon_fan ,
. config = nouveau_config_fan ,
} ;
static const struct hwmon_channel_info nouveau_in = {
. type = hwmon_in ,
. config = nouveau_config_in ,
} ;
static const struct hwmon_channel_info nouveau_pwm = {
. type = hwmon_pwm ,
. config = nouveau_config_pwm ,
} ;
static const struct hwmon_channel_info nouveau_power = {
. type = hwmon_power ,
. config = nouveau_config_power ,
} ;
static const struct hwmon_channel_info * nouveau_info [ ] = {
& nouveau_chip ,
& nouveau_temp ,
& nouveau_fan ,
& nouveau_in ,
& nouveau_pwm ,
& nouveau_power ,
NULL
} ;
2017-05-18 23:24:35 +02:00
static umode_t
nouveau_chip_is_visible ( const void * data , u32 attr , int channel )
{
switch ( attr ) {
case hwmon_chip_update_interval :
return 0444 ;
default :
return 0 ;
}
}
static umode_t
nouveau_power_is_visible ( const void * data , u32 attr , int channel )
{
struct nouveau_drm * drm = nouveau_drm ( ( struct drm_device * ) data ) ;
struct nvkm_iccsense * iccsense = nvxx_iccsense ( & drm - > client . device ) ;
if ( ! iccsense | | ! iccsense - > data_valid | | list_empty ( & iccsense - > rails ) )
return 0 ;
switch ( attr ) {
case hwmon_power_input :
return 0444 ;
case hwmon_power_max :
if ( iccsense - > power_w_max )
return 0444 ;
return 0 ;
case hwmon_power_crit :
if ( iccsense - > power_w_crit )
return 0444 ;
return 0 ;
default :
return 0 ;
}
}
static umode_t
nouveau_temp_is_visible ( const void * data , u32 attr , int channel )
{
struct nouveau_drm * drm = nouveau_drm ( ( struct drm_device * ) data ) ;
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
2018-04-22 17:47:12 -04:00
if ( ! therm | | ! therm - > attr_get | | nvkm_therm_temp_get ( therm ) < 0 )
2017-05-18 23:24:35 +02:00
return 0 ;
switch ( attr ) {
case hwmon_temp_input :
2017-05-18 23:24:36 +02:00
return 0444 ;
2017-05-18 23:24:35 +02:00
case hwmon_temp_max :
case hwmon_temp_max_hyst :
case hwmon_temp_crit :
case hwmon_temp_crit_hyst :
case hwmon_temp_emergency :
case hwmon_temp_emergency_hyst :
2017-05-18 23:24:36 +02:00
return 0644 ;
2017-05-18 23:24:35 +02:00
default :
return 0 ;
}
}
static umode_t
nouveau_pwm_is_visible ( const void * data , u32 attr , int channel )
{
struct nouveau_drm * drm = nouveau_drm ( ( struct drm_device * ) data ) ;
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
2018-04-22 17:47:12 -04:00
if ( ! therm | | ! therm - > attr_get | | ! therm - > fan_get | |
therm - > fan_get ( therm ) < 0 )
2017-05-18 23:24:35 +02:00
return 0 ;
switch ( attr ) {
case hwmon_pwm_enable :
case hwmon_pwm_input :
return 0644 ;
default :
return 0 ;
}
}
static umode_t
nouveau_input_is_visible ( const void * data , u32 attr , int channel )
{
struct nouveau_drm * drm = nouveau_drm ( ( struct drm_device * ) data ) ;
struct nvkm_volt * volt = nvxx_volt ( & drm - > client . device ) ;
if ( ! volt | | nvkm_volt_get ( volt ) < 0 )
return 0 ;
switch ( attr ) {
case hwmon_in_input :
case hwmon_in_label :
case hwmon_in_min :
case hwmon_in_max :
return 0444 ;
default :
return 0 ;
}
}
static umode_t
nouveau_fan_is_visible ( const void * data , u32 attr , int channel )
{
struct nouveau_drm * drm = nouveau_drm ( ( struct drm_device * ) data ) ;
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
if ( ! therm | | ! therm - > attr_get | | nvkm_therm_fan_sense ( therm ) < 0 )
return 0 ;
switch ( attr ) {
case hwmon_fan_input :
return 0444 ;
default :
return 0 ;
}
}
2017-05-18 23:24:36 +02:00
static int
nouveau_chip_read ( struct device * dev , u32 attr , int channel , long * val )
{
switch ( attr ) {
case hwmon_chip_update_interval :
* val = 1000 ;
break ;
default :
return - EOPNOTSUPP ;
}
return 0 ;
}
static int
nouveau_temp_read ( struct device * dev , u32 attr , int channel , long * val )
{
struct drm_device * drm_dev = dev_get_drvdata ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( drm_dev ) ;
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
int ret ;
if ( ! therm | | ! therm - > attr_get )
return - EOPNOTSUPP ;
switch ( attr ) {
case hwmon_temp_input :
2019-06-18 13:01:33 +02:00
if ( drm_dev - > switch_power_state ! = DRM_SWITCH_POWER_ON )
return - EINVAL ;
2017-05-18 23:24:36 +02:00
ret = nvkm_therm_temp_get ( therm ) ;
* val = ret < 0 ? ret : ( ret * 1000 ) ;
break ;
case hwmon_temp_max :
* val = therm - > attr_get ( therm , NVKM_THERM_ATTR_THRS_DOWN_CLK )
* 1000 ;
break ;
case hwmon_temp_max_hyst :
* val = therm - > attr_get ( therm , NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST )
* 1000 ;
break ;
case hwmon_temp_crit :
* val = therm - > attr_get ( therm , NVKM_THERM_ATTR_THRS_CRITICAL )
* 1000 ;
break ;
case hwmon_temp_crit_hyst :
* val = therm - > attr_get ( therm , NVKM_THERM_ATTR_THRS_CRITICAL_HYST )
* 1000 ;
break ;
case hwmon_temp_emergency :
* val = therm - > attr_get ( therm , NVKM_THERM_ATTR_THRS_SHUTDOWN )
* 1000 ;
break ;
case hwmon_temp_emergency_hyst :
* val = therm - > attr_get ( therm , NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST )
* 1000 ;
break ;
default :
return - EOPNOTSUPP ;
}
return 0 ;
}
static int
nouveau_fan_read ( struct device * dev , u32 attr , int channel , long * val )
{
struct drm_device * drm_dev = dev_get_drvdata ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( drm_dev ) ;
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
if ( ! therm )
return - EOPNOTSUPP ;
switch ( attr ) {
case hwmon_fan_input :
2019-06-18 13:01:33 +02:00
if ( drm_dev - > switch_power_state ! = DRM_SWITCH_POWER_ON )
return - EINVAL ;
2017-05-18 23:24:36 +02:00
* val = nvkm_therm_fan_sense ( therm ) ;
break ;
default :
return - EOPNOTSUPP ;
}
return 0 ;
}
static int
nouveau_in_read ( struct device * dev , u32 attr , int channel , long * val )
{
struct drm_device * drm_dev = dev_get_drvdata ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( drm_dev ) ;
struct nvkm_volt * volt = nvxx_volt ( & drm - > client . device ) ;
int ret ;
if ( ! volt )
return - EOPNOTSUPP ;
switch ( attr ) {
case hwmon_in_input :
2019-06-18 13:01:33 +02:00
if ( drm_dev - > switch_power_state ! = DRM_SWITCH_POWER_ON )
return - EINVAL ;
2017-05-18 23:24:36 +02:00
ret = nvkm_volt_get ( volt ) ;
* val = ret < 0 ? ret : ( ret / 1000 ) ;
break ;
case hwmon_in_min :
* val = volt - > min_uv > 0 ? ( volt - > min_uv / 1000 ) : - ENODEV ;
break ;
case hwmon_in_max :
* val = volt - > max_uv > 0 ? ( volt - > max_uv / 1000 ) : - ENODEV ;
break ;
default :
return - EOPNOTSUPP ;
}
return 0 ;
}
static int
nouveau_pwm_read ( struct device * dev , u32 attr , int channel , long * val )
{
struct drm_device * drm_dev = dev_get_drvdata ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( drm_dev ) ;
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
if ( ! therm | | ! therm - > attr_get | | ! therm - > fan_get )
return - EOPNOTSUPP ;
switch ( attr ) {
case hwmon_pwm_enable :
* val = therm - > attr_get ( therm , NVKM_THERM_ATTR_FAN_MODE ) ;
break ;
case hwmon_pwm_input :
2019-06-18 13:01:33 +02:00
if ( drm_dev - > switch_power_state ! = DRM_SWITCH_POWER_ON )
return - EINVAL ;
2017-05-18 23:24:36 +02:00
* val = therm - > fan_get ( therm ) ;
break ;
default :
return - EOPNOTSUPP ;
}
return 0 ;
}
static int
nouveau_power_read ( struct device * dev , u32 attr , int channel , long * val )
{
struct drm_device * drm_dev = dev_get_drvdata ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( drm_dev ) ;
struct nvkm_iccsense * iccsense = nvxx_iccsense ( & drm - > client . device ) ;
if ( ! iccsense )
return - EOPNOTSUPP ;
switch ( attr ) {
case hwmon_power_input :
2019-06-18 13:01:33 +02:00
if ( drm_dev - > switch_power_state ! = DRM_SWITCH_POWER_ON )
return - EINVAL ;
2017-05-18 23:24:36 +02:00
* val = nvkm_iccsense_read_all ( iccsense ) ;
break ;
case hwmon_power_max :
* val = iccsense - > power_w_max ;
break ;
case hwmon_power_crit :
* val = iccsense - > power_w_crit ;
break ;
default :
return - EOPNOTSUPP ;
}
return 0 ;
}
static int
nouveau_temp_write ( struct device * dev , u32 attr , int channel , long val )
{
struct drm_device * drm_dev = dev_get_drvdata ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( drm_dev ) ;
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
if ( ! therm | | ! therm - > attr_set )
return - EOPNOTSUPP ;
switch ( attr ) {
case hwmon_temp_max :
return therm - > attr_set ( therm , NVKM_THERM_ATTR_THRS_DOWN_CLK ,
val / 1000 ) ;
case hwmon_temp_max_hyst :
return therm - > attr_set ( therm , NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST ,
val / 1000 ) ;
case hwmon_temp_crit :
return therm - > attr_set ( therm , NVKM_THERM_ATTR_THRS_CRITICAL ,
val / 1000 ) ;
case hwmon_temp_crit_hyst :
return therm - > attr_set ( therm , NVKM_THERM_ATTR_THRS_CRITICAL_HYST ,
val / 1000 ) ;
case hwmon_temp_emergency :
return therm - > attr_set ( therm , NVKM_THERM_ATTR_THRS_SHUTDOWN ,
val / 1000 ) ;
case hwmon_temp_emergency_hyst :
return therm - > attr_set ( therm , NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST ,
val / 1000 ) ;
default :
return - EOPNOTSUPP ;
}
}
static int
nouveau_pwm_write ( struct device * dev , u32 attr , int channel , long val )
{
struct drm_device * drm_dev = dev_get_drvdata ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( drm_dev ) ;
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
if ( ! therm | | ! therm - > attr_set )
return - EOPNOTSUPP ;
switch ( attr ) {
case hwmon_pwm_input :
return therm - > fan_set ( therm , val ) ;
case hwmon_pwm_enable :
return therm - > attr_set ( therm , NVKM_THERM_ATTR_FAN_MODE , val ) ;
default :
return - EOPNOTSUPP ;
}
}
2017-05-18 23:24:35 +02:00
static umode_t
nouveau_is_visible ( const void * data , enum hwmon_sensor_types type , u32 attr ,
int channel )
{
switch ( type ) {
case hwmon_chip :
return nouveau_chip_is_visible ( data , attr , channel ) ;
case hwmon_temp :
return nouveau_temp_is_visible ( data , attr , channel ) ;
case hwmon_fan :
return nouveau_fan_is_visible ( data , attr , channel ) ;
case hwmon_in :
return nouveau_input_is_visible ( data , attr , channel ) ;
case hwmon_pwm :
return nouveau_pwm_is_visible ( data , attr , channel ) ;
case hwmon_power :
return nouveau_power_is_visible ( data , attr , channel ) ;
default :
return 0 ;
}
}
static const char input_label [ ] = " GPU core " ;
static int
nouveau_read_string ( struct device * dev , enum hwmon_sensor_types type , u32 attr ,
int channel , const char * * buf )
{
if ( type = = hwmon_in & & attr = = hwmon_in_label ) {
* buf = input_label ;
return 0 ;
}
return - EOPNOTSUPP ;
}
2017-05-18 23:24:36 +02:00
static int
nouveau_read ( struct device * dev , enum hwmon_sensor_types type , u32 attr ,
int channel , long * val )
{
switch ( type ) {
case hwmon_chip :
return nouveau_chip_read ( dev , attr , channel , val ) ;
case hwmon_temp :
return nouveau_temp_read ( dev , attr , channel , val ) ;
case hwmon_fan :
return nouveau_fan_read ( dev , attr , channel , val ) ;
case hwmon_in :
return nouveau_in_read ( dev , attr , channel , val ) ;
case hwmon_pwm :
return nouveau_pwm_read ( dev , attr , channel , val ) ;
case hwmon_power :
return nouveau_power_read ( dev , attr , channel , val ) ;
default :
return - EOPNOTSUPP ;
}
}
static int
nouveau_write ( struct device * dev , enum hwmon_sensor_types type , u32 attr ,
int channel , long val )
{
switch ( type ) {
case hwmon_temp :
return nouveau_temp_write ( dev , attr , channel , val ) ;
case hwmon_pwm :
return nouveau_pwm_write ( dev , attr , channel , val ) ;
default :
return - EOPNOTSUPP ;
}
}
2017-05-18 23:24:35 +02:00
static const struct hwmon_ops nouveau_hwmon_ops = {
. is_visible = nouveau_is_visible ,
2017-05-18 23:24:36 +02:00
. read = nouveau_read ,
2017-05-18 23:24:35 +02:00
. read_string = nouveau_read_string ,
2017-05-18 23:24:36 +02:00
. write = nouveau_write ,
2017-05-18 23:24:35 +02:00
} ;
static const struct hwmon_chip_info nouveau_chip_info = {
. ops = & nouveau_hwmon_ops ,
. info = nouveau_info ,
} ;
2010-10-26 12:48:28 +02:00
# endif
2010-09-22 20:54:22 +02:00
2013-10-15 09:44:02 +10:00
int
2010-09-22 20:54:22 +02:00
nouveau_hwmon_init ( struct drm_device * dev )
{
2012-01-10 10:13:16 +00:00
# if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
2013-01-01 03:28:00 -08:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2018-04-22 17:47:12 -04:00
struct nvkm_iccsense * iccsense = nvxx_iccsense ( & drm - > client . device ) ;
2016-05-18 13:57:42 +10:00
struct nvkm_therm * therm = nvxx_therm ( & drm - > client . device ) ;
2018-04-22 17:47:12 -04:00
struct nvkm_volt * volt = nvxx_volt ( & drm - > client . device ) ;
2017-05-18 23:24:37 +02:00
const struct attribute_group * special_groups [ N_ATTR_GROUPS ] ;
2013-10-15 09:44:02 +10:00
struct nouveau_hwmon * hwmon ;
2010-09-22 20:54:22 +02:00
struct device * hwmon_dev ;
2011-08-15 11:10:30 +10:00
int ret = 0 ;
2017-05-18 23:24:37 +02:00
int i = 0 ;
2010-09-22 20:54:22 +02:00
2018-04-22 17:47:12 -04:00
if ( ! iccsense & & ! therm & & ! volt ) {
NV_DEBUG ( drm , " Skipping hwmon registration \n " ) ;
return 0 ;
}
2013-10-15 09:44:02 +10:00
hwmon = drm - > hwmon = kzalloc ( sizeof ( * hwmon ) , GFP_KERNEL ) ;
if ( ! hwmon )
return - ENOMEM ;
hwmon - > dev = dev ;
2017-05-18 23:24:37 +02:00
if ( therm & & therm - > attr_get & & therm - > attr_set ) {
if ( nvkm_therm_temp_get ( therm ) > = 0 )
special_groups [ i + + ] = & temp1_auto_point_sensor_group ;
if ( therm - > fan_get & & therm - > fan_get ( therm ) > = 0 )
special_groups [ i + + ] = & pwm_fan_sensor_group ;
}
special_groups [ i ] = 0 ;
2017-05-18 23:24:36 +02:00
hwmon_dev = hwmon_device_register_with_info ( dev - > dev , " nouveau " , dev ,
2017-05-18 23:24:37 +02:00
& nouveau_chip_info ,
special_groups ) ;
2010-09-22 20:54:22 +02:00
if ( IS_ERR ( hwmon_dev ) ) {
ret = PTR_ERR ( hwmon_dev ) ;
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " Unable to register hwmon device: %d \n " , ret ) ;
2010-09-22 20:54:22 +02:00
return ret ;
}
2016-02-18 20:10:19 +01:00
2013-10-15 09:44:02 +10:00
hwmon - > hwmon = hwmon_dev ;
2010-09-22 20:54:22 +02:00
return 0 ;
2011-08-15 11:10:30 +10:00
# else
return 0 ;
# endif
2010-09-22 20:54:22 +02:00
}
2013-10-15 09:44:02 +10:00
void
2010-09-22 20:54:22 +02:00
nouveau_hwmon_fini ( struct drm_device * dev )
{
2011-07-03 19:54:28 +01:00
# if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
2013-10-15 09:44:02 +10:00
struct nouveau_hwmon * hwmon = nouveau_hwmon ( dev ) ;
2010-09-22 20:54:22 +02:00
2018-04-22 17:47:12 -04:00
if ( ! hwmon )
return ;
2017-05-18 23:24:36 +02:00
if ( hwmon - > hwmon )
2013-10-15 09:44:02 +10:00
hwmon_device_unregister ( hwmon - > hwmon ) ;
2010-10-12 12:31:32 +10:00
2013-10-15 09:44:02 +10:00
nouveau_drm ( dev ) - > hwmon = NULL ;
kfree ( hwmon ) ;
2010-10-12 12:31:32 +10:00
# endif
2010-09-17 13:35:25 +10:00
}