18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ad2s1200.c simple support for the ADI Resolver to Digital Converters: 48c2ecf20Sopenharmony_ci * AD2S1200/1205 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2018-2018 David Veenstra <davidjulianveenstra@gmail.com> 78c2ecf20Sopenharmony_ci * Copyright (c) 2010-2010 Analog Devices Inc. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/bitops.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 168c2ecf20Sopenharmony_ci#include <linux/mutex.h> 178c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 188c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 198c2ecf20Sopenharmony_ci#include <linux/types.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 228c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define DRV_NAME "ad2s1200" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* input clock on serial interface */ 278c2ecf20Sopenharmony_ci#define AD2S1200_HZ 8192000 288c2ecf20Sopenharmony_ci/* clock period in nano second */ 298c2ecf20Sopenharmony_ci#define AD2S1200_TSCLK (1000000000 / AD2S1200_HZ) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/** 328c2ecf20Sopenharmony_ci * struct ad2s1200_state - driver instance specific data. 338c2ecf20Sopenharmony_ci * @lock: protects both the GPIO pins and the rx buffer. 348c2ecf20Sopenharmony_ci * @sdev: spi device. 358c2ecf20Sopenharmony_ci * @sample: GPIO pin SAMPLE. 368c2ecf20Sopenharmony_ci * @rdvel: GPIO pin RDVEL. 378c2ecf20Sopenharmony_ci * @rx: buffer for spi transfers. 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistruct ad2s1200_state { 408c2ecf20Sopenharmony_ci struct mutex lock; 418c2ecf20Sopenharmony_ci struct spi_device *sdev; 428c2ecf20Sopenharmony_ci struct gpio_desc *sample; 438c2ecf20Sopenharmony_ci struct gpio_desc *rdvel; 448c2ecf20Sopenharmony_ci __be16 rx ____cacheline_aligned; 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int ad2s1200_read_raw(struct iio_dev *indio_dev, 488c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 498c2ecf20Sopenharmony_ci int *val, 508c2ecf20Sopenharmony_ci int *val2, 518c2ecf20Sopenharmony_ci long m) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct ad2s1200_state *st = iio_priv(indio_dev); 548c2ecf20Sopenharmony_ci int ret; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci switch (m) { 578c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 588c2ecf20Sopenharmony_ci switch (chan->type) { 598c2ecf20Sopenharmony_ci case IIO_ANGL: 608c2ecf20Sopenharmony_ci /* 2 * Pi / (2^12 - 1) ~= 0.001534355 */ 618c2ecf20Sopenharmony_ci *val = 0; 628c2ecf20Sopenharmony_ci *val2 = 1534355; 638c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_NANO; 648c2ecf20Sopenharmony_ci case IIO_ANGL_VEL: 658c2ecf20Sopenharmony_ci /* 2 * Pi ~= 6.283185 */ 668c2ecf20Sopenharmony_ci *val = 6; 678c2ecf20Sopenharmony_ci *val2 = 283185; 688c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 698c2ecf20Sopenharmony_ci default: 708c2ecf20Sopenharmony_ci return -EINVAL; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci break; 738c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 748c2ecf20Sopenharmony_ci mutex_lock(&st->lock); 758c2ecf20Sopenharmony_ci gpiod_set_value(st->sample, 0); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* delay (6 * AD2S1200_TSCLK + 20) nano seconds */ 788c2ecf20Sopenharmony_ci udelay(1); 798c2ecf20Sopenharmony_ci gpiod_set_value(st->sample, 1); 808c2ecf20Sopenharmony_ci gpiod_set_value(st->rdvel, !!(chan->type == IIO_ANGL)); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci ret = spi_read(st->sdev, &st->rx, 2); 838c2ecf20Sopenharmony_ci if (ret < 0) { 848c2ecf20Sopenharmony_ci mutex_unlock(&st->lock); 858c2ecf20Sopenharmony_ci return ret; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci switch (chan->type) { 898c2ecf20Sopenharmony_ci case IIO_ANGL: 908c2ecf20Sopenharmony_ci *val = be16_to_cpup(&st->rx) >> 4; 918c2ecf20Sopenharmony_ci break; 928c2ecf20Sopenharmony_ci case IIO_ANGL_VEL: 938c2ecf20Sopenharmony_ci *val = sign_extend32(be16_to_cpup(&st->rx) >> 4, 11); 948c2ecf20Sopenharmony_ci break; 958c2ecf20Sopenharmony_ci default: 968c2ecf20Sopenharmony_ci mutex_unlock(&st->lock); 978c2ecf20Sopenharmony_ci return -EINVAL; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* delay (2 * AD2S1200_TSCLK + 20) ns for sample pulse */ 1018c2ecf20Sopenharmony_ci udelay(1); 1028c2ecf20Sopenharmony_ci mutex_unlock(&st->lock); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return IIO_VAL_INT; 1058c2ecf20Sopenharmony_ci default: 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return -EINVAL; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic const struct iio_chan_spec ad2s1200_channels[] = { 1138c2ecf20Sopenharmony_ci { 1148c2ecf20Sopenharmony_ci .type = IIO_ANGL, 1158c2ecf20Sopenharmony_ci .indexed = 1, 1168c2ecf20Sopenharmony_ci .channel = 0, 1178c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 1188c2ecf20Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), 1198c2ecf20Sopenharmony_ci }, { 1208c2ecf20Sopenharmony_ci .type = IIO_ANGL_VEL, 1218c2ecf20Sopenharmony_ci .indexed = 1, 1228c2ecf20Sopenharmony_ci .channel = 0, 1238c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 1248c2ecf20Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic const struct iio_info ad2s1200_info = { 1298c2ecf20Sopenharmony_ci .read_raw = ad2s1200_read_raw, 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int ad2s1200_probe(struct spi_device *spi) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct ad2s1200_state *st; 1358c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 1368c2ecf20Sopenharmony_ci int ret; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); 1398c2ecf20Sopenharmony_ci if (!indio_dev) 1408c2ecf20Sopenharmony_ci return -ENOMEM; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci spi_set_drvdata(spi, indio_dev); 1438c2ecf20Sopenharmony_ci st = iio_priv(indio_dev); 1448c2ecf20Sopenharmony_ci mutex_init(&st->lock); 1458c2ecf20Sopenharmony_ci st->sdev = spi; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci st->sample = devm_gpiod_get(&spi->dev, "adi,sample", GPIOD_OUT_LOW); 1488c2ecf20Sopenharmony_ci if (IS_ERR(st->sample)) { 1498c2ecf20Sopenharmony_ci dev_err(&spi->dev, "Failed to claim SAMPLE gpio: err=%ld\n", 1508c2ecf20Sopenharmony_ci PTR_ERR(st->sample)); 1518c2ecf20Sopenharmony_ci return PTR_ERR(st->sample); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci st->rdvel = devm_gpiod_get(&spi->dev, "adi,rdvel", GPIOD_OUT_LOW); 1558c2ecf20Sopenharmony_ci if (IS_ERR(st->rdvel)) { 1568c2ecf20Sopenharmony_ci dev_err(&spi->dev, "Failed to claim RDVEL gpio: err=%ld\n", 1578c2ecf20Sopenharmony_ci PTR_ERR(st->rdvel)); 1588c2ecf20Sopenharmony_ci return PTR_ERR(st->rdvel); 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci indio_dev->info = &ad2s1200_info; 1628c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 1638c2ecf20Sopenharmony_ci indio_dev->channels = ad2s1200_channels; 1648c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(ad2s1200_channels); 1658c2ecf20Sopenharmony_ci indio_dev->name = spi_get_device_id(spi)->name; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci spi->max_speed_hz = AD2S1200_HZ; 1688c2ecf20Sopenharmony_ci spi->mode = SPI_MODE_3; 1698c2ecf20Sopenharmony_ci ret = spi_setup(spi); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (ret < 0) { 1728c2ecf20Sopenharmony_ci dev_err(&spi->dev, "spi_setup failed!\n"); 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return devm_iio_device_register(&spi->dev, indio_dev); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic const struct of_device_id ad2s1200_of_match[] = { 1808c2ecf20Sopenharmony_ci { .compatible = "adi,ad2s1200", }, 1818c2ecf20Sopenharmony_ci { .compatible = "adi,ad2s1205", }, 1828c2ecf20Sopenharmony_ci { } 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ad2s1200_of_match); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic const struct spi_device_id ad2s1200_id[] = { 1878c2ecf20Sopenharmony_ci { "ad2s1200" }, 1888c2ecf20Sopenharmony_ci { "ad2s1205" }, 1898c2ecf20Sopenharmony_ci {} 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, ad2s1200_id); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic struct spi_driver ad2s1200_driver = { 1948c2ecf20Sopenharmony_ci .driver = { 1958c2ecf20Sopenharmony_ci .name = DRV_NAME, 1968c2ecf20Sopenharmony_ci .of_match_table = ad2s1200_of_match, 1978c2ecf20Sopenharmony_ci }, 1988c2ecf20Sopenharmony_ci .probe = ad2s1200_probe, 1998c2ecf20Sopenharmony_ci .id_table = ad2s1200_id, 2008c2ecf20Sopenharmony_ci}; 2018c2ecf20Sopenharmony_cimodule_spi_driver(ad2s1200_driver); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Veenstra <davidjulianveenstra@gmail.com>"); 2048c2ecf20Sopenharmony_ciMODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>"); 2058c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Analog Devices AD2S1200/1205 Resolver to Digital SPI driver"); 2068c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 207