// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Nuvoton nct7201 and nct7202 power monitor chips. * * Copyright (c) 2024-2025 Nuvoton Technology corporation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NCT7201_REG_INTERRUPT_STATUS 0x0C #define NCT7201_REG_VOLT_LOW_BYTE 0x0F #define NCT7201_REG_CONFIGURATION 0x10 #define NCT7201_BIT_CONFIGURATION_START BIT(0) #define NCT7201_BIT_CONFIGURATION_ALERT_MSK BIT(1) #define NCT7201_BIT_CONFIGURATION_CONV_RATE BIT(2) #define NCT7201_BIT_CONFIGURATION_RESET BIT(7) #define NCT7201_REG_ADVANCED_CONFIGURATION 0x11 #define NCT7201_BIT_ADVANCED_CONF_MOD_ALERT BIT(0) #define NCT7201_BIT_ADVANCED_CONF_MOD_STS BIT(1) #define NCT7201_BIT_ADVANCED_CONF_FAULT_QUEUE BIT(2) #define NCT7201_BIT_ADVANCED_CONF_EN_DEEP_SHUTDOWN BIT(4) #define NCT7201_BIT_ADVANCED_CONF_EN_SMB_TIMEOUT BIT(5) #define NCT7201_BIT_ADVANCED_CONF_MOD_RSTIN BIT(7) #define NCT7201_REG_CHANNEL_INPUT_MODE 0x12 #define NCT7201_REG_CHANNEL_ENABLE 0x13 #define NCT7201_REG_INTERRUPT_MASK_1 0x15 #define NCT7201_REG_INTERRUPT_MASK_2 0x16 #define NCT7201_REG_BUSY_STATUS 0x1E #define NCT7201_BIT_BUSY BIT(0) #define NCT7201_BIT_PWR_UP BIT(1) #define NCT7201_REG_ONE_SHOT 0x1F #define NCT7201_REG_SMUS_ADDRESS 0xFC #define NCT7201_REG_VIN_MASK GENMASK(15, 3) #define NCT7201_REG_VIN(i) (0x00 + i) #define NCT7201_REG_VIN_HIGH_LIMIT(i) (0x20 + (i) * 2) #define NCT7201_REG_VIN_LOW_LIMIT(i) (0x21 + (i) * 2) #define NCT7201_MAX_CHANNEL 12 static const struct regmap_range nct7201_read_reg_range[] = { regmap_reg_range(NCT7201_REG_INTERRUPT_STATUS, NCT7201_REG_BUSY_STATUS), regmap_reg_range(NCT7201_REG_SMUS_ADDRESS, NCT7201_REG_SMUS_ADDRESS), }; static const struct regmap_access_table nct7201_readable_regs_tbl = { .yes_ranges = nct7201_read_reg_range, .n_yes_ranges = ARRAY_SIZE(nct7201_read_reg_range), }; static const struct regmap_range nct7201_write_reg_range[] = { regmap_reg_range(NCT7201_REG_CONFIGURATION, NCT7201_REG_INTERRUPT_MASK_2), regmap_reg_range(NCT7201_REG_ONE_SHOT, NCT7201_REG_ONE_SHOT), }; static const struct regmap_access_table nct7201_writeable_regs_tbl = { .yes_ranges = nct7201_write_reg_range, .n_yes_ranges = ARRAY_SIZE(nct7201_write_reg_range), }; static const struct regmap_range nct7201_read_vin_reg_range[] = { regmap_reg_range(NCT7201_REG_VIN(0), NCT7201_REG_VIN(NCT7201_MAX_CHANNEL - 1)), regmap_reg_range(NCT7201_REG_VIN_HIGH_LIMIT(0), NCT7201_REG_VIN_LOW_LIMIT(NCT7201_MAX_CHANNEL - 1)), }; static const struct regmap_access_table nct7201_readable_vin_regs_tbl = { .yes_ranges = nct7201_read_vin_reg_range, .n_yes_ranges = ARRAY_SIZE(nct7201_read_vin_reg_range), }; static const struct regmap_range nct7201_write_vin_reg_range[] = { regmap_reg_range(NCT7201_REG_VIN_HIGH_LIMIT(0), NCT7201_REG_VIN_LOW_LIMIT(NCT7201_MAX_CHANNEL - 1)), }; static const struct regmap_access_table nct7201_writeable_vin_regs_tbl = { .yes_ranges = nct7201_write_vin_reg_range, .n_yes_ranges = ARRAY_SIZE(nct7201_write_vin_reg_range), }; static const struct regmap_config nct7201_regmap8_config = { .name = "vin-data-read-byte", .reg_bits = 8, .val_bits = 8, .use_single_read = true, .use_single_write = true, .max_register = 0xff, .rd_table = &nct7201_readable_regs_tbl, .wr_table = &nct7201_writeable_regs_tbl, }; static const struct regmap_config nct7201_regmap16_config = { .name = "vin-data-read-word", .reg_bits = 8, .val_bits = 16, .max_register = 0xff, .rd_table = &nct7201_readable_vin_regs_tbl, .wr_table = &nct7201_writeable_vin_regs_tbl, }; struct nct7201_chip_info { struct regmap *regmap; struct regmap *regmap16; int num_vin_channels; __le16 vin_mask; }; struct nct7201_adc_model_data { const char *model_name; const struct iio_chan_spec *channels; unsigned int num_channels; int num_vin_channels; }; static const struct iio_event_spec nct7201_events[] = { { .type = IIO_EV_TYPE_THRESH, .dir = IIO_EV_DIR_RISING, .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), }, { .type = IIO_EV_TYPE_THRESH, .dir = IIO_EV_DIR_FALLING, .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), }, }; #define NCT7201_VOLTAGE_CHANNEL(num) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ .channel = num + 1, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .address = num, \ .event_spec = nct7201_events, \ .num_event_specs = ARRAY_SIZE(nct7201_events), \ } static const struct iio_chan_spec nct7201_channels[] = { NCT7201_VOLTAGE_CHANNEL(0), NCT7201_VOLTAGE_CHANNEL(1), NCT7201_VOLTAGE_CHANNEL(2), NCT7201_VOLTAGE_CHANNEL(3), NCT7201_VOLTAGE_CHANNEL(4), NCT7201_VOLTAGE_CHANNEL(5), NCT7201_VOLTAGE_CHANNEL(6), NCT7201_VOLTAGE_CHANNEL(7), }; static const struct iio_chan_spec nct7202_channels[] = { NCT7201_VOLTAGE_CHANNEL(0), NCT7201_VOLTAGE_CHANNEL(1), NCT7201_VOLTAGE_CHANNEL(2), NCT7201_VOLTAGE_CHANNEL(3), NCT7201_VOLTAGE_CHANNEL(4), NCT7201_VOLTAGE_CHANNEL(5), NCT7201_VOLTAGE_CHANNEL(6), NCT7201_VOLTAGE_CHANNEL(7), NCT7201_VOLTAGE_CHANNEL(8), NCT7201_VOLTAGE_CHANNEL(9), NCT7201_VOLTAGE_CHANNEL(10), NCT7201_VOLTAGE_CHANNEL(11), }; static int nct7201_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct nct7201_chip_info *chip = iio_priv(indio_dev); unsigned int value; int err; if (chan->type != IIO_VOLTAGE) return -EOPNOTSUPP; switch (mask) { case IIO_CHAN_INFO_RAW: err = regmap_read(chip->regmap16, NCT7201_REG_VIN(chan->address), &value); if (err) return err; *val = FIELD_GET(NCT7201_REG_VIN_MASK, value); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: /* From the datasheet, we have to multiply by 0.0004995 */ *val = 0; *val2 = 499500; return IIO_VAL_INT_PLUS_NANO; default: return -EINVAL; } } static int nct7201_read_event_value(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, enum iio_event_info info, int *val, int *val2) { struct nct7201_chip_info *chip = iio_priv(indio_dev); unsigned int value; int err; if (chan->type != IIO_VOLTAGE) return -EOPNOTSUPP; if (info != IIO_EV_INFO_VALUE) return -EINVAL; if (dir == IIO_EV_DIR_FALLING) err = regmap_read(chip->regmap16, NCT7201_REG_VIN_LOW_LIMIT(chan->address), &value); else err = regmap_read(chip->regmap16, NCT7201_REG_VIN_HIGH_LIMIT(chan->address), &value); if (err) return err; *val = FIELD_GET(NCT7201_REG_VIN_MASK, value); return IIO_VAL_INT; } static int nct7201_write_event_value(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, enum iio_event_info info, int val, int val2) { struct nct7201_chip_info *chip = iio_priv(indio_dev); int err; if (chan->type != IIO_VOLTAGE) return -EOPNOTSUPP; if (info != IIO_EV_INFO_VALUE) return -EOPNOTSUPP; if (dir == IIO_EV_DIR_FALLING) err = regmap_write(chip->regmap16, NCT7201_REG_VIN_LOW_LIMIT(chan->address), FIELD_PREP(NCT7201_REG_VIN_MASK, val)); else err = regmap_write(chip->regmap16, NCT7201_REG_VIN_HIGH_LIMIT(chan->address), FIELD_PREP(NCT7201_REG_VIN_MASK, val)); return err; } static int nct7201_read_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir) { struct nct7201_chip_info *chip = iio_priv(indio_dev); if (chan->type != IIO_VOLTAGE) return -EOPNOTSUPP; return !!(le16_to_cpu(chip->vin_mask) & BIT(chan->address)); } static int nct7201_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, bool state) { struct nct7201_chip_info *chip = iio_priv(indio_dev); __le16 mask = cpu_to_le16(BIT(chan->address)); int err; if (chan->type != IIO_VOLTAGE) return -EOPNOTSUPP; if (state) chip->vin_mask |= mask; else chip->vin_mask &= ~mask; if (chip->num_vin_channels <= 8) err = regmap_write(chip->regmap, NCT7201_REG_CHANNEL_ENABLE, le16_to_cpu(chip->vin_mask)); else err = regmap_bulk_write(chip->regmap, NCT7201_REG_CHANNEL_ENABLE, &chip->vin_mask, sizeof(chip->vin_mask)); return err; } static const struct iio_info nct7201_info = { .read_raw = nct7201_read_raw, .read_event_config = nct7201_read_event_config, .write_event_config = nct7201_write_event_config, .read_event_value = nct7201_read_event_value, .write_event_value = nct7201_write_event_value, }; static const struct iio_info nct7201_info_no_irq = { .read_raw = nct7201_read_raw, }; static const struct nct7201_adc_model_data nct7201_model_data = { .model_name = "nct7201", .channels = nct7201_channels, .num_channels = ARRAY_SIZE(nct7201_channels), .num_vin_channels = 8, }; static const struct nct7201_adc_model_data nct7202_model_data = { .model_name = "nct7202", .channels = nct7202_channels, .num_channels = ARRAY_SIZE(nct7202_channels), .num_vin_channels = 12, }; static int nct7201_init_chip(struct nct7201_chip_info *chip) { struct device *dev = regmap_get_device(chip->regmap); __le16 data = cpu_to_le16(GENMASK(chip->num_vin_channels - 1, 0)); unsigned int value; int err; err = regmap_write(chip->regmap, NCT7201_REG_CONFIGURATION, NCT7201_BIT_CONFIGURATION_RESET); if (err) return dev_err_probe(dev, err, "Failed to reset chip\n"); /* * After about 25 msecs, the device should be ready and then the power-up * bit will be set to 1. */ fsleep(25 * USEC_PER_MSEC); err = regmap_read(chip->regmap, NCT7201_REG_BUSY_STATUS, &value); if (err) return dev_err_probe(dev, err, "Failed to read busy status\n"); if (!(value & NCT7201_BIT_PWR_UP)) return dev_err_probe(dev, -EIO, "Failed to power up after reset\n"); /* Enable Channels */ if (chip->num_vin_channels <= 8) err = regmap_write(chip->regmap, NCT7201_REG_CHANNEL_ENABLE, le16_to_cpu(data)); else err = regmap_bulk_write(chip->regmap, NCT7201_REG_CHANNEL_ENABLE, &data, sizeof(data)); if (err) return dev_err_probe(dev, err, "Failed to enable channels\n"); err = regmap_bulk_read(chip->regmap, NCT7201_REG_CHANNEL_ENABLE, &chip->vin_mask, sizeof(chip->vin_mask)); if (err) return dev_err_probe(dev, err, "Failed to read channel enable register\n"); /* Start monitoring if needed */ err = regmap_set_bits(chip->regmap, NCT7201_REG_CONFIGURATION, NCT7201_BIT_CONFIGURATION_START); if (err) return dev_err_probe(dev, err, "Failed to start monitoring\n"); return 0; } static irqreturn_t nct7201_irq_handler(int irq, void *private) { struct iio_dev *indio_dev = private; struct nct7201_chip_info *chip = iio_priv(indio_dev); __le16 data; int err; err = regmap_bulk_read(chip->regmap, NCT7201_REG_INTERRUPT_STATUS, &data, sizeof(data)); if (err) return IRQ_NONE; if (data) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), iio_get_time_ns(indio_dev)); return IRQ_HANDLED; } static int nct7201_probe(struct i2c_client *client) { const struct nct7201_adc_model_data *model_data; struct device *dev = &client->dev; struct nct7201_chip_info *chip; struct iio_dev *indio_dev; int ret; model_data = i2c_get_match_data(client); if (!model_data) return -ENODEV; indio_dev = devm_iio_device_alloc(dev, sizeof(*chip)); if (!indio_dev) return -ENOMEM; chip = iio_priv(indio_dev); chip->regmap = devm_regmap_init_i2c(client, &nct7201_regmap8_config); if (IS_ERR(chip->regmap)) return dev_err_probe(dev, PTR_ERR(chip->regmap), "Failed to init regmap\n"); chip->regmap16 = devm_regmap_init_i2c(client, &nct7201_regmap16_config); if (IS_ERR(chip->regmap16)) return dev_err_probe(dev, PTR_ERR(chip->regmap16), "Failed to init regmap16\n"); chip->num_vin_channels = model_data->num_vin_channels; ret = nct7201_init_chip(chip); if (ret) return ret; indio_dev->name = model_data->model_name; indio_dev->channels = model_data->channels; indio_dev->num_channels = model_data->num_channels; if (client->irq) { /* Enable alert function */ ret = regmap_clear_bits(chip->regmap, NCT7201_REG_CONFIGURATION, NCT7201_BIT_CONFIGURATION_ALERT_MSK); if (ret) return dev_err_probe(dev, ret, "Failed to enable alert function\n"); ret = devm_request_threaded_irq(dev, client->irq, NULL, nct7201_irq_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, indio_dev); if (ret) return dev_err_probe(dev, ret, "Failed to assign interrupt.\n"); indio_dev->info = &nct7201_info; } else { indio_dev->info = &nct7201_info_no_irq; } indio_dev->modes = INDIO_DIRECT_MODE; return devm_iio_device_register(dev, indio_dev); } static const struct i2c_device_id nct7201_id[] = { { .name = "nct7201", .driver_data = (kernel_ulong_t)&nct7201_model_data }, { .name = "nct7202", .driver_data = (kernel_ulong_t)&nct7202_model_data }, { } }; MODULE_DEVICE_TABLE(i2c, nct7201_id); static const struct of_device_id nct7201_of_match[] = { { .compatible = "nuvoton,nct7201", .data = &nct7201_model_data, }, { .compatible = "nuvoton,nct7202", .data = &nct7202_model_data, }, { } }; MODULE_DEVICE_TABLE(of, nct7201_of_match); static struct i2c_driver nct7201_driver = { .driver = { .name = "nct7201", .of_match_table = nct7201_of_match, }, .probe = nct7201_probe, .id_table = nct7201_id, }; module_i2c_driver(nct7201_driver); MODULE_AUTHOR("Eason Yang "); MODULE_DESCRIPTION("Nuvoton NCT7201 voltage monitor driver"); MODULE_LICENSE("GPL");