/* * The pattern is 0b01101, but store it reversed (0b10110) due to writing from * LSB on the wire (c.f. d3323aa_write_settings()).
*/ #define D3323AA_SETTING_END_PATTERN 0x16 #define D3323AA_SETTING_END_PATTERN_NR_BITS 5
/* * Device should be ready for configuration after this many milliseconds. * Datasheet mentions "approx. 1.2 s". Measurements show around 1.23 s, * therefore add 100 ms of slack.
*/ #define D3323AA_RESET_TIMEOUT (1200 + 100)
/* * The configuration of the device (write and read) should be done within this * many milliseconds.
*/ #define D3323AA_CONFIG_TIMEOUT 1400
/* Number of IRQs needed for configuration stage after reset. */ #define D3323AA_IRQ_RESET_COUNT 2
/* * High-pass filter cutoff frequency for the band-pass filter. There is a * corresponding low-pass cutoff frequency for each of the filter types * (denoted A, B, C and D in the datasheet). The index in this array matches * that corresponding value in d3323aa_lp_filter_freq. * Note that this represents a fractional value (e.g. the first value * corresponds to 40 / 100 = 0.4 Hz).
*/ staticconstint d3323aa_hp_filter_freq[][2] = {
{ 40, 100 },
{ 30, 100 },
{ 30, 100 },
{ 1, 100 },
};
/* * Low-pass filter cutoff frequency for the band-pass filter. There is a * corresponding high-pass cutoff frequency for each of the filter types * (denoted A, B, C and D in the datasheet). The index in this array matches * that corresponding value in d3323aa_hp_filter_freq. * Note that this represents a fractional value (e.g. the first value * corresponds to 27 / 10 = 2.7 Hz).
*/ staticconstint d3323aa_lp_filter_freq[][2] = {
{ 27, 10 },
{ 15, 10 },
{ 5, 1 },
{ 100, 1 },
};
/* * Register bitmap values for filter types (denoted A, B, C and D in the * datasheet). The index in this array matches the corresponding value in * d3323aa_lp_filter_freq (which in turn matches d3323aa_hp_filter_freq). For * example, the first value 7 corresponds to 2.7 Hz low-pass and 0.4 Hz * high-pass cutoff frequency.
*/ staticconstint d3323aa_lp_filter_regval[] = {
7,
0,
1,
2,
};
/* * This is denoted as "step" in datasheet and corresponds to the gain at peak * for the band-pass filter. The index in this array is the corresponding index * in d3323aa_filter_gain_regval for the register bitmap value.
*/ staticconstint d3323aa_filter_gain[] = { 1, 2, 3 };
/* * Register bitmap values for the filter gain. The index in this array is the * corresponding index in d3323aa_filter_gain for the gain value.
*/ staticconst u8 d3323aa_filter_gain_regval[] = { 1, 3, 0 };
struct d3323aa_data { struct completion reset_completion; /* * Since the setup process always requires a complete write of _all_ * the state variables, we need to synchronize them with a lock.
*/ struct mutex statevar_lock;
struct device *dev;
/* Supply voltage. */ struct regulator *regulator_vdd; /* Input clock or output detection signal (Vout). */ struct gpio_desc *gpiod_clkin_detectout; /* Input (setting) or output data. */ struct gpio_desc *gpiod_data;
/* * We only need the low-pass cutoff frequency to unambiguously choose * the type of band-pass filter. For example, both filter type B and C * have 0.3 Hz as high-pass cutoff frequency (see * d3323aa_hp_filter_freq).
*/
size_t lp_filter_freq_idx;
size_t filter_gain_idx;
u8 detect_thresh;
u8 irq_reset_count;
/* Indicator for operational mode (configuring or detecting). */ bool detecting;
};
/* Bit bang the clock and data pins. */
ret = gpiod_direction_output(data->gpiod_clkin_detectout, 0); if (ret) return ret;
ret = gpiod_direction_output(data->gpiod_data, 0); if (ret) return ret;
dev_dbg(data->dev, "Writing settings...\n");
/* First bit (F37) is not used when writing the register bitmap. */ for (i = 1; i < REGBITMAP_LEN; ++i) {
gpiod_set_value(data->gpiod_data, test_bit(i, regbitmap));
/* Clock frequency needs to be 1 kHz. */
gpiod_set_value(data->gpiod_clkin_detectout, 1);
udelay(500);
gpiod_set_value(data->gpiod_clkin_detectout, 0);
udelay(500);
}
/* Datasheet says to wait 30 ms after writing the settings. */
msleep(30);
val = gpiod_get_value(data->gpiod_clkin_detectout); if (val < 0) {
dev_err_ratelimited(data->dev, "Could not read from GPIO vout-clk (%d)\n",
val); return IRQ_HANDLED;
}
if (!data->detecting) { /* Reset interrupt counting falling edges. */ if (!val && ++data->irq_reset_count == D3323AA_IRQ_RESET_COUNT)
complete(&data->reset_completion);
return IRQ_HANDLED;
}
/* Detection interrupt. */
dir = val ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
IIO_EV_TYPE_THRESH, dir),
iio_get_time_ns(indio_dev));
return IRQ_HANDLED;
}
staticint d3323aa_reset(struct iio_dev *indio_dev)
{ struct d3323aa_data *data = iio_priv(indio_dev); long time; int ret;
/* During probe() the regulator may already be disabled. */ if (regulator_is_enabled(data->regulator_vdd)) {
ret = regulator_disable(data->regulator_vdd); if (ret) return ret;
}
/* * Datasheet says VDD needs to be low at least for 30 ms. Let's add a * couple more to allow VDD to completely discharge as well.
*/
fsleep((30 + 5) * USEC_PER_MSEC);
/* * When later enabling VDD, the device will signal with * D3323AA_IRQ_RESET_COUNT falling edges on Vout/CLK that it is now * ready for configuration. Datasheet says that this should happen * within D3323AA_RESET_TIMEOUT ms. Count these two edges within that * timeout.
*/
data->irq_reset_count = 0;
reinit_completion(&data->reset_completion);
data->detecting = false;
ret = gpiod_direction_input(data->gpiod_clkin_detectout); if (ret) return ret;
dev_dbg(data->dev, "Resetting...\n");
ret = regulator_enable(data->regulator_vdd); if (ret) return ret;
/* * Wait for VDD to completely charge up. Measurements have shown that * Vout/CLK signal slowly ramps up during this period. Thus, the digital * signal will have bogus values. It is therefore necessary to wait * before we can count the "real" falling edges.
*/
fsleep(2000);
ret = d3323aa_reset(indio_dev); if (ret) { if (ret != -ERESTARTSYS)
dev_err(data->dev, "Could not reset device (%d)\n",
ret);
return ret;
}
/* * Datasheet says to wait 10 us before setting the configuration. * Moreover, the total configuration should be done within * D3323AA_CONFIG_TIMEOUT ms. Clock it.
*/
fsleep(10);
start_time = jiffies;
ret = d3323aa_write_settings(indio_dev, write_regbitmap); if (ret) {
dev_err(data->dev, "Could not write settings (%d)\n", ret); return ret;
}
ret = d3323aa_read_settings(indio_dev, read_regbitmap); if (ret) {
dev_err(data->dev, "Could not read settings (%d)\n", ret); return ret;
}
if (time_is_before_jiffies(start_time +
msecs_to_jiffies(D3323AA_CONFIG_TIMEOUT))) {
dev_err(data->dev, "Could not set up configuration in time\n"); return -EAGAIN;
}
/* Check if settings were set successfully. */ if (!bitmap_equal(write_regbitmap, read_regbitmap,
D3323AA_REG_NR_BITS)) {
dev_err(data->dev, "Settings data mismatch\n"); return -EIO;
}
/* Now in operational mode. */
ret = gpiod_direction_input(data->gpiod_clkin_detectout); if (ret) {
dev_err(data->dev, "Could not set GPIO vout-clk as input (%d)\n", ret); return ret;
}
ret = gpiod_direction_input(data->gpiod_data); if (ret) {
dev_err(data->dev, "Could not set GPIO data as input (%d)\n",
ret); return ret;
}
/* Truncate fractional part to two digits. */
val2 /= 10000;
for (idx = 0; idx < ARRAY_SIZE(d3323aa_hp_filter_freq); ++idx) { int integer = d3323aa_hp_filter_freq[idx][0] /
d3323aa_hp_filter_freq[idx][1]; int fract = d3323aa_hp_filter_freq[idx][0] %
d3323aa_hp_filter_freq[idx][1];
if (val == integer && val2 == fract) break;
}
if (idx == ARRAY_SIZE(d3323aa_hp_filter_freq)) return -EINVAL;
if (idx == data->lp_filter_freq_idx) { /* Corresponding filter frequency already set. */ return 0;
}
if (idx == 1 && data->lp_filter_freq_idx == 2) { /* * The low-pass cutoff frequency is the only way to * unambiguously choose the type of band-pass filter. For * example, both filter type B (index 1) and C (index 2) have * 0.3 Hz as high-pass cutoff frequency (see * d3323aa_hp_filter_freq). Therefore, if one of these are * requested _and_ the corresponding low-pass filter frequency * is already set, we can't know which filter type is the wanted * one. The low-pass filter frequency is the decider (i.e. in * this case index 2).
*/ return 0;
}
/* * During probe() the regulator may be disabled. It is enabled during * device setup (in d3323aa_reset(), where it is also briefly disabled). * The check is therefore needed in order to have balanced * regulator_enable/disable() calls.
*/ if (!regulator_is_enabled(data->regulator_vdd)) return;
ret = regulator_disable(data->regulator_vdd); if (ret)
dev_err(data->dev, "Could not disable regulator (%d)\n", ret);
}
indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return dev_err_probe(dev, -ENOMEM, "Could not allocate iio device\n");
data = iio_priv(indio_dev);
data->dev = dev;
init_completion(&data->reset_completion);
ret = devm_mutex_init(dev, &data->statevar_lock); if (ret) return dev_err_probe(dev, ret, "Could not initialize mutex\n");
data->regulator_vdd = devm_regulator_get_exclusive(dev, "vdd"); if (IS_ERR(data->regulator_vdd)) return dev_err_probe(dev, PTR_ERR(data->regulator_vdd), "Could not get regulator\n");
/* * The regulator will be enabled for the first time during the * device setup below (in d3323aa_reset()). However parameter changes * from userspace can require a temporary disable of the regulator. * To avoid complex handling of state, use a callback that will disable * the regulator if it happens to be enabled at time of devm unwind.
*/
ret = devm_add_action_or_reset(dev, d3323aa_disable_regulator, data); if (ret) return ret;
data->gpiod_clkin_detectout =
devm_gpiod_get(dev, "vout-clk", GPIOD_OUT_LOW); if (IS_ERR(data->gpiod_clkin_detectout)) return dev_err_probe(dev, PTR_ERR(data->gpiod_clkin_detectout), "Could not get GPIO vout-clk\n");
data->gpiod_data = devm_gpiod_get(dev, "data", GPIOD_OUT_LOW); if (IS_ERR(data->gpiod_data)) return dev_err_probe(dev, PTR_ERR(data->gpiod_data), "Could not get GPIO data\n");
ret = gpiod_to_irq(data->gpiod_clkin_detectout); if (ret < 0) return dev_err_probe(dev, ret, "Could not get IRQ\n");
/* * Device signals with a rising or falling detection signal when the * proximity data is above or below the threshold, respectively.
*/
ret = devm_request_irq(dev, ret, d3323aa_irq_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
dev_name(dev), indio_dev); if (ret) return dev_err_probe(dev, ret, "Could not request IRQ\n");
ret = d3323aa_setup(indio_dev, D3323AA_LP_FILTER_FREQ_DEFAULT_IDX,
D3323AA_FILTER_GAIN_DEFAULT_IDX,
D3323AA_THRESH_DEFAULT_VAL); if (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.