Merge c09183a926
into eaeea4bdfa
This commit is contained in:
commit
98beaab2cf
9 changed files with 453 additions and 2 deletions
|
@ -3,3 +3,4 @@
|
|||
|
||||
add_subdirectory_ifdef(CONFIG_ZMK_BATTERY battery)
|
||||
add_subdirectory_ifdef(CONFIG_EC11 ec11)
|
||||
add_subdirectory_ifdef(CONFIG_PINNACLE cirque_trackpad)
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
|
||||
rsource "battery/Kconfig"
|
||||
rsource "ec11/Kconfig"
|
||||
rsource "pinnacle/Kconfig"
|
||||
|
|
8
app/drivers/sensor/pinnacle/CMakeLists.txt
Normal file
8
app/drivers/sensor/pinnacle/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2022 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
zephyr_include_directories(.)
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources(pinnacle.c)
|
51
app/drivers/sensor/pinnacle/Kconfig
Normal file
51
app/drivers/sensor/pinnacle/Kconfig
Normal file
|
@ -0,0 +1,51 @@
|
|||
# Copyright (c) 2022 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
menuconfig PINNACLE
|
||||
bool "Cirque Pinnacle trackpads"
|
||||
depends on GPIO
|
||||
depends on SPI || I2C
|
||||
help
|
||||
Enable driver for Cirque Pinnacle trackpads
|
||||
|
||||
if PINNACLE
|
||||
|
||||
choice
|
||||
prompt "Trigger mode"
|
||||
default PINNACLE_TRIGGER_NONE
|
||||
help
|
||||
Specify the type of triggering to be used by the driver.
|
||||
|
||||
config PINNACLE_TRIGGER_NONE
|
||||
bool "No trigger"
|
||||
|
||||
config PINNACLE_TRIGGER_GLOBAL_THREAD
|
||||
bool "Use global thread"
|
||||
depends on GPIO
|
||||
select PINNACLE_TRIGGER
|
||||
|
||||
config PINNACLE_TRIGGER_OWN_THREAD
|
||||
bool "Use own thread"
|
||||
depends on GPIO
|
||||
select PINNACLE_TRIGGER
|
||||
|
||||
endchoice
|
||||
|
||||
config PINNACLE_TRIGGER
|
||||
bool
|
||||
|
||||
config PINNACLE_THREAD_PRIORITY
|
||||
int "Thread priority"
|
||||
depends on PINNACLE_TRIGGER_OWN_THREAD
|
||||
default 10
|
||||
help
|
||||
Priority of thread used by the driver to handle interrupts.
|
||||
|
||||
config PINNACLE_THREAD_STACK_SIZE
|
||||
int "Thread stack size"
|
||||
depends on PINNACLE_TRIGGER_OWN_THREAD
|
||||
default 1024
|
||||
help
|
||||
Stack size of thread used by the driver to handle interrupts.
|
||||
|
||||
endif # PINNACLE
|
283
app/drivers/sensor/pinnacle/pinnacle.c
Normal file
283
app/drivers/sensor/pinnacle/pinnacle.c
Normal file
|
@ -0,0 +1,283 @@
|
|||
#define DT_DRV_COMPAT cirque_pinnacle
|
||||
|
||||
#include <init.h>
|
||||
#include <drivers/sensor.h>
|
||||
#include <logging/log.h>
|
||||
|
||||
#include "pinnacle.h"
|
||||
|
||||
LOG_MODULE_REGISTER(pinnacle, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static int pinnacle_seq_read(const struct device *dev, const uint8_t addr, uint8_t *buf, const uint8_t len) {
|
||||
const struct pinnacle_config *config = dev->config;
|
||||
#if DT_INST_ON_BUS(0, spi)
|
||||
uint8_t tx_buffer[len + 3], rx_dummy[3];
|
||||
tx_buffer[0] = PINNACLE_READ | addr;
|
||||
memset(&tx_buffer[1], PINNACLE_AUTOINC, len + 1);
|
||||
tx_buffer[len + 2] = PINNACLE_DUMMY;
|
||||
|
||||
const struct spi_buf tx_buf = {
|
||||
.buf = tx_buffer,
|
||||
.len = len + 3,
|
||||
};
|
||||
const struct spi_buf_set tx = {
|
||||
.buffers = &tx_buf,
|
||||
.count = 1,
|
||||
};
|
||||
struct spi_buf rx_buf[2] = {
|
||||
{
|
||||
.buf = rx_dummy,
|
||||
.len = 3,
|
||||
},
|
||||
{
|
||||
.buf = buf,
|
||||
.len = len,
|
||||
},
|
||||
};
|
||||
const struct spi_buf_set rx = {
|
||||
.buffers = rx_buf,
|
||||
.count = 2,
|
||||
};
|
||||
return spi_transceive_dt(&config->bus, &tx, &rx);
|
||||
#elif DT_INST_ON_BUS(0, i2c)
|
||||
return i2c_burst_read_dt(&config->bus, PINNACLE_READ | addr, buf, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int pinnacle_write(const struct device *dev, const uint8_t addr, const uint8_t val) {
|
||||
const struct pinnacle_config *config = dev->config;
|
||||
#if DT_INST_ON_BUS(0, spi)
|
||||
uint8_t tx_buffer[2] = { PINNACLE_WRITE | addr, val };
|
||||
uint8_t rx_buffer[2];
|
||||
|
||||
const struct spi_buf tx_buf = {
|
||||
.buf = tx_buffer,
|
||||
.len = 2,
|
||||
};
|
||||
const struct spi_buf_set tx = {
|
||||
.buffers = &tx_buf,
|
||||
.count = 1,
|
||||
};
|
||||
const struct spi_buf rx_buf[1] = {
|
||||
{
|
||||
.buf = rx_buffer,
|
||||
.len = sizeof(rx_buffer),
|
||||
},
|
||||
};
|
||||
const struct spi_buf_set rx = {
|
||||
.buffers = rx_buf,
|
||||
.count = 1,
|
||||
};
|
||||
const int ret = spi_transceive_dt(&config->bus, &tx, &rx);
|
||||
if (rx_buffer[1] != 0xFB) {
|
||||
LOG_ERR("bad ret val");
|
||||
return -EIO;
|
||||
}
|
||||
if (ret < 0) {
|
||||
LOG_ERR("spi ret: %d", ret);
|
||||
}
|
||||
return ret;
|
||||
#elif DT_INST_ON_BUS(0, i2c)
|
||||
return i2c_reg_write_byte_dt(&config->bus, PINNACLE_WRITE | addr, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int pinnacle_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) {
|
||||
const struct pinnacle_data *data = dev->data;
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_POS_DX: val->val1 = data->dx; break;
|
||||
case SENSOR_CHAN_POS_DY: val->val1 = data->dy; break;
|
||||
case SENSOR_CHAN_PRESS: val->val1 = data->btn; break;
|
||||
default: return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pinnacle_sample_fetch(const struct device *dev, enum sensor_channel chan) {
|
||||
uint8_t packet[3];
|
||||
int ret;
|
||||
ret = pinnacle_seq_read(dev, PINNACLE_STATUS1, packet, 0);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("read status: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
if (!(packet[0] & PINNACLE_STATUS1_SW_DR)) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
ret = pinnacle_seq_read(dev, PINNACLE_2_2_PACKET0, packet, 3);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("read packet: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
struct pinnacle_data *data = dev->data;
|
||||
data->btn = packet[0] & PINNACLE_PACKET0_BTN_PRIM;
|
||||
data->dx = (int16_t) (int8_t) packet[1];
|
||||
data->dy = (int16_t) (int8_t) packet[2];
|
||||
if (!data->in_int) {
|
||||
ret = pinnacle_write(dev, PINNACLE_STATUS1, 0); // Clear SW_DR
|
||||
if (ret < 0) {
|
||||
LOG_ERR("clear dr: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PINNACLE_TRIGGER
|
||||
static void set_int(const struct device *dev, const bool en) {
|
||||
const struct pinnacle_config *config = dev->config;
|
||||
int ret = gpio_pin_interrupt_configure_dt(&config->dr, en ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("can't set interrupt");
|
||||
}
|
||||
}
|
||||
|
||||
static int pinnacle_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler) {
|
||||
struct pinnacle_data *data = dev->data;
|
||||
|
||||
set_int(dev, false);
|
||||
if (trig->type != SENSOR_TRIG_DATA_READY) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
data->data_ready_trigger = trig;
|
||||
data->data_ready_handler = handler;
|
||||
set_int(dev, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pinnacle_int_cb(const struct device *dev) {
|
||||
struct pinnacle_data *data = dev->data;
|
||||
data->data_ready_handler(dev, data->data_ready_trigger);
|
||||
set_int(dev, true);
|
||||
int ret = pinnacle_write(dev, PINNACLE_STATUS1, 0); // Clear SW_DR
|
||||
if (ret < 0) {
|
||||
LOG_ERR("clear dr: %d", ret);
|
||||
}
|
||||
data->in_int = false;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PINNACLE_TRIGGER_OWN_THREAD
|
||||
static void pinnacle_thread(void *arg) {
|
||||
const struct device *dev = arg;
|
||||
struct pinnacle_data *data = dev->data;
|
||||
|
||||
while (1) {
|
||||
k_sem_take(&data->gpio_sem, K_FOREVER);
|
||||
pinnacle_int_cb(dev);
|
||||
}
|
||||
}
|
||||
#elif defined(CONFIG_PINNACLE_TRIGGER_GLOBAL_THREAD)
|
||||
static void pinnacle_work_cb(struct k_work *work) {
|
||||
struct pinnacle_data *data = CONTAINER_OF(work, struct pinnacle_data, work);
|
||||
pinnacle_int_cb(data->dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void pinnacle_gpio_cb(const struct device *port, struct gpio_callback *cb, uint32_t pins) {
|
||||
struct pinnacle_data *data = CONTAINER_OF(cb, struct pinnacle_data, gpio_cb);
|
||||
data->in_int = true;
|
||||
#if defined(CONFIG_PINNACLE_TRIGGER_OWN_THREAD)
|
||||
k_sem_give(&data->gpio_sem);
|
||||
#elif defined(CONFIG_PINNACLE_TRIGGER_GLOBAL_THREAD)
|
||||
k_work_submit(&data->work);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static int pinnacle_init(const struct device *dev) {
|
||||
struct pinnacle_data *data = dev->data;
|
||||
const struct pinnacle_config *config = dev->config;
|
||||
|
||||
LOG_WRN("pinnacle start");
|
||||
data->in_int = false;
|
||||
int ret;
|
||||
ret = pinnacle_write(dev, PINNACLE_SYS_CFG, PINNACLE_SYS_CFG_RESET);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("can't reset %d", ret);
|
||||
return ret;
|
||||
}
|
||||
k_msleep(20);
|
||||
ret = pinnacle_write(dev, PINNACLE_STATUS1, 0); // Clear CC
|
||||
if (ret < 0) {
|
||||
LOG_ERR("can't write %d", ret);
|
||||
return ret;
|
||||
}
|
||||
k_usleep(50);
|
||||
ret = pinnacle_write(dev, PINNACLE_Z_IDLE, 0); // No Z-Idle packets
|
||||
if (ret < 0) {
|
||||
LOG_ERR("can't write %d", ret);
|
||||
return ret;
|
||||
}
|
||||
if (config->sleep_en) {
|
||||
ret = pinnacle_write(dev, PINNACLE_SYS_CFG, PINNACLE_SYS_CFG_EN_SLEEP);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("can't write %d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
uint8_t feed_cfg2 = PINNACLE_FEED_CFG2_EN_IM;
|
||||
if (config->no_taps) {
|
||||
feed_cfg2 |= PINNACLE_FEED_CFG2_DIS_TAP;
|
||||
}
|
||||
if (config->rotate_90) {
|
||||
feed_cfg2 |= PINNACLE_FEED_CFG2_ROTATE_90;
|
||||
}
|
||||
ret = pinnacle_write(dev, PINNACLE_FEED_CFG2, feed_cfg2);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("can't write %d", ret);
|
||||
return ret;
|
||||
}
|
||||
uint8_t feed_cfg1 = PINNACLE_FEED_CFG1_EN_FEED;
|
||||
if (feed_cfg1) {
|
||||
ret = pinnacle_write(dev, PINNACLE_FEED_CFG1, feed_cfg1);
|
||||
}
|
||||
if (ret < 0) {
|
||||
LOG_ERR("can't write %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PINNACLE_TRIGGER
|
||||
data->dev = dev;
|
||||
gpio_pin_configure_dt(&config->dr, GPIO_INPUT);
|
||||
gpio_init_callback(&data->gpio_cb, pinnacle_gpio_cb, BIT(config->dr.pin));
|
||||
ret = gpio_add_callback(config->dr.port, &data->gpio_cb);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to set DR callback: %d", ret);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PINNACLE_TRIGGER_OWN_THREAD)
|
||||
k_sem_init(&data->gpio_sem, 0, UINT_MAX);
|
||||
|
||||
k_thread_create(&data->thread, data->thread_stack, CONFIG_PINNACLE_THREAD_STACK_SIZE,
|
||||
(k_thread_entry_t) pinnacle_thread, (void *) dev, 0, NULL,
|
||||
K_PRIO_COOP(CONFIG_PINNACLE_THREAD_PRIORITY), 0, K_NO_WAIT);
|
||||
#elif defined(CONFIG_PINNACLE_TRIGGER_GLOBAL_THREAD)
|
||||
k_work_init(&data->work, pinnacle_work_cb);
|
||||
#endif
|
||||
pinnacle_write(dev, PINNACLE_FEED_CFG1, feed_cfg1);
|
||||
#endif
|
||||
LOG_WRN("inited");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sensor_driver_api pinnacle_driver_api = {
|
||||
#if CONFIG_PINNACLE_TRIGGER
|
||||
.trigger_set = pinnacle_trigger_set,
|
||||
#endif
|
||||
.sample_fetch = pinnacle_sample_fetch,
|
||||
.channel_get = pinnacle_channel_get,
|
||||
};
|
||||
|
||||
#define PINNACLE_INST(n) \
|
||||
static struct pinnacle_data pinnacle_data_##n; \
|
||||
static const struct pinnacle_config pinnacle_config_##n = { \
|
||||
.bus = COND_CODE_1(DT_INST_ON_BUS(0, i2c), (I2C_DT_SPEC_INST_GET(0)), (SPI_DT_SPEC_INST_GET(0, SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_LINES_SINGLE | SPI_TRANSFER_MSB, 0))), \
|
||||
.rotate_90 = DT_INST_PROP(0, rotate_90), \
|
||||
.sleep_en = DT_INST_PROP(0, sleep), \
|
||||
.no_taps = DT_INST_PROP(0, no_taps), \
|
||||
COND_CODE_1(CONFIG_PINNACLE_TRIGGER, (.dr = GPIO_DT_SPEC_GET(DT_DRV_INST(0), dr_gpios),), ()) \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(n, pinnacle_init, NULL, &pinnacle_data_##n, &pinnacle_config_##n, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &pinnacle_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(PINNACLE_INST)
|
85
app/drivers/sensor/pinnacle/pinnacle.h
Normal file
85
app/drivers/sensor/pinnacle/pinnacle.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
#pragma once
|
||||
|
||||
#include <device.h>
|
||||
#include <drivers/spi.h>
|
||||
#include <drivers/i2c.h>
|
||||
|
||||
#define PINNACLE_READ 0xA0
|
||||
#define PINNACLE_WRITE 0x80
|
||||
|
||||
#define PINNACLE_AUTOINC 0xFC
|
||||
#define PINNACLE_DUMMY 0xFB
|
||||
|
||||
// Registers
|
||||
#define PINNACLE_FW_ID 0x00 // ASIC ID.
|
||||
#define PINNACLE_FW_VER 0x01 // Firmware Version Firmware revision number.
|
||||
#define PINNACLE_STATUS1 0x02 // Contains status flags about the state of Pinnacle.
|
||||
#define PINNACLE_STATUS1_SW_DR BIT(2)
|
||||
#define PINNACLE_STATUS1_SW_CC BIT(3)
|
||||
#define PINNACLE_SYS_CFG 0x03 // Contains system operation and configuration bits.
|
||||
#define PINNACLE_SYS_CFG_EN_SLEEP BIT(2)
|
||||
#define PINNACLE_SYS_CFG_SHUTDOWN BIT(1)
|
||||
#define PINNACLE_SYS_CFG_RESET BIT(0)
|
||||
|
||||
#define PINNACLE_FEED_CFG1 0x04 // Contains feed operation and configuration bits.
|
||||
#define PINNACLE_FEED_CFG1_EN_FEED BIT(0)
|
||||
#define PINNACLE_FEED_CFG1_ABS_MODE BIT(1)
|
||||
#define PINNACLE_FEED_CFG1_DIS_FILT BIT(2)
|
||||
#define PINNACLE_FEED_CFG1_DIS_X BIT(3)
|
||||
#define PINNACLE_FEED_CFG1_DIS_Y BIT(4)
|
||||
#define PINNACLE_FEED_CFG1_INV_X BIT(6)
|
||||
#define PINNACLE_FEED_CFG1_INV_Y BIT(7)
|
||||
#define PINNACLE_FEED_CFG2 0x05 // Contains feed operation and configuration bits.
|
||||
#define PINNACLE_FEED_CFG2_EN_IM BIT(0) // Intellimouse
|
||||
#define PINNACLE_FEED_CFG2_DIS_TAP BIT(1) // Disable all taps
|
||||
#define PINNACLE_FEED_CFG2_DIS_SEC BIT(2) // Disable secondary tap
|
||||
#define PINNACLE_FEED_CFG2_DIS_SCRL BIT(3) // Disable scroll
|
||||
#define PINNACLE_FEED_CFG2_DIS_GE BIT(4) // Disable GlideExtend
|
||||
#define PINNACLE_FEED_CFG2_ROTATE_90 BIT(7) // Swap X & Y
|
||||
#define PINNACLE_CAL_CFG 0x07 // Contains calibration configuration bits.
|
||||
#define PINNACLE_PS2_AUX 0x08 // Contains Data register for PS/2 Aux Control.
|
||||
#define PINNACLE_SAMPLE 0x09 // Sample Rate Number of samples generated per second.
|
||||
#define PINNACLE_Z_IDLE 0x0A // Number of Z=0 packets sent when Z goes from >0 to 0.
|
||||
#define PINNACLE_Z_SCALER 0x0B // Contains the pen Z_On threshold.
|
||||
#define PINNACLE_SLEEP_INTERVAL 0x0C // Sleep Interval
|
||||
#define PINNACLE_SLEEP_TIMER 0x0D // Sleep Timer
|
||||
#define PINNACLE_AG_PACKET0 0x10 // trackpad Data (Pinnacle AG)
|
||||
#define PINNACLE_2_2_PACKET0 0x12 // trackpad Data
|
||||
#define PINNACLE_REG_COUNT 0x18
|
||||
|
||||
#define PINNACLE_PACKET0_BTN_PRIM BIT(0) // Primary button
|
||||
#define PINNACLE_PACKET0_BTN_SEC BIT(1) // Secondary button
|
||||
#define PINNACLE_PACKET0_BTN_AUX BIT(2) // Auxiliary (middle?) button
|
||||
#define PINNACLE_PACKET0_X_SIGN BIT(4) // X delta sign
|
||||
#define PINNACLE_PACKET0_Y_SIGN BIT(5) // Y delta sign
|
||||
|
||||
struct pinnacle_data {
|
||||
int16_t dx, dy;
|
||||
uint8_t btn;
|
||||
bool in_int;
|
||||
#ifdef CONFIG_PINNACLE_TRIGGER
|
||||
const struct device *dev;
|
||||
const struct sensor_trigger *data_ready_trigger;
|
||||
struct gpio_callback gpio_cb;
|
||||
sensor_trigger_handler_t data_ready_handler;
|
||||
#if defined(CONFIG_PINNACLE_TRIGGER_OWN_THREAD)
|
||||
K_THREAD_STACK_MEMBER(thread_stack, CONFIG_PINNACLE_THREAD_STACK_SIZE);
|
||||
struct k_sem gpio_sem;
|
||||
struct k_thread thread;
|
||||
#elif defined(CONFIG_PINNACLE_TRIGGER_GLOBAL_THREAD)
|
||||
struct k_work work;
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
struct pinnacle_config {
|
||||
#if DT_INST_ON_BUS(0, i2c)
|
||||
const struct i2c_dt_spec bus;
|
||||
#elif DT_INST_ON_BUS(0, spi)
|
||||
const struct spi_dt_spec bus;
|
||||
#endif
|
||||
bool rotate_90, sleep_en, no_taps;
|
||||
#ifdef CONFIG_PINNACLE_TRIGGER
|
||||
const struct gpio_dt_spec dr;
|
||||
#endif
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
properties:
|
||||
dr-gpios:
|
||||
type: phandle-array
|
||||
description: Data ready pin for the trackpad
|
||||
rotate-90:
|
||||
type: boolean
|
||||
sleep:
|
||||
type: boolean
|
||||
no-taps:
|
||||
type: boolean
|
|
@ -0,0 +1,6 @@
|
|||
description: |
|
||||
Sensor driver for the Cirque Pinnacle trackpad ASICs, using the I2C interface
|
||||
|
||||
compatible: "cirque,pinnacle"
|
||||
|
||||
include: ["i2c-device.yaml", "cirque,pinnacle-common.yaml"]
|
|
@ -0,0 +1,6 @@
|
|||
description: |
|
||||
Sensor driver for the Cirque Pinnacle trackpad ASICs, using the SPI interface
|
||||
|
||||
compatible: "cirque,pinnacle"
|
||||
|
||||
include: ["spi-device.yaml", "cirque,pinnacle-common.yaml"]
|
Loading…
Add table
Reference in a new issue