123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright 2021 Google LLC.
- *
- * Common part of most Semtech SAR sensor.
- */
- #include <linux/bitops.h>
- #include <linux/byteorder/generic.h>
- #include <linux/delay.h>
- #include <linux/device.h>
- #include <linux/err.h>
- #include <linux/export.h>
- #include <linux/interrupt.h>
- #include <linux/irqreturn.h>
- #include <linux/i2c.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/regmap.h>
- #include <linux/regulator/consumer.h>
- #include <vdso/bits.h>
- #include <linux/iio/buffer.h>
- #include <linux/iio/events.h>
- #include <linux/iio/iio.h>
- #include <linux/iio/trigger.h>
- #include <linux/iio/triggered_buffer.h>
- #include <linux/iio/trigger_consumer.h>
- #include "sx_common.h"
- /* All Semtech SAR sensors have IRQ bit in the same order. */
- #define SX_COMMON_CONVDONE_IRQ BIT(0)
- #define SX_COMMON_FAR_IRQ BIT(2)
- #define SX_COMMON_CLOSE_IRQ BIT(3)
- const struct iio_event_spec sx_common_events[3] = {
- {
- .type = IIO_EV_TYPE_THRESH,
- .dir = IIO_EV_DIR_RISING,
- .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD),
- },
- {
- .type = IIO_EV_TYPE_THRESH,
- .dir = IIO_EV_DIR_FALLING,
- .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD),
- },
- {
- .type = IIO_EV_TYPE_THRESH,
- .dir = IIO_EV_DIR_EITHER,
- .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
- BIT(IIO_EV_INFO_HYSTERESIS) |
- BIT(IIO_EV_INFO_VALUE),
- },
- };
- EXPORT_SYMBOL_NS_GPL(sx_common_events, SEMTECH_PROX);
- static irqreturn_t sx_common_irq_handler(int irq, void *private)
- {
- struct iio_dev *indio_dev = private;
- struct sx_common_data *data = iio_priv(indio_dev);
- if (data->trigger_enabled)
- iio_trigger_poll(data->trig);
- /*
- * Even if no event is enabled, we need to wake the thread to clear the
- * interrupt state by reading SX_COMMON_REG_IRQ_SRC.
- * It is not possible to do that here because regmap_read takes a mutex.
- */
- return IRQ_WAKE_THREAD;
- }
- static void sx_common_push_events(struct iio_dev *indio_dev)
- {
- int ret;
- unsigned int val, chan;
- struct sx_common_data *data = iio_priv(indio_dev);
- s64 timestamp = iio_get_time_ns(indio_dev);
- unsigned long prox_changed;
- /* Read proximity state on all channels */
- ret = regmap_read(data->regmap, data->chip_info->reg_stat, &val);
- if (ret) {
- dev_err(&data->client->dev, "i2c transfer error in irq\n");
- return;
- }
- val >>= data->chip_info->stat_offset;
- /*
- * Only iterate over channels with changes on proximity status that have
- * events enabled.
- */
- prox_changed = (data->chan_prox_stat ^ val) & data->chan_event;
- for_each_set_bit(chan, &prox_changed, data->chip_info->num_channels) {
- int dir;
- u64 ev;
- dir = (val & BIT(chan)) ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING;
- ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, chan,
- IIO_EV_TYPE_THRESH, dir);
- iio_push_event(indio_dev, ev, timestamp);
- }
- data->chan_prox_stat = val;
- }
- static int sx_common_enable_irq(struct sx_common_data *data, unsigned int irq)
- {
- if (!data->client->irq)
- return 0;
- return regmap_update_bits(data->regmap, data->chip_info->reg_irq_msk,
- irq << data->chip_info->irq_msk_offset,
- irq << data->chip_info->irq_msk_offset);
- }
- static int sx_common_disable_irq(struct sx_common_data *data, unsigned int irq)
- {
- if (!data->client->irq)
- return 0;
- return regmap_update_bits(data->regmap, data->chip_info->reg_irq_msk,
- irq << data->chip_info->irq_msk_offset, 0);
- }
- static int sx_common_update_chan_en(struct sx_common_data *data,
- unsigned long chan_read,
- unsigned long chan_event)
- {
- int ret;
- unsigned long channels = chan_read | chan_event;
- if ((data->chan_read | data->chan_event) != channels) {
- ret = regmap_update_bits(data->regmap,
- data->chip_info->reg_enable_chan,
- data->chip_info->mask_enable_chan,
- channels);
- if (ret)
- return ret;
- }
- data->chan_read = chan_read;
- data->chan_event = chan_event;
- return 0;
- }
- static int sx_common_get_read_channel(struct sx_common_data *data, int channel)
- {
- return sx_common_update_chan_en(data, data->chan_read | BIT(channel),
- data->chan_event);
- }
- static int sx_common_put_read_channel(struct sx_common_data *data, int channel)
- {
- return sx_common_update_chan_en(data, data->chan_read & ~BIT(channel),
- data->chan_event);
- }
- static int sx_common_get_event_channel(struct sx_common_data *data, int channel)
- {
- return sx_common_update_chan_en(data, data->chan_read,
- data->chan_event | BIT(channel));
- }
- static int sx_common_put_event_channel(struct sx_common_data *data, int channel)
- {
- return sx_common_update_chan_en(data, data->chan_read,
- data->chan_event & ~BIT(channel));
- }
- /**
- * sx_common_read_proximity() - Read raw proximity value.
- * @data: Internal data
- * @chan: Channel to read
- * @val: pointer to return read value.
- *
- * Request a conversion, wait for the sensor to be ready and
- * return the raw proximity value.
- */
- int sx_common_read_proximity(struct sx_common_data *data,
- const struct iio_chan_spec *chan, int *val)
- {
- int ret;
- __be16 rawval;
- mutex_lock(&data->mutex);
- ret = sx_common_get_read_channel(data, chan->channel);
- if (ret)
- goto out;
- ret = sx_common_enable_irq(data, SX_COMMON_CONVDONE_IRQ);
- if (ret)
- goto out_put_channel;
- mutex_unlock(&data->mutex);
- if (data->client->irq) {
- ret = wait_for_completion_interruptible(&data->completion);
- reinit_completion(&data->completion);
- } else {
- ret = data->chip_info->ops.wait_for_sample(data);
- }
- mutex_lock(&data->mutex);
- if (ret)
- goto out_disable_irq;
- ret = data->chip_info->ops.read_prox_data(data, chan, &rawval);
- if (ret)
- goto out_disable_irq;
- *val = sign_extend32(be16_to_cpu(rawval), chan->scan_type.realbits - 1);
- ret = sx_common_disable_irq(data, SX_COMMON_CONVDONE_IRQ);
- if (ret)
- goto out_put_channel;
- ret = sx_common_put_read_channel(data, chan->channel);
- if (ret)
- goto out;
- mutex_unlock(&data->mutex);
- return IIO_VAL_INT;
- out_disable_irq:
- sx_common_disable_irq(data, SX_COMMON_CONVDONE_IRQ);
- out_put_channel:
- sx_common_put_read_channel(data, chan->channel);
- out:
- mutex_unlock(&data->mutex);
- return ret;
- }
- EXPORT_SYMBOL_NS_GPL(sx_common_read_proximity, SEMTECH_PROX);
- /**
- * sx_common_read_event_config() - Configure event setting.
- * @indio_dev: iio device object
- * @chan: Channel to read
- * @type: Type of event (unused)
- * @dir: Direction of event (unused)
- *
- * return if the given channel is used for event gathering.
- */
- int sx_common_read_event_config(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan,
- enum iio_event_type type,
- enum iio_event_direction dir)
- {
- struct sx_common_data *data = iio_priv(indio_dev);
- return !!(data->chan_event & BIT(chan->channel));
- }
- EXPORT_SYMBOL_NS_GPL(sx_common_read_event_config, SEMTECH_PROX);
- /**
- * sx_common_write_event_config() - Configure event setting.
- * @indio_dev: iio device object
- * @chan: Channel to enable
- * @type: Type of event (unused)
- * @dir: Direction of event (unused)
- * @state: State of the event.
- *
- * Enable/Disable event on a given channel.
- */
- int sx_common_write_event_config(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan,
- enum iio_event_type type,
- enum iio_event_direction dir, int state)
- {
- struct sx_common_data *data = iio_priv(indio_dev);
- unsigned int eventirq = SX_COMMON_FAR_IRQ | SX_COMMON_CLOSE_IRQ;
- int ret;
- /* If the state hasn't changed, there's nothing to do. */
- if (!!(data->chan_event & BIT(chan->channel)) == state)
- return 0;
- mutex_lock(&data->mutex);
- if (state) {
- ret = sx_common_get_event_channel(data, chan->channel);
- if (ret)
- goto out_unlock;
- if (!(data->chan_event & ~BIT(chan->channel))) {
- ret = sx_common_enable_irq(data, eventirq);
- if (ret)
- sx_common_put_event_channel(data, chan->channel);
- }
- } else {
- ret = sx_common_put_event_channel(data, chan->channel);
- if (ret)
- goto out_unlock;
- if (!data->chan_event) {
- ret = sx_common_disable_irq(data, eventirq);
- if (ret)
- sx_common_get_event_channel(data, chan->channel);
- }
- }
- out_unlock:
- mutex_unlock(&data->mutex);
- return ret;
- }
- EXPORT_SYMBOL_NS_GPL(sx_common_write_event_config, SEMTECH_PROX);
- static int sx_common_set_trigger_state(struct iio_trigger *trig, bool state)
- {
- struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
- struct sx_common_data *data = iio_priv(indio_dev);
- int ret = 0;
- mutex_lock(&data->mutex);
- if (state)
- ret = sx_common_enable_irq(data, SX_COMMON_CONVDONE_IRQ);
- else if (!data->chan_read)
- ret = sx_common_disable_irq(data, SX_COMMON_CONVDONE_IRQ);
- if (ret)
- goto out;
- data->trigger_enabled = state;
- out:
- mutex_unlock(&data->mutex);
- return ret;
- }
- static const struct iio_trigger_ops sx_common_trigger_ops = {
- .set_trigger_state = sx_common_set_trigger_state,
- };
- static irqreturn_t sx_common_irq_thread_handler(int irq, void *private)
- {
- struct iio_dev *indio_dev = private;
- struct sx_common_data *data = iio_priv(indio_dev);
- int ret;
- unsigned int val;
- mutex_lock(&data->mutex);
- ret = regmap_read(data->regmap, SX_COMMON_REG_IRQ_SRC, &val);
- if (ret) {
- dev_err(&data->client->dev, "i2c transfer error in irq\n");
- goto out;
- }
- if (val & ((SX_COMMON_FAR_IRQ | SX_COMMON_CLOSE_IRQ) << data->chip_info->irq_msk_offset))
- sx_common_push_events(indio_dev);
- if (val & (SX_COMMON_CONVDONE_IRQ << data->chip_info->irq_msk_offset))
- complete(&data->completion);
- out:
- mutex_unlock(&data->mutex);
- return IRQ_HANDLED;
- }
- static irqreturn_t sx_common_trigger_handler(int irq, void *private)
- {
- struct iio_poll_func *pf = private;
- struct iio_dev *indio_dev = pf->indio_dev;
- struct sx_common_data *data = iio_priv(indio_dev);
- __be16 val;
- int bit, ret, i = 0;
- mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->active_scan_mask,
- indio_dev->masklength) {
- ret = data->chip_info->ops.read_prox_data(data,
- &indio_dev->channels[bit],
- &val);
- if (ret)
- goto out;
- data->buffer.channels[i++] = val;
- }
- iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer,
- pf->timestamp);
- out:
- mutex_unlock(&data->mutex);
- iio_trigger_notify_done(indio_dev->trig);
- return IRQ_HANDLED;
- }
- static int sx_common_buffer_preenable(struct iio_dev *indio_dev)
- {
- struct sx_common_data *data = iio_priv(indio_dev);
- unsigned long channels = 0;
- int bit, ret;
- mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->active_scan_mask,
- indio_dev->masklength)
- __set_bit(indio_dev->channels[bit].channel, &channels);
- ret = sx_common_update_chan_en(data, channels, data->chan_event);
- mutex_unlock(&data->mutex);
- return ret;
- }
- static int sx_common_buffer_postdisable(struct iio_dev *indio_dev)
- {
- struct sx_common_data *data = iio_priv(indio_dev);
- int ret;
- mutex_lock(&data->mutex);
- ret = sx_common_update_chan_en(data, 0, data->chan_event);
- mutex_unlock(&data->mutex);
- return ret;
- }
- static const struct iio_buffer_setup_ops sx_common_buffer_setup_ops = {
- .preenable = sx_common_buffer_preenable,
- .postdisable = sx_common_buffer_postdisable,
- };
- static void sx_common_regulator_disable(void *_data)
- {
- struct sx_common_data *data = _data;
- regulator_bulk_disable(ARRAY_SIZE(data->supplies), data->supplies);
- }
- #define SX_COMMON_SOFT_RESET 0xde
- static int sx_common_init_device(struct device *dev, struct iio_dev *indio_dev)
- {
- struct sx_common_data *data = iio_priv(indio_dev);
- struct sx_common_reg_default tmp;
- const struct sx_common_reg_default *initval;
- int ret;
- unsigned int i, val;
- ret = regmap_write(data->regmap, data->chip_info->reg_reset,
- SX_COMMON_SOFT_RESET);
- if (ret)
- return ret;
- usleep_range(1000, 2000); /* power-up time is ~1ms. */
- /* Clear reset interrupt state by reading SX_COMMON_REG_IRQ_SRC. */
- ret = regmap_read(data->regmap, SX_COMMON_REG_IRQ_SRC, &val);
- if (ret)
- return ret;
- /* Program defaults from constant or BIOS. */
- for (i = 0; i < data->chip_info->num_default_regs; i++) {
- initval = data->chip_info->ops.get_default_reg(dev, i, &tmp);
- ret = regmap_write(data->regmap, initval->reg, initval->def);
- if (ret)
- return ret;
- }
- return data->chip_info->ops.init_compensation(indio_dev);
- }
- /**
- * sx_common_probe() - Common setup for Semtech SAR sensor
- * @client: I2C client object
- * @chip_info: Semtech sensor chip information.
- * @regmap_config: Sensor registers map configuration.
- */
- int sx_common_probe(struct i2c_client *client,
- const struct sx_common_chip_info *chip_info,
- const struct regmap_config *regmap_config)
- {
- struct device *dev = &client->dev;
- struct iio_dev *indio_dev;
- struct sx_common_data *data;
- int ret;
- indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
- if (!indio_dev)
- return -ENOMEM;
- data = iio_priv(indio_dev);
- data->chip_info = chip_info;
- data->client = client;
- data->supplies[0].supply = "vdd";
- data->supplies[1].supply = "svdd";
- mutex_init(&data->mutex);
- init_completion(&data->completion);
- data->regmap = devm_regmap_init_i2c(client, regmap_config);
- if (IS_ERR(data->regmap))
- return dev_err_probe(dev, PTR_ERR(data->regmap),
- "Could init register map\n");
- ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
- data->supplies);
- if (ret)
- return dev_err_probe(dev, ret, "Unable to get regulators\n");
- ret = regulator_bulk_enable(ARRAY_SIZE(data->supplies), data->supplies);
- if (ret)
- return dev_err_probe(dev, ret, "Unable to enable regulators\n");
- /* Must wait for Tpor time after initial power up */
- usleep_range(1000, 1100);
- ret = devm_add_action_or_reset(dev, sx_common_regulator_disable, data);
- if (ret)
- return dev_err_probe(dev, ret,
- "Unable to register regulators deleter\n");
- ret = data->chip_info->ops.check_whoami(dev, indio_dev);
- if (ret)
- return dev_err_probe(dev, ret, "error reading WHOAMI\n");
- indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = data->chip_info->iio_channels;
- indio_dev->num_channels = data->chip_info->num_iio_channels;
- indio_dev->info = &data->chip_info->iio_info;
- i2c_set_clientdata(client, indio_dev);
- ret = sx_common_init_device(dev, indio_dev);
- if (ret)
- return dev_err_probe(dev, ret, "Unable to initialize sensor\n");
- if (client->irq) {
- ret = devm_request_threaded_irq(dev, client->irq,
- sx_common_irq_handler,
- sx_common_irq_thread_handler,
- IRQF_ONESHOT,
- "sx_event", indio_dev);
- if (ret)
- return dev_err_probe(dev, ret, "No IRQ\n");
- data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
- indio_dev->name,
- iio_device_id(indio_dev));
- if (!data->trig)
- return -ENOMEM;
- data->trig->ops = &sx_common_trigger_ops;
- iio_trigger_set_drvdata(data->trig, indio_dev);
- ret = devm_iio_trigger_register(dev, data->trig);
- if (ret)
- return ret;
- }
- ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
- iio_pollfunc_store_time,
- sx_common_trigger_handler,
- &sx_common_buffer_setup_ops);
- if (ret)
- return ret;
- return devm_iio_device_register(dev, indio_dev);
- }
- EXPORT_SYMBOL_NS_GPL(sx_common_probe, SEMTECH_PROX);
- MODULE_AUTHOR("Gwendal Grignou <[email protected]>");
- MODULE_DESCRIPTION("Common functions and structures for Semtech sensor");
- MODULE_LICENSE("GPL v2");
|