/* * Copyright (C) 2011-2014 Freescale Semiconductor, Inc. All Rights Reserved. */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #define ISL29023_DRV_NAME "isl29023" #define DRIVER_VERSION "1.0" #define ISL29023_COMMAND1 (0x00) #define ISL29023_MODE_SHIFT (5) #define ISL29023_MODE_MASK (0x7 << ISL29023_MODE_SHIFT) #define ISL29023_INT_FLAG_SHIFT (2) #define ISL29023_INT_FLAG_MASK (0x1 << ISL29023_INT_FLAG_SHIFT) #define ISL29023_INT_PERSISTS_SHIFT (0) #define ISL29023_INT_PERSISTS_MASK (0x3 << ISL29023_INT_PERSISTS_SHIFT) #define ISL29023_COMMAND2 (0x01) #define ISL29023_RES_SHIFT (2) #define ISL29023_RES_MASK (0x3 << ISL29023_RES_SHIFT) #define ISL29023_RANGE_SHIFT (0) #define ISL29023_RANGE_MASK (0x3 << ISL29023_RANGE_SHIFT) #define ISL29023_REG_LSB_SENSOR (0x02) #define ISL29023_REG_MSB_SENSOR (0x03) #define ISL29023_REG_IRQ_TH_LO_LSB (0x04) #define ISL29023_REG_IRQ_TH_LO_MSB (0x05) #define ISL29023_REG_IRQ_TH_HI_LSB (0x06) #define ISL29023_REG_IRQ_TH_HI_MSB (0x07) #define ISL29023_NUM_CACHABLE_REGS 8 #define DEF_RANGE 2 #define DEFAULT_REGISTOR_VAL 499 struct isl29023_data { struct i2c_client *client; struct mutex lock; struct input_dev *input; struct work_struct work; struct workqueue_struct *workqueue; char phys[32]; u8 reg_cache[ISL29023_NUM_CACHABLE_REGS]; u8 mode_before_suspend; u8 mode_before_interrupt; u16 rext; }; static int gain_range[] = { 1000, 4000, 16000, 64000 }; /* * register access helpers */ static int __isl29023_read_reg(struct i2c_client *client, u32 reg, u8 mask, u8 shift) { struct isl29023_data *data = i2c_get_clientdata(client); return (data->reg_cache[reg] & mask) >> shift; } static int __isl29023_write_reg(struct i2c_client *client, u32 reg, u8 mask, u8 shift, u8 val) { struct isl29023_data *data = i2c_get_clientdata(client); int ret = 0; u8 tmp; if (reg >= ISL29023_NUM_CACHABLE_REGS) return -EINVAL; mutex_lock(&data->lock); tmp = data->reg_cache[reg]; tmp &= ~mask; tmp |= val << shift; ret = i2c_smbus_write_byte_data(client, reg, tmp); if (!ret) data->reg_cache[reg] = tmp; mutex_unlock(&data->lock); return ret; } /* * internally used functions */ static int isl29023_get_int_persists(struct i2c_client *client) { return __isl29023_read_reg(client, ISL29023_COMMAND1, ISL29023_INT_PERSISTS_MASK, ISL29023_INT_PERSISTS_SHIFT); } static int isl29023_set_int_persists(struct i2c_client *client, int int_persists) { return __isl29023_write_reg(client, ISL29023_COMMAND1, ISL29023_INT_PERSISTS_MASK, ISL29023_INT_PERSISTS_SHIFT, int_persists); } /* * interrupt flag */ static int isl29023_get_int_flag(struct i2c_client *client) { return __isl29023_read_reg(client, ISL29023_COMMAND1, ISL29023_INT_FLAG_MASK, ISL29023_INT_FLAG_SHIFT); } static int isl29023_set_int_flag(struct i2c_client *client, int flag) { return __isl29023_write_reg(client, ISL29023_COMMAND1, ISL29023_INT_FLAG_MASK, ISL29023_INT_FLAG_SHIFT, flag); } /* * interrupt lt */ static int isl29023_get_int_lt(struct i2c_client *client) { struct isl29023_data *data = i2c_get_clientdata(client); int lsb, msb, lt; mutex_lock(&data->lock); lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB); if (lsb < 0) { mutex_unlock(&data->lock); return lsb; } msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB); mutex_unlock(&data->lock); if (msb < 0) return msb; lt = ((msb << 8) | lsb); return lt; } static int isl29023_set_int_lt(struct i2c_client *client, int lt) { int ret = 0; struct isl29023_data *data = i2c_get_clientdata(client); mutex_lock(&data->lock); ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB, lt & 0xff); if (ret < 0) { mutex_unlock(&data->lock); return ret; } ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB, (lt >> 8) & 0xff); if (ret < 0) { mutex_unlock(&data->lock); return ret; } data->reg_cache[ISL29023_REG_IRQ_TH_LO_MSB] = (lt >> 8) & 0xff; data->reg_cache[ISL29023_REG_IRQ_TH_LO_LSB] = lt & 0xff; mutex_unlock(&data->lock); return ret; } /* * interrupt ht */ static int isl29023_get_int_ht(struct i2c_client *client) { struct isl29023_data *data = i2c_get_clientdata(client); int lsb, msb, ht; mutex_lock(&data->lock); lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB); if (lsb < 0) { mutex_unlock(&data->lock); return lsb; } msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB); mutex_unlock(&data->lock); if (msb < 0) return msb; ht = ((msb << 8) | lsb); return ht; } static int isl29023_set_int_ht(struct i2c_client *client, int ht) { int ret = 0; struct isl29023_data *data = i2c_get_clientdata(client); mutex_lock(&data->lock); ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB, ht & 0xff); if (ret < 0) { mutex_unlock(&data->lock); return ret; } ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB, (ht >> 8) & 0xff); if (ret < 0) { mutex_unlock(&data->lock); return ret; } data->reg_cache[ISL29023_REG_IRQ_TH_HI_MSB] = (ht >> 8) & 0xff; data->reg_cache[ISL29023_REG_IRQ_TH_HI_LSB] = ht & 0xff; mutex_unlock(&data->lock); return ret; } /* * range */ static int isl29023_get_range(struct i2c_client *client) { return __isl29023_read_reg(client, ISL29023_COMMAND2, ISL29023_RANGE_MASK, ISL29023_RANGE_SHIFT); } static int isl29023_set_range(struct i2c_client *client, int range) { return __isl29023_write_reg(client, ISL29023_COMMAND2, ISL29023_RANGE_MASK, ISL29023_RANGE_SHIFT, range); } /* * resolution */ static int isl29023_get_resolution(struct i2c_client *client) { return __isl29023_read_reg(client, ISL29023_COMMAND2, ISL29023_RES_MASK, ISL29023_RES_SHIFT); } static int isl29023_set_resolution(struct i2c_client *client, int res) { return __isl29023_write_reg(client, ISL29023_COMMAND2, ISL29023_RES_MASK, ISL29023_RES_SHIFT, res); } /* * mode */ static int isl29023_get_mode(struct i2c_client *client) { return __isl29023_read_reg(client, ISL29023_COMMAND1, ISL29023_MODE_MASK, ISL29023_MODE_SHIFT); } static int isl29023_set_mode(struct i2c_client *client, int mode) { return __isl29023_write_reg(client, ISL29023_COMMAND1, ISL29023_MODE_MASK, ISL29023_MODE_SHIFT, mode); } /* * power_state */ static int isl29023_set_power_state(struct i2c_client *client, int state) { return __isl29023_write_reg(client, ISL29023_COMMAND1, ISL29023_MODE_MASK, ISL29023_MODE_SHIFT, state ? ISL29023_ALS_ONCE_MODE : ISL29023_PD_MODE); } static int isl29023_get_power_state(struct i2c_client *client) { struct isl29023_data *data = i2c_get_clientdata(client); u8 cmdreg = data->reg_cache[ISL29023_COMMAND1]; if (cmdreg & ISL29023_MODE_MASK) return 1; else return 0; } static int isl29023_get_adc_value(struct i2c_client *client) { struct isl29023_data *data = i2c_get_clientdata(client); int lsb, msb, range, bitdepth; mutex_lock(&data->lock); lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_LSB_SENSOR); if (lsb < 0) { mutex_unlock(&data->lock); return lsb; } msb = i2c_smbus_read_byte_data(client, ISL29023_REG_MSB_SENSOR); mutex_unlock(&data->lock); if (msb < 0) return msb; range = isl29023_get_range(client); bitdepth = (4 - isl29023_get_resolution(client)) * 4; return (((msb << 8) | lsb) * ((gain_range[range] * 499) / data->rext)) >> bitdepth; } static int isl29023_get_int_lt_value(struct i2c_client *client) { struct isl29023_data *data = i2c_get_clientdata(client); int lsb, msb, range, bitdepth; mutex_lock(&data->lock); lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB); if (lsb < 0) { mutex_unlock(&data->lock); return lsb; } msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB); mutex_unlock(&data->lock); if (msb < 0) return msb; range = isl29023_get_range(client); bitdepth = (4 - isl29023_get_resolution(client)) * 4; return (((msb << 8) | lsb) * ((gain_range[range] * 499) / data->rext)) >> bitdepth; } static int isl29023_get_int_ht_value(struct i2c_client *client) { struct isl29023_data *data = i2c_get_clientdata(client); int lsb, msb, range, bitdepth; mutex_lock(&data->lock); lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB); if (lsb < 0) { mutex_unlock(&data->lock); return lsb; } msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB); mutex_unlock(&data->lock); if (msb < 0) return msb; range = isl29023_get_range(client); bitdepth = (4 - isl29023_get_resolution(client)) * 4; return (((msb << 8) | lsb) * ((gain_range[range] * 499) / data->rext)) >> bitdepth; } /* * sysfs layer */ /* * interrupt persists */ static ssize_t isl29023_show_int_persists(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); return sprintf(buf, "%i\n", isl29023_get_int_persists(client)); } static ssize_t isl29023_store_int_persists(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); unsigned long val; int ret; if ((kstrtoul(buf, 10, &val) < 0) || (val > ISL29023_INT_PERSISTS_16)) return -EINVAL; ret = isl29023_set_int_persists(client, val); if (ret < 0) return ret; return count; } static DEVICE_ATTR(int_persists, S_IWUSR | S_IRUGO, isl29023_show_int_persists, isl29023_store_int_persists); /* *interrupt flag */ static ssize_t isl29023_show_int_flag(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); return sprintf(buf, "%i\n", isl29023_get_int_flag(client)); } static ssize_t isl29023_store_int_flag(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); unsigned long val; int ret; if ((kstrtoul(buf, 10, &val) < 0) || (val > 1)) return -EINVAL; ret = isl29023_set_int_flag(client, val); if (ret < 0) return ret; return count; } static DEVICE_ATTR(int_flag, S_IWUSR | S_IRUGO, isl29023_show_int_flag, isl29023_store_int_flag); /* * interrupt lt */ static ssize_t isl29023_show_int_lt(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); return sprintf(buf, "%i\n", isl29023_get_int_lt(client)); } static ssize_t isl29023_store_int_lt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); unsigned long val; int ret; if ((kstrtoul(buf, 16, &val) < 0) || (val > 0xffff)) return -EINVAL; ret = isl29023_set_int_lt(client, val); if (ret < 0) return ret; return count; } static DEVICE_ATTR(int_lt, S_IWUSR | S_IRUGO, isl29023_show_int_lt, isl29023_store_int_lt); /* *interrupt ht */ static ssize_t isl29023_show_int_ht(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); return sprintf(buf, "%i\n", isl29023_get_int_ht(client)); } static ssize_t isl29023_store_int_ht(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); unsigned long val; int ret; if ((kstrtoul(buf, 16, &val) < 0) || (val > 0xffff)) return -EINVAL; ret = isl29023_set_int_ht(client, val); if (ret < 0) return ret; return count; } static DEVICE_ATTR(int_ht, S_IWUSR | S_IRUGO, isl29023_show_int_ht, isl29023_store_int_ht); /* * range */ static ssize_t isl29023_show_range(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); return sprintf(buf, "%i\n", isl29023_get_range(client)); } static ssize_t isl29023_store_range(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); unsigned long val; int ret; if ((kstrtoul(buf, 10, &val) < 0) || (val > ISL29023_RANGE_64K)) return -EINVAL; ret = isl29023_set_range(client, val); if (ret < 0) return ret; return count; } static DEVICE_ATTR(range, S_IWUSR | S_IRUGO, isl29023_show_range, isl29023_store_range); /* * resolution */ static ssize_t isl29023_show_resolution(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); return sprintf(buf, "%d\n", isl29023_get_resolution(client)); } static ssize_t isl29023_store_resolution(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); unsigned long val; int ret; if ((kstrtoul(buf, 10, &val) < 0) || (val > ISL29023_RES_4)) return -EINVAL; ret = isl29023_set_resolution(client, val); if (ret < 0) return ret; return count; } static DEVICE_ATTR(resolution, S_IWUSR | S_IRUGO, isl29023_show_resolution, isl29023_store_resolution); /* *mode */ static ssize_t isl29023_show_mode(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); return sprintf(buf, "%d\n", isl29023_get_mode(client)); } static ssize_t isl29023_store_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); unsigned long val; int ret; if ((kstrtoul(buf, 10, &val) < 0) || (val > ISL29023_IR_CONT_MODE)) return -EINVAL; /* clear the interrupt flag */ i2c_smbus_read_byte_data(client, ISL29023_COMMAND1); ret = isl29023_set_mode(client, val); if (ret < 0) return ret; return count; } static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, isl29023_show_mode, isl29023_store_mode); /* *power state */ static ssize_t isl29023_show_power_state(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); return sprintf(buf, "%d\n", isl29023_get_power_state(client)); } static ssize_t isl29023_store_power_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); unsigned long val; int ret; if ((kstrtoul(buf, 10, &val) < 0) || (val > 1)) return -EINVAL; ret = isl29023_set_power_state(client, val); return ret ? ret : count; } static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO, isl29023_show_power_state, isl29023_store_power_state); /* * lux */ static ssize_t isl29023_show_lux(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); /* No LUX data if not operational */ if (!isl29023_get_power_state(client)) return -EBUSY; return sprintf(buf, "%d\n", isl29023_get_adc_value(client)); } static DEVICE_ATTR(lux, S_IRUGO, isl29023_show_lux, NULL); /* * lux interrupt low threshold */ static ssize_t isl29023_show_int_lt_lux(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); /* No LUX data if not operational */ if (isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE && isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE) return -EIO; return sprintf(buf, "%d\n", isl29023_get_int_lt_value(client)); } static ssize_t isl29023_store_int_lt_lux(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct isl29023_data *data = i2c_get_clientdata(client); unsigned long val, lux_data; int range, bitdepth, ret; u8 lsb, msb; if ((kstrtoul(buf, 10, &val) < 0)) return -EINVAL; /* No LUX data if not operational */ if (isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE && isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE) return -EIO; if (val > (gain_range[isl29023_get_range(client)]*499/data->rext)) return -EINVAL; range = isl29023_get_range(client); bitdepth = (4 - isl29023_get_resolution(client)) * 4; lux_data = ((unsigned long)(val << bitdepth)) / ((gain_range[range] * 499) / data->rext); lux_data &= 0xffff; msb = lux_data >> 8; lsb = lux_data & 0xff; mutex_lock(&data->lock); ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB, lsb); if (ret < 0) { mutex_unlock(&data->lock); return ret; } ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB, msb); if (ret < 0) { mutex_unlock(&data->lock); return ret; } data->reg_cache[ISL29023_REG_IRQ_TH_LO_MSB] = msb; data->reg_cache[ISL29023_REG_IRQ_TH_LO_LSB] = lsb; mutex_unlock(&data->lock); return count; } static DEVICE_ATTR(int_lt_lux, S_IWUSR | S_IRUGO, isl29023_show_int_lt_lux, isl29023_store_int_lt_lux); /* * lux interrupt high threshold */ static ssize_t isl29023_show_int_ht_lux(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); /* No LUX data if not operational */ if (isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE && isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE) return -EIO; return sprintf(buf, "%d\n", isl29023_get_int_ht_value(client)); } static ssize_t isl29023_store_int_ht_lux(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct isl29023_data *data = i2c_get_clientdata(client); unsigned long val, lux_data; int range, bitdepth, ret; u8 lsb, msb; if ((kstrtoul(buf, 10, &val) < 0)) return -EINVAL; /* No LUX data if not operational */ if (isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE && isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE) return -EIO; if (val > (gain_range[isl29023_get_range(client)]*499/data->rext)) return -EINVAL; range = isl29023_get_range(client); bitdepth = (4 - isl29023_get_resolution(client)) * 4; lux_data = ((unsigned long)(val << bitdepth)) / ((gain_range[range] * 499) / data->rext); lux_data &= 0xffff; msb = lux_data >> 8; lsb = lux_data & 0xff; mutex_lock(&data->lock); ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB, lsb); if (ret < 0) { mutex_unlock(&data->lock); return ret; } ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB, msb); if (ret < 0) { mutex_unlock(&data->lock); return ret; } data->reg_cache[ISL29023_REG_IRQ_TH_HI_MSB] = msb; data->reg_cache[ISL29023_REG_IRQ_TH_HI_LSB] = lsb; mutex_unlock(&data->lock); return count; } static DEVICE_ATTR(int_ht_lux, S_IWUSR | S_IRUGO, isl29023_show_int_ht_lux, isl29023_store_int_ht_lux); static struct attribute *isl29023_attributes[] = { &dev_attr_int_persists.attr, &dev_attr_range.attr, &dev_attr_resolution.attr, &dev_attr_mode.attr, &dev_attr_power_state.attr, &dev_attr_lux.attr, &dev_attr_int_lt_lux.attr, &dev_attr_int_ht_lux.attr, &dev_attr_int_lt.attr, &dev_attr_int_ht.attr, &dev_attr_int_flag.attr, NULL }; static const struct attribute_group isl29023_attr_group = { .attrs = isl29023_attributes, }; static int isl29023_init_client(struct i2c_client *client) { struct isl29023_data *data = i2c_get_clientdata(client); int i; /* read all the registers once to fill the cache. * if one of the reads fails, we consider the init failed */ for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) { int v = i2c_smbus_read_byte_data(client, i); if (v < 0) return -ENODEV; data->reg_cache[i] = v; } /* set defaults */ isl29023_set_int_persists(client, ISL29023_INT_PERSISTS_8); isl29023_set_int_ht(client, 0xffff); isl29023_set_int_lt(client, 0x0); isl29023_set_range(client, ISL29023_RANGE_16K); isl29023_set_resolution(client, ISL29023_RES_16); isl29023_set_mode(client, ISL29023_ALS_ONCE_MODE); isl29023_set_int_flag(client, 0); isl29023_set_power_state(client, 0); return 0; } static void isl29023_work(struct work_struct *work) { struct isl29023_data *data = container_of(work, struct isl29023_data, work); struct i2c_client *client = data->client; int lux; /* Clear interrupt flag */ isl29023_set_int_flag(client, 0); data->mode_before_interrupt = isl29023_get_mode(client); lux = isl29023_get_adc_value(client); /* To clear the interrpt status */ isl29023_set_power_state(client, ISL29023_PD_MODE); isl29023_set_mode(client, data->mode_before_interrupt); msleep(100); input_report_abs(data->input, ABS_MISC, lux); input_sync(data->input); } static irqreturn_t isl29023_irq_handler(int irq, void *handle) { struct isl29023_data *data = handle; int cmd_1; cmd_1 = i2c_smbus_read_byte_data(data->client, ISL29023_COMMAND1); if (!(cmd_1 & ISL29023_INT_FLAG_MASK)) return IRQ_NONE; queue_work(data->workqueue, &data->work); return IRQ_HANDLED; } /* * I2C layer */ static int isl29023_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct isl29023_data *data; struct input_dev *input_dev; int err = 0; struct regulator *vdd = NULL; u32 rext = 0; struct device_node *of_node = client->dev.of_node; struct irq_data *irq_data = irq_get_irq_data(client->irq); u32 irq_flag; bool shared_irq; vdd = devm_regulator_get(&client->dev, "vdd"); if (!IS_ERR(vdd)) { err = regulator_enable(vdd); if (err) { dev_err(&client->dev, "vdd set voltage error\n"); return err; } } err = of_property_read_u32(of_node, "rext", &rext); if (err) rext = DEFAULT_REGISTOR_VAL; shared_irq = of_property_read_bool(of_node, "shared-interrupt"); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) return -EIO; data = kzalloc(sizeof(struct isl29023_data), GFP_KERNEL); if (!data) return -ENOMEM; data->client = client; data->rext = (u16)rext; snprintf(data->phys, sizeof(data->phys), "%s", dev_name(&client->dev)); i2c_set_clientdata(client, data); mutex_init(&data->lock); /* initialize the ISL29023 chip */ err = isl29023_init_client(client); if (err) goto exit_kfree; /* register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &isl29023_attr_group); if (err) goto exit_kfree; input_dev = input_allocate_device(); if (!input_dev) { err = -ENOMEM; goto exit_kfree; } data->input = input_dev; input_dev->name = "isl29023 light sensor"; input_dev->id.bustype = BUS_I2C; input_dev->phys = data->phys; __set_bit(EV_ABS, input_dev->evbit); input_set_abs_params(input_dev, ABS_MISC, 0, gain_range[DEF_RANGE]*499/data->rext, 0, 0); err = input_register_device(input_dev); if (err) goto exit_free_input; irq_flag = irqd_get_trigger_type(irq_data); irq_flag |= IRQF_ONESHOT; if (shared_irq) irq_flag |= IRQF_SHARED; err = request_threaded_irq(client->irq, NULL, isl29023_irq_handler, irq_flag, client->dev.driver->name, data); if (err < 0) { dev_err(&client->dev, "failed to register irq %d!\n", client->irq); goto exit_free_input; } data->workqueue = create_singlethread_workqueue("isl29023"); INIT_WORK(&data->work, isl29023_work); if (data->workqueue == NULL) { dev_err(&client->dev, "couldn't create workqueue\n"); err = -ENOMEM; goto exit_free_interrupt; } dev_info(&client->dev, "driver version %s enabled\n", DRIVER_VERSION); return 0; exit_free_interrupt: free_irq(client->irq, data); exit_free_input: input_free_device(input_dev); exit_kfree: kfree(data); return err; } static int isl29023_remove(struct i2c_client *client) { struct isl29023_data *data = i2c_get_clientdata(client); cancel_work_sync(&data->work); destroy_workqueue(data->workqueue); free_irq(client->irq, data); input_unregister_device(data->input); input_free_device(data->input); sysfs_remove_group(&client->dev.kobj, &isl29023_attr_group); isl29023_set_power_state(client, 0); kfree(i2c_get_clientdata(client)); return 0; } #ifdef CONFIG_PM_SLEEP static int isl29023_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct isl29023_data *data = i2c_get_clientdata(client); data->mode_before_suspend = isl29023_get_mode(client); return isl29023_set_power_state(client, ISL29023_PD_MODE); } static int isl29023_resume(struct device *dev) { int i; struct i2c_client *client = to_i2c_client(dev); struct isl29023_data *data = i2c_get_clientdata(client); /* restore registers from cache */ for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) if (i2c_smbus_write_byte_data(client, i, data->reg_cache[i])) return -EIO; return isl29023_set_mode(client, data->mode_before_suspend); } #endif static SIMPLE_DEV_PM_OPS(isl29023_pm_ops, isl29023_suspend, isl29023_resume); static const struct i2c_device_id isl29023_id[] = { { ISL29023_DRV_NAME, 0 }, {} }; MODULE_DEVICE_TABLE(i2c, isl29023_id); static struct i2c_driver isl29023_driver = { .driver = { .name = ISL29023_DRV_NAME, .owner = THIS_MODULE, .pm = &isl29023_pm_ops, }, .probe = isl29023_probe, .remove = isl29023_remove, .id_table = isl29023_id, }; static int __init isl29023_init(void) { return i2c_add_driver(&isl29023_driver); } static void __exit isl29023_exit(void) { i2c_del_driver(&isl29023_driver); } MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_DESCRIPTION("ISL29023 ambient light sensor driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRIVER_VERSION); module_init(isl29023_init); module_exit(isl29023_exit);