PageRenderTime 35ms CodeModel.GetById 14ms app.highlight 18ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/staging/comedi/drivers/mpc624.c

https://bitbucket.org/cyanogenmod/android_kernel_asus_tf300t
C | 424 lines | 246 code | 41 blank | 137 comment | 21 complexity | b9a625efe25a4ac5243dee0ec3e94d05 MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
  1/*
  2    comedi/drivers/mpc624.c
  3    Hardware driver for a Micro/sys inc. MPC-624 PC/104 board
  4
  5    COMEDI - Linux Control and Measurement Device Interface
  6    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
  7
  8    This program is free software; you can redistribute it and/or modify
  9    it under the terms of the GNU General Public License as published by
 10    the Free Software Foundation; either version 2 of the License, or
 11    (at your option) any later version.
 12
 13    This program is distributed in the hope that it will be useful,
 14    but WITHOUT ANY WARRANTY; without even the implied warranty of
 15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16    GNU General Public License for more details.
 17
 18    You should have received a copy of the GNU General Public License
 19    along with this program; if not, write to the Free Software
 20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 21
 22*/
 23/*
 24Driver: mpc624
 25Description: Micro/sys MPC-624 PC/104 board
 26Devices: [Micro/sys] MPC-624 (mpc624)
 27Author: Stanislaw Raczynski <sraczynski@op.pl>
 28Updated: Thu, 15 Sep 2005 12:01:18 +0200
 29Status: working
 30
 31    The Micro/sys MPC-624 board is based on the LTC2440 24-bit sigma-delta
 32    ADC chip.
 33
 34    Subdevices supported by the driver:
 35    - Analog In:   supported
 36    - Digital I/O: not supported
 37    - LEDs:        not supported
 38    - EEPROM:      not supported
 39
 40Configuration Options:
 41  [0] - I/O base address
 42  [1] - conversion rate
 43	Conversion rate  RMS noise  Effective Number Of Bits
 44	0      3.52kHz        23uV                17
 45	1      1.76kHz       3.5uV                20
 46	2       880Hz         2uV                21.3
 47	3       440Hz        1.4uV               21.8
 48	4       220Hz         1uV                22.4
 49	5       110Hz        750uV               22.9
 50	6       55Hz         510nV               23.4
 51	7      27.5Hz        375nV                24
 52	8      13.75Hz       250nV               24.4
 53	9      6.875Hz       200nV               24.6
 54  [2] - voltage range
 55	0      -1.01V .. +1.01V
 56	1      -10.1V .. +10.1V
 57*/
 58
 59#include "../comedidev.h"
 60
 61#include <linux/ioport.h>
 62#include <linux/delay.h>
 63
 64/* Consecutive I/O port addresses */
 65#define MPC624_SIZE             16
 66
 67/* Offsets of different ports */
 68#define MPC624_MASTER_CONTROL	0 /* not used */
 69#define MPC624_GNMUXCH          1 /* Gain, Mux, Channel of ADC */
 70#define MPC624_ADC              2 /* read/write to/from ADC */
 71#define MPC624_EE               3 /* read/write to/from serial EEPROM via I2C */
 72#define MPC624_LEDS             4 /* write to LEDs */
 73#define MPC624_DIO              5 /* read/write to/from digital I/O ports */
 74#define MPC624_IRQ_MASK         6 /* IRQ masking enable/disable */
 75
 76/* Register bits' names */
 77#define MPC624_ADBUSY           (1<<5)
 78#define MPC624_ADSDO            (1<<4)
 79#define MPC624_ADFO             (1<<3)
 80#define MPC624_ADCS             (1<<2)
 81#define MPC624_ADSCK            (1<<1)
 82#define MPC624_ADSDI            (1<<0)
 83
 84/* SDI Speed/Resolution Programming bits */
 85#define MPC624_OSR4             (1<<31)
 86#define MPC624_OSR3             (1<<30)
 87#define MPC624_OSR2             (1<<29)
 88#define MPC624_OSR1             (1<<28)
 89#define MPC624_OSR0             (1<<27)
 90
 91/* 32-bit output value bits' names */
 92#define MPC624_EOC_BIT          (1<<31)
 93#define MPC624_DMY_BIT          (1<<30)
 94#define MPC624_SGN_BIT          (1<<29)
 95
 96/* Conversion speeds */
 97/* OSR4 OSR3 OSR2 OSR1 OSR0  Conversion rate  RMS noise  ENOB^
 98 *  X    0    0    0    1        3.52kHz        23uV      17
 99 *  X    0    0    1    0        1.76kHz       3.5uV      20
100 *  X    0    0    1    1         880Hz         2uV      21.3
101 *  X    0    1    0    0         440Hz        1.4uV     21.8
102 *  X    0    1    0    1         220Hz         1uV      22.4
103 *  X    0    1    1    0         110Hz        750uV     22.9
104 *  X    0    1    1    1          55Hz        510nV     23.4
105 *  X    1    0    0    0         27.5Hz       375nV      24
106 *  X    1    0    0    1        13.75Hz       250nV     24.4
107 *  X    1    1    1    1        6.875Hz       200nV     24.6
108 *
109 * ^ - Effective Number Of Bits
110 */
111
112#define MPC624_SPEED_3_52_kHz (MPC624_OSR4 | MPC624_OSR0)
113#define MPC624_SPEED_1_76_kHz (MPC624_OSR4 | MPC624_OSR1)
114#define MPC624_SPEED_880_Hz   (MPC624_OSR4 | MPC624_OSR1 | MPC624_OSR0)
115#define MPC624_SPEED_440_Hz   (MPC624_OSR4 | MPC624_OSR2)
116#define MPC624_SPEED_220_Hz   (MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR0)
117#define MPC624_SPEED_110_Hz   (MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR1)
118#define MPC624_SPEED_55_Hz \
119	(MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR1 | MPC624_OSR0)
120#define MPC624_SPEED_27_5_Hz  (MPC624_OSR4 | MPC624_OSR3)
121#define MPC624_SPEED_13_75_Hz (MPC624_OSR4 | MPC624_OSR3 | MPC624_OSR0)
122#define MPC624_SPEED_6_875_Hz \
123	(MPC624_OSR4 | MPC624_OSR3 | MPC624_OSR2 | MPC624_OSR1 | MPC624_OSR0)
124/* -------------------------------------------------------------------------- */
125struct skel_private {
126
127	/*  set by mpc624_attach() from driver's parameters */
128	unsigned long int ulConvertionRate;
129};
130
131#define devpriv ((struct skel_private *)dev->private)
132/* -------------------------------------------------------------------------- */
133static const struct comedi_lrange range_mpc624_bipolar1 = {
134	1,
135	{
136/* BIP_RANGE(1.01)  this is correct, */
137	 /*  but my MPC-624 actually seems to have a range of 2.02 */
138	 BIP_RANGE(2.02)
139	 }
140};
141
142static const struct comedi_lrange range_mpc624_bipolar10 = {
143	1,
144	{
145/* BIP_RANGE(10.1)   this is correct, */
146	 /*  but my MPC-624 actually seems to have a range of 20.2 */
147	 BIP_RANGE(20.2)
148	 }
149};
150
151/* -------------------------------------------------------------------------- */
152static int mpc624_attach(struct comedi_device *dev,
153			 struct comedi_devconfig *it);
154static int mpc624_detach(struct comedi_device *dev);
155/* -------------------------------------------------------------------------- */
156static struct comedi_driver driver_mpc624 = {
157	.driver_name = "mpc624",
158	.module = THIS_MODULE,
159	.attach = mpc624_attach,
160	.detach = mpc624_detach
161};
162
163/* -------------------------------------------------------------------------- */
164static int mpc624_ai_rinsn(struct comedi_device *dev,
165			   struct comedi_subdevice *s, struct comedi_insn *insn,
166			   unsigned int *data);
167/* -------------------------------------------------------------------------- */
168static int mpc624_attach(struct comedi_device *dev, struct comedi_devconfig *it)
169{
170	struct comedi_subdevice *s;
171	unsigned long iobase;
172
173	iobase = it->options[0];
174	printk(KERN_INFO "comedi%d: mpc624 [0x%04lx, ", dev->minor, iobase);
175	if (request_region(iobase, MPC624_SIZE, "mpc624") == NULL) {
176		printk(KERN_ERR "I/O port(s) in use\n");
177		return -EIO;
178	}
179
180	dev->iobase = iobase;
181	dev->board_name = "mpc624";
182
183	/*  Private structure initialization */
184	if (alloc_private(dev, sizeof(struct skel_private)) < 0)
185		return -ENOMEM;
186
187	switch (it->options[1]) {
188	case 0:
189		devpriv->ulConvertionRate = MPC624_SPEED_3_52_kHz;
190		printk(KERN_INFO "3.52 kHz, ");
191		break;
192	case 1:
193		devpriv->ulConvertionRate = MPC624_SPEED_1_76_kHz;
194		printk(KERN_INFO "1.76 kHz, ");
195		break;
196	case 2:
197		devpriv->ulConvertionRate = MPC624_SPEED_880_Hz;
198		printk(KERN_INFO "880 Hz, ");
199		break;
200	case 3:
201		devpriv->ulConvertionRate = MPC624_SPEED_440_Hz;
202		printk(KERN_INFO "440 Hz, ");
203		break;
204	case 4:
205		devpriv->ulConvertionRate = MPC624_SPEED_220_Hz;
206		printk(KERN_INFO "220 Hz, ");
207		break;
208	case 5:
209		devpriv->ulConvertionRate = MPC624_SPEED_110_Hz;
210		printk(KERN_INFO "110 Hz, ");
211		break;
212	case 6:
213		devpriv->ulConvertionRate = MPC624_SPEED_55_Hz;
214		printk(KERN_INFO "55 Hz, ");
215		break;
216	case 7:
217		devpriv->ulConvertionRate = MPC624_SPEED_27_5_Hz;
218		printk(KERN_INFO "27.5 Hz, ");
219		break;
220	case 8:
221		devpriv->ulConvertionRate = MPC624_SPEED_13_75_Hz;
222		printk(KERN_INFO "13.75 Hz, ");
223		break;
224	case 9:
225		devpriv->ulConvertionRate = MPC624_SPEED_6_875_Hz;
226		printk(KERN_INFO "6.875 Hz, ");
227		break;
228	default:
229		printk
230		    (KERN_ERR "illegal conversion rate setting!"
231			" Valid numbers are 0..9. Using 9 => 6.875 Hz, ");
232		devpriv->ulConvertionRate = MPC624_SPEED_3_52_kHz;
233	}
234
235	/*  Subdevices structures */
236	if (alloc_subdevices(dev, 1) < 0)
237		return -ENOMEM;
238
239	s = dev->subdevices + 0;
240	s->type = COMEDI_SUBD_AI;
241	s->subdev_flags = SDF_READABLE | SDF_DIFF;
242	s->n_chan = 8;
243	switch (it->options[1]) {
244	default:
245		s->maxdata = 0x3FFFFFFF;
246		printk(KERN_INFO "30 bit, ");
247	}
248
249	switch (it->options[1]) {
250	case 0:
251		s->range_table = &range_mpc624_bipolar1;
252		printk(KERN_INFO "1.01V]: ");
253		break;
254	default:
255		s->range_table = &range_mpc624_bipolar10;
256		printk(KERN_INFO "10.1V]: ");
257	}
258	s->len_chanlist = 1;
259	s->insn_read = mpc624_ai_rinsn;
260
261	printk(KERN_INFO "attached\n");
262
263	return 1;
264}
265
266static int mpc624_detach(struct comedi_device *dev)
267{
268	printk(KERN_INFO "comedi%d: mpc624: remove\n", dev->minor);
269
270	if (dev->iobase)
271		release_region(dev->iobase, MPC624_SIZE);
272
273	return 0;
274}
275
276/* Timeout 200ms */
277#define TIMEOUT 200
278
279static int mpc624_ai_rinsn(struct comedi_device *dev,
280			   struct comedi_subdevice *s, struct comedi_insn *insn,
281			   unsigned int *data)
282{
283	int n, i;
284	unsigned long int data_in, data_out;
285	unsigned char ucPort;
286
287	/*
288	 *  WARNING:
289	 *  We always write 0 to GNSWA bit, so the channel range is +-/10.1Vdc
290	 */
291	outb(insn->chanspec, dev->iobase + MPC624_GNMUXCH);
292/* printk("Channel %d:\n", insn->chanspec); */
293	if (!insn->n) {
294		printk(KERN_INFO "MPC624: Warning, no data to acquire\n");
295		return 0;
296	}
297
298	for (n = 0; n < insn->n; n++) {
299		/*  Trigger the conversion */
300		outb(MPC624_ADSCK, dev->iobase + MPC624_ADC);
301		udelay(1);
302		outb(MPC624_ADCS | MPC624_ADSCK, dev->iobase + MPC624_ADC);
303		udelay(1);
304		outb(0, dev->iobase + MPC624_ADC);
305		udelay(1);
306
307		/*  Wait for the conversion to end */
308		for (i = 0; i < TIMEOUT; i++) {
309			ucPort = inb(dev->iobase + MPC624_ADC);
310			if (ucPort & MPC624_ADBUSY)
311				udelay(1000);
312			else
313				break;
314		}
315		if (i == TIMEOUT) {
316			printk(KERN_ERR "MPC624: timeout (%dms)\n", TIMEOUT);
317			data[n] = 0;
318			return -ETIMEDOUT;
319		}
320		/*  Start reading data */
321		data_in = 0;
322		data_out = devpriv->ulConvertionRate;
323		udelay(1);
324		for (i = 0; i < 32; i++) {
325			/*  Set the clock low */
326			outb(0, dev->iobase + MPC624_ADC);
327			udelay(1);
328
329			if (data_out & (1 << 31)) { /*  the next bit is a 1 */
330				/*  Set the ADSDI line (send to MPC624) */
331				outb(MPC624_ADSDI, dev->iobase + MPC624_ADC);
332				udelay(1);
333				/*  Set the clock high */
334				outb(MPC624_ADSCK | MPC624_ADSDI,
335				     dev->iobase + MPC624_ADC);
336			} else {	/*  the next bit is a 0 */
337
338				/*  Set the ADSDI line (send to MPC624) */
339				outb(0, dev->iobase + MPC624_ADC);
340				udelay(1);
341				/*  Set the clock high */
342				outb(MPC624_ADSCK, dev->iobase + MPC624_ADC);
343			}
344			/*  Read ADSDO on high clock (receive from MPC624) */
345			udelay(1);
346			data_in <<= 1;
347			data_in |=
348			    (inb(dev->iobase + MPC624_ADC) & MPC624_ADSDO) >> 4;
349			udelay(1);
350
351			data_out <<= 1;
352		}
353
354		/*
355		 *  Received 32-bit long value consist of:
356		 *    31: EOC -
357		 *          (End Of Transmission) bit - should be 0
358		 *    30: DMY
359		 *          (Dummy) bit - should be 0
360		 *    29: SIG
361		 *          (Sign) bit- 1 if the voltage is positive,
362		 *                      0 if negative
363		 *    28: MSB
364		 *          (Most Significant Bit) - the first bit of
365		 *                                   the conversion result
366		 *    ....
367		 *    05: LSB
368		 *          (Least Significant Bit)- the last bit of the
369		 *                                   conversion result
370		 *    04-00: sub-LSB
371		 *          - sub-LSBs are basically noise, but when
372		 *            averaged properly, they can increase conversion
373		 *            precision up to 29 bits; they can be discarded
374		 *            without loss of resolution.
375		 */
376
377		if (data_in & MPC624_EOC_BIT)
378			printk(KERN_INFO "MPC624:EOC bit is set (data_in=%lu)!",
379			       data_in);
380		if (data_in & MPC624_DMY_BIT)
381			printk(KERN_INFO "MPC624:DMY bit is set (data_in=%lu)!",
382			       data_in);
383		if (data_in & MPC624_SGN_BIT) {	/* Volatge is positive */
384			/*
385			 * comedi operates on unsigned numbers, so mask off EOC
386			 * and DMY and don't clear the SGN bit
387			 */
388			data_in &= 0x3FFFFFFF;
389			data[n] = data_in;
390		} else { /*  The voltage is negative */
391			/*
392			 * data_in contains a number in 30-bit two's complement
393			 * code and we must deal with it
394			 */
395			data_in |= MPC624_SGN_BIT;
396			data_in = ~data_in;
397			data_in += 1;
398			data_in &= ~(MPC624_EOC_BIT | MPC624_DMY_BIT);
399			/*  clear EOC and DMY bits */
400			data_in = 0x20000000 - data_in;
401			data[n] = data_in;
402		}
403	}
404
405	/*  Return the number of samples read/written */
406	return n;
407}
408
409static int __init driver_mpc624_init_module(void)
410{
411	return comedi_driver_register(&driver_mpc624);
412}
413
414static void __exit driver_mpc624_cleanup_module(void)
415{
416	comedi_driver_unregister(&driver_mpc624);
417}
418
419module_init(driver_mpc624_init_module);
420module_exit(driver_mpc624_cleanup_module);
421
422MODULE_AUTHOR("Comedi http://www.comedi.org");
423MODULE_DESCRIPTION("Comedi low-level driver");
424MODULE_LICENSE("GPL");