1830 lines
48 KiB
C
1830 lines
48 KiB
C
/*! @file vfs7xxx.c
|
|
*******************************************************************************
|
|
** SPI Driver Interface Functions
|
|
**
|
|
** This file contains the SPI driver interface functions.
|
|
**
|
|
** Copyright (C) 2011-2013 Validity Sensors, Inc.
|
|
** This program is free software; you can redistribute it and/or
|
|
** modify it under the terms of the GNU General Public License
|
|
** as published by the Free Software Foundation; either version 2
|
|
** of the License, or (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program; if not, write to the Free Software
|
|
** Foundation, Inc., 51 Franklin Street,
|
|
** Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "fingerprint.h"
|
|
#include "vfs7xxx.h"
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/list.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/spi/spi.h>
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/ioctl.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/device.h>
|
|
#include <linux/err.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/io.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/i2c/twl.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/irq.h>
|
|
|
|
#include <asm-generic/siginfo.h>
|
|
#include <linux/rcupdate.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/jiffies.h>
|
|
|
|
#ifdef CONFIG_OF
|
|
#include <linux/of_gpio.h>
|
|
#endif
|
|
#include <linux/pinctrl/consumer.h>
|
|
#include "../pinctrl/core.h"
|
|
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
|
#include <linux/wakelock.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/spi/spidev.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of_dma.h>
|
|
#include <linux/amba/bus.h>
|
|
#include <linux/amba/pl330.h>
|
|
#include <linux/cpufreq.h>
|
|
#endif
|
|
|
|
|
|
#define VALIDITY_PART_NAME "validity_fingerprint"
|
|
static LIST_HEAD(device_list);
|
|
static DEFINE_MUTEX(device_list_mutex);
|
|
static struct class *vfsspi_device_class;
|
|
static int gpio_irq;
|
|
/* for irq enable, disable count */
|
|
static int cnt_irq=0;
|
|
|
|
#ifdef CONFIG_OF
|
|
static struct of_device_id vfsspi_match_table[] = {
|
|
{ .compatible = "vfsspi,vfs7xxx",},
|
|
{ },
|
|
};
|
|
#else
|
|
#define vfsspi_match_table NULL
|
|
#endif
|
|
|
|
/*
|
|
* vfsspi_devData - The spi driver private structure
|
|
* @devt:Device ID
|
|
* @vfs_spi_lock:The lock for the spi device
|
|
* @spi:The spi device
|
|
* @device_entry:Device entry list
|
|
* @buffer_mutex:The lock for the transfer buffer
|
|
* @is_opened:Indicates that driver is opened
|
|
* @buffer:buffer for transmitting data
|
|
* @null_buffer:buffer for transmitting zeros
|
|
* @stream_buffer:buffer for transmitting data stream
|
|
* @stream_buffer_size:streaming buffer size
|
|
* @drdy_pin:DRDY GPIO pin number
|
|
* @sleep_pin:Sleep GPIO pin number
|
|
* @user_pid:User process ID, to which the kernel signal
|
|
* indicating DRDY event is to be sent
|
|
* @signal_id:Signal ID which kernel uses to indicating
|
|
* user mode driver that DRDY is asserted
|
|
* @current_spi_speed:Current baud rate of SPI master clock
|
|
*/
|
|
struct vfsspi_device_data {
|
|
dev_t devt;
|
|
struct cdev cdev;
|
|
spinlock_t vfs_spi_lock;
|
|
struct spi_device *spi;
|
|
struct list_head device_entry;
|
|
struct mutex buffer_mutex;
|
|
unsigned int is_opened;
|
|
unsigned char *buffer;
|
|
unsigned char *null_buffer;
|
|
unsigned char *stream_buffer;
|
|
size_t stream_buffer_size;
|
|
unsigned int drdy_pin;
|
|
unsigned int sleep_pin;
|
|
struct task_struct *t;
|
|
int user_pid;
|
|
int signal_id;
|
|
unsigned int current_spi_speed;
|
|
atomic_t irq_enabled;
|
|
struct mutex kernel_lock;
|
|
bool ldo_onoff;
|
|
spinlock_t irq_lock;
|
|
unsigned short drdy_irq_flag;
|
|
unsigned int ldocontrol;
|
|
unsigned int min_cpufreq_limit;
|
|
unsigned int ocp_en;
|
|
unsigned int ldo_pin; /* Ldo 3.3V GPIO pin number */
|
|
unsigned int ldo_pin2; /* Ldo 1.8V GPIO pin number */
|
|
#ifdef CONFIG_SENSORS_FINGERPRINT_DUALIZATION
|
|
unsigned int vendor_pin; /* For checking vendor */
|
|
#endif
|
|
struct work_struct work_debug;
|
|
struct workqueue_struct *wq_dbg;
|
|
struct timer_list dbg_timer;
|
|
struct pinctrl *p;
|
|
struct pinctrl_state *pins_sleep;
|
|
struct pinctrl_state *pins_idle;
|
|
bool tz_mode;
|
|
#ifdef CONFIG_SENSORS_FINGERPRINT_SYSFS
|
|
struct device *fp_device;
|
|
#endif
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
|
unsigned int mosipin;
|
|
unsigned int misopin;
|
|
unsigned int cspin;
|
|
unsigned int clkpin;
|
|
bool isGpio_cfgDone;
|
|
bool enabled_clk;
|
|
#ifdef FEATURE_SPI_WAKELOCK
|
|
struct wake_lock fp_spi_lock;
|
|
#endif
|
|
#endif
|
|
int sensortype;
|
|
unsigned int orient;
|
|
};
|
|
|
|
#ifdef CONFIG_SENSORS_FINGERPRINT_DUALIZATION
|
|
int FP_CHECK = 0; /* extern variable init */
|
|
#endif
|
|
|
|
/* using for awake the samsung FP daemon */
|
|
extern bool fp_lockscreen_mode;
|
|
#ifdef CONFIG_SENSORS_FP_LOCKSCREEN_MODE
|
|
/* input/Keyboard/gpio_keys.c */
|
|
extern bool wakeup_by_key(void);
|
|
/* export variable for signaling */
|
|
EXPORT_SYMBOL(fp_lockscreen_mode);
|
|
#endif
|
|
|
|
bool fp_lockscreen_mode = false;
|
|
|
|
#define VENDOR "SYNAPTICS"
|
|
#define CHIP_ID "VIPER"
|
|
|
|
static struct vfsspi_device_data *g_data;
|
|
#ifndef ENABLE_SENSORS_FPRINT_SECURE
|
|
static int vfsspi_type_check(struct vfsspi_device_data *vfsspi_device);
|
|
#endif
|
|
|
|
#ifdef CONFIG_SENSORS_FINGERPRINT_SYSFS
|
|
extern int fingerprint_register(struct device *dev, void *drvdata,
|
|
struct device_attribute *attributes[], char *name);
|
|
extern void fingerprint_unregister(struct device *dev,
|
|
struct device_attribute *attributes[]);
|
|
#endif
|
|
|
|
static int vfsspi_send_drdy_signal(struct vfsspi_device_data *vfsspi_device)
|
|
{
|
|
int ret = 0;
|
|
|
|
pr_debug("%s\n", __func__);
|
|
|
|
if (vfsspi_device->t) {
|
|
/* notify DRDY signal to user process */
|
|
ret = send_sig_info(vfsspi_device->signal_id,
|
|
(struct siginfo *)1, vfsspi_device->t);
|
|
if (ret < 0)
|
|
pr_err("%s Error sending signal\n", __func__);
|
|
|
|
} else
|
|
pr_err("%s task_struct is not received yet\n", __func__);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Return no. of bytes written to device. Negative number for errors */
|
|
static inline ssize_t vfsspi_writeSync(struct vfsspi_device_data *vfsspi_device,
|
|
size_t len)
|
|
{
|
|
int status = 0;
|
|
struct spi_message m;
|
|
struct spi_transfer t;
|
|
|
|
pr_debug("%s\n", __func__);
|
|
|
|
spi_message_init(&m);
|
|
memset(&t, 0, sizeof(t));
|
|
|
|
t.rx_buf = vfsspi_device->null_buffer;
|
|
t.tx_buf = vfsspi_device->buffer;
|
|
t.len = len;
|
|
t.speed_hz = vfsspi_device->current_spi_speed;
|
|
|
|
spi_message_add_tail(&t, &m);
|
|
|
|
status = spi_sync(vfsspi_device->spi, &m);
|
|
|
|
if (status == 0)
|
|
status = m.actual_length;
|
|
pr_debug("%s vfsspi_writeSync,length=%d\n", __func__, m.actual_length);
|
|
return status;
|
|
}
|
|
|
|
/* Return no. of bytes read > 0. negative integer incase of error. */
|
|
static inline ssize_t vfsspi_readSync(struct vfsspi_device_data *vfsspi_device,
|
|
size_t len)
|
|
{
|
|
int status = 0;
|
|
struct spi_message m;
|
|
struct spi_transfer t;
|
|
|
|
pr_debug("%s\n", __func__);
|
|
|
|
spi_message_init(&m);
|
|
memset(&t, 0x0, sizeof(t));
|
|
|
|
memset(vfsspi_device->null_buffer, 0x0, len);
|
|
t.tx_buf = vfsspi_device->null_buffer;
|
|
t.rx_buf = vfsspi_device->buffer;
|
|
t.len = len;
|
|
t.speed_hz = vfsspi_device->current_spi_speed;
|
|
|
|
spi_message_add_tail(&t, &m);
|
|
|
|
status = spi_sync(vfsspi_device->spi, &m);
|
|
|
|
if (status == 0)
|
|
status = len;
|
|
|
|
pr_debug("%s vfsspi_readSync,length=%d\n", __func__, (int)len);
|
|
|
|
return status;
|
|
}
|
|
|
|
static ssize_t vfsspi_write(struct file *filp, const char __user *buf,
|
|
size_t count, loff_t *fPos)
|
|
{
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
|
return 0;
|
|
#else
|
|
struct vfsspi_device_data *vfsspi_device = NULL;
|
|
ssize_t status = 0;
|
|
|
|
pr_debug("%s\n", __func__);
|
|
|
|
if (count > DEFAULT_BUFFER_SIZE || !count)
|
|
return -EMSGSIZE;
|
|
|
|
vfsspi_device = filp->private_data;
|
|
|
|
mutex_lock(&vfsspi_device->buffer_mutex);
|
|
|
|
if (vfsspi_device->buffer) {
|
|
unsigned long missing = 0;
|
|
|
|
missing = copy_from_user(vfsspi_device->buffer, buf, count);
|
|
|
|
if (missing == 0)
|
|
status = vfsspi_writeSync(vfsspi_device, count);
|
|
else
|
|
status = -EFAULT;
|
|
}
|
|
|
|
mutex_unlock(&vfsspi_device->buffer_mutex);
|
|
|
|
return status;
|
|
#endif
|
|
}
|
|
|
|
static ssize_t vfsspi_read(struct file *filp, char __user *buf,
|
|
size_t count, loff_t *fPos)
|
|
{
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
|
return 0;
|
|
#else
|
|
struct vfsspi_device_data *vfsspi_device = NULL;
|
|
ssize_t status = 0;
|
|
|
|
pr_debug("%s\n", __func__);
|
|
|
|
if (count > DEFAULT_BUFFER_SIZE || !count)
|
|
return -EMSGSIZE;
|
|
if (buf == NULL)
|
|
return -EFAULT;
|
|
|
|
|
|
vfsspi_device = filp->private_data;
|
|
|
|
mutex_lock(&vfsspi_device->buffer_mutex);
|
|
|
|
status = vfsspi_readSync(vfsspi_device, count);
|
|
|
|
|
|
if (status > 0) {
|
|
unsigned long missing = 0;
|
|
/* data read. Copy to user buffer.*/
|
|
missing = copy_to_user(buf, vfsspi_device->buffer, status);
|
|
|
|
if (missing == status) {
|
|
pr_err("%s copy_to_user failed\n", __func__);
|
|
/* Nothing was copied to user space buffer. */
|
|
status = -EFAULT;
|
|
} else {
|
|
status = status - missing;
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&vfsspi_device->buffer_mutex);
|
|
|
|
return status;
|
|
#endif
|
|
}
|
|
|
|
#ifndef ENABLE_SENSORS_FPRINT_SECURE
|
|
static int vfsspi_xfer(struct vfsspi_device_data *vfsspi_device,
|
|
struct vfsspi_ioctl_transfer *tr)
|
|
{
|
|
int status = 0;
|
|
struct spi_message m;
|
|
struct spi_transfer t;
|
|
|
|
pr_debug("%s\n", __func__);
|
|
|
|
if (vfsspi_device == NULL || tr == NULL)
|
|
return -EFAULT;
|
|
|
|
if (tr->len > DEFAULT_BUFFER_SIZE || !tr->len)
|
|
return -EMSGSIZE;
|
|
|
|
if (tr->tx_buffer != NULL) {
|
|
|
|
if (copy_from_user(vfsspi_device->null_buffer,
|
|
tr->tx_buffer, tr->len) != 0)
|
|
return -EFAULT;
|
|
}
|
|
|
|
spi_message_init(&m);
|
|
memset(&t, 0, sizeof(t));
|
|
|
|
t.tx_buf = vfsspi_device->null_buffer;
|
|
t.rx_buf = vfsspi_device->buffer;
|
|
t.len = tr->len;
|
|
t.speed_hz = vfsspi_device->current_spi_speed;
|
|
|
|
spi_message_add_tail(&t, &m);
|
|
|
|
status = spi_sync(vfsspi_device->spi, &m);
|
|
|
|
if (status == 0) {
|
|
if (tr->rx_buffer != NULL) {
|
|
unsigned long missing = 0;
|
|
|
|
missing = copy_to_user(tr->rx_buffer,
|
|
vfsspi_device->buffer, tr->len);
|
|
|
|
if (missing != 0)
|
|
tr->len = tr->len - missing;
|
|
}
|
|
}
|
|
pr_debug("%s vfsspi_xfer,length=%d\n", __func__, tr->len);
|
|
return status;
|
|
|
|
} /* vfsspi_xfer */
|
|
#endif
|
|
|
|
#ifndef ENABLE_SENSORS_FPRINT_SECURE
|
|
static int vfsspi_rw_spi_message(struct vfsspi_device_data *vfsspi_device,
|
|
unsigned long arg)
|
|
{
|
|
struct vfsspi_ioctl_transfer *dup = NULL;
|
|
dup = kmalloc(sizeof(struct vfsspi_ioctl_transfer), GFP_KERNEL);
|
|
if (dup == NULL)
|
|
return -ENOMEM;
|
|
|
|
if (copy_from_user(dup, (void *)arg,
|
|
sizeof(struct vfsspi_ioctl_transfer)) != 0) {
|
|
kfree(dup);
|
|
return -EFAULT;
|
|
} else {
|
|
int err = vfsspi_xfer(vfsspi_device, dup);
|
|
if (err != 0) {
|
|
kfree(dup);
|
|
return err;
|
|
}
|
|
}
|
|
if (copy_to_user((void *)arg, dup,
|
|
sizeof(struct vfsspi_ioctl_transfer)) != 0)
|
|
return -EFAULT;
|
|
kfree(dup);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
void vfsspi_pin_control(struct vfsspi_device_data *vfsspi_device, bool pin_set)
|
|
{
|
|
int status = 0;
|
|
vfsspi_device->p->state = NULL;
|
|
if (pin_set) {
|
|
if (!IS_ERR(vfsspi_device->pins_idle)) {
|
|
status = pinctrl_select_state(vfsspi_device->p,
|
|
vfsspi_device->pins_idle);
|
|
if (status)
|
|
pr_err("%s: can't set pin default state\n",
|
|
__func__);
|
|
pr_debug("%s idle\n", __func__);
|
|
}
|
|
} else {
|
|
if (!IS_ERR(vfsspi_device->pins_sleep)) {
|
|
status = pinctrl_select_state(vfsspi_device->p,
|
|
vfsspi_device->pins_sleep);
|
|
if (status)
|
|
pr_err("%s: can't set pin sleep state\n",
|
|
__func__);
|
|
pr_debug("%s sleep\n", __func__);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int vfsspi_set_clk(struct vfsspi_device_data *vfsspi_device,
|
|
unsigned long arg)
|
|
{
|
|
int ret_val = 0;
|
|
unsigned short clock = 0;
|
|
struct spi_device *spidev = NULL;
|
|
|
|
if (copy_from_user(&clock, (void *)arg,
|
|
sizeof(unsigned short)) != 0)
|
|
return -EFAULT;
|
|
|
|
spin_lock_irq(&vfsspi_device->vfs_spi_lock);
|
|
|
|
spidev = spi_dev_get(vfsspi_device->spi);
|
|
|
|
spin_unlock_irq(&vfsspi_device->vfs_spi_lock);
|
|
if (spidev != NULL) {
|
|
switch (clock) {
|
|
case 0: /* Running baud rate. */
|
|
pr_debug("%s Running baud rate.\n", __func__);
|
|
spidev->max_speed_hz = MAX_BAUD_RATE;
|
|
vfsspi_device->current_spi_speed = MAX_BAUD_RATE;
|
|
break;
|
|
case 0xFFFF: /* Slow baud rate */
|
|
pr_debug("%s slow baud rate.\n", __func__);
|
|
spidev->max_speed_hz = SLOW_BAUD_RATE;
|
|
vfsspi_device->current_spi_speed = SLOW_BAUD_RATE;
|
|
break;
|
|
default:
|
|
pr_debug("%s baud rate is %d.\n", __func__, clock);
|
|
vfsspi_device->current_spi_speed =
|
|
clock * BAUD_RATE_COEF;
|
|
if (vfsspi_device->current_spi_speed > MAX_BAUD_RATE)
|
|
vfsspi_device->current_spi_speed =
|
|
MAX_BAUD_RATE;
|
|
spidev->max_speed_hz = vfsspi_device->current_spi_speed;
|
|
break;
|
|
}
|
|
|
|
pr_info("%s, clk speed: %d\n", __func__, vfsspi_device->current_spi_speed);
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
|
if (!vfsspi_device->enabled_clk) {
|
|
pr_info("%s ENABLE_SPI_CLOCK\n", __func__);
|
|
ret_val = fp_spi_clock_enable(spidev);
|
|
if (ret_val < 0)
|
|
pr_err("%s: Unable to enable spi clk\n",
|
|
__func__);
|
|
else {
|
|
ret_val = fp_spi_clock_set_rate(spidev);
|
|
if (ret_val < 0)
|
|
pr_err("%s: Unable to set spi clk rate\n",
|
|
__func__);
|
|
}
|
|
usleep_range(950, 1000);
|
|
#ifdef FEATURE_SPI_WAKELOCK
|
|
wake_lock(&vfsspi_device->fp_spi_lock);
|
|
#endif
|
|
vfsspi_device->enabled_clk = true;
|
|
}
|
|
#endif
|
|
spi_dev_put(spidev);
|
|
}
|
|
return ret_val;
|
|
}
|
|
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
|
static int vfsspi_ioctl_disable_spi_clock(
|
|
struct vfsspi_device_data *vfsspi_device)
|
|
{
|
|
struct spi_device *spidev = NULL;
|
|
int ret_val = 0;
|
|
|
|
if (vfsspi_device->enabled_clk) {
|
|
pr_info("%s DISABLE_SPI_CLOCK\n", __func__);
|
|
spin_lock_irq(&vfsspi_device->vfs_spi_lock);
|
|
spidev = spi_dev_get(vfsspi_device->spi);
|
|
spin_unlock_irq(&vfsspi_device->vfs_spi_lock);
|
|
|
|
ret_val = fp_spi_clock_disable(spidev);
|
|
if (ret_val < 0)
|
|
pr_err("%s: couldn't disable spi clks\n", __func__);
|
|
|
|
spi_dev_put(spidev);
|
|
usleep_range(950, 1000);
|
|
#ifdef FEATURE_SPI_WAKELOCK
|
|
wake_unlock(&vfsspi_device->fp_spi_lock);
|
|
#endif
|
|
vfsspi_device->enabled_clk = false;
|
|
}
|
|
|
|
return ret_val;
|
|
}
|
|
#endif
|
|
|
|
static int vfsspi_register_drdy_signal(struct vfsspi_device_data *vfsspi_device,
|
|
unsigned long arg)
|
|
{
|
|
struct vfsspi_ioctl_register_signal usr_signal;
|
|
if (copy_from_user(&usr_signal, (void *)arg, sizeof(usr_signal)) != 0) {
|
|
pr_err("%s Failed copy from user.\n", __func__);
|
|
return -EFAULT;
|
|
} else {
|
|
vfsspi_device->user_pid = usr_signal.user_pid;
|
|
vfsspi_device->signal_id = usr_signal.signal_id;
|
|
rcu_read_lock();
|
|
/* find the task_struct associated with userpid */
|
|
vfsspi_device->t = pid_task(find_pid_ns(vfsspi_device->user_pid, &init_pid_ns),
|
|
PIDTYPE_PID);
|
|
if (vfsspi_device->t == NULL) {
|
|
pr_debug("%s No such pid\n", __func__);
|
|
rcu_read_unlock();
|
|
return -ENODEV;
|
|
}
|
|
rcu_read_unlock();
|
|
pr_info("%s Searching task with PID=%08x, t = %p\n",
|
|
__func__, vfsspi_device->user_pid, vfsspi_device->t);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int vfsspi_enableIrq(struct vfsspi_device_data *vfsspi_device)
|
|
{
|
|
pr_info("%s\n", __func__);
|
|
spin_lock_irq(&vfsspi_device->irq_lock);
|
|
if (atomic_read(&vfsspi_device->irq_enabled)
|
|
== DRDY_IRQ_ENABLE) {
|
|
spin_unlock_irq(&vfsspi_device->irq_lock);
|
|
pr_err("%s DRDY irq already enabled\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
vfsspi_pin_control(vfsspi_device, true);
|
|
enable_irq(gpio_irq);
|
|
atomic_set(&vfsspi_device->irq_enabled, DRDY_IRQ_ENABLE);
|
|
cnt_irq++;
|
|
spin_unlock_irq(&vfsspi_device->irq_lock);
|
|
return 0;
|
|
}
|
|
|
|
static int vfsspi_disableIrq(struct vfsspi_device_data *vfsspi_device)
|
|
{
|
|
pr_info("%s\n", __func__);
|
|
spin_lock_irq(&vfsspi_device->irq_lock);
|
|
if (atomic_read(&vfsspi_device->irq_enabled)
|
|
== DRDY_IRQ_DISABLE) {
|
|
spin_unlock_irq(&vfsspi_device->irq_lock);
|
|
pr_err("%s DRDY irq already disabled\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
disable_irq_nosync(gpio_irq);
|
|
atomic_set(&vfsspi_device->irq_enabled, DRDY_IRQ_DISABLE);
|
|
vfsspi_pin_control(vfsspi_device, false);
|
|
cnt_irq--;
|
|
spin_unlock_irq(&vfsspi_device->irq_lock);
|
|
return 0;
|
|
}
|
|
|
|
static irqreturn_t vfsspi_irq(int irq, void *context)
|
|
{
|
|
struct vfsspi_device_data *vfsspi_device = context;
|
|
|
|
/* Linux kernel is designed so that when you disable
|
|
an edge-triggered interrupt, and the edge happens while
|
|
the interrupt is disabled, the system will re-play the
|
|
interrupt at enable time.
|
|
Therefore, we are checking DRDY GPIO pin state to make sure
|
|
if the interrupt handler has been called actually by DRDY
|
|
interrupt and it's not a previous interrupt re-play */
|
|
if (gpio_get_value(vfsspi_device->drdy_pin) == DRDY_ACTIVE_STATUS) {
|
|
spin_lock(&vfsspi_device->irq_lock);
|
|
if (atomic_read(&vfsspi_device->irq_enabled) == DRDY_IRQ_ENABLE) {
|
|
disable_irq_nosync(gpio_irq);
|
|
atomic_set(&vfsspi_device->irq_enabled, DRDY_IRQ_DISABLE);
|
|
vfsspi_pin_control(vfsspi_device, false);
|
|
cnt_irq--;
|
|
spin_unlock(&vfsspi_device->irq_lock);
|
|
vfsspi_send_drdy_signal(vfsspi_device);
|
|
pr_info("%s disableIrq\n", __func__);
|
|
}
|
|
else {
|
|
spin_unlock(&vfsspi_device->irq_lock);
|
|
pr_info("%s irq already diabled\n", __func__);
|
|
}
|
|
}
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int vfsspi_set_drdy_int(struct vfsspi_device_data *vfsspi_device,
|
|
unsigned long arg)
|
|
{
|
|
unsigned short drdy_enable_flag;
|
|
if (copy_from_user(&drdy_enable_flag, (void *)arg,
|
|
sizeof(drdy_enable_flag)) != 0) {
|
|
pr_err("%s Failed copy from user.\n", __func__);
|
|
return -EFAULT;
|
|
}
|
|
if (drdy_enable_flag == 0)
|
|
vfsspi_disableIrq(vfsspi_device);
|
|
else {
|
|
vfsspi_enableIrq(vfsspi_device);
|
|
/* Workaround the issue where the system
|
|
misses DRDY notification to host when
|
|
DRDY pin was asserted before enabling
|
|
device.*/
|
|
if (gpio_get_value(vfsspi_device->drdy_pin) ==
|
|
DRDY_ACTIVE_STATUS) {
|
|
pr_info("%s drdy pin is already active atatus\n", __func__);
|
|
vfsspi_send_drdy_signal(vfsspi_device);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void vfsspi_regulator_onoff(struct vfsspi_device_data *vfsspi_device,
|
|
bool onoff)
|
|
{
|
|
if (vfsspi_device->ldo_pin) {
|
|
if (vfsspi_device->ldocontrol) {
|
|
if (onoff) {
|
|
if (vfsspi_device->ocp_en) {
|
|
gpio_set_value(vfsspi_device->ocp_en, 1);
|
|
usleep_range(2950, 3000);
|
|
}
|
|
if (vfsspi_device->ldo_pin2)
|
|
gpio_set_value(vfsspi_device->ldo_pin2, 1);
|
|
|
|
gpio_set_value(vfsspi_device->ldo_pin, 1);
|
|
} else {
|
|
gpio_set_value(vfsspi_device->ldo_pin, 0);
|
|
|
|
if (vfsspi_device->ldo_pin2)
|
|
gpio_set_value(vfsspi_device->ldo_pin2, 0);
|
|
|
|
if (vfsspi_device->ocp_en)
|
|
gpio_set_value(vfsspi_device->ocp_en, 0);
|
|
}
|
|
vfsspi_device->ldo_onoff = onoff;
|
|
pr_info("%s: %s\n",
|
|
__func__, onoff ? "on" : "off");
|
|
} else
|
|
pr_info("%s: can't control in this revion\n", __func__);
|
|
}
|
|
}
|
|
|
|
static void vfsspi_hardReset(struct vfsspi_device_data *vfsspi_device)
|
|
{
|
|
pr_info("%s\n", __func__);
|
|
|
|
if (vfsspi_device != NULL) {
|
|
gpio_set_value(vfsspi_device->sleep_pin, 0);
|
|
mdelay(1);
|
|
gpio_set_value(vfsspi_device->sleep_pin, 1);
|
|
mdelay(10);
|
|
}
|
|
}
|
|
|
|
static void vfsspi_suspend(struct vfsspi_device_data *vfsspi_device)
|
|
{
|
|
pr_info("%s\n", __func__);
|
|
|
|
if (vfsspi_device != NULL) {
|
|
gpio_set_value(vfsspi_device->sleep_pin, 0);
|
|
}
|
|
}
|
|
|
|
static void vfsspi_ioctl_power_on(struct vfsspi_device_data *vfsspi_device)
|
|
{
|
|
if (vfsspi_device->ldocontrol && !vfsspi_device->ldo_onoff)
|
|
vfsspi_regulator_onoff(vfsspi_device, true);
|
|
else {
|
|
if (vfsspi_device->ldocontrol)
|
|
pr_info("%s already on\n", __func__);
|
|
else
|
|
pr_info("%s can't turn on ldo in this rev\n", __func__);
|
|
}
|
|
}
|
|
|
|
static void vfsspi_ioctl_power_off(struct vfsspi_device_data *vfsspi_device)
|
|
{
|
|
if (vfsspi_device->ldocontrol && vfsspi_device->ldo_onoff) {
|
|
vfsspi_regulator_onoff(vfsspi_device, false);
|
|
/* prevent sleep pin floating */
|
|
if (gpio_get_value(vfsspi_device->sleep_pin))
|
|
gpio_set_value(vfsspi_device->sleep_pin, 0);
|
|
} else {
|
|
if (vfsspi_device->ldocontrol)
|
|
pr_info("%s already off\n", __func__);
|
|
else
|
|
pr_info("%s can't turn off ldo in this rev\n", __func__);
|
|
}
|
|
}
|
|
|
|
static long vfsspi_ioctl(struct file *filp, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
int ret_val = 0;
|
|
struct vfsspi_device_data *vfsspi_device = NULL;
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
|
unsigned int onoff = 0;
|
|
unsigned int type_check = -1;
|
|
unsigned int lockscreen_mode = 0;
|
|
#endif
|
|
pr_debug("%s\n", __func__);
|
|
|
|
if (_IOC_TYPE(cmd) != VFSSPI_IOCTL_MAGIC) {
|
|
pr_err("%s invalid magic. cmd=0x%X Received=0x%X Expected=0x%X\n",
|
|
__func__, cmd, _IOC_TYPE(cmd), VFSSPI_IOCTL_MAGIC);
|
|
return -ENOTTY;
|
|
}
|
|
|
|
vfsspi_device = filp->private_data;
|
|
mutex_lock(&vfsspi_device->buffer_mutex);
|
|
switch (cmd) {
|
|
case VFSSPI_IOCTL_DEVICE_RESET:
|
|
pr_debug("%s VFSSPI_IOCTL_DEVICE_RESET\n", __func__);
|
|
vfsspi_hardReset(vfsspi_device);
|
|
break;
|
|
case VFSSPI_IOCTL_DEVICE_SUSPEND:
|
|
pr_debug("%s VFSSPI_IOCTL_DEVICE_SUSPEND\n", __func__);
|
|
vfsspi_suspend(vfsspi_device);
|
|
break;
|
|
#ifndef ENABLE_SENSORS_FPRINT_SECURE
|
|
case VFSSPI_IOCTL_RW_SPI_MESSAGE:
|
|
pr_debug("%s VFSSPI_IOCTL_RW_SPI_MESSAGE\n", __func__);
|
|
ret_val = vfsspi_rw_spi_message(vfsspi_device, arg);
|
|
if (ret_val) {
|
|
pr_err("%s : VFSSPI_IOCTL_RW_SPI_MESSAGE error %d\n",
|
|
__func__, ret_val);
|
|
}
|
|
break;
|
|
#endif
|
|
case VFSSPI_IOCTL_SET_CLK:
|
|
pr_info("%s VFSSPI_IOCTL_SET_CLK\n", __func__);
|
|
ret_val = vfsspi_set_clk(vfsspi_device, arg);
|
|
break;
|
|
case VFSSPI_IOCTL_REGISTER_DRDY_SIGNAL:
|
|
pr_info("%s VFSSPI_IOCTL_REGISTER_DRDY_SIGNAL\n", __func__);
|
|
ret_val = vfsspi_register_drdy_signal(vfsspi_device, arg);
|
|
break;
|
|
case VFSSPI_IOCTL_SET_DRDY_INT:
|
|
pr_info("%s VFSSPI_IOCTL_SET_DRDY_INT\n", __func__);
|
|
ret_val = vfsspi_set_drdy_int(vfsspi_device, arg);
|
|
break;
|
|
case VFSSPI_IOCTL_POWER_ON:
|
|
pr_info("%s VFSSPI_IOCTL_POWER_ON\n", __func__);
|
|
vfsspi_ioctl_power_on(vfsspi_device);
|
|
break;
|
|
case VFSSPI_IOCTL_POWER_OFF:
|
|
pr_info("%s VFSSPI_IOCTL_POWER_OFF\n", __func__);
|
|
vfsspi_ioctl_power_off(vfsspi_device);
|
|
break;
|
|
case VFSSPI_IOCTL_POWER_CONTROL:
|
|
break;
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
|
case VFSSPI_IOCTL_DISABLE_SPI_CLOCK:
|
|
ret_val = vfsspi_ioctl_disable_spi_clock(vfsspi_device);
|
|
break;
|
|
|
|
case VFSSPI_IOCTL_SET_SPI_CONFIGURATION:
|
|
pr_info("%s VFSSPI_IOCTL_SET_SPI_CONFIGURATION\n", __func__);
|
|
break;
|
|
case VFSSPI_IOCTL_RESET_SPI_CONFIGURATION:
|
|
pr_info("%s VFSSPI_IOCTL_RESET_SPI_CONFIGURATION\n", __func__);
|
|
break;
|
|
case VFSSPI_IOCTL_CPU_SPEEDUP:
|
|
if (copy_from_user(&onoff, (void *)arg,
|
|
sizeof(unsigned int)) != 0) {
|
|
pr_err("%s Failed copy from user.(CPU_SPEEDUP)\n", __func__);
|
|
mutex_unlock(&vfsspi_device->buffer_mutex);
|
|
return -EFAULT;
|
|
}
|
|
if (onoff == 1) {
|
|
u8 retry_cnt = 0;
|
|
pr_info("%s VFSSPI_IOCTL_CPU_SPPEEDUP ON:%d, retry: %d\n",
|
|
__func__, onoff, retry_cnt);
|
|
if(vfsspi_device->min_cpufreq_limit){
|
|
do {
|
|
ret_val = set_freq_limit(DVFS_FINGER_ID, vfsspi_device->min_cpufreq_limit);
|
|
retry_cnt++;
|
|
if (ret_val) {
|
|
pr_err("%s: clock speed up start failed. (%d) retry: %d\n",
|
|
__func__, ret_val, retry_cnt);
|
|
if (retry_cnt < 7)
|
|
usleep_range(500, 510);
|
|
}
|
|
} while (ret_val && retry_cnt < 7);
|
|
}
|
|
|
|
} else if (onoff == 0) {
|
|
pr_info("%s VFSSPI_IOCTL_CPU_SPEEDUP OFF\n", __func__);
|
|
if(vfsspi_device->min_cpufreq_limit){
|
|
ret_val = set_freq_limit(DVFS_FINGER_ID, -1);
|
|
if (ret_val)
|
|
pr_err("%s: clock speed up stop failed. (%d)\n",
|
|
__func__, ret_val);
|
|
}
|
|
}
|
|
break;
|
|
case VFSSPI_IOCTL_SET_SENSOR_TYPE:
|
|
if (copy_from_user(&type_check, (void *)arg,
|
|
sizeof(unsigned int)) != 0) {
|
|
pr_err("%s Failed copy from user.(SET_SENSOR_TYPE)\n", __func__);
|
|
mutex_unlock(&vfsspi_device->buffer_mutex);
|
|
return -EFAULT;
|
|
}
|
|
if ((int)type_check >= SENSOR_UNKNOWN && (int)type_check < (SENSOR_STATUS_SIZE - 1)) {
|
|
vfsspi_device->sensortype = (int)type_check;
|
|
pr_info("%s VFSSPI_IOCTL_SET_SENSOR_TYPE :%s\n",
|
|
__func__, sensor_status[g_data->sensortype + 1]);
|
|
} else {
|
|
pr_err("%sVFSSPI_IOCTL_SET_SENSOR_TYPE : invalid value %d\n",
|
|
__func__, (int)type_check);
|
|
vfsspi_device->sensortype = SENSOR_UNKNOWN;
|
|
}
|
|
break;
|
|
case VFSSPI_IOCTL_SET_LOCKSCREEN:
|
|
if (copy_from_user(&lockscreen_mode,
|
|
(void *)arg,sizeof(unsigned int)) != 0) {
|
|
pr_err("%s Failed copy from user.(SET_LOCKSCREEN_MODE)\n", __func__);
|
|
mutex_unlock(&vfsspi_device->buffer_mutex);
|
|
return -EFAULT;
|
|
}
|
|
lockscreen_mode?(fp_lockscreen_mode=true):(fp_lockscreen_mode=false);
|
|
pr_info("%s VFSSPI_IOCTL_SET_LOCKSCREEN :%s \n",
|
|
__func__, fp_lockscreen_mode?"ON":"OFF");
|
|
break;
|
|
#endif
|
|
case VFSSPI_IOCTL_GET_SENSOR_ORIENT:
|
|
pr_info("%s: orient is %d(0: normal, 1: upsidedown)\n",
|
|
__func__, vfsspi_device->orient);
|
|
if (copy_to_user((void *)arg,
|
|
&(vfsspi_device->orient),
|
|
sizeof(vfsspi_device->orient))
|
|
!= 0) {
|
|
ret_val = -EFAULT;
|
|
pr_err("%s cp to user fail\n", __func__);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
pr_info("%s default error. %u\n", __func__, cmd);
|
|
ret_val = -EFAULT;
|
|
break;
|
|
}
|
|
mutex_unlock(&vfsspi_device->buffer_mutex);
|
|
return ret_val;
|
|
}
|
|
|
|
static int vfsspi_open(struct inode *inode, struct file *filp)
|
|
{
|
|
struct vfsspi_device_data *vfsspi_device = NULL;
|
|
int status = -ENXIO;
|
|
|
|
pr_info("%s\n", __func__);
|
|
|
|
mutex_lock(&device_list_mutex);
|
|
|
|
list_for_each_entry(vfsspi_device, &device_list, device_entry) {
|
|
if (vfsspi_device->devt == inode->i_rdev) {
|
|
status = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!vfsspi_device->ldo_onoff) {
|
|
vfsspi_regulator_onoff(vfsspi_device, true);
|
|
msleep(100);
|
|
}
|
|
|
|
if (status == 0) {
|
|
mutex_lock(&vfsspi_device->kernel_lock);
|
|
if (vfsspi_device->is_opened != 0) {
|
|
status = -EBUSY;
|
|
pr_err("%s vfsspi_open: is_opened != 0, -EBUSY\n",
|
|
__func__);
|
|
goto vfsspi_open_out;
|
|
}
|
|
vfsspi_device->user_pid = 0;
|
|
if (vfsspi_device->buffer != NULL) {
|
|
pr_err("%s vfsspi_open: buffer != NULL\n", __func__);
|
|
goto vfsspi_open_out;
|
|
}
|
|
vfsspi_device->null_buffer =
|
|
kmalloc(DEFAULT_BUFFER_SIZE, GFP_KERNEL);
|
|
if (vfsspi_device->null_buffer == NULL) {
|
|
status = -ENOMEM;
|
|
pr_err("%s vfsspi_open: null_buffer == NULL, -ENOMEM\n",
|
|
__func__);
|
|
goto vfsspi_open_out;
|
|
}
|
|
vfsspi_device->buffer =
|
|
kmalloc(DEFAULT_BUFFER_SIZE, GFP_KERNEL);
|
|
if (vfsspi_device->buffer == NULL) {
|
|
status = -ENOMEM;
|
|
kfree(vfsspi_device->null_buffer);
|
|
pr_err("%s vfsspi_open: buffer == NULL, -ENOMEM\n",
|
|
__func__);
|
|
goto vfsspi_open_out;
|
|
}
|
|
vfsspi_device->is_opened = 1;
|
|
filp->private_data = vfsspi_device;
|
|
nonseekable_open(inode, filp);
|
|
|
|
vfsspi_open_out:
|
|
mutex_unlock(&vfsspi_device->kernel_lock);
|
|
}
|
|
mutex_unlock(&device_list_mutex);
|
|
return status;
|
|
}
|
|
|
|
|
|
static int vfsspi_release(struct inode *inode, struct file *filp)
|
|
{
|
|
struct vfsspi_device_data *vfsspi_device = NULL;
|
|
int status = 0;
|
|
|
|
pr_info("%s\n", __func__);
|
|
|
|
mutex_lock(&device_list_mutex);
|
|
vfsspi_device = filp->private_data;
|
|
filp->private_data = NULL;
|
|
vfsspi_device->is_opened = 0;
|
|
if (vfsspi_device->buffer != NULL) {
|
|
kfree(vfsspi_device->buffer);
|
|
vfsspi_device->buffer = NULL;
|
|
}
|
|
|
|
if (vfsspi_device->null_buffer != NULL) {
|
|
kfree(vfsspi_device->null_buffer);
|
|
vfsspi_device->null_buffer = NULL;
|
|
}
|
|
|
|
if (vfsspi_device->ldo_onoff)
|
|
vfsspi_regulator_onoff(vfsspi_device, false);
|
|
|
|
mutex_unlock(&device_list_mutex);
|
|
return status;
|
|
}
|
|
|
|
/* file operations associated with device */
|
|
static const struct file_operations vfsspi_fops = {
|
|
.owner = THIS_MODULE,
|
|
.write = vfsspi_write,
|
|
.read = vfsspi_read,
|
|
.unlocked_ioctl = vfsspi_ioctl,
|
|
.open = vfsspi_open,
|
|
.release = vfsspi_release,
|
|
};
|
|
|
|
static int vfsspi_platformInit(struct vfsspi_device_data *vfsspi_device)
|
|
{
|
|
int status = 0;
|
|
pr_info("%s\n", __func__);
|
|
|
|
if (vfsspi_device->ocp_en) {
|
|
status = gpio_request(vfsspi_device->ocp_en, "vfsspi_ocp_en");
|
|
if (status < 0) {
|
|
pr_err("%s gpio_request vfsspi_ocp_en failed\n",
|
|
__func__);
|
|
goto vfsspi_platformInit_ocpen_failed;
|
|
}
|
|
gpio_direction_output(vfsspi_device->ocp_en, 0);
|
|
pr_info("%s ocp off\n", __func__);
|
|
}
|
|
|
|
if (vfsspi_device->ldo_pin) {
|
|
status = gpio_request(vfsspi_device->ldo_pin, "vfsspi_ldo_en");
|
|
if (status < 0) {
|
|
pr_err("%s gpio_request vfsspi_ldo_en failed\n",
|
|
__func__);
|
|
goto vfsspi_platformInit_ldo_failed;
|
|
}
|
|
gpio_direction_output(vfsspi_device->ldo_pin, 0);
|
|
}
|
|
if (vfsspi_device->ldo_pin2) {
|
|
status =
|
|
gpio_request(vfsspi_device->ldo_pin2, "vfsspi_ldo_en2");
|
|
if (status < 0) {
|
|
pr_err("%s gpio_request vfsspi_ldo_en2 failed\n",
|
|
__func__);
|
|
goto vfsspi_platformInit_ldo2_failed;
|
|
}
|
|
gpio_direction_output(vfsspi_device->ldo_pin2, 0);
|
|
}
|
|
|
|
if (gpio_request(vfsspi_device->drdy_pin, "vfsspi_drdy") < 0) {
|
|
status = -EBUSY;
|
|
goto vfsspi_platformInit_drdy_failed;
|
|
}
|
|
|
|
if (gpio_request(vfsspi_device->sleep_pin, "vfsspi_sleep")) {
|
|
status = -EBUSY;
|
|
goto vfsspi_platformInit_sleep_failed;
|
|
}
|
|
|
|
status = gpio_direction_output(vfsspi_device->sleep_pin, 0);
|
|
if (status < 0) {
|
|
pr_err("%s gpio_direction_output SLEEP failed\n", __func__);
|
|
status = -EBUSY;
|
|
goto vfsspi_platformInit_gpio_init_failed;
|
|
}
|
|
|
|
spin_lock_init(&vfsspi_device->irq_lock);
|
|
|
|
status = gpio_direction_input(vfsspi_device->drdy_pin);
|
|
if (status < 0) {
|
|
pr_err("%s gpio_direction_input DRDY failed\n", __func__);
|
|
status = -EBUSY;
|
|
goto vfsspi_platformInit_gpio_init_failed;
|
|
}
|
|
|
|
gpio_irq = gpio_to_irq(vfsspi_device->drdy_pin);
|
|
|
|
if (gpio_irq < 0) {
|
|
pr_err("%s gpio_to_irq failed\n", __func__);
|
|
status = -EBUSY;
|
|
goto vfsspi_platformInit_gpio_init_failed;
|
|
}
|
|
|
|
if (request_irq(gpio_irq, vfsspi_irq, IRQF_TRIGGER_RISING,
|
|
"vfsspi_irq", vfsspi_device) < 0) {
|
|
pr_err("%s request_irq failed\n", __func__);
|
|
status = -EBUSY;
|
|
goto vfsspi_platformInit_irq_failed;
|
|
}
|
|
disable_irq(gpio_irq);
|
|
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
|
#ifdef FEATURE_SPI_WAKELOCK
|
|
wake_lock_init(&vfsspi_device->fp_spi_lock,
|
|
WAKE_LOCK_SUSPEND, "vfsspi_wake_lock");
|
|
#endif
|
|
#endif
|
|
|
|
pr_info("%s : platformInit success!\n", __func__);
|
|
return status;
|
|
|
|
vfsspi_platformInit_irq_failed:
|
|
vfsspi_platformInit_gpio_init_failed:
|
|
gpio_free(vfsspi_device->sleep_pin);
|
|
vfsspi_platformInit_sleep_failed:
|
|
gpio_free(vfsspi_device->drdy_pin);
|
|
vfsspi_platformInit_drdy_failed:
|
|
if (vfsspi_device->ldo_pin2)
|
|
gpio_free(vfsspi_device->ldo_pin2);
|
|
vfsspi_platformInit_ldo2_failed:
|
|
gpio_free(vfsspi_device->ldo_pin);
|
|
vfsspi_platformInit_ldo_failed:
|
|
if (vfsspi_device->ocp_en)
|
|
gpio_free(vfsspi_device->ocp_en);
|
|
vfsspi_platformInit_ocpen_failed:
|
|
pr_info("%s : platformInit failed!\n", __func__);
|
|
return status;
|
|
}
|
|
|
|
static void vfsspi_platformUninit(struct vfsspi_device_data *vfsspi_device)
|
|
{
|
|
pr_info("%s\n", __func__);
|
|
|
|
if (vfsspi_device != NULL) {
|
|
free_irq(gpio_irq, vfsspi_device);
|
|
vfsspi_device->drdy_irq_flag = DRDY_IRQ_DISABLE;
|
|
if (vfsspi_device->ldo_pin)
|
|
gpio_free(vfsspi_device->ldo_pin);
|
|
if (vfsspi_device->ldo_pin2)
|
|
gpio_free(vfsspi_device->ldo_pin2);
|
|
gpio_free(vfsspi_device->sleep_pin);
|
|
gpio_free(vfsspi_device->drdy_pin);
|
|
#ifdef CONFIG_SENSORS_FINGERPRINT_DUALIZATION
|
|
if (vfsspi_device->vendor_pin)
|
|
gpio_free(vfsspi_device->vendor_pin);
|
|
#endif
|
|
if (vfsspi_device->ocp_en)
|
|
gpio_free(vfsspi_device->ocp_en);
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
|
#ifdef FEATURE_SPI_WAKELOCK
|
|
wake_lock_destroy(&vfsspi_device->fp_spi_lock);
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static int vfsspi_parse_dt(struct device *dev,
|
|
struct vfsspi_device_data *data)
|
|
{
|
|
struct device_node *np = dev->of_node;
|
|
int errorno = 0;
|
|
int gpio;
|
|
|
|
gpio = of_get_named_gpio(np, "vfsspi-sleepPin", 0);
|
|
if (gpio < 0) {
|
|
errorno = gpio;
|
|
goto dt_exit;
|
|
} else {
|
|
data->sleep_pin = gpio;
|
|
pr_info("%s: sleepPin=%d\n",
|
|
__func__, data->sleep_pin);
|
|
}
|
|
gpio = of_get_named_gpio(np, "vfsspi-drdyPin", 0);
|
|
if (gpio < 0) {
|
|
errorno = gpio;
|
|
goto dt_exit;
|
|
} else {
|
|
data->drdy_pin = gpio;
|
|
pr_info("%s: drdyPin=%d\n",
|
|
__func__, data->drdy_pin);
|
|
}
|
|
if (!of_find_property(np, "vfsspi-ocpen", NULL)) {
|
|
pr_info("%s: not set ocp_en in dts\n", __func__);
|
|
} else {
|
|
gpio = of_get_named_gpio(np, "vfsspi-ocpen", 0);
|
|
if (gpio < 0)
|
|
pr_err("%s: fail to get ocp_en\n", __func__);
|
|
else {
|
|
data->ocp_en = gpio;
|
|
pr_info("%s: ocp_en=%d\n",
|
|
__func__, data->ocp_en);
|
|
}
|
|
}
|
|
|
|
gpio = of_get_named_gpio(np, "vfsspi-ldoPin", 0);
|
|
if (gpio < 0) {
|
|
data->ldo_pin = 0;
|
|
pr_err("%s: fail to get ldo_pin\n", __func__);
|
|
} else {
|
|
data->ldo_pin = gpio;
|
|
pr_info("%s: ldo_pin=%d\n",
|
|
__func__, data->ldo_pin);
|
|
}
|
|
if (!of_find_property(np, "vfsspi-ldoPin2", NULL)) {
|
|
pr_info("%s: not set ldo2 in dts\n", __func__);
|
|
data->ldo_pin2 = 0;
|
|
} else {
|
|
gpio = of_get_named_gpio(np, "vfsspi-ldoPin2", 0);
|
|
if (gpio < 0) {
|
|
data->ldo_pin2 = 0;
|
|
pr_err("%s: fail to get ldo_pin2\n", __func__);
|
|
} else {
|
|
data->ldo_pin2 = gpio;
|
|
pr_info("%s: ldo_pin2=%d\n",
|
|
__func__, data->ldo_pin2);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_SENSORS_FINGERPRINT_DUALIZATION
|
|
if (!of_find_property(np, "vfsspi-vendorPin", NULL)) {
|
|
pr_err("%s: not set vendorPin in dts\n", __func__);
|
|
data->vendor_pin = 0;
|
|
} else {
|
|
gpio = of_get_named_gpio(np, "vfsspi-vendorPin", 0);
|
|
if (gpio < 0) {
|
|
data->vendor_pin = 0;
|
|
pr_err("%s: fail to get vendorPin\n", __func__);
|
|
} else {
|
|
data->vendor_pin = gpio;
|
|
pr_info("%s: vendorPin=%d\n",
|
|
__func__, data->vendor_pin);
|
|
}
|
|
}
|
|
#endif
|
|
if (of_property_read_u32(np, "vfsspi-ldocontrol",
|
|
&data->ldocontrol))
|
|
data->ldocontrol = 0;
|
|
if (of_property_read_u32(np, "vfsspi-min_cpufeq_limit",
|
|
&data->min_cpufreq_limit))
|
|
data->min_cpufreq_limit = 0;
|
|
|
|
pr_info("%s: ldocontrol=%d, min_cpufreq_limit=%d\n",
|
|
__func__, data->ldocontrol, data->min_cpufreq_limit);
|
|
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
|
if (of_property_read_u32(np, "vfsspi-mosipin", &data->mosipin))
|
|
data->mosipin = 0;
|
|
if (of_property_read_u32(np, "vfsspi-misopin", &data->misopin))
|
|
data->misopin = 0;
|
|
if (of_property_read_u32(np, "vfsspi-cspin", &data->cspin))
|
|
data->cspin = 0;
|
|
if (of_property_read_u32(np, "vfsspi-clkpin", &data->clkpin))
|
|
data->clkpin = 0;
|
|
|
|
data->tz_mode = true;
|
|
#endif
|
|
|
|
if (of_property_read_u32(np, "vfsspi-orient",
|
|
&data->orient))
|
|
data->orient = 0;
|
|
|
|
pr_info("%s: orient=%d\n",
|
|
__func__, data->orient);
|
|
|
|
data->p = pinctrl_get_select_default(dev);
|
|
if (IS_ERR(data->p)) {
|
|
errorno = -EINVAL;
|
|
pr_err("%s: failed pinctrl_get\n", __func__);
|
|
goto dt_exit;
|
|
}
|
|
|
|
data->pins_sleep = pinctrl_lookup_state(data->p, PINCTRL_STATE_SLEEP);
|
|
if(IS_ERR(data->pins_sleep)) {
|
|
errorno = -EINVAL;
|
|
pr_err("%s : could not get pins sleep_state (%li)\n",
|
|
__func__, PTR_ERR(data->pins_sleep));
|
|
goto fail_pinctrl_get;
|
|
}
|
|
|
|
data->pins_idle = pinctrl_lookup_state(data->p, PINCTRL_STATE_IDLE);
|
|
if(IS_ERR(data->pins_idle)) {
|
|
errorno = -EINVAL;
|
|
pr_err("%s : could not get pins idle_state (%li)\n",
|
|
__func__, PTR_ERR(data->pins_idle));
|
|
goto fail_pinctrl_get;
|
|
}
|
|
return 0;
|
|
fail_pinctrl_get:
|
|
pinctrl_put(data->p);
|
|
dt_exit:
|
|
return errorno;
|
|
}
|
|
|
|
#ifdef CONFIG_SENSORS_FINGERPRINT_SYSFS
|
|
static ssize_t vfsspi_type_check_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct vfsspi_device_data *data = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", data->sensortype);
|
|
}
|
|
|
|
static ssize_t vfsspi_vendor_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return sprintf(buf, "%s\n", VENDOR);
|
|
}
|
|
|
|
static ssize_t vfsspi_name_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return sprintf(buf, "%s\n", CHIP_ID);
|
|
}
|
|
|
|
static DEVICE_ATTR(type_check, S_IRUGO,
|
|
vfsspi_type_check_show, NULL);
|
|
static DEVICE_ATTR(vendor, S_IRUGO,
|
|
vfsspi_vendor_show, NULL);
|
|
static DEVICE_ATTR(name, S_IRUGO,
|
|
vfsspi_name_show, NULL);
|
|
|
|
static struct device_attribute *fp_attrs[] = {
|
|
&dev_attr_type_check,
|
|
&dev_attr_vendor,
|
|
&dev_attr_name,
|
|
NULL,
|
|
};
|
|
#endif
|
|
|
|
static void vfsspi_work_func_debug(struct work_struct *work)
|
|
{
|
|
u8 ldo_value = 0;
|
|
|
|
if (g_data->ldo_pin2 == 0) {
|
|
ldo_value = gpio_get_value(g_data->ldo_pin);
|
|
} else {
|
|
ldo_value = (gpio_get_value(g_data->ldo_pin2) << 1)
|
|
| gpio_get_value(g_data->ldo_pin);
|
|
}
|
|
|
|
pr_info("%s ldo: %d,"
|
|
" sleep: %d, irq: %d, tz: %d, type: %s, cnt_irq: %d\n",
|
|
__func__,
|
|
ldo_value, gpio_get_value(g_data->sleep_pin),
|
|
gpio_get_value(g_data->drdy_pin),
|
|
g_data->tz_mode,
|
|
sensor_status[g_data->sensortype + 1],
|
|
cnt_irq);
|
|
}
|
|
|
|
static void vfsspi_enable_debug_timer(void)
|
|
{
|
|
mod_timer(&g_data->dbg_timer,
|
|
round_jiffies_up(jiffies + FPSENSOR_DEBUG_TIMER_SEC));
|
|
}
|
|
|
|
static void vfsspi_disable_debug_timer(void)
|
|
{
|
|
del_timer_sync(&g_data->dbg_timer);
|
|
cancel_work_sync(&g_data->work_debug);
|
|
}
|
|
|
|
static void vfsspi_timer_func(unsigned long ptr)
|
|
{
|
|
queue_work(g_data->wq_dbg, &g_data->work_debug);
|
|
mod_timer(&g_data->dbg_timer,
|
|
round_jiffies_up(jiffies + FPSENSOR_DEBUG_TIMER_SEC));
|
|
}
|
|
#ifdef CONFIG_SENSORS_FINGERPRINT_DUALIZATION
|
|
static int vfsspi_vendor_check(struct vfsspi_device_data *vfsspi_device)
|
|
{
|
|
int status = 0;
|
|
pr_info("%s\n", __func__);
|
|
|
|
vfsspi_regulator_onoff(vfsspi_device, true); /* power on */
|
|
msleep(10);
|
|
|
|
status = gpio_request(vfsspi_device->vendor_pin, "vfsspi_vendor");
|
|
if (status < 0) {
|
|
pr_info("%s: gpio_request - vfsspi_vendor failed\n", __func__);
|
|
goto vfsspi_vendor_check_vendor_failed;
|
|
}
|
|
|
|
status = gpio_direction_input(vfsspi_device->vendor_pin);
|
|
if (status < 0) {
|
|
pr_err("%s gpio_direction_input VENDOR failed\n", __func__);
|
|
status = -EBUSY;
|
|
gpio_free(vfsspi_device->vendor_pin);
|
|
goto vfsspi_vendor_check_vendor_failed;
|
|
}
|
|
|
|
status = gpio_get_value(vfsspi_device->vendor_pin);
|
|
|
|
pr_info("%s is success, vendorPin (%d), value (%d)\n"
|
|
, __func__, vfsspi_device->vendor_pin, status);
|
|
|
|
vfsspi_vendor_check_vendor_failed:
|
|
vfsspi_regulator_onoff(vfsspi_device, false); /* power off */
|
|
return status;
|
|
}
|
|
#endif
|
|
#ifndef ENABLE_SENSORS_FPRINT_SECURE
|
|
static int vfsspi_type_check(struct vfsspi_device_data *vfsspi_device)
|
|
{
|
|
struct spi_device * spi = NULL;
|
|
char tx_buf[64] = {1};
|
|
char rx_buf[64] = {0};
|
|
int i = 0;
|
|
struct spi_transfer t;
|
|
struct spi_message m;
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE_
|
|
struct spi_device *spidev = NULL;
|
|
int ret_val = 0;
|
|
#endif
|
|
pr_info("%s\n", __func__);
|
|
vfsspi_regulator_onoff(vfsspi_device, true);
|
|
|
|
/* check sensor if it is viper */
|
|
vfsspi_hardReset(vfsspi_device);
|
|
msleep(20);
|
|
|
|
spi = vfsspi_device->spi;
|
|
tx_buf[0] = 1; /* EP0 Read */
|
|
tx_buf[1] = 0;
|
|
spi->bits_per_word = BITS_PER_WORD;
|
|
spi->mode = SPI_MODE_0;
|
|
memset(&t, 0, sizeof(t));
|
|
t.tx_buf = tx_buf;
|
|
t.rx_buf = rx_buf;
|
|
t.len = 6;
|
|
spi_setup(spi);
|
|
spi_message_init(&m);
|
|
spi_message_add_tail(&t, &m);
|
|
pr_info("%s ValiditySensor: spi_sync returned %d\n",
|
|
__func__, spi_sync(spi, &m));
|
|
|
|
if (((rx_buf[2] == 0x01) || (rx_buf[2] == 0x41))
|
|
&& (rx_buf[5] == 0x68)) {
|
|
vfsspi_device->sensortype = SENSOR_VIPER;
|
|
gpio_set_value(vfsspi_device->sleep_pin, 0);
|
|
pr_info("%s sensor type is VIPER\n", __func__);
|
|
goto type_check_exit;
|
|
} else {
|
|
pr_info("%s sensor type is not VIPER\n", __func__);
|
|
for (i = 0; i < 6; i++)
|
|
pr_info("%s, %0x\n", __func__, rx_buf[i]);
|
|
}
|
|
|
|
/* check sensor if it is raptor */
|
|
gpio_set_value(vfsspi_device->sleep_pin, 0);
|
|
|
|
msleep(20);
|
|
|
|
tx_buf[0] = 5;
|
|
tx_buf[1] = 0;
|
|
|
|
spi->bits_per_word = 16;
|
|
memset(&t, 0, sizeof(t));
|
|
t.tx_buf = tx_buf;
|
|
t.rx_buf = rx_buf;
|
|
t.len = 64;
|
|
spi_setup(spi);
|
|
spi_message_init(&m);
|
|
spi_message_add_tail(&t, &m);
|
|
pr_info("ValiditySensor: spi_sync returned %d\n",
|
|
spi_sync(spi, &m));
|
|
|
|
if (((rx_buf[0] == 0x98) || (rx_buf[0] == 0xBA))
|
|
&& ((rx_buf[1] == 0x98) || (rx_buf[1] == 0xBA))) {
|
|
vfsspi_device->sensortype = SENSOR_RAPTOR;
|
|
pr_info("%s sensor type is RAPTOR\n", __func__);
|
|
} else {
|
|
vfsspi_device->sensortype = SENSOR_FAILED;
|
|
pr_info("%s sensor type is FAILED\n", __func__);
|
|
}
|
|
|
|
spi->bits_per_word = BITS_PER_WORD;
|
|
spi->max_speed_hz = SLOW_BAUD_RATE;
|
|
spi->mode = SPI_MODE_0;
|
|
spi_setup(spi);
|
|
|
|
type_check_exit:
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE_
|
|
pr_info("%s ENABLE_SPI_CLOCK\n", __func__);
|
|
spin_lock_irq(&vfsspi_device->vfs_spi_lock);
|
|
spidev = spi_dev_get(vfsspi_device->spi);
|
|
spin_unlock_irq(&vfsspi_device->vfs_spi_lock);
|
|
|
|
ret_val = fp_spi_clock_enable(spidev);
|
|
if (ret_val < 0)
|
|
pr_err("%s: Unable to enable spi clk\n",
|
|
__func__);
|
|
else {
|
|
ret_val = fp_spi_clock_set_rate(spidev);
|
|
if (ret_val < 0)
|
|
pr_err("%s: Unable to set spi clk rate\n",
|
|
__func__);
|
|
}
|
|
usleep_range(950, 1000);
|
|
|
|
spi_dev_put(spidev);
|
|
|
|
pr_info("%s DISABLE_SPI_CLOCK\n", __func__);
|
|
spin_lock_irq(&vfsspi_device->vfs_spi_lock);
|
|
spidev = spi_dev_get(vfsspi_device->spi);
|
|
spin_unlock_irq(&vfsspi_device->vfs_spi_lock);
|
|
|
|
ret_val = fp_spi_clock_disable(spidev);
|
|
if (ret_val < 0)
|
|
pr_err("%s: couldn't disable spi clks\n", __func__);
|
|
|
|
spi_dev_put(spidev);
|
|
|
|
pr_info("%s SET_SPI_CONFIGURATION\n", __func__);
|
|
#endif /* ENABLE_SENSORS_FPRINT_SECURE */
|
|
vfsspi_regulator_onoff(vfsspi_device, false);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
|
static int vfsspi_wakeup_daemon(struct vfsspi_device_data *vfsspi_device)
|
|
{
|
|
#ifdef CONFIG_SENSORS_FP_LOCKSCREEN_MODE
|
|
if (fp_lockscreen_mode) {
|
|
if (vfsspi_device->signal_id) {
|
|
if (wakeup_by_key() == true &&
|
|
atomic_read(&vfsspi_device->irq_enabled) == DRDY_IRQ_DISABLE) {
|
|
vfsspi_send_drdy_signal(vfsspi_device);
|
|
pr_info("%s send signal done!\n", __func__);
|
|
} else {
|
|
pr_err("%s send signal failed by wakeup(%d)\n",
|
|
__func__, wakeup_by_key());
|
|
}
|
|
} else {
|
|
pr_err("%s fingerprint has no signal_id\n", __func__);
|
|
}
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int vfsspi_probe(struct spi_device *spi)
|
|
{
|
|
int status = 0;
|
|
#ifndef ENABLE_SENSORS_FPRINT_SECURE
|
|
int retry = 0;
|
|
#endif
|
|
struct vfsspi_device_data *vfsspi_device;
|
|
struct device *dev;
|
|
|
|
pr_info("%s\n", __func__);
|
|
|
|
vfsspi_device = kzalloc(sizeof(*vfsspi_device), GFP_KERNEL);
|
|
|
|
if (vfsspi_device == NULL)
|
|
return -ENOMEM;
|
|
|
|
if (spi->dev.of_node) {
|
|
status = vfsspi_parse_dt(&spi->dev, vfsspi_device);
|
|
if (status) {
|
|
pr_err("%s - Failed to parse DT\n", __func__);
|
|
goto vfsspi_probe_parse_dt_failed;
|
|
}
|
|
}
|
|
|
|
/* Initialize driver data. */
|
|
vfsspi_device->current_spi_speed = SLOW_BAUD_RATE;
|
|
vfsspi_device->spi = spi;
|
|
g_data = vfsspi_device;
|
|
|
|
spin_lock_init(&vfsspi_device->vfs_spi_lock);
|
|
mutex_init(&vfsspi_device->buffer_mutex);
|
|
mutex_init(&vfsspi_device->kernel_lock);
|
|
|
|
INIT_LIST_HEAD(&vfsspi_device->device_entry);
|
|
|
|
status = vfsspi_platformInit(vfsspi_device);
|
|
if (status) {
|
|
pr_err("%s - Failed to platformInit\n", __func__);
|
|
goto vfsspi_probe_platformInit_failed;
|
|
}
|
|
#ifdef CONFIG_SENSORS_FINGERPRINT_DUALIZATION
|
|
/* vendor check */
|
|
if (vfsspi_device->vendor_pin) {
|
|
status = vfsspi_vendor_check(vfsspi_device);
|
|
|
|
if (status) { /* normal = 0, not viper = 1 */
|
|
if (status) {
|
|
pr_info("%s: It is not viper sensor.\n", __func__);
|
|
status = -ENODEV;
|
|
goto vfsspi_vendor_check_failed;
|
|
} else if (status < 0) {
|
|
pr_info("%s: vendor gpio request failed.\n", __func__);
|
|
goto vfsspi_vendor_request_failed;
|
|
}
|
|
} else {
|
|
FP_CHECK = 1; /* It is viper sensor */
|
|
}
|
|
} else
|
|
pr_info("%s: This model has no vendor pin dts.\n", __func__);
|
|
#endif
|
|
spi->bits_per_word = BITS_PER_WORD;
|
|
spi->max_speed_hz = SLOW_BAUD_RATE;
|
|
spi->mode = SPI_MODE_0;
|
|
|
|
#ifndef ENABLE_SENSORS_FPRINT_SECURE
|
|
status = spi_setup(spi);
|
|
if (status) {
|
|
pr_err("%s : spi_setup failed\n", __func__);
|
|
goto vfsspi_probe_spi_setup_failed;
|
|
}
|
|
#endif
|
|
|
|
mutex_lock(&device_list_mutex);
|
|
/* Create device node */
|
|
/* register major number for character device */
|
|
status = alloc_chrdev_region(&(vfsspi_device->devt),
|
|
0, 1, VALIDITY_PART_NAME);
|
|
if (status < 0) {
|
|
pr_err("%s alloc_chrdev_region failed\n", __func__);
|
|
goto vfsspi_probe_alloc_chardev_failed;
|
|
}
|
|
|
|
cdev_init(&(vfsspi_device->cdev), &vfsspi_fops);
|
|
vfsspi_device->cdev.owner = THIS_MODULE;
|
|
status = cdev_add(&(vfsspi_device->cdev), vfsspi_device->devt, 1);
|
|
if (status < 0) {
|
|
pr_err("%s cdev_add failed\n", __func__);
|
|
goto vfsspi_probe_cdev_add_failed;
|
|
}
|
|
|
|
vfsspi_device_class = class_create(THIS_MODULE, "validity_fingerprint");
|
|
|
|
if (IS_ERR(vfsspi_device_class)) {
|
|
pr_err("%s vfsspi_init: class_create() is failed - unregister chrdev.\n",
|
|
__func__);
|
|
status = PTR_ERR(vfsspi_device_class);
|
|
goto vfsspi_probe_class_create_failed;
|
|
}
|
|
|
|
dev = device_create(vfsspi_device_class, &spi->dev,
|
|
vfsspi_device->devt, vfsspi_device, "vfsspi");
|
|
status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
|
|
if (status == 0)
|
|
list_add(&vfsspi_device->device_entry, &device_list);
|
|
mutex_unlock(&device_list_mutex);
|
|
|
|
if (status != 0)
|
|
goto vfsspi_probe_failed;
|
|
|
|
spi_set_drvdata(spi, vfsspi_device);
|
|
|
|
#ifdef CONFIG_SENSORS_FINGERPRINT_SYSFS
|
|
status = fingerprint_register(vfsspi_device->fp_device,
|
|
vfsspi_device, fp_attrs, "fingerprint");
|
|
if (status) {
|
|
pr_err("%s sysfs register failed\n", __func__);
|
|
goto vfsspi_probe_failed;
|
|
}
|
|
#endif
|
|
|
|
/* debug polling function */
|
|
setup_timer(&vfsspi_device->dbg_timer,
|
|
vfsspi_timer_func, (unsigned long)vfsspi_device);
|
|
|
|
vfsspi_device->wq_dbg =
|
|
create_singlethread_workqueue("vfsspi_debug_wq");
|
|
if (!vfsspi_device->wq_dbg) {
|
|
status = -ENOMEM;
|
|
pr_err("%s: could not create workqueue\n", __func__);
|
|
goto vfsspi_sysfs_failed;
|
|
}
|
|
INIT_WORK(&vfsspi_device->work_debug, vfsspi_work_func_debug);
|
|
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
|
vfsspi_device->sensortype = SENSOR_UNKNOWN;
|
|
#else
|
|
/* sensor hw type check */
|
|
do {
|
|
vfsspi_type_check(vfsspi_device);
|
|
pr_info("%s, type (%u), retry (%d)\n"
|
|
, __func__, vfsspi_device->sensortype, retry);
|
|
} while (!vfsspi_device->sensortype && ++retry < 3);
|
|
#endif
|
|
|
|
vfsspi_pin_control(vfsspi_device, false);
|
|
vfsspi_enable_debug_timer();
|
|
pr_info("%s successful\n", __func__);
|
|
|
|
return 0;
|
|
|
|
vfsspi_sysfs_failed:
|
|
#ifdef CONFIG_SENSORS_FINGERPRINT_SYSFS
|
|
fingerprint_unregister(vfsspi_device->fp_device, fp_attrs);
|
|
#endif
|
|
vfsspi_probe_failed:
|
|
device_destroy(vfsspi_device_class, vfsspi_device->devt);
|
|
class_destroy(vfsspi_device_class);
|
|
vfsspi_probe_class_create_failed:
|
|
cdev_del(&(vfsspi_device->cdev));
|
|
vfsspi_probe_cdev_add_failed:
|
|
unregister_chrdev_region(vfsspi_device->devt, 1);
|
|
vfsspi_probe_alloc_chardev_failed:
|
|
#ifndef ENABLE_SENSORS_FPRINT_SECURE
|
|
vfsspi_probe_spi_setup_failed:
|
|
#endif
|
|
#ifdef CONFIG_SENSORS_FINGERPRINT_DUALIZATION
|
|
vfsspi_vendor_check_failed:
|
|
pinctrl_put(vfsspi_device->p);
|
|
#endif
|
|
vfsspi_platformUninit(vfsspi_device);
|
|
#ifdef CONFIG_SENSORS_FINGERPRINT_DUALIZATION
|
|
vfsspi_vendor_request_failed:
|
|
#endif
|
|
vfsspi_probe_platformInit_failed:
|
|
mutex_destroy(&vfsspi_device->buffer_mutex);
|
|
mutex_destroy(&vfsspi_device->kernel_lock);
|
|
vfsspi_probe_parse_dt_failed:
|
|
kfree(vfsspi_device);
|
|
pr_err("%s vfsspi_probe failed!!\n", __func__);
|
|
return status;
|
|
}
|
|
|
|
static int vfsspi_remove(struct spi_device *spi)
|
|
{
|
|
int status = 0;
|
|
|
|
struct vfsspi_device_data *vfsspi_device = NULL;
|
|
|
|
pr_info("%s\n", __func__);
|
|
|
|
vfsspi_device = spi_get_drvdata(spi);
|
|
|
|
if (vfsspi_device != NULL) {
|
|
vfsspi_disable_debug_timer();
|
|
spin_lock_irq(&vfsspi_device->vfs_spi_lock);
|
|
vfsspi_device->spi = NULL;
|
|
spi_set_drvdata(spi, NULL);
|
|
spin_unlock_irq(&vfsspi_device->vfs_spi_lock);
|
|
|
|
mutex_lock(&device_list_mutex);
|
|
|
|
vfsspi_platformUninit(vfsspi_device);
|
|
|
|
#ifdef CONFIG_SENSORS_FINGERPRINT_SYSFS
|
|
fingerprint_unregister(vfsspi_device->fp_device, fp_attrs);
|
|
#endif
|
|
/* Remove device entry. */
|
|
list_del(&vfsspi_device->device_entry);
|
|
device_destroy(vfsspi_device_class, vfsspi_device->devt);
|
|
class_destroy(vfsspi_device_class);
|
|
cdev_del(&(vfsspi_device->cdev));
|
|
unregister_chrdev_region(vfsspi_device->devt, 1);
|
|
|
|
mutex_destroy(&vfsspi_device->buffer_mutex);
|
|
mutex_destroy(&vfsspi_device->kernel_lock);
|
|
|
|
kfree(vfsspi_device);
|
|
mutex_unlock(&device_list_mutex);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static void vfsspi_shutdown(struct spi_device *spi)
|
|
{
|
|
if (g_data != NULL)
|
|
vfsspi_disable_debug_timer();
|
|
|
|
pr_info("%s\n", __func__);
|
|
}
|
|
|
|
static int vfsspi_pm_suspend(struct device *dev)
|
|
{
|
|
if (g_data != NULL) {
|
|
vfsspi_disable_debug_timer();
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
|
vfsspi_ioctl_power_off(g_data);
|
|
#endif
|
|
pr_info("%s\n", __func__);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int vfsspi_pm_resume(struct device *dev)
|
|
{
|
|
if (g_data != NULL) {
|
|
#ifdef ENABLE_SENSORS_FPRINT_SECURE
|
|
vfsspi_wakeup_daemon(g_data);
|
|
vfsspi_ioctl_power_on(g_data);
|
|
#endif
|
|
vfsspi_enable_debug_timer();
|
|
pr_info("%s\n", __func__);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops vfsspi_pm_ops = {
|
|
.suspend = vfsspi_pm_suspend,
|
|
.resume = vfsspi_pm_resume
|
|
};
|
|
|
|
struct spi_driver vfsspi_spi = {
|
|
.driver = {
|
|
.name = VALIDITY_PART_NAME,
|
|
.owner = THIS_MODULE,
|
|
.pm = &vfsspi_pm_ops,
|
|
.of_match_table = vfsspi_match_table,
|
|
},
|
|
.probe = vfsspi_probe,
|
|
.remove = vfsspi_remove,
|
|
.shutdown = vfsspi_shutdown,
|
|
};
|
|
|
|
static int __init vfsspi_init(void)
|
|
{
|
|
int status = 0;
|
|
|
|
pr_info("%s vfsspi_init\n", __func__);
|
|
|
|
status = spi_register_driver(&vfsspi_spi);
|
|
if (status < 0) {
|
|
pr_err("%s spi_register_driver() failed\n", __func__);
|
|
return status;
|
|
}
|
|
pr_info("%s init is successful\n", __func__);
|
|
|
|
return status;
|
|
}
|
|
|
|
static void __exit vfsspi_exit(void)
|
|
{
|
|
pr_debug("%s vfsspi_exit\n", __func__);
|
|
spi_unregister_driver(&vfsspi_spi);
|
|
}
|
|
|
|
module_init(vfsspi_init);
|
|
module_exit(vfsspi_exit);
|
|
|
|
MODULE_DESCRIPTION("Validity FPS sensor");
|
|
MODULE_LICENSE("GPL");
|