ADT7462 can operate in manual mode for fan control. Currently if we want to read fan speed, there is a check if TACH measurement is enabled for a fan. (In fan_enabled function). There is no way to enable TACH measurement currently. This is addressed in this commit. Along with the above support few more features are enabled - Support for setting fan presence. - Support for setting low and high frequency mode. - Support for setting easy config option. - Support for setting the duration of the fan startup timeout. - Once the setting is done, there is a setup complete bit in cfg1 register. Settings this bit will start the monitoring of all selected channels. Added support for that. Based on this, below is the flow to set/get fan speed (example:pwm1) echo 1 > pwm1_enable #Set to manual mode echo 1 > pwm_freq_mode #High freq mode (optional.newly added) echo 1 > fan1_presence #Set fan 1 as present(newly added) echo 1 > fan1_tach_enable #Start TACH measurement-fan1(newly added) echo 1 > setup_complete #Mark as setup complete (newly added) cat fan1_input #Read Fan1 RPM. echo 192 > pwm1 #Change PWM1(has fan1) to 75%(192/255). This is tested on x86 CPU which connects via PCIE to FGPA which has I2C Controller. ADT7462 is connected to the I2C controller(on FPGA). Signed-off-by: Ashwin H <ashwin@xxxxxxxxxxx> --- Documentation/hwmon/adt7462.rst | 22 ++- drivers/hwmon/adt7462.c | 308 ++++++++++++++++++++++++++++++++ 2 files changed, 329 insertions(+), 1 deletion(-) diff --git a/Documentation/hwmon/adt7462.rst b/Documentation/hwmon/adt7462.rst index 139e19696188..a4418ed6f2fa 100644 --- a/Documentation/hwmon/adt7462.rst +++ b/Documentation/hwmon/adt7462.rst @@ -56,7 +56,7 @@ Configuration Notes Besides standard interfaces driver adds the following: -* PWM Control +* PWM Auto Control * pwm#_auto_point1_pwm and temp#_auto_point1_temp and * pwm#_auto_point2_pwm and temp#_auto_point2_temp - @@ -68,3 +68,23 @@ The ADT7462 will scale the pwm between the lower and higher pwm speed when the temperature is between the two temperature boundaries. PWM values range from 0 (off) to 255 (full speed). Fan speed will be set to maximum when the temperature sensor associated with the PWM control exceeds temp#_max. + +* PWM Manual Control +The ADT7462 can operate in manual mode for PWM control. +Below is the typical flow to contol PWM manually. +(Example for PWM1 which controls fan1) + + - Set PWM to manual mode + - echo 1 > pwm1_enable + - Enable High Freq Mode (optional) + - echo 1 > pwm_freq_mode + - Set fan1 as present + - echo 1 > fan1_presence + - Start TACH measurement for fan1 + - echo 1 > fan1_tach_enable + - Mark as setup complete. This will start monitoring of all enabled channels. + - echo 1 > setup_complete + - Read fan1 RPM + - cat fan1_input + - Change PWM1 to 75% (192/255) + - echo 192 > pwm1 diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index e75bbd87ad09..d9faa1224ed8 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -39,8 +39,12 @@ static const unsigned short normal_i2c[] = { 0x58, 0x5C, I2C_CLIENT_END }; #define ADT7462_REG_FAN_MIN_BASE_ADDR 0x78 #define ADT7462_REG_FAN_MIN_MAX_ADDR 0x7F +#define ADT7462_REG_CFG1 0x01 +#define ADT7462_SETUP_COMPLETE_MASK 0x20 + #define ADT7462_REG_CFG2 0x02 #define ADT7462_FSPD_MASK 0x20 +#define ADT7462_PWM_FREQ_MODE_MASK 0x04 #define ADT7462_REG_PWM_BASE_ADDR 0xAA #define ADT7462_REG_PWM_MAX_ADDR 0xAD @@ -58,6 +62,7 @@ static const unsigned short normal_i2c[] = { 0x58, 0x5C, I2C_CLIENT_END }; #define ADT7462_REG_PWM_CFG_MAX_ADDR 0x24 #define ADT7462_PWM_CHANNEL_MASK 0xE0 #define ADT7462_PWM_CHANNEL_SHIFT 5 +#define ADT7462_SPINUP_TIMEOUT_MASK 0x07 #define ADT7462_REG_PIN_CFG_BASE_ADDR 0x10 #define ADT7462_REG_PIN_CFG_MAX_ADDR 0x13 @@ -84,6 +89,10 @@ static const unsigned short normal_i2c[] = { 0x58, 0x5C, I2C_CLIENT_END }; #define ADT7462_PIN28_SHIFT 4 /* cfg3 */ #define ADT7462_PIN28_VOLT 0x5 +#define ADT7462_REG_EASY_CONFIG 0x14 + +#define ADT7462_REG_FAN_PRESENCE 0x1D + #define ADT7462_REG_ALARM1 0xB8 #define ADT7462_LT_ALARM 0x02 #define ADT7462_R1T_ALARM 0x04 @@ -203,8 +212,11 @@ struct adt7462_data { u8 temp_max[ADT7462_TEMP_COUNT]; u16 fan[ADT7462_FAN_COUNT]; u8 fan_enabled; + u8 fan_presence; u8 fan_min[ADT7462_FAN_COUNT]; + u8 cfg1; u8 cfg2; + u8 easy_config; u8 pwm[ADT7462_PWM_COUNT]; u8 pin_cfg[ADT7462_PIN_CFG_REG_COUNT]; u8 voltages[ADT7462_VOLT_COUNT]; @@ -700,6 +712,9 @@ static struct adt7462_data *adt7462_update_device(struct device *dev) data->fan_enabled = i2c_smbus_read_byte_data(client, ADT7462_REG_FAN_ENABLE); + data->fan_presence = i2c_smbus_read_byte_data(client, + ADT7462_REG_FAN_PRESENCE); + for (i = 0; i < ADT7462_PWM_COUNT; i++) data->pwm[i] = i2c_smbus_read_byte_data(client, ADT7462_REG_PWM(i)); @@ -765,8 +780,13 @@ static struct adt7462_data *adt7462_update_device(struct device *dev) data->pwm_max = i2c_smbus_read_byte_data(client, ADT7462_REG_PWM_MAX); + data->cfg1 = i2c_smbus_read_byte_data(client, ADT7462_REG_CFG1); + data->cfg2 = i2c_smbus_read_byte_data(client, ADT7462_REG_CFG2); + data->easy_config = i2c_smbus_read_byte_data(client, + ADT7462_REG_EASY_CONFIG); + data->limits_last_updated = local_jiffies; data->limits_valid = 1; @@ -1049,6 +1069,117 @@ static ssize_t fan_show(struct device *dev, struct device_attribute *devattr, FAN_PERIOD_TO_RPM(data->fan[attr->index])); } +static ssize_t fan_tach_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + + return sprintf(buf, "%d\n", fan_enabled(data, attr->index) ? 1 : 0); +} + +static ssize_t fan_tach_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long temp; + u8 reg; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + + mutex_lock(&data->lock); + reg = i2c_smbus_read_byte_data(client, ADT7462_REG_FAN_ENABLE); + + if (temp) + reg |= (1 << attr->index); + else + reg &= (~(1 << attr->index)); + data->fan_enabled = reg; + + i2c_smbus_write_byte_data(client, ADT7462_REG_FAN_ENABLE, reg); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t fan_presence_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + + return sprintf(buf, "%d\n", ((data->fan_presence >> attr->index) & 1) ? 1 : 0); +} + +static ssize_t fan_presence_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long temp; + u8 reg; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + + mutex_lock(&data->lock); + reg = i2c_smbus_read_byte_data(client, ADT7462_REG_FAN_PRESENCE); + + if (temp) + reg |= (1 << attr->index); + else + reg &= (~(1 << attr->index)); + data->fan_presence = reg; + + i2c_smbus_write_byte_data(client, ADT7462_REG_FAN_PRESENCE, reg); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t setup_complete_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct adt7462_data *data = adt7462_update_device(dev); + + return sprintf(buf, "%d\n", (data->cfg1 & ADT7462_SETUP_COMPLETE_MASK ? 1 : 0)); +} + +static ssize_t setup_complete_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct adt7462_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long temp; + u8 reg; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + + mutex_lock(&data->lock); + reg = i2c_smbus_read_byte_data(client, ADT7462_REG_CFG1); + if (temp) + reg |= ADT7462_SETUP_COMPLETE_MASK; + else + reg &= ~ADT7462_SETUP_COMPLETE_MASK; + data->cfg1 = reg; + i2c_smbus_write_byte_data(client, ADT7462_REG_CFG1, reg); + mutex_unlock(&data->lock); + + return count; +} + static ssize_t force_pwm_max_show(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -1081,6 +1212,75 @@ static ssize_t force_pwm_max_store(struct device *dev, return count; } +static ssize_t pwm_freq_mode_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct adt7462_data *data = adt7462_update_device(dev); + + return sprintf(buf, "%d\n", (data->cfg2 & ADT7462_PWM_FREQ_MODE_MASK ? 1 : 0)); +} + +static ssize_t pwm_freq_mode_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct adt7462_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long temp; + u8 reg; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + + mutex_lock(&data->lock); + reg = i2c_smbus_read_byte_data(client, ADT7462_REG_CFG2); + if (temp) + reg |= ADT7462_PWM_FREQ_MODE_MASK; + else + reg &= ~ADT7462_PWM_FREQ_MODE_MASK; + + data->cfg2 = reg; + i2c_smbus_write_byte_data(client, ADT7462_REG_CFG2, reg); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t easy_config_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct adt7462_data *data = adt7462_update_device(dev); + + return sprintf(buf, "%d\n", data->easy_config); +} + +static ssize_t easy_config_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct adt7462_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long temp; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + /* Only 1 bit needs to be set and set bit should be below bit 5. */ + + if (((temp & (temp - 1)) != 0) || (temp > 16)) + return -EINVAL; + + mutex_lock(&data->lock); + data->easy_config = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_EASY_CONFIG, temp); + mutex_unlock(&data->lock); + + return count; +} + static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -1339,6 +1539,56 @@ static ssize_t pwm_auto_store(struct device *dev, } } +static ssize_t pwm_spinup_timeout_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + int cfg = data->pwm_cfg[attr->index] & ADT7462_SPINUP_TIMEOUT_MASK; + + return sprintf(buf, "%d\n", cfg); + +} + +static void pwm_spinup_timeout_reg_store(struct i2c_client *client, + struct adt7462_data *data, + int which, + int value) +{ + int temp = data->pwm_cfg[which] & ~ADT7462_SPINUP_TIMEOUT_MASK; + + temp |= value; + + mutex_lock(&data->lock); + data->pwm_cfg[which] = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_CFG(which), temp); + mutex_unlock(&data->lock); +} + +static ssize_t pwm_spinup_timeout_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long temp; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + /* Only 3 bits are valid. */ + if (temp > 7) + return -EINVAL; + + pwm_spinup_timeout_reg_store(client, data, attr->index, temp); + + return count; +} + + + static ssize_t pwm_auto_temp_show(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -1540,8 +1790,32 @@ static SENSOR_DEVICE_ATTR_RO(fan7_alarm, alarm, static SENSOR_DEVICE_ATTR_RO(fan8_alarm, alarm, ADT7462_ALARM4 | ADT7462_F7_ALARM); +static SENSOR_DEVICE_ATTR_RW(fan1_tach_enable, fan_tach, 0); +static SENSOR_DEVICE_ATTR_RW(fan2_tach_enable, fan_tach, 1); +static SENSOR_DEVICE_ATTR_RW(fan3_tach_enable, fan_tach, 2); +static SENSOR_DEVICE_ATTR_RW(fan4_tach_enable, fan_tach, 3); +static SENSOR_DEVICE_ATTR_RW(fan5_tach_enable, fan_tach, 4); +static SENSOR_DEVICE_ATTR_RW(fan6_tach_enable, fan_tach, 5); +static SENSOR_DEVICE_ATTR_RW(fan7_tach_enable, fan_tach, 6); +static SENSOR_DEVICE_ATTR_RW(fan8_tach_enable, fan_tach, 7); + +static SENSOR_DEVICE_ATTR_RW(fan1_presence, fan_presence, 0); +static SENSOR_DEVICE_ATTR_RW(fan2_presence, fan_presence, 1); +static SENSOR_DEVICE_ATTR_RW(fan3_presence, fan_presence, 2); +static SENSOR_DEVICE_ATTR_RW(fan4_presence, fan_presence, 3); +static SENSOR_DEVICE_ATTR_RW(fan5_presence, fan_presence, 4); +static SENSOR_DEVICE_ATTR_RW(fan6_presence, fan_presence, 5); +static SENSOR_DEVICE_ATTR_RW(fan7_presence, fan_presence, 6); +static SENSOR_DEVICE_ATTR_RW(fan8_presence, fan_presence, 7); + static SENSOR_DEVICE_ATTR_RW(force_pwm_max, force_pwm_max, 0); +static SENSOR_DEVICE_ATTR_RW(setup_complete, setup_complete, 0); + +static SENSOR_DEVICE_ATTR_RW(pwm_freq_mode, pwm_freq_mode, 0); + +static SENSOR_DEVICE_ATTR_RW(easy_config, easy_config, 0); + static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0); static SENSOR_DEVICE_ATTR_RW(pwm2, pwm, 1); static SENSOR_DEVICE_ATTR_RW(pwm3, pwm, 2); @@ -1582,6 +1856,11 @@ static SENSOR_DEVICE_ATTR_RW(pwm2_enable, pwm_auto, 1); static SENSOR_DEVICE_ATTR_RW(pwm3_enable, pwm_auto, 2); static SENSOR_DEVICE_ATTR_RW(pwm4_enable, pwm_auto, 3); +static SENSOR_DEVICE_ATTR_RW(pwm1_spinup_timeout, pwm_spinup_timeout, 0); +static SENSOR_DEVICE_ATTR_RW(pwm2_spinup_timeout, pwm_spinup_timeout, 1); +static SENSOR_DEVICE_ATTR_RW(pwm3_spinup_timeout, pwm_spinup_timeout, 2); +static SENSOR_DEVICE_ATTR_RW(pwm4_spinup_timeout, pwm_spinup_timeout, 3); + static SENSOR_DEVICE_ATTR_RW(pwm1_auto_channels_temp, pwm_auto_temp, 0); static SENSOR_DEVICE_ATTR_RW(pwm2_auto_channels_temp, pwm_auto_temp, 1); static SENSOR_DEVICE_ATTR_RW(pwm3_auto_channels_temp, pwm_auto_temp, 2); @@ -1710,12 +1989,36 @@ static struct attribute *adt7462_attrs[] = { &sensor_dev_attr_fan7_alarm.dev_attr.attr, &sensor_dev_attr_fan8_alarm.dev_attr.attr, + &sensor_dev_attr_fan1_tach_enable.dev_attr.attr, + &sensor_dev_attr_fan2_tach_enable.dev_attr.attr, + &sensor_dev_attr_fan3_tach_enable.dev_attr.attr, + &sensor_dev_attr_fan4_tach_enable.dev_attr.attr, + &sensor_dev_attr_fan5_tach_enable.dev_attr.attr, + &sensor_dev_attr_fan6_tach_enable.dev_attr.attr, + &sensor_dev_attr_fan7_tach_enable.dev_attr.attr, + &sensor_dev_attr_fan8_tach_enable.dev_attr.attr, + + &sensor_dev_attr_fan1_presence.dev_attr.attr, + &sensor_dev_attr_fan2_presence.dev_attr.attr, + &sensor_dev_attr_fan3_presence.dev_attr.attr, + &sensor_dev_attr_fan4_presence.dev_attr.attr, + &sensor_dev_attr_fan5_presence.dev_attr.attr, + &sensor_dev_attr_fan6_presence.dev_attr.attr, + &sensor_dev_attr_fan7_presence.dev_attr.attr, + &sensor_dev_attr_fan8_presence.dev_attr.attr, + + &sensor_dev_attr_force_pwm_max.dev_attr.attr, + &sensor_dev_attr_pwm_freq_mode.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_setup_complete.dev_attr.attr, + + &sensor_dev_attr_easy_config.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, @@ -1751,6 +2054,11 @@ static struct attribute *adt7462_attrs[] = { &sensor_dev_attr_pwm3_enable.dev_attr.attr, &sensor_dev_attr_pwm4_enable.dev_attr.attr, + &sensor_dev_attr_pwm1_spinup_timeout.dev_attr.attr, + &sensor_dev_attr_pwm2_spinup_timeout.dev_attr.attr, + &sensor_dev_attr_pwm3_spinup_timeout.dev_attr.attr, + &sensor_dev_attr_pwm4_spinup_timeout.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, -- 2.31.1