13af35092SJavier Carrasco // SPDX-License-Identifier: GPL-2.0+ 23af35092SJavier Carrasco /* 33af35092SJavier Carrasco * cc2.c - Support for the Amphenol ChipCap 2 relative humidity, temperature sensor 43af35092SJavier Carrasco * 53af35092SJavier Carrasco * Part numbers supported: 63af35092SJavier Carrasco * CC2D23, CC2D23S, CC2D25, CC2D25S, CC2D33, CC2D33S, CC2D35, CC2D35S 73af35092SJavier Carrasco * 83af35092SJavier Carrasco * Author: Javier Carrasco <javier.carrasco.cruz@gmail.com> 93af35092SJavier Carrasco * 103af35092SJavier Carrasco * Datasheet and application notes: 113af35092SJavier Carrasco * https://www.amphenol-sensors.com/en/telaire/humidity/527-humidity-sensors/3095-chipcap-2 123af35092SJavier Carrasco */ 133af35092SJavier Carrasco 143af35092SJavier Carrasco #include <linux/bitfield.h> 153af35092SJavier Carrasco #include <linux/bits.h> 163af35092SJavier Carrasco #include <linux/completion.h> 173af35092SJavier Carrasco #include <linux/delay.h> 183af35092SJavier Carrasco #include <linux/hwmon.h> 193af35092SJavier Carrasco #include <linux/i2c.h> 203af35092SJavier Carrasco #include <linux/interrupt.h> 213af35092SJavier Carrasco #include <linux/irq.h> 223af35092SJavier Carrasco #include <linux/module.h> 233af35092SJavier Carrasco #include <linux/regulator/consumer.h> 243af35092SJavier Carrasco 253af35092SJavier Carrasco #define CC2_START_CM 0xA0 263af35092SJavier Carrasco #define CC2_START_NOM 0x80 273af35092SJavier Carrasco #define CC2_R_ALARM_H_ON 0x18 283af35092SJavier Carrasco #define CC2_R_ALARM_H_OFF 0x19 293af35092SJavier Carrasco #define CC2_R_ALARM_L_ON 0x1A 303af35092SJavier Carrasco #define CC2_R_ALARM_L_OFF 0x1B 313af35092SJavier Carrasco #define CC2_RW_OFFSET 0x40 323af35092SJavier Carrasco #define CC2_W_ALARM_H_ON (CC2_R_ALARM_H_ON + CC2_RW_OFFSET) 333af35092SJavier Carrasco #define CC2_W_ALARM_H_OFF (CC2_R_ALARM_H_OFF + CC2_RW_OFFSET) 343af35092SJavier Carrasco #define CC2_W_ALARM_L_ON (CC2_R_ALARM_L_ON + CC2_RW_OFFSET) 353af35092SJavier Carrasco #define CC2_W_ALARM_L_OFF (CC2_R_ALARM_L_OFF + CC2_RW_OFFSET) 363af35092SJavier Carrasco 373af35092SJavier Carrasco #define CC2_STATUS_FIELD GENMASK(7, 6) 383af35092SJavier Carrasco #define CC2_STATUS_VALID_DATA 0x00 393af35092SJavier Carrasco #define CC2_STATUS_STALE_DATA 0x01 403af35092SJavier Carrasco #define CC2_STATUS_CMD_MODE 0x02 413af35092SJavier Carrasco 423af35092SJavier Carrasco #define CC2_RESPONSE_FIELD GENMASK(1, 0) 433af35092SJavier Carrasco #define CC2_RESPONSE_BUSY 0x00 443af35092SJavier Carrasco #define CC2_RESPONSE_ACK 0x01 453af35092SJavier Carrasco #define CC2_RESPONSE_NACK 0x02 463af35092SJavier Carrasco 473af35092SJavier Carrasco #define CC2_ERR_CORR_EEPROM BIT(2) 483af35092SJavier Carrasco #define CC2_ERR_UNCORR_EEPROM BIT(3) 493af35092SJavier Carrasco #define CC2_ERR_RAM_PARITY BIT(4) 503af35092SJavier Carrasco #define CC2_ERR_CONFIG_LOAD BIT(5) 513af35092SJavier Carrasco 523af35092SJavier Carrasco #define CC2_EEPROM_SIZE 10 533af35092SJavier Carrasco #define CC2_EEPROM_DATA_LEN 3 543af35092SJavier Carrasco #define CC2_MEASUREMENT_DATA_LEN 4 553af35092SJavier Carrasco 563af35092SJavier Carrasco #define CC2_RH_DATA_FIELD GENMASK(13, 0) 573af35092SJavier Carrasco 583af35092SJavier Carrasco /* ensure clean off -> on transitions */ 593af35092SJavier Carrasco #define CC2_POWER_CYCLE_MS 80 603af35092SJavier Carrasco 613af35092SJavier Carrasco #define CC2_STARTUP_TO_DATA_MS 55 623af35092SJavier Carrasco #define CC2_RESP_START_CM_US 100 633af35092SJavier Carrasco #define CC2_RESP_EEPROM_R_US 100 643af35092SJavier Carrasco #define CC2_RESP_EEPROM_W_MS 12 653af35092SJavier Carrasco #define CC2_STARTUP_TIME_US 1250 663af35092SJavier Carrasco 673af35092SJavier Carrasco #define CC2_RH_MAX (100 * 1000U) 683af35092SJavier Carrasco 693af35092SJavier Carrasco #define CC2_CM_RETRIES 5 703af35092SJavier Carrasco 713af35092SJavier Carrasco struct cc2_rh_alarm_info { 723af35092SJavier Carrasco bool low_alarm; 733af35092SJavier Carrasco bool high_alarm; 743af35092SJavier Carrasco bool low_alarm_visible; 753af35092SJavier Carrasco bool high_alarm_visible; 763af35092SJavier Carrasco }; 773af35092SJavier Carrasco 783af35092SJavier Carrasco struct cc2_data { 793af35092SJavier Carrasco struct cc2_rh_alarm_info rh_alarm; 803af35092SJavier Carrasco struct completion complete; 813af35092SJavier Carrasco struct device *hwmon; 823af35092SJavier Carrasco struct i2c_client *client; 833af35092SJavier Carrasco struct mutex dev_access_lock; /* device access lock */ 843af35092SJavier Carrasco struct regulator *regulator; 853af35092SJavier Carrasco const char *name; 863af35092SJavier Carrasco int irq_ready; 873af35092SJavier Carrasco int irq_low; 883af35092SJavier Carrasco int irq_high; 893af35092SJavier Carrasco bool process_irqs; 903af35092SJavier Carrasco }; 913af35092SJavier Carrasco 923af35092SJavier Carrasco enum cc2_chan_addr { 933af35092SJavier Carrasco CC2_CHAN_TEMP = 0, 943af35092SJavier Carrasco CC2_CHAN_HUMIDITY, 953af35092SJavier Carrasco }; 963af35092SJavier Carrasco 973af35092SJavier Carrasco /* %RH as a per cent mille from a register value */ 983af35092SJavier Carrasco static long cc2_rh_convert(u16 data) 993af35092SJavier Carrasco { 1003af35092SJavier Carrasco unsigned long tmp = (data & CC2_RH_DATA_FIELD) * CC2_RH_MAX; 1013af35092SJavier Carrasco 1023af35092SJavier Carrasco return tmp / ((1 << 14) - 1); 1033af35092SJavier Carrasco } 1043af35092SJavier Carrasco 1053af35092SJavier Carrasco /* convert %RH to a register value */ 1063af35092SJavier Carrasco static u16 cc2_rh_to_reg(long data) 1073af35092SJavier Carrasco { 1083af35092SJavier Carrasco return data * ((1 << 14) - 1) / CC2_RH_MAX; 1093af35092SJavier Carrasco } 1103af35092SJavier Carrasco 1113af35092SJavier Carrasco /* temperature in milli degrees celsius from a register value */ 1123af35092SJavier Carrasco static long cc2_temp_convert(u16 data) 1133af35092SJavier Carrasco { 1143af35092SJavier Carrasco unsigned long tmp = ((data >> 2) * 165 * 1000U) / ((1 << 14) - 1); 1153af35092SJavier Carrasco 1163af35092SJavier Carrasco return tmp - 40 * 1000U; 1173af35092SJavier Carrasco } 1183af35092SJavier Carrasco 1193af35092SJavier Carrasco static int cc2_enable(struct cc2_data *data) 1203af35092SJavier Carrasco { 1213af35092SJavier Carrasco int ret; 1223af35092SJavier Carrasco 1233af35092SJavier Carrasco /* exclusive regulator, check in case a disable failed */ 1243af35092SJavier Carrasco if (regulator_is_enabled(data->regulator)) 1253af35092SJavier Carrasco return 0; 1263af35092SJavier Carrasco 1273af35092SJavier Carrasco /* clear any pending completion */ 1283af35092SJavier Carrasco try_wait_for_completion(&data->complete); 1293af35092SJavier Carrasco 1303af35092SJavier Carrasco ret = regulator_enable(data->regulator); 1313af35092SJavier Carrasco if (ret < 0) 1323af35092SJavier Carrasco return ret; 1333af35092SJavier Carrasco 1343af35092SJavier Carrasco usleep_range(CC2_STARTUP_TIME_US, CC2_STARTUP_TIME_US + 125); 1353af35092SJavier Carrasco 1363af35092SJavier Carrasco data->process_irqs = true; 1373af35092SJavier Carrasco 1383af35092SJavier Carrasco return 0; 1393af35092SJavier Carrasco } 1403af35092SJavier Carrasco 1413af35092SJavier Carrasco static void cc2_disable(struct cc2_data *data) 1423af35092SJavier Carrasco { 1433af35092SJavier Carrasco int err; 1443af35092SJavier Carrasco 1453af35092SJavier Carrasco /* ignore alarms triggered by voltage toggling when powering up */ 1463af35092SJavier Carrasco data->process_irqs = false; 1473af35092SJavier Carrasco 1483af35092SJavier Carrasco /* exclusive regulator, check in case an enable failed */ 1493af35092SJavier Carrasco if (regulator_is_enabled(data->regulator)) { 1503af35092SJavier Carrasco err = regulator_disable(data->regulator); 1513af35092SJavier Carrasco if (err) 1523af35092SJavier Carrasco dev_dbg(&data->client->dev, "Failed to disable device"); 1533af35092SJavier Carrasco } 1543af35092SJavier Carrasco } 1553af35092SJavier Carrasco 1563af35092SJavier Carrasco static int cc2_cmd_response_diagnostic(struct device *dev, u8 status) 1573af35092SJavier Carrasco { 1583af35092SJavier Carrasco int resp; 1593af35092SJavier Carrasco 1603af35092SJavier Carrasco if (FIELD_GET(CC2_STATUS_FIELD, status) != CC2_STATUS_CMD_MODE) { 1613af35092SJavier Carrasco dev_dbg(dev, "Command sent out of command window\n"); 1623af35092SJavier Carrasco return -ETIMEDOUT; 1633af35092SJavier Carrasco } 1643af35092SJavier Carrasco 1653af35092SJavier Carrasco resp = FIELD_GET(CC2_RESPONSE_FIELD, status); 1663af35092SJavier Carrasco switch (resp) { 1673af35092SJavier Carrasco case CC2_RESPONSE_ACK: 1683af35092SJavier Carrasco return 0; 1693af35092SJavier Carrasco case CC2_RESPONSE_BUSY: 1703af35092SJavier Carrasco return -EBUSY; 1713af35092SJavier Carrasco case CC2_RESPONSE_NACK: 1723af35092SJavier Carrasco if (resp & CC2_ERR_CORR_EEPROM) 1733af35092SJavier Carrasco dev_dbg(dev, "Command failed: corrected EEPROM\n"); 1743af35092SJavier Carrasco if (resp & CC2_ERR_UNCORR_EEPROM) 1753af35092SJavier Carrasco dev_dbg(dev, "Command failed: uncorrected EEPROM\n"); 1763af35092SJavier Carrasco if (resp & CC2_ERR_RAM_PARITY) 1773af35092SJavier Carrasco dev_dbg(dev, "Command failed: RAM parity\n"); 1783af35092SJavier Carrasco if (resp & CC2_ERR_RAM_PARITY) 1793af35092SJavier Carrasco dev_dbg(dev, "Command failed: configuration error\n"); 1803af35092SJavier Carrasco return -ENODATA; 1813af35092SJavier Carrasco default: 1823af35092SJavier Carrasco dev_dbg(dev, "Unknown command reply\n"); 1833af35092SJavier Carrasco return -EINVAL; 1843af35092SJavier Carrasco } 1853af35092SJavier Carrasco } 1863af35092SJavier Carrasco 1873af35092SJavier Carrasco static int cc2_read_command_status(struct i2c_client *client) 1883af35092SJavier Carrasco { 1893af35092SJavier Carrasco u8 status; 1903af35092SJavier Carrasco int ret; 1913af35092SJavier Carrasco 1923af35092SJavier Carrasco ret = i2c_master_recv(client, &status, 1); 1933af35092SJavier Carrasco if (ret != 1) { 1943af35092SJavier Carrasco ret = ret < 0 ? ret : -EIO; 1953af35092SJavier Carrasco return ret; 1963af35092SJavier Carrasco } 1973af35092SJavier Carrasco 1983af35092SJavier Carrasco return cc2_cmd_response_diagnostic(&client->dev, status); 1993af35092SJavier Carrasco } 2003af35092SJavier Carrasco 2013af35092SJavier Carrasco /* 2023af35092SJavier Carrasco * The command mode is only accessible after sending the START_CM command in the 2033af35092SJavier Carrasco * first 10 ms after power-up. Only in case the command window is missed, 2043af35092SJavier Carrasco * CC2_CM_RETRIES retries are attempted before giving up and returning an error. 2053af35092SJavier Carrasco */ 2063af35092SJavier Carrasco static int cc2_command_mode_start(struct cc2_data *data) 2073af35092SJavier Carrasco { 2083af35092SJavier Carrasco unsigned long timeout; 2093af35092SJavier Carrasco int i, ret; 2103af35092SJavier Carrasco 2113af35092SJavier Carrasco for (i = 0; i < CC2_CM_RETRIES; i++) { 2123af35092SJavier Carrasco ret = cc2_enable(data); 2133af35092SJavier Carrasco if (ret < 0) 2143af35092SJavier Carrasco return ret; 2153af35092SJavier Carrasco 2163af35092SJavier Carrasco ret = i2c_smbus_write_word_data(data->client, CC2_START_CM, 0); 2173af35092SJavier Carrasco if (ret < 0) 2183af35092SJavier Carrasco return ret; 2193af35092SJavier Carrasco 2203af35092SJavier Carrasco if (data->irq_ready > 0) { 2213af35092SJavier Carrasco timeout = usecs_to_jiffies(2 * CC2_RESP_START_CM_US); 2223af35092SJavier Carrasco ret = wait_for_completion_timeout(&data->complete, 2233af35092SJavier Carrasco timeout); 2243af35092SJavier Carrasco if (!ret) 2253af35092SJavier Carrasco return -ETIMEDOUT; 2263af35092SJavier Carrasco } else { 2273af35092SJavier Carrasco usleep_range(CC2_RESP_START_CM_US, 2283af35092SJavier Carrasco 2 * CC2_RESP_START_CM_US); 2293af35092SJavier Carrasco } 2303af35092SJavier Carrasco ret = cc2_read_command_status(data->client); 2313af35092SJavier Carrasco if (ret != -ETIMEDOUT || i == CC2_CM_RETRIES) 2323af35092SJavier Carrasco break; 2333af35092SJavier Carrasco 2343af35092SJavier Carrasco /* command window missed, prepare for a retry */ 2353af35092SJavier Carrasco cc2_disable(data); 2363af35092SJavier Carrasco msleep(CC2_POWER_CYCLE_MS); 2373af35092SJavier Carrasco } 2383af35092SJavier Carrasco 2393af35092SJavier Carrasco return ret; 2403af35092SJavier Carrasco } 2413af35092SJavier Carrasco 2423af35092SJavier Carrasco /* Sending a Start_NOM command finishes the command mode immediately with no 2433af35092SJavier Carrasco * reply and the device enters normal operation mode 2443af35092SJavier Carrasco */ 2453af35092SJavier Carrasco static int cc2_command_mode_finish(struct cc2_data *data) 2463af35092SJavier Carrasco { 2473af35092SJavier Carrasco int ret; 2483af35092SJavier Carrasco 2493af35092SJavier Carrasco ret = i2c_smbus_write_word_data(data->client, CC2_START_NOM, 0); 2503af35092SJavier Carrasco if (ret < 0) 2513af35092SJavier Carrasco return ret; 2523af35092SJavier Carrasco 2533af35092SJavier Carrasco return 0; 2543af35092SJavier Carrasco } 2553af35092SJavier Carrasco 2563af35092SJavier Carrasco static int cc2_write_reg(struct cc2_data *data, u8 reg, u16 val) 2573af35092SJavier Carrasco { 2583af35092SJavier Carrasco unsigned long timeout; 2593af35092SJavier Carrasco int ret; 2603af35092SJavier Carrasco 2613af35092SJavier Carrasco ret = cc2_command_mode_start(data); 2623af35092SJavier Carrasco if (ret < 0) 2633af35092SJavier Carrasco goto disable; 2643af35092SJavier Carrasco 2653af35092SJavier Carrasco cpu_to_be16s(&val); 2663af35092SJavier Carrasco ret = i2c_smbus_write_word_data(data->client, reg, val); 2673af35092SJavier Carrasco if (ret < 0) 2683af35092SJavier Carrasco goto disable; 2693af35092SJavier Carrasco 2703af35092SJavier Carrasco if (data->irq_ready > 0) { 2713af35092SJavier Carrasco timeout = msecs_to_jiffies(2 * CC2_RESP_EEPROM_W_MS); 2723af35092SJavier Carrasco ret = wait_for_completion_timeout(&data->complete, timeout); 2733af35092SJavier Carrasco if (!ret) { 2743af35092SJavier Carrasco ret = -ETIMEDOUT; 2753af35092SJavier Carrasco goto disable; 2763af35092SJavier Carrasco } 2773af35092SJavier Carrasco } else { 2783af35092SJavier Carrasco msleep(CC2_RESP_EEPROM_W_MS); 2793af35092SJavier Carrasco } 2803af35092SJavier Carrasco 2813af35092SJavier Carrasco ret = cc2_read_command_status(data->client); 2823af35092SJavier Carrasco 2833af35092SJavier Carrasco disable: 2843af35092SJavier Carrasco cc2_disable(data); 2853af35092SJavier Carrasco 2863af35092SJavier Carrasco return ret; 2873af35092SJavier Carrasco } 2883af35092SJavier Carrasco 2893af35092SJavier Carrasco static int cc2_read_reg(struct cc2_data *data, u8 reg, u16 *val) 2903af35092SJavier Carrasco { 2913af35092SJavier Carrasco u8 buf[CC2_EEPROM_DATA_LEN]; 2923af35092SJavier Carrasco unsigned long timeout; 2933af35092SJavier Carrasco int ret; 2943af35092SJavier Carrasco 2953af35092SJavier Carrasco ret = cc2_command_mode_start(data); 2963af35092SJavier Carrasco if (ret < 0) 2973af35092SJavier Carrasco return ret; 2983af35092SJavier Carrasco 2993af35092SJavier Carrasco ret = i2c_smbus_write_word_data(data->client, reg, 0); 3003af35092SJavier Carrasco if (ret < 0) 3013af35092SJavier Carrasco return ret; 3023af35092SJavier Carrasco 3033af35092SJavier Carrasco if (data->irq_ready > 0) { 3043af35092SJavier Carrasco timeout = usecs_to_jiffies(2 * CC2_RESP_EEPROM_R_US); 3053af35092SJavier Carrasco ret = wait_for_completion_timeout(&data->complete, timeout); 3063af35092SJavier Carrasco if (!ret) 3073af35092SJavier Carrasco return -ETIMEDOUT; 3083af35092SJavier Carrasco 3093af35092SJavier Carrasco } else { 3103af35092SJavier Carrasco usleep_range(CC2_RESP_EEPROM_R_US, CC2_RESP_EEPROM_R_US + 10); 3113af35092SJavier Carrasco } 3123af35092SJavier Carrasco ret = i2c_master_recv(data->client, buf, CC2_EEPROM_DATA_LEN); 3133af35092SJavier Carrasco if (ret != CC2_EEPROM_DATA_LEN) 3143af35092SJavier Carrasco return ret < 0 ? ret : -EIO; 3153af35092SJavier Carrasco 3163af35092SJavier Carrasco *val = be16_to_cpup((__be16 *)&buf[1]); 3173af35092SJavier Carrasco 3183af35092SJavier Carrasco return cc2_read_command_status(data->client); 3193af35092SJavier Carrasco } 3203af35092SJavier Carrasco 3213af35092SJavier Carrasco static int cc2_get_reg_val(struct cc2_data *data, u8 reg, long *val) 3223af35092SJavier Carrasco { 3233af35092SJavier Carrasco u16 reg_val; 3243af35092SJavier Carrasco int ret; 3253af35092SJavier Carrasco 3263af35092SJavier Carrasco ret = cc2_read_reg(data, reg, ®_val); 327f16fb6d2SJavier Carrasco if (!ret) 3283af35092SJavier Carrasco *val = cc2_rh_convert(reg_val); 329f16fb6d2SJavier Carrasco 3303af35092SJavier Carrasco cc2_disable(data); 3313af35092SJavier Carrasco 3323af35092SJavier Carrasco return ret; 3333af35092SJavier Carrasco } 3343af35092SJavier Carrasco 3353af35092SJavier Carrasco static int cc2_data_fetch(struct i2c_client *client, 3363af35092SJavier Carrasco enum hwmon_sensor_types type, long *val) 3373af35092SJavier Carrasco { 3383af35092SJavier Carrasco u8 data[CC2_MEASUREMENT_DATA_LEN]; 3393af35092SJavier Carrasco u8 status; 3403af35092SJavier Carrasco int ret; 3413af35092SJavier Carrasco 3423af35092SJavier Carrasco ret = i2c_master_recv(client, data, CC2_MEASUREMENT_DATA_LEN); 3433af35092SJavier Carrasco if (ret != CC2_MEASUREMENT_DATA_LEN) { 3443af35092SJavier Carrasco ret = ret < 0 ? ret : -EIO; 3453af35092SJavier Carrasco return ret; 3463af35092SJavier Carrasco } 3473af35092SJavier Carrasco status = FIELD_GET(CC2_STATUS_FIELD, data[0]); 3483af35092SJavier Carrasco if (status == CC2_STATUS_STALE_DATA) 3493af35092SJavier Carrasco return -EBUSY; 3503af35092SJavier Carrasco 3513af35092SJavier Carrasco if (status != CC2_STATUS_VALID_DATA) 3523af35092SJavier Carrasco return -EIO; 3533af35092SJavier Carrasco 3543af35092SJavier Carrasco switch (type) { 3553af35092SJavier Carrasco case hwmon_humidity: 3563af35092SJavier Carrasco *val = cc2_rh_convert(be16_to_cpup((__be16 *)&data[0])); 3573af35092SJavier Carrasco break; 3583af35092SJavier Carrasco case hwmon_temp: 3593af35092SJavier Carrasco *val = cc2_temp_convert(be16_to_cpup((__be16 *)&data[2])); 3603af35092SJavier Carrasco break; 3613af35092SJavier Carrasco default: 3623af35092SJavier Carrasco return -EINVAL; 3633af35092SJavier Carrasco } 3643af35092SJavier Carrasco 3653af35092SJavier Carrasco return 0; 3663af35092SJavier Carrasco } 3673af35092SJavier Carrasco 3683af35092SJavier Carrasco static int cc2_read_measurement(struct cc2_data *data, 3693af35092SJavier Carrasco enum hwmon_sensor_types type, long *val) 3703af35092SJavier Carrasco { 3713af35092SJavier Carrasco unsigned long timeout; 3723af35092SJavier Carrasco int ret; 3733af35092SJavier Carrasco 3743af35092SJavier Carrasco if (data->irq_ready > 0) { 3753af35092SJavier Carrasco timeout = msecs_to_jiffies(CC2_STARTUP_TO_DATA_MS * 2); 3763af35092SJavier Carrasco ret = wait_for_completion_timeout(&data->complete, timeout); 3773af35092SJavier Carrasco if (!ret) 3783af35092SJavier Carrasco return -ETIMEDOUT; 3793af35092SJavier Carrasco 3803af35092SJavier Carrasco } else { 3813af35092SJavier Carrasco msleep(CC2_STARTUP_TO_DATA_MS); 3823af35092SJavier Carrasco } 3833af35092SJavier Carrasco 3843af35092SJavier Carrasco ret = cc2_data_fetch(data->client, type, val); 3853af35092SJavier Carrasco 3863af35092SJavier Carrasco return ret; 3873af35092SJavier Carrasco } 3883af35092SJavier Carrasco 3893af35092SJavier Carrasco /* 3903af35092SJavier Carrasco * A measurement requires enabling the device, waiting for the automatic 3913af35092SJavier Carrasco * measurement to finish, reading the measurement data and disabling the device 3923af35092SJavier Carrasco * again. 3933af35092SJavier Carrasco */ 3943af35092SJavier Carrasco static int cc2_measurement(struct cc2_data *data, enum hwmon_sensor_types type, 3953af35092SJavier Carrasco long *val) 3963af35092SJavier Carrasco { 3973af35092SJavier Carrasco int ret; 3983af35092SJavier Carrasco 3993af35092SJavier Carrasco ret = cc2_enable(data); 4003af35092SJavier Carrasco if (ret) 4013af35092SJavier Carrasco return ret; 4023af35092SJavier Carrasco 4033af35092SJavier Carrasco ret = cc2_read_measurement(data, type, val); 4043af35092SJavier Carrasco 4053af35092SJavier Carrasco cc2_disable(data); 4063af35092SJavier Carrasco 4073af35092SJavier Carrasco return ret; 4083af35092SJavier Carrasco } 4093af35092SJavier Carrasco 4103af35092SJavier Carrasco /* 4113af35092SJavier Carrasco * In order to check alarm status, the corresponding ALARM_OFF (hysteresis) 4123af35092SJavier Carrasco * register must be read and a new measurement must be carried out to trigger 4133af35092SJavier Carrasco * the alarm signals. Given that the device carries out a measurement after 4143af35092SJavier Carrasco * exiting the command mode, there is no need to force two power-up sequences. 4153af35092SJavier Carrasco * Instead, a NOM command is sent and the device is disabled after the 4163af35092SJavier Carrasco * measurement is read. 4173af35092SJavier Carrasco */ 4183af35092SJavier Carrasco static int cc2_read_hyst_and_measure(struct cc2_data *data, u8 reg, 4193af35092SJavier Carrasco long *hyst, long *measurement) 4203af35092SJavier Carrasco { 4213af35092SJavier Carrasco u16 reg_val; 4223af35092SJavier Carrasco int ret; 4233af35092SJavier Carrasco 4243af35092SJavier Carrasco ret = cc2_read_reg(data, reg, ®_val); 4253af35092SJavier Carrasco if (ret) 4263af35092SJavier Carrasco goto disable; 4273af35092SJavier Carrasco 4283af35092SJavier Carrasco *hyst = cc2_rh_convert(reg_val); 4293af35092SJavier Carrasco 4303af35092SJavier Carrasco ret = cc2_command_mode_finish(data); 4313af35092SJavier Carrasco if (ret) 4323af35092SJavier Carrasco goto disable; 4333af35092SJavier Carrasco 4343af35092SJavier Carrasco ret = cc2_read_measurement(data, hwmon_humidity, measurement); 4353af35092SJavier Carrasco 4363af35092SJavier Carrasco disable: 4373af35092SJavier Carrasco cc2_disable(data); 4383af35092SJavier Carrasco 4393af35092SJavier Carrasco return ret; 4403af35092SJavier Carrasco } 4413af35092SJavier Carrasco 4423af35092SJavier Carrasco static umode_t cc2_is_visible(const void *data, enum hwmon_sensor_types type, 4433af35092SJavier Carrasco u32 attr, int channel) 4443af35092SJavier Carrasco { 4453af35092SJavier Carrasco const struct cc2_data *cc2 = data; 4463af35092SJavier Carrasco 4473af35092SJavier Carrasco switch (type) { 4483af35092SJavier Carrasco case hwmon_humidity: 4493af35092SJavier Carrasco switch (attr) { 4503af35092SJavier Carrasco case hwmon_humidity_input: 4513af35092SJavier Carrasco return 0444; 4523af35092SJavier Carrasco case hwmon_humidity_min_alarm: 4533af35092SJavier Carrasco return cc2->rh_alarm.low_alarm_visible ? 0444 : 0; 4543af35092SJavier Carrasco case hwmon_humidity_max_alarm: 4553af35092SJavier Carrasco return cc2->rh_alarm.high_alarm_visible ? 0444 : 0; 4563af35092SJavier Carrasco case hwmon_humidity_min: 4573af35092SJavier Carrasco case hwmon_humidity_min_hyst: 4583af35092SJavier Carrasco return cc2->rh_alarm.low_alarm_visible ? 0644 : 0; 4593af35092SJavier Carrasco case hwmon_humidity_max: 4603af35092SJavier Carrasco case hwmon_humidity_max_hyst: 4613af35092SJavier Carrasco return cc2->rh_alarm.high_alarm_visible ? 0644 : 0; 4623af35092SJavier Carrasco default: 4633af35092SJavier Carrasco return 0; 4643af35092SJavier Carrasco } 4653af35092SJavier Carrasco case hwmon_temp: 4663af35092SJavier Carrasco switch (attr) { 4673af35092SJavier Carrasco case hwmon_temp_input: 4683af35092SJavier Carrasco return 0444; 4693af35092SJavier Carrasco default: 4703af35092SJavier Carrasco return 0; 4713af35092SJavier Carrasco } 4723af35092SJavier Carrasco default: 4733af35092SJavier Carrasco break; 4743af35092SJavier Carrasco } 4753af35092SJavier Carrasco 4763af35092SJavier Carrasco return 0; 4773af35092SJavier Carrasco } 4783af35092SJavier Carrasco 4793af35092SJavier Carrasco static irqreturn_t cc2_ready_interrupt(int irq, void *data) 4803af35092SJavier Carrasco { 4813af35092SJavier Carrasco struct cc2_data *cc2 = data; 4823af35092SJavier Carrasco 4833af35092SJavier Carrasco if (cc2->process_irqs) 4843af35092SJavier Carrasco complete(&cc2->complete); 4853af35092SJavier Carrasco 4863af35092SJavier Carrasco return IRQ_HANDLED; 4873af35092SJavier Carrasco } 4883af35092SJavier Carrasco 4893af35092SJavier Carrasco static irqreturn_t cc2_low_interrupt(int irq, void *data) 4903af35092SJavier Carrasco { 4913af35092SJavier Carrasco struct cc2_data *cc2 = data; 4923af35092SJavier Carrasco 4933af35092SJavier Carrasco if (cc2->process_irqs) { 4943af35092SJavier Carrasco hwmon_notify_event(cc2->hwmon, hwmon_humidity, 4953af35092SJavier Carrasco hwmon_humidity_min_alarm, CC2_CHAN_HUMIDITY); 4963af35092SJavier Carrasco cc2->rh_alarm.low_alarm = true; 4973af35092SJavier Carrasco } 4983af35092SJavier Carrasco 4993af35092SJavier Carrasco return IRQ_HANDLED; 5003af35092SJavier Carrasco } 5013af35092SJavier Carrasco 5023af35092SJavier Carrasco static irqreturn_t cc2_high_interrupt(int irq, void *data) 5033af35092SJavier Carrasco { 5043af35092SJavier Carrasco struct cc2_data *cc2 = data; 5053af35092SJavier Carrasco 5063af35092SJavier Carrasco if (cc2->process_irqs) { 5073af35092SJavier Carrasco hwmon_notify_event(cc2->hwmon, hwmon_humidity, 5083af35092SJavier Carrasco hwmon_humidity_max_alarm, CC2_CHAN_HUMIDITY); 5093af35092SJavier Carrasco cc2->rh_alarm.high_alarm = true; 5103af35092SJavier Carrasco } 5113af35092SJavier Carrasco 5123af35092SJavier Carrasco return IRQ_HANDLED; 5133af35092SJavier Carrasco } 5143af35092SJavier Carrasco 5153af35092SJavier Carrasco static int cc2_humidity_min_alarm_status(struct cc2_data *data, long *val) 5163af35092SJavier Carrasco { 5173af35092SJavier Carrasco long measurement, min_hyst; 5183af35092SJavier Carrasco int ret; 5193af35092SJavier Carrasco 5203af35092SJavier Carrasco ret = cc2_read_hyst_and_measure(data, CC2_R_ALARM_L_OFF, &min_hyst, 5213af35092SJavier Carrasco &measurement); 5223af35092SJavier Carrasco if (ret < 0) 5233af35092SJavier Carrasco return ret; 5243af35092SJavier Carrasco 5253af35092SJavier Carrasco if (data->rh_alarm.low_alarm) { 5263af35092SJavier Carrasco *val = (measurement < min_hyst) ? 1 : 0; 5273af35092SJavier Carrasco data->rh_alarm.low_alarm = *val; 5283af35092SJavier Carrasco } else { 5293af35092SJavier Carrasco *val = 0; 5303af35092SJavier Carrasco } 5313af35092SJavier Carrasco 5323af35092SJavier Carrasco return 0; 5333af35092SJavier Carrasco } 5343af35092SJavier Carrasco 5353af35092SJavier Carrasco static int cc2_humidity_max_alarm_status(struct cc2_data *data, long *val) 5363af35092SJavier Carrasco { 5373af35092SJavier Carrasco long measurement, max_hyst; 5383af35092SJavier Carrasco int ret; 5393af35092SJavier Carrasco 5403af35092SJavier Carrasco ret = cc2_read_hyst_and_measure(data, CC2_R_ALARM_H_OFF, &max_hyst, 5413af35092SJavier Carrasco &measurement); 5423af35092SJavier Carrasco if (ret < 0) 5433af35092SJavier Carrasco return ret; 5443af35092SJavier Carrasco 5453af35092SJavier Carrasco if (data->rh_alarm.high_alarm) { 5463af35092SJavier Carrasco *val = (measurement > max_hyst) ? 1 : 0; 5473af35092SJavier Carrasco data->rh_alarm.high_alarm = *val; 5483af35092SJavier Carrasco } else { 5493af35092SJavier Carrasco *val = 0; 5503af35092SJavier Carrasco } 5513af35092SJavier Carrasco 5523af35092SJavier Carrasco return 0; 5533af35092SJavier Carrasco } 5543af35092SJavier Carrasco 5553af35092SJavier Carrasco static int cc2_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, 5563af35092SJavier Carrasco int channel, long *val) 5573af35092SJavier Carrasco { 5583af35092SJavier Carrasco struct cc2_data *data = dev_get_drvdata(dev); 5593af35092SJavier Carrasco int ret = 0; 5603af35092SJavier Carrasco 5613af35092SJavier Carrasco mutex_lock(&data->dev_access_lock); 5623af35092SJavier Carrasco 5633af35092SJavier Carrasco switch (type) { 5643af35092SJavier Carrasco case hwmon_temp: 5653af35092SJavier Carrasco ret = cc2_measurement(data, type, val); 5663af35092SJavier Carrasco break; 5673af35092SJavier Carrasco case hwmon_humidity: 5683af35092SJavier Carrasco switch (attr) { 5693af35092SJavier Carrasco case hwmon_humidity_input: 5703af35092SJavier Carrasco ret = cc2_measurement(data, type, val); 5713af35092SJavier Carrasco break; 5723af35092SJavier Carrasco case hwmon_humidity_min: 5733af35092SJavier Carrasco ret = cc2_get_reg_val(data, CC2_R_ALARM_L_ON, val); 5743af35092SJavier Carrasco break; 5753af35092SJavier Carrasco case hwmon_humidity_min_hyst: 5763af35092SJavier Carrasco ret = cc2_get_reg_val(data, CC2_R_ALARM_L_OFF, val); 5773af35092SJavier Carrasco break; 5783af35092SJavier Carrasco case hwmon_humidity_max: 5793af35092SJavier Carrasco ret = cc2_get_reg_val(data, CC2_R_ALARM_H_ON, val); 5803af35092SJavier Carrasco break; 5813af35092SJavier Carrasco case hwmon_humidity_max_hyst: 5823af35092SJavier Carrasco ret = cc2_get_reg_val(data, CC2_R_ALARM_H_OFF, val); 5833af35092SJavier Carrasco break; 5843af35092SJavier Carrasco case hwmon_humidity_min_alarm: 5853af35092SJavier Carrasco ret = cc2_humidity_min_alarm_status(data, val); 5863af35092SJavier Carrasco break; 5873af35092SJavier Carrasco case hwmon_humidity_max_alarm: 5883af35092SJavier Carrasco ret = cc2_humidity_max_alarm_status(data, val); 5893af35092SJavier Carrasco break; 5903af35092SJavier Carrasco default: 5913af35092SJavier Carrasco ret = -EOPNOTSUPP; 5923af35092SJavier Carrasco } 5933af35092SJavier Carrasco break; 5943af35092SJavier Carrasco default: 5953af35092SJavier Carrasco ret = -EOPNOTSUPP; 5963af35092SJavier Carrasco } 5973af35092SJavier Carrasco 5983af35092SJavier Carrasco mutex_unlock(&data->dev_access_lock); 5993af35092SJavier Carrasco 6003af35092SJavier Carrasco return ret; 6013af35092SJavier Carrasco } 6023af35092SJavier Carrasco 6033af35092SJavier Carrasco static int cc2_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, 6043af35092SJavier Carrasco int channel, long val) 6053af35092SJavier Carrasco { 6063af35092SJavier Carrasco struct cc2_data *data = dev_get_drvdata(dev); 6073af35092SJavier Carrasco int ret; 6083af35092SJavier Carrasco u16 arg; 6093af35092SJavier Carrasco u8 cmd; 6103af35092SJavier Carrasco 6113af35092SJavier Carrasco if (type != hwmon_humidity) 6123af35092SJavier Carrasco return -EOPNOTSUPP; 6133af35092SJavier Carrasco 6143af35092SJavier Carrasco if (val < 0 || val > CC2_RH_MAX) 6153af35092SJavier Carrasco return -EINVAL; 6163af35092SJavier Carrasco 6173af35092SJavier Carrasco mutex_lock(&data->dev_access_lock); 6183af35092SJavier Carrasco 6193af35092SJavier Carrasco switch (attr) { 6203af35092SJavier Carrasco case hwmon_humidity_min: 6213af35092SJavier Carrasco cmd = CC2_W_ALARM_L_ON; 6223af35092SJavier Carrasco arg = cc2_rh_to_reg(val); 6233af35092SJavier Carrasco ret = cc2_write_reg(data, cmd, arg); 6243af35092SJavier Carrasco break; 6253af35092SJavier Carrasco 6263af35092SJavier Carrasco case hwmon_humidity_min_hyst: 6273af35092SJavier Carrasco cmd = CC2_W_ALARM_L_OFF; 6283af35092SJavier Carrasco arg = cc2_rh_to_reg(val); 6293af35092SJavier Carrasco ret = cc2_write_reg(data, cmd, arg); 6303af35092SJavier Carrasco break; 6313af35092SJavier Carrasco 6323af35092SJavier Carrasco case hwmon_humidity_max: 6333af35092SJavier Carrasco cmd = CC2_W_ALARM_H_ON; 6343af35092SJavier Carrasco arg = cc2_rh_to_reg(val); 6353af35092SJavier Carrasco ret = cc2_write_reg(data, cmd, arg); 6363af35092SJavier Carrasco break; 6373af35092SJavier Carrasco 6383af35092SJavier Carrasco case hwmon_humidity_max_hyst: 6393af35092SJavier Carrasco cmd = CC2_W_ALARM_H_OFF; 6403af35092SJavier Carrasco arg = cc2_rh_to_reg(val); 6413af35092SJavier Carrasco ret = cc2_write_reg(data, cmd, arg); 6423af35092SJavier Carrasco break; 6433af35092SJavier Carrasco 6443af35092SJavier Carrasco default: 6453af35092SJavier Carrasco ret = -EOPNOTSUPP; 6463af35092SJavier Carrasco break; 6473af35092SJavier Carrasco } 6483af35092SJavier Carrasco 6493af35092SJavier Carrasco mutex_unlock(&data->dev_access_lock); 6503af35092SJavier Carrasco 6513af35092SJavier Carrasco return ret; 6523af35092SJavier Carrasco } 6533af35092SJavier Carrasco 6543af35092SJavier Carrasco static int cc2_request_ready_irq(struct cc2_data *data, struct device *dev) 6553af35092SJavier Carrasco { 6563af35092SJavier Carrasco int ret = 0; 6573af35092SJavier Carrasco 6583af35092SJavier Carrasco data->irq_ready = fwnode_irq_get_byname(dev_fwnode(dev), "ready"); 6593af35092SJavier Carrasco if (data->irq_ready > 0) { 6603af35092SJavier Carrasco init_completion(&data->complete); 6613af35092SJavier Carrasco ret = devm_request_threaded_irq(dev, data->irq_ready, NULL, 6623af35092SJavier Carrasco cc2_ready_interrupt, 6633af35092SJavier Carrasco IRQF_ONESHOT | 6643af35092SJavier Carrasco IRQF_TRIGGER_RISING, 6653af35092SJavier Carrasco dev_name(dev), data); 6663af35092SJavier Carrasco } 6673af35092SJavier Carrasco 6683af35092SJavier Carrasco return ret; 6693af35092SJavier Carrasco } 6703af35092SJavier Carrasco 6713af35092SJavier Carrasco static int cc2_request_alarm_irqs(struct cc2_data *data, struct device *dev) 6723af35092SJavier Carrasco { 673*efd49b8eSJavier Carrasco int ret = 0; 6743af35092SJavier Carrasco 6753af35092SJavier Carrasco data->irq_low = fwnode_irq_get_byname(dev_fwnode(dev), "low"); 6763af35092SJavier Carrasco if (data->irq_low > 0) { 6773af35092SJavier Carrasco ret = devm_request_threaded_irq(dev, data->irq_low, NULL, 6783af35092SJavier Carrasco cc2_low_interrupt, 6793af35092SJavier Carrasco IRQF_ONESHOT | 6803af35092SJavier Carrasco IRQF_TRIGGER_RISING, 6813af35092SJavier Carrasco dev_name(dev), data); 682*efd49b8eSJavier Carrasco if (ret) 683*efd49b8eSJavier Carrasco return ret; 684*efd49b8eSJavier Carrasco 6853af35092SJavier Carrasco data->rh_alarm.low_alarm_visible = true; 6863af35092SJavier Carrasco } 6873af35092SJavier Carrasco 6883af35092SJavier Carrasco data->irq_high = fwnode_irq_get_byname(dev_fwnode(dev), "high"); 6893af35092SJavier Carrasco if (data->irq_high > 0) { 6903af35092SJavier Carrasco ret = devm_request_threaded_irq(dev, data->irq_high, NULL, 6913af35092SJavier Carrasco cc2_high_interrupt, 6923af35092SJavier Carrasco IRQF_ONESHOT | 6933af35092SJavier Carrasco IRQF_TRIGGER_RISING, 6943af35092SJavier Carrasco dev_name(dev), data); 695*efd49b8eSJavier Carrasco if (ret) 696*efd49b8eSJavier Carrasco return ret; 697*efd49b8eSJavier Carrasco 6983af35092SJavier Carrasco data->rh_alarm.high_alarm_visible = true; 6993af35092SJavier Carrasco } 7003af35092SJavier Carrasco 7013af35092SJavier Carrasco return ret; 7023af35092SJavier Carrasco } 7033af35092SJavier Carrasco 7043af35092SJavier Carrasco static const struct hwmon_channel_info *cc2_info[] = { 7053af35092SJavier Carrasco HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), 7063af35092SJavier Carrasco HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT | HWMON_H_MIN | HWMON_H_MAX | 7073af35092SJavier Carrasco HWMON_H_MIN_HYST | HWMON_H_MAX_HYST | 7083af35092SJavier Carrasco HWMON_H_MIN_ALARM | HWMON_H_MAX_ALARM), 7093af35092SJavier Carrasco NULL 7103af35092SJavier Carrasco }; 7113af35092SJavier Carrasco 7123af35092SJavier Carrasco static const struct hwmon_ops cc2_hwmon_ops = { 7133af35092SJavier Carrasco .is_visible = cc2_is_visible, 7143af35092SJavier Carrasco .read = cc2_read, 7153af35092SJavier Carrasco .write = cc2_write, 7163af35092SJavier Carrasco }; 7173af35092SJavier Carrasco 7183af35092SJavier Carrasco static const struct hwmon_chip_info cc2_chip_info = { 7193af35092SJavier Carrasco .ops = &cc2_hwmon_ops, 7203af35092SJavier Carrasco .info = cc2_info, 7213af35092SJavier Carrasco }; 7223af35092SJavier Carrasco 7233af35092SJavier Carrasco static int cc2_probe(struct i2c_client *client) 7243af35092SJavier Carrasco { 7253af35092SJavier Carrasco struct cc2_data *data; 7263af35092SJavier Carrasco struct device *dev = &client->dev; 7273af35092SJavier Carrasco int ret; 7283af35092SJavier Carrasco 7293af35092SJavier Carrasco if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 7303af35092SJavier Carrasco return -EOPNOTSUPP; 7313af35092SJavier Carrasco 7323af35092SJavier Carrasco data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 7333af35092SJavier Carrasco if (!data) 7343af35092SJavier Carrasco return -ENOMEM; 7353af35092SJavier Carrasco 7363af35092SJavier Carrasco i2c_set_clientdata(client, data); 7373af35092SJavier Carrasco 7383af35092SJavier Carrasco mutex_init(&data->dev_access_lock); 7393af35092SJavier Carrasco 7403af35092SJavier Carrasco data->client = client; 7413af35092SJavier Carrasco 7423af35092SJavier Carrasco data->regulator = devm_regulator_get_exclusive(dev, "vdd"); 7433af35092SJavier Carrasco if (IS_ERR(data->regulator)) { 7443af35092SJavier Carrasco dev_err_probe(dev, PTR_ERR(data->regulator), 7453af35092SJavier Carrasco "Failed to get regulator\n"); 7463af35092SJavier Carrasco return PTR_ERR(data->regulator); 7473af35092SJavier Carrasco } 7483af35092SJavier Carrasco 7493af35092SJavier Carrasco ret = cc2_request_ready_irq(data, dev); 7503af35092SJavier Carrasco if (ret) { 7513af35092SJavier Carrasco dev_err_probe(dev, ret, "Failed to request ready irq\n"); 7523af35092SJavier Carrasco return ret; 7533af35092SJavier Carrasco } 7543af35092SJavier Carrasco 7553af35092SJavier Carrasco ret = cc2_request_alarm_irqs(data, dev); 7563af35092SJavier Carrasco if (ret) { 7573af35092SJavier Carrasco dev_err_probe(dev, ret, "Failed to request alarm irqs\n"); 7583af35092SJavier Carrasco goto disable; 7593af35092SJavier Carrasco } 7603af35092SJavier Carrasco 7613af35092SJavier Carrasco data->hwmon = devm_hwmon_device_register_with_info(dev, client->name, 7623af35092SJavier Carrasco data, &cc2_chip_info, 7633af35092SJavier Carrasco NULL); 7643af35092SJavier Carrasco if (IS_ERR(data->hwmon)) { 7653af35092SJavier Carrasco dev_err_probe(dev, PTR_ERR(data->hwmon), 7663af35092SJavier Carrasco "Failed to register hwmon device\n"); 7673af35092SJavier Carrasco ret = PTR_ERR(data->hwmon); 7683af35092SJavier Carrasco } 7693af35092SJavier Carrasco 7703af35092SJavier Carrasco disable: 7713af35092SJavier Carrasco cc2_disable(data); 7723af35092SJavier Carrasco 7733af35092SJavier Carrasco return ret; 7743af35092SJavier Carrasco } 7753af35092SJavier Carrasco 7763af35092SJavier Carrasco static void cc2_remove(struct i2c_client *client) 7773af35092SJavier Carrasco { 7783af35092SJavier Carrasco struct cc2_data *data = i2c_get_clientdata(client); 7793af35092SJavier Carrasco 7803af35092SJavier Carrasco cc2_disable(data); 7813af35092SJavier Carrasco } 7823af35092SJavier Carrasco 7833af35092SJavier Carrasco static const struct i2c_device_id cc2_id[] = { 7843af35092SJavier Carrasco { "cc2d23" }, 7853af35092SJavier Carrasco { "cc2d23s" }, 7863af35092SJavier Carrasco { "cc2d25" }, 7873af35092SJavier Carrasco { "cc2d25s" }, 7883af35092SJavier Carrasco { "cc2d33" }, 7893af35092SJavier Carrasco { "cc2d33s" }, 7903af35092SJavier Carrasco { "cc2d35" }, 7913af35092SJavier Carrasco { "cc2d35s" }, 7923af35092SJavier Carrasco { } 7933af35092SJavier Carrasco }; 7943af35092SJavier Carrasco MODULE_DEVICE_TABLE(i2c, cc2_id); 7953af35092SJavier Carrasco 7963af35092SJavier Carrasco static const struct of_device_id cc2_of_match[] = { 7973af35092SJavier Carrasco { .compatible = "amphenol,cc2d23" }, 7983af35092SJavier Carrasco { .compatible = "amphenol,cc2d23s" }, 7993af35092SJavier Carrasco { .compatible = "amphenol,cc2d25" }, 8003af35092SJavier Carrasco { .compatible = "amphenol,cc2d25s" }, 8013af35092SJavier Carrasco { .compatible = "amphenol,cc2d33" }, 8023af35092SJavier Carrasco { .compatible = "amphenol,cc2d33s" }, 8033af35092SJavier Carrasco { .compatible = "amphenol,cc2d35" }, 8043af35092SJavier Carrasco { .compatible = "amphenol,cc2d35s" }, 8053af35092SJavier Carrasco { }, 8063af35092SJavier Carrasco }; 8073af35092SJavier Carrasco MODULE_DEVICE_TABLE(of, cc2_of_match); 8083af35092SJavier Carrasco 8093af35092SJavier Carrasco static struct i2c_driver cc2_driver = { 8103af35092SJavier Carrasco .driver = { 8113af35092SJavier Carrasco .name = "cc2d23", 8123af35092SJavier Carrasco .of_match_table = cc2_of_match, 8133af35092SJavier Carrasco }, 8143af35092SJavier Carrasco .probe = cc2_probe, 8153af35092SJavier Carrasco .remove = cc2_remove, 8163af35092SJavier Carrasco .id_table = cc2_id, 8173af35092SJavier Carrasco }; 8183af35092SJavier Carrasco module_i2c_driver(cc2_driver); 8193af35092SJavier Carrasco 8203af35092SJavier Carrasco MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gamil.com>"); 8213af35092SJavier Carrasco MODULE_DESCRIPTION("Amphenol ChipCap 2 humidity and temperature sensor driver"); 8223af35092SJavier Carrasco MODULE_LICENSE("GPL"); 823