1 | // SPDX-License-Identifier: GPL-2.0-or-later |
---|---|
2 | /* |
3 | * nct6775 - Platform driver for the hardware monitoring |
4 | * functionality of Nuvoton NCT677x Super-I/O chips |
5 | * |
6 | * Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net> |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | |
11 | #include <linux/acpi.h> |
12 | #include <linux/dmi.h> |
13 | #include <linux/hwmon-sysfs.h> |
14 | #include <linux/hwmon-vid.h> |
15 | #include <linux/init.h> |
16 | #include <linux/io.h> |
17 | #include <linux/module.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/regmap.h> |
20 | |
21 | #include "nct6775.h" |
22 | |
23 | enum sensor_access { access_direct, access_asuswmi }; |
24 | |
25 | static const char * const nct6775_sio_names[] __initconst = { |
26 | [nct6106] = "NCT6106D", |
27 | [nct6116] = "NCT6116D", |
28 | [nct6775] = "NCT6775F", |
29 | [nct6776] = "NCT6776D/F", |
30 | [nct6779] = "NCT6779D", |
31 | [nct6791] = "NCT6791D", |
32 | [nct6792] = "NCT6792D", |
33 | [nct6793] = "NCT6793D", |
34 | [nct6795] = "NCT6795D", |
35 | [nct6796] = "NCT6796D", |
36 | [nct6797] = "NCT6797D", |
37 | [nct6798] = "NCT6798D", |
38 | [nct6799] = "NCT6796D-S/NCT6799D-R", |
39 | }; |
40 | |
41 | static unsigned short force_id; |
42 | module_param(force_id, ushort, 0); |
43 | MODULE_PARM_DESC(force_id, "Override the detected device ID"); |
44 | |
45 | static unsigned short fan_debounce; |
46 | module_param(fan_debounce, ushort, 0); |
47 | MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); |
48 | |
49 | #define DRVNAME "nct6775" |
50 | |
51 | #define NCT6775_PORT_CHIPID 0x58 |
52 | |
53 | /* |
54 | * ISA constants |
55 | */ |
56 | |
57 | #define IOREGION_ALIGNMENT (~7) |
58 | #define IOREGION_OFFSET 5 |
59 | #define IOREGION_LENGTH 2 |
60 | #define ADDR_REG_OFFSET 0 |
61 | #define DATA_REG_OFFSET 1 |
62 | |
63 | /* |
64 | * Super-I/O constants and functions |
65 | */ |
66 | |
67 | #define NCT6775_LD_ACPI 0x0a |
68 | #define NCT6775_LD_HWM 0x0b |
69 | #define NCT6775_LD_VID 0x0d |
70 | #define NCT6775_LD_12 0x12 |
71 | |
72 | #define SIO_REG_LDSEL 0x07 /* Logical device select */ |
73 | #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ |
74 | #define SIO_REG_ENABLE 0x30 /* Logical device enable */ |
75 | #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ |
76 | |
77 | #define SIO_NCT6106_ID 0xc450 |
78 | #define SIO_NCT6116_ID 0xd280 |
79 | #define SIO_NCT6775_ID 0xb470 |
80 | #define SIO_NCT6776_ID 0xc330 |
81 | #define SIO_NCT6779_ID 0xc560 |
82 | #define SIO_NCT6791_ID 0xc800 |
83 | #define SIO_NCT6792_ID 0xc910 |
84 | #define SIO_NCT6793_ID 0xd120 |
85 | #define SIO_NCT6795_ID 0xd350 |
86 | #define SIO_NCT6796_ID 0xd420 |
87 | #define SIO_NCT6797_ID 0xd450 |
88 | #define SIO_NCT6798_ID 0xd428 |
89 | #define SIO_NCT6799_ID 0xd800 |
90 | #define SIO_ID_MASK 0xFFF8 |
91 | |
92 | /* |
93 | * Control registers |
94 | */ |
95 | #define NCT6775_REG_CR_FAN_DEBOUNCE 0xf0 |
96 | |
97 | struct nct6775_sio_data { |
98 | int sioreg; |
99 | int ld; |
100 | enum kinds kind; |
101 | enum sensor_access access; |
102 | |
103 | /* superio_() callbacks */ |
104 | void (*sio_outb)(struct nct6775_sio_data *sio_data, int reg, int val); |
105 | int (*sio_inb)(struct nct6775_sio_data *sio_data, int reg); |
106 | void (*sio_select)(struct nct6775_sio_data *sio_data, int ld); |
107 | int (*sio_enter)(struct nct6775_sio_data *sio_data); |
108 | void (*sio_exit)(struct nct6775_sio_data *sio_data); |
109 | }; |
110 | |
111 | #define ASUSWMI_METHOD "WMBD" |
112 | #define ASUSWMI_METHODID_RSIO 0x5253494F |
113 | #define ASUSWMI_METHODID_WSIO 0x5753494F |
114 | #define ASUSWMI_METHODID_RHWM 0x5248574D |
115 | #define ASUSWMI_METHODID_WHWM 0x5748574D |
116 | #define ASUSWMI_UNSUPPORTED_METHOD 0xFFFFFFFE |
117 | #define ASUSWMI_DEVICE_HID "PNP0C14" |
118 | #define ASUSWMI_DEVICE_UID "ASUSWMI" |
119 | #define ASUSMSI_DEVICE_UID "AsusMbSwInterface" |
120 | |
121 | #if IS_ENABLED(CONFIG_ACPI) |
122 | /* |
123 | * ASUS boards have only one device with WMI "WMBD" method and have provided |
124 | * access to only one SuperIO chip at 0x0290. |
125 | */ |
126 | static struct acpi_device *asus_acpi_dev; |
127 | #endif |
128 | |
129 | static int nct6775_asuswmi_evaluate_method(u32 method_id, u8 bank, u8 reg, u8 val, u32 *retval) |
130 | { |
131 | #if IS_ENABLED(CONFIG_ACPI) |
132 | acpi_handle handle = acpi_device_handle(adev: asus_acpi_dev); |
133 | u32 args = bank | (reg << 8) | (val << 16); |
134 | struct acpi_object_list input; |
135 | union acpi_object params[3]; |
136 | unsigned long long result; |
137 | acpi_status status; |
138 | |
139 | params[0].type = ACPI_TYPE_INTEGER; |
140 | params[0].integer.value = 0; |
141 | params[1].type = ACPI_TYPE_INTEGER; |
142 | params[1].integer.value = method_id; |
143 | params[2].type = ACPI_TYPE_BUFFER; |
144 | params[2].buffer.length = sizeof(args); |
145 | params[2].buffer.pointer = (void *)&args; |
146 | input.count = 3; |
147 | input.pointer = params; |
148 | |
149 | status = acpi_evaluate_integer(handle, ASUSWMI_METHOD, arguments: &input, data: &result); |
150 | if (ACPI_FAILURE(status)) |
151 | return -EIO; |
152 | |
153 | if (retval) |
154 | *retval = result; |
155 | |
156 | return 0; |
157 | #else |
158 | return -EOPNOTSUPP; |
159 | #endif |
160 | } |
161 | |
162 | static inline int nct6775_asuswmi_write(u8 bank, u8 reg, u8 val) |
163 | { |
164 | return nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_WHWM, bank, |
165 | reg, val, NULL); |
166 | } |
167 | |
168 | static inline int nct6775_asuswmi_read(u8 bank, u8 reg, u8 *val) |
169 | { |
170 | u32 ret, tmp = 0; |
171 | |
172 | ret = nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_RHWM, bank, |
173 | reg, val: 0, retval: &tmp); |
174 | *val = tmp; |
175 | return ret; |
176 | } |
177 | |
178 | static int superio_wmi_inb(struct nct6775_sio_data *sio_data, int reg) |
179 | { |
180 | int tmp = 0; |
181 | |
182 | nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_RSIO, bank: sio_data->ld, |
183 | reg, val: 0, retval: &tmp); |
184 | return tmp; |
185 | } |
186 | |
187 | static void superio_wmi_outb(struct nct6775_sio_data *sio_data, int reg, int val) |
188 | { |
189 | nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_WSIO, bank: sio_data->ld, |
190 | reg, val, NULL); |
191 | } |
192 | |
193 | static void superio_wmi_select(struct nct6775_sio_data *sio_data, int ld) |
194 | { |
195 | sio_data->ld = ld; |
196 | } |
197 | |
198 | static int superio_wmi_enter(struct nct6775_sio_data *sio_data) |
199 | { |
200 | return 0; |
201 | } |
202 | |
203 | static void superio_wmi_exit(struct nct6775_sio_data *sio_data) |
204 | { |
205 | } |
206 | |
207 | static void superio_outb(struct nct6775_sio_data *sio_data, int reg, int val) |
208 | { |
209 | int ioreg = sio_data->sioreg; |
210 | |
211 | outb(value: reg, port: ioreg); |
212 | outb(value: val, port: ioreg + 1); |
213 | } |
214 | |
215 | static int superio_inb(struct nct6775_sio_data *sio_data, int reg) |
216 | { |
217 | int ioreg = sio_data->sioreg; |
218 | |
219 | outb(value: reg, port: ioreg); |
220 | return inb(port: ioreg + 1); |
221 | } |
222 | |
223 | static void superio_select(struct nct6775_sio_data *sio_data, int ld) |
224 | { |
225 | int ioreg = sio_data->sioreg; |
226 | |
227 | outb(SIO_REG_LDSEL, port: ioreg); |
228 | outb(value: ld, port: ioreg + 1); |
229 | } |
230 | |
231 | static int superio_enter(struct nct6775_sio_data *sio_data) |
232 | { |
233 | int ioreg = sio_data->sioreg; |
234 | |
235 | /* |
236 | * Try to reserve <ioreg> and <ioreg + 1> for exclusive access. |
237 | */ |
238 | if (!request_muxed_region(ioreg, 2, DRVNAME)) |
239 | return -EBUSY; |
240 | |
241 | outb(value: 0x87, port: ioreg); |
242 | outb(value: 0x87, port: ioreg); |
243 | |
244 | return 0; |
245 | } |
246 | |
247 | static void superio_exit(struct nct6775_sio_data *sio_data) |
248 | { |
249 | int ioreg = sio_data->sioreg; |
250 | |
251 | outb(value: 0xaa, port: ioreg); |
252 | outb(value: 0x02, port: ioreg); |
253 | outb(value: 0x02, port: ioreg + 1); |
254 | release_region(ioreg, 2); |
255 | } |
256 | |
257 | static inline void nct6775_wmi_set_bank(struct nct6775_data *data, u16 reg) |
258 | { |
259 | u8 bank = reg >> 8; |
260 | |
261 | data->bank = bank; |
262 | } |
263 | |
264 | static int nct6775_wmi_reg_read(void *ctx, unsigned int reg, unsigned int *val) |
265 | { |
266 | struct nct6775_data *data = ctx; |
267 | int err, word_sized = nct6775_reg_is_word_sized(data, reg); |
268 | u8 tmp = 0; |
269 | u16 res; |
270 | |
271 | nct6775_wmi_set_bank(data, reg); |
272 | |
273 | err = nct6775_asuswmi_read(bank: data->bank, reg: reg & 0xff, val: &tmp); |
274 | if (err) |
275 | return err; |
276 | |
277 | res = tmp; |
278 | if (word_sized) { |
279 | err = nct6775_asuswmi_read(bank: data->bank, reg: (reg & 0xff) + 1, val: &tmp); |
280 | if (err) |
281 | return err; |
282 | |
283 | res = (res << 8) + tmp; |
284 | } |
285 | *val = res; |
286 | return 0; |
287 | } |
288 | |
289 | static int nct6775_wmi_reg_write(void *ctx, unsigned int reg, unsigned int value) |
290 | { |
291 | struct nct6775_data *data = ctx; |
292 | int res, word_sized = nct6775_reg_is_word_sized(data, reg); |
293 | |
294 | nct6775_wmi_set_bank(data, reg); |
295 | |
296 | if (word_sized) { |
297 | res = nct6775_asuswmi_write(bank: data->bank, reg: reg & 0xff, val: value >> 8); |
298 | if (res) |
299 | return res; |
300 | |
301 | res = nct6775_asuswmi_write(bank: data->bank, reg: (reg & 0xff) + 1, val: value); |
302 | } else { |
303 | res = nct6775_asuswmi_write(bank: data->bank, reg: reg & 0xff, val: value); |
304 | } |
305 | |
306 | return res; |
307 | } |
308 | |
309 | /* |
310 | * On older chips, only registers 0x50-0x5f are banked. |
311 | * On more recent chips, all registers are banked. |
312 | * Assume that is the case and set the bank number for each access. |
313 | * Cache the bank number so it only needs to be set if it changes. |
314 | */ |
315 | static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg) |
316 | { |
317 | u8 bank = reg >> 8; |
318 | |
319 | if (data->bank != bank) { |
320 | outb_p(NCT6775_REG_BANK, port: data->addr + ADDR_REG_OFFSET); |
321 | outb_p(value: bank, port: data->addr + DATA_REG_OFFSET); |
322 | data->bank = bank; |
323 | } |
324 | } |
325 | |
326 | static int nct6775_reg_read(void *ctx, unsigned int reg, unsigned int *val) |
327 | { |
328 | struct nct6775_data *data = ctx; |
329 | int word_sized = nct6775_reg_is_word_sized(data, reg); |
330 | |
331 | nct6775_set_bank(data, reg); |
332 | outb_p(value: reg & 0xff, port: data->addr + ADDR_REG_OFFSET); |
333 | *val = inb_p(port: data->addr + DATA_REG_OFFSET); |
334 | if (word_sized) { |
335 | outb_p(value: (reg & 0xff) + 1, |
336 | port: data->addr + ADDR_REG_OFFSET); |
337 | *val = (*val << 8) + inb_p(port: data->addr + DATA_REG_OFFSET); |
338 | } |
339 | return 0; |
340 | } |
341 | |
342 | static int nct6775_reg_write(void *ctx, unsigned int reg, unsigned int value) |
343 | { |
344 | struct nct6775_data *data = ctx; |
345 | int word_sized = nct6775_reg_is_word_sized(data, reg); |
346 | |
347 | nct6775_set_bank(data, reg); |
348 | outb_p(value: reg & 0xff, port: data->addr + ADDR_REG_OFFSET); |
349 | if (word_sized) { |
350 | outb_p(value: value >> 8, port: data->addr + DATA_REG_OFFSET); |
351 | outb_p(value: (reg & 0xff) + 1, |
352 | port: data->addr + ADDR_REG_OFFSET); |
353 | } |
354 | outb_p(value: value & 0xff, port: data->addr + DATA_REG_OFFSET); |
355 | return 0; |
356 | } |
357 | |
358 | static void nct6791_enable_io_mapping(struct nct6775_sio_data *sio_data) |
359 | { |
360 | int val; |
361 | |
362 | val = sio_data->sio_inb(sio_data, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE); |
363 | if (val & 0x10) { |
364 | pr_info("Enabling hardware monitor logical device mappings.\n"); |
365 | sio_data->sio_outb(sio_data, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE, |
366 | val & ~0x10); |
367 | } |
368 | } |
369 | |
370 | static int nct6775_suspend(struct device *dev) |
371 | { |
372 | int err; |
373 | u16 tmp; |
374 | struct nct6775_data *data = nct6775_update_device(dev); |
375 | |
376 | if (IS_ERR(ptr: data)) |
377 | return PTR_ERR(ptr: data); |
378 | |
379 | mutex_lock(&data->update_lock); |
380 | err = nct6775_read_value(data, reg: data->REG_VBAT, value: &tmp); |
381 | if (err) |
382 | goto out; |
383 | data->vbat = tmp; |
384 | if (data->kind == nct6775) { |
385 | err = nct6775_read_value(data, NCT6775_REG_FANDIV1, value: &tmp); |
386 | if (err) |
387 | goto out; |
388 | data->fandiv1 = tmp; |
389 | |
390 | err = nct6775_read_value(data, NCT6775_REG_FANDIV2, value: &tmp); |
391 | if (err) |
392 | goto out; |
393 | data->fandiv2 = tmp; |
394 | } |
395 | out: |
396 | mutex_unlock(lock: &data->update_lock); |
397 | |
398 | return err; |
399 | } |
400 | |
401 | static int nct6775_resume(struct device *dev) |
402 | { |
403 | struct nct6775_data *data = dev_get_drvdata(dev); |
404 | struct nct6775_sio_data *sio_data = dev_get_platdata(dev); |
405 | int i, j, err = 0; |
406 | u8 reg; |
407 | |
408 | mutex_lock(&data->update_lock); |
409 | data->bank = 0xff; /* Force initial bank selection */ |
410 | |
411 | err = sio_data->sio_enter(sio_data); |
412 | if (err) |
413 | goto abort; |
414 | |
415 | sio_data->sio_select(sio_data, NCT6775_LD_HWM); |
416 | reg = sio_data->sio_inb(sio_data, SIO_REG_ENABLE); |
417 | if (reg != data->sio_reg_enable) |
418 | sio_data->sio_outb(sio_data, SIO_REG_ENABLE, data->sio_reg_enable); |
419 | |
420 | if (data->kind == nct6791 || data->kind == nct6792 || |
421 | data->kind == nct6793 || data->kind == nct6795 || |
422 | data->kind == nct6796 || data->kind == nct6797 || |
423 | data->kind == nct6798 || data->kind == nct6799) |
424 | nct6791_enable_io_mapping(sio_data); |
425 | |
426 | sio_data->sio_exit(sio_data); |
427 | |
428 | /* Restore limits */ |
429 | for (i = 0; i < data->in_num; i++) { |
430 | if (!(data->have_in & BIT(i))) |
431 | continue; |
432 | |
433 | err = nct6775_write_value(data, reg: data->REG_IN_MINMAX[0][i], value: data->in[i][1]); |
434 | if (err) |
435 | goto abort; |
436 | err = nct6775_write_value(data, reg: data->REG_IN_MINMAX[1][i], value: data->in[i][2]); |
437 | if (err) |
438 | goto abort; |
439 | } |
440 | |
441 | for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) { |
442 | if (!(data->has_fan_min & BIT(i))) |
443 | continue; |
444 | |
445 | err = nct6775_write_value(data, reg: data->REG_FAN_MIN[i], value: data->fan_min[i]); |
446 | if (err) |
447 | goto abort; |
448 | } |
449 | |
450 | for (i = 0; i < NUM_TEMP; i++) { |
451 | if (!(data->have_temp & BIT(i))) |
452 | continue; |
453 | |
454 | for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++) |
455 | if (data->reg_temp[j][i]) { |
456 | err = nct6775_write_temp(data, reg: data->reg_temp[j][i], |
457 | value: data->temp[j][i]); |
458 | if (err) |
459 | goto abort; |
460 | } |
461 | } |
462 | |
463 | /* Restore other settings */ |
464 | err = nct6775_write_value(data, reg: data->REG_VBAT, value: data->vbat); |
465 | if (err) |
466 | goto abort; |
467 | if (data->kind == nct6775) { |
468 | err = nct6775_write_value(data, NCT6775_REG_FANDIV1, value: data->fandiv1); |
469 | if (err) |
470 | goto abort; |
471 | err = nct6775_write_value(data, NCT6775_REG_FANDIV2, value: data->fandiv2); |
472 | } |
473 | |
474 | abort: |
475 | /* Force re-reading all values */ |
476 | data->valid = false; |
477 | mutex_unlock(lock: &data->update_lock); |
478 | |
479 | return err; |
480 | } |
481 | |
482 | static DEFINE_SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume); |
483 | |
484 | static void |
485 | nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio_data) |
486 | { |
487 | bool fan3pin = false, fan4pin = false, fan4min = false; |
488 | bool fan5pin = false, fan6pin = false, fan7pin = false; |
489 | bool pwm3pin = false, pwm4pin = false, pwm5pin = false; |
490 | bool pwm6pin = false, pwm7pin = false; |
491 | |
492 | /* Store SIO_REG_ENABLE for use during resume */ |
493 | sio_data->sio_select(sio_data, NCT6775_LD_HWM); |
494 | data->sio_reg_enable = sio_data->sio_inb(sio_data, SIO_REG_ENABLE); |
495 | |
496 | /* fan4 and fan5 share some pins with the GPIO and serial flash */ |
497 | if (data->kind == nct6775) { |
498 | int cr2c = sio_data->sio_inb(sio_data, 0x2c); |
499 | |
500 | fan3pin = cr2c & BIT(6); |
501 | pwm3pin = cr2c & BIT(7); |
502 | |
503 | /* On NCT6775, fan4 shares pins with the fdc interface */ |
504 | fan4pin = !(sio_data->sio_inb(sio_data, 0x2A) & 0x80); |
505 | } else if (data->kind == nct6776) { |
506 | bool gpok = sio_data->sio_inb(sio_data, 0x27) & 0x80; |
507 | const char *board_vendor, *board_name; |
508 | |
509 | board_vendor = dmi_get_system_info(field: DMI_BOARD_VENDOR); |
510 | board_name = dmi_get_system_info(field: DMI_BOARD_NAME); |
511 | |
512 | if (board_name && board_vendor && |
513 | !strcmp(board_vendor, "ASRock")) { |
514 | /* |
515 | * Auxiliary fan monitoring is not enabled on ASRock |
516 | * Z77 Pro4-M if booted in UEFI Ultra-FastBoot mode. |
517 | * Observed with BIOS version 2.00. |
518 | */ |
519 | if (!strcmp(board_name, "Z77 Pro4-M")) { |
520 | if ((data->sio_reg_enable & 0xe0) != 0xe0) { |
521 | data->sio_reg_enable |= 0xe0; |
522 | sio_data->sio_outb(sio_data, SIO_REG_ENABLE, |
523 | data->sio_reg_enable); |
524 | } |
525 | } |
526 | } |
527 | |
528 | if (data->sio_reg_enable & 0x80) |
529 | fan3pin = gpok; |
530 | else |
531 | fan3pin = !(sio_data->sio_inb(sio_data, 0x24) & 0x40); |
532 | |
533 | if (data->sio_reg_enable & 0x40) |
534 | fan4pin = gpok; |
535 | else |
536 | fan4pin = sio_data->sio_inb(sio_data, 0x1C) & 0x01; |
537 | |
538 | if (data->sio_reg_enable & 0x20) |
539 | fan5pin = gpok; |
540 | else |
541 | fan5pin = sio_data->sio_inb(sio_data, 0x1C) & 0x02; |
542 | |
543 | fan4min = fan4pin; |
544 | pwm3pin = fan3pin; |
545 | } else if (data->kind == nct6106) { |
546 | int cr24 = sio_data->sio_inb(sio_data, 0x24); |
547 | |
548 | fan3pin = !(cr24 & 0x80); |
549 | pwm3pin = cr24 & 0x08; |
550 | } else if (data->kind == nct6116) { |
551 | int cr1a = sio_data->sio_inb(sio_data, 0x1a); |
552 | int cr1b = sio_data->sio_inb(sio_data, 0x1b); |
553 | int cr24 = sio_data->sio_inb(sio_data, 0x24); |
554 | int cr2a = sio_data->sio_inb(sio_data, 0x2a); |
555 | int cr2b = sio_data->sio_inb(sio_data, 0x2b); |
556 | int cr2f = sio_data->sio_inb(sio_data, 0x2f); |
557 | |
558 | fan3pin = !(cr2b & 0x10); |
559 | fan4pin = (cr2b & 0x80) || // pin 1(2) |
560 | (!(cr2f & 0x10) && (cr1a & 0x04)); // pin 65(66) |
561 | fan5pin = (cr2b & 0x80) || // pin 126(127) |
562 | (!(cr1b & 0x03) && (cr2a & 0x02)); // pin 94(96) |
563 | |
564 | pwm3pin = fan3pin && (cr24 & 0x08); |
565 | pwm4pin = fan4pin; |
566 | pwm5pin = fan5pin; |
567 | } else { |
568 | /* |
569 | * NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D, |
570 | * NCT6797D, NCT6798D, NCT6799D |
571 | */ |
572 | int cr1a = sio_data->sio_inb(sio_data, 0x1a); |
573 | int cr1b = sio_data->sio_inb(sio_data, 0x1b); |
574 | int cr1c = sio_data->sio_inb(sio_data, 0x1c); |
575 | int cr1d = sio_data->sio_inb(sio_data, 0x1d); |
576 | int cr2a = sio_data->sio_inb(sio_data, 0x2a); |
577 | int cr2b = sio_data->sio_inb(sio_data, 0x2b); |
578 | int cr2d = sio_data->sio_inb(sio_data, 0x2d); |
579 | int cr2f = sio_data->sio_inb(sio_data, 0x2f); |
580 | bool vsb_ctl_en = cr2f & BIT(0); |
581 | bool dsw_en = cr2f & BIT(3); |
582 | bool ddr4_en = cr2f & BIT(4); |
583 | bool as_seq1_en = cr2f & BIT(7); |
584 | int cre0; |
585 | int cre6; |
586 | int creb; |
587 | int cred; |
588 | |
589 | cre6 = sio_data->sio_inb(sio_data, 0xe6); |
590 | |
591 | sio_data->sio_select(sio_data, NCT6775_LD_12); |
592 | cre0 = sio_data->sio_inb(sio_data, 0xe0); |
593 | creb = sio_data->sio_inb(sio_data, 0xeb); |
594 | cred = sio_data->sio_inb(sio_data, 0xed); |
595 | |
596 | fan3pin = !(cr1c & BIT(5)); |
597 | fan4pin = !(cr1c & BIT(6)); |
598 | fan5pin = !(cr1c & BIT(7)); |
599 | |
600 | pwm3pin = !(cr1c & BIT(0)); |
601 | pwm4pin = !(cr1c & BIT(1)); |
602 | pwm5pin = !(cr1c & BIT(2)); |
603 | |
604 | switch (data->kind) { |
605 | case nct6791: |
606 | fan6pin = cr2d & BIT(1); |
607 | pwm6pin = cr2d & BIT(0); |
608 | break; |
609 | case nct6792: |
610 | fan6pin = !dsw_en && (cr2d & BIT(1)); |
611 | pwm6pin = !dsw_en && (cr2d & BIT(0)); |
612 | break; |
613 | case nct6793: |
614 | fan5pin |= cr1b & BIT(5); |
615 | fan5pin |= creb & BIT(5); |
616 | |
617 | fan6pin = !dsw_en && (cr2d & BIT(1)); |
618 | fan6pin |= creb & BIT(3); |
619 | |
620 | pwm5pin |= cr2d & BIT(7); |
621 | pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); |
622 | |
623 | pwm6pin = !dsw_en && (cr2d & BIT(0)); |
624 | pwm6pin |= creb & BIT(2); |
625 | break; |
626 | case nct6795: |
627 | fan5pin |= cr1b & BIT(5); |
628 | fan5pin |= creb & BIT(5); |
629 | |
630 | fan6pin = (cr2a & BIT(4)) && |
631 | (!dsw_en || (cred & BIT(4))); |
632 | fan6pin |= creb & BIT(3); |
633 | |
634 | pwm5pin |= cr2d & BIT(7); |
635 | pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); |
636 | |
637 | pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2)); |
638 | pwm6pin |= creb & BIT(2); |
639 | break; |
640 | case nct6796: |
641 | fan5pin |= cr1b & BIT(5); |
642 | fan5pin |= (cre0 & BIT(3)) && !(cr1b & BIT(0)); |
643 | fan5pin |= creb & BIT(5); |
644 | |
645 | fan6pin = (cr2a & BIT(4)) && |
646 | (!dsw_en || (cred & BIT(4))); |
647 | fan6pin |= creb & BIT(3); |
648 | |
649 | fan7pin = !(cr2b & BIT(2)); |
650 | |
651 | pwm5pin |= cr2d & BIT(7); |
652 | pwm5pin |= (cre0 & BIT(4)) && !(cr1b & BIT(0)); |
653 | pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); |
654 | |
655 | pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2)); |
656 | pwm6pin |= creb & BIT(2); |
657 | |
658 | pwm7pin = !(cr1d & (BIT(2) | BIT(3))); |
659 | break; |
660 | case nct6797: |
661 | fan5pin |= !ddr4_en && (cr1b & BIT(5)); |
662 | fan5pin |= creb & BIT(5); |
663 | |
664 | fan6pin = cr2a & BIT(4); |
665 | fan6pin |= creb & BIT(3); |
666 | |
667 | fan7pin = cr1a & BIT(1); |
668 | |
669 | pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); |
670 | pwm5pin |= !ddr4_en && (cr2d & BIT(7)); |
671 | |
672 | pwm6pin = creb & BIT(2); |
673 | pwm6pin |= cred & BIT(2); |
674 | |
675 | pwm7pin = cr1d & BIT(4); |
676 | break; |
677 | case nct6798: |
678 | fan6pin = !(cr1b & BIT(0)) && (cre0 & BIT(3)); |
679 | fan6pin |= cr2a & BIT(4); |
680 | fan6pin |= creb & BIT(5); |
681 | |
682 | fan7pin = cr1b & BIT(5); |
683 | fan7pin |= !(cr2b & BIT(2)); |
684 | fan7pin |= creb & BIT(3); |
685 | |
686 | pwm6pin = !(cr1b & BIT(0)) && (cre0 & BIT(4)); |
687 | pwm6pin |= !(cred & BIT(2)) && (cr2a & BIT(3)); |
688 | pwm6pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); |
689 | |
690 | pwm7pin = !(cr1d & (BIT(2) | BIT(3))); |
691 | pwm7pin |= cr2d & BIT(7); |
692 | pwm7pin |= creb & BIT(2); |
693 | break; |
694 | case nct6799: |
695 | fan4pin = cr1c & BIT(6); |
696 | fan5pin = cr1c & BIT(7); |
697 | |
698 | fan6pin = !(cr1b & BIT(0)) && (cre0 & BIT(3)); |
699 | fan6pin |= cre6 & BIT(5); |
700 | fan6pin |= creb & BIT(5); |
701 | fan6pin |= !as_seq1_en && (cr2a & BIT(4)); |
702 | |
703 | fan7pin = cr1b & BIT(5); |
704 | fan7pin |= !vsb_ctl_en && !(cr2b & BIT(2)); |
705 | fan7pin |= creb & BIT(3); |
706 | |
707 | pwm6pin = !(cr1b & BIT(0)) && (cre0 & BIT(4)); |
708 | pwm6pin |= !as_seq1_en && !(cred & BIT(2)) && (cr2a & BIT(3)); |
709 | pwm6pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); |
710 | pwm6pin |= cre6 & BIT(3); |
711 | |
712 | pwm7pin = !vsb_ctl_en && !(cr1d & (BIT(2) | BIT(3))); |
713 | pwm7pin |= creb & BIT(2); |
714 | pwm7pin |= cr2d & BIT(7); |
715 | |
716 | break; |
717 | default: /* NCT6779D */ |
718 | break; |
719 | } |
720 | |
721 | fan4min = fan4pin; |
722 | } |
723 | |
724 | /* fan 1 and 2 (0x03) are always present */ |
725 | data->has_fan = 0x03 | (fan3pin << 2) | (fan4pin << 3) | |
726 | (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6); |
727 | data->has_fan_min = 0x03 | (fan3pin << 2) | (fan4min << 3) | |
728 | (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6); |
729 | data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) | |
730 | (pwm5pin << 4) | (pwm6pin << 5) | (pwm7pin << 6); |
731 | } |
732 | |
733 | static ssize_t |
734 | cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf) |
735 | { |
736 | struct nct6775_data *data = dev_get_drvdata(dev); |
737 | |
738 | return sprintf(buf, fmt: "%d\n", vid_from_reg(val: data->vid, vrm: data->vrm)); |
739 | } |
740 | |
741 | static DEVICE_ATTR_RO(cpu0_vid); |
742 | |
743 | /* Case open detection */ |
744 | |
745 | static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee }; |
746 | static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 }; |
747 | |
748 | static ssize_t |
749 | clear_caseopen(struct device *dev, struct device_attribute *attr, |
750 | const char *buf, size_t count) |
751 | { |
752 | struct nct6775_data *data = dev_get_drvdata(dev); |
753 | struct nct6775_sio_data *sio_data = data->driver_data; |
754 | int nr = to_sensor_dev_attr(attr)->index - INTRUSION_ALARM_BASE; |
755 | unsigned long val; |
756 | u8 reg; |
757 | int ret; |
758 | |
759 | if (kstrtoul(s: buf, base: 10, res: &val) || val != 0) |
760 | return -EINVAL; |
761 | |
762 | mutex_lock(&data->update_lock); |
763 | |
764 | /* |
765 | * Use CR registers to clear caseopen status. |
766 | * The CR registers are the same for all chips, and not all chips |
767 | * support clearing the caseopen status through "regular" registers. |
768 | */ |
769 | ret = sio_data->sio_enter(sio_data); |
770 | if (ret) { |
771 | count = ret; |
772 | goto error; |
773 | } |
774 | |
775 | sio_data->sio_select(sio_data, NCT6775_LD_ACPI); |
776 | reg = sio_data->sio_inb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr]); |
777 | reg |= NCT6775_CR_CASEOPEN_CLR_MASK[nr]; |
778 | sio_data->sio_outb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); |
779 | reg &= ~NCT6775_CR_CASEOPEN_CLR_MASK[nr]; |
780 | sio_data->sio_outb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); |
781 | sio_data->sio_exit(sio_data); |
782 | |
783 | data->valid = false; /* Force cache refresh */ |
784 | error: |
785 | mutex_unlock(lock: &data->update_lock); |
786 | return count; |
787 | } |
788 | |
789 | static SENSOR_DEVICE_ATTR(intrusion0_alarm, 0644, nct6775_show_alarm, |
790 | clear_caseopen, INTRUSION_ALARM_BASE); |
791 | static SENSOR_DEVICE_ATTR(intrusion1_alarm, 0644, nct6775_show_alarm, |
792 | clear_caseopen, INTRUSION_ALARM_BASE + 1); |
793 | static SENSOR_DEVICE_ATTR(intrusion0_beep, 0644, nct6775_show_beep, |
794 | nct6775_store_beep, INTRUSION_ALARM_BASE); |
795 | static SENSOR_DEVICE_ATTR(intrusion1_beep, 0644, nct6775_show_beep, |
796 | nct6775_store_beep, INTRUSION_ALARM_BASE + 1); |
797 | static SENSOR_DEVICE_ATTR(beep_enable, 0644, nct6775_show_beep, |
798 | nct6775_store_beep, BEEP_ENABLE_BASE); |
799 | |
800 | static umode_t nct6775_other_is_visible(struct kobject *kobj, |
801 | struct attribute *attr, int index) |
802 | { |
803 | struct device *dev = kobj_to_dev(kobj); |
804 | struct nct6775_data *data = dev_get_drvdata(dev); |
805 | |
806 | if (index == 0 && !data->have_vid) |
807 | return 0; |
808 | |
809 | if (index == 1 || index == 2) { |
810 | if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 1] < 0) |
811 | return 0; |
812 | } |
813 | |
814 | if (index == 3 || index == 4) { |
815 | if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 3] < 0) |
816 | return 0; |
817 | } |
818 | |
819 | return nct6775_attr_mode(data, attr); |
820 | } |
821 | |
822 | /* |
823 | * nct6775_other_is_visible uses the index into the following array |
824 | * to determine if attributes should be created or not. |
825 | * Any change in order or content must be matched. |
826 | */ |
827 | static struct attribute *nct6775_attributes_other[] = { |
828 | &dev_attr_cpu0_vid.attr, /* 0 */ |
829 | &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, /* 1 */ |
830 | &sensor_dev_attr_intrusion1_alarm.dev_attr.attr, /* 2 */ |
831 | &sensor_dev_attr_intrusion0_beep.dev_attr.attr, /* 3 */ |
832 | &sensor_dev_attr_intrusion1_beep.dev_attr.attr, /* 4 */ |
833 | &sensor_dev_attr_beep_enable.dev_attr.attr, /* 5 */ |
834 | |
835 | NULL |
836 | }; |
837 | |
838 | static const struct attribute_group nct6775_group_other = { |
839 | .attrs = nct6775_attributes_other, |
840 | .is_visible = nct6775_other_is_visible, |
841 | }; |
842 | |
843 | static int nct6775_platform_probe_init(struct nct6775_data *data) |
844 | { |
845 | int err; |
846 | u8 cr2a; |
847 | struct nct6775_sio_data *sio_data = data->driver_data; |
848 | |
849 | err = sio_data->sio_enter(sio_data); |
850 | if (err) |
851 | return err; |
852 | |
853 | cr2a = sio_data->sio_inb(sio_data, 0x2a); |
854 | switch (data->kind) { |
855 | case nct6775: |
856 | data->have_vid = (cr2a & 0x40); |
857 | break; |
858 | case nct6776: |
859 | data->have_vid = (cr2a & 0x60) == 0x40; |
860 | break; |
861 | case nct6106: |
862 | case nct6116: |
863 | case nct6779: |
864 | case nct6791: |
865 | case nct6792: |
866 | case nct6793: |
867 | case nct6795: |
868 | case nct6796: |
869 | case nct6797: |
870 | case nct6798: |
871 | case nct6799: |
872 | break; |
873 | } |
874 | |
875 | /* |
876 | * Read VID value |
877 | * We can get the VID input values directly at logical device D 0xe3. |
878 | */ |
879 | if (data->have_vid) { |
880 | sio_data->sio_select(sio_data, NCT6775_LD_VID); |
881 | data->vid = sio_data->sio_inb(sio_data, 0xe3); |
882 | data->vrm = vid_which_vrm(); |
883 | } |
884 | |
885 | if (fan_debounce) { |
886 | u8 tmp; |
887 | |
888 | sio_data->sio_select(sio_data, NCT6775_LD_HWM); |
889 | tmp = sio_data->sio_inb(sio_data, |
890 | NCT6775_REG_CR_FAN_DEBOUNCE); |
891 | switch (data->kind) { |
892 | case nct6106: |
893 | case nct6116: |
894 | tmp |= 0xe0; |
895 | break; |
896 | case nct6775: |
897 | tmp |= 0x1e; |
898 | break; |
899 | case nct6776: |
900 | case nct6779: |
901 | tmp |= 0x3e; |
902 | break; |
903 | case nct6791: |
904 | case nct6792: |
905 | case nct6793: |
906 | case nct6795: |
907 | case nct6796: |
908 | case nct6797: |
909 | case nct6798: |
910 | case nct6799: |
911 | tmp |= 0x7e; |
912 | break; |
913 | } |
914 | sio_data->sio_outb(sio_data, NCT6775_REG_CR_FAN_DEBOUNCE, |
915 | tmp); |
916 | pr_info("Enabled fan debounce for chip %s\n", data->name); |
917 | } |
918 | |
919 | nct6775_check_fan_inputs(data, sio_data); |
920 | |
921 | sio_data->sio_exit(sio_data); |
922 | |
923 | return nct6775_add_attr_group(data, group: &nct6775_group_other); |
924 | } |
925 | |
926 | static const struct regmap_config nct6775_regmap_config = { |
927 | .reg_bits = 16, |
928 | .val_bits = 16, |
929 | .reg_read = nct6775_reg_read, |
930 | .reg_write = nct6775_reg_write, |
931 | }; |
932 | |
933 | static const struct regmap_config nct6775_wmi_regmap_config = { |
934 | .reg_bits = 16, |
935 | .val_bits = 16, |
936 | .reg_read = nct6775_wmi_reg_read, |
937 | .reg_write = nct6775_wmi_reg_write, |
938 | }; |
939 | |
940 | static int nct6775_platform_probe(struct platform_device *pdev) |
941 | { |
942 | struct device *dev = &pdev->dev; |
943 | struct nct6775_sio_data *sio_data = dev_get_platdata(dev); |
944 | struct nct6775_data *data; |
945 | struct resource *res; |
946 | const struct regmap_config *regmapcfg; |
947 | |
948 | if (sio_data->access == access_direct) { |
949 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
950 | if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH, DRVNAME)) |
951 | return -EBUSY; |
952 | } |
953 | |
954 | data = devm_kzalloc(dev: &pdev->dev, size: sizeof(*data), GFP_KERNEL); |
955 | if (!data) |
956 | return -ENOMEM; |
957 | |
958 | data->kind = sio_data->kind; |
959 | data->sioreg = sio_data->sioreg; |
960 | |
961 | if (sio_data->access == access_direct) { |
962 | data->addr = res->start; |
963 | regmapcfg = &nct6775_regmap_config; |
964 | } else { |
965 | regmapcfg = &nct6775_wmi_regmap_config; |
966 | } |
967 | |
968 | platform_set_drvdata(pdev, data); |
969 | |
970 | data->driver_data = sio_data; |
971 | data->driver_init = nct6775_platform_probe_init; |
972 | |
973 | return nct6775_probe(dev: &pdev->dev, data, regmapcfg); |
974 | } |
975 | |
976 | static struct platform_driver nct6775_driver = { |
977 | .driver = { |
978 | .name = DRVNAME, |
979 | .pm = pm_sleep_ptr(&nct6775_dev_pm_ops), |
980 | }, |
981 | .probe = nct6775_platform_probe, |
982 | }; |
983 | |
984 | /* nct6775_find() looks for a '627 in the Super-I/O config space */ |
985 | static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) |
986 | { |
987 | u16 val; |
988 | int err; |
989 | int addr; |
990 | |
991 | sio_data->access = access_direct; |
992 | sio_data->sioreg = sioaddr; |
993 | |
994 | err = sio_data->sio_enter(sio_data); |
995 | if (err) |
996 | return err; |
997 | |
998 | val = (sio_data->sio_inb(sio_data, SIO_REG_DEVID) << 8) | |
999 | sio_data->sio_inb(sio_data, SIO_REG_DEVID + 1); |
1000 | if (force_id && val != 0xffff) |
1001 | val = force_id; |
1002 | |
1003 | switch (val & SIO_ID_MASK) { |
1004 | case SIO_NCT6106_ID: |
1005 | sio_data->kind = nct6106; |
1006 | break; |
1007 | case SIO_NCT6116_ID: |
1008 | sio_data->kind = nct6116; |
1009 | break; |
1010 | case SIO_NCT6775_ID: |
1011 | sio_data->kind = nct6775; |
1012 | break; |
1013 | case SIO_NCT6776_ID: |
1014 | sio_data->kind = nct6776; |
1015 | break; |
1016 | case SIO_NCT6779_ID: |
1017 | sio_data->kind = nct6779; |
1018 | break; |
1019 | case SIO_NCT6791_ID: |
1020 | sio_data->kind = nct6791; |
1021 | break; |
1022 | case SIO_NCT6792_ID: |
1023 | sio_data->kind = nct6792; |
1024 | break; |
1025 | case SIO_NCT6793_ID: |
1026 | sio_data->kind = nct6793; |
1027 | break; |
1028 | case SIO_NCT6795_ID: |
1029 | sio_data->kind = nct6795; |
1030 | break; |
1031 | case SIO_NCT6796_ID: |
1032 | sio_data->kind = nct6796; |
1033 | break; |
1034 | case SIO_NCT6797_ID: |
1035 | sio_data->kind = nct6797; |
1036 | break; |
1037 | case SIO_NCT6798_ID: |
1038 | sio_data->kind = nct6798; |
1039 | break; |
1040 | case SIO_NCT6799_ID: |
1041 | sio_data->kind = nct6799; |
1042 | break; |
1043 | default: |
1044 | if (val != 0xffff) |
1045 | pr_debug("unsupported chip ID: 0x%04x\n", val); |
1046 | sio_data->sio_exit(sio_data); |
1047 | return -ENODEV; |
1048 | } |
1049 | |
1050 | /* We have a known chip, find the HWM I/O address */ |
1051 | sio_data->sio_select(sio_data, NCT6775_LD_HWM); |
1052 | val = (sio_data->sio_inb(sio_data, SIO_REG_ADDR) << 8) |
1053 | | sio_data->sio_inb(sio_data, SIO_REG_ADDR + 1); |
1054 | addr = val & IOREGION_ALIGNMENT; |
1055 | if (addr == 0) { |
1056 | pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n"); |
1057 | sio_data->sio_exit(sio_data); |
1058 | return -ENODEV; |
1059 | } |
1060 | |
1061 | /* Activate logical device if needed */ |
1062 | val = sio_data->sio_inb(sio_data, SIO_REG_ENABLE); |
1063 | if (!(val & 0x01)) { |
1064 | pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n"); |
1065 | sio_data->sio_outb(sio_data, SIO_REG_ENABLE, val | 0x01); |
1066 | } |
1067 | |
1068 | if (sio_data->kind == nct6791 || sio_data->kind == nct6792 || |
1069 | sio_data->kind == nct6793 || sio_data->kind == nct6795 || |
1070 | sio_data->kind == nct6796 || sio_data->kind == nct6797 || |
1071 | sio_data->kind == nct6798 || sio_data->kind == nct6799) |
1072 | nct6791_enable_io_mapping(sio_data); |
1073 | |
1074 | sio_data->sio_exit(sio_data); |
1075 | pr_info("Found %s or compatible chip at %#x:%#x\n", |
1076 | nct6775_sio_names[sio_data->kind], sioaddr, addr); |
1077 | |
1078 | return addr; |
1079 | } |
1080 | |
1081 | /* |
1082 | * when Super-I/O functions move to a separate file, the Super-I/O |
1083 | * bus will manage the lifetime of the device and this module will only keep |
1084 | * track of the nct6775 driver. But since we use platform_device_alloc(), we |
1085 | * must keep track of the device |
1086 | */ |
1087 | static struct platform_device *pdev[2]; |
1088 | |
1089 | static const char * const asus_wmi_boards[] = { |
1090 | "B360M-BASALT", |
1091 | "B360M-D3H", |
1092 | "EX-B360M-V", |
1093 | "EX-B360M-V3", |
1094 | "EX-B360M-V5", |
1095 | "EX-B460M-V5", |
1096 | "EX-H410M-V3", |
1097 | "PRIME A520M-A", |
1098 | "PRIME A520M-A II", |
1099 | "PRIME A520M-E", |
1100 | "PRIME A520M-K", |
1101 | "PRIME B360-PLUS", |
1102 | "PRIME B360M-A", |
1103 | "PRIME B360M-C", |
1104 | "PRIME B360M-D", |
1105 | "PRIME B360M-K", |
1106 | "PRIME B460-PLUS", |
1107 | "PRIME B460I-PLUS", |
1108 | "PRIME B460M-A", |
1109 | "PRIME B460M-A R2.0", |
1110 | "PRIME B460M-K", |
1111 | "PRIME B550-PLUS", |
1112 | "PRIME B550-PLUS AC-HES", |
1113 | "PRIME B550M-A", |
1114 | "PRIME B550M-A (WI-FI)", |
1115 | "PRIME B550M-A AC", |
1116 | "PRIME B550M-A WIFI II", |
1117 | "PRIME B550M-K", |
1118 | "PRIME H310-PLUS", |
1119 | "PRIME H310I-PLUS", |
1120 | "PRIME H310M-A", |
1121 | "PRIME H310M-C", |
1122 | "PRIME H310M-D", |
1123 | "PRIME H310M-DASH", |
1124 | "PRIME H310M-E", |
1125 | "PRIME H310M-E/BR", |
1126 | "PRIME H310M-F", |
1127 | "PRIME H310M-K", |
1128 | "PRIME H310T", |
1129 | "PRIME H370-A", |
1130 | "PRIME H370-PLUS", |
1131 | "PRIME H370M-PLUS", |
1132 | "PRIME H410I-PLUS", |
1133 | "PRIME H410M-A", |
1134 | "PRIME H410M-D", |
1135 | "PRIME H410M-E", |
1136 | "PRIME H410M-F", |
1137 | "PRIME H410M-K", |
1138 | "PRIME H410M-K R2.0", |
1139 | "PRIME H410M-R", |
1140 | "PRIME H470-PLUS", |
1141 | "PRIME H470M-PLUS", |
1142 | "PRIME H510M-K R2.0", |
1143 | "PRIME Q370M-C", |
1144 | "PRIME X570-P", |
1145 | "PRIME X570-PRO", |
1146 | "PRIME Z390-A", |
1147 | "PRIME Z390-A/H10", |
1148 | "PRIME Z390-P", |
1149 | "PRIME Z390M-PLUS", |
1150 | "PRIME Z490-A", |
1151 | "PRIME Z490-P", |
1152 | "PRIME Z490-V", |
1153 | "PRIME Z490M-PLUS", |
1154 | "PRO B460M-C", |
1155 | "PRO H410M-C", |
1156 | "PRO H410T", |
1157 | "PRO Q470M-C", |
1158 | "Pro A520M-C", |
1159 | "Pro A520M-C II", |
1160 | "Pro B550M-C", |
1161 | "Pro WS X570-ACE", |
1162 | "ProArt B550-CREATOR", |
1163 | "ProArt X570-CREATOR WIFI", |
1164 | "ProArt Z490-CREATOR 10G", |
1165 | "ROG CROSSHAIR VIII DARK HERO", |
1166 | "ROG CROSSHAIR VIII EXTREME", |
1167 | "ROG CROSSHAIR VIII FORMULA", |
1168 | "ROG CROSSHAIR VIII HERO", |
1169 | "ROG CROSSHAIR VIII HERO (WI-FI)", |
1170 | "ROG CROSSHAIR VIII IMPACT", |
1171 | "ROG MAXIMUS XI APEX", |
1172 | "ROG MAXIMUS XI CODE", |
1173 | "ROG MAXIMUS XI EXTREME", |
1174 | "ROG MAXIMUS XI FORMULA", |
1175 | "ROG MAXIMUS XI GENE", |
1176 | "ROG MAXIMUS XI HERO", |
1177 | "ROG MAXIMUS XI HERO (WI-FI)", |
1178 | "ROG MAXIMUS XII APEX", |
1179 | "ROG MAXIMUS XII EXTREME", |
1180 | "ROG MAXIMUS XII FORMULA", |
1181 | "ROG MAXIMUS XII HERO (WI-FI)", |
1182 | "ROG STRIX B360-F GAMING", |
1183 | "ROG STRIX B360-G GAMING", |
1184 | "ROG STRIX B360-H GAMING", |
1185 | "ROG STRIX B360-H GAMING/OPTANE", |
1186 | "ROG STRIX B360-I GAMING", |
1187 | "ROG STRIX B460-F GAMING", |
1188 | "ROG STRIX B460-G GAMING", |
1189 | "ROG STRIX B460-H GAMING", |
1190 | "ROG STRIX B460-I GAMING", |
1191 | "ROG STRIX B550-A GAMING", |
1192 | "ROG STRIX B550-E GAMING", |
1193 | "ROG STRIX B550-F GAMING", |
1194 | "ROG STRIX B550-F GAMING (WI-FI)", |
1195 | "ROG STRIX B550-F GAMING WIFI II", |
1196 | "ROG STRIX B550-I GAMING", |
1197 | "ROG STRIX B550-XE GAMING WIFI", |
1198 | "ROG STRIX H370-F GAMING", |
1199 | "ROG STRIX H370-I GAMING", |
1200 | "ROG STRIX H470-I GAMING", |
1201 | "ROG STRIX X570-E GAMING", |
1202 | "ROG STRIX X570-E GAMING WIFI II", |
1203 | "ROG STRIX X570-F GAMING", |
1204 | "ROG STRIX X570-I GAMING", |
1205 | "ROG STRIX Z390-E GAMING", |
1206 | "ROG STRIX Z390-F GAMING", |
1207 | "ROG STRIX Z390-H GAMING", |
1208 | "ROG STRIX Z390-I GAMING", |
1209 | "ROG STRIX Z490-A GAMING", |
1210 | "ROG STRIX Z490-E GAMING", |
1211 | "ROG STRIX Z490-F GAMING", |
1212 | "ROG STRIX Z490-G GAMING", |
1213 | "ROG STRIX Z490-G GAMING (WI-FI)", |
1214 | "ROG STRIX Z490-H GAMING", |
1215 | "ROG STRIX Z490-I GAMING", |
1216 | "TUF B360-PLUS GAMING", |
1217 | "TUF B360-PRO GAMING", |
1218 | "TUF B360-PRO GAMING (WI-FI)", |
1219 | "TUF B360M-E GAMING", |
1220 | "TUF B360M-PLUS GAMING", |
1221 | "TUF B360M-PLUS GAMING S", |
1222 | "TUF B360M-PLUS GAMING/BR", |
1223 | "TUF GAMING A520M-PLUS", |
1224 | "TUF GAMING A520M-PLUS II", |
1225 | "TUF GAMING A520M-PLUS WIFI", |
1226 | "TUF GAMING B460-PLUS", |
1227 | "TUF GAMING B460-PRO (WI-FI)", |
1228 | "TUF GAMING B460M-PLUS", |
1229 | "TUF GAMING B460M-PLUS (WI-FI)", |
1230 | "TUF GAMING B460M-PRO", |
1231 | "TUF GAMING B550-PLUS", |
1232 | "TUF GAMING B550-PLUS (WI-FI)", |
1233 | "TUF GAMING B550-PLUS WIFI II", |
1234 | "TUF GAMING B550-PRO", |
1235 | "TUF GAMING B550M ZAKU (WI-FI)", |
1236 | "TUF GAMING B550M-E", |
1237 | "TUF GAMING B550M-E WIFI", |
1238 | "TUF GAMING B550M-PLUS", |
1239 | "TUF GAMING B550M-PLUS (WI-FI)", |
1240 | "TUF GAMING B550M-PLUS WIFI II", |
1241 | "TUF GAMING H470-PRO", |
1242 | "TUF GAMING H470-PRO (WI-FI)", |
1243 | "TUF GAMING X570-PLUS", |
1244 | "TUF GAMING X570-PLUS (WI-FI)", |
1245 | "TUF GAMING X570-PLUS_BR", |
1246 | "TUF GAMING X570-PRO (WI-FI)", |
1247 | "TUF GAMING X570-PRO WIFI II", |
1248 | "TUF GAMING Z490-PLUS", |
1249 | "TUF GAMING Z490-PLUS (WI-FI)", |
1250 | "TUF H310-PLUS GAMING", |
1251 | "TUF H310M-PLUS GAMING", |
1252 | "TUF H310M-PLUS GAMING/BR", |
1253 | "TUF H370-PRO GAMING", |
1254 | "TUF H370-PRO GAMING (WI-FI)", |
1255 | "TUF Z390-PLUS GAMING", |
1256 | "TUF Z390-PLUS GAMING (WI-FI)", |
1257 | "TUF Z390-PRO GAMING", |
1258 | "TUF Z390M-PRO GAMING", |
1259 | "TUF Z390M-PRO GAMING (WI-FI)", |
1260 | "WS Z390 PRO", |
1261 | "Z490-GUNDAM (WI-FI)", |
1262 | }; |
1263 | |
1264 | static const char * const asus_msi_boards[] = { |
1265 | "B560M-P", |
1266 | "EX-B560M-V5", |
1267 | "EX-B660M-V5 D4", |
1268 | "EX-B660M-V5 PRO D4", |
1269 | "EX-B760M-V5 D4", |
1270 | "EX-H510M-V3", |
1271 | "EX-H610M-V3 D4", |
1272 | "PRIME A620M-A", |
1273 | "PRIME B560-PLUS", |
1274 | "PRIME B560-PLUS AC-HES", |
1275 | "PRIME B560M-A", |
1276 | "PRIME B560M-A AC", |
1277 | "PRIME B560M-K", |
1278 | "PRIME B650-PLUS", |
1279 | "PRIME B650M-A", |
1280 | "PRIME B650M-A AX", |
1281 | "PRIME B650M-A AX II", |
1282 | "PRIME B650M-A II", |
1283 | "PRIME B650M-A WIFI", |
1284 | "PRIME B650M-A WIFI II", |
1285 | "PRIME B660-PLUS D4", |
1286 | "PRIME B660M-A AC D4", |
1287 | "PRIME B660M-A D4", |
1288 | "PRIME B660M-A WIFI D4", |
1289 | "PRIME B760-PLUS", |
1290 | "PRIME B760-PLUS D4", |
1291 | "PRIME B760M-A", |
1292 | "PRIME B760M-A AX D4", |
1293 | "PRIME B760M-A D4", |
1294 | "PRIME B760M-A WIFI", |
1295 | "PRIME B760M-A WIFI D4", |
1296 | "PRIME B760M-AJ D4", |
1297 | "PRIME B760M-K D4", |
1298 | "PRIME H510M-A", |
1299 | "PRIME H510M-A WIFI", |
1300 | "PRIME H510M-D", |
1301 | "PRIME H510M-E", |
1302 | "PRIME H510M-F", |
1303 | "PRIME H510M-K", |
1304 | "PRIME H510M-R", |
1305 | "PRIME H510T2/CSM", |
1306 | "PRIME H570-PLUS", |
1307 | "PRIME H570M-PLUS", |
1308 | "PRIME H610I-PLUS D4", |
1309 | "PRIME H610M-A D4", |
1310 | "PRIME H610M-A WIFI D4", |
1311 | "PRIME H610M-D D4", |
1312 | "PRIME H610M-E D4", |
1313 | "PRIME H610M-F D4", |
1314 | "PRIME H610M-K D4", |
1315 | "PRIME H610M-R D4", |
1316 | "PRIME H670-PLUS D4", |
1317 | "PRIME H770-PLUS D4", |
1318 | "PRIME X670-P", |
1319 | "PRIME X670-P WIFI", |
1320 | "PRIME X670E-PRO WIFI", |
1321 | "PRIME Z590-A", |
1322 | "PRIME Z590-P", |
1323 | "PRIME Z590-P WIFI", |
1324 | "PRIME Z590-V", |
1325 | "PRIME Z590M-PLUS", |
1326 | "PRIME Z690-A", |
1327 | "PRIME Z690-P", |
1328 | "PRIME Z690-P D4", |
1329 | "PRIME Z690-P WIFI", |
1330 | "PRIME Z690-P WIFI D4", |
1331 | "PRIME Z690M-PLUS D4", |
1332 | "PRIME Z790-A WIFI", |
1333 | "PRIME Z790-P", |
1334 | "PRIME Z790-P D4", |
1335 | "PRIME Z790-P WIFI", |
1336 | "PRIME Z790-P WIFI D4", |
1337 | "PRIME Z790M-PLUS", |
1338 | "PRIME Z790M-PLUS D4", |
1339 | "Pro B560M-C", |
1340 | "Pro B560M-CT", |
1341 | "Pro B660M-C", |
1342 | "Pro B660M-C D4", |
1343 | "Pro B760M-C", |
1344 | "Pro B760M-CT", |
1345 | "Pro H510M-C", |
1346 | "Pro H510M-CT", |
1347 | "Pro H610M-C", |
1348 | "Pro H610M-C D4", |
1349 | "Pro H610M-CT D4", |
1350 | "Pro H610T D4", |
1351 | "Pro Q670M-C", |
1352 | "Pro WS W680-ACE", |
1353 | "Pro WS W680-ACE IPMI", |
1354 | "Pro WS W790-ACE", |
1355 | "Pro WS W790E-SAGE SE", |
1356 | "ProArt B650-CREATOR", |
1357 | "ProArt B660-CREATOR D4", |
1358 | "ProArt B760-CREATOR D4", |
1359 | "ProArt X670E-CREATOR WIFI", |
1360 | "ProArt Z690-CREATOR WIFI", |
1361 | "ProArt Z790-CREATOR WIFI", |
1362 | "ROG CROSSHAIR X670E EXTREME", |
1363 | "ROG CROSSHAIR X670E GENE", |
1364 | "ROG CROSSHAIR X670E HERO", |
1365 | "ROG MAXIMUS XIII APEX", |
1366 | "ROG MAXIMUS XIII EXTREME", |
1367 | "ROG MAXIMUS XIII EXTREME GLACIAL", |
1368 | "ROG MAXIMUS XIII HERO", |
1369 | "ROG MAXIMUS Z690 APEX", |
1370 | "ROG MAXIMUS Z690 EXTREME", |
1371 | "ROG MAXIMUS Z690 EXTREME GLACIAL", |
1372 | "ROG MAXIMUS Z690 FORMULA", |
1373 | "ROG MAXIMUS Z690 HERO", |
1374 | "ROG MAXIMUS Z690 HERO EVA", |
1375 | "ROG MAXIMUS Z790 APEX", |
1376 | "ROG MAXIMUS Z790 EXTREME", |
1377 | "ROG MAXIMUS Z790 HERO", |
1378 | "ROG STRIX B560-A GAMING WIFI", |
1379 | "ROG STRIX B560-E GAMING WIFI", |
1380 | "ROG STRIX B560-F GAMING WIFI", |
1381 | "ROG STRIX B560-G GAMING WIFI", |
1382 | "ROG STRIX B560-I GAMING WIFI", |
1383 | "ROG STRIX B650-A GAMING WIFI", |
1384 | "ROG STRIX B650E-E GAMING WIFI", |
1385 | "ROG STRIX B650E-F GAMING WIFI", |
1386 | "ROG STRIX B650E-I GAMING WIFI", |
1387 | "ROG STRIX B660-A GAMING WIFI", |
1388 | "ROG STRIX B660-A GAMING WIFI D4", |
1389 | "ROG STRIX B660-F GAMING WIFI", |
1390 | "ROG STRIX B660-G GAMING WIFI", |
1391 | "ROG STRIX B660-I GAMING WIFI", |
1392 | "ROG STRIX B760-A GAMING WIFI", |
1393 | "ROG STRIX B760-A GAMING WIFI D4", |
1394 | "ROG STRIX B760-F GAMING WIFI", |
1395 | "ROG STRIX B760-G GAMING WIFI", |
1396 | "ROG STRIX B760-G GAMING WIFI D4", |
1397 | "ROG STRIX B760-I GAMING WIFI", |
1398 | "ROG STRIX X670E-A GAMING WIFI", |
1399 | "ROG STRIX X670E-E GAMING WIFI", |
1400 | "ROG STRIX X670E-F GAMING WIFI", |
1401 | "ROG STRIX X670E-I GAMING WIFI", |
1402 | "ROG STRIX Z590-A GAMING WIFI", |
1403 | "ROG STRIX Z590-A GAMING WIFI II", |
1404 | "ROG STRIX Z590-E GAMING WIFI", |
1405 | "ROG STRIX Z590-F GAMING WIFI", |
1406 | "ROG STRIX Z590-I GAMING WIFI", |
1407 | "ROG STRIX Z690-A GAMING WIFI", |
1408 | "ROG STRIX Z690-A GAMING WIFI D4", |
1409 | "ROG STRIX Z690-E GAMING WIFI", |
1410 | "ROG STRIX Z690-F GAMING WIFI", |
1411 | "ROG STRIX Z690-G GAMING WIFI", |
1412 | "ROG STRIX Z690-I GAMING WIFI", |
1413 | "ROG STRIX Z790-A GAMING WIFI", |
1414 | "ROG STRIX Z790-A GAMING WIFI D4", |
1415 | "ROG STRIX Z790-E GAMING WIFI", |
1416 | "ROG STRIX Z790-F GAMING WIFI", |
1417 | "ROG STRIX Z790-H GAMING WIFI", |
1418 | "ROG STRIX Z790-I GAMING WIFI", |
1419 | "TUF GAMING A620M-PLUS", |
1420 | "TUF GAMING A620M-PLUS WIFI", |
1421 | "TUF GAMING B560-PLUS WIFI", |
1422 | "TUF GAMING B560M-E", |
1423 | "TUF GAMING B560M-PLUS", |
1424 | "TUF GAMING B560M-PLUS WIFI", |
1425 | "TUF GAMING B650-PLUS", |
1426 | "TUF GAMING B650-PLUS WIFI", |
1427 | "TUF GAMING B650M-PLUS", |
1428 | "TUF GAMING B650M-PLUS WIFI", |
1429 | "TUF GAMING B660-PLUS WIFI D4", |
1430 | "TUF GAMING B660M-E D4", |
1431 | "TUF GAMING B660M-PLUS D4", |
1432 | "TUF GAMING B660M-PLUS WIFI", |
1433 | "TUF GAMING B660M-PLUS WIFI D4", |
1434 | "TUF GAMING B760-PLUS WIFI", |
1435 | "TUF GAMING B760-PLUS WIFI D4", |
1436 | "TUF GAMING B760M-BTF WIFI D4", |
1437 | "TUF GAMING B760M-E D4", |
1438 | "TUF GAMING B760M-PLUS", |
1439 | "TUF GAMING B760M-PLUS D4", |
1440 | "TUF GAMING B760M-PLUS WIFI", |
1441 | "TUF GAMING B760M-PLUS WIFI D4", |
1442 | "TUF GAMING H570-PRO", |
1443 | "TUF GAMING H570-PRO WIFI", |
1444 | "TUF GAMING H670-PRO WIFI D4", |
1445 | "TUF GAMING H770-PRO WIFI", |
1446 | "TUF GAMING X670E-PLUS", |
1447 | "TUF GAMING X670E-PLUS WIFI", |
1448 | "TUF GAMING Z590-PLUS", |
1449 | "TUF GAMING Z590-PLUS WIFI", |
1450 | "TUF GAMING Z690-PLUS", |
1451 | "TUF GAMING Z690-PLUS D4", |
1452 | "TUF GAMING Z690-PLUS WIFI", |
1453 | "TUF GAMING Z690-PLUS WIFI D4", |
1454 | "TUF GAMING Z790-PLUS D4", |
1455 | "TUF GAMING Z790-PLUS WIFI", |
1456 | "TUF GAMING Z790-PLUS WIFI D4", |
1457 | "Z590 WIFI GUNDAM EDITION", |
1458 | }; |
1459 | |
1460 | #if IS_ENABLED(CONFIG_ACPI) |
1461 | /* |
1462 | * Callback for acpi_bus_for_each_dev() to find the right device |
1463 | * by _UID and _HID and return 1 to stop iteration. |
1464 | */ |
1465 | static int nct6775_asuswmi_device_match(struct device *dev, void *data) |
1466 | { |
1467 | struct acpi_device *adev = to_acpi_device(dev); |
1468 | |
1469 | if (acpi_dev_hid_uid_match(adev, ASUSWMI_DEVICE_HID, data)) { |
1470 | asus_acpi_dev = adev; |
1471 | return 1; |
1472 | } |
1473 | |
1474 | return 0; |
1475 | } |
1476 | #endif |
1477 | |
1478 | static enum sensor_access nct6775_determine_access(const char *device_uid) |
1479 | { |
1480 | #if IS_ENABLED(CONFIG_ACPI) |
1481 | u8 tmp; |
1482 | |
1483 | acpi_bus_for_each_dev(fn: nct6775_asuswmi_device_match, data: (void *)device_uid); |
1484 | if (!asus_acpi_dev) |
1485 | return access_direct; |
1486 | |
1487 | /* if reading chip id via ACPI succeeds, use WMI "WMBD" method for access */ |
1488 | if (!nct6775_asuswmi_read(bank: 0, NCT6775_PORT_CHIPID, val: &tmp) && tmp) { |
1489 | pr_debug("Using Asus WMBD method of %s to access %#x chip.\n", device_uid, tmp); |
1490 | return access_asuswmi; |
1491 | } |
1492 | #endif |
1493 | |
1494 | return access_direct; |
1495 | } |
1496 | |
1497 | static int __init sensors_nct6775_platform_init(void) |
1498 | { |
1499 | int i, err; |
1500 | bool found = false; |
1501 | int address; |
1502 | struct resource res; |
1503 | struct nct6775_sio_data sio_data; |
1504 | int sioaddr[2] = { 0x2e, 0x4e }; |
1505 | enum sensor_access access = access_direct; |
1506 | const char *board_vendor, *board_name; |
1507 | |
1508 | err = platform_driver_register(&nct6775_driver); |
1509 | if (err) |
1510 | return err; |
1511 | |
1512 | board_vendor = dmi_get_system_info(field: DMI_BOARD_VENDOR); |
1513 | board_name = dmi_get_system_info(field: DMI_BOARD_NAME); |
1514 | |
1515 | if (board_name && board_vendor && |
1516 | !strcmp(board_vendor, "ASUSTeK COMPUTER INC.")) { |
1517 | err = match_string(array: asus_wmi_boards, ARRAY_SIZE(asus_wmi_boards), |
1518 | string: board_name); |
1519 | if (err >= 0) |
1520 | access = nct6775_determine_access(ASUSWMI_DEVICE_UID); |
1521 | |
1522 | err = match_string(array: asus_msi_boards, ARRAY_SIZE(asus_msi_boards), |
1523 | string: board_name); |
1524 | if (err >= 0) |
1525 | access = nct6775_determine_access(ASUSMSI_DEVICE_UID); |
1526 | } |
1527 | |
1528 | /* |
1529 | * initialize sio_data->kind and sio_data->sioreg. |
1530 | * |
1531 | * when Super-I/O functions move to a separate file, the Super-I/O |
1532 | * driver will probe 0x2e and 0x4e and auto-detect the presence of a |
1533 | * nct6775 hardware monitor, and call probe() |
1534 | */ |
1535 | for (i = 0; i < ARRAY_SIZE(pdev); i++) { |
1536 | sio_data.sio_outb = superio_outb; |
1537 | sio_data.sio_inb = superio_inb; |
1538 | sio_data.sio_select = superio_select; |
1539 | sio_data.sio_enter = superio_enter; |
1540 | sio_data.sio_exit = superio_exit; |
1541 | |
1542 | address = nct6775_find(sioaddr: sioaddr[i], sio_data: &sio_data); |
1543 | if (address <= 0) |
1544 | continue; |
1545 | |
1546 | found = true; |
1547 | |
1548 | sio_data.access = access; |
1549 | |
1550 | if (access == access_asuswmi) { |
1551 | sio_data.sio_outb = superio_wmi_outb; |
1552 | sio_data.sio_inb = superio_wmi_inb; |
1553 | sio_data.sio_select = superio_wmi_select; |
1554 | sio_data.sio_enter = superio_wmi_enter; |
1555 | sio_data.sio_exit = superio_wmi_exit; |
1556 | } |
1557 | |
1558 | pdev[i] = platform_device_alloc(DRVNAME, id: address); |
1559 | if (!pdev[i]) { |
1560 | err = -ENOMEM; |
1561 | goto exit_device_unregister; |
1562 | } |
1563 | |
1564 | err = platform_device_add_data(pdev: pdev[i], data: &sio_data, |
1565 | size: sizeof(struct nct6775_sio_data)); |
1566 | if (err) |
1567 | goto exit_device_put; |
1568 | |
1569 | if (sio_data.access == access_direct) { |
1570 | memset(&res, 0, sizeof(res)); |
1571 | res.name = DRVNAME; |
1572 | res.start = address + IOREGION_OFFSET; |
1573 | res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1; |
1574 | res.flags = IORESOURCE_IO; |
1575 | |
1576 | err = acpi_check_resource_conflict(res: &res); |
1577 | if (err) { |
1578 | platform_device_put(pdev: pdev[i]); |
1579 | pdev[i] = NULL; |
1580 | continue; |
1581 | } |
1582 | |
1583 | err = platform_device_add_resources(pdev: pdev[i], res: &res, num: 1); |
1584 | if (err) |
1585 | goto exit_device_put; |
1586 | } |
1587 | |
1588 | /* platform_device_add calls probe() */ |
1589 | err = platform_device_add(pdev: pdev[i]); |
1590 | if (err) |
1591 | goto exit_device_put; |
1592 | } |
1593 | if (!found) { |
1594 | err = -ENODEV; |
1595 | goto exit_unregister; |
1596 | } |
1597 | |
1598 | return 0; |
1599 | |
1600 | exit_device_put: |
1601 | platform_device_put(pdev: pdev[i]); |
1602 | exit_device_unregister: |
1603 | while (i--) |
1604 | platform_device_unregister(pdev[i]); |
1605 | exit_unregister: |
1606 | platform_driver_unregister(&nct6775_driver); |
1607 | return err; |
1608 | } |
1609 | |
1610 | static void __exit sensors_nct6775_platform_exit(void) |
1611 | { |
1612 | int i; |
1613 | |
1614 | for (i = 0; i < ARRAY_SIZE(pdev); i++) |
1615 | platform_device_unregister(pdev[i]); |
1616 | platform_driver_unregister(&nct6775_driver); |
1617 | } |
1618 | |
1619 | MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); |
1620 | MODULE_DESCRIPTION("Platform driver for NCT6775F and compatible chips"); |
1621 | MODULE_LICENSE("GPL"); |
1622 | MODULE_IMPORT_NS(HWMON_NCT6775); |
1623 | |
1624 | module_init(sensors_nct6775_platform_init); |
1625 | module_exit(sensors_nct6775_platform_exit); |
1626 |
Definitions
- sensor_access
- nct6775_sio_names
- force_id
- fan_debounce
- nct6775_sio_data
- asus_acpi_dev
- nct6775_asuswmi_evaluate_method
- nct6775_asuswmi_write
- nct6775_asuswmi_read
- superio_wmi_inb
- superio_wmi_outb
- superio_wmi_select
- superio_wmi_enter
- superio_wmi_exit
- superio_outb
- superio_inb
- superio_select
- superio_enter
- superio_exit
- nct6775_wmi_set_bank
- nct6775_wmi_reg_read
- nct6775_wmi_reg_write
- nct6775_set_bank
- nct6775_reg_read
- nct6775_reg_write
- nct6791_enable_io_mapping
- nct6775_suspend
- nct6775_resume
- nct6775_dev_pm_ops
- nct6775_check_fan_inputs
- cpu0_vid_show
- NCT6775_REG_CR_CASEOPEN_CLR
- NCT6775_CR_CASEOPEN_CLR_MASK
- clear_caseopen
- nct6775_other_is_visible
- nct6775_attributes_other
- nct6775_group_other
- nct6775_platform_probe_init
- nct6775_regmap_config
- nct6775_wmi_regmap_config
- nct6775_platform_probe
- nct6775_driver
- nct6775_find
- pdev
- asus_wmi_boards
- asus_msi_boards
- nct6775_asuswmi_device_match
- nct6775_determine_access
- sensors_nct6775_platform_init
Improve your Profiling and Debugging skills
Find out more