Intel® Quark™ Microcontroller Software Interface  1.4.0
Intel® Quark™ Microcontroller BSP
qm_adc.c
1 /*
2  * {% copyright %}
3  */
4 
5 #include "qm_adc.h"
6 #include "clk.h"
7 #include <string.h>
8 
9 /* FIFO_INTERRUPT_THRESHOLD is used by qm_adc_irq_convert to set the threshold
10  * at which the FIFO will trigger an interrupt. */
11 #define FIFO_INTERRUPT_THRESHOLD (16)
12 
13 #define QM_ADC_CHAN_SEQ_MAX (32)
14 
15 /* ADC commands. */
16 #define QM_ADC_CMD_START_SINGLE (0)
17 #define QM_ADC_CMD_START_CONT (1)
18 #define QM_ADC_CMD_RESET_CAL (2)
19 #define QM_ADC_CMD_START_CAL (3)
20 #define QM_ADC_CMD_LOAD_CAL (4)
21 #define QM_ADC_CMD_STOP_CONT (5)
22 
23 static uint8_t sample_window[QM_ADC_NUM];
24 static qm_adc_resolution_t resolution[QM_ADC_NUM];
25 
26 static qm_adc_xfer_t *irq_xfer[QM_ADC_NUM];
27 static uint32_t count[QM_ADC_NUM];
28 static bool dummy_conversion = false;
29 
30 /* Callbacks for mode change and calibration. */
31 static void (*mode_callback[QM_ADC_NUM])(void *data, int error,
32  qm_adc_status_t status,
33  qm_adc_cb_source_t source);
34 static void (*cal_callback[QM_ADC_NUM])(void *data, int error,
35  qm_adc_status_t status,
36  qm_adc_cb_source_t source);
37 static void *mode_callback_data[QM_ADC_NUM];
38 static void *cal_callback_data[QM_ADC_NUM];
39 
40 /* ISR handler for command/calibration complete. */
41 static void qm_adc_isr_handler(const qm_adc_t adc)
42 {
43  uint32_t int_status = 0;
44  uint32_t i, samples_to_read;
45 
46  int_status = QM_ADC[adc].adc_intr_status;
47 
48  /* FIFO overrun interrupt. */
49  if (int_status & QM_ADC_INTR_STATUS_FO) {
50  /* Stop the transfer. */
51  QM_ADC[adc].adc_cmd = QM_ADC_CMD_STOP_CONT;
52  /* Disable all interrupts. */
53  QM_ADC[adc].adc_intr_enable = 0;
54  /* Call the user callback. */
55  if (irq_xfer[adc]->callback) {
56  irq_xfer[adc]->callback(irq_xfer[adc]->callback_data,
57  -EIO, QM_ADC_OVERFLOW,
59  }
60  }
61 
62  /* Continuous mode command complete interrupt. */
63  if (int_status & QM_ADC_INTR_STATUS_CONT_CC) {
64  /* Clear the interrupt. */
65  QM_ADC[adc].adc_intr_status &= QM_ADC_INTR_STATUS_CONT_CC;
66 
67  /* Calculate the number of samples to read. */
68  samples_to_read = QM_ADC[adc].adc_fifo_count;
69  if (samples_to_read >
70  (irq_xfer[adc]->samples_len - count[adc])) {
71  samples_to_read =
72  irq_xfer[adc]->samples_len - count[adc];
73  }
74 
75  /* Copy data out of FIFO. The sample must be shifted right by
76  * 2, 4 or 6 bits for 10, 8 and 6 bit resolution respectively
77  * to get the correct value. */
78  for (i = 0; i < samples_to_read; i++) {
79  irq_xfer[adc]->samples[count[adc]] =
80  (QM_ADC[adc].adc_sample >>
81  (2 * (3 - resolution[adc])));
82  count[adc]++;
83  }
84 
85  /* Check if we have the requested number of samples, stop the
86  * conversion and call the user callback function. */
87  if (count[adc] == irq_xfer[adc]->samples_len) {
88  /* Stop the transfer. */
89  QM_ADC[adc].adc_cmd = QM_ADC_CMD_STOP_CONT;
90  /* Disable all interrupts. */
91  QM_ADC[adc].adc_intr_enable = 0;
92  /* Call the user callback. */
93  if (irq_xfer[adc]->callback) {
94  irq_xfer[adc]->callback(
95  irq_xfer[adc]->callback_data, 0,
97  }
98  }
99  }
100 
101  /* The Command Complete interrupt is currently used to notify of the
102  * completion of a calibration command or a dummy conversion. */
103  if ((int_status & QM_ADC_INTR_STATUS_CC) && (!dummy_conversion)) {
104  /* Disable and clear the Command Complete interrupt */
105  QM_ADC[adc].adc_intr_enable &= ~QM_ADC_INTR_ENABLE_CC;
106  QM_ADC[adc].adc_intr_status = QM_ADC_INTR_STATUS_CC;
107 
108  /* Call the user callback if it is set. */
109  if (cal_callback[adc]) {
110  cal_callback[adc](irq_xfer[adc]->callback_data, 0,
112  }
113  }
114 
115  /* This dummy conversion is needed when switching to normal mode or
116  * normal mode with calibration. */
117  if ((int_status & QM_ADC_INTR_STATUS_CC) && (dummy_conversion)) {
118  /* Flush the FIFO to get rid of the dummy values. */
119  QM_ADC[adc].adc_sample = QM_ADC_FIFO_CLEAR;
120  /* Disable and clear the Command Complete interrupt. */
121  QM_ADC[adc].adc_intr_enable &= ~QM_ADC_INTR_ENABLE_CC;
122  QM_ADC[adc].adc_intr_status = QM_ADC_INTR_STATUS_CC;
123 
124  dummy_conversion = false;
125 
126  /* Call the user callback if it is set. */
127  if (mode_callback[adc]) {
128  mode_callback[adc](irq_xfer[adc]->callback_data, 0,
130  }
131  }
132 }
133 
134 /* ISR handler for mode change. */
135 static void qm_adc_pwr_0_isr_handler(const qm_adc_t adc)
136 {
137  /* Clear the interrupt. Note that this operates differently to the
138  * QM_ADC_INTR_STATUS regiseter because you have to write to the
139  * QM_ADC_OP_MODE register, Interrupt Enable bit to clear. */
140  QM_ADC[adc].adc_op_mode &= ~QM_ADC_OP_MODE_IE;
141 
142  /* Perform a dummy conversion if we are transitioning to Normal Mode or
143  * Normal Mode With Calibration */
144  if ((QM_ADC[adc].adc_op_mode & QM_ADC_OP_MODE_OM_MASK) >=
146 
147  /* Set the first sequence register back to its default (ch 0) */
148  QM_ADC[adc].adc_seq0 = QM_ADC_CAL_SEQ_TABLE_DEFAULT;
149  /* Clear the command complete interrupt status field */
150  QM_ADC[adc].adc_intr_status = QM_ADC_INTR_STATUS_CC;
151 
152  dummy_conversion = true;
153 
154  /* Run a dummy conversion */
155  QM_ADC[adc].adc_cmd = (QM_ADC_CMD_IE | QM_ADC_CMD_START_SINGLE);
156  } else {
157  /* Call the user callback function */
158  if (mode_callback[adc]) {
159  mode_callback[adc](irq_xfer[adc]->callback_data, 0,
161  }
162  }
163 }
164 
165 /* ISR for ADC 0 Command/Calibration Complete. */
166 QM_ISR_DECLARE(qm_adc_0_cal_isr)
167 {
168  qm_adc_isr_handler(QM_ADC_0);
169 
170  QM_ISR_EOI(QM_IRQ_ADC_0_CAL_INT_VECTOR);
171 }
172 
173 /* ISR for ADC 0 Mode Change. */
174 QM_ISR_DECLARE(qm_adc_0_pwr_isr)
175 {
176  qm_adc_pwr_0_isr_handler(QM_ADC_0);
177 
178  QM_ISR_EOI(QM_IRQ_ADC_0_PWR_0_VECTOR);
179 }
180 
181 static void setup_seq_table(const qm_adc_t adc, qm_adc_xfer_t *xfer)
182 {
183  uint32_t i, offset = 0;
184  volatile uint32_t *reg_pointer = NULL;
185 
186  /* Loop over all of the channels to be added. */
187  for (i = 0; i < xfer->ch_len; i++) {
188  /* Get a pointer to the correct address. */
189  reg_pointer = &QM_ADC[adc].adc_seq0 + (i / 4);
190  /* Get the offset within the register */
191  offset = ((i % 4) * 8);
192  /* Clear the Last bit from all entries we will use. */
193  *reg_pointer &= ~(1 << (offset + 7));
194  /* Place the channel numnber into the sequence table. */
195  *reg_pointer |= (xfer->ch[i] << offset);
196  }
197 
198  if (reg_pointer) {
199  /* Set the correct Last bit. */
200  *reg_pointer |= (1 << (offset + 7));
201  }
202 }
203 
205 {
206  QM_CHECK(adc < QM_ADC_NUM, -EINVAL);
207 
208  /* Clear the command complete interrupt status field. */
209  QM_ADC[adc].adc_intr_status = QM_ADC_INTR_STATUS_CC;
210  /* Start the calibration and wait for it to complete. */
211  QM_ADC[adc].adc_cmd = (QM_ADC_CMD_IE | QM_ADC_CMD_START_CAL);
212  while (!(QM_ADC[adc].adc_intr_status & QM_ADC_INTR_STATUS_CC))
213  ;
214  /* Clear the command complete interrupt status field again. */
215  QM_ADC[adc].adc_intr_status = QM_ADC_INTR_STATUS_CC;
216 
217  return 0;
218 }
219 
221  void (*callback)(void *data, int error,
222  qm_adc_status_t status,
223  qm_adc_cb_source_t source),
224  void *callback_data)
225 {
226  QM_CHECK(adc < QM_ADC_NUM, -EINVAL);
227 
228  /* Set the callback. */
229  cal_callback[adc] = callback;
230  cal_callback_data[adc] = callback_data;
231 
232  /* Clear and enable the command complete interrupt. */
233  QM_ADC[adc].adc_intr_status = QM_ADC_INTR_STATUS_CC;
234  QM_ADC[adc].adc_intr_enable |= QM_ADC_INTR_ENABLE_CC;
235 
236  /* Start the calibration */
237  QM_ADC[adc].adc_cmd = (QM_ADC_CMD_IE | QM_ADC_CMD_START_CAL);
238 
239  return 0;
240 }
241 
243 {
244  QM_CHECK(adc < QM_ADC_NUM, -EINVAL);
245  QM_CHECK(cal < 0x3F, -EINVAL);
246 
247  /* Clear the command complete interrupt status field. */
248  QM_ADC[adc].adc_intr_status = QM_ADC_INTR_STATUS_CC;
249 
250  /* Set the calibration data and wait for it to complete. */
251  QM_ADC[adc].adc_cmd = ((cal << QM_ADC_CMD_CAL_DATA_OFFSET) |
252  QM_ADC_CMD_IE | QM_ADC_CMD_LOAD_CAL);
253  while (!(QM_ADC[adc].adc_intr_status & QM_ADC_INTR_STATUS_CC))
254  ;
255  /* Clear the command complete interrupt status field again. */
256  QM_ADC[adc].adc_intr_status = QM_ADC_INTR_STATUS_CC;
257 
258  return 0;
259 }
260 
262 {
263  QM_CHECK(adc < QM_ADC_NUM, -EINVAL);
264  QM_CHECK(NULL != cal, -EINVAL);
265 
266  *cal = QM_ADC[adc].adc_calibration;
267 
268  return 0;
269 }
270 
271 int qm_adc_set_mode(const qm_adc_t adc, const qm_adc_mode_t mode)
272 {
273  QM_CHECK(adc < QM_ADC_NUM, -EINVAL);
274  QM_CHECK(mode <= QM_ADC_MODE_NORM_NO_CAL, -EINVAL);
275 
276  /* Issue mode change command and wait for it to complete. */
277  QM_ADC[adc].adc_op_mode = mode;
278  while ((QM_ADC[adc].adc_op_mode & QM_ADC_OP_MODE_OM_MASK) != mode)
279  ;
280 
281  /* Perform a dummy conversion if we are transitioning to Normal Mode. */
282  if ((mode >= QM_ADC_MODE_NORM_CAL)) {
283  /* Set the first sequence register back to its default. */
284  QM_ADC[adc].adc_seq0 = QM_ADC_CAL_SEQ_TABLE_DEFAULT;
285 
286  /* Clear the command complete interrupt status field. */
287  QM_ADC[adc].adc_intr_status = QM_ADC_INTR_STATUS_CC;
288  /* Run a dummy convert and wait for it to complete. */
289  QM_ADC[adc].adc_cmd = (QM_ADC_CMD_IE | QM_ADC_CMD_START_SINGLE);
290  while (!(QM_ADC[adc].adc_intr_status & QM_ADC_INTR_STATUS_CC))
291  ;
292 
293  /* Flush the FIFO to get rid of the dummy values. */
294  QM_ADC[adc].adc_sample = QM_ADC_FIFO_CLEAR;
295  /* Clear the command complete interrupt status field. */
296  QM_ADC[adc].adc_intr_status = QM_ADC_INTR_STATUS_CC;
297  }
298 
299  return 0;
300 }
301 
302 int qm_adc_irq_set_mode(const qm_adc_t adc, const qm_adc_mode_t mode,
303  void (*callback)(void *data, int error,
304  qm_adc_status_t status,
305  qm_adc_cb_source_t source),
306  void *callback_data)
307 {
308  QM_CHECK(adc < QM_ADC_NUM, -EINVAL);
309  QM_CHECK(mode <= QM_ADC_MODE_NORM_NO_CAL, -EINVAL);
310 
311  /* Set the callback. */
312  mode_callback[adc] = callback;
313  mode_callback_data[adc] = callback_data;
314 
315  /* When transitioning to Normal Mode or Normal Mode With Calibration,
316  * enable command complete interrupt to perform a dummy conversion. */
317  if ((mode >= QM_ADC_MODE_NORM_CAL)) {
318  QM_ADC[adc].adc_intr_enable |= QM_ADC_INTR_ENABLE_CC;
319  }
320 
321  /* Issue mode change command. Completion if this command is notified via
322  * the ADC Power interrupt source, which is serviced separately to the
323  * Command/Calibration Complete interrupt. */
324  QM_ADC[adc].adc_op_mode = (QM_ADC_OP_MODE_IE | mode);
325 
326  return 0;
327 }
328 
329 int qm_adc_set_config(const qm_adc_t adc, const qm_adc_config_t *const cfg)
330 {
331  QM_CHECK(adc < QM_ADC_NUM, -EINVAL);
332  QM_CHECK(NULL != cfg, -EINVAL);
333  QM_CHECK(cfg->resolution <= QM_ADC_RES_12_BITS, -EINVAL);
334  /* Convert cfg->resolution to actual resolution (2x+6) and add 2 to get
335  * minimum value for window size. */
336  QM_CHECK(cfg->window >= ((cfg->resolution * 2) + 8), -EINVAL);
337 
338  /* Set the sample window and resolution. */
339  sample_window[adc] = cfg->window;
340  resolution[adc] = cfg->resolution;
341 
342  return 0;
343 }
344 
345 int qm_adc_convert(const qm_adc_t adc, qm_adc_xfer_t *xfer,
346  qm_adc_status_t *const status)
347 {
348  uint32_t i;
349 
350  QM_CHECK(adc < QM_ADC_NUM, -EINVAL);
351  QM_CHECK(NULL != xfer, -EINVAL);
352  QM_CHECK(NULL != xfer->ch, -EINVAL);
353  QM_CHECK(NULL != xfer->samples, -EINVAL);
354  QM_CHECK(xfer->ch_len > 0, -EINVAL);
355  QM_CHECK(xfer->ch_len <= QM_ADC_CHAN_SEQ_MAX, -EINVAL);
356  QM_CHECK(xfer->samples_len > 0, -EINVAL);
357  QM_CHECK(xfer->samples_len <= QM_ADC_FIFO_LEN, -EINVAL);
358 
359  /* Flush the FIFO. */
360  QM_ADC[adc].adc_sample = QM_ADC_FIFO_CLEAR;
361 
362  /* Populate the sample sequence table. */
363  setup_seq_table(adc, xfer);
364 
365  /* Issue cmd: window & resolution, number of samples, command. */
366  QM_ADC[adc].adc_cmd =
367  (sample_window[adc] << QM_ADC_CMD_SW_OFFSET |
368  resolution[adc] << QM_ADC_CMD_RESOLUTION_OFFSET |
369  ((xfer->samples_len - 1) << QM_ADC_CMD_NS_OFFSET) |
370  QM_ADC_CMD_START_SINGLE);
371 
372  /* Wait for fifo count to reach number of samples. */
373  while (QM_ADC[adc].adc_fifo_count != xfer->samples_len)
374  ;
375 
376  if (status) {
377  *status = QM_ADC_COMPLETE;
378  }
379 
380  /* Read the value into the data structure. The sample must be shifted
381  * right by 2, 4 or 6 bits for 10, 8 and 6 bit resolution respectively
382  * to get the correct value. */
383  for (i = 0; i < xfer->samples_len; i++) {
384  xfer->samples[i] =
385  (QM_ADC[adc].adc_sample >> (2 * (3 - resolution[adc])));
386  }
387 
388  return 0;
389 }
390 
392 {
393  QM_CHECK(adc < QM_ADC_NUM, -EINVAL);
394  QM_CHECK(NULL != xfer, -EINVAL);
395  QM_CHECK(NULL != xfer->ch, -EINVAL);
396  QM_CHECK(NULL != xfer->samples, -EINVAL);
397  QM_CHECK(xfer->ch_len > 0, -EINVAL);
398  QM_CHECK(xfer->ch_len <= QM_ADC_CHAN_SEQ_MAX, -EINVAL);
399  QM_CHECK(xfer->samples_len > 0, -EINVAL);
400 
401  /* Reset the count and flush the FIFO. */
402  count[adc] = 0;
403  QM_ADC[adc].adc_sample = QM_ADC_FIFO_CLEAR;
404 
405  /* Populate the sample sequence table. */
406  setup_seq_table(adc, xfer);
407 
408  /* Copy the xfer struct so we can get access from the ISR. */
409  irq_xfer[adc] = xfer;
410 
411  /* Clear all pending interrupts. */
412  QM_ADC[adc].adc_intr_status = QM_ADC_INTR_STATUS_CC |
413  QM_ADC_INTR_STATUS_FO |
414  QM_ADC_INTR_STATUS_CONT_CC;
415  /* Enable the continuous command and fifo overrun interupts. */
416  QM_ADC[adc].adc_intr_enable =
417  QM_ADC_INTR_ENABLE_FO | QM_ADC_INTR_ENABLE_CONT_CC;
418 
419  /* Issue cmd: window & resolution, number of samples, interrupt enable
420  * and start continuous coversion command. If xfer->samples_len is less
421  * than FIFO_INTERRUPT_THRESHOLD extra samples will be discarded in the
422  * ISR. */
423  QM_ADC[adc].adc_cmd =
424  (sample_window[adc] << QM_ADC_CMD_SW_OFFSET |
425  resolution[adc] << QM_ADC_CMD_RESOLUTION_OFFSET |
426  ((FIFO_INTERRUPT_THRESHOLD - 1) << QM_ADC_CMD_NS_OFFSET) |
427  QM_ADC_CMD_IE | QM_ADC_CMD_START_CONT);
428 
429  return 0;
430 }
void(* callback)(void *data, int error, qm_adc_status_t status, qm_adc_cb_source_t source)
Transfer callback.
Definition: qm_adc.h:124
qm_adc_mode_t
ADC operating mode type.
Definition: qm_adc.h:49
qm_adc_channel_t * ch
Channel sequence array (1-32 channels).
Definition: qm_adc.h:108
ADC transfer complete.
Definition: qm_adc.h:32
qm_adc_resolution_t resolution
12, 10, 8, 6-bit resolution.
Definition: qm_adc.h:101
ADC transfer type.
Definition: qm_adc.h:107
int qm_adc_irq_set_mode(const qm_adc_t adc, const qm_adc_mode_t mode, void(*callback)(void *data, int error, qm_adc_status_t status, qm_adc_cb_source_t source), void *callback_data)
Switch operating mode of ADC.
Definition: qm_adc.c:302
int qm_adc_irq_calibrate(const qm_adc_t adc, void(*callback)(void *data, int error, qm_adc_status_t status, qm_adc_cb_source_t source), void *callback_data)
Calibrate the ADC.
Definition: qm_adc.c:220
int qm_adc_get_calibration(const qm_adc_t adc, qm_adc_calibration_t *const cal)
Get the current calibration data for an ADC.
Definition: qm_adc.c:261
qm_adc_t
Number of ADC controllers.
Definition: qm_soc_regs.h:1066
qm_adc_status_t
Definition: qm_adc.h:30
Calibration complete callback.
Definition: qm_adc.h:88
Mode change complete callback.
Definition: qm_adc.h:87
qm_adc_cb_source_t
ADC interrupt callback source.
Definition: qm_adc.h:85
uint8_t ch_len
Number of channels in the above array.
Definition: qm_adc.h:109
uint32_t samples_len
Length of sample array.
Definition: qm_adc.h:111
void * callback_data
Callback user data.
Definition: qm_adc.h:126
int qm_adc_irq_convert(const qm_adc_t adc, qm_adc_xfer_t *xfer)
Asynchronously read values from the ADC.
Definition: qm_adc.c:391
int qm_adc_convert(const qm_adc_t adc, qm_adc_xfer_t *xfer, qm_adc_status_t *const status)
Synchronously read values from the ADC.
Definition: qm_adc.c:345
Transfer complete or error callback.
Definition: qm_adc.h:86
ADC configuration type.
Definition: qm_adc.h:94
int qm_adc_set_config(const qm_adc_t adc, const qm_adc_config_t *const cfg)
Set ADC configuration.
Definition: qm_adc.c:329
qm_adc_sample_t * samples
Array to store samples.
Definition: qm_adc.h:110
int qm_adc_calibrate(const qm_adc_t adc)
Calibrate the ADC.
Definition: qm_adc.c:204
int qm_adc_set_mode(const qm_adc_t adc, const qm_adc_mode_t mode)
Switch operating mode of ADC.
Definition: qm_adc.c:271
ADC idle.
Definition: qm_adc.h:31
ADC FIFO overflow error.
Definition: qm_adc.h:33
uint8_t qm_adc_calibration_t
ADC calibration type.
Definition: qm_adc.h:28
QM_ISR_DECLARE(qm_adc_0_cal_isr)
ISR for ADC 0 convert and calibration interrupt.
Definition: qm_adc.c:166
int qm_adc_set_calibration(const qm_adc_t adc, const qm_adc_calibration_t cal)
Set ADC calibration data.
Definition: qm_adc.c:242
12-bit mode.
Definition: qm_adc.h:43
qm_adc_resolution_t
ADC resolution type.
Definition: qm_adc.h:39
uint8_t window
Sample interval in ADC clock cycles, defines the period to wait between the start of each sample and ...
Definition: qm_adc.h:100
Normal mode, with calibration.
Definition: qm_adc.h:53
Normal mode, no calibration.
Definition: qm_adc.h:54