From: Anton Lundin <glance@ac2.se>
To: linux-hwmon@vger.kernel.org
Cc: Guenter Roeck <linux@roeck-us.net>,
Zev Weiss <zev@bewilderbeest.net>, Anton Lundin <glance@ac2.se>
Subject: [PATCH] hwmon: (nct6775) Add support for NCT5525
Date: Tue, 3 Nov 2026 14:29:48 +0100 [thread overview]
Message-ID: <20261103132948.144216-1-glance@ac2.se> (raw)
Extend nct6775 driver to support NCT5525.
Assisted-by: Copilot:GPT-5.6-Terra
Signed-off-by: Anton Lundin <glance@ac2.se>
---
.../bindings/hwmon/nuvoton,nct6775.yaml | 1 +
Documentation/hwmon/nct6775.rst | 10 +-
drivers/hwmon/nct6775-core.c | 130 +++++++++++++++++-
drivers/hwmon/nct6775-i2c.c | 6 +-
drivers/hwmon/nct6775-platform.c | 12 ++
drivers/hwmon/nct6775.h | 3 +-
6 files changed, 156 insertions(+), 6 deletions(-)
diff --git a/Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml b/Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml
index 244470282890..16c21f1f46ef 100644
--- a/Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml
+++ b/Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml
@@ -25,6 +25,7 @@ properties:
- nuvoton,nct6797
- nuvoton,nct6798
- nuvoton,nct6799
+ - nuvoton,nct5525
reg:
maxItems: 1
diff --git a/Documentation/hwmon/nct6775.rst b/Documentation/hwmon/nct6775.rst
index d2dedade0606..3e2992a1e24f 100644
--- a/Documentation/hwmon/nct6775.rst
+++ b/Documentation/hwmon/nct6775.rst
@@ -48,6 +48,14 @@ Supported chips:
Datasheet: Available from the Nuvoton web site
+ * Nuvoton NCT5525D
+
+ Prefix: 'nct5525'
+
+ Addresses scanned: ISA address retrieved from Super I/O registers
+
+ Datasheet: Available from Nuvoton upon request
+
* Nuvoton NCT5572D/NCT6771F/NCT6772F/NCT6775F/W83677HG-I
Prefix: 'nct6775'
@@ -159,7 +167,7 @@ direct temperature sensor inputs, the others are special sources such as PECI,
PCH, and SMBUS. Depending on the chip type, 2 to 6 of the temperature sources
can be monitored and compared against minimum, maximum, and critical
temperatures. The driver reports up to 10 of the temperatures to the user.
-There are 4 to 5 fan rotation speed sensors, 8 to 15 analog voltage sensors,
+There are 3 to 7 fan rotation speed sensors, 8 to 18 analog voltage sensors,
one VID, alarms with beep warnings (control unimplemented), and some automatic
fan regulation strategies (plus manual fan control mode).
diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
index cf27b9c7a89e..fb99bdf16764 100644
--- a/drivers/hwmon/nct6775-core.c
+++ b/drivers/hwmon/nct6775-core.c
@@ -38,6 +38,7 @@
* nct5585d 14 7 7 2+6 0xd428 0xc1 0x5ca3
* nct6796d-s 18 7 7 6+2 0xd801 0xc1 0x5ca3
* nct6799d-r 18 7 7 6+2 0xd802 0xc1 0x5ca3
+ * nct5525d 8 3 2 3+3 0xd2b3 0xc1 0x5ca3
*
* #temp lists the number of monitored temperature sources (first value) plus
* the number of directly connectable temperature sensors (second value).
@@ -80,6 +81,7 @@ static const char * const nct6775_device_names[] = {
[nct6797] = "nct6797",
[nct6798] = "nct6798",
[nct6799] = "nct6799",
+ [nct5525] = "nct5525",
};
/* Common and NCT6775 specific data */
@@ -840,6 +842,33 @@ static const u16 NCT6106_REG_TEMP_CRIT[32] = {
static const u16 NCT6106_REG_TSI_TEMP[] = { 0x59, 0x5b, 0x5d, 0x5f, 0x61, 0x63, 0x65, 0x67 };
+/* NCT5525D specific data */
+
+static const u16 NCT5525_REG_IN_MAX[] = {
+ 0x92, 0x94, 0x96, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2 };
+static const u16 NCT5525_REG_IN_MIN[] = {
+ 0x93, 0x95, 0x97, 0x9b, 0x9d, 0x9f, 0xa1, 0xa3 };
+static const u16 NCT5525_REG_IN[] = {
+ 0x01, 0x02, 0x03, 0x05, 0x06, 0x07, 0x08, 0x09 };
+
+static const u16 NCT5525_REG_FAN[] = { 0x20, 0x22, 0x28 };
+static const u16 NCT5525_REG_FAN_MIN[] = { 0xe0, 0xe2, 0xe8 };
+static const u16 NCT5525_REG_FAN_PULSES[] = { 0xf6, 0xf6, 0xf5 };
+static const u16 NCT5525_FAN_PULSE_SHIFT[] = { 0, 2, 6 };
+
+static const s8 NCT5525_ALARM_BITS[NUM_ALARM_BITS] = {
+ 1, 2, 3, 5, 6, 7, 8, 9, -1, -1, -1, -1, /* in0-in11 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */
+ 32, 33, 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */
+ 16, 17, 18, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */
+ -1, -1, /* intr0-intr1 */
+};
+
+static const u16 NCT5525_REG_BEEP[NUM_REG_BEEP] = { 0 };
+static const s8 NCT5525_BEEP_BITS[NUM_BEEP_BITS] = {
+ [0 ... NUM_BEEP_BITS - 1] = -1,
+};
+
/* NCT6112D/NCT6114D/NCT6116D specific data */
static const u16 NCT6116_REG_FAN[] = { 0x20, 0x22, 0x24, 0x26, 0x28 };
@@ -1034,6 +1063,10 @@ static const u16 scale_in_6126[10] = {
800, 800, 1600, 1600, 800, 800, 1600, 1600, 1600, 800
};
+static const u16 scale_in_5525[8] = {
+ 800, 1600, 1600, 800, 1600, 1600, 1600, 800
+};
+
static inline long in_from_reg(u8 reg, u8 nr, const u16 *scales)
{
return DIV_ROUND_CLOSEST(reg * scales[nr], 100);
@@ -1191,6 +1224,10 @@ bool nct6775_reg_is_word_sized(struct nct6775_data *data, u16 reg)
(reg >= 0x59 && reg < 0x69 && (reg & 1)) ||
reg == 0xe0 || reg == 0xe2 || reg == 0xe4 ||
reg == 0x111 || reg == 0x121 || reg == 0x131;
+ case nct5525:
+ return reg == 0x20 || reg == 0x22 || reg == 0x28 ||
+ reg == 0x59 || reg == 0x5b || reg == 0xe0 || reg == 0xe2 ||
+ reg == 0xe8 || reg == 0x111 || reg == 0x121;
case nct6116:
case nct6126:
return reg == 0x20 || reg == 0x22 || reg == 0x24 ||
@@ -1574,6 +1611,7 @@ static int nct6775_update_pwm_limits(struct device *dev)
data->auto_pwm[i][data->auto_pwm_num] = 0xff;
break;
case nct6106:
+ case nct5525:
case nct6116:
case nct6126:
case nct6779:
@@ -1955,6 +1993,8 @@ static umode_t nct6775_in_is_visible(struct kobject *kobj,
if (nr == 1 && data->ALARM_BITS[in] == -1)
return 0;
+ if (nr == 2 && data->BEEP_BITS[in] == -1)
+ return 0;
if (!(data->have_in & BIT(in)))
return 0;
@@ -2398,8 +2438,12 @@ static umode_t nct6775_temp_is_visible(struct kobject *kobj,
if (nr == 2 && find_temp_source(data, temp, data->num_temp_alarms) < 0)
return 0; /* alarm */
- if (nr == 3 && find_temp_source(data, temp, data->num_temp_beeps) < 0)
- return 0; /* beep */
+ if (nr == 3) {
+ int source = find_temp_source(data, temp, data->num_temp_beeps);
+
+ if (source < 0 || data->BEEP_BITS[source + TEMP_ALARM_BASE] == -1)
+ return 0; /* beep */
+ }
if (nr == 4 && !data->reg_temp[1][temp]) /* max */
return 0;
@@ -3240,6 +3284,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
case nct6776:
break; /* always enabled, nothing to do */
case nct6106:
+ case nct5525:
case nct6116:
case nct6126:
case nct6779:
@@ -3640,6 +3685,87 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
reg_temp_crit_h = NCT6106_REG_TEMP_CRIT_H;
+ break;
+ case nct5525:
+ data->in_num = 8;
+ data->scale_in = scale_in_5525;
+ data->pwm_num = 2;
+ data->auto_pwm_num = 4;
+ data->temp_fixed_num = 3;
+ data->num_temp_alarms = 3;
+ data->num_temp_beeps = 0;
+
+ data->ALARM_BITS = NCT5525_ALARM_BITS;
+ data->BEEP_BITS = NCT5525_BEEP_BITS;
+
+ data->fan_from_reg = fan_from_reg13;
+ data->fan_from_reg_min = fan_from_reg13;
+ data->target_temp_mask = 0xff;
+ data->tolerance_mask = 0x07;
+ data->speed_tolerance_limit = 63;
+
+ data->temp_label = nct6776_temp_label;
+ data->temp_mask = NCT6776_TEMP_MASK;
+ data->virt_temp_mask = NCT6776_VIRT_TEMP_MASK;
+
+ data->REG_VBAT = NCT6106_REG_VBAT;
+ data->REG_DIODE = NCT6106_REG_DIODE;
+ data->DIODE_MASK = NCT6106_DIODE_MASK;
+ data->REG_VIN = NCT5525_REG_IN;
+ data->REG_IN_MINMAX[0] = NCT5525_REG_IN_MIN;
+ data->REG_IN_MINMAX[1] = NCT5525_REG_IN_MAX;
+ data->REG_TARGET = NCT6106_REG_TARGET;
+ data->REG_FAN = NCT5525_REG_FAN;
+ data->REG_FAN_MODE = NCT6106_REG_FAN_MODE;
+ data->REG_FAN_MIN = NCT5525_REG_FAN_MIN;
+ data->REG_FAN_PULSES = NCT5525_REG_FAN_PULSES;
+ data->FAN_PULSE_SHIFT = NCT5525_FAN_PULSE_SHIFT;
+ data->REG_FAN_TIME[0] = NCT6106_REG_FAN_STOP_TIME;
+ data->REG_FAN_TIME[1] = NCT6106_REG_FAN_STEP_UP_TIME;
+ data->REG_FAN_TIME[2] = NCT6106_REG_FAN_STEP_DOWN_TIME;
+ data->REG_TOLERANCE_H = NCT6106_REG_TOLERANCE_H;
+ data->REG_PWM[0] = NCT6116_REG_PWM;
+ data->REG_PWM[1] = NCT6106_REG_FAN_START_OUTPUT;
+ data->REG_PWM[2] = NCT6106_REG_FAN_STOP_OUTPUT;
+ data->REG_PWM[5] = NCT6106_REG_WEIGHT_DUTY_STEP;
+ data->REG_PWM[6] = NCT6106_REG_WEIGHT_DUTY_BASE;
+ data->REG_PWM_READ = NCT6106_REG_PWM_READ;
+ data->REG_PWM_MODE = NCT6106_REG_PWM_MODE;
+ data->PWM_MODE_MASK = NCT6106_PWM_MODE_MASK;
+ data->REG_AUTO_TEMP = NCT6106_REG_AUTO_TEMP;
+ data->REG_AUTO_PWM = NCT6106_REG_AUTO_PWM;
+ data->REG_CRITICAL_TEMP = NCT6106_REG_CRITICAL_TEMP;
+ data->REG_CRITICAL_TEMP_TOLERANCE
+ = NCT6106_REG_CRITICAL_TEMP_TOLERANCE;
+ data->REG_CRITICAL_PWM_ENABLE = NCT6106_REG_CRITICAL_PWM_ENABLE;
+ data->CRITICAL_PWM_ENABLE_MASK
+ = NCT6106_CRITICAL_PWM_ENABLE_MASK;
+ data->REG_CRITICAL_PWM = NCT6106_REG_CRITICAL_PWM;
+ data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET;
+ data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE;
+ data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL;
+ data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL;
+ data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP;
+ data->REG_WEIGHT_TEMP[1] = NCT6106_REG_WEIGHT_TEMP_STEP_TOL;
+ data->REG_WEIGHT_TEMP[2] = NCT6106_REG_WEIGHT_TEMP_BASE;
+ data->REG_ALARM = NCT6106_REG_ALARM;
+ data->REG_BEEP = NCT5525_REG_BEEP;
+ data->REG_TSI_TEMP = NCT6116_REG_TSI_TEMP;
+
+ reg_temp = NCT6106_REG_TEMP;
+ reg_temp_mon = NCT6106_REG_TEMP_MON;
+ num_reg_temp = 3;
+ num_reg_temp_mon = 2;
+ num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP);
+ reg_temp_over = NCT6106_REG_TEMP_OVER;
+ reg_temp_hyst = NCT6106_REG_TEMP_HYST;
+ reg_temp_config = NCT6106_REG_TEMP_CONFIG;
+ num_reg_temp_config = 3;
+ reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE;
+ reg_temp_crit = NCT6106_REG_TEMP_CRIT;
+ reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
+ reg_temp_crit_h = NCT6106_REG_TEMP_CRIT_H;
+
break;
case nct6116:
data->in_num = 9;
diff --git a/drivers/hwmon/nct6775-i2c.c b/drivers/hwmon/nct6775-i2c.c
index 07783910c058..3efae4bf5e4a 100644
--- a/drivers/hwmon/nct6775-i2c.c
+++ b/drivers/hwmon/nct6775-i2c.c
@@ -88,6 +88,7 @@ static const struct of_device_id __maybe_unused nct6775_i2c_of_match[] = {
{ .compatible = "nuvoton,nct6797", .data = (void *)nct6797, },
{ .compatible = "nuvoton,nct6798", .data = (void *)nct6798, },
{ .compatible = "nuvoton,nct6799", .data = (void *)nct6799, },
+ { .compatible = "nuvoton,nct5525", .data = (void *)nct5525, },
{ },
};
MODULE_DEVICE_TABLE(of, nct6775_i2c_of_match);
@@ -106,6 +107,7 @@ static const struct i2c_device_id nct6775_i2c_id[] = {
{ .name = "nct6797", .driver_data = nct6797 },
{ .name = "nct6798", .driver_data = nct6798 },
{ .name = "nct6799", .driver_data = nct6799 },
+ { .name = "nct5525", .driver_data = nct5525 },
{ }
};
MODULE_DEVICE_TABLE(i2c, nct6775_i2c_id);
@@ -126,8 +128,8 @@ static int nct6775_i2c_probe_init(struct nct6775_data *data)
* controlling/monitoring the fans -- these are thus unlikely to be
* doing anything actually useful.
*/
- data->has_fan = 0x03;
- data->has_fan_min = 0x03;
+ data->has_fan = data->kind == nct5525 ? 0x07 : 0x03;
+ data->has_fan_min = data->has_fan;
data->has_pwm = 0x03;
/*
diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c
index d359c28f3e0f..3bc7a27d58a6 100644
--- a/drivers/hwmon/nct6775-platform.c
+++ b/drivers/hwmon/nct6775-platform.c
@@ -37,6 +37,7 @@ static const char * const nct6775_sio_names[] __initconst = {
[nct6797] = "NCT6797D",
[nct6798] = "NCT6798D/NCT5585D",
[nct6799] = "NCT6796D-S/NCT6799D-R",
+ [nct5525] = "NCT5525D",
};
static unsigned short force_id;
@@ -91,6 +92,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
#define SIO_NCT6797_ID 0xd450
#define SIO_NCT6798_ID 0xd428
#define SIO_NCT6799_ID 0xd800
+#define SIO_NCT5525_ID 0xd2b0
#define SIO_ID_MASK 0xFFF8
/*
@@ -552,6 +554,8 @@ nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio
fan3pin = !(cr24 & 0x80);
pwm3pin = cr24 & 0x08;
+ } else if (data->kind == nct5525) {
+ fan3pin = true;
} else if (data->kind == nct6116) {
int cr1a = sio_data->sio_inb(sio_data, 0x1a);
int cr1b = sio_data->sio_inb(sio_data, 0x1b);
@@ -839,6 +843,8 @@ static umode_t nct6775_other_is_visible(struct kobject *kobj,
if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 3] < 0)
return 0;
}
+ if (index == 5 && data->BEEP_BITS[BEEP_ENABLE_BASE] < 0)
+ return 0;
return nct6775_attr_mode(data, attr);
}
@@ -883,6 +889,7 @@ static int nct6775_platform_probe_init(struct nct6775_data *data)
data->have_vid = (cr2a & 0x60) == 0x40;
break;
case nct6106:
+ case nct5525:
case nct6116:
case nct6126:
case nct6779:
@@ -919,6 +926,8 @@ static int nct6775_platform_probe_init(struct nct6775_data *data)
case nct6126:
tmp |= 0xe0;
break;
+ case nct5525:
+ break;
case nct6775:
tmp |= 0x1e;
break;
@@ -1072,6 +1081,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
case SIO_NCT6799_ID:
sio_data->kind = nct6799;
break;
+ case SIO_NCT5525_ID:
+ sio_data->kind = nct5525;
+ break;
default:
if (val != 0xffff)
pr_debug("unsupported chip ID: 0x%04x\n", val);
diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h
index d18ac91be497..d94a1c1a31f5 100644
--- a/drivers/hwmon/nct6775.h
+++ b/drivers/hwmon/nct6775.h
@@ -5,7 +5,8 @@
#include <linux/types.h>
enum kinds { nct6106, nct6116, nct6126, nct6775, nct6776, nct6779, nct6791,
- nct6792, nct6793, nct6795, nct6796, nct6797, nct6798, nct6799 };
+ nct6792, nct6793, nct6795, nct6796, nct6797, nct6798, nct6799,
+ nct5525 };
enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
#define NUM_TEMP 12 /* Max number of temp attribute sets w/ limits*/
--
2.47.3
next reply other threads:[~2026-11-03 13:30 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-03 13:29 Anton Lundin [this message]
2026-11-03 13:42 ` [PATCH] hwmon: (nct6775) Add support for NCT5525 sashiko-bot
2026-11-03 13:47 ` Krzysztof Kozlowski
2026-11-03 15:06 ` Guenter Roeck
2026-11-03 17:33 ` Anton Lundin
2026-11-03 19:01 ` Guenter Roeck
2026-11-03 15:07 ` Guenter Roeck
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261103132948.144216-1-glance@ac2.se \
--to=glance@ac2.se \
--cc=linux-hwmon@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=zev@bewilderbeest.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.