android_kernel_xiaomi_sm8450/drivers/iio/ipd.c
Sampath Kumar Sudi 8534f61271 ipd: Adding Support for sku3 device
Adding support to sku3 device, AS per AKM we are moving ipd from
0.3mm to 0.6mm and Z-axis to 3.8mm.

Change-Id: I0d2520d4e72c68f699ea289850a183833c8df746
Signed-off-by: Sampath Kumar Sudi <quic_ssudi@quicinc.com>
2024-03-13 12:34:12 -07:00

1195 lines
29 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/bitops.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/acpi.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/cpumask.h>
#include <linux/iio/consumer.h>
// REGISTER MAP
#define IPD_REG_WIA 0x00
#define ipd_DEVICE_ID 0x48C1
#define IPD_REG_WORD_ST 0x10
#define IPD_REG_WORD_ST_X 0x11
#define IPD_REG_WORD_ST_Y 0x12
#define IPD_REG_WORD_ST_Y_X 0x13
#define IPD_REG_WORD_ST_Z 0x14
#define IPD_REG_WORD_ST_Z_X 0x15
#define IPD_REG_WORD_ST_Z_Y 0x16
#define IPD_REG_WORD_ST_Z_Y_X 0x17
#define IPD_REG_BYTE_ST_V 0x18
#define IPD_REG_BYTE_ST_X 0x19
#define IPD_REG_BYTE_ST_Y 0x1A
#define IPD_REG_BYTE_ST_Y_X 0x1B
#define IPD_REG_BYTE_ST_Z 0x1C
#define IPD_REG_BYTE_ST_Z_X 0x1D
#define IPD_REG_BYTE_ST_Z_Y 0x1E
#define IPD_REG_BYTE_ST_Z_Y_X 0x1F
#define IPD_REG_CNTL1 0x20
#define IPD_REG_CNTL2 0x21
#define IPD_REG_THX 0x22
#define IPD_REG_THY 0x23
#define IPD_REG_THZ 0x24
#define IPD_REG_THV 0x25
#define IPD_REG_SRST 0x30
#define ipd_MAX_REGS IPD_REG_SRST
#define IPD_MEASUREMENT_WAIT_TIME 2
#define IPD_WORD_LEN 2
#define SECOND_VAL_ENABLED 2
#define IPD_STEP_BASE 2
#define IPD_MAX_REG_LEN 7
#define MAX_IPD_RETRIES 1
#define MIN_SLEEP_TIME 100
#define IPD_MIN_STEP 0
#define IPD_MAX_STEP 6
#define MIN_READ_VALUE 32768
#define MAX_READ_VALUE 65536
#define SKU1_SKU2_DEV_ID 1
#define SKU3_DEV_ID 2
#define INVALID_DEV_ID 0
static int BOPX[] = {400, 400, 400, 400, 400, 400, 0, 0};
static int BRPX[] = {250, 250, 250, 250, 250, 250, 0, 0};
static u16 BOPZ[] = {0, 65286, 65286, 65286, 150, 400, 400, 400};
static u16 BRPZ[] = {0, 65136, 65136, 65136, 50, 250, 250, 250};
static u16 CTRL1[] = {0x02, 0x40A, 0x40A, 0x40A, 0x0A, 0x0A, 0x8, 0x8};
static int modeBitTable[] = {0, 0x2, 0x4, 0x6, 0x8, 0xA, 0xC, 0xE, 0x10};
enum {
ipd_MSRNO_WORD_ST = 0,
ipd_MSRNO_WORD_ST_X, // 1
ipd_MSRNO_WORD_ST_Y, // 2
ipd_MSRNO_WORD_ST_Y_X, // 3
ipd_MSRNO_WORD_ST_Z, // 4
ipd_MSRNO_WORD_ST_Z_X, // 5
ipd_MSRNO_WORD_ST_Z_Y, // 6
ipd_MSRNO_WORD_ST_Z_Y_X, // 7
ipd_MSRNO_WORD_ST_V, // 8
ipd_MSRNO_BYTE_ST_X, // 9
ipd_MSRNO_BYTE_ST_Y, // 10
ipd_MSRNO_BYTE_ST_Y_X, // 11
ipd_MSRNO_BYTE_ST_Z, // 12
ipd_MSRNO_BYTE_ST_Z_X, // 13
ipd_MSRNO_BYTE_ST_Z_Y, // 14
ipd_MSRNO_BYTE_ST_Z_Y_X, // 15
};
static struct mutex ipd_mutex;
struct ipd_data {
struct i2c_client *client;
struct iio_channel *adc;
struct task_struct *distance_thread;
struct work_struct update_registers;
struct workqueue_struct *ipd_wq;
int int_gpio;
int irq;
int enable;
int resistance;
int ipd_distance;
int near_res;
int far_res;
int near_ipd;
int far_ipd;
int sleep_time;
int enable_print;
int ipd_retry;
int enable_interrupt;
u8 mode;
u8 device_type;
s16 numMode;
u8 msrNo;
u8 DRDYENbit;
u8 SWXENbit;
u8 SWYENbit;
u8 SWZENbit;
u8 SWVENbit;
u8 ERRENbit;
u8 POLXbit;
u8 POLYbit;
u8 POLZbit;
u8 POLVbit;
u8 SDRbit;
u8 SMRbit;
u16 BOPXbits;
u16 BRPXbits;
u16 BOPYbits;
u16 BRPYbits;
u16 BOPZbits;
u16 BRPZbits;
u16 BOPVbits;
u16 BRPVbits;
};
static int ipd_read_axis(struct ipd_data *ipd, int address, int *val);
static int ipd_read_axis_u(struct ipd_data *ipd, int address, u16 *val);
static void ipd_compute_distance(struct ipd_data *ipd);
static void ipd_read_control_status_register(struct ipd_data *ipd);
static int ipd_i2c_reads(struct i2c_client *client, u8 *reg, int reglen, u8 *rdata, int datalen)
{
struct i2c_msg xfer[2];
int ret;
/* Write register */
xfer[0].addr = client->addr;
xfer[0].flags = 0;
xfer[0].len = reglen;
xfer[0].buf = reg;
/* Read data */
xfer[1].addr = client->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = datalen;
xfer[1].buf = rdata;
ret = i2c_transfer(client->adapter, xfer, 2);
if (ret == 2)
return 0;
else if (ret < 0)
return ret;
else
return -EIO;
}
static int ipd_i2c_read8_16(struct i2c_client *client,
u8 address, int wordLen, u16 *rdata)
{
u8 tx[1];
u8 rx[8];
int i, ret;
if ((wordLen < 1) || (wordLen > 4)) {
dev_err(&client->dev, "[ipd] %s Read Word Length Error %d\n", __func__, wordLen);
return -EINVAL;
}
tx[0] = address;
ret = ipd_i2c_reads(client, tx, 1, rx, ((2 * wordLen) - 1));
if (ret < 0) {
dev_err(&client->dev, "[ipd] I2C Read Error\n");
for (i = 0 ; i < wordLen ; i++)
rdata[i] = 0;
} else {
rdata[0] = (u16)rx[0];
for (i = 1 ; i < wordLen ; i++)
rdata[i] = ((u16)rx[2*i-1] << 8) + (u16)rx[2*i];
}
pr_debug("[ipd] %s, addr = %x\n", __func__, address);
for (i = 0 ; i < ((2 * wordLen) - 1) ; i++)
pr_debug("%x\n", (int)rx[i]);
pr_debug("\n");
return ret;
}
static int ipd_i2c_write(struct i2c_client *client, const u8 *tx, size_t wlen)
{
int ret;
ret = i2c_master_send(client, tx, wlen);
if (ret != wlen)
pr_err("%s: comm error, ret %d, wlen %d\n", __func__, ret, (int)wlen);
return ret;
}
static s32 ipd_i2c_write16(struct i2c_client *client,
u8 address, int valueNum, u16 value1, u16 value2)
{
u8 tx[5];
s32 ret;
int n;
if ((valueNum != 1) && (valueNum != 2)) {
pr_err("%s: valueNum error, valueNum= %d\n", __func__, valueNum);
return -EINVAL;
}
n = 0;
tx[n++] = address;
tx[n++] = (u8)((0xFF00 & value1) >> 8);
tx[n++] = (u8)(0xFF & value1);
pr_debug("[ipd]%02XH,%02XH,%02XH\n", (int)tx[0], (int)tx[1], (int)tx[2]);
if (valueNum == 2) {
tx[n++] = (u8)((0xFF00 & value2) >> 8);
tx[n++] = (u8)(0xFF & value2);
}
ret = ipd_i2c_write(client, tx, n);
return ret;
}
static void ipd_read_control_status_register(struct ipd_data *ipd)
{
int rValue = 0;
int ret;
ret = ipd_read_axis(ipd, IPD_REG_WORD_ST_Z_X, &rValue);
pr_info("[ipd] IPD_REG_WORD_ST_X\n");
ret = ipd_read_axis(ipd, IPD_REG_WORD_ST_X, &rValue);
pr_info("[ipd] IPD_REG_WORD_ST_Y\n");
ret = ipd_read_axis(ipd, IPD_REG_WORD_ST_Y, &rValue);
pr_info("[ipd] IPD_REG_WORD_ST_Z\n");
ret = ipd_read_axis(ipd, IPD_REG_WORD_ST_Z, &rValue);
}
static void ipd_compute_distance(struct ipd_data *ipd)
{
int ret = 0;
ret = iio_read_channel_processed(ipd->adc, &ipd->resistance);
if (ret) {
if (ipd->resistance < ipd->near_res) {
ipd->ipd_distance = ipd->near_ipd - 1;
if ((ipd->near_res - ipd->resistance) >= 500)
ipd->ipd_distance = ipd->near_ipd - IPD_STEP_BASE;
} else if (ipd->resistance > ipd->far_res) {
ipd->ipd_distance = ipd->far_ipd + IPD_STEP_BASE;
} else {
ipd->ipd_distance = (int)(ipd->near_ipd +
(((ipd->far_ipd - ipd->near_ipd) * (ipd->resistance - ipd->near_res))
/ (ipd->far_res - ipd->near_res)));
}
} else
pr_err("[ipd] Failed to read Reistance value (%d)\n", ret);
}
int ipd_thread_function(void *ipd_)
{
struct ipd_data *ipd = ipd_;
while (!kthread_should_stop()) {
ipd_compute_distance(ipd);
if (ipd->enable_print) {
/*READ Status Control register */
ipd_read_control_status_register(ipd);
pr_info("In ipd Thread Function && resistance=%d && distance=%f\n",
ipd->resistance, ipd->ipd_distance);
}
if (ipd->sleep_time)
msleep(ipd->sleep_time);
}
return 0;
}
static ssize_t enable_thread_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", ipd->enable);
}
static ssize_t enable_thread_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
int ret;
ret = sscanf(buf, "%d\n", &ipd->enable);
if (ipd->enable) {
if (ipd->distance_thread) {
pr_debug("ipd resuming kthread\n");
wake_up_process(ipd->distance_thread);
} else {
pr_debug("ipd creating kthread\n");
ipd->distance_thread = kthread_create(
ipd_thread_function, ipd,
"IPD Thread");
if (ipd->distance_thread)
wake_up_process(ipd->distance_thread);
else
pr_err("%s ERROR creating IPD Thread\n", __func__);
}
} else {
if (ipd->distance_thread) {
kthread_stop(ipd->distance_thread);
ipd->distance_thread = NULL;
}
}
return count;
}
static ssize_t ipd_distance_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", ipd->ipd_distance);
}
static ssize_t ipd_distance_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
pr_info("cannot store ipd_distance\n");
return count;
}
static ssize_t resistance_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", ipd->resistance);
}
static ssize_t resistance_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
pr_info("cannot store resistance\n");
return count;
}
static ssize_t near_res_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", ipd->near_res);
}
static ssize_t near_res_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
int ret;
ret = sscanf(buf, "%d\n", &ipd->near_res);
return count;
}
static ssize_t far_res_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", ipd->far_res);
}
static ssize_t far_res_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
int ret;
ret = sscanf(buf, "%d\n", &ipd->far_res);
return count;
}
static ssize_t ipd_retry_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", ipd->ipd_retry);
}
static ssize_t ipd_retry_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
int ret;
ret = sscanf(buf, "%d\n", &ipd->ipd_retry);
return count;
}
static ssize_t near_ipd_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", ipd->near_ipd);
}
static ssize_t near_ipd_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
int ret;
ret = sscanf(buf, "%d\n", &ipd->near_ipd);
return count;
}
static ssize_t far_ipd_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", ipd->far_ipd);
}
static ssize_t far_ipd_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
int ret;
ret = sscanf(buf, "%d\n", &ipd->far_ipd);
return count;
}
static ssize_t sleep_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", ipd->sleep_time);
}
static ssize_t sleep_time_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
int ret;
ret = sscanf(buf, "%d\n", &ipd->sleep_time);
return count;
}
static ssize_t enable_print_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", ipd->enable_print);
}
static ssize_t enable_print_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
int ret;
ret = sscanf(buf, "%d\n", &ipd->enable_print);
return count;
}
static ssize_t enable_interrupt_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", ipd->enable_interrupt);
}
static ssize_t enable_interrupt_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
int ret;
ret = sscanf(buf, "%d\n", &ipd->enable_interrupt);
/*Enable disable interrupt on sys node*/
if (ipd->enable_interrupt)
enable_irq(ipd->irq);
else
disable_irq(ipd->irq);
return count;
}
static DEVICE_ATTR_RW(enable_thread);
static DEVICE_ATTR_RW(ipd_distance);
static DEVICE_ATTR_RW(resistance);
static DEVICE_ATTR_RW(near_ipd);
static DEVICE_ATTR_RW(far_ipd);
static DEVICE_ATTR_RW(near_res);
static DEVICE_ATTR_RW(far_res);
static DEVICE_ATTR_RW(sleep_time);
static DEVICE_ATTR_RW(enable_print);
static DEVICE_ATTR_RW(enable_interrupt);
static DEVICE_ATTR_RW(ipd_retry);
static struct attribute *ipd_i2c_sysfs_attrs[] = {
&dev_attr_enable_thread.attr,
&dev_attr_ipd_distance.attr,
&dev_attr_resistance.attr,
&dev_attr_far_res.attr,
&dev_attr_near_res.attr,
&dev_attr_far_ipd.attr,
&dev_attr_near_ipd.attr,
&dev_attr_sleep_time.attr,
&dev_attr_enable_print.attr,
&dev_attr_enable_interrupt.attr,
&dev_attr_ipd_retry.attr,
NULL,
};
static struct attribute_group ipd_i2c_attribute_group = {
.attrs = ipd_i2c_sysfs_attrs,
};
static int ipd_compute_step(struct ipd_data *ipd)
{
int step_mode = 0;
if (ipd->resistance < ipd->near_res) {
step_mode = IPD_MIN_STEP + 1;
if ((ipd->near_res - ipd->resistance) >= 500)
step_mode = IPD_MIN_STEP;
} else if (ipd->resistance > ipd->far_res) {
step_mode = IPD_MAX_STEP;
} else {
step_mode = (int)((((ipd->far_ipd - ipd->near_ipd)
* (ipd->resistance - ipd->near_res))
/ (ipd->far_res - ipd->near_res)));
step_mode += IPD_STEP_BASE;
if (step_mode != 1)
step_mode = (step_mode / 2);
}
return step_mode;
}
static void ipd_update_y_z_axis(struct ipd_data *ipd, int step)
{
u16 readValue = 0;
int status;
/*Read Y axis */
status = ipd_read_axis_u(ipd, IPD_REG_WORD_ST_Y, &readValue);
if (ipd->enable_print)
pr_info("[ipd] Y raw readValue=%d\n",
readValue >= MIN_READ_VALUE ? readValue - MAX_READ_VALUE : readValue);
ipd->BOPYbits = readValue + 400;
ipd->BRPYbits = readValue + 250;
status = ipd_i2c_write16(ipd->client, IPD_REG_THY,
SECOND_VAL_ENABLED, ipd->BOPYbits, ipd->BRPYbits);
readValue = 0;
/*Read Z axis */
status = ipd_read_axis_u(ipd, IPD_REG_WORD_ST_Z, &readValue);
if (ipd->enable_print)
pr_info("[ipd] Z raw readValue=%d\n",
readValue >= MIN_READ_VALUE ? readValue - MAX_READ_VALUE : readValue);
ipd->BOPZbits = readValue + 760;
ipd->BRPZbits = readValue + 610;
status = ipd_i2c_write16(ipd->client, IPD_REG_THZ,
SECOND_VAL_ENABLED, ipd->BOPZbits, ipd->BRPZbits);
}
static void ipd_update_x_z_axis(struct ipd_data *ipd, int step)
{
u16 readValue;
int status;
/*Read X axis */
status = ipd_read_axis_u(ipd, IPD_REG_WORD_ST_X, &readValue);
if (ipd->enable_print)
pr_info("[ipd] %s : X raw readValue=%d\n",
__func__, readValue >= MIN_READ_VALUE ?
readValue - MAX_READ_VALUE : readValue);
ipd->BOPXbits = readValue + BOPX[step];
ipd->BRPXbits = readValue + BRPX[step];
status = ipd_i2c_write16(ipd->client, IPD_REG_THX,
SECOND_VAL_ENABLED, ipd->BOPXbits, ipd->BRPXbits);
/*Read Z axis */
status = ipd_read_axis_u(ipd, IPD_REG_WORD_ST_Z, &readValue);
if (ipd->enable_print)
pr_info("[ipd] Z raw readValue=%d\n", readValue >= MIN_READ_VALUE ?
readValue - MAX_READ_VALUE : readValue);
ipd->BOPZbits = readValue + BOPZ[step];
ipd->BRPZbits = readValue + BRPZ[step];
status = ipd_i2c_write16(ipd->client, IPD_REG_THZ,
SECOND_VAL_ENABLED, ipd->BOPZbits, ipd->BRPZbits);
}
static void ipd_update_y_v_axis(struct ipd_data *ipd, int step)
{
int status;
/*Read Z axis */
status = ipd_i2c_write16(ipd->client, IPD_REG_THY,
SECOND_VAL_ENABLED, ipd->BOPYbits, ipd->BRPYbits);
/*Read V axis */
status = ipd_i2c_write16(ipd->client, IPD_REG_THV,
SECOND_VAL_ENABLED, ipd->BOPVbits, ipd->BRPVbits);
}
static int ipd_write_mode(struct ipd_data *ipd, int modeBit)
{
int mode;
int ret;
mode = (ipd->SMRbit << 6) + (ipd->SDRbit << 5) + modeBit;
pr_info("[ipd] REG_CNTL2(%x), value(%d))\n", IPD_REG_CNTL2, mode);
ret = i2c_smbus_write_byte_data(ipd->client, (u8)IPD_REG_CNTL2, (u8)mode);
if (ret < 0)
pr_err("%s: comm error, ret= %d\n", __func__, ret);
return ret;
}
static int ipd_read_axis(struct ipd_data *akm, int address, int *val)
{
u16 rdata[4] = {0,};
u16 wordLen = IPD_WORD_LEN;
if (akm->mode == 0)
ipd_write_mode(akm, 1);
*val = 0;
if ((address == IPD_REG_WORD_ST || address == IPD_REG_CNTL2))
wordLen = 1;
if ((address == IPD_REG_WORD_ST_Z_X) || (address == IPD_REG_WORD_ST_Y_X) ||
(address == IPD_REG_WORD_ST_Z_Y))
wordLen = 3;
if ((address == IPD_REG_WORD_ST_Z) || (address == IPD_REG_WORD_ST_Y) ||
(address == IPD_REG_WORD_ST_X))
wordLen = 2;
ipd_i2c_read8_16(akm->client, address, wordLen, rdata);
pr_info("[ipd] %s address:%x (%x)(%x)\n",
__func__, address, (int)rdata[0], (int)rdata[1]);
*val = rdata[wordLen - 1];
if (address == IPD_REG_WORD_ST || address == IPD_REG_CNTL2)
*val &= 0x3FF;
return 0;
}
static int ipd_read_axis_u(struct ipd_data *ipd, int address, u16 *val)
{
u16 rdata[IPD_WORD_LEN] = {0,};
if (ipd->mode == 0)
ipd_write_mode(ipd, 0x8);
*val = 0;
ipd_i2c_read8_16(ipd->client, address, IPD_WORD_LEN, rdata);
*val = rdata[IPD_WORD_LEN - 1];
if (ipd->enable_print)
pr_debug("[ipd] %s address :%x (%x)(%x)\n",
__func__, address, rdata[0], rdata[1]);
if (*val >= MIN_READ_VALUE)
*val -= MAX_READ_VALUE;
return 0;
}
static int ipd_setup(struct i2c_client *client)
{
struct ipd_data *ipd = i2c_get_clientdata(client);
u8 mod_value = 0;
u16 ctrl1_reg = 0;
int status;
int step = 0;
pr_info("[ipd] %s Device_type (%d)\n", __func__, ipd->device_type);
if (ipd->mode < ARRAY_SIZE(modeBitTable))
mod_value = modeBitTable[ipd->mode];
status = ipd_write_mode(ipd, mod_value);
ipd_compute_distance(ipd);
/*Update CTRL1 Register */
ctrl1_reg = (u16)(ipd->POLXbit + (ipd->POLYbit << 1)
+ (ipd->POLZbit << 2) + (ipd->POLVbit << 3));
ctrl1_reg <<= 8;
ctrl1_reg += ((u16)(ipd->DRDYENbit + (ipd->SWXENbit << 1)
+ (ipd->SWYENbit << 2) + (ipd->SWZENbit << 3)
+ (ipd->SWVENbit << 4) + (ipd->ERRENbit << 5)));
status = ipd_i2c_write16(ipd->client, IPD_REG_CNTL1, 1, ctrl1_reg, 0);
pr_info("[ipd] IPD_REG_CNTL1 (%x) Value (%d)\n", IPD_REG_CNTL1, ctrl1_reg);
ipd->client = client;
step = ipd_compute_step(ipd);
/*On device type,We need update or skip updating BOPX/BRPX/BOPZ/BRPZ*/
if (ipd->device_type == SKU1_SKU2_DEV_ID) {
/*Update X and Z axis*/
ipd_update_x_z_axis(ipd, step);
/*Update Y and V axis*/
ipd_update_y_v_axis(ipd, step);
status = ipd_i2c_write16(ipd->client, IPD_REG_CNTL1, 1, CTRL1[step], 0);
} else if (ipd->device_type == SKU3_DEV_ID) {
/*Update Y and Z axis*/
ipd_update_y_z_axis(ipd, step);
}
pr_info("***********************IPD-SETUP**************************************************\n");
if (ipd->device_type != INVALID_DEV_ID) {
pr_info("[ipd] BOPX : (%d) BRPX : (%d)\n", ipd->BOPXbits, ipd->BRPXbits);
pr_info("[ipd] BOPY : (%d) BRPY : (%d)\n", ipd->BOPYbits, ipd->BRPYbits);
pr_info("[ipd] BOPZ : (%d) BRPZ : (%d)\n", ipd->BOPZbits, ipd->BRPZbits);
pr_info("[ipd] BOPV : (%d) BRPV : (%d)\n", ipd->BOPVbits, ipd->BRPVbits);
pr_info("[ipd] STEP : (%d) IPD_REG_CNTL1: (%d)\n", step, CTRL1[step]);
}
pr_info("[ipd] MODE : (%d) Device_type : (%d)\n", ipd->mode, ipd->device_type);
pr_info("[ipd] MIN_IPD : (%d) MAX_IPD : (%d)\n", ipd->near_ipd, ipd->far_ipd);
pr_info("[ipd] MIN_RES : (%d) MAX_RES : (%d)\n", ipd->near_res, ipd->far_res);
pr_info("[ipd] Reistance (%d) Distance (%d)\n", ipd->resistance, ipd->ipd_distance);
pr_info("****************************IPD-SETUP-END*****************************************\n");
return 0;
}
static int ipd_parse_dt(struct ipd_data *ipd)
{
u32 buf[8];
struct device *dev;
struct device_node *np;
int ret;
pr_info("[ipd] %s(%d)\n", __func__, __LINE__);
dev = &(ipd->client->dev);
np = dev->of_node;
if (!np)
return -EINVAL;
ret = of_property_read_u32_array(np, "ipd,measurment_number", buf, 1);
if (ret < 0)
ipd->msrNo = ipd_MSRNO_WORD_ST_Z_Y_X;
else {
ipd->msrNo = buf[0];
if (buf[0] > ipd_MSRNO_BYTE_ST_Z_Y_X)
ipd->msrNo = ipd_MSRNO_WORD_ST_Z_Y_X;
}
ret = of_property_read_u32_array(np, "ipd,DRDY_event", buf, 1);
if (ret < 0)
ipd->DRDYENbit = 1;
else
ipd->DRDYENbit = buf[0];
ret = of_property_read_u32_array(np, "ipd,ERR_event", buf, 1);
if (ret < 0)
ipd->ERRENbit = 1;
else
ipd->ERRENbit = buf[0];
ret = of_property_read_u32_array(np, "ipd,POL_setting", buf, 4);
if (ret < 0) {
ipd->POLXbit = 0;
ipd->POLYbit = 0;
ipd->POLZbit = 0;
ipd->POLVbit = 0;
} else {
ipd->POLXbit = buf[0];
ipd->POLYbit = buf[1];
ipd->POLZbit = buf[2];
ipd->POLVbit = buf[3];
}
ret = of_property_read_u32_array(np, "ipd,SDR_setting", buf, 1);
if (ret < 0)
ipd->SDRbit = 0;
else
ipd->SDRbit = buf[0];
ret = of_property_read_u32_array(np, "ipd,Mode", buf, 1);
if (ret < 0)
ipd->mode = 0;
else
ipd->mode = buf[0];
ret = of_property_read_u32_array(np, "ipd,near_ipd", buf, 1);
if (ret < 0)
ipd->near_ipd = 0;
else
ipd->near_ipd = buf[0];
ret = of_property_read_u32_array(np, "ipd,far_ipd", buf, 1);
if (ret < 0)
ipd->far_ipd = 0;
else
ipd->far_ipd = buf[0];
ret = of_property_read_u32_array(np, "ipd,near_res", buf, 1);
if (ret < 0)
ipd->near_res = 0;
else
ipd->near_res = buf[0];
ret = of_property_read_u32_array(np, "ipd,far_res", buf, 1);
if (ret < 0)
ipd->far_res = 0;
else
ipd->far_res = buf[0];
ret = of_property_read_u32_array(np, "ipd,Device_type", buf, 1);
if (ret < 0)
ipd->device_type = 0;
else
ipd->device_type = buf[0];
ret = of_property_read_u32_array(np, "ipd,SMR_setting", buf, 1);
if (ret < 0)
ipd->SMRbit = 0;
else
ipd->SMRbit = buf[0];
ret = of_property_read_u32_array(np, "ipd,threshold_X", buf, 2);
if (ret < 0) {
ipd->BOPXbits = 0;
ipd->BRPXbits = 0;
} else {
ipd->BOPXbits = buf[0];
ipd->BRPXbits = buf[1];
}
ret = of_property_read_u32_array(np, "ipd,threshold_Y", buf, 2);
if (ret < 0) {
ipd->BOPYbits = 0;
ipd->BRPYbits = 0;
} else {
ipd->BOPYbits = buf[0];
ipd->BRPYbits = buf[1];
}
ret = of_property_read_u32_array(np, "ipd,threshold_Z", buf, 2);
if (ret < 0) {
ipd->BOPZbits = 0;
ipd->BRPZbits = 0;
} else {
ipd->BOPZbits = buf[0];
ipd->BRPZbits = buf[1];
}
ret = of_property_read_u32_array(np, "ipd,threshold_V", buf, 2);
if (ret < 0) {
ipd->BOPVbits = 0;
ipd->BRPVbits = 0;
} else {
ipd->BOPVbits = buf[0];
ipd->BRPVbits = buf[1];
}
ret = of_property_read_u32_array(np, "ipd,SW_event", buf, 4);
if (ret < 0) {
ipd->SWXENbit = 0;
ipd->SWYENbit = 0;
ipd->SWZENbit = 0;
ipd->SWVENbit = 0;
} else {
ipd->SWXENbit = buf[0];
ipd->SWYENbit = buf[1];
ipd->SWZENbit = buf[2];
ipd->SWVENbit = buf[3];
}
return 0;
}
/*
* Handle data ready irq
*/
static void ipd_update_registers(struct work_struct *w)
{
struct ipd_data *ipd = container_of(w, struct ipd_data, update_registers);
unsigned int retry = ipd->ipd_retry;
int step = 0;
int ret = 0;
mutex_lock(&ipd_mutex);
do {
retry -= 1;
ipd_compute_distance(ipd);
if (ipd->enable_print)
ipd_read_control_status_register(ipd);
step = ipd_compute_step(ipd);
/*Based in device type
* We need update or skip updating BOPX/BRPX/BOPZ/BRPZ
*/
if (ipd->device_type == SKU1_SKU2_DEV_ID) {
/*CNTL1 Registers*/
ret = ipd_i2c_write16(ipd->client, IPD_REG_CNTL1, 1, CTRL1[step], 0);
/*Update X and Z axis*/
ipd_update_x_z_axis(ipd, step);
} else if (ipd->device_type == SKU3_DEV_ID) {
/*Update Y and Z axis*/
ipd_update_y_z_axis(ipd, step);
}
if (retry)
msleep(ipd->sleep_time);
if (ipd->enable_print) {
if (ipd->device_type) {
pr_info("[ipd] BOPX : (%d) BRPX : (%d)\n", ipd->BOPXbits,
ipd->BRPXbits);
pr_info("[ipd] BOPY : (%d) BRPY : (%d)\n", ipd->BOPYbits,
ipd->BRPYbits);
pr_info("[ipd] BOPZ : (%d) BRPZ : (%d)\n", ipd->BOPZbits,
ipd->BRPZbits);
}
pr_info("[ipd] MODE : (%d) STEP : (%d) IPD_REG_CNTL1: (%d)\n",
modeBitTable[ipd->mode], step, CTRL1[step]);
pr_info("[ipd] MIN_IPD : (%d) MAX_IPD : (%d)\n",
ipd->near_ipd, ipd->far_ipd);
pr_info("[ipd] MIN_RES : (%d) MAX_RES : (%d)\n",
ipd->near_res, ipd->far_res);
pr_info("[ipd] DISTANCE : (%d) RESISTANCE : (%d)\n",
ipd->ipd_distance, ipd->resistance);
}
} while (retry);
mutex_unlock(&ipd_mutex);
/*enable IRQ's*/
enable_irq(ipd->irq);
}
static irqreturn_t ipd_irq_handler(int irq, void *data)
{
struct ipd_data *ipd = data;
disable_irq_nosync(ipd->irq);
if (ipd->enable_print)
pr_info("In ipd INT Handler ENTER %d\n", ipd->irq);
queue_work(ipd->ipd_wq, &ipd->update_registers);
if (ipd->enable_print)
pr_info("In ipd INT Handler EXIT %d\n", ipd->irq);
return IRQ_HANDLED;
}
static int ipd_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ipd_data *ipd;
int err;
const char *name = NULL;
ipd = devm_kzalloc(&client->dev, sizeof(*ipd), GFP_KERNEL);
if (!ipd)
return -ENOMEM;
i2c_set_clientdata(client, ipd);
dev_set_drvdata(&client->dev, ipd);
ipd->client = client;
dev_err(&client->dev, "Probe IPD Driver\n");
INIT_WORK(&ipd->update_registers, ipd_update_registers);
ipd->ipd_wq = alloc_ordered_workqueue("ipd_wq", 0);
ipd->adc = devm_iio_channel_get(&(ipd->client->dev), "ipd");
if (IS_ERR(ipd->adc)) {
dev_err(&client->dev, "Not able to fetch ipd IIO channel\n");
return PTR_ERR(ipd->adc);
}
ipd->int_gpio = of_get_gpio(client->dev.of_node, 0);
if (gpio_is_valid(ipd->int_gpio)) {
err = devm_gpio_request_one(&client->dev, ipd->int_gpio,
GPIOF_IN, "ipd_int");
if (err < 0) {
dev_err(&client->dev, "[ipd] failed to request GPIO %d, error %d\n",
ipd->int_gpio, err);
return err;
}
}
ipd->irq = gpio_to_irq(ipd->int_gpio);
err = ipd_parse_dt(ipd);
if (err < 0)
dev_err(&client->dev, "[ipd] Device Tree Setting was not found!\n");
if (id)
name = id->name;
if (ipd->irq) {
err = devm_request_irq(&client->dev, ipd->irq,
ipd_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"ipd_INT", ipd);
if (err < 0) {
dev_err(&client->dev, "%s dev_request_irq fails\n", name);
return err;
}
pr_info("[ipd] %s Register Trigger Interrupt :%d\n", __func__, err);
}
ipd->sleep_time = MIN_SLEEP_TIME;
ipd->enable_interrupt = 1;
ipd->ipd_retry = MAX_IPD_RETRIES;
err = ipd_setup(client);
if (err < 0) {
dev_err(&client->dev, "%s initialization fails\n", name);
return err;
}
mutex_init(&ipd_mutex);
err = devm_device_add_group(&client->dev, &ipd_i2c_attribute_group);
if (err < 0) {
dev_err(&client->dev, "couldn't register sysfs group\n");
return err;
}
pr_info("[ipd] %s(ipd_device_register=%d)\n", __func__, err);
return err;
}
static int ipd_remove(struct i2c_client *client)
{
struct ipd_data *ipd = i2c_get_clientdata(client);
if (ipd->distance_thread) {
kthread_stop(ipd->distance_thread);
ipd->distance_thread = NULL;
}
i2c_unregister_device(client);
return 0;
}
static int ipd_i2c_suspend(struct device *dev)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
if (ipd->distance_thread) {
kthread_stop(ipd->distance_thread);
ipd->distance_thread = NULL;
} else {
pr_info("[ipd] %s\n", __func__);
mutex_lock(&ipd_mutex);
disable_irq_nosync(ipd->irq);
mutex_unlock(&ipd_mutex);
}
return 0;
}
static int ipd_i2c_resume(struct device *dev)
{
struct ipd_data *ipd = dev_get_drvdata(dev);
if (ipd->enable) {
ipd->distance_thread = kthread_create(ipd_thread_function, ipd, "IPD Thread");
if (ipd->distance_thread)
wake_up_process(ipd->distance_thread);
else
pr_err("%s ERROR creating IPD Thread\n", __func__);
} else {
pr_info("[ipd] %s\n", __func__);
mutex_lock(&ipd_mutex);
enable_irq(ipd->irq);
mutex_unlock(&ipd_mutex);
}
return 0;
}
static const struct dev_pm_ops ipd_i2c_pops = {
.suspend = ipd_i2c_suspend,
.resume = ipd_i2c_resume,
};
static const struct i2c_device_id ipd_id[] = {
{ "ipd", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ipd_id);
static const struct of_device_id ipd_of_match[] = {
{ .compatible = "qcom,ipd"},
{}
};
MODULE_DEVICE_TABLE(of, ipd_of_match);
static struct i2c_driver ipd_driver = {
.driver = {
.name = "ipd",
.pm = &ipd_i2c_pops,
.of_match_table = of_match_ptr(ipd_of_match),
},
.probe = ipd_probe,
.remove = ipd_remove,
.id_table = ipd_id,
};
module_i2c_driver(ipd_driver);
MODULE_DESCRIPTION("Inter Pupillary Distance(IPD) Driver");
MODULE_LICENSE("GPL v2");