dlkm: add new silead fps driver

Silead fingerprint sensor is a new chipset and vendor
Add new driver to support it.

Change-Id: Ibbad53b820b527e4d4e42af9c6f7ec5718d1d6dd
Signed-off-by: wuwl10 <wuwl10@motorola.com>
Reviewed-on: https://gerrit.mot.com/2062795
SME-Granted: SME Approvals Granted
SLTApproved: Slta Waiver
Tested-by: Jira Key
Reviewed-by: Ling Zhong <zhongl1@motorola.com>
Reviewed-by: Yan Zhang <zhangy26@motorola.com>
Reviewed-by: Hujun Liao <liaohj@motorola.com>
Submit-Approved: Jira Key
This commit is contained in:
wuwl10 2021-09-09 10:51:46 +08:00 • committed by Weilong Wu
commit 0411fbfbcd
7 changed files with 2560 additions and 0 deletions

View file

@ -0,0 +1,15 @@
DLKM_DIR := motorola/kernel/modules
LOCAL_PATH := $(call my-dir)
ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true)
SILEAD_FPS_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/
else
SILEAD_FPS_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT)
endif
include $(CLEAR_VARS)
LOCAL_MODULE := silead_fps_mmi.ko
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(SILEAD_FPS_MMI_MODULE_PATH)
include $(DLKM_DIR)/AndroidKernelModule.mk

View file

@ -0,0 +1,21 @@
# add -Wall to try to catch everything we can.
EXTRA_CFLAGS += -Wall
EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include
ifdef CONFIG_ARCH_MSM
ccflags-y += -DBSP_SIL_PLAT_QCOM
endif
ifdef CONFIG_ARCH_QCOM
ccflags-y += -DBSP_SIL_PLAT_QCOM
endif
ifdef CONFIG_ARCH_SPRD
ccflags-y += -DBSP_SIL_PLAT_SPRD
endif
silead_fps_mmi-y += silead_fp.o
obj-m += silead_fps_mmi.o

View file

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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,316 @@
/*
* @file silead_fp.h
* @brief Contains silead_fp device head file.
*
*
* Copyright 2016-2021 GigaDevice/Silead Inc.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*
* ------------------- Revision History ------------------------------
* <author> <date> <version> <desc>
* Bill Yu 2018/5/2 0.1.0 Init version
* Bill Yu 2018/5/28 0.1.1 Disable netlink if netlink id = 0
* Bill Yu 2018/6/1 0.1.2 Support wakelock
* Bill Yu 2018/6/5 0.1.3 Support chip enter power down
* Bill Yu 2018/6/7 0.1.4 Support create proc node
* Bill Yu 2018/6/27 0.1.5 Expand pwdn I/F
* Rui Wu 2019/10/10 0.1.6 Support create class node
* Bill Yu 2020/5/11 0.1.7 Netlink id should < 32
* Bill Yu 2020/5/29 0.1.8 Default use poll
* Bill Yu 2020/12/22 0.1.9 Support 5.x version wakeup
* Melvin cao 2021/01/29 0.2.0 Support silead spi device driver
*/
#ifndef __SILEAD_FP_H__
#define __SILEAD_FP_H__
#include <linux/printk.h>
#include <linux/version.h>
#ifndef _LINUX_WAKELOCK_H
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
#define USE_WAKEUP_REG
#endif
enum {
WAKE_LOCK_SUSPEND, /* Prevent suspend */
WAKE_LOCK_TYPE_COUNT
};
#ifdef USE_WAKEUP_REG
struct wake_lock {
struct wakeup_source *ws;
};
#define ws_init(s, n) s = wakeup_source_register(NULL, n)
#define ws_deinit(s) wakeup_source_unregister(s)
#define ws_lock(s) __pm_stay_awake(s)
#define ws_lock_tm(s, t) __pm_wakeup_event(s, t)
#define ws_unlock(s) __pm_relax(s)
#else
struct wake_lock {
struct wakeup_source ws;
};
#define ws_init(s, n) wakeup_source_init(&s, n)
#define ws_deinit(s) wakeup_source_trash(&s)
#define ws_lock(s) __pm_stay_awake(&s)
#define ws_lock_tm(s, t) __pm_wakeup_event(&s, t)
#define ws_unlock(s) __pm_relax(&s)
#endif /* USE_WAKEUP_REG */
static inline void wake_lock_init(struct wake_lock *lock, int type,
const char *name)
{
ws_init(lock->ws, name);
}
static inline void wake_lock_destroy(struct wake_lock *lock)
{
ws_deinit(lock->ws);
}
static inline void wake_lock(struct wake_lock *lock)
{
ws_lock(lock->ws);
}
static inline void wake_lock_timeout(struct wake_lock *lock, long timeout)
{
ws_lock_tm(lock->ws, jiffies_to_msecs(timeout));
}
static inline void wake_unlock(struct wake_lock *lock)
{
ws_unlock(lock->ws);
}
#endif /* _LINUX_WAKELOCK_H */
enum _pwdn_mode_t {
SIFP_PWDN_NONE = 0,
SIFP_PWDN_POWEROFF = 1, /* shutdown the avdd power supply */
SIFP_PWDN_FLASH = 2, /* shutdown avdd 200ms for H/W full reset */
SIFP_PWDN_MAX,
};
enum _netlink_cmd_t {
SIFP_NETLINK_START = 0,
SIFP_NETLINK_IRQ = 1,
SIFP_NETLINK_SCR_OFF,
SIFP_NETLINK_SCR_ON,
SIFP_NETLINK_CONNECT,
SIFP_NETLINK_DISCONNECT,
SIFP_NETLINK_TP_TOUCHDOWN,
SIFP_NETLINK_TP_TOUCHUP,
SIFP_NETLINK_MAX,
};
enum _fp_nav_key_v_t {
NAV_KEY_UNKNOWN = 0,
NAV_KEY_START = 1,
NAV_KEY_UP = NAV_KEY_START,
NAV_KEY_DOWN,
NAV_KEY_RIGHT,
NAV_KEY_LEFT,
NAV_KEY_CLICK,
NAV_KEY_DCLICK,
NAV_KEY_LONGPRESS,
NAV_KEY_CLICK_DOWN,
NAV_KEY_CLICK_UP,
NAV_KEY_MAX,
NAV_KEY_WAITMORE = 1000,
};
#define IS_KEY_VALID(k) ((k) > NAV_KEY_UNKNOWN && (k) < NAV_KEY_MAX)
enum _fp_nav_key_f_t {
NAV_KEY_FLAG_UP = 0,
NAV_KEY_FLAG_DOWN,
NAV_KEY_FLAG_CLICK,
};
struct fp_dev_key_t {
int value;
uint32_t flag; /* key down = 1, key up = 0, key down+up = 2 */
};
#define DEVNAME_LEN 16
struct fp_dev_init_t {
uint8_t mode;
uint8_t bits;
uint16_t delay;
uint32_t speed;
char dev[DEVNAME_LEN];
uint8_t nl_id;
uint8_t dev_id;
uint16_t reserve;
uint32_t reg;
char ta[DEVNAME_LEN];
};
struct fp_dev_debug_t {
uint8_t cmd[4];
};
struct fp_dev_kmap_t {
uint16_t k[NAV_KEY_MAX-NAV_KEY_START]; /* Up/Down/Right/Left/Click/Double Click/Longpress */
};
struct fp_dev_touch_info {
uint8_t touch_state;
uint8_t area_rate;
uint16_t x;
uint16_t y;
};
#define PROC_VND_ID_LEN 32
#define SIFP_IOC_MAGIC 's'
#define SIFP_IOC_RESET _IOW(SIFP_IOC_MAGIC, 10, u8)
#define SIFP_IOC_ENABLE_IRQ _IO(SIFP_IOC_MAGIC, 11)
#define SIFP_IOC_DISABLE_IRQ _IO(SIFP_IOC_MAGIC, 12)
#define SIFP_IOC_WAIT_IRQ _IOR(SIFP_IOC_MAGIC, 13, u8)
#define SIFP_IOC_CLR_IRQ _IO(SIFP_IOC_MAGIC, 14)
//#define SPFP_IOC_EXIT _IOR(SIFP_IOC_MAGIC, 1, u8)
#define SIFP_IOC_KEY_EVENT _IOW(SIFP_IOC_MAGIC, 15, struct fp_dev_key_t)
#define SIFP_IOC_INIT _IOR(SIFP_IOC_MAGIC, 16, struct fp_dev_init_t)
#define SIFP_IOC_DEINIT _IO(SIFP_IOC_MAGIC, 17)
#define SIFP_IOC_IRQ_STATUS _IOR(SIFP_IOC_MAGIC, 18, u8)
#define SIFP_IOC_DEBUG _IOR(SIFP_IOC_MAGIC, 19, struct fp_dev_debug_t)
#define SIFP_IOC_SCR_STATUS _IOR(SIFP_IOC_MAGIC, 20, u8)
#define SIFP_IOC_GET_VER _IOR(SIFP_IOC_MAGIC, 21, char[10])
#define SIFP_IOC_SET_KMAP _IOW(SIFP_IOC_MAGIC, 22, uint16_t[7])
#define SIFP_IOC_ACQ_SPI _IO(SIFP_IOC_MAGIC, 23)
#define SIFP_IOC_RLS_SPI _IO(SIFP_IOC_MAGIC, 24)
#define SIFP_IOC_PKG_SIZE _IOR(SIFP_IOC_MAGIC, 25, u8)
#define SIFP_IOC_DBG_LEVEL _IOWR(SIFP_IOC_MAGIC,26, u8)
#define SIFP_IOC_WAKELOCK _IOW(SIFP_IOC_MAGIC, 27, u8)
#define SIFP_IOC_PWDN _IOW(SIFP_IOC_MAGIC, 28, u8)
#define SIFP_IOC_PROC_NODE _IOW(SIFP_IOC_MAGIC, 29, char[PROC_VND_ID_LEN])
#define SIFP_IOC_SET_FEATURE _IOW(SIFP_IOC_MAGIC, 30, u8)
#define FEATURE_FLASH_CS 0x01
#define RESET_TIME 2 /* Default chip reset wait time(ms) */
#define RESET_TIME_MULTIPLE 1 /* Multiple for reset time multiple*wait_time */
#define SIFP_NETLINK_ROUTE 0 /* 0: poll/1-31: netlink(cat /proc/net/netlink)/ > 31: epoll/ 30 */
#define NL_MSG_LEN 16
//#define PROC_DIR "fp" /* if defined, create node under /proc/fp/xxx */
#define PROC_NODE "fp_id" /* proc node name */
//#define CLASS_NODE "fingerprint" /* if defined, create class node /sys/class/fingerprint/fingerprint */
#if (SIFP_NETLINK_ROUTE > 0) && (SIFP_NETLINK_ROUTE < 32)
#define BSP_SIL_NETLINK
#endif
#if !defined(BSP_SIL_PLAT_MTK) && !defined(BSP_SIL_PLAT_QCOM)
#define BSP_SIL_PLAT_COMM
#endif /* ! BSP_SIL_PLAT_MTK & ! BSP_SIL_PLAT_QCOM */
/* Todo: enable correct power supply mode */
//#define BSP_SIL_POWER_SUPPLY_REGULATOR
//#define BSP_SIL_POWER_SUPPLY_PINCTRL
#define BSP_SIL_POWER_SUPPLY_GPIO
/* AVDD voltage range 2.8v ~ 3.3v */
#define AVDD_MAX 2800000
#define AVDD_MIN 2800000
/* VDDIO voltage range 1.8v ~ AVDD */
#define VDDIO_MAX 1800000
#define VDDIO_MIN 1800000
#if defined(BSP_SIL_POWER_SUPPLY_REGULATOR) && defined(BSP_SIL_POWER_SUPPLY_PINCTRL) || defined(BSP_SIL_POWER_SUPPLY_REGULATOR) && defined(BSP_SIL_POWER_SUPPLY_GPIO) || defined(BSP_SIL_POWER_SUPPLY_GPIO) && defined(BSP_SIL_POWER_SUPPLY_PINCTRL)
#error "Don't define multiple power supply mode!"
#endif
#ifdef BSP_SIL_PLAT_MTK
#include "silead_fp_mtk.h"
#define PLAT_H "silead_fp_mtk.c"
#define SILFP_SPI_REE /* REE silead_spi driver, disable it if CONFIG_SPIDEV is enabled */
#ifdef SILFP_SPI_REE
#define DEVICE "/dev/silead_spi"
#else
#define DEVICE "/dev/spidev1.0"
#endif
//#define BSP_SIL_IRQ_CONFIRM
#define PKG_SIZE 1
#define BSP_SIL_DYNAMIC_SPI
#ifndef CONFIG_SILEAD_FP_PLATFORM
#define BSP_SIL_CTRL_SPI
#endif /* !CONFIG_SILEAD_FP_PLATFORM */
#elif defined(BSP_SIL_PLAT_QCOM)
#define QSEE_V4 /* Enable it if QSEE v4 or higher */
#include "silead_fp_qcom.h"
#define PLAT_H "silead_fp_qcom.c"
#define DEVICE "/dev/spidev0.0"
//#define BSP_SIL_IRQ_CONFIRM
#define PKG_SIZE 4
#define TANAME "sileadta"
#elif defined(BSP_SIL_PLAT_SPRD)
#include "silead_fp_sprd.h"
#define PLAT_H "silead_fp_sprd.c"
#define DEVICE "/dev/spidev0.0"
//#define BSP_SIL_IRQ_CONFIRM
//#define BSP_SIL_QCOM_SPECIAL
#define PKG_SIZE 4
#define TANAME "silead"
#else
#include "silead_fp_comm.h"
#define PLAT_H "silead_fp_comm.c"
#define DEVICE "/dev/spidev0.0"
//#define BSP_SIL_IRQ_CONFIRM
#define PKG_SIZE 4
#define TANAME ""
#endif /* BSP_SIL_PLAT_XXX */
#ifdef BSP_SIL_QCOM_SPECIAL
#define SIL_EVENT_BLANK MSM_DRM_EARLY_EVENT_BLANK
#define SIL_EVENT_UNBLANK MSM_DRM_BLANK_UNBLANK
#define SIL_EVENT_POWERDOWN MSM_DRM_BLANK_POWERDOWN
#define SIL_REGISTER_CLIENT(a) do{ msm_drm_register_client(a); }while(0)
#else
#define SIL_EVENT_BLANK FB_EVENT_BLANK
#define SIL_EVENT_UNBLANK FB_BLANK_UNBLANK
#define SIL_EVENT_POWERDOWN FB_BLANK_POWERDOWN
#define SIL_REGISTER_CLIENT(a) do{ fb_register_client(a); }while(0)
#endif
/* -------------------------------------------------------------------- */
/* debug settings */
/* -------------------------------------------------------------------- */
typedef enum {
ERR_LOG=0,
INFO_LOG,
DBG_LOG,
ALL_LOG,
} fp_debug_level_t;
#define LOG_TAG "[+silead_fp-] "
#define LOG_MSG_DEBUG(level, fmt, args...) do { \
if (sil_debug_level >= level) {\
pr_warn(LOG_TAG fmt, ##args); \
} \
} while (0)
#endif /* __SILEAD_FP_H__ */
/* End of file silead_fp.h */

View file

@ -0,0 +1,603 @@
/*
* @file silead_fp_qcom.c
* @brief Contains silead_fp device implements for Qualcomm platform.
*
*
* Copyright 2016-2021 Gigadevice/Silead Inc.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ------------------- Revision History ------------------------------
* <author> <date> <version> <desc>
* Bill Yu 2018/5/2 0.1.0 Init version
* Bill Yu 2018/5/20 0.1.1 Default wait 3ms after reset
* Bill Yu 2018/6/5 0.1.2 Support chip enter power down
* Bill Yu 2018/6/27 0.1.3 Expand pwdn I/F
* Taobb 2019/6/6 0.1.4 Expand feature interface, irq pin set to reset pin
* Bill Yu 2020/12/16 0.1.5 Allow GPIO ID number to be zero
*
*/
#ifdef BSP_SIL_PLAT_QCOM
#ifndef __SILEAD_FP_QCOM__
#define __SILEAD_FP_QCOM__
#include <linux/of_irq.h>
#ifndef INIT_COMPLETION
#define INIT_COMPLETION(x) ((x).done = 0)
#endif /* INIT_COMPLETION */
/* Qualcomm MNC platform, need define this. */
#define reinit_completion(x) INIT_COMPLETION(*(x))
#define SPI_PINCTRL_STATE_DEFAULT "spi_default"
#define SPI_PINCTRL_STATE_SLEEP "spi_sleep"
static irqreturn_t silfp_irq_handler(int irq, void *dev_id);
static void silfp_work_func(struct work_struct *work);
static int silfp_input_init(struct silfp_data *fp_dev);
/* -------------------------------------------------------------------- */
/* power supply */
/* -------------------------------------------------------------------- */
static void silfp_hw_poweron(struct silfp_data *fp_dev)
{
int err = 0;
LOG_MSG_DEBUG(INFO_LOG, "[%s] enter.\n", __func__);
#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR
/* Power control by Regulators(LDO) */
if ( fp_dev->avdd_ldo ) {
err = regulator_set_voltage(fp_dev->avdd_ldo, AVDD_MIN, AVDD_MAX); /*set 2.8v*/
err = regulator_enable(fp_dev->avdd_ldo); /*enable regulator*/
//pmic_set_register_value(PMIC_RG_VCAMA_CAL,0x0A);
}
if ( fp_dev->vddio_ldo ) {
err = regulator_set_voltage(fp_dev->vddio_ldo, VDDIO_MIN, VDDIO_MAX); /*set 1.8v*/
err = regulator_enable(fp_dev->vddio_ldo); /*enable regulator*/
//pmic_set_register_value(PMIC_RG_VCAMA_CAL,0x0A);
}
#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */
#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL
/* Power control by GPIOs */
if ( fp_dev->pin.pins_avdd_h ) {
err = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.pins_avdd_h);
}
if ( fp_dev->pin.pins_vddio_h ) {
err = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.pins_vddio_h);
}
#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */
#ifdef BSP_SIL_POWER_SUPPLY_GPIO
if ( fp_dev->avdd_port >= 0 ) {
err = gpio_direction_output(fp_dev->avdd_port, 1);
}
if ( fp_dev->vddio_port >= 0 ) {
err = gpio_direction_output(fp_dev->vddio_port, 1);
}
#endif /* BSP_SIL_POWER_SUPPLY_GPIO */
fp_dev->power_is_off = 0;
LOG_MSG_DEBUG(INFO_LOG, "%s: power supply ret:%d \n", __func__, err);
}
static void silfp_hw_poweroff(struct silfp_data *fp_dev)
{
LOG_MSG_DEBUG(INFO_LOG, "[%s] enter.\n", __func__);
#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR
/* Power control by Regulators(LDO) */
if ( fp_dev->avdd_ldo && (regulator_is_enabled(fp_dev->avdd_ldo) > 0)) {
regulator_disable(fp_dev->avdd_ldo); /*disable regulator*/
}
if ( fp_dev->vddio_ldo && (regulator_is_enabled(fp_dev->vddio_ldo) > 0)) {
regulator_disable(fp_dev->vddio_ldo); /*disable regulator*/
}
#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */
#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL
/* Power control by GPIOs */
//fp_dev->pin.pins_avdd_h = NULL;
//fp_dev->pin.pins_vddio_h = NULL;
#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */
#ifdef BSP_SIL_POWER_SUPPLY_GPIO
if ( fp_dev->avdd_port >= 0 ) {
gpio_direction_output(fp_dev->avdd_port, 0);
}
if ( fp_dev->vddio_port >= 0 ) {
gpio_direction_output(fp_dev->vddio_port, 0);
}
#endif /* BSP_SIL_POWER_SUPPLY_GPIO */
fp_dev->power_is_off = 1;
}
static void silfp_power_deinit(struct silfp_data *fp_dev)
{
LOG_MSG_DEBUG(INFO_LOG, "[%s] enter.\n", __func__);
#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR
/* Power control by Regulators(LDO) */
if ( fp_dev->avdd_ldo ) {
regulator_disable(fp_dev->avdd_ldo); /*disable regulator*/
regulator_put(fp_dev->avdd_ldo);
fp_dev->avdd_ldo = NULL;
}
if ( fp_dev->vddio_ldo ) {
regulator_disable(fp_dev->vddio_ldo); /*disable regulator*/
regulator_put(fp_dev->vddio_ldo);
fp_dev->vddio_ldo = NULL;
}
#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */
#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL
/* Power control by GPIOs */
fp_dev->pin.pins_avdd_h = NULL;
fp_dev->pin.pins_vddio_h = NULL;
if (fp_dev->pin.pinctrl) {
devm_pinctrl_put(fp_dev->pin.pinctrl);
}
#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */
#ifdef BSP_SIL_POWER_SUPPLY_GPIO
if ( fp_dev->avdd_port >= 0 ) {
gpio_direction_output(fp_dev->avdd_port, 0);
gpio_free(fp_dev->avdd_port);
fp_dev->avdd_port = -1;
}
if ( fp_dev->vddio_port >= 0 ) {
gpio_direction_output(fp_dev->vddio_port, 0);
gpio_free(fp_dev->vddio_port);
fp_dev->vddio_port = -1;
}
#endif /* BSP_SIL_POWER_SUPPLY_GPIO */
}
/* -------------------------------------------------------------------- */
/* hardware reset */
/* -------------------------------------------------------------------- */
static void silfp_hw_reset(struct silfp_data *fp_dev, u8 delay)
{
LOG_MSG_DEBUG(INFO_LOG, "[%s] enter, port=%d\n", __func__, fp_dev->rst_port);
if ( fp_dev->rst_port >= 0 ) {
gpio_direction_output(fp_dev->rst_port, 0);
if (fp_dev->irq_no_use && fp_dev->int_port >= 0) {
gpio_direction_output(fp_dev->int_port, 1);
}
mdelay((delay?delay:5)*RESET_TIME_MULTIPLE);
gpio_direction_output(fp_dev->rst_port, 1);
if (fp_dev->irq_no_use && fp_dev->int_port >= 0) {
gpio_direction_output(fp_dev->int_port, 0);
}
mdelay((delay?delay:3)*RESET_TIME_MULTIPLE);
}
}
/* -------------------------------------------------------------------- */
/* power down */
/* -------------------------------------------------------------------- */
static void silfp_pwdn(struct silfp_data *fp_dev, u8 flag_avdd)
{
LOG_MSG_DEBUG(INFO_LOG, "[%s] enter, port=%d\n", __func__, fp_dev->rst_port);
if (SIFP_PWDN_FLASH == flag_avdd) {
silfp_hw_poweroff(fp_dev);
msleep(200*RESET_TIME_MULTIPLE);
silfp_hw_poweron(fp_dev);
}
if ( fp_dev->rst_port >= 0 ) {
gpio_direction_output(fp_dev->rst_port, 0);
if (fp_dev->irq_no_use && fp_dev->int_port >= 0) {
gpio_direction_output(fp_dev->int_port, 1);
}
}
if (SIFP_PWDN_POWEROFF == flag_avdd) {
silfp_hw_poweroff(fp_dev);
}
}
/* -------------------------------------------------------------------- */
/* init/deinit functions */
/* -------------------------------------------------------------------- */
static int silfp_parse_dts(struct silfp_data* fp_dev)
{
int ret;
#ifndef QSEE_V4
/* Get the pinctrl node */
fp_dev->pin.pinctrl = devm_pinctrl_get(&fp_dev->spi->dev);
if (IS_ERR_OR_NULL(fp_dev->pin.pinctrl)) {
LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get pinctrl\n", __func__);
return PTR_ERR(fp_dev->pin.pinctrl);
}
/* Get the active setting */
fp_dev->pin.active = pinctrl_lookup_state(fp_dev->pin.pinctrl, SPI_PINCTRL_STATE_DEFAULT);
if (IS_ERR_OR_NULL(fp_dev->pin.active)) {
fp_dev->pin.active = NULL;
fp_dev->pin.pinctrl = NULL;
LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get pinctrl state active\n",__func__);
return PTR_ERR(fp_dev->pin.active);
}
/* Get power settings */
#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL
fp_dev->pin.pins_avdd_h = pinctrl_lookup_state(fp_dev->pin.pinctrl, "avdd-enable");
if (IS_ERR_OR_NULL(fp_dev->pin.pins_avdd_h)) {
fp_dev->pin.pins_avdd_h = NULL;
LOG_MSG_DEBUG(ERR_LOG, "%s can't find silfp avdd-enable\n", __func__);
// Ignore error
}
fp_dev->pin.pins_vddio_h = pinctrl_lookup_state(fp_dev->pin.pinctrl, "vddio-enable");
if (IS_ERR_OR_NULL(fp_dev->pin.pins_vddio_h)) {
fp_dev->pin.pins_vddio_h = NULL;
LOG_MSG_DEBUG(ERR_LOG, "%s can't find silfp vddio-enable\n", __func__);
// Ignore error
}
#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */
#endif /* QSEE_V4 */
#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR
// Todo: use correct settings.
fp_dev->avdd_ldo = regulator_get(&fp_dev->spi->dev, "avdd");
fp_dev->vddio_ldo= regulator_get(&fp_dev->spi->dev, "vddio");
#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */
#ifndef QSEE_V4
/* Get sleep settings */
fp_dev->pin.sleep = pinctrl_lookup_state(fp_dev->pin.pinctrl, SPI_PINCTRL_STATE_SLEEP);
if (IS_ERR_OR_NULL(fp_dev->pin.sleep)) {
fp_dev->pin.active = NULL;
fp_dev->pin.pinctrl = NULL;
LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get pinctrl state sleep\n",__func__);
return PTR_ERR(fp_dev->pin.sleep);
}
/* Get iface_clk info */
fp_dev->pin.iface_clk = clk_get(&fp_dev->spi->dev, "iface_clk");
if (IS_ERR(fp_dev->pin.iface_clk)) {
fp_dev->pin.active = NULL;
fp_dev->pin.pinctrl = NULL;
LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get iface_clk %ld\n", __func__, PTR_ERR(fp_dev->pin.iface_clk));
return PTR_ERR(fp_dev->pin.iface_clk);
}
/* Get core_clk info */
fp_dev->pin.core_clk = clk_get(&fp_dev->spi->dev, "core_clk");
if (IS_ERR(fp_dev->pin.core_clk)) {
fp_dev->pin.active = NULL;
fp_dev->pin.pinctrl = NULL;
LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get core_clk %p\n", __func__, fp_dev->pin.core_clk);
return PTR_ERR(fp_dev->pin.core_clk);
}
#else
/* Get the SPI Max speed */
ret = of_property_read_u32(fp_dev->spi->dev.of_node,"spi-max-frequency", &fp_dev->pin.max_speed_hz);
if (ret) {
fp_dev->pin.max_speed_hz = 0;
LOG_MSG_DEBUG(ERR_LOG, "Error getting spi max speed\n");
}
#endif /* QSEE_V4 */
/* Get the QUP ID (#1-12) */
ret = of_property_read_u32(fp_dev->spi->dev.of_node,"qcom,qup-id", &fp_dev->pin.qup_id);
if (ret) {
fp_dev->pin.qup_id = 0;
LOG_MSG_DEBUG(ERR_LOG, "Error getting qup_id\n");
}
LOG_MSG_DEBUG(INFO_LOG, "[%s] done (%d).\n",__func__,ret);
/*
Grab SPI master lock for exclusive access
call spi_bus_unlock to unlock the lock.
*/
//spi_bus_lock(fp_dev->spi->master);
return ret;
}
#ifndef QSEE_V4
static int spi_set_pinctrl(struct silfp_data* fp_dev, bool active)
{
int ret = -1;
if ( IS_ERR_OR_NULL(fp_dev->pin.pinctrl) || IS_ERR_OR_NULL(fp_dev->pin.active)) {
LOG_MSG_DEBUG(ERR_LOG, "%s: not support\n", __func__);
return ret;
}
if (active) { /* Change to active settings */
ret = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.active);
} else {
ret = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.sleep);
}
LOG_MSG_DEBUG(INFO_LOG, "%s: pinctrl_select_state ret:%d Setting:%d\n", __func__, ret, active);
return ret;
}
static int spi_set_clks(struct silfp_data* fp_dev, bool enable)
{
int ret = -1;
int clk = 19200000;
if ( IS_ERR_OR_NULL(fp_dev->pin.pinctrl) || IS_ERR_OR_NULL(fp_dev->pin.active)) {
LOG_MSG_DEBUG(INFO_LOG, "%s: not support\n", __func__);
return ret;
}
if (enable) {
/* Enable the spi clocks */
ret = clk_set_rate(fp_dev->pin.core_clk, fp_dev->spi->max_speed_hz);
if (ret) {
while ( clk > fp_dev->spi->max_speed_hz ) {
clk >>= 1;
}
if ( clk < 4800000 ) {
clk = 4800000;
}
ret = clk_set_rate(fp_dev->pin.core_clk, clk);
if ( ret ) {
LOG_MSG_DEBUG(ERR_LOG, "%s: Error setting clk_rate:%d, ret=%d\n",__func__, clk,ret);
} else {
fp_dev->spi->max_speed_hz = clk;
}
}
ret = clk_prepare_enable(fp_dev->pin.core_clk);
if (ret) {
LOG_MSG_DEBUG(ERR_LOG, "%s: Error enabling core clk, ret=%d\n",__func__,ret);
}
ret = clk_prepare_enable(fp_dev->pin.iface_clk);
if (ret) {
LOG_MSG_DEBUG(ERR_LOG, "%s: Error enabling iface clk, ret=%d\n",__func__,ret);
}
} else {
/* Disable the clocks */
clk_disable_unprepare(fp_dev->pin.iface_clk);
clk_disable_unprepare(fp_dev->pin.core_clk);
ret = 0;
}
LOG_MSG_DEBUG(DBG_LOG, "[%s] done (%d), speed = %d.\n",__func__,ret,fp_dev->spi->max_speed_hz );
return ret;
}
static int spi_set_fabric(struct silfp_data* fp_dev, bool active)
{
int ret;
struct spi_master *master = fp_dev->spi->master;
if (active) {
ret = master->prepare_transfer_hardware(master);
} else {
ret = master->unprepare_transfer_hardware(master);
}
LOG_MSG_DEBUG(DBG_LOG, "[%s] done (%d).\n",__func__,ret);
return ret;
}
#endif /* !QSEE_V4 */
static int silfp_set_spi(struct silfp_data *fp_dev, bool enable)
{
int ret = -ENOENT;
#ifndef QSEE_V4
if ( IS_ERR_OR_NULL(fp_dev->pin.pinctrl) || IS_ERR_OR_NULL(fp_dev->pin.active) || !fp_dev->pin.qup_id ) {
LOG_MSG_DEBUG(ERR_LOG, "%s: not support!\n", __func__);
return ret;
}
if ( enable && !atomic_read(&fp_dev->spionoff_count) ) {
atomic_inc(&fp_dev->spionoff_count);
ret = spi_set_pinctrl(fp_dev, enable);
ret |= spi_set_fabric(fp_dev, enable);
ret |= spi_set_clks(fp_dev, enable);
} else if (atomic_read(&fp_dev->spionoff_count)) {
atomic_dec(&fp_dev->spionoff_count);
//spi_change_pipe_owner(false);
ret = spi_set_clks(fp_dev, enable);
ret |= spi_set_fabric(fp_dev, enable);
ret |= spi_set_pinctrl(fp_dev, enable);
}
#else
LOG_MSG_DEBUG(ERR_LOG, "%s: qsee4 no needed!\n", __func__);
#endif /* !QSEE_V4 */
return ret;
}
static int silfp_irq_to_reset_init(struct silfp_data *fp_dev)
{
int ret = 0;
silfp_irq_disable(fp_dev);
free_irq(fp_dev->irq, fp_dev);
if (fp_dev->int_port >= 0) {
gpio_free(fp_dev->int_port);
ret = gpio_request(fp_dev->int_port, "SILFP_IRQ_TO_RST_PIN");
if (ret < 0) {
LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->int_port, ret);
return -ENODEV;
} else {
gpio_direction_output(fp_dev->int_port, 0);
}
}
fp_dev->irq_no_use = 1;
return ret;
}
static int silfp_set_feature(struct silfp_data *fp_dev, u8 feature)
{
int ret = 0;
switch (feature) {
case FEATURE_FLASH_CS:
LOG_MSG_DEBUG(INFO_LOG, "%s set feature flash cs\n", __func__);
ret = silfp_irq_to_reset_init(fp_dev);
break;
default:
break;
}
return ret;
}
static int silfp_resource_init(struct silfp_data *fp_dev, struct fp_dev_init_t *dev_info)
{
int status = 0;
int ret;
LOG_MSG_DEBUG(ERR_LOG, "[%s] enter.\n", __func__);
printk(KERN_INFO "silfp_resource_init enter!\n");
if (atomic_read(&fp_dev->init)) {
atomic_inc(&fp_dev->init);
LOG_MSG_DEBUG(DBG_LOG, "[%s] dev already init(%d).\n",__func__,atomic_read(&fp_dev->init));
return status;
}
fp_dev->int_port = -1;
fp_dev->rst_port = -1;
silfp_parse_dts(fp_dev);
#ifdef BSP_SIL_POWER_SUPPLY_GPIO
fp_dev->avdd_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "avdd-gpios", 0);
fp_dev->vddio_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "vddio-gpios", 0);
if (fp_dev->avdd_port >= 0 ) {
gpio_free(fp_dev->avdd_port);
ret = gpio_request(fp_dev->avdd_port, "SILFP_AVDD_PIN");
if (ret < 0) {
LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->avdd_port, ret);
status = -ENODEV;
goto err_avdd;
}
}
if (fp_dev->vddio_port >= 0 ) {
gpio_free(fp_dev->vddio_port);
ret = gpio_request(fp_dev->vddio_port, "SILFP_VDDIO_PIN");
if (ret < 0) {
LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->vddio_port, ret);
status = -ENODEV;
//goto err_vddio;
}
}
#endif /* BSP_SIL_POWER_SUPPLY_GPIO */
silfp_hw_poweron(fp_dev);
fp_dev->int_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "irq-gpios", 0);
fp_dev->rst_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "rst-gpios", 0);
LOG_MSG_DEBUG(INFO_LOG, "[%s] int_port %d, rst_port %d.\n",__func__,fp_dev->int_port,fp_dev->rst_port);
if (fp_dev->int_port >= 0 ) {
gpio_free(fp_dev->int_port);
}
if (fp_dev->rst_port >= 0 ) {
gpio_free(fp_dev->rst_port);
}
ret = gpio_request(fp_dev->int_port, "SILFP_INT_IRQ");
if (ret < 0) {
LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->int_port, ret);
status = -ENODEV;
goto err;
} else {
gpio_direction_input(fp_dev->int_port);
fp_dev->irq = gpio_to_irq(fp_dev->int_port);
fp_dev->irq_is_disable = 0;
ret = request_irq(fp_dev->irq,
silfp_irq_handler,
IRQ_TYPE_EDGE_RISING, //IRQ_TYPE_LEVEL_HIGH, //irq_table[ts->int_trigger_type],
"silfp",
fp_dev);
if ( ret < 0 ) {
LOG_MSG_DEBUG(ERR_LOG, "[%s] Filed to request_irq (%d), ert=%d",__func__,fp_dev->irq, ret);
status = -ENODEV;
goto err_irq;
} else {
LOG_MSG_DEBUG(INFO_LOG,"[%s] Enable_irq_wake.\n",__func__);
enable_irq_wake(fp_dev->irq);
silfp_irq_disable(fp_dev);
}
}
if (fp_dev->rst_port >= 0 ) {
ret = gpio_request(fp_dev->rst_port, "SILFP_RST_PIN");
if (ret < 0) {
LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->rst_port, ret);
status = -ENODEV;
goto err_rst;
} else {
gpio_direction_output(fp_dev->rst_port, 1);
}
}
if (!ret) {
if (silfp_input_init(fp_dev)) {
goto err_input;
}
atomic_set(&fp_dev->init,1);
}
fp_dev->irq_no_use = 0;
dev_info->reserve = PKG_SIZE;
dev_info->reserve <<= 12;
#ifdef QSEE_V4
if (fp_dev->pin.max_speed_hz) {
dev_info->speed = fp_dev->pin.max_speed_hz;
}
#endif /* QSEE_V4 */
if (dev_info && fp_dev->pin.qup_id && (fp_dev->pin.qup_id <= 32)) {
dev_info->dev_id = fp_dev->pin.qup_id;
strncpy(dev_info->ta,TANAME,sizeof(dev_info->ta));
}
return status;
err_input:
if (fp_dev->rst_port >= 0 ) {
gpio_free(fp_dev->rst_port);
}
err_rst:
free_irq(fp_dev->irq, fp_dev);
gpio_direction_input(fp_dev->int_port);
err_irq:
gpio_free(fp_dev->int_port);
#ifdef BSP_SIL_POWER_SUPPLY_GPIO
gpio_free(fp_dev->vddio_port);
#if 0
err_vddio:
gpio_free(fp_dev->avdd_port);
#endif
err_avdd:
fp_dev->avdd_port = -1;
fp_dev->vddio_port = -1;
#endif /* BSP_SIL_POWER_SUPPLY_GPIO */
err:
fp_dev->int_port = -1;
fp_dev->rst_port = -1;
return status;
}
#endif /* __SILEAD_FP_QCOM__ */
#endif /* BSP_SIL_PLAT_QCOM */
/* End of file spilead_fp_qcom.c */

View file

@ -0,0 +1,52 @@
/*
* @file silead_fp_qcom.h
* @brief Contains silead_fp Qualcomm platform specific head file.
*
*
* Copyright 2016-2021 Gigadevice/Silead Inc.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ------------------- Revision History ------------------------------
* <author> <date> <version> <desc>
* Bill Yu 2018/5/2 0.1.0 Init version
*
*/
#ifndef __SILEAD_FP_QCOM_H__
#define __SILEAD_FP_QCOM_H__
#include <linux/pinctrl/consumer.h>
#include <linux/clk.h>
struct fp_plat_t {
u32 qup_id;
#ifdef QSEE_V4
u32 max_speed_hz;
#else
/* pinctrl info */
struct pinctrl *pinctrl;
struct pinctrl_state *active;
struct pinctrl_state *sleep;
#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL
struct pinctrl_state *pins_avdd_h, *pins_vddio_h;
#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */
/* clock info */
struct clk *core_clk;
struct clk *iface_clk;
#endif /* QSEE_V4 */
};
#endif /* __SILEAD_FP_QCOM_H__ */
/* End of file silead_fp_qcom.h */