brintos

brintos / linux-shallow public Read only

0
0
Text · 18.2 KiB · edf4544 Raw
812 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * cc2.c - Support for the Amphenol ChipCap 2 relative humidity, temperature sensor4 *5 * Part numbers supported:6 * CC2D23, CC2D23S, CC2D25, CC2D25S, CC2D33, CC2D33S, CC2D35, CC2D35S7 *8 * Author: Javier Carrasco <javier.carrasco.cruz@gmail.com>9 *10 * Datasheet and application notes:11 * https://www.amphenol-sensors.com/en/telaire/humidity/527-humidity-sensors/3095-chipcap-212 */13 14#include <linux/bitfield.h>15#include <linux/bits.h>16#include <linux/completion.h>17#include <linux/delay.h>18#include <linux/hwmon.h>19#include <linux/i2c.h>20#include <linux/interrupt.h>21#include <linux/irq.h>22#include <linux/module.h>23#include <linux/regulator/consumer.h>24 25#define CC2_START_CM			0xA026#define CC2_START_NOM			0x8027#define CC2_R_ALARM_H_ON		0x1828#define CC2_R_ALARM_H_OFF		0x1929#define CC2_R_ALARM_L_ON		0x1A30#define CC2_R_ALARM_L_OFF		0x1B31#define CC2_RW_OFFSET			0x4032#define CC2_W_ALARM_H_ON		(CC2_R_ALARM_H_ON + CC2_RW_OFFSET)33#define CC2_W_ALARM_H_OFF		(CC2_R_ALARM_H_OFF + CC2_RW_OFFSET)34#define CC2_W_ALARM_L_ON		(CC2_R_ALARM_L_ON + CC2_RW_OFFSET)35#define CC2_W_ALARM_L_OFF		(CC2_R_ALARM_L_OFF + CC2_RW_OFFSET)36 37#define CC2_STATUS_FIELD		GENMASK(7, 6)38#define CC2_STATUS_VALID_DATA		0x0039#define CC2_STATUS_STALE_DATA		0x0140#define CC2_STATUS_CMD_MODE		0x0241 42#define CC2_RESPONSE_FIELD		GENMASK(1, 0)43#define CC2_RESPONSE_BUSY		0x0044#define CC2_RESPONSE_ACK		0x0145#define CC2_RESPONSE_NACK		0x0246 47#define CC2_ERR_CORR_EEPROM		BIT(2)48#define CC2_ERR_UNCORR_EEPROM		BIT(3)49#define CC2_ERR_RAM_PARITY		BIT(4)50#define CC2_ERR_CONFIG_LOAD		BIT(5)51 52#define CC2_EEPROM_SIZE			1053#define CC2_EEPROM_DATA_LEN		354#define CC2_MEASUREMENT_DATA_LEN	455 56#define CC2_RH_DATA_FIELD		GENMASK(13, 0)57 58/* ensure clean off -> on transitions */59#define CC2_POWER_CYCLE_MS		8060 61#define CC2_STARTUP_TO_DATA_MS		5562#define CC2_RESP_START_CM_US		10063#define CC2_RESP_EEPROM_R_US		10064#define CC2_RESP_EEPROM_W_MS		1265#define CC2_STARTUP_TIME_US		125066 67#define CC2_RH_MAX			(100 * 1000U)68 69#define CC2_CM_RETRIES			570 71struct cc2_rh_alarm_info {72	bool low_alarm;73	bool high_alarm;74	bool low_alarm_visible;75	bool high_alarm_visible;76};77 78struct cc2_data {79	struct cc2_rh_alarm_info rh_alarm;80	struct completion complete;81	struct device *hwmon;82	struct i2c_client *client;83	struct mutex dev_access_lock; /* device access lock */84	struct regulator *regulator;85	const char *name;86	int irq_ready;87	int irq_low;88	int irq_high;89	bool process_irqs;90};91 92enum cc2_chan_addr {93	CC2_CHAN_TEMP = 0,94	CC2_CHAN_HUMIDITY,95};96 97/* %RH as a per cent mille from a register value */98static long cc2_rh_convert(u16 data)99{100	unsigned long tmp = (data & CC2_RH_DATA_FIELD) * CC2_RH_MAX;101 102	return tmp / ((1 << 14) - 1);103}104 105/* convert %RH to a register value */106static u16 cc2_rh_to_reg(long data)107{108	return data * ((1 << 14) - 1) / CC2_RH_MAX;109}110 111/* temperature in milli degrees celsius from a register value */112static long cc2_temp_convert(u16 data)113{114	unsigned long tmp = ((data >> 2) * 165 * 1000U) / ((1 << 14) - 1);115 116	return tmp - 40 * 1000U;117}118 119static int cc2_enable(struct cc2_data *data)120{121	int ret;122 123	/* exclusive regulator, check in case a disable failed */124	if (regulator_is_enabled(data->regulator))125		return 0;126 127	/* clear any pending completion */128	try_wait_for_completion(&data->complete);129 130	ret = regulator_enable(data->regulator);131	if (ret < 0)132		return ret;133 134	usleep_range(CC2_STARTUP_TIME_US, CC2_STARTUP_TIME_US + 125);135 136	data->process_irqs = true;137 138	return 0;139}140 141static void cc2_disable(struct cc2_data *data)142{143	int err;144 145	/* ignore alarms triggered by voltage toggling when powering up */146	data->process_irqs = false;147 148	/* exclusive regulator, check in case an enable failed */149	if (regulator_is_enabled(data->regulator)) {150		err = regulator_disable(data->regulator);151		if (err)152			dev_dbg(&data->client->dev, "Failed to disable device");153	}154}155 156static int cc2_cmd_response_diagnostic(struct device *dev, u8 status)157{158	int resp;159 160	if (FIELD_GET(CC2_STATUS_FIELD, status) != CC2_STATUS_CMD_MODE) {161		dev_dbg(dev, "Command sent out of command window\n");162		return -ETIMEDOUT;163	}164 165	resp = FIELD_GET(CC2_RESPONSE_FIELD, status);166	switch (resp) {167	case CC2_RESPONSE_ACK:168		return 0;169	case CC2_RESPONSE_BUSY:170		return -EBUSY;171	case CC2_RESPONSE_NACK:172		if (resp & CC2_ERR_CORR_EEPROM)173			dev_dbg(dev, "Command failed: corrected EEPROM\n");174		if (resp & CC2_ERR_UNCORR_EEPROM)175			dev_dbg(dev, "Command failed: uncorrected EEPROM\n");176		if (resp & CC2_ERR_RAM_PARITY)177			dev_dbg(dev, "Command failed: RAM parity\n");178		if (resp & CC2_ERR_RAM_PARITY)179			dev_dbg(dev, "Command failed: configuration error\n");180		return -ENODATA;181	default:182		dev_dbg(dev, "Unknown command reply\n");183		return -EINVAL;184	}185}186 187static int cc2_read_command_status(struct i2c_client *client)188{189	u8 status;190	int ret;191 192	ret = i2c_master_recv(client, &status, 1);193	if (ret != 1) {194		ret = ret < 0 ? ret : -EIO;195		return ret;196	}197 198	return cc2_cmd_response_diagnostic(&client->dev, status);199}200 201/*202 * The command mode is only accessible after sending the START_CM command in the203 * first 10 ms after power-up. Only in case the command window is missed,204 * CC2_CM_RETRIES retries are attempted before giving up and returning an error.205 */206static int cc2_command_mode_start(struct cc2_data *data)207{208	unsigned long timeout;209	int i, ret;210 211	for (i = 0; i < CC2_CM_RETRIES; i++) {212		ret = cc2_enable(data);213		if (ret < 0)214			return ret;215 216		ret = i2c_smbus_write_word_data(data->client, CC2_START_CM, 0);217		if (ret < 0)218			return ret;219 220		if (data->irq_ready > 0) {221			timeout = usecs_to_jiffies(2 * CC2_RESP_START_CM_US);222			ret = wait_for_completion_timeout(&data->complete,223							  timeout);224			if (!ret)225				return -ETIMEDOUT;226		} else {227			usleep_range(CC2_RESP_START_CM_US,228				     2 * CC2_RESP_START_CM_US);229		}230		ret = cc2_read_command_status(data->client);231		if (ret != -ETIMEDOUT || i == CC2_CM_RETRIES)232			break;233 234		/* command window missed, prepare for a retry */235		cc2_disable(data);236		msleep(CC2_POWER_CYCLE_MS);237	}238 239	return ret;240}241 242/* Sending a Start_NOM command finishes the command mode immediately with no243 * reply and the device enters normal operation mode244 */245static int cc2_command_mode_finish(struct cc2_data *data)246{247	int ret;248 249	ret = i2c_smbus_write_word_data(data->client, CC2_START_NOM, 0);250	if (ret < 0)251		return ret;252 253	return 0;254}255 256static int cc2_write_reg(struct cc2_data *data, u8 reg, u16 val)257{258	unsigned long timeout;259	int ret;260 261	ret = cc2_command_mode_start(data);262	if (ret < 0)263		goto disable;264 265	cpu_to_be16s(&val);266	ret = i2c_smbus_write_word_data(data->client, reg, val);267	if (ret < 0)268		goto disable;269 270	if (data->irq_ready > 0) {271		timeout = msecs_to_jiffies(2 * CC2_RESP_EEPROM_W_MS);272		ret = wait_for_completion_timeout(&data->complete, timeout);273		if (!ret) {274			ret = -ETIMEDOUT;275			goto disable;276		}277	} else {278		msleep(CC2_RESP_EEPROM_W_MS);279	}280 281	ret = cc2_read_command_status(data->client);282 283disable:284	cc2_disable(data);285 286	return ret;287}288 289static int cc2_read_reg(struct cc2_data *data, u8 reg, u16 *val)290{291	u8 buf[CC2_EEPROM_DATA_LEN];292	unsigned long timeout;293	int ret;294 295	ret = cc2_command_mode_start(data);296	if (ret < 0)297		return ret;298 299	ret = i2c_smbus_write_word_data(data->client, reg, 0);300	if (ret < 0)301		return ret;302 303	if (data->irq_ready > 0) {304		timeout = usecs_to_jiffies(2 * CC2_RESP_EEPROM_R_US);305		ret = wait_for_completion_timeout(&data->complete, timeout);306		if (!ret)307			return -ETIMEDOUT;308 309	} else {310		usleep_range(CC2_RESP_EEPROM_R_US, CC2_RESP_EEPROM_R_US + 10);311	}312	ret = i2c_master_recv(data->client, buf, CC2_EEPROM_DATA_LEN);313	if (ret != CC2_EEPROM_DATA_LEN)314		return ret < 0 ? ret : -EIO;315 316	*val = be16_to_cpup((__be16 *)&buf[1]);317 318	return cc2_read_command_status(data->client);319}320 321static int cc2_get_reg_val(struct cc2_data *data, u8 reg, long *val)322{323	u16 reg_val;324	int ret;325 326	ret = cc2_read_reg(data, reg, &reg_val);327	if (!ret)328		*val = cc2_rh_convert(reg_val);329 330	cc2_disable(data);331 332	return ret;333}334 335static int cc2_data_fetch(struct i2c_client *client,336			  enum hwmon_sensor_types type, long *val)337{338	u8 data[CC2_MEASUREMENT_DATA_LEN];339	u8 status;340	int ret;341 342	ret = i2c_master_recv(client, data, CC2_MEASUREMENT_DATA_LEN);343	if (ret != CC2_MEASUREMENT_DATA_LEN) {344		ret = ret < 0 ? ret : -EIO;345		return ret;346	}347	status = FIELD_GET(CC2_STATUS_FIELD, data[0]);348	if (status == CC2_STATUS_STALE_DATA)349		return -EBUSY;350 351	if (status != CC2_STATUS_VALID_DATA)352		return -EIO;353 354	switch (type) {355	case hwmon_humidity:356		*val = cc2_rh_convert(be16_to_cpup((__be16 *)&data[0]));357		break;358	case hwmon_temp:359		*val = cc2_temp_convert(be16_to_cpup((__be16 *)&data[2]));360		break;361	default:362		return -EINVAL;363	}364 365	return 0;366}367 368static int cc2_read_measurement(struct cc2_data *data,369				enum hwmon_sensor_types type, long *val)370{371	unsigned long timeout;372	int ret;373 374	if (data->irq_ready > 0) {375		timeout = msecs_to_jiffies(CC2_STARTUP_TO_DATA_MS * 2);376		ret = wait_for_completion_timeout(&data->complete, timeout);377		if (!ret)378			return -ETIMEDOUT;379 380	} else {381		msleep(CC2_STARTUP_TO_DATA_MS);382	}383 384	ret = cc2_data_fetch(data->client, type, val);385 386	return ret;387}388 389/*390 * A measurement requires enabling the device, waiting for the automatic391 * measurement to finish, reading the measurement data and disabling the device392 * again.393 */394static int cc2_measurement(struct cc2_data *data, enum hwmon_sensor_types type,395			   long *val)396{397	int ret;398 399	ret = cc2_enable(data);400	if (ret)401		return ret;402 403	ret = cc2_read_measurement(data, type, val);404 405	cc2_disable(data);406 407	return ret;408}409 410/*411 * In order to check alarm status, the corresponding ALARM_OFF (hysteresis)412 * register must be read and a new measurement must be carried out to trigger413 * the alarm signals. Given that the device carries out a measurement after414 * exiting the command mode, there is no need to force two power-up sequences.415 * Instead, a NOM command is sent and the device is disabled after the416 * measurement is read.417 */418static int cc2_read_hyst_and_measure(struct cc2_data *data, u8 reg,419				     long *hyst, long *measurement)420{421	u16 reg_val;422	int ret;423 424	ret = cc2_read_reg(data, reg, &reg_val);425	if (ret)426		goto disable;427 428	*hyst = cc2_rh_convert(reg_val);429 430	ret = cc2_command_mode_finish(data);431	if (ret)432		goto disable;433 434	ret = cc2_read_measurement(data, hwmon_humidity, measurement);435 436disable:437	cc2_disable(data);438 439	return ret;440}441 442static umode_t cc2_is_visible(const void *data, enum hwmon_sensor_types type,443			      u32 attr, int channel)444{445	const struct cc2_data *cc2 = data;446 447	switch (type) {448	case hwmon_humidity:449		switch (attr) {450		case hwmon_humidity_input:451			return 0444;452		case hwmon_humidity_min_alarm:453			return cc2->rh_alarm.low_alarm_visible ? 0444 : 0;454		case hwmon_humidity_max_alarm:455			return cc2->rh_alarm.high_alarm_visible ? 0444 : 0;456		case hwmon_humidity_min:457		case hwmon_humidity_min_hyst:458			return cc2->rh_alarm.low_alarm_visible ? 0644 : 0;459		case hwmon_humidity_max:460		case hwmon_humidity_max_hyst:461			return cc2->rh_alarm.high_alarm_visible ? 0644 : 0;462		default:463			return 0;464		}465	case hwmon_temp:466		switch (attr) {467		case hwmon_temp_input:468			return 0444;469		default:470			return 0;471		}472	default:473		break;474	}475 476	return 0;477}478 479static irqreturn_t cc2_ready_interrupt(int irq, void *data)480{481	struct cc2_data *cc2 = data;482 483	if (cc2->process_irqs)484		complete(&cc2->complete);485 486	return IRQ_HANDLED;487}488 489static irqreturn_t cc2_low_interrupt(int irq, void *data)490{491	struct cc2_data *cc2 = data;492 493	if (cc2->process_irqs) {494		hwmon_notify_event(cc2->hwmon, hwmon_humidity,495				   hwmon_humidity_min_alarm, CC2_CHAN_HUMIDITY);496		cc2->rh_alarm.low_alarm = true;497	}498 499	return IRQ_HANDLED;500}501 502static irqreturn_t cc2_high_interrupt(int irq, void *data)503{504	struct cc2_data *cc2 = data;505 506	if (cc2->process_irqs) {507		hwmon_notify_event(cc2->hwmon, hwmon_humidity,508				   hwmon_humidity_max_alarm, CC2_CHAN_HUMIDITY);509		cc2->rh_alarm.high_alarm = true;510	}511 512	return IRQ_HANDLED;513}514 515static int cc2_humidity_min_alarm_status(struct cc2_data *data, long *val)516{517	long measurement, min_hyst;518	int ret;519 520	ret = cc2_read_hyst_and_measure(data, CC2_R_ALARM_L_OFF, &min_hyst,521					&measurement);522	if (ret < 0)523		return ret;524 525	if (data->rh_alarm.low_alarm) {526		*val = (measurement < min_hyst) ? 1 : 0;527		data->rh_alarm.low_alarm = *val;528	} else {529		*val = 0;530	}531 532	return 0;533}534 535static int cc2_humidity_max_alarm_status(struct cc2_data *data, long *val)536{537	long measurement, max_hyst;538	int ret;539 540	ret = cc2_read_hyst_and_measure(data, CC2_R_ALARM_H_OFF, &max_hyst,541					&measurement);542	if (ret < 0)543		return ret;544 545	if (data->rh_alarm.high_alarm) {546		*val = (measurement > max_hyst) ? 1 : 0;547		data->rh_alarm.high_alarm = *val;548	} else {549		*val = 0;550	}551 552	return 0;553}554 555static int cc2_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,556		    int channel, long *val)557{558	struct cc2_data *data = dev_get_drvdata(dev);559	int ret = 0;560 561	mutex_lock(&data->dev_access_lock);562 563	switch (type) {564	case hwmon_temp:565		ret = cc2_measurement(data, type, val);566		break;567	case hwmon_humidity:568		switch (attr) {569		case hwmon_humidity_input:570			ret = cc2_measurement(data, type, val);571			break;572		case hwmon_humidity_min:573			ret = cc2_get_reg_val(data, CC2_R_ALARM_L_ON, val);574			break;575		case hwmon_humidity_min_hyst:576			ret = cc2_get_reg_val(data, CC2_R_ALARM_L_OFF, val);577			break;578		case hwmon_humidity_max:579			ret = cc2_get_reg_val(data, CC2_R_ALARM_H_ON, val);580			break;581		case hwmon_humidity_max_hyst:582			ret = cc2_get_reg_val(data, CC2_R_ALARM_H_OFF, val);583			break;584		case hwmon_humidity_min_alarm:585			ret = cc2_humidity_min_alarm_status(data, val);586			break;587		case hwmon_humidity_max_alarm:588			ret = cc2_humidity_max_alarm_status(data, val);589			break;590		default:591			ret = -EOPNOTSUPP;592		}593		break;594	default:595		ret = -EOPNOTSUPP;596	}597 598	mutex_unlock(&data->dev_access_lock);599 600	return ret;601}602 603static int cc2_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,604		     int channel, long val)605{606	struct cc2_data *data = dev_get_drvdata(dev);607	int ret;608	u16 arg;609	u8 cmd;610 611	if (type != hwmon_humidity)612		return -EOPNOTSUPP;613 614	if (val < 0 || val > CC2_RH_MAX)615		return -EINVAL;616 617	mutex_lock(&data->dev_access_lock);618 619	switch (attr) {620	case hwmon_humidity_min:621		cmd = CC2_W_ALARM_L_ON;622		arg = cc2_rh_to_reg(val);623		ret = cc2_write_reg(data, cmd, arg);624		break;625 626	case hwmon_humidity_min_hyst:627		cmd = CC2_W_ALARM_L_OFF;628		arg = cc2_rh_to_reg(val);629		ret = cc2_write_reg(data, cmd, arg);630		break;631 632	case hwmon_humidity_max:633		cmd = CC2_W_ALARM_H_ON;634		arg = cc2_rh_to_reg(val);635		ret = cc2_write_reg(data, cmd, arg);636		break;637 638	case hwmon_humidity_max_hyst:639		cmd = CC2_W_ALARM_H_OFF;640		arg = cc2_rh_to_reg(val);641		ret = cc2_write_reg(data, cmd, arg);642		break;643 644	default:645		ret = -EOPNOTSUPP;646		break;647	}648 649	mutex_unlock(&data->dev_access_lock);650 651	return ret;652}653 654static int cc2_request_ready_irq(struct cc2_data *data, struct device *dev)655{656	int ret = 0;657 658	data->irq_ready = fwnode_irq_get_byname(dev_fwnode(dev), "ready");659	if (data->irq_ready > 0) {660		init_completion(&data->complete);661		ret = devm_request_threaded_irq(dev, data->irq_ready, NULL,662						cc2_ready_interrupt,663						IRQF_ONESHOT |664						IRQF_TRIGGER_RISING,665						dev_name(dev), data);666	}667 668	return ret;669}670 671static int cc2_request_alarm_irqs(struct cc2_data *data, struct device *dev)672{673	int ret = 0;674 675	data->irq_low = fwnode_irq_get_byname(dev_fwnode(dev), "low");676	if (data->irq_low > 0) {677		ret = devm_request_threaded_irq(dev, data->irq_low, NULL,678						cc2_low_interrupt,679						IRQF_ONESHOT |680						IRQF_TRIGGER_RISING,681						dev_name(dev), data);682		if (ret)683			return ret;684 685		data->rh_alarm.low_alarm_visible = true;686	}687 688	data->irq_high = fwnode_irq_get_byname(dev_fwnode(dev), "high");689	if (data->irq_high > 0) {690		ret = devm_request_threaded_irq(dev, data->irq_high, NULL,691						cc2_high_interrupt,692						IRQF_ONESHOT |693						IRQF_TRIGGER_RISING,694						dev_name(dev), data);695		if (ret)696			return ret;697 698		data->rh_alarm.high_alarm_visible = true;699	}700 701	return ret;702}703 704static const struct hwmon_channel_info *cc2_info[] = {705	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),706	HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT | HWMON_H_MIN | HWMON_H_MAX |707			   HWMON_H_MIN_HYST | HWMON_H_MAX_HYST |708			   HWMON_H_MIN_ALARM | HWMON_H_MAX_ALARM),709	NULL710};711 712static const struct hwmon_ops cc2_hwmon_ops = {713	.is_visible = cc2_is_visible,714	.read = cc2_read,715	.write = cc2_write,716};717 718static const struct hwmon_chip_info cc2_chip_info = {719	.ops = &cc2_hwmon_ops,720	.info = cc2_info,721};722 723static int cc2_probe(struct i2c_client *client)724{725	struct cc2_data *data;726	struct device *dev = &client->dev;727	int ret;728 729	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))730		return -EOPNOTSUPP;731 732	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);733	if (!data)734		return -ENOMEM;735 736	i2c_set_clientdata(client, data);737 738	mutex_init(&data->dev_access_lock);739 740	data->client = client;741 742	data->regulator = devm_regulator_get_exclusive(dev, "vdd");743	if (IS_ERR(data->regulator))744		return dev_err_probe(dev, PTR_ERR(data->regulator),745				     "Failed to get regulator\n");746 747	ret = cc2_request_ready_irq(data, dev);748	if (ret)749		return dev_err_probe(dev, ret, "Failed to request ready irq\n");750 751	ret = cc2_request_alarm_irqs(data, dev);752	if (ret)753		return dev_err_probe(dev, ret, "Failed to request alarm irqs\n");754 755	data->hwmon = devm_hwmon_device_register_with_info(dev, client->name,756							   data, &cc2_chip_info,757							   NULL);758	if (IS_ERR(data->hwmon))759		return dev_err_probe(dev, PTR_ERR(data->hwmon),760				     "Failed to register hwmon device\n");761 762	return 0;763}764 765static void cc2_remove(struct i2c_client *client)766{767	struct cc2_data *data = i2c_get_clientdata(client);768 769	cc2_disable(data);770}771 772static const struct i2c_device_id cc2_id[] = {773	{ "cc2d23" },774	{ "cc2d23s" },775	{ "cc2d25" },776	{ "cc2d25s" },777	{ "cc2d33" },778	{ "cc2d33s" },779	{ "cc2d35" },780	{ "cc2d35s" },781	{ }782};783MODULE_DEVICE_TABLE(i2c, cc2_id);784 785static const struct of_device_id cc2_of_match[] = {786	{ .compatible = "amphenol,cc2d23" },787	{ .compatible = "amphenol,cc2d23s" },788	{ .compatible = "amphenol,cc2d25" },789	{ .compatible = "amphenol,cc2d25s" },790	{ .compatible = "amphenol,cc2d33" },791	{ .compatible = "amphenol,cc2d33s" },792	{ .compatible = "amphenol,cc2d35" },793	{ .compatible = "amphenol,cc2d35s" },794	{ },795};796MODULE_DEVICE_TABLE(of, cc2_of_match);797 798static struct i2c_driver cc2_driver = {799	.driver = {800		.name	= "cc2d23",801		.of_match_table = cc2_of_match,802	},803	.probe		= cc2_probe,804	.remove		= cc2_remove,805	.id_table = cc2_id,806};807module_i2c_driver(cc2_driver);808 809MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gamil.com>");810MODULE_DESCRIPTION("Amphenol ChipCap 2 humidity and temperature sensor driver");811MODULE_LICENSE("GPL");812