Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hwmon: (lm73) Add 'update_interval' attribute

The LM73 supports four A/D conversion resolutions. The default used by
the existing lm73 driver is the chip's default, 11-bit (0.25 C/LSB).
This patch enables changing of this resolution from userspace via the
update_interval sysfs attribute. Full details on usage are included in
Documentation/hwmon/lm73.

Signed-off-by: Chris Verges <kg4ysn@gmail.com>
[linux@roeck-us.net: cleanup]
Signed-off-by: Guenter Roeck <linux@roeck-us.net>

authored by

Chris Verges and committed by
Guenter Roeck
8c14d126 91bba688

+165 -11
+79
Documentation/hwmon/lm73
··· 1 + Kernel driver lm73 2 + ================== 3 + 4 + Supported chips: 5 + * Texas Instruments LM73 6 + Prefix: 'lm73' 7 + Addresses scanned: I2C 0x48, 0x49, 0x4a, 0x4c, 0x4d, and 0x4e 8 + Datasheet: Publicly available at the Texas Instruments website 9 + http://www.ti.com/product/lm73 10 + 11 + Author: Guillaume Ligneul <guillaume.ligneul@gmail.com> 12 + Documentation: Chris Verges <kg4ysn@gmail.com> 13 + 14 + 15 + Description 16 + ----------- 17 + 18 + The LM73 is a digital temperature sensor. All temperature values are 19 + given in degrees Celsius. 20 + 21 + Measurement Resolution Support 22 + ------------------------------ 23 + 24 + The LM73 supports four resolutions, defined in terms of degrees C per 25 + LSB: 0.25, 0.125, 0.0625, and 0.3125. Changing the resolution mode 26 + affects the conversion time of the LM73's analog-to-digital converter. 27 + From userspace, the desired resolution can be specified as a function of 28 + conversion time via the 'update_interval' sysfs attribute for the 29 + device. This attribute will normalize ranges of input values to the 30 + maximum times defined for the resolution in the datasheet. 31 + 32 + Resolution Conv. Time Input Range 33 + (C/LSB) (msec) (msec) 34 + -------------------------------------- 35 + 0.25 14 0..14 36 + 0.125 28 15..28 37 + 0.0625 56 29..56 38 + 0.03125 112 57..infinity 39 + -------------------------------------- 40 + 41 + The following examples show how the 'update_interval' attribute can be 42 + used to change the conversion time: 43 + 44 + $ echo 0 > update_interval 45 + $ cat update_interval 46 + 14 47 + $ cat temp1_input 48 + 24250 49 + 50 + $ echo 22 > update_interval 51 + $ cat update_interval 52 + 28 53 + $ cat temp1_input 54 + 24125 55 + 56 + $ echo 56 > update_interval 57 + $ cat update_interval 58 + 56 59 + $ cat temp1_input 60 + 24062 61 + 62 + $ echo 85 > update_interval 63 + $ cat update_interval 64 + 112 65 + $ cat temp1_input 66 + 24031 67 + 68 + As shown here, the lm73 driver automatically adjusts any user input for 69 + 'update_interval' via a step function. Reading back the 70 + 'update_interval' value after a write operation will confirm the 71 + conversion time actively in use. 72 + 73 + Mathematically, the resolution can be derived from the conversion time 74 + via the following function: 75 + 76 + g(x) = 0.250 * [log(x/14) / log(2)] 77 + 78 + where 'x' is the output from 'update_interval' and 'g(x)' is the 79 + resolution in degrees C per LSB.
+86 -11
drivers/hwmon/lm73.c
··· 39 39 #define LM73_TEMP_MIN (-256000 / 250) 40 40 #define LM73_TEMP_MAX (255750 / 250) 41 41 42 - /*-----------------------------------------------------------------------*/ 42 + #define LM73_CTRL_RES_SHIFT 5 43 + #define LM73_CTRL_RES_MASK (BIT(5) | BIT(6)) 44 + #define LM73_CTRL_TO_MASK BIT(7) 43 45 46 + static const unsigned short lm73_convrates[] = { 47 + 14, /* 11-bits (0.25000 C/LSB): RES1 Bit = 0, RES0 Bit = 0 */ 48 + 28, /* 12-bits (0.12500 C/LSB): RES1 Bit = 0, RES0 Bit = 1 */ 49 + 56, /* 13-bits (0.06250 C/LSB): RES1 Bit = 1, RES0 Bit = 0 */ 50 + 112, /* 14-bits (0.03125 C/LSB): RES1 Bit = 1, RES0 Bit = 1 */ 51 + }; 52 + 53 + struct lm73_data { 54 + struct device *hwmon_dev; 55 + struct mutex lock; 56 + u8 ctrl; /* control register value */ 57 + }; 58 + 59 + /*-----------------------------------------------------------------------*/ 44 60 45 61 static ssize_t set_temp(struct device *dev, struct device_attribute *da, 46 62 const char *buf, size_t count) ··· 94 78 return scnprintf(buf, PAGE_SIZE, "%d\n", temp); 95 79 } 96 80 81 + static ssize_t set_convrate(struct device *dev, struct device_attribute *da, 82 + const char *buf, size_t count) 83 + { 84 + struct i2c_client *client = to_i2c_client(dev); 85 + struct lm73_data *data = i2c_get_clientdata(client); 86 + unsigned long convrate; 87 + s32 err; 88 + int res = 0; 89 + 90 + err = kstrtoul(buf, 10, &convrate); 91 + if (err < 0) 92 + return err; 93 + 94 + /* 95 + * Convert the desired conversion rate into register bits. 96 + * res is already initialized, and everything past the second-to-last 97 + * value in the array is treated as belonging to the last value 98 + * in the array. 99 + */ 100 + while (res < (ARRAY_SIZE(lm73_convrates) - 1) && 101 + convrate > lm73_convrates[res]) 102 + res++; 103 + 104 + mutex_lock(&data->lock); 105 + data->ctrl &= LM73_CTRL_TO_MASK; 106 + data->ctrl |= res << LM73_CTRL_RES_SHIFT; 107 + err = i2c_smbus_write_byte_data(client, LM73_REG_CTRL, data->ctrl); 108 + mutex_unlock(&data->lock); 109 + 110 + if (err < 0) 111 + return err; 112 + 113 + return count; 114 + } 115 + 116 + static ssize_t show_convrate(struct device *dev, struct device_attribute *da, 117 + char *buf) 118 + { 119 + struct i2c_client *client = to_i2c_client(dev); 120 + struct lm73_data *data = i2c_get_clientdata(client); 121 + int res; 122 + 123 + res = (data->ctrl & LM73_CTRL_RES_MASK) >> LM73_CTRL_RES_SHIFT; 124 + return scnprintf(buf, PAGE_SIZE, "%hu\n", lm73_convrates[res]); 125 + } 97 126 98 127 /*-----------------------------------------------------------------------*/ 99 128 ··· 150 89 show_temp, set_temp, LM73_REG_MIN); 151 90 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, 152 91 show_temp, NULL, LM73_REG_INPUT); 153 - 92 + static SENSOR_DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, 93 + show_convrate, set_convrate, 0); 154 94 155 95 static struct attribute *lm73_attributes[] = { 156 96 &sensor_dev_attr_temp1_input.dev_attr.attr, 157 97 &sensor_dev_attr_temp1_max.dev_attr.attr, 158 98 &sensor_dev_attr_temp1_min.dev_attr.attr, 159 - 99 + &sensor_dev_attr_update_interval.dev_attr.attr, 160 100 NULL 161 101 }; 162 102 ··· 172 110 static int 173 111 lm73_probe(struct i2c_client *client, const struct i2c_device_id *id) 174 112 { 175 - struct device *hwmon_dev; 176 113 int status; 114 + struct lm73_data *data; 115 + int ctrl; 116 + 117 + data = devm_kzalloc(&client->dev, sizeof(struct lm73_data), 118 + GFP_KERNEL); 119 + if (!data) 120 + return -ENOMEM; 121 + 122 + i2c_set_clientdata(client, data); 123 + mutex_init(&data->lock); 124 + 125 + ctrl = i2c_smbus_read_byte_data(client, LM73_REG_CTRL); 126 + if (ctrl < 0) 127 + return ctrl; 128 + data->ctrl = ctrl; 177 129 178 130 /* Register sysfs hooks */ 179 131 status = sysfs_create_group(&client->dev.kobj, &lm73_group); 180 132 if (status) 181 133 return status; 182 134 183 - hwmon_dev = hwmon_device_register(&client->dev); 184 - if (IS_ERR(hwmon_dev)) { 185 - status = PTR_ERR(hwmon_dev); 135 + data->hwmon_dev = hwmon_device_register(&client->dev); 136 + if (IS_ERR(data->hwmon_dev)) { 137 + status = PTR_ERR(data->hwmon_dev); 186 138 goto exit_remove; 187 139 } 188 - i2c_set_clientdata(client, hwmon_dev); 189 140 190 141 dev_info(&client->dev, "%s: sensor '%s'\n", 191 - dev_name(hwmon_dev), client->name); 142 + dev_name(data->hwmon_dev), client->name); 192 143 193 144 return 0; 194 145 ··· 212 137 213 138 static int lm73_remove(struct i2c_client *client) 214 139 { 215 - struct device *hwmon_dev = i2c_get_clientdata(client); 140 + struct lm73_data *data = i2c_get_clientdata(client); 216 141 217 - hwmon_device_unregister(hwmon_dev); 142 + hwmon_device_unregister(data->hwmon_dev); 218 143 sysfs_remove_group(&client->dev.kobj, &lm73_group); 219 144 return 0; 220 145 }