summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLei YU <mine260309@gmail.com>2017-11-12 22:27:33 -0500
committerGuenter Roeck <linux@roeck-us.net>2018-01-02 18:05:34 -0500
commitee249f271524d111aed8d6e7c61e220aa6b4d714 (patch)
treec63c697758f065320abdb1f75c207216de70c189
parent30a7acd573899fd8b8ac39236eff6468b195ac7d (diff)
hwmon: Add W83773G driver
Nuvoton W83773G is a hardware monitor IC providing one local temperature and two remote temperature sensors. Signed-off-by: Lei YU <mine260309@gmail.com> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
-rw-r--r--drivers/hwmon/Kconfig10
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/w83773g.c329
3 files changed, 340 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 7ad017690e3a..530ff7c9234c 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1725,6 +1725,16 @@ config SENSORS_VT8231
1725 This driver can also be built as a module. If so, the module 1725 This driver can also be built as a module. If so, the module
1726 will be called vt8231. 1726 will be called vt8231.
1727 1727
1728config SENSORS_W83773G
1729 tristate "Nuvoton W83773G"
1730 depends on I2C
1731 help
1732 If you say yes here you get support for the Nuvoton W83773G hardware
1733 monitoring chip.
1734
1735 This driver can also be built as a module. If so, the module
1736 will be called w83773g.
1737
1728config SENSORS_W83781D 1738config SENSORS_W83781D
1729 tristate "Winbond W83781D, W83782D, W83783S, Asus AS99127F" 1739 tristate "Winbond W83781D, W83782D, W83783S, Asus AS99127F"
1730 depends on I2C 1740 depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 0fe489fab663..f814b4ace138 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_SENSORS_ATK0110) += asus_atk0110.o
14# asb100, then w83781d go first, as they can override other drivers' addresses. 14# asb100, then w83781d go first, as they can override other drivers' addresses.
15obj-$(CONFIG_SENSORS_ASB100) += asb100.o 15obj-$(CONFIG_SENSORS_ASB100) += asb100.o
16obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o 16obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o
17obj-$(CONFIG_SENSORS_W83773G) += w83773g.o
17obj-$(CONFIG_SENSORS_W83792D) += w83792d.o 18obj-$(CONFIG_SENSORS_W83792D) += w83792d.o
18obj-$(CONFIG_SENSORS_W83793) += w83793.o 19obj-$(CONFIG_SENSORS_W83793) += w83793.o
19obj-$(CONFIG_SENSORS_W83795) += w83795.o 20obj-$(CONFIG_SENSORS_W83795) += w83795.o
diff --git a/drivers/hwmon/w83773g.c b/drivers/hwmon/w83773g.c
new file mode 100644
index 000000000000..0b97c285b049
--- /dev/null
+++ b/drivers/hwmon/w83773g.c
@@ -0,0 +1,329 @@
1/*
2 * Copyright (C) 2017 IBM Corp.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * Driver for the Nuvoton W83773G SMBus temperature sensor IC.
10 * Supported models: W83773G
11 */
12
13#include <linux/module.h>
14#include <linux/init.h>
15#include <linux/i2c.h>
16#include <linux/hwmon.h>
17#include <linux/hwmon-sysfs.h>
18#include <linux/err.h>
19#include <linux/of_device.h>
20#include <linux/regmap.h>
21
22/* W83773 has 3 channels */
23#define W83773_CHANNELS 3
24
25/* The W83773 registers */
26#define W83773_CONVERSION_RATE_REG_READ 0x04
27#define W83773_CONVERSION_RATE_REG_WRITE 0x0A
28#define W83773_MANUFACTURER_ID_REG 0xFE
29#define W83773_LOCAL_TEMP 0x00
30
31static const u8 W83773_STATUS[2] = { 0x02, 0x17 };
32
33static const u8 W83773_TEMP_LSB[2] = { 0x10, 0x25 };
34static const u8 W83773_TEMP_MSB[2] = { 0x01, 0x24 };
35
36static const u8 W83773_OFFSET_LSB[2] = { 0x12, 0x16 };
37static const u8 W83773_OFFSET_MSB[2] = { 0x11, 0x15 };
38
39/* this is the number of sensors in the device */
40static const struct i2c_device_id w83773_id[] = {
41 { "w83773g" },
42 { }
43};
44
45MODULE_DEVICE_TABLE(i2c, w83773_id);
46
47static const struct of_device_id w83773_of_match[] = {
48 {
49 .compatible = "nuvoton,w83773g"
50 },
51 { },
52};
53MODULE_DEVICE_TABLE(of, w83773_of_match);
54
55static inline long temp_of_local(s8 reg)
56{
57 return reg * 1000;
58}
59
60static inline long temp_of_remote(s8 hb, u8 lb)
61{
62 return (hb << 3 | lb >> 5) * 125;
63}
64
65static int get_local_temp(struct regmap *regmap, long *val)
66{
67 unsigned int regval;
68 int ret;
69
70 ret = regmap_read(regmap, W83773_LOCAL_TEMP, &regval);
71 if (ret < 0)
72 return ret;
73
74 *val = temp_of_local(regval);
75 return 0;
76}
77
78static int get_remote_temp(struct regmap *regmap, int index, long *val)
79{
80 unsigned int regval_high;
81 unsigned int regval_low;
82 int ret;
83
84 ret = regmap_read(regmap, W83773_TEMP_MSB[index], &regval_high);
85 if (ret < 0)
86 return ret;
87
88 ret = regmap_read(regmap, W83773_TEMP_LSB[index], &regval_low);
89 if (ret < 0)
90 return ret;
91
92 *val = temp_of_remote(regval_high, regval_low);
93 return 0;
94}
95
96static int get_fault(struct regmap *regmap, int index, long *val)
97{
98 unsigned int regval;
99 int ret;
100
101 ret = regmap_read(regmap, W83773_STATUS[index], &regval);
102 if (ret < 0)
103 return ret;
104
105 *val = (u8)regval & 0x04 >> 2;
106 return 0;
107}
108
109static int get_offset(struct regmap *regmap, int index, long *val)
110{
111 unsigned int regval_high;
112 unsigned int regval_low;
113 int ret;
114
115 ret = regmap_read(regmap, W83773_OFFSET_MSB[index], &regval_high);
116 if (ret < 0)
117 return ret;
118
119 ret = regmap_read(regmap, W83773_OFFSET_LSB[index], &regval_low);
120 if (ret < 0)
121 return ret;
122
123 *val = temp_of_remote(regval_high, regval_low);
124 return 0;
125}
126
127static int set_offset(struct regmap *regmap, int index, long val)
128{
129 int ret;
130 u8 high_byte;
131 u8 low_byte;
132
133 val = clamp_val(val, -127825, 127825);
134 /* offset value equals to (high_byte << 3 | low_byte >> 5) * 125 */
135 val /= 125;
136 high_byte = val >> 3;
137 low_byte = (val & 0x07) << 5;
138
139 ret = regmap_write(regmap, W83773_OFFSET_MSB[index], high_byte);
140 if (ret < 0)
141 return ret;
142
143 return regmap_write(regmap, W83773_OFFSET_LSB[index], low_byte);
144}
145
146static int get_update_interval(struct regmap *regmap, long *val)
147{
148 unsigned int regval;
149 int ret;
150
151 ret = regmap_read(regmap, W83773_CONVERSION_RATE_REG_READ, &regval);
152 if (ret < 0)
153 return ret;
154
155 *val = 16000 >> regval;
156 return 0;
157}
158
159static int set_update_interval(struct regmap *regmap, long val)
160{
161 int rate;
162
163 /*
164 * For valid rates, interval can be calculated as
165 * interval = (1 << (8 - rate)) * 62.5;
166 * Rounded rate is therefore
167 * rate = 8 - __fls(interval * 8 / (62.5 * 7));
168 * Use clamp_val() to avoid overflows, and to ensure valid input
169 * for __fls.
170 */
171 val = clamp_val(val, 62, 16000) * 10;
172 rate = 8 - __fls((val * 8 / (625 * 7)));
173 return regmap_write(regmap, W83773_CONVERSION_RATE_REG_WRITE, rate);
174}
175
176static int w83773_read(struct device *dev, enum hwmon_sensor_types type,
177 u32 attr, int channel, long *val)
178{
179 struct regmap *regmap = dev_get_drvdata(dev);
180
181 if (type == hwmon_chip) {
182 if (attr == hwmon_chip_update_interval)
183 return get_update_interval(regmap, val);
184 return -EOPNOTSUPP;
185 }
186
187 switch (attr) {
188 case hwmon_temp_input:
189 if (channel == 0)
190 return get_local_temp(regmap, val);
191 return get_remote_temp(regmap, channel - 1, val);
192 case hwmon_temp_fault:
193 return get_fault(regmap, channel - 1, val);
194 case hwmon_temp_offset:
195 return get_offset(regmap, channel - 1, val);
196 default:
197 return -EOPNOTSUPP;
198 }
199}
200
201static int w83773_write(struct device *dev, enum hwmon_sensor_types type,
202 u32 attr, int channel, long val)
203{
204 struct regmap *regmap = dev_get_drvdata(dev);
205
206 if (type == hwmon_chip && attr == hwmon_chip_update_interval)
207 return set_update_interval(regmap, val);
208
209 if (type == hwmon_temp && attr == hwmon_temp_offset)
210 return set_offset(regmap, channel - 1, val);
211
212 return -EOPNOTSUPP;
213}
214
215static umode_t w83773_is_visible(const void *data, enum hwmon_sensor_types type,
216 u32 attr, int channel)
217{
218 switch (type) {
219 case hwmon_chip:
220 switch (attr) {
221 case hwmon_chip_update_interval:
222 return 0644;
223 }
224 break;
225 case hwmon_temp:
226 switch (attr) {
227 case hwmon_temp_input:
228 case hwmon_temp_fault:
229 return 0444;
230 case hwmon_temp_offset:
231 return 0644;
232 }
233 break;
234 default:
235 break;
236 }
237 return 0;
238}
239
240static const u32 w83773_chip_config[] = {
241 HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL,
242 0
243};
244
245static const struct hwmon_channel_info w83773_chip = {
246 .type = hwmon_chip,
247 .config = w83773_chip_config,
248};
249
250static const u32 w83773_temp_config[] = {
251 HWMON_T_INPUT,
252 HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_OFFSET,
253 HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_OFFSET,
254 0
255};
256
257static const struct hwmon_channel_info w83773_temp = {
258 .type = hwmon_temp,
259 .config = w83773_temp_config,
260};
261
262static const struct hwmon_channel_info *w83773_info[] = {
263 &w83773_chip,
264 &w83773_temp,
265 NULL
266};
267
268static const struct hwmon_ops w83773_ops = {
269 .is_visible = w83773_is_visible,
270 .read = w83773_read,
271 .write = w83773_write,
272};
273
274static const struct hwmon_chip_info w83773_chip_info = {
275 .ops = &w83773_ops,
276 .info = w83773_info,
277};
278
279static const struct regmap_config w83773_regmap_config = {
280 .reg_bits = 8,
281 .val_bits = 8,
282};
283
284static int w83773_probe(struct i2c_client *client,
285 const struct i2c_device_id *id)
286{
287 struct device *dev = &client->dev;
288 struct device *hwmon_dev;
289 struct regmap *regmap;
290 int ret;
291
292 regmap = devm_regmap_init_i2c(client, &w83773_regmap_config);
293 if (IS_ERR(regmap)) {
294 dev_err(dev, "failed to allocate register map\n");
295 return PTR_ERR(regmap);
296 }
297
298 /* Set the conversion rate to 2 Hz */
299 ret = regmap_write(regmap, W83773_CONVERSION_RATE_REG_WRITE, 0x05);
300 if (ret < 0) {
301 dev_err(&client->dev, "error writing config rate register\n");
302 return ret;
303 }
304
305 i2c_set_clientdata(client, regmap);
306
307 hwmon_dev = devm_hwmon_device_register_with_info(dev,
308 client->name,
309 regmap,
310 &w83773_chip_info,
311 NULL);
312 return PTR_ERR_OR_ZERO(hwmon_dev);
313}
314
315static struct i2c_driver w83773_driver = {
316 .class = I2C_CLASS_HWMON,
317 .driver = {
318 .name = "w83773g",
319 .of_match_table = of_match_ptr(w83773_of_match),
320 },
321 .probe = w83773_probe,
322 .id_table = w83773_id,
323};
324
325module_i2c_driver(w83773_driver);
326
327MODULE_AUTHOR("Lei YU <mine260309@gmail.com>");
328MODULE_DESCRIPTION("W83773G temperature sensor driver");
329MODULE_LICENSE("GPL");