/* * Copyright (c) 2024 Ilia Kharin * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT cirque_pinnacle #include #include #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) #include #endif #if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) #include #endif #include #include #include #include #include LOG_MODULE_REGISTER(pinnacle, CONFIG_INPUT_LOG_LEVEL); /* * Register Access Protocol Standard Registers. * Standard registers have 5-bit addresses, BIT[4:0], that range from * 0x00 to 0x1F. For reading, a register address has to be combined with * 0xA0 for reading and 0x80 for writing bits, BIT[7:5]. */ #define PINNACLE_REG_FIRMWARE_ID 0x00 /* R */ #define PINNACLE_REG_FIRMWARE_VERSION 0x01 /* R */ #define PINNACLE_REG_STATUS1 0x02 /* R/W */ #define PINNACLE_REG_SYS_CONFIG1 0x03 /* R/W */ #define PINNACLE_REG_FEED_CONFIG1 0x04 /* R/W */ #define PINNACLE_REG_FEED_CONFIG2 0x05 /* R/W */ #define PINNACLE_REG_FEED_CONFIG3 0x06 /* R/W */ #define PINNACLE_REG_CAL_CONFIG1 0x07 /* R/W */ #define PINNACLE_REG_PS2_AUX_CONTROL 0x08 /* R/W */ #define PINNACLE_REG_SAMPLE_RATE 0x09 /* R/W */ #define PINNACLE_REG_Z_IDLE 0x0A /* R/W */ #define PINNACLE_REG_Z_SCALER 0x0B /* R/W */ #define PINNACLE_REG_SLEEP_INTERVAL 0x0C /* R/W */ #define PINNACLE_REG_SLEEP_TIMER 0x0D /* R/W */ #define PINNACLE_REG_EMI_THRESHOLD 0x0E /* R/W */ #define PINNACLE_REG_PACKET_BYTE0 0x12 /* R */ #define PINNACLE_REG_PACKET_BYTE1 0x13 /* R */ #define PINNACLE_REG_PACKET_BYTE2 0x14 /* R */ #define PINNACLE_REG_PACKET_BYTE3 0x15 /* R */ #define PINNACLE_REG_PACKET_BYTE4 0x16 /* R */ #define PINNACLE_REG_PACKET_BYTE5 0x17 /* R */ #define PINNACLE_REG_GPIO_A_CTRL 0x18 /* R/W */ #define PINNACLE_REG_GPIO_A_DATA 0x19 /* R/W */ #define PINNACLE_REG_GPIO_B_CTRL_DATA 0x1A /* R/W */ /* Value of the extended register */ #define PINNACLE_REG_ERA_VALUE 0x1B /* R/W */ /* High byte BIT[15:8] of the 16 bit extended register */ #define PINNACLE_REG_ERA_ADDR_HIGH 0x1C /* R/W */ /* Low byte BIT[7:0] of the 16 bit extended register */ #define PINNACLE_REG_ERA_ADDR_LOW 0x1D /* R/W */ #define PINNACLE_REG_ERA_CTRL 0x1E /* R/W */ #define PINNACLE_REG_PRODUCT_ID 0x1F /* R */ /* Extended Register Access */ #define PINNACLE_ERA_REG_CONFIG 0x0187 /* R/W */ /* Firmware ASIC ID value */ #define PINNACLE_FIRMWARE_ID 0x07 /* Status1 definition */ #define PINNACLE_STATUS1_SW_DR BIT(2) #define PINNACLE_STATUS1_SW_CC BIT(3) /* SysConfig1 definition */ #define PINNACLE_SYS_CONFIG1_RESET BIT(0) #define PINNACLE_SYS_CONFIG1_SHUTDOWN BIT(1) #define PINNACLE_SYS_CONFIG1_LOW_POWER_MODE BIT(2) /* FeedConfig1 definition */ #define PINNACLE_FEED_CONFIG1_FEED_ENABLE BIT(0) #define PINNACLE_FEED_CONFIG1_DATA_MODE_ABSOLUTE BIT(1) #define PINNACLE_FEED_CONFIG1_FILTER_DISABLE BIT(2) #define PINNACLE_FEED_CONFIG1_X_DISABLE BIT(3) #define PINNACLE_FEED_CONFIG1_Y_DISABLE BIT(4) #define PINNACLE_FEED_CONFIG1_X_INVERT BIT(6) #define PINNACLE_FEED_CONFIG1_Y_INVERT BIT(7) /* X max to 0 */ #define PINNACLE_FEED_CONFIG1_X_DATA_INVERT BIT(6) /* Y max to 0 */ #define PINNACLE_FEED_CONFIG1_Y_DATA_INVERT BIT(7) /* FeedConfig2 definition */ #define PINNACLE_FEED_CONFIG2_INTELLIMOUSE_ENABLE BIT(0) #define PINNACLE_FEED_CONFIG2_ALL_TAPS_DISABLE BIT(1) #define PINNACLE_FEED_CONFIG2_SECONDARY_TAP_DISABLE BIT(2) #define PINNACLE_FEED_CONFIG2_SCROLL_DISABLE BIT(3) #define PINNACLE_FEED_CONFIG2_GLIDE_EXTEND_DISABLE BIT(4) /* 90 degrees rotation */ #define PINNACLE_FEED_CONFIG2_SWAP_X_AND_Y BIT(7) /* Relative position status in PacketByte0 */ #define PINNACLE_PACKET_BYTE0_BTN_PRIMARY BIT(0) #define PINNACLE_PACKET_BYTE0_BTN_SECONDRY BIT(1) /* Extended Register Access Control */ #define PINNACLE_ERA_CTRL_READ BIT(0) #define PINNACLE_ERA_CTRL_WRITE BIT(1) #define PINNACLE_ERA_CTRL_READ_AUTO_INC BIT(2) #define PINNACLE_ERA_CTRL_WRITE_AUTO_INC BIT(3) /* Asserting both BIT(1) and BIT(0) means WRITE/Verify */ #define PINNACLE_ERA_CTRL_WRITE_VERIFY (BIT(1) | BIT(0)) #define PINNACLE_ERA_CTRL_COMPLETE 0x00 /* Extended Register Access Config */ #define PINNACLE_ERA_CONFIG_ADC_ATTENUATION_X1 0x00 #define PINNACLE_ERA_CONFIG_ADC_ATTENUATION_X2 0x40 #define PINNACLE_ERA_CONFIG_ADC_ATTENUATION_X3 0x80 #define PINNACLE_ERA_CONFIG_ADC_ATTENUATION_X4 0xC0 /* * Delay and retry count for waiting completion of calibration with 200 ms of * timeout. */ #define PINNACLE_CALIBRATION_AWAIT_DELAY_POLL_US 50000 #define PINNACLE_CALIBRATION_AWAIT_RETRY_COUNT 4 /* * Delay and retry count for waiting completion of ERA command with 50 ms of * timeout. */ #define PINNACLE_ERA_AWAIT_DELAY_POLL_US 10000 #define PINNACLE_ERA_AWAIT_RETRY_COUNT 5 /* Special definitions */ #define PINNACLE_SPI_FB 0xFB /* Filler byte */ #define PINNACLE_SPI_FC 0xFC /* Auto-increment byte */ /* Read and write masks */ #define PINNACLE_READ_MSK 0xA0 #define PINNACLE_WRITE_MSK 0x80 /* Read and write register addresses */ #define PINNACLE_READ_REG(addr) (PINNACLE_READ_MSK | addr) #define PINNACLE_WRITE_REG(addr) (PINNACLE_WRITE_MSK | addr) struct pinnacle_bus { union { #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) struct i2c_dt_spec i2c; #endif #if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) struct spi_dt_spec spi; #endif }; bool (*is_ready)(const struct pinnacle_bus *bus); int (*write)(const struct pinnacle_bus *bus, uint8_t address, uint8_t value); int (*seq_write)(const struct pinnacle_bus *bus, uint8_t *address, uint8_t *value, uint8_t count); int (*read)(const struct pinnacle_bus *bus, uint8_t address, uint8_t *value); int (*seq_read)(const struct pinnacle_bus *bus, uint8_t address, uint8_t *data, uint8_t count); }; enum pinnacle_sensitivity { PINNACLE_SENSITIVITY_X1, PINNACLE_SENSITIVITY_X2, PINNACLE_SENSITIVITY_X3, PINNACLE_SENSITIVITY_X4, }; struct pinnacle_config { const struct pinnacle_bus bus; struct gpio_dt_spec dr_gpio; enum pinnacle_sensitivity sensitivity; bool relative_mode; uint8_t idle_packets_count; bool clipping_enabled; bool scaling_enabled; bool invert_x; bool invert_y; bool primary_tap_enabled; bool swap_xy; uint16_t active_range_x_min; uint16_t active_range_x_max; uint16_t active_range_y_min; uint16_t active_range_y_max; uint16_t resolution_x; uint16_t resolution_y; }; union pinnacle_sample { struct { uint16_t abs_x; uint16_t abs_y; uint8_t abs_z; }; struct { int16_t rel_x; int16_t rel_y; bool btn_primary; }; }; struct pinnacle_data { union pinnacle_sample sample; const struct device *dev; struct gpio_callback dr_cb_data; struct k_work work; }; static inline bool pinnacle_bus_is_ready(const struct device *dev) { const struct pinnacle_config *config = dev->config; return config->bus.is_ready(&config->bus); } static inline int pinnacle_write(const struct device *dev, uint8_t address, uint8_t value) { const struct pinnacle_config *config = dev->config; return config->bus.write(&config->bus, address, value); } static inline int pinnacle_seq_write(const struct device *dev, uint8_t *address, uint8_t *value, uint8_t count) { const struct pinnacle_config *config = dev->config; return config->bus.seq_write(&config->bus, address, value, count); } static inline int pinnacle_read(const struct device *dev, uint8_t address, uint8_t *value) { const struct pinnacle_config *config = dev->config; return config->bus.read(&config->bus, address, value); } static inline int pinnacle_seq_read(const struct device *dev, uint8_t address, uint8_t *data, uint8_t count) { const struct pinnacle_config *config = dev->config; return config->bus.seq_read(&config->bus, address, data, count); } static inline int pinnacle_clear_cmd_complete(const struct device *dev) { const struct pinnacle_config *config = dev->config; return config->bus.write(&config->bus, PINNACLE_REG_STATUS1, 0x00); } static int pinnacle_era_wait_for_completion(const struct device *dev) { bool ret; uint8_t value; ret = WAIT_FOR(pinnacle_read(dev, PINNACLE_REG_ERA_CTRL, &value) == 0 && value == PINNACLE_ERA_CTRL_COMPLETE, PINNACLE_ERA_AWAIT_RETRY_COUNT * PINNACLE_ERA_AWAIT_DELAY_POLL_US, k_sleep(K_USEC(PINNACLE_ERA_AWAIT_DELAY_POLL_US))); if (!ret) { return -EIO; } return 0; } static int pinnacle_era_write(const struct device *dev, uint16_t address, uint8_t value) { uint8_t address_buf[] = { PINNACLE_REG_ERA_VALUE, PINNACLE_REG_ERA_ADDR_HIGH, PINNACLE_REG_ERA_ADDR_LOW, PINNACLE_REG_ERA_CTRL, }; uint8_t value_buf[] = { value, address >> 8, address & 0xFF, PINNACLE_ERA_CTRL_WRITE, }; int rc; rc = pinnacle_seq_write(dev, address_buf, value_buf, sizeof(address_buf)); if (rc) { return rc; } return pinnacle_era_wait_for_completion(dev); } static int pinnacle_era_read(const struct device *dev, uint16_t address, uint8_t *value) { uint8_t address_buf[] = { PINNACLE_REG_ERA_ADDR_HIGH, PINNACLE_REG_ERA_ADDR_LOW, PINNACLE_REG_ERA_CTRL, }; uint8_t value_buf[] = { address >> 8, address & 0xFF, PINNACLE_ERA_CTRL_READ, }; int rc; rc = pinnacle_seq_write(dev, address_buf, value_buf, sizeof(address_buf)); if (rc) { return rc; } rc = pinnacle_era_wait_for_completion(dev); if (rc) { return rc; } return pinnacle_read(dev, PINNACLE_REG_ERA_VALUE, value); } static int pinnacle_set_sensitivity(const struct device *dev) { const struct pinnacle_config *config = dev->config; uint8_t value; int rc; rc = pinnacle_era_read(dev, PINNACLE_ERA_REG_CONFIG, &value); if (rc) { return rc; } /* Clear BIT(7) and BIT(6) */ value &= 0x3F; switch (config->sensitivity) { case PINNACLE_SENSITIVITY_X1: value |= PINNACLE_ERA_CONFIG_ADC_ATTENUATION_X1; break; case PINNACLE_SENSITIVITY_X2: value |= PINNACLE_ERA_CONFIG_ADC_ATTENUATION_X2; break; case PINNACLE_SENSITIVITY_X3: value |= PINNACLE_ERA_CONFIG_ADC_ATTENUATION_X3; break; case PINNACLE_SENSITIVITY_X4: value |= PINNACLE_ERA_CONFIG_ADC_ATTENUATION_X4; break; } rc = pinnacle_era_write(dev, PINNACLE_ERA_REG_CONFIG, value); if (rc) { return rc; } /* Clear SW_CC after setting sensitivity */ rc = pinnacle_clear_cmd_complete(dev); if (rc) { return rc; } return 0; } #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) static bool pinnacle_is_ready_i2c(const struct pinnacle_bus *bus) { if (!i2c_is_ready_dt(&bus->i2c)) { LOG_ERR("I2C bus %s is not ready", bus->i2c.bus->name); return false; } return true; } static int pinnacle_write_i2c(const struct pinnacle_bus *bus, uint8_t address, uint8_t value) { uint8_t buf[] = {PINNACLE_WRITE_REG(address), value}; return i2c_write_dt(&bus->i2c, buf, 2); } static int pinnacle_seq_write_i2c(const struct pinnacle_bus *bus, uint8_t *address, uint8_t *value, uint8_t count) { uint8_t buf[count * 2]; for (uint8_t i = 0; i < count; ++i) { buf[i * 2] = PINNACLE_WRITE_REG(address[i]); buf[i * 2 + 1] = value[i]; } return i2c_write_dt(&bus->i2c, buf, count * 2); } static int pinnacle_read_i2c(const struct pinnacle_bus *bus, uint8_t address, uint8_t *value) { uint8_t reg = PINNACLE_READ_REG(address); return i2c_write_read_dt(&bus->i2c, ®, 1, value, 1); } static int pinnacle_seq_read_i2c(const struct pinnacle_bus *bus, uint8_t address, uint8_t *buf, uint8_t count) { uint8_t reg = PINNACLE_READ_REG(address); return i2c_burst_read_dt(&bus->i2c, reg, buf, count); } #endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */ #if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) static bool pinnacle_is_ready_spi(const struct pinnacle_bus *bus) { if (!spi_is_ready_dt(&bus->spi)) { LOG_ERR("SPI bus %s is not ready", bus->spi.bus->name); return false; } return true; } static int pinnacle_write_spi(const struct pinnacle_bus *bus, uint8_t address, uint8_t value) { uint8_t tx_data[] = { PINNACLE_WRITE_REG(address), value, }; const struct spi_buf tx_buf[] = {{ .buf = tx_data, .len = sizeof(tx_data), }}; const struct spi_buf_set tx_set = { .buffers = tx_buf, .count = ARRAY_SIZE(tx_buf), }; return spi_write_dt(&bus->spi, &tx_set); } static int pinnacle_seq_write_spi(const struct pinnacle_bus *bus, uint8_t *address, uint8_t *value, uint8_t count) { uint8_t tx_data[count * 2]; const struct spi_buf tx_buf[] = {{ .buf = tx_data, .len = sizeof(tx_data), }}; const struct spi_buf_set tx_set = { .buffers = tx_buf, .count = ARRAY_SIZE(tx_buf), }; for (uint8_t i = 0; i < count; ++i) { tx_data[i * 2] = PINNACLE_WRITE_REG(address[i]); tx_data[i * 2 + 1] = value[i]; } return spi_write_dt(&bus->spi, &tx_set); } static int pinnacle_read_spi(const struct pinnacle_bus *bus, uint8_t address, uint8_t *value) { uint8_t tx_data[] = { PINNACLE_READ_REG(address), PINNACLE_SPI_FB, PINNACLE_SPI_FB, PINNACLE_SPI_FB, }; const struct spi_buf tx_buf[] = {{ .buf = tx_data, .len = sizeof(tx_data), }}; const struct spi_buf_set tx_set = { .buffers = tx_buf, .count = ARRAY_SIZE(tx_buf), }; const struct spi_buf rx_buf[] = { { .buf = NULL, .len = 3, }, { .buf = value, .len = 1, }, }; const struct spi_buf_set rx_set = { .buffers = rx_buf, .count = ARRAY_SIZE(rx_buf), }; int rc; rc = spi_transceive_dt(&bus->spi, &tx_set, &rx_set); if (rc) { LOG_ERR("Failed to read from SPI %s", bus->spi.bus->name); return rc; } return 0; } static int pinnacle_seq_read_spi(const struct pinnacle_bus *bus, uint8_t address, uint8_t *buf, uint8_t count) { uint8_t size = count + 3; uint8_t tx_data[size]; tx_data[0] = PINNACLE_READ_REG(address); tx_data[1] = PINNACLE_SPI_FC; tx_data[2] = PINNACLE_SPI_FC; uint8_t i = 3; for (; i < (count + 2); ++i) { tx_data[i] = PINNACLE_SPI_FC; } tx_data[i++] = PINNACLE_SPI_FB; const struct spi_buf tx_buf[] = {{ .buf = tx_data, .len = size, }}; const struct spi_buf_set tx_set = { .buffers = tx_buf, .count = 1, }; const struct spi_buf rx_buf[] = { { .buf = NULL, .len = 3, }, { .buf = buf, .len = count, }, }; const struct spi_buf_set rx_set = { .buffers = rx_buf, .count = ARRAY_SIZE(rx_buf), }; int rc; rc = spi_transceive_dt(&bus->spi, &tx_set, &rx_set); if (rc) { LOG_ERR("Failed to read from SPI %s", bus->spi.bus->name); return rc; } return 0; } #endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */ static void pinnacle_decode_sample(const struct device *dev, uint8_t *rx, union pinnacle_sample *sample) { const struct pinnacle_config *config = dev->config; if (config->relative_mode) { if (config->primary_tap_enabled) { sample->btn_primary = (rx[0] & BIT(0)) == BIT(0); } sample->rel_x = ((rx[0] & BIT(4)) == BIT(4)) ? -(256 - rx[1]) : rx[1]; sample->rel_y = ((rx[0] & BIT(5)) == BIT(5)) ? -(256 - rx[2]) : rx[2]; } else { sample->abs_x = ((rx[2] & 0x0F) << 8) | rx[0]; sample->abs_y = ((rx[2] & 0xF0) << 4) | rx[1]; sample->abs_z = rx[3] & 0x3F; } } static bool pinnacle_is_idle_sample(const union pinnacle_sample *sample) { return (sample->abs_x == 0 && sample->abs_y == 0 && sample->abs_z == 0); } static void pinnacle_clip_sample(const struct device *dev, union pinnacle_sample *sample) { const struct pinnacle_config *config = dev->config; if (sample->abs_x < config->active_range_x_min) { sample->abs_x = config->active_range_x_min; } if (sample->abs_x > config->active_range_x_max) { sample->abs_x = config->active_range_x_max; } if (sample->abs_y < config->active_range_y_min) { sample->abs_y = config->active_range_y_min; } if (sample->abs_y > config->active_range_y_max) { sample->abs_y = config->active_range_y_max; } } static void pinnacle_scale_sample(const struct device *dev, union pinnacle_sample *sample) { const struct pinnacle_config *config = dev->config; uint16_t range_x = config->active_range_x_max - config->active_range_x_min; uint16_t range_y = config->active_range_y_max - config->active_range_y_min; sample->abs_x = (uint16_t)((uint32_t)(sample->abs_x - config->active_range_x_min) * config->resolution_x / range_x); sample->abs_y = (uint16_t)((uint32_t)(sample->abs_y - config->active_range_y_min) * config->resolution_y / range_y); } static int pinnacle_sample_fetch(const struct device *dev, union pinnacle_sample *sample) { const struct pinnacle_config *config = dev->config; uint8_t rx[4]; int rc; if (config->relative_mode) { rc = pinnacle_seq_read(dev, PINNACLE_REG_PACKET_BYTE0, rx, 3); } else { rc = pinnacle_seq_read(dev, PINNACLE_REG_PACKET_BYTE2, rx, 4); } if (rc) { LOG_ERR("Failed to read data from SPI device"); return rc; } pinnacle_decode_sample(dev, rx, sample); rc = pinnacle_write(dev, PINNACLE_REG_STATUS1, 0x00); if (rc) { LOG_ERR("Failed to clear SW_CC and SW_DR"); return rc; } return 0; } static int pinnacle_handle_interrupt(const struct device *dev) { const struct pinnacle_config *config = dev->config; struct pinnacle_data *drv_data = dev->data; union pinnacle_sample *sample = &drv_data->sample; int rc; rc = pinnacle_sample_fetch(dev, sample); if (rc) { LOG_ERR("Failed to read data packets"); return rc; } if (config->relative_mode) { input_report_rel(dev, INPUT_REL_X, sample->rel_x, false, K_FOREVER); input_report_rel(dev, INPUT_REL_Y, sample->rel_y, !config->primary_tap_enabled, K_FOREVER); if (config->primary_tap_enabled) { input_report_key(dev, INPUT_BTN_TOUCH, sample->btn_primary, true, K_FOREVER); } } else { if (config->clipping_enabled && !pinnacle_is_idle_sample(sample)) { pinnacle_clip_sample(dev, sample); if (config->scaling_enabled) { pinnacle_scale_sample(dev, sample); } } input_report_abs(dev, INPUT_ABS_X, sample->abs_x, false, K_FOREVER); input_report_abs(dev, INPUT_ABS_Y, sample->abs_y, false, K_FOREVER); input_report_abs(dev, INPUT_ABS_Z, sample->abs_z, true, K_FOREVER); } return 0; } static void pinnacle_data_ready_gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { struct pinnacle_data *drv_data = CONTAINER_OF(cb, struct pinnacle_data, dr_cb_data); k_work_submit(&drv_data->work); } static void pinnacle_work_cb(struct k_work *work) { struct pinnacle_data *drv_data = CONTAINER_OF(work, struct pinnacle_data, work); pinnacle_handle_interrupt(drv_data->dev); } int pinnacle_init_interrupt(const struct device *dev) { struct pinnacle_data *drv_data = dev->data; const struct pinnacle_config *config = dev->config; const struct gpio_dt_spec *gpio = &config->dr_gpio; int rc; drv_data->dev = dev; drv_data->work.handler = pinnacle_work_cb; /* Configure GPIO pin for HW_DR signal */ rc = gpio_is_ready_dt(gpio); if (!rc) { LOG_ERR("GPIO device %s/%d is not ready", gpio->port->name, gpio->pin); return -ENODEV; } rc = gpio_pin_configure_dt(gpio, GPIO_INPUT); if (rc) { LOG_ERR("Failed to configure %s/%d as input", gpio->port->name, gpio->pin); return rc; } rc = gpio_pin_interrupt_configure_dt(gpio, GPIO_INT_EDGE_TO_ACTIVE); if (rc) { LOG_ERR("Failed to configured interrupt for %s/%d", gpio->port->name, gpio->pin); return rc; } gpio_init_callback(&drv_data->dr_cb_data, pinnacle_data_ready_gpio_callback, BIT(gpio->pin)); rc = gpio_add_callback(gpio->port, &drv_data->dr_cb_data); if (rc) { LOG_ERR("Failed to configured interrupt for %s/%d", gpio->port->name, gpio->pin); return rc; } return 0; } static int pinnacle_init(const struct device *dev) { const struct pinnacle_config *config = dev->config; int rc; bool ret; uint8_t value; if (!pinnacle_bus_is_ready(dev)) { return -ENODEV; } rc = pinnacle_read(dev, PINNACLE_REG_FIRMWARE_ID, &value); if (rc) { LOG_ERR("Failed to read FirmwareId"); return rc; } if (value != PINNACLE_FIRMWARE_ID) { LOG_ERR("Incorrect Firmware ASIC ID %x", value); return -ENODEV; } /* Wait until the calibration is completed (SW_CC is asserted) */ ret = WAIT_FOR(pinnacle_read(dev, PINNACLE_REG_STATUS1, &value) == 0 && (value & PINNACLE_STATUS1_SW_CC) == PINNACLE_STATUS1_SW_CC, PINNACLE_CALIBRATION_AWAIT_RETRY_COUNT * PINNACLE_CALIBRATION_AWAIT_DELAY_POLL_US, k_sleep(K_USEC(PINNACLE_CALIBRATION_AWAIT_DELAY_POLL_US))); if (!ret) { LOG_ERR("Failed to wait for calibration complition"); return -EIO; } /* Clear SW_CC after Power on Reset */ rc = pinnacle_clear_cmd_complete(dev); if (rc) { LOG_ERR("Failed to clear SW_CC in Status1"); return -EIO; } /* Set trackpad sensitivity */ rc = pinnacle_set_sensitivity(dev); if (rc) { LOG_ERR("Failed to set sensitivity"); return -EIO; } rc = pinnacle_write(dev, PINNACLE_REG_SYS_CONFIG1, 0x00); if (rc) { LOG_ERR("Failed to write SysConfig1"); return rc; } /* Relative mode features */ if (config->relative_mode) { value = (PINNACLE_FEED_CONFIG2_GLIDE_EXTEND_DISABLE | PINNACLE_FEED_CONFIG2_SCROLL_DISABLE | PINNACLE_FEED_CONFIG2_SECONDARY_TAP_DISABLE); if (config->swap_xy) { value |= PINNACLE_FEED_CONFIG2_SWAP_X_AND_Y; } if (!config->primary_tap_enabled) { value |= PINNACLE_FEED_CONFIG2_ALL_TAPS_DISABLE; } } else { value = (PINNACLE_FEED_CONFIG2_GLIDE_EXTEND_DISABLE | PINNACLE_FEED_CONFIG2_SCROLL_DISABLE | PINNACLE_FEED_CONFIG2_SECONDARY_TAP_DISABLE | PINNACLE_FEED_CONFIG2_ALL_TAPS_DISABLE); } rc = pinnacle_write(dev, PINNACLE_REG_FEED_CONFIG2, value); if (rc) { LOG_ERR("Failed to write FeedConfig2"); return rc; } /* Data output flags */ value = PINNACLE_FEED_CONFIG1_FEED_ENABLE; if (!config->relative_mode) { value |= PINNACLE_FEED_CONFIG1_DATA_MODE_ABSOLUTE; if (config->invert_x) { value |= PINNACLE_FEED_CONFIG1_X_INVERT; } if (config->invert_y) { value |= PINNACLE_FEED_CONFIG1_Y_INVERT; } } rc = pinnacle_write(dev, PINNACLE_REG_FEED_CONFIG1, value); if (rc) { LOG_ERR("Failed to enable Feed in FeedConfig1"); return rc; } /* Configure count of Z-Idle packets */ rc = pinnacle_write(dev, PINNACLE_REG_Z_IDLE, config->idle_packets_count); if (rc) { LOG_ERR("Failed to set count of Z-idle packets"); return rc; } rc = pinnacle_init_interrupt(dev); if (rc) { LOG_ERR("Failed to initialize interrupts"); return rc; } return 0; } #define PINNACLE_CONFIG_BUS_I2C(inst) \ .bus = { \ .i2c = I2C_DT_SPEC_INST_GET(inst), \ .is_ready = pinnacle_is_ready_i2c, \ .write = pinnacle_write_i2c, \ .seq_write = pinnacle_seq_write_i2c, \ .read = pinnacle_read_i2c, \ .seq_read = pinnacle_seq_read_i2c, \ } #define PINNACLE_SPI_OP (SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_MODE_CPHA | SPI_WORD_SET(8)) #define PINNACLE_CONFIG_BUS_SPI(inst) \ .bus = { \ .spi = SPI_DT_SPEC_INST_GET(inst, PINNACLE_SPI_OP, 0U), \ .is_ready = pinnacle_is_ready_spi, \ .write = pinnacle_write_spi, \ .seq_write = pinnacle_seq_write_spi, \ .read = pinnacle_read_spi, \ .seq_read = pinnacle_seq_read_spi, \ } #define PINNACLE_DEFINE(inst) \ static const struct pinnacle_config pinnacle_config_##inst = { \ COND_CODE_1(DT_INST_ON_BUS(inst, i2c), (PINNACLE_CONFIG_BUS_I2C(inst),), ()) \ COND_CODE_1(DT_INST_ON_BUS(inst, spi), (PINNACLE_CONFIG_BUS_SPI(inst),), ()) \ .dr_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, data_ready_gpios, {}), \ .relative_mode = DT_INST_ENUM_IDX(inst, data_mode), \ .sensitivity = DT_INST_ENUM_IDX(inst, sensitivity), \ .idle_packets_count = DT_INST_PROP(inst, idle_packets_count), \ .clipping_enabled = DT_INST_PROP(inst, clipping_enable), \ .active_range_x_min = DT_INST_PROP(inst, active_range_x_min), \ .active_range_x_max = DT_INST_PROP(inst, active_range_x_max), \ .active_range_y_min = DT_INST_PROP(inst, active_range_y_min), \ .active_range_y_max = DT_INST_PROP(inst, active_range_y_max), \ .scaling_enabled = DT_INST_PROP(inst, scaling_enable), \ .resolution_x = DT_INST_PROP(inst, scaling_x_resolution), \ .resolution_y = DT_INST_PROP(inst, scaling_y_resolution), \ .invert_x = DT_INST_PROP(inst, invert_x), \ .invert_y = DT_INST_PROP(inst, invert_y), \ .primary_tap_enabled = DT_INST_PROP(inst, primary_tap_enable), \ .swap_xy = DT_INST_PROP(inst, swap_xy), \ }; \ static struct pinnacle_data pinnacle_data_##inst; \ DEVICE_DT_INST_DEFINE(inst, pinnacle_init, NULL, &pinnacle_data_##inst, \ &pinnacle_config_##inst, POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, \ NULL); \ BUILD_ASSERT(DT_INST_PROP(inst, active_range_x_min) < \ DT_INST_PROP(inst, active_range_x_max), \ "active-range-x-min must be less than active-range-x-max"); \ BUILD_ASSERT(DT_INST_PROP(inst, active_range_y_min) < \ DT_INST_PROP(inst, active_range_y_max), \ "active_range-y-min must be less than active_range-y-max"); \ BUILD_ASSERT(DT_INST_PROP(inst, scaling_x_resolution) > 0, \ "scaling-x-resolution must be positive"); \ BUILD_ASSERT(DT_INST_PROP(inst, scaling_y_resolution) > 0, \ "scaling-y-resolution must be positive"); \ BUILD_ASSERT(IN_RANGE(DT_INST_PROP(inst, idle_packets_count), 0, UINT8_MAX), \ "idle-packets-count must be in range [0:255]"); DT_INST_FOREACH_STATUS_OKAY(PINNACLE_DEFINE)