]>
Commit | Line | Data |
---|---|---|
74ba9207 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
d42139a3 MP |
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 :) | |
d42139a3 MP |
22 | */ |
23 | ||
24 | #include <linux/init.h> | |
25 | #include <linux/module.h> | |
26 | #include <linux/kernel.h> | |
5a0e3ad6 | 27 | #include <linux/slab.h> |
d42139a3 MP |
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> | |
d2a5c10f | 34 | #include <linux/mod_devicetable.h> |
d42139a3 MP |
35 | #include <linux/spi/spi.h> |
36 | ||
37 | #define DRVNAME "adcxx" | |
38 | ||
39 | struct adcxx { | |
40 | struct device *hwmon_dev; | |
41 | struct mutex lock; | |
42 | u32 channels; | |
43 | u32 reference; /* in millivolts */ | |
44 | }; | |
45 | ||
46 | /* sysfs hook function */ | |
b0c130fd GR |
47 | static ssize_t adcxx_show(struct device *dev, |
48 | struct device_attribute *devattr, char *buf) | |
d42139a3 MP |
49 | { |
50 | struct spi_device *spi = to_spi_device(dev); | |
51 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | |
95de3b25 | 52 | struct adcxx *adc = spi_get_drvdata(spi); |
5748150e | 53 | u8 tx_buf[2]; |
d42139a3 MP |
54 | u8 rx_buf[2]; |
55 | int status; | |
5748150e | 56 | u32 value; |
d42139a3 MP |
57 | |
58 | if (mutex_lock_interruptible(&adc->lock)) | |
59 | return -ERESTARTSYS; | |
60 | ||
5748150e JMG |
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 | } | |
d42139a3 | 68 | if (status < 0) { |
5748150e | 69 | dev_warn(dev, "SPI synch. transfer failed with status %d\n", |
d42139a3 MP |
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); | |
79 | out: | |
80 | mutex_unlock(&adc->lock); | |
81 | return status; | |
82 | } | |
83 | ||
b0c130fd GR |
84 | static ssize_t adcxx_min_show(struct device *dev, |
85 | struct device_attribute *devattr, char *buf) | |
d42139a3 MP |
86 | { |
87 | /* The minimum reference is 0 for this chip family */ | |
88 | return sprintf(buf, "0\n"); | |
89 | } | |
90 | ||
b0c130fd GR |
91 | static ssize_t adcxx_max_show(struct device *dev, |
92 | struct device_attribute *devattr, char *buf) | |
d42139a3 MP |
93 | { |
94 | struct spi_device *spi = to_spi_device(dev); | |
95de3b25 | 95 | struct adcxx *adc = spi_get_drvdata(spi); |
d42139a3 MP |
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 | ||
b0c130fd GR |
108 | static ssize_t adcxx_max_store(struct device *dev, |
109 | struct device_attribute *devattr, | |
110 | const char *buf, size_t count) | |
d42139a3 MP |
111 | { |
112 | struct spi_device *spi = to_spi_device(dev); | |
95de3b25 | 113 | struct adcxx *adc = spi_get_drvdata(spi); |
d42139a3 MP |
114 | unsigned long value; |
115 | ||
179c4fdb | 116 | if (kstrtoul(buf, 10, &value)) |
d42139a3 MP |
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 | ||
b0c130fd GR |
129 | static ssize_t adcxx_name_show(struct device *dev, |
130 | struct device_attribute *devattr, char *buf) | |
d42139a3 | 131 | { |
cc00e4dd | 132 | return sprintf(buf, "%s\n", to_spi_device(dev)->modalias); |
d42139a3 MP |
133 | } |
134 | ||
135 | static struct sensor_device_attribute ad_input[] = { | |
b0c130fd GR |
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), | |
d42139a3 MP |
147 | }; |
148 | ||
149 | /*----------------------------------------------------------------------*/ | |
150 | ||
6c931ae1 | 151 | static int adcxx_probe(struct spi_device *spi) |
d42139a3 | 152 | { |
d2a5c10f | 153 | int channels = spi_get_device_id(spi)->driver_data; |
d42139a3 MP |
154 | struct adcxx *adc; |
155 | int status; | |
156 | int i; | |
157 | ||
c60da825 | 158 | adc = devm_kzalloc(&spi->dev, sizeof(*adc), GFP_KERNEL); |
d42139a3 MP |
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 | ||
95de3b25 | 169 | spi_set_drvdata(spi, adc); |
d42139a3 MP |
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 | ||
189 | out_err: | |
190 | for (i--; i >= 0; i--) | |
191 | device_remove_file(&spi->dev, &ad_input[i].dev_attr); | |
192 | ||
d42139a3 | 193 | mutex_unlock(&adc->lock); |
d42139a3 MP |
194 | return status; |
195 | } | |
196 | ||
281dfd0b | 197 | static int adcxx_remove(struct spi_device *spi) |
d42139a3 | 198 | { |
95de3b25 | 199 | struct adcxx *adc = spi_get_drvdata(spi); |
d42139a3 MP |
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 | ||
d42139a3 | 207 | mutex_unlock(&adc->lock); |
d42139a3 MP |
208 | |
209 | return 0; | |
210 | } | |
211 | ||
d2a5c10f AV |
212 | static const struct spi_device_id adcxx_ids[] = { |
213 | { "adcxx1s", 1 }, | |
214 | { "adcxx2s", 2 }, | |
215 | { "adcxx4s", 4 }, | |
216 | { "adcxx8s", 8 }, | |
217 | { }, | |
d42139a3 | 218 | }; |
d2a5c10f | 219 | MODULE_DEVICE_TABLE(spi, adcxx_ids); |
d42139a3 | 220 | |
d2a5c10f | 221 | static struct spi_driver adcxx_driver = { |
d42139a3 | 222 | .driver = { |
d2a5c10f | 223 | .name = "adcxx", |
d42139a3 | 224 | }, |
d2a5c10f AV |
225 | .id_table = adcxx_ids, |
226 | .probe = adcxx_probe, | |
9e5e9b7a | 227 | .remove = adcxx_remove, |
d42139a3 MP |
228 | }; |
229 | ||
91efffe2 | 230 | module_spi_driver(adcxx_driver); |
d42139a3 MP |
231 | |
232 | MODULE_AUTHOR("Marc Pignat"); | |
233 | MODULE_DESCRIPTION("National Semiconductor adcxx8sxxx Linux driver"); | |
234 | MODULE_LICENSE("GPL"); |