summaryrefslogtreecommitdiff
path: root/chip/npcx
diff options
context:
space:
mode:
authorAseda Aboagye <aaboagye@google.com>2020-01-29 23:19:43 -0800
committerCommit Bot <commit-bot@chromium.org>2020-02-05 08:28:42 +0000
commit5d4a91818b08cee77fef812ea5acf9d31e110f27 (patch)
tree5bd92408f2385c456b83085560f9a969446cca3b /chip/npcx
parentc7313be9af31a4a2cd833b2e2440404aab52c04e (diff)
downloadchrome-ec-5d4a91818b08cee77fef812ea5acf9d31e110f27.tar.gz
npcx: Add support for ADC threshold interrupts
The Nuvoton NPCX EC series support a threshold event detector for the ADC channels. There are three threshold event detectors and they can be mapped to any ADC channel. The block compares the measured voltage input to either a programmable threshold or a pair of thresholds and generates an interrupt if the measured voltage is above or below the thresholds. This commit adds support for this feature. A new structure is created, npcx_adc_thresh_t which allows a board to define the ADC channel that should be monitored, the assertion and deassertion thresholds, a callback that can be run when the interrupt fires and a flag for whether or not the interrupt should fire if the measured value is above or below the threshold. BUG=b:148169171 BRANCH=None TEST=Enable on waddledoo, verify that the ADC dual threshold interrupt functionality works. Change-Id: I5a3e517207a71b1298865fe36b80cc6298567e9e Signed-off-by: Aseda Aboagye <aaboagye@google.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2030204 Tested-by: Aseda Aboagye <aaboagye@chromium.org> Reviewed-by: Diana Z <dzigterman@chromium.org> Commit-Queue: Aseda Aboagye <aaboagye@chromium.org>
Diffstat (limited to 'chip/npcx')
-rw-r--r--chip/npcx/adc.c145
-rw-r--r--chip/npcx/adc_chip.h60
-rw-r--r--chip/npcx/registers.h25
3 files changed, 224 insertions, 6 deletions
diff --git a/chip/npcx/adc.c b/chip/npcx/adc.c
index e1225a8e66..0f67f18542 100644
--- a/chip/npcx/adc.c
+++ b/chip/npcx/adc.c
@@ -20,6 +20,9 @@
#include "timer.h"
#include "util.h"
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args)
+
/* Maximum time we allow for an ADC conversion */
#define ADC_TIMEOUT_US SECOND
#define ADC_CLK 2000000
@@ -103,6 +106,39 @@ static int start_single_and_wait(enum npcx_adc_input_channel input_ch
}
+static uint16_t repetitive_enabled;
+void npcx_set_adc_repetitive(enum npcx_adc_input_channel input_ch, int enable)
+{
+ if (enable) {
+ /* Forbid EC enter deep sleep during conversion. */
+ disable_sleep(SLEEP_MASK_ADC);
+ /* Turn on ADC */
+ SET_BIT(NPCX_ADCCNF, NPCX_ADCCNF_ADCEN);
+ /* Set ADC conversion code to SW conversion mode */
+ SET_FIELD(NPCX_ADCCNF, NPCX_ADCCNF_ADCMD_FIELD,
+ ADC_SCAN_CONVERSION_MODE);
+ /* Update number of channel to be converted */
+ SET_BIT(NPCX_ADCCS, input_ch);
+ /* Set conversion type to repetitive (runs continuously) */
+ SET_BIT(NPCX_ADCCNF, NPCX_ADCCNF_ADCRPTC);
+ repetitive_enabled |= BIT(input_ch);
+
+ /* Start conversion */
+ SET_BIT(NPCX_ADCCNF, NPCX_ADCCNF_START);
+ } else {
+ CLEAR_BIT(NPCX_ADCCNF, NPCX_ADCCNF_ADCRPTC);
+ CLEAR_BIT(NPCX_ADCCS, input_ch);
+ repetitive_enabled &= ~BIT(input_ch);
+
+ if (!repetitive_enabled) {
+ /* Turn off ADC */
+ CLEAR_BIT(NPCX_ADCCNF, NPCX_ADCCNF_ADCEN);
+ /* Allow ec enter deep sleep */
+ enable_sleep(SLEEP_MASK_ADC);
+ }
+ }
+}
+
/**
* ADC read specific channel.
*
@@ -138,16 +174,101 @@ int adc_read_channel(enum adc_channel ch)
value = ADC_READ_ERROR;
}
- /* Turn off ADC */
- CLEAR_BIT(NPCX_ADCCNF, NPCX_ADCCNF_ADCEN);
- /* Allow ec enter deep sleep */
- enable_sleep(SLEEP_MASK_ADC);
+ if (!repetitive_enabled) {
+ /* Turn off ADC */
+ CLEAR_BIT(NPCX_ADCCNF, NPCX_ADCCNF_ADCEN);
+ /* Allow ec enter deep sleep */
+ enable_sleep(SLEEP_MASK_ADC);
+ }
mutex_unlock(&adc_lock);
return value;
}
+/* Board should register these callbacks with npcx_adc_cfg_thresh_int(). */
+static void (*adc_thresh_irqs[NPCX_ADC_THRESH_CNT])(void);
+
+void npcx_adc_thresh_int_enable(int threshold_idx, int enable)
+{
+ enable = !!enable;
+
+ if ((threshold_idx < 1) || (threshold_idx > 3)) {
+ CPRINTS("Invalid ADC thresh index! (%d)",
+ threshold_idx);
+ return;
+ }
+ threshold_idx--; /* convert to 0-based */
+
+ if (enable)
+ SET_BIT(NPCX_THRCTS, NPCX_THRCTS_THR1_IEN+threshold_idx);
+ else
+ CLEAR_BIT(NPCX_THRCTS, NPCX_THRCTS_THR1_IEN+threshold_idx);
+}
+
+void npcx_adc_register_thresh_irq(int threshold_idx,
+ const struct npcx_adc_thresh_t *thresh_cfg)
+{
+ int npcx_adc_ch;
+ int raw_val;
+ int mul;
+ int div;
+ int shift;
+
+ if ((threshold_idx < 1) || (threshold_idx > 3)) {
+ CPRINTS("Invalid ADC thresh index! (%d)",
+ threshold_idx);
+ return;
+ }
+ npcx_adc_ch = adc_channels[thresh_cfg->adc_ch].input_ch;
+
+ if (!thresh_cfg->adc_thresh_cb) {
+ CPRINTS("No callback for ADC Threshold %d!",
+ threshold_idx);
+ return;
+ }
+
+ /* Fill in the table */
+ adc_thresh_irqs[threshold_idx-1] = thresh_cfg->adc_thresh_cb;
+
+ /* Select the channel */
+ SET_FIELD(NPCX_THRCTL(threshold_idx), NPCX_THRCTL_CHNSEL,
+ npcx_adc_ch);
+
+ if (thresh_cfg->lower_or_higher)
+ SET_BIT(NPCX_THRCTL(threshold_idx), NPCX_THRCTL_L_H);
+ else
+ CLEAR_BIT(NPCX_THRCTL(threshold_idx), NPCX_THRCTL_L_H);
+
+ /*
+ * Set the single threshold value. This is also the assertion threshold
+ * value if dual threshold configuration is being used.
+ */
+ mul = adc_channels[thresh_cfg->adc_ch].factor_mul;
+ div = adc_channels[thresh_cfg->adc_ch].factor_div;
+ shift = adc_channels[thresh_cfg->adc_ch].shift;
+
+ raw_val = (thresh_cfg->thresh_assert - shift) * div / mul;
+ CPRINTS("ADC THR%d: Setting THRVAL = %d, L_H: %d", threshold_idx,
+ raw_val, thresh_cfg->lower_or_higher);
+ SET_FIELD(NPCX_THRCTL(threshold_idx), NPCX_THRCTL_THRVAL,
+ raw_val);
+
+ /* Configure dual threshold detection if desired. */
+ if (thresh_cfg->thresh_deassert != -1) {
+ raw_val = (thresh_cfg->thresh_deassert - shift) * div / mul;
+ SET_FIELD(NPCX_THR_DCTL(threshold_idx),
+ NPCX_THR_DCTL_THR_DVAL,
+ raw_val);
+ SET_BIT(NPCX_THR_DCTL(threshold_idx), NPCX_THR_DCTL_THRD_EN);
+ } else {
+ CLEAR_BIT(NPCX_THR_DCTL(threshold_idx), NPCX_THR_DCTL_THRD_EN);
+ }
+
+ /* Enable threshold detection */
+ SET_BIT(NPCX_THRCTL(threshold_idx), NPCX_THRCTL_THEN);
+}
+
/**
* ADC interrupt handler
*
@@ -157,12 +278,15 @@ int adc_read_channel(enum adc_channel ch)
*/
void adc_interrupt(void)
{
+ int i;
+
if (IS_BIT_SET(NPCX_ADCSTS, NPCX_ADCSTS_EOCEV)) {
/* Disable End-of-Conversion Interrupt */
CLEAR_BIT(NPCX_ADCCNF, NPCX_ADCCNF_INTECEN);
- /* Stop conversion */
- SET_BIT(NPCX_ADCCNF, NPCX_ADCCNF_STOP);
+ /* Stop conversion for single-shot mode */
+ if (!repetitive_enabled)
+ SET_BIT(NPCX_ADCCNF, NPCX_ADCCNF_STOP);
/* Clear End-of-Conversion Event status */
SET_BIT(NPCX_ADCSTS, NPCX_ADCSTS_EOCEV);
@@ -171,6 +295,15 @@ void adc_interrupt(void)
if (task_waiting != TASK_ID_INVALID)
task_set_event(task_waiting, TASK_EVENT_ADC_DONE, 0);
}
+
+ for (i = NPCX_THRCTS_THR1_STS; i <= NPCX_THRCTS_THR3_STS; i++) {
+ if (IS_BIT_SET(NPCX_THRCTS, i)) {
+ /* Clear threshold status */
+ SET_BIT(NPCX_THRCTS, i);
+ if (adc_thresh_irqs[i])
+ adc_thresh_irqs[i]();
+ }
+ }
}
DECLARE_IRQ(NPCX_IRQ_ADC, adc_interrupt, 4);
diff --git a/chip/npcx/adc_chip.h b/chip/npcx/adc_chip.h
index 16c431d779..74f18c66d6 100644
--- a/chip/npcx/adc_chip.h
+++ b/chip/npcx/adc_chip.h
@@ -8,6 +8,8 @@
#ifndef __CROS_EC_ADC_CHIP_H
#define __CROS_EC_ADC_CHIP_H
+#include "adc.h"
+
/* Minimum and maximum values returned by raw ADC read. */
#define ADC_READ_MIN 0
#define ADC_READ_MAX 1023
@@ -45,4 +47,62 @@ struct adc_t {
*/
extern const struct adc_t adc_channels[];
+/*
+ * Boards may configure a ADC channel for use with thershold interrupts. For
+ * dual threshold detection, set both the thresh_assert and thresh_deassert
+ * member variables. The threshold levels may be set from 0 to ADC_MAX_VOLT
+ * inclusive.
+ */
+struct npcx_adc_thresh_t {
+ /* The ADC channel to monitor to generate threshold interrupts. */
+ enum adc_channel adc_ch;
+
+ /* Called when the interrupt fires */
+ void (*adc_thresh_cb)(void);
+
+ /* If set, threshold event is asserted when <= threshold level */
+ int lower_or_higher;
+
+ /*
+ * Desired level in mV to assert. Set for both single and dual
+ * thresholds.
+ */
+ int thresh_assert;
+
+ /* Desired level in mV to de-assert. Set to -1 for single thresholds. */
+ int thresh_deassert;
+};
+
+/**
+ * Boards should call this function to register their threshold interrupt with
+ * one of the 3 threshold detectors. threshold_idx is 1-based.
+ *
+ * @param threshold_idx - 1-based threshold detector index (max 3)
+ * @param thresh_cfg - Pointer to ADC threshold interrupt configuration
+ */
+void npcx_adc_register_thresh_irq(int threshold_idx,
+ const struct npcx_adc_thresh_t *thresh_cfg);
+
+/**
+ * Configure an ADC channel for repetitive conversion.
+ *
+ * If you are using ADC threshold interrupts and the need is timing critical,
+ * you will want to enable this on the ADC channels you have configured for
+ * threshold interrupts.
+ *
+ * NOTE: Enabling this will prevent the EC from entering deep sleep and will
+ * increase power consumption!
+ *
+ * @param input_ch - The ADC channel you wish to configure
+ * @param enable - 1 to enable, 0 to disable
+ */
+void npcx_set_adc_repetitive(enum npcx_adc_input_channel input_ch, int enable);
+
+/**
+ * Enable/Disable ADC threshold detector interrupt.
+ *
+ * @param threshold_idx - 1-based threshold detector index
+ * @param enable - 1 to enable, 0 to disable
+ */
+void npcx_adc_thresh_int_enable(int threshold_idx, int enable);
#endif /* __CROS_EC_ADC_CHIP_H */
diff --git a/chip/npcx/registers.h b/chip/npcx/registers.h
index 5065fdf6a7..8e94f1f125 100644
--- a/chip/npcx/registers.h
+++ b/chip/npcx/registers.h
@@ -1318,6 +1318,11 @@ enum PM_CHANNEL_T {
#define NPCX_ATCTL REG16(NPCX_ADC_BASE_ADDR + 0x004)
#define NPCX_ASCADD REG16(NPCX_ADC_BASE_ADDR + 0x006)
#define NPCX_ADCCS REG16(NPCX_ADC_BASE_ADDR + 0x008)
+/* NOTE: These are 1-based for the threshold detectors. */
+#define NPCX_THRCTL(n) REG16(NPCX_ADC_BASE_ADDR + 0x012 + (2L*(n)))
+#define NPCX_THRCTS REG16(NPCX_ADC_BASE_ADDR + 0x01A)
+#define NPCX_THR_DCTL(n) REG16(NPCX_ADC_BASE_ADDR + 0x038 + (2L*(n)))
+/* NOTE: This is 0-based for the ADC channels. */
#define NPCX_CHNDAT(n) REG16(NPCX_ADC_BASE_ADDR + 0x040 + (2L*(n)))
#define NPCX_ADCCNF2 REG16(NPCX_ADC_BASE_ADDR + 0x020)
#define NPCX_GENDLY REG16(NPCX_ADC_BASE_ADDR + 0x022)
@@ -1336,6 +1341,26 @@ enum PM_CHANNEL_T {
#define NPCX_ADCCNF_STOP 11
#define NPCX_CHNDAT_CHDAT_FIELD FIELD(0, 10)
#define NPCX_CHNDAT_NEW 15
+#define NPCX_THRCTL_THEN 15
+#define NPCX_THRCTL_L_H 14
+#define NPCX_THRCTL_CHNSEL FIELD(10, 4)
+#define NPCX_THRCTL_THRVAL FIELD(0, 10)
+#define NPCX_THRCTS_ADC_WKEN 15
+#define NPCX_THRCTS_THR3_IEN 10
+#define NPCX_THRCTS_THR2_IEN 9
+#define NPCX_THRCTS_THR1_IEN 8
+#define NPCX_THRCTS_ADC_EVENT 7
+#define NPCX_THRCTS_THR3_STS 2
+#define NPCX_THRCTS_THR2_STS 1
+#define NPCX_THRCTS_THR1_STS 0
+#define NPCX_THR_DCTL_THRD_EN 15
+#define NPCX_THR_DCTL_THR_DVAL FIELD(0, 10)
+
+#define NPCX_ADC_THRESH1 1
+#define NPCX_ADC_THRESH2 2
+#define NPCX_ADC_THRESH3 3
+#define NPCX_ADC_THRESH_CNT 3
+
/******************************************************************************/
/* SPI Register */
#define NPCX_SPI_DATA REG16(NPCX_SPI_BASE_ADDR + 0x00)