From 18500f672d0dcc76c79c8975a014293d7c7e94db Mon Sep 17 00:00:00 2001 From: Ruibin Chang Date: Fri, 6 Mar 2020 15:55:07 +0800 Subject: it83xx/adc: add voltage comparator feature Add voltage comparator feature. BUG=b:149094481 BRANCH=none TEST=on board it83xx_evb, 1.set VCMP1 threshold 2.8v: external input 3v, the INT would be triggered and ADC5 read the correctly voltage. 2.set VCMP0 threshold 0.2v: external input 0v, the INT would be triggered and ADC5 read the correctly voltage. Change-Id: I59510b1c6bd38004ff06e0fcbd2a671e895d59e3 Signed-off-by: Ruibin Chang Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2062110 Tested-by: Ruibin Chang Reviewed-by: Diana Z Commit-Queue: Ruibin Chang --- chip/it83xx/adc.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++ chip/it83xx/adc_chip.h | 73 +++++++++++++++++++++++++ chip/it83xx/intc.c | 5 ++ chip/it83xx/intc.h | 3 ++ chip/it83xx/registers.h | 22 ++++++++ include/config.h | 3 ++ 6 files changed, 244 insertions(+) diff --git a/chip/it83xx/adc.c b/chip/it83xx/adc.c index b6d5a7dbc1..0af56e32a3 100644 --- a/chip/it83xx/adc.c +++ b/chip/it83xx/adc.c @@ -62,6 +62,31 @@ const struct adc_ctrl_t adc_ctrl_regs[] = { }; BUILD_ASSERT(ARRAY_SIZE(adc_ctrl_regs) == CHIP_ADC_COUNT); +#ifdef CONFIG_ADC_VOLTAGE_COMPARATOR +#define VCMP_ADC_CH_MASK_H BIT(3) +#define VCMP_ADC_CH_MASK_L 0x7 +/* 10-bits resolution */ +#define VCMP_RESOLUTION BIT(10) +#define VCMP_MAX_MVOLT 3000 + +/* Data structure of voltage comparator control registers. */ +const struct vcmp_ctrl_t vcmp_ctrl_regs[] = { + {&IT83XX_ADC_VCMP0CTL, &IT83XX_ADC_VCMP0CSELM, &IT83XX_ADC_CMP0THRDATM, + &IT83XX_ADC_CMP0THRDATL}, + {&IT83XX_ADC_VCMP1CTL, &IT83XX_ADC_VCMP1CSELM, &IT83XX_ADC_CMP1THRDATM, + &IT83XX_ADC_CMP1THRDATL}, + {&IT83XX_ADC_VCMP2CTL, &IT83XX_ADC_VCMP2CSELM, &IT83XX_ADC_CMP2THRDATM, + &IT83XX_ADC_CMP2THRDATL}, + {&IT83XX_ADC_VCMP3CTL, &IT83XX_ADC_VCMP3CSELM, &IT83XX_ADC_CMP3THRDATM, + &IT83XX_ADC_CMP3THRDATL}, + {&IT83XX_ADC_VCMP4CTL, &IT83XX_ADC_VCMP4CSELM, &IT83XX_ADC_CMP4THRDATM, + &IT83XX_ADC_CMP4THRDATL}, + {&IT83XX_ADC_VCMP5CTL, &IT83XX_ADC_VCMP5CSELM, &IT83XX_ADC_CMP5THRDATM, + &IT83XX_ADC_CMP5THRDATL}, +}; +BUILD_ASSERT(ARRAY_SIZE(vcmp_ctrl_regs) == CHIP_VCMP_COUNT); +#endif + static void adc_enable_channel(int ch) { if (ch < CHIP_ADC_CH4) @@ -190,6 +215,110 @@ void adc_interrupt(void) task_set_event(task_waiting, TASK_EVENT_ADC_DONE, 0); } +#ifdef CONFIG_ADC_VOLTAGE_COMPARATOR +/* Clear voltage comparator interrupt status */ +void clear_vcmp_status(int vcmp_x) +{ + if (vcmp_x <= CHIP_VCMP2) + IT83XX_ADC_VCMPSTS = BIT(vcmp_x); + else + IT83XX_ADC_VCMPSTS2 = BIT(vcmp_x - CHIP_VCMP3); +} + +/* Enable/Disable voltage comparator interrupt */ +void vcmp_enable(int idx, int enable) +{ + if (enable) { + /* Enable comparator interrupt */ + *vcmp_ctrl_regs[idx].vcmp_ctrl |= ADC_VCMP_CMPINTEN; + /* Start voltage comparator */ + *vcmp_ctrl_regs[idx].vcmp_ctrl |= ADC_VCMP_CMPEN; + } else { + /* Stop voltage comparator */ + *vcmp_ctrl_regs[idx].vcmp_ctrl &= ~ADC_VCMP_CMPEN; + /* Disable comparator interrupt */ + *vcmp_ctrl_regs[idx].vcmp_ctrl &= ~ADC_VCMP_CMPINTEN; + } +} + +/* Set voltage comparator conditions */ +void set_voltage_comparator_condition(int idx) +{ + int val; + + /* CMPXTHRDAT[9:0] = threshold(mv) * 1024 / 3000(mv) */ + val = vcmp_list[idx].threshold * VCMP_RESOLUTION / VCMP_MAX_MVOLT; + *vcmp_ctrl_regs[idx].vcmp_datl = (uint8_t)(val & 0xff); + *vcmp_ctrl_regs[idx].vcmp_datm = (uint8_t)((val >> 8) & 0xff); + + /* Select greater or less equal than threshold */ + if (vcmp_list[idx].flag & GREATER_THRESHOLD) + *vcmp_ctrl_regs[idx].vcmp_ctrl |= ADC_VCMP_GREATER_THRESHOLD; + else + *vcmp_ctrl_regs[idx].vcmp_ctrl &= ~ADC_VCMP_GREATER_THRESHOLD; +} + +/* Voltage comparator interrupt, handle one channel at a time. */ +void voltage_comparator_interrupt(void) +{ + int idx, status; + + /* Find out which voltage comparator triggered */ + status = IT83XX_ADC_VCMPSTS & 0x07; + status |= (IT83XX_ADC_VCMPSTS2 & 0x07) << 3; + + for (idx = CHIP_VCMP0; idx < VCMP_COUNT; idx++) { + if (status & BIT(idx)) { + /* Called back to board-level function */ + if (vcmp_list[idx].vcmp_thresh_cb) + vcmp_list[idx].vcmp_thresh_cb(); + /* Clear voltage comparator interrupt status */ + clear_vcmp_status(idx); + } + } + + /* Clear interrupt status */ + task_clear_pending_irq(IT83XX_IRQ_V_COMP); +} + +/* Voltage comparator initialization */ +static void voltage_comparator_init(void) +{ + int idx; + + /* No voltage comparator is declared */ + if (!VCMP_COUNT) + return; + + for (idx = CHIP_VCMP0; idx < VCMP_COUNT; idx++) { + /* + * Select voltage comparator: + * vcmp_list[i] use voltage comparator i, i = 0 ~ 5. + */ + + /* Select which ADC channel output voltage into comparator */ + *vcmp_ctrl_regs[idx].vcmp_ctrl |= + vcmp_list[idx].adc_ch & VCMP_ADC_CH_MASK_L; + if (vcmp_list[idx].adc_ch & VCMP_ADC_CH_MASK_H) + *vcmp_ctrl_regs[idx].vcmp_adc_chm |= ADC_VCMP_VCMPCSELM; + + /* Set "all voltage comparator" scan period */ + IT83XX_ADC_VCMPSCP = vcmp_list[idx].scan_period; + /* Set voltage comparator conditions */ + set_voltage_comparator_condition(idx); + /* Clear voltage comparator interrupt status */ + clear_vcmp_status(idx); + /* Enable comparator interrupt and start */ + vcmp_enable(idx, 1); + } + + /* Clear interrupt status */ + task_clear_pending_irq(IT83XX_IRQ_V_COMP); + /* Enable voltage comparator to interrupt MCU */ + task_enable_irq(IT83XX_IRQ_V_COMP); +} +#endif + /* * ADC analog accuracy initialization (only once after VSTBY power on) * @@ -241,6 +370,15 @@ static void adc_init(void) /* disable adc interrupt */ task_disable_irq(IT83XX_IRQ_ADC); +#ifdef CONFIG_ADC_VOLTAGE_COMPARATOR + /* + * Init voltage comparator + * NOTE:ADC channel signal output to voltage comparator, + * so we need set the channel to ADC alternate mode first. + */ + voltage_comparator_init(); +#endif + adc_init_done = 1; } DECLARE_HOOK(HOOK_INIT, adc_init, HOOK_PRIO_INIT_ADC); diff --git a/chip/it83xx/adc_chip.h b/chip/it83xx/adc_chip.h index c43a64c132..e894e97b64 100644 --- a/chip/it83xx/adc_chip.h +++ b/chip/it83xx/adc_chip.h @@ -41,6 +41,33 @@ enum chip_adc_channel { CHIP_ADC_COUNT, }; +/* List of voltage comparator. */ +enum chip_vcmp { + CHIP_VCMP0 = 0, + CHIP_VCMP1, + CHIP_VCMP2, + CHIP_VCMP3, + CHIP_VCMP4, + CHIP_VCMP5, + CHIP_VCMP_COUNT, +}; + +/* List of voltage comparator scan period times. */ +enum vcmp_scan_period { + VCMP_SCAN_PERIOD_100US = 0x10, + VCMP_SCAN_PERIOD_200US = 0x20, + VCMP_SCAN_PERIOD_400US = 0x30, + VCMP_SCAN_PERIOD_600US = 0x40, + VCMP_SCAN_PERIOD_800US = 0x50, + VCMP_SCAN_PERIOD_1MS = 0x60, + VCMP_SCAN_PERIOD_1_5MS = 0x70, + VCMP_SCAN_PERIOD_2MS = 0x80, + VCMP_SCAN_PERIOD_2_5MS = 0x90, + VCMP_SCAN_PERIOD_3MS = 0xA0, + VCMP_SCAN_PERIOD_4MS = 0xB0, + VCMP_SCAN_PERIOD_5MS = 0xC0, +}; + /* Data structure to define ADC channel control registers. */ struct adc_ctrl_t { volatile uint8_t *adc_ctrl; @@ -58,10 +85,56 @@ struct adc_t { enum chip_adc_channel channel; }; +/* Data structure to define voltage comparator control registers. */ +struct vcmp_ctrl_t { + volatile uint8_t *vcmp_ctrl; + volatile uint8_t *vcmp_adc_chm; + volatile uint8_t *vcmp_datm; + volatile uint8_t *vcmp_datl; +}; + +/* supported flags (member "flag" in struct vcmp_t) for voltage comparator */ +#define GREATER_THRESHOLD BIT(0) +#define LESS_EQUAL_THRESHOLD BIT(1) + +/* Data structure for board to define voltage comparator list. */ +struct vcmp_t { + const char *name; + int threshold; + /* + * Select greater/less equal threshold. + * NOTE: once edge trigger interrupt fires, we need disable the voltage + * comparator, or the matching threshold level will infinitely + * triggers interrupt. + */ + char flag; + /* Called when the interrupt fires */ + void (*vcmp_thresh_cb)(void); + /* + * Select "all voltage comparator" scan period time. + * The power consumption is positively relative with scan frequency. + */ + enum vcmp_scan_period scan_period; + /* + * Select which ADC channel output voltage into comparator and we + * should set the ADC channel pin in alternate mode via adc_channels[]. + */ + enum chip_adc_channel adc_ch; +}; + /* * Boards must provide this list of ADC channel definitions. This must match * the enum adc_channel list provided by the board. */ extern const struct adc_t adc_channels[]; +#ifdef CONFIG_ADC_VOLTAGE_COMPARATOR +/* + * Boards must provide this list of voltage comparator definitions. + * This must match the enum board_vcmp list provided by the board. + */ +extern const struct vcmp_t vcmp_list[]; +void vcmp_enable(int index, int enable); +#endif + #endif /* __CROS_EC_ADC_CHIP_H */ diff --git a/chip/it83xx/intc.c b/chip/it83xx/intc.c index 1a6e6c74dc..8c1c092ed5 100644 --- a/chip/it83xx/intc.c +++ b/chip/it83xx/intc.c @@ -178,6 +178,11 @@ void intc_cpu_int_group_7(void) case IT83XX_IRQ_ADC: adc_interrupt(); break; +#ifdef CONFIG_ADC_VOLTAGE_COMPARATOR + case IT83XX_IRQ_V_COMP: + voltage_comparator_interrupt(); + break; +#endif #endif default: break; diff --git a/chip/it83xx/intc.h b/chip/it83xx/intc.h index 5a31c41b9c..45447db485 100644 --- a/chip/it83xx/intc.h +++ b/chip/it83xx/intc.h @@ -27,6 +27,9 @@ void pm5_ibf_interrupt(void); void lpcrst_interrupt(enum gpio_signal signal); void peci_interrupt(void); void adc_interrupt(void); +#ifdef CONFIG_ADC_VOLTAGE_COMPARATOR +void voltage_comparator_interrupt(void); +#endif void i2c_interrupt(int port); #ifdef CONFIG_I2C_SLAVE void i2c_slv_interrupt(int port); diff --git a/chip/it83xx/registers.h b/chip/it83xx/registers.h index 3eceda91de..ff2467c606 100644 --- a/chip/it83xx/registers.h +++ b/chip/it83xx/registers.h @@ -1065,6 +1065,11 @@ enum clock_gate_offsets { #define IT83XX_ADC_ADCDVSTS REG8(IT83XX_ADC_BASE+0x44) #define IT83XX_ADC_VCMPSTS REG8(IT83XX_ADC_BASE+0x45) #define IT83XX_ADC_VCMP0CTL REG8(IT83XX_ADC_BASE+0x46) +#define ADC_VCMP_CMPEN BIT(7) +#define ADC_VCMP_CMPINTEN BIT(6) +#define ADC_VCMP_GREATER_THRESHOLD BIT(5) +#define ADC_VCMP_EDGE_TRIGGER BIT(4) +#define ADC_VCMP_GPIO_ACTIVE_LOW BIT(3) #define IT83XX_ADC_CMP0THRDATM REG8(IT83XX_ADC_BASE+0x47) #define IT83XX_ADC_CMP0THRDATL REG8(IT83XX_ADC_BASE+0x48) #define IT83XX_ADC_VCMP1CTL REG8(IT83XX_ADC_BASE+0x49) @@ -1086,6 +1091,23 @@ enum clock_gate_offsets { #define IT83XX_ADC_VCH16DATM REG8(IT83XX_ADC_BASE+0x6A) #define IT83XX_ADC_VCH16DATL REG8(IT83XX_ADC_BASE+0x6B) #define IT83XX_ADC_ADCDVSTS2 REG8(IT83XX_ADC_BASE+0x6C) +#define IT83XX_ADC_VCMPSTS2 REG8(IT83XX_ADC_BASE+0x6D) +#define IT83XX_ADC_VCMP3CTL REG8(IT83XX_ADC_BASE+0x6E) +#define IT83XX_ADC_CMP3THRDATM REG8(IT83XX_ADC_BASE+0x6F) +#define IT83XX_ADC_CMP3THRDATL REG8(IT83XX_ADC_BASE+0x70) +#define IT83XX_ADC_VCMP4CTL REG8(IT83XX_ADC_BASE+0x71) +#define IT83XX_ADC_CMP4THRDATM REG8(IT83XX_ADC_BASE+0x72) +#define IT83XX_ADC_CMP4THRDATL REG8(IT83XX_ADC_BASE+0x73) +#define IT83XX_ADC_VCMP5CTL REG8(IT83XX_ADC_BASE+0x74) +#define IT83XX_ADC_CMP5THRDATM REG8(IT83XX_ADC_BASE+0x75) +#define IT83XX_ADC_CMP5THRDATL REG8(IT83XX_ADC_BASE+0x76) +#define IT83XX_ADC_VCMP0CSELM REG8(IT83XX_ADC_BASE+0x77) +#define ADC_VCMP_VCMPCSELM BIT(0) +#define IT83XX_ADC_VCMP1CSELM REG8(IT83XX_ADC_BASE+0x78) +#define IT83XX_ADC_VCMP2CSELM REG8(IT83XX_ADC_BASE+0x79) +#define IT83XX_ADC_VCMP3CSELM REG8(IT83XX_ADC_BASE+0x7A) +#define IT83XX_ADC_VCMP4CSELM REG8(IT83XX_ADC_BASE+0x7B) +#define IT83XX_ADC_VCMP5CSELM REG8(IT83XX_ADC_BASE+0x7C) /* Digital to Analog Converter (DAC) */ #define IT83XX_DAC_BASE 0x00F01A00 diff --git a/include/config.h b/include/config.h index 969e6aa674..ba48d21b71 100644 --- a/include/config.h +++ b/include/config.h @@ -248,6 +248,9 @@ */ #undef CONFIG_ADC_SAMPLE_TIME +/* Support voltage comparator */ +#undef CONFIG_ADC_VOLTAGE_COMPARATOR + /* Include the ADC analog watchdog feature in the ADC code */ #define CONFIG_ADC_WATCHDOG -- cgit v1.2.1