1d6ad8058SMatt Ranostay // SPDX-License-Identifier: GPL-2.0+ 24d671b71SMatt Ranostay /* 34d671b71SMatt Ranostay * ti-adc161s626.c - Texas Instruments ADC161S626 1-channel differential ADC 44d671b71SMatt Ranostay * 54d671b71SMatt Ranostay * ADC Devices Supported: 64d671b71SMatt Ranostay * adc141s626 - 14-bit ADC 74d671b71SMatt Ranostay * adc161s626 - 16-bit ADC 84d671b71SMatt Ranostay * 9d6ad8058SMatt Ranostay * Copyright (C) 2016-2018 10d6ad8058SMatt Ranostay * Author: Matt Ranostay <matt.ranostay@konsulko.com> 114d671b71SMatt Ranostay */ 124d671b71SMatt Ranostay 134d671b71SMatt Ranostay #include <linux/module.h> 14*a6b40706SJonathan Cameron #include <linux/mod_devicetable.h> 154d671b71SMatt Ranostay #include <linux/init.h> 164d671b71SMatt Ranostay #include <linux/err.h> 174d671b71SMatt Ranostay #include <linux/spi/spi.h> 184d671b71SMatt Ranostay #include <linux/iio/iio.h> 194d671b71SMatt Ranostay #include <linux/iio/trigger.h> 204d671b71SMatt Ranostay #include <linux/iio/buffer.h> 214d671b71SMatt Ranostay #include <linux/iio/trigger_consumer.h> 224d671b71SMatt Ranostay #include <linux/iio/triggered_buffer.h> 2392f0afb5SMatt Ranostay #include <linux/regulator/consumer.h> 244d671b71SMatt Ranostay 254d671b71SMatt Ranostay #define TI_ADC_DRV_NAME "ti-adc161s626" 264d671b71SMatt Ranostay 274d671b71SMatt Ranostay enum { 284d671b71SMatt Ranostay TI_ADC141S626, 294d671b71SMatt Ranostay TI_ADC161S626, 304d671b71SMatt Ranostay }; 314d671b71SMatt Ranostay 324d671b71SMatt Ranostay static const struct iio_chan_spec ti_adc141s626_channels[] = { 334d671b71SMatt Ranostay { 344d671b71SMatt Ranostay .type = IIO_VOLTAGE, 354d671b71SMatt Ranostay .channel = 0, 3692f0afb5SMatt Ranostay .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 3792f0afb5SMatt Ranostay BIT(IIO_CHAN_INFO_SCALE) | 3892f0afb5SMatt Ranostay BIT(IIO_CHAN_INFO_OFFSET), 394d671b71SMatt Ranostay .scan_index = 0, 404d671b71SMatt Ranostay .scan_type = { 414d671b71SMatt Ranostay .sign = 's', 424d671b71SMatt Ranostay .realbits = 14, 434d671b71SMatt Ranostay .storagebits = 16, 444d671b71SMatt Ranostay }, 454d671b71SMatt Ranostay }, 464d671b71SMatt Ranostay IIO_CHAN_SOFT_TIMESTAMP(1), 474d671b71SMatt Ranostay }; 484d671b71SMatt Ranostay 494d671b71SMatt Ranostay static const struct iio_chan_spec ti_adc161s626_channels[] = { 504d671b71SMatt Ranostay { 514d671b71SMatt Ranostay .type = IIO_VOLTAGE, 524d671b71SMatt Ranostay .channel = 0, 5392f0afb5SMatt Ranostay .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 5492f0afb5SMatt Ranostay BIT(IIO_CHAN_INFO_SCALE) | 5592f0afb5SMatt Ranostay BIT(IIO_CHAN_INFO_OFFSET), 564d671b71SMatt Ranostay .scan_index = 0, 574d671b71SMatt Ranostay .scan_type = { 584d671b71SMatt Ranostay .sign = 's', 594d671b71SMatt Ranostay .realbits = 16, 604d671b71SMatt Ranostay .storagebits = 16, 614d671b71SMatt Ranostay }, 624d671b71SMatt Ranostay }, 634d671b71SMatt Ranostay IIO_CHAN_SOFT_TIMESTAMP(1), 644d671b71SMatt Ranostay }; 654d671b71SMatt Ranostay 664d671b71SMatt Ranostay struct ti_adc_data { 674d671b71SMatt Ranostay struct iio_dev *indio_dev; 684d671b71SMatt Ranostay struct spi_device *spi; 6992f0afb5SMatt Ranostay struct regulator *ref; 7092f0afb5SMatt Ranostay 714d671b71SMatt Ranostay u8 read_size; 724d671b71SMatt Ranostay u8 shift; 734d671b71SMatt Ranostay 744d671b71SMatt Ranostay u8 buffer[16] ____cacheline_aligned; 754d671b71SMatt Ranostay }; 764d671b71SMatt Ranostay 774d671b71SMatt Ranostay static int ti_adc_read_measurement(struct ti_adc_data *data, 784d671b71SMatt Ranostay struct iio_chan_spec const *chan, int *val) 794d671b71SMatt Ranostay { 804d671b71SMatt Ranostay int ret; 814d671b71SMatt Ranostay 824d671b71SMatt Ranostay switch (data->read_size) { 834d671b71SMatt Ranostay case 2: { 844d671b71SMatt Ranostay __be16 buf; 854d671b71SMatt Ranostay 864d671b71SMatt Ranostay ret = spi_read(data->spi, (void *) &buf, 2); 874d671b71SMatt Ranostay if (ret) 884d671b71SMatt Ranostay return ret; 894d671b71SMatt Ranostay 904d671b71SMatt Ranostay *val = be16_to_cpu(buf); 914d671b71SMatt Ranostay break; 924d671b71SMatt Ranostay } 934d671b71SMatt Ranostay case 3: { 944d671b71SMatt Ranostay __be32 buf; 954d671b71SMatt Ranostay 964d671b71SMatt Ranostay ret = spi_read(data->spi, (void *) &buf, 3); 974d671b71SMatt Ranostay if (ret) 984d671b71SMatt Ranostay return ret; 994d671b71SMatt Ranostay 1004d671b71SMatt Ranostay *val = be32_to_cpu(buf) >> 8; 1014d671b71SMatt Ranostay break; 1024d671b71SMatt Ranostay } 1034d671b71SMatt Ranostay default: 1044d671b71SMatt Ranostay return -EINVAL; 1054d671b71SMatt Ranostay } 1064d671b71SMatt Ranostay 1074d671b71SMatt Ranostay *val = sign_extend32(*val >> data->shift, chan->scan_type.realbits - 1); 1084d671b71SMatt Ranostay 1094d671b71SMatt Ranostay return 0; 1104d671b71SMatt Ranostay } 1114d671b71SMatt Ranostay 1124d671b71SMatt Ranostay static irqreturn_t ti_adc_trigger_handler(int irq, void *private) 1134d671b71SMatt Ranostay { 1144d671b71SMatt Ranostay struct iio_poll_func *pf = private; 1154d671b71SMatt Ranostay struct iio_dev *indio_dev = pf->indio_dev; 1164d671b71SMatt Ranostay struct ti_adc_data *data = iio_priv(indio_dev); 1174d671b71SMatt Ranostay int ret; 1184d671b71SMatt Ranostay 1194d671b71SMatt Ranostay ret = ti_adc_read_measurement(data, &indio_dev->channels[0], 1204d671b71SMatt Ranostay (int *) &data->buffer); 1214d671b71SMatt Ranostay if (!ret) 1224d671b71SMatt Ranostay iio_push_to_buffers_with_timestamp(indio_dev, 1234d671b71SMatt Ranostay data->buffer, 1244d671b71SMatt Ranostay iio_get_time_ns(indio_dev)); 1254d671b71SMatt Ranostay 1264d671b71SMatt Ranostay iio_trigger_notify_done(indio_dev->trig); 1274d671b71SMatt Ranostay 1284d671b71SMatt Ranostay return IRQ_HANDLED; 1294d671b71SMatt Ranostay } 1304d671b71SMatt Ranostay 1314d671b71SMatt Ranostay static int ti_adc_read_raw(struct iio_dev *indio_dev, 1324d671b71SMatt Ranostay struct iio_chan_spec const *chan, 1334d671b71SMatt Ranostay int *val, int *val2, long mask) 1344d671b71SMatt Ranostay { 1354d671b71SMatt Ranostay struct ti_adc_data *data = iio_priv(indio_dev); 1364d671b71SMatt Ranostay int ret; 1374d671b71SMatt Ranostay 13892f0afb5SMatt Ranostay switch (mask) { 13992f0afb5SMatt Ranostay case IIO_CHAN_INFO_RAW: 1404d671b71SMatt Ranostay ret = iio_device_claim_direct_mode(indio_dev); 1414d671b71SMatt Ranostay if (ret) 1424d671b71SMatt Ranostay return ret; 1434d671b71SMatt Ranostay 1444d671b71SMatt Ranostay ret = ti_adc_read_measurement(data, chan, val); 1454d671b71SMatt Ranostay iio_device_release_direct_mode(indio_dev); 1464d671b71SMatt Ranostay 14792f0afb5SMatt Ranostay if (ret) 14892f0afb5SMatt Ranostay return ret; 14992f0afb5SMatt Ranostay 1504d671b71SMatt Ranostay return IIO_VAL_INT; 15192f0afb5SMatt Ranostay case IIO_CHAN_INFO_SCALE: 15292f0afb5SMatt Ranostay ret = regulator_get_voltage(data->ref); 15392f0afb5SMatt Ranostay if (ret < 0) 15492f0afb5SMatt Ranostay return ret; 15592f0afb5SMatt Ranostay 15692f0afb5SMatt Ranostay *val = ret / 1000; 15792f0afb5SMatt Ranostay *val2 = chan->scan_type.realbits; 15892f0afb5SMatt Ranostay 15992f0afb5SMatt Ranostay return IIO_VAL_FRACTIONAL_LOG2; 16092f0afb5SMatt Ranostay case IIO_CHAN_INFO_OFFSET: 16192f0afb5SMatt Ranostay *val = 1 << (chan->scan_type.realbits - 1); 16292f0afb5SMatt Ranostay return IIO_VAL_INT; 16392f0afb5SMatt Ranostay } 1644d671b71SMatt Ranostay 1654d671b71SMatt Ranostay return 0; 1664d671b71SMatt Ranostay } 1674d671b71SMatt Ranostay 1684d671b71SMatt Ranostay static const struct iio_info ti_adc_info = { 1694d671b71SMatt Ranostay .read_raw = ti_adc_read_raw, 1704d671b71SMatt Ranostay }; 1714d671b71SMatt Ranostay 1724d671b71SMatt Ranostay static int ti_adc_probe(struct spi_device *spi) 1734d671b71SMatt Ranostay { 1744d671b71SMatt Ranostay struct iio_dev *indio_dev; 1754d671b71SMatt Ranostay struct ti_adc_data *data; 1764d671b71SMatt Ranostay int ret; 1774d671b71SMatt Ranostay 1784d671b71SMatt Ranostay indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); 1794d671b71SMatt Ranostay if (!indio_dev) 1804d671b71SMatt Ranostay return -ENOMEM; 1814d671b71SMatt Ranostay 1824d671b71SMatt Ranostay indio_dev->info = &ti_adc_info; 1834d671b71SMatt Ranostay indio_dev->name = TI_ADC_DRV_NAME; 1844d671b71SMatt Ranostay indio_dev->modes = INDIO_DIRECT_MODE; 1854d671b71SMatt Ranostay spi_set_drvdata(spi, indio_dev); 1864d671b71SMatt Ranostay 1874d671b71SMatt Ranostay data = iio_priv(indio_dev); 1884d671b71SMatt Ranostay data->spi = spi; 1894d671b71SMatt Ranostay 1904d671b71SMatt Ranostay switch (spi_get_device_id(spi)->driver_data) { 1914d671b71SMatt Ranostay case TI_ADC141S626: 1924d671b71SMatt Ranostay indio_dev->channels = ti_adc141s626_channels; 1934d671b71SMatt Ranostay indio_dev->num_channels = ARRAY_SIZE(ti_adc141s626_channels); 1944d671b71SMatt Ranostay data->shift = 0; 1954d671b71SMatt Ranostay data->read_size = 2; 1964d671b71SMatt Ranostay break; 1974d671b71SMatt Ranostay case TI_ADC161S626: 1984d671b71SMatt Ranostay indio_dev->channels = ti_adc161s626_channels; 1994d671b71SMatt Ranostay indio_dev->num_channels = ARRAY_SIZE(ti_adc161s626_channels); 2004d671b71SMatt Ranostay data->shift = 6; 2014d671b71SMatt Ranostay data->read_size = 3; 2024d671b71SMatt Ranostay break; 2034d671b71SMatt Ranostay } 2044d671b71SMatt Ranostay 20592f0afb5SMatt Ranostay data->ref = devm_regulator_get(&spi->dev, "vdda"); 20692f0afb5SMatt Ranostay if (!IS_ERR(data->ref)) { 20792f0afb5SMatt Ranostay ret = regulator_enable(data->ref); 20892f0afb5SMatt Ranostay if (ret < 0) 20992f0afb5SMatt Ranostay return ret; 21092f0afb5SMatt Ranostay } 21192f0afb5SMatt Ranostay 2124d671b71SMatt Ranostay ret = iio_triggered_buffer_setup(indio_dev, NULL, 2134d671b71SMatt Ranostay ti_adc_trigger_handler, NULL); 2144d671b71SMatt Ranostay if (ret) 21592f0afb5SMatt Ranostay goto error_regulator_disable; 2164d671b71SMatt Ranostay 2174d671b71SMatt Ranostay ret = iio_device_register(indio_dev); 2184d671b71SMatt Ranostay if (ret) 2194d671b71SMatt Ranostay goto error_unreg_buffer; 2204d671b71SMatt Ranostay 2214d671b71SMatt Ranostay return 0; 2224d671b71SMatt Ranostay 2234d671b71SMatt Ranostay error_unreg_buffer: 2244d671b71SMatt Ranostay iio_triggered_buffer_cleanup(indio_dev); 2254d671b71SMatt Ranostay 22692f0afb5SMatt Ranostay error_regulator_disable: 22792f0afb5SMatt Ranostay regulator_disable(data->ref); 22892f0afb5SMatt Ranostay 2294d671b71SMatt Ranostay return ret; 2304d671b71SMatt Ranostay } 2314d671b71SMatt Ranostay 2324d671b71SMatt Ranostay static int ti_adc_remove(struct spi_device *spi) 2334d671b71SMatt Ranostay { 2344d671b71SMatt Ranostay struct iio_dev *indio_dev = spi_get_drvdata(spi); 23592f0afb5SMatt Ranostay struct ti_adc_data *data = iio_priv(indio_dev); 2364d671b71SMatt Ranostay 2374d671b71SMatt Ranostay iio_device_unregister(indio_dev); 2384d671b71SMatt Ranostay iio_triggered_buffer_cleanup(indio_dev); 23992f0afb5SMatt Ranostay regulator_disable(data->ref); 2404d671b71SMatt Ranostay 2414d671b71SMatt Ranostay return 0; 2424d671b71SMatt Ranostay } 2434d671b71SMatt Ranostay 2444d671b71SMatt Ranostay static const struct of_device_id ti_adc_dt_ids[] = { 2454d671b71SMatt Ranostay { .compatible = "ti,adc141s626", }, 2464d671b71SMatt Ranostay { .compatible = "ti,adc161s626", }, 2474d671b71SMatt Ranostay {} 2484d671b71SMatt Ranostay }; 2494d671b71SMatt Ranostay MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); 2504d671b71SMatt Ranostay 2514d671b71SMatt Ranostay static const struct spi_device_id ti_adc_id[] = { 2524d671b71SMatt Ranostay {"adc141s626", TI_ADC141S626}, 2534d671b71SMatt Ranostay {"adc161s626", TI_ADC161S626}, 2544d671b71SMatt Ranostay {}, 2554d671b71SMatt Ranostay }; 2564d671b71SMatt Ranostay MODULE_DEVICE_TABLE(spi, ti_adc_id); 2574d671b71SMatt Ranostay 2584d671b71SMatt Ranostay static struct spi_driver ti_adc_driver = { 2594d671b71SMatt Ranostay .driver = { 2604d671b71SMatt Ranostay .name = TI_ADC_DRV_NAME, 261*a6b40706SJonathan Cameron .of_match_table = ti_adc_dt_ids, 2624d671b71SMatt Ranostay }, 2634d671b71SMatt Ranostay .probe = ti_adc_probe, 2644d671b71SMatt Ranostay .remove = ti_adc_remove, 2654d671b71SMatt Ranostay .id_table = ti_adc_id, 2664d671b71SMatt Ranostay }; 2674d671b71SMatt Ranostay module_spi_driver(ti_adc_driver); 2684d671b71SMatt Ranostay 269d6ad8058SMatt Ranostay MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>"); 2704d671b71SMatt Ranostay MODULE_DESCRIPTION("Texas Instruments ADC1x1S 1-channel differential ADC"); 2714d671b71SMatt Ranostay MODULE_LICENSE("GPL"); 272