Message ID | 20231204055650.788388-3-kcfeng0@nuvoton.com (mailing list archive) |
---|---|
State | Changes Requested |
Headers | show |
Series | hwmon: Driver for Nuvoton NCT736X | expand |
On 12/3/23 21:56, baneric926@gmail.com wrote: > From: Ban Feng <kcfeng0@nuvoton.com> > > NCT736X is an I2C based hardware monitoring chip from Nuvoton. > > Signed-off-by: Ban Feng <kcfeng0@nuvoton.com> > --- [ ... ] > + hwmon_dev = devm_hwmon_device_register_with_groups(dev, > + client->name, > + data, data->groups); Please resubmit using devm_hwmon_device_register_with_info(). Drivers using deprecated APIs will not be accepted. Guenter
On 04/12/2023 06:56, baneric926@gmail.com wrote: > From: Ban Feng <kcfeng0@nuvoton.com> > > NCT736X is an I2C based hardware monitoring chip from Nuvoton. > > Signed-off-by: Ban Feng <kcfeng0@nuvoton.com> > --- > + > +static const struct i2c_device_id nct736x_id[] = { > + {"nct7362", nct7362}, > + {"nct7363", nct7363}, > + {} > +}; > +MODULE_DEVICE_TABLE(i2c, nct736x_id); > + All ID tables are next to each other. Move it down. Why does it not match of_device_id? ... > + > +static int nct736x_probe(struct i2c_client *client) > +{ > + struct device *dev = &client->dev; > + struct nct736x_data *data; > + struct device *hwmon_dev; > + u32 pwm_mask, fanin_mask, val, wdt_cfg; > + int ret; > + > + data = devm_kzalloc(dev, sizeof(struct nct736x_data), GFP_KERNEL); sizeof(*) > + if (!data) > + return -ENOMEM; > + > + i2c_set_clientdata(client, data); > + mutex_init(&data->update_lock); > + > + data->client = client; > + > + if (of_property_read_u32(dev->of_node, "nuvoton,pwm-mask", &pwm_mask)) > + pwm_mask = 0; > + if (of_property_read_u32(dev->of_node, > + "nuvoton,fanin-mask", &fanin_mask)) > + fanin_mask = 0; > + if (of_property_read_u32(dev->of_node, "nuvoton,wdt-timeout", &val)) > + wdt_cfg = 0xff; > + else > + wdt_cfg = WDT_CFG(val) | EN_WDT; > + > + /* Initialize the chip */ > + ret = nct736x_init_chip(client, pwm_mask, fanin_mask, wdt_cfg); > + if (ret) > + return ret; > + > + data->fan_mask = (u16)fanin_mask; > + data->pwm_mask = (u16)pwm_mask; > + > + data = nct736x_update_device(dev); > + > + data->groups[0] = &nct736x_group_fan; > + data->groups[1] = &nct736x_group_pwm; > + data->groups[2] = NULL; > + > + hwmon_dev = devm_hwmon_device_register_with_groups(dev, > + client->name, > + data, data->groups); > + return PTR_ERR_OR_ZERO(hwmon_dev); > +} > + > +static const struct of_device_id nct736x_of_match[] = { > + { .compatible = "nuvoton,nct7362" }, > + { .compatible = "nuvoton,nct7363" }, This means your devices are compatible. Express compatibility in your bindings (specific compatible followed by fallback). But then your i2c_device_id is not matching this one here... confusing and clearly wrong. Best regards, Krzysztof
Hi Guenter -----Original Message----- From: Guenter Roeck <groeck7@gmail.com> On Behalf Of Guenter Roeck Sent: Monday, December 4, 2023 3:07 PM To: baneric926@gmail.com; jdelvare@suse.com; robh+dt@kernel.org; krzysztof.kozlowski+dt@linaro.org; conor+dt@kernel.org; corbet@lwn.net Cc: linux-hwmon@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; linux-doc@vger.kernel.org; openbmc@lists.ozlabs.org; CS20 KWLiu <KWLIU@nuvoton.com>; CS20 KCFeng0 <KCFENG0@nuvoton.com>; DELPHINE_CHIU@wiwynn.com; Bonnie_Lo@wiwynn.com Subject: Re: [PATCH v1 2/2] hwmon: Driver for Nuvoton NCT736X CAUTION - External Email: Do not click links or open attachments unless you acknowledge the sender and content. On 12/3/23 21:56, baneric926@gmail.com wrote: > From: Ban Feng <kcfeng0@nuvoton.com> > > NCT736X is an I2C based hardware monitoring chip from Nuvoton. > > Signed-off-by: Ban Feng <kcfeng0@nuvoton.com> > --- [ ... ] > + hwmon_dev = devm_hwmon_device_register_with_groups(dev, > + client->name, > + data, data->groups); Please resubmit using devm_hwmon_device_register_with_info(). Drivers using deprecated APIs will not be accepted. I'll convert to devm_hwmon_device_register_with_info in PATCH v2. Guenter Thanks, Ban
On 05/12/2023 06:02, KCFENG0@nuvoton.com wrote: > Hi Guenter ... > The privileged confidential information contained in this email is intended for use only by the addressees as indicated by the original sender of this email. If you are not the addressee indicated in this email or are not responsible for delivery of the email to such a person, please kindly reply to the sender indicating this fact and delete all copies of it from your computer and network server immediately. As requested: I am going to delete all the copies of your emails. Best regards, Krzysztof
Hi, kernel test robot noticed the following build warnings: [auto build test WARNING on linus/master] [also build test WARNING on v6.7-rc4 next-20231205] [cannot apply to groeck-staging/hwmon-next] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/baneric926-gmail-com/dt-bindings-hwmon-Add-nct736x-bindings/20231204-135942 base: linus/master patch link: https://lore.kernel.org/r/20231204055650.788388-3-kcfeng0%40nuvoton.com patch subject: [PATCH v1 2/2] hwmon: Driver for Nuvoton NCT736X config: i386-allmodconfig (https://download.01.org/0day-ci/archive/20231205/202312051854.qBIoJW1N-lkp@intel.com/config) compiler: clang version 16.0.4 (https://github.com/llvm/llvm-project.git ae42196bc493ffe877a7e3dff8be32035dea4d07) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231205/202312051854.qBIoJW1N-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202312051854.qBIoJW1N-lkp@intel.com/ All warnings (new ones prefixed by >>): >> drivers/hwmon/nct736x.c:352:5: warning: variable 'gpio14_17' is uninitialized when used here [-Wuninitialized] gpio14_17 |= FANIN_SEL(i); ^~~~~~~~~ drivers/hwmon/nct736x.c:339:46: note: initialize the variable 'gpio14_17' to silence this warning u8 i, gpio0_3, gpio4_7, gpio10_13, gpio14_17; ^ = '\0' >> drivers/hwmon/nct736x.c:347:5: warning: variable 'gpio10_13' is uninitialized when used here [-Wuninitialized] gpio10_13 |= FANIN_SEL(i); ^~~~~~~~~ drivers/hwmon/nct736x.c:339:35: note: initialize the variable 'gpio10_13' to silence this warning u8 i, gpio0_3, gpio4_7, gpio10_13, gpio14_17; ^ = '\0' >> drivers/hwmon/nct736x.c:350:5: warning: variable 'gpio4_7' is uninitialized when used here [-Wuninitialized] gpio4_7 |= PWM_SEL(i); ^~~~~~~ drivers/hwmon/nct736x.c:339:24: note: initialize the variable 'gpio4_7' to silence this warning u8 i, gpio0_3, gpio4_7, gpio10_13, gpio14_17; ^ = '\0' >> drivers/hwmon/nct736x.c:345:5: warning: variable 'gpio0_3' is uninitialized when used here [-Wuninitialized] gpio0_3 |= PWM_SEL(i); ^~~~~~~ drivers/hwmon/nct736x.c:339:15: note: initialize the variable 'gpio0_3' to silence this warning u8 i, gpio0_3, gpio4_7, gpio10_13, gpio14_17; ^ = '\0' 4 warnings generated. vim +/gpio14_17 +352 drivers/hwmon/nct736x.c 334 335 static int nct736x_init_chip(struct i2c_client *client, 336 u32 pwm_mask, u32 fanin_mask, u32 wdt_cfg) 337 { 338 const struct i2c_device_id *id = i2c_match_id(nct736x_id, client); 339 u8 i, gpio0_3, gpio4_7, gpio10_13, gpio14_17; 340 int ret; 341 342 for (i = 0; i < NCT736X_PWM_COUNT; i++) { 343 if (i < 4) { 344 if (pwm_mask & BIT_CHECK(i)) > 345 gpio0_3 |= PWM_SEL(i); 346 if (fanin_mask & BIT_CHECK(i)) > 347 gpio10_13 |= FANIN_SEL(i); 348 } else if (i < 8) { 349 if (pwm_mask & BIT_CHECK(i)) > 350 gpio4_7 |= PWM_SEL(i); 351 if (fanin_mask & BIT_CHECK(i)) > 352 gpio14_17 |= FANIN_SEL(i); 353 } else if (i < 12) { 354 if (pwm_mask & BIT_CHECK(i)) 355 gpio10_13 |= PWM_SEL(i); 356 if (fanin_mask & BIT_CHECK(i)) 357 gpio0_3 |= FANIN_SEL(i); 358 } else { 359 if (pwm_mask & BIT_CHECK(i)) 360 gpio14_17 |= PWM_SEL(i); 361 if (fanin_mask & BIT_CHECK(i)) 362 gpio4_7 |= FANIN_SEL(i); 363 } 364 } 365 366 /* Pin Function Configuration */ 367 ret = nct736x_write_reg(client, NCT736X_REG_GPIO_0_3, gpio0_3); 368 if (ret < 0) 369 return ret; 370 ret = nct736x_write_reg(client, NCT736X_REG_GPIO_4_7, gpio4_7); 371 if (ret < 0) 372 return ret; 373 ret = nct736x_write_reg(client, NCT736X_REG_GPIO_10_13, gpio10_13); 374 if (ret < 0) 375 return ret; 376 ret = nct736x_write_reg(client, NCT736X_REG_GPIO_14_17, gpio14_17); 377 if (ret < 0) 378 return ret; 379 380 /* PWM and FANIN Monitoring Enable */ 381 ret = nct736x_write_reg(client, NCT736X_REG_PWMEN_0_7, 382 pwm_mask & 0xff); 383 if (ret < 0) 384 return ret; 385 ret = nct736x_write_reg(client, 386 NCT736X_REG_PWMEN_8_15, (pwm_mask >> 8) & 0xff); 387 if (ret < 0) 388 return ret; 389 ret = nct736x_write_reg(client, NCT736X_REG_FANINEN_0_7, 390 fanin_mask & 0xff); 391 if (ret < 0) 392 return ret; 393 ret = nct736x_write_reg(client, NCT736X_REG_FANINEN_8_15, 394 (fanin_mask >> 8) & 0xff); 395 if (ret < 0) 396 return ret; 397 398 /* Watchdog Timer Configuration */ 399 if (wdt_cfg != 0xff && id->driver_data == nct7363) { 400 ret = nct736x_write_reg(client, NCT7363_REG_WDT, wdt_cfg); 401 if (ret < 0) 402 return ret; 403 } 404 405 return 0; 406 } 407
Hi, kernel test robot noticed the following build warnings: [auto build test WARNING on linus/master] [also build test WARNING on v6.7-rc4 next-20231205] [cannot apply to groeck-staging/hwmon-next] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/baneric926-gmail-com/dt-bindings-hwmon-Add-nct736x-bindings/20231204-135942 base: linus/master patch link: https://lore.kernel.org/r/20231204055650.788388-3-kcfeng0%40nuvoton.com patch subject: [PATCH v1 2/2] hwmon: Driver for Nuvoton NCT736X config: um-randconfig-001-20231205 (https://download.01.org/0day-ci/archive/20231205/202312052243.AbRqbNyT-lkp@intel.com/config) compiler: gcc-11 (Debian 11.3.0-12) 11.3.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231205/202312052243.AbRqbNyT-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202312052243.AbRqbNyT-lkp@intel.com/ All warnings (new ones prefixed by >>): drivers/hwmon/nct736x.c: In function 'nct736x_init_chip.constprop': >> drivers/hwmon/nct736x.c:367:15: warning: 'gpio0_3' is used uninitialized [-Wuninitialized] 367 | ret = nct736x_write_reg(client, NCT736X_REG_GPIO_0_3, gpio0_3); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ vim +/gpio0_3 +367 drivers/hwmon/nct736x.c 334 335 static int nct736x_init_chip(struct i2c_client *client, 336 u32 pwm_mask, u32 fanin_mask, u32 wdt_cfg) 337 { 338 const struct i2c_device_id *id = i2c_match_id(nct736x_id, client); 339 u8 i, gpio0_3, gpio4_7, gpio10_13, gpio14_17; 340 int ret; 341 342 for (i = 0; i < NCT736X_PWM_COUNT; i++) { 343 if (i < 4) { 344 if (pwm_mask & BIT_CHECK(i)) 345 gpio0_3 |= PWM_SEL(i); 346 if (fanin_mask & BIT_CHECK(i)) 347 gpio10_13 |= FANIN_SEL(i); 348 } else if (i < 8) { 349 if (pwm_mask & BIT_CHECK(i)) 350 gpio4_7 |= PWM_SEL(i); 351 if (fanin_mask & BIT_CHECK(i)) 352 gpio14_17 |= FANIN_SEL(i); 353 } else if (i < 12) { 354 if (pwm_mask & BIT_CHECK(i)) 355 gpio10_13 |= PWM_SEL(i); 356 if (fanin_mask & BIT_CHECK(i)) 357 gpio0_3 |= FANIN_SEL(i); 358 } else { 359 if (pwm_mask & BIT_CHECK(i)) 360 gpio14_17 |= PWM_SEL(i); 361 if (fanin_mask & BIT_CHECK(i)) 362 gpio4_7 |= FANIN_SEL(i); 363 } 364 } 365 366 /* Pin Function Configuration */ > 367 ret = nct736x_write_reg(client, NCT736X_REG_GPIO_0_3, gpio0_3); 368 if (ret < 0) 369 return ret; 370 ret = nct736x_write_reg(client, NCT736X_REG_GPIO_4_7, gpio4_7); 371 if (ret < 0) 372 return ret; 373 ret = nct736x_write_reg(client, NCT736X_REG_GPIO_10_13, gpio10_13); 374 if (ret < 0) 375 return ret; 376 ret = nct736x_write_reg(client, NCT736X_REG_GPIO_14_17, gpio14_17); 377 if (ret < 0) 378 return ret; 379 380 /* PWM and FANIN Monitoring Enable */ 381 ret = nct736x_write_reg(client, NCT736X_REG_PWMEN_0_7, 382 pwm_mask & 0xff); 383 if (ret < 0) 384 return ret; 385 ret = nct736x_write_reg(client, 386 NCT736X_REG_PWMEN_8_15, (pwm_mask >> 8) & 0xff); 387 if (ret < 0) 388 return ret; 389 ret = nct736x_write_reg(client, NCT736X_REG_FANINEN_0_7, 390 fanin_mask & 0xff); 391 if (ret < 0) 392 return ret; 393 ret = nct736x_write_reg(client, NCT736X_REG_FANINEN_8_15, 394 (fanin_mask >> 8) & 0xff); 395 if (ret < 0) 396 return ret; 397 398 /* Watchdog Timer Configuration */ 399 if (wdt_cfg != 0xff && id->driver_data == nct7363) { 400 ret = nct736x_write_reg(client, NCT7363_REG_WDT, wdt_cfg); 401 if (ret < 0) 402 return ret; 403 } 404 405 return 0; 406 } 407
Hi Krzysztof, Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> 於 2023年12月4日 週一 下午4:06寫道: > > On 04/12/2023 06:56, baneric926@gmail.com wrote: > > From: Ban Feng <kcfeng0@nuvoton.com> > > > > NCT736X is an I2C based hardware monitoring chip from Nuvoton. > > > > Signed-off-by: Ban Feng <kcfeng0@nuvoton.com> > > --- > > > > + > > +static const struct i2c_device_id nct736x_id[] = { > > + {"nct7362", nct7362}, > > + {"nct7363", nct7363}, > > + {} > > +}; > > +MODULE_DEVICE_TABLE(i2c, nct736x_id); > > + > > All ID tables are next to each other. Move it down. Why does it not > match of_device_id? ok, I'll put all ID tables together, and add .data to of_device_id so that matching i2c_device_id. > > ... > > > + > > +static int nct736x_probe(struct i2c_client *client) > > +{ > > + struct device *dev = &client->dev; > > + struct nct736x_data *data; > > + struct device *hwmon_dev; > > + u32 pwm_mask, fanin_mask, val, wdt_cfg; > > + int ret; > > + > > + data = devm_kzalloc(dev, sizeof(struct nct736x_data), GFP_KERNEL); > > sizeof(*) ok, I'll modify it in v2. > > > + if (!data) > > + return -ENOMEM; > > + > > + i2c_set_clientdata(client, data); > > + mutex_init(&data->update_lock); > > + > > + data->client = client; > > + > > + if (of_property_read_u32(dev->of_node, "nuvoton,pwm-mask", &pwm_mask)) > > + pwm_mask = 0; > > + if (of_property_read_u32(dev->of_node, > > + "nuvoton,fanin-mask", &fanin_mask)) > > + fanin_mask = 0; > > + if (of_property_read_u32(dev->of_node, "nuvoton,wdt-timeout", &val)) > > + wdt_cfg = 0xff; > > + else > > + wdt_cfg = WDT_CFG(val) | EN_WDT; > > + > > + /* Initialize the chip */ > > + ret = nct736x_init_chip(client, pwm_mask, fanin_mask, wdt_cfg); > > + if (ret) > > + return ret; > > + > > + data->fan_mask = (u16)fanin_mask; > > + data->pwm_mask = (u16)pwm_mask; > > + > > + data = nct736x_update_device(dev); > > + > > + data->groups[0] = &nct736x_group_fan; > > + data->groups[1] = &nct736x_group_pwm; > > + data->groups[2] = NULL; > > + > > + hwmon_dev = devm_hwmon_device_register_with_groups(dev, > > + client->name, > > + data, data->groups); > > + return PTR_ERR_OR_ZERO(hwmon_dev); > > +} > > + > > +static const struct of_device_id nct736x_of_match[] = { > > + { .compatible = "nuvoton,nct7362" }, > > + { .compatible = "nuvoton,nct7363" }, > > This means your devices are compatible. Express compatibility in your > bindings (specific compatible followed by fallback). But then your > i2c_device_id is not matching this one here... confusing and clearly wrong. > Same as above. > Best regards, > Krzysztof >
Hi, kernel test robot noticed the following build warnings: https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/baneric926-gmail-com/dt-bindings-hwmon-Add-nct736x-bindings/20231204-135942 base: linus/master patch link: https://lore.kernel.org/r/20231204055650.788388-3-kcfeng0%40nuvoton.com patch subject: [PATCH v1 2/2] hwmon: Driver for Nuvoton NCT736X config: m68k-randconfig-r071-20231207 (https://download.01.org/0day-ci/archive/20231207/202312071152.Kfcw1KlD-lkp@intel.com/config) compiler: m68k-linux-gcc (GCC) 13.2.0 reproduce: (https://download.01.org/0day-ci/archive/20231207/202312071152.Kfcw1KlD-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Reported-by: Dan Carpenter <dan.carpenter@linaro.org> | Closes: https://lore.kernel.org/r/202312071152.Kfcw1KlD-lkp@intel.com/ smatch warnings: drivers/hwmon/nct736x.c:367 nct736x_init_chip() error: uninitialized symbol 'gpio0_3'. drivers/hwmon/nct736x.c:370 nct736x_init_chip() error: uninitialized symbol 'gpio4_7'. drivers/hwmon/nct736x.c:373 nct736x_init_chip() error: uninitialized symbol 'gpio10_13'. drivers/hwmon/nct736x.c:376 nct736x_init_chip() error: uninitialized symbol 'gpio14_17'. vim +/gpio0_3 +367 drivers/hwmon/nct736x.c 16e62bcf3c9b93 Ban Feng 2023-12-04 335 static int nct736x_init_chip(struct i2c_client *client, 16e62bcf3c9b93 Ban Feng 2023-12-04 336 u32 pwm_mask, u32 fanin_mask, u32 wdt_cfg) 16e62bcf3c9b93 Ban Feng 2023-12-04 337 { 16e62bcf3c9b93 Ban Feng 2023-12-04 338 const struct i2c_device_id *id = i2c_match_id(nct736x_id, client); 16e62bcf3c9b93 Ban Feng 2023-12-04 339 u8 i, gpio0_3, gpio4_7, gpio10_13, gpio14_17; 16e62bcf3c9b93 Ban Feng 2023-12-04 340 int ret; 16e62bcf3c9b93 Ban Feng 2023-12-04 341 16e62bcf3c9b93 Ban Feng 2023-12-04 342 for (i = 0; i < NCT736X_PWM_COUNT; i++) { 16e62bcf3c9b93 Ban Feng 2023-12-04 343 if (i < 4) { 16e62bcf3c9b93 Ban Feng 2023-12-04 344 if (pwm_mask & BIT_CHECK(i)) 16e62bcf3c9b93 Ban Feng 2023-12-04 345 gpio0_3 |= PWM_SEL(i); This doesn't work. gpio0_3 needs to be initialized to zero before we can turn on individual bits. 16e62bcf3c9b93 Ban Feng 2023-12-04 346 if (fanin_mask & BIT_CHECK(i)) 16e62bcf3c9b93 Ban Feng 2023-12-04 347 gpio10_13 |= FANIN_SEL(i); Etc... 16e62bcf3c9b93 Ban Feng 2023-12-04 348 } else if (i < 8) { 16e62bcf3c9b93 Ban Feng 2023-12-04 349 if (pwm_mask & BIT_CHECK(i)) 16e62bcf3c9b93 Ban Feng 2023-12-04 350 gpio4_7 |= PWM_SEL(i); 16e62bcf3c9b93 Ban Feng 2023-12-04 351 if (fanin_mask & BIT_CHECK(i)) 16e62bcf3c9b93 Ban Feng 2023-12-04 352 gpio14_17 |= FANIN_SEL(i); 16e62bcf3c9b93 Ban Feng 2023-12-04 353 } else if (i < 12) { 16e62bcf3c9b93 Ban Feng 2023-12-04 354 if (pwm_mask & BIT_CHECK(i)) 16e62bcf3c9b93 Ban Feng 2023-12-04 355 gpio10_13 |= PWM_SEL(i); 16e62bcf3c9b93 Ban Feng 2023-12-04 356 if (fanin_mask & BIT_CHECK(i)) 16e62bcf3c9b93 Ban Feng 2023-12-04 357 gpio0_3 |= FANIN_SEL(i); 16e62bcf3c9b93 Ban Feng 2023-12-04 358 } else { 16e62bcf3c9b93 Ban Feng 2023-12-04 359 if (pwm_mask & BIT_CHECK(i)) 16e62bcf3c9b93 Ban Feng 2023-12-04 360 gpio14_17 |= PWM_SEL(i); 16e62bcf3c9b93 Ban Feng 2023-12-04 361 if (fanin_mask & BIT_CHECK(i)) 16e62bcf3c9b93 Ban Feng 2023-12-04 362 gpio4_7 |= FANIN_SEL(i); 16e62bcf3c9b93 Ban Feng 2023-12-04 363 } 16e62bcf3c9b93 Ban Feng 2023-12-04 364 } 16e62bcf3c9b93 Ban Feng 2023-12-04 365 16e62bcf3c9b93 Ban Feng 2023-12-04 366 /* Pin Function Configuration */ 16e62bcf3c9b93 Ban Feng 2023-12-04 @367 ret = nct736x_write_reg(client, NCT736X_REG_GPIO_0_3, gpio0_3); ^^^^^^^ 16e62bcf3c9b93 Ban Feng 2023-12-04 368 if (ret < 0) 16e62bcf3c9b93 Ban Feng 2023-12-04 369 return ret; 16e62bcf3c9b93 Ban Feng 2023-12-04 @370 ret = nct736x_write_reg(client, NCT736X_REG_GPIO_4_7, gpio4_7); 16e62bcf3c9b93 Ban Feng 2023-12-04 371 if (ret < 0) 16e62bcf3c9b93 Ban Feng 2023-12-04 372 return ret; 16e62bcf3c9b93 Ban Feng 2023-12-04 @373 ret = nct736x_write_reg(client, NCT736X_REG_GPIO_10_13, gpio10_13); 16e62bcf3c9b93 Ban Feng 2023-12-04 374 if (ret < 0) 16e62bcf3c9b93 Ban Feng 2023-12-04 375 return ret; 16e62bcf3c9b93 Ban Feng 2023-12-04 @376 ret = nct736x_write_reg(client, NCT736X_REG_GPIO_14_17, gpio14_17); 16e62bcf3c9b93 Ban Feng 2023-12-04 377 if (ret < 0) 16e62bcf3c9b93 Ban Feng 2023-12-04 378 return ret; 16e62bcf3c9b93 Ban Feng 2023-12-04 379 16e62bcf3c9b93 Ban Feng 2023-12-04 380 /* PWM and FANIN Monitoring Enable */
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 72f4e6065bae..98eb0efeb121 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -161,6 +161,7 @@ Hardware Monitoring Kernel Drivers mp5023 nct6683 nct6775 + nct736x nct7802 nct7904 npcm750-pwm-fan diff --git a/Documentation/hwmon/nct736x.rst b/Documentation/hwmon/nct736x.rst new file mode 100644 index 000000000000..b7d64087263a --- /dev/null +++ b/Documentation/hwmon/nct736x.rst @@ -0,0 +1,35 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver nct736x +===================== + +Supported chip: + + * Nuvoton NCT7362Y NCT7363Y + + Prefix: nct736x + + Addresses: I2C 0x20, 0x21, 0x22, 0x23 + +Author: Ban Feng <kcfeng0@nuvoton.com> + + +Description +----------- + +The NCT736X is a Fan controller which provides up to 16 independent +FAN input monitors, and up to 16 independent PWM output with SMBus interface. +Besides, NCT7363Y has a built-in watchdog timer which is used for +conditionally generating a system reset output (INT#). + + +Sysfs entries +------------- + +Currently, the driver supports the following features: + +======================= ======================================================= +fanX_input provide current fan rotation value in RPM + +pwmX get or set PWM fan control value. +======================= ======================================================= diff --git a/MAINTAINERS b/MAINTAINERS index eef44c13278c..fed10a0bc49c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14905,6 +14905,8 @@ M: Ban Feng <kcfeng0@nuvoton.com> L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/hwmon/nuvoton,nct736x.yaml +F: Documentation/hwmon/nct736x.rst +F: drivers/hwmon/nct736x.c NETDEVSIM M: Jakub Kicinski <kuba@kernel.org> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index cf27523eed5a..b2ca07e6c03a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1605,6 +1605,16 @@ config SENSORS_NCT6775_I2C This driver can also be built as a module. If so, the module will be called nct6775-i2c. +config SENSORS_NCT736X + tristate "Nuvoton NCT736X" + depends on I2C + help + If you say yes here you get support for the Nuvoton NCT7362Y, + NCT7363Y hardware monitoring chip. + + This driver can also be built as a module. If so, the module + will be called nct736x. + config SENSORS_NCT7802 tristate "Nuvoton NCT7802Y" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index e84bd9685b5c..13f378452a4b 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -166,6 +166,7 @@ obj-$(CONFIG_SENSORS_NCT6775_CORE) += nct6775-core.o nct6775-objs := nct6775-platform.o obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o obj-$(CONFIG_SENSORS_NCT6775_I2C) += nct6775-i2c.o +obj-$(CONFIG_SENSORS_NCT736X) += nct736x.o obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o obj-$(CONFIG_SENSORS_NCT7904) += nct7904.o obj-$(CONFIG_SENSORS_NPCM7XX) += npcm750-pwm-fan.o diff --git a/drivers/hwmon/nct736x.c b/drivers/hwmon/nct736x.c new file mode 100644 index 000000000000..4c59e823062d --- /dev/null +++ b/drivers/hwmon/nct736x.c @@ -0,0 +1,479 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Nuvoton Technology corporation. + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/i2c.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> + +#define NCT736X_REG_GPIO_0_3 0x20 +#define NCT736X_REG_GPIO_4_7 0x21 +#define NCT736X_REG_GPIO_10_13 0x22 +#define NCT736X_REG_GPIO_14_17 0x23 +#define NCT7363_REG_WDT 0x2A +#define WDT_CFG_MASK GENMASK(3, 2) +#define WDT_CFG(x) FIELD_PREP(WDT_CFG_MASK, (x)) +#define EN_WDT BIT(7) +#define NCT736X_REG_PWMEN_0_7 0x38 +#define NCT736X_REG_PWMEN_8_15 0x39 +#define NCT736X_REG_FANINEN_0_7 0x41 +#define NCT736X_REG_FANINEN_8_15 0x42 +#define NCT736X_REG_FANINx_HVAL(x) (0x48 + ((x) * 2)) +#define NCT736X_REG_FANINx_LVAL(x) (0x49 + ((x) * 2)) +#define NCT736X_REG_FSCPxDUTY(x) (0x90 + ((x) * 2)) +#define NCT736X_REG_VENDOR_ID 0xFD +#define NCT736X_REG_CHIP_ID 0xFE +#define NCT736X_REG_DEVICE_ID 0xFF + +#define NUVOTON_ID 0x49 +#define CHIP_ID 0x19 +#define DEVICE_ID 0x88 + +#define PWM_SEL(x) (BIT(0) << ((x % 4) * 2)) +#define FANIN_SEL(x) (BIT(1) << ((x % 4) * 2)) +#define BIT_CHECK(x) (BIT(0) << x) + +#define NCT736X_FANINx_LVAL_MASK GENMASK(4, 0) +#define NCT736X_FANIN_MASK GENMASK(12, 0) + +#define NCT736X_PWM_COUNT 16 +#define NCT736X_FANIN_COUNT 16 + +#define REFRESH_INTERVAL (2 * HZ) + +static inline unsigned long FAN_FROM_REG(u16 val) +{ + if ((val >= NCT736X_FANIN_MASK) || (val == 0)) + return 0; + + return (1350000UL / val); +} + +static const unsigned short normal_i2c[] = { + 0x20, 0x21, 0x22, 0x23, I2C_CLIENT_END +}; + +enum chips { nct7362, nct7363 }; + +struct nct736x_data { + struct i2c_client *client; + const struct attribute_group *groups[3]; + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* In jiffies */ + + u16 fan_mask; + u16 fan[NCT736X_FANIN_COUNT]; + u16 pwm_mask; + u8 pwm[NCT736X_PWM_COUNT]; +}; + +/* Read 1-byte register. Returns unsigned reg or -ERRNO on error. */ +static int nct736x_read_reg(struct i2c_client *client, unsigned int reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + return ret; +} + +/* Write 1-byte register. Returns 0 or -ERRNO on error. */ +static int nct736x_write_reg(struct i2c_client *client, + unsigned int reg, u8 value) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, reg, value); + return ret; +} + +static struct nct736x_data *nct736x_update_device(struct device *dev) +{ + struct nct736x_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int i; + + mutex_lock(&data->update_lock); + + if (!(time_after(jiffies, data->last_updated + REFRESH_INTERVAL) + || !data->valid)) + goto no_sensor_update; + + for (i = 0; i < ARRAY_SIZE(data->fan); i++) { + if (!(data->fan_mask & BIT_CHECK(i))) + continue; + + data->fan[i] = ((u16)nct736x_read_reg(client, + NCT736X_REG_FANINx_HVAL(i))) << 5; + data->fan[i] |= nct736x_read_reg(client, + NCT736X_REG_FANINx_LVAL(i)) & NCT736X_FANINx_LVAL_MASK; + } + + for (i = 0; i < ARRAY_SIZE(data->pwm); i++) { + if (!(data->pwm_mask & BIT_CHECK(i))) + continue; + + data->pwm[i] = nct736x_read_reg(client, + NCT736X_REG_FSCPxDUTY(i)); + } + + data->last_updated = jiffies; + data->valid = true; + +no_sensor_update: + mutex_unlock(&data->update_lock); + return data; +} + +static ssize_t +fan_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct736x_data *data = nct736x_update_device(dev); + u16 val; + + val = data->fan[sattr->index] & NCT736X_FANIN_MASK; + + return sprintf(buf, "%lu\n", FAN_FROM_REG(val)); +} + +static ssize_t +pwm_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct736x_data *data = nct736x_update_device(dev); + u16 val; + + val = data->pwm[sattr->index]; + + return sprintf(buf, "%u\n", (val)); +} + +static ssize_t +pwm_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct736x_data *data = nct736x_update_device(dev); + struct i2c_client *client = data->client; + unsigned long tmpVal; + int err; + + err = kstrtoul(buf, 10, &tmpVal); + if (err) + return err; + + tmpVal = clamp_val(tmpVal, 0, 255); + + mutex_lock(&data->update_lock); + err = nct736x_write_reg(client, NCT736X_REG_FSCPxDUTY(sattr->index), + tmpVal); + if (err) + goto abort; + + data->pwm[sattr->index] = tmpVal; + +abort: + mutex_unlock(&data->update_lock); + return count; +} + +static umode_t nct736x_fan_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct736x_data *data = dev_get_drvdata(dev); + + if (!(data->fan_mask & BIT_CHECK(index))) + return 0; + + return attr->mode; +} + +static umode_t nct736x_pwm_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct736x_data *data = dev_get_drvdata(dev); + + if (!(data->pwm_mask & BIT_CHECK(index))) + return 0; + + return attr->mode; +} + +#define FAN_INPUT 0 + +static SENSOR_DEVICE_ATTR_2_RO(fan1_input, fan, FAN_INPUT, 0); +static SENSOR_DEVICE_ATTR_2_RO(fan2_input, fan, FAN_INPUT, 1); +static SENSOR_DEVICE_ATTR_2_RO(fan3_input, fan, FAN_INPUT, 2); +static SENSOR_DEVICE_ATTR_2_RO(fan4_input, fan, FAN_INPUT, 3); +static SENSOR_DEVICE_ATTR_2_RO(fan5_input, fan, FAN_INPUT, 4); +static SENSOR_DEVICE_ATTR_2_RO(fan6_input, fan, FAN_INPUT, 5); +static SENSOR_DEVICE_ATTR_2_RO(fan7_input, fan, FAN_INPUT, 6); +static SENSOR_DEVICE_ATTR_2_RO(fan8_input, fan, FAN_INPUT, 7); +static SENSOR_DEVICE_ATTR_2_RO(fan9_input, fan, FAN_INPUT, 8); +static SENSOR_DEVICE_ATTR_2_RO(fan10_input, fan, FAN_INPUT, 9); +static SENSOR_DEVICE_ATTR_2_RO(fan11_input, fan, FAN_INPUT, 10); +static SENSOR_DEVICE_ATTR_2_RO(fan12_input, fan, FAN_INPUT, 11); +static SENSOR_DEVICE_ATTR_2_RO(fan13_input, fan, FAN_INPUT, 12); +static SENSOR_DEVICE_ATTR_2_RO(fan14_input, fan, FAN_INPUT, 13); +static SENSOR_DEVICE_ATTR_2_RO(fan15_input, fan, FAN_INPUT, 14); +static SENSOR_DEVICE_ATTR_2_RO(fan16_input, fan, FAN_INPUT, 15); + +static struct attribute *nct736x_attributes_fan[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan5_input.dev_attr.attr, + &sensor_dev_attr_fan6_input.dev_attr.attr, + &sensor_dev_attr_fan7_input.dev_attr.attr, + &sensor_dev_attr_fan8_input.dev_attr.attr, + &sensor_dev_attr_fan9_input.dev_attr.attr, + &sensor_dev_attr_fan10_input.dev_attr.attr, + &sensor_dev_attr_fan11_input.dev_attr.attr, + &sensor_dev_attr_fan12_input.dev_attr.attr, + &sensor_dev_attr_fan13_input.dev_attr.attr, + &sensor_dev_attr_fan14_input.dev_attr.attr, + &sensor_dev_attr_fan15_input.dev_attr.attr, + &sensor_dev_attr_fan16_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group nct736x_group_fan = { + .attrs = nct736x_attributes_fan, + .is_visible = nct736x_fan_is_visible, +}; + +#define PWM_OUTPUT 0 + +static SENSOR_DEVICE_ATTR_2_RW(pwm1, pwm, PWM_OUTPUT, 0); +static SENSOR_DEVICE_ATTR_2_RW(pwm2, pwm, PWM_OUTPUT, 1); +static SENSOR_DEVICE_ATTR_2_RW(pwm3, pwm, PWM_OUTPUT, 2); +static SENSOR_DEVICE_ATTR_2_RW(pwm4, pwm, PWM_OUTPUT, 3); +static SENSOR_DEVICE_ATTR_2_RW(pwm5, pwm, PWM_OUTPUT, 4); +static SENSOR_DEVICE_ATTR_2_RW(pwm6, pwm, PWM_OUTPUT, 5); +static SENSOR_DEVICE_ATTR_2_RW(pwm7, pwm, PWM_OUTPUT, 6); +static SENSOR_DEVICE_ATTR_2_RW(pwm8, pwm, PWM_OUTPUT, 7); +static SENSOR_DEVICE_ATTR_2_RW(pwm9, pwm, PWM_OUTPUT, 8); +static SENSOR_DEVICE_ATTR_2_RW(pwm10, pwm, PWM_OUTPUT, 9); +static SENSOR_DEVICE_ATTR_2_RW(pwm11, pwm, PWM_OUTPUT, 10); +static SENSOR_DEVICE_ATTR_2_RW(pwm12, pwm, PWM_OUTPUT, 11); +static SENSOR_DEVICE_ATTR_2_RW(pwm13, pwm, PWM_OUTPUT, 12); +static SENSOR_DEVICE_ATTR_2_RW(pwm14, pwm, PWM_OUTPUT, 13); +static SENSOR_DEVICE_ATTR_2_RW(pwm15, pwm, PWM_OUTPUT, 14); +static SENSOR_DEVICE_ATTR_2_RW(pwm16, pwm, PWM_OUTPUT, 15); + +static struct attribute *nct736x_attributes_pwm[] = { + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, + &sensor_dev_attr_pwm4.dev_attr.attr, + &sensor_dev_attr_pwm5.dev_attr.attr, + &sensor_dev_attr_pwm6.dev_attr.attr, + &sensor_dev_attr_pwm7.dev_attr.attr, + &sensor_dev_attr_pwm8.dev_attr.attr, + &sensor_dev_attr_pwm9.dev_attr.attr, + &sensor_dev_attr_pwm10.dev_attr.attr, + &sensor_dev_attr_pwm11.dev_attr.attr, + &sensor_dev_attr_pwm12.dev_attr.attr, + &sensor_dev_attr_pwm13.dev_attr.attr, + &sensor_dev_attr_pwm14.dev_attr.attr, + &sensor_dev_attr_pwm15.dev_attr.attr, + &sensor_dev_attr_pwm16.dev_attr.attr, + NULL +}; + +static const struct attribute_group nct736x_group_pwm = { + .attrs = nct736x_attributes_pwm, + .is_visible = nct736x_pwm_is_visible, +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int nct736x_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + int vendor, chip, device; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + vendor = i2c_smbus_read_byte_data(client, NCT736X_REG_VENDOR_ID); + if (vendor != NUVOTON_ID) + return -ENODEV; + + chip = i2c_smbus_read_byte_data(client, NCT736X_REG_CHIP_ID); + if (chip != CHIP_ID) + return -ENODEV; + + device = i2c_smbus_read_byte_data(client, NCT736X_REG_DEVICE_ID); + if (device != DEVICE_ID) + return -ENODEV; + + strscpy(info->type, "nct736x", I2C_NAME_SIZE); + + return 0; +} + +static const struct i2c_device_id nct736x_id[] = { + {"nct7362", nct7362}, + {"nct7363", nct7363}, + {} +}; +MODULE_DEVICE_TABLE(i2c, nct736x_id); + +static int nct736x_init_chip(struct i2c_client *client, + u32 pwm_mask, u32 fanin_mask, u32 wdt_cfg) +{ + const struct i2c_device_id *id = i2c_match_id(nct736x_id, client); + u8 i, gpio0_3, gpio4_7, gpio10_13, gpio14_17; + int ret; + + for (i = 0; i < NCT736X_PWM_COUNT; i++) { + if (i < 4) { + if (pwm_mask & BIT_CHECK(i)) + gpio0_3 |= PWM_SEL(i); + if (fanin_mask & BIT_CHECK(i)) + gpio10_13 |= FANIN_SEL(i); + } else if (i < 8) { + if (pwm_mask & BIT_CHECK(i)) + gpio4_7 |= PWM_SEL(i); + if (fanin_mask & BIT_CHECK(i)) + gpio14_17 |= FANIN_SEL(i); + } else if (i < 12) { + if (pwm_mask & BIT_CHECK(i)) + gpio10_13 |= PWM_SEL(i); + if (fanin_mask & BIT_CHECK(i)) + gpio0_3 |= FANIN_SEL(i); + } else { + if (pwm_mask & BIT_CHECK(i)) + gpio14_17 |= PWM_SEL(i); + if (fanin_mask & BIT_CHECK(i)) + gpio4_7 |= FANIN_SEL(i); + } + } + + /* Pin Function Configuration */ + ret = nct736x_write_reg(client, NCT736X_REG_GPIO_0_3, gpio0_3); + if (ret < 0) + return ret; + ret = nct736x_write_reg(client, NCT736X_REG_GPIO_4_7, gpio4_7); + if (ret < 0) + return ret; + ret = nct736x_write_reg(client, NCT736X_REG_GPIO_10_13, gpio10_13); + if (ret < 0) + return ret; + ret = nct736x_write_reg(client, NCT736X_REG_GPIO_14_17, gpio14_17); + if (ret < 0) + return ret; + + /* PWM and FANIN Monitoring Enable */ + ret = nct736x_write_reg(client, NCT736X_REG_PWMEN_0_7, + pwm_mask & 0xff); + if (ret < 0) + return ret; + ret = nct736x_write_reg(client, + NCT736X_REG_PWMEN_8_15, (pwm_mask >> 8) & 0xff); + if (ret < 0) + return ret; + ret = nct736x_write_reg(client, NCT736X_REG_FANINEN_0_7, + fanin_mask & 0xff); + if (ret < 0) + return ret; + ret = nct736x_write_reg(client, NCT736X_REG_FANINEN_8_15, + (fanin_mask >> 8) & 0xff); + if (ret < 0) + return ret; + + /* Watchdog Timer Configuration */ + if (wdt_cfg != 0xff && id->driver_data == nct7363) { + ret = nct736x_write_reg(client, NCT7363_REG_WDT, wdt_cfg); + if (ret < 0) + return ret; + } + + return 0; +} + +static int nct736x_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct nct736x_data *data; + struct device *hwmon_dev; + u32 pwm_mask, fanin_mask, val, wdt_cfg; + int ret; + + data = devm_kzalloc(dev, sizeof(struct nct736x_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + data->client = client; + + if (of_property_read_u32(dev->of_node, "nuvoton,pwm-mask", &pwm_mask)) + pwm_mask = 0; + if (of_property_read_u32(dev->of_node, + "nuvoton,fanin-mask", &fanin_mask)) + fanin_mask = 0; + if (of_property_read_u32(dev->of_node, "nuvoton,wdt-timeout", &val)) + wdt_cfg = 0xff; + else + wdt_cfg = WDT_CFG(val) | EN_WDT; + + /* Initialize the chip */ + ret = nct736x_init_chip(client, pwm_mask, fanin_mask, wdt_cfg); + if (ret) + return ret; + + data->fan_mask = (u16)fanin_mask; + data->pwm_mask = (u16)pwm_mask; + + data = nct736x_update_device(dev); + + data->groups[0] = &nct736x_group_fan; + data->groups[1] = &nct736x_group_pwm; + data->groups[2] = NULL; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, + client->name, + data, data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id nct736x_of_match[] = { + { .compatible = "nuvoton,nct7362" }, + { .compatible = "nuvoton,nct7363" }, + { }, +}; +MODULE_DEVICE_TABLE(of, nct736x_of_match); + +static struct i2c_driver nct736x_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "nct736x", + .of_match_table = nct736x_of_match, + }, + .probe = nct736x_probe, + .id_table = nct736x_id, + .detect = nct736x_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(nct736x_driver); + +MODULE_AUTHOR("CWHo <cwho@nuvoton.com>"); +MODULE_AUTHOR("Ban Feng <kcfeng0@nuvoton.com>"); +MODULE_DESCRIPTION("NCT736X Hardware Monitoring Driver"); +MODULE_LICENSE("GPL");