diff options
author | Chris Verges <kg4ysn@gmail.com> | 2013-01-05 04:41:19 -0500 |
---|---|---|
committer | Guenter Roeck <linux@roeck-us.net> | 2013-02-06 12:57:57 -0500 |
commit | 8c14d126ae2efbcd094b24e5413b8cbe1d2c01e4 (patch) | |
tree | ac4ee5056c7889dda4a08d248f655f8b43e10c6b /drivers | |
parent | 91bba688016be0ba87ecb9d80d6490c2ebc63b0d (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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hwmon/lm73.c | 97 |
1 files changed, 86 insertions, 11 deletions
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 | |||
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 | }; | ||
43 | 58 | ||
59 | /*-----------------------------------------------------------------------*/ | ||
44 | 60 | ||
45 | static ssize_t set_temp(struct device *dev, struct device_attribute *da, | 61 | static 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 | ||
97 | static 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 | |||
132 | static 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); |
90 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, | 151 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, |
91 | show_temp, NULL, LM73_REG_INPUT); | 152 | show_temp, NULL, LM73_REG_INPUT); |
92 | 153 | static SENSOR_DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, | |
154 | show_convrate, set_convrate, 0); | ||
93 | 155 | ||
94 | static struct attribute *lm73_attributes[] = { | 156 | static 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 = { | |||
110 | static int | 172 | static int |
111 | lm73_probe(struct i2c_client *client, const struct i2c_device_id *id) | 173 | lm73_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 | ||
138 | static int lm73_remove(struct i2c_client *client) | 213 | static 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 | } |