diff options
author | Vadim Pasternak <vadimp@mellanox.com> | 2017-10-03 14:08:27 -0400 |
---|---|---|
committer | Guenter Roeck <linux@roeck-us.net> | 2017-10-29 21:36:03 -0400 |
commit | 92b64580f14b24a3d5cfd1e9dff0b745826a824b (patch) | |
tree | d8d256e060e0f853ede11701c1bde2359168c2a2 | |
parent | 7a76a7f34afdfb080ec8e51ed18891b4f72ec907 (diff) |
hwmon: (max6621) Add support for Maxim MAX6621 temperature sensor
MAX6621 is a PECI-to-I2C translator provides an efficient, low-cost
solution for PECI-to-SMBus/I2C protocol conversion. It allows reading the
temperature from the PECI-compliant host directly from up to four
PECI-enabled CPUs.
Signed-off-by: Vadim Pasternak <vadimp@mellanox.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
-rw-r--r-- | drivers/hwmon/Kconfig | 14 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/max6621.c | 593 |
3 files changed, 608 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d65431417b17..fae8a8904c10 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig | |||
@@ -862,6 +862,20 @@ tristate "MAX31722 temperature sensor" | |||
862 | This driver can also be built as a module. If so, the module | 862 | This driver can also be built as a module. If so, the module |
863 | will be called max31722. | 863 | will be called max31722. |
864 | 864 | ||
865 | config SENSORS_MAX6621 | ||
866 | tristate "Maxim MAX6621 sensor chip" | ||
867 | depends on I2C | ||
868 | select REGMAP_I2C | ||
869 | help | ||
870 | If you say yes here you get support for MAX6621 sensor chip. | ||
871 | MAX6621 is a PECI-to-I2C translator provides an efficient, | ||
872 | low-cost solution for PECI-to-SMBus/I2C protocol conversion. | ||
873 | It allows reading the temperature from the PECI-compliant | ||
874 | host directly from up to four PECI-enabled CPUs. | ||
875 | |||
876 | This driver can also be built as a module. If so, the module | ||
877 | will be called max6621. | ||
878 | |||
865 | config SENSORS_MAX6639 | 879 | config SENSORS_MAX6639 |
866 | tristate "Maxim MAX6639 sensor chip" | 880 | tristate "Maxim MAX6639 sensor chip" |
867 | depends on I2C | 881 | depends on I2C |
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index c84d9784be98..8941a478d2a7 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile | |||
@@ -117,6 +117,7 @@ obj-$(CONFIG_SENSORS_MAX1619) += max1619.o | |||
117 | obj-$(CONFIG_SENSORS_MAX1668) += max1668.o | 117 | obj-$(CONFIG_SENSORS_MAX1668) += max1668.o |
118 | obj-$(CONFIG_SENSORS_MAX197) += max197.o | 118 | obj-$(CONFIG_SENSORS_MAX197) += max197.o |
119 | obj-$(CONFIG_SENSORS_MAX31722) += max31722.o | 119 | obj-$(CONFIG_SENSORS_MAX31722) += max31722.o |
120 | obj-$(CONFIG_SENSORS_MAX6621) += max6621.o | ||
120 | obj-$(CONFIG_SENSORS_MAX6639) += max6639.o | 121 | obj-$(CONFIG_SENSORS_MAX6639) += max6639.o |
121 | obj-$(CONFIG_SENSORS_MAX6642) += max6642.o | 122 | obj-$(CONFIG_SENSORS_MAX6642) += max6642.o |
122 | obj-$(CONFIG_SENSORS_MAX6650) += max6650.o | 123 | obj-$(CONFIG_SENSORS_MAX6650) += max6650.o |
diff --git a/drivers/hwmon/max6621.c b/drivers/hwmon/max6621.c new file mode 100644 index 000000000000..22079ec29660 --- /dev/null +++ b/drivers/hwmon/max6621.c | |||
@@ -0,0 +1,593 @@ | |||
1 | /* | ||
2 | * Hardware monitoring driver for Maxim MAX6621 | ||
3 | * | ||
4 | * Copyright (c) 2017 Mellanox Technologies. All rights reserved. | ||
5 | * Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/bitops.h> | ||
19 | #include <linux/hwmon.h> | ||
20 | #include <linux/hwmon-sysfs.h> | ||
21 | #include <linux/i2c.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/of_device.h> | ||
25 | #include <linux/regmap.h> | ||
26 | |||
27 | #define MAX6621_DRV_NAME "max6621" | ||
28 | #define MAX6621_TEMP_INPUT_REG_NUM 9 | ||
29 | #define MAX6621_TEMP_INPUT_MIN -127000 | ||
30 | #define MAX6621_TEMP_INPUT_MAX 128000 | ||
31 | #define MAX6621_TEMP_ALERT_CHAN_SHIFT 1 | ||
32 | |||
33 | #define MAX6621_TEMP_S0D0_REG 0x00 | ||
34 | #define MAX6621_TEMP_S0D1_REG 0x01 | ||
35 | #define MAX6621_TEMP_S1D0_REG 0x02 | ||
36 | #define MAX6621_TEMP_S1D1_REG 0x03 | ||
37 | #define MAX6621_TEMP_S2D0_REG 0x04 | ||
38 | #define MAX6621_TEMP_S2D1_REG 0x05 | ||
39 | #define MAX6621_TEMP_S3D0_REG 0x06 | ||
40 | #define MAX6621_TEMP_S3D1_REG 0x07 | ||
41 | #define MAX6621_TEMP_MAX_REG 0x08 | ||
42 | #define MAX6621_TEMP_MAX_ADDR_REG 0x0a | ||
43 | #define MAX6621_TEMP_ALERT_CAUSE_REG 0x0b | ||
44 | #define MAX6621_CONFIG0_REG 0x0c | ||
45 | #define MAX6621_CONFIG1_REG 0x0d | ||
46 | #define MAX6621_CONFIG2_REG 0x0e | ||
47 | #define MAX6621_CONFIG3_REG 0x0f | ||
48 | #define MAX6621_TEMP_S0_ALERT_REG 0x10 | ||
49 | #define MAX6621_TEMP_S1_ALERT_REG 0x11 | ||
50 | #define MAX6621_TEMP_S2_ALERT_REG 0x12 | ||
51 | #define MAX6621_TEMP_S3_ALERT_REG 0x13 | ||
52 | #define MAX6621_CLEAR_ALERT_REG 0x15 | ||
53 | #define MAX6621_REG_MAX (MAX6621_CLEAR_ALERT_REG + 1) | ||
54 | #define MAX6621_REG_TEMP_SHIFT 0x06 | ||
55 | |||
56 | #define MAX6621_ENABLE_TEMP_ALERTS_BIT 4 | ||
57 | #define MAX6621_ENABLE_I2C_CRC_BIT 5 | ||
58 | #define MAX6621_ENABLE_ALTERNATE_DATA 6 | ||
59 | #define MAX6621_ENABLE_LOCKUP_TO 7 | ||
60 | #define MAX6621_ENABLE_S0D0_BIT 8 | ||
61 | #define MAX6621_ENABLE_S3D1_BIT 15 | ||
62 | #define MAX6621_ENABLE_TEMP_ALL GENMASK(MAX6621_ENABLE_S3D1_BIT, \ | ||
63 | MAX6621_ENABLE_S0D0_BIT) | ||
64 | #define MAX6621_POLL_DELAY_MASK 0x5 | ||
65 | #define MAX6621_CONFIG0_INIT (MAX6621_ENABLE_TEMP_ALL | \ | ||
66 | BIT(MAX6621_ENABLE_LOCKUP_TO) | \ | ||
67 | BIT(MAX6621_ENABLE_I2C_CRC_BIT) | \ | ||
68 | MAX6621_POLL_DELAY_MASK) | ||
69 | #define MAX6621_PECI_BIT_TIME 0x2 | ||
70 | #define MAX6621_PECI_RETRY_NUM 0x3 | ||
71 | #define MAX6621_CONFIG1_INIT ((MAX6621_PECI_BIT_TIME << 8) | \ | ||
72 | MAX6621_PECI_RETRY_NUM) | ||
73 | |||
74 | /* Error codes */ | ||
75 | #define MAX6621_TRAN_FAILED 0x8100 /* | ||
76 | * PECI transaction failed for more | ||
77 | * than the configured number of | ||
78 | * consecutive retries. | ||
79 | */ | ||
80 | #define MAX6621_POOL_DIS 0x8101 /* | ||
81 | * Polling disabled for requested | ||
82 | * socket/domain. | ||
83 | */ | ||
84 | #define MAX6621_POOL_UNCOMPLETE 0x8102 /* | ||
85 | * First poll not yet completed for | ||
86 | * requested socket/domain (on | ||
87 | * startup). | ||
88 | */ | ||
89 | #define MAX6621_SD_DIS 0x8103 /* | ||
90 | * Read maximum temperature requested, | ||
91 | * but no sockets/domains enabled or | ||
92 | * all enabled sockets/domains have | ||
93 | * errors; or read maximum temperature | ||
94 | * address requested, but read maximum | ||
95 | * temperature was not called. | ||
96 | */ | ||
97 | #define MAX6621_ALERT_DIS 0x8104 /* | ||
98 | * Get alert socket/domain requested, | ||
99 | * but no alert active. | ||
100 | */ | ||
101 | #define MAX6621_PECI_ERR_MIN 0x8000 /* Intel spec PECI error min value. */ | ||
102 | #define MAX6621_PECI_ERR_MAX 0x80ff /* Intel spec PECI error max value. */ | ||
103 | |||
104 | static const u32 max6621_temp_regs[] = { | ||
105 | MAX6621_TEMP_MAX_REG, MAX6621_TEMP_S0D0_REG, MAX6621_TEMP_S1D0_REG, | ||
106 | MAX6621_TEMP_S2D0_REG, MAX6621_TEMP_S3D0_REG, MAX6621_TEMP_S0D1_REG, | ||
107 | MAX6621_TEMP_S1D1_REG, MAX6621_TEMP_S2D1_REG, MAX6621_TEMP_S3D1_REG, | ||
108 | }; | ||
109 | |||
110 | static const char *const max6621_temp_labels[] = { | ||
111 | "maximum", | ||
112 | "socket0_0", | ||
113 | "socket1_0", | ||
114 | "socket2_0", | ||
115 | "socket3_0", | ||
116 | "socket0_1", | ||
117 | "socket1_1", | ||
118 | "socket2_1", | ||
119 | "socket3_1", | ||
120 | }; | ||
121 | |||
122 | static const int max6621_temp_alert_chan2reg[] = { | ||
123 | MAX6621_TEMP_S0_ALERT_REG, | ||
124 | MAX6621_TEMP_S1_ALERT_REG, | ||
125 | MAX6621_TEMP_S2_ALERT_REG, | ||
126 | MAX6621_TEMP_S3_ALERT_REG, | ||
127 | }; | ||
128 | |||
129 | /** | ||
130 | * struct max6621_data - private data: | ||
131 | * | ||
132 | * @client: I2C client; | ||
133 | * @regmap: register map handle; | ||
134 | * @input_chan2reg: mapping from channel to register; | ||
135 | */ | ||
136 | struct max6621_data { | ||
137 | struct i2c_client *client; | ||
138 | struct regmap *regmap; | ||
139 | int input_chan2reg[MAX6621_TEMP_INPUT_REG_NUM + 1]; | ||
140 | }; | ||
141 | |||
142 | static long max6621_temp_mc2reg(long val) | ||
143 | { | ||
144 | return (val / 1000L) << MAX6621_REG_TEMP_SHIFT; | ||
145 | } | ||
146 | |||
147 | static umode_t | ||
148 | max6621_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, | ||
149 | int channel) | ||
150 | { | ||
151 | /* Skip channels which are not physically conncted. */ | ||
152 | if (((struct max6621_data *)data)->input_chan2reg[channel] < 0) | ||
153 | return 0; | ||
154 | |||
155 | switch (type) { | ||
156 | case hwmon_temp: | ||
157 | switch (attr) { | ||
158 | case hwmon_temp_input: | ||
159 | case hwmon_temp_label: | ||
160 | case hwmon_temp_crit_alarm: | ||
161 | return 0444; | ||
162 | case hwmon_temp_offset: | ||
163 | case hwmon_temp_crit: | ||
164 | return 0644; | ||
165 | default: | ||
166 | break; | ||
167 | } | ||
168 | |||
169 | default: | ||
170 | break; | ||
171 | } | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static int max6621_verify_reg_data(struct device *dev, int regval) | ||
177 | { | ||
178 | if (regval >= MAX6621_PECI_ERR_MIN && | ||
179 | regval <= MAX6621_PECI_ERR_MAX) { | ||
180 | dev_dbg(dev, "PECI error code - err 0x%04x.\n", | ||
181 | regval); | ||
182 | |||
183 | return -EIO; | ||
184 | } | ||
185 | |||
186 | switch (regval) { | ||
187 | case MAX6621_TRAN_FAILED: | ||
188 | dev_dbg(dev, "PECI transaction failed - err 0x%04x.\n", | ||
189 | regval); | ||
190 | return -EIO; | ||
191 | case MAX6621_POOL_DIS: | ||
192 | dev_dbg(dev, "Polling disabled - err 0x%04x.\n", regval); | ||
193 | return -EOPNOTSUPP; | ||
194 | case MAX6621_POOL_UNCOMPLETE: | ||
195 | dev_dbg(dev, "First poll not completed on startup - err 0x%04x.\n", | ||
196 | regval); | ||
197 | return -EIO; | ||
198 | case MAX6621_SD_DIS: | ||
199 | dev_dbg(dev, "Resource is disabled - err 0x%04x.\n", regval); | ||
200 | return -EOPNOTSUPP; | ||
201 | case MAX6621_ALERT_DIS: | ||
202 | dev_dbg(dev, "No alert active - err 0x%04x.\n", regval); | ||
203 | return -EOPNOTSUPP; | ||
204 | default: | ||
205 | return 0; | ||
206 | } | ||
207 | } | ||
208 | |||
209 | static int | ||
210 | max6621_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, | ||
211 | int channel, long *val) | ||
212 | { | ||
213 | struct max6621_data *data = dev_get_drvdata(dev); | ||
214 | u32 regval; | ||
215 | int reg; | ||
216 | s8 temp; | ||
217 | int ret; | ||
218 | |||
219 | switch (type) { | ||
220 | case hwmon_temp: | ||
221 | switch (attr) { | ||
222 | case hwmon_temp_input: | ||
223 | reg = data->input_chan2reg[channel]; | ||
224 | ret = regmap_read(data->regmap, reg, ®val); | ||
225 | if (ret) | ||
226 | return ret; | ||
227 | |||
228 | ret = max6621_verify_reg_data(dev, regval); | ||
229 | if (ret) | ||
230 | return ret; | ||
231 | |||
232 | /* | ||
233 | * Bit MAX6621_REG_TEMP_SHIFT represents 1 degree step. | ||
234 | * The temperature is given in two's complement and 8 | ||
235 | * bits is used for the register conversion. | ||
236 | */ | ||
237 | temp = (regval >> MAX6621_REG_TEMP_SHIFT); | ||
238 | *val = temp * 1000L; | ||
239 | |||
240 | break; | ||
241 | case hwmon_temp_offset: | ||
242 | ret = regmap_read(data->regmap, MAX6621_CONFIG2_REG, | ||
243 | ®val); | ||
244 | if (ret) | ||
245 | return ret; | ||
246 | |||
247 | ret = max6621_verify_reg_data(dev, regval); | ||
248 | if (ret) | ||
249 | return ret; | ||
250 | |||
251 | *val = (regval >> MAX6621_REG_TEMP_SHIFT) * | ||
252 | 1000L; | ||
253 | |||
254 | break; | ||
255 | case hwmon_temp_crit: | ||
256 | channel -= MAX6621_TEMP_ALERT_CHAN_SHIFT; | ||
257 | reg = max6621_temp_alert_chan2reg[channel]; | ||
258 | ret = regmap_read(data->regmap, reg, ®val); | ||
259 | if (ret) | ||
260 | return ret; | ||
261 | |||
262 | ret = max6621_verify_reg_data(dev, regval); | ||
263 | if (ret) | ||
264 | return ret; | ||
265 | |||
266 | *val = regval * 1000L; | ||
267 | |||
268 | break; | ||
269 | case hwmon_temp_crit_alarm: | ||
270 | /* | ||
271 | * Set val to zero to recover the case, when reading | ||
272 | * MAX6621_TEMP_ALERT_CAUSE_REG results in for example | ||
273 | * MAX6621_ALERT_DIS. Reading will return with error, | ||
274 | * but in such case alarm should be returned as 0. | ||
275 | */ | ||
276 | *val = 0; | ||
277 | ret = regmap_read(data->regmap, | ||
278 | MAX6621_TEMP_ALERT_CAUSE_REG, | ||
279 | ®val); | ||
280 | if (ret) | ||
281 | return ret; | ||
282 | |||
283 | ret = max6621_verify_reg_data(dev, regval); | ||
284 | if (ret) { | ||
285 | /* Do not report error if alert is disabled. */ | ||
286 | if (regval == MAX6621_ALERT_DIS) | ||
287 | return 0; | ||
288 | else | ||
289 | return ret; | ||
290 | } | ||
291 | |||
292 | /* | ||
293 | * Clear the alert automatically, using send-byte | ||
294 | * smbus protocol for clearing alert. | ||
295 | */ | ||
296 | if (regval) { | ||
297 | ret = i2c_smbus_write_byte(data->client, | ||
298 | MAX6621_CLEAR_ALERT_REG); | ||
299 | if (!ret) | ||
300 | return ret; | ||
301 | } | ||
302 | |||
303 | *val = !!regval; | ||
304 | |||
305 | break; | ||
306 | default: | ||
307 | return -EOPNOTSUPP; | ||
308 | } | ||
309 | break; | ||
310 | |||
311 | default: | ||
312 | return -EOPNOTSUPP; | ||
313 | } | ||
314 | |||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | static int | ||
319 | max6621_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, | ||
320 | int channel, long val) | ||
321 | { | ||
322 | struct max6621_data *data = dev_get_drvdata(dev); | ||
323 | u32 reg; | ||
324 | |||
325 | switch (type) { | ||
326 | case hwmon_temp: | ||
327 | switch (attr) { | ||
328 | case hwmon_temp_offset: | ||
329 | /* Clamp to allowed range to prevent overflow. */ | ||
330 | val = clamp_val(val, MAX6621_TEMP_INPUT_MIN, | ||
331 | MAX6621_TEMP_INPUT_MAX); | ||
332 | val = max6621_temp_mc2reg(val); | ||
333 | |||
334 | return regmap_write(data->regmap, | ||
335 | MAX6621_CONFIG2_REG, val); | ||
336 | case hwmon_temp_crit: | ||
337 | channel -= MAX6621_TEMP_ALERT_CHAN_SHIFT; | ||
338 | reg = max6621_temp_alert_chan2reg[channel]; | ||
339 | /* Clamp to allowed range to prevent overflow. */ | ||
340 | val = clamp_val(val, MAX6621_TEMP_INPUT_MIN, | ||
341 | MAX6621_TEMP_INPUT_MAX); | ||
342 | val = val / 1000L; | ||
343 | |||
344 | return regmap_write(data->regmap, reg, val); | ||
345 | default: | ||
346 | return -EOPNOTSUPP; | ||
347 | } | ||
348 | break; | ||
349 | |||
350 | default: | ||
351 | return -EOPNOTSUPP; | ||
352 | } | ||
353 | |||
354 | return -EOPNOTSUPP; | ||
355 | } | ||
356 | |||
357 | static int | ||
358 | max6621_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, | ||
359 | int channel, const char **str) | ||
360 | { | ||
361 | switch (type) { | ||
362 | case hwmon_temp: | ||
363 | switch (attr) { | ||
364 | case hwmon_temp_label: | ||
365 | *str = max6621_temp_labels[channel]; | ||
366 | return 0; | ||
367 | default: | ||
368 | return -EOPNOTSUPP; | ||
369 | } | ||
370 | break; | ||
371 | default: | ||
372 | return -EOPNOTSUPP; | ||
373 | } | ||
374 | |||
375 | return -EOPNOTSUPP; | ||
376 | } | ||
377 | |||
378 | static bool max6621_writeable_reg(struct device *dev, unsigned int reg) | ||
379 | { | ||
380 | switch (reg) { | ||
381 | case MAX6621_CONFIG0_REG: | ||
382 | case MAX6621_CONFIG1_REG: | ||
383 | case MAX6621_CONFIG2_REG: | ||
384 | case MAX6621_CONFIG3_REG: | ||
385 | case MAX6621_TEMP_S0_ALERT_REG: | ||
386 | case MAX6621_TEMP_S1_ALERT_REG: | ||
387 | case MAX6621_TEMP_S2_ALERT_REG: | ||
388 | case MAX6621_TEMP_S3_ALERT_REG: | ||
389 | case MAX6621_TEMP_ALERT_CAUSE_REG: | ||
390 | return true; | ||
391 | } | ||
392 | return false; | ||
393 | } | ||
394 | |||
395 | static bool max6621_readable_reg(struct device *dev, unsigned int reg) | ||
396 | { | ||
397 | switch (reg) { | ||
398 | case MAX6621_TEMP_S0D0_REG: | ||
399 | case MAX6621_TEMP_S0D1_REG: | ||
400 | case MAX6621_TEMP_S1D0_REG: | ||
401 | case MAX6621_TEMP_S1D1_REG: | ||
402 | case MAX6621_TEMP_S2D0_REG: | ||
403 | case MAX6621_TEMP_S2D1_REG: | ||
404 | case MAX6621_TEMP_S3D0_REG: | ||
405 | case MAX6621_TEMP_S3D1_REG: | ||
406 | case MAX6621_TEMP_MAX_REG: | ||
407 | case MAX6621_TEMP_MAX_ADDR_REG: | ||
408 | case MAX6621_CONFIG0_REG: | ||
409 | case MAX6621_CONFIG1_REG: | ||
410 | case MAX6621_CONFIG2_REG: | ||
411 | case MAX6621_CONFIG3_REG: | ||
412 | case MAX6621_TEMP_S0_ALERT_REG: | ||
413 | case MAX6621_TEMP_S1_ALERT_REG: | ||
414 | case MAX6621_TEMP_S2_ALERT_REG: | ||
415 | case MAX6621_TEMP_S3_ALERT_REG: | ||
416 | return true; | ||
417 | } | ||
418 | return false; | ||
419 | } | ||
420 | |||
421 | static bool max6621_volatile_reg(struct device *dev, unsigned int reg) | ||
422 | { | ||
423 | switch (reg) { | ||
424 | case MAX6621_TEMP_S0D0_REG: | ||
425 | case MAX6621_TEMP_S0D1_REG: | ||
426 | case MAX6621_TEMP_S1D0_REG: | ||
427 | case MAX6621_TEMP_S1D1_REG: | ||
428 | case MAX6621_TEMP_S2D0_REG: | ||
429 | case MAX6621_TEMP_S2D1_REG: | ||
430 | case MAX6621_TEMP_S3D0_REG: | ||
431 | case MAX6621_TEMP_S3D1_REG: | ||
432 | case MAX6621_TEMP_MAX_REG: | ||
433 | case MAX6621_TEMP_S0_ALERT_REG: | ||
434 | case MAX6621_TEMP_S1_ALERT_REG: | ||
435 | case MAX6621_TEMP_S2_ALERT_REG: | ||
436 | case MAX6621_TEMP_S3_ALERT_REG: | ||
437 | case MAX6621_TEMP_ALERT_CAUSE_REG: | ||
438 | return true; | ||
439 | } | ||
440 | return false; | ||
441 | } | ||
442 | |||
443 | static const struct reg_default max6621_regmap_default[] = { | ||
444 | { MAX6621_CONFIG0_REG, MAX6621_CONFIG0_INIT }, | ||
445 | { MAX6621_CONFIG1_REG, MAX6621_CONFIG1_INIT }, | ||
446 | }; | ||
447 | |||
448 | static const struct regmap_config max6621_regmap_config = { | ||
449 | .reg_bits = 8, | ||
450 | .val_bits = 16, | ||
451 | .max_register = MAX6621_REG_MAX, | ||
452 | .val_format_endian = REGMAP_ENDIAN_LITTLE, | ||
453 | .cache_type = REGCACHE_FLAT, | ||
454 | .writeable_reg = max6621_writeable_reg, | ||
455 | .readable_reg = max6621_readable_reg, | ||
456 | .volatile_reg = max6621_volatile_reg, | ||
457 | .reg_defaults = max6621_regmap_default, | ||
458 | .num_reg_defaults = ARRAY_SIZE(max6621_regmap_default), | ||
459 | }; | ||
460 | |||
461 | static u32 max6621_chip_config[] = { | ||
462 | HWMON_C_REGISTER_TZ, | ||
463 | 0 | ||
464 | }; | ||
465 | |||
466 | static const struct hwmon_channel_info max6621_chip = { | ||
467 | .type = hwmon_chip, | ||
468 | .config = max6621_chip_config, | ||
469 | }; | ||
470 | |||
471 | static const u32 max6621_temp_config[] = { | ||
472 | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | ||
473 | HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_LABEL, | ||
474 | HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_LABEL, | ||
475 | HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_LABEL, | ||
476 | HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_LABEL, | ||
477 | HWMON_T_INPUT | HWMON_T_LABEL, | ||
478 | HWMON_T_INPUT | HWMON_T_LABEL, | ||
479 | HWMON_T_INPUT | HWMON_T_LABEL, | ||
480 | HWMON_T_INPUT | HWMON_T_LABEL, | ||
481 | 0 | ||
482 | }; | ||
483 | |||
484 | static const struct hwmon_channel_info max6621_temp = { | ||
485 | .type = hwmon_temp, | ||
486 | .config = max6621_temp_config, | ||
487 | }; | ||
488 | |||
489 | static const struct hwmon_channel_info *max6621_info[] = { | ||
490 | &max6621_chip, | ||
491 | &max6621_temp, | ||
492 | NULL | ||
493 | }; | ||
494 | |||
495 | static const struct hwmon_ops max6621_hwmon_ops = { | ||
496 | .read = max6621_read, | ||
497 | .write = max6621_write, | ||
498 | .read_string = max6621_read_string, | ||
499 | .is_visible = max6621_is_visible, | ||
500 | }; | ||
501 | |||
502 | static const struct hwmon_chip_info max6621_chip_info = { | ||
503 | .ops = &max6621_hwmon_ops, | ||
504 | .info = max6621_info, | ||
505 | }; | ||
506 | |||
507 | static int max6621_probe(struct i2c_client *client, | ||
508 | const struct i2c_device_id *id) | ||
509 | { | ||
510 | struct device *dev = &client->dev; | ||
511 | struct max6621_data *data; | ||
512 | struct device *hwmon_dev; | ||
513 | int i; | ||
514 | int ret; | ||
515 | |||
516 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | ||
517 | if (!data) | ||
518 | return -ENOMEM; | ||
519 | |||
520 | data->regmap = devm_regmap_init_i2c(client, &max6621_regmap_config); | ||
521 | if (IS_ERR(data->regmap)) | ||
522 | return PTR_ERR(data->regmap); | ||
523 | |||
524 | i2c_set_clientdata(client, data); | ||
525 | data->client = client; | ||
526 | |||
527 | /* Set CONFIG0 register masking temperature alerts and PEC. */ | ||
528 | ret = regmap_write(data->regmap, MAX6621_CONFIG0_REG, | ||
529 | MAX6621_CONFIG0_INIT); | ||
530 | if (ret) | ||
531 | return ret; | ||
532 | |||
533 | /* Set CONFIG1 register for PEC access retry number. */ | ||
534 | ret = regmap_write(data->regmap, MAX6621_CONFIG1_REG, | ||
535 | MAX6621_CONFIG1_INIT); | ||
536 | if (ret) | ||
537 | return ret; | ||
538 | |||
539 | /* Sync registers with hardware. */ | ||
540 | regcache_mark_dirty(data->regmap); | ||
541 | ret = regcache_sync(data->regmap); | ||
542 | if (ret) | ||
543 | return ret; | ||
544 | |||
545 | /* Verify which temperature input registers are enabled. */ | ||
546 | for (i = 0; i < MAX6621_TEMP_INPUT_REG_NUM; i++) { | ||
547 | ret = i2c_smbus_read_word_data(client, max6621_temp_regs[i]); | ||
548 | if (ret < 0) | ||
549 | return ret; | ||
550 | ret = max6621_verify_reg_data(dev, ret); | ||
551 | if (ret) { | ||
552 | data->input_chan2reg[i] = -1; | ||
553 | continue; | ||
554 | } | ||
555 | |||
556 | data->input_chan2reg[i] = max6621_temp_regs[i]; | ||
557 | } | ||
558 | |||
559 | hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, | ||
560 | data, | ||
561 | &max6621_chip_info, | ||
562 | NULL); | ||
563 | |||
564 | return PTR_ERR_OR_ZERO(hwmon_dev); | ||
565 | } | ||
566 | |||
567 | static const struct i2c_device_id max6621_id[] = { | ||
568 | { MAX6621_DRV_NAME, 0 }, | ||
569 | { } | ||
570 | }; | ||
571 | MODULE_DEVICE_TABLE(i2c, max6621_id); | ||
572 | |||
573 | static const struct of_device_id max6621_of_match[] = { | ||
574 | { .compatible = "maxim,max6621" }, | ||
575 | { } | ||
576 | }; | ||
577 | MODULE_DEVICE_TABLE(of, max6621_of_match); | ||
578 | |||
579 | static struct i2c_driver max6621_driver = { | ||
580 | .class = I2C_CLASS_HWMON, | ||
581 | .driver = { | ||
582 | .name = MAX6621_DRV_NAME, | ||
583 | .of_match_table = of_match_ptr(max6621_of_match), | ||
584 | }, | ||
585 | .probe = max6621_probe, | ||
586 | .id_table = max6621_id, | ||
587 | }; | ||
588 | |||
589 | module_i2c_driver(max6621_driver); | ||
590 | |||
591 | MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); | ||
592 | MODULE_DESCRIPTION("Driver for Maxim MAX6621"); | ||
593 | MODULE_LICENSE("GPL"); | ||