| // SPDX-License-Identifier: GPL-2.0+ | 
 | /* | 
 |  * Driver for Nuvoton nct7201 and nct7202 power monitor chips. | 
 |  * | 
 |  * Copyright (c) 2024-2025 Nuvoton Technology corporation. | 
 |  */ | 
 |  | 
 | #include <linux/array_size.h> | 
 | #include <linux/bitfield.h> | 
 | #include <linux/bits.h> | 
 | #include <linux/delay.h> | 
 | #include <linux/dev_printk.h> | 
 | #include <linux/err.h> | 
 | #include <linux/i2c.h> | 
 | #include <linux/mod_devicetable.h> | 
 | #include <linux/module.h> | 
 | #include <linux/regmap.h> | 
 | #include <linux/time.h> | 
 | #include <linux/types.h> | 
 | #include <linux/unaligned.h> | 
 |  | 
 | #include <linux/iio/events.h> | 
 | #include <linux/iio/iio.h> | 
 |  | 
 | #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 <j2anfernee@gmail.com>"); | 
 | MODULE_DESCRIPTION("Nuvoton NCT7201 voltage monitor driver"); | 
 | MODULE_LICENSE("GPL"); |