So you don't have to compare them
Please disregard the previous one.
I will add the next patch to this next time.
> Thanks,
Il Han
> Signed-off-by: Il Han <corone.il.han@xxxxxxxxx <mailto:corone.il.han@xxxxxxxxx>>
> ---
> Documentation/hwmon/ctf2304.rst | 41 +++
> Documentation/hwmon/index.rst | 1 +
> drivers/hwmon/Kconfig | 10 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/ctf2304.c | 522 ++++++++++++++++++++++++++++++++
> 5 files changed, 575 insertions(+)
> create mode 100644 Documentation/hwmon/ctf2304.rst
> create mode 100644 drivers/hwmon/ctf2304.c
>
> diff --git a/Documentation/hwmon/ctf2304.rst b/Documentation/hwmon/ctf2304.rst
> new file mode 100644
> index 000000000000..e1584524d612
> --- /dev/null
> +++ b/Documentation/hwmon/ctf2304.rst
> @@ -0,0 +1,41 @@
> +.. SPDX-License-Identifier: GPL-2.0-or-later
> +
> +Kernel driver ctf2304
> +=====================
> +
> +Supported chips:
> +
> + * Sensylink CTF2304
> +
> + Prefix: 'ctf2304'
> +
> + Addresses scanned: -
> +
> + Datasheet:
> +
> +Author: Il Han <corone.il.han@xxxxxxxxx <mailto:corone.il.han@xxxxxxxxx>>
> +
> +
> +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.
> +
> +
> +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
> +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[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 5b3b76477b0e..da9fbb0f8af3 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -474,6 +474,16 @@ config SENSORS_CORSAIR_PSU
> This driver can also be built as a module. If so, the module
> will be called corsair-psu.
>
> +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.
> +
> + This driver can also be built as a module. If so, the module
> + will be called ctf2304.
> +
> config SENSORS_DRIVETEMP
> tristate "Hard disk drives with temperature sensors"
> depends on SCSI && ATA
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 88712b5031c8..3742b52f032d 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -60,6 +60,7 @@ obj-$(CONFIG_SENSORS_BT1_PVT) += bt1-pvt.o
> obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
> obj-$(CONFIG_SENSORS_CORSAIR_CPRO) += corsair-cpro.o
> obj-$(CONFIG_SENSORS_CORSAIR_PSU) += corsair-psu.o
> +obj-$(CONFIG_SENSORS_CTF2304) += ctf2304.o
> obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o
> obj-$(CONFIG_SENSORS_DA9055)+= da9055-hwmon.o
> obj-$(CONFIG_SENSORS_DELL_SMM) += dell-smm-hwmon.o
> diff --git a/drivers/hwmon/ctf2304.c b/drivers/hwmon/ctf2304.c
> new file mode 100644
> index 000000000000..102c41957219
> --- /dev/null
> +++ b/drivers/hwmon/ctf2304.c
> @@ -0,0 +1,522 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * ctf2304.c - Part of lm_sensors, Linux kernel modules for hardware
> + * monitoring.
> + *
> + * (C) 2023 by Il Han <corone.il.han@xxxxxxxxx <mailto:corone.il.han@xxxxxxxxx>>
> + */
> +
> +#include <linux/err.h>
> +#include <linux/hwmon.h>
> +#include <linux/i2c.h>
> +#include <linux/init.h>
> +#include <linux/jiffies.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +
> +/* CTF2304 registers */
> +#define CTF2304_REG_LOCAL_TEMP 0x00
> +#define CTF2304_REG_REMOTE_CHANNEL(ch) (0x01 + (ch))
> +#define CTF2304_REG_TACH_COUNT(ch) (0x09 + (ch))
> +#define CTF2304_REG_FAN_CONFIG1 0x10
> +#define CTF2304_REG_FAN_CONFIG2 0x11
> +#define CTF2304_REG_FAN_RPM_CTRL 0x18
> +#define CTF2304_REG_PWMOUT(ch) (0x40 + (ch))
> +#define CTF2304_REG_TARGET_COUNT(ch) (0x44 + (ch))
> +
> +/* Fan Configure1 register bits */
> +#define CTF2304_FAN_CFG1_TRANGE 0x0400
> +#define CTF2304_FAN_CFG1_MODE_MASK (0x7)
> +#define CTF2304_FAN_CFG1_MODE_SHIFT 7
> +
> +/* Fan Configure2 register bits */
> +#define CTF2304_FAN_CFG2_MODE_MASK(ch) (0x6 << (ch) * 4)
> +#define CTF2304_FAN_CFG2_MODE_SHIFT(ch) (1 + (ch) * 4)
> +#define CTF2304_FAN_CFG2_DCY_MODE 0
> +#define CTF2304_FAN_CFG2_RPM_MODE 1
> +#define CTF2304_FAN_CFG2_TEMP_MODE 2
> +#define CTF2304_FAN_CFG2_MAX_MODE 3
> +
> +/* Fan RPM CTRL register bits */
> +#define CTF2304_FAN_DIV_MASK(ch) (0x6 << (ch) * 4)
> +#define CTF2304_FAN_DIV_SHIFT(ch) (1 + (ch) * 4)
> +
> +#define CTF2304_VCC 3300
> +
> +#define FAN_RPM_MIN 480
> +#define FAN_RPM_MAX 1966080
> +
> +#define FAN_COUNT_REG_MAX 0xFFF0
> +
> +#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 : \
> + (div == 4) ? 0x2 : \
> + (div == 1) ? 0x0 : 0x1)
> +#define RPM_FROM_REG(reg) (((reg) >> 4) ? \
> + ((32768 * 60) / ((reg) >> 4)) : \
> + FAN_RPM_MAX)
> +#define RPM_TO_REG(rpm) ((rpm) ? \
> + ((32768 * 60) / (rpm)) : \
> + FAN_COUNT_REG_MAX)
> +
> +#define NR_CHANNEL 8
> +#define NR_FAN_CHANNEL 4
> +
> +/*
> + * Client data (each client gets its own)
> + */
> +struct ctf2304_data {
> + struct i2c_client *client;
> + struct mutex update_lock;
> + char valid; /* zero until following fields are valid */
> + unsigned long last_updated; /* in jiffies */
> +
> + /* register values */
> + u16 local_temp;
> + u16 remote_channel[NR_CHANNEL];
> + u16 tach[NR_FAN_CHANNEL];
> + u16 fan_config1;
> + u16 fan_config2;
> + u16 fan_rpm_ctrl;
> + u16 pwm[NR_FAN_CHANNEL];
> + u16 target_count[NR_FAN_CHANNEL];
> +};
> +
> +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;
> +
> + mutex_lock(&data->update_lock);
> +
> + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
> + rv = i2c_smbus_read_word_swapped(client,
> + CTF2304_REG_LOCAL_TEMP);
> + 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)
> + goto abort;
> + data->remote_channel[i] = rv;
> + }
> +
> + rv = i2c_smbus_read_word_swapped(client,
> + CTF2304_REG_FAN_CONFIG1);
> + if (rv < 0)
> + goto abort;
> + data->fan_config1 = rv;
> + rv = i2c_smbus_read_word_swapped(client,
> + CTF2304_REG_FAN_CONFIG2);
> + if (rv < 0)
> + goto abort;
> + data->fan_config2 = rv;
> + rv = i2c_smbus_read_word_swapped(client,
> + CTF2304_REG_FAN_RPM_CTRL);
> + 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)
> + goto abort;
> + data->tach[i] = rv;
> + rv = i2c_smbus_read_word_swapped(client,
> + CTF2304_REG_PWMOUT(i));
> + if (rv < 0)
> + goto abort;
> + data->pwm[i] = rv;
> + rv = i2c_smbus_read_word_swapped(client,
> + CTF2304_REG_TARGET_COUNT(i));
> + 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 ctf2304_read_temp(struct device *dev, u32 attr, int channel,
> + long *val)
> +{
> + struct ctf2304_data *data = ctf2304_update_device(dev);
> + u16 reg;
> +
> + 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 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;
> +}
> +
> +static int ctf2304_read_in(struct device *dev, u32 attr, int channel,
> + long *val)
> +{
> + struct ctf2304_data *data = ctf2304_update_device(dev);
> +
> + 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 int ctf2304_read_fan(struct device *dev, u32 attr, int channel,
> + long *val)
> +{
> + struct ctf2304_data *data = ctf2304_update_device(dev);
> + u8 bits;
> +
> + 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 int ctf2304_write_fan(struct device *dev, u32 attr, int channel,
> + long val)
> +{
> + struct ctf2304_data *data = dev_get_drvdata(dev);
> + struct i2c_client *client = data->client;
> + int target_count;
> + int err = 0;
> +
> + mutex_lock(&data->update_lock);
> +
> + 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 err;
> +}
> +
> +static int ctf2304_read_pwm(struct device *dev, u32 attr, int channel,
> + long *val)
> +{
> + struct ctf2304_data *data = ctf2304_update_device(dev);
> + u8 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 int ctf2304_write_pwm(struct device *dev, u32 attr, int channel,
> + long val)
> +{
> + struct ctf2304_data *data = dev_get_drvdata(dev);
> + struct i2c_client *client = data->client;
> + int err = 0;
> +
> + mutex_lock(&data->update_lock);
> +
> + 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 err;
> +}
> +
> +static int ctf2304_read(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long *val)
> +{
> + 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 int ctf2304_write(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long val)
> +{
> + 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 umode_t ctf2304_is_visible(const void *data,
> + enum hwmon_sensor_types type,
> + u32 attr, int channel)
> +{
> + 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:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +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
> +};
> +
> +static const struct hwmon_ops ctf2304_hwmon_ops = {
> + .is_visible = ctf2304_is_visible,
> + .read = ctf2304_read,
> + .write = ctf2304_write,
> +};
> +
> +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;
> +
> + data->client = client;
> + mutex_init(&data->update_lock);
> +
> + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
> + data,
> + &ctf2304_chip_info,
> + NULL);
> +
> + return PTR_ERR_OR_ZERO(hwmon_dev);
> +}
> +
> +static const struct i2c_device_id ctf2304_id[] = {
> + { "ctf2304", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, ctf2304_id);
> +
> +static struct i2c_driver ctf2304_driver = {
> + .class = I2C_CLASS_HWMON,
> + .driver = {
> + .name = "ctf2304",
> + },
> + .probe_new = ctf2304_probe,
> + .id_table = ctf2304_id,
> +};
> +
> +module_i2c_driver(ctf2304_driver);
> +
> +MODULE_AUTHOR("Il Han <corone.il.han@xxxxxxxxx <mailto:corone.il.han@xxxxxxxxx>>");
> +MODULE_DESCRIPTION("CTF2304 sensor driver");
> +MODULE_LICENSE("GPL");
> --
> 2.26.3
>