summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Adolfsson <sadolfsson@google.com>2018-05-06 19:53:26 +0200
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2018-06-19 19:58:35 +0000
commit3d22bbe9cd9efe6063bc17de416d888887a27bf9 (patch)
tree520fa64f31e84e796a5a0ffef92412de79e317fe
parent741d9be8c0f3124bb4382d07e91ae6f15b703e73 (diff)
downloadchrome-ec-3d22bbe9cd9efe6063bc17de416d888887a27bf9.tar.gz
npcx: CEC: Add bus-contention handling
If low-impedance is detected during the time from free-time until the end of the initiator address, the current send is postponed until the bus is free again. Signed-off-by: Stefan Adolfsson <sadolfsson@chromium.org> BUG=b:76467407 BRANCH=none TEST=none CQ-DEPEND=CL:1030224 Reviewed-on: https://chromium-review.googlesource.com/1030225 Reviewed-by: Randall Spangler <rspangler@chromium.org> Change-Id: If4b9ed43306cf2e38770085603f7fa83a1f76ddc Reviewed-on: https://chromium-review.googlesource.com/1055524 Tested-by: Stefan Adolfsson <sadolfsson@chromium.org> Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org> Commit-Queue: Stefan Adolfsson <sadolfsson@chromium.org>
-rw-r--r--chip/npcx/cec.c103
1 files changed, 95 insertions, 8 deletions
diff --git a/chip/npcx/cec.c b/chip/npcx/cec.c
index f1dabcfc70..349fdbc54c 100644
--- a/chip/npcx/cec.c
+++ b/chip/npcx/cec.c
@@ -101,6 +101,12 @@ enum cec_state {
CEC_STATE_INITIATOR_ACK_VERIFY,
};
+/* Edge to trigger capture timer interrupt on */
+enum cap_edge {
+ CAP_EDGE_FALLING,
+ CAP_EDGE_RISING
+};
+
/* CEC message during transfer */
struct cec_msg_transfer {
/* The CEC message */
@@ -141,6 +147,43 @@ static void send_mkbp_event(uint32_t event)
mkbp_send_event(EC_MKBP_EVENT_CEC);
}
+static void tmr_cap_start(enum cap_edge edge, int timeout)
+{
+ int mdl = NPCX_MFT_MODULE_1;
+
+ /* Select edge to trigger capture on */
+ UPDATE_BIT(NPCX_TMCTRL(mdl), NPCX_TMCTRL_TAEDG,
+ edge == CAP_EDGE_RISING);
+
+ /*
+ * Set capture timeout. If we don't have a timeout, we
+ * turn the timeout interrupt off and only care about
+ * the edge change.
+ */
+ if (timeout > 0) {
+ NPCX_TCNT1(mdl) = timeout;
+ SET_BIT(NPCX_TIEN(mdl), NPCX_TIEN_TCIEN);
+ } else {
+ CLEAR_BIT(NPCX_TIEN(mdl), NPCX_TIEN_TCIEN);
+ NPCX_TCNT1(mdl) = 0;
+ }
+
+ /* Clear out old events */
+ SET_BIT(NPCX_TECLR(mdl), NPCX_TECLR_TACLR);
+ SET_BIT(NPCX_TECLR(mdl), NPCX_TECLR_TCCLR);
+ NPCX_TCRA(mdl) = 0;
+ /* Start the capture timer */
+ SET_FIELD(NPCX_TCKC(mdl), NPCX_TCKC_C1CSEL_FIELD, 1);
+}
+
+static void tmr_cap_stop(void)
+{
+ int mdl = NPCX_MFT_MODULE_1;
+
+ CLEAR_BIT(NPCX_TIEN(mdl), NPCX_TIEN_TCIEN);
+ SET_FIELD(NPCX_TCKC(mdl), NPCX_TCKC_C1CSEL_FIELD, 0);
+}
+
static void tmr_oneshot_start(int timeout)
{
int mdl = NPCX_MFT_MODULE_1;
@@ -192,6 +235,7 @@ static int msgt_is_eom(const struct cec_msg_transfer *msgt, int len)
void enter_state(enum cec_state new_state)
{
int gpio = -1, timeout = -1;
+ enum cap_edge cap_edge = -1;
cec_state = new_state;
switch (new_state) {
@@ -203,9 +247,14 @@ void enter_state(enum cec_state new_state)
case CEC_STATE_IDLE:
cec_tx.msgt.bit = 0;
cec_tx.msgt.byte = 0;
+ if (cec_tx.len > 0) {
+ /* Execute a postponed send */
+ enter_state(CEC_STATE_INITIATOR_FREE_TIME);
+ }
break;
case CEC_STATE_INITIATOR_FREE_TIME:
gpio = 1;
+ cap_edge = CAP_EDGE_FALLING;
if (cec_tx.resends)
timeout = FREE_TIME_RS;
else
@@ -219,6 +268,7 @@ void enter_state(enum cec_state new_state)
break;
case CEC_STATE_INITIATOR_START_HIGH:
gpio = 1;
+ cap_edge = CAP_EDGE_FALLING;
timeout = START_BIT_HIGH;
break;
case CEC_STATE_INITIATOR_HEADER_INIT_LOW:
@@ -228,6 +278,10 @@ void enter_state(enum cec_state new_state)
timeout = DATA_LOW(msgt_get_bit(&cec_tx.msgt));
break;
case CEC_STATE_INITIATOR_HEADER_INIT_HIGH:
+ gpio = 1;
+ cap_edge = CAP_EDGE_FALLING;
+ timeout = DATA_HIGH(msgt_get_bit(&cec_tx.msgt));
+ break;
case CEC_STATE_INITIATOR_HEADER_DEST_HIGH:
case CEC_STATE_INITIATOR_DATA_HIGH:
gpio = 1;
@@ -271,8 +325,12 @@ void enter_state(enum cec_state new_state)
if (gpio >= 0)
gpio_set_level(CEC_GPIO_OUT, gpio);
- if (timeout >= 0)
- tmr_oneshot_start(timeout);
+ if (timeout >= 0) {
+ if (cap_edge >= 0)
+ tmr_cap_start(cap_edge, timeout);
+ else
+ tmr_oneshot_start(timeout);
+ }
}
static void cec_event_timeout(void)
@@ -361,6 +419,25 @@ static void cec_event_timeout(void)
}
}
+static void cec_event_cap(void)
+{
+ switch (cec_state) {
+ case CEC_STATE_INITIATOR_FREE_TIME:
+ case CEC_STATE_INITIATOR_START_HIGH:
+ case CEC_STATE_INITIATOR_HEADER_INIT_HIGH:
+ /*
+ * A falling edge during free-time, postpone
+ * this send
+ */
+ cec_tx.msgt.bit = 0;
+ cec_tx.msgt.byte = 0;
+ enter_state(CEC_STATE_IDLE);
+ break;
+ default:
+ break;
+ }
+}
+
static void cec_event_tx(void)
{
enter_state(CEC_STATE_INITIATOR_FREE_TIME);
@@ -374,10 +451,19 @@ static void cec_isr(void)
/* Retrieve events NPCX_TECTRL_TAXND */
events = GET_FIELD(NPCX_TECTRL(mdl), FIELD(0, 4));
- /* Timer event for bit-flipping */
- if (events & (1 << NPCX_TECTRL_TCPND))
- cec_event_timeout();
-
+ if (events & (1 << NPCX_TECTRL_TAPND)) {
+ /* Capture event */
+ cec_event_cap();
+ } else {
+ /*
+ * Capture timeout
+ * We only care about this if the capture event is not
+ * happening, since we will get both events in the
+ * edge-trigger case
+ */
+ if (events & (1 << NPCX_TECTRL_TCPND))
+ cec_event_timeout();
+ }
/* Oneshot timer, a transfer has been initiated from AP */
if (events & (1 << NPCX_TECTRL_TDPND)) {
tmr2_stop();
@@ -446,7 +532,7 @@ static int cec_set_enable(uint8_t enable)
enter_state(CEC_STATE_IDLE);
/* Enable timer interrupts */
- SET_BIT(NPCX_TIEN(mdl), NPCX_TIEN_TCIEN);
+ SET_BIT(NPCX_TIEN(mdl), NPCX_TIEN_TAIEN);
SET_BIT(NPCX_TIEN(mdl), NPCX_TIEN_TDIEN);
/* Enable multifunction timer interrupt */
@@ -455,10 +541,11 @@ static int cec_set_enable(uint8_t enable)
CPRINTF("CEC enabled\n");
} else {
/* Disable timer interrupts */
- CLEAR_BIT(NPCX_TIEN(mdl), NPCX_TIEN_TCIEN);
+ CLEAR_BIT(NPCX_TIEN(mdl), NPCX_TIEN_TAIEN);
CLEAR_BIT(NPCX_TIEN(mdl), NPCX_TIEN_TDIEN);
tmr2_stop();
+ tmr_cap_stop();
task_disable_irq(NPCX_IRQ_MFT_1);