18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * adcxx.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * The adcxx4s is an AD converter family from National Semiconductor (NS). 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2008 Marc Pignat <marc.pignat@hevs.ch> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * The adcxx4s communicates with a host processor via an SPI/Microwire Bus 108c2ecf20Sopenharmony_ci * interface. This driver supports the whole family of devices with name 118c2ecf20Sopenharmony_ci * ADC<bb><c>S<sss>, where 128c2ecf20Sopenharmony_ci * * bb is the resolution in number of bits (8, 10, 12) 138c2ecf20Sopenharmony_ci * * c is the number of channels (1, 2, 4, 8) 148c2ecf20Sopenharmony_ci * * sss is the maximum conversion speed (021 for 200 kSPS, 051 for 500 kSPS 158c2ecf20Sopenharmony_ci * and 101 for 1 MSPS) 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Complete datasheets are available at National's website here: 188c2ecf20Sopenharmony_ci * http://www.national.com/ds/DC/ADC<bb><c>S<sss>.pdf 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Handling of 8, 10 and 12 bits converters are the same, the 218c2ecf20Sopenharmony_ci * unavailable bits are 0 :) 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/init.h> 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/kernel.h> 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <linux/device.h> 298c2ecf20Sopenharmony_ci#include <linux/err.h> 308c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 318c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 328c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 338c2ecf20Sopenharmony_ci#include <linux/mutex.h> 348c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 358c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define DRVNAME "adcxx" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct adcxx { 408c2ecf20Sopenharmony_ci struct device *hwmon_dev; 418c2ecf20Sopenharmony_ci struct mutex lock; 428c2ecf20Sopenharmony_ci u32 channels; 438c2ecf20Sopenharmony_ci u32 reference; /* in millivolts */ 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* sysfs hook function */ 478c2ecf20Sopenharmony_cistatic ssize_t adcxx_show(struct device *dev, 488c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct spi_device *spi = to_spi_device(dev); 518c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 528c2ecf20Sopenharmony_ci struct adcxx *adc = spi_get_drvdata(spi); 538c2ecf20Sopenharmony_ci u8 tx_buf[2]; 548c2ecf20Sopenharmony_ci u8 rx_buf[2]; 558c2ecf20Sopenharmony_ci int status; 568c2ecf20Sopenharmony_ci u32 value; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&adc->lock)) 598c2ecf20Sopenharmony_ci return -ERESTARTSYS; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (adc->channels == 1) { 628c2ecf20Sopenharmony_ci status = spi_read(spi, rx_buf, sizeof(rx_buf)); 638c2ecf20Sopenharmony_ci } else { 648c2ecf20Sopenharmony_ci tx_buf[0] = attr->index << 3; /* other bits are don't care */ 658c2ecf20Sopenharmony_ci status = spi_write_then_read(spi, tx_buf, sizeof(tx_buf), 668c2ecf20Sopenharmony_ci rx_buf, sizeof(rx_buf)); 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci if (status < 0) { 698c2ecf20Sopenharmony_ci dev_warn(dev, "SPI synch. transfer failed with status %d\n", 708c2ecf20Sopenharmony_ci status); 718c2ecf20Sopenharmony_ci goto out; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci value = (rx_buf[0] << 8) + rx_buf[1]; 758c2ecf20Sopenharmony_ci dev_dbg(dev, "raw value = 0x%x\n", value); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci value = value * adc->reference >> 12; 788c2ecf20Sopenharmony_ci status = sprintf(buf, "%d\n", value); 798c2ecf20Sopenharmony_ciout: 808c2ecf20Sopenharmony_ci mutex_unlock(&adc->lock); 818c2ecf20Sopenharmony_ci return status; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic ssize_t adcxx_min_show(struct device *dev, 858c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci /* The minimum reference is 0 for this chip family */ 888c2ecf20Sopenharmony_ci return sprintf(buf, "0\n"); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic ssize_t adcxx_max_show(struct device *dev, 928c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct spi_device *spi = to_spi_device(dev); 958c2ecf20Sopenharmony_ci struct adcxx *adc = spi_get_drvdata(spi); 968c2ecf20Sopenharmony_ci u32 reference; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&adc->lock)) 998c2ecf20Sopenharmony_ci return -ERESTARTSYS; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci reference = adc->reference; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci mutex_unlock(&adc->lock); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", reference); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic ssize_t adcxx_max_store(struct device *dev, 1098c2ecf20Sopenharmony_ci struct device_attribute *devattr, 1108c2ecf20Sopenharmony_ci const char *buf, size_t count) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct spi_device *spi = to_spi_device(dev); 1138c2ecf20Sopenharmony_ci struct adcxx *adc = spi_get_drvdata(spi); 1148c2ecf20Sopenharmony_ci unsigned long value; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (kstrtoul(buf, 10, &value)) 1178c2ecf20Sopenharmony_ci return -EINVAL; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&adc->lock)) 1208c2ecf20Sopenharmony_ci return -ERESTARTSYS; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci adc->reference = value; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci mutex_unlock(&adc->lock); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return count; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic ssize_t adcxx_name_show(struct device *dev, 1308c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", to_spi_device(dev)->modalias); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic struct sensor_device_attribute ad_input[] = { 1368c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(name, adcxx_name, 0), 1378c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(in_min, adcxx_min, 0), 1388c2ecf20Sopenharmony_ci SENSOR_ATTR_RW(in_max, adcxx_max, 0), 1398c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(in0_input, adcxx, 0), 1408c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(in1_input, adcxx, 1), 1418c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(in2_input, adcxx, 2), 1428c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(in3_input, adcxx, 3), 1438c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(in4_input, adcxx, 4), 1448c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(in5_input, adcxx, 5), 1458c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(in6_input, adcxx, 6), 1468c2ecf20Sopenharmony_ci SENSOR_ATTR_RO(in7_input, adcxx, 7), 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/*----------------------------------------------------------------------*/ 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int adcxx_probe(struct spi_device *spi) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci int channels = spi_get_device_id(spi)->driver_data; 1548c2ecf20Sopenharmony_ci struct adcxx *adc; 1558c2ecf20Sopenharmony_ci int status; 1568c2ecf20Sopenharmony_ci int i; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci adc = devm_kzalloc(&spi->dev, sizeof(*adc), GFP_KERNEL); 1598c2ecf20Sopenharmony_ci if (!adc) 1608c2ecf20Sopenharmony_ci return -ENOMEM; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* set a default value for the reference */ 1638c2ecf20Sopenharmony_ci adc->reference = 3300; 1648c2ecf20Sopenharmony_ci adc->channels = channels; 1658c2ecf20Sopenharmony_ci mutex_init(&adc->lock); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci mutex_lock(&adc->lock); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci spi_set_drvdata(spi, adc); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci for (i = 0; i < 3 + adc->channels; i++) { 1728c2ecf20Sopenharmony_ci status = device_create_file(&spi->dev, &ad_input[i].dev_attr); 1738c2ecf20Sopenharmony_ci if (status) { 1748c2ecf20Sopenharmony_ci dev_err(&spi->dev, "device_create_file failed.\n"); 1758c2ecf20Sopenharmony_ci goto out_err; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci adc->hwmon_dev = hwmon_device_register(&spi->dev); 1808c2ecf20Sopenharmony_ci if (IS_ERR(adc->hwmon_dev)) { 1818c2ecf20Sopenharmony_ci dev_err(&spi->dev, "hwmon_device_register failed.\n"); 1828c2ecf20Sopenharmony_ci status = PTR_ERR(adc->hwmon_dev); 1838c2ecf20Sopenharmony_ci goto out_err; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci mutex_unlock(&adc->lock); 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ciout_err: 1908c2ecf20Sopenharmony_ci for (i--; i >= 0; i--) 1918c2ecf20Sopenharmony_ci device_remove_file(&spi->dev, &ad_input[i].dev_attr); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci mutex_unlock(&adc->lock); 1948c2ecf20Sopenharmony_ci return status; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int adcxx_remove(struct spi_device *spi) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct adcxx *adc = spi_get_drvdata(spi); 2008c2ecf20Sopenharmony_ci int i; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci mutex_lock(&adc->lock); 2038c2ecf20Sopenharmony_ci hwmon_device_unregister(adc->hwmon_dev); 2048c2ecf20Sopenharmony_ci for (i = 0; i < 3 + adc->channels; i++) 2058c2ecf20Sopenharmony_ci device_remove_file(&spi->dev, &ad_input[i].dev_attr); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci mutex_unlock(&adc->lock); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic const struct spi_device_id adcxx_ids[] = { 2138c2ecf20Sopenharmony_ci { "adcxx1s", 1 }, 2148c2ecf20Sopenharmony_ci { "adcxx2s", 2 }, 2158c2ecf20Sopenharmony_ci { "adcxx4s", 4 }, 2168c2ecf20Sopenharmony_ci { "adcxx8s", 8 }, 2178c2ecf20Sopenharmony_ci { }, 2188c2ecf20Sopenharmony_ci}; 2198c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, adcxx_ids); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic struct spi_driver adcxx_driver = { 2228c2ecf20Sopenharmony_ci .driver = { 2238c2ecf20Sopenharmony_ci .name = "adcxx", 2248c2ecf20Sopenharmony_ci }, 2258c2ecf20Sopenharmony_ci .id_table = adcxx_ids, 2268c2ecf20Sopenharmony_ci .probe = adcxx_probe, 2278c2ecf20Sopenharmony_ci .remove = adcxx_remove, 2288c2ecf20Sopenharmony_ci}; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cimodule_spi_driver(adcxx_driver); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marc Pignat"); 2338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("National Semiconductor adcxx8sxxx Linux driver"); 2348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 235