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