diff mbox series

hwmon: (ctf2304) rewrote from scratch to implement new hwmon registration API

Message ID 20230407200810.693258-1-corone.il.han@gmail.com (mailing list archive)
State Changes Requested
Headers show
Series hwmon: (ctf2304) rewrote from scratch to implement new hwmon registration API | expand

Commit Message

Il Han April 7, 2023, 8:08 p.m. UTC
rewrote from scratch to implement new hwmon registration API.

Signed-off-by: Il Han <corone.il.han@gmail.com>
---
 Documentation/hwmon/ctf2304.rst |   9 +-
 Documentation/hwmon/index.rst   |   1 +
 drivers/hwmon/Kconfig           |   5 +-
 drivers/hwmon/ctf2304.c         | 598 +++++++++++++++-----------------
 4 files changed, 285 insertions(+), 328 deletions(-)

Comments

Guenter Roeck April 7, 2023, 9:16 p.m. UTC | #1
On Sat, Apr 08, 2023 at 05:08:10AM +0900, Il Han wrote:
> rewrote from scratch to implement new hwmon registration API.
> 
> Signed-off-by: Il Han <corone.il.han@gmail.com>

I have no idea what this is about, but this is even worse
than the previous versions. Please read available documentation
about submitting drivers nd follow it.

Guenter

> ---
>  Documentation/hwmon/ctf2304.rst |   9 +-
>  Documentation/hwmon/index.rst   |   1 +
>  drivers/hwmon/Kconfig           |   5 +-
>  drivers/hwmon/ctf2304.c         | 598 +++++++++++++++-----------------
>  4 files changed, 285 insertions(+), 328 deletions(-)
> 
> diff --git a/Documentation/hwmon/ctf2304.rst b/Documentation/hwmon/ctf2304.rst
> index d0e9ef9ea52e..46f4b1671795 100644
> --- a/Documentation/hwmon/ctf2304.rst
> +++ b/Documentation/hwmon/ctf2304.rst
> @@ -21,8 +21,9 @@ Description
>  
>  This driver implements support for the Sensylink CTF2304 chip.
>  
> -The CTF2304 controls the speeds of up to four fans using four independent
> -PWM outputs with local and remote temperature and remote voltage sensing.
> +The CTF2304 is a system level thermal management solution chip, including
> +1-channel local temperature, 8-channels remote temperature or voltage monitor
> +and 4-channels fan speed monitor and driver with I2C/SMBus digital interface.
>  
>  
>  Sysfs entries
> @@ -31,11 +32,11 @@ Sysfs entries
>  ================== === =======================================================
>  fan[1-4]_input     RO  fan tachometer speed in RPM
>  fan[1-4]_target    RW  desired fan speed in RPM
> -fan[1-4]_div       RW  sets the RPM range of the fan.
> +fan[1-4]_div       RW  sets the RPM range of the fan
>  pwm[1-4]_enable    RW  regulator mode,
>                         0=auto temperature mode, 1=manual mode, 2=rpm mode
>  pwm[1-4]           RW  read: current pwm duty cycle,
>                         write: target pwm duty cycle (0-255)
> -in[1-8]_input      RO  measured output voltage
> +in[0-7]_input      RO  measured output voltage
>  temp[1-9]_input    RO  measured temperature
>  ================== === =======================================================
> diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
> index f1fe75f596a5..a74cd43a3916 100644
> --- a/Documentation/hwmon/index.rst
> +++ b/Documentation/hwmon/index.rst
> @@ -54,6 +54,7 @@ Hardware Monitoring Kernel Drivers
>     coretemp
>     corsair-cpro
>     corsair-psu
> +   ctf2304
>     da9052
>     da9055
>     dell-smm-hwmon
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index da9fbb0f8af3..df30a5a921de 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -478,8 +478,9 @@ config SENSORS_CTF2304
>  	tristate "Sensylink CTF2304 sensor chip"
>  	depends on I2C
>  	help
> -	  If you say yes here you get support for PWM and Fan Controller
> -	  with temperature and voltage sensing.
> +	  If you say yes here you get support for 1-channel local temperature,
> +	  8-channels remote temperature or voltage monitor and 4-channels fan
> +	  speed monitor and driver.
>  
>  	  This driver can also be built as a module. If so, the module
>  	  will be called ctf2304.
> diff --git a/drivers/hwmon/ctf2304.c b/drivers/hwmon/ctf2304.c
> index 5788740a57e5..42939e39babf 100644
> --- a/drivers/hwmon/ctf2304.c
> +++ b/drivers/hwmon/ctf2304.c
> @@ -8,7 +8,6 @@
>  
>  #include <linux/err.h>
>  #include <linux/hwmon.h>
> -#include <linux/hwmon-sysfs.h>
>  #include <linux/i2c.h>
>  #include <linux/init.h>
>  #include <linux/jiffies.h>
> @@ -47,9 +46,12 @@
>  #define FAN_RPM_MIN			480
>  #define FAN_RPM_MAX			1966080
>  
> -#define FAN_COUNT_REG_MAX		0xFFF
> +#define FAN_COUNT_REG_MAX		0xFFF0
>  
> -#define TEMP_FROM_REG(reg)		(((reg) * 1000) >> 8)
> +#define TEMP_FROM_REG(reg, tr)		((tr) ? \
> +					 (((((reg) & 0x7FF0) * 1000) >> 8) \
> +					  + ((reg) >> 15) ? -64000 : 0) : \
> +					 (((reg) * 1000) >> 8))
>  #define VOLT_FROM_REG(reg, fs)		((((reg) >> 4) * (fs)) >> 12)
>  #define DIV_FROM_REG(reg)		(1 << (reg))
>  #define DIV_TO_REG(div)			((div == 8) ? 0x3 : \
> @@ -71,7 +73,7 @@
>  struct ctf2304_data {
>  	struct i2c_client *client;
>  	struct mutex update_lock;
> -	char valid; /* zero until following fields are valid */
> +	bool valid; /* zero until following fields are valid */
>  	unsigned long last_updated; /* in jiffies */
>  
>  	/* register values */
> @@ -89,6 +91,7 @@ static struct ctf2304_data *ctf2304_update_device(struct device *dev)
>  {
>  	struct ctf2304_data *data = dev_get_drvdata(dev);
>  	struct i2c_client *client = data->client;
> +	struct ctf2304_data *ret = data;
>  	int i;
>  	int rv;
>  
> @@ -97,427 +100,377 @@ static struct ctf2304_data *ctf2304_update_device(struct device *dev)
>  	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
>  		rv = i2c_smbus_read_word_swapped(client,
>  				CTF2304_REG_LOCAL_TEMP);
> -		if (rv >= 0)
> -			data->local_temp = rv;
> +		if (rv < 0)
> +			goto abort;
> +		data->local_temp = rv;
>  
>  		for (i = 0; i < NR_CHANNEL; i++) {
>  			rv = i2c_smbus_read_word_swapped(client,
>  					CTF2304_REG_REMOTE_CHANNEL(i));
> -			if (rv >= 0)
> -				data->remote_channel[i] = rv;
> +			if (rv < 0)
> +				goto abort;
> +			data->remote_channel[i] = rv;
>  		}
>  
>  		rv = i2c_smbus_read_word_swapped(client,
>  				CTF2304_REG_FAN_CONFIG1);
> -		if (rv >= 0)
> -			data->fan_config1 = rv;
> +		if (rv < 0)
> +			goto abort;
> +		data->fan_config1 = rv;
>  		rv = i2c_smbus_read_word_swapped(client,
>  				CTF2304_REG_FAN_CONFIG2);
> -		if (rv >= 0)
> -			data->fan_config2 = rv;
> +		if (rv < 0)
> +			goto abort;
> +		data->fan_config2 = rv;
>  		rv = i2c_smbus_read_word_swapped(client,
>  				CTF2304_REG_FAN_RPM_CTRL);
> -		if (rv >= 0)
> -			data->fan_rpm_ctrl = rv;
> +		if (rv < 0)
> +			goto abort;
> +		data->fan_rpm_ctrl = rv;
>  
>  		for (i = 0; i < NR_FAN_CHANNEL; i++) {
>  			rv = i2c_smbus_read_word_swapped(client,
>  					CTF2304_REG_TACH_COUNT(i));
> -			if (rv >= 0)
> -				data->tach[i] = rv;
> +			if (rv < 0)
> +				goto abort;
> +			data->tach[i] = rv;
>  			rv = i2c_smbus_read_word_swapped(client,
>  					CTF2304_REG_PWMOUT(i));
> -			if (rv >= 0)
> -				data->pwm[i] = rv;
> +			if (rv < 0)
> +				goto abort;
> +			data->pwm[i] = rv;
>  			rv = i2c_smbus_read_word_swapped(client,
>  					CTF2304_REG_TARGET_COUNT(i));
> -			if (rv >= 0)
> -				data->target_count[i] = rv;
> +			if (rv < 0)
> +				goto abort;
> +			data->target_count[i] = rv;
>  		}
>  
>  		data->last_updated = jiffies;
>  		data->valid = true;
>  	}
> +	goto done;
>  
> +abort:
> +	data->valid = false;
> +	ret = ERR_PTR(rv);
> +
> +done:
>  	mutex_unlock(&data->update_lock);
>  
>  	return data;
>  }
>  
> -static int register_to_temp(u16 reg, u16 config)
> -{
> -	if (config & CTF2304_FAN_CFG1_TRANGE)
> -		return TEMP_FROM_REG(reg & 0x7FF0) + ((reg >> 15) ? -64000:0);
> -	else
> -		return TEMP_FROM_REG(reg);
> -}
> -
> -static ssize_t show_temp(struct device *dev,
> -			 struct device_attribute *devattr, char *buf)
> +static int ctf2304_read_temp(struct device *dev, u32 attr, int channel,
> +			     long *val)
>  {
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
>  	struct ctf2304_data *data = ctf2304_update_device(dev);
>  	u16 reg;
>  
> -	if (attr->index == 0)
> -		reg = data->local_temp;
> -	else
> -		reg = data->remote_channel[attr->index-1];
> -
> -	return sprintf(buf, "%d\n", register_to_temp(reg, data->fan_config1));
> +	switch (attr) {
> +	case hwmon_temp_input:
> +		if (channel == 0)
> +			reg = data->local_temp;
> +		else
> +			reg = data->remote_channel[channel-1];
> +		*val = TEMP_FROM_REG(reg, (data->fan_config1
> +					   & CTF2304_FAN_CFG1_TRANGE));
> +		return 0;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
>  }
>  
> +static const int full_scale[8] = { 2560, CTF2304_VCC, 4096, 2048, 1024, 512, 256, 256 };
> +
>  static int get_full_scale(u16 config)
>  {
> -	int full_scale;
>  	u8 bits;
>  
>  	bits = (config >> CTF2304_FAN_CFG1_MODE_SHIFT)
>  	       & CTF2304_FAN_CFG1_MODE_MASK;
>  
> -	if (bits == 0x0)
> -		full_scale = 2560;
> -	else if (bits == 0x1)
> -		full_scale = CTF2304_VCC;
> -	else if (bits == 0x2)
> -		full_scale = 4096;
> -	else if (bits == 0x3)
> -		full_scale = 2048;
> -	else if (bits == 0x4)
> -		full_scale = 1024;
> -	else if (bits == 0x5)
> -		full_scale = 512;
> -	else
> -		full_scale = 256;
> -
> -	return full_scale;
> +	return full_scale[bits];
>  }
>  
> -static int register_to_volt(u16 reg, u16 config)
> +static int ctf2304_read_in(struct device *dev, u32 attr, int channel,
> +			   long *val)
>  {
> -	int full_scale;
> -
> -	full_scale = get_full_scale(config);
> -
> -	return VOLT_FROM_REG(reg, full_scale);
> -}
> -
> -static ssize_t show_voltage(struct device *dev,
> -			    struct device_attribute *devattr, char *buf)
> -{
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
> -	struct ctf2304_data *data = ctf2304_update_device(dev);
> -	int voltage;
> -
> -	voltage = register_to_volt(data->remote_channel[attr->index],
> -			data->fan_config1);
> -
> -	return sprintf(buf, "%d\n", voltage);
> -}
> -
> -static ssize_t get_fan(struct device *dev,
> -		       struct device_attribute *devattr, char *buf)
> -{
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
>  	struct ctf2304_data *data = ctf2304_update_device(dev);
> -	int rpm;
>  
> -	rpm = RPM_FROM_REG(data->tach[attr->index]);
> -
> -	return sprintf(buf, "%d\n", rpm);
> +	switch (attr) {
> +	case hwmon_temp_input:
> +		*val = VOLT_FROM_REG(data->remote_channel[channel],
> +				     get_full_scale(data->fan_config1));
> +		return 0;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
>  }
>  
> -static ssize_t get_fan_target(struct device *dev,
> -			      struct device_attribute *devattr, char *buf)
> +static int ctf2304_read_fan(struct device *dev, u32 attr, int channel,
> +			    long *val)
>  {
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
>  	struct ctf2304_data *data = ctf2304_update_device(dev);
> -	int rpm;
> -
> -	rpm = RPM_FROM_REG(data->target_count[attr->index]);
> +	u8 bits;
>  
> -	return sprintf(buf, "%d\n", rpm);
> +	if (IS_ERR(data))
> +		return PTR_ERR(data);
> +
> +	switch (attr) {
> +	case hwmon_fan_input:
> +		if (data->tach[channel] == FAN_COUNT_REG_MAX)
> +			*val = 0;
> +		else
> +			*val = RPM_FROM_REG(data->tach[channel]);
> +		return 0;
> +	case hwmon_fan_target:
> +		*val = RPM_FROM_REG(data->target_count[channel]);
> +		return 0;
> +	case hwmon_fan_div:
> +		bits = (data->fan_rpm_ctrl & CTF2304_FAN_DIV_MASK(channel))
> +		       >> CTF2304_FAN_DIV_SHIFT(channel);
> +		*val = DIV_FROM_REG(bits);
> +		return 0;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
>  }
>  
> -static ssize_t set_fan_target(struct device *dev,
> -			      struct device_attribute *devattr,
> -			      const char *buf, size_t count)
> +static int ctf2304_write_fan(struct device *dev, u32 attr, int channel,
> +			     long val)
>  {
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
>  	struct ctf2304_data *data = dev_get_drvdata(dev);
>  	struct i2c_client *client = data->client;
> -	unsigned long rpm;
>  	int target_count;
> -	int err;
> -
> -	err = kstrtoul(buf, 10, &rpm);
> -	if (err)
> -		return err;
> -
> -	rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
> -	target_count = RPM_TO_REG(rpm);
> -	target_count = clamp_val(target_count, 0x1, 0xFFF);
> +	int err = 0;
>  
>  	mutex_lock(&data->update_lock);
>  
> -	data->target_count[attr->index] = target_count << 4;
> -	i2c_smbus_write_word_swapped(client,
> -			CTF2304_REG_TARGET_COUNT(attr->index),
> -			data->target_count[attr->index]);
> +	switch (attr) {
> +	case hwmon_fan_target:
> +		val = clamp_val(val, FAN_RPM_MIN, FAN_RPM_MAX);
> +		target_count = RPM_TO_REG(val);
> +		target_count = clamp_val(target_count, 0x1, 0xFFF);
> +		data->target_count[channel] = target_count << 4;
> +		err = i2c_smbus_write_word_swapped(client,
> +				CTF2304_REG_TARGET_COUNT(channel),
> +				data->target_count[channel]);
> +		break;
> +	case hwmon_fan_div:
> +		data->fan_rpm_ctrl = (data->fan_rpm_ctrl
> +				      & ~CTF2304_FAN_DIV_MASK(channel))
> +				     | (DIV_TO_REG(val)
> +					<< CTF2304_FAN_DIV_SHIFT(channel));
> +		err = i2c_smbus_write_word_swapped(client,
> +				CTF2304_REG_FAN_RPM_CTRL,
> +				data->fan_rpm_ctrl);
> +		break;
> +	default:
> +		err = -EOPNOTSUPP;
> +		break;
> +	}
>  
>  	mutex_unlock(&data->update_lock);
>  
> -	return count;
> +	return err;
>  }
>  
> -static ssize_t show_fan_div(struct device *dev,
> -			    struct device_attribute *devattr, char *buf)
> +static int ctf2304_read_pwm(struct device *dev, u32 attr, int channel,
> +			    long *val)
>  {
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
>  	struct ctf2304_data *data = ctf2304_update_device(dev);
>  	u8 bits;
>  
> -	bits = (data->fan_rpm_ctrl & CTF2304_FAN_DIV_MASK(attr->index))
> -	       >> CTF2304_FAN_DIV_SHIFT(attr->index);
> -
> -	return sprintf(buf, "%d\n", DIV_FROM_REG(bits));
> +	if (IS_ERR(data))
> +		return PTR_ERR(data);
> +
> +	switch (attr) {
> +	case hwmon_pwm_input:
> +		*val = data->pwm[channel] >> 8;
> +		return 0;
> +	case hwmon_pwm_enable:
> +		bits = (data->fan_config2
> +			& CTF2304_FAN_CFG2_MODE_MASK(channel))
> +		       >> CTF2304_FAN_CFG2_MODE_SHIFT(channel);
> +		if (bits == CTF2304_FAN_CFG2_RPM_MODE)
> +			*val = 2;
> +		else if (bits == CTF2304_FAN_CFG2_DCY_MODE)
> +			*val = 1;
> +		else
> +			*val = 0;
> +		return 0;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
>  }
>  
> -static ssize_t set_fan_div(struct device *dev,
> -			   struct device_attribute *devattr,
> -			   const char *buf, size_t count)
> +static int ctf2304_write_pwm(struct device *dev, u32 attr, int channel,
> +			     long val)
>  {
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
>  	struct ctf2304_data *data = dev_get_drvdata(dev);
>  	struct i2c_client *client = data->client;
> -	unsigned long div;
> -	int err;
> -
> -	err = kstrtoul(buf, 10, &div);
> -	if (err)
> -		return err;
> +	int err = 0;
>  
>  	mutex_lock(&data->update_lock);
>  
> -	data->fan_rpm_ctrl = (data->fan_rpm_ctrl
> -			      & ~CTF2304_FAN_DIV_MASK(attr->index))
> -			     | (DIV_TO_REG(div)
> -				<< CTF2304_FAN_DIV_SHIFT(attr->index));
> -
> -	i2c_smbus_write_word_swapped(client,
> -			CTF2304_REG_FAN_RPM_CTRL, data->fan_rpm_ctrl);
> +	switch (attr) {
> +	case hwmon_pwm_input:
> +		if (val < 0 || val > 255) {
> +			err = -EINVAL;
> +			break;
> +		}
> +		data->pwm[channel] = (data->pwm[channel] & 0xFF) | (val << 8);
> +		err = i2c_smbus_write_word_swapped(client,
> +						   CTF2304_REG_PWMOUT(channel),
> +						   data->pwm[channel]);
> +		break;
> +	case hwmon_pwm_enable:
> +		if (val == 0) {
> +			data->fan_config2 = (data->fan_config2
> +					     & ~CTF2304_FAN_CFG2_MODE_MASK(channel))
> +					    | (CTF2304_FAN_CFG2_TEMP_MODE
> +					       << CTF2304_FAN_CFG2_MODE_SHIFT(channel));
> +		} else if (val == 1) {
> +			data->fan_config2 = (data->fan_config2
> +					     & ~CTF2304_FAN_CFG2_MODE_MASK(channel))
> +					    | (CTF2304_FAN_CFG2_DCY_MODE
> +					       << CTF2304_FAN_CFG2_MODE_SHIFT(channel));
> +		} else if (val == 2) {
> +			data->fan_config2 = (data->fan_config2
> +					     & ~CTF2304_FAN_CFG2_MODE_MASK(channel))
> +					    | (CTF2304_FAN_CFG2_RPM_MODE
> +					       << CTF2304_FAN_CFG2_MODE_SHIFT(channel));
> +		} else {
> +			err = -EINVAL;
> +			break;
> +		}
> +		err = i2c_smbus_write_word_swapped(client,
> +						   CTF2304_REG_FAN_CONFIG2,
> +						   data->fan_config2);
> +		break;
> +	default:
> +		err = -EOPNOTSUPP;
> +		break;
> +	}
>  
>  	mutex_unlock(&data->update_lock);
>  
> -	return count;
> +	return err;
>  }
>  
> -static ssize_t get_pwm(struct device *dev,
> -		       struct device_attribute *devattr, char *buf)
> +static int ctf2304_read(struct device *dev, enum hwmon_sensor_types type,
> +			u32 attr, int channel, long *val)
>  {
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
> -	struct ctf2304_data *data = ctf2304_update_device(dev);
> -	int pwm;
> -
> -	pwm = data->pwm[attr->index] >> 8;
> -
> -	return sprintf(buf, "%d\n", pwm);
> -}
> -
> -static ssize_t set_pwm(struct device *dev,
> -		       struct device_attribute *devattr,
> -		       const char *buf, size_t count)
> -{
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
> -	struct ctf2304_data *data = dev_get_drvdata(dev);
> -	struct i2c_client *client = data->client;
> -	unsigned long pwm;
> -	int err;
> -
> -	err = kstrtoul(buf, 10, &pwm);
> -	if (err)
> -		return err;
> -
> -	pwm = clamp_val(pwm, 0, 255);
> -
> -	mutex_lock(&data->update_lock);
> -
> -	data->pwm[attr->index] = (data->pwm[attr->index] & 0xFF) | (pwm << 8);
> -	i2c_smbus_write_word_swapped(client,
> -			CTF2304_REG_PWMOUT(attr->index),
> -			data->pwm[attr->index]);
> -
> -	mutex_unlock(&data->update_lock);
> -
> -	return count;
> +	switch (type) {
> +	case hwmon_temp:
> +		return ctf2304_read_temp(dev, attr, channel, val);
> +	case hwmon_in:
> +		return ctf2304_read_in(dev, attr, channel, val);
> +	case hwmon_fan:
> +		return ctf2304_read_fan(dev, attr, channel, val);
> +	case hwmon_pwm:
> +		return ctf2304_read_pwm(dev, attr, channel, val);
> +	default:
> +		return -EOPNOTSUPP;
> +	}
>  }
>  
> -static ssize_t get_pwm_enable(struct device *dev,
> -			      struct device_attribute *devattr, char *buf)
> +static int ctf2304_write(struct device *dev, enum hwmon_sensor_types type,
> +			 u32 attr, int channel, long val)
>  {
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
> -	struct ctf2304_data *data = ctf2304_update_device(dev);
> -	int config;
> -	int mode;
> -
> -	config = (data->fan_config2 & CTF2304_FAN_CFG2_MODE_MASK(attr->index))
> -		 >> CTF2304_FAN_CFG2_MODE_SHIFT(attr->index);
> -	if (config == CTF2304_FAN_CFG2_RPM_MODE)
> -		mode = 2;
> -	else if (config == CTF2304_FAN_CFG2_DCY_MODE)
> -		mode = 1;
> -	else
> -		mode = 0;
> -
> -	return sprintf(buf, "%d\n", mode);
> +	switch (type) {
> +	case hwmon_fan:
> +		return ctf2304_write_fan(dev, attr, channel, val);
> +	case hwmon_pwm:
> +		return ctf2304_write_pwm(dev, attr, channel, val);
> +	default:
> +		return -EOPNOTSUPP;
> +	}
>  }
>  
> -static ssize_t set_pwm_enable(struct device *dev,
> -			      struct device_attribute *devattr,
> -			      const char *buf, size_t count)
> +static umode_t ctf2304_is_visible(const void *data,
> +				  enum hwmon_sensor_types type,
> +				  u32 attr, int channel)
>  {
> -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
> -	struct ctf2304_data *data = dev_get_drvdata(dev);
> -	struct i2c_client *client = data->client;
> -	unsigned long mode;
> -	int err;
> -
> -	err = kstrtoul(buf, 10, &mode);
> -	if (err)
> -		return err;
> -
> -	mutex_lock(&data->update_lock);
> -
> -	switch (mode) {
> -	case 0:
> -		data->fan_config2 =
> -			(data->fan_config2
> -			 & ~CTF2304_FAN_CFG2_MODE_MASK(attr->index))
> -			| (CTF2304_FAN_CFG2_TEMP_MODE
> -			   << CTF2304_FAN_CFG2_MODE_SHIFT(attr->index));
> -		break;
> -	case 1:
> -		data->fan_config2 =
> -			(data->fan_config2
> -			 & ~CTF2304_FAN_CFG2_MODE_MASK(attr->index))
> -			| (CTF2304_FAN_CFG2_DCY_MODE
> -			   << CTF2304_FAN_CFG2_MODE_SHIFT(attr->index));
> -		break;
> -	case 2:
> -		data->fan_config2 =
> -			(data->fan_config2
> -			 & ~CTF2304_FAN_CFG2_MODE_MASK(attr->index))
> -			| (CTF2304_FAN_CFG2_RPM_MODE
> -			   << CTF2304_FAN_CFG2_MODE_SHIFT(attr->index));
> +	switch (type) {
> +	case hwmon_temp:
> +	case hwmon_in:
> +		return 0444;
> +	case hwmon_fan:
> +		switch (attr) {
> +		case hwmon_fan_input:
> +			return 0444;
> +		case hwmon_fan_target:
> +		case hwmon_fan_div:
> +			return 0644;
> +		default:
> +			break;
> +		}
>  		break;
> +	case hwmon_pwm:
> +		return 0644;
>  	default:
> -		return -EINVAL;
> +		break;
>  	}
>  
> -	i2c_smbus_write_word_swapped(client,
> -			CTF2304_REG_FAN_CONFIG2, data->fan_config2);
> -
> -	mutex_unlock(&data->update_lock);
> -
> -	return count;
> +	return 0;
>  }
>  
> -static SENSOR_DEVICE_ATTR(temp1_input, 0444, show_temp, NULL, 0);
> -static SENSOR_DEVICE_ATTR(temp2_input, 0444, show_temp, NULL, 1);
> -static SENSOR_DEVICE_ATTR(temp3_input, 0444, show_temp, NULL, 2);
> -static SENSOR_DEVICE_ATTR(temp4_input, 0444, show_temp, NULL, 3);
> -static SENSOR_DEVICE_ATTR(temp5_input, 0444, show_temp, NULL, 4);
> -static SENSOR_DEVICE_ATTR(temp6_input, 0444, show_temp, NULL, 5);
> -static SENSOR_DEVICE_ATTR(temp7_input, 0444, show_temp, NULL, 6);
> -static SENSOR_DEVICE_ATTR(temp8_input, 0444, show_temp, NULL, 7);
> -static SENSOR_DEVICE_ATTR(temp9_input, 0444, show_temp, NULL, 8);
> -static SENSOR_DEVICE_ATTR(in1_input, 0444, show_voltage, NULL, 0);
> -static SENSOR_DEVICE_ATTR(in2_input, 0444, show_voltage, NULL, 1);
> -static SENSOR_DEVICE_ATTR(in3_input, 0444, show_voltage, NULL, 2);
> -static SENSOR_DEVICE_ATTR(in4_input, 0444, show_voltage, NULL, 3);
> -static SENSOR_DEVICE_ATTR(in5_input, 0444, show_voltage, NULL, 4);
> -static SENSOR_DEVICE_ATTR(in6_input, 0444, show_voltage, NULL, 5);
> -static SENSOR_DEVICE_ATTR(in7_input, 0444, show_voltage, NULL, 6);
> -static SENSOR_DEVICE_ATTR(in8_input, 0444, show_voltage, NULL, 7);
> -static SENSOR_DEVICE_ATTR(fan1_input, 0444, get_fan, NULL, 0);
> -static SENSOR_DEVICE_ATTR(fan2_input, 0444, get_fan, NULL, 1);
> -static SENSOR_DEVICE_ATTR(fan3_input, 0444, get_fan, NULL, 2);
> -static SENSOR_DEVICE_ATTR(fan4_input, 0444, get_fan, NULL, 3);
> -static SENSOR_DEVICE_ATTR(fan1_target, 0644,
> -		get_fan_target, set_fan_target, 0);
> -static SENSOR_DEVICE_ATTR(fan2_target, 0644,
> -		get_fan_target, set_fan_target, 1);
> -static SENSOR_DEVICE_ATTR(fan3_target, 0644,
> -		get_fan_target, set_fan_target, 2);
> -static SENSOR_DEVICE_ATTR(fan4_target, 0644,
> -		get_fan_target, set_fan_target, 3);
> -static SENSOR_DEVICE_ATTR(fan1_div, 0644, show_fan_div, set_fan_div, 0);
> -static SENSOR_DEVICE_ATTR(fan2_div, 0644, show_fan_div, set_fan_div, 1);
> -static SENSOR_DEVICE_ATTR(fan3_div, 0644, show_fan_div, set_fan_div, 2);
> -static SENSOR_DEVICE_ATTR(fan4_div, 0644, show_fan_div, set_fan_div, 3);
> -static SENSOR_DEVICE_ATTR(pwm1, 0644, get_pwm, set_pwm, 0);
> -static SENSOR_DEVICE_ATTR(pwm2, 0644, get_pwm, set_pwm, 1);
> -static SENSOR_DEVICE_ATTR(pwm3, 0644, get_pwm, set_pwm, 2);
> -static SENSOR_DEVICE_ATTR(pwm4, 0644, get_pwm, set_pwm, 3);
> -static SENSOR_DEVICE_ATTR(pwm1_enable, 0644,
> -		get_pwm_enable, set_pwm_enable, 0);
> -static SENSOR_DEVICE_ATTR(pwm2_enable, 0644,
> -		get_pwm_enable, set_pwm_enable, 1);
> -static SENSOR_DEVICE_ATTR(pwm3_enable, 0644,
> -		get_pwm_enable, set_pwm_enable, 2);
> -static SENSOR_DEVICE_ATTR(pwm4_enable, 0644,
> -		get_pwm_enable, set_pwm_enable, 3);
> -
> -static struct attribute *ctf2304_attrs[] = {
> -	&sensor_dev_attr_temp1_input.dev_attr.attr,
> -	&sensor_dev_attr_temp2_input.dev_attr.attr,
> -	&sensor_dev_attr_temp3_input.dev_attr.attr,
> -	&sensor_dev_attr_temp4_input.dev_attr.attr,
> -	&sensor_dev_attr_temp5_input.dev_attr.attr,
> -	&sensor_dev_attr_temp6_input.dev_attr.attr,
> -	&sensor_dev_attr_temp7_input.dev_attr.attr,
> -	&sensor_dev_attr_temp8_input.dev_attr.attr,
> -	&sensor_dev_attr_temp9_input.dev_attr.attr,
> -	&sensor_dev_attr_in1_input.dev_attr.attr,
> -	&sensor_dev_attr_in2_input.dev_attr.attr,
> -	&sensor_dev_attr_in3_input.dev_attr.attr,
> -	&sensor_dev_attr_in4_input.dev_attr.attr,
> -	&sensor_dev_attr_in5_input.dev_attr.attr,
> -	&sensor_dev_attr_in6_input.dev_attr.attr,
> -	&sensor_dev_attr_in7_input.dev_attr.attr,
> -	&sensor_dev_attr_in8_input.dev_attr.attr,
> -	&sensor_dev_attr_fan1_input.dev_attr.attr,
> -	&sensor_dev_attr_fan2_input.dev_attr.attr,
> -	&sensor_dev_attr_fan3_input.dev_attr.attr,
> -	&sensor_dev_attr_fan4_input.dev_attr.attr,
> -	&sensor_dev_attr_fan1_target.dev_attr.attr,
> -	&sensor_dev_attr_fan2_target.dev_attr.attr,
> -	&sensor_dev_attr_fan3_target.dev_attr.attr,
> -	&sensor_dev_attr_fan4_target.dev_attr.attr,
> -	&sensor_dev_attr_fan1_div.dev_attr.attr,
> -	&sensor_dev_attr_fan2_div.dev_attr.attr,
> -	&sensor_dev_attr_fan3_div.dev_attr.attr,
> -	&sensor_dev_attr_fan4_div.dev_attr.attr,
> -	&sensor_dev_attr_pwm1.dev_attr.attr,
> -	&sensor_dev_attr_pwm2.dev_attr.attr,
> -	&sensor_dev_attr_pwm3.dev_attr.attr,
> -	&sensor_dev_attr_pwm4.dev_attr.attr,
> -	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
> -	&sensor_dev_attr_pwm2_enable.dev_attr.attr,
> -	&sensor_dev_attr_pwm3_enable.dev_attr.attr,
> -	&sensor_dev_attr_pwm4_enable.dev_attr.attr,
> +static const struct hwmon_channel_info *ctf2304_info[] = {
> +	HWMON_CHANNEL_INFO(temp,
> +			   HWMON_T_INPUT,
> +			   HWMON_T_INPUT,
> +			   HWMON_T_INPUT,
> +			   HWMON_T_INPUT,
> +			   HWMON_T_INPUT,
> +			   HWMON_T_INPUT,
> +			   HWMON_T_INPUT,
> +			   HWMON_T_INPUT,
> +			   HWMON_T_INPUT),
> +	HWMON_CHANNEL_INFO(in,
> +			   HWMON_I_INPUT,
> +			   HWMON_I_INPUT,
> +			   HWMON_I_INPUT,
> +			   HWMON_I_INPUT,
> +			   HWMON_I_INPUT,
> +			   HWMON_I_INPUT,
> +			   HWMON_I_INPUT,
> +			   HWMON_I_INPUT),
> +	HWMON_CHANNEL_INFO(fan,
> +			   HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV,
> +			   HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV,
> +			   HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV,
> +			   HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV),
> +	HWMON_CHANNEL_INFO(pwm,
> +			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
> +			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
> +			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
> +			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
>  	NULL
>  };
>  
> -ATTRIBUTE_GROUPS(ctf2304);
> +static const struct hwmon_ops ctf2304_hwmon_ops = {
> +	.is_visible = ctf2304_is_visible,
> +	.read = ctf2304_read,
> +	.write = ctf2304_write,
> +};
>  
> -static int ctf2304_probe(struct i2c_client *client,
> -			 const struct i2c_device_id *id)
> +static const struct hwmon_chip_info ctf2304_chip_info = {
> +	.ops = &ctf2304_hwmon_ops,
> +	.info = ctf2304_info,
> +};
> +
> +static int ctf2304_probe(struct i2c_client *client)
>  {
> +	struct i2c_adapter *adapter = client->adapter;
>  	struct device *dev = &client->dev;
>  	struct ctf2304_data *data;
>  	struct device *hwmon_dev;
>  
> +	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
> +		return -ENODEV;
> +
>  	data = devm_kzalloc(dev, sizeof(struct ctf2304_data), GFP_KERNEL);
>  	if (!data)
>  		return -ENOMEM;
> @@ -525,9 +478,10 @@ static int ctf2304_probe(struct i2c_client *client,
>  	data->client = client;
>  	mutex_init(&data->update_lock);
>  
> -	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
> -							   data,
> -							   ctf2304_groups);
> +	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
> +							 data,
> +							 &ctf2304_chip_info,
> +							 NULL);
>  
>  	return PTR_ERR_OR_ZERO(hwmon_dev);
>  }
> @@ -540,10 +494,10 @@ MODULE_DEVICE_TABLE(i2c, ctf2304_id);
>  
>  static struct i2c_driver ctf2304_driver = {
>  	.class		= I2C_CLASS_HWMON,
> -	.probe		= ctf2304_probe,
>  	.driver = {
>  		.name	= "ctf2304",
>  	},
> +	.probe_new	= ctf2304_probe,
>  	.id_table	= ctf2304_id,
>  };
>  
> -- 
> 2.26.3
>
diff mbox series

Patch

diff --git a/Documentation/hwmon/ctf2304.rst b/Documentation/hwmon/ctf2304.rst
index d0e9ef9ea52e..46f4b1671795 100644
--- a/Documentation/hwmon/ctf2304.rst
+++ b/Documentation/hwmon/ctf2304.rst
@@ -21,8 +21,9 @@  Description
 
 This driver implements support for the Sensylink CTF2304 chip.
 
-The CTF2304 controls the speeds of up to four fans using four independent
-PWM outputs with local and remote temperature and remote voltage sensing.
+The CTF2304 is a system level thermal management solution chip, including
+1-channel local temperature, 8-channels remote temperature or voltage monitor
+and 4-channels fan speed monitor and driver with I2C/SMBus digital interface.
 
 
 Sysfs entries
@@ -31,11 +32,11 @@  Sysfs entries
 ================== === =======================================================
 fan[1-4]_input     RO  fan tachometer speed in RPM
 fan[1-4]_target    RW  desired fan speed in RPM
-fan[1-4]_div       RW  sets the RPM range of the fan.
+fan[1-4]_div       RW  sets the RPM range of the fan
 pwm[1-4]_enable    RW  regulator mode,
                        0=auto temperature mode, 1=manual mode, 2=rpm mode
 pwm[1-4]           RW  read: current pwm duty cycle,
                        write: target pwm duty cycle (0-255)
-in[1-8]_input      RO  measured output voltage
+in[0-7]_input      RO  measured output voltage
 temp[1-9]_input    RO  measured temperature
 ================== === =======================================================
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index f1fe75f596a5..a74cd43a3916 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -54,6 +54,7 @@  Hardware Monitoring Kernel Drivers
    coretemp
    corsair-cpro
    corsair-psu
+   ctf2304
    da9052
    da9055
    dell-smm-hwmon
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index da9fbb0f8af3..df30a5a921de 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -478,8 +478,9 @@  config SENSORS_CTF2304
 	tristate "Sensylink CTF2304 sensor chip"
 	depends on I2C
 	help
-	  If you say yes here you get support for PWM and Fan Controller
-	  with temperature and voltage sensing.
+	  If you say yes here you get support for 1-channel local temperature,
+	  8-channels remote temperature or voltage monitor and 4-channels fan
+	  speed monitor and driver.
 
 	  This driver can also be built as a module. If so, the module
 	  will be called ctf2304.
diff --git a/drivers/hwmon/ctf2304.c b/drivers/hwmon/ctf2304.c
index 5788740a57e5..42939e39babf 100644
--- a/drivers/hwmon/ctf2304.c
+++ b/drivers/hwmon/ctf2304.c
@@ -8,7 +8,6 @@ 
 
 #include <linux/err.h>
 #include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
 #include <linux/i2c.h>
 #include <linux/init.h>
 #include <linux/jiffies.h>
@@ -47,9 +46,12 @@ 
 #define FAN_RPM_MIN			480
 #define FAN_RPM_MAX			1966080
 
-#define FAN_COUNT_REG_MAX		0xFFF
+#define FAN_COUNT_REG_MAX		0xFFF0
 
-#define TEMP_FROM_REG(reg)		(((reg) * 1000) >> 8)
+#define TEMP_FROM_REG(reg, tr)		((tr) ? \
+					 (((((reg) & 0x7FF0) * 1000) >> 8) \
+					  + ((reg) >> 15) ? -64000 : 0) : \
+					 (((reg) * 1000) >> 8))
 #define VOLT_FROM_REG(reg, fs)		((((reg) >> 4) * (fs)) >> 12)
 #define DIV_FROM_REG(reg)		(1 << (reg))
 #define DIV_TO_REG(div)			((div == 8) ? 0x3 : \
@@ -71,7 +73,7 @@ 
 struct ctf2304_data {
 	struct i2c_client *client;
 	struct mutex update_lock;
-	char valid; /* zero until following fields are valid */
+	bool valid; /* zero until following fields are valid */
 	unsigned long last_updated; /* in jiffies */
 
 	/* register values */
@@ -89,6 +91,7 @@  static struct ctf2304_data *ctf2304_update_device(struct device *dev)
 {
 	struct ctf2304_data *data = dev_get_drvdata(dev);
 	struct i2c_client *client = data->client;
+	struct ctf2304_data *ret = data;
 	int i;
 	int rv;
 
@@ -97,427 +100,377 @@  static struct ctf2304_data *ctf2304_update_device(struct device *dev)
 	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
 		rv = i2c_smbus_read_word_swapped(client,
 				CTF2304_REG_LOCAL_TEMP);
-		if (rv >= 0)
-			data->local_temp = rv;
+		if (rv < 0)
+			goto abort;
+		data->local_temp = rv;
 
 		for (i = 0; i < NR_CHANNEL; i++) {
 			rv = i2c_smbus_read_word_swapped(client,
 					CTF2304_REG_REMOTE_CHANNEL(i));
-			if (rv >= 0)
-				data->remote_channel[i] = rv;
+			if (rv < 0)
+				goto abort;
+			data->remote_channel[i] = rv;
 		}
 
 		rv = i2c_smbus_read_word_swapped(client,
 				CTF2304_REG_FAN_CONFIG1);
-		if (rv >= 0)
-			data->fan_config1 = rv;
+		if (rv < 0)
+			goto abort;
+		data->fan_config1 = rv;
 		rv = i2c_smbus_read_word_swapped(client,
 				CTF2304_REG_FAN_CONFIG2);
-		if (rv >= 0)
-			data->fan_config2 = rv;
+		if (rv < 0)
+			goto abort;
+		data->fan_config2 = rv;
 		rv = i2c_smbus_read_word_swapped(client,
 				CTF2304_REG_FAN_RPM_CTRL);
-		if (rv >= 0)
-			data->fan_rpm_ctrl = rv;
+		if (rv < 0)
+			goto abort;
+		data->fan_rpm_ctrl = rv;
 
 		for (i = 0; i < NR_FAN_CHANNEL; i++) {
 			rv = i2c_smbus_read_word_swapped(client,
 					CTF2304_REG_TACH_COUNT(i));
-			if (rv >= 0)
-				data->tach[i] = rv;
+			if (rv < 0)
+				goto abort;
+			data->tach[i] = rv;
 			rv = i2c_smbus_read_word_swapped(client,
 					CTF2304_REG_PWMOUT(i));
-			if (rv >= 0)
-				data->pwm[i] = rv;
+			if (rv < 0)
+				goto abort;
+			data->pwm[i] = rv;
 			rv = i2c_smbus_read_word_swapped(client,
 					CTF2304_REG_TARGET_COUNT(i));
-			if (rv >= 0)
-				data->target_count[i] = rv;
+			if (rv < 0)
+				goto abort;
+			data->target_count[i] = rv;
 		}
 
 		data->last_updated = jiffies;
 		data->valid = true;
 	}
+	goto done;
 
+abort:
+	data->valid = false;
+	ret = ERR_PTR(rv);
+
+done:
 	mutex_unlock(&data->update_lock);
 
 	return data;
 }
 
-static int register_to_temp(u16 reg, u16 config)
-{
-	if (config & CTF2304_FAN_CFG1_TRANGE)
-		return TEMP_FROM_REG(reg & 0x7FF0) + ((reg >> 15) ? -64000:0);
-	else
-		return TEMP_FROM_REG(reg);
-}
-
-static ssize_t show_temp(struct device *dev,
-			 struct device_attribute *devattr, char *buf)
+static int ctf2304_read_temp(struct device *dev, u32 attr, int channel,
+			     long *val)
 {
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 	struct ctf2304_data *data = ctf2304_update_device(dev);
 	u16 reg;
 
-	if (attr->index == 0)
-		reg = data->local_temp;
-	else
-		reg = data->remote_channel[attr->index-1];
-
-	return sprintf(buf, "%d\n", register_to_temp(reg, data->fan_config1));
+	switch (attr) {
+	case hwmon_temp_input:
+		if (channel == 0)
+			reg = data->local_temp;
+		else
+			reg = data->remote_channel[channel-1];
+		*val = TEMP_FROM_REG(reg, (data->fan_config1
+					   & CTF2304_FAN_CFG1_TRANGE));
+		return 0;
+	default:
+		return -EOPNOTSUPP;
+	}
 }
 
+static const int full_scale[8] = { 2560, CTF2304_VCC, 4096, 2048, 1024, 512, 256, 256 };
+
 static int get_full_scale(u16 config)
 {
-	int full_scale;
 	u8 bits;
 
 	bits = (config >> CTF2304_FAN_CFG1_MODE_SHIFT)
 	       & CTF2304_FAN_CFG1_MODE_MASK;
 
-	if (bits == 0x0)
-		full_scale = 2560;
-	else if (bits == 0x1)
-		full_scale = CTF2304_VCC;
-	else if (bits == 0x2)
-		full_scale = 4096;
-	else if (bits == 0x3)
-		full_scale = 2048;
-	else if (bits == 0x4)
-		full_scale = 1024;
-	else if (bits == 0x5)
-		full_scale = 512;
-	else
-		full_scale = 256;
-
-	return full_scale;
+	return full_scale[bits];
 }
 
-static int register_to_volt(u16 reg, u16 config)
+static int ctf2304_read_in(struct device *dev, u32 attr, int channel,
+			   long *val)
 {
-	int full_scale;
-
-	full_scale = get_full_scale(config);
-
-	return VOLT_FROM_REG(reg, full_scale);
-}
-
-static ssize_t show_voltage(struct device *dev,
-			    struct device_attribute *devattr, char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct ctf2304_data *data = ctf2304_update_device(dev);
-	int voltage;
-
-	voltage = register_to_volt(data->remote_channel[attr->index],
-			data->fan_config1);
-
-	return sprintf(buf, "%d\n", voltage);
-}
-
-static ssize_t get_fan(struct device *dev,
-		       struct device_attribute *devattr, char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 	struct ctf2304_data *data = ctf2304_update_device(dev);
-	int rpm;
 
-	rpm = RPM_FROM_REG(data->tach[attr->index]);
-
-	return sprintf(buf, "%d\n", rpm);
+	switch (attr) {
+	case hwmon_temp_input:
+		*val = VOLT_FROM_REG(data->remote_channel[channel],
+				     get_full_scale(data->fan_config1));
+		return 0;
+	default:
+		return -EOPNOTSUPP;
+	}
 }
 
-static ssize_t get_fan_target(struct device *dev,
-			      struct device_attribute *devattr, char *buf)
+static int ctf2304_read_fan(struct device *dev, u32 attr, int channel,
+			    long *val)
 {
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 	struct ctf2304_data *data = ctf2304_update_device(dev);
-	int rpm;
-
-	rpm = RPM_FROM_REG(data->target_count[attr->index]);
+	u8 bits;
 
-	return sprintf(buf, "%d\n", rpm);
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	switch (attr) {
+	case hwmon_fan_input:
+		if (data->tach[channel] == FAN_COUNT_REG_MAX)
+			*val = 0;
+		else
+			*val = RPM_FROM_REG(data->tach[channel]);
+		return 0;
+	case hwmon_fan_target:
+		*val = RPM_FROM_REG(data->target_count[channel]);
+		return 0;
+	case hwmon_fan_div:
+		bits = (data->fan_rpm_ctrl & CTF2304_FAN_DIV_MASK(channel))
+		       >> CTF2304_FAN_DIV_SHIFT(channel);
+		*val = DIV_FROM_REG(bits);
+		return 0;
+	default:
+		return -EOPNOTSUPP;
+	}
 }
 
-static ssize_t set_fan_target(struct device *dev,
-			      struct device_attribute *devattr,
-			      const char *buf, size_t count)
+static int ctf2304_write_fan(struct device *dev, u32 attr, int channel,
+			     long val)
 {
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 	struct ctf2304_data *data = dev_get_drvdata(dev);
 	struct i2c_client *client = data->client;
-	unsigned long rpm;
 	int target_count;
-	int err;
-
-	err = kstrtoul(buf, 10, &rpm);
-	if (err)
-		return err;
-
-	rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
-	target_count = RPM_TO_REG(rpm);
-	target_count = clamp_val(target_count, 0x1, 0xFFF);
+	int err = 0;
 
 	mutex_lock(&data->update_lock);
 
-	data->target_count[attr->index] = target_count << 4;
-	i2c_smbus_write_word_swapped(client,
-			CTF2304_REG_TARGET_COUNT(attr->index),
-			data->target_count[attr->index]);
+	switch (attr) {
+	case hwmon_fan_target:
+		val = clamp_val(val, FAN_RPM_MIN, FAN_RPM_MAX);
+		target_count = RPM_TO_REG(val);
+		target_count = clamp_val(target_count, 0x1, 0xFFF);
+		data->target_count[channel] = target_count << 4;
+		err = i2c_smbus_write_word_swapped(client,
+				CTF2304_REG_TARGET_COUNT(channel),
+				data->target_count[channel]);
+		break;
+	case hwmon_fan_div:
+		data->fan_rpm_ctrl = (data->fan_rpm_ctrl
+				      & ~CTF2304_FAN_DIV_MASK(channel))
+				     | (DIV_TO_REG(val)
+					<< CTF2304_FAN_DIV_SHIFT(channel));
+		err = i2c_smbus_write_word_swapped(client,
+				CTF2304_REG_FAN_RPM_CTRL,
+				data->fan_rpm_ctrl);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
 
 	mutex_unlock(&data->update_lock);
 
-	return count;
+	return err;
 }
 
-static ssize_t show_fan_div(struct device *dev,
-			    struct device_attribute *devattr, char *buf)
+static int ctf2304_read_pwm(struct device *dev, u32 attr, int channel,
+			    long *val)
 {
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 	struct ctf2304_data *data = ctf2304_update_device(dev);
 	u8 bits;
 
-	bits = (data->fan_rpm_ctrl & CTF2304_FAN_DIV_MASK(attr->index))
-	       >> CTF2304_FAN_DIV_SHIFT(attr->index);
-
-	return sprintf(buf, "%d\n", DIV_FROM_REG(bits));
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	switch (attr) {
+	case hwmon_pwm_input:
+		*val = data->pwm[channel] >> 8;
+		return 0;
+	case hwmon_pwm_enable:
+		bits = (data->fan_config2
+			& CTF2304_FAN_CFG2_MODE_MASK(channel))
+		       >> CTF2304_FAN_CFG2_MODE_SHIFT(channel);
+		if (bits == CTF2304_FAN_CFG2_RPM_MODE)
+			*val = 2;
+		else if (bits == CTF2304_FAN_CFG2_DCY_MODE)
+			*val = 1;
+		else
+			*val = 0;
+		return 0;
+	default:
+		return -EOPNOTSUPP;
+	}
 }
 
-static ssize_t set_fan_div(struct device *dev,
-			   struct device_attribute *devattr,
-			   const char *buf, size_t count)
+static int ctf2304_write_pwm(struct device *dev, u32 attr, int channel,
+			     long val)
 {
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 	struct ctf2304_data *data = dev_get_drvdata(dev);
 	struct i2c_client *client = data->client;
-	unsigned long div;
-	int err;
-
-	err = kstrtoul(buf, 10, &div);
-	if (err)
-		return err;
+	int err = 0;
 
 	mutex_lock(&data->update_lock);
 
-	data->fan_rpm_ctrl = (data->fan_rpm_ctrl
-			      & ~CTF2304_FAN_DIV_MASK(attr->index))
-			     | (DIV_TO_REG(div)
-				<< CTF2304_FAN_DIV_SHIFT(attr->index));
-
-	i2c_smbus_write_word_swapped(client,
-			CTF2304_REG_FAN_RPM_CTRL, data->fan_rpm_ctrl);
+	switch (attr) {
+	case hwmon_pwm_input:
+		if (val < 0 || val > 255) {
+			err = -EINVAL;
+			break;
+		}
+		data->pwm[channel] = (data->pwm[channel] & 0xFF) | (val << 8);
+		err = i2c_smbus_write_word_swapped(client,
+						   CTF2304_REG_PWMOUT(channel),
+						   data->pwm[channel]);
+		break;
+	case hwmon_pwm_enable:
+		if (val == 0) {
+			data->fan_config2 = (data->fan_config2
+					     & ~CTF2304_FAN_CFG2_MODE_MASK(channel))
+					    | (CTF2304_FAN_CFG2_TEMP_MODE
+					       << CTF2304_FAN_CFG2_MODE_SHIFT(channel));
+		} else if (val == 1) {
+			data->fan_config2 = (data->fan_config2
+					     & ~CTF2304_FAN_CFG2_MODE_MASK(channel))
+					    | (CTF2304_FAN_CFG2_DCY_MODE
+					       << CTF2304_FAN_CFG2_MODE_SHIFT(channel));
+		} else if (val == 2) {
+			data->fan_config2 = (data->fan_config2
+					     & ~CTF2304_FAN_CFG2_MODE_MASK(channel))
+					    | (CTF2304_FAN_CFG2_RPM_MODE
+					       << CTF2304_FAN_CFG2_MODE_SHIFT(channel));
+		} else {
+			err = -EINVAL;
+			break;
+		}
+		err = i2c_smbus_write_word_swapped(client,
+						   CTF2304_REG_FAN_CONFIG2,
+						   data->fan_config2);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
 
 	mutex_unlock(&data->update_lock);
 
-	return count;
+	return err;
 }
 
-static ssize_t get_pwm(struct device *dev,
-		       struct device_attribute *devattr, char *buf)
+static int ctf2304_read(struct device *dev, enum hwmon_sensor_types type,
+			u32 attr, int channel, long *val)
 {
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct ctf2304_data *data = ctf2304_update_device(dev);
-	int pwm;
-
-	pwm = data->pwm[attr->index] >> 8;
-
-	return sprintf(buf, "%d\n", pwm);
-}
-
-static ssize_t set_pwm(struct device *dev,
-		       struct device_attribute *devattr,
-		       const char *buf, size_t count)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct ctf2304_data *data = dev_get_drvdata(dev);
-	struct i2c_client *client = data->client;
-	unsigned long pwm;
-	int err;
-
-	err = kstrtoul(buf, 10, &pwm);
-	if (err)
-		return err;
-
-	pwm = clamp_val(pwm, 0, 255);
-
-	mutex_lock(&data->update_lock);
-
-	data->pwm[attr->index] = (data->pwm[attr->index] & 0xFF) | (pwm << 8);
-	i2c_smbus_write_word_swapped(client,
-			CTF2304_REG_PWMOUT(attr->index),
-			data->pwm[attr->index]);
-
-	mutex_unlock(&data->update_lock);
-
-	return count;
+	switch (type) {
+	case hwmon_temp:
+		return ctf2304_read_temp(dev, attr, channel, val);
+	case hwmon_in:
+		return ctf2304_read_in(dev, attr, channel, val);
+	case hwmon_fan:
+		return ctf2304_read_fan(dev, attr, channel, val);
+	case hwmon_pwm:
+		return ctf2304_read_pwm(dev, attr, channel, val);
+	default:
+		return -EOPNOTSUPP;
+	}
 }
 
-static ssize_t get_pwm_enable(struct device *dev,
-			      struct device_attribute *devattr, char *buf)
+static int ctf2304_write(struct device *dev, enum hwmon_sensor_types type,
+			 u32 attr, int channel, long val)
 {
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct ctf2304_data *data = ctf2304_update_device(dev);
-	int config;
-	int mode;
-
-	config = (data->fan_config2 & CTF2304_FAN_CFG2_MODE_MASK(attr->index))
-		 >> CTF2304_FAN_CFG2_MODE_SHIFT(attr->index);
-	if (config == CTF2304_FAN_CFG2_RPM_MODE)
-		mode = 2;
-	else if (config == CTF2304_FAN_CFG2_DCY_MODE)
-		mode = 1;
-	else
-		mode = 0;
-
-	return sprintf(buf, "%d\n", mode);
+	switch (type) {
+	case hwmon_fan:
+		return ctf2304_write_fan(dev, attr, channel, val);
+	case hwmon_pwm:
+		return ctf2304_write_pwm(dev, attr, channel, val);
+	default:
+		return -EOPNOTSUPP;
+	}
 }
 
-static ssize_t set_pwm_enable(struct device *dev,
-			      struct device_attribute *devattr,
-			      const char *buf, size_t count)
+static umode_t ctf2304_is_visible(const void *data,
+				  enum hwmon_sensor_types type,
+				  u32 attr, int channel)
 {
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct ctf2304_data *data = dev_get_drvdata(dev);
-	struct i2c_client *client = data->client;
-	unsigned long mode;
-	int err;
-
-	err = kstrtoul(buf, 10, &mode);
-	if (err)
-		return err;
-
-	mutex_lock(&data->update_lock);
-
-	switch (mode) {
-	case 0:
-		data->fan_config2 =
-			(data->fan_config2
-			 & ~CTF2304_FAN_CFG2_MODE_MASK(attr->index))
-			| (CTF2304_FAN_CFG2_TEMP_MODE
-			   << CTF2304_FAN_CFG2_MODE_SHIFT(attr->index));
-		break;
-	case 1:
-		data->fan_config2 =
-			(data->fan_config2
-			 & ~CTF2304_FAN_CFG2_MODE_MASK(attr->index))
-			| (CTF2304_FAN_CFG2_DCY_MODE
-			   << CTF2304_FAN_CFG2_MODE_SHIFT(attr->index));
-		break;
-	case 2:
-		data->fan_config2 =
-			(data->fan_config2
-			 & ~CTF2304_FAN_CFG2_MODE_MASK(attr->index))
-			| (CTF2304_FAN_CFG2_RPM_MODE
-			   << CTF2304_FAN_CFG2_MODE_SHIFT(attr->index));
+	switch (type) {
+	case hwmon_temp:
+	case hwmon_in:
+		return 0444;
+	case hwmon_fan:
+		switch (attr) {
+		case hwmon_fan_input:
+			return 0444;
+		case hwmon_fan_target:
+		case hwmon_fan_div:
+			return 0644;
+		default:
+			break;
+		}
 		break;
+	case hwmon_pwm:
+		return 0644;
 	default:
-		return -EINVAL;
+		break;
 	}
 
-	i2c_smbus_write_word_swapped(client,
-			CTF2304_REG_FAN_CONFIG2, data->fan_config2);
-
-	mutex_unlock(&data->update_lock);
-
-	return count;
+	return 0;
 }
 
-static SENSOR_DEVICE_ATTR(temp1_input, 0444, show_temp, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp2_input, 0444, show_temp, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp3_input, 0444, show_temp, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp4_input, 0444, show_temp, NULL, 3);
-static SENSOR_DEVICE_ATTR(temp5_input, 0444, show_temp, NULL, 4);
-static SENSOR_DEVICE_ATTR(temp6_input, 0444, show_temp, NULL, 5);
-static SENSOR_DEVICE_ATTR(temp7_input, 0444, show_temp, NULL, 6);
-static SENSOR_DEVICE_ATTR(temp8_input, 0444, show_temp, NULL, 7);
-static SENSOR_DEVICE_ATTR(temp9_input, 0444, show_temp, NULL, 8);
-static SENSOR_DEVICE_ATTR(in1_input, 0444, show_voltage, NULL, 0);
-static SENSOR_DEVICE_ATTR(in2_input, 0444, show_voltage, NULL, 1);
-static SENSOR_DEVICE_ATTR(in3_input, 0444, show_voltage, NULL, 2);
-static SENSOR_DEVICE_ATTR(in4_input, 0444, show_voltage, NULL, 3);
-static SENSOR_DEVICE_ATTR(in5_input, 0444, show_voltage, NULL, 4);
-static SENSOR_DEVICE_ATTR(in6_input, 0444, show_voltage, NULL, 5);
-static SENSOR_DEVICE_ATTR(in7_input, 0444, show_voltage, NULL, 6);
-static SENSOR_DEVICE_ATTR(in8_input, 0444, show_voltage, NULL, 7);
-static SENSOR_DEVICE_ATTR(fan1_input, 0444, get_fan, NULL, 0);
-static SENSOR_DEVICE_ATTR(fan2_input, 0444, get_fan, NULL, 1);
-static SENSOR_DEVICE_ATTR(fan3_input, 0444, get_fan, NULL, 2);
-static SENSOR_DEVICE_ATTR(fan4_input, 0444, get_fan, NULL, 3);
-static SENSOR_DEVICE_ATTR(fan1_target, 0644,
-		get_fan_target, set_fan_target, 0);
-static SENSOR_DEVICE_ATTR(fan2_target, 0644,
-		get_fan_target, set_fan_target, 1);
-static SENSOR_DEVICE_ATTR(fan3_target, 0644,
-		get_fan_target, set_fan_target, 2);
-static SENSOR_DEVICE_ATTR(fan4_target, 0644,
-		get_fan_target, set_fan_target, 3);
-static SENSOR_DEVICE_ATTR(fan1_div, 0644, show_fan_div, set_fan_div, 0);
-static SENSOR_DEVICE_ATTR(fan2_div, 0644, show_fan_div, set_fan_div, 1);
-static SENSOR_DEVICE_ATTR(fan3_div, 0644, show_fan_div, set_fan_div, 2);
-static SENSOR_DEVICE_ATTR(fan4_div, 0644, show_fan_div, set_fan_div, 3);
-static SENSOR_DEVICE_ATTR(pwm1, 0644, get_pwm, set_pwm, 0);
-static SENSOR_DEVICE_ATTR(pwm2, 0644, get_pwm, set_pwm, 1);
-static SENSOR_DEVICE_ATTR(pwm3, 0644, get_pwm, set_pwm, 2);
-static SENSOR_DEVICE_ATTR(pwm4, 0644, get_pwm, set_pwm, 3);
-static SENSOR_DEVICE_ATTR(pwm1_enable, 0644,
-		get_pwm_enable, set_pwm_enable, 0);
-static SENSOR_DEVICE_ATTR(pwm2_enable, 0644,
-		get_pwm_enable, set_pwm_enable, 1);
-static SENSOR_DEVICE_ATTR(pwm3_enable, 0644,
-		get_pwm_enable, set_pwm_enable, 2);
-static SENSOR_DEVICE_ATTR(pwm4_enable, 0644,
-		get_pwm_enable, set_pwm_enable, 3);
-
-static struct attribute *ctf2304_attrs[] = {
-	&sensor_dev_attr_temp1_input.dev_attr.attr,
-	&sensor_dev_attr_temp2_input.dev_attr.attr,
-	&sensor_dev_attr_temp3_input.dev_attr.attr,
-	&sensor_dev_attr_temp4_input.dev_attr.attr,
-	&sensor_dev_attr_temp5_input.dev_attr.attr,
-	&sensor_dev_attr_temp6_input.dev_attr.attr,
-	&sensor_dev_attr_temp7_input.dev_attr.attr,
-	&sensor_dev_attr_temp8_input.dev_attr.attr,
-	&sensor_dev_attr_temp9_input.dev_attr.attr,
-	&sensor_dev_attr_in1_input.dev_attr.attr,
-	&sensor_dev_attr_in2_input.dev_attr.attr,
-	&sensor_dev_attr_in3_input.dev_attr.attr,
-	&sensor_dev_attr_in4_input.dev_attr.attr,
-	&sensor_dev_attr_in5_input.dev_attr.attr,
-	&sensor_dev_attr_in6_input.dev_attr.attr,
-	&sensor_dev_attr_in7_input.dev_attr.attr,
-	&sensor_dev_attr_in8_input.dev_attr.attr,
-	&sensor_dev_attr_fan1_input.dev_attr.attr,
-	&sensor_dev_attr_fan2_input.dev_attr.attr,
-	&sensor_dev_attr_fan3_input.dev_attr.attr,
-	&sensor_dev_attr_fan4_input.dev_attr.attr,
-	&sensor_dev_attr_fan1_target.dev_attr.attr,
-	&sensor_dev_attr_fan2_target.dev_attr.attr,
-	&sensor_dev_attr_fan3_target.dev_attr.attr,
-	&sensor_dev_attr_fan4_target.dev_attr.attr,
-	&sensor_dev_attr_fan1_div.dev_attr.attr,
-	&sensor_dev_attr_fan2_div.dev_attr.attr,
-	&sensor_dev_attr_fan3_div.dev_attr.attr,
-	&sensor_dev_attr_fan4_div.dev_attr.attr,
-	&sensor_dev_attr_pwm1.dev_attr.attr,
-	&sensor_dev_attr_pwm2.dev_attr.attr,
-	&sensor_dev_attr_pwm3.dev_attr.attr,
-	&sensor_dev_attr_pwm4.dev_attr.attr,
-	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
-	&sensor_dev_attr_pwm2_enable.dev_attr.attr,
-	&sensor_dev_attr_pwm3_enable.dev_attr.attr,
-	&sensor_dev_attr_pwm4_enable.dev_attr.attr,
+static const struct hwmon_channel_info *ctf2304_info[] = {
+	HWMON_CHANNEL_INFO(temp,
+			   HWMON_T_INPUT,
+			   HWMON_T_INPUT,
+			   HWMON_T_INPUT,
+			   HWMON_T_INPUT,
+			   HWMON_T_INPUT,
+			   HWMON_T_INPUT,
+			   HWMON_T_INPUT,
+			   HWMON_T_INPUT,
+			   HWMON_T_INPUT),
+	HWMON_CHANNEL_INFO(in,
+			   HWMON_I_INPUT,
+			   HWMON_I_INPUT,
+			   HWMON_I_INPUT,
+			   HWMON_I_INPUT,
+			   HWMON_I_INPUT,
+			   HWMON_I_INPUT,
+			   HWMON_I_INPUT,
+			   HWMON_I_INPUT),
+	HWMON_CHANNEL_INFO(fan,
+			   HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV,
+			   HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV,
+			   HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV,
+			   HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV),
+	HWMON_CHANNEL_INFO(pwm,
+			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
 	NULL
 };
 
-ATTRIBUTE_GROUPS(ctf2304);
+static const struct hwmon_ops ctf2304_hwmon_ops = {
+	.is_visible = ctf2304_is_visible,
+	.read = ctf2304_read,
+	.write = ctf2304_write,
+};
 
-static int ctf2304_probe(struct i2c_client *client,
-			 const struct i2c_device_id *id)
+static const struct hwmon_chip_info ctf2304_chip_info = {
+	.ops = &ctf2304_hwmon_ops,
+	.info = ctf2304_info,
+};
+
+static int ctf2304_probe(struct i2c_client *client)
 {
+	struct i2c_adapter *adapter = client->adapter;
 	struct device *dev = &client->dev;
 	struct ctf2304_data *data;
 	struct device *hwmon_dev;
 
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
+		return -ENODEV;
+
 	data = devm_kzalloc(dev, sizeof(struct ctf2304_data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
@@ -525,9 +478,10 @@  static int ctf2304_probe(struct i2c_client *client,
 	data->client = client;
 	mutex_init(&data->update_lock);
 
-	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
-							   data,
-							   ctf2304_groups);
+	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+							 data,
+							 &ctf2304_chip_info,
+							 NULL);
 
 	return PTR_ERR_OR_ZERO(hwmon_dev);
 }
@@ -540,10 +494,10 @@  MODULE_DEVICE_TABLE(i2c, ctf2304_id);
 
 static struct i2c_driver ctf2304_driver = {
 	.class		= I2C_CLASS_HWMON,
-	.probe		= ctf2304_probe,
 	.driver = {
 		.name	= "ctf2304",
 	},
+	.probe_new	= ctf2304_probe,
 	.id_table	= ctf2304_id,
 };