Ping for review

On 06/13/2017 02:27 PM, Wang Yafei wrote:
> V2 changes:
>
> - replace touchscreen properties according to the description in
>   Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt
>
> - Droped all compat stuff for older kernels
>
> - Removed Android stuff (EARLY_SUSPEND, CONFIG_FB)
>
> - Use device_property_read_*  get device properties
>
> - use get-unaligned_*() API
>
> - Use dev_err() dev_dbg() for logging
>
> - remove pinctrl functions
>
> - remove some unused functions
>
> V1 info:
> This driver is for GOODiX GTx5 series touchscreen controllers
> such as GT8589, GT7589. This driver designed with hierarchial structure,
> for that can be modified to support subsequent controllers easily.
> Some zones of the touchscreen can be set to buttons(according to the
> hardware). That is why it handles button and multitouch events.
>
> A brief description of driver structure
> - Core Layer: This layer responsible for basic input events report,
>   GPIO pinctrl, Interrupt, Power resources manager and submodules
>   manager.
> - Hardware Layer: This layer responsible for controllers initialization,
>   irq handle as well as bus read/write.
> - External Module Layer: This layer used for support more features
>   such as firmware update, debug tools and gesture wakeup.
>
> Signed-off-by: Wang Yafei <wangya...@goodix.com>
> ---
>  drivers/input/touchscreen/Kconfig                  |    1 +
>  drivers/input/touchscreen/Makefile                 |    1 +
>  .../input/touchscreen/goodix-ts-sunrise/Kconfig    |   36 +
>  .../input/touchscreen/goodix-ts-sunrise/Makefile   |    6 +
>  .../touchscreen/goodix-ts-sunrise/goodix-gtx5.txt  |   75 +
>  .../goodix-ts-sunrise/goodix_gtx5_i2c.c            |  895 ++++++++++++
>  .../goodix-ts-sunrise/goodix_gtx5_update.c         | 1450 
> ++++++++++++++++++++
>  .../touchscreen/goodix-ts-sunrise/goodix_ts_core.c | 1366 ++++++++++++++++++
>  .../touchscreen/goodix-ts-sunrise/goodix_ts_core.h |  553 ++++++++
>  .../goodix-ts-sunrise/goodix_ts_tools.c            |  542 ++++++++
>  10 files changed, 4925 insertions(+)
>  create mode 100755 drivers/input/touchscreen/goodix-ts-sunrise/Kconfig
>  create mode 100755 drivers/input/touchscreen/goodix-ts-sunrise/Makefile
>  create mode 100755 
> drivers/input/touchscreen/goodix-ts-sunrise/goodix-gtx5.txt
>  create mode 100755 
> drivers/input/touchscreen/goodix-ts-sunrise/goodix_gtx5_i2c.c
>  create mode 100755 
> drivers/input/touchscreen/goodix-ts-sunrise/goodix_gtx5_update.c
>  create mode 100755 
> drivers/input/touchscreen/goodix-ts-sunrise/goodix_ts_core.c
>  create mode 100755 
> drivers/input/touchscreen/goodix-ts-sunrise/goodix_ts_core.h
>  create mode 100755 
> drivers/input/touchscreen/goodix-ts-sunrise/goodix_ts_tools.c
>
> diff --git a/drivers/input/touchscreen/Kconfig 
> b/drivers/input/touchscreen/Kconfig
> index cf26ca4..f3642bb 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -15,6 +15,7 @@ config TOUCHSCREEN_PROPERTIES
>       def_tristate INPUT
>       depends on INPUT
>  
> +source "drivers/input/touchscreen/goodix-ts-sunrise/Kconfig"
>  config TOUCHSCREEN_88PM860X
>       tristate "Marvell 88PM860x touchscreen"
>       depends on MFD_88PM860X
> diff --git a/drivers/input/touchscreen/Makefile 
> b/drivers/input/touchscreen/Makefile
> index 18e4769..d9408c0 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -6,6 +6,7 @@
>  
>  wm97xx-ts-y := wm97xx-core.o
>  
> +obj-$(CONFIG_TOUCHSCREEN_GOODIX_GTX5)        += goodix-ts-sunrise/
>  obj-$(CONFIG_TOUCHSCREEN_PROPERTIES) += of_touchscreen.o
>  obj-$(CONFIG_TOUCHSCREEN_88PM860X)   += 88pm860x-ts.o
>  obj-$(CONFIG_TOUCHSCREEN_AD7877)     += ad7877.o
> diff --git a/drivers/input/touchscreen/goodix-ts-sunrise/Kconfig 
> b/drivers/input/touchscreen/goodix-ts-sunrise/Kconfig
> new file mode 100755
> index 0000000..8e16595
> --- /dev/null
> +++ b/drivers/input/touchscreen/goodix-ts-sunrise/Kconfig
> @@ -0,0 +1,36 @@
> +#
> +# Goodix touchscreen driver configuration
> +#
> +menuconfig TOUCHSCREEN_GOODIX_GTX5
> +     bool "Goodix GTx5 touchscreen"
> +     depends on I2C
> +     default y
> +     help
> +       Say Y here if you have a Goodix GTx5xx touchscreen connected
> +       to your system.
> +
> +       If unsure, say N.
> +
> +if TOUCHSCREEN_GOODIX_GTX5
> +
> +config TOUCHSCREEN_GOODIX_GTX5_UPDATE
> +     tristate "Goodix GTx5xx firmware update module"
> +     default y
> +     help
> +       Say Y here to enable support for doing firmware update.
> +
> +       If unsure, say N.
> +
> +       To compile this driver as a module, choose M here.
> +
> +config TOUCHSCREEN_GOODIX_GTX5_TOOLS
> +     tristate "Goodix touch tools support"
> +     default n
> +     help
> +       Say Y here to enable debug tools.
> +
> +       If unsure, say N.
> +
> +       To compile this driver as a module, choose M here.
> +
> +endif
> diff --git a/drivers/input/touchscreen/goodix-ts-sunrise/Makefile 
> b/drivers/input/touchscreen/goodix-ts-sunrise/Makefile
> new file mode 100755
> index 0000000..690f256
> --- /dev/null
> +++ b/drivers/input/touchscreen/goodix-ts-sunrise/Makefile
> @@ -0,0 +1,6 @@
> +# Goodix Touchscreen Makefile
> +
> +obj-$(CONFIG_TOUCHSCREEN_GOODIX_GTX5)           += goodix_gtx5_i2c.o
> +obj-$(CONFIG_TOUCHSCREEN_GOODIX_GTX5)           += goodix_ts_core.o
> +obj-$(CONFIG_TOUCHSCREEN_GOODIX_GTX5_TOOLS)    += goodix_ts_tools.o
> +obj-$(CONFIG_TOUCHSCREEN_GOODIX_GTX5_UPDATE)    += goodix_gtx5_update.o
> diff --git a/drivers/input/touchscreen/goodix-ts-sunrise/goodix-gtx5.txt 
> b/drivers/input/touchscreen/goodix-ts-sunrise/goodix-gtx5.txt
> new file mode 100755
> index 0000000..116c3ec
> --- /dev/null
> +++ b/drivers/input/touchscreen/goodix-ts-sunrise/goodix-gtx5.txt
> @@ -0,0 +1,75 @@
> +Device tree bindings for Goodix GTx5 series touchscreen controller
> +
> +Required properties:
> +
> +- compatible : should be "goodix,gtx5" or "goodix,gsx" 
> +
> +- reg        : I2C address of the chip. Should be 0x5d or 0x14
> +- interrupt-parent   : Inerrupt controller to which the chip is connected
> +- interrupts : Interrrupt to which the chip is connected
> +- touchscreen-size-x : horizontal resolution of touchscreen          
> +                                  (in pixels)                                
>    
> +- touchscreen-size-y : vertical resolution of touchscreen            
> +                                  (in pixels)                                
>   
> +- touchscreen-max-id : panel supported max touch number.
> +- touchscreen-max-w  : panel max width value.
> +
> +
> +Optional properties:
> +- reset-gpios        : reset gpio.
> +- irq-gpios  : interrupt gpio. 
> +- irq-flags  : irq trigger type config, value should be:
> +                       1 - rising edge,
> +                       2 - falling edge,
> +                       4 - high level,
> +                       5 - low level.
> +- touchscreen-swapped-x-y: swap  x/y axis coordinates.
> +- touchscreen-key-map: keycode value map  /*KEY_HOMEPAGE, KEY_BACK*/
> +- power-on-delay-us: delay after power on.
> +- power-off-delay-us: delay after power off.
> +- normal-cfg: touch device normal config data.
> +- vtouch-supply      : power supply for the touch device.
> +Example:
> +i2c@00000000 {
> +     /* ... */
> +
> +     goodix-ts-i2c@14 {
> +             compatible = "goodix,gtx5";
> +             reg = <0x14>;
> +             interrupt-parent = <&msm_gpio>;
> +             interrupts = <13 0x2800>;
> +             vtouch-supply = <&pm8916_l15>;
> +             reset-gpios = <&msm_gpio 12 0x0>;
> +             irq-gpios = <&msm_gpio 13 0x2800>;
> +             irq-flags = <1>; /* 1:trigger rising, 2:trigger falling;*/
> +             touchscreen-max-id = <10>;
> +             touchscreen-size-x = <400>;
> +             touchscreen-size-y = <400>;
> +             touchscreen-max-w = <400>;
> +             touchscreen-max-pressure = <255>;
> +             touchscreen-swapped-x-y;
> +             touchscreen-key-map = <172 158>; /*KEY_HOMEPAGE, KEY_BACK*/
> +             sensor0 {
> +                     normal-cfg = [
> +                             02 00 00 09 09 01 07 02 00 00 00 00 01 00 3C 00 
> 07 07
> +                             00 00 00 00 00 00 40 01 40 01 C8 00 96 00 F4 01 
> F4 01
> +                             F4 01 20 01 11 0A 0A 03 14 14 14 14 0A 0C 01 01 
> 11 11
> +                             11 00 14 14 14 14 14 14 14 14 14 00 00 0F 00 00 
> 00 00
> +                             00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
> 00 00
> +                             00 00 00 00 00 00 00 00 00 00 11 09 10 00 31 32 
> 33 34
> +                             00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 CA 
> 64 00
> +                             00 00 00 00 00 00 09 00 13 00 00 00 00 00 00 00 
> 00 00
> +                             50 B0 19 00 19 00 05 00 00 00 00 0A 05 00 00 00 
> 00 00
> +                             01 00 FF 00 0B 06 0D 02 FF 04 05 03 07 01 08 0A 
> 0E 11
> +                             0F 10 09 13 0C 16 17 14 18 12 19 15 1D 1E 1C 1F 
> 1B 20
> +                             1A 2A 29 28 25 2B 27 21 FF 24 22 2C 26 23 FF 00 
> 00 00
> +                             00 00 00 00 00 00 00 00 00 00 00 00 80 80 80 80 
> 80 80
> +                             80 80 80 80 80 80 80 80 80 80 9F 22 01 AE];
> +             };
> +             sensor1 {
> +                     normal-cfg = [ ];
> +             };
> +     };
> +}
> +
> +
> diff --git a/drivers/input/touchscreen/goodix-ts-sunrise/goodix_gtx5_i2c.c 
> b/drivers/input/touchscreen/goodix-ts-sunrise/goodix_gtx5_i2c.c
> new file mode 100755
> index 0000000..5a0585a
> --- /dev/null
> +++ b/drivers/input/touchscreen/goodix-ts-sunrise/goodix_gtx5_i2c.c
> @@ -0,0 +1,895 @@
> +/*
> + * Goodix GTx5 Touchscreen Dirver
> + * Hardware interface layer of touchdriver architecture.
> + *
> + * Copyright (C) 2015 - 2016 Goodix, Inc.
> + * Authors:  Wang Yafei <wangya...@goodix.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be a reference
> + * to you, when you are integrating the GOODiX's CTP IC into your system,
> + * 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.
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/ctype.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/property.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include "goodix_ts_core.h"
> +
> +#define TS_DT_COMPATIBLE     "goodix,gtx5"
> +#define TS_DRIVER_NAME               "goodix_i2c"
> +#define I2C_MAX_TRANSFER_SIZE        256
> +#define TS_ADDR_LENGTH               2
> +
> +#define TS_REG_COORDS_BASE   0x824E
> +#define TS_REG_CMD           0x8040
> +#define TS_REG_REQUEST               0x8044
> +#define TS_REG_VERSION               0x8240
> +#define TS_REG_CFG_BASE              0x8050
> +
> +#define CFG_XMAX_OFFSET (0x8052 - 0x8050)
> +#define CFG_YMAX_OFFSET      (0x8054 - 0x8050)
> +
> +#define REQUEST_HANDLED      0x00
> +#define REQUEST_CONFIG       0x01
> +#define REQUEST_BAKREF       0x02
> +#define REQUEST_RESET        0x03
> +#define REQUEST_MAINCLK      0x04
> +#define REQUEST_IDLE 0x05
> +
> +#define TS_MAX_SENSORID      5
> +#define TS_CFG_MAX_LEN       495
> +/* set defalut irq flags as Falling edge */
> +#define DEFAULT_IRQ_FLAGS 2
> +#if TS_CFG_MAX_LEN > GOODIX_CFG_MAX_SIZE
> +#error GOODIX_CFG_MAX_SIZE too small, please fix.
> +#endif
> +
> +#ifdef CONFIG_OF
> +/*
> + * goodix_parse_dt_resolution - parse resolution from dt
> + * @dev: device
> + * @board_data: pointer to board data structure
> + * return: 0 - no error, <0 error
> + */
> +static int goodix_parse_dt_resolution(struct device *dev,
> +             struct goodix_ts_board_data *board_data)
> +{
> +     int r, err = 0;
> +
> +     r = device_property_read_u32(dev, "touchscreen-max-id",
> +                             &board_data->panel_max_id);
> +     if (r || board_data->panel_max_id > GOODIX_MAX_TOUCH)
> +             board_data->panel_max_id = GOODIX_MAX_TOUCH;
> +
> +     r = device_property_read_u32(dev, "touchscreen-size-x",
> +                              &board_data->panel_max_x);
> +     if (r)
> +             err = -ENOENT;
> +
> +     r = device_property_read_u32(dev, "touchscreen-size-y",
> +                             &board_data->panel_max_y);
> +     if (r)
> +             err = -ENOENT;
> +
> +     r = device_property_read_u32(dev, "touchscreen-max-w",
> +                             &board_data->panel_max_w);
> +     if (r)
> +             err = -ENOENT;
> +
> +     board_data->swap_axis = device_property_read_bool(dev,
> +                     "touchscreen-swapped-x-y");
> +
> +     return err;
> +}
> +
> +/**
> + * goodix_parse_dt - parse board data from dt
> + * @dev: pointer to device
> + * @board_data: pointer to board data structure
> + * return: 0 - no error, <0 error
> + */
> +static int goodix_parse_dt(struct device *dev,
> +     struct goodix_ts_board_data *board_data)
> +{
> +     int r;
> +
> +     if (!board_data) {
> +             dev_err(dev, "Invalid board data\n");
> +             return -EINVAL;
> +     }
> +
> +     r = device_property_read_u32(dev, "irq-flags",
> +                     &board_data->irq_flags);
> +     if (r) {
> +             dev_info(dev, "Use default irq flags:falling_edge\n");
> +             board_data->irq_flags = DEFAULT_IRQ_FLAGS;
> +     }
> +
> +     board_data->avdd_name = "vtouch";
> +     r = device_property_read_u32(dev, "power-on-delay-us",
> +                             &board_data->power_on_delay_us);
> +     if (!r) {
> +             /* 1000ms is too large, maybe you have pass a wrong value */
> +             if (board_data->power_on_delay_us > 1000 * 1000) {
> +                     dev_warn(dev, "Power on delay time exceed 1s\n");
> +                     board_data->power_on_delay_us = 0;
> +             }
> +     }
> +
> +     r = device_property_read_u32(dev, "power-off-delay-us",
> +                             &board_data->power_off_delay_us);
> +     if (!r) {
> +             /* 1000ms is too large, maybe you have pass a wrong value */
> +             if (board_data->power_off_delay_us > 1000 * 1000) {
> +                     dev_warn(dev, "Power off delay time exceed 1s\n");
> +                     board_data->power_off_delay_us = 0;
> +             }
> +     }
> +
> +     /* get xyz resolutions */
> +     r = goodix_parse_dt_resolution(dev, board_data);
> +     if (r < 0) {
> +             dev_err(dev, "Failed to parse resolutions:%d\n", r);
> +             return r;
> +     }
> +
> +     /* parse key map */
> +     r = device_property_read_u32_array(dev, "panel-key-map",
> +                     NULL, GOODIX_MAX_KEY);
> +     if (r > 0 && r <= GOODIX_MAX_KEY) {
> +             board_data->panel_max_key = r;
> +             r = device_property_read_u32_array(dev,
> +                             "panel-key-map",
> +                             &board_data->panel_key_map[0],
> +                             board_data->panel_max_key);
> +             if (r)
> +                     dev_err(dev, "Failed get key map info\n");
> +     } else {
> +             dev_info(dev, "No key map found\n");
> +     }
> +
> +     dev_dbg(dev, "[DT]id:%d, x:%d, y:%d, w:%d, p:%d\n",
> +                     board_data->panel_max_id,
> +                     board_data->panel_max_x,
> +                     board_data->panel_max_y,
> +                     board_data->panel_max_w);
> +     return 0;
> +}
> +
> +/**
> + * goodix_parse_dt_cfg - pares config data from devicetree dev
> + * @dev: pointer to device
> + * @cfg_type: config type such as normal_config, highsense_cfg ...
> + * @config: pointer to config data structure
> + * @sensor_id: sensor id
> + * return: 0 - no error, <0 error
> + */
> +static int goodix_parse_dt_cfg(struct goodix_ts_device *ts_dev,
> +             char *cfg_type, struct goodix_ts_config *config,
> +             unsigned int sensor_id)
> +{
> +     int r, len;
> +     char sub_node_name[24] = {0};
> +     struct fwnode_handle *fwnode;
> +     struct device *dev = ts_dev->dev;
> +     struct goodix_ts_board_data *ts_bdata = ts_dev->board_data;
> +
> +     u16 checksum;
> +
> +     BUG_ON(config == NULL);
> +     if (sensor_id > TS_MAX_SENSORID) {
> +             dev_err(dev, "Invalid sensor id\n");
> +             return -EINVAL;
> +     }
> +
> +     if (config->initialized) {
> +             dev_dbg(dev, "Config already initialized\n");
> +             return 0;
> +     }
> +
> +     /*
> +      * config data are located in child node called
> +      * 'sensorx', x is the sensor ID got from touch
> +      * device.
> +      */
> +     snprintf(sub_node_name, sizeof(sub_node_name),
> +                     "sensor%u", sensor_id);
> +     fwnode = device_get_named_child_node(dev, "sub_node_name");
> +     if (!fwnode) {
> +             dev_dbg(dev, "Child property[%s] not found\n",
> +                             sub_node_name);
> +             return -EINVAL;
> +     }
> +
> +     len = fwnode_property_read_u8_array(fwnode, cfg_type,
> +             NULL, TS_CFG_MAX_LEN);
> +     if (len <= 0 || len % 2 != 1) {
> +             dev_err(dev, "Invalid cfg type%s, size:%u\n", cfg_type, len);
> +             return -EINVAL;
> +     }
> +
> +     config->length = len;
> +
> +     mutex_init(&config->lock);
> +     mutex_lock(&config->lock);
> +
> +     r = fwnode_property_read_u8_array(fwnode, cfg_type,
> +             config->data, TS_CFG_MAX_LEN);
> +     if (r) {
> +             mutex_unlock(&config->lock);
> +             return r;
> +     }
> +
> +     /* modify max-x max-y resolution, little-endian */
> +     config->data[CFG_XMAX_OFFSET] = (u8)ts_bdata->panel_max_x;
> +     config->data[CFG_XMAX_OFFSET + 1] = (u8)(ts_bdata->panel_max_x >> 8);
> +     config->data[CFG_YMAX_OFFSET] = (u8)ts_bdata->panel_max_y;
> +     config->data[CFG_YMAX_OFFSET + 1] = (u8)(ts_bdata->panel_max_y >> 8);
> +
> +     /*
> +      * checksum: u16 little-endian format
> +      * the last byte of config is the config update flag
> +      */
> +     checksum = checksum_le16(config->data, len - 3);
> +     checksum = 0 - checksum;
> +     config->data[len - 3] = (u8)checksum;
> +     config->data[len - 2] = (u8)(checksum >> 8 & 0xff);
> +     config->data[len - 1] = 0x01;
> +
> +     strlcpy(config->name, cfg_type, sizeof(config->name));
> +     config->reg_base = TS_REG_CFG_BASE;
> +     config->delay = 0;
> +     config->initialized = true;
> +     mutex_unlock(&config->lock);
> +
> +     dev_dbg(dev, "Config name:%s,ver:%02xh,size:%d,checksum:%04xh\n",
> +                     config->name, config->data[0],
> +                     config->length, checksum);
> +     return 0;
> +}
> +#endif
> +
> +/**
> + * goodix_i2c_read - read device register through i2c bus
> + * @dev: pointer to device data
> + * @addr: register address
> + * @data: read buffer
> + * @len: bytes to read
> + * return: 0 - read ok, < 0 - i2c transter error
> + */
> +static int goodix_i2c_read(struct goodix_ts_device *dev, unsigned int reg,
> +     unsigned char *data, unsigned int len)
> +{
> +     struct i2c_client *client = to_i2c_client(dev->dev);
> +     unsigned int transfer_length = 0;
> +     unsigned int pos = 0, address = reg;
> +     unsigned char get_buf[64], addr_buf[2];
> +     int retry, r = 0;
> +     struct i2c_msg msgs[] = {
> +             {
> +                     .addr = client->addr,
> +                     .flags = !I2C_M_RD,
> +                     .buf = &addr_buf[0],
> +                     .len = TS_ADDR_LENGTH,
> +             }, {
> +                     .addr = client->addr,
> +                     .flags = I2C_M_RD,
> +             }
> +     };
> +
> +     if (likely(len < sizeof(get_buf))) {
> +             /* code optimize, use stack memory */
> +             msgs[1].buf = &get_buf[0];
> +     } else {
> +             msgs[1].buf = kzalloc(len > I2C_MAX_TRANSFER_SIZE
> +                             ? I2C_MAX_TRANSFER_SIZE : len, GFP_KERNEL);
> +             if (!msgs[1].buf)
> +                     return -ENOMEM;
> +     }
> +
> +     while (pos != len) {
> +             if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE))
> +                     transfer_length = I2C_MAX_TRANSFER_SIZE;
> +             else
> +                     transfer_length = len - pos;
> +
> +             msgs[0].buf[0] = (address >> 8) & 0xFF;
> +             msgs[0].buf[1] = address & 0xFF;
> +             msgs[1].len = transfer_length;
> +
> +             for (retry = 0; retry < GOODIX_BUS_RETRY_TIMES; retry++) {
> +                     if (likely(i2c_transfer(client->adapter, msgs, 2) == 
> 2)) {
> +                             memcpy(&data[pos], msgs[1].buf, 
> transfer_length);
> +                             pos += transfer_length;
> +                             address += transfer_length;
> +                             break;
> +                     }
> +                     dev_info(&client->dev, "I2c read retry[%d]:0x%x\n",
> +                             retry + 1, reg);
> +                     msleep(20);
> +             }
> +             if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) {
> +                     dev_err(&client->dev,
> +                             "I2c read failed,dev:%02x,reg:%04x,size:%u\n",
> +                             client->addr, reg, len);
> +                     r = -EBUS;
> +                     goto read_exit;
> +             }
> +     }
> +
> +read_exit:
> +     if (unlikely(len >= sizeof(get_buf)))
> +             kfree(msgs[1].buf);
> +     return r;
> +}
> +
> +/**
> + * goodix_i2c_write - write device register through i2c bus
> + * @ts_dev: pointer to goodix device data
> + * @addr: register address
> + * @data: write buffer
> + * @len: bytes to write
> + * return: 0 - write ok; < 0 - i2c transter error.
> + */
> +static int goodix_i2c_write(struct goodix_ts_device *ts_dev, unsigned int 
> reg,
> +             unsigned char *data, unsigned int len)
> +{
> +     struct i2c_client *client = to_i2c_client(ts_dev->dev);
> +     unsigned int pos = 0, transfer_length = 0;
> +     unsigned int address = reg;
> +     unsigned char put_buf[64];
> +     int retry, r = 0;
> +     struct i2c_msg msg = {
> +                     .addr = client->addr,
> +                     .flags = !I2C_M_RD,
> +     };
> +
> +     if (likely(len + TS_ADDR_LENGTH < sizeof(put_buf))) {
> +             /* code optimize,use stack memory*/
> +             msg.buf = &put_buf[0];
> +     } else {
> +             msg.buf = kmalloc(len + TS_ADDR_LENGTH > I2C_MAX_TRANSFER_SIZE
> +                     ? I2C_MAX_TRANSFER_SIZE : len + TS_ADDR_LENGTH, 
> GFP_KERNEL);
> +             if (!msg.buf)
> +                     return -ENOMEM;
> +     }
> +
> +     while (pos != len) {
> +             if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE - 
> TS_ADDR_LENGTH))
> +                     transfer_length = I2C_MAX_TRANSFER_SIZE - 
> TS_ADDR_LENGTH;
> +             else
> +                     transfer_length = len - pos;
> +
> +             msg.buf[0] = (unsigned char)((address >> 8) & 0xFF);
> +             msg.buf[1] = (unsigned char)(address & 0xFF);
> +             msg.len = transfer_length + 2;
> +             memcpy(&msg.buf[2], &data[pos], transfer_length);
> +
> +             for (retry = 0; retry < GOODIX_BUS_RETRY_TIMES; retry++) {
> +                     if (likely(i2c_transfer(client->adapter, &msg, 1) == 
> 1)) {
> +                             pos += transfer_length;
> +                             address += transfer_length;
> +                             break;
> +                     }
> +                     dev_info(&client->dev, "I2c write retry[%d]\n", retry + 
> 1);
> +                     msleep(20);
> +             }
> +             if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) {
> +                     dev_err(&client->dev,
> +                             "I2c write failed,dev:%02x,reg:%04x,size:%u",
> +                             client->addr, reg, len);
> +                     r = -EBUS;
> +                     goto write_exit;
> +             }
> +     }
> +
> +write_exit:
> +     if (likely(len + TS_ADDR_LENGTH >= sizeof(put_buf)))
> +             kfree(msg.buf);
> +     return r;
> +}
> +
> +static int goodix_read_version(struct goodix_ts_device *ts_dev,
> +             struct goodix_ts_version *version)
> +{
> +     u8 buffer[12];
> +     int r;
> +
> +     r = goodix_i2c_read(ts_dev, TS_REG_VERSION,
> +                     buffer, sizeof(buffer));
> +     if (r < 0) {
> +             dev_err(ts_dev->dev, "Read chip version failed\n");
> +             if (version)
> +                     version->valid = false;
> +             return r;
> +     }
> +
> +     /* if checksum is right and first 4 bytes are not invalid value */
> +     if (checksum_u8(buffer, sizeof(buffer)) == 0 &&
> +                     isalnum(buffer[0]) && isalnum(buffer[1]) &&
> +                     isalnum(buffer[2]) && isalnum(buffer[3])) {
> +             if (version) {
> +                     memcpy(&version->pid[0], buffer, 4);
> +                     version->pid[4] = '\0';
> +                     version->cid = buffer[4];
> +                     /* vid = main version + minor version */
> +                     version->vid = get_unaligned_be16(&buffer[5]);
> +                     version->sensor_id = buffer[10] & 0x0F;
> +                     version->valid = true;
> +
> +                     if (version->cid)
> +                             dev_info(ts_dev->dev,
> +                                     "PID:%s,CID: %c,VID:%04x,SensorID:%u\n",
> +                                     version->pid, version->cid + 'A' - 1,
> +                                     version->vid, version->sensor_id);
> +                     else
> +                             dev_info(ts_dev->dev,
> +                                     "PID:%s,VID:%04x,SensorID:%u\n",
> +                                     version->pid, version->vid,
> +                                     version->sensor_id);
> +             }
> +     } else {
> +             dev_warn(ts_dev->dev, "Checksum error:%*ph\n",
> +                     (int)sizeof(buffer), buffer);
> +             /* mark this version is invalid */
> +             if (version)
> +                     version->valid = false;
> +             r = -EINVAL;
> +     }
> +
> +     return r;
> +}
> +
> +/**
> + * goodix_send_config - send config data to device.
> + * @ts_dev: pointer to goodix device data
> + * @config: pointer to config data struct to be send
> + * @return: 0 - succeed, < 0 - failed
> + */
> +static int goodix_send_config(struct goodix_ts_device *ts_dev,
> +             struct goodix_ts_config *config)
> +{
> +     int r = 0;
> +
> +     if (!config || !config->data) {
> +             dev_warn(ts_dev->dev, "Null config data\n");
> +             return -EINVAL;
> +     }
> +
> +     dev_dbg(ts_dev->dev, "Send %s,ver:%02xh,size:%d\n",
> +             config->name, config->data[0],
> +             config->length);
> +
> +     mutex_lock(&config->lock);
> +     r = goodix_i2c_write(ts_dev, config->reg_base,
> +                     config->data, config->length);
> +     if (r)
> +             goto exit;
> +
> +     /* make sure the firmware accept the config data*/
> +     if (config->delay)
> +             msleep(config->delay);
> +exit:
> +     mutex_unlock(&config->lock);
> +     return r;
> +}
> +
> +static inline int goodix_cmds_init(struct goodix_ts_device *ts_dev)
> +{
> +     /* low power mode command */
> +     ts_dev->sleep_cmd.cmd_reg = TS_REG_CMD;
> +     ts_dev->sleep_cmd.length = 3;
> +     ts_dev->sleep_cmd.cmds[0] = 0x05;
> +     ts_dev->sleep_cmd.cmds[1] = 0x0;
> +     ts_dev->sleep_cmd.cmds[2] = 0 - 0x05;
> +     ts_dev->sleep_cmd.initialized = true;
> +
> +     return 0;
> +}
> +
> +/**
> + * goodix_hw_init - hardware initialize
> + *   Called by touch core module when bootup
> + * @ts_dev: pointer to touch device
> + * return: 0 - no error, <0 error
> + */
> +static int goodix_hw_init(struct goodix_ts_device *ts_dev)
> +{
> +     int r;
> +
> +     BUG_ON(!ts_dev);
> +     goodix_cmds_init(ts_dev);
> +
> +     /* goodix_hw_init may be called many times */
> +     if (!ts_dev->normal_cfg) {
> +             ts_dev->normal_cfg = devm_kzalloc(ts_dev->dev,
> +                             sizeof(*ts_dev->normal_cfg), GFP_KERNEL);
> +             if (!ts_dev->normal_cfg) {
> +                     dev_err(ts_dev->dev,
> +                             "Failed to alloc memory for normal cfg\n");
> +                     return -ENOMEM;
> +             }
> +     }
> +
> +     /* read chip version: PID/VID/sensor ID,etc.*/
> +     r = goodix_read_version(ts_dev, &ts_dev->chip_version);
> +     if (r < 0)
> +             return r;
> +
> +#ifdef CONFIG_OF
> +     /* parse normal-cfg from devicetree node */
> +     r = goodix_parse_dt_cfg(ts_dev, "normal-cfg",
> +                     ts_dev->normal_cfg,
> +                     ts_dev->chip_version.sensor_id);
> +     if (r < 0) {
> +             dev_warn(ts_dev->dev, "Failed to obtain normal-cfg\n");
> +             return r;
> +     }
> +#endif
> +
> +     ts_dev->normal_cfg->delay = 500;
> +     /* send normal-cfg to firmware */
> +     r = goodix_send_config(ts_dev, ts_dev->normal_cfg);
> +
> +     return r;
> +}
> +
> +/**
> + * goodix_hw_reset - reset device
> + *
> + * @dev: pointer to touch device
> + * Returns 0 - succeed,<0 - failed
> + */
> +static void goodix_hw_reset(struct goodix_ts_device *dev)
> +{
> +     dev_dbg(dev->dev, "HW reset\n");
> +
> +     if (!dev->board_data->reset_gpiod) {
> +             msleep(80);
> +             return;
> +     }
> +     gpiod_direction_output(dev->board_data->reset_gpiod, 0);
> +     udelay(200);
> +     gpiod_direction_output(dev->board_data->reset_gpiod, 1);
> +     msleep(80);
> +}
> +
> +/**
> + * goodix_request_handler - handle firmware request
> + *
> + * @dev: pointer to touch device
> + * @request_data: requset information
> + * Returns 0 - succeed,<0 - failed
> + */
> +static int goodix_request_handler(struct goodix_ts_device *dev,
> +             struct goodix_request_data *request_data) {
> +     unsigned char buffer[1];
> +     int r;
> +
> +     r = goodix_i2c_read(dev, TS_REG_REQUEST, buffer, 1);
> +     if (r < 0)
> +             return r;
> +
> +     switch (buffer[0]) {
> +     case REQUEST_CONFIG:
> +             dev_dbg(dev->dev, "HW request config\n");
> +             goodix_send_config(dev, dev->normal_cfg);
> +             goto clear_requ;
> +     case REQUEST_BAKREF:
> +             dev_dbg(dev->dev, "HW request bakref\n");
> +             goto clear_requ;
> +     case REQUEST_RESET:
> +             dev_dbg(dev->dev, "HW requset reset\n");
> +             goto clear_requ;
> +     case REQUEST_MAINCLK:
> +             dev_dbg(dev->dev, "HW request mainclk\n");
> +             goto clear_requ;
> +     default:
> +             dev_dbg(dev->dev, "Unknown hw request:%d\n", buffer[0]);
> +             return 0;
> +     }
> +
> +clear_requ:
> +     buffer[0] = 0x00;
> +     r = goodix_i2c_write(dev, TS_REG_REQUEST, buffer, 1);
> +     return r;
> +}
> +
> +/**
> + * goodix_eventt_handler - handle firmware event
> + *
> + * @dev: pointer to touch device
> + * @ts_event: pointer to touch event structure
> + * Returns 0 - succeed,<0 - failed
> + */
> +static int goodix_event_handler(struct goodix_ts_device *dev,
> +             struct goodix_ts_event *ts_event)
> +{
> +#define BYTES_PER_COORD 8
> +     struct goodix_touch_data *touch_data =
> +                     &ts_event->event_data.touch_data;
> +     struct goodix_ts_coords *coords = &touch_data->coords[0];
> +     int max_touch_num = dev->board_data->panel_max_id;
> +     unsigned char buffer[2 + BYTES_PER_COORD * max_touch_num];
> +     unsigned char coord_sta;
> +     int touch_num = 0, i, r;
> +     unsigned char chksum = 0;
> +
> +     r = goodix_i2c_read(dev, TS_REG_COORDS_BASE,
> +                     buffer, 3 + BYTES_PER_COORD/* * 1*/);
> +     if (unlikely(r < 0))
> +             return r;
> +
> +     /* buffer[0]: event state */
> +     coord_sta = buffer[0];
> +     if (unlikely(coord_sta == 0x00)) {
> +             /* handle request event */
> +             ts_event->event_type = EVENT_REQUEST;
> +             goodix_request_handler(dev,
> +                             &ts_event->event_data.request_data);
> +             goto exit_clean_sta;
> +     } else if (unlikely((coord_sta & 0x80) != 0x80)) {
> +             r = -EINVAL;
> +             return r;
> +     }
> +
> +     /* bit7 of coord_sta is 1, touch data is ready */
> +     /* handle touch event */
> +     touch_data->key_value = (coord_sta >> 4) & 0x01;
> +     touch_num = coord_sta & 0x0F;
> +     if (unlikely(touch_num > max_touch_num)) {
> +             touch_num = -EINVAL;
> +             goto exit_clean_sta;
> +     } else if (unlikely(touch_num > 1)) {
> +             r = goodix_i2c_read(dev,
> +                             TS_REG_COORDS_BASE + 3 + BYTES_PER_COORD,
> +                             &buffer[3 + BYTES_PER_COORD],
> +                             (touch_num - 1) * BYTES_PER_COORD);
> +             if (unlikely(r < 0))
> +                     goto exit_clean_sta;
> +     }
> +
> +     /* touch_num * BYTES_PER_COORD + 1(touch event state) + 1(checksum)
> +      * + 1(key value)
> +      */
> +     chksum = checksum_u8(&buffer[0], touch_num * BYTES_PER_COORD + 3);
> +     if (unlikely(chksum != 0)) {
> +             dev_warn(dev->dev, "Checksum error:%X\n", chksum);
> +             r = -EINVAL;
> +             goto exit_clean_sta;
> +     }
> +
> +     memset(touch_data->coords, 0x00, sizeof(touch_data->coords));
> +     for (i = 0; i < touch_num; i++) {
> +             coords->id = buffer[i * BYTES_PER_COORD + 1] & 0x0f;
> +             coords->x = get_unaligned_le16(&buffer[i * BYTES_PER_COORD + 
> 2]);
> +             coords->y = get_unaligned_le16(&buffer[i * BYTES_PER_COORD + 
> 4]);
> +             coords->w = get_unaligned_le16(&buffer[i * BYTES_PER_COORD + 
> 6]);
> +
> +             dev_dbg(dev->dev, "D:[%d](%d, %d)[%d]\n",
> +                     coords->id, coords->x, coords->y, coords->w);
> +             coords++;
> +     }
> +
> +     touch_data->touch_num = touch_num;
> +     /* mark this event as touch event */
> +     ts_event->event_type = EVENT_TOUCH;
> +     r = 0;
> +
> +exit_clean_sta:
> +     /* handshake */
> +     buffer[0] = 0x00;
> +     goodix_i2c_write(dev, TS_REG_COORDS_BASE, buffer, 1);
> +     return r;
> +}
> +
> +/**
> + * goodix_send_command - seng cmd to firmware
> + *
> + * @dev: pointer to device
> + * @cmd: pointer to command struct which cotain command data
> + * Returns 0 - succeed,<0 - failed
> + */
> +int goodix_send_command(struct goodix_ts_device *dev,
> +             struct goodix_ts_cmd *cmd)
> +{
> +     int ret;
> +
> +     if (!cmd || !cmd->initialized)
> +             return -EINVAL;
> +     ret = goodix_i2c_write(dev, cmd->cmd_reg, cmd->cmds,
> +                     cmd->length);
> +     return ret;
> +}
> +
> +/**
> + * goodix_hw_suspend - Let touch deivce stay in lowpower mode.
> + * @dev: pointer to goodix touch device
> + * @return: 0 - succeed, < 0 - failed
> + */
> +static int goodix_hw_suspend(struct goodix_ts_device *dev)
> +{
> +     struct goodix_ts_cmd *sleep_cmd =
> +                     &dev->sleep_cmd;
> +     int r = 0;
> +
> +     if (sleep_cmd->initialized) {
> +             r = goodix_send_command(dev, sleep_cmd);
> +             if (!r)
> +                     dev_dbg(dev->dev, "Chip in sleep mode\n");
> +     } else {
> +             dev_dbg(dev->dev, "Uninitialized sleep command\n");
> +     }
> +
> +     return r;
> +}
> +
> +/**
> + * goodix_hw_resume - Let touch deivce stay in active  mode.
> + * @dev: pointer to goodix touch device
> + * @return: 0 - succeed, < 0 - failed
> + */
> +static int goodix_hw_resume(struct goodix_ts_device *dev)
> +{
> +     struct goodix_ts_version ver;
> +     int r, retry = GOODIX_BUS_RETRY_TIMES;
> +
> +     for (; retry--;) {
> +             goodix_hw_reset(dev);
> +             r = goodix_read_version(dev, &ver);
> +             if (!r)
> +                     break;
> +     }
> +
> +     return r;
> +}
> +
> +/* hardware opeation funstions */
> +static const struct goodix_ts_hw_ops hw_i2c_ops = {
> +     .init = goodix_hw_init,
> +     .read = goodix_i2c_read,
> +     .write = goodix_i2c_write,
> +     .reset = goodix_hw_reset,
> +     .event_handler = goodix_event_handler,
> +     .send_config = goodix_send_config,
> +     .send_cmd = goodix_send_command,
> +     .read_version = goodix_read_version,
> +     .suspend = goodix_hw_suspend,
> +     .resume = goodix_hw_resume,
> +};
> +
> +static struct platform_device *goodix_pdev;
> +static void goodix_pdev_release(struct device *dev)
> +{
> +     kfree(goodix_pdev);
> +}
> +
> +static int goodix_i2c_probe(struct i2c_client *client,
> +     const struct i2c_device_id *dev_id)
> +{
> +     struct goodix_ts_device *ts_device = NULL;
> +     struct goodix_ts_board_data *ts_bdata = NULL;
> +     int r = 0;
> +
> +     r = i2c_check_functionality(client->adapter,
> +             I2C_FUNC_I2C);
> +     if (!r)
> +             return -EIO;
> +
> +     /* board data */
> +     ts_bdata = devm_kzalloc(&client->dev,
> +                     sizeof(struct goodix_ts_board_data), GFP_KERNEL);
> +     if (!ts_bdata)
> +             return -ENOMEM;
> +
> +#ifdef CONFIG_OF
> +     if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) {
> +             /* parse devicetree property */
> +             r = goodix_parse_dt(client->dev, ts_bdata);
> +             if (r < 0)
> +                     return r;
> +     } else
> +#endif
> +     {
> +             /* use platform data */
> +             dev_info(&client->dev, "use platform data\n");
> +             devm_kfree(&client->dev, ts_bdata);
> +             ts_bdata = client->dev.platform_data;
> +     }
> +
> +     if (!ts_bdata)
> +             return -ENODEV;
> +
> +     ts_device = devm_kzalloc(&client->dev,
> +             sizeof(struct goodix_ts_device), GFP_KERNEL);
> +     if (!ts_device)
> +             return -ENOMEM;
> +
> +     ts_bdata->irq = client->irq;
> +     ts_device->name = "GTx5 TouchDevcie";
> +     ts_device->dev = &client->dev;
> +     ts_device->board_data = ts_bdata;
> +     ts_device->hw_ops = &hw_i2c_ops;
> +
> +     /* ts core device */
> +     goodix_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL);
> +     if (!goodix_pdev)
> +             return -ENOMEM;
> +
> +     goodix_pdev->name = GOODIX_CORE_DRIVER_NAME;
> +     goodix_pdev->id = 0;
> +     goodix_pdev->num_resources = 0;
> +     /*
> +      * you could find this platform dev in
> +      * /sys/devices/platform/goodix_ts.0
> +      * goodix_pdev->dev.parent = &client->dev;
> +      */
> +     goodix_pdev->dev.platform_data = ts_device;
> +     goodix_pdev->dev.release = goodix_pdev_release;
> +
> +     /* register platform device, then the goodix_ts_core module will probe
> +      * the touch deivce.
> +      */
> +     r = platform_device_register(goodix_pdev);
> +     return r;
> +}
> +
> +static int goodix_i2c_remove(struct i2c_client *client)
> +{
> +     platform_device_unregister(goodix_pdev);
> +     return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id gtx5_of_matchs[] = {
> +     {.compatible = TS_DT_COMPATIBLE,},
> +     {},
> +};
> +MODULE_DEVICE_TABLE(of, gtx5_of_matchs);
> +#endif
> +
> +static const struct i2c_device_id gtx5_id_table[] = {
> +     {TS_DRIVER_NAME, 0},
> +     {},
> +};
> +MODULE_DEVICE_TABLE(i2c, gtx5_id_table);
> +
> +static struct i2c_driver goodix_i2c_driver = {
> +     .driver = {
> +             .name = TS_DRIVER_NAME,
> +             .owner = THIS_MODULE,
> +             .of_match_table = of_match_ptr(gtx5_of_matchs),
> +     },
> +     .probe = goodix_i2c_probe,
> +     .remove = goodix_i2c_remove,
> +     .id_table = gtx5_id_table,
> +};
> +
> +static int __init goodix_i2c_init(void)
> +{
> +     return i2c_add_driver(&goodix_i2c_driver);
> +}
> +
> +static void __exit goodix_i2c_exit(void)
> +{
> +     i2c_del_driver(&goodix_i2c_driver);
> +}
> +
> +module_init(goodix_i2c_init);
> +module_exit(goodix_i2c_exit);
> +
> +MODULE_DESCRIPTION("Goodix GTx5 Touchscreen Hardware Module");
> +MODULE_AUTHOR("Goodix, Inc.");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/input/touchscreen/goodix-ts-sunrise/goodix_gtx5_update.c 
> b/drivers/input/touchscreen/goodix-ts-sunrise/goodix_gtx5_update.c
> new file mode 100755
> index 0000000..7dd0e6b
> --- /dev/null
> +++ b/drivers/input/touchscreen/goodix-ts-sunrise/goodix_gtx5_update.c
> @@ -0,0 +1,1450 @@
> +/*
> + * Goodix GTx5 Touchscreen Driver.
> + *
> + * Copyright (C) 2015 - 2016 Goodix, Inc.
> + * Authors:  Wang Yafei <wangya...@goodix.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be a reference
> + * to you, when you are integrating the GOODiX's CTP IC into your system,
> + * 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.
> + */
> +#include "goodix_ts_core.h"
> +
> +/* COMMON PART - START */
> +#define TS_DEFAULT_FIRMWARE                  "goodix_ts_fw.bin"
> +
> +#define FW_HEADER_SIZE                               256
> +#define FW_SUBSYS_INFO_SIZE                  8
> +#define FW_SUBSYS_INFO_OFFSET                        32
> +#define FW_SUBSYS_MAX_NUM                    24
> +#define FW_NAME_MAX                          128
> +
> +#define ISP_MAX_BUFFERSIZE                   (1024 * 16)
> +
> +#define HW_REG_CPU_EN                                0x4180
> +#define HW_REG_ILM_ACCESS                    0x50C0
> +#define HW_REG_BANK_SELECT                   0x50C4
> +#define HW_REG_ISP_ADDR                              0x8000
> +#define HW_REG_ISP_STAT                              0x4195
> +#define HW_REG_ISP_CMD                               0x4196
> +#define HW_REG_ISP_PKT_INFO                  0xFFF0
> +#define HW_REG_ISP_RESULT                    0x4197
> +#define HW_REG_ISP_BUFFER                    0x8000
> +#define HW_REG_BOOT_FLAG                     0x434C
> +#define HW_REG_BOOT_CTRL0                    0xF7CC
> +#define HW_REG_BOOT_CTRL1                    0xF7EC
> +#define HW_REG_WDT                           0x40B0
> +
> +#define CPU_CTRL_PENDING                     0x00
> +#define CPU_CTRL_RUNNING                     0x01
> +
> +#define ISP_STAT_IDLE                                0xFF
> +#define ISP_STAT_READY                               0xAA
> +#define ISP_STAT_WRITING                     0xCC
> +#define ISP_FLASH_ERROR                              0xEE
> +#define ISP_FLASH_SUCCESS                    0xDD
> +#define ISP_CMD_PREPARE                              0x55
> +#define ISP_CMD_FLASH                                0xAA
> +
> +/**
> + * fw_subsys_info - subsytem firmware information
> + * @type: sybsystem type
> + * @size: firmware size
> + * @flash_addr: flash address
> + * @data: firmware data
> + */
> +struct fw_subsys_info {
> +     u8 type;
> +     u32 size;
> +     u32 flash_addr;
> +     const u8 *data;
> +};
> +
> +#pragma pack(1)
> +/**
> + * firmware_info
> + * @size: fw total length
> + * @checksum: checksum of fw
> + * @hw_pid: mask pid string
> + * @hw_pid: mask vid code
> + * @fw_pid: fw pid string
> + * @fw_vid: fw vid code
> + * @subsys_num: number of fw subsystem
> + * @chip_type: chip type
> + * @protocol_ver: firmware packing
> + *   protocol version
> + * @subsys: sybsystem info
> + */
> +struct firmware_info {
> +     u32 size;
> +     u16 checksum;
> +     u8 hw_pid[6];
> +     u8 hw_vid[3];
> +     u8 fw_pid[8];
> +     u8 fw_vid[3];
> +     u8 subsys_num;
> +     u8 chip_type;
> +     u8 protocol_ver;
> +     u8 reserved[3];
> +     struct fw_subsys_info subsys[FW_SUBSYS_MAX_NUM];
> +};
> +
> +/**
> + * firmware_packet - firmware packet information
> + * @packet_size: firmware packet size, max 4Kbytes.
> + * @flash_addr: device flash address
> + * @packet_checksum: checksum of the firmware in this packet
> + * @data: pointer to firmware data.
> + */
> +struct firmware_packet {
> +     u32 packet_size;
> +     u32 flash_addr;
> +     u32 packet_checksum;
> +     const u8 *data;
> +};
> +#pragma pack()
> +
> +/**
> + * firmware_data - firmware data structure
> + * @fw_info: firmware infromation
> + * @firmware: firmware data structure
> + */
> +struct firmware_data {
> +     struct firmware_info fw_info;
> +     const struct firmware *firmware;
> +};
> +
> +enum update_status {
> +     UPSTA_NOTWORK = 0,
> +     UPSTA_PREPARING,
> +     UPSTA_UPDATING,
> +     UPSTA_ABORT,
> +     UPSTA_SUCCESS,
> +     UPSTA_FAILED
> +};
> +
> +/**
> + * fw_update_ctrl - structure used to control the
> + *  firmware update process
> + * @status: update status
> + * @progress: indicate the progress of update
> + * @allow_reset: control the reset callback
> + * @allow_irq: control the irq callback
> + * @allow_suspend: control the suspend callback
> + * @allow_resume: allow resume callback
> + * @fw_data: firmware data
> + * @ts_dev: touch device
> + * @fw_name: firmware name
> + * @attr_fwimage: sysfs bin attrs, for storing fw image
> + * @fw_from_sysfs: whether the firmware image is loadind
> + *           from sysfs
> + */
> +struct fw_update_ctrl {
> +     enum update_status status;
> +     unsigned int progress;
> +     bool force_update;
> +
> +     bool allow_reset;
> +     bool allow_irq;
> +     bool allow_suspend;
> +     bool allow_resume;
> +
> +     struct firmware_data fw_data;
> +     struct goodix_ts_device *ts_dev;
> +
> +     char fw_name[FW_NAME_MAX];
> +     struct bin_attribute attr_fwimage;
> +     bool fw_from_sysfs;
> +};
> +
> +static struct goodix_ext_module goodix_fwu_module;
> +/**
> + * goodix_parse_firmware - parse firmware header information
> + *   and subsystem information from firmware data buffer
> + *
> + * @fw_data: firmware struct, contains firmware header info
> + *   and firmware data.
> + * return: 0 - OK, < 0 - error
> + */
> +static int goodix_parse_firmware(struct fw_update_ctrl *fwu_ctrl)
> +{
> +     const struct firmware *firmware;
> +     struct firmware_info *fw_info;
> +     struct firmware_data *fw_data = &fwu_ctrl->fw_data;
> +     const struct device *dev = fwu_ctrl->ts_dev->dev;
> +     unsigned int i, fw_offset, info_offset;
> +     u16 checksum;
> +     int r = 0;
> +
> +     if (!fw_data || !fw_data->firmware) {
> +             dev_err(dev, "Invalid firmware data\n");
> +             return -EINVAL;
> +     }
> +     fw_info = &fw_data->fw_info;
> +
> +     /* copy firmware head info */
> +     firmware = fw_data->firmware;
> +     if (firmware->size < FW_SUBSYS_INFO_OFFSET) {
> +             dev_err(dev, "Invalid firmware size:%zu\n", firmware->size);
> +             r = -EINVAL;
> +             goto err_size;
> +     }
> +     memcpy(fw_info, firmware->data, FW_SUBSYS_INFO_OFFSET);
> +
> +     /* check firmware size */
> +     fw_info->size = be32_to_cpu(fw_info->size);
> +     if (firmware->size != fw_info->size + 6) {
> +             dev_err(dev, "Bad firmware, size not match\n");
> +             r = -EINVAL;
> +             goto err_size;
> +     }
> +
> +     /* calculate checksum, note: sum of bytes, but check by u16 checksum */
> +     for (i = 6, checksum = 0; i < firmware->size; i++)
> +             checksum += firmware->data[i];
> +
> +     /* byte order change, and check */
> +     fw_info->checksum = be16_to_cpu(fw_info->checksum);
> +     if (checksum != fw_info->checksum) {
> +             dev_err(dev, "Bad firmware, cheksum error\n");
> +             r = -EINVAL;
> +             goto err_size;
> +     }
> +
> +     if (fw_info->subsys_num > FW_SUBSYS_MAX_NUM) {
> +             dev_err(dev, "Bad firmware, invalid subsys num\n");
> +             r = -EINVAL;
> +             goto err_size;
> +     }
> +
> +     /* parse subsystem info */
> +     fw_offset = FW_HEADER_SIZE;
> +     for (i = 0; i < fw_info->subsys_num; i++) {
> +             info_offset = FW_SUBSYS_INFO_OFFSET +
> +                                     i * FW_SUBSYS_INFO_SIZE;
> +
> +             fw_info->subsys[i].type = firmware->data[info_offset];
> +             fw_info->subsys[i].size =
> +                     be32_to_cpup((__be32 *)&firmware->data[info_offset + 
> 1]);
> +             fw_info->subsys[i].flash_addr =
> +                     be16_to_cpup((__be16 *)&firmware->data[info_offset + 
> 5]);
> +             fw_info->subsys[i].flash_addr <<= 8; /* important! */
> +
> +             if (fw_offset > firmware->size) {
> +                     dev_err(dev, "Sybsys offset exceed Firmware size\n");
> +                     goto err_size;
> +             }
> +
> +             fw_info->subsys[i].data = firmware->data + fw_offset;
> +             fw_offset += fw_info->subsys[i].size;
> +     }
> +
> +     dev_info(dev, "Firmware package protocol: V%u\n", 
> fw_info->protocol_ver);
> +     dev_info(dev, "Fimware PID:GT%s\n", fw_info->fw_pid);
> +     dev_info(dev, "Fimware VID:%02X%02X%02X\n", fw_info->fw_vid[0],
> +                             fw_info->fw_vid[1], fw_info->fw_vid[2]);
> +     dev_info(dev, "Firmware chip type:%02X\n", fw_info->chip_type);
> +     dev_info(dev, "Firmware size:%u\n", fw_info->size);
> +     dev_info(dev, "Firmware subsystem num:%u\n", fw_info->subsys_num);
> +
> +     for (i = 0; i < fw_info->subsys_num; i++) {
> +             dev_dbg(dev, "Index:%d\n", i);
> +             dev_dbg(dev, "Subsystem type:%02X\n", fw_info->subsys[i].type);
> +             dev_dbg(dev, "Subsystem size:%u\n", fw_info->subsys[i].size);
> +             dev_dbg(dev, "Subsystem flash_addr:%08X\n",
> +                     fw_info->subsys[i].flash_addr);
> +             dev_dbg(dev, "Subsystem Ptr:%p\n", fw_info->subsys[i].data);
> +     }
> +
> +err_size:
> +     return r;
> +}
> +
> +/**
> + * goodix_check_update - compare the version of firmware running in
> + *  touch device with the version getting from the firmware file.
> + * @fw_info: firmware information to be compared
> + * return: 0 firmware in the touch device needs to be updated
> + *                   < 0 no need to update firmware
> + */
> +static int goodix_check_update(struct goodix_ts_device *ts_dev,
> +             const struct firmware_info *fw_info)
> +{
> +     struct goodix_ts_version fw_ver = {0};
> +     const struct device *dev = ts_dev->dev;
> +     u16 fwimg_vid;
> +     u8 fwimg_cid;
> +     int r = 0;
> +
> +     /* read version from chip, if we got invalid firmware version, maybe
> +      * fimware in flash is incorrect, so we need to update firmware
> +      */
> +     r = ts_dev->hw_ops->read_version(ts_dev, &fw_ver);
> +     if (r == -EBUS)
> +             return r;
> +
> +     if (fw_ver.valid) {
> +             if (memcmp(fw_ver.pid, fw_info->fw_pid, 4)) {
> +                     dev_err(dev, "Product ID is not match\n");
> +                     return -EPERM;
> +             }
> +
> +             fwimg_cid = fw_info->fw_vid[0];
> +             fwimg_vid = fw_info->fw_vid[1] << 8 | fw_info->fw_vid[2];
> +             if (fw_ver.vid == fwimg_vid && fw_ver.cid == fwimg_cid) {
> +                     dev_err(dev, "FW version is equal to the IC's\n");
> +                     return -EPERM;
> +             } else if (fw_ver.vid > fwimg_vid) {
> +                     dev_info(dev, "Warning: fw version is lower the 
> IC's\n");
> +             }
> +     } /* else invalid firmware, update firmware */
> +
> +     dev_info(dev, "Firmware needs to be updated\n");
> +     return 0;
> +}
> +
> +/**
> + * goodix_reg_write_confirm - write register and confirm the value
> + *  in the register.
> + * @ts_dev: pointer to touch device
> + * @addr: register address
> + * @data: pointer to data buffer
> + * @len: data length
> + * return: 0 write success and confirm ok
> + *              < 0 failed
> + */
> +static int goodix_reg_write_confirm(struct goodix_ts_device *ts_dev,
> +             unsigned int addr, unsigned char *data, unsigned int len)
> +{
> +     u8 *cfm, cfm_buf[32];
> +     int r, i;
> +
> +     if (len > sizeof(cfm_buf)) {
> +             cfm = kzalloc(len, GFP_KERNEL);
> +             if (!cfm)
> +                     return -ENOMEM;
> +     } else {
> +             cfm = &cfm_buf[0];
> +     }
> +
> +     for (i = 0; i < GOODIX_BUS_RETRY_TIMES; i++) {
> +             r = ts_dev->hw_ops->write(ts_dev, addr, data, len);
> +             if (r < 0)
> +                     goto exit;
> +
> +             r = ts_dev->hw_ops->read(ts_dev, addr, cfm, len);
> +             if (r < 0)
> +                     goto exit;
> +
> +             if (memcmp(data, cfm, len)) {
> +                     r = -EMEMCMP;
> +                     continue;
> +             } else {
> +                     r = 0;
> +                     break;
> +             }
> +     }
> +
> +exit:
> +     if (cfm != &cfm_buf[0])
> +             kfree(cfm);
> +     return r;
> +}
> +
> +static inline int goodix_reg_write(struct goodix_ts_device *ts_dev,
> +             unsigned int addr, unsigned char *data, unsigned int len)
> +{
> +     return ts_dev->hw_ops->write(ts_dev, addr, data, len);
> +}
> +
> +static inline int goodix_reg_read(struct goodix_ts_device *ts_dev,
> +             unsigned int addr, unsigned char *data, unsigned int len)
> +{
> +     return ts_dev->hw_ops->read(ts_dev, addr, data, len);
> +}
> +
> +/**
> + * goodix_cpu_ctrl - Let cpu stay in pending state or running state
> + * @ts_dev: pointer to touch device
> + * @flag: control flag, which can be:
> + *           CPU_CTRL_PENDING - Pending cpu
> + *   Other type of control to cpu is not support.
> + * return: 0 OK, < 0 Failed, -EAGAIN try again
> + */
> +static int goodix_cpu_ctrl(struct goodix_ts_device *ts_dev, int flag)
> +{
> +     const struct device *dev = ts_dev->dev;
> +     u8 ctrl;
> +     int r;
> +
> +     if (flag == CPU_CTRL_PENDING) {
> +             dev_info(dev, "Pending CPU\n");
> +             ctrl = 0x04;
> +     } else if (flag == CPU_CTRL_RUNNING) {
> +             dev_info(dev, "Running CPU\n");
> +             ctrl = 0x00;
> +     } else {
> +             dev_err(dev, "Invalid cpu ctrl flag\n");
> +             return -EPERM;
> +     }
> +
> +     /* Pending Cpu */
> +     r = goodix_reg_write_confirm(ts_dev, HW_REG_CPU_EN, &ctrl, 1);
> +     if (unlikely(r < 0)) {
> +             dev_err(dev, "CPU ctrl failed:%d\n", r);
> +             r = -EAGAIN; /* hw reset and try again */
> +     }
> +
> +     return r;
> +}
> +
> +/**
> + * goodix_isp_wait_stat - waitting ISP state
> + * @ts_dev: pointer to touch device
> + * @state: state to wait
> + * return: 0 - ok, < 0 error, -ETIMEOUT timeout
> + */
> +static int goodix_isp_wait_stat(struct goodix_ts_device *ts_dev, u16 state)
> +{
> +     const struct device *dev = ts_dev->dev;
> +     static u8 last_state;
> +     u8  isp_state;
> +     int i, r, err_cnt = 0;
> +
> +     for (i = 0; i < 200; i++) {
> +             /* read isp state */
> +             r = goodix_reg_read(ts_dev, HW_REG_ISP_STAT,
> +                                     &isp_state, 1);
> +             if (r < 0) {
> +                     dev_err(dev, "Failed to read ISP state\n");
> +                     if (++err_cnt > GOODIX_BUS_RETRY_TIMES)
> +                             return r;
> +                     continue;
> +             }
> +             err_cnt = 0;
> +
> +             if (isp_state != last_state) {
> +                     switch (isp_state) {
> +                     case ISP_STAT_IDLE:
> +                             dev_info(dev, "ISP state: Idle\n");
> +                             break;
> +                     case ISP_STAT_WRITING:
> +                             dev_info(dev, "ISP state: Writing...\n");
> +                             break;
> +                     case ISP_STAT_READY:
> +                             dev_info(dev, "ISP state: Ready to write\n");
> +                             break;
> +                     default:
> +                             dev_err(dev, "ISP state: Unknown\n");
> +                             break;
> +                     }
> +             }
> +
> +             last_state = isp_state;
> +             r = -ETIMEOUT;
> +             if (isp_state == state) {
> +                     r = 0;
> +                     break;
> +             }
> +
> +             usleep_range(5000, 5010);
> +     }
> +
> +     return r;
> +}
> +
> +/**
> + * goodix_isp_flash_done - check whether flash is successful
> + * @ts_dev: pointer to touch device
> + * return: 0 - ok, < 0 error
> + */
> +static int goodix_isp_flash_done(struct goodix_ts_device *ts_dev)
> +{
> +     u8  isp_result;
> +     int r, i;
> +
> +     for (i = 0; i < 2; i++) {
> +             r = goodix_reg_read(ts_dev, HW_REG_ISP_RESULT,
> +                             &isp_result, 1);
> +             if (r < 0) {
> +                     /* bus error */
> +                     break;
> +             } else if (isp_result == ISP_FLASH_SUCCESS) {
> +                     dev_info(ts_dev->dev, "ISP result: OK!\n");
> +                     r = 0;
> +                     break;
> +             } else if (isp_result == ISP_FLASH_ERROR) {
> +                     dev_err(ts_dev->dev, "ISP result: ERROR!\n");
> +                     r = -EAGAIN;
> +             }
> +     }
> +     return r;
> +}
> +
> +/**
> + * goodix_isp_command - communication with ISP.
> + * @cmd: ISP command.
> + * return: 0 ok, <0 error
> + */
> +static int goodix_isp_command(struct goodix_ts_device *ts_dev, u8 cmd)
> +{
> +     switch (cmd) {
> +     case ISP_CMD_PREPARE:
> +             break;
> +     case ISP_CMD_FLASH:
> +             break;
> +     default:
> +             dev_err(ts_dev->dev, "Invalid ISP cmd\n");
> +             return -EINVAL;
> +     }
> +
> +     return goodix_reg_write(ts_dev, HW_REG_ISP_CMD, &cmd, 1);
> +}
> +
> +/**
> + * goodix_load_isp - load ISP program to deivce ram
> + * @ts_dev: pointer to touch device
> + * @fw_data: firmware data
> + * return 0 ok, <0 error
> + */
> +static inline int goodix_load_isp(struct goodix_ts_device *ts_dev,
> +                     struct firmware_data *fw_data)
> +{
> +     struct fw_subsys_info *fw_isp;
> +     int r;
> +
> +     fw_isp = &fw_data->fw_info.subsys[0];
> +
> +     dev_info(ts_dev->dev, "Loading ISP program\n");
> +     r = goodix_reg_write_confirm(ts_dev, HW_REG_ISP_ADDR,
> +                             (u8 *)fw_isp->data, fw_isp->size);
> +     if (r < 0)
> +             dev_err(ts_dev->dev, "Loading ISP error\n");
> +
> +     return r;
> +}
> +
> +/**
> + * goodix_enter_update - update prepare, loading ISP program
> + *  and make sure the ISP is running.
> + * @fwu_ctrl: pointer to fimrware control structure
> + * return: 0 ok, <0 error
> + */
> +static int goodix_update_prepare(struct fw_update_ctrl *fwu_ctrl)
> +{
> +     struct goodix_ts_device *ts_dev = fwu_ctrl->ts_dev;
> +     const struct device *dev = fwu_ctrl->ts_dev->dev;
> +     u8 boot_val0[4] = {0xb8, 0x3f, 0x35, 0x56};
> +     u8 boot_val1[4] = {0xb9, 0x3e, 0xb5, 0x54};
> +     u8 reg_val[4] = {0x00};
> +     int r;
> +
> +     fwu_ctrl->allow_reset = true;
> +     ts_dev->hw_ops->reset(ts_dev);
> +     fwu_ctrl->allow_reset = false;
> +
> +     /* enable ILM access */
> +     reg_val[0] = 0x06;
> +     r = goodix_reg_write_confirm(ts_dev, HW_REG_ILM_ACCESS,
> +                     reg_val, 1);
> +     if (r < 0) {
> +             dev_err(dev, "Failed to enable ILM access\n");
> +             return r;
> +     }
> +
> +     /* Pending CPU */
> +     r = goodix_cpu_ctrl(ts_dev, CPU_CTRL_PENDING);
> +     if (r < 0)
> +             return r;
> +
> +     /* disable watchdog timer */
> +     reg_val[0] = 0x00;
> +     r = goodix_reg_write_confirm(ts_dev, HW_REG_WDT,
> +                     reg_val, 1);
> +     if (r < 0) {
> +             dev_err(dev, "Failed to disable watchdog\n");
> +             return r;
> +     }
> +
> +     /* select bank 2 */
> +     reg_val[0] = 0x02;
> +     r = goodix_reg_write_confirm(ts_dev, HW_REG_BANK_SELECT,
> +                     reg_val, 1);
> +     if (r < 0) {
> +             dev_err(dev, "Failed to select bank2\n");
> +             return r;
> +     }
> +
> +     /* load ISP code */
> +     r = goodix_load_isp(ts_dev, &fwu_ctrl->fw_data);
> +     if (r < 0)
> +             return r;
> +
> +     /* Clear ISP state */
> +     reg_val[0] = reg_val[1] = 0x00;
> +     r = goodix_reg_write_confirm(ts_dev, HW_REG_ISP_STAT,
> +                     reg_val, 2);
> +     if (r < 0) {
> +             dev_err(dev, "Failed to clear ISP state\n");
> +             return r;
> +     }
> +
> +     /* set boot flag */
> +     reg_val[0] = 0;
> +     r = goodix_reg_write_confirm(ts_dev, HW_REG_BOOT_FLAG,
> +                     reg_val, 1);
> +     if (r < 0) {
> +             dev_err(dev, "Failed to set boot flag\n");
> +             return r;
> +     }
> +
> +     /* set boot from sRam */
> +     r = goodix_reg_write_confirm(ts_dev, HW_REG_BOOT_CTRL0,
> +                     boot_val0, sizeof(boot_val0));
> +     if (r < 0) {
> +             dev_err(dev, "Failed to set boot flag\n");
> +             return r;
> +     }
> +
> +     /* set boot from sRam */
> +     r = goodix_reg_write_confirm(ts_dev, HW_REG_BOOT_CTRL1,
> +                     boot_val1, sizeof(boot_val1));
> +     if (r < 0) {
> +             dev_err(dev, "Failed to set boot flag\n");
> +             return r;
> +     }
> +
> +     /* disbale ILM access */
> +     reg_val[0] = 0x00;
> +     r = goodix_reg_write_confirm(ts_dev, HW_REG_ILM_ACCESS,
> +                     reg_val, 1);
> +     if (r < 0) {
> +             dev_err(dev, "Failed to disable ILM access\n");
> +             return r;
> +     }
> +
> +     /* Release CPU */
> +     r = goodix_cpu_ctrl(ts_dev, CPU_CTRL_RUNNING);
> +     if (r < 0)
> +             return r;
> +
> +     /* wait isp idel */
> +     r = goodix_isp_wait_stat(ts_dev, ISP_STAT_IDLE);
> +     if (r < 0) {
> +             dev_err(dev, "Wait ISP IDLE timeout\n");
> +             return r;
> +     }
> +
> +     return r;
> +}
> +
> +/**
> + * goodix_write_fwdata - write firmware data to ISP buffer
> + * @ts_dev: pointer to touch device
> + * @fw_data: firmware data
> + * @size: size of data, size can not exceed ISP_MAX_BUFFERSIZE
> + *  + checksum size{2},
> + * return: 0 ok, <0 error
> + */
> +static int goodix_write_fwdata(struct goodix_ts_device *ts_dev,
> +                     const u8 *fw_data, u32 size)
> +{
> +     if (!fw_data || size > ISP_MAX_BUFFERSIZE)
> +             return -EINVAL;
> +
> +     return  goodix_reg_write(ts_dev, HW_REG_ISP_BUFFER,
> +                             (u8 *)fw_data, size);
> +}
> +
> +/**
> + * goodix_format_fw_packet - formate one flash packet
> + * @pkt: target firmware packet
> + * @flash_addr: flash address
> + * @size: packet size
> + * @data: packet data
> + */
> +static int goodix_format_fw_packet(struct firmware_packet *pkt,
> +     u32 flash_addr, u32 size, const u8 *data)
> +{
> +     if (!pkt || !data || size % 4)
> +             return -EINVAL;
> +
> +     /*
> +      * checksum rule:sum of data in one format is equal to zero
> +      * data format: byte/le16/be16/le32/be32/le64/be64
> +      */
> +     pkt->flash_addr = cpu_to_le32(flash_addr);
> +     pkt->packet_size = cpu_to_le32(size);
> +     pkt->packet_checksum = checksum_le32((u8 *)data, size);
> +     pkt->data = data;
> +     return 0;
> +}
> +
> +/**
> + * goodix_send_fw_packet - send one firmware packet to ISP
> + * @ts_dev: target touch device
> + * @pkt: firmware packet
> + * return:0 ok, <0 error
> + */
> +static int goodix_send_fw_packet(struct goodix_ts_device *ts_dev,
> +             struct firmware_packet *pkt)
> +{
> +     u8 pkt_info[12];
> +     int r;
> +
> +     if (!pkt)
> +             return -EINVAL;
> +
> +     /* 1: wait ISP idle */
> +     r = goodix_isp_wait_stat(ts_dev, ISP_STAT_IDLE);
> +     if (r < 0)
> +             return r;
> +
> +     /* 2: write packet information */
> +     memcpy(pkt_info, pkt, sizeof(pkt_info));
> +     r = goodix_reg_write(ts_dev, HW_REG_ISP_PKT_INFO,
> +                     pkt_info, sizeof(pkt_info));
> +     if (r < 0) {
> +             dev_err(ts_dev->dev, "Failed to write packet info\n");
> +             return r;
> +     }
> +
> +     /* 3: Make ISP ready to flash */
> +     r = goodix_isp_command(ts_dev, ISP_CMD_PREPARE);
> +     if (r  < 0) {
> +             dev_err(ts_dev->dev, "Failed to make ISP ready\n");
> +             return r;
> +     }
> +
> +     /* 4: write packet data(firmware block) to ISP buffer */
> +     r = goodix_write_fwdata(ts_dev, pkt->data, pkt->packet_size);
> +     if (r < 0) {
> +             dev_err(ts_dev->dev, "Failed to write firmware packet\n");
> +             return r;
> +     }
> +
> +     /* 5: wait ISP ready */
> +     r = goodix_isp_wait_stat(ts_dev, ISP_STAT_READY);
> +     if (r < 0) {
> +             dev_err(ts_dev->dev, "Failed to wait ISP ready\n");
> +             return r;
> +     }
> +
> +     /* 6: start writting to flash */
> +     r = goodix_isp_command(ts_dev, ISP_CMD_FLASH);
> +     if (r < 0) {
> +             dev_err(ts_dev->dev, "Failed to start flash\n");
> +             return r;
> +     }
> +
> +     /* 7: wait idle */
> +     r = goodix_isp_wait_stat(ts_dev, ISP_STAT_IDLE);
> +     if (r < 0) {
> +             dev_err(ts_dev->dev, "Error occurred when wait ISP idle\n");
> +             return r;
> +     };
> +
> +     /* check ISP result */
> +     r = goodix_isp_flash_done(ts_dev);
> +     if (r < 0) {
> +             dev_err(ts_dev->dev, "Flash fw packet failed:%d\n", r);
> +             return r;
> +     }
> +
> +     return 0;
> +}
> +
> +/**
> + * goodix_flash_subsystem - flash subsystem firmware,
> + *  Main flow of flashing firmware.
> + *   Each firmware subsystem is divided into several
> + *   packets, the max size of packet is limited to
> + *   @{ISP_MAX_BUFFERSIZE}
> + * @ts_dev: pointer to touch device
> + * @subsys: subsystem information
> + * return: 0 ok, < 0 error
> + */
> +static int goodix_flash_subsystem(struct goodix_ts_device *ts_dev,
> +                             struct fw_subsys_info *subsys)
> +{
> +     struct firmware_packet fw_pkt;
> +     u32 data_size, total_size, offset;
> +     int r = 0;
> +
> +     /*
> +      * if bus(i2c/spi) error occued, then exit, we will do
> +      * hardware reset and re-prepare ISP and then retry
> +      * flashing
> +      */
> +     total_size = subsys->size;
> +     offset = 0;
> +     while (total_size > 0) {
> +             data_size = total_size > ISP_MAX_BUFFERSIZE ?
> +                             ISP_MAX_BUFFERSIZE : total_size;
> +             dev_info(ts_dev->dev, "Flash firmware to %08x,size:%u bytes\n",
> +                             subsys->flash_addr + offset, data_size);
> +
> +             /* format one firmware packet */
> +             r = goodix_format_fw_packet(&fw_pkt, subsys->flash_addr + 
> offset,
> +                             data_size, &subsys->data[offset]);
> +             if (r < 0) {
> +                     dev_err(ts_dev->dev, "Invalid packet params\n");
> +                     goto exit;
> +             }
> +
> +             /* send one firmware packet */
> +             r = goodix_send_fw_packet(ts_dev, &fw_pkt);
> +             if (r < 0) {
> +                     dev_err(ts_dev->dev,
> +                             "Failed to send firmware packet,err:%d\n", r);
> +                     goto exit;
> +             }
> +
> +             offset += data_size;
> +             total_size -= data_size;
> +     } /* end while */
> +
> +exit:
> +     return r;
> +}
> +
> +/**
> + * goodix_flash_firmware - flash firmware
> + * @ts_dev: pointer to touch device
> + * @fw_data: firmware data
> + * return: 0 ok, < 0 error
> + */
> +static int goodix_flash_firmware(struct goodix_ts_device *ts_dev,
> +                             struct firmware_data *fw_data)
> +{
> +     struct fw_update_ctrl *fw_ctrl;
> +     struct firmware_info  *fw_info;
> +     struct fw_subsys_info *fw_x;
> +     int retry = GOODIX_BUS_RETRY_TIMES;
> +     int i, r = 0, fw_num, prog_step;
> +
> +     /* start from subsystem 1, subsystem 0 is the ISP program */
> +     fw_ctrl = container_of(fw_data, struct fw_update_ctrl, fw_data);
> +     fw_info = &fw_data->fw_info;
> +     fw_num = fw_info->subsys_num;
> +
> +     /* we have 80% work here */
> +     prog_step = 80 / (fw_num - 1);
> +
> +     for (i = 1; i < fw_num && retry;) {
> +             dev_info(ts_dev->dev,
> +                     "--- Start to flash subsystem[%d] ---", i);
> +             fw_x = &fw_info->subsys[i];
> +             r = goodix_flash_subsystem(ts_dev, fw_x);
> +             if (r == 0) {
> +                     dev_info(ts_dev->dev,
> +                             "--- End flash subsystem[%d]: OK ---", i);
> +                     fw_ctrl->progress += prog_step;
> +                     i++;
> +             } else if (r == -EAGAIN) {
> +                     retry--;
> +                     dev_err(ts_dev->dev,
> +                             "--- End flash subsystem%d: Fail, errno:%d, 
> retry:%d ---",
> +                             i, r, GOODIX_BUS_RETRY_TIMES - retry);
> +             } else if (r < 0) { /* bus error */
> +                     dev_err(ts_dev->dev,
> +                             "--- End flash subsystem%d: Fatal error:%d exit 
> ---",
> +                             i, r);
> +                     goto exit_flash;
> +             }
> +     }
> +
> +exit_flash:
> +     return r;
> +}
> +
> +/**
> + * goodix_update_finish - update finished, free resource
> + *  and reset flags---
> + * @fwu_ctrl: pointer to fw_update_ctrl structrue
> + * return: 0 ok, < 0 error
> + */
> +static int goodix_update_finish(struct fw_update_ctrl *fwu_ctrl)
> +{
> +     struct goodix_ts_version ver;
> +     int r = 0;
> +
> +     fwu_ctrl->ts_dev->hw_ops->reset(fwu_ctrl->ts_dev);
> +     r = fwu_ctrl->ts_dev->hw_ops->read_version(fwu_ctrl->ts_dev, &ver);
> +     return r;
> +}
> +
> +/**
> + * goodix_fw_update_proc - firmware update process, the entry of
> + *  firmware update flow
> + * @fwu_ctrl: firmware control
> + * return: 0 ok, < 0 error
> + */
> +int goodix_fw_update_proc(struct fw_update_ctrl *fwu_ctrl)
> +{
> +#define FW_UPDATE_RETRY      2
> +     const struct device *dev = fwu_ctrl->ts_dev->dev;
> +     int retry0 = FW_UPDATE_RETRY, retry1 = FW_UPDATE_RETRY;
> +     int r = 0;
> +
> +     if (fwu_ctrl->status == UPSTA_PREPARING ||
> +                     fwu_ctrl->status == UPSTA_UPDATING) {
> +             dev_err(dev, "Firmware update already in progress\n");
> +             return -EBUSY;
> +     }
> +     fwu_ctrl->progress = 0;
> +     fwu_ctrl->status = UPSTA_PREPARING;
> +     r = goodix_parse_firmware(fwu_ctrl);
> +     if (r < 0) {
> +             fwu_ctrl->status = UPSTA_ABORT;
> +             goto err_parse_fw;
> +     }
> +     fwu_ctrl->progress = 10;
> +     if (fwu_ctrl->force_update == false) {
> +             r = goodix_check_update(fwu_ctrl->ts_dev,
> +                             &fwu_ctrl->fw_data.fw_info);
> +             if (r < 0) {
> +                     fwu_ctrl->status = UPSTA_ABORT;
> +                     goto err_check_update;
> +             }
> +     }
> +start_update:
> +     fwu_ctrl->progress = 20;
> +     fwu_ctrl->status = UPSTA_UPDATING; /* show upgrading status */
> +     r = goodix_update_prepare(fwu_ctrl);
> +     if ((r == -EBUS || r == -EAGAIN) && --retry0 > 0) {
> +             dev_err(dev, "Bus error, retry prepare ISP:%d\n",
> +                             FW_UPDATE_RETRY - retry0);
> +             goto start_update;
> +     } else if (r < 0) {
> +             dev_err(dev, "Failed to prepare ISP, exit update:%d\n", r);
> +             fwu_ctrl->status = UPSTA_FAILED;
> +             goto err_fw_prepare;
> +     }
> +     /* progress: 20%~100% */
> +     r = goodix_flash_firmware(fwu_ctrl->ts_dev, &fwu_ctrl->fw_data);
> +     if ((r == -EBUS || r == -ETIMEOUT) && --retry1 > 0) {
> +             /* we will retry[twice] if returns bus error[i2c/spi]
> +              * we will do hardware reset and re-prepare ISP and then retry
> +              * flashing
> +              */
> +             dev_err(dev, "Bus error, retry firmware update:%d\n",
> +                             FW_UPDATE_RETRY - retry1);
> +             goto start_update;
> +     } else if (r < 0) {
> +             dev_err(dev, "Fatal error, exit update:%d\n", r);
> +             fwu_ctrl->status = UPSTA_FAILED;
> +             goto err_fw_flash;
> +     }
> +     fwu_ctrl->status = UPSTA_SUCCESS;
> +err_fw_flash:
> +err_fw_prepare:
> +     goodix_update_finish(fwu_ctrl);
> +err_check_update:
> +err_parse_fw:
> +     if (fwu_ctrl->status == UPSTA_SUCCESS)
> +             dev_info(dev, "Firmware update successfully\n");
> +     else if (fwu_ctrl->status == UPSTA_FAILED)
> +             dev_err(dev, "Firmware update failed\n");
> +     fwu_ctrl->progress = 100; /* 100% */
> +     return r;
> +}
> +/* COMMON PART - END */
> +
> +/**
> + * goodix_request_firmware - request firmware data from user space
> + *
> + * @fw_data: firmware struct, contains firmware header info
> + *   and firmware data pointer.
> + * return: 0 - OK, < 0 - error
> + */
> +static int goodix_request_firmware(struct firmware_data *fw_data,
> +                             const char *name)
> +{
> +     struct fw_update_ctrl *fw_ctrl =
> +             container_of(fw_data, struct fw_update_ctrl, fw_data);
> +     struct device *dev = fw_ctrl->ts_dev->dev;
> +     int r;
> +
> +     r = request_firmware(&fw_data->firmware, name, dev);
> +     if (r < 0)
> +             dev_err(dev,
> +                     "Firmware image [%s] not available,errno:%d\n", name, 
> r);
> +     else
> +             dev_info(dev, "Firmware image [%s] is ready\n", name);
> +     return r;
> +}
> +
> +/**
> + * relase firmware resources
> + *
> + */
> +static inline void goodix_release_firmware(struct firmware_data *fw_data)
> +{
> +     if (fw_data->firmware) {
> +             release_firmware(fw_data->firmware);
> +             fw_data->firmware = NULL;
> +     }
> +}
> +
> +static int goodix_fw_update_thread(void *data)
> +{
> +     struct fw_update_ctrl *fwu_ctrl = data;
> +     static DEFINE_MUTEX(fwu_lock);
> +     int r = -EINVAL;
> +
> +     if (!fwu_ctrl)
> +             return r;
> +
> +     if (goodix_register_ext_module(&goodix_fwu_module))
> +             return -EIO;
> +
> +     mutex_lock(&fwu_lock);
> +     /* judge where to get firmware data */
> +     if (!fwu_ctrl->fw_from_sysfs) {
> +             r = goodix_request_firmware(&fwu_ctrl->fw_data,
> +                             fwu_ctrl->fw_name);
> +             if (r < 0) {
> +                     fwu_ctrl->status = UPSTA_ABORT;
> +                     fwu_ctrl->progress = 100;
> +                     goto out;
> +             }
> +     } else {
> +             if (!fwu_ctrl->fw_data.firmware) {
> +                     fwu_ctrl->status = UPSTA_ABORT;
> +                     fwu_ctrl->progress = 100;
> +                     r = -EINVAL;
> +                     goto out;
> +             }
> +     }
> +
> +     /* DONT allow reset/irq/suspend/resume during update */
> +     fwu_ctrl->allow_irq = false;
> +     fwu_ctrl->allow_suspend = false;
> +     fwu_ctrl->allow_resume = false;
> +     goodix_ts_blocking_notify(NOTIFY_FWUPDATE_START, NULL);
> +
> +     /* ready to update */
> +     r = goodix_fw_update_proc(fwu_ctrl);
> +
> +     goodix_ts_blocking_notify(NOTIFY_FWUPDATE_END, NULL);
> +     fwu_ctrl->allow_reset = true;
> +     fwu_ctrl->allow_irq = true;
> +     fwu_ctrl->allow_suspend = true;
> +     fwu_ctrl->allow_resume = true;
> +
> +     /* clean */
> +     if (!fwu_ctrl->fw_from_sysfs) {
> +             goodix_release_firmware(&fwu_ctrl->fw_data);
> +     } else {
> +             fwu_ctrl->fw_from_sysfs = false;
> +             vfree(fwu_ctrl->fw_data.firmware);
> +             fwu_ctrl->fw_data.firmware = NULL;
> +     }
> +
> +out:
> +     goodix_unregister_ext_module(&goodix_fwu_module);
> +     mutex_unlock(&fwu_lock);
> +     return r;
> +}
> +
> +/* sysfs attributes */
> +static ssize_t goodix_sysfs_update_fw_store(
> +             struct goodix_ext_module *module,
> +             const char *buf, size_t count)
> +{
> +     int ret;
> +
> +     ret = goodix_fw_update_thread(module->priv_data);
> +     if (ret)
> +             count = ret;
> +
> +     return count;
> +}
> +
> +static ssize_t goodix_sysfs_update_progress_show(
> +             struct goodix_ext_module *module,
> +             char *buf)
> +{
> +     struct fw_update_ctrl *fw_ctrl = module->priv_data;
> +
> +     return scnprintf(buf, PAGE_SIZE, "%d\n", fw_ctrl->progress);
> +}
> +
> +static ssize_t goodix_sysfs_update_result_show(
> +             struct goodix_ext_module *module,
> +             char *buf)
> +{
> +     char *result = NULL;
> +     struct fw_update_ctrl *fw_ctrl = module->priv_data;
> +
> +     switch (fw_ctrl->status) {
> +     case UPSTA_NOTWORK:
> +             result = "notwork";
> +             break;
> +     case UPSTA_PREPARING:
> +             result = "preparing";
> +             break;
> +     case UPSTA_UPDATING:
> +             result = "upgrading";
> +             break;
> +     case UPSTA_ABORT:
> +             result = "abort";
> +             break;
> +     case UPSTA_SUCCESS:
> +             result = "success";
> +             break;
> +     case UPSTA_FAILED:
> +             result = "failed";
> +             break;
> +     default:
> +             break;
> +     }
> +
> +     return scnprintf(buf, PAGE_SIZE, "%s\n", result);
> +}
> +
> +static ssize_t goodix_sysfs_update_fwversion_show(
> +             struct goodix_ext_module *module,
> +             char *buf)
> +{
> +     struct goodix_ts_version fw_ver;
> +     struct fw_update_ctrl *fw_ctrl = module->priv_data;
> +     int r = 0;
> +     char str[5];
> +
> +     /* read version from chip */
> +     r = fw_ctrl->ts_dev->hw_ops->read_version(fw_ctrl->ts_dev,
> +                     &fw_ver);
> +     if (!r) {
> +             memcpy(str, fw_ver.pid, 4);
> +             str[4] = '\0';
> +             return scnprintf(buf, PAGE_SIZE, "PID:%s VID:%04x 
> SENSOR_ID:%d\n",
> +                             str, fw_ver.vid, fw_ver.sensor_id);
> +     }
> +     return 0;
> +}
> +
> +static ssize_t goodix_sysfs_fwsize_show(struct goodix_ext_module *module,
> +             char *buf)
> +{
> +     struct fw_update_ctrl *fw_ctrl = module->priv_data;
> +     int r = -EINVAL;
> +
> +     if (fw_ctrl && fw_ctrl->fw_data.firmware)
> +             r = snprintf(buf, PAGE_SIZE, "%zu\n",
> +                             fw_ctrl->fw_data.firmware->size);
> +     return r;
> +}
> +
> +static ssize_t goodix_sysfs_fwsize_store(struct goodix_ext_module *module,
> +             const char *buf, size_t count)
> +{
> +     struct fw_update_ctrl *fw_ctrl = module->priv_data;
> +     struct firmware *fw;
> +     u8 **data;
> +     size_t size = 0;
> +
> +     if (!fw_ctrl)
> +             return -EINVAL;
> +
> +     if (sscanf(buf, "%zu", &size) < 0 || !size) {
> +             dev_err(fw_ctrl->ts_dev->dev, "Failed to get fwsize");
> +             return -EFAULT;
> +     }
> +
> +     fw = vmalloc(sizeof(*fw) + size);
> +     if (!fw)
> +             return -ENOMEM;
> +
> +     memset(fw, 0x00, sizeof(*fw) + size);
> +     data = (u8 **)&fw->data;
> +     *data = (u8 *)fw + sizeof(struct firmware);
> +     fw->size = size;
> +     fw_ctrl->fw_data.firmware = fw;
> +     fw_ctrl->fw_from_sysfs = true;
> +
> +     return count;
> +}
> +
> +static ssize_t goodix_sysfs_fwimage_store(struct file *file,
> +             struct kobject *kobj, struct bin_attribute *attr,
> +             char *buf, loff_t pos, size_t count)
> +{
> +     struct fw_update_ctrl *fw_ctrl;
> +     struct firmware_data *fw_data;
> +
> +     fw_ctrl = container_of(attr, struct fw_update_ctrl,
> +                     attr_fwimage);
> +     fw_data = &fw_ctrl->fw_data;
> +
> +     if (!fw_data->firmware) {
> +             dev_err(fw_ctrl->ts_dev->dev, "Need set fw image size first");
> +             return -ENOMEM;
> +     }
> +
> +     if (fw_data->firmware->size == 0) {
> +             dev_err(fw_ctrl->ts_dev->dev, "Invalid firmware size");
> +             return -EINVAL;
> +     }
> +
> +     if (pos + count > fw_data->firmware->size)
> +             return -EFAULT;
> +
> +     memcpy((u8 *)&fw_data->firmware->data[pos], buf, count);
> +     fw_ctrl->force_update = true;
> +
> +     return count;
> +}
> +
> +static ssize_t goodix_sysfs_force_update_store(
> +             struct goodix_ext_module *module,
> +             const char *buf, size_t count)
> +{
> +     struct fw_update_ctrl *fw_ctrl = module->priv_data;
> +     int val;
> +
> +     if (kstrtoint(buf, 10, &val))
> +             return -EINVAL;
> +
> +     if (val)
> +             fw_ctrl->force_update = true;
> +     else
> +             fw_ctrl->force_update = false;
> +
> +     return count;
> +}
> +
> +
> +static ssize_t goodix_sysfs_update_hwversion_show(
> +             struct goodix_ext_module *module,
> +             char *buf)
> +{
> +     struct goodix_ts_version fw_ver;
> +     struct fw_update_ctrl *fw_ctrl = module->priv_data;
> +     int r = 0;
> +     char str[5];
> +
> +     /* read version from chip */
> +     r = fw_ctrl->ts_dev->hw_ops->read_version(fw_ctrl->ts_dev,
> +                     &fw_ver);
> +     if (!r) {
> +             memcpy(str, fw_ver.pid, 4);
> +             str[4] = '\0';
> +             return scnprintf(buf, PAGE_SIZE, "%s\n", str);
> +     }
> +     return 0;
> +}
> +
> +static ssize_t goodix_sysfs_update_fw_version_show(
> +             struct goodix_ext_module *module,
> +             char *buf)
> +{
> +     struct goodix_ts_version fw_ver;
> +     struct fw_update_ctrl *fw_ctrl = module->priv_data;
> +     int r = 0;
> +
> +     /* read version from chip */
> +     r = fw_ctrl->ts_dev->hw_ops->read_version(fw_ctrl->ts_dev,
> +                     &fw_ver);
> +     if (!r) {
> +             /* firmversion major+minor store formate is 2byte compress BCD 
> */
> +             return scnprintf(buf, PAGE_SIZE, "%2x.%2x\n",
> +                             fw_ver.vid >> 8, fw_ver.vid & 0xff);
> +
> +     }
> +     return 0;
> +}
> +
> +static ssize_t goodix_sysfs_fw_name_store(
> +             struct goodix_ext_module *module,
> +             const char *buf, size_t count)
> +{
> +     struct fw_update_ctrl *fwu_ctrl;
> +
> +     if (!module || !module->priv_data)
> +             return -ENOMEM;
> +
> +     fwu_ctrl = module->priv_data;
> +     if (count > FW_NAME_MAX) {
> +             dev_err(fwu_ctrl->ts_dev->dev, "Firmware name too long");
> +             return -EINVAL;
> +     }
> +     memset(fwu_ctrl->fw_name, 0, FW_NAME_MAX);
> +     memcpy(fwu_ctrl->fw_name, buf, count);
> +
> +     return count;
> +}
> +
> +static struct goodix_ext_attribute goodix_fwu_attrs[] = {
> +     __EXTMOD_ATTR(progress, 0444, goodix_sysfs_update_progress_show, NULL),
> +     __EXTMOD_ATTR(result, 0444, goodix_sysfs_update_result_show, NULL),
> +     __EXTMOD_ATTR(fwversion, 0444, goodix_sysfs_update_fwversion_show, 
> NULL),
> +     __EXTMOD_ATTR(fwsize, 0644, goodix_sysfs_fwsize_show,
> +                     goodix_sysfs_fwsize_store),
> +     __EXTMOD_ATTR(force_update, 0200, NULL, 
> goodix_sysfs_force_update_store),
> +     __EXTMOD_ATTR(update_fw, 0200, NULL, goodix_sysfs_update_fw_store),
> +     __EXTMOD_ATTR(fw_version, 0444, goodix_sysfs_update_fw_version_show, 
> NULL),
> +     __EXTMOD_ATTR(fw_name, 0200, NULL, goodix_sysfs_fw_name_store),
> +     __EXTMOD_ATTR(hw_version, 0444, goodix_sysfs_update_hwversion_show, 
> NULL),
> +};
> +
> +static int goodix_syfs_init(struct goodix_ts_core *core_data,
> +             struct goodix_ext_module *module)
> +{
> +     struct fw_update_ctrl *fw_ctrl = module->priv_data;
> +     const struct device *dev = &core_data->pdev->dev;
> +     struct kobj_type *ktype;
> +     int ret = 0, i;
> +
> +     ktype = goodix_get_default_ktype();
> +     ret = kobject_init_and_add(&module->kobj,
> +                     ktype,
> +                     &core_data->pdev->dev.kobj,
> +                     "fwupdate");
> +     if (ret) {
> +             dev_err(dev, "Create fwupdate sysfs node error!\n");
> +             goto exit_sysfs_init;
> +     }
> +
> +     for (i = 0; i < ARRAY_SIZE(goodix_fwu_attrs); i++) {
> +             if (sysfs_create_file(&module->kobj,
> +                             &goodix_fwu_attrs[i].attr)) {
> +                     dev_warn(dev, "Create sysfs attr file error\n");
> +                     kobject_put(&module->kobj);
> +                     ret = -EINVAL;
> +                     goto exit_sysfs_init;
> +             }
> +     }
> +
> +     fw_ctrl->attr_fwimage.attr.name = "fwimage";
> +     fw_ctrl->attr_fwimage.attr.mode = 0200;
> +     fw_ctrl->attr_fwimage.size = 0;
> +     fw_ctrl->attr_fwimage.write = goodix_sysfs_fwimage_store;
> +     ret = sysfs_create_bin_file(&module->kobj,
> +                     &fw_ctrl->attr_fwimage);
> +
> +exit_sysfs_init:
> +     return ret;
> +}
> +
> +static int goodix_fw_update_init(struct goodix_ts_core *core_data,
> +                             struct goodix_ext_module *module)
> +{
> +     struct goodix_ts_board_data *ts_bdata = board_data(core_data);
> +     struct fw_update_ctrl *fwu_ctrl;
> +     static bool init_sysfs = true;
> +
> +     if (!core_data->ts_dev)
> +             return -ENODEV;
> +
> +     if (!module->priv_data) {
> +             module->priv_data = kzalloc(sizeof(struct fw_update_ctrl),
> +                                                     GFP_KERNEL);
> +             if (!module->priv_data)
> +                     return -ENOMEM;
> +     }
> +     fwu_ctrl = module->priv_data;
> +     fwu_ctrl->ts_dev = core_data->ts_dev;
> +
> +     /* find a valid firmware image name */
> +     if (strlen(fwu_ctrl->fw_name) == 0) {
> +             if (ts_bdata && ts_bdata->fw_name)
> +                     strlcpy(fwu_ctrl->fw_name, ts_bdata->fw_name,
> +                                     sizeof(fwu_ctrl->fw_name));
> +             else
> +                     strlcpy(fwu_ctrl->fw_name, TS_DEFAULT_FIRMWARE,
> +                                     sizeof(fwu_ctrl->fw_name));
> +     }
> +
> +     /* create sysfs interface */
> +     if (init_sysfs) {
> +             if (!goodix_syfs_init(core_data, module))
> +                     init_sysfs = false;
> +     }
> +
> +     return 0;
> +}
> +
> +static int goodix_fw_update_exit(struct goodix_ts_core *core_data,
> +                             struct goodix_ext_module *module)
> +{
> +     return 0;
> +}
> +
> +static int goodix_fw_before_suspend(struct goodix_ts_core *core_data,
> +                             struct goodix_ext_module *module)
> +{
> +     struct fw_update_ctrl *fwu_ctrl = module->priv_data;
> +
> +     return fwu_ctrl->allow_suspend ?
> +                             EVT_HANDLED : EVT_CANCEL_SUSPEND;
> +}
> +
> +static int goodix_fw_before_resume(struct goodix_ts_core *core_data,
> +                             struct goodix_ext_module *module)
> +{
> +     struct fw_update_ctrl *fwu_ctrl = module->priv_data;
> +
> +     return fwu_ctrl->allow_resume ?
> +                             EVT_HANDLED : EVT_CANCEL_RESUME;
> +}
> +
> +static int goodix_fw_irq_event(struct goodix_ts_core *core_data,
> +                             struct goodix_ext_module *module)
> +{
> +     struct fw_update_ctrl *fwu_ctrl = module->priv_data;
> +
> +     return fwu_ctrl->allow_irq ?
> +                             EVT_HANDLED : EVT_CANCEL_IRQEVT;
> +}
> +
> +static int goodix_fw_before_reset(struct goodix_ts_core *core_data,
> +                             struct goodix_ext_module *module)
> +{
> +     struct fw_update_ctrl *fwu_ctrl = module->priv_data;
> +
> +     return fwu_ctrl->allow_reset ?
> +                             EVT_HANDLED : EVT_CANCEL_RESET;
> +}
> +
> +static const struct goodix_ext_module_funcs goodix_ext_funcs = {
> +     .init = goodix_fw_update_init,
> +     .exit = goodix_fw_update_exit,
> +     .before_reset = goodix_fw_before_reset,
> +     .after_reset = NULL,
> +     .before_suspend = goodix_fw_before_suspend,
> +     .after_suspend = NULL,
> +     .before_resume = goodix_fw_before_resume,
> +     .after_resume = NULL,
> +     .irq_event = goodix_fw_irq_event,
> +};
> +
> +static struct goodix_ext_module goodix_fwu_module = {
> +     .name = "goodix-fwu",
> +     .funcs = &goodix_ext_funcs,
> +     .priority = EXTMOD_PRIO_FWUPDATE,
> +};
> +
> +static int __init goodix_fwu_module_init(void)
> +{
> +     return goodix_register_ext_module(&goodix_fwu_module);
> +}
> +
> +static void __exit goodix_fwu_module_exit(void)
> +{
> +}
> +
> +module_init(goodix_fwu_module_init);
> +module_exit(goodix_fwu_module_exit);
> +
> +MODULE_DESCRIPTION("Goodix FWU Module");
> +MODULE_AUTHOR("Goodix, Inc.");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/input/touchscreen/goodix-ts-sunrise/goodix_ts_core.c 
> b/drivers/input/touchscreen/goodix-ts-sunrise/goodix_ts_core.c
> new file mode 100755
> index 0000000..f562b36
> --- /dev/null
> +++ b/drivers/input/touchscreen/goodix-ts-sunrise/goodix_ts_core.c
> @@ -0,0 +1,1366 @@
> + /*
> +  * Goodix GTx5 Touchscreen Driver
> +  * Core layer of touchdriver architecture.
> +  *
> +  * Copyright (C) 2015 - 2016 Goodix, Inc.
> +  * Authors:  Wang Yafei <wangya...@goodix.com>
> +  *
> +  * This program is free software; you can redistribute it and/or modify
> +  * it under the terms of the GNU General Public License as published by
> +  * the Free Software Foundation; either version 2 of the License, or
> +  * (at your option) any later version.
> +  *
> +  * This program is distributed in the hope that it will be a reference
> +  * to you, when you are integrating the GOODiX's CTP IC into your system,
> +  * 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.
> +  *
> +  */
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/of_platform.h>
> +#include <linux/completion.h>
> +#include <linux/debugfs.h>
> +#include <linux/of_irq.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/input/mt.h>
> +#include "goodix_ts_core.h"
> +
> +#define INPUT_TYPE_B_PROTOCOL
> +
> +#define GOOIDX_INPUT_PHYS    "goodix_ts/input0"
> +#define PINCTRL_STATE_ACTIVE    "pmx_ts_active"
> +#define PINCTRL_STATE_SUSPEND   "pmx_ts_suspend"
> +
> +/*
> + * struct goodix_modules - external modules container
> + * @head: external modules list
> + * @initilized: whether this struct is initilized
> + * @mutex: mutex lock
> + * @count: current number of registered external module
> + * @wq: workqueue to do register work
> + * @core_exit: if goodix touch core exit, then no
> + *   registration is allowed.
> + * @core_data: core_data pointer
> + */
> +struct goodix_modules {
> +     struct list_head head;
> +     bool initilized;
> +     struct mutex mutex;
> +     unsigned int count;
> +     struct workqueue_struct *wq;
> +     bool core_exit;
> +     struct completion core_comp;
> +     struct goodix_ts_core *core_data;
> +};
> +static struct goodix_modules goodix_modules;
> +
> +/**
> + * __do_register_ext_module - register external module
> + * to register into touch core modules structure
> + */
> +static void  __do_register_ext_module(struct work_struct *work)
> +{
> +     struct goodix_ext_module *module =
> +                     container_of(work, struct goodix_ext_module, work);
> +     struct goodix_ext_module *ext_module;
> +     struct list_head *insert_point = &goodix_modules.head;
> +
> +     /* waitting for core layer */
> +     if (!wait_for_completion_timeout(&goodix_modules.core_comp, 5 * HZ))
> +             return;
> +
> +     /* driver probe failed */
> +     if (goodix_modules.core_exit)
> +             return;
> +
> +     /* prority level *must* be set */
> +     if (module->priority == EXTMOD_PRIO_RESERVED)
> +             return;
> +
> +     mutex_lock(&goodix_modules.mutex);
> +     if (!list_empty(&goodix_modules.head)) {
> +             list_for_each_entry(ext_module, &goodix_modules.head, list) {
> +                     if (ext_module == module) {
> +                             mutex_unlock(&goodix_modules.mutex);
> +                             return;
> +                     }
> +             }
> +
> +             list_for_each_entry(ext_module, &goodix_modules.head, list) {
> +                     /* small value of priority have higher priority level */
> +                     if (ext_module->priority >= module->priority) {
> +                             insert_point = &ext_module->list;
> +                             break;
> +                     }
> +             }
> +             /* else module will be inserted to goodix_modules->head */
> +     }
> +
> +     if (module->funcs && module->funcs->init) {
> +             if (module->funcs->init(goodix_modules.core_data,
> +                                     module) < 0) {
> +                     mutex_unlock(&goodix_modules.mutex);
> +                     return;
> +             }
> +     }
> +
> +     list_add(&module->list, insert_point->prev);
> +     goodix_modules.count++;
> +     mutex_unlock(&goodix_modules.mutex);
> +}
> +
> +/**
> + * goodix_register_ext_module - interface for external module
> + * to register into touch core modules structure
> + *
> + * @module: pointer to external module to be register
> + * return: 0 ok, <0 failed
> + */
> +int goodix_register_ext_module(struct goodix_ext_module *module)
> +{
> +     if (!module)
> +             return -EINVAL;
> +
> +     if (!goodix_modules.initilized) {
> +             goodix_modules.initilized = true;
> +             INIT_LIST_HEAD(&goodix_modules.head);
> +             mutex_init(&goodix_modules.mutex);
> +             init_completion(&goodix_modules.core_comp);
> +     }
> +
> +     if (goodix_modules.core_exit)
> +             return -EFAULT;
> +
> +     INIT_WORK(&module->work, __do_register_ext_module);
> +     schedule_work(&module->work);
> +
> +     return 0;
> +}
> +EXPORT_SYMBOL_GPL(goodix_register_ext_module);
> +
> +/**
> + * goodix_unregister_ext_module - interface for external module
> + * to unregister external modules
> + *
> + * @module: pointer to external module
> + * return: 0 ok, <0 failed
> + */
> +int goodix_unregister_ext_module(struct goodix_ext_module *module)
> +{
> +     struct goodix_ext_module *ext_module;
> +     bool found = false;
> +
> +     if (!module)
> +             return -EINVAL;
> +
> +     if (!goodix_modules.initilized)
> +             return -EINVAL;
> +
> +     if (!goodix_modules.core_data)
> +             return -ENODEV;
> +
> +     mutex_lock(&goodix_modules.mutex);
> +     if (!list_empty(&goodix_modules.head)) {
> +             list_for_each_entry(ext_module, &goodix_modules.head, list) {
> +                     if (ext_module == module) {
> +                             found = true;
> +                             break;
> +                     }
> +             }
> +     } else {
> +             mutex_unlock(&goodix_modules.mutex);
> +             return -EFAULT;
> +     }
> +
> +     if (!found) {
> +             mutex_unlock(&goodix_modules.mutex);
> +             return -EFAULT;
> +     }
> +
> +     list_del(&module->list);
> +     mutex_unlock(&goodix_modules.mutex);
> +
> +     if (module->funcs && module->funcs->exit)
> +             module->funcs->exit(goodix_modules.core_data, module);
> +     goodix_modules.count--;
> +
> +     return 0;
> +}
> +EXPORT_SYMBOL_GPL(goodix_unregister_ext_module);
> +
> +static void goodix_ext_sysfs_release(struct kobject *kobj)
> +{
> +     return;
> +}
> +
> +#define to_ext_module(kobj) container_of(kobj,\
> +                             struct goodix_ext_module, kobj)
> +#define to_ext_attr(attr) container_of(attr,\
> +                             struct goodix_ext_attribute, attr)
> +
> +static ssize_t goodix_ext_sysfs_show(struct kobject *kobj,
> +             struct attribute *attr, char *buf)
> +{
> +     struct goodix_ext_module *module = to_ext_module(kobj);
> +     struct goodix_ext_attribute *ext_attr = to_ext_attr(attr);
> +
> +     if (ext_attr->show)
> +             return ext_attr->show(module, buf);
> +
> +     return -EIO;
> +}
> +
> +static ssize_t goodix_ext_sysfs_store(struct kobject *kobj,
> +             struct attribute *attr, const char *buf, size_t count)
> +{
> +     struct goodix_ext_module *module = to_ext_module(kobj);
> +     struct goodix_ext_attribute *ext_attr = to_ext_attr(attr);
> +
> +     if (ext_attr->store)
> +             return ext_attr->store(module, buf, count);
> +
> +     return -EIO;
> +}
> +
> +static const struct sysfs_ops goodix_ext_ops = {
> +     .show = goodix_ext_sysfs_show,
> +     .store = goodix_ext_sysfs_store
> +};
> +
> +static struct kobj_type goodix_ext_ktype = {
> +     .release = goodix_ext_sysfs_release,
> +     .sysfs_ops = &goodix_ext_ops,
> +};
> +
> +struct kobj_type *goodix_get_default_ktype(void)
> +{
> +     return &goodix_ext_ktype;
> +}
> +EXPORT_SYMBOL_GPL(goodix_get_default_ktype);
> +
> +struct kobject *goodix_get_default_kobj(void)
> +{
> +     struct kobject *kobj = NULL;
> +
> +     if (goodix_modules.core_data &&
> +                     goodix_modules.core_data->pdev)
> +             kobj = &goodix_modules.core_data->pdev->dev.kobj;
> +     return kobj;
> +}
> +EXPORT_SYMBOL_GPL(goodix_get_default_kobj);
> +
> +/* show external module information */
> +static ssize_t goodix_ts_extmod_show(struct device *dev,
> +             struct device_attribute *attr, char *buf)
> +{
> +     struct goodix_ext_module *module;
> +     size_t offset = 0;
> +     int r;
> +
> +     mutex_lock(&goodix_modules.mutex);
> +     if (!list_empty(&goodix_modules.head)) {
> +             list_for_each_entry(module, &goodix_modules.head, list) {
> +                     r = snprintf(&buf[offset], PAGE_SIZE,
> +                                     "priority:%u module:%s\n",
> +                                     module->priority, module->name);
> +                     if (r < 0) {
> +                             mutex_unlock(&goodix_modules.mutex);
> +                             return -EINVAL;
> +                     }
> +                     offset += r;
> +             }
> +     }
> +
> +     mutex_unlock(&goodix_modules.mutex);
> +     return offset;
> +}
> +
> +/* show driver information */
> +static ssize_t goodix_ts_driver_info_show(struct device *dev,
> +             struct device_attribute *attr, char *buf)
> +{
> +     return snprintf(buf, PAGE_SIZE, "DriverVersion:%s\n",
> +                     GOODIX_DRIVER_VERSION);
> +}
> +
> +/* show chip infoamtion */
> +static ssize_t goodix_ts_chip_info_show(struct device  *dev,
> +             struct device_attribute *attr, char *buf)
> +{
> +     struct goodix_ts_core *core_data =
> +             dev_get_drvdata(dev);
> +     struct goodix_ts_device *ts_dev = core_data->ts_dev;
> +     struct goodix_ts_version chip_ver;
> +     int r, cnt = 0;
> +
> +     cnt += snprintf(buf, PAGE_SIZE,
> +                     "TouchDeviceName:%s\n", ts_dev->name);
> +     if (ts_dev->hw_ops->read_version) {
> +             r = ts_dev->hw_ops->read_version(ts_dev, &chip_ver);
> +             if (!r && chip_ver.valid) {
> +                     cnt += snprintf(&buf[cnt], PAGE_SIZE,
> +                                     "PID:%s\nVID:%04x\nSensorID:%02x\n",
> +                                     chip_ver.pid, chip_ver.vid,
> +                                     chip_ver.sensor_id);
> +             }
> +     }
> +
> +     return cnt;
> +}
> +
> +/* show chip configuration data */
> +static ssize_t goodix_ts_config_data_show(struct device *dev,
> +             struct device_attribute *attr, char *buf)
> +{
> +     struct goodix_ts_core *core_data =
> +             dev_get_drvdata(dev);
> +     struct goodix_ts_device *ts_dev = core_data->ts_dev;
> +     struct goodix_ts_config *ncfg = ts_dev->normal_cfg;
> +     u8 *data;
> +     int i, r, offset = 0;
> +
> +     if (ncfg && ncfg->initialized && ncfg->length < PAGE_SIZE) {
> +             data = kmalloc(ncfg->length, GFP_KERNEL);
> +             if (!data)
> +                     return -ENOMEM;
> +
> +             r = ts_dev->hw_ops->read(ts_dev, ncfg->reg_base,
> +                             &data[0], ncfg->length);
> +             if (r < 0) {
> +                     kfree(data);
> +                     return -EINVAL;
> +             }
> +
> +             for (i = 0; i < ncfg->length; i++) {
> +                     if (i != 0 && i % 20 == 0)
> +                             buf[offset++] = '\n';
> +                     offset += snprintf(&buf[offset], PAGE_SIZE - offset,
> +                                     "%02x ", data[i]);
> +             }
> +             buf[offset++] = '\n';
> +             buf[offset++] = '\0';
> +             kfree(data);
> +             return offset;
> +     }
> +
> +     return -EINVAL;
> +}
> +
> +/* reset chip */
> +static ssize_t goodix_ts_reset_store(struct device *dev,
> +             struct device_attribute *attr,
> +             const char *buf,
> +             size_t count)
> +{
> +     struct goodix_ts_core *core_data =
> +             dev_get_drvdata(dev);
> +     struct goodix_ts_device *ts_dev = core_data->ts_dev;
> +     int en;
> +
> +     if (kstrtoint(buf, 10, &en))
> +             return -EINVAL;
> +
> +     if (en != 1)
> +             return -EINVAL;
> +
> +     if (ts_dev->hw_ops->reset)
> +             ts_dev->hw_ops->reset(ts_dev);
> +     return count;
> +
> +}
> +
> +/* show irq information */
> +static ssize_t goodix_ts_irq_info_show(struct device *dev,
> +             struct device_attribute *attr,
> +             char *buf)
> +{
> +     struct goodix_ts_core *core_data =
> +             dev_get_drvdata(dev);
> +     struct irq_desc *desc;
> +     size_t offset = 0;
> +     int r;
> +
> +     r = snprintf(&buf[offset], PAGE_SIZE, "irq:%u\n",
> +                     core_data->irq);
> +     if (r < 0)
> +             return -EINVAL;
> +
> +     offset += r;
> +     r = snprintf(&buf[offset], PAGE_SIZE - offset, "state:%s\n",
> +             atomic_read(&core_data->irq_enabled) ?
> +             "enabled" : "disabled");
> +     if (r < 0)
> +             return -EINVAL;
> +
> +     desc = irq_to_desc(core_data->irq);
> +     offset += r;
> +     r = snprintf(&buf[offset], PAGE_SIZE - offset, "disable-depth:%d\n",
> +             desc->depth);
> +     if (r < 0)
> +             return -EINVAL;
> +
> +     offset += r;
> +     r = snprintf(&buf[offset], PAGE_SIZE - offset, "trigger-count:%zu\n",
> +             core_data->irq_trig_cnt);
> +     if (r < 0)
> +             return -EINVAL;
> +
> +     offset += r;
> +     r = snprintf(&buf[offset], PAGE_SIZE - offset,
> +                     "echo 0/1 > irq_info to disable/enable irq");
> +     if (r < 0)
> +             return -EINVAL;
> +
> +     offset += r;
> +     return offset;
> +}
> +
> +/* enable/disable irq */
> +static ssize_t goodix_ts_irq_info_store(struct device *dev,
> +             struct device_attribute *attr,
> +             const char *buf,
> +             size_t count)
> +{
> +     struct goodix_ts_core *core_data =
> +             dev_get_drvdata(dev);
> +     int en;
> +
> +     if (kstrtoint(buf, 10, &en))
> +             return -EINVAL;
> +
> +     goodix_ts_irq_enable(core_data, en);
> +     return count;
> +}
> +
> +static DEVICE_ATTR(extmod_info, 0444, goodix_ts_extmod_show, NULL);
> +static DEVICE_ATTR(driver_info, 0444, goodix_ts_driver_info_show, NULL);
> +static DEVICE_ATTR(chip_info, 0444, goodix_ts_chip_info_show, NULL);
> +static DEVICE_ATTR(config_data, 0444, goodix_ts_config_data_show, NULL);
> +static DEVICE_ATTR(reset, 0200, NULL, goodix_ts_reset_store);
> +static DEVICE_ATTR(irq_info, 0644,
> +             goodix_ts_irq_info_show, goodix_ts_irq_info_store);
> +
> +static struct attribute *sysfs_attrs[] = {
> +     &dev_attr_extmod_info.attr,
> +     &dev_attr_driver_info.attr,
> +     &dev_attr_chip_info.attr,
> +     &dev_attr_config_data.attr,
> +     &dev_attr_reset.attr,
> +     &dev_attr_irq_info.attr,
> +     NULL,
> +};
> +
> +static const struct attribute_group sysfs_group = {
> +     .attrs = sysfs_attrs,
> +};
> +
> +static int goodix_ts_sysfs_init(struct goodix_ts_core *core_data)
> +{
> +     return sysfs_create_group(&core_data->pdev->dev.kobj, &sysfs_group);
> +}
> +
> +static void goodix_ts_sysfs_exit(struct goodix_ts_core *core_data)
> +{
> +     sysfs_remove_group(&core_data->pdev->dev.kobj, &sysfs_group);
> +}
> +
> +/* event notifier */
> +static BLOCKING_NOTIFIER_HEAD(ts_notifier_list);
> +/**
> + * goodix_ts_register_client - register a client notifier
> + * @nb: notifier block to callback on events
> + *  see enum ts_notify_event in goodix_ts_core.h
> + */
> +int goodix_ts_register_notifier(struct notifier_block *nb)
> +{
> +     return blocking_notifier_chain_register(&ts_notifier_list, nb);
> +}
> +EXPORT_SYMBOL(goodix_ts_register_notifier);
> +
> +/**
> + * goodix_ts_unregister_client - unregister a client notifier
> + * @nb: notifier block to callback on events
> + *   see enum ts_notify_event in goodix_ts_core.h
> + */
> +int goodix_ts_unregister_notifier(struct notifier_block *nb)
> +{
> +     return blocking_notifier_chain_unregister(&ts_notifier_list, nb);
> +}
> +EXPORT_SYMBOL(goodix_ts_unregister_notifier);
> +
> +/**
> + * goodix_ts_blocking_notify - notify clients of certain events
> + *   see enum ts_notify_event in goodix_ts_core.h
> + */
> +int goodix_ts_blocking_notify(enum ts_notify_event evt, void *v)
> +{
> +     return blocking_notifier_call_chain(&ts_notifier_list,
> +                     (unsigned long)evt, v);
> +}
> +EXPORT_SYMBOL_GPL(goodix_ts_blocking_notify);
> +
> +/**
> + * goodix_ts_input_report - report touch event to input subsystem
> + *
> + * @dev: input device pointer
> + * @touch_data: touch data pointer
> + * return: 0 ok, <0 failed
> + */
> +static int goodix_ts_input_report(struct input_dev *dev,
> +             struct goodix_touch_data *touch_data)
> +{
> +     struct goodix_ts_coords *coords = &touch_data->coords[0];
> +     struct goodix_ts_core *core_data = input_get_drvdata(dev);
> +     struct goodix_ts_board_data *ts_bdata = board_data(core_data);
> +     unsigned int touch_num = touch_data->touch_num, x, y;
> +     static u16 pre_fin;
> +     int i, id;
> +
> +     /* report touch-key */
> +     if (unlikely(touch_data->key_value)) {
> +             for (i = 0; i < ts_bdata->panel_max_key; i++) {
> +                     input_report_key(dev, ts_bdata->panel_key_map[i],
> +                                     touch_data->key_value & (1 << i));
> +             }
> +     }
> +
> +     /* first touch down and last touch up condition */
> +     if (touch_num != 0 && pre_fin == 0x0000) {
> +             /* first touch down event */
> +             input_report_key(dev, BTN_TOUCH, 1);
> +             input_report_key(dev, BTN_TOOL_FINGER, 1);
> +     } else if (touch_num == 0 && pre_fin != 0x0000) {
> +             /* no finger exist */
> +             input_report_key(dev, BTN_TOUCH, 0);
> +             input_report_key(dev, BTN_TOOL_FINGER, 0);
> +     } else if (touch_num == 0 && pre_fin == 0x0000) {
> +             return 0;
> +     }
> +
> +     /* report abs */
> +     id = coords->id;
> +     for (i = 0; i < ts_bdata->panel_max_id; i++) {
> +             if (touch_num && i == id) {
> +                     /* this is a valid touch down event */
> +#ifdef INPUT_TYPE_B_PROTOCOL
> +                     input_mt_slot(dev, id);
> +                     input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
> +#else
> +                     input_report_abs(dev, ABS_MT_TRACKING_ID, id);
> +#endif
> +                     if (unlikely(ts_bdata->swap_axis)) {
> +                             x = coords->y;
> +                             y = coords->x;
> +                     } else {
> +                             x = coords->x;
> +                             y = coords->y;
> +                     }
> +                     input_report_abs(dev, ABS_MT_POSITION_X, x);
> +                     input_report_abs(dev, ABS_MT_POSITION_Y, y);
> +                     input_report_abs(dev, ABS_MT_TOUCH_MAJOR, coords->w);
> +                     pre_fin |= 1 << i;
> +                     id = (++coords)->id;
> +#ifndef INPUT_TYPE_B_PROTOCOL
> +                     input_mt_sync(dev);
> +#endif
> +             } else {
> +                     if (pre_fin & (1 << i)) {/* release touch */
> +#ifdef INPUT_TYPE_B_PROTOCOL
> +                             input_mt_slot(dev, i);
> +                             input_mt_report_slot_state(dev, MT_TOOL_FINGER,
> +                                             false);
> +#endif
> +                             pre_fin &= ~(1 << i);
> +                     }
> +             }
> +     }
> +
> +#ifndef INPUT_TYPE_B_PROTOCOL
> +     if (!pre_fin)
> +             input_mt_sync(dev);
> +#endif
> +     input_sync(dev);
> +     return 0;
> +}
> +
> +/**
> + * goodix_ts_threadirq_func - Bottom half of interrupt
> + * This functions is excuted in thread context,
> + * sleep in this function is permit.
> + *
> + * @core_data: pointer to touch core data
> + * return: 0 ok, <0 failed
> + */
> +static irqreturn_t goodix_ts_threadirq_func(int irq, void *data)
> +{
> +     struct goodix_ts_core *core_data = data;
> +     struct goodix_ts_device *ts_dev =  core_data->ts_dev;
> +     struct goodix_ext_module *ext_module;
> +     struct goodix_ts_event *ts_event = &core_data->ts_event;
> +     int r;
> +
> +     core_data->irq_trig_cnt++;
> +     /* inform external module */
> +     list_for_each_entry(ext_module, &goodix_modules.head, list) {
> +             if (!ext_module->funcs->irq_event)
> +                     continue;
> +             r = ext_module->funcs->irq_event(core_data, ext_module);
> +             if (r == EVT_CANCEL_IRQEVT)
> +                     return IRQ_HANDLED;
> +     }
> +
> +     /* read touch data from touch device */
> +     r = ts_dev->hw_ops->event_handler(ts_dev, ts_event);
> +     if (likely(r >= 0)) {
> +             if (ts_event->event_type == EVENT_TOUCH) {
> +                     /* report touch */
> +                     goodix_ts_input_report(core_data->input_dev,
> +                                     &ts_event->event_data.touch_data);
> +             }
> +     }
> +
> +     return IRQ_HANDLED;
> +}
> +
> +/**
> + * goodix_ts_init_irq - Requset interrupt line from system
> + * @core_data: pointer to touch core data
> + * return: 0 ok, <0 failed
> + */
> +static int goodix_ts_irq_setup(struct goodix_ts_core *core_data)
> +{
> +     const struct goodix_ts_board_data *ts_bdata =
> +                     board_data(core_data);
> +     const struct device *dev = &core_data->pdev->dev;
> +     int r;
> +
> +     /* if ts_bdata->irq is invalid get it from irq-gpio */
> +     if (ts_bdata->irq <= 0)
> +             core_data->irq = gpiod_to_irq(ts_bdata->irq_gpiod);
> +     else
> +             core_data->irq = ts_bdata->irq;
> +
> +     dev_info(dev, "IRQ:%u,flags:%d\n", core_data->irq, 
> (int)ts_bdata->irq_flags);
> +     r = devm_request_threaded_irq(&core_data->pdev->dev,
> +                     core_data->irq, NULL,
> +                     goodix_ts_threadirq_func,
> +                     ts_bdata->irq_flags | IRQF_ONESHOT,
> +                     GOODIX_CORE_DRIVER_NAME,
> +                     core_data);
> +     if (r < 0)
> +             dev_err(dev, "Failed to requeset threaded irq:%d\n", r);
> +     else
> +             atomic_set(&core_data->irq_enabled, 1);
> +
> +     return r;
> +}
> +
> +/**
> + * goodix_ts_irq_enable - Enable/Disable a irq
> + * @core_data: pointer to touch core data
> + * enable: enable or disable irq
> + * return: 0 ok, <0 failed
> + */
> +int goodix_ts_irq_enable(struct goodix_ts_core *core_data,
> +                     bool enable)
> +{
> +     const struct device *dev = &core_data->pdev->dev;
> +
> +     if (enable) {
> +             if (!atomic_cmpxchg(&core_data->irq_enabled, 0, 1)) {
> +                     enable_irq(core_data->irq);
> +                     dev_dbg(dev, "Irq enabled\n");
> +             }
> +     } else {
> +             if (atomic_cmpxchg(&core_data->irq_enabled, 1, 0)) {
> +                     disable_irq(core_data->irq);
> +                     dev_dbg(dev, "Irq disabled\n");
> +             }
> +     }
> +
> +     return 0;
> +}
> +EXPORT_SYMBOL(goodix_ts_irq_enable);
> +/**
> + * goodix_ts_power_init - Get regulator for touch device
> + * @core_data: pointer to touch core data
> + * return: 0 ok, <0 failed
> + */
> +static int goodix_ts_power_init(struct goodix_ts_core *core_data)
> +{
> +     struct device *dev = NULL;
> +     struct goodix_ts_board_data *ts_bdata;
> +
> +     /* dev:i2c client device or spi slave device*/
> +     dev =  core_data->ts_dev->dev;
> +     ts_bdata = board_data(core_data);
> +
> +     if (ts_bdata->avdd_name) {
> +             core_data->avdd = devm_regulator_get(dev, ts_bdata->avdd_name);
> +             if (IS_ERR_OR_NULL(core_data->avdd)) {
> +                     core_data->avdd = NULL;
> +                     return -ENOENT;
> +             }
> +     } else {
> +             return -EINVAL;
> +     }
> +
> +     return 0;
> +}
> +
> +/**
> + * goodix_ts_power_on - Turn on power to the touch device
> + * @core_data: pointer to touch core data
> + * return: 0 ok, <0 failed
> + */
> +static int goodix_ts_power_on(struct goodix_ts_core *core_data)
> +{
> +     struct goodix_ts_board_data *ts_bdata =
> +                     board_data(core_data);
> +     const struct device *dev = &core_data->pdev->dev;
> +     int r;
> +
> +     dev_info(dev, "Device power on\n");
> +     if (core_data->power_on)
> +             return 0;
> +
> +     if (core_data->avdd) {
> +             r = regulator_enable(core_data->avdd);
> +             if (!r) {
> +                     if (ts_bdata->power_on_delay_us)
> +                             usleep_range(ts_bdata->power_on_delay_us,
> +                                     ts_bdata->power_on_delay_us);
> +             } else {
> +                     dev_err(dev, "Failed to enable analog power:%d\n", r);
> +                     return r;
> +             }
> +     }
> +
> +     core_data->power_on = 1;
> +     return 0;
> +}
> +
> +/**
> + * goodix_ts_power_off - Turn off power to the touch device
> + * @core_data: pointer to touch core data
> + * return: 0 ok, <0 failed
> + */
> +static int goodix_ts_power_off(struct goodix_ts_core *core_data)
> +{
> +     struct goodix_ts_board_data *ts_bdata =
> +                     board_data(core_data);
> +     const struct device *dev = &core_data->pdev->dev;
> +     int r;
> +
> +     dev_info(dev, "Device power off\n");
> +     if (!core_data->power_on)
> +             return 0;
> +
> +     if (core_data->avdd) {
> +             r = regulator_disable(core_data->avdd);
> +             if (!r) {
> +                     if (ts_bdata->power_off_delay_us)
> +                             usleep_range(ts_bdata->power_off_delay_us,
> +                                             ts_bdata->power_off_delay_us);
> +             } else {
> +                     dev_err(dev, "Failed to disable analog power:%d\n", r);
> +                     return r;
> +             }
> +     }
> +
> +     core_data->power_on = 0;
> +     return 0;
> +}
> +
> +/**
> + * goodix_ts_gpio_setup - Request gpio resources from GPIO subsysten
> + *   reset_gpio and irq_gpio number are obtained from goodix_ts_device
> + *  which created in hardware layer driver. e.g.goodix_xx_i2c.c
> + *   A goodix_ts_device should set those two fileds to right value
> + *   before registed to touch core driver.
> + * @core_data: pointer to touch core data
> + * return: 0 ok, <0 failed
> + */
> +static void goodix_ts_gpio_setup(struct goodix_ts_core *core_data)
> +{
> +     struct goodix_ts_board_data *ts_bdata = board_data(core_data);
> +     struct goodix_ts_device *ts_dev = core_data->ts_dev;
> +     const struct device *dev = &core_data->pdev->dev;
> +
> +     ts_bdata->reset_gpiod = devm_gpiod_get_optional(ts_dev->dev,
> +                     "reset", GPIOD_OUT_LOW);
> +     if (!ts_bdata->reset_gpiod)
> +             dev_info(dev, "No reset gpio found\n");
> +
> +     ts_bdata->irq_gpiod = devm_gpiod_get_optional(ts_dev->dev,
> +                     "irq", GPIOD_IN);
> +     if (!ts_bdata->irq_gpiod)
> +             dev_info(dev, "No irq gpio found\n");
> +
> +}
> +
> +/**
> + * goodix_input_set_params - set input parameters
> + */
> +static void goodix_ts_set_input_params(struct input_dev *input_dev,
> +             struct goodix_ts_board_data *ts_bdata)
> +{
> +     int i;
> +
> +     if (ts_bdata->swap_axis)
> +             swap(ts_bdata->panel_max_x, ts_bdata->panel_max_y);
> +
> +     input_set_abs_params(input_dev, ABS_MT_TRACKING_ID,
> +                     0, ts_bdata->panel_max_id, 0, 0);
> +     input_set_abs_params(input_dev, ABS_MT_POSITION_X,
> +                     0, ts_bdata->panel_max_x, 0, 0);
> +
> +     input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
> +                     0, ts_bdata->panel_max_y, 0, 0);
> +
> +     input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
> +                     0, ts_bdata->panel_max_w, 0, 0);
> +     if (ts_bdata->panel_max_key) {
> +             for (i = 0; i < ts_bdata->panel_max_key; i++)
> +                     input_set_capability(input_dev, EV_KEY,
> +                                     ts_bdata->panel_key_map[i]);
> +     }
> +}
> +
> +/**
> + * goodix_ts_input_dev_config - Requset and config a input device
> + *  then register it to input sybsystem.
> + *  NOTE that some hardware layer may provide a input device
> + *  (ts_dev->input_dev not NULL).
> + * @core_data: pointer to touch core data
> + * return: 0 ok, <0 failed
> + */
> +static int goodix_ts_input_dev_config(struct goodix_ts_core *core_data)
> +{
> +     struct goodix_ts_board_data *ts_bdata = board_data(core_data);
> +     struct device *dev = &core_data->pdev->dev;
> +     struct input_dev *input_dev = NULL;
> +     int r;
> +
> +     input_dev = devm_input_allocate_device(dev);
> +     if (!input_dev) {
> +             dev_err(dev, "Failed to allocated input device\n");
> +             return -ENOMEM;
> +     }
> +
> +     core_data->input_dev = input_dev;
> +     input_set_drvdata(input_dev, core_data);
> +
> +     input_dev->name = GOODIX_CORE_DRIVER_NAME;
> +     input_dev->phys = GOOIDX_INPUT_PHYS;
> +     input_dev->id.product = 0xDEAD;
> +     input_dev->id.vendor = 0xBEEF;
> +     input_dev->id.version = 10427;
> +
> +     __set_bit(EV_SYN, input_dev->evbit);
> +     __set_bit(EV_KEY, input_dev->evbit);
> +     __set_bit(EV_ABS, input_dev->evbit);
> +     __set_bit(BTN_TOUCH, input_dev->keybit);
> +     __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
> +
> +#ifdef INPUT_PROP_DIRECT
> +     __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
> +#endif
> +
> +     /* set input parameters */
> +     goodix_ts_set_input_params(input_dev, ts_bdata);
> +
> +#ifdef INPUT_TYPE_B_PROTOCOL
> +     input_mt_init_slots(input_dev, ts_bdata->panel_max_id,
> +                     INPUT_MT_DIRECT);
> +#endif
> +
> +     input_set_capability(input_dev, EV_KEY, KEY_POWER);
> +     r = input_register_device(input_dev);
> +     if (r < 0) {
> +             dev_err(dev, "Unable to register input device\n");
> +             return r;
> +     }
> +
> +     return 0;
> +}
> +
> +/**
> + * goodix_ts_hw_init - Hardware initialize
> + *  poweron - hardware reset - sendconfig
> + * @core_data: pointer to touch core data
> + * return: 0 intilize ok, <0 failed
> + */
> +static int goodix_ts_hw_init(struct goodix_ts_core *core_data)
> +{
> +     const struct goodix_ts_hw_ops *hw_ops =
> +             ts_hw_ops(core_data);
> +     int r;
> +
> +     r = goodix_ts_power_on(core_data);
> +     if (r < 0)
> +             goto exit;
> +
> +     /* reset touch device */
> +     if (hw_ops->reset)
> +             hw_ops->reset(core_data->ts_dev);
> +
> +     /* init */
> +     if (hw_ops->init) {
> +             r = hw_ops->init(core_data->ts_dev);
> +             if (r < 0) {
> +                     core_data->hw_err = true;
> +                     goto exit;
> +             }
> +     }
> +
> +exit:
> +     /* if bus communication error occurred then exit driver binding, other
> +      * errors will be ignored
> +      */
> +     if (r != -EBUS)
> +             r = 0;
> +     return r;
> +}
> +
> +/**
> + * goodix_ts_esd_work - check hardware status and recovery
> + *  the hardware if needed.
> + */
> +static void goodix_ts_esd_work(struct work_struct *work)
> +{
> +     struct delayed_work *dwork = to_delayed_work(work);
> +     struct goodix_ts_esd *ts_esd = container_of(dwork,
> +                     struct goodix_ts_esd, esd_work);
> +     struct goodix_ts_core *core = container_of(ts_esd,
> +                     struct goodix_ts_core, ts_esd);
> +     const struct goodix_ts_hw_ops *hw_ops = ts_hw_ops(core);
> +     int r = 0;
> +
> +     if (ts_esd->esd_on == false)
> +             return;
> +
> +     if (hw_ops->check_hw)
> +             r = hw_ops->check_hw(core->ts_dev);
> +     if (r < 0) {
> +             goodix_ts_power_off(core);
> +             goodix_ts_power_on(core);
> +             if (hw_ops->reset)
> +                     hw_ops->reset(core->ts_dev);
> +     }
> +
> +     mutex_lock(&ts_esd->esd_mutex);
> +     if (ts_esd->esd_on)
> +             schedule_delayed_work(&ts_esd->esd_work, 2 * HZ);
> +     mutex_unlock(&ts_esd->esd_mutex);
> +}
> +
> +/**
> + * goodix_ts_esd_on - turn on esd protection
> + */
> +static void goodix_ts_esd_on(struct goodix_ts_core *core_data)
> +{
> +     struct goodix_ts_esd *ts_esd = &core_data->ts_esd;
> +     const struct device *dev = &core_data->pdev->dev;
> +
> +     mutex_lock(&ts_esd->esd_mutex);
> +     if (ts_esd->esd_on == false) {
> +             ts_esd->esd_on = true;
> +             schedule_delayed_work(&ts_esd->esd_work, 2 * HZ);
> +             mutex_unlock(&ts_esd->esd_mutex);
> +             dev_info(dev, "Esd on\n");
> +             return;
> +     }
> +     mutex_unlock(&ts_esd->esd_mutex);
> +}
> +
> +/**
> + * goodix_ts_esd_off - turn off esd protection
> + */
> +static void goodix_ts_esd_off(struct goodix_ts_core *core_data)
> +{
> +     struct goodix_ts_esd *ts_esd = &core_data->ts_esd;
> +     const struct device *dev = &core_data->pdev->dev;
> +
> +     mutex_lock(&ts_esd->esd_mutex);
> +     if (ts_esd->esd_on == true) {
> +             ts_esd->esd_on = false;
> +             cancel_delayed_work(&ts_esd->esd_work);
> +             mutex_unlock(&ts_esd->esd_mutex);
> +             dev_info(dev, "Esd off\n");
> +             return;
> +     }
> +     mutex_unlock(&ts_esd->esd_mutex);
> +}
> +
> +/**
> + * goodix_esd_notifier_callback - notification callback
> + *  under certain condition, we need to turn off/on the esd
> + *  protector, we use kernel notify call chain to achieve this.
> + *
> + *  for example: before firmware update we need to turn off the
> + *  esd protector and after firmware update finished, we should
> + *  turn on the esd protector.
> + */
> +static int goodix_esd_notifier_callback(struct notifier_block *nb,
> +             unsigned long action, void *data)
> +{
> +     struct goodix_ts_esd *ts_esd = container_of(nb,
> +                     struct goodix_ts_esd, esd_notifier);
> +
> +     switch (action) {
> +     case NOTIFY_FWUPDATE_START:
> +     case NOTIFY_SUSPEND:
> +             goodix_ts_esd_off(ts_esd->ts_core);
> +             break;
> +     case NOTIFY_FWUPDATE_END:
> +     case NOTIFY_RESUME:
> +             goodix_ts_esd_on(ts_esd->ts_core);
> +             break;
> +     }
> +
> +     return 0;
> +}
> +
> +/**
> + * goodix_ts_esd_init - initialize esd protection
> + */
> +static int goodix_ts_esd_init(struct goodix_ts_core *core)
> +{
> +     struct goodix_ts_esd *ts_esd = &core->ts_esd;
> +
> +     INIT_DELAYED_WORK(&ts_esd->esd_work, goodix_ts_esd_work);
> +     mutex_init(&ts_esd->esd_mutex);
> +     ts_esd->ts_core = core;
> +     ts_esd->esd_on = false;
> +     ts_esd->esd_notifier.notifier_call = goodix_esd_notifier_callback;
> +     goodix_ts_register_notifier(&ts_esd->esd_notifier);
> +
> +     if (core->ts_dev->board_data->esd_default_on == true
> +                     && core->ts_dev->hw_ops->check_hw)
> +             goodix_ts_esd_on(core);
> +     return 0;
> +}
> +
> +/**
> + * goodix_ts_suspend - Touchscreen suspend function
> + */
> +static int goodix_ts_suspend(struct goodix_ts_core *core_data)
> +{
> +     struct goodix_ext_module *ext_module;
> +     struct goodix_ts_device *ts_dev = core_data->ts_dev;
> +     const struct device *dev = &core_data->pdev->dev;
> +     int r;
> +
> +     dev_dbg(dev, "Suspend start\n");
> +     /*
> +      * notify suspend event, inform the esd protector
> +      * and charger detector to turn off the work
> +      */
> +     goodix_ts_blocking_notify(NOTIFY_SUSPEND, NULL);
> +
> +     /* inform external module */
> +     mutex_lock(&goodix_modules.mutex);
> +     if (!list_empty(&goodix_modules.head)) {
> +             list_for_each_entry(ext_module, &goodix_modules.head, list) {
> +                     if (!ext_module->funcs->before_suspend)
> +                             continue;
> +
> +                     r = ext_module->funcs->before_suspend(core_data, 
> ext_module);
> +                     if (r == EVT_CANCEL_SUSPEND) {
> +                             mutex_unlock(&goodix_modules.mutex);
> +                             dev_dbg(dev, "Canceled by module:%s\n",
> +                                     ext_module->name);
> +                             goto out;
> +                     }
> +             }
> +     }
> +     mutex_unlock(&goodix_modules.mutex);
> +
> +     /* disable irq */
> +     goodix_ts_irq_enable(core_data, false);
> +
> +     /* let touch ic work in sleep mode */
> +     if (ts_dev && ts_dev->hw_ops->suspend)
> +             ts_dev->hw_ops->suspend(ts_dev);
> +     atomic_set(&core_data->suspended, 1);
> +
> +     /* inform exteranl modules */
> +     mutex_lock(&goodix_modules.mutex);
> +     if (!list_empty(&goodix_modules.head)) {
> +             list_for_each_entry(ext_module, &goodix_modules.head, list) {
> +                     if (!ext_module->funcs->after_suspend)
> +                             continue;
> +
> +                     r = ext_module->funcs->after_suspend(core_data, 
> ext_module);
> +                     if (r == EVT_CANCEL_SUSPEND) {
> +                             mutex_unlock(&goodix_modules.mutex);
> +                             dev_dbg(dev, "Canceled by module:%s\n",
> +                                     ext_module->name);
> +                             goto out;
> +                     }
> +             }
> +     }
> +     mutex_unlock(&goodix_modules.mutex);
> +
> +out:
> +     /* release all the touch IDs */
> +     core_data->ts_event.event_data.touch_data.touch_num = 0;
> +     goodix_ts_input_report(core_data->input_dev,
> +                     &core_data->ts_event.event_data.touch_data);
> +     dev_dbg(dev, "Suspend end\n");
> +     return 0;
> +}
> +
> +/**
> + * goodix_ts_resume - Touchscreen resume function
> + * Called by PM/FB/EARLYSUSPEN module to wakeup device
> + */
> +static int goodix_ts_resume(struct goodix_ts_core *core_data)
> +{
> +     struct goodix_ext_module *ext_module;
> +     struct goodix_ts_device *ts_dev = core_data->ts_dev;
> +     const struct device *dev = &core_data->pdev->dev;
> +     int r;
> +
> +     dev_dbg(dev, "Resume start\n");
> +     mutex_lock(&goodix_modules.mutex);
> +     if (!list_empty(&goodix_modules.head)) {
> +             list_for_each_entry(ext_module, &goodix_modules.head, list) {
> +                     if (!ext_module->funcs->before_resume)
> +                             continue;
> +
> +                     r = ext_module->funcs->before_resume(core_data, 
> ext_module);
> +                     if (r == EVT_CANCEL_RESUME) {
> +                             mutex_unlock(&goodix_modules.mutex);
> +                             dev_dbg(dev, "Canceled by module:%s\n",
> +                                     ext_module->name);
> +                             goto out;
> +                     }
> +             }
> +     }
> +     mutex_unlock(&goodix_modules.mutex);
> +
> +     atomic_set(&core_data->suspended, 0);
> +     /* resume device */
> +     if (ts_dev && ts_dev->hw_ops->resume)
> +             ts_dev->hw_ops->resume(ts_dev);
> +
> +     goodix_ts_irq_enable(core_data, true);
> +
> +     mutex_lock(&goodix_modules.mutex);
> +     if (!list_empty(&goodix_modules.head)) {
> +             list_for_each_entry(ext_module, &goodix_modules.head, list) {
> +                     if (!ext_module->funcs->after_resume)
> +                             continue;
> +
> +                     r = ext_module->funcs->after_resume(core_data, 
> ext_module);
> +                     if (r == EVT_CANCEL_RESUME) {
> +                             mutex_unlock(&goodix_modules.mutex);
> +                             dev_dbg(dev, "Canceled by module:%s\n",
> +                                     ext_module->name);
> +                             goto out;
> +                     }
> +             }
> +     }
> +     mutex_unlock(&goodix_modules.mutex);
> +
> +out:
> +     /*
> +      * notify resume event, inform the esd protector
> +      * and charger detector to turn on the work
> +      */
> +     goodix_ts_blocking_notify(NOTIFY_RESUME, NULL);
> +     dev_dbg(dev, "Resume end\n");
> +     return 0;
> +}
> +
> +/**
> + * goodix_ts_pm_suspend - PM suspend function
> + * Called by kernel during system suspend phrase
> + */
> +static int __maybe_unused goodix_ts_pm_suspend(struct device *dev)
> +{
> +     struct goodix_ts_core *core_data =
> +             dev_get_drvdata(dev);
> +
> +     return goodix_ts_suspend(core_data);
> +}
> +/**
> + * goodix_ts_pm_resume - PM resume function
> + * Called by kernel during system wakeup
> + */
> +static int __maybe_unused goodix_ts_pm_resume(struct device *dev)
> +{
> +     struct goodix_ts_core *core_data =
> +             dev_get_drvdata(dev);
> +
> +     return goodix_ts_resume(core_data);
> +}
> +
> +/**
> + * goodix_generic_noti_callback - generic notifier callback
> + *  for goodix touch notification event.
> + */
> +static int goodix_generic_noti_callback(struct notifier_block *self,
> +             unsigned long action, void *data)
> +{
> +     struct goodix_ts_core *ts_core = container_of(self,
> +                     struct goodix_ts_core, ts_notifier);
> +     const struct goodix_ts_hw_ops *hw_ops = ts_hw_ops(ts_core);
> +     int r;
> +
> +     switch (action) {
> +     case NOTIFY_FWUPDATE_END:
> +             if (ts_core->hw_err && hw_ops->init) {
> +                     /* Firmware has been updated, we need to reinit
> +                      * the chip, read the sensor ID and send the
> +                      * correct config data based on sensor ID.
> +                      * The input parameters also needs to be updated.
> +                      */
> +                     r = hw_ops->init(ts_core->ts_dev);
> +                     if (r < 0)
> +                             goto exit;
> +
> +                     goodix_ts_set_input_params(ts_core->input_dev,
> +                                     ts_core->ts_dev->board_data);
> +                     ts_core->hw_err = false;
> +             }
> +             break;
> +     }
> +
> +exit:
> +     return 0;
> +}
> +
> +/**
> + * goodix_ts_probe - called by kernel when a Goodix touch
> + *  platform driver is added.
> + */
> +static int goodix_ts_probe(struct platform_device *pdev)
> +{
> +     struct goodix_ts_core *core_data = NULL;
> +     struct goodix_ts_device *ts_device;
> +     int r;
> +
> +     ts_device = pdev->dev.platform_data;
> +     if (!ts_device || !ts_device->hw_ops || !ts_device->board_data) {
> +             dev_err(&pdev->dev, "Invalid touch device\n");
> +             return -ENODEV;
> +     }
> +
> +     core_data = devm_kzalloc(&pdev->dev, sizeof(struct goodix_ts_core),
> +                                             GFP_KERNEL);
> +     if (!core_data)
> +             return -ENOMEM;
> +
> +     /* touch core layer is a platform driver */
> +     core_data->pdev = pdev;
> +     core_data->ts_dev = ts_device;
> +     platform_set_drvdata(pdev, core_data);
> +
> +     r = goodix_ts_power_init(core_data);
> +     if (r < 0)
> +             dev_err(&pdev->dev, "Failed power init\n");
> +
> +     /* get GPIO resource if have */
> +     goodix_ts_gpio_setup(core_data);
> +
> +     /* initialize firmware */
> +     r = goodix_ts_hw_init(core_data);
> +     if (r < 0)
> +             goto out;
> +
> +     /* alloc/config/register input device */
> +     r = goodix_ts_input_dev_config(core_data);
> +     if (r < 0)
> +             goto out;
> +
> +     /* request irq line */
> +     r = goodix_ts_irq_setup(core_data);
> +     if (r < 0)
> +             goto out;
> +
> +     /* inform the external module manager that
> +      * touch core layer is ready now
> +      */
> +     goodix_modules.core_data = core_data;
> +     complete_all(&goodix_modules.core_comp);
> +
> +     /* create sysfs files */
> +     goodix_ts_sysfs_init(core_data);
> +
> +     /* esd protector */
> +     goodix_ts_esd_init(core_data);
> +
> +     /* generic notifier callback */
> +     core_data->ts_notifier.notifier_call = goodix_generic_noti_callback;
> +     goodix_ts_register_notifier(&core_data->ts_notifier);
> +
> +     return 0;
> +     /* we use resource managed api(devm_), no need to free resource */
> +out:
> +     goodix_modules.core_exit = true;
> +     complete_all(&goodix_modules.core_comp);
> +     dev_err(&pdev->dev, "Core layer probe failed");
> +     return r;
> +}
> +
> +static int goodix_ts_remove(struct platform_device *pdev)
> +{
> +     struct goodix_ts_core *core_data =
> +             platform_get_drvdata(pdev);
> +
> +     goodix_ts_power_off(core_data);
> +     goodix_ts_sysfs_exit(core_data);
> +     return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(dev_pm_ops, goodix_ts_pm_suspend, 
> goodix_ts_pm_resume);
> +
> +static const struct platform_device_id ts_core_ids[] = {
> +     {.name = GOODIX_CORE_DRIVER_NAME},
> +     {}
> +};
> +MODULE_DEVICE_TABLE(platform, ts_core_ids);
> +
> +static struct platform_driver goodix_ts_driver = {
> +     .driver = {
> +             .name = GOODIX_CORE_DRIVER_NAME,
> +             .owner = THIS_MODULE,
> +             .pm = &dev_pm_ops,
> +     },
> +     .probe = goodix_ts_probe,
> +     .remove = goodix_ts_remove,
> +     .id_table = ts_core_ids,
> +};
> +
> +static int __init goodix_ts_core_init(void)
> +{
> +     if (!goodix_modules.initilized) {
> +             goodix_modules.initilized = true;
> +             INIT_LIST_HEAD(&goodix_modules.head);
> +             mutex_init(&goodix_modules.mutex);
> +             init_completion(&goodix_modules.core_comp);
> +     }
> +
> +     return platform_driver_register(&goodix_ts_driver);
> +}
> +
> +
> +static void __exit goodix_ts_core_exit(void)
> +{
> +     platform_driver_unregister(&goodix_ts_driver);
> +}
> +
> +module_init(goodix_ts_core_init);
> +module_exit(goodix_ts_core_exit);
> +
> +MODULE_DESCRIPTION("Goodix Touchscreen Core Module");
> +MODULE_AUTHOR("Goodix, Inc.");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/input/touchscreen/goodix-ts-sunrise/goodix_ts_core.h 
> b/drivers/input/touchscreen/goodix-ts-sunrise/goodix_ts_core.h
> new file mode 100755
> index 0000000..400746e
> --- /dev/null
> +++ b/drivers/input/touchscreen/goodix-ts-sunrise/goodix_ts_core.h
> @@ -0,0 +1,553 @@
> +/*
> + * Goodix GTx5 Touchscreen Driver
> + * Core layer of touchdriver architecture.
> + *
> + * Copyright (C) 2015 - 2016 Goodix, Inc.
> + * Authors:  Wang Yafei <wangya...@goodix.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be a reference
> + * to you, when you are integrating the GOODiX's CTP IC into your system,
> + * 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.
> + *
> + */
> +#ifndef _GOODIX_TS_CORE_H_
> +#define _GOODIX_TS_CORE_H_
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/firmware.h>
> +#include <linux/slab.h>
> +#include <linux/vmalloc.h>
> +#include <linux/kthread.h>
> +#include <linux/version.h>
> +#include <linux/delay.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_device.h>
> +#include <linux/input.h>
> +#include <asm/unaligned.h>
> +#ifdef CONFIG_OF
> +#include <linux/of_gpio.h>
> +#include <linux/regulator/consumer.h>
> +#endif
> +
> +#include <linux/gpio/consumer.h>
> +
> +/* macros definition */
> +#define GOODIX_CORE_DRIVER_NAME      "goodix_ts"
> +#define GOODIX_DRIVER_VERSION        "v0.8"
> +#define GOODIX_BUS_RETRY_TIMES       3
> +#define GOODIX_MAX_TOUCH     10
> +#define GOODIX_MAX_KEY               3
> +#define GOODIX_CFG_MAX_SIZE  1024
> +
> +/*
> + * struct goodix_ts_board_data -  board data
> + * @avdd_name: name of analoy regulator
> + * @reset_gpio: reset gpio number
> + * @irq_gpio: interrupt gpio number
> + * @irq_flag: irq trigger type
> + * @power_on_delay_us: power on delay time (us)
> + * @power_off_delay_us: power off delay time (us)
> + * @swap_axis: whether swaw x y axis
> + * @panel_max_id: max supported fingers
> + * @panel_max_x/y/w/p: resolution and size
> + * @panel_max_key: max supported keys
> + * @pannel_key_map: key map
> + * @fw_name: name of the firmware image
> + */
> +struct goodix_ts_board_data {
> +     const char *avdd_name;
> +     struct gpio_desc *reset_gpiod;
> +     struct gpio_desc *irq_gpiod;
> +     int irq;
> +     unsigned int  irq_flags;
> +
> +     unsigned int power_on_delay_us;
> +     unsigned int power_off_delay_us;
> +
> +     unsigned int swap_axis;
> +     unsigned int panel_max_id; /*max touch id*/
> +     unsigned int panel_max_x;
> +     unsigned int panel_max_y;
> +     unsigned int panel_max_w; /*major and minor*/
> +     unsigned int panel_max_key;
> +     unsigned int panel_key_map[GOODIX_MAX_KEY];
> +
> +     const char *fw_name;
> +     bool esd_default_on;
> +};
> +
> +/*
> + * struct goodix_ts_config - chip config data
> + * @initialized: whether intialized
> + * @name: name of this config
> + * @lock: mutex
> + * @reg_base: register base of config data
> + * @length: bytes of the config
> + * @delay: delay time after sending config
> + * @data: config data buffer
> + */
> +struct goodix_ts_config {
> +     bool initialized;
> +     char name[24];
> +     struct mutex lock;
> +     unsigned int reg_base;
> +     unsigned int length;
> +     unsigned int delay; /*ms*/
> +     unsigned char data[GOODIX_CFG_MAX_SIZE];
> +};
> +
> +/*
> + * struct goodix_ts_cmd - command package
> + * @initialized: whether initialized
> + * @cmd_reg: command register
> + * @length: command length in bytes
> + * @cmds: command data
> + */
> +#pragma pack(4)
> +struct goodix_ts_cmd {
> +     u32 initialized;
> +     u32 cmd_reg;
> +     u32 length;
> +     u8 cmds[3];
> +};
> +#pragma pack()
> +
> +/* interrupt event type */
> +enum ts_event_type {
> +     EVENT_INVALID,
> +     EVENT_TOUCH,
> +     EVENT_REQUEST,
> +};
> +
> +/* requset event type */
> +enum ts_request_type {
> +     REQUEST_INVALID,
> +     REQUEST_CONFIG,
> +     REQUEST_BAKREF,
> +     REQUEST_RESET,
> +     REQUEST_MAINCLK,
> +};
> +
> +/* notifier event */
> +
> +enum ts_notify_event {
> +     NOTIFY_FWUPDATE_START,
> +     NOTIFY_FWUPDATE_END,
> +     NOTIFY_SUSPEND,
> +     NOTIFY_RESUME,
> +};
> +
> +/* coordinate package */
> +struct goodix_ts_coords {
> +     int id;
> +     unsigned int x, y, w, p;
> +};
> +
> +/* touch event data */
> +struct goodix_touch_data {
> +     /* finger */
> +     int touch_num;
> +     struct goodix_ts_coords coords[GOODIX_MAX_TOUCH];
> +     /* key */
> +     u16 key_value;
> +};
> +
> +/* request event data */
> +struct goodix_request_data {
> +     enum ts_request_type request_type;
> +};
> +
> +/*
> + * struct goodix_ts_event - touch event struct
> + * @event_type: touch event type, touch data or
> + *   request event
> + * @event_data: event data
> + */
> +struct goodix_ts_event {
> +     enum ts_event_type event_type;
> +     union {
> +             struct goodix_touch_data touch_data;
> +             struct goodix_request_data request_data;
> +     } event_data;
> +};
> +
> +/*
> + * struct goodix_ts_version - firmware version
> + * @valid: whether these infomation is valid
> + * @pid: product id string
> + * @vid: firmware version code
> + * @cid: customer id code
> + * @sensor_id: sendor id
> + */
> +struct goodix_ts_version {
> +     bool valid;
> +     char pid[5];
> +     u16 vid;
> +     u8 cid;
> +     u8 sensor_id;
> +};
> +
> +/*
> + * struct goodix_ts_device - ts device data
> + * @name: device name
> + * @version: reserved
> + * @bus_type: i2c or spi
> + * @board_data: board data obtained from dts
> + * @normal_cfg: normal config data
> + * @highsense_cfg: high sense config data
> + * @hw_ops: hardware operations
> + * @chip_version: firmware version infomation
> + * @sleep_cmd: sleep commang
> + * @gesture_cmd: gesture command
> + * @dev: device pointer,may be a i2c or spi device
> + * @of_node: device node
> + */
> +struct goodix_ts_device {
> +     char *name;
> +     int version;
> +     int bus_type;
> +
> +     struct goodix_ts_board_data *board_data;
> +     struct goodix_ts_config *normal_cfg;
> +     struct goodix_ts_config *highsense_cfg;
> +     const struct goodix_ts_hw_ops *hw_ops;
> +
> +     struct goodix_ts_version chip_version;
> +     struct goodix_ts_cmd sleep_cmd;
> +     struct goodix_ts_cmd gesture_cmd;
> +
> +     struct device *dev;
> +};
> +
> +/*
> + * struct goodix_ts_hw_ops -  hardware opeartions
> + * @init: hardware initialization
> + * @reset: hardware reset
> + * @read: read data from touch device
> + * @write: write data to touch device
> + * @send_cmd: send command to touch device
> + * @send_config: send configuration data
> + * @read_version: read firmware version
> + * @event_handler: touch event handler
> + * @suspend: put touch device into low power mode
> + * @resume: put touch device into working mode
> + */
> +struct goodix_ts_hw_ops {
> +
> +     int (*init)(struct goodix_ts_device *dev);
> +     void (*reset)(struct goodix_ts_device *dev);
> +     int (*read)(struct goodix_ts_device *dev, unsigned int addr,
> +                      unsigned char *data, unsigned int len);
> +     int (*write)(struct goodix_ts_device *dev, unsigned int addr,
> +                     unsigned char *data, unsigned int len);
> +     int (*send_cmd)(struct goodix_ts_device *dev,
> +                     struct goodix_ts_cmd  *cmd);
> +     int (*send_config)(struct goodix_ts_device *dev,
> +                     struct goodix_ts_config *config);
> +     int (*read_version)(struct goodix_ts_device *dev,
> +                     struct goodix_ts_version *version);
> +     int (*event_handler)(struct goodix_ts_device *dev,
> +                     struct goodix_ts_event *ts_event);
> +     int (*check_hw)(struct goodix_ts_device *dev);
> +     int (*suspend)(struct goodix_ts_device *dev);
> +     int (*resume)(struct goodix_ts_device *dev);
> +};
> +
> +/*
> + * struct goodix_ts_esd - esd protector structure
> + * @esd_work: esd delayed work
> + * @esd_on: true - turn on esd protection, false - turn
> + *  off esd protection
> + * @esd_mutex: protect @esd_on flag
> + */
> +struct goodix_ts_esd {
> +     struct delayed_work esd_work;
> +     struct mutex esd_mutex;
> +     struct notifier_block esd_notifier;
> +     struct goodix_ts_core *ts_core;
> +     bool esd_on;
> +};
> +
> +/*
> + * struct godix_ts_core - core layer data struct
> + * @pdev: core layer platform device
> + * @ts_dev: hardware layer touch device
> + * @input_dev: input device
> + * @avdd: analog regulator
> + * @pinctrl: pinctrl handler
> + * @pin_sta_active: active/normal pin state
> + * @pin_sta_suspend: suspend/sleep pin state
> + * @ts_event: touch event data struct
> + * @power_on: power on/off flag
> + * @irq: irq number
> + * @irq_enabled: irq enabled/disabled flag
> + * @suspended: suspend/resume flag
> + * @hw_err: indicate that hw_ops->init() failed
> + * @ts_notifier: generic notifier
> + * @ts_esd: esd protector structure
> + * @fb_notifier: framebuffer notifier
> + * @early_suspend: early suspend
> + */
> +struct goodix_ts_core {
> +     struct platform_device *pdev;
> +     struct goodix_ts_device *ts_dev;
> +     struct input_dev *input_dev;
> +
> +     struct regulator *avdd;
> +     struct goodix_ts_event ts_event;
> +     int power_on;
> +     int irq;
> +     size_t irq_trig_cnt;
> +
> +     atomic_t irq_enabled;
> +     atomic_t suspended;
> +     bool hw_err;
> +
> +     struct notifier_block ts_notifier;
> +     struct goodix_ts_esd ts_esd;
> +
> +#ifdef CONFIG_FB
> +     struct notifier_block fb_notifier;
> +#endif
> +};
> +
> +/* external module structures */
> +enum goodix_ext_priority {
> +     EXTMOD_PRIO_RESERVED = 0,
> +     EXTMOD_PRIO_FWUPDATE,
> +     EXTMOD_PRIO_GESTURE,
> +     EXTMOD_PRIO_HOTKNOT,
> +     EXTMOD_PRIO_DBGTOOL,
> +     EXTMOD_PRIO_DEFAULT,
> +};
> +
> +struct goodix_ext_module;
> +/* external module's operations callback */
> +struct goodix_ext_module_funcs {
> +     int (*init)(struct goodix_ts_core *core_data,
> +                     struct goodix_ext_module *module);
> +     int (*exit)(struct goodix_ts_core *core_data,
> +                     struct goodix_ext_module *module);
> +
> +     int (*before_reset)(struct goodix_ts_core *core_data,
> +                     struct goodix_ext_module *module);
> +     int (*after_reset)(struct goodix_ts_core *core_data,
> +                     struct goodix_ext_module *module);
> +
> +     int (*before_suspend)(struct goodix_ts_core *core_data,
> +                     struct goodix_ext_module *module);
> +     int (*after_suspend)(struct goodix_ts_core *core_data,
> +                     struct goodix_ext_module *module);
> +
> +     int (*before_resume)(struct goodix_ts_core *core_data,
> +                     struct goodix_ext_module *module);
> +     int (*after_resume)(struct goodix_ts_core *core_data,
> +                     struct goodix_ext_module *module);
> +
> +     int (*irq_event)(struct goodix_ts_core *core_data,
> +                     struct goodix_ext_module *module);
> +};
> +
> +/*
> + * struct goodix_ext_module - external module struct
> + * @list: list used to link into modules manager
> + * @name: name of external module
> + * @priority: module priority vlaue, zero is invalid
> + * @funcs: operations callback
> + * @priv_data: private data region
> + * @kobj: kobject
> + * @work: used to queue one work to do registration
> + */
> +struct goodix_ext_module {
> +     struct list_head list;
> +     char *name;
> +     enum goodix_ext_priority priority;
> +     const struct goodix_ext_module_funcs *funcs;
> +     void *priv_data;
> +     struct kobject kobj;
> +     struct work_struct work;
> +};
> +
> +/*
> + * struct goodix_ext_attribute - exteranl attribute struct
> + * @attr: attribute
> + * @show: show interface of external attribute
> + * @store: store interface of external attribute
> + */
> +struct goodix_ext_attribute {
> +     struct attribute attr;
> +     ssize_t (*show)(struct goodix_ext_module *, char *);
> +     ssize_t (*store)(struct goodix_ext_module *, const char *, size_t);
> +};
> +
> +/* external attrs helper macro */
> +#define __EXTMOD_ATTR(_name, _mode, _show, _store)   {       \
> +     .attr = {.name = __stringify(_name), .mode = _mode },   \
> +     .show   = _show,        \
> +     .store  = _store,       \
> +}
> +
> +/* external attrs helper macro, used to define external attrs */
> +#define DEFINE_EXTMOD_ATTR(_name, _mode, _show, _store)      \
> +static struct goodix_ext_attribute ext_attr_##_name = \
> +     __EXTMOD_ATTR(_name, _mode, _show, _store)
> +
> +/*
> + * get board data pointer
> + */
> +static inline struct goodix_ts_board_data *board_data(
> +             struct goodix_ts_core *core)
> +{
> +     return core->ts_dev->board_data;
> +}
> +
> +/*
> + * get touch hardware operations pointer
> + */
> +static inline const struct goodix_ts_hw_ops *ts_hw_ops(
> +             struct goodix_ts_core *core)
> +{
> +     return core->ts_dev->hw_ops;
> +}
> +
> +/*
> + * checksum helper functions
> + * checksum can be u8/le16/be16/le32/be32 format
> + * NOTE: the caller shoule be responsible for the
> + * legality of @data and @size parameters, so be
> + * careful when call these functions.
> + */
> +static inline u8 checksum_u8(u8 *data, u32 size)
> +{
> +     u8 checksum = 0;
> +     u32 i;
> +
> +     for (i = 0; i < size; i++)
> +             checksum += data[i];
> +     return checksum;
> +}
> +
> +static inline u16 checksum_le16(u8 *data, u32 size)
> +{
> +     u16 checksum = 0;
> +     u32 i;
> +
> +     for (i = 0; i < size; i += 2)
> +             checksum += le16_to_cpup((__le16 *)(data + i));
> +     return checksum;
> +}
> +
> +static inline u16 checksum_be16(u8 *data, u32 size)
> +{
> +     u16 checksum = 0;
> +     u32 i;
> +
> +     for (i = 0; i < size; i += 2)
> +             checksum += be16_to_cpup((__be16 *)(data + i));
> +     return checksum;
> +}
> +
> +static inline u32 checksum_le32(u8 *data, u32 size)
> +{
> +     u32 checksum = 0;
> +     u32 i;
> +
> +     for (i = 0; i < size; i += 4)
> +             checksum += le32_to_cpup((__le32 *)(data + i));
> +     return checksum;
> +}
> +
> +static inline u32 checksum_be32(u8 *data, u32 size)
> +{
> +     u32 checksum = 0;
> +     u32 i;
> +
> +     for (i = 0; i < size; i += 4)
> +             checksum += be32_to_cpup((__be32 *)(data + i));
> +     return checksum;
> +}
> +
> +/*
> + * define event action
> + * EVT_xxx macros are used in opeartions callback
> + * defined in @goodix_ext_module_funcs to control
> + * the behaviors of event such as suspend/resume/
> + * irq_event.
> + *
> + * generally there are two types of behaviors:
> + *   1. you want the flow of this event be canceled,
> + *   in this condition, you should return EVT_CANCEL_XXX
> + *   in the operations callback.
> + *           e.g. the firmware update module is updating
> + *           the firmware, you want to cancel suspend flow,
> + *           so you need to return EVT_CANCEL_SUSPEND in
> + *           suspend callback function.
> + *   2. you want the flow of this event continue, in
> + *   this condition, you should return EVT_HANDLED in
> + *   the callback function.
> + */
> +#define EVT_HANDLED                  0
> +#define EVT_CONTINUE                 0
> +#define EVT_CANCEL                   1
> +#define EVT_CANCEL_IRQEVT            1
> +#define EVT_CANCEL_SUSPEND           1
> +#define EVT_CANCEL_RESUME            1
> +#define EVT_CANCEL_RESET             1
> +
> +/*
> + * errno define
> + * Note:
> + *   1. bus read/write functions defined in hardware
> + *     layer code(e.g. goodix_xxx_i2c.c) *must* return
> + *     -EBUS if failed to transfer data on bus.
> + */
> +#define EBUS                                 1000
> +#define ETIMEOUT                             1001
> +#define ECHKSUM                                      1002
> +#define EMEMCMP                                      1003
> +
> +/**
> + * goodix_register_ext_module - interface for external module
> + * to register into touch core modules structure
> + *
> + * @module: pointer to external module to be register
> + * return: 0 ok, <0 failed
> + */
> +int goodix_register_ext_module(struct goodix_ext_module *module);
> +
> +/**
> + * goodix_unregister_ext_module - interface for external module
> + * to unregister external modules
> + *
> + * @module: pointer to external module
> + * return: 0 ok, <0 failed
> + */
> +int goodix_unregister_ext_module(struct goodix_ext_module *module);
> +
> +/**
> + * goodix_ts_irq_enable - Enable/Disable a irq
> +
> + * @core_data: pointer to touch core data
> + * enable: enable or disable irq
> + * return: 0 ok, <0 failed
> + */
> +int goodix_ts_irq_enable(struct goodix_ts_core *core_data, bool enable);
> +
> +struct kobj_type *goodix_get_default_ktype(void);
> +
> +/**
> + * goodix_ts_blocking_notify - notify clients of certain events
> + *   see enum ts_notify_event in goodix_ts_core.h
> + */
> +int goodix_ts_blocking_notify(enum ts_notify_event evt, void *v);
> +
> +#endif
> diff --git a/drivers/input/touchscreen/goodix-ts-sunrise/goodix_ts_tools.c 
> b/drivers/input/touchscreen/goodix-ts-sunrise/goodix_ts_tools.c
> new file mode 100755
> index 0000000..b0dd121
> --- /dev/null
> +++ b/drivers/input/touchscreen/goodix-ts-sunrise/goodix_ts_tools.c
> @@ -0,0 +1,542 @@
> +/*
> + * Goodix GTx5 Touchscreen Dirver
> + *
> + * Copyright (C) 2015 - 2016 Goodix, Inc.
> + * Authors:  Wang Yafei <wangya...@goodix.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be a reference
> + * to you, when you are integrating the GOODiX's CTP IC into your system,
> + * 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/compat.h>
> +#include <linux/kernel.h>
> +#include <linux/atomic.h>
> +#include <linux/miscdevice.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <linux/fs.h>
> +#include <linux/list.h>
> +#include <linux/ioctl.h>
> +#include <linux/wait.h>
> +#include "goodix_ts_core.h"
> +
> +#define GOODIX_TOOLS_NAME            "gtp_tools"
> +#define GOODIX_TS_IOC_MAGIC          'G'
> +#define NEGLECT_SIZE_MASK            (~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
> +
> +#define GTP_IRQ_ENABLE       _IO(GOODIX_TS_IOC_MAGIC, 0)
> +#define GTP_DEV_RESET        _IO(GOODIX_TS_IOC_MAGIC, 1)
> +#define GTP_SEND_COMMAND (_IOW(GOODIX_TS_IOC_MAGIC, 2, u8) & 
> NEGLECT_SIZE_MASK)
> +#define GTP_SEND_CONFIG      (_IOW(GOODIX_TS_IOC_MAGIC, 3, u8) & 
> NEGLECT_SIZE_MASK)
> +#define GTP_ASYNC_READ       (_IOR(GOODIX_TS_IOC_MAGIC, 4, u8) & 
> NEGLECT_SIZE_MASK)
> +#define GTP_SYNC_READ        (_IOR(GOODIX_TS_IOC_MAGIC, 5, u8) & 
> NEGLECT_SIZE_MASK)
> +#define GTP_ASYNC_WRITE      (_IOW(GOODIX_TS_IOC_MAGIC, 6, u8) & 
> NEGLECT_SIZE_MASK)
> +
> +#define GOODIX_TS_IOC_MAXNR          6
> +
> +#define IRQ_FALG     (0x01 << 2)
> +
> +#define I2C_MSG_HEAD_LEN     20
> +#define MAX_DATA_LEN 4096
> +#define TS_REG_COORDS_BASE   0x824E
> +/*
> + * struct goodix_tools_data - goodix tools data message used in sync read
> + * @data: The buffer into which data is written
> + * @reg_addr: Slave device register start address to start read data
> + * @length: Number of data bytes in @data being read from slave device
> + * @filled: When buffer @data be filled will set this flag with 1, outhrwise > 0
> + * @list_head:Eonnet every goodix_tools_data struct into a list
> + */
> +
> +struct goodix_tools_data {
> +     u32 reg_addr;
> +     u32 length;
> +     u8 *data;
> +     bool filled;
> +     struct list_head list;
> +};
> +
> +
> +/*
> + * struct goodix_tools_dev - goodix tools device struct
> + * @ts_core: The core data struct of ts driver
> + * @ops_mode: represent device work mode
> + * @rawdiffcmd: Set slave device into rawdata mode
> + * @normalcmd: Set slave device into normal mode
> + * @wq: Wait queue struct use in synchronous data read
> + * @mutex: Protect goodix_tools_dev
> + * @ref_count: reference count
> + * @ref_mutex: Protect ref_count
> + */
> +struct goodix_tools_dev {
> +     struct goodix_ts_core *ts_core;
> +     struct list_head head;
> +     unsigned int ops_mode;
> +     struct goodix_ts_cmd rawdiffcmd, normalcmd;
> +     wait_queue_head_t wq;
> +     struct mutex mutex;
> +     int ref_count;
> +     struct mutex ref_mutex;
> +     struct goodix_ext_module module;
> +} *goodix_tools_dev;
> +
> +static int goodix_tools_open(struct inode *inode, struct file *filp);
> +static int goodix_tools_release(struct inode *inode, struct file *filp);
> +static long goodix_tools_ioctl(struct file *filp, unsigned int cmd,
> +                                     unsigned long arg);
> +#ifdef CONFIG_COMPAT
> +static long goodix_tools_compat_ioctl(struct file *file, unsigned int cmd,
> +                             unsigned long arg);
> +#endif
> +
> +static const struct file_operations goodix_tools_fops = {
> +     .owner          = THIS_MODULE,
> +     .open           = goodix_tools_open,
> +     .release        = goodix_tools_release,
> +     .unlocked_ioctl = goodix_tools_ioctl,
> +#ifdef CONFIG_COMPAT
> +     .compat_ioctl = goodix_tools_compat_ioctl,
> +#endif
> +};
> +
> +static struct miscdevice goodix_tools_miscdev = {
> +     .minor  = MISC_DYNAMIC_MINOR,
> +     .name   = GOODIX_TOOLS_NAME,
> +     .fops   = &goodix_tools_fops,
> +};
> +/* read data from i2c asynchronous,
> + * success return bytes read, else return <= 0
> + */
> +static int async_read(struct goodix_tools_dev *dev, void __user *arg)
> +{
> +     u8 *databuf = NULL;
> +     int ret = 0;
> +     u32 reg_addr, length;
> +     u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
> +     struct goodix_ts_device *ts_dev = dev->ts_core->ts_dev;
> +     const struct goodix_ts_hw_ops *hw_ops = ts_dev->hw_ops;
> +
> +     ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
> +     if (ret)
> +             return ret;
> +
> +     reg_addr = le32_to_cpup((__le32 *)&i2c_msg_head[0]);
> +     length = le32_to_cpup((__le32 *)&i2c_msg_head[4]);
> +     if (length > MAX_DATA_LEN)
> +             length = MAX_DATA_LEN;
> +
> +     databuf = kzalloc(length, GFP_KERNEL);
> +     if (!databuf)
> +             return -ENOMEM;
> +
> +     if (!hw_ops->read(ts_dev, reg_addr, databuf, length)) {
> +             if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN,
> +                                     databuf, length))
> +                     ret = -EFAULT;
> +             else
> +                     ret = length;
> +     } else {
> +             ret = -EBUSY;
> +     }
> +
> +     kfree(databuf);
> +     return ret;
> +}
> +
> +/* read data from i2c synchronous,
> + * success return bytes read, else return <= 0
> + */
> +static int sync_read(struct goodix_tools_dev *dev, void __user *arg)
> +{
> +     int ret = 0;
> +     u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
> +     struct goodix_tools_data tools_data;
> +
> +     ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
> +     if (ret)
> +             return ret;
> +
> +     tools_data.reg_addr = le32_to_cpup((__le32 *)&i2c_msg_head[0]);
> +     tools_data.length = le32_to_cpup((__le32 *)&i2c_msg_head[4]);
> +     tools_data.filled = 0;
> +     if (tools_data.length > MAX_DATA_LEN)
> +             tools_data.length = MAX_DATA_LEN;
> +
> +     tools_data.data = kzalloc(tools_data.length, GFP_KERNEL);
> +     if (!tools_data.data)
> +             return -ENOMEM;
> +
> +     mutex_lock(&dev->mutex);
> +     list_add_tail(&tools_data.list, &dev->head);
> +     mutex_unlock(&dev->mutex);
> +     /* wait queue will timeout after 1 seconds */
> +     wait_event_interruptible_timeout(dev->wq, tools_data.filled == 1, HZ);
> +
> +     mutex_lock(&dev->mutex);
> +     list_del(&tools_data.list);
> +     mutex_unlock(&dev->mutex);
> +     if (tools_data.filled == 1) {
> +             if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, tools_data.data,
> +                                                     tools_data.length))
> +                     ret = -EFAULT;
> +             else
> +                     ret = tools_data.length;
> +     } else {
> +             ret = -EAGAIN;
> +             dev_dbg(goodix_tools_miscdev.this_device,
> +                     "Wait queue timeout\n");
> +     }
> +
> +     kfree(tools_data.data);
> +     return ret;
> +}
> +
> +/* write data to i2c asynchronous,
> + * success return bytes write, else return <= 0
> + */
> +static int async_write(struct goodix_tools_dev *dev, void __user *arg)
> +{
> +     u8 *databuf;
> +     int ret = 0;
> +     u32 reg_addr, length;
> +     u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
> +     struct goodix_ts_device *ts_dev = dev->ts_core->ts_dev;
> +     const struct goodix_ts_hw_ops *hw_ops = ts_dev->hw_ops;
> +
> +     ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
> +     if (ret)
> +             return -EFAULT;
> +
> +     reg_addr = le32_to_cpup((__le32 *)&i2c_msg_head[0]);
> +     length = le32_to_cpup((__le32 *)&i2c_msg_head[4]);
> +     if (length > MAX_DATA_LEN)
> +             length = MAX_DATA_LEN;
> +
> +     databuf = kzalloc(length, GFP_KERNEL);
> +     if (!databuf)
> +             return -ENOMEM;
> +
> +     ret = copy_from_user(databuf, (u8 *)arg + I2C_MSG_HEAD_LEN, length);
> +     if (ret) {
> +             ret = -EFAULT;
> +             goto err_out;
> +     }
> +
> +     if (hw_ops->write(ts_dev, reg_addr, databuf, length))
> +             ret = -EBUSY;
> +     else
> +             ret = length;
> +
> +err_out:
> +     kfree(databuf);
> +     return ret;
> +}
> +
> +static int init_cfg_data(struct goodix_ts_config *cfg, void __user *arg)
> +{
> +     int ret = 0;
> +     u32 reg_addr, length;
> +     u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
> +
> +     cfg->initialized = 0;
> +     mutex_init(&cfg->lock);
> +     ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
> +     if (ret)
> +             return -EFAULT;
> +
> +     reg_addr = le32_to_cpup((__le32 *)&i2c_msg_head[0]);
> +     length = le32_to_cpup((__le32 *)&i2c_msg_head[4]);
> +     if (length > MAX_DATA_LEN)
> +             length = MAX_DATA_LEN;
> +
> +     ret = copy_from_user(cfg->data, (u8 *)arg + I2C_MSG_HEAD_LEN, length);
> +     if (ret) {
> +             ret = -EFAULT;
> +             dev_dbg(goodix_tools_miscdev.this_device,
> +                     "Copy data from user failed\n");
> +             goto err_out;
> +     }
> +     cfg->reg_base = reg_addr;
> +     cfg->length = length;
> +     strlcpy(cfg->name, "tools-send-cfg", sizeof(cfg->name));
> +     cfg->delay = 50;
> +     cfg->initialized = true;
> +     return 0;
> +
> +err_out:
> +     return ret;
> +}
> +/**
> + * goodix_tools_ioctl - ioctl implementation
> + *
> + * @filp: Pointer to file opened
> + * @cmd: Ioctl opertion command
> + * @arg: Command data
> + * Returns >=0 - succeed, else failed
> + */
> +static long goodix_tools_ioctl(struct file *filp, unsigned int cmd,
> +                                     unsigned long arg)
> +{
> +     int ret = 0;
> +     struct goodix_tools_dev *dev = filp->private_data;
> +     struct goodix_ts_device *ts_dev;
> +     const struct goodix_ts_hw_ops *hw_ops;
> +     struct goodix_ts_cmd temp_cmd;
> +     struct goodix_ts_config *temp_cfg;
> +
> +     if (dev->ts_core == NULL) {
> +             dev_err(goodix_tools_miscdev.this_device,
> +                     "Tools module not register\n");
> +             return -EINVAL;
> +     }
> +     ts_dev = dev->ts_core->ts_dev;
> +     hw_ops = ts_dev->hw_ops;
> +
> +     if (_IOC_TYPE(cmd) != GOODIX_TS_IOC_MAGIC)
> +             return -ENOTTY;
> +     if (_IOC_NR(cmd) > GOODIX_TS_IOC_MAXNR)
> +             return -ENOTTY;
> +
> +
> +     switch (cmd & NEGLECT_SIZE_MASK) {
> +     case GTP_IRQ_ENABLE:
> +             if (arg == 1) {
> +                     goodix_ts_irq_enable(dev->ts_core, true);
> +                     mutex_lock(&dev->mutex);
> +                     dev->ops_mode |= IRQ_FALG;
> +                     mutex_unlock(&dev->mutex);
> +                     dev_dbg(goodix_tools_miscdev.this_device,
> +                             "IRQ enabled\n");
> +             } else if (arg == 0) {
> +                     goodix_ts_irq_enable(dev->ts_core, false);
> +                     mutex_lock(&dev->mutex);
> +                     dev->ops_mode &= ~IRQ_FALG;
> +                     mutex_unlock(&dev->mutex);
> +                     dev_dbg(goodix_tools_miscdev.this_device,
> +                             "IRQ disabled\n");
> +             } else {
> +                     dev_dbg(goodix_tools_miscdev.this_device,
> +                             "Irq already set with, arg = %ld\n", arg);
> +             }
> +             ret = 0;
> +             break;
> +     case GTP_DEV_RESET:
> +             hw_ops->reset(ts_dev);
> +             break;
> +     case GTP_SEND_COMMAND:
> +             ret = copy_from_user(&temp_cmd, (void __user *)arg,
> +                                     sizeof(struct goodix_ts_cmd));
> +             if (ret) {
> +                     ret = -EINVAL;
> +                     goto err_out;
> +             }
> +
> +             ret = hw_ops->send_cmd(ts_dev, &temp_cmd);
> +             if (ret) {
> +                     dev_warn(goodix_tools_miscdev.this_device,
> +                             "Send command failed\n");
> +                     ret = -EAGAIN;
> +             }
> +     break;
> +     case GTP_SEND_CONFIG:
> +             temp_cfg = kzalloc(sizeof(struct goodix_ts_config), GFP_KERNEL);
> +             if (!temp_cfg) {
> +                     ret = -ENOMEM;
> +                     goto err_out;
> +             }
> +             ret = init_cfg_data(temp_cfg, (void __user *)arg);
> +
> +             if (!ret) {
> +                     ret = hw_ops->send_config(ts_dev, temp_cfg);
> +                     if (ret) {
> +                             dev_warn(goodix_tools_miscdev.this_device,
> +                                     "Failed send config\n");
> +                             ret = -EAGAIN;
> +                     }
> +             }
> +             kfree(temp_cfg);
> +     break;
> +     case GTP_ASYNC_READ:
> +             ret = async_read(dev, (void __user *)arg);
> +             if (ret < 0)
> +                     dev_warn(goodix_tools_miscdev.this_device,
> +                             "Async data read failed");
> +     break;
> +     case GTP_SYNC_READ:
> +             if (filp->f_flags & O_NONBLOCK) {
> +                     dev_dbg(goodix_tools_miscdev.this_device,
> +                             "Goodix tools now worked in sync_bus mode\n");
> +                     ret = -EAGAIN;
> +                     goto err_out;
> +             }
> +             ret = sync_read(dev, (void __user *)arg);
> +             if (ret < 0)
> +                     dev_warn(goodix_tools_miscdev.this_device,
> +                             "Sync data read failed\n");
> +     break;
> +     case GTP_ASYNC_WRITE:
> +             ret = async_write(dev, (void __user *)arg);
> +             if (ret < 0)
> +                     dev_warn(goodix_tools_miscdev.this_device,
> +                             "Async data write failed\n");
> +     break;
> +     default:
> +             dev_info(goodix_tools_miscdev.this_device, "Invalid cmd\n");
> +             ret = -ENOTTY;
> +             break;
> +     }
> +
> +err_out:
> +     return ret;
> +}
> +
> +#ifdef CONFIG_COMPAT
> +static long goodix_tools_compat_ioctl(struct file *file, unsigned int cmd,
> +                             unsigned long arg)
> +{
> +     if (!file->f_op || !file->f_op->unlocked_ioctl)
> +             return -ENOTTY;
> +
> +     return goodix_tools_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
> +}
> +#endif
> +
> +static int goodix_tools_open(struct inode *inode, struct file *filp)
> +{
> +     int ret = 0;
> +
> +     filp->private_data = goodix_tools_dev;
> +
> +     mutex_lock(&goodix_tools_dev->ref_mutex);
> +
> +     /* Only the first time open device need to register module */
> +     if (goodix_tools_dev->ref_count == 0)
> +             ret = goodix_register_ext_module(&goodix_tools_dev->module);
> +     if (!ret)
> +             goodix_tools_dev->ref_count++;
> +
> +     mutex_unlock(&goodix_tools_dev->ref_mutex);
> +     return ret;
> +}
> +
> +static int goodix_tools_release(struct inode *inode, struct file *filp)
> +{
> +     int ret = 0;
> +
> +     mutex_lock(&goodix_tools_dev->ref_mutex);
> +
> +     goodix_tools_dev->ref_count--;
> +     /* when the last close this dev node unregister the module */
> +     if (goodix_tools_dev->ref_count == 0)
> +             ret = goodix_unregister_ext_module(&goodix_tools_dev->module);
> +
> +     mutex_unlock(&goodix_tools_dev->ref_mutex);
> +     return ret;
> +}
> +
> +/**
> + * goodix_tools_module_irq - goodix tools Irq handle
> + * This functions is excuted when interrupt happened
> + *
> + * @core_data: pointer to touch core data
> + * @module: pointer to goodix_ext_module struct
> + * return: EVT_CONTINUE let other module handle this irq
> + */
> +static int goodix_tools_module_irq(struct goodix_ts_core *core_data,
> +     struct goodix_ext_module *module)
> +{
> +     struct goodix_tools_dev *dev = module->priv_data;
> +     struct goodix_ts_device *ts_dev = dev->ts_core->ts_dev;
> +     const struct goodix_ts_hw_ops *hw_ops = ts_dev->hw_ops;
> +     struct goodix_tools_data *tools_data;
> +     int r = 0;
> +     u8 evt_sta = 0;
> +
> +     if (!list_empty(&dev->head)) {
> +             r = hw_ops->read(ts_dev, TS_REG_COORDS_BASE, &evt_sta, 1);
> +             if (r < 0 || ((evt_sta & 0x80) == 0))
> +                     return EVT_CONTINUE;
> +
> +             mutex_lock(&dev->mutex);
> +             list_for_each_entry(tools_data, &dev->head, list) {
> +                     if (!hw_ops->read(ts_dev, tools_data->reg_addr,
> +                                     tools_data->data, tools_data->length)) {
> +                             tools_data->filled = 1;
> +                     }
> +             }
> +             mutex_unlock(&dev->mutex);
> +             wake_up(&dev->wq);
> +     }
> +     return EVT_CONTINUE;
> +}
> +
> +static int goodix_tools_module_init(struct goodix_ts_core *core_data,
> +                     struct goodix_ext_module *module)
> +{
> +     struct goodix_tools_dev *dev = module->priv_data;
> +
> +     if (core_data) {
> +             dev->ts_core = core_data;
> +             return 0;
> +     } else {
> +             return -ENODEV;
> +     }
> +}
> +
> +static struct goodix_ext_module_funcs goodix_tools_module_funcs = {
> +     .irq_event = goodix_tools_module_irq,
> +     .init = goodix_tools_module_init,
> +};
> +
> +/**
> + * goodix_tools_init - init goodix tools device and register a miscdevice
> + *
> + * return: 0 success, else failed
> + */
> +static int __init goodix_tools_init(void)
> +{
> +     int ret;
> +
> +     goodix_tools_dev = kzalloc(sizeof(struct goodix_tools_dev), GFP_KERNEL);
> +     if (!goodix_tools_dev)
> +             return -ENOMEM;
> +
> +     INIT_LIST_HEAD(&goodix_tools_dev->head);
> +     goodix_tools_dev->ops_mode = 0;
> +     goodix_tools_dev->ops_mode |= IRQ_FALG;
> +     init_waitqueue_head(&goodix_tools_dev->wq);
> +     mutex_init(&goodix_tools_dev->mutex);
> +     goodix_tools_dev->ref_count = 0;
> +     mutex_init(&goodix_tools_dev->ref_mutex);
> +
> +     goodix_tools_dev->module.funcs = &goodix_tools_module_funcs;
> +     goodix_tools_dev->module.name = GOODIX_TOOLS_NAME;
> +     goodix_tools_dev->module.priv_data = goodix_tools_dev;
> +     goodix_tools_dev->module.priority = EXTMOD_PRIO_DBGTOOL;
> +
> +     ret = misc_register(&goodix_tools_miscdev);
> +
> +     return ret;
> +}
> +
> +static void __exit goodix_tools_exit(void)
> +{
> +     misc_deregister(&goodix_tools_miscdev);
> +     kfree(goodix_tools_dev);
> +}
> +
> +module_init(goodix_tools_init);
> +module_exit(goodix_tools_exit);
> +
> +MODULE_DESCRIPTION("Goodix tools Module");
> +MODULE_AUTHOR("Goodix, Inc.");
> +MODULE_LICENSE("GPL v2");

Reply via email to