/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

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