1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Support for the FTS Systemmonitoring Chip "Teutates" |
4 | * |
5 | * Copyright (C) 2016 Fujitsu Technology Solutions GmbH, |
6 | * Thilo Cestonaro <thilo.cestonaro@ts.fujitsu.com> |
7 | */ |
8 | #include <linux/err.h> |
9 | #include <linux/hwmon.h> |
10 | #include <linux/i2c.h> |
11 | #include <linux/init.h> |
12 | #include <linux/jiffies.h> |
13 | #include <linux/math.h> |
14 | #include <linux/module.h> |
15 | #include <linux/mutex.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/watchdog.h> |
18 | |
19 | #define FTS_DEVICE_ID_REG 0x0000 |
20 | #define FTS_DEVICE_REVISION_REG 0x0001 |
21 | #define FTS_DEVICE_STATUS_REG 0x0004 |
22 | #define FTS_SATELLITE_STATUS_REG 0x0005 |
23 | #define FTS_EVENT_STATUS_REG 0x0006 |
24 | #define FTS_GLOBAL_CONTROL_REG 0x0007 |
25 | |
26 | #define FTS_DEVICE_DETECT_REG_1 0x0C |
27 | #define FTS_DEVICE_DETECT_REG_2 0x0D |
28 | #define FTS_DEVICE_DETECT_REG_3 0x0E |
29 | |
30 | #define FTS_SENSOR_EVENT_REG 0x0010 |
31 | |
32 | #define FTS_FAN_EVENT_REG 0x0014 |
33 | #define FTS_FAN_PRESENT_REG 0x0015 |
34 | |
35 | #define FTS_POWER_ON_TIME_COUNTER_A 0x007A |
36 | #define FTS_POWER_ON_TIME_COUNTER_B 0x007B |
37 | #define FTS_POWER_ON_TIME_COUNTER_C 0x007C |
38 | |
39 | #define FTS_PAGE_SELECT_REG 0x007F |
40 | |
41 | #define FTS_WATCHDOG_TIME_PRESET 0x000B |
42 | #define FTS_WATCHDOG_CONTROL 0x5081 |
43 | |
44 | #define FTS_NO_FAN_SENSORS 0x08 |
45 | #define FTS_NO_TEMP_SENSORS 0x10 |
46 | #define FTS_NO_VOLT_SENSORS 0x04 |
47 | |
48 | #define FTS_FAN_SOURCE_INVALID 0xff |
49 | |
50 | static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; |
51 | |
52 | static const struct i2c_device_id fts_id[] = { |
53 | { "ftsteutates" , 0 }, |
54 | { } |
55 | }; |
56 | MODULE_DEVICE_TABLE(i2c, fts_id); |
57 | |
58 | enum WATCHDOG_RESOLUTION { |
59 | seconds = 1, |
60 | minutes = 60 |
61 | }; |
62 | |
63 | struct fts_data { |
64 | struct i2c_client *client; |
65 | /* update sensor data lock */ |
66 | struct mutex update_lock; |
67 | /* read/write register lock */ |
68 | struct mutex access_lock; |
69 | unsigned long last_updated; /* in jiffies */ |
70 | struct watchdog_device wdd; |
71 | enum WATCHDOG_RESOLUTION resolution; |
72 | bool valid; /* false until following fields are valid */ |
73 | |
74 | u8 volt[FTS_NO_VOLT_SENSORS]; |
75 | |
76 | u8 temp_input[FTS_NO_TEMP_SENSORS]; |
77 | u8 temp_alarm; |
78 | |
79 | u8 fan_present; |
80 | u8 fan_input[FTS_NO_FAN_SENSORS]; /* in rps */ |
81 | u8 fan_source[FTS_NO_FAN_SENSORS]; |
82 | u8 fan_alarm; |
83 | }; |
84 | |
85 | #define FTS_REG_FAN_INPUT(idx) ((idx) + 0x20) |
86 | #define FTS_REG_FAN_SOURCE(idx) ((idx) + 0x30) |
87 | #define FTS_REG_FAN_CONTROL(idx) (((idx) << 16) + 0x4881) |
88 | |
89 | #define FTS_REG_TEMP_INPUT(idx) ((idx) + 0x40) |
90 | #define FTS_REG_TEMP_CONTROL(idx) (((idx) << 16) + 0x0681) |
91 | |
92 | #define FTS_REG_VOLT(idx) ((idx) + 0x18) |
93 | |
94 | /*****************************************************************************/ |
95 | /* I2C Helper functions */ |
96 | /*****************************************************************************/ |
97 | static int fts_read_byte(struct i2c_client *client, unsigned short reg) |
98 | { |
99 | int ret; |
100 | unsigned char page = reg >> 8; |
101 | struct fts_data *data = dev_get_drvdata(dev: &client->dev); |
102 | |
103 | mutex_lock(&data->access_lock); |
104 | |
105 | dev_dbg(&client->dev, "page select - page: 0x%.02x\n" , page); |
106 | ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, value: page); |
107 | if (ret < 0) |
108 | goto error; |
109 | |
110 | reg &= 0xFF; |
111 | ret = i2c_smbus_read_byte_data(client, command: reg); |
112 | dev_dbg(&client->dev, "read - reg: 0x%.02x: val: 0x%.02x\n" , reg, ret); |
113 | |
114 | error: |
115 | mutex_unlock(lock: &data->access_lock); |
116 | return ret; |
117 | } |
118 | |
119 | static int fts_write_byte(struct i2c_client *client, unsigned short reg, |
120 | unsigned char value) |
121 | { |
122 | int ret; |
123 | unsigned char page = reg >> 8; |
124 | struct fts_data *data = dev_get_drvdata(dev: &client->dev); |
125 | |
126 | mutex_lock(&data->access_lock); |
127 | |
128 | dev_dbg(&client->dev, "page select - page: 0x%.02x\n" , page); |
129 | ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, value: page); |
130 | if (ret < 0) |
131 | goto error; |
132 | |
133 | reg &= 0xFF; |
134 | dev_dbg(&client->dev, |
135 | "write - reg: 0x%.02x: val: 0x%.02x\n" , reg, value); |
136 | ret = i2c_smbus_write_byte_data(client, command: reg, value); |
137 | |
138 | error: |
139 | mutex_unlock(lock: &data->access_lock); |
140 | return ret; |
141 | } |
142 | |
143 | /*****************************************************************************/ |
144 | /* Data Updater Helper function */ |
145 | /*****************************************************************************/ |
146 | static int fts_update_device(struct fts_data *data) |
147 | { |
148 | int i; |
149 | int err = 0; |
150 | |
151 | mutex_lock(&data->update_lock); |
152 | if (!time_after(jiffies, data->last_updated + 2 * HZ) && data->valid) |
153 | goto exit; |
154 | |
155 | err = fts_read_byte(client: data->client, FTS_DEVICE_STATUS_REG); |
156 | if (err < 0) |
157 | goto exit; |
158 | |
159 | data->valid = !!(err & 0x02); /* Data not ready yet */ |
160 | if (unlikely(!data->valid)) { |
161 | err = -EAGAIN; |
162 | goto exit; |
163 | } |
164 | |
165 | err = fts_read_byte(client: data->client, FTS_FAN_PRESENT_REG); |
166 | if (err < 0) |
167 | goto exit; |
168 | data->fan_present = err; |
169 | |
170 | err = fts_read_byte(client: data->client, FTS_FAN_EVENT_REG); |
171 | if (err < 0) |
172 | goto exit; |
173 | data->fan_alarm = err; |
174 | |
175 | for (i = 0; i < FTS_NO_FAN_SENSORS; i++) { |
176 | if (data->fan_present & BIT(i)) { |
177 | err = fts_read_byte(client: data->client, FTS_REG_FAN_INPUT(i)); |
178 | if (err < 0) |
179 | goto exit; |
180 | data->fan_input[i] = err; |
181 | |
182 | err = fts_read_byte(client: data->client, |
183 | FTS_REG_FAN_SOURCE(i)); |
184 | if (err < 0) |
185 | goto exit; |
186 | data->fan_source[i] = err; |
187 | } else { |
188 | data->fan_input[i] = 0; |
189 | data->fan_source[i] = FTS_FAN_SOURCE_INVALID; |
190 | } |
191 | } |
192 | |
193 | err = fts_read_byte(client: data->client, FTS_SENSOR_EVENT_REG); |
194 | if (err < 0) |
195 | goto exit; |
196 | data->temp_alarm = err; |
197 | |
198 | for (i = 0; i < FTS_NO_TEMP_SENSORS; i++) { |
199 | err = fts_read_byte(client: data->client, FTS_REG_TEMP_INPUT(i)); |
200 | if (err < 0) |
201 | goto exit; |
202 | data->temp_input[i] = err; |
203 | } |
204 | |
205 | for (i = 0; i < FTS_NO_VOLT_SENSORS; i++) { |
206 | err = fts_read_byte(client: data->client, FTS_REG_VOLT(i)); |
207 | if (err < 0) |
208 | goto exit; |
209 | data->volt[i] = err; |
210 | } |
211 | data->last_updated = jiffies; |
212 | err = 0; |
213 | exit: |
214 | mutex_unlock(lock: &data->update_lock); |
215 | return err; |
216 | } |
217 | |
218 | /*****************************************************************************/ |
219 | /* Watchdog functions */ |
220 | /*****************************************************************************/ |
221 | static int fts_wd_set_resolution(struct fts_data *data, |
222 | enum WATCHDOG_RESOLUTION resolution) |
223 | { |
224 | int ret; |
225 | |
226 | if (data->resolution == resolution) |
227 | return 0; |
228 | |
229 | ret = fts_read_byte(client: data->client, FTS_WATCHDOG_CONTROL); |
230 | if (ret < 0) |
231 | return ret; |
232 | |
233 | if ((resolution == seconds && ret & BIT(1)) || |
234 | (resolution == minutes && (ret & BIT(1)) == 0)) { |
235 | data->resolution = resolution; |
236 | return 0; |
237 | } |
238 | |
239 | if (resolution == seconds) |
240 | ret |= BIT(1); |
241 | else |
242 | ret &= ~BIT(1); |
243 | |
244 | ret = fts_write_byte(client: data->client, FTS_WATCHDOG_CONTROL, value: ret); |
245 | if (ret < 0) |
246 | return ret; |
247 | |
248 | data->resolution = resolution; |
249 | return ret; |
250 | } |
251 | |
252 | static int fts_wd_set_timeout(struct watchdog_device *wdd, unsigned int timeout) |
253 | { |
254 | struct fts_data *data; |
255 | enum WATCHDOG_RESOLUTION resolution = seconds; |
256 | int ret; |
257 | |
258 | data = watchdog_get_drvdata(wdd); |
259 | /* switch watchdog resolution to minutes if timeout does not fit |
260 | * into a byte |
261 | */ |
262 | if (timeout > 0xFF) { |
263 | timeout = DIV_ROUND_UP(timeout, 60) * 60; |
264 | resolution = minutes; |
265 | } |
266 | |
267 | ret = fts_wd_set_resolution(data, resolution); |
268 | if (ret < 0) |
269 | return ret; |
270 | |
271 | wdd->timeout = timeout; |
272 | return 0; |
273 | } |
274 | |
275 | static int fts_wd_start(struct watchdog_device *wdd) |
276 | { |
277 | struct fts_data *data = watchdog_get_drvdata(wdd); |
278 | |
279 | return fts_write_byte(client: data->client, FTS_WATCHDOG_TIME_PRESET, |
280 | value: wdd->timeout / (u8)data->resolution); |
281 | } |
282 | |
283 | static int fts_wd_stop(struct watchdog_device *wdd) |
284 | { |
285 | struct fts_data *data; |
286 | |
287 | data = watchdog_get_drvdata(wdd); |
288 | return fts_write_byte(client: data->client, FTS_WATCHDOG_TIME_PRESET, value: 0); |
289 | } |
290 | |
291 | static const struct watchdog_info fts_wd_info = { |
292 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, |
293 | .identity = "FTS Teutates Hardware Watchdog" , |
294 | }; |
295 | |
296 | static const struct watchdog_ops fts_wd_ops = { |
297 | .owner = THIS_MODULE, |
298 | .start = fts_wd_start, |
299 | .stop = fts_wd_stop, |
300 | .set_timeout = fts_wd_set_timeout, |
301 | }; |
302 | |
303 | static int fts_watchdog_init(struct fts_data *data) |
304 | { |
305 | int timeout, ret; |
306 | |
307 | watchdog_set_drvdata(wdd: &data->wdd, data); |
308 | |
309 | timeout = fts_read_byte(client: data->client, FTS_WATCHDOG_TIME_PRESET); |
310 | if (timeout < 0) |
311 | return timeout; |
312 | |
313 | /* watchdog not running, set timeout to a default of 60 sec. */ |
314 | if (timeout == 0) { |
315 | ret = fts_wd_set_resolution(data, resolution: seconds); |
316 | if (ret < 0) |
317 | return ret; |
318 | data->wdd.timeout = 60; |
319 | } else { |
320 | ret = fts_read_byte(client: data->client, FTS_WATCHDOG_CONTROL); |
321 | if (ret < 0) |
322 | return ret; |
323 | |
324 | data->resolution = ret & BIT(1) ? seconds : minutes; |
325 | data->wdd.timeout = timeout * (u8)data->resolution; |
326 | set_bit(WDOG_HW_RUNNING, addr: &data->wdd.status); |
327 | } |
328 | |
329 | /* Register our watchdog part */ |
330 | data->wdd.info = &fts_wd_info; |
331 | data->wdd.ops = &fts_wd_ops; |
332 | data->wdd.parent = &data->client->dev; |
333 | data->wdd.min_timeout = 1; |
334 | |
335 | /* max timeout 255 minutes. */ |
336 | data->wdd.max_hw_heartbeat_ms = 0xFF * 60 * MSEC_PER_SEC; |
337 | |
338 | return devm_watchdog_register_device(dev: &data->client->dev, &data->wdd); |
339 | } |
340 | |
341 | static umode_t fts_is_visible(const void *devdata, enum hwmon_sensor_types type, u32 attr, |
342 | int channel) |
343 | { |
344 | switch (type) { |
345 | case hwmon_temp: |
346 | switch (attr) { |
347 | case hwmon_temp_input: |
348 | case hwmon_temp_fault: |
349 | return 0444; |
350 | case hwmon_temp_alarm: |
351 | return 0644; |
352 | default: |
353 | break; |
354 | } |
355 | break; |
356 | case hwmon_fan: |
357 | switch (attr) { |
358 | case hwmon_fan_input: |
359 | case hwmon_fan_fault: |
360 | return 0444; |
361 | case hwmon_fan_alarm: |
362 | return 0644; |
363 | default: |
364 | break; |
365 | } |
366 | break; |
367 | case hwmon_pwm: |
368 | case hwmon_in: |
369 | return 0444; |
370 | default: |
371 | break; |
372 | } |
373 | |
374 | return 0; |
375 | } |
376 | |
377 | static int fts_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, |
378 | long *val) |
379 | { |
380 | struct fts_data *data = dev_get_drvdata(dev); |
381 | int ret = fts_update_device(data); |
382 | |
383 | if (ret < 0) |
384 | return ret; |
385 | |
386 | switch (type) { |
387 | case hwmon_temp: |
388 | switch (attr) { |
389 | case hwmon_temp_input: |
390 | *val = (data->temp_input[channel] - 64) * 1000; |
391 | |
392 | return 0; |
393 | case hwmon_temp_alarm: |
394 | *val = !!(data->temp_alarm & BIT(channel)); |
395 | |
396 | return 0; |
397 | case hwmon_temp_fault: |
398 | /* 00h Temperature = Sensor Error */; |
399 | *val = (data->temp_input[channel] == 0); |
400 | |
401 | return 0; |
402 | default: |
403 | break; |
404 | } |
405 | break; |
406 | case hwmon_fan: |
407 | switch (attr) { |
408 | case hwmon_fan_input: |
409 | *val = data->fan_input[channel] * 60; |
410 | |
411 | return 0; |
412 | case hwmon_fan_alarm: |
413 | *val = !!(data->fan_alarm & BIT(channel)); |
414 | |
415 | return 0; |
416 | case hwmon_fan_fault: |
417 | *val = !(data->fan_present & BIT(channel)); |
418 | |
419 | return 0; |
420 | default: |
421 | break; |
422 | } |
423 | break; |
424 | case hwmon_pwm: |
425 | switch (attr) { |
426 | case hwmon_pwm_auto_channels_temp: |
427 | if (data->fan_source[channel] == FTS_FAN_SOURCE_INVALID) |
428 | *val = 0; |
429 | else |
430 | *val = BIT(data->fan_source[channel]); |
431 | |
432 | return 0; |
433 | default: |
434 | break; |
435 | } |
436 | break; |
437 | case hwmon_in: |
438 | switch (attr) { |
439 | case hwmon_in_input: |
440 | *val = DIV_ROUND_CLOSEST(data->volt[channel] * 3300, 255); |
441 | |
442 | return 0; |
443 | default: |
444 | break; |
445 | } |
446 | break; |
447 | default: |
448 | break; |
449 | } |
450 | |
451 | return -EOPNOTSUPP; |
452 | } |
453 | |
454 | static int fts_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, |
455 | long val) |
456 | { |
457 | struct fts_data *data = dev_get_drvdata(dev); |
458 | int ret = fts_update_device(data); |
459 | |
460 | if (ret < 0) |
461 | return ret; |
462 | |
463 | switch (type) { |
464 | case hwmon_temp: |
465 | switch (attr) { |
466 | case hwmon_temp_alarm: |
467 | if (val) |
468 | return -EINVAL; |
469 | |
470 | mutex_lock(&data->update_lock); |
471 | ret = fts_read_byte(client: data->client, FTS_REG_TEMP_CONTROL(channel)); |
472 | if (ret >= 0) |
473 | ret = fts_write_byte(client: data->client, FTS_REG_TEMP_CONTROL(channel), |
474 | value: ret | 0x1); |
475 | if (ret >= 0) |
476 | data->valid = false; |
477 | |
478 | mutex_unlock(lock: &data->update_lock); |
479 | if (ret < 0) |
480 | return ret; |
481 | |
482 | return 0; |
483 | default: |
484 | break; |
485 | } |
486 | break; |
487 | case hwmon_fan: |
488 | switch (attr) { |
489 | case hwmon_fan_alarm: |
490 | if (val) |
491 | return -EINVAL; |
492 | |
493 | mutex_lock(&data->update_lock); |
494 | ret = fts_read_byte(client: data->client, FTS_REG_FAN_CONTROL(channel)); |
495 | if (ret >= 0) |
496 | ret = fts_write_byte(client: data->client, FTS_REG_FAN_CONTROL(channel), |
497 | value: ret | 0x1); |
498 | if (ret >= 0) |
499 | data->valid = false; |
500 | |
501 | mutex_unlock(lock: &data->update_lock); |
502 | if (ret < 0) |
503 | return ret; |
504 | |
505 | return 0; |
506 | default: |
507 | break; |
508 | } |
509 | break; |
510 | default: |
511 | break; |
512 | } |
513 | |
514 | return -EOPNOTSUPP; |
515 | } |
516 | |
517 | static const struct hwmon_ops fts_ops = { |
518 | .is_visible = fts_is_visible, |
519 | .read = fts_read, |
520 | .write = fts_write, |
521 | }; |
522 | |
523 | static const struct hwmon_channel_info * const fts_info[] = { |
524 | HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), |
525 | HWMON_CHANNEL_INFO(temp, |
526 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, |
527 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, |
528 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, |
529 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, |
530 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, |
531 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, |
532 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, |
533 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, |
534 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, |
535 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, |
536 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, |
537 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, |
538 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, |
539 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, |
540 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, |
541 | HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT |
542 | ), |
543 | HWMON_CHANNEL_INFO(fan, |
544 | HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT, |
545 | HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT, |
546 | HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT, |
547 | HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT, |
548 | HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT, |
549 | HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT, |
550 | HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT, |
551 | HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT |
552 | ), |
553 | HWMON_CHANNEL_INFO(pwm, |
554 | HWMON_PWM_AUTO_CHANNELS_TEMP, |
555 | HWMON_PWM_AUTO_CHANNELS_TEMP, |
556 | HWMON_PWM_AUTO_CHANNELS_TEMP, |
557 | HWMON_PWM_AUTO_CHANNELS_TEMP, |
558 | HWMON_PWM_AUTO_CHANNELS_TEMP, |
559 | HWMON_PWM_AUTO_CHANNELS_TEMP, |
560 | HWMON_PWM_AUTO_CHANNELS_TEMP, |
561 | HWMON_PWM_AUTO_CHANNELS_TEMP |
562 | ), |
563 | HWMON_CHANNEL_INFO(in, |
564 | HWMON_I_INPUT, |
565 | HWMON_I_INPUT, |
566 | HWMON_I_INPUT, |
567 | HWMON_I_INPUT |
568 | ), |
569 | NULL |
570 | }; |
571 | |
572 | static const struct hwmon_chip_info fts_chip_info = { |
573 | .ops = &fts_ops, |
574 | .info = fts_info, |
575 | }; |
576 | |
577 | /*****************************************************************************/ |
578 | /* Module initialization / remove functions */ |
579 | /*****************************************************************************/ |
580 | static int fts_detect(struct i2c_client *client, |
581 | struct i2c_board_info *info) |
582 | { |
583 | int val; |
584 | |
585 | /* detection works with revision greater or equal to 0x2b */ |
586 | val = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG); |
587 | if (val < 0x2b) |
588 | return -ENODEV; |
589 | |
590 | /* Device Detect Regs must have 0x17 0x34 and 0x54 */ |
591 | val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_1); |
592 | if (val != 0x17) |
593 | return -ENODEV; |
594 | |
595 | val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_2); |
596 | if (val != 0x34) |
597 | return -ENODEV; |
598 | |
599 | val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_3); |
600 | if (val != 0x54) |
601 | return -ENODEV; |
602 | |
603 | /* |
604 | * 0x10 == Baseboard Management Controller, 0x01 == Teutates |
605 | * Device ID Reg needs to be 0x11 |
606 | */ |
607 | val = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG); |
608 | if (val != 0x11) |
609 | return -ENODEV; |
610 | |
611 | strscpy(info->type, fts_id[0].name, I2C_NAME_SIZE); |
612 | info->flags = 0; |
613 | return 0; |
614 | } |
615 | |
616 | static int fts_probe(struct i2c_client *client) |
617 | { |
618 | u8 revision; |
619 | struct fts_data *data; |
620 | int err; |
621 | s8 deviceid; |
622 | struct device *hwmon_dev; |
623 | |
624 | if (client->addr != 0x73) |
625 | return -ENODEV; |
626 | |
627 | /* Baseboard Management Controller check */ |
628 | deviceid = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG); |
629 | if (deviceid > 0 && (deviceid & 0xF0) == 0x10) { |
630 | switch (deviceid & 0x0F) { |
631 | case 0x01: |
632 | break; |
633 | default: |
634 | dev_dbg(&client->dev, |
635 | "No Baseboard Management Controller\n" ); |
636 | return -ENODEV; |
637 | } |
638 | } else { |
639 | dev_dbg(&client->dev, "No fujitsu board\n" ); |
640 | return -ENODEV; |
641 | } |
642 | |
643 | data = devm_kzalloc(dev: &client->dev, size: sizeof(struct fts_data), |
644 | GFP_KERNEL); |
645 | if (!data) |
646 | return -ENOMEM; |
647 | |
648 | mutex_init(&data->update_lock); |
649 | mutex_init(&data->access_lock); |
650 | data->client = client; |
651 | dev_set_drvdata(dev: &client->dev, data); |
652 | |
653 | err = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG); |
654 | if (err < 0) |
655 | return err; |
656 | revision = err; |
657 | |
658 | hwmon_dev = devm_hwmon_device_register_with_info(dev: &client->dev, name: "ftsteutates" , drvdata: data, |
659 | info: &fts_chip_info, NULL); |
660 | if (IS_ERR(ptr: hwmon_dev)) |
661 | return PTR_ERR(ptr: hwmon_dev); |
662 | |
663 | err = fts_watchdog_init(data); |
664 | if (err) |
665 | return err; |
666 | |
667 | dev_info(&client->dev, "Detected FTS Teutates chip, revision: %d.%d\n" , |
668 | (revision & 0xF0) >> 4, revision & 0x0F); |
669 | return 0; |
670 | } |
671 | |
672 | /*****************************************************************************/ |
673 | /* Module Details */ |
674 | /*****************************************************************************/ |
675 | static struct i2c_driver fts_driver = { |
676 | .class = I2C_CLASS_HWMON, |
677 | .driver = { |
678 | .name = "ftsteutates" , |
679 | }, |
680 | .id_table = fts_id, |
681 | .probe = fts_probe, |
682 | .detect = fts_detect, |
683 | .address_list = normal_i2c, |
684 | }; |
685 | |
686 | module_i2c_driver(fts_driver); |
687 | |
688 | MODULE_AUTHOR("Thilo Cestonaro <thilo.cestonaro@ts.fujitsu.com>" ); |
689 | MODULE_DESCRIPTION("FTS Teutates driver" ); |
690 | MODULE_LICENSE("GPL" ); |
691 | |