| /* |
| * Cypress CY8CMBR3xxx CapSense Express Controller Driver |
| * All registers mentioned in the document are little endian. |
| */ |
| #include <linux/bsearch.h> |
| #include <linux/debugfs.h> |
| #include <linux/delay.h> |
| #include <linux/i2c.h> |
| #include <linux/init.h> |
| #include <linux/input.h> |
| #include <linux/interrupt.h> |
| #include <linux/module.h> |
| #include <linux/of.h> |
| #include <linux/of_gpio.h> |
| #include <linux/seq_file.h> |
| #include <linux/slab.h> |
| |
| #include <linux/input/cy8cmbr3108.h> |
| |
| #define CY8CMBR31XX_DRIVER_NAME "cy8cmbr3108" |
| |
| /* |
| * Defines the maximum number of sensor channels that the Cypress |
| * touch controller can support. Currently we only use the |
| * cy8cmbr3108, so the maximum number of sensor channels supported is 8. |
| * If this driver is used for a model with a larger number of channels, |
| * this value should be increased accordingly. |
| */ |
| #define MAX_NUM_SENSORS 8 |
| |
| /* |
| * Cypress controller is entering a low power state that it wakes from on the |
| * first address match, but will NACK it. Then, it will ACK the following |
| * addresses until it re-enters low power mode. |
| * We retry if the first few reads/writes fail. |
| */ |
| #define MAX_RETRIES 5 |
| |
| /* |
| * Capacitive sensor enable/disable configuration. (R/W) |
| * bit x corresponds to CSx |
| * 0: Disabled, 1: Enabled |
| */ |
| #define SENSOR_EN 0x00 |
| |
| /* |
| * This register configures inclusion of sensors in the group |
| * undergoing Flanking Sensor Suppression (FSS) processing. |
| * FSS should only be enabled on button sensors. |
| * If Bit x is 0, then CSx is excluded from FSS processing. |
| * If Bit x is 1, then CSx is included in FSS processing. |
| */ |
| #define FSS_EN 0x02 |
| |
| /* |
| * Sensitivities (units: count/pF) for button sensors 0-3, 4-7 |
| * Each sensor accounts for two bits |
| * Most sensitive <-----------------------------------------> Least sensitive |
| * 0: 50counts/0.1pF, 1: 50counts/0.2pF, 2: 50counts/0.3pF, 3: 50counts/0.4pF |
| * |
| * ADDR: SENSITIVITY_BASE |
| * +-------+-----+-----+-----+-----+-----+-----+-----+-----+ |
| * | Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
| * |-------+-----+-----+-----+-----+-----+-----+-----+-----| |
| * | sensor| CS3 | CS2 | CS1 | CS0 | |
| * +-------+-----------+-----------+-----------+-----------+ |
| * ADDR: SENSITIVITY_BASE+1 |
| * +-------+-----+-----+-----+-----+-----+-----+-----+-----+ |
| * | Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
| * |-------+-----+-----+-----+-----+-----+-----+-----+-----| |
| * | sensor| CS7 | CS6 | CS5 | CS4 | |
| * +-------+-----+-----+-----+-----+-----+-----+-----+-----+ |
| * |
| */ |
| #define SENSITIVITY_BASE 0x08 |
| |
| /* |
| * Finger threshold (units: counts) for sensor 0. (R/W) |
| * The valid range of this bit field: 31~200 |
| * The lower the more sensitive. |
| * Dafault value: 128 |
| * sensor threshold address for CSx is 0x0C+x |
| */ |
| #define SENSOR_THRESHOLD_BASE 0x0C |
| |
| /* |
| * Sensor ON debounce configuration. |
| * Number of consecutive scans for which a sensor's signal must be |
| * above the finger threshold plus hysteresis in order for the device to report |
| * an ON status. |
| * The valid range of this bit field: 1~15 |
| */ |
| #define SENSOR_DEBOUNCE 0x1C |
| |
| /* |
| * Button hysteresis override configuration. |
| * Bit 7 Hysteresis override. 0: Disabled, 1: Enabled |
| * Bit [4:0] Hysteresis value (unit: counts) to apply for button hysteresis |
| * override. |
| */ |
| #define BUTTON_HYS 0x1D |
| |
| /* |
| * Low baseline reset parameter configuration for button sensor. |
| * Bit 7 Low_baseline reset threshold override. 0: Disabled, 1: Enabled |
| * Bit [6:0] Low baseline reset threshold |
| */ |
| #define BUTTON_LBR 0x1F |
| |
| /* |
| * Button negative noise threshold configuration. |
| * Bit 7 Button negative noise threshold override. 0: Disabled, 1: Enabled |
| * Bit [6:0] Button negative noise threshold |
| */ |
| #define BUTTON_NNT 0x20 |
| |
| /* |
| * Button noise threshold configuration. |
| * Bit 7 Button noise threshold override. 0: Disabled, 1: Enabled |
| * Bit [6:0] Button noise threshold |
| */ |
| #define BUTTON_NT 0x21 |
| |
| /* |
| * Filter settings. (R/W) |
| * |
| * Reserved (Bits 2-7) |
| * |
| * IIR Filter Enable. (Bit 1) |
| * 0: IIR filter disabled, 1: IIR filter enabled |
| * |
| * Median Filter Enable. (Bit 0) |
| * 0: Median filter disabled, 1: Median filter enabled |
| */ |
| #define DEVICE_CFG0 0x4D |
| |
| /* |
| * Global sensing and processing configuration. (R/W) |
| * EMC solution enable (improves noise mitigation). (Bit 2) |
| * 0: EMC solution disabled, 1: enabled |
| * |
| * Automatic threshold enable/disable configuration. (Bit 3) |
| * 0: Disabled, 1: Enabled |
| * To use customized thresholds, this bit must be unset. |
| * Note that automatic thresholds can only be enabled if EMC solution is |
| * disabled. |
| * |
| * Button and slider auto-reset configuration. (Bit 4 and 5) |
| * This feature prevents a sensor from getting stuck ON in situations such as |
| * a metal object placed too close to it. |
| * 0: Disabled, 1: Enabled; timeout = 5s, 2: Enabled; timeout = 20s |
| */ |
| #define DEVICE_CFG2 0x4F |
| |
| /* |
| * Device power supply configuration (Bit 0) |
| * 0: 1.8V - 5.5V internally regulated mode (VCC not connected to VDD) |
| * 1: 1.8V +/- 5% externally regulated mode (VDD and VCC connected together) |
| */ |
| #define DEVICE_CFG3 0x50 |
| |
| /* |
| * Look for Touch/Look for Prox scan refresh time selection. (R/W) |
| * Refresh interval in units of 20 ms. Default to 1. |
| * The valid range of this bit field: 1~25 |
| * Bit 6 and 7 are reserved. |
| */ |
| #define REFRESH_CTRL 0x52 |
| |
| /* |
| * Timeout (in seconds) of no touch activity to trigger (R/W) |
| * Active mode -> Look for Touch mode |
| * Look for Touch mode -> Look for Prox mode |
| * Default value is 63. |
| * The valid range of this bit field: 0~63 |
| * Bit 6 and 7 are reserved. |
| */ |
| #define STATE_TIMEOUT 0x55 |
| |
| /* |
| * Configuration data CRC. (R/W) |
| * CCITT CRC16 checksum for all data from offset 0 to 125. |
| * The valid value of this bit field ranges from 0 to 65535. |
| */ |
| #define CONFIG_CRC 0x7E // 16 bits wide, use word R/W |
| |
| /* |
| * Command to execute. (R/W) |
| * |
| * Write x to CTRL_CMD... |
| * x=2: The device calculates a CRC checksum over the configuration data in |
| * this register map and compares the result with the content of CONFIG_CRC. |
| * If the two values match, the device saves the configuration and the CRC |
| * checksum to nonvolatile memory. |
| * |
| * x=3: The device calculates a CRC checksum over the configuration data in |
| * this register map and places the result in the CALC_CRC register. |
| * |
| * x=255: The device resets itself |
| */ |
| #define CTRL_CMD 0x86 |
| |
| /* |
| * Status returned by the most recently executed command. (R only) |
| * bit 0 indicates status returned by the most recently executed command |
| * 0: no error, 1: an error occurred |
| */ |
| #define CTRL_CMD_STATUS 0x88 |
| |
| /* |
| * Error code returned from most recently executed command. (R only) |
| * 0 : Command was successful |
| * 253: Write to flash failed |
| * 254: Stored configuration CRC checksum (in CONFIG_CRC register) did not |
| * match calculated configuration CRC checksum |
| * 255: Invalid command |
| */ |
| #define CTRL_CMD_ERR 0x89 |
| |
| /* |
| * Configuration data CRC calculated by host command. (R only) |
| */ |
| #define CALC_CRC 0x94 // 16 bits wide, use word R |
| |
| /* |
| * Button status indicators. (R only) |
| * bit x corresponds to CSx. |
| * 0: Not activated, 1: Activated |
| * e.g. a value 6 (0b110) means CS2 and CS1 are activated. |
| */ |
| #define BUTTON_STAT 0xAA |
| |
| /* |
| * Capacitive sensor 0 difference count signal. (R only) |
| * 16 bits wide but the MSB is reserved, so only LSB is used |
| * The valid range of this bit field: 0~255 |
| * difference count address for sensor x is (0xBA + 2*x) |
| */ |
| #define DIFFERENCE_COUNT_SENSOR_BASE 0xBA |
| |
| /* |
| * Maps representing event to be fired when there is activity |
| * on the corresponding CSx. When touch activity occurs, |
| * the IRQ triggers a read of BUTTON_STAT, and the bit x in |
| * the output value corresponds to touch activity on CSx. |
| * |
| * The map to use must be specified in the dts. It is |
| * set with: |
| * sensor_event_map_idx = <map_index_to_use>; |
| */ |
| struct event_map_entry { |
| int bitmask; |
| int event_code; |
| }; |
| |
| static const struct event_map_entry kSensorEventMap0[] = { |
| {0x01, BTN_MIDDLE}, // CS0 --> b00000001 |
| // {0x02, KEY_UNKNOWN}, // CS1 --> b00000010 |
| // {0x04, KEY_UNKNOWN}, // CS2 --> b00000100 |
| // {0x08, KEY_UNKNOWN}, // CS3 --> b00001000 |
| {0x10, BTN_LEFT}, // CS4 --> b00010000 |
| {0x20, BTN_RIGHT}, // CS5 --> b00100000 |
| // {0x40, KEY_UNKNOWN}, // CS6 --> b01000000 |
| // {0x80, KEY_UNKNOWN}, // CS7 --> b10000000 |
| }; |
| |
| static const struct event_map_entry kSensorEventMap1[] = { |
| // {0x01, KEY_UNKNOWN}, // CS0 --> b00000001 |
| // {0x02, KEY_UNKNOWN}, // CS1 --> b00000010 |
| {0x04, BTN_MIDDLE}, // CS2 --> b00000100 |
| // {0x08, KEY_UNKNOWN}, // CS3 --> b00001000 |
| {0x10, BTN_RIGHT}, // CS4 --> b00010000 |
| {0x20, BTN_LEFT}, // CS5 --> b00100000 |
| // {0x40, KEY_UNKNOWN}, // CS6 --> b01000000 |
| // {0x80, KEY_UNKNOWN}, // CS7 --> b10000000 |
| }; |
| |
| static const struct event_map_entry kSensorEventMap2[] = { |
| {0x01, BTN_RIGHT}, // CS0 --> b00000001 |
| // {0x02, KEY_UNKNOWN}, // CS1 --> b00000010 |
| // {0x04, KEY_UNKNOWN}, // CS2 --> b00000100 |
| // {0x08, KEY_UNKNOWN}, // CS3 --> b00001000 |
| {0x10, BTN_MIDDLE}, // CS4 --> b00010000 |
| {0x20, BTN_LEFT}, // CS5 --> b00100000 |
| // {0x40, KEY_UNKNOWN}, // CS6 --> b01000000 |
| // {0x80, KEY_UNKNOWN}, // CS7 --> b10000000 |
| }; |
| |
| static const struct { |
| const struct event_map_entry* event_map; |
| int num_entries; |
| } kSensorEventMaps[] = { |
| {kSensorEventMap0, |
| sizeof(kSensorEventMap0)/sizeof(kSensorEventMap0[0])}, |
| {kSensorEventMap1, |
| sizeof(kSensorEventMap1)/sizeof(kSensorEventMap1[0])}, |
| {kSensorEventMap2, |
| sizeof(kSensorEventMap2)/sizeof(kSensorEventMap2[0])}, |
| }; |
| |
| /* Maps defining the default register values |
| * to be used on the touch controller. On init, |
| * if the checksum in the controller does not |
| * match the expected checksum, the register values |
| * specified will be written. |
| * |
| * The map to use must be specified in the dts. It is |
| * set with: |
| * reg_val_map_idx = <max_idx_to_use>; |
| */ |
| |
| struct reg_val_entry { |
| int reg_address; |
| int reg_value; |
| }; |
| |
| #define kDefaultRegValChecksum0 0xE7FF |
| static const struct reg_val_entry kDefaultRegVals0[] = { |
| {SENSOR_EN, 0x31}, |
| {DEVICE_CFG2, 0x24}, |
| {DEVICE_CFG3, 0x01}, |
| {SENSITIVITY_BASE, 0xFF}, |
| {SENSITIVITY_BASE+1, 0x0F}, |
| {SENSOR_THRESHOLD_BASE, 0x9F}, |
| {SENSOR_THRESHOLD_BASE+4, 0x77}, |
| {SENSOR_THRESHOLD_BASE+5, 0x77}, |
| {REFRESH_CTRL, 0x01}, |
| {STATE_TIMEOUT, 0x3F}, |
| {SENSOR_DEBOUNCE, 0x03}, |
| {BUTTON_HYS, 0x9F}, |
| {BUTTON_LBR, 0x32}, |
| {BUTTON_NNT, 0x33}, |
| {BUTTON_NT, 0x33}, |
| }; |
| |
| #define kDefaultRegValChecksum1 0x2D69 |
| static const struct reg_val_entry kDefaultRegVals1[] = { |
| {SENSOR_EN, 0x34}, |
| {DEVICE_CFG2, 0x24}, |
| {DEVICE_CFG3, 0x01}, |
| {SENSITIVITY_BASE, 0xFF}, |
| {SENSITIVITY_BASE+1, 0x0F}, |
| {SENSOR_THRESHOLD_BASE+2, 0x9F}, |
| {SENSOR_THRESHOLD_BASE+4, 0x9F}, |
| {SENSOR_THRESHOLD_BASE+5, 0x9F}, |
| {REFRESH_CTRL, 0x01}, |
| {STATE_TIMEOUT, 0x3F}, |
| {SENSOR_DEBOUNCE, 0x03}, |
| {BUTTON_HYS, 0x9F}, |
| {BUTTON_LBR, 0x32}, |
| {BUTTON_NNT, 0x33}, |
| {BUTTON_NT, 0x33}, |
| }; |
| |
| #define kDefaultRegValChecksum2 0xD9BD |
| static const struct reg_val_entry kDefaultRegVals2[] = { |
| {SENSOR_EN, 0x31}, |
| {FSS_EN, 0x31}, |
| {DEVICE_CFG2, 0x24}, |
| {DEVICE_CFG3, 0x01}, |
| {SENSITIVITY_BASE, 0x01}, |
| {SENSITIVITY_BASE+1, 0x07}, |
| {SENSOR_THRESHOLD_BASE, 0x80}, |
| {SENSOR_THRESHOLD_BASE+4, 0x9F}, |
| {SENSOR_THRESHOLD_BASE+5, 0x80}, |
| {REFRESH_CTRL, 0x01}, |
| {STATE_TIMEOUT, 0x3F}, |
| {SENSOR_DEBOUNCE, 0x03}, |
| {BUTTON_HYS, 0x9F}, |
| {BUTTON_LBR, 0x32}, |
| {BUTTON_NNT, 0x33}, |
| {BUTTON_NT, 0x33}, |
| }; |
| |
| static const struct { |
| const struct reg_val_entry* reg_val_map; |
| int num_entries; |
| int checksum; |
| } kDefaultRegValMaps[] = { |
| {kDefaultRegVals0, |
| sizeof(kDefaultRegVals0)/sizeof(kDefaultRegVals0[0]), |
| kDefaultRegValChecksum0}, |
| {kDefaultRegVals1, |
| sizeof(kDefaultRegVals1)/sizeof(kDefaultRegVals1[0]), |
| kDefaultRegValChecksum1}, |
| {kDefaultRegVals2, |
| sizeof(kDefaultRegVals2)/sizeof(kDefaultRegVals2[0]), |
| kDefaultRegValChecksum2}, |
| |
| }; |
| |
| struct cy8cmbr31xx { |
| struct i2c_client *client; |
| struct input_dev *input; |
| const struct cy8cmbr31xx_pdata *pdata; |
| char phys[32]; |
| int irq; |
| int reset; |
| }; |
| |
| // return value read (non-negative) else negative errno on error |
| static int cy8cmbr31xx_read_regs_byte(struct i2c_client *client, u8 reg){ |
| int ret, retry = 0; |
| do { |
| ret = i2c_smbus_read_byte_data(client, reg); |
| retry++; |
| } while( (ret < 0) && (retry < MAX_RETRIES) ); |
| |
| if (ret < 0) { |
| dev_err(&client->dev, "%s: failed to read reg 0x%x.\n", |
| __func__, reg); |
| return ret; |
| } |
| |
| dev_dbg(&client->dev, "%s: read 0x%x from register 0x%x\n", __func__, |
| ret, reg); |
| dev_dbg(&client->dev, "%s: retry = %d\n", __func__, retry); |
| return ret; |
| } |
| |
| // return negative errno else zero on success |
| static int cy8cmbr31xx_write_regs_byte(struct i2c_client *client, u8 reg, |
| u8 val) |
| { |
| int ret, retry = 0; |
| do { |
| ret = i2c_smbus_write_byte_data(client, reg, val); |
| retry++; |
| } while( (ret < 0) && (retry < MAX_RETRIES) ); |
| |
| if (ret < 0) { |
| dev_err(&client->dev, "%s: failed to write reg 0x%x.\n", |
| __func__, reg); |
| return ret; |
| } |
| |
| dev_dbg(&client->dev, "%s: wrote 0x%x to register 0x%x\n", __func__, |
| val, reg); |
| dev_dbg(&client->dev, "%s: retry = %d\n", __func__, retry ); |
| |
| return ret; |
| } |
| |
| // return value read (non-negative) else negative errno on error |
| static int cy8cmbr31xx_read_regs_word(struct i2c_client *client, u8 reg){ |
| int ret, retry = 0; |
| do { |
| ret = i2c_smbus_read_word_data(client, reg); |
| retry++; |
| } while( (ret < 0) && (retry < MAX_RETRIES) ); |
| |
| if (ret < 0) { |
| dev_err(&client->dev, "%s: failed to read reg 0x%x.\n", |
| __func__, reg); |
| return ret; |
| } |
| |
| dev_dbg(&client->dev, "%s: read 0x%x from register 0x%x\n", __func__, |
| ret, reg); |
| dev_dbg(&client->dev, "%s: retry = %d\n", __func__, retry ); |
| return ret; |
| } |
| |
| // return negative errno else zero on success |
| static int cy8cmbr31xx_write_regs_word(struct i2c_client *client, u8 reg, |
| u16 val) |
| { |
| int ret, retry = 0; |
| do { |
| ret = i2c_smbus_write_word_data(client, reg, val); |
| retry++; |
| } while( (ret < 0) && (retry < MAX_RETRIES) ); |
| |
| if (ret < 0) { |
| dev_err(&client->dev, "%s: failed to write reg 0x%x, " |
| "ret=%d\n", __func__, reg, ret); |
| return ret; |
| } |
| |
| dev_dbg(&client->dev, "%s: wrote 0x%x to register 0x%x\n", __func__, |
| val, reg); |
| dev_dbg(&client->dev, "%s: retry = %d\n", __func__, retry); |
| |
| return ret; |
| } |
| |
| // must be called each time after writing to CTRL_CMD |
| // return zero on success |
| // positive value on control command error |
| // negative value on reading error |
| static int cy8cmbr31xx_check_control_cmd_status(struct i2c_client *client) { |
| int ret; |
| ret = cy8cmbr31xx_read_regs_byte(client, CTRL_CMD_STATUS); |
| if (ret < 0) { |
| dev_err(&client->dev, "%s: failed to get control command " |
| "status\n", __func__); |
| return ret; |
| } else if (ret & 0x1){ |
| ret = cy8cmbr31xx_read_regs_byte(client, CTRL_CMD_ERR); |
| if (ret > 0) |
| dev_err(&client->dev,"%s: command error. err: %d\n", |
| __func__, ret); |
| return ret; |
| } |
| return 0; |
| } |
| |
| // must be called each time configuration is changed (i.e. write is performed) |
| // return zero on success |
| // positive value on control command error |
| // negative value on r/w error |
| static int cy8cmbr31xx_save_configuration(struct i2c_client *client) { |
| int ret; |
| /* calculate CRC */ |
| ret = cy8cmbr31xx_write_regs_byte(client, CTRL_CMD, 3); |
| if (ret < 0){ |
| dev_err(&client->dev, "%s: failed to write calculate CRC " |
| "command.\n", __func__); |
| return ret; |
| } |
| |
| ret = cy8cmbr31xx_check_control_cmd_status(client); |
| if ( ret != 0) |
| return ret; |
| |
| msleep(1); //to allow calculating CRC |
| |
| /* read out CRC just calculated */ |
| ret = cy8cmbr31xx_read_regs_word(client, CALC_CRC); |
| if (ret < 0){ |
| dev_err(&client->dev,"%s: failed to get calculated CRC\n", |
| __func__); |
| return ret; |
| } |
| |
| /* copy the value over and store in CONFIG_CRC */ |
| ret = cy8cmbr31xx_write_regs_word(client, CONFIG_CRC, ret); |
| if (ret < 0){ |
| dev_err(&client->dev,"%s: failed to set CONFIG_CRC\n", |
| __func__); |
| return ret; |
| } |
| |
| /* compare CRCs and save configuration */ |
| ret = cy8cmbr31xx_write_regs_byte(client, CTRL_CMD, 2); |
| if (ret < 0){ |
| dev_err(&client->dev, "%s: failed to save CRC\n", __func__); |
| return ret; |
| } |
| dev_info(&client->dev, "%s: saving CRC\n", __func__); |
| msleep(1000); |
| ret = cy8cmbr31xx_check_control_cmd_status(client); |
| if ( ret != 0) { |
| dev_err(&client->dev,"%s: failed to save configuration\n", |
| __func__); |
| return ret; |
| } |
| ret = cy8cmbr31xx_write_regs_byte(client, CTRL_CMD, 255); |
| if (ret < 0){ |
| dev_err(&client->dev, "%s: failed to do software reset\n", |
| __func__); |
| return ret; |
| } |
| |
| dev_info(&client->dev, "%s: successfully saved configuration\n", |
| __func__); |
| return 0; |
| } |
| |
| static ssize_t cy8cmbr31xx_show_helper(struct i2c_client *client, |
| char *buf, u8 reg) |
| { |
| int ret; |
| ret = cy8cmbr31xx_read_regs_byte(client, reg); |
| if (ret < 0) { |
| return ret; |
| } |
| return scnprintf(buf, PAGE_SIZE, "%hhu\n", ret); |
| } |
| |
| // use when you only care about few bits (must be consecutive) in that register |
| static ssize_t cy8cmbr31xx_show_helper_masked(struct i2c_client *client, |
| char *buf, u8 reg, u8 mask, u8 effective_lsb) |
| { |
| int ret; |
| ret = cy8cmbr31xx_read_regs_byte(client, reg); |
| if (ret < 0) { |
| return ret; |
| } |
| return scnprintf(buf, PAGE_SIZE, "%hhu\n", |
| (ret & mask) >> effective_lsb); |
| } |
| |
| static ssize_t cy8cmbr31xx_store_helper(struct i2c_client *client, |
| const char *buf, size_t count, u8 reg) |
| { |
| int ret; |
| u8 val; |
| |
| ret = sscanf(buf, "%hhu", &val); |
| if (ret != 1) { |
| dev_err(&client->dev, "%s: failed to parse input value (%s), " |
| "ret=%d.\n", __func__, buf, ret); |
| return -EINVAL; |
| } |
| |
| if (val > 255) { |
| dev_err(&client->dev, |
| "%s: invalid input value (%s), must be in the range of " |
| "[0:255]\n", __func__, buf); |
| return -EINVAL; |
| } |
| |
| ret = cy8cmbr31xx_write_regs_byte(client, reg, val); |
| if (ret < 0) |
| return ret; |
| |
| ret = cy8cmbr31xx_save_configuration(client); |
| if (ret) |
| return ret; |
| return count; |
| } |
| |
| // use when you only modify few bits (must be consecutive) in that register |
| static ssize_t cy8cmbr31xx_store_helper_masked(struct i2c_client *client, |
| const char *buf, size_t count, u8 reg, u8 mask, u8 effective_lsb) |
| { |
| int ret; |
| u8 val; |
| |
| ret = sscanf(buf, "%hhu", &val); |
| if (ret != 1) { |
| dev_err(&client->dev, "%s: failed to parse input value (%s), " |
| "ret=%d.\n", __func__, buf, ret); |
| return -EINVAL; |
| } |
| |
| if (val > (mask >> effective_lsb)) { |
| dev_err(&client->dev, |
| "%s: invalid input value (%s), must be in the range of " |
| "[0:%d]\n", __func__, buf, mask >> effective_lsb); |
| return -EINVAL; |
| } |
| |
| ret = cy8cmbr31xx_read_regs_byte(client, reg); |
| if (ret < 0) |
| return ret; |
| |
| val = (ret & ~mask) | (val << effective_lsb); |
| // config unchanged |
| if (val == ret) |
| return count; |
| |
| ret = cy8cmbr31xx_write_regs_byte(client, reg, val); |
| if (ret < 0) |
| return ret; |
| |
| ret = cy8cmbr31xx_save_configuration(client); |
| if (ret) |
| return ret; |
| return count; |
| } |
| |
| static ssize_t cy8cmbr31xx_sensor_threshold_show(struct device *dev, |
| struct device_attribute *attr, char *buf){ |
| struct i2c_client *client = to_i2c_client(dev); |
| struct cy8cmbr31xx *ts = i2c_get_clientdata(client); |
| unsigned int sensor_index; |
| int ret = sscanf(attr->attr.name, "thold%u", &sensor_index); |
| if (ret != 1) { |
| dev_err(dev, "%s: failed to get sensor index.\n", __func__); |
| return -EINVAL; |
| } |
| |
| if (sensor_index >= ts->pdata->num_sensors) { |
| dev_err(dev, "%s: invalid sensor index: %d\n", __func__, |
| sensor_index); |
| return -EINVAL; |
| } |
| |
| return cy8cmbr31xx_show_helper(client, buf, |
| SENSOR_THRESHOLD_BASE+sensor_index); |
| } |
| |
| static ssize_t cy8cmbr31xx_sensor_threshold_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count){ |
| struct i2c_client *client = to_i2c_client(dev); |
| struct cy8cmbr31xx *ts = i2c_get_clientdata(client); |
| unsigned int sensor_index; |
| int ret = sscanf(attr->attr.name, "thold%u", &sensor_index); |
| if (ret != 1) { |
| dev_err(dev, "%s: failed to get sensor index.\n", __func__); |
| return -EINVAL; |
| } |
| |
| if (sensor_index >= ts->pdata->num_sensors) { |
| dev_err(dev, "%s: invalid sensor index: %d\n", __func__, |
| sensor_index); |
| return -EINVAL; |
| } |
| |
| return cy8cmbr31xx_store_helper(client, buf, count, |
| SENSOR_THRESHOLD_BASE+sensor_index); |
| } |
| |
| static ssize_t cy8cmbr31xx_sensitivity_show(struct device *dev, |
| struct device_attribute *attr, char *buf){ |
| struct i2c_client *client = to_i2c_client(dev); |
| struct cy8cmbr31xx *ts = i2c_get_clientdata(client); |
| unsigned int sensor_index; |
| int ret = sscanf(attr->attr.name, "sens%u", &sensor_index); |
| if (ret != 1) { |
| dev_err(dev, "%s: failed to get sensor index.\n", __func__); |
| return -EINVAL; |
| } |
| |
| if (sensor_index >= ts->pdata->num_sensors) { |
| dev_err(dev, "%s: invalid sensor index: %d\n", __func__, |
| sensor_index); |
| return -EINVAL; |
| } |
| |
| return cy8cmbr31xx_show_helper_masked(client, buf, |
| SENSITIVITY_BASE + sensor_index / 4, |
| 0x03 << (2 * (sensor_index % 4)), 2 * (sensor_index % 4)); |
| } |
| |
| static ssize_t cy8cmbr31xx_sensitivity_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count){ |
| struct i2c_client *client = to_i2c_client(dev); |
| struct cy8cmbr31xx *ts = i2c_get_clientdata(client); |
| unsigned int sensor_index; |
| int ret = sscanf(attr->attr.name, "sens%u", &sensor_index); |
| if (ret != 1) { |
| dev_err(dev, "%s: failed to get sensor index.\n", __func__); |
| return -EINVAL; |
| } |
| |
| if (sensor_index >= ts->pdata->num_sensors) { |
| dev_err(dev, "%s: invalid sensor index: %d\n", __func__, |
| sensor_index); |
| return -EINVAL; |
| } |
| |
| return cy8cmbr31xx_store_helper_masked(client, buf, count, |
| SENSITIVITY_BASE + sensor_index / 4, |
| 0x03 << (2 * (sensor_index % 4)), 2 * (sensor_index % 4)); |
| } |
| |
| static ssize_t cy8cmbr31xx_sensor_count_show(struct device *dev, |
| struct device_attribute *attr, char *buf){ |
| struct i2c_client *client = to_i2c_client(dev); |
| struct cy8cmbr31xx *ts = i2c_get_clientdata(client); |
| unsigned int sensor_index; |
| int ret = sscanf(attr->attr.name, "count%u", &sensor_index); |
| if (ret != 1) { |
| dev_err(dev, "%s: failed to get sensor index.\n", __func__); |
| return -EINVAL; |
| } |
| |
| if (sensor_index >= ts->pdata->num_sensors) { |
| dev_err(dev, "%s: invalid sensor index: %d\n", __func__, |
| sensor_index); |
| return -EINVAL; |
| } |
| |
| return cy8cmbr31xx_show_helper(client, buf, |
| DIFFERENCE_COUNT_SENSOR_BASE+2*sensor_index); |
| } |
| |
| static struct attribute *all_attrs[MAX_NUM_SENSORS * 3 + 1]; |
| static struct attribute_group cy8cmbr31xx_sensors_attr_group = { |
| .attrs = all_attrs, |
| }; |
| static struct device_attribute sensors_attrs[MAX_NUM_SENSORS * 3]; |
| |
| static void cy8cmbr31xx_construct_sensors_attributes(int num_sensors, |
| struct kobject *kobj) |
| { |
| int i, error; |
| for (i = 0; i < 3 * num_sensors; ++i) { |
| char buf[8]; |
| char* name; |
| if (i < num_sensors) |
| snprintf(buf, sizeof(buf), "count%u", i); |
| else if (i < 2 * num_sensors) |
| snprintf(buf, sizeof(buf), "thold%u", i - num_sensors); |
| else |
| snprintf(buf, sizeof(buf), "sens%u", i - 2 * num_sensors); |
| |
| name = kzalloc(strlen(buf) + 1, GFP_KERNEL); |
| if (name == NULL) |
| return; |
| strncpy(name, buf, strlen(buf) + 1); |
| |
| sensors_attrs[i].attr.name = name; |
| if (i < num_sensors) { //count (R only) |
| sensors_attrs[i].attr.mode = S_IRUGO; |
| sensors_attrs[i].show = cy8cmbr31xx_sensor_count_show; |
| } else if (i < 2 * num_sensors){ //thold |
| sensors_attrs[i].attr.mode = S_IWUSR | S_IWGRP | S_IRUGO; |
| sensors_attrs[i].show = cy8cmbr31xx_sensor_threshold_show; |
| sensors_attrs[i].store = cy8cmbr31xx_sensor_threshold_store; |
| } else { //sens |
| sensors_attrs[i].attr.mode = S_IWUSR | S_IWGRP | S_IRUGO; |
| sensors_attrs[i].show = cy8cmbr31xx_sensitivity_show; |
| sensors_attrs[i].store = cy8cmbr31xx_sensitivity_store; |
| } |
| all_attrs[i] = &sensors_attrs[i].attr; |
| } |
| error = sysfs_create_group(kobj, &cy8cmbr31xx_sensors_attr_group); |
| if (error) |
| printk("%s: failed to create sysfs, err:%d\n", __func__, error); |
| return; |
| } |
| |
| static ssize_t cy8cmbr31xx_reset_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count){ |
| |
| int error; |
| struct i2c_client *client = to_i2c_client(dev); |
| struct cy8cmbr31xx *ts = i2c_get_clientdata(client); |
| |
| error = gpio_request(ts->reset, "TouchRST"); |
| if (error) { |
| dev_err(dev, "%s: failed to request GPIO%d.\n", |
| __func__, ts->reset); |
| return error; |
| } |
| |
| error = gpio_direction_output(ts->reset, 1); |
| if (error) { |
| dev_err(dev, "%s: failed to set gpio to output high.\n", |
| __func__); |
| gpio_free(ts->reset); |
| return error; |
| } |
| |
| dev_info(dev, "%s: resetting touch controller...\n", __func__); |
| gpio_set_value(ts->reset, 0); |
| msleep(1); |
| gpio_set_value(ts->reset, 1); |
| gpio_free(ts->reset); |
| return count; |
| } |
| |
| static const struct map_entry { |
| const char* name; |
| const u8 reg; |
| const u8 mask; |
| const u8 lsb; |
| } kNameRegisterMaskLsbMap[] = { |
| // Must keep in alphabetical order to use bsearch |
| /* attr.name register mask lsb */ |
| {"auto_reset", DEVICE_CFG2, 0x30, 4}, |
| {"auto_threshold", DEVICE_CFG2, 0x08, 3}, |
| {"debounce", SENSOR_DEBOUNCE, 0x0F, 0}, |
| {"emc", DEVICE_CFG2, 0x04, 2}, |
| {"fss_en", FSS_EN, 0xFF, 0}, |
| {"hys_override", BUTTON_HYS, 0x80, 7}, |
| {"hys_threshold", BUTTON_HYS, 0x1F, 0}, |
| {"iir_filter", DEVICE_CFG0, 0x02, 1}, |
| {"lbr_override", BUTTON_LBR, 0x80, 7}, |
| {"lbr_threshold", BUTTON_LBR, 0x7F, 0}, |
| {"median_filter", DEVICE_CFG0, 0x01, 0}, |
| {"nnt_override", BUTTON_NNT, 0x80, 7}, |
| {"nnt_threshold", BUTTON_NNT, 0x7F, 0}, |
| {"nt_override", BUTTON_NT, 0x80, 7}, |
| {"nt_threshold", BUTTON_NT, 0x7F, 0}, |
| {"pwr_supply_cfg", DEVICE_CFG3, 0x01, 0}, |
| {"refresh_ctrl", REFRESH_CTRL, 0x3F, 0}, |
| {"state_timeout", STATE_TIMEOUT, 0x3F, 0}, |
| }; |
| |
| /* |
| * Value of MAX_REGISTER_MAP_LENGTH must be at least the length |
| * of kNameRegisterMaskLsbMap. |
| */ |
| #define MAX_REGISTER_MAP_LENGTH ARRAY_SIZE(kNameRegisterMaskLsbMap) |
| const size_t kMapLength = ARRAY_SIZE(kNameRegisterMaskLsbMap); |
| |
| static int compare_map_entry(const void *e1, const void *e2) { |
| struct map_entry *entry1 = (struct map_entry *) e1; |
| struct map_entry *entry2 = (struct map_entry *) e2; |
| return strcmp(entry1->name, entry2->name); |
| } |
| |
| static ssize_t cy8cmbr31xx_common_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct i2c_client *client = to_i2c_client(dev); |
| struct map_entry key = { |
| .name = attr->attr.name, |
| }; |
| struct map_entry *res; |
| res = bsearch(&key, kNameRegisterMaskLsbMap, kMapLength, |
| sizeof(kNameRegisterMaskLsbMap[0]), compare_map_entry); |
| if (res == NULL) { |
| printk("%s: attribute name (%s) cannot be found!\n", __func__, |
| key.name); |
| return -ENOENT; |
| } |
| return cy8cmbr31xx_show_helper_masked(client, buf, res->reg, res->mask, |
| res->lsb); |
| } |
| |
| static ssize_t cy8cmbr31xx_common_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count) { |
| struct i2c_client *client = to_i2c_client(dev); |
| struct map_entry key, *res; |
| key.name = attr->attr.name; |
| res = bsearch(&key, kNameRegisterMaskLsbMap, kMapLength, |
| sizeof(struct map_entry), compare_map_entry); |
| if (res == NULL) { |
| printk("%s: attribute name (%s) cannot be found!\n", __func__, |
| key.name); |
| return -ENOENT; |
| } |
| return cy8cmbr31xx_store_helper_masked(client, buf, count, res->reg, |
| res->mask, res->lsb); |
| } |
| |
| static struct attribute *all_common_attrs[MAX_REGISTER_MAP_LENGTH + 2]; |
| static struct attribute_group cy8cmbr31xx_common_attr_group = { |
| .attrs = all_common_attrs, |
| }; |
| static struct device_attribute common_attrs[MAX_REGISTER_MAP_LENGTH + 1]; |
| |
| static void cy8cmbr31xx_construct_common_attributes(struct kobject *kobj) |
| { |
| int i, error; |
| for (i = 0; i < kMapLength; ++i) { |
| common_attrs[i].attr.name = kNameRegisterMaskLsbMap[i].name; |
| common_attrs[i].attr.mode = S_IWUSR | S_IWGRP | S_IRUGO; |
| common_attrs[i].show = cy8cmbr31xx_common_show; |
| common_attrs[i].store = cy8cmbr31xx_common_store; |
| all_common_attrs[i] = &common_attrs[i].attr; |
| } |
| // The last one is for reset |
| common_attrs[i].attr.name = "reset"; |
| common_attrs[i].attr.mode = S_IWUSR | S_IWGRP | S_IRUGO; |
| common_attrs[i].show = NULL; |
| common_attrs[i].store = cy8cmbr31xx_reset_store; |
| all_common_attrs[i] = &common_attrs[i].attr; |
| |
| error = sysfs_create_group(kobj, &cy8cmbr31xx_common_attr_group); |
| if (error) |
| printk("%s: failed to create sysfs, err:%d\n", __func__, error); |
| return; |
| } |
| |
| static void cy8cmbr31xx_send_events(struct input_dev *input, int stat_val, |
| int sensor_event_map_idx) |
| { |
| int cs_idx; |
| int input_val = 0; |
| int cs_with_touch = 0; |
| int num_cs = kSensorEventMaps[sensor_event_map_idx].num_entries; |
| for (cs_idx = 0; cs_idx < num_cs; ++cs_idx) { |
| input_val = kSensorEventMaps[sensor_event_map_idx].event_map[cs_idx].bitmask & stat_val; |
| cs_with_touch |= input_val; |
| input_report_key(input, kSensorEventMaps[sensor_event_map_idx].event_map[cs_idx].event_code, |
| input_val); |
| } |
| input_sync(input); |
| |
| // If the |cs_with_touch| value does not equal |stat_val|, this |
| // implies that a touch occurred on a cap sensor channel not |
| // covered in the |kSensorEventMap|. |
| if (cs_with_touch != stat_val) { |
| printk("%s: unexpected output from touch controller, val:%d", |
| __func__, stat_val); |
| } |
| } |
| |
| static void cy8cmbr31xx_report_touch(struct cy8cmbr31xx *ts) |
| { |
| struct i2c_client *client = ts->client; |
| struct input_dev *input = ts->input; |
| int stat_val; |
| stat_val = cy8cmbr31xx_read_regs_byte(client, BUTTON_STAT); |
| |
| if (stat_val >= 0) { |
| cy8cmbr31xx_send_events(input, stat_val, ts->pdata->sensor_event_map_idx); |
| } |
| } |
| |
| static irqreturn_t cy8cmbr31xx_interrupt(int irq, void *handle) |
| { |
| struct cy8cmbr31xx *ts = handle; |
| cy8cmbr31xx_report_touch(ts); |
| return IRQ_HANDLED; |
| } |
| |
| |
| #ifdef CONFIG_OF |
| static struct cy8cmbr31xx_pdata *cy8cmbr31xx_parse_devtree( |
| struct i2c_client *client) |
| { |
| struct device *dev = &client->dev; |
| struct device_node *node; |
| struct cy8cmbr31xx_pdata *pdata; |
| int touch_gpio = -1; |
| int reset_gpio = -1; |
| int error = 0; |
| |
| node = dev->of_node; |
| if (!node) { |
| dev_err(dev, "%s: of_node is NULL.\n", __func__); |
| return ERR_PTR(-ENODEV); |
| } |
| |
| pdata = devm_kzalloc(dev, sizeof(struct device_node), GFP_KERNEL); |
| if (!pdata) { |
| dev_err(dev, "%s: not enough memory left.\n", __func__); |
| return ERR_PTR(-ENOMEM); |
| } |
| |
| if (of_property_read_u32(node, "num_sensors", &pdata->num_sensors)) { |
| dev_err(dev, "%s: failed to get number of sensors.\n", |
| __func__); |
| devm_kfree(dev, pdata); |
| return ERR_PTR(-EINVAL); |
| } |
| |
| if (of_property_read_u32(node, "sensor_event_map_idx", &pdata->sensor_event_map_idx)) { |
| dev_err(dev, "%s: failed to get sensor event map number.\n", |
| __func__); |
| devm_kfree(dev, pdata); |
| return ERR_PTR(-EINVAL); |
| } |
| |
| if (of_property_read_u32(node, "reg_val_map_idx", &pdata->reg_val_map_idx)) { |
| dev_err(dev, "%s: failed to get register value map number.\n", |
| __func__); |
| devm_kfree(dev, pdata); |
| return ERR_PTR(-EINVAL); |
| } |
| |
| if (pdata->sensor_event_map_idx >= |
| sizeof(kSensorEventMaps)/sizeof(kSensorEventMaps[0]) || |
| pdata->sensor_event_map_idx < 0) { |
| dev_err(dev, "%s: sensor event map number is invalid, %d", |
| __func__, pdata->sensor_event_map_idx); |
| return ERR_PTR(-EINVAL); |
| } |
| |
| /* interrupt gpio. */ |
| touch_gpio = of_get_named_gpio(node, "touch_gpio", 0); |
| if (!gpio_is_valid(touch_gpio)) |
| goto exit; |
| |
| error = gpio_request(touch_gpio, "TouchIRQ"); |
| |
| if (error) |
| goto exit; |
| |
| error = gpio_direction_input(touch_gpio); |
| if (error) { |
| gpio_free(touch_gpio); |
| goto exit; |
| } |
| pdata->irq = gpio_to_irq(touch_gpio); |
| if (pdata->irq < 0) { |
| dev_err(dev, "%s: failed to get irq number for GPIO%d. " |
| "err:%d.\n", __func__, touch_gpio, pdata->irq); |
| goto exit; |
| } |
| |
| /* reset gpio */ |
| reset_gpio = of_get_named_gpio(node, "reset_gpio", 0); |
| if (!gpio_is_valid(reset_gpio)) |
| goto exit; |
| |
| pdata->reset = reset_gpio; |
| |
| return pdata; |
| exit: |
| if (touch_gpio != -1) |
| gpio_free(touch_gpio); |
| dev_err(dev, "%s: Failed to request gpio\n", __func__); |
| return ERR_PTR(-ENODEV); |
| } |
| #endif |
| |
| // return zero on success |
| // positive value on control command error |
| // negative value on r/w error |
| static int cy8cmbr31xx_init(struct cy8cmbr31xx *ts) |
| { |
| int ret, i, num_regs, reg_val_map_idx, checksum; |
| int reg_address, reg_value; |
| struct i2c_client *client = ts->client; |
| |
| reg_val_map_idx = ts->pdata->reg_val_map_idx; |
| ret = cy8cmbr31xx_read_regs_word(client, CONFIG_CRC); |
| // If the CRC checksum matches the expected value, |
| // the register values don't need to be updated |
| if (ret == kDefaultRegValMaps[reg_val_map_idx].checksum) { |
| dev_info(&client->dev, "%s: default reg values are already " |
| "stored in the IC. No updates required\n", __func__); |
| return 0; |
| } |
| if (ret > 0) { |
| dev_info(&client->dev, "%s: config CRC (0x%X) does not match " |
| "the expected value (0x%X). Need to reconfigure regs.\n", __func__, |
| ret, kDefaultRegValMaps[reg_val_map_idx].checksum); |
| } |
| |
| // Configure all registers specified in the default reg val maps |
| num_regs = kDefaultRegValMaps[reg_val_map_idx].num_entries; |
| for (i = 0; i < num_regs; ++i) { |
| reg_address = kDefaultRegValMaps[reg_val_map_idx].reg_val_map[i].reg_address; |
| reg_value = kDefaultRegValMaps[reg_val_map_idx].reg_val_map[i].reg_value; |
| ret = cy8cmbr31xx_write_regs_byte(client, reg_address, reg_value); |
| if (ret < 0) { |
| dev_err(&client->dev, "%s: failed to configure register " |
| "0x%02X. Return value %d", __func__, reg_address, |
| ret); |
| return ret; |
| } |
| } |
| |
| ret = cy8cmbr31xx_save_configuration(client); |
| checksum = cy8cmbr31xx_read_regs_word(client, CONFIG_CRC); |
| dev_info(&client->dev, "%s: Updated checksum is 0x%X\n", |
| __func__, checksum); |
| |
| return ret; |
| } |
| |
| // return zero on success |
| static int cy8cmbr31xx_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| struct cy8cmbr31xx *ts; |
| struct device *dev = &client->dev; |
| const struct cy8cmbr31xx_pdata *pdata = dev_get_platdata(dev); |
| struct input_dev *input_dev; |
| int error = 0; |
| |
| if (!pdata) { |
| pdata = cy8cmbr31xx_parse_devtree(client); |
| if (IS_ERR(pdata)) { |
| dev_err(dev, |
| "%s: failed to get device data from device tree.\n", |
| __func__); |
| error = -EINVAL; |
| goto err_get_pdata; |
| } |
| } |
| |
| ts = kzalloc(sizeof(struct cy8cmbr31xx), GFP_KERNEL); |
| input_dev = input_allocate_device(); |
| if (!ts || !input_dev) { |
| dev_err(&client->dev, "%s: No enough memory.\n", __func__); |
| error = -ENOMEM; |
| goto err_free_mem; |
| } |
| |
| ts->client = client; |
| ts->input = input_dev; |
| ts->pdata = pdata; |
| ts->irq = pdata->irq; |
| ts->reset = pdata->reset; |
| |
| snprintf(ts->phys, sizeof(ts->phys), "%s/input0", |
| dev_name(&client->dev)); |
| |
| input_dev->name = CY8CMBR31XX_DRIVER_NAME; |
| input_dev->phys = ts->phys; |
| input_dev->id.bustype = BUS_I2C; |
| |
| input_dev->evbit[0] = BIT_MASK(EV_KEY); |
| input_set_capability(input_dev, EV_KEY, BTN_LEFT); |
| input_set_capability(input_dev, EV_KEY, BTN_RIGHT); |
| input_set_capability(input_dev, EV_KEY, BTN_MIDDLE); |
| |
| input_set_drvdata(input_dev, ts); |
| i2c_set_clientdata(client, ts); |
| |
| error = cy8cmbr31xx_init(ts); |
| if (error) { |
| dev_err(&client->dev, "%s: init failed...\n", __func__ ); |
| goto err_free_mem; |
| } |
| |
| cy8cmbr31xx_construct_sensors_attributes(pdata->num_sensors, |
| &client->dev.kobj); |
| cy8cmbr31xx_construct_common_attributes(&client->dev.kobj); |
| |
| error = input_register_device(input_dev); |
| if (error) { |
| dev_err(&client->dev, |
| "%s: failed to register input_dev, err:%d\n", |
| __func__, error); |
| goto err_remove_sysfs; |
| } |
| |
| error = request_threaded_irq(pdata->irq, NULL, cy8cmbr31xx_interrupt, |
| IRQF_ONESHOT | IRQF_TRIGGER_RISING, |
| client->name, ts); |
| |
| if (error) { |
| dev_err(&client->dev, "%s: failed to request IRQ %d, err:%d\n", |
| __func__, ts->irq, error); |
| goto err_unregister_device; |
| } |
| |
| dev_info(&client->dev, "%s: probe successfully!\n", __func__); |
| return 0; |
| |
| err_unregister_device: |
| input_unregister_device(input_dev); |
| input_dev = NULL; |
| err_remove_sysfs: |
| sysfs_remove_group(&client->dev.kobj, &cy8cmbr31xx_common_attr_group); |
| sysfs_remove_group(&client->dev.kobj, &cy8cmbr31xx_sensors_attr_group); |
| err_free_mem: |
| input_free_device(input_dev); |
| kfree(ts); |
| err_get_pdata: |
| return error; |
| } |
| |
| static int cy8cmbr31xx_remove(struct i2c_client *client) |
| { |
| return 0; |
| } |
| |
| static void cy8cmbr31xx_shutdown(struct i2c_client *client) |
| { |
| struct cy8cmbr31xx *ts = i2c_get_clientdata(client); |
| dev_info(&client->dev, "%s: prepare to shutdown device.\n", __func__); |
| |
| sysfs_remove_group(&client->dev.kobj, &cy8cmbr31xx_common_attr_group); |
| sysfs_remove_group(&client->dev.kobj, &cy8cmbr31xx_sensors_attr_group); |
| kfree(ts); |
| } |
| |
| static const struct i2c_device_id cy8cmbr31xx_idtable[] = { |
| { CY8CMBR31XX_DRIVER_NAME, 0 }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(i2c, cy8cmbr31xx_idtable); |
| |
| #ifdef CONFIG_OF |
| static const struct of_device_id cy8cmbr31xx_of_match[] = { |
| { .compatible = CY8CMBR31XX_DRIVER_NAME }, |
| { } |
| }; |
| #endif |
| |
| static struct i2c_driver cy8cmbr31xx_driver = { |
| .driver = { |
| .name = CY8CMBR31XX_DRIVER_NAME, |
| .owner = THIS_MODULE, |
| .pm = NULL, |
| .of_match_table = of_match_ptr(cy8cmbr31xx_of_match), |
| }, |
| .probe = cy8cmbr31xx_probe, |
| .remove = cy8cmbr31xx_remove, |
| .shutdown = cy8cmbr31xx_shutdown, |
| .id_table = cy8cmbr31xx_idtable, |
| }; |
| |
| module_i2c_driver(cy8cmbr31xx_driver); |
| |
| MODULE_AUTHOR("Yi-Chun Gina Ko <yichunko@google.com>"); |
| MODULE_DESCRIPTION("CYPRESS CY8CMBR31XX CAPSENSE TOUCH driver"); |
| MODULE_LICENSE("GPL"); |