Blame view

drivers/hwmon/adcxx.c 6.42 KB
d42139a3f   Marc Pignat   hwmon: ADC124S501...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  /*
   * adcxx.c
   *
   * The adcxx4s is an AD converter family from National Semiconductor (NS).
   *
   * Copyright (c) 2008 Marc Pignat <marc.pignat@hevs.ch>
   *
   * The adcxx4s communicates with a host processor via an SPI/Microwire Bus
   * interface. This driver supports the whole family of devices with name
   * ADC<bb><c>S<sss>, where
   * * bb is the resolution in number of bits (8, 10, 12)
   * * c is the number of channels (1, 2, 4, 8)
   * * sss is the maximum conversion speed (021 for 200 kSPS, 051 for 500 kSPS
   *   and 101 for 1 MSPS)
   *
   * Complete datasheets are available at National's website here:
   * http://www.national.com/ds/DC/ADC<bb><c>S<sss>.pdf
   *
   * Handling of 8, 10 and 12 bits converters are the same, the
   * unavailable bits are 0 :)
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/kernel.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
40
  #include <linux/slab.h>
d42139a3f   Marc Pignat   hwmon: ADC124S501...
41
42
43
44
45
46
  #include <linux/device.h>
  #include <linux/err.h>
  #include <linux/sysfs.h>
  #include <linux/hwmon.h>
  #include <linux/hwmon-sysfs.h>
  #include <linux/mutex.h>
d2a5c10f8   Anton Vorontsov   hwmon: adxx: conv...
47
  #include <linux/mod_devicetable.h>
d42139a3f   Marc Pignat   hwmon: ADC124S501...
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  #include <linux/spi/spi.h>
  
  #define DRVNAME		"adcxx"
  
  struct adcxx {
  	struct device *hwmon_dev;
  	struct mutex lock;
  	u32 channels;
  	u32 reference; /* in millivolts */
  };
  
  /* sysfs hook function */
  static ssize_t adcxx_read(struct device *dev,
  		struct device_attribute *devattr, char *buf)
  {
  	struct spi_device *spi = to_spi_device(dev);
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
95de3b257   Jean Delvare   hwmon: Use helper...
65
  	struct adcxx *adc = spi_get_drvdata(spi);
5748150ea   José Miguel Gonçalves   drivers/hwmon/adc...
66
  	u8 tx_buf[2];
d42139a3f   Marc Pignat   hwmon: ADC124S501...
67
68
  	u8 rx_buf[2];
  	int status;
5748150ea   José Miguel Gonçalves   drivers/hwmon/adc...
69
  	u32 value;
d42139a3f   Marc Pignat   hwmon: ADC124S501...
70
71
72
  
  	if (mutex_lock_interruptible(&adc->lock))
  		return -ERESTARTSYS;
5748150ea   José Miguel Gonçalves   drivers/hwmon/adc...
73
74
75
76
77
78
79
  	if (adc->channels == 1) {
  		status = spi_read(spi, rx_buf, sizeof(rx_buf));
  	} else {
  		tx_buf[0] = attr->index << 3; /* other bits are don't care */
  		status = spi_write_then_read(spi, tx_buf, sizeof(tx_buf),
  						rx_buf, sizeof(rx_buf));
  	}
d42139a3f   Marc Pignat   hwmon: ADC124S501...
80
  	if (status < 0) {
5748150ea   José Miguel Gonçalves   drivers/hwmon/adc...
81
82
  		dev_warn(dev, "SPI synch. transfer failed with status %d
  ",
d42139a3f   Marc Pignat   hwmon: ADC124S501...
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  				status);
  		goto out;
  	}
  
  	value = (rx_buf[0] << 8) + rx_buf[1];
  	dev_dbg(dev, "raw value = 0x%x
  ", value);
  
  	value = value * adc->reference >> 12;
  	status = sprintf(buf, "%d
  ", value);
  out:
  	mutex_unlock(&adc->lock);
  	return status;
  }
  
  static ssize_t adcxx_show_min(struct device *dev,
  		struct device_attribute *devattr, char *buf)
  {
  	/* The minimum reference is 0 for this chip family */
  	return sprintf(buf, "0
  ");
  }
  
  static ssize_t adcxx_show_max(struct device *dev,
  		struct device_attribute *devattr, char *buf)
  {
  	struct spi_device *spi = to_spi_device(dev);
95de3b257   Jean Delvare   hwmon: Use helper...
111
  	struct adcxx *adc = spi_get_drvdata(spi);
d42139a3f   Marc Pignat   hwmon: ADC124S501...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  	u32 reference;
  
  	if (mutex_lock_interruptible(&adc->lock))
  		return -ERESTARTSYS;
  
  	reference = adc->reference;
  
  	mutex_unlock(&adc->lock);
  
  	return sprintf(buf, "%d
  ", reference);
  }
  
  static ssize_t adcxx_set_max(struct device *dev,
  	struct device_attribute *devattr, const char *buf, size_t count)
  {
  	struct spi_device *spi = to_spi_device(dev);
95de3b257   Jean Delvare   hwmon: Use helper...
129
  	struct adcxx *adc = spi_get_drvdata(spi);
d42139a3f   Marc Pignat   hwmon: ADC124S501...
130
  	unsigned long value;
179c4fdb5   Frans Meulenbroeks   hwmon: replaced s...
131
  	if (kstrtoul(buf, 10, &value))
d42139a3f   Marc Pignat   hwmon: ADC124S501...
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
  		return -EINVAL;
  
  	if (mutex_lock_interruptible(&adc->lock))
  		return -ERESTARTSYS;
  
  	adc->reference = value;
  
  	mutex_unlock(&adc->lock);
  
  	return count;
  }
  
  static ssize_t adcxx_show_name(struct device *dev, struct device_attribute
  			      *devattr, char *buf)
  {
cc00e4ddb   Guenter Roeck   hwmon: (adcxx) Si...
147
148
  	return sprintf(buf, "%s
  ", to_spi_device(dev)->modalias);
d42139a3f   Marc Pignat   hwmon: ADC124S501...
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  }
  
  static struct sensor_device_attribute ad_input[] = {
  	SENSOR_ATTR(name, S_IRUGO, adcxx_show_name, NULL, 0),
  	SENSOR_ATTR(in_min, S_IRUGO, adcxx_show_min, NULL, 0),
  	SENSOR_ATTR(in_max, S_IWUSR | S_IRUGO, adcxx_show_max,
  					adcxx_set_max, 0),
  	SENSOR_ATTR(in0_input, S_IRUGO, adcxx_read, NULL, 0),
  	SENSOR_ATTR(in1_input, S_IRUGO, adcxx_read, NULL, 1),
  	SENSOR_ATTR(in2_input, S_IRUGO, adcxx_read, NULL, 2),
  	SENSOR_ATTR(in3_input, S_IRUGO, adcxx_read, NULL, 3),
  	SENSOR_ATTR(in4_input, S_IRUGO, adcxx_read, NULL, 4),
  	SENSOR_ATTR(in5_input, S_IRUGO, adcxx_read, NULL, 5),
  	SENSOR_ATTR(in6_input, S_IRUGO, adcxx_read, NULL, 6),
  	SENSOR_ATTR(in7_input, S_IRUGO, adcxx_read, NULL, 7),
  };
  
  /*----------------------------------------------------------------------*/
6c931ae1c   Bill Pemberton   hwmon: remove use...
167
  static int adcxx_probe(struct spi_device *spi)
d42139a3f   Marc Pignat   hwmon: ADC124S501...
168
  {
d2a5c10f8   Anton Vorontsov   hwmon: adxx: conv...
169
  	int channels = spi_get_device_id(spi)->driver_data;
d42139a3f   Marc Pignat   hwmon: ADC124S501...
170
171
172
  	struct adcxx *adc;
  	int status;
  	int i;
c60da8259   Guenter Roeck   hwmon: (adcxx) Co...
173
  	adc = devm_kzalloc(&spi->dev, sizeof(*adc), GFP_KERNEL);
d42139a3f   Marc Pignat   hwmon: ADC124S501...
174
175
176
177
178
179
180
181
182
  	if (!adc)
  		return -ENOMEM;
  
  	/* set a default value for the reference */
  	adc->reference = 3300;
  	adc->channels = channels;
  	mutex_init(&adc->lock);
  
  	mutex_lock(&adc->lock);
95de3b257   Jean Delvare   hwmon: Use helper...
183
  	spi_set_drvdata(spi, adc);
d42139a3f   Marc Pignat   hwmon: ADC124S501...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  
  	for (i = 0; i < 3 + adc->channels; i++) {
  		status = device_create_file(&spi->dev, &ad_input[i].dev_attr);
  		if (status) {
  			dev_err(&spi->dev, "device_create_file failed.
  ");
  			goto out_err;
  		}
  	}
  
  	adc->hwmon_dev = hwmon_device_register(&spi->dev);
  	if (IS_ERR(adc->hwmon_dev)) {
  		dev_err(&spi->dev, "hwmon_device_register failed.
  ");
  		status = PTR_ERR(adc->hwmon_dev);
  		goto out_err;
  	}
  
  	mutex_unlock(&adc->lock);
  	return 0;
  
  out_err:
  	for (i--; i >= 0; i--)
  		device_remove_file(&spi->dev, &ad_input[i].dev_attr);
d42139a3f   Marc Pignat   hwmon: ADC124S501...
208
  	mutex_unlock(&adc->lock);
d42139a3f   Marc Pignat   hwmon: ADC124S501...
209
210
  	return status;
  }
281dfd0b6   Bill Pemberton   hwmon: remove use...
211
  static int adcxx_remove(struct spi_device *spi)
d42139a3f   Marc Pignat   hwmon: ADC124S501...
212
  {
95de3b257   Jean Delvare   hwmon: Use helper...
213
  	struct adcxx *adc = spi_get_drvdata(spi);
d42139a3f   Marc Pignat   hwmon: ADC124S501...
214
215
216
217
218
219
  	int i;
  
  	mutex_lock(&adc->lock);
  	hwmon_device_unregister(adc->hwmon_dev);
  	for (i = 0; i < 3 + adc->channels; i++)
  		device_remove_file(&spi->dev, &ad_input[i].dev_attr);
d42139a3f   Marc Pignat   hwmon: ADC124S501...
220
  	mutex_unlock(&adc->lock);
d42139a3f   Marc Pignat   hwmon: ADC124S501...
221
222
223
  
  	return 0;
  }
d2a5c10f8   Anton Vorontsov   hwmon: adxx: conv...
224
225
226
227
228
229
  static const struct spi_device_id adcxx_ids[] = {
  	{ "adcxx1s", 1 },
  	{ "adcxx2s", 2 },
  	{ "adcxx4s", 4 },
  	{ "adcxx8s", 8 },
  	{ },
d42139a3f   Marc Pignat   hwmon: ADC124S501...
230
  };
d2a5c10f8   Anton Vorontsov   hwmon: adxx: conv...
231
  MODULE_DEVICE_TABLE(spi, adcxx_ids);
d42139a3f   Marc Pignat   hwmon: ADC124S501...
232

d2a5c10f8   Anton Vorontsov   hwmon: adxx: conv...
233
  static struct spi_driver adcxx_driver = {
d42139a3f   Marc Pignat   hwmon: ADC124S501...
234
  	.driver = {
d2a5c10f8   Anton Vorontsov   hwmon: adxx: conv...
235
  		.name	= "adcxx",
d42139a3f   Marc Pignat   hwmon: ADC124S501...
236
  	},
d2a5c10f8   Anton Vorontsov   hwmon: adxx: conv...
237
238
  	.id_table = adcxx_ids,
  	.probe	= adcxx_probe,
9e5e9b7a9   Bill Pemberton   hwmon: remove use...
239
  	.remove	= adcxx_remove,
d42139a3f   Marc Pignat   hwmon: ADC124S501...
240
  };
91efffe26   Axel Lin   hwmon: convert dr...
241
  module_spi_driver(adcxx_driver);
d42139a3f   Marc Pignat   hwmon: ADC124S501...
242
243
244
245
  
  MODULE_AUTHOR("Marc Pignat");
  MODULE_DESCRIPTION("National Semiconductor adcxx8sxxx Linux driver");
  MODULE_LICENSE("GPL");