kernel-brax3-ubuntu-touch/drivers/input/fingerprint/focaltech_ree_ata/ff_spi.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

581 lines
16 KiB
C
Executable file

/*
*
* The spi driver for FocalTech FingerPrint driver.
*
* Copyright (c) 2017-2022, FocalTech Systems, Ltd., all rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#include <linux/module.h>
//#include <linux/init.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#ifdef CONFIG_FINGERPRINT_FOCALTECH_ARCH_MTK
#include <linux/clk.h>
#endif
#include "ff_log.h"
#include "ff_core.h"
#include "ff_spi.h"
/*****************************************************************************
* Private constant and macro definitions using #define
*****************************************************************************/
#undef LOG_TAG
#define LOG_TAG "focaltech_spi"
#define FF_SPI_DRIVER_NAME "focaltech_fpspi"
/*****************************************************************************
* static variable or structure
*****************************************************************************/
struct ff_spi_sync_data {
char *tx;
char *rx;
unsigned int size;
};
struct ff_spi_sync_data_2 {
char *tx;
char *rx;
unsigned int tx_len;
unsigned int rx_len;
};
#define FF_IOCTL_SPI_DEVICE 0xC6
#define FF_IOCTL_SPI_SYNC _IOWR(FF_IOCTL_SPI_DEVICE, 0x01, struct ff_spi_sync_data)
#define FF_IOCTL_GET_SPI_MODE _IOR(FF_IOCTL_SPI_DEVICE, 0x02, u32)
#define FF_IOCTL_SET_SPI_MODE _IOW(FF_IOCTL_SPI_DEVICE, 0x02, u32)
#define FF_IOCTL_GET_SPI_SPEED _IOR(FF_IOCTL_SPI_DEVICE, 0x03, u32)
#define FF_IOCTL_SET_SPI_SPEED _IOW(FF_IOCTL_SPI_DEVICE, 0x03, u32)
#define FF_IOCTL_SPI_SYNC_2 _IOWR(FF_IOCTL_SPI_DEVICE, 0x10, struct ff_spi_sync_data_2)
/*****************************************************************************
* Global variable or extern global variabls/functions
*****************************************************************************/
/* spi interface */
static int ff_spi_sync(struct ff_spi_context *spi_ctx, uint8_t *tx_buf, uint8_t *rx_buf, uint32_t len)
{
int ret = 0;
struct spi_message msg;
struct spi_transfer xfer = {
.tx_buf = tx_buf,
.rx_buf = rx_buf,
.len = len,
};
if (!spi_ctx) {
FF_LOGE("spi_ctx is null");
return -ENODATA;
}
mutex_lock(&spi_ctx->bus_lock);
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
ret = spi_sync(spi_ctx->spi, &msg);
if (ret) {
FF_LOGE("spi_sync fail,ret:%d", ret);
}
mutex_unlock(&spi_ctx->bus_lock);
return ret;
}
static int ff_ioctl_spi_sync(struct ff_spi_context *spi_ctx, unsigned long arg)
{
int ret = 0;
struct ff_spi_sync_data spi_data;
u8 *txbuf = spi_ctx->bus_tx_buf;
u8 *rxbuf = spi_ctx->bus_rx_buf;
FF_LOGV("'%s' enter.", __func__);
if (copy_from_user(&spi_data, (void *)arg, sizeof(struct ff_spi_sync_data))) {
FF_LOGE("copy spi_sync data from userspace fail");
return -EFAULT;
}
if (!spi_data.tx || !spi_data.size) {
FF_LOGE("tx/size(%d) from userspace are invalid", spi_data.size);
return -EINVAL;
}
if (!txbuf || !rxbuf) {
FF_LOGE("spi tx/rx buf is null");
return -EINVAL;
}
if (spi_data.size > PAGE_SIZE) {
txbuf = kzalloc(spi_data.size, GFP_KERNEL);
rxbuf = kzalloc(spi_data.size, GFP_KERNEL);
if (!txbuf || !rxbuf) {
FF_LOGE("kzalloc memory(size:%d) for spi tx/rx buffer fail", spi_data.size);
ret = -ENOMEM;
goto spi_sync_err;
}
} else {
memset(txbuf, 0, spi_data.size);
memset(rxbuf, 0, spi_data.size);
}
if (copy_from_user(txbuf, spi_data.tx, spi_data.size)) {
FF_LOGE("copy spi tx data from userspace fail");
ret = -EFAULT;
goto spi_sync_err;
}
ret = ff_spi_sync(spi_ctx, txbuf, rxbuf, spi_data.size);
if (ret) {
FF_LOGE("spi sync fail");
ret = -EIO;
goto spi_sync_err;
}
if (spi_data.rx && copy_to_user(spi_data.rx, rxbuf, spi_data.size)) {
FF_LOGE("copy spi rx data to userspace fail");
ret = -EFAULT;
goto spi_sync_err;
}
ret = 0;
spi_sync_err:
if (spi_data.size > PAGE_SIZE) {
kfree(txbuf);
kfree(rxbuf);
}
FF_LOGV("'%s' leave.", __func__);
return ret;
}
static int ff_ioctl_spi_sync_2(struct ff_spi_context *spi_ctx, unsigned long arg)
{
int ret = 0;
struct ff_spi_sync_data_2 spi_data;
u8 *txbuf = spi_ctx->bus_tx_buf;
u8 *rxbuf = spi_ctx->bus_rx_buf;
u32 size = 0;
FF_LOGV("'%s' enter.", __func__);
if (copy_from_user(&spi_data, (void *)arg, sizeof(struct ff_spi_sync_data_2))) {
FF_LOGE("copy spi_sync data from userspace fail");
return -EFAULT;
}
size = spi_data.tx_len + spi_data.rx_len;
if ((!spi_data.tx && !spi_data.rx) || !size) {
FF_LOGE("tx_rx/size(%d) from userspace are invalid", size);
return -EINVAL;
}
if (!txbuf || !rxbuf) {
FF_LOGE("spi tx/rx buf is null");
return -EINVAL;
}
if (size > PAGE_SIZE) {
txbuf = kzalloc(size, GFP_KERNEL);
rxbuf = kzalloc(size, GFP_KERNEL);
if (!txbuf || !rxbuf) {
FF_LOGE("kzalloc memory(size:%d) for spi tx/rx buffer fail", size);
ret = -ENOMEM;
goto spi_sync2_err;
}
} else {
memset(txbuf, 0, size);
memset(rxbuf, 0, size);
}
if (spi_data.tx && spi_data.tx_len && \
copy_from_user(txbuf, spi_data.tx, spi_data.tx_len)) {
FF_LOGE("copy spi tx data from userspace fail");
ret = -EFAULT;
goto spi_sync2_err;
}
ret = ff_spi_sync(spi_ctx, txbuf, rxbuf, size);
if (ret) {
FF_LOGE("spi sync fail");
ret = -EIO;
goto spi_sync2_err;
}
if (spi_data.rx && spi_data.rx_len && \
copy_to_user(spi_data.rx, rxbuf + spi_data.tx_len, spi_data.rx_len)) {
FF_LOGE("copy spi rx data to userspace fail");
ret = -EFAULT;
goto spi_sync2_err;
}
ret = 0;
spi_sync2_err:
if (size > PAGE_SIZE) {
kfree(txbuf);
kfree(rxbuf);
}
FF_LOGV("'%s' leave.", __func__);
return ret;
}
static int ff_ioctl_set_spi_mode(struct ff_spi_context *spi_ctx, unsigned long arg)
{
int ret = 0;
u32 mode = 0;
FF_LOGD("'%s' enter.", __func__);
ret = __get_user(mode, (__u32 __user *)arg);
FF_LOGD("set spi mode:%d", mode);
if ((ret == 0) && (spi_ctx->spi->mode != mode)) {
spi_ctx->spi->mode = mode;
ret = spi_setup(spi_ctx->spi);
if (ret < 0) FF_LOGE("spi setup fail, ret:%d", ret);
else FF_LOGI("set spi mode:0x%x", (int)spi_ctx->spi->mode);
}
FF_LOGD("'%s' leave.", __func__);
return ret;
}
static int ff_ioctl_set_spi_speed(struct ff_spi_context *spi_ctx, unsigned long arg)
{
int ret = 0;
u32 speed = 0;
FF_LOGD("'%s' enter.", __func__);
ret = __get_user(speed, (__u32 __user *)arg);
FF_LOGD("set spi speed:%d", speed);
if ((ret == 0) && (spi_ctx->spi->max_speed_hz != speed)) {
spi_ctx->spi->max_speed_hz = speed;
ret = spi_setup(spi_ctx->spi);
if (ret < 0) FF_LOGE("spi setup fail, ret:%d", ret);
else FF_LOGI("set spi speed:%d", spi_ctx->spi->max_speed_hz);
}
FF_LOGD("'%s' leave.", __func__);
return ret;
}
static int ff_spi_open(struct inode *inode, struct file *filp)
{
struct ff_spi_context *spi_ctx = container_of(filp->private_data, struct ff_spi_context, mdev);
FF_LOGD("'%s' enter.", __func__);
if (!spi_ctx) {
FF_LOGE("spi_ctx is null");
return -ENODATA;
}
if (!spi_ctx->bus_tx_buf) {
spi_ctx->bus_tx_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (NULL == spi_ctx->bus_tx_buf) {
FF_LOGE("failed to allocate memory for bus_tx_buf");
return -ENOMEM;
}
}
if (!spi_ctx->bus_rx_buf) {
spi_ctx->bus_rx_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (NULL == spi_ctx->bus_rx_buf) {
FF_LOGE("failed to allocate memory for bus_rx_buf");
kfree(spi_ctx->bus_tx_buf);
spi_ctx->bus_tx_buf = NULL;
return -ENOMEM;
}
}
FF_LOGD("'%s' leave.", __func__);
return 0;
}
static int ff_spi_close(struct inode *inode, struct file *filp)
{
struct ff_spi_context *spi_ctx = container_of(filp->private_data, struct ff_spi_context, mdev);
FF_LOGD("'%s' enter.", __func__);
if (!spi_ctx) {
FF_LOGE("spi_ctx is null");
return -ENODATA;
}
if (spi_ctx->bus_tx_buf) {
kfree(spi_ctx->bus_tx_buf);
spi_ctx->bus_tx_buf = NULL;
}
if (spi_ctx->bus_rx_buf) {
kfree(spi_ctx->bus_rx_buf);
spi_ctx->bus_rx_buf = NULL;
}
FF_LOGD("'%s' leave.", __func__);
return 0;
}
static long ff_spi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
struct ff_spi_context *spi_ctx = container_of(filp->private_data, struct ff_spi_context, mdev);
FF_LOGV("'%s' enter.", __func__);
if (!spi_ctx) {
FF_LOGE("spi_ctx is null");
return -ENODATA;
}
FF_LOGV("ioctl cmd:%x,arg:%lx,%x", (int)cmd, arg, FF_IOCTL_SPI_SYNC);
switch (cmd) {
case FF_IOCTL_SPI_SYNC:
ret = ff_ioctl_spi_sync(spi_ctx, arg);
break;
case FF_IOCTL_GET_SPI_MODE:
ret = __put_user(spi_ctx->spi->mode, (__u32 __user *)arg);
break;
case FF_IOCTL_GET_SPI_SPEED:
ret = __put_user(spi_ctx->spi->max_speed_hz, (__u32 __user *)arg);
break;
case FF_IOCTL_SET_SPI_MODE:
ret = ff_ioctl_set_spi_mode(spi_ctx, arg);
case FF_IOCTL_SET_SPI_SPEED:
ret = ff_ioctl_set_spi_speed(spi_ctx, arg);
break;
case FF_IOCTL_SPI_SYNC_2:
ret = ff_ioctl_spi_sync_2(spi_ctx, arg);
break;
default:
FF_LOGI("unkown ioctl cmd(0x%x)", (int)cmd);
break;
}
FF_LOGV("'%s' leave.", __func__);
return ret;
}
static struct file_operations ff_spi_fops = {
.open = ff_spi_open,
.release = ff_spi_close,
.unlocked_ioctl = ff_spi_ioctl,
};
#ifdef FF_SPI_PINCTRL
static void ff_spi_set_active(struct device *dev)
{
int ret = 0;
struct pinctrl *pinctrl;
struct pinctrl_state *pins_spi_active;
FF_LOGV("'%s' enter.", __func__);
FF_LOGI("set spi pinctrl");
pinctrl = devm_pinctrl_get(dev);
if (IS_ERR(pinctrl)) {
ret = PTR_ERR(pinctrl);
FF_LOGD("no SPI pinctrl,ret=%d", ret);
return ;
}
pins_spi_active = pinctrl_lookup_state(pinctrl, "ffspi_pins_active");
if (IS_ERR(pins_spi_active)) {
ret = PTR_ERR(pins_spi_active);
FF_LOGE("Cannot find pinctrl ffspi_active,ret=%d", ret);
return ;
}
pinctrl_select_state(pinctrl, pins_spi_active);
FF_LOGV("'%s' leave.", __func__);
}
#endif
#ifdef CONFIG_FINGERPRINT_FOCALTECH_ARCH_MTK
static uint32_t ff_spi_get_clk_hz(struct spi_device *spi)
{
int ret = 0;
uint32_t hz = 0;
struct clk *spiclk;
FF_LOGI("start to get spi-clk hz");
spiclk = devm_clk_get(spi->controller->dev.parent, "spi-clk");
if (IS_ERR(spiclk)) {
ret = PTR_ERR(spiclk);
FF_LOGE("failed to get spi-clk: %d\n", ret);
put_device(&spi->controller->dev);
return 0;
}
ret = clk_prepare_enable(spiclk);
if (ret < 0) {
FF_LOGE("clk_prepare_enable fails");
put_device(&spi->controller->dev);
return 0;
}
hz = clk_get_rate(spiclk);
clk_disable_unprepare(spiclk);
FF_LOGI("get spi hz(mtk):%d", hz);
return hz;
}
#endif
//prize add by dengbinggui for ata node 20240927 start
#ifdef CONFIG_PRI_FINGER_ATA_NODE
extern void pri_ftm_node_exist(int exist);
#endif
//prize add by dengbinggui for ata node 20240927 end
static int ff_spi_probe(struct spi_device *spi)
{
int ret = 0;
struct ff_spi_context *spi_ctx = NULL;
ff_context_t *ff_ctx = g_ff_ctx;
FF_LOGI("'%s' enter.", __func__);
ff_ctx->spi = spi;
ff_ctx->b_spiclk_enabled = 0;
#ifdef FF_SPI_PINCTRL
/*set SPI pinctrl*/
ff_spi_set_active(&spi->dev);
#endif
#ifdef CONFIG_FINGERPRINT_FOCALTECH_ARCH_MTK
ff_ctx->spi_clk_hz = ff_spi_get_clk_hz(spi);
if (ff_ctx->spi_clk_hz) {
ff_ctx->feature.fbs_spiclk_hz = true;
}
#endif
#ifdef CONFIG_FINGERPRINT_FOCALTECH_TEE_REE
FF_LOGI("emulation TEE");
ff_ctx->b_ree = 1;
#endif
if (ff_ctx->b_read_chipid || ff_ctx->b_ree) {
FF_LOGI("need configure spi for communication");
/*spi_setup*/
spi->mode = SPI_MODE_0;
spi->bits_per_word = 8;
spi->max_speed_hz = 4000000;
ret = spi_setup(spi);
if (ret < 0) {
FF_LOGE("spi setup fail");
return ret;
}
/* malloc memory for global struct variable */
spi_ctx = (struct ff_spi_context *)kzalloc(sizeof(*spi_ctx), GFP_KERNEL);
if (!spi_ctx) {
FF_LOGE("allocate memory for ff_spi_context fail");
return -ENOMEM;
}
/*init*/
spi_ctx->spi = spi;
spi_set_drvdata(spi, spi_ctx);
mutex_init(&spi_ctx->bus_lock);
}
if (ff_ctx->b_read_chipid) {
ret = ff_probe_id();
if (ret < 0) {
FF_LOGE("probe id fail, ret=%d", ret);
ff_ctx->probe_id_ret = ret;
kfree(spi_ctx);
spi_ctx = NULL;
spi_set_drvdata(spi, NULL);
return ret;
}
// prize add by dengbinggui for ata node 20240927 start
#ifdef CONFIG_PRI_FINGER_ATA_NODE
else{
FF_LOGE("get chip id success, ret=%d", ret);
pri_ftm_node_exist(1);
}
#endif
// prize add by dengbinggui for ata node 20240927 end
}
if (ff_ctx->b_ree) {
FF_LOGI("need add spi ioctrl for ree");
/*register misc_device*/
spi_ctx->mdev.minor = MISC_DYNAMIC_MINOR;
spi_ctx->mdev.name = FF_SPI_DRIVER_NAME;
spi_ctx->mdev.fops = &ff_spi_fops;
ret = misc_register(&spi_ctx->mdev);
if (ret < 0) {
FF_LOGE("misc_register(%s) fail", FF_SPI_DRIVER_NAME);
kfree(spi_ctx);
spi_ctx = NULL;
spi_set_drvdata(spi, NULL);
return ret;
}
spi_ctx->b_misc = true;
}
FF_LOGI("'%s' leave.", __func__);
return 0;
}
static int ff_spi_remove(struct spi_device *spi)
{
struct ff_spi_context *spi_ctx = spi_get_drvdata(spi);
FF_LOGI("'%s' enter.", __func__);
if (spi_ctx) {
if (spi_ctx->b_misc) misc_deregister(&spi_ctx->mdev);
mutex_destroy(&spi_ctx->bus_lock);
if (spi_ctx->bus_tx_buf) kfree(spi_ctx->bus_tx_buf);
if (spi_ctx->bus_rx_buf) kfree(spi_ctx->bus_rx_buf);
kfree(spi_ctx);
spi_ctx = NULL;
}
spi_set_drvdata(spi, NULL);
FF_LOGI("'%s' leave.", __func__);
return 0;
}
static const struct of_device_id ff_spi_dt_match[] = {
{.compatible = "focaltech,fpspi", },
{},
};
MODULE_DEVICE_TABLE(of, ff_spi_dt_match);
static struct spi_driver ff_spi_driver = {
.driver = {
.name = FF_SPI_DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ff_spi_dt_match),
},
.probe = ff_spi_probe,
.remove = ff_spi_remove,
};
int ff_spi_init(void)
{
int ret = 0;
FF_LOGI("'%s' enter.", __func__);
ret = spi_register_driver(&ff_spi_driver);
if ( ret != 0 ) {
FF_LOGE("ff spi driver init failed!");
}
FF_LOGI("'%s' leave.", __func__);
return ret;
}
void ff_spi_exit(void)
{
FF_LOGI("'%s' enter.", __func__);
spi_unregister_driver(&ff_spi_driver);
FF_LOGI("'%s' leave.", __func__);
}