1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Nuvoton NCT6694 HWMON driver based on USB interface. 4 * 5 * Copyright (C) 2025 Nuvoton Technology Corp. 6 */ 7 8 #include <linux/bits.h> 9 #include <linux/bitfield.h> 10 #include <linux/hwmon.h> 11 #include <linux/kernel.h> 12 #include <linux/mfd/core.h> 13 #include <linux/mfd/nct6694.h> 14 #include <linux/module.h> 15 #include <linux/platform_device.h> 16 #include <linux/slab.h> 17 18 /* 19 * USB command module type for NCT6694 report channel 20 * This defines the module type used for communication with the NCT6694 21 * report channel over the USB interface. 22 */ 23 #define NCT6694_RPT_MOD 0xFF 24 25 /* Report channel */ 26 /* 27 * The report channel is used to report the status of the hardware monitor 28 * devices, such as voltage, temperature, fan speed, and PWM. 29 */ 30 #define NCT6694_VIN_IDX(x) (0x00 + (x)) 31 #define NCT6694_TIN_IDX(x) \ 32 ({ typeof(x) (_x) = (x); \ 33 ((_x) < 10) ? (0x10 + ((_x) * 2)) : \ 34 (0x30 + (((_x) - 10) * 2)); }) 35 #define NCT6694_FIN_IDX(x) (0x50 + ((x) * 2)) 36 #define NCT6694_PWM_IDX(x) (0x70 + (x)) 37 #define NCT6694_VIN_STS(x) (0x68 + (x)) 38 #define NCT6694_TIN_STS(x) (0x6A + (x)) 39 #define NCT6694_FIN_STS(x) (0x6E + (x)) 40 41 /* 42 * USB command module type for NCT6694 HWMON controller. 43 * This defines the module type used for communication with the NCT6694 44 * HWMON controller over the USB interface. 45 */ 46 #define NCT6694_HWMON_MOD 0x00 47 48 /* Command 00h - Hardware Monitor Control */ 49 #define NCT6694_HWMON_CONTROL 0x00 50 #define NCT6694_HWMON_CONTROL_SEL 0x00 51 52 /* Command 02h - Alarm Control */ 53 #define NCT6694_HWMON_ALARM 0x02 54 #define NCT6694_HWMON_ALARM_SEL 0x00 55 56 /* 57 * USB command module type for NCT6694 PWM controller. 58 * This defines the module type used for communication with the NCT6694 59 * PWM controller over the USB interface. 60 */ 61 #define NCT6694_PWM_MOD 0x01 62 63 /* PWM Command - Manual Control */ 64 #define NCT6694_PWM_CONTROL 0x01 65 #define NCT6694_PWM_CONTROL_SEL 0x00 66 67 #define NCT6694_FREQ_FROM_REG(reg) ((reg) * 25000 / 255) 68 #define NCT6694_FREQ_TO_REG(val) \ 69 (DIV_ROUND_CLOSEST(clamp_val((val), 100, 25000) * 255, 25000)) 70 71 #define NCT6694_LSB_REG_MASK GENMASK(7, 5) 72 #define NCT6694_TIN_HYST_MASK GENMASK(7, 5) 73 74 enum nct6694_hwmon_temp_mode { 75 NCT6694_HWMON_TWOTIME_IRQ = 0, 76 NCT6694_HWMON_ONETIME_IRQ, 77 NCT6694_HWMON_REALTIME_IRQ, 78 NCT6694_HWMON_COMPARE_IRQ, 79 }; 80 81 struct __packed nct6694_hwmon_control { 82 u8 vin_en[2]; 83 u8 tin_en[2]; 84 u8 fin_en[2]; 85 u8 pwm_en[2]; 86 u8 reserved1[40]; 87 u8 pwm_freq[10]; 88 u8 reserved2[6]; 89 }; 90 91 struct __packed nct6694_hwmon_alarm { 92 u8 smi_ctrl; 93 u8 reserved1[15]; 94 struct { 95 u8 hl; 96 u8 ll; 97 } vin_limit[16]; 98 struct { 99 u8 hyst; 100 s8 hl; 101 } tin_cfg[32]; 102 __be16 fin_ll[10]; 103 u8 reserved2[4]; 104 }; 105 106 struct __packed nct6694_pwm_control { 107 u8 mal_en[2]; 108 u8 mal_val[10]; 109 u8 reserved[12]; 110 }; 111 112 union __packed nct6694_hwmon_rpt { 113 u8 vin; 114 struct { 115 u8 msb; 116 u8 lsb; 117 } tin; 118 __be16 fin; 119 u8 pwm; 120 u8 status; 121 }; 122 123 union __packed nct6694_hwmon_msg { 124 struct nct6694_hwmon_alarm hwmon_alarm; 125 struct nct6694_pwm_control pwm_ctrl; 126 }; 127 128 struct nct6694_hwmon_data { 129 struct nct6694 *nct6694; 130 struct mutex lock; 131 struct nct6694_hwmon_control hwmon_en; 132 union nct6694_hwmon_rpt *rpt; 133 union nct6694_hwmon_msg *msg; 134 }; 135 136 static inline long in_from_reg(u8 reg) 137 { 138 return reg * 16; 139 } 140 141 static inline u8 in_to_reg(long val) 142 { 143 return DIV_ROUND_CLOSEST(val, 16); 144 } 145 146 static inline long temp_from_reg(s8 reg) 147 { 148 return reg * 1000; 149 } 150 151 static inline s8 temp_to_reg(long val) 152 { 153 return DIV_ROUND_CLOSEST(val, 1000); 154 } 155 156 #define NCT6694_HWMON_IN_CONFIG (HWMON_I_INPUT | HWMON_I_ENABLE | \ 157 HWMON_I_MAX | HWMON_I_MIN | \ 158 HWMON_I_ALARM) 159 #define NCT6694_HWMON_TEMP_CONFIG (HWMON_T_INPUT | HWMON_T_ENABLE | \ 160 HWMON_T_MAX | HWMON_T_MAX_HYST | \ 161 HWMON_T_MAX_ALARM) 162 #define NCT6694_HWMON_FAN_CONFIG (HWMON_F_INPUT | HWMON_F_ENABLE | \ 163 HWMON_F_MIN | HWMON_F_MIN_ALARM) 164 #define NCT6694_HWMON_PWM_CONFIG (HWMON_PWM_INPUT | HWMON_PWM_ENABLE | \ 165 HWMON_PWM_FREQ) 166 static const struct hwmon_channel_info *nct6694_info[] = { 167 HWMON_CHANNEL_INFO(in, 168 NCT6694_HWMON_IN_CONFIG, /* VIN0 */ 169 NCT6694_HWMON_IN_CONFIG, /* VIN1 */ 170 NCT6694_HWMON_IN_CONFIG, /* VIN2 */ 171 NCT6694_HWMON_IN_CONFIG, /* VIN3 */ 172 NCT6694_HWMON_IN_CONFIG, /* VIN5 */ 173 NCT6694_HWMON_IN_CONFIG, /* VIN6 */ 174 NCT6694_HWMON_IN_CONFIG, /* VIN7 */ 175 NCT6694_HWMON_IN_CONFIG, /* VIN14 */ 176 NCT6694_HWMON_IN_CONFIG, /* VIN15 */ 177 NCT6694_HWMON_IN_CONFIG, /* VIN16 */ 178 NCT6694_HWMON_IN_CONFIG, /* VBAT */ 179 NCT6694_HWMON_IN_CONFIG, /* VSB */ 180 NCT6694_HWMON_IN_CONFIG, /* AVSB */ 181 NCT6694_HWMON_IN_CONFIG, /* VCC */ 182 NCT6694_HWMON_IN_CONFIG, /* VHIF */ 183 NCT6694_HWMON_IN_CONFIG), /* VTT */ 184 185 HWMON_CHANNEL_INFO(temp, 186 NCT6694_HWMON_TEMP_CONFIG, /* THR1 */ 187 NCT6694_HWMON_TEMP_CONFIG, /* THR2 */ 188 NCT6694_HWMON_TEMP_CONFIG, /* THR14 */ 189 NCT6694_HWMON_TEMP_CONFIG, /* THR15 */ 190 NCT6694_HWMON_TEMP_CONFIG, /* THR16 */ 191 NCT6694_HWMON_TEMP_CONFIG, /* TDP0 */ 192 NCT6694_HWMON_TEMP_CONFIG, /* TDP1 */ 193 NCT6694_HWMON_TEMP_CONFIG, /* TDP2 */ 194 NCT6694_HWMON_TEMP_CONFIG, /* TDP3 */ 195 NCT6694_HWMON_TEMP_CONFIG, /* TDP4 */ 196 NCT6694_HWMON_TEMP_CONFIG, /* DTIN0 */ 197 NCT6694_HWMON_TEMP_CONFIG, /* DTIN1 */ 198 NCT6694_HWMON_TEMP_CONFIG, /* DTIN2 */ 199 NCT6694_HWMON_TEMP_CONFIG, /* DTIN3 */ 200 NCT6694_HWMON_TEMP_CONFIG, /* DTIN4 */ 201 NCT6694_HWMON_TEMP_CONFIG, /* DTIN5 */ 202 NCT6694_HWMON_TEMP_CONFIG, /* DTIN6 */ 203 NCT6694_HWMON_TEMP_CONFIG, /* DTIN7 */ 204 NCT6694_HWMON_TEMP_CONFIG, /* DTIN8 */ 205 NCT6694_HWMON_TEMP_CONFIG, /* DTIN9 */ 206 NCT6694_HWMON_TEMP_CONFIG, /* DTIN10 */ 207 NCT6694_HWMON_TEMP_CONFIG, /* DTIN11 */ 208 NCT6694_HWMON_TEMP_CONFIG, /* DTIN12 */ 209 NCT6694_HWMON_TEMP_CONFIG, /* DTIN13 */ 210 NCT6694_HWMON_TEMP_CONFIG, /* DTIN14 */ 211 NCT6694_HWMON_TEMP_CONFIG), /* DTIN15 */ 212 213 HWMON_CHANNEL_INFO(fan, 214 NCT6694_HWMON_FAN_CONFIG, /* FIN0 */ 215 NCT6694_HWMON_FAN_CONFIG, /* FIN1 */ 216 NCT6694_HWMON_FAN_CONFIG, /* FIN2 */ 217 NCT6694_HWMON_FAN_CONFIG, /* FIN3 */ 218 NCT6694_HWMON_FAN_CONFIG, /* FIN4 */ 219 NCT6694_HWMON_FAN_CONFIG, /* FIN5 */ 220 NCT6694_HWMON_FAN_CONFIG, /* FIN6 */ 221 NCT6694_HWMON_FAN_CONFIG, /* FIN7 */ 222 NCT6694_HWMON_FAN_CONFIG, /* FIN8 */ 223 NCT6694_HWMON_FAN_CONFIG), /* FIN9 */ 224 225 HWMON_CHANNEL_INFO(pwm, 226 NCT6694_HWMON_PWM_CONFIG, /* PWM0 */ 227 NCT6694_HWMON_PWM_CONFIG, /* PWM1 */ 228 NCT6694_HWMON_PWM_CONFIG, /* PWM2 */ 229 NCT6694_HWMON_PWM_CONFIG, /* PWM3 */ 230 NCT6694_HWMON_PWM_CONFIG, /* PWM4 */ 231 NCT6694_HWMON_PWM_CONFIG, /* PWM5 */ 232 NCT6694_HWMON_PWM_CONFIG, /* PWM6 */ 233 NCT6694_HWMON_PWM_CONFIG, /* PWM7 */ 234 NCT6694_HWMON_PWM_CONFIG, /* PWM8 */ 235 NCT6694_HWMON_PWM_CONFIG), /* PWM9 */ 236 NULL 237 }; 238 239 static int nct6694_in_read(struct device *dev, u32 attr, int channel, 240 long *val) 241 { 242 struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 243 struct nct6694_cmd_header cmd_hd; 244 unsigned char vin_en; 245 int ret; 246 247 guard(mutex)(&data->lock); 248 249 switch (attr) { 250 case hwmon_in_enable: 251 vin_en = data->hwmon_en.vin_en[(channel / 8)]; 252 *val = !!(vin_en & BIT(channel % 8)); 253 254 return 0; 255 case hwmon_in_input: 256 cmd_hd = (struct nct6694_cmd_header) { 257 .mod = NCT6694_RPT_MOD, 258 .offset = cpu_to_le16(NCT6694_VIN_IDX(channel)), 259 .len = cpu_to_le16(sizeof(data->rpt->vin)) 260 }; 261 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 262 &data->rpt->vin); 263 if (ret) 264 return ret; 265 266 *val = in_from_reg(data->rpt->vin); 267 268 return 0; 269 case hwmon_in_max: 270 cmd_hd = (struct nct6694_cmd_header) { 271 .mod = NCT6694_HWMON_MOD, 272 .cmd = NCT6694_HWMON_ALARM, 273 .sel = NCT6694_HWMON_ALARM_SEL, 274 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 275 }; 276 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 277 &data->msg->hwmon_alarm); 278 if (ret) 279 return ret; 280 281 *val = in_from_reg(data->msg->hwmon_alarm.vin_limit[channel].hl); 282 283 return 0; 284 case hwmon_in_min: 285 cmd_hd = (struct nct6694_cmd_header) { 286 .mod = NCT6694_HWMON_MOD, 287 .cmd = NCT6694_HWMON_ALARM, 288 .sel = NCT6694_HWMON_ALARM_SEL, 289 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 290 }; 291 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 292 &data->msg->hwmon_alarm); 293 if (ret) 294 return ret; 295 296 *val = in_from_reg(data->msg->hwmon_alarm.vin_limit[channel].ll); 297 298 return 0; 299 case hwmon_in_alarm: 300 cmd_hd = (struct nct6694_cmd_header) { 301 .mod = NCT6694_RPT_MOD, 302 .offset = cpu_to_le16(NCT6694_VIN_STS(channel / 8)), 303 .len = cpu_to_le16(sizeof(data->rpt->status)) 304 }; 305 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 306 &data->rpt->status); 307 if (ret) 308 return ret; 309 310 *val = !!(data->rpt->status & BIT(channel % 8)); 311 312 return 0; 313 default: 314 return -EOPNOTSUPP; 315 } 316 } 317 318 static int nct6694_temp_read(struct device *dev, u32 attr, int channel, 319 long *val) 320 { 321 struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 322 struct nct6694_cmd_header cmd_hd; 323 unsigned char temp_en, temp_hyst; 324 signed char temp_max; 325 int ret, temp_raw; 326 327 guard(mutex)(&data->lock); 328 329 switch (attr) { 330 case hwmon_temp_enable: 331 temp_en = data->hwmon_en.tin_en[channel / 8]; 332 *val = !!(temp_en & BIT(channel % 8)); 333 334 return 0; 335 case hwmon_temp_input: 336 cmd_hd = (struct nct6694_cmd_header) { 337 .mod = NCT6694_RPT_MOD, 338 .offset = cpu_to_le16(NCT6694_TIN_IDX(channel)), 339 .len = cpu_to_le16(sizeof(data->rpt->tin)) 340 }; 341 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 342 &data->rpt->tin); 343 if (ret) 344 return ret; 345 346 temp_raw = data->rpt->tin.msb << 3; 347 temp_raw |= FIELD_GET(NCT6694_LSB_REG_MASK, data->rpt->tin.lsb); 348 349 /* Real temperature(milli degrees Celsius) = temp_raw * 1000 * 0.125 */ 350 *val = sign_extend32(temp_raw, 10) * 125; 351 352 return 0; 353 case hwmon_temp_max: 354 cmd_hd = (struct nct6694_cmd_header) { 355 .mod = NCT6694_HWMON_MOD, 356 .cmd = NCT6694_HWMON_ALARM, 357 .sel = NCT6694_HWMON_ALARM_SEL, 358 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 359 }; 360 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 361 &data->msg->hwmon_alarm); 362 if (ret) 363 return ret; 364 365 *val = temp_from_reg(data->msg->hwmon_alarm.tin_cfg[channel].hl); 366 367 return 0; 368 case hwmon_temp_max_hyst: 369 cmd_hd = (struct nct6694_cmd_header) { 370 .mod = NCT6694_HWMON_MOD, 371 .cmd = NCT6694_HWMON_ALARM, 372 .sel = NCT6694_HWMON_ALARM_SEL, 373 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 374 }; 375 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 376 &data->msg->hwmon_alarm); 377 if (ret) 378 return ret; 379 380 temp_max = data->msg->hwmon_alarm.tin_cfg[channel].hl; 381 temp_hyst = FIELD_GET(NCT6694_TIN_HYST_MASK, 382 data->msg->hwmon_alarm.tin_cfg[channel].hyst); 383 *val = temp_from_reg(temp_max - temp_hyst); 384 385 return 0; 386 case hwmon_temp_max_alarm: 387 cmd_hd = (struct nct6694_cmd_header) { 388 .mod = NCT6694_RPT_MOD, 389 .offset = cpu_to_le16(NCT6694_TIN_STS(channel / 8)), 390 .len = cpu_to_le16(sizeof(data->rpt->status)) 391 }; 392 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 393 &data->rpt->status); 394 if (ret) 395 return ret; 396 397 *val = !!(data->rpt->status & BIT(channel % 8)); 398 399 return 0; 400 default: 401 return -EOPNOTSUPP; 402 } 403 } 404 405 static int nct6694_fan_read(struct device *dev, u32 attr, int channel, 406 long *val) 407 { 408 struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 409 struct nct6694_cmd_header cmd_hd; 410 unsigned char fanin_en; 411 int ret; 412 413 guard(mutex)(&data->lock); 414 415 switch (attr) { 416 case hwmon_fan_enable: 417 fanin_en = data->hwmon_en.fin_en[channel / 8]; 418 *val = !!(fanin_en & BIT(channel % 8)); 419 420 return 0; 421 case hwmon_fan_input: 422 cmd_hd = (struct nct6694_cmd_header) { 423 .mod = NCT6694_RPT_MOD, 424 .offset = cpu_to_le16(NCT6694_FIN_IDX(channel)), 425 .len = cpu_to_le16(sizeof(data->rpt->fin)) 426 }; 427 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 428 &data->rpt->fin); 429 if (ret) 430 return ret; 431 432 *val = be16_to_cpu(data->rpt->fin); 433 434 return 0; 435 case hwmon_fan_min: 436 cmd_hd = (struct nct6694_cmd_header) { 437 .mod = NCT6694_HWMON_MOD, 438 .cmd = NCT6694_HWMON_ALARM, 439 .sel = NCT6694_HWMON_ALARM_SEL, 440 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 441 }; 442 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 443 &data->msg->hwmon_alarm); 444 if (ret) 445 return ret; 446 447 *val = be16_to_cpu(data->msg->hwmon_alarm.fin_ll[channel]); 448 449 return 0; 450 case hwmon_fan_min_alarm: 451 cmd_hd = (struct nct6694_cmd_header) { 452 .mod = NCT6694_RPT_MOD, 453 .offset = cpu_to_le16(NCT6694_FIN_STS(channel / 8)), 454 .len = cpu_to_le16(sizeof(data->rpt->status)) 455 }; 456 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 457 &data->rpt->status); 458 if (ret) 459 return ret; 460 461 *val = !!(data->rpt->status & BIT(channel % 8)); 462 463 return 0; 464 default: 465 return -EOPNOTSUPP; 466 } 467 } 468 469 static int nct6694_pwm_read(struct device *dev, u32 attr, int channel, 470 long *val) 471 { 472 struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 473 struct nct6694_cmd_header cmd_hd; 474 unsigned char pwm_en; 475 int ret; 476 477 guard(mutex)(&data->lock); 478 479 switch (attr) { 480 case hwmon_pwm_enable: 481 pwm_en = data->hwmon_en.pwm_en[channel / 8]; 482 *val = !!(pwm_en & BIT(channel % 8)); 483 484 return 0; 485 case hwmon_pwm_input: 486 cmd_hd = (struct nct6694_cmd_header) { 487 .mod = NCT6694_RPT_MOD, 488 .offset = cpu_to_le16(NCT6694_PWM_IDX(channel)), 489 .len = cpu_to_le16(sizeof(data->rpt->pwm)) 490 }; 491 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 492 &data->rpt->pwm); 493 if (ret) 494 return ret; 495 496 *val = data->rpt->pwm; 497 498 return 0; 499 case hwmon_pwm_freq: 500 *val = NCT6694_FREQ_FROM_REG(data->hwmon_en.pwm_freq[channel]); 501 502 return 0; 503 default: 504 return -EOPNOTSUPP; 505 } 506 } 507 508 static int nct6694_in_write(struct device *dev, u32 attr, int channel, 509 long val) 510 { 511 struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 512 struct nct6694_cmd_header cmd_hd; 513 int ret; 514 515 guard(mutex)(&data->lock); 516 517 switch (attr) { 518 case hwmon_in_enable: 519 if (val == 0) 520 data->hwmon_en.vin_en[channel / 8] &= ~BIT(channel % 8); 521 else if (val == 1) 522 data->hwmon_en.vin_en[channel / 8] |= BIT(channel % 8); 523 else 524 return -EINVAL; 525 526 cmd_hd = (struct nct6694_cmd_header) { 527 .mod = NCT6694_HWMON_MOD, 528 .cmd = NCT6694_HWMON_CONTROL, 529 .sel = NCT6694_HWMON_CONTROL_SEL, 530 .len = cpu_to_le16(sizeof(data->hwmon_en)) 531 }; 532 533 return nct6694_write_msg(data->nct6694, &cmd_hd, 534 &data->hwmon_en); 535 case hwmon_in_max: 536 cmd_hd = (struct nct6694_cmd_header) { 537 .mod = NCT6694_HWMON_MOD, 538 .cmd = NCT6694_HWMON_ALARM, 539 .sel = NCT6694_HWMON_ALARM_SEL, 540 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 541 }; 542 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 543 &data->msg->hwmon_alarm); 544 if (ret) 545 return ret; 546 547 val = clamp_val(val, 0, 2032); 548 data->msg->hwmon_alarm.vin_limit[channel].hl = in_to_reg(val); 549 550 return nct6694_write_msg(data->nct6694, &cmd_hd, 551 &data->msg->hwmon_alarm); 552 case hwmon_in_min: 553 cmd_hd = (struct nct6694_cmd_header) { 554 .mod = NCT6694_HWMON_MOD, 555 .cmd = NCT6694_HWMON_ALARM, 556 .sel = NCT6694_HWMON_ALARM_SEL, 557 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 558 }; 559 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 560 &data->msg->hwmon_alarm); 561 if (ret) 562 return ret; 563 564 val = clamp_val(val, 0, 2032); 565 data->msg->hwmon_alarm.vin_limit[channel].ll = in_to_reg(val); 566 567 return nct6694_write_msg(data->nct6694, &cmd_hd, 568 &data->msg->hwmon_alarm); 569 default: 570 return -EOPNOTSUPP; 571 } 572 } 573 574 static int nct6694_temp_write(struct device *dev, u32 attr, int channel, 575 long val) 576 { 577 struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 578 struct nct6694_cmd_header cmd_hd; 579 unsigned char temp_hyst; 580 signed char temp_max; 581 int ret; 582 583 guard(mutex)(&data->lock); 584 585 switch (attr) { 586 case hwmon_temp_enable: 587 if (val == 0) 588 data->hwmon_en.tin_en[channel / 8] &= ~BIT(channel % 8); 589 else if (val == 1) 590 data->hwmon_en.tin_en[channel / 8] |= BIT(channel % 8); 591 else 592 return -EINVAL; 593 594 cmd_hd = (struct nct6694_cmd_header) { 595 .mod = NCT6694_HWMON_MOD, 596 .cmd = NCT6694_HWMON_CONTROL, 597 .sel = NCT6694_HWMON_CONTROL_SEL, 598 .len = cpu_to_le16(sizeof(data->hwmon_en)) 599 }; 600 601 return nct6694_write_msg(data->nct6694, &cmd_hd, 602 &data->hwmon_en); 603 case hwmon_temp_max: 604 cmd_hd = (struct nct6694_cmd_header) { 605 .mod = NCT6694_HWMON_MOD, 606 .cmd = NCT6694_HWMON_ALARM, 607 .sel = NCT6694_HWMON_ALARM_SEL, 608 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 609 }; 610 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 611 &data->msg->hwmon_alarm); 612 if (ret) 613 return ret; 614 615 val = clamp_val(val, -127000, 127000); 616 data->msg->hwmon_alarm.tin_cfg[channel].hl = temp_to_reg(val); 617 618 return nct6694_write_msg(data->nct6694, &cmd_hd, 619 &data->msg->hwmon_alarm); 620 case hwmon_temp_max_hyst: 621 cmd_hd = (struct nct6694_cmd_header) { 622 .mod = NCT6694_HWMON_MOD, 623 .cmd = NCT6694_HWMON_ALARM, 624 .sel = NCT6694_HWMON_ALARM_SEL, 625 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 626 }; 627 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 628 &data->msg->hwmon_alarm); 629 630 val = clamp_val(val, -127000, 127000); 631 temp_max = data->msg->hwmon_alarm.tin_cfg[channel].hl; 632 temp_hyst = temp_max - temp_to_reg(val); 633 temp_hyst = clamp_val(temp_hyst, 0, 7); 634 data->msg->hwmon_alarm.tin_cfg[channel].hyst = 635 (data->msg->hwmon_alarm.tin_cfg[channel].hyst & ~NCT6694_TIN_HYST_MASK) | 636 FIELD_PREP(NCT6694_TIN_HYST_MASK, temp_hyst); 637 638 return nct6694_write_msg(data->nct6694, &cmd_hd, 639 &data->msg->hwmon_alarm); 640 default: 641 return -EOPNOTSUPP; 642 } 643 } 644 645 static int nct6694_fan_write(struct device *dev, u32 attr, int channel, 646 long val) 647 { 648 struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 649 struct nct6694_cmd_header cmd_hd; 650 int ret; 651 652 guard(mutex)(&data->lock); 653 654 switch (attr) { 655 case hwmon_fan_enable: 656 if (val == 0) 657 data->hwmon_en.fin_en[channel / 8] &= ~BIT(channel % 8); 658 else if (val == 1) 659 data->hwmon_en.fin_en[channel / 8] |= BIT(channel % 8); 660 else 661 return -EINVAL; 662 663 cmd_hd = (struct nct6694_cmd_header) { 664 .mod = NCT6694_HWMON_MOD, 665 .cmd = NCT6694_HWMON_CONTROL, 666 .sel = NCT6694_HWMON_CONTROL_SEL, 667 .len = cpu_to_le16(sizeof(data->hwmon_en)) 668 }; 669 670 return nct6694_write_msg(data->nct6694, &cmd_hd, 671 &data->hwmon_en); 672 case hwmon_fan_min: 673 cmd_hd = (struct nct6694_cmd_header) { 674 .mod = NCT6694_HWMON_MOD, 675 .cmd = NCT6694_HWMON_ALARM, 676 .sel = NCT6694_HWMON_ALARM_SEL, 677 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 678 }; 679 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 680 &data->msg->hwmon_alarm); 681 if (ret) 682 return ret; 683 684 val = clamp_val(val, 1, 65535); 685 data->msg->hwmon_alarm.fin_ll[channel] = cpu_to_be16(val); 686 687 return nct6694_write_msg(data->nct6694, &cmd_hd, 688 &data->msg->hwmon_alarm); 689 default: 690 return -EOPNOTSUPP; 691 } 692 } 693 694 static int nct6694_pwm_write(struct device *dev, u32 attr, int channel, 695 long val) 696 { 697 struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 698 struct nct6694_cmd_header cmd_hd; 699 int ret; 700 701 guard(mutex)(&data->lock); 702 703 switch (attr) { 704 case hwmon_pwm_enable: 705 if (val == 0) 706 data->hwmon_en.pwm_en[channel / 8] &= ~BIT(channel % 8); 707 else if (val == 1) 708 data->hwmon_en.pwm_en[channel / 8] |= BIT(channel % 8); 709 else 710 return -EINVAL; 711 712 cmd_hd = (struct nct6694_cmd_header) { 713 .mod = NCT6694_HWMON_MOD, 714 .cmd = NCT6694_HWMON_CONTROL, 715 .sel = NCT6694_HWMON_CONTROL_SEL, 716 .len = cpu_to_le16(sizeof(data->hwmon_en)) 717 }; 718 719 return nct6694_write_msg(data->nct6694, &cmd_hd, 720 &data->hwmon_en); 721 case hwmon_pwm_input: 722 if (val < 0 || val > 255) 723 return -EINVAL; 724 725 cmd_hd = (struct nct6694_cmd_header) { 726 .mod = NCT6694_PWM_MOD, 727 .cmd = NCT6694_PWM_CONTROL, 728 .sel = NCT6694_PWM_CONTROL_SEL, 729 .len = cpu_to_le16(sizeof(data->msg->pwm_ctrl)) 730 }; 731 732 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 733 &data->msg->pwm_ctrl); 734 if (ret) 735 return ret; 736 737 data->msg->pwm_ctrl.mal_val[channel] = val; 738 739 return nct6694_write_msg(data->nct6694, &cmd_hd, 740 &data->msg->pwm_ctrl); 741 case hwmon_pwm_freq: 742 cmd_hd = (struct nct6694_cmd_header) { 743 .mod = NCT6694_HWMON_MOD, 744 .cmd = NCT6694_HWMON_CONTROL, 745 .sel = NCT6694_HWMON_CONTROL_SEL, 746 .len = cpu_to_le16(sizeof(data->hwmon_en)) 747 }; 748 749 data->hwmon_en.pwm_freq[channel] = NCT6694_FREQ_TO_REG(val); 750 751 return nct6694_write_msg(data->nct6694, &cmd_hd, 752 &data->hwmon_en); 753 default: 754 return -EOPNOTSUPP; 755 } 756 } 757 758 static int nct6694_read(struct device *dev, enum hwmon_sensor_types type, 759 u32 attr, int channel, long *val) 760 { 761 switch (type) { 762 case hwmon_in: 763 /* in mV */ 764 return nct6694_in_read(dev, attr, channel, val); 765 case hwmon_temp: 766 /* in mC */ 767 return nct6694_temp_read(dev, attr, channel, val); 768 case hwmon_fan: 769 /* in RPM */ 770 return nct6694_fan_read(dev, attr, channel, val); 771 case hwmon_pwm: 772 /* in value 0~255 */ 773 return nct6694_pwm_read(dev, attr, channel, val); 774 default: 775 return -EOPNOTSUPP; 776 } 777 } 778 779 static int nct6694_write(struct device *dev, enum hwmon_sensor_types type, 780 u32 attr, int channel, long val) 781 { 782 switch (type) { 783 case hwmon_in: 784 return nct6694_in_write(dev, attr, channel, val); 785 case hwmon_temp: 786 return nct6694_temp_write(dev, attr, channel, val); 787 case hwmon_fan: 788 return nct6694_fan_write(dev, attr, channel, val); 789 case hwmon_pwm: 790 return nct6694_pwm_write(dev, attr, channel, val); 791 default: 792 return -EOPNOTSUPP; 793 } 794 } 795 796 static umode_t nct6694_is_visible(const void *data, 797 enum hwmon_sensor_types type, 798 u32 attr, int channel) 799 { 800 switch (type) { 801 case hwmon_in: 802 switch (attr) { 803 case hwmon_in_enable: 804 case hwmon_in_max: 805 case hwmon_in_min: 806 return 0644; 807 case hwmon_in_alarm: 808 case hwmon_in_input: 809 return 0444; 810 default: 811 return 0; 812 } 813 case hwmon_temp: 814 switch (attr) { 815 case hwmon_temp_enable: 816 case hwmon_temp_max: 817 case hwmon_temp_max_hyst: 818 return 0644; 819 case hwmon_temp_input: 820 case hwmon_temp_max_alarm: 821 return 0444; 822 default: 823 return 0; 824 } 825 case hwmon_fan: 826 switch (attr) { 827 case hwmon_fan_enable: 828 case hwmon_fan_min: 829 return 0644; 830 case hwmon_fan_input: 831 case hwmon_fan_min_alarm: 832 return 0444; 833 default: 834 return 0; 835 } 836 case hwmon_pwm: 837 switch (attr) { 838 case hwmon_pwm_enable: 839 case hwmon_pwm_freq: 840 case hwmon_pwm_input: 841 return 0644; 842 default: 843 return 0; 844 } 845 default: 846 return 0; 847 } 848 } 849 850 static const struct hwmon_ops nct6694_hwmon_ops = { 851 .is_visible = nct6694_is_visible, 852 .read = nct6694_read, 853 .write = nct6694_write, 854 }; 855 856 static const struct hwmon_chip_info nct6694_chip_info = { 857 .ops = &nct6694_hwmon_ops, 858 .info = nct6694_info, 859 }; 860 861 static int nct6694_hwmon_init(struct nct6694_hwmon_data *data) 862 { 863 struct nct6694_cmd_header cmd_hd = { 864 .mod = NCT6694_HWMON_MOD, 865 .cmd = NCT6694_HWMON_CONTROL, 866 .sel = NCT6694_HWMON_CONTROL_SEL, 867 .len = cpu_to_le16(sizeof(data->hwmon_en)) 868 }; 869 int ret; 870 871 /* 872 * Record each Hardware Monitor Channel enable status 873 * and PWM frequency register 874 */ 875 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 876 &data->hwmon_en); 877 if (ret) 878 return ret; 879 880 cmd_hd = (struct nct6694_cmd_header) { 881 .mod = NCT6694_HWMON_MOD, 882 .cmd = NCT6694_HWMON_ALARM, 883 .sel = NCT6694_HWMON_ALARM_SEL, 884 .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 885 }; 886 887 /* Select hwmon device alarm mode */ 888 ret = nct6694_read_msg(data->nct6694, &cmd_hd, 889 &data->msg->hwmon_alarm); 890 if (ret) 891 return ret; 892 893 data->msg->hwmon_alarm.smi_ctrl = NCT6694_HWMON_REALTIME_IRQ; 894 895 return nct6694_write_msg(data->nct6694, &cmd_hd, 896 &data->msg->hwmon_alarm); 897 } 898 899 static int nct6694_hwmon_probe(struct platform_device *pdev) 900 { 901 struct nct6694_hwmon_data *data; 902 struct nct6694 *nct6694 = dev_get_drvdata(pdev->dev.parent); 903 struct device *hwmon_dev; 904 int ret; 905 906 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 907 if (!data) 908 return -ENOMEM; 909 910 data->rpt = devm_kzalloc(&pdev->dev, sizeof(union nct6694_hwmon_rpt), 911 GFP_KERNEL); 912 if (!data->rpt) 913 return -ENOMEM; 914 915 data->msg = devm_kzalloc(&pdev->dev, sizeof(union nct6694_hwmon_msg), 916 GFP_KERNEL); 917 if (!data->msg) 918 return -ENOMEM; 919 920 data->nct6694 = nct6694; 921 ret = devm_mutex_init(&pdev->dev, &data->lock); 922 if (ret) 923 return ret; 924 925 ret = nct6694_hwmon_init(data); 926 if (ret) 927 return ret; 928 929 /* Register hwmon device to HWMON framework */ 930 hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, 931 "nct6694", data, 932 &nct6694_chip_info, 933 NULL); 934 return PTR_ERR_OR_ZERO(hwmon_dev); 935 } 936 937 static struct platform_driver nct6694_hwmon_driver = { 938 .driver = { 939 .name = "nct6694-hwmon", 940 }, 941 .probe = nct6694_hwmon_probe, 942 }; 943 944 module_platform_driver(nct6694_hwmon_driver); 945 946 MODULE_DESCRIPTION("USB-HWMON driver for NCT6694"); 947 MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>"); 948 MODULE_LICENSE("GPL"); 949 MODULE_ALIAS("platform:nct6694-hwmon"); 950