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

Change-Id: If4b9ed43306cf2e38770085603f7fa83a1f76ddc
Reviewed-on: https://chromium-review.googlesource.com/1030225
Commit-Ready: Stefan Adolfsson <sadolfsson@chromium.org>
Tested-by: Stefan Adolfsson <sadolfsson@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
This commit is contained in:
Stefan Adolfsson
2018-05-06 19:53:26 +02:00
committed by chrome-bot
parent e317b78cd3
commit ea5d72e85b

View File

@@ -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);