summaryrefslogtreecommitdiff
path: root/chip/npcx/cec.c
diff options
context:
space:
mode:
authorJack Rosenthal <jrosenth@chromium.org>2021-11-04 12:11:58 -0600
committerCommit Bot <commit-bot@chromium.org>2021-11-05 04:22:34 +0000
commit252457d4b21f46889eebad61d4c0a65331919cec (patch)
tree01856c4d31d710b20e85a74c8d7b5836e35c3b98 /chip/npcx/cec.c
parent08f5a1e6fc2c9467230444ac9b582dcf4d9f0068 (diff)
downloadchrome-ec-stabilize-14333.B-ish.tar.gz
In the interest of making long-term branch maintenance incur as little technical debt on us as possible, we should not maintain any files on the branch we are not actually using. This has the added effect of making it extremely clear when merging CLs from the main branch when changes have the possibility to affect us. The follow-on CL adds a convenience script to actually pull updates from the main branch and generate a CL for the update. BUG=b:204206272 BRANCH=ish TEST=make BOARD=arcada_ish && make BOARD=drallion_ish Signed-off-by: Jack Rosenthal <jrosenth@chromium.org> Change-Id: I17e4694c38219b5a0823e0a3e55a28d1348f4b18 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3262038 Reviewed-by: Jett Rink <jettrink@chromium.org> Reviewed-by: Tom Hughes <tomhughes@chromium.org>
Diffstat (limited to 'chip/npcx/cec.c')
-rw-r--r--chip/npcx/cec.c1040
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);
- }
- }
-}