/* * * 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 "ff_core.h" #include "ff_spi.h" #if IS_ENABLED(CONFIG_TRUSTKERNEL_TEE_SUPPORT) #include "linux/tee_fp.h" #endif /***************************************************************************** * Private constant and macro definitions using #define *****************************************************************************/ #undef LOG_TAG #define LOG_TAG "focaltech_chip" /*prize added by durunshen,ATA test,start*/ static struct kobject *focaltech_kobj = NULL; /*prize added by durunshen,ATA tesh,end*/ #define FF_CMD_SFR_WRITE (0x09) #define FF_CMD_SFR_READ (0x08) #define FF_CMD_SRAM_WRITE (0x05) #define FF_CMD_SRAM_READ (0x04) #define FF_SPI_BUF_SIZE (32) /***************************************************************************** * static variable or structure *****************************************************************************/ /***************************************************************************** * Global variable or extern global variabls/functions *****************************************************************************/ #if IS_ENABLED(CONFIG_TRUSTKERNEL_TEE_SUPPORT) static struct mt_chip_conf ff_mt_chip_conf = { .setuptime = 30, .holdtime = 30, .high_time = 25, .low_time = 25, /* default 2MHz */ .cs_idletime = 10, .ulthgh_thrsh = 0, .sample_sel = POSITIVE_EDGE, .cpol = SPI_CPOL_0, .cpha = SPI_CPHA_0, .rx_mlsb = SPI_MSB, .tx_mlsb = SPI_MSB, .tx_endian = SPI_LENDIAN, .rx_endian = SPI_LENDIAN, .com_mod = FIFO_TRANSFER, /*only read ID*/ .pause = PAUSE_MODE_DISABLE, .finish_intr = FINISH_INTR_EN, .deassert = DEASSERT_DISABLE, .ulthigh = ULTRA_HIGH_DISABLE, .tckdly = TICK_DLY0, }; static int ff_spi_transfer(uint8_t *tx_buf, uint8_t *rx_buf, uint32_t len) { int ret = 0; FF_LOGV("'%s' enter.", __func__); if ((!tx_buf) || (!rx_buf) || (!len)) { FF_LOGE("tx_buf/rx_buf/len(%d) is null", len); return -EINVAL; } ret = tee_spi_transfer(&ff_mt_chip_conf, sizeof(struct mt_chip_conf), tx_buf, rx_buf, len); if (ret) { FF_LOGE("tee_spi_transfer fail, ret=%d", ret); } FF_LOGV("'%s' leave.", __func__); return ret; } #endif #if !IS_ENABLED(CONFIG_TRUSTKERNEL_TEE_SUPPORT) static int ff_spi_transfer(uint8_t *tx_buf, uint8_t *rx_buf, uint32_t len) { int ret = 0; ff_context_t *ff_ctx = g_ff_ctx; struct spi_message msg; struct spi_transfer xfer = { 0 }; uint8_t *kernel_tx_buf = NULL; uint8_t *kernel_rx_buf = NULL; if ((!ff_ctx) || (!(ff_ctx->spi)) || (!tx_buf) || (!rx_buf) || (!len)) { FF_LOGE("ff_ctx/spi/tx_buf/rx_buf/len(%d) is null", len); return -ENODATA; } kernel_tx_buf = kzalloc(FF_SPI_BUF_SIZE, GFP_KERNEL); kernel_rx_buf = kzalloc(FF_SPI_BUF_SIZE, GFP_KERNEL); if ((!kernel_tx_buf) || (!kernel_rx_buf)) { FF_LOGE("failed to allocate memory for kernel_tx/rx_buf"); ret = -ENOMEM; goto err_spi_transfer; } memcpy(kernel_tx_buf, tx_buf, len); xfer.tx_buf = kernel_tx_buf; xfer.rx_buf = kernel_rx_buf; xfer.len = len; spi_message_init(&msg); spi_message_add_tail(&xfer, &msg); ret = spi_sync(ff_ctx->spi, &msg); if (ret) { FF_LOGE("spi_sync fail,ret:%d", ret); } memcpy(rx_buf, kernel_rx_buf, len); err_spi_transfer: if (kernel_tx_buf) { kfree(kernel_tx_buf); kernel_tx_buf = NULL; } if (kernel_rx_buf) { kfree(kernel_rx_buf); kernel_rx_buf = NULL; } return ret; } #endif /*prize added by durunshen,ATA test,start*/ static ssize_t chip_status_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int result = 0; FF_LOGE("g_ff_ctx->chip_id=0x%x\n", g_ff_ctx->chip_id); if (g_ff_ctx->chip_id && g_ff_ctx->chip_id != 0xffff){ result = 1; }else{ result = 0; } return sprintf(buf, "%d\n", result); } static struct kobj_attribute chipid_attr = __ATTR_RO(chip_status); static struct attribute *chip_status_attrs[] = { &chipid_attr.attr, NULL }; static struct attribute_group chip_status_attr_group = { .attrs = chip_status_attrs, }; /*prize added by durunshen,ATA test,end*/ static int ff_sfr_write(uint8_t addr, uint8_t value) { int ret = 0; uint8_t txbuf[FF_SPI_BUF_SIZE] = { 0 }; uint8_t rxbuf[FF_SPI_BUF_SIZE] = { 0 }; uint32_t txlen = 0; FF_LOGV("'%s' enter.", __func__); memset(txbuf, 0, FF_SPI_BUF_SIZE); memset(rxbuf, 0, FF_SPI_BUF_SIZE); txbuf[txlen++] = (uint8_t)(FF_CMD_SFR_WRITE); txbuf[txlen++] = (uint8_t)(~FF_CMD_SFR_WRITE); txbuf[txlen++] = addr; txbuf[txlen++] = value; ret = ff_spi_transfer(txbuf, rxbuf, txlen); if (ret < 0) { FF_LOGE("ff_sfr_write fail,addr=0x%x,ret=%d", addr, ret); } FF_LOGV("'%s' leave.", __func__); return ret; } static int ff_sfr_read(uint8_t addr, uint8_t *value) { int ret = 0; uint8_t txbuf[FF_SPI_BUF_SIZE] = { 0 }; uint8_t rxbuf[FF_SPI_BUF_SIZE] = { 0 }; uint32_t txlen = 0; FF_LOGV("'%s' enter.", __func__); memset(txbuf, 0, FF_SPI_BUF_SIZE); memset(rxbuf, 0, FF_SPI_BUF_SIZE); txbuf[txlen++] = (uint8_t)(FF_CMD_SFR_READ); txbuf[txlen++] = (uint8_t)(~FF_CMD_SFR_READ); txbuf[txlen++] = addr; txbuf[txlen++] = 0; txbuf[txlen++] = 0; ret = ff_spi_transfer(txbuf, rxbuf, txlen); if (ret < 0) { FF_LOGE("ff_sfr_read fail,addr=0x%x,ret=%d", addr, ret); } else { *value = rxbuf[txlen - 1]; } FF_LOGV("'%s' leave.", __func__); return ret; } static int ff_sram_write(uint16_t addr, uint16_t value) { int ret = 0; uint8_t txbuf[FF_SPI_BUF_SIZE] = { 0 }; uint8_t rxbuf[FF_SPI_BUF_SIZE] = { 0 }; uint32_t txlen = 0; uint16_t sram_addr = (addr | 0x8000); uint16_t sram_len = 0x0000; FF_LOGV("'%s' enter.", __func__); memset(txbuf, 0, FF_SPI_BUF_SIZE); memset(rxbuf, 0, FF_SPI_BUF_SIZE); txbuf[txlen++] = (uint8_t)(FF_CMD_SRAM_WRITE); txbuf[txlen++] = (uint8_t)(~FF_CMD_SRAM_WRITE); txbuf[txlen++] = (uint8_t)((sram_addr >> 8) & 0x00FF); txbuf[txlen++] = (uint8_t)(sram_addr & 0x00FF); txbuf[txlen++] = (uint8_t)((sram_len >> 8) & 0x00FF); txbuf[txlen++] = (uint8_t)(sram_len & 0x00FF); txbuf[txlen++] = (uint8_t)((value >> 8) & 0x00FF); txbuf[txlen++] = (uint8_t)(value & 0x00FF); ret = ff_spi_transfer(txbuf, rxbuf, txlen); if (ret < 0) { FF_LOGE("ff_sram_write fail,addr=0x%x,ret=%d", addr, ret); } FF_LOGV("'%s' leave.", __func__); return ret; } static int ff_sram_read(uint16_t addr, uint16_t *value) { int ret = 0; //int i = 0; uint8_t txbuf[FF_SPI_BUF_SIZE] = { 0 }; uint8_t rxbuf[FF_SPI_BUF_SIZE] = { 0 }; uint32_t txlen = 0; uint32_t dp = 0; uint16_t sram_addr = (addr | 0x8000); uint16_t sram_len = 0x0001; FF_LOGV("'%s' enter.", __func__); memset(txbuf, 0, FF_SPI_BUF_SIZE); memset(rxbuf, 0, FF_SPI_BUF_SIZE); txbuf[txlen++] = (uint8_t)(FF_CMD_SRAM_READ); txbuf[txlen++] = (uint8_t)(~FF_CMD_SRAM_READ); txbuf[txlen++] = (uint8_t)((sram_addr >> 8) & 0x00FF); txbuf[txlen++] = (uint8_t)(sram_addr & 0x00FF); txbuf[txlen++] = (uint8_t)((sram_len >> 8) & 0x00FF); txbuf[txlen++] = (uint8_t)(sram_len & 0x00FF); dp = txlen; txlen += 4; ret = ff_spi_transfer(txbuf, rxbuf, txlen); if (ret < 0) { FF_LOGE("ff_sram_read fail,addr=0x%x,ret=%d", addr, ret); } else { *value = (rxbuf[dp] << 8) + rxbuf[dp + 1]; } FF_LOGV("'%s' leave.", __func__); return ret; } static int ff_chip_probe_id(void) { int ret = 0; int i = 0; int read_id_retries = 0; uint16_t chip_id = 0xFFFF; uint8_t val = 0xFF; uint8_t set_iov_tries = 5; uint8_t set_spimode_tries = 3; #if IS_ENABLED(CONFIG_TRUSTKERNEL_TEE_SUPPORT) /*max spi trasfer count:20*/ set_iov_tries = 1; set_spimode_tries = 1; #endif FF_LOGV("'%s' enter.", __func__); for (read_id_retries = 0; read_id_retries < 3; read_id_retries++) { fts_power_sequence(1); /*set io voltage*/ for (i = 0; i < set_iov_tries; i++) { ff_sfr_write(0xFD, 0x0A); ff_sfr_write(0xFE, 0x7F); msleep(1); ff_sfr_read(0xFE, &val); if (0x7F != val) { FF_LOGI("set io voltage abnormal,read:%d,retry:%d", val, i); continue; } FF_LOGD("set io voltage pass"); break; } if (i >= set_iov_tries) { FF_LOGE("set io voltage fail"); } /*set spi mode*/ for (i = 0; i < set_spimode_tries; i++) { ff_sfr_write(0xC6, 0x01); msleep(4); ff_sfr_read(0xC6, &val); if (0x01 != val) { FF_LOGI("set spi mode abnormal,read:%d,retry:%d", val, i); continue; } FF_LOGD("set spi mode pass"); break; } if (i >= set_spimode_tries) { FF_LOGE("set spi mode fail"); } /*clear intr*/ ff_sram_write((0x3500 / 2 + 0x04), 0xFFFF); /*read id*/ ff_sram_read((0x3500 / 2 + 0x0B), &chip_id); FF_LOGI("read chip id:0x%x", chip_id); if ((chip_id & 0xFF00) == 0x9300) { FF_LOGI("probe id(0x%x) pass", chip_id); if (g_ff_ctx) g_ff_ctx->chip_id = chip_id; /*prize added by durunshen,ATA test,start*/ focaltech_kobj = kobject_create_and_add("focaltech_finger", NULL); if(focaltech_kobj == NULL) { FF_LOGI("focaltech_kobj create_and_add failed\n"); ret = -ENOMEM; return ret; } ret = sysfs_create_group(focaltech_kobj, &chip_status_attr_group); /*prize added by durunshen,ATA test,start*/ ret = 0; break; } else { FF_LOGI("probe id fail,read:0x%x,retry=%d", chip_id, read_id_retries); fts_power_sequence(0); ret = -EIO; } } FF_LOGV("'%s' leave.", __func__); return ret; } int ff_probe_id(void) { int ret = 0; FF_LOGI("'%s' enter.", __func__); ret = ff_init_driver(); if (ret < 0) { FF_LOGE("ff_init_driver fail, ret=%d", ret); goto err_init_driver; } ret = ff_chip_probe_id(); if (ret < 0) { FF_LOGE("probe chip id fail, ret=%d", ret); goto err_init_driver; } ret = 0; err_init_driver: ff_free_driver(); FF_LOGI("'%s' leave.", __func__); return ret; }