summaryrefslogtreecommitdiff
path: root/chip/it83xx/adc.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/it83xx/adc.c')
-rw-r--r--chip/it83xx/adc.c100
1 files changed, 78 insertions, 22 deletions
diff --git a/chip/it83xx/adc.c b/chip/it83xx/adc.c
index 88615b8873..f91944b1a7 100644
--- a/chip/it83xx/adc.c
+++ b/chip/it83xx/adc.c
@@ -3,7 +3,7 @@
* found in the LICENSE file.
*/
-/* IT8380 ADC module for Chrome EC */
+/* IT83xx ADC module for Chrome EC */
#include "adc.h"
#include "adc_chip.h"
@@ -17,6 +17,11 @@
#include "timer.h"
#include "util.h"
+/* Global variables */
+static struct mutex adc_lock;
+static int adc_init_done;
+static volatile task_id_t task_waiting;
+
/* Data structure of ADC channel control registers. */
const struct adc_ctrl_t adc_ctrl_regs[] = {
{&IT83XX_ADC_VCH0CTL, &IT83XX_ADC_VCH0DATM, &IT83XX_ADC_VCH0DATL,
@@ -44,16 +49,21 @@ static void adc_enable_channel(int ch)
* for channel 0, 1, 2, and 3
* bit4 ~ bit0 : indicates voltage channel[x]
* input is selected for measurement (enable)
- * bit 7 : W/C data valid flag
+ * bit5 : data valid interrupt of adc.
+ * bit7 : W/C data valid flag
*/
- *adc_ctrl_regs[ch].adc_ctrl = 0x80 + ch;
+ *adc_ctrl_regs[ch].adc_ctrl = 0xa0 + ch;
else
/*
* for channel 4, 5, 6, and 7
* bit4 : voltage channel enable (ch 4~7 only)
+ * bit5 : data valid interrupt of adc.
* bit7 : W/C data valid flag
*/
- *adc_ctrl_regs[ch].adc_ctrl = 0x90;
+ *adc_ctrl_regs[ch].adc_ctrl = 0xb0;
+
+ task_clear_pending_irq(IT83XX_IRQ_ADC);
+ task_enable_irq(IT83XX_IRQ_ADC);
/* bit 0 : adc module enable */
IT83XX_ADC_ADCCFG |= 0x01;
@@ -79,24 +89,31 @@ static void adc_disable_channel(int ch)
/* bit 0 : adc module disable */
IT83XX_ADC_ADCCFG &= ~0x01;
+
+ task_disable_irq(IT83XX_IRQ_ADC);
}
int adc_read_channel(enum adc_channel ch)
{
+ uint32_t events;
/* voltage 0 ~ 3v = adc data register raw data 0 ~ 3FFh (10-bit ) */
uint16_t adc_raw_data;
- int num;
- int adc_ch;
+ int valid = 0;
+ int adc_ch, mv;
- adc_ch = adc_channels[ch].channel;
+ if (!adc_init_done)
+ return ADC_READ_ERROR;
- adc_enable_channel(adc_ch);
+ mutex_lock(&adc_lock);
- /* Maximum time for waiting ADC conversion is ~1.525ms */
- for (num = 0x00; num < 100; num++) {
- /* delay ~15.25us */
- IT83XX_GCTRL_WNCKR = 0;
+ task_waiting = task_get_current();
+ adc_ch = adc_channels[ch].channel;
+ adc_enable_channel(adc_ch);
+ /* Wait for interrupt */
+ events = task_wait_event_mask(TASK_EVENT_ADC_DONE, ADC_TIMEOUT_US);
+ task_waiting = TASK_ID_INVALID;
+ if (events & TASK_EVENT_ADC_DONE) {
/* data valid of adc channel[x] */
if (IT83XX_ADC_ADCDVSTS & (1 << adc_ch)) {
/* read adc raw data msb and lsb */
@@ -106,15 +123,40 @@ int adc_read_channel(enum adc_channel ch)
/* W/C data valid flag */
IT83XX_ADC_ADCDVSTS = (1 << adc_ch);
- adc_disable_channel(adc_ch);
- return adc_raw_data * adc_channels[ch].factor_mul /
+ mv = adc_raw_data * adc_channels[ch].factor_mul /
adc_channels[ch].factor_div +
adc_channels[ch].shift;
+ valid = 1;
}
}
-
adc_disable_channel(adc_ch);
- return ADC_READ_ERROR;
+
+ mutex_unlock(&adc_lock);
+
+ return valid ? mv : ADC_READ_ERROR;
+}
+
+void adc_interrupt(void)
+{
+ /*
+ * Clear the interrupt status.
+ *
+ * NOTE:
+ * The ADC interrupt pending flag won't be cleared unless
+ * we W/C data valid flag of ADC module as well.
+ * (If interrupt type setting is high-level triggered)
+ */
+ task_clear_pending_irq(IT83XX_IRQ_ADC);
+ /*
+ * We disable ADC interrupt here, because current setting of
+ * interrupt type is high-level triggered.
+ * The interrupt will be triggered again and again until
+ * we W/C data valid flag if we don't disable it.
+ */
+ task_disable_irq(IT83XX_IRQ_ADC);
+ /* Wake up the task which was waiting for the interrupt */
+ if (task_waiting != TASK_ID_INVALID)
+ task_set_event(task_waiting, TASK_EVENT_ADC_DONE, 0);
}
/*
@@ -122,7 +164,7 @@ int adc_read_channel(enum adc_channel ch)
*
* Write 1 to this bit and write 0 to this bit immediately once and
* only once during the firmware initialization and do not write 1 again
- * after initialization since IT8380 takes much power consumption
+ * after initialization since IT83xx takes much power consumption
* if this bit is set as 1
*/
static void adc_accuracy_initialization(void)
@@ -150,10 +192,24 @@ static void adc_init(void)
/* enable adc channel[x] function pin */
*adc_ctrl_regs[ch].adc_pin_ctrl = 0x00;
}
-
- /* bit 5 : ADCCTS0 = 1 */
- IT83XX_ADC_ADCCFG = 0x20;
-
- IT83XX_ADC_ADCCTL = 0x04;
+ /*
+ * bit7@ADCSTS : ADCCTS1 = 0
+ * bit5@ADCCFG : ADCCTS0 = 0
+ * bit[5-0]@ADCCTL : SCLKDIV
+ * The ADC channel conversion time is 30.8*(SCLKDIV+1) us.
+ * (Current setting is 61.6us)
+ *
+ * NOTE: A sample time delay (60us) also need to be included in
+ * conversion time, so the final result is ~= 121.6us.
+ */
+ IT83XX_ADC_ADCSTS &= ~(1 << 7);
+ IT83XX_ADC_ADCCFG &= ~(1 << 5);
+ IT83XX_ADC_ADCCTL = 1;
+
+ task_waiting = TASK_ID_INVALID;
+ /* disable adc interrupt */
+ task_disable_irq(IT83XX_IRQ_ADC);
+
+ adc_init_done = 1;
}
DECLARE_HOOK(HOOK_INIT, adc_init, HOOK_PRIO_INIT_ADC);