blob: c28fceeaeb3a2a0729a8627ef0950b96ec18e5d2 [file] [log] [blame]
/*
* Copyright (c) 2024 Ilia Kharin
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT cirque_pinnacle
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
#include <zephyr/drivers/i2c.h>
#endif
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
#include <zephyr/drivers/spi.h>
#endif
#include <zephyr/init.h>
#include <zephyr/input/input.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h>
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, &reg, 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)