1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * nct6683 - Driver for the hardware monitoring functionality of |
4 | * Nuvoton NCT6683D/NCT6686D/NCT6687D eSIO |
5 | * |
6 | * Copyright (C) 2013 Guenter Roeck <linux@roeck-us.net> |
7 | * |
8 | * Derived from nct6775 driver |
9 | * Copyright (C) 2012, 2013 Guenter Roeck <linux@roeck-us.net> |
10 | * |
11 | * Supports the following chips: |
12 | * |
13 | * Chip #vin #fan #pwm #temp chip ID |
14 | * nct6683d 21(1) 16 8 32(1) 0xc730 |
15 | * nct6686d 21(1) 16 8 32(1) 0xd440 |
16 | * nct6687d 21(1) 16 8 32(1) 0xd590 |
17 | * |
18 | * Notes: |
19 | * (1) Total number of vin and temp inputs is 32. |
20 | */ |
21 | |
22 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
23 | |
24 | #include <linux/acpi.h> |
25 | #include <linux/delay.h> |
26 | #include <linux/err.h> |
27 | #include <linux/init.h> |
28 | #include <linux/io.h> |
29 | #include <linux/jiffies.h> |
30 | #include <linux/hwmon.h> |
31 | #include <linux/hwmon-sysfs.h> |
32 | #include <linux/module.h> |
33 | #include <linux/mutex.h> |
34 | #include <linux/platform_device.h> |
35 | #include <linux/slab.h> |
36 | |
37 | enum kinds { nct6683, nct6686, nct6687 }; |
38 | |
39 | static bool force; |
40 | module_param(force, bool, 0); |
41 | MODULE_PARM_DESC(force, "Set to one to enable support for unknown vendors" ); |
42 | |
43 | static const char * const nct6683_device_names[] = { |
44 | "nct6683" , |
45 | "nct6686" , |
46 | "nct6687" , |
47 | }; |
48 | |
49 | static const char * const nct6683_chip_names[] = { |
50 | "NCT6683D" , |
51 | "NCT6686D" , |
52 | "NCT6687D" , |
53 | }; |
54 | |
55 | #define DRVNAME "nct6683" |
56 | |
57 | /* |
58 | * Super-I/O constants and functions |
59 | */ |
60 | |
61 | #define NCT6683_LD_ACPI 0x0a |
62 | #define NCT6683_LD_HWM 0x0b |
63 | #define NCT6683_LD_VID 0x0d |
64 | |
65 | #define SIO_REG_LDSEL 0x07 /* Logical device select */ |
66 | #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ |
67 | #define SIO_REG_ENABLE 0x30 /* Logical device enable */ |
68 | #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ |
69 | |
70 | #define SIO_NCT6681_ID 0xb270 /* for later */ |
71 | #define SIO_NCT6683_ID 0xc730 |
72 | #define SIO_NCT6686_ID 0xd440 |
73 | #define SIO_NCT6687_ID 0xd590 |
74 | #define SIO_ID_MASK 0xFFF0 |
75 | |
76 | static inline void |
77 | superio_outb(int ioreg, int reg, int val) |
78 | { |
79 | outb(value: reg, port: ioreg); |
80 | outb(value: val, port: ioreg + 1); |
81 | } |
82 | |
83 | static inline int |
84 | superio_inb(int ioreg, int reg) |
85 | { |
86 | outb(value: reg, port: ioreg); |
87 | return inb(port: ioreg + 1); |
88 | } |
89 | |
90 | static inline void |
91 | superio_select(int ioreg, int ld) |
92 | { |
93 | outb(SIO_REG_LDSEL, port: ioreg); |
94 | outb(value: ld, port: ioreg + 1); |
95 | } |
96 | |
97 | static inline int |
98 | superio_enter(int ioreg) |
99 | { |
100 | /* |
101 | * Try to reserve <ioreg> and <ioreg + 1> for exclusive access. |
102 | */ |
103 | if (!request_muxed_region(ioreg, 2, DRVNAME)) |
104 | return -EBUSY; |
105 | |
106 | outb(value: 0x87, port: ioreg); |
107 | outb(value: 0x87, port: ioreg); |
108 | |
109 | return 0; |
110 | } |
111 | |
112 | static inline void |
113 | superio_exit(int ioreg) |
114 | { |
115 | outb(value: 0xaa, port: ioreg); |
116 | outb(value: 0x02, port: ioreg); |
117 | outb(value: 0x02, port: ioreg + 1); |
118 | release_region(ioreg, 2); |
119 | } |
120 | |
121 | /* |
122 | * ISA constants |
123 | */ |
124 | |
125 | #define IOREGION_ALIGNMENT (~7) |
126 | #define IOREGION_OFFSET 4 /* Use EC port 1 */ |
127 | #define IOREGION_LENGTH 4 |
128 | |
129 | #define EC_PAGE_REG 0 |
130 | #define EC_INDEX_REG 1 |
131 | #define EC_DATA_REG 2 |
132 | #define EC_EVENT_REG 3 |
133 | |
134 | /* Common and NCT6683 specific data */ |
135 | |
136 | #define NCT6683_NUM_REG_MON 32 |
137 | #define NCT6683_NUM_REG_FAN 16 |
138 | #define NCT6683_NUM_REG_PWM 8 |
139 | |
140 | #define NCT6683_REG_MON(x) (0x100 + (x) * 2) |
141 | #define NCT6683_REG_FAN_RPM(x) (0x140 + (x) * 2) |
142 | #define NCT6683_REG_PWM(x) (0x160 + (x)) |
143 | #define NCT6683_REG_PWM_WRITE(x) (0xa28 + (x)) |
144 | |
145 | #define NCT6683_REG_MON_STS(x) (0x174 + (x)) |
146 | #define NCT6683_REG_IDLE(x) (0x178 + (x)) |
147 | |
148 | #define NCT6683_REG_FAN_STS(x) (0x17c + (x)) |
149 | #define NCT6683_REG_FAN_ERRSTS 0x17e |
150 | #define NCT6683_REG_FAN_INITSTS 0x17f |
151 | |
152 | #define NCT6683_HWM_CFG 0x180 |
153 | |
154 | #define NCT6683_REG_MON_CFG(x) (0x1a0 + (x)) |
155 | #define NCT6683_REG_FANIN_CFG(x) (0x1c0 + (x)) |
156 | #define NCT6683_REG_FANOUT_CFG(x) (0x1d0 + (x)) |
157 | |
158 | #define NCT6683_REG_INTEL_TEMP_MAX(x) (0x901 + (x) * 16) |
159 | #define NCT6683_REG_INTEL_TEMP_CRIT(x) (0x90d + (x) * 16) |
160 | |
161 | #define NCT6683_REG_TEMP_HYST(x) (0x330 + (x)) /* 8 bit */ |
162 | #define NCT6683_REG_TEMP_MAX(x) (0x350 + (x)) /* 8 bit */ |
163 | #define NCT6683_REG_MON_HIGH(x) (0x370 + (x) * 2) /* 8 bit */ |
164 | #define NCT6683_REG_MON_LOW(x) (0x371 + (x) * 2) /* 8 bit */ |
165 | |
166 | #define NCT6683_REG_FAN_MIN(x) (0x3b8 + (x) * 2) /* 16 bit */ |
167 | |
168 | #define NCT6683_REG_FAN_CFG_CTRL 0xa01 |
169 | #define NCT6683_FAN_CFG_REQ 0x80 |
170 | #define NCT6683_FAN_CFG_DONE 0x40 |
171 | |
172 | #define NCT6683_REG_CUSTOMER_ID 0x602 |
173 | #define NCT6683_CUSTOMER_ID_INTEL 0x805 |
174 | #define NCT6683_CUSTOMER_ID_MITAC 0xa0e |
175 | #define NCT6683_CUSTOMER_ID_MSI 0x201 |
176 | #define NCT6683_CUSTOMER_ID_MSI2 0x200 |
177 | #define NCT6683_CUSTOMER_ID_MSI3 0x207 |
178 | #define NCT6683_CUSTOMER_ID_ASROCK 0xe2c |
179 | #define NCT6683_CUSTOMER_ID_ASROCK2 0xe1b |
180 | #define NCT6683_CUSTOMER_ID_ASROCK3 0x1631 |
181 | |
182 | #define NCT6683_REG_BUILD_YEAR 0x604 |
183 | #define NCT6683_REG_BUILD_MONTH 0x605 |
184 | #define NCT6683_REG_BUILD_DAY 0x606 |
185 | #define NCT6683_REG_SERIAL 0x607 |
186 | #define NCT6683_REG_VERSION_HI 0x608 |
187 | #define NCT6683_REG_VERSION_LO 0x609 |
188 | |
189 | #define NCT6683_REG_CR_CASEOPEN 0xe8 |
190 | #define NCT6683_CR_CASEOPEN_MASK (1 << 7) |
191 | |
192 | #define NCT6683_REG_CR_BEEP 0xe0 |
193 | #define NCT6683_CR_BEEP_MASK (1 << 6) |
194 | |
195 | static const char *const nct6683_mon_label[] = { |
196 | NULL, /* disabled */ |
197 | "Local" , |
198 | "Diode 0 (curr)" , |
199 | "Diode 1 (curr)" , |
200 | "Diode 2 (curr)" , |
201 | "Diode 0 (volt)" , |
202 | "Diode 1 (volt)" , |
203 | "Diode 2 (volt)" , |
204 | "Thermistor 14" , |
205 | "Thermistor 15" , |
206 | "Thermistor 16" , |
207 | "Thermistor 0" , |
208 | "Thermistor 1" , |
209 | "Thermistor 2" , |
210 | "Thermistor 3" , |
211 | "Thermistor 4" , |
212 | "Thermistor 5" , /* 0x10 */ |
213 | "Thermistor 6" , |
214 | "Thermistor 7" , |
215 | "Thermistor 8" , |
216 | "Thermistor 9" , |
217 | "Thermistor 10" , |
218 | "Thermistor 11" , |
219 | "Thermistor 12" , |
220 | "Thermistor 13" , |
221 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
222 | "PECI 0.0" , /* 0x20 */ |
223 | "PECI 1.0" , |
224 | "PECI 2.0" , |
225 | "PECI 3.0" , |
226 | "PECI 0.1" , |
227 | "PECI 1.1" , |
228 | "PECI 2.1" , |
229 | "PECI 3.1" , |
230 | "PECI DIMM 0" , |
231 | "PECI DIMM 1" , |
232 | "PECI DIMM 2" , |
233 | "PECI DIMM 3" , |
234 | NULL, NULL, NULL, NULL, |
235 | "PCH CPU" , /* 0x30 */ |
236 | "PCH CHIP" , |
237 | "PCH CHIP CPU MAX" , |
238 | "PCH MCH" , |
239 | "PCH DIMM 0" , |
240 | "PCH DIMM 1" , |
241 | "PCH DIMM 2" , |
242 | "PCH DIMM 3" , |
243 | "SMBus 0" , |
244 | "SMBus 1" , |
245 | "SMBus 2" , |
246 | "SMBus 3" , |
247 | "SMBus 4" , |
248 | "SMBus 5" , |
249 | "DIMM 0" , |
250 | "DIMM 1" , |
251 | "DIMM 2" , /* 0x40 */ |
252 | "DIMM 3" , |
253 | "AMD TSI Addr 90h" , |
254 | "AMD TSI Addr 92h" , |
255 | "AMD TSI Addr 94h" , |
256 | "AMD TSI Addr 96h" , |
257 | "AMD TSI Addr 98h" , |
258 | "AMD TSI Addr 9ah" , |
259 | "AMD TSI Addr 9ch" , |
260 | "AMD TSI Addr 9dh" , |
261 | NULL, NULL, NULL, NULL, NULL, NULL, |
262 | "Virtual 0" , /* 0x50 */ |
263 | "Virtual 1" , |
264 | "Virtual 2" , |
265 | "Virtual 3" , |
266 | "Virtual 4" , |
267 | "Virtual 5" , |
268 | "Virtual 6" , |
269 | "Virtual 7" , |
270 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
271 | "VCC" , /* 0x60 voltage sensors */ |
272 | "VSB" , |
273 | "AVSB" , |
274 | "VTT" , |
275 | "VBAT" , |
276 | "VREF" , |
277 | "VIN0" , |
278 | "VIN1" , |
279 | "VIN2" , |
280 | "VIN3" , |
281 | "VIN4" , |
282 | "VIN5" , |
283 | "VIN6" , |
284 | "VIN7" , |
285 | "VIN8" , |
286 | "VIN9" , |
287 | "VIN10" , |
288 | "VIN11" , |
289 | "VIN12" , |
290 | "VIN13" , |
291 | "VIN14" , |
292 | "VIN15" , |
293 | "VIN16" , |
294 | }; |
295 | |
296 | #define NUM_MON_LABELS ARRAY_SIZE(nct6683_mon_label) |
297 | #define MON_VOLTAGE_START 0x60 |
298 | |
299 | /* ------------------------------------------------------- */ |
300 | |
301 | struct nct6683_data { |
302 | int addr; /* IO base of EC space */ |
303 | int sioreg; /* SIO register */ |
304 | enum kinds kind; |
305 | u16 customer_id; |
306 | |
307 | struct device *hwmon_dev; |
308 | const struct attribute_group *groups[6]; |
309 | |
310 | int temp_num; /* number of temperature attributes */ |
311 | u8 temp_index[NCT6683_NUM_REG_MON]; |
312 | u8 temp_src[NCT6683_NUM_REG_MON]; |
313 | |
314 | u8 in_num; /* number of voltage attributes */ |
315 | u8 in_index[NCT6683_NUM_REG_MON]; |
316 | u8 in_src[NCT6683_NUM_REG_MON]; |
317 | |
318 | struct mutex update_lock; /* used to protect sensor updates */ |
319 | bool valid; /* true if following fields are valid */ |
320 | unsigned long last_updated; /* In jiffies */ |
321 | |
322 | /* Voltage attribute values */ |
323 | u8 in[3][NCT6683_NUM_REG_MON]; /* [0]=in, [1]=in_max, [2]=in_min */ |
324 | |
325 | /* Temperature attribute values */ |
326 | s16 temp_in[NCT6683_NUM_REG_MON]; |
327 | s8 temp[4][NCT6683_NUM_REG_MON];/* [0]=min, [1]=max, [2]=hyst, |
328 | * [3]=crit |
329 | */ |
330 | |
331 | /* Fan attribute values */ |
332 | unsigned int rpm[NCT6683_NUM_REG_FAN]; |
333 | u16 fan_min[NCT6683_NUM_REG_FAN]; |
334 | u8 fanin_cfg[NCT6683_NUM_REG_FAN]; |
335 | u8 fanout_cfg[NCT6683_NUM_REG_FAN]; |
336 | u16 have_fan; /* some fan inputs can be disabled */ |
337 | |
338 | u8 have_pwm; |
339 | u8 pwm[NCT6683_NUM_REG_PWM]; |
340 | |
341 | #ifdef CONFIG_PM |
342 | /* Remember extra register values over suspend/resume */ |
343 | u8 hwm_cfg; |
344 | #endif |
345 | }; |
346 | |
347 | struct nct6683_sio_data { |
348 | int sioreg; |
349 | enum kinds kind; |
350 | }; |
351 | |
352 | struct sensor_device_template { |
353 | struct device_attribute dev_attr; |
354 | union { |
355 | struct { |
356 | u8 nr; |
357 | u8 index; |
358 | } s; |
359 | int index; |
360 | } u; |
361 | bool s2; /* true if both index and nr are used */ |
362 | }; |
363 | |
364 | struct sensor_device_attr_u { |
365 | union { |
366 | struct sensor_device_attribute a1; |
367 | struct sensor_device_attribute_2 a2; |
368 | } u; |
369 | char name[32]; |
370 | }; |
371 | |
372 | #define __TEMPLATE_ATTR(_template, _mode, _show, _store) { \ |
373 | .attr = {.name = _template, .mode = _mode }, \ |
374 | .show = _show, \ |
375 | .store = _store, \ |
376 | } |
377 | |
378 | #define SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, _index) \ |
379 | { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \ |
380 | .u.index = _index, \ |
381 | .s2 = false } |
382 | |
383 | #define SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \ |
384 | _nr, _index) \ |
385 | { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \ |
386 | .u.s.index = _index, \ |
387 | .u.s.nr = _nr, \ |
388 | .s2 = true } |
389 | |
390 | #define SENSOR_TEMPLATE(_name, _template, _mode, _show, _store, _index) \ |
391 | static struct sensor_device_template sensor_dev_template_##_name \ |
392 | = SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, \ |
393 | _index) |
394 | |
395 | #define SENSOR_TEMPLATE_2(_name, _template, _mode, _show, _store, \ |
396 | _nr, _index) \ |
397 | static struct sensor_device_template sensor_dev_template_##_name \ |
398 | = SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \ |
399 | _nr, _index) |
400 | |
401 | struct sensor_template_group { |
402 | struct sensor_device_template **templates; |
403 | umode_t (*is_visible)(struct kobject *, struct attribute *, int); |
404 | int base; |
405 | }; |
406 | |
407 | static struct attribute_group * |
408 | nct6683_create_attr_group(struct device *dev, |
409 | const struct sensor_template_group *tg, |
410 | int repeat) |
411 | { |
412 | struct sensor_device_attribute_2 *a2; |
413 | struct sensor_device_attribute *a; |
414 | struct sensor_device_template **t; |
415 | struct sensor_device_attr_u *su; |
416 | struct attribute_group *group; |
417 | struct attribute **attrs; |
418 | int i, count; |
419 | |
420 | if (repeat <= 0) |
421 | return ERR_PTR(error: -EINVAL); |
422 | |
423 | t = tg->templates; |
424 | for (count = 0; *t; t++, count++) |
425 | ; |
426 | |
427 | if (count == 0) |
428 | return ERR_PTR(error: -EINVAL); |
429 | |
430 | group = devm_kzalloc(dev, size: sizeof(*group), GFP_KERNEL); |
431 | if (group == NULL) |
432 | return ERR_PTR(error: -ENOMEM); |
433 | |
434 | attrs = devm_kcalloc(dev, n: repeat * count + 1, size: sizeof(*attrs), |
435 | GFP_KERNEL); |
436 | if (attrs == NULL) |
437 | return ERR_PTR(error: -ENOMEM); |
438 | |
439 | su = devm_kzalloc(dev, array3_size(repeat, count, sizeof(*su)), |
440 | GFP_KERNEL); |
441 | if (su == NULL) |
442 | return ERR_PTR(error: -ENOMEM); |
443 | |
444 | group->attrs = attrs; |
445 | group->is_visible = tg->is_visible; |
446 | |
447 | for (i = 0; i < repeat; i++) { |
448 | t = tg->templates; |
449 | while (*t) { |
450 | snprintf(buf: su->name, size: sizeof(su->name), |
451 | fmt: (*t)->dev_attr.attr.name, tg->base + i); |
452 | if ((*t)->s2) { |
453 | a2 = &su->u.a2; |
454 | sysfs_attr_init(&a2->dev_attr.attr); |
455 | a2->dev_attr.attr.name = su->name; |
456 | a2->nr = (*t)->u.s.nr + i; |
457 | a2->index = (*t)->u.s.index; |
458 | a2->dev_attr.attr.mode = |
459 | (*t)->dev_attr.attr.mode; |
460 | a2->dev_attr.show = (*t)->dev_attr.show; |
461 | a2->dev_attr.store = (*t)->dev_attr.store; |
462 | *attrs = &a2->dev_attr.attr; |
463 | } else { |
464 | a = &su->u.a1; |
465 | sysfs_attr_init(&a->dev_attr.attr); |
466 | a->dev_attr.attr.name = su->name; |
467 | a->index = (*t)->u.index + i; |
468 | a->dev_attr.attr.mode = |
469 | (*t)->dev_attr.attr.mode; |
470 | a->dev_attr.show = (*t)->dev_attr.show; |
471 | a->dev_attr.store = (*t)->dev_attr.store; |
472 | *attrs = &a->dev_attr.attr; |
473 | } |
474 | attrs++; |
475 | su++; |
476 | t++; |
477 | } |
478 | } |
479 | |
480 | return group; |
481 | } |
482 | |
483 | /* LSB is 16 mV, except for the following sources, where it is 32 mV */ |
484 | #define MON_SRC_VCC 0x60 |
485 | #define MON_SRC_VSB 0x61 |
486 | #define MON_SRC_AVSB 0x62 |
487 | #define MON_SRC_VBAT 0x64 |
488 | |
489 | static inline long in_from_reg(u16 reg, u8 src) |
490 | { |
491 | int scale = 16; |
492 | |
493 | if (src == MON_SRC_VCC || src == MON_SRC_VSB || src == MON_SRC_AVSB || |
494 | src == MON_SRC_VBAT) |
495 | scale <<= 1; |
496 | return reg * scale; |
497 | } |
498 | |
499 | static u16 nct6683_read(struct nct6683_data *data, u16 reg) |
500 | { |
501 | int res; |
502 | |
503 | outb_p(value: 0xff, port: data->addr + EC_PAGE_REG); /* unlock */ |
504 | outb_p(value: reg >> 8, port: data->addr + EC_PAGE_REG); |
505 | outb_p(value: reg & 0xff, port: data->addr + EC_INDEX_REG); |
506 | res = inb_p(port: data->addr + EC_DATA_REG); |
507 | return res; |
508 | } |
509 | |
510 | static u16 nct6683_read16(struct nct6683_data *data, u16 reg) |
511 | { |
512 | return (nct6683_read(data, reg) << 8) | nct6683_read(data, reg: reg + 1); |
513 | } |
514 | |
515 | static void nct6683_write(struct nct6683_data *data, u16 reg, u16 value) |
516 | { |
517 | outb_p(value: 0xff, port: data->addr + EC_PAGE_REG); /* unlock */ |
518 | outb_p(value: reg >> 8, port: data->addr + EC_PAGE_REG); |
519 | outb_p(value: reg & 0xff, port: data->addr + EC_INDEX_REG); |
520 | outb_p(value: value & 0xff, port: data->addr + EC_DATA_REG); |
521 | } |
522 | |
523 | static int get_in_reg(struct nct6683_data *data, int nr, int index) |
524 | { |
525 | int ch = data->in_index[index]; |
526 | int reg = -EINVAL; |
527 | |
528 | switch (nr) { |
529 | case 0: |
530 | reg = NCT6683_REG_MON(ch); |
531 | break; |
532 | case 1: |
533 | if (data->customer_id != NCT6683_CUSTOMER_ID_INTEL) |
534 | reg = NCT6683_REG_MON_LOW(ch); |
535 | break; |
536 | case 2: |
537 | if (data->customer_id != NCT6683_CUSTOMER_ID_INTEL) |
538 | reg = NCT6683_REG_MON_HIGH(ch); |
539 | break; |
540 | default: |
541 | break; |
542 | } |
543 | return reg; |
544 | } |
545 | |
546 | static int get_temp_reg(struct nct6683_data *data, int nr, int index) |
547 | { |
548 | int ch = data->temp_index[index]; |
549 | int reg = -EINVAL; |
550 | |
551 | switch (data->customer_id) { |
552 | case NCT6683_CUSTOMER_ID_INTEL: |
553 | switch (nr) { |
554 | default: |
555 | case 1: /* max */ |
556 | reg = NCT6683_REG_INTEL_TEMP_MAX(ch); |
557 | break; |
558 | case 3: /* crit */ |
559 | reg = NCT6683_REG_INTEL_TEMP_CRIT(ch); |
560 | break; |
561 | } |
562 | break; |
563 | case NCT6683_CUSTOMER_ID_MITAC: |
564 | default: |
565 | switch (nr) { |
566 | default: |
567 | case 0: /* min */ |
568 | reg = NCT6683_REG_MON_LOW(ch); |
569 | break; |
570 | case 1: /* max */ |
571 | reg = NCT6683_REG_TEMP_MAX(ch); |
572 | break; |
573 | case 2: /* hyst */ |
574 | reg = NCT6683_REG_TEMP_HYST(ch); |
575 | break; |
576 | case 3: /* crit */ |
577 | reg = NCT6683_REG_MON_HIGH(ch); |
578 | break; |
579 | } |
580 | break; |
581 | } |
582 | return reg; |
583 | } |
584 | |
585 | static void nct6683_update_pwm(struct device *dev) |
586 | { |
587 | struct nct6683_data *data = dev_get_drvdata(dev); |
588 | int i; |
589 | |
590 | for (i = 0; i < NCT6683_NUM_REG_PWM; i++) { |
591 | if (!(data->have_pwm & (1 << i))) |
592 | continue; |
593 | data->pwm[i] = nct6683_read(data, NCT6683_REG_PWM(i)); |
594 | } |
595 | } |
596 | |
597 | static struct nct6683_data *nct6683_update_device(struct device *dev) |
598 | { |
599 | struct nct6683_data *data = dev_get_drvdata(dev); |
600 | int i, j; |
601 | |
602 | mutex_lock(&data->update_lock); |
603 | |
604 | if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { |
605 | /* Measured voltages and limits */ |
606 | for (i = 0; i < data->in_num; i++) { |
607 | for (j = 0; j < 3; j++) { |
608 | int reg = get_in_reg(data, nr: j, index: i); |
609 | |
610 | if (reg >= 0) |
611 | data->in[j][i] = |
612 | nct6683_read(data, reg); |
613 | } |
614 | } |
615 | |
616 | /* Measured temperatures and limits */ |
617 | for (i = 0; i < data->temp_num; i++) { |
618 | u8 ch = data->temp_index[i]; |
619 | |
620 | data->temp_in[i] = nct6683_read16(data, |
621 | NCT6683_REG_MON(ch)); |
622 | for (j = 0; j < 4; j++) { |
623 | int reg = get_temp_reg(data, nr: j, index: i); |
624 | |
625 | if (reg >= 0) |
626 | data->temp[j][i] = |
627 | nct6683_read(data, reg); |
628 | } |
629 | } |
630 | |
631 | /* Measured fan speeds and limits */ |
632 | for (i = 0; i < ARRAY_SIZE(data->rpm); i++) { |
633 | if (!(data->have_fan & (1 << i))) |
634 | continue; |
635 | |
636 | data->rpm[i] = nct6683_read16(data, |
637 | NCT6683_REG_FAN_RPM(i)); |
638 | data->fan_min[i] = nct6683_read16(data, |
639 | NCT6683_REG_FAN_MIN(i)); |
640 | } |
641 | |
642 | nct6683_update_pwm(dev); |
643 | |
644 | data->last_updated = jiffies; |
645 | data->valid = true; |
646 | } |
647 | |
648 | mutex_unlock(lock: &data->update_lock); |
649 | return data; |
650 | } |
651 | |
652 | /* |
653 | * Sysfs callback functions |
654 | */ |
655 | static ssize_t |
656 | show_in_label(struct device *dev, struct device_attribute *attr, char *buf) |
657 | { |
658 | struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); |
659 | struct nct6683_data *data = nct6683_update_device(dev); |
660 | int nr = sattr->index; |
661 | |
662 | return sprintf(buf, fmt: "%s\n" , nct6683_mon_label[data->in_src[nr]]); |
663 | } |
664 | |
665 | static ssize_t |
666 | show_in_reg(struct device *dev, struct device_attribute *attr, char *buf) |
667 | { |
668 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); |
669 | struct nct6683_data *data = nct6683_update_device(dev); |
670 | int index = sattr->index; |
671 | int nr = sattr->nr; |
672 | |
673 | return sprintf(buf, fmt: "%ld\n" , |
674 | in_from_reg(reg: data->in[index][nr], src: data->in_index[index])); |
675 | } |
676 | |
677 | static umode_t nct6683_in_is_visible(struct kobject *kobj, |
678 | struct attribute *attr, int index) |
679 | { |
680 | struct device *dev = kobj_to_dev(kobj); |
681 | struct nct6683_data *data = dev_get_drvdata(dev); |
682 | int nr = index % 4; /* attribute */ |
683 | |
684 | /* |
685 | * Voltage limits exist for Intel boards, |
686 | * but register location and encoding is unknown |
687 | */ |
688 | if ((nr == 2 || nr == 3) && |
689 | data->customer_id == NCT6683_CUSTOMER_ID_INTEL) |
690 | return 0; |
691 | |
692 | return attr->mode; |
693 | } |
694 | |
695 | SENSOR_TEMPLATE(in_label, "in%d_label" , S_IRUGO, show_in_label, NULL, 0); |
696 | SENSOR_TEMPLATE_2(in_input, "in%d_input" , S_IRUGO, show_in_reg, NULL, 0, 0); |
697 | SENSOR_TEMPLATE_2(in_min, "in%d_min" , S_IRUGO, show_in_reg, NULL, 0, 1); |
698 | SENSOR_TEMPLATE_2(in_max, "in%d_max" , S_IRUGO, show_in_reg, NULL, 0, 2); |
699 | |
700 | static struct sensor_device_template *nct6683_attributes_in_template[] = { |
701 | &sensor_dev_template_in_label, |
702 | &sensor_dev_template_in_input, |
703 | &sensor_dev_template_in_min, |
704 | &sensor_dev_template_in_max, |
705 | NULL |
706 | }; |
707 | |
708 | static const struct sensor_template_group nct6683_in_template_group = { |
709 | .templates = nct6683_attributes_in_template, |
710 | .is_visible = nct6683_in_is_visible, |
711 | }; |
712 | |
713 | static ssize_t |
714 | show_fan(struct device *dev, struct device_attribute *attr, char *buf) |
715 | { |
716 | struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); |
717 | struct nct6683_data *data = nct6683_update_device(dev); |
718 | |
719 | return sprintf(buf, fmt: "%d\n" , data->rpm[sattr->index]); |
720 | } |
721 | |
722 | static ssize_t |
723 | show_fan_min(struct device *dev, struct device_attribute *attr, char *buf) |
724 | { |
725 | struct nct6683_data *data = nct6683_update_device(dev); |
726 | struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); |
727 | int nr = sattr->index; |
728 | |
729 | return sprintf(buf, fmt: "%d\n" , data->fan_min[nr]); |
730 | } |
731 | |
732 | static ssize_t |
733 | show_fan_pulses(struct device *dev, struct device_attribute *attr, char *buf) |
734 | { |
735 | struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); |
736 | struct nct6683_data *data = nct6683_update_device(dev); |
737 | |
738 | return sprintf(buf, fmt: "%d\n" , |
739 | ((data->fanin_cfg[sattr->index] >> 5) & 0x03) + 1); |
740 | } |
741 | |
742 | static umode_t nct6683_fan_is_visible(struct kobject *kobj, |
743 | struct attribute *attr, int index) |
744 | { |
745 | struct device *dev = kobj_to_dev(kobj); |
746 | struct nct6683_data *data = dev_get_drvdata(dev); |
747 | int fan = index / 3; /* fan index */ |
748 | int nr = index % 3; /* attribute index */ |
749 | |
750 | if (!(data->have_fan & (1 << fan))) |
751 | return 0; |
752 | |
753 | /* |
754 | * Intel may have minimum fan speed limits, |
755 | * but register location and encoding are unknown. |
756 | */ |
757 | if (nr == 2 && data->customer_id == NCT6683_CUSTOMER_ID_INTEL) |
758 | return 0; |
759 | |
760 | return attr->mode; |
761 | } |
762 | |
763 | SENSOR_TEMPLATE(fan_input, "fan%d_input" , S_IRUGO, show_fan, NULL, 0); |
764 | SENSOR_TEMPLATE(fan_pulses, "fan%d_pulses" , S_IRUGO, show_fan_pulses, NULL, 0); |
765 | SENSOR_TEMPLATE(fan_min, "fan%d_min" , S_IRUGO, show_fan_min, NULL, 0); |
766 | |
767 | /* |
768 | * nct6683_fan_is_visible uses the index into the following array |
769 | * to determine if attributes should be created or not. |
770 | * Any change in order or content must be matched. |
771 | */ |
772 | static struct sensor_device_template *nct6683_attributes_fan_template[] = { |
773 | &sensor_dev_template_fan_input, |
774 | &sensor_dev_template_fan_pulses, |
775 | &sensor_dev_template_fan_min, |
776 | NULL |
777 | }; |
778 | |
779 | static const struct sensor_template_group nct6683_fan_template_group = { |
780 | .templates = nct6683_attributes_fan_template, |
781 | .is_visible = nct6683_fan_is_visible, |
782 | .base = 1, |
783 | }; |
784 | |
785 | static ssize_t |
786 | show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) |
787 | { |
788 | struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); |
789 | struct nct6683_data *data = nct6683_update_device(dev); |
790 | int nr = sattr->index; |
791 | |
792 | return sprintf(buf, fmt: "%s\n" , nct6683_mon_label[data->temp_src[nr]]); |
793 | } |
794 | |
795 | static ssize_t |
796 | show_temp8(struct device *dev, struct device_attribute *attr, char *buf) |
797 | { |
798 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); |
799 | struct nct6683_data *data = nct6683_update_device(dev); |
800 | int index = sattr->index; |
801 | int nr = sattr->nr; |
802 | |
803 | return sprintf(buf, fmt: "%d\n" , data->temp[index][nr] * 1000); |
804 | } |
805 | |
806 | static ssize_t |
807 | show_temp_hyst(struct device *dev, struct device_attribute *attr, char *buf) |
808 | { |
809 | struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); |
810 | struct nct6683_data *data = nct6683_update_device(dev); |
811 | int nr = sattr->index; |
812 | int temp = data->temp[1][nr] - data->temp[2][nr]; |
813 | |
814 | return sprintf(buf, fmt: "%d\n" , temp * 1000); |
815 | } |
816 | |
817 | static ssize_t |
818 | show_temp16(struct device *dev, struct device_attribute *attr, char *buf) |
819 | { |
820 | struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); |
821 | struct nct6683_data *data = nct6683_update_device(dev); |
822 | int index = sattr->index; |
823 | |
824 | return sprintf(buf, fmt: "%d\n" , (data->temp_in[index] / 128) * 500); |
825 | } |
826 | |
827 | /* |
828 | * Temperature sensor type is determined by temperature source |
829 | * and can not be modified. |
830 | * 0x02..0x07: Thermal diode |
831 | * 0x08..0x18: Thermistor |
832 | * 0x20..0x2b: Intel PECI |
833 | * 0x42..0x49: AMD TSI |
834 | * Others are unspecified (not visible) |
835 | */ |
836 | |
837 | static int get_temp_type(u8 src) |
838 | { |
839 | if (src >= 0x02 && src <= 0x07) |
840 | return 3; /* thermal diode */ |
841 | else if (src >= 0x08 && src <= 0x18) |
842 | return 4; /* thermistor */ |
843 | else if (src >= 0x20 && src <= 0x2b) |
844 | return 6; /* PECI */ |
845 | else if (src >= 0x42 && src <= 0x49) |
846 | return 5; |
847 | |
848 | return 0; |
849 | } |
850 | |
851 | static ssize_t |
852 | show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) |
853 | { |
854 | struct nct6683_data *data = nct6683_update_device(dev); |
855 | struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); |
856 | int nr = sattr->index; |
857 | return sprintf(buf, fmt: "%d\n" , get_temp_type(src: data->temp_src[nr])); |
858 | } |
859 | |
860 | static umode_t nct6683_temp_is_visible(struct kobject *kobj, |
861 | struct attribute *attr, int index) |
862 | { |
863 | struct device *dev = kobj_to_dev(kobj); |
864 | struct nct6683_data *data = dev_get_drvdata(dev); |
865 | int temp = index / 7; /* temp index */ |
866 | int nr = index % 7; /* attribute index */ |
867 | |
868 | /* |
869 | * Intel does not have low temperature limits or temperature hysteresis |
870 | * registers, or at least register location and encoding is unknown. |
871 | */ |
872 | if ((nr == 2 || nr == 4) && |
873 | data->customer_id == NCT6683_CUSTOMER_ID_INTEL) |
874 | return 0; |
875 | |
876 | if (nr == 6 && get_temp_type(src: data->temp_src[temp]) == 0) |
877 | return 0; /* type */ |
878 | |
879 | return attr->mode; |
880 | } |
881 | |
882 | SENSOR_TEMPLATE(temp_input, "temp%d_input" , S_IRUGO, show_temp16, NULL, 0); |
883 | SENSOR_TEMPLATE(temp_label, "temp%d_label" , S_IRUGO, show_temp_label, NULL, 0); |
884 | SENSOR_TEMPLATE_2(temp_min, "temp%d_min" , S_IRUGO, show_temp8, NULL, 0, 0); |
885 | SENSOR_TEMPLATE_2(temp_max, "temp%d_max" , S_IRUGO, show_temp8, NULL, 0, 1); |
886 | SENSOR_TEMPLATE(temp_max_hyst, "temp%d_max_hyst" , S_IRUGO, show_temp_hyst, NULL, |
887 | 0); |
888 | SENSOR_TEMPLATE_2(temp_crit, "temp%d_crit" , S_IRUGO, show_temp8, NULL, 0, 3); |
889 | SENSOR_TEMPLATE(temp_type, "temp%d_type" , S_IRUGO, show_temp_type, NULL, 0); |
890 | |
891 | /* |
892 | * nct6683_temp_is_visible uses the index into the following array |
893 | * to determine if attributes should be created or not. |
894 | * Any change in order or content must be matched. |
895 | */ |
896 | static struct sensor_device_template *nct6683_attributes_temp_template[] = { |
897 | &sensor_dev_template_temp_input, |
898 | &sensor_dev_template_temp_label, |
899 | &sensor_dev_template_temp_min, /* 2 */ |
900 | &sensor_dev_template_temp_max, /* 3 */ |
901 | &sensor_dev_template_temp_max_hyst, /* 4 */ |
902 | &sensor_dev_template_temp_crit, /* 5 */ |
903 | &sensor_dev_template_temp_type, /* 6 */ |
904 | NULL |
905 | }; |
906 | |
907 | static const struct sensor_template_group nct6683_temp_template_group = { |
908 | .templates = nct6683_attributes_temp_template, |
909 | .is_visible = nct6683_temp_is_visible, |
910 | .base = 1, |
911 | }; |
912 | |
913 | static ssize_t |
914 | show_pwm(struct device *dev, struct device_attribute *attr, char *buf) |
915 | { |
916 | struct nct6683_data *data = nct6683_update_device(dev); |
917 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); |
918 | int index = sattr->index; |
919 | |
920 | return sprintf(buf, fmt: "%d\n" , data->pwm[index]); |
921 | } |
922 | |
923 | static ssize_t |
924 | store_pwm(struct device *dev, struct device_attribute *attr, const char *buf, |
925 | size_t count) |
926 | { |
927 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); |
928 | struct nct6683_data *data = dev_get_drvdata(dev); |
929 | int index = sattr->index; |
930 | unsigned long val; |
931 | |
932 | if (kstrtoul(s: buf, base: 10, res: &val) || val > 255) |
933 | return -EINVAL; |
934 | |
935 | mutex_lock(&data->update_lock); |
936 | nct6683_write(data, NCT6683_REG_FAN_CFG_CTRL, NCT6683_FAN_CFG_REQ); |
937 | usleep_range(min: 1000, max: 2000); |
938 | nct6683_write(data, NCT6683_REG_PWM_WRITE(index), value: val); |
939 | nct6683_write(data, NCT6683_REG_FAN_CFG_CTRL, NCT6683_FAN_CFG_DONE); |
940 | mutex_unlock(lock: &data->update_lock); |
941 | |
942 | return count; |
943 | } |
944 | |
945 | SENSOR_TEMPLATE(pwm, "pwm%d" , S_IRUGO, show_pwm, store_pwm, 0); |
946 | |
947 | static umode_t nct6683_pwm_is_visible(struct kobject *kobj, |
948 | struct attribute *attr, int index) |
949 | { |
950 | struct device *dev = kobj_to_dev(kobj); |
951 | struct nct6683_data *data = dev_get_drvdata(dev); |
952 | int pwm = index; /* pwm index */ |
953 | |
954 | if (!(data->have_pwm & (1 << pwm))) |
955 | return 0; |
956 | |
957 | /* Only update pwm values for Mitac boards */ |
958 | if (data->customer_id == NCT6683_CUSTOMER_ID_MITAC) |
959 | return attr->mode | S_IWUSR; |
960 | |
961 | return attr->mode; |
962 | } |
963 | |
964 | static struct sensor_device_template *nct6683_attributes_pwm_template[] = { |
965 | &sensor_dev_template_pwm, |
966 | NULL |
967 | }; |
968 | |
969 | static const struct sensor_template_group nct6683_pwm_template_group = { |
970 | .templates = nct6683_attributes_pwm_template, |
971 | .is_visible = nct6683_pwm_is_visible, |
972 | .base = 1, |
973 | }; |
974 | |
975 | static ssize_t |
976 | beep_enable_show(struct device *dev, struct device_attribute *attr, char *buf) |
977 | { |
978 | struct nct6683_data *data = dev_get_drvdata(dev); |
979 | int ret; |
980 | u8 reg; |
981 | |
982 | mutex_lock(&data->update_lock); |
983 | |
984 | ret = superio_enter(ioreg: data->sioreg); |
985 | if (ret) |
986 | goto error; |
987 | superio_select(ioreg: data->sioreg, NCT6683_LD_HWM); |
988 | reg = superio_inb(ioreg: data->sioreg, NCT6683_REG_CR_BEEP); |
989 | superio_exit(ioreg: data->sioreg); |
990 | |
991 | mutex_unlock(lock: &data->update_lock); |
992 | |
993 | return sprintf(buf, fmt: "%u\n" , !!(reg & NCT6683_CR_BEEP_MASK)); |
994 | |
995 | error: |
996 | mutex_unlock(lock: &data->update_lock); |
997 | return ret; |
998 | } |
999 | |
1000 | static ssize_t |
1001 | beep_enable_store(struct device *dev, struct device_attribute *attr, |
1002 | const char *buf, size_t count) |
1003 | { |
1004 | struct nct6683_data *data = dev_get_drvdata(dev); |
1005 | unsigned long val; |
1006 | u8 reg; |
1007 | int ret; |
1008 | |
1009 | if (kstrtoul(s: buf, base: 10, res: &val) || (val != 0 && val != 1)) |
1010 | return -EINVAL; |
1011 | |
1012 | mutex_lock(&data->update_lock); |
1013 | |
1014 | ret = superio_enter(ioreg: data->sioreg); |
1015 | if (ret) { |
1016 | count = ret; |
1017 | goto error; |
1018 | } |
1019 | |
1020 | superio_select(ioreg: data->sioreg, NCT6683_LD_HWM); |
1021 | reg = superio_inb(ioreg: data->sioreg, NCT6683_REG_CR_BEEP); |
1022 | if (val) |
1023 | reg |= NCT6683_CR_BEEP_MASK; |
1024 | else |
1025 | reg &= ~NCT6683_CR_BEEP_MASK; |
1026 | superio_outb(ioreg: data->sioreg, NCT6683_REG_CR_BEEP, val: reg); |
1027 | superio_exit(ioreg: data->sioreg); |
1028 | error: |
1029 | mutex_unlock(lock: &data->update_lock); |
1030 | return count; |
1031 | } |
1032 | |
1033 | /* Case open detection */ |
1034 | |
1035 | static ssize_t |
1036 | intrusion0_alarm_show(struct device *dev, struct device_attribute *attr, |
1037 | char *buf) |
1038 | { |
1039 | struct nct6683_data *data = dev_get_drvdata(dev); |
1040 | int ret; |
1041 | u8 reg; |
1042 | |
1043 | mutex_lock(&data->update_lock); |
1044 | |
1045 | ret = superio_enter(ioreg: data->sioreg); |
1046 | if (ret) |
1047 | goto error; |
1048 | superio_select(ioreg: data->sioreg, NCT6683_LD_ACPI); |
1049 | reg = superio_inb(ioreg: data->sioreg, NCT6683_REG_CR_CASEOPEN); |
1050 | superio_exit(ioreg: data->sioreg); |
1051 | |
1052 | mutex_unlock(lock: &data->update_lock); |
1053 | |
1054 | return sprintf(buf, fmt: "%u\n" , !(reg & NCT6683_CR_CASEOPEN_MASK)); |
1055 | |
1056 | error: |
1057 | mutex_unlock(lock: &data->update_lock); |
1058 | return ret; |
1059 | } |
1060 | |
1061 | static ssize_t |
1062 | intrusion0_alarm_store(struct device *dev, struct device_attribute *attr, |
1063 | const char *buf, size_t count) |
1064 | { |
1065 | struct nct6683_data *data = dev_get_drvdata(dev); |
1066 | unsigned long val; |
1067 | u8 reg; |
1068 | int ret; |
1069 | |
1070 | if (kstrtoul(s: buf, base: 10, res: &val) || val != 0) |
1071 | return -EINVAL; |
1072 | |
1073 | mutex_lock(&data->update_lock); |
1074 | |
1075 | /* |
1076 | * Use CR registers to clear caseopen status. |
1077 | * Caseopen is activ low, clear by writing 1 into the register. |
1078 | */ |
1079 | |
1080 | ret = superio_enter(ioreg: data->sioreg); |
1081 | if (ret) { |
1082 | count = ret; |
1083 | goto error; |
1084 | } |
1085 | |
1086 | superio_select(ioreg: data->sioreg, NCT6683_LD_ACPI); |
1087 | reg = superio_inb(ioreg: data->sioreg, NCT6683_REG_CR_CASEOPEN); |
1088 | reg |= NCT6683_CR_CASEOPEN_MASK; |
1089 | superio_outb(ioreg: data->sioreg, NCT6683_REG_CR_CASEOPEN, val: reg); |
1090 | reg &= ~NCT6683_CR_CASEOPEN_MASK; |
1091 | superio_outb(ioreg: data->sioreg, NCT6683_REG_CR_CASEOPEN, val: reg); |
1092 | superio_exit(ioreg: data->sioreg); |
1093 | |
1094 | data->valid = false; /* Force cache refresh */ |
1095 | error: |
1096 | mutex_unlock(lock: &data->update_lock); |
1097 | return count; |
1098 | } |
1099 | |
1100 | static DEVICE_ATTR_RW(intrusion0_alarm); |
1101 | static DEVICE_ATTR_RW(beep_enable); |
1102 | |
1103 | static struct attribute *nct6683_attributes_other[] = { |
1104 | &dev_attr_intrusion0_alarm.attr, |
1105 | &dev_attr_beep_enable.attr, |
1106 | NULL |
1107 | }; |
1108 | |
1109 | static const struct attribute_group nct6683_group_other = { |
1110 | .attrs = nct6683_attributes_other, |
1111 | }; |
1112 | |
1113 | /* Get the monitoring functions started */ |
1114 | static inline void nct6683_init_device(struct nct6683_data *data) |
1115 | { |
1116 | u8 tmp; |
1117 | |
1118 | /* Start hardware monitoring if needed */ |
1119 | tmp = nct6683_read(data, NCT6683_HWM_CFG); |
1120 | if (!(tmp & 0x80)) |
1121 | nct6683_write(data, NCT6683_HWM_CFG, value: tmp | 0x80); |
1122 | } |
1123 | |
1124 | /* |
1125 | * There are a total of 24 fan inputs. Each can be configured as input |
1126 | * or as output. A maximum of 16 inputs and 8 outputs is configurable. |
1127 | */ |
1128 | static void |
1129 | nct6683_setup_fans(struct nct6683_data *data) |
1130 | { |
1131 | int i; |
1132 | u8 reg; |
1133 | |
1134 | for (i = 0; i < NCT6683_NUM_REG_FAN; i++) { |
1135 | reg = nct6683_read(data, NCT6683_REG_FANIN_CFG(i)); |
1136 | if (reg & 0x80) |
1137 | data->have_fan |= 1 << i; |
1138 | data->fanin_cfg[i] = reg; |
1139 | } |
1140 | for (i = 0; i < NCT6683_NUM_REG_PWM; i++) { |
1141 | reg = nct6683_read(data, NCT6683_REG_FANOUT_CFG(i)); |
1142 | if (reg & 0x80) |
1143 | data->have_pwm |= 1 << i; |
1144 | data->fanout_cfg[i] = reg; |
1145 | } |
1146 | } |
1147 | |
1148 | /* |
1149 | * Translation from monitoring register to temperature and voltage attributes |
1150 | * ========================================================================== |
1151 | * |
1152 | * There are a total of 32 monitoring registers. Each can be assigned to either |
1153 | * a temperature or voltage monitoring source. |
1154 | * NCT6683_REG_MON_CFG(x) defines assignment for each monitoring source. |
1155 | * |
1156 | * Temperature and voltage attribute mapping is determined by walking through |
1157 | * the NCT6683_REG_MON_CFG registers. If the assigned source is |
1158 | * a temperature, temp_index[n] is set to the monitor register index, and |
1159 | * temp_src[n] is set to the temperature source. If the assigned source is |
1160 | * a voltage, the respective values are stored in in_index[] and in_src[], |
1161 | * respectively. |
1162 | */ |
1163 | |
1164 | static void nct6683_setup_sensors(struct nct6683_data *data) |
1165 | { |
1166 | u8 reg; |
1167 | int i; |
1168 | |
1169 | data->temp_num = 0; |
1170 | data->in_num = 0; |
1171 | for (i = 0; i < NCT6683_NUM_REG_MON; i++) { |
1172 | reg = nct6683_read(data, NCT6683_REG_MON_CFG(i)) & 0x7f; |
1173 | /* Ignore invalid assignments */ |
1174 | if (reg >= NUM_MON_LABELS) |
1175 | continue; |
1176 | /* Skip if disabled or reserved */ |
1177 | if (nct6683_mon_label[reg] == NULL) |
1178 | continue; |
1179 | if (reg < MON_VOLTAGE_START) { |
1180 | data->temp_index[data->temp_num] = i; |
1181 | data->temp_src[data->temp_num] = reg; |
1182 | data->temp_num++; |
1183 | } else { |
1184 | data->in_index[data->in_num] = i; |
1185 | data->in_src[data->in_num] = reg; |
1186 | data->in_num++; |
1187 | } |
1188 | } |
1189 | } |
1190 | |
1191 | static int nct6683_probe(struct platform_device *pdev) |
1192 | { |
1193 | struct device *dev = &pdev->dev; |
1194 | struct nct6683_sio_data *sio_data = dev->platform_data; |
1195 | struct attribute_group *group; |
1196 | struct nct6683_data *data; |
1197 | struct device *hwmon_dev; |
1198 | struct resource *res; |
1199 | int groups = 0; |
1200 | char build[16]; |
1201 | |
1202 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
1203 | if (!devm_request_region(dev, res->start, IOREGION_LENGTH, DRVNAME)) |
1204 | return -EBUSY; |
1205 | |
1206 | data = devm_kzalloc(dev, size: sizeof(struct nct6683_data), GFP_KERNEL); |
1207 | if (!data) |
1208 | return -ENOMEM; |
1209 | |
1210 | data->kind = sio_data->kind; |
1211 | data->sioreg = sio_data->sioreg; |
1212 | data->addr = res->start; |
1213 | mutex_init(&data->update_lock); |
1214 | platform_set_drvdata(pdev, data); |
1215 | |
1216 | data->customer_id = nct6683_read16(data, NCT6683_REG_CUSTOMER_ID); |
1217 | |
1218 | /* By default only instantiate driver if the customer ID is known */ |
1219 | switch (data->customer_id) { |
1220 | case NCT6683_CUSTOMER_ID_INTEL: |
1221 | break; |
1222 | case NCT6683_CUSTOMER_ID_MITAC: |
1223 | break; |
1224 | case NCT6683_CUSTOMER_ID_MSI: |
1225 | break; |
1226 | case NCT6683_CUSTOMER_ID_MSI2: |
1227 | break; |
1228 | case NCT6683_CUSTOMER_ID_MSI3: |
1229 | break; |
1230 | case NCT6683_CUSTOMER_ID_ASROCK: |
1231 | break; |
1232 | case NCT6683_CUSTOMER_ID_ASROCK2: |
1233 | break; |
1234 | case NCT6683_CUSTOMER_ID_ASROCK3: |
1235 | break; |
1236 | default: |
1237 | if (!force) |
1238 | return -ENODEV; |
1239 | } |
1240 | |
1241 | nct6683_init_device(data); |
1242 | nct6683_setup_fans(data); |
1243 | nct6683_setup_sensors(data); |
1244 | |
1245 | /* Register sysfs hooks */ |
1246 | |
1247 | if (data->have_pwm) { |
1248 | group = nct6683_create_attr_group(dev, |
1249 | tg: &nct6683_pwm_template_group, |
1250 | repeat: fls(x: data->have_pwm)); |
1251 | if (IS_ERR(ptr: group)) |
1252 | return PTR_ERR(ptr: group); |
1253 | data->groups[groups++] = group; |
1254 | } |
1255 | |
1256 | if (data->in_num) { |
1257 | group = nct6683_create_attr_group(dev, |
1258 | tg: &nct6683_in_template_group, |
1259 | repeat: data->in_num); |
1260 | if (IS_ERR(ptr: group)) |
1261 | return PTR_ERR(ptr: group); |
1262 | data->groups[groups++] = group; |
1263 | } |
1264 | |
1265 | if (data->have_fan) { |
1266 | group = nct6683_create_attr_group(dev, |
1267 | tg: &nct6683_fan_template_group, |
1268 | repeat: fls(x: data->have_fan)); |
1269 | if (IS_ERR(ptr: group)) |
1270 | return PTR_ERR(ptr: group); |
1271 | data->groups[groups++] = group; |
1272 | } |
1273 | |
1274 | if (data->temp_num) { |
1275 | group = nct6683_create_attr_group(dev, |
1276 | tg: &nct6683_temp_template_group, |
1277 | repeat: data->temp_num); |
1278 | if (IS_ERR(ptr: group)) |
1279 | return PTR_ERR(ptr: group); |
1280 | data->groups[groups++] = group; |
1281 | } |
1282 | data->groups[groups++] = &nct6683_group_other; |
1283 | |
1284 | if (data->customer_id == NCT6683_CUSTOMER_ID_INTEL) |
1285 | scnprintf(buf: build, size: sizeof(build), fmt: "%02x/%02x/%02x" , |
1286 | nct6683_read(data, NCT6683_REG_BUILD_MONTH), |
1287 | nct6683_read(data, NCT6683_REG_BUILD_DAY), |
1288 | nct6683_read(data, NCT6683_REG_BUILD_YEAR)); |
1289 | else |
1290 | scnprintf(buf: build, size: sizeof(build), fmt: "%02d/%02d/%02d" , |
1291 | nct6683_read(data, NCT6683_REG_BUILD_MONTH), |
1292 | nct6683_read(data, NCT6683_REG_BUILD_DAY), |
1293 | nct6683_read(data, NCT6683_REG_BUILD_YEAR)); |
1294 | |
1295 | dev_info(dev, "%s EC firmware version %d.%d build %s\n" , |
1296 | nct6683_chip_names[data->kind], |
1297 | nct6683_read(data, NCT6683_REG_VERSION_HI), |
1298 | nct6683_read(data, NCT6683_REG_VERSION_LO), |
1299 | build); |
1300 | |
1301 | hwmon_dev = devm_hwmon_device_register_with_groups(dev, |
1302 | name: nct6683_device_names[data->kind], drvdata: data, groups: data->groups); |
1303 | return PTR_ERR_OR_ZERO(ptr: hwmon_dev); |
1304 | } |
1305 | |
1306 | #ifdef CONFIG_PM |
1307 | static int nct6683_suspend(struct device *dev) |
1308 | { |
1309 | struct nct6683_data *data = nct6683_update_device(dev); |
1310 | |
1311 | mutex_lock(&data->update_lock); |
1312 | data->hwm_cfg = nct6683_read(data, NCT6683_HWM_CFG); |
1313 | mutex_unlock(lock: &data->update_lock); |
1314 | |
1315 | return 0; |
1316 | } |
1317 | |
1318 | static int nct6683_resume(struct device *dev) |
1319 | { |
1320 | struct nct6683_data *data = dev_get_drvdata(dev); |
1321 | |
1322 | mutex_lock(&data->update_lock); |
1323 | |
1324 | nct6683_write(data, NCT6683_HWM_CFG, value: data->hwm_cfg); |
1325 | |
1326 | /* Force re-reading all values */ |
1327 | data->valid = false; |
1328 | mutex_unlock(lock: &data->update_lock); |
1329 | |
1330 | return 0; |
1331 | } |
1332 | |
1333 | static const struct dev_pm_ops nct6683_dev_pm_ops = { |
1334 | .suspend = nct6683_suspend, |
1335 | .resume = nct6683_resume, |
1336 | .freeze = nct6683_suspend, |
1337 | .restore = nct6683_resume, |
1338 | }; |
1339 | |
1340 | #define NCT6683_DEV_PM_OPS (&nct6683_dev_pm_ops) |
1341 | #else |
1342 | #define NCT6683_DEV_PM_OPS NULL |
1343 | #endif /* CONFIG_PM */ |
1344 | |
1345 | static struct platform_driver nct6683_driver = { |
1346 | .driver = { |
1347 | .name = DRVNAME, |
1348 | .pm = NCT6683_DEV_PM_OPS, |
1349 | }, |
1350 | .probe = nct6683_probe, |
1351 | }; |
1352 | |
1353 | static int __init nct6683_find(int sioaddr, struct nct6683_sio_data *sio_data) |
1354 | { |
1355 | int addr; |
1356 | u16 val; |
1357 | int err; |
1358 | |
1359 | err = superio_enter(ioreg: sioaddr); |
1360 | if (err) |
1361 | return err; |
1362 | |
1363 | val = (superio_inb(ioreg: sioaddr, SIO_REG_DEVID) << 8) |
1364 | | superio_inb(ioreg: sioaddr, SIO_REG_DEVID + 1); |
1365 | |
1366 | switch (val & SIO_ID_MASK) { |
1367 | case SIO_NCT6683_ID: |
1368 | sio_data->kind = nct6683; |
1369 | break; |
1370 | case SIO_NCT6686_ID: |
1371 | sio_data->kind = nct6686; |
1372 | break; |
1373 | case SIO_NCT6687_ID: |
1374 | sio_data->kind = nct6687; |
1375 | break; |
1376 | default: |
1377 | if (val != 0xffff) |
1378 | pr_debug("unsupported chip ID: 0x%04x\n" , val); |
1379 | goto fail; |
1380 | } |
1381 | |
1382 | /* We have a known chip, find the HWM I/O address */ |
1383 | superio_select(ioreg: sioaddr, NCT6683_LD_HWM); |
1384 | val = (superio_inb(ioreg: sioaddr, SIO_REG_ADDR) << 8) |
1385 | | superio_inb(ioreg: sioaddr, SIO_REG_ADDR + 1); |
1386 | addr = val & IOREGION_ALIGNMENT; |
1387 | if (addr == 0) { |
1388 | pr_err("EC base I/O port unconfigured\n" ); |
1389 | goto fail; |
1390 | } |
1391 | |
1392 | /* Activate logical device if needed */ |
1393 | val = superio_inb(ioreg: sioaddr, SIO_REG_ENABLE); |
1394 | if (!(val & 0x01)) { |
1395 | pr_warn("Forcibly enabling EC access. Data may be unusable.\n" ); |
1396 | superio_outb(ioreg: sioaddr, SIO_REG_ENABLE, val: val | 0x01); |
1397 | } |
1398 | |
1399 | superio_exit(ioreg: sioaddr); |
1400 | pr_info("Found %s or compatible chip at %#x:%#x\n" , |
1401 | nct6683_chip_names[sio_data->kind], sioaddr, addr); |
1402 | sio_data->sioreg = sioaddr; |
1403 | |
1404 | return addr; |
1405 | |
1406 | fail: |
1407 | superio_exit(ioreg: sioaddr); |
1408 | return -ENODEV; |
1409 | } |
1410 | |
1411 | /* |
1412 | * when Super-I/O functions move to a separate file, the Super-I/O |
1413 | * bus will manage the lifetime of the device and this module will only keep |
1414 | * track of the nct6683 driver. But since we use platform_device_alloc(), we |
1415 | * must keep track of the device |
1416 | */ |
1417 | static struct platform_device *pdev[2]; |
1418 | |
1419 | static int __init sensors_nct6683_init(void) |
1420 | { |
1421 | struct nct6683_sio_data sio_data; |
1422 | int sioaddr[2] = { 0x2e, 0x4e }; |
1423 | struct resource res; |
1424 | bool found = false; |
1425 | int address; |
1426 | int i, err; |
1427 | |
1428 | err = platform_driver_register(&nct6683_driver); |
1429 | if (err) |
1430 | return err; |
1431 | |
1432 | /* |
1433 | * initialize sio_data->kind and sio_data->sioreg. |
1434 | * |
1435 | * when Super-I/O functions move to a separate file, the Super-I/O |
1436 | * driver will probe 0x2e and 0x4e and auto-detect the presence of a |
1437 | * nct6683 hardware monitor, and call probe() |
1438 | */ |
1439 | for (i = 0; i < ARRAY_SIZE(pdev); i++) { |
1440 | address = nct6683_find(sioaddr: sioaddr[i], sio_data: &sio_data); |
1441 | if (address <= 0) |
1442 | continue; |
1443 | |
1444 | found = true; |
1445 | |
1446 | pdev[i] = platform_device_alloc(DRVNAME, id: address); |
1447 | if (!pdev[i]) { |
1448 | err = -ENOMEM; |
1449 | goto exit_device_unregister; |
1450 | } |
1451 | |
1452 | err = platform_device_add_data(pdev: pdev[i], data: &sio_data, |
1453 | size: sizeof(struct nct6683_sio_data)); |
1454 | if (err) |
1455 | goto exit_device_put; |
1456 | |
1457 | memset(&res, 0, sizeof(res)); |
1458 | res.name = DRVNAME; |
1459 | res.start = address + IOREGION_OFFSET; |
1460 | res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1; |
1461 | res.flags = IORESOURCE_IO; |
1462 | |
1463 | err = acpi_check_resource_conflict(res: &res); |
1464 | if (err) { |
1465 | platform_device_put(pdev: pdev[i]); |
1466 | pdev[i] = NULL; |
1467 | continue; |
1468 | } |
1469 | |
1470 | err = platform_device_add_resources(pdev: pdev[i], res: &res, num: 1); |
1471 | if (err) |
1472 | goto exit_device_put; |
1473 | |
1474 | /* platform_device_add calls probe() */ |
1475 | err = platform_device_add(pdev: pdev[i]); |
1476 | if (err) |
1477 | goto exit_device_put; |
1478 | } |
1479 | if (!found) { |
1480 | err = -ENODEV; |
1481 | goto exit_unregister; |
1482 | } |
1483 | |
1484 | return 0; |
1485 | |
1486 | exit_device_put: |
1487 | platform_device_put(pdev: pdev[i]); |
1488 | exit_device_unregister: |
1489 | while (--i >= 0) { |
1490 | if (pdev[i]) |
1491 | platform_device_unregister(pdev[i]); |
1492 | } |
1493 | exit_unregister: |
1494 | platform_driver_unregister(&nct6683_driver); |
1495 | return err; |
1496 | } |
1497 | |
1498 | static void __exit sensors_nct6683_exit(void) |
1499 | { |
1500 | int i; |
1501 | |
1502 | for (i = 0; i < ARRAY_SIZE(pdev); i++) { |
1503 | if (pdev[i]) |
1504 | platform_device_unregister(pdev[i]); |
1505 | } |
1506 | platform_driver_unregister(&nct6683_driver); |
1507 | } |
1508 | |
1509 | MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>" ); |
1510 | MODULE_DESCRIPTION("NCT6683D driver" ); |
1511 | MODULE_LICENSE("GPL" ); |
1512 | |
1513 | module_init(sensors_nct6683_init); |
1514 | module_exit(sensors_nct6683_exit); |
1515 | |