summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Verges <kg4ysn@gmail.com>2013-01-05 04:41:19 -0500
committerGuenter Roeck <linux@roeck-us.net>2013-02-06 12:57:57 -0500
commit8c14d126ae2efbcd094b24e5413b8cbe1d2c01e4 (patch)
treeac4ee5056c7889dda4a08d248f655f8b43e10c6b
parent91bba688016be0ba87ecb9d80d6490c2ebc63b0d (diff)
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>
-rw-r--r--Documentation/hwmon/lm7379
-rw-r--r--drivers/hwmon/lm73.c97
2 files changed, 165 insertions, 11 deletions
diff --git a/Documentation/hwmon/lm73 b/Documentation/hwmon/lm73
new file mode 100644
index 000000000000..782b3e1055ca
--- /dev/null
+++ b/Documentation/hwmon/lm73
@@ -0,0 +1,79 @@
1Kernel driver lm73
2==================
3
4Supported 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
11Author: Guillaume Ligneul <guillaume.ligneul@gmail.com>
12Documentation: Chris Verges <kg4ysn@gmail.com>
13
14
15Description
16-----------
17
18The LM73 is a digital temperature sensor. All temperature values are
19given in degrees Celsius.
20
21Measurement Resolution Support
22------------------------------
23
24The LM73 supports four resolutions, defined in terms of degrees C per
25LSB: 0.25, 0.125, 0.0625, and 0.3125. Changing the resolution mode
26affects the conversion time of the LM73's analog-to-digital converter.
27From userspace, the desired resolution can be specified as a function of
28conversion time via the 'update_interval' sysfs attribute for the
29device. This attribute will normalize ranges of input values to the
30maximum 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
41The following examples show how the 'update_interval' attribute can be
42used 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
68As 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
71conversion time actively in use.
72
73Mathematically, the resolution can be derived from the conversion time
74via the following function:
75
76 g(x) = 0.250 * [log(x/14) / log(2)]
77
78where 'x' is the output from 'update_interval' and 'g(x)' is the
79resolution in degrees C per LSB.
diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c
index dad83fea807d..0b19f03950e0 100644
--- a/drivers/hwmon/lm73.c
+++ b/drivers/hwmon/lm73.c
@@ -39,8 +39,24 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c,
39#define LM73_TEMP_MIN (-256000 / 250) 39#define LM73_TEMP_MIN (-256000 / 250)
40#define LM73_TEMP_MAX (255750 / 250) 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)
45
46static 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
53struct lm73_data {
54 struct device *hwmon_dev;
55 struct mutex lock;
56 u8 ctrl; /* control register value */
57};
43 58
59/*-----------------------------------------------------------------------*/
44 60
45static ssize_t set_temp(struct device *dev, struct device_attribute *da, 61static ssize_t set_temp(struct device *dev, struct device_attribute *da,
46 const char *buf, size_t count) 62 const char *buf, size_t count)
@@ -78,6 +94,51 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *da,
78 return scnprintf(buf, PAGE_SIZE, "%d\n", temp); 94 return scnprintf(buf, PAGE_SIZE, "%d\n", temp);
79} 95}
80 96
97static ssize_t set_convrate(struct device *dev, struct device_attribute *da,
98 const char *buf, size_t count)
99{
100 struct i2c_client *client = to_i2c_client(dev);
101 struct lm73_data *data = i2c_get_clientdata(client);
102 unsigned long convrate;
103 s32 err;
104 int res = 0;
105
106 err = kstrtoul(buf, 10, &convrate);
107 if (err < 0)
108 return err;
109
110 /*
111 * Convert the desired conversion rate into register bits.
112 * res is already initialized, and everything past the second-to-last
113 * value in the array is treated as belonging to the last value
114 * in the array.
115 */
116 while (res < (ARRAY_SIZE(lm73_convrates) - 1) &&
117 convrate > lm73_convrates[res])
118 res++;
119
120 mutex_lock(&data->lock);
121 data->ctrl &= LM73_CTRL_TO_MASK;
122 data->ctrl |= res << LM73_CTRL_RES_SHIFT;
123 err = i2c_smbus_write_byte_data(client, LM73_REG_CTRL, data->ctrl);
124 mutex_unlock(&data->lock);
125
126 if (err < 0)
127 return err;
128
129 return count;
130}
131
132static ssize_t show_convrate(struct device *dev, struct device_attribute *da,
133 char *buf)
134{
135 struct i2c_client *client = to_i2c_client(dev);
136 struct lm73_data *data = i2c_get_clientdata(client);
137 int res;
138
139 res = (data->ctrl & LM73_CTRL_RES_MASK) >> LM73_CTRL_RES_SHIFT;
140 return scnprintf(buf, PAGE_SIZE, "%hu\n", lm73_convrates[res]);
141}
81 142
82/*-----------------------------------------------------------------------*/ 143/*-----------------------------------------------------------------------*/
83 144
@@ -89,13 +150,14 @@ static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
89 show_temp, set_temp, LM73_REG_MIN); 150 show_temp, set_temp, LM73_REG_MIN);
90static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, 151static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
91 show_temp, NULL, LM73_REG_INPUT); 152 show_temp, NULL, LM73_REG_INPUT);
92 153static SENSOR_DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO,
154 show_convrate, set_convrate, 0);
93 155
94static struct attribute *lm73_attributes[] = { 156static struct attribute *lm73_attributes[] = {
95 &sensor_dev_attr_temp1_input.dev_attr.attr, 157 &sensor_dev_attr_temp1_input.dev_attr.attr,
96 &sensor_dev_attr_temp1_max.dev_attr.attr, 158 &sensor_dev_attr_temp1_max.dev_attr.attr,
97 &sensor_dev_attr_temp1_min.dev_attr.attr, 159 &sensor_dev_attr_temp1_min.dev_attr.attr,
98 160 &sensor_dev_attr_update_interval.dev_attr.attr,
99 NULL 161 NULL
100}; 162};
101 163
@@ -110,23 +172,36 @@ static const struct attribute_group lm73_group = {
110static int 172static int
111lm73_probe(struct i2c_client *client, const struct i2c_device_id *id) 173lm73_probe(struct i2c_client *client, const struct i2c_device_id *id)
112{ 174{
113 struct device *hwmon_dev;
114 int status; 175 int status;
176 struct lm73_data *data;
177 int ctrl;
178
179 data = devm_kzalloc(&client->dev, sizeof(struct lm73_data),
180 GFP_KERNEL);
181 if (!data)
182 return -ENOMEM;
183
184 i2c_set_clientdata(client, data);
185 mutex_init(&data->lock);
186
187 ctrl = i2c_smbus_read_byte_data(client, LM73_REG_CTRL);
188 if (ctrl < 0)
189 return ctrl;
190 data->ctrl = ctrl;
115 191
116 /* Register sysfs hooks */ 192 /* Register sysfs hooks */
117 status = sysfs_create_group(&client->dev.kobj, &lm73_group); 193 status = sysfs_create_group(&client->dev.kobj, &lm73_group);
118 if (status) 194 if (status)
119 return status; 195 return status;
120 196
121 hwmon_dev = hwmon_device_register(&client->dev); 197 data->hwmon_dev = hwmon_device_register(&client->dev);
122 if (IS_ERR(hwmon_dev)) { 198 if (IS_ERR(data->hwmon_dev)) {
123 status = PTR_ERR(hwmon_dev); 199 status = PTR_ERR(data->hwmon_dev);
124 goto exit_remove; 200 goto exit_remove;
125 } 201 }
126 i2c_set_clientdata(client, hwmon_dev);
127 202
128 dev_info(&client->dev, "%s: sensor '%s'\n", 203 dev_info(&client->dev, "%s: sensor '%s'\n",
129 dev_name(hwmon_dev), client->name); 204 dev_name(data->hwmon_dev), client->name);
130 205
131 return 0; 206 return 0;
132 207
@@ -137,9 +212,9 @@ exit_remove:
137 212
138static int lm73_remove(struct i2c_client *client) 213static int lm73_remove(struct i2c_client *client)
139{ 214{
140 struct device *hwmon_dev = i2c_get_clientdata(client); 215 struct lm73_data *data = i2c_get_clientdata(client);
141 216
142 hwmon_device_unregister(hwmon_dev); 217 hwmon_device_unregister(data->hwmon_dev);
143 sysfs_remove_group(&client->dev.kobj, &lm73_group); 218 sysfs_remove_group(&client->dev.kobj, &lm73_group);
144 return 0; 219 return 0;
145} 220}