summaryrefslogtreecommitdiff
path: root/driver/touchpad_st.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 /driver/touchpad_st.c
parent08f5a1e6fc2c9467230444ac9b582dcf4d9f0068 (diff)
downloadchrome-ec-stabilize-14695.107.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 'driver/touchpad_st.c')
-rw-r--r--driver/touchpad_st.c1895
1 files changed, 0 insertions, 1895 deletions
diff --git a/driver/touchpad_st.c b/driver/touchpad_st.c
deleted file mode 100644
index 56633bad16..0000000000
--- a/driver/touchpad_st.c
+++ /dev/null
@@ -1,1895 +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 "board.h"
-#include "common.h"
-#include "console.h"
-#include "gpio.h"
-#include "hwtimer.h"
-#include "hooks.h"
-#include "i2c.h"
-#include "registers.h"
-#include "spi.h"
-#include "task.h"
-#include "tablet_mode.h"
-#include "timer.h"
-#include "touchpad.h"
-#include "touchpad_st.h"
-#include "update_fw.h"
-#include "usb_api.h"
-#include "usb_hid_touchpad.h"
-#include "usb_isochronous.h"
-#include "util.h"
-#include "watchdog.h"
-
-/* Console output macros */
-#define CC_TOUCHPAD CC_USB
-#define CPUTS(outstr) cputs(CC_TOUCHPAD, outstr)
-#define CPRINTF(format, args...) cprintf(CC_TOUCHPAD, format, ## args)
-#define CPRINTS(format, args...) cprints(CC_TOUCHPAD, format, ## args)
-
-#define TASK_EVENT_POWER TASK_EVENT_CUSTOM_BIT(0)
-#define TASK_EVENT_TP_UPDATED TASK_EVENT_CUSTOM_BIT(1)
-
-#define SPI (&(spi_devices[SPI_ST_TP_DEVICE_ID]))
-
-BUILD_ASSERT(sizeof(struct st_tp_event_t) == 8);
-BUILD_ASSERT(BYTES_PER_PIXEL == 1);
-
-/* Function prototypes */
-static int st_tp_panel_init(int full);
-static int st_tp_read_all_events(int show_error);
-static int st_tp_read_host_buffer_header(void);
-static int st_tp_send_ack(void);
-static int st_tp_start_scan(void);
-static int st_tp_stop_scan(void);
-static int st_tp_update_system_state(int new_state, int mask);
-static void touchpad_power_control(void);
-
-/* Global variables */
-/*
- * Current system state, meaning of each bit is defined below.
- */
-static int system_state;
-
-#define SYSTEM_STATE_DEBUG_MODE BIT(0)
-#define SYSTEM_STATE_ENABLE_HEAT_MAP BIT(1)
-#define SYSTEM_STATE_ENABLE_DOME_SWITCH BIT(2)
-#define SYSTEM_STATE_ACTIVE_MODE BIT(3)
-#define SYSTEM_STATE_DOME_SWITCH_LEVEL BIT(4)
-#define SYSTEM_STATE_READY BIT(5)
-
-/*
- * Pending action for touchpad.
- */
-static int tp_control;
-
-#define TP_CONTROL_SHALL_HALT BIT(0)
-#define TP_CONTROL_SHALL_RESET BIT(1)
-#define TP_CONTROL_SHALL_INIT BIT(2)
-#define TP_CONTROL_SHALL_INIT_FULL BIT(3)
-#define TP_CONTROL_SHALL_DUMP_ERROR BIT(4)
-#define TP_CONTROL_RESETTING BIT(5)
-#define TP_CONTROL_INIT BIT(6)
-#define TP_CONTROL_INIT_FULL BIT(7)
-
-/*
- * Number of times we have reset the touchpad because of errors.
- */
-static int tp_reset_retry_count;
-
-#define MAX_TP_RESET_RETRY_COUNT 3
-
-static int dump_memory_on_error;
-
-/*
- * Bitmap to track if a finger exists.
- */
-static int touch_slot;
-
-/*
- * Timestamp of last interrupt (32 bits are enough as we divide the value by 100
- * and then put it in a 16-bit field).
- */
-static uint32_t irq_ts;
-
-/*
- * Cached system info.
- */
-static struct st_tp_system_info_t system_info;
-
-static struct {
-#if ST_TP_EXTRA_BYTE == 1
- uint8_t extra_byte;
-#endif
- union {
- uint8_t bytes[512];
- struct st_tp_host_buffer_header_t buffer_header;
- struct st_tp_host_buffer_heat_map_t heat_map;
- struct st_tp_host_data_header_t data_header;
- struct st_tp_event_t events[32];
- uint32_t dump_info[32];
- } /* anonymous */;
-} __packed rx_buf;
-
-
-#ifdef CONFIG_USB_ISOCHRONOUS
-#define USB_ISO_PACKET_SIZE 256
-/*
- * Header of each USB pacaket.
- */
-struct packet_header_t {
- uint8_t index;
-
-#define HEADER_FLAGS_NEW_FRAME BIT(0)
- uint8_t flags;
-} __packed;
-BUILD_ASSERT(sizeof(struct packet_header_t) < USB_ISO_PACKET_SIZE);
-
-static struct packet_header_t packet_header;
-
-/* What will be sent to USB interface. */
-struct st_tp_usb_packet_t {
-#define USB_FRAME_FLAGS_BUTTON BIT(0)
- /*
- * This will be true if user clicked on touchpad.
- * TODO(b/70482333): add corresponding code for button signal.
- */
- uint8_t flags;
-
- /*
- * This will be `st_tp_host_buffer_heat_map_t.frame` but each pixel
- * will be scaled to 8 bits value.
- */
- uint8_t frame[ST_TOUCH_ROWS * ST_TOUCH_COLS];
-} __packed;
-
-/* Next buffer index SPI will write to. */
-static volatile uint32_t spi_buffer_index;
-/* Next buffer index USB will read from. */
-static volatile uint32_t usb_buffer_index;
-static struct st_tp_usb_packet_t usb_packet[2]; /* double buffering */
-/* How many bytes we have transmitted. */
-static size_t transmit_report_offset;
-
-/* Function prototypes */
-static int get_heat_map_addr(void);
-static void print_frame(void);
-static void st_tp_disable_heat_map(void);
-static void st_tp_enable_heat_map(void);
-static int st_tp_read_frame(void);
-static void st_tp_interrupt_send(void);
-DECLARE_DEFERRED(st_tp_interrupt_send);
-#endif
-
-
-/* Function implementations */
-
-static void set_bits(int *lvalue, int rvalue, int mask)
-{
- *lvalue &= ~mask;
- *lvalue |= rvalue & mask;
-}
-
-/*
- * Parse a finger report from ST event and save it to (report)->finger.
- *
- * @param report: pointer to a USB HID touchpad report.
- * @param event: a pointer event from ST.
- * @param i: array index for next finger.
- *
- * @return array index of next finger (i.e. (i + 1) if a finger is added).
- */
-static int st_tp_parse_finger(struct usb_hid_touchpad_report *report,
- struct st_tp_event_t *event,
- int i)
-{
- const int id = event->finger.touch_id;
-
- /* This is not a finger */
- if (event->finger.touch_type == ST_TP_TOUCH_TYPE_INVALID)
- return i;
-
- if (event->evt_id == ST_TP_EVENT_ID_ENTER_POINTER)
- touch_slot |= 1 << id;
- else if (event->evt_id == ST_TP_EVENT_ID_LEAVE_POINTER)
- touch_slot &= ~BIT(id);
-
- /* We cannot report more fingers */
- if (i >= ARRAY_SIZE(report->finger)) {
- CPRINTS("WARN: ST reports more than %d fingers", i);
- return i;
- }
-
- switch (event->evt_id) {
- case ST_TP_EVENT_ID_ENTER_POINTER:
- case ST_TP_EVENT_ID_MOTION_POINTER:
- /* Pressure == 255 is a palm. */
- report->finger[i].confidence = (event->finger.z < 255);
- report->finger[i].tip = 1;
- report->finger[i].inrange = 1;
- report->finger[i].id = id;
- report->finger[i].pressure = event->finger.z;
- report->finger[i].width = (event->finger.minor |
- (event->minor_high << 4)) << 5;
- report->finger[i].height = (event->finger.major |
- (event->major_high << 4)) << 5;
-
- report->finger[i].x = (CONFIG_USB_HID_TOUCHPAD_LOGICAL_MAX_X -
- event->finger.x);
- report->finger[i].y = (CONFIG_USB_HID_TOUCHPAD_LOGICAL_MAX_Y -
- event->finger.y);
- break;
- case ST_TP_EVENT_ID_LEAVE_POINTER:
- report->finger[i].id = id;
- /* When a finger is leaving, it's not a palm */
- report->finger[i].confidence = 1;
- break;
- }
- return i + 1;
-}
-
-/*
- * Read domeswitch level from touchpad, and save in `system_state`.
- *
- * After calling this function, use
- * `system_state & SYSTEM_STATE_DOME_SWITCH_LEVEL`
- * to get current value.
- *
- * @return error code on failure.
- */
-static int st_tp_check_domeswitch_state(void)
-{
- int ret = st_tp_read_host_buffer_header();
-
- if (ret)
- return ret;
-
- ret = rx_buf.buffer_header.flags & ST_TP_BUFFER_HEADER_DOMESWITCH_LVL;
- /*
- * Domeswitch level from device is inverted.
- * That is, 0 => pressed, 1 => released.
- */
- set_bits(&system_state,
- ret ? 0 : SYSTEM_STATE_DOME_SWITCH_LEVEL,
- SYSTEM_STATE_DOME_SWITCH_LEVEL);
- return 0;
-}
-
-static int st_tp_write_hid_report(void)
-{
- int ret, i, num_finger, num_events;
- const int old_system_state = system_state;
- int domeswitch_changed;
- struct usb_hid_touchpad_report report;
-
- ret = st_tp_check_domeswitch_state();
- if (ret)
- return ret;
-
- domeswitch_changed = ((old_system_state ^ system_state) &
- SYSTEM_STATE_DOME_SWITCH_LEVEL);
-
- num_events = st_tp_read_all_events(1);
- if (tp_control)
- return 1;
-
- memset(&report, 0, sizeof(report));
- report.id = REPORT_ID_TOUCHPAD;
- num_finger = 0;
-
- for (i = 0; i < num_events; i++) {
- struct st_tp_event_t *e = &rx_buf.events[i];
-
- switch (e->evt_id) {
- case ST_TP_EVENT_ID_ENTER_POINTER:
- case ST_TP_EVENT_ID_MOTION_POINTER:
- case ST_TP_EVENT_ID_LEAVE_POINTER:
- num_finger = st_tp_parse_finger(&report, e, num_finger);
- break;
- default:
- break;
- }
- }
-
- if (!num_finger && !domeswitch_changed) /* nothing changed */
- return 0;
-
- /* Don't report 0 finger click. */
- if (num_finger && (system_state & SYSTEM_STATE_DOME_SWITCH_LEVEL))
- report.button = 1;
- report.count = num_finger;
- report.timestamp = irq_ts / USB_HID_TOUCHPAD_TIMESTAMP_UNIT;
-
- set_touchpad_report(&report);
- return 0;
-}
-
-static int st_tp_read_report(void)
-{
- if (system_state & SYSTEM_STATE_ENABLE_HEAT_MAP) {
-#ifdef CONFIG_USB_ISOCHRONOUS
- /*
- * Because we are using double buffering, so, if
- * usb_buffer_index = N
- *
- * 1. spi_buffer_index == N => ok, both slots are empty
- * 2. spi_buffer_index == N + 1 => ok, second slot is empty
- * 3. spi_buffer_index == N + 2 => not ok, need to wait for USB
- */
- if (spi_buffer_index - usb_buffer_index <= 1) {
- if (st_tp_read_frame() == EC_SUCCESS) {
- spi_buffer_index++;
- if (system_state & SYSTEM_STATE_DEBUG_MODE) {
- print_frame();
- usb_buffer_index++;
- }
- }
- }
- if (spi_buffer_index > usb_buffer_index)
- hook_call_deferred(&st_tp_interrupt_send_data, 0);
-#endif
- } else {
- st_tp_write_hid_report();
- }
- return st_tp_send_ack();
-}
-
-static int st_tp_read_host_buffer_header(void)
-{
- const uint8_t tx_buf[] = { ST_TP_CMD_READ_SPI_HOST_BUFFER, 0x00, 0x00 };
- int rx_len = ST_TP_EXTRA_BYTE + sizeof(rx_buf.buffer_header);
-
- return spi_transaction(SPI, tx_buf, sizeof(tx_buf),
- (uint8_t *)&rx_buf, rx_len);
-}
-
-static int st_tp_send_ack(void)
-{
- uint8_t tx_buf[] = { ST_TP_CMD_SPI_HOST_BUFFER_ACK };
-
- return spi_transaction(SPI, tx_buf, sizeof(tx_buf), NULL, 0);
-}
-
-static int st_tp_update_system_state(int new_state, int mask)
-{
- int ret = EC_SUCCESS;
- int need_locked_scan_mode = 0;
-
- /* copy reserved bits */
- set_bits(&new_state, system_state, ~mask);
-
- mask = SYSTEM_STATE_DEBUG_MODE;
- if ((new_state & mask) != (system_state & mask))
- set_bits(&system_state, new_state, mask);
-
- mask = SYSTEM_STATE_ENABLE_HEAT_MAP | SYSTEM_STATE_ENABLE_DOME_SWITCH;
- if ((new_state & mask) != (system_state & mask)) {
- uint8_t tx_buf[] = {
- ST_TP_CMD_WRITE_FEATURE_SELECT,
- 0x05,
- 0
- };
- if (new_state & SYSTEM_STATE_ENABLE_HEAT_MAP) {
- CPRINTS("Heatmap enabled");
- tx_buf[2] |= BIT(0);
- need_locked_scan_mode = 1;
- } else {
- CPRINTS("Heatmap disabled");
- }
-
- if (new_state & SYSTEM_STATE_ENABLE_DOME_SWITCH)
- tx_buf[2] |= BIT(1);
- ret = spi_transaction(SPI, tx_buf, sizeof(tx_buf), NULL, 0);
- if (ret)
- return ret;
- set_bits(&system_state, new_state, mask);
- }
-
- mask = SYSTEM_STATE_ACTIVE_MODE;
- if ((new_state & mask) != (system_state & mask)) {
- uint8_t tx_buf[] = {
- ST_TP_CMD_WRITE_SCAN_MODE_SELECT,
- ST_TP_SCAN_MODE_ACTIVE,
- !!(new_state & SYSTEM_STATE_ACTIVE_MODE),
- };
- CPRINTS("Enable Multi-Touch: %d", tx_buf[2]);
- ret = spi_transaction(SPI, tx_buf, sizeof(tx_buf), NULL, 0);
- if (ret)
- return ret;
- set_bits(&system_state, new_state, mask);
- }
-
- /*
- * We need to lock scan mode to prevent scan rate drop when heat map
- * mode is enabled.
- */
- if (need_locked_scan_mode) {
- uint8_t tx_buf[] = {
- ST_TP_CMD_WRITE_SCAN_MODE_SELECT,
- ST_TP_SCAN_MODE_LOCKED,
- 0x0,
- };
-
- ret = spi_transaction(SPI, tx_buf, sizeof(tx_buf), NULL, 0);
- if (ret)
- return ret;
- }
- return ret;
-}
-
-static void st_tp_enable_interrupt(int enable)
-{
- uint8_t tx_buf[] = {
- ST_TP_CMD_WRITE_SYSTEM_COMMAND, 0x01, enable ? 1 : 0};
- if (enable)
- gpio_enable_interrupt(GPIO_TOUCHPAD_INT);
- spi_transaction(SPI, tx_buf, sizeof(tx_buf), NULL, 0);
- if (!enable)
- gpio_disable_interrupt(GPIO_TOUCHPAD_INT);
-}
-
-static int st_tp_start_scan(void)
-{
- int new_state = (SYSTEM_STATE_ACTIVE_MODE |
- SYSTEM_STATE_ENABLE_DOME_SWITCH);
- int mask = new_state;
- int ret;
-
- CPRINTS("ST: Start scanning");
- ret = st_tp_update_system_state(new_state, mask);
- if (ret)
- return ret;
- st_tp_send_ack();
- st_tp_enable_interrupt(1);
-
- return ret;
-}
-
-static int st_tp_read_host_data_memory(uint16_t addr, void *rx_buf, int len)
-{
- uint8_t tx_buf[] = {
- ST_TP_CMD_READ_HOST_DATA_MEMORY, addr >> 8, addr & 0xFF
- };
-
- return spi_transaction(SPI, tx_buf, sizeof(tx_buf), rx_buf, len);
-}
-
-static int st_tp_stop_scan(void)
-{
- int new_state = 0;
- int mask = SYSTEM_STATE_ACTIVE_MODE;
- int ret;
-
- CPRINTS("ST: Stop scanning");
- ret = st_tp_update_system_state(new_state, mask);
- st_tp_enable_interrupt(0);
-
- return ret;
-}
-
-static int st_tp_load_host_data(uint8_t mem_id)
-{
- uint8_t tx_buf[] = {
- ST_TP_CMD_WRITE_SYSTEM_COMMAND, 0x06, mem_id
- };
- int retry, ret;
- uint16_t count;
- struct st_tp_host_data_header_t *header = &rx_buf.data_header;
- int rx_len = sizeof(*header) + ST_TP_EXTRA_BYTE;
-
- st_tp_read_host_data_memory(0x0000, &rx_buf, rx_len);
- if (header->host_data_mem_id == mem_id)
- return EC_SUCCESS; /* already loaded no need to reload */
-
- count = header->count;
-
- ret = spi_transaction(SPI, tx_buf, sizeof(tx_buf), NULL, 0);
- if (ret)
- return ret;
-
- ret = EC_ERROR_TIMEOUT;
- retry = 5;
- while (retry--) {
- st_tp_read_host_data_memory(0x0000, &rx_buf, rx_len);
- if (header->magic == ST_TP_HEADER_MAGIC &&
- header->host_data_mem_id == mem_id &&
- header->count != count) {
- ret = EC_SUCCESS;
- break;
- }
- msleep(10);
- }
- return ret;
-}
-
-/*
- * Read System Info from Host Data Memory.
- *
- * @param reload: true to force reloading system info into host data memory
- * before reading.
- */
-static int st_tp_read_system_info(int reload)
-{
- int ret = EC_SUCCESS;
- int rx_len = ST_TP_EXTRA_BYTE + ST_TP_SYSTEM_INFO_LEN;
- uint8_t *ptr = rx_buf.bytes;
-
- if (reload)
- ret = st_tp_load_host_data(ST_TP_MEM_ID_SYSTEM_INFO);
- if (ret)
- return ret;
- ret = st_tp_read_host_data_memory(0x0000, &rx_buf, rx_len);
- if (ret)
- return ret;
-
- /* Parse the content */
- memcpy(&system_info, ptr, ST_TP_SYSTEM_INFO_PART_1_SIZE);
-
- /* Check header */
- if (system_info.header.magic != ST_TP_HEADER_MAGIC ||
- system_info.header.host_data_mem_id != ST_TP_MEM_ID_SYSTEM_INFO)
- return EC_ERROR_UNKNOWN;
-
- ptr += ST_TP_SYSTEM_INFO_PART_1_SIZE;
- ptr += ST_TP_SYSTEM_INFO_PART_1_RESERVED;
- memcpy(&system_info.scr_res_x, ptr, ST_TP_SYSTEM_INFO_PART_2_SIZE);
-
-#define ST_TP_SHOW(attr) CPRINTS(#attr ": %04x", system_info.attr)
- ST_TP_SHOW(chip0_id[0]);
- ST_TP_SHOW(chip0_id[1]);
- ST_TP_SHOW(chip0_ver);
- ST_TP_SHOW(scr_tx_len);
- ST_TP_SHOW(scr_rx_len);
-#define ST_TP_SHOW64(attr) CPRINTS(#attr ": %04llx", system_info.attr)
- ST_TP_SHOW64(release_info);
-#undef ST_TP_SHOW
-#undef ST_TP_SHOW64
- return ret;
-}
-
-/*
- * Enable / disable deep sleep on memory and bus.
- *
- * Before calling dump_error() and dump_error(), deep sleep should be disabled,
- * otherwise response data might be garbage.
- */
-static void enable_deep_sleep(int enable)
-{
- uint8_t cmd[] = {0xFA, 0x20, 0x00, 0x00, 0x68, enable ? 0x0B : 0x08};
-
- spi_transaction(SPI, cmd, sizeof(cmd), NULL, 0);
-}
-
-static void dump_error(void)
-{
- uint8_t tx_buf[] = {0xFB, 0x20, 0x01, 0xEF, 0x80};
- int rx_len = sizeof(rx_buf.dump_info) + ST_TP_EXTRA_BYTE;
- int i;
-
- spi_transaction(SPI, tx_buf, sizeof(tx_buf),
- (uint8_t *)&rx_buf, rx_len);
-
- for (i = 0; i < ARRAY_SIZE(rx_buf.dump_info); i += 4)
- CPRINTS("%08x %08x %08x %08x",
- rx_buf.dump_info[i + 0], rx_buf.dump_info[i + 1],
- rx_buf.dump_info[i + 2], rx_buf.dump_info[i + 3]);
- msleep(8);
-}
-
-/*
- * Dump entire 64K memory on touchpad.
- *
- * This is very time consuming. For now, let's disable this in production
- * build.
- */
-static void dump_memory(void)
-{
- uint32_t size = 0x10000, rx_len = 512 + ST_TP_EXTRA_BYTE;
- uint32_t offset, i;
- uint8_t cmd[] = {0xFB, 0x00, 0x10, 0x00, 0x00};
-
- if (!dump_memory_on_error)
- return;
-
- for (offset = 0; offset < size; offset += 512) {
- cmd[3] = (offset >> 8) & 0xFF;
- cmd[4] = (offset >> 0) & 0xFF;
- spi_transaction(SPI, cmd, sizeof(cmd),
- (uint8_t *)&rx_buf, rx_len);
-
- for (i = 0; i < rx_len - ST_TP_EXTRA_BYTE; i += 32) {
- CPRINTF("%ph %ph %ph %ph "
- "%ph %ph %ph %ph\n",
- HEX_BUF(rx_buf.bytes + i + 4 * 0, 4),
- HEX_BUF(rx_buf.bytes + i + 4 * 1, 4),
- HEX_BUF(rx_buf.bytes + i + 4 * 2, 4),
- HEX_BUF(rx_buf.bytes + i + 4 * 3, 4),
- HEX_BUF(rx_buf.bytes + i + 4 * 4, 4),
- HEX_BUF(rx_buf.bytes + i + 4 * 5, 4),
- HEX_BUF(rx_buf.bytes + i + 4 * 6, 4),
- HEX_BUF(rx_buf.bytes + i + 4 * 7, 4));
- msleep(8);
- }
- }
- CPRINTF("===============================\n");
- msleep(8);
-}
-
-/*
- * Set `tp_control` if there are any actions should be taken.
- */
-static void st_tp_handle_error(uint8_t error_type)
-{
- tp_control |= TP_CONTROL_SHALL_DUMP_ERROR;
-
- /*
- * Suggest action: memory dump and power cycle.
- */
- if (error_type <= 0x06 ||
- error_type == 0xF1 ||
- error_type == 0xF2 ||
- error_type == 0xF3 ||
- (error_type >= 0x47 && error_type <= 0x4E)) {
- tp_control |= TP_CONTROL_SHALL_RESET;
- return;
- }
-
- /*
- * Suggest action: FW shall halt, consult ST.
- */
- if ((error_type >= 0x20 && error_type <= 0x23) ||
- error_type == 0x25 ||
- (error_type >= 0x2E && error_type <= 0x46)) {
- CPRINTS("tp shall halt");
- tp_control |= TP_CONTROL_SHALL_HALT;
- return;
- }
-
- /*
- * Corrupted panel configuration, a panel init should fix it.
- */
- if (error_type >= 0x28 && error_type <= 0x29) {
- tp_control |= TP_CONTROL_SHALL_INIT;
- return;
- }
-
- /*
- * Corrupted CX section, a full panel init should fix it.
- */
- if (error_type >= 0xA0 && error_type <= 0xA6) {
- tp_control |= TP_CONTROL_SHALL_INIT_FULL;
- return;
- }
-
- /*
- * When 0xFF is received, it's very likely ST touchpad is down.
- * Try if touchpad can be recovered by reset.
- */
- if (error_type == 0xFF) {
- if (tp_reset_retry_count < MAX_TP_RESET_RETRY_COUNT) {
- tp_control |= TP_CONTROL_SHALL_RESET;
- tp_reset_retry_count++;
- } else {
- tp_control |= TP_CONTROL_SHALL_HALT;
- }
- return;
- }
-}
-
-/*
- * Handles error reports.
- */
-static void st_tp_handle_error_report(struct st_tp_event_t *e)
-{
- uint8_t error_type = e->report.report_type;
-
- CPRINTS("Touchpad error: %x %x", error_type,
- ((e->report.info[0] << 0) | (e->report.info[1] << 8) |
- (e->report.info[2] << 16) | (e->report.info[3] << 24)));
-
- st_tp_handle_error(error_type);
-}
-
-static void st_tp_handle_status_report(struct st_tp_event_t *e)
-{
- static uint32_t prev_idle_count;
- uint32_t info = ((e->report.info[0] << 0) |
- (e->report.info[1] << 8) |
- (e->report.info[2] << 16) |
- (e->report.info[3] << 24));
-
- if (e->report.report_type == ST_TP_STATUS_FCAL ||
- e->report.report_type == ST_TP_STATUS_FRAME_DROP)
- CPRINTS("TP STATUS REPORT: %02x %08x",
- e->report.report_type, info);
-
- /*
- * Idle count might not change if ST FW is busy (for example, when the
- * user puts a big palm on touchpad). Therefore if idle count doesn't
- * change, we need to double check with touch count.
- *
- * If touch count is 0, and idle count doesn't change, it means that:
- *
- * 1) ST doesn't think there are any fingers.
- * 2) ST is busy on something, can't get into idle mode, and this
- * might cause (1).
- *
- * Resetting touchpad should be the correct action.
- */
- if (e->report.report_type == ST_TP_STATUS_BEACON) {
-#if 0
- const uint8_t touch_count = e->report.reserved;
-
- CPRINTS("BEACON: idle count=%08x", info);
- CPRINTS(" touch count=%d touch slot=%04x",
- touch_count, touch_slot);
-#endif
- if (prev_idle_count == info && touch_slot == 0) {
- CPRINTS(" idle count=%08x not changed", info);
- tp_control |= TP_CONTROL_SHALL_RESET;
- return;
- }
- prev_idle_count = info;
- }
-}
-
-/*
- * Read all events, and handle errors.
- *
- * When there are error events, suggested action will be saved in `tp_control`.
- *
- * @param show_error: whether EC should read and dump error or not.
- * ***If this is true, rx_buf.events[] will be cleared.***
- *
- * @return number of events available
- */
-static int st_tp_read_all_events(int show_error)
-{
- uint8_t cmd = ST_TP_CMD_READ_ALL_EVENTS;
- int rx_len = sizeof(rx_buf.events) + ST_TP_EXTRA_BYTE;
- int i;
-
- if (spi_transaction(SPI, &cmd, 1, (uint8_t *)&rx_buf, rx_len))
- return 0;
-
- for (i = 0; i < ARRAY_SIZE(rx_buf.events); i++) {
- struct st_tp_event_t *e = &rx_buf.events[i];
-
- if (e->magic != ST_TP_EVENT_MAGIC)
- break;
-
- switch (e->evt_id) {
- case ST_TP_EVENT_ID_ERROR_REPORT:
- st_tp_handle_error_report(e);
- break;
- case ST_TP_EVENT_ID_STATUS_REPORT:
- st_tp_handle_status_report(e);
- break;
- }
- }
-
- if (show_error && (tp_control & TP_CONTROL_SHALL_DUMP_ERROR)) {
- enable_deep_sleep(0);
- dump_error();
- dump_memory();
- enable_deep_sleep(1);
- /* rx_buf.events[] is invalid now */
- i = 0;
- }
- tp_control &= ~TP_CONTROL_SHALL_DUMP_ERROR;
-
- return i;
-}
-
-/*
- * Reset touchpad. This function will wait for "controller ready" event after
- * the touchpad is reset.
- */
-static int st_tp_reset(void)
-{
- int i, num_events, retry = 100;
-
- board_touchpad_reset();
-
- while (retry--) {
- num_events = st_tp_read_all_events(0);
-
- /*
- * We are not doing full panel initialization, and error code
- * suggest us to reset or halt.
- */
- if (!(tp_control & (TP_CONTROL_INIT | TP_CONTROL_INIT_FULL)) &&
- (tp_control & (TP_CONTROL_SHALL_HALT |
- TP_CONTROL_SHALL_RESET)))
- break;
-
- for (i = 0; i < num_events; i++) {
- struct st_tp_event_t *e = &rx_buf.events[i];
-
- if (e->evt_id == ST_TP_EVENT_ID_CONTROLLER_READY) {
- CPRINTS("Touchpad ready");
- tp_reset_retry_count = 0;
- return 0;
- }
- }
-
- msleep(10);
- }
- CPRINTS("Timeout waiting for controller ready.");
- return EC_ERROR_TIMEOUT;
-}
-
-/* Initialize the controller ICs after reset */
-static void st_tp_init(void)
-{
- tp_control = 0;
- system_state = 0;
-
- if (st_tp_reset())
- return;
-
- if (tp_control) {
- CPRINTS("tp_control = %x", tp_control);
- return;
- }
- /*
- * On boot, ST firmware will load system info to host data memory,
- * So we don't need to reload it.
- */
- st_tp_read_system_info(0);
-
- system_state = SYSTEM_STATE_READY;
- touch_slot = 0;
-
- touchpad_power_control();
-}
-DECLARE_DEFERRED(st_tp_init);
-
-#ifdef CONFIG_USB_UPDATE
-int touchpad_get_info(struct touchpad_info *tp)
-{
- if (st_tp_read_system_info(1)) {
- tp->status = EC_RES_SUCCESS;
- tp->vendor = ST_VENDOR_ID;
- /*
- * failed to get system info, FW corrupted, return some default
- * values.
- */
- tp->st.id = 0x3936;
- tp->st.fw_version = 0;
- tp->st.fw_checksum = 0;
- return sizeof(*tp);
- }
-
- tp->status = EC_RES_SUCCESS;
- tp->vendor = ST_VENDOR_ID;
- tp->st.id = (system_info.chip0_id[0] << 8) | system_info.chip0_id[1];
- tp->st.fw_version = system_info.release_info;
- tp->st.fw_checksum = system_info.fw_crc;
-
- return sizeof(*tp);
-}
-
-/*
- * Helper functions for firmware update
- *
- * There is no documentation about ST_TP_CMD_WRITE_HW_REG (0xFA).
- * All implementations below are based on sample code from ST.
- */
-static int write_hwreg_cmd32(uint32_t address, uint32_t data)
-{
- uint8_t tx_buf[] = {
- ST_TP_CMD_WRITE_HW_REG,
- (address >> 24) & 0xFF,
- (address >> 16) & 0xFF,
- (address >> 8) & 0xFF,
- (address >> 0) & 0xFF,
- (data >> 24) & 0xFF,
- (data >> 16) & 0xFF,
- (data >> 8) & 0xFF,
- (data >> 0) & 0xFF,
- };
-
- return spi_transaction(SPI, tx_buf, sizeof(tx_buf), NULL, 0);
-}
-
-static int write_hwreg_cmd8(uint32_t address, uint8_t data)
-{
- uint8_t tx_buf[] = {
- ST_TP_CMD_WRITE_HW_REG,
- (address >> 24) & 0xFF,
- (address >> 16) & 0xFF,
- (address >> 8) & 0xFF,
- (address >> 0) & 0xFF,
- data,
- };
-
- return spi_transaction(SPI, tx_buf, sizeof(tx_buf), NULL, 0);
-}
-
-static int wait_for_flash_ready(uint8_t type)
-{
- uint8_t tx_buf[] = {
- ST_TP_CMD_READ_HW_REG,
- 0x20, 0x00, 0x00, type,
- };
- int ret = EC_SUCCESS, retry = 200;
-
- while (retry--) {
- ret = spi_transaction(SPI, tx_buf, sizeof(tx_buf),
- (uint8_t *)&rx_buf, 1 + ST_TP_EXTRA_BYTE);
- if (ret == EC_SUCCESS && !(rx_buf.bytes[0] & 0x80))
- break;
- msleep(50);
- }
- return retry >= 0 ? ret : EC_ERROR_TIMEOUT;
-}
-
-static int erase_flash(int full_init_required)
-{
- int ret;
-
- if (full_init_required)
- ret = write_hwreg_cmd32(0x20000128, 0xFFFFFFFF);
- else
- /* Erase everything, except CX */
- ret = write_hwreg_cmd32(0x20000128, 0xFFFFFF83);
- if (ret)
- return ret;
- ret = write_hwreg_cmd8(0x2000006B, 0x00);
- if (ret)
- return ret;
- ret = write_hwreg_cmd8(0x2000006A, 0xA0);
- if (ret)
- return ret;
- return wait_for_flash_ready(0x6A);
-}
-
-static int st_tp_prepare_for_update(int full_init_required)
-{
- /* hold m3 */
- write_hwreg_cmd8(0x20000024, 0x01);
- /* unlock flash */
- write_hwreg_cmd8(0x20000025, 0x20);
- /* unlock flash erase */
- write_hwreg_cmd8(0x200000DE, 0x03);
- erase_flash(full_init_required);
-
- return EC_SUCCESS;
-}
-
-static int st_tp_start_flash_dma(void)
-{
- int ret;
-
- ret = write_hwreg_cmd8(0x20000071, 0xC0);
- if (ret)
- return ret;
- ret = wait_for_flash_ready(0x71);
- return ret;
-}
-
-static int st_tp_write_one_chunk(const uint8_t *head,
- uint32_t addr, uint32_t chunk_size)
-{
- uint8_t tx_buf[ST_TP_DMA_CHUNK_SIZE + 5];
- uint32_t index = 0;
- int ret;
-
- index = 0;
-
- tx_buf[index++] = ST_TP_CMD_WRITE_HW_REG;
- tx_buf[index++] = (addr >> 24) & 0xFF;
- tx_buf[index++] = (addr >> 16) & 0xFF;
- tx_buf[index++] = (addr >> 8) & 0xFF;
- tx_buf[index++] = (addr >> 0) & 0xFF;
- memcpy(tx_buf + index, head, chunk_size);
- ret = spi_transaction(SPI, tx_buf, chunk_size + 5, NULL, 0);
-
- return ret;
-}
-
-/*
- * @param offset: offset in memory to copy the data (in bytes).
- * @param size: length of data (in bytes).
- * @param data: pointer to data bytes.
- */
-static int st_tp_write_flash(int offset, int size, const uint8_t *data)
-{
- uint8_t tx_buf[12] = {0};
- const uint8_t *head = data, *tail = data + size;
- uint32_t addr, index, chunk_size;
- uint32_t flash_buffer_size;
- int ret;
-
- offset >>= 2; /* offset should be count in words */
- /*
- * To write to flash, the data has to be separated into several chunks.
- * Each chunk will be no more than `ST_TP_DMA_CHUNK_SIZE` bytes.
- * The chunks will first be saved into a buffer, the buffer can only
- * holds `ST_TP_FLASH_BUFFER_SIZE` bytes. We have to flush the buffer
- * when the capacity is reached.
- */
- while (head < tail) {
- addr = 0x00100000;
- flash_buffer_size = 0;
- while (flash_buffer_size < ST_TP_FLASH_BUFFER_SIZE) {
- chunk_size = MIN(ST_TP_DMA_CHUNK_SIZE, tail - head);
- ret = st_tp_write_one_chunk(head, addr, chunk_size);
- if (ret)
- return ret;
-
- flash_buffer_size += chunk_size;
- addr += chunk_size;
- head += chunk_size;
-
- if (head >= tail)
- break;
- }
-
- /* configuring the DMA */
- flash_buffer_size = flash_buffer_size / 4 - 1;
- index = 0;
-
- tx_buf[index++] = ST_TP_CMD_WRITE_HW_REG;
- tx_buf[index++] = 0x20;
- tx_buf[index++] = 0x00;
- tx_buf[index++] = 0x00;
- tx_buf[index++] = 0x72; /* flash DMA config */
- tx_buf[index++] = 0x00;
- tx_buf[index++] = 0x00;
-
- tx_buf[index++] = offset & 0xFF;
- tx_buf[index++] = (offset >> 8) & 0xFF;
- tx_buf[index++] = flash_buffer_size & 0xFF;
- tx_buf[index++] = (flash_buffer_size >> 8) & 0xFF;
- tx_buf[index++] = 0x00;
-
- ret = spi_transaction(SPI, tx_buf, index, NULL, 0);
- if (ret)
- return ret;
- ret = st_tp_start_flash_dma();
- if (ret)
- return ret;
-
- offset += ST_TP_FLASH_BUFFER_SIZE / 4;
- }
- return EC_SUCCESS;
-}
-
-static int st_tp_check_command_echo(const uint8_t *cmd, const size_t len)
-{
- int num_events, i;
- num_events = st_tp_read_all_events(0);
-
- for (i = 0; i < num_events; i++) {
- struct st_tp_event_t *e = &rx_buf.events[i];
-
- if (e->evt_id == ST_TP_EVENT_ID_STATUS_REPORT &&
- e->report.report_type == ST_TP_STATUS_CMD_ECHO &&
- memcmp(e->report.info, cmd, MIN(4, len)) == 0)
- return EC_SUCCESS;
- }
- return EC_ERROR_BUSY;
-}
-
-static uint8_t get_cx_version(uint8_t tp_version)
-{
- /*
- * CX version is tracked by ST release note: go/whiskers-st-release-note
- */
-
- if (tp_version >= 32)
- return 3;
-
- if (tp_version >= 20)
- return 2;
-
- if (tp_version >= 18)
- return 1;
- return 0;
-}
-
-/*
- * Perform panel initialization.
- *
- * This function will wait until the initialization is done, or 10 second
- * timeout is reached.
- *
- * @param full: 1 => force "full" panel initialization. Otherwise, tp_control
- * will be checked to decide if full panel initialization is
- * required.
- *
- * @return EC_SUCCESS or error code.
- */
-static int st_tp_panel_init(int full)
-{
- uint8_t tx_buf[] = {
- ST_TP_CMD_WRITE_SYSTEM_COMMAND, 0x00, 0x02
- };
- int ret, retry;
-
- if (tp_control & (TP_CONTROL_INIT | TP_CONTROL_INIT_FULL))
- return EC_ERROR_BUSY;
-
- st_tp_stop_scan();
- ret = st_tp_reset();
- /*
- * TODO(b:118312397): Figure out how to handle st_tp_reset errors (if
- * needed at all).
- */
- CPRINTS("st_tp_reset ret=%d", ret);
-
- full |= tp_control & TP_CONTROL_SHALL_INIT_FULL;
- if (full) {
- /* should perform full panel initialization */
- tx_buf[2] = 0x3;
- tp_control = TP_CONTROL_INIT_FULL;
- } else {
- tp_control = TP_CONTROL_INIT;
- }
-
- CPRINTS("Start panel initialization (full=%d)", full);
- spi_transaction(SPI, tx_buf, sizeof(tx_buf), NULL, 0);
-
- retry = 100;
- while (retry--) {
- watchdog_reload();
- msleep(100);
-
- ret = st_tp_check_command_echo(tx_buf, sizeof(tx_buf));
- if (ret == EC_SUCCESS) {
- CPRINTS("Panel initialization completed.");
- tp_control &= ~(TP_CONTROL_INIT | TP_CONTROL_INIT_FULL);
- st_tp_init();
- return EC_SUCCESS;
- } else if (ret == EC_ERROR_BUSY) {
- CPRINTS("Panel initialization on going...");
- } else if (tp_control & ~(TP_CONTROL_INIT |
- TP_CONTROL_INIT_FULL)) {
- /* there are other kind of errors. */
- CPRINTS("Panel initialization failed, tp_control: %x",
- tp_control);
- return EC_ERROR_UNKNOWN;
- }
- }
- return EC_ERROR_TIMEOUT;
-}
-
-/*
- * @param offset: should be address between 0 to 1M, aligned with
- * ST_TP_DMA_CHUNK_SIZE.
- * @param size: length of `data` array.
- * @param data: content of new touchpad firmware.
- */
-int touchpad_update_write(int offset, int size, const uint8_t *data)
-{
- static int full_init_required;
- int ret, flash_offset;
-
- CPRINTS("%s %08x %d", __func__, offset, size);
- if (offset == 0) {
- const struct st_tp_fw_header_t *header;
- uint8_t old_cx_version;
- uint8_t new_cx_version;
- int retry;
-
- header = (const struct st_tp_fw_header_t *)data;
- if (header->signature != 0xAA55AA55)
- return EC_ERROR_INVAL;
-
- for (retry = 50; retry > 0; retry--) {
- watchdog_reload();
- if (system_state & SYSTEM_STATE_READY)
- break;
- if (retry % 10 == 0)
- CPRINTS("TP not ready for update, "
- "will check again");
- msleep(100);
- }
-
- old_cx_version = get_cx_version(system_info.release_info);
- new_cx_version = get_cx_version(header->release_info);
-
- full_init_required = old_cx_version != new_cx_version;
-
- /* stop scanning, interrupt, etc... */
- st_tp_stop_scan();
-
- ret = st_tp_prepare_for_update(full_init_required);
- if (ret)
- return ret;
- return EC_SUCCESS;
- }
-
- flash_offset = offset - CONFIG_UPDATE_PDU_SIZE;
- if (flash_offset % ST_TP_DMA_CHUNK_SIZE)
- return EC_ERROR_INVAL;
-
- if (flash_offset >= ST_TP_FLASH_OFFSET_PANEL_CFG &&
- flash_offset < ST_TP_FLASH_OFFSET_CONFIG)
- /* don't update CX section && panel config section */
- return EC_SUCCESS;
-
- ret = st_tp_write_flash(flash_offset, size, data);
- if (ret)
- return ret;
-
- if (offset + size == CONFIG_TOUCHPAD_VIRTUAL_SIZE) {
- CPRINTS("%s: End update, wait for reset.", __func__);
-
- ret = st_tp_panel_init(full_init_required);
- task_set_event(TASK_ID_TOUCHPAD, TASK_EVENT_TP_UPDATED);
- return ret;
- }
-
- return EC_SUCCESS;
-}
-
-int touchpad_debug(const uint8_t *param, unsigned int param_size,
- uint8_t **data, unsigned int *data_size)
-{
- static uint8_t buf[8];
- int num_events;
-
- if (param_size != 1)
- return EC_RES_INVALID_PARAM;
-
- switch (*param) {
- case ST_TP_DEBUG_CMD_RESET_TOUCHPAD:
- *data = NULL;
- *data_size = 0;
- st_tp_stop_scan();
- hook_call_deferred(&st_tp_init_data, 100 * MSEC);
- return EC_SUCCESS;
- case ST_TP_DEBUG_CMD_CALIBRATE:
- /* no return value */
- *data = NULL;
- *data_size = 0;
- st_tp_panel_init(1);
- return EC_SUCCESS;
- case ST_TP_DEBUG_CMD_START_SCAN:
- *data = NULL;
- *data_size = 0;
- st_tp_start_scan();
- return EC_SUCCESS;
- case ST_TP_DEBUG_CMD_STOP_SCAN:
- *data = NULL;
- *data_size = 0;
- st_tp_stop_scan();
- return EC_SUCCESS;
- case ST_TP_DEBUG_CMD_READ_BUF_HEADER:
- *data = buf;
- *data_size = 8;
- st_tp_read_host_buffer_header();
- memcpy(buf, rx_buf.bytes, *data_size);
- CPRINTS("header: %ph", HEX_BUF(buf, *data_size));
- return EC_SUCCESS;
- case ST_TP_DEBUG_CMD_READ_EVENTS:
- num_events = st_tp_read_all_events(0);
- if (num_events) {
- int i;
-
- for (i = 0; i < num_events; i++) {
- CPRINTS("event[%d]: id=%d, type=%d",
- i, rx_buf.events[i].evt_id,
- rx_buf.events[i].report.report_type);
- }
- }
- *data = buf;
- *data_size = 1;
- *data[0] = num_events;
- st_tp_send_ack();
- return EC_SUCCESS;
- }
- return EC_RES_INVALID_PARAM;
-}
-#endif
-
-void touchpad_interrupt(enum gpio_signal signal)
-{
- irq_ts = __hw_clock_source_read();
-
- task_wake(TASK_ID_TOUCHPAD);
-}
-
-static int touchpad_should_enable(void)
-{
- /* touchpad is not ready. */
- if (tp_control)
- return 0;
-
-#ifdef CONFIG_USB_SUSPEND
- if (usb_is_suspended() && !usb_is_remote_wakeup_enabled())
- return 0;
-#endif
-
-#ifdef CONFIG_TABLET_MODE
- if (tablet_get_mode())
- return 0;
-#endif
- return 1;
-}
-
-/* Make a decision on touchpad power, based on USB and tablet mode status. */
-static void touchpad_power_control(void)
-{
- const int enabled = !!(system_state & SYSTEM_STATE_ACTIVE_MODE);
- int enable = touchpad_should_enable();
-
- if (enabled == enable)
- return;
-
- if (enable)
- st_tp_start_scan();
- else
- st_tp_stop_scan();
-}
-
-static void touchpad_read_idle_count(void)
-{
- static uint32_t prev_count;
- uint32_t count;
- int ret;
- int rx_len = 2 + ST_TP_EXTRA_BYTE;
- uint8_t cmd_read_counter[] = {
- 0xFB, 0x00, 0x10, 0xff, 0xff
- };
-
- /* Find address of idle count. */
- ret = st_tp_load_host_data(ST_TP_MEM_ID_SYSTEM_INFO);
- if (ret)
- return;
- st_tp_read_host_data_memory(0x0082, &rx_buf, rx_len);
-
- /* Fill in address of idle count, the byte order is reversed. */
- cmd_read_counter[3] = rx_buf.bytes[1];
- cmd_read_counter[4] = rx_buf.bytes[0];
-
- /* Read idle count */
- spi_transaction(SPI, cmd_read_counter, sizeof(cmd_read_counter),
- (uint8_t *)&rx_buf, 4 + ST_TP_EXTRA_BYTE);
-
- count = rx_buf.dump_info[0];
-
- CPRINTS("idle_count = %08x", count);
- if (count == prev_count)
- CPRINTS("counter doesn't change...");
- else
- prev_count = count;
-}
-
-/*
- * Try to collect symptoms of type B error.
- *
- * There are three possible symptoms:
- * 1. error dump section is corrupted / contains error.
- * 2. memory stack is corrupted (not 0xCC).
- * 3. idle count is not changing.
- */
-static void touchpad_collect_error(void)
-{
- const uint8_t tx_dump_error[] = {
- 0xFB, 0x20, 0x01, 0xEF, 0x80
- };
- uint32_t dump_info[2];
- const uint8_t tx_dump_memory[] = {
- 0xFB, 0x00, 0x10, 0x00, 0x00
- };
- uint32_t dump_memory[16];
- int i;
-
- enable_deep_sleep(0);
- spi_transaction(SPI, tx_dump_error, sizeof(tx_dump_error),
- (uint8_t *)&rx_buf,
- sizeof(dump_info) + ST_TP_EXTRA_BYTE);
- memcpy(dump_info, rx_buf.bytes, sizeof(dump_info));
-
- spi_transaction(SPI, tx_dump_memory, sizeof(tx_dump_memory),
- (uint8_t *)&rx_buf,
- sizeof(dump_memory) + ST_TP_EXTRA_BYTE);
- memcpy(dump_memory, rx_buf.bytes, sizeof(dump_memory));
-
- CPRINTS("check error dump: %08x %08x", dump_info[0], dump_info[1]);
- CPRINTS("check memory dump:");
- for (i = 0; i < ARRAY_SIZE(dump_memory); i += 8) {
- CPRINTF("%08x %08x %08x %08x %08x %08x %08x %08x\n",
- dump_memory[i + 0],
- dump_memory[i + 1],
- dump_memory[i + 2],
- dump_memory[i + 3],
- dump_memory[i + 4],
- dump_memory[i + 5],
- dump_memory[i + 6],
- dump_memory[i + 7]);
- }
-
- for (i = 0; i < 3; i++)
- touchpad_read_idle_count();
- enable_deep_sleep(1);
-
- tp_control |= TP_CONTROL_SHALL_RESET;
-}
-
-void touchpad_task(void *u)
-{
- uint32_t event;
-
- while (1) {
- uint32_t retry;
-
- for (retry = 0; retry < 3; retry++) {
- CPRINTS("st_tp_init: trial %d", retry + 1);
- st_tp_init();
-
- if (system_state & SYSTEM_STATE_READY)
- break;
- /*
- * React on touchpad errors.
- */
- if (tp_control & TP_CONTROL_SHALL_INIT_FULL) {
- /* suppress other handlers */
- tp_control = TP_CONTROL_SHALL_INIT_FULL;
- st_tp_panel_init(1);
- } else if (tp_control & TP_CONTROL_SHALL_INIT) {
- /* suppress other handlers */
- tp_control = TP_CONTROL_SHALL_INIT;
- st_tp_panel_init(0);
- } else if (tp_control & TP_CONTROL_SHALL_RESET) {
- /* suppress other handlers */
- tp_control = TP_CONTROL_SHALL_RESET;
- } else if (tp_control & TP_CONTROL_SHALL_HALT) {
- CPRINTS("shall halt");
- tp_control = 0;
- break;
- }
- }
-
- if (system_state & SYSTEM_STATE_READY)
- break;
-
- /* failed to init, mark it as ready to allow upgrade */
- system_state = SYSTEM_STATE_READY;
- /* wait for upgrade complete */
- task_wait_event_mask(TASK_EVENT_TP_UPDATED, -1);
- }
- touchpad_power_control();
-
- while (1) {
- /* wait for at most 3 seconds */
- event = task_wait_event(3 * 1000 * 1000);
-
- if ((event & TASK_EVENT_TIMER) &&
- (system_state & SYSTEM_STATE_ACTIVE_MODE))
- /*
- * Haven't received anything for 3 seconds, and we are
- * supposed to be in active mode. This is not normal,
- * check for errors and reset.
- */
- touchpad_collect_error();
-
- if (event & TASK_EVENT_WAKE)
- while (!tp_control &&
- !gpio_get_level(GPIO_TOUCHPAD_INT))
- st_tp_read_report();
-
- /*
- * React on touchpad errors.
- */
- if (tp_control & TP_CONTROL_SHALL_INIT_FULL) {
- /* suppress other handlers */
- tp_control = TP_CONTROL_SHALL_INIT_FULL;
- st_tp_panel_init(1);
- } else if (tp_control & TP_CONTROL_SHALL_INIT) {
- /* suppress other handlers */
- tp_control = TP_CONTROL_SHALL_INIT;
- st_tp_panel_init(0);
- } else if (tp_control & TP_CONTROL_SHALL_RESET) {
- /* suppress other handlers */
- tp_control = TP_CONTROL_SHALL_RESET;
- st_tp_init();
- } else if (tp_control & TP_CONTROL_SHALL_HALT) {
- tp_control = 0;
- st_tp_stop_scan();
- }
-
- if (event & TASK_EVENT_POWER)
- touchpad_power_control();
- }
-}
-
-/*
- * When USB PM status changes, or tablet mode changes, call in the main task to
- * decide whether to turn touchpad on or off.
- */
-#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_TABLET_MODE)
-static void touchpad_power_change(void)
-{
- task_set_event(TASK_ID_TOUCHPAD, TASK_EVENT_POWER);
-}
-#endif
-#ifdef CONFIG_USB_SUSPEND
-DECLARE_HOOK(HOOK_USB_PM_CHANGE, touchpad_power_change, HOOK_PRIO_DEFAULT);
-#endif
-#ifdef CONFIG_TABLET_MODE
-DECLARE_HOOK(HOOK_TABLET_MODE_CHANGE, touchpad_power_change, HOOK_PRIO_DEFAULT);
-#endif
-
-#ifdef CONFIG_USB_ISOCHRONOUS
-static void st_tp_enable_heat_map(void)
-{
- int new_state = (SYSTEM_STATE_ENABLE_HEAT_MAP |
- SYSTEM_STATE_ENABLE_DOME_SWITCH |
- SYSTEM_STATE_ACTIVE_MODE);
- int mask = new_state;
-
- st_tp_update_system_state(new_state, mask);
-}
-DECLARE_DEFERRED(st_tp_enable_heat_map);
-
-static void st_tp_disable_heat_map(void)
-{
- int new_state = 0;
- int mask = SYSTEM_STATE_ENABLE_HEAT_MAP;
-
- st_tp_update_system_state(new_state, mask);
-}
-DECLARE_DEFERRED(st_tp_disable_heat_map);
-
-static void print_frame(void)
-{
- char debug_line[ST_TOUCH_COLS + 5];
- int i, j, index;
- int v;
- struct st_tp_usb_packet_t *packet = &usb_packet[usb_buffer_index & 1];
-
- if (usb_buffer_index == spi_buffer_index)
- /* buffer is empty. */
- return;
-
- /* We will have ~150 FPS, let's print ~4 frames per second */
- if (usb_buffer_index % 37 == 0) {
- /* move cursor back to top left corner */
- CPRINTF("\x1b[H");
- CPUTS("==============\n");
- for (i = 0; i < ST_TOUCH_ROWS; i++) {
- for (j = 0; j < ST_TOUCH_COLS; j++) {
- index = i * ST_TOUCH_COLS;
- index += (ST_TOUCH_COLS - j - 1); // flip X
- v = packet->frame[index];
-
- if (v > 0)
- debug_line[j] = '0' + v * 10 / 256;
- else
- debug_line[j] = ' ';
- }
- debug_line[j++] = '\n';
- debug_line[j++] = '\0';
- CPRINTF(debug_line);
- }
- CPUTS("==============\n");
- }
-}
-
-static int st_tp_read_frame(void)
-{
- int ret = EC_SUCCESS;
- int rx_len = ST_TOUCH_FRAME_SIZE + ST_TP_EXTRA_BYTE;
- int heat_map_addr = get_heat_map_addr();
- uint8_t tx_buf[] = {
- ST_TP_CMD_READ_SPI_HOST_BUFFER,
- (heat_map_addr >> 8) & 0xFF,
- (heat_map_addr >> 0) & 0xFF,
- };
-
- /*
- * Since usb_packet.frame is already ane uint8_t byte array, we can just
- * make it the RX buffer for SPI transaction.
- *
- * When there is a extra byte, since we know that flags is a one byte
- * value, and we will override it later, it's okay for SPI transaction
- * to write the extra byte to flags address.
- */
-#if ST_TP_EXTRA_BYTE == 1
- BUILD_ASSERT(sizeof(usb_packet[0].flags) == 1);
- uint8_t *rx_buf = &usb_packet[spi_buffer_index & 1].flags;
-#else
- uint8_t *rx_buf = usb_packet[spi_buffer_index & 1].frame;
-#endif
-
- st_tp_read_all_events(1);
- if (tp_control) {
- ret = EC_ERROR_UNKNOWN;
- goto failed;
- }
-
- if (heat_map_addr < 0)
- goto failed;
-
- ret = st_tp_check_domeswitch_state();
- if (ret)
- goto failed;
-
- /*
- * Theoretically, we should read host buffer header to check if data is
- * valid, but the data should always be ready when interrupt pin is low.
- * Let's skip this check for now.
- */
- ret = spi_transaction(SPI, tx_buf, sizeof(tx_buf),
- (uint8_t *)rx_buf, rx_len);
- if (ret == EC_SUCCESS) {
- int i;
- uint8_t *dest = usb_packet[spi_buffer_index & 1].frame;
- uint8_t max_value = 0;
-
- for (i = 0; i < ST_TOUCH_COLS * ST_TOUCH_ROWS; i++)
- max_value |= dest[i];
- if (max_value == 0) // empty frame
- return -1;
-
- usb_packet[spi_buffer_index & 1].flags = 0;
- if (system_state & SYSTEM_STATE_DOME_SWITCH_LEVEL)
- usb_packet[spi_buffer_index & 1].flags |=
- USB_FRAME_FLAGS_BUTTON;
- }
-failed:
- return ret;
-}
-
-/* Define USB interface for heat_map */
-
-/* function prototypes */
-static int st_tp_usb_set_interface(usb_uint alternate_setting,
- usb_uint interface);
-static int heatmap_send_packet(struct usb_isochronous_config const *config);
-static void st_tp_usb_tx_callback(struct usb_isochronous_config const *config);
-
-/* USB descriptors */
-USB_ISOCHRONOUS_CONFIG_FULL(usb_st_tp_heatmap_config,
- USB_IFACE_ST_TOUCHPAD,
- USB_CLASS_VENDOR_SPEC,
- USB_SUBCLASS_GOOGLE_HEATMAP,
- USB_PROTOCOL_GOOGLE_HEATMAP,
- USB_STR_HEATMAP_NAME, /* interface name */
- USB_EP_ST_TOUCHPAD,
- USB_ISO_PACKET_SIZE,
- st_tp_usb_tx_callback,
- st_tp_usb_set_interface,
- 1 /* 1 extra EP for interrupts */)
-
-/* ***This function will be executed in interrupt context*** */
-void st_tp_usb_tx_callback(struct usb_isochronous_config const *config)
-{
- task_wake(TASK_ID_HEATMAP);
-}
-
-void heatmap_task(void *unused)
-{
- struct usb_isochronous_config const *config;
-
- config = &usb_st_tp_heatmap_config;
-
- while (1) {
- /* waiting st_tp_usb_tx_callback() */
- task_wait_event(-1);
-
- if (system_state & SYSTEM_STATE_DEBUG_MODE)
- continue;
-
- if (usb_buffer_index == spi_buffer_index)
- /* buffer is empty */
- continue;
-
- while (heatmap_send_packet(config))
- /* We failed to write a packet, try again later. */
- task_wait_event(100);
- }
-}
-
-/* USB interface has completed TX, it's asking for more data */
-static int heatmap_send_packet(struct usb_isochronous_config const *config)
-{
- size_t num_byte_available;
- size_t offset = 0;
- int ret, buffer_id = -1;
- struct st_tp_usb_packet_t *packet = &usb_packet[usb_buffer_index & 1];
-
- packet_header.flags = 0;
- num_byte_available = sizeof(*packet) - transmit_report_offset;
- if (num_byte_available > 0) {
- if (transmit_report_offset == 0)
- packet_header.flags |= HEADER_FLAGS_NEW_FRAME;
- ret = usb_isochronous_write_buffer(
- config,
- (uint8_t *)&packet_header,
- sizeof(packet_header),
- offset,
- &buffer_id,
- 0);
- /*
- * Since USB_ISO_PACKET_SIZE > sizeof(packet_header), this must
- * be true.
- */
- if (ret != sizeof(packet_header))
- return -1;
-
- offset += ret;
- packet_header.index++;
-
- ret = usb_isochronous_write_buffer(
- config,
- (uint8_t *)packet + transmit_report_offset,
- num_byte_available,
- offset,
- &buffer_id,
- 1);
- if (ret < 0) {
- /*
- * TODO(b/70482333): handle this error, it might be:
- * 1. timeout (buffer_id changed)
- * 2. invalid offset
- *
- * For now, let's just return an error and try again.
- */
- CPRINTS("%s %d: %d", __func__, __LINE__, -ret);
- return ret;
- }
-
- /* We should have sent some bytes, update offset */
- transmit_report_offset += ret;
- if (transmit_report_offset == sizeof(*packet)) {
- transmit_report_offset = 0;
- usb_buffer_index++;
- }
- }
- return 0;
-}
-
-static int st_tp_usb_set_interface(usb_uint alternate_setting,
- usb_uint interface)
-{
- if (alternate_setting == 1) {
- if ((system_info.release_info & 0xFF) <
- ST_TP_MIN_HEATMAP_VERSION) {
- CPRINTS("release version %04llx doesn't support heatmap",
- system_info.release_info);
- /* Heatmap mode is not supported in this version. */
- return -1;
- }
-
- hook_call_deferred(&st_tp_enable_heat_map_data, 0);
- return 0;
- } else if (alternate_setting == 0) {
- hook_call_deferred(&st_tp_disable_heat_map_data, 0);
- return 0;
- } else /* we only have two settings. */
- return -1;
-}
-
-static int get_heat_map_addr(void)
-{
- /*
- * TODO(stimim): drop this when we are sure all trackpads are having the
- * same config (e.g. after EVT).
- */
- if (system_info.release_info >= 0x3)
- return 0x0120;
- else if (system_info.release_info == 0x1)
- return 0x20;
- else
- return -1; /* Unknown version */
-}
-
-struct st_tp_interrupt_t {
-#define ST_TP_INT_FRAME_AVAILABLE BIT(0)
- uint8_t flags;
-} __packed;
-
-static usb_uint st_tp_usb_int_buffer[
- DIV_ROUND_UP(sizeof(struct st_tp_interrupt_t), 2)] __usb_ram;
-
-const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_ST_TOUCHPAD, 81) = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 0x80 | USB_EP_ST_TOUCHPAD_INT,
- .bmAttributes = 0x03 /* Interrupt endpoint */,
- .wMaxPacketSize = sizeof(struct st_tp_interrupt_t),
- .bInterval = 1 /* ms */,
-};
-
-static void st_tp_interrupt_send(void)
-{
- struct st_tp_interrupt_t report;
-
- memset(&report, 0, sizeof(report));
-
- if (usb_buffer_index < spi_buffer_index)
- report.flags |= ST_TP_INT_FRAME_AVAILABLE;
- memcpy_to_usbram((void *)usb_sram_addr(st_tp_usb_int_buffer),
- &report, sizeof(report));
- /* enable TX */
- STM32_TOGGLE_EP(USB_EP_ST_TOUCHPAD_INT, EP_TX_MASK, EP_TX_VALID, 0);
- usb_wake();
-}
-
-static void st_tp_interrupt_tx(void)
-{
- STM32_USB_EP(USB_EP_ST_TOUCHPAD_INT) &= EP_MASK;
-
- if (usb_buffer_index < spi_buffer_index)
- /* pending frames */
- hook_call_deferred(&st_tp_interrupt_send_data, 0);
-}
-
-static void st_tp_interrupt_event(enum usb_ep_event evt)
-{
- int ep = USB_EP_ST_TOUCHPAD_INT;
-
- if (evt == USB_EVENT_RESET) {
- btable_ep[ep].tx_addr = usb_sram_addr(st_tp_usb_int_buffer);
- btable_ep[ep].tx_count = sizeof(struct st_tp_interrupt_t);
-
- STM32_USB_EP(ep) = ((ep << 0) |
- EP_TX_VALID |
- (3 << 9) /* interrupt EP */ |
- EP_RX_DISAB);
- }
-}
-
-USB_DECLARE_EP(USB_EP_ST_TOUCHPAD_INT, st_tp_interrupt_tx, st_tp_interrupt_tx,
- st_tp_interrupt_event);
-
-#endif
-
-/* Debugging commands */
-static int command_touchpad_st(int argc, char **argv)
-{
- if (argc < 2)
- return EC_ERROR_PARAM_COUNT;
- if (strcasecmp(argv[1], "version") == 0) {
- st_tp_read_system_info(1);
- return EC_SUCCESS;
- } else if (strcasecmp(argv[1], "calibrate") == 0) {
- st_tp_panel_init(1);
- return EC_SUCCESS;
- } else if (strcasecmp(argv[1], "enable") == 0) {
-#ifdef CONFIG_USB_ISOCHRONOUS
- set_bits(&system_state, SYSTEM_STATE_DEBUG_MODE,
- SYSTEM_STATE_DEBUG_MODE);
- hook_call_deferred(&st_tp_enable_heat_map_data, 0);
- return 0;
-#else
- return EC_ERROR_NOT_HANDLED;
-#endif
- } else if (strcasecmp(argv[1], "disable") == 0) {
-#ifdef CONFIG_USB_ISOCHRONOUS
- set_bits(&system_state, 0, SYSTEM_STATE_DEBUG_MODE);
- hook_call_deferred(&st_tp_disable_heat_map_data, 0);
- return 0;
-#else
- return EC_ERROR_NOT_HANDLED;
-#endif
- } else if (strcasecmp(argv[1], "dump") == 0) {
- enable_deep_sleep(0);
- dump_error();
- dump_memory();
- enable_deep_sleep(1);
- return EC_SUCCESS;
- } else if (strcasecmp(argv[1], "memory_dump") == 0) {
- if (argc == 3 && !parse_bool(argv[2], &dump_memory_on_error))
- return EC_ERROR_PARAM2;
-
- ccprintf("memory_dump: %d\n", dump_memory_on_error);
- return EC_SUCCESS;
- } else {
- return EC_ERROR_PARAM1;
- }
-}
-DECLARE_CONSOLE_COMMAND(touchpad_st, command_touchpad_st,
- "<enable | disable | version | calibrate | dump | "
- "memory_dump <enable|disable>>",
- "Read write spi. id is spi_devices array index");