adcxx.c (5738B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * adcxx.c 4 * 5 * The adcxx4s is an AD converter family from National Semiconductor (NS). 6 * 7 * Copyright (c) 2008 Marc Pignat <marc.pignat@hevs.ch> 8 * 9 * The adcxx4s communicates with a host processor via an SPI/Microwire Bus 10 * interface. This driver supports the whole family of devices with name 11 * ADC<bb><c>S<sss>, where 12 * * bb is the resolution in number of bits (8, 10, 12) 13 * * c is the number of channels (1, 2, 4, 8) 14 * * sss is the maximum conversion speed (021 for 200 kSPS, 051 for 500 kSPS 15 * and 101 for 1 MSPS) 16 * 17 * Complete datasheets are available at National's website here: 18 * http://www.national.com/ds/DC/ADC<bb><c>S<sss>.pdf 19 * 20 * Handling of 8, 10 and 12 bits converters are the same, the 21 * unavailable bits are 0 :) 22 */ 23 24#include <linux/init.h> 25#include <linux/module.h> 26#include <linux/kernel.h> 27#include <linux/slab.h> 28#include <linux/device.h> 29#include <linux/err.h> 30#include <linux/sysfs.h> 31#include <linux/hwmon.h> 32#include <linux/hwmon-sysfs.h> 33#include <linux/mutex.h> 34#include <linux/mod_devicetable.h> 35#include <linux/spi/spi.h> 36 37#define DRVNAME "adcxx" 38 39struct adcxx { 40 struct device *hwmon_dev; 41 struct mutex lock; 42 u32 channels; 43 u32 reference; /* in millivolts */ 44}; 45 46/* sysfs hook function */ 47static ssize_t adcxx_show(struct device *dev, 48 struct device_attribute *devattr, char *buf) 49{ 50 struct spi_device *spi = to_spi_device(dev); 51 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 52 struct adcxx *adc = spi_get_drvdata(spi); 53 u8 tx_buf[2]; 54 u8 rx_buf[2]; 55 int status; 56 u32 value; 57 58 if (mutex_lock_interruptible(&adc->lock)) 59 return -ERESTARTSYS; 60 61 if (adc->channels == 1) { 62 status = spi_read(spi, rx_buf, sizeof(rx_buf)); 63 } else { 64 tx_buf[0] = attr->index << 3; /* other bits are don't care */ 65 status = spi_write_then_read(spi, tx_buf, sizeof(tx_buf), 66 rx_buf, sizeof(rx_buf)); 67 } 68 if (status < 0) { 69 dev_warn(dev, "SPI synch. transfer failed with status %d\n", 70 status); 71 goto out; 72 } 73 74 value = (rx_buf[0] << 8) + rx_buf[1]; 75 dev_dbg(dev, "raw value = 0x%x\n", value); 76 77 value = value * adc->reference >> 12; 78 status = sprintf(buf, "%d\n", value); 79out: 80 mutex_unlock(&adc->lock); 81 return status; 82} 83 84static ssize_t adcxx_min_show(struct device *dev, 85 struct device_attribute *devattr, char *buf) 86{ 87 /* The minimum reference is 0 for this chip family */ 88 return sprintf(buf, "0\n"); 89} 90 91static ssize_t adcxx_max_show(struct device *dev, 92 struct device_attribute *devattr, char *buf) 93{ 94 struct spi_device *spi = to_spi_device(dev); 95 struct adcxx *adc = spi_get_drvdata(spi); 96 u32 reference; 97 98 if (mutex_lock_interruptible(&adc->lock)) 99 return -ERESTARTSYS; 100 101 reference = adc->reference; 102 103 mutex_unlock(&adc->lock); 104 105 return sprintf(buf, "%d\n", reference); 106} 107 108static ssize_t adcxx_max_store(struct device *dev, 109 struct device_attribute *devattr, 110 const char *buf, size_t count) 111{ 112 struct spi_device *spi = to_spi_device(dev); 113 struct adcxx *adc = spi_get_drvdata(spi); 114 unsigned long value; 115 116 if (kstrtoul(buf, 10, &value)) 117 return -EINVAL; 118 119 if (mutex_lock_interruptible(&adc->lock)) 120 return -ERESTARTSYS; 121 122 adc->reference = value; 123 124 mutex_unlock(&adc->lock); 125 126 return count; 127} 128 129static ssize_t adcxx_name_show(struct device *dev, 130 struct device_attribute *devattr, char *buf) 131{ 132 return sprintf(buf, "%s\n", to_spi_device(dev)->modalias); 133} 134 135static struct sensor_device_attribute ad_input[] = { 136 SENSOR_ATTR_RO(name, adcxx_name, 0), 137 SENSOR_ATTR_RO(in_min, adcxx_min, 0), 138 SENSOR_ATTR_RW(in_max, adcxx_max, 0), 139 SENSOR_ATTR_RO(in0_input, adcxx, 0), 140 SENSOR_ATTR_RO(in1_input, adcxx, 1), 141 SENSOR_ATTR_RO(in2_input, adcxx, 2), 142 SENSOR_ATTR_RO(in3_input, adcxx, 3), 143 SENSOR_ATTR_RO(in4_input, adcxx, 4), 144 SENSOR_ATTR_RO(in5_input, adcxx, 5), 145 SENSOR_ATTR_RO(in6_input, adcxx, 6), 146 SENSOR_ATTR_RO(in7_input, adcxx, 7), 147}; 148 149/*----------------------------------------------------------------------*/ 150 151static int adcxx_probe(struct spi_device *spi) 152{ 153 int channels = spi_get_device_id(spi)->driver_data; 154 struct adcxx *adc; 155 int status; 156 int i; 157 158 adc = devm_kzalloc(&spi->dev, sizeof(*adc), GFP_KERNEL); 159 if (!adc) 160 return -ENOMEM; 161 162 /* set a default value for the reference */ 163 adc->reference = 3300; 164 adc->channels = channels; 165 mutex_init(&adc->lock); 166 167 mutex_lock(&adc->lock); 168 169 spi_set_drvdata(spi, adc); 170 171 for (i = 0; i < 3 + adc->channels; i++) { 172 status = device_create_file(&spi->dev, &ad_input[i].dev_attr); 173 if (status) { 174 dev_err(&spi->dev, "device_create_file failed.\n"); 175 goto out_err; 176 } 177 } 178 179 adc->hwmon_dev = hwmon_device_register(&spi->dev); 180 if (IS_ERR(adc->hwmon_dev)) { 181 dev_err(&spi->dev, "hwmon_device_register failed.\n"); 182 status = PTR_ERR(adc->hwmon_dev); 183 goto out_err; 184 } 185 186 mutex_unlock(&adc->lock); 187 return 0; 188 189out_err: 190 for (i--; i >= 0; i--) 191 device_remove_file(&spi->dev, &ad_input[i].dev_attr); 192 193 mutex_unlock(&adc->lock); 194 return status; 195} 196 197static void adcxx_remove(struct spi_device *spi) 198{ 199 struct adcxx *adc = spi_get_drvdata(spi); 200 int i; 201 202 mutex_lock(&adc->lock); 203 hwmon_device_unregister(adc->hwmon_dev); 204 for (i = 0; i < 3 + adc->channels; i++) 205 device_remove_file(&spi->dev, &ad_input[i].dev_attr); 206 207 mutex_unlock(&adc->lock); 208} 209 210static const struct spi_device_id adcxx_ids[] = { 211 { "adcxx1s", 1 }, 212 { "adcxx2s", 2 }, 213 { "adcxx4s", 4 }, 214 { "adcxx8s", 8 }, 215 { }, 216}; 217MODULE_DEVICE_TABLE(spi, adcxx_ids); 218 219static struct spi_driver adcxx_driver = { 220 .driver = { 221 .name = "adcxx", 222 }, 223 .id_table = adcxx_ids, 224 .probe = adcxx_probe, 225 .remove = adcxx_remove, 226}; 227 228module_spi_driver(adcxx_driver); 229 230MODULE_AUTHOR("Marc Pignat"); 231MODULE_DESCRIPTION("National Semiconductor adcxx8sxxx Linux driver"); 232MODULE_LICENSE("GPL");