blob: 4e5c7d1f0fdd26e5b1d5551e63dd551ab1b1330c [file] [log] [blame]
/*
* Copyright 2013 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation (the "GPL").
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* A copy of the GPL is available at
* http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/freezer.h>
#include <linux/proc_fs.h>
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/poll.h>
#include <linux/kfifo.h>
#include <linux/version.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/firmware.h>
#include <linux/input/mt.h>
#include <linux/time.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#ifdef CONFIG_REGULATOR
#include <linux/regulator/consumer.h>
#endif
#include <linux/timer.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#include <linux/i2c/bcmtch15xxx.h>
#include <linux/i2c/bcmtch15xxx_settings.h>
#undef CONFIG_OF
/* -------------------------------------- */
/* - BCM Touch Controller Driver Macros - */
/* -------------------------------------- */
/* -- driver version -- */
#define BCMTCH_DRIVER_VERSION "1.4.4.00"
#define BCMTCH_DRIVER_BUILD_DATE __DATE__
#define BCMTCH_DRIVER_BUILD_TIME __TIME__
/* -- SPM addresses -- */
#define BCMTCH_SPM_REG_REVISIONID 0x40
#define BCMTCH_SPM_REG_CHIPID0 0x41
#define BCMTCH_SPM_REG_CHIPID1 0x42
#define BCMTCH_SPM_REG_CHIPID2 0x43
#define BCMTCH_SPM_REG_SPI_I2C_SEL 0x44
#define BCMTCH_SPM_REG_I2CS_CHIPID 0x45
#define BCMTCH_SPM_REG_PSR 0x48
#define BCMTCH_SPM_REG_MSG_FROM_HOST 0x49
#define BCMTCH_SPM_REG_MSG_FROM_HOST_1 0x4a
#define BCMTCH_SPM_REG_MSG_FROM_HOST_2 0x4b
#define BCMTCH_SPM_REG_MSG_FROM_HOST_3 0x4c
#define BCMTCH_SPM_REG_RQST_FROM_HOST 0x4d
#define BCMTCH_SPM_REG_MSG_TO_HOST 0x4e
#define BCMTCH_SPM_REG_PMU_CONTROL2 0x52
#define BCMTCH_SPM_REG_SOFT_RESETS 0x59
#define BCMTCH_SPM_REG_FLL_STATUS 0x5c
#define BCMTCH_SPM_REG_ALFO_CTRL 0x60
#define BCMTCH_SPM_REG_LPLFO_CTRL 0x61
#define BCMTCH_SPM_REG_DMA_ADDR 0x80
#define BCMTCH_SPM_REG_DMA_STATUS 0x89
#define BCMTCH_SPM_REG_DMA_WFIFO 0x92
#define BCMTCH_SPM_REG_DMA_RFIFO 0xa2
/* -- SYS addresses -- */
#define BCMTCH_ADDR_BASE 0x30000000
#define BCMTCH_ADDR_SPM_BASE (BCMTCH_ADDR_BASE + 0x00100000)
#define BCMTCH_ADDR_SPM_PWR_CTRL (BCMTCH_ADDR_SPM_BASE + 0x1c)
#define BCMTCH_ADDR_SPM_LPLFO_CTRL_RO (BCMTCH_ADDR_SPM_BASE + 0xa0)
#define BCMTCH_ADDR_SPM_REMAP (BCMTCH_ADDR_SPM_BASE + 0x100)
#define BCMTCH_ADDR_SPM_STICKY_BITS (BCMTCH_ADDR_SPM_BASE + 0x144)
#define BCMTCH_ADDR_COMMON_BASE (BCMTCH_ADDR_BASE + 0x00110000)
#define BCMTCH_ADDR_COMMON_ARM_REMAP (BCMTCH_ADDR_COMMON_BASE + 0x00)
#define BCMTCH_ADDR_COMMON_SYS_HCLK_CTRL (BCMTCH_ADDR_COMMON_BASE + 0x20)
#define BCMTCH_ADDR_COMMON_CLOCK_ENABLE (BCMTCH_ADDR_COMMON_BASE + 0x48)
#define BCMTCH_ADDR_COMMON_FLL_CTRL0 (BCMTCH_ADDR_COMMON_BASE + 0x104)
#define BCMTCH_ADDR_COMMON_FLL_LPF_CTRL2 (BCMTCH_ADDR_COMMON_BASE + 0x114)
#define BCMTCH_ADDR_COMMON_FLL_TEST_CTRL1 (BCMTCH_ADDR_COMMON_BASE + 0x144)
#define BCMTCH_ADDR_TCH_BASE (BCMTCH_ADDR_BASE + 0x00300000)
#define BCMTCH_ADDR_TCH_VER (BCMTCH_ADDR_TCH_BASE + 0x00)
/* -- SYS MEM addresses -- */
#define BCMTCH_ADDR_VECTORS 0x00000000
#define BCMTCH_ADDR_CODE 0x10000000
#define BCMTCH_ADDR_DATA 0x10009000
#define BCMTCH_ADDR_TOC_BASE 0x0020c000
/* -- guards -- */
#define BCMTCH_STATE_PROTOCOL 1
/* -- constants -- */
#define BCMTCH_MAX_TOUCH 10
#define BCMTCH_MAX_BUTTONS 16
#define BCMTCH_AXIS_MAX 4095
#define BCMTCH_AXIS_SHIFT_BITS 4
#define BCMTCH_MAX_PRESSURE 500
#define BCMTCH_MIN_ORIENTATION -512
#define BCMTCH_MAX_ORIENTATION 511
#define BCMTCH_DMA_MODE_READ 1
#define BCMTCH_DMA_MODE_WRITE 3
#define BCMTCH_IF_I2C_SEL 0
#define BCMTCH_IF_SPI_SEL 1
#define BCMTCH_DEFAULT_I2C_ADDR_SYS 0x68
#define BCMTCH_IF_I2C_COMMON_CLOCK 0x387B
#define BCMTCH_IF_SPI_COMMON_CLOCK 0x387F
#define BCMTCH_COMMON_CLOCK_USE_FLL (0x1 << 18)
#define BCMTCH_POWER_STATE_SLEEP 0
#define BCMTCH_POWER_STATE_RETENTION 1
#define BCMTCH_POWER_STATE_IDLE 3
#define BCMTCH_POWER_STATE_ACTIVE 4
#define BCMTCH_POWER_MODE_SLEEP 0x01
#define BCMTCH_POWER_MODE_WAKE 0x02
#define BCMTCH_POWER_MODE_NOWAKE 0x00
#define BCMTCH_PMU_CNTL2_DLDO_1_1V 0x08
#define BCMTCH_RESET_MODE_SOFT_CLEAR 0x00
#define BCMTCH_RESET_MODE_SOFT_CHIP 0x01
#define BCMTCH_RESET_MODE_SOFT_ARM 0x02
#define BCMTCH_RESET_MODE_HARD 0x04
#define BCMTCH_MEM_REMAP_ADDR BCMTCH_ADDR_SPM_REMAP
#define BCMTCH_MEM_ROM_BOOT 0x00
#define BCMTCH_MEM_RAM_BOOT 0x01
#define BCMTCH_MEM_MAP_1000 0x00
#define BCMTCH_MEM_MAP_3000 0x02
#define BCMTCH_FW_READY_WAIT 1000
#define BCMTCH_SPM_STICKY_BITS_PIN_RESET 0x02
/* development and test */
#define BCMPFX "BCMTCH:"
#define BCMTCH_USE_PRINTK 0
#if BCMTCH_USE_PRINTK
#define BCMTCH_INFO(fmt, arg...) printk(BCMPFX fmt, ##arg)
#define BCMTCH_ERR(fmt, arg...) printk(BCMPFX fmt, ##arg)
#define BCMTCH_DBG(flag, fmt, arg...) \
{if (bcmtch_debug_flag & flag) printk(BCMPFX fmt, ##arg); }
#else
#define BCMTCH_INFO(fmt, arg...) pr_info(BCMPFX fmt, ##arg)
#define BCMTCH_ERR(fmt, arg...) pr_err(BCMPFX fmt, ##arg)
#define BCMTCH_DBG(flag, fmt, arg...) pr_debug(BCMPFX fmt, ##arg)
#endif
#define PROGRESS() (BCMTCH_INFO("%s : %d\n", __func__, __LINE__))
/* -------------------------------------- */
/* - Touch Firmware Environment (ToFE) - */
/* -------------------------------------- */
#define TOFE_MESSAGE_SUCCESS 0x12
#define TOFE_MESSAGE_SOC_REBOOT_PENDING 0x16
#define TOFE_MESSAGE_COMMAND_ECHO 0x20
#define TOFE_MESSAGE_FW_READY 0x80
#define TOFE_MESSAGE_FW_READY_INTERRUPT 0x81
#define TOFE_MESSAGE_FW_READY_OVERRIDE 0x82
#define TOFE_MESSAGE_FW_READY_INTERRUPT_OVERRIDE 0x83
enum tofe_command {
TOFE_COMMAND_NO_COMMAND = 0,
TOFE_COMMAND_INTERRUPT_ACK,
TOFE_COMMAND_SCAN_START,
TOFE_COMMAND_SCAN_STOP,
TOFE_COMMAND_SCAN_SET_RATE,
TOFE_COMMAND_SET_MODE,
TOFE_COMMAND_CALIBRATE,
TOFE_COMMAND_AFEREGREAD,
TOFE_COMMAND_AFEREGWRITE,
TOFE_COMMAND_RUN_SEM,
TOFE_COMMAND_SET_LOG_MASK,
TOFE_COMMAND_SET_LOG_MASK_FWID_ALL,
TOFE_COMMAND_GET_LOG_MASK,
TOFE_COMMAND_INIT_POST_BOOT_PATCHES,
TOFE_COMMAND_SET_POWER_MODE,
TOFE_COMMAND_POWER_MODE_SUSPEND,
TOFE_COMMAND_POWER_MODE_RESUME,
TOFE_COMMAND_HOST_OVERRIDE_REQ,
TOFE_COMMAND_HOST_OVERRIDE_REL,
TOFE_COMMAND_REBOOT_APPROVED,
TOFE_COMMAND_LAST,
TOFE_COMMAND_MAX = 0xff
};
enum bcmtch_status {
BCMTCH_STATUS_SUCCESS = 0, /* Success */
BCMTCH_STATUS_ERR_FAIL, /* Generic failure */
BCMTCH_STATUS_ERR_NOMEM, /* Memory error */
BCMTCH_STATUS_ERR_NOARG, /* No proper arguments */
BCMTCH_STATUS_ERR_BADARG, /* Bad argument */
BCMTCH_STATUS_ERR_TOUT, /* Timeout */
BCMTCH_STATUS_ERR_IO, /* I/O error */
BCMTCH_STATUS_ERR_NOCFG, /* No configuration */
BCMTCH_STATUS_ERR_NOCHN, /* No required channel */
};
/**
@ struct tofe_command_response
@ brief Entry structure for command channel.
*/
struct tofe_command_response {
uint8_t flags;
uint8_t command;
uint16_t result;
uint32_t data;
};
#define TOFE_COMMAND_FLAG_COMMAND_PROCESSED (1 << 0)
#define TOFE_COMMAND_FLAG_REQUEST_RESULT (1 << 7)
/* ToFE Signature */
#define TOFE_SIGNATURE_MAGIC_SIZE 8
#define TOFE_MAGIC {'B', 'C', 'M', 'N', 'A', 'P', 'A', '\0'}
struct tofe_version {
uint8_t generation;
uint8_t spin;
uint8_t major;
uint8_t minor;
};
enum tofe_chip_variant {
TOFE_CHIP_VARIANT_INVALID,
TOFE_CHIP_VARIANT_PHONE,
TOFE_CHIP_VARIANT_TABLET,
};
/**
@struct tofe_signature
@brief Firmware ROM image signature structure.
*/
#pragma pack(push, 1)
struct tofe_signature {
char magic[TOFE_SIGNATURE_MAGIC_SIZE];
struct tofe_version version;
uint64_t commit;
uint32_t build;
uint16_t compatibility;
uint8_t variant; /* tofe_chip_variant */
uint8_t release_type;
uint8_t release_number;
uint8_t _pad;
uint16_t cust_release_num;
};
#pragma pack(pop)
#define TOFE_SIGNATURE_SIZE sizeof(struct tofe_signature)
/**
@enum tofe_channel_flag
@brief Channel flag field bit assignment.
*/
enum tofe_channel_flag {
TOFE_CHANNEL_FLAG_STATUS_OVERFLOW = 1 << 0,
TOFE_CHANNEL_FLAG_STATUS_LEVEL_TRIGGER = 1 << 1,
TOFE_CHANNEL_FLAG_FWDMA_BUFFER = 1 << 3,
TOFE_CHANNEL_FLAG_FWDMA_ENABLE = 1 << 4,
TOFE_CHANNEL_FLAG_INTERRUPT_ENABLE = 1 << 5,
TOFE_CHANNEL_FLAG_OVERFLOW_STALL = 1 << 6,
TOFE_CHANNEL_FLAG_INBOUND = 1 << 7,
};
enum tofe_toc_index {
TOFE_TOC_INDEX_CHANNEL = 2,
TOFE_TOC_INDEX_TCH = 3,
TOFE_TOC_INDEX_DETECT = 6,
};
enum tofe_channel_id {
TOFE_CHANNEL_ID_TOUCH,
TOFE_CHANNEL_ID_COMMAND,
TOFE_CHANNEL_ID_RESPONSE,
TOFE_CHANNEL_ID_LOG,
};
struct tofe_dmac_header {
uint16_t min_size;
uint16_t size;
};
struct tofe_channel_buffer_header {
struct tofe_dmac_header dmac;
uint8_t channel_id:4;
uint8_t flags:4;
uint8_t seq_number;
uint8_t entry_size;
uint8_t entry_count;
};
struct tofe_channel_buffer {
struct tofe_channel_buffer_header header;
uint32_t data[256];
};
struct tofe_channel_header {
/* Channel ID */
uint8_t channel_id;
/* Number of entries. Limited to 255 entries. */
uint8_t entry_num;
/* Entry size in bytes. Limited to 255 bytes. */
uint8_t entry_size;
/* Number of entries in channel to trigger notification */
uint8_t trig_level;
/* Bit definitions shared with configuration. */
uint8_t flags;
/* Number of datat buffers for this channel */
uint8_t buffer_num;
/* Select the buffer to write [0 .. buffer_num-1]. */
uint8_t buffer_idx;
/* Count the number of buffer swapped for debug. */
uint8_t seq_count;
struct tofe_channel_buffer *buffer[2];
};
struct tofe_channel_instance_cfg {
uint8_t entry_num; /* Must be > 0. */
uint8_t entry_size; /* Range [1..255]. */
uint8_t trig_level; /* 0 - entry_num */
uint8_t flags;
uint8_t buffer_num; /* Number of buffers for this channel */
uint8_t _pad8;
uint16_t offset;
struct tofe_channel_header *channel_header;
void *channel_data;
};
#pragma pack(push, 1)
struct mtc_detect_cfg {
/* compressed structure definition */
uint16_t _pad[17];
int16_t scaling_x_offset;
uint16_t scaling_x_gain;
uint16_t scaling_x_range;
int16_t scaling_y_offset;
uint16_t scaling_y_gain;
uint16_t scaling_y_range;
uint16_t class_finger_gate;
uint16_t _pad0;
uint16_t class_stylus_gate;
};
#pragma pack(pop)
struct tofe_log_msg {
uint16_t log_code;
uint16_t param_0;
uint32_t params[2];
uint32_t timestamp;
};
struct combi_entry {
uint32_t offset;
uint32_t addr;
uint32_t length;
uint32_t flags;
};
/* ------------------------------------- */
/* - BCM Touch Controller Driver Enums - */
/* ------------------------------------- */
enum bcmtch_channel_id {
/* NOTE : see above tofe_channel_id */
BCMTCH_CHANNEL_TOUCH,
BCMTCH_CHANNEL_COMMAND,
BCMTCH_CHANNEL_RESPONSE,
BCMTCH_CHANNEL_LOG,
/* last */
BCMTCH_CHANNEL_MAX
};
enum bcmtch_channel_set {
BCMTCH_RAM_CHANNELS = 0,
BCMTCH_ROM_CHANNELS = 1,
BCMTCH_MAX_CHANNEL_SET
};
/* event kinds from BCM Touch Controller */
enum bcmtch_event_kind {
BCMTCH_EVENT_KIND_RESERVED, /* Avoiding zero, but you may use it. */
BCMTCH_EVENT_KIND_FRAME,
BCMTCH_EVENT_KIND_TOUCH,
BCMTCH_EVENT_KIND_TOUCH_END,
BCMTCH_EVENT_KIND_BUTTON,
BCMTCH_EVENT_KIND_GESTURE,
BCMTCH_EVENT_KIND_EXTENSION = 7,
BCMTCH_EVENT_KIND_MAX = BCMTCH_EVENT_KIND_EXTENSION,
};
enum _bcmtch_touch_status {
BCMTCH_TOUCH_STATUS_INACTIVE,
BCMTCH_TOUCH_STATUS_UP,
BCMTCH_TOUCH_STATUS_MOVE,
BCMTCH_TOUCH_STATUS_MOVING,
};
/* -------------------------------------- */
/* - BCM Touch Controller Device Tables - */
/* -------------------------------------- */
static const uint32_t const BCMTCH_CHIP_IDS[] = {
0x15200,
0x15300,
0x15500,
};
/* ------------------------------------------ */
/* - BCM Touch Controller Driver Parameters - */
/* ------------------------------------------ */
#define BCMTCH_BF_HARD_RESET_ON_LOAD 0x00000001
#define BCMTCH_BF_SOFT_RESET_ON_LOAD 0x00000002
#define BCMTCH_BF_VERIFY_CHIP 0x00000004
#define BCMTCH_BF_DISABLE_POST_BOOT 0x00000008
#define BCMTCH_BF_CHECK_INTERRUPT 0x00000010
#define BCMTCH_BF_SUSPEND_COLD_BOOT 0x00000020
#define BCMTCH_BF_FW_RESET_ON_WD 0x00000040
#define BCMTCH_BF_STATE_PROTOCOL 0x00000080
#if BCMTCH_STATE_PROTOCOL
#define BCMTCH_BF_STATE_SHORT_SLOT 0x00000100
#define BCMTCH_BF_STATE_SYNC_MODE 0x00000200
#endif /* BCMTCH_STATE_PROTOCOL */
#define BCMTCH_BF_RESET_ON_LOAD_MASK \
(BCMTCH_BF_HARD_RESET_ON_LOAD \
| BCMTCH_BF_SOFT_RESET_ON_LOAD)
static int bcmtch_boot_flag =
(BCMTCH_BF_SOFT_RESET_ON_LOAD
| BCMTCH_BF_SUSPEND_COLD_BOOT
| BCMTCH_BF_FW_RESET_ON_WD
#if BCMTCH_STATE_PROTOCOL
| BCMTCH_BF_STATE_SYNC_MODE
#endif
| BCMTCH_BF_VERIFY_CHIP
);
module_param_named(boot_flag, bcmtch_boot_flag, int, S_IRUGO);
MODULE_PARM_DESC(boot_flag, "Boot bit-fields [RAM|RESET]");
/*-*/
#define BCMTCH_CHANNEL_FLAG_USE_TOUCH 0x00000001
#define BCMTCH_CHANNEL_FLAG_USE_CMD_RESP 0x00000002
#define BCMTCH_CHANNEL_FLAG_USE_LOG 0x00000004
static int bcmtch_channel_flag = BCMTCH_CHANNEL_FLAG_USE_TOUCH
| BCMTCH_CHANNEL_FLAG_USE_CMD_RESP;
module_param_named(channel_flag, bcmtch_channel_flag, int, S_IRUGO);
MODULE_PARM_DESC(channel_flag, "Channels allowed bit-fields [L|C/R|T]");
/*-*/
#define BCMTCH_EVENT_FLAG_TOUCH_SIZE 0x00000001
#define BCMTCH_EVENT_FLAG_TOOL_SIZE 0x00000002
#define BCMTCH_EVENT_FLAG_PRESSURE 0x00000004
#define BCMTCH_EVENT_FLAG_ORIENTATION 0x00000008
static int bcmtch_event_flag = BCMTCH_EVENT_FLAG_TOUCH_SIZE
| BCMTCH_EVENT_FLAG_ORIENTATION
| BCMTCH_EVENT_FLAG_PRESSURE;
module_param_named(event_flag, bcmtch_event_flag, int, S_IRUGO);
MODULE_PARM_DESC(event_flag, "Extension events bit-fields [ORIEN|PRESSURE|TOOL_SIZE|TOUCH_SIZE]");
/*- firmware -*/
#define BCMTCH_FIRMWARE_FLAGS_CODE 0x0
#define BCMTCH_FIRMWARE_FLAGS_CONFIGS 0x01
#define BCMTCH_FIRMWARE_FLAGS_POST_BOOT 0x02
#define BCMTCH_FIRMWARE_FLAGS_POST_BOOT_CODE 0x02
#define BCMTCH_FIRMWARE_FLAGS_POST_BOOT_CONFIGS 0x03
#define BCMTCH_FIRMWARE_FLAGS_MASK 0x03
#define BCMTCH_FIRMWARE_FLAGS_ROM_BOOT 0x04
#define BCMTCH_FIRMWARE_FLAGS_POST_BOOT_PATCH 0x08
#define BCMTCH_FIRMWARE_FLAGS_COMBI 0x10
static int bcmtch_firmware_flag = BCMTCH_FIRMWARE_FLAGS_COMBI;
module_param_named(firmware_flag, bcmtch_firmware_flag, int, S_IRUGO);
MODULE_PARM_DESC(firmware_flag, "Firmware flag bit-fields (combi = 0x10 config = 0x01");
static char *bcmtch_firmware;
module_param_named(firmware, bcmtch_firmware, charp, S_IRUGO);
MODULE_PARM_DESC(firmware, "Filename of firmware to load");
static int bcmtch_firmware_addr = 0x0;
module_param_named(firmware_addr, bcmtch_firmware_addr, int, S_IRUGO);
MODULE_PARM_DESC(firmware_addr, "Address to load firmware");
/*- post boot -*/
#define BCMTCH_POST_BOOT_RATE_HIGH (1<<10)
#define BCMTCH_POST_BOOT_RATE_LOW 80
static int bcmtch_post_boot_rate_high = BCMTCH_POST_BOOT_RATE_HIGH;
module_param_named(
pbr_high,
bcmtch_post_boot_rate_high,
int,
S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(pbr_high, "Post Boot Download Rate - High");
static int bcmtch_post_boot_rate_low = BCMTCH_POST_BOOT_RATE_LOW;
module_param_named(pbr_low, bcmtch_post_boot_rate_low, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(pbr_low, "Post Boot Download Rate - Low");
/*- watch dog duration -*/
/* x milliseconds in jiffies */
#define BCMTCH_WATCHDOG_NORMAL 5000
#define BCMTCH_WATCHDOG_POST_BOOT 50
static uint32_t bcmtch_watchdog_normal = BCMTCH_WATCHDOG_NORMAL;
module_param_named(
wdg_normal,
bcmtch_watchdog_normal,
uint,
S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(wdg_normal, "Watch dog rate in normal operation (ms)");
static uint32_t bcmtch_watchdog_post_boot = BCMTCH_WATCHDOG_POST_BOOT;
module_param_named(
wdg_post_boot,
bcmtch_watchdog_post_boot,
uint,
S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(wdg_post_boot, "Watch dog rate at post boot download (ms)");
/*- debug flag -*/
#define BCMTCH_DF_MV 0x00000001 /* touch MOVE event */
#define BCMTCH_DF_UP 0x00000002 /* touch UP event */
#define BCMTCH_DF_TE 0x00000004 /* touch extension events */
#define BCMTCH_DF_BT 0x00000008 /* BUTTON event */
#define BCMTCH_DF_CH 0x00000010 /* channel protocol */
#define BCMTCH_DF_ST 0x00000020 /* state protocol */
#define BCMTCH_DF_RC 0x00000040 /* rm */
#define BCMTCH_DF_RF 0x00000080 /* rf */
#define BCMTCH_DF_FR 0x00000100 /* frame events */
#define BCMTCH_DF_FE 0x00000200 /* frame extension events */
#define BCMTCH_DF_PB 0x00000400 /* post-boot */
#define BCMTCH_DF_DT 0x00000800 /* device tree */
#define BCMTCH_DF_HO 0x00001000 /* host override */
#define BCMTCH_DF_PM 0x00002000 /* power management */
#define BCMTCH_DF_WD 0x00004000 /* watch dog */
#define BCMTCH_DF_IH 0x00008000 /* interrupt */
#define BCMTCH_DF_I2C 0x00010000 /* i2c */
#define BCMTCH_DF_INFO 0x00020000 /* info */
static int bcmtch_debug_flag; /* = 0; */
module_param_named(debug_flag, bcmtch_debug_flag, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug_flag, "Debug Bit-Flag");
/* ---------------------------------------- */
/* - BCM Touch Controller Firmware Tables - */
/* ---------------------------------------- */
#define BCMTCHWC 0x9999
struct firmware_load_info_t {
uint32_t chip_id;
uint32_t chip_rev;
uint8_t *filename;
uint32_t addr;
uint32_t flags;
};
static const struct firmware_load_info_t BCMTCH_BINARIES[] = {
/* MUST ALWAYS START WITH WILDCARD */
{BCMTCHWC, BCMTCHWC, "bcmtchfw_bin", 0, BCMTCH_FIRMWARE_FLAGS_COMBI},
/*
* ADD CHIP SPECIFIC BINARIES HERE
*/
/* CHIP ID specific */
{0x15200,
BCMTCHWC,
"bcmtchfw15200_bin",
0,
BCMTCH_FIRMWARE_FLAGS_COMBI},
{0x15300,
BCMTCHWC,
"bcmtchfw15300_bin",
0,
BCMTCH_FIRMWARE_FLAGS_COMBI},
/*
** CHIP ID & CHIP REV specific
*
*/
};
/* ------------------------------------------ */
/* - BCM Touch Controller Driver Structures - */
/* ------------------------------------------ */
/*- work queue enum -*/
enum bcmtch_work_process_function {
BCMTCH_WP_CHANNEL,
BCMTCH_WP_PATCH_INIT,
BCMTCH_WP_STATE,
BCMTCH_WP_NUMBER
};
/*- bcmtch channel structures -*/
struct bcmtch_channel {
struct tofe_channel_instance_cfg cfg;
struct tofe_channel_header hdr;
uint16_t queued;
uint8_t active;
/* intentional pad - may use for i2c tranactions */
uint8_t _pad8;
uint32_t data;
};
/**
@ struct bcmtch_response_wait
@ brief storage of the results from response channel.
*/
struct bcmtch_response_wait {
bool wait;
uint32_t resp_data;
};
struct bcmtch_event {
uint32_t event_kind:3;
uint32_t _pad:29;
};
enum bcmtch_event_frame_extension_kind {
BCMTCH_EVENT_FRAME_EXTENSION_KIND_TIMESTAMP,
BCMTCH_EVENT_FRAME_EXTENSION_KIND_CHECKSUM,
BCMTCH_EVENT_FRAME_EXTENSION_KIND_HEARTBEAT,
BCMTCH_EVENT_FRAME_EXTENSION_KIND_MAX = 7 /* 3 bits */
};
struct bcmtch_event_frame {
uint32_t event_kind:3;
uint32_t _pad:1;
uint32_t frame_id:12;
uint32_t timestamp:16;
};
struct bcmtch_event_frame_extension {
uint32_t event_kind:3;
uint32_t frame_kind:3;
uint32_t _pad:26;
};
struct bcmtch_event_frame_extension_timestamp {
uint32_t event_kind:3;
uint32_t frame_kind:3;
uint32_t _pad:2;
uint32_t scan_end:8;
uint32_t mtc_start:8;
uint32_t mtc_end:8;
};
struct bcmtch_event_frame_extension_checksum {
uint32_t event_kind:3;
uint32_t frame_kind:3;
uint32_t _pad:10;
uint32_t hash:16;
};
struct bcmtch_event_frame_extension_heartbeat {
uint32_t event_kind:3;
uint32_t frame_kind:3;
uint32_t _pad:2;
uint32_t timestamp:24; /* 100 us units, free running */
};
enum bcmtch_event_touch_extension_kind {
BCMTCH_EVENT_TOUCH_EXTENSION_KIND_DETAIL,
BCMTCH_EVENT_TOUCH_EXTENSION_KIND_BLOB,
BCMTCH_EVENT_TOUCH_EXTENSION_KIND_SIZE,
BCMTCH_EVENT_TOUCH_EXTENSION_KIND_HOVER,
BCMTCH_EVENT_TOUCH_EXTENSION_KIND_TOOL,
BCMTCH_EVENT_TOUCH_EXTENSION_KIND_MAX = 7 /* 3 bits */
};
enum bcmtch_event_touch_tool {
BCMTCH_EVENT_TOUCH_TOOL_FINGER,
BCMTCH_EVENT_TOUCH_TOOL_STYLUS,
};
struct bcmtch_event_touch {
uint32_t event_kind:3;
uint32_t track_tag:5;
uint32_t x:12;
uint32_t y:12;
};
struct bcmtch_event_touch_end {
uint32_t event_kind:3;
uint32_t track_tag:5;
uint32_t _pad:24;
};
struct bcmtch_event_touch_extension {
uint32_t event_kind:3;
uint32_t touch_kind:3;
uint32_t _pad:26;
};
struct bcmtch_event_touch_extension_detail {
uint32_t event_kind:3;
uint32_t touch_kind:3;
uint32_t confident:1;
uint32_t suppressed:1;
uint32_t hover:1;
uint32_t tool:1;
uint32_t large_touch:1;
uint32_t _pad:1;
uint32_t pressure:8;
uint32_t orientation:12;
};
struct bcmtch_event_touch_extension_blob {
uint32_t event_kind:3;
uint32_t touch_kind:3;
uint32_t area:8;
uint32_t total_cap:18;
};
#define BCMTCH_EVENT_TOUCH_EXTENSION_AREA_MAX 255
struct bcmtch_event_touch_extension_size {
uint32_t event_kind:3;
uint32_t touch_kind:3;
uint32_t _pad:6;
uint32_t major_axis:10;
uint32_t minor_axis:10;
};
struct bcmtch_event_touch_extension_hover {
uint32_t event_kind:3;
uint32_t touch_kind:3;
uint32_t _pad:16;
uint32_t height:10;
};
struct bcmtch_event_touch_extension_tool {
uint32_t event_kind:3;
uint32_t touch_kind:3;
uint32_t _pad:6;
uint32_t width_major:10;
uint32_t width_minor:10;
};
enum bcmtch_event_button_kind {
BCMTCH_EVENT_BUTTON_KIND_CONTACT,
BCMTCH_EVENT_BUTTON_KIND_HOVER,
BCMTCH_EVENT_BUTTON_KIND_MAX = 1
};
struct bcmtch_event_button {
uint32_t event_kind:3;
uint32_t button_kind:1;
uint32_t _pad:12;
uint32_t status:16;
};
/* driver structure for a single touch point */
struct bcmtch_touch {
uint16_t x; /* X Coordinate */
uint16_t y; /* Y Coordinate */
uint16_t major_axis;
uint16_t minor_axis;
uint16_t width_major;
uint16_t width_minor;
uint8_t type;
uint8_t pressure;
int16_t orientation;
/* Touch status: Down, Move, Up (Inactive) */
enum _bcmtch_touch_status status;
enum bcmtch_event_kind event; /* Touch Event Kind */
};
#if BCMTCH_STATE_PROTOCOL
/* ---------------------------- */
/* - State Protocol Constants - */
/* ---------------------------- */
enum bcmtch_state_protocol_cmd {
BCMTCH_CMD_LONG = 0x20,
BCMTCH_CMD_START_SCAN = 0x21,
BCMTCH_CMD_STOP_SCAN = 0x22,
BCMTCH_CMD_SUSPEND = 0x23,
BCMTCH_CMD_RESUME = 0x24,
BCMTCH_CMD_SLEEP = 0x28,
BCMTCH_CMD_SINGLE_SCAN = 0x30,
BCMTCH_CMD_READ_RAM = 0x31,
BCMTCH_CMD_WRITE_RAM = 0x32,
};
enum bcmtch_state_slot_type {
STATE_SLOT_TYPE_EMPTY = 0,
STATE_SLOT_TYPE_BUTTON = 1,
STATE_SLOT_TYPE_STYLUS = 2,
STATE_SLOT_TYPE_FINGER = 8,
STATE_SLOT_TYPE_RESERVED = 96,
};
#define TOFE_HOST_CMD_BUF_SIZE 512
#define TOFE_HOST_CMD_SHORT_SIZE 4
#define TOFE_HOST_MAX_NUM_SLOTS 15
#define TOFE_HOST_TOUCH_SLOT_SMALL_SIZE 4
#define TOFE_HOST_TOUCH_SLOT_BIG_SIZE 8
#define TOFE_HOST_LOG_SLOT_SIZE 12
#define TOFE_HOST_SLOT_AREA_MIN 1
#define TOFE_HOST_EXCEPTION_REGS 24
#define TOFE_HOST_RSP_BUF_SIZE \
(BCMTCH_MAX_TOUCH \
* TOFE_HOST_TOUCH_SLOT_BIG_SIZE + 1)
/*- State protocol RAM access configuration */
struct bcmtch_state_cfg_rw {
uint16_t cfg_size;
uint16_t max_read_size;
uint16_t max_write_size;
uint16_t patch_cfg_max_size;
uint32_t patch_cfg_addr;
};
#pragma pack(push, 1)
struct bcmtch_state_rw_params {
uint32_t addr;
uint16_t len;
};
#pragma pack(pop)
/*- Touch scan command/response format. -*/
union bcmtch_state_cmd_start_scan {
struct {
uint32_t touch_slot_format:3;
uint32_t sync_mode:1;
uint32_t max_slot_number:4;
};
uint8_t reg;
};
union bcmtch_state_ret_start_scan {
struct {
uint32_t slots:4;
uint32_t frame:3;
uint32_t done:1;
};
uint8_t reg;
};
struct bcmtch_state_start_scan {
union bcmtch_state_cmd_start_scan cmd;
union bcmtch_state_ret_start_scan status;
uint8_t touch_slot_size;
uint8_t touch_slot_num;
uint8_t log_slot_num;
};
union bcmtch_state_resp_slot {
uint8_t type:7;
uint8_t frame:1;
struct {
uint32_t type:7;
uint32_t frame:1;
uint32_t x:12;
uint32_t y:12;
uint32_t pressure:8;
uint32_t orientation:8;
uint32_t major:8;
uint32_t minor:8;
} long_slot;
struct {
uint32_t type:7;
uint32_t frame:1;
uint32_t x:12;
uint32_t y:12;
} short_slot;
struct {
uint32_t pad:8;
uint32_t buttons:24;
} button_slot;
struct {
uint32_t code_low:7;
uint32_t frame:1;
uint32_t code_hi:8;
uint32_t param0:16;
uint32_t param1;
uint32_t param2;
} log_slot;
};
#endif /* BCMTCH_STATE_PROTOCOL */
struct bcmtch_data {
/* core 0S elements */
/* Work queue structure for defining work queue handler */
struct work_struct work;
/* Work queue structure for transaction handling */
struct workqueue_struct *p_workqueue;
/* The table of worker process functions */
void (*bcmtch_dev_process_table[BCMTCH_WP_NUMBER])
(struct bcmtch_data *);
/* The index of the process function table */
uint8_t work_process_index;
/* Critical Section : Mutex : */
struct mutex mutex_work;
/* Pointer to allocated memory for input device */
struct input_dev *p_input_device;
struct device *p_device;
/* I2C 0S elements */
/* SPM I2C Client structure pointer */
struct i2c_client *p_i2c_client_spm;
/* SYS I2C Client structure pointer */
struct i2c_client *p_i2c_client_sys;
/* Local copy of platform data structure */
struct bcmtch_platform_data platform_data;
/* BCM Touch elements */
struct bcmtch_channel *p_channels
[BCMTCH_MAX_CHANNEL_SET][BCMTCH_CHANNEL_MAX];
/* BCMTCH touch structure */
struct bcmtch_touch touch[BCMTCH_MAX_TOUCH];
uint32_t touch_count;
uint8_t touch_event_track_id;
/* Response storage */
struct bcmtch_response_wait bcmtch_cmd_response[TOFE_COMMAND_LAST];
/* BCM Button elements */
uint16_t button_status;
#ifdef CONFIG_OF
const int32_t bcmtch_button_map[BCMTCH_MAX_BUTTONS];
#endif
/* DMA transfer mode */
bool has_dma_channel;
bool host_override;
uint32_t fw_dma_buffer_size;
void *fw_dma_buffer;
/* Interrupts */
bool irq_pending;
bool irq_enabled;
#ifdef CONFIG_REGULATOR
struct regulator *regulator_avdd33;
struct regulator *regulator_vddo;
struct regulator *regulator_avdd_adldo;
#endif
/* Power Management */
uint32_t power_on_delay_us; /* us wait time after power on */
/* Watchdog Timer */
uint32_t watchdog_expires;
struct timer_list watchdog;
/* Post Boot Downloads */
uint8_t channel_set; /* ROM/RAM channels */
uint8_t post_boot_pending;
uint8_t *post_boot_buffer;
uint8_t *post_boot_data;
uint32_t post_boot_addr;
uint16_t post_boot_left;
uint16_t post_boot_sections;
uint16_t post_boot_section;
uint16_t post_boot_patches;
uint32_t postboot_cfg_addr;
uint32_t postboot_cfg_length;
/* Axis Limits */
uint16_t axis_x_max;
uint16_t axis_x_min;
uint16_t axis_y_max;
uint16_t axis_y_min;
uint16_t axis_h_max;
uint16_t axis_h_min;
/* Thresholds */
uint16_t threshold_gate_finger;
uint16_t threshold_gate_stylus;
/* FW Signature */
struct tofe_signature fw_signature;
/* BCMTCH chip ID */
uint32_t chip_id;
/* BCMTCH revision ID */
uint8_t rev_id;
/* ROM boot */
bool boot_from_rom;
#ifdef CONFIG_HAS_EARLYSUSPEND
/* Early suspend */
struct early_suspend bcmtch_early_suspend_desc;
#endif
#if BCMTCH_STATE_PROTOCOL
struct bcmtch_state_cfg_rw ram_rw_cfg;
struct bcmtch_state_start_scan scan_data;
uint32_t exception_buffer[TOFE_HOST_EXCEPTION_REGS];
uint8_t bcmtch_state_cmd_buffer[TOFE_HOST_CMD_BUF_SIZE];
uint8_t bcmtch_state_resp_buffer[TOFE_HOST_RSP_BUF_SIZE];
#endif /* BCMTCH_STATE_PROTOCOL */
/* sysfs ABI data */
struct mutex mutex_abi;
uint8_t abi_spm_addr;
uint32_t abi_sys_addr;
uint16_t abi_sys_size;
bool abi_suspend;
};
/* -------------------------------------------- */
/* - BCM Touch Controller Function Prototypes - */
/* -------------------------------------------- */
/* DEV Prototypes */
static void bcmtch_dev_process(
struct bcmtch_data *);
static void bcmtch_dev_process_pb_patch_init(
struct bcmtch_data *);
static int32_t bcmtch_dev_reset(struct bcmtch_data *,
uint8_t);
static int32_t bcmtch_dev_suspend(
struct bcmtch_data *);
static int32_t bcmtch_dev_resume(
struct bcmtch_data *);
static int32_t bcmtch_dev_request_power_mode(
struct bcmtch_data *,
uint8_t, enum tofe_command);
static int32_t bcmtch_dev_send_command(
struct bcmtch_data *,
enum tofe_command, uint32_t, uint16_t, uint8_t);
static inline bool bcmtch_dev_verify_buffer_header(
struct bcmtch_data *,
struct tofe_channel_buffer_header*);
static int32_t bcmtch_dev_post_boot_download(
struct bcmtch_data *bcmtch_data_ptr,
int16_t data_rate);
static int32_t bcmtch_dev_post_boot_get_section(
struct bcmtch_data *);
static void bcmtch_dev_watchdog_work(unsigned long int data);
static void bcmtch_dev_watchdog_start(
struct bcmtch_data *);
static int32_t bcmtch_dev_watchdog_reset(
struct bcmtch_data *);
static int32_t bcmtch_dev_watchdog_stop(
struct bcmtch_data *);
static int32_t bcmtch_dev_watchdog_restart(
struct bcmtch_data *, uint32_t);
static int32_t bcmtch_dev_watchdog_check(
struct bcmtch_data *);
static int32_t bcmtch_dev_power_init(
struct bcmtch_data *);
static int32_t bcmtch_dev_power_enable(
struct bcmtch_data *,
bool);
static int32_t bcmtch_dev_power_free(
struct bcmtch_data *);
static void bcmtch_dev_reset_events(
struct bcmtch_data *bcmtch_data_ptr);
static int32_t bcmtch_dev_sync_event_frame(
struct bcmtch_data *bcmtch_data_ptr);
#if BCMTCH_STATE_PROTOCOL
static void bcmtch_dev_init_state(
struct bcmtch_data *);
static void bcmtch_dev_process_state_touches(
struct bcmtch_data *);
static int32_t bcmtch_state_start_scan(
struct bcmtch_data *);
static int32_t bcmtch_state_stop_scan(
struct bcmtch_data *);
static int32_t bcmtch_state_sleep(
struct bcmtch_data *);
static int32_t bcmtch_state_suspend(
struct bcmtch_data *);
static int32_t bcmtch_state_resume(
struct bcmtch_data *);
static int32_t bcmtch_state_write_ram(
struct bcmtch_data *, uint32_t, uint16_t, uint8_t *);
static int32_t bcmtch_state_read_ram(
struct bcmtch_data *, uint32_t, uint16_t, uint8_t *);
static int32_t bcmtch_state_check_command_status(
struct bcmtch_data *, uint8_t);
static int32_t bcmtch_state_i2c_send_command(
struct bcmtch_data *, uint8_t, uint8_t*, int);
#endif /* BCMTCH_STATE_PROTOCOL */
/* COM Prototypes */
static int32_t bcmtch_com_init(struct bcmtch_data *);
static int32_t bcmtch_com_read_spm(
struct bcmtch_data *, uint8_t, uint8_t*);
static int32_t bcmtch_com_write_spm(
struct bcmtch_data *, uint8_t, uint8_t);
static int32_t bcmtch_com_read_sys(
struct bcmtch_data *, uint32_t, uint16_t, uint8_t*);
static int32_t bcmtch_com_write_sys(
struct bcmtch_data *, uint32_t, uint16_t, uint8_t*);
static int32_t bcmtch_com_read_dma(
struct bcmtch_data *, uint16_t, uint8_t*);
/* COM Helper */
static inline int32_t bcmtch_com_fast_write_spm(
struct bcmtch_data *, uint8_t, uint8_t*, uint8_t*);
static inline int32_t bcmtch_com_write_sys32(
struct bcmtch_data *, uint32_t, uint32_t);
/* OS Prototypes */
static void bcmtch_reset(struct bcmtch_data *);
static int32_t bcmtch_interrupt_enable(
struct bcmtch_data *);
static void bcmtch_interrupt_disable(
struct bcmtch_data *);
static void bcmtch_deferred_worker(
struct work_struct *work);
static void bcmtch_clear_deferred_worker(
struct bcmtch_data *);
#ifdef CONFIG_PM
#ifndef CONFIG_HAS_EARLYSUSPEND
static int bcmtch_suspend(
struct i2c_client *p_client,
pm_message_t mesg);
static int bcmtch_resume(struct i2c_client *p_client);
#endif
#endif
/* OS I2C Prototypes */
static int32_t bcmtch_i2c_probe(
struct i2c_client*,
const struct i2c_device_id *);
static int32_t bcmtch_i2c_remove(struct i2c_client *);
static int32_t bcmtch_i2c_read_spm(struct i2c_client*, uint8_t, uint8_t*);
static int32_t bcmtch_i2c_write_spm(struct i2c_client*, uint8_t, uint8_t);
static int32_t bcmtch_i2c_fast_write_spm(
struct i2c_client*,
uint8_t,
uint8_t*,
uint8_t*);
static int32_t bcmtch_i2c_read_sys(
struct i2c_client*,
uint32_t,
uint16_t,
uint8_t*);
static int32_t bcmtch_i2c_write_sys(
struct i2c_client*,
uint32_t,
uint16_t,
uint8_t*);
static int32_t bcmtch_i2c_init_clients(struct i2c_client *);
static void bcmtch_i2c_free_clients(struct bcmtch_data *);
static int32_t bcmtch_i2c_read_dma(
struct i2c_client*,
uint16_t,
uint8_t*);
/* ------------------------------------------------- */
/* - BCM Touch Controller SysFs ABI Implementation - */
/* ------------------------------------------------- */
/* -- SYS peek/poke ABI -- */
static ssize_t bcmtch_os_abi_sys_addr_show(
struct device *dev,
struct device_attribute *devattr,
char *buf)
{
int count = 0;
struct bcmtch_data *bcmtch_data_ptr =
dev_get_drvdata(dev);
count += snprintf(buf + count, PAGE_SIZE - count,
"0x%04X %u\n",
bcmtch_data_ptr->abi_sys_addr,
bcmtch_data_ptr->abi_sys_size);
return count;
}
static ssize_t bcmtch_os_abi_sys_addr_store(
struct device *dev,
struct device_attribute *devattr,
const char *buf,
size_t count)
{
int ret_val = 0;
struct bcmtch_data *bcmtch_data_ptr =
dev_get_drvdata(dev);
uint32_t addr;
unsigned int len;
ret_val = sscanf(buf, "0x%x %u", &addr, &len);
if (ret_val != 2) {
BCMTCH_ERR(
"format: 0x<32bit address> <memory chunk size in bytes>\n");
return -EINVAL;
}
mutex_lock(&bcmtch_data_ptr->mutex_abi);
bcmtch_data_ptr->abi_sys_addr = addr;
bcmtch_data_ptr->abi_sys_size = (uint16_t)len;
mutex_unlock(&bcmtch_data_ptr->mutex_abi);
return count;
}
static DEVICE_ATTR(sys_addr, 0664,
bcmtch_os_abi_sys_addr_show,
bcmtch_os_abi_sys_addr_store);
static ssize_t bcmtch_os_abi_sys_data_show(
struct device *dev,
struct device_attribute *devattr,
char *buf)
{
int count = 0;
int32_t ret_val = 0;
struct bcmtch_data *bcmtch_data_ptr =
dev_get_drvdata(dev);
int i;
uint8_t r8[512];
if (bcmtch_data_ptr->abi_sys_size > 512)
return -EINVAL;
mutex_lock(&bcmtch_data_ptr->mutex_work);
#if BCMTCH_STATE_PROTOCOL
if (bcmtch_data_ptr->work_process_index >= BCMTCH_WP_STATE)
ret_val = bcmtch_state_read_ram(
bcmtch_data_ptr,
bcmtch_data_ptr->abi_sys_addr,
bcmtch_data_ptr->abi_sys_size,
r8);
else
#endif
ret_val = bcmtch_com_read_sys(
bcmtch_data_ptr,
bcmtch_data_ptr->abi_sys_addr,
bcmtch_data_ptr->abi_sys_size,
r8);
mutex_unlock(&bcmtch_data_ptr->mutex_work);
if (!ret_val) {
for (i = 0; i < bcmtch_data_ptr->abi_sys_size; i++) {
count += snprintf(buf + count, PAGE_SIZE - count,
"%02X ", r8[i]);
}
count += snprintf(buf + count,
PAGE_SIZE - count, "\n");
}
return count;
}
static ssize_t bcmtch_os_abi_sys_data_store(
struct device *dev,
struct device_attribute *devattr,
const char *buf,
size_t count)
{
int ret_val = 0;
struct bcmtch_data *bcmtch_data_ptr =
dev_get_drvdata(dev);
char tok[5] = "0xff";
unsigned int in_data;
uint8_t r8[512];
int i, j, k;
for (i = 0, j = 0, k = 0; i < count; i++) {
if (buf[i] == ' ' || buf[i] == '\t' || buf[i] == '\n') {
if (j) {
tok[j] = '\0';
ret_val = kstrtouint(tok, 16, &in_data);
if (!ret_val)
r8[k++] = (uint8_t)in_data;
}
j = 0;
continue;
}
tok[j++] = buf[i];
if (j > 4) {
j = 0;
continue;
}
}
if (k < bcmtch_data_ptr->abi_sys_size) {
BCMTCH_ERR("ERROR: only read %u out of %u data\n",
k, bcmtch_data_ptr->abi_sys_size);
return -EINVAL;
}
mutex_lock(&bcmtch_data_ptr->mutex_work);
#if BCMTCH_STATE_PROTOCOL
if (bcmtch_data_ptr->work_process_index >= BCMTCH_WP_STATE)
bcmtch_state_write_ram(
bcmtch_data_ptr,
bcmtch_data_ptr->abi_sys_addr,
bcmtch_data_ptr->abi_sys_size,
r8);
else
#endif
bcmtch_com_write_sys(
bcmtch_data_ptr,
bcmtch_data_ptr->abi_sys_addr,
bcmtch_data_ptr->abi_sys_size,
r8);
mutex_unlock(&bcmtch_data_ptr->mutex_work);
return count;
}
static DEVICE_ATTR(sys_data, 0664,
bcmtch_os_abi_sys_data_show,
bcmtch_os_abi_sys_data_store);
/* -- SPM peek/poke ABI -- */
static ssize_t bcmtch_os_abi_spm_addr_show(
struct device *dev,
struct device_attribute *devattr,
char *buf)
{
int count = 0;
struct bcmtch_data *bcmtch_data_ptr =
dev_get_drvdata(dev);
count += snprintf(buf + count, PAGE_SIZE - count,
"0x%02x\n", bcmtch_data_ptr->abi_spm_addr);
return count;
}
static ssize_t bcmtch_os_abi_spm_addr_store(
struct device *dev,
struct device_attribute *devattr,
const char *buf,
size_t count)
{
int ret_val = 0;
struct bcmtch_data *bcmtch_data_ptr =
dev_get_drvdata(dev);
unsigned int addr;
ret_val = kstrtouint(buf, 16, &addr);
if (ret_val)
return ret_val;
mutex_lock(&bcmtch_data_ptr->mutex_abi);
bcmtch_data_ptr->abi_spm_addr = (uint8_t)addr;
mutex_unlock(&bcmtch_data_ptr->mutex_abi);
return count;
}
static DEVICE_ATTR(spm_addr, 0664,
bcmtch_os_abi_spm_addr_show,
bcmtch_os_abi_spm_addr_store);
static ssize_t bcmtch_os_abi_spm_data_show(
struct device *dev,
struct device_attribute *devattr,
char *buf)
{
int count = 0;
struct bcmtch_data *bcmtch_data_ptr =
dev_get_drvdata(dev);
uint8_t r8;
mutex_lock(&bcmtch_data_ptr->mutex_work);
bcmtch_com_read_spm(bcmtch_data_ptr,
bcmtch_data_ptr->abi_spm_addr, &r8);
mutex_unlock(&bcmtch_data_ptr->mutex_work);
count += snprintf(buf + count, PAGE_SIZE - count,
"0x%02x\n", r8);
return count;
}
static ssize_t bcmtch_os_abi_spm_data_store(
struct device *dev,
struct device_attribute *devattr,
const char *buf,
size_t count)
{
int ret_val = 0;
struct bcmtch_data *bcmtch_data_ptr =
dev_get_drvdata(dev);
unsigned int in_data;
ret_val = kstrtouint(buf, 16, &in_data);
if (ret_val)
return ret_val;
mutex_lock(&bcmtch_data_ptr->mutex_work);
bcmtch_com_write_spm(bcmtch_data_ptr,
bcmtch_data_ptr->abi_spm_addr,
(uint8_t)in_data);
mutex_unlock(&bcmtch_data_ptr->mutex_work);
return count;
}
static DEVICE_ATTR(spm_data, 0664,
bcmtch_os_abi_spm_data_show,
bcmtch_os_abi_spm_data_store);
/* -- Finger threshold ABI -- */
static ssize_t bcmtch_os_abi_finger_threshold(
struct device *dev,
struct device_attribute *devattr,
char *buf)
{
int count = 0;
struct bcmtch_data *bcmtch_data_ptr =
dev_get_drvdata(dev);
count += snprintf(buf + count, PAGE_SIZE - count,
"%u\n", bcmtch_data_ptr->threshold_gate_finger);
return count;
}
static DEVICE_ATTR(
finger_threshold,
0444,
bcmtch_os_abi_finger_threshold,
NULL);
/* -- Stylus threshold ABI -- */
static ssize_t bcmtch_os_abi_stylus_threshold(
struct device *dev,
struct device_attribute *devattr,
char *buf)
{
int count = 0;
struct bcmtch_data *bcmtch_data_ptr =
dev_get_drvdata(dev);
count += snprintf(buf + count, PAGE_SIZE - count,
"%u\n", bcmtch_data_ptr->threshold_gate_stylus);
return count;
}
static DEVICE_ATTR(
stylus_threshold,
0444,
bcmtch_os_abi_stylus_threshold,
NULL);
/* -- Suspend ABI -- */
static ssize_t bcmtch_os_abi_suspend_show(
struct device *dev,
struct device_attribute *devattr,
char *buf)
{
int count = 0;
struct bcmtch_data *bcmtch_data_ptr =
dev_get_drvdata(dev);
count += snprintf(buf + count, PAGE_SIZE - count,
"%u\n",
bcmtch_data_ptr->abi_suspend ? 1 : 0);
return count;
}
static ssize_t bcmtch_os_abi_suspend_store(
struct device *dev,
struct device_attribute *devattr,
const char *buf,
size_t count)
{
int ret_val = 0;
struct bcmtch_data *bcmtch_data_ptr =
dev_get_drvdata(dev);
unsigned int val;
ret_val = kstrtouint(buf, 10, &val);
if (ret_val)
goto abi_suspend_error;
switch (val) {
case 0:
if (bcmtch_data_ptr->abi_suspend)
bcmtch_dev_resume(bcmtch_data_ptr);
break;
case 1:
if (!bcmtch_data_ptr->abi_suspend)
bcmtch_dev_suspend(bcmtch_data_ptr);
break;
default:
goto abi_suspend_error;
}
return count;
abi_suspend_error:
BCMTCH_ERR("ERROR: suspend ABI - set 0 or 1\n");
return -EINVAL;
}
static DEVICE_ATTR(suspend, 0664,
bcmtch_os_abi_suspend_show,
bcmtch_os_abi_suspend_store);
static struct attribute *bcmtch_abi_attrs[] = {
&dev_attr_spm_addr.attr,
&dev_attr_spm_data.attr,
&dev_attr_sys_addr.attr,
&dev_attr_sys_data.attr,
&dev_attr_finger_threshold.attr,
&dev_attr_stylus_threshold.attr,
&dev_attr_suspend.attr,
NULL
};
static const struct attribute_group bcmtch_attr_group = {
.attrs = bcmtch_abi_attrs,
};
static int32_t bcmtch_os_init_abi(struct device *p_device)
{
int32_t ret_val = 0;
struct bcmtch_data *bcmtch_data_ptr =
dev_get_drvdata(p_device);
ret_val = sysfs_create_group(&p_device->kobj, &bcmtch_attr_group);
if (ret_val)
BCMTCH_ERR(
"ERROR: %s() - device_create_file() failed!\n",
__func__);
bcmtch_data_ptr->abi_suspend = false;
return ret_val;
}
static inline void bcmtch_os_free_abi(struct device *p_device)
{
sysfs_remove_group(&p_device->kobj, &bcmtch_attr_group);
}
/* ------------------------------------------- */
/* - BCM Touch Controller CLI Implementation - */
/* ------------------------------------------- */
static void bcmtch_os_cli_versions(
struct bcmtch_data *bcmtch_data_ptr)
{
/* Return driver and firmware version info */
BCMTCH_INFO("Driver: %s : %s : %s\n",
BCMTCH_DRIVER_VERSION,
BCMTCH_DRIVER_BUILD_DATE,
BCMTCH_DRIVER_BUILD_TIME);
BCMTCH_INFO("F/W Version: %d.%d.%d.%d\n",
bcmtch_data_ptr->fw_signature.version.generation,
bcmtch_data_ptr->fw_signature.version.spin,
bcmtch_data_ptr->fw_signature.version.major,
bcmtch_data_ptr->fw_signature.version.minor);
BCMTCH_INFO("F/W Commit: %016llx\n",
bcmtch_data_ptr->fw_signature.commit);
BCMTCH_INFO(
"F/W Build:%d Compatibility:%d Variant:%d\n",
bcmtch_data_ptr->fw_signature.build,
bcmtch_data_ptr->fw_signature.compatibility,
bcmtch_data_ptr->fw_signature.variant);
BCMTCH_INFO(
"F/W ReleaseType:%c ReleaseNum:%d CustReleaseNum:%d\n",
bcmtch_data_ptr->fw_signature.release_type,
bcmtch_data_ptr->fw_signature.release_number,
bcmtch_data_ptr->fw_signature.cust_release_num);
}
static void bcmtch_os_cli_logmask_get(
struct bcmtch_data *bcmtch_data_ptr,
uint32_t in_logmask)
{
struct bcmtch_response_wait *p_resp;
uint32_t logmask;
uint32_t ret_val;
BCMTCH_INFO("get logmask module_id=0x%04x\n",
in_logmask);
logmask = (uint32_t)(in_logmask << 16);
p_resp = (struct bcmtch_response_wait *)
&(bcmtch_data_ptr->bcmtch_cmd_response
[TOFE_COMMAND_GET_LOG_MASK]);
p_resp->wait = 1;
mutex_lock(&bcmtch_data_ptr->mutex_work);
ret_val = bcmtch_dev_send_command(
bcmtch_data_ptr,
TOFE_COMMAND_GET_LOG_MASK,
logmask,
0,
TOFE_COMMAND_FLAG_REQUEST_RESULT);
if (ret_val != 0) {
BCMTCH_ERR("send_command error [%d] cmd=%x\n",
ret_val,
TOFE_COMMAND_GET_LOG_MASK);
}
mutex_unlock(&bcmtch_data_ptr->mutex_work);
}
static void bcmtch_os_cli_logmask_set(
struct bcmtch_data *bcmtch_data_ptr,
uint32_t in_logmask)
{
struct bcmtch_response_wait *p_resp;
BCMTCH_INFO("set logmask mask=0x%08x . . .\n",
in_logmask);
p_resp = (struct bcmtch_response_wait *)
&(bcmtch_data_ptr->bcmtch_cmd_response
[TOFE_COMMAND_SET_LOG_MASK]);
p_resp->wait = 1;
mutex_lock(&bcmtch_data_ptr->mutex_work);
bcmtch_dev_send_command(
bcmtch_data_ptr,
TOFE_COMMAND_SET_LOG_MASK,
in_logmask,
0,
TOFE_COMMAND_FLAG_REQUEST_RESULT);
mutex_unlock(&bcmtch_data_ptr->mutex_work);
}
static void bcmtch_os_cli_spm_poke(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t in_addr,
uint8_t in_data)
{
mutex_lock(&bcmtch_data_ptr->mutex_work);
bcmtch_com_write_spm(bcmtch_data_ptr,
in_addr, in_data);
mutex_unlock(&bcmtch_data_ptr->mutex_work);
BCMTCH_INFO("poke spm Reg=%08x data=%08x\n",
in_addr, in_data);
}
static void bcmtch_os_cli_spm_peek(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t in_addr)
{
uint8_t r8;
mutex_lock(&bcmtch_data_ptr->mutex_work);
bcmtch_com_read_spm(bcmtch_data_ptr, in_addr, &r8);
mutex_unlock(&bcmtch_data_ptr->mutex_work);
BCMTCH_INFO("peek spm reg=0x%02x data=0x%02x\n",
in_addr, r8);
}
static void bcmtch_os_cli_sys_poke(
struct bcmtch_data *bcmtch_data_ptr,
uint32_t in_addr,
uint32_t in_data)
{
mutex_lock(&bcmtch_data_ptr->mutex_work);
#if BCMTCH_STATE_PROTOCOL
if (bcmtch_data_ptr->work_process_index >= BCMTCH_WP_STATE)
bcmtch_state_write_ram(
bcmtch_data_ptr,
in_addr,
sizeof(uint32_t),
(uint8_t *)&in_data);
else
#endif
bcmtch_com_write_sys32(bcmtch_data_ptr, in_addr, in_data);
mutex_unlock(&bcmtch_data_ptr->mutex_work);
BCMTCH_INFO("poke sys addr=0x%08x data=0x%08x\n",
in_addr, in_data);
}
static void bcmtch_os_cli_sys_peek(
struct bcmtch_data *bcmtch_data_ptr,
uint32_t in_addr,
uint32_t in_count)
{
uint32_t r_buf[8];
uint32_t addr = in_addr;
uint32_t count = in_count;
memset(r_buf, 0, 8 * sizeof(uint32_t));
BCMTCH_INFO("peek sys addr=0x%08x len=0x%08x\n",
in_addr, in_count);
mutex_lock(&bcmtch_data_ptr->mutex_work);
while (count) {
#if BCMTCH_STATE_PROTOCOL
if (bcmtch_data_ptr->work_process_index >= BCMTCH_WP_STATE)
bcmtch_state_read_ram(
bcmtch_data_ptr,
addr,
8 * sizeof(uint32_t),
(uint8_t *)r_buf);
else
#endif
bcmtch_com_read_sys(bcmtch_data_ptr,
addr, 8 * sizeof(uint32_t),
(uint8_t *)r_buf);
BCMTCH_INFO(
"0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
addr,
r_buf[0], r_buf[1], r_buf[2], r_buf[3],
r_buf[4], r_buf[5], r_buf[6], r_buf[7]);
count =
(count > (8 * sizeof(uint32_t))) ?
(count - (8 * sizeof(uint32_t))) :
0;
addr += (8 * sizeof(uint32_t));
}
mutex_unlock(&bcmtch_data_ptr->mutex_work);
}
static ssize_t bcmtch_os_cli(
struct device *dev,
struct device_attribute *devattr,
const char *buf,
size_t count)
{
uint32_t in_addr = 0;
uint32_t in_value_count = 0;
struct bcmtch_data *bcmtch_data_ptr =
dev_get_drvdata(dev);
/* We are now checking for exact number of matches
* to the format. Buf and format are const.
* sscanf will return 0 on first mismatch.
* We don't anticipate buf to be NULL here
* Annotate this if coverity flags it
*/
/* In future, we should also veriify that address
* is valid. We might change this interface in
* near future so leave that part as is
*/
if ((count > strlen("poke sys 0x 0x")) &&
sscanf(buf, "poke sys %x %x", &in_addr,
&in_value_count) == 2) {
bcmtch_os_cli_sys_poke(bcmtch_data_ptr,
in_addr, in_value_count);
} else if ((count > strlen("peek sys 0x 0x")) &&
sscanf(buf, "peek sys %x %x", &in_addr,
&in_value_count) == 2) {
bcmtch_os_cli_sys_peek(bcmtch_data_ptr,
in_addr, in_value_count);
} else if ((count > strlen("poke spm 0x 0x")) &&
sscanf(buf, "poke spm %x %x", &in_addr,
&in_value_count) == 2) {
bcmtch_os_cli_spm_poke(bcmtch_data_ptr,
in_addr & 0xff, in_value_count & 0xff);
} else if ((count > strlen("peek spm 0x")) &&
sscanf(buf, "peek spm %x",
&in_addr) == 1) {
bcmtch_os_cli_spm_peek(bcmtch_data_ptr,
in_addr & 0xff);
} else if ((count > strlen("set logmask 0x")) &&
sscanf(buf, "set logmask %x",
&in_value_count) == 1) {
bcmtch_os_cli_logmask_set(bcmtch_data_ptr,
in_value_count);
} else if ((count > strlen("get logmask 0x")) &&
sscanf(buf, "get logmask %x",
&in_value_count) == 1) {
bcmtch_os_cli_logmask_get(bcmtch_data_ptr,
in_value_count);
}
#if defined(CONFIG_PM)
else if (strncmp(buf, "suspend", strlen("suspend")) == 0) {
BCMTCH_INFO("cli --> suspend\n");
bcmtch_dev_suspend(bcmtch_data_ptr);
} else if (strncmp(buf, "resume", strlen("resume")) == 0) {
BCMTCH_INFO("cli --> resume\n");
bcmtch_dev_resume(bcmtch_data_ptr);
}
#endif
else if (strncmp(buf, "threshold finger",
strlen("threshold finger")) == 0) {
BCMTCH_INFO("finger threshold = %u\n",
bcmtch_data_ptr->threshold_gate_finger);
} else if (strncmp(buf, "threshold stylus",
strlen("threshold stylus")) == 0) {
BCMTCH_INFO("stylus threshold = %u\n",
bcmtch_data_ptr->threshold_gate_stylus);
} else if (strncmp(buf, "versions", strlen("versions")) == 0) {
bcmtch_os_cli_versions(bcmtch_data_ptr);
} else {
BCMTCH_INFO("Usage:\n");
BCMTCH_INFO("poke sys 0x<addr> 0x<data>\n");
BCMTCH_INFO("peek sys 0x<addr> 0x<len>\n");
BCMTCH_INFO("poke spm 0x<reg> 0x<data>\n");
BCMTCH_INFO("peek spm 0x<reg>\n");
BCMTCH_INFO("set logmask 0x<module>\n");
BCMTCH_INFO("get logmask 0x<mask - bitmap>\n");
#if defined(CONFIG_PM)
BCMTCH_INFO("suspend\n");
BCMTCH_INFO("resume\n");
#endif
BCMTCH_INFO("threshold finger\n");
BCMTCH_INFO("threshold stylus\n");
BCMTCH_INFO("versions\n");
}
return count;
}
static struct device_attribute bcmtch_cli_attr =
__ATTR(cli, S_IWUSR|S_IWGRP, NULL, bcmtch_os_cli);
/* ------------------------------------------- */
/* - BCM Touch Controller Internal Functions - */
/* ------------------------------------------- */
static inline
unsigned bcmtch_channel_num_queued(
struct tofe_channel_header *channel)
{
return (unsigned)channel->
buffer[0]->header.entry_count;
}
/*
Note: Internal use only function.
*/
static inline char *
_bcmtch_inline_channel_entry(
struct tofe_channel_header *channel,
uint32_t byte_index)
{
return (char *)channel->buffer[0]->data
+ byte_index;
}
/*
Note: Internal use only function.
*/
static inline size_t
_bcmtch_inline_channel_byte_index(
struct tofe_channel_header *channel,
uint8_t entry_index)
{
return entry_index * channel->entry_size;
}
/**
Check if a channel is empty.
Events are not considered read or writen until the transaction is
complete. Therefore, a channel is empty even when in the middle of a
set of writes.
@param
[in] channel Pointer to channel object.
@retval
bool True if channel is empty.
*/
static inline bool
bcmtch_inline_channel_is_empty(struct tofe_channel_header *channel)
{
return (channel->buffer[0]->header.entry_count == 0);
}
/**
Read a single entry from a channel. This function must be called during
a read transaction.
The pointer returned by this function points into the channel object itself.
Callers should not modify or reuse this memory. Callers may not free the
memory.
@param
[in] channel Pointer to channel object.
@retval
void * Pointer to returned entry.
*/
static inline void *bcmtch_inline_channel_read(
struct tofe_channel_header *channel,
uint16_t index)
{
size_t byte_index;
struct tofe_channel_buffer *buff = channel->buffer[0];
/* Validate that channel has entries. */
if (buff->header.entry_count == 0)
return NULL;
/* Check if buffer data corrupted */
if (buff->header.entry_size != channel->entry_size)
return NULL;
/* Check if read end. */
if (index >= buff->header.entry_count)
return NULL;
/* Find entry in the channel. */
byte_index = _bcmtch_inline_channel_byte_index(channel, index);
return (void *)((char *)channel->buffer[0]->data
+ byte_index);
}
static inline void
tofe_channel_write_begin(struct tofe_channel_header *channel)
{
struct tofe_channel_buffer_header *buff =
&channel->buffer[0]->header;
if (channel->flags & TOFE_CHANNEL_FLAG_INBOUND)
buff->entry_count = 0;
}
static inline uint32_t
tofe_channel_write(struct tofe_channel_header *channel, void *entry)
{
struct tofe_channel_buffer_header *buff =
&channel->buffer[0]->header;
size_t byte_index =
channel->entry_size * buff->entry_count;
char *p_data = _bcmtch_inline_channel_entry(
channel,
byte_index);
if ((channel->flags & TOFE_CHANNEL_FLAG_INBOUND) == 0)
return -EINVAL;
if (buff->entry_count >= channel->entry_num)
return -ENOMEM;
memcpy(p_data, entry, channel->entry_size);
buff->entry_count++;
return 0;
}
static
uint16_t bcmtch_max_h_axis(uint16_t x, uint16_t y)
{
uint16_t i;
uint16_t res = 0;
uint16_t add = 0x8000;
uint32_t g = (x * x) + (y * y);
for (i = 0; i < 16 ; i++) {
uint16_t temp = res | add;
uint32_t g2 = temp * temp;
if (g >= g2)
res = temp;
add >>= 1;
}
return res;
}
/* ------------------------------------------- */
/* - BCM Touch Controller DEV Functions - */
/* ------------------------------------------- */
static int32_t bcmtch_dev_alloc(struct i2c_client *p_i2c_client)
{
int32_t ret_val = 0;
struct bcmtch_data *bcmtch_data_ptr;
bcmtch_data_ptr =
kzalloc(sizeof(struct bcmtch_data), GFP_KERNEL);
if (bcmtch_data_ptr == NULL) {
BCMTCH_ERR("%s: failed to alloc mem.\n", __func__);
ret_val = -ENOMEM;
}
mutex_init(&bcmtch_data_ptr->mutex_work);
mutex_init(&bcmtch_data_ptr->mutex_abi);
i2c_set_clientdata(p_i2c_client, (void *)bcmtch_data_ptr);
bcmtch_data_ptr->p_device = &p_i2c_client->dev;
return ret_val;
}
static void bcmtch_dev_free(struct i2c_client *p_i2c_client)
{
struct bcmtch_data *local_bcmtch_data_p =
(struct bcmtch_data *)
i2c_get_clientdata(p_i2c_client);
kfree(local_bcmtch_data_p);
i2c_set_clientdata(p_i2c_client, NULL);
}
static int32_t bcmtch_dev_init_clocks(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
uint32_t val32;
/* setup LPLFO - read OTP and set from value */
bcmtch_com_read_sys(
bcmtch_data_ptr,
BCMTCH_ADDR_SPM_LPLFO_CTRL_RO,
4,
(uint8_t *)&val32);
val32 &= 0xF0000000;
val32 >>= 28;
bcmtch_com_write_spm(
bcmtch_data_ptr,
BCMTCH_SPM_REG_LPLFO_CTRL,
(uint8_t)val32 | 0x10);
return ret_val;
}
static int32_t bcmtch_dev_init_memory(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
uint32_t mem_map;
mem_map = (bcmtch_data_ptr->boot_from_rom) ?
BCMTCH_MEM_ROM_BOOT : BCMTCH_MEM_RAM_BOOT;
bcmtch_data_ptr->channel_set =
(bcmtch_data_ptr->boot_from_rom) ?
BCMTCH_ROM_CHANNELS : BCMTCH_RAM_CHANNELS;
if (mem_map)
ret_val =
bcmtch_com_write_sys32(
bcmtch_data_ptr,
BCMTCH_MEM_REMAP_ADDR, mem_map);
return ret_val;
}
static inline void
tofe_channel_header_init(
struct bcmtch_data *bcmtch_data_ptr,
struct bcmtch_channel *p_channel,
struct tofe_channel_instance_cfg *p_chan_cfg)
{
struct tofe_channel_header *hdr = &p_channel->hdr;
struct tofe_channel_buffer_header *buff;
hdr->entry_num = p_chan_cfg->entry_num;
hdr->entry_size = p_chan_cfg->entry_size;
hdr->trig_level = p_chan_cfg->trig_level;
hdr->flags = p_chan_cfg->flags;
hdr->buffer_num = p_chan_cfg->buffer_num;
hdr->buffer_idx = 0;
hdr->seq_count = 0;
hdr->buffer[0] = (struct tofe_channel_buffer *)&p_channel->data;
hdr->buffer[1] = (struct tofe_channel_buffer *)&p_channel->data;
if (hdr->flags & TOFE_CHANNEL_FLAG_FWDMA_ENABLE) {
bcmtch_data_ptr->has_dma_channel = true;
buff = &hdr->buffer[1]->header;
buff->channel_id = hdr->channel_id;
buff->entry_count = 0;
buff->entry_size = hdr->entry_size;
bcmtch_data_ptr->fw_dma_buffer_size +=
sizeof(struct tofe_channel_buffer_header)
+ (hdr->entry_num * hdr->entry_size);
}
}
static int32_t bcmtch_dev_init_channel(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t chan_set,
enum bcmtch_channel_id chan_id,
struct tofe_channel_instance_cfg *p_chan_cfg,
uint8_t active)
{
int32_t ret_val = 0;
uint32_t channel_size;
struct bcmtch_channel *p_channel;
if (active) {
channel_size =
/* channel data size */
sizeof(struct tofe_channel_buffer_header)
+ (p_chan_cfg->flags &
TOFE_CHANNEL_FLAG_FWDMA_ENABLE ? 0 :
p_chan_cfg->entry_num
* p_chan_cfg->entry_size)
/* channel header size */
+ sizeof(struct tofe_channel_header)
/* channel config size */
+ sizeof(struct tofe_channel_instance_cfg)
/* sizes for added elements: queued, pad */
+ (sizeof(uint16_t) * 2);
} else {
channel_size =
/* channel header size */
sizeof(struct tofe_channel_header)
/* channel config size */
+ sizeof(struct tofe_channel_instance_cfg)
/* sizes for added elements: queued, pad */
+ (sizeof(uint16_t) * 2);
}
p_channel = kzalloc(channel_size, GFP_KERNEL);
if (p_channel) {
p_channel->cfg = *p_chan_cfg;
/* Initialize Header */
p_channel->hdr.channel_id = (uint8_t)chan_id;
p_channel->active = active;
tofe_channel_header_init(bcmtch_data_ptr,
p_channel, p_chan_cfg);
bcmtch_data_ptr->p_channels[chan_set][chan_id] = p_channel;
} else if (active) {
ret_val = -ENOMEM;
bcmtch_data_ptr->p_channels[chan_set][chan_id] = NULL;
}
return ret_val;
}
static void bcmtch_dev_free_channels(
struct bcmtch_data *bcmtch_data_ptr)
{
uint32_t chan = 0;
uint8_t chan_set = 0;
kfree(bcmtch_data_ptr->fw_dma_buffer);
bcmtch_data_ptr->fw_dma_buffer = NULL;
while (chan_set < BCMTCH_MAX_CHANNEL_SET) {
while (chan < BCMTCH_CHANNEL_MAX) {
if (bcmtch_data_ptr->p_channels[chan_set][chan]) {
kfree(bcmtch_data_ptr->
p_channels[chan_set][chan]);
bcmtch_data_ptr->
p_channels[chan_set][chan] = NULL;
}
chan++;
}
chan_set++;
}
}
static int32_t bcmtch_dev_init_channels(
struct bcmtch_data *bcmtch_data_ptr,
uint32_t mem_addr,
uint8_t *mem_data,
uint8_t chan_set)
{
int32_t ret_val = 0;
void *p_buffer = NULL;
uint32_t *p_cfg = NULL;
struct tofe_channel_instance_cfg *p_chan_cfg = NULL;
/* find channel configs */
p_cfg = (uint32_t *)(mem_data + TOFE_SIGNATURE_SIZE);
p_chan_cfg =
(struct tofe_channel_instance_cfg *)
((uint32_t)mem_data + p_cfg[TOFE_TOC_INDEX_CHANNEL] - mem_addr);
/* check if processing channel(s) - add */
ret_val = bcmtch_dev_init_channel(
bcmtch_data_ptr,
chan_set,
BCMTCH_CHANNEL_TOUCH,
&p_chan_cfg[TOFE_CHANNEL_ID_TOUCH],
bcmtch_channel_flag & BCMTCH_CHANNEL_FLAG_USE_TOUCH);
if (ret_val) {
BCMTCH_ERR("%s: [%d] Touch Event Channel not initialized!\n",
__func__, ret_val);
}
/* Command & response channels */
if (!ret_val) {
ret_val = bcmtch_dev_init_channel(
bcmtch_data_ptr,
chan_set,
BCMTCH_CHANNEL_COMMAND,
&p_chan_cfg[TOFE_CHANNEL_ID_COMMAND],
bcmtch_channel_flag &
BCMTCH_CHANNEL_FLAG_USE_CMD_RESP);
ret_val |= bcmtch_dev_init_channel(
bcmtch_data_ptr,
chan_set,
BCMTCH_CHANNEL_RESPONSE,
&p_chan_cfg[TOFE_CHANNEL_ID_RESPONSE],
bcmtch_channel_flag &
BCMTCH_CHANNEL_FLAG_USE_CMD_RESP);
if (ret_val)
BCMTCH_ERR(
"%s: [%d] C/R Channel initialization failed!\n",
__func__, ret_val);
}
/* Log channel */
if (!ret_val) {
ret_val = bcmtch_dev_init_channel(
bcmtch_data_ptr,
chan_set,
BCMTCH_CHANNEL_LOG,
&p_chan_cfg[TOFE_CHANNEL_ID_LOG],
bcmtch_channel_flag &
BCMTCH_CHANNEL_FLAG_USE_LOG);
if (ret_val)
BCMTCH_ERR(
"%s: [%d] Log Channel initialization failed!\n",
__func__, ret_val);
}
/* Initialize DMA buffer if there is any DMA mode channel */
if (!ret_val &&
bcmtch_data_ptr->has_dma_channel) {
BCMTCH_DBG(
BCMTCH_DF_CH,
"CH:%s() dma buffer size=%d\n",
__func__,
bcmtch_data_ptr->fw_dma_buffer_size);
p_buffer =
kzalloc(
bcmtch_data_ptr->fw_dma_buffer_size,
GFP_KERNEL);
if (p_buffer) {
bcmtch_data_ptr->fw_dma_buffer = p_buffer;
} else {
BCMTCH_ERR("%s: [%d] DMA buffer allocation failed!\n",
__func__, ret_val);
bcmtch_data_ptr->fw_dma_buffer = NULL;
ret_val = -ENOMEM;
}
}
return ret_val;
}
static int32_t bcmtch_dev_write_channel(
struct bcmtch_data *bcmtch_data_ptr,
struct bcmtch_channel *chan)
{
int32_t ret_val = 0;
int16_t write_size;
uint32_t sys_addr;
/* read channel header and data all-at-once : need combined size */
write_size = sizeof(struct tofe_channel_buffer_header)
+ (chan->cfg.entry_num * chan->cfg.entry_size);
sys_addr = (uint32_t)chan->cfg.channel_data;
/* write channel header & channel data buffer */
ret_val = bcmtch_com_write_sys(
bcmtch_data_ptr,
sys_addr,
write_size,
(uint8_t *)chan->hdr.buffer[0]);
if (ret_val) {
BCMTCH_ERR("BCMTOUCH: %s() write_sys err addr=0x%08x, rv=%d\n",
__func__,
sys_addr,
ret_val);
}
return ret_val;
}
static int32_t bcmtch_dev_sync_channel(
struct bcmtch_data *bcmtch_data_ptr,
struct bcmtch_channel *chan)
{
int32_t ret_val = 0;
uint16_t read_size;
uint32_t sys_addr;
struct tofe_channel_header sync_hdr;
/* Read channel header from firmware */
read_size = sizeof(struct tofe_channel_header);
sys_addr = (uint32_t)chan->cfg.channel_header;
ret_val = bcmtch_com_read_sys(
bcmtch_data_ptr,
(uint32_t)chan->cfg.channel_header,
read_size,
(uint8_t *)&sync_hdr);
if (ret_val) {
BCMTCH_ERR("BCMTOUCH: %s() read hdr err addr=0x%08x, rv=%d\n",
__func__,
sys_addr,
ret_val);
return ret_val;
}
/* Read channel */
read_size = sizeof(struct tofe_channel_buffer_header)
+ (chan->cfg.entry_num * chan->cfg.entry_size);
sys_addr = (uint32_t)(sync_hdr.buffer_idx > 0 ?
(char *)chan->cfg.channel_data :
(char *)chan->cfg.channel_data
+ chan->cfg.offset);
ret_val = bcmtch_com_read_sys(
bcmtch_data_ptr,
sys_addr,
read_size,
(uint8_t *)chan->hdr.buffer[0]);
if (ret_val) {
BCMTCH_ERR(
"BCMTOUCH: %s() read buffer err addr=0x%08x, rv=%d\n",
__func__,
sys_addr,
ret_val);
return ret_val;
}
chan->queued = bcmtch_channel_num_queued(&chan->hdr);
/* Sync the channel header */
chan->hdr.buffer_idx = sync_hdr.buffer_idx;
if (bcmtch_data_ptr->channel_set == BCMTCH_ROM_CHANNELS)
chan->hdr.seq_count = sync_hdr.seq_count;
else
chan->hdr.seq_count = sync_hdr.seq_count + 1;
return ret_val;
}
static int32_t bcmtch_dev_read_channel(
struct bcmtch_data *bcmtch_data_ptr,
struct bcmtch_channel *chan)
{
int32_t ret_val = 0;
uint8_t buffer_idx = chan->hdr.buffer_idx;
uint8_t seq_count = chan->hdr.seq_count;
struct tofe_channel_buffer_header *buff =
&chan->hdr.buffer[1]->header;
uint16_t read_size;
uint32_t sys_addr;
/* channel buffer size: buffer header + entries */
read_size = sizeof(struct tofe_channel_buffer_header)
+ (chan->cfg.entry_num * chan->cfg.entry_size);
sys_addr = (uint32_t)(buffer_idx == 0 ?
(char *)chan->cfg.channel_data :
(char *)chan->cfg.channel_data
+ chan->cfg.offset);
/* read channel header & channel data buffer */
ret_val = bcmtch_com_read_sys(
bcmtch_data_ptr,
sys_addr,
read_size,
(uint8_t *)chan->hdr.buffer[1]);
if (ret_val) {
BCMTCH_ERR("BCMTOUCH: %s() read_sys err addr=0x%08x, rv=%d\n",
__func__,
sys_addr,
ret_val);
return ret_val;
}
/* check if data corrupted */
if (!bcmtch_dev_verify_buffer_header(bcmtch_data_ptr,
buff)) {
BCMTCH_DBG(
BCMTCH_DF_CH,
"CH:%s() ch=%d buffer data corrupted!\n",
__func__,
chan->hdr.channel_id);
return -EIO;
}
if (buff->flags & TOFE_CHANNEL_FLAG_STATUS_OVERFLOW)
BCMTCH_DBG(
BCMTCH_DF_CH,
"CH:%s() ch=%d channel overflow\n",
__func__,
chan->hdr.channel_id);
if (buff->seq_number != seq_count) {
BCMTCH_DBG(
BCMTCH_DF_CH,
"CH:%s() host ch=%d seq=%d, fw seq=%d NOT Matched!!\n",
__func__,
buff->channel_id,
seq_count,
buff->seq_number);
/* sync channel */
ret_val =
bcmtch_dev_sync_channel(bcmtch_data_ptr, chan);
return ret_val;
}
/* Update channel header */
chan->hdr.seq_count++;
chan->hdr.buffer_idx = (buffer_idx > 0 ? 0 : 1);
/* get count */
chan->queued = bcmtch_channel_num_queued(&chan->hdr);
return ret_val;
}
static uint32_t bcmtch_dev_read_dma_buffer(
struct bcmtch_data *bcmtch_data_ptr)
{
uint32_t read_size = 0;
uint32_t dma_buff_size = bcmtch_data_ptr->fw_dma_buffer_size;
uint8_t dma_reg = BCMTCH_SPM_REG_DMA_RFIFO;
uint8_t *dma_buff = (uint8_t *)bcmtch_data_ptr->fw_dma_buffer;
struct i2c_client *p_i2c = bcmtch_data_ptr->p_i2c_client_sys;
struct tofe_dmac_header *p_dmac;
/* setup I2C messages for DMA read request transaction */
struct i2c_msg dma_request[2] = {
/* write the RFIFO address */
{.addr = p_i2c->addr,
.flags = 0,
.len = 1,
.buf = &dma_reg},
/* read RFIFO data */
{.addr = p_i2c->addr,
.flags = I2C_M_RD,
.len = (uint32_t)sizeof(struct tofe_dmac_header),
.buf = dma_buff}
};
/* Set I2C master to read from RFIFO */
if (dma_buff_size && dma_buff) {
/* 1st I2C read dmac header */
if (i2c_transfer(p_i2c->adapter, dma_request, 2) != 2) {
BCMTCH_ERR("%s: I2C transfer error.\n",
__func__);
return 0;
} else {
p_dmac = (struct tofe_dmac_header *)dma_buff;
read_size = (uint32_t)p_dmac->size;
BCMTCH_DBG(
BCMTCH_DF_CH,
"CH:DMA buffer read size=%d min_size=%d.\n",
read_size,
p_dmac->min_size);
if (read_size > dma_buff_size) {
BCMTCH_ERR(
"%s: DMA read overflow buffer [%d].\n",
__func__,
dma_buff_size);
return 0;
} else if (read_size <
sizeof(struct tofe_dmac_header))
return 0;
/* 2nd I2C read entire DMA buffer */
dma_request[1].len =
read_size
- sizeof(struct tofe_dmac_header);
dma_request[1].buf =
(uint8_t *)dma_buff
+ sizeof(struct tofe_dmac_header);
if (i2c_transfer(p_i2c->adapter, dma_request, 2) != 2) {
BCMTCH_ERR("%s: I2C transfer error.\n",
__func__);
return 0;
}
}
} else {
BCMTCH_ERR("%s: DMA buffer/size is NULL.\n",
__func__);
}
return read_size;
}
static inline bool bcmtch_dev_verify_buffer_header(
struct bcmtch_data *bcmtch_data_ptr,
struct tofe_channel_buffer_header *buff)
{
uint8_t channel;
uint8_t chan_set = bcmtch_data_ptr->channel_set;
struct bcmtch_channel *p_chan;
bool ret_val = true;
p_chan = NULL;
channel = (uint8_t)buff->channel_id;
if (channel >= BCMTCH_CHANNEL_MAX)
ret_val = false;
else {
p_chan = bcmtch_data_ptr->p_channels[chan_set][channel];
if (!p_chan ||
(buff->entry_size != p_chan->cfg.entry_size))
ret_val = false;
}
if (ret_val == false)
BCMTCH_DBG(
BCMTCH_DF_CH,
"CH:ERROR : id=%d entry_size=%d [%d]\n",
buff->channel_id,
buff->entry_size,
(p_chan) ? p_chan->cfg.entry_size : -1);
return ret_val;
}
static int32_t bcmtch_dev_read_dma_channels(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
uint32_t read_size;
uint32_t read_head;
uint32_t offset;
uint32_t channel;
uint8_t *p_dma = (uint8_t *)bcmtch_data_ptr->fw_dma_buffer;
uint8_t chan_set = bcmtch_data_ptr->channel_set;
struct tofe_channel_buffer_header *buff;
struct tofe_channel_header *hdr;
/* Read DMA buffer via I2C */
read_size = bcmtch_dev_read_dma_buffer(bcmtch_data_ptr);
BCMTCH_DBG(BCMTCH_DF_CH, "CH:%s: read DMA buffer %d bytes.\n",
__func__,
read_size);
if (read_size > bcmtch_data_ptr->fw_dma_buffer_size) {
BCMTCH_ERR("%s: Invalid DMA data read size %d.\n",
__func__,
read_size);
return -EIO;
}
/* Parse DMA buffer for channels */
read_head = 0;
while (read_head < read_size) {
buff = (struct tofe_channel_buffer_header *)p_dma;
if (!bcmtch_dev_verify_buffer_header(bcmtch_data_ptr,
buff)) {
BCMTCH_ERR(
"%s: corrupted buffer header in DMA channel!\n",
__func__);
return -EIO;
}
channel = buff->channel_id;
BCMTCH_DBG(
BCMTCH_DF_CH,
"CH:%s: parsing channel [%d] min_size=%d\n",
__func__,
channel,
buff->dmac.min_size);
hdr = &bcmtch_data_ptr->p_channels[chan_set][channel]->hdr;
if (hdr->flags & TOFE_CHANNEL_FLAG_FWDMA_ENABLE)
hdr->buffer[0] = (struct tofe_channel_buffer *)p_dma;
offset = (uint32_t)(buff->dmac.min_size ? buff->dmac.min_size :
(buff->entry_size * buff->entry_count)
+ sizeof(struct tofe_channel_buffer_header));
read_head += offset;
p_dma += offset;
}
return ret_val;
}
#if BCMTCH_STATE_PROTOCOL
static int32_t bcmtch_dev_parse_response_data(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t *data,
uint16_t data_size)
{
int32_t ret_val = 0;
uint32_t slot_size =
(uint32_t)
bcmtch_data_ptr->scan_data.touch_slot_size;
uint32_t slot_num =
(uint32_t)
bcmtch_data_ptr->scan_data.status.slots;
/* Touch slot format - 0: long slot, 1: short slot */
uint8_t slot_format =
bcmtch_data_ptr->scan_data.cmd
.touch_slot_format;
struct input_dev *input_dev_ptr =
bcmtch_data_ptr->p_input_device;
uint32_t button_index = 0;
uint8_t *data_ptr = data;
uint8_t slot_type;
uint16_t tmp_axis;
uint16_t button_check;
uint16_t evt_status;
uint16_t btn_status;
uint32_t touch_index;
union bcmtch_state_resp_slot *slot;
struct bcmtch_touch *touch_ptr;
int32_t ao_flag =
bcmtch_data_ptr->platform_data.axis_orientation_flag;
for (touch_index = 0;
touch_index < slot_num;
data_ptr += slot_size,
touch_index++) {
slot = (union bcmtch_state_resp_slot *) data_ptr;
touch_ptr = &bcmtch_data_ptr->touch[touch_index];
/* Categorize slot type */
if (slot->type > 7) {
slot_type = STATE_SLOT_TYPE_FINGER;
touch_ptr->type = MT_TOOL_FINGER;
} else if (slot->type > 1) {
slot_type = STATE_SLOT_TYPE_STYLUS;
touch_ptr->type = MT_TOOL_PEN;
} else if (slot->type == 1)
slot_type = STATE_SLOT_TYPE_BUTTON;
else
slot_type = STATE_SLOT_TYPE_EMPTY;
/* Process slots */
switch (slot_type) {
case STATE_SLOT_TYPE_EMPTY:
touch_ptr->status = BCMTCH_TOUCH_STATUS_UP;
break;
case STATE_SLOT_TYPE_BUTTON:
btn_status = bcmtch_data_ptr->button_status;
evt_status = (uint16_t)(slot->button_slot.buttons);
if (btn_status != evt_status) {
BCMTCH_DBG(
BCMTCH_DF_ST,
"ST:BTN: 0x%0x\n",
evt_status);
while (button_index < bcmtch_data_ptr->
platform_data.ext_button_count) {
button_check = (0x1 << button_index);
if ((btn_status & button_check) !=
(evt_status & button_check)) {
input_report_key(
input_dev_ptr,
bcmtch_data_ptr->
platform_data.
ext_button_map
[button_index],
(evt_status &
button_check));
}
button_index++;
}
/* Update status */
bcmtch_data_ptr->button_status = evt_status;
}
break;
case STATE_SLOT_TYPE_STYLUS:
/* Not support yet */
case STATE_SLOT_TYPE_FINGER:
if (slot_format) {
/* short slot */
touch_ptr->x = slot->short_slot.x;
touch_ptr->y = slot->short_slot.y;
touch_ptr->pressure = 0;
touch_ptr->orientation = 0;
touch_ptr->major_axis = 0;
touch_ptr->minor_axis = 0;
} else {
/* long slot */
touch_ptr->x = slot->long_slot.x;
touch_ptr->y = slot->long_slot.y;
touch_ptr->pressure = slot->long_slot.pressure;
touch_ptr->orientation =
slot->long_slot.orientation;
touch_ptr->major_axis =
slot->long_slot.major
<< BCMTCH_AXIS_SHIFT_BITS;
touch_ptr->minor_axis =
slot->long_slot.minor
<< BCMTCH_AXIS_SHIFT_BITS;
}
/* axis reverse adjust */
if (ao_flag & BCMTCH_AXIS_FLAG_X_REVERSED_MASK)
touch_ptr->x =
bcmtch_data_ptr->axis_x_max
- touch_ptr->x;
if (ao_flag & BCMTCH_AXIS_FLAG_Y_REVERSED_MASK)
touch_ptr->y =
bcmtch_data_ptr->axis_y_max
- touch_ptr->y;
if (ao_flag & BCMTCH_AXIS_FLAG_X_Y_SWAPPED_MASK) {
tmp_axis = touch_ptr->x;
touch_ptr->x = touch_ptr->y;
touch_ptr->y = tmp_axis;
}
if (bcmtch_data_ptr->chip_id == 0x015200) {
touch_ptr->y =
bcmtch_data_ptr->axis_y_max
- touch_ptr->y;
}
touch_ptr->status = BCMTCH_TOUCH_STATUS_MOVING;
BCMTCH_DBG(
BCMTCH_DF_ST,
"ST: SLOT %d: X=%d Y=%d P=%d O=%d MAJX=%d MINX=%d T=%d\n",
touch_index,
touch_ptr->x,
touch_ptr->y,
touch_ptr->pressure,
touch_ptr->orientation,
touch_ptr->major_axis,
touch_ptr->minor_axis,
touch_ptr->type);
break;
default:
BCMTCH_ERR("ST: SLOT %d: Unknown",
touch_index);
}
}
/* Report touch event to kernel */
ret_val = bcmtch_dev_sync_event_frame(bcmtch_data_ptr);
return ret_val;
}
static int32_t bcmtch_dev_read_scan_touches(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t slots)
{
int32_t ret_val = 0;
uint16_t data_size;
uint8_t *dma_buff = bcmtch_data_ptr->bcmtch_state_resp_buffer;
if (!slots)
return ret_val;
data_size = slots * bcmtch_data_ptr->scan_data.touch_slot_size;
if (data_size > TOFE_HOST_RSP_BUF_SIZE) {
BCMTCH_ERR(
"ST: data size to read exceed response buffer.");
BCMTCH_ERR(
"ST: slots=%d data_size=%d resp_buf_size=%d",
slots,
data_size,
TOFE_HOST_RSP_BUF_SIZE);
return -EINVAL;
}
memset(dma_buff, 0, data_size);
BCMTCH_DBG(
BCMTCH_DF_ST,
"ST: %s read %d touch slots size=%d",
__func__,
slots,
data_size);
ret_val =
bcmtch_com_read_dma(
bcmtch_data_ptr,
data_size + 1,
dma_buff);
if (ret_val) {
BCMTCH_ERR("%s: DMA read error.\n", __func__);
return ret_val;
}
/* parse touch data: Slot 0 ... Slot N-1 */
bcmtch_dev_parse_response_data(
bcmtch_data_ptr,
&dma_buff[1],
data_size);
return ret_val;
}
#endif /* BCMTCH_STATE_PROTOCOL */
static int32_t bcmtch_dev_read_channels(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
uint32_t channel = 0;
uint8_t chan_set = bcmtch_data_ptr->channel_set;
while (channel < BCMTCH_CHANNEL_MAX) {
if (bcmtch_data_ptr->p_channels[chan_set][channel]->active &&
!(bcmtch_data_ptr->
p_channels[chan_set][channel]->cfg.flags &
(TOFE_CHANNEL_FLAG_INBOUND
| TOFE_CHANNEL_FLAG_FWDMA_ENABLE))) {
ret_val = bcmtch_dev_read_channel(
bcmtch_data_ptr,
bcmtch_data_ptr->p_channels[chan_set][channel]);
}
channel++;
}
return ret_val;
}
static int32_t bcmtch_dev_process_event_frame(
struct bcmtch_data *bcmtch_data_ptr,
struct bcmtch_event_frame *p_frame_event)
{
int32_t ret_val = 0;
BCMTCH_DBG(BCMTCH_DF_FR, "FR:T=%d ID=%d TS=%d\n",
bcmtch_data_ptr->touch_count,
p_frame_event->frame_id,
p_frame_event->timestamp);
return ret_val;
}
static int32_t bcmtch_dev_process_event_frame_extension(
struct bcmtch_event_frame_extension
*p_frame_event_extension)
{
int32_t ret_val = 0;
struct bcmtch_event_frame_extension_timestamp *timestamp;
struct bcmtch_event_frame_extension_checksum *checksum;
switch (p_frame_event_extension->frame_kind) {
case BCMTCH_EVENT_FRAME_EXTENSION_KIND_TIMESTAMP:
timestamp = (struct bcmtch_event_frame_extension_timestamp *)
p_frame_event_extension;
BCMTCH_DBG(
BCMTCH_DF_FE,
"FE:Time offsets. %d %d %d",
timestamp->scan_end,
timestamp->mtc_start,
timestamp->mtc_end);
break;
case BCMTCH_EVENT_FRAME_EXTENSION_KIND_CHECKSUM:
checksum =
(struct bcmtch_event_frame_extension_checksum *)
p_frame_event_extension;
BCMTCH_DBG(
BCMTCH_DF_FE,
"FE:ERROR: Checksum not supported. %#x",
checksum->hash);
break;
case BCMTCH_EVENT_FRAME_EXTENSION_KIND_HEARTBEAT:
BCMTCH_DBG(
BCMTCH_DF_FE,
"FE:ERROR: Heartbeat not supported.");
break;
default:
BCMTCH_DBG(
BCMTCH_DF_FE,
"FE:Invalid frame extension. %d",
p_frame_event_extension->frame_kind);
break;
}
return ret_val;
}
static int32_t bcmtch_dev_sync_event_frame(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
struct input_dev *input_dev_ptr =
bcmtch_data_ptr->p_input_device;
struct bcmtch_touch *touch_ptr;
uint32_t num_touches = 0;
uint32_t touch_index = 0;
for (touch_index = 0; touch_index < ARRAY_SIZE(bcmtch_data_ptr->touch);
touch_index++) {
touch_ptr =
(struct bcmtch_touch *)
&bcmtch_data_ptr->touch[touch_index];
input_mt_slot(input_dev_ptr, touch_index);
input_mt_report_slot_state(
input_dev_ptr,
touch_ptr->type,
(touch_ptr->status > BCMTCH_TOUCH_STATUS_UP));
if (touch_ptr->status > BCMTCH_TOUCH_STATUS_UP) {
/* Count both of STATUS_MOVE and STATUS_MOVING */
num_touches++;
if (touch_ptr->status > BCMTCH_TOUCH_STATUS_MOVE) {
input_report_abs(
input_dev_ptr,
ABS_MT_POSITION_X,
touch_ptr->x);
input_report_abs(
input_dev_ptr,
ABS_MT_POSITION_Y,
touch_ptr->y);
if (bcmtch_event_flag &
BCMTCH_EVENT_FLAG_PRESSURE) {
input_report_abs(
input_dev_ptr,
ABS_MT_PRESSURE,
touch_ptr->pressure);
}
if (bcmtch_event_flag &
BCMTCH_EVENT_FLAG_TOUCH_SIZE) {
input_report_abs(
input_dev_ptr,
ABS_MT_TOUCH_MAJOR,
touch_ptr->major_axis);
input_report_abs(
input_dev_ptr,
ABS_MT_TOUCH_MINOR,
touch_ptr->minor_axis);
}
if (bcmtch_event_flag &
BCMTCH_EVENT_FLAG_ORIENTATION) {
input_report_abs(
input_dev_ptr,
ABS_MT_ORIENTATION,
touch_ptr->orientation);
}
if (bcmtch_event_flag &
BCMTCH_EVENT_FLAG_TOOL_SIZE) {
input_report_abs(
input_dev_ptr,
ABS_MT_WIDTH_MAJOR,
touch_ptr->width_major);
input_report_abs(
input_dev_ptr,
ABS_MT_WIDTH_MINOR,
touch_ptr->width_minor);
}
/* reset the status from MOVING to MOVE. */
touch_ptr->status = BCMTCH_TOUCH_STATUS_MOVE;
}
}
}
input_report_key(input_dev_ptr, BTN_TOUCH, (num_touches > 0));
input_sync(input_dev_ptr);
/* remember */
bcmtch_data_ptr->touch_count = num_touches;
return ret_val;
}
static void
bcmtch_dev_process_event_touch_extension(
struct bcmtch_data *bcmtch_data_ptr,
struct bcmtch_event_touch_extension *extension,
uint8_t track_id)
{
char *tool_str;
struct bcmtch_event_touch_extension_detail *detail;
struct bcmtch_event_touch_extension_blob *blob;
struct bcmtch_event_touch_extension_size *size;
struct bcmtch_event_touch_extension_tool *tool;
struct bcmtch_touch *touch_ptr =
(struct bcmtch_touch *)&bcmtch_data_ptr->touch[track_id];
switch (extension->touch_kind) {
case BCMTCH_EVENT_TOUCH_EXTENSION_KIND_DETAIL:
detail =
(struct bcmtch_event_touch_extension_detail *)
extension;
/* logic is inverted */
if (detail->tool == BCMTCH_EVENT_TOUCH_TOOL_FINGER) {
touch_ptr->type = MT_TOOL_PEN;
tool_str = "stylus";
} else {
touch_ptr->type = MT_TOOL_FINGER;
tool_str = "finger";
}
BCMTCH_DBG(
BCMTCH_DF_TE,
"TE:C%d:S%d:H%d %s(%d) Pres=%d Ornt=%#x",
detail->confident,
detail->suppressed,
detail->hover,
tool_str,
detail->tool,
detail->pressure,
detail->orientation);
/**
* ABS_MT_TOOL_TYPE
* - MT_TOOL_FINGER
* - MT_TOOL_PEN
**/
touch_ptr->pressure = detail->pressure;
/* get orientation
* - handle int12 to int16 conversion
*/
touch_ptr->orientation = detail->orientation;
if (touch_ptr->orientation & (1<<11))
touch_ptr->orientation -= 1<<12;
break;
case BCMTCH_EVENT_TOUCH_EXTENSION_KIND_BLOB:
blob =
(struct bcmtch_event_touch_extension_blob *)
extension;
BCMTCH_DBG(BCMTCH_DF_TE, "TE:Area=%d TCap=%d",
blob->area, blob->total_cap);
/**
* ABS_MT_BLOB_ID
*/
break;
case BCMTCH_EVENT_TOUCH_EXTENSION_KIND_SIZE:
size =
(struct bcmtch_event_touch_extension_size *)
extension;
BCMTCH_DBG(
BCMTCH_DF_TE,
"TE:Track %d:\tMajor=%d Minor=%d",
track_id,
size->major_axis,
size->minor_axis);
/**
* ABS_MT_MAJOR/MINOR_AXIS
*/
touch_ptr->major_axis =
size->major_axis << BCMTCH_AXIS_SHIFT_BITS;
touch_ptr->minor_axis =
size->minor_axis << BCMTCH_AXIS_SHIFT_BITS;
break;
case BCMTCH_EVENT_TOUCH_EXTENSION_KIND_HOVER:
BCMTCH_DBG(
BCMTCH_DF_TE,
"TE:ERROR:Hover not supported.");
break;
case BCMTCH_EVENT_TOUCH_EXTENSION_KIND_TOOL:
tool =
(struct bcmtch_event_touch_extension_tool *)
extension;
BCMTCH_DBG(
BCMTCH_DF_TE,
"TE:Track %d:\tMajor=%d Minor=%d",
track_id,
tool->width_major,
tool->width_minor);
/**
* ABS_MT_MAJOR/MINOR_AXIS
*/
touch_ptr->width_major = tool->width_major;
touch_ptr->width_minor = tool->width_minor;
break;
default:
BCMTCH_DBG(
BCMTCH_DF_TE,
"TE:Invalid touch extension. %d",
extension->touch_kind);
break;
}
}
static int32_t bcmtch_dev_process_event_touch(
struct bcmtch_data *bcmtch_data_ptr,
struct bcmtch_event_touch *p_touch_event)
{
int axis_orientation_flag;
int32_t ret_val = 0;
struct bcmtch_touch *p_touch;
enum bcmtch_event_kind kind;
if (p_touch_event->track_tag < BCMTCH_MAX_TOUCH) {
axis_orientation_flag =
bcmtch_data_ptr->platform_data.axis_orientation_flag;
p_touch =
(struct bcmtch_touch *)
&bcmtch_data_ptr->touch[p_touch_event->track_tag];
if (axis_orientation_flag & BCMTCH_AXIS_FLAG_X_REVERSED_MASK)
p_touch_event->x =
bcmtch_data_ptr->axis_x_max - p_touch_event->x;
if ((axis_orientation_flag & BCMTCH_AXIS_FLAG_Y_REVERSED_MASK)
||
(bcmtch_data_ptr->chip_id == 0x015200))
p_touch_event->y =
bcmtch_data_ptr->axis_y_max - p_touch_event->y;
if (axis_orientation_flag & BCMTCH_AXIS_FLAG_X_Y_SWAPPED_MASK) {
p_touch->y = p_touch_event->x;
p_touch->x = p_touch_event->y;
} else {
p_touch->x = p_touch_event->x;
p_touch->y = p_touch_event->y;
}
kind = (enum bcmtch_event_kind)p_touch_event->event_kind;
switch (kind) {
case BCMTCH_EVENT_KIND_TOUCH:
p_touch->event = kind;
p_touch->status = BCMTCH_TOUCH_STATUS_MOVING;
BCMTCH_DBG(
BCMTCH_DF_MV,
"MV:T%d: (%04x , %04x)\n",
p_touch_event->track_tag,
p_touch->x,
p_touch->y);
break;
case BCMTCH_EVENT_KIND_TOUCH_END:
p_touch->event = kind;
p_touch->status = BCMTCH_TOUCH_STATUS_UP;
BCMTCH_DBG(
BCMTCH_DF_UP,
"UP:T%d: (%04x , %04x)\n",
p_touch_event->track_tag,
p_touch->x,
p_touch->y);
break;
default:
BCMTCH_ERR("%s: Invalid touch event", __func__);
break;
}
}
return ret_val;
}
static int32_t bcmtch_dev_process_event_button(
struct bcmtch_data *bcmtch_data_ptr,
struct bcmtch_event_button *p_button_event)
{
int32_t ret_val = 0;
uint16_t evt_status = p_button_event->status;
uint16_t btn_status = bcmtch_data_ptr->button_status;
struct input_dev *input_dev_ptr =
bcmtch_data_ptr->p_input_device;
enum bcmtch_event_kind kind = p_button_event->button_kind;
uint32_t button_index = 0;
uint16_t button_check;
if (btn_status != evt_status) {
switch (kind) {
case BCMTCH_EVENT_BUTTON_KIND_CONTACT:
while (button_index < bcmtch_data_ptr->
platform_data.ext_button_count) {
button_check = (0x1 << button_index);
if ((btn_status & button_check) !=
(evt_status & button_check)) {
input_report_key(
input_dev_ptr,
bcmtch_data_ptr->platform_data.
ext_button_map[button_index],
(evt_status & button_check));
}
button_index++;
}
BCMTCH_DBG(BCMTCH_DF_BT, "BT:%s %#04x\n",
"press",
evt_status);
break;
case BCMTCH_EVENT_BUTTON_KIND_HOVER:
BCMTCH_DBG(BCMTCH_DF_BT, "BT:%s %#04x\n",
"hover",
evt_status);
break;
default:
BCMTCH_ERR("%s: Invalid button kind %d\n",
__func__,
kind);
break;
}
/* Report SYNC */
input_sync(input_dev_ptr);
/* Update status */
bcmtch_data_ptr->button_status = evt_status;
} else {
BCMTCH_DBG(
BCMTCH_DF_BT,
"BT:unchanged. status=0x%04x\n",
btn_status);
}
return ret_val;
}
/**
* To process whole frames of data this variable should
* be made global because one frame can be split across
* two invocations of the function process_channel_touch().
*/
static enum bcmtch_event_kind top_level_kind = BCMTCH_EVENT_KIND_EXTENSION;
static int32_t bcmtch_dev_process_channel_touch(
struct bcmtch_data *bcmtch_data_ptr,
struct bcmtch_channel *chan)
{
int32_t ret_val = 0;
bool syn_report_pending = false;
uint16_t read_idx;
struct bcmtch_event *ptch_event;
enum bcmtch_event_kind kind;
struct bcmtch_event_touch *ptouch_event = NULL;
struct tofe_channel_header *chan_hdr =
(struct tofe_channel_header *)&chan->hdr;
uint32_t frames_in = 0;
read_idx = 0;
while ((ptch_event = (struct bcmtch_event *)
bcmtch_inline_channel_read(chan_hdr,
read_idx++))) {
kind = (enum bcmtch_event_kind)ptch_event->event_kind;
if (kind != BCMTCH_EVENT_KIND_EXTENSION) {
top_level_kind = kind;
if (syn_report_pending) {
/**
* The end of frame extension events.
* Send the SYN_REPORT for the frame.
*/
bcmtch_dev_sync_event_frame(bcmtch_data_ptr);
syn_report_pending = false;
if (frames_in)
usleep_range(1000, 1500);
}
}
switch (kind) {
case BCMTCH_EVENT_KIND_FRAME:
/**
* Only set the flag to wait for the following frame extension events
* rather than directly send SYN_REPORT message.
*/
frames_in++;
syn_report_pending = true;
bcmtch_dev_process_event_frame(
bcmtch_data_ptr,
(struct bcmtch_event_frame *)
ptch_event);
break;
case BCMTCH_EVENT_KIND_TOUCH:
case BCMTCH_EVENT_KIND_TOUCH_END:
ptouch_event =
(struct bcmtch_event_touch *)ptch_event;
bcmtch_data_ptr->touch_event_track_id =
ptouch_event->track_tag;
bcmtch_dev_process_event_touch(
bcmtch_data_ptr,
ptouch_event);
break;
case BCMTCH_EVENT_KIND_BUTTON:
bcmtch_dev_process_event_button(
bcmtch_data_ptr,
(struct bcmtch_event_button *)ptch_event);
break;
case BCMTCH_EVENT_KIND_GESTURE:
BCMTCH_INFO("ERROR: Gesture: NOT SUPPORTED\n");
break;
case BCMTCH_EVENT_KIND_EXTENSION:
switch (top_level_kind) {
case BCMTCH_EVENT_KIND_FRAME:
bcmtch_dev_process_event_frame_extension(
(struct bcmtch_event_frame_extension *)
ptch_event);
break;
case BCMTCH_EVENT_KIND_TOUCH:
bcmtch_dev_process_event_touch_extension(
bcmtch_data_ptr,
(struct bcmtch_event_touch_extension *)
ptch_event,
bcmtch_data_ptr->touch_event_track_id);
break;
default:
BCMTCH_INFO(
"ERROR: Improper event extension for: tlk=%d k=%d\n",
top_level_kind, kind);
break;
}
break;
default:
BCMTCH_INFO("ERROR: Invalid event kind: %d\n", kind);
}
}
/**
* The last event in the channel is a frame (extension) event.
* Send the SYN_REPORT for the frame.
*/
if (syn_report_pending) {
bcmtch_dev_sync_event_frame(bcmtch_data_ptr);
syn_report_pending = false;
}
BCMTCH_DBG(BCMTCH_DF_FR, "FR:%d", frames_in);
return ret_val;
}
static int32_t bcmtch_dev_process_channel_response(
struct bcmtch_data *bcmtch_data_ptr,
struct bcmtch_channel *chan)
{
int32_t ret_val = 0;
uint16_t read_idx;
struct bcmtch_response_wait *p_resp;
struct tofe_command_response *resp_event;
struct tofe_channel_header *chan_hdr =
(struct tofe_channel_header *)&chan->hdr;
BCMTCH_DBG(
BCMTCH_DF_CH,
"CH:%s() - swap count=%d response evt count=%d.\n",
__func__,
chan_hdr->seq_count,
chan->queued);
read_idx = 0;
/* Process response events */
while ((resp_event =
(struct tofe_command_response *)
bcmtch_inline_channel_read(chan_hdr,
read_idx++))) {
if (resp_event->flags &
TOFE_COMMAND_FLAG_COMMAND_PROCESSED) {
if (resp_event->command > TOFE_COMMAND_LAST)
continue;
/* Save the response result */
p_resp = (struct bcmtch_response_wait *)
&(bcmtch_data_ptr->bcmtch_cmd_response
[resp_event->command]);
p_resp->wait = 0;
p_resp->resp_data = resp_event->data;
}
BCMTCH_DBG(
BCMTCH_DF_CH,
"CH:Response - command=0x%02x result=0x%04x data=0x%08x.\n",
resp_event->command,
resp_event->result,
resp_event->data);
}
return ret_val;
}
/**
Log routine for printing log message in TOFE
@param
[in] code 16-bit log code
[in] param_0 16-bit parameter
[in] param_1 32-bit parameter
[in] param_2 32-bit parameter
@retval
none
*/
static void bcmtch_log_str_print(uint16_t code, uint16_t param0,
uint32_t param1,
uint32_t param2,
uint32_t timestamp)
{
BCMTCH_INFO(
" code:0x%04x para0:0x%04x para1:0x%08x para2:0x%08x ts:0x%08x\n",
code,
param0,
param1,
param2,
timestamp);
}
static int32_t bcmtch_dev_process_channel_log(struct bcmtch_channel *chan)
{
int32_t ret_val = 0;
uint16_t read_idx;
struct tofe_log_msg *log_msg;
struct tofe_channel_header *chan_hdr =
(struct tofe_channel_header *)&chan->hdr;
read_idx = 0;
while ((log_msg =
(struct tofe_log_msg *)
bcmtch_inline_channel_read(chan_hdr,
read_idx++))) {
bcmtch_log_str_print(log_msg->log_code,
log_msg->param_0,
log_msg->params[0],
log_msg->params[1],
log_msg->timestamp);
}
return ret_val;
}
static int32_t bcmtch_dev_process_channels(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
uint32_t channel = 0;
uint8_t chan_set = bcmtch_data_ptr->channel_set;
struct bcmtch_channel *p_chan = NULL;
while (channel < BCMTCH_CHANNEL_MAX) {
p_chan = bcmtch_data_ptr->p_channels[chan_set][channel];
if (!p_chan->active) {
channel++;
continue;
}
switch (channel) {
case BCMTCH_CHANNEL_TOUCH:
bcmtch_dev_process_channel_touch(
bcmtch_data_ptr,
p_chan);
break;
case BCMTCH_CHANNEL_COMMAND:
break;
case BCMTCH_CHANNEL_RESPONSE:
bcmtch_dev_process_channel_response(
bcmtch_data_ptr,
p_chan);
break;
case BCMTCH_CHANNEL_LOG:
bcmtch_dev_process_channel_log(p_chan);
break;
default:
break;
}
if (p_chan->cfg.flags & TOFE_CHANNEL_FLAG_FWDMA_ENABLE)
p_chan->hdr.buffer[0] = p_chan->hdr.buffer[1];
channel++;
}
return ret_val;
}
static int32_t bcmtch_dev_wait_for_firmware_ready(
struct bcmtch_data *bcmtch_data_ptr,
int32_t count)
{
int32_t ret_val = 0;
uint8_t ready;
do {
ret_val =
bcmtch_com_read_spm(
bcmtch_data_ptr,
BCMTCH_SPM_REG_MSG_TO_HOST, &ready);
} while ((!ret_val) && !(ready & TOFE_MESSAGE_FW_READY) && (count--));
if (count <= 0) {
BCMTCH_ERR(
"ERROR: Failed to communicate with Napa FW. Error: 0x%x\n",
ready);
ret_val = -1;
}
return ret_val;
}
static int32_t bcmtch_dev_run_firmware(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
ret_val = bcmtch_dev_reset(bcmtch_data_ptr,
BCMTCH_RESET_MODE_SOFT_CLEAR);
/* set DLDO output */
if (!ret_val)
ret_val = bcmtch_com_write_spm(
bcmtch_data_ptr,
BCMTCH_SPM_REG_PMU_CONTROL2,
BCMTCH_PMU_CNTL2_DLDO_1_1V);
if (bcmtch_dev_wait_for_firmware_ready(
bcmtch_data_ptr,
BCMTCH_FW_READY_WAIT)) {
uint8_t xaddr = 0x40;
uint8_t xdata;
while (xaddr <= 0x61) {
bcmtch_com_read_spm(bcmtch_data_ptr,
xaddr, &xdata);
BCMTCH_ERR("%s: addr = 0x%02x data = 0x%02x\n",
__func__,
xaddr++,
xdata);
}
}
return ret_val;
}
static int32_t bcmtch_dev_parse_firmware(
struct bcmtch_data *bcmtch_data_ptr,
const struct firmware *p_fw)
{
int32_t ret_val = 0;
uint32_t entry_id = 1;
uint32_t *p_cfg = NULL;
uint8_t *mem_data = NULL;
struct tofe_signature *p_tofe_sig = NULL;
struct mtc_detect_cfg *p_mtc_cfg = NULL;
struct combi_entry *p_entry = (struct combi_entry *) p_fw->data;
while (p_entry[entry_id].length) {
if (p_entry[entry_id].flags & BCMTCH_FIRMWARE_FLAGS_ROM_BOOT)
bcmtch_data_ptr->boot_from_rom = true;
switch (p_entry[entry_id].flags & BCMTCH_FIRMWARE_FLAGS_MASK) {
case BCMTCH_FIRMWARE_FLAGS_POST_BOOT_CONFIGS:
mem_data = (uint8_t *)
((uint32_t)p_fw->data
+ p_entry[entry_id].offset);
p_cfg = (uint32_t *)(mem_data + TOFE_SIGNATURE_SIZE);
p_tofe_sig = (struct tofe_signature *)mem_data;
/* Parse F/W Signature */
bcmtch_data_ptr->fw_signature = *p_tofe_sig;
/* Parse MTC parameters */
p_mtc_cfg =
(struct mtc_detect_cfg *)
((uint32_t)mem_data
+ p_cfg[TOFE_TOC_INDEX_DETECT]
- BCMTCH_ADDR_TOC_BASE);
if (p_mtc_cfg) {
bcmtch_data_ptr->axis_x_max =
(p_mtc_cfg->scaling_x_range
> BCMTCH_AXIS_MAX) ?
BCMTCH_AXIS_MAX :
p_mtc_cfg->scaling_x_range - 1;
bcmtch_data_ptr->axis_y_max =
(p_mtc_cfg->scaling_y_range
> BCMTCH_AXIS_MAX) ?
BCMTCH_AXIS_MAX :
p_mtc_cfg->scaling_y_range - 1;
bcmtch_data_ptr->axis_h_max =
bcmtch_max_h_axis(
bcmtch_data_ptr->axis_x_max,
bcmtch_data_ptr->axis_y_max);
bcmtch_data_ptr->threshold_gate_finger =
p_mtc_cfg->class_finger_gate;
bcmtch_data_ptr->threshold_gate_stylus =
p_mtc_cfg->class_stylus_gate;
}
/* Check channel/state protocol mode */
if (!p_cfg[TOFE_TOC_INDEX_CHANNEL])
bcmtch_boot_flag |= BCMTCH_BF_STATE_PROTOCOL;
else
bcmtch_boot_flag &= ~BCMTCH_BF_STATE_PROTOCOL;
break;
case BCMTCH_FIRMWARE_FLAGS_POST_BOOT_CODE:
break;
case BCMTCH_FIRMWARE_FLAGS_CONFIGS:
mem_data = (uint8_t *)
((uint32_t)p_fw->data
+ p_entry[entry_id].offset);
p_cfg = (uint32_t *)(mem_data + TOFE_SIGNATURE_SIZE);
p_tofe_sig = (struct tofe_signature *)mem_data;
/* Parse F/W Signature */
bcmtch_data_ptr->fw_signature = *p_tofe_sig;
/* Parse MTC parameters */
p_mtc_cfg =
(struct mtc_detect_cfg *)
((uint32_t)mem_data
+ p_cfg[TOFE_TOC_INDEX_DETECT]
- p_entry[entry_id].addr);
if (p_mtc_cfg) {
bcmtch_data_ptr->axis_x_max =
(p_mtc_cfg->scaling_x_range
> BCMTCH_AXIS_MAX) ?
BCMTCH_AXIS_MAX :
p_mtc_cfg->scaling_x_range - 1;
bcmtch_data_ptr->axis_y_max =
(p_mtc_cfg->scaling_y_range
> BCMTCH_AXIS_MAX) ?
BCMTCH_AXIS_MAX :
p_mtc_cfg->scaling_y_range - 1;
bcmtch_data_ptr->axis_h_max =
bcmtch_max_h_axis(
bcmtch_data_ptr->axis_x_max,
bcmtch_data_ptr->axis_y_max);
bcmtch_data_ptr->threshold_gate_finger =
p_mtc_cfg->class_finger_gate;
bcmtch_data_ptr->threshold_gate_stylus =
p_mtc_cfg->class_stylus_gate;
}
/* Check channel/state protocol mode */
if (!p_cfg[TOFE_TOC_INDEX_CHANNEL])
bcmtch_boot_flag |= BCMTCH_BF_STATE_PROTOCOL;
else
bcmtch_boot_flag &= ~BCMTCH_BF_STATE_PROTOCOL;
break;
case BCMTCH_FIRMWARE_FLAGS_CODE:
break;
default:
BCMTCH_INFO("UNKNOWN BFF!!! : %d\n", entry_id);
break;
}
/* next */
entry_id++;
}
return ret_val;
}
static unsigned char bcmtchfw15200_bin[] = {
#include "bcmtchfw15200_bin.i"
};
static unsigned char bcmtchfw15300_bin[] = {
#include "bcmtchfw15300_bin.i"
};
struct firmware bcmtchfw15200_fw = {
.data = bcmtchfw15200_bin,
.size = sizeof(bcmtchfw15200_bin)
};
struct firmware bcmtchfw15300_fw = {
.data = bcmtchfw15300_bin,
.size = sizeof(bcmtchfw15300_bin)
};
static int32_t bcmtch_dev_download_firmware(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t *fw_name,
uint32_t fw_addr,
uint32_t fw_flags)
{
struct firmware *p_fw;
int32_t ret_val = 0;
uint32_t entry_id = 1;
struct combi_entry *p_entry = NULL;
struct combi_entry default_entry[] = {
{.addr = fw_addr, .flags = fw_flags,},
{0, 0, 0, 0},
};
if (strncmp("bcmtchfw15200_bin", fw_name, 13) == 0) {
p_fw = &bcmtchfw15200_fw;
BCMTCH_INFO("choosiing fw bcmtchfw15200_bin fw_size = %d\n",
p_fw->size);
} else if (strncmp("bcmtchfw15300_bin", fw_name, 13) == 0) {
p_fw = &bcmtchfw15300_fw;
BCMTCH_INFO("choosiing fw bcmtchfw15300_bin fw_size = %d\n",
p_fw->size);
} else {
ret_val = -1;
BCMTCH_ERR("cannot find a matching firmware\n");
}
if (ret_val) {
BCMTCH_ERR("%s: Firmware request failed (%d) for %s\n",
__func__,
ret_val,
fw_name);
} else {
BCMTCH_DBG(
BCMTCH_DF_INFO,
"INFO: FIRMWARE: %s\n",
fw_name);
BCMTCH_DBG(
BCMTCH_DF_PB,
"PB:f/w size= 0x%x\n",
p_fw->size);
/* pre-process binary according to flags */
if (fw_flags & BCMTCH_FIRMWARE_FLAGS_COMBI) {
p_entry = (struct combi_entry *) p_fw->data;
} else {
p_entry = default_entry;
p_entry[entry_id].length = p_fw->size;
}
/* Parse firmware section flags and set mode */
bcmtch_dev_parse_firmware(
bcmtch_data_ptr,
p_fw);
/* init memory */
ret_val = bcmtch_dev_init_memory(bcmtch_data_ptr);
if (ret_val)
return ret_val;
while (p_entry[entry_id].length && !ret_val) {
switch (p_entry[entry_id].flags &
BCMTCH_FIRMWARE_FLAGS_MASK) {
case BCMTCH_FIRMWARE_FLAGS_POST_BOOT_CONFIGS:
BCMTCH_DBG(
BCMTCH_DF_PB,
"PB:entry_id=%d PB CONFIG\n",
entry_id);
BCMTCH_DBG(
BCMTCH_DF_PB,
"PB:pb chans init addr=0x%08x\n",
p_entry[entry_id].addr);
bcmtch_data_ptr->postboot_cfg_addr =
p_entry[entry_id].addr;
bcmtch_data_ptr->postboot_cfg_length =
p_entry[entry_id].length;
#if BCMTCH_STATE_PROTOCOL
if (!(bcmtch_boot_flag &
BCMTCH_BF_STATE_PROTOCOL))
#endif
ret_val =
bcmtch_dev_init_channels(
bcmtch_data_ptr,
BCMTCH_ADDR_TOC_BASE,
(uint8_t *)
((uint32_t)p_fw->data +
p_entry[entry_id].
offset),
BCMTCH_RAM_CHANNELS);
case BCMTCH_FIRMWARE_FLAGS_POST_BOOT_CODE:
if (!bcmtch_data_ptr->post_boot_sections)
bcmtch_data_ptr->post_boot_buffer =
(uint8_t *) p_fw->data;
bcmtch_data_ptr->post_boot_sections++;
if (p_entry[entry_id].flags &
BCMTCH_FIRMWARE_FLAGS_POST_BOOT_PATCH)
bcmtch_data_ptr->post_boot_patches++;
BCMTCH_DBG(
BCMTCH_DF_PB,
"PB:entry_id=%d PB pb_sec=%d\n",
entry_id,
bcmtch_data_ptr->
post_boot_sections);
break;
case BCMTCH_FIRMWARE_FLAGS_CONFIGS:
#if BCMTCH_STATE_PROTOCOL
if ((bcmtch_data_ptr->boot_from_rom) ||
!(bcmtch_boot_flag &
BCMTCH_BF_STATE_PROTOCOL)) {
/* Channel protocol */
bcmtch_data_ptr->work_process_index =
BCMTCH_WP_CHANNEL;
#endif /* BCMTCH_STATE_PROTOCOL */
/* Initialize channels */
BCMTCH_DBG(
BCMTCH_DF_PB,
"PB:entry_id=%d CONFIG\n",
entry_id);
BCMTCH_DBG(
BCMTCH_DF_PB,
"PB:%s chan init addr=0x%08x\n",
(bcmtch_data_ptr->channel_set) ?
"rom" : "ram",
p_entry[entry_id].addr);
ret_val =
bcmtch_dev_init_channels(
bcmtch_data_ptr,
p_entry[entry_id].addr,
(uint8_t *)
((uint32_t)p_fw->data
+ p_entry[entry_id].
offset),
(bcmtch_data_ptr->
channel_set) ?
BCMTCH_ROM_CHANNELS :
BCMTCH_RAM_CHANNELS);
#if BCMTCH_STATE_PROTOCOL
} else {
/* State protocol */
bcmtch_data_ptr->work_process_index =
BCMTCH_WP_STATE;
}
#endif
default:
BCMTCH_DBG(
BCMTCH_DF_PB,
"PB:entry_id=%d PREBOOT\n",
entry_id);
/** download to chip **/
ret_val = bcmtch_com_write_sys(
bcmtch_data_ptr,
p_entry[entry_id].addr,
p_entry[entry_id].length,
(uint8_t *)((uint32_t)p_fw->data +
p_entry[entry_id].offset));
}
/* next */
entry_id++;
}
}
if (bcmtch_boot_flag &
BCMTCH_BF_DISABLE_POST_BOOT)
bcmtch_data_ptr->post_boot_sections = 0;
if (bcmtch_data_ptr->post_boot_sections) {
/* setup first section for download */
if (!bcmtch_dev_post_boot_get_section(bcmtch_data_ptr))
bcmtch_data_ptr->post_boot_sections = 0;
}
BCMTCH_DBG(BCMTCH_DF_INFO, "INFO: FIRMWARE: loaded\n");
return ret_val;
}
static int32_t bcmtch_dev_find_firmware(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_file = -ENOENT;
uint8_t file = 0;
bool found_chip_id = false;
uint32_t id = bcmtch_data_ptr->chip_id;
uint32_t rev = bcmtch_data_ptr->rev_id;
while (file < ARRAY_SIZE(BCMTCH_BINARIES)) {
/* find matching chip id */
if (BCMTCH_BINARIES[file].chip_id == id) {
found_chip_id = true;
/* if chip id found find matching chip rev */
if (BCMTCH_BINARIES[file].chip_rev == rev) {
ret_file = file;
break;
} else if (BCMTCH_BINARIES[file].chip_rev == BCMTCHWC) {
ret_file = file;
}
} else if (BCMTCH_BINARIES[file].chip_id == BCMTCHWC) {
if (!found_chip_id)
ret_file = file;
}
file++;
}
if (ret_file < 0)
BCMTCH_ERR(
" firmware not configured:chip=0x%8x rev=0x%x\n",
id,
rev);
return ret_file;
}
static int32_t bcmtch_dev_init_firmware(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
uint8_t bin_file = 0;
if (bcmtch_firmware) {
ret_val =
bcmtch_dev_download_firmware(
bcmtch_data_ptr,
bcmtch_firmware,
bcmtch_firmware_addr,
bcmtch_firmware_flag);
} else {
bin_file = bcmtch_dev_find_firmware(bcmtch_data_ptr);
if (bin_file >= 0) {
ret_val =
bcmtch_dev_download_firmware(
bcmtch_data_ptr,
BCMTCH_BINARIES[bin_file].filename,
BCMTCH_BINARIES[bin_file].addr,
BCMTCH_BINARIES[bin_file].flags);
} else
ret_val = bin_file;
}
#if BCMTCH_STATE_PROTOCOL
/* init state protocol data structures. */
if ((bcmtch_boot_flag & BCMTCH_BF_STATE_PROTOCOL)
&& (!bcmtch_data_ptr->boot_from_rom))
bcmtch_dev_init_state(bcmtch_data_ptr);
#endif
if (!ret_val)
ret_val = bcmtch_dev_run_firmware(bcmtch_data_ptr);
#if BCMTCH_STATE_PROTOCOL
if (bcmtch_data_ptr->work_process_index >= BCMTCH_WP_STATE) {
if (!ret_val)
ret_val =
bcmtch_state_resume(
bcmtch_data_ptr);
if (!ret_val) {
ret_val =
bcmtch_state_start_scan(
bcmtch_data_ptr);
}
}
#endif /* BCMTCH_STATE_PROTOCOL */
return ret_val;
}
static int32_t bcmtch_dev_init_platform(struct device *p_device)
{
int32_t ret_val = 0;
struct bcmtch_data *local_bcmtch_data_p = NULL;
#ifdef CONFIG_OF
int32_t idx;
int32_t btn_count;
int32_t of_ret_val;
struct device_node *np;
enum of_gpio_flags gpio_flags;
#else
struct bcmtch_platform_data *p_platform_data = NULL;
#endif
if (p_device) {
local_bcmtch_data_p =
dev_get_drvdata(p_device);
#ifdef CONFIG_OF
np = p_device->of_node;
if (!np) {
BCMTCH_ERR(
" Device tree (DT) error! of_node is NULL.\n");
ret_val = -ENODEV;
return ret_val;
}
/*
* Obtain the address of the SYS/AHB on I2C bus.
*/
of_ret_val =
of_property_read_u32(np, "addr-sys",
&local_bcmtch_data_p
->platform_data.i2c_addr_sys);
if (of_ret_val) {
BCMTCH_ERR("DT property addr-sys not found!\n");
goto of_read_error;
}
BCMTCH_DBG(BCMTCH_DF_DT, "DT:addr-sys = 0x%x\n",
local_bcmtch_data_p->platform_data.i2c_addr_sys);
/*
* Obtain the GPIO reset pin.
*/
if (!of_find_property(np, "reset-gpios", NULL)) {
BCMTCH_ERR(
" DT property reset-gpios not found!\n");
ret_val = of_ret_val;
local_bcmtch_data_p->
platform_data.gpio_reset_pin = -1;
local_bcmtch_data_p->
platform_data.gpio_reset_polarity = -1;
} else {
local_bcmtch_data_p->
platform_data.gpio_reset_pin =
of_get_named_gpio_flags(
np,
"reset-gpios",
0,
&gpio_flags);
BCMTCH_DBG(
BCMTCH_DF_DT,
"DT:gpio-reset-pin = 0x%x\n",
local_bcmtch_data_p->platform_data
.gpio_reset_pin);
local_bcmtch_data_p->
platform_data.gpio_reset_polarity =
gpio_flags & OF_GPIO_ACTIVE_LOW;
BCMTCH_DBG(
BCMTCH_DF_DT,
"DT:gpio-reset-polarity = 0x%x\n",
local_bcmtch_data_p->platform_data
.gpio_reset_polarity);
/*
* Obtain the GPIO reset time in ms.
*/
of_ret_val = of_property_read_u32(np, "reset-time-ms",
&local_bcmtch_data_p
->platform_data
.gpio_reset_time_ms);
if (of_ret_val) {
/* set default value */
local_bcmtch_data_p->
platform_data.
gpio_reset_time_ms = 100;
}
BCMTCH_DBG(
BCMTCH_DF_DT,
"DT:gpio-reset-time = %u\n",
local_bcmtch_data_p->platform_data
.gpio_reset_time_ms);
}
/*
* Obtain the interrupt pin.
*/
local_bcmtch_data_p->platform_data.touch_irq =
irq_of_parse_and_map(np, 0);
if (local_bcmtch_data_p->platform_data.touch_irq) {
BCMTCH_DBG(BCMTCH_DF_DT, "DT: irq = 0x%x",
local_bcmtch_data_p->platform_data.touch_irq);
local_bcmtch_data_p->
platform_data.gpio_interrupt_pin = -1;
local_bcmtch_data_p->
platform_data.gpio_interrupt_trigger =
IRQF_TRIGGER_NONE;
} else {
BCMTCH_ERR(
"DT: interrupts (irq) request failed!\n");
of_ret_val = -ENOENT;
goto of_read_error;
}
/*
* Setup function pointers for axis coordinates.
*/
of_ret_val =
of_property_read_u32(np,
"axis-orientation-flag",
&local_bcmtch_data_p->platform_data
.axis_orientation_flag);
if (of_ret_val) {
pr_warn(" DT property axis-orientation-flag not found!\n");
local_bcmtch_data_p->platform_data
.axis_orientation_flag = 0;
}
BCMTCH_DBG(
BCMTCH_DF_DT,
"DT:axis-orientation-flag = 0x%02x\n",
local_bcmtch_data_p->platform_data
.axis_orientation_flag);
/*
* Obtain the key map.
*/
of_ret_val =
of_property_read_u32(np, "ext-button-count",
&btn_count);
if (of_ret_val) {
BCMTCH_INFO(
"DT property ext-button-count not found!\n");
btn_count = 0;
}
if (btn_count) {
BCMTCH_DBG(
BCMTCH_DF_DT,
"DT:ext-button-count = %d\n",
btn_count);
/* Allocate array */
local_bcmtch_data_p
->platform_data.ext_button_map =
(const int *)local_bcmtch_data_p
->bcmtch_button_map;
/* Read array data from device tree */
of_ret_val =
of_property_read_u32_array(
np, "ext-button-map",
(u32 *)local_bcmtch_data_p
->platform_data
.ext_button_map,
btn_count);
if (of_ret_val) {
BCMTCH_ERR(
" DT property ext-button-map read failed!\n");
local_bcmtch_data_p->platform_data
.ext_button_count = 0;
} else {
local_bcmtch_data_p->platform_data
.ext_button_count = btn_count;
BCMTCH_DBG(
BCMTCH_DF_DT,
"DT:ext-button-map =");
for (idx = 0; idx < btn_count; idx++)
BCMTCH_DBG(
BCMTCH_DF_DT,
"DT: %d",
local_bcmtch_data_p
->platform_data
.ext_button_map[idx]);
}
}
#else /* CONFIG_OF */
p_platform_data =
(struct bcmtch_platform_data *)p_device->platform_data;
local_bcmtch_data_p->platform_data.i2c_addr_sys =
p_platform_data->i2c_addr_sys;
local_bcmtch_data_p->platform_data.i2c_addr_spm =
p_platform_data->i2c_addr_spm;
local_bcmtch_data_p->platform_data.gpio_reset_pin =
p_platform_data->gpio_reset_pin;
local_bcmtch_data_p->platform_data.gpio_reset_polarity =
p_platform_data->gpio_reset_polarity;
local_bcmtch_data_p->platform_data.gpio_reset_time_ms =
p_platform_data->gpio_reset_time_ms;
local_bcmtch_data_p->platform_data.gpio_interrupt_pin =
p_platform_data->gpio_interrupt_pin;
local_bcmtch_data_p->platform_data.gpio_interrupt_trigger =
p_platform_data->gpio_interrupt_trigger;
local_bcmtch_data_p->platform_data.touch_irq = gpio_to_irq(
p_platform_data->gpio_interrupt_pin);
local_bcmtch_data_p->platform_data.ext_button_count =
p_platform_data->ext_button_count;
local_bcmtch_data_p->platform_data.ext_button_map =
p_platform_data->ext_button_map;
local_bcmtch_data_p->platform_data.axis_orientation_flag =
p_platform_data->axis_orientation_flag;
#endif /* CONFIG_OF */
/*
* FIXME - these values would come from DT or FW or insmod
*
* NAPA POR is 4ms == 4000us
* - Value should NOT be less than 5000us
*/
local_bcmtch_data_p->power_on_delay_us = 60000; /* >= 5000 */
} else {
BCMTCH_ERR("%s() error, platform data == NULL\n",
__func__);
ret_val = -ENODATA;
}
return ret_val;
#ifdef CONFIG_OF
of_read_error:
if (!ret_val)
ret_val = -ENODEV;
return ret_val;
#endif
}
static int32_t bcmtch_dev_request_power_mode(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t mode, enum tofe_command command)
{
int32_t ret_val = 0;
uint8_t regs[5];
uint8_t data[5];
regs[0] = BCMTCH_SPM_REG_MSG_FROM_HOST;
data[0] = command;
regs[1] = BCMTCH_SPM_REG_RQST_FROM_HOST;
data[1] = 0;
switch (mode) {
case BCMTCH_POWER_MODE_SLEEP:
data[1] = BCMTCH_POWER_MODE_SLEEP;
break;
case BCMTCH_POWER_MODE_WAKE:
data[1] = BCMTCH_POWER_MODE_WAKE;
break;
case BCMTCH_POWER_MODE_NOWAKE:
data[1] = BCMTCH_POWER_MODE_NOWAKE;
break;
default:
PROGRESS();
break;
}
ret_val = bcmtch_com_fast_write_spm(bcmtch_data_ptr, 2, regs, data);
return ret_val;
}
static int32_t bcmtch_dev_get_power_state(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
uint8_t power_state;
ret_val = bcmtch_com_read_spm(bcmtch_data_ptr,
BCMTCH_SPM_REG_PSR, &power_state);
return (ret_val) ? (ret_val) : ((uint32_t)power_state);
}
static int32_t bcmtch_dev_set_power_state(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t power_state)
{
int32_t ret_val = 0;
switch (power_state) {
case BCMTCH_POWER_STATE_SLEEP:
ret_val =
bcmtch_dev_request_power_mode(
bcmtch_data_ptr,
BCMTCH_POWER_MODE_SLEEP,
TOFE_COMMAND_NO_COMMAND);
break;
case BCMTCH_POWER_STATE_RETENTION:
PROGRESS();
break;
case BCMTCH_POWER_STATE_IDLE:
PROGRESS();
break;
case BCMTCH_POWER_STATE_ACTIVE:
PROGRESS();
break;
default:
PROGRESS();
break;
}
return ret_val;
}
static int32_t bcmtch_dev_check_power_state(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t power_state,
uint8_t wait_count)
{
int32_t ret_val = -EAGAIN;
int32_t read_state;
do {
read_state = bcmtch_dev_get_power_state(bcmtch_data_ptr);
if (read_state == power_state) {
ret_val = 0;
break;
}
} while (wait_count--);
return ret_val;
}
static enum bcmtch_status bcmtch_dev_request_host_override(
struct bcmtch_data *bcmtch_data_ptr,
enum tofe_command command)
{
enum bcmtch_status ret_val = BCMTCH_STATUS_ERR_FAIL;
int32_t count = 250;
uint8_t m2h;
/* Request channel & wakeup the firmware */
ret_val = bcmtch_dev_request_power_mode(
bcmtch_data_ptr,
BCMTCH_POWER_MODE_WAKE,
command);
if (!ret_val)
ret_val = bcmtch_dev_check_power_state(
bcmtch_data_ptr,
BCMTCH_POWER_STATE_ACTIVE,
25);
if (ret_val) {
BCMTCH_ERR("%s: [%d] wake firmware failed.\n",
__func__,
ret_val);
} else {
/* Wait till FW OVERRIDE is ready */
do {
ret_val = bcmtch_com_read_spm(
bcmtch_data_ptr,
BCMTCH_SPM_REG_MSG_TO_HOST,
&m2h);
switch (m2h) {
case TOFE_MESSAGE_FW_READY_OVERRIDE:
case TOFE_MESSAGE_FW_READY_INTERRUPT_OVERRIDE:
bcmtch_data_ptr->host_override = true;
BCMTCH_DBG(
BCMTCH_DF_HO,
"HO: Request m=0x%0x ho=%d",
m2h,
bcmtch_data_ptr->host_override);
break;
case TOFE_MESSAGE_FW_READY_INTERRUPT:
BCMTCH_DBG(
BCMTCH_DF_HO,
"HO: Request m=0x%0x ho=%d -> interrupt",
m2h,
bcmtch_data_ptr->host_override);
bcmtch_dev_process(bcmtch_data_ptr);
case TOFE_MESSAGE_FW_READY:
default:
BCMTCH_DBG(
BCMTCH_DF_HO,
"HO: Request m=0x%0x ho=%d",
m2h,
bcmtch_data_ptr->host_override);
break;
}
} while (!bcmtch_data_ptr->host_override && count--);
if (bcmtch_data_ptr->host_override)
ret_val = BCMTCH_STATUS_SUCCESS;
}
return ret_val;
}
static enum bcmtch_status bcmtch_dev_release_host_override(
struct bcmtch_data *bcmtch_data_ptr,
enum tofe_command command)
{
enum bcmtch_status ret_val = BCMTCH_STATUS_ERR_FAIL;
int32_t count = 250;
uint8_t m2h;
/* this should release hostOverride - do we need to check */
/* Release channel */
ret_val = bcmtch_dev_request_power_mode(
bcmtch_data_ptr,
BCMTCH_POWER_MODE_NOWAKE,
command);
/* Wait till FW is ready */
do {
ret_val = bcmtch_com_read_spm(bcmtch_data_ptr,
BCMTCH_SPM_REG_MSG_TO_HOST, &m2h);
switch (m2h) {
case TOFE_MESSAGE_FW_READY_INTERRUPT_OVERRIDE:
BCMTCH_DBG(
BCMTCH_DF_HO,
"HO: Release m=0x%0x ho=%d -> interrupt",
m2h,
bcmtch_data_ptr->host_override);
bcmtch_dev_process(bcmtch_data_ptr);
case TOFE_MESSAGE_FW_READY_OVERRIDE:
default:
BCMTCH_DBG(
BCMTCH_DF_HO,
"HO: Release m=0x%0x ho=%d",
m2h,
bcmtch_data_ptr->host_override);
break;
case TOFE_MESSAGE_FW_READY_INTERRUPT:
case TOFE_MESSAGE_FW_READY:
bcmtch_data_ptr->host_override = false;
BCMTCH_DBG(
BCMTCH_DF_HO,
"HO: Release m=0x%0x ho=%d",
m2h,
bcmtch_data_ptr->host_override);
break;
}
} while (bcmtch_data_ptr->host_override && count--);
if (!bcmtch_data_ptr->host_override)
ret_val = BCMTCH_STATUS_SUCCESS;
return ret_val;
}
static int32_t bcmtch_dev_send_command(
struct bcmtch_data *bcmtch_data_ptr,
enum tofe_command command,
uint32_t data,
uint16_t data16,
uint8_t flags)
{
int32_t ret_val = 0;
struct tofe_command_response cmd;
struct bcmtch_channel *chan;
uint8_t chan_set = bcmtch_data_ptr->channel_set;
if (command == TOFE_COMMAND_NO_COMMAND) {
BCMTCH_ERR("%s: no_command.\n",
__func__);
return -EINVAL;
}
chan =
bcmtch_data_ptr->p_channels[chan_set][TOFE_CHANNEL_ID_COMMAND];
if (chan == NULL) {
BCMTCH_ERR(
"%s: command channel has not initialized!\n",
__func__);
return -ENXIO;
}
if (bcmtch_dev_request_host_override(
bcmtch_data_ptr, command) ==
BCMTCH_STATUS_SUCCESS) {
/* Setup the command entry */
memset(
(void *)(&cmd),
0,
sizeof(struct tofe_command_response));
cmd.flags = flags;
cmd.command = command;
cmd.data = data;
cmd.result = data16;
/* Write sys to command channel */
tofe_channel_write_begin(&chan->hdr);
ret_val = tofe_channel_write(&chan->hdr, &cmd);
if (ret_val) {
BCMTCH_ERR("%s: [%d] cmd channel write failed.\n",
__func__,
ret_val);
goto send_command_exit;
}
ret_val = bcmtch_dev_write_channel(bcmtch_data_ptr,
chan);
if (ret_val) {
BCMTCH_ERR(
"%s: [%d] cmd channel write back FW failed.\n",
__func__,
ret_val);
goto send_command_exit;
}
}
bcmtch_dev_release_host_override(bcmtch_data_ptr,
command);
send_command_exit:
return ret_val;
}
static int32_t bcmtch_dev_reset(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t mode)
{
int32_t ret_val = 0;
switch (mode) {
case BCMTCH_RESET_MODE_HARD:
bcmtch_reset(bcmtch_data_ptr);
break;
case BCMTCH_RESET_MODE_SOFT_CHIP:
bcmtch_com_write_spm(
bcmtch_data_ptr,
BCMTCH_SPM_REG_SOFT_RESETS,
BCMTCH_RESET_MODE_SOFT_CHIP);
break;
case BCMTCH_RESET_MODE_SOFT_ARM:
bcmtch_com_write_spm(
bcmtch_data_ptr,
BCMTCH_SPM_REG_SOFT_RESETS,
BCMTCH_RESET_MODE_SOFT_ARM);
break;
case (BCMTCH_RESET_MODE_SOFT_CHIP | BCMTCH_RESET_MODE_SOFT_ARM):
bcmtch_com_write_spm(
bcmtch_data_ptr,
BCMTCH_SPM_REG_SOFT_RESETS,
BCMTCH_RESET_MODE_SOFT_CHIP);
bcmtch_com_write_spm(
bcmtch_data_ptr,
BCMTCH_SPM_REG_SOFT_RESETS,
BCMTCH_RESET_MODE_SOFT_ARM);
break;
case BCMTCH_RESET_MODE_SOFT_CLEAR:
ret_val = bcmtch_com_write_spm(
bcmtch_data_ptr,
BCMTCH_SPM_REG_SOFT_RESETS,
BCMTCH_RESET_MODE_SOFT_CLEAR);
break;
default:
break;
}
return ret_val;
}
static int32_t bcmtch_dev_get_rev_id(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = -ENXIO;
if (bcmtch_data_ptr) {
ret_val =
bcmtch_com_read_spm(bcmtch_data_ptr,
BCMTCH_SPM_REG_REVISIONID,
&bcmtch_data_ptr->rev_id);
}
return ret_val;
}
static int32_t bcmtch_dev_get_chip_id(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = -ENXIO;
if (bcmtch_data_ptr) {
uint8_t id[3];
ret_val =
bcmtch_com_read_spm(bcmtch_data_ptr,
BCMTCH_SPM_REG_CHIPID0, &id[0]);
ret_val |=
bcmtch_com_read_spm(bcmtch_data_ptr,
BCMTCH_SPM_REG_CHIPID1, &id[1]);
ret_val |=
bcmtch_com_read_spm(bcmtch_data_ptr,
BCMTCH_SPM_REG_CHIPID2, &id[2]);
bcmtch_data_ptr->chip_id = ((((uint32_t)id[2]) << 16)
| (((uint32_t)id[1]) << 8)
| (uint32_t)id[0]);
}
return ret_val;
}
static int32_t bcmtch_dev_verify_chip_version(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
uint32_t idx = 0;
uint32_t *chips_ptr = (uint32_t *)BCMTCH_CHIP_IDS;
/* Get Chip ID AFTER Power On due to OTP */
ret_val = bcmtch_dev_get_chip_id(bcmtch_data_ptr);
if (!ret_val)
ret_val = bcmtch_dev_get_rev_id(bcmtch_data_ptr);
if (!ret_val) {
ret_val = -ENXIO;
for (idx = 0;
idx < ARRAY_SIZE(BCMTCH_CHIP_IDS);
idx++) {
if (chips_ptr[idx] ==
bcmtch_data_ptr->chip_id) {
/* Found a match in above search */
ret_val = 0;
BCMTCH_DBG(
BCMTCH_DF_INFO,
"INFO: chip_id = 0x%06X rev = 0x%2X : %s\n",
bcmtch_data_ptr->chip_id,
bcmtch_data_ptr->rev_id,
"Verified");
}
}
}
if (ret_val)
BCMTCH_ERR(
" chip_id = 0x%06X rev = 0x%2X : %s : 0x%x\n",
bcmtch_data_ptr->chip_id,
bcmtch_data_ptr->rev_id,
"Error - Unknown device",
ret_val);
return ret_val;
}
static void bcmtch_dev_init_worker_process(
struct bcmtch_data *bcmtch_data_ptr)
{
/* Channel protocol process function. */
bcmtch_data_ptr->bcmtch_dev_process_table
[BCMTCH_WP_CHANNEL] = bcmtch_dev_process;
/* State protocol process function. */
#if BCMTCH_STATE_PROTOCOL
bcmtch_data_ptr->bcmtch_dev_process_table
[BCMTCH_WP_STATE] =
bcmtch_dev_process_state_touches;
#endif /* BCMTCH_STATE_PROTOCOL */
/* The process function during the post boot patch
* switch (boot from ROM to boot from RAM). */
bcmtch_data_ptr->bcmtch_dev_process_table
[BCMTCH_WP_PATCH_INIT] =
bcmtch_dev_process_pb_patch_init;
/* Set the default work process function. */
bcmtch_data_ptr->work_process_index =
BCMTCH_WP_CHANNEL;
}
#if BCMTCH_STATE_PROTOCOL
static void bcmtch_dev_init_state(
struct bcmtch_data *bcmtch_data_ptr)
{
if (bcmtch_boot_flag & BCMTCH_BF_STATE_PROTOCOL) {
bcmtch_data_ptr->scan_data.cmd.touch_slot_format =
(bcmtch_boot_flag & BCMTCH_BF_STATE_SHORT_SLOT) ?
1 : 0;
bcmtch_data_ptr->scan_data.cmd.sync_mode =
(bcmtch_boot_flag & BCMTCH_BF_STATE_SYNC_MODE) ?
0 : 1;
bcmtch_data_ptr->scan_data.cmd.max_slot_number =
BCMTCH_MAX_TOUCH;
bcmtch_data_ptr->scan_data.touch_slot_size =
(bcmtch_boot_flag & BCMTCH_BF_STATE_SHORT_SLOT) ?
TOFE_HOST_TOUCH_SLOT_SMALL_SIZE
: TOFE_HOST_TOUCH_SLOT_BIG_SIZE;
memset(&bcmtch_data_ptr->ram_rw_cfg, 0,
sizeof(struct bcmtch_state_cfg_rw));
bcmtch_data_ptr->work_process_index =
BCMTCH_WP_STATE;
BCMTCH_DBG(
BCMTCH_DF_ST,
"ST:%s() p0=0x%x slot_format %d sync_mode %d resp_buff_size %d max_slot_number %d\n",
__func__,
bcmtch_data_ptr->scan_data.cmd.reg,
bcmtch_data_ptr->scan_data.cmd.touch_slot_format,
bcmtch_data_ptr->scan_data.cmd.sync_mode,
TOFE_HOST_RSP_BUF_SIZE,
bcmtch_data_ptr->scan_data.cmd.max_slot_number);
} else {
bcmtch_data_ptr->work_process_index =
BCMTCH_WP_CHANNEL;
}
}
#endif /* BCMTCH_STATE_PROTOCOL */
static int32_t bcmtch_dev_init(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
/* init com */
if (!ret_val)
ret_val = bcmtch_com_init(bcmtch_data_ptr);
/* wakeup */
if (!ret_val)
ret_val = bcmtch_dev_request_power_mode(
bcmtch_data_ptr,
BCMTCH_POWER_MODE_WAKE,
TOFE_COMMAND_NO_COMMAND);
if (!ret_val)
ret_val = bcmtch_dev_check_power_state(
bcmtch_data_ptr,
BCMTCH_POWER_STATE_ACTIVE,
25);
/* init clocks */
if (!ret_val)
ret_val = bcmtch_dev_init_clocks(bcmtch_data_ptr);
/* read chip version and id with power on */
if (!ret_val && (BCMTCH_BF_VERIFY_CHIP & bcmtch_boot_flag))
ret_val = bcmtch_dev_verify_chip_version(bcmtch_data_ptr);
/* download and run */
if (!ret_val)
ret_val = bcmtch_dev_init_firmware(bcmtch_data_ptr);
return ret_val;
}
static void bcmtch_dev_process(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
uint8_t m2h;
if (bcmtch_boot_flag & BCMTCH_BF_CHECK_INTERRUPT) {
/* Check msg2host */
bcmtch_com_read_spm(bcmtch_data_ptr,
BCMTCH_SPM_REG_MSG_TO_HOST, &m2h);
if ((m2h & TOFE_MESSAGE_FW_READY_INTERRUPT) !=
TOFE_MESSAGE_FW_READY_INTERRUPT) {
BCMTCH_INFO("False interrupt\n");
return;
}
}
/* read DMA buffer */
if (bcmtch_data_ptr->has_dma_channel &&
!bcmtch_data_ptr->host_override) {
ret_val =
bcmtch_dev_read_dma_channels(bcmtch_data_ptr);
}
/* read channels */
ret_val = bcmtch_dev_read_channels(bcmtch_data_ptr);
/* release memory */
bcmtch_dev_request_power_mode(
bcmtch_data_ptr,
BCMTCH_POWER_MODE_NOWAKE,
TOFE_COMMAND_NO_COMMAND);
/* process channels */
ret_val = bcmtch_dev_process_channels(bcmtch_data_ptr);
}
static void bcmtch_dev_process_pb_patch_init(
struct bcmtch_data *bcmtch_data_ptr)
{
uint8_t m2h;
/* Check post boot init req TOFE_MESSAGE_SOC_REBOOT_PENDING */
if (bcmtch_data_ptr->post_boot_pending) {
bcmtch_com_read_spm(bcmtch_data_ptr,
BCMTCH_SPM_REG_MSG_TO_HOST, &m2h);
if (m2h == TOFE_MESSAGE_SOC_REBOOT_PENDING) {
/* Reset post boot pending */
bcmtch_data_ptr->post_boot_pending = 0;
/* Send command TOFE_COMMAND_REBOOT_APPROVED */
bcmtch_dev_request_power_mode(
bcmtch_data_ptr,
BCMTCH_POWER_MODE_NOWAKE,
TOFE_COMMAND_REBOOT_APPROVED);
BCMTCH_DBG(
BCMTCH_DF_PB,
"PB:sent REBOOT_APPROVED\n");
#if BCMTCH_STATE_PROTOCOL
if (bcmtch_boot_flag &
BCMTCH_BF_STATE_PROTOCOL) {
/* Switch to state protocol
work process function.*/
bcmtch_dev_init_state(bcmtch_data_ptr);
bcmtch_state_resume(bcmtch_data_ptr);
bcmtch_state_start_scan(
bcmtch_data_ptr);
} else {
#endif
/* Switch channel mode. */
bcmtch_data_ptr->work_process_index =
BCMTCH_WP_CHANNEL;
bcmtch_data_ptr->channel_set =
BCMTCH_RAM_CHANNELS;
bcmtch_dev_reset_events(bcmtch_data_ptr);
#if BCMTCH_STATE_PROTOCOL
}
#endif
return;
}
}
/* Process the ROM channel */
bcmtch_dev_process(bcmtch_data_ptr);
}
#if BCMTCH_STATE_PROTOCOL
static inline int32_t bcmtch_state_start_scan(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
uint8_t parameters[TOFE_HOST_CMD_SHORT_SIZE];
parameters[0] = bcmtch_data_ptr->scan_data.cmd.reg;
ret_val = bcmtch_state_i2c_send_command(
bcmtch_data_ptr,
BCMTCH_CMD_START_SCAN,
parameters,
1);
BCMTCH_DBG(BCMTCH_DF_ST, "ST: SCAN START: (p0=0x%x) = %d\n",
bcmtch_data_ptr->scan_data.cmd.reg,
ret_val);
return ret_val;
}
static inline int32_t bcmtch_state_stop_scan(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
ret_val = bcmtch_state_i2c_send_command(
bcmtch_data_ptr,
BCMTCH_CMD_STOP_SCAN,
NULL,
0);
BCMTCH_DBG(BCMTCH_DF_ST, "ST: SCAN STOP: = %d\n", ret_val);
return ret_val;
}
static inline int32_t bcmtch_state_suspend(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
ret_val = bcmtch_state_stop_scan(bcmtch_data_ptr);
if (ret_val)
return ret_val;
ret_val = bcmtch_state_i2c_send_command(
bcmtch_data_ptr,
BCMTCH_CMD_SUSPEND,
NULL,
0);
if (!ret_val) {
/* check command status */
ret_val = bcmtch_state_check_command_status(
bcmtch_data_ptr,
100);
BCMTCH_DBG(BCMTCH_DF_ST, "ST: CMD SUSPEND - 0x%x\n",
bcmtch_data_ptr->bcmtch_state_resp_buffer[0]);
}
return ret_val;
}
static inline int32_t bcmtch_state_sleep(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
ret_val = bcmtch_state_stop_scan(bcmtch_data_ptr);
if (ret_val)
return ret_val;
ret_val = bcmtch_state_i2c_send_command(
bcmtch_data_ptr,
BCMTCH_CMD_SLEEP,
NULL,
0);
if (!ret_val) {
/* check command status */
ret_val = bcmtch_state_check_command_status(
bcmtch_data_ptr,
100);
BCMTCH_DBG(BCMTCH_DF_ST, "ST: CMD SLEEP - 0x%x\n",
bcmtch_data_ptr->bcmtch_state_resp_buffer[0]);
}
return ret_val;
}
static inline int32_t bcmtch_state_resume(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
ret_val = bcmtch_state_stop_scan(bcmtch_data_ptr);
if (ret_val)
return ret_val;
ret_val = bcmtch_state_i2c_send_command(
bcmtch_data_ptr,
BCMTCH_CMD_RESUME,
NULL,
0);
if (!ret_val) {
/* check command status */
ret_val = bcmtch_state_check_command_status(
bcmtch_data_ptr,
100);
BCMTCH_DBG(BCMTCH_DF_ST, "ST: CMD RESUME - 0x%x\n",
bcmtch_data_ptr->bcmtch_state_resp_buffer[0]);
}
return ret_val;
}
static int32_t bcmtch_state_rw_cfg_init(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
uint8_t *parameters =
bcmtch_data_ptr->bcmtch_state_resp_buffer;
uint8_t *dma_buff =
bcmtch_data_ptr->bcmtch_state_resp_buffer;
struct bcmtch_state_rw_params *header =
(struct bcmtch_state_rw_params *)
bcmtch_data_ptr->bcmtch_state_resp_buffer;
/* The Write RAM command returns configuration when
* the data size is set to zero.
*/
header->len = 0;
ret_val = bcmtch_state_i2c_send_command(
bcmtch_data_ptr,
BCMTCH_CMD_WRITE_RAM,
parameters,
sizeof(struct bcmtch_state_rw_params));
if (ret_val) {
BCMTCH_ERR("ST: %s send cmd error! (%d)\n",
__func__,
ret_val);
return ret_val;
}
/* check command status */
ret_val = bcmtch_state_check_command_status(
bcmtch_data_ptr,
5);
if (!ret_val)
ret_val = bcmtch_com_read_dma(
bcmtch_data_ptr,
sizeof(struct bcmtch_state_cfg_rw) + 1,
dma_buff);
if (!ret_val)
memcpy(&bcmtch_data_ptr->ram_rw_cfg,
&dma_buff[1],
sizeof(struct bcmtch_state_cfg_rw));
BCMTCH_INFO("ST: RAM RW cfg init: max_r=%d max_w=%d\n",
bcmtch_data_ptr->ram_rw_cfg.max_read_size,
bcmtch_data_ptr->ram_rw_cfg.max_write_size);
/* Resume the touch scan */
bcmtch_state_start_scan(bcmtch_data_ptr);
return ret_val;
}
static int32_t bcmtch_state_read_ram(
struct bcmtch_data *bcmtch_data_ptr,
uint32_t addr,
uint16_t len,
uint8_t *out)
{
int32_t ret_val = 0;
uint8_t *parameters =
bcmtch_data_ptr->bcmtch_state_resp_buffer;
uint8_t *dma_buff =
bcmtch_data_ptr->bcmtch_state_resp_buffer;
struct bcmtch_state_rw_params *header =
(struct bcmtch_state_rw_params *)
bcmtch_data_ptr->bcmtch_state_resp_buffer;
/* Check the initialization of the read/write config. */
if (bcmtch_data_ptr->ram_rw_cfg.cfg_size == 0) {
ret_val =
bcmtch_state_rw_cfg_init(bcmtch_data_ptr);
if (ret_val)
return ret_val;
}
if (len > bcmtch_data_ptr->ram_rw_cfg.max_read_size)
return -EINVAL;
ret_val = bcmtch_state_stop_scan(bcmtch_data_ptr);
if (ret_val)
return ret_val;
header->addr = addr;
header->len = len;
ret_val = bcmtch_state_i2c_send_command(
bcmtch_data_ptr,
BCMTCH_CMD_READ_RAM,
parameters,
sizeof(struct bcmtch_state_rw_params));
if (ret_val) {
BCMTCH_ERR("ST: %s send cmd error! (%d)\n",
__func__,
ret_val);
return ret_val;
}
/* check command status */
ret_val = bcmtch_state_check_command_status(
bcmtch_data_ptr,
5);
BCMTCH_DBG(
BCMTCH_DF_ST,
"ST: CMD R RAM - (%d) addr=0x%08x size=%d\n",
bcmtch_data_ptr->bcmtch_state_resp_buffer[0],
addr,
len);
/* read response data */
if (!ret_val)
ret_val = bcmtch_com_read_dma(
bcmtch_data_ptr,
len + 1,
dma_buff);
/* copy the RAM content */
memcpy(out, &dma_buff[1], len);
/* Resume the touch scan */
bcmtch_state_start_scan(bcmtch_data_ptr);
return ret_val;
}
static int32_t bcmtch_state_write_ram(
struct bcmtch_data *bcmtch_data_ptr,
uint32_t addr,
uint16_t len,
uint8_t *data)
{
int32_t ret_val = 0;
uint8_t *parameters =
bcmtch_data_ptr->bcmtch_state_resp_buffer;
struct bcmtch_state_rw_params *header =
(struct bcmtch_state_rw_params *)
bcmtch_data_ptr->bcmtch_state_resp_buffer;
uint16_t data_size = len
+ sizeof(struct bcmtch_state_rw_params);
/* Check the initialization of the read/write config. */
if (bcmtch_data_ptr->ram_rw_cfg.cfg_size == 0) {
ret_val =
bcmtch_state_rw_cfg_init(bcmtch_data_ptr);
if (ret_val)
return ret_val;
}
if (data_size > bcmtch_data_ptr->ram_rw_cfg.max_write_size)
return -EINVAL;
header->addr = addr;
header->len = len;
memcpy(parameters + sizeof(struct bcmtch_state_rw_params),
data, len);
ret_val = bcmtch_state_stop_scan(bcmtch_data_ptr);
if (ret_val)
return ret_val;
ret_val = bcmtch_state_i2c_send_command(
bcmtch_data_ptr,
BCMTCH_CMD_WRITE_RAM,
parameters,
data_size);
if (ret_val) {
BCMTCH_ERR("ST: %s send cmd error! (%d)\n",
__func__,
ret_val);
return ret_val;
}
/* check command status */
ret_val = bcmtch_state_check_command_status(
bcmtch_data_ptr,
5);
BCMTCH_DBG(
BCMTCH_DF_ST,
"ST: CMD W RAM - (%d) addr=0x%08x size=%d\n",
bcmtch_data_ptr->bcmtch_state_resp_buffer[0],
addr,
len);
/* Resume the touch scan */
bcmtch_state_start_scan(bcmtch_data_ptr);
return ret_val;
}
#if BCMTCH_STATE_PROTOCOL
static void bcmtch_state_check_fw_exception(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t fw_exception)
{
char *msg[] = {
"Reset", "Reset", "NMI", "HardFault",
"MemManage", "BusFault", "UsageFault",
};
uint32_t read_size =
ARRAY_SIZE(bcmtch_data_ptr->exception_buffer)
* sizeof(uint32_t);
uint32_t *ex_buf = bcmtch_data_ptr->exception_buffer;
if (fw_exception < ARRAY_SIZE(msg))
BCMTCH_ERR("FWE:Critical error %d [%s]\n",
fw_exception, msg[fw_exception]);
else
BCMTCH_ERR("FWE:Critical error %d\n", fw_exception);
if (bcmtch_data_ptr->work_process_index >= BCMTCH_WP_STATE) {
memset(ex_buf, 0, read_size);
bcmtch_com_read_dma(
bcmtch_data_ptr, read_size, (uint8_t *)ex_buf);
if (ex_buf[15] == 0xDEADBEEF || ex_buf[15] == 0xDEADBEEB) {
BCMTCH_ERR(
"FWE:R0 = 0x%08X\n",
ex_buf[0]);
BCMTCH_ERR(
"FWE:R1 = 0x%08X\n",
ex_buf[1]);
BCMTCH_ERR(
"FWE:R2 = 0x%08X\n",
ex_buf[2]);
BCMTCH_ERR(
"FWE:R3 = 0x%08X\n",
ex_buf[3]);
BCMTCH_ERR(
"FWE:R12 = 0x%08X\n",
ex_buf[4]);
BCMTCH_ERR(
"FWE:LR = 0x%08X\n",
ex_buf[5]);
BCMTCH_ERR(
"FWE:PC = 0x%08X\n",
ex_buf[6]);
BCMTCH_ERR(
"FWE:PSR = 0x%08X\n",
ex_buf[7]);
BCMTCH_ERR(
"FWE:SP = 0x%08X\n",
ex_buf[8]);
BCMTCH_ERR(
"FWE:SCB_CCR = 0x%08X\n",
ex_buf[9]);
BCMTCH_ERR(
"FWE:SCB_SHCSR = 0x%08X\n",
ex_buf[10]);
BCMTCH_ERR(
"FWE:SCB_CFS = 0x%08X\n",
ex_buf[11]);
BCMTCH_ERR(
"FWE:SCB_HFSR = 0x%08X\n",
ex_buf[12]);
BCMTCH_ERR(
"FWE:SCB_MMFAR = 0x%08X\n",
ex_buf[13]);
BCMTCH_ERR(
"FWE:SCB_BFAR = 0x%08X\n",
ex_buf[14]);
if (ex_buf[15] == 0xDEADBEEB) {
BCMTCH_ERR(
"FWE:Stack[0] = 0x%08X\n",
ex_buf[16]);
BCMTCH_ERR(
"FWE:Stack[1] = 0x%08X\n",
ex_buf[17]);
BCMTCH_ERR(
"FWE:Stack[2] = 0x%08X\n",
ex_buf[18]);
BCMTCH_ERR(
"FWE:Stack[3] = 0x%08X\n",
ex_buf[19]);
BCMTCH_ERR(
"FWE:Stack[4] = 0x%08X\n",
ex_buf[20]);
BCMTCH_ERR(
"FWE:Stack[5] = 0x%08X\n",
ex_buf[21]);
BCMTCH_ERR(
"FWE:Stack[6] = 0x%08X\n",
ex_buf[22]);
BCMTCH_ERR(
"FWE:Stack[7] = 0x%08X\n",
ex_buf[23]);
}
}
}
}
#endif
static int32_t bcmtch_state_check_command_status(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t timeout_ms)
{
uint8_t timeout = timeout_ms;
uint8_t m2h;
int32_t ret_val = 0;
do {
ret_val =
bcmtch_com_read_spm(
bcmtch_data_ptr,
BCMTCH_SPM_REG_MSG_TO_HOST,
&m2h);
if (ret_val)
return ret_val;
if ((m2h > 0x1) && (m2h < TOFE_MESSAGE_SUCCESS)) {
ret_val = m2h;
#if BCMTCH_STATE_PROTOCOL
bcmtch_state_check_fw_exception(
bcmtch_data_ptr,
m2h);
#else
BCMTCH_ERR("exception = 0x%x\n", m2h);
#endif
break;
} else if (m2h > TOFE_MESSAGE_COMMAND_ECHO) {
break;
}
usleep_range(800, 1200);
} while (timeout--
&& !(m2h & TOFE_MESSAGE_FW_READY));
bcmtch_data_ptr->bcmtch_state_resp_buffer[0] =
m2h & ~TOFE_MESSAGE_FW_READY;
return ret_val;
}
static void bcmtch_dev_process_state_touches(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
int slots;
/* Check command status */
ret_val = bcmtch_state_check_command_status(
bcmtch_data_ptr,
20);
if (ret_val)
return;
/* Read command response status */
bcmtch_data_ptr->scan_data.status.reg =
bcmtch_data_ptr->bcmtch_state_resp_buffer[0];
slots = bcmtch_data_ptr->scan_data.status.slots;
/* FIXME: Need to take the log slot case into account. */
/* Read variable-size response (long) */
ret_val = bcmtch_dev_read_scan_touches(
bcmtch_data_ptr,
slots);
/* Next touch scan */
bcmtch_state_start_scan(bcmtch_data_ptr);
}
#endif /* BCMTCH_STATE_PROTOCOL */
static void bcmtch_dev_reset_events(
struct bcmtch_data *bcmtch_data_ptr)
{
uint32_t touch = 0;
/* clear active touch structure */
memset(
&bcmtch_data_ptr->touch[0],
0,
sizeof(struct bcmtch_touch) * BCMTCH_MAX_TOUCH);
/* clear system touches */
if (bcmtch_data_ptr->touch_count) {
for (touch = 0; touch < BCMTCH_MAX_TOUCH; touch++) {
input_mt_slot(
bcmtch_data_ptr->p_input_device,
touch);
input_mt_report_slot_state(
bcmtch_data_ptr->p_input_device,
MT_TOOL_FINGER,
false);
}
input_report_key(
bcmtch_data_ptr->p_input_device,
BTN_TOUCH,
false);
input_sync(bcmtch_data_ptr->p_input_device);
}
bcmtch_data_ptr->touch_count = 0;
}
static void bcmtch_dev_post_boot_reset(
struct bcmtch_data *bcmtch_data_ptr)
{
bcmtch_data_ptr->post_boot_section = 0;
bcmtch_data_ptr->post_boot_sections = 0;
bcmtch_data_ptr->post_boot_patches = 0;
bcmtch_data_ptr->post_boot_pending = 0;
bcmtch_data_ptr->postboot_cfg_addr = 0;
bcmtch_data_ptr->postboot_cfg_length = 0;
/* free communication channels */
bcmtch_dev_free_channels(bcmtch_data_ptr);
bcmtch_data_ptr->post_boot_buffer = NULL;
}
static int32_t bcmtch_dev_suspend(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
/* disable interrupts */
bcmtch_interrupt_disable(bcmtch_data_ptr);
/* free watchdog timer */
bcmtch_dev_watchdog_stop(bcmtch_data_ptr);
/* clear worker */
bcmtch_clear_deferred_worker(bcmtch_data_ptr);
/* lock */
mutex_lock(&bcmtch_data_ptr->mutex_work);
if (bcmtch_boot_flag & BCMTCH_BF_SUSPEND_COLD_BOOT) {
/* post boot reset */
bcmtch_dev_post_boot_reset(bcmtch_data_ptr);
#if BCMTCH_STATE_PROTOCOL
if (bcmtch_data_ptr->work_process_index >= BCMTCH_WP_STATE)
ret_val = bcmtch_state_sleep(bcmtch_data_ptr);
else
#endif /* BCMTCH_STATE_PROTOCOL */
ret_val = bcmtch_dev_set_power_state(
bcmtch_data_ptr,
BCMTCH_POWER_STATE_SLEEP);
bcmtch_dev_power_enable(bcmtch_data_ptr, false);
} else {
#if BCMTCH_STATE_PROTOCOL
if (bcmtch_data_ptr->work_process_index >= BCMTCH_WP_STATE)
ret_val = bcmtch_state_suspend(bcmtch_data_ptr);
else
#endif /* BCMTCH_STATE_PROTOCOL */
/* suspend */
ret_val = bcmtch_dev_request_power_mode(
bcmtch_data_ptr,
BCMTCH_POWER_MODE_NOWAKE,
TOFE_COMMAND_POWER_MODE_SUSPEND);
}
/* clear events */
bcmtch_dev_reset_events(bcmtch_data_ptr);
/* unlock */
mutex_unlock(&bcmtch_data_ptr->mutex_work);
/* update ABI status */
mutex_lock(&bcmtch_data_ptr->mutex_abi);
bcmtch_data_ptr->abi_suspend = true;
mutex_unlock(&bcmtch_data_ptr->mutex_abi);
BCMTCH_DBG(BCMTCH_DF_PM, "PM:%s() - 0x%x\n",
__func__,
ret_val);
return ret_val;
}
static int32_t bcmtch_dev_resume(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
/* lock */
mutex_lock(&bcmtch_data_ptr->mutex_work);
if (bcmtch_boot_flag & BCMTCH_BF_SUSPEND_COLD_BOOT) {
ret_val = bcmtch_dev_power_enable(
bcmtch_data_ptr,
true);
if (!ret_val)
ret_val = bcmtch_dev_init(bcmtch_data_ptr);
} else {
#if BCMTCH_STATE_PROTOCOL
if (bcmtch_data_ptr->work_process_index >= BCMTCH_WP_STATE)
ret_val = bcmtch_state_resume(
bcmtch_data_ptr);
else {
#endif
ret_val = bcmtch_dev_request_power_mode(
bcmtch_data_ptr,
BCMTCH_POWER_MODE_WAKE,
TOFE_COMMAND_NO_COMMAND);
if (!ret_val)
ret_val =
bcmtch_dev_check_power_state(
bcmtch_data_ptr,
BCMTCH_POWER_STATE_ACTIVE,
25);
if (!ret_val)
ret_val =
bcmtch_dev_wait_for_firmware_ready(
bcmtch_data_ptr,
BCMTCH_FW_READY_WAIT);
#if BCMTCH_STATE_PROTOCOL
}
#endif
}
if (!ret_val)
bcmtch_dev_watchdog_start(bcmtch_data_ptr);
if (!ret_val)
ret_val = bcmtch_interrupt_enable(bcmtch_data_ptr);
#if BCMTCH_STATE_PROTOCOL
if (!ret_val &&
(bcmtch_data_ptr->work_process_index >= BCMTCH_WP_STATE)) {
bcmtch_state_start_scan(bcmtch_data_ptr);
}
#endif /* BCMTCH_STATE_PROTOCOL */
if (ret_val)
BCMTCH_ERR("BCMTOUCH: %s() error rv=%d bf=0x%x\n",
__func__,
ret_val,
bcmtch_boot_flag);
/* unlock */
mutex_unlock(&bcmtch_data_ptr->mutex_work);
/* update ABI status */
mutex_lock(&bcmtch_data_ptr->mutex_abi);
bcmtch_data_ptr->abi_suspend = false;
mutex_unlock(&bcmtch_data_ptr->mutex_abi);
BCMTCH_DBG(BCMTCH_DF_PM, "PM: %s() - 0x%x\n",
__func__,
ret_val);
return ret_val;
}
void bcmtch_dev_watchdog_work(unsigned long int data)
{
struct bcmtch_data *bcmtch_data_ptr =
(struct bcmtch_data *)data;
BCMTCH_DBG(BCMTCH_DF_WD, "WD:\n");
/* queue the interrupt handler */
queue_work(
bcmtch_data_ptr->p_workqueue,
(struct work_struct *)&bcmtch_data_ptr->work);
bcmtch_dev_watchdog_reset(bcmtch_data_ptr);
}
static void bcmtch_dev_watchdog_start(
struct bcmtch_data *bcmtch_data_ptr)
{
if (timer_pending(&bcmtch_data_ptr->watchdog))
del_timer_sync(&bcmtch_data_ptr->watchdog);
init_timer(&bcmtch_data_ptr->watchdog);
bcmtch_data_ptr->watchdog_expires =
(bcmtch_data_ptr->post_boot_sections) ?
msecs_to_jiffies(bcmtch_watchdog_post_boot) :
msecs_to_jiffies(bcmtch_watchdog_normal);
bcmtch_data_ptr->watchdog.function = bcmtch_dev_watchdog_work;
bcmtch_data_ptr->watchdog.data = (unsigned long)bcmtch_data_ptr;
bcmtch_data_ptr->watchdog.expires =
jiffies + bcmtch_data_ptr->watchdog_expires;
add_timer(&bcmtch_data_ptr->watchdog);
}
static int32_t bcmtch_dev_watchdog_restart(
struct bcmtch_data *bcmtch_data_ptr,
uint32_t expires)
{
int32_t ret_val = 0;
bcmtch_data_ptr->watchdog_expires = expires;
ret_val = mod_timer(
&bcmtch_data_ptr->watchdog,
(jiffies + bcmtch_data_ptr->watchdog_expires));
return ret_val;
}
static int32_t bcmtch_dev_watchdog_reset(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
ret_val = mod_timer(
&bcmtch_data_ptr->watchdog,
(jiffies + bcmtch_data_ptr->watchdog_expires));
return ret_val;
}
static int32_t bcmtch_dev_watchdog_stop(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
if (bcmtch_data_ptr) {
ret_val = del_timer_sync(&bcmtch_data_ptr->watchdog);
bcmtch_data_ptr->watchdog_expires = 0;
}
return ret_val;
}
static int32_t bcmtch_dev_reset_fw(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
/* disable interrupts */
bcmtch_interrupt_disable(bcmtch_data_ptr);
/* free watchdog timer */
bcmtch_dev_watchdog_stop(bcmtch_data_ptr);
/* post boot reset */
bcmtch_dev_post_boot_reset(bcmtch_data_ptr);
/* cycle power - off */
bcmtch_dev_power_enable(
bcmtch_data_ptr,
false);
/* pause */
msleep(20);
/* cycle power - on */
ret_val = bcmtch_dev_power_enable(
bcmtch_data_ptr,
true);
if (ret_val) {
BCMTCH_ERR("WG: %s power enable error!\n",
__func__);
goto wdg_reset_error;
}
/* reset the chip on driver load ? */
if (bcmtch_boot_flag &
BCMTCH_BF_HARD_RESET_ON_LOAD) {
bcmtch_dev_reset(
bcmtch_data_ptr,
BCMTCH_RESET_MODE_HARD);
} else if (bcmtch_boot_flag &
BCMTCH_BF_SOFT_RESET_ON_LOAD) {
bcmtch_dev_reset(
bcmtch_data_ptr,
BCMTCH_RESET_MODE_SOFT_CHIP |
BCMTCH_RESET_MODE_SOFT_ARM);
}
/* clear events */
bcmtch_dev_reset_events(bcmtch_data_ptr);
/* init touch controller */
ret_val = bcmtch_dev_init(bcmtch_data_ptr);
if (ret_val) {
BCMTCH_ERR("WG: %s dev_init error!\n",
__func__);
goto wdg_reset_error;
}
/* restart watchdog */
bcmtch_dev_watchdog_start(bcmtch_data_ptr);
/* re-enable interrupts */
ret_val =
bcmtch_interrupt_enable(bcmtch_data_ptr);
wdg_reset_error:
return ret_val;
}
static int32_t bcmtch_dev_watchdog_check(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
bool bcmtch_error = false;
BCMTCH_DBG(BCMTCH_DF_WD, "WD: Watch DOG\n");
#if BCMTCH_STATE_PROTOCOL
/* Check status */
ret_val =
bcmtch_state_check_command_status(bcmtch_data_ptr, 1);
if (ret_val)
bcmtch_error = true;
else if (bcmtch_data_ptr->work_process_index >= BCMTCH_WP_STATE) {
/* Test stop command */
bcmtch_state_stop_scan(bcmtch_data_ptr);
ret_val =
bcmtch_state_check_command_status(bcmtch_data_ptr, 25);
if (ret_val)
bcmtch_error = true;
else {
/* Test start command */
bcmtch_state_start_scan(bcmtch_data_ptr);
ret_val =
bcmtch_state_check_command_status(
bcmtch_data_ptr, 10);
if (ret_val ||
(bcmtch_data_ptr->bcmtch_state_resp_buffer[0]
< TOFE_MESSAGE_SUCCESS)) {
bcmtch_error = true;
}
}
}
#endif
/* Recover FW */
if ((bcmtch_error) &&
(bcmtch_boot_flag & BCMTCH_BF_FW_RESET_ON_WD)) {
BCMTCH_ERR("WG: watch dog reset [%d]\n", ret_val);
ret_val = bcmtch_dev_reset_fw(bcmtch_data_ptr);
if (ret_val)
return ret_val;
}
return ret_val;
}
static int32_t bcmtch_dev_power_init(
struct bcmtch_data *p_bcmtch_data)
{
int32_t ret_val = 0;
#ifdef CONFIG_REGULATOR
p_bcmtch_data->regulator_avdd33 =
regulator_get(
p_bcmtch_data->p_device,
"avdd33");
if (IS_ERR(p_bcmtch_data->regulator_avdd33))
p_bcmtch_data->regulator_avdd33 = NULL;
p_bcmtch_data->regulator_vddo =
regulator_get(
p_bcmtch_data->p_device,
"vddo");
if (IS_ERR(p_bcmtch_data->regulator_vddo))
p_bcmtch_data->regulator_vddo = NULL;
p_bcmtch_data->regulator_avdd_adldo =
regulator_get(
p_bcmtch_data->p_device,
"avdd_adldo");
if (IS_ERR(p_bcmtch_data->regulator_avdd_adldo))
p_bcmtch_data->regulator_avdd_adldo = NULL;
BCMTCH_DBG(BCMTCH_DF_PM, "PM:%s() %x %x %x\n",
__func__,
(uint32_t)p_bcmtch_data->regulator_avdd33,
(uint32_t)p_bcmtch_data->regulator_vddo,
(uint32_t)p_bcmtch_data->regulator_avdd_adldo);
#endif
return ret_val;
}
static int32_t bcmtch_dev_power_enable(
struct bcmtch_data *p_bcmtch_data,
bool enable)
{
int32_t ret_val = 0;
#ifdef CONFIG_REGULATOR
if (enable) {
if (p_bcmtch_data->regulator_avdd33)
ret_val |= regulator_enable(p_bcmtch_data
->regulator_avdd33);
if (p_bcmtch_data->regulator_vddo)
ret_val |= regulator_enable(p_bcmtch_data
->regulator_vddo);
if (p_bcmtch_data->regulator_avdd_adldo)
ret_val |= regulator_enable(p_bcmtch_data
->regulator_avdd_adldo);
if (p_bcmtch_data->power_on_delay_us)
usleep_range(
p_bcmtch_data->power_on_delay_us - 500,
p_bcmtch_data->power_on_delay_us + 500);
} else {
if (p_bcmtch_data->regulator_avdd33 &&
regulator_is_enabled(
p_bcmtch_data->regulator_avdd33))
regulator_disable(
p_bcmtch_data->regulator_avdd33);
if (p_bcmtch_data->regulator_vddo &&
regulator_is_enabled(
p_bcmtch_data->regulator_vddo))
regulator_disable(
p_bcmtch_data->regulator_vddo);
if (p_bcmtch_data->regulator_avdd_adldo &&
regulator_is_enabled(
p_bcmtch_data->regulator_avdd_adldo))
regulator_disable(
p_bcmtch_data->regulator_avdd_adldo);
}
#endif
if (ret_val)
BCMTCH_ERR("BCMTOUCH: %s() error rv=%d\n",
__func__,
ret_val);
return ret_val;
}
static int32_t bcmtch_dev_power_free(
struct bcmtch_data *p_bcmtch_data)
{
int32_t ret_val = 0;
#ifdef CONFIG_REGULATOR
if (p_bcmtch_data->regulator_avdd33)
regulator_put(p_bcmtch_data->regulator_avdd33);
if (p_bcmtch_data->regulator_vddo)
regulator_put(p_bcmtch_data->regulator_vddo);
if (p_bcmtch_data->regulator_avdd_adldo)
regulator_put(p_bcmtch_data->regulator_avdd_adldo);
p_bcmtch_data->regulator_avdd33 = NULL;
p_bcmtch_data->regulator_vddo = NULL;
p_bcmtch_data->regulator_avdd_adldo = NULL;
#endif
return ret_val;
}
/* -------------------------------------------------- */
/* - BCM Touch Controller Com(munication) Functions - */
/* -------------------------------------------------- */
static int32_t bcmtch_com_init(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
if (BCMTCH_DEFAULT_I2C_ADDR_SYS !=
bcmtch_data_ptr->platform_data.i2c_addr_sys)
ret_val |= bcmtch_com_write_spm(
bcmtch_data_ptr,
BCMTCH_SPM_REG_I2CS_CHIPID,
bcmtch_data_ptr->platform_data.i2c_addr_sys);
return ret_val;
}
static int32_t bcmtch_com_read_spm(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t reg, uint8_t *data)
{
int32_t ret_val = 0;
if (bcmtch_data_ptr) {
ret_val = bcmtch_i2c_read_spm(
bcmtch_data_ptr->p_i2c_client_spm,
reg,
data);
} else {
BCMTCH_ERR("%s() error, bcmtch_data_ptr == NULL\n", __func__);
ret_val = -ENODATA;
}
return ret_val;
}
static int32_t bcmtch_com_write_spm(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t reg, uint8_t data)
{
int32_t ret_val = 0;
if (bcmtch_data_ptr) {
ret_val = bcmtch_i2c_write_spm(
bcmtch_data_ptr->p_i2c_client_spm,
reg,
data);
} else {
BCMTCH_ERR("%s() error, bcmtch_data_ptr == NULL\n", __func__);
ret_val = -ENODATA;
}
return ret_val;
}
static inline int32_t bcmtch_com_fast_write_spm(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t count, uint8_t *regs, uint8_t *data)
{
int32_t ret_val = 0;
ret_val = bcmtch_i2c_fast_write_spm(
bcmtch_data_ptr->p_i2c_client_spm,
count,
regs,
data);
return ret_val;
}
static int32_t bcmtch_com_read_sys(
struct bcmtch_data *bcmtch_data_ptr,
uint32_t sys_addr,
uint16_t read_len,
uint8_t *read_data)
{
int32_t ret_val = 0;
if (bcmtch_data_ptr) {
ret_val = bcmtch_i2c_read_sys(
bcmtch_data_ptr->p_i2c_client_sys,
sys_addr,
read_len,
read_data);
} else {
BCMTCH_ERR("%s() error, bcmtch_data_ptr == NULL\n", __func__);
ret_val = -ENODATA;
}
return ret_val;
}
static inline int32_t bcmtch_com_write_sys32(
struct bcmtch_data *bcmtch_data_ptr,
uint32_t sys_addr,
uint32_t write_data)
{
return bcmtch_com_write_sys(bcmtch_data_ptr,
sys_addr, 4, (uint8_t *)&write_data);
}
static int32_t bcmtch_com_write_sys(
struct bcmtch_data *bcmtch_data_ptr,
uint32_t sys_addr,
uint16_t write_len,
uint8_t *write_data)
{
int32_t ret_val = 0;
if (bcmtch_data_ptr) {
ret_val = bcmtch_i2c_write_sys(
bcmtch_data_ptr->p_i2c_client_sys,
sys_addr,
write_len,
write_data);
} else {
BCMTCH_ERR("%s() error, bcmtch_data_ptr == NULL\n", __func__);
ret_val = -ENODATA;
}
return ret_val;
}
static int32_t bcmtch_com_read_dma(
struct bcmtch_data *bcmtch_data_ptr,
uint16_t read_len,
uint8_t *read_data)
{
int32_t ret_val = 0;
if (bcmtch_data_ptr) {
ret_val = bcmtch_i2c_read_dma(
bcmtch_data_ptr->p_i2c_client_sys,
read_len,
read_data);
} else {
BCMTCH_ERR("%s() error, bcmtch_data_ptr == NULL\n",
__func__);
ret_val = -ENODATA;
}
return ret_val;
}
/* ------------------------------------- */
/* - BCM Touch Controller OS Functions - */
/* ------------------------------------- */
static irqreturn_t bcmtch_interrupt_handler(int32_t irq, void *dev_id)
{
struct bcmtch_data *bcmtch_data_ptr =
(struct bcmtch_data *)dev_id;
/* track interrupts */
bcmtch_data_ptr->irq_pending = true;
BCMTCH_DBG(BCMTCH_DF_IH, "IH:p=%d\n",
bcmtch_data_ptr->irq_pending);
/* queue the interrupt handler */
queue_work(
bcmtch_data_ptr->p_workqueue,
(struct work_struct *)&bcmtch_data_ptr->work);
/* reset watchdog */
bcmtch_dev_watchdog_reset(bcmtch_data_ptr);
return IRQ_HANDLED;
}
static int32_t bcmtch_interrupt_enable(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
struct bcmtch_platform_data *p_data;
if (bcmtch_data_ptr->irq_enabled)
return ret_val;
p_data = &bcmtch_data_ptr->platform_data;
ret_val = request_irq(
p_data->touch_irq,
bcmtch_interrupt_handler,
p_data->gpio_interrupt_trigger,
BCMTCH15XXX_NAME,
bcmtch_data_ptr);
if (ret_val) {
BCMTCH_ERR("ERROR: %s() - Unable to request interrupt irq %d\n",
__func__,
p_data->touch_irq);
/* note :
* - polling is not enabled in this release
* - it is an error if an irq is requested
* and not granted
*/
} else
bcmtch_data_ptr->irq_enabled = true;
return ret_val;
}
static void bcmtch_interrupt_disable(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t irq;
if (bcmtch_data_ptr && bcmtch_data_ptr->irq_enabled) {
irq = bcmtch_data_ptr->platform_data.touch_irq;
if (irq) {
free_irq(irq, bcmtch_data_ptr);
bcmtch_data_ptr->irq_enabled = false;
}
}
}
static int32_t bcmtch_init_input_device(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
int32_t button_init = 0;
if (bcmtch_data_ptr) {
bcmtch_data_ptr->p_input_device = input_allocate_device();
if (bcmtch_data_ptr->p_input_device) {
bcmtch_data_ptr->p_input_device->name =
"BCMTCH15xxx Touch Screen";
bcmtch_data_ptr->p_input_device->phys =
"I2C";
bcmtch_data_ptr->p_input_device->id.bustype =
BUS_I2C;
bcmtch_data_ptr->p_input_device->id.vendor =
BCMTCH_VENDOR_ID;
bcmtch_data_ptr->p_input_device->id.product =
((bcmtch_data_ptr->chip_id
& BCMTCH_CHIPID_PID_MASK)
>> BCMTCH_CHIPID_PID_SHIFT);
bcmtch_data_ptr->p_input_device->id.version =
bcmtch_data_ptr->rev_id;
set_bit(EV_SYN, bcmtch_data_ptr->p_input_device->evbit);
set_bit(EV_ABS, bcmtch_data_ptr->p_input_device->evbit);
__set_bit(
INPUT_PROP_DIRECT,
bcmtch_data_ptr->p_input_device->propbit);
set_bit(EV_KEY,
bcmtch_data_ptr->p_input_device->evbit);
set_bit(
BTN_TOUCH,
bcmtch_data_ptr->p_input_device->keybit);
while (button_init < bcmtch_data_ptr->
platform_data.ext_button_count) {
set_bit(
bcmtch_data_ptr->platform_data.
ext_button_map[button_init],
bcmtch_data_ptr->
p_input_device->keybit);
button_init++;
}
input_set_abs_params(
bcmtch_data_ptr->p_input_device,
ABS_MT_POSITION_X,
bcmtch_data_ptr->axis_x_min,
bcmtch_data_ptr->axis_x_max,
0,
0);
input_set_abs_params(
bcmtch_data_ptr->p_input_device,
ABS_MT_POSITION_Y,
bcmtch_data_ptr->axis_y_min,
bcmtch_data_ptr->axis_y_max,
0,
0);
/* input_set_abs_params(
bcmtch_data_ptr->p_input_device,
ABS_MT_TOOL_TYPE,
0,
MT_TOOL_MAX,
0,
0);
*/
if (bcmtch_event_flag &
BCMTCH_EVENT_FLAG_TOUCH_SIZE) {
input_set_abs_params(
bcmtch_data_ptr->p_input_device,
ABS_MT_TOUCH_MAJOR,
bcmtch_data_ptr->axis_h_min,
bcmtch_data_ptr->axis_h_max,
0,
0);
input_set_abs_params(
bcmtch_data_ptr->p_input_device,
ABS_MT_TOUCH_MINOR,
bcmtch_data_ptr->axis_h_min,
bcmtch_data_ptr->axis_h_max,
0,
0);
}
if (bcmtch_event_flag &
BCMTCH_EVENT_FLAG_TOOL_SIZE) {
input_set_abs_params(
bcmtch_data_ptr->p_input_device,
ABS_MT_WIDTH_MAJOR,
bcmtch_data_ptr->axis_h_min,
bcmtch_data_ptr->axis_h_max,
0,
0);
input_set_abs_params(
bcmtch_data_ptr->p_input_device,
ABS_MT_WIDTH_MINOR,
bcmtch_data_ptr->axis_h_min,
bcmtch_data_ptr->axis_h_max,
0,
0);
}
if (bcmtch_event_flag &
BCMTCH_EVENT_FLAG_PRESSURE) {
input_set_abs_params(
bcmtch_data_ptr->p_input_device,
ABS_MT_PRESSURE,
0,
BCMTCH_MAX_PRESSURE,
0,
0);
}
if (bcmtch_event_flag &
BCMTCH_EVENT_FLAG_ORIENTATION) {
input_set_abs_params(
bcmtch_data_ptr->p_input_device,
ABS_MT_ORIENTATION,
BCMTCH_MIN_ORIENTATION,
BCMTCH_MAX_ORIENTATION,
0,
0);
}
set_bit(
BTN_TOOL_FINGER,
bcmtch_data_ptr->p_input_device->keybit);
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)
input_mt_init_slots(
bcmtch_data_ptr->p_input_device,
BCMTCH_MAX_TOUCH);
#else
input_mt_init_slots(
bcmtch_data_ptr->p_input_device,
BCMTCH_MAX_TOUCH,
0);
#endif
/* request new os input queue size for this device */
input_set_events_per_packet(
bcmtch_data_ptr->p_input_device,
6 * BCMTCH_MAX_TOUCH);
/* register device */
ret_val =
input_register_device(
bcmtch_data_ptr->p_input_device);
if (ret_val) {
BCMTCH_INFO(
"%s() Unable to register input device\n",
__func__);
input_free_device(
bcmtch_data_ptr->p_input_device);
bcmtch_data_ptr->p_input_device = NULL;
}
} else {
BCMTCH_ERR("%s() Unable to create device\n",
__func__);
ret_val = -ENODEV;
}
} else {
BCMTCH_ERR("%s() error, driver data structure == NULL\n",
__func__);
ret_val = -ENODATA;
}
return ret_val;
}
static inline void bcmtch_free_input_device(
struct bcmtch_data *bcmtch_data_ptr)
{
if (bcmtch_data_ptr && bcmtch_data_ptr->p_input_device) {
input_unregister_device(bcmtch_data_ptr->p_input_device);
bcmtch_data_ptr->p_input_device = NULL;
}
}
static bool bcmtch_dev_process_post_boot(
struct bcmtch_data *bcmtch_data_ptr,
bool irq_serviced)
{
int32_t still_downloading;
int32_t ret_val;
if (irq_serviced) {
/* download shorter packet */
still_downloading =
bcmtch_dev_post_boot_download(
bcmtch_data_ptr,
bcmtch_post_boot_rate_low);
} else {
/* download larger packet */
still_downloading =
bcmtch_dev_post_boot_download(
bcmtch_data_ptr,
bcmtch_post_boot_rate_high);
}
if (!still_downloading) {
BCMTCH_DBG(BCMTCH_DF_PB, "PB:DOWNLOAD COMPLETE\n");
if (bcmtch_data_ptr->postboot_cfg_length) {
/* don't want any stray interrupts */
bcmtch_interrupt_disable(bcmtch_data_ptr);
/* Send post boot init command */
ret_val = bcmtch_dev_send_command(
bcmtch_data_ptr,
TOFE_COMMAND_INIT_POST_BOOT_PATCHES,
bcmtch_data_ptr->postboot_cfg_addr,
bcmtch_data_ptr->postboot_cfg_length,
0);
if (ret_val != 0) {
BCMTCH_ERR(
" send_command error [%d] cmd=%x\n",
ret_val,
TOFE_COMMAND_INIT_POST_BOOT_PATCHES);
}
/* Set to check post boot init request from FW */
bcmtch_data_ptr->post_boot_pending = 1;
/* Set the worker process function to the transient
* one which handles the ISR workqueue during
* the switch from ROM FW to post boot patched FW.
*/
bcmtch_data_ptr->work_process_index =
BCMTCH_WP_PATCH_INIT;
bcmtch_dev_watchdog_restart(
bcmtch_data_ptr,
msecs_to_jiffies(bcmtch_watchdog_normal));
bcmtch_interrupt_enable(bcmtch_data_ptr);
}
return true;
}
return false;
}
static void bcmtch_deferred_worker(struct work_struct *work)
{
struct bcmtch_data *bcmtch_data_ptr =
container_of(work, struct bcmtch_data, work);
bool work_done = false;
mutex_lock(&bcmtch_data_ptr->mutex_work);
if (bcmtch_data_ptr->irq_pending) {
bcmtch_data_ptr->irq_pending = false;
/* Process channels */
bcmtch_data_ptr->bcmtch_dev_process_table
[bcmtch_data_ptr->work_process_index]
(bcmtch_data_ptr);
work_done = true;
}
/* Amortized post boot download */
if (bcmtch_data_ptr->post_boot_sections) {
bcmtch_dev_process_post_boot(
bcmtch_data_ptr,
work_done);
work_done = true;
}
if (!work_done)
bcmtch_dev_watchdog_check(bcmtch_data_ptr);
mutex_unlock(&bcmtch_data_ptr->mutex_work);
}
static int32_t bcmtch_dev_post_boot_get_section(
struct bcmtch_data *bcmtch_data_ptr)
{
struct combi_entry *pb_entry = NULL;
if (bcmtch_data_ptr->post_boot_sections &&
bcmtch_data_ptr->post_boot_buffer) {
pb_entry = (struct combi_entry *)
bcmtch_data_ptr->post_boot_buffer;
while (pb_entry[bcmtch_data_ptr->post_boot_section].length) {
if (pb_entry[bcmtch_data_ptr->post_boot_section].flags &
BCMTCH_FIRMWARE_FLAGS_POST_BOOT) {
bcmtch_data_ptr->post_boot_data =
bcmtch_data_ptr->post_boot_buffer
+ pb_entry[bcmtch_data_ptr->
post_boot_section].offset;
bcmtch_data_ptr->post_boot_addr =
pb_entry[bcmtch_data_ptr->
post_boot_section].addr;
bcmtch_data_ptr->post_boot_left =
pb_entry[bcmtch_data_ptr->
post_boot_section].length;
break;
} else {
bcmtch_data_ptr->post_boot_section++;
}
}
}
return pb_entry[bcmtch_data_ptr->post_boot_section].length;
}
static int32_t bcmtch_dev_post_boot_download(
struct bcmtch_data *bcmtch_data_ptr,
int16_t data_rate)
{
int32_t ret_val;
int16_t write_length;
struct combi_entry *pb_entry = NULL;
if (bcmtch_data_ptr->post_boot_left) {
write_length = data_rate;
if (bcmtch_data_ptr->post_boot_left < write_length)
write_length = bcmtch_data_ptr->post_boot_left;
ret_val = bcmtch_com_write_sys(
bcmtch_data_ptr,
bcmtch_data_ptr->post_boot_addr,
write_length,
bcmtch_data_ptr->post_boot_data);
if (ret_val) {
BCMTCH_ERR("%s() Error - did not download\n",
__func__);
} else {
bcmtch_data_ptr->post_boot_addr += write_length;
bcmtch_data_ptr->post_boot_left -= write_length;
bcmtch_data_ptr->post_boot_data += write_length;
}
} else {
BCMTCH_ERR("%s() Error - no bytes to download\n",
__func__);
}
if (!bcmtch_data_ptr->post_boot_left) {
pb_entry = (struct combi_entry *)
bcmtch_data_ptr->post_boot_buffer;
BCMTCH_DBG(
BCMTCH_DF_PB,
"PB:Section %d Addr 0x%08x Len %d\n",
bcmtch_data_ptr->post_boot_section,
pb_entry[bcmtch_data_ptr->
post_boot_section].addr,
pb_entry[bcmtch_data_ptr->
post_boot_section].length);
if (pb_entry[bcmtch_data_ptr->post_boot_section].flags &
BCMTCH_FIRMWARE_FLAGS_POST_BOOT_PATCH) {
if (--bcmtch_data_ptr->post_boot_patches == 0) {
/* send patch init */
ret_val = bcmtch_dev_send_command(
bcmtch_data_ptr,
TOFE_COMMAND_INIT_POST_BOOT_PATCHES,
0,
0,
0);
if (ret_val != 0) {
BCMTCH_ERR(
" send_command error [%d] cmd=%x\n",
ret_val,
TOFE_COMMAND_INIT_POST_BOOT_PATCHES);
}
}
}
if (--bcmtch_data_ptr->post_boot_sections) {
bcmtch_data_ptr->post_boot_section++;
bcmtch_dev_post_boot_get_section(bcmtch_data_ptr);
}
}
BCMTCH_DBG(BCMTCH_DF_PB, "PB:pb_sec=%d post_boot_left=%d.\n",
bcmtch_data_ptr->post_boot_sections,
bcmtch_data_ptr->post_boot_left);
return bcmtch_data_ptr->post_boot_sections;
}
static int32_t bcmtch_init_deferred_worker(
struct bcmtch_data *bcmtch_data_ptr)
{
int32_t ret_val = 0;
if (bcmtch_data_ptr) {
bcmtch_data_ptr->p_workqueue =
create_workqueue("bcmtch_wq");
if (bcmtch_data_ptr->p_workqueue) {
INIT_WORK(
&bcmtch_data_ptr->work,
bcmtch_deferred_worker);
} else {
BCMTCH_ERR("%s() Unable to create workqueue\n",
__func__);
ret_val = -ENOMEM;
}
} else {
BCMTCH_ERR("%s() error, driver data structure == NULL\n",
__func__);
ret_val = -ENODATA;
}
return ret_val;
}
static void bcmtch_clear_deferred_worker(
struct bcmtch_data *bcmtch_data_ptr)
{
if (bcmtch_data_ptr && bcmtch_data_ptr->p_workqueue)
flush_workqueue(bcmtch_data_ptr->p_workqueue);
}
static void bcmtch_free_deferred_worker(
struct bcmtch_data *bcmtch_data_ptr)
{
if (bcmtch_data_ptr && bcmtch_data_ptr->p_workqueue) {
cancel_work_sync(&bcmtch_data_ptr->work);
destroy_workqueue(bcmtch_data_ptr->p_workqueue);
bcmtch_data_ptr->p_workqueue = NULL;
}
}
static void bcmtch_reset(
struct bcmtch_data *bcmtch_data_ptr)
{
if (bcmtch_data_ptr &&
gpio_is_valid(bcmtch_data_ptr->
platform_data.gpio_reset_pin)) {
msleep(bcmtch_data_ptr->platform_data.gpio_reset_time_ms);
gpio_set_value(
bcmtch_data_ptr->platform_data.gpio_reset_pin,
bcmtch_data_ptr->platform_data.gpio_reset_polarity);
msleep(bcmtch_data_ptr->platform_data.gpio_reset_time_ms);
gpio_set_value(
bcmtch_data_ptr->platform_data.gpio_reset_pin,
!bcmtch_data_ptr->platform_data.gpio_reset_polarity);
msleep(bcmtch_data_ptr->platform_data.gpio_reset_time_ms);
}
}
static int32_t bcmtch_init_gpio(
struct bcmtch_data *bcmtch_data_ptr)
{
struct bcmtch_platform_data *p_platform_data;
int32_t ret_val = 0;
if (!bcmtch_data_ptr) {
BCMTCH_ERR("%s() error, driver data structure == NULL\n",
__func__);
ret_val = -ENODATA;
return ret_val;
}
p_platform_data =
(struct bcmtch_platform_data *)&bcmtch_data_ptr->platform_data;
/*
* setup a gpio pin for BCM Touch Controller reset function
*/
if (gpio_is_valid(p_platform_data->gpio_reset_pin)) {
ret_val = gpio_request(
p_platform_data->gpio_reset_pin,
"BCMTCH reset");
if (ret_val < 0) {
BCMTCH_ERR(
"ERROR: %s() - Unable to request reset pin %d\n",
__func__,
p_platform_data->gpio_reset_pin);
/* note :
* it is an error if a reset pin is requested
* and not granted --> return
*/
return ret_val;
}
/*
* setup reset pin as output
* - invert reset polarity --> don't want to hold in reset
*/
ret_val = gpio_direction_output(
p_platform_data->gpio_reset_pin,
!p_platform_data->gpio_reset_polarity);
if (ret_val < 0) {
BCMTCH_ERR("ERROR: %s() - Unable to set reset pin %d\n",
__func__,
p_platform_data->gpio_reset_pin);
/* note :
* it is an error if a reset pin is requested
* and not set --> return
*/
return ret_val;
}
} else {
BCMTCH_INFO("%s() : no reset pin configured\n",
__func__);
}
/*
* setup a gpio pin for BCM Touch Controller interrupt function
*/
if (gpio_is_valid(p_platform_data->gpio_interrupt_pin)) {
ret_val = gpio_request(
p_platform_data->gpio_interrupt_pin,
"BCMTCH Interrupt");
if (ret_val < 0) {
BCMTCH_ERR(
"ERROR: %s() - Unable to request interrupt pin %d\n",
__func__,
p_platform_data->gpio_interrupt_pin);
/* note :
* it is an error if an interrupt pin is requested
* and not granted --> return
*/
return ret_val;
}
/* setup interrupt pin as input */
ret_val = gpio_direction_input(
p_platform_data->gpio_interrupt_pin);
if (ret_val < 0) {
BCMTCH_ERR(
"ERROR: %s() - Unable to set interrupt pin %d\n",
__func__,
p_platform_data->gpio_interrupt_pin);
/* note :
* it is an error if a interrupt pin is requested
* and not set --> return
*/
return ret_val;
}
} else if (!p_platform_data->touch_irq) {
BCMTCH_INFO("%s() : no interrupt pin configured\n",
__func__);
}
return ret_val;
}
static void bcmtch_free_gpio(
struct bcmtch_data *bcmtch_data_ptr)
{
struct bcmtch_platform_data *p_platform_data;
if (bcmtch_data_ptr) {
p_platform_data =
(struct bcmtch_platform_data *)
&bcmtch_data_ptr->platform_data;
if (gpio_is_valid(p_platform_data->gpio_reset_pin))
gpio_free(p_platform_data->gpio_reset_pin);
if (gpio_is_valid(p_platform_data->gpio_interrupt_pin))
gpio_free(p_platform_data->gpio_interrupt_pin);
}
}
static int32_t bcmtch_os_init_cli(struct device *p_device)
{
int32_t ret_val = 0;
ret_val = device_create_file(p_device, &bcmtch_cli_attr);
if (ret_val)
BCMTCH_ERR(
"ERROR: %s() - device_create_file() failed!\n",
__func__);
return ret_val;
}
static inline void bcmtch_os_free_cli(struct device *p_device)
{
device_remove_file(p_device, &bcmtch_cli_attr);
}
/* ----------------------------------------- */
/* --- BCM Touch Controller PM Functions --- */
/* ----------------------------------------- */
#ifdef CONFIG_PM
#ifndef CONFIG_HAS_EARLYSUSPEND
static int bcmtch_suspend(struct i2c_client *p_client, pm_message_t mesg)
{
if (mesg.event == PM_EVENT_SUSPEND)
bcmtch_dev_suspend(i2c_get_clientdata(p_client));
return 0;
}
static int bcmtch_resume(struct i2c_client *p_client)
{
int rc = 0;
bcmtch_dev_resume(i2c_get_clientdata(p_client));
return rc;
}
#endif
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
static void bcmtch_early_suspend(struct early_suspend *h)
{
struct bcmtch_data *local_bcmtch_data_p =
container_of(
h,
struct bcmtch_data,
bcmtch_early_suspend_desc);
bcmtch_dev_suspend(local_bcmtch_data_p);
}
static void bcmtch_late_resume(struct early_suspend *h)
{
struct bcmtch_data *bcmtch_data_ptr =
container_of(h, struct bcmtch_data,
bcmtch_early_suspend_desc);
bcmtch_dev_resume(bcmtch_data_ptr);
}
static void bcmtch_register_early_suspend(
struct bcmtch_data *bcmtch_data_ptr)
{
/* Init early suspend parameters */
bcmtch_data_ptr->bcmtch_early_suspend_desc.level =
EARLY_SUSPEND_LEVEL_STOP_DRAWING;
bcmtch_data_ptr->bcmtch_early_suspend_desc.suspend =
bcmtch_early_suspend;
bcmtch_data_ptr->bcmtch_early_suspend_desc.resume =
bcmtch_late_resume;
/* Register early suspend parameters */
register_early_suspend(&bcmtch_data_ptr->
bcmtch_early_suspend_desc);
}
static inline void bcmtch_unregister_early_suspend(
struct bcmtch_data *bcmtch_data_ptr)
{
/* Unregister early suspend parameters */
unregister_early_suspend(&bcmtch_data_ptr->
bcmtch_early_suspend_desc);
}
#endif
/* ----------------------------------------- */
/* -- BCM Touch Controller I2C Functions --- */
/* ----------------------------------------- */
static int32_t bcmtch_i2c_read_spm(
struct i2c_client *p_i2c,
uint8_t reg,
uint8_t *data)
{
int32_t ret_val = 0;
/* setup I2C messages for single byte read transaction */
struct i2c_msg msg[2] = {
/* first write register to spm */
{.addr = p_i2c->addr, .flags = 0, .len = 1, .buf = &reg},
/* Second read data from spm reg */
{.addr = p_i2c->addr, .flags = I2C_M_RD, .len = 1, .buf = data}
};
if (i2c_transfer(p_i2c->adapter, msg, 2) != 2)
ret_val = -EIO;
return ret_val;
}
static int32_t bcmtch_i2c_write_spm(
struct i2c_client *p_i2c,
uint8_t reg,
uint8_t data)
{
int32_t ret_val = 0;
/* setup buffer with reg address and data */
uint8_t buffer[2] = { reg, data };
/* setup I2C message for single byte write transaction */
struct i2c_msg msg[1] = {
/* first write message to spm */
{.addr = p_i2c->addr, .flags = 0, .len = 2, .buf = buffer}
};
if (i2c_transfer(p_i2c->adapter, msg, 1) != 1)
ret_val = -EIO;
return ret_val;
}
static int32_t bcmtch_i2c_fast_write_spm(
struct i2c_client *p_i2c,
uint8_t count,
uint8_t *regs,
uint8_t *data)
{
int32_t ret_val = 0;
/*
* support hard-coded for a max of 5 spm write messages
*
* - 1 i2c message uses 2 uint8_t buffers
*
*/
uint8_t buffer[10]; /* buffers for reg address and data */
struct i2c_msg msg[5];
uint32_t n_msg = 0;
uint8_t *buf_ptr = buffer;
/* setup I2C message for single byte write transaction */
while (n_msg < count) {
msg[n_msg].addr = p_i2c->addr;
msg[n_msg].flags = 0;
msg[n_msg].len = 2;
msg[n_msg].buf = buf_ptr;
*buf_ptr++ = regs[n_msg];
*buf_ptr++ = data[n_msg];
n_msg++;
}
if (i2c_transfer(p_i2c->adapter, msg, n_msg) != n_msg)
ret_val = -EIO;
return ret_val;
}
#if BCMTCH_STATE_PROTOCOL
static int32_t bcmtch_state_i2c_send_command(
struct bcmtch_data *bcmtch_data_ptr,
uint8_t cmd,
uint8_t *params,
int param_num)
{
int ret_val = 0;
struct i2c_client *i2c_spm_ptr =
bcmtch_data_ptr->p_i2c_client_spm;
struct i2c_client *i2c_sys_ptr =
bcmtch_data_ptr->p_i2c_client_sys;
uint8_t *i2c_buffer =
bcmtch_data_ptr->bcmtch_state_cmd_buffer;
uint8_t *cmd_buffer = &i2c_buffer[1];
struct i2c_msg msg;
int i, dma_len;
/* set target register */
i2c_buffer[0] = BCMTCH_SPM_REG_MSG_FROM_HOST;
/* distinguish short/long command */
if (param_num < TOFE_HOST_CMD_SHORT_SIZE) {
/* set command */
cmd_buffer[0] = cmd;
/* set parameters */
for (i = 0; i < param_num; i++)
cmd_buffer[i+1] = params[i];
/* padding */
for (i = param_num; i < TOFE_HOST_CMD_SHORT_SIZE; i++)
cmd_buffer[i+1] = 0;
if (cmd == BCMTCH_CMD_RESUME)
cmd_buffer[TOFE_HOST_CMD_SHORT_SIZE] =
BCMTCH_POWER_MODE_WAKE;
/* i2c write transaction */
msg.addr = i2c_spm_ptr->addr;
msg.flags = 0;
msg.len = TOFE_HOST_CMD_SHORT_SIZE+2;
msg.buf = i2c_buffer;
if (i2c_transfer(i2c_spm_ptr->adapter, &msg, 1) != 1)
return -EIO;
/* 500 us delay for FW to process the command */
usleep_range(500, 1000);
} else {
BCMTCH_DBG(
BCMTCH_DF_ST,
"ST: %s long cmd=0x%0x param_num=%d\n",
__func__,
cmd,
param_num);
/* Before sending a long command, we need to make sure the chip
* is in Active mode (which enables the I2C DMA). The Retention
* mode is used only in two modes: StartScan command (for power
* saving) and Suspend. Here we terminate the StartScan command
* in case it's pending.
*/
if (cmd_buffer[0] == BCMTCH_CMD_START_SCAN ||
cmd_buffer[0] == BCMTCH_CMD_SINGLE_SCAN) {
cmd_buffer[0] = 0;
ret_val = bcmtch_state_stop_scan(bcmtch_data_ptr);
if (ret_val)
return ret_val;
}
cmd_buffer[0] = BCMTCH_CMD_LONG;
for (i = 0; i < TOFE_HOST_CMD_SHORT_SIZE; i++)
cmd_buffer[i+1] = 0;
/* i2c write transaction */
msg.addr = i2c_spm_ptr->addr;
msg.flags = 0;
msg.len = TOFE_HOST_CMD_SHORT_SIZE+2;
msg.buf = i2c_buffer;
if (i2c_transfer(i2c_spm_ptr->adapter, &msg, 1) != 1)
return -EIO;
/* 500 us delay for FW to process the command */
usleep_range(500, 1000);
/* setup i2c messages for DMA write request transaction */
i2c_buffer[0] = BCMTCH_SPM_REG_DMA_WFIFO;
cmd_buffer[0] = cmd;
memcpy(&cmd_buffer[1], params, param_num);
dma_len = (((param_num+1) + 3) & ~0x3) + 1;
/* padding */
for (i = param_num + 2; i < dma_len; i++)
i2c_buffer[i] = 0;
/* debug */
BCMTCH_DBG(
BCMTCH_DF_ST,
"ST: send long cmd params: dma_len=%d param_num=%d",
dma_len,
param_num);
for (i = 0; i < dma_len; i++)
BCMTCH_DBG(
BCMTCH_DF_I2C,
"I2C: 0x%02x",
i2c_buffer[i]);
BCMTCH_DBG(BCMTCH_DF_ST, "ST: END.");
/* i2c write transaction */
msg.addr = i2c_sys_ptr->addr;
msg.flags = 0;
msg.len = dma_len;
msg.buf = i2c_buffer;
if (i2c_transfer(i2c_spm_ptr->adapter, &msg, 1) != 1)
ret_val = -EIO;
}
return ret_val;
}
#endif /* BCMTCH_STATE_PROTOCOL */
static int32_t bcmtch_i2c_read_dma(
struct i2c_client *p_i2c,
uint16_t read_len,
uint8_t *read_data)
{
int32_t ret_val = 0;
uint8_t dma_reg = BCMTCH_SPM_REG_DMA_RFIFO;
/* setup I2C messages for DMA read transaction */
struct i2c_msg dma_read[2] = {
/* next write messages to read the DMA request status */
{.addr = p_i2c->addr, .flags = 0, .len = 1, .buf = &dma_reg},
{.addr = p_i2c->addr, .flags = I2C_M_RD,
.len = read_len, .buf = read_data}
};
/* read status */
if (i2c_transfer(p_i2c->adapter, dma_read, 2) != 2)
ret_val = -EIO;
return ret_val;
}
static int32_t bcmtch_i2c_read_sys(
struct i2c_client *p_i2c,
uint32_t sys_addr,
uint16_t read_len,
uint8_t *read_data)
{
int32_t ret_val = 0;
uint8_t dma_reg = BCMTCH_SPM_REG_DMA_RFIFO;
/* setup the DMA header for this read transaction */
uint8_t dma_header[8] = {
/* set dma controller addr */
BCMTCH_SPM_REG_DMA_ADDR,
/* setup dma address */
(sys_addr & 0xFF),
((sys_addr & 0xFF00) >> 8),
((sys_addr & 0xFF0000) >> 16),
((sys_addr & 0xFF000000) >> 24),
/* setup dma length */
(read_len & 0xFF),
((read_len & 0xFF00) >> 8),
/* setup dma mode */
BCMTCH_DMA_MODE_READ
};
/* setup I2C messages for DMA read request transaction */
struct i2c_msg dma_request[3] = {
/* write DMA request header */
{.addr = p_i2c->addr, .flags = 0, .len = 8, .buf = dma_header},
/* next write messages to read the DMA request */
{.addr = p_i2c->addr, .flags = 0, .len = 1, .buf = &dma_reg},
{.addr = p_i2c->addr, .flags = I2C_M_RD,
.len = read_len, .buf = read_data}
};
/* send complete DMA request */
if (i2c_transfer(p_i2c->adapter, dma_request, 3) != 3)
ret_val = -EIO;
return ret_val;
}
static int32_t bcmtch_i2c_write_sys(
struct i2c_client *p_i2c,
uint32_t sys_addr,
uint16_t write_len,
uint8_t *write_data)
{
int total_dma_len, len, dma_len, ret_val = 0;
uint8_t *dma_data;
struct i2c_msg request;
uint8_t dma_header[8] = {
/* dma controller addr */
BCMTCH_SPM_REG_DMA_ADDR,
/* dma address */
(sys_addr & 0xFF),
((sys_addr & 0xFF00) >> 8),
((sys_addr & 0xFF0000) >> 16),
((sys_addr & 0xFF000000) >> 24),
/* dma length */
(write_len & 0xFF),
((write_len & 0xFF00) >> 8),
/* dma mode */
BCMTCH_DMA_MODE_WRITE
};
total_dma_len = write_len + 1;
dma_data = kzalloc(SZ_4K, GFP_KERNEL);
if (!dma_data)
return -ENOMEM;
request.addr = p_i2c->addr;
request.flags = 0;
request.len = 8;
request.buf = dma_header;
if (i2c_transfer(p_i2c->adapter, &request, 1) != 1) {
ret_val = -EIO;
goto error;
}
for (len = total_dma_len; len > 0; len -= (SZ_4K - 1)) {
if (len < (SZ_4K - 1))
dma_len = len;
else
dma_len = SZ_4K;
request.addr = p_i2c->addr;
request.flags = 0;
request.len = dma_len;
request.buf = dma_data;
dma_data[0] = BCMTCH_SPM_REG_DMA_WFIFO;
memcpy(&dma_data[1],
(write_data + (total_dma_len - len)), dma_len - 1);
if (i2c_transfer(p_i2c->adapter, &request, 1) != 1) {
ret_val = -EIO;
break;
}
}
error:
kfree(dma_data);
return ret_val;
}
static int32_t bcmtch_i2c_init_clients(struct i2c_client *p_i2c_client_spm)
{
int32_t ret_val = 0;
struct i2c_client *p_i2c_client_sys;
struct bcmtch_data *bcmtch_data_ptr =
i2c_get_clientdata(p_i2c_client_spm);
if (p_i2c_client_spm->adapter) {
/* Configure the second I2C slave address. */
p_i2c_client_sys =
i2c_new_dummy(
p_i2c_client_spm->adapter,
bcmtch_data_ptr->platform_data.i2c_addr_sys);
if (p_i2c_client_sys) {
/* assign */
bcmtch_data_ptr->p_i2c_client_spm = p_i2c_client_spm;
bcmtch_data_ptr->p_i2c_client_sys = p_i2c_client_sys;
} else {
BCMTCH_ERR(
"%s() i2c_new_dummy == NULL, slave address: 0x%x\n",
__func__,
bcmtch_data_ptr->platform_data.i2c_addr_sys);
ret_val = -ENODEV;
}
} else {
BCMTCH_ERR("%s() p_i2c_adapter == NULL\n",
__func__);
ret_val = -ENODEV;
}
return ret_val;
}
static inline void bcmtch_i2c_free_clients(
struct bcmtch_data *bcmtch_data_ptr)
{
if (bcmtch_data_ptr && (bcmtch_data_ptr->p_i2c_client_sys)) {
i2c_unregister_device(bcmtch_data_ptr->p_i2c_client_sys);
bcmtch_data_ptr->p_i2c_client_sys = NULL;
}
}
/* ------------------------------------------------- */
/* -- BCM Touch Controller I2C Driver Structures --- */
/* ------------------------------------------------- */
static const struct i2c_device_id bcmtch_i2c_id[] = {
{BCMTCH15XXX_NAME, 0},
{}
};
MODULE_DEVICE_TABLE(i2c, bcmtch_i2c_id);
static struct i2c_driver bcmtch_i2c_driver = {
.driver = {
.name = BCMTCH15XXX_NAME,
.owner = THIS_MODULE,
},
.probe = bcmtch_i2c_probe,
.remove = bcmtch_i2c_remove,
.id_table = bcmtch_i2c_id,
#ifdef CONFIG_PM
#ifndef CONFIG_HAS_EARLYSUSPEND
.suspend = bcmtch_suspend,
.resume = bcmtch_resume,
#endif
#endif
};
/* ------------------------------------------------ */
/* -- BCM Touch Controller I2C Driver Functions --- */
/* ------------------------------------------------ */
static int32_t bcmtch_i2c_probe(
struct i2c_client *p_i2c_client,
const struct i2c_device_id *id)
{
struct bcmtch_data *bcmtch_data_ptr;
int32_t ret_val = 0;
BCMTCH_DBG(BCMTCH_DF_INFO, "INFO: Driver: %s : %s : %s\n",
BCMTCH_DRIVER_VERSION,
BCMTCH_DRIVER_BUILD_DATE,
BCMTCH_DRIVER_BUILD_TIME);
/* print driver probe header */
if (p_i2c_client)
BCMTCH_DBG(
BCMTCH_DF_INFO,
"INFO: dev=%s addr=0x%x irq=%d\n",
p_i2c_client->name,
p_i2c_client->addr,
p_i2c_client->irq);
if (id)
BCMTCH_DBG(BCMTCH_DF_INFO, "INFO: match id=%s\n",
id->name);
/* allocate global BCM Touch Controller driver structure */
ret_val = bcmtch_dev_alloc(p_i2c_client);
if (ret_val)
goto probe_error;
/* setup local platform data from client device structure */
ret_val = bcmtch_dev_init_platform(&p_i2c_client->dev);
if (ret_val)
goto init_platform_error;
bcmtch_data_ptr = i2c_get_clientdata(p_i2c_client);
mutex_lock(&bcmtch_data_ptr->mutex_work);
/* initialize deferred worker (workqueue/tasklet/etc */
ret_val = bcmtch_init_deferred_worker(bcmtch_data_ptr);
if (ret_val)
goto worker_error;
/* initialize power supplies */
ret_val = bcmtch_dev_power_init(bcmtch_data_ptr);
if (ret_val)
goto power_init_error;
/* enable power supplies */
ret_val = bcmtch_dev_power_enable(bcmtch_data_ptr, true);
if (ret_val)
goto power_enable_error;
/* setup the gpio pins
* - 1 gpio used for reset control signal to BCM Touch Controller
* - 1 gpio used as interrupt signal from BCM Touch Controller
*/
ret_val = bcmtch_init_gpio(bcmtch_data_ptr);
if (ret_val)
goto gpio_error;
/*
* setup the i2c clients and bind (store pointers in global structure)
* 1. SPM I2C client
* 2. SYS I2C client
*/
ret_val = bcmtch_i2c_init_clients(p_i2c_client);
if (ret_val)
goto i2c_client_error;
/* reset the chip on driver load ? */
if (bcmtch_boot_flag &
BCMTCH_BF_HARD_RESET_ON_LOAD) {
bcmtch_dev_reset(
bcmtch_data_ptr,
BCMTCH_RESET_MODE_HARD);
} else if (bcmtch_boot_flag &
BCMTCH_BF_SOFT_RESET_ON_LOAD) {
bcmtch_dev_reset(
bcmtch_data_ptr,
BCMTCH_RESET_MODE_SOFT_CHIP |
BCMTCH_RESET_MODE_SOFT_ARM);
}
/* init the table of the worker process functions */
bcmtch_dev_init_worker_process(bcmtch_data_ptr);
/* perform BCM Touch Controller initialization */
ret_val = bcmtch_dev_init(bcmtch_data_ptr);
if (ret_val)
goto init_dev_error;
/* setup the os cli */
ret_val = bcmtch_os_init_cli(&p_i2c_client->dev);
if (ret_val)
goto cli_error;
/* setup the sysfs api */
ret_val = bcmtch_os_init_abi(&p_i2c_client->dev);
if (ret_val)
goto sysfs_api_error;
/* setup the os input device*/
ret_val = bcmtch_init_input_device(bcmtch_data_ptr);
if (ret_val)
goto input_dev_error;
bcmtch_dev_watchdog_start(bcmtch_data_ptr);
ret_val = bcmtch_interrupt_enable(bcmtch_data_ptr);
if (ret_val)
goto interrupt_error;
mutex_unlock(&bcmtch_data_ptr->mutex_work);
#ifdef CONFIG_HAS_EARLYSUSPEND
bcmtch_register_early_suspend(bcmtch_data_ptr);
#endif /* CONFIG_HAS_EARLYSUSPEND */
BCMTCH_INFO("PROBE: success\n");
return 0;
interrupt_error:
input_dev_error:
/* Undo input device init */
bcmtch_free_input_device(bcmtch_data_ptr);
sysfs_api_error:
/* Undo sysfs api init */
bcmtch_os_free_abi(&p_i2c_client->dev);
cli_error:
/* Undo os cli init */
bcmtch_os_free_cli(&p_i2c_client->dev);
init_dev_error:
/* Undo touch controller initialization */
i2c_client_error:
/* Undo i2c clients init */
bcmtch_i2c_free_clients(bcmtch_data_ptr);
gpio_error:
/* Undo gpio init */
bcmtch_free_gpio(bcmtch_data_ptr);
power_enable_error:
/* undo power enable */
bcmtch_dev_power_enable(bcmtch_data_ptr, false);
power_init_error:
/* undo power init */
bcmtch_dev_power_free(bcmtch_data_ptr);
/* Undo worker init */
bcmtch_free_deferred_worker(bcmtch_data_ptr);
worker_error:
mutex_unlock(&bcmtch_data_ptr->mutex_work);
init_platform_error:
/* Undo platform init */
bcmtch_dev_free(p_i2c_client);
probe_error:
BCMTCH_ERR("PROBE: failure\n");
return ret_val;
}
static int32_t bcmtch_i2c_remove(struct i2c_client *p_i2c_client)
{
struct bcmtch_data *bcmtch_data_ptr =
i2c_get_clientdata(p_i2c_client);
/* disable interrupts */
bcmtch_interrupt_disable(bcmtch_data_ptr);
#if BCMTCH_STATE_PROTOCOL
/* state protocol stop scan */
if (bcmtch_data_ptr->work_process_index >= BCMTCH_WP_STATE)
bcmtch_state_stop_scan(bcmtch_data_ptr);
#endif /* BCMTCH_STATE_PROTOCOL */
/* free watchdog timer */
bcmtch_dev_watchdog_stop(bcmtch_data_ptr);
/* free deferred worker (queue) */
bcmtch_free_deferred_worker(bcmtch_data_ptr);
mutex_lock(&bcmtch_data_ptr->mutex_work);
#ifdef CONFIG_HAS_EARLYSUSPEND
bcmtch_unregister_early_suspend(bcmtch_data_ptr);
#endif /* CONFIG_HAS_EARLYSUSPEND */
/* force chip to sleep before exiting */
if (BCMTCH_POWER_STATE_SLEEP !=
bcmtch_dev_get_power_state(bcmtch_data_ptr)) {
bcmtch_dev_set_power_state(bcmtch_data_ptr,
BCMTCH_POWER_STATE_SLEEP);
}
/* disable power */
bcmtch_dev_power_enable(bcmtch_data_ptr, false);
/* release power */
bcmtch_dev_power_free(bcmtch_data_ptr);
/* reset + free communication channels */
bcmtch_dev_post_boot_reset(bcmtch_data_ptr);
/* free i2c device clients */
bcmtch_i2c_free_clients(bcmtch_data_ptr);
/* Undo sysfs api init */
bcmtch_os_free_abi(&p_i2c_client->dev);
/* remove the os cli */
bcmtch_os_free_cli(&p_i2c_client->dev);
/* free input device */
bcmtch_free_input_device(bcmtch_data_ptr);
/* free used gpio pins */
bcmtch_free_gpio(bcmtch_data_ptr);
/* free this mem last */
bcmtch_dev_free(p_i2c_client);
return 0;
}
static int32_t __init bcmtch_i2c_init(void)
{
return i2c_add_driver(&bcmtch_i2c_driver);
}
late_initcall(bcmtch_i2c_init);
static void __exit bcmtch_i2c_exit(void)
{
i2c_del_driver(&bcmtch_i2c_driver);
}
module_exit(bcmtch_i2c_exit);
MODULE_DESCRIPTION("I2C support for BCMTCH15XXX Touchscreen");
MODULE_LICENSE("GPL");
MODULE_VERSION(BCMTCH_DRIVER_VERSION);