/* Minimum sleep after each command to ensure it's received */ #define SI1145_COMMAND_MINSLEEP_MS 5 /* Return -ETIMEDOUT after this long */ #define SI1145_COMMAND_TIMEOUT_MS 25
/** * struct si1145_data - si1145 chip state data * @client: I2C client * @lock: mutex to protect shared state. * @cmdlock: Low-level mutex to protect command execution only * @rsp_seq: Next expected response number or -1 if counter reset required * @scan_mask: Saved scan mask to avoid duplicate set_chlist * @autonomous: If automatic measurements are active (for buffer support) * @part_info: Part information * @trig: Pointer to iio trigger * @meas_rate: Value of MEAS_RATE register. Only set in HW in auto mode * @buffer: Used to pack data read from sensor.
*/ struct si1145_data { struct i2c_client *client; struct mutex lock; struct mutex cmdlock; int rsp_seq; conststruct si1145_part_info *part_info; unsignedlong scan_mask; bool autonomous; struct iio_trigger *trig; int meas_rate; /* * Ensure timestamp will be naturally aligned if present. * Maximum buffer size (may be only partly used if not all * channels are enabled): * 6*2 bytes channels data + 4 bytes alignment + * 8 bytes timestamp
*/
u8 buffer[24] __aligned(8);
};
/* * __si1145_command_reset() - Send CMD_NOP and wait for response 0 * * Does not modify data->rsp_seq * * Return: 0 on success and -errno on error.
*/ staticint __si1145_command_reset(struct si1145_data *data)
{ struct device *dev = &data->client->dev; unsignedlong stop_jiffies; int ret;
ret = i2c_smbus_write_byte_data(data->client, SI1145_REG_COMMAND,
SI1145_CMD_NOP); if (ret < 0) return ret;
msleep(SI1145_COMMAND_MINSLEEP_MS);
stop_jiffies = jiffies + SI1145_COMMAND_TIMEOUT_MS * HZ / 1000; while (true) {
ret = i2c_smbus_read_byte_data(data->client,
SI1145_REG_RESPONSE); if (ret <= 0) return ret; if (time_after(jiffies, stop_jiffies)) {
dev_warn(dev, "timeout on reset\n"); return -ETIMEDOUT;
}
msleep(SI1145_COMMAND_MINSLEEP_MS);
}
}
/* * si1145_command() - Execute a command and poll the response register * * All conversion overflows are reported as -EOVERFLOW * INVALID_SETTING is reported as -EINVAL * Timeouts are reported as -ETIMEDOUT * * Return: 0 on success or -errno on failure
*/ staticint si1145_command(struct si1145_data *data, u8 cmd)
{ struct device *dev = &data->client->dev; unsignedlong stop_jiffies; int ret;
mutex_lock(&data->cmdlock);
if (data->rsp_seq < 0) {
ret = __si1145_command_reset(data); if (ret < 0) {
dev_err(dev, "failed to reset command counter, ret=%d\n",
ret); goto out;
}
data->rsp_seq = 0;
}
ret = i2c_smbus_write_byte_data(data->client, SI1145_REG_COMMAND, cmd); if (ret) {
dev_warn(dev, "failed to write command, ret=%d\n", ret); goto out;
} /* Sleep a little to ensure the command is received */
msleep(SI1145_COMMAND_MINSLEEP_MS);
stop_jiffies = jiffies + SI1145_COMMAND_TIMEOUT_MS * HZ / 1000; while (true) {
ret = i2c_smbus_read_byte_data(data->client,
SI1145_REG_RESPONSE); if (ret < 0) {
dev_warn(dev, "failed to read response, ret=%d\n", ret); break;
}
if ((ret & ~SI1145_RSP_COUNTER_MASK) == 0) { if (ret == data->rsp_seq) { if (time_after(jiffies, stop_jiffies)) {
dev_warn(dev, "timeout on command 0x%02x\n",
cmd);
ret = -ETIMEDOUT; break;
}
msleep(SI1145_COMMAND_MINSLEEP_MS); continue;
} if (ret == ((data->rsp_seq + 1) &
SI1145_RSP_COUNTER_MASK)) {
data->rsp_seq = ret;
ret = 0; break;
}
dev_warn(dev, "unexpected response counter %d instead of %d\n",
ret, (data->rsp_seq + 1) &
SI1145_RSP_COUNTER_MASK);
ret = -EIO;
} else { if (ret == SI1145_RSP_INVALID_SETTING) {
dev_warn(dev, "INVALID_SETTING error on command 0x%02x\n",
cmd);
ret = -EINVAL;
} else { /* All overflows are treated identically */
dev_dbg(dev, "overflow, ret=%d, cmd=0x%02x\n",
ret, cmd);
ret = -EOVERFLOW;
}
}
/* Force a counter reset next time */
data->rsp_seq = -1; break;
}
staticint si1145_proximity_adcgain_from_scale(int val, int val2)
{
val = find_closest_descending(val, si1145_proximity_scale_available,
ARRAY_SIZE(si1145_proximity_scale_available)); if (val < 0 || val > 5 || val2 != 0) return -EINVAL;
return val;
}
staticint si1145_intensity_adcgain_from_scale(int val, int val2)
{
val = find_closest_descending(val, si1145_intensity_scale_available,
ARRAY_SIZE(si1145_intensity_scale_available)); if (val < 0 || val > 7 || val2 != 0) return -EINVAL;
return val;
}
staticint si1145_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask)
{ struct si1145_data *data = iio_priv(indio_dev); int ret;
u8 reg;
switch (mask) { case IIO_CHAN_INFO_RAW: switch (chan->type) { case IIO_INTENSITY: case IIO_PROXIMITY: case IIO_VOLTAGE: case IIO_TEMP: case IIO_UVINDEX: if (!iio_device_claim_direct(indio_dev)) return -EBUSY;
ret = si1145_measure(indio_dev, chan);
iio_device_release_direct(indio_dev);
if (ret < 0) return ret;
*val = ret;
return IIO_VAL_INT; case IIO_CURRENT:
ret = i2c_smbus_read_byte_data(data->client,
SI1145_PS_LED_REG(chan->channel)); if (ret < 0) return ret;
return IIO_VAL_INT; default: return -EINVAL;
} case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_PROXIMITY:
reg = SI1145_PARAM_PS_ADC_GAIN; break; case IIO_INTENSITY: if (chan->channel2 == IIO_MOD_LIGHT_IR)
reg = SI1145_PARAM_ALSIR_ADC_GAIN; else
reg = SI1145_PARAM_ALSVIS_ADC_GAIN; break; case IIO_TEMP:
*val = 28;
*val2 = 571429; return IIO_VAL_INT_PLUS_MICRO; case IIO_UVINDEX:
*val = 0;
*val2 = 10000; return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL;
}
ret = si1145_param_query(data, reg); if (ret < 0) return ret;
*val = si1145_scale_from_adcgain(ret & 0x07);
return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: switch (chan->type) { case IIO_TEMP: /* * -ADC offset - ADC counts @ 25°C - * 35 * ADC counts / °C
*/
*val = -256 - 11136 + 25 * 35; return IIO_VAL_INT; default: /* * All ADC measurements have are by default offset * by -256 * See AN498 5.6.3
*/
ret = si1145_param_query(data, SI1145_PARAM_ADC_OFFSET); if (ret < 0) return ret;
*val = -si1145_uncompress(ret); return IIO_VAL_INT;
} case IIO_CHAN_INFO_SAMP_FREQ: return si1145_read_samp_freq(data, val, val2); default: return -EINVAL;
}
}
staticint si1145_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask)
{ struct si1145_data *data = iio_priv(indio_dev);
u8 reg1, reg2, shift; int ret;
switch (mask) { case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_PROXIMITY:
val = si1145_proximity_adcgain_from_scale(val, val2); if (val < 0) return val;
reg1 = SI1145_PARAM_PS_ADC_GAIN;
reg2 = SI1145_PARAM_PS_ADC_COUNTER; break; case IIO_INTENSITY:
val = si1145_intensity_adcgain_from_scale(val, val2); if (val < 0) return val; if (chan->channel2 == IIO_MOD_LIGHT_IR) {
reg1 = SI1145_PARAM_ALSIR_ADC_GAIN;
reg2 = SI1145_PARAM_ALSIR_ADC_COUNTER;
} else {
reg1 = SI1145_PARAM_ALSVIS_ADC_GAIN;
reg2 = SI1145_PARAM_ALSVIS_ADC_COUNTER;
} break; default: return -EINVAL;
}
if (!iio_device_claim_direct(indio_dev)) return -EBUSY;
ret = si1145_param_set(data, reg1, val); if (ret < 0) {
iio_device_release_direct(indio_dev); return ret;
} /* Set recovery period to one's complement of gain */
ret = si1145_param_set(data, reg2, (~val & 0x07) << 4);
iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_RAW: if (chan->type != IIO_CURRENT) return -EINVAL;
if (val < 0 || val > 15 || val2 != 0) return -EINVAL;
ret = i2c_smbus_write_byte_data(client, SI1145_REG_COMMAND,
SI1145_CMD_RESET); if (ret < 0) return ret;
msleep(SI1145_COMMAND_TIMEOUT_MS);
/* Hardware key, magic value */
ret = i2c_smbus_write_byte_data(client, SI1145_REG_HW_KEY, 0x17); if (ret < 0) return ret;
msleep(SI1145_COMMAND_TIMEOUT_MS);
/* Turn off autonomous mode */
ret = si1145_set_meas_rate(data, 0); if (ret < 0) return ret;
/* Initialize sampling freq to 10 Hz */
ret = si1145_store_samp_freq(data, 10); if (ret < 0) return ret;
/* Set LED currents to 45 mA; have 4 bits, see Table 2 in datasheet */ switch (data->part_info->num_leds) { case 3:
ret = i2c_smbus_write_byte_data(client,
SI1145_REG_PS_LED3,
SI1145_LED_CURRENT_45mA); if (ret < 0) return ret;
fallthrough; case 2:
ret = i2c_smbus_write_byte_data(client,
SI1145_REG_PS_LED21,
(SI1145_LED_CURRENT_45mA << 4) |
SI1145_LED_CURRENT_45mA); break; case 1:
ret = i2c_smbus_write_byte_data(client,
SI1145_REG_PS_LED21,
SI1145_LED_CURRENT_45mA); break; default:
ret = 0; break;
} if (ret < 0) return ret;
/* Set normal proximity measurement mode */
ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_MISC,
SI1145_PS_ADC_MODE_NORMAL); if (ret < 0) return ret;
ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_GAIN, 0x01); if (ret < 0) return ret;
/* ADC_COUNTER should be one complement of ADC_GAIN */
ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_COUNTER, 0x06 << 4); if (ret < 0) return ret;
/* Set ALS visible measurement mode */
ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_MISC,
SI1145_ADC_MISC_RANGE); if (ret < 0) return ret;
ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_GAIN, 0x03); if (ret < 0) return ret;
ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_COUNTER,
0x04 << 4); if (ret < 0) return ret;
/* Set ALS IR measurement mode */
ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_MISC,
SI1145_ADC_MISC_RANGE); if (ret < 0) return ret;
ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_GAIN, 0x01); if (ret < 0) return ret;
ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_COUNTER,
0x06 << 4); if (ret < 0) return ret;
/* * Initialize UCOEF to default values in datasheet * These registers are normally zero on reset
*/ if (data->part_info == &si1145_part_info[SI1132] ||
data->part_info == &si1145_part_info[SI1145] ||
data->part_info == &si1145_part_info[SI1146] ||
data->part_info == &si1145_part_info[SI1147]) {
ret = i2c_smbus_write_byte_data(data->client,
SI1145_REG_UCOEF1,
SI1145_UCOEF1_DEFAULT); if (ret < 0) return ret;
ret = i2c_smbus_write_byte_data(data->client,
SI1145_REG_UCOEF2, SI1145_UCOEF2_DEFAULT); if (ret < 0) return ret;
ret = i2c_smbus_write_byte_data(data->client,
SI1145_REG_UCOEF3, SI1145_UCOEF3_DEFAULT); if (ret < 0) return ret;
ret = i2c_smbus_write_byte_data(data->client,
SI1145_REG_UCOEF4, SI1145_UCOEF4_DEFAULT); if (ret < 0) return ret;
}
return 0;
}
/* * Program the channels we want to measure with CMD_PSALS_AUTO. No need for * _postdisable as we stop with CMD_PSALS_PAUSE; single measurement (direct) * mode reprograms the channels list anyway...
*/ staticint si1145_buffer_preenable(struct iio_dev *indio_dev)
{ struct si1145_data *data = iio_priv(indio_dev); int ret;
mutex_lock(&data->lock);
ret = si1145_set_chlist(indio_dev, *indio_dev->active_scan_mask);
mutex_unlock(&data->lock);
/* Check that at most one AUX channel is enabled */
for_each_set_bit(i, scan_mask, data->part_info->num_channels) { if (indio_dev->channels[i].address == SI1145_REG_AUX_DATA)
count++;
}
/* * si1145_trigger_set_state() - Set trigger state * * When not using triggers interrupts are disabled and measurement rate is * set to zero in order to minimize power consumption.
*/ staticint si1145_trigger_set_state(struct iio_trigger *trig, bool state)
{ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); struct si1145_data *data = iio_priv(indio_dev); int err = 0, ret;
mutex_lock(&data->lock);
if (state) {
data->autonomous = true;
err = i2c_smbus_write_byte_data(data->client,
SI1145_REG_INT_CFG, SI1145_INT_CFG_OE); if (err < 0) goto disable;
err = i2c_smbus_write_byte_data(data->client,
SI1145_REG_IRQ_ENABLE, SI1145_MASK_ALL_IE); if (err < 0) goto disable;
err = si1145_set_meas_rate(data, data->meas_rate); if (err < 0) goto disable;
err = si1145_command(data, SI1145_CMD_PSALS_AUTO); if (err < 0) goto disable;
} else {
disable: /* Disable as much as possible skipping errors */
ret = si1145_command(data, SI1145_CMD_PSALS_PAUSE); if (ret < 0 && !err)
err = ret;
ret = si1145_set_meas_rate(data, 0); if (ret < 0 && !err)
err = ret;
ret = i2c_smbus_write_byte_data(data->client,
SI1145_REG_IRQ_ENABLE, 0); if (ret < 0 && !err)
err = ret;
ret = i2c_smbus_write_byte_data(data->client,
SI1145_REG_INT_CFG, 0); if (ret < 0 && !err)
err = ret;
data->autonomous = false;
}
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.