* [PATCH] hwmon: adt7470: Expose PWM frequency to sysfs
@ 2016-05-16 5:20 Joshua Scott
2016-05-18 15:42 ` Guenter Roeck
0 siblings, 1 reply; 6+ messages in thread
From: Joshua Scott @ 2016-05-16 5:20 UTC (permalink / raw)
To: Jean Delvare, Guenter Roeck, linux-hwmon; +Cc: Joshua Scott, Chris Packham
The ADT7470 supports a variety of PWM frequencies. This patch allows the
frequency to be configured and viewed through sysfs.
The valid options are:
-> 11.0 Hz
-> 14.7 Hz
-> 22.1 Hz
-> 29.4 Hz
-> 35.3 Hz
-> 44.1 Hz
-> 58.8 Hz
-> 88.2 Hz
-> 1.4 kHz
-> 22.5 kHz
Signed-off-by: Joshua Scott <joshua.scott@alliedtelesis.co.nz>
---
Documentation/hwmon/adt7470 | 17 +++++
drivers/hwmon/adt7470.c | 150 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 167 insertions(+)
diff --git a/Documentation/hwmon/adt7470 b/Documentation/hwmon/adt7470
index 8ce4aa0..f8a58f0 100644
--- a/Documentation/hwmon/adt7470
+++ b/Documentation/hwmon/adt7470
@@ -65,6 +65,23 @@ from 0 (off) to 255 (full speed). Fan speed will be set to maximum when the
temperature sensor associated with the PWM control exceeds
pwm#_auto_point2_temp.
+The driver also allows control of the PWM frequency:
+
+* pwm_freq
+
+The following frequencies are valid:
+
+* 11.0 Hz
+* 14.7 Hz
+* 22.1 Hz
+* 29.4 Hz
+* 35.3 Hz
+* 44.1 Hz
+* 58.8 Hz
+* 88.2 Hz
+* 1.4 kHz
+* 22.5 kHz
+
Notes
-----
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index f5da39a..e5f0d6e 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -83,6 +83,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D
#define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E
#define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71
+#define ADT7470_REG_CFG_2 0x74
#define ADT7470_REG_ACOUSTICS12 0x75
#define ADT7470_REG_ACOUSTICS34 0x76
#define ADT7470_REG_DEVICE 0x3D
@@ -142,6 +143,32 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define FAN_PERIOD_INVALID 65535
#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID)
+/* The datasheet includes a table of available pwm frequencies
+ * controlled by values in config registers 1 and 2.
+ */
+#define ADT7470_CFG_LF 0x40
+#define ADT7470_FREQ_BITS 0x70
+#define ADT7470_CFG_LF_11_0_HZ 0x00
+#define ADT7470_CFG_LF_14_7_HZ 0x10
+#define ADT7470_CFG_LF_22_1_HZ 0x20
+#define ADT7470_CFG_LF_29_4_HZ 0x30
+#define ADT7470_CFG_LF_35_3_HZ 0x40
+#define ADT7470_CFG_LF_44_1_HZ 0x50
+#define ADT7470_CFG_LF_58_8_HZ 0x60
+#define ADT7470_CFG_LF_88_2_HZ 0x70
+#define ADT7470_CFG_HF_1_4_KHZ 0x00
+#define ADT7470_CFG_HF_22_5_KHZ 0x10
+#define ADT7470_11_0_HZ_STR "11.0 Hz"
+#define ADT7470_14_7_HZ_STR "14.7 Hz"
+#define ADT7470_22_1_HZ_STR "22.1 Hz"
+#define ADT7470_29_4_HZ_STR "29.4 Hz"
+#define ADT7470_35_3_HZ_STR "35.3 Hz"
+#define ADT7470_44_1_HZ_STR "44.1 Hz"
+#define ADT7470_58_8_HZ_STR "58.8 Hz"
+#define ADT7470_88_2_HZ_STR "88.2 Hz"
+#define ADT7470_1_4_KHZ_STR "1.4 kHz"
+#define ADT7470_22_5_KHZ_STR "22.5 kHz"
+
struct adt7470_data {
struct i2c_client *client;
struct mutex lock;
@@ -688,6 +715,126 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
return count;
}
+static ssize_t show_pwm_freq(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct adt7470_data *data = adt7470_update_device(dev);
+ unsigned char cfg_reg_1;
+ unsigned char cfg_reg_2;
+ char *freq = "invalid";
+
+ mutex_lock(&data->lock);
+ cfg_reg_1 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG);
+ cfg_reg_2 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG_2);
+ mutex_unlock(&data->lock);
+
+ if (cfg_reg_1 & ADT7470_CFG_LF)
+ switch (cfg_reg_2 & ADT7470_FREQ_BITS) {
+ case ADT7470_CFG_LF_11_0_HZ:
+ freq = ADT7470_11_0_HZ_STR;
+ break;
+ case ADT7470_CFG_LF_14_7_HZ:
+ freq = ADT7470_14_7_HZ_STR;
+ break;
+ case ADT7470_CFG_LF_22_1_HZ:
+ freq = ADT7470_22_1_HZ_STR;
+ break;
+ case ADT7470_CFG_LF_29_4_HZ:
+ freq = ADT7470_29_4_HZ_STR;
+ break;
+ case ADT7470_CFG_LF_35_3_HZ:
+ freq = ADT7470_35_3_HZ_STR;
+ break;
+ case ADT7470_CFG_LF_44_1_HZ:
+ freq = ADT7470_44_1_HZ_STR;
+ break;
+ case ADT7470_CFG_LF_58_8_HZ:
+ freq = ADT7470_58_8_HZ_STR;
+ break;
+ case ADT7470_CFG_LF_88_2_HZ:
+ freq = ADT7470_88_2_HZ_STR;
+ break;
+ }
+ else
+ switch (cfg_reg_2 & ADT7470_FREQ_BITS) {
+ case ADT7470_CFG_HF_1_4_KHZ:
+ freq = ADT7470_1_4_KHZ_STR;
+ break;
+ default:
+ freq = ADT7470_22_5_KHZ_STR;
+ break;
+ }
+ return scnprintf(buf, PAGE_SIZE, "%s\n", freq);
+}
+
+static ssize_t set_pwm_freq(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adt7470_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int low_freq = ADT7470_CFG_LF;
+ int freq_bits;
+ unsigned char val;
+
+ if (!strncmp(ADT7470_11_0_HZ_STR, buf, strlen(ADT7470_11_0_HZ_STR)))
+ freq_bits = ADT7470_CFG_LF_11_0_HZ;
+
+ else if (!strncmp(ADT7470_14_7_HZ_STR, buf,
+ strlen(ADT7470_14_7_HZ_STR)))
+ freq_bits = ADT7470_CFG_LF_14_7_HZ;
+
+ else if (!strncmp(ADT7470_22_1_HZ_STR, buf,
+ strlen(ADT7470_22_1_HZ_STR)))
+ freq_bits = ADT7470_CFG_LF_22_1_HZ;
+
+ else if (!strncmp(ADT7470_29_4_HZ_STR, buf,
+ strlen(ADT7470_29_4_HZ_STR)))
+ freq_bits = ADT7470_CFG_LF_29_4_HZ;
+
+ else if (!strncmp(ADT7470_35_3_HZ_STR, buf,
+ strlen(ADT7470_35_3_HZ_STR)))
+ freq_bits = ADT7470_CFG_LF_35_3_HZ;
+
+ else if (!strncmp(ADT7470_44_1_HZ_STR, buf,
+ strlen(ADT7470_44_1_HZ_STR)))
+ freq_bits = ADT7470_CFG_LF_44_1_HZ;
+
+ else if (!strncmp(ADT7470_58_8_HZ_STR, buf,
+ strlen(ADT7470_58_8_HZ_STR)))
+ freq_bits = ADT7470_CFG_LF_58_8_HZ;
+
+ else if (!strncmp(ADT7470_88_2_HZ_STR, buf,
+ strlen(ADT7470_88_2_HZ_STR)))
+ freq_bits = ADT7470_CFG_LF_88_2_HZ;
+
+ else if (!strncmp(ADT7470_1_4_KHZ_STR, buf,
+ strlen(ADT7470_1_4_KHZ_STR))) {
+ freq_bits = ADT7470_CFG_HF_1_4_KHZ;
+ low_freq = 0;
+
+ } else if (!strncmp(ADT7470_22_5_KHZ_STR, buf,
+ strlen(ADT7470_22_5_KHZ_STR))) {
+ freq_bits = ADT7470_CFG_HF_22_5_KHZ;
+ low_freq = 0;
+
+ } else
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ /* Configuration Register 1 */
+ val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
+ i2c_smbus_write_byte_data(client, ADT7470_REG_CFG,
+ (val & ~ADT7470_CFG_LF) | low_freq);
+ /* Configuration Register 2 */
+ val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2);
+ i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2,
+ (val & ~ADT7470_FREQ_BITS) | freq_bits);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
static ssize_t show_pwm_max(struct device *dev,
struct device_attribute *devattr,
char *buf)
@@ -1038,6 +1185,8 @@ static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
+static DEVICE_ATTR(pwm_freq, S_IWUSR | S_IRUGO, show_pwm_freq, set_pwm_freq);
+
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
show_pwm_min, set_pwm_min, 0);
static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO,
@@ -1096,6 +1245,7 @@ static struct attribute *adt7470_attrs[] = {
&dev_attr_alarm_mask.attr,
&dev_attr_num_temp_sensors.attr,
&dev_attr_auto_update_interval.attr,
+ &dev_attr_pwm_freq.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp3_max.dev_attr.attr,
--
2.8.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] hwmon: adt7470: Expose PWM frequency to sysfs
2016-05-16 5:20 Joshua Scott
@ 2016-05-18 15:42 ` Guenter Roeck
0 siblings, 0 replies; 6+ messages in thread
From: Guenter Roeck @ 2016-05-18 15:42 UTC (permalink / raw)
To: Joshua Scott; +Cc: Jean Delvare, linux-hwmon, Chris Packham
On Mon, May 16, 2016 at 05:20:32PM +1200, Joshua Scott wrote:
> The ADT7470 supports a variety of PWM frequencies. This patch allows the
> frequency to be configured and viewed through sysfs.
>
> The valid options are:
> -> 11.0 Hz
> -> 14.7 Hz
> -> 22.1 Hz
> -> 29.4 Hz
> -> 35.3 Hz
> -> 44.1 Hz
> -> 58.8 Hz
> -> 88.2 Hz
> -> 1.4 kHz
> -> 22.5 kHz
>
Please use a standard attribute name (pwm1_freq), and the standard ABI
(frequency in Hz, no units). Sure, that means rounding for the lower
frequencies, but that is better than a non-standard ABI for which user
space applications would have to be modified and that would leave the user
guessing how exactly to provide an acceptable frequency.
Also, I strongly suggest to not expect the user to know acceptable
frequencies, but to select the closest supported frequency.
Other drivers use find_closest() or find_closest_descending(), and
the same should be possible here.
Thanks,
Guenter
> Signed-off-by: Joshua Scott <joshua.scott@alliedtelesis.co.nz>
> ---
> Documentation/hwmon/adt7470 | 17 +++++
> drivers/hwmon/adt7470.c | 150 ++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 167 insertions(+)
>
> diff --git a/Documentation/hwmon/adt7470 b/Documentation/hwmon/adt7470
> index 8ce4aa0..f8a58f0 100644
> --- a/Documentation/hwmon/adt7470
> +++ b/Documentation/hwmon/adt7470
> @@ -65,6 +65,23 @@ from 0 (off) to 255 (full speed). Fan speed will be set to maximum when the
> temperature sensor associated with the PWM control exceeds
> pwm#_auto_point2_temp.
>
> +The driver also allows control of the PWM frequency:
> +
> +* pwm_freq
> +
> +The following frequencies are valid:
> +
> +* 11.0 Hz
> +* 14.7 Hz
> +* 22.1 Hz
> +* 29.4 Hz
> +* 35.3 Hz
> +* 44.1 Hz
> +* 58.8 Hz
> +* 88.2 Hz
> +* 1.4 kHz
> +* 22.5 kHz
> +
> Notes
> -----
>
> diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
> index f5da39a..e5f0d6e 100644
> --- a/drivers/hwmon/adt7470.c
> +++ b/drivers/hwmon/adt7470.c
> @@ -83,6 +83,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
> #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D
> #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E
> #define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71
> +#define ADT7470_REG_CFG_2 0x74
> #define ADT7470_REG_ACOUSTICS12 0x75
> #define ADT7470_REG_ACOUSTICS34 0x76
> #define ADT7470_REG_DEVICE 0x3D
> @@ -142,6 +143,32 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
> #define FAN_PERIOD_INVALID 65535
> #define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID)
>
> +/* The datasheet includes a table of available pwm frequencies
> + * controlled by values in config registers 1 and 2.
> + */
> +#define ADT7470_CFG_LF 0x40
> +#define ADT7470_FREQ_BITS 0x70
> +#define ADT7470_CFG_LF_11_0_HZ 0x00
> +#define ADT7470_CFG_LF_14_7_HZ 0x10
> +#define ADT7470_CFG_LF_22_1_HZ 0x20
> +#define ADT7470_CFG_LF_29_4_HZ 0x30
> +#define ADT7470_CFG_LF_35_3_HZ 0x40
> +#define ADT7470_CFG_LF_44_1_HZ 0x50
> +#define ADT7470_CFG_LF_58_8_HZ 0x60
> +#define ADT7470_CFG_LF_88_2_HZ 0x70
> +#define ADT7470_CFG_HF_1_4_KHZ 0x00
> +#define ADT7470_CFG_HF_22_5_KHZ 0x10
> +#define ADT7470_11_0_HZ_STR "11.0 Hz"
> +#define ADT7470_14_7_HZ_STR "14.7 Hz"
> +#define ADT7470_22_1_HZ_STR "22.1 Hz"
> +#define ADT7470_29_4_HZ_STR "29.4 Hz"
> +#define ADT7470_35_3_HZ_STR "35.3 Hz"
> +#define ADT7470_44_1_HZ_STR "44.1 Hz"
> +#define ADT7470_58_8_HZ_STR "58.8 Hz"
> +#define ADT7470_88_2_HZ_STR "88.2 Hz"
> +#define ADT7470_1_4_KHZ_STR "1.4 kHz"
> +#define ADT7470_22_5_KHZ_STR "22.5 kHz"
> +
> struct adt7470_data {
> struct i2c_client *client;
> struct mutex lock;
> @@ -688,6 +715,126 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
> return count;
> }
>
> +static ssize_t show_pwm_freq(struct device *dev,
> + struct device_attribute *devattr, char *buf)
> +{
> + struct adt7470_data *data = adt7470_update_device(dev);
> + unsigned char cfg_reg_1;
> + unsigned char cfg_reg_2;
> + char *freq = "invalid";
> +
> + mutex_lock(&data->lock);
> + cfg_reg_1 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG);
> + cfg_reg_2 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG_2);
> + mutex_unlock(&data->lock);
> +
> + if (cfg_reg_1 & ADT7470_CFG_LF)
> + switch (cfg_reg_2 & ADT7470_FREQ_BITS) {
> + case ADT7470_CFG_LF_11_0_HZ:
> + freq = ADT7470_11_0_HZ_STR;
> + break;
> + case ADT7470_CFG_LF_14_7_HZ:
> + freq = ADT7470_14_7_HZ_STR;
> + break;
> + case ADT7470_CFG_LF_22_1_HZ:
> + freq = ADT7470_22_1_HZ_STR;
> + break;
> + case ADT7470_CFG_LF_29_4_HZ:
> + freq = ADT7470_29_4_HZ_STR;
> + break;
> + case ADT7470_CFG_LF_35_3_HZ:
> + freq = ADT7470_35_3_HZ_STR;
> + break;
> + case ADT7470_CFG_LF_44_1_HZ:
> + freq = ADT7470_44_1_HZ_STR;
> + break;
> + case ADT7470_CFG_LF_58_8_HZ:
> + freq = ADT7470_58_8_HZ_STR;
> + break;
> + case ADT7470_CFG_LF_88_2_HZ:
> + freq = ADT7470_88_2_HZ_STR;
> + break;
> + }
> + else
> + switch (cfg_reg_2 & ADT7470_FREQ_BITS) {
> + case ADT7470_CFG_HF_1_4_KHZ:
> + freq = ADT7470_1_4_KHZ_STR;
> + break;
> + default:
> + freq = ADT7470_22_5_KHZ_STR;
> + break;
> + }
> + return scnprintf(buf, PAGE_SIZE, "%s\n", freq);
> +}
> +
> +static ssize_t set_pwm_freq(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct adt7470_data *data = dev_get_drvdata(dev);
> + struct i2c_client *client = data->client;
> + int low_freq = ADT7470_CFG_LF;
> + int freq_bits;
> + unsigned char val;
> +
> + if (!strncmp(ADT7470_11_0_HZ_STR, buf, strlen(ADT7470_11_0_HZ_STR)))
> + freq_bits = ADT7470_CFG_LF_11_0_HZ;
> +
> + else if (!strncmp(ADT7470_14_7_HZ_STR, buf,
> + strlen(ADT7470_14_7_HZ_STR)))
> + freq_bits = ADT7470_CFG_LF_14_7_HZ;
> +
> + else if (!strncmp(ADT7470_22_1_HZ_STR, buf,
> + strlen(ADT7470_22_1_HZ_STR)))
> + freq_bits = ADT7470_CFG_LF_22_1_HZ;
> +
> + else if (!strncmp(ADT7470_29_4_HZ_STR, buf,
> + strlen(ADT7470_29_4_HZ_STR)))
> + freq_bits = ADT7470_CFG_LF_29_4_HZ;
> +
> + else if (!strncmp(ADT7470_35_3_HZ_STR, buf,
> + strlen(ADT7470_35_3_HZ_STR)))
> + freq_bits = ADT7470_CFG_LF_35_3_HZ;
> +
> + else if (!strncmp(ADT7470_44_1_HZ_STR, buf,
> + strlen(ADT7470_44_1_HZ_STR)))
> + freq_bits = ADT7470_CFG_LF_44_1_HZ;
> +
> + else if (!strncmp(ADT7470_58_8_HZ_STR, buf,
> + strlen(ADT7470_58_8_HZ_STR)))
> + freq_bits = ADT7470_CFG_LF_58_8_HZ;
> +
> + else if (!strncmp(ADT7470_88_2_HZ_STR, buf,
> + strlen(ADT7470_88_2_HZ_STR)))
> + freq_bits = ADT7470_CFG_LF_88_2_HZ;
> +
> + else if (!strncmp(ADT7470_1_4_KHZ_STR, buf,
> + strlen(ADT7470_1_4_KHZ_STR))) {
> + freq_bits = ADT7470_CFG_HF_1_4_KHZ;
> + low_freq = 0;
> +
> + } else if (!strncmp(ADT7470_22_5_KHZ_STR, buf,
> + strlen(ADT7470_22_5_KHZ_STR))) {
> + freq_bits = ADT7470_CFG_HF_22_5_KHZ;
> + low_freq = 0;
> +
> + } else
> + return -EINVAL;
> +
> + mutex_lock(&data->lock);
> + /* Configuration Register 1 */
> + val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
> + i2c_smbus_write_byte_data(client, ADT7470_REG_CFG,
> + (val & ~ADT7470_CFG_LF) | low_freq);
> + /* Configuration Register 2 */
> + val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2);
> + i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2,
> + (val & ~ADT7470_FREQ_BITS) | freq_bits);
> + mutex_unlock(&data->lock);
> +
> + return count;
> +}
> +
> static ssize_t show_pwm_max(struct device *dev,
> struct device_attribute *devattr,
> char *buf)
> @@ -1038,6 +1185,8 @@ static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
> static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
> static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
>
> +static DEVICE_ATTR(pwm_freq, S_IWUSR | S_IRUGO, show_pwm_freq, set_pwm_freq);
> +
> static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
> show_pwm_min, set_pwm_min, 0);
> static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO,
> @@ -1096,6 +1245,7 @@ static struct attribute *adt7470_attrs[] = {
> &dev_attr_alarm_mask.attr,
> &dev_attr_num_temp_sensors.attr,
> &dev_attr_auto_update_interval.attr,
> + &dev_attr_pwm_freq.attr,
> &sensor_dev_attr_temp1_max.dev_attr.attr,
> &sensor_dev_attr_temp2_max.dev_attr.attr,
> &sensor_dev_attr_temp3_max.dev_attr.attr,
> --
> 2.8.1
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH] hwmon: adt7470: Expose PWM frequency to sysfs
@ 2016-08-01 5:41 Joshua Scott
2016-08-02 22:44 ` Guenter Roeck
0 siblings, 1 reply; 6+ messages in thread
From: Joshua Scott @ 2016-08-01 5:41 UTC (permalink / raw)
To: Jean Delvare, Guenter Roeck, linux-hwmon; +Cc: Joshua Scott, Chris Packham
The ADT7470 supports a variety of PWM frequencies. This patch allows the
frequency to be configured and viewed through the sysfs entry pwm1_freq.
Signed-off-by: Joshua Scott <joshua.scott@alliedtelesis.co.nz>
---
Documentation/hwmon/adt7470 | 17 +++++
drivers/hwmon/adt7470.c | 149 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 166 insertions(+)
diff --git a/Documentation/hwmon/adt7470 b/Documentation/hwmon/adt7470
index 8ce4aa0..fe68e18 100644
--- a/Documentation/hwmon/adt7470
+++ b/Documentation/hwmon/adt7470
@@ -65,6 +65,23 @@ from 0 (off) to 255 (full speed). Fan speed will be set to maximum when the
temperature sensor associated with the PWM control exceeds
pwm#_auto_point2_temp.
+The driver also allows control of the PWM frequency:
+
+* pwm1_freq
+
+The PWM frequency is rounded to the nearest one of:
+
+* 11.0 Hz
+* 14.7 Hz
+* 22.1 Hz
+* 29.4 Hz
+* 35.3 Hz
+* 44.1 Hz
+* 58.8 Hz
+* 88.2 Hz
+* 1.4 kHz
+* 22.5 kHz
+
Notes
-----
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index f5da39a..f57026f 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -32,6 +32,7 @@
#include <linux/log2.h>
#include <linux/kthread.h>
#include <linux/slab.h>
+#include <linux/util_macros.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
@@ -83,6 +84,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D
#define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E
#define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71
+#define ADT7470_REG_CFG_2 0x74
#define ADT7470_REG_ACOUSTICS12 0x75
#define ADT7470_REG_ACOUSTICS34 0x76
#define ADT7470_REG_DEVICE 0x3D
@@ -142,6 +144,22 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define FAN_PERIOD_INVALID 65535
#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID)
+/* The datasheet includes a table of available pwm frequencies
+ * controlled by values in config registers 1 and 2.
+ */
+#define ADT7470_CFG_LF 0x40
+#define ADT7470_FREQ_BITS 0x70
+#define ADT7470_CFG_LF_11_0_HZ 0x00
+#define ADT7470_CFG_LF_14_7_HZ 0x10
+#define ADT7470_CFG_LF_22_1_HZ 0x20
+#define ADT7470_CFG_LF_29_4_HZ 0x30
+#define ADT7470_CFG_LF_35_3_HZ 0x40
+#define ADT7470_CFG_LF_44_1_HZ 0x50
+#define ADT7470_CFG_LF_58_8_HZ 0x60
+#define ADT7470_CFG_LF_88_2_HZ 0x70
+#define ADT7470_CFG_HF_1_4_KHZ 0x00
+#define ADT7470_CFG_HF_22_5_KHZ 0x10
+
struct adt7470_data {
struct i2c_client *client;
struct mutex lock;
@@ -688,6 +706,134 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
return count;
}
+static ssize_t show_pwm_freq(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct adt7470_data *data = adt7470_update_device(dev);
+ unsigned char cfg_reg_1;
+ unsigned char cfg_reg_2;
+ int freq = -EINVAL;
+
+ mutex_lock(&data->lock);
+ cfg_reg_1 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG);
+ cfg_reg_2 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG_2);
+ mutex_unlock(&data->lock);
+
+ if (cfg_reg_1 & ADT7470_CFG_LF)
+ switch (cfg_reg_2 & ADT7470_FREQ_BITS) {
+ case ADT7470_CFG_LF_11_0_HZ:
+ freq = 11;
+ break;
+ case ADT7470_CFG_LF_14_7_HZ:
+ freq = 15;
+ break;
+ case ADT7470_CFG_LF_22_1_HZ:
+ freq = 22;
+ break;
+ case ADT7470_CFG_LF_29_4_HZ:
+ freq = 29;
+ break;
+ case ADT7470_CFG_LF_35_3_HZ:
+ freq = 35;
+ break;
+ case ADT7470_CFG_LF_44_1_HZ:
+ freq = 44;
+ break;
+ case ADT7470_CFG_LF_58_8_HZ:
+ freq = 59;
+ break;
+ case ADT7470_CFG_LF_88_2_HZ:
+ freq = 88;
+ break;
+ }
+ else
+ switch (cfg_reg_2 & ADT7470_FREQ_BITS) {
+ case ADT7470_CFG_HF_1_4_KHZ:
+ freq = 1400;
+ break;
+ default:
+ freq = 22500;
+ break;
+ }
+ return scnprintf(buf, PAGE_SIZE, "%d\n", freq);
+}
+
+/* These are the valid PWM frequencies to the nearest Hz */
+static const int adt7470_freq_map[] = {
+ 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
+};
+
+static ssize_t set_pwm_freq(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adt7470_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long freq;
+ int freq_index;
+ int low_freq = ADT7470_CFG_LF;
+ int freq_bits;
+ unsigned char val;
+
+ if (kstrtol(buf, 10, &freq))
+ return -EINVAL;
+
+ /* Round the user value given to the closest available frequency */
+ freq_index = find_closest(freq, adt7470_freq_map,
+ ARRAY_SIZE(adt7470_freq_map));
+ freq = adt7470_freq_map[freq_index];
+
+ switch (freq) {
+ case 11:
+ freq_bits = ADT7470_CFG_LF_11_0_HZ;
+ break;
+ case 15:
+ freq_bits = ADT7470_CFG_LF_14_7_HZ;
+ break;
+ case 22:
+ freq_bits = ADT7470_CFG_LF_22_1_HZ;
+ break;
+ case 29:
+ freq_bits = ADT7470_CFG_LF_29_4_HZ;
+ break;
+ case 35:
+ freq_bits = ADT7470_CFG_LF_35_3_HZ;
+ break;
+ case 44:
+ freq_bits = ADT7470_CFG_LF_44_1_HZ;
+ break;
+ case 59:
+ freq_bits = ADT7470_CFG_LF_58_8_HZ;
+ break;
+ case 88:
+ freq_bits = ADT7470_CFG_LF_88_2_HZ;
+ break;
+ case 1400:
+ freq_bits = ADT7470_CFG_HF_1_4_KHZ;
+ low_freq = 0;
+ break;
+ case 22500:
+ freq_bits = ADT7470_CFG_HF_22_5_KHZ;
+ low_freq = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->lock);
+ /* Configuration Register 1 */
+ val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
+ i2c_smbus_write_byte_data(client, ADT7470_REG_CFG,
+ (val & ~ADT7470_CFG_LF) | low_freq);
+ /* Configuration Register 2 */
+ val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2);
+ i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2,
+ (val & ~ADT7470_FREQ_BITS) | freq_bits);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
static ssize_t show_pwm_max(struct device *dev,
struct device_attribute *devattr,
char *buf)
@@ -1038,6 +1184,8 @@ static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
+static DEVICE_ATTR(pwm1_freq, S_IWUSR | S_IRUGO, show_pwm_freq, set_pwm_freq);
+
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
show_pwm_min, set_pwm_min, 0);
static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO,
@@ -1096,6 +1244,7 @@ static struct attribute *adt7470_attrs[] = {
&dev_attr_alarm_mask.attr,
&dev_attr_num_temp_sensors.attr,
&dev_attr_auto_update_interval.attr,
+ &dev_attr_pwm1_freq.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp3_max.dev_attr.attr,
--
2.9.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] hwmon: adt7470: Expose PWM frequency to sysfs
2016-08-01 5:41 [PATCH] hwmon: adt7470: Expose PWM frequency to sysfs Joshua Scott
@ 2016-08-02 22:44 ` Guenter Roeck
0 siblings, 0 replies; 6+ messages in thread
From: Guenter Roeck @ 2016-08-02 22:44 UTC (permalink / raw)
To: Joshua Scott; +Cc: Jean Delvare, linux-hwmon, Chris Packham
On Mon, Aug 01, 2016 at 05:41:51PM +1200, Joshua Scott wrote:
> The ADT7470 supports a variety of PWM frequencies. This patch allows the
> frequency to be configured and viewed through the sysfs entry pwm1_freq.
>
> Signed-off-by: Joshua Scott <joshua.scott@alliedtelesis.co.nz>
> ---
> Documentation/hwmon/adt7470 | 17 +++++
> drivers/hwmon/adt7470.c | 149 ++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 166 insertions(+)
>
> diff --git a/Documentation/hwmon/adt7470 b/Documentation/hwmon/adt7470
> index 8ce4aa0..fe68e18 100644
> --- a/Documentation/hwmon/adt7470
> +++ b/Documentation/hwmon/adt7470
> @@ -65,6 +65,23 @@ from 0 (off) to 255 (full speed). Fan speed will be set to maximum when the
> temperature sensor associated with the PWM control exceeds
> pwm#_auto_point2_temp.
>
> +The driver also allows control of the PWM frequency:
> +
> +* pwm1_freq
> +
> +The PWM frequency is rounded to the nearest one of:
> +
> +* 11.0 Hz
> +* 14.7 Hz
> +* 22.1 Hz
> +* 29.4 Hz
> +* 35.3 Hz
> +* 44.1 Hz
> +* 58.8 Hz
> +* 88.2 Hz
> +* 1.4 kHz
> +* 22.5 kHz
> +
> Notes
> -----
>
> diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
> index f5da39a..f57026f 100644
> --- a/drivers/hwmon/adt7470.c
> +++ b/drivers/hwmon/adt7470.c
> @@ -32,6 +32,7 @@
> #include <linux/log2.h>
> #include <linux/kthread.h>
> #include <linux/slab.h>
> +#include <linux/util_macros.h>
>
> /* Addresses to scan */
> static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
> @@ -83,6 +84,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
> #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D
> #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E
> #define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71
> +#define ADT7470_REG_CFG_2 0x74
> #define ADT7470_REG_ACOUSTICS12 0x75
> #define ADT7470_REG_ACOUSTICS34 0x76
> #define ADT7470_REG_DEVICE 0x3D
> @@ -142,6 +144,22 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
> #define FAN_PERIOD_INVALID 65535
> #define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID)
>
> +/* The datasheet includes a table of available pwm frequencies
> + * controlled by values in config registers 1 and 2.
> + */
> +#define ADT7470_CFG_LF 0x40
> +#define ADT7470_FREQ_BITS 0x70
> +#define ADT7470_CFG_LF_11_0_HZ 0x00
> +#define ADT7470_CFG_LF_14_7_HZ 0x10
> +#define ADT7470_CFG_LF_22_1_HZ 0x20
> +#define ADT7470_CFG_LF_29_4_HZ 0x30
> +#define ADT7470_CFG_LF_35_3_HZ 0x40
> +#define ADT7470_CFG_LF_44_1_HZ 0x50
> +#define ADT7470_CFG_LF_58_8_HZ 0x60
> +#define ADT7470_CFG_LF_88_2_HZ 0x70
> +#define ADT7470_CFG_HF_1_4_KHZ 0x00
> +#define ADT7470_CFG_HF_22_5_KHZ 0x10
> +
> struct adt7470_data {
> struct i2c_client *client;
> struct mutex lock;
> @@ -688,6 +706,134 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
> return count;
> }
>
> +static ssize_t show_pwm_freq(struct device *dev,
> + struct device_attribute *devattr, char *buf)
> +{
> + struct adt7470_data *data = adt7470_update_device(dev);
> + unsigned char cfg_reg_1;
> + unsigned char cfg_reg_2;
> + int freq = -EINVAL;
> +
> + mutex_lock(&data->lock);
> + cfg_reg_1 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG);
> + cfg_reg_2 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG_2);
[ Note: Lack of error checking accepted because the rest of the driver
doesn't check for errors either ]
> + mutex_unlock(&data->lock);
> +
> + if (cfg_reg_1 & ADT7470_CFG_LF)
> + switch (cfg_reg_2 & ADT7470_FREQ_BITS) {
> + case ADT7470_CFG_LF_11_0_HZ:
> + freq = 11;
> + break;
> + case ADT7470_CFG_LF_14_7_HZ:
> + freq = 15;
> + break;
> + case ADT7470_CFG_LF_22_1_HZ:
> + freq = 22;
> + break;
> + case ADT7470_CFG_LF_29_4_HZ:
> + freq = 29;
> + break;
> + case ADT7470_CFG_LF_35_3_HZ:
> + freq = 35;
> + break;
> + case ADT7470_CFG_LF_44_1_HZ:
> + freq = 44;
> + break;
> + case ADT7470_CFG_LF_58_8_HZ:
> + freq = 59;
> + break;
> + case ADT7470_CFG_LF_88_2_HZ:
> + freq = 88;
> + break;
> + }
> + else
> + switch (cfg_reg_2 & ADT7470_FREQ_BITS) {
> + case ADT7470_CFG_HF_1_4_KHZ:
> + freq = 1400;
> + break;
> + default:
> + freq = 22500;
> + break;
> + }
> + return scnprintf(buf, PAGE_SIZE, "%d\n", freq);
> +}
This can be implemented more easily by using adt7470_freq_map[].
Something like
index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT;
if (!(cfg_reg_1 & ADT7470_CFG_LF))
index += 8;
if (index >= ARRAY_SIZE(adt7470_freq_map))
index = ARRAY_SIZE(adt7470_freq_map) - 1;
return scnprintf(buf, PAGE_SIZE, "%d\n", adt7470_freq_map[index]);
with
#define ADT7470_FREQ_MASK 0x70
#define ADT7470_FREQ_SHIFT 4
> +
> +/* These are the valid PWM frequencies to the nearest Hz */
> +static const int adt7470_freq_map[] = {
> + 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
> +};
> +
> +static ssize_t set_pwm_freq(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct adt7470_data *data = dev_get_drvdata(dev);
> + struct i2c_client *client = data->client;
> + long freq;
> + int freq_index;
> + int low_freq = ADT7470_CFG_LF;
> + int freq_bits;
> + unsigned char val;
> +
> + if (kstrtol(buf, 10, &freq))
> + return -EINVAL;
> +
> + /* Round the user value given to the closest available frequency */
> + freq_index = find_closest(freq, adt7470_freq_map,
> + ARRAY_SIZE(adt7470_freq_map));
> + freq = adt7470_freq_map[freq_index];
> +
> + switch (freq) {
> + case 11:
> + freq_bits = ADT7470_CFG_LF_11_0_HZ;
> + break;
> + case 15:
> + freq_bits = ADT7470_CFG_LF_14_7_HZ;
> + break;
> + case 22:
> + freq_bits = ADT7470_CFG_LF_22_1_HZ;
> + break;
> + case 29:
> + freq_bits = ADT7470_CFG_LF_29_4_HZ;
> + break;
> + case 35:
> + freq_bits = ADT7470_CFG_LF_35_3_HZ;
> + break;
> + case 44:
> + freq_bits = ADT7470_CFG_LF_44_1_HZ;
> + break;
> + case 59:
> + freq_bits = ADT7470_CFG_LF_58_8_HZ;
> + break;
> + case 88:
> + freq_bits = ADT7470_CFG_LF_88_2_HZ;
> + break;
> + case 1400:
> + freq_bits = ADT7470_CFG_HF_1_4_KHZ;
> + low_freq = 0;
> + break;
> + case 22500:
> + freq_bits = ADT7470_CFG_HF_22_5_KHZ;
> + low_freq = 0;
> + break;
freq_index can be used directly to calculate the register values
without going through the switch statement.
if (freq_index >= 8) {
freq_index -= 8;
low_freq = 0;
}
...
/* Configuration Register 2 */
val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2);
i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2,
(val & ~ADT7470_FREQ_MASK) | (freq_index << ADT7470_FREQ_SHIFT);
> + default:
> + return -EINVAL;
> + }
> +
> + mutex_lock(&data->lock);
> + /* Configuration Register 1 */
> + val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
> + i2c_smbus_write_byte_data(client, ADT7470_REG_CFG,
> + (val & ~ADT7470_CFG_LF) | low_freq);
> + /* Configuration Register 2 */
> + val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2);
> + i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2,
> + (val & ~ADT7470_FREQ_BITS) | freq_bits);
> + mutex_unlock(&data->lock);
> +
> + return count;
> +}
> +
> static ssize_t show_pwm_max(struct device *dev,
> struct device_attribute *devattr,
> char *buf)
> @@ -1038,6 +1184,8 @@ static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
> static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
> static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
>
> +static DEVICE_ATTR(pwm1_freq, S_IWUSR | S_IRUGO, show_pwm_freq, set_pwm_freq);
> +
> static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
> show_pwm_min, set_pwm_min, 0);
> static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO,
> @@ -1096,6 +1244,7 @@ static struct attribute *adt7470_attrs[] = {
> &dev_attr_alarm_mask.attr,
> &dev_attr_num_temp_sensors.attr,
> &dev_attr_auto_update_interval.attr,
> + &dev_attr_pwm1_freq.attr,
Should be right after &sensor_dev_attr_pwm1.dev_attr.attr.
> &sensor_dev_attr_temp1_max.dev_attr.attr,
> &sensor_dev_attr_temp2_max.dev_attr.attr,
> &sensor_dev_attr_temp3_max.dev_attr.attr,
> --
> 2.9.0
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH] hwmon: adt7470: Expose PWM frequency to sysfs
@ 2016-08-08 1:35 Joshua Scott
2016-08-12 13:12 ` Guenter Roeck
0 siblings, 1 reply; 6+ messages in thread
From: Joshua Scott @ 2016-08-08 1:35 UTC (permalink / raw)
To: Jean Delvare, Guenter Roeck, linux-hwmon; +Cc: Joshua Scott, Chris Packham
The ADT7470 supports a variety of PWM frequencies. This patch allows the
frequency to be configured and viewed through the sysfs entry pwm1_freq.
Signed-off-by: Joshua Scott <joshua.scott@alliedtelesis.co.nz>
---
Documentation/hwmon/adt7470 | 17 +++++++++++
drivers/hwmon/adt7470.c | 74 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 91 insertions(+)
diff --git a/Documentation/hwmon/adt7470 b/Documentation/hwmon/adt7470
index 8ce4aa0..fe68e18 100644
--- a/Documentation/hwmon/adt7470
+++ b/Documentation/hwmon/adt7470
@@ -65,6 +65,23 @@ from 0 (off) to 255 (full speed). Fan speed will be set to maximum when the
temperature sensor associated with the PWM control exceeds
pwm#_auto_point2_temp.
+The driver also allows control of the PWM frequency:
+
+* pwm1_freq
+
+The PWM frequency is rounded to the nearest one of:
+
+* 11.0 Hz
+* 14.7 Hz
+* 22.1 Hz
+* 29.4 Hz
+* 35.3 Hz
+* 44.1 Hz
+* 58.8 Hz
+* 88.2 Hz
+* 1.4 kHz
+* 22.5 kHz
+
Notes
-----
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index f5da39a..94550d7 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -32,6 +32,7 @@
#include <linux/log2.h>
#include <linux/kthread.h>
#include <linux/slab.h>
+#include <linux/util_macros.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
@@ -83,6 +84,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D
#define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E
#define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71
+#define ADT7470_REG_CFG_2 0x74
#define ADT7470_REG_ACOUSTICS12 0x75
#define ADT7470_REG_ACOUSTICS34 0x76
#define ADT7470_REG_DEVICE 0x3D
@@ -142,6 +144,11 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define FAN_PERIOD_INVALID 65535
#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID)
+/* Config registers 1 and 2 include fields for selecting the PWM frequency */
+#define ADT7470_CFG_LF 0x40
+#define ADT7470_FREQ_MASK 0x70
+#define ADT7470_FREQ_SHIFT 4
+
struct adt7470_data {
struct i2c_client *client;
struct mutex lock;
@@ -688,6 +695,70 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
return count;
}
+/* These are the valid PWM frequencies to the nearest Hz */
+static const int adt7470_freq_map[] = {
+ 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
+};
+
+static ssize_t show_pwm_freq(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct adt7470_data *data = adt7470_update_device(dev);
+ unsigned char cfg_reg_1;
+ unsigned char cfg_reg_2;
+ int index;
+
+ mutex_lock(&data->lock);
+ cfg_reg_1 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG);
+ cfg_reg_2 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG_2);
+ mutex_unlock(&data->lock);
+
+ index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT;
+ if (!(cfg_reg_1 & ADT7470_CFG_LF))
+ index += 8;
+ if (index >= ARRAY_SIZE(adt7470_freq_map))
+ index = ARRAY_SIZE(adt7470_freq_map) - 1;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", adt7470_freq_map[index]);
+}
+
+static ssize_t set_pwm_freq(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adt7470_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long freq;
+ int index;
+ int low_freq = ADT7470_CFG_LF;
+ unsigned char val;
+
+ if (kstrtol(buf, 10, &freq))
+ return -EINVAL;
+
+ /* Round the user value given to the closest available frequency */
+ index = find_closest(freq, adt7470_freq_map,
+ ARRAY_SIZE(adt7470_freq_map));
+
+ if (index >= 8) {
+ index -= 8;
+ low_freq = 0;
+ }
+
+ mutex_lock(&data->lock);
+ /* Configuration Register 1 */
+ val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
+ i2c_smbus_write_byte_data(client, ADT7470_REG_CFG,
+ (val & ~ADT7470_CFG_LF) | low_freq);
+ /* Configuration Register 2 */
+ val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2);
+ i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2,
+ (val & ~ADT7470_FREQ_MASK) | (index << ADT7470_FREQ_SHIFT));
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
static ssize_t show_pwm_max(struct device *dev,
struct device_attribute *devattr,
char *buf)
@@ -1038,6 +1109,8 @@ static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
+static DEVICE_ATTR(pwm1_freq, S_IWUSR | S_IRUGO, show_pwm_freq, set_pwm_freq);
+
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
show_pwm_min, set_pwm_min, 0);
static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO,
@@ -1154,6 +1227,7 @@ static struct attribute *adt7470_attrs[] = {
&sensor_dev_attr_fan4_alarm.dev_attr.attr,
&sensor_dev_attr_force_pwm_max.dev_attr.attr,
&sensor_dev_attr_pwm1.dev_attr.attr,
+ &dev_attr_pwm1_freq.attr,
&sensor_dev_attr_pwm2.dev_attr.attr,
&sensor_dev_attr_pwm3.dev_attr.attr,
&sensor_dev_attr_pwm4.dev_attr.attr,
--
2.9.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] hwmon: adt7470: Expose PWM frequency to sysfs
2016-08-08 1:35 Joshua Scott
@ 2016-08-12 13:12 ` Guenter Roeck
0 siblings, 0 replies; 6+ messages in thread
From: Guenter Roeck @ 2016-08-12 13:12 UTC (permalink / raw)
To: Joshua Scott, Jean Delvare, linux-hwmon; +Cc: Chris Packham
On 08/07/2016 06:35 PM, Joshua Scott wrote:
> The ADT7470 supports a variety of PWM frequencies. This patch allows the
> frequency to be configured and viewed through the sysfs entry pwm1_freq.
>
> Signed-off-by: Joshua Scott <joshua.scott@alliedtelesis.co.nz>
Applied to -next.
Thanks,
Guenter
> ---
> Documentation/hwmon/adt7470 | 17 +++++++++++
> drivers/hwmon/adt7470.c | 74 +++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 91 insertions(+)
>
> diff --git a/Documentation/hwmon/adt7470 b/Documentation/hwmon/adt7470
> index 8ce4aa0..fe68e18 100644
> --- a/Documentation/hwmon/adt7470
> +++ b/Documentation/hwmon/adt7470
> @@ -65,6 +65,23 @@ from 0 (off) to 255 (full speed). Fan speed will be set to maximum when the
> temperature sensor associated with the PWM control exceeds
> pwm#_auto_point2_temp.
>
> +The driver also allows control of the PWM frequency:
> +
> +* pwm1_freq
> +
> +The PWM frequency is rounded to the nearest one of:
> +
> +* 11.0 Hz
> +* 14.7 Hz
> +* 22.1 Hz
> +* 29.4 Hz
> +* 35.3 Hz
> +* 44.1 Hz
> +* 58.8 Hz
> +* 88.2 Hz
> +* 1.4 kHz
> +* 22.5 kHz
> +
> Notes
> -----
>
> diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
> index f5da39a..94550d7 100644
> --- a/drivers/hwmon/adt7470.c
> +++ b/drivers/hwmon/adt7470.c
> @@ -32,6 +32,7 @@
> #include <linux/log2.h>
> #include <linux/kthread.h>
> #include <linux/slab.h>
> +#include <linux/util_macros.h>
>
> /* Addresses to scan */
> static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
> @@ -83,6 +84,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
> #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D
> #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E
> #define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71
> +#define ADT7470_REG_CFG_2 0x74
> #define ADT7470_REG_ACOUSTICS12 0x75
> #define ADT7470_REG_ACOUSTICS34 0x76
> #define ADT7470_REG_DEVICE 0x3D
> @@ -142,6 +144,11 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
> #define FAN_PERIOD_INVALID 65535
> #define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID)
>
> +/* Config registers 1 and 2 include fields for selecting the PWM frequency */
> +#define ADT7470_CFG_LF 0x40
> +#define ADT7470_FREQ_MASK 0x70
> +#define ADT7470_FREQ_SHIFT 4
> +
> struct adt7470_data {
> struct i2c_client *client;
> struct mutex lock;
> @@ -688,6 +695,70 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
> return count;
> }
>
> +/* These are the valid PWM frequencies to the nearest Hz */
> +static const int adt7470_freq_map[] = {
> + 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
> +};
> +
> +static ssize_t show_pwm_freq(struct device *dev,
> + struct device_attribute *devattr, char *buf)
> +{
> + struct adt7470_data *data = adt7470_update_device(dev);
> + unsigned char cfg_reg_1;
> + unsigned char cfg_reg_2;
> + int index;
> +
> + mutex_lock(&data->lock);
> + cfg_reg_1 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG);
> + cfg_reg_2 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG_2);
> + mutex_unlock(&data->lock);
> +
> + index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT;
> + if (!(cfg_reg_1 & ADT7470_CFG_LF))
> + index += 8;
> + if (index >= ARRAY_SIZE(adt7470_freq_map))
> + index = ARRAY_SIZE(adt7470_freq_map) - 1;
> +
> + return scnprintf(buf, PAGE_SIZE, "%d\n", adt7470_freq_map[index]);
> +}
> +
> +static ssize_t set_pwm_freq(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct adt7470_data *data = dev_get_drvdata(dev);
> + struct i2c_client *client = data->client;
> + long freq;
> + int index;
> + int low_freq = ADT7470_CFG_LF;
> + unsigned char val;
> +
> + if (kstrtol(buf, 10, &freq))
> + return -EINVAL;
> +
> + /* Round the user value given to the closest available frequency */
> + index = find_closest(freq, adt7470_freq_map,
> + ARRAY_SIZE(adt7470_freq_map));
> +
> + if (index >= 8) {
> + index -= 8;
> + low_freq = 0;
> + }
> +
> + mutex_lock(&data->lock);
> + /* Configuration Register 1 */
> + val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
> + i2c_smbus_write_byte_data(client, ADT7470_REG_CFG,
> + (val & ~ADT7470_CFG_LF) | low_freq);
> + /* Configuration Register 2 */
> + val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2);
> + i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2,
> + (val & ~ADT7470_FREQ_MASK) | (index << ADT7470_FREQ_SHIFT));
> + mutex_unlock(&data->lock);
> +
> + return count;
> +}
> +
> static ssize_t show_pwm_max(struct device *dev,
> struct device_attribute *devattr,
> char *buf)
> @@ -1038,6 +1109,8 @@ static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
> static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
> static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
>
> +static DEVICE_ATTR(pwm1_freq, S_IWUSR | S_IRUGO, show_pwm_freq, set_pwm_freq);
> +
> static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
> show_pwm_min, set_pwm_min, 0);
> static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO,
> @@ -1154,6 +1227,7 @@ static struct attribute *adt7470_attrs[] = {
> &sensor_dev_attr_fan4_alarm.dev_attr.attr,
> &sensor_dev_attr_force_pwm_max.dev_attr.attr,
> &sensor_dev_attr_pwm1.dev_attr.attr,
> + &dev_attr_pwm1_freq.attr,
> &sensor_dev_attr_pwm2.dev_attr.attr,
> &sensor_dev_attr_pwm3.dev_attr.attr,
> &sensor_dev_attr_pwm4.dev_attr.attr,
>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2016-08-12 13:12 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-08-01 5:41 [PATCH] hwmon: adt7470: Expose PWM frequency to sysfs Joshua Scott
2016-08-02 22:44 ` Guenter Roeck
-- strict thread matches above, loose matches on Subject: below --
2016-08-08 1:35 Joshua Scott
2016-08-12 13:12 ` Guenter Roeck
2016-05-16 5:20 Joshua Scott
2016-05-18 15:42 ` Guenter Roeck
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).