struct tsl2772_chip {
kernel_ulong_t id; struct mutex prox_mutex; struct mutex als_mutex; struct i2c_client *client; struct regulator_bulk_data supplies[TSL2772_NUM_SUPPLIES];
u16 prox_data; struct tsl2772_als_info als_cur_info; struct tsl2772_settings settings; struct tsl2772_platform_data *pdata; int als_gain_time_scale; int als_saturation; int tsl2772_chip_status;
u8 tsl2772_config[TSL2772_MAX_CONFIG_REG]; conststruct tsl2772_chip_info *chip_info; conststruct iio_info *info;
s64 event_timestamp; /* * This structure is intentionally large to accommodate * updates via sysfs. * Sized to 9 = max 8 segments + 1 termination segment
*/ struct tsl2772_lux tsl2772_device_lux[TSL2772_MAX_LUX_TABLE_SIZE];
};
/* * Different devices require different coefficents, and these numbers were * derived from the 'Lux Equation' section of the various device datasheets. * All of these coefficients assume a Glass Attenuation (GA) factor of 1. * The coefficients are multiplied by 1000 to avoid floating point operations. * The two rows in each table correspond to the Lux1 and Lux2 equations from * the datasheets.
*/ staticconststruct tsl2772_lux tsl2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = {
{ 53000, 106000 },
{ 31800, 53000 },
{ 0, 0 },
};
staticint tsl2772_read_status(struct tsl2772_chip *chip)
{ int ret;
ret = i2c_smbus_read_byte_data(chip->client,
TSL2772_CMD_REG | TSL2772_STATUS); if (ret < 0)
dev_err(&chip->client->dev, "%s: failed to read STATUS register: %d\n", __func__,
ret);
return ret;
}
staticint tsl2772_write_control_reg(struct tsl2772_chip *chip, u8 data)
{ int ret;
ret = i2c_smbus_write_byte_data(chip->client,
TSL2772_CMD_REG | TSL2772_CNTRL, data); if (ret < 0) {
dev_err(&chip->client->dev, "%s: failed to write to control register %x: %d\n",
__func__, data, ret);
}
return ret;
}
staticint tsl2772_read_autoinc_regs(struct tsl2772_chip *chip, int lower_reg, int upper_reg)
{
u8 buf[2]; int ret;
ret = i2c_smbus_write_byte(chip->client,
TSL2772_CMD_REG | TSL2772_CMD_AUTOINC_PROTO |
lower_reg); if (ret < 0) {
dev_err(&chip->client->dev, "%s: failed to enable auto increment protocol: %d\n",
__func__, ret); return ret;
}
ret = i2c_smbus_read_byte_data(chip->client,
TSL2772_CMD_REG | lower_reg); if (ret < 0) {
dev_err(&chip->client->dev, "%s: failed to read from register %x: %d\n", __func__,
lower_reg, ret); return ret;
}
buf[0] = ret;
ret = i2c_smbus_read_byte_data(chip->client,
TSL2772_CMD_REG | upper_reg); if (ret < 0) {
dev_err(&chip->client->dev, "%s: failed to read from register %x: %d\n", __func__,
upper_reg, ret); return ret;
}
buf[1] = ret;
ret = i2c_smbus_write_byte(chip->client,
TSL2772_CMD_REG | TSL2772_CMD_REPEAT_PROTO |
lower_reg); if (ret < 0) {
dev_err(&chip->client->dev, "%s: failed to enable repeated byte protocol: %d\n",
__func__, ret); return ret;
}
return le16_to_cpup((const __le16 *)&buf[0]);
}
/** * tsl2772_get_lux() - Reads and calculates current lux value. * @indio_dev: pointer to IIO device * * The raw ch0 and ch1 values of the ambient light sensed in the last * integration cycle are read from the device. The raw values are multiplied * by a device-specific scale factor, and divided by the integration time and * device gain. The code supports multiple lux equations through the lux table * coefficients. A lux gain trim is applied to each lux equation, and then the * maximum lux within the interval 0..65535 is selected.
*/ staticint tsl2772_get_lux(struct iio_dev *indio_dev)
{ struct tsl2772_chip *chip = iio_priv(indio_dev); struct tsl2772_lux *p; int max_lux, ret; bool overflow;
mutex_lock(&chip->als_mutex);
if (chip->tsl2772_chip_status != TSL2772_CHIP_WORKING) {
dev_err(&chip->client->dev, "%s: device is not enabled\n",
__func__);
ret = -EBUSY; goto out_unlock;
}
ret = tsl2772_read_status(chip); if (ret < 0) goto out_unlock;
if (!(ret & TSL2772_STA_ADC_VALID)) {
dev_err(&chip->client->dev, "%s: data not valid yet\n", __func__);
ret = chip->als_cur_info.lux; /* return LAST VALUE */ goto out_unlock;
}
ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN0LO,
TSL2772_ALS_CHAN0HI); if (ret < 0) goto out_unlock;
chip->als_cur_info.als_ch0 = ret;
ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN1LO,
TSL2772_ALS_CHAN1HI); if (ret < 0) goto out_unlock;
chip->als_cur_info.als_ch1 = ret;
/* * The als_gain_trim can have a value within the range 250..4000 * and is a multiplier for the lux. A trim of 1000 makes no * changes to the lux, less than 1000 scales it down, and * greater than 1000 scales it up.
*/
lux = (lux * chip->settings.als_gain_trim) / 1000;
if (lux > TSL2772_LUX_CALC_OVER_FLOW) {
overflow = true; continue;
}
max_lux = max(max_lux, lux);
}
if (overflow && max_lux == 0)
max_lux = TSL2772_LUX_CALC_OVER_FLOW;
update_struct_with_max_lux:
chip->als_cur_info.lux = max_lux;
ret = max_lux;
out_unlock:
mutex_unlock(&chip->als_mutex);
return ret;
}
/** * tsl2772_get_prox() - Reads proximity data registers and updates * chip->prox_data. * * @indio_dev: pointer to IIO device
*/ staticint tsl2772_get_prox(struct iio_dev *indio_dev)
{ struct tsl2772_chip *chip = iio_priv(indio_dev); int ret;
mutex_lock(&chip->prox_mutex);
ret = tsl2772_read_status(chip); if (ret < 0) goto prox_poll_err;
switch (chip->id) { case tsl2571: case tsl2671: case tmd2671: case tsl2771: case tmd2771: if (!(ret & TSL2772_STA_ADC_VALID)) {
ret = -EINVAL; goto prox_poll_err;
} break; case tsl2572: case tsl2672: case tmd2672: case tsl2772: case tmd2772: case apds9930: if (!(ret & TSL2772_STA_PRX_VALID)) {
ret = -EINVAL; goto prox_poll_err;
} break;
}
ret = tsl2772_read_autoinc_regs(chip, TSL2772_PRX_LO, TSL2772_PRX_HI); if (ret < 0) goto prox_poll_err;
chip->prox_data = ret;
/** * tsl2772_defaults() - Populates the device nominal operating parameters * with those provided by a 'platform' data struct or * with prefined defaults. * * @chip: pointer to device structure.
*/ staticvoid tsl2772_defaults(struct tsl2772_chip *chip)
{ /* If Operational settings defined elsewhere.. */ if (chip->pdata && chip->pdata->platform_default_settings)
memcpy(&chip->settings, chip->pdata->platform_default_settings, sizeof(tsl2772_default_settings)); else
memcpy(&chip->settings, &tsl2772_default_settings, sizeof(tsl2772_default_settings));
/* Load up the proper lux table. */ if (chip->pdata && chip->pdata->platform_lux_table[0].ch0 != 0)
memcpy(chip->tsl2772_device_lux,
chip->pdata->platform_lux_table, sizeof(chip->pdata->platform_lux_table)); else
memcpy(chip->tsl2772_device_lux,
tsl2772_default_lux_table_group[chip->id],
TSL2772_DEFAULT_TABLE_BYTES);
tsl2772_parse_dt(chip);
}
/** * tsl2772_als_calibrate() - Obtain single reading and calculate * the als_gain_trim. * * @indio_dev: pointer to IIO device
*/ staticint tsl2772_als_calibrate(struct iio_dev *indio_dev)
{ struct tsl2772_chip *chip = iio_priv(indio_dev); int ret, lux_val;
ret = i2c_smbus_read_byte_data(chip->client,
TSL2772_CMD_REG | TSL2772_CNTRL); if (ret < 0) {
dev_err(&chip->client->dev, "%s: failed to read from the CNTRL register\n",
__func__); return ret;
}
if ((ret & (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON))
!= (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) {
dev_err(&chip->client->dev, "%s: Device is not powered on and/or ADC is not enabled\n",
__func__); return -EINVAL;
} elseif ((ret & TSL2772_STA_ADC_VALID) != TSL2772_STA_ADC_VALID) {
dev_err(&chip->client->dev, "%s: The two ADC channels have not completed an integration cycle\n",
__func__); return -ENODATA;
}
lux_val = tsl2772_get_lux(indio_dev); if (lux_val < 0) {
dev_err(&chip->client->dev, "%s: failed to get lux\n", __func__); return lux_val;
} if (lux_val == 0) return -ERANGE;
ret = (chip->settings.als_cal_target * chip->settings.als_gain_trim) /
lux_val; if (ret < TSL2772_ALS_GAIN_TRIM_MIN || ret > TSL2772_ALS_GAIN_TRIM_MAX) return -ERANGE;
/* and make sure we're not already on */ if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) { /* if forcing a register update - turn off, then on */
dev_info(&chip->client->dev, "device is already enabled\n"); return -EINVAL;
}
/* Set the gain based on tsl2772_settings struct */
chip->tsl2772_config[TSL2772_GAIN] =
(chip->settings.als_gain & 0xFF) |
((chip->settings.prox_gain & 0xFF) << 2) |
(chip->settings.prox_diode << 4) |
(chip->settings.prox_power << 6);
/* set chip time scaling and saturation */
als_count = 256 - chip->settings.als_time;
als_time_us = als_count * tsl2772_int_time_avail[chip->id][3];
chip->als_saturation = als_count * 768; /* 75% of full scale */
chip->als_gain_time_scale = als_time_us *
tsl2772_als_gain[chip->settings.als_gain];
/* * TSL2772 Specific power-on / adc enable sequence * Power on the device 1st.
*/
ret = tsl2772_write_control_reg(chip, TSL2772_CNTL_PWR_ON); if (ret < 0) return ret;
/* * Use the following shadow copy for our delay before enabling ADC. * Write all the registers.
*/ for (i = 0, dev_reg = chip->tsl2772_config;
i < TSL2772_MAX_CONFIG_REG; i++) { int reg = TSL2772_CMD_REG + i;
ret = i2c_smbus_write_byte_data(chip->client, reg,
*dev_reg++); if (ret < 0) {
dev_err(&chip->client->dev, "%s: failed to write to register %x: %d\n",
__func__, reg, ret); return ret;
}
}
/* Power-on settling time */
usleep_range(3000, 3500);
reg_val = TSL2772_CNTL_PWR_ON | TSL2772_CNTL_ADC_ENBL |
TSL2772_CNTL_PROX_DET_ENBL; if (chip->settings.als_interrupt_en)
reg_val |= TSL2772_CNTL_ALS_INT_ENBL; if (chip->settings.prox_interrupt_en)
reg_val |= TSL2772_CNTL_PROX_INT_ENBL;
ret = tsl2772_write_control_reg(chip, reg_val); if (ret < 0) return ret;
ret = i2c_smbus_write_byte(chip->client,
TSL2772_CMD_REG | TSL2772_CMD_SPL_FN |
TSL2772_CMD_PROXALS_INT_CLR); if (ret < 0) {
dev_err(&chip->client->dev, "%s: failed to clear interrupt status: %d\n",
__func__, ret); return ret;
}
/** * tsl2772_invoke_change - power cycle the device to implement the user * parameters * @indio_dev: pointer to IIO device * * Obtain and lock both ALS and PROX resources, determine and save device state * (On/Off), cycle device to implement updated parameter, put device back into * proper state, and unlock resource.
*/ staticint tsl2772_invoke_change(struct iio_dev *indio_dev)
{ struct tsl2772_chip *chip = iio_priv(indio_dev); int device_status = chip->tsl2772_chip_status; int ret;
staticint tsl2772_prox_cal(struct iio_dev *indio_dev)
{ struct tsl2772_chip *chip = iio_priv(indio_dev); int prox_history[MAX_SAMPLES_CAL + 1]; int i, ret, mean, max, sample_sum;
if (chip->settings.prox_max_samples_cal < 1 ||
chip->settings.prox_max_samples_cal > MAX_SAMPLES_CAL) return -EINVAL;
for (i = 0; i < chip->settings.prox_max_samples_cal; i++) {
usleep_range(15000, 17500);
ret = tsl2772_get_prox(indio_dev); if (ret < 0) return ret;
prox_history[i] = chip->prox_data;
}
sample_sum = 0;
max = INT_MIN; for (i = 0; i < chip->settings.prox_max_samples_cal; i++) {
sample_sum += prox_history[i];
max = max(max, prox_history[i]);
}
mean = sample_sum / chip->settings.prox_max_samples_cal;
if (kstrtobool(buf, &value) || !value) return -EINVAL;
ret = tsl2772_als_calibrate(indio_dev); if (ret < 0) return ret;
ret = tsl2772_invoke_change(indio_dev); if (ret < 0) return ret;
return len;
}
static ssize_t in_illuminance0_lux_table_show(struct device *dev, struct device_attribute *attr, char *buf)
{ struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev)); int i = 0; int offset = 0;
while (i < TSL2772_MAX_LUX_TABLE_SIZE) {
offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%u,%u,",
chip->tsl2772_device_lux[i].ch0,
chip->tsl2772_device_lux[i].ch1); if (chip->tsl2772_device_lux[i].ch0 == 0) { /* * We just printed the first "0" entry. * Now get rid of the extra "," and break.
*/
offset--; break;
}
i++;
}
/* * We now have an array of ints starting at value[1], and * enumerated by value[0]. * We expect each group of two ints to be one table entry, * and the last table entry is all 0.
*/
n = value[0]; if ((n % 2) || n < 4 ||
n > ((ARRAY_SIZE(chip->tsl2772_device_lux) - 1) * 2)) return -EINVAL;
if ((value[(n - 1)] | value[n]) != 0) return -EINVAL;
if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) {
ret = tsl2772_chip_off(indio_dev); if (ret < 0) return ret;
}
/* Zero out the table */
memset(chip->tsl2772_device_lux, 0, sizeof(chip->tsl2772_device_lux));
memcpy(chip->tsl2772_device_lux, &value[1], (value[0] * 4));
ret = tsl2772_invoke_change(indio_dev); if (ret < 0) return ret;
/* Use the default register values to identify the Taos device */ staticint tsl2772_device_id_verif(int id, int target)
{ switch (target) { case tsl2571: case tsl2671: case tsl2771: return (id & 0xf0) == TRITON_ID; case tmd2671: case tmd2771: return (id & 0xf0) == HALIBUT_ID; case tsl2572: case tsl2672: case tmd2672: case tsl2772: case tmd2772: case apds9930: return (id & 0xf0) == SWORDFISH_ID;
}
ret = tsl2772_read_status(chip); if (ret < 0) return IRQ_HANDLED;
/* What type of interrupt do we need to process */ if (ret & TSL2772_STA_PRX_INTR) {
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
0,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_EITHER),
timestamp);
}
ret = devm_regulator_bulk_get(&clientp->dev,
ARRAY_SIZE(chip->supplies),
chip->supplies); if (ret < 0) return dev_err_probe(&clientp->dev, ret, "Failed to get regulators\n");
ret = regulator_bulk_enable(ARRAY_SIZE(chip->supplies), chip->supplies); if (ret < 0) {
dev_err(&clientp->dev, "Failed to enable regulators: %d\n",
ret); return ret;
}
ret = devm_add_action_or_reset(&clientp->dev,
tsl2772_disable_regulators_action,
chip); if (ret < 0) {
dev_err(&clientp->dev, "Failed to setup regulator cleanup action %d\n",
ret); return ret;
}
ret = i2c_smbus_read_byte_data(chip->client,
TSL2772_CMD_REG | TSL2772_CHIPID); if (ret < 0) return ret;
if (tsl2772_device_id_verif(ret, id->driver_data) <= 0) {
dev_info(&chip->client->dev, "%s: i2c device found does not match expected id\n",
__func__); return -EINVAL;
}
ret = i2c_smbus_write_byte(clientp, TSL2772_CMD_REG | TSL2772_CNTRL); if (ret < 0) {
dev_err(&clientp->dev, "%s: Failed to write to CMD register: %d\n",
__func__, ret); return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.