/* * Copyright (C) 2018, Samsung Electronics Co. Ltd. All Rights Reserved. * * 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. * */ #include "fingerprint.h" #include "gw9558x_common.h" static LIST_HEAD(device_list); static DEFINE_MUTEX(device_list_lock); static const struct of_device_id gw9558_of_match[] = { { .compatible = "goodix,gw9558x", }, {}, }; MODULE_DEVICE_TABLE(of, gw9558_of_match); struct debug_logger *g_logger; static ssize_t bfs_values_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gf_device *gf_dev = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "\"FP_SPICLK\":\"%d\"\n", gf_dev->clk_setting->spi_speed); } static ssize_t type_check_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gf_device *gf_dev = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%d\n", gf_dev->sensortype); } static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%s\n", "GOODIX"); } static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gf_device *gf_dev = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%s\n", gf_dev->chipid); } static ssize_t adm_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", DETECT_ADM); } static ssize_t resetcnt_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gf_device *gf_dev = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%d\n", gf_dev->reset_count); } static ssize_t resetcnt_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gf_device *gf_dev = dev_get_drvdata(dev); if (sysfs_streq(buf, "c")) { gf_dev->reset_count = 0; pr_info("initialization is done\n"); } return size; } static ssize_t position_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gf_device *gf_dev = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%s\n", gf_dev->sensor_position); } static ssize_t rb_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gf_device *gf_dev = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%s\n", gf_dev->rb); } static DEVICE_ATTR_RO(bfs_values); static DEVICE_ATTR_RO(type_check); static DEVICE_ATTR_RO(vendor); static DEVICE_ATTR_RO(name); static DEVICE_ATTR_RO(adm); static DEVICE_ATTR_RW(resetcnt); static DEVICE_ATTR_RO(position); static DEVICE_ATTR_RO(rb); static struct device_attribute *fp_attrs[] = { &dev_attr_bfs_values, &dev_attr_type_check, &dev_attr_vendor, &dev_attr_name, &dev_attr_adm, &dev_attr_resetcnt, &dev_attr_position, &dev_attr_rb, NULL, }; static ssize_t gw9558_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { return -EFAULT; } static ssize_t gw9558_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { return -EFAULT; } static long gw9558_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct gf_device *gf_dev = NULL; unsigned int onoff = 0; int retval = 0; u8 temp = 0; if (_IOC_TYPE(cmd) != GF_IOC_MAGIC) return -EINVAL; if (!filp || !filp->private_data) { pr_err("NULL pointer passed\n"); return -EINVAL; } if (IS_ERR((void __user *)arg)) { pr_err("invalid user space pointer %lu\n", arg); return -EINVAL; } gf_dev = (struct gf_device *)filp->private_data; switch (cmd) { case GF_IOC_INIT: pr_debug("GF_IOC_INIT\n"); if (copy_to_user((void __user *)arg, (void *)&temp, sizeof(temp))) retval = -EFAULT; break; case GF_IOC_RESET: pr_info("GF_IOC_RESET\n"); gw9558_hw_reset(gf_dev, 0); break; case GF_IOC_ENABLE_SPI_CLK: if (copy_from_user(&onoff, (void __user *)arg, sizeof(onoff))) { pr_err("Failed to copy spi_speed value from user to kernel\n"); } pr_info("GF_IOC_ENABLE_SPI_CLK : %d, fromUser : %d\n", gf_dev->clk_setting->spi_speed, onoff); spi_clk_enable(gf_dev->clk_setting); break; case GF_IOC_DISABLE_SPI_CLK: pr_info("GF_IOC_DISABLE_SPI_CLK\n"); spi_clk_disable(gf_dev->clk_setting); break; case GF_IOC_ENABLE_POWER: pr_info("GF_IOC_ENABLE_POWER\n"); gw9558_hw_power_enable(gf_dev, 1); break; case GF_IOC_DISABLE_POWER: pr_info("GF_IOC_DISABLE_POWER\n"); gw9558_hw_power_enable(gf_dev, 0); break; case GF_IOC_POWER_CONTROL: if (copy_from_user(&onoff, (void __user *)arg, sizeof(onoff))) { pr_err("Failed to copy onoff value from user to kernel\n"); retval = -EFAULT; break; } pr_info("GF_IOC_POWER_CONTROL %d\n", onoff); gw9558_hw_power_enable(gf_dev, onoff); break; case GF_IOC_GET_FW_INFO: temp = 1; pr_debug("GET_FW_INFO : 0x%x\n", temp); if (copy_to_user((void __user *)arg, (void *)&temp, sizeof(temp))) { pr_err("Failed to copy data to user\n"); retval = -EFAULT; } break; #ifndef ENABLE_SENSORS_FPRINT_SECURE case GF_IOC_TRANSFER_RAW_CMD: mutex_lock(&gf_dev->buf_lock); retval = gw9558_ioctl_transfer_raw_cmd(gf_dev, arg, (unsigned int)TANSFER_MAX_LEN); mutex_unlock(&gf_dev->buf_lock); break; #endif /* !ENABLE_SENSORS_FPRINT_SECURE */ #ifdef ENABLE_SENSORS_FPRINT_SECURE case GF_IOC_SET_SENSOR_TYPE: if (copy_from_user(&onoff, (void __user *)arg, sizeof(onoff)) != 0) { pr_err("Failed to copy sensor type from user to kernel\n"); return -EFAULT; } set_sensor_type((int)onoff, &gf_dev->sensortype); break; #endif case GF_IOC_SPEEDUP: if (copy_from_user(&onoff, (void __user *)arg, sizeof(onoff)) != 0) { pr_err("Failed to copy speedup from user to kernel\n"); return -EFAULT; } if (onoff) retval = cpu_speedup_enable(gf_dev->boosting); else retval = cpu_speedup_disable(gf_dev->boosting); break; case GF_MODEL_INFO: pr_info("GF_MODEL_INFO : %s\n", gf_dev->model_info); if (copy_to_user((void __user *)arg, gf_dev->model_info, 10)) { pr_err("Failed to copy data to user\n"); retval = -EFAULT; } break; case GF_IOC_RESERVED01: case GF_IOC_RESERVED02: case GF_IOC_RESERVED03: case GF_IOC_RESERVED04: case GF_IOC_RESERVED05: case GF_IOC_RESERVED06: case GF_IOC_RESERVED07: break; default: pr_err("doesn't support this command(%x)\n", cmd); break; } return retval; } #ifdef CONFIG_COMPAT static long gw9558_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int retval = 0; retval = filp->f_op->unlocked_ioctl(filp, cmd, arg); return retval; } #endif static int gw9558_open(struct inode *inode, struct file *filp) { struct gf_device *gf_dev = NULL; int retval = -ENXIO; pr_info("Entry\n"); mutex_lock(&device_list_lock); list_for_each_entry(gf_dev, &device_list, device_entry) { if (gf_dev->devno == inode->i_rdev) { pr_info("Found\n"); retval = 0; break; } } mutex_unlock(&device_list_lock); if (retval == 0) { filp->private_data = gf_dev; nonseekable_open(inode, filp); pr_info("Success to open device\n"); } else { pr_err("No device for minor %d\n", iminor(inode)); } return retval; } static int gw9558_release(struct inode *inode, struct file *filp) { struct gf_device *gf_dev = NULL; pr_info("Entry\n"); gf_dev = filp->private_data; return 0; } static const struct file_operations gw9558_fops = { .owner = THIS_MODULE, .write = gw9558_write, .read = gw9558_read, .unlocked_ioctl = gw9558_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = gw9558_compat_ioctl, #endif .open = gw9558_open, .release = gw9558_release, }; static void gw9558_work_func_debug(struct work_struct *work) { u8 rst_value = -1; struct debug_logger *logger; struct gf_device *gf_dev; logger = container_of(work, struct debug_logger, work_debug); gf_dev = dev_get_drvdata(logger->dev); if (gf_dev->reset_gpio) rst_value = gpio_get_value(gf_dev->reset_gpio); pr_info("ldo: %d, sleep: %d, tz: %d type: %s\n", gf_dev->ldo_onoff, rst_value, gf_dev->tz_mode, gf_dev->sensortype > 0 ? gf_dev->chipid : sensor_status[gf_dev->sensortype + 2]); } int gw9558_pin_control(struct gf_device *gf_dev, bool pin_set) { int retval = 0; #ifndef ENABLE_SENSORS_FPRINT_SECURE if (pin_set) { if (!IS_ERR(gf_dev->pins_poweron)) { retval = pinctrl_select_state(gf_dev->p, gf_dev->pins_poweron); if (retval) pr_err("can't set pin wakeup state\n"); pr_debug("idle\n"); } } else { if (!IS_ERR(gf_dev->pins_poweroff)) { retval = pinctrl_select_state(gf_dev->p, gf_dev->pins_poweroff); if (retval) pr_err("can't set pin sleep state\n"); pr_debug("sleep\n"); } } #endif return retval; } void gw9558_hw_power_enable(struct gf_device *gf_dev, u8 onoff) { int retval = 0; if (onoff && !gf_dev->ldo_onoff) { gw9558_pin_control(gf_dev, 1); if (gf_dev->pwr_gpio) { gpio_set_value(gf_dev->pwr_gpio, 1); } else if (gf_dev->regulator_3p3 != NULL) { retval = regulator_enable(gf_dev->regulator_3p3); if (retval) { pr_err("regulator enable failed, rc=%d\n", retval); goto regulator_failed; } } if (gf_dev->reset_gpio) { usleep_range(11000, 11050); gpio_set_value(gf_dev->reset_gpio, 1); } gf_dev->ldo_onoff = 1; } else if (!onoff && gf_dev->ldo_onoff) { if (gf_dev->reset_gpio) { gpio_set_value(gf_dev->reset_gpio, 0); usleep_range(11000, 11050); } if (gf_dev->pwr_gpio) { gpio_set_value(gf_dev->pwr_gpio, 0); } else if (gf_dev->regulator_3p3 != NULL) { retval = regulator_disable(gf_dev->regulator_3p3); if (retval) { pr_err("regulator disable failed, rc=%d\n", retval); if (gf_dev->reset_gpio) gpio_set_value(gf_dev->reset_gpio, 1); goto regulator_failed; } } gf_dev->ldo_onoff = 0; gw9558_pin_control(gf_dev, 0); } else if (onoff == 0 || onoff == 1) { pr_err("power is already %s\n", (gf_dev->ldo_onoff ? "Enabled" : "Disabled")); } else { pr_err("can't support this value:%d\n", onoff); } pr_debug("status = %d\n", gf_dev->ldo_onoff); regulator_failed: return; } void gw9558_hw_reset(struct gf_device *gf_dev, u8 delay) { if (gf_dev->reset_gpio) { gpio_set_value(gf_dev->reset_gpio, 0); usleep_range(3000, 3050); gpio_set_value(gf_dev->reset_gpio, 1); usleep_range((delay * 1000), (delay * 1000) + 50); gf_dev->reset_count++; } } /* GPIO pins reference */ int gw9558_get_gpio_dts_info(struct device *dev, struct gf_device *gf_dev) { struct device_node *np = dev->of_node; int retval = 0; /* get pwr resource */ gf_dev->pwr_gpio = of_get_named_gpio(np, "goodix,gpio_pwr", 0); if (!gpio_is_valid(gf_dev->pwr_gpio)) { pr_info("not use PWR GPIO\n"); gf_dev->pwr_gpio = 0; } else { pr_info("goodix_pwr:%d\n", gf_dev->pwr_gpio); retval = gpio_request(gf_dev->pwr_gpio, "goodix_pwr"); if (retval < 0) { pr_err("Failed to request PWR GPIO = %d\n", retval); return retval; } gpio_direction_output(gf_dev->pwr_gpio, 0); } if (of_property_read_string(np, "goodix,btp-regulator", &gf_dev->btp_vdd) < 0) { pr_info("not use btp_regulator\n"); gf_dev->btp_vdd = NULL; } else { gf_dev->regulator_3p3 = regulator_get(NULL, gf_dev->btp_vdd); if ((gf_dev->regulator_3p3) == NULL) { pr_info("fail to get regulator_3p3\n"); return -EINVAL; } else if (IS_ERR(gf_dev->regulator_3p3)) { pr_info("fail to get regulator_3p3: %ld", PTR_ERR(gf_dev->regulator_3p3)); return PTR_ERR(gf_dev->regulator_3p3); } pr_info("btp_regulator ok\n"); } /* get reset resource */ gf_dev->reset_gpio = of_get_named_gpio(np, "goodix,gpio_reset", 0); if (!gpio_is_valid(gf_dev->reset_gpio)) { pr_err("RESET GPIO is invalid.\n"); return gf_dev->reset_gpio; } pr_info("goodix_reset:%d\n", gf_dev->reset_gpio); retval = gpio_request(gf_dev->reset_gpio, "goodix_reset"); if (retval < 0) { pr_err("Failed to request RESET GPIO = %d\n", retval); return retval; } gpio_direction_output(gf_dev->reset_gpio, 0); if (of_property_read_u32(np, "goodix,min_cpufreq_limit", &gf_dev->boosting->min_cpufreq_limit)) gf_dev->boosting->min_cpufreq_limit = 0; if (of_property_read_string_index(np, "goodix,chip_id", 0, (const char **)&gf_dev->chipid)) gf_dev->chipid = "NULL"; pr_info("Chip ID:%s\n", gf_dev->chipid); if (of_property_read_string_index(np, "goodix,position", 0, (const char **)&gf_dev->sensor_position)) { gf_dev->sensor_position = "14.31,0.00,9.10,9.10,14.80,14.80,12.00,12.00,5.00"; } pr_info("position: %s\n", gf_dev->sensor_position); if (of_property_read_string_index(np, "goodix,rb", 0, (const char **)&gf_dev->rb)) { gf_dev->rb = "525,-1,-1"; } pr_info("rb: %s\n", gf_dev->rb); if (of_property_read_string_index(np, "goodix,modelinfo", 0, (const char **)&gf_dev->model_info)) { gf_dev->model_info = "NONE"; } pr_info("modelinfo: %s\n", gf_dev->model_info); gf_dev->p = pinctrl_get_select_default(dev); if (IS_ERR(gf_dev->p)) { pr_err("failed pinctrl_get\n"); return PTR_ERR(gf_dev->p); } #ifndef ENABLE_SENSORS_FPRINT_SECURE gf_dev->pins_poweroff = pinctrl_lookup_state(gf_dev->p, "pins_poweroff"); if (IS_ERR(gf_dev->pins_poweroff)) { pr_err("could not get pins sleep_state (%li)\n", PTR_ERR(gf_dev->pins_poweroff)); } gf_dev->pins_poweron = pinctrl_lookup_state(gf_dev->p, "pins_poweron"); if (IS_ERR(gf_dev->pins_poweron)) { pr_err("could not get pins idle_state (%li)\n", PTR_ERR(gf_dev->pins_poweron)); } #endif return retval; } void gw9558_cleanup_info(struct gf_device *gf_dev) { if (gpio_is_valid(gf_dev->reset_gpio)) { gpio_free(gf_dev->reset_gpio); pr_debug("remove reset_gpio\n"); } if (gpio_is_valid(gf_dev->pwr_gpio)) { gpio_free(gf_dev->pwr_gpio); pr_debug("remove pwr_gpio\n"); } if (gf_dev->regulator_3p3) { regulator_put(gf_dev->regulator_3p3); pr_debug("remove regulator\n"); } } #ifndef ENABLE_SENSORS_FPRINT_SECURE int gw9558_type_check(struct gf_device *gf_dev) { int retval = -ENODEV; unsigned char mcuid[2] = {0x00, 0x00}; u32 mcuid32 = 0; #if defined(CONFIG_SENSORS_FINGERPRINT_NORMALSPI) unsigned char wake_wp_0[4] = {0x00, 0x01, 0x00, 0x00}; unsigned char spi_mode[4] = {0x00, 0x01, 0x00, 0x11}; #endif gw9558_hw_power_enable(gf_dev, 1); usleep_range(4950, 5000); #if defined(CONFIG_SENSORS_FINGERPRINT_NORMALSPI) gw9558_spi_write_bytes(gf_dev, 0xe500, 4, wake_wp_0); usleep_range(1000, 1050); gw9558_spi_write_bytes(gf_dev, 0x0022, 4, spi_mode); usleep_range(1000, 1050); #endif gw9558_spi_read_bytes(gf_dev, 0x0000, 2, mcuid); mcuid32 = mcuid[0] << 8 | mcuid[1]; pr_info("Sensor read : %x %x\n", mcuid[0], mcuid[1]); pr_info("Sensor read : 0x%4x\n", mcuid32); if (mcuid32 == G3_MCU_ID) { gf_dev->sensortype = SENSOR_OK; pr_info("sensor type is G3 %s\n", gf_dev->chipid); retval = 0; } else if (mcuid32 == GX_MCU_ID) { gf_dev->sensortype = SENSOR_OK; pr_info("sensor type is GX %s\n", gf_dev->chipid); retval = 0; } else if (mcuid32 == G3S_MCU_ID) { gf_dev->sensortype = SENSOR_OK; pr_info("sensor type is G3S %s\n", gf_dev->chipid); retval = 0; } else { gf_dev->sensortype = SENSOR_FAILED; pr_err("sensor type is FAILED 0x%4x\n", mcuid32); } gw9558_hw_power_enable(gf_dev, 0); return retval; } #endif static struct gf_device *alloc_platformdata(struct device *dev) { struct gf_device *gf_dev; gf_dev = devm_kzalloc(dev, sizeof(struct gf_device), GFP_KERNEL); if (gf_dev == NULL) return NULL; gf_dev->clk_setting = devm_kzalloc(dev, sizeof(*gf_dev->clk_setting), GFP_KERNEL); if (gf_dev->clk_setting == NULL) return NULL; gf_dev->boosting = devm_kzalloc(dev, sizeof(*gf_dev->boosting), GFP_KERNEL); if (gf_dev->boosting == NULL) return NULL; gf_dev->logger = devm_kzalloc(dev, sizeof(*gf_dev->logger), GFP_KERNEL); if (gf_dev->logger == NULL) return NULL; return gf_dev; } static int gw9558_probe_common(struct device *dev, struct gf_device *gf_dev) { int retval = -EINVAL; #ifndef ENABLE_SENSORS_FPRINT_SECURE int retry = 0; #endif pr_info("Entry\n"); spin_lock_init(&gf_dev->spi_lock); INIT_LIST_HEAD(&gf_dev->device_entry); /* Initialize the driver data */ gf_dev->device_count = 0; gf_dev->ldo_onoff = 0; gf_dev->reset_count = 0; gf_dev->dev = dev; gf_dev->logger->dev = dev; dev_set_drvdata(dev, gf_dev); /* get gpio info from dts or defination */ retval = gw9558_get_gpio_dts_info(dev, gf_dev); if (retval < 0) { pr_err("Failed to get gpio info:%d\n", retval); goto gw9558_probe_get_gpio; } #ifndef ENABLE_SENSORS_FPRINT_SECURE gw9558_spi_setup_conf(gf_dev, 1); #endif /* set AP spectific configuration */ retval = spi_clk_register(gf_dev->clk_setting, dev); if (retval < 0) { pr_err("Failed to register spi clk:%d\n", retval); goto gw9558_probe_spi_clk_register; } /* create class */ gf_dev->class = class_create(THIS_MODULE, GF_CLASS_NAME); if (IS_ERR(gf_dev->class)) { pr_err("Failed to create class.\n"); retval = -ENODEV; goto gw9558_probe_class_create; } /* get device no */ if (GF_DEV_MAJOR > 0) { gf_dev->devno = MKDEV(GF_DEV_MAJOR, gf_dev->device_count++); retval = register_chrdev_region(gf_dev->devno, 1, GF_DEV_NAME); } else { retval = alloc_chrdev_region(&gf_dev->devno, gf_dev->device_count++, 1, GF_DEV_NAME); } if (retval < 0) { pr_err("Failed to alloc devno.\n"); goto gw9558_probe_devno; } else { pr_info("major=%d, minor=%d\n", MAJOR(gf_dev->devno), MINOR(gf_dev->devno)); } /* create device */ gf_dev->fp_device = device_create(gf_dev->class, dev, gf_dev->devno, gf_dev, GF_DEV_NAME); if (IS_ERR(gf_dev->fp_device)) { pr_err("Failed to create device.\n"); retval = -ENODEV; goto gw9558_probe_device; } else { mutex_lock(&device_list_lock); list_add(&gf_dev->device_entry, &device_list); mutex_unlock(&device_list_lock); pr_info("device create success.\n"); } #ifndef ENABLE_SENSORS_FPRINT_SECURE do { retval = gw9558_type_check(gf_dev); pr_info("type (%u), retry (%d)\n", gf_dev->sensortype, retry); } while (!gf_dev->sensortype && ++retry < 3); if (retval == -ENODEV) pr_err("type_check failed\n"); #endif /* create sysfs */ retval = fingerprint_register(gf_dev->fp_device, gf_dev, fp_attrs, "fingerprint"); if (retval) { pr_err("sysfs register failed\n"); goto gw9558_probe_sysfs; } /* cdev init and add */ cdev_init(&gf_dev->cdev, &gw9558_fops); gf_dev->cdev.owner = THIS_MODULE; retval = cdev_add(&gf_dev->cdev, gf_dev->devno, 1); if (retval) { pr_err("Failed to add cdev.\n"); goto gw9558_probe_cdev; } #if KERNEL_VERSION(4, 19, 188) > LINUX_VERSION_CODE /* 4.19 R */ wakeup_source_init(gf_dev->clk_setting->spi_wake_lock, "gw9558_wake_lock"); /* 4.19 Q */ if (!(gf_dev->clk_setting->spi_wake_lock)) { gf_dev->clk_setting->spi_wake_lock = wakeup_source_create("gw9558_wake_lock"); if (gf_dev->clk_setting->spi_wake_lock) wakeup_source_add(gf_dev->clk_setting->spi_wake_lock); } #else /* 5.4 R */ gf_dev->clk_setting->spi_wake_lock = wakeup_source_register(gf_dev->dev, "gw9558_wake_lock"); #endif g_logger = gf_dev->logger; retval = set_fp_debug_timer(gf_dev->logger, gw9558_work_func_debug); if (retval) goto gw9558_probe_debug_timer; enable_fp_debug_timer(gf_dev->logger); pr_info("probe finished\n"); return 0; gw9558_probe_debug_timer: cdev_del(&gf_dev->cdev); gw9558_probe_cdev: fingerprint_unregister(gf_dev->fp_device, fp_attrs); gw9558_probe_sysfs: device_destroy(gf_dev->class, gf_dev->devno); list_del(&gf_dev->device_entry); gw9558_probe_device: unregister_chrdev_region(gf_dev->devno, 1); gw9558_probe_devno: class_destroy(gf_dev->class); gw9558_probe_class_create: spi_clk_unregister(gf_dev->clk_setting); gw9558_probe_spi_clk_register: gw9558_probe_get_gpio: pr_err("failed. %d", retval); return retval; } #ifdef ENABLE_SENSORS_FPRINT_SECURE static int gw9558_probe(struct platform_device *pdev) { int retval = -ENOMEM; struct gf_device *gf_dev; pr_info("platform_driver Entry\n"); gf_dev = alloc_platformdata(&pdev->dev); if (gf_dev == NULL) goto gw9558_platform_alloc_failed; gf_dev->clk_setting->enabled_clk = false; gf_dev->tz_mode = true; gf_dev->sensortype = SENSOR_UNKNOWN; gf_dev->clk_setting->spi_speed = (unsigned int)GW9558_SPI_BAUD_RATE; retval = gw9558_probe_common(&pdev->dev, gf_dev); if (retval) goto gw9558_platform_probe_failed; pr_info("is successful\n"); return retval; gw9558_platform_probe_failed: gf_dev = NULL; gw9558_platform_alloc_failed: pr_err("is failed : %d\n", retval); return retval; } #else static int gw9558_probe(struct spi_device *spi) { int retval = -ENOMEM; struct gf_device *gf_dev = NULL; pr_info("spi_driver Entry\n"); gf_dev = alloc_platformdata(&spi->dev); if (gf_dev == NULL) goto gw9558_spi_probe_alloc_failed; spi->mode = SPI_MODE_0; spi->max_speed_hz = (unsigned int)GW9558_SPI_BAUD_RATE; spi->bits_per_word = 8; gf_dev->clk_setting->spi_speed = (unsigned int)GW9558_SPI_BAUD_RATE; gf_dev->prev_bits_per_word = 8; gf_dev->tz_mode = false; gf_dev->spi = spi; mutex_init(&gf_dev->buf_lock); if (spi_setup(gf_dev->spi)) { pr_err("failed to setup spi conf\n"); goto gw9558_spi_spi_setup_failed; } else { pr_info("setup spi success.\n"); } /* init transfer buffer */ retval = gw9558_init_buffer(gf_dev); if (retval < 0) { pr_err("Failed to Init transfer buffer.\n"); goto gw9558_spi_init_buffer_failed; } retval = gw9558_probe_common(&spi->dev, gf_dev); if (retval) goto gw9558_spi_probe_failed; pr_info("is successful\n"); return retval; gw9558_spi_probe_failed: gw9558_free_buffer(gf_dev); gw9558_spi_init_buffer_failed: gw9558_spi_spi_setup_failed: mutex_destroy(&gf_dev->buf_lock); gf_dev = NULL; gw9558_spi_probe_alloc_failed: pr_err("is failed : %d\n", retval); return retval; } #endif static int gw9558_remove_common(struct device *dev) { struct gf_device *gf_dev = dev_get_drvdata(dev); pr_info("Entry\n"); gw9558_hw_power_enable(gf_dev, 0); spi_clk_unregister(gf_dev->clk_setting); disable_fp_debug_timer(gf_dev->logger); wakeup_source_unregister(gf_dev->clk_setting->spi_wake_lock); gw9558_cleanup_info(gf_dev); fingerprint_unregister(gf_dev->fp_device, fp_attrs); cdev_del(&gf_dev->cdev); device_destroy(gf_dev->class, gf_dev->devno); list_del(&gf_dev->device_entry); unregister_chrdev_region(gf_dev->devno, 1); class_destroy(gf_dev->class); return 0; } #ifdef ENABLE_SENSORS_FPRINT_SECURE static int gw9558_remove(struct platform_device *pdev) { struct gf_device *gf_dev = platform_get_drvdata(pdev); gw9558_remove_common(&pdev->dev); gf_dev = NULL; return 0; } #else #if LINUX_VERSION_CODE > KERNEL_VERSION(6, 1, 0) static void gw9558_remove(struct spi_device *spi) { struct gf_device *gf_dev = spi_get_drvdata(spi); gw9558_free_buffer(gf_dev); gw9558_remove_common(&spi->dev); mutex_destroy(&gf_dev->buf_lock); spin_lock_irq(&gf_dev->spi_lock); gf_dev->spi = NULL; spin_unlock_irq(&gf_dev->spi_lock); gf_dev = NULL; } #else static int gw9558_remove(struct spi_device *spi) { struct gf_device *gf_dev = spi_get_drvdata(spi); gw9558_free_buffer(gf_dev); gw9558_remove_common(&spi->dev); mutex_destroy(&gf_dev->buf_lock); spin_lock_irq(&gf_dev->spi_lock); gf_dev->spi = NULL; spin_unlock_irq(&gf_dev->spi_lock); gf_dev = NULL; return 0; } #endif #endif static int gw9558_pm_suspend(struct device *dev) { struct gf_device *gf_dev = dev_get_drvdata(dev); pr_info("Entry\n"); disable_fp_debug_timer(gf_dev->logger); return 0; } static int gw9558_pm_resume(struct device *dev) { struct gf_device *gf_dev = dev_get_drvdata(dev); pr_info("Entry\n"); enable_fp_debug_timer(gf_dev->logger); return 0; } static const struct dev_pm_ops gw9558_pm_ops = { .suspend = gw9558_pm_suspend, .resume = gw9558_pm_resume }; #ifndef ENABLE_SENSORS_FPRINT_SECURE static struct spi_driver gw9558_spi_driver = { #else static struct platform_driver gw9558_spi_driver = { #endif .driver = { .name = GF_DEV_NAME, #ifndef ENABLE_SENSORS_FPRINT_SECURE .bus = &spi_bus_type, #endif .owner = THIS_MODULE, .pm = &gw9558_pm_ops, .of_match_table = gw9558_of_match, }, .probe = gw9558_probe, .remove = gw9558_remove, }; static int __init gw9558_init(void) { int retval = 0; pr_info("Entry\n"); #ifndef ENABLE_SENSORS_FPRINT_SECURE retval = spi_register_driver(&gw9558_spi_driver); #else retval = platform_driver_register(&gw9558_spi_driver); #endif if (retval < 0) { pr_err("Failed to register SPI driver.\n"); return -EINVAL; } return retval; } module_init(gw9558_init); static void __exit gw9558_exit(void) { pr_info("Entry\n"); #ifndef ENABLE_SENSORS_FPRINT_SECURE spi_unregister_driver(&gw9558_spi_driver); #else platform_driver_unregister(&gw9558_spi_driver); #endif } module_exit(gw9558_exit); MODULE_AUTHOR("fp.sec@samsung.com"); MODULE_DESCRIPTION("Samsung Electronics Inc. GW9558 driver"); MODULE_LICENSE("GPL");