ldc2114_mmi: report raw data through debugfs

Replace character device with debugfs for reporting raw data.
Multiple instances of driver and raw data reports supported.
Source files moved to ldc2114_mmi folder to align with other
drivers.

Change-Id: I0357117962f5d492459091c28e8fe1cd403490a2
Signed-off-by: Konstantin Makariev <hcv867@motorola.com>
Reviewed-on: https://gerrit.mot.com/1277626
SLTApproved: Slta Waiver
SME-Granted: SME Approvals Granted
Tested-by: Jira Key
Reviewed-by: Konstantin Makariev <kmakariev@motorola.com>
Submit-Approved: Jira Key
This commit is contained in:
Konstantin Makariev 2018-11-29 16:27:02 -06:00 • committed by Konstantin Makariev
commit 4f16051c59
8 changed files with 413 additions and 362 deletions

View file

@ -1,13 +0,0 @@
# add -Wall to try to catch everything we can.
EXTRA_CFLAGS += -Wall
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include -I$(TOP)/motorola/kernel/modules/drivers/input/misc
ldc2114_keys-objs := ldc2114.o
ifeq ($(TARGET_BUILD_VARIANT),userdebug)
# Following line is in control of extra debug
EXTRA_CFLAGS += -DLDC2114_POLL_DEBUG -DLDC2114_CFG_DEBUG #-DLDC2114_CHARDEV
#ldc2114_keys-objs += ldc2114-cdev.o
endif
obj-m = ldc2114_keys.o

View file

@ -1,28 +0,0 @@
#ifndef _LDC2114_CDEV_HDR_
#define _LDC2114_CDEV_HDR_
#include <linux/notifier.h>
#if defined(LDC2114_CHARDEV)
int ldc2114_cdev_init(void);
void ldc2114_cdev_remove(void);
int ldc2114_buffer(int d0, int d1, int d2, int d3);
int ldc2114_register_client(struct notifier_block *nb);
#else
static int ldc2114_cdev_init(void)
{
return -ENODEV;
}
static void ldc2114_cdev_remove(void)
{
}
static int ldc2114_buffer(int d0, int d1, int d2, int d3)
{
return 0;
}
static int ldc2114_register_client(struct notifier_block *nb)
{
return -ENODEV;
}
#endif
#endif

View file

@ -6,5 +6,3 @@ LOCAL_MODULE := ldc2114_keys.ko
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
include $(DLKM_DIR)/AndroidKernelModule.mk
include $(call first-makefiles-under,$(LOCAL_PATH))

View file

@ -0,0 +1,12 @@
# add -Wall to try to catch everything we can.
EXTRA_CFLAGS += -Wall
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include
ldc2114_keys-objs := ldc2114.o ldc2114-debugfs.o
ifeq ($(TARGET_BUILD_VARIANT),userdebug)
# Following line is in control of extra debug
EXTRA_CFLAGS += -DLDC2114_POLL_DEBUG -DLDC2114_CFG_DEBUG
endif
obj-m = ldc2114_keys.o

View file

@ -0,0 +1,10 @@
KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS)
modules_install:
$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install
clean:
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean

View file

@ -1,6 +1,6 @@
/* linux/drivers/input/misc/ldc2114-cdev.c
/* linux/drivers/input/misc/ldc2114-debugfs.c
*
* LDC2114 chardev driver. Allows user space access to raw data.
* LDC2114 debugfs driver. Allows user space access to raw data.
* Copyright (C) 2017 Motorola, Inc.
*
* This program is free software; you can redistribute it and/or modify
@ -12,50 +12,21 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#if defined(CONFIG_DEBUG_FS)
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/memory.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/atomic.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include "ldc2114.h"
#define DRVNAME "ldc2114_raw"
#define BUFFER_SIZE 256
typedef struct data {
int d[4];
} item_t;
struct buffer {
struct mutex mutex;
int capacity;
unsigned long opened;
atomic_t start;
atomic_t end;
atomic_t active;
item_t **data;
wait_queue_head_t wait;
};
struct ldc2114_drv {
struct class *class;
struct cdev cdev;
struct platform_device *pdev;
dev_t devid;
int major;
struct buffer ds;
struct blocking_notifier_head nhead;
};
static struct ldc2114_drv *cxt;
static int buffer_init(struct buffer *buffer, size_t capacity)
{
@ -84,8 +55,17 @@ static void inline buffer_destroy(struct buffer *buffer)
{
int i;
mutex_lock(&buffer->mutex);
buffer->capacity = 0;
atomic_set(&buffer->start, 0);
atomic_set(&buffer->end, 0);
atomic_set(&buffer->active, 0);
/* wait for remaining msgs */
wait_event_interruptible(buffer->wait,
!atomic_read(&buffer->active));
for (i = 0; i < buffer->capacity; i++)
kfree(buffer->data[i]);
mutex_unlock(&buffer->mutex);
kfree(buffer->data);
}
@ -102,7 +82,7 @@ static void buffer_push(struct buffer *buffer,
cstart = atomic_read(&buffer->start);
cend = atomic_read(&buffer->end);
dev_dbg(&context->pdev->dev, "%s: writing at pos %d\n", __func__, cend);
dev_dbg(context->dev, "%s: writing at pos %d\n", __func__, cend);
memcpy(&buffer->data[cend], data, length);
cend = (cend + 1) % buffer->capacity;
@ -116,7 +96,7 @@ static void buffer_push(struct buffer *buffer,
atomic_set(&buffer->start, cstart);
}
dev_dbg(&context->pdev->dev, "%s: next write=%d, read=%d\n",
dev_dbg(context->dev, "%s: next write=%d, read=%d\n",
__func__, cend, cstart);
mutex_unlock(&buffer->mutex);
@ -141,14 +121,14 @@ static size_t buffer_pop(struct buffer *buffer, item_t *item)
cstart = atomic_read(&buffer->start);
memcpy(item, &buffer->data[cstart], sizeof(item_t));
dev_dbg(&context->pdev->dev, "%s: reading from pos %d\n",
dev_dbg(context->dev, "%s: reading from pos %d\n",
__func__, cstart);
count += sizeof(item_t);
cactive--;
cstart = (cstart + 1) % buffer->capacity;
dev_dbg(&context->pdev->dev, "%s: next read pos %d\n",
dev_dbg(context->dev, "%s: next read pos %d\n",
__func__, cstart);
atomic_set(&buffer->start, cstart);
@ -162,28 +142,51 @@ static size_t buffer_pop(struct buffer *buffer, item_t *item)
/*
* External function called by polling work
*/
int ldc2114_buffer(int d0, int d1, int d2, int d3)
int ldc2114_buffer(struct ldc2114_data *ldc, int type, ...)
{
struct ldc2114_drv *cxt = dev_get_drvdata(ldc->dev);
va_list vp;
item_t data;
int ret = 0;
int rc = 0;
if (cxt && test_bit(0, &cxt->ds.opened)) {
data.d[0] = d0;
data.d[1] = d1;
data.d[2] = d2;
data.d[3] = d3;
buffer_push(&cxt->ds, &data, sizeof(data));
} else
ret = -ENODEV;
if (!(cxt && test_bit(0, &cxt->ds.opened)))
return -ENODEV;
return ret;
va_start(vp, type);
data.type = type;
switch (type) {
case LDC2114_EV_DATA:
data.de = va_arg(vp, struct ldc2114_raw_ext);
break;
default:
rc = -EINVAL;
goto leave_now;
}
buffer_push(&cxt->ds, &data, sizeof(data));
leave_now:
va_end(vp);
return rc;
}
static int inline comp2_12b(struct ldc2114_16bit *data)
{
int result;
/* 12bits 2's compliment data */
if (data->msb & 0x8)
result = (data->lsb | (data->msb << 8) | 0xFFFFF000);
else
result = (data->lsb | (data->msb << 8));
return result;
}
static ssize_t ldc2114_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct ldc2114_drv *context = file->private_data;
int ret;
int i, ret;
item_t data;
char out[128];
@ -191,8 +194,18 @@ static ssize_t ldc2114_read(struct file *file, char __user *buf,
if (ret < 0)
return ret;
count = snprintf(out, sizeof(out), "%5d %5d %5d %5d\n",
data.d[0], data.d[1], data.d[2], data.d[3]);
switch(data.type) {
case LDC2114_EV_DATA:
count = snprintf(out, sizeof(out), "I:%d",
(data.de.status & LDC2114_REG_STATUS_OUT) != 0);
for (i = 0; i < MAX_KEYS; i++) {
count += snprintf(out+count, sizeof(out)-count, " [%d]:%d/%d",
i, (data.de.data.out & (1 << i)) != 0,
comp2_12b(&data.de.data.values[i]));
}
count += snprintf(out+count, sizeof(out)-count, "\n");
break;
}
if (copy_to_user(buf, out, count))
count = -EFAULT;
@ -202,6 +215,7 @@ static ssize_t ldc2114_read(struct file *file, char __user *buf,
static int ldc2114_open(struct inode *inode, struct file *file)
{
struct ldc2114_drv *cxt = inode->i_private;
int ret;
if (!cxt)
@ -218,8 +232,8 @@ static int ldc2114_open(struct inode *inode, struct file *file)
return -EBUSY;
}
dev_dbg(cxt->dev, "%s: allocated circular buffer\n", __func__);
blocking_notifier_call_chain(&cxt->nhead, cxt->ds.opened, NULL);
dev_dbg(&cxt->pdev->dev, "allocated circular buffer\n");
return nonseekable_open(inode, file);
}
@ -228,16 +242,17 @@ static int ldc2114_release(struct inode *inode, struct file *file)
{
struct ldc2114_drv *context = file->private_data;
__clear_bit_unlock(0, &context->ds.opened);
clear_bit_unlock(0, &context->ds.opened);
dev_dbg(context->dev, "%s: opend flag %lu\n", __func__, context->ds.opened);
blocking_notifier_call_chain(&context->nhead, context->ds.opened, NULL);
buffer_destroy(&context->ds);
dev_dbg(&cxt->pdev->dev, "destroyed circular buffer\n");
dev_dbg(context->dev, "%s: destroyed circular buffer\n", __func__);
return 0;
}
const struct file_operations ldc2114_cdev_fops = {
const struct file_operations ldc2114_debugfs_fops = {
.owner = THIS_MODULE,
.open = ldc2114_open,
.read = ldc2114_read,
@ -245,79 +260,72 @@ const struct file_operations ldc2114_cdev_fops = {
.llseek = no_llseek,
};
int ldc2114_register_client(struct notifier_block *nb)
int ldc2114_register_client(struct ldc2114_data *ldc, struct notifier_block *nb)
{
struct ldc2114_drv *cxt = dev_get_drvdata(ldc->dev);
dev_dbg(ldc->dev, "cxt %p\n", cxt);
if (!cxt)
return -ENODEV;
dev_dbg(ldc->dev, "%s: registering notifier\n", __func__);
return blocking_notifier_chain_register(&cxt->nhead, nb);
}
int ldc2114_cdev_init(void)
static char *INSTANCE(char *string, struct ldc2114_data *ldc)
{
struct device *cd;
int rc;
char instance[64];
snprintf(instance, sizeof(instance), "%s.%d",
string, ldc->instance);
return kstrdup(instance, GFP_KERNEL);
}
static struct dentry *root_dir;
int ldc2114_debugfs_init(struct ldc2114_data *ldc)
{
struct ldc2114_drv *cxt;
int rc = 0;
if (!root_dir)
root_dir = debugfs_create_dir(DRVNAME, NULL);
if (!root_dir)
return -ENODEV;
cxt = kzalloc(sizeof(*cxt), GFP_KERNEL);
if (!cxt)
return -ENOMEM;
cxt->pdev = platform_device_alloc(DRVNAME, 0);
if (!cxt->pdev)
return -ENOMEM;
rc = platform_device_add(cxt->pdev);
if (rc)
goto undo_malloc;
rc = register_chrdev(0, DRVNAME, &ldc2114_cdev_fops);
if (rc < 0) {
dev_err(&cxt->pdev->dev, "error register chardev: %d\n", rc);
goto undo_platform_device_add;
if (!cxt) {
rc = -ENOMEM;
goto remove_dbg_root;
}
cxt->major = rc;
cxt->name = INSTANCE(DRVNAME, ldc);
cxt->dev = ldc->dev;
dev_set_drvdata(ldc->dev, cxt);
cxt->class = class_create(THIS_MODULE, DRVNAME "-dev");
if (IS_ERR(cxt->class)) {
rc = PTR_ERR(cxt->class);
dev_err(&cxt->pdev->dev, "error chrdev: %d\n", rc);
goto undo_register_chrdev;
}
cd = device_create(cxt->class, NULL, MKDEV(cxt->major, 0), NULL, DRVNAME);
if (IS_ERR(cd)) {
rc = PTR_ERR(cd);
dev_err(&cxt->pdev->dev, "error chrdev create: %d\n", rc);
goto undo_class_create;
}
cxt->dentry = debugfs_create_file(cxt->name,
0444, root_dir, cxt, &ldc2114_debugfs_fops);
dev_dbg(ldc->dev, "%s: debugfs file '%s' created\n", __func__, cxt->name);
BLOCKING_INIT_NOTIFIER_HEAD(&cxt->nhead);
return 0;
undo_class_create:
class_destroy(cxt->class);
undo_register_chrdev:
unregister_chrdev(cxt->major, DRVNAME);
undo_platform_device_add:
platform_device_del(cxt->pdev);
undo_malloc:
platform_device_put(cxt->pdev);
kfree(cxt);
remove_dbg_root:
debugfs_remove(root_dir);
return rc;
}
void ldc2114_cdev_remove(void)
void ldc2114_debugfs_remove(struct ldc2114_data *ldc)
{
struct ldc2114_drv *cxt = dev_get_drvdata(ldc->dev);
if (!cxt)
return;
device_destroy(cxt->class, MKDEV(cxt->major, 0));
class_destroy(cxt->class);
unregister_chrdev(cxt->major, DRVNAME);
platform_device_unregister(cxt->pdev);
debugfs_remove(cxt->dentry);
kfree(cxt);
dev_set_drvdata(ldc->dev, NULL);
}
#endif

View file

@ -14,13 +14,10 @@
* General Public License for more details.
*/
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/semaphore.h>
#include <linux/regmap.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
@ -28,10 +25,14 @@
#include <linux/input.h>
#include <linux/reboot.h>
#include "ldc2114_cdev.h"
#include "ldc2114.h"
#define LDC2114_DRIVER_NAME "ldc2114"
#define LDC2114_EV_DATA 1
#define LDC2114_EV_IRQ 2
#define LDC2114_EV_PRESS 3
#define LDC2114_SHORT_DELAY 100
#define LDC2114_LONG_DELAY (LDC2114_SHORT_DELAY * 5)
#define LDC2114_MAX_RETRIES 10
@ -43,7 +44,6 @@
#define WAIT_USEC_LOW (LDC2114_SHORT_DELAY * USEC_PER_MSEC)
#define WAIT_USEC_HIGH (WAIT_USEC_LOW + (WAIT_USEC_LOW >> 3))
#define MAX_KEYS 4
#define irq_to_gpio(irq) ((irq) - gpio_to_irq(0))
/* LDC2114 registers */
@ -87,71 +87,7 @@
#define LDC2114_DEVICE_ID_LSB 0xFE
#define LDC2114_DEVICE_ID_MSB 0xFF
enum ldc2114_fields {
/* Status */
F_STATUS_TIMEOUT, F_STATUS_LC_WD, F_STATUS_FSM_WD, F_STATUS_MAXOUT,
F_STATUS_RDY_TO_WRITE, F_STATUS_CHIP_READY, F_STATUS_OUT_STATUS,
/* Output */
F_OUT_OUT0, F_OUT_OUT1, F_OUT_OUT2, F_OUT_OUT3,
/* Reset */
F_RESET_STATE_RESET, F_RESET_FULL_RESET,
/* Enable */
F_EN_EN0, F_EN_EN1, F_EN_EN2, F_EN_EN3,
F_EN_LPEN0, F_EN_LPEN1, F_EN_LPEN2, F_EN_LPEN3,
/* Scan rate */
F_NP_SCAN_RATE_NPSR, F_LP_SCAN_RATE_LPSR,
/* Gain */
F_GAIN0_GAIN0, F_GAIN1_GAIN1, F_GAIN2_GAIN2, F_GAIN3_GAIN3,
/* Interrupt polarity */
F_INTPOL_INTPOL,
/* Base Increment */
F_LP_BASE_INC_LPBI, F_NP_BASE_INC_NPBI,
/* Max-win */
F_MAXWIN_MAXWIN0, F_MAXWIN_MAXWIN1, F_MAXWIN_MAXWIN2, F_MAXWIN_MAXWIN3,
/* Frequency divider */
F_LC_DIVIDER_LCDIV,
/* Hysteresis */
F_HYST_HYST,
/* Anti-twist */
F_TWIST_ANTITWST,
/* Anti-deform */
F_COMMON_DEFORM_ANTIDFRM0, F_COMMON_DEFORM_ANTIDFRM1,
F_COMMON_DEFORM_ANTIDFRM2, F_COMMON_DEFORM_ANTIDFRM3,
/* Anti-common */
F_COMMON_DEFORM_ANTICM0, F_COMMON_DEFORM_ANTICM1,
F_COMMON_DEFORM_ANTICM2, F_COMMON_DEFORM_ANTICM3,
/* Output Polarity */
F_OPOL_OPOL0, F_OPOL_OPOL1, F_OPOL_OPOL2, F_OPOL_OPOL3,
/* Counter Scale */
F_CNTSC_CNTSC0, F_CNTSC_CNTSC1, F_CNTSC_CNTSC2, F_CNTSC_CNTSC3,
/* Sensor configuration */
F_SENSOR0_CONFIG_SENCYC0, F_SENSOR0_CONFIG_FREQ0, F_SENSOR0_CONFIG_RP0,
F_SENSOR1_CONFIG_SENCYC1, F_SENSOR1_CONFIG_FREQ1, F_SENSOR1_CONFIG_RP1,
F_SENSOR2_CONFIG_SENCYC2, F_SENSOR2_CONFIG_FREQ2, F_SENSOR2_CONFIG_RP2,
F_SENSOR3_CONFIG_SENCYC3, F_SENSOR3_CONFIG_FREQ3, F_SENSOR3_CONFIG_RP3,
/* Fast Tracking Factor */
F_FTF0_FTF0, F_FTF1_2_FTF1, F_FTF1_2_FTF2, F_FTF3_FTF3,
/* sentinel */
F_MAX_FIELDS
};
static int drv_instance_counter;
static const struct reg_field ldc2114_reg_fields[] = {
[F_STATUS_TIMEOUT] = REG_FIELD(LDC2114_STATUS, 1, 1),
@ -225,63 +161,6 @@ static const struct reg_field ldc2114_reg_fields[] = {
[F_FTF3_FTF3] = REG_FIELD(LDC2114_FTF3, 0, 1),
};
struct ldc2114_cfg_data {
uint8_t address, value;
};
struct ldc2114_config {
int size;
struct ldc2114_cfg_data data[0];
};
struct ldc2114_cfg_info {
struct ldc2114_config *normal;
struct ldc2114_config *lpm;
struct ldc2114_config *active;
int attempts;
int delay_ms;
bool toggled;
bool reschedulable;
struct completion done;
};
/**
* struct ldc2114_data - Instance data for LDC2114
* @dev: Device structure
* @regmap - Register map of the device
* @fields: Register fields of the device
* @irq: INTB line interrupt number
*/
struct ldc2114_data {
struct device *dev;
struct regmap *regmap;
struct regmap_field *fields[F_MAX_FIELDS];
atomic_t irq_work_running;
atomic_t poll_work_running;
struct semaphore semaphore;
int intb_gpio;
int lpwm_gpio;
int signal_gpio;
int intb_polarity;
int irq;
int failures;
bool irq_enabled;
bool btn_enabled;
bool data_polling;
bool not_connected;
unsigned int poll_interval;
unsigned int button_map[MAX_KEYS];
struct delayed_work polling_work;
struct delayed_work irq_work;
struct delayed_work config_work;
struct input_dev *input;
struct notifier_block poll_nb;
struct notifier_block reboot_nb;
unsigned int verno;
struct ldc2114_cfg_info config;
struct workqueue_struct *wq;
};
struct ldc2114_attr {
struct device_attribute dev_attr;
unsigned int field;
@ -710,46 +589,6 @@ static const struct regmap_config ldc2114_regmap_config = {
.volatile_table = &ldc2114_volatile_table,
};
#define LDC2114_REG_STATUS_OUT (0x80)
#define LDC2114_REG_STATUS_CHIP_READY (0x40)
#define LDC2114_REG_STATUS_RDY_TO_WRITE (0x20)
#define LDC2114_REG_STATUS_MAXOUT (0x10)
#define LDC2114_REG_STATUS_FSM_WD (0x08)
#define LDC2114_REG_STATUS_LC_WD (0x04)
#define LDC2114_REG_STATUS_TIMEOUT (0x02)
#define LDC2114_REG_STATUS_INTEGRITY (0x01)
#define LDC2114_REG_STATUS_ERROR_MASK (0x0f)
#define LDC2114_REG_STATUS_ERROR_OTHERS (0x1b) /* (ERROR_MASK & ~LC_WD) | MAX_OUT */
#define LDC2114_REG_RESET_FULL (0x10)
#define LDC2114_REG_RESET_CONFIG_MODE (0x01)
#define LDC2114_REG_RESET_NONE (0x00)
struct ldc2114_16bit {
uint8_t lsb;
uint8_t msb;
};
struct ldc2114_raw {
uint8_t out;
struct ldc2114_16bit values[MAX_KEYS];
};
static int buttons[MAX_KEYS];
static int output_bits;
static int inline comp2_12b(struct ldc2114_16bit *data)
{
int result;
/* 12bits 2's compliment data */
if (data->msb & 0x8)
result = (data->lsb | (data->msb << 8) | 0xFFFFF000);
else
result = (data->lsb | (data->msb << 8));
return result;
}
static int ldc2114_input_device(struct ldc2114_data *ldc)
{
int i, ret;
@ -807,16 +646,18 @@ static void ldc2114_toggle(struct ldc2114_data *ldc,
}
}
static int ldc2114_poll(struct ldc2114_data *ldc,
struct ldc2114_raw *data)
static int ldc2114_poll(struct ldc2114_data *ldc, uint8_t *out)
{
int ret;
struct ldc2114_raw_ext de;
static unsigned long executed;
if (ldc->failures >= CONFIG_LDC2114_MAX_FAILURES)
return -EFAULT;
ret = ldc2114_read_bulk(ldc->dev, LDC2114_OUT, data, sizeof(*data));
ret = ldc2114_read_bulk(ldc->dev, LDC2114_STATUS, &de, sizeof(de));
if (out)
*out = de.data.out;
if (ret) {
ldc->failures++;
@ -826,15 +667,13 @@ static int ldc2114_poll(struct ldc2114_data *ldc,
}
} else {
ldc->failures = 0;
ret = ldc2114_buffer(
comp2_12b(&data->values[0]), comp2_12b(&data->values[1]),
comp2_12b(&data->values[2]), comp2_12b(&data->values[3]));
ret = ldc2114_buffer(ldc, LDC2114_EV_DATA, de);
if (ret)
dev_err(ldc->dev, "buffer is not ready\n");
}
if (!(executed++%10))
dev_err(ldc->dev, "polled %lu\n", executed);
dev_info(ldc->dev, "polled %lu\n", executed);
return 0;
}
@ -866,20 +705,12 @@ static void ldc2114_polling_work(struct work_struct *work)
container_of(work, struct delayed_work, work);
struct ldc2114_data *ldc =
container_of(dw, struct ldc2114_data, polling_work);
struct ldc2114_raw data;
int ret;
if (!ldc->irq_enabled) {
uint8_t status;
ret = ldc2114_read_reg8(ldc->dev, LDC2114_STATUS, &status);
dev_dbg(ldc->dev, "status = 0x%02x\n", status);
}
ret = ldc2114_poll(ldc, &data);
ret = ldc2114_poll(ldc, NULL);
if (atomic_read(&ldc->poll_work_running))
queue_delayed_work(ldc->wq, &ldc->polling_work,
schedule_delayed_work(&ldc->polling_work,
msecs_to_jiffies(ldc->poll_interval));
}
@ -895,13 +726,15 @@ static void ldc2114_input_report(struct ldc2114_data *ldc,
for (i = 0, s = 0; i < MAX_KEYS; i++) {
status = (output_bits >> i) & 1;
if (status == buttons[i])
if (status == ldc->buttons[i])
continue;
buttons[i] = status;
input_report_key(ldc->input, ldc->button_map[i], status);
s++;
dev_dbg(ldc->dev, "sent key code = %d(%d)\n",
ldc->button_map[i], status);
ldc->buttons[i] = status;
if (ldc->btn_enabled) {
input_report_key(ldc->input, ldc->button_map[i], status);
s++;
dev_dbg(ldc->dev, "sent key code = %d(%d)\n",
ldc->button_map[i], status);
}
}
if (s) { /* only send sync if there were keys reported */
@ -932,13 +765,12 @@ static void ldc2114_irq_work(struct work_struct *work)
if (status_reg & LDC2114_REG_STATUS_OUT) {
ret = ldc2114_read_bulk(ldc->dev,
LDC2114_OUT, &data, sizeof(data));
if (data.out != output_bits) {
output_bits = data.out;
dev_dbg(ldc->dev, "OUTPUT bits 0x%x\n", output_bits);
if (data.out != ldc->output_bits) {
ldc->output_bits = data.out;
dev_dbg(ldc->dev, "OUTPUT bits 0x%x\n", ldc->output_bits);
}
if (ldc->btn_enabled)
ldc2114_input_report(ldc, output_bits);
ldc2114_input_report(ldc, ldc->output_bits);
}
if (atomic_read(&ldc->irq_work_running)) {
@ -949,8 +781,8 @@ static void ldc2114_irq_work(struct work_struct *work)
up(&ldc->semaphore);
} else {
#ifdef LDC2114_POLL_DEBUG
dev_info(ldc->dev, "polled %d times within %llums\n",
polling_counter,
dev_info(ldc->dev, "%s: polled %d times within %llums\n",
__func__, polling_counter,
timediff_ms(ktime_to_timespec(start_polling_tm),
ktime_to_timespec(ktime_get())));
polling_counter = 0;
@ -960,7 +792,7 @@ static void ldc2114_irq_work(struct work_struct *work)
if (ldc->btn_enabled)
ldc2114_input_report(ldc, 0);
memset(buttons, 0, sizeof(buttons));
memset(ldc->buttons, 0, sizeof(ldc->buttons));
}
}
}
@ -1117,6 +949,11 @@ static int ldc2114_of_init(struct i2c_client *client)
dev_info(ldc->dev, "has keymap\n");
}
if (of_property_read_bool(np, "ldc2114,do-polling")) {
ldc->data_polling = true;
dev_info(ldc->dev,"supports data polling\n");
}
config_np = of_find_node_by_name(np, "configs");
if (!config_np) {
dev_info(ldc->dev, "does not support configs\n");
@ -1284,12 +1121,12 @@ error_workaround:
} else {
/* reading OUT helps with issues when INTB gets stuck due */
/* to internal issues between LDC2114 and Silego parts */
struct ldc2114_raw data;
uint8_t out;
ldc2114_poll(ldc, &data);
ldc2114_poll(ldc, &out);
#ifdef LDC2114_CFG_DEBUG
dev_info(ldc->dev, "OUT 0x%02x, INTB %d\n",
data.out, gpio_get_value(ldc->intb_gpio));
out, gpio_get_value(ldc->intb_gpio));
#endif
/* some errors require STATUS register to be read twice, */
/* thus do STATUS read again here if errors detected */
@ -1326,16 +1163,16 @@ static int ldc2114_poll_enable_cb(struct notifier_block *n,
container_of(n, struct ldc2114_data, poll_nb);
int state = test_bit(0, &val) ? 1 : 0;
dev_info(ldc->dev, "polling flag %d; state=%d\n",
ldc->data_polling, state);
if (!ldc->data_polling)
return 0;
atomic_set(&ldc->poll_work_running, state);
dev_info(ldc->dev, "polling state changed to %d\n", state);
if (state) {
queue_delayed_work(ldc->wq, &ldc->polling_work, 0);
dev_dbg(ldc->dev, "polling resumed\n");
}
if (state)
schedule_work(&ldc->polling_work.work);
return 0;
}
@ -1406,6 +1243,7 @@ static int ldc2114_probe(struct i2c_client *client,
i2c_set_clientdata(client, ldc);
ldc->dev = &client->dev;
ldc->irq = client->irq;
ldc->data_polling = true;
if (client->dev.of_node) {
ret = ldc2114_of_init(client);
@ -1445,6 +1283,7 @@ static int ldc2114_probe(struct i2c_client *client,
return ret;
}
ldc->instance = ++drv_instance_counter;
sema_init(&ldc->semaphore, 0);
if (!ldc->not_connected) {
@ -1469,16 +1308,17 @@ static int ldc2114_probe(struct i2c_client *client,
if (ldc->btn_enabled)
ldc2114_input_device(ldc);
ret = ldc2114_cdev_init();
if (ret < 0 && ret != -ENODEV)
dev_warn(ldc->dev, "Error registering chardev: %d\n", ret);
else {
ldc->data_polling = true;
ret = ldc2114_debugfs_init(ldc);
if (ret < 0) {
ldc->data_polling = false;
if (ret != -ENODEV)
dev_warn(ldc->dev, "Error registering debugfs: %d\n", ret);
} else {
ldc->poll_interval = 250;
INIT_DELAYED_WORK(&ldc->polling_work, ldc2114_polling_work);
ldc->poll_nb.notifier_call = ldc2114_poll_enable_cb;
ret = ldc2114_register_client(&ldc->poll_nb);
ret = ldc2114_register_client(ldc, &ldc->poll_nb);
if (ret < 0)
dev_warn(ldc->dev, "Unable to register notifier: %d\n", ret);
}
@ -1512,7 +1352,7 @@ static int ldc2114_remove(struct i2c_client *client)
if (ldc->input)
input_unregister_device(ldc->input);
ldc2114_cdev_remove();
ldc2114_debugfs_remove(ldc);
sysfs_remove_group(&client->dev.kobj, &ldc2114_attr_group);

View file

@ -0,0 +1,224 @@
#ifndef _LDC2114_HDR_
#define _LDC2114_HDR_
#include <linux/device.h>
#include <linux/notifier.h>
#include <linux/semaphore.h>
#include <linux/atomic.h>
#include <linux/wait.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#define MAX_KEYS 4
#define BUFFER_SIZE 256
#define LDC2114_EV_DATA 1
#define LDC2114_EV_IRQ 2
#define LDC2114_EV_PRESS 3
#define LDC2114_REG_RESET_FULL (0x10)
#define LDC2114_REG_RESET_CONFIG_MODE (0x01)
#define LDC2114_REG_RESET_NONE (0x00)
#define LDC2114_REG_STATUS_OUT (0x80)
#define LDC2114_REG_STATUS_CHIP_READY (0x40)
#define LDC2114_REG_STATUS_RDY_TO_WRITE (0x20)
#define LDC2114_REG_STATUS_MAXOUT (0x10)
#define LDC2114_REG_STATUS_FSM_WD (0x08)
#define LDC2114_REG_STATUS_LC_WD (0x04)
#define LDC2114_REG_STATUS_TIMEOUT (0x02)
#define LDC2114_REG_STATUS_INTEGRITY (0x01)
#define LDC2114_REG_STATUS_ERROR_MASK (0x0f)
#define LDC2114_REG_STATUS_ERROR_OTHERS (0x1b) /* (ERROR_MASK & ~LC_WD) | MAX_OUT */
enum ldc2114_fields {
/* Status */
F_STATUS_TIMEOUT, F_STATUS_LC_WD, F_STATUS_FSM_WD, F_STATUS_MAXOUT,
F_STATUS_RDY_TO_WRITE, F_STATUS_CHIP_READY, F_STATUS_OUT_STATUS,
/* Output */
F_OUT_OUT0, F_OUT_OUT1, F_OUT_OUT2, F_OUT_OUT3,
/* Reset */
F_RESET_STATE_RESET, F_RESET_FULL_RESET,
/* Enable */
F_EN_EN0, F_EN_EN1, F_EN_EN2, F_EN_EN3,
F_EN_LPEN0, F_EN_LPEN1, F_EN_LPEN2, F_EN_LPEN3,
/* Scan rate */
F_NP_SCAN_RATE_NPSR, F_LP_SCAN_RATE_LPSR,
/* Gain */
F_GAIN0_GAIN0, F_GAIN1_GAIN1, F_GAIN2_GAIN2, F_GAIN3_GAIN3,
/* Interrupt polarity */
F_INTPOL_INTPOL,
/* Base Increment */
F_LP_BASE_INC_LPBI, F_NP_BASE_INC_NPBI,
/* Max-win */
F_MAXWIN_MAXWIN0, F_MAXWIN_MAXWIN1, F_MAXWIN_MAXWIN2, F_MAXWIN_MAXWIN3,
/* Frequency divider */
F_LC_DIVIDER_LCDIV,
/* Hysteresis */
F_HYST_HYST,
/* Anti-twist */
F_TWIST_ANTITWST,
/* Anti-deform */
F_COMMON_DEFORM_ANTIDFRM0, F_COMMON_DEFORM_ANTIDFRM1,
F_COMMON_DEFORM_ANTIDFRM2, F_COMMON_DEFORM_ANTIDFRM3,
/* Anti-common */
F_COMMON_DEFORM_ANTICM0, F_COMMON_DEFORM_ANTICM1,
F_COMMON_DEFORM_ANTICM2, F_COMMON_DEFORM_ANTICM3,
/* Output Polarity */
F_OPOL_OPOL0, F_OPOL_OPOL1, F_OPOL_OPOL2, F_OPOL_OPOL3,
/* Counter Scale */
F_CNTSC_CNTSC0, F_CNTSC_CNTSC1, F_CNTSC_CNTSC2, F_CNTSC_CNTSC3,
/* Sensor configuration */
F_SENSOR0_CONFIG_SENCYC0, F_SENSOR0_CONFIG_FREQ0, F_SENSOR0_CONFIG_RP0,
F_SENSOR1_CONFIG_SENCYC1, F_SENSOR1_CONFIG_FREQ1, F_SENSOR1_CONFIG_RP1,
F_SENSOR2_CONFIG_SENCYC2, F_SENSOR2_CONFIG_FREQ2, F_SENSOR2_CONFIG_RP2,
F_SENSOR3_CONFIG_SENCYC3, F_SENSOR3_CONFIG_FREQ3, F_SENSOR3_CONFIG_RP3,
/* Fast Tracking Factor */
F_FTF0_FTF0, F_FTF1_2_FTF1, F_FTF1_2_FTF2, F_FTF3_FTF3,
/* sentinel */
F_MAX_FIELDS
};
struct ldc2114_cfg_data {
uint8_t address, value;
};
struct ldc2114_config {
int size;
struct ldc2114_cfg_data data[0];
};
struct ldc2114_cfg_info {
struct ldc2114_config *normal;
struct ldc2114_config *lpm;
struct ldc2114_config *active;
int attempts;
int delay_ms;
bool toggled;
bool reschedulable;
struct completion done;
};
struct ldc2114_16bit {
uint8_t lsb;
uint8_t msb;
} __packed;
struct ldc2114_raw {
uint8_t out;
struct ldc2114_16bit values[MAX_KEYS];
} __packed;
struct ldc2114_raw_ext {
uint8_t status;
struct ldc2114_raw data;
} __packed;
/**
* struct ldc2114_data - Instance data for LDC2114
* @dev: Device structure
* @regmap - Register map of the device
* @fields: Register fields of the device
* @irq: INTB line interrupt number
*/
struct ldc2114_data {
struct device *dev;
struct regmap *regmap;
struct regmap_field *fields[F_MAX_FIELDS];
atomic_t irq_work_running;
atomic_t poll_work_running;
struct semaphore semaphore;
int intb_gpio;
int lpwm_gpio;
int signal_gpio;
int intb_polarity;
int irq;
int failures;
int instance;
bool irq_enabled;
bool btn_enabled;
bool data_polling;
bool not_connected;
unsigned int poll_interval;
unsigned int button_map[MAX_KEYS];
struct delayed_work polling_work;
struct delayed_work irq_work;
struct delayed_work config_work;
struct input_dev *input;
struct notifier_block poll_nb;
struct notifier_block reboot_nb;
unsigned int verno;
struct ldc2114_cfg_info config;
struct workqueue_struct *wq;
int buttons[MAX_KEYS];
int output_bits;
};
typedef struct data {
int type;
struct ldc2114_raw_ext de;
} item_t;
struct buffer {
struct mutex mutex;
int capacity;
unsigned long opened;
atomic_t start;
atomic_t end;
atomic_t active;
item_t **data;
wait_queue_head_t wait;
};
struct ldc2114_drv {
struct dentry *dentry;
struct device *dev;
char *name;
struct buffer ds;
struct blocking_notifier_head nhead;
};
#if defined(CONFIG_DEBUG_FS)
int ldc2114_debugfs_init(struct ldc2114_data *ldc);
void ldc2114_debugfs_remove(struct ldc2114_data *ldc);
int ldc2114_buffer(struct ldc2114_data *ldc, int type, ...);
int ldc2114_register_client(struct ldc2114_data *ldc, struct notifier_block *nb);
#else
static int ldc2114_debugfs_init(struct ldc2114_data *ldc)
{
return -ENODEV;
}
static void ldc2114_debugfs_remove(struct ldc2114_data *ldc)
{
}
static int ldc2114_buffer(struct ldc2114_data *ldc, int type, ...)
{
return 0;
}
static int ldc2114_register_client(struct ldc2114_data *ldc, struct notifier_block *nb)
{
return -ENODEV;
}
#endif
#endif