diff options
Diffstat (limited to 'chip/npcx/cec.c')
-rw-r--r-- | chip/npcx/cec.c | 1040 |
1 files changed, 0 insertions, 1040 deletions
diff --git a/chip/npcx/cec.c b/chip/npcx/cec.c deleted file mode 100644 index ebaefa551a..0000000000 --- a/chip/npcx/cec.c +++ /dev/null @@ -1,1040 +0,0 @@ -/* Copyright 2018 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "atomic.h" -#include "cec.h" -#include "clock_chip.h" -#include "console.h" -#include "ec_commands.h" -#include "fan_chip.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "mkbp_event.h" -#include "registers.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -#if !(DEBUG_CEC) -#define CPRINTF(...) -#define CPRINTS(...) -#else -#define CPRINTF(format, args...) cprintf(CC_CEC, format, ## args) -#define CPRINTS(format, args...) cprints(CC_CEC, format, ## args) -#endif - -/* Time in us to timer clock ticks */ -#define APB1_TICKS(t) ((t) * apb1_freq_div_10k / 100) -#if DEBUG_CEC -/* Timer clock ticks to us */ -#define APB1_US(ticks) (100*(ticks)/apb1_freq_div_10k) -#endif - -/* Notification from interrupt to CEC task that data has been received */ -#define TASK_EVENT_RECEIVED_DATA TASK_EVENT_CUSTOM_BIT(0) - -/* CEC broadcast address. Also the highest possible CEC address */ -#define CEC_BROADCAST_ADDR 15 - -/* Address to indicate that no logical address has been set */ -#define CEC_UNREGISTERED_ADDR 255 - -/* - * The CEC specification requires at least one and a maximum of - * five resends attempts - */ -#define CEC_MAX_RESENDS 5 - -/* - * Free time timing (us). Our free-time is calculated from the end of - * the last bit (not from the start). We compensate by having one - * free-time period less than in the spec. - */ -#define NOMINAL_BIT_TICKS APB1_TICKS(2400) - /* Resend */ -#define FREE_TIME_RS_TICKS (2 * (NOMINAL_BIT_TICKS)) -/* New initiator */ -#define FREE_TIME_NI_TICKS (4 * (NOMINAL_BIT_TICKS)) -/* Present initiator */ -#define FREE_TIME_PI_TICKS (6 * (NOMINAL_BIT_TICKS)) - -/* Start bit timing */ -#define START_BIT_LOW_TICKS APB1_TICKS(3700) -#define START_BIT_MIN_LOW_TICKS APB1_TICKS(3500) -#define START_BIT_MAX_LOW_TICKS APB1_TICKS(3900) -#define START_BIT_HIGH_TICKS APB1_TICKS(800) -#define START_BIT_MIN_DURATION_TICKS APB1_TICKS(4300) -#define START_BIT_MAX_DURATION_TICKS APB1_TICKS(5700) - -/* Data bit timing */ -#define DATA_ZERO_LOW_TICKS APB1_TICKS(1500) -#define DATA_ZERO_MIN_LOW_TICKS APB1_TICKS(1300) -#define DATA_ZERO_MAX_LOW_TICKS APB1_TICKS(1700) -#define DATA_ZERO_HIGH_TICKS APB1_TICKS(900) -#define DATA_ZERO_MIN_DURATION_TICKS APB1_TICKS(2050) -#define DATA_ZERO_MAX_DURATION_TICKS APB1_TICKS(2750) - -#define DATA_ONE_LOW_TICKS APB1_TICKS(600) -#define DATA_ONE_MIN_LOW_TICKS APB1_TICKS(400) -#define DATA_ONE_MAX_LOW_TICKS APB1_TICKS(800) -#define DATA_ONE_HIGH_TICKS APB1_TICKS(1800) -#define DATA_ONE_MIN_DURATION_TICKS APB1_TICKS(2050) -#define DATA_ONE_MAX_DURATION_TICKS APB1_TICKS(2750) - -/* Time from low that it should be safe to sample an ACK */ -#define NOMINAL_SAMPLE_TIME_TICKS APB1_TICKS(1050) - -#define DATA_TIME(type, data) ((data) ? (DATA_ONE_ ## type ## _TICKS) : \ - (DATA_ZERO_ ## type ## _TICKS)) -#define DATA_HIGH(data) DATA_TIME(HIGH, data) -#define DATA_LOW(data) DATA_TIME(LOW, data) - -/* - * Number of short pulses seen before the debounce logic goes into ignoring - * the bus for DEBOUNCE_WAIT_LONG instead of DEBOUNCE_WAIT_SHORT - */ -#define DEBOUNCE_CUTOFF 3 - -/* The limit how short a start-bit can be to trigger debounce logic */ -#define DEBOUNCE_LIMIT_TICKS APB1_TICKS(200) -/* The time we ignore the bus for the first three debounce cases */ -#define DEBOUNCE_WAIT_SHORT_TICKS APB1_TICKS(100) -/* The time we ignore the bus after the three initial debounce cases */ -#define DEBOUNCE_WAIT_LONG_TICKS APB1_TICKS(500) - -/* - * The variance in timing we allow outside of the CEC specification for - * incoming signals. Our measurements aren't 100% accurate either, so this - * gives some robustness. - */ -#define VALID_TOLERANCE_TICKS APB1_TICKS(100) - -/* - * Defines used for setting capture timers to a point where we are - * sure that if we get a timeout, something is wrong. - */ -#define CAP_START_LOW_TICKS (START_BIT_MAX_LOW_TICKS + VALID_TOLERANCE_TICKS) -#define CAP_START_HIGH_TICKS (START_BIT_MAX_DURATION_TICKS - \ - START_BIT_MIN_LOW_TICKS + \ - VALID_TOLERANCE_TICKS) -#define CAP_DATA_LOW_TICKS (DATA_ZERO_MAX_LOW_TICKS + VALID_TOLERANCE_TICKS) -#define CAP_DATA_HIGH_TICKS (DATA_ONE_MAX_DURATION_TICKS - \ - DATA_ONE_MIN_LOW_TICKS + \ - VALID_TOLERANCE_TICKS) - -#define VALID_TIME(type, bit, t) \ - ((t) >= ((bit ## _MIN_ ## type ## _TICKS) - (VALID_TOLERANCE_TICKS)) \ - && (t) <= (bit ##_MAX_ ## type ## _TICKS) + (VALID_TOLERANCE_TICKS)) -#define VALID_LOW(bit, t) VALID_TIME(LOW, bit, t) -#define VALID_HIGH(bit, low_time, high_time) \ - (((low_time) + (high_time) <= \ - bit ## _MAX_DURATION_TICKS + VALID_TOLERANCE_TICKS) && \ - ((low_time) + (high_time) >= \ - bit ## _MIN_DURATION_TICKS - VALID_TOLERANCE_TICKS)) -#define VALID_DATA_HIGH(data, low_time, high_time) ((data) ? \ - VALID_HIGH(DATA_ONE, low_time, high_time) : \ - VALID_HIGH(DATA_ZERO, low_time, high_time)) - -/* - * CEC state machine states. Each state typically takes action on entry and - * timeouts. INITIATIOR states are used for sending, FOLLOWER states are used - * for receiving. - */ -enum cec_state { - CEC_STATE_DISABLED = 0, - CEC_STATE_IDLE, - CEC_STATE_INITIATOR_FREE_TIME, - CEC_STATE_INITIATOR_START_LOW, - CEC_STATE_INITIATOR_START_HIGH, - CEC_STATE_INITIATOR_HEADER_INIT_LOW, - CEC_STATE_INITIATOR_HEADER_INIT_HIGH, - CEC_STATE_INITIATOR_HEADER_DEST_LOW, - CEC_STATE_INITIATOR_HEADER_DEST_HIGH, - CEC_STATE_INITIATOR_DATA_LOW, - CEC_STATE_INITIATOR_DATA_HIGH, - CEC_STATE_INITIATOR_EOM_LOW, - CEC_STATE_INITIATOR_EOM_HIGH, - CEC_STATE_INITIATOR_ACK_LOW, - CEC_STATE_INITIATOR_ACK_HIGH, - CEC_STATE_INITIATOR_ACK_VERIFY, - CEC_STATE_FOLLOWER_START_LOW, - CEC_STATE_FOLLOWER_START_HIGH, - CEC_STATE_FOLLOWER_DEBOUNCE, - CEC_STATE_FOLLOWER_HEADER_INIT_LOW, - CEC_STATE_FOLLOWER_HEADER_INIT_HIGH, - CEC_STATE_FOLLOWER_HEADER_DEST_LOW, - CEC_STATE_FOLLOWER_HEADER_DEST_HIGH, - CEC_STATE_FOLLOWER_EOM_LOW, - CEC_STATE_FOLLOWER_EOM_HIGH, - CEC_STATE_FOLLOWER_ACK_LOW, - CEC_STATE_FOLLOWER_ACK_VERIFY, - CEC_STATE_FOLLOWER_ACK_FINISH, - CEC_STATE_FOLLOWER_DATA_LOW, - CEC_STATE_FOLLOWER_DATA_HIGH, -}; - -/* Edge to trigger capture timer interrupt on */ -enum cap_edge { - CAP_EDGE_FALLING, - CAP_EDGE_RISING -}; - -/* Receive buffer and states */ -struct cec_rx { - /* - * The current incoming message being parsed. Copied to - * receive queue upon completion - */ - struct cec_msg_transfer transfer; - /* End of Message received from source? */ - uint8_t eom; - /* A follower NAK:ed a broadcast transfer */ - uint8_t broadcast_nak; - /* - * Keep track of pulse low time to be able to verify - * pulse duration - */ - int low_ticks; - /* Number of too short pulses seen in a row */ - int debounce_count; -}; - -/* Transfer buffer and states */ -struct cec_tx { - /* Outgoing message */ - struct cec_msg_transfer transfer; - /* Message length */ - uint8_t len; - /* Number of resends attempted in current send */ - uint8_t resends; - /* Acknowledge received from sink? */ - uint8_t ack; - /* - * When sending multiple concurrent frames, - * the free-time is slightly higher - */ - int present_initiator; -}; - -/* Single state for CEC. We are INITIATOR, FOLLOWER or IDLE */ -static enum cec_state cec_state; - -/* Parameters and buffers for follower (receiver) state */ -static struct cec_rx cec_rx; - -/* Queue of completed incoming CEC messages */ -static struct cec_rx_queue cec_rx_queue; - -/* Parameters and buffer for initiator (sender) state */ -static struct cec_tx cec_tx; - -/* - * Time between interrupt triggered and the next timer was - * set when measuring pulse width - */ -static int cap_delay; - -/* Value charged into the capture timer on last capture start */ -static int cap_charge; - -/* - * CEC address of ourself. We ack incoming packages on this address. - * However, the AP is responsible for writing the initiator address - * on writes. UINT32_MAX means means that the address hasn't been - * set by the AP yet. - */ -static uint8_t cec_addr = UINT8_MAX; - -/* Events to send to AP */ -static uint32_t cec_events; - -/* APB1 frequency. Store divided by 10k to avoid some runtime divisions */ -static uint32_t apb1_freq_div_10k; - -static void send_mkbp_event(uint32_t event) -{ - atomic_or(&cec_events, event); - mkbp_send_event(EC_MKBP_EVENT_CEC_EVENT); -} - -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) { - /* - * Store the time it takes from the interrupts starts to when we - * actually get here. This part of the pulse-width needs to be - * taken into account - */ - cap_delay = (0xffff - NPCX_TCNT1(mdl)); - cap_charge = timeout - cap_delay; - NPCX_TCNT1(mdl) = cap_charge; - 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 int tmr_cap_get(void) -{ - int mdl = NPCX_MFT_MODULE_1; - - return (cap_charge + cap_delay - NPCX_TCRA(mdl)); -} - -static void tmr_oneshot_start(int timeout) -{ - int mdl = NPCX_MFT_MODULE_1; - - NPCX_TCNT1(mdl) = timeout; - SET_FIELD(NPCX_TCKC(mdl), NPCX_TCKC_C1CSEL_FIELD, 1); -} - -static void tmr2_start(int timeout) -{ - int mdl = NPCX_MFT_MODULE_1; - - NPCX_TCNT2(mdl) = timeout; - SET_FIELD(NPCX_TCKC(mdl), NPCX_TCKC_C2CSEL_FIELD, 1); -} - -static void tmr2_stop(void) -{ - int mdl = NPCX_MFT_MODULE_1; - - SET_FIELD(NPCX_TCKC(mdl), NPCX_TCKC_C2CSEL_FIELD, 0); -} - -void enter_state(enum cec_state new_state) -{ - int gpio = -1, timeout = -1; - enum cap_edge cap_edge = -1; - uint8_t addr; - - cec_state = new_state; - switch (new_state) { - case CEC_STATE_DISABLED: - gpio = 1; - memset(&cec_rx, 0, sizeof(struct cec_rx)); - memset(&cec_tx, 0, sizeof(struct cec_tx)); - memset(&cec_rx_queue, 0, sizeof(struct cec_rx_queue)); - cap_charge = 0; - cap_delay = 0; - cec_events = 0; - break; - case CEC_STATE_IDLE: - cec_tx.transfer.bit = 0; - cec_tx.transfer.byte = 0; - cec_rx.transfer.bit = 0; - cec_rx.transfer.byte = 0; - if (cec_tx.len > 0) { - /* Execute a postponed send */ - enter_state(CEC_STATE_INITIATOR_FREE_TIME); - } else { - /* Wait for incoming command */ - gpio = 1; - cap_edge = CAP_EDGE_FALLING; - timeout = 0; - } - break; - case CEC_STATE_INITIATOR_FREE_TIME: - gpio = 1; - cap_edge = CAP_EDGE_FALLING; - if (cec_tx.resends) - timeout = FREE_TIME_RS_TICKS; - else if (cec_tx.present_initiator) - timeout = FREE_TIME_PI_TICKS; - else - timeout = FREE_TIME_NI_TICKS; - break; - case CEC_STATE_INITIATOR_START_LOW: - cec_tx.present_initiator = 1; - cec_tx.transfer.bit = 0; - cec_tx.transfer.byte = 0; - gpio = 0; - timeout = START_BIT_LOW_TICKS; - break; - case CEC_STATE_INITIATOR_START_HIGH: - gpio = 1; - cap_edge = CAP_EDGE_FALLING; - timeout = START_BIT_HIGH_TICKS; - break; - case CEC_STATE_INITIATOR_HEADER_INIT_LOW: - case CEC_STATE_INITIATOR_HEADER_DEST_LOW: - case CEC_STATE_INITIATOR_DATA_LOW: - gpio = 0; - timeout = DATA_LOW(cec_transfer_get_bit(&cec_tx.transfer)); - break; - case CEC_STATE_INITIATOR_HEADER_INIT_HIGH: - gpio = 1; - cap_edge = CAP_EDGE_FALLING; - timeout = DATA_HIGH(cec_transfer_get_bit(&cec_tx.transfer)); - break; - case CEC_STATE_INITIATOR_HEADER_DEST_HIGH: - case CEC_STATE_INITIATOR_DATA_HIGH: - gpio = 1; - timeout = DATA_HIGH(cec_transfer_get_bit(&cec_tx.transfer)); - break; - case CEC_STATE_INITIATOR_EOM_LOW: - gpio = 0; - timeout = DATA_LOW(cec_transfer_is_eom(&cec_tx.transfer, - cec_tx.len)); - break; - case CEC_STATE_INITIATOR_EOM_HIGH: - gpio = 1; - timeout = DATA_HIGH(cec_transfer_is_eom(&cec_tx.transfer, - cec_tx.len)); - break; - case CEC_STATE_INITIATOR_ACK_LOW: - gpio = 0; - timeout = DATA_LOW(1); - break; - case CEC_STATE_INITIATOR_ACK_HIGH: - gpio = 1; - /* Aim for the middle of the safe sample time */ - timeout = (DATA_ONE_LOW_TICKS + DATA_ZERO_LOW_TICKS)/2 - - DATA_ONE_LOW_TICKS; - break; - case CEC_STATE_INITIATOR_ACK_VERIFY: - cec_tx.ack = !gpio_get_level(CEC_GPIO_OUT); - if ((cec_tx.transfer.buf[0] & 0x0f) == CEC_BROADCAST_ADDR) { - /* - * We are sending a broadcast. Any follower can - * can NAK a broadcast message the same way they - * would ACK a direct message - */ - cec_tx.ack = !cec_tx.ack; - } - /* - * We are at the safe sample time. Wait - * until the end of this bit - */ - timeout = NOMINAL_BIT_TICKS - NOMINAL_SAMPLE_TIME_TICKS; - break; - case CEC_STATE_FOLLOWER_START_LOW: - cec_tx.present_initiator = 0; - cap_edge = CAP_EDGE_RISING; - timeout = CAP_START_LOW_TICKS; - break; - case CEC_STATE_FOLLOWER_START_HIGH: - cec_rx.debounce_count = 0; - cap_edge = CAP_EDGE_FALLING; - timeout = CAP_START_HIGH_TICKS; - break; - case CEC_STATE_FOLLOWER_DEBOUNCE: - if (cec_rx.debounce_count >= DEBOUNCE_CUTOFF) { - timeout = DEBOUNCE_WAIT_LONG_TICKS; - } else { - timeout = DEBOUNCE_WAIT_SHORT_TICKS; - cec_rx.debounce_count++; - } - break; - case CEC_STATE_FOLLOWER_HEADER_INIT_LOW: - case CEC_STATE_FOLLOWER_HEADER_DEST_LOW: - case CEC_STATE_FOLLOWER_EOM_LOW: - cap_edge = CAP_EDGE_RISING; - timeout = CAP_DATA_LOW_TICKS; - break; - case CEC_STATE_FOLLOWER_HEADER_INIT_HIGH: - case CEC_STATE_FOLLOWER_HEADER_DEST_HIGH: - case CEC_STATE_FOLLOWER_EOM_HIGH: - cap_edge = CAP_EDGE_FALLING; - timeout = CAP_DATA_HIGH_TICKS; - break; - case CEC_STATE_FOLLOWER_ACK_LOW: - addr = cec_rx.transfer.buf[0] & 0x0f; - if (addr == cec_addr) { - /* Destination is our address */ - gpio = 0; - timeout = NOMINAL_SAMPLE_TIME_TICKS; - } else if (addr == CEC_BROADCAST_ADDR) { - /* Don't ack broadcast or packets which destination - * are us, but continue reading - */ - timeout = NOMINAL_SAMPLE_TIME_TICKS; - } - break; - case CEC_STATE_FOLLOWER_ACK_VERIFY: - /* - * We are at safe sample time. A broadcast frame is considered - * lost if any follower pulls the line low - */ - if ((cec_rx.transfer.buf[0] & 0x0f) == CEC_BROADCAST_ADDR) - cec_rx.broadcast_nak = !gpio_get_level(CEC_GPIO_OUT); - else - cec_rx.broadcast_nak = 0; - - /* - * We release the ACK at the end of data zero low - * period (ACK is technically a zero). - */ - timeout = DATA_ZERO_LOW_TICKS - NOMINAL_SAMPLE_TIME_TICKS; - break; - case CEC_STATE_FOLLOWER_ACK_FINISH: - gpio = 1; - if (cec_rx.eom || cec_rx.transfer.byte >= MAX_CEC_MSG_LEN) { - addr = cec_rx.transfer.buf[0] & 0x0f; - if (addr == cec_addr || addr == CEC_BROADCAST_ADDR) { - task_set_event(TASK_ID_CEC, - TASK_EVENT_RECEIVED_DATA); - } - timeout = DATA_ZERO_HIGH_TICKS; - } else { - cap_edge = CAP_EDGE_FALLING; - timeout = CAP_DATA_HIGH_TICKS; - } - break; - case CEC_STATE_FOLLOWER_DATA_LOW: - cap_edge = CAP_EDGE_RISING; - timeout = CAP_DATA_LOW_TICKS; - break; - case CEC_STATE_FOLLOWER_DATA_HIGH: - cap_edge = CAP_EDGE_FALLING; - timeout = CAP_DATA_HIGH_TICKS; - break; - /* No default case, since all states must be handled explicitly */ - } - - if (gpio >= 0) - gpio_set_level(CEC_GPIO_OUT, gpio); - if (timeout >= 0) { - if (cap_edge >= 0) - tmr_cap_start(cap_edge, timeout); - else - tmr_oneshot_start(timeout); - } -} - -static void cec_event_timeout(void) -{ - switch (cec_state) { - case CEC_STATE_DISABLED: - case CEC_STATE_IDLE: - break; - case CEC_STATE_INITIATOR_FREE_TIME: - enter_state(CEC_STATE_INITIATOR_START_LOW); - break; - case CEC_STATE_INITIATOR_START_LOW: - enter_state(CEC_STATE_INITIATOR_START_HIGH); - break; - case CEC_STATE_INITIATOR_START_HIGH: - enter_state(CEC_STATE_INITIATOR_HEADER_INIT_LOW); - break; - case CEC_STATE_INITIATOR_HEADER_INIT_LOW: - enter_state(CEC_STATE_INITIATOR_HEADER_INIT_HIGH); - break; - case CEC_STATE_INITIATOR_HEADER_INIT_HIGH: - cec_transfer_inc_bit(&cec_tx.transfer); - if (cec_tx.transfer.bit == 4) - enter_state(CEC_STATE_INITIATOR_HEADER_DEST_LOW); - else - enter_state(CEC_STATE_INITIATOR_HEADER_INIT_LOW); - break; - case CEC_STATE_INITIATOR_HEADER_DEST_LOW: - enter_state(CEC_STATE_INITIATOR_HEADER_DEST_HIGH); - break; - case CEC_STATE_INITIATOR_HEADER_DEST_HIGH: - cec_transfer_inc_bit(&cec_tx.transfer); - if (cec_tx.transfer.byte == 1) - enter_state(CEC_STATE_INITIATOR_EOM_LOW); - else - enter_state(CEC_STATE_INITIATOR_HEADER_DEST_LOW); - break; - case CEC_STATE_INITIATOR_EOM_LOW: - enter_state(CEC_STATE_INITIATOR_EOM_HIGH); - break; - case CEC_STATE_INITIATOR_EOM_HIGH: - enter_state(CEC_STATE_INITIATOR_ACK_LOW); - break; - case CEC_STATE_INITIATOR_ACK_LOW: - enter_state(CEC_STATE_INITIATOR_ACK_HIGH); - break; - case CEC_STATE_INITIATOR_ACK_HIGH: - enter_state(CEC_STATE_INITIATOR_ACK_VERIFY); - break; - case CEC_STATE_INITIATOR_ACK_VERIFY: - if (cec_tx.ack) { - if (!cec_transfer_is_eom(&cec_tx.transfer, - cec_tx.len)) { - /* More data in this frame */ - enter_state(CEC_STATE_INITIATOR_DATA_LOW); - } else { - /* Transfer completed successfully */ - cec_tx.len = 0; - cec_tx.resends = 0; - enter_state(CEC_STATE_IDLE); - send_mkbp_event(EC_MKBP_CEC_SEND_OK); - } - } else { - if (cec_tx.resends < CEC_MAX_RESENDS) { - /* Resend */ - cec_tx.resends++; - enter_state(CEC_STATE_INITIATOR_FREE_TIME); - } else { - /* Transfer failed */ - cec_tx.len = 0; - cec_tx.resends = 0; - enter_state(CEC_STATE_IDLE); - send_mkbp_event(EC_MKBP_CEC_SEND_FAILED); - } - } - break; - case CEC_STATE_INITIATOR_DATA_LOW: - enter_state(CEC_STATE_INITIATOR_DATA_HIGH); - break; - case CEC_STATE_INITIATOR_DATA_HIGH: - cec_transfer_inc_bit(&cec_tx.transfer); - if (cec_tx.transfer.bit == 0) - enter_state(CEC_STATE_INITIATOR_EOM_LOW); - else - enter_state(CEC_STATE_INITIATOR_DATA_LOW); - break; - case CEC_STATE_FOLLOWER_ACK_LOW: - enter_state(CEC_STATE_FOLLOWER_ACK_VERIFY); - break; - case CEC_STATE_FOLLOWER_ACK_VERIFY: - if (cec_rx.broadcast_nak) - enter_state(CEC_STATE_IDLE); - else - enter_state(CEC_STATE_FOLLOWER_ACK_FINISH); - break; - case CEC_STATE_FOLLOWER_START_LOW: - case CEC_STATE_FOLLOWER_START_HIGH: - case CEC_STATE_FOLLOWER_DEBOUNCE: - case CEC_STATE_FOLLOWER_HEADER_INIT_LOW: - case CEC_STATE_FOLLOWER_HEADER_INIT_HIGH: - case CEC_STATE_FOLLOWER_HEADER_DEST_LOW: - case CEC_STATE_FOLLOWER_HEADER_DEST_HIGH: - case CEC_STATE_FOLLOWER_EOM_LOW: - case CEC_STATE_FOLLOWER_EOM_HIGH: - case CEC_STATE_FOLLOWER_ACK_FINISH: - case CEC_STATE_FOLLOWER_DATA_LOW: - case CEC_STATE_FOLLOWER_DATA_HIGH: - enter_state(CEC_STATE_IDLE); - break; - - } -} - -static void cec_event_cap(void) -{ - int t; - int data; - - switch (cec_state) { - case CEC_STATE_IDLE: - /* A falling edge during idle, likely a start bit */ - enter_state(CEC_STATE_FOLLOWER_START_LOW); - break; - 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 and listen - */ - cec_tx.transfer.bit = 0; - cec_tx.transfer.byte = 0; - enter_state(CEC_STATE_FOLLOWER_START_LOW); - break; - case CEC_STATE_FOLLOWER_START_LOW: - /* Rising edge of start bit, validate low time */ - t = tmr_cap_get(); - if (VALID_LOW(START_BIT, t)) { - cec_rx.low_ticks = t; - enter_state(CEC_STATE_FOLLOWER_START_HIGH); - } else if (t < DEBOUNCE_LIMIT_TICKS) { - /* Wait a bit if start-pulses are really short */ - enter_state(CEC_STATE_FOLLOWER_DEBOUNCE); - } else { - enter_state(CEC_STATE_IDLE); - } - break; - case CEC_STATE_FOLLOWER_START_HIGH: - if (VALID_HIGH(START_BIT, cec_rx.low_ticks, tmr_cap_get())) - enter_state(CEC_STATE_FOLLOWER_HEADER_INIT_LOW); - else - enter_state(CEC_STATE_IDLE); - break; - case CEC_STATE_FOLLOWER_HEADER_INIT_LOW: - case CEC_STATE_FOLLOWER_HEADER_DEST_LOW: - case CEC_STATE_FOLLOWER_DATA_LOW: - t = tmr_cap_get(); - if (VALID_LOW(DATA_ZERO, t)) { - cec_rx.low_ticks = t; - cec_transfer_set_bit(&cec_rx.transfer, 0); - enter_state(cec_state + 1); - } else if (VALID_LOW(DATA_ONE, t)) { - cec_rx.low_ticks = t; - cec_transfer_set_bit(&cec_rx.transfer, 1); - enter_state(cec_state + 1); - } else { - enter_state(CEC_STATE_IDLE); - } - break; - case CEC_STATE_FOLLOWER_HEADER_INIT_HIGH: - t = tmr_cap_get(); - data = cec_transfer_get_bit(&cec_rx.transfer); - if (VALID_DATA_HIGH(data, cec_rx.low_ticks, t)) { - cec_transfer_inc_bit(&cec_rx.transfer); - if (cec_rx.transfer.bit == 4) - enter_state(CEC_STATE_FOLLOWER_HEADER_DEST_LOW); - else - enter_state(CEC_STATE_FOLLOWER_HEADER_INIT_LOW); - } else { - enter_state(CEC_STATE_IDLE); - } - break; - case CEC_STATE_FOLLOWER_HEADER_DEST_HIGH: - t = tmr_cap_get(); - data = cec_transfer_get_bit(&cec_rx.transfer); - if (VALID_DATA_HIGH(data, cec_rx.low_ticks, t)) { - cec_transfer_inc_bit(&cec_rx.transfer); - if (cec_rx.transfer.bit == 0) - enter_state(CEC_STATE_FOLLOWER_EOM_LOW); - else - enter_state(CEC_STATE_FOLLOWER_HEADER_DEST_LOW); - } else { - enter_state(CEC_STATE_IDLE); - } - break; - case CEC_STATE_FOLLOWER_EOM_LOW: - t = tmr_cap_get(); - if (VALID_LOW(DATA_ZERO, t)) { - cec_rx.low_ticks = t; - cec_rx.eom = 0; - enter_state(CEC_STATE_FOLLOWER_EOM_HIGH); - } else if (VALID_LOW(DATA_ONE, t)) { - cec_rx.low_ticks = t; - cec_rx.eom = 1; - enter_state(CEC_STATE_FOLLOWER_EOM_HIGH); - } else { - enter_state(CEC_STATE_IDLE); - } - break; - case CEC_STATE_FOLLOWER_EOM_HIGH: - t = tmr_cap_get(); - data = cec_rx.eom; - if (VALID_DATA_HIGH(data, cec_rx.low_ticks, t)) - enter_state(CEC_STATE_FOLLOWER_ACK_LOW); - else - enter_state(CEC_STATE_IDLE); - break; - case CEC_STATE_FOLLOWER_ACK_LOW: - enter_state(CEC_STATE_FOLLOWER_ACK_FINISH); - break; - case CEC_STATE_FOLLOWER_ACK_FINISH: - enter_state(CEC_STATE_FOLLOWER_DATA_LOW); - break; - case CEC_STATE_FOLLOWER_DATA_HIGH: - t = tmr_cap_get(); - data = cec_transfer_get_bit(&cec_rx.transfer); - if (VALID_DATA_HIGH(data, cec_rx.low_ticks, t)) { - cec_transfer_inc_bit(&cec_rx.transfer); - if (cec_rx.transfer.bit == 0) - enter_state(CEC_STATE_FOLLOWER_EOM_LOW); - else - enter_state(CEC_STATE_FOLLOWER_DATA_LOW); - } else { - enter_state(CEC_STATE_IDLE); - } - break; - default: - break; - } -} - -static void cec_event_tx(void) -{ - /* - * If we have an ongoing receive, this transfer - * will start when transitioning to IDLE - */ - if (cec_state == CEC_STATE_IDLE) - enter_state(CEC_STATE_INITIATOR_FREE_TIME); -} - -void cec_isr(void) -{ - int mdl = NPCX_MFT_MODULE_1; - uint8_t events; - - /* Retrieve events NPCX_TECTRL_TAXND */ - events = GET_FIELD(NPCX_TECTRL(mdl), FIELD(0, 4)); - - if (events & BIT(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 & BIT(NPCX_TECTRL_TCPND)) - cec_event_timeout(); - } - /* Oneshot timer, a transfer has been initiated from AP */ - if (events & BIT(NPCX_TECTRL_TDPND)) { - tmr2_stop(); - cec_event_tx(); - } - - /* Clear handled events */ - SET_FIELD(NPCX_TECLR(mdl), FIELD(0, 4), events); -} -DECLARE_IRQ(NPCX_IRQ_MFT_1, cec_isr, 4); - -static int cec_send(const uint8_t *msg, uint8_t len) -{ - int i; - - if (cec_tx.len != 0) - return -1; - - cec_tx.len = len; - - CPRINTS("Send CEC:"); - for (i = 0; i < len && i < MAX_CEC_MSG_LEN; i++) - CPRINTS(" 0x%02x", msg[i]); - - memcpy(cec_tx.transfer.buf, msg, len); - - /* Elevate to interrupt context */ - tmr2_start(0); - - return 0; -} - -static enum ec_status hc_cec_write(struct host_cmd_handler_args *args) -{ - const struct ec_params_cec_write *params = args->params; - - if (cec_state == CEC_STATE_DISABLED) - return EC_RES_UNAVAILABLE; - - if (args->params_size == 0 || args->params_size > MAX_CEC_MSG_LEN) - return EC_RES_INVALID_PARAM; - - if (cec_send(params->msg, args->params_size) != 0) - return EC_RES_BUSY; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_CEC_WRITE_MSG, hc_cec_write, EC_VER_MASK(0)); - -static int cec_set_enable(uint8_t enable) -{ - int mdl = NPCX_MFT_MODULE_1; - - if (enable != 0 && enable != 1) - return EC_RES_INVALID_PARAM; - - /* Enabling when already enabled? */ - if (enable && cec_state != CEC_STATE_DISABLED) - return EC_RES_SUCCESS; - - /* Disabling when already disabled? */ - if (!enable && cec_state == CEC_STATE_DISABLED) - return EC_RES_SUCCESS; - - if (enable) { - /* Configure GPIO40/TA1 as capture timer input (TA1) */ - CLEAR_BIT(NPCX_DEVALT(0xC), NPCX_DEVALTC_TA1_SL2); - SET_BIT(NPCX_DEVALT(3), NPCX_DEVALT3_TA1_SL1); - - enter_state(CEC_STATE_IDLE); - - /* - * Capture falling edge of first start - * bit to get things going - */ - tmr_cap_start(CAP_EDGE_FALLING, 0); - - /* Enable timer interrupts */ - SET_BIT(NPCX_TIEN(mdl), NPCX_TIEN_TAIEN); - SET_BIT(NPCX_TIEN(mdl), NPCX_TIEN_TDIEN); - - /* Enable multifunction timer interrupt */ - task_enable_irq(NPCX_IRQ_MFT_1); - - CPRINTF("CEC enabled\n"); - } else { - /* Disable timer interrupts */ - 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); - - /* Configure GPIO40/TA1 back to GPIO */ - CLEAR_BIT(NPCX_DEVALT(3), NPCX_DEVALT3_TA1_SL1); - SET_BIT(NPCX_DEVALT(0xC), NPCX_DEVALTC_TA1_SL2); - - enter_state(CEC_STATE_DISABLED); - - CPRINTF("CEC disabled\n"); - } - - return EC_RES_SUCCESS; -} - -static int cec_set_logical_addr(uint8_t logical_addr) -{ - if (logical_addr >= CEC_BROADCAST_ADDR && - logical_addr != CEC_UNREGISTERED_ADDR) - return EC_RES_INVALID_PARAM; - - cec_addr = logical_addr; - CPRINTF("CEC address set to: %u\n", cec_addr); - - return EC_RES_SUCCESS; -} - -static enum ec_status hc_cec_set(struct host_cmd_handler_args *args) -{ - const struct ec_params_cec_set *params = args->params; - - switch (params->cmd) { - case CEC_CMD_ENABLE: - return cec_set_enable(params->val); - case CEC_CMD_LOGICAL_ADDRESS: - return cec_set_logical_addr(params->val); - } - - return EC_RES_INVALID_PARAM; -} -DECLARE_HOST_COMMAND(EC_CMD_CEC_SET, hc_cec_set, EC_VER_MASK(0)); - - -static enum ec_status hc_cec_get(struct host_cmd_handler_args *args) -{ - struct ec_response_cec_get *response = args->response; - const struct ec_params_cec_get *params = args->params; - - switch (params->cmd) { - case CEC_CMD_ENABLE: - response->val = cec_state == CEC_STATE_DISABLED ? 0 : 1; - break; - case CEC_CMD_LOGICAL_ADDRESS: - response->val = cec_addr; - break; - default: - return EC_RES_INVALID_PARAM; - } - - args->response_size = sizeof(*response); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_CEC_GET, hc_cec_get, EC_VER_MASK(0)); - -static int cec_get_next_event(uint8_t *out) -{ - uint32_t event_out = atomic_clear(&cec_events); - - memcpy(out, &event_out, sizeof(event_out)); - - return sizeof(event_out); -} -DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_CEC_EVENT, cec_get_next_event); - -static int cec_get_next_msg(uint8_t *out) -{ - int rv; - uint8_t msg_len, msg[MAX_CEC_MSG_LEN]; - - rv = cec_rx_queue_pop(&cec_rx_queue, msg, &msg_len); - if (rv != 0) - return EC_RES_UNAVAILABLE; - - memcpy(out, msg, msg_len); - - return msg_len; -} -DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_CEC_MESSAGE, cec_get_next_msg); - - -static void cec_init(void) -{ - int mdl = NPCX_MFT_MODULE_1; - - /* APB1 is the clock we base the timers on */ - apb1_freq_div_10k = clock_get_apb1_freq()/10000; - - /* Ensure Multi-Function timer is powered up. */ - CLEAR_BIT(NPCX_PWDWN_CTL(mdl), NPCX_PWDWN_CTL1_MFT1_PD); - - /* Mode 2 - Dual-input capture */ - SET_FIELD(NPCX_TMCTRL(mdl), NPCX_TMCTRL_MDSEL_FIELD, NPCX_MFT_MDSEL_2); - - /* Enable capture TCNT1 into TCRA and preset TCNT1. */ - SET_BIT(NPCX_TMCTRL(mdl), NPCX_TMCTRL_TAEN); - - /* If RO doesn't set it, RW needs to set it explicitly. */ - gpio_set_level(CEC_GPIO_PULL_UP, 1); - - /* Ensure the CEC bus is not pulled low by default on startup. */ - gpio_set_level(CEC_GPIO_OUT, 1); - - CPRINTS("CEC initialized"); -} -DECLARE_HOOK(HOOK_INIT, cec_init, HOOK_PRIO_LAST); - -void cec_task(void *unused) -{ - int rv; - uint32_t events; - - CPRINTF("CEC task starting\n"); - - while (1) { - events = task_wait_event(-1); - if (events & TASK_EVENT_RECEIVED_DATA) { - rv = cec_rx_queue_push(&cec_rx_queue, - cec_rx.transfer.buf, - cec_rx.transfer.byte); - if (rv == EC_ERROR_OVERFLOW) { - /* Queue full, prefer the most recent msg */ - cec_rx_queue_flush(&cec_rx_queue); - rv = cec_rx_queue_push(&cec_rx_queue, - cec_rx.transfer.buf, - cec_rx.transfer.byte); - } - if (rv == EC_SUCCESS) - mkbp_send_event(EC_MKBP_EVENT_CEC_MESSAGE); - } - } -} |