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