diff options
Diffstat (limited to 'drivers/input')
94 files changed, 60505 insertions, 10 deletions
diff --git a/drivers/input/input.c b/drivers/input/input.c index 880605959aa6..baaddd168804 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1667,8 +1667,14 @@ void input_reset_device(struct input_dev *dev) mutex_lock(&dev->mutex); spin_lock_irqsave(&dev->event_lock, flags); - input_dev_toggle(dev, true); - input_dev_release_keys(dev); + /* + * Keys that have been pressed at suspend time are unlikely + * to be still pressed when we resume. + */ + if (!test_bit(INPUT_PROP_NO_DUMMY_RELEASE, dev->propbit)) { + input_dev_toggle(dev, true); + input_dev_release_keys(dev); + } spin_unlock_irqrestore(&dev->event_lock, flags); mutex_unlock(&dev->mutex); diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index bef317ff7352..c93dd193a496 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -3,6 +3,7 @@ * * Copyright 2005 Phil Blundell * Copyright 2010, 2011 David Jander <david@protonic.nl> + * Copyright (c) 2015, 2017, The Linux Foundation. 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 version 2 as @@ -31,6 +32,8 @@ #include <linux/of_gpio.h> #include <linux/of_irq.h> #include <linux/spinlock.h> +#include <linux/pinctrl/consumer.h> +#include <linux/syscore_ops.h> struct gpio_button_data { const struct gpio_keys_button *button; @@ -50,11 +53,17 @@ struct gpio_button_data { struct gpio_keys_drvdata { const struct gpio_keys_platform_data *pdata; + struct pinctrl *key_pinctrl; struct input_dev *input; struct mutex disable_lock; struct gpio_button_data data[0]; }; +static struct device *global_dev; +static struct syscore_ops gpio_keys_syscore_pm_ops; + +static void gpio_keys_syscore_resume(void); + /* * SYSFS interface for enabling/disabling keys and switches: * @@ -341,14 +350,14 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; - int state = gpio_get_value_cansleep(button->gpio); + int state; + state = (__gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low; if (state < 0) { dev_err(input->dev.parent, "failed to get gpio state\n"); return; } - state = (state ? 1 : 0) ^ button->active_low; if (type == EV_ABS) { if (state) input_event(input, type, button->code, button->value); @@ -564,6 +573,41 @@ static void gpio_keys_report_state(struct gpio_keys_drvdata *ddata) input_sync(input); } +static int gpio_keys_pinctrl_configure(struct gpio_keys_drvdata *ddata, + bool active) +{ + struct pinctrl_state *set_state; + int retval; + + if (active) { + set_state = + pinctrl_lookup_state(ddata->key_pinctrl, + "tlmm_gpio_key_active"); + if (IS_ERR(set_state)) { + dev_err(&ddata->input->dev, + "cannot get ts pinctrl active state\n"); + return PTR_ERR(set_state); + } + } else { + set_state = + pinctrl_lookup_state(ddata->key_pinctrl, + "tlmm_gpio_key_suspend"); + if (IS_ERR(set_state)) { + dev_err(&ddata->input->dev, + "cannot get gpiokey pinctrl sleep state\n"); + return PTR_ERR(set_state); + } + } + retval = pinctrl_select_state(ddata->key_pinctrl, set_state); + if (retval) { + dev_err(&ddata->input->dev, + "cannot set ts pinctrl active state\n"); + return retval; + } + + return 0; +} + static int gpio_keys_open(struct input_dev *input) { struct gpio_keys_drvdata *ddata = input_get_drvdata(input); @@ -627,6 +671,8 @@ gpio_keys_get_devtree_pdata(struct device *dev) pdata->nbuttons = nbuttons; pdata->rep = !!of_get_property(node, "autorepeat", NULL); + pdata->name = of_get_property(node, "input-name", NULL); + pdata->use_syscore = of_property_read_bool(node, "use-syscore"); i = 0; for_each_child_of_node(node, pp) { @@ -673,7 +719,7 @@ gpio_keys_get_devtree_pdata(struct device *dev) button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL); if (of_property_read_u32(pp, "debounce-interval", - &button->debounce_interval)) + &button->debounce_interval)) button->debounce_interval = 5; } @@ -708,6 +754,7 @@ static int gpio_keys_probe(struct platform_device *pdev) size_t size; int i, error; int wakeup = 0; + struct pinctrl_state *set_state; if (!pdata) { pdata = gpio_keys_get_devtree_pdata(dev); @@ -729,6 +776,7 @@ static int gpio_keys_probe(struct platform_device *pdev) return -ENOMEM; } + global_dev = dev; ddata->pdata = pdata; ddata->input = input; mutex_init(&ddata->disable_lock); @@ -736,7 +784,7 @@ static int gpio_keys_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); input_set_drvdata(input, ddata); - input->name = pdata->name ? : pdev->name; + input->name = GPIO_KEYS_DEV_NAME; input->phys = "gpio-keys/input0"; input->dev.parent = &pdev->dev; input->open = gpio_keys_open; @@ -751,13 +799,31 @@ static int gpio_keys_probe(struct platform_device *pdev) if (pdata->rep) __set_bit(EV_REP, input->evbit); + /* Get pinctrl if target uses pinctrl */ + ddata->key_pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(ddata->key_pinctrl)) { + if (PTR_ERR(ddata->key_pinctrl) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + pr_debug("Target does not use pinctrl\n"); + ddata->key_pinctrl = NULL; + } + + if (ddata->key_pinctrl) { + error = gpio_keys_pinctrl_configure(ddata, true); + if (error) { + dev_err(dev, "cannot set ts pinctrl active state\n"); + return error; + } + } + for (i = 0; i < pdata->nbuttons; i++) { const struct gpio_keys_button *button = &pdata->buttons[i]; struct gpio_button_data *bdata = &ddata->data[i]; error = gpio_keys_setup_key(pdev, input, bdata, button); if (error) - return error; + goto err_setup_key; if (button->wakeup) wakeup = 1; @@ -767,7 +833,7 @@ static int gpio_keys_probe(struct platform_device *pdev) if (error) { dev_err(dev, "Unable to export keys/switches, error: %d\n", error); - return error; + goto err_create_sysfs; } error = input_register_device(input); @@ -779,16 +845,34 @@ static int gpio_keys_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, wakeup); + if (pdata->use_syscore) + gpio_keys_syscore_pm_ops.resume = gpio_keys_syscore_resume; + + register_syscore_ops(&gpio_keys_syscore_pm_ops); + return 0; err_remove_group: sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); +err_create_sysfs: +err_setup_key: + if (ddata->key_pinctrl) { + set_state = + pinctrl_lookup_state(ddata->key_pinctrl, + "tlmm_gpio_key_suspend"); + if (IS_ERR(set_state)) + dev_err(dev, "cannot get gpiokey pinctrl sleep state\n"); + else + pinctrl_select_state(ddata->key_pinctrl, set_state); + } + return error; } static int gpio_keys_remove(struct platform_device *pdev) { sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); + unregister_syscore_ops(&gpio_keys_syscore_pm_ops); device_init_wakeup(&pdev->dev, 0); @@ -796,11 +880,54 @@ static int gpio_keys_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP +static void gpio_keys_syscore_resume(void) +{ + struct gpio_keys_drvdata *ddata = dev_get_drvdata(global_dev); + struct input_dev *input = ddata->input; + struct gpio_button_data *bdata = NULL; + int error = 0; + int i; + + if (ddata->key_pinctrl) { + error = gpio_keys_pinctrl_configure(ddata, true); + if (error) { + dev_err(global_dev, "failed to put the pin in resume state\n"); + return; + } + } + + if (device_may_wakeup(global_dev)) { + for (i = 0; i < ddata->pdata->nbuttons; i++) { + bdata = &ddata->data[i]; + if (bdata->button->wakeup) + disable_irq_wake(bdata->irq); + } + } else { + mutex_lock(&input->mutex); + if (input->users) + error = gpio_keys_open(input); + mutex_unlock(&input->mutex); + } + + if (error) + return; + + gpio_keys_report_state(ddata); +} + static int gpio_keys_suspend(struct device *dev) { struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); struct input_dev *input = ddata->input; - int i; + int i, ret; + + if (ddata->key_pinctrl) { + ret = gpio_keys_pinctrl_configure(ddata, false); + if (ret) { + dev_err(dev, "failed to put the pin in suspend state\n"); + return ret; + } + } if (device_may_wakeup(dev)) { for (i = 0; i < ddata->pdata->nbuttons; i++) { @@ -825,6 +952,19 @@ static int gpio_keys_resume(struct device *dev) int error = 0; int i; + if (ddata->pdata->use_syscore == true) { + dev_dbg(global_dev, "Using syscore resume, no need of this resume.\n"); + return 0; + } + + if (ddata->key_pinctrl) { + error = gpio_keys_pinctrl_configure(ddata, true); + if (error) { + dev_err(dev, "failed to put the pin in resume state\n"); + return error; + } + } + if (device_may_wakeup(dev)) { for (i = 0; i < ddata->pdata->nbuttons; i++) { struct gpio_button_data *bdata = &ddata->data[i]; @@ -844,6 +984,21 @@ static int gpio_keys_resume(struct device *dev) gpio_keys_report_state(ddata); return 0; } + +#else + +static void gpio_keys_syscore_resume(void){} + +static int gpio_keys_suspend(struct device *dev) +{ + return 0; +} + +static int gpio_keys_resume(struct device *dev) +{ + return 0; +} + #endif static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 25ac47b9a180..dd98d8c8fd1f 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -103,6 +103,17 @@ config INPUT_E3X0_BUTTON To compile this driver as a module, choose M here: the module will be called e3x0_button. +config INPUT_HBTP_INPUT + tristate "HBTP input driver support" + help + This option enables an input driver for the host based touch + processing. + + Say Y to enable HBTP input driver. + + To compile this driver as a module, choose M here: the + module will be called hbtp_input. + config INPUT_PCSPKR tristate "PC Speaker support" depends on PCSPKR_PLATFORM @@ -150,6 +161,15 @@ config INPUT_PMIC8XXX_PWRKEY To compile this driver as a module, choose M here: the module will be called pmic8xxx-pwrkey. +config INPUT_QPNP_POWER_ON + tristate "QPNP PMIC Power-on support" + depends on SPMI + help + This option enables device driver support for the power-on + functionality of Qualcomm Technologies, Inc. PNP PMICs. It supports + reporting the change in status of the KPDPWR_N line (connected to the + power-key) as well as reset features. + config INPUT_SPARCSPKR tristate "SPARC Speaker support" depends on PCI && SPARC64 @@ -812,4 +832,16 @@ config INPUT_DRV2667_HAPTICS To compile this driver as a module, choose M here: the module will be called drv2667-haptics. +source "drivers/input/misc/ots_pat9125/Kconfig" + +config INPUT_STMVL53L0 + tristate "STM VL53L0 Proximity support" + depends on INPUT && I2C + help + Say Y here if you want to use STMicroelectronics's proximity sensor + through I2C interface. + + To compile this driver as a module, choose M here: the + module will be called stmvl53l0. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 66c3cc9f181c..44c026abcb6f 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o +obj-$(CONFIG_INPUT_HBTP_INPUT) += hbtp_input.o hbtp_vm.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o @@ -56,6 +57,7 @@ obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_PM8941_PWRKEY) += pm8941-pwrkey.o obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o +obj-$(CONFIG_INPUT_QPNP_POWER_ON) += qpnp-power-on.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o @@ -77,3 +79,5 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o +obj-$(CONFIG_INPUT_PIXART_OTS_PAT9125_SWITCH) += ots_pat9125/ +obj-$(CONFIG_INPUT_STMVL53L0) += vl53L0/ diff --git a/drivers/input/misc/hbtp_input.c b/drivers/input/misc/hbtp_input.c new file mode 100644 index 000000000000..56f2732334db --- /dev/null +++ b/drivers/input/misc/hbtp_input.c @@ -0,0 +1,1553 @@ + +/* Copyright (c) 2014-2017, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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/poll.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/input/mt.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <uapi/linux/hbtp_input.h> +#include "../input-compat.h" +#include <linux/ktime.h> +#include <linux/uaccess.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/delay.h> +#include <linux/completion.h> + +#if defined(CONFIG_FB) +#include <linux/notifier.h> +#include <linux/fb.h> +#endif + +#define HBTP_INPUT_NAME "hbtp_input" +#define DISP_COORDS_SIZE 2 + +#define HBTP_PINCTRL_VALID_STATE_CNT (2) +#define HBTP_HOLD_DURATION_US (10) +#define HBTP_PINCTRL_DDIC_SEQ_NUM (4) + +struct hbtp_data { + struct platform_device *pdev; + struct input_dev *input_dev; + s32 count; + struct mutex mutex; + struct mutex sensormutex; + struct hbtp_sensor_data *sensor_data; + bool touch_status[HBTP_MAX_FINGER]; +#if defined(CONFIG_FB) + struct notifier_block fb_notif; +#endif + struct pinctrl *ts_pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; + bool ddic_rst_enabled; + struct pinctrl_state *ddic_rst_state_active; + struct pinctrl_state *ddic_rst_state_suspend; + u32 ts_pinctrl_seq_delay; + u32 ddic_pinctrl_seq_delay[HBTP_PINCTRL_DDIC_SEQ_NUM]; + u32 fb_resume_seq_delay; + int lcd_state; + bool power_suspended; + bool power_sync_enabled; + bool power_sig_enabled; + struct completion power_resume_sig; + struct completion power_suspend_sig; + struct regulator *vcc_ana; + struct regulator *vcc_dig; + int afe_load_ua; + int afe_vtg_min_uv; + int afe_vtg_max_uv; + int dig_load_ua; + int dig_vtg_min_uv; + int dig_vtg_max_uv; + int disp_maxx; /* Display Max X */ + int disp_maxy; /* Display Max Y */ + int def_maxx; /* Default Max X */ + int def_maxy; /* Default Max Y */ + int des_maxx; /* Desired Max X */ + int des_maxy; /* Desired Max Y */ + bool use_scaling; + bool override_disp_coords; + bool manage_afe_power_ana; + bool manage_power_dig; + u32 power_on_delay; + u32 power_off_delay; + bool manage_pin_ctrl; + s16 ROI[MAX_ROI_SIZE]; + s16 accelBuffer[MAX_ACCEL_SIZE]; +}; + +static struct hbtp_data *hbtp; + +static struct kobject *sensor_kobject; + +#if defined(CONFIG_FB) +static int hbtp_fb_suspend(struct hbtp_data *ts); +static int hbtp_fb_early_resume(struct hbtp_data *ts); +static int hbtp_fb_resume(struct hbtp_data *ts); +#endif + +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + int blank; + int lcd_state; + struct fb_event *evdata = data; + struct fb_info *fbi = NULL; + struct hbtp_data *hbtp_data = + container_of(self, struct hbtp_data, fb_notif); + + if (!evdata) { + pr_debug("evdata is NULL"); + return 0; + } + fbi = evdata->info; + + /* + * Node 0 is the primary display and others are + * external displays such as HDMI/DP. + * We need to handle only fb event for the primary display. + */ + if (!fbi || fbi->node != 0) { + pr_debug("%s: no need to handle the fb event", __func__); + return 0; + } + + if (evdata->data && hbtp_data && + (event == FB_EARLY_EVENT_BLANK || + event == FB_R_EARLY_EVENT_BLANK)) { + blank = *(int *)(evdata->data); + lcd_state = hbtp->lcd_state; + if (event == FB_EARLY_EVENT_BLANK) { + if (blank <= FB_BLANK_NORMAL && + lcd_state == FB_BLANK_POWERDOWN) { + pr_debug("%s: receives EARLY_BLANK:UNBLANK\n", + __func__); + hbtp_fb_early_resume(hbtp_data); + } else if (blank == FB_BLANK_POWERDOWN && + lcd_state <= FB_BLANK_NORMAL) { + pr_debug("%s: receives EARLY_BLANK:POWERDOWN\n", + __func__); + } else { + pr_debug("%s: receives EARLY_BLANK:%d in %d state\n", + __func__, blank, lcd_state); + } + } else if (event == FB_R_EARLY_EVENT_BLANK) { + if (blank <= FB_BLANK_NORMAL) { + pr_debug("%s: receives R_EARLY_BALNK:UNBLANK\n", + __func__); + hbtp_fb_suspend(hbtp_data); + } else if (blank == FB_BLANK_POWERDOWN) { + pr_debug("%s: receives R_EARLY_BALNK:POWERDOWN\n", + __func__); + } else { + pr_debug("%s: receives R_EARLY_BALNK:%d in %d state\n", + __func__, blank, lcd_state); + } + } + } + + if (evdata->data && hbtp_data && + event == FB_EVENT_BLANK) { + blank = *(int *)(evdata->data); + lcd_state = hbtp->lcd_state; + if (blank == FB_BLANK_POWERDOWN && + lcd_state <= FB_BLANK_NORMAL) { + pr_debug("%s: receives BLANK:POWERDOWN\n", __func__); + hbtp_fb_suspend(hbtp_data); + } else if (blank <= FB_BLANK_NORMAL && + lcd_state == FB_BLANK_POWERDOWN) { + pr_debug("%s: receives BLANK:UNBLANK\n", __func__); + hbtp_fb_resume(hbtp_data); + } else { + pr_debug("%s: receives BLANK:%d in %d state\n", + __func__, blank, lcd_state); + } + hbtp_data->lcd_state = blank; + } + return 0; +} +#endif + +static ssize_t hbtp_sensor_roi_show(struct file *dev, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t pos, + size_t size) { + mutex_lock(&hbtp->sensormutex); + memcpy(buf, hbtp->ROI, size); + mutex_unlock(&hbtp->sensormutex); + + return size; +} + +static ssize_t hbtp_sensor_vib_show(struct file *dev, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t pos, + size_t size) { + mutex_lock(&hbtp->sensormutex); + memcpy(buf, hbtp->accelBuffer, size); + mutex_unlock(&hbtp->sensormutex); + + return size; +} + +static struct bin_attribute capdata_attr = { + .attr = { + .name = "capdata", + .mode = S_IRUGO, + }, + .size = 1024, + .read = hbtp_sensor_roi_show, + .write = NULL, +}; + +static struct bin_attribute vibdata_attr = { + .attr = { + .name = "vib_data", + .mode = S_IRUGO, + }, + .size = MAX_ACCEL_SIZE*sizeof(int16_t), + .read = hbtp_sensor_vib_show, + .write = NULL, +}; + +static int hbtp_input_open(struct inode *inode, struct file *file) +{ + mutex_lock(&hbtp->mutex); + if (hbtp->count) { + pr_err("%s is busy\n", HBTP_INPUT_NAME); + mutex_unlock(&hbtp->mutex); + return -EBUSY; + } + hbtp->count++; + mutex_unlock(&hbtp->mutex); + + return 0; +} + +static int hbtp_input_release(struct inode *inode, struct file *file) +{ + mutex_lock(&hbtp->mutex); + if (!hbtp->count) { + pr_err("%s wasn't opened\n", HBTP_INPUT_NAME); + mutex_unlock(&hbtp->mutex); + return -ENOTTY; + } + hbtp->count--; + if (hbtp->power_sig_enabled) + hbtp->power_sig_enabled = false; + mutex_unlock(&hbtp->mutex); + + return 0; +} + +static int hbtp_input_create_input_dev(struct hbtp_input_absinfo *absinfo) +{ + struct input_dev *input_dev; + struct hbtp_input_absinfo *abs; + int error; + int i; + + input_dev = input_allocate_device(); + if (!input_dev) { + pr_err("%s: input_allocate_device failed\n", __func__); + return -ENOMEM; + } + + kfree(input_dev->name); + input_dev->name = kstrndup(HBTP_INPUT_NAME, sizeof(HBTP_INPUT_NAME), + GFP_KERNEL); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + for (i = KEY_HOME; i <= KEY_MICMUTE; i++) + __set_bit(i, input_dev->keybit); + + /* For multi touch */ + input_mt_init_slots(input_dev, HBTP_MAX_FINGER, 0); + for (i = 0; i <= ABS_MT_LAST - ABS_MT_FIRST; i++) { + abs = absinfo + i; + if (abs->active) { + if (abs->code >= 0 && abs->code < ABS_CNT) + input_set_abs_params(input_dev, abs->code, + abs->minimum, abs->maximum, 0, 0); + else + pr_err("%s: ABS code out of bound\n", __func__); + } + } + + if (hbtp->override_disp_coords) { + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, hbtp->disp_maxx, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, hbtp->disp_maxy, 0, 0); + } + + error = input_register_device(input_dev); + if (error) { + pr_err("%s: input_register_device failed\n", __func__); + goto err_input_reg_dev; + } + + hbtp->input_dev = input_dev; + return 0; + +err_input_reg_dev: + input_free_device(input_dev); + + return error; +} + +static int hbtp_input_report_events(struct hbtp_data *hbtp_data, + struct hbtp_input_mt *mt_data) +{ + int i; + struct hbtp_input_touch *tch; + + for (i = 0; i < HBTP_MAX_FINGER; i++) { + tch = &(mt_data->touches[i]); + if (tch->active || hbtp_data->touch_status[i]) { + input_mt_slot(hbtp_data->input_dev, i); + input_mt_report_slot_state(hbtp_data->input_dev, + MT_TOOL_FINGER, tch->active); + + if (tch->active) { + input_report_abs(hbtp_data->input_dev, + ABS_MT_TOOL_TYPE, + tch->tool); + input_report_abs(hbtp_data->input_dev, + ABS_MT_TOUCH_MAJOR, + tch->major); + input_report_abs(hbtp_data->input_dev, + ABS_MT_TOUCH_MINOR, + tch->minor); + input_report_abs(hbtp_data->input_dev, + ABS_MT_ORIENTATION, + tch->orientation); + input_report_abs(hbtp_data->input_dev, + ABS_MT_PRESSURE, + tch->pressure); + /* + * Scale up/down the X-coordinate as per + * DT property + */ + if (hbtp_data->use_scaling && + hbtp_data->def_maxx > 0 && + hbtp_data->des_maxx > 0) + tch->x = (tch->x * hbtp_data->des_maxx) + / hbtp_data->def_maxx; + + input_report_abs(hbtp_data->input_dev, + ABS_MT_POSITION_X, + tch->x); + /* + * Scale up/down the Y-coordinate as per + * DT property + */ + if (hbtp_data->use_scaling && + hbtp_data->def_maxy > 0 && + hbtp_data->des_maxy > 0) + tch->y = (tch->y * hbtp_data->des_maxy) + / hbtp_data->def_maxy; + + input_report_abs(hbtp_data->input_dev, + ABS_MT_POSITION_Y, + tch->y); + } + hbtp_data->touch_status[i] = tch->active; + } + } + + input_report_key(hbtp->input_dev, BTN_TOUCH, mt_data->num_touches > 0); + input_sync(hbtp->input_dev); + + return 0; +} + +static int reg_set_load_check(struct regulator *reg, int load_uA) +{ + return (regulator_count_voltages(reg) > 0) ? + regulator_set_load(reg, load_uA) : 0; +} + +static int hbtp_pdev_power_on(struct hbtp_data *hbtp, bool on) +{ + int ret; + + if (!hbtp->vcc_ana) + pr_err("%s: analog regulator is not available\n", __func__); + + if (!hbtp->vcc_dig) + pr_err("%s: digital regulator is not available\n", __func__); + + if (!hbtp->vcc_ana && !hbtp->vcc_dig) { + pr_err("%s: no regulators available\n", __func__); + return -EINVAL; + } + + if (!on) + goto reg_off; + + if (hbtp->vcc_ana) { + ret = reg_set_load_check(hbtp->vcc_ana, + hbtp->afe_load_ua); + if (ret < 0) { + pr_err("%s: Regulator vcc_ana set_opt failed rc=%d\n", + __func__, ret); + return ret; + } + + ret = regulator_enable(hbtp->vcc_ana); + if (ret) { + pr_err("%s: Regulator vcc_ana enable failed rc=%d\n", + __func__, ret); + reg_set_load_check(hbtp->vcc_ana, 0); + return ret; + } + } + + if (hbtp->power_on_delay) { + pr_debug("%s: power-on-delay = %u\n", __func__, + hbtp->power_on_delay); + usleep_range(hbtp->power_on_delay, + hbtp->power_on_delay + HBTP_HOLD_DURATION_US); + } + + if (hbtp->vcc_dig) { + ret = reg_set_load_check(hbtp->vcc_dig, + hbtp->dig_load_ua); + if (ret < 0) { + pr_err("%s: Regulator vcc_dig set_opt failed rc=%d\n", + __func__, ret); + return ret; + } + + ret = regulator_enable(hbtp->vcc_dig); + if (ret) { + pr_err("%s: Regulator vcc_dig enable failed rc=%d\n", + __func__, ret); + reg_set_load_check(hbtp->vcc_dig, 0); + return ret; + } + } + + return 0; + +reg_off: + if (hbtp->vcc_dig) { + reg_set_load_check(hbtp->vcc_dig, 0); + regulator_disable(hbtp->vcc_dig); + } + + if (hbtp->power_off_delay) { + pr_debug("%s: power-off-delay = %u\n", __func__, + hbtp->power_off_delay); + usleep_range(hbtp->power_off_delay, + hbtp->power_off_delay + HBTP_HOLD_DURATION_US); + } + + if (hbtp->vcc_ana) { + reg_set_load_check(hbtp->vcc_ana, 0); + regulator_disable(hbtp->vcc_ana); + } + return 0; +} + +static int hbtp_gpio_select(struct hbtp_data *data, bool on) +{ + struct pinctrl_state *pins_state; + int ret = 0; + + pins_state = on ? data->gpio_state_active : data->gpio_state_suspend; + if (!IS_ERR_OR_NULL(pins_state)) { + ret = pinctrl_select_state(data->ts_pinctrl, pins_state); + if (ret) { + dev_err(&data->pdev->dev, + "can not set %s pins\n", + on ? "ts_active" : "ts_suspend"); + return ret; + } + + if (on) { + if (data->ts_pinctrl_seq_delay) { + usleep_range(data->ts_pinctrl_seq_delay, + data->ts_pinctrl_seq_delay + + HBTP_HOLD_DURATION_US); + dev_dbg(&data->pdev->dev, "ts_pinctrl_seq_delay = %u\n", + data->ts_pinctrl_seq_delay); + } + } + } else { + dev_warn(&data->pdev->dev, + "not a valid '%s' pinstate\n", + on ? "ts_active" : "ts_suspend"); + return ret; + } + + return ret; +} + +static int hbtp_ddic_rst_select(struct hbtp_data *data, bool on) +{ + struct pinctrl_state *active, *suspend; + int ret = 0; + + active = data->ddic_rst_state_active; + if (IS_ERR_OR_NULL(active)) { + dev_warn(&data->pdev->dev, + "not a valid ddic_rst_active pinstate\n"); + return ret; + } + + suspend = data->ddic_rst_state_suspend; + if (IS_ERR_OR_NULL(suspend)) { + dev_warn(&data->pdev->dev, + "not a valid ddic_rst_suspend pinstate\n"); + return ret; + } + + if (on) { + if (data->ddic_pinctrl_seq_delay[0]) { + usleep_range(data->ddic_pinctrl_seq_delay[0], + data->ddic_pinctrl_seq_delay[0] + + HBTP_HOLD_DURATION_US); + dev_dbg(&data->pdev->dev, "ddic_seq_delay[0] = %u\n", + data->ddic_pinctrl_seq_delay[0]); + } + + ret = pinctrl_select_state(data->ts_pinctrl, active); + if (ret) { + dev_err(&data->pdev->dev, + "can not set ddic_rst_active pins\n"); + return ret; + } + if (data->ddic_pinctrl_seq_delay[1]) { + usleep_range(data->ddic_pinctrl_seq_delay[1], + data->ddic_pinctrl_seq_delay[1] + + HBTP_HOLD_DURATION_US); + dev_dbg(&data->pdev->dev, "ddic_seq_delay[1] = %u\n", + data->ddic_pinctrl_seq_delay[1]); + } + ret = pinctrl_select_state(data->ts_pinctrl, suspend); + if (ret) { + dev_err(&data->pdev->dev, + "can not set ddic_rst_suspend pins\n"); + return ret; + } + + if (data->ddic_pinctrl_seq_delay[2]) { + usleep_range(data->ddic_pinctrl_seq_delay[2], + data->ddic_pinctrl_seq_delay[2] + + HBTP_HOLD_DURATION_US); + dev_dbg(&data->pdev->dev, "ddic_seq_delay[2] = %u\n", + data->ddic_pinctrl_seq_delay[2]); + } + + ret = pinctrl_select_state(data->ts_pinctrl, active); + if (ret) { + dev_err(&data->pdev->dev, + "can not set ddic_rst_active pins\n"); + return ret; + } + + if (data->ddic_pinctrl_seq_delay[3]) { + usleep_range(data->ddic_pinctrl_seq_delay[3], + data->ddic_pinctrl_seq_delay[3] + + HBTP_HOLD_DURATION_US); + dev_dbg(&data->pdev->dev, "ddic_seq_delay[3] = %u\n", + data->ddic_pinctrl_seq_delay[3]); + } + } else { + ret = pinctrl_select_state(data->ts_pinctrl, suspend); + if (ret) { + dev_err(&data->pdev->dev, + "can not set ddic_rst_suspend pins\n"); + return ret; + } + } + + return ret; +} + +static int hbtp_pinctrl_enable(struct hbtp_data *ts, bool on) +{ + int rc = 0; + + if (!ts->manage_pin_ctrl) { + pr_info("%s: pinctrl info is not available\n", __func__); + return 0; + } + + if (!on) + goto pinctrl_suspend; + + rc = hbtp_gpio_select(ts, true); + if (rc < 0) + return -EINVAL; + + if (ts->ddic_rst_enabled) { + rc = hbtp_ddic_rst_select(ts, true); + if (rc < 0) + goto err_ddic_rst_pinctrl_enable; + } + + return rc; + +pinctrl_suspend: + if (ts->ddic_rst_enabled) + hbtp_ddic_rst_select(ts, false); +err_ddic_rst_pinctrl_enable: + hbtp_gpio_select(ts, false); + return rc; +} + +static long hbtp_input_ioctl_handler(struct file *file, unsigned int cmd, + unsigned long arg, void __user *p) +{ + int error = 0; + struct hbtp_input_mt mt_data; + struct hbtp_input_absinfo absinfo[ABS_MT_LAST - ABS_MT_FIRST + 1]; + struct hbtp_input_key key_data; + enum hbtp_afe_power_cmd power_cmd; + enum hbtp_afe_signal afe_signal; + enum hbtp_afe_power_ctrl afe_power_ctrl; + + switch (cmd) { + case HBTP_SET_ABSPARAM: + if (hbtp && hbtp->input_dev) { + pr_err("%s: The input device is already created\n", + __func__); + return 0; + } + + if (copy_from_user(absinfo, (void *)arg, + sizeof(struct hbtp_input_absinfo) * + (ABS_MT_LAST - ABS_MT_FIRST + 1))) { + pr_err("%s: Error copying data for ABS param\n", + __func__); + return -EFAULT; + } + + error = hbtp_input_create_input_dev(absinfo); + if (error) + pr_err("%s, hbtp_input_create_input_dev failed (%d)\n", + __func__, error); + break; + + case HBTP_SET_TOUCHDATA: + if (!hbtp || !hbtp->input_dev) { + pr_err("%s: The input device hasn't been created\n", + __func__); + return -EFAULT; + } + + if (copy_from_user(&mt_data, (void *)arg, + sizeof(struct hbtp_input_mt))) { + pr_err("%s: Error copying data\n", __func__); + return -EFAULT; + } + + hbtp_input_report_events(hbtp, &mt_data); + error = 0; + break; + + case HBTP_SET_POWERSTATE: + if (!hbtp || !hbtp->input_dev) { + pr_err("%s: The input device hasn't been created\n", + __func__); + return -EFAULT; + } + + if (copy_from_user(&power_cmd, (void *)arg, + sizeof(enum hbtp_afe_power_cmd))) { + pr_err("%s: Error copying data\n", __func__); + return -EFAULT; + } + + switch (power_cmd) { + case HBTP_AFE_POWER_ON: + error = hbtp_pdev_power_on(hbtp, true); + if (error) + pr_err("%s: failed to power on\n", __func__); + break; + case HBTP_AFE_POWER_OFF: + error = hbtp_pdev_power_on(hbtp, false); + if (error) + pr_err("%s: failed to power off\n", __func__); + break; + default: + pr_err("%s: Unsupported command for power state, %d\n", + __func__, power_cmd); + return -EINVAL; + } + break; + + case HBTP_SET_KEYDATA: + if (!hbtp || !hbtp->input_dev) { + pr_err("%s: The input device hasn't been created\n", + __func__); + return -EFAULT; + } + + if (copy_from_user(&key_data, (void *)arg, + sizeof(struct hbtp_input_key))) { + pr_err("%s: Error copying data for key info\n", + __func__); + return -EFAULT; + } + + input_report_key(hbtp->input_dev, key_data.code, + key_data.value); + input_sync(hbtp->input_dev); + break; + + case HBTP_SET_SYNCSIGNAL: + if (!hbtp || !hbtp->input_dev) { + pr_err("%s: The input device hasn't been created\n", + __func__); + return -EFAULT; + } + + if (!hbtp->power_sig_enabled) { + pr_err("%s: power_signal is not enabled", __func__); + return -EPERM; + } + + if (copy_from_user(&afe_signal, (void *)arg, + sizeof(enum hbtp_afe_signal))) { + pr_err("%s: Error copying data\n", __func__); + return -EFAULT; + } + + pr_debug("%s: receives %d signal\n", __func__, afe_signal); + + switch (afe_signal) { + case HBTP_AFE_SIGNAL_ON_RESUME: + mutex_lock(&hbtp->mutex); + if (!hbtp->power_suspended) { + complete(&hbtp->power_resume_sig); + } else { + pr_err("%s: resume signal in wrong state\n", + __func__); + } + mutex_unlock(&hbtp->mutex); + break; + case HBTP_AFE_SIGNAL_ON_SUSPEND: + mutex_lock(&hbtp->mutex); + if (hbtp->power_suspended) { + complete(&hbtp->power_suspend_sig); + } else { + pr_err("%s: suspend signal in wrong state\n", + __func__); + } + mutex_unlock(&hbtp->mutex); + break; + default: + pr_err("%s: Unsupported command for afe signal, %d\n", + __func__, afe_signal); + return -EINVAL; + } + break; + case HBTP_SET_POWER_CTRL: + if (!hbtp || !hbtp->input_dev) { + pr_err("%s: The input device hasn't been created\n", + __func__); + return -EFAULT; + } + + if (copy_from_user(&afe_power_ctrl, (void *)arg, + sizeof(enum hbtp_afe_power_ctrl))) { + pr_err("%s: Error copying data\n", __func__); + return -EFAULT; + } + switch (afe_power_ctrl) { + case HBTP_AFE_POWER_ENABLE_SYNC: + pr_debug("%s: power_sync is enabled\n", __func__); + if (!hbtp->manage_pin_ctrl || !hbtp->manage_power_dig || + !hbtp->manage_afe_power_ana) { + pr_err("%s: power/pin is not available\n", + __func__); + return -EFAULT; + } + mutex_lock(&hbtp->mutex); + error = hbtp_pdev_power_on(hbtp, true); + if (error) { + mutex_unlock(&hbtp->mutex); + pr_err("%s: failed to power on\n", __func__); + return error; + } + error = hbtp_pinctrl_enable(hbtp, true); + if (error) { + mutex_unlock(&hbtp->mutex); + pr_err("%s: failed to enable pins\n", __func__); + hbtp_pdev_power_on(hbtp, false); + return error; + } + hbtp->power_sync_enabled = true; + mutex_unlock(&hbtp->mutex); + pr_debug("%s: power_sync option is enabled\n", + __func__); + break; + case HBTP_AFE_POWER_ENABLE_SYNC_SIGNAL: + if (!hbtp->power_sync_enabled) { + pr_err("%s: power_sync is not enabled\n", + __func__); + return -EFAULT; + } + mutex_lock(&hbtp->mutex); + init_completion(&hbtp->power_resume_sig); + init_completion(&hbtp->power_suspend_sig); + hbtp->power_sig_enabled = true; + mutex_unlock(&hbtp->mutex); + pr_err("%s: sync_signal option is enabled\n", __func__); + break; + default: + pr_err("%s: unsupported power ctrl, %d\n", + __func__, afe_power_ctrl); + return -EINVAL; + } + break; + + case HBTP_SET_SENSORDATA: + if (copy_from_user(hbtp->sensor_data, (void *)arg, + sizeof(struct hbtp_sensor_data))) { + pr_err("%s: Error copying data\n", __func__); + return -EFAULT; + } + mutex_lock(&hbtp->sensormutex); + memcpy(hbtp->ROI, hbtp->sensor_data->ROI, sizeof(hbtp->ROI)); + memcpy(hbtp->accelBuffer, hbtp->sensor_data->accelBuffer, + sizeof(hbtp->accelBuffer)); + mutex_unlock(&hbtp->sensormutex); + + error = 0; + break; + + default: + pr_err("%s: Unsupported ioctl command %u\n", __func__, cmd); + error = -EINVAL; + break; + } + + return error; +} + +static long hbtp_input_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return hbtp_input_ioctl_handler(file, cmd, arg, (void __user *)arg); +} + +#ifdef CONFIG_COMPAT +static long hbtp_input_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return hbtp_input_ioctl_handler(file, cmd, arg, compat_ptr(arg)); +} +#endif + +static const struct file_operations hbtp_input_fops = { + .owner = THIS_MODULE, + .open = hbtp_input_open, + .release = hbtp_input_release, + .unlocked_ioctl = hbtp_input_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = hbtp_input_compat_ioctl, +#endif +}; + +static struct miscdevice hbtp_input_misc = { + .fops = &hbtp_input_fops, + .minor = MISC_DYNAMIC_MINOR, + .name = HBTP_INPUT_NAME, +}; +MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR); +MODULE_ALIAS("devname:" HBTP_INPUT_NAME); + +#ifdef CONFIG_OF +static int hbtp_parse_dt(struct device *dev) +{ + int rc, size; + struct device_node *np = dev->of_node; + struct property *prop; + u32 temp_val; + u32 disp_reso[DISP_COORDS_SIZE]; + + if (of_find_property(np, "vcc_ana-supply", NULL)) + hbtp->manage_afe_power_ana = true; + if (of_find_property(np, "vcc_dig-supply", NULL)) + hbtp->manage_power_dig = true; + + if (hbtp->manage_afe_power_ana) { + rc = of_property_read_u32(np, "qcom,afe-load", &temp_val); + if (!rc) { + hbtp->afe_load_ua = (int) temp_val; + } else { + dev_err(dev, "Unable to read AFE load\n"); + return rc; + } + + rc = of_property_read_u32(np, "qcom,afe-vtg-min", &temp_val); + if (!rc) { + hbtp->afe_vtg_min_uv = (int) temp_val; + } else { + dev_err(dev, "Unable to read AFE min voltage\n"); + return rc; + } + + rc = of_property_read_u32(np, "qcom,afe-vtg-max", &temp_val); + if (!rc) { + hbtp->afe_vtg_max_uv = (int) temp_val; + } else { + dev_err(dev, "Unable to read AFE max voltage\n"); + return rc; + } + } + if (hbtp->manage_power_dig) { + rc = of_property_read_u32(np, "qcom,dig-load", &temp_val); + if (!rc) { + hbtp->dig_load_ua = (int) temp_val; + } else { + dev_err(dev, "Unable to read digital load\n"); + return rc; + } + + rc = of_property_read_u32(np, "qcom,dig-vtg-min", &temp_val); + if (!rc) { + hbtp->dig_vtg_min_uv = (int) temp_val; + } else { + dev_err(dev, "Unable to read digital min voltage\n"); + return rc; + } + + rc = of_property_read_u32(np, "qcom,dig-vtg-max", &temp_val); + if (!rc) { + hbtp->dig_vtg_max_uv = (int) temp_val; + } else { + dev_err(dev, "Unable to read digital max voltage\n"); + return rc; + } + } + + if (hbtp->manage_power_dig && hbtp->manage_afe_power_ana) { + rc = of_property_read_u32(np, + "qcom,afe-power-on-delay-us", &temp_val); + if (!rc) + hbtp->power_on_delay = (u32)temp_val; + else + dev_info(dev, "Power-On Delay is not specified\n"); + + rc = of_property_read_u32(np, + "qcom,afe-power-off-delay-us", &temp_val); + if (!rc) + hbtp->power_off_delay = (u32)temp_val; + else + dev_info(dev, "Power-Off Delay is not specified\n"); + + dev_dbg(dev, "power-on-delay = %u, power-off-delay = %u\n", + hbtp->power_on_delay, hbtp->power_off_delay); + } + + prop = of_find_property(np, "qcom,display-resolution", NULL); + if (prop != NULL) { + if (!prop->value) + return -ENODATA; + + size = prop->length / sizeof(u32); + if (size != DISP_COORDS_SIZE) { + dev_err(dev, "invalid qcom,display-resolution DT property\n"); + return -EINVAL; + } + + rc = of_property_read_u32_array(np, "qcom,display-resolution", + disp_reso, size); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read DT property qcom,display-resolution\n"); + return rc; + } + + hbtp->disp_maxx = disp_reso[0]; + hbtp->disp_maxy = disp_reso[1]; + + hbtp->override_disp_coords = true; + } + + hbtp->use_scaling = of_property_read_bool(np, "qcom,use-scale"); + if (hbtp->use_scaling) { + rc = of_property_read_u32(np, "qcom,default-max-x", &temp_val); + if (!rc) { + hbtp->def_maxx = (int) temp_val; + } else if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read default max x\n"); + return rc; + } + + rc = of_property_read_u32(np, "qcom,desired-max-x", &temp_val); + if (!rc) { + hbtp->des_maxx = (int) temp_val; + } else if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read desired max x\n"); + return rc; + } + + /* + * Either both DT properties i.e. Default max X and + * Desired max X should be defined simultaneously, or none + * of them should be defined. + */ + if ((hbtp->def_maxx == 0 && hbtp->des_maxx != 0) || + (hbtp->def_maxx != 0 && hbtp->des_maxx == 0)) { + dev_err(dev, "default or desired max-X properties are incorrect\n"); + return -EINVAL; + } + + rc = of_property_read_u32(np, "qcom,default-max-y", &temp_val); + if (!rc) { + hbtp->def_maxy = (int) temp_val; + } else if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read default max y\n"); + return rc; + } + + rc = of_property_read_u32(np, "qcom,desired-max-y", &temp_val); + if (!rc) { + hbtp->des_maxy = (int) temp_val; + } else if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read desired max y\n"); + return rc; + } + + /* + * Either both DT properties i.e. Default max X and + * Desired max X should be defined simultaneously, or none + * of them should be defined. + */ + if ((hbtp->def_maxy == 0 && hbtp->des_maxy != 0) || + (hbtp->def_maxy != 0 && hbtp->des_maxy == 0)) { + dev_err(dev, "default or desired max-Y properties are incorrect\n"); + return -EINVAL; + } + + } + + return 0; +} +#else +static int hbtp_parse_dt(struct device *dev) +{ + return -ENODEV; +} +#endif + +static int hbtp_pinctrl_init(struct hbtp_data *data) +{ + const char *statename; + int rc; + int state_cnt, i; + struct device_node *np = data->pdev->dev.of_node; + bool pinctrl_state_act_found = false; + bool pinctrl_state_sus_found = false; + bool pinctrl_ddic_act_found = false; + bool pinctrl_ddic_sus_found = false; + int count = 0; + + data->ts_pinctrl = devm_pinctrl_get(&(data->pdev->dev)); + if (IS_ERR_OR_NULL(data->ts_pinctrl)) { + dev_err(&data->pdev->dev, + "Target does not use pinctrl\n"); + rc = PTR_ERR(data->ts_pinctrl); + data->ts_pinctrl = NULL; + return rc; + } + + state_cnt = of_property_count_strings(np, "pinctrl-names"); + if (state_cnt < HBTP_PINCTRL_VALID_STATE_CNT) { + /* + *if pinctrl names are not available then, + *power_sync can't be enabled + */ + dev_info(&data->pdev->dev, + "pinctrl names are not available\n"); + rc = -EINVAL; + goto error; + } + + for (i = 0; i < state_cnt; i++) { + rc = of_property_read_string_index(np, + "pinctrl-names", i, &statename); + if (rc) { + dev_err(&data->pdev->dev, + "failed to read pinctrl states by index\n"); + goto error; + } + + if (!strcmp(statename, "pmx_ts_active")) { + data->gpio_state_active + = pinctrl_lookup_state(data->ts_pinctrl, + statename); + if (IS_ERR_OR_NULL(data->gpio_state_active)) { + dev_err(&data->pdev->dev, + "Can not get ts default state\n"); + rc = PTR_ERR(data->gpio_state_active); + goto error; + } + pinctrl_state_act_found = true; + } else if (!strcmp(statename, "pmx_ts_suspend")) { + data->gpio_state_suspend + = pinctrl_lookup_state(data->ts_pinctrl, + statename); + if (IS_ERR_OR_NULL(data->gpio_state_suspend)) { + dev_err(&data->pdev->dev, + "Can not get ts sleep state\n"); + rc = PTR_ERR(data->gpio_state_suspend); + goto error; + } + pinctrl_state_sus_found = true; + } else if (!strcmp(statename, "ddic_rst_active")) { + data->ddic_rst_state_active + = pinctrl_lookup_state(data->ts_pinctrl, + statename); + if (IS_ERR(data->ddic_rst_state_active)) { + dev_err(&data->pdev->dev, + "Can not get DDIC rst act state\n"); + rc = PTR_ERR(data->ddic_rst_state_active); + goto error; + } + pinctrl_ddic_act_found = true; + } else if (!strcmp(statename, "ddic_rst_suspend")) { + data->ddic_rst_state_suspend + = pinctrl_lookup_state(data->ts_pinctrl, + statename); + if (IS_ERR(data->ddic_rst_state_suspend)) { + dev_err(&data->pdev->dev, + "Can not get DDIC rst sleep state\n"); + rc = PTR_ERR(data->ddic_rst_state_suspend); + goto error; + } + pinctrl_ddic_sus_found = true; + } else { + dev_err(&data->pdev->dev, "invalid pinctrl state\n"); + rc = -EINVAL; + goto error; + } + } + + if (!pinctrl_state_act_found || !pinctrl_state_sus_found) { + dev_err(&data->pdev->dev, + "missing required pinctrl states\n"); + rc = -EINVAL; + goto error; + } + + if (of_property_read_u32(np, "qcom,pmx-ts-on-seq-delay-us", + &data->ts_pinctrl_seq_delay)) { + dev_warn(&data->pdev->dev, "Can not find ts seq delay\n"); + } + + if (of_property_read_u32(np, "qcom,fb-resume-delay-us", + &data->fb_resume_seq_delay)) { + dev_warn(&data->pdev->dev, "Can not find fb resume seq delay\n"); + } + + if (pinctrl_ddic_act_found && pinctrl_ddic_sus_found) { + count = of_property_count_u32_elems(np, + "qcom,ddic-rst-on-seq-delay-us"); + if (count == HBTP_PINCTRL_DDIC_SEQ_NUM) { + of_property_read_u32_array(np, + "qcom,ddic-rst-on-seq-delay-us", + data->ddic_pinctrl_seq_delay, count); + } else { + dev_err(&data->pdev->dev, "count(%u) is not same as %u\n", + (u32)count, HBTP_PINCTRL_DDIC_SEQ_NUM); + } + + data->ddic_rst_enabled = true; + } else { + dev_warn(&data->pdev->dev, "ddic pinctrl act/sus not found\n"); + } + + data->manage_pin_ctrl = true; + return 0; + +error: + devm_pinctrl_put(data->ts_pinctrl); + data->ts_pinctrl = NULL; + return rc; +} + +static int hbtp_fb_suspend(struct hbtp_data *ts) +{ + int rc; + char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL}; + + mutex_lock(&hbtp->mutex); + if (ts->pdev && ts->power_sync_enabled) { + pr_debug("%s: power_sync is enabled\n", __func__); + if (ts->power_suspended) { + mutex_unlock(&hbtp->mutex); + pr_err("%s: power is not resumed\n", __func__); + return 0; + } + rc = hbtp_pinctrl_enable(ts, false); + if (rc) { + pr_err("%s: failed to disable GPIO pins\n", __func__); + goto err_pin_disable; + } + + rc = hbtp_pdev_power_on(ts, false); + if (rc) { + pr_err("%s: failed to disable power\n", __func__); + goto err_power_disable; + } + ts->power_suspended = true; + } + + if (ts->input_dev) { + kobject_uevent_env(&ts->input_dev->dev.kobj, + KOBJ_OFFLINE, envp); + + if (ts->power_sig_enabled) { + pr_debug("%s: power_sig is enabled, wait for signal\n", + __func__); + mutex_unlock(&hbtp->mutex); + rc = wait_for_completion_interruptible( + &hbtp->power_suspend_sig); + if (rc != 0) { + pr_err("%s: wait for suspend is interrupted\n", + __func__); + } + mutex_lock(&hbtp->mutex); + pr_debug("%s: Wait is done for suspend\n", __func__); + } else { + pr_debug("%s: power_sig is NOT enabled", __func__); + } + } + + mutex_unlock(&hbtp->mutex); + return 0; +err_power_disable: + hbtp_pinctrl_enable(ts, true); +err_pin_disable: + mutex_unlock(&hbtp->mutex); + return rc; +} + +static int hbtp_fb_early_resume(struct hbtp_data *ts) +{ + char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL}; + int rc; + + mutex_lock(&hbtp->mutex); + + pr_debug("%s: hbtp_fb_early_resume\n", __func__); + + if (ts->pdev && ts->power_sync_enabled) { + pr_debug("%s: power_sync is enabled\n", __func__); + if (!ts->power_suspended) { + pr_err("%s: power is not suspended\n", __func__); + mutex_unlock(&hbtp->mutex); + return 0; + } + rc = hbtp_pdev_power_on(ts, true); + if (rc) { + pr_err("%s: failed to enable panel power\n", __func__); + goto err_power_on; + } + + rc = hbtp_pinctrl_enable(ts, true); + + if (rc) { + pr_err("%s: failed to enable pin\n", __func__); + goto err_pin_enable; + } + + ts->power_suspended = false; + + if (ts->input_dev) { + + kobject_uevent_env(&ts->input_dev->dev.kobj, + KOBJ_ONLINE, envp); + + if (ts->power_sig_enabled) { + pr_err("%s: power_sig is enabled, wait for signal\n", + __func__); + mutex_unlock(&hbtp->mutex); + rc = wait_for_completion_interruptible( + &hbtp->power_resume_sig); + if (rc != 0) { + pr_err("%s: wait for resume is interrupted\n", + __func__); + } + mutex_lock(&hbtp->mutex); + pr_debug("%s: wait is done\n", __func__); + } else { + pr_debug("%s: power_sig is NOT enabled\n", + __func__); + } + + if (ts->fb_resume_seq_delay) { + usleep_range(ts->fb_resume_seq_delay, + ts->fb_resume_seq_delay + + HBTP_HOLD_DURATION_US); + pr_err("%s: fb_resume_seq_delay = %u\n", + __func__, ts->fb_resume_seq_delay); + } + } + } + mutex_unlock(&hbtp->mutex); + return 0; + +err_pin_enable: + hbtp_pdev_power_on(ts, false); +err_power_on: + mutex_unlock(&hbtp->mutex); + return rc; +} + +static int hbtp_fb_resume(struct hbtp_data *ts) +{ + char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL}; + + mutex_lock(&hbtp->mutex); + if (!ts->power_sync_enabled) { + pr_debug("%s: power_sync is disabled, send uevent\n", __func__); + if (ts->input_dev) { + kobject_uevent_env(&ts->input_dev->dev.kobj, + KOBJ_ONLINE, envp); + } + } + mutex_unlock(&hbtp->mutex); + return 0; +} + +static int hbtp_pdev_probe(struct platform_device *pdev) +{ + int error; + struct regulator *vcc_ana, *vcc_dig; + + hbtp->pdev = pdev; + + if (pdev->dev.of_node) { + error = hbtp_parse_dt(&pdev->dev); + if (error) { + pr_err("%s: parse dt failed, rc=%d\n", __func__, error); + return error; + } + } + + platform_set_drvdata(pdev, hbtp); + + error = hbtp_pinctrl_init(hbtp); + if (error) { + pr_info("%s: pinctrl isn't available, rc=%d\n", __func__, + error); + } + + if (hbtp->manage_afe_power_ana) { + vcc_ana = regulator_get(&pdev->dev, "vcc_ana"); + if (IS_ERR(vcc_ana)) { + error = PTR_ERR(vcc_ana); + pr_err("%s: regulator get failed vcc_ana rc=%d\n", + __func__, error); + return error; + } + + if (regulator_count_voltages(vcc_ana) > 0) { + error = regulator_set_voltage(vcc_ana, + hbtp->afe_vtg_min_uv, hbtp->afe_vtg_max_uv); + if (error) { + pr_err("%s: regulator set vtg failed vcc_ana rc=%d\n", + __func__, error); + regulator_put(vcc_ana); + return error; + } + } + hbtp->vcc_ana = vcc_ana; + } + + if (hbtp->manage_power_dig) { + vcc_dig = regulator_get(&pdev->dev, "vcc_dig"); + if (IS_ERR(vcc_dig)) { + error = PTR_ERR(vcc_dig); + pr_err("%s: regulator get failed vcc_dig rc=%d\n", + __func__, error); + return error; + } + + if (regulator_count_voltages(vcc_dig) > 0) { + error = regulator_set_voltage(vcc_dig, + hbtp->dig_vtg_min_uv, hbtp->dig_vtg_max_uv); + if (error) { + pr_err("%s: regulator set vtg failed vcc_dig rc=%d\n", + __func__, error); + regulator_put(vcc_dig); + return error; + } + } + hbtp->vcc_dig = vcc_dig; + } + + return 0; +} + +static int hbtp_pdev_remove(struct platform_device *pdev) +{ + if (hbtp->vcc_ana || hbtp->vcc_dig) { + hbtp_pdev_power_on(hbtp, false); + if (hbtp->vcc_ana) + regulator_put(hbtp->vcc_ana); + if (hbtp->vcc_dig) + regulator_put(hbtp->vcc_dig); + } + + if (hbtp->ts_pinctrl) + devm_pinctrl_put(hbtp->ts_pinctrl); + + return 0; +} + +#ifdef CONFIG_OF +static struct of_device_id hbtp_match_table[] = { + { .compatible = "qcom,hbtp-input",}, + { }, +}; +#else +#define hbtp_match_table NULL +#endif + +static struct platform_driver hbtp_pdev_driver = { + .probe = hbtp_pdev_probe, + .remove = hbtp_pdev_remove, + .driver = { + .name = "hbtp", + .owner = THIS_MODULE, + .of_match_table = hbtp_match_table, + }, +}; + +static int __init hbtp_init(void) +{ + int error; + + hbtp = kzalloc(sizeof(struct hbtp_data), GFP_KERNEL); + if (!hbtp) + return -ENOMEM; + + hbtp->sensor_data = kzalloc(sizeof(struct hbtp_sensor_data), + GFP_KERNEL); + if (!hbtp->sensor_data) { + error = -ENOMEM; + goto err_sensordata; + } + + mutex_init(&hbtp->mutex); + mutex_init(&hbtp->sensormutex); + + error = misc_register(&hbtp_input_misc); + if (error) { + pr_err("%s: misc_register failed\n", HBTP_INPUT_NAME); + goto err_misc_reg; + } + +#if defined(CONFIG_FB) + hbtp->fb_notif.notifier_call = fb_notifier_callback; + error = fb_register_client(&hbtp->fb_notif); + if (error) { + pr_err("%s: Unable to register fb_notifier: %d\n", + HBTP_INPUT_NAME, error); + goto err_fb_reg; + } +#endif + + sensor_kobject = kobject_create_and_add("hbtpsensor", kernel_kobj); + if (!sensor_kobject) { + pr_err("%s: Could not create hbtpsensor kobject\n", __func__); + error = -ENOMEM; + goto err_kobject_create; + } + + error = sysfs_create_bin_file(sensor_kobject, &capdata_attr); + if (error < 0) { + pr_err("%s: hbtp capdata sysfs creation failed: %d\n", __func__, + error); + goto err_sysfs_create_capdata; + } + pr_debug("capdata sysfs creation success\n"); + + error = sysfs_create_bin_file(sensor_kobject, &vibdata_attr); + if (error < 0) { + pr_err("%s: vibdata sysfs creation failed: %d\n", __func__, + error); + goto err_sysfs_create_vibdata; + } + pr_debug("vibdata sysfs creation success\n"); + + error = platform_driver_register(&hbtp_pdev_driver); + if (error) { + pr_err("Failed to register platform driver: %d\n", error); + goto err_platform_drv_reg; + } + + return 0; + +err_platform_drv_reg: + sysfs_remove_bin_file(sensor_kobject, &vibdata_attr); +err_sysfs_create_vibdata: + sysfs_remove_bin_file(sensor_kobject, &capdata_attr); +err_sysfs_create_capdata: + kobject_put(sensor_kobject); +err_kobject_create: +#if defined(CONFIG_FB) + fb_unregister_client(&hbtp->fb_notif); +err_fb_reg: +#endif + misc_deregister(&hbtp_input_misc); +err_misc_reg: + kfree(hbtp->sensor_data); +err_sensordata: + kfree(hbtp); + + return error; +} + +static void __exit hbtp_exit(void) +{ + sysfs_remove_bin_file(sensor_kobject, &vibdata_attr); + sysfs_remove_bin_file(sensor_kobject, &capdata_attr); + kobject_put(sensor_kobject); + misc_deregister(&hbtp_input_misc); + if (hbtp->input_dev) + input_unregister_device(hbtp->input_dev); + +#if defined(CONFIG_FB) + fb_unregister_client(&hbtp->fb_notif); +#endif + + platform_driver_unregister(&hbtp_pdev_driver); + + kfree(hbtp->sensor_data); + kfree(hbtp); +} + +MODULE_DESCRIPTION("Kernel driver to support host based touch processing"); +MODULE_LICENSE("GPLv2"); + +module_init(hbtp_init); +module_exit(hbtp_exit); diff --git a/drivers/input/misc/hbtp_vm.c b/drivers/input/misc/hbtp_vm.c new file mode 100644 index 000000000000..408bf5d67b63 --- /dev/null +++ b/drivers/input/misc/hbtp_vm.c @@ -0,0 +1,293 @@ +/* Copyright (c) 2015, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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/poll.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/input/mt.h> +#include <uapi/linux/hbtp_input.h> +#include <uapi/linux/hbtp_vm.h> + +#define hbtp_vm_name "hbtp_vm" + +struct hbtp_virtual_mouse { + struct input_dev *input_dev; + s32 open_count; + struct mutex mutex; + bool touch_status[HBTP_MAX_FINGER]; + + int enabled; + int last_x; + int last_y; + int parking_dist_x; +}; + +static struct hbtp_virtual_mouse *hbtp_vm; + +static int hbtp_vm_open(struct inode *inode, struct file *file) +{ + mutex_lock(&hbtp_vm->mutex); + if (hbtp_vm->open_count) + pr_debug("hbtp_vm was already opened\n"); + hbtp_vm->open_count++; + mutex_unlock(&hbtp_vm->mutex); + + return 0; +} + +static int hbtp_vm_release(struct inode *inode, struct file *file) +{ + mutex_lock(&hbtp_vm->mutex); + if (!hbtp_vm->open_count) { + pr_err("%s wasn't opened\n", hbtp_vm_name); + mutex_unlock(&hbtp_vm->mutex); + return -ENOTTY; + } + hbtp_vm->open_count--; + mutex_unlock(&hbtp_vm->mutex); + return 0; +} + +static int hbtp_vm_create_input_dev(void) +{ + struct input_dev *input_dev; + int error; + + input_dev = input_allocate_device(); + if (!input_dev) { + pr_err("%s: input_allocate_device failed\n", __func__); + return -ENOMEM; + } + + kfree(input_dev->name); + input_dev->name = kstrndup(hbtp_vm_name, sizeof(hbtp_vm_name), + GFP_KERNEL); + + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + __set_bit(EV_REL, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(REL_HWHEEL, input_dev->relbit); + + input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); + + error = input_register_device(input_dev); + if (error) { + pr_err("%s: input_register_device failed\n", __func__); + goto err_input_reg_dev; + } + + hbtp_vm->input_dev = input_dev; + return 0; + +err_input_reg_dev: + input_free_device(input_dev); + return error; +} + +static int hbtp_vm_report_events(struct hbtp_virtual_mouse *hbtp_data, + struct hbtp_input_mt *mt_data) +{ + struct hbtp_input_touch *tch; + int dx = 0; + int dy = 0; + + if (mt_data->num_touches > 1) { + pr_err("virtual mouse received multi touch reports\n"); + return 0; + } + + if (mt_data->num_touches == 1) { + tch = &(mt_data->touches[0]); + if (hbtp_vm->last_x != -1 && hbtp_vm->last_x != -1) { + dx = tch->x - hbtp_vm->last_x; + dy = tch->y - hbtp_vm->last_y; + } + + if (!(dx == 0 && dy == 0)) { + input_report_rel(hbtp_vm->input_dev, REL_X, dx); + input_report_rel(hbtp_vm->input_dev, REL_Y, dy); + input_sync(hbtp_vm->input_dev); + } + + hbtp_vm->last_x = tch->x; + hbtp_vm->last_y = tch->y; + } else { + hbtp_vm->last_x = -1; + hbtp_vm->last_y = -1; + } + + return 0; +} + +static int hbtp_vm_report_clicks(struct hbtp_virtual_mouse *hbtp_data, + struct hbtp_vm_click *click_data) +{ + unsigned int code = BTN_LEFT; + int value = 1; + + if (click_data->mask & HBTP_VM_BUTTON_RIGHT) + code = BTN_RIGHT; + + if (click_data->mask & HBTP_VM_BUTTON_UP) + value = 0; + + input_report_key(hbtp_vm->input_dev, code, value); + input_sync(hbtp_vm->input_dev); + + return 0; +} + +static long hbtp_vm_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int error = 0; + struct hbtp_input_mt mt_data; + struct hbtp_vm_click clik_data; + + if (!hbtp_vm) { + pr_err("%s: virtual mouse driver not initialized\n", + __func__); + return -ENOMEM; + } + + switch (cmd) { + case HBTP_VM_ENABLE: + if (hbtp_vm->enabled) { + pr_err("virtual mouse device is already enabled\n"); + return 0; + } + hbtp_vm->enabled = true; + input_report_rel(hbtp_vm->input_dev, REL_X, -2000); + input_report_rel(hbtp_vm->input_dev, REL_Y, -2000); + input_sync(hbtp_vm->input_dev); + input_report_rel(hbtp_vm->input_dev, REL_X, 100); + input_report_rel(hbtp_vm->input_dev, REL_Y, 100); + input_sync(hbtp_vm->input_dev); + + break; + + case HBTP_VM_DISABLE: + if (!hbtp_vm->enabled) { + pr_err("virtual mouse device is already disabled\n"); + return 0; + } + hbtp_vm->enabled = false; + input_report_rel(hbtp_vm->input_dev, REL_X, 2000); + input_report_rel(hbtp_vm->input_dev, REL_Y, 2000); + input_sync(hbtp_vm->input_dev); + break; + + case HBTP_VM_SET_TOUCHDATA: + if (!hbtp_vm->input_dev) { + pr_err("%s: virtual mouse input device hasn't been created\n", + __func__); + return -EFAULT; + } + + if (copy_from_user(&mt_data, (void *)arg, + sizeof(struct hbtp_input_mt))) { + pr_err("%s: Error copying data\n", __func__); + return -EFAULT; + } + + hbtp_vm_report_events(hbtp_vm, &mt_data); + error = 0; + break; + + case HBTP_VM_SEND_CLICK: + if (!hbtp_vm->input_dev) { + pr_err("%s: virtual mouse input device hasn't been created\n", + __func__); + return -EFAULT; + } + + if (copy_from_user(&clik_data, (void *)arg, + sizeof(struct hbtp_vm_click))) { + pr_err("%s: Error copying data\n", __func__); + return -EFAULT; + } + + hbtp_vm_report_clicks(hbtp_vm, &clik_data); + error = 0; + break; + default: + pr_err("%s: Unsupported ioctl command %u\n", __func__, cmd); + error = -EINVAL; + break; + } + + return error; +} + +static const struct file_operations hbtp_vm_fops = { + .owner = THIS_MODULE, + .open = hbtp_vm_open, + .release = hbtp_vm_release, + .unlocked_ioctl = hbtp_vm_ioctl, + .compat_ioctl = hbtp_vm_ioctl, +}; + +static struct miscdevice hbtp_vm_misc = { + .fops = &hbtp_vm_fops, + .minor = MISC_DYNAMIC_MINOR, + .name = hbtp_vm_name, +}; + +static int __init hbtp_vm_init(void) +{ + int error; + + hbtp_vm = kzalloc(sizeof(struct hbtp_virtual_mouse), GFP_KERNEL); + + if (!hbtp_vm) + return -ENOMEM; + + hbtp_vm->last_x = -1; + hbtp_vm->last_y = -1; + + mutex_init(&hbtp_vm->mutex); + error = misc_register(&hbtp_vm_misc); + if (error) { + pr_err("%s: misc_register failed\n", hbtp_vm_name); + goto err_misc_reg; + } + + hbtp_vm_create_input_dev(); + return 0; + +err_misc_reg: + kfree(hbtp_vm); + return error; +} + +static void __exit hbtp_vm_exit(void) +{ + misc_deregister(&hbtp_vm_misc); + if (hbtp_vm->input_dev) { + input_unregister_device(hbtp_vm->input_dev); + hbtp_vm->input_dev = NULL; + } + kfree(hbtp_vm); +} + +MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR); +MODULE_ALIAS("devname:" hbtp_vm_name); +MODULE_DESCRIPTION("kernel module to support virtual mouse"); +MODULE_LICENSE("GPL v2"); +module_init(hbtp_vm_init); +module_exit(hbtp_vm_exit); diff --git a/drivers/input/misc/ots_pat9125/Kconfig b/drivers/input/misc/ots_pat9125/Kconfig new file mode 100644 index 000000000000..af82edd0faae --- /dev/null +++ b/drivers/input/misc/ots_pat9125/Kconfig @@ -0,0 +1,14 @@ +# +# PixArt OTS switch driver configuration +# + +config INPUT_PIXART_OTS_PAT9125_SWITCH + tristate "PixArt PAT9125 Rotating Switch driver" + depends on INPUT && I2C && GPIOLIB + help + Say Y to enable support for the PixArt OTS pat9125 + rotating switch driver. + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ots_pat9125. diff --git a/drivers/input/misc/ots_pat9125/Makefile b/drivers/input/misc/ots_pat9125/Makefile new file mode 100644 index 000000000000..a697caf69644 --- /dev/null +++ b/drivers/input/misc/ots_pat9125/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the PixArt OST switch driver. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_INPUT_PIXART_OTS_PAT9125_SWITCH) += pat9125_linux_driver.o pixart_ots.o diff --git a/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c new file mode 100644 index 000000000000..fa5e4cca129d --- /dev/null +++ b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c @@ -0,0 +1,627 @@ +/* drivers/input/misc/ots_pat9125/pat9125_linux_driver.c + * + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + */ + +#include <linux/input.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/of_gpio.h> +#include <linux/delay.h> +#include <linux/regulator/consumer.h> +#include "pixart_ots.h" + +struct pixart_pat9125_data { + struct i2c_client *client; + struct input_dev *input; + int irq_gpio; + u32 press_keycode; + bool press_en; + bool inverse_x; + bool inverse_y; + struct regulator *vdd; + struct regulator *vld; + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; +}; + +/* Declaration of suspend and resume functions */ +static int pat9125_suspend(struct device *dev); +static int pat9125_resume(struct device *dev); + +static int pat9125_i2c_write(struct i2c_client *client, u8 reg, u8 *data, + int len) +{ + u8 buf[MAX_BUF_SIZE]; + int ret = 0, i; + struct device *dev = &client->dev; + + buf[0] = reg; + if (len >= MAX_BUF_SIZE) { + dev_err(dev, "%s Failed: buffer size is %d [Max Limit is %d]\n", + __func__, len, MAX_BUF_SIZE); + return -ENODEV; + } + for (i = 0 ; i < len; i++) + buf[i+1] = data[i]; + /* Returns negative errno, or else the number of bytes written. */ + ret = i2c_master_send(client, buf, len+1); + if (ret != len+1) + dev_err(dev, "%s Failed: writing to reg 0x%x\n", __func__, reg); + + return ret; +} + +static int pat9125_i2c_read(struct i2c_client *client, u8 reg, u8 *data) +{ + u8 buf[MAX_BUF_SIZE]; + int ret; + struct device *dev = &client->dev; + + buf[0] = reg; + /* + * If everything went ok (1 msg transmitted), return #bytes transmitted, + * else error code. thus if transmit is ok return value 1 + */ + ret = i2c_master_send(client, buf, 1); + if (ret != 1) { + dev_err(dev, "%s Failed: writing to reg 0x%x\n", __func__, reg); + return ret; + } + /* returns negative errno, or else the number of bytes read */ + ret = i2c_master_recv(client, buf, 1); + if (ret != 1) { + dev_err(dev, "%s Failed: reading reg 0x%x\n", __func__, reg); + return ret; + } + *data = buf[0]; + + return ret; +} + +u8 read_data(struct i2c_client *client, u8 addr) +{ + u8 data = 0xff; + + pat9125_i2c_read(client, addr, &data); + return data; +} + +void write_data(struct i2c_client *client, u8 addr, u8 data) +{ + pat9125_i2c_write(client, addr, &data, 1); +} + +static irqreturn_t pat9125_irq(int irq, void *dev_data) +{ + u8 delta_x = 0, delta_y = 0, motion; + struct pixart_pat9125_data *data = dev_data; + struct input_dev *ipdev = data->input; + struct device *dev = &data->client->dev; + + motion = read_data(data->client, PIXART_PAT9125_MOTION_STATUS_REG); + do { + /* check if MOTION bit is set or not */ + if (motion & PIXART_PAT9125_VALID_MOTION_DATA) { + delta_x = read_data(data->client, + PIXART_PAT9125_DELTA_X_LO_REG); + delta_y = read_data(data->client, + PIXART_PAT9125_DELTA_Y_LO_REG); + + /* Inverse x depending upon the device orientation */ + delta_x = (data->inverse_x) ? -delta_x : delta_x; + /* Inverse y depending upon the device orientation */ + delta_y = (data->inverse_y) ? -delta_y : delta_y; + } + + dev_dbg(dev, "motion = %x, delta_x = %x, delta_y = %x\n", + motion, delta_x, delta_y); + + if (delta_x != 0) { + /* Send delta_x as REL_WHEEL for rotation */ + input_report_rel(ipdev, REL_WHEEL, (s8) delta_x); + input_sync(ipdev); + } + + if (data->press_en && delta_y != 0) { + if ((s8) delta_y > 0) { + /* Send DOWN event for press keycode */ + input_report_key(ipdev, data->press_keycode, 1); + input_sync(ipdev); + } else { + /* Send UP event for press keycode */ + input_report_key(ipdev, data->press_keycode, 0); + input_sync(ipdev); + } + } + usleep_range(PIXART_SAMPLING_PERIOD_US_MIN, + PIXART_SAMPLING_PERIOD_US_MAX); + + motion = read_data(data->client, + PIXART_PAT9125_MOTION_STATUS_REG); + } while (motion & PIXART_PAT9125_VALID_MOTION_DATA); + + return IRQ_HANDLED; +} + +static ssize_t pat9125_suspend_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct pixart_pat9125_data *data = + (struct pixart_pat9125_data *) dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int mode; + + if (kstrtoint(buf, 10, &mode)) { + dev_err(dev, "failed to read input for sysfs\n"); + return -EINVAL; + } + + if (mode == 1) + pat9125_suspend(&client->dev); + else if (mode == 0) + pat9125_resume(&client->dev); + + return count; +} + +static ssize_t pat9125_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char s[256], *p = s; + int reg_data = 0, i; + long rd_addr, wr_addr, wr_data; + struct pixart_pat9125_data *data = + (struct pixart_pat9125_data *) dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + for (i = 0; i < sizeof(s); i++) + s[i] = buf[i]; + *(s+1) = '\0'; + *(s+4) = '\0'; + *(s+7) = '\0'; + /* example(in console): echo w 12 34 > rw_reg */ + if (*p == 'w') { + p += 2; + if (!kstrtol(p, 16, &wr_addr)) { + p += 3; + if (!kstrtol(p, 16, &wr_data)) { + dev_dbg(dev, "w 0x%x 0x%x\n", + (u8)wr_addr, (u8)wr_data); + write_data(client, (u8)wr_addr, (u8)wr_data); + } + } + } + /* example(in console): echo r 12 > rw_reg */ + else if (*p == 'r') { + p += 2; + + if (!kstrtol(p, 16, &rd_addr)) { + reg_data = read_data(client, (u8)rd_addr); + dev_dbg(dev, "r 0x%x 0x%x\n", + (unsigned int)rd_addr, reg_data); + } + } + return count; +} + +static ssize_t pat9125_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return 0; +} + +static DEVICE_ATTR(suspend, S_IRUGO | S_IWUSR | S_IWGRP, + NULL, pat9125_suspend_store); +static DEVICE_ATTR(test, S_IRUGO | S_IWUSR | S_IWGRP, + pat9125_test_show, pat9125_test_store); + +static struct attribute *pat9125_attr_list[] = { + &dev_attr_test.attr, + &dev_attr_suspend.attr, + NULL, +}; + +static struct attribute_group pat9125_attr_grp = { + .attrs = pat9125_attr_list, +}; + +static int pixart_pinctrl_init(struct pixart_pat9125_data *data) +{ + int err; + struct device *dev = &data->client->dev; + + data->pinctrl = devm_pinctrl_get(&(data->client->dev)); + if (IS_ERR_OR_NULL(data->pinctrl)) { + err = PTR_ERR(data->pinctrl); + dev_err(dev, "Target does not use pinctrl %d\n", err); + return err; + } + + data->pinctrl_state_active = pinctrl_lookup_state(data->pinctrl, + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(data->pinctrl_state_active)) { + err = PTR_ERR(data->pinctrl_state_active); + dev_err(dev, "Can not lookup active pinctrl state %d\n", err); + return err; + } + + data->pinctrl_state_suspend = pinctrl_lookup_state(data->pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(data->pinctrl_state_suspend)) { + err = PTR_ERR(data->pinctrl_state_suspend); + dev_err(dev, "Can not lookup suspend pinctrl state %d\n", err); + return err; + } + + data->pinctrl_state_release = pinctrl_lookup_state(data->pinctrl, + PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(data->pinctrl_state_release)) { + err = PTR_ERR(data->pinctrl_state_release); + dev_err(dev, "Can not lookup release pinctrl state %d\n", err); + return err; + } + return 0; +} + +static int pat9125_regulator_init(struct pixart_pat9125_data *data) +{ + int err = 0; + struct device *dev = &data->client->dev; + + data->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(data->vdd)) { + dev_err(dev, "Failed to get regulator vdd %ld\n", + PTR_ERR(data->vdd)); + return PTR_ERR(data->vdd); + } + + data->vld = devm_regulator_get(dev, "vld"); + if (IS_ERR(data->vld)) { + dev_err(dev, "Failed to get regulator vld %ld\n", + PTR_ERR(data->vld)); + return PTR_ERR(data->vld); + } + + err = regulator_set_voltage(data->vdd, VDD_VTG_MIN_UV, VDD_VTG_MAX_UV); + if (err) { + dev_err(dev, "Failed to set voltage for vdd reg %d\n", err); + return err; + } + + err = regulator_set_load(data->vdd, VDD_ACTIVE_LOAD_UA); + if (err < 0) { + dev_err(dev, "Failed to set opt mode for vdd reg %d\n", err); + return err; + } + + err = regulator_set_voltage(data->vld, VLD_VTG_MIN_UV, VLD_VTG_MAX_UV); + if (err) { + dev_err(dev, "Failed to set voltage for vld reg %d\n", err); + return err; + } + + err = regulator_set_load(data->vld, VLD_ACTIVE_LOAD_UA); + if (err < 0) { + dev_err(dev, "Failed to set opt mode for vld reg %d\n", err); + return err; + } + + return 0; +} + +static int pat9125_power_on(struct pixart_pat9125_data *data, bool on) +{ + int err = 0; + struct device *dev = &data->client->dev; + + if (on) { + err = regulator_enable(data->vdd); + if (err) { + dev_err(dev, "Failed to enable vdd reg %d\n", err); + return err; + } + + usleep_range(DELAY_BETWEEN_REG_US, DELAY_BETWEEN_REG_US + 1); + + /* + * Initialize pixart sensor after some delay, when vdd + * regulator is enabled + */ + if (!ots_sensor_init(data->client)) { + err = -ENODEV; + dev_err(dev, "Failed to initialize sensor %d\n", err); + return err; + } + + err = regulator_enable(data->vld); + if (err) { + dev_err(dev, "Failed to enable vld reg %d\n", err); + return err; + } + } else { + err = regulator_disable(data->vld); + if (err) { + dev_err(dev, "Failed to disable vld reg %d\n", err); + return err; + } + + err = regulator_disable(data->vdd); + if (err) { + dev_err(dev, "Failed to disable vdd reg %d\n", err); + return err; + } + } + + return 0; +} + +static int pat9125_parse_dt(struct device *dev, + struct pixart_pat9125_data *data) +{ + struct device_node *np = dev->of_node; + u32 temp_val; + int ret; + + data->inverse_x = of_property_read_bool(np, "pixart,inverse-x"); + data->inverse_y = of_property_read_bool(np, "pixart,inverse-y"); + data->press_en = of_property_read_bool(np, "pixart,press-enabled"); + if (data->press_en) { + ret = of_property_read_u32(np, "pixart,press-keycode", + &temp_val); + if (!ret) { + data->press_keycode = temp_val; + } else { + dev_err(dev, "Unable to parse press-keycode\n"); + return ret; + } + } + + data->irq_gpio = of_get_named_gpio_flags(np, "pixart,irq-gpio", + 0, NULL); + + return 0; +} + +static int pat9125_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err = 0; + struct pixart_pat9125_data *data; + struct input_dev *input; + struct device *dev = &client->dev; + + err = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE); + if (err < 0) { + dev_err(dev, "I2C not supported\n"); + return -ENXIO; + } + + if (client->dev.of_node) { + data = devm_kzalloc(dev, sizeof(struct pixart_pat9125_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + err = pat9125_parse_dt(dev, data); + if (err) { + dev_err(dev, "DT parsing failed, errno:%d\n", err); + return err; + } + } else { + data = client->dev.platform_data; + if (!data) { + dev_err(dev, "Invalid pat9125 data\n"); + return -EINVAL; + } + } + data->client = client; + + input = devm_input_allocate_device(dev); + if (!input) { + dev_err(dev, "Failed to alloc input device\n"); + return -ENOMEM; + } + + input_set_capability(input, EV_REL, REL_WHEEL); + if (data->press_en) + input_set_capability(input, EV_KEY, data->press_keycode); + + i2c_set_clientdata(client, data); + input_set_drvdata(input, data); + input->name = PAT9125_DEV_NAME; + + data->input = input; + err = input_register_device(data->input); + if (err < 0) { + dev_err(dev, "Failed to register input device\n"); + return err; + } + + err = pixart_pinctrl_init(data); + if (!err && data->pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is found + * let pins to be configured in active state. If not + * found continue further without error. + */ + err = pinctrl_select_state(data->pinctrl, + data->pinctrl_state_active); + if (err < 0) + dev_err(dev, "Could not set pin to active state %d\n", + err); + } else { + if (gpio_is_valid(data->irq_gpio)) { + err = devm_gpio_request(dev, data->irq_gpio, + "pixart_pat9125_irq_gpio"); + if (err) { + dev_err(dev, "Couldn't request gpio %d\n", err); + return err; + } + err = gpio_direction_input(data->irq_gpio); + if (err) { + dev_err(dev, "Couldn't set dir for gpio %d\n", + err); + return err; + } + } else { + dev_err(dev, "Invalid gpio %d\n", data->irq_gpio); + return -EINVAL; + } + } + + err = pat9125_regulator_init(data); + if (err) { + dev_err(dev, "Failed to init regulator, %d\n", err); + return err; + } + + err = pat9125_power_on(data, true); + if (err) { + dev_err(dev, "Failed to power-on the sensor %d\n", err); + goto err_power_on; + } + + err = devm_request_threaded_irq(dev, client->irq, NULL, pat9125_irq, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW, + "pixart_pat9125_irq", data); + if (err) { + dev_err(dev, "Req irq %d failed, errno:%d\n", client->irq, err); + goto err_request_threaded_irq; + } + + err = sysfs_create_group(&(input->dev.kobj), &pat9125_attr_grp); + if (err) { + dev_err(dev, "Failed to create sysfs group, errno:%d\n", err); + goto err_sysfs_create; + } + + return 0; + +err_sysfs_create: +err_request_threaded_irq: +err_power_on: + regulator_set_load(data->vdd, 0); + regulator_set_load(data->vld, 0); + if (pat9125_power_on(data, false) < 0) + dev_err(dev, "Failed to disable regulators\n"); + if (data->pinctrl) + if (pinctrl_select_state(data->pinctrl, + data->pinctrl_state_release) < 0) + dev_err(dev, "Couldn't set pin to release state\n"); + + return err; +} + +static int pat9125_i2c_remove(struct i2c_client *client) +{ + struct pixart_pat9125_data *data = i2c_get_clientdata(client); + struct device *dev = &data->client->dev; + + sysfs_remove_group(&(data->input->dev.kobj), &pat9125_attr_grp); + if (data->pinctrl) + if (pinctrl_select_state(data->pinctrl, + data->pinctrl_state_release) < 0) + dev_err(dev, "Couldn't set pin to release state\n"); + regulator_set_load(data->vdd, 0); + regulator_set_load(data->vld, 0); + pat9125_power_on(data, false); + return 0; +} + +static int pat9125_suspend(struct device *dev) +{ + int rc; + struct pixart_pat9125_data *data = + (struct pixart_pat9125_data *) dev_get_drvdata(dev); + + disable_irq(data->client->irq); + if (data->pinctrl) { + rc = pinctrl_select_state(data->pinctrl, + data->pinctrl_state_suspend); + if (rc < 0) + dev_err(dev, "Could not set pin to suspend state %d\n", + rc); + } + + rc = pat9125_power_on(data, false); + if (rc) { + dev_err(dev, "Failed to disable regulators %d\n", rc); + return rc; + } + + return 0; +} + +static int pat9125_resume(struct device *dev) +{ + int rc; + struct pixart_pat9125_data *data = + (struct pixart_pat9125_data *) dev_get_drvdata(dev); + + if (data->pinctrl) { + rc = pinctrl_select_state(data->pinctrl, + data->pinctrl_state_active); + if (rc < 0) + dev_err(dev, "Could not set pin to active state %d\n", + rc); + } + + rc = pat9125_power_on(data, true); + if (rc) { + dev_err(dev, "Failed to power-on the sensor %d\n", rc); + goto err_sensor_init; + } + + enable_irq(data->client->irq); + + return 0; + +err_sensor_init: + if (data->pinctrl) + if (pinctrl_select_state(data->pinctrl, + data->pinctrl_state_suspend) < 0) + dev_err(dev, "Couldn't set pin to suspend state\n"); + if (pat9125_power_on(data, false) < 0) + dev_err(dev, "Failed to disable regulators\n"); + + return rc; +} + +static const struct i2c_device_id pat9125_device_id[] = { + {PAT9125_DEV_NAME, 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, pat9125_device_id); + +static const struct dev_pm_ops pat9125_pm_ops = { + .suspend = pat9125_suspend, + .resume = pat9125_resume +}; + +static const struct of_device_id pixart_pat9125_match_table[] = { + { .compatible = "pixart,pat9125",}, + { }, +}; + +static struct i2c_driver pat9125_i2c_driver = { + .driver = { + .name = PAT9125_DEV_NAME, + .owner = THIS_MODULE, + .pm = &pat9125_pm_ops, + .of_match_table = pixart_pat9125_match_table, + }, + .probe = pat9125_i2c_probe, + .remove = pat9125_i2c_remove, + .id_table = pat9125_device_id, +}; +module_i2c_driver(pat9125_i2c_driver); + +MODULE_AUTHOR("pixart"); +MODULE_DESCRIPTION("pixart pat9125 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/ots_pat9125/pixart_ots.c b/drivers/input/misc/ots_pat9125/pixart_ots.c new file mode 100644 index 000000000000..3d44d068423a --- /dev/null +++ b/drivers/input/misc/ots_pat9125/pixart_ots.c @@ -0,0 +1,78 @@ +/* drivers/input/misc/ots_pat9125/pixart_ots.c + * + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + */ + +#include "pixart_platform.h" +#include "pixart_ots.h" + +static void ots_write_read(struct i2c_client *client, u8 address, u8 wdata) +{ + u8 read_value; + + do { + write_data(client, address, wdata); + read_value = read_data(client, address); + } while (read_value != wdata); +} + +bool ots_sensor_init(struct i2c_client *client) +{ + u8 sensor_pid = 0; + bool read_id_ok = false; + + /* + * Read sensor_pid in address 0x00 to check if the + * serial link is valid, read value should be 0x31. + */ + sensor_pid = read_data(client, PIXART_PAT9125_PRODUCT_ID1_REG); + + if (sensor_pid == PIXART_PAT9125_SENSOR_ID) { + read_id_ok = true; + + /* + * PAT9125 sensor recommended settings: + * switch to bank0, not allowed to perform ots_write_read + */ + write_data(client, PIXART_PAT9125_SELECT_BANK_REG, + PIXART_PAT9125_BANK0); + /* + * software reset (i.e. set bit7 to 1). + * It will reset to 0 automatically + * so perform OTS_RegWriteRead is not allowed. + */ + write_data(client, PIXART_PAT9125_CONFIG_REG, + PIXART_PAT9125_RESET); + + /* delay 1ms */ + usleep_range(RESET_DELAY_US, RESET_DELAY_US + 1); + + /* disable write protect */ + ots_write_read(client, PIXART_PAT9125_WRITE_PROTECT_REG, + PIXART_PAT9125_DISABLE_WRITE_PROTECT); + /* set X-axis resolution (depends on application) */ + ots_write_read(client, PIXART_PAT9125_SET_CPI_RES_X_REG, + PIXART_PAT9125_CPI_RESOLUTION_X); + /* set Y-axis resolution (depends on application) */ + ots_write_read(client, PIXART_PAT9125_SET_CPI_RES_Y_REG, + PIXART_PAT9125_CPI_RESOLUTION_Y); + /* set 12-bit X/Y data format (depends on application) */ + ots_write_read(client, PIXART_PAT9125_ORIENTATION_REG, + PIXART_PAT9125_MOTION_DATA_LENGTH); + /* ONLY for VDD=VDDA=1.7~1.9V: for power saving */ + ots_write_read(client, PIXART_PAT9125_VOLTAGE_SEGMENT_SEL_REG, + PIXART_PAT9125_LOW_VOLTAGE_SEGMENT); + + if (read_data(client, PIXART_PAT9125_MISC2_REG) == 0x04) { + ots_write_read(client, PIXART_PAT9125_MISC2_REG, 0x08); + if (read_data(client, PIXART_PAT9125_MISC1_REG) == 0x10) + ots_write_read(client, PIXART_PAT9125_MISC1_REG, + 0x19); + } + /* enable write protect */ + ots_write_read(client, PIXART_PAT9125_WRITE_PROTECT_REG, + PIXART_PAT9125_ENABLE_WRITE_PROTECT); + } + return read_id_ok; +} diff --git a/drivers/input/misc/ots_pat9125/pixart_ots.h b/drivers/input/misc/ots_pat9125/pixart_ots.h new file mode 100644 index 000000000000..5320d588d341 --- /dev/null +++ b/drivers/input/misc/ots_pat9125/pixart_ots.h @@ -0,0 +1,58 @@ +/* drivers/input/misc/ots_pat9125/pixart_ots.h + * + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + */ + +#ifndef __PIXART_OTS_H_ +#define __PIXART_OTS_H_ + +#define PAT9125_DEV_NAME "pixart_pat9125" +#define MAX_BUF_SIZE 20 +#define RESET_DELAY_US 1000 +#define PINCTRL_STATE_ACTIVE "pmx_rot_switch_active" +#define PINCTRL_STATE_SUSPEND "pmx_rot_switch_suspend" +#define PINCTRL_STATE_RELEASE "pmx_rot_switch_release" +#define VDD_VTG_MIN_UV 1800000 +#define VDD_VTG_MAX_UV 1800000 +#define VDD_ACTIVE_LOAD_UA 10000 +#define VLD_VTG_MIN_UV 2800000 +#define VLD_VTG_MAX_UV 3300000 +#define VLD_ACTIVE_LOAD_UA 10000 +#define DELAY_BETWEEN_REG_US 20000 + +/* Register addresses */ +#define PIXART_PAT9125_PRODUCT_ID1_REG 0x00 +#define PIXART_PAT9125_PRODUCT_ID2_REG 0x01 +#define PIXART_PAT9125_MOTION_STATUS_REG 0x02 +#define PIXART_PAT9125_DELTA_X_LO_REG 0x03 +#define PIXART_PAT9125_DELTA_Y_LO_REG 0x04 +#define PIXART_PAT9125_CONFIG_REG 0x06 +#define PIXART_PAT9125_WRITE_PROTECT_REG 0x09 +#define PIXART_PAT9125_SET_CPI_RES_X_REG 0x0D +#define PIXART_PAT9125_SET_CPI_RES_Y_REG 0x0E +#define PIXART_PAT9125_DELTA_XY_HI_REG 0x12 +#define PIXART_PAT9125_ORIENTATION_REG 0x19 +#define PIXART_PAT9125_VOLTAGE_SEGMENT_SEL_REG 0x4B +#define PIXART_PAT9125_SELECT_BANK_REG 0x7F +#define PIXART_PAT9125_MISC1_REG 0x5D +#define PIXART_PAT9125_MISC2_REG 0x5E +/*Register configuration data */ +#define PIXART_PAT9125_SENSOR_ID 0x31 +#define PIXART_PAT9125_RESET 0x97 +#define PIXART_PAT9125_MOTION_DATA_LENGTH 0x04 +#define PIXART_PAT9125_BANK0 0x00 +#define PIXART_PAT9125_DISABLE_WRITE_PROTECT 0x5A +#define PIXART_PAT9125_ENABLE_WRITE_PROTECT 0x00 +#define PIXART_PAT9125_CPI_RESOLUTION_X 0x65 +#define PIXART_PAT9125_CPI_RESOLUTION_Y 0xFF +#define PIXART_PAT9125_LOW_VOLTAGE_SEGMENT 0x04 +#define PIXART_PAT9125_VALID_MOTION_DATA 0x80 + +#define PIXART_SAMPLING_PERIOD_US_MIN 4000 +#define PIXART_SAMPLING_PERIOD_US_MAX 8000 + +/* Export functions */ +bool ots_sensor_init(struct i2c_client *); + +#endif diff --git a/drivers/input/misc/ots_pat9125/pixart_platform.h b/drivers/input/misc/ots_pat9125/pixart_platform.h new file mode 100644 index 000000000000..1fe448fdc2cb --- /dev/null +++ b/drivers/input/misc/ots_pat9125/pixart_platform.h @@ -0,0 +1,17 @@ +/* drivers/input/misc/ots_pat9125/pixart_platform.h + * + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + */ + +#ifndef __PIXART_PLATFORM_H_ +#define __PIXART_PLATFORM_H_ + +#include <linux/i2c.h> +#include <linux/delay.h> + +/* extern functions */ +extern unsigned char read_data(struct i2c_client *, u8 addr); +extern void write_data(struct i2c_client *, u8 addr, u8 data); + +#endif diff --git a/drivers/input/misc/qpnp-power-on.c b/drivers/input/misc/qpnp-power-on.c new file mode 100644 index 000000000000..339f94c072f4 --- /dev/null +++ b/drivers/input/misc/qpnp-power-on.c @@ -0,0 +1,2413 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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/debugfs.h> +#include <linux/kernel.h> +#include <linux/regmap.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/spmi.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/input.h> +#include <linux/log2.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/input/qpnp-power-on.h> +#include <linux/power_supply.h> + +#define PMIC_VER_8941 0x01 +#define PMIC_VERSION_REG 0x0105 +#define PMIC_VERSION_REV4_REG 0x0103 + +#define PMIC8941_V1_REV4 0x01 +#define PMIC8941_V2_REV4 0x02 +#define PON_PRIMARY 0x01 +#define PON_SECONDARY 0x02 +#define PON_1REG 0x03 +#define PON_GEN2_PRIMARY 0x04 +#define PON_GEN2_SECONDARY 0x05 + +#define PON_OFFSET(subtype, offset_gen1, offset_gen2) \ + (((subtype == PON_PRIMARY) || \ + (subtype == PON_SECONDARY) || \ + (subtype == PON_1REG)) ? offset_gen1 : offset_gen2) + +/* Common PNP defines */ +#define QPNP_PON_REVISION2(pon) ((pon)->base + 0x01) +#define QPNP_PON_PERPH_SUBTYPE(pon) ((pon)->base + 0x05) + +/* PON common register addresses */ +#define QPNP_PON_RT_STS(pon) ((pon)->base + 0x10) +#define QPNP_PON_PULL_CTL(pon) ((pon)->base + 0x70) +#define QPNP_PON_DBC_CTL(pon) ((pon)->base + 0x71) + +/* PON/RESET sources register addresses */ +#define QPNP_PON_REASON1(pon) \ + ((pon)->base + PON_OFFSET((pon)->subtype, 0x8, 0xC0)) +#define QPNP_PON_WARM_RESET_REASON1(pon) \ + ((pon)->base + PON_OFFSET((pon)->subtype, 0xA, 0xC2)) +#define QPNP_POFF_REASON1(pon) \ + ((pon)->base + PON_OFFSET((pon)->subtype, 0xC, 0xC5)) +#define QPNP_PON_WARM_RESET_REASON2(pon) ((pon)->base + 0xB) +#define QPNP_PON_OFF_REASON(pon) ((pon)->base + 0xC7) +#define QPNP_FAULT_REASON1(pon) ((pon)->base + 0xC8) +#define QPNP_S3_RESET_REASON(pon) ((pon)->base + 0xCA) +#define QPNP_PON_KPDPWR_S1_TIMER(pon) ((pon)->base + 0x40) +#define QPNP_PON_KPDPWR_S2_TIMER(pon) ((pon)->base + 0x41) +#define QPNP_PON_KPDPWR_S2_CNTL(pon) ((pon)->base + 0x42) +#define QPNP_PON_KPDPWR_S2_CNTL2(pon) ((pon)->base + 0x43) +#define QPNP_PON_RESIN_S1_TIMER(pon) ((pon)->base + 0x44) +#define QPNP_PON_RESIN_S2_TIMER(pon) ((pon)->base + 0x45) +#define QPNP_PON_RESIN_S2_CNTL(pon) ((pon)->base + 0x46) +#define QPNP_PON_RESIN_S2_CNTL2(pon) ((pon)->base + 0x47) +#define QPNP_PON_KPDPWR_RESIN_S1_TIMER(pon) ((pon)->base + 0x48) +#define QPNP_PON_KPDPWR_RESIN_S2_TIMER(pon) ((pon)->base + 0x49) +#define QPNP_PON_KPDPWR_RESIN_S2_CNTL(pon) ((pon)->base + 0x4A) +#define QPNP_PON_KPDPWR_RESIN_S2_CNTL2(pon) ((pon)->base + 0x4B) +#define QPNP_PON_PS_HOLD_RST_CTL(pon) ((pon)->base + 0x5A) +#define QPNP_PON_PS_HOLD_RST_CTL2(pon) ((pon)->base + 0x5B) +#define QPNP_PON_WD_RST_S2_CTL(pon) ((pon)->base + 0x56) +#define QPNP_PON_WD_RST_S2_CTL2(pon) ((pon)->base + 0x57) +#define QPNP_PON_S3_SRC(pon) ((pon)->base + 0x74) +#define QPNP_PON_S3_DBC_CTL(pon) ((pon)->base + 0x75) +#define QPNP_PON_SMPL_CTL(pon) ((pon)->base + 0x7F) +#define QPNP_PON_TRIGGER_EN(pon) ((pon)->base + 0x80) +#define QPNP_PON_XVDD_RB_SPARE(pon) ((pon)->base + 0x8E) +#define QPNP_PON_SOFT_RB_SPARE(pon) ((pon)->base + 0x8F) +#define QPNP_PON_SEC_ACCESS(pon) ((pon)->base + 0xD0) + +#define QPNP_PON_SEC_UNLOCK 0xA5 + +#define QPNP_PON_WARM_RESET_TFT BIT(4) + +#define QPNP_PON_RESIN_PULL_UP BIT(0) +#define QPNP_PON_KPDPWR_PULL_UP BIT(1) +#define QPNP_PON_CBLPWR_PULL_UP BIT(2) +#define QPNP_PON_FAULT_PULL_UP BIT(4) +#define QPNP_PON_S2_CNTL_EN BIT(7) +#define QPNP_PON_S2_RESET_ENABLE BIT(7) +#define QPNP_PON_DELAY_BIT_SHIFT 6 +#define QPNP_PON_GEN2_DELAY_BIT_SHIFT 14 + +#define QPNP_PON_S1_TIMER_MASK (0xF) +#define QPNP_PON_S2_TIMER_MASK (0x7) +#define QPNP_PON_S2_CNTL_TYPE_MASK (0xF) + +#define QPNP_PON_DBC_DELAY_MASK(pon) \ + PON_OFFSET((pon)->subtype, 0x7, 0xF) + +#define QPNP_PON_KPDPWR_N_SET BIT(0) +#define QPNP_PON_RESIN_N_SET BIT(1) +#define QPNP_PON_CBLPWR_N_SET BIT(2) +#define QPNP_PON_RESIN_BARK_N_SET BIT(4) +#define QPNP_PON_KPDPWR_RESIN_BARK_N_SET BIT(5) + +#define QPNP_PON_WD_EN BIT(7) +#define QPNP_PON_RESET_EN BIT(7) +#define QPNP_PON_POWER_OFF_MASK 0xF +#define QPNP_GEN2_POFF_SEQ BIT(7) +#define QPNP_GEN2_FAULT_SEQ BIT(6) +#define QPNP_GEN2_S3_RESET_SEQ BIT(5) + +#define QPNP_PON_S3_SRC_KPDPWR 0 +#define QPNP_PON_S3_SRC_RESIN 1 +#define QPNP_PON_S3_SRC_KPDPWR_AND_RESIN 2 +#define QPNP_PON_S3_SRC_KPDPWR_OR_RESIN 3 +#define QPNP_PON_S3_SRC_MASK 0x3 +#define QPNP_PON_HARD_RESET_MASK GENMASK(7, 5) + +#define QPNP_PON_UVLO_DLOAD_EN BIT(7) +#define QPNP_PON_SMPL_EN BIT(7) + +/* Ranges */ +#define QPNP_PON_S1_TIMER_MAX 10256 +#define QPNP_PON_S2_TIMER_MAX 2000 +#define QPNP_PON_S3_TIMER_SECS_MAX 128 +#define QPNP_PON_S3_DBC_DELAY_MASK 0x07 +#define QPNP_PON_RESET_TYPE_MAX 0xF +#define PON_S1_COUNT_MAX 0xF +#define QPNP_PON_MIN_DBC_US (USEC_PER_SEC / 64) +#define QPNP_PON_MAX_DBC_US (USEC_PER_SEC * 2) +#define QPNP_PON_GEN2_MIN_DBC_US 62 +#define QPNP_PON_GEN2_MAX_DBC_US (USEC_PER_SEC / 4) + +#define QPNP_KEY_STATUS_DELAY msecs_to_jiffies(250) + +#define QPNP_PON_BUFFER_SIZE 9 + +#define QPNP_POFF_REASON_UVLO 13 + +enum qpnp_pon_version { + QPNP_PON_GEN1_V1, + QPNP_PON_GEN1_V2, + QPNP_PON_GEN2, +}; + +enum pon_type { + PON_KPDPWR, + PON_RESIN, + PON_CBLPWR, + PON_KPDPWR_RESIN, +}; + +struct qpnp_pon_config { + u32 pon_type; + u32 support_reset; + u32 key_code; + u32 s1_timer; + u32 s2_timer; + u32 s2_type; + u32 pull_up; + u32 state_irq; + u32 bark_irq; + u16 s2_cntl_addr; + u16 s2_cntl2_addr; + bool old_state; + bool use_bark; + bool config_reset; +}; + +struct pon_regulator { + struct qpnp_pon *pon; + struct regulator_dev *rdev; + struct regulator_desc rdesc; + u32 addr; + u32 bit; + bool enabled; +}; + +struct qpnp_pon { + struct platform_device *pdev; + struct regmap *regmap; + struct input_dev *pon_input; + struct qpnp_pon_config *pon_cfg; + struct pon_regulator *pon_reg_cfg; + struct list_head list; + struct delayed_work bark_work; + struct dentry *debugfs; + int pon_trigger_reason; + int pon_power_off_reason; + int num_pon_reg; + int num_pon_config; + u32 dbc_time_us; + u32 uvlo; + int warm_reset_poff_type; + int hard_reset_poff_type; + int shutdown_poff_type; + u16 base; + u8 subtype; + u8 pon_ver; + u8 warm_reset_reason1; + u8 warm_reset_reason2; + bool is_spon; + bool store_hard_reset_reason; + bool kpdpwr_dbc_enable; + ktime_t kpdpwr_last_release_time; +}; + +static int pon_ship_mode_en; +module_param_named( + ship_mode_en, pon_ship_mode_en, int, 0600 +); + +static struct qpnp_pon *sys_reset_dev; +static DEFINE_SPINLOCK(spon_list_slock); +static LIST_HEAD(spon_dev_list); + +static u32 s1_delay[PON_S1_COUNT_MAX + 1] = { + 0, 32, 56, 80, 138, 184, 272, 408, 608, 904, 1352, 2048, + 3072, 4480, 6720, 10256 +}; + +static const char * const qpnp_pon_reason[] = { + [0] = "Triggered from Hard Reset", + [1] = "Triggered from SMPL (sudden momentary power loss)", + [2] = "Triggered from RTC (RTC alarm expiry)", + [3] = "Triggered from DC (DC charger insertion)", + [4] = "Triggered from USB (USB charger insertion)", + [5] = "Triggered from PON1 (secondary PMIC)", + [6] = "Triggered from CBL (external power supply)", + [7] = "Triggered from KPD (power key press)", +}; + +#define POFF_REASON_FAULT_OFFSET 16 +#define POFF_REASON_S3_RESET_OFFSET 32 +static const char * const qpnp_poff_reason[] = { + /* QPNP_PON_GEN1 POFF reasons */ + [0] = "Triggered from SOFT (Software)", + [1] = "Triggered from PS_HOLD (PS_HOLD/MSM controlled shutdown)", + [2] = "Triggered from PMIC_WD (PMIC watchdog)", + [3] = "Triggered from GP1 (Keypad_Reset1)", + [4] = "Triggered from GP2 (Keypad_Reset2)", + [5] = "Triggered from KPDPWR_AND_RESIN (Simultaneous power key and reset line)", + [6] = "Triggered from RESIN_N (Reset line/Volume Down Key)", + [7] = "Triggered from KPDPWR_N (Long Power Key hold)", + [8] = "N/A", + [9] = "N/A", + [10] = "N/A", + [11] = "Triggered from CHARGER (Charger ENUM_TIMER, BOOT_DONE)", + [12] = "Triggered from TFT (Thermal Fault Tolerance)", + [13] = "Triggered from UVLO (Under Voltage Lock Out)", + [14] = "Triggered from OTST3 (Overtemp)", + [15] = "Triggered from STAGE3 (Stage 3 reset)", + + /* QPNP_PON_GEN2 FAULT reasons */ + [16] = "Triggered from GP_FAULT0", + [17] = "Triggered from GP_FAULT1", + [18] = "Triggered from GP_FAULT2", + [19] = "Triggered from GP_FAULT3", + [20] = "Triggered from MBG_FAULT", + [21] = "Triggered from OVLO (Over Voltage Lock Out)", + [22] = "Triggered from UVLO (Under Voltage Lock Out)", + [23] = "Triggered from AVDD_RB", + [24] = "N/A", + [25] = "N/A", + [26] = "N/A", + [27] = "Triggered from FAULT_FAULT_N", + [28] = "Triggered from FAULT_PBS_WATCHDOG_TO", + [29] = "Triggered from FAULT_PBS_NACK", + [30] = "Triggered from FAULT_RESTART_PON", + [31] = "Triggered from OTST3 (Overtemp)", + + /* QPNP_PON_GEN2 S3_RESET reasons */ + [32] = "N/A", + [33] = "N/A", + [34] = "N/A", + [35] = "N/A", + [36] = "Triggered from S3_RESET_FAULT_N", + [37] = "Triggered from S3_RESET_PBS_WATCHDOG_TO", + [38] = "Triggered from S3_RESET_PBS_NACK", + [39] = "Triggered from S3_RESET_KPDPWR_ANDOR_RESIN (power key and/or reset line)", +}; + +static int +qpnp_pon_masked_write(struct qpnp_pon *pon, u16 addr, u8 mask, u8 val) +{ + int rc; + + rc = regmap_update_bits(pon->regmap, addr, mask, val); + if (rc) + dev_err(&pon->pdev->dev, + "Unable to regmap_update_bits to addr=%hx, rc(%d)\n", + addr, rc); + return rc; +} + +static bool is_pon_gen1(struct qpnp_pon *pon) +{ + return pon->subtype == PON_PRIMARY || + pon->subtype == PON_SECONDARY; +} + +static bool is_pon_gen2(struct qpnp_pon *pon) +{ + return pon->subtype == PON_GEN2_PRIMARY || + pon->subtype == PON_GEN2_SECONDARY; +} + +/** + * qpnp_pon_set_restart_reason - Store device restart reason in PMIC register. + * + * Returns = 0 if PMIC feature is not available or store restart reason + * successfully. + * Returns > 0 for errors + * + * This function is used to store device restart reason in PMIC register. + * It checks here to see if the restart reason register has been specified. + * If it hasn't, this function should immediately return 0 + */ +int qpnp_pon_set_restart_reason(enum pon_restart_reason reason) +{ + int rc = 0; + struct qpnp_pon *pon = sys_reset_dev; + + if (!pon) + return 0; + + if (!pon->store_hard_reset_reason) + return 0; + + if (is_pon_gen2(pon)) + rc = qpnp_pon_masked_write(pon, QPNP_PON_SOFT_RB_SPARE(pon), + GENMASK(7, 1), (reason << 1)); + else + rc = qpnp_pon_masked_write(pon, QPNP_PON_SOFT_RB_SPARE(pon), + GENMASK(7, 2), (reason << 2)); + + if (rc) + dev_err(&pon->pdev->dev, + "Unable to write to addr=%x, rc(%d)\n", + QPNP_PON_SOFT_RB_SPARE(pon), rc); + return rc; +} +EXPORT_SYMBOL(qpnp_pon_set_restart_reason); + +/* + * qpnp_pon_check_hard_reset_stored - Checks if the PMIC need to + * store hard reset reason. + * + * Returns true if reset reason can be stored, false if it cannot be stored + * + */ +bool qpnp_pon_check_hard_reset_stored(void) +{ + struct qpnp_pon *pon = sys_reset_dev; + + if (!pon) + return false; + + return pon->store_hard_reset_reason; +} +EXPORT_SYMBOL(qpnp_pon_check_hard_reset_stored); + +static int qpnp_pon_set_dbc(struct qpnp_pon *pon, u32 delay) +{ + int rc = 0; + u32 val; + + if (delay == pon->dbc_time_us) + goto out; + + if (pon->pon_input) + mutex_lock(&pon->pon_input->mutex); + + if (is_pon_gen2(pon)) { + if (delay < QPNP_PON_GEN2_MIN_DBC_US) + delay = QPNP_PON_GEN2_MIN_DBC_US; + else if (delay > QPNP_PON_GEN2_MAX_DBC_US) + delay = QPNP_PON_GEN2_MAX_DBC_US; + val = (delay << QPNP_PON_GEN2_DELAY_BIT_SHIFT) / USEC_PER_SEC; + } else { + if (delay < QPNP_PON_MIN_DBC_US) + delay = QPNP_PON_MIN_DBC_US; + else if (delay > QPNP_PON_MAX_DBC_US) + delay = QPNP_PON_MAX_DBC_US; + val = (delay << QPNP_PON_DELAY_BIT_SHIFT) / USEC_PER_SEC; + } + + val = ilog2(val); + rc = qpnp_pon_masked_write(pon, QPNP_PON_DBC_CTL(pon), + QPNP_PON_DBC_DELAY_MASK(pon), val); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to set PON debounce\n"); + goto unlock; + } + + pon->dbc_time_us = delay; + +unlock: + if (pon->pon_input) + mutex_unlock(&pon->pon_input->mutex); +out: + return rc; +} + +static int qpnp_pon_get_dbc(struct qpnp_pon *pon, u32 *delay) +{ + int rc; + unsigned int val; + + rc = regmap_read(pon->regmap, QPNP_PON_DBC_CTL(pon), &val); + if (rc) { + pr_err("Unable to read pon_dbc_ctl rc=%d\n", rc); + return rc; + } + val &= QPNP_PON_DBC_DELAY_MASK(pon); + + if (is_pon_gen2(pon)) + *delay = USEC_PER_SEC / + (1 << (QPNP_PON_GEN2_DELAY_BIT_SHIFT - val)); + else + *delay = USEC_PER_SEC / + (1 << (QPNP_PON_DELAY_BIT_SHIFT - val)); + + return rc; +} + +static ssize_t qpnp_pon_dbc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qpnp_pon *pon = dev_get_drvdata(dev); + + return snprintf(buf, QPNP_PON_BUFFER_SIZE, "%d\n", pon->dbc_time_us); +} + +static ssize_t qpnp_pon_dbc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct qpnp_pon *pon = dev_get_drvdata(dev); + u32 value; + int rc; + + if (size > QPNP_PON_BUFFER_SIZE) + return -EINVAL; + + rc = kstrtou32(buf, 10, &value); + if (rc) + return rc; + + rc = qpnp_pon_set_dbc(pon, value); + if (rc < 0) + return rc; + + return size; +} + +static DEVICE_ATTR(debounce_us, 0664, qpnp_pon_dbc_show, qpnp_pon_dbc_store); + +static int qpnp_pon_reset_config(struct qpnp_pon *pon, + enum pon_power_off_type type) +{ + int rc; + u16 rst_en_reg; + + if (pon->pon_ver == QPNP_PON_GEN1_V1) + rst_en_reg = QPNP_PON_PS_HOLD_RST_CTL(pon); + else + rst_en_reg = QPNP_PON_PS_HOLD_RST_CTL2(pon); + + /* + * Based on the poweroff type set for a PON device through device tree + * change the type being configured into PS_HOLD_RST_CTL. + */ + switch (type) { + case PON_POWER_OFF_WARM_RESET: + if (pon->warm_reset_poff_type != -EINVAL) + type = pon->warm_reset_poff_type; + break; + case PON_POWER_OFF_HARD_RESET: + if (pon->hard_reset_poff_type != -EINVAL) + type = pon->hard_reset_poff_type; + break; + case PON_POWER_OFF_SHUTDOWN: + if (pon->shutdown_poff_type != -EINVAL) + type = pon->shutdown_poff_type; + break; + default: + break; + } + + rc = qpnp_pon_masked_write(pon, rst_en_reg, QPNP_PON_RESET_EN, 0); + if (rc) + dev_err(&pon->pdev->dev, + "Unable to write to addr=%hx, rc(%d)\n", + rst_en_reg, rc); + + /* + * We need 10 sleep clock cycles here. But since the clock is + * internally generated, we need to add 50% tolerance to be + * conservative. + */ + udelay(500); + + rc = qpnp_pon_masked_write(pon, QPNP_PON_PS_HOLD_RST_CTL(pon), + QPNP_PON_POWER_OFF_MASK, type); + if (rc) + dev_err(&pon->pdev->dev, + "Unable to write to addr=%x, rc(%d)\n", + QPNP_PON_PS_HOLD_RST_CTL(pon), rc); + + rc = qpnp_pon_masked_write(pon, rst_en_reg, QPNP_PON_RESET_EN, + QPNP_PON_RESET_EN); + if (rc) + dev_err(&pon->pdev->dev, + "Unable to write to addr=%hx, rc(%d)\n", + rst_en_reg, rc); + + dev_dbg(&pon->pdev->dev, "power off type = 0x%02X\n", type); + return rc; +} + +/** + * qpnp_pon_system_pwr_off - Configure system-reset PMIC for shutdown or reset + * @type: Determines the type of power off to perform - shutdown, reset, etc + * + * This function will support configuring for multiple PMICs. In some cases, the + * PON of secondary PMICs also needs to be configured. So this supports that + * requirement. Once the system-reset and secondary PMIC is configured properly, + * the MSM can drop PS_HOLD to activate the specified configuration. Note that + * this function may be called from atomic context as in the case of the panic + * notifier path and thus it should not rely on function calls that may sleep. + */ +int qpnp_pon_system_pwr_off(enum pon_power_off_type type) +{ + int rc = 0; + struct qpnp_pon *pon = sys_reset_dev; + struct qpnp_pon *tmp; + struct power_supply *batt_psy; + union power_supply_propval val; + unsigned long flags; + + if (!pon) + return -ENODEV; + + rc = qpnp_pon_reset_config(pon, type); + if (rc) { + dev_err(&pon->pdev->dev, + "Error configuring main PON rc: %d\n", + rc); + return rc; + } + + /* + * Check if a secondary PON device needs to be configured. If it + * is available, configure that also as per the requested power off + * type + */ + spin_lock_irqsave(&spon_list_slock, flags); + if (list_empty(&spon_dev_list)) + goto out; + + list_for_each_entry_safe(pon, tmp, &spon_dev_list, list) { + dev_emerg(&pon->pdev->dev, + "PMIC@SID%d: configuring PON for reset\n", + to_spmi_device(pon->pdev->dev.parent)->usid); + rc = qpnp_pon_reset_config(pon, type); + if (rc) { + dev_err(&pon->pdev->dev, + "Error configuring secondary PON rc: %d\n", + rc); + goto out; + } + } + /* Set ship mode here if it has been requested */ + if (!!pon_ship_mode_en) { + batt_psy = power_supply_get_by_name("battery"); + if (batt_psy) { + pr_debug("Set ship mode!\n"); + val.intval = 1; + rc = power_supply_set_property(batt_psy, + POWER_SUPPLY_PROP_SET_SHIP_MODE, &val); + if (rc) + dev_err(&pon->pdev->dev, + "Set ship-mode failed\n"); + } + } +out: + spin_unlock_irqrestore(&spon_list_slock, flags); + return rc; +} +EXPORT_SYMBOL(qpnp_pon_system_pwr_off); + +/** + * qpnp_pon_is_warm_reset - Checks if the PMIC went through a warm reset. + * + * Returns > 0 for warm resets, 0 for not warm reset, < 0 for errors + * + * Note that this function will only return the warm vs not-warm reset status + * of the PMIC that is configured as the system-reset device. + */ +int qpnp_pon_is_warm_reset(void) +{ + struct qpnp_pon *pon = sys_reset_dev; + + if (!pon) + return -EPROBE_DEFER; + + if (is_pon_gen1(pon) || pon->subtype == PON_1REG) + return pon->warm_reset_reason1 + || (pon->warm_reset_reason2 & QPNP_PON_WARM_RESET_TFT); + else + return pon->warm_reset_reason1; +} +EXPORT_SYMBOL(qpnp_pon_is_warm_reset); + +/** + * qpnp_pon_wd_config - Disable the wd in a warm reset. + * @enable: to enable or disable the PON watch dog + * + * Returns = 0 for operate successfully, < 0 for errors + */ +int qpnp_pon_wd_config(bool enable) +{ + struct qpnp_pon *pon = sys_reset_dev; + int rc = 0; + + if (!pon) + return -EPROBE_DEFER; + + rc = qpnp_pon_masked_write(pon, QPNP_PON_WD_RST_S2_CTL2(pon), + QPNP_PON_WD_EN, enable ? QPNP_PON_WD_EN : 0); + if (rc) + dev_err(&pon->pdev->dev, + "Unable to write to addr=%x, rc(%d)\n", + QPNP_PON_WD_RST_S2_CTL2(pon), rc); + + return rc; +} +EXPORT_SYMBOL(qpnp_pon_wd_config); + +static int qpnp_pon_get_trigger_config(enum pon_trigger_source pon_src, + bool *enabled) +{ + struct qpnp_pon *pon = sys_reset_dev; + int rc; + u16 addr; + int val; + u8 mask; + + if (!pon) + return -ENODEV; + + if (pon_src < PON_SMPL || pon_src > PON_KPDPWR_N) { + dev_err(&pon->pdev->dev, "Invalid PON source\n"); + return -EINVAL; + } + + addr = QPNP_PON_TRIGGER_EN(pon); + mask = BIT(pon_src); + if (is_pon_gen2(pon) && pon_src == PON_SMPL) { + addr = QPNP_PON_SMPL_CTL(pon); + mask = QPNP_PON_SMPL_EN; + } + + + rc = regmap_read(pon->regmap, addr, &val); + if (rc) + dev_err(&pon->pdev->dev, + "Unable to read from addr=%hx, rc(%d)\n", + addr, rc); + else + *enabled = !!(val & mask); + + return rc; +} + +/** + * qpnp_pon_trigger_config - Configures (enable/disable) the PON trigger source + * @pon_src: PON source to be configured + * @enable: to enable or disable the PON trigger + * + * This function configures the power-on trigger capability of a + * PON source. If a specific PON trigger is disabled it cannot act + * as a power-on source to the PMIC. + */ + +int qpnp_pon_trigger_config(enum pon_trigger_source pon_src, bool enable) +{ + struct qpnp_pon *pon = sys_reset_dev; + int rc; + + if (!pon) + return -EPROBE_DEFER; + + if (pon_src < PON_SMPL || pon_src > PON_KPDPWR_N) { + dev_err(&pon->pdev->dev, "Invalid PON source\n"); + return -EINVAL; + } + + if (is_pon_gen2(pon) && pon_src == PON_SMPL) { + rc = qpnp_pon_masked_write(pon, QPNP_PON_SMPL_CTL(pon), + QPNP_PON_SMPL_EN, enable ? QPNP_PON_SMPL_EN : 0); + if (rc) + dev_err(&pon->pdev->dev, + "Unable to write to addr=%x, rc(%d)\n", + QPNP_PON_SMPL_CTL(pon), rc); + } else { + rc = qpnp_pon_masked_write(pon, QPNP_PON_TRIGGER_EN(pon), + BIT(pon_src), enable ? BIT(pon_src) : 0); + if (rc) + dev_err(&pon->pdev->dev, + "Unable to write to addr=%x, rc(%d)\n", + QPNP_PON_TRIGGER_EN(pon), rc); + } + + return rc; +} +EXPORT_SYMBOL(qpnp_pon_trigger_config); + +/* + * This function stores the PMIC warm reset reason register values. It also + * clears these registers if the qcom,clear-warm-reset device tree property + * is specified. + */ +static int qpnp_pon_store_and_clear_warm_reset(struct qpnp_pon *pon) +{ + int rc; + u8 reg = 0; + uint val; + + rc = regmap_read(pon->regmap, QPNP_PON_WARM_RESET_REASON1(pon), + &val); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to read addr=%x, rc(%d)\n", + QPNP_PON_WARM_RESET_REASON1(pon), rc); + return rc; + } + pon->warm_reset_reason1 = (u8)val; + + if (is_pon_gen1(pon) || pon->subtype == PON_1REG) { + rc = regmap_read(pon->regmap, QPNP_PON_WARM_RESET_REASON2(pon), + &val); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to read addr=%x, rc(%d)\n", + QPNP_PON_WARM_RESET_REASON2(pon), rc); + return rc; + } + pon->warm_reset_reason2 = (u8)val; + } + + if (of_property_read_bool(pon->pdev->dev.of_node, + "qcom,clear-warm-reset")) { + rc = regmap_write(pon->regmap, + QPNP_PON_WARM_RESET_REASON1(pon), reg); + if (rc) + dev_err(&pon->pdev->dev, + "Unable to write to addr=%hx, rc(%d)\n", + QPNP_PON_WARM_RESET_REASON1(pon), rc); + } + + return 0; +} + +static struct qpnp_pon_config * +qpnp_get_cfg(struct qpnp_pon *pon, u32 pon_type) +{ + int i; + + for (i = 0; i < pon->num_pon_config; i++) { + if (pon_type == pon->pon_cfg[i].pon_type) + return &pon->pon_cfg[i]; + } + + return NULL; +} + +static int +qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type) +{ + int rc; + struct qpnp_pon_config *cfg = NULL; + u8 pon_rt_bit = 0; + u32 key_status; + uint pon_rt_sts; + u64 elapsed_us; + + cfg = qpnp_get_cfg(pon, pon_type); + if (!cfg) + return -EINVAL; + + /* Check if key reporting is supported */ + if (!cfg->key_code) + return 0; + + if (pon->kpdpwr_dbc_enable && cfg->pon_type == PON_KPDPWR) { + elapsed_us = ktime_us_delta(ktime_get(), + pon->kpdpwr_last_release_time); + if (elapsed_us < pon->dbc_time_us) { + pr_debug("Ignoring kpdpwr event - within debounce time\n"); + return 0; + } + } + + /* check the RT status to get the current status of the line */ + rc = regmap_read(pon->regmap, QPNP_PON_RT_STS(pon), &pon_rt_sts); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to read PON RT status\n"); + return rc; + } + + switch (cfg->pon_type) { + case PON_KPDPWR: + pon_rt_bit = QPNP_PON_KPDPWR_N_SET; + break; + case PON_RESIN: + pon_rt_bit = QPNP_PON_RESIN_N_SET; + break; + case PON_CBLPWR: + pon_rt_bit = QPNP_PON_CBLPWR_N_SET; + break; + case PON_KPDPWR_RESIN: + pon_rt_bit = QPNP_PON_KPDPWR_RESIN_BARK_N_SET; + break; + default: + return -EINVAL; + } + + pr_debug("PMIC input: code=%d, sts=0x%hhx\n", + cfg->key_code, pon_rt_sts); + key_status = pon_rt_sts & pon_rt_bit; + + if (pon->kpdpwr_dbc_enable && cfg->pon_type == PON_KPDPWR) { + if (!key_status) + pon->kpdpwr_last_release_time = ktime_get(); + } + + /* + * simulate press event in case release event occurred + * without a press event + */ + if (!cfg->old_state && !key_status) { + input_report_key(pon->pon_input, cfg->key_code, 1); + input_sync(pon->pon_input); + } + + input_report_key(pon->pon_input, cfg->key_code, key_status); + input_sync(pon->pon_input); + + cfg->old_state = !!key_status; + + return 0; +} + +static irqreturn_t qpnp_kpdpwr_irq(int irq, void *_pon) +{ + int rc; + struct qpnp_pon *pon = _pon; + + rc = qpnp_pon_input_dispatch(pon, PON_KPDPWR); + if (rc) + dev_err(&pon->pdev->dev, "Unable to send input event\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t qpnp_kpdpwr_bark_irq(int irq, void *_pon) +{ + return IRQ_HANDLED; +} + +static irqreturn_t qpnp_resin_irq(int irq, void *_pon) +{ + int rc; + struct qpnp_pon *pon = _pon; + + rc = qpnp_pon_input_dispatch(pon, PON_RESIN); + if (rc) + dev_err(&pon->pdev->dev, "Unable to send input event\n"); + return IRQ_HANDLED; +} + +static irqreturn_t qpnp_kpdpwr_resin_bark_irq(int irq, void *_pon) +{ + return IRQ_HANDLED; +} + +static irqreturn_t qpnp_cblpwr_irq(int irq, void *_pon) +{ + int rc; + struct qpnp_pon *pon = _pon; + + rc = qpnp_pon_input_dispatch(pon, PON_CBLPWR); + if (rc) + dev_err(&pon->pdev->dev, "Unable to send input event\n"); + + return IRQ_HANDLED; +} + +static void print_pon_reg(struct qpnp_pon *pon, u16 offset) +{ + int rc; + u16 addr; + uint reg; + + addr = pon->base + offset; + rc = regmap_read(pon->regmap, addr, ®); + if (rc) + dev_emerg(&pon->pdev->dev, + "Unable to read reg at 0x%04hx\n", addr); + else + dev_emerg(&pon->pdev->dev, "reg@0x%04hx: %02hhx\n", addr, reg); +} + +#define PON_PBL_STATUS 0x7 +#define PON_PON_REASON1(subtype) PON_OFFSET(subtype, 0x8, 0xC0) +#define PON_PON_REASON2 0x9 +#define PON_WARM_RESET_REASON1(subtype) PON_OFFSET(subtype, 0xA, 0xC2) +#define PON_WARM_RESET_REASON2 0xB +#define PON_POFF_REASON1(subtype) PON_OFFSET(subtype, 0xC, 0xC5) +#define PON_POFF_REASON2 0xD +#define PON_SOFT_RESET_REASON1(subtype) PON_OFFSET(subtype, 0xE, 0xCB) +#define PON_SOFT_RESET_REASON2 0xF +#define PON_FAULT_REASON1 0xC8 +#define PON_FAULT_REASON2 0xC9 +#define PON_PMIC_WD_RESET_S1_TIMER 0x54 +#define PON_PMIC_WD_RESET_S2_TIMER 0x55 +static irqreturn_t qpnp_pmic_wd_bark_irq(int irq, void *_pon) +{ + struct qpnp_pon *pon = _pon; + + print_pon_reg(pon, PON_PBL_STATUS); + print_pon_reg(pon, PON_PON_REASON1(pon->subtype)); + print_pon_reg(pon, PON_WARM_RESET_REASON1(pon->subtype)); + print_pon_reg(pon, PON_SOFT_RESET_REASON1(pon->subtype)); + print_pon_reg(pon, PON_POFF_REASON1(pon->subtype)); + if (is_pon_gen1(pon) || pon->subtype == PON_1REG) { + print_pon_reg(pon, PON_PON_REASON2); + print_pon_reg(pon, PON_WARM_RESET_REASON2); + print_pon_reg(pon, PON_POFF_REASON2); + print_pon_reg(pon, PON_SOFT_RESET_REASON2); + } else { + print_pon_reg(pon, PON_FAULT_REASON1); + print_pon_reg(pon, PON_FAULT_REASON2); + } + print_pon_reg(pon, PON_PMIC_WD_RESET_S1_TIMER); + print_pon_reg(pon, PON_PMIC_WD_RESET_S2_TIMER); + panic("PMIC Watch dog triggered"); + + return IRQ_HANDLED; +} + +static void bark_work_func(struct work_struct *work) +{ + int rc; + uint pon_rt_sts = 0; + struct qpnp_pon_config *cfg; + struct qpnp_pon *pon = + container_of(work, struct qpnp_pon, bark_work.work); + + cfg = qpnp_get_cfg(pon, PON_RESIN); + if (!cfg) { + dev_err(&pon->pdev->dev, "Invalid config pointer\n"); + goto err_return; + } + + /* enable reset */ + rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr, + QPNP_PON_S2_CNTL_EN, QPNP_PON_S2_CNTL_EN); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to configure S2 enable\n"); + goto err_return; + } + /* bark RT status update delay */ + msleep(100); + /* read the bark RT status */ + rc = regmap_read(pon->regmap, QPNP_PON_RT_STS(pon), &pon_rt_sts); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to read PON RT status\n"); + goto err_return; + } + + if (!(pon_rt_sts & QPNP_PON_RESIN_BARK_N_SET)) { + /* report the key event and enable the bark IRQ */ + input_report_key(pon->pon_input, cfg->key_code, 0); + input_sync(pon->pon_input); + enable_irq(cfg->bark_irq); + } else { + /* disable reset */ + rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr, + QPNP_PON_S2_CNTL_EN, 0); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to configure S2 enable\n"); + goto err_return; + } + /* re-arm the work */ + schedule_delayed_work(&pon->bark_work, QPNP_KEY_STATUS_DELAY); + } + +err_return: + return; +} + +static irqreturn_t qpnp_resin_bark_irq(int irq, void *_pon) +{ + int rc; + struct qpnp_pon *pon = _pon; + struct qpnp_pon_config *cfg; + + /* disable the bark interrupt */ + disable_irq_nosync(irq); + + cfg = qpnp_get_cfg(pon, PON_RESIN); + if (!cfg) { + dev_err(&pon->pdev->dev, "Invalid config pointer\n"); + goto err_exit; + } + + /* disable reset */ + rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr, + QPNP_PON_S2_CNTL_EN, 0); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to configure S2 enable\n"); + goto err_exit; + } + + /* report the key event */ + input_report_key(pon->pon_input, cfg->key_code, 1); + input_sync(pon->pon_input); + /* schedule work to check the bark status for key-release */ + schedule_delayed_work(&pon->bark_work, QPNP_KEY_STATUS_DELAY); +err_exit: + return IRQ_HANDLED; +} + +static int +qpnp_config_pull(struct qpnp_pon *pon, struct qpnp_pon_config *cfg) +{ + int rc; + u8 pull_bit; + + switch (cfg->pon_type) { + case PON_KPDPWR: + pull_bit = QPNP_PON_KPDPWR_PULL_UP; + break; + case PON_RESIN: + pull_bit = QPNP_PON_RESIN_PULL_UP; + break; + case PON_CBLPWR: + pull_bit = QPNP_PON_CBLPWR_PULL_UP; + break; + case PON_KPDPWR_RESIN: + pull_bit = QPNP_PON_KPDPWR_PULL_UP | QPNP_PON_RESIN_PULL_UP; + break; + default: + return -EINVAL; + } + + rc = qpnp_pon_masked_write(pon, QPNP_PON_PULL_CTL(pon), + pull_bit, cfg->pull_up ? pull_bit : 0); + if (rc) + dev_err(&pon->pdev->dev, "Unable to config pull-up\n"); + + return rc; +} + +static int +qpnp_config_reset(struct qpnp_pon *pon, struct qpnp_pon_config *cfg) +{ + int rc; + u8 i; + u16 s1_timer_addr, s2_timer_addr; + + switch (cfg->pon_type) { + case PON_KPDPWR: + s1_timer_addr = QPNP_PON_KPDPWR_S1_TIMER(pon); + s2_timer_addr = QPNP_PON_KPDPWR_S2_TIMER(pon); + break; + case PON_RESIN: + s1_timer_addr = QPNP_PON_RESIN_S1_TIMER(pon); + s2_timer_addr = QPNP_PON_RESIN_S2_TIMER(pon); + break; + case PON_KPDPWR_RESIN: + s1_timer_addr = QPNP_PON_KPDPWR_RESIN_S1_TIMER(pon); + s2_timer_addr = QPNP_PON_KPDPWR_RESIN_S2_TIMER(pon); + break; + default: + return -EINVAL; + } + /* disable S2 reset */ + rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr, + QPNP_PON_S2_CNTL_EN, 0); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to configure S2 enable\n"); + return rc; + } + + usleep_range(100, 120); + + /* configure s1 timer, s2 timer and reset type */ + for (i = 0; i < PON_S1_COUNT_MAX + 1; i++) { + if (cfg->s1_timer <= s1_delay[i]) + break; + } + rc = qpnp_pon_masked_write(pon, s1_timer_addr, + QPNP_PON_S1_TIMER_MASK, i); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to configure S1 timer\n"); + return rc; + } + + i = 0; + if (cfg->s2_timer) { + i = cfg->s2_timer / 10; + i = ilog2(i + 1); + } + + rc = qpnp_pon_masked_write(pon, s2_timer_addr, + QPNP_PON_S2_TIMER_MASK, i); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to configure S2 timer\n"); + return rc; + } + + rc = qpnp_pon_masked_write(pon, cfg->s2_cntl_addr, + QPNP_PON_S2_CNTL_TYPE_MASK, (u8)cfg->s2_type); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to configure S2 reset type\n"); + return rc; + } + + /* enable S2 reset */ + rc = qpnp_pon_masked_write(pon, cfg->s2_cntl2_addr, + QPNP_PON_S2_CNTL_EN, QPNP_PON_S2_CNTL_EN); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to configure S2 enable\n"); + return rc; + } + + return 0; +} + +static int +qpnp_pon_request_irqs(struct qpnp_pon *pon, struct qpnp_pon_config *cfg) +{ + int rc = 0; + + switch (cfg->pon_type) { + case PON_KPDPWR: + rc = devm_request_irq(&pon->pdev->dev, cfg->state_irq, + qpnp_kpdpwr_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "qpnp_kpdpwr_status", pon); + if (rc < 0) { + dev_err(&pon->pdev->dev, "Can't request %d IRQ\n", + cfg->state_irq); + return rc; + } + if (cfg->use_bark) { + rc = devm_request_irq(&pon->pdev->dev, cfg->bark_irq, + qpnp_kpdpwr_bark_irq, + IRQF_TRIGGER_RISING, + "qpnp_kpdpwr_bark", pon); + if (rc < 0) { + dev_err(&pon->pdev->dev, + "Can't request %d IRQ\n", + cfg->bark_irq); + return rc; + } + } + break; + case PON_RESIN: + rc = devm_request_irq(&pon->pdev->dev, cfg->state_irq, + qpnp_resin_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "qpnp_resin_status", pon); + if (rc < 0) { + dev_err(&pon->pdev->dev, "Can't request %d IRQ\n", + cfg->state_irq); + return rc; + } + if (cfg->use_bark) { + rc = devm_request_irq(&pon->pdev->dev, cfg->bark_irq, + qpnp_resin_bark_irq, + IRQF_TRIGGER_RISING, + "qpnp_resin_bark", pon); + if (rc < 0) { + dev_err(&pon->pdev->dev, + "Can't request %d IRQ\n", + cfg->bark_irq); + return rc; + } + } + break; + case PON_CBLPWR: + rc = devm_request_irq(&pon->pdev->dev, cfg->state_irq, + qpnp_cblpwr_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "qpnp_cblpwr_status", pon); + if (rc < 0) { + dev_err(&pon->pdev->dev, "Can't request %d IRQ\n", + cfg->state_irq); + return rc; + } + break; + case PON_KPDPWR_RESIN: + if (cfg->use_bark) { + rc = devm_request_irq(&pon->pdev->dev, cfg->bark_irq, + qpnp_kpdpwr_resin_bark_irq, + IRQF_TRIGGER_RISING, + "qpnp_kpdpwr_resin_bark", pon); + if (rc < 0) { + dev_err(&pon->pdev->dev, + "Can't request %d IRQ\n", + cfg->bark_irq); + return rc; + } + } + break; + default: + return -EINVAL; + } + + /* mark the interrupts wakeable if they support linux-key */ + if (cfg->key_code) { + enable_irq_wake(cfg->state_irq); + /* special handling for RESIN due to a hardware bug */ + if (cfg->pon_type == PON_RESIN && cfg->support_reset) + enable_irq_wake(cfg->bark_irq); + } + + return rc; +} + +static int +qpnp_pon_config_input(struct qpnp_pon *pon, struct qpnp_pon_config *cfg) +{ + if (!pon->pon_input) { + pon->pon_input = input_allocate_device(); + if (!pon->pon_input) { + dev_err(&pon->pdev->dev, + "Can't allocate pon input device\n"); + return -ENOMEM; + } + pon->pon_input->name = "qpnp_pon"; + pon->pon_input->phys = "qpnp_pon/input0"; + } + + input_set_capability(pon->pon_input, EV_KEY, cfg->key_code); + + return 0; +} + +static int qpnp_pon_config_init(struct qpnp_pon *pon) +{ + int rc = 0, i = 0, pmic_wd_bark_irq; + struct device_node *pp = NULL; + struct qpnp_pon_config *cfg; + uint pmic_type; + uint revid_rev4; + + if (!pon->num_pon_config) { + dev_dbg(&pon->pdev->dev, "num_pon_config: %d\n", + pon->num_pon_config); + return 0; + } + + /* iterate through the list of pon configs */ + for_each_available_child_of_node(pon->pdev->dev.of_node, pp) { + if (!of_find_property(pp, "qcom,pon-type", NULL)) + continue; + + cfg = &pon->pon_cfg[i++]; + + rc = of_property_read_u32(pp, "qcom,pon-type", &cfg->pon_type); + if (rc) { + dev_err(&pon->pdev->dev, "PON type not specified\n"); + return rc; + } + + switch (cfg->pon_type) { + case PON_KPDPWR: + cfg->state_irq = platform_get_irq_byname(pon->pdev, + "kpdpwr"); + if (cfg->state_irq < 0) { + dev_err(&pon->pdev->dev, + "Unable to get kpdpwr irq\n"); + return cfg->state_irq; + } + + rc = of_property_read_u32(pp, "qcom,support-reset", + &cfg->support_reset); + + if (rc) { + if (rc == -EINVAL) { + dev_dbg(&pon->pdev->dev, + "'qcom,support-reset' DT property doesn't exist\n"); + } else { + dev_err(&pon->pdev->dev, + "Unable to read 'qcom,support-reset'\n"); + return rc; + } + } else { + cfg->config_reset = true; + } + + cfg->use_bark = of_property_read_bool(pp, + "qcom,use-bark"); + if (cfg->use_bark) { + cfg->bark_irq + = platform_get_irq_byname(pon->pdev, + "kpdpwr-bark"); + if (cfg->bark_irq < 0) { + dev_err(&pon->pdev->dev, + "Unable to get kpdpwr-bark irq\n"); + return cfg->bark_irq; + } + } + + /* + * If the value read from REVISION2 register is 0x00, + * then there is a single register to control s2 reset. + * Otherwise there are separate registers for s2 reset + * type and s2 reset enable. + */ + if (pon->pon_ver == QPNP_PON_GEN1_V1) { + cfg->s2_cntl_addr = cfg->s2_cntl2_addr = + QPNP_PON_KPDPWR_S2_CNTL(pon); + } else { + cfg->s2_cntl_addr = + QPNP_PON_KPDPWR_S2_CNTL(pon); + cfg->s2_cntl2_addr = + QPNP_PON_KPDPWR_S2_CNTL2(pon); + } + + break; + case PON_RESIN: + cfg->state_irq = platform_get_irq_byname(pon->pdev, + "resin"); + if (cfg->state_irq < 0) { + dev_err(&pon->pdev->dev, + "Unable to get resin irq\n"); + return cfg->bark_irq; + } + + rc = of_property_read_u32(pp, "qcom,support-reset", + &cfg->support_reset); + + if (rc) { + if (rc == -EINVAL) { + dev_dbg(&pon->pdev->dev, + "'qcom,support-reset' DT property doesn't exist\n"); + } else { + dev_err(&pon->pdev->dev, + "Unable to read 'qcom,support-reset'\n"); + return rc; + } + } else { + cfg->config_reset = true; + } + + cfg->use_bark = of_property_read_bool(pp, + "qcom,use-bark"); + + rc = regmap_read(pon->regmap, PMIC_VERSION_REG, + &pmic_type); + + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to read PMIC type\n"); + return rc; + } + + if (pmic_type == PMIC_VER_8941) { + + rc = regmap_read(pon->regmap, + PMIC_VERSION_REV4_REG, + &revid_rev4); + + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to read PMIC revision ID\n"); + return rc; + } + + /* + * PM8941 V3 does not have hardware bug. Hence + * bark is not required from PMIC versions 3.0. + */ + if (!(revid_rev4 == PMIC8941_V1_REV4 || + revid_rev4 == PMIC8941_V2_REV4)) { + cfg->support_reset = false; + cfg->use_bark = false; + } + } + + if (cfg->use_bark) { + cfg->bark_irq + = platform_get_irq_byname(pon->pdev, + "resin-bark"); + if (cfg->bark_irq < 0) { + dev_err(&pon->pdev->dev, + "Unable to get resin-bark irq\n"); + return cfg->bark_irq; + } + } + + if (pon->pon_ver == QPNP_PON_GEN1_V1) { + cfg->s2_cntl_addr = cfg->s2_cntl2_addr = + QPNP_PON_RESIN_S2_CNTL(pon); + } else { + cfg->s2_cntl_addr = + QPNP_PON_RESIN_S2_CNTL(pon); + cfg->s2_cntl2_addr = + QPNP_PON_RESIN_S2_CNTL2(pon); + } + + break; + case PON_CBLPWR: + cfg->state_irq = platform_get_irq_byname(pon->pdev, + "cblpwr"); + if (cfg->state_irq < 0) { + dev_err(&pon->pdev->dev, + "Unable to get cblpwr irq\n"); + return rc; + } + break; + case PON_KPDPWR_RESIN: + rc = of_property_read_u32(pp, "qcom,support-reset", + &cfg->support_reset); + + if (rc) { + if (rc == -EINVAL) { + dev_dbg(&pon->pdev->dev, + "'qcom,support-reset' DT property doesn't exist\n"); + } else { + dev_err(&pon->pdev->dev, + "Unable to read 'qcom,support-reset'\n"); + return rc; + } + } else { + cfg->config_reset = true; + } + + cfg->use_bark = of_property_read_bool(pp, + "qcom,use-bark"); + if (cfg->use_bark) { + cfg->bark_irq + = platform_get_irq_byname(pon->pdev, + "kpdpwr-resin-bark"); + if (cfg->bark_irq < 0) { + dev_err(&pon->pdev->dev, + "Unable to get kpdpwr-resin-bark irq\n"); + return cfg->bark_irq; + } + } + + if (pon->pon_ver == QPNP_PON_GEN1_V1) { + cfg->s2_cntl_addr = cfg->s2_cntl2_addr = + QPNP_PON_KPDPWR_RESIN_S2_CNTL(pon); + } else { + cfg->s2_cntl_addr = + QPNP_PON_KPDPWR_RESIN_S2_CNTL(pon); + cfg->s2_cntl2_addr = + QPNP_PON_KPDPWR_RESIN_S2_CNTL2(pon); + } + + break; + default: + dev_err(&pon->pdev->dev, "PON RESET %d not supported", + cfg->pon_type); + return -EINVAL; + } + + if (cfg->support_reset) { + /* + * Get the reset parameters (bark debounce time and + * reset debounce time) for the reset line. + */ + rc = of_property_read_u32(pp, "qcom,s1-timer", + &cfg->s1_timer); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to read s1-timer\n"); + return rc; + } + if (cfg->s1_timer > QPNP_PON_S1_TIMER_MAX) { + dev_err(&pon->pdev->dev, + "Incorrect S1 debounce time\n"); + return -EINVAL; + } + rc = of_property_read_u32(pp, "qcom,s2-timer", + &cfg->s2_timer); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to read s2-timer\n"); + return rc; + } + if (cfg->s2_timer > QPNP_PON_S2_TIMER_MAX) { + dev_err(&pon->pdev->dev, + "Incorrect S2 debounce time\n"); + return -EINVAL; + } + rc = of_property_read_u32(pp, "qcom,s2-type", + &cfg->s2_type); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to read s2-type\n"); + return rc; + } + if (cfg->s2_type > QPNP_PON_RESET_TYPE_MAX) { + dev_err(&pon->pdev->dev, + "Incorrect reset type specified\n"); + return -EINVAL; + } + } + /* + * Get the standard-key parameters. This might not be + * specified if there is no key mapping on the reset line. + */ + rc = of_property_read_u32(pp, "linux,code", &cfg->key_code); + if (rc && rc != -EINVAL) { + dev_err(&pon->pdev->dev, "Unable to read key-code\n"); + return rc; + } + /* Register key configuration */ + if (cfg->key_code) { + rc = qpnp_pon_config_input(pon, cfg); + if (rc < 0) + return rc; + } + /* get the pull-up configuration */ + rc = of_property_read_u32(pp, "qcom,pull-up", &cfg->pull_up); + if (rc && rc != -EINVAL) { + dev_err(&pon->pdev->dev, "Unable to read pull-up\n"); + return rc; + } + } + + pmic_wd_bark_irq = platform_get_irq_byname(pon->pdev, "pmic-wd-bark"); + /* request the pmic-wd-bark irq only if it is defined */ + if (pmic_wd_bark_irq >= 0) { + rc = devm_request_irq(&pon->pdev->dev, pmic_wd_bark_irq, + qpnp_pmic_wd_bark_irq, + IRQF_TRIGGER_RISING, + "qpnp_pmic_wd_bark", pon); + if (rc < 0) { + dev_err(&pon->pdev->dev, + "Can't request %d IRQ\n", + pmic_wd_bark_irq); + goto free_input_dev; + } + } + + /* register the input device */ + if (pon->pon_input) { + rc = input_register_device(pon->pon_input); + if (rc) { + dev_err(&pon->pdev->dev, + "Can't register pon key: %d\n", rc); + goto free_input_dev; + } + } + + for (i = 0; i < pon->num_pon_config; i++) { + cfg = &pon->pon_cfg[i]; + /* Configure the pull-up */ + rc = qpnp_config_pull(pon, cfg); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to config pull-up\n"); + goto unreg_input_dev; + } + + if (cfg->config_reset) { + /* Configure the reset-configuration */ + if (cfg->support_reset) { + rc = qpnp_config_reset(pon, cfg); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to config pon reset\n"); + goto unreg_input_dev; + } + } else { + if (cfg->pon_type != PON_CBLPWR) { + /* disable S2 reset */ + rc = qpnp_pon_masked_write(pon, + cfg->s2_cntl2_addr, + QPNP_PON_S2_CNTL_EN, 0); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to disable S2 reset\n"); + goto unreg_input_dev; + } + } + } + } + + rc = qpnp_pon_request_irqs(pon, cfg); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to request-irq's\n"); + goto unreg_input_dev; + } + } + + device_init_wakeup(&pon->pdev->dev, 1); + + return rc; + +unreg_input_dev: + if (pon->pon_input) + input_unregister_device(pon->pon_input); +free_input_dev: + if (pon->pon_input) + input_free_device(pon->pon_input); + return rc; +} + +static int pon_spare_regulator_enable(struct regulator_dev *rdev) +{ + int rc = 0; + u8 value; + struct pon_regulator *pon_reg = rdev_get_drvdata(rdev); + + pr_debug("reg %s enable addr: %x bit: %d\n", rdev->desc->name, + pon_reg->addr, pon_reg->bit); + + value = BIT(pon_reg->bit) & 0xFF; + rc = qpnp_pon_masked_write(pon_reg->pon, pon_reg->pon->base + + pon_reg->addr, value, value); + if (rc) + dev_err(&pon_reg->pon->pdev->dev, "Unable to write to %x\n", + pon_reg->pon->base + pon_reg->addr); + else + pon_reg->enabled = true; + return rc; +} + +static int pon_spare_regulator_disable(struct regulator_dev *rdev) +{ + int rc = 0; + u8 mask; + struct pon_regulator *pon_reg = rdev_get_drvdata(rdev); + + pr_debug("reg %s disable addr: %x bit: %d\n", rdev->desc->name, + pon_reg->addr, pon_reg->bit); + + mask = BIT(pon_reg->bit) & 0xFF; + rc = qpnp_pon_masked_write(pon_reg->pon, pon_reg->pon->base + + pon_reg->addr, mask, 0); + if (rc) + dev_err(&pon_reg->pon->pdev->dev, "Unable to write to %x\n", + pon_reg->pon->base + pon_reg->addr); + else + pon_reg->enabled = false; + return rc; +} + +static int pon_spare_regulator_is_enable(struct regulator_dev *rdev) +{ + struct pon_regulator *pon_reg = rdev_get_drvdata(rdev); + + return pon_reg->enabled; +} + +struct regulator_ops pon_spare_reg_ops = { + .enable = pon_spare_regulator_enable, + .disable = pon_spare_regulator_disable, + .is_enabled = pon_spare_regulator_is_enable, +}; + +static int pon_regulator_init(struct qpnp_pon *pon) +{ + int rc = 0, i = 0; + struct regulator_init_data *init_data; + struct regulator_config reg_cfg = {}; + struct device_node *node = NULL; + struct device *dev = &pon->pdev->dev; + struct pon_regulator *pon_reg; + + if (!pon->num_pon_reg) + return 0; + + pon->pon_reg_cfg = devm_kcalloc(dev, pon->num_pon_reg, + sizeof(*(pon->pon_reg_cfg)), + GFP_KERNEL); + + if (!pon->pon_reg_cfg) + return -ENOMEM; + + for_each_available_child_of_node(dev->of_node, node) { + if (!of_find_property(node, "regulator-name", NULL)) + continue; + + pon_reg = &pon->pon_reg_cfg[i++]; + pon_reg->pon = pon; + + rc = of_property_read_u32(node, "qcom,pon-spare-reg-addr", + &pon_reg->addr); + if (rc) { + dev_err(dev, "Unable to read address for regulator, rc=%d\n", + rc); + return rc; + } + + rc = of_property_read_u32(node, "qcom,pon-spare-reg-bit", + &pon_reg->bit); + if (rc) { + dev_err(dev, "Unable to read bit for regulator, rc=%d\n", + rc); + return rc; + } + + init_data = of_get_regulator_init_data(dev, node, + &pon_reg->rdesc); + if (!init_data) { + dev_err(dev, "regulator init data is missing\n"); + return -EINVAL; + } + init_data->constraints.valid_ops_mask + |= REGULATOR_CHANGE_STATUS; + + if (!init_data->constraints.name) { + dev_err(dev, "regulator-name is missing\n"); + return -EINVAL; + } + + pon_reg->rdesc.owner = THIS_MODULE; + pon_reg->rdesc.type = REGULATOR_VOLTAGE; + pon_reg->rdesc.ops = &pon_spare_reg_ops; + pon_reg->rdesc.name = init_data->constraints.name; + + reg_cfg.dev = dev; + reg_cfg.init_data = init_data; + reg_cfg.driver_data = pon_reg; + reg_cfg.of_node = node; + + pon_reg->rdev = regulator_register(&pon_reg->rdesc, ®_cfg); + if (IS_ERR(pon_reg->rdev)) { + rc = PTR_ERR(pon_reg->rdev); + pon_reg->rdev = NULL; + if (rc != -EPROBE_DEFER) + dev_err(dev, "regulator_register failed, rc=%d\n", + rc); + return rc; + } + } + return rc; +} + +static bool smpl_en; + +static int qpnp_pon_smpl_en_get(char *buf, const struct kernel_param *kp) +{ + bool enabled = 0; + int rc; + + rc = qpnp_pon_get_trigger_config(PON_SMPL, &enabled); + if (rc < 0) + return rc; + + return snprintf(buf, QPNP_PON_BUFFER_SIZE, "%d", enabled); +} + +static int qpnp_pon_smpl_en_set(const char *val, + const struct kernel_param *kp) +{ + int rc; + + rc = param_set_bool(val, kp); + if (rc < 0) { + pr_err("Unable to set smpl_en rc=%d\n", rc); + return rc; + } + + rc = qpnp_pon_trigger_config(PON_SMPL, *(bool *)kp->arg); + return rc; +} + +static struct kernel_param_ops smpl_en_ops = { + .set = qpnp_pon_smpl_en_set, + .get = qpnp_pon_smpl_en_get, +}; + +module_param_cb(smpl_en, &smpl_en_ops, &smpl_en, 0644); + +static bool dload_on_uvlo; + +static int qpnp_pon_debugfs_uvlo_dload_get(char *buf, + const struct kernel_param *kp) +{ + struct qpnp_pon *pon = sys_reset_dev; + int rc = 0; + uint reg; + + if (!pon) + return -ENODEV; + + rc = regmap_read(pon->regmap, QPNP_PON_XVDD_RB_SPARE(pon), ®); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to read addr=%x, rc(%d)\n", + QPNP_PON_XVDD_RB_SPARE(pon), rc); + return rc; + } + + return snprintf(buf, PAGE_SIZE, "%d", + !!(QPNP_PON_UVLO_DLOAD_EN & reg)); +} + +static int qpnp_pon_debugfs_uvlo_dload_set(const char *val, + const struct kernel_param *kp) +{ + struct qpnp_pon *pon = sys_reset_dev; + int rc = 0; + uint reg; + + if (!pon) + return -ENODEV; + + rc = param_set_bool(val, kp); + if (rc) { + pr_err("Unable to set bms_reset: %d\n", rc); + return rc; + } + + rc = regmap_read(pon->regmap, QPNP_PON_XVDD_RB_SPARE(pon), ®); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to read addr=%x, rc(%d)\n", + QPNP_PON_XVDD_RB_SPARE(pon), rc); + return rc; + } + + reg &= ~QPNP_PON_UVLO_DLOAD_EN; + if (*(bool *)kp->arg) + reg |= QPNP_PON_UVLO_DLOAD_EN; + + rc = regmap_write(pon->regmap, QPNP_PON_XVDD_RB_SPARE(pon), reg); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to write to addr=%hx, rc(%d)\n", + QPNP_PON_XVDD_RB_SPARE(pon), rc); + return rc; + } + + return 0; +} + +static struct kernel_param_ops dload_on_uvlo_ops = { + .set = qpnp_pon_debugfs_uvlo_dload_set, + .get = qpnp_pon_debugfs_uvlo_dload_get, +}; + +module_param_cb(dload_on_uvlo, &dload_on_uvlo_ops, &dload_on_uvlo, 0644); + +#if defined(CONFIG_DEBUG_FS) + +static int qpnp_pon_debugfs_uvlo_get(void *data, u64 *val) +{ + struct qpnp_pon *pon = (struct qpnp_pon *) data; + + *val = pon->uvlo; + + return 0; +} + +static int qpnp_pon_debugfs_uvlo_set(void *data, u64 val) +{ + struct qpnp_pon *pon = (struct qpnp_pon *) data; + + if (pon->pon_trigger_reason == PON_SMPL || + pon->pon_power_off_reason == QPNP_POFF_REASON_UVLO) + panic("An UVLO was occurred.\n"); + pon->uvlo = val; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(qpnp_pon_debugfs_uvlo_fops, qpnp_pon_debugfs_uvlo_get, + qpnp_pon_debugfs_uvlo_set, "0x%02llx\n"); + +static void qpnp_pon_debugfs_init(struct platform_device *pdev) +{ + struct qpnp_pon *pon = dev_get_drvdata(&pdev->dev); + struct dentry *ent; + + pon->debugfs = debugfs_create_dir(dev_name(&pdev->dev), NULL); + if (!pon->debugfs) { + dev_err(&pon->pdev->dev, + "Unable to create debugfs directory\n"); + } else { + ent = debugfs_create_file("uvlo_panic", 0644, + pon->debugfs, pon, &qpnp_pon_debugfs_uvlo_fops); + if (!ent) + dev_err(&pon->pdev->dev, + "Unable to create uvlo_panic debugfs file.\n"); + } +} + +static void qpnp_pon_debugfs_remove(struct platform_device *pdev) +{ + struct qpnp_pon *pon = dev_get_drvdata(&pdev->dev); + + debugfs_remove_recursive(pon->debugfs); +} + +#else + +static void qpnp_pon_debugfs_init(struct platform_device *pdev) +{} + +static void qpnp_pon_debugfs_remove(struct platform_device *pdev) +{} +#endif + +static int read_gen2_pon_off_reason(struct qpnp_pon *pon, u16 *reason, + int *reason_index_offset) +{ + int rc; + int buf[2], reg; + + rc = regmap_read(pon->regmap, + QPNP_PON_OFF_REASON(pon), + ®); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to read PON_OFF_REASON reg rc:%d\n", + rc); + return rc; + } + + if (reg & QPNP_GEN2_POFF_SEQ) { + rc = regmap_read(pon->regmap, + QPNP_POFF_REASON1(pon), + buf); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to read POFF_REASON1 reg rc:%d\n", + rc); + return rc; + } + *reason = (u8)buf[0]; + *reason_index_offset = 0; + } else if (reg & QPNP_GEN2_FAULT_SEQ) { + rc = regmap_bulk_read(pon->regmap, + QPNP_FAULT_REASON1(pon), + buf, 2); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to read FAULT_REASON regs rc:%d\n", + rc); + return rc; + } + *reason = (u8)buf[0] | (u16)(buf[1] << 8); + *reason_index_offset = POFF_REASON_FAULT_OFFSET; + } else if (reg & QPNP_GEN2_S3_RESET_SEQ) { + rc = regmap_read(pon->regmap, + QPNP_S3_RESET_REASON(pon), + buf); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to read S3_RESET_REASON reg rc:%d\n", + rc); + return rc; + } + *reason = (u8)buf[0]; + *reason_index_offset = POFF_REASON_S3_RESET_OFFSET; + } + + return 0; +} + +static int qpnp_pon_probe(struct platform_device *pdev) +{ + struct qpnp_pon *pon; + unsigned int base; + struct device_node *node = NULL; + u32 delay = 0, s3_debounce = 0; + int rc, sys_reset, index; + int reason_index_offset = 0; + u8 buf[2]; + uint pon_sts = 0; + u16 poff_sts = 0; + const char *s3_src; + u8 s3_src_reg; + unsigned long flags; + uint temp = 0; + + pon = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_pon), GFP_KERNEL); + if (!pon) + return -ENOMEM; + + pon->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!pon->regmap) { + dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); + return -EINVAL; + } + + sys_reset = of_property_read_bool(pdev->dev.of_node, + "qcom,system-reset"); + if (sys_reset && sys_reset_dev) { + dev_err(&pdev->dev, + "qcom,system-reset property can only be specified for one device on the system\n"); + return -EINVAL; + } else if (sys_reset) { + sys_reset_dev = pon; + } + + pon->pdev = pdev; + + rc = of_property_read_u32(pdev->dev.of_node, "reg", &base); + if (rc < 0) { + dev_err(&pdev->dev, + "Couldn't find reg in node = %s rc = %d\n", + pdev->dev.of_node->full_name, rc); + return rc; + } + pon->base = base; + + /* get the total number of pon configurations */ + for_each_available_child_of_node(pdev->dev.of_node, node) { + if (of_find_property(node, "regulator-name", NULL)) { + pon->num_pon_reg++; + } else if (of_find_property(node, "qcom,pon-type", NULL)) { + pon->num_pon_config++; + } else { + pr_err("Unknown sub-node\n"); + return -EINVAL; + } + } + + pr_debug("PON@SID %d: num_pon_config: %d num_pon_reg: %d\n", + to_spmi_device(pon->pdev->dev.parent)->usid, + pon->num_pon_config, pon->num_pon_reg); + + rc = pon_regulator_init(pon); + if (rc) { + dev_err(&pdev->dev, "Error in pon_regulator_init rc: %d\n", + rc); + return rc; + } + + if (!pon->num_pon_config) + /* No PON config., do not register the driver */ + dev_info(&pdev->dev, "No PON config. specified\n"); + else + pon->pon_cfg = devm_kzalloc(&pdev->dev, + sizeof(struct qpnp_pon_config) * + pon->num_pon_config, GFP_KERNEL); + + /* Read PON_PERPH_SUBTYPE register to get PON type */ + rc = regmap_read(pon->regmap, + QPNP_PON_PERPH_SUBTYPE(pon), + &temp); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to read PON_PERPH_SUBTYPE register rc: %d\n", + rc); + return rc; + } + pon->subtype = temp; + + /* Check if it is rev B */ + rc = regmap_read(pon->regmap, + QPNP_PON_REVISION2(pon), &temp); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to read addr=%x, rc(%d)\n", + QPNP_PON_REVISION2(pon), rc); + return rc; + } + + pon->pon_ver = temp; + if (is_pon_gen1(pon)) { + if (pon->pon_ver == 0) + pon->pon_ver = QPNP_PON_GEN1_V1; + else + pon->pon_ver = QPNP_PON_GEN1_V2; + } else if (is_pon_gen2(pon)) { + pon->pon_ver = QPNP_PON_GEN2; + } else if (pon->subtype == PON_1REG) { + pon->pon_ver = QPNP_PON_GEN1_V2; + } else { + dev_err(&pon->pdev->dev, + "Invalid PON_PERPH_SUBTYPE value %x\n", + pon->subtype); + return -EINVAL; + } + + pr_debug("%s: pon_subtype=%x, pon_version=%x\n", __func__, + pon->subtype, pon->pon_ver); + + rc = qpnp_pon_store_and_clear_warm_reset(pon); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to store/clear WARM_RESET_REASONx registers rc: %d\n", + rc); + return rc; + } + + /* PON reason */ + rc = regmap_read(pon->regmap, QPNP_PON_REASON1(pon), &pon_sts); + if (rc) { + dev_err(&pon->pdev->dev, + "Unable to read PON_RESASON1 reg rc: %d\n", + rc); + return rc; + } + + if (sys_reset) + boot_reason = ffs(pon_sts); + + index = ffs(pon_sts) - 1; + cold_boot = !qpnp_pon_is_warm_reset(); + if (index >= ARRAY_SIZE(qpnp_pon_reason) || index < 0) { + dev_info(&pon->pdev->dev, + "PMIC@SID%d Power-on reason: Unknown and '%s' boot\n", + to_spmi_device(pon->pdev->dev.parent)->usid, + cold_boot ? "cold" : "warm"); + } else { + pon->pon_trigger_reason = index; + dev_info(&pon->pdev->dev, + "PMIC@SID%d Power-on reason: %s and '%s' boot\n", + to_spmi_device(pon->pdev->dev.parent)->usid, + qpnp_pon_reason[index], + cold_boot ? "cold" : "warm"); + } + + /* POFF reason */ + if (!is_pon_gen1(pon) && pon->subtype != PON_1REG) { + rc = read_gen2_pon_off_reason(pon, &poff_sts, + &reason_index_offset); + if (rc) + return rc; + } else { + rc = regmap_bulk_read(pon->regmap, QPNP_POFF_REASON1(pon), + buf, 2); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to read POFF_REASON regs rc:%d\n", + rc); + return rc; + } + poff_sts = buf[0] | (buf[1] << 8); + } + index = ffs(poff_sts) - 1 + reason_index_offset; + if (index >= ARRAY_SIZE(qpnp_poff_reason) || index < 0) { + dev_info(&pon->pdev->dev, + "PMIC@SID%d: Unknown power-off reason\n", + to_spmi_device(pon->pdev->dev.parent)->usid); + } else { + pon->pon_power_off_reason = index; + dev_info(&pon->pdev->dev, + "PMIC@SID%d: Power-off reason: %s\n", + to_spmi_device(pon->pdev->dev.parent)->usid, + qpnp_poff_reason[index]); + } + + if (pon->pon_trigger_reason == PON_SMPL || + pon->pon_power_off_reason == QPNP_POFF_REASON_UVLO) { + if (of_property_read_bool(pdev->dev.of_node, + "qcom,uvlo-panic")) + panic("An UVLO was occurred."); + } + + /* program s3 debounce */ + rc = of_property_read_u32(pon->pdev->dev.of_node, + "qcom,s3-debounce", &s3_debounce); + if (rc) { + if (rc != -EINVAL) { + dev_err(&pon->pdev->dev, + "Unable to read s3 timer rc:%d\n", + rc); + return rc; + } + } else { + if (s3_debounce > QPNP_PON_S3_TIMER_SECS_MAX) { + dev_info(&pon->pdev->dev, + "Exceeded S3 max value, set it to max\n"); + s3_debounce = QPNP_PON_S3_TIMER_SECS_MAX; + } + + /* 0 is a special value to indicate instant s3 reset */ + if (s3_debounce != 0) + s3_debounce = ilog2(s3_debounce); + + /* s3 debounce is SEC_ACCESS register */ + rc = qpnp_pon_masked_write(pon, QPNP_PON_SEC_ACCESS(pon), + 0xFF, QPNP_PON_SEC_UNLOCK); + if (rc) { + dev_err(&pdev->dev, "Unable to do SEC_ACCESS rc:%d\n", + rc); + return rc; + } + + rc = qpnp_pon_masked_write(pon, QPNP_PON_S3_DBC_CTL(pon), + QPNP_PON_S3_DBC_DELAY_MASK, s3_debounce); + if (rc) { + dev_err(&pdev->dev, + "Unable to set S3 debounce rc:%d\n", + rc); + return rc; + } + } + + /* program s3 source */ + s3_src = "kpdpwr-and-resin"; + rc = of_property_read_string(pon->pdev->dev.of_node, + "qcom,s3-src", &s3_src); + if (rc && rc != -EINVAL) { + dev_err(&pon->pdev->dev, "Unable to read s3 timer rc: %d\n", + rc); + return rc; + } + + if (!strcmp(s3_src, "kpdpwr")) + s3_src_reg = QPNP_PON_S3_SRC_KPDPWR; + else if (!strcmp(s3_src, "resin")) + s3_src_reg = QPNP_PON_S3_SRC_RESIN; + else if (!strcmp(s3_src, "kpdpwr-or-resin")) + s3_src_reg = QPNP_PON_S3_SRC_KPDPWR_OR_RESIN; + else /* default combination */ + s3_src_reg = QPNP_PON_S3_SRC_KPDPWR_AND_RESIN; + + /* + * S3 source is a write once register. If the register has + * been configured by bootloader then this operation will + * not be effective. + */ + rc = qpnp_pon_masked_write(pon, QPNP_PON_S3_SRC(pon), + QPNP_PON_S3_SRC_MASK, s3_src_reg); + if (rc) { + dev_err(&pdev->dev, "Unable to program s3 source rc: %d\n", + rc); + return rc; + } + + dev_set_drvdata(&pdev->dev, pon); + + INIT_DELAYED_WORK(&pon->bark_work, bark_work_func); + + /* register the PON configurations */ + rc = qpnp_pon_config_init(pon); + if (rc) { + dev_err(&pdev->dev, + "Unable to initialize PON configurations rc: %d\n", rc); + return rc; + } + + rc = of_property_read_u32(pon->pdev->dev.of_node, + "qcom,pon-dbc-delay", &delay); + if (rc) { + if (rc != -EINVAL) { + dev_err(&pdev->dev, + "Unable to read debounce delay rc: %d\n", rc); + return rc; + } + } else { + rc = qpnp_pon_set_dbc(pon, delay); + if (rc) { + dev_err(&pdev->dev, + "Unable to set PON debounce delay rc=%d\n", rc); + return rc; + } + } + rc = qpnp_pon_get_dbc(pon, &pon->dbc_time_us); + if (rc) { + dev_err(&pdev->dev, + "Unable to get PON debounce delay rc=%d\n", rc); + return rc; + } + + pon->kpdpwr_dbc_enable = of_property_read_bool(pon->pdev->dev.of_node, + "qcom,kpdpwr-sw-debounce"); + + rc = of_property_read_u32(pon->pdev->dev.of_node, + "qcom,warm-reset-poweroff-type", + &pon->warm_reset_poff_type); + if (rc) { + if (rc != -EINVAL) { + dev_err(&pdev->dev, "Unable to read warm reset poweroff type rc: %d\n", + rc); + return rc; + } + pon->warm_reset_poff_type = -EINVAL; + } else if (pon->warm_reset_poff_type <= PON_POWER_OFF_RESERVED || + pon->warm_reset_poff_type >= PON_POWER_OFF_MAX_TYPE) { + dev_err(&pdev->dev, "Invalid warm-reset-poweroff-type\n"); + pon->warm_reset_poff_type = -EINVAL; + } + + rc = of_property_read_u32(pon->pdev->dev.of_node, + "qcom,hard-reset-poweroff-type", + &pon->hard_reset_poff_type); + if (rc) { + if (rc != -EINVAL) { + dev_err(&pdev->dev, "Unable to read hard reset poweroff type rc: %d\n", + rc); + return rc; + } + pon->hard_reset_poff_type = -EINVAL; + } else if (pon->hard_reset_poff_type <= PON_POWER_OFF_RESERVED || + pon->hard_reset_poff_type >= PON_POWER_OFF_MAX_TYPE) { + dev_err(&pdev->dev, "Invalid hard-reset-poweroff-type\n"); + pon->hard_reset_poff_type = -EINVAL; + } + + rc = of_property_read_u32(pon->pdev->dev.of_node, + "qcom,shutdown-poweroff-type", + &pon->shutdown_poff_type); + if (rc) { + if (rc != -EINVAL) { + dev_err(&pdev->dev, "Unable to read shutdown poweroff type rc: %d\n", + rc); + return rc; + } + pon->shutdown_poff_type = -EINVAL; + } else if (pon->shutdown_poff_type <= PON_POWER_OFF_RESERVED || + pon->shutdown_poff_type >= PON_POWER_OFF_MAX_TYPE) { + dev_err(&pdev->dev, "Invalid shutdown-poweroff-type\n"); + pon->shutdown_poff_type = -EINVAL; + } + + rc = device_create_file(&pdev->dev, &dev_attr_debounce_us); + if (rc) { + dev_err(&pdev->dev, "sys file creation failed rc: %d\n", rc); + return rc; + } + + if (of_property_read_bool(pdev->dev.of_node, + "qcom,secondary-pon-reset")) { + if (sys_reset) { + dev_err(&pdev->dev, + "qcom,system-reset property shouldn't be used along with qcom,secondary-pon-reset property\n"); + return -EINVAL; + } + spin_lock_irqsave(&spon_list_slock, flags); + list_add(&pon->list, &spon_dev_list); + spin_unlock_irqrestore(&spon_list_slock, flags); + pon->is_spon = true; + } + + /* config whether store the hard reset reason */ + pon->store_hard_reset_reason = of_property_read_bool(pdev->dev.of_node, + "qcom,store-hard-reset-reason"); + + qpnp_pon_debugfs_init(pdev); + return 0; +} + +static int qpnp_pon_remove(struct platform_device *pdev) +{ + struct qpnp_pon *pon = dev_get_drvdata(&pdev->dev); + unsigned long flags; + + device_remove_file(&pdev->dev, &dev_attr_debounce_us); + + cancel_delayed_work_sync(&pon->bark_work); + + if (pon->pon_input) + input_unregister_device(pon->pon_input); + qpnp_pon_debugfs_remove(pdev); + if (pon->is_spon) { + spin_lock_irqsave(&spon_list_slock, flags); + list_del(&pon->list); + spin_unlock_irqrestore(&spon_list_slock, flags); + } + return 0; +} + +static const struct of_device_id spmi_match_table[] = { + { .compatible = "qcom,qpnp-power-on", }, + {} +}; + +static struct platform_driver qpnp_pon_driver = { + .driver = { + .name = "qcom,qpnp-power-on", + .of_match_table = spmi_match_table, + }, + .probe = qpnp_pon_probe, + .remove = qpnp_pon_remove, +}; + +static int __init qpnp_pon_init(void) +{ + return platform_driver_register(&qpnp_pon_driver); +} +subsys_initcall(qpnp_pon_init); + +static void __exit qpnp_pon_exit(void) +{ + return platform_driver_unregister(&qpnp_pon_driver); +} +module_exit(qpnp_pon_exit); + +MODULE_DESCRIPTION("QPNP PMIC POWER-ON driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/misc/vl53L0/Makefile b/drivers/input/misc/vl53L0/Makefile new file mode 100644 index 000000000000..f105e1c3c60f --- /dev/null +++ b/drivers/input/misc/vl53L0/Makefile @@ -0,0 +1,20 @@ +# +# Makefile for the vl53L0 drivers. +# + +# Each configuration option enables a list of files. +#FEATURE_USE_CCI := false +FEATURE_USE_CCI := true + +ifeq ($(FEATURE_USE_CCI), true) +ccflags-y += -Idrivers/input/misc/vl53L0/inc -DCAMERA_CCI +else +ccflags-y += -Idrivers/input/misc/vl53L0/inc +endif + +ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io +ccflags-y += -Idrivers/media/platform/msm/camera_v2 +ccflags-y += -Idrivers/media/platform/msm/camera_v2/common +ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci +obj-$(CONFIG_INPUT_STMVL53L0) += stmvl53l0.o +stmvl53l0-objs := stmvl53l0_module.o stmvl53l0_module-i2c.o stmvl53l0_module-cci.o src/vl53l0_api_calibration.o src/vl53l0_api_core.o src/vl53l0_api_histogram.o src/vl53l0_api_ranging.o src/vl53l0_api_strings.o src/vl53l0_api.o src/vl53l0_platform.o src/vl53l0_i2c_platform.o src/vl53l0_port_i2c.o src/vl53l010_api.o src/vl53l010_tuning.o diff --git a/drivers/input/misc/vl53L0/inc/vl53l010_api.h b/drivers/input/misc/vl53L0/inc/vl53l010_api.h new file mode 100644 index 000000000000..282c65e33a93 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l010_api.h @@ -0,0 +1,1476 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ +/* + * @file vl53l0_api.h + * $Date: 2014-12-04 16:15:06 +0100 (Thu, 04 Dec 2014) $ + * Revision: 1906 + */ + + + +#ifndef _VL53L010_API_H_ +#define _VL53L010_API_H_ + +#include "vl53l010_device.h" +#include "vl53l010_strings.h" +#include "vl53l0_def.h" +#include "vl53l0_platform.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef _MSC_VER +# ifdef VL53L0_API_EXPORTS +# define VL53L010_API __declspec(dllexport) +# else +# define VL53L010_API +# endif +#else +# define VL53L010_API +#endif + + +/** @defgroup VL53L010_cut10_group VL53L010 cut1.0 Function Definition + * @brief VL53L010 cut1.0 Function Definition + * @{ + */ + +/** @defgroup VL53L010_general_group VL53L010 General Functions + * @brief VL53L010 General functions and definitions + * @{ + */ + +/** + * @brief Return the VL53L0 PAL Implementation Version + * + * @note This function doesn't access to the device + * + * @param pVersion Pointer to current PAL Implementation Version + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetVersion(VL53L0_Version_t *pVersion); + +/** + * @brief Return the PAL Specification Version used for the current + * implementation. + * + * @note This function doesn't access to the device + * + * @param pPalSpecVersion Pointer to current PAL Specification Version + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetPalSpecVersion( + VL53L0_Version_t *pPalSpecVersion); + + +/** + * @brief Reads the Device information for given Device + * + * @note This function Access to the device.\n + * Use ProductRevisionMajor and ProductRevisionMinor to know the cut + * of the device used. + * + * @param Dev Device Handle + * @param pVL53L0_DeviceInfo Pointer to current device info for a given + * Device + * + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetDeviceInfo(VL53L0_DEV Dev, + VL53L0_DeviceInfo_t *pVL53L0_DeviceInfo); + + +/** + * @brief Read current status of the error register for the selected device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pDeviceErrorStatus Pointer to current error code of the device + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetDeviceErrorStatus(VL53L0_DEV Dev, + VL53L010_DeviceError * pDeviceErrorStatus); + +/** + * @brief Human readable error string for a given Error Code + * + * @note This function doesn't access to the device + * + * @param ErrorCode The error code as stored on + * ::VL53L0_DeviceError + * @param pDeviceErrorString The error string corresponding to the + * ErrorCode + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetDeviceErrorString( + VL53L010_DeviceError ErrorCode, + char *pDeviceErrorString); + + +/** + * @brief Human readable error string for current PAL error status + * + * @note This function doesn't access to the device + * + * @param PalErrorCode The error code as stored on @a VL53L0_Error + * @param pPalErrorString The error string corresponding to the + * PalErrorCode + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetPalErrorString(VL53L0_Error PalErrorCode, + char *pPalErrorString); + + +/** + * @brief Reads the internal state of the PAL for a given Device + * + * @note This function doesn't access to the device + * + * @param Dev Device Handle + * @param pPalState Pointer to current state of the PAL for a + * given Device + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetPalState(VL53L0_DEV Dev, + VL53L0_State * pPalState); + + +/** + * @brief Set the power mode for a given Device + * The power mode can be Standby or Idle. Different level of both Standby and + * Idle can exists. + * This function should not be used when device is in Ranging state. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param PowerMode The value of the power mode to set. + * see ::VL53L0_PowerModes Valid values are: + * VL53L0_POWERMODE_STANDBY_LEVEL1, VL53L0_POWERMODE_IDLE_LEVEL1 + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_MODE_NOT_SUPPORTED This error occurs when PowerMode + * is not in the supported list + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetPowerMode(VL53L0_DEV Dev, + VL53L0_PowerModes PowerMode); + +/** + * @brief Get the power mode for a given Device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pPowerMode Pointer to the current value of the power + * mode. see ::VL53L0_PowerModes. Valid values are: + * VL53L0_POWERMODE_STANDBY_LEVEL1, VL53L0_POWERMODE_IDLE_LEVEL1 + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetPowerMode(VL53L0_DEV Dev, + VL53L0_PowerModes * pPowerMode); + + +/** + * Set or over-hide part to part calibration offset + * \sa VL53L0_DataInit() VL53L0_GetOffsetCalibrationDataMicroMeter() + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param OffsetCalibrationDataMicroMeter Offset (in micrometer) + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetOffsetCalibrationDataMicroMeter( + VL53L0_DEV Dev, + int32_t OffsetCalibrationDataMicroMeter); + +/** + * @brief Get part to part calibration offset + * + * @par Function Description + * Should only be used after a successful call to @a VL53L0_DataInit to backup + * device NVM value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pOffsetCalibrationDataMicroMeter Return part to part calibration + * offset from device (in micro meter) + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetOffsetCalibrationDataMicroMeter( + VL53L0_DEV Dev, + int32_t *pOffsetCalibrationDataMicroMeter); + +/** + * Set Group parameter Hold state + * + * @par Function Description + * Set or remove device internal group parameter hold + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param GroupParamHold Group parameter Hold state to be set (on/off) + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L010_API VL53L0_Error VL53L010_SetGroupParamHold(VL53L0_DEV Dev, + uint8_t GroupParamHold); + +/** + * @brief Get the maximal distance for actual setup + * @par Function Description + * Device must be initialized through @a VL53L0_SetParameters() prior calling + * this function. + * + * Any range value more than the value returned is to be considered as "no + * target detected" + * or "no target in detectable range"\n + * @warning The maximal distance depends on the setup + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param pUpperLimitMilliMeter The maximal range limit for actual setup + * (in millimeter) + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L010_API VL53L0_Error VL53L010_GetUpperLimitMilliMeter(VL53L0_DEV Dev, + uint16_t *pUpperLimitMilliMeter); + +/** @} VL53L010_general_group */ + + +/** @defgroup VL53L010_init_group VL53L010 Init Functions + * @brief VL53L010 Init Functions + * @{ + */ + +/** + * @brief Set new device address + * + * After completion the device will answer to the new address programmed. This + * function should be called when several devices are used in parallel + * before start programming the sensor. + * When a single device us used, there is no need to call this function. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param DeviceAddress The new Device address + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetDeviceAddress(VL53L0_DEV Dev, + uint8_t DeviceAddress); + +/** + * + * @brief One time device initialization + * + * To be called once and only once after device is brought out of reset (Chip + * enable) and booted see @a VL53L0_WaitDeviceBooted() + * + * @par Function Description + * When not used after a fresh device "power up" or reset, it may return @a + * #VL53L0_ERROR_CALIBRATION_WARNING + * meaning wrong calibration data may have been fetched from device that can + * result in ranging offset error\n + * If application cannot execute device reset or need to run VL53L0_DataInit + * multiple time + * then it must ensure proper offset calibration saving and restore on its own + * by using @a VL53L0_GetOffsetCalibrationData() on first power up and then @a + * VL53L0_SetOffsetCalibrationData() in all subsequent init + * This function will change the VL53L0_State from VL53L0_STATE_POWERDOWN to + * VL53L0_STATE_WAIT_STATICINIT. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_DataInit(VL53L0_DEV Dev); + +/** + * @brief Do basic device init (and eventually patch loading) + * This function will change the VL53L0_State from VL53L0_STATE_WAIT_STATICINIT + * to VL53L0_STATE_IDLE. + * In this stage all defalut setting will be applied. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_StaticInit(VL53L0_DEV Dev); + +/** + * @brief Wait for device booted after chip enable (hardware standby) + * This function can be run only when VL53L0_State is VL53L0_STATE_POWERDOWN. + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + * + */ +VL53L010_API VL53L0_Error VL53L010_WaitDeviceBooted(VL53L0_DEV Dev); + +/** + * @brief Do an hard reset or soft reset (depending on implementation) of the + * device \n + * After call of this function, device must be in same state as right after a + * power-up sequence. + * This function will change the VL53L0_State to VL53L0_STATE_POWERDOWN. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_ResetDevice(VL53L0_DEV Dev); + +/** @} VL53L010_init_group */ + + +/** @defgroup VL53L010_parameters_group VL53L010 Parameters Functions + * @brief VL53L010 Functions used to prepare and setup the device + * @{ + */ + +/** + * @brief Prepare device for operation + * @par Function Description + * Update device with provided parameters + * @li Then start ranging operation. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pDeviceParameters Pointer to store current device parameters. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetDeviceParameters(VL53L0_DEV Dev, + const VL53L0_DeviceParameters_t *pDeviceParameters); + +/** + * @brief Retrieve current device parameters + * @par Function Description + * Get actual parameters of the device + * @li Then start ranging operation. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pDeviceParameters Pointer to store current device parameters. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetDeviceParameters(VL53L0_DEV Dev, + VL53L0_DeviceParameters_t *pDeviceParameters); + +/** + * @brief Set a new device mode + * @par Function Description + * Set device to a new mode (ranging, histogram ...) + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param DeviceMode New device mode to apply + * Valid values are: + * VL53L0_DEVICEMODE_SINGLE_RANGING + * VL53L0_DEVICEMODE_CONTINUOUS_RANGING + * VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING + * VL53L0_DEVICEMODE_SINGLE_HISTOGRAM + * (functionality not available) + * + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_MODE_NOT_SUPPORTED This error occurs when + * DeviceMode is not in the supported list + */ +VL53L010_API VL53L0_Error VL53L010_SetDeviceMode(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode); + +/** + * @brief Get current new device mode + * @par Function Description + * Get actual mode of the device(ranging, histogram ...) + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pDeviceMode Pointer to current apply mode value + * Valid values are: + * VL53L0_DEVICEMODE_SINGLE_RANGING + * VL53L0_DEVICEMODE_CONTINUOUS_RANGING + * VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING + * VL53L0_DEVICEMODE_SINGLE_HISTOGRAM + * (functionality not available) + * + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_MODE_NOT_SUPPORTED This error occurs when + * DeviceMode is not in the supported list + */ +VL53L010_API VL53L0_Error VL53L010_GetDeviceMode(VL53L0_DEV Dev, + VL53L0_DeviceModes * pDeviceMode); + +/** + * @brief Set a new Histogram mode + * @par Function Description + * Set device to a new Histogram mode + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param HistogramMode New device mode to apply + * Valid values are: + * VL53L0_HISTOGRAMMODE_DISABLED + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_MODE_NOT_SUPPORTED This error occurs when + * HistogramMode is not in the supported list + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetHistogramMode(VL53L0_DEV Dev, + VL53L0_HistogramModes HistogramMode); + +/** + * @brief Get current new device mode + * @par Function Description + * Get current Histogram mode of a Device + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pHistogramMode Pointer to current Histogram Mode value + * Valid values are: + * VL53L0_HISTOGRAMMODE_DISABLED + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetHistogramMode(VL53L0_DEV Dev, + VL53L0_HistogramModes * pHistogramMode); + +/** + * @brief Set Ranging Timing Budget in microseconds + * + * @par Function Description + * Defines the maximum time allowed by the user to the device to run a full + * ranging sequence + * for the current mode (ranging, histogram, ASL ...) + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param MeasurementTimingBudgetMicroSeconds Max measurement time in + * microseconds. + * Valid values are: + * >= 17000 microseconds when + * wraparound is enabled + * >= 12000 microseconds when + * wraparound is disabled + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned if + * MeasurementTimingBudgetMicroSeconds is out of range + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetMeasurementTimingBudgetMicroSeconds( + VL53L0_DEV Dev, + uint32_t MeasurementTimingBudgetMicroSeconds); + +/** + * @brief Get Ranging Timing Budget in microseconds + * + * @par Function Description + * Returns the programmed the maximum time allowed by the user to the device to + * run a full ranging sequence + * for the current mode (ranging, histogram, ASL ...) + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pMeasurementTimingBudgetMicroSeconds Max measurement time in + * microseconds. + * Valid values are: + * >= 17000 microseconds when + * wraparound is enabled + * >= 12000 microseconds when + * wraparound is disabled + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetMeasurementTimingBudgetMicroSeconds( + VL53L0_DEV Dev, + uint32_t *pMeasurementTimingBudgetMicroSeconds); + +/** + * Program continuous mode Inter-Measurement period in milliseconds + * + * @par Function Description + * When trying to set too short time return INVALID_PARAMS minimal value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param InterMeasurementPeriodMilliSeconds Requires Inter-Measurement + * Period in milliseconds. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetInterMeasurementPeriodMilliSeconds( + VL53L0_DEV Dev, + uint32_t InterMeasurementPeriodMilliSeconds); + +/** + * Get continuous mode Inter-Measurement period in milliseconds + * + * @par Function Description + * When trying to set too short time return INVALID_PARAMS minimal value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pInterMeasurementPeriodMilliSeconds Pointer to programmed + * Inter-Measurement Period in milliseconds. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetInterMeasurementPeriodMilliSeconds( + VL53L0_DEV Dev, + uint32_t *pInterMeasurementPeriodMilliSeconds); + +/** + * @brief Enable/Disable Cross talk compensation feature + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param XTalkCompensationEnable Cross talk compensation to be set + * 0=disabled else = enabled + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetXTalkCompensationEnable( + VL53L0_DEV Dev, uint8_t XTalkCompensationEnable); + +/** + * @brief Get Cross talk compensation rate + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pXTalkCompensationEnable Pointer to the Cross talk compensation + * state 0=disabled or 1 = enabled + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetXTalkCompensationEnable( + VL53L0_DEV Dev, uint8_t *pXTalkCompensationEnable); + +/** + * @brief Set Cross talk compensation rate + * + * @par Function Description + * Set Cross talk compensation rate. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param XTalkCompensationRateMegaCps Compensation rate in Mega counts per + * second (16.16 fix point) see datasheet for details + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetXTalkCompensationRateMegaCps( + VL53L0_DEV Dev, + FixPoint1616_t XTalkCompensationRateMegaCps); + +/** + * @brief Get Cross talk compensation rate + * + * @par Function Description + * Get Cross talk compensation rate. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pXTalkCompensationRateMegaCps Pointer to Compensation rate in Mega + * counts per second (16.16 fix point) see datasheet for details + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetXTalkCompensationRateMegaCps( + VL53L0_DEV Dev, + FixPoint1616_t *pXTalkCompensationRateMegaCps); + + +/** + * @brief Get the number of the check limit managed by a given Device + * + * @par Function Description + * This function give the number of the check limit managed by the Device + * + * @note This function doesn't Access to the device + * + * @param pNumberOfLimitCheck Pointer to the number of check limit. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetNumberOfLimitCheck( + uint16_t *pNumberOfLimitCheck); + +/** + * @brief Return a description string for a given limit check number + * + * @par Function Description + * This function returns a description string for a given limit check number. + * The limit check is identified with the LimitCheckId. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID (0<= LimitCheckId < + * VL53L0_GetNumberOfLimitCheck() ). + * @param pLimitCheckString Pointer to the description string of + * the given check limit. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned when + * LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetLimitCheckInfo(VL53L0_DEV Dev, + uint16_t LimitCheckId, char *pLimitCheckString); + + +/** + * @brief Enable/Disable a specific limit check + * + * @par Function Description + * This function Enable/Disable a specific limit check. + * The limit check is identified with the LimitCheckId. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID (0<= LimitCheckId < + * VL53L0_GetNumberOfLimitCheck() ). + * @param LimitCheckEnable if 1 the check limit corresponding to + * LimitCheckId is Enabled + * if 0 the check limit corresponding to + * LimitCheckId is disabled + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned when + * LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetLimitCheckEnable(VL53L0_DEV Dev, + uint16_t LimitCheckId, uint8_t LimitCheckEnable); + + +/** + * @brief Get specific limit check enable state + * + * @par Function Description + * This function get the enable state of a specific limit check. + * The limit check is identified with the LimitCheckId. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID (0<= LimitCheckId < + * VL53L0_GetNumberOfLimitCheck() ). + * @param pLimitCheckEnable Pointer to the check limit enable + * value. + * if 1 the check limit corresponding to + * LimitCheckId is Enabled + * if 0 the check limit corresponding to + * LimitCheckId is disabled + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned when + * LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetLimitCheckEnable(VL53L0_DEV Dev, + uint16_t LimitCheckId, uint8_t *pLimitCheckEnable); + +/** + * @brief Set a specific limit check value + * + * @par Function Description + * This function set a specific limit check value. + * The limit check is identified with the LimitCheckId. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID (0<= LimitCheckId < + * VL53L0_GetNumberOfLimitCheck() ). + * @param LimitCheckValue Limit check Value for a given + * LimitCheckId + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned when either + * LimitCheckId or LimitCheckValue value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetLimitCheckValue(VL53L0_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t LimitCheckValue); + +/** + * @brief Get a specific limit check value + * + * @par Function Description + * This function get a specific limit check value from device then it updates + * internal values and check enables. + * The limit check is identified with the LimitCheckId. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID (0<= LimitCheckId < + * VL53L0_GetNumberOfLimitCheck() ). + * @param pLimitCheckValue Pointer to Limit check Value for a + * given LimitCheckId. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned when + * LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetLimitCheckValue(VL53L0_DEV Dev, + uint16_t LimitCheckId, + FixPoint1616_t *pLimitCheckValue); + + +/** + * @brief Get the current value of the signal used for the limit check + * + * @par Function Description + * This function get a the current value of the signal used for the limit check. + * To obtain the latest value you should run a ranging before. + * The value reported is linked to the limit check identified with the + * LimitCheckId. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID (0<= LimitCheckId < + * VL53L0_GetNumberOfLimitCheck() ). + * @param pLimitCheckCurrent Pointer to current Value for a + * given LimitCheckId. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned when + * LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetLimitCheckCurrent(VL53L0_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t *pLimitCheckCurrent); + +/** + * @brief Enable (or disable) Wrap around Check + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param WrapAroundCheckEnable Wrap around Check to be set 0=disabled, + * other = enabled + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetWrapAroundCheckEnable(VL53L0_DEV Dev, + uint8_t WrapAroundCheckEnable); + +/** + * @brief Get setup of Wrap around Check + * + * @par Function Description + * This function get the wrapAround check enable parameters + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pWrapAroundCheckEnable Pointer to the Wrap around Check state + * 0=disabled or 1 = enabled + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetWrapAroundCheckEnable(VL53L0_DEV Dev, + uint8_t *pWrapAroundCheckEnable); + +/** @} VL53L010_parameters_group */ + + +/** @defgroup VL53L010_measurement_group VL53L010 Measurement Functions + * @brief VL53L010 Functions used for the measurements + * @{ + */ + +/** + * @brief Single shot measurement. + * + * @par Function Description + * Perform simple measurement sequence (Start measure, Wait measure to end, and + * returns when measurement is done). + * Once function returns, user can get valid data by calling + * VL53L0_GetRangingMeasurement or VL53L0_GetHistogramMeasurement depending on + * defined measurement mode + * User should Clear the interrupt in case this are enabled by using the + * function VL53L0_ClearInterruptMask(). + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_PerformSingleMeasurement(VL53L0_DEV Dev); + +/** + * @brief Perform Reference Calibration + * + * @details Perform a reference calibration of the Device. + * This function should be run from time to time before doing a ranging + * measurement. + * This function will launch a special ranging measurement, so if interrupt are + * enable an interrupt will be done. + * This function will clear the interrupt generated automatically. + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_PerformRefCalibration(VL53L0_DEV Dev); + +/** + * @brief Perform XTalk Calibration + * + * @details Perform a XTalk calibration of the Device. + * This function will launch a ranging measurement, if interrupts are enabled + * an interrupt will be done. + * This function will clear the interrupt generated automatically. + * This function will program a new value for the XTalk compensation and it + * will enable the cross talk before exit. + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @note This function change the device mode to + * VL53L0_DEVICEMODE_SINGLE_RANGING + * + * @param Dev Device Handle + * @param XTalkCalDistance XTalkCalDistance value used for the + * XTalk computation. + * @param pXTalkCompensationRateMegaCps Pointer to new XTalkCompensation + * value. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_PerformXTalkCalibration(VL53L0_DEV Dev, + FixPoint1616_t XTalkCalDistance, + FixPoint1616_t *pXTalkCompensationRateMegaCps); + +/** + * @brief Perform Offset Calibration + * + * @details Perform a Offset calibration of the Device. + * This function will launch a ranging measurement, if interrupts are + * enabled an interrupt will be done. + * This function will clear the interrupt generated automatically. + * This function will program a new value for the Offset calibration value and + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @note This function does not change the device mode. + * + * @param Dev Device Handle + * @param CalDistanceMilliMeter Calibration distance value used for the + * offset compensation. + * @param pOffsetMicroMeter Pointer to new Offset value computed by the + * function. + * + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_PerformOffsetCalibration(VL53L0_DEV Dev, + FixPoint1616_t CalDistanceMilliMeter, int32_t *pOffsetMicroMeter); + +/** + * @brief Start device measurement + * + * @details Started measurement will depend on device parameters set through @a + * VL53L0_SetParameters() + * This is a non-blocking function + * This function will change the VL53L0_State from VL53L0_STATE_IDLE to + * VL53L0_STATE_RUNNING. + * + * @note This function Access to the device + * + + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_MODE_NOT_SUPPORTED This error occurs when + * DeviceMode programmed with @a VL53L0_SetDeviceMode is not in the supported + * list: + * Supported mode are: + * VL53L0_DEVICEMODE_SINGLE_RANGING, VL53L0_DEVICEMODE_CONTINUOUS_RANGING, + * VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING + * @return VL53L0_ERROR_TIME_OUT Time out on start measurement + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_StartMeasurement(VL53L0_DEV Dev); + +/** + * @brief Stop device measurement + * + * @details Will set the device in standby mode at end of current measurement \n + * Not necessary in single mode as device shall return automatically + * in standby mode at end of measurement. + * This function will change the VL53L0_State from + * VL53L0_STATE_RUNNING to VL53L0_STATE_IDLE. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_StopMeasurement(VL53L0_DEV Dev); + +/** + * @brief Return Measurement Data Ready + * + * @par Function Description + * This function indicate that a measurement data is ready. + * This function check if interrupt mode is used then check is done accordingly. + * If perform function clear the interrupt, this function will not work, like + * in case of @a VL53L0_PerformSingleRangingMeasurement(). + * The previous function is blocking function, VL53L0_GetMeasurementDataReady + * is used for non-blocking capture. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pMeasurementDataReady Pointer to Measurement Data Ready. 0=data + * not ready, 1 = data ready + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetMeasurementDataReady(VL53L0_DEV Dev, + uint8_t *pMeasurementDataReady); + +/** + * @brief Wait for device ready for a new measurement command. Blocking + * function. + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param MaxLoop Max Number of polling loop (timeout). + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L010_API VL53L0_Error VL53L010_WaitDeviceReadyForNewMeasurement( + VL53L0_DEV Dev, + uint32_t MaxLoop); + + +/** + * @brief Retrieve the measurements from device for a given setup + * + * @par Function Description + * Get data from last successful Ranging measurement + * @warning USER should take care about @a VL53L0_GetNumberOfROIZones() before + * get data. + * PAL will fill a NumberOfROIZones times the corresponding data structure used + * in the measurement function. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pRangingMeasurementData Pointer to the data structure to fill up. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetRangingMeasurementData(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData); + +/** + * @brief Retrieve the measurements from device for a given setup + * + * @par Function Description + * Get data from last successful Histogram measurement + * @warning USER should take care about @a VL53L0_GetNumberOfROIZones() before + * get data. + * PAL will fill a NumberOfROIZones times the corresponding data structure used + * in the measurement function. + * @note This function is not Implemented + * @param Dev Device Handle + * @param pHistogramMeasurementData Pointer to the data structure to fill + * up. + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L010_API VL53L0_Error VL53L010_GetHistogramMeasurementData(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData); + +/** + * @brief Performs a single ranging measurement and retrieve the ranging + * measurement data + * + * @par Function Description + * This function will change the device mode to + * VL53L0_DEVICEMODE_SINGLE_RANGING with @a VL53L0_SetDeviceMode(), + * It performs measurement with @a VL53L0_PerformSingleMeasurement() + * It get data from last successful Ranging measurement with @a + * VL53L0_GetRangingMeasurementData. + * Finally it clear the interrupt with @a VL53L0_ClearInterruptMask(). + * @note This function Access to the device + * + * @note This function change the device mode to + * VL53L0_DEVICEMODE_SINGLE_RANGING + * + * @param Dev Device Handle + * @param pRangingMeasurementData Pointer to the data structure to fill up. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_PerformSingleRangingMeasurement( + VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData); + +/** + * @brief Performs a single histogram measurement and retrieve the histogram + * measurement data + * Is equivalent to VL53L0_PerformSingleMeasurement + + * VL53L0_GetHistogramMeasurementData + * + * @par Function Description + * Get data from last successful Ranging measurement. + * This function will clear the interrupt in case of these are enabled. + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param pHistogramMeasurementData Pointer to the data structure to fill + * up. + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L010_API VL53L0_Error VL53L010_PerformSingleHistogramMeasurement( + VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData); + + + +/** + * @brief Set the number of ROI Zones to be used for a specific Device + * + * @par Function Description + * Set the number of ROI Zones to be used for a specific Device. + * The programmed value should be less than the max number of ROI Zones given + * with @a VL53L0_GetMaxNumberOfROIZones(). + * This version of API manage only one zone. + * + * @param Dev Device Handle + * @param NumberOfROIZones Number of ROI Zones to be used for a + * specific Device. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned if + * NumberOfROIZones != 1 + */ +VL53L010_API VL53L0_Error VL53L010_SetNumberOfROIZones(VL53L0_DEV Dev, + uint8_t NumberOfROIZones); + +/** + * @brief Get the number of ROI Zones managed by the Device + * + * @par Function Description + * Get number of ROI Zones managed by the Device + * USER should take care about @a VL53L0_GetNumberOfROIZones() before get data + * after a perform measurement. + * PAL will fill a NumberOfROIZones times the corresponding data structure used + * in the measurement function. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pNumberOfROIZones Pointer to the Number of ROI Zones + * value. + * @return VL53L0_ERROR_NONE Success + */ +VL53L010_API VL53L0_Error VL53L010_GetNumberOfROIZones(VL53L0_DEV Dev, + uint8_t *pNumberOfROIZones); + +/** + * @brief Get the Maximum number of ROI Zones managed by the Device + * + * @par Function Description + * Get Maximum number of ROI Zones managed by the Device. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pMaxNumberOfROIZones Pointer to the Maximum Number of ROI + * Zones value. + * @return VL53L0_ERROR_NONE Success + */ +VL53L010_API VL53L0_Error VL53L010_GetMaxNumberOfROIZones(VL53L0_DEV Dev, + uint8_t *pMaxNumberOfROIZones); + + +/** @} VL53L010_measurement_group */ + + +/** @defgroup VL53L010_interrupt_group VL53L010 Interrupt Functions + * @brief VL53L010 Functions used for interrupt managements + * @{ + */ + +/** + * @brief Set the configuration of GPIO pin for a given device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param Pin ID of the GPIO Pin + * @param Functionality Select Pin functionality. Refer to + * ::VL53L0_GpioFunctionality + * @param DeviceMode Device Mode associated to the Gpio. + * @param Polarity Set interrupt polarity. Active high or active + * low see ::VL53L0_InterruptPolarity + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_GPIO_NOT_EXISTING Only Pin=0 is + * accepted. + * @return VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED This error occurs + * when Functionality programmed is not in the supported list: + * Supported value + * are: + * + * VL53L0_GPIOFUNCTIONALITY_OFF, VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW, + * VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH, + * VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT, + * + * VL53L0_GPIOFUNCTIONALITY_NEW_MEASURE_READY + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetGpioConfig(VL53L0_DEV Dev, uint8_t Pin, + VL53L0_DeviceModes DeviceMode, + VL53L0_GpioFunctionality Functionality, + VL53L0_InterruptPolarity Polarity); + + +/** + * @brief Get current configuration for GPIO pin for a given device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param Pin ID of the GPIO Pin + * @param pDeviceMode Pointer to Device Mode associated to the Gpio. + * @param pFunctionality Pointer to Pin functionality. + * Refer to ::VL53L0_GpioFunctionality + * @param pPolarity Pointer to interrupt polarity. Active high or + * active low see ::VL53L0_InterruptPolarity + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_GPIO_NOT_EXISTING Only Pin=0 is accepted. + * @return VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED This error occurs + * when Functionality programmed is not in the supported list: + * Supported value are: + * VL53L0_GPIOFUNCTIONALITY_OFF, VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW, + * VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH, + * VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT, + * VL53L0_GPIOFUNCTIONALITY_NEW_MEASURE_READY + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetGpioConfig(VL53L0_DEV Dev, uint8_t Pin, + VL53L0_DeviceModes * pDeviceMode, + VL53L0_GpioFunctionality * pFunctionality, + VL53L0_InterruptPolarity * pPolarity); + +/** + * @brief Set low and high Interrupt thresholds for a given mode (ranging, ALS, + * ...) for a given device + * + * @par Function Description + * Set low and high Interrupt thresholds for a given mode (ranging, ALS, ...) + * for a given device + * + * @note This function Access to the device + * + * @note DeviceMode is ignored for the current device + * + * @param Dev Device Handle + * @param DeviceMode Device Mode for which change thresholds + * @param ThresholdLow Low threshold (mm, lux ..., depending on the + * mode) + * @param ThresholdHigh High threshold (mm, lux ..., depending on the + * mode) + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetInterruptThresholds(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode, + FixPoint1616_t ThresholdLow, + FixPoint1616_t ThresholdHigh); + +/** + * @brief Get high and low Interrupt thresholds for a given mode (ranging, + * ALS, ...) for a given device + * + * @par Function Description + * Get high and low Interrupt thresholds for a given mode (ranging, ALS, ...) + * for a given device + * + * @note This function Access to the device + * + * @note DeviceMode is ignored for the current device + * + * @param Dev Device Handle + * @param DeviceMode Device Mode from which read thresholds + * @param pThresholdLow Low threshold (mm, lux ..., depending on the + * mode) + * @param pThresholdHigh High threshold (mm, lux ..., depending on the + * mode) + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetInterruptThresholds(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode, + FixPoint1616_t *pThresholdLow, + FixPoint1616_t *pThresholdHigh); + +/** + * @brief Clear given system interrupt condition + * + * @par Function Description + * Clear given interrupt(s). + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param InterruptMask Mask of interrupts to clear + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_ClearInterruptMask(VL53L0_DEV Dev, + uint32_t InterruptMask); + +/** + * @brief Return device interrupt status + * + * @par Function Description + * Returns currently raised interrupts by the device. + * User shall be able to activate/deactivate interrupts through + * @a VL53L0_SetGpioConfig() + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pInterruptMaskStatus Pointer to status variable to update + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetInterruptMaskStatus(VL53L0_DEV Dev, + uint32_t *pInterruptMaskStatus); + + +/** + * @brief Configure ranging interrupt reported to system + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param InterruptMask Mask of interrupt to Enable/disable + * (0:interrupt disabled or 1: interrupt enabled) + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L010_API VL53L0_Error VL53L010_EnableInterruptMask(VL53L0_DEV Dev, + uint32_t InterruptMask); + + +/** @} VL53L010_interrupt_group */ + + +/** @defgroup VL53L010_SPADfunctions_group VL53L010 SPAD Functions + * @brief VL53L010 Functions used for SPAD managements + * @{ + */ + + + +/** + * @brief Set the SPAD Ambient Damper Threshold value + * + * @par Function Description + * This function set the SPAD Ambient Damper Threshold value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param SpadAmbientDamperThreshold SPAD Ambient Damper Threshold value + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetSpadAmbientDamperThreshold(VL53L0_DEV Dev, + uint16_t SpadAmbientDamperThreshold); + +/** + * @brief Get the current SPAD Ambient Damper Threshold value + * + * @par Function Description + * This function get the SPAD Ambient Damper Threshold value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pSpadAmbientDamperThreshold Pointer to programmed SPAD Ambient + * Damper Threshold value + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetSpadAmbientDamperThreshold(VL53L0_DEV Dev, + uint16_t *pSpadAmbientDamperThreshold); + + +/** + * @brief Set the SPAD Ambient Damper Factor value + * + * @par Function Description + * This function set the SPAD Ambient Damper Factor value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param SpadAmbientDamperFactor SPAD Ambient Damper Factor value + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_SetSpadAmbientDamperFactor(VL53L0_DEV Dev, + uint16_t SpadAmbientDamperFactor); + +/** + * @brief Get the current SPAD Ambient Damper Factor value + * + * @par Function Description + * This function get the SPAD Ambient Damper Factor value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pSpadAmbientDamperFactor Pointer to programmed SPAD Ambient + * Damper Factor value + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L010_API VL53L0_Error VL53L010_GetSpadAmbientDamperFactor(VL53L0_DEV Dev, + uint16_t *pSpadAmbientDamperFactor); + + +/** @} VL53L010_SPADfunctions_group */ + +/** @} VL53L010_cut10_group */ +#define VL53L010_EXTERNAL + + +/* Internal functions declaration */ +VL53L010_EXTERNAL VL53L0_Error VL53L010_get_vcsel_pulse_period(VL53L0_DEV Dev, + uint8_t *pVCSELPulsePeriod, + uint8_t RangeIndex); +VL53L010_EXTERNAL uint8_t VL53L010_encode_vcsel_period( + uint8_t vcsel_period_pclks); +VL53L010_EXTERNAL uint8_t VL53L010_decode_vcsel_period(uint8_t + vcsel_period_reg); +VL53L010_EXTERNAL uint16_t VL53L010_calc_encoded_timeout(VL53L0_DEV Dev, + uint32_t + timeout_period_us, + uint8_t vcsel_period); +VL53L010_EXTERNAL uint32_t VL53L010_calc_ranging_wait_us(VL53L0_DEV Dev, + uint16_t + timeout_overall_periods, + uint8_t vcsel_period); +VL53L010_EXTERNAL VL53L0_Error VL53L010_load_additional_settings1(VL53L0_DEV + Dev); +VL53L010_EXTERNAL VL53L0_Error VL53L010_load_additional_settings3(VL53L0_DEV + Dev); +VL53L010_EXTERNAL VL53L0_Error VL53L010_check_part_used(VL53L0_DEV Dev, + uint8_t *Revision, + VL53L0_DeviceInfo_t * + pVL53L0_DeviceInfo); +VL53L010_EXTERNAL VL53L0_Error VL53L010_get_info_from_device(VL53L0_DEV Dev); +VL53L010_EXTERNAL VL53L0_Error VL53L010_device_read_strobe(VL53L0_DEV Dev); +VL53L010_EXTERNAL VL53L0_Error VL53L010_get_pal_range_status(VL53L0_DEV Dev, + uint8_t + DeviceRangeStatus, + FixPoint1616_t + SignalRate, + FixPoint1616_t + CrosstalkCompensation, + uint16_t + EffectiveSpadRtnCount, + VL53L0_RangingMeasurementData_t + * + pRangingMeasurementData, + uint8_t * + pPalRangeStatus); + + +VL53L010_EXTERNAL uint32_t VL53L010_calc_macro_period_ps(VL53L0_DEV Dev, + uint8_t vcsel_period); +VL53L010_EXTERNAL uint16_t VL53L010_encode_timeout(uint32_t timeout_mclks); +VL53L010_EXTERNAL uint32_t VL53L010_decode_timeout(uint16_t encoded_timeout); + + + + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L010_API_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l010_device.h b/drivers/input/misc/vl53L0/inc/vl53l010_device.h new file mode 100644 index 000000000000..5d36ab0f65e4 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l010_device.h @@ -0,0 +1,237 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +/** + * Device specific defines. To be adapted by implementer for the targeted + * device. + */ + +#ifndef _VL53L010_DEVICE_H_ +#define _VL53L010_DEVICE_H_ + +#include "vl53l0_types.h" + +/** @defgroup VL53L010_SpecDefines_group VL53L010 cut1.0 Device Specific Defines + * @brief VL53L010 cut1.0 Device Specific Defines + * @{ + */ + +/** @defgroup VL53L010_DeviceError_group Device Error + * @brief Device Error code + * + * This enum is Device specific it should be updated in the implementation + * Use @a VL53L010_GetStatusErrorString() to get the string. + * It is related to Status Register of the Device. + * @{ + */ +typedef uint8_t VL53L010_DeviceError; + +#define VL53L010_DEVICEERROR_NONE ((VL53L010_DeviceError) 0) +#define VL53L010_DEVICEERROR_VCSELCONTINUITYTESTFAILURE \ + ((VL53L010_DeviceError) 1) +#define VL53L010_DEVICEERROR_VCSELWATCHDOGTESTFAILURE \ + ((VL53L010_DeviceError) 2) +#define VL53L010_DEVICEERROR_NOVHVVALUEFOUND \ + ((VL53L010_DeviceError) 3) +#define VL53L010_DEVICEERROR_MSRCNOTARGET \ + ((VL53L010_DeviceError) 4) +#define VL53L010_DEVICEERROR_MSRCMINIMUMSNR \ + ((VL53L010_DeviceError) 5) +#define VL53L010_DEVICEERROR_MSRCWRAPAROUND \ + ((VL53L010_DeviceError) 6) +#define VL53L010_DEVICEERROR_TCC \ + ((VL53L010_DeviceError) 7) +#define VL53L010_DEVICEERROR_RANGEAWRAPAROUND ((VL53L010_DeviceError)8) +#define VL53L010_DEVICEERROR_RANGEBWRAPAROUND ((VL53L010_DeviceError)9) +#define VL53L010_DEVICEERROR_MINCLIP ((VL53L010_DeviceError) 10) +#define VL53L010_DEVICEERROR_RANGECOMPLETE ((VL53L010_DeviceError) 11) +#define VL53L010_DEVICEERROR_ALGOUNDERFLOW ((VL53L010_DeviceError) 12) +#define VL53L010_DEVICEERROR_ALGOOVERFLOW ((VL53L010_DeviceError) 13) +#define VL53L010_DEVICEERROR_FINALSNRLIMIT ((VL53L010_DeviceError) 14) +#define VL53L010_DEVICEERROR_NOTARGETIGNORE ((VL53L010_DeviceError) 15) + +/** @} VL53L010_DeviceError_group */ + +/** @defgroup VL53L010_CheckEnable_group Check Enable list + * @brief Check Enable code + * + * Define used to specify the LimitCheckId. + * Use @a VL53L010_GetLimitCheckInfo() to get the string. + * @{ + */ + +#define VL53L010_CHECKENABLE_SIGMA_FINAL_RANGE 0 +#define VL53L010_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE 1 +#define VL53L010_CHECKENABLE_NUMBER_OF_CHECKS 2 + +/** @} VL53L010_CheckEnable_group */ + +/** @defgroup VL53L010_GpioFunctionality_group Gpio Functionality + * @brief Defines the different functionalities for the device GPIO(s) + * @{ + */ +typedef uint8_t VL53L010_GpioFunctionality; + +#define VL53L010_GPIOFUNCTIONALITY_OFF \ + ((VL53L010_GpioFunctionality) 0)/*!< NO Interrupt */ +#define VL53L010_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW \ + ((VL53L010_GpioFunctionality) 1)/*!< Level Low (value < thresh_low) */ +#define VL53L010_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH \ + ((VL53L010_GpioFunctionality) 2)/*!< Level High (value>thresh_high) */ +/*!< Out Of Window (value < thresh_low OR value > thresh_high) */ +#define VL53L010_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT \ + ((VL53L010_GpioFunctionality) 3) +#define VL53L010_GPIOFUNCTIONALITY_NEW_MEASURE_READY \ + ((VL53L010_GpioFunctionality) 4) /*!< New Sample Ready */ + +/** @} VL53L010_GpioFunctionality_group */ + +/* Device register map */ + +/** @defgroup VL53L010_DefineRegisters_group Define Registers + * @brief List of all the defined registers + * @{ + */ +#define VL53L010_REG_SYSRANGE_START 0x000 + /** mask existing bit in #VL53L010_REG_SYSRANGE_START*/ +#define VL53L010_REG_SYSRANGE_MODE_MASK 0x0F + /** bit 0 in #VL53L010_REG_SYSRANGE_START write 1 toggle state in + * continuous mode and arm next shot in single shot mode + */ +#define VL53L010_REG_SYSRANGE_MODE_START_STOP 0x01 + /** bit 1 write 0 in #VL53L010_REG_SYSRANGE_START set single shot mode */ +#define VL53L010_REG_SYSRANGE_MODE_SINGLESHOT 0x00 + /** bit 1 write 1 in #VL53L010_REG_SYSRANGE_START set back-to-back + * operation mode + */ +#define VL53L010_REG_SYSRANGE_MODE_BACKTOBACK 0x02 + /** bit 2 write 1 in #VL53L010_REG_SYSRANGE_START set timed operation + * mode + */ +#define VL53L010_REG_SYSRANGE_MODE_TIMED 0x04 + /** bit 3 write 1 in #VL53L010_REG_SYSRANGE_START set histogram + * operation mode + */ +#define VL53L010_REG_SYSRANGE_MODE_HISTOGRAM 0x08 + +#define VL53L010_REG_SYSTEM_THRESH_HIGH 0x000C /* NOSLC 2 bytes */ +#define VL53L010_REG_SYSTEM_THRESH_LOW 0x000E /* NOSLC 2 bytes */ + +/* FPGA bitstream */ +#define VL53L010_REG_SYSTEM_SEQUENCE_CONFIG 0x0001 +#define VL53L010_REG_SYSTEM_INTERMEASUREMENT_PERIOD 0x0004 + +#define VL53L010_REG_SYSTEM_REPORT_REQUEST 0x0009 +#define VL53L010_REG_SYSTEM_RANGEA_DATA 0x04 +#define VL53L010_REG_SYSTEM_RANGEB_DATA 0x05 + +#define VL53L010_REG_SYSTEM_INTERRUPT_CONFIG_GPIO 0x000A +#define VL53L010_REG_SYSTEM_INTERRUPT_GPIO_DISABLED 0x00 +#define VL53L010_REG_SYSTEM_INTERRUPT_GPIO_LEVEL_LOW 0x01 +#define VL53L010_REG_SYSTEM_INTERRUPT_GPIO_LEVEL_HIGH 0x02 +#define VL53L010_REG_SYSTEM_INTERRUPT_GPIO_OUT_OF_WINDOW 0x03 +#define VL53L010_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY 0x04 + +#define VL53L010_REG_GPIO_HV_MUX_ACTIVE_HIGH 0x0084 + +#define VL53L010_REG_SYSTEM_INTERRUPT_CLEAR 0x000B + +/* Result registers */ +#define VL53L010_REG_RESULT_INTERRUPT_STATUS 0x0013 +#define VL53L010_REG_RESULT_RANGE_STATUS 0x0014 + +#define VL53L010_REG_RESULT_SIGNAL_COUNT_RATE_RET 0x001A +#define VL53L010_REG_RESULT_AMBIENT_COUNT_RATE_RET 0x001C +#define VL53L010_REG_RESULT_FINAL_RANGE 0x001E + +/* Algo register */ +#define VL53L010_REG_ALGO_CROSSTALK_COMPENSATION_RATE 0x0020 +#define VL53L010_REG_ALGO_RANGE_IGNORE_VALID_HEIGHT 0x0025 +#define VL53L010_REG_ALGO_RANGE_IGNORE_THRESHOLD 0x0026 +#define VL53L010_REG_ALGO_SNR_RATIO 0x0027 +#define VL53L010_REG_ALGO_RANGE_CHECK_ENABLES 0x0028 + +#define VL53L010_REG_ALGO_PART_TO_PART_RANGE_OFFSET 0x0029 + +#define VL53L010_REG_I2C_SLAVE_DEVICE_ADDRESS 0x008a + +/* MSRC registers */ +#define VL53L010_REG_MSRC_CONFIG_COUNT 0x0044 +#define VL53L010_REG_MSRC_CONFIG_TIMEOUT 0x0046 +#define VL53L010_REG_MSRC_CONFIG_MIN_SNR 0x0055 +#define VL53L010_REG_MSRC_CONFIG_VALID_PHASE_LOW 0x0047 +#define VL53L010_REG_MSRC_CONFIG_VALID_PHASE_HIGH 0x0048 + +/* RANGE A registers */ +#define VL53L010_REG_RNGA_CONFIG_VCSEL_PERIOD 0x0050 +#define VL53L010_REG_RNGA_TIMEOUT_MSB 0x0051 +#define VL53L010_REG_RNGA_TIMEOUT_LSB 0x0052 +#define VL53L010_REG_RNGA_CONFIG_VALID_PHASE_LOW 0x0056 +#define VL53L010_REG_RNGA_CONFIG_VALID_PHASE_HIGH 0x0057 + +/* RANGE B1 registers */ +#define VL53L010_REG_RNGB1_CONFIG_VCSEL_PERIOD 0x0060 +#define VL53L010_REG_RNGB1_TIMEOUT_MSB 0x0061 +#define VL53L010_REG_RNGB1_TIMEOUT_LSB 0x0062 +#define VL53L010_REG_RNGB1_CONFIG_VALID_PHASE_LOW 0x0066 +#define VL53L010_REG_RNGB1_CONFIG_VALID_PHASE_HIGH 0x0067 + +/* RANGE B2 registers */ +#define VL53L010_REG_RNGB2_CONFIG_VCSEL_PERIOD 0x0070 +#define VL53L010_REG_RNGB2_TIMEOUT_MSB 0x0071 +#define VL53L010_REG_RNGB2_TIMEOUT_LSB 0x0072 +#define VL53L010_REG_RNGB2_CONFIG_VALID_PHASE_LOW 0x0076 +#define VL53L010_REG_RNGB2_CONFIG_VALID_PHASE_HIGH 0x0077 + +#define VL53L010_REG_SOFT_RESET_GO2_SOFT_RESET_N 0x00bf +#define VL53L010_REG_IDENTIFICATION_MODEL_ID 0x00c0 +#define VL53L010_REG_IDENTIFICATION_REVISION_ID 0x00c2 +#define VL53L010_REG_IDENTIFICATION_MODULE_ID 0x00c3 + +#define VL53L010_REG_OSC_CALIBRATE_VAL 0x00f8 + +#define VL53L010_REG_FIRMWARE_MODE_STATUS 0x00C5 + +#define VL53L010_REG_DYNAMIC_SPAD_ACTUAL_RTN_SPADS_INT 0x0016 + +#define VL53L010_SIGMA_ESTIMATE_MAX_VALUE 65535 +/*equivalent to a range sigma of 655.35mm */ + +/* + * Speed of light in um per 1E-10 Seconds + */ + +#define VL53L010_SPEED_OF_LIGHT_IN_AIR 2997 + +/** @} VL53L010_DefineRegisters_group */ + +/** @} VL53L010_SpecDefines_group */ + +#endif + +/* _VL53L010_DEVICE_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l010_strings.h b/drivers/input/misc/vl53L0/inc/vl53l010_strings.h new file mode 100644 index 000000000000..714a0f1c479f --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l010_strings.h @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#ifndef VL53L010_STRINGS_H_ +#define VL53L010_STRINGS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + #define VL53L010_STRING_DEVICE_INFO_NAME \ + "VL53L0 cut1.0" + #define VL53L010_STRING_DEVICE_INFO_NAME_TS0 \ + "VL53L0 TS0" + #define VL53L010_STRING_DEVICE_INFO_NAME_TS1 \ + "VL53L0 TS1" + #define VL53L010_STRING_DEVICE_INFO_NAME_TS2 \ + "VL53L0 TS2" + #define VL53L010_STRING_DEVICE_INFO_NAME_ES1 \ + "VL53L0 ES1 or later" + #define VL53L010_STRING_DEVICE_INFO_TYPE \ + "VL53L0" + + /* PAL ERROR strings */ + #define VL53L010_STRING_ERROR_NONE \ + "No Error" + #define VL53L010_STRING_ERROR_CALIBRATION_WARNING \ + "Calibration Warning Error" + #define VL53L010_STRING_ERROR_MIN_CLIPPED \ + "Min clipped error" + #define VL53L010_STRING_ERROR_UNDEFINED \ + "Undefined error" + #define VL53L010_STRING_ERROR_INVALID_PARAMS \ + "Invalid parameters error" + #define VL53L010_STRING_ERROR_NOT_SUPPORTED \ + "Not supported error" + #define VL53L010_STRING_ERROR_RANGE_ERROR \ + "Range error" + #define VL53L010_STRING_ERROR_TIME_OUT \ + "Time out error" + #define VL53L010_STRING_ERROR_MODE_NOT_SUPPORTED \ + "Mode not supported error" + #define VL53L010_STRING_ERROR_NOT_IMPLEMENTED \ + "Not implemented error" + + #define VL53L010_STRING_UNKNOW_ERROR_CODE \ + "Unknow Error Code" + #define VL53L010_STRING_ERROR_BUFFER_TOO_SMALL \ + "Buffer too small" + + #define VL53L010_STRING_ERROR_GPIO_NOT_EXISTING \ + "GPIO not existing" + #define VL53L010_STRING_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED \ + "GPIO functionality not supported" + #define VL53L010_STRING_ERROR_CONTROL_INTERFACE \ + "Control Interface Error" + + + /* Device Specific */ + #define VL53L010_STRING_DEVICEERROR_NONE \ + "No Update" + #define VL53L010_STRING_DEVICEERROR_VCSELCONTINUITYTESTFAILURE \ + "VCSEL Continuity Test Failure" + #define VL53L010_STRING_DEVICEERROR_VCSELWATCHDOGTESTFAILURE \ + "VCSEL Watchdog Test Failure" + #define VL53L010_STRING_DEVICEERROR_NOVHVVALUEFOUND \ + "No VHV Value found" + #define VL53L010_STRING_DEVICEERROR_MSRCNOTARGET \ + "MSRC No Target Error" + #define VL53L010_STRING_DEVICEERROR_MSRCMINIMUMSNR \ + "MSRC Minimum SNR Error" + #define VL53L010_STRING_DEVICEERROR_MSRCWRAPAROUND \ + "MSRC Wraparound Error" + #define VL53L010_STRING_DEVICEERROR_TCC \ + "TCC Error" + #define VL53L010_STRING_DEVICEERROR_RANGEAWRAPAROUND \ + "Range A Wraparound Error" + #define VL53L010_STRING_DEVICEERROR_RANGEBWRAPAROUND \ + "Range B Wraparound Error" + #define VL53L010_STRING_DEVICEERROR_MINCLIP \ + "Min Clip Error" + #define VL53L010_STRING_DEVICEERROR_RANGECOMPLETE \ + "Range Complete" + #define VL53L010_STRING_DEVICEERROR_ALGOUNDERFLOW \ + "Range Algo Underflow Error" + #define VL53L010_STRING_DEVICEERROR_ALGOOVERFLOW \ + "Range Algo Overlow Error" + #define VL53L010_STRING_DEVICEERROR_FINALSNRLIMIT \ + "Final Minimum SNR Error" + #define VL53L010_STRING_DEVICEERROR_NOTARGETIGNORE \ + "No Target Ignore Error" + #define VL53L010_STRING_DEVICEERROR_UNKNOWN \ + "Unknown error code" + + + /* Check Enable */ + #define VL53L010_STRING_CHECKENABLE_SIGMA \ + "SIGMA" + #define VL53L010_STRING_CHECKENABLE_SIGNAL_RATE \ + "SIGNAL RATE" + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L0/inc/vl53l010_tuning.h b/drivers/input/misc/vl53L0/inc/vl53l010_tuning.h new file mode 100644 index 000000000000..fe7906044feb --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l010_tuning.h @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#ifndef _VL53L010_TUNING_H_ +#define _VL53L010_TUNING_H_ + +#include "vl53l0_def.h" +#include "vl53l0_platform.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @brief Internal function used to Program the default tuning settings + * + * @ingroup VL53L0_general_group + * @note This function access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L010_load_tuning_settings(VL53L0_DEV Dev); + + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L010_TUNING_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_api.h b/drivers/input/misc/vl53L0/inc/vl53l0_api.h new file mode 100644 index 000000000000..4f6f8020de49 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_api.h @@ -0,0 +1,1950 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef _VL53L0_API_H_ +#define _VL53L0_API_H_ + +#include "vl53l0_api_strings.h" +#include "vl53l0_def.h" +#include "vl53l0_platform.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef _MSC_VER +# ifdef VL53L0_API_EXPORTS +# define VL53L0_API __declspec(dllexport) +# else +# define VL53L0_API +# endif +#else +# define VL53L0_API +#endif + +/** @defgroup VL53L0_cut11_group VL53L0 cut1.1 Function Definition + * @brief VL53L0 cut1.1 Function Definition + * @{ + */ + +/** @defgroup VL53L0_general_group VL53L0 General Functions + * @brief General functions and definitions + * @{ + */ + +/** + * @brief Return the VL53L0 PAL Implementation Version + * + * @note This function doesn't access to the device + * + * @param pVersion Pointer to current PAL Implementation Version + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetVersion(VL53L0_Version_t *pVersion); + +/** + * @brief Return the PAL Specification Version used for the current + * implementation. + * + * @note This function doesn't access to the device + * + * @param pPalSpecVersion Pointer to current PAL Specification Version + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetPalSpecVersion( + VL53L0_Version_t *pPalSpecVersion); + +/** + * @brief Reads the Product Revision for a for given Device + * This function can be used to distinguish cut1.0 from cut1.1. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pProductRevisionMajor Pointer to Product Revision Major + * for a given Device + * @param pProductRevisionMinor Pointer to Product Revision Minor + * for a given Device + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetProductRevision(VL53L0_DEV Dev, + uint8_t *pProductRevisionMajor, uint8_t *pProductRevisionMinor); + +/** + * @brief Reads the Device information for given Device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pVL53L0_DeviceInfo Pointer to current device info for a given + * Device + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetDeviceInfo(VL53L0_DEV Dev, + VL53L0_DeviceInfo_t *pVL53L0_DeviceInfo); + +/** + * @brief Read current status of the error register for the selected device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pDeviceErrorStatus Pointer to current error code of the device + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetDeviceErrorStatus(VL53L0_DEV Dev, + VL53L0_DeviceError * pDeviceErrorStatus); + +/** + * @brief Human readable Range Status string for a given RangeStatus + * + * @note This function doesn't access to the device + * + * @param RangeStatus The RangeStatus code as stored on + * @a VL53L0_RangingMeasurementData_t + * @param pRangeStatusString The returned RangeStatus string. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetRangeStatusString(uint8_t RangeStatus, + char *pRangeStatusString); + +/** + * @brief Human readable error string for a given Error Code + * + * @note This function doesn't access to the device + * + * @param ErrorCode The error code as stored on ::VL53L0_DeviceError + * @param pDeviceErrorString The error string corresponding to the ErrorCode + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetDeviceErrorString( + VL53L0_DeviceError ErrorCode, char *pDeviceErrorString); + +/** + * @brief Human readable error string for current PAL error status + * + * @note This function doesn't access to the device + * + * @param PalErrorCode The error code as stored on @a VL53L0_Error + * @param pPalErrorString The error string corresponding to the + * PalErrorCode + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetPalErrorString(VL53L0_Error PalErrorCode, + char *pPalErrorString); + +/** + * @brief Human readable PAL State string + * + * @note This function doesn't access to the device + * + * @param PalStateCode The State code as stored on @a VL53L0_State + * @param pPalStateString The State string corresponding to the + * PalStateCode + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetPalStateString(VL53L0_State PalStateCode, + char *pPalStateString); + +/** + * @brief Reads the internal state of the PAL for a given Device + * + * @note This function doesn't access to the device + * + * @param Dev Device Handle + * @param pPalState Pointer to current state of the PAL for a + * given Device + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetPalState(VL53L0_DEV Dev, + VL53L0_State * pPalState); + +/** + * @brief Set the power mode for a given Device + * The power mode can be Standby or Idle. Different level of both Standby and + * Idle can exists. + * This function should not be used when device is in Ranging state. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param PowerMode The value of the power mode to set. + * see ::VL53L0_PowerModes + * Valid values are: + * VL53L0_POWERMODE_STANDBY_LEVEL1, + * VL53L0_POWERMODE_IDLE_LEVEL1 + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_MODE_NOT_SUPPORTED This error occurs when PowerMode + * is not in the supported list + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetPowerMode(VL53L0_DEV Dev, + VL53L0_PowerModes PowerMode); + +/** + * @brief Get the power mode for a given Device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pPowerMode Pointer to the current value of the power + * mode. see ::VL53L0_PowerModes + * Valid values are: + * VL53L0_POWERMODE_STANDBY_LEVEL1, + * VL53L0_POWERMODE_IDLE_LEVEL1 + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetPowerMode(VL53L0_DEV Dev, + VL53L0_PowerModes * pPowerMode); + +/** + * Set or over-hide part to part calibration offset + * \sa VL53L0_DataInit() VL53L0_GetOffsetCalibrationDataMicroMeter() + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param OffsetCalibrationDataMicroMeter Offset (microns) + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetOffsetCalibrationDataMicroMeter( + VL53L0_DEV Dev, int32_t OffsetCalibrationDataMicroMeter); + +/** + * @brief Get part to part calibration offset + * + * @par Function Description + * Should only be used after a successful call to @a VL53L0_DataInit to backup + * device NVM value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pOffsetCalibrationDataMicroMeter Return part to part + * calibration offset from device (microns) + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetOffsetCalibrationDataMicroMeter( + VL53L0_DEV Dev, int32_t *pOffsetCalibrationDataMicroMeter); + +/** + * Set the linearity corrective gain + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LinearityCorrectiveGain Linearity corrective + * gain in x1000 + * if value is 1000 then no modification is applied. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetLinearityCorrectiveGain(VL53L0_DEV Dev, + int16_t LinearityCorrectiveGain); + +/** + * @brief Get the linearity corrective gain + * + * @par Function Description + * Should only be used after a successful call to @a VL53L0_DataInit to backup + * device NVM value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pLinearityCorrectiveGain Pointer to the linearity + * corrective gain in x1000 + * if value is 1000 then no modification is applied. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetLinearityCorrectiveGain(VL53L0_DEV Dev, + uint16_t *pLinearityCorrectiveGain); + +/** + * Set Group parameter Hold state + * + * @par Function Description + * Set or remove device internal group parameter hold + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param GroupParamHold Group parameter Hold state to be set (on/off) + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_SetGroupParamHold(VL53L0_DEV Dev, + uint8_t GroupParamHold); + +/** + * @brief Get the maximal distance for actual setup + * @par Function Description + * Device must be initialized through @a VL53L0_SetParameters() prior calling + * this function. + * + * Any range value more than the value returned is to be considered as + * "no target detected" or + * "no target in detectable range"\n + * @warning The maximal distance depends on the setup + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param pUpperLimitMilliMeter The maximal range limit for actual setup + * (in millimeter) + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_GetUpperLimitMilliMeter(VL53L0_DEV Dev, + uint16_t *pUpperLimitMilliMeter); + + +/** + * @brief Get the Total Signal Rate + * @par Function Description + * This function will return the Total Signal Rate after a good ranging is done. + * + * @note This function access to Device + * + * @param Dev Device Handle + * @param pTotalSignalRate Total Signal Rate value in Mega count per second + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_GetTotalSignalRate(VL53L0_DEV Dev, + FixPoint1616_t *pTotalSignalRate); + +/** @} VL53L0_general_group */ + +/** @defgroup VL53L0_init_group VL53L0 Init Functions + * @brief VL53L0 Init Functions + * @{ + */ + +/** + * @brief Set new device address + * + * After completion the device will answer to the new address programmed. + * This function should be called when several devices are used in parallel + * before start programming the sensor. + * When a single device us used, there is no need to call this function. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param DeviceAddress The new Device address + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetDeviceAddress(VL53L0_DEV Dev, + uint8_t DeviceAddress); + +/** + * + * @brief One time device initialization + * + * To be called once and only once after device is brought out of reset + * (Chip enable) and booted see @a VL53L0_WaitDeviceBooted() + * + * @par Function Description + * When not used after a fresh device "power up" or reset, it may return + * @a #VL53L0_ERROR_CALIBRATION_WARNING meaning wrong calibration data + * may have been fetched from device that can result in ranging offset error\n + * If application cannot execute device reset or need to run VL53L0_DataInit + * multiple time then it must ensure proper offset calibration saving and + * restore on its own by using @a VL53L0_GetOffsetCalibrationData() on first + * power up and then @a VL53L0_SetOffsetCalibrationData() in all subsequent init + * This function will change the VL53L0_State from VL53L0_STATE_POWERDOWN to + * VL53L0_STATE_WAIT_STATICINIT. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_DataInit(VL53L0_DEV Dev); + +/** + * @brief Set the tuning settings pointer + * + * This function is used to specify the Tuning settings buffer to be used + * for a given device. The buffer contains all the necessary data to permit + * the API to write tuning settings. + * This function permit to force the usage of either external or internal + * tuning settings. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pTuningSettingBuffer Pointer to tuning settings buffer. + * @param UseInternalTuningSettings Use internal tuning settings value. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetTuningSettingBuffer(VL53L0_DEV Dev, + uint8_t *pTuningSettingBuffer, uint8_t UseInternalTuningSettings); + +/** + * @brief Get the tuning settings pointer and the internal external switch + * value. + * + * This function is used to get the Tuning settings buffer pointer and the + * value. + * of the switch to select either external or internal tuning settings. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param ppTuningSettingBuffer Pointer to tuning settings buffer. + * @param pUseInternalTuningSettings Pointer to store Use internal tuning + * settings value. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetTuningSettingBuffer(VL53L0_DEV Dev, + uint8_t **ppTuningSettingBuffer, uint8_t *pUseInternalTuningSettings); + +/** + * @brief Do basic device init (and eventually patch loading) + * This function will change the VL53L0_State from + * VL53L0_STATE_WAIT_STATICINIT to VL53L0_STATE_IDLE. + * In this stage all default setting will be applied. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_StaticInit(VL53L0_DEV Dev); + +/** + * @brief Wait for device booted after chip enable (hardware standby) + * This function can be run only when VL53L0_State is VL53L0_STATE_POWERDOWN. + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + * + */ +VL53L0_API VL53L0_Error VL53L0_WaitDeviceBooted(VL53L0_DEV Dev); + +/** + * @brief Do an hard reset or soft reset (depending on implementation) of the + * device \nAfter call of this function, device must be in same state as right + * after a power-up sequence.This function will change the VL53L0_State to + * VL53L0_STATE_POWERDOWN. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_ResetDevice(VL53L0_DEV Dev); + +/** @} VL53L0_init_group */ + +/** @defgroup VL53L0_parameters_group VL53L0 Parameters Functions + * @brief Functions used to prepare and setup the device + * @{ + */ + +/** + * @brief Prepare device for operation + * @par Function Description + * Update device with provided parameters + * @li Then start ranging operation. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pDeviceParameters Pointer to store current device parameters. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetDeviceParameters(VL53L0_DEV Dev, + const VL53L0_DeviceParameters_t *pDeviceParameters); + +/** + * @brief Retrieve current device parameters + * @par Function Description + * Get actual parameters of the device + * @li Then start ranging operation. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pDeviceParameters Pointer to store current device parameters. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetDeviceParameters(VL53L0_DEV Dev, + VL53L0_DeviceParameters_t *pDeviceParameters); + +/** + * @brief Set a new device mode + * @par Function Description + * Set device to a new mode (ranging, histogram ...) + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param DeviceMode New device mode to apply + * Valid values are: + * VL53L0_DEVICEMODE_SINGLE_RANGING + * VL53L0_DEVICEMODE_CONTINUOUS_RANGING + * VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING + * VL53L0_DEVICEMODE_SINGLE_HISTOGRAM + * VL53L0_HISTOGRAMMODE_REFERENCE_ONLY + * VL53L0_HISTOGRAMMODE_RETURN_ONLY + * VL53L0_HISTOGRAMMODE_BOTH + * + * + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_MODE_NOT_SUPPORTED This error occurs when DeviceMode is + * not in the supported list + */ +VL53L0_API VL53L0_Error VL53L0_SetDeviceMode(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode); + +/** + * @brief Get current new device mode + * @par Function Description + * Get actual mode of the device(ranging, histogram ...) + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pDeviceMode Pointer to current apply mode value + * Valid values are: + * VL53L0_DEVICEMODE_SINGLE_RANGING + * VL53L0_DEVICEMODE_CONTINUOUS_RANGING + * VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING + * VL53L0_DEVICEMODE_SINGLE_HISTOGRAM + * VL53L0_HISTOGRAMMODE_REFERENCE_ONLY + * VL53L0_HISTOGRAMMODE_RETURN_ONLY + * VL53L0_HISTOGRAMMODE_BOTH + * + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_MODE_NOT_SUPPORTED This error occurs when + * DeviceMode is not in the supported list + */ +VL53L0_API VL53L0_Error VL53L0_GetDeviceMode(VL53L0_DEV Dev, + VL53L0_DeviceModes * pDeviceMode); + +/** + * @brief Sets the resolution of range measurements. + * @par Function Description + * Set resolution of range measurements to either 0.25mm if + * fraction enabled or 1mm if not enabled. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param Enable Enable high resolution + * + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetRangeFractionEnable(VL53L0_DEV Dev, + uint8_t Enable); + +/** + * @brief Gets the fraction enable parameter indicating the resolution of + * range measurements. + * + * @par Function Description + * Gets the fraction enable state, which translates to the resolution of + * range measurements as follows :Enabled:=0.25mm resolution, + * Not Enabled:=1mm resolution. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param pEnable Output Parameter reporting the fraction enable state. + * + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetFractionEnable(VL53L0_DEV Dev, + uint8_t *pEnable); + +/** + * @brief Set a new Histogram mode + * @par Function Description + * Set device to a new Histogram mode + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param HistogramMode New device mode to apply + * Valid values are: + * VL53L0_HISTOGRAMMODE_DISABLED + * VL53L0_DEVICEMODE_SINGLE_HISTOGRAM + * VL53L0_HISTOGRAMMODE_REFERENCE_ONLY + * VL53L0_HISTOGRAMMODE_RETURN_ONLY + * VL53L0_HISTOGRAMMODE_BOTH + * + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_MODE_NOT_SUPPORTED This error occurs when + * HistogramMode is not in the supported list + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetHistogramMode(VL53L0_DEV Dev, + VL53L0_HistogramModes HistogramMode); + +/** + * @brief Get current new device mode + * @par Function Description + * Get current Histogram mode of a Device + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pHistogramMode Pointer to current Histogram Mode value + * Valid values are: + * VL53L0_HISTOGRAMMODE_DISABLED + * VL53L0_DEVICEMODE_SINGLE_HISTOGRAM + * VL53L0_HISTOGRAMMODE_REFERENCE_ONLY + * VL53L0_HISTOGRAMMODE_RETURN_ONLY + * VL53L0_HISTOGRAMMODE_BOTH + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetHistogramMode(VL53L0_DEV Dev, + VL53L0_HistogramModes * pHistogramMode); + +/** + * @brief Set Ranging Timing Budget in microseconds + * + * @par Function Description + * Defines the maximum time allowed by the user to the device to run a + * full ranging sequence for the current mode (ranging, histogram, ASL ...) + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param MeasurementTimingBudgetMicroSeconds Max measurement time in + * microseconds. + * Valid values are: + * >= 17000 microsecs when wraparound enabled + * >= 12000 microsecs when wraparound disabled + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned if + MeasurementTimingBudgetMicroSeconds out of range + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetMeasurementTimingBudgetMicroSeconds( + VL53L0_DEV Dev, uint32_t MeasurementTimingBudgetMicroSeconds); + +/** + * @brief Get Ranging Timing Budget in microseconds + * + * @par Function Description + * Returns the programmed the maximum time allowed by the user to the + * device to run a full ranging sequence for the current mode + * (ranging, histogram, ASL ...) + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pMeasurementTimingBudgetMicroSeconds Max measurement time in + * microseconds. + * Valid values are: + * >= 17000 microsecs when wraparound enabled + * >= 12000 microsecs when wraparound disabled + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetMeasurementTimingBudgetMicroSeconds( + VL53L0_DEV Dev, uint32_t *pMeasurementTimingBudgetMicroSeconds); + +/** + * @brief Gets the VCSEL pulse period. + * + * @par Function Description + * This function retrieves the VCSEL pulse period for the given period type. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param VcselPeriodType VCSEL period identifier (pre-range|final). + * @param pVCSELPulsePeriod Pointer to VCSEL period value. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS Error VcselPeriodType parameter not + * supported. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetVcselPulsePeriod(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t *pVCSELPulsePeriod); + +/** + * @brief Sets the VCSEL pulse period. + * + * @par Function Description + * This function retrieves the VCSEL pulse period for the given period type. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param VcselPeriodType VCSEL period identifier (pre-range|final). + * @param VCSELPulsePeriod VCSEL period value + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS Error VcselPeriodType parameter not + * supported. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetVcselPulsePeriod(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t VCSELPulsePeriod); + +/** + * @brief Sets the (on/off) state of a requested sequence step. + * + * @par Function Description + * This function enables/disables a requested sequence step. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param SequenceStepId Sequence step identifier. + * @param SequenceStepEnabled Demanded state {0=Off,1=On} + * is enabled. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS Error SequenceStepId parameter not + * supported. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetSequenceStepEnable(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, uint8_t SequenceStepEnabled); + +/** + * @brief Gets the (on/off) state of a requested sequence step. + * + * @par Function Description + * This function retrieves the state of a requested sequence step, i.e. on/off. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param SequenceStepId Sequence step identifier. + * @param pSequenceStepEnabled Out parameter reporting if the sequence step + * is enabled {0=Off,1=On}. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS Error SequenceStepId parameter not + * supported. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetSequenceStepEnable(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, uint8_t *pSequenceStepEnabled); + +/** + * @brief Gets the (on/off) state of all sequence steps. + * + * @par Function Description + * This function retrieves the state of all sequence step in the scheduler. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param pSchedulerSequenceSteps Pointer to struct containing result. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetSequenceStepEnables(VL53L0_DEV Dev, + VL53L0_SchedulerSequenceSteps_t *pSchedulerSequenceSteps); + +/** + * @brief Sets the timeout of a requested sequence step. + * + * @par Function Description + * This function sets the timeout of a requested sequence step. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param SequenceStepId Sequence step identifier. + * @param TimeOutMilliSecs Demanded timeout + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS Error SequenceStepId parameter not + * supported. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetSequenceStepTimeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, FixPoint1616_t TimeOutMilliSecs); + +/** + * @brief Gets the timeout of a requested sequence step. + * + * @par Function Description + * This function retrieves the timeout of a requested sequence step. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param SequenceStepId Sequence step identifier. + * @param pTimeOutMilliSecs Timeout value. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS Error SequenceStepId parameter not + * supported. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetSequenceStepTimeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + FixPoint1616_t *pTimeOutMilliSecs); + +/** + * @brief Gets number of sequence steps managed by the API. + * + * @par Function Description + * This function retrieves the number of sequence steps currently managed + * by the API + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param pNumberOfSequenceSteps Out parameter reporting the number of + * sequence steps. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetNumberOfSequenceSteps(VL53L0_DEV Dev, + uint8_t *pNumberOfSequenceSteps); + +/** + * @brief Gets the name of a given sequence step. + * + * @par Function Description + * This function retrieves the name of sequence steps corresponding to + * SequenceStepId. + * + * @note This function doesn't Accesses the device + * + * @param SequenceStepId Sequence step identifier. + * @param pSequenceStepsString Pointer to Info string + * + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetSequenceStepsInfo( + VL53L0_SequenceStepId SequenceStepId, char *pSequenceStepsString); + +/** + * Program continuous mode Inter-Measurement period in milliseconds + * + * @par Function Description + * When trying to set too short time return INVALID_PARAMS minimal value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param InterMeasurementPeriodMilliSeconds Inter-Measurement Period in ms. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetInterMeasurementPeriodMilliSeconds( + VL53L0_DEV Dev, uint32_t InterMeasurementPeriodMilliSeconds); + +/** + * Get continuous mode Inter-Measurement period in milliseconds + * + * @par Function Description + * When trying to set too short time return INVALID_PARAMS minimal value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pInterMeasurementPeriodMilliSeconds Pointer to programmed + * Inter-Measurement Period in milliseconds. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetInterMeasurementPeriodMilliSeconds( + VL53L0_DEV Dev, uint32_t *pInterMeasurementPeriodMilliSeconds); + +/** + * @brief Enable/Disable Cross talk compensation feature + * + * @note This function is not Implemented. + * Enable/Disable Cross Talk by set to zero the Cross Talk value + * by using @a VL53L0_SetXTalkCompensationRateMegaCps(). + * + * @param Dev Device Handle + * @param XTalkCompensationEnable Cross talk compensation + * to be set 0=disabled else = enabled + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_SetXTalkCompensationEnable(VL53L0_DEV Dev, + uint8_t XTalkCompensationEnable); + +/** + * @brief Get Cross talk compensation rate + * + * @note This function is not Implemented. + * Enable/Disable Cross Talk by set to zero the Cross Talk value by + * using @a VL53L0_SetXTalkCompensationRateMegaCps(). + * + * @param Dev Device Handle + * @param pXTalkCompensationEnable Pointer to the Cross talk compensation + * state 0=disabled or 1 = enabled + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_GetXTalkCompensationEnable(VL53L0_DEV Dev, + uint8_t *pXTalkCompensationEnable); + +/** + * @brief Set Cross talk compensation rate + * + * @par Function Description + * Set Cross talk compensation rate. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param XTalkCompensationRateMegaCps Compensation rate in + * Mega counts per second (16.16 fix point) see datasheet for details + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetXTalkCompensationRateMegaCps(VL53L0_DEV Dev, + FixPoint1616_t XTalkCompensationRateMegaCps); + +/** + * @brief Get Cross talk compensation rate + * + * @par Function Description + * Get Cross talk compensation rate. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pXTalkCompensationRateMegaCps Pointer to Compensation rate + in Mega counts per second (16.16 fix point) see datasheet for details + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetXTalkCompensationRateMegaCps(VL53L0_DEV Dev, + FixPoint1616_t *pXTalkCompensationRateMegaCps); + +/** + * @brief Set Reference Calibration Parameters + * + * @par Function Description + * Set Reference Calibration Parameters. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param VhvSettings Parameter for VHV + * @param PhaseCal Parameter for PhaseCal + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetRefCalibration(VL53L0_DEV Dev, + uint8_t VhvSettings, uint8_t PhaseCal); + +/** + * @brief Get Reference Calibration Parameters + * + * @par Function Description + * Get Reference Calibration Parameters. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pVhvSettings Pointer to VHV parameter + * @param pPhaseCal Pointer to PhaseCal Parameter + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetRefCalibration(VL53L0_DEV Dev, + uint8_t *pVhvSettings, uint8_t *pPhaseCal); + +/** + * @brief Get the number of the check limit managed by a given Device + * + * @par Function Description + * This function give the number of the check limit managed by the Device + * + * @note This function doesn't Access to the device + * + * @param pNumberOfLimitCheck Pointer to the number of check limit. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetNumberOfLimitCheck( + uint16_t *pNumberOfLimitCheck); + +/** + * @brief Return a description string for a given limit check number + * + * @par Function Description + * This function returns a description string for a given limit check number. + * The limit check is identified with the LimitCheckId. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + (0<= LimitCheckId < VL53L0_GetNumberOfLimitCheck() ). + * @param pLimitCheckString Pointer to the + description string of the given check limit. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is + returned when LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetLimitCheckInfo(VL53L0_DEV Dev, + uint16_t LimitCheckId, char *pLimitCheckString); + +/** + * @brief Return a the Status of the specified check limit + * + * @par Function Description + * This function returns the Status of the specified check limit. + * The value indicate if the check is fail or not. + * The limit check is identified with the LimitCheckId. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + (0<= LimitCheckId < VL53L0_GetNumberOfLimitCheck() ). + * @param pLimitCheckStatus Pointer to the + Limit Check Status of the given check limit. + * LimitCheckStatus : + * 0 the check is not fail + * 1 the check if fail or not enabled + * + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is + returned when LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetLimitCheckStatus(VL53L0_DEV Dev, + uint16_t LimitCheckId, uint8_t *pLimitCheckStatus); + +/** + * @brief Enable/Disable a specific limit check + * + * @par Function Description + * This function Enable/Disable a specific limit check. + * The limit check is identified with the LimitCheckId. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L0_GetNumberOfLimitCheck() ). + * @param LimitCheckEnable if 1 the check limit + * corresponding to LimitCheckId is Enabled + * if 0 the check limit + * corresponding to LimitCheckId is disabled + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned + * when LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetLimitCheckEnable(VL53L0_DEV Dev, + uint16_t LimitCheckId, uint8_t LimitCheckEnable); + +/** + * @brief Get specific limit check enable state + * + * @par Function Description + * This function get the enable state of a specific limit check. + * The limit check is identified with the LimitCheckId. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L0_GetNumberOfLimitCheck() ). + * @param pLimitCheckEnable Pointer to the check limit enable + * value. + * if 1 the check limit + * corresponding to LimitCheckId is Enabled + * if 0 the check limit + * corresponding to LimitCheckId is disabled + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned + * when LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetLimitCheckEnable(VL53L0_DEV Dev, + uint16_t LimitCheckId, uint8_t *pLimitCheckEnable); + +/** + * @brief Set a specific limit check value + * + * @par Function Description + * This function set a specific limit check value. + * The limit check is identified with the LimitCheckId. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L0_GetNumberOfLimitCheck() ). + * @param LimitCheckValue Limit check Value for a given + * LimitCheckId + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned when either + * LimitCheckId or LimitCheckValue value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetLimitCheckValue(VL53L0_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t LimitCheckValue); + +/** + * @brief Get a specific limit check value + * + * @par Function Description + * This function get a specific limit check value from device then it updates + * internal values and check enables. + * The limit check is identified with the LimitCheckId. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L0_GetNumberOfLimitCheck() ). + * @param pLimitCheckValue Pointer to Limit + * check Value for a given LimitCheckId. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned + * when LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetLimitCheckValue(VL53L0_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t *pLimitCheckValue); + +/** + * @brief Get the current value of the signal used for the limit check + * + * @par Function Description + * This function get a the current value of the signal used for the limit check. + * To obtain the latest value you should run a ranging before. + * The value reported is linked to the limit check identified with the + * LimitCheckId. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L0_GetNumberOfLimitCheck() ). + * @param pLimitCheckCurrent Pointer to current Value for a + * given LimitCheckId. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned when + * LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetLimitCheckCurrent(VL53L0_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t *pLimitCheckCurrent); + +/** + * @brief Enable (or disable) Wrap around Check + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param WrapAroundCheckEnable Wrap around Check to be set + * 0=disabled, other = enabled + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetWrapAroundCheckEnable(VL53L0_DEV Dev, + uint8_t WrapAroundCheckEnable); + +/** + * @brief Get setup of Wrap around Check + * + * @par Function Description + * This function get the wrapAround check enable parameters + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pWrapAroundCheckEnable Pointer to the Wrap around Check state + * 0=disabled or 1 = enabled + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetWrapAroundCheckEnable(VL53L0_DEV Dev, + uint8_t *pWrapAroundCheckEnable); + +/** + * @brief Set Dmax Calibration Parameters for a given device + * When one of the parameter is zero, this function will get parameter + * from NVM. + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param RangeMilliMeter Calibration Distance + * @param SignalRateRtnMegaCps Signal rate return read at CalDistance + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetDmaxCalParameters(VL53L0_DEV Dev, + uint16_t RangeMilliMeter, FixPoint1616_t SignalRateRtnMegaCps); + +/** + * @brief Get Dmax Calibration Parameters for a given device + * + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pRangeMilliMeter Pointer to Calibration Distance + * @param pSignalRateRtnMegaCps Pointer to Signal rate return + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetDmaxCalParameters(VL53L0_DEV Dev, + uint16_t *pRangeMilliMeter, FixPoint1616_t *pSignalRateRtnMegaCps); + +/** @} VL53L0_parameters_group */ + +/** @defgroup VL53L0_measurement_group VL53L0 Measurement Functions + * @brief Functions used for the measurements + * @{ + */ + +/** + * @brief Single shot measurement. + * + * @par Function Description + * Perform simple measurement sequence (Start measure, Wait measure to end, + * and returns when measurement is done). + * Once function returns, user can get valid data by calling + * VL53L0_GetRangingMeasurement or VL53L0_GetHistogramMeasurement + * depending on defined measurement mode + * User should Clear the interrupt in case this are enabled by using the + * function VL53L0_ClearInterruptMask(). + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_PerformSingleMeasurement(VL53L0_DEV Dev); + +/** + * @brief Perform Reference Calibration + * + * @details Perform a reference calibration of the Device. + * This function should be run from time to time before doing + * a ranging measurement. + * This function will launch a special ranging measurement, so + * if interrupt are enable an interrupt will be done. + * This function will clear the interrupt generated automatically. + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pVhvSettings Pointer to vhv settings parameter. + * @param pPhaseCal Pointer to PhaseCal parameter. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_PerformRefCalibration(VL53L0_DEV Dev, + uint8_t *pVhvSettings, uint8_t *pPhaseCal); + +/** + * @brief Perform XTalk Measurement + * + * @details Measures the current cross talk from glass in front + * of the sensor. + * This functions performs a histogram measurement and uses the results + * to measure the crosstalk. For the function to be successful, there + * must be no target in front of the sensor. + * + * @warning This function is a blocking function + * + * @warning This function is not supported when the final range + * vcsel clock period is set below 10 PCLKS. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param TimeoutMs Histogram measurement duration. + * @param pXtalkPerSpad Output parameter containing the crosstalk + * measurement result, in MCPS/Spad. Format fixpoint 16:16. + * @param pAmbientTooHigh Output parameter which indicate that + * pXtalkPerSpad is not good if the Ambient is too high. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS vcsel clock period not supported + * for this operation. Must not be less than 10PCLKS. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_PerformXTalkMeasurement(VL53L0_DEV Dev, + uint32_t TimeoutMs, FixPoint1616_t *pXtalkPerSpad, + uint8_t *pAmbientTooHigh); + +/** + * @brief Perform XTalk Calibration + * + * @details Perform a XTalk calibration of the Device. + * This function will launch a ranging measurement, if interrupts + * are enabled an interrupt will be done. + * This function will clear the interrupt generated automatically. + * This function will program a new value for the XTalk compensation + * and it will enable the cross talk before exit. + * This function will disable the VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD. + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @note This function change the device mode to + * VL53L0_DEVICEMODE_SINGLE_RANGING + * + * @param Dev Device Handle + * @param XTalkCalDistance XTalkCalDistance value used for the XTalk + * computation. + * @param pXTalkCompensationRateMegaCps Pointer to new + * XTalkCompensation value. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_PerformXTalkCalibration(VL53L0_DEV Dev, + FixPoint1616_t XTalkCalDistance, + FixPoint1616_t *pXTalkCompensationRateMegaCps); + +/** + * @brief Perform Offset Calibration + * + * @details Perform a Offset calibration of the Device. + * This function will launch a ranging measurement, if interrupts are + * enabled an interrupt will be done. + * This function will clear the interrupt generated automatically. + * This function will program a new value for the Offset calibration value + * This function will disable the VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD. + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @note This function does not change the device mode. + * + * @param Dev Device Handle + * @param CalDistanceMilliMeter Calibration distance value used for the + * offset compensation. + * @param pOffsetMicroMeter Pointer to new Offset value computed by the + * function. + * + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_PerformOffsetCalibration(VL53L0_DEV Dev, + FixPoint1616_t CalDistanceMilliMeter, int32_t *pOffsetMicroMeter); + +/** + * @brief Start device measurement + * + * @details Started measurement will depend on device parameters set through + * @a VL53L0_SetParameters() + * This is a non-blocking function. + * This function will change the VL53L0_State from VL53L0_STATE_IDLE to + * VL53L0_STATE_RUNNING. + * + * @note This function Access to the device + * + + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_MODE_NOT_SUPPORTED This error occurs when + * DeviceMode programmed with @a VL53L0_SetDeviceMode is not in the supported + * list: + * Supported mode are: + * VL53L0_DEVICEMODE_SINGLE_RANGING, + * VL53L0_DEVICEMODE_CONTINUOUS_RANGING, + * VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING + * @return VL53L0_ERROR_TIME_OUT Time out on start measurement + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_StartMeasurement(VL53L0_DEV Dev); + +/** + * @brief Stop device measurement + * + * @details Will set the device in standby mode at end of current measurement\n + * Not necessary in single mode as device shall return automatically + * in standby mode at end of measurement. + * This function will change the VL53L0_State from VL53L0_STATE_RUNNING + * to VL53L0_STATE_IDLE. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_StopMeasurement(VL53L0_DEV Dev); + +/** + * @brief Return Measurement Data Ready + * + * @par Function Description + * This function indicate that a measurement data is ready. + * This function check if interrupt mode is used then check is done accordingly. + * If perform function clear the interrupt, this function will not work, + * like in case of @a VL53L0_PerformSingleRangingMeasurement(). + * The previous function is blocking function, VL53L0_GetMeasurementDataReady + * is used for non-blocking capture. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pMeasurementDataReady Pointer to Measurement Data Ready. + * 0=data not ready, 1 = data ready + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetMeasurementDataReady(VL53L0_DEV Dev, + uint8_t *pMeasurementDataReady); + +/** + * @brief Wait for device ready for a new measurement command. + * Blocking function. + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param MaxLoop Max Number of polling loop (timeout). + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_WaitDeviceReadyForNewMeasurement(VL53L0_DEV Dev, + uint32_t MaxLoop); + +/** + * @brief Retrieve the Reference Signal after a measurements + * + * @par Function Description + * Get Reference Signal from last successful Ranging measurement + * This function return a valid value after that you call the + * @a VL53L0_GetRangingMeasurementData(). + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pMeasurementRefSignal Pointer to the Ref Signal to fill up. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetMeasurementRefSignal(VL53L0_DEV Dev, + FixPoint1616_t *pMeasurementRefSignal); + +/** + * @brief Retrieve the measurements from device for a given setup + * + * @par Function Description + * Get data from last successful Ranging measurement + * @warning USER should take care about @a VL53L0_GetNumberOfROIZones() + * before get data. + * PAL will fill a NumberOfROIZones times the corresponding data + * structure used in the measurement function. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pRangingMeasurementData Pointer to the data structure to fill up. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetRangingMeasurementData(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData); + +/** + * @brief Retrieve the measurements from device for a given setup + * + * @par Function Description + * Get data from last successful Histogram measurement + * @warning USER should take care about @a VL53L0_GetNumberOfROIZones() + * before get data. + * PAL will fill a NumberOfROIZones times the corresponding data structure + * used in the measurement function. + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param pHistogramMeasurementData Pointer to the histogram data structure. + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_GetHistogramMeasurementData(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData); + +/** + * @brief Performs a single ranging measurement and retrieve the ranging + * measurement data + * + * @par Function Description + * This function will change the device mode to VL53L0_DEVICEMODE_SINGLE_RANGING + * with @a VL53L0_SetDeviceMode(), + * It performs measurement with @a VL53L0_PerformSingleMeasurement() + * It get data from last successful Ranging measurement with + * @a VL53L0_GetRangingMeasurementData. + * Finally it clear the interrupt with @a VL53L0_ClearInterruptMask(). + * + * @note This function Access to the device + * + * @note This function change the device mode to + * VL53L0_DEVICEMODE_SINGLE_RANGING + * + * @param Dev Device Handle + * @param pRangingMeasurementData Pointer to the data structure to fill up. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_PerformSingleRangingMeasurement(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData); + +/** + * @brief Performs a single histogram measurement and retrieve the histogram + * measurement data + * Is equivalent to VL53L0_PerformSingleMeasurement + + * VL53L0_GetHistogramMeasurementData + * + * @par Function Description + * Get data from last successful Ranging measurement. + * This function will clear the interrupt in case of these are enabled. + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param pHistogramMeasurementData Pointer to the data structure to fill up. + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_PerformSingleHistogramMeasurement(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData); + +/** + * @brief Set the number of ROI Zones to be used for a specific Device + * + * @par Function Description + * Set the number of ROI Zones to be used for a specific Device. + * The programmed value should be less than the max number of ROI Zones given + * with @a VL53L0_GetMaxNumberOfROIZones(). + * This version of API manage only one zone. + * + * @param Dev Device Handle + * @param NumberOfROIZones Number of ROI Zones to be used for a + * specific Device. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned if + * NumberOfROIZones != 1 + */ +VL53L0_API VL53L0_Error VL53L0_SetNumberOfROIZones(VL53L0_DEV Dev, + uint8_t NumberOfROIZones); + +/** + * @brief Get the number of ROI Zones managed by the Device + * + * @par Function Description + * Get number of ROI Zones managed by the Device + * USER should take care about @a VL53L0_GetNumberOfROIZones() + * before get data after a perform measurement. + * PAL will fill a NumberOfROIZones times the corresponding data + * structure used in the measurement function. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pNumberOfROIZones Pointer to the Number of ROI Zones value. + * @return VL53L0_ERROR_NONE Success + */ +VL53L0_API VL53L0_Error VL53L0_GetNumberOfROIZones(VL53L0_DEV Dev, + uint8_t *pNumberOfROIZones); + +/** + * @brief Get the Maximum number of ROI Zones managed by the Device + * + * @par Function Description + * Get Maximum number of ROI Zones managed by the Device. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pMaxNumberOfROIZones Pointer to the Maximum Number + * of ROI Zones value. + * @return VL53L0_ERROR_NONE Success + */ +VL53L0_API VL53L0_Error VL53L0_GetMaxNumberOfROIZones(VL53L0_DEV Dev, + uint8_t *pMaxNumberOfROIZones); + +/** @} VL53L0_measurement_group */ + +/** @defgroup VL53L0_interrupt_group VL53L0 Interrupt Functions + * @brief Functions used for interrupt managements + * @{ + */ + +/** + * @brief Set the configuration of GPIO pin for a given device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param Pin ID of the GPIO Pin + * @param Functionality Select Pin functionality. + * Refer to ::VL53L0_GpioFunctionality + * @param DeviceMode Device Mode associated to the Gpio. + * @param Polarity Set interrupt polarity. Active high + * or active low see ::VL53L0_InterruptPolarity + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_GPIO_NOT_EXISTING Only Pin=0 is accepted. + * @return VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED This error occurs + * when Functionality programmed is not in the supported list: + * Supported value are: + * VL53L0_GPIOFUNCTIONALITY_OFF, + * VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW, + * VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH, + VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT, + * VL53L0_GPIOFUNCTIONALITY_NEW_MEASURE_READY + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetGpioConfig(VL53L0_DEV Dev, uint8_t Pin, + VL53L0_DeviceModes DeviceMode, VL53L0_GpioFunctionality Functionality, + VL53L0_InterruptPolarity Polarity); + +/** + * @brief Get current configuration for GPIO pin for a given device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param Pin ID of the GPIO Pin + * @param pDeviceMode Pointer to Device Mode associated to the Gpio. + * @param pFunctionality Pointer to Pin functionality. + * Refer to ::VL53L0_GpioFunctionality + * @param pPolarity Pointer to interrupt polarity. + * Active high or active low see ::VL53L0_InterruptPolarity + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_GPIO_NOT_EXISTING Only Pin=0 is accepted. + * @return VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED This error occurs + * when Functionality programmed is not in the supported list: + * Supported value are: + * VL53L0_GPIOFUNCTIONALITY_OFF, + * VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW, + * VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH, + * VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT, + * VL53L0_GPIOFUNCTIONALITY_NEW_MEASURE_READY + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetGpioConfig(VL53L0_DEV Dev, uint8_t Pin, + VL53L0_DeviceModes * pDeviceMode, + VL53L0_GpioFunctionality * pFunctionality, + VL53L0_InterruptPolarity * pPolarity); + +/** + * @brief Set low and high Interrupt thresholds for a given mode + * (ranging, ALS, ...) for a given device + * + * @par Function Description + * Set low and high Interrupt thresholds for a given mode (ranging, ALS, ...) + * for a given device + * + * @note This function Access to the device + * + * @note DeviceMode is ignored for the current device + * + * @param Dev Device Handle + * @param DeviceMode Device Mode for which change thresholds + * @param ThresholdLow Low threshold (mm, lux ..., depending on the mode) + * @param ThresholdHigh High threshold (mm, lux ..., depending on the mode) + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetInterruptThresholds(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode, FixPoint1616_t ThresholdLow, + FixPoint1616_t ThresholdHigh); + +/** + * @brief Get high and low Interrupt thresholds for a given mode + * (ranging, ALS, ...) for a given device + * + * @par Function Description + * Get high and low Interrupt thresholds for a given mode (ranging, ALS, ...) + * for a given device + * + * @note This function Access to the device + * + * @note DeviceMode is ignored for the current device + * + * @param Dev Device Handle + * @param DeviceMode Device Mode from which read thresholds + * @param pThresholdLow Low threshold (mm, lux ..., depending on the mode) + * @param pThresholdHigh High threshold (mm, lux ..., depending on the mode) + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetInterruptThresholds(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode, FixPoint1616_t *pThresholdLow, + FixPoint1616_t *pThresholdHigh); + +/** + * @brief Return device stop completion status + * + * @par Function Description + * Returns stop completiob status. + * User shall call this function after a stop command + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pStopStatus Pointer to status variable to update + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetStopCompletedStatus(VL53L0_DEV Dev, + uint32_t *pStopStatus); + + +/** + * @brief Clear given system interrupt condition + * + * @par Function Description + * Clear given interrupt(s). + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param InterruptMask Mask of interrupts to clear + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INTERRUPT_NOT_CLEARED Cannot clear interrupts + * + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_ClearInterruptMask(VL53L0_DEV Dev, + uint32_t InterruptMask); + +/** + * @brief Return device interrupt status + * + * @par Function Description + * Returns currently raised interrupts by the device. + * User shall be able to activate/deactivate interrupts through + * @a VL53L0_SetGpioConfig() + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pInterruptMaskStatus Pointer to status variable to update + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetInterruptMaskStatus(VL53L0_DEV Dev, + uint32_t *pInterruptMaskStatus); + +/** + * @brief Configure ranging interrupt reported to system + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param InterruptMask Mask of interrupt to Enable/disable + * (0:interrupt disabled or 1: interrupt enabled) + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_EnableInterruptMask(VL53L0_DEV Dev, + uint32_t InterruptMask); + +/** @} VL53L0_interrupt_group */ + +/** @defgroup VL53L0_SPADfunctions_group VL53L0 SPAD Functions + * @brief Functions used for SPAD managements + * @{ + */ + +/** + * @brief Set the SPAD Ambient Damper Threshold value + * + * @par Function Description + * This function set the SPAD Ambient Damper Threshold value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param SpadAmbientDamperThreshold SPAD Ambient Damper Threshold value + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetSpadAmbientDamperThreshold(VL53L0_DEV Dev, + uint16_t SpadAmbientDamperThreshold); + +/** + * @brief Get the current SPAD Ambient Damper Threshold value + * + * @par Function Description + * This function get the SPAD Ambient Damper Threshold value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pSpadAmbientDamperThreshold Pointer to programmed + * SPAD Ambient Damper Threshold value + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetSpadAmbientDamperThreshold(VL53L0_DEV Dev, + uint16_t *pSpadAmbientDamperThreshold); + +/** + * @brief Set the SPAD Ambient Damper Factor value + * + * @par Function Description + * This function set the SPAD Ambient Damper Factor value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param SpadAmbientDamperFactor SPAD Ambient Damper Factor value + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetSpadAmbientDamperFactor(VL53L0_DEV Dev, + uint16_t SpadAmbientDamperFactor); + +/** + * @brief Get the current SPAD Ambient Damper Factor value + * + * @par Function Description + * This function get the SPAD Ambient Damper Factor value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pSpadAmbientDamperFactor Pointer to programmed SPAD Ambient + * Damper Factor value + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetSpadAmbientDamperFactor(VL53L0_DEV Dev, + uint16_t *pSpadAmbientDamperFactor); + +/** + * @brief Performs Reference Spad Management + * + * @par Function Description + * The reference SPAD initialization procedure determines the minimum amount + * of reference spads to be enables to achieve a target reference signal rate + * and should be performed once during initialization. + * + * @note This function Access to the device + * + * @note This function change the device mode to + * VL53L0_DEVICEMODE_SINGLE_RANGING + * + * @param Dev Device Handle + * @param refSpadCount Reports ref Spad Count + * @param isApertureSpads Reports if spads are of type + * aperture or non-aperture. + * 1:=aperture, 0:=Non-Aperture + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_REF_SPAD_INIT Error in the Ref Spad procedure. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_PerformRefSpadManagement(VL53L0_DEV Dev, + uint32_t *refSpadCount, uint8_t *isApertureSpads); + +/** + * @brief Applies Reference SPAD configuration + * + * @par Function Description + * This function applies a given number of reference spads, identified as + * either Aperture or Non-Aperture. + * The requested spad count and type are stored within the device specific + * parameters data for access by the host. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param refSpadCount Number of ref spads. + * @param isApertureSpads Defines if spads are of type + * aperture or non-aperture. + * 1:=aperture, 0:=Non-Aperture + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_REF_SPAD_INIT Error in the in the reference + * spad configuration. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetReferenceSpads(VL53L0_DEV Dev, + uint32_t refSpadCount, uint8_t isApertureSpads); + +/** + * @brief Retrieves SPAD configuration + * + * @par Function Description + * This function retrieves the current number of applied reference spads + * and also their type : Aperture or Non-Aperture. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param refSpadCount Number ref Spad Count + * @param isApertureSpads Reports if spads are of type + * aperture or non-aperture. + * 1:=aperture, 0:=Non-Aperture + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_REF_SPAD_INIT Error in the in the reference + * spad configuration. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetReferenceSpads(VL53L0_DEV Dev, + uint32_t *refSpadCount, uint8_t *isApertureSpads); + +/** @} VL53L0_SPADfunctions_group */ + +/** @} VL53L0_cut11_group */ + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L0_API_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_api_calibration.h b/drivers/input/misc/vl53L0/inc/vl53l0_api_calibration.h new file mode 100644 index 000000000000..df9b43987eb5 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_api_calibration.h @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#ifndef _VL53L0_API_CALIBRATION_H_ +#define _VL53L0_API_CALIBRATION_H_ + +#include "vl53l0_def.h" +#include "vl53l0_platform.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +VL53L0_Error VL53L0_perform_xtalk_calibration(VL53L0_DEV Dev, + FixPoint1616_t XTalkCalDistance, + FixPoint1616_t *pXTalkCompensationRateMegaCps); + +VL53L0_Error VL53L0_perform_offset_calibration(VL53L0_DEV Dev, + FixPoint1616_t CalDistanceMilliMeter, + int32_t *pOffsetMicroMeter); + +VL53L0_Error VL53L0_set_offset_calibration_data_micro_meter(VL53L0_DEV Dev, + int32_t OffsetCalibrationDataMicroMeter); + +VL53L0_Error VL53L0_get_offset_calibration_data_micro_meter(VL53L0_DEV Dev, + int32_t *pOffsetCalibrationDataMicroMeter); + +VL53L0_Error VL53L0_apply_offset_adjustment(VL53L0_DEV Dev); + +VL53L0_Error VL53L0_perform_ref_spad_management(VL53L0_DEV Dev, + uint32_t *refSpadCount, uint8_t *isApertureSpads); + +VL53L0_Error VL53L0_set_reference_spads(VL53L0_DEV Dev, + uint32_t count, uint8_t isApertureSpads); + +VL53L0_Error VL53L0_get_reference_spads(VL53L0_DEV Dev, + uint32_t *pSpadCount, uint8_t *pIsApertureSpads); + +VL53L0_Error VL53L0_perform_phase_calibration(VL53L0_DEV Dev, + uint8_t *pPhaseCal, const uint8_t get_data_enable, + const uint8_t restore_config); + +VL53L0_Error VL53L0_perform_ref_calibration(VL53L0_DEV Dev, + uint8_t *pVhvSettings, uint8_t *pPhaseCal, uint8_t get_data_enable); + +VL53L0_Error VL53L0_set_ref_calibration(VL53L0_DEV Dev, + uint8_t VhvSettings, uint8_t PhaseCal); + +VL53L0_Error VL53L0_get_ref_calibration(VL53L0_DEV Dev, + uint8_t *pVhvSettings, uint8_t *pPhaseCal); + + + + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L0_API_CALIBRATION_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_api_core.h b/drivers/input/misc/vl53L0/inc/vl53l0_api_core.h new file mode 100644 index 000000000000..21cf9edec0e0 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_api_core.h @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#ifndef _VL53L0_API_CORE_H_ +#define _VL53L0_API_CORE_H_ + +#include "vl53l0_def.h" +#include "vl53l0_platform.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +VL53L0_Error VL53L0_reverse_bytes(uint8_t *data, uint32_t size); + +VL53L0_Error VL53L0_measurement_poll_for_completion(VL53L0_DEV Dev); + +uint8_t VL53L0_encode_vcsel_period(uint8_t vcsel_period_pclks); + +uint8_t VL53L0_decode_vcsel_period(uint8_t vcsel_period_reg); + +uint32_t VL53L0_isqrt(uint32_t num); + +uint32_t VL53L0_quadrature_sum(uint32_t a, uint32_t b); + +VL53L0_Error VL53L0_get_info_from_device(VL53L0_DEV Dev, uint8_t option); + +VL53L0_Error VL53L0_set_vcsel_pulse_period(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t VCSELPulsePeriodPCLK); + +VL53L0_Error VL53L0_get_vcsel_pulse_period(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t *pVCSELPulsePeriodPCLK); + +uint32_t VL53L0_decode_timeout(uint16_t encoded_timeout); + +VL53L0_Error get_sequence_step_timeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + uint32_t *pTimeOutMicroSecs); + +VL53L0_Error set_sequence_step_timeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + uint32_t TimeOutMicroSecs); + +VL53L0_Error VL53L0_set_measurement_timing_budget_micro_seconds(VL53L0_DEV Dev, + uint32_t MeasurementTimingBudgetMicroSeconds); + +VL53L0_Error VL53L0_get_measurement_timing_budget_micro_seconds(VL53L0_DEV Dev, + uint32_t *pMeasurementTimingBudgetMicroSeconds); + +VL53L0_Error VL53L0_load_tuning_settings(VL53L0_DEV Dev, + uint8_t *pTuningSettingBuffer); + +VL53L0_Error VL53L0_calc_sigma_estimate(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + FixPoint1616_t *pSigmaEstimate, uint32_t *pDmax_mm); + +VL53L0_Error VL53L0_get_total_xtalk_rate(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + FixPoint1616_t *ptotal_xtalk_rate_mcps); + +VL53L0_Error VL53L0_get_total_signal_rate(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + FixPoint1616_t *ptotal_signal_rate_mcps); + +VL53L0_Error VL53L0_get_pal_range_status(VL53L0_DEV Dev, + uint8_t DeviceRangeStatus, + FixPoint1616_t SignalRate, + uint16_t EffectiveSpadRtnCount, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + uint8_t *pPalRangeStatus); + +uint32_t VL53L0_calc_timeout_mclks(VL53L0_DEV Dev, + uint32_t timeout_period_us, uint8_t vcsel_period_pclks); + +uint16_t VL53L0_encode_timeout(uint32_t timeout_macro_clks); + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L0_API_CORE_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_api_histogram.h b/drivers/input/misc/vl53L0/inc/vl53l0_api_histogram.h new file mode 100644 index 000000000000..c2438a8cd79b --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_api_histogram.h @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#ifndef _VL53L0_API_HISTOGRAM_H_ +#define _VL53L0_API_HISTOGRAM_H_ + +#include "vl53l0_def.h" +#include "vl53l0_platform.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +VL53L0_Error VL53L0_confirm_measurement_start(VL53L0_DEV Dev); + +VL53L0_Error VL53L0_set_histogram_mode(VL53L0_DEV Dev, + VL53L0_HistogramModes HistogramMode); + +VL53L0_Error VL53L0_get_histogram_mode(VL53L0_DEV Dev, + VL53L0_HistogramModes *pHistogramMode); + +VL53L0_Error VL53L0_start_histogram_measurement(VL53L0_DEV Dev, + VL53L0_HistogramModes histoMode, + uint32_t count); + +VL53L0_Error VL53L0_perform_single_histogram_measurement(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData); + +VL53L0_Error VL53L0_get_histogram_measurement_data(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData); + +VL53L0_Error VL53L0_read_histo_measurement(VL53L0_DEV Dev, + uint32_t *histoData, uint32_t offset, VL53L0_HistogramModes histoMode); + +VL53L0_Error VL53L0_perform_xtalk_measurement(VL53L0_DEV dev, + uint32_t timeout_ms, FixPoint1616_t *pxtalk_per_spad, + uint8_t *pambient_too_high); + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L0_API_HISTOGRAM_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_api_ranging.h b/drivers/input/misc/vl53L0/inc/vl53l0_api_ranging.h new file mode 100644 index 000000000000..ba0bd03fe71f --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_api_ranging.h @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#ifndef _VL53L0_API_RANGING_H_ +#define _VL53L0_API_RANGING_H_ + +#include "vl53l0_def.h" +#include "vl53l0_platform.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L0_API_RANGING_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_api_strings.h b/drivers/input/misc/vl53L0/inc/vl53l0_api_strings.h new file mode 100644 index 000000000000..2bb6a1e8ea33 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_api_strings.h @@ -0,0 +1,277 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#ifndef VL53L0_API_STRINGS_H_ +#define VL53L0_API_STRINGS_H_ + +#include "vl53l0_def.h" +#include "vl53l0_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +VL53L0_Error VL53L0_get_device_info(VL53L0_DEV Dev, + VL53L0_DeviceInfo_t *pVL53L0_DeviceInfo); + +VL53L0_Error VL53L0_get_device_error_string(VL53L0_DeviceError ErrorCode, + char *pDeviceErrorString); + +VL53L0_Error VL53L0_get_range_status_string(uint8_t RangeStatus, + char *pRangeStatusString); + +VL53L0_Error VL53L0_get_pal_error_string(VL53L0_Error PalErrorCode, + char *pPalErrorString); + +VL53L0_Error VL53L0_get_pal_state_string(VL53L0_State PalStateCode, + char *pPalStateString); + +VL53L0_Error VL53L0_get_sequence_steps_info( + VL53L0_SequenceStepId SequenceStepId, + char *pSequenceStepsString); + +VL53L0_Error VL53L0_get_limit_check_info(VL53L0_DEV Dev, uint16_t LimitCheckId, + char *pLimitCheckString); + + +#ifdef USE_EMPTY_STRING + #define VL53L0_STRING_DEVICE_INFO_NAME "" + #define VL53L0_STRING_DEVICE_INFO_NAME_TS0 "" + #define VL53L0_STRING_DEVICE_INFO_NAME_TS1 "" + #define VL53L0_STRING_DEVICE_INFO_NAME_TS2 "" + #define VL53L0_STRING_DEVICE_INFO_NAME_ES1 "" + #define VL53L0_STRING_DEVICE_INFO_TYPE "" + + /* PAL ERROR strings */ + #define VL53L0_STRING_ERROR_NONE "" + #define VL53L0_STRING_ERROR_CALIBRATION_WARNING "" + #define VL53L0_STRING_ERROR_MIN_CLIPPED "" + #define VL53L0_STRING_ERROR_UNDEFINED "" + #define VL53L0_STRING_ERROR_INVALID_PARAMS "" + #define VL53L0_STRING_ERROR_NOT_SUPPORTED "" + #define VL53L0_STRING_ERROR_RANGE_ERROR "" + #define VL53L0_STRING_ERROR_TIME_OUT "" + #define VL53L0_STRING_ERROR_MODE_NOT_SUPPORTED "" + #define VL53L0_STRING_ERROR_BUFFER_TOO_SMALL "" + #define VL53L0_STRING_ERROR_GPIO_NOT_EXISTING "" + #define VL53L0_STRING_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED "" + #define VL53L0_STRING_ERROR_CONTROL_INTERFACE "" + #define VL53L0_STRING_ERROR_INVALID_COMMAND "" + #define VL53L0_STRING_ERROR_DIVISION_BY_ZERO "" + #define VL53L0_STRING_ERROR_REF_SPAD_INIT "" + #define VL53L0_STRING_ERROR_NOT_IMPLEMENTED "" + + #define VL53L0_STRING_UNKNOW_ERROR_CODE "" + + + + /* Range Status */ + #define VL53L0_STRING_RANGESTATUS_NONE "" + #define VL53L0_STRING_RANGESTATUS_RANGEVALID "" + #define VL53L0_STRING_RANGESTATUS_SIGMA "" + #define VL53L0_STRING_RANGESTATUS_SIGNAL "" + #define VL53L0_STRING_RANGESTATUS_MINRANGE "" + #define VL53L0_STRING_RANGESTATUS_PHASE "" + #define VL53L0_STRING_RANGESTATUS_HW "" + + + /* Range Status */ + #define VL53L0_STRING_STATE_POWERDOWN "" + #define VL53L0_STRING_STATE_WAIT_STATICINIT "" + #define VL53L0_STRING_STATE_STANDBY "" + #define VL53L0_STRING_STATE_IDLE "" + #define VL53L0_STRING_STATE_RUNNING "" + #define VL53L0_STRING_STATE_UNKNOWN "" + #define VL53L0_STRING_STATE_ERROR "" + + + /* Device Specific */ + #define VL53L0_STRING_DEVICEERROR_NONE "" + #define VL53L0_STRING_DEVICEERROR_VCSELCONTINUITYTESTFAILURE "" + #define VL53L0_STRING_DEVICEERROR_VCSELWATCHDOGTESTFAILURE "" + #define VL53L0_STRING_DEVICEERROR_NOVHVVALUEFOUND "" + #define VL53L0_STRING_DEVICEERROR_MSRCNOTARGET "" + #define VL53L0_STRING_DEVICEERROR_SNRCHECK "" + #define VL53L0_STRING_DEVICEERROR_RANGEPHASECHECK "" + #define VL53L0_STRING_DEVICEERROR_SIGMATHRESHOLDCHECK "" + #define VL53L0_STRING_DEVICEERROR_TCC "" + #define VL53L0_STRING_DEVICEERROR_PHASECONSISTENCY "" + #define VL53L0_STRING_DEVICEERROR_MINCLIP "" + #define VL53L0_STRING_DEVICEERROR_RANGECOMPLETE "" + #define VL53L0_STRING_DEVICEERROR_ALGOUNDERFLOW "" + #define VL53L0_STRING_DEVICEERROR_ALGOOVERFLOW "" + #define VL53L0_STRING_DEVICEERROR_RANGEIGNORETHRESHOLD "" + #define VL53L0_STRING_DEVICEERROR_UNKNOWN "" + + /* Check Enable */ + #define VL53L0_STRING_CHECKENABLE_SIGMA_FINAL_RANGE "" + #define VL53L0_STRING_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE "" + #define VL53L0_STRING_CHECKENABLE_SIGNAL_REF_CLIP "" + #define VL53L0_STRING_CHECKENABLE_RANGE_IGNORE_THRESHOLD "" + + /* Sequence Step */ + #define VL53L0_STRING_SEQUENCESTEP_TCC "" + #define VL53L0_STRING_SEQUENCESTEP_DSS "" + #define VL53L0_STRING_SEQUENCESTEP_MSRC "" + #define VL53L0_STRING_SEQUENCESTEP_PRE_RANGE "" + #define VL53L0_STRING_SEQUENCESTEP_FINAL_RANGE "" +#else + #define VL53L0_STRING_DEVICE_INFO_NAME "VL53L0 cut1.0" + #define VL53L0_STRING_DEVICE_INFO_NAME_TS0 "VL53L0 TS0" + #define VL53L0_STRING_DEVICE_INFO_NAME_TS1 "VL53L0 TS1" + #define VL53L0_STRING_DEVICE_INFO_NAME_TS2 "VL53L0 TS2" + #define VL53L0_STRING_DEVICE_INFO_NAME_ES1 "VL53L0 ES1 or later" + #define VL53L0_STRING_DEVICE_INFO_TYPE "VL53L0" + + /* PAL ERROR strings */ + #define VL53L0_STRING_ERROR_NONE \ + "No Error" + #define VL53L0_STRING_ERROR_CALIBRATION_WARNING \ + "Calibration Warning Error" + #define VL53L0_STRING_ERROR_MIN_CLIPPED \ + "Min clipped error" + #define VL53L0_STRING_ERROR_UNDEFINED \ + "Undefined error" + #define VL53L0_STRING_ERROR_INVALID_PARAMS \ + "Invalid parameters error" + #define VL53L0_STRING_ERROR_NOT_SUPPORTED \ + "Not supported error" + #define VL53L0_STRING_ERROR_RANGE_ERROR \ + "Range error" + #define VL53L0_STRING_ERROR_TIME_OUT \ + "Time out error" + #define VL53L0_STRING_ERROR_MODE_NOT_SUPPORTED \ + "Mode not supported error" + #define VL53L0_STRING_ERROR_BUFFER_TOO_SMALL \ + "Buffer too small" + #define VL53L0_STRING_ERROR_GPIO_NOT_EXISTING \ + "GPIO not existing" + #define VL53L0_STRING_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED \ + "GPIO funct not supported" + #define VL53L0_STRING_ERROR_INTERRUPT_NOT_CLEARED \ + "Interrupt not Cleared" + #define VL53L0_STRING_ERROR_CONTROL_INTERFACE \ + "Control Interface Error" + #define VL53L0_STRING_ERROR_INVALID_COMMAND \ + "Invalid Command Error" + #define VL53L0_STRING_ERROR_DIVISION_BY_ZERO \ + "Division by zero Error" + #define VL53L0_STRING_ERROR_REF_SPAD_INIT \ + "Reference Spad Init Error" + #define VL53L0_STRING_ERROR_NOT_IMPLEMENTED \ + "Not implemented error" + + #define VL53L0_STRING_UNKNOW_ERROR_CODE \ + "Unknown Error Code" + + + + /* Range Status */ + #define VL53L0_STRING_RANGESTATUS_NONE "No Update" + #define VL53L0_STRING_RANGESTATUS_RANGEVALID "Range Valid" + #define VL53L0_STRING_RANGESTATUS_SIGMA "Sigma Fail" + #define VL53L0_STRING_RANGESTATUS_SIGNAL "Signal Fail" + #define VL53L0_STRING_RANGESTATUS_MINRANGE "Min Range Fail" + #define VL53L0_STRING_RANGESTATUS_PHASE "Phase Fail" + #define VL53L0_STRING_RANGESTATUS_HW "Hardware Fail" + + + /* Range Status */ + #define VL53L0_STRING_STATE_POWERDOWN "POWERDOWN State" + #define VL53L0_STRING_STATE_WAIT_STATICINIT \ + "Wait for staticinit State" + #define VL53L0_STRING_STATE_STANDBY "STANDBY State" + #define VL53L0_STRING_STATE_IDLE "IDLE State" + #define VL53L0_STRING_STATE_RUNNING "RUNNING State" + #define VL53L0_STRING_STATE_UNKNOWN "UNKNOWN State" + #define VL53L0_STRING_STATE_ERROR "ERROR State" + + + /* Device Specific */ + #define VL53L0_STRING_DEVICEERROR_NONE "No Update" + #define VL53L0_STRING_DEVICEERROR_VCSELCONTINUITYTESTFAILURE \ + "VCSEL Continuity Test Failure" + #define VL53L0_STRING_DEVICEERROR_VCSELWATCHDOGTESTFAILURE \ + "VCSEL Watchdog Test Failure" + #define VL53L0_STRING_DEVICEERROR_NOVHVVALUEFOUND \ + "No VHV Value found" + #define VL53L0_STRING_DEVICEERROR_MSRCNOTARGET \ + "MSRC No Target Error" + #define VL53L0_STRING_DEVICEERROR_SNRCHECK \ + "SNR Check Exit" + #define VL53L0_STRING_DEVICEERROR_RANGEPHASECHECK \ + "Range Phase Check Error" + #define VL53L0_STRING_DEVICEERROR_SIGMATHRESHOLDCHECK \ + "Sigma Threshold Check Error" + #define VL53L0_STRING_DEVICEERROR_TCC \ + "TCC Error" + #define VL53L0_STRING_DEVICEERROR_PHASECONSISTENCY \ + "Phase Consistency Error" + #define VL53L0_STRING_DEVICEERROR_MINCLIP \ + "Min Clip Error" + #define VL53L0_STRING_DEVICEERROR_RANGECOMPLETE \ + "Range Complete" + #define VL53L0_STRING_DEVICEERROR_ALGOUNDERFLOW \ + "Range Algo Underflow Error" + #define VL53L0_STRING_DEVICEERROR_ALGOOVERFLOW \ + "Range Algo Overlow Error" + #define VL53L0_STRING_DEVICEERROR_RANGEIGNORETHRESHOLD \ + "Range Ignore Threshold Error" + #define VL53L0_STRING_DEVICEERROR_UNKNOWN \ + "Unknown error code" + + /* Check Enable */ + #define VL53L0_STRING_CHECKENABLE_SIGMA_FINAL_RANGE \ + "SIGMA FINAL RANGE" + #define VL53L0_STRING_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE \ + "SIGNAL RATE FINAL RANGE" + #define VL53L0_STRING_CHECKENABLE_SIGNAL_REF_CLIP \ + "SIGNAL REF CLIP" + #define VL53L0_STRING_CHECKENABLE_RANGE_IGNORE_THRESHOLD \ + "RANGE IGNORE THRESHOLD" + #define VL53L0_STRING_CHECKENABLE_SIGNAL_RATE_MSRC \ + "SIGNAL RATE MSRC" + #define VL53L0_STRING_CHECKENABLE_SIGNAL_RATE_PRE_RANGE \ + "SIGNAL RATE PRE RANGE" + + /* Sequence Step */ + #define VL53L0_STRING_SEQUENCESTEP_TCC "TCC" + #define VL53L0_STRING_SEQUENCESTEP_DSS "DSS" + #define VL53L0_STRING_SEQUENCESTEP_MSRC "MSRC" + #define VL53L0_STRING_SEQUENCESTEP_PRE_RANGE "PRE RANGE" + #define VL53L0_STRING_SEQUENCESTEP_FINAL_RANGE "FINAL RANGE" +#endif /* USE_EMPTY_STRING */ + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_def.h b/drivers/input/misc/vl53L0/inc/vl53l0_def.h new file mode 100644 index 000000000000..009715a5924f --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_def.h @@ -0,0 +1,663 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +/** + * @file VL53L0_def.h + * + * @brief Type definitions for VL53L0 API. + * + */ + + +#ifndef _VL53L0_DEF_H_ +#define _VL53L0_DEF_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup VL53L0_globaldefine_group VL53L0 Defines + * @brief VL53L0 Defines + * @{ + */ + + +/** PAL SPECIFICATION major version */ +#define VL53L010_SPECIFICATION_VER_MAJOR 1 +/** PAL SPECIFICATION minor version */ +#define VL53L010_SPECIFICATION_VER_MINOR 2 +/** PAL SPECIFICATION sub version */ +#define VL53L010_SPECIFICATION_VER_SUB 7 +/** PAL SPECIFICATION sub version */ +#define VL53L010_SPECIFICATION_VER_REVISION 1440 + +/** VL53L0 PAL IMPLEMENTATION major version */ +#define VL53L010_IMPLEMENTATION_VER_MAJOR 1 +/** VL53L0 PAL IMPLEMENTATION minor version */ +#define VL53L010_IMPLEMENTATION_VER_MINOR 0 +/** VL53L0 PAL IMPLEMENTATION sub version */ +#define VL53L010_IMPLEMENTATION_VER_SUB 9 +/** VL53L0 PAL IMPLEMENTATION sub version */ +#define VL53L010_IMPLEMENTATION_VER_REVISION 3673 + +/** PAL SPECIFICATION major version */ +#define VL53L0_SPECIFICATION_VER_MAJOR 1 +/** PAL SPECIFICATION minor version */ +#define VL53L0_SPECIFICATION_VER_MINOR 2 +/** PAL SPECIFICATION sub version */ +#define VL53L0_SPECIFICATION_VER_SUB 7 +/** PAL SPECIFICATION sub version */ +#define VL53L0_SPECIFICATION_VER_REVISION 1440 + +/** VL53L0 PAL IMPLEMENTATION major version */ +#define VL53L0_IMPLEMENTATION_VER_MAJOR 1 +/** VL53L0 PAL IMPLEMENTATION minor version */ +#define VL53L0_IMPLEMENTATION_VER_MINOR 1 +/** VL53L0 PAL IMPLEMENTATION sub version */ +#define VL53L0_IMPLEMENTATION_VER_SUB 20 +/** VL53L0 PAL IMPLEMENTATION sub version */ +#define VL53L0_IMPLEMENTATION_VER_REVISION 4606 +#define VL53L0_DEFAULT_MAX_LOOP 200 +#define VL53L0_MAX_STRING_LENGTH 32 + + +#include "vl53l0_device.h" +#include "vl53l0_types.h" + + +/**************************************** + * PRIVATE define do not edit + ****************************************/ + +/** @brief Defines the parameters of the Get Version Functions + */ +typedef struct { + uint32_t revision; /*!< revision number */ + uint8_t major; /*!< major number */ + uint8_t minor; /*!< minor number */ + uint8_t build; /*!< build number */ +} VL53L0_Version_t; + + +/** @brief Defines the parameters of the Get Device Info Functions + */ +typedef struct { + char Name[VL53L0_MAX_STRING_LENGTH]; + /*!< Name of the Device e.g. Left_Distance */ + char Type[VL53L0_MAX_STRING_LENGTH]; + /*!< Type of the Device e.g VL53L0 */ + char ProductId[VL53L0_MAX_STRING_LENGTH]; + /*!< Product Identifier String */ + uint8_t ProductType; + /*!< Product Type, VL53L0 = 1, VL53L1 = 2 */ + uint8_t ProductRevisionMajor; + /*!< Product revision major */ + uint8_t ProductRevisionMinor; + /*!< Product revision minor */ +} VL53L0_DeviceInfo_t; + + +/** @defgroup VL53L0_define_Error_group Error and Warning code returned by API + * The following DEFINE are used to identify the PAL ERROR + * @{ + */ + +typedef int8_t VL53L0_Error; + +#define VL53L0_ERROR_NONE ((VL53L0_Error) 0) +#define VL53L0_ERROR_CALIBRATION_WARNING ((VL53L0_Error) - 1) + /*!< Warning invalid calibration data may be in used + * \a VL53L0_InitData() + * \a VL53L0_GetOffsetCalibrationData + * \a VL53L0_SetOffsetCalibrationData + */ +#define VL53L0_ERROR_MIN_CLIPPED ((VL53L0_Error) - 2) + /*!< Warning parameter passed was clipped to min before to be applied */ + +#define VL53L0_ERROR_UNDEFINED ((VL53L0_Error) - 3) + /*!< Unqualified error */ +#define VL53L0_ERROR_INVALID_PARAMS ((VL53L0_Error) - 4) + /*!< Parameter passed is invalid or out of range */ +#define VL53L0_ERROR_NOT_SUPPORTED ((VL53L0_Error) - 5) + /*!< Function is not supported in current mode or configuration */ +#define VL53L0_ERROR_RANGE_ERROR ((VL53L0_Error) - 6) + /*!< Device report a ranging error interrupt status */ +#define VL53L0_ERROR_TIME_OUT ((VL53L0_Error) - 7) + /*!< Aborted due to time out */ +#define VL53L0_ERROR_MODE_NOT_SUPPORTED ((VL53L0_Error) - 8) + /*!< Asked mode is not supported by the device */ +#define VL53L0_ERROR_BUFFER_TOO_SMALL ((VL53L0_Error) - 9) + /*!< ... */ +#define VL53L0_ERROR_GPIO_NOT_EXISTING ((VL53L0_Error) - 10) + /*!< User tried to setup a non-existing GPIO pin */ +#define VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED ((VL53L0_Error) - 11) + /*!< unsupported GPIO functionality */ +#define VL53L0_ERROR_INTERRUPT_NOT_CLEARED ((VL53L0_Error) - 12) + /*!< Error during interrupt clear */ +#define VL53L0_ERROR_CONTROL_INTERFACE ((VL53L0_Error) - 20) + /*!< error reported from IO functions */ +#define VL53L0_ERROR_INVALID_COMMAND ((VL53L0_Error) - 30) + /*!< The command is not allowed in the current device state + * (power down) + */ +#define VL53L0_ERROR_DIVISION_BY_ZERO ((VL53L0_Error) - 40) + /*!< In the function a division by zero occurs */ +#define VL53L0_ERROR_REF_SPAD_INIT ((VL53L0_Error) - 50) + /*!< Error during reference SPAD initialization */ +#define VL53L0_ERROR_NOT_IMPLEMENTED ((VL53L0_Error) - 99) + /*!< Tells requested functionality has not been implemented yet or + * not compatible with the device + */ +/** @} VL53L0_define_Error_group */ + + +/** @defgroup VL53L0_define_DeviceModes_group Defines Device modes + * Defines all possible modes for the device + * @{ + */ +typedef uint8_t VL53L0_DeviceModes; + +#define VL53L0_DEVICEMODE_SINGLE_RANGING ((VL53L0_DeviceModes) 0) +#define VL53L0_DEVICEMODE_CONTINUOUS_RANGING ((VL53L0_DeviceModes) 1) +#define VL53L0_DEVICEMODE_SINGLE_HISTOGRAM ((VL53L0_DeviceModes) 2) +#define VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING ((VL53L0_DeviceModes) 3) +#define VL53L0_DEVICEMODE_SINGLE_ALS ((VL53L0_DeviceModes) 10) +#define VL53L0_DEVICEMODE_GPIO_DRIVE ((VL53L0_DeviceModes) 20) +#define VL53L0_DEVICEMODE_GPIO_OSC ((VL53L0_DeviceModes) 21) + /* ... Modes to be added depending on device */ +/** @} VL53L0_define_DeviceModes_group */ + + + +/** @defgroup VL53L0_define_HistogramModes_group Defines Histogram modes + * Defines all possible Histogram modes for the device + * @{ + */ +typedef uint8_t VL53L0_HistogramModes; + +#define VL53L0_HISTOGRAMMODE_DISABLED ((VL53L0_HistogramModes) 0) + /*!< Histogram Disabled */ +#define VL53L0_HISTOGRAMMODE_REFERENCE_ONLY ((VL53L0_HistogramModes) 1) + /*!< Histogram Reference array only */ +#define VL53L0_HISTOGRAMMODE_RETURN_ONLY ((VL53L0_HistogramModes) 2) + /*!< Histogram Return array only */ +#define VL53L0_HISTOGRAMMODE_BOTH ((VL53L0_HistogramModes) 3) + /*!< Histogram both Reference and Return Arrays */ + /* ... Modes to be added depending on device */ +/** @} VL53L0_define_HistogramModes_group */ + + +/** @defgroup VL53L0_define_PowerModes_group List of available Power Modes + * List of available Power Modes + * @{ + */ + +typedef uint8_t VL53L0_PowerModes; + +#define VL53L0_POWERMODE_STANDBY_LEVEL1 ((VL53L0_PowerModes) 0) + /*!< Standby level 1 */ +#define VL53L0_POWERMODE_STANDBY_LEVEL2 ((VL53L0_PowerModes) 1) + /*!< Standby level 2 */ +#define VL53L0_POWERMODE_IDLE_LEVEL1 ((VL53L0_PowerModes) 2) + /*!< Idle level 1 */ +#define VL53L0_POWERMODE_IDLE_LEVEL2 ((VL53L0_PowerModes) 3) + /*!< Idle level 2 */ + +/** @} VL53L0_define_PowerModes_group */ + + +/** @brief Defines all parameters for the device + */ +typedef struct { + VL53L0_DeviceModes DeviceMode; + /*!< Defines type of measurement to be done for the next measure */ + VL53L0_HistogramModes HistogramMode; + /*!< Defines type of histogram measurement to be done for the next + * measure + */ + uint32_t MeasurementTimingBudgetMicroSeconds; + /*!< Defines the allowed total time for a single measurement */ + uint32_t InterMeasurementPeriodMilliSeconds; + /*!< Defines time between two consecutive measurements (between two + * measurement starts). If set to 0 means back-to-back mode + */ + uint8_t XTalkCompensationEnable; + /*!< Tells if Crosstalk compensation shall be enable or not */ + uint16_t XTalkCompensationRangeMilliMeter; + /*!< CrossTalk compensation range in millimeter */ + FixPoint1616_t XTalkCompensationRateMegaCps; + /*!< CrossTalk compensation rate in Mega counts per seconds. + * Expressed in 16.16 fixed point format. + */ + int32_t RangeOffsetMicroMeters; + /*!< Range offset adjustment (mm). */ + + uint8_t LimitChecksEnable[VL53L0_CHECKENABLE_NUMBER_OF_CHECKS]; + /*!< This Array store all the Limit Check enable for this device. */ + uint8_t LimitChecksStatus[VL53L0_CHECKENABLE_NUMBER_OF_CHECKS]; + /*!< This Array store all the Status of the check linked to last + * measurement. + */ + FixPoint1616_t LimitChecksValue[VL53L0_CHECKENABLE_NUMBER_OF_CHECKS]; + /*!< This Array store all the Limit Check value for this device */ + + uint8_t WrapAroundCheckEnable; + /*!< Tells if Wrap Around Check shall be enable or not */ +} VL53L0_DeviceParameters_t; + + +/** @defgroup VL53L0_define_State_group Defines the current status of the device + * Defines the current status of the device + * @{ + */ + +typedef uint8_t VL53L0_State; + +#define VL53L0_STATE_POWERDOWN ((VL53L0_State) 0) + /*!< Device is in HW reset */ +#define VL53L0_STATE_WAIT_STATICINIT ((VL53L0_State) 1) + /*!< Device is initialized and wait for static initialization */ +#define VL53L0_STATE_STANDBY ((VL53L0_State) 2) + /*!< Device is in Low power Standby mode */ +#define VL53L0_STATE_IDLE ((VL53L0_State) 3) + /*!< Device has been initialized and ready to do measurements */ +#define VL53L0_STATE_RUNNING ((VL53L0_State) 4) + /*!< Device is performing measurement */ +#define VL53L0_STATE_UNKNOWN ((VL53L0_State) 98) + /*!< Device is in unknown state and need to be rebooted */ +#define VL53L0_STATE_ERROR ((VL53L0_State) 99) + /*!< Device is in error state and need to be rebooted */ + +/** @} VL53L0_define_State_group */ + + +/** @brief Structure containing the Dmax computation parameters and data + */ +typedef struct { + int32_t AmbTuningWindowFactor_K; + /*!< internal algo tuning (*1000) */ + int32_t RetSignalAt0mm; + /*!< intermediate dmax computation value caching */ +} VL53L0_DMaxData_t; + +/** + * @struct VL53L0_RangeData_t + * @brief Range measurement data. + */ +typedef struct { + uint32_t TimeStamp; /*!< 32-bit time stamp. */ + uint32_t MeasurementTimeUsec; + /*!< Give the Measurement time needed by the device to do the + * measurement. + */ + + + uint16_t RangeMilliMeter; /*!< range distance in millimeter. */ + + uint16_t RangeDMaxMilliMeter; + /*!< Tells what is the maximum detection distance of the device + * in current setup and environment conditions (Filled when + * applicable) + */ + + FixPoint1616_t SignalRateRtnMegaCps; + /*!< Return signal rate (MCPS)\n these is a 16.16 fix point + * value, which is effectively a measure of target + * reflectance. + */ + FixPoint1616_t AmbientRateRtnMegaCps; + /*!< Return ambient rate (MCPS)\n these is a 16.16 fix point + * value, which is effectively a measure of the ambien + * t light. + */ + + uint16_t EffectiveSpadRtnCount; + /*!< Return the effective SPAD count for the return signal. + * To obtain Real value it should be divided by 256 + */ + + uint8_t ZoneId; + /*!< Denotes which zone and range scheduler stage the range + * data relates to. + */ + uint8_t RangeFractionalPart; + /*!< Fractional part of range distance. Final value is a + * FixPoint168 value. + */ + uint8_t RangeStatus; + /*!< Range Status for the current measurement. This is device + * dependent. Value = 0 means value is valid. + * See \ref RangeStatusPage + */ +} VL53L0_RangingMeasurementData_t; + + +#define VL53L0_HISTOGRAM_BUFFER_SIZE 24 + +/** + * @struct VL53L0_HistogramData_t + * @brief Histogram measurement data. + */ +typedef struct { + /* Histogram Measurement data */ + uint32_t HistogramData[VL53L0_HISTOGRAM_BUFFER_SIZE]; + /*!< Histogram data */ + /*!< Indicate the types of histogram data : + *Return only, Reference only, both Return and Reference + */ + uint8_t HistogramType; + uint8_t FirstBin; /*!< First Bin value */ + uint8_t BufferSize; /*!< Buffer Size - Set by the user.*/ + uint8_t NumberOfBins; + /*!< Number of bins filled by the histogram measurement */ + + VL53L0_DeviceError ErrorStatus; + /*!< Error status of the current measurement. \n + * see @a ::VL53L0_DeviceError @a VL53L0_GetStatusErrorString() + */ +} VL53L0_HistogramMeasurementData_t; + +#define VL53L0_REF_SPAD_BUFFER_SIZE 6 + +/** + * @struct VL53L0_SpadData_t + * @brief Spad Configuration Data. + */ +typedef struct { + uint8_t RefSpadEnables[VL53L0_REF_SPAD_BUFFER_SIZE]; + /*!< Reference Spad Enables */ + uint8_t RefGoodSpadMap[VL53L0_REF_SPAD_BUFFER_SIZE]; + /*!< Reference Spad Good Spad Map */ +} VL53L0_SpadData_t; + +typedef struct { + FixPoint1616_t OscFrequencyMHz; /* Frequency used */ + + uint16_t LastEncodedTimeout; + /* last encoded Time out used for timing budget*/ + + VL53L0_GpioFunctionality Pin0GpioFunctionality; + /* store the functionality of the GPIO: pin0 */ + + uint32_t FinalRangeTimeoutMicroSecs; + /*!< Execution time of the final range*/ + uint8_t FinalRangeVcselPulsePeriod; + /*!< Vcsel pulse period (pll clocks) for the final range measurement*/ + uint32_t PreRangeTimeoutMicroSecs; + /*!< Execution time of the final range*/ + uint8_t PreRangeVcselPulsePeriod; + /*!< Vcsel pulse period (pll clocks) for the pre-range measurement*/ + + uint16_t SigmaEstRefArray; + /*!< Reference array sigma value in 1/100th of [mm] e.g. 100 = 1mm */ + uint16_t SigmaEstEffPulseWidth; + /*!< Effective Pulse width for sigma estimate in 1/100th + * of ns e.g. 900 = 9.0ns + */ + uint16_t SigmaEstEffAmbWidth; + /*!< Effective Ambient width for sigma estimate in 1/100th of ns + * e.g. 500 = 5.0ns + */ + + + /* Indicate if read from device has been done (==1) or not (==0) */ + uint8_t ReadDataFromDeviceDone; + uint8_t ModuleId; /* Module ID */ + uint8_t Revision; /* test Revision */ + char ProductId[VL53L0_MAX_STRING_LENGTH]; + /* Product Identifier String */ + uint8_t ReferenceSpadCount; /* used for ref spad management */ + uint8_t ReferenceSpadType; /* used for ref spad management */ + uint8_t RefSpadsInitialised; /* reports if ref spads are initialised. */ + uint32_t PartUIDUpper; /*!< Unique Part ID Upper */ + uint32_t PartUIDLower; /*!< Unique Part ID Lower */ + /*!< Peek Signal rate at 400 mm*/ + FixPoint1616_t SignalRateMeasFixed400mm; + +} VL53L0_DeviceSpecificParameters_t; + +/** + * @struct VL53L0_DevData_t + * + * @brief VL53L0 PAL device ST private data structure \n + * End user should never access any of these field directly + * + * These must never access directly but only via macro + */ +typedef struct { + VL53L0_DMaxData_t DMaxData; + /*!< Dmax Data */ + int32_t Part2PartOffsetNVMMicroMeter; + /*!< backed up NVM value */ + int32_t Part2PartOffsetAdjustmentNVMMicroMeter; + /*!< backed up NVM value representing additional offset adjustment */ + VL53L0_DeviceParameters_t CurrentParameters; + /*!< Current Device Parameter */ + VL53L0_RangingMeasurementData_t LastRangeMeasure; + /*!< Ranging Data */ + VL53L0_HistogramMeasurementData_t LastHistogramMeasure; + /*!< Histogram Data */ + VL53L0_DeviceSpecificParameters_t DeviceSpecificParameters; + /*!< Parameters specific to the device */ + VL53L0_SpadData_t SpadData; + /*!< Spad Data */ + uint8_t SequenceConfig; + /*!< Internal value for the sequence config */ + uint8_t RangeFractionalEnable; + /*!< Enable/Disable fractional part of ranging data */ + VL53L0_State PalState; + /*!< Current state of the PAL for this device */ + VL53L0_PowerModes PowerMode; + /*!< Current Power Mode */ + uint16_t SigmaEstRefArray; + /*!< Reference array sigma value in 1/100th of [mm] e.g. 100 = 1mm */ + uint16_t SigmaEstEffPulseWidth; + /*!< Effective Pulse width for sigma estimate in 1/100th + * of ns e.g. 900 = 9.0ns + */ + uint16_t SigmaEstEffAmbWidth; + /*!< Effective Ambient width for sigma estimate in 1/100th of ns + * e.g. 500 = 5.0ns + */ + uint8_t StopVariable; + /*!< StopVariable used during the stop sequence */ + uint16_t targetRefRate; + /*!< Target Ambient Rate for Ref spad management */ + FixPoint1616_t SigmaEstimate; + /*!< Sigma Estimate - based on ambient & VCSEL rates and + * signal_total_events + */ + FixPoint1616_t SignalEstimate; + /*!< Signal Estimate - based on ambient & VCSEL rates and cross talk */ + FixPoint1616_t LastSignalRefMcps; + /*!< Latest Signal ref in Mcps */ + uint8_t *pTuningSettingsPointer; + /*!< Pointer for Tuning Settings table */ + uint8_t UseInternalTuningSettings; + /*!< Indicate if we use Tuning Settings table */ + uint16_t LinearityCorrectiveGain; + /*!< Linearity Corrective Gain value in x1000 */ + uint16_t DmaxCalRangeMilliMeter; + /*!< Dmax Calibration Range millimeter */ + FixPoint1616_t DmaxCalSignalRateRtnMegaCps; + /*!< Dmax Calibration Signal Rate Return MegaCps */ + +} VL53L0_DevData_t; + + +/** @defgroup VL53L0_define_InterruptPolarity_group Defines the Polarity + * of the Interrupt + * Defines the Polarity of the Interrupt + * @{ + */ +typedef uint8_t VL53L0_InterruptPolarity; + +#define VL53L0_INTERRUPTPOLARITY_LOW ((VL53L0_InterruptPolarity) 0) +/*!< Set active low polarity best setup for falling edge. */ +#define VL53L0_INTERRUPTPOLARITY_HIGH ((VL53L0_InterruptPolarity) 1) +/*!< Set active high polarity best setup for rising edge. */ + +/** @} VL53L0_define_InterruptPolarity_group */ + + +/** @defgroup VL53L0_define_VcselPeriod_group Vcsel Period Defines + * Defines the range measurement for which to access the vcsel period. + * @{ + */ +typedef uint8_t VL53L0_VcselPeriod; + +#define VL53L0_VCSEL_PERIOD_PRE_RANGE ((VL53L0_VcselPeriod) 0) +/*!<Identifies the pre-range vcsel period. */ +#define VL53L0_VCSEL_PERIOD_FINAL_RANGE ((VL53L0_VcselPeriod) 1) +/*!<Identifies the final range vcsel period. */ + +/** @} VL53L0_define_VcselPeriod_group */ + +/** @defgroup VL53L0_define_SchedulerSequence_group Defines the steps + * carried out by the scheduler during a range measurement. + * @{ + * Defines the states of all the steps in the scheduler + * i.e. enabled/disabled. + */ +typedef struct { + uint8_t TccOn; /*!<Reports if Target Centre Check On */ + uint8_t MsrcOn; /*!<Reports if MSRC On */ + uint8_t DssOn; /*!<Reports if DSS On */ + uint8_t PreRangeOn; /*!<Reports if Pre-Range On */ + uint8_t FinalRangeOn; /*!<Reports if Final-Range On */ +} VL53L0_SchedulerSequenceSteps_t; + +/** @} VL53L0_define_SchedulerSequence_group */ + +/** @defgroup VL53L0_define_SequenceStepId_group Defines the Polarity + * of the Interrupt + * Defines the the sequence steps performed during ranging.. + * @{ + */ +typedef uint8_t VL53L0_SequenceStepId; + +#define VL53L0_SEQUENCESTEP_TCC ((VL53L0_VcselPeriod) 0) +/*!<Target CentreCheck identifier. */ +#define VL53L0_SEQUENCESTEP_DSS ((VL53L0_VcselPeriod) 1) +/*!<Dynamic Spad Selection function Identifier. */ +#define VL53L0_SEQUENCESTEP_MSRC ((VL53L0_VcselPeriod) 2) +/*!<Minimum Signal Rate Check function Identifier. */ +#define VL53L0_SEQUENCESTEP_PRE_RANGE ((VL53L0_VcselPeriod) 3) +/*!<Pre-Range check Identifier. */ +#define VL53L0_SEQUENCESTEP_FINAL_RANGE ((VL53L0_VcselPeriod) 4) +/*!<Final Range Check Identifier. */ + +#define VL53L0_SEQUENCESTEP_NUMBER_OF_CHECKS 5 +/*!<Number of Sequence Step Managed by the API. */ + +/** @} VL53L0_define_SequenceStepId_group */ + + +/* MACRO Definitions */ +/** @defgroup VL53L0_define_GeneralMacro_group General Macro Defines + * General Macro Defines + * @{ + */ + +/* Defines */ +#define VL53L0_SETPARAMETERFIELD(Dev, field, value) \ + PALDevDataSet(Dev, CurrentParameters.field, value) + +#define VL53L0_GETPARAMETERFIELD(Dev, field, variable) \ + (variable = ((PALDevDataGet(Dev, CurrentParameters)).field)) + + +#define VL53L0_SETARRAYPARAMETERFIELD(Dev, field, index, value) \ + PALDevDataSet(Dev, CurrentParameters.field[index], value) + +#define VL53L0_GETARRAYPARAMETERFIELD(Dev, field, index, variable) \ + (variable = (PALDevDataGet(Dev, CurrentParameters)).field[index]) + + +#define VL53L0_SETDEVICESPECIFICPARAMETER(Dev, field, value) \ + PALDevDataSet(Dev, DeviceSpecificParameters.field, value) + +#define VL53L0_GETDEVICESPECIFICPARAMETER(Dev, field) \ + PALDevDataGet(Dev, DeviceSpecificParameters).field + + +#define VL53L0_FIXPOINT1616TOFIXPOINT97(Value) \ + (uint16_t)((Value>>9)&0xFFFF) +#define VL53L0_FIXPOINT97TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value<<9) + +#define VL53L0_FIXPOINT1616TOFIXPOINT88(Value) \ + (uint16_t)((Value>>8)&0xFFFF) +#define VL53L0_FIXPOINT88TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value<<8) + +#define VL53L0_FIXPOINT1616TOFIXPOINT412(Value) \ + (uint16_t)((Value>>4)&0xFFFF) +#define VL53L0_FIXPOINT412TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value<<4) + +#define VL53L0_FIXPOINT1616TOFIXPOINT313(Value) \ + (uint16_t)((Value>>3)&0xFFFF) +#define VL53L0_FIXPOINT313TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value<<3) + +#define VL53L0_FIXPOINT1616TOFIXPOINT08(Value) \ + (uint8_t)((Value>>8)&0x00FF) +#define VL53L0_FIXPOINT08TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value<<8) + +#define VL53L0_FIXPOINT1616TOFIXPOINT53(Value) \ + (uint8_t)((Value>>13)&0x00FF) +#define VL53L0_FIXPOINT53TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value<<13) + +#define VL53L0_FIXPOINT1616TOFIXPOINT102(Value) \ + (uint16_t)((Value>>14)&0x0FFF) +#define VL53L0_FIXPOINT102TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value<<12) + +#define VL53L0_MAKEUINT16(lsb, msb) (uint16_t)((((uint16_t)msb)<<8) + \ + (uint16_t)lsb) + +/** @} VL53L0_define_GeneralMacro_group */ + +/** @} VL53L0_globaldefine_group */ + + + + + + + +#ifdef __cplusplus +} +#endif + + +#endif /* _VL53L0_DEF_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_device.h b/drivers/input/misc/vl53L0/inc/vl53l0_device.h new file mode 100644 index 000000000000..b69b9cf72279 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_device.h @@ -0,0 +1,261 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +/** + * Device specific defines. To be adapted by implementer for the targeted + * device. + */ + +#ifndef _VL53L0_DEVICE_H_ +#define _VL53L0_DEVICE_H_ + +#include "vl53l0_types.h" + + +/** @defgroup VL53L0_DevSpecDefines_group VL53L0 cut1.1 Device Specific Defines + * @brief VL53L0 cut1.1 Device Specific Defines + * @{ + */ + + +/** @defgroup VL53L0_DeviceError_group Device Error + * @brief Device Error code + * + * This enum is Device specific it should be updated in the implementation + * Use @a VL53L0_GetStatusErrorString() to get the string. + * It is related to Status Register of the Device. + * @{ + */ +typedef uint8_t VL53L0_DeviceError; + +#define VL53L0_DEVICEERROR_NONE ((VL53L0_DeviceError) 0) + /*!< 0 NoError */ +#define VL53L0_DEVICEERROR_VCSELCONTINUITYTESTFAILURE ((VL53L0_DeviceError) 1) +#define VL53L0_DEVICEERROR_VCSELWATCHDOGTESTFAILURE ((VL53L0_DeviceError) 2) +#define VL53L0_DEVICEERROR_NOVHVVALUEFOUND ((VL53L0_DeviceError) 3) +#define VL53L0_DEVICEERROR_MSRCNOTARGET ((VL53L0_DeviceError) 4) +#define VL53L0_DEVICEERROR_SNRCHECK ((VL53L0_DeviceError) 5) +#define VL53L0_DEVICEERROR_RANGEPHASECHECK ((VL53L0_DeviceError) 6) +#define VL53L0_DEVICEERROR_SIGMATHRESHOLDCHECK ((VL53L0_DeviceError) 7) +#define VL53L0_DEVICEERROR_TCC ((VL53L0_DeviceError) 8) +#define VL53L0_DEVICEERROR_PHASECONSISTENCY ((VL53L0_DeviceError) 9) +#define VL53L0_DEVICEERROR_MINCLIP ((VL53L0_DeviceError) 10) +#define VL53L0_DEVICEERROR_RANGECOMPLETE ((VL53L0_DeviceError) 11) +#define VL53L0_DEVICEERROR_ALGOUNDERFLOW ((VL53L0_DeviceError) 12) +#define VL53L0_DEVICEERROR_ALGOOVERFLOW ((VL53L0_DeviceError) 13) +#define VL53L0_DEVICEERROR_RANGEIGNORETHRESHOLD ((VL53L0_DeviceError) 14) + +/** @} end of VL53L0_DeviceError_group */ + + +/** @defgroup VL53L0_CheckEnable_group Check Enable list + * @brief Check Enable code + * + * Define used to specify the LimitCheckId. + * Use @a VL53L0_GetLimitCheckInfo() to get the string. + * @{ + */ + +#define VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE 0 +#define VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE 1 +#define VL53L0_CHECKENABLE_SIGNAL_REF_CLIP 2 +#define VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD 3 +#define VL53L0_CHECKENABLE_SIGNAL_RATE_MSRC 4 +#define VL53L0_CHECKENABLE_SIGNAL_RATE_PRE_RANGE 5 + +#define VL53L0_CHECKENABLE_NUMBER_OF_CHECKS 6 + +/** @} end of VL53L0_CheckEnable_group */ + + +/** @defgroup VL53L0_GpioFunctionality_group Gpio Functionality + * @brief Defines the different functionalities for the device GPIO(s) + * @{ + */ +typedef uint8_t VL53L0_GpioFunctionality; + +#define VL53L0_GPIOFUNCTIONALITY_OFF \ + ((VL53L0_GpioFunctionality) 0) /*!< NO Interrupt */ +#define VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW \ + ((VL53L0_GpioFunctionality) 1) /*!< Level Low (value < thresh_low) */ +#define VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH \ + ((VL53L0_GpioFunctionality) 2) /*!< Level High (value > thresh_high) */ +#define VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT \ + ((VL53L0_GpioFunctionality) 3) + /*!< Out Of Window (value < thresh_low OR value > thresh_high) */ +#define VL53L0_GPIOFUNCTIONALITY_NEW_MEASURE_READY \ + ((VL53L0_GpioFunctionality) 4) /*!< New Sample Ready */ + +/** @} end of VL53L0_GpioFunctionality_group */ + + +/* Device register map */ + +/** @defgroup VL53L0_DefineRegisters_group Define Registers + * @brief List of all the defined registers + * @{ + */ +#define VL53L0_REG_SYSRANGE_START 0x000 + /** mask existing bit in #VL53L0_REG_SYSRANGE_START*/ + #define VL53L0_REG_SYSRANGE_MODE_MASK 0x0F + /** bit 0 in #VL53L0_REG_SYSRANGE_START write 1 toggle state in + * continuous mode and arm next shot in single shot mode + */ + #define VL53L0_REG_SYSRANGE_MODE_START_STOP 0x01 + /** bit 1 write 0 in #VL53L0_REG_SYSRANGE_START set single shot mode */ + #define VL53L0_REG_SYSRANGE_MODE_SINGLESHOT 0x00 + /** bit 1 write 1 in #VL53L0_REG_SYSRANGE_START set back-to-back + * operation mode + */ + #define VL53L0_REG_SYSRANGE_MODE_BACKTOBACK 0x02 + /** bit 2 write 1 in #VL53L0_REG_SYSRANGE_START set timed operation + * mode + */ + #define VL53L0_REG_SYSRANGE_MODE_TIMED 0x04 + /** bit 3 write 1 in #VL53L0_REG_SYSRANGE_START set histogram operation + * mode + */ + #define VL53L0_REG_SYSRANGE_MODE_HISTOGRAM 0x08 + + +#define VL53L0_REG_SYSTEM_THRESH_HIGH 0x000C +#define VL53L0_REG_SYSTEM_THRESH_LOW 0x000E + + +#define VL53L0_REG_SYSTEM_SEQUENCE_CONFIG 0x0001 +#define VL53L0_REG_SYSTEM_RANGE_CONFIG 0x0009 +#define VL53L0_REG_SYSTEM_INTERMEASUREMENT_PERIOD 0x0004 + + +#define VL53L0_REG_SYSTEM_INTERRUPT_CONFIG_GPIO 0x000A + #define VL53L0_REG_SYSTEM_INTERRUPT_GPIO_DISABLED 0x00 + #define VL53L0_REG_SYSTEM_INTERRUPT_GPIO_LEVEL_LOW 0x01 + #define VL53L0_REG_SYSTEM_INTERRUPT_GPIO_LEVEL_HIGH 0x02 + #define VL53L0_REG_SYSTEM_INTERRUPT_GPIO_OUT_OF_WINDOW 0x03 + #define VL53L0_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY 0x04 + +#define VL53L0_REG_GPIO_HV_MUX_ACTIVE_HIGH 0x0084 + + +#define VL53L0_REG_SYSTEM_INTERRUPT_CLEAR 0x000B + +/* Result registers */ +#define VL53L0_REG_RESULT_INTERRUPT_STATUS 0x0013 +#define VL53L0_REG_RESULT_RANGE_STATUS 0x0014 + +#define VL53L0_REG_RESULT_CORE_PAGE 1 +#define VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN 0x00BC +#define VL53L0_REG_RESULT_CORE_RANGING_TOTAL_EVENTS_RTN 0x00C0 +#define VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF 0x00D0 +#define VL53L0_REG_RESULT_CORE_RANGING_TOTAL_EVENTS_REF 0x00D4 +#define VL53L0_REG_RESULT_PEAK_SIGNAL_RATE_REF 0x00B6 + +/* Algo register */ + +#define VL53L0_REG_ALGO_PART_TO_PART_RANGE_OFFSET_MM 0x0028 + +#define VL53L0_REG_I2C_SLAVE_DEVICE_ADDRESS 0x008a + +/* Check Limit registers */ +#define VL53L0_REG_MSRC_CONFIG_CONTROL 0x0060 + +#define VL53L0_REG_PRE_RANGE_CONFIG_MIN_SNR 0X0027 +#define VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_LOW 0x0056 +#define VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_HIGH 0x0057 +#define VL53L0_REG_PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT 0x0064 + +#define VL53L0_REG_FINAL_RANGE_CONFIG_MIN_SNR 0X0067 +#define VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_LOW 0x0047 +#define VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH 0x0048 +#define VL53L0_REG_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT 0x0044 + + +#define VL53L0_REG_PRE_RANGE_CONFIG_SIGMA_THRESH_HI 0X0061 +#define VL53L0_REG_PRE_RANGE_CONFIG_SIGMA_THRESH_LO 0X0062 + +/* PRE RANGE registers */ +#define VL53L0_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD 0x0050 +#define VL53L0_REG_PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI 0x0051 +#define VL53L0_REG_PRE_RANGE_CONFIG_TIMEOUT_MACROP_LO 0x0052 + +#define VL53L0_REG_SYSTEM_HISTOGRAM_BIN 0x0081 +#define VL53L0_REG_HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT 0x0033 +#define VL53L0_REG_HISTOGRAM_CONFIG_READOUT_CTRL 0x0055 + +#define VL53L0_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD 0x0070 +#define VL53L0_REG_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI 0x0071 +#define VL53L0_REG_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_LO 0x0072 +#define VL53L0_REG_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS 0x0020 + +#define VL53L0_REG_MSRC_CONFIG_TIMEOUT_MACROP 0x0046 + + +#define VL53L0_REG_SOFT_RESET_GO2_SOFT_RESET_N 0x00bf +#define VL53L0_REG_IDENTIFICATION_MODEL_ID 0x00c0 +#define VL53L0_REG_IDENTIFICATION_REVISION_ID 0x00c2 + +#define VL53L0_REG_OSC_CALIBRATE_VAL 0x00f8 + + +#define VL53L0_SIGMA_ESTIMATE_MAX_VALUE 65535 +/* equivalent to a range sigma of 655.35mm */ + +#define VL53L0_REG_GLOBAL_CONFIG_VCSEL_WIDTH 0x032 +#define VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_0 0x0B0 +#define VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_1 0x0B1 +#define VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_2 0x0B2 +#define VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_3 0x0B3 +#define VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_4 0x0B4 +#define VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_5 0x0B5 + +#define VL53L0_REG_GLOBAL_CONFIG_REF_EN_START_SELECT 0xB6 +#define VL53L0_REG_DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD 0x4E /* 0x14E */ +#define VL53L0_REG_DYNAMIC_SPAD_REF_EN_START_OFFSET 0x4F /* 0x14F */ +#define VL53L0_REG_POWER_MANAGEMENT_GO1_POWER_FORCE 0x80 + +/* + * Speed of light in um per 1E-10 Seconds + */ + +#define VL53L0_SPEED_OF_LIGHT_IN_AIR 2997 + +#define VL53L0_REG_VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV 0x0089 + +#define VL53L0_REG_ALGO_PHASECAL_LIM 0x0030 /* 0x130 */ +#define VL53L0_REG_ALGO_PHASECAL_CONFIG_TIMEOUT 0x0030 + +/** @} VL53L0_DefineRegisters_group */ + +/** @} VL53L0_DevSpecDefines_group */ + + +#endif + +/* _VL53L0_DEVICE_H_ */ + + diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_i2c_platform.h b/drivers/input/misc/vl53L0/inc/vl53l0_i2c_platform.h new file mode 100644 index 000000000000..9b1bdd7f3c54 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_i2c_platform.h @@ -0,0 +1,402 @@ +/* + * vl53l0_i2c_platform.h - Linux kernel modules for STM VL53L0 FlightSense TOF + * sensor + * + * Copyright (C) 2016 STMicroelectronics Imaging Division. + * + * 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. + */ + +/** + * @file VL53L0_i2c_platform.h + * @brief Function prototype definitions for EWOK Platform layer. + * + */ + + +#ifndef _VL53L0_I2C_PLATFORM_H_ +#define _VL53L0_I2C_PLATFORM_H_ + +#include "vl53l0_def.h" + + +/** Maximum buffer size to be used in i2c */ +#define VL53L0_MAX_I2C_XFER_SIZE 64 + +/** + * @brief Typedef defining .\n + * The developer should modify this to suit the platform being deployed. + * + */ + +/** + * @brief Typedef defining 8 bit unsigned char type.\n + * The developer should modify this to suit the platform being deployed. + * + */ + +#ifndef bool_t +typedef unsigned char bool_t; +#endif + + +#define I2C 0x01 +#define SPI 0x00 + +#define COMMS_BUFFER_SIZE 64 +/*MUST be the same size as the SV task buffer */ + +#define BYTES_PER_WORD 2 +#define BYTES_PER_DWORD 4 + +#define VL53L0_MAX_STRING_LENGTH_PLT 256 + +/** + * @brief Initialise platform comms. + * + * @param comms_type - selects between I2C and SPI + * @param comms_speed_khz - unsigned short containing the I2C speed in kHz + * + * @return status - status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_comms_initialise(uint8_t comms_type, + uint16_t comms_speed_khz); + +/** + * @brief Close platform comms. + * + * @return status - status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_comms_close(void); + +/** + * @brief Cycle Power to Device + * + * @return status - status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_cycle_power(void); + +int32_t VL53L0_set_page(VL53L0_DEV dev, uint8_t page_data); + +/** + * @brief Writes the supplied byte buffer to the device + * + * Wrapper for SystemVerilog Write Multi task + * + * @code + * + * Example: + * + * uint8_t *spad_enables; + * + * int status = VL53L0_write_multi(RET_SPAD_EN_0, spad_enables, 36); + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param pdata - pointer to uint8_t buffer containing the data to be written + * @param count - number of bytes in the supplied byte buffer + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_write_multi(VL53L0_DEV dev, uint8_t index, uint8_t *pdata, + int32_t count); + + +/** + * @brief Reads the requested number of bytes from the device + * + * Wrapper for SystemVerilog Read Multi task + * + * @code + * + * Example: + * + * uint8_t buffer[COMMS_BUFFER_SIZE]; + * + * int status = status = VL53L0_read_multi(DEVICE_ID, buffer, 2) + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param pdata - pointer to the uint8_t buffer to store read data + * @param count - number of uint8_t's to read + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_read_multi(VL53L0_DEV dev, uint8_t index, uint8_t *pdata, + int32_t count); + + +/** + * @brief Writes a single byte to the device + * + * Wrapper for SystemVerilog Write Byte task + * + * @code + * + * Example: + * + * uint8_t page_number = MAIN_SELECT_PAGE; + * + * int status = VL53L0_write_byte(PAGE_SELECT, page_number); + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param data - uint8_t data value to write + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_write_byte(VL53L0_DEV dev, uint8_t index, uint8_t data); + + +/** + * @brief Writes a single word (16-bit unsigned) to the device + * + * Manages the big-endian nature of the device (first byte written is the + * MS byte). + * Uses SystemVerilog Write Multi task. + * + * @code + * + * Example: + * + * uint16_t nvm_ctrl_pulse_width = 0x0004; + * + * int status = VL53L0_write_word(NVM_CTRL__PULSE_WIDTH_MSB, + * nvm_ctrl_pulse_width); + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param data - uin16_t data value write + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_write_word(VL53L0_DEV dev, uint8_t index, uint16_t data); + + +/** + * @brief Writes a single dword (32-bit unsigned) to the device + * + * Manages the big-endian nature of the device (first byte written is the + * MS byte). + * Uses SystemVerilog Write Multi task. + * + * @code + * + * Example: + * + * uint32_t nvm_data = 0x0004; + * + * int status = VL53L0_write_dword(NVM_CTRL__DATAIN_MMM, nvm_data); + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param data - uint32_t data value to write + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_write_dword(VL53L0_DEV dev, uint8_t index, uint32_t data); + + + +/** + * @brief Reads a single byte from the device + * + * Uses SystemVerilog Read Byte task. + * + * @code + * + * Example: + * + * uint8_t device_status = 0; + * + * int status = VL53L0_read_byte(STATUS, &device_status); + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param pdata - pointer to uint8_t data value + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_read_byte(VL53L0_DEV dev, uint8_t index, uint8_t *pdata); + + +/** + * @brief Reads a single word (16-bit unsigned) from the device + * + * Manages the big-endian nature of the device (first byte read is the MS byte). + * Uses SystemVerilog Read Multi task. + * + * @code + * + * Example: + * + * uint16_t timeout = 0; + * + * int status = VL53L0_read_word(TIMEOUT_OVERALL_PERIODS_MSB, &timeout); + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param pdata - pointer to uint16_t data value + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_read_word(VL53L0_DEV dev, uint8_t index, uint16_t *pdata); + + +/** + * @brief Reads a single dword (32-bit unsigned) from the device + * + * Manages the big-endian nature of the device (first byte read is the MS byte). + * Uses SystemVerilog Read Multi task. + * + * @code + * + * Example: + * + * uint32_t range_1 = 0; + * + * int status = VL53L0_read_dword(RANGE_1_MMM, &range_1); + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param pdata - pointer to uint32_t data value + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_read_dword(VL53L0_DEV dev, uint8_t index, uint32_t *pdata); + + +/** + * @brief Implements a programmable wait in us + * + * Wrapper for SystemVerilog Wait in micro seconds task + * + * @param wait_us - integer wait in micro seconds + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_platform_wait_us(int32_t wait_us); + + +/** + * @brief Implements a programmable wait in ms + * + * Wrapper for SystemVerilog Wait in milli seconds task + * + * @param wait_ms - integer wait in milli seconds + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_wait_ms(int32_t wait_ms); + + +/** + * @brief Set GPIO value + * + * @param level - input level - either 0 or 1 + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_set_gpio(uint8_t level); + + +/** + * @brief Get GPIO value + * + * @param plevel - uint8_t pointer to store GPIO level (0 or 1) + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_get_gpio(uint8_t *plevel); + +/** + * @brief Release force on GPIO + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_release_gpio(void); + + +/** +* @brief Get the frequency of the timer used for ranging results time stamps +* +* @param[out] ptimer_freq_hz : pointer for timer frequency +* +* @return status : 0 = ok, 1 = error +* +*/ + +int32_t VL53L0_get_timer_frequency(int32_t *ptimer_freq_hz); + +/** +* @brief Get the timer value in units of timer_freq_hz +* (see VL53L0_get_timestamp_frequency()) +* +* @param[out] ptimer_count : pointer for timer count value +* +* @return status : 0 = ok, 1 = error +* +*/ + +int32_t VL53L0_get_timer_value(int32_t *ptimer_count); +int VL53L0_I2CWrite(VL53L0_DEV dev, uint8_t *buff, uint8_t len); +int VL53L0_I2CRead(VL53L0_DEV dev, uint8_t *buff, uint8_t len); + +#endif /* _VL53L0_I2C_PLATFORM_H_ */ + diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_interrupt_threshold_settings.h b/drivers/input/misc/vl53L0/inc/vl53l0_interrupt_threshold_settings.h new file mode 100644 index 000000000000..35acd8eb42e3 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_interrupt_threshold_settings.h @@ -0,0 +1,194 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + + +#ifndef _VL53L0_INTERRUPT_THRESHOLD_SETTINGS_H_ +#define _VL53L0_INTERRUPT_THRESHOLD_SETTINGS_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + + +uint8_t InterruptThresholdSettings[] = { + + /* Start of Interrupt Threshold Settings */ + 0x1, 0xff, 0x00, + 0x1, 0x80, 0x01, + 0x1, 0xff, 0x01, + 0x1, 0x00, 0x00, + 0x1, 0xff, 0x01, + 0x1, 0x4f, 0x02, + 0x1, 0xFF, 0x0E, + 0x1, 0x00, 0x03, + 0x1, 0x01, 0x84, + 0x1, 0x02, 0x0A, + 0x1, 0x03, 0x03, + 0x1, 0x04, 0x08, + 0x1, 0x05, 0xC8, + 0x1, 0x06, 0x03, + 0x1, 0x07, 0x8D, + 0x1, 0x08, 0x08, + 0x1, 0x09, 0xC6, + 0x1, 0x0A, 0x01, + 0x1, 0x0B, 0x02, + 0x1, 0x0C, 0x00, + 0x1, 0x0D, 0xD5, + 0x1, 0x0E, 0x18, + 0x1, 0x0F, 0x12, + 0x1, 0x10, 0x01, + 0x1, 0x11, 0x82, + 0x1, 0x12, 0x00, + 0x1, 0x13, 0xD5, + 0x1, 0x14, 0x18, + 0x1, 0x15, 0x13, + 0x1, 0x16, 0x03, + 0x1, 0x17, 0x86, + 0x1, 0x18, 0x0A, + 0x1, 0x19, 0x09, + 0x1, 0x1A, 0x08, + 0x1, 0x1B, 0xC2, + 0x1, 0x1C, 0x03, + 0x1, 0x1D, 0x8F, + 0x1, 0x1E, 0x0A, + 0x1, 0x1F, 0x06, + 0x1, 0x20, 0x01, + 0x1, 0x21, 0x02, + 0x1, 0x22, 0x00, + 0x1, 0x23, 0xD5, + 0x1, 0x24, 0x18, + 0x1, 0x25, 0x22, + 0x1, 0x26, 0x01, + 0x1, 0x27, 0x82, + 0x1, 0x28, 0x00, + 0x1, 0x29, 0xD5, + 0x1, 0x2A, 0x18, + 0x1, 0x2B, 0x0B, + 0x1, 0x2C, 0x28, + 0x1, 0x2D, 0x78, + 0x1, 0x2E, 0x28, + 0x1, 0x2F, 0x91, + 0x1, 0x30, 0x00, + 0x1, 0x31, 0x0B, + 0x1, 0x32, 0x00, + 0x1, 0x33, 0x0B, + 0x1, 0x34, 0x00, + 0x1, 0x35, 0xA1, + 0x1, 0x36, 0x00, + 0x1, 0x37, 0xA0, + 0x1, 0x38, 0x00, + 0x1, 0x39, 0x04, + 0x1, 0x3A, 0x28, + 0x1, 0x3B, 0x30, + 0x1, 0x3C, 0x0C, + 0x1, 0x3D, 0x04, + 0x1, 0x3E, 0x0F, + 0x1, 0x3F, 0x79, + 0x1, 0x40, 0x28, + 0x1, 0x41, 0x1E, + 0x1, 0x42, 0x2F, + 0x1, 0x43, 0x87, + 0x1, 0x44, 0x00, + 0x1, 0x45, 0x0B, + 0x1, 0x46, 0x00, + 0x1, 0x47, 0x0B, + 0x1, 0x48, 0x00, + 0x1, 0x49, 0xA7, + 0x1, 0x4A, 0x00, + 0x1, 0x4B, 0xA6, + 0x1, 0x4C, 0x00, + 0x1, 0x4D, 0x04, + 0x1, 0x4E, 0x01, + 0x1, 0x4F, 0x00, + 0x1, 0x50, 0x00, + 0x1, 0x51, 0x80, + 0x1, 0x52, 0x09, + 0x1, 0x53, 0x08, + 0x1, 0x54, 0x01, + 0x1, 0x55, 0x00, + 0x1, 0x56, 0x0F, + 0x1, 0x57, 0x79, + 0x1, 0x58, 0x09, + 0x1, 0x59, 0x05, + 0x1, 0x5A, 0x00, + 0x1, 0x5B, 0x60, + 0x1, 0x5C, 0x05, + 0x1, 0x5D, 0xD1, + 0x1, 0x5E, 0x0C, + 0x1, 0x5F, 0x3C, + 0x1, 0x60, 0x00, + 0x1, 0x61, 0xD0, + 0x1, 0x62, 0x0B, + 0x1, 0x63, 0x03, + 0x1, 0x64, 0x28, + 0x1, 0x65, 0x10, + 0x1, 0x66, 0x2A, + 0x1, 0x67, 0x39, + 0x1, 0x68, 0x0B, + 0x1, 0x69, 0x02, + 0x1, 0x6A, 0x28, + 0x1, 0x6B, 0x10, + 0x1, 0x6C, 0x2A, + 0x1, 0x6D, 0x61, + 0x1, 0x6E, 0x0C, + 0x1, 0x6F, 0x00, + 0x1, 0x70, 0x0F, + 0x1, 0x71, 0x79, + 0x1, 0x72, 0x00, + 0x1, 0x73, 0x0B, + 0x1, 0x74, 0x00, + 0x1, 0x75, 0x0B, + 0x1, 0x76, 0x00, + 0x1, 0x77, 0xA1, + 0x1, 0x78, 0x00, + 0x1, 0x79, 0xA0, + 0x1, 0x7A, 0x00, + 0x1, 0x7B, 0x04, + 0x1, 0xFF, 0x04, + 0x1, 0x79, 0x1D, + 0x1, 0x7B, 0x27, + 0x1, 0x96, 0x0E, + 0x1, 0x97, 0xFE, + 0x1, 0x98, 0x03, + 0x1, 0x99, 0xEF, + 0x1, 0x9A, 0x02, + 0x1, 0x9B, 0x44, + 0x1, 0x73, 0x07, + 0x1, 0x70, 0x01, + 0x1, 0xff, 0x01, + 0x1, 0x00, 0x01, + 0x1, 0xff, 0x00, + 0x00, 0x00, 0x00 +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L0_INTERRUPT_THRESHOLD_SETTINGS_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_platform.h b/drivers/input/misc/vl53L0/inc/vl53l0_platform.h new file mode 100644 index 000000000000..f723a552a7f1 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_platform.h @@ -0,0 +1,231 @@ +/******************************************************************************* + * Copyright © 2015, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + + +#ifndef _VL53L0_PLATFORM_H_ +#define _VL53L0_PLATFORM_H_ + +#include <linux/delay.h> +#include "vl53l0_def.h" +#include "vl53l0_platform_log.h" + +#include "stmvl53l0-i2c.h" +#include "stmvl53l0-cci.h" +#include "stmvl53l0.h" + +/** + * @file vl53l0_platform.h + * + * @brief All end user OS/platform/application porting + */ + +/** + * @defgroup VL53L0_platform_group VL53L0 Platform Functions + * @brief VL53L0 Platform Functions + * @{ + */ + +/** + * @struct VL53L0_Dev_t + * @brief Generic PAL device type that does link between API and platform + * abstraction layer + * + */ +typedef struct stmvl53l0_data *VL53L0_DEV; + +/** + * @def PALDevDataGet + * @brief Get ST private structure @a VL53L0_DevData_t data access + * + * @param Dev Device Handle + * @param field ST structure field name + * It maybe used and as real data "ref" not just as "get" for sub-structure item + * like PALDevDataGet(FilterData.field)[i] or + * PALDevDataGet(FilterData.MeasurementIndex)++ + */ +#define PALDevDataGet(Dev, field) (Dev->Data.field) + +/** + * @def PALDevDataSet(Dev, field, data) + * @brief Set ST private structure @a VL53L0_DevData_t data field + * @param Dev Device Handle + * @param field ST structure field na*me + * @param data Data to be set + */ +#define PALDevDataSet(Dev, field, data) ((Dev->Data.field) = (data)) + + +/** + * @defgroup VL53L0_registerAccess_group PAL Register Access Functions + * @brief PAL Register Access Functions + * @{ + */ + +/** + * Lock comms interface to serialize all commands to a shared I2C interface + * for a specific device + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_LockSequenceAccess(VL53L0_DEV Dev); + +/** + * Unlock comms interface to serialize all commands to a shared I2C interface + * for a specific device + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_UnlockSequenceAccess(VL53L0_DEV Dev); + + +/** + * Writes the supplied byte buffer to the device + * @param Dev Device Handle + * @param index The register index + * @param pdata Pointer to uint8_t buffer containing the data to be written + * @param count Number of bytes in the supplied byte buffer + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_WriteMulti(VL53L0_DEV Dev, uint8_t index, + uint8_t *pdata, uint32_t count); + +/** + * Reads the requested number of bytes from the device + * @param Dev Device Handle + * @param index The register index + * @param pdata Pointer to the uint8_t buffer to store read data + * @param count Number of uint8_t's to read + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_ReadMulti(VL53L0_DEV Dev, uint8_t index, + uint8_t *pdata, uint32_t count); + +/** + * Write single byte register + * @param Dev Device Handle + * @param index The register index + * @param data 8 bit register data + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_WrByte(VL53L0_DEV Dev, uint8_t index, uint8_t data); + +/** + * Write word register + * @param Dev Device Handle + * @param index The register index + * @param data 16 bit register data + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_WrWord(VL53L0_DEV Dev, uint8_t index, uint16_t data); + +/** + * Write double word (4 byte) register + * @param Dev Device Handle + * @param index The register index + * @param data 32 bit register data + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_WrDWord(VL53L0_DEV Dev, uint8_t index, uint32_t data); + +/** + * Read single byte register + * @param Dev Device Handle + * @param index The register index + * @param data pointer to 8 bit data + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_RdByte(VL53L0_DEV Dev, uint8_t index, uint8_t *data); + +/** + * Read word (2byte) register + * @param Dev Device Handle + * @param index The register index + * @param data pointer to 16 bit data + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_RdWord(VL53L0_DEV Dev, uint8_t index, uint16_t *data); + +/** + * Read dword (4byte) register + * @param Dev Device Handle + * @param index The register index + * @param data pointer to 32 bit data + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_RdDWord(VL53L0_DEV Dev, uint8_t index, uint32_t *data); + +/** + * Threat safe Update (read/modify/write) single byte register + * + * Final_reg = (Initial_reg & and_data) |or_data + * + * @param Dev Device Handle + * @param index The register index + * @param AndData 8 bit and data + * @param OrData 8 bit or data + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_UpdateByte(VL53L0_DEV Dev, uint8_t index, + uint8_t AndData, uint8_t OrData); + +/** @} end of VL53L0_registerAccess_group */ + + +/** + * @brief execute delay in all polling API call + * + * A typical multi-thread or RTOs implementation is to sleep the task for + * some 5ms (with 100Hz max rate faster polling is not needed) + * if nothing specific is need you can define it as an empty/void macro + * @code + * #define VL53L0_PollingDelay(...) (void)0 + * @endcode + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_PollingDelay(VL53L0_DEV Dev); +/* usually best implemented as a real function */ + +/** @} end of VL53L0_platform_group */ + +#endif /* _VL53L0_PLATFORM_H_ */ + + + diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_platform_log.h b/drivers/input/misc/vl53L0/inc/vl53l0_platform_log.h new file mode 100644 index 000000000000..8c38615239ad --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_platform_log.h @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright © 2015, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + + +#ifndef _VL53L0_PLATFORM_LOG_H_ +#define _VL53L0_PLATFORM_LOG_H_ + +#include <linux/string.h> +/* LOG Functions */ + + +/** + * @file vl53l0_platform_log.h + * + * @brief platform log function definition + */ + +/* #define VL53L0_LOG_ENABLE */ + +enum { + TRACE_LEVEL_NONE, + TRACE_LEVEL_ERRORS, + TRACE_LEVEL_WARNING, + TRACE_LEVEL_INFO, + TRACE_LEVEL_DEBUG, + TRACE_LEVEL_ALL, + TRACE_LEVEL_IGNORE +}; + +enum { + TRACE_FUNCTION_NONE = 0, + TRACE_FUNCTION_I2C = 1, + TRACE_FUNCTION_ALL = 0x7fffffff /* all bits except sign */ +}; + +enum { + TRACE_MODULE_NONE = 0x0, + TRACE_MODULE_API = 0x1, + TRACE_MODULE_PLATFORM = 0x2, + TRACE_MODULE_ALL = 0x7fffffff /* all bits except sign */ +}; + + +#ifdef VL53L0_LOG_ENABLE + +#include <linux/module.h> + + +extern uint32_t _trace_level; + + + +int32_t VL53L0_trace_config(char *filename, uint32_t modules, + uint32_t level, uint32_t functions); + +#if 0 +void trace_print_module_function(uint32_t module, uint32_t level, + uint32_t function, const char *format, ...); +#else +#define trace_print_module_function(...) +#endif + +#define LOG_GET_TIME() (int)0 +/* + * #define _LOG_FUNCTION_START(module, fmt, ...) \ + printk(KERN_INFO"beg %s start @%d\t" fmt "\n", \ + __func__, LOG_GET_TIME(), ##__VA_ARGS__) + + * #define _LOG_FUNCTION_END(module, status, ...)\ + printk(KERN_INFO"end %s @%d %d\n", \ + __func__, LOG_GET_TIME(), (int)status) + + * #define _LOG_FUNCTION_END_FMT(module, status, fmt, ...)\ + printk(KERN_INFO"End %s @%d %d\t"fmt"\n" , \ + __func__, LOG_GET_TIME(), (int)status, ##__VA_ARGS__) +*/ +#define _LOG_FUNCTION_START(module, fmt, ...) \ + pr_err("beg %s start @%d\t" fmt "\n", \ + __func__, LOG_GET_TIME(), ##__VA_ARGS__) + +#define _LOG_FUNCTION_END(module, status, ...)\ + pr_err("end %s start @%d Status %d\n", \ + __func__, LOG_GET_TIME(), (int)status) + +#define _LOG_FUNCTION_END_FMT(module, status, fmt, ...)\ + pr_err("End %s @%d %d\t"fmt"\n", \ + __func__, LOG_GET_TIME(), (int)status, ##__VA_ARGS__) + + +#else /* VL53L0_LOG_ENABLE no logging */ + #define VL53L0_ErrLog(...) (void)0 + #define _LOG_FUNCTION_START(module, fmt, ...) (void)0 + #define _LOG_FUNCTION_END(module, status, ...) (void)0 + #define _LOG_FUNCTION_END_FMT(module, status, fmt, ...) (void)0 +#endif /* else */ + +#define VL53L0_COPYSTRING(str, ...) strlcpy(str, ##__VA_ARGS__, sizeof(str)) + + +#endif /* _VL53L0_PLATFORM_LOG_H_ */ + + + diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_tuning.h b/drivers/input/misc/vl53L0/inc/vl53l0_tuning.h new file mode 100644 index 000000000000..a9f7ca70b5ac --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_tuning.h @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + + +#ifndef _VL53L0_TUNING_H_ +#define _VL53L0_TUNING_H_ + +#include "vl53l0_def.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +uint8_t DefaultTuningSettings[] = { + + /* update 02/11/2015_v36 */ + 0x01, 0xFF, 0x01, + 0x01, 0x00, 0x00, + + 0x01, 0xFF, 0x00, + 0x01, 0x09, 0x00, + 0x01, 0x10, 0x00, + 0x01, 0x11, 0x00, + + 0x01, 0x24, 0x01, + 0x01, 0x25, 0xff, + 0x01, 0x75, 0x00, + + 0x01, 0xFF, 0x01, + 0x01, 0x4e, 0x2c, + 0x01, 0x48, 0x00, + 0x01, 0x30, 0x20, + + 0x01, 0xFF, 0x00, + 0x01, 0x30, 0x09, /* mja changed from 0x64. */ + 0x01, 0x54, 0x00, + 0x01, 0x31, 0x04, + 0x01, 0x32, 0x03, + 0x01, 0x40, 0x83, + 0x01, 0x46, 0x25, + 0x01, 0x60, 0x00, + 0x01, 0x27, 0x00, + 0x01, 0x50, 0x06, + 0x01, 0x51, 0x00, + 0x01, 0x52, 0x96, + 0x01, 0x56, 0x08, + 0x01, 0x57, 0x30, + 0x01, 0x61, 0x00, + 0x01, 0x62, 0x00, + 0x01, 0x64, 0x00, + 0x01, 0x65, 0x00, + 0x01, 0x66, 0xa0, + + 0x01, 0xFF, 0x01, + 0x01, 0x22, 0x32, + 0x01, 0x47, 0x14, + 0x01, 0x49, 0xff, + 0x01, 0x4a, 0x00, + + 0x01, 0xFF, 0x00, + 0x01, 0x7a, 0x0a, + 0x01, 0x7b, 0x00, + 0x01, 0x78, 0x21, + + 0x01, 0xFF, 0x01, + 0x01, 0x23, 0x34, + 0x01, 0x42, 0x00, + 0x01, 0x44, 0xff, + 0x01, 0x45, 0x26, + 0x01, 0x46, 0x05, + 0x01, 0x40, 0x40, + 0x01, 0x0E, 0x06, + 0x01, 0x20, 0x1a, + 0x01, 0x43, 0x40, + + 0x01, 0xFF, 0x00, + 0x01, 0x34, 0x03, + 0x01, 0x35, 0x44, + + 0x01, 0xFF, 0x01, + 0x01, 0x31, 0x04, + 0x01, 0x4b, 0x09, + 0x01, 0x4c, 0x05, + 0x01, 0x4d, 0x04, + + + 0x01, 0xFF, 0x00, + 0x01, 0x44, 0x00, + 0x01, 0x45, 0x20, + 0x01, 0x47, 0x08, + 0x01, 0x48, 0x28, + 0x01, 0x67, 0x00, + 0x01, 0x70, 0x04, + 0x01, 0x71, 0x01, + 0x01, 0x72, 0xfe, + 0x01, 0x76, 0x00, + 0x01, 0x77, 0x00, + + 0x01, 0xFF, 0x01, + 0x01, 0x0d, 0x01, + + 0x01, 0xFF, 0x00, + 0x01, 0x80, 0x01, + 0x01, 0x01, 0xF8, + + 0x01, 0xFF, 0x01, + 0x01, 0x8e, 0x01, + 0x01, 0x00, 0x01, + 0x01, 0xFF, 0x00, + 0x01, 0x80, 0x00, + + 0x00, 0x00, 0x00 +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L0_TUNING_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_types.h b/drivers/input/misc/vl53L0/inc/vl53l0_types.h new file mode 100644 index 000000000000..c509913146ed --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_types.h @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +#ifndef VL53L0_TYPES_H_ +#define VL53L0_TYPES_H_ + +#include <linux/types.h> + +#ifndef NULL +#error "TODO review NULL definition or add required include " +#define NULL 0 +#endif +/** use where fractional values are expected + * + * Given a floating point value f it's .16 bit point is (int)(f*(1<<16)) + */ +typedef unsigned int FixPoint1616_t; + +#if !defined(STDINT_H) && !defined(_GCC_STDINT_H) \ + && !defined(_STDINT_H) && !defined(_LINUX_TYPES_H) + +#pragma message("Please review type definition of STDINT define for your" \ +"platform and add to list above ") + +/* +* target platform do not provide stdint or use a different #define than above +* to avoid seeing the message below addapt the #define list above or implement +* all type and delete these pragma +*/ + +typedef unsigned int uint32_t; +typedef int int32_t; + +typedef unsigned short uint16_t; +typedef short int16_t; + +typedef unsigned char uint8_t; + +typedef signed char int8_t; + + +#endif /* VL53L0_TYPES_H_ */ + +#endif /* VL6180x_TYPES_H_ */ diff --git a/drivers/input/misc/vl53L0/src/vl53l010_api.c b/drivers/input/misc/vl53L0/src/vl53l010_api.c new file mode 100644 index 000000000000..6c706bd0fe3b --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l010_api.c @@ -0,0 +1,4175 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include "vl53l010_api.h" +#include "vl53l010_device.h" +#include "vl53l010_tuning.h" + +/* use macro for abs */ +#ifndef __KERNEL__ +#include <stdlib.h> +#endif + + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__) + +#ifdef VL53L0_LOG_ENABLE +#define trace_print(level, ...) trace_print_module_function(TRACE_MODULE_API, \ + level, TRACE_FUNCTION_NONE, ##__VA_ARGS__) +#endif + +/* Defines */ +#define VL53L010_SETPARAMETERFIELD(Dev, field, value) \ + do { \ + if (Status == VL53L0_ERROR_NONE) {\ + CurrentParameters = \ + PALDevDataGet(Dev, CurrentParameters); \ + CurrentParameters.field = value; \ + CurrentParameters = \ + PALDevDataSet(Dev, CurrentParameters, \ + CurrentParameters); \ + } \ + } while (0) +#define VL53L010_SETARRAYPARAMETERFIELD(Dev, field, index, value) \ + do { \ + if (Status == VL53L0_ERROR_NONE) {\ + CurrentParameters = \ + PALDevDataGet(Dev, CurrentParameters); \ + CurrentParameters.field[index] = value; \ + CurrentParameters = \ + PALDevDataSet(Dev, CurrentParameters, \ + CurrentParameters); \ + } \ + } while (0) + +#define VL53L010_GETPARAMETERFIELD(Dev, field, variable) \ + do { \ + if (Status == VL53L0_ERROR_NONE) { \ + CurrentParameters = \ + PALDevDataGet(Dev, CurrentParameters); \ + variable = CurrentParameters.field; \ + } \ + } while (0) + +#define VL53L010_GETARRAYPARAMETERFIELD(Dev, field, index, variable) \ + do { \ + if (Status == VL53L0_ERROR_NONE) { \ + CurrentParameters = \ + PALDevDataGet(Dev, CurrentParameters); \ + variable = CurrentParameters.field[index]; \ + } \ + } while (0) + +#define VL53L010_SETDEVICESPECIFICPARAMETER(Dev, field, value) \ + do { \ + if (Status == VL53L0_ERROR_NONE) { \ + DeviceSpecificParameters = \ + PALDevDataGet(Dev, DeviceSpecificParameters); \ + DeviceSpecificParameters.field = value; \ + DeviceSpecificParameters = \ + PALDevDataSet(Dev, DeviceSpecificParameters, \ + DeviceSpecificParameters); \ + } \ + } while (0) + +#define VL53L010_GETDEVICESPECIFICPARAMETER(Dev, field) \ + PALDevDataGet(Dev, DeviceSpecificParameters).field + +#define VL53L010_FIXPOINT1616TOFIXPOINT97(Value) \ + (uint16_t)((Value >> 9) & 0xFFFF) +#define VL53L010_FIXPOINT97TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value << 9) +#define VL53L010_FIXPOINT1616TOFIXPOINT412(Value) \ + (uint16_t)((Value >> 4) & 0xFFFF) +#define VL53L010_FIXPOINT412TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value << 4) +#define VL53L010_FIXPOINT1616TOFIXPOINT08(Value) \ + (uint8_t)((Value >> 8) & 0x00FF) +#define VL53L010_FIXPOINT08TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value << 8) +#define VL53L010_MAKEUINT16(lsb, msb) \ + (uint16_t)((((uint16_t)msb) << 8) + (uint16_t)lsb) + + + +/* Group PAL General Functions */ +VL53L0_Error VL53L010_GetVersion(VL53L0_Version_t *pVersion) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + pVersion->major = VL53L010_IMPLEMENTATION_VER_MAJOR; + pVersion->minor = VL53L010_IMPLEMENTATION_VER_MINOR; + pVersion->build = VL53L010_IMPLEMENTATION_VER_SUB; + + pVersion->revision = VL53L0_IMPLEMENTATION_VER_REVISION; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetPalSpecVersion(VL53L0_Version_t *pPalSpecVersion) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + pPalSpecVersion->major = VL53L010_SPECIFICATION_VER_MAJOR; + pPalSpecVersion->minor = VL53L010_SPECIFICATION_VER_MINOR; + pPalSpecVersion->build = VL53L010_SPECIFICATION_VER_SUB; + + pPalSpecVersion->revision = VL53L010_SPECIFICATION_VER_REVISION; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetDeviceInfo(VL53L0_DEV Dev, + VL53L0_DeviceInfo_t *pVL53L0_DeviceInfo) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t model_id; + uint8_t Revision; + + LOG_FUNCTION_START(""); + + Status = VL53L010_check_part_used(Dev, &Revision, pVL53L0_DeviceInfo); + + if (Status == VL53L0_ERROR_NONE) { + if (Revision == 0) { + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->Name, + VL53L010_STRING_DEVICE_INFO_NAME_TS0); + } else if ((Revision <= 34) && (Revision != 32)) { + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->Name, + VL53L010_STRING_DEVICE_INFO_NAME_TS1); + } else if (Revision < 39) { + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->Name, + VL53L010_STRING_DEVICE_INFO_NAME_TS2); + } else { + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->Name, + VL53L010_STRING_DEVICE_INFO_NAME_ES1); + } + + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->Type, + VL53L010_STRING_DEVICE_INFO_TYPE); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = + VL53L0_RdByte(Dev, VL53L010_REG_IDENTIFICATION_MODEL_ID, + &pVL53L0_DeviceInfo->ProductType); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = + VL53L0_RdByte(Dev, VL53L010_REG_IDENTIFICATION_REVISION_ID, + &model_id); + pVL53L0_DeviceInfo->ProductRevisionMajor = 1; + pVL53L0_DeviceInfo->ProductRevisionMinor = + (model_id & 0xF0) >> 4; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetDeviceErrorStatus(VL53L0_DEV Dev, + VL53L010_DeviceError * + pDeviceErrorStatus) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t RangeStatus; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L010_REG_RESULT_RANGE_STATUS, + &RangeStatus); + + *pDeviceErrorStatus = (VL53L0_DeviceError) ((RangeStatus & 0x78) >> 3); + + LOG_FUNCTION_END(Status); + return Status; +} + +#define VL53L010_BUILDSTATUSERRORSTRING(BUFFER, ERRORCODE, STRINGVALUE) do {\ + case ERRORCODE: \ + VL53L0_COPYSTRING(BUFFER, STRINGVALUE);\ + break;\ + } while (0) + +VL53L0_Error VL53L010_GetDeviceErrorString(VL53L0_DeviceError ErrorCode, + char *pDeviceErrorString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (ErrorCode) { + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_NONE, + VL53L010_STRING_DEVICEERROR_NONE); + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_VCSELCONTINUITYTESTFAILURE, + VL53L010_STRING_DEVICEERROR_VCSELCONTINUITYTESTFAILURE); + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_VCSELWATCHDOGTESTFAILURE, + VL53L010_STRING_DEVICEERROR_VCSELWATCHDOGTESTFAILURE); + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_NOVHVVALUEFOUND, + VL53L010_STRING_DEVICEERROR_NOVHVVALUEFOUND); + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_MSRCNOTARGET, + VL53L010_STRING_DEVICEERROR_MSRCNOTARGET); + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_MSRCMINIMUMSNR, + VL53L010_STRING_DEVICEERROR_MSRCMINIMUMSNR); + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_MSRCWRAPAROUND, + VL53L010_STRING_DEVICEERROR_MSRCWRAPAROUND); + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_TCC, + VL53L010_STRING_DEVICEERROR_TCC); + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_RANGEAWRAPAROUND, + VL53L010_STRING_DEVICEERROR_RANGEAWRAPAROUND); + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_RANGEBWRAPAROUND, + VL53L010_STRING_DEVICEERROR_RANGEBWRAPAROUND); + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_MINCLIP, + VL53L010_STRING_DEVICEERROR_MINCLIP); + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_RANGECOMPLETE, + VL53L010_STRING_DEVICEERROR_RANGECOMPLETE); + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_ALGOUNDERFLOW, + VL53L010_STRING_DEVICEERROR_ALGOUNDERFLOW); + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_ALGOOVERFLOW, + VL53L010_STRING_DEVICEERROR_ALGOOVERFLOW); + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_FINALSNRLIMIT, + VL53L010_STRING_DEVICEERROR_FINALSNRLIMIT); + VL53L010_BUILDSTATUSERRORSTRING(pDeviceErrorString, + VL53L010_DEVICEERROR_NOTARGETIGNORE, + VL53L010_STRING_DEVICEERROR_NOTARGETIGNORE); + default: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L010_STRING_UNKNOW_ERROR_CODE); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetPalErrorString(VL53L0_Error PalErrorCode, + char *pPalErrorString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (PalErrorCode) { + VL53L010_BUILDSTATUSERRORSTRING(pPalErrorString, + VL53L0_ERROR_NONE, + VL53L010_STRING_ERROR_NONE); + VL53L010_BUILDSTATUSERRORSTRING(pPalErrorString, + VL53L0_ERROR_CALIBRATION_WARNING, + VL53L010_STRING_ERROR_CALIBRATION_WARNING); + VL53L010_BUILDSTATUSERRORSTRING(pPalErrorString, + VL53L0_ERROR_MIN_CLIPPED, + VL53L010_STRING_ERROR_MIN_CLIPPED); + VL53L010_BUILDSTATUSERRORSTRING(pPalErrorString, + VL53L0_ERROR_UNDEFINED, + VL53L010_STRING_ERROR_UNDEFINED); + VL53L010_BUILDSTATUSERRORSTRING(pPalErrorString, + VL53L0_ERROR_INVALID_PARAMS, + VL53L010_STRING_ERROR_INVALID_PARAMS); + VL53L010_BUILDSTATUSERRORSTRING(pPalErrorString, + VL53L0_ERROR_NOT_SUPPORTED, + VL53L010_STRING_ERROR_NOT_SUPPORTED); + VL53L010_BUILDSTATUSERRORSTRING(pPalErrorString, + VL53L0_ERROR_RANGE_ERROR, + VL53L010_STRING_ERROR_RANGE_ERROR); + VL53L010_BUILDSTATUSERRORSTRING(pPalErrorString, + VL53L0_ERROR_TIME_OUT, + VL53L010_STRING_ERROR_TIME_OUT); + VL53L010_BUILDSTATUSERRORSTRING(pPalErrorString, + VL53L0_ERROR_MODE_NOT_SUPPORTED, + VL53L010_STRING_ERROR_MODE_NOT_SUPPORTED); + VL53L010_BUILDSTATUSERRORSTRING(pPalErrorString, + VL53L0_ERROR_NOT_IMPLEMENTED, + VL53L010_STRING_ERROR_NOT_IMPLEMENTED); + VL53L010_BUILDSTATUSERRORSTRING(pPalErrorString, + VL53L0_ERROR_BUFFER_TOO_SMALL, + VL53L010_STRING_ERROR_BUFFER_TOO_SMALL); + VL53L010_BUILDSTATUSERRORSTRING(pPalErrorString, + VL53L0_ERROR_GPIO_NOT_EXISTING, + VL53L010_STRING_ERROR_GPIO_NOT_EXISTING); + VL53L010_BUILDSTATUSERRORSTRING(pPalErrorString, + VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED, + VL53L010_STRING_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED); + VL53L010_BUILDSTATUSERRORSTRING(pPalErrorString, + VL53L0_ERROR_CONTROL_INTERFACE, + VL53L010_STRING_ERROR_CONTROL_INTERFACE); + default: + VL53L0_COPYSTRING(pPalErrorString, + VL53L010_STRING_UNKNOW_ERROR_CODE); + break; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetPalState(VL53L0_DEV Dev, VL53L0_State *pPalState) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pPalState = PALDevDataGet(Dev, PalState); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_SetPowerMode(VL53L0_DEV Dev, VL53L0_PowerModes PowerMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + /* Only level1 of Power mode exists */ + if ((PowerMode != VL53L0_POWERMODE_STANDBY_LEVEL1) && + (PowerMode != VL53L0_POWERMODE_IDLE_LEVEL1)) { + Status = VL53L0_ERROR_MODE_NOT_SUPPORTED; + } else if (PowerMode == VL53L0_POWERMODE_STANDBY_LEVEL1) { + /* set the standby level1 of power mode */ + Status = VL53L0_WrByte(Dev, 0x80, 0x00); + if (Status == VL53L0_ERROR_NONE) { + /* Set PAL State to standby */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_STANDBY); + PALDevDataSet(Dev, PowerMode, + VL53L0_POWERMODE_STANDBY_LEVEL1); + } + + } else { + /* VL53L0_POWERMODE_IDLE_LEVEL1 */ + Status = VL53L0_WrByte(Dev, 0x80, 0x01); + if (Status == VL53L0_ERROR_NONE) + Status = VL53L010_StaticInit(Dev); + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, PowerMode, + VL53L0_POWERMODE_IDLE_LEVEL1); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetPowerMode(VL53L0_DEV Dev, + VL53L0_PowerModes *pPowerMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + /* Only level1 of Power mode exists */ + Status = VL53L0_RdByte(Dev, 0x80, &Byte); + + if (Status == VL53L0_ERROR_NONE) { + if (Byte == 1) + PALDevDataSet(Dev, PowerMode, + VL53L0_POWERMODE_IDLE_LEVEL1); + else + PALDevDataSet(Dev, PowerMode, + VL53L0_POWERMODE_STANDBY_LEVEL1); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_SetOffsetCalibrationDataMicroMeter(VL53L0_DEV Dev, + int32_t + OffsetCalibrationDataMicroMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t OffsetCalibrationData; + + LOG_FUNCTION_START(""); + + OffsetCalibrationData = (uint8_t) (OffsetCalibrationDataMicroMeter + / 1000); + Status = VL53L0_WrByte(Dev, VL53L010_REG_ALGO_PART_TO_PART_RANGE_OFFSET, + *(uint8_t *) &OffsetCalibrationData); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetOffsetCalibrationDataMicroMeter(VL53L0_DEV Dev, + int32_t * + pOffsetCalibrationDataMicroMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t RangeOffsetRegister; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L010_REG_ALGO_PART_TO_PART_RANGE_OFFSET, + &RangeOffsetRegister); + if (Status == VL53L0_ERROR_NONE) { + *pOffsetCalibrationDataMicroMeter = + (*((int8_t *) (&RangeOffsetRegister))) * 1000; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_SetGroupParamHold(VL53L0_DEV Dev, uint8_t GroupParamHold) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + /* not implemented on VL53L0 */ + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetUpperLimitMilliMeter(VL53L0_DEV Dev, + uint16_t *pUpperLimitMilliMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + /* not implemented on VL53L0 */ + + LOG_FUNCTION_END(Status); + return Status; +} + +/* End Group PAL General Functions */ + +/* Group PAL Init Functions */ +VL53L0_Error VL53L010_SetDeviceAddress(VL53L0_DEV Dev, uint8_t DeviceAddress) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, VL53L010_REG_I2C_SLAVE_DEVICE_ADDRESS, + DeviceAddress / 2); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_DataInit(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + VL53L0_DeviceSpecificParameters_t DeviceSpecificParameters; + int32_t OffsetCalibrationData; + + LOG_FUNCTION_START(""); + + if (Status == VL53L0_ERROR_NONE) { + /* read device info */ + VL53L010_SETDEVICESPECIFICPARAMETER(Dev, ReadDataFromDeviceDone, + 0); + + Status = VL53L010_get_info_from_device(Dev); + } + + /* Set Default static parameters */ + /* set first temporary values 11.3999MHz * 65536 = 748421 */ + VL53L010_SETDEVICESPECIFICPARAMETER(Dev, OscFrequencyMHz, 748421); + /* 11.3999MHz * 65536 = 748421 */ + + /* Get default parameters */ + Status = VL53L010_GetDeviceParameters(Dev, &CurrentParameters); + if (Status == VL53L0_ERROR_NONE) { + /* initialize PAL values */ + CurrentParameters.DeviceMode = VL53L0_DEVICEMODE_SINGLE_RANGING; + CurrentParameters.HistogramMode = VL53L0_HISTOGRAMMODE_DISABLED; + PALDevDataSet(Dev, CurrentParameters, CurrentParameters); + } + + /* Sigma estimator variable */ + PALDevDataSet(Dev, SigmaEstRefArray, 100); + PALDevDataSet(Dev, SigmaEstEffPulseWidth, 900); + PALDevDataSet(Dev, SigmaEstEffAmbWidth, 500); + + /* Set Signal and Sigma check */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_SetLimitCheckEnable(Dev, + VL53L010_CHECKENABLE_SIGMA_FINAL_RANGE, + 0); + } + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_SetLimitCheckEnable(Dev, + VL53L010_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + 0); + } + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_SetLimitCheckValue(Dev, + VL53L010_CHECKENABLE_SIGMA_FINAL_RANGE, + (FixPoint1616_t) (32 << + 16)); + } + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_SetLimitCheckValue(Dev, + VL53L010_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + (FixPoint1616_t) (25 * 65536 / 100)); + /* 0.25 * 65538 */ + } + + /* Read back NVM offset */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_GetOffsetCalibrationDataMicroMeter(Dev, + &OffsetCalibrationData); + } + + if (Status == VL53L0_ERROR_NONE) { + PALDevDataSet(Dev, Part2PartOffsetNVMMicroMeter, + OffsetCalibrationData); + + PALDevDataSet(Dev, SequenceConfig, 0xFF); + + /* Set PAL state to tell that we are waiting for call + * to VL53L010_StaticInit + */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_WAIT_STATICINIT); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_StaticInit(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceSpecificParameters_t DeviceSpecificParameters; + VL53L0_DeviceParameters_t CurrentParameters; + uint16_t TempWord; + uint8_t TempByte; + uint8_t localBuffer[32]; + uint8_t i; + uint8_t Revision; + + LOG_FUNCTION_START(""); + + /* Set I2C standard mode */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, 0x88, 0x00); + + /* this function do nothing if it has been called before */ + Status = VL53L010_get_info_from_device(Dev); + + if (Status == VL53L0_ERROR_NONE) + Revision = VL53L010_GETDEVICESPECIFICPARAMETER(Dev, Revision); + + if (Status == VL53L0_ERROR_NONE) { + if (Revision == 0) + Status = VL53L010_load_additional_settings1(Dev); + } + + /* update13_05_15 */ + if (Status == VL53L0_ERROR_NONE) { + if ((Revision <= 34) && (Revision != 32)) { + + for (i = 0; i < 32; i++) + localBuffer[i] = 0xff; + + Status = VL53L0_WriteMulti(Dev, 0x90, localBuffer, 32); + + Status |= VL53L0_WrByte(Dev, 0xb6, 16); + Status |= VL53L0_WrByte(Dev, 0xb0, 0x0); + Status |= VL53L0_WrByte(Dev, 0xb1, 0x0); + Status |= VL53L0_WrByte(Dev, 0xb2, 0xE0); + Status |= VL53L0_WrByte(Dev, 0xb3, 0xE0); + Status |= VL53L0_WrByte(Dev, 0xb4, 0xE0); + Status |= VL53L0_WrByte(Dev, 0xb5, 0xE0); + } + } + + /* update 17_06_15_v10 */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L010_load_tuning_settings(Dev); + + /* check if GO1 power is ON after load default tuning */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdByte(Dev, 0x80, &TempByte); + if ((TempByte != 0) && (Status == VL53L0_ERROR_NONE)) { + /* update 07_05_15 */ + Status = VL53L010_load_additional_settings3(Dev); + } + } + + /* Set interrupt config to new sample ready */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_SetGpioConfig(Dev, 0, 0, + VL53L010_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY, + VL53L0_INTERRUPTPOLARITY_LOW); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_RdWord(Dev, 0x84, &TempWord); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L010_SETDEVICESPECIFICPARAMETER(Dev, OscFrequencyMHz, + VL53L010_FIXPOINT412TOFIXPOINT1616 + (TempWord)); + } + + /* After static init, some device parameters may be changed, + * so update them + */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L010_GetDeviceParameters(Dev, &CurrentParameters); + + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, CurrentParameters, CurrentParameters); + + /* read the sequence config and save it */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdByte(Dev, VL53L010_REG_SYSTEM_SEQUENCE_CONFIG, + &TempByte); + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, SequenceConfig, TempByte); + + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L010_PerformRefCalibration(Dev); + + /* Set PAL State to standby */ + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, PalState, VL53L0_STATE_IDLE); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_WaitDeviceBooted(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + /* not implemented on VL53L0 */ + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_ResetDevice(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + /* Set reset bit */ + Status = VL53L0_WrByte(Dev, VL53L010_REG_SOFT_RESET_GO2_SOFT_RESET_N, + 0x00); + + /* Wait for some time */ + if (Status == VL53L0_ERROR_NONE) { + do { + Status = VL53L0_RdByte(Dev, + VL53L010_REG_IDENTIFICATION_MODEL_ID, + &Byte); + } while (Byte != 0x00); + } + + /* Release reset */ + Status = VL53L0_WrByte(Dev, VL53L010_REG_SOFT_RESET_GO2_SOFT_RESET_N, + 0x01); + + /* Wait until correct boot-up of the device */ + if (Status == VL53L0_ERROR_NONE) { + do { + Status = VL53L0_RdByte(Dev, + VL53L010_REG_IDENTIFICATION_MODEL_ID, + &Byte); + } while (Byte == 0x00); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +/* End Group PAL Init Functions */ + +/* Group PAL Parameters Functions */ +VL53L0_Error VL53L010_SetDeviceParameters(VL53L0_DEV Dev, + const VL53L0_DeviceParameters_t * + pDeviceParameters) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int i; + + LOG_FUNCTION_START(""); + + Status = VL53L010_SetDeviceMode(Dev, pDeviceParameters->DeviceMode); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_SetHistogramMode(Dev, + pDeviceParameters-> + HistogramMode); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_SetInterMeasurementPeriodMilliSeconds(Dev, + pDeviceParameters->InterMeasurementPeriodMilliSeconds); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_SetXTalkCompensationEnable(Dev, + pDeviceParameters-> + XTalkCompensationEnable); + } + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_SetXTalkCompensationRateMegaCps(Dev, + pDeviceParameters-> + XTalkCompensationRateMegaCps); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_SetOffsetCalibrationDataMicroMeter(Dev, + pDeviceParameters-> + RangeOffsetMicroMeters); + } + + for (i = 0; i < VL53L010_CHECKENABLE_NUMBER_OF_CHECKS; i++) { + if (Status == VL53L0_ERROR_NONE) { + Status |= VL53L010_SetLimitCheckEnable(Dev, i, + pDeviceParameters-> + LimitChecksEnable + [i]); + } else { + break; + } + if (Status == VL53L0_ERROR_NONE) { + Status |= VL53L010_SetLimitCheckValue(Dev, i, + pDeviceParameters-> + LimitChecksValue + [i]); + } else { + break; + } + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_SetWrapAroundCheckEnable(Dev, + pDeviceParameters-> + WrapAroundCheckEnable); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_SetMeasurementTimingBudgetMicroSeconds(Dev, + pDeviceParameters->MeasurementTimingBudgetMicroSeconds); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetDeviceParameters(VL53L0_DEV Dev, + VL53L0_DeviceParameters_t * + pDeviceParameters) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int i; + + LOG_FUNCTION_START(""); + + Status = VL53L010_GetDeviceMode(Dev, &(pDeviceParameters->DeviceMode)); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_GetHistogramMode(Dev, + &(pDeviceParameters-> + HistogramMode)); + } + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_GetInterMeasurementPeriodMilliSeconds(Dev, + &(pDeviceParameters->InterMeasurementPeriodMilliSeconds)); + } + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_GetXTalkCompensationEnable(Dev, + & + (pDeviceParameters-> + XTalkCompensationEnable)); + } + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_GetXTalkCompensationRateMegaCps(Dev, + &(pDeviceParameters->XTalkCompensationRateMegaCps)); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_GetOffsetCalibrationDataMicroMeter(Dev, + & + (pDeviceParameters-> + RangeOffsetMicroMeters)); + } + + if (Status == VL53L0_ERROR_NONE) { + for (i = 0; i < VL53L010_CHECKENABLE_NUMBER_OF_CHECKS; i++) { + /* get first the values, then the enables. + *VL53L0_GetLimitCheckValue will modify the enable flags + */ + if (Status == VL53L0_ERROR_NONE) { + Status |= VL53L010_GetLimitCheckValue(Dev, i, + & + (pDeviceParameters-> + LimitChecksValue + [i])); + } else { + break; + } + if (Status == VL53L0_ERROR_NONE) { + Status |= VL53L010_GetLimitCheckEnable(Dev, i, + & + (pDeviceParameters-> + LimitChecksEnable + [i])); + } else { + break; + } + } + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_GetWrapAroundCheckEnable(Dev, + &(pDeviceParameters-> + WrapAroundCheckEnable)); + } + + /* Need to be done at the end as it uses VCSELPulsePeriod */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_GetMeasurementTimingBudgetMicroSeconds(Dev, + &(pDeviceParameters->MeasurementTimingBudgetMicroSeconds)); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_SetDeviceMode(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + + LOG_FUNCTION_START("%d", (int)DeviceMode); + + switch (DeviceMode) { + case VL53L0_DEVICEMODE_SINGLE_RANGING: + case VL53L0_DEVICEMODE_CONTINUOUS_RANGING: + case VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING: + case VL53L0_DEVICEMODE_SINGLE_HISTOGRAM: + case VL53L0_DEVICEMODE_GPIO_DRIVE: + case VL53L0_DEVICEMODE_GPIO_OSC: + /* Supported mode */ + VL53L010_SETPARAMETERFIELD(Dev, DeviceMode, DeviceMode); + break; + default: + /* Unsupported mode */ + Status = VL53L0_ERROR_MODE_NOT_SUPPORTED; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetDeviceMode(VL53L0_DEV Dev, + VL53L0_DeviceModes *pDeviceMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + + LOG_FUNCTION_START(""); + + VL53L010_GETPARAMETERFIELD(Dev, DeviceMode, *pDeviceMode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_SetHistogramMode(VL53L0_DEV Dev, + VL53L0_HistogramModes HistogramMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + + LOG_FUNCTION_START("%d", (int)HistogramMode); + + switch (HistogramMode) { + case VL53L0_HISTOGRAMMODE_DISABLED: + /* Supported mode */ + VL53L010_SETPARAMETERFIELD(Dev, HistogramMode, HistogramMode); + break; + case VL53L0_HISTOGRAMMODE_REFERENCE_ONLY: + case VL53L0_HISTOGRAMMODE_RETURN_ONLY: + case VL53L0_HISTOGRAMMODE_BOTH: + default: + /* Unsupported mode */ + Status = VL53L0_ERROR_MODE_NOT_SUPPORTED; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetHistogramMode(VL53L0_DEV Dev, + VL53L0_HistogramModes *pHistogramMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + + LOG_FUNCTION_START(""); + + VL53L010_GETPARAMETERFIELD(Dev, HistogramMode, *pHistogramMode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_SetMeasurementTimingBudgetMicroSeconds(VL53L0_DEV Dev, + uint32_t MeasurementTimingBudgetMicroSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + VL53L0_DeviceSpecificParameters_t DeviceSpecificParameters; + uint8_t CurrentVCSELPulsePeriod; + uint8_t CurrentVCSELPulsePeriodPClk; + uint8_t Byte; + uint32_t NewTimingBudgetMicroSeconds; + uint16_t encodedTimeOut; + + LOG_FUNCTION_START(""); + + /* check if rangeB is done: */ + Status = VL53L010_GetWrapAroundCheckEnable(Dev, &Byte); + + if (Status == VL53L0_ERROR_NONE) { + if (((Byte == 1) && (MeasurementTimingBudgetMicroSeconds < + 17000)) || + ((Byte == 0) && (MeasurementTimingBudgetMicroSeconds < + 12000))) { + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + if (Status == VL53L0_ERROR_NONE) { + NewTimingBudgetMicroSeconds = + MeasurementTimingBudgetMicroSeconds - 7000; + if (Byte == 1) { + NewTimingBudgetMicroSeconds = + (uint32_t) (NewTimingBudgetMicroSeconds >> 1); + } + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_get_vcsel_pulse_period(Dev, + &CurrentVCSELPulsePeriodPClk, + 0); + } + + if (Status == VL53L0_ERROR_NONE) { + CurrentVCSELPulsePeriod = + VL53L010_encode_vcsel_period(CurrentVCSELPulsePeriodPClk); + encodedTimeOut = + VL53L010_calc_encoded_timeout(Dev, + NewTimingBudgetMicroSeconds, + (uint8_t) + CurrentVCSELPulsePeriod); + VL53L010_SETPARAMETERFIELD(Dev, + MeasurementTimingBudgetMicroSeconds, + MeasurementTimingBudgetMicroSeconds); + VL53L010_SETDEVICESPECIFICPARAMETER(Dev, LastEncodedTimeout, + encodedTimeOut); + } + + /* Program in register */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrWord(Dev, VL53L010_REG_RNGA_TIMEOUT_MSB, + encodedTimeOut); + } + + /* Temp: program same value for rangeB1 and rangeB2 */ + /* Range B1 */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_get_vcsel_pulse_period(Dev, + &CurrentVCSELPulsePeriodPClk, + 1); + if (Status == VL53L0_ERROR_NONE) { + CurrentVCSELPulsePeriod = + VL53L010_encode_vcsel_period + (CurrentVCSELPulsePeriodPClk); + encodedTimeOut = + VL53L010_calc_encoded_timeout(Dev, + NewTimingBudgetMicroSeconds, + (uint8_t) + CurrentVCSELPulsePeriod); + } + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrWord(Dev, VL53L010_REG_RNGB1_TIMEOUT_MSB, + encodedTimeOut); + } + + /* Range B2 */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_get_vcsel_pulse_period(Dev, + &CurrentVCSELPulsePeriodPClk, + 2); + if (Status == VL53L0_ERROR_NONE) { + CurrentVCSELPulsePeriod = + VL53L010_encode_vcsel_period + (CurrentVCSELPulsePeriodPClk); + encodedTimeOut = + VL53L010_calc_encoded_timeout(Dev, + NewTimingBudgetMicroSeconds, + (uint8_t) + CurrentVCSELPulsePeriod); + } + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrWord(Dev, VL53L010_REG_RNGB2_TIMEOUT_MSB, + encodedTimeOut); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetMeasurementTimingBudgetMicroSeconds(VL53L0_DEV Dev, + uint32_t *pMeasurementTimingBudgetMicroSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + uint8_t CurrentVCSELPulsePeriod; + uint8_t CurrentVCSELPulsePeriodPClk; + uint16_t encodedTimeOut; + uint32_t RangATimingBudgetMicroSeconds = 0; + uint32_t RangBTimingBudgetMicroSeconds = 0; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + /* check if rangeB is done: */ + Status = VL53L010_GetWrapAroundCheckEnable(Dev, &Byte); + + if (Status == VL53L0_ERROR_NONE) { + VL53L010_get_vcsel_pulse_period(Dev, + &CurrentVCSELPulsePeriodPClk, + 0); + CurrentVCSELPulsePeriod = + VL53L010_encode_vcsel_period(CurrentVCSELPulsePeriodPClk); + + /* Read from register */ + Status = VL53L0_RdWord(Dev, VL53L010_REG_RNGA_TIMEOUT_MSB, + &encodedTimeOut); + if (Status == VL53L0_ERROR_NONE) { + RangATimingBudgetMicroSeconds = + VL53L010_calc_ranging_wait_us(Dev, + encodedTimeOut, + CurrentVCSELPulsePeriod); + } + } + + if (Status == VL53L0_ERROR_NONE) { + if (Byte == 0) { + *pMeasurementTimingBudgetMicroSeconds = + RangATimingBudgetMicroSeconds + 7000; + VL53L010_SETPARAMETERFIELD(Dev, + MeasurementTimingBudgetMicroSeconds, + *pMeasurementTimingBudgetMicroSeconds); + } else { + VL53L010_get_vcsel_pulse_period(Dev, + &CurrentVCSELPulsePeriodPClk, + 1); + CurrentVCSELPulsePeriod = + VL53L010_encode_vcsel_period + (CurrentVCSELPulsePeriodPClk); + + /* Read from register */ + Status = VL53L0_RdWord(Dev, + VL53L010_REG_RNGB1_TIMEOUT_MSB, + &encodedTimeOut); + if (Status == VL53L0_ERROR_NONE) { + RangBTimingBudgetMicroSeconds = + VL53L010_calc_ranging_wait_us(Dev, + encodedTimeOut, + CurrentVCSELPulsePeriod); + } + + *pMeasurementTimingBudgetMicroSeconds = + RangATimingBudgetMicroSeconds + + RangBTimingBudgetMicroSeconds + 7000; + VL53L010_SETPARAMETERFIELD(Dev, + MeasurementTimingBudgetMicroSeconds, + *pMeasurementTimingBudgetMicroSeconds); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_SetInterMeasurementPeriodMilliSeconds(VL53L0_DEV Dev, + uint32_t + InterMeasurementPeriodMilliSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + uint16_t osc_calibrate_val; + uint32_t IMPeriodMilliSeconds; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdWord(Dev, VL53L010_REG_OSC_CALIBRATE_VAL, + &osc_calibrate_val); + + if (Status == VL53L0_ERROR_NONE) { + + if (osc_calibrate_val != 0) { + + IMPeriodMilliSeconds = + InterMeasurementPeriodMilliSeconds * + osc_calibrate_val; + } else { + IMPeriodMilliSeconds = + InterMeasurementPeriodMilliSeconds; + } + Status = VL53L0_WrDWord(Dev, + VL53L010_REG_SYSTEM_INTERMEASUREMENT_PERIOD, + IMPeriodMilliSeconds); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L010_SETPARAMETERFIELD(Dev, + InterMeasurementPeriodMilliSeconds, + InterMeasurementPeriodMilliSeconds); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetInterMeasurementPeriodMilliSeconds(VL53L0_DEV Dev, + uint32_t * + pInterMeasurementPeriodMilliSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + uint16_t osc_calibrate_val; + uint32_t IMPeriodMilliSeconds; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdWord(Dev, VL53L010_REG_OSC_CALIBRATE_VAL, + &osc_calibrate_val); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdDWord(Dev, + VL53L010_REG_SYSTEM_INTERMEASUREMENT_PERIOD, + &IMPeriodMilliSeconds); + } + + if (Status == VL53L0_ERROR_NONE) { + if (osc_calibrate_val != 0) + *pInterMeasurementPeriodMilliSeconds = + IMPeriodMilliSeconds / osc_calibrate_val; + + VL53L010_SETPARAMETERFIELD(Dev, + InterMeasurementPeriodMilliSeconds, + *pInterMeasurementPeriodMilliSeconds); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_SetXTalkCompensationEnable(VL53L0_DEV Dev, + uint8_t + XTalkCompensationEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + uint8_t XTalkCompensationEnableValue; + + LOG_FUNCTION_START(""); + + if (XTalkCompensationEnable == 0) { + /* Disable the crosstalk compensation */ + XTalkCompensationEnableValue = 0x00; + } else { + /* Enable the crosstalk compensation */ + XTalkCompensationEnableValue = 0x01; + } + Status = VL53L0_UpdateByte(Dev, VL53L010_REG_ALGO_RANGE_CHECK_ENABLES, + 0xFE, XTalkCompensationEnableValue); + if (Status == VL53L0_ERROR_NONE) { + VL53L010_SETPARAMETERFIELD(Dev, XTalkCompensationEnable, + XTalkCompensationEnableValue); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetXTalkCompensationEnable(VL53L0_DEV Dev, uint8_t * + pXTalkCompensationEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + uint8_t data; + uint8_t Temp; + + LOG_FUNCTION_START(""); + + Status = + VL53L0_RdByte(Dev, VL53L010_REG_ALGO_RANGE_CHECK_ENABLES, &data); + if (Status == VL53L0_ERROR_NONE) { + if (data & 0x01) + Temp = 0x01; + else + Temp = 0x00; + + *pXTalkCompensationEnable = Temp; + } + if (Status == VL53L0_ERROR_NONE) + VL53L010_SETPARAMETERFIELD(Dev, XTalkCompensationEnable, Temp); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_SetXTalkCompensationRateMegaCps(VL53L0_DEV Dev, + FixPoint1616_t + XTalkCompensationRateMegaCps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + + LOG_FUNCTION_START(""); + + Status = + VL53L0_WrWord(Dev, VL53L010_REG_ALGO_CROSSTALK_COMPENSATION_RATE, + VL53L010_FIXPOINT1616TOFIXPOINT412 + (XTalkCompensationRateMegaCps)); + if (Status == VL53L0_ERROR_NONE) { + VL53L010_SETPARAMETERFIELD(Dev, XTalkCompensationRateMegaCps, + XTalkCompensationRateMegaCps); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetXTalkCompensationRateMegaCps(VL53L0_DEV Dev, + FixPoint1616_t * + pXTalkCompensationRateMegaCps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t Value; + FixPoint1616_t TempFix1616; + VL53L0_DeviceParameters_t CurrentParameters; + + LOG_FUNCTION_START(""); + + Status = + VL53L0_RdWord(Dev, VL53L010_REG_ALGO_CROSSTALK_COMPENSATION_RATE, + (uint16_t *) &Value); + if (Status == VL53L0_ERROR_NONE) { + TempFix1616 = VL53L010_FIXPOINT412TOFIXPOINT1616(Value); + *pXTalkCompensationRateMegaCps = TempFix1616; + VL53L010_SETPARAMETERFIELD(Dev, XTalkCompensationRateMegaCps, + TempFix1616); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +/* + * CHECK LIMIT FUNCTIONS + */ + +VL53L0_Error VL53L010_GetNumberOfLimitCheck(uint16_t *pNumberOfLimitCheck) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pNumberOfLimitCheck = VL53L010_CHECKENABLE_NUMBER_OF_CHECKS; + + LOG_FUNCTION_END(Status); + return Status; +} + +#define VL53L010_BUILDCASESTRING(BUFFER, CODE, STRINGVALUE) \ + do { \ + case CODE: \ + VL53L0_COPYSTRING(BUFFER, STRINGVALUE); \ + break; \ + } while (0) + +VL53L0_Error VL53L010_GetLimitCheckInfo(VL53L0_DEV Dev, uint16_t LimitCheckId, + char *pLimitCheckString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (LimitCheckId) { + VL53L010_BUILDCASESTRING(pLimitCheckString, + VL53L010_CHECKENABLE_SIGMA_FINAL_RANGE, + VL53L010_STRING_CHECKENABLE_SIGMA); + VL53L010_BUILDCASESTRING(pLimitCheckString, + VL53L010_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + VL53L010_STRING_CHECKENABLE_SIGNAL_RATE); + + default: + VL53L0_COPYSTRING(pLimitCheckString, + VL53L010_STRING_UNKNOW_ERROR_CODE); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_SetLimitCheckEnable(VL53L0_DEV Dev, uint16_t LimitCheckId, + uint8_t LimitCheckEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L010_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L0_ERROR_INVALID_PARAMS; + } else { + if (LimitCheckEnable == 0) { + VL53L010_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + LimitCheckId, 0); + } else { + VL53L010_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + LimitCheckId, 1); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetLimitCheckEnable(VL53L0_DEV Dev, uint16_t LimitCheckId, + uint8_t *pLimitCheckEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + uint8_t Temp8; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L010_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L0_ERROR_INVALID_PARAMS; + } else { + VL53L010_GETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + LimitCheckId, Temp8); + *pLimitCheckEnable = Temp8; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_SetLimitCheckValue(VL53L0_DEV Dev, + uint16_t LimitCheckId, + FixPoint1616_t LimitCheckValue) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L010_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L0_ERROR_INVALID_PARAMS; + } else { + VL53L010_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + LimitCheckId, LimitCheckValue); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetLimitCheckValue(VL53L0_DEV Dev, + uint16_t LimitCheckId, + FixPoint1616_t *pLimitCheckValue) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L010_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L0_ERROR_INVALID_PARAMS; + } else { + VL53L010_GETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + LimitCheckId, + *pLimitCheckValue); + } + + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L0_Error VL53L010_GetLimitCheckCurrent(VL53L0_DEV Dev, + uint16_t LimitCheckId, + FixPoint1616_t *pLimitCheckCurrent) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L010_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L0_ERROR_INVALID_PARAMS; + } else { + switch (LimitCheckId) { + case VL53L010_CHECKENABLE_SIGMA_FINAL_RANGE: + /* Need to run a ranging to have the latest values */ + *pLimitCheckCurrent = PALDevDataGet(Dev, SigmaEstimate); + + break; + + case VL53L010_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + /* Need to run a ranging to have the latest values */ + *pLimitCheckCurrent = + PALDevDataGet(Dev, SignalEstimate); + + break; + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + LOG_FUNCTION_END(Status); + return Status; + +} + +/* + * WRAPAROUND LIMIT + */ +VL53L0_Error VL53L010_SetWrapAroundCheckEnable(VL53L0_DEV Dev, uint8_t + WrapAroundCheckEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + uint8_t WrapAroundCheckEnableInt; + VL53L0_DeviceParameters_t CurrentParameters; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L010_REG_SYSTEM_SEQUENCE_CONFIG, &Byte); + if (WrapAroundCheckEnable == 0) { + /* Disable wraparound */ + Byte = Byte & 0x7F; + WrapAroundCheckEnableInt = 0; + } else { + /* Enable wraparound */ + Byte = Byte | 0x80; + WrapAroundCheckEnableInt = 1; + } + + Status = VL53L0_WrByte(Dev, VL53L010_REG_SYSTEM_SEQUENCE_CONFIG, Byte); + + if (Status == VL53L0_ERROR_NONE) { + PALDevDataSet(Dev, SequenceConfig, Byte); + VL53L010_SETPARAMETERFIELD(Dev, WrapAroundCheckEnable, + WrapAroundCheckEnableInt); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetWrapAroundCheckEnable(VL53L0_DEV Dev, + uint8_t *pWrapAroundCheckEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t data; + VL53L0_DeviceParameters_t CurrentParameters; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L010_REG_SYSTEM_SEQUENCE_CONFIG, &data); + if (Status == VL53L0_ERROR_NONE) { + PALDevDataSet(Dev, SequenceConfig, data); + if (data & (0x01 << 7)) + *pWrapAroundCheckEnable = 0x01; + else + *pWrapAroundCheckEnable = 0x00; + + } + if (Status == VL53L0_ERROR_NONE) { + VL53L010_SETPARAMETERFIELD(Dev, WrapAroundCheckEnable, + *pWrapAroundCheckEnable); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +/* End Group PAL Parameters Functions */ + +/* Group PAL Measurement Functions */ +VL53L0_Error VL53L010_PerformSingleMeasurement(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceModes DeviceMode; + uint8_t NewDatReady = 0; + uint32_t LoopNb; + + LOG_FUNCTION_START(""); + + /* Get Current DeviceMode */ + Status = VL53L010_GetDeviceMode(Dev, &DeviceMode); + + /* Start immediately to run a single ranging measurement in case of + * single ranging or single histogram + */ + if ((Status == VL53L0_ERROR_NONE) && + ((DeviceMode == VL53L0_DEVICEMODE_SINGLE_RANGING) || + (DeviceMode == VL53L0_DEVICEMODE_SINGLE_HISTOGRAM))) { + Status = VL53L010_StartMeasurement(Dev); + } + + /* Wait until it finished + * use timeout to avoid deadlock + */ + if (Status == VL53L0_ERROR_NONE) { + LoopNb = 0; + do { + Status = VL53L010_GetMeasurementDataReady(Dev, + &NewDatReady); + if ((NewDatReady == 0x01) || Status != + VL53L0_ERROR_NONE) { + break; + } + LoopNb = LoopNb + 1; + VL53L0_PollingDelay(Dev); + } while (LoopNb < VL53L0_DEFAULT_MAX_LOOP); + + if (LoopNb >= VL53L0_DEFAULT_MAX_LOOP) + Status = VL53L0_ERROR_TIME_OUT; + + } + + /* Change PAL State in case of single ranging or single histogram + */ + if ((Status == VL53L0_ERROR_NONE) && + ((DeviceMode == VL53L0_DEVICEMODE_SINGLE_RANGING) || + (DeviceMode == VL53L0_DEVICEMODE_SINGLE_HISTOGRAM))) { + PALDevDataSet(Dev, PalState, VL53L0_STATE_IDLE); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_PerformRefCalibration(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t NewDatReady = 0; + uint8_t Byte = 0; + uint8_t SequenceConfig = 0; + uint32_t LoopNb; + + LOG_FUNCTION_START(""); + + /* store the value of the sequence config, + * this will be reset before the end of the function + */ + + SequenceConfig = PALDevDataGet(Dev, SequenceConfig); + + Status = VL53L0_WrByte(Dev, VL53L010_REG_SYSTEM_SEQUENCE_CONFIG, 0x03); + + if (Status == VL53L0_ERROR_NONE) { + PALDevDataSet(Dev, SequenceConfig, 0x03); + Status = VL53L0_WrByte(Dev, VL53L010_REG_SYSRANGE_START, + VL53L010_REG_SYSRANGE_MODE_START_STOP); + } + + if (Status == VL53L0_ERROR_NONE) { + /* Wait until start bit has been cleared */ + LoopNb = 0; + do { + if (LoopNb > 0) + Status = VL53L0_RdByte(Dev, + VL53L010_REG_SYSRANGE_START, + &Byte); + LoopNb = LoopNb + 1; + } while (((Byte & VL53L010_REG_SYSRANGE_MODE_START_STOP) == + VL53L010_REG_SYSRANGE_MODE_START_STOP) && + (Status == VL53L0_ERROR_NONE) && + (LoopNb < VL53L0_DEFAULT_MAX_LOOP)); + } + + /* Wait until it finished + * use timeout to avoid deadlock + */ + if (Status == VL53L0_ERROR_NONE) { + LoopNb = 0; + do { + Status = VL53L010_GetMeasurementDataReady(Dev, + &NewDatReady); + if ((NewDatReady == 0x01) || Status != + VL53L0_ERROR_NONE) { + break; + } + LoopNb = LoopNb + 1; + VL53L0_PollingDelay(Dev); + } while (LoopNb < VL53L0_DEFAULT_MAX_LOOP); + + if (LoopNb >= VL53L0_DEFAULT_MAX_LOOP) + Status = VL53L0_ERROR_TIME_OUT; + + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x04); + Status |= VL53L0_RdByte(Dev, 0x30, &Byte); + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0x31, Byte); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x01); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L010_ClearInterruptMask(Dev, 0); + + if (Status == VL53L0_ERROR_NONE) { + /* restore the previous Sequence Config */ + Status = VL53L0_WrByte(Dev, VL53L010_REG_SYSTEM_SEQUENCE_CONFIG, + SequenceConfig); + } + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, SequenceConfig, SequenceConfig); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L010_API VL53L0_Error VL53L010_PerformXTalkCalibration(VL53L0_DEV Dev, + FixPoint1616_t + XTalkCalDistance, + FixPoint1616_t * + pXTalkCompensationRateMegaCps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t sum_ranging = 0; + uint16_t sum_spads = 0; + FixPoint1616_t sum_signalRate = 0; + FixPoint1616_t total_count = 0; + uint8_t xtalk_meas = 0; + VL53L0_RangingMeasurementData_t RangingMeasurementData; + FixPoint1616_t xTalkStoredMeanSignalRate; + FixPoint1616_t xTalkStoredMeanRange; + FixPoint1616_t xTalkStoredMeanRtnSpads; + uint32_t signalXTalkTotalPerSpad; + uint32_t xTalkStoredMeanRtnSpadsAsInt; + uint32_t xTalkCalDistanceAsInt; + FixPoint1616_t XTalkCompensationRateMegaCps; + + LOG_FUNCTION_START(""); + + if (XTalkCalDistance <= 0) + Status = VL53L0_ERROR_INVALID_PARAMS; + + /* Disable the XTalk compensation */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L010_SetXTalkCompensationEnable(Dev, 0); + + /* Perform 50 measurements and compute the averages */ + if (Status == VL53L0_ERROR_NONE) { + sum_ranging = 0; + sum_spads = 0; + sum_signalRate = 0; + total_count = 0; + for (xtalk_meas = 0; xtalk_meas < 50; xtalk_meas++) { + Status = VL53L010_PerformSingleRangingMeasurement(Dev, + &RangingMeasurementData); + + if (Status != VL53L0_ERROR_NONE) + break; + + /* The range is valid when RangeStatus = 0 */ + if (RangingMeasurementData.RangeStatus == 0) { + sum_ranging = sum_ranging + + RangingMeasurementData.RangeMilliMeter; + sum_signalRate = sum_signalRate + + RangingMeasurementData.SignalRateRtnMegaCps; + sum_spads = sum_spads + + RangingMeasurementData.EffectiveSpadRtnCount + / 32; + total_count = total_count + 1; + } + } + + if (total_count == 0) { + /* no valid values found */ + Status = VL53L0_ERROR_DIVISION_BY_ZERO; + } + } + + if (Status == VL53L0_ERROR_NONE) { + /* FixPoint1616_t / uint16_t = FixPoint1616_t */ + xTalkStoredMeanSignalRate = sum_signalRate / total_count; + xTalkStoredMeanRange = + (FixPoint1616_t) ((uint32_t) (sum_ranging << 16) / + total_count); + xTalkStoredMeanRtnSpads = + (FixPoint1616_t) ((uint32_t) (sum_spads << 16) / + total_count); + + /* Round Mean Spads to Whole Number. + * Typically the calculated mean SPAD count is a whole number or + * very close to a whole + * number, therefore any truncation will not result in a + * significant loss in accuracy. + * Also, for a grey target at a typical distance of + * around 400mm, around 220 SPADs will + * be enabled, therefore, any truncation will result in a loss + * of accuracy of less than 0.5%. + */ + xTalkStoredMeanRtnSpadsAsInt = (xTalkStoredMeanRtnSpads + + 0x8000) >> 16; + + /* Round Cal Distance to Whole Number. + * Note that the cal distance is in mm, + * therefore no resolution is lost. + */ + xTalkCalDistanceAsInt = (XTalkCalDistance + 0x8000) >> 16; + + if (xTalkStoredMeanRtnSpadsAsInt == 0 || xTalkCalDistanceAsInt + == 0 || xTalkStoredMeanRange >= XTalkCalDistance) { + XTalkCompensationRateMegaCps = 0; + } else { + /* Round Cal Distance to Whole Number. + * Note that the cal distance is in mm, therefore no + * resolution is lost. + */ + xTalkCalDistanceAsInt = (XTalkCalDistance + 0x8000) >> + 16; + + /* Apply division by mean spad count early in the + * calculation to keep the numbers small. + * This ensures we can maintain a 32bit calculation. + * Fixed1616 / int := Fixed1616 + */ + signalXTalkTotalPerSpad = + (xTalkStoredMeanSignalRate) / + xTalkStoredMeanRtnSpadsAsInt; + + /* Complete the calculation for total Signal XTalk per + * SPAD Fixed1616 * (Fixed1616 - Fixed1616/int) + * := (2^16 * Fixed1616) + */ + signalXTalkTotalPerSpad *= ((1 << 16) - + (xTalkStoredMeanRange / + xTalkCalDistanceAsInt)); + + /* Round from 2^16 * Fixed1616, to Fixed1616. */ + XTalkCompensationRateMegaCps = (signalXTalkTotalPerSpad + + 0x8000) >> 16; + } + + *pXTalkCompensationRateMegaCps = XTalkCompensationRateMegaCps; + + /* Enable the XTalk compensation */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L010_SetXTalkCompensationEnable(Dev, 1); + + /* Enable the XTalk compensation */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_SetXTalkCompensationRateMegaCps(Dev, + XTalkCompensationRateMegaCps); + } + + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L010_API VL53L0_Error VL53L010_PerformOffsetCalibration(VL53L0_DEV Dev, + FixPoint1616_t + CalDistanceMilliMeter, + int32_t * + pOffsetMicroMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t sum_ranging = 0; + FixPoint1616_t total_count = 0; + VL53L0_RangingMeasurementData_t RangingMeasurementData; + FixPoint1616_t StoredMeanRange; + uint32_t StoredMeanRangeAsInt; + VL53L0_DeviceParameters_t CurrentParameters; + uint32_t CalDistanceAsInt_mm; + int meas = 0; + + LOG_FUNCTION_START(""); + + if (CalDistanceMilliMeter <= 0) + Status = VL53L0_ERROR_INVALID_PARAMS; + + if (Status == VL53L0_ERROR_NONE) + VL53L010_SetOffsetCalibrationDataMicroMeter(Dev, 0); + + /* Perform 50 measurements and compute the averages */ + if (Status == VL53L0_ERROR_NONE) { + sum_ranging = 0; + total_count = 0; + for (meas = 0; meas < 50; meas++) { + Status = + VL53L010_PerformSingleRangingMeasurement(Dev, + &RangingMeasurementData); + + if (Status != VL53L0_ERROR_NONE) + break; + + /* The range is valid when RangeStatus = 0 */ + if (RangingMeasurementData.RangeStatus == 0) { + sum_ranging = + sum_ranging + + RangingMeasurementData.RangeMilliMeter; + total_count = total_count + 1; + } + } + + if (total_count == 0) { + /* no valid values found */ + Status = VL53L0_ERROR_RANGE_ERROR; + } + } + + if (Status == VL53L0_ERROR_NONE) { + /* FixPoint1616_t / uint16_t = FixPoint1616_t */ + StoredMeanRange = + (FixPoint1616_t) ((uint32_t) (sum_ranging << 16) / + total_count); + + StoredMeanRangeAsInt = (StoredMeanRange + 0x8000) >> 16; + + /* Round Cal Distance to Whole Number. + * Note that the cal distance is in mm, + * therefore no resolution is lost. + */ + CalDistanceAsInt_mm = (CalDistanceMilliMeter + 0x8000) >> 16; + + *pOffsetMicroMeter = + (CalDistanceAsInt_mm - StoredMeanRangeAsInt) * 1000; + + /* Apply the calculated offset */ + if (Status == VL53L0_ERROR_NONE) { + VL53L010_SETPARAMETERFIELD(Dev, RangeOffsetMicroMeters, + *pOffsetMicroMeter); + Status = + VL53L010_SetOffsetCalibrationDataMicroMeter(Dev, + *pOffsetMicroMeter); + } + + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_StartMeasurement(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceModes DeviceMode; + uint8_t Byte = 0; + uint32_t LoopNb; + + LOG_FUNCTION_START(""); + + /* Get Current DeviceMode */ + VL53L010_GetDeviceMode(Dev, &DeviceMode); + + switch (DeviceMode) { + case VL53L0_DEVICEMODE_SINGLE_RANGING: + Status = VL53L0_WrByte(Dev, VL53L010_REG_SYSRANGE_START, + VL53L010_REG_SYSRANGE_MODE_SINGLESHOT | + VL53L010_REG_SYSRANGE_MODE_START_STOP); + break; + case VL53L0_DEVICEMODE_CONTINUOUS_RANGING: + /* Back-to-back mode */ + Status = VL53L0_WrByte(Dev, VL53L010_REG_SYSRANGE_START, + VL53L010_REG_SYSRANGE_MODE_BACKTOBACK | + VL53L010_REG_SYSRANGE_MODE_START_STOP); + if (Status == VL53L0_ERROR_NONE) { + /* Set PAL State to Running */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_RUNNING); + } + break; + case VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING: + /* Continuous mode */ + Status = VL53L0_WrByte(Dev, VL53L010_REG_SYSRANGE_START, + VL53L010_REG_SYSRANGE_MODE_TIMED | + VL53L010_REG_SYSRANGE_MODE_START_STOP); + if (Status == VL53L0_ERROR_NONE) { + /* Set PAL State to Running */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_RUNNING); + } + break; + default: + /* Selected mode not supported */ + Status = VL53L0_ERROR_MODE_NOT_SUPPORTED; + } + + if (Status == VL53L0_ERROR_NONE) { + /* Wait until start bit has been cleared */ + LoopNb = 0; + do { + if (LoopNb > 0) + Status = VL53L0_RdByte(Dev, + VL53L010_REG_SYSRANGE_START, + &Byte); + LoopNb = LoopNb + 1; + } while (((Byte & VL53L010_REG_SYSRANGE_MODE_START_STOP) == + VL53L010_REG_SYSRANGE_MODE_START_STOP) && + (Status == VL53L0_ERROR_NONE) && + (LoopNb < VL53L0_DEFAULT_MAX_LOOP)); + + if (LoopNb >= VL53L0_DEFAULT_MAX_LOOP) + Status = VL53L0_ERROR_TIME_OUT; + + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_StopMeasurement(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, VL53L010_REG_SYSRANGE_START, + VL53L010_REG_SYSRANGE_MODE_SINGLESHOT); + + if (Status == VL53L0_ERROR_NONE) { + /* Set PAL State to Idle */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_IDLE); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetMeasurementDataReady(VL53L0_DEV Dev, uint8_t + *pMeasurementDataReady) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SysRangeStatusRegister; + uint8_t InterruptConfig; + uint32_t InterruptMask; + + LOG_FUNCTION_START(""); + + InterruptConfig = VL53L010_GETDEVICESPECIFICPARAMETER(Dev, + Pin0GpioFunctionality); + + if (InterruptConfig == + VL53L010_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY) { + VL53L010_GetInterruptMaskStatus(Dev, &InterruptMask); + if (InterruptMask == + VL53L010_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY) { + *pMeasurementDataReady = 1; + } else { + *pMeasurementDataReady = 0; + } + } else { + Status = VL53L0_RdByte(Dev, VL53L010_REG_RESULT_RANGE_STATUS, + &SysRangeStatusRegister); + if (Status == VL53L0_ERROR_NONE) { + if (SysRangeStatusRegister & 0x01) + *pMeasurementDataReady = 1; + else + *pMeasurementDataReady = 0; + + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_WaitDeviceReadyForNewMeasurement(VL53L0_DEV Dev, uint32_t + MaxLoop) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + /* not implemented for VL53L0 */ + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetRangingMeasurementData(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t + *pRangingMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t DeviceRangeStatus; + uint8_t PalRangeStatus; + uint16_t AmbientRate; + FixPoint1616_t SignalRate; + FixPoint1616_t CrosstalkCompensation; + uint16_t EffectiveSpadRtnCount; + uint8_t localBuffer[14]; + VL53L0_RangingMeasurementData_t LastRangeDataBuffer; + + LOG_FUNCTION_START(""); + + /* use multi read even if some registers are not useful, result will be + * more efficient + * start reading at 0x14 dec20 + * end reading at 0x21 dec33 total 14 bytes to read + */ + Status = VL53L0_ReadMulti(Dev, 0x14, localBuffer, 14); + + if (Status == VL53L0_ERROR_NONE) { + + pRangingMeasurementData->ZoneId = 0; /* Only one zone */ + pRangingMeasurementData->TimeStamp = 0; /* Not Implemented */ + + pRangingMeasurementData->RangeMilliMeter = + VL53L010_MAKEUINT16(localBuffer[11], localBuffer[10]); + + pRangingMeasurementData->RangeDMaxMilliMeter = 0; + pRangingMeasurementData->RangeFractionalPart = 0; + pRangingMeasurementData->MeasurementTimeUsec = 0; + + SignalRate = + VL53L010_FIXPOINT97TOFIXPOINT1616(VL53L010_MAKEUINT16 + (localBuffer[7], + localBuffer[6])); + pRangingMeasurementData->SignalRateRtnMegaCps = SignalRate; + + AmbientRate = + VL53L010_MAKEUINT16(localBuffer[9], localBuffer[8]); + pRangingMeasurementData->AmbientRateRtnMegaCps = + VL53L010_FIXPOINT97TOFIXPOINT1616(AmbientRate); + + EffectiveSpadRtnCount = VL53L010_MAKEUINT16(localBuffer[3], + localBuffer[2]); + pRangingMeasurementData->EffectiveSpadRtnCount = + EffectiveSpadRtnCount; + + DeviceRangeStatus = localBuffer[0]; + + /* initial format = 4.12, when pass to 16.16 from 9.7 we shift + * 5 bit more this will be absorbed in the further computation + */ + CrosstalkCompensation = + VL53L010_FIXPOINT97TOFIXPOINT1616(VL53L010_MAKEUINT16 + (localBuffer[13], + localBuffer[12])); + + /* + * For a standard definition of RangeStatus, this should return + * 0 in case of good result after a ranging + * The range status depends on the device so call a device + * specific function to obtain the right Status. + */ + Status = VL53L010_get_pal_range_status(Dev, DeviceRangeStatus, + SignalRate, + CrosstalkCompensation, + EffectiveSpadRtnCount, + pRangingMeasurementData, + &PalRangeStatus); + + if (Status == VL53L0_ERROR_NONE) + pRangingMeasurementData->RangeStatus = PalRangeStatus; + + } + + if (Status == VL53L0_ERROR_NONE) { + /* Copy last read data into Dev buffer */ + LastRangeDataBuffer = PALDevDataGet(Dev, LastRangeMeasure); + + LastRangeDataBuffer.RangeMilliMeter = + pRangingMeasurementData->RangeMilliMeter; + LastRangeDataBuffer.RangeFractionalPart = + pRangingMeasurementData->RangeFractionalPart; + LastRangeDataBuffer.RangeDMaxMilliMeter = + pRangingMeasurementData->RangeDMaxMilliMeter; + LastRangeDataBuffer.MeasurementTimeUsec = + pRangingMeasurementData->MeasurementTimeUsec; + LastRangeDataBuffer.SignalRateRtnMegaCps = + pRangingMeasurementData->SignalRateRtnMegaCps; + LastRangeDataBuffer.AmbientRateRtnMegaCps = + pRangingMeasurementData->AmbientRateRtnMegaCps; + LastRangeDataBuffer.EffectiveSpadRtnCount = + pRangingMeasurementData->EffectiveSpadRtnCount; + LastRangeDataBuffer.RangeStatus = + pRangingMeasurementData->RangeStatus; + + PALDevDataSet(Dev, LastRangeMeasure, LastRangeDataBuffer); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetHistogramMeasurementData(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t + *pHistogramMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_PerformSingleRangingMeasurement(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t + *pRangingMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + /* This function will do a complete single ranging + * Here we fix the mode! + */ + Status = VL53L010_SetDeviceMode(Dev, VL53L0_DEVICEMODE_SINGLE_RANGING); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L010_PerformSingleMeasurement(Dev); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_GetRangingMeasurementData(Dev, + pRangingMeasurementData); + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L010_ClearInterruptMask(Dev, 0); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_PerformSingleHistogramMeasurement(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t + * + pHistogramMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_SetNumberOfROIZones(VL53L0_DEV Dev, uint8_t + NumberOfROIZones) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (NumberOfROIZones != 1) + Status = VL53L0_ERROR_INVALID_PARAMS; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetNumberOfROIZones(VL53L0_DEV Dev, uint8_t * + pNumberOfROIZones) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pNumberOfROIZones = 1; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetMaxNumberOfROIZones(VL53L0_DEV Dev, uint8_t + *pMaxNumberOfROIZones) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pMaxNumberOfROIZones = 1; + + LOG_FUNCTION_END(Status); + return Status; +} + +/* End Group PAL Measurement Functions */ + +VL53L0_Error VL53L010_SetGpioConfig(VL53L0_DEV Dev, uint8_t Pin, + VL53L0_DeviceModes DeviceMode, + VL53L0_GpioFunctionality Functionality, + VL53L0_InterruptPolarity Polarity) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceSpecificParameters_t DeviceSpecificParameters; + uint8_t data; + + LOG_FUNCTION_START(""); + + if (Pin != 0) { + Status = VL53L0_ERROR_GPIO_NOT_EXISTING; + } else if (DeviceMode == VL53L0_DEVICEMODE_GPIO_DRIVE) { + if (Polarity == VL53L0_INTERRUPTPOLARITY_LOW) + data = 0x10; + else + data = 1; + + Status = VL53L0_WrByte(Dev, + VL53L010_REG_GPIO_HV_MUX_ACTIVE_HIGH, + data); + + } else if (DeviceMode == VL53L0_DEVICEMODE_GPIO_OSC) { + + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + Status |= VL53L0_WrByte(Dev, 0x80, 0x01); + Status |= VL53L0_WrByte(Dev, 0x85, 0x02); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x04); + Status |= VL53L0_WrByte(Dev, 0xcd, 0x00); + Status |= VL53L0_WrByte(Dev, 0xcc, 0x11); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x07); + Status |= VL53L0_WrByte(Dev, 0xbe, 0x00); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x06); + Status |= VL53L0_WrByte(Dev, 0xcc, 0x09); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + + } else { + + if (Status == VL53L0_ERROR_NONE) { + switch (Functionality) { + case VL53L010_GPIOFUNCTIONALITY_OFF: + data = 0x00; + break; + case VL53L010_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW: + data = 0x01; + break; + case VL53L010_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH: + data = 0x02; + break; + case VL53L010_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT: + data = 0x03; + break; + case VL53L010_GPIOFUNCTIONALITY_NEW_MEASURE_READY: + data = 0x04; + break; + default: + Status = + VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED; + } + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrByte(Dev, + VL53L010_REG_SYSTEM_INTERRUPT_CONFIG_GPIO, + data); + } + + if (Status == VL53L0_ERROR_NONE) { + if (Polarity == VL53L0_INTERRUPTPOLARITY_LOW) + data = 0; + else + data = (uint8_t) (1 << 4); + + Status = VL53L0_UpdateByte(Dev, + VL53L010_REG_GPIO_HV_MUX_ACTIVE_HIGH, + 0xEF, data); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L010_SETDEVICESPECIFICPARAMETER(Dev, + Pin0GpioFunctionality, + Functionality); + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L010_ClearInterruptMask(Dev, 0); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetGpioConfig(VL53L0_DEV Dev, uint8_t Pin, + VL53L0_DeviceModes *DeviceMode, + VL53L0_GpioFunctionality *pFunctionality, + VL53L0_InterruptPolarity *pPolarity) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceSpecificParameters_t DeviceSpecificParameters; + VL53L0_GpioFunctionality GpioFunctionality; + uint8_t data; + + LOG_FUNCTION_START(""); + + if (Pin != 0) { + Status = VL53L0_ERROR_GPIO_NOT_EXISTING; + } else { + Status = VL53L0_RdByte(Dev, + VL53L010_REG_SYSTEM_INTERRUPT_CONFIG_GPIO, + &data); + } + + if (Status == VL53L0_ERROR_NONE) { + switch (data & 0x07) { + case 0x00: + GpioFunctionality = VL53L010_GPIOFUNCTIONALITY_OFF; + break; + case 0x01: + GpioFunctionality = + VL53L010_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW; + break; + case 0x02: + GpioFunctionality = + VL53L010_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH; + break; + case 0x03: + GpioFunctionality = + VL53L010_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT; + break; + case 0x04: + GpioFunctionality = + VL53L010_GPIOFUNCTIONALITY_NEW_MEASURE_READY; + break; + default: + Status = VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED; + } + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_RdByte(Dev, + VL53L010_REG_GPIO_HV_MUX_ACTIVE_HIGH, + &data); + + if (Status == VL53L0_ERROR_NONE) { + if ((data & (uint8_t) (1 << 4)) == 0) + *pPolarity = VL53L0_INTERRUPTPOLARITY_LOW; + else + *pPolarity = VL53L0_INTERRUPTPOLARITY_HIGH; + } + + if (Status == VL53L0_ERROR_NONE) { + *pFunctionality = GpioFunctionality; + VL53L010_SETDEVICESPECIFICPARAMETER(Dev, Pin0GpioFunctionality, + GpioFunctionality); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_SetInterruptThresholds(VL53L0_DEV Dev, VL53L0_DeviceModes + DeviceMode, + FixPoint1616_t ThresholdLow, + FixPoint1616_t ThresholdHigh) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t Threshold16; + + LOG_FUNCTION_START(""); + + /* no dependency on DeviceMode for Ewok */ + /* Need to divide by 2 because the FW will apply a x2 */ + Threshold16 = (uint16_t) ((ThresholdLow >> 17) & 0x00fff); + Status = + VL53L0_WrWord(Dev, VL53L010_REG_SYSTEM_THRESH_LOW, Threshold16); + + if (Status == VL53L0_ERROR_NONE) { + /* Need to divide by 2 because the FW will apply a x2 */ + Threshold16 = (uint16_t) ((ThresholdHigh >> 17) & 0x00fff); + Status = VL53L0_WrWord(Dev, VL53L010_REG_SYSTEM_THRESH_HIGH, + Threshold16); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetInterruptThresholds(VL53L0_DEV Dev, VL53L0_DeviceModes + DeviceMode, + FixPoint1616_t *pThresholdLow, + FixPoint1616_t *pThresholdHigh) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t Threshold16; + + LOG_FUNCTION_START(""); + + /* no dependency on DeviceMode for Ewok */ + + Status = + VL53L0_RdWord(Dev, VL53L010_REG_SYSTEM_THRESH_LOW, &Threshold16); + /* Need to multiply by 2 because the FW will apply a x2 */ + *pThresholdLow = (FixPoint1616_t) ((0x00fff & Threshold16) << 17); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdWord(Dev, VL53L010_REG_SYSTEM_THRESH_HIGH, + &Threshold16); + /* Need to multiply by 2 because the FW will apply a x2 */ + *pThresholdHigh = + (FixPoint1616_t) ((0x00fff & Threshold16) << 17); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +/* Group PAL Interrupt Functions */ +VL53L0_Error VL53L010_ClearInterruptMask(VL53L0_DEV Dev, uint32_t InterruptMask) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t LoopCount; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + /* clear bit 0 range interrupt, bit 1 error interrupt */ + Status = VL53L0_WrByte(Dev, VL53L010_REG_SYSTEM_INTERRUPT_CLEAR, 0x01); + LoopCount = 0; + do { + VL53L0_RdByte(Dev, VL53L010_REG_RESULT_INTERRUPT_STATUS, &Byte); + LoopCount++; + } while (((Byte & 0x07) != 0x00) && (LoopCount < 8)); + Status = VL53L0_WrByte(Dev, VL53L010_REG_SYSTEM_INTERRUPT_CLEAR, 0x00); + /* clear all */ + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetInterruptMaskStatus(VL53L0_DEV Dev, uint32_t + *pInterruptMaskStatus) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + Status = + VL53L0_RdByte(Dev, VL53L010_REG_RESULT_INTERRUPT_STATUS, &Byte); + *pInterruptMaskStatus = Byte & 0x07; + + /* check if some error occurs */ + if (Byte & 0x18) + Status = VL53L0_ERROR_RANGE_ERROR; + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_EnableInterruptMask(VL53L0_DEV Dev, + uint32_t InterruptMask) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + /* not implemented for VL53L0 */ + + LOG_FUNCTION_END(Status); + return Status; +} + +/* End Group PAL Interrupt Functions */ + +/* Group SPAD functions */ + +VL53L0_Error VL53L010_SetSpadAmbientDamperThreshold(VL53L0_DEV Dev, uint16_t + SpadAmbientDamperThreshold) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + VL53L0_WrByte(Dev, 0xFF, 0x01); + Status = VL53L0_WrWord(Dev, 0x40, SpadAmbientDamperThreshold); + VL53L0_WrByte(Dev, 0xFF, 0x00); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetSpadAmbientDamperThreshold(VL53L0_DEV Dev, uint16_t + * + pSpadAmbientDamperThreshold) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + VL53L0_WrByte(Dev, 0xFF, 0x01); + Status = VL53L0_RdWord(Dev, 0x40, pSpadAmbientDamperThreshold); + VL53L0_WrByte(Dev, 0xFF, 0x00); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_SetSpadAmbientDamperFactor(VL53L0_DEV Dev, uint16_t + SpadAmbientDamperFactor) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + Byte = (uint8_t) (SpadAmbientDamperFactor & 0x00FF); + + VL53L0_WrByte(Dev, 0xFF, 0x01); + Status = VL53L0_WrByte(Dev, 0x42, Byte); + VL53L0_WrByte(Dev, 0xFF, 0x00); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_GetSpadAmbientDamperFactor(VL53L0_DEV Dev, uint16_t + *pSpadAmbientDamperFactor) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + VL53L0_WrByte(Dev, 0xFF, 0x01); + Status = VL53L0_RdByte(Dev, 0x42, &Byte); + VL53L0_WrByte(Dev, 0xFF, 0x00); + *pSpadAmbientDamperFactor = (uint16_t) Byte; + + LOG_FUNCTION_END(Status); + return Status; +} + +/* END Group SPAD functions */ + +/* + * Internal functions + */ + + + +VL53L010_EXTERNAL VL53L0_Error VL53L010_get_vcsel_pulse_period(VL53L0_DEV Dev, + uint8_t * + pVCSELPulsePeriod, + uint8_t + RangeIndex) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t vcsel_period_reg; + + LOG_FUNCTION_START(""); + + switch (RangeIndex) { + case 0: + Status = + VL53L0_RdByte(Dev, VL53L010_REG_RNGA_CONFIG_VCSEL_PERIOD, + &vcsel_period_reg); + break; + case 1: + Status = VL53L0_RdByte(Dev, + VL53L010_REG_RNGB1_CONFIG_VCSEL_PERIOD, + &vcsel_period_reg); + break; + case 2: + Status = VL53L0_RdByte(Dev, + VL53L010_REG_RNGB2_CONFIG_VCSEL_PERIOD, + &vcsel_period_reg); + break; + default: + Status = + VL53L0_RdByte(Dev, VL53L010_REG_RNGA_CONFIG_VCSEL_PERIOD, + &vcsel_period_reg); + } + + if (Status == VL53L0_ERROR_NONE) { + *pVCSELPulsePeriod = + VL53L010_decode_vcsel_period(vcsel_period_reg); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +/* To convert ms into register value */ +VL53L010_EXTERNAL uint16_t VL53L010_calc_encoded_timeout(VL53L0_DEV Dev, + uint32_t + timeout_period_us, + uint8_t vcsel_period) +{ + uint32_t macro_period_ps; + uint32_t macro_period_ns; + uint32_t timeout_period_mclks = 0; + uint16_t timeout_overall_periods = 0; + + macro_period_ps = VL53L010_calc_macro_period_ps(Dev, vcsel_period); + macro_period_ns = macro_period_ps / 1000; + + timeout_period_mclks = (uint32_t) (((timeout_period_us * 1000) + + (macro_period_ns / 2)) / + macro_period_ns); + timeout_overall_periods = VL53L010_encode_timeout(timeout_period_mclks); + + return timeout_overall_periods; +} + +/* To convert register value into us */ +VL53L010_EXTERNAL uint32_t VL53L010_calc_ranging_wait_us(VL53L0_DEV Dev, + uint16_t + timeout_overall_periods, + uint8_t vcsel_period) +{ + uint32_t macro_period_ps; + uint32_t macro_period_ns; + uint32_t timeout_period_mclks = 0; + uint32_t actual_timeout_period_us = 0; + + macro_period_ps = VL53L010_calc_macro_period_ps(Dev, vcsel_period); + macro_period_ns = macro_period_ps / 1000; + + timeout_period_mclks = VL53L010_decode_timeout(timeout_overall_periods); + actual_timeout_period_us = ((timeout_period_mclks * macro_period_ns) + + (macro_period_ns / 2)) / 1000; + + return actual_timeout_period_us; +} + +VL53L010_EXTERNAL uint32_t VL53L010_calc_macro_period_ps(VL53L0_DEV Dev, + uint8_t vcsel_period) +{ + uint32_t PLL_multiplier; + uint64_t PLL_period_ps; + uint8_t vcsel_period_pclks; + uint32_t macro_period_vclks; + uint32_t macro_period_ps; + FixPoint1616_t OscFrequencyMHz; + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceSpecificParameters_t DeviceSpecificParameters; + + LOG_FUNCTION_START(""); + + PLL_multiplier = 65536 / 64; /* PLL multiplier is 64 */ + + OscFrequencyMHz = VL53L010_GETDEVICESPECIFICPARAMETER(Dev, + OscFrequencyMHz); + + if (OscFrequencyMHz == 0) { + /* Use default one */ + VL53L010_SETDEVICESPECIFICPARAMETER(Dev, OscFrequencyMHz, + 748421); + OscFrequencyMHz = 748421; + } + PLL_period_ps = (1000 * 1000 * PLL_multiplier) / OscFrequencyMHz; + + vcsel_period_pclks = VL53L010_decode_vcsel_period(vcsel_period); + + macro_period_vclks = 2304; + macro_period_ps = (uint32_t) (macro_period_vclks * vcsel_period_pclks * + PLL_period_ps); + + LOG_FUNCTION_END(""); + return macro_period_ps; +} + +VL53L010_EXTERNAL uint8_t VL53L010_decode_vcsel_period(uint8_t vcsel_period_reg) +{ + + /*! + * Converts the encoded VCSEL period register value into the real + * period in PLL clocks + */ + + uint8_t vcsel_period_pclks = 0; + + vcsel_period_pclks = (vcsel_period_reg + 1) << 1; + + return vcsel_period_pclks; +} + +VL53L010_EXTERNAL uint8_t VL53L010_encode_vcsel_period(uint8_t + vcsel_period_pclks) +{ + + /*! + * Converts the encoded VCSEL period register value into the real + * period in PLL clocks + */ + + uint8_t vcsel_period_reg = 0; + + vcsel_period_reg = (vcsel_period_pclks >> 1) - 1; + + return vcsel_period_reg; +} + +VL53L010_EXTERNAL uint16_t VL53L010_encode_timeout(uint32_t timeout_mclks) +{ + /*! + * Encode timeout in macro periods in (LSByte * 2^MSByte) + 1 format + * + */ + + uint16_t encoded_timeout = 0; + uint32_t ls_byte = 0; + uint16_t ms_byte = 0; + + if (timeout_mclks > 0) { + ls_byte = timeout_mclks - 1; + + while ((ls_byte & 0xFFFFFF00) > 0) { + ls_byte = ls_byte >> 1; + ms_byte++; + } + + encoded_timeout = (ms_byte << 8) + (uint16_t) (ls_byte & + 0x000000FF); + + } + + return encoded_timeout; + +} + +VL53L010_EXTERNAL uint32_t VL53L010_decode_timeout(uint16_t encoded_timeout) +{ + /*! + * Decode 16-bit timeout register value - format (LSByte * 2^MSByte) + 1 + * + */ + + uint32_t timeout_mclks = 0; + + timeout_mclks = ((uint32_t) (encoded_timeout & 0x00FF) << (uint32_t) + ((encoded_timeout & 0xFF00) >> 8)) + 1; + + return timeout_mclks; + +} + +VL53L010_EXTERNAL VL53L0_Error VL53L010_load_additional_settings1(VL53L0_DEV + Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + /* update 12_05_15_v6 */ + /* OSCT */ + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0x80, 0x01); + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x14, 0x01); + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0xCD, 0x6C); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x86, 0x03); + Status |= VL53L0_WrByte(Dev, 0x87, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x01); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + /* update 12_05_15_v6 */ + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0xcd, 0x6c); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x90, 0x07); + Status |= VL53L0_WrByte(Dev, 0x91, 0x3f); + Status |= VL53L0_WrByte(Dev, 0x92, 0x3f); + Status |= VL53L0_WrByte(Dev, 0x88, 0x2b); + Status |= VL53L0_WrByte(Dev, 0x89, 0x03); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0xcd, 0x00); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x01); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + /* update 12_05_15 */ + Status |= VL53L0_WrByte(Dev, 0xb0, 0x00); + Status |= VL53L0_WrByte(Dev, 0xb1, 0xfc); + Status |= VL53L0_WrByte(Dev, 0xb2, 0x00); + Status |= VL53L0_WrByte(Dev, 0xb3, 0x00); + Status |= VL53L0_WrByte(Dev, 0xb4, 0x00); + Status |= VL53L0_WrByte(Dev, 0xb5, 0x00); + Status |= VL53L0_WrByte(Dev, 0xb6, 0xb0); + + Status |= VL53L0_WrByte(Dev, 0x32, 0x03); + + Status |= VL53L0_WrByte(Dev, 0x41, 0xff); + Status |= VL53L0_WrByte(Dev, 0x42, 0x07); + Status |= VL53L0_WrByte(Dev, 0x43, 0x01); + + Status |= VL53L0_WrByte(Dev, 0x01, 0x01); + + if (Status != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L010_EXTERNAL VL53L0_Error VL53L010_load_additional_settings3(VL53L0_DEV + Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + /* update 150624_b */ + + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + Status |= VL53L0_WrByte(Dev, 0x80, 0x01); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, 0x4f, 0x0B); + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x0E); + + Status |= VL53L0_WrByte(Dev, 0x00, 0x0C); + Status |= VL53L0_WrByte(Dev, 0x01, 0x0C); + Status |= VL53L0_WrByte(Dev, 0x02, 0x0A); + Status |= VL53L0_WrByte(Dev, 0x03, 0x0D); + Status |= VL53L0_WrByte(Dev, 0x04, 0x00); + Status |= VL53L0_WrByte(Dev, 0x05, 0x60); + Status |= VL53L0_WrByte(Dev, 0x06, 0x06); + Status |= VL53L0_WrByte(Dev, 0x07, 0x47); + Status |= VL53L0_WrByte(Dev, 0x08, 0x28); + Status |= VL53L0_WrByte(Dev, 0x09, 0x20); + Status |= VL53L0_WrByte(Dev, 0x0A, 0x28); + Status |= VL53L0_WrByte(Dev, 0x0B, 0x49); + Status |= VL53L0_WrByte(Dev, 0x0C, 0x00); + Status |= VL53L0_WrByte(Dev, 0x0D, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x0E, 0x00); + Status |= VL53L0_WrByte(Dev, 0x0F, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x10, 0x00); + Status |= VL53L0_WrByte(Dev, 0x11, 0xA1); + Status |= VL53L0_WrByte(Dev, 0x12, 0x00); + Status |= VL53L0_WrByte(Dev, 0x13, 0xA0); + Status |= VL53L0_WrByte(Dev, 0x14, 0x00); + Status |= VL53L0_WrByte(Dev, 0x15, 0x04); + Status |= VL53L0_WrByte(Dev, 0x16, 0x01); + Status |= VL53L0_WrByte(Dev, 0x17, 0x00); + Status |= VL53L0_WrByte(Dev, 0x18, 0x01); + Status |= VL53L0_WrByte(Dev, 0x19, 0xA0); + Status |= VL53L0_WrByte(Dev, 0x1A, 0x11); + Status |= VL53L0_WrByte(Dev, 0x1B, 0x00); + Status |= VL53L0_WrByte(Dev, 0x1C, 0x09); + Status |= VL53L0_WrByte(Dev, 0x1D, 0x04); + Status |= VL53L0_WrByte(Dev, 0x1E, 0x11); + Status |= VL53L0_WrByte(Dev, 0x1F, 0x08); + Status |= VL53L0_WrByte(Dev, 0x20, 0x09); + Status |= VL53L0_WrByte(Dev, 0x21, 0x02); + Status |= VL53L0_WrByte(Dev, 0x22, 0x0C); + Status |= VL53L0_WrByte(Dev, 0x23, 0x00); + Status |= VL53L0_WrByte(Dev, 0x24, 0x0A); + Status |= VL53L0_WrByte(Dev, 0x25, 0x0D); + Status |= VL53L0_WrByte(Dev, 0x26, 0x00); + Status |= VL53L0_WrByte(Dev, 0x27, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x28, 0x00); + Status |= VL53L0_WrByte(Dev, 0x29, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x2A, 0x00); + Status |= VL53L0_WrByte(Dev, 0x2B, 0x04); + Status |= VL53L0_WrByte(Dev, 0x2C, 0x01); + Status |= VL53L0_WrByte(Dev, 0x2D, 0x60); + Status |= VL53L0_WrByte(Dev, 0x2E, 0x09); + Status |= VL53L0_WrByte(Dev, 0x2F, 0x92); + Status |= VL53L0_WrByte(Dev, 0x30, 0x01); + Status |= VL53L0_WrByte(Dev, 0x31, 0x64); + Status |= VL53L0_WrByte(Dev, 0x32, 0x09); + Status |= VL53L0_WrByte(Dev, 0x33, 0x8A); + Status |= VL53L0_WrByte(Dev, 0x34, 0x01); + Status |= VL53L0_WrByte(Dev, 0x35, 0xE0); + Status |= VL53L0_WrByte(Dev, 0x36, 0x0F); + Status |= VL53L0_WrByte(Dev, 0x37, 0xAA); + Status |= VL53L0_WrByte(Dev, 0x38, 0x01); + Status |= VL53L0_WrByte(Dev, 0x39, 0xE4); + Status |= VL53L0_WrByte(Dev, 0x3A, 0x0F); + Status |= VL53L0_WrByte(Dev, 0x3B, 0xAE); + Status |= VL53L0_WrByte(Dev, 0x3C, 0x01); + Status |= VL53L0_WrByte(Dev, 0x3D, 0x00); + Status |= VL53L0_WrByte(Dev, 0x3E, 0x00); + Status |= VL53L0_WrByte(Dev, 0x3F, 0x54); + Status |= VL53L0_WrByte(Dev, 0x40, 0x05); + Status |= VL53L0_WrByte(Dev, 0x41, 0x88); + Status |= VL53L0_WrByte(Dev, 0x42, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x43, 0x02); + Status |= VL53L0_WrByte(Dev, 0x44, 0x0C); + Status |= VL53L0_WrByte(Dev, 0x45, 0x04); + Status |= VL53L0_WrByte(Dev, 0x46, 0x06); + Status |= VL53L0_WrByte(Dev, 0x47, 0x87); + Status |= VL53L0_WrByte(Dev, 0x48, 0x28); + Status |= VL53L0_WrByte(Dev, 0x49, 0x38); + Status |= VL53L0_WrByte(Dev, 0x4A, 0x2B); + Status |= VL53L0_WrByte(Dev, 0x4B, 0x89); + Status |= VL53L0_WrByte(Dev, 0x4C, 0x00); + Status |= VL53L0_WrByte(Dev, 0x4D, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x4E, 0x00); + Status |= VL53L0_WrByte(Dev, 0x4F, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x50, 0x00); + Status |= VL53L0_WrByte(Dev, 0x51, 0xA1); + Status |= VL53L0_WrByte(Dev, 0x52, 0x00); + Status |= VL53L0_WrByte(Dev, 0x53, 0xA0); + Status |= VL53L0_WrByte(Dev, 0x54, 0x00); + Status |= VL53L0_WrByte(Dev, 0x55, 0x04); + Status |= VL53L0_WrByte(Dev, 0x56, 0x01); + Status |= VL53L0_WrByte(Dev, 0x57, 0x00); + Status |= VL53L0_WrByte(Dev, 0x58, 0x01); + Status |= VL53L0_WrByte(Dev, 0x59, 0x0D); + Status |= VL53L0_WrByte(Dev, 0x5A, 0x09); + Status |= VL53L0_WrByte(Dev, 0x5B, 0x02); + Status |= VL53L0_WrByte(Dev, 0x5C, 0x00); + Status |= VL53L0_WrByte(Dev, 0x5D, 0x60); + Status |= VL53L0_WrByte(Dev, 0x5E, 0x0D); + Status |= VL53L0_WrByte(Dev, 0x5F, 0x67); + Status |= VL53L0_WrByte(Dev, 0x60, 0x00); + Status |= VL53L0_WrByte(Dev, 0x61, 0x60); + Status |= VL53L0_WrByte(Dev, 0x62, 0x0D); + Status |= VL53L0_WrByte(Dev, 0x63, 0xB0); + Status |= VL53L0_WrByte(Dev, 0x64, 0x28); + Status |= VL53L0_WrByte(Dev, 0x65, 0x20); + Status |= VL53L0_WrByte(Dev, 0x66, 0x29); + Status |= VL53L0_WrByte(Dev, 0x67, 0xC1); + Status |= VL53L0_WrByte(Dev, 0x68, 0x00); + Status |= VL53L0_WrByte(Dev, 0x69, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x6A, 0x00); + Status |= VL53L0_WrByte(Dev, 0x6B, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x6C, 0x00); + Status |= VL53L0_WrByte(Dev, 0x6D, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x6E, 0x00); + Status |= VL53L0_WrByte(Dev, 0x6F, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x70, 0x00); + Status |= VL53L0_WrByte(Dev, 0x71, 0xA1); + Status |= VL53L0_WrByte(Dev, 0x72, 0x00); + Status |= VL53L0_WrByte(Dev, 0x73, 0xA0); + Status |= VL53L0_WrByte(Dev, 0x74, 0x00); + Status |= VL53L0_WrByte(Dev, 0x75, 0x04); + Status |= VL53L0_WrByte(Dev, 0x76, 0x04); + Status |= VL53L0_WrByte(Dev, 0x77, 0x09); + Status |= VL53L0_WrByte(Dev, 0x78, 0x09); + Status |= VL53L0_WrByte(Dev, 0x79, 0x0D); + Status |= VL53L0_WrByte(Dev, 0x7A, 0x04); + Status |= VL53L0_WrByte(Dev, 0x7B, 0x1B); + Status |= VL53L0_WrByte(Dev, 0x7C, 0x09); + Status |= VL53L0_WrByte(Dev, 0x7D, 0x82); + Status |= VL53L0_WrByte(Dev, 0x7E, 0x04); + Status |= VL53L0_WrByte(Dev, 0x7F, 0x24); + Status |= VL53L0_WrByte(Dev, 0x80, 0x09); + Status |= VL53L0_WrByte(Dev, 0x81, 0x09); + Status |= VL53L0_WrByte(Dev, 0x82, 0x28); + Status |= VL53L0_WrByte(Dev, 0x83, 0x00); + Status |= VL53L0_WrByte(Dev, 0x84, 0x28); + Status |= VL53L0_WrByte(Dev, 0x85, 0x01); + Status |= VL53L0_WrByte(Dev, 0x86, 0x03); + Status |= VL53L0_WrByte(Dev, 0x87, 0x21); + Status |= VL53L0_WrByte(Dev, 0x88, 0x03); + Status |= VL53L0_WrByte(Dev, 0x89, 0x58); + Status |= VL53L0_WrByte(Dev, 0x8A, 0x03); + Status |= VL53L0_WrByte(Dev, 0x8B, 0xCC); + Status |= VL53L0_WrByte(Dev, 0x8C, 0x03); + Status |= VL53L0_WrByte(Dev, 0x8D, 0xC3); + Status |= VL53L0_WrByte(Dev, 0x8E, 0x00); + Status |= VL53L0_WrByte(Dev, 0x8F, 0x94); + Status |= VL53L0_WrByte(Dev, 0x90, 0x00); + Status |= VL53L0_WrByte(Dev, 0x91, 0x53); + Status |= VL53L0_WrByte(Dev, 0x92, 0x1E); + Status |= VL53L0_WrByte(Dev, 0x93, 0x03); + Status |= VL53L0_WrByte(Dev, 0x94, 0x01); + Status |= VL53L0_WrByte(Dev, 0x95, 0x00); + Status |= VL53L0_WrByte(Dev, 0x96, 0x00); + Status |= VL53L0_WrByte(Dev, 0x97, 0x28); + Status |= VL53L0_WrByte(Dev, 0x98, 0x20); + Status |= VL53L0_WrByte(Dev, 0x99, 0x20); + Status |= VL53L0_WrByte(Dev, 0x9A, 0x08); + Status |= VL53L0_WrByte(Dev, 0x9B, 0x10); + Status |= VL53L0_WrByte(Dev, 0x9C, 0x09); + Status |= VL53L0_WrByte(Dev, 0x9D, 0x03); + Status |= VL53L0_WrByte(Dev, 0x9E, 0x28); + Status |= VL53L0_WrByte(Dev, 0x9F, 0x50); + Status |= VL53L0_WrByte(Dev, 0xA0, 0x2B); + Status |= VL53L0_WrByte(Dev, 0xA1, 0xB1); + Status |= VL53L0_WrByte(Dev, 0xA2, 0x0B); + Status |= VL53L0_WrByte(Dev, 0xA3, 0x02); + Status |= VL53L0_WrByte(Dev, 0xA4, 0x28); + Status |= VL53L0_WrByte(Dev, 0xA5, 0x50); + Status |= VL53L0_WrByte(Dev, 0xA6, 0x2C); + Status |= VL53L0_WrByte(Dev, 0xA7, 0x11); + Status |= VL53L0_WrByte(Dev, 0xA8, 0x00); + Status |= VL53L0_WrByte(Dev, 0xA9, 0x0B); + Status |= VL53L0_WrByte(Dev, 0xAA, 0x00); + Status |= VL53L0_WrByte(Dev, 0xAB, 0x0B); + Status |= VL53L0_WrByte(Dev, 0xAC, 0x00); + Status |= VL53L0_WrByte(Dev, 0xAD, 0xA1); + Status |= VL53L0_WrByte(Dev, 0xAE, 0x00); + Status |= VL53L0_WrByte(Dev, 0xAF, 0xA0); + Status |= VL53L0_WrByte(Dev, 0xB0, 0x00); + Status |= VL53L0_WrByte(Dev, 0xB1, 0x04); + Status |= VL53L0_WrByte(Dev, 0xB2, 0x28); + Status |= VL53L0_WrByte(Dev, 0xB3, 0x4E); + Status |= VL53L0_WrByte(Dev, 0xB4, 0x2D); + Status |= VL53L0_WrByte(Dev, 0xB5, 0x47); + Status |= VL53L0_WrByte(Dev, 0xB6, 0x00); + Status |= VL53L0_WrByte(Dev, 0xB7, 0x0B); + Status |= VL53L0_WrByte(Dev, 0xB8, 0x00); + Status |= VL53L0_WrByte(Dev, 0xB9, 0x0B); + Status |= VL53L0_WrByte(Dev, 0xBA, 0x00); + Status |= VL53L0_WrByte(Dev, 0xBB, 0xA7); + Status |= VL53L0_WrByte(Dev, 0xBC, 0x00); + Status |= VL53L0_WrByte(Dev, 0xBD, 0xA6); + Status |= VL53L0_WrByte(Dev, 0xBE, 0x01); + Status |= VL53L0_WrByte(Dev, 0xBF, 0x02); + Status |= VL53L0_WrByte(Dev, 0xC0, 0x04); + Status |= VL53L0_WrByte(Dev, 0xC1, 0x30); + Status |= VL53L0_WrByte(Dev, 0xC2, 0x00); + Status |= VL53L0_WrByte(Dev, 0xC3, 0x04); + Status |= VL53L0_WrByte(Dev, 0xC4, 0x28); + Status |= VL53L0_WrByte(Dev, 0xC5, 0x60); + Status |= VL53L0_WrByte(Dev, 0xC6, 0x2D); + Status |= VL53L0_WrByte(Dev, 0xC7, 0x89); + Status |= VL53L0_WrByte(Dev, 0xC8, 0x00); + Status |= VL53L0_WrByte(Dev, 0xC9, 0x0B); + Status |= VL53L0_WrByte(Dev, 0xCA, 0x00); + Status |= VL53L0_WrByte(Dev, 0xCB, 0x0B); + Status |= VL53L0_WrByte(Dev, 0xCC, 0x00); + Status |= VL53L0_WrByte(Dev, 0xCD, 0xA1); + Status |= VL53L0_WrByte(Dev, 0xCE, 0x00); + Status |= VL53L0_WrByte(Dev, 0xCF, 0xA0); + Status |= VL53L0_WrByte(Dev, 0xD0, 0x00); + Status |= VL53L0_WrByte(Dev, 0xD1, 0x04); + Status |= VL53L0_WrByte(Dev, 0xD2, 0x00); + Status |= VL53L0_WrByte(Dev, 0xD3, 0x25); + Status |= VL53L0_WrByte(Dev, 0xD4, 0x00); + Status |= VL53L0_WrByte(Dev, 0xD5, 0x2E); + Status |= VL53L0_WrByte(Dev, 0xD6, 0x00); + Status |= VL53L0_WrByte(Dev, 0xD7, 0x25); + Status |= VL53L0_WrByte(Dev, 0xD8, 0x00); + Status |= VL53L0_WrByte(Dev, 0xD9, 0x2E); + Status |= VL53L0_WrByte(Dev, 0xDA, 0x03); + Status |= VL53L0_WrByte(Dev, 0xDB, 0xF3); + Status |= VL53L0_WrByte(Dev, 0xDC, 0x03); + Status |= VL53L0_WrByte(Dev, 0xDD, 0xEA); + Status |= VL53L0_WrByte(Dev, 0xDE, 0x28); + Status |= VL53L0_WrByte(Dev, 0xDF, 0x58); + Status |= VL53L0_WrByte(Dev, 0xE0, 0x2C); + Status |= VL53L0_WrByte(Dev, 0xE1, 0xD9); + Status |= VL53L0_WrByte(Dev, 0xE2, 0x00); + Status |= VL53L0_WrByte(Dev, 0xE3, 0x0B); + Status |= VL53L0_WrByte(Dev, 0xE4, 0x00); + Status |= VL53L0_WrByte(Dev, 0xE5, 0x0B); + Status |= VL53L0_WrByte(Dev, 0xE6, 0x00); + Status |= VL53L0_WrByte(Dev, 0xE7, 0xA1); + Status |= VL53L0_WrByte(Dev, 0xE8, 0x00); + Status |= VL53L0_WrByte(Dev, 0xE9, 0xA0); + Status |= VL53L0_WrByte(Dev, 0xEA, 0x00); + Status |= VL53L0_WrByte(Dev, 0xEB, 0x04); + Status |= VL53L0_WrByte(Dev, 0xEC, 0x01); + Status |= VL53L0_WrByte(Dev, 0xED, 0x26); + Status |= VL53L0_WrByte(Dev, 0xEE, 0x00); + Status |= VL53L0_WrByte(Dev, 0xEF, 0xDC); + Status |= VL53L0_WrByte(Dev, 0xF0, 0x28); + Status |= VL53L0_WrByte(Dev, 0xF1, 0x58); + Status |= VL53L0_WrByte(Dev, 0xF2, 0x2F); + Status |= VL53L0_WrByte(Dev, 0xF3, 0x21); + Status |= VL53L0_WrByte(Dev, 0xF4, 0x00); + Status |= VL53L0_WrByte(Dev, 0xF5, 0x0B); + Status |= VL53L0_WrByte(Dev, 0xF6, 0x00); + Status |= VL53L0_WrByte(Dev, 0xF7, 0x0B); + Status |= VL53L0_WrByte(Dev, 0xF8, 0x00); + Status |= VL53L0_WrByte(Dev, 0xF9, 0xA1); + Status |= VL53L0_WrByte(Dev, 0xFA, 0x00); + Status |= VL53L0_WrByte(Dev, 0xFB, 0xA0); + Status |= VL53L0_WrByte(Dev, 0xFC, 0x00); + Status |= VL53L0_WrByte(Dev, 0xFD, 0x04); + Status |= VL53L0_WrWord(Dev, 0xFE, 0x01E3); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x0F); + Status |= VL53L0_WrByte(Dev, 0x00, 0x04); + Status |= VL53L0_WrByte(Dev, 0x01, 0x48); + Status |= VL53L0_WrByte(Dev, 0x02, 0x01); + Status |= VL53L0_WrByte(Dev, 0x03, 0x60); + Status |= VL53L0_WrByte(Dev, 0x04, 0x09); + Status |= VL53L0_WrByte(Dev, 0x05, 0xA4); + Status |= VL53L0_WrByte(Dev, 0x06, 0x05); + Status |= VL53L0_WrByte(Dev, 0x07, 0xB8); + Status |= VL53L0_WrByte(Dev, 0x08, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x09, 0x07); + Status |= VL53L0_WrByte(Dev, 0x0A, 0x00); + Status |= VL53L0_WrByte(Dev, 0x0B, 0x60); + Status |= VL53L0_WrByte(Dev, 0x0C, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x0D, 0x6B); + Status |= VL53L0_WrByte(Dev, 0x0E, 0x01); + Status |= VL53L0_WrByte(Dev, 0x0F, 0x64); + Status |= VL53L0_WrByte(Dev, 0x10, 0x04); + Status |= VL53L0_WrByte(Dev, 0x11, 0x3C); + Status |= VL53L0_WrByte(Dev, 0x12, 0x00); + Status |= VL53L0_WrByte(Dev, 0x13, 0x60); + Status |= VL53L0_WrByte(Dev, 0x14, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x15, 0x74); + Status |= VL53L0_WrByte(Dev, 0x16, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x17, 0x02); + Status |= VL53L0_WrByte(Dev, 0x18, 0x28); + Status |= VL53L0_WrByte(Dev, 0x19, 0x02); + Status |= VL53L0_WrByte(Dev, 0x1A, 0x28); + Status |= VL53L0_WrByte(Dev, 0x1B, 0x03); + Status |= VL53L0_WrByte(Dev, 0x1C, 0x01); + Status |= VL53L0_WrByte(Dev, 0x1D, 0xA2); + Status |= VL53L0_WrByte(Dev, 0x1E, 0x07); + Status |= VL53L0_WrByte(Dev, 0x1F, 0x8E); + Status |= VL53L0_WrByte(Dev, 0x20, 0x28); + Status |= VL53L0_WrByte(Dev, 0x21, 0x50); + Status |= VL53L0_WrByte(Dev, 0x22, 0x2E); + Status |= VL53L0_WrByte(Dev, 0x23, 0xC9); + Status |= VL53L0_WrByte(Dev, 0x24, 0x00); + Status |= VL53L0_WrByte(Dev, 0x25, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x26, 0x00); + Status |= VL53L0_WrByte(Dev, 0x27, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x28, 0x00); + Status |= VL53L0_WrByte(Dev, 0x29, 0xA1); + Status |= VL53L0_WrByte(Dev, 0x2A, 0x00); + Status |= VL53L0_WrByte(Dev, 0x2B, 0xA0); + Status |= VL53L0_WrByte(Dev, 0x2C, 0x28); + Status |= VL53L0_WrByte(Dev, 0x2D, 0x00); + Status |= VL53L0_WrByte(Dev, 0x2E, 0x00); + Status |= VL53L0_WrByte(Dev, 0x2F, 0x04); + Status |= VL53L0_WrByte(Dev, 0x30, 0x01); + Status |= VL53L0_WrByte(Dev, 0x31, 0x00); + Status |= VL53L0_WrByte(Dev, 0x32, 0x01); + Status |= VL53L0_WrByte(Dev, 0x33, 0xA0); + Status |= VL53L0_WrByte(Dev, 0x34, 0x11); + Status |= VL53L0_WrByte(Dev, 0x35, 0x00); + Status |= VL53L0_WrByte(Dev, 0x36, 0x09); + Status |= VL53L0_WrByte(Dev, 0x37, 0x05); + Status |= VL53L0_WrByte(Dev, 0x38, 0x11); + Status |= VL53L0_WrByte(Dev, 0x39, 0x08); + Status |= VL53L0_WrByte(Dev, 0x3A, 0x09); + Status |= VL53L0_WrByte(Dev, 0x3B, 0x03); + Status |= VL53L0_WrByte(Dev, 0x3C, 0x11); + Status |= VL53L0_WrByte(Dev, 0x3D, 0x18); + Status |= VL53L0_WrByte(Dev, 0x3E, 0x09); + Status |= VL53L0_WrByte(Dev, 0x3F, 0x01); + Status |= VL53L0_WrByte(Dev, 0x40, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x41, 0x04); + Status |= VL53L0_WrByte(Dev, 0x42, 0x0C); + Status |= VL53L0_WrByte(Dev, 0x43, 0x00); + Status |= VL53L0_WrByte(Dev, 0x44, 0x0A); + Status |= VL53L0_WrByte(Dev, 0x45, 0x01); + Status |= VL53L0_WrByte(Dev, 0x46, 0x0C); + Status |= VL53L0_WrByte(Dev, 0x47, 0x08); + Status |= VL53L0_WrByte(Dev, 0x48, 0x0A); + Status |= VL53L0_WrByte(Dev, 0x49, 0x01); + Status |= VL53L0_WrByte(Dev, 0x4A, 0x28); + Status |= VL53L0_WrByte(Dev, 0x4B, 0x40); + Status |= VL53L0_WrByte(Dev, 0x4C, 0x2F); + Status |= VL53L0_WrByte(Dev, 0x4D, 0xD1); + Status |= VL53L0_WrByte(Dev, 0x4E, 0x00); + Status |= VL53L0_WrByte(Dev, 0x4F, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x50, 0x00); + Status |= VL53L0_WrByte(Dev, 0x51, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x52, 0x00); + Status |= VL53L0_WrByte(Dev, 0x53, 0xA1); + Status |= VL53L0_WrByte(Dev, 0x54, 0x00); + Status |= VL53L0_WrByte(Dev, 0x55, 0xA0); + Status |= VL53L0_WrByte(Dev, 0x56, 0x00); + Status |= VL53L0_WrByte(Dev, 0x57, 0x04); + Status |= VL53L0_WrByte(Dev, 0x58, 0x28); + Status |= VL53L0_WrByte(Dev, 0x59, 0x11); + Status |= VL53L0_WrByte(Dev, 0x5A, 0x05); + Status |= VL53L0_WrByte(Dev, 0x5B, 0x48); + Status |= VL53L0_WrByte(Dev, 0x5C, 0x00); + Status |= VL53L0_WrByte(Dev, 0x5D, 0x60); + Status |= VL53L0_WrByte(Dev, 0x5E, 0x0A); + Status |= VL53L0_WrByte(Dev, 0x5F, 0xA2); + Status |= VL53L0_WrByte(Dev, 0x60, 0x00); + Status |= VL53L0_WrByte(Dev, 0x61, 0x60); + Status |= VL53L0_WrByte(Dev, 0x62, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x63, 0x3E); + Status |= VL53L0_WrByte(Dev, 0x64, 0x01); + Status |= VL53L0_WrByte(Dev, 0x65, 0x00); + Status |= VL53L0_WrByte(Dev, 0x66, 0x00); + Status |= VL53L0_WrByte(Dev, 0x67, 0x54); + Status |= VL53L0_WrByte(Dev, 0x68, 0x05); + Status |= VL53L0_WrByte(Dev, 0x69, 0x80); + Status |= VL53L0_WrByte(Dev, 0x6A, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x6B, 0x03); + Status |= VL53L0_WrByte(Dev, 0x6C, 0x28); + Status |= VL53L0_WrByte(Dev, 0x6D, 0x38); + Status |= VL53L0_WrByte(Dev, 0x6E, 0x28); + Status |= VL53L0_WrByte(Dev, 0x6F, 0xE1); + Status |= VL53L0_WrByte(Dev, 0x70, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x71, 0x02); + Status |= VL53L0_WrByte(Dev, 0x72, 0x28); + Status |= VL53L0_WrByte(Dev, 0x73, 0x38); + Status |= VL53L0_WrByte(Dev, 0x74, 0x29); + Status |= VL53L0_WrByte(Dev, 0x75, 0x21); + Status |= VL53L0_WrByte(Dev, 0x76, 0x00); + Status |= VL53L0_WrByte(Dev, 0x77, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x78, 0x00); + Status |= VL53L0_WrByte(Dev, 0x79, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x7A, 0x00); + Status |= VL53L0_WrByte(Dev, 0x7B, 0xA1); + Status |= VL53L0_WrByte(Dev, 0x7C, 0x00); + Status |= VL53L0_WrByte(Dev, 0x7D, 0xA0); + Status |= VL53L0_WrByte(Dev, 0x7E, 0x00); + Status |= VL53L0_WrByte(Dev, 0x7F, 0x04); + Status |= VL53L0_WrByte(Dev, 0x80, 0x03); + Status |= VL53L0_WrByte(Dev, 0x81, 0x33); + Status |= VL53L0_WrByte(Dev, 0x82, 0x03); + Status |= VL53L0_WrByte(Dev, 0x83, 0x6A); + Status |= VL53L0_WrByte(Dev, 0x84, 0x03); + Status |= VL53L0_WrByte(Dev, 0x85, 0x61); + Status |= VL53L0_WrByte(Dev, 0x86, 0x05); + Status |= VL53L0_WrByte(Dev, 0x87, 0xF9); + Status |= VL53L0_WrByte(Dev, 0x88, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x89, 0x03); + Status |= VL53L0_WrByte(Dev, 0x8A, 0x28); + Status |= VL53L0_WrByte(Dev, 0x8B, 0x00); + Status |= VL53L0_WrByte(Dev, 0x8C, 0x28); + Status |= VL53L0_WrByte(Dev, 0x8D, 0x09); + Status |= VL53L0_WrByte(Dev, 0x8E, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x8F, 0x03); + Status |= VL53L0_WrByte(Dev, 0x90, 0x28); + Status |= VL53L0_WrByte(Dev, 0x91, 0x66); + Status |= VL53L0_WrByte(Dev, 0x92, 0x2A); + Status |= VL53L0_WrByte(Dev, 0x93, 0x67); + Status |= VL53L0_WrByte(Dev, 0x94, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x95, 0x02); + Status |= VL53L0_WrByte(Dev, 0x96, 0x28); + Status |= VL53L0_WrByte(Dev, 0x97, 0x66); + Status |= VL53L0_WrByte(Dev, 0x98, 0x2A); + Status |= VL53L0_WrByte(Dev, 0x99, 0xAF); + Status |= VL53L0_WrByte(Dev, 0x9A, 0x00); + Status |= VL53L0_WrByte(Dev, 0x9B, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x9C, 0x00); + Status |= VL53L0_WrByte(Dev, 0x9D, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x9E, 0x00); + Status |= VL53L0_WrByte(Dev, 0x9F, 0xA7); + Status |= VL53L0_WrByte(Dev, 0xA0, 0x00); + Status |= VL53L0_WrByte(Dev, 0xA1, 0xA6); + Status |= VL53L0_WrByte(Dev, 0xA2, 0x00); + Status |= VL53L0_WrByte(Dev, 0xA3, 0x04); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x04); + + Status |= VL53L0_WrByte(Dev, 0x79, 0x0B); + Status |= VL53L0_WrByte(Dev, 0x7B, 0x16); + Status |= VL53L0_WrByte(Dev, 0x7D, 0x2B); + Status |= VL53L0_WrByte(Dev, 0x7F, 0x3B); + Status |= VL53L0_WrByte(Dev, 0x81, 0x59); + Status |= VL53L0_WrByte(Dev, 0x83, 0x62); + Status |= VL53L0_WrByte(Dev, 0x85, 0x69); + Status |= VL53L0_WrByte(Dev, 0x87, 0x76); + Status |= VL53L0_WrByte(Dev, 0x89, 0x7F); + Status |= VL53L0_WrByte(Dev, 0x8B, 0x98); + Status |= VL53L0_WrByte(Dev, 0x8D, 0xAC); + Status |= VL53L0_WrByte(Dev, 0x8F, 0xC0); + Status |= VL53L0_WrByte(Dev, 0x90, 0x0C); + Status |= VL53L0_WrByte(Dev, 0x91, 0x30); + Status |= VL53L0_WrByte(Dev, 0x92, 0x28); + Status |= VL53L0_WrByte(Dev, 0x93, 0x02); + Status |= VL53L0_WrByte(Dev, 0x94, 0x37); + Status |= VL53L0_WrByte(Dev, 0x95, 0x62); + + Status |= VL53L0_WrByte(Dev, 0x96, 0x04); + Status |= VL53L0_WrByte(Dev, 0x97, 0x08); + Status |= VL53L0_WrByte(Dev, 0x98, 0x07); + Status |= VL53L0_WrByte(Dev, 0x99, 0x18); + Status |= VL53L0_WrByte(Dev, 0x9A, 0x07); + Status |= VL53L0_WrByte(Dev, 0x9B, 0x6F); + Status |= VL53L0_WrByte(Dev, 0x9C, 0x05); + Status |= VL53L0_WrByte(Dev, 0x9D, 0xD4); + Status |= VL53L0_WrByte(Dev, 0x9E, 0x0A); + Status |= VL53L0_WrByte(Dev, 0x9F, 0x6E); + Status |= VL53L0_WrByte(Dev, 0xA0, 0x09); + Status |= VL53L0_WrByte(Dev, 0xA1, 0xA2); + Status |= VL53L0_WrByte(Dev, 0xA2, 0x0C); + Status |= VL53L0_WrByte(Dev, 0xA3, 0xAA); + Status |= VL53L0_WrByte(Dev, 0xA4, 0x0B); + Status |= VL53L0_WrByte(Dev, 0xA5, 0x97); + Status |= VL53L0_WrByte(Dev, 0xA6, 0x0B); + Status |= VL53L0_WrByte(Dev, 0xA7, 0xD8); + Status |= VL53L0_WrByte(Dev, 0xA8, 0x0A); + Status |= VL53L0_WrByte(Dev, 0xA9, 0xD7); + Status |= VL53L0_WrByte(Dev, 0xAA, 0x08); + Status |= VL53L0_WrByte(Dev, 0xAB, 0xF6); + Status |= VL53L0_WrByte(Dev, 0xAC, 0x07); + Status |= VL53L0_WrByte(Dev, 0xAD, 0x1A); + Status |= VL53L0_WrByte(Dev, 0xAE, 0x0C); + Status |= VL53L0_WrByte(Dev, 0xAF, 0x49); + Status |= VL53L0_WrByte(Dev, 0xB0, 0x09); + Status |= VL53L0_WrByte(Dev, 0xB1, 0x17); + Status |= VL53L0_WrByte(Dev, 0xB2, 0x03); + Status |= VL53L0_WrByte(Dev, 0xB3, 0xCD); + Status |= VL53L0_WrByte(Dev, 0xB4, 0x04); + Status |= VL53L0_WrByte(Dev, 0xB5, 0x55); + + Status |= VL53L0_WrByte(Dev, 0x72, 0xFF); + Status |= VL53L0_WrByte(Dev, 0x73, 0xFF); + + Status |= VL53L0_WrByte(Dev, 0x74, 0xE0); + + Status |= VL53L0_WrByte(Dev, 0x70, 0x01); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x01); + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + + if (Status != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L010_EXTERNAL VL53L0_Error VL53L010_check_part_used(VL53L0_DEV Dev, + uint8_t *Revision, + VL53L0_DeviceInfo_t * + pVL53L0_DeviceInfo) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t ModuleIdInt; + char *ProductId_tmp; + + LOG_FUNCTION_START(""); + + Status = VL53L010_get_info_from_device(Dev); + + if (Status == VL53L0_ERROR_NONE) { + ModuleIdInt = + VL53L010_GETDEVICESPECIFICPARAMETER(Dev, ModuleId); + + if (ModuleIdInt == 0) { + *Revision = 0; + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->ProductId, ""); + } else { + *Revision = + VL53L010_GETDEVICESPECIFICPARAMETER(Dev, Revision); + ProductId_tmp = + VL53L010_GETDEVICESPECIFICPARAMETER(Dev, ProductId); + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->ProductId, + ProductId_tmp); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L010_EXTERNAL VL53L0_Error VL53L010_get_info_from_device(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t byte; + uint32_t TmpDWord; + VL53L0_DeviceSpecificParameters_t DeviceSpecificParameters; + uint8_t ModuleId; + uint8_t Revision; + uint8_t ReferenceSpadCount; + uint8_t ReferenceSpadType; + char ProductId[19]; + char *ProductId_tmp; + uint8_t ReadDataFromDeviceDone; + + LOG_FUNCTION_START(""); + + ReadDataFromDeviceDone = VL53L010_GETDEVICESPECIFICPARAMETER(Dev, + ReadDataFromDeviceDone); + + /* This access is done only once after that a GetDeviceInfo or + * datainit is done + */ + if (ReadDataFromDeviceDone == 0) { + + Status |= VL53L0_WrByte(Dev, 0x80, 0x01); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x06); + Status |= VL53L0_RdByte(Dev, 0x83, &byte); + Status |= VL53L0_WrByte(Dev, 0x83, byte | 4); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x07); + Status |= VL53L0_WrByte(Dev, 0x81, 0x01); + + Status |= VL53L0_PollingDelay(Dev); + + Status |= VL53L0_WrByte(Dev, 0x80, 0x01); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x6b); + Status |= VL53L010_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + ReferenceSpadCount = (uint8_t) ((TmpDWord >> 8) & 0x07f); + ReferenceSpadType = (uint8_t) ((TmpDWord >> 15) & 0x01); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x02); + Status |= VL53L010_device_read_strobe(Dev); + Status |= VL53L0_RdByte(Dev, 0x90, &ModuleId); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x7B); + Status |= VL53L010_device_read_strobe(Dev); + Status |= VL53L0_RdByte(Dev, 0x90, &Revision); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x77); + Status |= VL53L010_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + ProductId[0] = (char)((TmpDWord >> 25) & 0x07f); + ProductId[1] = (char)((TmpDWord >> 18) & 0x07f); + ProductId[2] = (char)((TmpDWord >> 11) & 0x07f); + ProductId[3] = (char)((TmpDWord >> 4) & 0x07f); + + byte = (uint8_t) ((TmpDWord & 0x00f) << 3); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x78); + Status |= VL53L010_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + ProductId[4] = (char)(byte + ((TmpDWord >> 29) & 0x07f)); + ProductId[5] = (char)((TmpDWord >> 22) & 0x07f); + ProductId[6] = (char)((TmpDWord >> 15) & 0x07f); + ProductId[7] = (char)((TmpDWord >> 8) & 0x07f); + ProductId[8] = (char)((TmpDWord >> 1) & 0x07f); + + byte = (uint8_t) ((TmpDWord & 0x001) << 6); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x79); + + Status |= VL53L010_device_read_strobe(Dev); + + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + ProductId[9] = (char)(byte + ((TmpDWord >> 26) & 0x07f)); + ProductId[10] = (char)((TmpDWord >> 19) & 0x07f); + ProductId[11] = (char)((TmpDWord >> 12) & 0x07f); + ProductId[12] = (char)((TmpDWord >> 5) & 0x07f); + + byte = (uint8_t) ((TmpDWord & 0x01f) << 2); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x80); + + Status |= VL53L010_device_read_strobe(Dev); + + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + ProductId[13] = (char)(byte + ((TmpDWord >> 30) & 0x07f)); + ProductId[14] = (char)((TmpDWord >> 23) & 0x07f); + ProductId[15] = (char)((TmpDWord >> 16) & 0x07f); + ProductId[16] = (char)((TmpDWord >> 9) & 0x07f); + ProductId[17] = (char)((TmpDWord >> 2) & 0x07f); + ProductId[18] = '\0'; + + Status |= VL53L0_WrByte(Dev, 0x81, 0x00); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x06); + Status |= VL53L0_RdByte(Dev, 0x83, &byte); + Status |= VL53L0_WrByte(Dev, 0x83, byte & 0xfb); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x01); + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0x80, 0x00); + + if (Status == VL53L0_ERROR_NONE) { + VL53L010_SETDEVICESPECIFICPARAMETER(Dev, + ModuleId, ModuleId); + + VL53L010_SETDEVICESPECIFICPARAMETER(Dev, + Revision, Revision); + + VL53L010_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadCount, + ReferenceSpadCount); + + VL53L010_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadType, + ReferenceSpadType); + + ProductId_tmp = VL53L010_GETDEVICESPECIFICPARAMETER(Dev, + ProductId); + VL53L0_COPYSTRING(ProductId_tmp, ProductId); + + VL53L010_SETDEVICESPECIFICPARAMETER(Dev, + ReadDataFromDeviceDone, + 1); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +uint32_t VL53L010_isqrt(uint32_t num) +{ + + /* + * Implements an integer square root + * + * From: http://en.wikipedia.org/wiki/Methods_of_computing_square_roots + */ + + uint32_t res = 0; + uint32_t bit = 1 << 30; + /* The second-to-top bit is set: 1 << 14 for + * 16-bits, 1 << 30 for 32 bits + */ + /* "bit" starts at the highest power of four <= the argument. */ + while (bit > num) + bit >>= 2; + + while (bit != 0) { + if (num >= res + bit) { + num -= res + bit; + res = (res >> 1) + bit; + } else + res >>= 1; + + bit >>= 2; + } + + return res; +} + +VL53L0_Error VL53L010_device_read_strobe(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t strobe; + uint32_t LoopNb; + + LOG_FUNCTION_START(""); + + Status |= VL53L0_WrByte(Dev, 0x83, 0x00); + + /* polling use timeout to avoid deadlock */ + if (Status == VL53L0_ERROR_NONE) { + LoopNb = 0; + do { + Status = VL53L0_RdByte(Dev, 0x83, &strobe); + if ((strobe != 0x00) || Status != VL53L0_ERROR_NONE) + break; + + LoopNb = LoopNb + 1; + } while (LoopNb < VL53L0_DEFAULT_MAX_LOOP); + + if (LoopNb >= VL53L0_DEFAULT_MAX_LOOP) + Status = VL53L0_ERROR_TIME_OUT; + + } + + Status |= VL53L0_WrByte(Dev, 0x83, 0x01); + + LOG_FUNCTION_END(Status); + return Status; + +} + +uint32_t VL53L010_quadrature_sum(uint32_t a, uint32_t b) +{ + /* + * Implements a quadrature sum + * + * rea = sqrt(a^2 + b^2) + * + * Trap overflow case max input value is 65535 (16-bit value) + * as internal calc are 32-bit wide + * + * If overflow then seta output to maximum + */ + uint32_t res = 0; + + if (a > 65535 || b > 65535) + res = 65535; + else + res = VL53L010_isqrt(a * a + b * b); + + return res; +} + +VL53L0_Error VL53L010_get_jmp_vcsel_ambient_rate(VL53L0_DEV Dev, + uint32_t *pAmbient_rate_kcps, + uint32_t *pVcsel_rate_kcps, + uint32_t * + pSignalTotalEventsRtn) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t encodedTimeOut; + + uint32_t total_periods_elapsed_rtn__macrop = 0; + uint32_t result_core__total_periods_elapsed_rtn = 0; + uint32_t rngb1_config__timeout__macrop = 0; + uint32_t rngb2_config__timeout__macrop = 0; + uint32_t result_core__ambient_window_events_rtn = 0; + uint32_t result_core__signal_total_events_rtn = 0; + uint8_t last_woi_period; + uint8_t rnga_config__vcsel_period; + uint8_t rngb1_config__vcsel_period; + uint8_t rngb2_config__vcsel_period; + uint8_t global_config__vcsel_width; + + uint32_t ambient_duration_us = 0; + uint32_t vcsel_duration_us = 0; + + uint32_t pll_period_us = 0; + + LOG_FUNCTION_START(""); + + /* read the following */ + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_RdDWord(Dev, 0xC8, + &result_core__total_periods_elapsed_rtn); + Status |= VL53L0_RdDWord(Dev, 0xF0, &pll_period_us); + Status |= VL53L0_RdDWord(Dev, 0xbc, + &result_core__ambient_window_events_rtn); + Status |= VL53L0_RdDWord(Dev, 0xc4, + &result_core__signal_total_events_rtn); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + if (Status == VL53L0_ERROR_NONE) { + result_core__total_periods_elapsed_rtn = + (int32_t) (result_core__total_periods_elapsed_rtn & + 0x00ffffff); + pll_period_us = (int32_t) (pll_period_us & 0x3ffff); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdWord(Dev, VL53L010_REG_RNGB1_TIMEOUT_MSB, + &encodedTimeOut); + if (Status == VL53L0_ERROR_NONE) + rngb1_config__timeout__macrop = + VL53L010_decode_timeout(encodedTimeOut) - 1; + + } + + if (Status == VL53L0_ERROR_NONE) { + Status = + VL53L0_RdByte(Dev, VL53L010_REG_RNGA_CONFIG_VCSEL_PERIOD, + &rnga_config__vcsel_period); + } + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdByte(Dev, + VL53L010_REG_RNGB1_CONFIG_VCSEL_PERIOD, + &rngb1_config__vcsel_period); + } + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdByte(Dev, + VL53L010_REG_RNGB2_CONFIG_VCSEL_PERIOD, + &rngb2_config__vcsel_period); + } + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_RdByte(Dev, 0x32, &global_config__vcsel_width); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdWord(Dev, VL53L010_REG_RNGB2_TIMEOUT_MSB, + &encodedTimeOut); + if (Status == VL53L0_ERROR_NONE) + rngb2_config__timeout__macrop = + VL53L010_decode_timeout(encodedTimeOut) - 1; + + } + + if (Status == VL53L0_ERROR_NONE) { + total_periods_elapsed_rtn__macrop = + result_core__total_periods_elapsed_rtn + 1; + + if (result_core__total_periods_elapsed_rtn == + rngb1_config__timeout__macrop) { + last_woi_period = rngb1_config__vcsel_period; + } else if (result_core__total_periods_elapsed_rtn == + rngb2_config__timeout__macrop) { + last_woi_period = rngb2_config__vcsel_period; + } else { + last_woi_period = rnga_config__vcsel_period; + + } + /* 512 = 1<<9 ==> 24-9=15 */ + ambient_duration_us = last_woi_period * + total_periods_elapsed_rtn__macrop * pll_period_us; + ambient_duration_us = ambient_duration_us / 1000; + + if (ambient_duration_us != 0) { + *pAmbient_rate_kcps = ((1 << 15) * + result_core__ambient_window_events_rtn) + / ambient_duration_us; + } else { + Status = VL53L0_ERROR_DIVISION_BY_ZERO; + } + + if (Status == VL53L0_ERROR_NONE) { + + /* 2048 = 1<<11 ==> 24-11=13 */ + vcsel_duration_us = + (10 * global_config__vcsel_width + 4) + * total_periods_elapsed_rtn__macrop * pll_period_us; + vcsel_duration_us = vcsel_duration_us / 10000; + + if (vcsel_duration_us != 0) { + *pVcsel_rate_kcps = ((1 << 13) * + result_core__signal_total_events_rtn) + / vcsel_duration_us; + *pSignalTotalEventsRtn = + result_core__signal_total_events_rtn; + } else { + Status = VL53L0_ERROR_DIVISION_BY_ZERO; + } + + } + } + + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L0_Error VL53L010_calc_sigma_estimate(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t + *pRangingMeasurementData, + FixPoint1616_t *pSigmaEstimate) +{ + /* Expressed in 100ths of a ns, i.e. centi-ns */ + const uint32_t cPulseEffectiveWidth_centi_ns = 800; + /* Expressed in 100ths of a ns, i.e. centi-ns */ + const uint32_t cAmbientEffectiveWidth_centi_ns = 600; + const FixPoint1616_t cSigmaEstRef = 0x00000042; + /* pico secs */ + const uint32_t cVcselPulseWidth_ps = 4700; + const FixPoint1616_t cSigmaEstMax = 0x028F87AE; + /* Time Of Flight per mm (6.6 pico secs) */ + const FixPoint1616_t cTOF_per_mm_ps = 0x0006999A; + const uint32_t c16BitRoundingParam = 0x00008000; + const FixPoint1616_t cMaxXTalk_kcps = 0x00320000; + + uint32_t signalTotalEventsRtn; + FixPoint1616_t sigmaEstimateP1; + FixPoint1616_t sigmaEstimateP2; + FixPoint1616_t sigmaEstimateP3; + FixPoint1616_t deltaT_ps; + FixPoint1616_t pwMult; + FixPoint1616_t sigmaEstRtn; + FixPoint1616_t sigmaEstimate; + FixPoint1616_t xTalkCorrection; + uint32_t signalTotalEventsRtnRawVal; + FixPoint1616_t ambientRate_kcps; + FixPoint1616_t vcselRate_kcps; + FixPoint1616_t xTalkCompRate_mcps; + uint32_t xTalkCompRate_kcps; + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + FixPoint1616_t diff1_mcps; + FixPoint1616_t diff2_mcps; + FixPoint1616_t sqr1; + FixPoint1616_t sqr2; + FixPoint1616_t sqrSum; + FixPoint1616_t sqrtResult_centi_ns; + FixPoint1616_t sqrtResult; + + /*! \addtogroup calc_sigma_estimate + * @{ + * + * Estimates the range sigma based on the + * + * - vcsel_rate_kcps + * - ambient_rate_kcps + * - signal_total_events + * - xtalk_rate + * + * and the following parameters + * + * - SigmaEstRefArray + * - SigmaEstEffPulseWidth + * - SigmaEstEffAmbWidth + */ + + LOG_FUNCTION_START(""); + + VL53L010_GETPARAMETERFIELD(Dev, XTalkCompensationRateMegaCps, + xTalkCompRate_mcps); + /* + * We work in kcps rather than mcps as this helps keep + * within the confines of the 32 Fix1616 type. + */ + + xTalkCompRate_kcps = xTalkCompRate_mcps * 1000; + if (xTalkCompRate_kcps > cMaxXTalk_kcps) + xTalkCompRate_kcps = cMaxXTalk_kcps; + + Status = VL53L010_get_jmp_vcsel_ambient_rate(Dev, + &ambientRate_kcps, + &vcselRate_kcps, + &signalTotalEventsRtnRawVal); + + if (Status == VL53L0_ERROR_NONE) { + if (vcselRate_kcps == 0) { + Status = VL53L0_ERROR_DIVISION_BY_ZERO; + } else { + signalTotalEventsRtn = signalTotalEventsRtnRawVal; + if (signalTotalEventsRtn < 1) + signalTotalEventsRtn = 1; + + /* + * Calculate individual components of the main + * equation - replicating the equation implemented in + * the script OpenAll_Ewok_ranging_data.jsl. + * + * sigmaEstimateP1 represents the effective pulse width, + * which is a tuning parameter, rather than a real + * value. + * + * sigmaEstimateP2 represents the ambient/signal rate + * ratio expressed as a multiple of the effective + * ambient width (tuning parameter). + * + * sigmaEstimateP3 provides the signal event component, + * with the knowledge that + * - Noise of a square pulse is 1/sqrt(12) of the + * pulse width. + * - at 0Lux, sigma is proportional to + * effectiveVcselPulseWidth / + * sqrt(12 * signalTotalEvents) + * + * deltaT_ps represents the time of flight in pico secs + * for the current range measurement, using the + * "TOF per mm" constant (in ps). + */ + + sigmaEstimateP1 = cPulseEffectiveWidth_centi_ns; + + /* + * ((FixPoint1616 << 16)* uint32)/FixPoint1616 = + * FixPoint1616 + */ + sigmaEstimateP2 = (ambientRate_kcps << 16) / + vcselRate_kcps; + sigmaEstimateP2 *= cAmbientEffectiveWidth_centi_ns; + + sigmaEstimateP3 = 2 * + VL53L010_isqrt(signalTotalEventsRtn * 12); + + /* uint32 * FixPoint1616 = FixPoint1616 */ + deltaT_ps = + pRangingMeasurementData->RangeMilliMeter * + cTOF_per_mm_ps; + + /* + * vcselRate - xtalkCompRate + * (uint32 << 16) - FixPoint1616 = FixPoint1616. + * Divide result by 1000 to convert to mcps. + * 500 is added to ensure rounding when integer + * division truncates. + */ + diff1_mcps = (((vcselRate_kcps << 16) - + xTalkCompRate_kcps) + 500) / 1000; + + /* vcselRate + xtalkCompRate */ + diff2_mcps = (((vcselRate_kcps << 16) + + xTalkCompRate_kcps) + 500) / 1000; + + /* Shift by 12 bits to increase resolution prior to the + * division + */ + diff1_mcps <<= 12; + + /* FixPoint0428/FixPoint1616 = FixPoint2012 */ + xTalkCorrection = abs(diff1_mcps / diff2_mcps); + + /* FixPoint2012 << 4 = FixPoint1616 */ + xTalkCorrection <<= 4; + + /* FixPoint1616/uint32 = FixPoint1616 */ + pwMult = deltaT_ps / cVcselPulseWidth_ps; + /* smaller than 1.0f */ + + /* + * FixPoint1616 * FixPoint1616 = FixPoint3232, however + * both values are small enough such that32 bits will + * not be exceeded. + */ + pwMult *= ((1 << 16) - xTalkCorrection); + + /* (FixPoint3232 >> 16) = FixPoint1616 */ + pwMult = (pwMult + c16BitRoundingParam) >> 16; + + /* FixPoint1616 + FixPoint1616 = FixPoint1616 */ + pwMult += (1 << 16); + + /* + * At this point the value will be 1.xx, therefore if we + * square the value this will exceed 32 bits. To address + * this perform a single shift to the right before the + * multiplication. + */ + pwMult >>= 1; + /* FixPoint1715 * FixPoint1715 = FixPoint3430 */ + pwMult = pwMult * pwMult; + + /* (FixPoint3430 >> 14) = Fix1616 */ + pwMult >>= 14; + + /* FixPoint1616 * FixPoint1616 = FixPoint3232 */ + sqr1 = pwMult * sigmaEstimateP1; + + /* (FixPoint1616 >> 12) = FixPoint2804 */ + sqr1 = (sqr1 + 0x800) >> 12; + + /* FixPoint2804 * FixPoint2804 = FixPoint5608 */ + sqr1 *= sqr1; + + sqr2 = sigmaEstimateP2; + + /* (FixPoint1616 >> 12) = FixPoint2804 */ + sqr2 = (sqr2 + 0x800) >> 12; + + /* FixPoint2804 * FixPoint2804 = FixPoint5608 */ + sqr2 *= sqr2; + + /* FixPoint5608 + FixPoint5608 = FixPoint5608 */ + sqrSum = sqr1 + sqr2; + + /* SQRT(FixPoint5608) = FixPoint2804 */ + sqrtResult_centi_ns = VL53L010_isqrt(sqrSum); + + /* (FixPoint2804 << 12) = FixPoint1616 */ + sqrtResult_centi_ns <<= 12; + + /* + * Note that the Speed Of Light is expressed in um + * per 1E-10 seconds (2997). Therefore to get mm/ns + * we have to divide by 10000 + */ + sigmaEstRtn = + ((sqrtResult_centi_ns + 50) / 100 * + VL53L010_SPEED_OF_LIGHT_IN_AIR); + sigmaEstRtn /= (sigmaEstimateP3); + /* Add 5000 before dividing by 10000 to ensure + * rounding. + */ + sigmaEstRtn += 5000; + sigmaEstRtn /= 10000; + + /* FixPoint1616 * FixPoint1616 = FixPoint3232 */ + sqr1 = sigmaEstRtn * sigmaEstRtn; + /* FixPoint1616 * FixPoint1616 = FixPoint3232 */ + sqr2 = cSigmaEstRef * cSigmaEstRef; + + /* sqrt(FixPoint3232 << 12) = FixPoint1022 */ + sqrtResult = VL53L010_isqrt((sqr1 + sqr2) << 12); + sqrtResult = (sqrtResult + 0x20) >> 6; + /* + * Note that the Shift by 12bits increases resolution + * prior to the sqrt, therefore the result must be + * shifted by 6bits to the right to revert back to the + * FixPoint1616 format. + */ + + sigmaEstimate = 1000 * sqrtResult; + + if ((vcselRate_kcps < 1) || + (signalTotalEventsRtn < 1) || + (sigmaEstimate > cSigmaEstMax)) { + sigmaEstimate = cSigmaEstMax; + } + + *pSigmaEstimate = (uint32_t) (sigmaEstimate); + PALDevDataSet(Dev, SigmaEstimate, *pSigmaEstimate); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L010_get_pal_range_status(VL53L0_DEV Dev, + uint8_t DeviceRangeStatus, + FixPoint1616_t SignalRate, + FixPoint1616_t CrosstalkCompensation, + uint16_t EffectiveSpadRtnCount, + VL53L0_RangingMeasurementData_t * + pRangingMeasurementData, + uint8_t *pPalRangeStatus) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t tmpByte; + uint8_t SigmaLimitCheckEnable; + uint8_t SignalLimitCheckEnable; + FixPoint1616_t SigmaEstimate; + FixPoint1616_t SignalEstimate; + FixPoint1616_t SigmaLimitValue; + FixPoint1616_t SignalLimitValue; + uint8_t DeviceRangeStatusInternal = 0; + + LOG_FUNCTION_START(""); + + /* + * VL53L0 has a good ranging when the value of the + * DeviceRangeStatus = 11. This function will replace + * the value 0 with the value 11 in the DeviceRangeStatus. + * In addition, the SigmaEstimator is not included in the + * VL53L0 DeviceRangeStatus, this will be added in the + * PalRangeStatus. + */ + + DeviceRangeStatusInternal = ((DeviceRangeStatus & 0x78) >> 3); + + if (DeviceRangeStatusInternal == 11) + tmpByte = 0; + else if (DeviceRangeStatusInternal == 0) + tmpByte = 11; + else + tmpByte = DeviceRangeStatusInternal; + + /* + * Check if Sigma limit is enabled, if yes then do comparison with + * limit value and put the result back into pPalRangeStatus. + */ + Status = VL53L010_GetLimitCheckEnable(Dev, + VL53L010_CHECKENABLE_SIGMA_FINAL_RANGE, + &SigmaLimitCheckEnable); + + if ((SigmaLimitCheckEnable != 0) && (Status == VL53L0_ERROR_NONE)) { + /* + * compute the Sigma and check with limit + */ + Status = VL53L010_calc_sigma_estimate(Dev, + pRangingMeasurementData, + &SigmaEstimate); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L010_GetLimitCheckValue(Dev, + VL53L010_CHECKENABLE_SIGMA_FINAL_RANGE, + &SigmaLimitValue); + + if ((SigmaLimitValue > 0) && + (SigmaEstimate > SigmaLimitValue)) { + /* Limit Fail add 2^4 to range status */ + tmpByte += 16; + } + } + } + + /* + * Check if Signal limit is enabled, if yes then do comparison with + * limit value and put the result back into pPalRangeStatus. + */ + Status = VL53L010_GetLimitCheckEnable(Dev, + VL53L010_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &SignalLimitCheckEnable); + + if ((SignalLimitCheckEnable != 0) && (Status == VL53L0_ERROR_NONE)) { + /* + * compute the Signal and check with limit + */ + + SignalEstimate = (FixPoint1616_t) (SignalRate - + (FixPoint1616_t) + ((EffectiveSpadRtnCount * CrosstalkCompensation) >> 1)); + + PALDevDataSet(Dev, SignalEstimate, SignalEstimate); + + Status = VL53L010_GetLimitCheckValue(Dev, + VL53L010_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &SignalLimitValue); + + if ((SignalLimitValue > 0) && (SignalEstimate < + SignalLimitValue)) { + /* Limit Fail add 2^5 to range status */ + tmpByte += 32; + } + } + + if (Status == VL53L0_ERROR_NONE) + *pPalRangeStatus = tmpByte; + + LOG_FUNCTION_END(Status); + return Status; + +} diff --git a/drivers/input/misc/vl53L0/src/vl53l010_tuning.c b/drivers/input/misc/vl53L0/src/vl53l010_tuning.c new file mode 100644 index 000000000000..ee3f57872ffc --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l010_tuning.c @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include "vl53l010_tuning.h" + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__) + +#ifdef VL53L0_LOG_ENABLE +#define trace_print(level, ...) \ + trace_print_module_function(TRACE_MODULE_API,\ + level, TRACE_FUNCTION_NONE, ##__VA_ARGS__) +#endif + +/* + * ////////////////////////////////////////////////////// + * //// DEFAULT TUNING SETTINGS //// + * ////////////////////////////////////////////////////// + */ +VL53L0_Error VL53L010_load_tuning_settings(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + /* update 14_12_15_v11 */ + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + Status |= VL53L0_WrByte(Dev, 0x91, 0x3C); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0x54, 0x01); + Status |= VL53L0_WrByte(Dev, 0x33, 0x05); + Status |= VL53L0_WrByte(Dev, 0x32, 0x03); + Status |= VL53L0_WrByte(Dev, 0x30, 0x05); + Status |= VL53L0_WrByte(Dev, 0x50, 0x05); + Status |= VL53L0_WrByte(Dev, 0x60, 0x04); + Status |= VL53L0_WrByte(Dev, 0x70, 0x06); + + Status |= VL53L0_WrByte(Dev, 0x46, 0x1a); + Status |= VL53L0_WrWord(Dev, 0x51, 0x01a3); + Status |= VL53L0_WrWord(Dev, 0x61, 0x01c4); + Status |= VL53L0_WrWord(Dev, 0x71, 0x018c); + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x31, 0x0f); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0x66, 0x38); + + Status |= VL53L0_WrByte(Dev, 0x47, 0x00); + Status |= VL53L0_WrByte(Dev, 0x48, 0xff); + Status |= VL53L0_WrByte(Dev, 0x57, 0x4c); + Status |= VL53L0_WrByte(Dev, 0x67, 0x3c); + Status |= VL53L0_WrByte(Dev, 0x77, 0x5c); + + Status |= VL53L0_WrWord(Dev, 0x44, 0x0000); + + Status |= VL53L0_WrByte(Dev, 0x27, 0x00); + Status |= VL53L0_WrByte(Dev, 0x55, 0x00); + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x30, 0x28); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + Status |= VL53L0_WrByte(Dev, 0x10, 0x0f); + Status |= VL53L0_WrByte(Dev, 0x11, 0xff); + Status |= VL53L0_WrByte(Dev, 0x40, 0x82); + Status |= VL53L0_WrByte(Dev, 0x41, 0xff); + Status |= VL53L0_WrByte(Dev, 0x42, 0x07); + Status |= VL53L0_WrByte(Dev, 0x43, 0x12); + + Status |= VL53L0_WrByte(Dev, 0x20, 0x00); + Status |= VL53L0_WrByte(Dev, 0x21, 0x00); + + Status |= VL53L0_WrByte(Dev, 0x28, 0x06); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x48, 0x28); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + Status |= VL53L0_WrByte(Dev, 0x7a, 0x0a); + Status |= VL53L0_WrByte(Dev, 0x7b, 0x00); + Status |= VL53L0_WrByte(Dev, 0x78, 0x00); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x44, 0xff); + Status |= VL53L0_WrByte(Dev, 0x45, 0x00); + Status |= VL53L0_WrByte(Dev, 0x46, 0x10); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + Status |= VL53L0_WrByte(Dev, 0x04, 0x00); + Status |= VL53L0_WrByte(Dev, 0x05, 0x04); + Status |= VL53L0_WrByte(Dev, 0x06, 0x00); + Status |= VL53L0_WrByte(Dev, 0x07, 0x00); + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x0d, 0x01); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0x80, 0x01); + Status |= VL53L0_WrByte(Dev, 0x01, 0xF8); + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x8e, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x01); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + if (Status != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + LOG_FUNCTION_END(Status); + return Status; +} diff --git a/drivers/input/misc/vl53L0/src/vl53l0_api.c b/drivers/input/misc/vl53L0/src/vl53l0_api.c new file mode 100644 index 000000000000..f9c2c2a4b7b7 --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_api.c @@ -0,0 +1,3109 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include "vl53l0_api.h" +#include "vl53l0_tuning.h" +#include "vl53l0_interrupt_threshold_settings.h" +#include "vl53l0_api_core.h" +#include "vl53l0_api_histogram.h" +#include "vl53l0_api_calibration.h" +#include "vl53l0_api_strings.h" + +#ifndef __KERNEL__ +#include <stdlib.h> +#endif +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__) + +#ifdef VL53L0_LOG_ENABLE +#define trace_print(level, ...) trace_print_module_function(TRACE_MODULE_API, \ + level, TRACE_FUNCTION_NONE, ##__VA_ARGS__) +#endif + +/* Group PAL General Functions */ + +VL53L0_Error VL53L0_GetVersion(VL53L0_Version_t *pVersion) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + pVersion->major = VL53L0_IMPLEMENTATION_VER_MAJOR; + pVersion->minor = VL53L0_IMPLEMENTATION_VER_MINOR; + pVersion->build = VL53L0_IMPLEMENTATION_VER_SUB; + + pVersion->revision = VL53L0_IMPLEMENTATION_VER_REVISION; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetPalSpecVersion(VL53L0_Version_t *pPalSpecVersion) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + pPalSpecVersion->major = VL53L0_SPECIFICATION_VER_MAJOR; + pPalSpecVersion->minor = VL53L0_SPECIFICATION_VER_MINOR; + pPalSpecVersion->build = VL53L0_SPECIFICATION_VER_SUB; + + pPalSpecVersion->revision = VL53L0_SPECIFICATION_VER_REVISION; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetProductRevision(VL53L0_DEV Dev, + uint8_t *pProductRevisionMajor, uint8_t *pProductRevisionMinor) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t revision_id; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_IDENTIFICATION_REVISION_ID, + &revision_id); + *pProductRevisionMajor = 1; + *pProductRevisionMinor = (revision_id & 0xF0) >> 4; + + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L0_Error VL53L0_GetDeviceInfo(VL53L0_DEV Dev, + VL53L0_DeviceInfo_t *pVL53L0_DeviceInfo) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_device_info(Dev, pVL53L0_DeviceInfo); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetDeviceErrorStatus(VL53L0_DEV Dev, + VL53L0_DeviceError *pDeviceErrorStatus) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t RangeStatus; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_RESULT_RANGE_STATUS, + &RangeStatus); + + *pDeviceErrorStatus = (VL53L0_DeviceError)((RangeStatus & 0x78) >> 3); + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L0_Error VL53L0_GetDeviceErrorString(VL53L0_DeviceError ErrorCode, + char *pDeviceErrorString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_device_error_string(ErrorCode, pDeviceErrorString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetRangeStatusString(uint8_t RangeStatus, + char *pRangeStatusString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_range_status_string(RangeStatus, + pRangeStatusString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetPalErrorString(VL53L0_Error PalErrorCode, + char *pPalErrorString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_pal_error_string(PalErrorCode, pPalErrorString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetPalStateString(VL53L0_State PalStateCode, + char *pPalStateString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_pal_state_string(PalStateCode, pPalStateString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetPalState(VL53L0_DEV Dev, VL53L0_State *pPalState) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pPalState = PALDevDataGet(Dev, PalState); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetPowerMode(VL53L0_DEV Dev, VL53L0_PowerModes PowerMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + /* Only level1 of Power mode exists */ + if ((PowerMode != VL53L0_POWERMODE_STANDBY_LEVEL1) + && (PowerMode != VL53L0_POWERMODE_IDLE_LEVEL1)) { + Status = VL53L0_ERROR_MODE_NOT_SUPPORTED; + } else if (PowerMode == VL53L0_POWERMODE_STANDBY_LEVEL1) { + /* set the standby level1 of power mode */ + Status = VL53L0_WrByte(Dev, 0x80, 0x00); + if (Status == VL53L0_ERROR_NONE) { + /* Set PAL State to standby */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_STANDBY); + PALDevDataSet(Dev, PowerMode, + VL53L0_POWERMODE_STANDBY_LEVEL1); + } + + } else { + /* VL53L0_POWERMODE_IDLE_LEVEL1 */ + Status = VL53L0_WrByte(Dev, 0x80, 0x00); + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_StaticInit(Dev); + + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, PowerMode, + VL53L0_POWERMODE_IDLE_LEVEL1); + + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetPowerMode(VL53L0_DEV Dev, VL53L0_PowerModes *pPowerMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + /* Only level1 of Power mode exists */ + Status = VL53L0_RdByte(Dev, 0x80, &Byte); + + if (Status == VL53L0_ERROR_NONE) { + if (Byte == 1) { + PALDevDataSet(Dev, PowerMode, + VL53L0_POWERMODE_IDLE_LEVEL1); + } else { + PALDevDataSet(Dev, PowerMode, + VL53L0_POWERMODE_STANDBY_LEVEL1); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetOffsetCalibrationDataMicroMeter(VL53L0_DEV Dev, + int32_t OffsetCalibrationDataMicroMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_set_offset_calibration_data_micro_meter(Dev, + OffsetCalibrationDataMicroMeter); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetOffsetCalibrationDataMicroMeter(VL53L0_DEV Dev, + int32_t *pOffsetCalibrationDataMicroMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_offset_calibration_data_micro_meter(Dev, + pOffsetCalibrationDataMicroMeter); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetLinearityCorrectiveGain(VL53L0_DEV Dev, + int16_t LinearityCorrectiveGain) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if ((LinearityCorrectiveGain < 0) || (LinearityCorrectiveGain > 1000)) + Status = VL53L0_ERROR_INVALID_PARAMS; + else { + PALDevDataSet(Dev, LinearityCorrectiveGain, + LinearityCorrectiveGain); + + if (LinearityCorrectiveGain != 1000) { + /* Disable FW Xtalk */ + Status = VL53L0_WrWord(Dev, + VL53L0_REG_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS, 0); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetLinearityCorrectiveGain(VL53L0_DEV Dev, + uint16_t *pLinearityCorrectiveGain) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pLinearityCorrectiveGain = PALDevDataGet(Dev, LinearityCorrectiveGain); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetGroupParamHold(VL53L0_DEV Dev, uint8_t GroupParamHold) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + /* not implemented on VL53L0 */ + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetUpperLimitMilliMeter(VL53L0_DEV Dev, + uint16_t *pUpperLimitMilliMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + /* not implemented on VL53L0 */ + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetTotalSignalRate(VL53L0_DEV Dev, + FixPoint1616_t *pTotalSignalRate) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_RangingMeasurementData_t LastRangeDataBuffer; + + LOG_FUNCTION_START(""); + + LastRangeDataBuffer = PALDevDataGet(Dev, LastRangeMeasure); + + Status = VL53L0_get_total_signal_rate( + Dev, &LastRangeDataBuffer, pTotalSignalRate); + + LOG_FUNCTION_END(Status); + return Status; +} + +/* End Group PAL General Functions */ + +/* Group PAL Init Functions */ +VL53L0_Error VL53L0_SetDeviceAddress(VL53L0_DEV Dev, uint8_t DeviceAddress) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, VL53L0_REG_I2C_SLAVE_DEVICE_ADDRESS, + DeviceAddress / 2); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_DataInit(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + int i; + uint8_t StopVariable; + + LOG_FUNCTION_START(""); + + /* by default the I2C is running at 1V8 if you want to change it you + * need to include this define at compilation level. + */ +#ifdef USE_I2C_2V8 + Status = VL53L0_UpdateByte(Dev, + VL53L0_REG_VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV, + 0xFE, + 0x01); +#endif + + /* Set I2C standard mode */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, 0x88, 0x00); + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, ReadDataFromDeviceDone, 0); + +#ifdef USE_IQC_STATION + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_apply_offset_adjustment(Dev); +#endif + + /* Default value is 1000 for Linearity Corrective Gain */ + PALDevDataSet(Dev, LinearityCorrectiveGain, 1000); + + /* Dmax default Parameter */ + PALDevDataSet(Dev, DmaxCalRangeMilliMeter, 400); + PALDevDataSet(Dev, DmaxCalSignalRateRtnMegaCps, + (FixPoint1616_t)((0x00016B85))); /* 1.42 No Cover Glass*/ + + /* Set Default static parameters + *set first temporary values 9.44MHz * 65536 = 618660 + */ + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, OscFrequencyMHz, 618660); + + /* Set Default XTalkCompensationRateMegaCps to 0 */ + VL53L0_SETPARAMETERFIELD(Dev, XTalkCompensationRateMegaCps, 0); + + /* Get default parameters */ + Status = VL53L0_GetDeviceParameters(Dev, &CurrentParameters); + if (Status == VL53L0_ERROR_NONE) { + /* initialize PAL values */ + CurrentParameters.DeviceMode = VL53L0_DEVICEMODE_SINGLE_RANGING; + CurrentParameters.HistogramMode = VL53L0_HISTOGRAMMODE_DISABLED; + PALDevDataSet(Dev, CurrentParameters, CurrentParameters); + } + + /* Sigma estimator variable */ + PALDevDataSet(Dev, SigmaEstRefArray, 100); + PALDevDataSet(Dev, SigmaEstEffPulseWidth, 900); + PALDevDataSet(Dev, SigmaEstEffAmbWidth, 500); + PALDevDataSet(Dev, targetRefRate, 0x0A00); /* 20 MCPS in 9:7 format */ + + /* Use internal default settings */ + PALDevDataSet(Dev, UseInternalTuningSettings, 1); + + Status |= VL53L0_WrByte(Dev, 0x80, 0x01); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + Status |= VL53L0_RdByte(Dev, 0x91, &StopVariable); + PALDevDataSet(Dev, StopVariable, StopVariable); + Status |= VL53L0_WrByte(Dev, 0x00, 0x01); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0x80, 0x00); + + /* Enable all check */ + for (i = 0; i < VL53L0_CHECKENABLE_NUMBER_OF_CHECKS; i++) { + if (Status == VL53L0_ERROR_NONE) + Status |= VL53L0_SetLimitCheckEnable(Dev, i, 1); + else + break; + + } + + /* Disable the following checks */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, 0); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, 0); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_MSRC, 0); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_PRE_RANGE, 0); + + /* Limit default values */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_SetLimitCheckValue(Dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + (FixPoint1616_t)(18 * 65536)); + } + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_SetLimitCheckValue(Dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + (FixPoint1616_t)(25 * 65536 / 100)); + /* 0.25 * 65536 */ + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_SetLimitCheckValue(Dev, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, + (FixPoint1616_t)(35 * 65536)); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_SetLimitCheckValue(Dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + (FixPoint1616_t)(0 * 65536)); + } + + if (Status == VL53L0_ERROR_NONE) { + + PALDevDataSet(Dev, SequenceConfig, 0xFF); + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + 0xFF); + + /* Set PAL state to tell that we are waiting for call to + * VL53L0_StaticInit + */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_WAIT_STATICINIT); + } + + if (Status == VL53L0_ERROR_NONE) + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, RefSpadsInitialised, 0); + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetTuningSettingBuffer(VL53L0_DEV Dev, + uint8_t *pTuningSettingBuffer, uint8_t UseInternalTuningSettings) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (UseInternalTuningSettings == 1) { + /* Force use internal settings */ + PALDevDataSet(Dev, UseInternalTuningSettings, 1); + } else { + + /* check that the first byte is not 0 */ + if (*pTuningSettingBuffer != 0) { + PALDevDataSet(Dev, pTuningSettingsPointer, + pTuningSettingBuffer); + PALDevDataSet(Dev, UseInternalTuningSettings, 0); + + } else { + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetTuningSettingBuffer(VL53L0_DEV Dev, + uint8_t **ppTuningSettingBuffer, uint8_t *pUseInternalTuningSettings) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *ppTuningSettingBuffer = PALDevDataGet(Dev, pTuningSettingsPointer); + *pUseInternalTuningSettings = PALDevDataGet(Dev, + UseInternalTuningSettings); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_StaticInit(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters = {0}; + uint8_t *pTuningSettingBuffer; + uint16_t tempword = 0; + uint8_t tempbyte = 0; + uint8_t UseInternalTuningSettings = 0; + uint32_t count = 0; + uint8_t isApertureSpads = 0; + uint32_t refSpadCount = 0; + uint8_t ApertureSpads = 0; + uint8_t vcselPulsePeriodPCLK; + FixPoint1616_t seqTimeoutMilliSecs; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_info_from_device(Dev, 1); + + /* set the ref spad from NVM */ + count = (uint32_t)VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadCount); + ApertureSpads = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadType); + + /* NVM value invalid */ + if ((ApertureSpads > 1) || + ((ApertureSpads == 1) && (count > 32)) || + ((ApertureSpads == 0) && (count > 12))) + Status = VL53L0_perform_ref_spad_management(Dev, &refSpadCount, + &isApertureSpads); + else + Status = VL53L0_set_reference_spads(Dev, count, ApertureSpads); + + + /* Initialize tuning settings buffer to prevent compiler warning. */ + pTuningSettingBuffer = DefaultTuningSettings; + + if (Status == VL53L0_ERROR_NONE) { + UseInternalTuningSettings = PALDevDataGet(Dev, + UseInternalTuningSettings); + + if (UseInternalTuningSettings == 0) + pTuningSettingBuffer = PALDevDataGet(Dev, + pTuningSettingsPointer); + else + pTuningSettingBuffer = DefaultTuningSettings; + + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_load_tuning_settings(Dev, pTuningSettingBuffer); + + + /* Set interrupt config to new sample ready */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_SetGpioConfig(Dev, 0, 0, + VL53L0_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY, + VL53L0_INTERRUPTPOLARITY_LOW); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_RdWord(Dev, 0x84, &tempword); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, OscFrequencyMHz, + VL53L0_FIXPOINT412TOFIXPOINT1616(tempword)); + } + + /* After static init, some device parameters may be changed, + * so update them + */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetDeviceParameters(Dev, &CurrentParameters); + + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetFractionEnable(Dev, &tempbyte); + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, RangeFractionalEnable, tempbyte); + + } + + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, CurrentParameters, CurrentParameters); + + + /* read the sequence config and save it */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdByte(Dev, + VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, &tempbyte); + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, SequenceConfig, tempbyte); + + } + + /* Disable MSRC and TCC by default */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_TCC, 0); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_MSRC, 0); + + + /* Set PAL State to standby */ + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, PalState, VL53L0_STATE_IDLE); + + + + /* Store pre-range vcsel period */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetVcselPulsePeriod( + Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &vcselPulsePeriodPCLK); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + PreRangeVcselPulsePeriod, + vcselPulsePeriodPCLK); + } + + /* Store final-range vcsel period */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetVcselPulsePeriod( + Dev, + VL53L0_VCSEL_PERIOD_FINAL_RANGE, + &vcselPulsePeriodPCLK); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + FinalRangeVcselPulsePeriod, + vcselPulsePeriodPCLK); + } + + /* Store pre-range timeout */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetSequenceStepTimeout( + Dev, + VL53L0_SEQUENCESTEP_PRE_RANGE, + &seqTimeoutMilliSecs); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + PreRangeTimeoutMicroSecs, + seqTimeoutMilliSecs); + } + + /* Store final-range timeout */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetSequenceStepTimeout( + Dev, + VL53L0_SEQUENCESTEP_FINAL_RANGE, + &seqTimeoutMilliSecs); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + FinalRangeTimeoutMicroSecs, + seqTimeoutMilliSecs); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_WaitDeviceBooted(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + /* not implemented on VL53L0 */ + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_ResetDevice(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + /* Set reset bit */ + Status = VL53L0_WrByte(Dev, VL53L0_REG_SOFT_RESET_GO2_SOFT_RESET_N, + 0x00); + + /* Wait for some time */ + if (Status == VL53L0_ERROR_NONE) { + do { + Status = VL53L0_RdByte(Dev, + VL53L0_REG_IDENTIFICATION_MODEL_ID, &Byte); + } while (Byte != 0x00); + } + + /* Release reset */ + Status = VL53L0_WrByte(Dev, VL53L0_REG_SOFT_RESET_GO2_SOFT_RESET_N, + 0x01); + + /* Wait until correct boot-up of the device */ + if (Status == VL53L0_ERROR_NONE) { + do { + Status = VL53L0_RdByte(Dev, + VL53L0_REG_IDENTIFICATION_MODEL_ID, &Byte); + } while (Byte == 0x00); + } + + /* Set PAL State to VL53L0_STATE_POWERDOWN */ + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, PalState, VL53L0_STATE_POWERDOWN); + + + LOG_FUNCTION_END(Status); + return Status; +} +/* End Group PAL Init Functions */ + +/* Group PAL Parameters Functions */ +VL53L0_Error VL53L0_SetDeviceParameters(VL53L0_DEV Dev, + const VL53L0_DeviceParameters_t *pDeviceParameters) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int i; + + LOG_FUNCTION_START(""); + Status = VL53L0_SetDeviceMode(Dev, pDeviceParameters->DeviceMode); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetHistogramMode(Dev, + pDeviceParameters->HistogramMode); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetInterMeasurementPeriodMilliSeconds(Dev, + pDeviceParameters->InterMeasurementPeriodMilliSeconds); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetXTalkCompensationRateMegaCps(Dev, + pDeviceParameters->XTalkCompensationRateMegaCps); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetOffsetCalibrationDataMicroMeter(Dev, + pDeviceParameters->RangeOffsetMicroMeters); + + + for (i = 0; i < VL53L0_CHECKENABLE_NUMBER_OF_CHECKS; i++) { + if (Status == VL53L0_ERROR_NONE) + Status |= VL53L0_SetLimitCheckEnable(Dev, i, + pDeviceParameters->LimitChecksEnable[i]); + else + break; + + if (Status == VL53L0_ERROR_NONE) + Status |= VL53L0_SetLimitCheckValue(Dev, i, + pDeviceParameters->LimitChecksValue[i]); + else + break; + + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetWrapAroundCheckEnable(Dev, + pDeviceParameters->WrapAroundCheckEnable); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetMeasurementTimingBudgetMicroSeconds(Dev, + pDeviceParameters->MeasurementTimingBudgetMicroSeconds); + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetDeviceParameters(VL53L0_DEV Dev, + VL53L0_DeviceParameters_t *pDeviceParameters) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int i; + + LOG_FUNCTION_START(""); + + Status = VL53L0_GetDeviceMode(Dev, &(pDeviceParameters->DeviceMode)); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetHistogramMode(Dev, + &(pDeviceParameters->HistogramMode)); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetInterMeasurementPeriodMilliSeconds(Dev, + &(pDeviceParameters->InterMeasurementPeriodMilliSeconds)); + + + if (Status == VL53L0_ERROR_NONE) + pDeviceParameters->XTalkCompensationEnable = 0; + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetXTalkCompensationRateMegaCps(Dev, + &(pDeviceParameters->XTalkCompensationRateMegaCps)); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetOffsetCalibrationDataMicroMeter(Dev, + &(pDeviceParameters->RangeOffsetMicroMeters)); + + + if (Status == VL53L0_ERROR_NONE) { + for (i = 0; i < VL53L0_CHECKENABLE_NUMBER_OF_CHECKS; i++) { + /* get first the values, then the enables. + * VL53L0_GetLimitCheckValue will modify the enable + * flags + */ + if (Status == VL53L0_ERROR_NONE) { + Status |= VL53L0_GetLimitCheckValue(Dev, i, + &(pDeviceParameters->LimitChecksValue[i])); + } else { + break; + } + if (Status == VL53L0_ERROR_NONE) { + Status |= VL53L0_GetLimitCheckEnable(Dev, i, + &(pDeviceParameters->LimitChecksEnable[i])); + } else { + break; + } + } + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetWrapAroundCheckEnable(Dev, + &(pDeviceParameters->WrapAroundCheckEnable)); + } + + /* Need to be done at the end as it uses VCSELPulsePeriod */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetMeasurementTimingBudgetMicroSeconds(Dev, + &(pDeviceParameters->MeasurementTimingBudgetMicroSeconds)); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetDeviceMode(VL53L0_DEV Dev, VL53L0_DeviceModes DeviceMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START("%d", (int)DeviceMode); + + switch (DeviceMode) { + case VL53L0_DEVICEMODE_SINGLE_RANGING: + case VL53L0_DEVICEMODE_CONTINUOUS_RANGING: + case VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING: + case VL53L0_DEVICEMODE_SINGLE_HISTOGRAM: + case VL53L0_DEVICEMODE_GPIO_DRIVE: + case VL53L0_DEVICEMODE_GPIO_OSC: + /* Supported modes */ + VL53L0_SETPARAMETERFIELD(Dev, DeviceMode, DeviceMode); + break; + default: + /* Unsupported mode */ + Status = VL53L0_ERROR_MODE_NOT_SUPPORTED; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetDeviceMode(VL53L0_DEV Dev, + VL53L0_DeviceModes *pDeviceMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + VL53L0_GETPARAMETERFIELD(Dev, DeviceMode, *pDeviceMode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetRangeFractionEnable(VL53L0_DEV Dev, uint8_t Enable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START("%d", (int)Enable); + + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_RANGE_CONFIG, Enable); + + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, RangeFractionalEnable, Enable); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetFractionEnable(VL53L0_DEV Dev, uint8_t *pEnabled) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_SYSTEM_RANGE_CONFIG, pEnabled); + + if (Status == VL53L0_ERROR_NONE) + *pEnabled = (*pEnabled & 1); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetHistogramMode(VL53L0_DEV Dev, + VL53L0_HistogramModes HistogramMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START("%d", (int)HistogramMode); + + Status = VL53L0_set_histogram_mode(Dev, HistogramMode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetHistogramMode(VL53L0_DEV Dev, + VL53L0_HistogramModes *pHistogramMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_histogram_mode(Dev, pHistogramMode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetMeasurementTimingBudgetMicroSeconds(VL53L0_DEV Dev, + uint32_t MeasurementTimingBudgetMicroSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_set_measurement_timing_budget_micro_seconds(Dev, + MeasurementTimingBudgetMicroSeconds); + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error VL53L0_GetMeasurementTimingBudgetMicroSeconds(VL53L0_DEV Dev, + uint32_t *pMeasurementTimingBudgetMicroSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_measurement_timing_budget_micro_seconds(Dev, + pMeasurementTimingBudgetMicroSeconds); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetVcselPulsePeriod(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t VCSELPulsePeriodPCLK) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_set_vcsel_pulse_period(Dev, VcselPeriodType, + VCSELPulsePeriodPCLK); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetVcselPulsePeriod(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t *pVCSELPulsePeriodPCLK) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_vcsel_pulse_period(Dev, VcselPeriodType, + pVCSELPulsePeriodPCLK); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetSequenceStepEnable(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, uint8_t SequenceStepEnabled) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SequenceConfig = 0; + uint8_t SequenceConfigNew = 0; + uint32_t MeasurementTimingBudgetMicroSeconds; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + &SequenceConfig); + + SequenceConfigNew = SequenceConfig; + + if (Status == VL53L0_ERROR_NONE) { + if (SequenceStepEnabled == 1) { + + /* Enable requested sequence step + */ + switch (SequenceStepId) { + case VL53L0_SEQUENCESTEP_TCC: + SequenceConfigNew |= 0x10; + break; + case VL53L0_SEQUENCESTEP_DSS: + SequenceConfigNew |= 0x28; + break; + case VL53L0_SEQUENCESTEP_MSRC: + SequenceConfigNew |= 0x04; + break; + case VL53L0_SEQUENCESTEP_PRE_RANGE: + SequenceConfigNew |= 0x40; + break; + case VL53L0_SEQUENCESTEP_FINAL_RANGE: + SequenceConfigNew |= 0x80; + break; + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } else { + /* Disable requested sequence step + */ + switch (SequenceStepId) { + case VL53L0_SEQUENCESTEP_TCC: + SequenceConfigNew &= 0xef; + break; + case VL53L0_SEQUENCESTEP_DSS: + SequenceConfigNew &= 0xd7; + break; + case VL53L0_SEQUENCESTEP_MSRC: + SequenceConfigNew &= 0xfb; + break; + case VL53L0_SEQUENCESTEP_PRE_RANGE: + SequenceConfigNew &= 0xbf; + break; + case VL53L0_SEQUENCESTEP_FINAL_RANGE: + SequenceConfigNew &= 0x7f; + break; + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + } + + if (SequenceConfigNew != SequenceConfig) { + /* Apply New Setting */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrByte(Dev, + VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, SequenceConfigNew); + } + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, SequenceConfig, SequenceConfigNew); + + + /* Recalculate timing budget */ + if (Status == VL53L0_ERROR_NONE) { + VL53L0_GETPARAMETERFIELD(Dev, + MeasurementTimingBudgetMicroSeconds, + MeasurementTimingBudgetMicroSeconds); + + VL53L0_SetMeasurementTimingBudgetMicroSeconds(Dev, + MeasurementTimingBudgetMicroSeconds); + } + } + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error sequence_step_enabled(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, uint8_t SequenceConfig, + uint8_t *pSequenceStepEnabled) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + *pSequenceStepEnabled = 0; + LOG_FUNCTION_START(""); + + switch (SequenceStepId) { + case VL53L0_SEQUENCESTEP_TCC: + *pSequenceStepEnabled = (SequenceConfig & 0x10) >> 4; + break; + case VL53L0_SEQUENCESTEP_DSS: + *pSequenceStepEnabled = (SequenceConfig & 0x08) >> 3; + break; + case VL53L0_SEQUENCESTEP_MSRC: + *pSequenceStepEnabled = (SequenceConfig & 0x04) >> 2; + break; + case VL53L0_SEQUENCESTEP_PRE_RANGE: + *pSequenceStepEnabled = (SequenceConfig & 0x40) >> 6; + break; + case VL53L0_SEQUENCESTEP_FINAL_RANGE: + *pSequenceStepEnabled = (SequenceConfig & 0x80) >> 7; + break; + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetSequenceStepEnable(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, uint8_t *pSequenceStepEnabled) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SequenceConfig = 0; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + &SequenceConfig); + + if (Status == VL53L0_ERROR_NONE) { + Status = sequence_step_enabled(Dev, SequenceStepId, + SequenceConfig, pSequenceStepEnabled); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetSequenceStepEnables(VL53L0_DEV Dev, + VL53L0_SchedulerSequenceSteps_t *pSchedulerSequenceSteps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SequenceConfig = 0; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + &SequenceConfig); + + if (Status == VL53L0_ERROR_NONE) { + Status = sequence_step_enabled(Dev, + VL53L0_SEQUENCESTEP_TCC, SequenceConfig, + &pSchedulerSequenceSteps->TccOn); + } + if (Status == VL53L0_ERROR_NONE) { + Status = sequence_step_enabled(Dev, + VL53L0_SEQUENCESTEP_DSS, SequenceConfig, + &pSchedulerSequenceSteps->DssOn); + } + if (Status == VL53L0_ERROR_NONE) { + Status = sequence_step_enabled(Dev, + VL53L0_SEQUENCESTEP_MSRC, SequenceConfig, + &pSchedulerSequenceSteps->MsrcOn); + } + if (Status == VL53L0_ERROR_NONE) { + Status = sequence_step_enabled(Dev, + VL53L0_SEQUENCESTEP_PRE_RANGE, SequenceConfig, + &pSchedulerSequenceSteps->PreRangeOn); + } + if (Status == VL53L0_ERROR_NONE) { + Status = sequence_step_enabled(Dev, + VL53L0_SEQUENCESTEP_FINAL_RANGE, SequenceConfig, + &pSchedulerSequenceSteps->FinalRangeOn); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetNumberOfSequenceSteps(VL53L0_DEV Dev, + uint8_t *pNumberOfSequenceSteps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pNumberOfSequenceSteps = VL53L0_SEQUENCESTEP_NUMBER_OF_CHECKS; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetSequenceStepsInfo(VL53L0_SequenceStepId SequenceStepId, + char *pSequenceStepsString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_sequence_steps_info( + SequenceStepId, + pSequenceStepsString); + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error VL53L0_SetSequenceStepTimeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, FixPoint1616_t TimeOutMilliSecs) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_Error Status1 = VL53L0_ERROR_NONE; + uint32_t TimeoutMicroSeconds = ((TimeOutMilliSecs * 1000) + 0x8000) + >> 16; + uint32_t MeasurementTimingBudgetMicroSeconds; + FixPoint1616_t OldTimeOutMicroSeconds; + + LOG_FUNCTION_START(""); + + /* Read back the current value in case we need to revert back to this. + */ + Status = get_sequence_step_timeout(Dev, SequenceStepId, + &OldTimeOutMicroSeconds); + + if (Status == VL53L0_ERROR_NONE) { + Status = set_sequence_step_timeout(Dev, SequenceStepId, + TimeoutMicroSeconds); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_GETPARAMETERFIELD(Dev, + MeasurementTimingBudgetMicroSeconds, + MeasurementTimingBudgetMicroSeconds); + + /* At this point we don't know if the requested value is valid, + * therefore proceed to update the entire timing budget and + * if this fails, revert back to the previous value. + */ + Status = VL53L0_SetMeasurementTimingBudgetMicroSeconds(Dev, + MeasurementTimingBudgetMicroSeconds); + + if (Status != VL53L0_ERROR_NONE) { + Status1 = set_sequence_step_timeout(Dev, SequenceStepId, + OldTimeOutMicroSeconds); + + if (Status1 == VL53L0_ERROR_NONE) { + Status1 = + VL53L0_SetMeasurementTimingBudgetMicroSeconds( + Dev, + MeasurementTimingBudgetMicroSeconds); + } + + Status = Status1; + } + } + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error VL53L0_GetSequenceStepTimeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, FixPoint1616_t *pTimeOutMilliSecs) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint32_t TimeoutMicroSeconds; + uint32_t WholeNumber_ms = 0; + uint32_t Fraction_ms = 0; + + LOG_FUNCTION_START(""); + + Status = get_sequence_step_timeout(Dev, SequenceStepId, + &TimeoutMicroSeconds); + if (Status == VL53L0_ERROR_NONE) { + WholeNumber_ms = TimeoutMicroSeconds / 1000; + Fraction_ms = TimeoutMicroSeconds - (WholeNumber_ms * 1000); + *pTimeOutMilliSecs = (WholeNumber_ms << 16) + + (((Fraction_ms * 0xffff) + 500) / 1000); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetInterMeasurementPeriodMilliSeconds(VL53L0_DEV Dev, + uint32_t InterMeasurementPeriodMilliSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t osc_calibrate_val; + uint32_t IMPeriodMilliSeconds; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdWord(Dev, VL53L0_REG_OSC_CALIBRATE_VAL, + &osc_calibrate_val); + + if (Status == VL53L0_ERROR_NONE) { + if (osc_calibrate_val != 0) { + IMPeriodMilliSeconds = + InterMeasurementPeriodMilliSeconds + * osc_calibrate_val; + } else { + IMPeriodMilliSeconds = + InterMeasurementPeriodMilliSeconds; + } + Status = VL53L0_WrDWord(Dev, + VL53L0_REG_SYSTEM_INTERMEASUREMENT_PERIOD, + IMPeriodMilliSeconds); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETPARAMETERFIELD(Dev, + InterMeasurementPeriodMilliSeconds, + InterMeasurementPeriodMilliSeconds); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetInterMeasurementPeriodMilliSeconds(VL53L0_DEV Dev, + uint32_t *pInterMeasurementPeriodMilliSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t osc_calibrate_val; + uint32_t IMPeriodMilliSeconds; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdWord(Dev, VL53L0_REG_OSC_CALIBRATE_VAL, + &osc_calibrate_val); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdDWord(Dev, + VL53L0_REG_SYSTEM_INTERMEASUREMENT_PERIOD, + &IMPeriodMilliSeconds); + } + + if (Status == VL53L0_ERROR_NONE) { + if (osc_calibrate_val != 0) { + *pInterMeasurementPeriodMilliSeconds = + IMPeriodMilliSeconds / osc_calibrate_val; + } + VL53L0_SETPARAMETERFIELD(Dev, + InterMeasurementPeriodMilliSeconds, + *pInterMeasurementPeriodMilliSeconds); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetXTalkCompensationEnable(VL53L0_DEV Dev, + uint8_t XTalkCompensationEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + FixPoint1616_t TempFix1616; + uint16_t LinearityCorrectiveGain; + + LOG_FUNCTION_START(""); + + LinearityCorrectiveGain = PALDevDataGet(Dev, LinearityCorrectiveGain); + + if ((XTalkCompensationEnable == 0) + || (LinearityCorrectiveGain != 1000)) { + TempFix1616 = 0; + } else { + VL53L0_GETPARAMETERFIELD(Dev, XTalkCompensationRateMegaCps, + TempFix1616); + } + + /* the following register has a format 3.13 */ + Status = VL53L0_WrWord(Dev, + VL53L0_REG_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS, + VL53L0_FIXPOINT1616TOFIXPOINT313(TempFix1616)); + + if (Status == VL53L0_ERROR_NONE) { + if (XTalkCompensationEnable == 0) { + VL53L0_SETPARAMETERFIELD(Dev, XTalkCompensationEnable, + 0); + } else { + VL53L0_SETPARAMETERFIELD(Dev, XTalkCompensationEnable, + 1); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetXTalkCompensationEnable(VL53L0_DEV Dev, + uint8_t *pXTalkCompensationEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Temp8; + + LOG_FUNCTION_START(""); + + VL53L0_GETPARAMETERFIELD(Dev, XTalkCompensationEnable, Temp8); + *pXTalkCompensationEnable = Temp8; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetXTalkCompensationRateMegaCps(VL53L0_DEV Dev, + FixPoint1616_t XTalkCompensationRateMegaCps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Temp8; + uint16_t LinearityCorrectiveGain; + uint16_t data; + + LOG_FUNCTION_START(""); + + VL53L0_GETPARAMETERFIELD(Dev, XTalkCompensationEnable, Temp8); + LinearityCorrectiveGain = PALDevDataGet(Dev, LinearityCorrectiveGain); + + if (Temp8 == 0) { /* disabled write only internal value */ + VL53L0_SETPARAMETERFIELD(Dev, XTalkCompensationRateMegaCps, + XTalkCompensationRateMegaCps); + } else { + /* the following register has a format 3.13 */ + if (LinearityCorrectiveGain == 1000) { + data = VL53L0_FIXPOINT1616TOFIXPOINT313( + XTalkCompensationRateMegaCps); + } else { + data = 0; + } + + Status = VL53L0_WrWord(Dev, + VL53L0_REG_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS, data); + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETPARAMETERFIELD(Dev, + XTalkCompensationRateMegaCps, + XTalkCompensationRateMegaCps); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetXTalkCompensationRateMegaCps(VL53L0_DEV Dev, + FixPoint1616_t *pXTalkCompensationRateMegaCps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t Value; + FixPoint1616_t TempFix1616; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdWord(Dev, + VL53L0_REG_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS, (uint16_t *)&Value); + if (Status == VL53L0_ERROR_NONE) { + if (Value == 0) { + /* the Xtalk is disabled return value from memory */ + VL53L0_GETPARAMETERFIELD(Dev, + XTalkCompensationRateMegaCps, TempFix1616); + *pXTalkCompensationRateMegaCps = TempFix1616; + VL53L0_SETPARAMETERFIELD(Dev, XTalkCompensationEnable, + 0); + } else { + TempFix1616 = VL53L0_FIXPOINT313TOFIXPOINT1616(Value); + *pXTalkCompensationRateMegaCps = TempFix1616; + VL53L0_SETPARAMETERFIELD(Dev, + XTalkCompensationRateMegaCps, TempFix1616); + VL53L0_SETPARAMETERFIELD(Dev, XTalkCompensationEnable, + 1); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetRefCalibration(VL53L0_DEV Dev, uint8_t VhvSettings, + uint8_t PhaseCal) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_set_ref_calibration(Dev, VhvSettings, PhaseCal); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetRefCalibration(VL53L0_DEV Dev, uint8_t *pVhvSettings, + uint8_t *pPhaseCal) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_ref_calibration(Dev, pVhvSettings, pPhaseCal); + + LOG_FUNCTION_END(Status); + return Status; +} + +/* + * CHECK LIMIT FUNCTIONS + */ + +VL53L0_Error VL53L0_GetNumberOfLimitCheck(uint16_t *pNumberOfLimitCheck) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pNumberOfLimitCheck = VL53L0_CHECKENABLE_NUMBER_OF_CHECKS; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetLimitCheckInfo(VL53L0_DEV Dev, uint16_t LimitCheckId, + char *pLimitCheckString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_limit_check_info(Dev, LimitCheckId, + pLimitCheckString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetLimitCheckStatus(VL53L0_DEV Dev, uint16_t LimitCheckId, + uint8_t *pLimitCheckStatus) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Temp8; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L0_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L0_ERROR_INVALID_PARAMS; + } else { + + VL53L0_GETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + LimitCheckId, Temp8); + + *pLimitCheckStatus = Temp8; + + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetLimitCheckEnable(VL53L0_DEV Dev, uint16_t LimitCheckId, + uint8_t LimitCheckEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + FixPoint1616_t TempFix1616 = 0; + uint8_t LimitCheckEnableInt = 0; + uint8_t LimitCheckDisable = 0; + uint8_t Temp8; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L0_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L0_ERROR_INVALID_PARAMS; + } else { + if (LimitCheckEnable == 0) { + TempFix1616 = 0; + LimitCheckEnableInt = 0; + LimitCheckDisable = 1; + + } else { + VL53L0_GETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + LimitCheckId, TempFix1616); + LimitCheckDisable = 0; + /* this to be sure to have either 0 or 1 */ + LimitCheckEnableInt = 1; + } + + switch (LimitCheckId) { + + case VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE: + /* internal computation: */ + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + LimitCheckEnableInt); + + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + + Status = VL53L0_WrWord(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT, + VL53L0_FIXPOINT1616TOFIXPOINT97(TempFix1616)); + + break; + + case VL53L0_CHECKENABLE_SIGNAL_REF_CLIP: + + /* internal computation: */ + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, + LimitCheckEnableInt); + + break; + + case VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD: + + /* internal computation: */ + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + LimitCheckEnableInt); + + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_MSRC: + + Temp8 = (uint8_t)(LimitCheckDisable << 1); + Status = VL53L0_UpdateByte(Dev, + VL53L0_REG_MSRC_CONFIG_CONTROL, + 0xFE, Temp8); + + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_PRE_RANGE: + + Temp8 = (uint8_t)(LimitCheckDisable << 4); + Status = VL53L0_UpdateByte(Dev, + VL53L0_REG_MSRC_CONFIG_CONTROL, + 0xEF, Temp8); + + break; + + + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + + } + + } + + if (Status == VL53L0_ERROR_NONE) { + if (LimitCheckEnable == 0) { + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + LimitCheckId, 0); + } else { + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + LimitCheckId, 1); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetLimitCheckEnable(VL53L0_DEV Dev, uint16_t LimitCheckId, + uint8_t *pLimitCheckEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Temp8; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L0_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L0_ERROR_INVALID_PARAMS; + *pLimitCheckEnable = 0; + } else { + VL53L0_GETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + LimitCheckId, Temp8); + *pLimitCheckEnable = Temp8; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetLimitCheckValue(VL53L0_DEV Dev, uint16_t LimitCheckId, + FixPoint1616_t LimitCheckValue) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Temp8; + + LOG_FUNCTION_START(""); + + VL53L0_GETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, LimitCheckId, + Temp8); + + if (Temp8 == 0) { /* disabled write only internal value */ + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + LimitCheckId, LimitCheckValue); + } else { + + switch (LimitCheckId) { + + case VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE: + /* internal computation: */ + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + LimitCheckValue); + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + + Status = VL53L0_WrWord(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT, + VL53L0_FIXPOINT1616TOFIXPOINT97( + LimitCheckValue)); + + break; + + case VL53L0_CHECKENABLE_SIGNAL_REF_CLIP: + + /* internal computation: */ + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, + LimitCheckValue); + + break; + + case VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD: + + /* internal computation: */ + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + LimitCheckValue); + + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_MSRC: + case VL53L0_CHECKENABLE_SIGNAL_RATE_PRE_RANGE: + + Status = VL53L0_WrWord(Dev, + VL53L0_REG_PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT, + VL53L0_FIXPOINT1616TOFIXPOINT97( + LimitCheckValue)); + + break; + + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + LimitCheckId, LimitCheckValue); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetLimitCheckValue(VL53L0_DEV Dev, uint16_t LimitCheckId, + FixPoint1616_t *pLimitCheckValue) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t EnableZeroValue = 0; + uint16_t Temp16; + FixPoint1616_t TempFix1616; + + LOG_FUNCTION_START(""); + + switch (LimitCheckId) { + + case VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE: + /* internal computation: */ + VL53L0_GETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, TempFix1616); + EnableZeroValue = 0; + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + Status = VL53L0_RdWord(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT, + &Temp16); + if (Status == VL53L0_ERROR_NONE) + TempFix1616 = VL53L0_FIXPOINT97TOFIXPOINT1616(Temp16); + + + EnableZeroValue = 1; + break; + + case VL53L0_CHECKENABLE_SIGNAL_REF_CLIP: + /* internal computation: */ + VL53L0_GETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, TempFix1616); + EnableZeroValue = 0; + break; + + case VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD: + /* internal computation: */ + VL53L0_GETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, TempFix1616); + EnableZeroValue = 0; + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_MSRC: + case VL53L0_CHECKENABLE_SIGNAL_RATE_PRE_RANGE: + Status = VL53L0_RdWord(Dev, + VL53L0_REG_PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT, + &Temp16); + if (Status == VL53L0_ERROR_NONE) + TempFix1616 = VL53L0_FIXPOINT97TOFIXPOINT1616(Temp16); + + + EnableZeroValue = 0; + break; + + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + + } + + if (Status == VL53L0_ERROR_NONE) { + + if (EnableZeroValue == 1) { + + if (TempFix1616 == 0) { + /* disabled: return value from memory */ + VL53L0_GETARRAYPARAMETERFIELD(Dev, + LimitChecksValue, LimitCheckId, + TempFix1616); + *pLimitCheckValue = TempFix1616; + VL53L0_SETARRAYPARAMETERFIELD(Dev, + LimitChecksEnable, LimitCheckId, 0); + } else { + *pLimitCheckValue = TempFix1616; + VL53L0_SETARRAYPARAMETERFIELD(Dev, + LimitChecksValue, LimitCheckId, + TempFix1616); + VL53L0_SETARRAYPARAMETERFIELD(Dev, + LimitChecksEnable, LimitCheckId, 1); + } + } else { + *pLimitCheckValue = TempFix1616; + } + } + + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L0_Error VL53L0_GetLimitCheckCurrent(VL53L0_DEV Dev, uint16_t LimitCheckId, + FixPoint1616_t *pLimitCheckCurrent) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_RangingMeasurementData_t LastRangeDataBuffer; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L0_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L0_ERROR_INVALID_PARAMS; + } else { + switch (LimitCheckId) { + case VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE: + /* Need to run a ranging to have the latest values */ + *pLimitCheckCurrent = PALDevDataGet(Dev, SigmaEstimate); + + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + /* Need to run a ranging to have the latest values */ + LastRangeDataBuffer = PALDevDataGet(Dev, + LastRangeMeasure); + *pLimitCheckCurrent = + LastRangeDataBuffer.SignalRateRtnMegaCps; + + break; + + case VL53L0_CHECKENABLE_SIGNAL_REF_CLIP: + /* Need to run a ranging to have the latest values */ + *pLimitCheckCurrent = PALDevDataGet(Dev, + LastSignalRefMcps); + + break; + + case VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD: + /* Need to run a ranging to have the latest values */ + LastRangeDataBuffer = PALDevDataGet(Dev, + LastRangeMeasure); + *pLimitCheckCurrent = + LastRangeDataBuffer.SignalRateRtnMegaCps; + + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_MSRC: + /* Need to run a ranging to have the latest values */ + LastRangeDataBuffer = PALDevDataGet(Dev, + LastRangeMeasure); + *pLimitCheckCurrent = + LastRangeDataBuffer.SignalRateRtnMegaCps; + + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_PRE_RANGE: + /* Need to run a ranging to have the latest values */ + LastRangeDataBuffer = PALDevDataGet(Dev, + LastRangeMeasure); + *pLimitCheckCurrent = + LastRangeDataBuffer.SignalRateRtnMegaCps; + + break; + + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + LOG_FUNCTION_END(Status); + return Status; + +} + +/* + * WRAPAROUND Check + */ +VL53L0_Error VL53L0_SetWrapAroundCheckEnable(VL53L0_DEV Dev, + uint8_t WrapAroundCheckEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + uint8_t WrapAroundCheckEnableInt; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, &Byte); + if (WrapAroundCheckEnable == 0) { + /* Disable wraparound */ + Byte = Byte & 0x7F; + WrapAroundCheckEnableInt = 0; + } else { + /*Enable wraparound */ + Byte = Byte | 0x80; + WrapAroundCheckEnableInt = 1; + } + + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, Byte); + + if (Status == VL53L0_ERROR_NONE) { + PALDevDataSet(Dev, SequenceConfig, Byte); + VL53L0_SETPARAMETERFIELD(Dev, WrapAroundCheckEnable, + WrapAroundCheckEnableInt); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetWrapAroundCheckEnable(VL53L0_DEV Dev, + uint8_t *pWrapAroundCheckEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t data; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, &data); + if (Status == VL53L0_ERROR_NONE) { + PALDevDataSet(Dev, SequenceConfig, data); + if (data & (0x01 << 7)) + *pWrapAroundCheckEnable = 0x01; + else + *pWrapAroundCheckEnable = 0x00; + } + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETPARAMETERFIELD(Dev, WrapAroundCheckEnable, + *pWrapAroundCheckEnable); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetDmaxCalParameters(VL53L0_DEV Dev, + uint16_t RangeMilliMeter, FixPoint1616_t SignalRateRtnMegaCps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + FixPoint1616_t SignalRateRtnMegaCpsTemp = 0; + + LOG_FUNCTION_START(""); + + /* Check if one of input parameter is zero, in that case the + * value are get from NVM + */ + if ((RangeMilliMeter == 0) || (SignalRateRtnMegaCps == 0)) { + /* NVM parameters */ + /* Run VL53L0_get_info_from_device wit option 4 to get + * signal rate at 400 mm if the value have been already + * get this function will return with no access to device + */ + VL53L0_get_info_from_device(Dev, 4); + + SignalRateRtnMegaCpsTemp = VL53L0_GETDEVICESPECIFICPARAMETER( + Dev, SignalRateMeasFixed400mm); + + PALDevDataSet(Dev, DmaxCalRangeMilliMeter, 400); + PALDevDataSet(Dev, DmaxCalSignalRateRtnMegaCps, + SignalRateRtnMegaCpsTemp); + } else { + /* User parameters */ + PALDevDataSet(Dev, DmaxCalRangeMilliMeter, RangeMilliMeter); + PALDevDataSet(Dev, DmaxCalSignalRateRtnMegaCps, + SignalRateRtnMegaCps); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetDmaxCalParameters(VL53L0_DEV Dev, + uint16_t *pRangeMilliMeter, FixPoint1616_t *pSignalRateRtnMegaCps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pRangeMilliMeter = PALDevDataGet(Dev, DmaxCalRangeMilliMeter); + *pSignalRateRtnMegaCps = PALDevDataGet(Dev, + DmaxCalSignalRateRtnMegaCps); + + LOG_FUNCTION_END(Status); + return Status; +} + +/* End Group PAL Parameters Functions */ + +/* Group PAL Measurement Functions */ +VL53L0_Error VL53L0_PerformSingleMeasurement(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceModes DeviceMode; + + LOG_FUNCTION_START(""); + + /* Get Current DeviceMode */ + Status = VL53L0_GetDeviceMode(Dev, &DeviceMode); + + /* Start immediately to run a single ranging measurement in case of + * single ranging or single histogram + */ + if (Status == VL53L0_ERROR_NONE + && DeviceMode == VL53L0_DEVICEMODE_SINGLE_RANGING) + Status = VL53L0_StartMeasurement(Dev); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_measurement_poll_for_completion(Dev); + + + /* Change PAL State in case of single ranging or single histogram */ + if (Status == VL53L0_ERROR_NONE + && DeviceMode == VL53L0_DEVICEMODE_SINGLE_RANGING) + PALDevDataSet(Dev, PalState, VL53L0_STATE_IDLE); + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_PerformSingleHistogramMeasurement(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_perform_single_histogram_measurement(Dev, + pHistogramMeasurementData); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_PerformRefCalibration(VL53L0_DEV Dev, uint8_t *pVhvSettings, + uint8_t *pPhaseCal) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_perform_ref_calibration(Dev, pVhvSettings, + pPhaseCal, 1); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_PerformXTalkMeasurement(VL53L0_DEV Dev, + uint32_t TimeoutMs, FixPoint1616_t *pXtalkPerSpad, + uint8_t *pAmbientTooHigh) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_perform_xtalk_measurement(Dev, TimeoutMs, + pXtalkPerSpad, pAmbientTooHigh); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_PerformXTalkCalibration(VL53L0_DEV Dev, + FixPoint1616_t XTalkCalDistance, + FixPoint1616_t *pXTalkCompensationRateMegaCps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_perform_xtalk_calibration(Dev, XTalkCalDistance, + pXTalkCompensationRateMegaCps); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_PerformOffsetCalibration(VL53L0_DEV Dev, + FixPoint1616_t CalDistanceMilliMeter, int32_t *pOffsetMicroMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_perform_offset_calibration(Dev, CalDistanceMilliMeter, + pOffsetMicroMeter); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_CheckAndLoadInterruptSettings(VL53L0_DEV Dev, + uint8_t StartNotStopFlag) +{ + uint8_t InterruptConfig; + FixPoint1616_t ThresholdLow; + FixPoint1616_t ThresholdHigh; + VL53L0_Error Status = VL53L0_ERROR_NONE; + + InterruptConfig = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + Pin0GpioFunctionality); + + if ((InterruptConfig == + VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW) || + (InterruptConfig == + VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH) || + (InterruptConfig == + VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT)) { + + Status = VL53L0_GetInterruptThresholds(Dev, + VL53L0_DEVICEMODE_CONTINUOUS_RANGING, + &ThresholdLow, &ThresholdHigh); + + if (((ThresholdLow > 255*65536) || + (ThresholdHigh > 255*65536)) && + (Status == VL53L0_ERROR_NONE)) { + + if (StartNotStopFlag != 0) { + Status = VL53L0_load_tuning_settings(Dev, + InterruptThresholdSettings); + } else { + Status |= VL53L0_WrByte(Dev, 0xFF, 0x04); + Status |= VL53L0_WrByte(Dev, 0x70, 0x00); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0x80, 0x00); + } + + } + + + } + + return Status; + +} + + +VL53L0_Error VL53L0_StartMeasurement(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceModes DeviceMode; + uint8_t Byte; + uint8_t StartStopByte = VL53L0_REG_SYSRANGE_MODE_START_STOP; + uint32_t LoopNb; + + LOG_FUNCTION_START(""); + + /* Get Current DeviceMode */ + VL53L0_GetDeviceMode(Dev, &DeviceMode); + + Status = VL53L0_WrByte(Dev, 0x80, 0x01); + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status = VL53L0_WrByte(Dev, 0x00, 0x00); + Status = VL53L0_WrByte(Dev, 0x91, PALDevDataGet(Dev, StopVariable)); + Status = VL53L0_WrByte(Dev, 0x00, 0x01); + Status = VL53L0_WrByte(Dev, 0xFF, 0x00); + Status = VL53L0_WrByte(Dev, 0x80, 0x00); + + switch (DeviceMode) { + case VL53L0_DEVICEMODE_SINGLE_RANGING: + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSRANGE_START, 0x01); + + Byte = StartStopByte; + if (Status == VL53L0_ERROR_NONE) { + /* Wait until start bit has been cleared */ + LoopNb = 0; + do { + if (LoopNb > 0) + Status = VL53L0_RdByte(Dev, + VL53L0_REG_SYSRANGE_START, &Byte); + LoopNb = LoopNb + 1; + } while (((Byte & StartStopByte) == StartStopByte) + && (Status == VL53L0_ERROR_NONE) + && (LoopNb < VL53L0_DEFAULT_MAX_LOOP)); + + if (LoopNb >= VL53L0_DEFAULT_MAX_LOOP) + Status = VL53L0_ERROR_TIME_OUT; + + } + + break; + case VL53L0_DEVICEMODE_CONTINUOUS_RANGING: + /* Back-to-back mode */ + + /* Check if need to apply interrupt settings */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_CheckAndLoadInterruptSettings(Dev, 1); + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_SYSRANGE_START, + VL53L0_REG_SYSRANGE_MODE_BACKTOBACK); + if (Status == VL53L0_ERROR_NONE) { + /* Set PAL State to Running */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_RUNNING); + } + break; + case VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING: + /* Continuous mode */ + /* Check if need to apply interrupt settings */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_CheckAndLoadInterruptSettings(Dev, 1); + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_SYSRANGE_START, + VL53L0_REG_SYSRANGE_MODE_TIMED); + + if (Status == VL53L0_ERROR_NONE) { + /* Set PAL State to Running */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_RUNNING); + } + break; + default: + /* Selected mode not supported */ + Status = VL53L0_ERROR_MODE_NOT_SUPPORTED; + } + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_StopMeasurement(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSRANGE_START, + VL53L0_REG_SYSRANGE_MODE_SINGLESHOT); + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status = VL53L0_WrByte(Dev, 0x00, 0x00); + Status = VL53L0_WrByte(Dev, 0x91, 0x00); + Status = VL53L0_WrByte(Dev, 0x00, 0x01); + Status = VL53L0_WrByte(Dev, 0xFF, 0x00); + + if (Status == VL53L0_ERROR_NONE) { + /* Set PAL State to Idle */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_IDLE); + } + + /* Check if need to apply interrupt settings */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_CheckAndLoadInterruptSettings(Dev, 0); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetMeasurementDataReady(VL53L0_DEV Dev, + uint8_t *pMeasurementDataReady) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SysRangeStatusRegister; + uint8_t InterruptConfig; + uint32_t InterruptMask; + + LOG_FUNCTION_START(""); + + InterruptConfig = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + Pin0GpioFunctionality); + + if (InterruptConfig == + VL53L0_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY) { + Status = VL53L0_GetInterruptMaskStatus(Dev, &InterruptMask); + if (InterruptMask == + VL53L0_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY) + *pMeasurementDataReady = 1; + else + *pMeasurementDataReady = 0; + } else { + Status = VL53L0_RdByte(Dev, VL53L0_REG_RESULT_RANGE_STATUS, + &SysRangeStatusRegister); + if (Status == VL53L0_ERROR_NONE) { + if (SysRangeStatusRegister & 0x01) + *pMeasurementDataReady = 1; + else + *pMeasurementDataReady = 0; + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_WaitDeviceReadyForNewMeasurement(VL53L0_DEV Dev, + uint32_t MaxLoop) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + /* not implemented for VL53L0 */ + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L0_Error VL53L0_GetRangingMeasurementData(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t DeviceRangeStatus; + uint8_t RangeFractionalEnable; + uint8_t PalRangeStatus; + uint8_t XTalkCompensationEnable; + uint16_t AmbientRate; + FixPoint1616_t SignalRate; + uint16_t XTalkCompensationRateMegaCps; + uint16_t EffectiveSpadRtnCount; + uint16_t tmpuint16; + uint16_t XtalkRangeMilliMeter; + uint16_t LinearityCorrectiveGain; + uint8_t localBuffer[12]; + VL53L0_RangingMeasurementData_t LastRangeDataBuffer; + + LOG_FUNCTION_START(""); + + /* + * use multi read even if some registers are not useful, result will + * be more efficient + * start reading at 0x14 dec20 + * end reading at 0x21 dec33 total 14 bytes to read + */ + Status = VL53L0_ReadMulti(Dev, 0x14, localBuffer, 12); + + if (Status == VL53L0_ERROR_NONE) { + + pRangingMeasurementData->ZoneId = 0; /* Only one zone */ + pRangingMeasurementData->TimeStamp = 0; /* Not Implemented */ + + tmpuint16 = VL53L0_MAKEUINT16(localBuffer[11], localBuffer[10]); + /* cut1.1 if SYSTEM__RANGE_CONFIG if 1 range is 2bits fractional + *(format 11.2) else no fractional + */ + + pRangingMeasurementData->MeasurementTimeUsec = 0; + + SignalRate = VL53L0_FIXPOINT97TOFIXPOINT1616( + VL53L0_MAKEUINT16(localBuffer[7], localBuffer[6])); + /* peak_signal_count_rate_rtn_mcps */ + pRangingMeasurementData->SignalRateRtnMegaCps = SignalRate; + + AmbientRate = VL53L0_MAKEUINT16(localBuffer[9], localBuffer[8]); + pRangingMeasurementData->AmbientRateRtnMegaCps = + VL53L0_FIXPOINT97TOFIXPOINT1616(AmbientRate); + + EffectiveSpadRtnCount = VL53L0_MAKEUINT16(localBuffer[3], + localBuffer[2]); + /* EffectiveSpadRtnCount is 8.8 format */ + pRangingMeasurementData->EffectiveSpadRtnCount = + EffectiveSpadRtnCount; + + DeviceRangeStatus = localBuffer[0]; + + /* Get Linearity Corrective Gain */ + LinearityCorrectiveGain = PALDevDataGet(Dev, + LinearityCorrectiveGain); + + /* Get ranging configuration */ + RangeFractionalEnable = PALDevDataGet(Dev, + RangeFractionalEnable); + + if (LinearityCorrectiveGain != 1000) { + + tmpuint16 = (uint16_t)((LinearityCorrectiveGain + * tmpuint16 + 500) / 1000); + + /* Implement Xtalk */ + VL53L0_GETPARAMETERFIELD(Dev, + XTalkCompensationRateMegaCps, + XTalkCompensationRateMegaCps); + VL53L0_GETPARAMETERFIELD(Dev, XTalkCompensationEnable, + XTalkCompensationEnable); + + if (XTalkCompensationEnable) { + + if ((SignalRate + - ((XTalkCompensationRateMegaCps + * EffectiveSpadRtnCount) >> 8)) + <= 0) { + if (RangeFractionalEnable) + XtalkRangeMilliMeter = 8888; + else + XtalkRangeMilliMeter = 8888 + << 2; + } else { + XtalkRangeMilliMeter = + (tmpuint16 * SignalRate) + / (SignalRate + - ((XTalkCompensationRateMegaCps + * EffectiveSpadRtnCount) + >> 8)); + } + + tmpuint16 = XtalkRangeMilliMeter; + } + + } + + if (RangeFractionalEnable) { + pRangingMeasurementData->RangeMilliMeter = + (uint16_t)((tmpuint16) >> 2); + pRangingMeasurementData->RangeFractionalPart = + (uint8_t)((tmpuint16 & 0x03) << 6); + } else { + pRangingMeasurementData->RangeMilliMeter = tmpuint16; + pRangingMeasurementData->RangeFractionalPart = 0; + } + + /* + * For a standard definition of RangeStatus, this should + * return 0 in case of good result after a ranging + * The range status depends on the device so call a device + * specific function to obtain the right Status. + */ + Status |= VL53L0_get_pal_range_status(Dev, DeviceRangeStatus, + SignalRate, EffectiveSpadRtnCount, + pRangingMeasurementData, &PalRangeStatus); + + if (Status == VL53L0_ERROR_NONE) + pRangingMeasurementData->RangeStatus = PalRangeStatus; + + } + + if (Status == VL53L0_ERROR_NONE) { + /* Copy last read data into Dev buffer */ + LastRangeDataBuffer = PALDevDataGet(Dev, LastRangeMeasure); + + LastRangeDataBuffer.RangeMilliMeter = + pRangingMeasurementData->RangeMilliMeter; + LastRangeDataBuffer.RangeFractionalPart = + pRangingMeasurementData->RangeFractionalPart; + LastRangeDataBuffer.RangeDMaxMilliMeter = + pRangingMeasurementData->RangeDMaxMilliMeter; + LastRangeDataBuffer.MeasurementTimeUsec = + pRangingMeasurementData->MeasurementTimeUsec; + LastRangeDataBuffer.SignalRateRtnMegaCps = + pRangingMeasurementData->SignalRateRtnMegaCps; + LastRangeDataBuffer.AmbientRateRtnMegaCps = + pRangingMeasurementData->AmbientRateRtnMegaCps; + LastRangeDataBuffer.EffectiveSpadRtnCount = + pRangingMeasurementData->EffectiveSpadRtnCount; + LastRangeDataBuffer.RangeStatus = + pRangingMeasurementData->RangeStatus; + + PALDevDataSet(Dev, LastRangeMeasure, LastRangeDataBuffer); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetMeasurementRefSignal(VL53L0_DEV Dev, + FixPoint1616_t *pMeasurementRefSignal) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pMeasurementRefSignal = PALDevDataGet(Dev, LastSignalRefMcps); + + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L0_Error VL53L0_GetHistogramMeasurementData(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_PerformSingleRangingMeasurement(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + /* This function will do a complete single ranging + * Here we fix the mode! + */ + Status = VL53L0_SetDeviceMode(Dev, VL53L0_DEVICEMODE_SINGLE_RANGING); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_PerformSingleMeasurement(Dev); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetRangingMeasurementData(Dev, + pRangingMeasurementData); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_ClearInterruptMask(Dev, 0); + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetNumberOfROIZones(VL53L0_DEV Dev, + uint8_t NumberOfROIZones) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (NumberOfROIZones != 1) + Status = VL53L0_ERROR_INVALID_PARAMS; + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetNumberOfROIZones(VL53L0_DEV Dev, + uint8_t *pNumberOfROIZones) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pNumberOfROIZones = 1; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetMaxNumberOfROIZones(VL53L0_DEV Dev, + uint8_t *pMaxNumberOfROIZones) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pMaxNumberOfROIZones = 1; + + LOG_FUNCTION_END(Status); + return Status; +} + +/* End Group PAL Measurement Functions */ + +VL53L0_Error VL53L0_SetGpioConfig(VL53L0_DEV Dev, uint8_t Pin, + VL53L0_DeviceModes DeviceMode, VL53L0_GpioFunctionality Functionality, + VL53L0_InterruptPolarity Polarity) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t data; + + LOG_FUNCTION_START(""); + + if (Pin != 0) { + Status = VL53L0_ERROR_GPIO_NOT_EXISTING; + } else if (DeviceMode == VL53L0_DEVICEMODE_GPIO_DRIVE) { + if (Polarity == VL53L0_INTERRUPTPOLARITY_LOW) + data = 0x10; + else + data = 1; + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_GPIO_HV_MUX_ACTIVE_HIGH, data); + + } else if (DeviceMode == VL53L0_DEVICEMODE_GPIO_OSC) { + + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + Status |= VL53L0_WrByte(Dev, 0x80, 0x01); + Status |= VL53L0_WrByte(Dev, 0x85, 0x02); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x04); + Status |= VL53L0_WrByte(Dev, 0xcd, 0x00); + Status |= VL53L0_WrByte(Dev, 0xcc, 0x11); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x07); + Status |= VL53L0_WrByte(Dev, 0xbe, 0x00); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x06); + Status |= VL53L0_WrByte(Dev, 0xcc, 0x09); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + + } else { + + if (Status == VL53L0_ERROR_NONE) { + switch (Functionality) { + case VL53L0_GPIOFUNCTIONALITY_OFF: + data = 0x00; + break; + case VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW: + data = 0x01; + break; + case VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH: + data = 0x02; + break; + case VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT: + data = 0x03; + break; + case VL53L0_GPIOFUNCTIONALITY_NEW_MEASURE_READY: + data = 0x04; + break; + default: + Status = + VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED; + } + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_SYSTEM_INTERRUPT_CONFIG_GPIO, data); + + if (Status == VL53L0_ERROR_NONE) { + if (Polarity == VL53L0_INTERRUPTPOLARITY_LOW) + data = 0; + else + data = (uint8_t)(1 << 4); + + Status = VL53L0_UpdateByte(Dev, + VL53L0_REG_GPIO_HV_MUX_ACTIVE_HIGH, 0xEF, data); + } + + if (Status == VL53L0_ERROR_NONE) + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + Pin0GpioFunctionality, Functionality); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_ClearInterruptMask(Dev, 0); + + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetGpioConfig(VL53L0_DEV Dev, uint8_t Pin, + VL53L0_DeviceModes *pDeviceMode, + VL53L0_GpioFunctionality *pFunctionality, + VL53L0_InterruptPolarity *pPolarity) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_GpioFunctionality GpioFunctionality; + uint8_t data; + + LOG_FUNCTION_START(""); + + /* pDeviceMode not managed by Ewok it return the current mode */ + + Status = VL53L0_GetDeviceMode(Dev, pDeviceMode); + + if (Status == VL53L0_ERROR_NONE) { + if (Pin != 0) { + Status = VL53L0_ERROR_GPIO_NOT_EXISTING; + } else { + Status = VL53L0_RdByte(Dev, + VL53L0_REG_SYSTEM_INTERRUPT_CONFIG_GPIO, &data); + } + } + + if (Status == VL53L0_ERROR_NONE) { + switch (data & 0x07) { + case 0x00: + GpioFunctionality = VL53L0_GPIOFUNCTIONALITY_OFF; + break; + case 0x01: + GpioFunctionality = + VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW; + break; + case 0x02: + GpioFunctionality = + VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH; + break; + case 0x03: + GpioFunctionality = + VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT; + break; + case 0x04: + GpioFunctionality = + VL53L0_GPIOFUNCTIONALITY_NEW_MEASURE_READY; + break; + default: + Status = VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED; + } + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_RdByte(Dev, VL53L0_REG_GPIO_HV_MUX_ACTIVE_HIGH, + &data); + + if (Status == VL53L0_ERROR_NONE) { + if ((data & (uint8_t)(1 << 4)) == 0) + *pPolarity = VL53L0_INTERRUPTPOLARITY_LOW; + else + *pPolarity = VL53L0_INTERRUPTPOLARITY_HIGH; + } + + if (Status == VL53L0_ERROR_NONE) { + *pFunctionality = GpioFunctionality; + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, Pin0GpioFunctionality, + GpioFunctionality); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetInterruptThresholds(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode, FixPoint1616_t ThresholdLow, + FixPoint1616_t ThresholdHigh) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t Threshold16; + + LOG_FUNCTION_START(""); + + /* no dependency on DeviceMode for Ewok */ + /* Need to divide by 2 because the FW will apply a x2 */ + Threshold16 = (uint16_t)((ThresholdLow >> 17) & 0x00fff); + Status = VL53L0_WrWord(Dev, VL53L0_REG_SYSTEM_THRESH_LOW, Threshold16); + + if (Status == VL53L0_ERROR_NONE) { + /* Need to divide by 2 because the FW will apply a x2 */ + Threshold16 = (uint16_t)((ThresholdHigh >> 17) & 0x00fff); + Status = VL53L0_WrWord(Dev, VL53L0_REG_SYSTEM_THRESH_HIGH, + Threshold16); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetInterruptThresholds(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode, FixPoint1616_t *pThresholdLow, + FixPoint1616_t *pThresholdHigh) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t Threshold16; + + LOG_FUNCTION_START(""); + + /* no dependency on DeviceMode for Ewok */ + + Status = VL53L0_RdWord(Dev, VL53L0_REG_SYSTEM_THRESH_LOW, &Threshold16); + /* Need to multiply by 2 because the FW will apply a x2 */ + *pThresholdLow = (FixPoint1616_t)((0x00fff & Threshold16) << 17); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdWord(Dev, VL53L0_REG_SYSTEM_THRESH_HIGH, + &Threshold16); + /* Need to multiply by 2 because the FW will apply a x2 */ + *pThresholdHigh = + (FixPoint1616_t)((0x00fff & Threshold16) << 17); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetStopCompletedStatus(VL53L0_DEV Dev, + uint32_t *pStopStatus) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte = 0; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_RdByte(Dev, 0x04, &Byte); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, 0xFF, 0x0); + + *pStopStatus = Byte; + + if (Byte == 0) { + Status = VL53L0_WrByte(Dev, 0x80, 0x01); + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status = VL53L0_WrByte(Dev, 0x00, 0x00); + Status = VL53L0_WrByte(Dev, 0x91, + PALDevDataGet(Dev, StopVariable)); + Status = VL53L0_WrByte(Dev, 0x00, 0x01); + Status = VL53L0_WrByte(Dev, 0xFF, 0x00); + Status = VL53L0_WrByte(Dev, 0x80, 0x00); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +/* Group PAL Interrupt Functions */ +VL53L0_Error VL53L0_ClearInterruptMask(VL53L0_DEV Dev, uint32_t InterruptMask) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t LoopCount; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + /* clear bit 0 range interrupt, bit 1 error interrupt */ + LoopCount = 0; + do { + Status = VL53L0_WrByte(Dev, + VL53L0_REG_SYSTEM_INTERRUPT_CLEAR, 0x01); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_SYSTEM_INTERRUPT_CLEAR, 0x00); + Status |= VL53L0_RdByte(Dev, + VL53L0_REG_RESULT_INTERRUPT_STATUS, &Byte); + LoopCount++; + } while (((Byte & 0x07) != 0x00) + && (LoopCount < 3) + && (Status == VL53L0_ERROR_NONE)); + + + if (LoopCount >= 3) + Status = VL53L0_ERROR_INTERRUPT_NOT_CLEARED; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetInterruptMaskStatus(VL53L0_DEV Dev, + uint32_t *pInterruptMaskStatus) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_RESULT_INTERRUPT_STATUS, &Byte); + *pInterruptMaskStatus = Byte & 0x07; + + if (Byte & 0x18) + Status = VL53L0_ERROR_RANGE_ERROR; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_EnableInterruptMask(VL53L0_DEV Dev, uint32_t InterruptMask) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + /* not implemented for VL53L0 */ + + LOG_FUNCTION_END(Status); + return Status; +} + +/* End Group PAL Interrupt Functions */ + +/* Group SPAD functions */ + +VL53L0_Error VL53L0_SetSpadAmbientDamperThreshold(VL53L0_DEV Dev, + uint16_t SpadAmbientDamperThreshold) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrWord(Dev, 0x40, SpadAmbientDamperThreshold); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetSpadAmbientDamperThreshold(VL53L0_DEV Dev, + uint16_t *pSpadAmbientDamperThreshold) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_RdWord(Dev, 0x40, pSpadAmbientDamperThreshold); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetSpadAmbientDamperFactor(VL53L0_DEV Dev, + uint16_t SpadAmbientDamperFactor) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + Byte = (uint8_t)(SpadAmbientDamperFactor & 0x00FF); + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x42, Byte); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetSpadAmbientDamperFactor(VL53L0_DEV Dev, + uint16_t *pSpadAmbientDamperFactor) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_RdByte(Dev, 0x42, &Byte); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + *pSpadAmbientDamperFactor = (uint16_t)Byte; + + LOG_FUNCTION_END(Status); + return Status; +} + +/* END Group SPAD functions */ + +/***************************************************************************** + * Internal functions + *****************************************************************************/ + +VL53L0_Error VL53L0_SetReferenceSpads(VL53L0_DEV Dev, uint32_t count, + uint8_t isApertureSpads) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_set_reference_spads(Dev, count, isApertureSpads); + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error VL53L0_GetReferenceSpads(VL53L0_DEV Dev, uint32_t *pSpadCount, + uint8_t *pIsApertureSpads) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_reference_spads(Dev, pSpadCount, pIsApertureSpads); + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error VL53L0_PerformRefSpadManagement(VL53L0_DEV Dev, + uint32_t *refSpadCount, uint8_t *isApertureSpads) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_perform_ref_spad_management(Dev, refSpadCount, + isApertureSpads); + + LOG_FUNCTION_END(Status); + + return Status; +} diff --git a/drivers/input/misc/vl53L0/src/vl53l0_api_calibration.c b/drivers/input/misc/vl53L0/src/vl53l0_api_calibration.c new file mode 100644 index 000000000000..fa7e579ddc17 --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_api_calibration.c @@ -0,0 +1,1284 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include "vl53l0_api.h" +#include "vl53l0_api_core.h" +#include "vl53l0_api_calibration.h" + +#ifndef __KERNEL__ +#include <stdlib.h> +#endif + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__) + +#define REF_ARRAY_SPAD_0 0 +#define REF_ARRAY_SPAD_5 5 +#define REF_ARRAY_SPAD_10 10 + +uint32_t refArrayQuadrants[4] = {REF_ARRAY_SPAD_10, REF_ARRAY_SPAD_5, + REF_ARRAY_SPAD_0, REF_ARRAY_SPAD_5 }; + +VL53L0_Error VL53L0_perform_xtalk_calibration(VL53L0_DEV Dev, + FixPoint1616_t XTalkCalDistance, + FixPoint1616_t *pXTalkCompensationRateMegaCps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t sum_ranging = 0; + uint16_t sum_spads = 0; + FixPoint1616_t sum_signalRate = 0; + FixPoint1616_t total_count = 0; + uint8_t xtalk_meas = 0; + VL53L0_RangingMeasurementData_t RangingMeasurementData; + FixPoint1616_t xTalkStoredMeanSignalRate; + FixPoint1616_t xTalkStoredMeanRange; + FixPoint1616_t xTalkStoredMeanRtnSpads; + uint32_t signalXTalkTotalPerSpad; + uint32_t xTalkStoredMeanRtnSpadsAsInt; + uint32_t xTalkCalDistanceAsInt; + FixPoint1616_t XTalkCompensationRateMegaCps; + + if (XTalkCalDistance <= 0) + Status = VL53L0_ERROR_INVALID_PARAMS; + + /* Disable the XTalk compensation */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetXTalkCompensationEnable(Dev, 0); + + /* Disable the RIT */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_SetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, 0); + } + + /* Perform 50 measurements and compute the averages */ + if (Status == VL53L0_ERROR_NONE) { + sum_ranging = 0; + sum_spads = 0; + sum_signalRate = 0; + total_count = 0; + for (xtalk_meas = 0; xtalk_meas < 50; xtalk_meas++) { + Status = VL53L0_PerformSingleRangingMeasurement(Dev, + &RangingMeasurementData); + + if (Status != VL53L0_ERROR_NONE) + break; + + /* The range is valid when RangeStatus = 0 */ + if (RangingMeasurementData.RangeStatus == 0) { + sum_ranging = sum_ranging + + RangingMeasurementData.RangeMilliMeter; + sum_signalRate = sum_signalRate + + RangingMeasurementData.SignalRateRtnMegaCps; + sum_spads = sum_spads + + RangingMeasurementData.EffectiveSpadRtnCount + / 256; + total_count = total_count + 1; + } + } + + /* no valid values found */ + if (total_count == 0) + Status = VL53L0_ERROR_RANGE_ERROR; + + } + + + if (Status == VL53L0_ERROR_NONE) { + /* FixPoint1616_t / uint16_t = FixPoint1616_t */ + xTalkStoredMeanSignalRate = sum_signalRate / total_count; + xTalkStoredMeanRange = (FixPoint1616_t)((uint32_t)( + sum_ranging << 16) / total_count); + xTalkStoredMeanRtnSpads = (FixPoint1616_t)((uint32_t)( + sum_spads << 16) / total_count); + + /* Round Mean Spads to Whole Number. + * Typically the calculated mean SPAD count is a whole number + * or very close to a whole + * number, therefore any truncation will not result in a + * significant loss in accuracy. + * Also, for a grey target at a typical distance of around + * 400mm, around 220 SPADs will + * be enabled, therefore, any truncation will result in a loss + * of accuracy of less than + * 0.5%. + */ + xTalkStoredMeanRtnSpadsAsInt = (xTalkStoredMeanRtnSpads + + 0x8000) >> 16; + + /* Round Cal Distance to Whole Number. + * Note that the cal distance is in mm, therefore no resolution + * is lost. + */ + xTalkCalDistanceAsInt = (XTalkCalDistance + 0x8000) >> 16; + + if (xTalkStoredMeanRtnSpadsAsInt == 0 || + xTalkCalDistanceAsInt == 0 || + xTalkStoredMeanRange >= XTalkCalDistance) { + XTalkCompensationRateMegaCps = 0; + } else { + /* Round Cal Distance to Whole Number. + * Note that the cal distance is in mm, therefore no + * resolution is lost. + */ + xTalkCalDistanceAsInt = (XTalkCalDistance + + 0x8000) >> 16; + + /* Apply division by mean spad count early in the + * calculation to keep the numbers small. + * This ensures we can maintain a 32bit calculation. + * Fixed1616 / int := Fixed1616 + */ + signalXTalkTotalPerSpad = (xTalkStoredMeanSignalRate) / + xTalkStoredMeanRtnSpadsAsInt; + + /* Complete the calculation for total Signal XTalk per + * SPAD + * Fixed1616 * (Fixed1616 - Fixed1616/int) := + * (2^16 * Fixed1616) + */ + signalXTalkTotalPerSpad *= ((1 << 16) - + (xTalkStoredMeanRange / xTalkCalDistanceAsInt)); + + /* Round from 2^16 * Fixed1616, to Fixed1616. */ + XTalkCompensationRateMegaCps = (signalXTalkTotalPerSpad + + 0x8000) >> 16; + } + + *pXTalkCompensationRateMegaCps = XTalkCompensationRateMegaCps; + + /* Enable the XTalk compensation */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetXTalkCompensationEnable(Dev, 1); + + /* Enable the XTalk compensation */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetXTalkCompensationRateMegaCps(Dev, + XTalkCompensationRateMegaCps); + + } + + return Status; +} + +VL53L0_Error VL53L0_perform_offset_calibration(VL53L0_DEV Dev, + FixPoint1616_t CalDistanceMilliMeter, + int32_t *pOffsetMicroMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t sum_ranging = 0; + FixPoint1616_t total_count = 0; + VL53L0_RangingMeasurementData_t RangingMeasurementData; + FixPoint1616_t StoredMeanRange; + uint32_t StoredMeanRangeAsInt; + uint32_t CalDistanceAsInt_mm; + uint8_t SequenceStepEnabled; + int meas = 0; + + if (CalDistanceMilliMeter <= 0) + Status = VL53L0_ERROR_INVALID_PARAMS; + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetOffsetCalibrationDataMicroMeter(Dev, 0); + + + /* Get the value of the TCC */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_TCC, &SequenceStepEnabled); + + + /* Disable the TCC */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_TCC, 0); + + + /* Disable the RIT */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, 0); + + /* Perform 50 measurements and compute the averages */ + if (Status == VL53L0_ERROR_NONE) { + sum_ranging = 0; + total_count = 0; + for (meas = 0; meas < 50; meas++) { + Status = VL53L0_PerformSingleRangingMeasurement(Dev, + &RangingMeasurementData); + + if (Status != VL53L0_ERROR_NONE) + break; + + /* The range is valid when RangeStatus = 0 */ + if (RangingMeasurementData.RangeStatus == 0) { + sum_ranging = sum_ranging + + RangingMeasurementData.RangeMilliMeter; + total_count = total_count + 1; + } + } + + /* no valid values found */ + if (total_count == 0) + Status = VL53L0_ERROR_RANGE_ERROR; + } + + + if (Status == VL53L0_ERROR_NONE) { + /* FixPoint1616_t / uint16_t = FixPoint1616_t */ + StoredMeanRange = (FixPoint1616_t)((uint32_t)(sum_ranging << 16) + / total_count); + + StoredMeanRangeAsInt = (StoredMeanRange + 0x8000) >> 16; + + /* Round Cal Distance to Whole Number. + * Note that the cal distance is in mm, therefore no resolution + * is lost. + */ + CalDistanceAsInt_mm = (CalDistanceMilliMeter + 0x8000) >> 16; + + *pOffsetMicroMeter = (CalDistanceAsInt_mm - + StoredMeanRangeAsInt) * 1000; + + /* Apply the calculated offset */ + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETPARAMETERFIELD(Dev, RangeOffsetMicroMeters, + *pOffsetMicroMeter); + Status = VL53L0_SetOffsetCalibrationDataMicroMeter(Dev, + *pOffsetMicroMeter); + } + + } + + /* Restore the TCC */ + if (Status == VL53L0_ERROR_NONE) { + if (SequenceStepEnabled != 0) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_TCC, 1); + } + + return Status; +} + + +VL53L0_Error VL53L0_set_offset_calibration_data_micro_meter(VL53L0_DEV Dev, + int32_t OffsetCalibrationDataMicroMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t cMaxOffsetMicroMeter = 511000; + int32_t cMinOffsetMicroMeter = -512000; + int16_t cOffsetRange = 4096; + uint32_t encodedOffsetVal; + + LOG_FUNCTION_START(""); + + if (OffsetCalibrationDataMicroMeter > cMaxOffsetMicroMeter) + OffsetCalibrationDataMicroMeter = cMaxOffsetMicroMeter; + else if (OffsetCalibrationDataMicroMeter < cMinOffsetMicroMeter) + OffsetCalibrationDataMicroMeter = cMinOffsetMicroMeter; + + /* The offset register is 10.2 format and units are mm + * therefore conversion is applied by a division of + * 250. + */ + if (OffsetCalibrationDataMicroMeter >= 0) { + encodedOffsetVal = + OffsetCalibrationDataMicroMeter/250; + } else { + encodedOffsetVal = + cOffsetRange + + OffsetCalibrationDataMicroMeter/250; + } + + Status = VL53L0_WrWord(Dev, + VL53L0_REG_ALGO_PART_TO_PART_RANGE_OFFSET_MM, + encodedOffsetVal); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_get_offset_calibration_data_micro_meter(VL53L0_DEV Dev, + int32_t *pOffsetCalibrationDataMicroMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t RangeOffsetRegister; + int16_t cMaxOffset = 2047; + int16_t cOffsetRange = 4096; + + /* Note that offset has 10.2 format */ + + Status = VL53L0_RdWord(Dev, + VL53L0_REG_ALGO_PART_TO_PART_RANGE_OFFSET_MM, + &RangeOffsetRegister); + + if (Status == VL53L0_ERROR_NONE) { + RangeOffsetRegister = (RangeOffsetRegister & 0x0fff); + + /* Apply 12 bit 2's compliment conversion */ + if (RangeOffsetRegister > cMaxOffset) + *pOffsetCalibrationDataMicroMeter = + (int16_t)(RangeOffsetRegister - cOffsetRange) + * 250; + else + *pOffsetCalibrationDataMicroMeter = + (int16_t)RangeOffsetRegister * 250; + + } + + return Status; +} + + +VL53L0_Error VL53L0_apply_offset_adjustment(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t CorrectedOffsetMicroMeters; + int32_t CurrentOffsetMicroMeters; + + /* if we run on this function we can read all the NVM info + * used by the API + */ + Status = VL53L0_get_info_from_device(Dev, 7); + + /* Read back current device offset */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetOffsetCalibrationDataMicroMeter(Dev, + &CurrentOffsetMicroMeters); + } + + /* Apply Offset Adjustment derived from 400mm measurements */ + if (Status == VL53L0_ERROR_NONE) { + + /* Store initial device offset */ + PALDevDataSet(Dev, Part2PartOffsetNVMMicroMeter, + CurrentOffsetMicroMeters); + + CorrectedOffsetMicroMeters = CurrentOffsetMicroMeters + + (int32_t)PALDevDataGet(Dev, + Part2PartOffsetAdjustmentNVMMicroMeter); + + Status = VL53L0_SetOffsetCalibrationDataMicroMeter(Dev, + CorrectedOffsetMicroMeters); + + /* store current, adjusted offset */ + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETPARAMETERFIELD(Dev, RangeOffsetMicroMeters, + CorrectedOffsetMicroMeters); + } + } + + return Status; +} + +void get_next_good_spad(uint8_t goodSpadArray[], uint32_t size, + uint32_t curr, int32_t *next) +{ + uint32_t startIndex; + uint32_t fineOffset; + uint32_t cSpadsPerByte = 8; + uint32_t coarseIndex; + uint32_t fineIndex; + uint8_t dataByte; + uint8_t success = 0; + + /* + * Starting with the current good spad, loop through the array to find + * the next. i.e. the next bit set in the sequence. + * + * The coarse index is the byte index of the array and the fine index is + * the index of the bit within each byte. + */ + + *next = -1; + + startIndex = curr / cSpadsPerByte; + fineOffset = curr % cSpadsPerByte; + + for (coarseIndex = startIndex; ((coarseIndex < size) && !success); + coarseIndex++) { + fineIndex = 0; + dataByte = goodSpadArray[coarseIndex]; + + if (coarseIndex == startIndex) { + /* locate the bit position of the provided current + * spad bit before iterating + */ + dataByte >>= fineOffset; + fineIndex = fineOffset; + } + + while (fineIndex < cSpadsPerByte) { + if ((dataByte & 0x1) == 1) { + success = 1; + *next = coarseIndex * cSpadsPerByte + fineIndex; + break; + } + dataByte >>= 1; + fineIndex++; + } + } +} + + +uint8_t is_aperture(uint32_t spadIndex) +{ + /* + * This function reports if a given spad index is an aperture SPAD by + * deriving the quadrant. + */ + uint32_t quadrant; + uint8_t isAperture = 1; + + quadrant = spadIndex >> 6; + if (refArrayQuadrants[quadrant] == REF_ARRAY_SPAD_0) + isAperture = 0; + + return isAperture; +} + + +VL53L0_Error enable_spad_bit(uint8_t spadArray[], uint32_t size, + uint32_t spadIndex) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + uint32_t cSpadsPerByte = 8; + uint32_t coarseIndex; + uint32_t fineIndex; + + coarseIndex = spadIndex / cSpadsPerByte; + fineIndex = spadIndex % cSpadsPerByte; + if (coarseIndex >= size) + status = VL53L0_ERROR_REF_SPAD_INIT; + else + spadArray[coarseIndex] |= (1 << fineIndex); + + return status; +} + +VL53L0_Error count_enabled_spads(uint8_t spadArray[], + uint32_t byteCount, uint32_t maxSpads, + uint32_t *pTotalSpadsEnabled, uint8_t *pIsAperture) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + uint32_t cSpadsPerByte = 8; + uint32_t lastByte; + uint32_t lastBit; + uint32_t byteIndex = 0; + uint32_t bitIndex = 0; + uint8_t tempByte; + uint8_t spadTypeIdentified = 0; + + /* The entire array will not be used for spads, therefore the last + * byte and last bit is determined from the max spads value. + */ + + lastByte = maxSpads / cSpadsPerByte; + lastBit = maxSpads % cSpadsPerByte; + + /* Check that the max spads value does not exceed the array bounds. */ + if (lastByte >= byteCount) + status = VL53L0_ERROR_REF_SPAD_INIT; + + *pTotalSpadsEnabled = 0; + + /* Count the bits enabled in the whole bytes */ + for (byteIndex = 0; byteIndex <= (lastByte - 1); byteIndex++) { + tempByte = spadArray[byteIndex]; + + for (bitIndex = 0; bitIndex <= cSpadsPerByte; bitIndex++) { + if ((tempByte & 0x01) == 1) { + (*pTotalSpadsEnabled)++; + + if (!spadTypeIdentified) { + *pIsAperture = 1; + if ((byteIndex < 2) && (bitIndex < 4)) + *pIsAperture = 0; + spadTypeIdentified = 1; + } + } + tempByte >>= 1; + } + } + + /* Count the number of bits enabled in the last byte accounting + * for the fact that not all bits in the byte may be used. + */ + tempByte = spadArray[lastByte]; + + for (bitIndex = 0; bitIndex <= lastBit; bitIndex++) { + if ((tempByte & 0x01) == 1) + (*pTotalSpadsEnabled)++; + } + + return status; +} + +VL53L0_Error set_ref_spad_map(VL53L0_DEV Dev, uint8_t *refSpadArray) +{ + VL53L0_Error status = VL53L0_WriteMulti(Dev, + VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_0, + refSpadArray, 6); + return status; +} + +VL53L0_Error get_ref_spad_map(VL53L0_DEV Dev, uint8_t *refSpadArray) +{ + VL53L0_Error status = VL53L0_ReadMulti(Dev, + VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_0, + refSpadArray, + 6); + return status; +} + +VL53L0_Error enable_ref_spads(VL53L0_DEV Dev, + uint8_t apertureSpads, + uint8_t goodSpadArray[], + uint8_t spadArray[], + uint32_t size, + uint32_t start, + uint32_t offset, + uint32_t spadCount, + uint32_t *lastSpad) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + uint32_t index; + uint32_t i; + int32_t nextGoodSpad = offset; + uint32_t currentSpad; + uint8_t checkSpadArray[6]; + + /* + * This function takes in a spad array which may or may not have SPADS + * already enabled and appends from a given offset a requested number + * of new SPAD enables. The 'good spad map' is applied to + * determine the next SPADs to enable. + * + * This function applies to only aperture or only non-aperture spads. + * Checks are performed to ensure this. + */ + + currentSpad = offset; + for (index = 0; index < spadCount; index++) { + get_next_good_spad(goodSpadArray, size, currentSpad, + &nextGoodSpad); + + if (nextGoodSpad == -1) { + status = VL53L0_ERROR_REF_SPAD_INIT; + break; + } + + /* Confirm that the next good SPAD is non-aperture */ + if (is_aperture(start + nextGoodSpad) != apertureSpads) { + /* if we can't get the required number of good aperture + * spads from the current quadrant then this is an error + */ + status = VL53L0_ERROR_REF_SPAD_INIT; + break; + } + currentSpad = (uint32_t)nextGoodSpad; + enable_spad_bit(spadArray, size, currentSpad); + currentSpad++; + } + *lastSpad = currentSpad; + + if (status == VL53L0_ERROR_NONE) + status = set_ref_spad_map(Dev, spadArray); + + + if (status == VL53L0_ERROR_NONE) { + status = get_ref_spad_map(Dev, checkSpadArray); + + i = 0; + + /* Compare spad maps. If not equal report error. */ + while (i < size) { + if (spadArray[i] != checkSpadArray[i]) { + status = VL53L0_ERROR_REF_SPAD_INIT; + break; + } + i++; + } + } + return status; +} + + +VL53L0_Error perform_ref_signal_measurement(VL53L0_DEV Dev, + uint16_t *refSignalRate) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + VL53L0_RangingMeasurementData_t rangingMeasurementData; + + uint8_t SequenceConfig = 0; + + /* store the value of the sequence config, + * this will be reset before the end of the function + */ + + SequenceConfig = PALDevDataGet(Dev, SequenceConfig); + + /* + * This function performs a reference signal rate measurement. + */ + if (status == VL53L0_ERROR_NONE) + status = VL53L0_WrByte(Dev, + VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, 0xC0); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_PerformSingleRangingMeasurement(Dev, + &rangingMeasurementData); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_WrByte(Dev, 0xFF, 0x01); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_RdWord(Dev, + VL53L0_REG_RESULT_PEAK_SIGNAL_RATE_REF, + refSignalRate); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_WrByte(Dev, 0xFF, 0x00); + + if (status == VL53L0_ERROR_NONE) { + /* restore the previous Sequence Config */ + status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + SequenceConfig); + if (status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, SequenceConfig, SequenceConfig); + } + + return status; +} + +VL53L0_Error VL53L0_perform_ref_spad_management(VL53L0_DEV Dev, + uint32_t *refSpadCount, + uint8_t *isApertureSpads) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t lastSpadArray[6]; + uint8_t startSelect = 0xB4; + uint32_t minimumSpadCount = 3; + uint32_t maxSpadCount = 44; + uint32_t currentSpadIndex = 0; + uint32_t lastSpadIndex = 0; + int32_t nextGoodSpad = 0; + uint16_t targetRefRate = 0x0A00; /* 20 MCPS in 9:7 format */ + uint16_t peakSignalRateRef; + uint32_t needAptSpads = 0; + uint32_t index = 0; + uint32_t spadArraySize = 6; + uint32_t signalRateDiff = 0; + uint32_t lastSignalRateDiff = 0; + uint8_t complete = 0; + uint8_t VhvSettings = 0; + uint8_t PhaseCal = 0; + uint32_t refSpadCount_int = 0; + uint8_t isApertureSpads_int = 0; + + /* + * The reference SPAD initialization procedure determines the minimum + * amount of reference spads to be enables to achieve a target reference + * signal rate and should be performed once during initialization. + * + * Either aperture or non-aperture spads are applied but never both. + * Firstly non-aperture spads are set, begining with 5 spads, and + * increased one spad at a time until the closest measurement to the + * target rate is achieved. + * + * If the target rate is exceeded when 5 non-aperture spads are enabled, + * initialization is performed instead with aperture spads. + * + * When setting spads, a 'Good Spad Map' is applied. + * + * This procedure operates within a SPAD window of interest of a maximum + * 44 spads. + * The start point is currently fixed to 180, which lies towards the end + * of the non-aperture quadrant and runs in to the adjacent aperture + * quadrant. + */ + + + targetRefRate = PALDevDataGet(Dev, targetRefRate); + + /* + * Initialize Spad arrays. + * Currently the good spad map is initialised to 'All good'. + * This is a short term implementation. The good spad map will be + * provided as an input. + * Note that there are 6 bytes. Only the first 44 bits will be used to + * represent spads. + */ + for (index = 0; index < spadArraySize; index++) + Dev->Data.SpadData.RefSpadEnables[index] = 0; + + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_DYNAMIC_SPAD_REF_EN_START_OFFSET, 0x00); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD, 0x2C); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, 0xFF, 0x00); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_GLOBAL_CONFIG_REF_EN_START_SELECT, + startSelect); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_POWER_MANAGEMENT_GO1_POWER_FORCE, 0); + + /* Perform ref calibration */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_perform_ref_calibration(Dev, &VhvSettings, + &PhaseCal, 0); + + if (Status == VL53L0_ERROR_NONE) { + /* Enable Minimum NON-APERTURE Spads */ + currentSpadIndex = 0; + lastSpadIndex = currentSpadIndex; + needAptSpads = 0; + Status = enable_ref_spads(Dev, + needAptSpads, + Dev->Data.SpadData.RefGoodSpadMap, + Dev->Data.SpadData.RefSpadEnables, + spadArraySize, + startSelect, + currentSpadIndex, + minimumSpadCount, + &lastSpadIndex); + } + + if (Status == VL53L0_ERROR_NONE) { + currentSpadIndex = lastSpadIndex; + + Status = perform_ref_signal_measurement(Dev, + &peakSignalRateRef); + if ((Status == VL53L0_ERROR_NONE) && + (peakSignalRateRef > targetRefRate)) { + /* Signal rate measurement too high, + * switch to APERTURE SPADs + */ + + for (index = 0; index < spadArraySize; index++) + Dev->Data.SpadData.RefSpadEnables[index] = 0; + + + /* Increment to the first APERTURE spad */ + while ((is_aperture(startSelect + currentSpadIndex) + == 0) && (currentSpadIndex < maxSpadCount)) { + currentSpadIndex++; + } + + needAptSpads = 1; + + Status = enable_ref_spads(Dev, + needAptSpads, + Dev->Data.SpadData.RefGoodSpadMap, + Dev->Data.SpadData.RefSpadEnables, + spadArraySize, + startSelect, + currentSpadIndex, + minimumSpadCount, + &lastSpadIndex); + + if (Status == VL53L0_ERROR_NONE) { + currentSpadIndex = lastSpadIndex; + Status = perform_ref_signal_measurement(Dev, + &peakSignalRateRef); + + if ((Status == VL53L0_ERROR_NONE) && + (peakSignalRateRef > targetRefRate)) { + /* Signal rate still too high after + * setting the minimum number of + * APERTURE spads. Can do no more + * therefore set the min number of + * aperture spads as the result. + */ + isApertureSpads_int = 1; + refSpadCount_int = minimumSpadCount; + } + } + } else { + needAptSpads = 0; + } + } + + if ((Status == VL53L0_ERROR_NONE) && + (peakSignalRateRef < targetRefRate)) { + /* At this point, the minimum number of either aperture + * or non-aperture spads have been set. Proceed to add + * spads and perform measurements until the target + * reference is reached. + */ + isApertureSpads_int = needAptSpads; + refSpadCount_int = minimumSpadCount; + + memcpy(lastSpadArray, Dev->Data.SpadData.RefSpadEnables, + spadArraySize); + lastSignalRateDiff = abs(peakSignalRateRef - + targetRefRate); + complete = 0; + + while (!complete) { + get_next_good_spad( + Dev->Data.SpadData.RefGoodSpadMap, + spadArraySize, currentSpadIndex, + &nextGoodSpad); + + if (nextGoodSpad == -1) { + Status = VL53L0_ERROR_REF_SPAD_INIT; + break; + } + + (refSpadCount_int)++; + + /* Cannot combine Aperture and Non-Aperture spads, so + * ensure the current spad is of the correct type. + */ + if (is_aperture((uint32_t)startSelect + nextGoodSpad) != + needAptSpads) { + Status = VL53L0_ERROR_REF_SPAD_INIT; + break; + } + + currentSpadIndex = nextGoodSpad; + Status = enable_spad_bit( + Dev->Data.SpadData.RefSpadEnables, + spadArraySize, currentSpadIndex); + + if (Status == VL53L0_ERROR_NONE) { + currentSpadIndex++; + /* Proceed to apply the additional spad and + * perform measurement. + */ + Status = set_ref_spad_map(Dev, + Dev->Data.SpadData.RefSpadEnables); + } + + if (Status != VL53L0_ERROR_NONE) + break; + + Status = perform_ref_signal_measurement(Dev, + &peakSignalRateRef); + + if (Status != VL53L0_ERROR_NONE) + break; + + signalRateDiff = abs(peakSignalRateRef - targetRefRate); + + if (peakSignalRateRef > targetRefRate) { + /* Select the spad map that provides the + * measurement closest to the target rate, + * either above or below it. + */ + if (signalRateDiff > lastSignalRateDiff) { + /* Previous spad map produced a closer + * measurement, so choose this. + */ + Status = set_ref_spad_map(Dev, + lastSpadArray); + memcpy( + Dev->Data.SpadData.RefSpadEnables, + lastSpadArray, spadArraySize); + + (refSpadCount_int)--; + } + complete = 1; + } else { + /* Continue to add spads */ + lastSignalRateDiff = signalRateDiff; + memcpy(lastSpadArray, + Dev->Data.SpadData.RefSpadEnables, + spadArraySize); + } + + } /* while */ + } + + if (Status == VL53L0_ERROR_NONE) { + *refSpadCount = refSpadCount_int; + *isApertureSpads = isApertureSpads_int; + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, RefSpadsInitialised, 1); + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadCount, (uint8_t)(*refSpadCount)); + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadType, *isApertureSpads); + } + + return Status; +} + +VL53L0_Error VL53L0_set_reference_spads(VL53L0_DEV Dev, + uint32_t count, uint8_t isApertureSpads) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint32_t currentSpadIndex = 0; + uint8_t startSelect = 0xB4; + uint32_t spadArraySize = 6; + uint32_t maxSpadCount = 44; + uint32_t lastSpadIndex; + uint32_t index; + + /* + * This function applies a requested number of reference spads, either + * aperture or + * non-aperture, as requested. + * The good spad map will be applied. + */ + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_DYNAMIC_SPAD_REF_EN_START_OFFSET, 0x00); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD, 0x2C); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, 0xFF, 0x00); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_GLOBAL_CONFIG_REF_EN_START_SELECT, + startSelect); + + for (index = 0; index < spadArraySize; index++) + Dev->Data.SpadData.RefSpadEnables[index] = 0; + + if (isApertureSpads) { + /* Increment to the first APERTURE spad */ + while ((is_aperture(startSelect + currentSpadIndex) == 0) && + (currentSpadIndex < maxSpadCount)) { + currentSpadIndex++; + } + } + Status = enable_ref_spads(Dev, + isApertureSpads, + Dev->Data.SpadData.RefGoodSpadMap, + Dev->Data.SpadData.RefSpadEnables, + spadArraySize, + startSelect, + currentSpadIndex, + count, + &lastSpadIndex); + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, RefSpadsInitialised, 1); + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadCount, (uint8_t)(count)); + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadType, isApertureSpads); + } + + return Status; +} + +VL53L0_Error VL53L0_get_reference_spads(VL53L0_DEV Dev, + uint32_t *pSpadCount, uint8_t *pIsApertureSpads) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t refSpadsInitialised; + uint8_t refSpadArray[6]; + uint32_t cMaxSpadCount = 44; + uint32_t cSpadArraySize = 6; + uint32_t spadsEnabled; + uint8_t isApertureSpads = 0; + + refSpadsInitialised = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + RefSpadsInitialised); + + if (refSpadsInitialised == 1) { + + *pSpadCount = (uint32_t)VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadCount); + *pIsApertureSpads = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadType); + } else { + + /* obtain spad info from device.*/ + Status = get_ref_spad_map(Dev, refSpadArray); + + if (Status == VL53L0_ERROR_NONE) { + /* count enabled spads within spad map array and + * determine if Aperture or Non-Aperture. + */ + Status = count_enabled_spads(refSpadArray, + cSpadArraySize, + cMaxSpadCount, + &spadsEnabled, + &isApertureSpads); + + if (Status == VL53L0_ERROR_NONE) { + + *pSpadCount = spadsEnabled; + *pIsApertureSpads = isApertureSpads; + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + RefSpadsInitialised, 1); + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadCount, + (uint8_t)spadsEnabled); + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadType, isApertureSpads); + } + } + } + + return Status; +} + + +VL53L0_Error VL53L0_perform_single_ref_calibration(VL53L0_DEV Dev, + uint8_t vhv_init_byte) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSRANGE_START, + VL53L0_REG_SYSRANGE_MODE_START_STOP | + vhv_init_byte); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_measurement_poll_for_completion(Dev); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_ClearInterruptMask(Dev, 0); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSRANGE_START, 0x00); + + return Status; +} + + +VL53L0_Error VL53L0_ref_calibration_io(VL53L0_DEV Dev, uint8_t read_not_write, + uint8_t VhvSettings, uint8_t PhaseCal, + uint8_t *pVhvSettings, uint8_t *pPhaseCal, + const uint8_t vhv_enable, const uint8_t phase_enable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t PhaseCalint = 0; + + /* Read VHV from device */ + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + if (read_not_write) { + if (vhv_enable) + Status |= VL53L0_RdByte(Dev, 0xCB, pVhvSettings); + if (phase_enable) + Status |= VL53L0_RdByte(Dev, 0xEE, &PhaseCalint); + } else { + if (vhv_enable) + Status |= VL53L0_WrByte(Dev, 0xCB, VhvSettings); + if (phase_enable) + Status |= VL53L0_UpdateByte(Dev, 0xEE, 0x80, PhaseCal); + } + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x01); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + *pPhaseCal = (uint8_t)(PhaseCalint&0xEF); + + return Status; +} + + +VL53L0_Error VL53L0_perform_vhv_calibration(VL53L0_DEV Dev, + uint8_t *pVhvSettings, const uint8_t get_data_enable, + const uint8_t restore_config) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SequenceConfig = 0; + uint8_t VhvSettings = 0; + uint8_t PhaseCal = 0; + uint8_t PhaseCalInt = 0; + + /* store the value of the sequence config, + * this will be reset before the end of the function + */ + + if (restore_config) + SequenceConfig = PALDevDataGet(Dev, SequenceConfig); + + /* Run VHV */ + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, 0x01); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_perform_single_ref_calibration(Dev, 0x40); + + /* Read VHV from device */ + if ((Status == VL53L0_ERROR_NONE) && (get_data_enable == 1)) { + Status = VL53L0_ref_calibration_io(Dev, 1, + VhvSettings, PhaseCal, /* Not used here */ + pVhvSettings, &PhaseCalInt, + 1, 0); + } else + *pVhvSettings = 0; + + + if ((Status == VL53L0_ERROR_NONE) && restore_config) { + /* restore the previous Sequence Config */ + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + SequenceConfig); + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, SequenceConfig, SequenceConfig); + + } + + return Status; +} + +VL53L0_Error VL53L0_perform_phase_calibration(VL53L0_DEV Dev, + uint8_t *pPhaseCal, const uint8_t get_data_enable, + const uint8_t restore_config) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SequenceConfig = 0; + uint8_t VhvSettings = 0; + uint8_t PhaseCal = 0; + uint8_t VhvSettingsint; + + /* store the value of the sequence config, + * this will be reset before the end of the function + */ + + if (restore_config) + SequenceConfig = PALDevDataGet(Dev, SequenceConfig); + + /* Run PhaseCal */ + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, 0x02); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_perform_single_ref_calibration(Dev, 0x0); + + /* Read PhaseCal from device */ + if ((Status == VL53L0_ERROR_NONE) && (get_data_enable == 1)) { + Status = VL53L0_ref_calibration_io(Dev, 1, + VhvSettings, PhaseCal, /* Not used here */ + &VhvSettingsint, pPhaseCal, + 0, 1); + } else + *pPhaseCal = 0; + + + if ((Status == VL53L0_ERROR_NONE) && restore_config) { + /* restore the previous Sequence Config */ + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + SequenceConfig); + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, SequenceConfig, SequenceConfig); + + } + + return Status; +} + +VL53L0_Error VL53L0_perform_ref_calibration(VL53L0_DEV Dev, + uint8_t *pVhvSettings, uint8_t *pPhaseCal, uint8_t get_data_enable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SequenceConfig = 0; + + /* store the value of the sequence config, + * this will be reset before the end of the function + */ + + SequenceConfig = PALDevDataGet(Dev, SequenceConfig); + + /* In the following function we don't save the config to optimize + * writes on device. Config is saved and restored only once. + */ + Status = VL53L0_perform_vhv_calibration( + Dev, pVhvSettings, get_data_enable, 0); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_perform_phase_calibration( + Dev, pPhaseCal, get_data_enable, 0); + + + if (Status == VL53L0_ERROR_NONE) { + /* restore the previous Sequence Config */ + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + SequenceConfig); + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, SequenceConfig, SequenceConfig); + + } + + return Status; +} + +VL53L0_Error VL53L0_set_ref_calibration(VL53L0_DEV Dev, + uint8_t VhvSettings, uint8_t PhaseCal) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t pVhvSettings; + uint8_t pPhaseCal; + + Status = VL53L0_ref_calibration_io(Dev, 0, + VhvSettings, PhaseCal, + &pVhvSettings, &pPhaseCal, + 1, 1); + + return Status; +} + +VL53L0_Error VL53L0_get_ref_calibration(VL53L0_DEV Dev, + uint8_t *pVhvSettings, uint8_t *pPhaseCal) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t VhvSettings = 0; + uint8_t PhaseCal = 0; + + Status = VL53L0_ref_calibration_io(Dev, 1, + VhvSettings, PhaseCal, + pVhvSettings, pPhaseCal, + 1, 1); + + return Status; +} diff --git a/drivers/input/misc/vl53L0/src/vl53l0_api_core.c b/drivers/input/misc/vl53L0/src/vl53l0_api_core.c new file mode 100644 index 000000000000..a04d0c4f5915 --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_api_core.c @@ -0,0 +1,2270 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include "vl53l0_api.h" +#include "vl53l0_api_core.h" +#include "vl53l0_api_calibration.h" + + +#ifndef __KERNEL__ +#include <stdlib.h> +#endif +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__) + +VL53L0_Error VL53L0_reverse_bytes(uint8_t *data, uint32_t size) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t tempData; + uint32_t mirrorIndex; + uint32_t middle = size/2; + uint32_t index; + + for (index = 0; index < middle; index++) { + mirrorIndex = size - index - 1; + tempData = data[index]; + data[index] = data[mirrorIndex]; + data[mirrorIndex] = tempData; + } + return Status; +} + +VL53L0_Error VL53L0_measurement_poll_for_completion(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t NewDataReady = 0; + uint32_t LoopNb; + + LOG_FUNCTION_START(""); + + LoopNb = 0; + + do { + Status = VL53L0_GetMeasurementDataReady(Dev, &NewDataReady); + if (Status != 0) + break; /* the error is set */ + + if (NewDataReady == 1) + break; /* done note that status == 0 */ + + LoopNb++; + if (LoopNb >= VL53L0_DEFAULT_MAX_LOOP) { + Status = VL53L0_ERROR_TIME_OUT; + break; + } + + VL53L0_PollingDelay(Dev); + } while (1); + + LOG_FUNCTION_END(Status); + + return Status; +} + + +uint8_t VL53L0_decode_vcsel_period(uint8_t vcsel_period_reg) +{ + /*! + * Converts the encoded VCSEL period register value into the real + * period in PLL clocks + */ + + uint8_t vcsel_period_pclks = 0; + + vcsel_period_pclks = (vcsel_period_reg + 1) << 1; + + return vcsel_period_pclks; +} + +uint8_t VL53L0_encode_vcsel_period(uint8_t vcsel_period_pclks) +{ + /*! + * Converts the encoded VCSEL period register value into the real period + * in PLL clocks + */ + + uint8_t vcsel_period_reg = 0; + + vcsel_period_reg = (vcsel_period_pclks >> 1) - 1; + + return vcsel_period_reg; +} + + +uint32_t VL53L0_isqrt(uint32_t num) +{ + /* + * Implements an integer square root + * + * From: http://en.wikipedia.org/wiki/Methods_of_computing_square_roots + */ + + uint32_t res = 0; + uint32_t bit = 1 << 30; + /* The second-to-top bit is set: + * 1 << 14 for 16-bits, 1 << 30 for 32 bits + */ + + /* "bit" starts at the highest power of four <= the argument. */ + while (bit > num) + bit >>= 2; + + + while (bit != 0) { + if (num >= res + bit) { + num -= res + bit; + res = (res >> 1) + bit; + } else + res >>= 1; + + bit >>= 2; + } + + return res; +} + + +uint32_t VL53L0_quadrature_sum(uint32_t a, uint32_t b) +{ + /* + * Implements a quadrature sum + * + * rea = sqrt(a^2 + b^2) + * + * Trap overflow case max input value is 65535 (16-bit value) + * as internal calc are 32-bit wide + * + * If overflow then seta output to maximum + */ + uint32_t res = 0; + + if (a > 65535 || b > 65535) + res = 65535; + else + res = VL53L0_isqrt(a * a + b * b); + + return res; +} + + +VL53L0_Error VL53L0_device_read_strobe(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t strobe; + uint32_t LoopNb; + + LOG_FUNCTION_START(""); + + Status |= VL53L0_WrByte(Dev, 0x83, 0x00); + + /* polling + * use timeout to avoid deadlock + */ + if (Status == VL53L0_ERROR_NONE) { + LoopNb = 0; + do { + Status = VL53L0_RdByte(Dev, 0x83, &strobe); + if ((strobe != 0x00) || Status != VL53L0_ERROR_NONE) + break; + + LoopNb = LoopNb + 1; + } while (LoopNb < VL53L0_DEFAULT_MAX_LOOP); + + if (LoopNb >= VL53L0_DEFAULT_MAX_LOOP) + Status = VL53L0_ERROR_TIME_OUT; + + } + + Status |= VL53L0_WrByte(Dev, 0x83, 0x01); + + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L0_Error VL53L0_get_info_from_device(VL53L0_DEV Dev, uint8_t option) +{ + + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t byte; + uint32_t TmpDWord; + uint8_t ModuleId; + uint8_t Revision; + uint8_t ReferenceSpadCount = 0; + uint8_t ReferenceSpadType = 0; + uint32_t PartUIDUpper = 0; + uint32_t PartUIDLower = 0; + uint32_t OffsetFixed1104_mm = 0; + int16_t OffsetMicroMeters = 0; + uint32_t DistMeasTgtFixed1104_mm = 400 << 4; + uint32_t DistMeasFixed1104_400_mm = 0; + uint32_t SignalRateMeasFixed1104_400_mm = 0; + char ProductId[19]; + char *ProductId_tmp; + uint8_t ReadDataFromDeviceDone; + FixPoint1616_t SignalRateMeasFixed400mmFix = 0; + uint8_t NvmRefGoodSpadMap[VL53L0_REF_SPAD_BUFFER_SIZE]; + int i; + + + LOG_FUNCTION_START(""); + + ReadDataFromDeviceDone = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + ReadDataFromDeviceDone); + + /* This access is done only once after that a GetDeviceInfo or + * datainit is done + */ + if (ReadDataFromDeviceDone != 7) { + + Status |= VL53L0_WrByte(Dev, 0x80, 0x01); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x06); + Status |= VL53L0_RdByte(Dev, 0x83, &byte); + Status |= VL53L0_WrByte(Dev, 0x83, byte|4); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x07); + Status |= VL53L0_WrByte(Dev, 0x81, 0x01); + + Status |= VL53L0_PollingDelay(Dev); + + Status |= VL53L0_WrByte(Dev, 0x80, 0x01); + + if (((option & 1) == 1) && + ((ReadDataFromDeviceDone & 1) == 0)) { + Status |= VL53L0_WrByte(Dev, 0x94, 0x6b); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + ReferenceSpadCount = (uint8_t)((TmpDWord >> 8) & 0x07f); + ReferenceSpadType = (uint8_t)((TmpDWord >> 15) & 0x01); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x24); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + + NvmRefGoodSpadMap[0] = (uint8_t)((TmpDWord >> 24) + & 0xff); + NvmRefGoodSpadMap[1] = (uint8_t)((TmpDWord >> 16) + & 0xff); + NvmRefGoodSpadMap[2] = (uint8_t)((TmpDWord >> 8) + & 0xff); + NvmRefGoodSpadMap[3] = (uint8_t)(TmpDWord & 0xff); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x25); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + NvmRefGoodSpadMap[4] = (uint8_t)((TmpDWord >> 24) + & 0xff); + NvmRefGoodSpadMap[5] = (uint8_t)((TmpDWord >> 16) + & 0xff); + } + + if (((option & 2) == 2) && + ((ReadDataFromDeviceDone & 2) == 0)) { + + Status |= VL53L0_WrByte(Dev, 0x94, 0x02); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdByte(Dev, 0x90, &ModuleId); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x7B); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdByte(Dev, 0x90, &Revision); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x77); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + ProductId[0] = (char)((TmpDWord >> 25) & 0x07f); + ProductId[1] = (char)((TmpDWord >> 18) & 0x07f); + ProductId[2] = (char)((TmpDWord >> 11) & 0x07f); + ProductId[3] = (char)((TmpDWord >> 4) & 0x07f); + + byte = (uint8_t)((TmpDWord & 0x00f) << 3); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x78); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + ProductId[4] = (char)(byte + + ((TmpDWord >> 29) & 0x07f)); + ProductId[5] = (char)((TmpDWord >> 22) & 0x07f); + ProductId[6] = (char)((TmpDWord >> 15) & 0x07f); + ProductId[7] = (char)((TmpDWord >> 8) & 0x07f); + ProductId[8] = (char)((TmpDWord >> 1) & 0x07f); + + byte = (uint8_t)((TmpDWord & 0x001) << 6); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x79); + + Status |= VL53L0_device_read_strobe(Dev); + + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + ProductId[9] = (char)(byte + + ((TmpDWord >> 26) & 0x07f)); + ProductId[10] = (char)((TmpDWord >> 19) & 0x07f); + ProductId[11] = (char)((TmpDWord >> 12) & 0x07f); + ProductId[12] = (char)((TmpDWord >> 5) & 0x07f); + + byte = (uint8_t)((TmpDWord & 0x01f) << 2); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x7A); + + Status |= VL53L0_device_read_strobe(Dev); + + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + ProductId[13] = (char)(byte + + ((TmpDWord >> 30) & 0x07f)); + ProductId[14] = (char)((TmpDWord >> 23) & 0x07f); + ProductId[15] = (char)((TmpDWord >> 16) & 0x07f); + ProductId[16] = (char)((TmpDWord >> 9) & 0x07f); + ProductId[17] = (char)((TmpDWord >> 2) & 0x07f); + ProductId[18] = '\0'; + + } + + if (((option & 4) == 4) && + ((ReadDataFromDeviceDone & 4) == 0)) { + + Status |= VL53L0_WrByte(Dev, 0x94, 0x7B); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &PartUIDUpper); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x7C); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &PartUIDLower); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x73); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + SignalRateMeasFixed1104_400_mm = (TmpDWord & + 0x0000000ff) << 8; + + Status |= VL53L0_WrByte(Dev, 0x94, 0x74); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + SignalRateMeasFixed1104_400_mm |= ((TmpDWord & + 0xff000000) >> 24); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x75); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + DistMeasFixed1104_400_mm = (TmpDWord & 0x0000000ff) + << 8; + + Status |= VL53L0_WrByte(Dev, 0x94, 0x76); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + DistMeasFixed1104_400_mm |= ((TmpDWord & 0xff000000) + >> 24); + } + + Status |= VL53L0_WrByte(Dev, 0x81, 0x00); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x06); + Status |= VL53L0_RdByte(Dev, 0x83, &byte); + Status |= VL53L0_WrByte(Dev, 0x83, byte&0xfb); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x01); + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0x80, 0x00); + } + + if ((Status == VL53L0_ERROR_NONE) && + (ReadDataFromDeviceDone != 7)) { + /* Assign to variable if status is ok */ + if (((option & 1) == 1) && + ((ReadDataFromDeviceDone & 1) == 0)) { + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadCount, ReferenceSpadCount); + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadType, ReferenceSpadType); + + for (i = 0; i < VL53L0_REF_SPAD_BUFFER_SIZE; i++) { + Dev->Data.SpadData.RefGoodSpadMap[i] = + NvmRefGoodSpadMap[i]; + } + } + + if (((option & 2) == 2) && + ((ReadDataFromDeviceDone & 2) == 0)) { + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ModuleId, ModuleId); + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + Revision, Revision); + + ProductId_tmp = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + ProductId); + VL53L0_COPYSTRING(ProductId_tmp, ProductId); + + } + + if (((option & 4) == 4) && + ((ReadDataFromDeviceDone & 4) == 0)) { + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + PartUIDUpper, PartUIDUpper); + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + PartUIDLower, PartUIDLower); + + SignalRateMeasFixed400mmFix = + VL53L0_FIXPOINT97TOFIXPOINT1616( + SignalRateMeasFixed1104_400_mm); + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + SignalRateMeasFixed400mm, + SignalRateMeasFixed400mmFix); + + OffsetMicroMeters = 0; + if (DistMeasFixed1104_400_mm != 0) { + OffsetFixed1104_mm = + DistMeasFixed1104_400_mm - + DistMeasTgtFixed1104_mm; + OffsetMicroMeters = (OffsetFixed1104_mm + * 1000) >> 4; + OffsetMicroMeters *= -1; + } + + PALDevDataSet(Dev, + Part2PartOffsetAdjustmentNVMMicroMeter, + OffsetMicroMeters); + } + byte = (uint8_t)(ReadDataFromDeviceDone|option); + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, ReadDataFromDeviceDone, + byte); + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +uint32_t VL53L0_calc_macro_period_ps(VL53L0_DEV Dev, uint8_t vcsel_period_pclks) +{ + uint64_t PLL_period_ps; + uint32_t macro_period_vclks; + uint32_t macro_period_ps; + + LOG_FUNCTION_START(""); + + /* The above calculation will produce rounding errors, + * therefore set fixed value + */ + PLL_period_ps = 1655; + + macro_period_vclks = 2304; + macro_period_ps = (uint32_t)(macro_period_vclks + * vcsel_period_pclks * PLL_period_ps); + + LOG_FUNCTION_END(""); + return macro_period_ps; +} + +uint16_t VL53L0_encode_timeout(uint32_t timeout_macro_clks) +{ + /*! + * Encode timeout in macro periods in (LSByte * 2^MSByte) + 1 format + */ + + uint16_t encoded_timeout = 0; + uint32_t ls_byte = 0; + uint16_t ms_byte = 0; + + if (timeout_macro_clks > 0) { + ls_byte = timeout_macro_clks - 1; + + while ((ls_byte & 0xFFFFFF00) > 0) { + ls_byte = ls_byte >> 1; + ms_byte++; + } + + encoded_timeout = (ms_byte << 8) + + (uint16_t) (ls_byte & 0x000000FF); + } + + return encoded_timeout; + +} + +uint32_t VL53L0_decode_timeout(uint16_t encoded_timeout) +{ + /*! + * Decode 16-bit timeout register value - format (LSByte * 2^MSByte) + 1 + */ + + uint32_t timeout_macro_clks = 0; + + timeout_macro_clks = ((uint32_t) (encoded_timeout & 0x00FF) + << (uint32_t) ((encoded_timeout & 0xFF00) >> 8)) + 1; + + return timeout_macro_clks; +} + + +/* To convert ms into register value */ +uint32_t VL53L0_calc_timeout_mclks(VL53L0_DEV Dev, + uint32_t timeout_period_us, + uint8_t vcsel_period_pclks) +{ + uint32_t macro_period_ps; + uint32_t macro_period_ns; + uint32_t timeout_period_mclks = 0; + + macro_period_ps = VL53L0_calc_macro_period_ps(Dev, vcsel_period_pclks); + macro_period_ns = (macro_period_ps + 500) / 1000; + + timeout_period_mclks = + (uint32_t) (((timeout_period_us * 1000) + + (macro_period_ns / 2)) / macro_period_ns); + + return timeout_period_mclks; +} + +/* To convert register value into us */ +uint32_t VL53L0_calc_timeout_us(VL53L0_DEV Dev, + uint16_t timeout_period_mclks, + uint8_t vcsel_period_pclks) +{ + uint32_t macro_period_ps; + uint32_t macro_period_ns; + uint32_t actual_timeout_period_us = 0; + + macro_period_ps = VL53L0_calc_macro_period_ps(Dev, vcsel_period_pclks); + macro_period_ns = (macro_period_ps + 500) / 1000; + + actual_timeout_period_us = + ((timeout_period_mclks * macro_period_ns) + + (macro_period_ns / 2)) / 1000; + + return actual_timeout_period_us; +} + + +VL53L0_Error get_sequence_step_timeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + uint32_t *pTimeOutMicroSecs) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t CurrentVCSELPulsePeriodPClk; + uint8_t EncodedTimeOutByte = 0; + uint32_t TimeoutMicroSeconds = 0; + uint16_t PreRangeEncodedTimeOut = 0; + uint16_t MsrcTimeOutMClks; + uint16_t PreRangeTimeOutMClks; + uint16_t FinalRangeTimeOutMClks = 0; + uint16_t FinalRangeEncodedTimeOut; + VL53L0_SchedulerSequenceSteps_t SchedulerSequenceSteps; + + if ((SequenceStepId == VL53L0_SEQUENCESTEP_TCC) || + (SequenceStepId == VL53L0_SEQUENCESTEP_DSS) || + (SequenceStepId == VL53L0_SEQUENCESTEP_MSRC)) { + + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &CurrentVCSELPulsePeriodPClk); + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdByte(Dev, + VL53L0_REG_MSRC_CONFIG_TIMEOUT_MACROP, + &EncodedTimeOutByte); + } + MsrcTimeOutMClks = VL53L0_decode_timeout(EncodedTimeOutByte); + + TimeoutMicroSeconds = VL53L0_calc_timeout_us(Dev, + MsrcTimeOutMClks, + CurrentVCSELPulsePeriodPClk); + } else if (SequenceStepId == VL53L0_SEQUENCESTEP_PRE_RANGE) { + /* Retrieve PRE-RANGE VCSEL Period */ + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &CurrentVCSELPulsePeriodPClk); + + /* Retrieve PRE-RANGE Timeout in Macro periods (MCLKS) */ + if (Status == VL53L0_ERROR_NONE) { + + /* Retrieve PRE-RANGE VCSEL Period */ + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &CurrentVCSELPulsePeriodPClk); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdWord(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI, + &PreRangeEncodedTimeOut); + } + + PreRangeTimeOutMClks = VL53L0_decode_timeout( + PreRangeEncodedTimeOut); + + TimeoutMicroSeconds = VL53L0_calc_timeout_us(Dev, + PreRangeTimeOutMClks, + CurrentVCSELPulsePeriodPClk); + } + } else if (SequenceStepId == VL53L0_SEQUENCESTEP_FINAL_RANGE) { + + VL53L0_GetSequenceStepEnables(Dev, &SchedulerSequenceSteps); + PreRangeTimeOutMClks = 0; + + if (SchedulerSequenceSteps.PreRangeOn) { + /* Retrieve PRE-RANGE VCSEL Period */ + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &CurrentVCSELPulsePeriodPClk); + + /* Retrieve PRE-RANGE Timeout in Macro periods + * (MCLKS) + */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdWord(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI, + &PreRangeEncodedTimeOut); + PreRangeTimeOutMClks = VL53L0_decode_timeout( + PreRangeEncodedTimeOut); + } + } + + if (Status == VL53L0_ERROR_NONE) { + /* Retrieve FINAL-RANGE VCSEL Period */ + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_FINAL_RANGE, + &CurrentVCSELPulsePeriodPClk); + } + + /* Retrieve FINAL-RANGE Timeout in Macro periods (MCLKS) */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdWord(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI, + &FinalRangeEncodedTimeOut); + FinalRangeTimeOutMClks = VL53L0_decode_timeout( + FinalRangeEncodedTimeOut); + } + + FinalRangeTimeOutMClks -= PreRangeTimeOutMClks; + TimeoutMicroSeconds = VL53L0_calc_timeout_us(Dev, + FinalRangeTimeOutMClks, + CurrentVCSELPulsePeriodPClk); + } + + *pTimeOutMicroSecs = TimeoutMicroSeconds; + + return Status; +} + + +VL53L0_Error set_sequence_step_timeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + uint32_t TimeOutMicroSecs) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t CurrentVCSELPulsePeriodPClk; + uint8_t MsrcEncodedTimeOut; + uint16_t PreRangeEncodedTimeOut; + uint16_t PreRangeTimeOutMClks; + uint16_t MsrcRangeTimeOutMClks; + uint16_t FinalRangeTimeOutMClks; + uint16_t FinalRangeEncodedTimeOut; + VL53L0_SchedulerSequenceSteps_t SchedulerSequenceSteps; + + if ((SequenceStepId == VL53L0_SEQUENCESTEP_TCC) || + (SequenceStepId == VL53L0_SEQUENCESTEP_DSS) || + (SequenceStepId == VL53L0_SEQUENCESTEP_MSRC)) { + + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &CurrentVCSELPulsePeriodPClk); + + if (Status == VL53L0_ERROR_NONE) { + MsrcRangeTimeOutMClks = VL53L0_calc_timeout_mclks(Dev, + TimeOutMicroSecs, + (uint8_t)CurrentVCSELPulsePeriodPClk); + + if (MsrcRangeTimeOutMClks > 256) + MsrcEncodedTimeOut = 255; + else + MsrcEncodedTimeOut = + (uint8_t)MsrcRangeTimeOutMClks - 1; + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + LastEncodedTimeout, + MsrcEncodedTimeOut); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrByte(Dev, + VL53L0_REG_MSRC_CONFIG_TIMEOUT_MACROP, + MsrcEncodedTimeOut); + } + } else { + + if (SequenceStepId == VL53L0_SEQUENCESTEP_PRE_RANGE) { + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &CurrentVCSELPulsePeriodPClk); + PreRangeTimeOutMClks = + VL53L0_calc_timeout_mclks(Dev, + TimeOutMicroSecs, + (uint8_t)CurrentVCSELPulsePeriodPClk); + PreRangeEncodedTimeOut = VL53L0_encode_timeout( + PreRangeTimeOutMClks); + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + LastEncodedTimeout, + PreRangeEncodedTimeOut); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrWord(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI, + PreRangeEncodedTimeOut); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + PreRangeTimeoutMicroSecs, + TimeOutMicroSecs); + } + } else if (SequenceStepId == VL53L0_SEQUENCESTEP_FINAL_RANGE) { + + /* For the final range timeout, the pre-range timeout + * must be added. To do this both final and pre-range + * timeouts must be expressed in macro periods MClks + * because they have different vcsel periods. + */ + + VL53L0_GetSequenceStepEnables(Dev, + &SchedulerSequenceSteps); + PreRangeTimeOutMClks = 0; + if (SchedulerSequenceSteps.PreRangeOn) { + + /* Retrieve PRE-RANGE VCSEL Period */ + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &CurrentVCSELPulsePeriodPClk); + + /* Retrieve PRE-RANGE Timeout in Macro periods + * (MCLKS) + */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdWord(Dev, 0x51, + &PreRangeEncodedTimeOut); + PreRangeTimeOutMClks = + VL53L0_decode_timeout( + PreRangeEncodedTimeOut); + } + } + + /* Calculate FINAL RANGE Timeout in Macro Periods + * (MCLKS) and add PRE-RANGE value + */ + if (Status == VL53L0_ERROR_NONE) { + + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_FINAL_RANGE, + &CurrentVCSELPulsePeriodPClk); + } + if (Status == VL53L0_ERROR_NONE) { + + FinalRangeTimeOutMClks = + VL53L0_calc_timeout_mclks(Dev, + TimeOutMicroSecs, + (uint8_t) CurrentVCSELPulsePeriodPClk); + + FinalRangeTimeOutMClks += PreRangeTimeOutMClks; + + FinalRangeEncodedTimeOut = + VL53L0_encode_timeout(FinalRangeTimeOutMClks); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrWord(Dev, 0x71, + FinalRangeEncodedTimeOut); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + FinalRangeTimeoutMicroSecs, + TimeOutMicroSecs); + } + } + } else + Status = VL53L0_ERROR_INVALID_PARAMS; + + } + return Status; +} + +VL53L0_Error VL53L0_set_vcsel_pulse_period(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t VCSELPulsePeriodPCLK) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t vcsel_period_reg; + uint8_t MinPreVcselPeriodPCLK = 12; + uint8_t MaxPreVcselPeriodPCLK = 18; + uint8_t MinFinalVcselPeriodPCLK = 8; + uint8_t MaxFinalVcselPeriodPCLK = 14; + uint32_t MeasurementTimingBudgetMicroSeconds; + uint32_t FinalRangeTimeoutMicroSeconds; + uint32_t PreRangeTimeoutMicroSeconds; + uint32_t MsrcTimeoutMicroSeconds; + uint8_t PhaseCalInt = 0; + + /* Check if valid clock period requested */ + + if ((VCSELPulsePeriodPCLK % 2) != 0) { + /* Value must be an even number */ + Status = VL53L0_ERROR_INVALID_PARAMS; + } else if (VcselPeriodType == VL53L0_VCSEL_PERIOD_PRE_RANGE && + (VCSELPulsePeriodPCLK < MinPreVcselPeriodPCLK || + VCSELPulsePeriodPCLK > MaxPreVcselPeriodPCLK)) { + Status = VL53L0_ERROR_INVALID_PARAMS; + } else if (VcselPeriodType == VL53L0_VCSEL_PERIOD_FINAL_RANGE && + (VCSELPulsePeriodPCLK < MinFinalVcselPeriodPCLK || + VCSELPulsePeriodPCLK > MaxFinalVcselPeriodPCLK)) { + + Status = VL53L0_ERROR_INVALID_PARAMS; + } + + /* Apply specific settings for the requested clock period */ + + if (Status != VL53L0_ERROR_NONE) + return Status; + + + if (VcselPeriodType == VL53L0_VCSEL_PERIOD_PRE_RANGE) { + + /* Set phase check limits */ + if (VCSELPulsePeriodPCLK == 12) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x18); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + } else if (VCSELPulsePeriodPCLK == 14) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x30); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + } else if (VCSELPulsePeriodPCLK == 16) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x40); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + } else if (VCSELPulsePeriodPCLK == 18) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x50); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + } + } else if (VcselPeriodType == VL53L0_VCSEL_PERIOD_FINAL_RANGE) { + + if (VCSELPulsePeriodPCLK == 8) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x10); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_GLOBAL_CONFIG_VCSEL_WIDTH, 0x02); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_CONFIG_TIMEOUT, 0x0C); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_LIM, + 0x30); + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + } else if (VCSELPulsePeriodPCLK == 10) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x28); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_GLOBAL_CONFIG_VCSEL_WIDTH, 0x03); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_CONFIG_TIMEOUT, 0x09); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_LIM, + 0x20); + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + } else if (VCSELPulsePeriodPCLK == 12) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x38); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_GLOBAL_CONFIG_VCSEL_WIDTH, 0x03); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_CONFIG_TIMEOUT, 0x08); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_LIM, + 0x20); + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + } else if (VCSELPulsePeriodPCLK == 14) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x048); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_GLOBAL_CONFIG_VCSEL_WIDTH, 0x03); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_CONFIG_TIMEOUT, 0x07); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_LIM, + 0x20); + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + } + } + + + /* Re-calculate and apply timeouts, in macro periods */ + + if (Status == VL53L0_ERROR_NONE) { + vcsel_period_reg = VL53L0_encode_vcsel_period((uint8_t) + VCSELPulsePeriodPCLK); + + /* When the VCSEL period for the pre or final range is changed, + * the corresponding timeout must be read from the device using + * the current VCSEL period, then the new VCSEL period can be + * applied. The timeout then must be written back to the device + * using the new VCSEL period. + * + * For the MSRC timeout, the same applies - this timeout being + * dependant on the pre-range vcsel period. + */ + switch (VcselPeriodType) { + case VL53L0_VCSEL_PERIOD_PRE_RANGE: + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_PRE_RANGE, + &PreRangeTimeoutMicroSeconds); + + if (Status == VL53L0_ERROR_NONE) + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_MSRC, + &MsrcTimeoutMicroSeconds); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD, + vcsel_period_reg); + + + if (Status == VL53L0_ERROR_NONE) + Status = set_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_PRE_RANGE, + PreRangeTimeoutMicroSeconds); + + + if (Status == VL53L0_ERROR_NONE) + Status = set_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_MSRC, + MsrcTimeoutMicroSeconds); + + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + PreRangeVcselPulsePeriod, + VCSELPulsePeriodPCLK); + break; + case VL53L0_VCSEL_PERIOD_FINAL_RANGE: + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_FINAL_RANGE, + &FinalRangeTimeoutMicroSeconds); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD, + vcsel_period_reg); + + + if (Status == VL53L0_ERROR_NONE) + Status = set_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_FINAL_RANGE, + FinalRangeTimeoutMicroSeconds); + + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + FinalRangeVcselPulsePeriod, + VCSELPulsePeriodPCLK); + break; + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + /* Finally, the timing budget must be re-applied */ + if (Status == VL53L0_ERROR_NONE) { + VL53L0_GETPARAMETERFIELD(Dev, + MeasurementTimingBudgetMicroSeconds, + MeasurementTimingBudgetMicroSeconds); + + Status = VL53L0_SetMeasurementTimingBudgetMicroSeconds(Dev, + MeasurementTimingBudgetMicroSeconds); + } + + /* Perform the phase calibration. This is needed after changing on + * vcsel period. + * get_data_enable = 0, restore_config = 1 + */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_perform_phase_calibration( + Dev, &PhaseCalInt, 0, 1); + + return Status; +} + +VL53L0_Error VL53L0_get_vcsel_pulse_period(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t *pVCSELPulsePeriodPCLK) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t vcsel_period_reg; + + switch (VcselPeriodType) { + case VL53L0_VCSEL_PERIOD_PRE_RANGE: + Status = VL53L0_RdByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD, + &vcsel_period_reg); + break; + case VL53L0_VCSEL_PERIOD_FINAL_RANGE: + Status = VL53L0_RdByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD, + &vcsel_period_reg); + break; + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + } + + if (Status == VL53L0_ERROR_NONE) + *pVCSELPulsePeriodPCLK = + VL53L0_decode_vcsel_period(vcsel_period_reg); + + return Status; +} + + + +VL53L0_Error VL53L0_set_measurement_timing_budget_micro_seconds(VL53L0_DEV Dev, + uint32_t MeasurementTimingBudgetMicroSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint32_t FinalRangeTimingBudgetMicroSeconds; + VL53L0_SchedulerSequenceSteps_t SchedulerSequenceSteps; + uint32_t MsrcDccTccTimeoutMicroSeconds = 2000; + uint32_t StartOverheadMicroSeconds = 1320; + uint32_t EndOverheadMicroSeconds = 960; + uint32_t MsrcOverheadMicroSeconds = 660; + uint32_t TccOverheadMicroSeconds = 590; + uint32_t DssOverheadMicroSeconds = 690; + uint32_t PreRangeOverheadMicroSeconds = 660; + uint32_t FinalRangeOverheadMicroSeconds = 550; + uint32_t PreRangeTimeoutMicroSeconds = 0; + uint32_t cMinTimingBudgetMicroSeconds = 20000; + uint32_t SubTimeout = 0; + + LOG_FUNCTION_START(""); + + if (MeasurementTimingBudgetMicroSeconds + < cMinTimingBudgetMicroSeconds) { + Status = VL53L0_ERROR_INVALID_PARAMS; + return Status; + } + + FinalRangeTimingBudgetMicroSeconds = + MeasurementTimingBudgetMicroSeconds - + (StartOverheadMicroSeconds + EndOverheadMicroSeconds); + + Status = VL53L0_GetSequenceStepEnables(Dev, &SchedulerSequenceSteps); + + if (Status == VL53L0_ERROR_NONE && + (SchedulerSequenceSteps.TccOn || + SchedulerSequenceSteps.MsrcOn || + SchedulerSequenceSteps.DssOn)) { + + /* TCC, MSRC and DSS all share the same timeout */ + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_MSRC, + &MsrcDccTccTimeoutMicroSeconds); + + /* Subtract the TCC, MSRC and DSS timeouts if they are + * enabled. + */ + + if (Status != VL53L0_ERROR_NONE) + return Status; + + /* TCC */ + if (SchedulerSequenceSteps.TccOn) { + + SubTimeout = MsrcDccTccTimeoutMicroSeconds + + TccOverheadMicroSeconds; + + if (SubTimeout < + FinalRangeTimingBudgetMicroSeconds) { + FinalRangeTimingBudgetMicroSeconds -= + SubTimeout; + } else { + /* Requested timeout too big. */ + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + if (Status != VL53L0_ERROR_NONE) { + LOG_FUNCTION_END(Status); + return Status; + } + + /* DSS */ + if (SchedulerSequenceSteps.DssOn) { + + SubTimeout = 2 * (MsrcDccTccTimeoutMicroSeconds + + DssOverheadMicroSeconds); + + if (SubTimeout < FinalRangeTimingBudgetMicroSeconds) { + FinalRangeTimingBudgetMicroSeconds + -= SubTimeout; + } else { + /* Requested timeout too big. */ + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } else if (SchedulerSequenceSteps.MsrcOn) { + /* MSRC */ + SubTimeout = MsrcDccTccTimeoutMicroSeconds + + MsrcOverheadMicroSeconds; + + if (SubTimeout < FinalRangeTimingBudgetMicroSeconds) { + FinalRangeTimingBudgetMicroSeconds + -= SubTimeout; + } else { + /* Requested timeout too big. */ + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + } + + if (Status != VL53L0_ERROR_NONE) { + LOG_FUNCTION_END(Status); + return Status; + } + + if (SchedulerSequenceSteps.PreRangeOn) { + + /* Subtract the Pre-range timeout if enabled. */ + + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_PRE_RANGE, + &PreRangeTimeoutMicroSeconds); + + SubTimeout = PreRangeTimeoutMicroSeconds + + PreRangeOverheadMicroSeconds; + + if (SubTimeout < FinalRangeTimingBudgetMicroSeconds) { + FinalRangeTimingBudgetMicroSeconds -= SubTimeout; + } else { + /* Requested timeout too big. */ + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + + if (Status == VL53L0_ERROR_NONE && + SchedulerSequenceSteps.FinalRangeOn) { + + FinalRangeTimingBudgetMicroSeconds -= + FinalRangeOverheadMicroSeconds; + + /* Final Range Timeout + * Note that the final range timeout is determined by the timing + * budget and the sum of all other timeouts within the sequence. + * If there is no room for the final range timeout, then an error + * will be set. Otherwise the remaining time will be applied to + * the final range. + */ + Status = set_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_FINAL_RANGE, + FinalRangeTimingBudgetMicroSeconds); + + VL53L0_SETPARAMETERFIELD(Dev, + MeasurementTimingBudgetMicroSeconds, + MeasurementTimingBudgetMicroSeconds); + } + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error VL53L0_get_measurement_timing_budget_micro_seconds(VL53L0_DEV Dev, + uint32_t *pMeasurementTimingBudgetMicroSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_SchedulerSequenceSteps_t SchedulerSequenceSteps; + uint32_t FinalRangeTimeoutMicroSeconds; + uint32_t MsrcDccTccTimeoutMicroSeconds = 2000; + uint32_t StartOverheadMicroSeconds = 1910; + uint32_t EndOverheadMicroSeconds = 960; + uint32_t MsrcOverheadMicroSeconds = 660; + uint32_t TccOverheadMicroSeconds = 590; + uint32_t DssOverheadMicroSeconds = 690; + uint32_t PreRangeOverheadMicroSeconds = 660; + uint32_t FinalRangeOverheadMicroSeconds = 550; + uint32_t PreRangeTimeoutMicroSeconds = 0; + + LOG_FUNCTION_START(""); + + /* Start and end overhead times always present */ + *pMeasurementTimingBudgetMicroSeconds + = StartOverheadMicroSeconds + EndOverheadMicroSeconds; + + Status = VL53L0_GetSequenceStepEnables(Dev, &SchedulerSequenceSteps); + + if (Status != VL53L0_ERROR_NONE) { + LOG_FUNCTION_END(Status); + return Status; + } + + + if (SchedulerSequenceSteps.TccOn || + SchedulerSequenceSteps.MsrcOn || + SchedulerSequenceSteps.DssOn) { + + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_MSRC, + &MsrcDccTccTimeoutMicroSeconds); + + if (Status == VL53L0_ERROR_NONE) { + if (SchedulerSequenceSteps.TccOn) { + *pMeasurementTimingBudgetMicroSeconds += + MsrcDccTccTimeoutMicroSeconds + + TccOverheadMicroSeconds; + } + + if (SchedulerSequenceSteps.DssOn) { + *pMeasurementTimingBudgetMicroSeconds += + 2 * (MsrcDccTccTimeoutMicroSeconds + + DssOverheadMicroSeconds); + } else if (SchedulerSequenceSteps.MsrcOn) { + *pMeasurementTimingBudgetMicroSeconds += + MsrcDccTccTimeoutMicroSeconds + + MsrcOverheadMicroSeconds; + } + } + } + + if (Status == VL53L0_ERROR_NONE) { + if (SchedulerSequenceSteps.PreRangeOn) { + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_PRE_RANGE, + &PreRangeTimeoutMicroSeconds); + *pMeasurementTimingBudgetMicroSeconds += + PreRangeTimeoutMicroSeconds + + PreRangeOverheadMicroSeconds; + } + } + + if (Status == VL53L0_ERROR_NONE) { + if (SchedulerSequenceSteps.FinalRangeOn) { + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_FINAL_RANGE, + &FinalRangeTimeoutMicroSeconds); + *pMeasurementTimingBudgetMicroSeconds += + (FinalRangeTimeoutMicroSeconds + + FinalRangeOverheadMicroSeconds); + } + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETPARAMETERFIELD(Dev, + MeasurementTimingBudgetMicroSeconds, + *pMeasurementTimingBudgetMicroSeconds); + } + + LOG_FUNCTION_END(Status); + return Status; +} + + + +VL53L0_Error VL53L0_load_tuning_settings(VL53L0_DEV Dev, + uint8_t *pTuningSettingBuffer) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int i; + int Index; + uint8_t msb; + uint8_t lsb; + uint8_t SelectParam; + uint8_t NumberOfWrites; + uint8_t Address; + uint8_t localBuffer[4]; /* max */ + uint16_t Temp16; + + LOG_FUNCTION_START(""); + + Index = 0; + + while ((*(pTuningSettingBuffer + Index) != 0) && + (Status == VL53L0_ERROR_NONE)) { + NumberOfWrites = *(pTuningSettingBuffer + Index); + Index++; + if (NumberOfWrites == 0xFF) { + /* internal parameters */ + SelectParam = *(pTuningSettingBuffer + Index); + Index++; + switch (SelectParam) { + case 0: /* uint16_t SigmaEstRefArray -> 2 bytes */ + msb = *(pTuningSettingBuffer + Index); + Index++; + lsb = *(pTuningSettingBuffer + Index); + Index++; + Temp16 = VL53L0_MAKEUINT16(lsb, msb); + PALDevDataSet(Dev, SigmaEstRefArray, Temp16); + break; + case 1: /* uint16_t SigmaEstEffPulseWidth -> 2 bytes */ + msb = *(pTuningSettingBuffer + Index); + Index++; + lsb = *(pTuningSettingBuffer + Index); + Index++; + Temp16 = VL53L0_MAKEUINT16(lsb, msb); + PALDevDataSet(Dev, SigmaEstEffPulseWidth, + Temp16); + break; + case 2: /* uint16_t SigmaEstEffAmbWidth -> 2 bytes */ + msb = *(pTuningSettingBuffer + Index); + Index++; + lsb = *(pTuningSettingBuffer + Index); + Index++; + Temp16 = VL53L0_MAKEUINT16(lsb, msb); + PALDevDataSet(Dev, SigmaEstEffAmbWidth, Temp16); + break; + case 3: /* uint16_t targetRefRate -> 2 bytes */ + msb = *(pTuningSettingBuffer + Index); + Index++; + lsb = *(pTuningSettingBuffer + Index); + Index++; + Temp16 = VL53L0_MAKEUINT16(lsb, msb); + PALDevDataSet(Dev, targetRefRate, Temp16); + break; + default: /* invalid parameter */ + Status = VL53L0_ERROR_INVALID_PARAMS; + } + + } else if (NumberOfWrites <= 4) { + Address = *(pTuningSettingBuffer + Index); + Index++; + + for (i = 0; i < NumberOfWrites; i++) { + localBuffer[i] = *(pTuningSettingBuffer + + Index); + Index++; + } + + Status = VL53L0_WriteMulti(Dev, Address, localBuffer, + NumberOfWrites); + + } else { + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_get_total_xtalk_rate(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + FixPoint1616_t *ptotal_xtalk_rate_mcps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + uint8_t xtalkCompEnable; + FixPoint1616_t totalXtalkMegaCps; + FixPoint1616_t xtalkPerSpadMegaCps; + + *ptotal_xtalk_rate_mcps = 0; + + Status = VL53L0_GetXTalkCompensationEnable(Dev, &xtalkCompEnable); + if (Status == VL53L0_ERROR_NONE) { + + if (xtalkCompEnable) { + + VL53L0_GETPARAMETERFIELD( + Dev, + XTalkCompensationRateMegaCps, + xtalkPerSpadMegaCps); + + /* FixPoint1616 * FixPoint 8:8 = FixPoint0824 */ + totalXtalkMegaCps = + pRangingMeasurementData->EffectiveSpadRtnCount * + xtalkPerSpadMegaCps; + + /* FixPoint0824 >> 8 = FixPoint1616 */ + *ptotal_xtalk_rate_mcps = + (totalXtalkMegaCps + 0x80) >> 8; + } + } + + return Status; +} + +VL53L0_Error VL53L0_get_total_signal_rate(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + FixPoint1616_t *ptotal_signal_rate_mcps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + FixPoint1616_t totalXtalkMegaCps; + + LOG_FUNCTION_START(""); + + *ptotal_signal_rate_mcps = + pRangingMeasurementData->SignalRateRtnMegaCps; + + Status = VL53L0_get_total_xtalk_rate( + Dev, pRangingMeasurementData, &totalXtalkMegaCps); + + if (Status == VL53L0_ERROR_NONE) + *ptotal_signal_rate_mcps += totalXtalkMegaCps; + + return Status; +} + +VL53L0_Error VL53L0_calc_dmax( + VL53L0_DEV Dev, + FixPoint1616_t totalSignalRate_mcps, + FixPoint1616_t totalCorrSignalRate_mcps, + FixPoint1616_t pwMult, + uint32_t sigmaEstimateP1, + FixPoint1616_t sigmaEstimateP2, + uint32_t peakVcselDuration_us, + uint32_t *pdmax_mm) +{ + const uint32_t cSigmaLimit = 18; + const FixPoint1616_t cSignalLimit = 0x4000; /* 0.25 */ + const FixPoint1616_t cSigmaEstRef = 0x00000042; /* 0.001 */ + const uint32_t cAmbEffWidthSigmaEst_ns = 6; + const uint32_t cAmbEffWidthDMax_ns = 7; + uint32_t dmaxCalRange_mm; + FixPoint1616_t dmaxCalSignalRateRtn_mcps; + FixPoint1616_t minSignalNeeded; + FixPoint1616_t minSignalNeeded_p1; + FixPoint1616_t minSignalNeeded_p2; + FixPoint1616_t minSignalNeeded_p3; + FixPoint1616_t minSignalNeeded_p4; + FixPoint1616_t sigmaLimitTmp; + FixPoint1616_t sigmaEstSqTmp; + FixPoint1616_t signalLimitTmp; + FixPoint1616_t SignalAt0mm; + FixPoint1616_t dmaxDark; + FixPoint1616_t dmaxAmbient; + FixPoint1616_t dmaxDarkTmp; + FixPoint1616_t sigmaEstP2Tmp; + uint32_t signalRateTemp_mcps; + + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + dmaxCalRange_mm = + PALDevDataGet(Dev, DmaxCalRangeMilliMeter); + + dmaxCalSignalRateRtn_mcps = + PALDevDataGet(Dev, DmaxCalSignalRateRtnMegaCps); + + /* uint32 * FixPoint1616 = FixPoint1616 */ + SignalAt0mm = dmaxCalRange_mm * dmaxCalSignalRateRtn_mcps; + + /* FixPoint1616 >> 8 = FixPoint2408 */ + SignalAt0mm = (SignalAt0mm + 0x80) >> 8; + SignalAt0mm *= dmaxCalRange_mm; + + minSignalNeeded_p1 = 0; + if (totalCorrSignalRate_mcps > 0) { + + /* Shift by 10 bits to increase resolution prior to the + * division + */ + signalRateTemp_mcps = totalSignalRate_mcps << 10; + + /* Add rounding value prior to division */ + minSignalNeeded_p1 = signalRateTemp_mcps + + (totalCorrSignalRate_mcps/2); + + /* FixPoint0626/FixPoint1616 = FixPoint2210 */ + minSignalNeeded_p1 /= totalCorrSignalRate_mcps; + + /* Apply a factored version of the speed of light. + * Correction to be applied at the end + */ + minSignalNeeded_p1 *= 3; + + /* FixPoint2210 * FixPoint2210 = FixPoint1220 */ + minSignalNeeded_p1 *= minSignalNeeded_p1; + + /* FixPoint1220 >> 16 = FixPoint2804 */ + minSignalNeeded_p1 = (minSignalNeeded_p1 + 0x8000) >> 16; + } + + minSignalNeeded_p2 = pwMult * sigmaEstimateP1; + + /* FixPoint1616 >> 16 = uint32 */ + minSignalNeeded_p2 = (minSignalNeeded_p2 + 0x8000) >> 16; + + /* uint32 * uint32 = uint32 */ + minSignalNeeded_p2 *= minSignalNeeded_p2; + + /* Check sigmaEstimateP2 + * If this value is too high there is not enough signal rate + * to calculate dmax value so set a suitable value to ensure + * a very small dmax. + */ + sigmaEstP2Tmp = (sigmaEstimateP2 + 0x8000) >> 16; + sigmaEstP2Tmp = (sigmaEstP2Tmp + cAmbEffWidthSigmaEst_ns/2)/ + cAmbEffWidthSigmaEst_ns; + sigmaEstP2Tmp *= cAmbEffWidthDMax_ns; + + if (sigmaEstP2Tmp > 0xffff) { + minSignalNeeded_p3 = 0xfff00000; + } else { + + /* DMAX uses a different ambient width from sigma, so apply + * correction. + * Perform division before multiplication to prevent overflow. + */ + sigmaEstimateP2 = (sigmaEstimateP2 + cAmbEffWidthSigmaEst_ns/2)/ + cAmbEffWidthSigmaEst_ns; + sigmaEstimateP2 *= cAmbEffWidthDMax_ns; + + /* FixPoint1616 >> 16 = uint32 */ + minSignalNeeded_p3 = (sigmaEstimateP2 + 0x8000) >> 16; + + minSignalNeeded_p3 *= minSignalNeeded_p3; + + } + + /* FixPoint1814 / uint32 = FixPoint1814 */ + sigmaLimitTmp = ((cSigmaLimit << 14) + 500) / 1000; + + /* FixPoint1814 * FixPoint1814 = FixPoint3628 := FixPoint0428 */ + sigmaLimitTmp *= sigmaLimitTmp; + + /* FixPoint1616 * FixPoint1616 = FixPoint3232 */ + sigmaEstSqTmp = cSigmaEstRef * cSigmaEstRef; + + /* FixPoint3232 >> 4 = FixPoint0428 */ + sigmaEstSqTmp = (sigmaEstSqTmp + 0x08) >> 4; + + /* FixPoint0428 - FixPoint0428 = FixPoint0428 */ + sigmaLimitTmp -= sigmaEstSqTmp; + + /* uint32_t * FixPoint0428 = FixPoint0428 */ + minSignalNeeded_p4 = 4 * 12 * sigmaLimitTmp; + + /* FixPoint0428 >> 14 = FixPoint1814 */ + minSignalNeeded_p4 = (minSignalNeeded_p4 + 0x2000) >> 14; + + /* uint32 + uint32 = uint32 */ + minSignalNeeded = (minSignalNeeded_p2 + minSignalNeeded_p3); + + /* uint32 / uint32 = uint32 */ + minSignalNeeded += (peakVcselDuration_us/2); + minSignalNeeded /= peakVcselDuration_us; + + /* uint32 << 14 = FixPoint1814 */ + minSignalNeeded <<= 14; + + /* FixPoint1814 / FixPoint1814 = uint32 */ + minSignalNeeded += (minSignalNeeded_p4/2); + minSignalNeeded /= minSignalNeeded_p4; + + /* FixPoint3200 * FixPoint2804 := FixPoint2804*/ + minSignalNeeded *= minSignalNeeded_p1; + + /* Apply correction by dividing by 1000000. + * This assumes 10E16 on the numerator of the equation + * and 10E-22 on the denominator. + * We do this because 32bit fix point calculation can't + * handle the larger and smaller elements of this equation, + * i.e. speed of light and pulse widths. + */ + minSignalNeeded = (minSignalNeeded + 500) / 1000; + minSignalNeeded <<= 4; + + minSignalNeeded = (minSignalNeeded + 500) / 1000; + + /* FixPoint1616 >> 8 = FixPoint2408 */ + signalLimitTmp = (cSignalLimit + 0x80) >> 8; + + /* FixPoint2408/FixPoint2408 = uint32 */ + if (signalLimitTmp != 0) + dmaxDarkTmp = (SignalAt0mm + (signalLimitTmp / 2)) + / signalLimitTmp; + else + dmaxDarkTmp = 0; + + dmaxDark = VL53L0_isqrt(dmaxDarkTmp); + + /* FixPoint2408/FixPoint2408 = uint32 */ + if (minSignalNeeded != 0) + dmaxAmbient = (SignalAt0mm + minSignalNeeded/2) + / minSignalNeeded; + else + dmaxAmbient = 0; + + dmaxAmbient = VL53L0_isqrt(dmaxAmbient); + + *pdmax_mm = dmaxDark; + if (dmaxDark > dmaxAmbient) + *pdmax_mm = dmaxAmbient; + + LOG_FUNCTION_END(Status); + + return Status; +} + + +VL53L0_Error VL53L0_calc_sigma_estimate(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + FixPoint1616_t *pSigmaEstimate, + uint32_t *pDmax_mm) +{ + /* Expressed in 100ths of a ns, i.e. centi-ns */ + const uint32_t cPulseEffectiveWidth_centi_ns = 800; + /* Expressed in 100ths of a ns, i.e. centi-ns */ + const uint32_t cAmbientEffectiveWidth_centi_ns = 600; + const FixPoint1616_t cSigmaEstRef = 0x00000042; /* 0.001 */ + const uint32_t cVcselPulseWidth_ps = 4700; /* pico secs */ + const FixPoint1616_t cSigmaEstMax = 0x028F87AE; + const FixPoint1616_t cSigmaEstRtnMax = 0xF000; + const FixPoint1616_t cAmbToSignalRatioMax = 0xF0000000/ + cAmbientEffectiveWidth_centi_ns; + /* Time Of Flight per mm (6.6 pico secs) */ + const FixPoint1616_t cTOF_per_mm_ps = 0x0006999A; + const uint32_t c16BitRoundingParam = 0x00008000; + const FixPoint1616_t cMaxXTalk_kcps = 0x00320000; + const uint32_t cPllPeriod_ps = 1655; + + uint32_t vcselTotalEventsRtn; + uint32_t finalRangeTimeoutMicroSecs; + uint32_t preRangeTimeoutMicroSecs; + FixPoint1616_t sigmaEstimateP1; + FixPoint1616_t sigmaEstimateP2; + FixPoint1616_t sigmaEstimateP3; + FixPoint1616_t deltaT_ps; + FixPoint1616_t pwMult; + FixPoint1616_t sigmaEstRtn; + FixPoint1616_t sigmaEstimate; + FixPoint1616_t xTalkCorrection; + FixPoint1616_t ambientRate_kcps; + FixPoint1616_t peakSignalRate_kcps; + FixPoint1616_t xTalkCompRate_mcps; + uint32_t xTalkCompRate_kcps; + VL53L0_Error Status = VL53L0_ERROR_NONE; + FixPoint1616_t diff1_mcps; + FixPoint1616_t diff2_mcps; + FixPoint1616_t sqr1; + FixPoint1616_t sqr2; + FixPoint1616_t sqrSum; + FixPoint1616_t sqrtResult_centi_ns; + FixPoint1616_t sqrtResult; + FixPoint1616_t totalSignalRate_mcps; + FixPoint1616_t correctedSignalRate_mcps; + uint32_t vcselWidth; + uint32_t finalRangeMacroPCLKS; + uint32_t preRangeMacroPCLKS; + uint32_t peakVcselDuration_us; + uint8_t finalRangeVcselPCLKS; + uint8_t preRangeVcselPCLKS; + /*! \addtogroup calc_sigma_estimate + * @{ + * + * Estimates the range sigma based on the + * + * - vcsel_rate_kcps + * - ambient_rate_kcps + * - signal_total_events + * - xtalk_rate + * + * and the following parameters + * + * - SigmaEstRefArray + * - SigmaEstEffPulseWidth + * - SigmaEstEffAmbWidth + */ + + LOG_FUNCTION_START(""); + + VL53L0_GETPARAMETERFIELD(Dev, XTalkCompensationRateMegaCps, + xTalkCompRate_mcps); + + /* + * We work in kcps rather than mcps as this helps keep within the + * confines of the 32 Fix1616 type. + */ + + ambientRate_kcps = + (pRangingMeasurementData->AmbientRateRtnMegaCps * 1000) >> 16; + + correctedSignalRate_mcps = + pRangingMeasurementData->SignalRateRtnMegaCps; + + + Status = VL53L0_get_total_signal_rate( + Dev, pRangingMeasurementData, &totalSignalRate_mcps); + Status = VL53L0_get_total_xtalk_rate( + Dev, pRangingMeasurementData, &xTalkCompRate_mcps); + + + /* Signal rate measurement provided by device is the + * peak signal rate, not average. + */ + peakSignalRate_kcps = (totalSignalRate_mcps * 1000); + peakSignalRate_kcps = (peakSignalRate_kcps + 0x8000) >> 16; + + xTalkCompRate_kcps = xTalkCompRate_mcps * 1000; + + if (xTalkCompRate_kcps > cMaxXTalk_kcps) + xTalkCompRate_kcps = cMaxXTalk_kcps; + + if (Status == VL53L0_ERROR_NONE) { + + /* Calculate final range macro periods */ + finalRangeTimeoutMicroSecs = VL53L0_GETDEVICESPECIFICPARAMETER( + Dev, FinalRangeTimeoutMicroSecs); + + finalRangeVcselPCLKS = VL53L0_GETDEVICESPECIFICPARAMETER( + Dev, FinalRangeVcselPulsePeriod); + + finalRangeMacroPCLKS = VL53L0_calc_timeout_mclks( + Dev, finalRangeTimeoutMicroSecs, finalRangeVcselPCLKS); + + /* Calculate pre-range macro periods */ + preRangeTimeoutMicroSecs = VL53L0_GETDEVICESPECIFICPARAMETER( + Dev, PreRangeTimeoutMicroSecs); + + preRangeVcselPCLKS = VL53L0_GETDEVICESPECIFICPARAMETER( + Dev, PreRangeVcselPulsePeriod); + + preRangeMacroPCLKS = VL53L0_calc_timeout_mclks( + Dev, preRangeTimeoutMicroSecs, preRangeVcselPCLKS); + + vcselWidth = 3; + if (finalRangeVcselPCLKS == 8) + vcselWidth = 2; + + + peakVcselDuration_us = vcselWidth * 2048 * + (preRangeMacroPCLKS + finalRangeMacroPCLKS); + peakVcselDuration_us = (peakVcselDuration_us + 500)/1000; + peakVcselDuration_us *= cPllPeriod_ps; + peakVcselDuration_us = (peakVcselDuration_us + 500)/1000; + + /* Fix1616 >> 8 = Fix2408 */ + totalSignalRate_mcps = (totalSignalRate_mcps + 0x80) >> 8; + + /* Fix2408 * uint32 = Fix2408 */ + vcselTotalEventsRtn = totalSignalRate_mcps * + peakVcselDuration_us; + + /* Fix2408 >> 8 = uint32 */ + vcselTotalEventsRtn = (vcselTotalEventsRtn + 0x80) >> 8; + + /* Fix2408 << 8 = Fix1616 = */ + totalSignalRate_mcps <<= 8; + } + + if (Status != VL53L0_ERROR_NONE) { + LOG_FUNCTION_END(Status); + return Status; + } + + if (peakSignalRate_kcps == 0) { + *pSigmaEstimate = cSigmaEstMax; + PALDevDataSet(Dev, SigmaEstimate, cSigmaEstMax); + *pDmax_mm = 0; + } else { + if (vcselTotalEventsRtn < 1) + vcselTotalEventsRtn = 1; + + /* + * Calculate individual components of the main equation - + * replicating the equation implemented in the script + * OpenAll_Ewok_ranging_data.jsl. + * + * sigmaEstimateP1 represents the effective pulse width, which + * is a tuning parameter, rather than a real value. + * + * sigmaEstimateP2 represents the ambient/signal rate ratio + * expressed as a multiple of the effective ambient width + * (tuning parameter). + * + * sigmaEstimateP3 provides the signal event component, with the + * knowledge that + * - Noise of a square pulse is 1/sqrt(12) of the pulse + * width. + * - at 0Lux, sigma is proportional to + * effectiveVcselPulseWidth/sqrt(12 * signalTotalEvents) + * + * deltaT_ps represents the time of flight in pico secs for the + * current range measurement, using the "TOF per mm" constant + * (in ps). + */ + + sigmaEstimateP1 = cPulseEffectiveWidth_centi_ns; + + /* ((FixPoint1616 << 16)* uint32)/uint32 = FixPoint1616 */ + sigmaEstimateP2 = (ambientRate_kcps << 16)/peakSignalRate_kcps; + if (sigmaEstimateP2 > cAmbToSignalRatioMax) { + /* Clip to prevent overflow. Will ensure safe + * max result. + */ + sigmaEstimateP2 = cAmbToSignalRatioMax; + } + sigmaEstimateP2 *= cAmbientEffectiveWidth_centi_ns; + + sigmaEstimateP3 = 2 * VL53L0_isqrt(vcselTotalEventsRtn * 12); + + /* uint32 * FixPoint1616 = FixPoint1616 */ + deltaT_ps = pRangingMeasurementData->RangeMilliMeter * + cTOF_per_mm_ps; + + /* + * vcselRate - xtalkCompRate + * (uint32 << 16) - FixPoint1616 = FixPoint1616. + * Divide result by 1000 to convert to mcps. + * 500 is added to ensure rounding when integer division + * truncates. + */ + diff1_mcps = (((peakSignalRate_kcps << 16) - + xTalkCompRate_kcps) + 500)/1000; + + /* vcselRate + xtalkCompRate */ + diff2_mcps = (((peakSignalRate_kcps << 16) + + xTalkCompRate_kcps) + 500)/1000; + + /* Shift by 8 bits to increase resolution prior to the + * division + */ + diff1_mcps <<= 8; + + /* FixPoint0824/FixPoint1616 = FixPoint2408 */ + xTalkCorrection = abs(diff1_mcps/diff2_mcps); + + /* FixPoint2408 << 8 = FixPoint1616 */ + xTalkCorrection <<= 8; + + /* FixPoint1616/uint32 = FixPoint1616 */ + pwMult = deltaT_ps/cVcselPulseWidth_ps; /* smaller than 1.0f */ + + /* + * FixPoint1616 * FixPoint1616 = FixPoint3232, however both + * values are small enough such that32 bits will not be + * exceeded. + */ + pwMult *= ((1 << 16) - xTalkCorrection); + + /* (FixPoint3232 >> 16) = FixPoint1616 */ + pwMult = (pwMult + c16BitRoundingParam) >> 16; + + /* FixPoint1616 + FixPoint1616 = FixPoint1616 */ + pwMult += (1 << 16); + + /* + * At this point the value will be 1.xx, therefore if we square + * the value this will exceed 32 bits. To address this perform + * a single shift to the right before the multiplication. + */ + pwMult >>= 1; + /* FixPoint1715 * FixPoint1715 = FixPoint3430 */ + pwMult = pwMult * pwMult; + + /* (FixPoint3430 >> 14) = Fix1616 */ + pwMult >>= 14; + + /* FixPoint1616 * uint32 = FixPoint1616 */ + sqr1 = pwMult * sigmaEstimateP1; + + /* (FixPoint1616 >> 16) = FixPoint3200 */ + sqr1 = (sqr1 + 0x8000) >> 16; + + /* FixPoint3200 * FixPoint3200 = FixPoint6400 */ + sqr1 *= sqr1; + + sqr2 = sigmaEstimateP2; + + /* (FixPoint1616 >> 16) = FixPoint3200 */ + sqr2 = (sqr2 + 0x8000) >> 16; + + /* FixPoint3200 * FixPoint3200 = FixPoint6400 */ + sqr2 *= sqr2; + + /* FixPoint64000 + FixPoint6400 = FixPoint6400 */ + sqrSum = sqr1 + sqr2; + + /* SQRT(FixPoin6400) = FixPoint3200 */ + sqrtResult_centi_ns = VL53L0_isqrt(sqrSum); + + /* (FixPoint3200 << 16) = FixPoint1616 */ + sqrtResult_centi_ns <<= 16; + + /* + * Note that the Speed Of Light is expressed in um per 1E-10 + * seconds (2997) Therefore to get mm/ns we have to divide by + * 10000 + */ + sigmaEstRtn = (((sqrtResult_centi_ns+50)/100) / + sigmaEstimateP3); + sigmaEstRtn *= VL53L0_SPEED_OF_LIGHT_IN_AIR; + + /* Add 5000 before dividing by 10000 to ensure rounding. */ + sigmaEstRtn += 5000; + sigmaEstRtn /= 10000; + + if (sigmaEstRtn > cSigmaEstRtnMax) { + /* Clip to prevent overflow. Will ensure safe + * max result. + */ + sigmaEstRtn = cSigmaEstRtnMax; + } + + /* FixPoint1616 * FixPoint1616 = FixPoint3232 */ + sqr1 = sigmaEstRtn * sigmaEstRtn; + /* FixPoint1616 * FixPoint1616 = FixPoint3232 */ + sqr2 = cSigmaEstRef * cSigmaEstRef; + + /* sqrt(FixPoint3232) = FixPoint1616 */ + sqrtResult = VL53L0_isqrt((sqr1 + sqr2)); + /* + * Note that the Shift by 4 bits increases resolution prior to + * the sqrt, therefore the result must be shifted by 2 bits to + * the right to revert back to the FixPoint1616 format. + */ + + sigmaEstimate = 1000 * sqrtResult; + + if ((peakSignalRate_kcps < 1) || (vcselTotalEventsRtn < 1) || + (sigmaEstimate > cSigmaEstMax)) { + sigmaEstimate = cSigmaEstMax; + } + + *pSigmaEstimate = (uint32_t)(sigmaEstimate); + PALDevDataSet(Dev, SigmaEstimate, *pSigmaEstimate); + Status = VL53L0_calc_dmax( + Dev, + totalSignalRate_mcps, + correctedSignalRate_mcps, + pwMult, + sigmaEstimateP1, + sigmaEstimateP2, + peakVcselDuration_us, + pDmax_mm); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_get_pal_range_status(VL53L0_DEV Dev, + uint8_t DeviceRangeStatus, + FixPoint1616_t SignalRate, + uint16_t EffectiveSpadRtnCount, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + uint8_t *pPalRangeStatus) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t NoneFlag; + uint8_t SigmaLimitflag = 0; + uint8_t SignalRefClipflag = 0; + uint8_t RangeIgnoreThresholdflag = 0; + uint8_t SigmaLimitCheckEnable = 0; + uint8_t SignalRateFinalRangeLimitCheckEnable = 0; + uint8_t SignalRefClipLimitCheckEnable = 0; + uint8_t RangeIgnoreThresholdLimitCheckEnable = 0; + FixPoint1616_t SigmaEstimate; + FixPoint1616_t SigmaLimitValue; + FixPoint1616_t SignalRefClipValue; + FixPoint1616_t RangeIgnoreThresholdValue; + FixPoint1616_t SignalRatePerSpad; + uint8_t DeviceRangeStatusInternal = 0; + uint16_t tmpWord = 0; + uint8_t Temp8; + uint32_t Dmax_mm = 0; + FixPoint1616_t LastSignalRefMcps; + + LOG_FUNCTION_START(""); + + + /* + * VL53L0 has a good ranging when the value of the + * DeviceRangeStatus = 11. This function will replace the value 0 with + * the value 11 in the DeviceRangeStatus. + * In addition, the SigmaEstimator is not included in the VL53L0 + * DeviceRangeStatus, this will be added in the PalRangeStatus. + */ + + DeviceRangeStatusInternal = ((DeviceRangeStatus & 0x78) >> 3); + + if (DeviceRangeStatusInternal == 0 || + DeviceRangeStatusInternal == 5 || + DeviceRangeStatusInternal == 7 || + DeviceRangeStatusInternal == 12 || + DeviceRangeStatusInternal == 13 || + DeviceRangeStatusInternal == 14 || + DeviceRangeStatusInternal == 15 + ) { + NoneFlag = 1; + } else { + NoneFlag = 0; + } + + /* LastSignalRefMcps */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_RdWord(Dev, + VL53L0_REG_RESULT_PEAK_SIGNAL_RATE_REF, + &tmpWord); + + LastSignalRefMcps = VL53L0_FIXPOINT97TOFIXPOINT1616(tmpWord); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, 0xFF, 0x00); + + PALDevDataSet(Dev, LastSignalRefMcps, LastSignalRefMcps); + + /* + * Check if Sigma limit is enabled, if yes then do comparison with limit + * value and put the result back into pPalRangeStatus. + */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + &SigmaLimitCheckEnable); + + if ((SigmaLimitCheckEnable != 0) && (Status == VL53L0_ERROR_NONE)) { + /* + * compute the Sigma and check with limit + */ + Status = VL53L0_calc_sigma_estimate( + Dev, + pRangingMeasurementData, + &SigmaEstimate, + &Dmax_mm); + if (Status == VL53L0_ERROR_NONE) + pRangingMeasurementData->RangeDMaxMilliMeter = Dmax_mm; + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetLimitCheckValue(Dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + &SigmaLimitValue); + + if ((SigmaLimitValue > 0) && + (SigmaEstimate > SigmaLimitValue)) + /* Limit Fail */ + SigmaLimitflag = 1; + } + } + + /* + * Check if Signal ref clip limit is enabled, if yes then do comparison + * with limit value and put the result back into pPalRangeStatus. + */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, + &SignalRefClipLimitCheckEnable); + + if ((SignalRefClipLimitCheckEnable != 0) && + (Status == VL53L0_ERROR_NONE)) { + + Status = VL53L0_GetLimitCheckValue(Dev, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, + &SignalRefClipValue); + + if ((SignalRefClipValue > 0) && + (LastSignalRefMcps > SignalRefClipValue)) { + /* Limit Fail */ + SignalRefClipflag = 1; + } + } + + /* + * Check if Signal ref clip limit is enabled, if yes then do comparison + * with limit value and put the result back into pPalRangeStatus. + * EffectiveSpadRtnCount has a format 8.8 + * If (Return signal rate < (1.5 x Xtalk x number of Spads)) : FAIL + */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + &RangeIgnoreThresholdLimitCheckEnable); + + if ((RangeIgnoreThresholdLimitCheckEnable != 0) && + (Status == VL53L0_ERROR_NONE)) { + + /* Compute the signal rate per spad */ + if (EffectiveSpadRtnCount == 0) { + SignalRatePerSpad = 0; + } else { + SignalRatePerSpad = (FixPoint1616_t)((256 * SignalRate) + / EffectiveSpadRtnCount); + } + + Status = VL53L0_GetLimitCheckValue(Dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + &RangeIgnoreThresholdValue); + + if ((RangeIgnoreThresholdValue > 0) && + (SignalRatePerSpad < RangeIgnoreThresholdValue)) { + /* Limit Fail add 2^6 to range status */ + RangeIgnoreThresholdflag = 1; + } + } + + if (Status == VL53L0_ERROR_NONE) { + if (NoneFlag == 1) { + *pPalRangeStatus = 255; /* NONE */ + } else if (DeviceRangeStatusInternal == 1 || + DeviceRangeStatusInternal == 2 || + DeviceRangeStatusInternal == 3) { + *pPalRangeStatus = 5; /* HW fail */ + } else if (DeviceRangeStatusInternal == 6 || + DeviceRangeStatusInternal == 9) { + *pPalRangeStatus = 4; /* Phase fail */ + } else if (DeviceRangeStatusInternal == 8 || + DeviceRangeStatusInternal == 10 || + SignalRefClipflag == 1) { + *pPalRangeStatus = 3; /* Min range */ + } else if (DeviceRangeStatusInternal == 4 || + RangeIgnoreThresholdflag == 1) { + *pPalRangeStatus = 2; /* Signal Fail */ + } else if (SigmaLimitflag == 1) { + *pPalRangeStatus = 1; /* Sigma Fail */ + } else { + *pPalRangeStatus = 0; /* Range Valid */ + } + } + + /* DMAX only relevant during range error */ + if (*pPalRangeStatus == 0) + pRangingMeasurementData->RangeDMaxMilliMeter = 0; + + /* fill the Limit Check Status */ + + Status = VL53L0_GetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &SignalRateFinalRangeLimitCheckEnable); + + if (Status == VL53L0_ERROR_NONE) { + if ((SigmaLimitCheckEnable == 0) || (SigmaLimitflag == 1)) + Temp8 = 1; + else + Temp8 = 0; + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, Temp8); + + if ((DeviceRangeStatusInternal == 4) || + (SignalRateFinalRangeLimitCheckEnable == 0)) + Temp8 = 1; + else + Temp8 = 0; + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + Temp8); + + if ((SignalRefClipLimitCheckEnable == 0) || + (SignalRefClipflag == 1)) + Temp8 = 1; + else + Temp8 = 0; + + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, Temp8); + + if ((RangeIgnoreThresholdLimitCheckEnable == 0) || + (RangeIgnoreThresholdflag == 1)) + Temp8 = 1; + else + Temp8 = 0; + + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + Temp8); + } + + LOG_FUNCTION_END(Status); + return Status; + +} diff --git a/drivers/input/misc/vl53L0/src/vl53l0_api_histogram.c b/drivers/input/misc/vl53L0/src/vl53l0_api_histogram.c new file mode 100644 index 000000000000..21b0410dd6ae --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_api_histogram.c @@ -0,0 +1,750 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include "vl53l0_api.h" +#include "vl53l0_api_core.h" +#include "vl53l0_api_histogram.h" + + +#ifndef __KERNEL__ +#include <stdlib.h> +#endif +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__) + + +typedef uint32_t WindowSelection; +#define VL53L0_AMBIENT_WINDOW_ONLY ((WindowSelection) 0) + /*!< Measure Ambient Signal only */ +#define VL53L0_AMBIENT_AND_SIGNAL_WINDOW ((WindowSelection) 1) + /*!< Measure Combined Ambient and Signal Rate. */ + + +VL53L0_Error VL53L0_start_histogram_measurement(VL53L0_DEV Dev, + VL53L0_HistogramModes histoMode, + uint32_t count) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t dataByte; + + LOG_FUNCTION_START(""); + + + dataByte = VL53L0_REG_SYSRANGE_MODE_SINGLESHOT | + VL53L0_REG_SYSRANGE_MODE_START_STOP; + + /* First histogram measurement must have bit 5 set */ + if (count == 0) + dataByte |= (1 << 5); + + switch (histoMode) { + case VL53L0_HISTOGRAMMODE_DISABLED: + /* Selected mode not supported */ + Status = VL53L0_ERROR_INVALID_COMMAND; + break; + + case VL53L0_HISTOGRAMMODE_REFERENCE_ONLY: + case VL53L0_HISTOGRAMMODE_RETURN_ONLY: + case VL53L0_HISTOGRAMMODE_BOTH: + dataByte |= (histoMode << 3); + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSRANGE_START, + dataByte); + if (Status == VL53L0_ERROR_NONE) { + /* Set PAL State to Running */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_RUNNING); + } + break; + + default: + /* Selected mode not supported */ + Status = VL53L0_ERROR_MODE_NOT_SUPPORTED; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_confirm_measurement_start(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t NewDataReady = 0; + uint32_t LoopNb; + + LOG_FUNCTION_START(""); + + LoopNb = 0; + do { + Status = VL53L0_GetMeasurementDataReady(Dev, &NewDataReady); + if ((NewDataReady == 0x01) || Status != VL53L0_ERROR_NONE) + break; + + LoopNb = LoopNb + 1; + VL53L0_PollingDelay(Dev); + } while (LoopNb < VL53L0_DEFAULT_MAX_LOOP); + + if (LoopNb >= VL53L0_DEFAULT_MAX_LOOP) + Status = VL53L0_ERROR_TIME_OUT; + + LOG_FUNCTION_END(Status); + + return Status; +} + + +VL53L0_Error VL53L0_set_histogram_mode(VL53L0_DEV Dev, + VL53L0_HistogramModes HistogramMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + switch (HistogramMode) { + case VL53L0_HISTOGRAMMODE_DISABLED: + case VL53L0_HISTOGRAMMODE_REFERENCE_ONLY: + case VL53L0_HISTOGRAMMODE_RETURN_ONLY: + case VL53L0_HISTOGRAMMODE_BOTH: + /* Supported mode */ + VL53L0_SETPARAMETERFIELD(Dev, HistogramMode, HistogramMode); + break; + default: + /* Unsupported mode */ + Status = VL53L0_ERROR_MODE_NOT_SUPPORTED; + } + + return Status; +} + +VL53L0_Error VL53L0_get_histogram_mode(VL53L0_DEV Dev, + VL53L0_HistogramModes *pHistogramMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + VL53L0_GETPARAMETERFIELD(Dev, HistogramMode, *pHistogramMode); + + return Status; +} + + +VL53L0_Error VL53L0_perform_single_histogram_measurement(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceModes DeviceMode; + VL53L0_HistogramModes HistogramMode = VL53L0_HISTOGRAMMODE_DISABLED; + uint32_t MeasCount; + uint32_t Measurements; + + /* Get Current DeviceMode */ + Status = VL53L0_GetHistogramMode(Dev, &HistogramMode); + + + if (Status != VL53L0_ERROR_NONE) + return Status; + + + if (HistogramMode == VL53L0_HISTOGRAMMODE_BOTH) { + if (pHistogramMeasurementData->BufferSize < + VL53L0_HISTOGRAM_BUFFER_SIZE) { + Status = VL53L0_ERROR_BUFFER_TOO_SMALL; + } + } else { + if (pHistogramMeasurementData->BufferSize < + VL53L0_HISTOGRAM_BUFFER_SIZE/2) { + Status = VL53L0_ERROR_BUFFER_TOO_SMALL; + } + } + pHistogramMeasurementData->HistogramType = (uint8_t)HistogramMode; + pHistogramMeasurementData->ErrorStatus = VL53L0_DEVICEERROR_NONE; + pHistogramMeasurementData->FirstBin = 0; + pHistogramMeasurementData->NumberOfBins = 0; + + + /* Get Current DeviceMode */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetDeviceMode(Dev, &DeviceMode); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_HISTOGRAM_BIN, + 0x00); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT, 0x00); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_HISTOGRAM_CONFIG_READOUT_CTRL, 0x01); + + if (Status != VL53L0_ERROR_NONE) + return Status; + + Measurements = 3; + if (HistogramMode == VL53L0_HISTOGRAMMODE_BOTH) + Measurements = 6; + + if (DeviceMode != VL53L0_DEVICEMODE_SINGLE_HISTOGRAM) { + Status = VL53L0_ERROR_INVALID_COMMAND; + return Status; + } + + /* DeviceMode == VL53L0_DEVICEMODE_SINGLE_HISTOGRAM */ + MeasCount = 0; + while ((MeasCount < Measurements) && (Status == VL53L0_ERROR_NONE)) { + Status = VL53L0_start_histogram_measurement(Dev, HistogramMode, + MeasCount); + + if (Status == VL53L0_ERROR_NONE) + VL53L0_confirm_measurement_start(Dev); + + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, PalState, VL53L0_STATE_RUNNING); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_measurement_poll_for_completion(Dev); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_read_histo_measurement(Dev, + pHistogramMeasurementData->HistogramData, + MeasCount, + HistogramMode); + + if (Status == VL53L0_ERROR_NONE) { + /* + * When reading both rtn and ref arrays, + * histograms are read two bins at a time. + * For rtn or ref only, histograms are read four + * bins at a time. + */ + if (HistogramMode == VL53L0_HISTOGRAMMODE_BOTH) + pHistogramMeasurementData->NumberOfBins + += 2; + else + pHistogramMeasurementData->NumberOfBins + += 4; + + } + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_ClearInterruptMask(Dev, 0); + + MeasCount++; + } + + /* Change PAL State in case of single ranging or single histogram */ + if (Status == VL53L0_ERROR_NONE) { + pHistogramMeasurementData->NumberOfBins = 12; + PALDevDataSet(Dev, PalState, VL53L0_STATE_IDLE); + } + + return Status; +} + + +VL53L0_Error VL53L0_get_histogram_measurement_data(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_read_histo_measurement(VL53L0_DEV Dev, + uint32_t *histoData, + uint32_t offset, + VL53L0_HistogramModes histoMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t localBuffer[28]; + uint32_t cDataSize = 4; + uint32_t offset1; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, 0xFF, VL53L0_REG_RESULT_CORE_PAGE); + Status = VL53L0_ReadMulti(Dev, + (uint8_t)VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN, + localBuffer, + 28); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_reverse_bytes(&localBuffer[0], cDataSize); + VL53L0_reverse_bytes(&localBuffer[4], cDataSize); + VL53L0_reverse_bytes(&localBuffer[20], cDataSize); + VL53L0_reverse_bytes(&localBuffer[24], cDataSize); + + offset1 = offset * cDataSize; + if (histoMode == VL53L0_HISTOGRAMMODE_BOTH) { + /* + * When reading both return and ref data, each + * measurement reads two ref values and two return + * values. Data is stored in an interleaved sequence, + * starting with the return histogram. + * + * Some result Core registers are reused for the + * histogram measurements + * + * The bin values are retrieved in the following order + * VL53L0_REG_RESULT_CORE_RANGING_TOTAL_EVENTS_RTN + * VL53L0_REG_RESULT_CORE_RANGING_TOTAL_EVENTS_REF + * VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN + * VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF + */ + + memcpy(&histoData[offset1], &localBuffer[4], + cDataSize); /* rtn */ + memcpy(&histoData[offset1 + 1], &localBuffer[24], + cDataSize); /* ref */ + memcpy(&histoData[offset1 + 2], &localBuffer[0], + cDataSize); /* rtn */ + memcpy(&histoData[offset1 + 3], &localBuffer[20], + cDataSize); /* ref */ + + } else { + /* + * When reading either return and ref data, each + * measurement reads four bin values. + * + * The bin values are retrieved in the following order + * VL53L0_REG_RESULT_CORE_RANGING_TOTAL_EVENTS_RTN + * VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN + * VL53L0_REG_RESULT_CORE_RANGING_TOTAL_EVENTS_REF + * VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF + */ + + memcpy(&histoData[offset1], &localBuffer[24], + cDataSize); + memcpy(&histoData[offset1 + 1], &localBuffer[20], + cDataSize); + memcpy(&histoData[offset1 + 2], &localBuffer[4], + cDataSize); + memcpy(&histoData[offset1 + 3], &localBuffer[0], + cDataSize); + + } + } + + LOG_FUNCTION_END(Status); + + return Status; +} + + +VL53L0_Error VL53L0_get_max_spads(VL53L0_DEV Dev, + uint32_t *pmax_spads, uint8_t *pambient_too_high) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t TCC_Enabled; + uint8_t MSRC_Enabled; + VL53L0_RangingMeasurementData_t RangingMeasurementData; + FixPoint1616_t ratio = 0; + uint32_t max_spads = 0; + + /* Get the value of the TCC */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_TCC, &TCC_Enabled); + + /* Get the value of the MSRC */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_MSRC, &MSRC_Enabled); + + /* Disable the TCC */ + if ((Status == VL53L0_ERROR_NONE) && (TCC_Enabled != 0)) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_TCC, 0); + + /* Disable the MSRC */ + if ((Status == VL53L0_ERROR_NONE) && (MSRC_Enabled != 0)) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_MSRC, 0); + + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_PerformSingleRangingMeasurement(Dev, + &RangingMeasurementData); + max_spads = (RangingMeasurementData.EffectiveSpadRtnCount + + 128)/256; + *pmax_spads = max_spads; + } + + /* Check Ambient per spad > 10 Kcps */ + if (Status == VL53L0_ERROR_NONE) { + if (max_spads <= 0) { + *pambient_too_high = 1; + Status = VL53L0_ERROR_DIVISION_BY_ZERO; + } else { + ratio = RangingMeasurementData.AmbientRateRtnMegaCps / + max_spads; + + /* ratio is given in mega count per second and + * FixPoint1616_t + */ + if (ratio > 65536/100) + *pambient_too_high = 1; + else + *pambient_too_high = 0; + + } + } + + + /* Restore the TCC */ + if (Status == VL53L0_ERROR_NONE) { + if (TCC_Enabled != 0) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_TCC, 1); + } + + /* Restore the MSRC */ + if (Status == VL53L0_ERROR_NONE) { + if (MSRC_Enabled != 0) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_MSRC, 1); + } + + return Status; + +} + + +VL53L0_Error calc_xtalk_mcps_per_spad( + uint32_t rtn_signal_events, + uint32_t timeout_ms, + uint32_t max_spads, + uint8_t vcsel_pulse_period_pclk, + FixPoint1616_t *pxtalk_mcps_per_spad) +{ + /* Calculate the X-Talk per Spad based on given inputs. + * + * To calculate x-talk, only a portion of the vcsel pulse period is + * used, therefore we use the ratio between vcsel_width_pclk and + * vcsel_pulse_period_pclks to determine the integration time. + * + * With the rtn signal events, and the integration time, + * the x-talk rate per spad is then determined. + */ + + const FixPoint1616_t cmin_xtalk_per_spad = 8; /* 0.000122 */ + const FixPoint1616_t ccompensation2 = 13;/* 0.0002 */ + const FixPoint1616_t ccompensation1 = 7; /* 0.0001; */ + const FixPoint1616_t ctalk_thresh = 66; /* 0.001 */ + const uint32_t c16BitRoundingParam = 0x00008000; + VL53L0_Error status = VL53L0_ERROR_NONE; + FixPoint1616_t xtalk_mcps; + FixPoint1616_t vcsel_width_to_period_ratio; + FixPoint1616_t integration_time_us; + uint32_t integration_time_us_int; + uint8_t vcsel_width_pclk = 3; + + LOG_FUNCTION_START(""); + + if (vcsel_pulse_period_pclk == 0 || timeout_ms == 0) + status = VL53L0_ERROR_DIVISION_BY_ZERO; + + + if (status == VL53L0_ERROR_NONE) { + + /* (FixPoint1616 + uint32)/uint32 = FixPoint1616 */ + vcsel_width_to_period_ratio = + ((vcsel_width_pclk << 16) + + (vcsel_pulse_period_pclk/2))/vcsel_pulse_period_pclk; + + /* uint32_t * FixPoint1616 = FixPoint1616 */ + integration_time_us = timeout_ms * vcsel_width_to_period_ratio + * 1000; + + /*FixPoint1616 >>16 = uint32_t */ + integration_time_us_int = (integration_time_us + + c16BitRoundingParam) >> 16; + + /* (uint32_t << 16)/uint32_t = FixPoint1616 */ + xtalk_mcps = rtn_signal_events << 16; + xtalk_mcps = (xtalk_mcps + + (integration_time_us_int/2))/integration_time_us_int; + + /* (FixPoint1616 + uint32)/uint32 = FixPoint1616 */ + *pxtalk_mcps_per_spad = (xtalk_mcps + (max_spads/2))/max_spads; + + /* Apply compensation to prevent overshoot. + */ + if (*pxtalk_mcps_per_spad < ctalk_thresh) + *pxtalk_mcps_per_spad = *pxtalk_mcps_per_spad + - ccompensation2; + else + *pxtalk_mcps_per_spad = *pxtalk_mcps_per_spad + - ccompensation1; + + if (*pxtalk_mcps_per_spad < cmin_xtalk_per_spad) + *pxtalk_mcps_per_spad = cmin_xtalk_per_spad; + + } + LOG_FUNCTION_END(""); + + return status; +} + + +uint32_t bytes_to_int(uint8_t *data_bytes) +{ + /* Convert an array of 4 bytes to an integer. + */ + uint32_t data = (uint32_t)data_bytes[0] << 24; + + data += ((uint32_t)data_bytes[1] << 16); + data += ((uint32_t)data_bytes[2] << 8); + data += ((uint32_t)data_bytes[3]); + return data; +} + +VL53L0_Error perform_histo_signal_meas(VL53L0_DEV dev, + WindowSelection window_select, + uint32_t *psignal_events) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + uint8_t data[8]; + uint8_t readout_ctrl_val; + uint32_t bin_width = 3; + + LOG_FUNCTION_START(""); + + /* Select Ambient or Total Signal Measurement + */ + if (status == VL53L0_ERROR_NONE) { + readout_ctrl_val = bin_width; + if (window_select == VL53L0_AMBIENT_WINDOW_ONLY) + readout_ctrl_val += 0x80; + + status = VL53L0_WrByte( + dev, VL53L0_REG_HISTOGRAM_CONFIG_READOUT_CTRL, + readout_ctrl_val); + } + + /* Perform Measurement. + */ + if (status == VL53L0_ERROR_NONE) + status = VL53L0_start_histogram_measurement( + dev, VL53L0_HISTOGRAMMODE_RETURN_ONLY, 0); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_measurement_poll_for_completion(dev); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_ClearInterruptMask(dev, 0); + + /* Read Measurement Data. + */ + if (status == VL53L0_ERROR_NONE) + status = VL53L0_WrByte(dev, 0xFF, VL53L0_REG_RESULT_CORE_PAGE); + + + if (status == VL53L0_ERROR_NONE) { + status = VL53L0_ReadMulti(dev, + (uint8_t)VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN, + data, + 8); + } + + if (status == VL53L0_ERROR_NONE) + status |= VL53L0_WrByte(dev, 0xFF, 0x00); + + + /* Take the sum of the Ambient and Signal Window Event readings. + */ + if (status == VL53L0_ERROR_NONE) + *psignal_events = bytes_to_int(data) + + bytes_to_int(&(data[4])); + + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L0_Error set_final_range_timeout_us( + VL53L0_DEV dev, uint32_t timeout_microSecs, + uint16_t final_range_vcsel_period_pclks) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + uint16_t final_range_timeout_mclks; + uint16_t final_range_encoded_timeOut; + + LOG_FUNCTION_START(""); + + /* Calculate FINAL RANGE Timeout in Macro Periods (MCLKS) + */ + + /* convert timeout to mclks. */ + final_range_timeout_mclks = VL53L0_calc_timeout_mclks(dev, + timeout_microSecs, (uint8_t) final_range_vcsel_period_pclks); + + /* Encode timeout */ + final_range_encoded_timeOut = VL53L0_encode_timeout( + final_range_timeout_mclks); + + /* Write to device */ + status = VL53L0_WrWord(dev, + VL53L0_REG_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI, + final_range_encoded_timeOut); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L0_Error perform_histogram_config(VL53L0_DEV dev, + uint32_t timeout_ms, uint16_t final_range_vcsel_period_pclks) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + uint8_t phaseSelect = 1; + + LOG_FUNCTION_START(""); + + if (status == VL53L0_ERROR_NONE) + status = set_final_range_timeout_us( + dev, timeout_ms * 1000, final_range_vcsel_period_pclks); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_SetDeviceMode(dev, + VL53L0_DEVICEMODE_SINGLE_HISTOGRAM); + + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_SetHistogramMode(dev, + VL53L0_HISTOGRAMMODE_BOTH); + + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_WrByte(dev, VL53L0_REG_SYSTEM_HISTOGRAM_BIN, + 0x00); + + + /* Apply specific phase select for x-talk measurement */ + if (status == VL53L0_ERROR_NONE) + status = VL53L0_WrByte(dev, + VL53L0_REG_HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT, + phaseSelect); + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L0_Error VL53L0_perform_xtalk_measurement(VL53L0_DEV dev, + uint32_t timeout_ms, FixPoint1616_t *pxtalk_per_spad, + uint8_t *pambient_too_high) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + uint32_t signal_events = 0; + uint32_t amb_events = 0; + uint32_t meas_timing_budget_us; + VL53L0_DeviceModes device_mode; + uint8_t final_range_vcsel_period_pclks; + uint32_t max_spads; + + /* Get Current DeviceMode */ + status = VL53L0_GetDeviceMode(dev, &device_mode); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_get_max_spads(dev, &max_spads, + pambient_too_high); + + if (status != VL53L0_ERROR_NONE) + return status; + + + if (status == VL53L0_ERROR_NONE) { + status = VL53L0_GetVcselPulsePeriod( + dev, + VL53L0_VCSEL_PERIOD_FINAL_RANGE, + &final_range_vcsel_period_pclks); + } + + if (status == VL53L0_ERROR_NONE) { + if (final_range_vcsel_period_pclks < 10) + status = VL53L0_ERROR_INVALID_PARAMS; + } + + if (status == VL53L0_ERROR_NONE) { + perform_histogram_config( + dev, timeout_ms, final_range_vcsel_period_pclks); + } + + if (status == VL53L0_ERROR_NONE) { + status = perform_histo_signal_meas( + dev, + VL53L0_AMBIENT_WINDOW_ONLY, + &amb_events); + } + + if (status == VL53L0_ERROR_NONE) { + status = perform_histo_signal_meas( + dev, + VL53L0_AMBIENT_AND_SIGNAL_WINDOW, + &signal_events); + } + + if (status == VL53L0_ERROR_NONE) { + status = calc_xtalk_mcps_per_spad( + (signal_events - amb_events), + timeout_ms, + max_spads, + final_range_vcsel_period_pclks, + pxtalk_per_spad); + } + + /* Revert previous device mode. */ + if (status == VL53L0_ERROR_NONE) + status = VL53L0_SetDeviceMode(dev, device_mode); + + /* Revert previous timing budget, to ensure previous final range vcsel + * period is applied. + */ + if (status == VL53L0_ERROR_NONE) { + VL53L0_GETPARAMETERFIELD( + dev, + MeasurementTimingBudgetMicroSeconds, + meas_timing_budget_us); + + status = VL53L0_SetMeasurementTimingBudgetMicroSeconds( + dev, meas_timing_budget_us); + } + + return status; +} + diff --git a/drivers/input/misc/vl53L0/src/vl53l0_api_ranging.c b/drivers/input/misc/vl53L0/src/vl53l0_api_ranging.c new file mode 100644 index 000000000000..f575aec7c5ed --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_api_ranging.c @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include "vl53l0_api.h" +#include "vl53l0_api_core.h" + + +#ifndef __KERNEL__ +#include <stdlib.h> +#endif +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__) + diff --git a/drivers/input/misc/vl53L0/src/vl53l0_api_strings.c b/drivers/input/misc/vl53L0/src/vl53l0_api_strings.c new file mode 100644 index 000000000000..a5f2bbadd290 --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_api_strings.c @@ -0,0 +1,463 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include "vl53l0_api.h" +#include "vl53l0_api_core.h" +#include "vl53l0_api_strings.h" + +#ifndef __KERNEL__ +#include <stdlib.h> +#endif + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__) + + +VL53L0_Error VL53L0_check_part_used(VL53L0_DEV Dev, + uint8_t *Revision, + VL53L0_DeviceInfo_t *pVL53L0_DeviceInfo) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t ModuleIdInt; + char *ProductId_tmp; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_info_from_device(Dev, 2); + + if (Status == VL53L0_ERROR_NONE) { + ModuleIdInt = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, ModuleId); + + if (ModuleIdInt == 0) { + *Revision = 0; + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->ProductId, ""); + } else { + *Revision = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, Revision); + ProductId_tmp = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + ProductId); + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->ProductId, ProductId_tmp); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L0_Error VL53L0_get_device_info(VL53L0_DEV Dev, + VL53L0_DeviceInfo_t *pVL53L0_DeviceInfo) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t revision_id; + uint8_t Revision; + + Status = VL53L0_check_part_used(Dev, &Revision, pVL53L0_DeviceInfo); + + if (Status == VL53L0_ERROR_NONE) { + if (Revision == 0) { + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->Name, + VL53L0_STRING_DEVICE_INFO_NAME_TS0); + } else if ((Revision <= 34) && (Revision != 32)) { + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->Name, + VL53L0_STRING_DEVICE_INFO_NAME_TS1); + } else if (Revision < 39) { + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->Name, + VL53L0_STRING_DEVICE_INFO_NAME_TS2); + } else { + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->Name, + VL53L0_STRING_DEVICE_INFO_NAME_ES1); + } + + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->Type, + VL53L0_STRING_DEVICE_INFO_TYPE); + + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdByte(Dev, VL53L0_REG_IDENTIFICATION_MODEL_ID, + &pVL53L0_DeviceInfo->ProductType); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdByte(Dev, + VL53L0_REG_IDENTIFICATION_REVISION_ID, + &revision_id); + pVL53L0_DeviceInfo->ProductRevisionMajor = 1; + pVL53L0_DeviceInfo->ProductRevisionMinor = + (revision_id & 0xF0) >> 4; + } + + return Status; +} + + +VL53L0_Error VL53L0_get_device_error_string(VL53L0_DeviceError ErrorCode, + char *pDeviceErrorString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (ErrorCode) { + case VL53L0_DEVICEERROR_NONE: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_NONE); + break; + case VL53L0_DEVICEERROR_VCSELCONTINUITYTESTFAILURE: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_VCSELCONTINUITYTESTFAILURE); + break; + case VL53L0_DEVICEERROR_VCSELWATCHDOGTESTFAILURE: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_VCSELWATCHDOGTESTFAILURE); + break; + case VL53L0_DEVICEERROR_NOVHVVALUEFOUND: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_NOVHVVALUEFOUND); + break; + case VL53L0_DEVICEERROR_MSRCNOTARGET: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_MSRCNOTARGET); + break; + case VL53L0_DEVICEERROR_SNRCHECK: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_SNRCHECK); + break; + case VL53L0_DEVICEERROR_RANGEPHASECHECK: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_RANGEPHASECHECK); + break; + case VL53L0_DEVICEERROR_SIGMATHRESHOLDCHECK: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_SIGMATHRESHOLDCHECK); + break; + case VL53L0_DEVICEERROR_TCC: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_TCC); + break; + case VL53L0_DEVICEERROR_PHASECONSISTENCY: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_PHASECONSISTENCY); + break; + case VL53L0_DEVICEERROR_MINCLIP: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_MINCLIP); + break; + case VL53L0_DEVICEERROR_RANGECOMPLETE: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_RANGECOMPLETE); + break; + case VL53L0_DEVICEERROR_ALGOUNDERFLOW: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_ALGOUNDERFLOW); + break; + case VL53L0_DEVICEERROR_ALGOOVERFLOW: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_ALGOOVERFLOW); + break; + case VL53L0_DEVICEERROR_RANGEIGNORETHRESHOLD: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_RANGEIGNORETHRESHOLD); + break; + + default: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_UNKNOW_ERROR_CODE); + + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_get_range_status_string(uint8_t RangeStatus, + char *pRangeStatusString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (RangeStatus) { + case 0: + VL53L0_COPYSTRING(pRangeStatusString, + VL53L0_STRING_RANGESTATUS_RANGEVALID); + break; + case 1: + VL53L0_COPYSTRING(pRangeStatusString, + VL53L0_STRING_RANGESTATUS_SIGMA); + break; + case 2: + VL53L0_COPYSTRING(pRangeStatusString, + VL53L0_STRING_RANGESTATUS_SIGNAL); + break; + case 3: + VL53L0_COPYSTRING(pRangeStatusString, + VL53L0_STRING_RANGESTATUS_MINRANGE); + break; + case 4: + VL53L0_COPYSTRING(pRangeStatusString, + VL53L0_STRING_RANGESTATUS_PHASE); + break; + case 5: + VL53L0_COPYSTRING(pRangeStatusString, + VL53L0_STRING_RANGESTATUS_HW); + break; + + default: /**/ + VL53L0_COPYSTRING(pRangeStatusString, + VL53L0_STRING_RANGESTATUS_NONE); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_get_pal_error_string(VL53L0_Error PalErrorCode, + char *pPalErrorString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (PalErrorCode) { + case VL53L0_ERROR_NONE: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_NONE); + break; + case VL53L0_ERROR_CALIBRATION_WARNING: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_CALIBRATION_WARNING); + break; + case VL53L0_ERROR_MIN_CLIPPED: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_MIN_CLIPPED); + break; + case VL53L0_ERROR_UNDEFINED: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_UNDEFINED); + break; + case VL53L0_ERROR_INVALID_PARAMS: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_INVALID_PARAMS); + break; + case VL53L0_ERROR_NOT_SUPPORTED: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_NOT_SUPPORTED); + break; + case VL53L0_ERROR_INTERRUPT_NOT_CLEARED: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_INTERRUPT_NOT_CLEARED); + break; + case VL53L0_ERROR_RANGE_ERROR: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_RANGE_ERROR); + break; + case VL53L0_ERROR_TIME_OUT: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_TIME_OUT); + break; + case VL53L0_ERROR_MODE_NOT_SUPPORTED: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_MODE_NOT_SUPPORTED); + break; + case VL53L0_ERROR_BUFFER_TOO_SMALL: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_BUFFER_TOO_SMALL); + break; + case VL53L0_ERROR_GPIO_NOT_EXISTING: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_GPIO_NOT_EXISTING); + break; + case VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED); + break; + case VL53L0_ERROR_CONTROL_INTERFACE: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_CONTROL_INTERFACE); + break; + case VL53L0_ERROR_INVALID_COMMAND: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_INVALID_COMMAND); + break; + case VL53L0_ERROR_DIVISION_BY_ZERO: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_DIVISION_BY_ZERO); + break; + case VL53L0_ERROR_REF_SPAD_INIT: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_REF_SPAD_INIT); + break; + case VL53L0_ERROR_NOT_IMPLEMENTED: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_NOT_IMPLEMENTED); + break; + + default: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_UNKNOW_ERROR_CODE); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_get_pal_state_string(VL53L0_State PalStateCode, + char *pPalStateString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (PalStateCode) { + case VL53L0_STATE_POWERDOWN: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_POWERDOWN); + break; + case VL53L0_STATE_WAIT_STATICINIT: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_WAIT_STATICINIT); + break; + case VL53L0_STATE_STANDBY: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_STANDBY); + break; + case VL53L0_STATE_IDLE: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_IDLE); + break; + case VL53L0_STATE_RUNNING: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_RUNNING); + break; + case VL53L0_STATE_UNKNOWN: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_UNKNOWN); + break; + case VL53L0_STATE_ERROR: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_ERROR); + break; + + default: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_UNKNOWN); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_get_sequence_steps_info( + VL53L0_SequenceStepId SequenceStepId, + char *pSequenceStepsString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (SequenceStepId) { + case VL53L0_SEQUENCESTEP_TCC: + VL53L0_COPYSTRING(pSequenceStepsString, + VL53L0_STRING_SEQUENCESTEP_TCC); + break; + case VL53L0_SEQUENCESTEP_DSS: + VL53L0_COPYSTRING(pSequenceStepsString, + VL53L0_STRING_SEQUENCESTEP_DSS); + break; + case VL53L0_SEQUENCESTEP_MSRC: + VL53L0_COPYSTRING(pSequenceStepsString, + VL53L0_STRING_SEQUENCESTEP_MSRC); + break; + case VL53L0_SEQUENCESTEP_PRE_RANGE: + VL53L0_COPYSTRING(pSequenceStepsString, + VL53L0_STRING_SEQUENCESTEP_PRE_RANGE); + break; + case VL53L0_SEQUENCESTEP_FINAL_RANGE: + VL53L0_COPYSTRING(pSequenceStepsString, + VL53L0_STRING_SEQUENCESTEP_FINAL_RANGE); + break; + + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + } + + LOG_FUNCTION_END(Status); + + return Status; +} + + +VL53L0_Error VL53L0_get_limit_check_info(VL53L0_DEV Dev, uint16_t LimitCheckId, + char *pLimitCheckString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (LimitCheckId) { + case VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE: + VL53L0_COPYSTRING(pLimitCheckString, + VL53L0_STRING_CHECKENABLE_SIGMA_FINAL_RANGE); + break; + case VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + VL53L0_COPYSTRING(pLimitCheckString, + VL53L0_STRING_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE); + break; + case VL53L0_CHECKENABLE_SIGNAL_REF_CLIP: + VL53L0_COPYSTRING(pLimitCheckString, + VL53L0_STRING_CHECKENABLE_SIGNAL_REF_CLIP); + break; + case VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD: + VL53L0_COPYSTRING(pLimitCheckString, + VL53L0_STRING_CHECKENABLE_RANGE_IGNORE_THRESHOLD); + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_MSRC: + VL53L0_COPYSTRING(pLimitCheckString, + VL53L0_STRING_CHECKENABLE_SIGNAL_RATE_MSRC); + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_PRE_RANGE: + VL53L0_COPYSTRING(pLimitCheckString, + VL53L0_STRING_CHECKENABLE_SIGNAL_RATE_PRE_RANGE); + break; + + default: + VL53L0_COPYSTRING(pLimitCheckString, + VL53L0_STRING_UNKNOW_ERROR_CODE); + + } + + LOG_FUNCTION_END(Status); + return Status; +} diff --git a/drivers/input/misc/vl53L0/src/vl53l0_i2c_platform.c b/drivers/input/misc/vl53L0/src/vl53l0_i2c_platform.c new file mode 100644 index 000000000000..e4097e1ccdd5 --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_i2c_platform.c @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2016 STMicroelectronics Imaging Division. + * + * 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. + */ + +/*! + * \file VL53L0_i2c_platform.c + * \brief Code function defintions for EWOK Platform Layer + * + */ + + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/delay.h> +#include "stmvl53l0-i2c.h" +#include "stmvl53l0-cci.h" + +#include "vl53l0_platform.h" +#include "vl53l0_i2c_platform.h" +#include "vl53l0_def.h" + +#include "vl53l0_platform_log.h" + +#ifdef VL53L0_LOG_ENABLE +#define trace_print(level, ...) \ + trace_print_module_function(TRACE_MODULE_PLATFORM, level,\ + TRACE_FUNCTION_NONE, ##__VA_ARGS__) +#define trace_i2c(...) \ + trace_print_module_function(TRACE_MODULE_NONE, \ + TRACE_LEVEL_NONE, TRACE_FUNCTION_I2C, ##__VA_ARGS__) +#endif + +/** + * @def I2C_BUFFER_CONFIG + * + * @brief Configure Device register I2C access + * + * @li 0 : one GLOBAL buffer \n + * Use one global buffer of MAX_I2C_XFER_SIZE byte in data space \n + * This solution is not multi-Device compliant nor multi-thread cpu safe \n + * It can be the best option for small 8/16 bit MCU without stack and limited + * ram (STM8s, 80C51 ...) + * + * @li 1 : ON_STACK/local \n + * Use local variable (on stack) buffer \n + * This solution is multi-thread with use of i2c resource lock or mutex see + * VL6180x_GetI2CAccess() \n + * + * @li 2 : User defined \n + * Per Device potentially dynamic allocated. Requires VL6180x_GetI2cBuffer() + * to be implemented. + * @ingroup Configuration + */ +#define I2C_BUFFER_CONFIG 1 + +#if I2C_BUFFER_CONFIG == 0 + /* GLOBAL config buffer */ + uint8_t i2c_global_buffer[VL53L0_MAX_I2C_XFER_SIZE]; + + #define DECL_I2C_BUFFER + #define VL53L0_GetLocalBuffer(Dev, n_byte) i2c_global_buffer + +#elif I2C_BUFFER_CONFIG == 1 + /* ON STACK */ + uint8_t LocBuffer[VL53L0_MAX_I2C_XFER_SIZE]; + #define VL53L0_GetLocalBuffer(Dev, n_byte) LocBuffer +#elif I2C_BUFFER_CONFIG == 2 + /* user define buffer type declare DECL_I2C_BUFFER as access via + * VL53L0_GetLocalBuffer + */ + #define DECL_I2C_BUFFER +#else +#error "invalid I2C_BUFFER_CONFIG " +#endif + + +/* none but could be for a flag var to + * get/pass to mutex interruptible return flags and try again + */ +#define VL53L0_I2C_USER_VAR +#define VL53L0_GetI2CAccess(Dev) /* todo mutex acquire */ +#define VL53L0_DoneI2CAcces(Dev) /* todo mutex release */ + + +char debug_string[VL53L0_MAX_STRING_LENGTH_PLT]; + + +#define MIN_COMMS_VERSION_MAJOR 1 +#define MIN_COMMS_VERSION_MINOR 8 +#define MIN_COMMS_VERSION_BUILD 1 +#define MIN_COMMS_VERSION_REVISION 0 + +#define STATUS_OK 0x00 +#define STATUS_FAIL 0x01 + +bool_t _check_min_version(void) +{ + bool_t min_version_comms_dll = false; + + min_version_comms_dll = true; + + return min_version_comms_dll; +} + +int32_t VL53L0_comms_initialise(uint8_t comms_type, uint16_t comms_speed_khz) +{ + int32_t status = STATUS_OK; + + return status; +} + +int32_t VL53L0_comms_close(void) +{ + int32_t status = STATUS_OK; + + + return status; +} + +int32_t VL53L0_set_page(VL53L0_DEV dev, uint8_t page_data) +{ + int32_t status = STATUS_OK; + uint16_t page_index = 0xFF; + uint8_t *buffer; + + buffer = VL53L0_GetLocalBuffer(dev, 3); + buffer[0] = page_index >> 8; + buffer[1] = page_index & 0xff; + buffer[2] = page_data; + + status = VL53L0_I2CWrite(dev, buffer, (uint8_t) 3); + return status; +} + +int32_t VL53L0_write_multi(VL53L0_DEV dev, uint8_t index, uint8_t *pdata, + int32_t count) +{ + int32_t status = STATUS_OK; + uint8_t *buffer; + +#ifdef VL53L0_LOG_ENABLE + int32_t i = 0; + char value_as_str[VL53L0_MAX_STRING_LENGTH_PLT]; + char *pvalue_as_str; + + pvalue_as_str = value_as_str; + + for (i = 0 ; i < count ; i++) { + snprintf(pvalue_as_str, sizeof(pvalue_as_str), + "%02X", *(pdata + i)); + + pvalue_as_str += 2; + } + trace_i2c("Write reg : 0x%04X, Val : 0x%s\n", index, value_as_str); +#endif + if ((count + 1) > VL53L0_MAX_I2C_XFER_SIZE) + return STATUS_FAIL; + buffer = VL53L0_GetLocalBuffer(dev, (count+1)); + buffer[0] = index; + memcpy(&buffer[1], pdata, count); + status = VL53L0_I2CWrite(dev, buffer, (count+1)); + + return status; +} + +int32_t VL53L0_read_multi(VL53L0_DEV dev, uint8_t index, uint8_t *pdata, + int32_t count) +{ + int32_t status = STATUS_OK; + uint8_t *buffer; + +#ifdef VL53L0_LOG_ENABLE + int32_t i = 0; + char value_as_str[VL53L0_MAX_STRING_LENGTH_PLT]; + char *pvalue_as_str; +#endif + + if ((count + 1) > VL53L0_MAX_I2C_XFER_SIZE) + return STATUS_FAIL; + + buffer = VL53L0_GetLocalBuffer(dev, 1); + buffer[0] = index; + status = VL53L0_I2CWrite(dev, (uint8_t *)buffer, (uint8_t)1); + if (!status) { + pdata[0] = index; + status = VL53L0_I2CRead(dev, pdata, count); + } + +#ifdef VL53L0_LOG_ENABLE + pvalue_as_str = value_as_str; + + for (i = 0 ; i < count ; i++) { + snprintf(pvalue_as_str, sizeof(value_as_str), + "%02X", *(pdata+i)); + pvalue_as_str += 2; + } + + trace_i2c("Read reg : 0x%04X, Val : 0x%s\n", index, value_as_str); +#endif + + return status; +} + + +int32_t VL53L0_write_byte(VL53L0_DEV dev, uint8_t index, uint8_t data) +{ + int32_t status = STATUS_OK; + const int32_t cbyte_count = 1; + + status = VL53L0_write_multi(dev, index, &data, cbyte_count); + + return status; + +} + + +int32_t VL53L0_write_word(VL53L0_DEV dev, uint8_t index, uint16_t data) +{ + int32_t status = STATUS_OK; + + uint8_t buffer[BYTES_PER_WORD]; + + /* Split 16-bit word into MS and LS uint8_t */ + buffer[0] = (uint8_t)(data >> 8); + buffer[1] = (uint8_t)(data & 0x00FF); + + status = VL53L0_write_multi(dev, index, buffer, BYTES_PER_WORD); + + return status; + +} + + +int32_t VL53L0_write_dword(VL53L0_DEV dev, uint8_t index, uint32_t data) +{ + int32_t status = STATUS_OK; + uint8_t buffer[BYTES_PER_DWORD]; + + /* Split 32-bit word into MS ... LS bytes */ + buffer[0] = (uint8_t) (data >> 24); + buffer[1] = (uint8_t)((data & 0x00FF0000) >> 16); + buffer[2] = (uint8_t)((data & 0x0000FF00) >> 8); + buffer[3] = (uint8_t) (data & 0x000000FF); + + status = VL53L0_write_multi(dev, index, buffer, BYTES_PER_DWORD); + + return status; + +} + + +int32_t VL53L0_read_byte(VL53L0_DEV dev, uint8_t index, uint8_t *pdata) +{ + int32_t status = STATUS_OK; + int32_t cbyte_count = 1; + + status = VL53L0_read_multi(dev, index, pdata, cbyte_count); + + return status; + +} + + +int32_t VL53L0_read_word(VL53L0_DEV dev, uint8_t index, uint16_t *pdata) +{ + int32_t status = STATUS_OK; + uint8_t buffer[BYTES_PER_WORD]; + + status = VL53L0_read_multi(dev, index, buffer, BYTES_PER_WORD); + *pdata = ((uint16_t)buffer[0]<<8) + (uint16_t)buffer[1]; + + return status; + +} + +int32_t VL53L0_read_dword(VL53L0_DEV dev, uint8_t index, uint32_t *pdata) +{ + int32_t status = STATUS_OK; + uint8_t buffer[BYTES_PER_DWORD]; + + status = VL53L0_read_multi(dev, index, buffer, BYTES_PER_DWORD); + *pdata = ((uint32_t)buffer[0]<<24) + ((uint32_t)buffer[1]<<16) + + ((uint32_t)buffer[2]<<8) + (uint32_t)buffer[3]; + + return status; + +} + +int32_t VL53L0_platform_wait_us(int32_t wait_us) +{ + int32_t status = STATUS_OK; + + msleep((wait_us/1000)); + +#ifdef VL53L0_LOG_ENABLE + trace_i2c("Wait us : %6d\n", wait_us); +#endif + + return status; + +} + + +int32_t VL53L0_wait_ms(int32_t wait_ms) +{ + int32_t status = STATUS_OK; + + msleep(wait_ms); + +#ifdef VL53L0_LOG_ENABLE + trace_i2c("Wait ms : %6d\n", wait_ms); +#endif + + return status; + +} + + +int32_t VL53L0_set_gpio(uint8_t level) +{ + int32_t status = STATUS_OK; +#ifdef VL53L0_LOG_ENABLE + trace_i2c("// Set GPIO = %d;\n", level); +#endif + + return status; + +} + + +int32_t VL53L0_get_gpio(uint8_t *plevel) +{ + int32_t status = STATUS_OK; +#ifdef VL53L0_LOG_ENABLE + trace_i2c("// Get GPIO = %d;\n", *plevel); +#endif + return status; +} + + +int32_t VL53L0_release_gpio(void) +{ + int32_t status = STATUS_OK; +#ifdef VL53L0_LOG_ENABLE + trace_i2c("// Releasing force on GPIO\n"); +#endif + return status; + +} + +int32_t VL53L0_cycle_power(void) +{ + int32_t status = STATUS_OK; +#ifdef VL53L0_LOG_ENABLE + trace_i2c("// cycle sensor power\n"); +#endif + + return status; +} + + +int32_t VL53L0_get_timer_frequency(int32_t *ptimer_freq_hz) +{ + *ptimer_freq_hz = 0; + return STATUS_FAIL; +} + + +int32_t VL53L0_get_timer_value(int32_t *ptimer_count) +{ + *ptimer_count = 0; + return STATUS_FAIL; +} diff --git a/drivers/input/misc/vl53L0/src/vl53l0_platform.c b/drivers/input/misc/vl53L0/src/vl53l0_platform.c new file mode 100644 index 000000000000..f7292ab6f8f2 --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_platform.c @@ -0,0 +1,242 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of STMicroelectronics nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +/** + * @file VL53L0_i2c.c + * + * Copyright (C) 2014 ST MicroElectronics + * + * provide variable word size byte/Word/dword VL6180x register access via i2c + * + */ +#include "vl53l0_platform.h" +#include "vl53l0_i2c_platform.h" +#include "vl53l0_api.h" + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_PLATFORM, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_PLATFORM, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...)\ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_PLATFORM, status,\ + fmt, ##__VA_ARGS__) + + + +VL53L0_Error VL53L0_LockSequenceAccess(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + return Status; +} + +VL53L0_Error VL53L0_UnlockSequenceAccess(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + return Status; +} + +/* the ranging_sensor_comms.dll will take care of the page selection */ +VL53L0_Error VL53L0_WriteMulti(VL53L0_DEV Dev, uint8_t index, + uint8_t *pdata, uint32_t count) +{ + + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int = 0; + uint8_t deviceAddress; + + if (count >= VL53L0_MAX_I2C_XFER_SIZE) + Status = VL53L0_ERROR_INVALID_PARAMS; + + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_write_multi(Dev, index, pdata, count); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + +/* the ranging_sensor_comms.dll will take care of the page selection */ +VL53L0_Error VL53L0_ReadMulti(VL53L0_DEV Dev, uint8_t index, + uint8_t *pdata, uint32_t count) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + + if (count >= VL53L0_MAX_I2C_XFER_SIZE) + Status = VL53L0_ERROR_INVALID_PARAMS; + + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_read_multi(Dev, index, pdata, count); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + + +VL53L0_Error VL53L0_WrByte(VL53L0_DEV Dev, uint8_t index, uint8_t data) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_write_byte(Dev, index, data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + +VL53L0_Error VL53L0_WrWord(VL53L0_DEV Dev, uint8_t index, uint16_t data) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_write_word(Dev, index, data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + +VL53L0_Error VL53L0_WrDWord(VL53L0_DEV Dev, uint8_t index, uint32_t data) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_write_dword(Dev, index, data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + +VL53L0_Error VL53L0_UpdateByte(VL53L0_DEV Dev, uint8_t index, + uint8_t AndData, uint8_t OrData) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + uint8_t data; + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_read_byte(Dev, index, &data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + if (Status == VL53L0_ERROR_NONE) { + data = (data & AndData) | OrData; + status_int = VL53L0_write_byte(Dev, index, data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + } + + return Status; +} + +VL53L0_Error VL53L0_RdByte(VL53L0_DEV Dev, uint8_t index, uint8_t *data) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_read_byte(Dev, index, data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + +VL53L0_Error VL53L0_RdWord(VL53L0_DEV Dev, uint8_t index, uint16_t *data) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_read_word(Dev, index, data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + +VL53L0_Error VL53L0_RdDWord(VL53L0_DEV Dev, uint8_t index, uint32_t *data) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_read_dword(Dev, index, data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + +#define VL53L0_POLLINGDELAY_LOOPNB 250 +VL53L0_Error VL53L0_PollingDelay(VL53L0_DEV Dev) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + usleep_range(950, 1000); + LOG_FUNCTION_END(status); + return status; +} diff --git a/drivers/input/misc/vl53L0/src/vl53l0_port_i2c.c b/drivers/input/misc/vl53L0/src/vl53l0_port_i2c.c new file mode 100644 index 000000000000..3dc085f60326 --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_port_i2c.c @@ -0,0 +1,155 @@ +/* + * vl53l0_port_i2c.c + * + * Created on: July, 2015 + * Author: Teresa Tao + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include "stmvl53l0-i2c.h" +#include "stmvl53l0-cci.h" +#include "vl53l0_platform.h" +#include "vl53l0_i2c_platform.h" +#include "stmvl53l0.h" + +#define I2C_M_WR 0x00 +#define STATUS_OK 0x00 +#define STATUS_FAIL (-1) +/** int VL53L0_I2CWrite(VL53L0_Dev_t dev, void *buff, uint8_t len); + * @brief Write data buffer to VL53L0 device via i2c + * @param dev The device to write to + * @param buff The data buffer + * @param len The length of the transaction in byte + * @return 0 on success + */ +int VL53L0_I2CWrite(VL53L0_DEV dev, uint8_t *buff, uint8_t len) +{ + int err = 0; + + if (dev->bus_type == CCI_BUS) { +#ifdef CAMERA_CCI + uint16_t index; + struct cci_data *cci_client_obj = + (struct cci_data *)dev->client_object; + struct msm_camera_i2c_client *client = cci_client_obj->client; + + index = buff[0]; + /*pr_err("%s: index: %d len: %d\n", __func__, index, len); */ + + if (len == 2) { + uint8_t data; + + data = buff[1]; + /* for byte access */ + err = client->i2c_func_tbl->i2c_write(client, index, + data, MSM_CAMERA_I2C_BYTE_DATA); + if (err < 0) { + pr_err("%s:%d failed status=%d\n", + __func__, __LINE__, err); + return err; + } + } else if (len == 3) { + uint16_t data; + + data = ((uint16_t)buff[1] << 8) | (uint16_t)buff[2]; + err = client->i2c_func_tbl->i2c_write(client, index, + data, MSM_CAMERA_I2C_WORD_DATA); + if (err < 0) { + pr_err("%s:%d failed status=%d\n", + __func__, __LINE__, err); + return err; + } + } else if (len >= 5) { + err = client->i2c_func_tbl->i2c_write_seq(client, + index, &buff[1], (len-1)); + if (err < 0) { + pr_err("%s:%d failed status=%d\n", + __func__, __LINE__, err); + return err; + } + + } +#endif +#ifndef CAMERA_CCI + } else { + struct i2c_msg msg[1]; + struct i2c_data *i2c_client_obj = + (struct i2c_data *)dev->client_object; + struct i2c_client *client = + (struct i2c_client *)i2c_client_obj->client; + + msg[0].addr = client->addr; + msg[0].flags = I2C_M_WR; + msg[0].buf = buff; + msg[0].len = len; + + err = i2c_transfer(client->adapter, msg, 1); + /* return the actual messages transfer */ + if (err != 1) { + pr_err("%s: i2c_transfer err:%d, addr:0x%x, reg:0x%x\n", + __func__, err, client->addr, + (buff[0] << 8 | buff[1])); + return STATUS_FAIL; + } +#endif + } + + return 0; +} + + +/** int VL53L0_I2CRead(VL53L0_Dev_t dev, void *buff, uint8_t len); + * @brief Read data buffer from VL53L0 device via i2c + * @param dev The device to read from + * @param buff The data buffer to fill + * @param len The length of the transaction in byte + * @return transaction status + */ +int VL53L0_I2CRead(VL53L0_DEV dev, uint8_t *buff, uint8_t len) +{ + + int err = 0; + + if (dev->bus_type == CCI_BUS) { +#ifdef CAMERA_CCI + uint16_t index; + struct cci_data *cci_client_obj = + (struct cci_data *)dev->client_object; + struct msm_camera_i2c_client *client = cci_client_obj->client; + + index = buff[0]; + /* pr_err("%s: index: %d\n", __func__, index); */ + err = client->i2c_func_tbl->i2c_read_seq(client, + index, buff, len); + if (err < 0) { + pr_err("%s:%d failed status=%d\n", + __func__, __LINE__, err); + return err; + } +#endif + } else { +#ifndef CAMERA_CCI + struct i2c_msg msg[1]; + struct i2c_data *i2c_client_obj = + (struct i2c_data *)dev->client_object; + struct i2c_client *client = + (struct i2c_client *) i2c_client_obj->client; + + msg[0].addr = client->addr; + msg[0].flags = I2C_M_RD|client->flags; + msg[0].buf = buff; + msg[0].len = len; + + err = i2c_transfer(client->adapter, &msg[0], 1); + /* return the actual mesage transfer */ + if (err != 1) { + pr_err("%s: Read i2c_transfer err:%d, addr:0x%x\n", + __func__, err, client->addr); + return STATUS_FAIL; + } +#endif + } + + return 0; +} diff --git a/drivers/input/misc/vl53L0/stmvl53l0-cci.h b/drivers/input/misc/vl53L0/stmvl53l0-cci.h new file mode 100644 index 000000000000..b99ffe783e59 --- /dev/null +++ b/drivers/input/misc/vl53L0/stmvl53l0-cci.h @@ -0,0 +1,62 @@ +/* + * stmvl53l0-cci.h - Linux kernel modules for STM VL53L0 FlightSense TOF sensor + * + * Copyright (C) 2016 STMicroelectronics Imaging Division + * + * 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. + */ +/* + * Defines + */ +#ifndef STMVL53L0_CCI_H +#define STMVL53L0_CCI_H +#include <linux/types.h> + +#ifdef CAMERA_CCI +#include <soc/qcom/camera2.h> +#include "msm_camera_i2c.h" +#include "msm_camera_dt_util.h" +#include "msm_camera_io_util.h" +#include "msm_cci.h" + +#define MSM_TOF_MAX_VREGS (10) + +struct msm_tof_vreg { + struct camera_vreg_t *cam_vreg; + void *data[MSM_TOF_MAX_VREGS]; + int num_vreg; +}; + +struct cci_data { + struct msm_camera_i2c_client g_client; + struct msm_camera_i2c_client *client; + struct platform_device *pdev; + enum msm_camera_device_type_t device_type; + enum cci_i2c_master_t cci_master; + struct msm_tof_vreg vreg_cfg; + struct msm_sd_subdev msm_sd; + struct v4l2_subdev sdev; + struct v4l2_subdev_ops *subdev_ops; + char subdev_initialized; + uint32_t subdev_id; + uint8_t power_up; + struct msm_camera_gpio_conf *gconf; + struct msm_pinctrl_info pinctrl_info; + uint8_t cam_pinctrl_status; +}; + +int stmvl53l0_init_cci(void); +void stmvl53l0_exit_cci(void *); +int stmvl53l0_power_down_cci(void *); +int stmvl53l0_power_up_cci(void *, unsigned int *); +int stmvl53l0_cci_power_status(void *); +#endif /* CAMERA_CCI */ +#endif /* STMVL53L0_CCI_H */ diff --git a/drivers/input/misc/vl53L0/stmvl53l0-i2c.h b/drivers/input/misc/vl53L0/stmvl53l0-i2c.h new file mode 100644 index 000000000000..51a02c60802b --- /dev/null +++ b/drivers/input/misc/vl53L0/stmvl53l0-i2c.h @@ -0,0 +1,35 @@ +/* + * stmvl53l0-i2c.h - Linux kernel modules for STM VL53L0 FlightSense TOF sensor + * + * Copyright (C) 2016 STMicroelectronics Imaging Division + * + * 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. + */ +/* + * Defines + */ +#ifndef STMVL53L0_I2C_H +#define STMVL53L0_I2C_H +#include <linux/types.h> + +#ifndef CAMERA_CCI +struct i2c_data { + struct i2c_client *client; + struct regulator *vana; + uint8_t power_up; +}; +int stmvl53l0_init_i2c(void); +void stmvl53l0_exit_i2c(void *); +int stmvl53l0_power_up_i2c(void *, unsigned int *); +int stmvl53l0_power_down_i2c(void *); + +#endif /* NOT CAMERA_CCI */ +#endif /* STMVL53L0_I2C_H */ diff --git a/drivers/input/misc/vl53L0/stmvl53l0.h b/drivers/input/misc/vl53L0/stmvl53l0.h new file mode 100644 index 000000000000..b6ef547e86e7 --- /dev/null +++ b/drivers/input/misc/vl53L0/stmvl53l0.h @@ -0,0 +1,219 @@ +/* + * stmvl53l0.h - Linux kernel modules for STM VL53L0 FlightSense TOF sensor + * + * Copyright (C) 2016 STMicroelectronics Imaging Division + * + * 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. + */ +/* + * Defines + */ +#ifndef STMVL53L0_H +#define STMVL53L0_H + +#include <linux/types.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/miscdevice.h> +#include <linux/wait.h> + + +#define STMVL53L0_DRV_NAME "stmvl53l0" +#define STMVL53L0_SLAVE_ADDR (0x52>>1) + +#define DRIVER_VERSION "1.0.5" +#define I2C_M_WR 0x00 +/* #define INT_POLLING_DELAY 20 */ + +/* if don't want to have output from vl53l0_dbgmsg, comment out #DEBUG macro */ +#define DEBUG +#define vl53l0_dbgmsg(str, args...) \ + pr_err("%s: " str, __func__, ##args) +#define vl53l0_errmsg(str, args...) \ + pr_err("%s: " str, __func__, ##args) + +#define VL53L0_VDD_MIN 2600000 +#define VL53L0_VDD_MAX 3000000 + +typedef enum { + NORMAL_MODE = 0, + OFFSETCALIB_MODE = 1, + XTALKCALIB_MODE = 2, +} init_mode_e; + +typedef enum { + OFFSET_PAR = 0, + XTALKRATE_PAR = 1, + XTALKENABLE_PAR = 2, + GPIOFUNC_PAR = 3, + LOWTHRESH_PAR = 4, + HIGHTHRESH_PAR = 5, + DEVICEMODE_PAR = 6, + INTERMEASUREMENT_PAR = 7, + REFERENCESPADS_PAR = 8, + REFCALIBRATION_PAR = 9, +} parameter_name_e; + +enum { + CCI_BUS = 0, + I2C_BUS = 1, +}; + +/* + * IOCTL register data structs + */ +struct stmvl53l0_register { + uint32_t is_read; /*1: read 0: write*/ + uint32_t reg_index; + uint32_t reg_bytes; + uint32_t reg_data; + int32_t status; +}; + +/* + * IOCTL parameter structs + */ +struct stmvl53l0_parameter { + uint32_t is_read; /*1: Get 0: Set*/ + parameter_name_e name; + int32_t value; + int32_t value2; + int32_t status; +}; + +/* + * IOCTL Custom Use Case + */ +struct stmvl53l0_custom_use_case { + FixPoint1616_t signalRateLimit; + FixPoint1616_t sigmaLimit; + uint32_t preRangePulsePeriod; + uint32_t finalRangePulsePeriod; + uint32_t timingBudget; +}; + + +/* + * driver data structs + */ +struct stmvl53l0_data { + + /* !<embed ST VL53L0 Dev data as "dev_data" */ + VL53L0_DevData_t Data; + /*!< i2c device address user specific field*/ + uint8_t I2cDevAddr; + /*!< Type of comms : VL53L0_COMMS_I2C or VL53L0_COMMS_SPI */ + uint8_t comms_type; + /*!< Comms speed [kHz] : typically 400kHz for I2C */ + uint16_t comms_speed_khz; + /* CCI_BUS; I2C_BUS */ + uint8_t bus_type; + + void *client_object; /* cci or i2c client */ + + struct mutex update_lock; + struct delayed_work dwork; /* for PS work handler */ + struct input_dev *input_dev_ps; + struct kobject *range_kobj; + + const char *dev_name; + /* function pointer */ + + /* misc device */ + struct miscdevice miscdev; + + int irq; + int irq_gpio; + unsigned int reset; + + /* control flag from HAL */ + unsigned int enable_ps_sensor; + + /* PS parameters */ + unsigned int ps_data; /* to store PS data */ + + /* Calibration parameters */ + unsigned int offsetCalDistance; + unsigned int xtalkCalDistance; + + /* Calibration values */ + uint32_t refSpadCount; + uint8_t isApertureSpads; + uint8_t VhvSettings; + uint8_t PhaseCal; + int32_t OffsetMicroMeter; + FixPoint1616_t XTalkCompensationRateMegaCps; + uint32_t setCalibratedValue; + + /* Custom values set by app */ + FixPoint1616_t signalRateLimit; + FixPoint1616_t sigmaLimit; + uint32_t preRangePulsePeriod; + uint32_t finalRangePulsePeriod; + + + /* Range Data */ + VL53L0_RangingMeasurementData_t rangeData; + + /* Device parameters */ + VL53L0_DeviceModes deviceMode; + uint32_t interMeasurems; + VL53L0_GpioFunctionality gpio_function; + VL53L0_InterruptPolarity gpio_polarity; + FixPoint1616_t low_threshold; + FixPoint1616_t high_threshold; + + /* delay time in miniseconds*/ + uint8_t delay_ms; + + /* Timing Budget */ + uint32_t timingBudget; + /* Use this threshold to force restart ranging */ + uint32_t noInterruptCount; + /* Use this flag to denote use case*/ + uint8_t useCase; + /* Use this flag to indicate an update of use case */ + uint8_t updateUseCase; + /* Polling thread */ + struct task_struct *poll_thread; + /* Wait Queue on which the poll thread blocks */ + wait_queue_head_t poll_thread_wq; + + /* Recent interrupt status */ + uint32_t interruptStatus; + + struct mutex work_mutex; + + struct timer_list timer; + uint32_t flushCount; + + /* Debug */ + unsigned int enableDebug; + uint8_t interrupt_received; +}; + +/* + * function pointer structs + */ +struct stmvl53l0_module_fn_t { + int (*init)(void); + void (*deinit)(void *); + int (*power_up)(void *, unsigned int *); + int (*power_down)(void *); + int (*query_power_status)(void *); +}; + + + +int stmvl53l0_setup(struct stmvl53l0_data *data); +void stmvl53l0_cleanup(struct stmvl53l0_data *data); + +#endif /* STMVL53L0_H */ diff --git a/drivers/input/misc/vl53L0/stmvl53l0_module-cci.c b/drivers/input/misc/vl53L0/stmvl53l0_module-cci.c new file mode 100644 index 000000000000..8520a74c9961 --- /dev/null +++ b/drivers/input/misc/vl53L0/stmvl53l0_module-cci.c @@ -0,0 +1,547 @@ +/* + * stmvl53l0_module-cci.c - Linux kernel modules for STM VL53L0 FlightSense TOF + * sensor + * + * Copyright (C) 2016 STMicroelectronics Imaging Division. + * + * 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 <linux/uaccess.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/input.h> +#include <linux/miscdevice.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/time.h> +#include <linux/platform_device.h> +/* + * power specific includes + */ +#include <linux/pwm.h> +#include <linux/regulator/consumer.h> +#include <linux/pinctrl/consumer.h> +#include <linux/clk.h> +#include <linux/of_gpio.h> +/* + * API includes + */ +#include "vl53l0_api.h" +#include "vl53l0_def.h" +#include "vl53l0_platform.h" +#include "stmvl53l0-cci.h" +#include "stmvl53l0-i2c.h" +#include "stmvl53l0.h" + +#ifdef CAMERA_CCI +/* + * Global data + */ +static struct v4l2_file_operations msm_tof_v4l2_subdev_fops; +static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = { + .i2c_read = msm_camera_cci_i2c_read, + .i2c_read_seq = msm_camera_cci_i2c_read_seq, + .i2c_write = msm_camera_cci_i2c_write, + .i2c_write_seq = msm_camera_cci_i2c_write_seq, + .i2c_write_table = msm_camera_cci_i2c_write_table, + .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_cci_i2c_write_table_w_microdelay, + .i2c_util = msm_sensor_cci_i2c_util, + .i2c_poll = msm_camera_cci_i2c_poll, +}; + +static int stmvl53l0_get_dt_data(struct device *dev, struct cci_data *data); + +/* + * QCOM specific functions + */ +static int stmvl53l0_get_dt_data(struct device *dev, struct cci_data *data) +{ + int rc = 0; + + vl53l0_dbgmsg("Enter\n"); + + if (dev->of_node) { + struct device_node *of_node = dev->of_node; + struct msm_tof_vreg *vreg_cfg; + + if (!of_node) { + vl53l0_errmsg("failed %d\n", __LINE__); + return -EINVAL; + } + + rc = of_property_read_u32(of_node, + "cell-index", &data->pdev->id); + if (rc < 0) { + vl53l0_errmsg("failed %d\n", __LINE__); + return rc; + } + vl53l0_dbgmsg("cell-index: %d\n", data->pdev->id); + rc = of_property_read_u32(of_node, "qcom,cci-master", + &data->cci_master); + if (rc < 0) { + vl53l0_errmsg("failed %d\n", __LINE__); + /* Set default master 0 */ + data->cci_master = MASTER_0; + rc = 0; + } + vl53l0_dbgmsg("cci_master: %d\n", data->cci_master); + if (of_find_property(of_node, "qcom,cam-vreg-name", NULL)) { + vreg_cfg = &data->vreg_cfg; + rc = msm_camera_get_dt_vreg_data(of_node, + &vreg_cfg->cam_vreg, + &vreg_cfg->num_vreg); + if (rc < 0) { + vl53l0_errmsg("failed %d\n", __LINE__); + return rc; + } + vl53l0_dbgmsg("vreg-name: %s min_volt: %d max_volt: %d", + vreg_cfg->cam_vreg->reg_name, + vreg_cfg->cam_vreg->min_voltage, + vreg_cfg->cam_vreg->max_voltage); + } + + rc = msm_sensor_driver_get_gpio_data(&(data->gconf), of_node); + if ((rc < 0) || (data->gconf == NULL)) { + vl53l0_errmsg + ("No Laser Sensor GPIOs to be configured!\n"); + } + + } + vl53l0_dbgmsg("End rc =%d\n", rc); + + return rc; +} + +static int32_t stmvl53l0_vreg_control(struct cci_data *data, int config) +{ + int rc = 0, i, cnt; + struct msm_tof_vreg *vreg_cfg; + + vl53l0_dbgmsg("Enter\n"); + + vreg_cfg = &data->vreg_cfg; + cnt = vreg_cfg->num_vreg; + vl53l0_dbgmsg("num_vreg: %d\n", cnt); + if (!cnt) { + vl53l0_errmsg("failed %d\n", __LINE__); + return 0; + } + + if (cnt >= MSM_TOF_MAX_VREGS) { + vl53l0_errmsg("failed %d cnt %d\n", __LINE__, cnt); + return -EINVAL; + } + + for (i = 0; i < cnt; i++) { + rc = msm_camera_config_single_vreg(&(data->pdev->dev), + &vreg_cfg->cam_vreg[i], + (struct regulator **) + &vreg_cfg->data[i], config); + } + + vl53l0_dbgmsg("EXIT rc =%d\n", rc); + return rc; +} + +static int msm_tof_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + int rc = 0; +/* + * struct msm_tof_ctrl_t *tof_ctrl = v4l2_get_subdevdata(sd); + * if (!tof_ctrl) { + * pr_err("failed\n"); + * return -EINVAL; + * } + * if (tof_ctrl->tof_device_type == MSM_CAMERA_PLATFORM_DEVICE) { + * rc = tof_ctrl->i2c_client.i2c_func_tbl->i2c_util( + * &tof_ctrl->i2c_client, MSM_CCI_RELEASE); + * if (rc < 0) + * pr_err("cci_init failed\n"); + * } + * tof_ctrl->i2c_state = TOF_I2C_RELEASE; + */ + return rc; +} + +static const struct v4l2_subdev_internal_ops msm_tof_internal_ops = { + .close = msm_tof_close, +}; + +static long msm_tof_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct cci_data *cci_object = NULL; + int32_t rc = 0; + + cci_object = v4l2_get_subdevdata(sd); + if (cmd == MSM_SD_SHUTDOWN) + cci_object->power_up = 0; + + vl53l0_dbgmsg("cmd = %d power_up = %d", cmd, cci_object->power_up); + return rc; +} + +static int32_t msm_tof_power(struct v4l2_subdev *sd, int on) +{ + vl53l0_dbgmsg("TOF power called\n"); + return 0; +} + +static struct v4l2_subdev_core_ops msm_tof_subdev_core_ops = { + .ioctl = msm_tof_subdev_ioctl, + .s_power = msm_tof_power, +}; + +static struct v4l2_subdev_ops msm_tof_subdev_ops = { + .core = &msm_tof_subdev_core_ops, +}; + +static int stmvl53l0_cci_init(struct cci_data *data) +{ + int rc = 0; + struct msm_camera_cci_client *cci_client = data->client->cci_client; + + if (data->subdev_initialized == FALSE) { + data->client->i2c_func_tbl = &msm_sensor_cci_func_tbl; + data->client->cci_client = + kzalloc(sizeof(struct msm_camera_cci_client), GFP_KERNEL); + if (!data->client->cci_client) { + vl53l0_errmsg("%d, failed no memory\n", __LINE__); + return -ENOMEM; + } + cci_client = data->client->cci_client; + cci_client->cci_subdev = msm_cci_get_subdev(); + if (cci_client->cci_subdev == NULL) { + vl53l0_errmsg("CCI subdev is not initialized!!\n"); + return -ENODEV; + } + cci_client->cci_i2c_master = data->cci_master; + v4l2_subdev_init(&data->msm_sd.sd, data->subdev_ops); + v4l2_set_subdevdata(&data->msm_sd.sd, data); + data->msm_sd.sd.internal_ops = &msm_tof_internal_ops; + data->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(data->msm_sd.sd.name, + ARRAY_SIZE(data->msm_sd.sd.name), "msm_tof"); + media_entity_init(&data->msm_sd.sd.entity, 0, NULL, 0); + data->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + data->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_TOF; + data->msm_sd.close_seq = + MSM_SD_CLOSE_2ND_CATEGORY | 0x2; + msm_sd_register(&data->msm_sd); + msm_tof_v4l2_subdev_fops = v4l2_subdev_fops; + data->msm_sd.sd.devnode->fops = + &msm_tof_v4l2_subdev_fops; + data->subdev_initialized = TRUE; + } + + cci_client->sid = 0x29; + cci_client->retries = 3; + cci_client->id_map = 0; + cci_client->cci_i2c_master = data->cci_master; + cci_client->i2c_freq_mode = I2C_FAST_MODE; + rc = data->client->i2c_func_tbl->i2c_util(data->client, MSM_CCI_INIT); + if (rc < 0) { + vl53l0_errmsg("%d: CCI Init failed\n", __LINE__); + return rc; + } + vl53l0_dbgmsg("CCI Init Succeeded\n"); + + data->client->addr_type = MSM_CAMERA_I2C_BYTE_ADDR; + + return 0; +} + +static int32_t stmvl53l0_platform_probe(struct platform_device *pdev) +{ + struct stmvl53l0_data *vl53l0_data = NULL; + struct cci_data *cci_object = NULL; + int32_t rc = 0; + + vl53l0_dbgmsg("Enter\n"); + + if (!pdev->dev.of_node) { + vl53l0_errmsg("of_node NULL\n"); + return -EINVAL; + } + + vl53l0_data = kzalloc(sizeof(struct stmvl53l0_data), GFP_KERNEL); + if (!vl53l0_data) { + rc = -ENOMEM; + return rc; + } + if (vl53l0_data) { + vl53l0_data->client_object = + kzalloc(sizeof(struct cci_data), GFP_KERNEL); + if (!vl53l0_data->client_object) { + rc = -ENOMEM; + kfree(vl53l0_data); + return rc; + } + cci_object = (struct cci_data *)vl53l0_data->client_object; + } + cci_object->client = + (struct msm_camera_i2c_client *)&cci_object->g_client; + + /* setup bus type */ + vl53l0_data->bus_type = CCI_BUS; + + /* Set platform device handle */ + cci_object->subdev_ops = &msm_tof_subdev_ops; + cci_object->pdev = pdev; + rc = stmvl53l0_get_dt_data(&pdev->dev, cci_object); + if (rc < 0) { + vl53l0_errmsg("%d, failed rc %d\n", __LINE__, rc); + kfree(vl53l0_data->client_object); + kfree(vl53l0_data); + return rc; + } + vl53l0_data->irq_gpio = of_get_named_gpio_flags(pdev->dev.of_node, + "stm,irq-gpio", 0, NULL); + + if (!gpio_is_valid(vl53l0_data->irq_gpio)) { + vl53l0_errmsg("%d failed get irq gpio", __LINE__); + kfree(vl53l0_data->client_object); + kfree(vl53l0_data); + return -EINVAL; + } + + cci_object->subdev_id = pdev->id; + + /* Set device type as platform device */ + cci_object->device_type = MSM_CAMERA_PLATFORM_DEVICE; + cci_object->subdev_initialized = FALSE; + + /* setup device name */ + vl53l0_data->dev_name = dev_name(&pdev->dev); + + /* setup device data */ + dev_set_drvdata(&pdev->dev, vl53l0_data); + + /* setup other stuff */ + rc = stmvl53l0_setup(vl53l0_data); + + /* init default value */ + cci_object->power_up = 0; + + vl53l0_dbgmsg("End\n"); + + return rc; + +} + +static int32_t stmvl53l0_platform_remove(struct platform_device *pdev) +{ + struct stmvl53l0_data *vl53l0_data = platform_get_drvdata(pdev); + + stmvl53l0_cleanup(vl53l0_data); + platform_set_drvdata(pdev, NULL); + + kfree(vl53l0_data->client_object); + kfree(vl53l0_data); + + return 0; +} + +static const struct of_device_id st_stmvl53l0_dt_match[] = { + {.compatible = "st,stmvl53l0",}, + {}, +}; + +static struct platform_driver stmvl53l0_platform_driver = { + .probe = stmvl53l0_platform_probe, + .remove = stmvl53l0_platform_remove, + .driver = { + .name = STMVL53L0_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = st_stmvl53l0_dt_match, + }, +}; + +int stmvl53l0_power_up_cci(void *cci_object, unsigned int *preset_flag) +{ + int ret = 0; + struct cci_data *data = (struct cci_data *)cci_object; + struct gpio *gpio_tbl = NULL; + uint8_t gpio_tbl_size = 0; + int i = 0; + + vl53l0_dbgmsg("Enter"); + + /* need to init cci first */ + if (!data) { + pr_err("%s:%d failed\n", __func__, __LINE__); + return -EINVAL; + } + + ret = stmvl53l0_cci_init(data); + if (ret) { + vl53l0_errmsg("stmvl53l0_cci_init failed %d\n", __LINE__); + return ret; + } + + /* Check if GPIO needs to be enabled for chip select */ + vl53l0_dbgmsg("Get gpio table!size: %d\n", + data->gconf->cam_gpio_req_tbl_size); + gpio_tbl = data->gconf->cam_gpio_req_tbl; + gpio_tbl_size = data->gconf->cam_gpio_req_tbl_size; + if (gpio_tbl_size > 0) { + ret = msm_camera_pinctrl_init(&(data->pinctrl_info), + &(data->pdev->dev)); + if (ret < 0) { + vl53l0_errmsg("Initialization of pinctrl failed\n"); + data->cam_pinctrl_status = 0; + } else { + data->cam_pinctrl_status = 1; + } + + for (i = 0; i < gpio_tbl_size; i++) { + ret = gpio_request_one(gpio_tbl[i].gpio, + gpio_tbl[i].flags, + gpio_tbl[i].label); + if (ret < 0) { + vl53l0_errmsg + ("Request for GPIO %d failed! Err: %d\n", + gpio_tbl[i].gpio, ret); + } else { + if (data->cam_pinctrl_status) { + ret = + pinctrl_select_state( + data->pinctrl_info.pinctrl, + data->pinctrl_info.gpio_state_active); + if (ret < 0) { + vl53l0_errmsg( + "%s: Cannot set pin to active state!\n", + __func__); + } + } + vl53l0_dbgmsg("Set pin %d value to 1!\n", + gpio_tbl[i].gpio); + gpio_set_value_cansleep(gpio_tbl[i].gpio, 1); + } + } + } + + /* actual power up */ + if (data->device_type == MSM_CAMERA_PLATFORM_DEVICE) { + ret = stmvl53l0_vreg_control(data, 1); + if (ret < 0) { + vl53l0_errmsg("stmvl53l0_vreg_control failed %d\n", + __LINE__); + return ret; + } + } + data->power_up = 1; + usleep_range(3000, 3500); + *preset_flag = 1; + vl53l0_dbgmsg("End\n"); + + return ret; +} + +int stmvl53l0_power_down_cci(void *cci_object) +{ + int ret = 0; + struct cci_data *data = (struct cci_data *)cci_object; + int i = 0; + struct gpio *gpio_tbl = NULL; + uint8_t gpio_tbl_size = 0; + + vl53l0_dbgmsg("Enter\n"); + if (data->power_up) { + /* need to release cci first */ + ret = data->client->i2c_func_tbl->i2c_util(data->client, + MSM_CCI_RELEASE); + if (ret < 0) + vl53l0_errmsg("CCI Release failed rc %d\n", ret); + + /* actual power down */ + if (data->device_type == MSM_CAMERA_PLATFORM_DEVICE) { + ret = stmvl53l0_vreg_control(data, 0); + if (ret < 0) { + vl53l0_errmsg + ("stmvl53l0_vreg_control failed %d\n", + __LINE__); + return ret; + } + } + + /* reset GPIO pins */ + gpio_tbl = data->gconf->cam_gpio_req_tbl; + gpio_tbl_size = data->gconf->cam_gpio_req_tbl_size; + if (gpio_tbl_size > 0) { + for (i = 0; i < gpio_tbl_size; i++) + gpio_set_value_cansleep(gpio_tbl[i].gpio, 0); + if (data->cam_pinctrl_status) { + ret = + pinctrl_select_state(data->pinctrl_info. + pinctrl, + data->pinctrl_info. + gpio_state_suspend); + if (ret < 0) { + vl53l0_errmsg( + "Error setting gpio pin to supsend state!\n"); + } + + devm_pinctrl_put(data->pinctrl_info.pinctrl); + data->cam_pinctrl_status = 0; + gpio_free_array(gpio_tbl, gpio_tbl_size); + } + } + } + data->power_up = 0; + vl53l0_dbgmsg("End\n"); + return ret; +} + +int stmvl53l0_cci_power_status(void *cci_object) +{ + struct cci_data *data = (struct cci_data *)cci_object; + + return data->power_up; +} + +int stmvl53l0_init_cci(void) +{ + int ret = 0; + + vl53l0_dbgmsg("Enter\n"); + + /* register as a platform device */ + ret = platform_driver_register(&stmvl53l0_platform_driver); + if (ret) + vl53l0_errmsg("%d, error ret:%d\n", __LINE__, ret); + + vl53l0_dbgmsg("End\n"); + + return ret; +} + +void stmvl53l0_exit_cci(void *cci_object) +{ + struct cci_data *data = (struct cci_data *)cci_object; + + vl53l0_dbgmsg("Enter\n"); + + if (data && data->client->cci_client) + kfree(data->client->cci_client); + + vl53l0_dbgmsg("End\n"); +} +#endif /* end of CAMERA_CCI */ diff --git a/drivers/input/misc/vl53L0/stmvl53l0_module-i2c.c b/drivers/input/misc/vl53L0/stmvl53l0_module-i2c.c new file mode 100644 index 000000000000..0bff754de15b --- /dev/null +++ b/drivers/input/misc/vl53L0/stmvl53l0_module-i2c.c @@ -0,0 +1,266 @@ +/* + * stmvl53l0_module-i2c.c - Linux kernel modules for STM VL53L0 FlightSense TOF + * sensor + * + * Copyright (C) 2016 STMicroelectronics Imaging Division. + * + * 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 <linux/uaccess.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/input.h> +#include <linux/miscdevice.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/time.h> +#include <linux/platform_device.h> +/* + * power specific includes + */ +#include <linux/pwm.h> +#include <linux/regulator/consumer.h> +#include <linux/pinctrl/consumer.h> +#include <linux/clk.h> +#include <linux/of_gpio.h> +/* + * API includes + */ +#include "vl53l0_api.h" +#include "vl53l0_def.h" +#include "vl53l0_platform.h" +#include "stmvl53l0-i2c.h" +#include "stmvl53l0-cci.h" +#include "stmvl53l0.h" +#ifndef CAMERA_CCI + +/* + * Global data + */ +static int stmvl53l0_parse_vdd(struct device *dev, struct i2c_data *data); + +/* + * QCOM specific functions + */ +static int stmvl53l0_parse_vdd(struct device *dev, struct i2c_data *data) +{ + int ret = 0; + + vl53l0_dbgmsg("Enter\n"); + + if (dev->of_node) { + data->vana = regulator_get(dev, "vdd"); + if (IS_ERR(data->vana)) { + vl53l0_errmsg("vdd supply is not provided\n"); + ret = -1; + } + } + vl53l0_dbgmsg("End\n"); + + return ret; +} + +static int stmvl53l0_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + struct stmvl53l0_data *vl53l0_data = NULL; + struct i2c_data *i2c_object = NULL; + + vl53l0_dbgmsg("Enter\n"); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { + rc = -EIO; + return rc; + } + + vl53l0_data = kzalloc(sizeof(struct stmvl53l0_data), GFP_KERNEL); + if (!vl53l0_data) { + rc = -ENOMEM; + return rc; + } + if (vl53l0_data) { + vl53l0_data->client_object = + kzalloc(sizeof(struct i2c_data), GFP_KERNEL); + i2c_object = (struct i2c_data *)vl53l0_data->client_object; + } + i2c_object->client = client; + + /* setup bus type */ + vl53l0_data->bus_type = I2C_BUS; + + /* setup regulator */ + stmvl53l0_parse_vdd(&i2c_object->client->dev, i2c_object); + + /* setup device name */ + vl53l0_data->dev_name = dev_name(&client->dev); + + /* setup device data */ + dev_set_drvdata(&client->dev, vl53l0_data); + + /* setup client data */ + i2c_set_clientdata(client, vl53l0_data); + + /* setup other stuff */ + rc = stmvl53l0_setup(vl53l0_data); + + /* init default value */ + i2c_object->power_up = 0; + + vl53l0_dbgmsg("End\n"); + return rc; +} + +static int stmvl53l0_remove(struct i2c_client *client) +{ + struct stmvl53l0_data *data = i2c_get_clientdata(client); + + vl53l0_dbgmsg("Enter\n"); + + /* Power down the device */ + stmvl53l0_power_down_i2c(data->client_object); + stmvl53l0_cleanup(data); + kfree(data->client_object); + kfree(data); + vl53l0_dbgmsg("End\n"); + return 0; +} + +static const struct i2c_device_id stmvl53l0_id[] = { + {STMVL53L0_DRV_NAME, 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, stmvl53l0_id); + +static const struct of_device_id st_stmvl53l0_dt_match[] = { + {.compatible = "st,stmvl53l0",}, + {}, +}; + +static struct i2c_driver stmvl53l0_driver = { + .driver = { + .name = STMVL53L0_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = st_stmvl53l0_dt_match, + }, + .probe = stmvl53l0_probe, + .remove = stmvl53l0_remove, + .id_table = stmvl53l0_id, + +}; + +int stmvl53l0_power_up_i2c(void *i2c_object, unsigned int *preset_flag) +{ + int ret = 0; +#ifndef STM_TEST + struct i2c_data *data = (struct i2c_data *)i2c_object; +#endif + + vl53l0_dbgmsg("Enter\n"); + + /* actual power on */ +#ifndef STM_TEST + ret = regulator_set_voltage(data->vana, VL53L0_VDD_MIN, VL53L0_VDD_MAX); + if (ret < 0) { + vl53l0_errmsg("set_vol(%p) fail %d\n", data->vana, ret); + return ret; + } + ret = regulator_enable(data->vana); + + usleep_range(2950, 3000); + if (ret < 0) { + vl53l0_errmsg("reg enable(%p) failed.rc=%d\n", data->vana, ret); + return ret; + } + data->power_up = 1; + *preset_flag = 1; +#endif + + vl53l0_dbgmsg("End\n"); + return ret; +} + +int stmvl53l0_power_down_i2c(void *i2c_object) +{ + int ret = 0; +#ifndef STM_TEST + struct i2c_data *data = (struct i2c_data *)i2c_object; +#endif + + vl53l0_dbgmsg("Enter\n"); +#ifndef STM_TEST + usleep_range(2950, 3000); + ret = regulator_disable(data->vana); + if (ret < 0) + vl53l0_errmsg("reg disable(%p) failed.rc=%d\n", + data->vana, ret); + + data->power_up = 0; +#endif + + vl53l0_dbgmsg("End\n"); + return ret; +} + +int stmvl53l0_init_i2c(void) +{ + int ret = 0; + +#ifdef STM_TEST + struct i2c_client *client = NULL; + struct i2c_adapter *adapter; + struct i2c_board_info info = { + .type = "stmvl53l0", + .addr = STMVL53L0_SLAVE_ADDR, + }; +#endif + + vl53l0_dbgmsg("Enter\n"); + + /* register as a i2c client device */ + ret = i2c_add_driver(&stmvl53l0_driver); + if (ret) + vl53l0_errmsg("%d erro ret:%d\n", __LINE__, ret); + +#ifdef STM_TEST + if (!ret) { + adapter = i2c_get_adapter(4); + if (!adapter) + ret = -EINVAL; + else + client = i2c_new_device(adapter, &info); + if (!client) + ret = -EINVAL; + } +#endif + + vl53l0_dbgmsg("End with rc:%d\n", ret); + + return ret; +} + +void stmvl53l0_exit_i2c(void *i2c_object) +{ + vl53l0_dbgmsg("Enter\n"); + i2c_del_driver(&stmvl53l0_driver); + + vl53l0_dbgmsg("End\n"); +} + +#endif /* end of NOT CAMERA_CCI */ diff --git a/drivers/input/misc/vl53L0/stmvl53l0_module.c b/drivers/input/misc/vl53L0/stmvl53l0_module.c new file mode 100644 index 000000000000..27672d97448a --- /dev/null +++ b/drivers/input/misc/vl53L0/stmvl53l0_module.c @@ -0,0 +1,2885 @@ +/* + * stmvl53l0_module.c - Linux kernel modules for STM VL53L0 FlightSense TOF + * sensor + * + * Copyright (C) 2016 STMicroelectronics Imaging Division. + * + * 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 <linux/uaccess.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/input.h> +#include <linux/miscdevice.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/time.h> +#include <linux/platform_device.h> +#include <linux/kobject.h> +#include <linux/kthread.h> +/* + * API includes + */ +#include "vl53l0_api.h" +#include "vl53l010_api.h" + +#define USE_INT +/* #define DEBUG_TIME_LOG */ +#ifdef DEBUG_TIME_LOG +struct timeval start_tv, stop_tv; +#endif + +/* + * Global data + */ + +#ifdef CAMERA_CCI +static struct stmvl53l0_module_fn_t stmvl53l0_module_func_tbl = { + .init = stmvl53l0_init_cci, + .deinit = stmvl53l0_exit_cci, + .power_up = stmvl53l0_power_up_cci, + .power_down = stmvl53l0_power_down_cci, + .query_power_status = stmvl53l0_cci_power_status, +}; +#else +static struct stmvl53l0_module_fn_t stmvl53l0_module_func_tbl = { + .init = stmvl53l0_init_i2c, + .deinit = stmvl53l0_exit_i2c, + .power_up = stmvl53l0_power_up_i2c, + .power_down = stmvl53l0_power_down_i2c, + .stmv53l0_cci_power_status = NULL; +}; +#endif +struct stmvl53l0_module_fn_t *pmodule_func_tbl; + +struct stmvl53l0_api_fn_t { + int8_t (*GetVersion)(VL53L0_Version_t *pVersion); + int8_t (*GetPalSpecVersion)(VL53L0_Version_t *pPalSpecVersion); + + int8_t (*GetProductRevision)(VL53L0_DEV Dev, + uint8_t *pProductRevisionMajor, + uint8_t *pProductRevisionMinor); + int8_t (*GetDeviceInfo)(VL53L0_DEV Dev, + VL53L0_DeviceInfo_t *pVL53L0_DeviceInfo); + int8_t (*GetDeviceErrorStatus)(VL53L0_DEV Dev, + VL53L0_DeviceError *pDeviceErrorStatus); + int8_t (*GetRangeStatusString)(uint8_t RangeStatus, + char *pRangeStatusString); + int8_t (*GetDeviceErrorString)(VL53L0_DeviceError ErrorCode, + char *pDeviceErrorString); + int8_t (*GetPalErrorString)(VL53L0_Error PalErrorCode, + char *pPalErrorString); + int8_t (*GetPalStateString)(VL53L0_State PalStateCode, + char *pPalStateString); + int8_t (*GetPalState)(VL53L0_DEV Dev, VL53L0_State *pPalState); + int8_t (*SetPowerMode)(VL53L0_DEV Dev, + VL53L0_PowerModes PowerMode); + int8_t (*GetPowerMode)(VL53L0_DEV Dev, + VL53L0_PowerModes *pPowerMode); + int8_t (*SetOffsetCalibrationDataMicroMeter)(VL53L0_DEV Dev, + int32_t OffsetCalibrationDataMicroMeter); + int8_t (*GetOffsetCalibrationDataMicroMeter)(VL53L0_DEV Dev, + int32_t *pOffsetCalibrationDataMicroMeter); + int8_t (*SetLinearityCorrectiveGain)(VL53L0_DEV Dev, + int16_t LinearityCorrectiveGain); + int8_t (*GetLinearityCorrectiveGain)(VL53L0_DEV Dev, + uint16_t *pLinearityCorrectiveGain); + int8_t (*SetGroupParamHold)(VL53L0_DEV Dev, + uint8_t GroupParamHold); + int8_t (*GetUpperLimitMilliMeter)(VL53L0_DEV Dev, + uint16_t *pUpperLimitMilliMeter); + int8_t (*SetDeviceAddress)(VL53L0_DEV Dev, + uint8_t DeviceAddress); + int8_t (*DataInit)(VL53L0_DEV Dev); + int8_t (*SetTuningSettingBuffer)(VL53L0_DEV Dev, + uint8_t *pTuningSettingBuffer, + uint8_t UseInternalTuningSettings); + int8_t (*GetTuningSettingBuffer)(VL53L0_DEV Dev, + uint8_t **pTuningSettingBuffer, + uint8_t *pUseInternalTuningSettings); + int8_t (*StaticInit)(VL53L0_DEV Dev); + int8_t (*WaitDeviceBooted)(VL53L0_DEV Dev); + int8_t (*ResetDevice)(VL53L0_DEV Dev); + int8_t (*SetDeviceParameters)(VL53L0_DEV Dev, + const VL53L0_DeviceParameters_t *pDeviceParameters); + int8_t (*GetDeviceParameters)(VL53L0_DEV Dev, + VL53L0_DeviceParameters_t *pDeviceParameters); + int8_t (*SetDeviceMode)(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode); + int8_t (*GetDeviceMode)(VL53L0_DEV Dev, + VL53L0_DeviceModes *pDeviceMode); + int8_t (*SetHistogramMode)(VL53L0_DEV Dev, + VL53L0_HistogramModes HistogramMode); + int8_t (*GetHistogramMode)(VL53L0_DEV Dev, + VL53L0_HistogramModes *pHistogramMode); + int8_t (*SetMeasurementTimingBudgetMicroSeconds)(VL53L0_DEV Dev, + uint32_t MeasurementTimingBudgetMicroSeconds); + int8_t (*GetMeasurementTimingBudgetMicroSeconds)( + VL53L0_DEV Dev, + uint32_t *pMeasurementTimingBudgetMicroSeconds); + int8_t (*GetVcselPulsePeriod)(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, + uint8_t *pVCSELPulsePeriod); + int8_t (*SetVcselPulsePeriod)(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, + uint8_t VCSELPulsePeriod); + int8_t (*SetSequenceStepEnable)(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + uint8_t SequenceStepEnabled); + int8_t (*GetSequenceStepEnable)(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + uint8_t *pSequenceStepEnabled); + int8_t (*GetSequenceStepEnables)(VL53L0_DEV Dev, + VL53L0_SchedulerSequenceSteps_t *pSchedulerSequenceSteps); + int8_t (*SetSequenceStepTimeout)(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + FixPoint1616_t TimeOutMilliSecs); + int8_t (*GetSequenceStepTimeout)(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + FixPoint1616_t *pTimeOutMilliSecs); + int8_t (*GetNumberOfSequenceSteps)(VL53L0_DEV Dev, + uint8_t *pNumberOfSequenceSteps); + int8_t (*GetSequenceStepsInfo)( + VL53L0_SequenceStepId SequenceStepId, + char *pSequenceStepsString); + int8_t (*SetInterMeasurementPeriodMilliSeconds)( + VL53L0_DEV Dev, + uint32_t InterMeasurementPeriodMilliSeconds); + int8_t (*GetInterMeasurementPeriodMilliSeconds)( + VL53L0_DEV Dev, + uint32_t *pInterMeasurementPeriodMilliSeconds); + int8_t (*SetXTalkCompensationEnable)(VL53L0_DEV Dev, + uint8_t XTalkCompensationEnable); + int8_t (*GetXTalkCompensationEnable)(VL53L0_DEV Dev, + uint8_t *pXTalkCompensationEnable); + int8_t (*SetXTalkCompensationRateMegaCps)( + VL53L0_DEV Dev, + FixPoint1616_t XTalkCompensationRateMegaCps); + int8_t (*GetXTalkCompensationRateMegaCps)( + VL53L0_DEV Dev, + FixPoint1616_t *pXTalkCompensationRateMegaCps); + int8_t (*GetNumberOfLimitCheck)( + uint16_t *pNumberOfLimitCheck); + int8_t (*GetLimitCheckInfo)(VL53L0_DEV Dev, + uint16_t LimitCheckId, char *pLimitCheckString); + int8_t (*SetLimitCheckEnable)(VL53L0_DEV Dev, + uint16_t LimitCheckId, + uint8_t LimitCheckEnable); + int8_t (*GetLimitCheckEnable)(VL53L0_DEV Dev, + uint16_t LimitCheckId, uint8_t *pLimitCheckEnable); + int8_t (*SetLimitCheckValue)(VL53L0_DEV Dev, + uint16_t LimitCheckId, + FixPoint1616_t LimitCheckValue); + int8_t (*GetLimitCheckValue)(VL53L0_DEV Dev, + uint16_t LimitCheckId, + FixPoint1616_t *pLimitCheckValue); + int8_t (*GetLimitCheckCurrent)(VL53L0_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t *pLimitCheckCurrent); + int8_t (*SetWrapAroundCheckEnable)(VL53L0_DEV Dev, + uint8_t WrapAroundCheckEnable); + int8_t (*GetWrapAroundCheckEnable)(VL53L0_DEV Dev, + uint8_t *pWrapAroundCheckEnable); + int8_t (*PerformSingleMeasurement)(VL53L0_DEV Dev); + int8_t (*PerformRefCalibration)(VL53L0_DEV Dev, + uint8_t *pVhvSettings, uint8_t *pPhaseCal); + int8_t (*SetRefCalibration)(VL53L0_DEV Dev, + uint8_t VhvSettings, + uint8_t PhaseCal); + int8_t (*GetRefCalibration)(VL53L0_DEV Dev, + uint8_t *pVhvSettings, + uint8_t *pPhaseCal); + int8_t (*PerformXTalkCalibration)(VL53L0_DEV Dev, + FixPoint1616_t XTalkCalDistance, + FixPoint1616_t *pXTalkCompensationRateMegaCps); + int8_t (*PerformOffsetCalibration)(VL53L0_DEV Dev, + FixPoint1616_t CalDistanceMilliMeter, + int32_t *pOffsetMicroMeter); + int8_t (*StartMeasurement)(VL53L0_DEV Dev); + int8_t (*StopMeasurement)(VL53L0_DEV Dev); + int8_t (*GetMeasurementDataReady)(VL53L0_DEV Dev, + uint8_t *pMeasurementDataReady); + int8_t (*WaitDeviceReadyForNewMeasurement)(VL53L0_DEV Dev, + uint32_t MaxLoop); + int8_t (*GetRangingMeasurementData)(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData); + int8_t (*GetHistogramMeasurementData)(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData); + int8_t (*PerformSingleRangingMeasurement)(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData); + int8_t (*PerformSingleHistogramMeasurement)(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData); + int8_t (*SetNumberOfROIZones)(VL53L0_DEV Dev, + uint8_t NumberOfROIZones); + int8_t (*GetNumberOfROIZones)(VL53L0_DEV Dev, + uint8_t *pNumberOfROIZones); + int8_t (*GetMaxNumberOfROIZones)(VL53L0_DEV Dev, + uint8_t *pMaxNumberOfROIZones); + int8_t (*SetGpioConfig)(VL53L0_DEV Dev, + uint8_t Pin, + VL53L0_DeviceModes DeviceMode, + VL53L0_GpioFunctionality Functionality, + VL53L0_InterruptPolarity Polarity); + int8_t (*GetGpioConfig)(VL53L0_DEV Dev, + uint8_t Pin, + VL53L0_DeviceModes *pDeviceMode, + VL53L0_GpioFunctionality *pFunctionality, + VL53L0_InterruptPolarity *pPolarity); + int8_t (*SetInterruptThresholds)(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode, + FixPoint1616_t ThresholdLow, + FixPoint1616_t ThresholdHigh); + int8_t (*GetInterruptThresholds)(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode, + FixPoint1616_t *pThresholdLow, + FixPoint1616_t *pThresholdHigh); + int8_t (*ClearInterruptMask)(VL53L0_DEV Dev, + uint32_t InterruptMask); + int8_t (*GetInterruptMaskStatus)(VL53L0_DEV Dev, + uint32_t *pInterruptMaskStatus); + int8_t (*EnableInterruptMask)(VL53L0_DEV Dev, uint32_t InterruptMask); + int8_t (*SetSpadAmbientDamperThreshold)(VL53L0_DEV Dev, + uint16_t SpadAmbientDamperThreshold); + int8_t (*GetSpadAmbientDamperThreshold)(VL53L0_DEV Dev, + uint16_t *pSpadAmbientDamperThreshold); + int8_t (*SetSpadAmbientDamperFactor)(VL53L0_DEV Dev, + uint16_t SpadAmbientDamperFactor); + int8_t (*GetSpadAmbientDamperFactor)(VL53L0_DEV Dev, + uint16_t *pSpadAmbientDamperFactor); + int8_t (*PerformRefSpadManagement)(VL53L0_DEV Dev, + uint32_t *refSpadCount, uint8_t *isApertureSpads); + int8_t (*SetReferenceSpads)(VL53L0_DEV Dev, + uint32_t count, uint8_t isApertureSpads); + int8_t (*GetReferenceSpads)(VL53L0_DEV Dev, + uint32_t *pSpadCount, uint8_t *pIsApertureSpads); +}; + +static struct stmvl53l0_api_fn_t stmvl53l0_api_func_tbl = { + .GetVersion = VL53L0_GetVersion, + .GetPalSpecVersion = VL53L0_GetPalSpecVersion, + .GetProductRevision = VL53L0_GetProductRevision, + .GetDeviceInfo = VL53L0_GetDeviceInfo, + .GetDeviceErrorStatus = VL53L0_GetDeviceErrorStatus, + .GetRangeStatusString = VL53L0_GetRangeStatusString, + .GetDeviceErrorString = VL53L0_GetDeviceErrorString, + .GetPalErrorString = VL53L0_GetPalErrorString, + .GetPalState = VL53L0_GetPalState, + .SetPowerMode = VL53L0_SetPowerMode, + .GetPowerMode = VL53L0_GetPowerMode, + .SetOffsetCalibrationDataMicroMeter = + VL53L0_SetOffsetCalibrationDataMicroMeter, + .SetLinearityCorrectiveGain = + VL53L0_SetLinearityCorrectiveGain, + .GetLinearityCorrectiveGain = + VL53L0_GetLinearityCorrectiveGain, + .GetOffsetCalibrationDataMicroMeter = + VL53L0_GetOffsetCalibrationDataMicroMeter, + .SetGroupParamHold = VL53L0_SetGroupParamHold, + .GetUpperLimitMilliMeter = VL53L0_GetUpperLimitMilliMeter, + .SetDeviceAddress = VL53L0_SetDeviceAddress, + .DataInit = VL53L0_DataInit, + .SetTuningSettingBuffer = VL53L0_SetTuningSettingBuffer, + .GetTuningSettingBuffer = VL53L0_GetTuningSettingBuffer, + .StaticInit = VL53L0_StaticInit, + .WaitDeviceBooted = VL53L0_WaitDeviceBooted, + .ResetDevice = VL53L0_ResetDevice, + .SetDeviceParameters = VL53L0_SetDeviceParameters, + .SetDeviceMode = VL53L0_SetDeviceMode, + .GetDeviceMode = VL53L0_GetDeviceMode, + .SetHistogramMode = VL53L0_SetHistogramMode, + .GetHistogramMode = VL53L0_GetHistogramMode, + .SetMeasurementTimingBudgetMicroSeconds = + VL53L0_SetMeasurementTimingBudgetMicroSeconds, + .GetMeasurementTimingBudgetMicroSeconds = + VL53L0_GetMeasurementTimingBudgetMicroSeconds, + .GetVcselPulsePeriod = VL53L0_GetVcselPulsePeriod, + .SetVcselPulsePeriod = VL53L0_SetVcselPulsePeriod, + .SetSequenceStepEnable = VL53L0_SetSequenceStepEnable, + .GetSequenceStepEnable = VL53L0_GetSequenceStepEnable, + .GetSequenceStepEnables = VL53L0_GetSequenceStepEnables, + .SetSequenceStepTimeout = VL53L0_SetSequenceStepTimeout, + .GetSequenceStepTimeout = VL53L0_GetSequenceStepTimeout, + .GetNumberOfSequenceSteps = VL53L0_GetNumberOfSequenceSteps, + .GetSequenceStepsInfo = VL53L0_GetSequenceStepsInfo, + .SetInterMeasurementPeriodMilliSeconds = + VL53L0_SetInterMeasurementPeriodMilliSeconds, + .GetInterMeasurementPeriodMilliSeconds = + VL53L0_GetInterMeasurementPeriodMilliSeconds, + .SetXTalkCompensationEnable = VL53L0_SetXTalkCompensationEnable, + .GetXTalkCompensationEnable = VL53L0_GetXTalkCompensationEnable, + .SetXTalkCompensationRateMegaCps = + VL53L0_SetXTalkCompensationRateMegaCps, + .GetXTalkCompensationRateMegaCps = + VL53L0_GetXTalkCompensationRateMegaCps, + .GetNumberOfLimitCheck = VL53L0_GetNumberOfLimitCheck, + .GetLimitCheckInfo = VL53L0_GetLimitCheckInfo, + .SetLimitCheckEnable = VL53L0_SetLimitCheckEnable, + .GetLimitCheckEnable = VL53L0_GetLimitCheckEnable, + .SetLimitCheckValue = VL53L0_SetLimitCheckValue, + .GetLimitCheckValue = VL53L0_GetLimitCheckValue, + .GetLimitCheckCurrent = VL53L0_GetLimitCheckCurrent, + .SetWrapAroundCheckEnable = VL53L0_SetWrapAroundCheckEnable, + .GetWrapAroundCheckEnable = VL53L0_GetWrapAroundCheckEnable, + .PerformSingleMeasurement = VL53L0_PerformSingleMeasurement, + .PerformRefCalibration = VL53L0_PerformRefCalibration, + .SetRefCalibration = VL53L0_SetRefCalibration, + .GetRefCalibration = VL53L0_GetRefCalibration, + .PerformXTalkCalibration = VL53L0_PerformXTalkCalibration, + .PerformOffsetCalibration = VL53L0_PerformOffsetCalibration, + .StartMeasurement = VL53L0_StartMeasurement, + .StopMeasurement = VL53L0_StopMeasurement, + .GetMeasurementDataReady = VL53L0_GetMeasurementDataReady, + .WaitDeviceReadyForNewMeasurement = + VL53L0_WaitDeviceReadyForNewMeasurement, + .GetRangingMeasurementData = VL53L0_GetRangingMeasurementData, + .GetHistogramMeasurementData = VL53L0_GetHistogramMeasurementData, + .PerformSingleRangingMeasurement = + VL53L0_PerformSingleRangingMeasurement, + .PerformSingleHistogramMeasurement = + VL53L0_PerformSingleHistogramMeasurement, + .SetNumberOfROIZones = VL53L0_SetNumberOfROIZones, + .GetNumberOfROIZones = VL53L0_GetNumberOfROIZones, + .GetMaxNumberOfROIZones = VL53L0_GetMaxNumberOfROIZones, + .SetGpioConfig = VL53L0_SetGpioConfig, + .GetGpioConfig = VL53L0_GetGpioConfig, + .SetInterruptThresholds = VL53L0_SetInterruptThresholds, + .GetInterruptThresholds = VL53L0_GetInterruptThresholds, + .ClearInterruptMask = VL53L0_ClearInterruptMask, + .GetInterruptMaskStatus = VL53L0_GetInterruptMaskStatus, + .EnableInterruptMask = VL53L0_EnableInterruptMask, + .SetSpadAmbientDamperThreshold = VL53L0_SetSpadAmbientDamperThreshold, + .GetSpadAmbientDamperThreshold = VL53L0_GetSpadAmbientDamperThreshold, + .SetSpadAmbientDamperFactor = VL53L0_SetSpadAmbientDamperFactor, + .GetSpadAmbientDamperFactor = VL53L0_GetSpadAmbientDamperFactor, + .PerformRefSpadManagement = VL53L0_PerformRefSpadManagement, + .SetReferenceSpads = VL53L0_SetReferenceSpads, + .GetReferenceSpads = VL53L0_GetReferenceSpads, + +}; +struct stmvl53l0_api_fn_t *papi_func_tbl; + +/* + * IOCTL definitions + */ +#define VL53L0_IOCTL_INIT _IO('p', 0x01) +#define VL53L0_IOCTL_XTALKCALB _IOW('p', 0x02, unsigned int) +#define VL53L0_IOCTL_OFFCALB _IOW('p', 0x03, unsigned int) +#define VL53L0_IOCTL_STOP _IO('p', 0x05) +#define VL53L0_IOCTL_SETXTALK _IOW('p', 0x06, unsigned int) +#define VL53L0_IOCTL_SETOFFSET _IOW('p', 0x07, int8_t) +#define VL53L0_IOCTL_ACTIVATE_USE_CASE _IOW('p', 0x08, uint8_t) +#define VL53L0_IOCTL_ACTIVATE_CUSTOM_USE_CASE \ + _IOW('p', 0x09, struct stmvl53l0_custom_use_case) + +#define VL53L0_IOCTL_GETDATAS \ + _IOR('p', 0x0b, VL53L0_RangingMeasurementData_t) +#define VL53L0_IOCTL_REGISTER \ + _IOWR('p', 0x0c, struct stmvl53l0_register) +#define VL53L0_IOCTL_PARAMETER \ + _IOWR('p', 0x0d, struct stmvl53l0_parameter) + + +/* Mask fields to indicate Offset and Xtalk Comp + * values have been set by application + */ +#define SET_OFFSET_CALIB_DATA_MICROMETER_MASK 0x1 +#define SET_XTALK_COMP_RATE_MCPS_MASK 0x2 + +/* Macros used across different functions */ +#define USE_CASE_LONG_DISTANCE 1 +#define USE_CASE_HIGH_ACCURACY 2 +#define USE_CASE_HIGH_SPEED 3 +#define USE_CASE_CUSTOM 4 + +#define LONG_DISTANCE_TIMING_BUDGET 26000 +#define LONG_DISTANCE_SIGNAL_RATE_LIMIT (65536 / 10) /* 0.1 */ +#define LONG_DISTANCE_SIGMA_LIMIT (60*65536) +#define LONG_DISTANCE_PRE_RANGE_PULSE_PERIOD 18 +#define LONG_DISTANCE_FINAL_RANGE_PULSE_PERIOD 14 + + + +#define HIGH_ACCURACY_TIMING_BUDGET 200000 +#define HIGH_ACCURACY_SIGNAL_RATE_LIMIT (25 * 65536 / 100) /*0.25*/ +#define HIGH_ACCURACY_SIGMA_LIMIT (18*65536) +#define HIGH_ACCURACY_PRE_RANGE_PULSE_PERIOD 14 +#define HIGH_ACCURACY_FINAL_RANGE_PULSE_PERIOD 10 + + + +#define HIGH_SPEED_TIMING_BUDGET 20000 +#define HIGH_SPEED_SIGNAL_RATE_LIMIT (25 * 65536 / 100) /* 0.25 */ +#define HIGH_SPEED_SIGMA_LIMIT (32*65536) +#define HIGH_SPEED_PRE_RANGE_PULSE_PERIOD 14 +#define HIGH_SPEED_FINAL_RANGE_PULSE_PERIOD 10 + + + + + +static long stmvl53l0_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); +/*static int stmvl53l0_flush(struct file *file, fl_owner_t id);*/ +static int stmvl53l0_open(struct inode *inode, struct file *file); +static int stmvl53l0_init_client(struct stmvl53l0_data *data); +static int stmvl53l0_start(struct stmvl53l0_data *data, uint8_t scaling, + init_mode_e mode); +static int stmvl53l0_stop(struct stmvl53l0_data *data); +static int stmvl53l0_config_use_case(struct stmvl53l0_data *data); + +#ifdef DEBUG_TIME_LOG +static void stmvl53l0_DebugTimeGet(struct timeval *ptv) +{ + do_gettimeofday(ptv); +} + +static void stmvl53l0_DebugTimeDuration(struct timeval *pstart_tv, + struct timeval *pstop_tv) +{ + long total_sec, total_msec; + + total_sec = pstop_tv->tv_sec - pstart_tv->tv_sec; + total_msec = (pstop_tv->tv_usec - pstart_tv->tv_usec)/1000; + total_msec += total_sec * 1000; + pr_err("elapsedTime:%ld\n", total_msec); +} +#endif + +static void stmvl53l0_setupAPIFunctions(struct stmvl53l0_data *data) +{ + uint8_t revision = 0; + VL53L0_DEV vl53l0_dev = data; + + /* Read Revision ID */ + VL53L0_RdByte(vl53l0_dev, + VL53L0_REG_IDENTIFICATION_REVISION_ID, + &revision); + vl53l0_errmsg("read REVISION_ID: 0x%x\n", revision); + revision = (revision & 0xF0) >> 4; + if (revision == 1) { + /*cut 1.1*/ + vl53l0_errmsg("to setup API cut 1.1\n"); + papi_func_tbl->GetVersion = VL53L0_GetVersion; + papi_func_tbl->GetPalSpecVersion = VL53L0_GetPalSpecVersion; + papi_func_tbl->GetProductRevision = VL53L0_GetProductRevision; + papi_func_tbl->GetDeviceInfo = VL53L0_GetDeviceInfo; + papi_func_tbl->GetDeviceErrorStatus = + VL53L0_GetDeviceErrorStatus; + papi_func_tbl->GetRangeStatusString = + VL53L0_GetRangeStatusString; + papi_func_tbl->GetDeviceErrorString = + VL53L0_GetDeviceErrorString; + papi_func_tbl->GetPalErrorString = + VL53L0_GetPalErrorString; + papi_func_tbl->GetPalState = + VL53L0_GetPalState; + papi_func_tbl->SetPowerMode = + VL53L0_SetPowerMode; + papi_func_tbl->GetPowerMode = + VL53L0_GetPowerMode; + papi_func_tbl->SetOffsetCalibrationDataMicroMeter = + VL53L0_SetOffsetCalibrationDataMicroMeter; + papi_func_tbl->GetOffsetCalibrationDataMicroMeter = + VL53L0_GetOffsetCalibrationDataMicroMeter; + papi_func_tbl->SetLinearityCorrectiveGain = +VL53L0_SetLinearityCorrectiveGain; + papi_func_tbl->GetLinearityCorrectiveGain = +VL53L0_GetLinearityCorrectiveGain; + papi_func_tbl->SetGroupParamHold = VL53L0_SetGroupParamHold; + papi_func_tbl->GetUpperLimitMilliMeter = + VL53L0_GetUpperLimitMilliMeter; + papi_func_tbl->SetDeviceAddress = + VL53L0_SetDeviceAddress; + papi_func_tbl->DataInit = + VL53L0_DataInit; + papi_func_tbl->SetTuningSettingBuffer = + VL53L0_SetTuningSettingBuffer; + papi_func_tbl->GetTuningSettingBuffer = + VL53L0_GetTuningSettingBuffer; + papi_func_tbl->StaticInit = + VL53L0_StaticInit; + papi_func_tbl->WaitDeviceBooted = + VL53L0_WaitDeviceBooted; + papi_func_tbl->ResetDevice = + VL53L0_ResetDevice; + papi_func_tbl->SetDeviceParameters = + VL53L0_SetDeviceParameters; + papi_func_tbl->SetDeviceMode = VL53L0_SetDeviceMode; + papi_func_tbl->GetDeviceMode = VL53L0_GetDeviceMode; + papi_func_tbl->SetHistogramMode = VL53L0_SetHistogramMode; + papi_func_tbl->GetHistogramMode = VL53L0_GetHistogramMode; + papi_func_tbl->SetMeasurementTimingBudgetMicroSeconds = + VL53L0_SetMeasurementTimingBudgetMicroSeconds; + papi_func_tbl->GetMeasurementTimingBudgetMicroSeconds = + VL53L0_GetMeasurementTimingBudgetMicroSeconds; + papi_func_tbl->GetVcselPulsePeriod = VL53L0_GetVcselPulsePeriod; + papi_func_tbl->SetVcselPulsePeriod = VL53L0_SetVcselPulsePeriod; + papi_func_tbl->SetSequenceStepEnable = + VL53L0_SetSequenceStepEnable; + papi_func_tbl->GetSequenceStepEnable = + VL53L0_GetSequenceStepEnable; + papi_func_tbl->GetSequenceStepEnables = + VL53L0_GetSequenceStepEnables; + papi_func_tbl->SetSequenceStepTimeout = + VL53L0_SetSequenceStepTimeout; + papi_func_tbl->GetSequenceStepTimeout = + VL53L0_GetSequenceStepTimeout; + papi_func_tbl->GetNumberOfSequenceSteps = + VL53L0_GetNumberOfSequenceSteps; + papi_func_tbl->GetSequenceStepsInfo = + VL53L0_GetSequenceStepsInfo; + papi_func_tbl->SetInterMeasurementPeriodMilliSeconds = + VL53L0_SetInterMeasurementPeriodMilliSeconds; + papi_func_tbl->GetInterMeasurementPeriodMilliSeconds = + VL53L0_GetInterMeasurementPeriodMilliSeconds; + papi_func_tbl->SetXTalkCompensationEnable = + VL53L0_SetXTalkCompensationEnable; + papi_func_tbl->GetXTalkCompensationEnable = + VL53L0_GetXTalkCompensationEnable; + papi_func_tbl->SetXTalkCompensationRateMegaCps = + VL53L0_SetXTalkCompensationRateMegaCps; + papi_func_tbl->GetXTalkCompensationRateMegaCps = + VL53L0_GetXTalkCompensationRateMegaCps; + papi_func_tbl->GetNumberOfLimitCheck = + VL53L0_GetNumberOfLimitCheck; + papi_func_tbl->GetLimitCheckInfo = + VL53L0_GetLimitCheckInfo; + papi_func_tbl->SetLimitCheckEnable = + VL53L0_SetLimitCheckEnable; + papi_func_tbl->GetLimitCheckEnable = + VL53L0_GetLimitCheckEnable; + papi_func_tbl->SetLimitCheckValue = + VL53L0_SetLimitCheckValue; + papi_func_tbl->GetLimitCheckValue = + VL53L0_GetLimitCheckValue; + papi_func_tbl->GetLimitCheckCurrent = + VL53L0_GetLimitCheckCurrent; + papi_func_tbl->SetWrapAroundCheckEnable = + VL53L0_SetWrapAroundCheckEnable; + papi_func_tbl->GetWrapAroundCheckEnable = + VL53L0_GetWrapAroundCheckEnable; + papi_func_tbl->PerformSingleMeasurement = + VL53L0_PerformSingleMeasurement; + papi_func_tbl->PerformRefCalibration = + VL53L0_PerformRefCalibration; + papi_func_tbl->SetRefCalibration = + VL53L0_SetRefCalibration; + papi_func_tbl->GetRefCalibration = + VL53L0_GetRefCalibration; + papi_func_tbl->PerformXTalkCalibration = + VL53L0_PerformXTalkCalibration; + papi_func_tbl->PerformOffsetCalibration = + VL53L0_PerformOffsetCalibration; + papi_func_tbl->StartMeasurement = + VL53L0_StartMeasurement; + papi_func_tbl->StopMeasurement = + VL53L0_StopMeasurement; + papi_func_tbl->GetMeasurementDataReady = + VL53L0_GetMeasurementDataReady; + papi_func_tbl->WaitDeviceReadyForNewMeasurement = + VL53L0_WaitDeviceReadyForNewMeasurement; + papi_func_tbl->GetRangingMeasurementData = + VL53L0_GetRangingMeasurementData; + papi_func_tbl->GetHistogramMeasurementData = + VL53L0_GetHistogramMeasurementData; + papi_func_tbl->PerformSingleRangingMeasurement = + VL53L0_PerformSingleRangingMeasurement; + papi_func_tbl->PerformSingleHistogramMeasurement = + VL53L0_PerformSingleHistogramMeasurement; + papi_func_tbl->SetNumberOfROIZones = + VL53L0_SetNumberOfROIZones; + papi_func_tbl->GetNumberOfROIZones = + VL53L0_GetNumberOfROIZones; + papi_func_tbl->GetMaxNumberOfROIZones = + VL53L0_GetMaxNumberOfROIZones; + papi_func_tbl->SetGpioConfig = + VL53L0_SetGpioConfig; + papi_func_tbl->GetGpioConfig = + VL53L0_GetGpioConfig; + papi_func_tbl->SetInterruptThresholds = + VL53L0_SetInterruptThresholds; + papi_func_tbl->GetInterruptThresholds = + VL53L0_GetInterruptThresholds; + papi_func_tbl->ClearInterruptMask = + VL53L0_ClearInterruptMask; + papi_func_tbl->GetInterruptMaskStatus = + VL53L0_GetInterruptMaskStatus; + papi_func_tbl->EnableInterruptMask = VL53L0_EnableInterruptMask; + papi_func_tbl->SetSpadAmbientDamperThreshold = + VL53L0_SetSpadAmbientDamperThreshold; + papi_func_tbl->GetSpadAmbientDamperThreshold = + VL53L0_GetSpadAmbientDamperThreshold; + papi_func_tbl->SetSpadAmbientDamperFactor = + VL53L0_SetSpadAmbientDamperFactor; + papi_func_tbl->GetSpadAmbientDamperFactor = + VL53L0_GetSpadAmbientDamperFactor; + papi_func_tbl->PerformRefSpadManagement = + VL53L0_PerformRefSpadManagement; + papi_func_tbl->SetReferenceSpads = VL53L0_SetReferenceSpads; + papi_func_tbl->GetReferenceSpads = VL53L0_GetReferenceSpads; + } else if (revision == 0) { + /*cut 1.0*/ + vl53l0_errmsg("to setup API cut 1.0\n"); + papi_func_tbl->GetVersion = VL53L010_GetVersion; + papi_func_tbl->GetPalSpecVersion = VL53L010_GetPalSpecVersion; + /* papi_func_tbl->GetProductRevision = NULL;*/ + papi_func_tbl->GetDeviceInfo = VL53L010_GetDeviceInfo; + papi_func_tbl->GetDeviceErrorStatus = + VL53L010_GetDeviceErrorStatus; + papi_func_tbl->GetDeviceErrorString = + VL53L010_GetDeviceErrorString; + papi_func_tbl->GetPalErrorString = + VL53L010_GetPalErrorString; + papi_func_tbl->GetPalState = + VL53L010_GetPalState; + papi_func_tbl->SetPowerMode = + VL53L010_SetPowerMode; + papi_func_tbl->GetPowerMode = + VL53L010_GetPowerMode; + papi_func_tbl->SetOffsetCalibrationDataMicroMeter = + VL53L010_SetOffsetCalibrationDataMicroMeter; + papi_func_tbl->GetOffsetCalibrationDataMicroMeter = + VL53L010_GetOffsetCalibrationDataMicroMeter; + papi_func_tbl->SetGroupParamHold = + VL53L010_SetGroupParamHold; + papi_func_tbl->GetUpperLimitMilliMeter = + VL53L010_GetUpperLimitMilliMeter; + papi_func_tbl->SetDeviceAddress = + VL53L010_SetDeviceAddress; + papi_func_tbl->DataInit = VL53L010_DataInit; + /* + *papi_func_tbl->SetTuningSettingBuffer = NULL; + *papi_func_tbl->GetTuningSettingBuffer = NULL; + */ + papi_func_tbl->StaticInit = VL53L010_StaticInit; + papi_func_tbl->WaitDeviceBooted = VL53L010_WaitDeviceBooted; + papi_func_tbl->ResetDevice = VL53L010_ResetDevice; + papi_func_tbl->SetDeviceParameters = + VL53L010_SetDeviceParameters; + papi_func_tbl->SetDeviceMode = VL53L010_SetDeviceMode; + papi_func_tbl->GetDeviceMode = VL53L010_GetDeviceMode; + papi_func_tbl->SetHistogramMode = VL53L010_SetHistogramMode; + papi_func_tbl->GetHistogramMode = VL53L010_GetHistogramMode; + papi_func_tbl->SetMeasurementTimingBudgetMicroSeconds = + VL53L010_SetMeasurementTimingBudgetMicroSeconds; + papi_func_tbl->GetMeasurementTimingBudgetMicroSeconds = + VL53L010_GetMeasurementTimingBudgetMicroSeconds; + /* + * papi_func_tbl->GetVcselPulsePeriod = NULL; + *papi_func_tbl->SetVcselPulsePeriod = NULL; + *papi_func_tbl->SetSequenceStepEnable = NULL; + *papi_func_tbl->GetSequenceStepEnable = NULL; + *papi_func_tbl->GetSequenceStepEnables = NULL; + *papi_func_tbl->SetSequenceStepTimeout = NULL; + *papi_func_tbl->GetSequenceStepTimeout = NULL; + *papi_func_tbl->GetNumberOfSequenceSteps =NULL; + *papi_func_tbl->GetSequenceStepsInfo = NULL; + */ + papi_func_tbl->SetInterMeasurementPeriodMilliSeconds = + VL53L010_SetInterMeasurementPeriodMilliSeconds; + papi_func_tbl->GetInterMeasurementPeriodMilliSeconds = + VL53L010_GetInterMeasurementPeriodMilliSeconds; + papi_func_tbl->SetXTalkCompensationEnable = + VL53L010_SetXTalkCompensationEnable; + papi_func_tbl->GetXTalkCompensationEnable = + VL53L010_GetXTalkCompensationEnable; + papi_func_tbl->SetXTalkCompensationRateMegaCps = + VL53L010_SetXTalkCompensationRateMegaCps; + papi_func_tbl->GetXTalkCompensationRateMegaCps = + VL53L010_GetXTalkCompensationRateMegaCps; + papi_func_tbl->GetNumberOfLimitCheck = + VL53L010_GetNumberOfLimitCheck; + papi_func_tbl->GetLimitCheckInfo = VL53L010_GetLimitCheckInfo; + papi_func_tbl->SetLimitCheckEnable = + VL53L010_SetLimitCheckEnable; + papi_func_tbl->GetLimitCheckEnable = + VL53L010_GetLimitCheckEnable; + papi_func_tbl->SetLimitCheckValue = + VL53L010_SetLimitCheckValue; + papi_func_tbl->GetLimitCheckValue = + VL53L010_GetLimitCheckValue; + papi_func_tbl->GetLimitCheckCurrent = + VL53L010_GetLimitCheckCurrent; + papi_func_tbl->SetWrapAroundCheckEnable = + VL53L010_SetWrapAroundCheckEnable; + papi_func_tbl->GetWrapAroundCheckEnable = + VL53L010_GetWrapAroundCheckEnable; + papi_func_tbl->PerformSingleMeasurement = + VL53L010_PerformSingleMeasurement; + /*papi_func_tbl->PerformRefCalibration = + * VL53L010_PerformRefCalibration; + */ + papi_func_tbl->PerformRefCalibration = NULL; + papi_func_tbl->SetRefCalibration = NULL; + papi_func_tbl->GetRefCalibration = NULL; + papi_func_tbl->PerformXTalkCalibration = + VL53L010_PerformXTalkCalibration; + papi_func_tbl->PerformOffsetCalibration = + VL53L010_PerformOffsetCalibration; + papi_func_tbl->StartMeasurement = VL53L010_StartMeasurement; + papi_func_tbl->StopMeasurement = VL53L010_StopMeasurement; + papi_func_tbl->GetMeasurementDataReady = + VL53L010_GetMeasurementDataReady; + papi_func_tbl->WaitDeviceReadyForNewMeasurement = + VL53L010_WaitDeviceReadyForNewMeasurement; + papi_func_tbl->GetRangingMeasurementData = + VL53L010_GetRangingMeasurementData; + papi_func_tbl->GetHistogramMeasurementData = + VL53L010_GetHistogramMeasurementData; + papi_func_tbl->PerformSingleRangingMeasurement = + VL53L010_PerformSingleRangingMeasurement; + papi_func_tbl->PerformSingleHistogramMeasurement = + VL53L010_PerformSingleHistogramMeasurement; + papi_func_tbl->SetNumberOfROIZones = + VL53L010_SetNumberOfROIZones; + papi_func_tbl->GetNumberOfROIZones = + VL53L010_GetNumberOfROIZones; + papi_func_tbl->GetMaxNumberOfROIZones = + VL53L010_GetMaxNumberOfROIZones; + papi_func_tbl->SetGpioConfig = VL53L010_SetGpioConfig; + papi_func_tbl->GetGpioConfig = VL53L010_GetGpioConfig; + papi_func_tbl->SetInterruptThresholds = + VL53L010_SetInterruptThresholds; + papi_func_tbl->GetInterruptThresholds = + VL53L010_GetInterruptThresholds; + papi_func_tbl->ClearInterruptMask = + VL53L010_ClearInterruptMask; + papi_func_tbl->GetInterruptMaskStatus = + VL53L010_GetInterruptMaskStatus; + papi_func_tbl->EnableInterruptMask = + VL53L010_EnableInterruptMask; + papi_func_tbl->SetSpadAmbientDamperThreshold = + VL53L010_SetSpadAmbientDamperThreshold; + papi_func_tbl->GetSpadAmbientDamperThreshold = + VL53L010_GetSpadAmbientDamperThreshold; + papi_func_tbl->SetSpadAmbientDamperFactor = + VL53L010_SetSpadAmbientDamperFactor; + papi_func_tbl->GetSpadAmbientDamperFactor = + VL53L010_GetSpadAmbientDamperFactor; + papi_func_tbl->PerformRefSpadManagement = NULL; + papi_func_tbl->SetReferenceSpads = NULL; + papi_func_tbl->GetReferenceSpads = NULL; + } + +} + +static void stmvl53l0_ps_read_measurement(struct stmvl53l0_data *data) +{ + struct timeval tv; + VL53L0_DEV vl53l0_dev = data; + VL53L0_Error Status = VL53L0_ERROR_NONE; + FixPoint1616_t LimitCheckCurrent; + + do_gettimeofday(&tv); + + data->ps_data = data->rangeData.RangeMilliMeter; + input_report_abs(data->input_dev_ps, ABS_DISTANCE, + (int)(data->ps_data + 5) / 10); + input_report_abs(data->input_dev_ps, ABS_HAT0X, tv.tv_sec); + input_report_abs(data->input_dev_ps, ABS_HAT0Y, tv.tv_usec); + input_report_abs(data->input_dev_ps, ABS_HAT1X, + data->rangeData.RangeMilliMeter); + input_report_abs(data->input_dev_ps, ABS_HAT1Y, + data->rangeData.RangeStatus); + input_report_abs(data->input_dev_ps, ABS_HAT2X, + data->rangeData.SignalRateRtnMegaCps); + input_report_abs(data->input_dev_ps, ABS_HAT2Y, + data->rangeData.AmbientRateRtnMegaCps); + input_report_abs(data->input_dev_ps, ABS_HAT3X, + data->rangeData.MeasurementTimeUsec); + input_report_abs(data->input_dev_ps, ABS_HAT3Y, + data->rangeData.RangeDMaxMilliMeter); + Status = papi_func_tbl->GetLimitCheckCurrent(vl53l0_dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + &LimitCheckCurrent); + if (Status == VL53L0_ERROR_NONE) { + input_report_abs(data->input_dev_ps, ABS_WHEEL, + LimitCheckCurrent); + } + input_report_abs(data->input_dev_ps, ABS_PRESSURE, + data->rangeData.EffectiveSpadRtnCount); + input_sync(data->input_dev_ps); + + if (data->enableDebug) + vl53l0_errmsg( +"range:%d, RtnRateMcps:%d,err:0x%x,Dmax:%d,rtnambr:%d,time:%d,Spad:%d,SigmaLimit:%d\n", + data->rangeData.RangeMilliMeter, + data->rangeData.SignalRateRtnMegaCps, + data->rangeData.RangeStatus, + data->rangeData.RangeDMaxMilliMeter, + data->rangeData.AmbientRateRtnMegaCps, + data->rangeData.MeasurementTimeUsec, + data->rangeData.EffectiveSpadRtnCount, + LimitCheckCurrent + ); + + +} + +static void stmvl53l0_cancel_handler(struct stmvl53l0_data *data) +{ + unsigned long flags; + bool ret; + + spin_lock_irqsave(&data->update_lock.wait_lock, flags); + /* + * If work is already scheduled then subsequent schedules will not + * change the scheduled time that's why we have to cancel it first. + */ + ret = cancel_delayed_work(&data->dwork); + if (ret == 0) + vl53l0_errmsg("cancel_delayed_work return FALSE\n"); + + spin_unlock_irqrestore(&data->update_lock.wait_lock, flags); + +} + +void stmvl53l0_schedule_handler(struct stmvl53l0_data *data) +{ + unsigned long flags; + + spin_lock_irqsave(&data->update_lock.wait_lock, flags); + /* + * If work is already scheduled then subsequent schedules will not + * change the scheduled time that's why we have to cancel it first. + */ + cancel_delayed_work(&data->dwork); + schedule_delayed_work(&data->dwork, 0); + spin_unlock_irqrestore(&data->update_lock.wait_lock, flags); + +} + + +#ifdef USE_INT +static irqreturn_t stmvl53l0_interrupt_handler(int vec, void *info) +{ + + struct stmvl53l0_data *data = (struct stmvl53l0_data *)info; + + if (data->irq == vec) { + data->interrupt_received = 1; + schedule_delayed_work(&data->dwork, 0); + } + return IRQ_HANDLED; +} +#else +/* Flag used to exit the thread when kthread_stop() is invoked */ +static int poll_thread_exit; +int stmvl53l0_poll_thread(void *data) +{ + VL53L0_DEV vl53l0_dev = data; + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint32_t sleep_time = 0; + uint32_t interruptStatus = 0; + + pr_err("%s(%d) : Starting Polling thread\n", __func__, __LINE__); + + while (!kthread_should_stop()) { + /* Check if enable_ps_sensor is true or exit request is made. + * If not block + */ + wait_event(vl53l0_dev->poll_thread_wq, + (vl53l0_dev->enable_ps_sensor || poll_thread_exit)); + if (poll_thread_exit) { + pr_err( + "%s(%d) : Exiting the poll thread\n", __func__, __LINE__); + break; + } + + mutex_lock(&vl53l0_dev->work_mutex); + + sleep_time = vl53l0_dev->delay_ms; + Status = VL53L0_GetInterruptMaskStatus(vl53l0_dev, + &interruptStatus); + if (Status == VL53L0_ERROR_NONE && + interruptStatus && + interruptStatus != vl53l0_dev->interruptStatus) { + vl53l0_dev->interruptStatus = interruptStatus; + vl53l0_dev->noInterruptCount = 0; + stmvl53l0_schedule_handler(vl53l0_dev); + } else { + vl53l0_dev->noInterruptCount++; + } + + /*Force Clear interrupt mask and restart if + *no interrupt after twice the timingBudget + */ + if ((vl53l0_dev->noInterruptCount * vl53l0_dev->delay_ms) > + (vl53l0_dev->timingBudget * 2)) { + pr_err("No interrupt after (%u) msec(TimingBudget = %u) . Clear Interrupt Mask and restart\n", + (vl53l0_dev->noInterruptCount * + vl53l0_dev->delay_ms), + vl53l0_dev->timingBudget); + Status = papi_func_tbl->ClearInterruptMask(vl53l0_dev, + 0); + if (vl53l0_dev->deviceMode == + VL53L0_DEVICEMODE_SINGLE_RANGING) { + Status = papi_func_tbl->StartMeasurement( + vl53l0_dev); + if (Status != VL53L0_ERROR_NONE) { + pr_err("%s(%d) : Status = %d\n", + __func__, __LINE__, Status); + } + } + } + mutex_unlock(&vl53l0_dev->work_mutex); + /* Sleep for delay_ms milliseconds */ + msleep(sleep_time); + } + + return 0; +} +#endif + +/* work handler */ +static void stmvl53l0_work_handler(struct work_struct *work) +{ + struct stmvl53l0_data *data = container_of(work, struct stmvl53l0_data, + dwork.work); + VL53L0_DEV vl53l0_dev = data; + + VL53L0_Error Status = VL53L0_ERROR_NONE; + + mutex_lock(&data->work_mutex); + /* vl53l0_dbgmsg("Enter\n"); */ + + if (pmodule_func_tbl->query_power_status(data->client_object) == 0) { + if (data->enable_ps_sensor == 1) { + stmvl53l0_stop(data); + data->enable_ps_sensor = 0; + } + } + + if (vl53l0_dev->enable_ps_sensor == 1) { +#ifdef DEBUG_TIME_LOG + stmvl53l0_DebugTimeGet(&stop_tv); + stmvl53l0_DebugTimeDuration(&start_tv, &stop_tv); +#endif + /* Check if ISR has scheduled this function */ + if (vl53l0_dev->interrupt_received == 1) { + Status = papi_func_tbl->GetInterruptMaskStatus( + vl53l0_dev, + &vl53l0_dev->interruptStatus); + if (Status != VL53L0_ERROR_NONE) + pr_err("%s(%d) : Status = %d\n", + __func__, __LINE__, Status); + vl53l0_dev->interrupt_received = 0; + } + if (data->enableDebug) + pr_err("interruptStatus:0x%x, interrupt_received:%d\n", + vl53l0_dev->interruptStatus, + vl53l0_dev->interrupt_received); + + if (vl53l0_dev->interruptStatus == vl53l0_dev->gpio_function) { + Status = + papi_func_tbl->GetRangingMeasurementData( + vl53l0_dev, + &(data->rangeData)); + /* to push the measurement */ + if (Status == VL53L0_ERROR_NONE) + stmvl53l0_ps_read_measurement(data); + else + pr_err("%s(%d) : Status = %d\n", + __func__, __LINE__, Status); + + if (data->enableDebug) + pr_err("Measured range:%d\n", + data->rangeData.RangeMilliMeter); + + Status = papi_func_tbl->ClearInterruptMask( + vl53l0_dev, 0); + if (Status != VL53L0_ERROR_NONE) { + pr_err("%s(%d) : Status = %d\n", + __func__, __LINE__, Status); + } + + if (data->deviceMode == + VL53L0_DEVICEMODE_SINGLE_RANGING) { + /* Before restarting measurement + * check if use case needs to be changed + */ + + if (data->updateUseCase) { + Status = + stmvl53l0_config_use_case(data); + if (Status != + VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "Failed to configure Use case = %u\n", + vl53l0_dev->useCase); + } else { + data->updateUseCase = 0; + } + } + Status = + papi_func_tbl->StartMeasurement( + vl53l0_dev); + } + } +#ifdef DEBUG_TIME_LOG + stmvl53l0_DebugTimeGet(&start_tv); +#endif + + } + + + vl53l0_dev->interruptStatus = 0; + + mutex_unlock(&data->work_mutex); + +} + + +/* + * SysFS support + */ +static ssize_t stmvl53l0_show_enable_ps_sensor(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 5, "%d\n", data->enable_ps_sensor); +} + +static ssize_t stmvl53l0_store_enable_ps_sensor(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + unsigned long val = 0; + + int ret = kstrtoul(buf, 10, &val); + + if (ret != 0) + return ret; + + if ((val != 0) && (val != 1)) { + vl53l0_errmsg("store unvalid value = %lu\n", val); + return count; + } + mutex_lock(&data->work_mutex); + vl53l0_dbgmsg("Enter, enable_ps_sensor flag:%d\n", + data->enable_ps_sensor); + vl53l0_dbgmsg("enable ps senosr ( %ld)\n", val); + + if (val == 1) { + /* turn on tof sensor */ + if (data->enable_ps_sensor == 0) { + /* to start */ + stmvl53l0_start(data, 3, NORMAL_MODE); + } else { + vl53l0_errmsg("Already enabled. Skip !"); + } + } else { + /* turn off tof sensor */ + if (data->enable_ps_sensor == 1) { + data->enable_ps_sensor = 0; + /* to stop */ + stmvl53l0_stop(data); + } + } + vl53l0_dbgmsg("End\n"); + mutex_unlock(&data->work_mutex); + + return count; +} + +static DEVICE_ATTR(enable_ps_sensor, 0664/*S_IWUGO | S_IRUGO*/, + stmvl53l0_show_enable_ps_sensor, + stmvl53l0_store_enable_ps_sensor); + +static ssize_t stmvl53l0_show_enable_debug(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 5, "%d\n", data->enableDebug); +} + +/* for debug */ +static ssize_t stmvl53l0_store_enable_debug(struct device *dev, + struct device_attribute *attr, const + char *buf, size_t count) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + unsigned long on = 0; + + int ret = kstrtoul(buf, 10, &on); + + if (ret != 0) + return ret; + + if ((on != 0) && (on != 1)) { + vl53l0_errmsg("set debug=%lu\n", on); + return count; + } + data->enableDebug = on; + + return count; +} + +/* DEVICE_ATTR(name,mode,show,store) */ +static DEVICE_ATTR(enable_debug, 0660/*S_IWUSR | S_IRUGO*/, + stmvl53l0_show_enable_debug, + stmvl53l0_store_enable_debug); + +static ssize_t stmvl53l0_show_set_delay_ms(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 5, "%d\n", data->delay_ms); +} + +/* for work handler scheduler time */ +static ssize_t stmvl53l0_store_set_delay_ms(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + unsigned long delay_ms = 0; + + int ret = kstrtoul(buf, 10, &delay_ms); + + if (ret != 0) + return ret; + if (delay_ms == 0) { + vl53l0_errmsg("set delay_ms=%lu\n", delay_ms); + return count; + } + mutex_lock(&data->work_mutex); + data->delay_ms = delay_ms; + mutex_unlock(&data->work_mutex); + + return count; +} + +/* DEVICE_ATTR(name,mode,show,store) */ +static DEVICE_ATTR(set_delay_ms, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l0_show_set_delay_ms, + stmvl53l0_store_set_delay_ms); + +/* Timing Budget */ +static ssize_t stmvl53l0_show_timing_budget(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 10, "%d\n", data->timingBudget); +} + +static ssize_t stmvl53l0_store_set_timing_budget(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + unsigned long timingBudget = 0; + int ret = kstrtoul(buf, 10, &timingBudget); + + if (ret != 0) + return ret; + if (timingBudget == 0) { + vl53l0_errmsg("set timingBudget=%lu\n", timingBudget); + return count; + } + mutex_lock(&data->work_mutex); + data->timingBudget = timingBudget; + mutex_unlock(&data->work_mutex); + + return count; +} + +/* DEVICE_ATTR(name,mode,show,store) */ +static DEVICE_ATTR(set_timing_budget, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l0_show_timing_budget, + stmvl53l0_store_set_timing_budget); + + +/* Use case */ +static ssize_t stmvl53l0_show_use_case(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + + switch (data->useCase) { + case USE_CASE_LONG_DISTANCE: + return snprintf(buf, 20, "Long Distance\n"); + case USE_CASE_HIGH_ACCURACY: + return snprintf(buf, 20, "High Accuracy\n"); + case USE_CASE_HIGH_SPEED: + return snprintf(buf, 20, "High Speed\n"); + default: + break; + } + + return snprintf(buf, 25, "Unknown use case\n"); +} + +static ssize_t stmvl53l0_store_set_use_case(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + unsigned long useCase = 0; + int ret = kstrtoul(buf, 10, &useCase); + + if (ret != 0) + return ret; + + + mutex_lock(&data->work_mutex); + + if (useCase == USE_CASE_LONG_DISTANCE) { + data->timingBudget = LONG_DISTANCE_TIMING_BUDGET; + } else if (useCase == USE_CASE_HIGH_SPEED) { + data->timingBudget = HIGH_SPEED_TIMING_BUDGET; + } else if (useCase == USE_CASE_HIGH_ACCURACY) { + data->timingBudget = HIGH_ACCURACY_TIMING_BUDGET; + } else { + count = -EINVAL; + mutex_unlock(&data->work_mutex); + return count; + } + + data->useCase = useCase; + mutex_unlock(&data->work_mutex); + + return count; +} + +/* DEVICE_ATTR(name,mode,show,store) */ +static DEVICE_ATTR(set_use_case, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l0_show_use_case, + stmvl53l0_store_set_use_case); + + +/* Get Current configuration info */ +static ssize_t stmvl53l0_show_current_configuration(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l0_data *vl53l0_dev = dev_get_drvdata(dev); + VL53L0_Error Status = VL53L0_ERROR_NONE; + int ret = -1; + FixPoint1616_t LimitValue = 0; + uint8_t LimitEnable = 0; + uint32_t refSpadCount = 0; + uint8_t isApertureSpads = 0; + uint8_t VhvSettings = 0; + uint8_t PhaseCal = 0; + uint8_t pulsePeriod = 0; + uint32_t timingBudget = 0; + int32_t offsetCalibrationDataMicroMeter = -1; + uint8_t XTalkCompensationEnable = 0; + FixPoint1616_t xTalkCompensationRateMegaCps = 0; + + + ret = scnprintf(buf, PAGE_SIZE, "VL53L0 current configuration:\n"); + + mutex_lock(&vl53l0_dev->work_mutex); + pr_err("Driver Config:UseCase:%d, offsetCalDistance:%u,xtalkCalDistance:%u,setCalibratedValue:0x%X\n", + vl53l0_dev->useCase, vl53l0_dev->offsetCalDistance, + vl53l0_dev->xtalkCalDistance, + vl53l0_dev->setCalibratedValue); + + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "Driver Config:UseCase:%d, offsetCalDistance:%u,xtalkCalDistance:%u,setCalibratedValue:0x%X\n", + vl53l0_dev->useCase, + vl53l0_dev->offsetCalDistance, + vl53l0_dev->xtalkCalDistance, + vl53l0_dev->setCalibratedValue); + + if (vl53l0_dev->useCase == USE_CASE_CUSTOM) { + pr_err("CustomUseCase : Sigma=%u :Signal=%u: Pre=%u :Final=%u\n", + vl53l0_dev->sigmaLimit, + vl53l0_dev->signalRateLimit, + vl53l0_dev->preRangePulsePeriod, + vl53l0_dev->finalRangePulsePeriod); + + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "CustomUseCase : Sigma=%u :Signal=%u: Pre=%u :Final=%u\n", + vl53l0_dev->sigmaLimit, + vl53l0_dev->signalRateLimit, + vl53l0_dev->preRangePulsePeriod, + vl53l0_dev->finalRangePulsePeriod); + } + + + Status = papi_func_tbl->GetLimitCheckValue(vl53l0_dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + &LimitValue); + + if (Status == VL53L0_ERROR_NONE) { + Status = papi_func_tbl->GetLimitCheckEnable(vl53l0_dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + &LimitEnable); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("Get LimitCheckValue SIGMA_FINAL_RANGE as:%d,Enable:%d\n", + (LimitValue>>16), LimitEnable); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "Sigma Limit:%u, Enable:%u\n", + (LimitValue>>16), + LimitEnable); + Status = papi_func_tbl->GetLimitCheckValue(vl53l0_dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &LimitValue); + Status = papi_func_tbl->GetLimitCheckEnable(vl53l0_dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &LimitEnable); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("Get LimitCheckValue SIGNAL_FINAL_RANGE as:%d(Fix1616),Enable:%d\n", + (LimitValue), LimitEnable); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "SIGNAL Limit:%u, Enable:%u\n", + LimitValue, + LimitEnable); + + Status = papi_func_tbl->GetLimitCheckValue(vl53l0_dev, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, + &LimitValue); + Status = papi_func_tbl->GetLimitCheckEnable(vl53l0_dev, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, + &LimitEnable); + + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("Get LimitCheckValue SIGNAL_REF_CLIP as:%d(fix1616),Enable:%d\n", + (LimitValue), LimitEnable); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "RefClipLimit:%u, Enable:%u\n", + LimitValue, + LimitEnable); + + Status = papi_func_tbl->GetLimitCheckValue(vl53l0_dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + &LimitValue); + Status = papi_func_tbl->GetLimitCheckEnable(vl53l0_dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + &LimitEnable); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err( + "Get LimitCheckValue RANGE_IGNORE_THRESHOLDas:%d(fix1616),Enable:%d\n", + (LimitValue), + LimitEnable); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "RngIgnoreThresh:%u, Enable:%u\n", + LimitValue, + LimitEnable); + + Status = papi_func_tbl->GetRefCalibration(vl53l0_dev, + &VhvSettings, &PhaseCal); /* Ref calibration */ + + + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("GetRefCalibration - Vhv = %u, PhaseCal = %u\n", + VhvSettings, PhaseCal); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "Vhv:%u, PhCal:%u\n", + VhvSettings, + PhaseCal); + + /* Ref Spad Management */ + Status = papi_func_tbl->GetReferenceSpads(vl53l0_dev, + &refSpadCount, &isApertureSpads); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("GetSpads - Count = %u, IsAperture = %u\n", + refSpadCount, isApertureSpads); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "SpadCount:%u, IsAperture:%u\n", + refSpadCount, + isApertureSpads); + + Status = papi_func_tbl->GetMeasurementTimingBudgetMicroSeconds( + vl53l0_dev, + &timingBudget); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("TimingBudget = %u\n", timingBudget); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "TimBudget:%u\n", + timingBudget); + Status = papi_func_tbl->GetVcselPulsePeriod(vl53l0_dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &pulsePeriod); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("GetVcselPulsePeriod - PRE_RANGE = %u\n", + pulsePeriod); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "PulsePreRange:%u\n", + pulsePeriod); + + Status = papi_func_tbl->GetVcselPulsePeriod(vl53l0_dev, + VL53L0_VCSEL_PERIOD_FINAL_RANGE, + &pulsePeriod); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("GetVcselPulsePeriod - FINAL_RANGE = %u\n", + pulsePeriod); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "PulseFinalRange:%u\n", + pulsePeriod); + + Status = papi_func_tbl->GetOffsetCalibrationDataMicroMeter( + vl53l0_dev, + &offsetCalibrationDataMicroMeter); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("OffsetCalibrationDataMicroMeter = %d\n", + offsetCalibrationDataMicroMeter); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "Offset:%d\n", + offsetCalibrationDataMicroMeter); + + Status = papi_func_tbl->GetXTalkCompensationEnable(vl53l0_dev, + &XTalkCompensationEnable); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("Xtalk Enable = %u\n", XTalkCompensationEnable); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "XtalkEnable:%u\n", + XTalkCompensationEnable); + + Status = papi_func_tbl->GetXTalkCompensationRateMegaCps( + vl53l0_dev, + &xTalkCompensationRateMegaCps); + } + + + if (Status == VL53L0_ERROR_NONE) { + pr_err("XtalkComp MCPS = %u\n", xTalkCompensationRateMegaCps); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "XtalkMcps:%u\n", + xTalkCompensationRateMegaCps); + + } else { + pr_err("Error = %d\n", Status); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "Error:%d\n", + Status); + + } + + pr_err("Total Bytes returned = %d\n", ret); + + mutex_unlock(&vl53l0_dev->work_mutex); + + + return ret; +} + +/* DEVICE_ATTR(name,mode,show,store) */ +static DEVICE_ATTR(show_current_configuration, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l0_show_current_configuration, + NULL); +/* for work handler scheduler time */ +static ssize_t stmvl53l0_do_flush(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + + int ret = 0; + /* Revisit : Avoid lock if it takes too long time */ + mutex_lock(&data->work_mutex); + + vl53l0_dbgmsg("Starting timer to fire in 1ms (%ld)\n", jiffies); + ret = mod_timer(&data->timer, jiffies + msecs_to_jiffies(1)); + if (ret) + pr_err("Error from mod_timer = %d\n", ret); + + mutex_unlock(&data->work_mutex); + return count; +} + +/* DEVICE_ATTR(name,mode,show,store) */ +static DEVICE_ATTR(do_flush, 0660/*S_IWUGO | S_IRUGO*/, + NULL, + stmvl53l0_do_flush); +static struct attribute *stmvl53l0_attributes[] = { + &dev_attr_enable_ps_sensor.attr, + &dev_attr_enable_debug.attr, + &dev_attr_set_delay_ms.attr, + &dev_attr_set_timing_budget.attr, + &dev_attr_set_use_case.attr, + &dev_attr_do_flush.attr, + &dev_attr_show_current_configuration.attr, + NULL +}; + + +static const struct attribute_group stmvl53l0_attr_group = { + .attrs = stmvl53l0_attributes, +}; + +/* + * misc device file operation functions + */ +static int stmvl53l0_ioctl_handler(struct file *file, + unsigned int cmd, unsigned long arg, + void __user *p) +{ + int rc = 0; + unsigned int xtalkint = 0; + unsigned int targetDistance = 0; + int8_t offsetint = 0; + uint8_t useCase = 0; + struct stmvl53l0_custom_use_case customUseCase; + struct stmvl53l0_data *data = + container_of(file->private_data, + struct stmvl53l0_data, miscdev); + struct stmvl53l0_register reg; + struct stmvl53l0_parameter parameter; + VL53L0_DEV vl53l0_dev = data; + VL53L0_DeviceModes deviceMode; + uint8_t page_num = 0; + VL53L0_Error Status = VL53L0_ERROR_NONE; + + if (!data) + return -EINVAL; + + vl53l0_dbgmsg("Enter enable_ps_sensor:%d\n", data->enable_ps_sensor); + switch (cmd) { + /* enable */ + case VL53L0_IOCTL_INIT: + vl53l0_dbgmsg("VL53L0_IOCTL_INIT\n"); + /* turn on tof sensor only if it's not enabled by other + * client + */ + if (data->enable_ps_sensor == 0) { + /* to start */ + stmvl53l0_start(data, 3, NORMAL_MODE); + } else + rc = -EINVAL; + break; + /* crosstalk calibration */ + case VL53L0_IOCTL_XTALKCALB: + vl53l0_dbgmsg("VL53L0_IOCTL_XTALKCALB\n"); + data->xtalkCalDistance = 100; + if (copy_from_user(&targetDistance, (unsigned int *)p, + sizeof(unsigned int))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + data->xtalkCalDistance = targetDistance; + + /* turn on tof sensor only if it's not enabled by other + * client + */ + if (data->enable_ps_sensor == 0) { + /* to start */ + stmvl53l0_start(data, 3, XTALKCALIB_MODE); + } else + rc = -EINVAL; + break; + /* set up Xtalk value */ + case VL53L0_IOCTL_SETXTALK: + vl53l0_dbgmsg("VL53L0_IOCTL_SETXTALK\n"); + if (copy_from_user(&xtalkint, (unsigned int *)p, + sizeof(unsigned int))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + vl53l0_dbgmsg("SETXTALK as 0x%x\n", xtalkint); + + /* later + * SetXTalkCompensationRate(vl53l0_dev, xtalkint); + */ + break; + /* offset calibration */ + case VL53L0_IOCTL_OFFCALB: + vl53l0_dbgmsg("VL53L0_IOCTL_OFFCALB\n"); + data->offsetCalDistance = 50; + if (copy_from_user(&targetDistance, (unsigned int *)p, + sizeof(unsigned int))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + data->offsetCalDistance = targetDistance; + if (data->enable_ps_sensor == 0) { + /* to start */ + stmvl53l0_start(data, 3, OFFSETCALIB_MODE); + } else + rc = -EINVAL; + break; + /* set up offset value */ + case VL53L0_IOCTL_SETOFFSET: + vl53l0_dbgmsg("VL53L0_IOCTL_SETOFFSET\n"); + if (copy_from_user(&offsetint, (int8_t *)p, sizeof(int8_t))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + vl53l0_dbgmsg("SETOFFSET as %d\n", offsetint); + + /* later + * SetOffsetCalibrationData(vl53l0_dev, offsetint); + */ + break; + + /* Config use case */ + case VL53L0_IOCTL_ACTIVATE_USE_CASE: + vl53l0_dbgmsg("VL53L0_IOCTL_ACTIVATE_USE_CASE\n"); + if (copy_from_user(&useCase, (uint8_t *)p, sizeof(uint8_t))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + + /* Validate the user passed use case. + * Update the timingBudget value. The other + * parameters are updated approrpiately in config_use_case() + * Currently the timing budget can be updated through + * sysfs entry, and this needs additional steps to manage. + */ + switch (useCase) { + case USE_CASE_LONG_DISTANCE: + data->timingBudget = LONG_DISTANCE_TIMING_BUDGET; + break; + + case USE_CASE_HIGH_ACCURACY: + data->timingBudget = HIGH_ACCURACY_TIMING_BUDGET; + break; + + case USE_CASE_HIGH_SPEED: + data->timingBudget = HIGH_SPEED_TIMING_BUDGET; + break; + + default: + vl53l0_errmsg("%d, Unknown Use case = %u\n", __LINE__, + useCase); + return -EFAULT; + } + vl53l0_dbgmsg("useCase as %d\n", useCase); + /* record the use case */ + data->useCase = useCase; + + /* If ranging is in progress, let the work handler + * update the use case + */ + if (data->enable_ps_sensor) + data->updateUseCase = 1; + break; + + /* Config Custom use case */ + case VL53L0_IOCTL_ACTIVATE_CUSTOM_USE_CASE: + vl53l0_dbgmsg("VL53L0_IOCTL_ACTIVATE_CUSTOM_USE_CASE\n"); + if (copy_from_user(&customUseCase, + (struct stmvl53l0_custom_use_case *)p, + sizeof(struct stmvl53l0_custom_use_case))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + + data->sigmaLimit = customUseCase.sigmaLimit; + data->signalRateLimit = customUseCase.signalRateLimit; + data->preRangePulsePeriod = customUseCase.preRangePulsePeriod; + data->finalRangePulsePeriod = + customUseCase.finalRangePulsePeriod; + data->timingBudget = customUseCase.timingBudget; + vl53l0_dbgmsg( + "Sigma=%u,Signal=%u,Pre=%u,Final=%u,timingBudget=%u\n", + data->sigmaLimit, + data->signalRateLimit, + data->preRangePulsePeriod, + data->finalRangePulsePeriod, + data->timingBudget); + + /* record the use case */ + data->useCase = USE_CASE_CUSTOM; + /* If ranging is in progress, + * let the work handler update the use case + */ + if (data->enable_ps_sensor) + data->updateUseCase = 1; + + break; + + + /* disable */ + case VL53L0_IOCTL_STOP: + vl53l0_dbgmsg("VL53L0_IOCTL_STOP\n"); + /* turn off tof sensor only if it's enabled by other client */ + if (data->enable_ps_sensor == 1) { + data->enable_ps_sensor = 0; + /* to stop */ + stmvl53l0_stop(data); + } + break; + /* Get all range data */ + case VL53L0_IOCTL_GETDATAS: + vl53l0_dbgmsg("VL53L0_IOCTL_GETDATAS\n"); + if (copy_to_user((VL53L0_RangingMeasurementData_t *)p, + &(data->rangeData), + sizeof(VL53L0_RangingMeasurementData_t))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + break; + /* Register tool */ + case VL53L0_IOCTL_REGISTER: + vl53l0_dbgmsg("VL53L0_IOCTL_REGISTER\n"); + if (copy_from_user(®, (struct stmvl53l0_register *)p, + sizeof(struct stmvl53l0_register))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + reg.status = 0; + page_num = (uint8_t)((reg.reg_index & 0x0000ff00) >> 8); + vl53l0_dbgmsg( +"VL53L0_IOCTL_REGISTER, page number:%d\n", page_num); + if (page_num != 0) + reg.status = VL53L0_WrByte(vl53l0_dev, 0xFF, page_num); + + switch (reg.reg_bytes) { + case(4): + if (reg.is_read) + reg.status = VL53L0_RdDWord(vl53l0_dev, + (uint8_t)reg.reg_index, + ®.reg_data); + else + reg.status = VL53L0_WrDWord(vl53l0_dev, + (uint8_t)reg.reg_index, + reg.reg_data); + break; + case(2): + if (reg.is_read) + reg.status = VL53L0_RdWord(vl53l0_dev, + (uint8_t)reg.reg_index, + (uint16_t *)®.reg_data); + else + reg.status = VL53L0_WrWord(vl53l0_dev, + (uint8_t)reg.reg_index, + (uint16_t)reg.reg_data); + break; + case(1): + if (reg.is_read) + reg.status = VL53L0_RdByte(vl53l0_dev, + (uint8_t)reg.reg_index, + (uint8_t *)®.reg_data); + else + reg.status = VL53L0_WrByte(vl53l0_dev, + (uint8_t)reg.reg_index, + (uint8_t)reg.reg_data); + break; + default: + reg.status = -1; + + } + if (page_num != 0) + reg.status = VL53L0_WrByte(vl53l0_dev, 0xFF, 0); + + + if (copy_to_user((struct stmvl53l0_register *)p, ®, + sizeof(struct stmvl53l0_register))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + break; + /* parameter access */ + case VL53L0_IOCTL_PARAMETER: + vl53l0_dbgmsg("VL53L0_IOCTL_PARAMETER\n"); + if (copy_from_user(¶meter, (struct stmvl53l0_parameter *)p, + sizeof(struct stmvl53l0_parameter))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + parameter.status = 0; + if (data->enableDebug) + vl53l0_dbgmsg( + "VL53L0_IOCTL_PARAMETER Name = %d\n", parameter.name); + switch (parameter.name) { + case (OFFSET_PAR): + if (parameter.is_read) + parameter.status = + papi_func_tbl->GetOffsetCalibrationDataMicroMeter( + vl53l0_dev, ¶meter.value); + else { + parameter.status = + papi_func_tbl->SetOffsetCalibrationDataMicroMeter( + vl53l0_dev, parameter.value); + data->OffsetMicroMeter = parameter.value; + data->setCalibratedValue + |= SET_OFFSET_CALIB_DATA_MICROMETER_MASK; + + } + vl53l0_dbgmsg("get parameter value as %d\n", + parameter.value); + break; + + case (REFERENCESPADS_PAR): + if (parameter.is_read) { + parameter.status = + papi_func_tbl->GetReferenceSpads(vl53l0_dev, + (uint32_t *)&(parameter.value), + (uint8_t *)&(parameter.value2)); + if (data->enableDebug) + vl53l0_dbgmsg( + "Get RefSpad : Count:%u, Type:%u\n", + parameter.value, + (uint8_t)parameter.value2); + } else { + if (data->enableDebug) + vl53l0_dbgmsg( + "Set RefSpad : Count:%u, Type:%u\n", + parameter.value, + (uint8_t)parameter.value2); + + parameter.status = + papi_func_tbl->SetReferenceSpads(vl53l0_dev, + (uint32_t)(parameter.value), + (uint8_t)(parameter.value2)); + + data->refSpadCount = parameter.value; + data->isApertureSpads = + (uint8_t)(parameter.value2); + } + break; + + case (REFCALIBRATION_PAR): + if (parameter.is_read) { + parameter.status = + papi_func_tbl->GetRefCalibration(vl53l0_dev, + (uint8_t *)&(parameter.value), + (uint8_t *)&(parameter.value2)); + if (data->enableDebug) + vl53l0_dbgmsg( + "Get Ref : Vhv:%u, PhaseCal:%u\n", + (uint8_t)parameter.value, + (uint8_t)parameter.value2); + } else { + if (data->enableDebug) + vl53l0_dbgmsg( + "Set Ref : Vhv:%u, PhaseCal:%u\n", + (uint8_t)parameter.value, + (uint8_t)parameter.value2); + parameter.status = + papi_func_tbl->SetRefCalibration( + vl53l0_dev, + (uint8_t)(parameter.value), + (uint8_t)(parameter.value2)); + data->VhvSettings = (uint8_t)parameter.value; + data->PhaseCal = (uint8_t)(parameter.value2); + } + break; + case (XTALKRATE_PAR): + if (parameter.is_read) + parameter.status = + papi_func_tbl->GetXTalkCompensationRateMegaCps( + vl53l0_dev, + (FixPoint1616_t *) + ¶meter.value); + else { + /* Range Ignore Threshold value */ + FixPoint1616_t ritValue = 0; + + parameter.status = + papi_func_tbl->SetXTalkCompensationRateMegaCps( + vl53l0_dev, + (FixPoint1616_t) + parameter.value); + data->XTalkCompensationRateMegaCps = + parameter.value; + data->setCalibratedValue |= + SET_XTALK_COMP_RATE_MCPS_MASK; + + + /*0.7 KCps converted to MCps */ + if (data->XTalkCompensationRateMegaCps < + 7*65536/10000) { + ritValue = 15 * 7 * 65536/100000; + } else { + ritValue = 15 * + vl53l0_dev->XTalkCompensationRateMegaCps + /10; + } + + if (papi_func_tbl->SetLimitCheckEnable + != NULL) { + Status = + papi_func_tbl->SetLimitCheckEnable( + vl53l0_dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + 1); + } + + if ((Status == VL53L0_ERROR_NONE) && + (papi_func_tbl->SetLimitCheckValue + != NULL)) { + vl53l0_dbgmsg( + "Set RIT - %u\n", ritValue); + Status = + papi_func_tbl->SetLimitCheckValue( + vl53l0_dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + ritValue); + } + + } + break; + case (XTALKENABLE_PAR): + if (parameter.is_read) + parameter.status = + papi_func_tbl->GetXTalkCompensationEnable( + vl53l0_dev, + (uint8_t *) ¶meter.value); + else + parameter.status = + papi_func_tbl->SetXTalkCompensationEnable( + vl53l0_dev, + (uint8_t) parameter.value); + break; + case (GPIOFUNC_PAR): + if (parameter.is_read) { + parameter.status = + papi_func_tbl->GetGpioConfig( + vl53l0_dev, + 0, + &deviceMode, + &data->gpio_function, + &data->gpio_polarity); + parameter.value = + data->gpio_function; + } else { + data->gpio_function = parameter.value; + parameter.status = + papi_func_tbl->SetGpioConfig( + vl53l0_dev, + 0, + 0, + data->gpio_function, + data->gpio_polarity); + } + break; + case (LOWTHRESH_PAR): + if (parameter.is_read) { + parameter.status = + papi_func_tbl->GetInterruptThresholds( + vl53l0_dev, + 0, + &(data->low_threshold), + &(data->high_threshold)); + parameter.value = + data->low_threshold >> 16; + } else { + data->low_threshold = parameter.value << 16; + parameter.status = + papi_func_tbl->SetInterruptThresholds( + vl53l0_dev, + 0, + data->low_threshold, + data->high_threshold); + } + break; + case (HIGHTHRESH_PAR): + if (parameter.is_read) { + parameter.status = + papi_func_tbl->GetInterruptThresholds( + vl53l0_dev, + 0, + &(data->low_threshold), + &(data->high_threshold)); + parameter.value = + data->high_threshold >> 16; + } else { + data->high_threshold = + parameter.value << 16; + parameter.status = + papi_func_tbl->SetInterruptThresholds( + vl53l0_dev, + 0, + data->low_threshold, + data->high_threshold); + } + break; + case (DEVICEMODE_PAR): + if (parameter.is_read) { + parameter.status = + papi_func_tbl->GetDeviceMode( + vl53l0_dev, + (VL53L0_DeviceModes *)&(parameter.value)); + } else { + parameter.status = + papi_func_tbl->SetDeviceMode( + vl53l0_dev, + (VL53L0_DeviceModes)(parameter.value)); + data->deviceMode = + (VL53L0_DeviceModes)(parameter.value); + } + break; + + + + case (INTERMEASUREMENT_PAR): + if (parameter.is_read) { + parameter.status = + papi_func_tbl->GetInterMeasurementPeriodMilliSeconds( + vl53l0_dev, + (uint32_t *)&(parameter.value)); + } else { + parameter.status = + papi_func_tbl->SetInterMeasurementPeriodMilliSeconds( + vl53l0_dev, + (uint32_t)(parameter.value)); + data->interMeasurems = parameter.value; + } + break; + + } + + if (copy_to_user((struct stmvl53l0_parameter *)p, ¶meter, + sizeof(struct stmvl53l0_parameter))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + break; + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static int stmvl53l0_open(struct inode *inode, struct file *file) +{ + return 0; +} + +/* Flush will be invoked on close(device_fd) */ +static int stmvl53l0_flush(struct file *file, fl_owner_t id) +{ + struct stmvl53l0_data *data = container_of(file->private_data, + struct stmvl53l0_data, miscdev); + (void) file; + (void) id; + /* Revisit : Check if the + *instance are opened multiple times on some platforms + */ + mutex_lock(&data->work_mutex); + if (data) { + if (data->enable_ps_sensor == 1) { + /* turn off tof sensor if it's enabled */ + data->enable_ps_sensor = 0; + /* to stop */ + stmvl53l0_stop(data); + } + } + mutex_unlock(&data->work_mutex); + + return 0; +} + +static long stmvl53l0_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + long ret; + struct stmvl53l0_data *data = + container_of(file->private_data, + struct stmvl53l0_data, miscdev); + mutex_lock(&data->work_mutex); + ret = stmvl53l0_ioctl_handler(file, cmd, arg, (void __user *)arg); + mutex_unlock(&data->work_mutex); + + return ret; +} + +/* + * Initialization function + */ +static int stmvl53l0_init_client(struct stmvl53l0_data *data) +{ + + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceInfo_t DeviceInfo; + VL53L0_DEV vl53l0_dev = data; + uint32_t refSpadCount = 0; + uint8_t isApertureSpads = 0; + uint8_t VhvSettings = 0; + uint8_t PhaseCal = 0; + + + vl53l0_dbgmsg("Enter\n"); + + data->I2cDevAddr = 0x52; + data->comms_type = 1; + data->comms_speed_khz = 400; + + + + /* Setup API functions based on revision */ + stmvl53l0_setupAPIFunctions(data); + + /* Perform Ref and RefSpad calibrations and save the values */ + + + if (data->reset) { + pr_err("Call of VL53L0_DataInit\n"); + /* Data initialization */ + Status = papi_func_tbl->DataInit(vl53l0_dev); + /* data->reset = 0; */ + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "%d- error status %d\n", __LINE__, Status); + return Status; + } + } + + vl53l0_dbgmsg("VL53L0_GetDeviceInfo:\n"); + Status = papi_func_tbl->GetDeviceInfo(vl53l0_dev, &DeviceInfo); + if (Status == VL53L0_ERROR_NONE) { + pr_err("Device Name : %s\n", DeviceInfo.Name); + pr_err("Device Type : %s\n", DeviceInfo.Type); + pr_err("Device ID : %s\n", DeviceInfo.ProductId); + pr_err("Product type: %d\n", DeviceInfo.ProductType); + pr_err("ProductRevisionMajor : %d\n", + DeviceInfo.ProductRevisionMajor); + pr_err("ProductRevisionMinor : %d\n", + DeviceInfo.ProductRevisionMinor); + } + /* Device Initialization */ + vl53l0_dbgmsg("Call of VL53L0_StaticInit\n"); + Status = papi_func_tbl->StaticInit(vl53l0_dev); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg("%d- error status %d\n", __LINE__, Status); + return Status; + } + + + + + + if (papi_func_tbl->PerformRefCalibration != NULL && data->reset) { + vl53l0_dbgmsg("Call of VL53L0_PerformRefCalibration\n"); + Status = papi_func_tbl->PerformRefCalibration(vl53l0_dev, + &VhvSettings, &PhaseCal); /* Ref calibration */ + if (Status != + VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "%d- error status %d\n", __LINE__, Status); + return Status; + } + } + vl53l0_dbgmsg("VHV = %u, PhaseCal = %u\n", VhvSettings, PhaseCal); + vl53l0_dev->VhvSettings = VhvSettings; + vl53l0_dev->PhaseCal = PhaseCal; + + if (papi_func_tbl->PerformRefSpadManagement != NULL && data->reset) { + vl53l0_dbgmsg( + "Call of VL53L0_PerformRefSpadManagement\n"); + Status = papi_func_tbl->PerformRefSpadManagement(vl53l0_dev, + &refSpadCount, + &isApertureSpads); /* Ref Spad Management */ + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "%d- error status %d\n", __LINE__, Status); + return Status; + } + } + + vl53l0_dbgmsg( + "SpadCount = %u, isAperature = %u\n", + refSpadCount, isApertureSpads); + vl53l0_dev->refSpadCount = refSpadCount; + vl53l0_dev->isApertureSpads = isApertureSpads; + + + + + if (Status == VL53L0_ERROR_NONE && data->reset) { + if ((papi_func_tbl->SetOffsetCalibrationDataMicroMeter + != NULL) && + (vl53l0_dev->setCalibratedValue & + SET_OFFSET_CALIB_DATA_MICROMETER_MASK)) { + vl53l0_dbgmsg( + "Call of SetOffsetCalibrationDataMicroMeter\n"); + Status = + papi_func_tbl->SetOffsetCalibrationDataMicroMeter( + vl53l0_dev, + vl53l0_dev->OffsetMicroMeter); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "%d- error status %d\n", __LINE__, Status); + return Status; + } + } + } + + + + if (data->reset) { + if ((papi_func_tbl->SetXTalkCompensationRateMegaCps != NULL) && + (vl53l0_dev->setCalibratedValue & + SET_XTALK_COMP_RATE_MCPS_MASK)) { + vl53l0_dbgmsg( + "Call of SetXTalkCompensationRateMegaCps\n"); + Status = papi_func_tbl->SetXTalkCompensationRateMegaCps( + vl53l0_dev, + vl53l0_dev->XTalkCompensationRateMegaCps); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "%d- error status %d\n", + __LINE__, Status); + return Status; + } + } + } + + if (data->reset) { + if (vl53l0_dev->setCalibratedValue & + /* Xtalk calibration done*/ + SET_XTALK_COMP_RATE_MCPS_MASK) { + /* Range Ignore Threshold */ + FixPoint1616_t ritValue = 0; + + if (vl53l0_dev->XTalkCompensationRateMegaCps < + 7*65536/10000) { + /*0.7 KCps converted to MCps */ + ritValue = 15 * 7 * 65536/100000; + } else { + ritValue = 15 * + vl53l0_dev->XTalkCompensationRateMegaCps/10; + } + + if (papi_func_tbl->SetLimitCheckEnable != NULL) { + Status = papi_func_tbl->SetLimitCheckEnable( + vl53l0_dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + 1); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "%d- error status %d\n", __LINE__, + Status); + return Status; + } + } + + if (papi_func_tbl->SetLimitCheckValue != NULL) { + vl53l0_dbgmsg("Set RIT - %u\n", ritValue); + Status = papi_func_tbl->SetLimitCheckValue( + vl53l0_dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + ritValue); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "%d- error status %d\n", + __LINE__, Status); + return Status; + } + } + } + data->reset = 0; + } + + /* Setup in single ranging mode */ + + pr_err("Call of VL53L0_SetDeviceMode\n"); + Status = papi_func_tbl->SetDeviceMode(vl53l0_dev, + VL53L0_DEVICEMODE_SINGLE_RANGING); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg("%d- error status %d\n", __LINE__, Status); + return Status; + } + + + Status = papi_func_tbl->SetWrapAroundCheckEnable(vl53l0_dev, 1); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg("%d- error status %d\n", __LINE__, Status); + return Status; + } + + + vl53l0_dbgmsg("End\n"); + + return 0; +} + + +static int stmvl53l0_config_use_case(struct stmvl53l0_data *data) +{ + VL53L0_DEV vl53l0_dev = data; + VL53L0_Error Status = VL53L0_ERROR_NONE; + FixPoint1616_t signalRateLimit; + FixPoint1616_t sigmaLimit; + uint32_t preRangePulsePeriod; + uint32_t finalRangePulsePeriod; + + vl53l0_dbgmsg("Enter\n"); + + switch (vl53l0_dev->useCase) { + + case USE_CASE_LONG_DISTANCE: + sigmaLimit = LONG_DISTANCE_SIGMA_LIMIT; + signalRateLimit = LONG_DISTANCE_SIGNAL_RATE_LIMIT; + preRangePulsePeriod = LONG_DISTANCE_PRE_RANGE_PULSE_PERIOD; + finalRangePulsePeriod = LONG_DISTANCE_FINAL_RANGE_PULSE_PERIOD; + break; + + case USE_CASE_HIGH_ACCURACY: + sigmaLimit = HIGH_ACCURACY_SIGMA_LIMIT; + signalRateLimit = HIGH_ACCURACY_SIGNAL_RATE_LIMIT; + preRangePulsePeriod = HIGH_ACCURACY_PRE_RANGE_PULSE_PERIOD; + finalRangePulsePeriod = HIGH_ACCURACY_FINAL_RANGE_PULSE_PERIOD; + break; + + case USE_CASE_HIGH_SPEED: + sigmaLimit = HIGH_SPEED_SIGMA_LIMIT; + signalRateLimit = HIGH_SPEED_SIGNAL_RATE_LIMIT; + preRangePulsePeriod = HIGH_SPEED_PRE_RANGE_PULSE_PERIOD; + finalRangePulsePeriod = HIGH_SPEED_FINAL_RANGE_PULSE_PERIOD; + break; + + case USE_CASE_CUSTOM: + /* Set by application through IOCTL interface */ + sigmaLimit = vl53l0_dev->sigmaLimit; + signalRateLimit = vl53l0_dev->signalRateLimit; + preRangePulsePeriod = vl53l0_dev->preRangePulsePeriod; + finalRangePulsePeriod = vl53l0_dev->finalRangePulsePeriod; + break; + + default: + vl53l0_errmsg( + "Invalid use case = %d\n", vl53l0_dev->useCase); + /* Invalid parameter, should not reach here */ + return -EINVAL; + } + + vl53l0_dbgmsg( + "Configure UseCase(%d) : Sigma=%u,Signal=%u,Pre=%u,Final=%u,timingBudget=%u\n", + vl53l0_dev->useCase, + sigmaLimit, + signalRateLimit, + preRangePulsePeriod, + finalRangePulsePeriod, + vl53l0_dev->timingBudget); + + if (papi_func_tbl->SetLimitCheckEnable != NULL) { + Status = papi_func_tbl->SetLimitCheckEnable( + vl53l0_dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + 1); + + if (Status == VL53L0_ERROR_NONE) { + Status = papi_func_tbl->SetLimitCheckEnable( + vl53l0_dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + 1); + } else { + vl53l0_errmsg( + "SetLimitCheckEnable(SIGMA_FINAL_RANGE) failed with errcode = %d\n", + Status); + } + } + + + if (Status == VL53L0_ERROR_NONE) { + Status = papi_func_tbl->SetLimitCheckValue( + vl53l0_dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + signalRateLimit); + } else { + vl53l0_errmsg( + "SetLimitCheckEnable(SIGNAL_RATE_FINAL_RANGE) failed with errcode = %d\n", + Status); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = papi_func_tbl->SetLimitCheckValue(vl53l0_dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + sigmaLimit); + } else { + vl53l0_dbgmsg( + "SIGNAL_RATE_FINAL_RANGE failed with errcode = %d\n", + Status); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = + papi_func_tbl->SetMeasurementTimingBudgetMicroSeconds( + vl53l0_dev, + vl53l0_dev->timingBudget); + } else { + vl53l0_dbgmsg( + "SIGMA_FINAL_RANGE failed with errcode = %d\n", + Status); + } + + + if (Status == VL53L0_ERROR_NONE) { + Status = papi_func_tbl->SetVcselPulsePeriod(vl53l0_dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + preRangePulsePeriod); + } else { + vl53l0_dbgmsg( + "SetMeasurementTimingBudget failed with errcode = %d\n", + Status); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = papi_func_tbl->SetVcselPulsePeriod(vl53l0_dev, + VL53L0_VCSEL_PERIOD_FINAL_RANGE, + finalRangePulsePeriod); + } else + vl53l0_dbgmsg( + "SetVcselPulsePeriod(PRE) failed with errcode = %d\n", Status); + + + if (Status != VL53L0_ERROR_NONE) + vl53l0_dbgmsg( + "SetVcselPulsePeriod(FINAL)failed with errcode = %d\n", Status); + + vl53l0_dbgmsg("End\n"); + return Status; +} +static int stmvl53l0_start(struct stmvl53l0_data *data, uint8_t scaling, + init_mode_e mode) +{ + int rc = 0; + VL53L0_DEV vl53l0_dev = data; + VL53L0_Error Status = VL53L0_ERROR_NONE; + + vl53l0_dbgmsg("Enter\n"); + + /* Power up */ + rc = pmodule_func_tbl->power_up(data->client_object, &data->reset); + if (rc) { + vl53l0_errmsg("%d,error rc %d\n", __LINE__, rc); + return rc; + } + + /* init */ + rc = stmvl53l0_init_client(data); + if (rc) { + vl53l0_errmsg("%d, error rc %d\n", __LINE__, rc); + pmodule_func_tbl->power_down(data->client_object); + return -EINVAL; + } + + /* check mode */ + if (mode != NORMAL_MODE) + papi_func_tbl->SetXTalkCompensationEnable(vl53l0_dev, 1); + + if (mode == OFFSETCALIB_MODE) { + /*VL53L0_SetOffsetCalibrationDataMicroMeter(vl53l0_dev, 0);*/ + FixPoint1616_t OffsetMicroMeter; + + papi_func_tbl->PerformOffsetCalibration(vl53l0_dev, + (data->offsetCalDistance<<16), + &OffsetMicroMeter); + pr_err("Offset calibration:%u\n", OffsetMicroMeter); + vl53l0_dev->OffsetMicroMeter = OffsetMicroMeter; + vl53l0_dev->setCalibratedValue |= + SET_OFFSET_CALIB_DATA_MICROMETER_MASK; + + return rc; + } else if (mode == XTALKCALIB_MODE) { + FixPoint1616_t XTalkCompensationRateMegaCps; + /*caltarget distance : 100mm and convert to + * fixed point 16 16 format + */ + papi_func_tbl->PerformXTalkCalibration(vl53l0_dev, + (data->xtalkCalDistance<<16), + &XTalkCompensationRateMegaCps); + pr_err("Xtalk calibration:%u\n", + XTalkCompensationRateMegaCps); + vl53l0_dev->XTalkCompensationRateMegaCps = + XTalkCompensationRateMegaCps; + vl53l0_dev->setCalibratedValue |= + SET_XTALK_COMP_RATE_MCPS_MASK; + + return rc; + } + /* set up device parameters */ + data->gpio_polarity = VL53L0_INTERRUPTPOLARITY_LOW; + + /* Following two calls are made from IOCTL as well */ + Status = papi_func_tbl->SetGpioConfig(vl53l0_dev, 0, 0, + data->gpio_function, + VL53L0_INTERRUPTPOLARITY_LOW); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg("Failed to SetGpioConfig. Error = %d\n", Status); + return -EPERM; + } + + + Status = papi_func_tbl->SetInterruptThresholds(vl53l0_dev, 0, + data->low_threshold, + data->high_threshold); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "Failed to SetInterruptThresholds. Error = %d\n", Status); + return -EPERM; + } + + + if (data->deviceMode == VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING) { + Status = papi_func_tbl->SetInterMeasurementPeriodMilliSeconds( + vl53l0_dev, + data->interMeasurems); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "Failed to SetInterMeasurementPeriodMilliSeconds. Error = %d\n", + Status); + return -EPERM; + } + pr_err( + "DeviceMode:0x%x, interMeasurems:%d==\n", + data->deviceMode, + data->interMeasurems); + + + } + + + Status = papi_func_tbl->SetDeviceMode( + vl53l0_dev, + data->deviceMode); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg("Failed to SetDeviceMode. Error = %d\n", Status); + return -EPERM; + } + + Status = papi_func_tbl->ClearInterruptMask(vl53l0_dev, + 0); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "Failed to ClearInterruptMask. Error = %d\n", Status); + return -EPERM; + } + + + + Status = stmvl53l0_config_use_case(vl53l0_dev); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "Failed to configure Use case = %u\n", + vl53l0_dev->useCase); + return -EPERM; + } + + /* start the ranging */ + Status = papi_func_tbl->StartMeasurement(vl53l0_dev); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "Failed to StartMeasurement. Error = %d\n", Status); + return -EPERM; + } + + data->enable_ps_sensor = 1; + +#ifndef USE_INT + /* Unblock the thread execution */ + wake_up(&vl53l0_dev->poll_thread_wq); +#endif + + vl53l0_dbgmsg("End\n"); + + return rc; +} + +static int stmvl53l0_stop(struct stmvl53l0_data *data) +{ + int rc = 0; + VL53L0_DEV vl53l0_dev = data; + + vl53l0_dbgmsg("Enter\n"); + + /* stop - if continuous mode */ + if (data->deviceMode == VL53L0_DEVICEMODE_CONTINUOUS_RANGING || + data->deviceMode == VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING) + papi_func_tbl->StopMeasurement(vl53l0_dev); + + /* clean interrupt */ + papi_func_tbl->ClearInterruptMask(vl53l0_dev, 0); + + /* cancel work handler */ + stmvl53l0_cancel_handler(data); + + /* Clear updateUseCase pending operation */ + data->updateUseCase = 0; + /* power down */ + rc = pmodule_func_tbl->power_down(data->client_object); + if (rc) { + vl53l0_errmsg("%d, error rc %d\n", __LINE__, rc); + return rc; + } + vl53l0_dbgmsg("End\n"); + + return rc; +} +static void stmvl53l0_timer_fn(unsigned long data) +{ + + VL53L0_DEV vl53l0_dev = (VL53L0_DEV)data; + + vl53l0_dev->flushCount++; + + input_report_abs(vl53l0_dev->input_dev_ps, ABS_GAS, + vl53l0_dev->flushCount); + + + input_sync(vl53l0_dev->input_dev_ps); + + vl53l0_dbgmsg("Sensor HAL Flush Count = %u\n", vl53l0_dev->flushCount); +} + + + +/* + * I2C init/probing/exit functions + */ +static const struct file_operations stmvl53l0_ranging_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = stmvl53l0_ioctl, + .open = stmvl53l0_open, + .flush = stmvl53l0_flush, +}; + + + +int stmvl53l0_setup(struct stmvl53l0_data *data) +{ + int rc = 0; + +#ifdef USE_INT + int irq = 0; +#endif + + vl53l0_dbgmsg("Enter\n"); + + /* init mutex */ + mutex_init(&data->update_lock); + mutex_init(&data->work_mutex); + +#ifdef USE_INT + /* init interrupt */ + gpio_request(data->irq_gpio, "vl53l0_gpio_int"); + gpio_direction_input(data->irq_gpio); + irq = gpio_to_irq(data->irq_gpio); + if (irq < 0) { + vl53l0_errmsg("filed to map GPIO: %d to interrupt:%d\n", + data->irq_gpio, irq); + } else { + vl53l0_dbgmsg("register_irq:%d\n", irq); + /* IRQF_TRIGGER_FALLING- poliarity:0 IRQF_TRIGGER_RISNG - + * poliarty:1 + */ + rc = request_threaded_irq(irq, NULL, + stmvl53l0_interrupt_handler, + IRQF_TRIGGER_FALLING|IRQF_ONESHOT, + "vl53l0_interrupt", + (void *)data); + if (rc) { + vl53l0_errmsg( +"%d, Could not allocate STMVL53L0_INT ! result:%d\n", __LINE__, rc); + free_irq(irq, data); + goto exit_free_irq; + } + } + data->irq = irq; + vl53l0_errmsg("interrupt is hooked\n"); +#else + + init_waitqueue_head(&data->poll_thread_wq); + + data->poll_thread = kthread_run(&stmvl53l0_poll_thread, + (void *)data, + "STM-VL53L0"); + if (data->poll_thread == NULL) { + pr_err( + "%s(%d) - Failed to create Polling thread\n", __func__, __LINE__); + goto exit_free_irq; + } +#endif + + /* init work handler */ + INIT_DELAYED_WORK(&data->dwork, stmvl53l0_work_handler); + + /* Register to Input Device */ + data->input_dev_ps = input_allocate_device(); + if (!data->input_dev_ps) { + rc = -ENOMEM; + vl53l0_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_free_irq; + } + set_bit(EV_ABS, data->input_dev_ps->evbit); + /* range in cm*/ + input_set_abs_params(data->input_dev_ps, ABS_DISTANCE, 0, 76, 0, 0); + /* tv_sec */ + input_set_abs_params(data->input_dev_ps, ABS_HAT0X, 0, 0xffffffff, + 0, 0); + /* tv_usec */ + input_set_abs_params(data->input_dev_ps, ABS_HAT0Y, 0, 0xffffffff, + 0, 0); + /* range in_mm */ + input_set_abs_params(data->input_dev_ps, ABS_HAT1X, 0, 765, 0, 0); + /* error code change maximum to 0xff for more flexibility */ + input_set_abs_params(data->input_dev_ps, ABS_HAT1Y, 0, 0xff, 0, 0); + /* rtnRate */ + input_set_abs_params(data->input_dev_ps, ABS_HAT2X, 0, 0xffffffff, + 0, 0); + /* rtn_amb_rate */ + input_set_abs_params(data->input_dev_ps, ABS_HAT2Y, 0, 0xffffffff, + 0, 0); + /* rtn_conv_time */ + input_set_abs_params(data->input_dev_ps, ABS_HAT3X, 0, 0xffffffff, + 0, 0); + /* dmax */ + input_set_abs_params(data->input_dev_ps, ABS_HAT3Y, 0, 0xffffffff, + 0, 0); + + input_set_abs_params(data->input_dev_ps, ABS_PRESSURE, 0, 0xffffffff, + 0, 0); + + input_set_abs_params(data->input_dev_ps, ABS_WHEEL, 0, 0xffffffff, + 0, 0); + + input_set_abs_params(data->input_dev_ps, ABS_GAS, 0, 0xffffffff, + 0, 0); + data->input_dev_ps->name = "STM VL53L0 proximity sensor"; + + rc = input_register_device(data->input_dev_ps); + if (rc) { + rc = -ENOMEM; + vl53l0_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_free_dev_ps; + } + /* setup drv data */ + input_set_drvdata(data->input_dev_ps, data); + + /* Register sysfs hooks */ + data->range_kobj = kobject_create_and_add("range", kernel_kobj); + if (!data->range_kobj) { + rc = -ENOMEM; + vl53l0_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_unregister_dev_ps; + } + rc = sysfs_create_group(&data->input_dev_ps->dev.kobj, + &stmvl53l0_attr_group); + if (rc) { + rc = -ENOMEM; + vl53l0_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_unregister_dev_ps_1; + } + + setup_timer(&data->timer, + stmvl53l0_timer_fn, + (unsigned long)data); + + /* to register as a misc device */ + data->miscdev.minor = MISC_DYNAMIC_MINOR; + data->miscdev.name = "stmvl53l0_ranging"; + data->miscdev.fops = &stmvl53l0_ranging_fops; + vl53l0_errmsg("Misc device registration name:%s\n", data->dev_name); + if (misc_register(&data->miscdev) != 0) + vl53l0_errmsg( +"Could not register misc. dev for stmvl53l0 ranging\n"); + + /* init default device parameter value */ + data->enable_ps_sensor = 0; + data->reset = 1; + data->delay_ms = 30; /* delay time to 30ms */ + data->enableDebug = 0; + data->gpio_polarity = VL53L0_INTERRUPTPOLARITY_LOW; + data->gpio_function = VL53L0_GPIOFUNCTIONALITY_NEW_MEASURE_READY; + data->low_threshold = 60; + data->high_threshold = 200; + data->deviceMode = VL53L0_DEVICEMODE_SINGLE_RANGING; + data->interMeasurems = 30; + data->useCase = USE_CASE_LONG_DISTANCE; + data->timingBudget = LONG_DISTANCE_TIMING_BUDGET; + + /* Set default values used in Custom Mode Use Case */ + data->signalRateLimit = LONG_DISTANCE_SIGNAL_RATE_LIMIT; + data->sigmaLimit = LONG_DISTANCE_SIGMA_LIMIT; + data->preRangePulsePeriod = LONG_DISTANCE_PRE_RANGE_PULSE_PERIOD; + data->finalRangePulsePeriod = LONG_DISTANCE_FINAL_RANGE_PULSE_PERIOD; + + + + + vl53l0_dbgmsg("support ver. %s enabled\n", DRIVER_VERSION); + vl53l0_dbgmsg("End"); + + return 0; +exit_unregister_dev_ps_1: + kobject_put(data->range_kobj); +exit_unregister_dev_ps: + input_unregister_device(data->input_dev_ps); +exit_free_dev_ps: + input_free_device(data->input_dev_ps); +exit_free_irq: +#ifdef USE_INT + free_irq(irq, data); +#endif + kfree(data); + return rc; +} + +void stmvl53l0_cleanup(struct stmvl53l0_data *data) +{ +#ifndef USE_INT + pr_err("%s(%d) : Stop poll_thread\n", __func__, __LINE__); + poll_thread_exit = 1; + kthread_stop(data->poll_thread); +#endif +} +static int __init stmvl53l0_init(void) +{ + int ret = -1; + + vl53l0_dbgmsg("Enter\n"); + + /* assign function table */ + pmodule_func_tbl = &stmvl53l0_module_func_tbl; + papi_func_tbl = &stmvl53l0_api_func_tbl; + + /* client specific init function */ + ret = pmodule_func_tbl->init(); + + if (ret) + vl53l0_errmsg("%d failed with %d\n", __LINE__, ret); + + vl53l0_dbgmsg("End\n"); + + return ret; +} + +static void __exit stmvl53l0_exit(void) +{ + vl53l0_dbgmsg("Enter\n"); + + vl53l0_dbgmsg("End\n"); +} + + +MODULE_AUTHOR("STMicroelectronics Imaging Division"); +MODULE_DESCRIPTION("ST FlightSense Time-of-Flight sensor driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); + +module_init(stmvl53l0_init); +module_exit(stmvl53l0_exit); + diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index ae33da7ab51f..2d564aabbc74 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -11,7 +11,9 @@ menuconfig INPUT_TOUCHSCREEN if INPUT_TOUCHSCREEN -config TOUCHSCREEN_PROPERTIES +source "drivers/input/touchscreen/synaptics_dsx/Kconfig" + +config OF_TOUCHSCREEN def_tristate INPUT depends on INPUT @@ -115,6 +117,18 @@ config TOUCHSCREEN_ATMEL_MXT To compile this driver as a module, choose M here: the module will be called atmel_mxt_ts. +config TOUCHSCREEN_ATMEL_MAXTOUCH_TS + tristate "Atmel Maxtouch Touchscreen Family" + depends on I2C + help + Say Y here if you have Atmel MaXTouch Touchscreen + using i2c connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called atmel_maxtouch_ts. + config TOUCHSCREEN_AUO_PIXCIR tristate "AUO in-cell touchscreen using Pixcir ICs" depends on I2C @@ -1095,6 +1109,34 @@ config TOUCHSCREEN_COLIBRI_VF50 To compile this driver as a module, choose M here: the module will be called colibri_vf50_ts. +config TOUCHSCREEN_FT5X06_PSENSOR + tristate "FocalTech proximity feature support" + depends on TOUCHSCREEN_FT5X06 && SENSORS + help + Say Y here if you want to support ft5x06's proximity + feature. + + If unsure, say N. + +config TOUCHSCREEN_FT5X06_GESTURE + tristate "FocalTech gesture feature support" + depends on TOUCHSCREEN_FT5X06 + help + Say Y here if you want to support ft5x06's gesture + feature. + + If unsure, say N. + +config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS + bool "Synaptics DSX firmware update extra sysfs attributes" + depends on TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE + help + Say Y here to enable support for extra sysfs attributes + supporting firmware update in a development environment. + This does not affect the core or other subsystem attributes. + + If unsure, say N. + config TOUCHSCREEN_ROHM_BU21023 tristate "ROHM BU21023/24 Dual touch support resistive touchscreens" depends on I2C @@ -1106,4 +1148,81 @@ config TOUCHSCREEN_ROHM_BU21023 To compile this driver as a module, choose M here: the module will be called bu21023_ts. +config TOUCHSCREEN_MAXIM_STI + tristate "Maxim based STI touchscreens" + depends on SPI_MASTER + help + Say Y here if you have a touchscreen interface using the + Maxim STI based touch controller. + + If unsure, say N + + To compile this driver as a module, choose M here: the + module will be called maxim_sti. + +config SECURE_TOUCH + bool "Secure Touch" + depends on (TOUCHSCREEN_SYNAPTICS_I2C_RMI4 || \ + TOUCHSCREEN_SYNAPTICS_DSX_I2C_v21) + help + Say Y here to enable Secure Touch in supported drivers. + + If unsure, say N. + +config TOUCHSCREEN_GEN_VKEYS + tristate "Touchscreen Virtual Keys Driver" + help + Say Y here if you want to generate a sysfs entry for virtual + keys on Android. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called gen_vkeys. + +config TOUCHSCREEN_FT5X06 + tristate "FocalTech touchscreens" + depends on I2C + help + Say Y here if you have a ft5X06 touchscreen. + Ft5x06 controllers are multi touch controllers which can + report 5 touches at a time. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ft5x06_ts. + +config FT_SECURE_TOUCH + bool "Secure Touch support for Focaltech Touchscreen" + depends on TOUCHSCREEN_FT5X06 + help + Say Y here + -Focaltech touch driver is connected + -To enable secure touch for Focaltech touch driver + + If unsure, say N. + +config TOUCHSCREEN_IT7260_I2C + tristate "IT7260 Touchscreen Driver" + depends on I2C + help + Say Y here if you have a IT7260 Touchscreen Driver + connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called it7258_ts_i2c. + +config TOUCHSCREEN_ST + bool "STMicroelectronics Touchscreen Driver" + depends on I2C + help + Say Y here if you have a STMicroelectronics Touchscreen. + + If unsure, say N. + +source "drivers/input/touchscreen/st/Kconfig" + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index cbaa6abb08da..f5be6fc19751 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o +obj-$(CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH_TS) += atmel_maxtouch_ts.o obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o @@ -36,15 +37,19 @@ obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o +obj-$(CONFIG_TOUCHSCREEN_FT5X06) += ft5x06_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o +obj-$(CONFIG_TOUCHSCREEN_GEN_VKEYS) += gen_vkeys.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC) += imx6ul_tsc.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o +obj-$(CONFIG_TOUCHSCREEN_IT7260_I2C) += it7258_ts_i2c.o obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o +obj-$(CONFIG_TOUCHSCREEN_MAXIM_STI) += maxim_sti.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o @@ -64,6 +69,7 @@ obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o obj-$(CONFIG_TOUCHSCREEN_SUN4I) += sun4i-ts.o obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o +obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_v21) += synaptics_dsx/ obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o @@ -91,3 +97,4 @@ obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o +obj-$(CONFIG_TOUCHSCREEN_ST) += st/ diff --git a/drivers/input/touchscreen/atmel_maxtouch_ts.c b/drivers/input/touchscreen/atmel_maxtouch_ts.c new file mode 100644 index 000000000000..f58fc8555156 --- /dev/null +++ b/drivers/input/touchscreen/atmel_maxtouch_ts.c @@ -0,0 +1,4204 @@ +/* + * Atmel maXTouch Touchscreen driver + * + * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * + * Linux foundation chooses to take subject only to the GPLv2 license terms, + * and distributes only under these terms. + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Copyright (C) 2011-2012 Atmel Corporation + * Copyright (C) 2012 Google, Inc. + * + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/input/atmel_maxtouch_ts.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> + +#if defined(CONFIG_FB) +#include <linux/notifier.h> +#include <linux/fb.h> + +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include <linux/earlysuspend.h> +/* Early-suspend level */ +#define MXT_SUSPEND_LEVEL 1 +#endif + +#if defined(CONFIG_SECURE_TOUCH) +#include <linux/completion.h> +#include <linux/pm_runtime.h> +#include <linux/errno.h> +#include <linux/atomic.h> +#include <linux/clk.h> +#endif + +/* Configuration file */ +#define MXT_CFG_MAGIC "OBP_RAW V1" + +/* Registers */ +#define MXT_OBJECT_START 0x07 +#define MXT_OBJECT_SIZE 6 +#define MXT_INFO_CHECKSUM_SIZE 3 +#define MXT_MAX_BLOCK_WRITE 256 + +/* Object types */ +#define MXT_DEBUG_DIAGNOSTIC_T37 37 +#define MXT_GEN_MESSAGE_T5 5 +#define MXT_GEN_COMMAND_T6 6 +#define MXT_GEN_POWER_T7 7 +#define MXT_GEN_ACQUIRE_T8 8 +#define MXT_GEN_DATASOURCE_T53 53 +#define MXT_TOUCH_MULTI_T9 9 +#define MXT_TOUCH_KEYARRAY_T15 15 +#define MXT_TOUCH_PROXIMITY_T23 23 +#define MXT_TOUCH_PROXKEY_T52 52 +#define MXT_PROCI_GRIPFACE_T20 20 +#define MXT_PROCG_NOISE_T22 22 +#define MXT_PROCI_ONETOUCH_T24 24 +#define MXT_PROCI_TWOTOUCH_T27 27 +#define MXT_PROCI_GRIP_T40 40 +#define MXT_PROCI_PALM_T41 41 +#define MXT_PROCI_TOUCHSUPPRESSION_T42 42 +#define MXT_PROCI_STYLUS_T47 47 +#define MXT_PROCG_NOISESUPPRESSION_T48 48 +#define MXT_SPT_COMMSCONFIG_T18 18 +#define MXT_SPT_GPIOPWM_T19 19 +#define MXT_SPT_SELFTEST_T25 25 +#define MXT_SPT_CTECONFIG_T28 28 +#define MXT_SPT_USERDATA_T38 38 +#define MXT_SPT_DIGITIZER_T43 43 +#define MXT_SPT_MESSAGECOUNT_T44 44 +#define MXT_SPT_CTECONFIG_T46 46 +#define MXT_PROCI_ACTIVE_STYLUS_T63 63 +#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100 + +/* MXT_GEN_MESSAGE_T5 object */ +#define MXT_RPTID_NOMSG 0xff + +/* MXT_GEN_COMMAND_T6 field */ +#define MXT_COMMAND_RESET 0 +#define MXT_COMMAND_BACKUPNV 1 +#define MXT_COMMAND_CALIBRATE 2 +#define MXT_COMMAND_REPORTALL 3 +#define MXT_COMMAND_DIAGNOSTIC 5 + +/* Define for T6 status byte */ +#define MXT_T6_STATUS_RESET (1 << 7) +#define MXT_T6_STATUS_OFL (1 << 6) +#define MXT_T6_STATUS_SIGERR (1 << 5) +#define MXT_T6_STATUS_CAL (1 << 4) +#define MXT_T6_STATUS_CFGERR (1 << 3) +#define MXT_T6_STATUS_COMSERR (1 << 2) + +/* MXT_GEN_POWER_T7 field */ +struct t7_config { + u8 idle; + u8 active; +} __packed; + +#define MXT_POWER_CFG_RUN 0 +#define MXT_POWER_CFG_DEEPSLEEP 1 + +/* MXT_TOUCH_MULTI_T9 field */ +#define MXT_T9_ORIENT 9 +#define MXT_T9_RANGE 18 + +/* MXT_TOUCH_MULTI_T9 status */ +#define MXT_T9_UNGRIP (1 << 0) +#define MXT_T9_SUPPRESS (1 << 1) +#define MXT_T9_AMP (1 << 2) +#define MXT_T9_VECTOR (1 << 3) +#define MXT_T9_MOVE (1 << 4) +#define MXT_T9_RELEASE (1 << 5) +#define MXT_T9_PRESS (1 << 6) +#define MXT_T9_DETECT (1 << 7) + +struct t9_range { + u16 x; + u16 y; +} __packed; + +/* MXT_TOUCH_MULTI_T9 orient */ +#define MXT_T9_ORIENT_SWITCH (1 << 0) + +/* MXT_SPT_COMMSCONFIG_T18 */ +#define MXT_COMMS_CTRL 0 +#define MXT_COMMS_CMD 1 +#define MXT_COMMS_RETRIGEN (1 << 6) + +/* Define for MXT_GEN_COMMAND_T6 */ +#define MXT_BOOT_VALUE 0xa5 +#define MXT_RESET_VALUE 0x01 +#define MXT_BACKUP_VALUE 0x55 + +/* Define for MXT_PROCI_TOUCHSUPPRESSION_T42 */ +#define MXT_T42_MSG_TCHSUP (1 << 0) + +/* T47 Stylus */ +#define MXT_TOUCH_MAJOR_T47_STYLUS 1 + +/* T63 Stylus */ +#define MXT_T63_STYLUS_PRESS (1 << 0) +#define MXT_T63_STYLUS_RELEASE (1 << 1) +#define MXT_T63_STYLUS_MOVE (1 << 2) +#define MXT_T63_STYLUS_SUPPRESS (1 << 3) + +#define MXT_T63_STYLUS_DETECT (1 << 4) +#define MXT_T63_STYLUS_TIP (1 << 5) +#define MXT_T63_STYLUS_ERASER (1 << 6) +#define MXT_T63_STYLUS_BARREL (1 << 7) + +#define MXT_T63_STYLUS_PRESSURE_MASK 0x3F + +/* T100 Multiple Touch Touchscreen */ +#define MXT_T100_CTRL 0 +#define MXT_T100_CFG1 1 +#define MXT_T100_TCHAUX 3 +#define MXT_T100_XRANGE 13 +#define MXT_T100_YRANGE 24 + +#define MXT_T100_CFG_SWITCHXY (1 << 5) + +#define MXT_T100_TCHAUX_VECT (1 << 0) +#define MXT_T100_TCHAUX_AMPL (1 << 1) +#define MXT_T100_TCHAUX_AREA (1 << 2) + +#define MXT_T100_DETECT (1 << 7) +#define MXT_T100_TYPE_MASK 0x70 +#define MXT_T100_TYPE_STYLUS 0x20 + +/* Delay times */ +#define MXT_BACKUP_TIME 50 /* msec */ +#define MXT_RESET_TIME 200 /* msec */ +#define MXT_RESET_TIMEOUT 3000 /* msec */ +#define MXT_CRC_TIMEOUT 1000 /* msec */ +#define MXT_FW_RESET_TIME 3000 /* msec */ +#define MXT_FW_CHG_TIMEOUT 300 /* msec */ +#define MXT_WAKEUP_TIME 25 /* msec */ +#define MXT_REGULATOR_DELAY 150 /* msec */ +#define MXT_POWERON_DELAY 150 /* msec */ + +/* Command to unlock bootloader */ +#define MXT_UNLOCK_CMD_MSB 0xaa +#define MXT_UNLOCK_CMD_LSB 0xdc + +/* Bootloader mode status */ +#define MXT_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ +#define MXT_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ +#define MXT_FRAME_CRC_CHECK 0x02 +#define MXT_FRAME_CRC_FAIL 0x03 +#define MXT_FRAME_CRC_PASS 0x04 +#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ +#define MXT_BOOT_STATUS_MASK 0x3f +#define MXT_BOOT_EXTENDED_ID (1 << 5) +#define MXT_BOOT_ID_MASK 0x1f + +/* Touchscreen absolute values */ +#define MXT_MAX_AREA 0xff + +#define MXT_PIXELS_PER_MM 20 + +#define DEBUG_MSG_MAX 200 + +#define MXT_COORDS_ARR_SIZE 4 + +/* Orient */ +#define MXT_NORMAL 0x0 +#define MXT_DIAGONAL 0x1 +#define MXT_HORIZONTAL_FLIP 0x2 +#define MXT_ROTATED_90_COUNTER 0x3 +#define MXT_VERTICAL_FLIP 0x4 +#define MXT_ROTATED_90 0x5 +#define MXT_ROTATED_180 0x6 +#define MXT_DIAGONAL_COUNTER 0x7 + +/* MXT_TOUCH_KEYARRAY_T15 */ +#define MXT_KEYARRAY_MAX_KEYS 32 + +/* Bootoader IDs */ +#define MXT_BOOTLOADER_ID_224 0x0A +#define MXT_BOOTLOADER_ID_224E 0x06 +#define MXT_BOOTLOADER_ID_336S 0x1A +#define MXT_BOOTLOADER_ID_1386 0x01 +#define MXT_BOOTLOADER_ID_1386E 0x10 +#define MXT_BOOTLOADER_ID_1664S 0x14 + +/* recommended voltage specifications */ +#define MXT_VDD_VTG_MIN_UV 1800000 +#define MXT_VDD_VTG_MAX_UV 1800000 +#define MXT_AVDD_VTG_MIN_UV 2700000 +#define MXT_AVDD_VTG_MAX_UV 3300000 +#define MXT_XVDD_VTG_MIN_UV 2700000 +#define MXT_XVDD_VTG_MAX_UV 10000000 + +#define MXT_GEN_CFG "maxtouch_generic_cfg.raw" +#define MXT_NAME_MAX_LEN 100 + +struct mxt_info { + u8 family_id; + u8 variant_id; + u8 version; + u8 build; + u8 matrix_xsize; + u8 matrix_ysize; + u8 object_num; +}; + +struct mxt_object { + u8 type; + u16 start_address; + u8 size_minus_one; + u8 instances_minus_one; + u8 num_report_ids; +} __packed; + +/* Each client has this additional data */ +struct mxt_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct pinctrl *ts_pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; + char phys[64]; /* device physical location */ + struct mxt_platform_data *pdata; + struct mxt_object *object_table; + struct mxt_info *info; + void *raw_info_block; + unsigned int irq; + unsigned int max_x; + unsigned int max_y; + bool in_bootloader; + u16 mem_size; + u8 t100_aux_ampl; + u8 t100_aux_area; + u8 t100_aux_vect; + struct bin_attribute mem_access_attr; + bool debug_enabled; + bool debug_v2_enabled; + u8 *debug_msg_data; + u16 debug_msg_count; + struct bin_attribute debug_msg_attr; + struct mutex debug_msg_lock; + u8 max_reportid; + u32 config_crc; + u32 info_crc; + u8 bootloader_addr; + struct t7_config t7_cfg; + u8 *msg_buf; + u8 t6_status; + bool update_input; + u8 last_message_count; + u8 num_touchids; + u8 num_stylusids; + unsigned long t15_keystatus; + bool use_retrigen_workaround; + bool use_regulator; + struct regulator *reg_vdd; + struct regulator *reg_avdd; + struct regulator *reg_xvdd; + char fw_name[MXT_NAME_MAX_LEN]; + char cfg_name[MXT_NAME_MAX_LEN]; + u8 cfg_version[3]; + bool fw_w_no_cfg_update; + +#if defined(CONFIG_FB) + struct notifier_block fb_notif; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + /* Cached parameters from object table */ + u16 T5_address; + u8 T5_msg_size; + u8 T6_reportid; + u16 T6_address; + u16 T7_address; + u8 T9_reportid_min; + u8 T9_reportid_max; + u8 T15_reportid_min; + u8 T15_reportid_max; + u16 T18_address; + u8 T19_reportid; + u8 T42_reportid_min; + u8 T42_reportid_max; + u16 T44_address; + u8 T48_reportid; + u8 T63_reportid_min; + u8 T63_reportid_max; + u8 T100_reportid_min; + u8 T100_reportid_max; + + /* for fw update in bootloader */ + struct completion bl_completion; + + /* for reset handling */ + struct completion reset_completion; + + /* for reset handling */ + struct completion crc_completion; + + /* Enable reporting of input events */ + bool enable_reporting; + + /* Indicates whether device is in suspend */ + bool suspended; + +#if defined(CONFIG_SECURE_TOUCH) + atomic_t st_enabled; + atomic_t st_pending_irqs; + bool st_initialized; + struct completion st_powerdown; + struct clk *core_clk; + struct clk *iface_clk; +#endif +}; + +static inline unsigned int mxt_obj_size(const struct mxt_object *obj) +{ + return obj->size_minus_one + 1; +} + +static inline unsigned int mxt_obj_instances(const struct mxt_object *obj) +{ + return obj->instances_minus_one + 1; +} + +static bool mxt_object_readable(unsigned int type) +{ + switch (type) { + case MXT_GEN_COMMAND_T6: + case MXT_GEN_POWER_T7: + case MXT_GEN_ACQUIRE_T8: + case MXT_GEN_DATASOURCE_T53: + case MXT_TOUCH_MULTI_T9: + case MXT_TOUCH_KEYARRAY_T15: + case MXT_TOUCH_PROXIMITY_T23: + case MXT_TOUCH_PROXKEY_T52: + case MXT_PROCI_GRIPFACE_T20: + case MXT_PROCG_NOISE_T22: + case MXT_PROCI_ONETOUCH_T24: + case MXT_PROCI_TWOTOUCH_T27: + case MXT_PROCI_GRIP_T40: + case MXT_PROCI_PALM_T41: + case MXT_PROCI_TOUCHSUPPRESSION_T42: + case MXT_PROCI_STYLUS_T47: + case MXT_PROCG_NOISESUPPRESSION_T48: + case MXT_SPT_COMMSCONFIG_T18: + case MXT_SPT_GPIOPWM_T19: + case MXT_SPT_SELFTEST_T25: + case MXT_SPT_CTECONFIG_T28: + case MXT_SPT_USERDATA_T38: + case MXT_SPT_DIGITIZER_T43: + case MXT_SPT_CTECONFIG_T46: + return true; + default: + return false; + } +} + +static void mxt_dump_message(struct mxt_data *data, u8 *message) +{ + print_hex_dump(KERN_DEBUG, "MXT MSG:", DUMP_PREFIX_NONE, 16, 1, + message, data->T5_msg_size, false); +} + +static void mxt_debug_msg_enable(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + + if (data->debug_v2_enabled) + return; + + mutex_lock(&data->debug_msg_lock); + + data->debug_msg_data = kcalloc(DEBUG_MSG_MAX, + data->T5_msg_size, GFP_KERNEL); + if (!data->debug_msg_data) { + dev_err(&data->client->dev, "Failed to allocate buffer\n"); + return; + } + + data->debug_v2_enabled = true; + mutex_unlock(&data->debug_msg_lock); + + dev_info(dev, "Enabled message output\n"); +} + +static void mxt_debug_msg_disable(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + + if (!data->debug_v2_enabled) + return; + + dev_info(dev, "disabling message output\n"); + data->debug_v2_enabled = false; + + mutex_lock(&data->debug_msg_lock); + kfree(data->debug_msg_data); + data->debug_msg_data = NULL; + data->debug_msg_count = 0; + mutex_unlock(&data->debug_msg_lock); + dev_info(dev, "Disabled message output\n"); +} + +static void mxt_debug_msg_add(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + + mutex_lock(&data->debug_msg_lock); + + if (!data->debug_msg_data) { + dev_err(dev, "No buffer!\n"); + return; + } + + if (data->debug_msg_count < DEBUG_MSG_MAX) { + memcpy(data->debug_msg_data + data->debug_msg_count * data->T5_msg_size, + msg, + data->T5_msg_size); + data->debug_msg_count++; + } else { + dev_dbg(dev, "Discarding %u messages\n", data->debug_msg_count); + data->debug_msg_count = 0; + } + + mutex_unlock(&data->debug_msg_lock); + + sysfs_notify(&data->client->dev.kobj, NULL, "debug_notify"); +} + +static ssize_t mxt_debug_msg_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) +{ + return -EIO; +} + +static ssize_t mxt_debug_msg_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t bytes) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct mxt_data *data = dev_get_drvdata(dev); + int count; + size_t bytes_read; + + if (!data->debug_msg_data) { + dev_err(dev, "No buffer!\n"); + return 0; + } + + count = bytes / data->T5_msg_size; + + if (count > DEBUG_MSG_MAX) + count = DEBUG_MSG_MAX; + + mutex_lock(&data->debug_msg_lock); + + if (count > data->debug_msg_count) + count = data->debug_msg_count; + + bytes_read = count * data->T5_msg_size; + + memcpy(buf, data->debug_msg_data, bytes_read); + data->debug_msg_count = 0; + + mutex_unlock(&data->debug_msg_lock); + + return bytes_read; +} + +static int mxt_debug_msg_init(struct mxt_data *data) +{ + sysfs_bin_attr_init(&data->debug_msg_attr); + data->debug_msg_attr.attr.name = "debug_msg"; + data->debug_msg_attr.attr.mode = 0666; + data->debug_msg_attr.read = mxt_debug_msg_read; + data->debug_msg_attr.write = mxt_debug_msg_write; + data->debug_msg_attr.size = data->T5_msg_size * DEBUG_MSG_MAX; + + if (sysfs_create_bin_file(&data->client->dev.kobj, + &data->debug_msg_attr) < 0) + dev_info(&data->client->dev, "Debugfs already exists\n"); + + return 0; +} + +static void mxt_debug_msg_remove(struct mxt_data *data) +{ + if (data->debug_msg_attr.attr.name) + sysfs_remove_bin_file(&data->client->dev.kobj, + &data->debug_msg_attr); +} + +static int mxt_wait_for_completion(struct mxt_data *data, + struct completion *comp, unsigned int timeout_ms) +{ + struct device *dev = &data->client->dev; + unsigned long timeout = msecs_to_jiffies(timeout_ms); + long ret; + + ret = wait_for_completion_interruptible_timeout(comp, timeout); + if (ret < 0) { + dev_err(dev, "Wait for completion interrupted.\n"); + return -EINTR; + } else if (ret == 0) { + dev_err(dev, "Wait for completion timed out.\n"); + return -ETIMEDOUT; + } + return 0; +} + +static int mxt_bootloader_read(struct mxt_data *data, + u8 *val, unsigned int count) +{ + int ret; + struct i2c_msg msg; + + msg.addr = data->bootloader_addr; + msg.flags = data->client->flags & I2C_M_TEN; + msg.flags |= I2C_M_RD; + msg.len = count; + msg.buf = val; + + ret = i2c_transfer(data->client->adapter, &msg, 1); + + if (ret == 1) { + ret = 0; + } else { + ret = (ret < 0) ? ret : -EIO; + dev_err(&data->client->dev, "%s: i2c recv failed (%d)\n", + __func__, ret); + } + + return ret; +} + +static int mxt_bootloader_write(struct mxt_data *data, + const u8 * const val, unsigned int count) +{ + int ret; + struct i2c_msg msg; + + msg.addr = data->bootloader_addr; + msg.flags = data->client->flags & I2C_M_TEN; + msg.len = count; + msg.buf = (u8 *)val; + + ret = i2c_transfer(data->client->adapter, &msg, 1); + if (ret == 1) { + ret = 0; + } else { + ret = (ret < 0) ? ret : -EIO; + dev_err(&data->client->dev, "%s: i2c send failed (%d)\n", + __func__, ret); + } + + return ret; +} + +static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry) +{ + u8 appmode = data->client->addr; + u8 bootloader; + u8 family_id = 0; + + if (data->pdata->bl_addr) { + data->bootloader_addr = data->pdata->bl_addr; + return 0; + } + + if (data->info) + family_id = data->info->family_id; + + switch (appmode) { + case 0x4a: + case 0x4b: + /* Chips after 1664S use different scheme */ + if (retry || family_id >= 0xa2) { + bootloader = appmode - 0x24; + break; + } + /* Fall through for normal case */ + case 0x4c: + case 0x4d: + case 0x5a: + case 0x5b: + bootloader = appmode - 0x26; + break; + default: + dev_err(&data->client->dev, + "Appmode i2c address 0x%02x not found\n", + appmode); + return -EINVAL; + } + + data->bootloader_addr = bootloader; + return 0; +} + +static int mxt_probe_bootloader(struct mxt_data *data, bool retry) +{ + struct device *dev = &data->client->dev; + int ret; + u8 val; + bool crc_failure; + + ret = mxt_lookup_bootloader_address(data, retry); + if (ret) + return ret; + + ret = mxt_bootloader_read(data, &val, 1); + if (ret) + return ret; + + /* Check app crc fail mode */ + crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL; + + dev_err(dev, "Detected bootloader, status:%02X%s\n", + val, crc_failure ? ", APP_CRC_FAIL" : ""); + + return 0; +} + +static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) +{ + struct device *dev = &data->client->dev; + u8 buf[3]; + + if (val & MXT_BOOT_EXTENDED_ID) { + if (mxt_bootloader_read(data, &buf[0], 3) != 0) { + dev_err(dev, "%s: i2c failure\n", __func__); + return -EIO; + } + + dev_info(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]); + + return buf[0]; + } else { + dev_info(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK); + + return val; + } +} + +static int mxt_check_bootloader(struct mxt_data *data, unsigned int state, + bool wait) +{ + struct device *dev = &data->client->dev; + u8 val; + int ret; + +recheck: + if (wait) { + /* + * In application update mode, the interrupt + * line signals state transitions. We must wait for the + * CHG assertion before reading the status byte. + * Once the status byte has been read, the line is deasserted. + */ + ret = mxt_wait_for_completion(data, &data->bl_completion, + MXT_FW_CHG_TIMEOUT); + if (ret) { + /* + * TODO: handle -EINTR better by terminating fw update + * process before returning to userspace by writing + * length 0x000 to device (iff we are in + * WAITING_FRAME_DATA state). + */ + dev_err(dev, "Update wait error %d\n", ret); + return ret; + } + } + + ret = mxt_bootloader_read(data, &val, 1); + if (ret) + return ret; + + if (state == MXT_WAITING_BOOTLOAD_CMD) + val = mxt_get_bootloader_version(data, val); + + switch (state) { + case MXT_WAITING_BOOTLOAD_CMD: + case MXT_WAITING_FRAME_DATA: + case MXT_APP_CRC_FAIL: + val &= ~MXT_BOOT_STATUS_MASK; + break; + case MXT_FRAME_CRC_PASS: + if (val == MXT_FRAME_CRC_CHECK) { + goto recheck; + } else if (val == MXT_FRAME_CRC_FAIL) { + dev_err(dev, "Bootloader CRC fail\n"); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + if (val != state) { + dev_err(dev, "Invalid bootloader state %02X != %02X\n", + val, state); + return -EINVAL; + } + + return 0; +} + +static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock) +{ + int ret; + u8 buf[2]; + + if (unlock) { + buf[0] = MXT_UNLOCK_CMD_LSB; + buf[1] = MXT_UNLOCK_CMD_MSB; + } else { + buf[0] = 0x01; + buf[1] = 0x01; + } + + ret = mxt_bootloader_write(data, buf, 2); + if (ret) + return ret; + + return 0; +} + +static int __mxt_read_reg(struct i2c_client *client, + u16 reg, u16 len, void *val) +{ + struct i2c_msg xfer[2]; + u8 buf[2]; + int ret; + bool retry = false; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 2; + xfer[0].buf = buf; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = len; + xfer[1].buf = val; + +retry_read: + ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer)); + if (ret != ARRAY_SIZE(xfer)) { + if (!retry) { + dev_dbg(&client->dev, "%s: i2c retry\n", __func__); + msleep(MXT_WAKEUP_TIME); + retry = true; + goto retry_read; + } else { + dev_err(&client->dev, "%s: i2c transfer failed (%d)\n", + __func__, ret); + return -EIO; + } + } + + return 0; +} + +static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len, + const void *val) +{ + u8 *buf; + int count; + int ret; + bool retry = false; + + count = len + 2; + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + memcpy(&buf[2], val, len); + +retry_write: + ret = i2c_master_send(client, buf, count); + if (ret != count) { + if (!retry) { + dev_dbg(&client->dev, "%s: i2c retry\n", __func__); + msleep(MXT_WAKEUP_TIME); + retry = true; + goto retry_write; + } else { + dev_err(&client->dev, "%s: i2c send failed (%d)\n", + __func__, ret); + ret = -EIO; + } + } else { + ret = 0; + } + + kfree(buf); + return ret; +} + +static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val) +{ + return __mxt_write_reg(client, reg, 1, &val); +} + +static struct mxt_object * +mxt_get_object(struct mxt_data *data, u8 type) +{ + struct mxt_object *object; + int i; + + for (i = 0; i < data->info->object_num; i++) { + object = data->object_table + i; + if (object->type == type) + return object; + } + + dev_warn(&data->client->dev, "Invalid object type T%u\n", type); + return NULL; +} + +static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + u8 status = msg[1]; + u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16); + + if (crc != data->config_crc) { + data->config_crc = crc; + dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc); + complete(&data->crc_completion); + } + + /* Detect transition out of reset */ + if ((data->t6_status & MXT_T6_STATUS_RESET) && + !(status & MXT_T6_STATUS_RESET)) + complete(&data->reset_completion); + + /* Output debug if status has changed */ + if (status != data->t6_status) + dev_dbg(dev, "T6 Status 0x%02X%s%s%s%s%s%s%s\n", + status, + (status == 0) ? " OK" : "", + (status & MXT_T6_STATUS_RESET) ? " RESET" : "", + (status & MXT_T6_STATUS_OFL) ? " OFL" : "", + (status & MXT_T6_STATUS_SIGERR) ? " SIGERR" : "", + (status & MXT_T6_STATUS_CAL) ? " CAL" : "", + (status & MXT_T6_STATUS_CFGERR) ? " CFGERR" : "", + (status & MXT_T6_STATUS_COMSERR) ? " COMSERR" : ""); + + /* Save current status */ + data->t6_status = status; +} + +static void mxt_input_button(struct mxt_data *data, u8 *message) +{ + struct input_dev *input = data->input_dev; + const struct mxt_platform_data *pdata = data->pdata; + bool button; + int i; + + /* do not report events if input device not yet registered */ + if (!data->enable_reporting) + return; + + /* Active-low switch */ + for (i = 0; i < pdata->t19_num_keys; i++) { + if (pdata->t19_keymap[i] == KEY_RESERVED) + continue; + button = !(message[1] & (1 << i)); + input_report_key(input, pdata->t19_keymap[i], button); + } +} + +static void mxt_input_sync(struct input_dev *input_dev) +{ + input_mt_report_pointer_emulation(input_dev, false); + input_sync(input_dev); +} + +static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev = data->input_dev; + int id; + u8 status; + int x; + int y; + int area; + int amplitude; + u8 vector; + int tool; + + /* do not report events if input device not yet registered */ + if (!data->enable_reporting) + return; + + id = message[0] - data->T9_reportid_min; + status = message[1]; + x = (message[2] << 4) | ((message[4] >> 4) & 0xf); + y = (message[3] << 4) | ((message[4] & 0xf)); + + /* Handle 10/12 bit switching */ + if (data->max_x < 1024) + x >>= 2; + if (data->max_y < 1024) + y >>= 2; + + area = message[5]; + + amplitude = message[6]; + vector = message[7]; + + dev_dbg(dev, + "[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u vector: %02X\n", + id, + (status & MXT_T9_DETECT) ? 'D' : '.', + (status & MXT_T9_PRESS) ? 'P' : '.', + (status & MXT_T9_RELEASE) ? 'R' : '.', + (status & MXT_T9_MOVE) ? 'M' : '.', + (status & MXT_T9_VECTOR) ? 'V' : '.', + (status & MXT_T9_AMP) ? 'A' : '.', + (status & MXT_T9_SUPPRESS) ? 'S' : '.', + (status & MXT_T9_UNGRIP) ? 'U' : '.', + x, y, area, amplitude, vector); + + input_mt_slot(input_dev, id); + + if (status & MXT_T9_DETECT) { + /* Multiple bits may be set if the host is slow to read the + * status messages, indicating all the events that have + * happened */ + if (status & MXT_T9_RELEASE) { + input_mt_report_slot_state(input_dev, + MT_TOOL_FINGER, 0); + mxt_input_sync(input_dev); + } + + /* A reported size of zero indicates that the reported touch + * is a stylus from a linked Stylus T47 object. */ + if (area == 0) { + area = MXT_TOUCH_MAJOR_T47_STYLUS; + tool = MT_TOOL_PEN; + } else { + tool = MT_TOOL_FINGER; + } + + /* Touch active */ + input_mt_report_slot_state(input_dev, tool, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area); + input_report_abs(input_dev, ABS_MT_ORIENTATION, vector); + } else { + /* Touch no longer active, close out slot */ + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); + } + + data->update_input = true; +} + +static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev = data->input_dev; + int id; + u8 status; + int x; + int y; + int tool; + + /* do not report events if input device not yet registered */ + if (!data->enable_reporting) + return; + + id = message[0] - data->T100_reportid_min - 2; + + /* ignore SCRSTATUS events */ + if (id < 0) + return; + + status = message[1]; + x = (message[3] << 8) | message[2]; + y = (message[5] << 8) | message[4]; + + dev_dbg(dev, + "[%u] status:%02X x:%u y:%u area:%02X amp:%02X vec:%02X\n", + id, + status, + x, y, + (data->t100_aux_area) ? message[data->t100_aux_area] : 0, + (data->t100_aux_ampl) ? message[data->t100_aux_ampl] : 0, + (data->t100_aux_vect) ? message[data->t100_aux_vect] : 0); + + input_mt_slot(input_dev, id); + + if (status & MXT_T100_DETECT) { + /* A reported size of zero indicates that the reported touch + * is a stylus from a linked Stylus T47 object. */ + if ((status & MXT_T100_TYPE_MASK) == MXT_T100_TYPE_STYLUS) + tool = MT_TOOL_PEN; + else + tool = MT_TOOL_FINGER; + + /* Touch active */ + input_mt_report_slot_state(input_dev, tool, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + + if (data->t100_aux_ampl) + input_report_abs(input_dev, ABS_MT_PRESSURE, + message[data->t100_aux_ampl]); + else + input_report_abs(input_dev, ABS_MT_PRESSURE, 255); + + if (data->t100_aux_area) { + if (tool == MT_TOOL_PEN) + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + MXT_TOUCH_MAJOR_T47_STYLUS); + else + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + message[data->t100_aux_area]); + } + + if (data->t100_aux_vect) + input_report_abs(input_dev, ABS_MT_ORIENTATION, + message[data->t100_aux_vect]); + } else { + /* Touch no longer active, close out slot */ + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); + } + + data->update_input = true; +} + + +static void mxt_proc_t15_messages(struct mxt_data *data, u8 *msg) +{ + struct input_dev *input_dev = data->input_dev; + struct device *dev = &data->client->dev; + int key; + bool curr_state, new_state; + bool sync = false; + unsigned long keystates = le32_to_cpu(msg[2]); + + /* do not report events if input device not yet registered */ + if (!data->enable_reporting) + return; + + for (key = 0; key < data->pdata->t15_num_keys; key++) { + curr_state = test_bit(key, &data->t15_keystatus); + new_state = test_bit(key, &keystates); + + if (!curr_state && new_state) { + dev_dbg(dev, "T15 key press: %u\n", key); + __set_bit(key, &data->t15_keystatus); + input_event(input_dev, EV_KEY, + data->pdata->t15_keymap[key], 1); + sync = true; + } else if (curr_state && !new_state) { + dev_dbg(dev, "T15 key release: %u\n", key); + __clear_bit(key, &data->t15_keystatus); + input_event(input_dev, EV_KEY, + data->pdata->t15_keymap[key], 0); + sync = true; + } + } + + if (sync) + input_sync(input_dev); +} + +static void mxt_proc_t42_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + u8 status = msg[1]; + + if (status & MXT_T42_MSG_TCHSUP) + dev_info(dev, "T42 suppress\n"); + else + dev_info(dev, "T42 normal\n"); +} + +static int mxt_proc_t48_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + u8 status, state; + + status = msg[1]; + state = msg[4]; + + dev_dbg(dev, "T48 state %d status %02X %s%s%s%s%s\n", + state, + status, + (status & 0x01) ? "FREQCHG " : "", + (status & 0x02) ? "APXCHG " : "", + (status & 0x04) ? "ALGOERR " : "", + (status & 0x10) ? "STATCHG " : "", + (status & 0x20) ? "NLVLCHG " : ""); + + return 0; +} + +static void mxt_proc_t63_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev = data->input_dev; + u8 id; + u16 x, y; + u8 pressure; + + /* do not report events if input device not yet registered */ + if (!data->enable_reporting) + return; + + /* stylus slots come after touch slots */ + id = data->num_touchids + (msg[0] - data->T63_reportid_min); + + if (id < 0 || id > (data->num_touchids + data->num_stylusids)) { + dev_err(dev, "invalid stylus id %d, max slot is %d\n", + id, data->num_stylusids); + return; + } + + x = msg[3] | (msg[4] << 8); + y = msg[5] | (msg[6] << 8); + pressure = msg[7] & MXT_T63_STYLUS_PRESSURE_MASK; + + dev_dbg(dev, + "[%d] %c%c%c%c x: %d y: %d pressure: %d stylus:%c%c%c%c\n", + id, + (msg[1] & MXT_T63_STYLUS_SUPPRESS) ? 'S' : '.', + (msg[1] & MXT_T63_STYLUS_MOVE) ? 'M' : '.', + (msg[1] & MXT_T63_STYLUS_RELEASE) ? 'R' : '.', + (msg[1] & MXT_T63_STYLUS_PRESS) ? 'P' : '.', + x, y, pressure, + (msg[2] & MXT_T63_STYLUS_BARREL) ? 'B' : '.', + (msg[2] & MXT_T63_STYLUS_ERASER) ? 'E' : '.', + (msg[2] & MXT_T63_STYLUS_TIP) ? 'T' : '.', + (msg[2] & MXT_T63_STYLUS_DETECT) ? 'D' : '.'); + + input_mt_slot(input_dev, id); + + if (msg[2] & MXT_T63_STYLUS_DETECT) { + input_mt_report_slot_state(input_dev, MT_TOOL_PEN, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_PRESSURE, pressure); + } else { + input_mt_report_slot_state(input_dev, MT_TOOL_PEN, 0); + } + + input_report_key(input_dev, BTN_STYLUS, + (msg[2] & MXT_T63_STYLUS_ERASER)); + input_report_key(input_dev, BTN_STYLUS2, + (msg[2] & MXT_T63_STYLUS_BARREL)); + + mxt_input_sync(input_dev); +} + +static int mxt_proc_message(struct mxt_data *data, u8 *message) +{ + u8 report_id = message[0]; + bool dump = data->debug_enabled; + + if (report_id == MXT_RPTID_NOMSG) + return 0; + + if (report_id == data->T6_reportid) { + mxt_proc_t6_messages(data, message); + } else if (report_id >= data->T9_reportid_min + && report_id <= data->T9_reportid_max) { + mxt_proc_t9_message(data, message); + } else if (report_id >= data->T100_reportid_min + && report_id <= data->T100_reportid_max) { + mxt_proc_t100_message(data, message); + } else if (report_id == data->T19_reportid) { + mxt_input_button(data, message); + data->update_input = true; + } else if (report_id >= data->T63_reportid_min + && report_id <= data->T63_reportid_max) { + mxt_proc_t63_messages(data, message); + } else if (report_id >= data->T42_reportid_min + && report_id <= data->T42_reportid_max) { + mxt_proc_t42_messages(data, message); + } else if (report_id == data->T48_reportid) { + mxt_proc_t48_messages(data, message); + } else if (report_id >= data->T15_reportid_min + && report_id <= data->T15_reportid_max) { + mxt_proc_t15_messages(data, message); + } else { + dump = true; + } + + if (dump) + mxt_dump_message(data, message); + + if (data->debug_v2_enabled) + mxt_debug_msg_add(data, message); + + return 1; +} + +static int mxt_read_and_process_messages(struct mxt_data *data, u8 count) +{ + struct device *dev = &data->client->dev; + int ret; + int i; + u8 num_valid = 0; + + /* Safety check for msg_buf */ + if (count > data->max_reportid) + return -EINVAL; + + /* Process remaining messages if necessary */ + ret = __mxt_read_reg(data->client, data->T5_address, + data->T5_msg_size * count, data->msg_buf); + if (ret) { + dev_err(dev, "Failed to read %u messages (%d)\n", count, ret); + return ret; + } + + for (i = 0; i < count; i++) { + ret = mxt_proc_message(data, + data->msg_buf + data->T5_msg_size * i); + + if (ret == 1) + num_valid++; + } + + /* return number of messages read */ + return num_valid; +} + +static irqreturn_t mxt_process_messages_t44(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + u8 count, num_left; + + /* Read T44 and T5 together */ + ret = __mxt_read_reg(data->client, data->T44_address, + data->T5_msg_size + 1, data->msg_buf); + if (ret) { + dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret); + return IRQ_NONE; + } + + count = data->msg_buf[0]; + + if (count == 0) { + dev_warn(dev, "Interrupt triggered but zero messages\n"); + return IRQ_NONE; + } else if (count > data->max_reportid) { + dev_err(dev, "T44 count %d exceeded max report id\n", count); + count = data->max_reportid; + } + + /* Process first message */ + ret = mxt_proc_message(data, data->msg_buf + 1); + if (ret < 0) { + dev_warn(dev, "Unexpected invalid message\n"); + return IRQ_NONE; + } + + num_left = count - 1; + + /* Process remaining messages if necessary */ + if (num_left) { + ret = mxt_read_and_process_messages(data, num_left); + if (ret < 0) + goto end; + else if (ret != num_left) + dev_warn(dev, "Unexpected invalid message\n"); + } + +end: + if (data->update_input) { + mxt_input_sync(data->input_dev); + data->update_input = false; + } + + return IRQ_HANDLED; +} + +static int mxt_process_messages_until_invalid(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int count, read; + u8 tries = 2; + + count = data->max_reportid; + + /* Read messages until we force an invalid */ + do { + read = mxt_read_and_process_messages(data, count); + if (read < count) + return 0; + } while (--tries); + + if (data->update_input) { + mxt_input_sync(data->input_dev); + data->update_input = false; + } + + dev_err(dev, "CHG pin isn't cleared\n"); + return -EBUSY; +} + +static irqreturn_t mxt_process_messages(struct mxt_data *data) +{ + int total_handled, num_handled; + u8 count = data->last_message_count; + + if (count < 1 || count > data->max_reportid) + count = 1; + + /* include final invalid message */ + total_handled = mxt_read_and_process_messages(data, count + 1); + if (total_handled < 0) + return IRQ_NONE; + /* if there were invalid messages, then we are done */ + else if (total_handled <= count) + goto update_count; + + /* read two at a time until an invalid message or else we reach + * reportid limit */ + do { + num_handled = mxt_read_and_process_messages(data, 2); + if (num_handled < 0) + return IRQ_NONE; + + total_handled += num_handled; + + if (num_handled < 2) + break; + } while (total_handled < data->num_touchids); + +update_count: + data->last_message_count = total_handled; + + if (data->enable_reporting && data->update_input) { + mxt_input_sync(data->input_dev); + data->update_input = false; + } + + return IRQ_HANDLED; +} + +#if defined(CONFIG_SECURE_TOUCH) +static void mxt_secure_touch_notify(struct mxt_data *data) +{ + sysfs_notify(&data->client->dev.kobj, NULL, "secure_touch"); +} + +static irqreturn_t mxt_filter_interrupt(struct mxt_data *data) +{ + if (atomic_read(&data->st_enabled)) { + if (atomic_cmpxchg(&data->st_pending_irqs, 0, 1) == 0) + mxt_secure_touch_notify(data); + return IRQ_HANDLED; + } + return IRQ_NONE; +} +#else +static irqreturn_t mxt_filter_interrupt(struct mxt_data *data) +{ + return IRQ_NONE; +} +#endif + +static irqreturn_t mxt_interrupt(int irq, void *dev_id) +{ + struct mxt_data *data = dev_id; + + if (data->in_bootloader) { + /* bootloader state transition completion */ + complete(&data->bl_completion); + return IRQ_HANDLED; + } + + if (IRQ_HANDLED == mxt_filter_interrupt(data)) + return IRQ_HANDLED; + + if (!data->object_table) + return IRQ_NONE; + + if (data->T44_address) { + return mxt_process_messages_t44(data); + } else { + return mxt_process_messages(data); + } +} + +static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset, + u8 value, bool wait) +{ + u16 reg; + u8 command_register; + int timeout_counter = 0; + int ret; + + reg = data->T6_address + cmd_offset; + + ret = mxt_write_reg(data->client, reg, value); + if (ret) + return ret; + + if (!wait) + return 0; + + do { + msleep(20); + ret = __mxt_read_reg(data->client, reg, 1, &command_register); + if (ret) + return ret; + } while ((command_register != 0) && (timeout_counter++ <= 100)); + + if (timeout_counter > 100) { + dev_err(&data->client->dev, "Command failed!\n"); + return -EIO; + } + + return 0; +} + +static int mxt_soft_reset(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int ret = 0; + + dev_info(dev, "Resetting chip\n"); + + reinit_completion(&data->reset_completion); + + ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false); + if (ret) + return ret; + + ret = mxt_wait_for_completion(data, &data->reset_completion, + MXT_RESET_TIMEOUT); + if (ret) + return ret; + + return 0; +} + +static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) +{ + /* on failure, CRC is set to 0 and config will always be downloaded */ + data->config_crc = 0; + reinit_completion(&data->crc_completion); + + mxt_t6_command(data, cmd, value, true); + + /* Wait for crc message. On failure, CRC is set to 0 and config will + * always be downloaded */ + mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); +} + +static void mxt_calc_crc24(u32 *crc, u8 firstbyte, u8 secondbyte) +{ + static const unsigned int crcpoly = 0x80001B; + u32 result; + u32 data_word; + + data_word = (secondbyte << 8) | firstbyte; + result = ((*crc << 1) ^ data_word); + + if (result & 0x1000000) + result ^= crcpoly; + + *crc = result; +} + +static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off) +{ + u32 crc = 0; + u8 *ptr = base + start_off; + u8 *last_val = base + end_off - 1; + + if (end_off < start_off) + return -EINVAL; + + while (ptr < last_val) { + mxt_calc_crc24(&crc, *ptr, *(ptr + 1)); + ptr += 2; + } + + /* if len is odd, fill the last byte with 0 */ + if (ptr == last_val) + mxt_calc_crc24(&crc, *ptr, 0); + + /* Mask to 24-bit */ + crc &= 0x00FFFFFF; + + return crc; +} + +static int mxt_check_retrigen(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + int val; + + if (data->pdata->irqflags & IRQF_TRIGGER_LOW) + return 0; + + if (data->T18_address) { + error = __mxt_read_reg(client, + data->T18_address + MXT_COMMS_CTRL, + 1, &val); + if (error) + return error; + + if (val & MXT_COMMS_RETRIGEN) + return 0; + } + + dev_warn(&client->dev, "Enabling RETRIGEN workaround\n"); + data->use_retrigen_workaround = true; + return 0; +} + +static int mxt_init_t7_power_cfg(struct mxt_data *data); + +static int mxt_update_cfg_version(struct mxt_data *data) +{ + struct mxt_object *object; + int error; + + object = mxt_get_object(data, MXT_SPT_USERDATA_T38); + if (!object) + return -EINVAL; + + error = __mxt_read_reg(data->client, object->start_address, + sizeof(data->cfg_version), &data->cfg_version); + if (error) + return error; + + return 0; +} + +static int mxt_update_t100_resolution(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + struct mxt_object *object; + u16 range_x, range_y, temp; + u8 cfg, tchaux; + u8 aux; + bool update = false; + + object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100); + if (!object) + return -EINVAL; + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_XRANGE, + sizeof(range_x), &range_x); + if (error) + return error; + + le16_to_cpus(range_x); + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_YRANGE, + sizeof(range_y), &range_y); + if (error) + return error; + + le16_to_cpus(range_y); + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_CFG1, + 1, &cfg); + if (error) + return error; + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_TCHAUX, + 1, &tchaux); + if (error) + return error; + + /* Handle default values */ + if (range_x == 0) + range_x = 1023; + + /* Handle default values */ + if (range_x == 0) + range_x = 1023; + + if (range_y == 0) + range_y = 1023; + + dev_dbg(&client->dev, "initial x=%d y=%d\n", range_x, range_y); + + if (cfg & MXT_T100_CFG_SWITCHXY) { + dev_dbg(&client->dev, "flip x and y\n"); + temp = range_y; + range_y = range_x; + range_x = temp; + } + + if (data->pdata->panel_maxx != range_x) { + if (cfg & MXT_T100_CFG_SWITCHXY) + range_x = data->pdata->panel_maxy; + else + range_x = data->pdata->panel_maxx; + cpu_to_le16s(range_x); + error = __mxt_write_reg(client, + object->start_address + MXT_T100_XRANGE, + sizeof(range_x), &range_x); + if (error) + return error; + dev_dbg(&client->dev, "panel maxx mismatch. update\n"); + update = true; + } + + if (data->pdata->panel_maxy != range_y) { + if (cfg & MXT_T100_CFG_SWITCHXY) + range_y = data->pdata->panel_maxx; + else + range_y = data->pdata->panel_maxy; + + cpu_to_le16s(range_y); + error = __mxt_write_reg(client, + object->start_address + MXT_T100_YRANGE, + sizeof(range_y), &range_y); + if (error) + return error; + dev_dbg(&client->dev, "panel maxy mismatch. update\n"); + update = true; + } + + if (update) { + mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); + + mxt_soft_reset(data); + } + + /* allocate aux bytes */ + aux = 6; + + if (tchaux & MXT_T100_TCHAUX_VECT) + data->t100_aux_vect = aux++; + + if (tchaux & MXT_T100_TCHAUX_AMPL) + data->t100_aux_ampl = aux++; + + if (tchaux & MXT_T100_TCHAUX_AREA) + data->t100_aux_area = aux++; + + dev_info(&client->dev, + "T100 Touchscreen size X%uY%u\n", range_x, range_y); + + return 0; +} + +static int mxt_update_t9_resolution(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + struct t9_range range; + unsigned char orient; + struct mxt_object *object; + u16 temp; + bool update = false; + + object = mxt_get_object(data, MXT_TOUCH_MULTI_T9); + if (!object) + return -EINVAL; + + error = __mxt_read_reg(client, + object->start_address + MXT_T9_RANGE, + sizeof(range), &range); + if (error) + return error; + + le16_to_cpus(range.x); + le16_to_cpus(range.y); + + error = __mxt_read_reg(client, + object->start_address + MXT_T9_ORIENT, + 1, &orient); + if (error) + return error; + + /* Handle default values */ + if (range.x == 0) + range.x = 1023; + + if (range.y == 0) + range.y = 1023; + + dev_dbg(&client->dev, "initial x=%d y=%d\n", range.x, range.y); + + if (orient & MXT_T9_ORIENT_SWITCH) { + dev_dbg(&client->dev, "flip x and y\n"); + temp = range.y; + range.y = range.x; + range.x = temp; + } + + if (data->pdata->panel_maxx != range.x) { + if (orient & MXT_T9_ORIENT_SWITCH) + range.x = data->pdata->panel_maxy; + else + range.x = data->pdata->panel_maxx; + cpu_to_le16s(range.x); + error = __mxt_write_reg(client, + object->start_address + MXT_T100_XRANGE, + sizeof(range.x), &range.x); + if (error) + return error; + dev_dbg(&client->dev, "panel maxx mismatch. update\n"); + update = true; + } + + if (data->pdata->panel_maxy != range.y) { + if (orient & MXT_T9_ORIENT_SWITCH) + range.y = data->pdata->panel_maxx; + else + range.y = data->pdata->panel_maxy; + + cpu_to_le16s(range.y); + error = __mxt_write_reg(client, + object->start_address + MXT_T100_YRANGE, + sizeof(range.y), &range.y); + if (error) + return error; + dev_dbg(&client->dev, "panel maxy mismatch. update\n"); + update = true; + } + + if (update) { + mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); + + mxt_soft_reset(data); + } + + dev_info(&client->dev, + "Touchscreen size X%uY%u\n", range.x, range.y); + + return 0; +} + +/* + * mxt_load_cfg - download configuration to chip + * + * Atmel Raw Config File Format + * + * The first four lines of the raw config file contain: + * 1) Version + * 2) Chip ID Information (first 7 bytes of device memory) + * 3) Chip Information Block 24-bit CRC Checksum + * 4) Chip Configuration 24-bit CRC Checksum + * + * The rest of the file consists of one line per object instance: + * <TYPE> <INSTANCE> <SIZE> <CONTENTS> + * + * <TYPE> - 2-byte object type as hex + * <INSTANCE> - 2-byte object instance number as hex + * <SIZE> - 2-byte object size as hex + * <CONTENTS> - array of <SIZE> 1-byte hex values + */ +static int mxt_load_cfg(struct mxt_data *data, bool force) +{ + struct device *dev = &data->client->dev; + struct mxt_info cfg_info; + struct mxt_object *object; + const struct firmware *cfg = NULL; + int ret = 0; + int offset; + int data_pos; + int byte_offset; + int i; + int cfg_start_ofs; + u32 info_crc, config_crc, calculated_crc; + u8 *config_mem; + unsigned int config_mem_size; + unsigned int type, instance, size; + u8 val; + int ver[3]; + u16 reg; + + if (!data->cfg_name) { + dev_dbg(dev, "Skipping cfg download\n"); + goto report_enable; + } + + ret = request_firmware(&cfg, data->cfg_name, dev); + if (ret < 0) { + dev_err(dev, "Failure to request config file %s\n", + data->cfg_name); + goto report_enable; + } + + mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); + + if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { + dev_err(dev, "Unrecognised config file\n"); + ret = -EINVAL; + goto release; + } + + data_pos = strlen(MXT_CFG_MAGIC); + + /* Load information block and check */ + for (i = 0; i < sizeof(struct mxt_info); i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + (unsigned char *)&cfg_info + i, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + goto release; + } + + data_pos += offset; + } + + if (cfg_info.family_id != data->info->family_id) { + dev_err(dev, "Family ID mismatch!\n"); + ret = -EINVAL; + goto release; + } + + if (cfg_info.variant_id != data->info->variant_id) { + dev_err(dev, "Variant ID mismatch!\n"); + ret = -EINVAL; + goto release; + } + + /* Read CRCs */ + ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format: failed to parse Info CRC\n"); + ret = -EINVAL; + goto release; + } + data_pos += offset; + + ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format: failed to parse Config CRC\n"); + ret = -EINVAL; + goto release; + } + data_pos += offset; + + /* Malloc memory to store configuration */ + cfg_start_ofs = MXT_OBJECT_START + + data->info->object_num * sizeof(struct mxt_object) + + MXT_INFO_CHECKSUM_SIZE; + config_mem_size = data->mem_size - cfg_start_ofs; + config_mem = kzalloc(config_mem_size, GFP_KERNEL); + if (!config_mem) { + dev_err(dev, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto release; + } + + while (data_pos < cfg->size) { + /* Read type, instance, length */ + ret = sscanf(cfg->data + data_pos, "%x %x %x%n", + &type, &instance, &size, &offset); + if (ret == 0) { + /* EOF */ + break; + } else if (ret != 3) { + dev_err(dev, "Bad format: failed to parse object\n"); + ret = -EINVAL; + goto release_mem; + } + data_pos += offset; + + if (type == MXT_SPT_USERDATA_T38) { + ret = sscanf(cfg->data + data_pos, "%x %x %x", + &ver[0], &ver[1], &ver[2]); + dev_info(dev, "controller version:%d.%d.%d file version:%d.%d.%d", + data->cfg_version[0], data->cfg_version[1], + data->cfg_version[2], ver[0], ver[1], ver[2]); + + if (force || data->fw_w_no_cfg_update) { + dev_info(dev, "starting force cfg update\n"); + } else if (data->cfg_version[0] != ver[0]) { + dev_info(dev, "cfg major versions do not match\n"); + ret = -EINVAL; + goto release_mem; + } else if (data->cfg_version[1] > ver[1]) { + dev_info(dev, "configuration is up-to-date\n"); + ret = -EINVAL; + goto release_mem; + } else if (data->cfg_version[1] == ver[1]) { + if (data->cfg_version[2] >= ver[2]) { + dev_info(dev, "configuration is up-to-date\n"); + ret = -EINVAL; + goto release_mem; + } + } else { + dev_info(dev, "starting cfg update\n"); + } + } + + object = mxt_get_object(data, type); + if (!object) { + /* Skip object */ + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + &val, + &offset); + data_pos += offset; + } + continue; + } + + if (size > mxt_obj_size(object)) { + /* Either we are in fallback mode due to wrong + * config or config from a later fw version, + * or the file is corrupt or hand-edited */ + dev_warn(dev, "Discarding %u byte(s) in T%u\n", + size - mxt_obj_size(object), type); + } else if (mxt_obj_size(object) > size) { + /* If firmware is upgraded, new bytes may be added to + * end of objects. It is generally forward compatible + * to zero these bytes - previous behaviour will be + * retained. However this does invalidate the CRC and + * will force fallback mode until the configuration is + * updated. We warn here but do nothing else - the + * malloc has zeroed the entire configuration. */ + dev_warn(dev, "Zeroing %u byte(s) in T%d\n", + mxt_obj_size(object) - size, type); + } + + if (instance >= mxt_obj_instances(object)) { + dev_err(dev, "Object instances exceeded!\n"); + ret = -EINVAL; + goto release_mem; + } + + reg = object->start_address + mxt_obj_size(object) * instance; + + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + &val, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format in T%d\n", type); + ret = -EINVAL; + goto release_mem; + } + data_pos += offset; + + if (i > mxt_obj_size(object)) + continue; + + byte_offset = reg + i - cfg_start_ofs; + + if ((byte_offset >= 0) + && (byte_offset <= config_mem_size)) { + *(config_mem + byte_offset) = val; + } else { + dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", + reg, object->type, byte_offset); + ret = -EINVAL; + goto release_mem; + } + } + } + + /* calculate crc of the received configs (not the raw config file) */ + if (data->T7_address < cfg_start_ofs) { + dev_err(dev, "Bad T7 address, T7addr = %x, config offset %x\n", + data->T7_address, cfg_start_ofs); + ret = 0; + goto release_mem; + } + + calculated_crc = mxt_calculate_crc(config_mem, + data->T7_address - cfg_start_ofs, + config_mem_size); + + if (config_crc > 0 && (config_crc != calculated_crc)) + dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n", + calculated_crc, config_crc); + + /* Write configuration as blocks */ + byte_offset = 0; + while (byte_offset < config_mem_size) { + size = config_mem_size - byte_offset; + + if (size > MXT_MAX_BLOCK_WRITE) + size = MXT_MAX_BLOCK_WRITE; + + ret = __mxt_write_reg(data->client, + cfg_start_ofs + byte_offset, + size, config_mem + byte_offset); + if (ret != 0) { + dev_err(dev, "Config write error, ret=%d\n", ret); + goto release_mem; + } + + byte_offset += size; + } + + mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); + + ret = mxt_check_retrigen(data); + if (ret) + goto release_mem; + + ret = mxt_soft_reset(data); + if (ret) + goto release_mem; + + dev_info(dev, "Config written\n"); + + /* T7 config may have changed */ + mxt_init_t7_power_cfg(data); + + mxt_update_cfg_version(data); + + /* update resolution if needed */ + if (data->T9_reportid_min) { + ret = mxt_update_t9_resolution(data); + if (ret) + goto release_mem; + } else if (data->T100_reportid_min) { + ret = mxt_update_t100_resolution(data); + if (ret) + goto release_mem; + } else { + dev_warn(dev, "No touch object detected\n"); + } + +release_mem: + kfree(config_mem); +release: + release_firmware(cfg); +report_enable: + data->enable_reporting = true; + + return ret; +} + +static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep) +{ + struct device *dev = &data->client->dev; + int error; + struct t7_config *new_config; + struct t7_config deepsleep = { .active = 0, .idle = 0 }; + + if (sleep == MXT_POWER_CFG_DEEPSLEEP) + new_config = &deepsleep; + else + new_config = &data->t7_cfg; + + error = __mxt_write_reg(data->client, data->T7_address, + sizeof(data->t7_cfg), + new_config); + if (error) + return error; + + dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n", + new_config->active, new_config->idle); + + return 0; +} + +static int mxt_init_t7_power_cfg(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int error; + bool retry = false; + +recheck: + error = __mxt_read_reg(data->client, data->T7_address, + sizeof(data->t7_cfg), &data->t7_cfg); + if (error) + return error; + + if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) { + if (!retry) { + dev_info(dev, "T7 cfg zero, resetting\n"); + mxt_soft_reset(data); + retry = true; + goto recheck; + } else { + dev_dbg(dev, "T7 cfg zero after reset, overriding\n"); + data->t7_cfg.active = 20; + data->t7_cfg.idle = 100; + return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + } + } else { + dev_info(dev, "Initialised power cfg: ACTV %d, IDLE %d\n", + data->t7_cfg.active, data->t7_cfg.idle); + return 0; + } +} + +static int mxt_acquire_irq(struct mxt_data *data) +{ + int error; + + enable_irq(data->irq); + + if (data->use_retrigen_workaround) { + error = mxt_process_messages_until_invalid(data); + if (error) + return error; + } + + return 0; +} + +static void mxt_free_input_device(struct mxt_data *data) +{ + if (data->input_dev) { + input_unregister_device(data->input_dev); + data->input_dev = NULL; + } +} + +static void mxt_free_object_table(struct mxt_data *data) +{ + mxt_debug_msg_remove(data); + + kfree(data->raw_info_block); + data->object_table = NULL; + data->info = NULL; + data->raw_info_block = NULL; + kfree(data->msg_buf); + data->msg_buf = NULL; + + mxt_free_input_device(data); + + data->enable_reporting = false; + data->T5_address = 0; + data->T5_msg_size = 0; + data->T6_reportid = 0; + data->T7_address = 0; + data->T9_reportid_min = 0; + data->T9_reportid_max = 0; + data->T15_reportid_min = 0; + data->T15_reportid_max = 0; + data->T18_address = 0; + data->T19_reportid = 0; + data->T42_reportid_min = 0; + data->T42_reportid_max = 0; + data->T44_address = 0; + data->T48_reportid = 0; + data->T63_reportid_min = 0; + data->T63_reportid_max = 0; + data->T100_reportid_min = 0; + data->T100_reportid_max = 0; + data->max_reportid = 0; +} + +static int mxt_parse_object_table(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int i; + u8 reportid; + u16 end_address; + + /* Valid Report IDs start counting from 1 */ + reportid = 1; + data->mem_size = 0; + for (i = 0; i < data->info->object_num; i++) { + struct mxt_object *object = data->object_table + i; + u8 min_id, max_id; + + le16_to_cpus(&object->start_address); + + if (object->num_report_ids) { + min_id = reportid; + reportid += object->num_report_ids * + mxt_obj_instances(object); + max_id = reportid - 1; + } else { + min_id = 0; + max_id = 0; + } + + dev_dbg(&data->client->dev, + "T%u Start:%u Size:%u Instances:%u Report IDs:%u-%u\n", + object->type, object->start_address, + mxt_obj_size(object), mxt_obj_instances(object), + min_id, max_id); + + switch (object->type) { + case MXT_GEN_MESSAGE_T5: + if (data->info->family_id == 0x80) { + /* On mXT224 read and discard unused CRC byte + * otherwise DMA reads are misaligned */ + data->T5_msg_size = mxt_obj_size(object); + } else { + /* CRC not enabled, so skip last byte */ + data->T5_msg_size = mxt_obj_size(object) - 1; + } + data->T5_address = object->start_address; + case MXT_GEN_COMMAND_T6: + data->T6_reportid = min_id; + data->T6_address = object->start_address; + break; + case MXT_GEN_POWER_T7: + data->T7_address = object->start_address; + break; + case MXT_TOUCH_MULTI_T9: + /* Only handle messages from first T9 instance */ + data->T9_reportid_min = min_id; + data->T9_reportid_max = min_id + + object->num_report_ids - 1; + data->num_touchids = object->num_report_ids; + break; + case MXT_TOUCH_KEYARRAY_T15: + data->T15_reportid_min = min_id; + data->T15_reportid_max = max_id; + break; + case MXT_SPT_COMMSCONFIG_T18: + data->T18_address = object->start_address; + break; + case MXT_PROCI_TOUCHSUPPRESSION_T42: + data->T42_reportid_min = min_id; + data->T42_reportid_max = max_id; + break; + case MXT_SPT_MESSAGECOUNT_T44: + data->T44_address = object->start_address; + break; + case MXT_SPT_GPIOPWM_T19: + data->T19_reportid = min_id; + break; + case MXT_PROCG_NOISESUPPRESSION_T48: + data->T48_reportid = min_id; + break; + case MXT_PROCI_ACTIVE_STYLUS_T63: + /* Only handle messages from first T63 instance */ + data->T63_reportid_min = min_id; + data->T63_reportid_max = min_id; + data->num_stylusids = 1; + break; + case MXT_TOUCH_MULTITOUCHSCREEN_T100: + data->T100_reportid_min = min_id; + data->T100_reportid_max = max_id; + /* first two report IDs reserved */ + data->num_touchids = object->num_report_ids - 2; + break; + } + + end_address = object->start_address + + mxt_obj_size(object) * mxt_obj_instances(object) - 1; + + if (end_address >= data->mem_size) + data->mem_size = end_address + 1; + } + + /* Store maximum reportid */ + data->max_reportid = reportid; + + /* If T44 exists, T5 position has to be directly after */ + if (data->T44_address && (data->T5_address != data->T44_address + 1)) { + dev_err(&client->dev, "Invalid T44 position\n"); + return -EINVAL; + } + + data->msg_buf = kcalloc(data->max_reportid, + data->T5_msg_size, GFP_KERNEL); + if (!data->msg_buf) { + dev_err(&client->dev, "Failed to allocate message buffer\n"); + return -ENOMEM; + } + + return 0; +} + +static int mxt_read_info_block(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + u16 size; + void *buf; + uint8_t num_objects; + u32 calculated_crc; + u8 *crc_ptr; + + /* If info block already allocated, free it */ + if (data->raw_info_block != NULL) + mxt_free_object_table(data); + + /* Read 7-byte ID information block starting at address 0 */ + size = sizeof(struct mxt_info); + buf = kzalloc(size, GFP_KERNEL); + if (!buf) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + error = __mxt_read_reg(client, 0, size, buf); + if (error) + goto err_free_mem; + + /* Resize buffer to give space for rest of info block */ + num_objects = ((struct mxt_info *)buf)->object_num; + size += (num_objects * sizeof(struct mxt_object)) + + MXT_INFO_CHECKSUM_SIZE; + + buf = krealloc(buf, size, GFP_KERNEL); + if (!buf) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + /* Read rest of info block */ + error = __mxt_read_reg(client, MXT_OBJECT_START, + size - MXT_OBJECT_START, + buf + MXT_OBJECT_START); + if (error) + goto err_free_mem; + + /* Extract & calculate checksum */ + crc_ptr = buf + size - MXT_INFO_CHECKSUM_SIZE; + data->info_crc = crc_ptr[0] | (crc_ptr[1] << 8) | (crc_ptr[2] << 16); + + calculated_crc = mxt_calculate_crc(buf, 0, + size - MXT_INFO_CHECKSUM_SIZE); + + /* CRC mismatch can be caused by data corruption due to I2C comms + * issue or else device is not using Object Based Protocol */ + if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) { + dev_err(&client->dev, + "Info Block CRC error calculated=0x%06X read=0x%06X\n", + data->info_crc, calculated_crc); + if (!data->pdata->ignore_crc) + goto err_free_mem; + } + + /* Save pointers in device data structure */ + data->raw_info_block = buf; + data->info = (struct mxt_info *)buf; + data->object_table = (struct mxt_object *)(buf + MXT_OBJECT_START); + + /* Parse object table information */ + error = mxt_parse_object_table(data); + if (error) { + dev_err(&client->dev, "Error %d reading object table\n", error); + goto err_free_obj_table; + } + + error = mxt_update_cfg_version(data); + if (error) + goto err_free_obj_table; + + dev_info(&client->dev, + "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u cfg version: %d.%d.%d\n", + data->info->family_id, data->info->variant_id, + data->info->version >> 4, data->info->version & 0xf, + data->info->build, data->info->object_num, + data->cfg_version[0], data->cfg_version[1], + data->cfg_version[2]); + + + return 0; + +err_free_obj_table: + mxt_free_object_table(data); +err_free_mem: + kfree(buf); + return error; +} + +static int mxt_pinctrl_init(struct mxt_data *data) +{ + int error; + + /* Get pinctrl if target uses pinctrl */ + data->ts_pinctrl = devm_pinctrl_get((&data->client->dev)); + if (IS_ERR_OR_NULL(data->ts_pinctrl)) { + dev_dbg(&data->client->dev, + "Device does not use pinctrl\n"); + error = PTR_ERR(data->ts_pinctrl); + data->ts_pinctrl = NULL; + return error; + } + + data->gpio_state_active + = pinctrl_lookup_state(data->ts_pinctrl, "pmx_ts_active"); + if (IS_ERR_OR_NULL(data->gpio_state_active)) { + dev_dbg(&data->client->dev, + "Can not get ts default pinstate\n"); + error = PTR_ERR(data->gpio_state_active); + data->ts_pinctrl = NULL; + return error; + } + + data->gpio_state_suspend + = pinctrl_lookup_state(data->ts_pinctrl, "pmx_ts_suspend"); + if (IS_ERR_OR_NULL(data->gpio_state_suspend)) { + dev_dbg(&data->client->dev, + "Can not get ts sleep pinstate\n"); + error = PTR_ERR(data->gpio_state_suspend); + data->ts_pinctrl = NULL; + return error; + } + + return 0; +} + +static int mxt_pinctrl_select(struct mxt_data *data, bool on) +{ + struct pinctrl_state *pins_state; + int error; + + pins_state = on ? data->gpio_state_active + : data->gpio_state_suspend; + if (!IS_ERR_OR_NULL(pins_state)) { + error = pinctrl_select_state(data->ts_pinctrl, pins_state); + if (error) { + dev_err(&data->client->dev, + "can not set %s pins\n", + on ? "pmx_ts_active" : "pmx_ts_suspend"); + return error; + } + } else { + dev_err(&data->client->dev, + "not a valid '%s' pinstate\n", + on ? "pmx_ts_active" : "pmx_ts_suspend"); + } + + return 0; +} + +static int mxt_gpio_enable(struct mxt_data *data, bool enable) +{ + const struct mxt_platform_data *pdata = data->pdata; + int error; + + if (data->ts_pinctrl) { + error = mxt_pinctrl_select(data, enable); + if (error < 0) + return error; + } + + if (enable) { + if (gpio_is_valid(pdata->gpio_irq)) { + error = gpio_request(pdata->gpio_irq, + "maxtouch_gpio_irq"); + if (error) { + dev_err(&data->client->dev, + "unable to request %d gpio(%d)\n", + pdata->gpio_irq, error); + return error; + } + error = gpio_direction_input(pdata->gpio_irq); + if (error) { + dev_err(&data->client->dev, + "unable to set dir for %d gpio(%d)\n", + data->pdata->gpio_irq, error); + goto err_free_irq; + } + + } else { + dev_err(&data->client->dev, "irq gpio not provided\n"); + return -EINVAL; + } + + if (gpio_is_valid(pdata->gpio_reset)) { + error = gpio_request(pdata->gpio_reset, + "maxtouch_gpio_reset"); + if (error) { + dev_err(&data->client->dev, + "unable to request %d gpio(%d)\n", + pdata->gpio_reset, error); + goto err_free_irq; + } + error = gpio_direction_output(pdata->gpio_reset, 0); + if (error) { + dev_err(&data->client->dev, + "unable to set dir for %d gpio(%d)\n", + pdata->gpio_reset, error); + goto err_free_reset; + } + } else { + dev_err(&data->client->dev, + "reset gpio not provided\n"); + goto err_free_irq; + } + + if (gpio_is_valid(pdata->gpio_i2cmode)) { + error = gpio_request(pdata->gpio_i2cmode, + "maxtouch_gpio_i2cmode"); + if (error) { + dev_err(&data->client->dev, + "unable to request %d gpio (%d)\n", + pdata->gpio_i2cmode, error); + goto err_free_reset; + } + error = gpio_direction_output(pdata->gpio_i2cmode, 1); + if (error) { + dev_err(&data->client->dev, + "unable to set dir for %d gpio (%d)\n", + pdata->gpio_i2cmode, error); + goto err_free_i2cmode; + } + } else { + dev_info(&data->client->dev, + "i2cmode gpio is not used\n"); + } + } else { + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + + if (gpio_is_valid(pdata->gpio_reset)) { + gpio_free(pdata->gpio_reset); + } + + if (gpio_is_valid(pdata->gpio_i2cmode)) { + gpio_set_value(pdata->gpio_i2cmode, 0); + gpio_free(pdata->gpio_i2cmode); + } + } + + return 0; + +err_free_i2cmode: + if (gpio_is_valid(pdata->gpio_i2cmode)) { + gpio_set_value(pdata->gpio_i2cmode, 0); + gpio_free(pdata->gpio_i2cmode); + } +err_free_reset: + if (gpio_is_valid(pdata->gpio_reset)) { + gpio_free(pdata->gpio_reset); + } +err_free_irq: + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + return error; +} + +static int mxt_regulator_enable(struct mxt_data *data) +{ + int error; + + if (!data->use_regulator) + return 0; + + gpio_set_value(data->pdata->gpio_reset, 0); + + error = regulator_enable(data->reg_vdd); + if (error) { + dev_err(&data->client->dev, + "vdd enable failed, error=%d\n", error); + return error; + } + error = regulator_enable(data->reg_avdd); + if (error) { + dev_err(&data->client->dev, + "avdd enable failed, error=%d\n", error); + goto err_dis_vdd; + } + + if (!IS_ERR(data->reg_xvdd)) { + error = regulator_enable(data->reg_xvdd); + if (error) { + dev_err(&data->client->dev, + "xvdd enable failed, error=%d\n", error); + goto err_dis_avdd; + } + } + msleep(MXT_REGULATOR_DELAY); + + reinit_completion(&data->bl_completion); + gpio_set_value(data->pdata->gpio_reset, 1); + mxt_wait_for_completion(data, &data->bl_completion, MXT_POWERON_DELAY); + + return 0; + +err_dis_avdd: + regulator_disable(data->reg_avdd); +err_dis_vdd: + regulator_disable(data->reg_vdd); + return error; +} + +static void mxt_regulator_disable(struct mxt_data *data) +{ + regulator_disable(data->reg_vdd); + regulator_disable(data->reg_avdd); + if (!IS_ERR(data->reg_xvdd)) + regulator_disable(data->reg_xvdd); +} + +static int mxt_regulator_configure(struct mxt_data *data, bool state) +{ + struct device *dev = &data->client->dev; + struct device_node *np = dev->of_node; + struct property *prop; + int error = 0; + + /* According to maXTouch power sequencing specification, RESET line + * must be kept low until some time after regulators come up to + * voltage */ + if (!data->pdata->gpio_reset) { + dev_warn(dev, "Must have reset GPIO to use regulator support\n"); + return 0; + } + + if (!state) + goto deconfig; + + data->reg_vdd = regulator_get(dev, "vdd"); + if (IS_ERR(data->reg_vdd)) { + error = PTR_ERR(data->reg_vdd); + dev_err(dev, "Error %d getting vdd regulator\n", error); + return error; + } + + if (regulator_count_voltages(data->reg_vdd) > 0) { + error = regulator_set_voltage(data->reg_vdd, + MXT_VDD_VTG_MIN_UV, MXT_VDD_VTG_MAX_UV); + if (error) { + dev_err(&data->client->dev, + "vdd set_vtg failed err=%d\n", error); + goto fail_put_vdd; + } + } + + data->reg_avdd = regulator_get(dev, "avdd"); + if (IS_ERR(data->reg_avdd)) { + error = PTR_ERR(data->reg_avdd); + dev_err(dev, "Error %d getting avdd regulator\n", error); + goto fail_put_vdd; + } + + if (regulator_count_voltages(data->reg_avdd) > 0) { + error = regulator_set_voltage(data->reg_avdd, + MXT_AVDD_VTG_MIN_UV, MXT_AVDD_VTG_MAX_UV); + if (error) { + dev_err(&data->client->dev, + "avdd set_vtg failed err=%d\n", error); + goto fail_put_avdd; + } + } + + data->reg_xvdd = regulator_get(dev, "xvdd"); + if (IS_ERR(data->reg_xvdd)) { + error = PTR_ERR(data->reg_xvdd); + prop = of_find_property(np, "xvdd-supply", NULL); + if (prop && (error == -EPROBE_DEFER)) + return -EPROBE_DEFER; + dev_info(dev, "xvdd regulator is not used\n"); + } else { + if (regulator_count_voltages(data->reg_xvdd) > 0) { + error = regulator_set_voltage(data->reg_xvdd, + MXT_XVDD_VTG_MIN_UV, MXT_XVDD_VTG_MAX_UV); + if (error) + dev_err(&data->client->dev, + "xvdd set_vtg failed err=%d\n", error); + } + } + + data->use_regulator = true; + + dev_dbg(dev, "Initialised regulators\n"); + return 0; + +deconfig: + if (!IS_ERR(data->reg_xvdd)) + regulator_put(data->reg_xvdd); +fail_put_avdd: + regulator_put(data->reg_avdd); +fail_put_vdd: + regulator_put(data->reg_vdd); + return error; +} + + +#if defined(CONFIG_SECURE_TOUCH) +static void mxt_secure_touch_stop(struct mxt_data *data, int blocking) +{ + if (atomic_read(&data->st_enabled)) { + atomic_set(&data->st_pending_irqs, -1); + mxt_secure_touch_notify(data); + if (blocking) + wait_for_completion_interruptible(&data->st_powerdown); + } +} +#else +static void mxt_secure_touch_stop(struct mxt_data *data, int blocking) +{ +} +#endif + +static void mxt_start(struct mxt_data *data) +{ + if (!data->suspended || data->in_bootloader) + return; + + mxt_secure_touch_stop(data, 1); + + /* enable gpios */ + mxt_gpio_enable(data, true); + + /* enable regulators */ + mxt_regulator_enable(data); + + /* Discard any messages still in message buffer from before + * chip went to sleep */ + mxt_process_messages_until_invalid(data); + + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + + /* Recalibrate since chip has been in deep sleep */ + mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false); + + mxt_acquire_irq(data); + data->enable_reporting = true; + data->suspended = false; +} + +static void mxt_reset_slots(struct mxt_data *data) +{ + struct input_dev *input_dev = data->input_dev; + unsigned int num_mt_slots; + int id; + + num_mt_slots = data->num_touchids + data->num_stylusids; + + for (id = 0; id < num_mt_slots; id++) { + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); + } + + mxt_input_sync(input_dev); +} + +static void mxt_stop(struct mxt_data *data) +{ + if (data->suspended || data->in_bootloader) + return; + + mxt_secure_touch_stop(data, 1); + + data->enable_reporting = false; + disable_irq(data->irq); + + /* put in deep sleep */ + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); + + /* disable regulators */ + mxt_regulator_disable(data); + + /* disable gpios */ + mxt_gpio_enable(data, false); + + mxt_reset_slots(data); + data->suspended = true; +} + + +static int mxt_input_open(struct input_dev *dev) +{ + struct mxt_data *data = input_get_drvdata(dev); + + mxt_start(data); + + return 0; +} + +static void mxt_input_close(struct input_dev *dev) +{ + struct mxt_data *data = input_get_drvdata(dev); + + mxt_stop(data); +} + +static int mxt_create_input_dev(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev; + int error; + unsigned int num_mt_slots; + int i; + + if (data->T9_reportid_min) { + error = mxt_update_t9_resolution(data); + if (error) { + dev_err(dev, "update resolution failed\n"); + return error; + } + } else if (data->T100_reportid_min) { + error = mxt_update_t100_resolution(data); + if (error) { + dev_err(dev, "update resolution failed\n"); + return error; + } + } else { + dev_warn(dev, "No touch object detected\n"); + } + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + input_dev->name = "Atmel maXTouch Touchscreen"; + input_dev->phys = data->phys; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = dev; + input_dev->open = mxt_input_open; + input_dev->close = mxt_input_close; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + num_mt_slots = data->num_touchids + data->num_stylusids; + error = input_mt_init_slots(input_dev, num_mt_slots, 0); + if (error) { + dev_err(dev, "Error %d initialising slots\n", error); + goto err_free_mem; + } + + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, MXT_MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + data->pdata->disp_minx, data->pdata->disp_maxx, + 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + data->pdata->disp_miny, data->pdata->disp_maxy, + 0, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, + 0, 255, 0, 0); + + /* For T63 active stylus */ + if (data->T63_reportid_min) { + input_set_capability(input_dev, EV_KEY, BTN_STYLUS); + input_set_capability(input_dev, EV_KEY, BTN_STYLUS2); + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); + } + + /* For T15 key array */ + if (data->pdata->key_codes && data->T15_reportid_min) { + data->t15_keystatus = 0; + + for (i = 0; i < data->pdata->t15_num_keys; i++) + input_set_capability(input_dev, EV_KEY, + data->pdata->t15_keymap[i]); + } + + input_set_drvdata(input_dev, data); + + error = input_register_device(input_dev); + if (error) { + dev_err(dev, "Error %d registering input device\n", error); + goto err_free_mem; + } + + data->input_dev = input_dev; + + return 0; + +err_free_mem: + input_free_device(input_dev); + return error; +} + +static int mxt_configure_objects(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + + error = mxt_debug_msg_init(data); + if (error) + return error; + + error = mxt_init_t7_power_cfg(data); + if (error) + dev_dbg(&client->dev, "Failed to initialize power cfg\n"); + + return 0; +} + +#ifdef CONFIG_OF +static int mxt_get_dt_coords(struct device *dev, char *name, + struct mxt_platform_data *pdata) +{ + u32 coords[MXT_COORDS_ARR_SIZE]; + struct property *prop; + struct device_node *np = dev->of_node; + size_t coords_size; + int error; + + prop = of_find_property(np, name, NULL); + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + coords_size = prop->length / sizeof(u32); + if (coords_size != MXT_COORDS_ARR_SIZE) { + dev_err(dev, "invalid %s\n", name); + return -EINVAL; + } + + error = of_property_read_u32_array(np, name, coords, coords_size); + if (error && (error != -EINVAL)) { + dev_err(dev, "Unable to read %s\n", name); + return error; + } + + if (strcmp(name, "atmel,panel-coords") == 0) { + pdata->panel_minx = coords[0]; + pdata->panel_miny = coords[1]; + pdata->panel_maxx = coords[2]; + pdata->panel_maxy = coords[3]; + } else if (strcmp(name, "atmel,display-coords") == 0) { + pdata->disp_minx = coords[0]; + pdata->disp_miny = coords[1]; + pdata->disp_maxx = coords[2]; + pdata->disp_maxy = coords[3]; + } else { + dev_err(dev, "unsupported property %s\n", name); + return -EINVAL; + } + + return 0; +} + +static int mxt_search_fw_name(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + struct device_node *np = dev->of_node, *temp; + u32 temp_val; size_t len; + int rc; + + data->fw_name[0] = '\0'; + + if (data->in_bootloader) + return 0; + + for_each_child_of_node(np, temp) { + rc = of_property_read_u32(temp, "atmel,version", &temp_val); + if (rc) { + dev_err(dev, "Unable to read controller version\n"); + return rc; + } + + if (temp_val != data->info->version) + continue; + + rc = of_property_read_u32(temp, "atmel,build", &temp_val); + if (rc) { + dev_err(dev, "Unable to read build id\n"); + return rc; + } + + if (temp_val != data->info->build) + continue; + + rc = of_property_read_string(temp, "atmel,fw-name", + &data->pdata->fw_name); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw name\n"); + return rc; + } + + dev_dbg(dev, "fw name found(%s)\n", data->pdata->fw_name); + + if (data->pdata->fw_name) { + len = strlen(data->pdata->fw_name); + if (len > MXT_NAME_MAX_LEN - 1) { + dev_err(dev, "Invalid firmware name\n"); + return -EINVAL; + } + strlcpy(data->fw_name, data->pdata->fw_name, len + 1); + } + } + + return 0; +} + +static int mxt_parse_dt(struct device *dev, struct mxt_platform_data *pdata) +{ + int error; + u32 temp_val; + struct device_node *np = dev->of_node; + struct property *prop; + + error = mxt_get_dt_coords(dev, "atmel,panel-coords", pdata); + if (error) + return error; + + error = mxt_get_dt_coords(dev, "atmel,display-coords", pdata); + if (error) + return error; + + pdata->cfg_name = MXT_GEN_CFG; + error = of_property_read_string(np, "atmel,cfg-name", &pdata->cfg_name); + if (error && (error != -EINVAL)) { + dev_err(dev, "Unable to read cfg name\n"); + return error; + } + + /* reset, irq gpio info */ + pdata->gpio_reset = of_get_named_gpio_flags(np, "atmel,reset-gpio", + 0, &temp_val); + pdata->resetflags = temp_val; + pdata->gpio_irq = of_get_named_gpio_flags(np, "atmel,irq-gpio", + 0, &temp_val); + pdata->irqflags = temp_val; + pdata->gpio_i2cmode = of_get_named_gpio_flags(np, "atmel,i2cmode-gpio", + 0, &temp_val); + + pdata->ignore_crc = of_property_read_bool(np, "atmel,ignore-crc"); + + error = of_property_read_u32(np, "atmel,bl-addr", &temp_val); + if (error && (error != -EINVAL)) + dev_err(dev, "Unable to read bootloader address\n"); + else if (error != -EINVAL) + pdata->bl_addr = (u8) temp_val; + + /* keycodes for keyarray object */ + prop = of_find_property(np, "atmel,key-codes", NULL); + if (prop) { + pdata->key_codes = devm_kzalloc(dev, + sizeof(int) * MXT_KEYARRAY_MAX_KEYS, + GFP_KERNEL); + if (!pdata->key_codes) + return -ENOMEM; + if ((prop->length/sizeof(u32)) == MXT_KEYARRAY_MAX_KEYS) { + error = of_property_read_u32_array(np, + "atmel,key-codes", pdata->key_codes, + MXT_KEYARRAY_MAX_KEYS); + if (error) { + dev_err(dev, "Unable to read key codes\n"); + return error; + } + } else + return -EINVAL; + } + + return 0; +} +#else +static int mxt_parse_dt(struct device *dev, struct mxt_platform_data *pdata) +{ + return -ENODEV; +} + +static int mxt_search_fw_name(struct mxt_data *data) +{ + + return -ENODEV; +} +#endif + +static int mxt_initialize(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + bool alt_bootloader_addr = false; + bool retry = false; + +retry_info: + error = mxt_read_info_block(data); + if (error) { +retry_bootloader: + error = mxt_probe_bootloader(data, alt_bootloader_addr); + if (error) { + if (alt_bootloader_addr) { + /* Chip is not in appmode or bootloader mode */ + return error; + } + + dev_info(&client->dev, "Trying alternate bootloader address\n"); + alt_bootloader_addr = true; + goto retry_bootloader; + } else { + if (retry) { + dev_err(&client->dev, + "Could not recover device from " + "bootloader mode\n"); + /* this is not an error state, we can reflash + * from here */ + data->in_bootloader = true; + goto recover_bootloader; + } + + /* Attempt to exit bootloader into app mode */ + mxt_send_bootloader_cmd(data, false); + msleep(MXT_FW_RESET_TIME); + retry = true; + goto retry_info; + } + } + + error = mxt_check_retrigen(data); + if (error) + return error; + + error = mxt_acquire_irq(data); + if (error) + return error; + + error = mxt_configure_objects(data); + if (error) + return error; + +recover_bootloader: + error = mxt_create_input_dev(data); + if (error) { + dev_err(&client->dev, "Failed to create input dev\n"); + return error; + } + + data->enable_reporting = true; + + error = mxt_search_fw_name(data); + if (error) + dev_dbg(&client->dev, "firmware name search fail\n"); + + return 0; +} + +/* Firmware Version is returned as Major.Minor.Build */ +static ssize_t mxt_fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n", + data->info->version >> 4, data->info->version & 0xf, + data->info->build); +} + +/* Hardware Version is returned as FamilyID.VariantID */ +static ssize_t mxt_hw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%u.%u\n", + data->info->family_id, data->info->variant_id); +} + +/* Hardware Version is returned as FamilyID.VariantID */ +static ssize_t mxt_cfg_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%u.%u.%u\n", + data->cfg_version[0], data->cfg_version[1], + data->cfg_version[2]); +} + +static ssize_t mxt_show_instance(char *buf, int count, + struct mxt_object *object, int instance, + const u8 *val) +{ + int i; + + if (mxt_obj_instances(object) > 1) + count += scnprintf(buf + count, PAGE_SIZE - count, + "Instance %u\n", instance); + + for (i = 0; i < mxt_obj_size(object); i++) + count += scnprintf(buf + count, PAGE_SIZE - count, + "\t[%2u]: %02x (%d)\n", i, val[i], val[i]); + count += scnprintf(buf + count, PAGE_SIZE - count, "\n"); + + return count; +} + +static ssize_t mxt_object_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + struct mxt_object *object; + int count = 0; + int i, j; + int error; + u8 *obuf; + + /* Pre-allocate buffer large enough to hold max sized object. */ + obuf = kmalloc(256, GFP_KERNEL); + if (!obuf) + return -ENOMEM; + + error = 0; + for (i = 0; i < data->info->object_num; i++) { + object = data->object_table + i; + + if (!mxt_object_readable(object->type)) + continue; + + count += scnprintf(buf + count, PAGE_SIZE - count, + "T%u:\n", object->type); + + for (j = 0; j < mxt_obj_instances(object); j++) { + u16 size = mxt_obj_size(object); + u16 addr = object->start_address + j * size; + + error = __mxt_read_reg(data->client, addr, size, obuf); + if (error) + goto done; + + count = mxt_show_instance(buf, count, object, j, obuf); + } + } + +done: + kfree(obuf); + return error ?: count; +} + +static int mxt_check_firmware_format(struct device *dev, + const struct firmware *fw) +{ + unsigned int pos = 0; + char c; + + while (pos < fw->size) { + c = *(fw->data + pos); + + if (c < '0' || (c > '9' && c < 'A') || c > 'F') + return 0; + + pos++; + } + + /* To convert file try + * xxd -r -p mXTXXX__APP_VX-X-XX.enc > maxtouch.fw */ + dev_err(dev, "Aborting: firmware file must be in binary format\n"); + + return -1; +} + +static int mxt_load_fw(struct device *dev) +{ + struct mxt_data *data = dev_get_drvdata(dev); + const struct firmware *fw = NULL; + unsigned int frame_size; + unsigned int pos = 0; + unsigned int retry = 0; + unsigned int frame = 0; + int ret; + + ret = request_firmware(&fw, data->fw_name, dev); + if (ret) { + dev_err(dev, "Unable to open firmware %s\n", data->fw_name); + return ret; + } + + /* Check for incorrect enc file */ + ret = mxt_check_firmware_format(dev, fw); + if (ret) + goto release_firmware; + + if (data->suspended) { + if (data->use_regulator) + mxt_regulator_enable(data); + + enable_irq(data->irq); + data->suspended = false; + } + + if (!data->in_bootloader) { + /* Change to the bootloader mode */ + data->in_bootloader = true; + + ret = mxt_t6_command(data, MXT_COMMAND_RESET, + MXT_BOOT_VALUE, false); + if (ret) + goto release_firmware; + + msleep(MXT_RESET_TIME); + + /* At this stage, do not need to scan since we know + * family ID */ + ret = mxt_lookup_bootloader_address(data, 0); + if (ret) + goto release_firmware; + } else { + enable_irq(data->irq); + } + + mxt_free_object_table(data); + reinit_completion(&data->bl_completion); + + ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); + if (ret) { + /* Bootloader may still be unlocked from previous update + * attempt */ + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false); + if (ret) + goto disable_irq; + } else { + dev_info(dev, "Unlocking bootloader\n"); + + /* Unlock bootloader */ + ret = mxt_send_bootloader_cmd(data, true); + if (ret) + goto disable_irq; + } + + while (pos < fw->size) { + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true); + if (ret) + goto disable_irq; + + frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); + + /* Take account of CRC bytes */ + frame_size += 2; + + /* Write one frame to device */ + ret = mxt_bootloader_write(data, fw->data + pos, frame_size); + if (ret) + goto disable_irq; + + ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true); + if (ret) { + retry++; + + /* Back off by 20ms per retry */ + msleep(retry * 20); + + if (retry > 20) { + dev_err(dev, "Retry count exceeded\n"); + goto disable_irq; + } + } else { + retry = 0; + pos += frame_size; + frame++; + } + + if (frame % 50 == 0) + dev_info(dev, "Sent %d frames, %d/%zd bytes\n", + frame, pos, fw->size); + } + + /* Wait for flash. */ + ret = mxt_wait_for_completion(data, &data->bl_completion, + MXT_FW_RESET_TIME); + if (ret) + goto disable_irq; + + dev_info(dev, "Sent %d frames, %u bytes\n", frame, pos); + + /* Wait for device to reset. Some bootloader versions do not assert + * the CHG line after bootloading has finished, so ignore error */ + mxt_wait_for_completion(data, &data->bl_completion, + MXT_FW_RESET_TIME); + + data->in_bootloader = false; + +disable_irq: + disable_irq(data->irq); +release_firmware: + release_firmware(fw); + return ret; +} + +static ssize_t mxt_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int error; + + if (data->fw_name[0] == '\0') { + if (data->in_bootloader) + dev_info(dev, "Manual update needed\n"); + else + dev_info(dev, "firmware is up-to-date\n"); + return count; + } + + error = mxt_load_fw(dev); + if (error) { + dev_err(dev, "The firmware update failed(%d)\n", error); + count = error; + } else { + dev_info(dev, "The firmware update succeeded\n"); + + msleep(MXT_FW_RESET_TIME); + data->suspended = false; + + error = mxt_initialize(data); + if (error) + return error; + + data->fw_w_no_cfg_update = true; + } + + return count; +} + +static int mxt_update_cfg(struct mxt_data *data, bool force) +{ + int ret; + + if (data->in_bootloader) { + dev_err(&data->client->dev, "Not in appmode\n"); + return -EINVAL; + } + + data->enable_reporting = false; + + if (data->suspended) { + if (data->use_regulator) + mxt_regulator_enable(data); + + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + + mxt_acquire_irq(data); + + data->suspended = false; + } + + /* load config */ + ret = mxt_load_cfg(data, force); + if (ret) + return ret; + + return 0; +} + +static ssize_t mxt_update_cfg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int ret; + + /* update config */ + ret = mxt_update_cfg(data, false); + if (ret) + return ret; + + return count; +} + +static ssize_t mxt_force_update_cfg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int ret; + + /* update force config */ + ret = mxt_update_cfg(data, true); + if (ret) + return ret; + + return count; +} + +static ssize_t mxt_debug_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + char c; + + c = data->debug_enabled ? '1' : '0'; + return scnprintf(buf, PAGE_SIZE, "%c\n", c); +} + +static ssize_t mxt_debug_notify_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "0\n"); +} + +static ssize_t mxt_debug_v2_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int i; + + if (sscanf(buf, "%u", &i) == 1 && i < 2) { + if (i == 1) + mxt_debug_msg_enable(data); + else + mxt_debug_msg_disable(data); + + return count; + } else { + dev_dbg(dev, "debug_enabled write error\n"); + return -EINVAL; + } +} + +static ssize_t mxt_debug_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int i; + + if (sscanf(buf, "%u", &i) == 1 && i < 2) { + data->debug_enabled = (i == 1); + + dev_dbg(dev, "%s\n", i ? "debug enabled" : "debug disabled"); + return count; + } else { + dev_dbg(dev, "debug_enabled write error\n"); + return -EINVAL; + } +} + +static int mxt_check_mem_access_params(struct mxt_data *data, loff_t off, + size_t *count) +{ + if (off >= data->mem_size) + return -EIO; + + if (off + *count > data->mem_size) + *count = data->mem_size - off; + + if (*count > MXT_MAX_BLOCK_WRITE) + *count = MXT_MAX_BLOCK_WRITE; + + return 0; +} + +static ssize_t mxt_mem_access_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct mxt_data *data = dev_get_drvdata(dev); + int ret = 0; + + ret = mxt_check_mem_access_params(data, off, &count); + if (ret < 0) + return ret; + + if (count > 0) + ret = __mxt_read_reg(data->client, off, count, buf); + + return ret == 0 ? count : ret; +} + +static ssize_t mxt_mem_access_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct mxt_data *data = dev_get_drvdata(dev); + int ret = 0; + + ret = mxt_check_mem_access_params(data, off, &count); + if (ret < 0) + return ret; + + if (count > 0) + ret = __mxt_write_reg(data->client, off, count, buf); + + return ret == 0 ? count : 0; +} + +#if defined(CONFIG_SECURE_TOUCH) + +static int mxt_secure_touch_clk_prepare_enable( + struct mxt_data *data) +{ + int ret; + ret = clk_prepare_enable(data->iface_clk); + if (ret) { + dev_err(&data->client->dev, + "error on clk_prepare_enable(iface_clk):%d\n", ret); + return ret; + } + + ret = clk_prepare_enable(data->core_clk); + if (ret) { + clk_disable_unprepare(data->iface_clk); + dev_err(&data->client->dev, + "error clk_prepare_enable(core_clk):%d\n", ret); + } + return ret; +} + +static void mxt_secure_touch_clk_disable_unprepare( + struct mxt_data *data) +{ + clk_disable_unprepare(data->core_clk); + clk_disable_unprepare(data->iface_clk); +} + +static ssize_t mxt_secure_touch_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%d", atomic_read(&data->st_enabled)); +} +/* + * Accept only "0" and "1" valid values. + * "0" will reset the st_enabled flag, then wake up the reading process. + * The bus driver is notified via pm_runtime that it is not required to stay + * awake anymore. + * It will also make sure the queue of events is emptied in the controller, + * in case a touch happened in between the secure touch being disabled and + * the local ISR being ungated. + * "1" will set the st_enabled flag and clear the st_pending_irqs flag. + * The bus driver is requested via pm_runtime to stay awake. + */ +static ssize_t mxt_secure_touch_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + struct device *adapter = data->client->adapter->dev.parent; + unsigned long value; + int err = 0; + + if (count > 2) + return -EINVAL; + + err = kstrtoul(buf, 10, &value); + if (err != 0) + return err; + + if (!data->st_initialized) + return -EIO; + + err = count; + + switch (value) { + case 0: + if (atomic_read(&data->st_enabled) == 0) + break; + + mxt_secure_touch_clk_disable_unprepare(data); + pm_runtime_put_sync(adapter); + atomic_set(&data->st_enabled, 0); + mxt_secure_touch_notify(data); + mxt_interrupt(data->client->irq, data); + complete(&data->st_powerdown); + break; + case 1: + if (atomic_read(&data->st_enabled)) { + err = -EBUSY; + break; + } + + if (pm_runtime_get_sync(adapter) < 0) { + dev_err(&data->client->dev, "pm_runtime_get failed\n"); + err = -EIO; + break; + } + + if (mxt_secure_touch_clk_prepare_enable(data) < 0) { + pm_runtime_put_sync(adapter); + err = -EIO; + break; + } + reinit_completion(&data->st_powerdown); + atomic_set(&data->st_enabled, 1); + synchronize_irq(data->client->irq); + atomic_set(&data->st_pending_irqs, 0); + break; + default: + dev_err(&data->client->dev, "unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + + return err; +} + +static ssize_t mxt_secure_touch_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int val = 0; + + if (atomic_read(&data->st_enabled) == 0) + return -EBADF; + + if (atomic_cmpxchg(&data->st_pending_irqs, -1, 0) == -1) + return -EINVAL; + + if (atomic_cmpxchg(&data->st_pending_irqs, 1, 0) == 1) + val = 1; + + return scnprintf(buf, PAGE_SIZE, "%u", val); +} + +static DEVICE_ATTR(secure_touch_enable, S_IRUGO | S_IWUSR | S_IWGRP , + mxt_secure_touch_enable_show, + mxt_secure_touch_enable_store); +static DEVICE_ATTR(secure_touch, S_IRUGO, mxt_secure_touch_show, NULL); +#endif + +static ssize_t mxt_fw_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + return snprintf(buf, MXT_NAME_MAX_LEN - 1, "%s\n", data->fw_name); +} + +static ssize_t mxt_fw_name_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct mxt_data *data = dev_get_drvdata(dev); + + if (size > MXT_NAME_MAX_LEN - 1) + return -EINVAL; + + strlcpy(data->fw_name, buf, size); + if (data->fw_name[size-1] == '\n') + data->fw_name[size-1] = 0; + + return size; +} + +static ssize_t mxt_cfg_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + return snprintf(buf, MXT_NAME_MAX_LEN - 1, "%s\n", data->cfg_name); +} + +static ssize_t mxt_cfg_name_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct mxt_data *data = dev_get_drvdata(dev); + + if (size > MXT_NAME_MAX_LEN - 1) + return -EINVAL; + + strlcpy(data->cfg_name, buf, size); + if (data->cfg_name[size-1] == '\n') + data->cfg_name[size-1] = 0; + + return size; +} + +static DEVICE_ATTR(fw_name, S_IWUSR | S_IRUSR, + mxt_fw_name_show, mxt_fw_name_store); +static DEVICE_ATTR(cfg_name, S_IWUSR | S_IRUSR, + mxt_cfg_name_show, mxt_cfg_name_store); +static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL); +static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL); +static DEVICE_ATTR(cfg_version, S_IRUGO, mxt_cfg_version_show, NULL); +static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL); +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store); +static DEVICE_ATTR(update_cfg, S_IWUSR, NULL, mxt_update_cfg_store); +static DEVICE_ATTR(force_update_cfg, S_IWUSR, NULL, mxt_force_update_cfg_store); +static DEVICE_ATTR(debug_v2_enable, S_IWUSR | S_IRUSR, NULL, mxt_debug_v2_enable_store); +static DEVICE_ATTR(debug_notify, S_IRUGO, mxt_debug_notify_show, NULL); +static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show, + mxt_debug_enable_store); + +static struct attribute *mxt_attrs[] = { + &dev_attr_fw_name.attr, + &dev_attr_cfg_name.attr, + &dev_attr_fw_version.attr, + &dev_attr_hw_version.attr, + &dev_attr_cfg_version.attr, + &dev_attr_object.attr, + &dev_attr_update_fw.attr, + &dev_attr_update_cfg.attr, + &dev_attr_force_update_cfg.attr, + &dev_attr_debug_enable.attr, + &dev_attr_debug_v2_enable.attr, + &dev_attr_debug_notify.attr, +#if defined(CONFIG_SECURE_TOUCH) + &dev_attr_secure_touch_enable.attr, + &dev_attr_secure_touch.attr, +#endif + NULL +}; + +static const struct attribute_group mxt_attr_group = { + .attrs = mxt_attrs, +}; + +#ifdef CONFIG_PM_SLEEP +static int mxt_suspend(struct device *dev) +{ + struct mxt_data *data = dev_get_drvdata(dev); + struct input_dev *input_dev = data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + mxt_stop(data); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int mxt_resume(struct device *dev) +{ + struct mxt_data *data = dev_get_drvdata(dev); + struct input_dev *input_dev = data->input_dev; + + mxt_secure_touch_stop(data, 1); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + mxt_start(data); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct mxt_data *mxt_dev_data = + container_of(self, struct mxt_data, fb_notif); + + if (evdata && evdata->data && mxt_dev_data && mxt_dev_data->client) { + if (event == FB_EARLY_EVENT_BLANK) + mxt_secure_touch_stop(mxt_dev_data, 0); + else if (event == FB_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) + mxt_resume(&mxt_dev_data->client->dev); + else if (*blank == FB_BLANK_POWERDOWN) + mxt_suspend(&mxt_dev_data->client->dev); + } + } + + return 0; +} +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void mxt_early_suspend(struct early_suspend *h) +{ + struct mxt_data *data = container_of(h, struct mxt_data, + early_suspend); + mxt_suspend(&data->client->dev); +} + +static void mxt_late_resume(struct early_suspend *h) +{ + struct mxt_data *data = container_of(h, struct mxt_data, + early_suspend); + mxt_resume(&data->client->dev); +} +#endif + +static const struct dev_pm_ops mxt_pm_ops = { +#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) + .suspend = mxt_suspend, + .resume = mxt_resume, +#endif +}; +#endif + +#if defined(CONFIG_SECURE_TOUCH) +static void mxt_secure_touch_init(struct mxt_data *data) +{ + int ret = 0; + data->st_initialized = 0; + init_completion(&data->st_powerdown); + /* Get clocks */ + data->core_clk = clk_get(&data->client->dev, "core_clk"); + if (IS_ERR(data->core_clk)) { + ret = PTR_ERR(data->core_clk); + dev_err(&data->client->dev, + "%s: error on clk_get(core_clk):%d\n", __func__, ret); + return; + } + + data->iface_clk = clk_get(&data->client->dev, "iface_clk"); + if (IS_ERR(data->iface_clk)) { + ret = PTR_ERR(data->iface_clk); + dev_err(&data->client->dev, + "%s: error on clk_get(iface_clk):%d\n", __func__, ret); + goto err_iface_clk; + } + + data->st_initialized = 1; + return; + +err_iface_clk: + clk_put(data->core_clk); + data->core_clk = NULL; +} +#else +static void mxt_secure_touch_init(struct mxt_data *data) +{ +} +#endif + +static int mxt_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mxt_data *data; + struct mxt_platform_data *pdata; + int error, len; + + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, + sizeof(struct mxt_platform_data), GFP_KERNEL); + if (!pdata) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + error = mxt_parse_dt(&client->dev, pdata); + if (error) + return error; + } else + pdata = client->dev.platform_data; + + if (!pdata) + return -EINVAL; + + data = devm_kzalloc(&client->dev, sizeof(struct mxt_data), GFP_KERNEL); + if (!data) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", + client->adapter->nr, client->addr); + + data->client = client; + data->irq = client->irq; + data->pdata = pdata; + i2c_set_clientdata(client, data); + + init_completion(&data->bl_completion); + init_completion(&data->reset_completion); + init_completion(&data->crc_completion); + mutex_init(&data->debug_msg_lock); + + if (data->pdata->cfg_name) { + len = strlen(data->pdata->cfg_name); + if (len > MXT_NAME_MAX_LEN - 1) { + dev_err(&client->dev, "Invalid config name\n"); + goto err_destroy_mutex; + } + + strlcpy(data->cfg_name, data->pdata->cfg_name, len + 1); + } + + error = mxt_pinctrl_init(data); + if (error) + dev_info(&client->dev, "No pinctrl support\n"); + + error = mxt_gpio_enable(data, true); + if (error) { + dev_err(&client->dev, "Failed to configure gpios\n"); + goto err_destroy_mutex; + } + data->irq = data->client->irq = + gpio_to_irq(data->pdata->gpio_irq); + + error = mxt_regulator_configure(data, true); + if (error) { + dev_err(&client->dev, "Failed to probe regulators\n"); + goto err_free_gpios; + } + + error = mxt_regulator_enable(data); + if (error) { + dev_err(&client->dev, "Error %d enabling regulators\n", error); + goto err_put_regs; + } + + error = mxt_initialize(data); + if (error) + goto err_free_regs; + + error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); + if (error) { + dev_err(&client->dev, "Failure %d creating sysfs group\n", + error); + goto err_free_object; + } + + sysfs_bin_attr_init(&data->mem_access_attr); + data->mem_access_attr.attr.name = "mem_access"; + data->mem_access_attr.attr.mode = S_IRUGO | S_IWUSR; + data->mem_access_attr.read = mxt_mem_access_read; + data->mem_access_attr.write = mxt_mem_access_write; + data->mem_access_attr.size = data->mem_size; + + if (sysfs_create_bin_file(&client->dev.kobj, + &data->mem_access_attr) < 0) { + dev_err(&client->dev, "Failed to create %s\n", + data->mem_access_attr.attr.name); + goto err_remove_sysfs_group; + } + + error = request_threaded_irq(data->irq, NULL, mxt_interrupt, + data->pdata->irqflags | IRQF_ONESHOT, + client->name, data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_remove_sysfs_group; + } + +#if defined(CONFIG_FB) + data->fb_notif.notifier_call = fb_notifier_callback; + + error = fb_register_client(&data->fb_notif); + + if (error) { + dev_err(&client->dev, "Unable to register fb_notifier: %d\n", + error); + goto err_free_irq; + } +#elif defined(CONFIG_HAS_EARLYSUSPEND) + data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + + MXT_SUSPEND_LEVEL; + data->early_suspend.suspend = mxt_early_suspend; + data->early_suspend.resume = mxt_late_resume; + register_early_suspend(&data->early_suspend); +#endif + + mxt_secure_touch_init(data); + + return 0; + +err_free_irq: + free_irq(data->irq, data); +err_remove_sysfs_group: + sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); +err_free_object: + mxt_free_object_table(data); +err_put_regs: + mxt_regulator_configure(data, false); +err_free_regs: + mxt_regulator_disable(data); +err_free_gpios: + mxt_gpio_enable(data, false); +err_destroy_mutex: + mutex_destroy(&data->debug_msg_lock); + return error; +} + +static int mxt_remove(struct i2c_client *client) +{ + struct mxt_data *data = i2c_get_clientdata(client); + + if (data->mem_access_attr.attr.name) + sysfs_remove_bin_file(&client->dev.kobj, + &data->mem_access_attr); + +#if defined(CONFIG_FB) + fb_unregister_client(&data->fb_notif); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&data->early_suspend); +#endif + sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); + free_irq(data->irq, data); + mxt_regulator_configure(data, false); + mxt_regulator_disable(data); + if (!IS_ERR(data->reg_xvdd)) + regulator_put(data->reg_xvdd); + regulator_put(data->reg_avdd); + regulator_put(data->reg_vdd); + mxt_free_object_table(data); + if (!dev_get_platdata(&data->client->dev)) + kfree(data->pdata); + kfree(data); + + return 0; +} + +static void mxt_shutdown(struct i2c_client *client) +{ + struct mxt_data *data = i2c_get_clientdata(client); + + disable_irq(data->irq); +} + +static const struct i2c_device_id mxt_id[] = { + { "qt602240_ts", 0 }, + { "atmel_mxt_ts", 0 }, + { "atmel_maxtouch_ts", 0 }, + { "atmel_mxt_tp", 0 }, + { "mXT224", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mxt_id); +#ifdef CONFIG_OF +static struct of_device_id mxt_match_table[] = { + { .compatible = "atmel,maxtouch-ts",}, + { }, +}; +#else +#define mxt_match_table NULL +#endif + +static struct i2c_driver mxt_driver = { + .driver = { + .name = "atmel_maxtouch_ts", + .owner = THIS_MODULE, +#ifdef CONFIG_PM_SLEEP + .pm = &mxt_pm_ops, +#endif + .of_match_table = mxt_match_table, + }, + .probe = mxt_probe, + .remove = mxt_remove, + .shutdown = mxt_shutdown, + .id_table = mxt_id, +}; + +static int __init mxt_init(void) +{ + return i2c_add_driver(&mxt_driver); +} + +static void __exit mxt_exit(void) +{ + i2c_del_driver(&mxt_driver); +} + +late_initcall(mxt_init); +module_exit(mxt_exit); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); +MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c new file mode 100644 index 000000000000..fc4bc283ccaf --- /dev/null +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -0,0 +1,2781 @@ +/* + * + * FocalTech ft5x06 TouchScreen driver. + * + * Copyright (c) 2010 Focal tech Ltd. + * Copyright (c) 2012-2016, The Linux Foundation. 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/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> +#include <linux/firmware.h> +#include <linux/debugfs.h> +#include <linux/input/ft5x06_ts.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/kobject.h> +#include <linux/sysfs.h> + + +#if defined(CONFIG_FB) +#include <linux/notifier.h> +#include <linux/fb.h> + +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include <linux/earlysuspend.h> +/* Early-suspend level */ +#define FT_SUSPEND_LEVEL 1 +#endif + +#if defined(CONFIG_FT_SECURE_TOUCH) +#include <linux/completion.h> +#include <linux/atomic.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> +static irqreturn_t ft5x06_ts_interrupt(int irq, void *data); +#endif + +#define FT_DRIVER_VERSION 0x02 + +#define FT_META_REGS 3 +#define FT_ONE_TCH_LEN 6 +#define FT_TCH_LEN(x) (FT_META_REGS + FT_ONE_TCH_LEN * x) + +#define FT_PRESS 0x7F +#define FT_MAX_ID 0x0F +#define FT_TOUCH_X_H_POS 3 +#define FT_TOUCH_X_L_POS 4 +#define FT_TOUCH_Y_H_POS 5 +#define FT_TOUCH_Y_L_POS 6 +#define FT_TD_STATUS 2 +#define FT_TOUCH_EVENT_POS 3 +#define FT_TOUCH_ID_POS 5 +#define FT_TOUCH_DOWN 0 +#define FT_TOUCH_CONTACT 2 + +/* register address*/ +#define FT_REG_DEV_MODE 0x00 +#define FT_DEV_MODE_REG_CAL 0x02 +#define FT_REG_ID 0xA3 +#define FT_REG_PMODE 0xA5 +#define FT_REG_FW_VER 0xA6 +#define FT_REG_FW_VENDOR_ID 0xA8 +#define FT_REG_POINT_RATE 0x88 +#define FT_REG_THGROUP 0x80 +#define FT_REG_ECC 0xCC +#define FT_REG_RESET_FW 0x07 +#define FT_REG_FW_MIN_VER 0xB2 +#define FT_REG_FW_SUB_MIN_VER 0xB3 + +/* gesture register address*/ +#define FT_REG_GESTURE_ENABLE 0xD0 +#define FT_REG_GESTURE_OUTPUT 0xD3 + +/* gesture register bits*/ +#define FT_GESTURE_DOUBLECLICK_COORD_X 100 +#define FT_GESTURE_DOUBLECLICK_COORD_Y 100 +#define FT_GESTURE_WAKEUP_TIMEOUT 500 +#define FT_GESTURE_DEFAULT_TRACKING_ID 0x0A +#define FT_GESTURE_DOUBLECLICK_ID 0x24 +#define FT_GESTURE_POINTER_NUM_MAX 128 +#define FT_GESTURE_POINTER_SIZEOF 4 +#define FT_GESTURE_ID_FLAG_SIZE 1 +#define FT_GESTURE_POINTER_NUM_FLAG_SIZE 1 +/* 6 bytes are taken to mark which gesture is supported in firmware */ +#define FT_GESTURE_SET_FLAG_SIZE 6 +#define I2C_TRANSFER_MAX_BYTE 255 +#define FT_GESTURE_DATA_HEADER (FT_GESTURE_ID_FLAG_SIZE + \ + FT_GESTURE_POINTER_NUM_FLAG_SIZE + \ + FT_GESTURE_SET_FLAG_SIZE) + +/* power register bits*/ +#define FT_PMODE_ACTIVE 0x00 +#define FT_PMODE_MONITOR 0x01 +#define FT_PMODE_STANDBY 0x02 +#define FT_PMODE_HIBERNATE 0x03 +#define FT_FACTORYMODE_VALUE 0x40 +#define FT_WORKMODE_VALUE 0x00 +#define FT_RST_CMD_REG1 0xFC +#define FT_RST_CMD_REG2 0xBC +#define FT_READ_ID_REG 0x90 +#define FT_ERASE_APP_REG 0x61 +#define FT_ERASE_PANEL_REG 0x63 +#define FT_FW_START_REG 0xBF + +#define FT_STATUS_NUM_TP_MASK 0x0F + +#define FT_VTG_MIN_UV 2600000 +#define FT_VTG_MAX_UV 3300000 +#define FT_I2C_VTG_MIN_UV 1800000 +#define FT_I2C_VTG_MAX_UV 1800000 + +#define FT_COORDS_ARR_SIZE 4 +#define MAX_BUTTONS 4 + +#define FT_8BIT_SHIFT 8 +#define FT_4BIT_SHIFT 4 +#define FT_FW_NAME_MAX_LEN 50 + +#define FT5316_ID 0x0A +#define FT5306I_ID 0x55 +#define FT6X06_ID 0x06 +#define FT6X36_ID 0x36 + +#define FT_UPGRADE_AA 0xAA +#define FT_UPGRADE_55 0x55 + +#define FT_FW_MIN_SIZE 8 +#define FT_FW_MAX_SIZE 32768 + +/* Firmware file is not supporting minor and sub minor so use 0 */ +#define FT_FW_FILE_MAJ_VER(x) ((x)->data[(x)->size - 2]) +#define FT_FW_FILE_MIN_VER(x) 0 +#define FT_FW_FILE_SUB_MIN_VER(x) 0 +#define FT_FW_FILE_VENDOR_ID(x) ((x)->data[(x)->size - 1]) + +#define FT_FW_FILE_MAJ_VER_FT6X36(x) ((x)->data[0x10a]) +#define FT_FW_FILE_VENDOR_ID_FT6X36(x) ((x)->data[0x108]) + +/** +* Application data verification will be run before upgrade flow. +* Firmware image stores some flags with negative and positive value +* in corresponding addresses, we need pick them out do some check to +* make sure the application data is valid. +*/ +#define FT_FW_CHECK(x, ts_data) \ + (ts_data->family_id == FT6X36_ID ? \ + (((x)->data[0x104] ^ (x)->data[0x105]) == 0xFF \ + && ((x)->data[0x106] ^ (x)->data[0x107]) == 0xFF) : \ + (((x)->data[(x)->size - 8] ^ (x)->data[(x)->size - 6]) == 0xFF \ + && ((x)->data[(x)->size - 7] ^ (x)->data[(x)->size - 5]) == 0xFF \ + && ((x)->data[(x)->size - 3] ^ (x)->data[(x)->size - 4]) == 0xFF)) + +#define FT_MAX_TRIES 5 +#define FT_RETRY_DLY 20 + +#define FT_MAX_WR_BUF 10 +#define FT_MAX_RD_BUF 2 +#define FT_FW_PKT_LEN 128 +#define FT_FW_PKT_META_LEN 6 +#define FT_FW_PKT_DLY_MS 20 +#define FT_FW_LAST_PKT 0x6ffa +#define FT_EARSE_DLY_MS 100 +#define FT_55_AA_DLY_NS 5000 + +#define FT_UPGRADE_LOOP 30 +#define FT_CAL_START 0x04 +#define FT_CAL_FIN 0x00 +#define FT_CAL_STORE 0x05 +#define FT_CAL_RETRY 100 +#define FT_REG_CAL 0x00 +#define FT_CAL_MASK 0x70 + +#define FT_INFO_MAX_LEN 512 + +#define FT_BLOADER_SIZE_OFF 12 +#define FT_BLOADER_NEW_SIZE 30 +#define FT_DATA_LEN_OFF_OLD_FW 8 +#define FT_DATA_LEN_OFF_NEW_FW 14 +#define FT_FINISHING_PKT_LEN_OLD_FW 6 +#define FT_FINISHING_PKT_LEN_NEW_FW 12 +#define FT_MAGIC_BLOADER_Z7 0x7bfa +#define FT_MAGIC_BLOADER_LZ4 0x6ffa +#define FT_MAGIC_BLOADER_GZF_30 0x7ff4 +#define FT_MAGIC_BLOADER_GZF 0x7bf4 + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" + +static irqreturn_t ft5x06_ts_interrupt(int irq, void *data); + +enum { + FT_BLOADER_VERSION_LZ4 = 0, + FT_BLOADER_VERSION_Z7 = 1, + FT_BLOADER_VERSION_GZF = 2, +}; + +enum { + FT_FT5336_FAMILY_ID_0x11 = 0x11, + FT_FT5336_FAMILY_ID_0x12 = 0x12, + FT_FT5336_FAMILY_ID_0x13 = 0x13, + FT_FT5336_FAMILY_ID_0x14 = 0x14, +}; + +#define FT_STORE_TS_INFO(buf, id, fw_maj, fw_min, fw_sub_min) \ + snprintf(buf, FT_INFO_MAX_LEN, \ + "vendor name = Focaltech\n" \ + "model = 0x%x\n" \ + "fw_version = %d.%d.%d\n", \ + id, fw_maj, fw_min, fw_sub_min) +#define FT_TS_INFO_SYSFS_DIR_NAME "ts_info" +static char *ts_info_buff; + +#define FT_STORE_TS_DBG_INFO(buf, id, name, max_tch, group_id, \ + fw_vkey_support, fw_name, fw_maj, fw_min, fw_sub_min) \ + snprintf(buf, FT_INFO_MAX_LEN, \ + "controller\t= focaltech\n" \ + "model\t\t= 0x%x\n" \ + "name\t\t= %s\n" \ + "max_touches\t= %d\n" \ + "drv_ver\t\t= 0x%x\n" \ + "group_id\t= 0x%x\n" \ + "fw_vkey_support\t= %s\n" \ + "fw_name\t\t= %s\n" \ + "fw_ver\t\t= %d.%d.%d\n", id, name, \ + max_tch, FT_DRIVER_VERSION, group_id, \ + fw_vkey_support, fw_name, fw_maj, fw_min, \ + fw_sub_min) + +#define FT_DEBUG_DIR_NAME "ts_debug" + +struct ft5x06_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct ft5x06_ts_platform_data *pdata; + struct ft5x06_gesture_platform_data *gesture_pdata; + struct regulator *vdd; + struct regulator *vcc_i2c; + struct mutex ft_clk_io_ctrl_mutex; + char fw_name[FT_FW_NAME_MAX_LEN]; + bool loading_fw; + u8 family_id; + struct dentry *dir; + u16 addr; + bool suspended; + char *ts_info; + u8 *tch_data; + u32 tch_data_len; + u8 fw_ver[3]; + u8 fw_vendor_id; + struct kobject *ts_info_kobj; +#if defined(CONFIG_FB) + struct work_struct fb_notify_work; + struct notifier_block fb_notif; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; +#if defined(CONFIG_FT_SECURE_TOUCH) + atomic_t st_enabled; + atomic_t st_pending_irqs; + struct completion st_powerdown; + struct completion st_irq_processed; + bool st_initialized; + struct clk *core_clk; + struct clk *iface_clk; +#endif +}; + +static int ft5x06_ts_start(struct device *dev); +static int ft5x06_ts_stop(struct device *dev); + +#if defined(CONFIG_FT_SECURE_TOUCH) +static void ft5x06_secure_touch_init(struct ft5x06_ts_data *data) +{ + data->st_initialized = 0; + + init_completion(&data->st_powerdown); + init_completion(&data->st_irq_processed); + + /* Get clocks */ + data->core_clk = devm_clk_get(&data->client->dev, "core_clk"); + if (IS_ERR(data->core_clk)) { + data->core_clk = NULL; + dev_warn(&data->client->dev, + "%s: core_clk is not defined\n", __func__); + } + + data->iface_clk = devm_clk_get(&data->client->dev, "iface_clk"); + if (IS_ERR(data->iface_clk)) { + data->iface_clk = NULL; + dev_warn(&data->client->dev, + "%s: iface_clk is not defined", __func__); + } + data->st_initialized = 1; +} + +static void ft5x06_secure_touch_notify(struct ft5x06_ts_data *data) +{ + sysfs_notify(&data->input_dev->dev.kobj, NULL, "secure_touch"); +} + +static irqreturn_t ft5x06_filter_interrupt(struct ft5x06_ts_data *data) +{ + if (atomic_read(&data->st_enabled)) { + if (atomic_cmpxchg(&data->st_pending_irqs, 0, 1) == 0) { + reinit_completion(&data->st_irq_processed); + ft5x06_secure_touch_notify(data); + wait_for_completion_interruptible( + &data->st_irq_processed); + } + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +/* + * 'blocking' variable will have value 'true' when we want to prevent the driver + * from accessing the xPU/SMMU protected HW resources while the session is + * active. + */ +static void ft5x06_secure_touch_stop(struct ft5x06_ts_data *data, bool blocking) +{ + if (atomic_read(&data->st_enabled)) { + atomic_set(&data->st_pending_irqs, -1); + ft5x06_secure_touch_notify(data); + if (blocking) + wait_for_completion_interruptible( + &data->st_powerdown); + } +} + +static int ft5x06_clk_prepare_enable(struct ft5x06_ts_data *data) +{ + int ret; + + ret = clk_prepare_enable(data->iface_clk); + if (ret) { + dev_err(&data->client->dev, + "error on clk_prepare_enable(iface_clk):%d\n", ret); + return ret; + } + + ret = clk_prepare_enable(data->core_clk); + if (ret) { + clk_disable_unprepare(data->iface_clk); + dev_err(&data->client->dev, + "error clk_prepare_enable(core_clk):%d\n", ret); + } + return ret; +} + +static void ft5x06_clk_disable_unprepare(struct ft5x06_ts_data *data) +{ + clk_disable_unprepare(data->core_clk); + clk_disable_unprepare(data->iface_clk); +} + +static int ft5x06_bus_get(struct ft5x06_ts_data *data) +{ + int retval; + + mutex_lock(&data->ft_clk_io_ctrl_mutex); + retval = pm_runtime_get_sync(data->client->adapter->dev.parent); + if (retval >= 0 && data->core_clk != NULL && data->iface_clk != NULL) { + retval = ft5x06_clk_prepare_enable(data); + if (retval) + pm_runtime_put_sync(data->client->adapter->dev.parent); + } + mutex_unlock(&data->ft_clk_io_ctrl_mutex); + return retval; +} + +static void ft5x06_bus_put(struct ft5x06_ts_data *data) +{ + mutex_lock(&data->ft_clk_io_ctrl_mutex); + if (data->core_clk != NULL && data->iface_clk != NULL) + ft5x06_clk_disable_unprepare(data); + pm_runtime_put_sync(data->client->adapter->dev.parent); + mutex_unlock(&data->ft_clk_io_ctrl_mutex); +} + +static ssize_t ft5x06_secure_touch_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d", atomic_read(&data->st_enabled)); +} + +/* + * Accept only "0" and "1" valid values. + * "0" will reset the st_enabled flag, then wake up the reading process and + * the interrupt handler. + * The bus driver is notified via pm_runtime that it is not required to stay + * awake anymore. + * It will also make sure the queue of events is emptied in the controller, + * in case a touch happened in between the secure touch being disabled and + * the local ISR being ungated. + * "1" will set the st_enabled flag and clear the st_pending_irqs flag. + * The bus driver is requested via pm_runtime to stay awake. + */ +static ssize_t ft5x06_secure_touch_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + unsigned long value; + int err = 0; + + if (count > 2) + return -EINVAL; + err = kstrtoul(buf, 10, &value); + if (err != 0) + return err; + + if (!data->st_initialized) + return -EIO; + + err = count; + switch (value) { + case 0: + if (atomic_read(&data->st_enabled) == 0) + break; + ft5x06_bus_put(data); + atomic_set(&data->st_enabled, 0); + ft5x06_secure_touch_notify(data); + complete(&data->st_irq_processed); + ft5x06_ts_interrupt(data->client->irq, data); + complete(&data->st_powerdown); + break; + + case 1: + if (atomic_read(&data->st_enabled)) { + err = -EBUSY; + break; + } + synchronize_irq(data->client->irq); + if (ft5x06_bus_get(data) < 0) { + dev_err(&data->client->dev, "ft5x06_bus_get failed\n"); + err = -EIO; + break; + } + reinit_completion(&data->st_powerdown); + reinit_completion(&data->st_irq_processed); + atomic_set(&data->st_enabled, 1); + atomic_set(&data->st_pending_irqs, 0); + break; + + default: + dev_err(&data->client->dev, "unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + return err; +} + +/* + * This function returns whether there are pending interrupts, or + * other error conditions that need to be signaled to the userspace library, + * according tot he following logic: + * - st_enabled is 0 if secure touch is not enabled, returning -EBADF + * - st_pending_irqs is -1 to signal that secure touch is in being stopped, + * returning -EINVAL + * - st_pending_irqs is 1 to signal that there is a pending irq, returning + * the value "1" to the sysfs read operation + * - st_pending_irqs is 0 (only remaining case left) if the pending interrupt + * has been processed, so the interrupt handler can be allowed to continue. + */ +static ssize_t ft5x06_secure_touch_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + int val = 0; + + if (atomic_read(&data->st_enabled) == 0) + return -EBADF; + if (atomic_cmpxchg(&data->st_pending_irqs, -1, 0) == -1) + return -EINVAL; + if (atomic_cmpxchg(&data->st_pending_irqs, 1, 0) == 1) + val = 1; + else + complete(&data->st_irq_processed); + return scnprintf(buf, PAGE_SIZE, "%u", val); +} +#else +static void ft5x06_secure_touch_init(struct ft5x06_ts_data *data) +{ +} +static irqreturn_t ft5x06_filter_interrupt(struct ft5x06_ts_data *data) +{ + return IRQ_NONE; +} +static void ft5x06_secure_touch_stop(struct ft5x06_ts_data *data, bool blocking) +{ +} +#endif + +static struct device_attribute attrs[] = { +#if defined(CONFIG_FT_SECURE_TOUCH) + __ATTR(secure_touch_enable, (S_IRUGO | S_IWUSR | S_IWGRP), + ft5x06_secure_touch_enable_show, + ft5x06_secure_touch_enable_store), + __ATTR(secure_touch, S_IRUGO, + ft5x06_secure_touch_show, NULL), +#endif +}; + +static inline bool ft5x06_gesture_support_enabled(void) +{ + return config_enabled(CONFIG_TOUCHSCREEN_FT5X06_GESTURE); +} + +static int ft5x06_i2c_read(struct i2c_client *client, char *writebuf, + int writelen, char *readbuf, int readlen) +{ + int ret; + + if (writelen > 0) { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) + dev_err(&client->dev, "%s: i2c read error.\n", + __func__); + } else { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s:i2c read error.\n", __func__); + } + return ret; +} + +static int ft5x06_i2c_write(struct i2c_client *client, char *writebuf, + int writelen) +{ + int ret; + + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s: i2c write error.\n", __func__); + + return ret; +} + +static int ft5x0x_write_reg(struct i2c_client *client, u8 addr, const u8 val) +{ + u8 buf[2] = {0}; + + buf[0] = addr; + buf[1] = val; + + return ft5x06_i2c_write(client, buf, sizeof(buf)); +} + +static int ft5x0x_read_reg(struct i2c_client *client, u8 addr, u8 *val) +{ + return ft5x06_i2c_read(client, &addr, 1, val, 1); +} + +#ifdef CONFIG_TOUCHSCREEN_FT5X06_GESTURE +static ssize_t ft5x06_gesture_enable_to_set_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + data->gesture_pdata->gesture_enable_to_set); +} + +static ssize_t ft5x06_gesture_enable_to_set_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + unsigned long value = 0; + int ret; + + if (data->suspended) + return -EINVAL; + + ret = kstrtoul(buf, 16, &value); + if (ret < 0) { + dev_err(dev, "%s:kstrtoul failed, ret=0x%x\n", + __func__, ret); + return ret; + } + + if (value == 1) + data->gesture_pdata->gesture_enable_to_set = 1; + else + data->gesture_pdata->gesture_enable_to_set = 0; + return size; +} + +static DEVICE_ATTR(enable, 0664, + ft5x06_gesture_enable_to_set_show, + ft5x06_gesture_enable_to_set_store); + +static int ft5x06_entry_pocket(struct device *dev) +{ + return ft5x06_ts_stop(dev); +} + +static int ft5x06_leave_pocket(struct device *dev) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + int err; + + ft5x06_ts_start(dev); + ft5x0x_write_reg(data->client, FT_REG_GESTURE_ENABLE, 1); + err = enable_irq_wake(data->client->irq); + if (err) + dev_err(&data->client->dev, + "%s: set_irq_wake failed\n", __func__); + data->suspended = true; + + return err; +} + +static ssize_t gesture_in_pocket_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + data->gesture_pdata->in_pocket); +} + +static ssize_t gesture_in_pocket_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + unsigned long value = 0; + int ret; + + ret = kstrtoul(buf, 16, &value); + if (ret < 0) { + dev_err(dev, "%s:kstrtoul failed, ret=0x%x\n", + __func__, ret); + return ret; + } + + if (value == 1 && data->gesture_pdata->in_pocket == 0) { + data->gesture_pdata->in_pocket = 1; + ft5x06_entry_pocket(dev); + } else if (value == 0 && data->gesture_pdata->in_pocket == 1) { + ft5x06_leave_pocket(dev); + data->gesture_pdata->in_pocket = 0; + } + return size; +} + +static DEVICE_ATTR(pocket, 0664, + gesture_in_pocket_mode_show, + gesture_in_pocket_mode_store); + +static int ft5x06_report_gesture_doubleclick(struct input_dev *ip_dev) +{ + int i; + + for (i = 0; i < 2; i++) { + input_mt_slot(ip_dev, FT_GESTURE_DEFAULT_TRACKING_ID); + input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 1); + input_report_abs(ip_dev, ABS_MT_POSITION_X, + FT_GESTURE_DOUBLECLICK_COORD_X); + input_report_abs(ip_dev, ABS_MT_POSITION_Y, + FT_GESTURE_DOUBLECLICK_COORD_Y); + input_mt_report_pointer_emulation(ip_dev, false); + input_sync(ip_dev); + input_mt_slot(ip_dev, FT_GESTURE_DEFAULT_TRACKING_ID); + input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0); + input_mt_report_pointer_emulation(ip_dev, false); + input_sync(ip_dev); + } + return 0; +} + +static int ft5x06_report_gesture(struct i2c_client *i2c_client, + struct input_dev *ip_dev) +{ + int i, temp, gesture_data_size; + int gesture_coord_x, gesture_coord_y; + int ret = -1; + short pointnum = 0; + unsigned char buf[FT_GESTURE_POINTER_NUM_MAX * + FT_GESTURE_POINTER_SIZEOF + FT_GESTURE_DATA_HEADER]; + + buf[0] = FT_REG_GESTURE_OUTPUT; + ret = ft5x06_i2c_read(i2c_client, buf, 1, + buf, FT_GESTURE_DATA_HEADER); + if (ret < 0) { + dev_err(&i2c_client->dev, "%s read touchdata failed.\n", + __func__); + return ret; + } + + /* FW support doubleclick */ + if (buf[0] == FT_GESTURE_DOUBLECLICK_ID) { + ft5x06_report_gesture_doubleclick(ip_dev); + return 0; + } + + pointnum = (short)(buf[1]) & 0xff; + gesture_data_size = pointnum * FT_GESTURE_POINTER_SIZEOF + + FT_GESTURE_DATA_HEADER; + buf[0] = FT_REG_GESTURE_OUTPUT; + temp = gesture_data_size / I2C_TRANSFER_MAX_BYTE; + for (i = 0; i < temp; i++) + ret = ft5x06_i2c_read(i2c_client, buf, ((i == 0) ? 1 : 0), + buf + I2C_TRANSFER_MAX_BYTE * i, I2C_TRANSFER_MAX_BYTE); + ret = ft5x06_i2c_read(i2c_client, buf, ((temp == 0) ? 1 : 0), + buf + I2C_TRANSFER_MAX_BYTE * temp, + gesture_data_size - I2C_TRANSFER_MAX_BYTE * temp); + if (ret < 0) { + dev_err(&i2c_client->dev, "%s read touchdata failed.\n", + __func__); + return ret; + } + + for (i = 0; i < pointnum; i++) { + gesture_coord_x = (((s16) buf[FT_GESTURE_DATA_HEADER + + (FT_GESTURE_POINTER_SIZEOF * i)]) & 0x0F) << 8 | + (((s16) buf[FT_GESTURE_DATA_HEADER + 1 + + (FT_GESTURE_POINTER_SIZEOF * i)]) & 0xFF); + gesture_coord_y = (((s16) buf[FT_GESTURE_DATA_HEADER + 2 + + (FT_GESTURE_POINTER_SIZEOF * i)]) & 0x0F) << 8 | + (((s16) buf[FT_GESTURE_DATA_HEADER + 3 + + (FT_GESTURE_POINTER_SIZEOF * i)]) & 0xFF); + input_mt_slot(ip_dev, FT_GESTURE_DEFAULT_TRACKING_ID); + input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 1); + input_report_abs(ip_dev, ABS_MT_POSITION_X, gesture_coord_x); + input_report_abs(ip_dev, ABS_MT_POSITION_Y, gesture_coord_y); + input_mt_report_pointer_emulation(ip_dev, false); + input_sync(ip_dev); + } + input_mt_slot(ip_dev, FT_GESTURE_DEFAULT_TRACKING_ID); + input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0); + input_mt_report_pointer_emulation(ip_dev, false); + input_sync(ip_dev); + + return 0; +} +#else +static DEVICE_ATTR(pocket, 0664, NULL, NULL); +static DEVICE_ATTR(enable, 0664, NULL, NULL); + +static int ft5x06_report_gesture(struct i2c_client *i2c_client, + struct input_dev *ip_dev) +{ + return 0; +} +#endif + +static void ft5x06_update_fw_vendor_id(struct ft5x06_ts_data *data) +{ + struct i2c_client *client = data->client; + u8 reg_addr; + int err; + + reg_addr = FT_REG_FW_VENDOR_ID; + err = ft5x06_i2c_read(client, ®_addr, 1, &data->fw_vendor_id, 1); + if (err < 0) + dev_err(&client->dev, "fw vendor id read failed"); +} + +static void ft5x06_update_fw_ver(struct ft5x06_ts_data *data) +{ + struct i2c_client *client = data->client; + u8 reg_addr; + int err; + + reg_addr = FT_REG_FW_VER; + err = ft5x06_i2c_read(client, ®_addr, 1, &data->fw_ver[0], 1); + if (err < 0) + dev_err(&client->dev, "fw major version read failed"); + + reg_addr = FT_REG_FW_MIN_VER; + err = ft5x06_i2c_read(client, ®_addr, 1, &data->fw_ver[1], 1); + if (err < 0) + dev_err(&client->dev, "fw minor version read failed"); + + reg_addr = FT_REG_FW_SUB_MIN_VER; + err = ft5x06_i2c_read(client, ®_addr, 1, &data->fw_ver[2], 1); + if (err < 0) + dev_err(&client->dev, "fw sub minor version read failed"); + + dev_info(&client->dev, "Firmware version = %d.%d.%d\n", + data->fw_ver[0], data->fw_ver[1], data->fw_ver[2]); +} + +static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) +{ + struct ft5x06_ts_data *data = dev_id; + struct input_dev *ip_dev; + int rc, i; + u32 id, x, y, status, num_touches; + u8 reg, *buf, gesture_is_active; + bool update_input = false; + + if (!data) { + pr_err("%s: Invalid data\n", __func__); + return IRQ_HANDLED; + } + + if (ft5x06_filter_interrupt(data) == IRQ_HANDLED) + return IRQ_HANDLED; + + ip_dev = data->input_dev; + buf = data->tch_data; + + if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) { + ft5x0x_read_reg(data->client, FT_REG_GESTURE_ENABLE, + &gesture_is_active); + if (gesture_is_active) { + pm_wakeup_event(&(data->client->dev), + FT_GESTURE_WAKEUP_TIMEOUT); + ft5x06_report_gesture(data->client, ip_dev); + return IRQ_HANDLED; + } + } + + /* + * Read touch data start from register FT_REG_DEV_MODE. + * The touch x/y value start from FT_TOUCH_X_H/L_POS and + * FT_TOUCH_Y_H/L_POS in buf. + */ + reg = FT_REG_DEV_MODE; + rc = ft5x06_i2c_read(data->client, ®, 1, buf, data->tch_data_len); + if (rc < 0) { + dev_err(&data->client->dev, "%s: read data fail\n", __func__); + return IRQ_HANDLED; + } + + for (i = 0; i < data->pdata->num_max_touches; i++) { + /* + * Getting the finger ID of the touch event incase of + * multiple touch events + */ + id = (buf[FT_TOUCH_ID_POS + FT_ONE_TCH_LEN * i]) >> 4; + if (id >= FT_MAX_ID) + break; + + update_input = true; + + x = (buf[FT_TOUCH_X_H_POS + FT_ONE_TCH_LEN * i] & 0x0F) << 8 | + (buf[FT_TOUCH_X_L_POS + FT_ONE_TCH_LEN * i]); + y = (buf[FT_TOUCH_Y_H_POS + FT_ONE_TCH_LEN * i] & 0x0F) << 8 | + (buf[FT_TOUCH_Y_L_POS + FT_ONE_TCH_LEN * i]); + + status = buf[FT_TOUCH_EVENT_POS + FT_ONE_TCH_LEN * i] >> 6; + + num_touches = buf[FT_TD_STATUS] & FT_STATUS_NUM_TP_MASK; + + /* invalid combination */ + if (!num_touches && !status && !id) + break; + + input_mt_slot(ip_dev, id); + if (status == FT_TOUCH_DOWN || status == FT_TOUCH_CONTACT) { + input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 1); + input_report_abs(ip_dev, ABS_MT_POSITION_X, x); + input_report_abs(ip_dev, ABS_MT_POSITION_Y, y); + } else { + input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0); + } + } + + if (update_input) { + input_mt_report_pointer_emulation(ip_dev, false); + input_sync(ip_dev); + } + + return IRQ_HANDLED; +} + +static int ft5x06_gpio_configure(struct ft5x06_ts_data *data, bool on) +{ + int err = 0; + + if (on) { + if (gpio_is_valid(data->pdata->irq_gpio)) { + err = gpio_request(data->pdata->irq_gpio, + "ft5x06_irq_gpio"); + if (err) { + dev_err(&data->client->dev, + "irq gpio request failed"); + goto err_irq_gpio_req; + } + + err = gpio_direction_input(data->pdata->irq_gpio); + if (err) { + dev_err(&data->client->dev, + "set_direction for irq gpio failed\n"); + goto err_irq_gpio_dir; + } + } + + if (gpio_is_valid(data->pdata->reset_gpio)) { + err = gpio_request(data->pdata->reset_gpio, + "ft5x06_reset_gpio"); + if (err) { + dev_err(&data->client->dev, + "reset gpio request failed"); + goto err_irq_gpio_dir; + } + + err = gpio_direction_output(data->pdata->reset_gpio, 0); + if (err) { + dev_err(&data->client->dev, + "set_direction for reset gpio failed\n"); + goto err_reset_gpio_dir; + } + msleep(data->pdata->hard_rst_dly); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + + return 0; + } + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->irq_gpio); + if (gpio_is_valid(data->pdata->reset_gpio)) { + /* + * This is intended to save leakage current + * only. Even if the call(gpio_direction_input) + * fails, only leakage current will be more but + * functionality will not be affected. + */ + err = gpio_direction_input(data->pdata->reset_gpio); + if (err) { + dev_err(&data->client->dev, + "unable to set direction for gpio [%d]\n", + data->pdata->irq_gpio); + } + gpio_free(data->pdata->reset_gpio); + } + + return 0; + +err_reset_gpio_dir: + if (gpio_is_valid(data->pdata->reset_gpio)) + gpio_free(data->pdata->reset_gpio); +err_irq_gpio_dir: + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->irq_gpio); +err_irq_gpio_req: + return err; +} + +static int ft5x06_power_on(struct ft5x06_ts_data *data, bool on) +{ + int rc; + + if (!on) + goto power_off; + + rc = regulator_enable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd enable failed rc=%d\n", rc); + return rc; + } + + rc = regulator_enable(data->vcc_i2c); + if (rc) { + dev_err(&data->client->dev, + "Regulator vcc_i2c enable failed rc=%d\n", rc); + regulator_disable(data->vdd); + } + + return rc; + +power_off: + rc = regulator_disable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd disable failed rc=%d\n", rc); + return rc; + } + + rc = regulator_disable(data->vcc_i2c); + if (rc) { + dev_err(&data->client->dev, + "Regulator vcc_i2c disable failed rc=%d\n", rc); + rc = regulator_enable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd enable failed rc=%d\n", rc); + } + } + + return rc; +} + +static int ft5x06_power_init(struct ft5x06_ts_data *data, bool on) +{ + int rc; + + if (!on) + goto pwr_deinit; + + data->vdd = regulator_get(&data->client->dev, "vdd"); + if (IS_ERR(data->vdd)) { + rc = PTR_ERR(data->vdd); + dev_err(&data->client->dev, + "Regulator get failed vdd rc=%d\n", rc); + return rc; + } + + if (regulator_count_voltages(data->vdd) > 0) { + rc = regulator_set_voltage(data->vdd, FT_VTG_MIN_UV, + FT_VTG_MAX_UV); + if (rc) { + dev_err(&data->client->dev, + "Regulator set_vtg failed vdd rc=%d\n", rc); + goto reg_vdd_put; + } + } + + data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c"); + if (IS_ERR(data->vcc_i2c)) { + rc = PTR_ERR(data->vcc_i2c); + dev_err(&data->client->dev, + "Regulator get failed vcc_i2c rc=%d\n", rc); + goto reg_vdd_set_vtg; + } + + if (regulator_count_voltages(data->vcc_i2c) > 0) { + rc = regulator_set_voltage(data->vcc_i2c, FT_I2C_VTG_MIN_UV, + FT_I2C_VTG_MAX_UV); + if (rc) { + dev_err(&data->client->dev, + "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); + goto reg_vcc_i2c_put; + } + } + + return 0; + +reg_vcc_i2c_put: + regulator_put(data->vcc_i2c); +reg_vdd_set_vtg: + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FT_VTG_MAX_UV); +reg_vdd_put: + regulator_put(data->vdd); + return rc; + +pwr_deinit: + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FT_VTG_MAX_UV); + + regulator_put(data->vdd); + + if (regulator_count_voltages(data->vcc_i2c) > 0) + regulator_set_voltage(data->vcc_i2c, 0, FT_I2C_VTG_MAX_UV); + + regulator_put(data->vcc_i2c); + return 0; +} + +static int ft5x06_ts_pinctrl_init(struct ft5x06_ts_data *ft5x06_data) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + ft5x06_data->ts_pinctrl = devm_pinctrl_get(&(ft5x06_data->client->dev)); + if (IS_ERR_OR_NULL(ft5x06_data->ts_pinctrl)) { + retval = PTR_ERR(ft5x06_data->ts_pinctrl); + dev_dbg(&ft5x06_data->client->dev, + "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + ft5x06_data->pinctrl_state_active + = pinctrl_lookup_state(ft5x06_data->ts_pinctrl, + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(ft5x06_data->pinctrl_state_active)) { + retval = PTR_ERR(ft5x06_data->pinctrl_state_active); + dev_err(&ft5x06_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + ft5x06_data->pinctrl_state_suspend + = pinctrl_lookup_state(ft5x06_data->ts_pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(ft5x06_data->pinctrl_state_suspend)) { + retval = PTR_ERR(ft5x06_data->pinctrl_state_suspend); + dev_err(&ft5x06_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + ft5x06_data->pinctrl_state_release + = pinctrl_lookup_state(ft5x06_data->ts_pinctrl, + PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(ft5x06_data->pinctrl_state_release)) { + retval = PTR_ERR(ft5x06_data->pinctrl_state_release); + dev_dbg(&ft5x06_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(ft5x06_data->ts_pinctrl); +err_pinctrl_get: + ft5x06_data->ts_pinctrl = NULL; + return retval; +} + +#ifdef CONFIG_PM +static int ft5x06_ts_start(struct device *dev) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + int err; + + if (data->pdata->power_on) { + err = data->pdata->power_on(true); + if (err) { + dev_err(dev, "power on failed"); + return err; + } + } else { + err = ft5x06_power_on(data, true); + if (err) { + dev_err(dev, "power on failed"); + return err; + } + } + + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_active); + if (err < 0) + dev_err(dev, "Cannot get active pinctrl state\n"); + } + + err = ft5x06_gpio_configure(data, true); + if (err < 0) { + dev_err(&data->client->dev, + "failed to put gpios in resue state\n"); + goto err_gpio_configuration; + } + + if (gpio_is_valid(data->pdata->reset_gpio)) { + gpio_set_value_cansleep(data->pdata->reset_gpio, 0); + msleep(data->pdata->hard_rst_dly); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + + msleep(data->pdata->soft_rst_dly); + + enable_irq(data->client->irq); + data->suspended = false; + + return 0; + +err_gpio_configuration: + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_suspend); + if (err < 0) + dev_err(dev, "Cannot get suspend pinctrl state\n"); + } + if (data->pdata->power_on) { + err = data->pdata->power_on(false); + if (err) + dev_err(dev, "power off failed"); + } else { + err = ft5x06_power_on(data, false); + if (err) + dev_err(dev, "power off failed"); + } + return err; +} + +static int ft5x06_ts_stop(struct device *dev) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + char txbuf[2]; + int i, err; + + disable_irq(data->client->irq); + + /* release all touches */ + for (i = 0; i < data->pdata->num_max_touches; i++) { + input_mt_slot(data->input_dev, i); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, 0); + } + input_mt_report_pointer_emulation(data->input_dev, false); + input_sync(data->input_dev); + + if (gpio_is_valid(data->pdata->reset_gpio)) { + txbuf[0] = FT_REG_PMODE; + txbuf[1] = FT_PMODE_HIBERNATE; + ft5x06_i2c_write(data->client, txbuf, sizeof(txbuf)); + } + + if (data->pdata->power_on) { + err = data->pdata->power_on(false); + if (err) { + dev_err(dev, "power off failed"); + goto pwr_off_fail; + } + } else { + err = ft5x06_power_on(data, false); + if (err) { + dev_err(dev, "power off failed"); + goto pwr_off_fail; + } + } + + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_suspend); + if (err < 0) + dev_err(dev, "Cannot get suspend pinctrl state\n"); + } + + err = ft5x06_gpio_configure(data, false); + if (err < 0) { + dev_err(&data->client->dev, + "failed to put gpios in suspend state\n"); + goto gpio_configure_fail; + } + + data->suspended = true; + + return 0; + +gpio_configure_fail: + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_active); + if (err < 0) + dev_err(dev, "Cannot get active pinctrl state\n"); + } + if (data->pdata->power_on) { + err = data->pdata->power_on(true); + if (err) + dev_err(dev, "power on failed"); + } else { + err = ft5x06_power_on(data, true); + if (err) + dev_err(dev, "power on failed"); + } +pwr_off_fail: + if (gpio_is_valid(data->pdata->reset_gpio)) { + gpio_set_value_cansleep(data->pdata->reset_gpio, 0); + msleep(data->pdata->hard_rst_dly); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + enable_irq(data->client->irq); + return err; +} + +static int ft5x06_ts_suspend(struct device *dev) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + int err; + + if (data->loading_fw) { + dev_info(dev, "Firmware loading in process...\n"); + return 0; + } + + if (data->suspended) { + dev_info(dev, "Already in suspend state\n"); + return 0; + } + + ft5x06_secure_touch_stop(data, true); + + if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support && + device_may_wakeup(dev) && + data->gesture_pdata->gesture_enable_to_set) { + + ft5x0x_write_reg(data->client, FT_REG_GESTURE_ENABLE, 1); + err = enable_irq_wake(data->client->irq); + if (err) + dev_err(&data->client->dev, + "%s: set_irq_wake failed\n", __func__); + data->suspended = true; + return err; + } + + return ft5x06_ts_stop(dev); +} + +static int ft5x06_ts_resume(struct device *dev) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + int err; + + if (!data->suspended) { + dev_dbg(dev, "Already in awake state\n"); + return 0; + } + + ft5x06_secure_touch_stop(data, true); + + if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support && + device_may_wakeup(dev) && + !(data->gesture_pdata->in_pocket) && + data->gesture_pdata->gesture_enable_to_set) { + + ft5x0x_write_reg(data->client, FT_REG_GESTURE_ENABLE, 0); + err = disable_irq_wake(data->client->irq); + if (err) + dev_err(dev, "%s: disable_irq_wake failed\n", + __func__); + data->suspended = false; + return err; + } + + err = ft5x06_ts_start(dev); + if (err < 0) + return err; + + if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support && + device_may_wakeup(dev) && + data->gesture_pdata->in_pocket && + data->gesture_pdata->gesture_enable_to_set) { + + ft5x0x_write_reg(data->client, FT_REG_GESTURE_ENABLE, 0); + err = disable_irq_wake(data->client->irq); + if (err) + dev_err(dev, "%s: disable_irq_wake failed\n", + __func__); + data->suspended = false; + data->gesture_pdata->in_pocket = 0; + } + return 0; +} + +static const struct dev_pm_ops ft5x06_ts_pm_ops = { +#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) + .suspend = ft5x06_ts_suspend, + .resume = ft5x06_ts_resume, +#endif +}; + +#else +static int ft5x06_ts_suspend(struct device *dev) +{ + return 0; +} + +static int ft5x06_ts_resume(struct device *dev) +{ + return 0; +} + +#endif + +#if defined(CONFIG_FB) +static void fb_notify_resume_work(struct work_struct *work) +{ + struct ft5x06_ts_data *ft5x06_data = + container_of(work, struct ft5x06_ts_data, fb_notify_work); + ft5x06_ts_resume(&ft5x06_data->client->dev); +} + +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct ft5x06_ts_data *ft5x06_data = + container_of(self, struct ft5x06_ts_data, fb_notif); + + if (evdata && evdata->data && ft5x06_data && ft5x06_data->client) { + blank = evdata->data; + if (ft5x06_data->pdata->resume_in_workqueue) { + if (event == FB_EARLY_EVENT_BLANK && + *blank == FB_BLANK_UNBLANK) + schedule_work(&ft5x06_data->fb_notify_work); + else if (event == FB_EVENT_BLANK && + *blank == FB_BLANK_POWERDOWN) { + flush_work(&ft5x06_data->fb_notify_work); + ft5x06_ts_suspend(&ft5x06_data->client->dev); + } + } else { + if (event == FB_EVENT_BLANK) { + if (*blank == FB_BLANK_UNBLANK) + ft5x06_ts_resume( + &ft5x06_data->client->dev); + else if (*blank == FB_BLANK_POWERDOWN) + ft5x06_ts_suspend( + &ft5x06_data->client->dev); + } + } + } + + return 0; +} +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void ft5x06_ts_early_suspend(struct early_suspend *handler) +{ + struct ft5x06_ts_data *data = container_of(handler, + struct ft5x06_ts_data, + early_suspend); + + /* + * During early suspend/late resume, the driver doesn't access xPU/SMMU + * protected HW resources. So, there is no compelling need to block, + * but notifying the userspace that a power event has occurred is + * enough. Hence 'blocking' variable can be set to false. + */ + ft5x06_secure_touch_stop(data, false); + ft5x06_ts_suspend(&data->client->dev); +} + +static void ft5x06_ts_late_resume(struct early_suspend *handler) +{ + struct ft5x06_ts_data *data = container_of(handler, + struct ft5x06_ts_data, + early_suspend); + + ft5x06_secure_touch_stop(data, false); + ft5x06_ts_resume(&data->client->dev); +} +#endif + +static int ft5x06_auto_cal(struct i2c_client *client) +{ + struct ft5x06_ts_data *data = i2c_get_clientdata(client); + u8 temp = 0, i; + + /* set to factory mode */ + msleep(2 * data->pdata->soft_rst_dly); + ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE); + msleep(data->pdata->soft_rst_dly); + + /* start calibration */ + ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_START); + msleep(2 * data->pdata->soft_rst_dly); + for (i = 0; i < FT_CAL_RETRY; i++) { + ft5x0x_read_reg(client, FT_REG_CAL, &temp); + /* return to normal mode, calibration finish */ + if (((temp & FT_CAL_MASK) >> FT_4BIT_SHIFT) == FT_CAL_FIN) + break; + } + + /*calibration OK */ + msleep(2 * data->pdata->soft_rst_dly); + ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE); + msleep(data->pdata->soft_rst_dly); + + /* store calibration data */ + ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_STORE); + msleep(2 * data->pdata->soft_rst_dly); + + /* set to normal mode */ + ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_WORKMODE_VALUE); + msleep(2 * data->pdata->soft_rst_dly); + + return 0; +} + +static int ft5x06_fw_upgrade_start(struct i2c_client *client, + const u8 *data, u32 data_len) +{ + struct ft5x06_ts_data *ts_data = i2c_get_clientdata(client); + struct fw_upgrade_info info = ts_data->pdata->info; + u8 reset_reg; + u8 w_buf[FT_MAX_WR_BUF] = {0}, r_buf[FT_MAX_RD_BUF] = {0}; + u8 pkt_buf[FT_FW_PKT_LEN + FT_FW_PKT_META_LEN]; + int i, j, temp; + u32 pkt_num, pkt_len; + u8 is_5336_new_bootloader = false; + u8 is_5336_fwsize_30 = false; + u8 fw_ecc; + + /* determine firmware size */ + if (*(data + data_len - FT_BLOADER_SIZE_OFF) == FT_BLOADER_NEW_SIZE) + is_5336_fwsize_30 = true; + else + is_5336_fwsize_30 = false; + + for (i = 0, j = 0; i < FT_UPGRADE_LOOP; i++) { + msleep(FT_EARSE_DLY_MS); + /* reset - write 0xaa and 0x55 to reset register */ + if (ts_data->family_id == FT6X06_ID + || ts_data->family_id == FT6X36_ID) + reset_reg = FT_RST_CMD_REG2; + else + reset_reg = FT_RST_CMD_REG1; + + ft5x0x_write_reg(client, reset_reg, FT_UPGRADE_AA); + msleep(info.delay_aa); + + ft5x0x_write_reg(client, reset_reg, FT_UPGRADE_55); + if (i <= (FT_UPGRADE_LOOP / 2)) + msleep(info.delay_55 + i * 3); + else + msleep(info.delay_55 - (i - (FT_UPGRADE_LOOP / 2)) * 2); + + /* Enter upgrade mode */ + w_buf[0] = FT_UPGRADE_55; + ft5x06_i2c_write(client, w_buf, 1); + usleep_range(FT_55_AA_DLY_NS, FT_55_AA_DLY_NS + 1); + w_buf[0] = FT_UPGRADE_AA; + ft5x06_i2c_write(client, w_buf, 1); + + /* check READ_ID */ + msleep(info.delay_readid); + w_buf[0] = FT_READ_ID_REG; + w_buf[1] = 0x00; + w_buf[2] = 0x00; + w_buf[3] = 0x00; + + ft5x06_i2c_read(client, w_buf, 4, r_buf, 2); + + if (r_buf[0] != info.upgrade_id_1 + || r_buf[1] != info.upgrade_id_2) { + dev_err(&client->dev, "Upgrade ID mismatch(%d), IC=0x%x 0x%x, info=0x%x 0x%x\n", + i, r_buf[0], r_buf[1], + info.upgrade_id_1, info.upgrade_id_2); + } else + break; + } + + if (i >= FT_UPGRADE_LOOP) { + dev_err(&client->dev, "Abort upgrade\n"); + return -EIO; + } + + w_buf[0] = 0xcd; + ft5x06_i2c_read(client, w_buf, 1, r_buf, 1); + + if (r_buf[0] <= 4) + is_5336_new_bootloader = FT_BLOADER_VERSION_LZ4; + else if (r_buf[0] == 7) + is_5336_new_bootloader = FT_BLOADER_VERSION_Z7; + else if (r_buf[0] >= 0x0f && + ((ts_data->family_id == FT_FT5336_FAMILY_ID_0x11) || + (ts_data->family_id == FT_FT5336_FAMILY_ID_0x12) || + (ts_data->family_id == FT_FT5336_FAMILY_ID_0x13) || + (ts_data->family_id == FT_FT5336_FAMILY_ID_0x14))) + is_5336_new_bootloader = FT_BLOADER_VERSION_GZF; + else + is_5336_new_bootloader = FT_BLOADER_VERSION_LZ4; + + dev_dbg(&client->dev, "bootloader type=%d, r_buf=0x%x, family_id=0x%x\n", + is_5336_new_bootloader, r_buf[0], ts_data->family_id); + /* is_5336_new_bootloader = FT_BLOADER_VERSION_GZF; */ + + /* erase app and panel paramenter area */ + w_buf[0] = FT_ERASE_APP_REG; + ft5x06_i2c_write(client, w_buf, 1); + msleep(info.delay_erase_flash); + + if (is_5336_fwsize_30) { + w_buf[0] = FT_ERASE_PANEL_REG; + ft5x06_i2c_write(client, w_buf, 1); + } + msleep(FT_EARSE_DLY_MS); + + /* program firmware */ + if (is_5336_new_bootloader == FT_BLOADER_VERSION_LZ4 + || is_5336_new_bootloader == FT_BLOADER_VERSION_Z7) + data_len = data_len - FT_DATA_LEN_OFF_OLD_FW; + else + data_len = data_len - FT_DATA_LEN_OFF_NEW_FW; + + pkt_num = (data_len) / FT_FW_PKT_LEN; + pkt_len = FT_FW_PKT_LEN; + pkt_buf[0] = FT_FW_START_REG; + pkt_buf[1] = 0x00; + fw_ecc = 0; + + for (i = 0; i < pkt_num; i++) { + temp = i * FT_FW_PKT_LEN; + pkt_buf[2] = (u8) (temp >> FT_8BIT_SHIFT); + pkt_buf[3] = (u8) temp; + pkt_buf[4] = (u8) (pkt_len >> FT_8BIT_SHIFT); + pkt_buf[5] = (u8) pkt_len; + + for (j = 0; j < FT_FW_PKT_LEN; j++) { + pkt_buf[6 + j] = data[i * FT_FW_PKT_LEN + j]; + fw_ecc ^= pkt_buf[6 + j]; + } + + ft5x06_i2c_write(client, pkt_buf, + FT_FW_PKT_LEN + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + } + + /* send remaining bytes */ + if ((data_len) % FT_FW_PKT_LEN > 0) { + temp = pkt_num * FT_FW_PKT_LEN; + pkt_buf[2] = (u8) (temp >> FT_8BIT_SHIFT); + pkt_buf[3] = (u8) temp; + temp = (data_len) % FT_FW_PKT_LEN; + pkt_buf[4] = (u8) (temp >> FT_8BIT_SHIFT); + pkt_buf[5] = (u8) temp; + + for (i = 0; i < temp; i++) { + pkt_buf[6 + i] = data[pkt_num * FT_FW_PKT_LEN + i]; + fw_ecc ^= pkt_buf[6 + i]; + } + + ft5x06_i2c_write(client, pkt_buf, temp + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + } + + /* send the finishing packet */ + if (is_5336_new_bootloader == FT_BLOADER_VERSION_LZ4 || + is_5336_new_bootloader == FT_BLOADER_VERSION_Z7) { + for (i = 0; i < FT_FINISHING_PKT_LEN_OLD_FW; i++) { + if (is_5336_new_bootloader == FT_BLOADER_VERSION_Z7) + temp = FT_MAGIC_BLOADER_Z7 + i; + else if (is_5336_new_bootloader == + FT_BLOADER_VERSION_LZ4) + temp = FT_MAGIC_BLOADER_LZ4 + i; + pkt_buf[2] = (u8)(temp >> 8); + pkt_buf[3] = (u8)temp; + temp = 1; + pkt_buf[4] = (u8)(temp >> 8); + pkt_buf[5] = (u8)temp; + pkt_buf[6] = data[data_len + i]; + fw_ecc ^= pkt_buf[6]; + + ft5x06_i2c_write(client, + pkt_buf, temp + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + } + } else if (is_5336_new_bootloader == FT_BLOADER_VERSION_GZF) { + for (i = 0; i < FT_FINISHING_PKT_LEN_NEW_FW; i++) { + if (is_5336_fwsize_30) + temp = FT_MAGIC_BLOADER_GZF_30 + i; + else + temp = FT_MAGIC_BLOADER_GZF + i; + pkt_buf[2] = (u8)(temp >> 8); + pkt_buf[3] = (u8)temp; + temp = 1; + pkt_buf[4] = (u8)(temp >> 8); + pkt_buf[5] = (u8)temp; + pkt_buf[6] = data[data_len + i]; + fw_ecc ^= pkt_buf[6]; + + ft5x06_i2c_write(client, + pkt_buf, temp + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + + } + } + + /* verify checksum */ + w_buf[0] = FT_REG_ECC; + ft5x06_i2c_read(client, w_buf, 1, r_buf, 1); + if (r_buf[0] != fw_ecc) { + dev_err(&client->dev, "ECC error! dev_ecc=%02x fw_ecc=%02x\n", + r_buf[0], fw_ecc); + return -EIO; + } + + /* reset */ + w_buf[0] = FT_REG_RESET_FW; + ft5x06_i2c_write(client, w_buf, 1); + msleep(ts_data->pdata->soft_rst_dly); + + dev_info(&client->dev, "Firmware upgrade successful\n"); + + return 0; +} + +static int ft5x06_fw_upgrade(struct device *dev, bool force) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + const struct firmware *fw = NULL; + int rc; + u8 fw_file_maj, fw_file_min, fw_file_sub_min, fw_file_vendor_id; + bool fw_upgrade = false; + + if (data->suspended) { + dev_err(dev, "Device is in suspend state: Exit FW upgrade\n"); + return -EBUSY; + } + + rc = request_firmware(&fw, data->fw_name, dev); + if (rc < 0) { + dev_err(dev, "Request firmware failed - %s (%d)\n", + data->fw_name, rc); + return rc; + } + + if (fw->size < FT_FW_MIN_SIZE || fw->size > FT_FW_MAX_SIZE) { + dev_err(dev, "Invalid firmware size (%zu)\n", fw->size); + rc = -EIO; + goto rel_fw; + } + + if (data->family_id == FT6X36_ID) { + fw_file_maj = FT_FW_FILE_MAJ_VER_FT6X36(fw); + fw_file_vendor_id = FT_FW_FILE_VENDOR_ID_FT6X36(fw); + } else { + fw_file_maj = FT_FW_FILE_MAJ_VER(fw); + fw_file_vendor_id = FT_FW_FILE_VENDOR_ID(fw); + } + fw_file_min = FT_FW_FILE_MIN_VER(fw); + fw_file_sub_min = FT_FW_FILE_SUB_MIN_VER(fw); + fw_file_vendor_id = FT_FW_FILE_VENDOR_ID(fw); + + dev_info(dev, "Current firmware: %d.%d.%d", data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); + dev_info(dev, "New firmware: %d.%d.%d", fw_file_maj, + fw_file_min, fw_file_sub_min); + + if (force) + fw_upgrade = true; + else if ((data->fw_ver[0] < fw_file_maj) && + data->fw_vendor_id == fw_file_vendor_id) + fw_upgrade = true; + + if (!fw_upgrade) { + dev_info(dev, "Exiting fw upgrade...\n"); + rc = -EFAULT; + goto rel_fw; + } + + /* start firmware upgrade */ + if (FT_FW_CHECK(fw, data)) { + rc = ft5x06_fw_upgrade_start(data->client, fw->data, fw->size); + if (rc < 0) + dev_err(dev, "update failed (%d). try later...\n", rc); + else if (data->pdata->info.auto_cal) + ft5x06_auto_cal(data->client); + } else { + dev_err(dev, "FW format error\n"); + rc = -EIO; + } + + ft5x06_update_fw_ver(data); + + FT_STORE_TS_DBG_INFO(data->ts_info, data->family_id, data->pdata->name, + data->pdata->num_max_touches, data->pdata->group_id, + data->pdata->fw_vkey_support ? "yes" : "no", + data->pdata->fw_name, data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); + FT_STORE_TS_INFO(ts_info_buff, data->family_id, data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); +rel_fw: + release_firmware(fw); + return rc; +} + +static ssize_t ft5x06_update_fw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 2, "%d\n", data->loading_fw); +} + +static ssize_t ft5x06_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + unsigned long val; + int rc; + + if (size > 2) + return -EINVAL; + + rc = kstrtoul(buf, 10, &val); + if (rc != 0) + return rc; + + if (data->suspended) { + dev_info(dev, "In suspend state, try again later...\n"); + return size; + } + + mutex_lock(&data->input_dev->mutex); + if (!data->loading_fw && val) { + data->loading_fw = true; + ft5x06_fw_upgrade(dev, false); + data->loading_fw = false; + } + mutex_unlock(&data->input_dev->mutex); + + return size; +} + +static DEVICE_ATTR(update_fw, 0664, ft5x06_update_fw_show, + ft5x06_update_fw_store); + +static ssize_t ft5x06_force_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + unsigned long val; + int rc; + + if (size > 2) + return -EINVAL; + + rc = kstrtoul(buf, 10, &val); + if (rc != 0) + return rc; + + mutex_lock(&data->input_dev->mutex); + if (!data->loading_fw && val) { + data->loading_fw = true; + ft5x06_fw_upgrade(dev, true); + data->loading_fw = false; + } + mutex_unlock(&data->input_dev->mutex); + + return size; +} + +static DEVICE_ATTR(force_update_fw, 0664, ft5x06_update_fw_show, + ft5x06_force_update_fw_store); + +static ssize_t ft5x06_fw_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + + return snprintf(buf, FT_FW_NAME_MAX_LEN - 1, "%s\n", data->fw_name); +} + +static ssize_t ft5x06_fw_name_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct ft5x06_ts_data *data = dev_get_drvdata(dev); + + if (size > FT_FW_NAME_MAX_LEN - 1) + return -EINVAL; + + strlcpy(data->fw_name, buf, size); + if (data->fw_name[size-1] == '\n') + data->fw_name[size-1] = 0; + + return size; +} + +static DEVICE_ATTR(fw_name, 0664, ft5x06_fw_name_show, ft5x06_fw_name_store); + +static ssize_t ts_info_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + strlcpy(buf, ts_info_buff, FT_INFO_MAX_LEN); + return strnlen(buf, FT_INFO_MAX_LEN); +} +static struct kobj_attribute ts_info_attr = __ATTR_RO(ts_info); + +static bool ft5x06_debug_addr_is_valid(int addr) +{ + if (addr < 0 || addr > 0xFF) { + pr_err("FT reg address is invalid: 0x%x\n", addr); + return false; + } + + return true; +} + +static int ft5x06_debug_data_set(void *_data, u64 val) +{ + struct ft5x06_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (ft5x06_debug_addr_is_valid(data->addr)) + dev_info(&data->client->dev, + "Writing into FT registers not supported\n"); + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +static int ft5x06_debug_data_get(void *_data, u64 *val) +{ + struct ft5x06_ts_data *data = _data; + int rc; + u8 reg = 0; + + mutex_lock(&data->input_dev->mutex); + + if (ft5x06_debug_addr_is_valid(data->addr)) { + rc = ft5x0x_read_reg(data->client, data->addr, ®); + if (rc < 0) + dev_err(&data->client->dev, + "FT read register 0x%x failed (%d)\n", + data->addr, rc); + else + *val = reg; + } + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, ft5x06_debug_data_get, + ft5x06_debug_data_set, "0x%02llX\n"); + +static int ft5x06_debug_addr_set(void *_data, u64 val) +{ + struct ft5x06_ts_data *data = _data; + + if (ft5x06_debug_addr_is_valid(val)) { + mutex_lock(&data->input_dev->mutex); + data->addr = val; + mutex_unlock(&data->input_dev->mutex); + } + + return 0; +} + +static int ft5x06_debug_addr_get(void *_data, u64 *val) +{ + struct ft5x06_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (ft5x06_debug_addr_is_valid(data->addr)) + *val = data->addr; + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, ft5x06_debug_addr_get, + ft5x06_debug_addr_set, "0x%02llX\n"); + +static int ft5x06_debug_suspend_set(void *_data, u64 val) +{ + struct ft5x06_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (val) + ft5x06_ts_suspend(&data->client->dev); + else + ft5x06_ts_resume(&data->client->dev); + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +static int ft5x06_debug_suspend_get(void *_data, u64 *val) +{ + struct ft5x06_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + *val = data->suspended; + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, ft5x06_debug_suspend_get, + ft5x06_debug_suspend_set, "%lld\n"); + +static int ft5x06_debug_dump_info(struct seq_file *m, void *v) +{ + struct ft5x06_ts_data *data = m->private; + + seq_printf(m, "%s\n", data->ts_info); + + return 0; +} + +static int debugfs_dump_info_open(struct inode *inode, struct file *file) +{ + return single_open(file, ft5x06_debug_dump_info, inode->i_private); +} + +static const struct file_operations debug_dump_info_fops = { + .owner = THIS_MODULE, + .open = debugfs_dump_info_open, + .read = seq_read, + .release = single_release, +}; + +#ifdef CONFIG_OF +static int ft5x06_get_dt_coords(struct device *dev, char *name, + struct ft5x06_ts_platform_data *pdata) +{ + u32 coords[FT_COORDS_ARR_SIZE]; + struct property *prop; + struct device_node *np = dev->of_node; + int coords_size, rc; + + prop = of_find_property(np, name, NULL); + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + coords_size = prop->length / sizeof(u32); + if (coords_size != FT_COORDS_ARR_SIZE) { + dev_err(dev, "invalid %s\n", name); + return -EINVAL; + } + + rc = of_property_read_u32_array(np, name, coords, coords_size); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read %s\n", name); + return rc; + } + + if (!strcmp(name, "focaltech,panel-coords")) { + pdata->panel_minx = coords[0]; + pdata->panel_miny = coords[1]; + pdata->panel_maxx = coords[2]; + pdata->panel_maxy = coords[3]; + } else if (!strcmp(name, "focaltech,display-coords")) { + pdata->x_min = coords[0]; + pdata->y_min = coords[1]; + pdata->x_max = coords[2]; + pdata->y_max = coords[3]; + } else { + dev_err(dev, "unsupported property %s\n", name); + return -EINVAL; + } + + return 0; +} + +static int ft5x06_parse_dt(struct device *dev, + struct ft5x06_ts_platform_data *pdata) +{ + int rc; + struct device_node *np = dev->of_node; + struct property *prop; + u32 temp_val, num_buttons; + u32 button_map[MAX_BUTTONS]; + + pdata->name = "focaltech"; + rc = of_property_read_string(np, "focaltech,name", &pdata->name); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read name\n"); + return rc; + } + + rc = ft5x06_get_dt_coords(dev, "focaltech,panel-coords", pdata); + if (rc && (rc != -EINVAL)) + return rc; + + rc = ft5x06_get_dt_coords(dev, "focaltech,display-coords", pdata); + if (rc) + return rc; + + pdata->i2c_pull_up = of_property_read_bool(np, + "focaltech,i2c-pull-up"); + + pdata->no_force_update = of_property_read_bool(np, + "focaltech,no-force-update"); + /* reset, irq gpio info */ + pdata->reset_gpio = of_get_named_gpio_flags(np, "focaltech,reset-gpio", + 0, &pdata->reset_gpio_flags); + if (pdata->reset_gpio < 0) + return pdata->reset_gpio; + + pdata->irq_gpio = of_get_named_gpio_flags(np, "focaltech,irq-gpio", + 0, &pdata->irq_gpio_flags); + if (pdata->irq_gpio < 0) + return pdata->irq_gpio; + + pdata->fw_name = "ft_fw.bin"; + rc = of_property_read_string(np, "focaltech,fw-name", &pdata->fw_name); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw name\n"); + return rc; + } + + rc = of_property_read_u32(np, "focaltech,group-id", &temp_val); + if (!rc) + pdata->group_id = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,hard-reset-delay-ms", + &temp_val); + if (!rc) + pdata->hard_rst_dly = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,soft-reset-delay-ms", + &temp_val); + if (!rc) + pdata->soft_rst_dly = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,num-max-touches", &temp_val); + if (!rc) + pdata->num_max_touches = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,fw-delay-aa-ms", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay aa\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_aa = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-delay-55-ms", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay 55\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_55 = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-upgrade-id1", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw upgrade id1\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.upgrade_id_1 = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-upgrade-id2", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw upgrade id2\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.upgrade_id_2 = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-delay-readid-ms", + &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay read id\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_readid = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-delay-era-flsh-ms", + &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay erase flash\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_erase_flash = temp_val; + + pdata->info.auto_cal = of_property_read_bool(np, + "focaltech,fw-auto-cal"); + + pdata->fw_vkey_support = of_property_read_bool(np, + "focaltech,fw-vkey-support"); + + pdata->ignore_id_check = of_property_read_bool(np, + "focaltech,ignore-id-check"); + + pdata->gesture_support = of_property_read_bool(np, + "focaltech,gesture-support"); + + pdata->resume_in_workqueue = of_property_read_bool(np, + "focaltech,resume-in-workqueue"); + + rc = of_property_read_u32(np, "focaltech,family-id", &temp_val); + if (!rc) + pdata->family_id = temp_val; + else + return rc; + + prop = of_find_property(np, "focaltech,button-map", NULL); + if (prop) { + num_buttons = prop->length / sizeof(temp_val); + if (num_buttons > MAX_BUTTONS) + return -EINVAL; + + rc = of_property_read_u32_array(np, + "focaltech,button-map", button_map, + num_buttons); + if (rc) { + dev_err(dev, "Unable to read key codes\n"); + return rc; + } + } + + return 0; +} +#else +static int ft5x06_parse_dt(struct device *dev, + struct ft5x06_ts_platform_data *pdata) +{ + return -ENODEV; +} +#endif + +static int ft5x06_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ft5x06_ts_platform_data *pdata; + struct ft5x06_gesture_platform_data *gesture_pdata; + struct ft5x06_ts_data *data; + struct input_dev *input_dev; + struct dentry *temp; + u8 reg_value = 0; + u8 reg_addr; + int err, len, retval, attr_count; + + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, + sizeof(struct ft5x06_ts_platform_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + err = ft5x06_parse_dt(&client->dev, pdata); + if (err) { + dev_err(&client->dev, "DT parsing failed\n"); + return err; + } + } else + pdata = client->dev.platform_data; + + if (!pdata) { + dev_err(&client->dev, "Invalid pdata\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C not supported\n"); + return -ENODEV; + } + + data = devm_kzalloc(&client->dev, + sizeof(struct ft5x06_ts_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (pdata->fw_name) { + len = strlen(pdata->fw_name); + if (len > FT_FW_NAME_MAX_LEN - 1) { + dev_err(&client->dev, "Invalid firmware name\n"); + return -EINVAL; + } + + strlcpy(data->fw_name, pdata->fw_name, len + 1); + } + + data->tch_data_len = FT_TCH_LEN(pdata->num_max_touches); + data->tch_data = devm_kzalloc(&client->dev, + data->tch_data_len, GFP_KERNEL); + if (!data->tch_data) + return -ENOMEM; + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&client->dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + data->input_dev = input_dev; + data->client = client; + data->pdata = pdata; + + input_dev->name = "ft5x06_ts"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + input_set_drvdata(input_dev, data); + i2c_set_clientdata(client, data); + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + input_mt_init_slots(input_dev, pdata->num_max_touches, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, + pdata->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, + pdata->y_max, 0, 0); + + err = input_register_device(input_dev); + if (err) { + dev_err(&client->dev, "Input device registration failed\n"); + input_free_device(input_dev); + return err; + } + + if (pdata->power_init) { + err = pdata->power_init(true); + if (err) { + dev_err(&client->dev, "power init failed"); + goto unreg_inputdev; + } + } else { + err = ft5x06_power_init(data, true); + if (err) { + dev_err(&client->dev, "power init failed"); + goto unreg_inputdev; + } + } + + if (pdata->power_on) { + err = pdata->power_on(true); + if (err) { + dev_err(&client->dev, "power on failed"); + goto pwr_deinit; + } + } else { + err = ft5x06_power_on(data, true); + if (err) { + dev_err(&client->dev, "power on failed"); + goto pwr_deinit; + } + } + + err = ft5x06_ts_pinctrl_init(data); + if (!err && data->ts_pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is found + * let pins to be configured in active state. If not + * found continue further without error. + */ + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_active); + if (err < 0) { + dev_err(&client->dev, + "failed to select pin to active state"); + } + } + + err = ft5x06_gpio_configure(data, true); + if (err < 0) { + dev_err(&client->dev, + "Failed to configure the gpios\n"); + goto err_gpio_req; + } + + /* make sure CTP already finish startup process */ + msleep(data->pdata->soft_rst_dly); + + /* check the controller id */ + reg_addr = FT_REG_ID; + err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err < 0) { + dev_err(&client->dev, "version read failed"); + goto free_gpio; + } + + dev_info(&client->dev, "Device ID = 0x%x\n", reg_value); + + if ((pdata->family_id != reg_value) && (!pdata->ignore_id_check)) { + dev_err(&client->dev, "%s:Unsupported controller\n", __func__); + goto free_gpio; + } + + data->family_id = pdata->family_id; + + err = request_threaded_irq(client->irq, NULL, + ft5x06_ts_interrupt, + /* + * the interrupt trigger mode will be set in Device Tree with property + * "interrupts", so here we just need to set the flag IRQF_ONESHOT + */ + IRQF_ONESHOT, + client->dev.driver->name, data); + if (err) { + dev_err(&client->dev, "request irq failed\n"); + goto free_gpio; + } + + if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) { + device_init_wakeup(&client->dev, 1); + gesture_pdata = devm_kzalloc(&client->dev, + sizeof(struct ft5x06_gesture_platform_data), + GFP_KERNEL); + if (!gesture_pdata) { + dev_err(&client->dev, "Failed to allocate memory\n"); + goto free_gesture_dev; + } + data->gesture_pdata = gesture_pdata; + gesture_pdata->data = data; + + gesture_pdata->gesture_class = + class_create(THIS_MODULE, "gesture"); + if (IS_ERR(gesture_pdata->gesture_class)) { + err = PTR_ERR(gesture_pdata->gesture_class); + dev_err(&client->dev, "Failed to create class.\n"); + goto free_gesture_pdata; + } + + gesture_pdata->dev = device_create(gesture_pdata->gesture_class, + NULL, 0, NULL, "gesture_ft5x06"); + if (IS_ERR(gesture_pdata->dev)) { + err = PTR_ERR(gesture_pdata->dev); + dev_err(&client->dev, "Failed to create device.\n"); + goto free_gesture_class; + } + + dev_set_drvdata(gesture_pdata->dev, data); + err = device_create_file(gesture_pdata->dev, + &dev_attr_enable); + if (err) { + dev_err(gesture_pdata->dev, + "sys file creation failed\n"); + goto free_gesture_dev; + } + err = device_create_file(gesture_pdata->dev, + &dev_attr_pocket); + if (err) { + dev_err(gesture_pdata->dev, + "sys file creation failed\n"); + goto free_enable_sys; + } + } + + err = device_create_file(&client->dev, &dev_attr_fw_name); + if (err) { + dev_err(&client->dev, "sys file creation failed\n"); + goto free_pocket_sys; + } + + err = device_create_file(&client->dev, &dev_attr_update_fw); + if (err) { + dev_err(&client->dev, "sys file creation failed\n"); + goto free_fw_name_sys; + } + + err = device_create_file(&client->dev, &dev_attr_force_update_fw); + if (err) { + dev_err(&client->dev, "sys file creation failed\n"); + goto free_update_fw_sys; + } + + data->dir = debugfs_create_dir(FT_DEBUG_DIR_NAME, NULL); + if (data->dir == NULL || IS_ERR(data->dir)) { + pr_err("debugfs_create_dir failed(%ld)\n", PTR_ERR(data->dir)); + err = PTR_ERR(data->dir); + goto free_force_update_fw_sys; + } + + temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, data->dir, data, + &debug_addr_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, data->dir, data, + &debug_data_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, data->dir, + data, &debug_suspend_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + temp = debugfs_create_file("dump_info", S_IRUSR | S_IWUSR, data->dir, + data, &debug_dump_info_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + data->ts_info = devm_kzalloc(&client->dev, + FT_INFO_MAX_LEN, GFP_KERNEL); + if (!data->ts_info) + goto free_debug_dir; + + /*get some register information */ + reg_addr = FT_REG_POINT_RATE; + ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err < 0) + dev_err(&client->dev, "report rate read failed"); + + dev_info(&client->dev, "report rate = %dHz\n", reg_value * 10); + + reg_addr = FT_REG_THGROUP; + err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err < 0) + dev_err(&client->dev, "threshold read failed"); + + dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4); + + /*creation touch panel info kobj*/ + data->ts_info_kobj = kobject_create_and_add(FT_TS_INFO_SYSFS_DIR_NAME, + kernel_kobj); + if (!data->ts_info_kobj) { + dev_err(&client->dev, "kobject creation failed.\n"); + } else { + err = sysfs_create_file(data->ts_info_kobj, &ts_info_attr.attr); + if (err) { + kobject_put(data->ts_info_kobj); + dev_err(&client->dev, "sysfs creation failed.\n"); + } else { + ts_info_buff = devm_kzalloc(&client->dev, + FT_INFO_MAX_LEN, GFP_KERNEL); + if (!ts_info_buff) + goto free_debug_dir; + } + } + + /*Initialize secure touch */ + ft5x06_secure_touch_init(data); + ft5x06_secure_touch_stop(data, true); + mutex_init(&(data->ft_clk_io_ctrl_mutex)); + + /* Creation of secure touch sysfs files */ + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&data->input_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(&client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto free_secure_touch_sysfs; + } + } + + ft5x06_update_fw_ver(data); + ft5x06_update_fw_vendor_id(data); + + FT_STORE_TS_DBG_INFO(data->ts_info, data->family_id, data->pdata->name, + data->pdata->num_max_touches, data->pdata->group_id, + data->pdata->fw_vkey_support ? "yes" : "no", + data->pdata->fw_name, data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); + FT_STORE_TS_INFO(ts_info_buff, data->family_id, data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); +#if defined(CONFIG_FB) + INIT_WORK(&data->fb_notify_work, fb_notify_resume_work); + data->fb_notif.notifier_call = fb_notifier_callback; + + err = fb_register_client(&data->fb_notif); + + if (err) + dev_err(&client->dev, "Unable to register fb_notifier: %d\n", + err); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + + FT_SUSPEND_LEVEL; + data->early_suspend.suspend = ft5x06_ts_early_suspend; + data->early_suspend.resume = ft5x06_ts_late_resume; + register_early_suspend(&data->early_suspend); +#endif + return 0; + +free_secure_touch_sysfs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } +free_debug_dir: + debugfs_remove_recursive(data->dir); +free_force_update_fw_sys: + device_remove_file(&client->dev, &dev_attr_force_update_fw); +free_update_fw_sys: + device_remove_file(&client->dev, &dev_attr_update_fw); +free_fw_name_sys: + device_remove_file(&client->dev, &dev_attr_fw_name); +free_pocket_sys: + if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) + device_remove_file(&client->dev, &dev_attr_pocket); +free_enable_sys: + if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) + device_remove_file(&client->dev, &dev_attr_enable); +free_gesture_dev: + if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) + device_destroy(gesture_pdata->gesture_class, 0); +free_gesture_class: + if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) + class_destroy(gesture_pdata->gesture_class); +free_gesture_pdata: + if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) { + devm_kfree(&client->dev, gesture_pdata); + data->gesture_pdata = NULL; + } + +free_gpio: + if (gpio_is_valid(pdata->reset_gpio)) + gpio_free(pdata->reset_gpio); + if (gpio_is_valid(pdata->irq_gpio)) + gpio_free(pdata->irq_gpio); +err_gpio_req: + if (data->ts_pinctrl) { + if (IS_ERR_OR_NULL(data->pinctrl_state_release)) { + devm_pinctrl_put(data->ts_pinctrl); + data->ts_pinctrl = NULL; + } else { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_release); + if (err) + pr_err("failed to select relase pinctrl state\n"); + } + } + if (pdata->power_on) + pdata->power_on(false); + else + ft5x06_power_on(data, false); +pwr_deinit: + if (pdata->power_init) + pdata->power_init(false); + else + ft5x06_power_init(data, false); +unreg_inputdev: + input_unregister_device(input_dev); + return err; +} + +static int ft5x06_ts_remove(struct i2c_client *client) +{ + struct ft5x06_ts_data *data = i2c_get_clientdata(client); + int retval, attr_count; + + if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) { + device_init_wakeup(&client->dev, 0); + device_remove_file(&client->dev, &dev_attr_pocket); + device_remove_file(&client->dev, &dev_attr_enable); + device_destroy(data->gesture_pdata->gesture_class, 0); + class_destroy(data->gesture_pdata->gesture_class); + devm_kfree(&client->dev, data->gesture_pdata); + data->gesture_pdata = NULL; + } + + debugfs_remove_recursive(data->dir); + device_remove_file(&client->dev, &dev_attr_force_update_fw); + device_remove_file(&client->dev, &dev_attr_update_fw); + device_remove_file(&client->dev, &dev_attr_fw_name); + +#if defined(CONFIG_FB) + if (fb_unregister_client(&data->fb_notif)) + dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&data->early_suspend); +#endif + free_irq(client->irq, data); + + if (gpio_is_valid(data->pdata->reset_gpio)) + gpio_free(data->pdata->reset_gpio); + + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->irq_gpio); + + if (data->ts_pinctrl) { + if (IS_ERR_OR_NULL(data->pinctrl_state_release)) { + devm_pinctrl_put(data->ts_pinctrl); + data->ts_pinctrl = NULL; + } else { + retval = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_release); + if (retval < 0) + pr_err("failed to select release pinctrl state\n"); + } + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + + if (data->pdata->power_on) + data->pdata->power_on(false); + else + ft5x06_power_on(data, false); + + if (data->pdata->power_init) + data->pdata->power_init(false); + else + ft5x06_power_init(data, false); + + input_unregister_device(data->input_dev); + kobject_put(data->ts_info_kobj); + return 0; +} + +static const struct i2c_device_id ft5x06_ts_id[] = { + {"ft5x06_ts", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ft5x06_ts_id); + +#ifdef CONFIG_OF +static const struct of_device_id ft5x06_match_table[] = { + { .compatible = "focaltech,5x06",}, + { }, +}; +#else +#define ft5x06_match_table NULL +#endif + +static struct i2c_driver ft5x06_ts_driver = { + .probe = ft5x06_ts_probe, + .remove = ft5x06_ts_remove, + .driver = { + .name = "ft5x06_ts", + .owner = THIS_MODULE, + .of_match_table = ft5x06_match_table, +#ifdef CONFIG_PM + .pm = &ft5x06_ts_pm_ops, +#endif + }, + .id_table = ft5x06_ts_id, +}; + +static int __init ft5x06_ts_init(void) +{ + return i2c_add_driver(&ft5x06_ts_driver); +} +module_init(ft5x06_ts_init); + +static void __exit ft5x06_ts_exit(void) +{ + i2c_del_driver(&ft5x06_ts_driver); +} +module_exit(ft5x06_ts_exit); + +MODULE_DESCRIPTION("FocalTech ft5x06 TouchScreen driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/gen_vkeys.c b/drivers/input/touchscreen/gen_vkeys.c new file mode 100644 index 000000000000..dde582f4f8bf --- /dev/null +++ b/drivers/input/touchscreen/gen_vkeys.c @@ -0,0 +1,230 @@ +/* Copyright (c) 2013, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/input.h> +#include <linux/input/gen_vkeys.h> + +#define MAX_BUF_SIZE 256 +#define VKEY_VER_CODE "0x01" + +#define HEIGHT_SCALE_NUM 8 +#define HEIGHT_SCALE_DENOM 10 + +#define VKEY_Y_OFFSET_DEFAULT 0 + +/* numerator and denomenator for border equations */ +#define BORDER_ADJUST_NUM 3 +#define BORDER_ADJUST_DENOM 4 + +static struct kobject *vkey_obj; +static char *vkey_buf; + +static ssize_t vkey_show(struct kobject *obj, + struct kobj_attribute *attr, char *buf) +{ + strlcpy(buf, vkey_buf, MAX_BUF_SIZE); + return strnlen(buf, MAX_BUF_SIZE); +} + +static struct kobj_attribute vkey_obj_attr = { + .attr = { + .mode = S_IRUGO, + }, + .show = vkey_show, +}; + +static struct attribute *vkey_attr[] = { + &vkey_obj_attr.attr, + NULL, +}; + +static struct attribute_group vkey_grp = { + .attrs = vkey_attr, +}; + +static int vkey_parse_dt(struct device *dev, + struct vkeys_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + struct property *prop; + int rc, val; + + rc = of_property_read_string(np, "label", &pdata->name); + if (rc) { + dev_err(dev, "Failed to read label\n"); + return -EINVAL; + } + + rc = of_property_read_u32(np, "qcom,disp-maxx", &pdata->disp_maxx); + if (rc) { + dev_err(dev, "Failed to read display max x\n"); + return -EINVAL; + } + + rc = of_property_read_u32(np, "qcom,disp-maxy", &pdata->disp_maxy); + if (rc) { + dev_err(dev, "Failed to read display max y\n"); + return -EINVAL; + } + + rc = of_property_read_u32(np, "qcom,panel-maxx", &pdata->panel_maxx); + if (rc) { + dev_err(dev, "Failed to read panel max x\n"); + return -EINVAL; + } + + rc = of_property_read_u32(np, "qcom,panel-maxy", &pdata->panel_maxy); + if (rc) { + dev_err(dev, "Failed to read panel max y\n"); + return -EINVAL; + } + + prop = of_find_property(np, "qcom,key-codes", NULL); + if (prop) { + pdata->num_keys = prop->length / sizeof(u32); + pdata->keycodes = devm_kzalloc(dev, + sizeof(u32) * pdata->num_keys, GFP_KERNEL); + if (!pdata->keycodes) + return -ENOMEM; + rc = of_property_read_u32_array(np, "qcom,key-codes", + pdata->keycodes, pdata->num_keys); + if (rc) { + dev_err(dev, "Failed to read key codes\n"); + return -EINVAL; + } + } + + pdata->y_offset = VKEY_Y_OFFSET_DEFAULT; + rc = of_property_read_u32(np, "qcom,y-offset", &val); + if (!rc) + pdata->y_offset = val; + else if (rc != -EINVAL) { + dev_err(dev, "Failed to read y position offset\n"); + return rc; + } + return 0; +} + +static int vkeys_probe(struct platform_device *pdev) +{ + struct vkeys_platform_data *pdata; + int width, height, center_x, center_y; + int x1 = 0, x2 = 0, i, c = 0, ret, border; + char *name; + + vkey_buf = devm_kzalloc(&pdev->dev, MAX_BUF_SIZE, GFP_KERNEL); + if (!vkey_buf) { + dev_err(&pdev->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + if (pdev->dev.of_node) { + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct vkeys_platform_data), GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + ret = vkey_parse_dt(&pdev->dev, pdata); + if (ret) { + dev_err(&pdev->dev, "Parsing DT failed(%d)", ret); + return ret; + } + } else + pdata = pdev->dev.platform_data; + + if (!pdata || !pdata->name || !pdata->keycodes || !pdata->num_keys || + !pdata->disp_maxx || !pdata->disp_maxy || !pdata->panel_maxy) { + dev_err(&pdev->dev, "pdata is invalid\n"); + return -EINVAL; + } + + border = (pdata->panel_maxx - pdata->disp_maxx) * 2; + width = ((pdata->disp_maxx - (border * (pdata->num_keys - 1))) + / pdata->num_keys); + height = (pdata->panel_maxy - pdata->disp_maxy); + center_y = pdata->disp_maxy + (height / 2) + pdata->y_offset; + height = height * HEIGHT_SCALE_NUM / HEIGHT_SCALE_DENOM; + + x2 -= border * BORDER_ADJUST_NUM / BORDER_ADJUST_DENOM; + + for (i = 0; i < pdata->num_keys; i++) { + x1 = x2 + border; + x2 = x2 + border + width; + center_x = x1 + (x2 - x1) / 2; + c += snprintf(vkey_buf + c, MAX_BUF_SIZE - c, + "%s:%d:%d:%d:%d:%d\n", + VKEY_VER_CODE, pdata->keycodes[i], + center_x, center_y, width, height); + } + + vkey_buf[c] = '\0'; + + name = devm_kzalloc(&pdev->dev, sizeof(*name) * MAX_BUF_SIZE, + GFP_KERNEL); + if (!name) + return -ENOMEM; + + snprintf(name, MAX_BUF_SIZE, + "virtualkeys.%s", pdata->name); + vkey_obj_attr.attr.name = name; + + vkey_obj = kobject_create_and_add("board_properties", NULL); + if (!vkey_obj) { + dev_err(&pdev->dev, "unable to create kobject\n"); + return -ENOMEM; + } + + ret = sysfs_create_group(vkey_obj, &vkey_grp); + if (ret) { + dev_err(&pdev->dev, "failed to create attributes\n"); + goto destroy_kobj; + } + return 0; + +destroy_kobj: + kobject_put(vkey_obj); + + return ret; +} + +static int vkeys_remove(struct platform_device *pdev) +{ + sysfs_remove_group(vkey_obj, &vkey_grp); + kobject_put(vkey_obj); + + return 0; +} + +static struct of_device_id vkey_match_table[] = { + { .compatible = "qcom,gen-vkeys",}, + { }, +}; + +static struct platform_driver vkeys_driver = { + .probe = vkeys_probe, + .remove = vkeys_remove, + .driver = { + .owner = THIS_MODULE, + .name = "gen_vkeys", + .of_match_table = vkey_match_table, + }, +}; + +module_platform_driver(vkeys_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c new file mode 100644 index 000000000000..1a2afd1c1117 --- /dev/null +++ b/drivers/input/touchscreen/it7258_ts_i2c.c @@ -0,0 +1,2248 @@ +/* drivers/input/touchscreen/it7258_ts_i2c.c + * + * Copyright (C) 2014 ITE Tech. Inc. + * Copyright (c) 2015-2016, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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/delay.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +#include <linux/fb.h> +#include <linux/debugfs.h> +#include <linux/input/mt.h> +#include <linux/string.h> + +#define MAX_BUFFER_SIZE 144 +#define DEVICE_NAME "it7260" +#define SCREEN_X_RESOLUTION 320 +#define SCREEN_Y_RESOLUTION 320 +#define DEBUGFS_DIR_NAME "ts_debug" +#define FW_NAME "it7260_fw.bin" +#define CFG_NAME "it7260_cfg.bin" +#define VER_BUFFER_SIZE 4 +#define IT_FW_CHECK(x, y) \ + (((x)[0] < (y)->data[8]) || ((x)[1] < (y)->data[9]) || \ + ((x)[2] < (y)->data[10]) || ((x)[3] < (y)->data[11])) +#define IT_CFG_CHECK(x, y) \ + (((x)[0] < (y)->data[(y)->size - 8]) || \ + ((x)[1] < (y)->data[(y)->size - 7]) || \ + ((x)[2] < (y)->data[(y)->size - 6]) || \ + ((x)[3] < (y)->data[(y)->size - 5])) +#define IT7260_COORDS_ARR_SIZE 4 + +/* all commands writes go to this idx */ +#define BUF_COMMAND 0x20 +#define BUF_SYS_COMMAND 0x40 +/* + * "device ready?" and "wake up please" and "read touch data" reads + * go to this idx + */ +#define BUF_QUERY 0x80 +/* most command response reads go to this idx */ +#define BUF_RESPONSE 0xA0 +#define BUF_SYS_RESPONSE 0xC0 +/* reads of "point" go through here and produce 14 bytes of data */ +#define BUF_POINT_INFO 0xE0 + +/* + * commands and their subcommands. when no subcommands exist, a zero + * is send as the second byte + */ +#define CMD_IDENT_CHIP 0x00 +/* VERSION_LENGTH bytes of data in response */ +#define CMD_READ_VERSIONS 0x01 +#define SUB_CMD_READ_FIRMWARE_VERSION 0x00 +#define SUB_CMD_READ_CONFIG_VERSION 0x06 +#define VERSION_LENGTH 10 +/* subcommand is zero, next byte is power mode */ +#define CMD_PWR_CTL 0x04 +/* active mode */ +#define PWR_CTL_ACTIVE_MODE 0x00 +/* idle mode */ +#define PWR_CTL_LOW_POWER_MODE 0x01 +/* sleep mode */ +#define PWR_CTL_SLEEP_MODE 0x02 +#define WAIT_CHANGE_MODE 20 +/* command is not documented in the datasheet v1.0.0.7 */ +#define CMD_UNKNOWN_7 0x07 +#define CMD_FIRMWARE_REINIT_C 0x0C +/* needs to be followed by 4 bytes of zeroes */ +#define CMD_CALIBRATE 0x13 +#define CMD_FIRMWARE_UPGRADE 0x60 +#define SUB_CMD_ENTER_FW_UPGRADE_MODE 0x00 +#define SUB_CMD_EXIT_FW_UPGRADE_MODE 0x80 +/* address for FW read/write */ +#define CMD_SET_START_OFFSET 0x61 +/* subcommand is number of bytes to write */ +#define CMD_FW_WRITE 0x62 +/* subcommand is number of bytes to read */ +#define CMD_FW_READ 0x63 +#define CMD_FIRMWARE_REINIT_6F 0x6F + +#define FW_WRITE_CHUNK_SIZE 128 +#define FW_WRITE_RETRY_COUNT 4 +#define CHIP_FLASH_SIZE 0x8000 +#define DEVICE_READY_COUNT_MAX 500 +#define DEVICE_READY_COUNT_20 20 +#define IT_I2C_WAIT_10MS 10 +#define IT_I2C_READ_RET 2 +#define IT_I2C_WRITE_RET 1 + +/* result of reading with BUF_QUERY bits */ +#define CMD_STATUS_BITS 0x07 +#define CMD_STATUS_DONE 0x00 +#define CMD_STATUS_BUSY 0x01 +#define CMD_STATUS_ERROR 0x02 +#define CMD_STATUS_NO_CONN 0x07 +#define PT_INFO_BITS 0xF8 +#define PT_INFO_YES 0x80 + +#define PD_FLAGS_DATA_TYPE_BITS 0xF0 +/* other types (like chip-detected gestures) exist but we do not care */ +#define PD_FLAGS_DATA_TYPE_TOUCH 0x00 +#define PD_FLAGS_IDLE_TO_ACTIVE 0x10 +/* a bit for each finger data that is valid (from lsb to msb) */ +#define PD_FLAGS_HAVE_FINGERS 0x07 +#define PD_PALM_FLAG_BIT 0x01 +#define FD_PRESSURE_BITS 0x0F +#define FD_PRESSURE_NONE 0x00 +#define FD_PRESSURE_LIGHT 0x02 + +#define IT_VTG_MIN_UV 1800000 +#define IT_VTG_MAX_UV 1800000 +#define IT_ACTIVE_LOAD_UA 15000 +#define IT_I2C_VTG_MIN_UV 2600000 +#define IT_I2C_VTG_MAX_UV 3300000 +#define IT_I2C_ACTIVE_LOAD_UA 10000 +#define DELAY_VTG_REG_EN 170 + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" + +struct finger_data { + u8 xLo; + u8 hi; + u8 yLo; + u8 pressure; +} __packed; + +struct point_data { + u8 flags; + u8 gesture_id; + struct finger_data fd[3]; +} __packed; + +struct it7260_ts_platform_data { + u32 irq_gpio; + u32 irq_gpio_flags; + u32 reset_gpio; + u32 reset_gpio_flags; + bool wakeup; + bool palm_detect_en; + u16 palm_detect_keycode; + const char *fw_name; + const char *cfg_name; + unsigned int panel_minx; + unsigned int panel_miny; + unsigned int panel_maxx; + unsigned int panel_maxy; + unsigned int disp_minx; + unsigned int disp_miny; + unsigned int disp_maxx; + unsigned int disp_maxy; + unsigned num_of_fingers; + unsigned int reset_delay; + unsigned int avdd_lpm_cur; + bool low_reset; +}; + +struct it7260_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct it7260_ts_platform_data *pdata; + struct regulator *vdd; + struct regulator *avdd; + bool in_low_power_mode; + bool suspended; + bool fw_upgrade_result; + bool cfg_upgrade_result; + bool fw_cfg_uploading; + struct work_struct work_pm_relax; + bool calibration_success; + bool had_finger_down; + char fw_name[MAX_BUFFER_SIZE]; + char cfg_name[MAX_BUFFER_SIZE]; + struct mutex fw_cfg_mutex; + u8 fw_ver[VER_BUFFER_SIZE]; + u8 cfg_ver[VER_BUFFER_SIZE]; +#ifdef CONFIG_FB + struct notifier_block fb_notif; +#endif + struct dentry *dir; + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; +}; + +/* Function declarations */ +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +static int it7260_ts_resume(struct device *dev); +static int it7260_ts_suspend(struct device *dev); + +static int it7260_debug_suspend_set(void *_data, u64 val) +{ + struct it7260_ts_data *ts_data = _data; + + if (val) + it7260_ts_suspend(&ts_data->client->dev); + else + it7260_ts_resume(&ts_data->client->dev); + + return 0; +} + +static int it7260_debug_suspend_get(void *_data, u64 *val) +{ + struct it7260_ts_data *ts_data = _data; + + mutex_lock(&ts_data->input_dev->mutex); + *val = ts_data->suspended; + mutex_lock(&ts_data->input_dev->mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, it7260_debug_suspend_get, + it7260_debug_suspend_set, "%lld\n"); + +/* internal use func - does not make sure chip is ready before read */ +static int it7260_i2c_read_no_ready_check(struct it7260_ts_data *ts_data, + uint8_t buf_index, uint8_t *buffer, uint16_t buf_len) +{ + int ret; + struct i2c_msg msgs[2] = { + { + .addr = ts_data->client->addr, + .flags = I2C_M_NOSTART, + .len = 1, + .buf = &buf_index + }, + { + .addr = ts_data->client->addr, + .flags = I2C_M_RD, + .len = buf_len, + .buf = buffer + } + }; + + memset(buffer, 0xFF, buf_len); + + ret = i2c_transfer(ts_data->client->adapter, msgs, 2); + if (ret < 0) + dev_err(&ts_data->client->dev, "i2c read failed %d\n", ret); + + return ret; +} + +static int it7260_i2c_write_no_ready_check(struct it7260_ts_data *ts_data, + uint8_t buf_index, const uint8_t *buffer, uint16_t buf_len) +{ + uint8_t txbuf[257]; + int ret; + struct i2c_msg msg = { + .addr = ts_data->client->addr, + .flags = 0, + .len = buf_len + 1, + .buf = txbuf + }; + + /* just to be careful */ + if (buf_len > sizeof(txbuf) - 1) { + dev_err(&ts_data->client->dev, "buf length is out of limit\n"); + return false; + } + + txbuf[0] = buf_index; + memcpy(txbuf + 1, buffer, buf_len); + + ret = i2c_transfer(ts_data->client->adapter, &msg, 1); + if (ret < 0) + dev_err(&ts_data->client->dev, "i2c write failed %d\n", ret); + + return ret; +} + +/* + * Device is apparently always ready for I2C communication but not for + * actual register reads/writes. This function checks if it is ready + * for that too. The results of this call often were ignored. + * If forever is set to TRUE, then check the device's status until it + * becomes ready with 500 retries at max. Otherwise retry 25 times only. + * If slowly is set to TRUE, then add sleep of 50 ms in each retry, + * otherwise don't sleep. + */ +static int it7260_wait_device_ready(struct it7260_ts_data *ts_data, + bool forever, bool slowly) +{ + uint8_t query; + uint32_t count = DEVICE_READY_COUNT_20; + int ret; + + if (ts_data->fw_cfg_uploading || forever) + count = DEVICE_READY_COUNT_MAX; + + do { + ret = it7260_i2c_read_no_ready_check(ts_data, BUF_QUERY, &query, + sizeof(query)); + if (ret < 0 && ((query & CMD_STATUS_BITS) + == CMD_STATUS_NO_CONN)) + continue; + + if ((query & CMD_STATUS_BITS) == CMD_STATUS_DONE) + break; + + query = CMD_STATUS_BUSY; + if (slowly) + msleep(IT_I2C_WAIT_10MS); + } while (--count); + + return ((!(query & CMD_STATUS_BITS)) ? 0 : -ENODEV); +} + +static int it7260_i2c_read(struct it7260_ts_data *ts_data, uint8_t buf_index, + uint8_t *buffer, uint16_t buf_len) +{ + int ret; + + ret = it7260_wait_device_ready(ts_data, false, false); + if (ret < 0) + return ret; + + return it7260_i2c_read_no_ready_check(ts_data, buf_index, + buffer, buf_len); +} + +static int it7260_i2c_write(struct it7260_ts_data *ts_data, uint8_t buf_index, + const uint8_t *buffer, uint16_t buf_len) +{ + int ret; + + ret = it7260_wait_device_ready(ts_data, false, false); + if (ret < 0) + return ret; + + return it7260_i2c_write_no_ready_check(ts_data, buf_index, + buffer, buf_len); +} + +static int it7260_firmware_reinitialize(struct it7260_ts_data *ts_data, + u8 command) +{ + uint8_t cmd[] = {command}; + uint8_t rsp[2]; + int ret; + + ret = it7260_i2c_write(ts_data, BUF_COMMAND, cmd, sizeof(cmd)); + if (ret != IT_I2C_WRITE_RET) { + dev_err(&ts_data->client->dev, + "failed to write fw reinit command %d\n", ret); + return ret; + } + + ret = it7260_i2c_read(ts_data, BUF_RESPONSE, rsp, sizeof(rsp)); + if (ret != IT_I2C_READ_RET) { + dev_err(&ts_data->client->dev, + "failed to read any response from chip %d\n", ret); + return ret; + } + + /* a reply of two zero bytes signifies success */ + if (rsp[0] == 0 && rsp[1] == 0) + return 0; + else + return -EIO; +} + +static int it7260_enter_exit_fw_ugrade_mode(struct it7260_ts_data *ts_data, + bool enter) +{ + uint8_t cmd[] = {CMD_FIRMWARE_UPGRADE, 0, 'I', 'T', '7', '2', + '6', '0', 0x55, 0xAA}; + uint8_t resp[2]; + int ret; + + cmd[1] = enter ? SUB_CMD_ENTER_FW_UPGRADE_MODE : + SUB_CMD_EXIT_FW_UPGRADE_MODE; + + ret = it7260_i2c_write(ts_data, BUF_COMMAND, cmd, sizeof(cmd)); + if (ret != IT_I2C_WRITE_RET) { + dev_err(&ts_data->client->dev, + "failed to write CMD_FIRMWARE_UPGRADE %d\n", ret); + return ret; + } + + ret = it7260_i2c_read(ts_data, BUF_RESPONSE, resp, sizeof(resp)); + if (ret != IT_I2C_READ_RET) { + dev_err(&ts_data->client->dev, + "failed to read any response from chip %d\n", ret); + return ret; + } + + /* a reply of two zero bytes signifies success */ + if (resp[0] == 0 && resp[1] == 0) + return 0; + else + return -EIO; +} + +static int it7260_set_start_offset(struct it7260_ts_data *ts_data, + uint16_t offset) +{ + uint8_t cmd[] = {CMD_SET_START_OFFSET, 0, ((uint8_t)(offset)), + ((uint8_t)((offset) >> 8))}; + uint8_t resp[2]; + int ret; + + ret = it7260_i2c_write(ts_data, BUF_COMMAND, cmd, 4); + if (ret != IT_I2C_WRITE_RET) { + dev_err(&ts_data->client->dev, + "failed to write CMD_SET_START_OFFSET %d\n", ret); + return ret; + } + + + ret = it7260_i2c_read(ts_data, BUF_RESPONSE, resp, sizeof(resp)); + if (ret != IT_I2C_READ_RET) { + dev_err(&ts_data->client->dev, + "failed to read any response from chip %d\n", ret); + return ret; + } + + /* a reply of two zero bytes signifies success */ + if (resp[0] == 0 && resp[1] == 0) + return 0; + else + return -EIO; +} + + +/* write fw_length bytes from fw_data at chip offset wr_start_offset */ +static int it7260_fw_flash_write_verify(struct it7260_ts_data *ts_data, + unsigned int fw_length, const uint8_t *fw_data, + uint16_t wr_start_offset) +{ + uint32_t cur_data_off; + + for (cur_data_off = 0; cur_data_off < fw_length; + cur_data_off += FW_WRITE_CHUNK_SIZE) { + + uint8_t cmd_write[2 + FW_WRITE_CHUNK_SIZE] = {CMD_FW_WRITE}; + uint8_t buf_read[FW_WRITE_CHUNK_SIZE]; + uint8_t cmd_read[2] = {CMD_FW_READ}; + unsigned i, retries; + uint32_t cur_wr_size; + + /* figure out how much to write */ + cur_wr_size = fw_length - cur_data_off; + if (cur_wr_size > FW_WRITE_CHUNK_SIZE) + cur_wr_size = FW_WRITE_CHUNK_SIZE; + + /* prepare the write command */ + cmd_write[1] = cur_wr_size; + for (i = 0; i < cur_wr_size; i++) + cmd_write[i + 2] = fw_data[cur_data_off + i]; + + /* prepare the read command */ + cmd_read[1] = cur_wr_size; + + for (retries = 0; retries < FW_WRITE_RETRY_COUNT; + retries++) { + + /* set write offset and write the data */ + it7260_set_start_offset(ts_data, + wr_start_offset + cur_data_off); + it7260_i2c_write(ts_data, BUF_COMMAND, cmd_write, + cur_wr_size + 2); + + /* set offset and read the data back */ + it7260_set_start_offset(ts_data, + wr_start_offset + cur_data_off); + it7260_i2c_write(ts_data, BUF_COMMAND, cmd_read, + sizeof(cmd_read)); + it7260_i2c_read(ts_data, BUF_RESPONSE, buf_read, + cur_wr_size); + + /* verify. If success break out of retry loop */ + i = 0; + while (i < cur_wr_size && + buf_read[i] == cmd_write[i + 2]) + i++; + if (i == cur_wr_size) + break; + } + /* if we've failed after all the retries, tell the caller */ + if (retries == FW_WRITE_RETRY_COUNT) { + dev_err(&ts_data->client->dev, + "write of data offset %u failed on try %u at byte %u/%u\n", + cur_data_off, retries, i, cur_wr_size); + return -EIO; + } + } + + return 0; +} + +/* + * this code to get versions from the chip via i2c transactions, and save + * them in driver data structure. + */ +static void it7260_get_chip_versions(struct it7260_ts_data *ts_data) +{ + static const u8 cmd_read_fw_ver[] = {CMD_READ_VERSIONS, + SUB_CMD_READ_FIRMWARE_VERSION}; + static const u8 cmd_read_cfg_ver[] = {CMD_READ_VERSIONS, + SUB_CMD_READ_CONFIG_VERSION}; + u8 ver_fw[VERSION_LENGTH], ver_cfg[VERSION_LENGTH]; + int ret; + + ret = it7260_i2c_write(ts_data, BUF_COMMAND, cmd_read_fw_ver, + sizeof(cmd_read_fw_ver)); + if (ret == IT_I2C_WRITE_RET) { + /* + * Sometimes, the controller may not respond immediately after + * writing the command, so wait for device to get ready. + */ + ret = it7260_wait_device_ready(ts_data, true, false); + if (ret < 0) + dev_err(&ts_data->client->dev, + "failed to read chip status %d\n", ret); + + ret = it7260_i2c_read_no_ready_check(ts_data, BUF_RESPONSE, + ver_fw, VERSION_LENGTH); + if (ret == IT_I2C_READ_RET) + memcpy(ts_data->fw_ver, ver_fw + (5 * sizeof(u8)), + VER_BUFFER_SIZE * sizeof(u8)); + else + dev_err(&ts_data->client->dev, + "failed to read fw-ver from chip %d\n", ret); + } else { + dev_err(&ts_data->client->dev, + "failed to write fw-read command %d\n", ret); + } + + ret = it7260_i2c_write(ts_data, BUF_COMMAND, cmd_read_cfg_ver, + sizeof(cmd_read_cfg_ver)); + if (ret == IT_I2C_WRITE_RET) { + /* + * Sometimes, the controller may not respond immediately after + * writing the command, so wait for device to get ready. + */ + ret = it7260_wait_device_ready(ts_data, true, false); + if (ret < 0) + dev_err(&ts_data->client->dev, + "failed to read chip status %d\n", ret); + + ret = it7260_i2c_read_no_ready_check(ts_data, BUF_RESPONSE, + ver_cfg, VERSION_LENGTH); + if (ret == IT_I2C_READ_RET) + memcpy(ts_data->cfg_ver, ver_cfg + (1 * sizeof(u8)), + VER_BUFFER_SIZE * sizeof(u8)); + else + dev_err(&ts_data->client->dev, + "failed to read cfg-ver from chip %d\n", ret); + } else { + dev_err(&ts_data->client->dev, + "failed to write cfg-read command %d\n", ret); + } + + dev_info(&ts_data->client->dev, "Current fw{%X.%X.%X.%X} cfg{%X.%X.%X.%X}\n", + ts_data->fw_ver[0], ts_data->fw_ver[1], ts_data->fw_ver[2], + ts_data->fw_ver[3], ts_data->cfg_ver[0], ts_data->cfg_ver[1], + ts_data->cfg_ver[2], ts_data->cfg_ver[3]); +} + +static int it7260_cfg_upload(struct it7260_ts_data *ts_data, bool force) +{ + const struct firmware *cfg = NULL; + int ret; + bool cfg_upgrade = false; + struct device *dev = &ts_data->client->dev; + + ret = request_firmware(&cfg, ts_data->cfg_name, dev); + if (ret) { + dev_err(dev, "failed to get config data %s for it7260 %d\n", + ts_data->cfg_name, ret); + return ret; + } + + /* + * This compares the cfg version number from chip and the cfg + * data file. IT flashes only when version of cfg data file is + * greater than that of chip or if it is set for force cfg upgrade. + */ + if (force) + cfg_upgrade = true; + else if (IT_CFG_CHECK(ts_data->cfg_ver, cfg)) + cfg_upgrade = true; + + if (!cfg_upgrade) { + dev_err(dev, "CFG upgrade not required ...\n"); + dev_info(dev, + "Chip CFG : %X.%X.%X.%X Binary CFG : %X.%X.%X.%X\n", + ts_data->cfg_ver[0], ts_data->cfg_ver[1], + ts_data->cfg_ver[2], ts_data->cfg_ver[3], + cfg->data[cfg->size - 8], cfg->data[cfg->size - 7], + cfg->data[cfg->size - 6], cfg->data[cfg->size - 5]); + ret = -EFAULT; + goto out; + } else { + dev_info(dev, "Config upgrading...\n"); + + disable_irq(ts_data->client->irq); + /* enter cfg upload mode */ + ret = it7260_enter_exit_fw_ugrade_mode(ts_data, true); + if (ret < 0) { + dev_err(dev, "Can't enter cfg upgrade mode %d\n", ret); + enable_irq(ts_data->client->irq); + goto out; + } + /* flash config data if requested */ + ret = it7260_fw_flash_write_verify(ts_data, cfg->size, + cfg->data, CHIP_FLASH_SIZE - cfg->size); + if (ret < 0) { + dev_err(dev, + "failed to upgrade touch cfg data %d\n", ret); + ret = it7260_enter_exit_fw_ugrade_mode(ts_data, false); + if (ret < 0) + dev_err(dev, + "Can't exit cfg upgrade mode%d\n", ret); + + ret = it7260_firmware_reinitialize(ts_data, + CMD_FIRMWARE_REINIT_6F); + if (ret < 0) + dev_err(dev, "Can't reinit cfg %d\n", ret); + + ret = -EIO; + enable_irq(ts_data->client->irq); + goto out; + } else { + memcpy(ts_data->cfg_ver, cfg->data + + (cfg->size - 8 * sizeof(u8)), + VER_BUFFER_SIZE * sizeof(u8)); + dev_info(dev, "CFG upgrade is success. New cfg ver: %X.%X.%X.%X\n", + ts_data->cfg_ver[0], ts_data->cfg_ver[1], + ts_data->cfg_ver[2], ts_data->cfg_ver[3]); + + } + enable_irq(ts_data->client->irq); + } + +out: + release_firmware(cfg); + + return ret; +} + +static int it7260_fw_upload(struct it7260_ts_data *ts_data, bool force) +{ + const struct firmware *fw = NULL; + int ret; + bool fw_upgrade = false; + struct device *dev = &ts_data->client->dev; + + ret = request_firmware(&fw, ts_data->fw_name, dev); + if (ret) { + dev_err(dev, "failed to get firmware %s for it7260 %d\n", + ts_data->fw_name, ret); + return ret; + } + + /* + * This compares the fw version number from chip and the fw data + * file. It flashes only when version of fw data file is greater + * than that of chip or it it is set for force fw upgrade. + */ + if (force) + fw_upgrade = true; + else if (IT_FW_CHECK(ts_data->fw_ver, fw)) + fw_upgrade = true; + + if (!fw_upgrade) { + dev_err(dev, "FW upgrade not required ...\n"); + dev_info(dev, "Chip FW : %X.%X.%X.%X Binary FW : %X.%X.%X.%X\n", + ts_data->fw_ver[0], ts_data->fw_ver[1], + ts_data->fw_ver[2], ts_data->fw_ver[3], + fw->data[8], fw->data[9], fw->data[10], fw->data[11]); + ret = -EFAULT; + goto out; + } else { + dev_info(dev, "Firmware upgrading...\n"); + + disable_irq(ts_data->client->irq); + /* enter fw upload mode */ + ret = it7260_enter_exit_fw_ugrade_mode(ts_data, true); + if (ret < 0) { + dev_err(dev, "Can't enter fw upgrade mode %d\n", ret); + enable_irq(ts_data->client->irq); + goto out; + } + /* flash the firmware if requested */ + ret = it7260_fw_flash_write_verify(ts_data, fw->size, + fw->data, 0); + if (ret < 0) { + dev_err(dev, + "failed to upgrade touch firmware %d\n", ret); + ret = it7260_enter_exit_fw_ugrade_mode(ts_data, false); + if (ret < 0) + dev_err(dev, + "Can't exit fw upgrade mode %d\n", ret); + + ret = it7260_firmware_reinitialize(ts_data, + CMD_FIRMWARE_REINIT_6F); + if (ret < 0) + dev_err(dev, "Can't reinit firmware %d\n", ret); + + ret = -EIO; + enable_irq(ts_data->client->irq); + goto out; + } else { + memcpy(ts_data->fw_ver, fw->data + (8 * sizeof(u8)), + VER_BUFFER_SIZE * sizeof(u8)); + dev_info(dev, "FW upgrade is success. New fw ver: %X.%X.%X.%X\n", + ts_data->fw_ver[0], ts_data->fw_ver[1], + ts_data->fw_ver[2], ts_data->fw_ver[3]); + } + enable_irq(ts_data->client->irq); + } + +out: + release_firmware(fw); + + return ret; +} + +static int it7260_ts_chip_low_power_mode(struct it7260_ts_data *ts_data, + const u8 sleep_type) +{ + const u8 cmd_sleep[] = {CMD_PWR_CTL, 0x00, sleep_type}; + u8 dummy; + int ret; + + if (sleep_type) { + ret = it7260_i2c_write_no_ready_check(ts_data, BUF_COMMAND, + cmd_sleep, sizeof(cmd_sleep)); + if (ret != IT_I2C_WRITE_RET) + dev_err(&ts_data->client->dev, + "Can't go to sleep or low power mode(%d) %d\n", + sleep_type, ret); + else + ret = 0; + } else { + ret = it7260_i2c_read_no_ready_check(ts_data, BUF_QUERY, &dummy, + sizeof(dummy)); + if (ret != IT_I2C_READ_RET) + dev_err(&ts_data->client->dev, + "Can't go to active mode %d\n", ret); + else + ret = 0; + } + + msleep(WAIT_CHANGE_MODE); + return ret; +} + +static ssize_t sysfs_fw_upgrade_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + int mode = 0, ret; + + if (ts_data->suspended) { + dev_err(dev, "Device is suspended, can't flash fw!!!\n"); + return -EBUSY; + } + + ret = kstrtoint(buf, 10, &mode); + if (ret) { + dev_err(dev, "failed to read input for sysfs\n"); + return -EINVAL; + } + + mutex_lock(&ts_data->fw_cfg_mutex); + if (mode == 1) { + ts_data->fw_cfg_uploading = true; + ret = it7260_fw_upload(ts_data, false); + if (ret) { + dev_err(dev, "Failed to flash fw: %d", ret); + ts_data->fw_upgrade_result = false; + } else { + ts_data->fw_upgrade_result = true; + } + ts_data->fw_cfg_uploading = false; + } + mutex_unlock(&ts_data->fw_cfg_mutex); + + return count; +} + +static ssize_t sysfs_cfg_upgrade_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + int mode = 0, ret; + + if (ts_data->suspended) { + dev_err(dev, "Device is suspended, can't flash cfg!!!\n"); + return -EBUSY; + } + + ret = kstrtoint(buf, 10, &mode); + if (ret) { + dev_err(dev, "failed to read input for sysfs\n"); + return -EINVAL; + } + + mutex_lock(&ts_data->fw_cfg_mutex); + if (mode == 1) { + ts_data->fw_cfg_uploading = true; + ret = it7260_cfg_upload(ts_data, false); + if (ret) { + dev_err(dev, "Failed to flash cfg: %d", ret); + ts_data->cfg_upgrade_result = false; + } else { + ts_data->cfg_upgrade_result = true; + } + ts_data->fw_cfg_uploading = false; + } + mutex_unlock(&ts_data->fw_cfg_mutex); + + return count; +} + +static ssize_t sysfs_fw_upgrade_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + + return scnprintf(buf, MAX_BUFFER_SIZE, "%d\n", + ts_data->fw_upgrade_result); +} + +static ssize_t sysfs_cfg_upgrade_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + + return scnprintf(buf, MAX_BUFFER_SIZE, "%d\n", + ts_data->cfg_upgrade_result); +} + +static ssize_t sysfs_force_fw_upgrade_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + int mode = 0, ret; + + if (ts_data->suspended) { + dev_err(dev, "Device is suspended, can't flash fw!!!\n"); + return -EBUSY; + } + + ret = kstrtoint(buf, 10, &mode); + if (ret) { + dev_err(dev, "failed to read input for sysfs\n"); + return -EINVAL; + } + + mutex_lock(&ts_data->fw_cfg_mutex); + if (mode == 1) { + ts_data->fw_cfg_uploading = true; + ret = it7260_fw_upload(ts_data, true); + if (ret) { + dev_err(dev, "Failed to force flash fw: %d", ret); + ts_data->fw_upgrade_result = false; + } else { + ts_data->fw_upgrade_result = true; + } + ts_data->fw_cfg_uploading = false; + } + mutex_unlock(&ts_data->fw_cfg_mutex); + + return count; +} + +static ssize_t sysfs_force_cfg_upgrade_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + int mode = 0, ret; + + if (ts_data->suspended) { + dev_err(dev, "Device is suspended, can't flash cfg!!!\n"); + return -EBUSY; + } + + ret = kstrtoint(buf, 10, &mode); + if (ret) { + dev_err(dev, "failed to read input for sysfs\n"); + return -EINVAL; + } + + mutex_lock(&ts_data->fw_cfg_mutex); + if (mode == 1) { + ts_data->fw_cfg_uploading = true; + ret = it7260_cfg_upload(ts_data, true); + if (ret) { + dev_err(dev, "Failed to force flash cfg: %d", ret); + ts_data->cfg_upgrade_result = false; + } else { + ts_data->cfg_upgrade_result = true; + } + ts_data->fw_cfg_uploading = false; + } + mutex_unlock(&ts_data->fw_cfg_mutex); + + return count; +} + +static ssize_t sysfs_force_fw_upgrade_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + + return snprintf(buf, MAX_BUFFER_SIZE, "%d", ts_data->fw_upgrade_result); +} + +static ssize_t sysfs_force_cfg_upgrade_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + + return snprintf(buf, MAX_BUFFER_SIZE, "%d", + ts_data->cfg_upgrade_result); +} + +static ssize_t sysfs_calibration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + + return scnprintf(buf, MAX_BUFFER_SIZE, "%d\n", + ts_data->calibration_success); +} + +static int it7260_ts_send_calibration_cmd(struct it7260_ts_data *ts_data, + bool auto_tune_on) +{ + uint8_t cmd_calibrate[] = {CMD_CALIBRATE, 0, + auto_tune_on ? 1 : 0, 0, 0}; + + return it7260_i2c_write(ts_data, BUF_COMMAND, cmd_calibrate, + sizeof(cmd_calibrate)); +} + +static ssize_t sysfs_calibration_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + uint8_t resp; + int ret; + + ret = it7260_ts_send_calibration_cmd(ts_data, false); + if (ret < 0) { + dev_err(dev, "failed to send calibration command\n"); + } else { + ret = it7260_i2c_read(ts_data, BUF_RESPONSE, &resp, + sizeof(resp)); + if (ret == IT_I2C_READ_RET) + ts_data->calibration_success = true; + + /* + * previous logic that was here never called + * it7260_firmware_reinitialize() due to checking a + * guaranteed-not-null value against null. We now + * call it. Hopefully this is OK + */ + if (!resp) + dev_dbg(dev, "it7260_firmware_reinitialize-> %s\n", + it7260_firmware_reinitialize(ts_data, + CMD_FIRMWARE_REINIT_6F) ? "success" : "fail"); + } + + return count; +} + +static ssize_t sysfs_point_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + uint8_t pt_data[sizeof(struct point_data)]; + int readSuccess; + ssize_t ret; + + readSuccess = it7260_i2c_read_no_ready_check(ts_data, BUF_POINT_INFO, + pt_data, sizeof(pt_data)); + + if (readSuccess == IT_I2C_READ_RET) { + ret = scnprintf(buf, MAX_BUFFER_SIZE, + "point_show read ret[%d]--point[%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x][%x]\n", + readSuccess, pt_data[0], pt_data[1], + pt_data[2], pt_data[3], pt_data[4], + pt_data[5], pt_data[6], pt_data[7], + pt_data[8], pt_data[9], pt_data[10], + pt_data[11], pt_data[12], pt_data[13]); + } else { + ret = scnprintf(buf, MAX_BUFFER_SIZE, + "failed to read point data\n"); + } + dev_dbg(dev, "%s", buf); + + return ret; +} + +static ssize_t sysfs_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + + return scnprintf(buf, MAX_BUFFER_SIZE, + "fw{%X.%X.%X.%X} cfg{%X.%X.%X.%X}\n", + ts_data->fw_ver[0], ts_data->fw_ver[1], + ts_data->fw_ver[2], ts_data->fw_ver[3], + ts_data->cfg_ver[0], ts_data->cfg_ver[1], + ts_data->cfg_ver[2], ts_data->cfg_ver[3]); +} + +static ssize_t sysfs_sleep_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + /* + * The usefulness of this was questionable at best - we were at least + * leaking a byte of kernel data (by claiming to return a byte but not + * writing to buf. To fix this now we actually return the sleep status + */ + *buf = ts_data->suspended ? '1' : '0'; + + return 1; +} + +static ssize_t sysfs_sleep_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + int go_to_sleep, ret; + + ret = kstrtoint(buf, 10, &go_to_sleep); + + /* (ts_data->suspended == true && goToSleepVal > 0) means + * device is already suspended and you want it to be in sleep, + * (ts_data->suspended == false && goToSleepVal == 0) means + * device is already active and you also want it to be active. + */ + if ((ts_data->suspended && go_to_sleep > 0) || + (!ts_data->suspended && go_to_sleep == 0)) + dev_err(dev, "duplicate request to %s chip\n", + go_to_sleep ? "sleep" : "wake"); + else if (go_to_sleep) { + disable_irq(ts_data->client->irq); + it7260_ts_chip_low_power_mode(ts_data, PWR_CTL_SLEEP_MODE); + dev_dbg(dev, "touch is going to sleep...\n"); + } else { + it7260_ts_chip_low_power_mode(ts_data, PWR_CTL_ACTIVE_MODE); + enable_irq(ts_data->client->irq); + dev_dbg(dev, "touch is going to wake!\n"); + } + ts_data->suspended = go_to_sleep; + + return count; +} + +static ssize_t sysfs_cfg_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + char *strptr; + + if (count >= MAX_BUFFER_SIZE) { + dev_err(dev, "Input over %d chars long\n", MAX_BUFFER_SIZE); + return -EINVAL; + } + + strptr = strnstr(buf, ".bin", count); + if (!strptr) { + dev_err(dev, "Input is invalid cfg file\n"); + return -EINVAL; + } + + strlcpy(ts_data->cfg_name, buf, count); + + return count; +} + +static ssize_t sysfs_cfg_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + + if (strnlen(ts_data->cfg_name, MAX_BUFFER_SIZE) > 0) + return scnprintf(buf, MAX_BUFFER_SIZE, "%s\n", + ts_data->cfg_name); + else + return scnprintf(buf, MAX_BUFFER_SIZE, + "No config file name given\n"); +} + +static ssize_t sysfs_fw_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + char *strptr; + + if (count >= MAX_BUFFER_SIZE) { + dev_err(dev, "Input over %d chars long\n", MAX_BUFFER_SIZE); + return -EINVAL; + } + + strptr = strnstr(buf, ".bin", count); + if (!strptr) { + dev_err(dev, "Input is invalid fw file\n"); + return -EINVAL; + } + + strlcpy(ts_data->fw_name, buf, count); + return count; +} + +static ssize_t sysfs_fw_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + + if (strnlen(ts_data->fw_name, MAX_BUFFER_SIZE) > 0) + return scnprintf(buf, MAX_BUFFER_SIZE, "%s\n", + ts_data->fw_name); + else + return scnprintf(buf, MAX_BUFFER_SIZE, + "No firmware file name given\n"); +} + +static DEVICE_ATTR(version, S_IRUGO | S_IWUSR, + sysfs_version_show, NULL); +static DEVICE_ATTR(sleep, S_IRUGO | S_IWUSR, + sysfs_sleep_show, sysfs_sleep_store); +static DEVICE_ATTR(calibration, S_IRUGO | S_IWUSR, + sysfs_calibration_show, sysfs_calibration_store); +static DEVICE_ATTR(fw_update, S_IRUGO | S_IWUSR, + sysfs_fw_upgrade_show, sysfs_fw_upgrade_store); +static DEVICE_ATTR(cfg_update, S_IRUGO | S_IWUSR, + sysfs_cfg_upgrade_show, sysfs_cfg_upgrade_store); +static DEVICE_ATTR(point, S_IRUGO | S_IWUSR, + sysfs_point_show, NULL); +static DEVICE_ATTR(fw_name, S_IRUGO | S_IWUSR, + sysfs_fw_name_show, sysfs_fw_name_store); +static DEVICE_ATTR(cfg_name, S_IRUGO | S_IWUSR, + sysfs_cfg_name_show, sysfs_cfg_name_store); +static DEVICE_ATTR(force_fw_update, S_IRUGO | S_IWUSR, + sysfs_force_fw_upgrade_show, + sysfs_force_fw_upgrade_store); +static DEVICE_ATTR(force_cfg_update, S_IRUGO | S_IWUSR, + sysfs_force_cfg_upgrade_show, + sysfs_force_cfg_upgrade_store); + +static struct attribute *it7260_attributes[] = { + &dev_attr_version.attr, + &dev_attr_sleep.attr, + &dev_attr_calibration.attr, + &dev_attr_fw_update.attr, + &dev_attr_cfg_update.attr, + &dev_attr_point.attr, + &dev_attr_fw_name.attr, + &dev_attr_cfg_name.attr, + &dev_attr_force_fw_update.attr, + &dev_attr_force_cfg_update.attr, + NULL +}; + +static const struct attribute_group it7260_attr_group = { + .attrs = it7260_attributes, +}; + +static void it7260_ts_release_all(struct it7260_ts_data *ts_data) +{ + int finger; + + for (finger = 0; finger < ts_data->pdata->num_of_fingers; finger++) { + input_mt_slot(ts_data->input_dev, finger); + input_mt_report_slot_state(ts_data->input_dev, + MT_TOOL_FINGER, 0); + } + + input_report_key(ts_data->input_dev, BTN_TOUCH, 0); + input_sync(ts_data->input_dev); +} + +static irqreturn_t it7260_ts_threaded_handler(int irq, void *devid) +{ + struct point_data pt_data; + struct it7260_ts_data *ts_data = devid; + struct input_dev *input_dev = ts_data->input_dev; + u8 dev_status, finger, touch_count = 0, finger_status; + u8 pressure = FD_PRESSURE_NONE; + u16 x, y; + bool palm_detected; + int ret; + + /* verify there is point data to read & it is readable and valid */ + ret = it7260_i2c_read_no_ready_check(ts_data, BUF_QUERY, &dev_status, + sizeof(dev_status)); + if (ret == IT_I2C_READ_RET) + if (!((dev_status & PT_INFO_BITS) & PT_INFO_YES)) + return IRQ_HANDLED; + ret = it7260_i2c_read_no_ready_check(ts_data, BUF_POINT_INFO, + (void *)&pt_data, sizeof(pt_data)); + if (ret != IT_I2C_READ_RET) { + dev_err(&ts_data->client->dev, + "failed to read point data buffer\n"); + return IRQ_HANDLED; + } + + /* Check if controller moves from idle to active state */ + if ((pt_data.flags & PD_FLAGS_DATA_TYPE_BITS) != + PD_FLAGS_DATA_TYPE_TOUCH) { + /* + * This code adds the touch-to-wake functionality to the ITE + * tech driver. When user puts a finger on touch controller in + * idle state, the controller moves to active state and driver + * sends the KEY_WAKEUP event to wake the device. The + * pm_stay_awake() call tells the pm core to stay awake until + * the CPU cores are up already. The schedule_work() call + * schedule a work that tells the pm core to relax once the CPU + * cores are up. + */ + if ((pt_data.flags & PD_FLAGS_DATA_TYPE_BITS) == + PD_FLAGS_IDLE_TO_ACTIVE && + pt_data.gesture_id == 0) { + pm_stay_awake(&ts_data->client->dev); + input_report_key(input_dev, KEY_WAKEUP, 1); + input_sync(input_dev); + input_report_key(input_dev, KEY_WAKEUP, 0); + input_sync(input_dev); + schedule_work(&ts_data->work_pm_relax); + } else { + dev_dbg(&ts_data->client->dev, + "Ignore the touch data\n"); + } + return IRQ_HANDLED; + } + + /* + * Check if touch data also includes any palm gesture or not. + * If palm gesture is detected, then send the keycode parsed + * from the DT. + */ + palm_detected = pt_data.gesture_id & PD_PALM_FLAG_BIT; + if (palm_detected && ts_data->pdata->palm_detect_en) { + input_report_key(input_dev, + ts_data->pdata->palm_detect_keycode, 1); + input_sync(input_dev); + input_report_key(input_dev, + ts_data->pdata->palm_detect_keycode, 0); + input_sync(input_dev); + } + + for (finger = 0; finger < ts_data->pdata->num_of_fingers; finger++) { + finger_status = pt_data.flags & (0x01 << finger); + + input_mt_slot(input_dev, finger); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, + finger_status != 0); + + x = pt_data.fd[finger].xLo + + (((u16)(pt_data.fd[finger].hi & 0x0F)) << 8); + y = pt_data.fd[finger].yLo + + (((u16)(pt_data.fd[finger].hi & 0xF0)) << 4); + + pressure = pt_data.fd[finger].pressure & FD_PRESSURE_BITS; + + if (finger_status) { + if (pressure >= FD_PRESSURE_LIGHT) { + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_abs(input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(input_dev, + ABS_MT_POSITION_Y, y); + touch_count++; + } + } + } + + input_report_key(input_dev, BTN_TOUCH, touch_count > 0); + input_sync(input_dev); + + return IRQ_HANDLED; +} + +static void it7260_ts_work_func(struct work_struct *work) +{ + struct it7260_ts_data *ts_data = container_of(work, + struct it7260_ts_data, work_pm_relax); + + pm_relax(&ts_data->client->dev); +} + +static int it7260_ts_chip_identify(struct it7260_ts_data *ts_data) +{ + static const uint8_t cmd_ident[] = {CMD_IDENT_CHIP}; + static const uint8_t expected_id[] = {0x0A, 'I', 'T', 'E', '7', + '2', '6', '0'}; + uint8_t chip_id[10] = {0,}; + int ret; + + /* + * Sometimes, the controller may not respond immediately after + * writing the command, so wait for device to get ready. + * FALSE means to retry 20 times at max to read the chip status. + * TRUE means to add delay in each retry. + */ + ret = it7260_wait_device_ready(ts_data, false, true); + if (ret < 0) { + dev_err(&ts_data->client->dev, + "failed to read chip status %d\n", ret); + return ret; + } + + ret = it7260_i2c_write_no_ready_check(ts_data, BUF_COMMAND, cmd_ident, + sizeof(cmd_ident)); + if (ret != IT_I2C_WRITE_RET) { + dev_err(&ts_data->client->dev, + "failed to write CMD_IDENT_CHIP %d\n", ret); + return ret; + } + + /* + * Sometimes, the controller may not respond immediately after + * writing the command, so wait for device to get ready. + * TRUE means to retry 500 times at max to read the chip status. + * FALSE means to avoid unnecessary delays in each retry. + */ + ret = it7260_wait_device_ready(ts_data, true, false); + if (ret < 0) { + dev_err(&ts_data->client->dev, + "failed to read chip status %d\n", ret); + return ret; + } + + + ret = it7260_i2c_read_no_ready_check(ts_data, BUF_RESPONSE, chip_id, + sizeof(chip_id)); + if (ret != IT_I2C_READ_RET) { + dev_err(&ts_data->client->dev, + "failed to read chip-id %d\n", ret); + return ret; + } + dev_info(&ts_data->client->dev, + "it7260_ts_chip_identify read id: %02X %c%c%c%c%c%c%c %c%c\n", + chip_id[0], chip_id[1], chip_id[2], chip_id[3], chip_id[4], + chip_id[5], chip_id[6], chip_id[7], chip_id[8], chip_id[9]); + + if (memcmp(chip_id, expected_id, sizeof(expected_id))) + return -EINVAL; + + if (chip_id[8] == '5' && chip_id[9] == '6') + dev_info(&ts_data->client->dev, "rev BX3 found\n"); + else if (chip_id[8] == '6' && chip_id[9] == '6') + dev_info(&ts_data->client->dev, "rev BX4 found\n"); + else + dev_info(&ts_data->client->dev, "unknown revision (0x%02X 0x%02X) found\n", + chip_id[8], chip_id[9]); + + return 0; +} + +static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA) +{ + return (regulator_count_voltages(reg) > 0) ? + regulator_set_load(reg, load_uA) : 0; +} + +static int it7260_regulator_configure(struct it7260_ts_data *ts_data, bool on) +{ + int retval; + + if (on == false) + goto hw_shutdown; + + ts_data->vdd = devm_regulator_get(&ts_data->client->dev, "vdd"); + if (IS_ERR(ts_data->vdd)) { + dev_err(&ts_data->client->dev, + "%s: Failed to get vdd regulator\n", __func__); + return PTR_ERR(ts_data->vdd); + } + + if (regulator_count_voltages(ts_data->vdd) > 0) { + retval = regulator_set_voltage(ts_data->vdd, + IT_VTG_MIN_UV, IT_VTG_MAX_UV); + if (retval) { + dev_err(&ts_data->client->dev, + "regulator set_vtg failed retval =%d\n", + retval); + goto err_set_vtg_vdd; + } + } + + ts_data->avdd = devm_regulator_get(&ts_data->client->dev, "avdd"); + if (IS_ERR(ts_data->avdd)) { + dev_err(&ts_data->client->dev, + "%s: Failed to get i2c regulator\n", __func__); + retval = PTR_ERR(ts_data->avdd); + goto err_get_vtg_i2c; + } + + if (regulator_count_voltages(ts_data->avdd) > 0) { + retval = regulator_set_voltage(ts_data->avdd, + IT_I2C_VTG_MIN_UV, IT_I2C_VTG_MAX_UV); + if (retval) { + dev_err(&ts_data->client->dev, + "reg set i2c vtg failed retval =%d\n", + retval); + goto err_set_vtg_i2c; + } + } + + return 0; + +err_set_vtg_i2c: +err_get_vtg_i2c: + if (regulator_count_voltages(ts_data->vdd) > 0) + regulator_set_voltage(ts_data->vdd, 0, IT_VTG_MAX_UV); +err_set_vtg_vdd: + return retval; + +hw_shutdown: + if (regulator_count_voltages(ts_data->vdd) > 0) + regulator_set_voltage(ts_data->vdd, 0, IT_VTG_MAX_UV); + if (regulator_count_voltages(ts_data->avdd) > 0) + regulator_set_voltage(ts_data->avdd, 0, IT_I2C_VTG_MAX_UV); + return 0; +}; + +static int it7260_power_on(struct it7260_ts_data *ts_data, bool on) +{ + int retval; + + if (on == false) + goto power_off; + + retval = reg_set_optimum_mode_check(ts_data->vdd, + IT_ACTIVE_LOAD_UA); + if (retval < 0) { + dev_err(&ts_data->client->dev, + "Regulator vdd set_opt failed rc=%d\n", + retval); + return retval; + } + + retval = regulator_enable(ts_data->vdd); + if (retval) { + dev_err(&ts_data->client->dev, + "Regulator vdd enable failed rc=%d\n", + retval); + goto error_reg_en_vdd; + } + + retval = reg_set_optimum_mode_check(ts_data->avdd, + IT_I2C_ACTIVE_LOAD_UA); + if (retval < 0) { + dev_err(&ts_data->client->dev, + "Regulator avdd set_opt failed rc=%d\n", + retval); + goto error_reg_opt_i2c; + } + + retval = regulator_enable(ts_data->avdd); + if (retval) { + dev_err(&ts_data->client->dev, + "Regulator avdd enable failed rc=%d\n", + retval); + goto error_reg_en_avdd; + } + + return 0; + +error_reg_en_avdd: + reg_set_optimum_mode_check(ts_data->avdd, 0); +error_reg_opt_i2c: + regulator_disable(ts_data->vdd); +error_reg_en_vdd: + reg_set_optimum_mode_check(ts_data->vdd, 0); + return retval; + +power_off: + reg_set_optimum_mode_check(ts_data->vdd, 0); + regulator_disable(ts_data->vdd); + reg_set_optimum_mode_check(ts_data->avdd, 0); + regulator_disable(ts_data->avdd); + + return 0; +} + +static int it7260_gpio_configure(struct it7260_ts_data *ts_data, bool on) +{ + int retval = 0; + + if (on) { + if (gpio_is_valid(ts_data->pdata->irq_gpio)) { + /* configure touchscreen irq gpio */ + retval = gpio_request(ts_data->pdata->irq_gpio, + "ite_irq_gpio"); + if (retval) { + dev_err(&ts_data->client->dev, + "unable to request irq gpio [%d]\n", + retval); + goto err_irq_gpio_req; + } + + retval = gpio_direction_input(ts_data->pdata->irq_gpio); + if (retval) { + dev_err(&ts_data->client->dev, + "unable to set direction for irq gpio [%d]\n", + retval); + goto err_irq_gpio_dir; + } + } else { + dev_err(&ts_data->client->dev, + "irq gpio not provided\n"); + goto err_irq_gpio_req; + } + + if (gpio_is_valid(ts_data->pdata->reset_gpio)) { + /* configure touchscreen reset out gpio */ + retval = gpio_request(ts_data->pdata->reset_gpio, + "ite_reset_gpio"); + if (retval) { + dev_err(&ts_data->client->dev, + "unable to request reset gpio [%d]\n", + retval); + goto err_reset_gpio_req; + } + + retval = gpio_direction_output( + ts_data->pdata->reset_gpio, 1); + if (retval) { + dev_err(&ts_data->client->dev, + "unable to set direction for reset gpio [%d]\n", + retval); + goto err_reset_gpio_dir; + } + + if (ts_data->pdata->low_reset) + gpio_set_value(ts_data->pdata->reset_gpio, 0); + else + gpio_set_value(ts_data->pdata->reset_gpio, 1); + + msleep(ts_data->pdata->reset_delay); + } else { + dev_err(&ts_data->client->dev, + "reset gpio not provided\n"); + goto err_reset_gpio_req; + } + } else { + if (gpio_is_valid(ts_data->pdata->irq_gpio)) + gpio_free(ts_data->pdata->irq_gpio); + if (gpio_is_valid(ts_data->pdata->reset_gpio)) { + /* + * This is intended to save leakage current + * only. Even if the call(gpio_direction_input) + * fails, only leakage current will be more but + * functionality will not be affected. + */ + retval = gpio_direction_input( + ts_data->pdata->reset_gpio); + if (retval) { + dev_err(&ts_data->client->dev, + "unable to set direction for gpio reset [%d]\n", + retval); + } + gpio_free(ts_data->pdata->reset_gpio); + } + } + + return 0; + +err_reset_gpio_dir: + if (gpio_is_valid(ts_data->pdata->reset_gpio)) + gpio_free(ts_data->pdata->reset_gpio); +err_reset_gpio_req: +err_irq_gpio_dir: + if (gpio_is_valid(ts_data->pdata->irq_gpio)) + gpio_free(ts_data->pdata->irq_gpio); +err_irq_gpio_req: + return retval; +} + +#if CONFIG_OF +static int it7260_get_dt_coords(struct device *dev, char *name, + struct it7260_ts_platform_data *pdata) +{ + u32 coords[IT7260_COORDS_ARR_SIZE]; + struct property *prop; + struct device_node *np = dev->of_node; + int coords_size, rc; + + prop = of_find_property(np, name, NULL); + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + coords_size = prop->length / sizeof(u32); + if (coords_size != IT7260_COORDS_ARR_SIZE) { + dev_err(dev, "invalid %s\n", name); + return -EINVAL; + } + + rc = of_property_read_u32_array(np, name, coords, coords_size); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read %s\n", name); + return rc; + } + + if (strcmp(name, "ite,panel-coords") == 0) { + pdata->panel_minx = coords[0]; + pdata->panel_miny = coords[1]; + pdata->panel_maxx = coords[2]; + pdata->panel_maxy = coords[3]; + + if (pdata->panel_maxx == 0 || pdata->panel_minx > 0) + rc = -EINVAL; + else if (pdata->panel_maxy == 0 || pdata->panel_miny > 0) + rc = -EINVAL; + + if (rc) { + dev_err(dev, "Invalid panel resolution %d\n", rc); + return rc; + } + } else if (strcmp(name, "ite,display-coords") == 0) { + pdata->disp_minx = coords[0]; + pdata->disp_miny = coords[1]; + pdata->disp_maxx = coords[2]; + pdata->disp_maxy = coords[3]; + } else { + dev_err(dev, "unsupported property %s\n", name); + return -EINVAL; + } + + return 0; +} + +static int it7260_parse_dt(struct device *dev, + struct it7260_ts_platform_data *pdata) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + struct device_node *np = dev->of_node; + u32 temp_val; + int rc; + + /* reset, irq gpio info */ + pdata->reset_gpio = of_get_named_gpio_flags(np, + "ite,reset-gpio", 0, &pdata->reset_gpio_flags); + pdata->irq_gpio = of_get_named_gpio_flags(np, + "ite,irq-gpio", 0, &pdata->irq_gpio_flags); + + rc = of_property_read_u32(np, "ite,num-fingers", &temp_val); + if (!rc) + pdata->num_of_fingers = temp_val; + else if (rc != -EINVAL) { + dev_err(dev, "Unable to read reset delay\n"); + return rc; + } + + pdata->wakeup = of_property_read_bool(np, "ite,wakeup"); + pdata->palm_detect_en = of_property_read_bool(np, "ite,palm-detect-en"); + if (pdata->palm_detect_en) { + rc = of_property_read_u32(np, "ite,palm-detect-keycode", + &temp_val); + if (!rc) { + pdata->palm_detect_keycode = temp_val; + } else { + dev_err(dev, "Unable to read palm-detect-keycode\n"); + return rc; + } + } + + rc = of_property_read_string(np, "ite,fw-name", &pdata->fw_name); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw image name %d\n", rc); + return rc; + } + + rc = of_property_read_string(np, "ite,cfg-name", &pdata->cfg_name); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read cfg image name %d\n", rc); + return rc; + } + + snprintf(ts_data->fw_name, MAX_BUFFER_SIZE, "%s", + (pdata->fw_name != NULL) ? pdata->fw_name : FW_NAME); + snprintf(ts_data->cfg_name, MAX_BUFFER_SIZE, "%s", + (pdata->cfg_name != NULL) ? pdata->cfg_name : CFG_NAME); + + rc = of_property_read_u32(np, "ite,reset-delay", &temp_val); + if (!rc) + pdata->reset_delay = temp_val; + else if (rc != -EINVAL) { + dev_err(dev, "Unable to read reset delay\n"); + return rc; + } + + rc = of_property_read_u32(np, "ite,avdd-lpm-cur", &temp_val); + if (!rc) { + pdata->avdd_lpm_cur = temp_val; + } else if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read avdd lpm current value %d\n", rc); + return rc; + } + + pdata->low_reset = of_property_read_bool(np, "ite,low-reset"); + + rc = it7260_get_dt_coords(dev, "ite,display-coords", pdata); + if (rc && (rc != -EINVAL)) + return rc; + + rc = it7260_get_dt_coords(dev, "ite,panel-coords", pdata); + if (rc && (rc != -EINVAL)) + return rc; + + return 0; +} +#else +static inline int it7260_ts_parse_dt(struct device *dev, + struct it7260_ts_platform_data *pdata) +{ + return 0; +} +#endif + +static int it7260_ts_pinctrl_init(struct it7260_ts_data *ts_data) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + ts_data->ts_pinctrl = devm_pinctrl_get(&(ts_data->client->dev)); + if (IS_ERR_OR_NULL(ts_data->ts_pinctrl)) { + retval = PTR_ERR(ts_data->ts_pinctrl); + dev_dbg(&ts_data->client->dev, + "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + ts_data->pinctrl_state_active + = pinctrl_lookup_state(ts_data->ts_pinctrl, + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(ts_data->pinctrl_state_active)) { + retval = PTR_ERR(ts_data->pinctrl_state_active); + dev_err(&ts_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + ts_data->pinctrl_state_suspend + = pinctrl_lookup_state(ts_data->ts_pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(ts_data->pinctrl_state_suspend)) { + retval = PTR_ERR(ts_data->pinctrl_state_suspend); + dev_err(&ts_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + ts_data->pinctrl_state_release + = pinctrl_lookup_state(ts_data->ts_pinctrl, + PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(ts_data->pinctrl_state_release)) { + retval = PTR_ERR(ts_data->pinctrl_state_release); + dev_dbg(&ts_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(ts_data->ts_pinctrl); +err_pinctrl_get: + ts_data->ts_pinctrl = NULL; + return retval; +} + +static int it7260_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + static const uint8_t cmd_start[] = {CMD_UNKNOWN_7}; + struct it7260_ts_data *ts_data; + struct it7260_ts_platform_data *pdata; + uint8_t rsp[2]; + int ret = -1, err; + struct dentry *temp; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "need I2C_FUNC_I2C\n"); + return -ENODEV; + } + + ts_data = devm_kzalloc(&client->dev, sizeof(*ts_data), GFP_KERNEL); + if (!ts_data) + return -ENOMEM; + + ts_data->client = client; + i2c_set_clientdata(client, ts_data); + + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ret = it7260_parse_dt(&client->dev, pdata); + if (ret) + return ret; + } else { + pdata = client->dev.platform_data; + } + + if (!pdata) { + dev_err(&client->dev, "No platform data found\n"); + return -ENOMEM; + } + + ts_data->pdata = pdata; + + ret = it7260_regulator_configure(ts_data, true); + if (ret < 0) { + dev_err(&client->dev, "Failed to configure regulators\n"); + goto err_reg_configure; + } + + ret = it7260_power_on(ts_data, true); + if (ret < 0) { + dev_err(&client->dev, "Failed to power on\n"); + goto err_power_device; + } + + /* + * After enabling regulators, controller needs a delay to come to + * an active state. + */ + msleep(DELAY_VTG_REG_EN); + + ret = it7260_ts_pinctrl_init(ts_data); + if (!ret && ts_data->ts_pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is found + * let pins to be configured in active state. If not + * found continue further without error. + */ + ret = pinctrl_select_state(ts_data->ts_pinctrl, + ts_data->pinctrl_state_active); + if (ret < 0) { + dev_err(&ts_data->client->dev, + "failed to select pin to active state %d", + ret); + } + } else { + ret = it7260_gpio_configure(ts_data, true); + if (ret < 0) { + dev_err(&client->dev, "Failed to configure gpios\n"); + goto err_gpio_config; + } + } + + ret = it7260_ts_chip_identify(ts_data); + if (ret) { + dev_err(&client->dev, "Failed to identify chip %d!!!", ret); + goto err_identification_fail; + } + + it7260_get_chip_versions(ts_data); + + ts_data->input_dev = input_allocate_device(); + if (!ts_data->input_dev) { + dev_err(&client->dev, "failed to allocate input device\n"); + ret = -ENOMEM; + goto err_input_alloc; + } + + /* Initialize mutex for fw and cfg upgrade */ + mutex_init(&ts_data->fw_cfg_mutex); + + ts_data->input_dev->name = DEVICE_NAME; + ts_data->input_dev->phys = "I2C"; + ts_data->input_dev->id.bustype = BUS_I2C; + ts_data->input_dev->id.vendor = 0x0001; + ts_data->input_dev->id.product = 0x7260; + set_bit(EV_SYN, ts_data->input_dev->evbit); + set_bit(EV_KEY, ts_data->input_dev->evbit); + set_bit(EV_ABS, ts_data->input_dev->evbit); + set_bit(INPUT_PROP_DIRECT, ts_data->input_dev->propbit); + set_bit(BTN_TOUCH, ts_data->input_dev->keybit); + input_set_abs_params(ts_data->input_dev, ABS_MT_POSITION_X, + ts_data->pdata->disp_minx, ts_data->pdata->disp_maxx, 0, 0); + input_set_abs_params(ts_data->input_dev, ABS_MT_POSITION_Y, + ts_data->pdata->disp_miny, ts_data->pdata->disp_maxy, 0, 0); + input_mt_init_slots(ts_data->input_dev, + ts_data->pdata->num_of_fingers, 0); + + input_set_drvdata(ts_data->input_dev, ts_data); + + if (pdata->wakeup) { + set_bit(KEY_WAKEUP, ts_data->input_dev->keybit); + INIT_WORK(&ts_data->work_pm_relax, it7260_ts_work_func); + device_init_wakeup(&client->dev, pdata->wakeup); + } + + if (pdata->palm_detect_en) + set_bit(ts_data->pdata->palm_detect_keycode, + ts_data->input_dev->keybit); + + if (input_register_device(ts_data->input_dev)) { + dev_err(&client->dev, "failed to register input device\n"); + goto err_input_register; + } + + if (request_threaded_irq(client->irq, NULL, it7260_ts_threaded_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, ts_data)) { + dev_err(&client->dev, "request_irq failed\n"); + goto err_irq_reg; + } + + if (sysfs_create_group(&(client->dev.kobj), &it7260_attr_group)) { + dev_err(&client->dev, "failed to register sysfs #2\n"); + goto err_sysfs_grp_create; + } + +#if defined(CONFIG_FB) + ts_data->fb_notif.notifier_call = fb_notifier_callback; + + ret = fb_register_client(&ts_data->fb_notif); + if (ret) + dev_err(&client->dev, "Unable to register fb_notifier %d\n", + ret); +#endif + + it7260_i2c_write_no_ready_check(ts_data, BUF_COMMAND, cmd_start, + sizeof(cmd_start)); + msleep(pdata->reset_delay); + it7260_i2c_read_no_ready_check(ts_data, BUF_RESPONSE, rsp, sizeof(rsp)); + msleep(pdata->reset_delay); + + ts_data->dir = debugfs_create_dir(DEBUGFS_DIR_NAME, NULL); + if (ts_data->dir == NULL || IS_ERR(ts_data->dir)) { + dev_err(&client->dev, + "%s: Failed to create debugfs directory, ret = %ld\n", + __func__, PTR_ERR(ts_data->dir)); + ret = PTR_ERR(ts_data->dir); + goto err_create_debugfs_dir; + } + + temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, ts_data->dir, + ts_data, &debug_suspend_fops); + if (temp == NULL || IS_ERR(temp)) { + dev_err(&client->dev, + "%s: Failed to create suspend debugfs file, ret = %ld\n", + __func__, PTR_ERR(temp)); + ret = PTR_ERR(temp); + goto err_create_debugfs_file; + } + + return 0; + +err_create_debugfs_file: + debugfs_remove_recursive(ts_data->dir); +err_create_debugfs_dir: +#if defined(CONFIG_FB) + if (fb_unregister_client(&ts_data->fb_notif)) + dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); +#endif + sysfs_remove_group(&(client->dev.kobj), &it7260_attr_group); + +err_sysfs_grp_create: + free_irq(client->irq, ts_data); + +err_irq_reg: + input_unregister_device(ts_data->input_dev); + +err_input_register: + if (pdata->wakeup) { + cancel_work_sync(&ts_data->work_pm_relax); + device_init_wakeup(&client->dev, false); + } + if (ts_data->input_dev) + input_free_device(ts_data->input_dev); + ts_data->input_dev = NULL; + +err_input_alloc: +err_identification_fail: + if (ts_data->ts_pinctrl) { + if (IS_ERR_OR_NULL(ts_data->pinctrl_state_release)) { + devm_pinctrl_put(ts_data->ts_pinctrl); + ts_data->ts_pinctrl = NULL; + } else { + err = pinctrl_select_state(ts_data->ts_pinctrl, + ts_data->pinctrl_state_release); + if (err) + dev_err(&ts_data->client->dev, + "failed to select relase pinctrl state %d\n", + err); + } + } else { + if (gpio_is_valid(pdata->reset_gpio)) + gpio_free(pdata->reset_gpio); + if (gpio_is_valid(pdata->irq_gpio)) + gpio_free(pdata->irq_gpio); + } + +err_gpio_config: + it7260_power_on(ts_data, false); + +err_power_device: + it7260_regulator_configure(ts_data, false); + +err_reg_configure: + return ret; +} + +static int it7260_ts_remove(struct i2c_client *client) +{ + struct it7260_ts_data *ts_data = i2c_get_clientdata(client); + int ret; + + debugfs_remove_recursive(ts_data->dir); +#if defined(CONFIG_FB) + if (fb_unregister_client(&ts_data->fb_notif)) + dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); +#endif + sysfs_remove_group(&(client->dev.kobj), &it7260_attr_group); + free_irq(client->irq, ts_data); + input_unregister_device(ts_data->input_dev); + if (ts_data->input_dev) + input_free_device(ts_data->input_dev); + ts_data->input_dev = NULL; + if (ts_data->pdata->wakeup) { + cancel_work_sync(&ts_data->work_pm_relax); + device_init_wakeup(&client->dev, false); + } + if (ts_data->ts_pinctrl) { + if (IS_ERR_OR_NULL(ts_data->pinctrl_state_release)) { + devm_pinctrl_put(ts_data->ts_pinctrl); + ts_data->ts_pinctrl = NULL; + } else { + ret = pinctrl_select_state(ts_data->ts_pinctrl, + ts_data->pinctrl_state_release); + if (ret) + dev_err(&ts_data->client->dev, + "failed to select relase pinctrl state %d\n", + ret); + } + } else { + if (gpio_is_valid(ts_data->pdata->reset_gpio)) + gpio_free(ts_data->pdata->reset_gpio); + if (gpio_is_valid(ts_data->pdata->irq_gpio)) + gpio_free(ts_data->pdata->irq_gpio); + } + it7260_power_on(ts_data, false); + it7260_regulator_configure(ts_data, false); + + return 0; +} + +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct it7260_ts_data *ts_data = container_of(self, + struct it7260_ts_data, fb_notif); + struct fb_event *evdata = data; + int *blank; + + if (evdata && evdata->data && ts_data && ts_data->client) { + if (event == FB_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) + it7260_ts_resume(&(ts_data->client->dev)); + else if (*blank == FB_BLANK_POWERDOWN || + *blank == FB_BLANK_VSYNC_SUSPEND) + it7260_ts_suspend(&(ts_data->client->dev)); + } + } + + return 0; +} +#endif + +#ifdef CONFIG_PM +static int it7260_ts_resume(struct device *dev) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + int retval; + + if (device_may_wakeup(dev)) { + if (ts_data->in_low_power_mode) { + /* Set active current for the avdd regulator */ + if (ts_data->pdata->avdd_lpm_cur) { + retval = reg_set_optimum_mode_check( + ts_data->avdd, + IT_I2C_ACTIVE_LOAD_UA); + if (retval < 0) + dev_err(dev, "Regulator avdd set_opt failed at resume rc=%d\n", + retval); + } + + ts_data->in_low_power_mode = false; + disable_irq_wake(ts_data->client->irq); + } + return 0; + } + + if (ts_data->ts_pinctrl) { + retval = pinctrl_select_state(ts_data->ts_pinctrl, + ts_data->pinctrl_state_active); + if (retval < 0) { + dev_err(dev, "Cannot get default pinctrl state %d\n", + retval); + goto err_pinctrl_select_suspend; + } + } + + enable_irq(ts_data->client->irq); + ts_data->suspended = false; + return 0; + +err_pinctrl_select_suspend: + return retval; +} + +static int it7260_ts_suspend(struct device *dev) +{ + struct it7260_ts_data *ts_data = dev_get_drvdata(dev); + int retval; + + if (ts_data->fw_cfg_uploading) { + dev_dbg(dev, "Fw/cfg uploading. Can't go to suspend.\n"); + return -EBUSY; + } + + if (device_may_wakeup(dev)) { + if (!ts_data->in_low_power_mode) { + /* put the device in low power idle mode */ + retval = it7260_ts_chip_low_power_mode(ts_data, + PWR_CTL_LOW_POWER_MODE); + if (retval) + dev_err(dev, "Can't go to low power mode %d\n", + retval); + + /* Set lpm current for avdd regulator */ + if (ts_data->pdata->avdd_lpm_cur) { + retval = reg_set_optimum_mode_check( + ts_data->avdd, + ts_data->pdata->avdd_lpm_cur); + if (retval < 0) + dev_err(dev, "Regulator avdd set_opt failed at suspend rc=%d\n", + retval); + } + + ts_data->in_low_power_mode = true; + enable_irq_wake(ts_data->client->irq); + } + return 0; + } + + disable_irq(ts_data->client->irq); + + it7260_ts_release_all(ts_data); + + if (ts_data->ts_pinctrl) { + retval = pinctrl_select_state(ts_data->ts_pinctrl, + ts_data->pinctrl_state_suspend); + if (retval < 0) { + dev_err(dev, "Cannot get idle pinctrl state %d\n", + retval); + goto err_pinctrl_select_suspend; + } + } + + ts_data->suspended = true; + + return 0; + +err_pinctrl_select_suspend: + return retval; +} + +static const struct dev_pm_ops it7260_ts_dev_pm_ops = { + .suspend = it7260_ts_suspend, + .resume = it7260_ts_resume, +}; +#else +static int it7260_ts_resume(struct device *dev) +{ + return 0; +} + +static int it7260_ts_suspend(struct device *dev) +{ + return 0; +} +#endif + +static const struct i2c_device_id it7260_ts_id[] = { + { DEVICE_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, it7260_ts_id); + +static const struct of_device_id it7260_match_table[] = { + { .compatible = "ite,it7260_ts",}, + {}, +}; + +static struct i2c_driver it7260_ts_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DEVICE_NAME, + .of_match_table = it7260_match_table, +#ifdef CONFIG_PM + .pm = &it7260_ts_dev_pm_ops, +#endif + }, + .probe = it7260_ts_probe, + .remove = it7260_ts_remove, + .id_table = it7260_ts_id, +}; + +module_i2c_driver(it7260_ts_driver); + +MODULE_DESCRIPTION("it7260 Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/maxim_sti.c b/drivers/input/touchscreen/maxim_sti.c new file mode 100644 index 000000000000..d10f26357611 --- /dev/null +++ b/drivers/input/touchscreen/maxim_sti.c @@ -0,0 +1,2842 @@ +/* drivers/input/touchscreen/maxim_sti.c + * + * Maxim SmartTouch Imager Touchscreen Driver + * + * Copyright (c)2013 Maxim Integrated Products, Inc. + * Copyright (C) 2013, NVIDIA Corporation. All Rights Reserved. + * Copyright (c) 2015-2016, The Linux Foundation. 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/moduleparam.h> +#include <linux/kmod.h> +#include <linux/kthread.h> +#include <linux/spi/spi.h> +#include <linux/firmware.h> +#include <linux/crc16.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/regulator/consumer.h> +#include <net/genetlink.h> +#include <net/sock.h> +#include <uapi/linux/maxim_sti.h> +#include <linux/input/mt.h> +#include <asm/byteorder.h> /* MUST include this header to get byte order */ +#ifdef CONFIG_OF +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#endif + +/****************************************************************************\ +* Custom features * +\****************************************************************************/ + +#define VTG_DVDD_MIN_UV 1800000 +#define VTG_DVDD_MAX_UV 1800000 +#define VTG_AVDD_MIN_UV 2850000 +#define VTG_AVDD_MAX_UV 2850000 +#define INPUT_DEVICES 1 +#define CFG_FILE_NAME_MAX 64 + +#define BUF_SIZE 4100 +#define DEF_NL_MC_GROUPS 5 +#define DEF_CHIP_ACC_METHOD 1 +#define TOUCH_FUSION "touch_fusion" + +#if defined(CONFIG_FB) +#include <linux/fb.h> +#endif + +/****************************************************************************\ +* Device context structure, globals, and macros * +\****************************************************************************/ +#define MAXIM_STI_NAME "maxim_sti" + +struct maxim_sti_pdata { + char *touch_fusion; + char *config_file; + char *nl_family; + char *fw_name; + u32 nl_mc_groups; + u32 chip_access_method; + u32 default_reset_state; + u32 tx_buf_size; + u32 rx_buf_size; + int gpio_reset; + int gpio_irq; + int (*init)(struct maxim_sti_pdata *pdata, bool init); + void (*reset)(struct maxim_sti_pdata *pdata, int value); + int (*irq)(struct maxim_sti_pdata *pdata); + bool mt_type_b_enabled; +}; + +struct dev_data; + +struct chip_access_method { + int (*read)(struct dev_data *dd, u16 address, u8 *buf, u16 len); + int (*write)(struct dev_data *dd, u16 address, u8 *buf, u16 len); +}; + +struct dev_data { + u8 *tx_buf; + u8 *rx_buf; + u8 send_fail_count; + u32 nl_seq; + u8 nl_mc_group_count; + bool nl_enabled; + bool start_fusion; + bool suspended; + bool suspend_in_progress; + bool resume_in_progress; + bool expect_resume_ack; + bool eraser_active; + bool legacy_acceleration; + bool input_no_deconfig; + bool irq_registered; + u16 irq_param[MAX_IRQ_PARAMS]; + pid_t fusion_process; + char input_phys[128]; + struct input_dev *input_dev[INPUT_DEVICES]; + struct completion suspend_resume; + struct chip_access_method chip; + struct spi_device *spi; + struct genl_family nl_family; + struct genl_ops *nl_ops; + struct genl_multicast_group *nl_mc_groups; + struct sk_buff *outgoing_skb; + struct sk_buff_head incoming_skb_queue; + struct task_struct *thread; + struct sched_param thread_sched; + struct list_head dev_list; + struct regulator *reg_avdd; + struct regulator *reg_dvdd; + bool supply; + void (*service_irq)(struct dev_data *dd); + struct notifier_block fb_notifier; + struct kobject *parent; + char config_file[CFG_FILE_NAME_MAX]; + u8 sysfs_created; + u16 chip_id; + u16 glove_enabled; + u16 charger_mode_en; + u16 lcd_fps; + u16 tf_ver; + u16 drv_ver; + u16 fw_ver; + u16 fw_protocol; + u32 tf_status; + bool idle; + bool gesture_reset; + bool touch_status[INPUT_DEVICES] + [MAX_INPUT_EVENTS]; + u16 gesture_en; + unsigned long int sysfs_update_type; + struct completion sysfs_ack_glove; + struct completion sysfs_ack_charger; + struct completion sysfs_ack_lcd_fps; + struct mutex sysfs_update_mutex; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspended; + struct pinctrl *ts_pinctrl; +}; + +static unsigned short panel_id; +static struct list_head dev_list; +static spinlock_t dev_lock; + +static irqreturn_t irq_handler(int irq, void *context); +static void service_irq(struct dev_data *dd); +static void service_irq_legacy_acceleration(struct dev_data *dd); +static int send_sysfs_info(struct dev_data *dd); + +#define ERROR(a, b...) dev_err(&dd->spi->dev, "maxim: %s (ERROR:%s:%d): " \ + a "\n", dd->nl_family.name, __func__, \ + __LINE__, ##b) +#define INFO(a, b...) dev_info(&dd->spi->dev, "maxim: %s: " a "\n", \ + dd->nl_family.name, ##b) +#define DBG(a, b...) dev_dbg(&dd->spi->dev, "maxim: %s: " a "\n", \ + dd->nl_family.name, ##b) + +#define DRV_MSG_DBG(...) +#define DRV_PT_DBG(...) + +/****************************************************************************\ +* Chip access methods * +\****************************************************************************/ + +static int +spi_read_123(struct dev_data *dd, u16 address, u8 *buf, u16 len, bool add_len) +{ + struct spi_message message; + struct spi_transfer transfer; + u16 *tx_buf = (u16 *)dd->tx_buf; + u16 *rx_buf = (u16 *)dd->rx_buf; + u16 words = len / sizeof(u16), header_len = 1; + u16 *ptr2 = rx_buf + 1; + int ret; + + if (tx_buf == NULL || rx_buf == NULL) + return -ENOMEM; + + tx_buf[0] = (address << 1) | 0x0001; + + if (add_len) { + tx_buf[1] = words; + ptr2++; + header_len++; + } + + spi_message_init(&message); + memset(&transfer, 0, sizeof(transfer)); + + transfer.len = len + header_len * sizeof(u16); + transfer.tx_buf = tx_buf; + transfer.rx_buf = rx_buf; + spi_message_add_tail(&transfer, &message); + + do { + ret = spi_sync(dd->spi, &message); + } while (ret == -EAGAIN); + + memcpy(buf, ptr2, len); + + return ret; +} + +static int +spi_write_123(struct dev_data *dd, u16 address, u8 *buf, u16 len, + bool add_len) +{ + struct maxim_sti_pdata *pdata = dd->spi->dev.platform_data; + u16 *tx_buf = (u16 *)dd->tx_buf; + u16 words = len / sizeof(u16), header_len = 1; + int ret; + + if (tx_buf == NULL) + return -ENOMEM; + + tx_buf[0] = address << 1; + if (add_len) { + tx_buf[1] = words; + header_len++; + } + memcpy(tx_buf + header_len, buf, len); + + do { + ret = spi_write(dd->spi, tx_buf, + len + header_len * sizeof(u16)); + } while (ret == -EAGAIN); + + memset(dd->tx_buf, 0xFF, pdata->tx_buf_size); + return ret; +} + +/* ======================================================================== */ + +static int +spi_read_1(struct dev_data *dd, u16 address, u8 *buf, u16 len) +{ + return spi_read_123(dd, address, buf, len, true); +} + +static int +spi_write_1(struct dev_data *dd, u16 address, u8 *buf, u16 len) +{ + return spi_write_123(dd, address, buf, len, true); +} + +/* ======================================================================== */ + +static inline int +stop_legacy_acceleration(struct dev_data *dd) +{ + u16 value = 0xDEAD, status, i; + int ret; + + ret = spi_write_123(dd, 0x0003, (u8 *)&value, + sizeof(value), false); + if (ret < 0) + return -1; + usleep_range(100, 120); + + for (i = 0; i < 200; i++) { + ret = spi_read_123(dd, 0x0003, (u8 *)&status, sizeof(status), + false); + if (ret < 0) + return -1; + if (status == 0xABCD) + return 0; + } + + return -2; +} + +static inline int +start_legacy_acceleration(struct dev_data *dd) +{ + u16 value = 0xBEEF; + int ret; + + ret = spi_write_123(dd, 0x0003, (u8 *)&value, sizeof(value), false); + usleep_range(100, 120); + + return ret; +} + +static inline int +spi_rw_2_poll_status(struct dev_data *dd) +{ + u16 status, i; + int ret; + + for (i = 0; i < 200; i++) { + ret = spi_read_123(dd, 0x0000, (u8 *)&status, sizeof(status), + false); + if (ret < 0) + return -1; + if (status == 0xABCD) + return 0; + } + + return -2; +} + +static inline int +spi_read_2_page(struct dev_data *dd, u16 address, u8 *buf, u16 len) +{ + u16 request[] = {0xFEDC, (address << 1) | 0x0001, len / sizeof(u16)}; + int ret; + + /* write read request header */ + ret = spi_write_123(dd, 0x0000, (u8 *)request, sizeof(request), + false); + if (ret < 0) + return -1; + + /* poll status */ + ret = spi_rw_2_poll_status(dd); + if (ret < 0) + return ret; + + /* read data */ + ret = spi_read_123(dd, 0x0004, (u8 *)buf, len, false); + return ret; +} + +static inline int +spi_write_2_page(struct dev_data *dd, u16 address, u8 *buf, u16 len) +{ + u16 page[254]; + int ret; + + page[0] = 0xFEDC; + page[1] = address << 1; + page[2] = len / sizeof(u16); + page[3] = 0x0000; + memcpy(page + 4, buf, len); + + /* write data with write request header */ + ret = spi_write_123(dd, 0x0000, (u8 *)page, len + 4 * sizeof(u16), + false); + if (ret < 0) + return -1; + + /* poll status */ + return spi_rw_2_poll_status(dd); +} + +static inline int +spi_rw_2(struct dev_data *dd, u16 address, u8 *buf, u16 len, + int (*func)(struct dev_data *dd, u16 address, u8 *buf, u16 len)) +{ + u16 rx_len, rx_limit = 250 * sizeof(u16), offset = 0; + int ret; + + while (len > 0) { + rx_len = (len > rx_limit) ? rx_limit : len; + if (dd->legacy_acceleration) + stop_legacy_acceleration(dd); + ret = func(dd, address + (offset / sizeof(u16)), buf + offset, + rx_len); + if (dd->legacy_acceleration) + start_legacy_acceleration(dd); + if (ret < 0) + return ret; + offset += rx_len; + len -= rx_len; + } + + return 0; +} + +static int +spi_read_2(struct dev_data *dd, u16 address, u8 *buf, u16 len) +{ + return spi_rw_2(dd, address, buf, len, spi_read_2_page); +} + +static int +spi_write_2(struct dev_data *dd, u16 address, u8 *buf, u16 len) +{ + return spi_rw_2(dd, address, buf, len, spi_write_2_page); +} + +/* ======================================================================== */ + +static int +spi_read_3(struct dev_data *dd, u16 address, u8 *buf, u16 len) +{ + return spi_read_123(dd, address, buf, len, false); +} + +static int +spi_write_3(struct dev_data *dd, u16 address, u8 *buf, u16 len) +{ + return spi_write_123(dd, address, buf, len, false); +} + +/* ======================================================================== */ + +static struct chip_access_method chip_access_methods[] = { + { + .read = spi_read_1, + .write = spi_write_1, + }, + { + .read = spi_read_2, + .write = spi_write_2, + }, + { + .read = spi_read_3, + .write = spi_write_3, + }, +}; + +static int +set_chip_access_method(struct dev_data *dd, u8 method) +{ + if (method == 0 || method > ARRAY_SIZE(chip_access_methods)) + return -1; + + memcpy(&dd->chip, &chip_access_methods[method - 1], sizeof(dd->chip)); + return 0; +} + +/* ======================================================================== */ + +static inline int +stop_legacy_acceleration_canned(struct dev_data *dd) +{ + u16 value = dd->irq_param[18]; + + return dd->chip.write(dd, dd->irq_param[16], (u8 *)&value, + sizeof(value)); +} + +static inline int +start_legacy_acceleration_canned(struct dev_data *dd) +{ + u16 value = dd->irq_param[17]; + + return dd->chip.write(dd, dd->irq_param[16], (u8 *)&value, + sizeof(value)); +} + +/* ======================================================================== */ + +#define FLASH_BLOCK_SIZE 64 /* flash write buffer in words */ +#define FIRMWARE_SIZE 0xC000 /* fixed 48Kbytes */ + +static int bootloader_wait_ready(struct dev_data *dd) +{ + u16 status, i; + + for (i = 0; i < 15; i++) { + if (spi_read_3(dd, 0x00FF, (u8 *)&status, + sizeof(status)) != 0) + return -1; + if (status == 0xABCC) + return 0; + if (i >= 3) + usleep_range(500, 700); + } + ERROR("unexpected status %04X", status); + return -1; +} + +static int bootloader_complete(struct dev_data *dd) +{ + u16 value = 0x5432; + + return spi_write_3(dd, 0x00FF, (u8 *)&value, sizeof(value)); +} + +static int bootloader_read_data(struct dev_data *dd, u16 *value) +{ + u16 buffer[2]; + + if (spi_read_3(dd, 0x00FE, (u8 *)buffer, sizeof(buffer)) != 0) + return -1; + if (buffer[1] != 0xABCC) + return -1; + + *value = buffer[0]; + return bootloader_complete(dd); +} + +static int bootloader_write_data(struct dev_data *dd, u16 value) +{ + u16 buffer[2] = {value, 0x5432}; + + if (bootloader_wait_ready(dd) != 0) + return -1; + return spi_write_3(dd, 0x00FE, (u8 *)buffer, sizeof(buffer)); +} + +static int bootloader_wait_command(struct dev_data *dd) +{ + u16 value, i; + + for (i = 0; i < 15; i++) { + if (bootloader_read_data(dd, &value) == 0 && value == 0x003E) + return 0; + if (i >= 3) + usleep_range(500, 700); + } + return -1; +} + +static int bootloader_enter(struct dev_data *dd) +{ + int i; + u16 enter[3] = {0x0047, 0x00C7, 0x0007}; + + for (i = 0; i < 3; i++) { + if (spi_write_3(dd, 0x7F00, (u8 *)&enter[i], + sizeof(enter[i])) != 0) + return -1; + } + + if (bootloader_wait_command(dd) != 0) + return -1; + return 0; +} + +static int bootloader_exit(struct dev_data *dd) +{ + u16 value = 0x0000; + + if (bootloader_write_data(dd, 0x0001) != 0) + return -1; + return spi_write_3(dd, 0x7F00, (u8 *)&value, sizeof(value)); +} + +static int bootloader_get_crc(struct dev_data *dd, u16 *crc16, u16 len) +{ + u16 command[] = {0x0030, 0x0002, 0x0000, 0x0000, len & 0xFF, + len >> 8}, value[2], i; + + for (i = 0; i < ARRAY_SIZE(command); i++) + if (bootloader_write_data(dd, command[i]) != 0) + return -1; + msleep(200); /* wait 200ms for it to get done */ + + for (i = 0; i < 2; i++) + if (bootloader_read_data(dd, &value[i]) != 0) + return -1; + + if (bootloader_wait_command(dd) != 0) + return -1; + *crc16 = (value[1] << 8) | value[0]; + return 0; +} + +static int bootloader_set_byte_mode(struct dev_data *dd) +{ + u16 command[2] = {0x000A, 0x0000}, i; + + for (i = 0; i < ARRAY_SIZE(command); i++) + if (bootloader_write_data(dd, command[i]) != 0) + return -1; + if (bootloader_wait_command(dd) != 0) + return -1; + return 0; +} + +static int bootloader_erase_flash(struct dev_data *dd) +{ + if (bootloader_write_data(dd, 0x0002) != 0) + return -1; + msleep(60); /* wait 60ms */ + if (bootloader_wait_command(dd) != 0) + return -1; + return 0; +} + +static int bootloader_write_flash(struct dev_data *dd, u16 *image, u16 len) +{ + u16 command[] = {0x00F0, 0x0000, len >> 8, 0x0000, 0x0000}; + u16 i, buffer[FLASH_BLOCK_SIZE]; + + for (i = 0; i < ARRAY_SIZE(command); i++) + if (bootloader_write_data(dd, command[i]) != 0) + return -1; + + for (i = 0; i < ((len / sizeof(u16)) / FLASH_BLOCK_SIZE); i++) { + if (bootloader_wait_ready(dd) != 0) + return -1; + memcpy(buffer, (void *)(image + i * FLASH_BLOCK_SIZE), + sizeof(buffer)); + if (spi_write_3(dd, ((i % 2) == 0) ? 0x0000 : 0x0040, + (u8 *)buffer, sizeof(buffer)) != 0) + return -1; + if (bootloader_complete(dd) != 0) + return -1; + } + + usleep_range(10000, 11000); + if (bootloader_wait_command(dd) != 0) + return -1; + return 0; +} + +static int device_fw_load(struct dev_data *dd, const struct firmware *fw) +{ + u16 fw_crc16, chip_crc16; + + fw_crc16 = crc16(0, fw->data, fw->size); + INFO("firmware size (%d) CRC16(0x%04X)", (int)fw->size, fw_crc16); + if (bootloader_enter(dd) != 0) { + ERROR("failed to enter bootloader"); + return -1; + } + if (bootloader_get_crc(dd, &chip_crc16, fw->size) != 0) { + ERROR("failed to get CRC16 from the chip"); + return -1; + } + INFO("chip CRC16(0x%04X)", chip_crc16); + if (fw_crc16 != chip_crc16) { + INFO("will reprogram chip"); + if (bootloader_erase_flash(dd) != 0) { + ERROR("failed to erase chip flash"); + return -1; + } + INFO("flash erase OK"); + if (bootloader_set_byte_mode(dd) != 0) { + ERROR("failed to set byte mode"); + return -1; + } + INFO("byte mode OK"); + if (bootloader_write_flash(dd, (u16 *)fw->data, + fw->size) != 0) { + ERROR("failed to write flash"); + return -1; + } + INFO("flash write OK"); + if (bootloader_get_crc(dd, &chip_crc16, fw->size) != 0) { + ERROR("failed to get CRC16 from the chip"); + return -1; + } + if (fw_crc16 != chip_crc16) { + ERROR("failed to verify programming! (0x%04X)", + chip_crc16); + return -1; + } + INFO("chip programmed successfully, new chip CRC16(0x%04X)", + chip_crc16); + } + if (bootloader_exit(dd) != 0) { + ERROR("failed to exit bootloader"); + return -1; + } + return 0; +} + +static int fw_request_load(struct dev_data *dd) +{ + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, "maxim_fp35.bin", &dd->spi->dev); + if (ret || fw == NULL) { + ERROR("firmware request failed (%d,%p)", ret, fw); + return -1; + } + if (fw->size != FIRMWARE_SIZE) { + release_firmware(fw); + ERROR("incoming firmware is of wrong size (%04X)", + (unsigned int)fw->size); + return -1; + } + ret = device_fw_load(dd, fw); + if (ret != 0 && bootloader_exit(dd) != 0) + ERROR("failed to exit bootloader"); + release_firmware(fw); + return ret; +} + +/* ======================================================================== */ + +static void stop_idle_scan(struct dev_data *dd) +{ + u16 value; + + value = dd->irq_param[13]; + (void)dd->chip.write(dd, dd->irq_param[19], + (u8 *)&value, sizeof(value)); +} + +static void stop_scan_canned(struct dev_data *dd) +{ + u16 value; + u16 i, clear[2] = { 0 }; + + if (dd->legacy_acceleration) + (void)stop_legacy_acceleration_canned(dd); + value = dd->irq_param[13]; + (void)dd->chip.write(dd, dd->irq_param[12], (u8 *)&value, + sizeof(value)); + usleep_range(dd->irq_param[15], dd->irq_param[15] + 1000); + (void)dd->chip.read(dd, dd->irq_param[0], (u8 *)&clear[0], + sizeof(clear[0])); + (void)dd->chip.write(dd, dd->irq_param[0], (u8 *)&clear, + sizeof(clear)); + + for (i = 28; i < 32; i++) { + value = dd->irq_param[i]; + (void)dd->chip.write(dd, dd->irq_param[27], + (u8 *)&value, sizeof(value)); + } + value = dd->irq_param[33]; + (void)dd->chip.write(dd, dd->irq_param[32], + (u8 *)&value, sizeof(value)); + usleep_range(500, 1000); + for (i = 28; i < 32; i++) { + value = dd->irq_param[i]; + (void)dd->chip.write(dd, dd->irq_param[27], + (u8 *)&value, sizeof(value)); + } + value = dd->irq_param[13]; + (void)dd->chip.write(dd, dd->irq_param[32], + (u8 *)&value, sizeof(value)); +} + +static void start_scan_canned(struct dev_data *dd) +{ + u16 value; + + if (dd->legacy_acceleration) { + (void)start_legacy_acceleration_canned(dd); + } else { + value = dd->irq_param[14]; + (void)dd->chip.write(dd, dd->irq_param[12], (u8 *)&value, + sizeof(value)); + } +} + +static void enable_gesture(struct dev_data *dd) +{ + u16 value; + + if (!dd->idle) + stop_scan_canned(dd); + + value = dd->irq_param[13]; + (void)dd->chip.write(dd, dd->irq_param[34], + (u8 *)&value, sizeof(value)); + value = dd->irq_param[36]; + (void)dd->chip.write(dd, dd->irq_param[35], + (u8 *)&value, sizeof(value)); + + value = dd->irq_param[18]; + (void)dd->chip.write(dd, dd->irq_param[16], (u8 *)&value, + sizeof(value)); + + value = dd->irq_param[20]; + (void)dd->chip.write(dd, dd->irq_param[19], (u8 *)&value, + sizeof(value)); + if (!dd->idle) { + value = dd->irq_param[26]; + (void)dd->chip.write(dd, dd->irq_param[25], (u8 *)&value, + sizeof(value)); + } +} + +static void gesture_wake_detect(struct dev_data *dd) +{ + u16 status, value; + int ret = 0; + + ret = dd->chip.read(dd, dd->irq_param[0], + (u8 *)&status, sizeof(status)); + INFO("%s: %d = %04X", __func__, dd->irq_param[0], status); + if (status & dd->irq_param[10]) + dd->gesture_reset = true; + if (status & dd->irq_param[18] && dd->input_dev[INPUT_DEVICES - 1]) { + ret = dd->chip.read(dd, dd->irq_param[22], + (u8 *)&value, sizeof(value)); + INFO("gesture code = 0x%04X", value); + if (value == dd->irq_param[24]) { + INFO("gesture detected"); + input_report_key(dd->input_dev[INPUT_DEVICES - 1], KEY_POWER, 1); + input_sync(dd->input_dev[INPUT_DEVICES - 1]); + input_report_key(dd->input_dev[INPUT_DEVICES - 1], KEY_POWER, 0); + input_sync(dd->input_dev[INPUT_DEVICES - 1]); + } + value = dd->irq_param[18]; + ret = dd->chip.write(dd, dd->irq_param[0], + (u8 *)&value, sizeof(value)); + } else { + ret = dd->chip.write(dd, dd->irq_param[0], + (u8 *)&status, sizeof(status)); + } +} + +static void finish_gesture(struct dev_data *dd) +{ + struct maxim_sti_pdata *pdata = dd->spi->dev.platform_data; + u16 value; + u8 fail_count = 0; + int ret; + + value = dd->irq_param[21]; + (void)dd->chip.write(dd, dd->irq_param[19], (u8 *)&value, + sizeof(value)); + do { + msleep(20); + ret = dd->chip.read(dd, dd->irq_param[25], (u8 *)&value, + sizeof(value)); + if (ret < 0 || fail_count >= 20) { + ERROR("failed to read control register (%d)", ret); + pdata->reset(pdata, 0); + msleep(20); + pdata->reset(pdata, 1); + return; + } + INFO("ctrl = 0x%04X", value); + fail_count++; + } while (value); +} + +static int regulator_init(struct dev_data *dd) +{ + int ret; + + dd->reg_avdd = devm_regulator_get(&dd->spi->dev, "avdd"); + if (IS_ERR(dd->reg_avdd)) { + ret = PTR_ERR(dd->reg_avdd); + dd->reg_avdd = NULL; + dev_err(&dd->spi->dev, "can't find avdd regulator: %d\n", + ret); + return ret; + } + + ret = regulator_set_voltage(dd->reg_avdd, + VTG_AVDD_MIN_UV, VTG_AVDD_MAX_UV); + if (ret) { + dev_err(&dd->spi->dev, + "can't set avdd regulator voltage: %d\n", ret); + goto err_free_avdd; + } + + dd->reg_dvdd = devm_regulator_get(&dd->spi->dev, "dvdd"); + if (IS_ERR(dd->reg_dvdd)) { + ret = PTR_ERR(dd->reg_avdd); + dd->reg_dvdd = NULL; + dev_err(&dd->spi->dev, "can't find avdd regulator: %d\n", + ret); + goto err_free_avdd; + } + + ret = regulator_set_voltage(dd->reg_dvdd, + VTG_DVDD_MIN_UV, VTG_DVDD_MAX_UV); + if (ret) { + dev_err(&dd->spi->dev, + "can't set dvdd regulator voltage: %d\n", ret); + goto err_free_dvdd; + } + return 0; + +err_free_dvdd: + devm_regulator_put(dd->reg_dvdd); + dd->reg_dvdd = NULL; +err_free_avdd: + devm_regulator_put(dd->reg_avdd); + dd->reg_avdd = NULL; + return ret; +} + +static void regulator_uninit(struct dev_data *dd) +{ + if (dd->reg_dvdd) + devm_regulator_put(dd->reg_dvdd); + if (dd->reg_avdd) + devm_regulator_put(dd->reg_avdd); +} + +static int regulator_control(struct dev_data *dd, bool on) +{ + int ret; + + if (!dd->reg_avdd || !dd->reg_dvdd) + return 0; + + if (on && !dd->supply) { + DBG("regulator ON."); + ret = regulator_enable(dd->reg_dvdd); + if (ret < 0) { + ERROR("failed to enable dvdd regulator: %d", ret); + return ret; + } + usleep_range(1000, 1020); + + ret = regulator_enable(dd->reg_avdd); + if (ret < 0) { + ERROR("failed to enable avdd regulator: %d", ret); + regulator_disable(dd->reg_dvdd); + return ret; + } + + dd->supply = true; + } else if (!on && dd->supply) { + DBG("regulator OFF."); + + ret = regulator_disable(dd->reg_avdd); + if (ret < 0) + ERROR("Failed to disable regulator avdd: %d", ret); + + ret = regulator_disable(dd->reg_dvdd); + if (ret < 0) + ERROR("Failed to disable regulator dvdd: %d", ret); + + dd->supply = false; + } + + return 0; +} + +#define MAXIM_STI_GPIO_ERROR(ret, gpio, op) \ + if (ret < 0) { \ + pr_err("%s: GPIO %d %s failed (%d)\n", __func__, gpio, op, \ + ret); \ + return ret; \ + } + + +int maxim_sti_gpio_init(struct maxim_sti_pdata *pdata, bool init) +{ + int ret; + + if (init) { + ret = gpio_request(pdata->gpio_irq, "maxim_sti_irq"); + MAXIM_STI_GPIO_ERROR(ret, pdata->gpio_irq, "request"); + ret = gpio_direction_input(pdata->gpio_irq); + MAXIM_STI_GPIO_ERROR(ret, pdata->gpio_irq, "direction"); + ret = gpio_request(pdata->gpio_reset, "maxim_sti_reset"); + MAXIM_STI_GPIO_ERROR(ret, pdata->gpio_reset, "request"); + ret = gpio_direction_output(pdata->gpio_reset, + pdata->default_reset_state); + MAXIM_STI_GPIO_ERROR(ret, pdata->gpio_reset, "direction"); + } else { + gpio_free(pdata->gpio_irq); + gpio_free(pdata->gpio_reset); + } + + return 0; +} + +void maxim_sti_gpio_reset(struct maxim_sti_pdata *pdata, int value) +{ + gpio_set_value(pdata->gpio_reset, !!value); +} + +int maxim_sti_gpio_irq(struct maxim_sti_pdata *pdata) +{ + return gpio_get_value(pdata->gpio_irq); +} + +/****************************************************************************\ +* Device Tree Support +\****************************************************************************/ + +#ifdef CONFIG_OF +static int maxim_parse_dt(struct device *dev, struct maxim_sti_pdata *pdata) +{ + struct device_node *np = dev->of_node; + u32 flags; + const char *str; + int ret; + + pdata->gpio_reset = of_get_named_gpio_flags(np, + "maxim_sti,reset-gpio", 0, &flags); + pdata->default_reset_state = (u8)flags; + + pdata->gpio_irq = of_get_named_gpio_flags(np, + "maxim_sti,irq-gpio", 0, &flags); + + pdata->mt_type_b_enabled = + of_property_read_bool(np, "maxim_sti,mt_type_b_enabled"); + + ret = of_property_read_string(np, "maxim_sti,touch_fusion", &str); + if (ret) { + dev_err(dev, "%s: unable to read touch_fusion location (%d)\n", + __func__, ret); + goto fail; + } + pdata->touch_fusion = (char *)str; + + ret = of_property_read_string(np, "maxim_sti,config_file", &str); + if (ret) { + dev_err(dev, "%s: unable to read config_file location (%d)\n", + __func__, ret); + goto fail; + } + pdata->config_file = (char *)str; + + ret = of_property_read_string(np, "maxim_sti,fw_name", &str); + if (ret) { + dev_err(dev, "%s: unable to read fw_name (%d)\n", + __func__, ret); + } + pdata->fw_name = (char *)str; + + return 0; + +fail: + return ret; +} +#endif + +/****************************************************************************\ +* Suspend/resume processing * +\****************************************************************************/ + +#ifdef CONFIG_PM_SLEEP +static int suspend(struct device *dev) +{ + struct dev_data *dd = spi_get_drvdata(to_spi_device(dev)); + + DBG("%s: suspending", __func__); + + if (dd->suspended) { + DBG("%s: already suspended", __func__); + return 0; + } + + if (dd->suspend_in_progress) { + dev_err(dev, "suspend already in progress!"); + return -EINVAL; + } + + dd->suspend_in_progress = true; + wake_up_process(dd->thread); + wait_for_completion(&dd->suspend_resume); + + DBG("%s: suspended", __func__); + + return 0; +} + +static int resume(struct device *dev) +{ + struct dev_data *dd = spi_get_drvdata(to_spi_device(dev)); + + DBG("%s: resuming", __func__); + + if (!dd->suspended && !dd->suspend_in_progress) { + DBG("%s: not suspended", __func__); + return 0; + } + + dd->resume_in_progress = true; + wake_up_process(dd->thread); + wait_for_completion(&dd->suspend_resume); + DBG("%s: resumed", __func__); + return 0; +} + +static const struct dev_pm_ops pm_ops = { + .suspend = suspend, + .resume = resume, +}; + +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct dev_data *dd = container_of(self, + struct dev_data, fb_notifier); + + DBG("%s: event = %lu", __func__, event); + if (evdata && evdata->data && event == FB_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) + resume(&dd->spi->dev); + else if (*blank == FB_BLANK_POWERDOWN) + suspend(&dd->spi->dev); + } + return 0; +} +#endif +#endif + +/****************************************************************************\ +* Netlink processing * +\****************************************************************************/ + +static inline int +nl_msg_new(struct dev_data *dd, u8 dst) +{ + dd->outgoing_skb = alloc_skb(NL_BUF_SIZE, GFP_KERNEL); + if (dd->outgoing_skb == NULL) + return -ENOMEM; + nl_msg_init(dd->outgoing_skb->data, dd->nl_family.id, dd->nl_seq++, + dst); + if (dd->nl_seq == 0) + dd->nl_seq++; + return 0; +} + +static int +nl_callback_noop(struct sk_buff *skb, struct genl_info *info) +{ + return 0; +} + +static void +release_slot_events(struct dev_data *dd, bool *active_touch, int inp) +{ + int i; + + for (i = 0; i < MAX_INPUT_EVENTS; i++) { + if (dd->touch_status[inp][i] == true && + active_touch[i] == false) { + input_mt_slot(dd->input_dev[inp], i); + input_mt_report_slot_state(dd->input_dev[inp], + MT_TOOL_FINGER, 0); + } + dd->touch_status[inp][i] = active_touch[i]; + } +} + +static inline bool +nl_process_driver_msg(struct dev_data *dd, u16 msg_id, void *msg) +{ + struct maxim_sti_pdata *pdata = dd->spi->dev.platform_data; + struct dr_echo_request *echo_msg; + struct fu_echo_response *echo_response; + struct dr_chip_read *read_msg; + struct fu_chip_read_result *read_result; + struct dr_chip_write *write_msg; + struct dr_chip_access_method *chip_access_method_msg; + struct dr_delay *delay_msg; + struct fu_irqline_status *irqline_status; + struct dr_config_irq *config_irq_msg; + struct dr_config_input *config_input_msg; + struct dr_config_watchdog *config_watchdog_msg; + struct dr_input *input_msg; + struct dr_legacy_acceleration *legacy_acceleration_msg; + struct dr_handshake *handshake_msg; + struct fu_handshake_response *handshake_response; + struct dr_config_fw *config_fw_msg; + struct dr_sysfs_ack *sysfs_ack_msg; + struct dr_idle *idle_msg; + struct dr_tf_status *tf_status_msg; + u8 i, inp; + int ret, id; + u16 read_value[2] = { 0 }; + bool active_touch[INPUT_DEVICES][MAX_INPUT_EVENTS] = { {false} }; + + if (dd->expect_resume_ack && msg_id != DR_DECONFIG && + msg_id != DR_RESUME_ACK) + return false; + + switch (msg_id) { + case DR_ADD_MC_GROUP: + INFO("warning: deprecated dynamic mc group add request!"); + return false; + case DR_ECHO_REQUEST: + DRV_MSG_DBG("msg: %s", "DR_ECHO_REQUEST"); + echo_msg = msg; + echo_response = nl_alloc_attr(dd->outgoing_skb->data, + FU_ECHO_RESPONSE, + sizeof(*echo_response)); + if (echo_response == NULL) + goto alloc_attr_failure; + echo_response->cookie = echo_msg->cookie; + return true; + case DR_CHIP_READ: + DRV_MSG_DBG("msg: %s", "DR_CHIP_READ"); + read_msg = msg; + read_result = nl_alloc_attr(dd->outgoing_skb->data, + FU_CHIP_READ_RESULT, + sizeof(*read_result) + read_msg->length); + if (read_result == NULL) + goto alloc_attr_failure; + read_result->address = read_msg->address; + read_result->length = read_msg->length; + ret = dd->chip.read(dd, read_msg->address, read_result->data, + read_msg->length); + if (ret < 0) + ERROR("failed to read from chip (%d, %d, %d)", + read_msg->address, read_msg->length, ret); + return true; + case DR_CHIP_WRITE: + DRV_MSG_DBG("msg: %s", "DR_CHIP_WRITE"); + write_msg = msg; + if (write_msg->address == dd->irq_param[12] && + write_msg->data[0] == dd->irq_param[13]) { + ret = dd->chip.write(dd, write_msg->address, + write_msg->data, + write_msg->length); + if (ret < 0) + ERROR("failed to write chip (%d)", ret); + msleep(15); + ret = dd->chip.read(dd, dd->irq_param[0], + (u8 *)read_value, + sizeof(read_value)); + if (ret < 0) + ERROR("failed to read from chip (%d)", ret); + ret = dd->chip.write(dd, dd->irq_param[0], + (u8 *)read_value, + sizeof(read_value)); + return false; + } + if (write_msg->address == dd->irq_param[0]) { + if (((u16 *)write_msg->data)[0] == dd->irq_param[11]) { + ret = dd->chip.read(dd, dd->irq_param[0], + (u8 *)read_value, + sizeof(read_value)); + if (ret < 0) + ERROR("failed to read from chip (%d)", ret); + ret = dd->chip.write(dd, dd->irq_param[0], + (u8 *)read_value, + sizeof(read_value)); + return false; + } + read_value[0] = ((u16 *)write_msg->data)[0]; + ret = dd->chip.write(dd, write_msg->address, + (u8 *)read_value, + sizeof(read_value)); + return false; + } + ret = dd->chip.write(dd, write_msg->address, write_msg->data, + write_msg->length); + if (ret < 0) + ERROR("failed to write chip (%d, %d, %d)", + write_msg->address, write_msg->length, ret); + return false; + case DR_CHIP_RESET: + DRV_MSG_DBG("msg: %s", "DR_CHIP_RESET"); + pdata->reset(pdata, ((struct dr_chip_reset *)msg)->state); + return false; + case DR_GET_IRQLINE: + DRV_MSG_DBG("msg: %s", "DR_GET_IRQLINE"); + irqline_status = nl_alloc_attr(dd->outgoing_skb->data, + FU_IRQLINE_STATUS, + sizeof(*irqline_status)); + if (irqline_status == NULL) + goto alloc_attr_failure; + irqline_status->status = pdata->irq(pdata); + return true; + case DR_DELAY: + DRV_MSG_DBG("msg: %s", "DR_DELAY"); + delay_msg = msg; + if (delay_msg->period > 1000) + msleep(delay_msg->period / 1000); + usleep_range(delay_msg->period % 1000, + (delay_msg->period % 1000) + 10); + return false; + case DR_CHIP_ACCESS_METHOD: + DRV_MSG_DBG("msg: %s", "DR_CHIP_ACCESS_METHOD"); + chip_access_method_msg = msg; + ret = set_chip_access_method(dd, + chip_access_method_msg->method); + if (ret < 0) + ERROR("failed to set chip access method (%d) (%d)", + ret, chip_access_method_msg->method); + return false; + case DR_CONFIG_IRQ: + DRV_MSG_DBG("msg: %s", "DR_CONFIG_IRQ"); + config_irq_msg = msg; + if (config_irq_msg->irq_params > MAX_IRQ_PARAMS) { + ERROR("too many IRQ parameters"); + return false; + } + memcpy(dd->irq_param, config_irq_msg->irq_param, + config_irq_msg->irq_params * sizeof(dd->irq_param[0])); + if (dd->irq_registered) + return false; + dd->service_irq = service_irq; + ret = request_irq(dd->spi->irq, irq_handler, + (config_irq_msg->irq_edge == DR_IRQ_RISING_EDGE) ? + IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING, + pdata->nl_family, dd); + if (ret < 0) { + ERROR("failed to request IRQ (%d)", ret); + } else { + dd->irq_registered = true; + wake_up_process(dd->thread); + } + return false; + case DR_CONFIG_INPUT: + DRV_MSG_DBG("msg: %s", "DR_CONFIG_INPUT"); + config_input_msg = msg; + for (i = 0; i < INPUT_DEVICES; i++) + if (dd->input_dev[i] != NULL) + return false; + for (i = 0; i < INPUT_DEVICES; i++) { + dd->input_dev[i] = input_allocate_device(); + if (dd->input_dev[i] == NULL) { + ERROR("failed to allocate input device"); + continue; + } + snprintf(dd->input_phys, sizeof(dd->input_phys), + "%s/input%d", dev_name(&dd->spi->dev), i); + dd->input_dev[i]->name = pdata->nl_family; + dd->input_dev[i]->phys = dd->input_phys; + dd->input_dev[i]->id.bustype = BUS_SPI; + __set_bit(EV_SYN, dd->input_dev[i]->evbit); + __set_bit(EV_ABS, dd->input_dev[i]->evbit); + __set_bit(INPUT_PROP_DIRECT, dd->input_dev[i]->propbit); + if (i == (INPUT_DEVICES - 1)) { + __set_bit(EV_KEY, dd->input_dev[i]->evbit); + __set_bit(BTN_TOOL_RUBBER, + dd->input_dev[i]->keybit); + __set_bit(KEY_POWER, + dd->input_dev[i]->keybit); + } + if (pdata->mt_type_b_enabled && + input_mt_init_slots(dd->input_dev[i], + MAX_INPUT_EVENTS, 0)) { + ERROR("Error in initialising slots\n"); + input_free_device(dd->input_dev[i]); + dd->input_dev[i] = NULL; + continue; + } else { + input_set_abs_params(dd->input_dev[i], + ABS_MT_TRACKING_ID, 0, + MAX_INPUT_EVENTS, 0, 0); + } + + input_set_abs_params(dd->input_dev[i], + ABS_MT_POSITION_X, 0, + config_input_msg->x_range, 0, 0); + input_set_abs_params(dd->input_dev[i], + ABS_MT_POSITION_Y, 0, + config_input_msg->y_range, 0, 0); + input_set_abs_params(dd->input_dev[i], + ABS_MT_PRESSURE, 0, 0xFF, 0, 0); + + if (i == (INPUT_DEVICES - 1)) + input_set_abs_params(dd->input_dev[i], + ABS_MT_TOOL_TYPE, 0, + MT_TOOL_MAX, 0, 0); + else + input_set_abs_params(dd->input_dev[i], + ABS_MT_TOOL_TYPE, 0, + MT_TOOL_FINGER, 0, 0); + + ret = input_register_device(dd->input_dev[i]); + if (ret < 0) { + input_free_device(dd->input_dev[i]); + dd->input_dev[i] = NULL; + ERROR("failed to register input device"); + } + } +#if defined(CONFIG_FB) + dd->fb_notifier.notifier_call = fb_notifier_callback; + fb_register_client(&dd->fb_notifier); +#endif + /* create symlink */ + if (dd->parent == NULL && dd->input_dev[0] != NULL) { + dd->parent = dd->input_dev[0]->dev.kobj.parent; + ret = sysfs_create_link(dd->parent, &dd->spi->dev.kobj, + MAXIM_STI_NAME); + if (ret) { + ERROR("sysfs_create_link error\n"); + dd->parent = NULL; + } + } + return false; + case DR_CONFIG_WATCHDOG: + DRV_MSG_DBG("msg: %s", "DR_CONFIG_WATCHDOG"); + config_watchdog_msg = msg; + dd->fusion_process = (pid_t)config_watchdog_msg->pid; + return false; + case DR_DECONFIG: + DRV_MSG_DBG("msg: %s", "DR_DECONFIG"); + if (dd->irq_registered) { + free_irq(dd->spi->irq, dd); + dd->irq_registered = false; + } + stop_scan_canned(dd); + + if (!dd->input_no_deconfig) { + if (dd->parent != NULL) { + sysfs_remove_link( + dd->input_dev[0]->dev.kobj.parent, + MAXIM_STI_NAME); + dd->parent = NULL; + } + } + + if (!dd->input_no_deconfig) { + for (i = 0; i < INPUT_DEVICES; i++) { + if (dd->input_dev[i] == NULL) + continue; + input_unregister_device(dd->input_dev[i]); + dd->input_dev[i] = NULL; + } +#if defined(CONFIG_FB) + fb_unregister_client(&dd->fb_notifier); +#endif + } + dd->expect_resume_ack = false; + dd->eraser_active = false; + dd->legacy_acceleration = false; + dd->service_irq = service_irq; + dd->fusion_process = (pid_t)0; + dd->sysfs_update_type = DR_SYSFS_UPDATE_NONE; + return false; + case DR_INPUT: + DRV_MSG_DBG("msg: %s", "DR_INPUT"); + input_msg = msg; + if (input_msg->events == 0) { + if (dd->eraser_active) { + input_report_key( + dd->input_dev[INPUT_DEVICES - 1], + BTN_TOOL_RUBBER, 0); + dd->eraser_active = false; + } + for (i = 0; i < INPUT_DEVICES; i++) { + if (pdata->mt_type_b_enabled) + release_slot_events(dd, + active_touch[i], i); + else + input_mt_sync(dd->input_dev[i]); + + input_sync(dd->input_dev[i]); + } + } else { + for (i = 0; i < input_msg->events; i++) { + switch (input_msg->event[i].tool_type) { + case DR_INPUT_FINGER: + inp = 0; + id = input_msg->event[i].id; + if (pdata->mt_type_b_enabled) { + input_mt_slot( + dd->input_dev[inp], id); + input_mt_report_slot_state( + dd->input_dev[inp], + MT_TOOL_FINGER, 1); + active_touch[inp][id] = true; + } else { + input_report_abs( + dd->input_dev[inp], + ABS_MT_TOOL_TYPE, + MT_TOOL_FINGER); + } + break; + case DR_INPUT_STYLUS: + inp = INPUT_DEVICES - 1; + if (pdata->mt_type_b_enabled) { + input_mt_slot( + dd->input_dev[inp], id); + input_mt_report_slot_state( + dd->input_dev[inp], + MT_TOOL_PEN, 1); + active_touch[inp][id] = true; + } else { + input_report_abs( + dd->input_dev[inp], + ABS_MT_TOOL_TYPE, + MT_TOOL_PEN); + } + break; + case DR_INPUT_ERASER: + inp = INPUT_DEVICES - 1; + input_report_key(dd->input_dev[inp], + BTN_TOOL_RUBBER, 1); + dd->eraser_active = true; + break; + default: + inp = 0; + ERROR("invalid input tool type (%d)", + input_msg->event[i].tool_type); + break; + } + if (!pdata->mt_type_b_enabled) + input_report_abs(dd->input_dev[inp], + ABS_MT_TRACKING_ID, id); + input_report_abs(dd->input_dev[inp], + ABS_MT_POSITION_X, + input_msg->event[i].x); + input_report_abs(dd->input_dev[inp], + ABS_MT_POSITION_Y, + input_msg->event[i].y); + input_report_abs(dd->input_dev[inp], + ABS_MT_PRESSURE, + input_msg->event[i].z); + if (!pdata->mt_type_b_enabled) + input_mt_sync(dd->input_dev[inp]); + } + + if (pdata->mt_type_b_enabled) { + for (i = 0; i < INPUT_DEVICES; i++) + release_slot_events(dd, + active_touch[i], i); + } + + for (i = 0; i < INPUT_DEVICES; i++) + input_sync(dd->input_dev[i]); + } + return false; + case DR_RESUME_ACK: + DRV_MSG_DBG("msg: %s", "DR_RESUME_ACK"); + dd->expect_resume_ack = false; + if (dd->irq_registered + && !dd->gesture_en + ) + enable_irq(dd->spi->irq); + return false; + case DR_LEGACY_FWDL: + DRV_MSG_DBG("msg: %s", "DR_LEGACY_FWDL"); + ret = fw_request_load(dd); + if (ret < 0) + ERROR("firmware download failed (%d)", ret); + else + INFO("firmware download OK"); + return false; + case DR_LEGACY_ACCELERATION: + DRV_MSG_DBG("msg: %s", "DR_LEGACY_ACCELERATION"); + legacy_acceleration_msg = msg; + if (legacy_acceleration_msg->enable) { + dd->service_irq = service_irq_legacy_acceleration; + start_legacy_acceleration(dd); + dd->legacy_acceleration = true; + } else { + stop_legacy_acceleration(dd); + dd->legacy_acceleration = false; + dd->service_irq = service_irq; + } + return false; + case DR_HANDSHAKE: + DRV_MSG_DBG("msg: %s", "DR_HANDSHAKE"); + handshake_msg = msg; + dd->tf_ver = handshake_msg->tf_ver; + dd->chip_id = handshake_msg->chip_id; + dd->drv_ver = DRIVER_VERSION_NUM; + handshake_response = nl_alloc_attr(dd->outgoing_skb->data, + FU_HANDSHAKE_RESPONSE, + sizeof(*handshake_response)); + if (handshake_response == NULL) + goto alloc_attr_failure; + handshake_response->driver_ver = dd->drv_ver; + handshake_response->panel_id = panel_id; + handshake_response->driver_protocol = DRIVER_PROTOCOL; + return true; + case DR_CONFIG_FW: + DRV_MSG_DBG("msg: %s", "DR_CONFIG_FW"); + config_fw_msg = msg; + dd->fw_ver = config_fw_msg->fw_ver; + dd->fw_protocol = config_fw_msg->fw_protocol; + return false; + case DR_SYSFS_ACK: + sysfs_ack_msg = msg; + if (sysfs_ack_msg->type == DR_SYSFS_ACK_GLOVE) + complete(&dd->sysfs_ack_glove); + if (sysfs_ack_msg->type == DR_SYSFS_ACK_CHARGER) + complete(&dd->sysfs_ack_charger); + if (sysfs_ack_msg->type == DR_SYSFS_ACK_LCD_FPS) + complete(&dd->sysfs_ack_lcd_fps); + return false; + case DR_IDLE: + DRV_MSG_DBG("msg: %s", "DR_IDLE"); + idle_msg = msg; + dd->idle = !!(idle_msg->idle); + return false; + case DR_TF_STATUS: + tf_status_msg = msg; + dd->tf_status = tf_status_msg->tf_status; + return false; + default: + ERROR("unexpected message %d", msg_id); + return false; + } + +alloc_attr_failure: + ERROR("failed to allocate response for msg_id %d", msg_id); + return false; +} + +static int nl_process_msg(struct dev_data *dd, struct sk_buff *skb) +{ + struct nlattr *attr; + bool send_reply = false; + int ret = 0, ret2; + + /* process incoming message */ + attr = NL_ATTR_FIRST(skb->data); + for (; attr < NL_ATTR_LAST(skb->data); attr = NL_ATTR_NEXT(attr)) { + if (nl_process_driver_msg(dd, attr->nla_type, + NL_ATTR_VAL(attr, void))) + send_reply = true; + } + + /* send back reply if requested */ + if (send_reply) { + (void)skb_put(dd->outgoing_skb, + NL_SIZE(dd->outgoing_skb->data)); + if (NL_SEQ(skb->data) == 0) + ret = genlmsg_unicast(&init_net, + dd->outgoing_skb, + NETLINK_CB(skb).portid); + else + ret = genlmsg_multicast(&dd->nl_family, + dd->outgoing_skb, 0, + MC_FUSION, GFP_KERNEL); + if (ret < 0) + ERROR("could not reply to fusion (%d)", ret); + + /* allocate new outgoing skb */ + ret2 = nl_msg_new(dd, MC_FUSION); + if (ret2 < 0) + ERROR("could not allocate outgoing skb (%d)", ret2); + } + + /* free incoming message */ + kfree_skb(skb); + return ret; +} + +static int +nl_callback_driver(struct sk_buff *skb, struct genl_info *info) +{ + struct dev_data *dd; + struct sk_buff *skb2; + unsigned long flags; + + /* locate device structure */ + spin_lock_irqsave(&dev_lock, flags); + list_for_each_entry(dd, &dev_list, dev_list) + if (dd->nl_family.id == NL_TYPE(skb->data)) + break; + spin_unlock_irqrestore(&dev_lock, flags); + if (&dd->dev_list == &dev_list) + return -ENODEV; + if (!dd->nl_enabled) + return -EAGAIN; + + /* queue incoming skb and wake up processing thread */ + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2 == NULL) { + ERROR("failed to clone incoming skb"); + return -ENOMEM; + } else { + skb_queue_tail(&dd->incoming_skb_queue, skb2); + wake_up_process(dd->thread); + return 0; + } +} + +static int +nl_callback_fusion(struct sk_buff *skb, struct genl_info *info) +{ + struct dev_data *dd; + unsigned long flags; + + /* locate device structure */ + spin_lock_irqsave(&dev_lock, flags); + list_for_each_entry(dd, &dev_list, dev_list) + if (dd->nl_family.id == NL_TYPE(skb->data)) + break; + spin_unlock_irqrestore(&dev_lock, flags); + if (&dd->dev_list == &dev_list) + return -ENODEV; + if (!dd->nl_enabled) + return -EAGAIN; + + (void)genlmsg_multicast(&dd->nl_family, + skb_clone(skb, GFP_ATOMIC), 0, + MC_FUSION, GFP_ATOMIC); + return 0; +} + +/****************************************************************************\ +* Interrupt processing * +\****************************************************************************/ + +static unsigned long interrupt_count; +static irqreturn_t irq_handler(int irq, void *context) +{ + struct dev_data *dd = context; + + DBG("%s: interrupt #%lu", __func__, interrupt_count++); + + wake_up_process(dd->thread); + return IRQ_HANDLED; +} + +static void service_irq_legacy_acceleration(struct dev_data *dd) +{ + struct fu_async_data *async_data; + u16 len, rx_len = 0, offset = 0; + u16 buf[255], rx_limit = 250 * sizeof(u16); + int ret = 0, counter = 0; + + async_data = nl_alloc_attr(dd->outgoing_skb->data, FU_ASYNC_DATA, + sizeof(*async_data) + dd->irq_param[4] + + 2 * sizeof(u16)); + if (async_data == NULL) { + ERROR("can't add data to async IRQ buffer"); + return; + } + async_data->length = dd->irq_param[4] + 2 * sizeof(u16); + len = async_data->length; + async_data->address = 0; + + while (len > 0) { + rx_len = (len > rx_limit) ? rx_limit : len; + ret = spi_read_123(dd, 0x0000, (u8 *)&buf, + rx_len + 4 * sizeof(u16), false); + if (ret < 0) + break; + + if (buf[3] == 0xBABE) { + dd->legacy_acceleration = false; + dd->service_irq = service_irq; + nl_msg_init(dd->outgoing_skb->data, dd->nl_family.id, + dd->nl_seq - 1, MC_FUSION); + return; + } + + if (rx_limit == rx_len) + usleep_range(200, 300); + + if (buf[0] == 0x6060) { + ERROR("data not ready"); + start_legacy_acceleration_canned(dd); + ret = -EBUSY; + break; + } else if (buf[0] == 0x8070) { + if (buf[1] == dd->irq_param[1] || + buf[1] == dd->irq_param[2]) + async_data->address = buf[1]; + + if (async_data->address + + offset / sizeof(u16) != buf[1]) { + ERROR("sequence number incorrect %04X", buf[1]); + start_legacy_acceleration_canned(dd); + ret = -EBUSY; + break; + } + } + counter++; + memcpy(async_data->data + offset, buf + 4, rx_len); + offset += rx_len; + len -= rx_len; + } + async_data->status = *(buf + rx_len / sizeof(u16) + 2); + + if (ret < 0) { + ERROR("can't read IRQ buffer (%d)", ret); + nl_msg_init(dd->outgoing_skb->data, dd->nl_family.id, + dd->nl_seq - 1, MC_FUSION); + } else { + (void)skb_put(dd->outgoing_skb, + NL_SIZE(dd->outgoing_skb->data)); + ret = genlmsg_multicast(&dd->nl_family, dd->outgoing_skb, 0, + MC_FUSION, GFP_KERNEL); + if (ret < 0) { + ERROR("can't send IRQ buffer %d", ret); + msleep(300); + if (++dd->send_fail_count >= 10 && + dd->fusion_process != (pid_t)0) { + (void)kill_pid( + find_get_pid(dd->fusion_process), + SIGKILL, 1); + wake_up_process(dd->thread); + } + } else { + dd->send_fail_count = 0; + } + ret = nl_msg_new(dd, MC_FUSION); + if (ret < 0) + ERROR("could not allocate outgoing skb (%d)", ret); + } +} + +static void service_irq(struct dev_data *dd) +{ + struct fu_async_data *async_data; + u16 status, test, xbuf, value; + u16 address[2] = { 0 }; + u16 clear[2] = { 0 }; + bool read_buf[2] = {true, false}; + int ret, ret2; + + DBG("%s", __func__); + ret = dd->chip.read(dd, dd->irq_param[0], (u8 *)&status, + sizeof(status)); + if (ret < 0) { + ERROR("can't read IRQ status (%d)", ret); + return; + } + DBG("%s: status = 0x%04X", __func__, status); + + if (dd->gesture_reset) { + read_buf[0] = false; + clear[0] = dd->irq_param[10]; + status = dd->irq_param[10]; + dd->gesture_reset = false; + } else + if (status & dd->irq_param[10]) { + read_buf[0] = false; + clear[0] = dd->irq_param[10]; + } else if (status & dd->irq_param[18]) { + (void)dd->chip.read(dd, dd->irq_param[22], (u8 *)&value, + sizeof(value)); + if (value != dd->irq_param[23]) { + ERROR("unrecognized value [%#x] => %#x", + dd->irq_param[22], value); + clear[0] = dd->irq_param[18]; + ret2 = dd->chip.write(dd, dd->irq_param[0], + (u8 *)clear, sizeof(clear)); + return; + } + + test = status & (dd->irq_param[6] | dd->irq_param[7]); + + if (test == 0) + return; + else if (test == (dd->irq_param[6] | dd->irq_param[7])) + xbuf = ((status & dd->irq_param[5]) == 0) ? 0 : 1; + else if (test == dd->irq_param[6]) + xbuf = 0; + else if (test == dd->irq_param[7]) + xbuf = 1; + else { + ERROR("unexpected IRQ handler case 0x%04X ", status); + return; + } + + read_buf[1] = true; + address[1] = xbuf ? dd->irq_param[2] : dd->irq_param[1]; + address[0] = dd->irq_param[3]; + clear[0] = xbuf ? dd->irq_param[7] : dd->irq_param[6]; + clear[0] |= dd->irq_param[8]; + clear[0] |= dd->irq_param[18]; + + value = dd->irq_param[17]; + (void)dd->chip.write(dd, dd->irq_param[16], (u8 *)&value, + sizeof(value)); + } else if (status & dd->irq_param[9]) { + test = status & (dd->irq_param[6] | dd->irq_param[7]); + + if (test == (dd->irq_param[6] | dd->irq_param[7])) + xbuf = ((status & dd->irq_param[5]) != 0) ? 0 : 1; + else if (test == dd->irq_param[6]) + xbuf = 0; + else if (test == dd->irq_param[7]) + xbuf = 1; + else { + ERROR("unexpected IRQ handler case"); + return; + } + read_buf[1] = true; + address[1] = xbuf ? dd->irq_param[2] : dd->irq_param[1]; + + address[0] = dd->irq_param[3]; + clear[0] = dd->irq_param[6] | dd->irq_param[7] | + dd->irq_param[8] | dd->irq_param[9]; + value = dd->irq_param[13]; + (void)dd->chip.write(dd, dd->irq_param[12], (u8 *)&value, + sizeof(value)); + } else { + test = status & (dd->irq_param[6] | dd->irq_param[7]); + + if (test == 0) + return; + else if (test == (dd->irq_param[6] | dd->irq_param[7])) + xbuf = ((status & dd->irq_param[5]) == 0) ? 0 : 1; + else if (test == dd->irq_param[6]) + xbuf = 0; + else if (test == dd->irq_param[7]) + xbuf = 1; + else { + ERROR("unexpected IRQ handler case 0x%04X ",status); + return; + } + + address[0] = xbuf ? dd->irq_param[2] : dd->irq_param[1]; + clear[0] = xbuf ? dd->irq_param[7] : dd->irq_param[6]; + clear[0] |= dd->irq_param[8]; + } + + async_data = nl_alloc_attr(dd->outgoing_skb->data, FU_ASYNC_DATA, + sizeof(*async_data) + dd->irq_param[4]); + if (async_data == NULL) { + ERROR("can't add data to async IRQ buffer 1"); + return; + } + + async_data->status = status; + if (read_buf[0]) { + async_data->address = address[0]; + async_data->length = dd->irq_param[4]; + ret = dd->chip.read(dd, address[0], async_data->data, + dd->irq_param[4]); + } + + if (read_buf[1] && ret == 0) { + async_data = nl_alloc_attr(dd->outgoing_skb->data, + FU_ASYNC_DATA, + sizeof(*async_data) + + dd->irq_param[4]); + if (async_data == NULL) { + ERROR("can't add data to async IRQ buffer 2"); + nl_msg_init(dd->outgoing_skb->data, dd->nl_family.id, + dd->nl_seq - 1, MC_FUSION); + return; + } + async_data->address = address[1]; + async_data->length = dd->irq_param[4]; + async_data->status = status; + ret = dd->chip.read(dd, address[1], async_data->data, + dd->irq_param[4]); + } + + ret2 = dd->chip.write(dd, dd->irq_param[0], (u8 *)clear, + sizeof(clear)); + if (ret2 < 0) + ERROR("can't clear IRQ status (%d)", ret2); + + if (ret < 0) { + ERROR("can't read IRQ buffer (%d)", ret); + nl_msg_init(dd->outgoing_skb->data, dd->nl_family.id, + dd->nl_seq - 1, MC_FUSION); + } else { + DBG("%s: sending buffer", __func__); + (void)skb_put(dd->outgoing_skb, + NL_SIZE(dd->outgoing_skb->data)); + ret = genlmsg_multicast(&dd->nl_family, dd->outgoing_skb, 0, + MC_FUSION, GFP_KERNEL); + if (ret < 0) { + ERROR("can't send IRQ buffer %d", ret); + msleep(300); + if (read_buf[0] == false || + (++dd->send_fail_count >= 10 && + dd->fusion_process != (pid_t)0)) { + (void)kill_pid( + find_get_pid(dd->fusion_process), + SIGKILL, 1); + wake_up_process(dd->thread); + } + } else { + dd->send_fail_count = 0; + } + ret = nl_msg_new(dd, MC_FUSION); + if (ret < 0) + ERROR("could not allocate outgoing skb (%d)", ret); + } +} + +/****************************************************************************\ +* Processing thread * +\****************************************************************************/ + +static int processing_thread(void *arg) +{ + struct dev_data *dd = arg; + struct maxim_sti_pdata *pdata = dd->spi->dev.platform_data; + struct sk_buff *skb; + char *argv[] = { pdata->touch_fusion, "daemon", + pdata->nl_family, + dd->config_file, NULL }; + int ret = 0, ret2 = 0; + bool fusion_dead; + + DRV_PT_DBG("%s started", __FUNCTION__); + sched_setscheduler(current, SCHED_FIFO, &dd->thread_sched); + + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + DRV_PT_DBG("Begin"); + + /* ensure that we have outgoing skb */ + if (dd->outgoing_skb == NULL) + if (nl_msg_new(dd, MC_FUSION) < 0) { + schedule(); + continue; + } + + /* priority 1: start up fusion process */ + if (dd->fusion_process != (pid_t)0 && get_pid_task( + find_get_pid(dd->fusion_process), + PIDTYPE_PID) == NULL) { + DRV_PT_DBG("Re-Starting TF"); + stop_scan_canned(dd); + dd->start_fusion = true; + dd->fusion_process = (pid_t)0; + dd->input_no_deconfig = true; + } + if (dd->start_fusion) { + DRV_PT_DBG("Spawning TF"); + do { + ret = call_usermodehelper(argv[0], argv, NULL, + UMH_WAIT_EXEC); + if (ret != 0) + msleep(100); + } while (ret != 0 && !kthread_should_stop()); + + if (ret == 0) { + /* power-up and reset-high */ + ret = regulator_control(dd, true); + if (ret < 0) + ERROR("failed to enable regulators"); + + usleep_range(300, 400); + pdata->reset(pdata, 1); + + dd->start_fusion = false; + dd->tf_status |= TF_STATUS_BUSY; + } + } + if (kthread_should_stop()) + break; + + /* priority 2: process pending Netlink messages */ + while ((skb = skb_dequeue(&dd->incoming_skb_queue)) != NULL) { + if (kthread_should_stop()) + break; + if (nl_process_msg(dd, skb) < 0) + skb_queue_purge(&dd->incoming_skb_queue); + } + if (kthread_should_stop()) + break; + + /* priority 3: suspend/resume */ + if (dd->suspend_in_progress && !(dd->tf_status & TF_STATUS_BUSY)) { + if (dd->irq_registered) { + disable_irq(dd->spi->irq); + if (dd->gesture_en) { + enable_gesture(dd); + enable_irq(dd->spi->irq); + } else + if (!dd->idle) + stop_scan_canned(dd); + else + stop_idle_scan(dd); + } + + dd->suspended = true; + complete(&dd->suspend_resume); + dd->expect_resume_ack = true; + dd->gesture_reset = false; + while (!dd->resume_in_progress) { + /* the line below is a MUST */ + set_current_state(TASK_INTERRUPTIBLE); + if (kthread_should_stop()) + break; + if (dd->gesture_en && dd->irq_registered && + pdata->irq(pdata) == 0) + gesture_wake_detect(dd); + schedule(); + } + if (dd->irq_registered) { + if (dd->gesture_en) + finish_gesture(dd); + else + if (!dd->idle) + start_scan_canned(dd); + } + dd->suspended = false; + dd->resume_in_progress = false; + dd->suspend_in_progress = false; + complete(&dd->suspend_resume); + + fusion_dead = false; + dd->send_fail_count = 0; + do { + if (dd->fusion_process != (pid_t)0 && + get_pid_task(find_get_pid( + dd->fusion_process), + PIDTYPE_PID) == NULL) { + fusion_dead = true; + break; + } + ret = nl_add_attr(dd->outgoing_skb->data, + FU_RESUME, NULL, 0); + if (ret < 0) { + ERROR("can't add data to resume " \ + "buffer"); + nl_msg_init(dd->outgoing_skb->data, + dd->nl_family.id, + dd->nl_seq - 1, MC_FUSION); + msleep(100); + continue; + } + (void)skb_put(dd->outgoing_skb, + NL_SIZE(dd->outgoing_skb->data)); + ret = genlmsg_multicast(&dd->nl_family, + dd->outgoing_skb, 0, MC_FUSION, + GFP_KERNEL); + if (ret < 0) { + ERROR("can't send resume message %d", + ret); + msleep(100); + if (++dd->send_fail_count >= 10) { + fusion_dead = true; + ret = 0; + } + } + ret2 = nl_msg_new(dd, MC_FUSION); + if (ret2 < 0) + ERROR("could not allocate outgoing " \ + "skb (%d)", ret2); + } while (ret != 0 && !kthread_should_stop()); + + if (!ret && dd->send_fail_count>= 10 && + dd->fusion_process != (pid_t)0) + (void)kill_pid(find_get_pid(dd->fusion_process), + SIGKILL, 1); + + dd->send_fail_count = 0; + if (fusion_dead) + continue; + } + if (kthread_should_stop()) + break; + + /* priority 4: update /sys/device information */ + if (dd->sysfs_update_type != DR_SYSFS_UPDATE_NONE) + if (!dd->expect_resume_ack) { + if (send_sysfs_info(dd) < 0) + ERROR( + "Can not send sysfs update to touch fusion" + ); + dd->sysfs_update_type = DR_SYSFS_UPDATE_NONE; + } + + /* priority 5: service interrupt */ + if (dd->irq_registered && !dd->expect_resume_ack && + ((pdata->irq(pdata) == 0) + || dd->gesture_reset + )) + dd->service_irq(dd); + if (dd->irq_registered && !dd->expect_resume_ack && + pdata->irq(pdata) == 0) + continue; + + DRV_PT_DBG("End"); + /* nothing more to do; sleep */ + schedule(); + } + + return 0; +} + +/****************************************************************************\ +* SYSFS interface * +\****************************************************************************/ + +static ssize_t chip_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + return snprintf(buf, PAGE_SIZE, "0x%04X\n", dd->chip_id); +} + +static ssize_t fw_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + return snprintf(buf, PAGE_SIZE, "0x%04X\n", dd->fw_ver); +} + +static ssize_t driver_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + return snprintf(buf, PAGE_SIZE, "0x%04X\n", dd->drv_ver); +} + +static ssize_t tf_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + return snprintf(buf, PAGE_SIZE, "0x%04X\n", dd->tf_ver); +} + +static ssize_t panel_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%04X\n", panel_id); +} + +static ssize_t glove_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + return snprintf(buf, PAGE_SIZE, "%u\n", dd->glove_enabled); +} + +static ssize_t glove_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + u32 temp; + unsigned long time_left; + + mutex_lock(&dd->sysfs_update_mutex); + + if (sscanf(buf, "%u", &temp) != 1) { + ERROR("Invalid (%s)", buf); + return -EINVAL; + } + dd->glove_enabled = !!temp; + set_bit(DR_SYSFS_UPDATE_BIT_GLOVE, &(dd->sysfs_update_type)); + wake_up_process(dd->thread); + time_left = wait_for_completion_timeout(&dd->sysfs_ack_glove, 3*HZ); + + mutex_unlock(&dd->sysfs_update_mutex); + + if (time_left > 0) + return strnlen(buf, PAGE_SIZE); + else + return -EAGAIN; +} + +static ssize_t gesture_wakeup_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + return snprintf(buf, PAGE_SIZE, "%u\n", dd->gesture_en); +} + +static ssize_t gesture_wakeup_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + u32 temp; + + if (sscanf(buf, "%u", &temp) != 1) { + ERROR("Invalid (%s)", buf); + return -EINVAL; + } + dd->gesture_en = temp; + + return strnlen(buf, PAGE_SIZE); +} + +static ssize_t charger_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + return snprintf(buf, PAGE_SIZE, "%u\n", dd->charger_mode_en); +} + +static ssize_t charger_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + u32 temp; + unsigned long time_left; + + mutex_lock(&dd->sysfs_update_mutex); + + if (sscanf(buf, "%u", &temp) != 1) { + ERROR("Invalid (%s)", buf); + return -EINVAL; + } + if (temp > DR_WIRELESS_CHARGER) { + ERROR("Charger Mode Value Out of Range"); + return -EINVAL; + } + dd->charger_mode_en = temp; + set_bit(DR_SYSFS_UPDATE_BIT_CHARGER, &(dd->sysfs_update_type)); + wake_up_process(dd->thread); + time_left = wait_for_completion_timeout(&dd->sysfs_ack_charger, 3*HZ); + + mutex_unlock(&dd->sysfs_update_mutex); + + if (time_left > 0) + return strnlen(buf, PAGE_SIZE); + else + return -EAGAIN; +} + +static ssize_t info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + return snprintf(buf, PAGE_SIZE, "chip id: 0x%04X driver ver: 0x%04X " + "fw ver: 0x%04X panel id: 0x%04X " + "tf ver: 0x%04X\n", + dd->chip_id, dd->drv_ver, dd->fw_ver, + panel_id, dd->tf_ver); +} + +static ssize_t screen_status_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + u32 temp; + + if (sscanf(buf, "%u", &temp) != 1) { + ERROR("Invalid (%s)", buf); + return -EINVAL; + } + if (temp) + resume(dev); + else + suspend(dev); + + return strnlen(buf, PAGE_SIZE); +} + +static ssize_t tf_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + return snprintf(buf, PAGE_SIZE, "0x%08X", dd->tf_status); +} + +static ssize_t default_loaded_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + return snprintf(buf, PAGE_SIZE, "%d", + (dd->tf_status & TF_STATUS_DEFAULT_LOADED)); +} + +static ssize_t lcd_fps_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + return snprintf(buf, PAGE_SIZE, "%u\n", dd->lcd_fps); +} + +static ssize_t lcd_fps_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct spi_device *spi = to_spi_device(dev); + struct dev_data *dd = spi_get_drvdata(spi); + u32 temp; + unsigned long time_left; + + mutex_lock(&dd->sysfs_update_mutex); + + if (sscanf(buf, "%u", &temp) != 1) { + ERROR("Invalid (%s)", buf); + return -EINVAL; + } + + dd->lcd_fps = temp; + set_bit(DR_SYSFS_UPDATE_BIT_LCD_FPS, &(dd->sysfs_update_type)); + wake_up_process(dd->thread); + time_left = wait_for_completion_timeout(&dd->sysfs_ack_lcd_fps, 3*HZ); + + mutex_unlock(&dd->sysfs_update_mutex); + + if (time_left > 0) + return strnlen(buf, PAGE_SIZE); + else + return -EAGAIN; +} + +static struct device_attribute dev_attrs[] = { + __ATTR(fw_ver, S_IRUGO, fw_ver_show, NULL), + __ATTR(chip_id, S_IRUGO, chip_id_show, NULL), + __ATTR(tf_ver, S_IRUGO, tf_ver_show, NULL), + __ATTR(driver_ver, S_IRUGO, driver_ver_show, NULL), + __ATTR(panel_id, S_IRUGO, panel_id_show, NULL), + __ATTR(glove_en, S_IRUGO | S_IWUSR, glove_show, glove_store), + __ATTR(gesture_wakeup, S_IRUGO | S_IWUSR, gesture_wakeup_show, gesture_wakeup_store), + __ATTR(charger_mode, S_IRUGO | S_IWUSR, charger_mode_show, charger_mode_store), + __ATTR(info, S_IRUGO, info_show, NULL), + __ATTR(screen_status, S_IWUSR, NULL, screen_status_store), + __ATTR(tf_status, S_IRUGO, tf_status_show, NULL), + __ATTR(default_loaded, S_IRUGO, default_loaded_show, NULL), + __ATTR(lcd_fps, S_IRUGO | S_IWUSR, lcd_fps_show, lcd_fps_store), +}; + +static int create_sysfs_entries(struct dev_data *dd) +{ + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(dev_attrs); i++) { + ret = device_create_file(&dd->spi->dev, &dev_attrs[i]); + if (ret) { + for (; i >= 0; --i) { + device_remove_file(&dd->spi->dev, + &dev_attrs[i]); + dd->sysfs_created--; + } + break; + } + dd->sysfs_created++; + } + return ret; +} + +static void remove_sysfs_entries(struct dev_data *dd) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dev_attrs); i++) + if (dd->sysfs_created && dd->sysfs_created--) + device_remove_file(&dd->spi->dev, &dev_attrs[i]); +} + +static int send_sysfs_info(struct dev_data *dd) +{ + struct fu_sysfs_info *sysfs_info; + int ret, ret2; + + sysfs_info = nl_alloc_attr(dd->outgoing_skb->data, + FU_SYSFS_INFO, + sizeof(*sysfs_info)); + if (sysfs_info == NULL) { + ERROR("Can't allocate sysfs_info"); + return -EAGAIN; + } + sysfs_info->type = dd->sysfs_update_type; + + sysfs_info->glove_value = dd->glove_enabled; + sysfs_info->charger_value = dd->charger_mode_en; + sysfs_info->lcd_fps_value = dd->lcd_fps; + + (void)skb_put(dd->outgoing_skb, + NL_SIZE(dd->outgoing_skb->data)); + ret = genlmsg_multicast(&dd->nl_family, + dd->outgoing_skb, 0, + MC_FUSION, GFP_KERNEL); + if (ret < 0) + ERROR("could not reply to fusion (%d)", ret); + + /* allocate new outgoing skb */ + ret2 = nl_msg_new(dd, MC_FUSION); + if (ret2 < 0) + ERROR("could not allocate outgoing skb (%d)", ret2); + + return 0; +} + +/****************************************************************************\ +* Driver initialization * +\****************************************************************************/ + +static int probe(struct spi_device *spi) +{ + struct maxim_sti_pdata *pdata = spi->dev.platform_data; + struct dev_data *dd; + unsigned long flags; + int ret, i; + void *ptr; + +#ifdef CONFIG_OF + if (pdata == NULL && spi->dev.of_node) { + pdata = devm_kzalloc(&spi->dev, + sizeof(struct maxim_sti_pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&spi->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + ret = maxim_parse_dt(&spi->dev, pdata); + if (ret) + goto of_parse_failure; + spi->dev.platform_data = pdata; + } +#endif + + pdata->init = maxim_sti_gpio_init; + pdata->reset = maxim_sti_gpio_reset; + pdata->irq = maxim_sti_gpio_irq; + + pdata->nl_mc_groups = DEF_NL_MC_GROUPS; + pdata->chip_access_method = DEF_CHIP_ACC_METHOD; + pdata->tx_buf_size = BUF_SIZE; + pdata->rx_buf_size = BUF_SIZE; + pdata->nl_family = TOUCH_FUSION; + + dev_dbg(&spi->dev, "maxim_sti,reset-gpio: %d", pdata->gpio_reset); + dev_dbg(&spi->dev, "maxim_sti,irq-gpio: %d", pdata->gpio_irq); + dev_dbg(&spi->dev, "maxim_sti,nl_mc_groups: %d", pdata->nl_mc_groups); + dev_dbg(&spi->dev, "maxim_sti,chip_access_method: %d", pdata->chip_access_method); + dev_dbg(&spi->dev, "maxim_sti,default_reset_state: %d", pdata->default_reset_state); + dev_dbg(&spi->dev, "maxim_sti,tx_buf_size: %d", pdata->tx_buf_size); + dev_dbg(&spi->dev, "maxim_sti,rx_buf_size: %d", pdata->rx_buf_size); + dev_dbg(&spi->dev, "maxim_sti,touch_fusion: %s", pdata->touch_fusion); + dev_dbg(&spi->dev, "maxim_sti,config_file: %s", pdata->config_file); + dev_dbg(&spi->dev, "maxim_sti,nl_family: %s", pdata->nl_family); + dev_dbg(&spi->dev, "maxim_sti,fw_name: %s", pdata->fw_name); + + /* validate platform data */ + if (pdata == NULL || pdata->init == NULL || pdata->reset == NULL || + pdata->irq == NULL || pdata->touch_fusion == NULL || + pdata->config_file == NULL || pdata->nl_family == NULL || + GENL_CHK(pdata->nl_family) || + pdata->nl_mc_groups < MC_GROUPS || + pdata->chip_access_method == 0 || + pdata->chip_access_method > ARRAY_SIZE(chip_access_methods) || + pdata->default_reset_state > 1 || + pdata->tx_buf_size == 0 || + pdata->rx_buf_size == 0) { + dev_err(&spi->dev, "invalid platform data!"); + ret = -EINVAL; + goto of_parse_failure; + } + + /* device context: allocate structure */ + dd = kzalloc(sizeof(*dd) + + sizeof(*dd->nl_ops) * pdata->nl_mc_groups + + sizeof(*dd->nl_mc_groups) * pdata->nl_mc_groups, + GFP_KERNEL); + if (dd == NULL) { + dev_err(&spi->dev, "cannot allocate memory!"); + ret = -ENOMEM; + goto of_parse_failure; + } + + /* device context: set up dynamic allocation pointers */ + ptr = (void *)dd + sizeof(*dd); + dd->nl_ops = ptr; + ptr += sizeof(*dd->nl_ops) * pdata->nl_mc_groups; + dd->nl_mc_groups = ptr; + + /* device context: initialize structure members */ + spi_set_drvdata(spi, dd); + spi->bits_per_word = 16; + spi_setup(spi); + dd->spi = spi; + dd->nl_seq = 1; + init_completion(&dd->suspend_resume); + + /* allocate DMA memory */ + dd->tx_buf = kzalloc(pdata->tx_buf_size + pdata->rx_buf_size, + GFP_KERNEL | GFP_DMA); + if (dd->tx_buf == NULL) { + dev_err(&spi->dev, "cannot allocate DMA accesible memory"); + ret = -ENOMEM; + goto buf_alloc_failure; + } + dd->rx_buf = dd->tx_buf + pdata->tx_buf_size; + memset(dd->tx_buf, 0xFF, pdata->tx_buf_size); + (void)set_chip_access_method(dd, pdata->chip_access_method); + + /* initialize regulators */ + regulator_init(dd); + + /* initialize platform */ + + /* Get pinctrl if target uses pinctrl */ + dd->ts_pinctrl = devm_pinctrl_get(&(spi->dev)); + if (IS_ERR_OR_NULL(dd->ts_pinctrl)) { + ret = PTR_ERR(dd->ts_pinctrl); + pr_debug("Target does not use pinctrl %d\n", ret); + } + + dd->pinctrl_state_active + = pinctrl_lookup_state(dd->ts_pinctrl, "pmx_ts_active"); + if (IS_ERR_OR_NULL(dd->pinctrl_state_active)) { + ret = PTR_ERR(dd->pinctrl_state_active); + pr_debug("Can not lookup active pinstate %d\n", ret); + } + + dd->pinctrl_state_suspended + = pinctrl_lookup_state(dd->ts_pinctrl, "pmx_ts_suspend"); + if (IS_ERR_OR_NULL(dd->pinctrl_state_suspended)) { + ret = PTR_ERR(dd->pinctrl_state_suspended); + pr_debug("Can not lookup active pinstate %d\n", ret); + } + ret = pinctrl_select_state(dd->ts_pinctrl, + dd->pinctrl_state_active); + if (ret < 0) + pr_debug("pinctrl set active fail\n"); + + + ret = pdata->init(pdata, true); + if (ret < 0) + goto pinit_failure; + + /* Netlink: initialize incoming skb queue */ + skb_queue_head_init(&dd->incoming_skb_queue); + + /* Netlink: register GENL family */ + dd->nl_family.id = GENL_ID_GENERATE; + dd->nl_family.version = NL_FAMILY_VERSION; + GENL_COPY(dd->nl_family.name, pdata->nl_family); + + /* Netlink: register family ops */ + for (i = 0; i < MC_GROUPS; i++) { + dd->nl_ops[i].cmd = i; + dd->nl_ops[i].doit = nl_callback_noop; + } + dd->nl_ops[MC_DRIVER].doit = nl_callback_driver; + dd->nl_ops[MC_FUSION].doit = nl_callback_fusion; + dd->nl_ops[MC_EVENT_BROADCAST].doit = nl_callback_noop; + + /* Netlink: register family multicast groups */ + GENL_COPY(dd->nl_mc_groups[MC_DRIVER].name, MC_DRIVER_NAME); + GENL_COPY(dd->nl_mc_groups[MC_FUSION].name, MC_FUSION_NAME); + GENL_COPY(dd->nl_mc_groups[MC_EVENT_BROADCAST].name, + MC_EVENT_BROADCAST_NAME); + ret = _genl_register_family_with_ops_grps(&dd->nl_family, + dd->nl_ops, + MC_GROUPS, + dd->nl_mc_groups, + MC_GROUPS); + if (ret < 0) { + dev_err(&spi->dev, "error registering nl_mc_group"); + goto nl_family_failure; + } + dd->nl_mc_group_count = MC_GROUPS; + + /* Netlink: pre-allocate outgoing skb */ + ret = nl_msg_new(dd, MC_FUSION); + if (ret < 0) { + dev_err(&spi->dev, "error alloc msg"); + goto nl_failure; + } + + /* start processing thread */ + dd->thread_sched.sched_priority = MAX_USER_RT_PRIO / 2; + dd->thread = kthread_run(processing_thread, dd, pdata->nl_family); + if (IS_ERR(dd->thread)) { + dev_err(&spi->dev, "error creating kthread!"); + ret = PTR_ERR(dd->thread); + goto kthread_failure; + } + + /* Netlink: ready to start processing incoming messages */ + dd->nl_enabled = true; + + /* No sysfs update initially */ + dd->sysfs_update_type = DR_SYSFS_UPDATE_NONE; + init_completion(&dd->sysfs_ack_glove); + init_completion(&dd->sysfs_ack_charger); + init_completion(&dd->sysfs_ack_lcd_fps); + mutex_init(&dd->sysfs_update_mutex); + + /* add us to the devices list */ + spin_lock_irqsave(&dev_lock, flags); + list_add_tail(&dd->dev_list, &dev_list); + spin_unlock_irqrestore(&dev_lock, flags); + + ret = create_sysfs_entries(dd); + if (ret) { + dev_err(&spi->dev, "failed to create sysfs file"); + goto sysfs_failure; + } + snprintf(dd->config_file, CFG_FILE_NAME_MAX, pdata->config_file, panel_id); + dev_dbg(&spi->dev, "configuration file: %s", dd->config_file); + + /* start up Touch Fusion */ + dd->start_fusion = true; + wake_up_process(dd->thread); + dev_info(&spi->dev, "%s: driver loaded; version %s; release date %s", + dd->nl_family.name, DRIVER_VERSION, DRIVER_RELEASE); + + return 0; + +sysfs_failure: + dd->nl_enabled = false; + spin_lock_irqsave(&dev_lock, flags); + list_del(&dd->dev_list); + spin_unlock_irqrestore(&dev_lock, flags); + (void)kthread_stop(dd->thread); +kthread_failure: + if (dd->outgoing_skb) + kfree_skb(dd->outgoing_skb); +nl_failure: + genl_unregister_family(&dd->nl_family); +nl_family_failure: + pdata->init(pdata, false); +pinit_failure: + regulator_uninit(dd); + kfree(dd->tx_buf); +buf_alloc_failure: + spi_set_drvdata(spi, NULL); + kfree(dd); +of_parse_failure: + spi->dev.platform_data = NULL; + devm_kfree(&spi->dev, pdata); + return ret; +} + +static int remove(struct spi_device *spi) +{ + struct maxim_sti_pdata *pdata = spi->dev.platform_data; + struct dev_data *dd = spi_get_drvdata(spi); + unsigned long flags; + u8 i; + + dev_dbg(&spi->dev, "remove"); + + /* BEWARE: tear-down sequence below is carefully staged: */ + /* 1) first the feeder of Netlink messages to the processing thread */ + /* is turned off */ + /* 2) then the thread itself is shut down */ + /* 3) then Netlink family is torn down since no one would be using */ + /* it at this point */ + /* 4) above step (3) insures that all Netlink senders are */ + /* definitely gone and it is safe to free up outgoing skb buffer */ + /* and incoming skb queue */ + dev_dbg(&spi->dev, "stopping thread.."); + dd->nl_enabled = false; + (void)kthread_stop(dd->thread); + dev_dbg(&spi->dev, "kthread stopped."); + genl_unregister_family(&dd->nl_family); + if (dd->outgoing_skb) + kfree_skb(dd->outgoing_skb); + skb_queue_purge(&dd->incoming_skb_queue); + + if (dd->fusion_process != (pid_t)0) + (void)kill_pid(find_get_pid(dd->fusion_process), SIGKILL, 1); + + dev_dbg(&spi->dev, "removing sysfs entries.."); + if (dd->parent != NULL) + sysfs_remove_link(dd->input_dev[0]->dev.kobj.parent, + MAXIM_STI_NAME); + remove_sysfs_entries(dd); + + dev_dbg(&spi->dev, "unregister input devices.."); + for (i = 0; i < INPUT_DEVICES; i++) + if (dd->input_dev[i]) + input_unregister_device(dd->input_dev[i]); +#if defined(CONFIG_FB) + fb_unregister_client(&dd->fb_notifier); +#endif + + if (dd->irq_registered) { + dev_dbg(&spi->dev, "disabling interrupt"); + disable_irq(dd->spi->irq); + dev_dbg(&spi->dev, "stopping scan"); + stop_scan_canned(dd); + free_irq(dd->spi->irq, dd); + } + + spin_lock_irqsave(&dev_lock, flags); + list_del(&dd->dev_list); + spin_unlock_irqrestore(&dev_lock, flags); + + pdata->reset(pdata, 0); + usleep_range(100, 120); + regulator_control(dd, false); + pdata->init(pdata, false); + regulator_uninit(dd); + + dev_dbg(&spi->dev, "detaching from spi.."); + spi_set_drvdata(spi, NULL); + kzfree(dd); + spi->dev.platform_data = NULL; + devm_kfree(&spi->dev, pdata); + + dev_info(&spi->dev, "driver unloaded"); + return 0; +} + +static void shutdown(struct spi_device *spi) +{ + struct maxim_sti_pdata *pdata = spi->dev.platform_data; + struct dev_data *dd = spi_get_drvdata(spi); + + if (dd->parent != NULL) + sysfs_remove_link(dd->input_dev[0]->dev.kobj.parent, + MAXIM_STI_NAME); + remove_sysfs_entries(dd); + + pdata->reset(pdata, 0); + usleep_range(100, 120); + regulator_control(dd, false); +} + +/****************************************************************************\ +* Module initialization * +\****************************************************************************/ + +static const struct spi_device_id id[] = { + { MAXIM_STI_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(spi, id); + +#ifdef CONFIG_OF +static struct of_device_id maxim_match_table[] = { + { .compatible = "maxim,maxim_sti",}, + { }, +}; +#endif + +static struct spi_driver driver = { + .probe = probe, + .remove = remove, + .shutdown = shutdown, + .id_table = id, + .driver = { + .name = MAXIM_STI_NAME, +#ifdef CONFIG_OF + .of_match_table = maxim_match_table, +#endif + .owner = THIS_MODULE, +#ifdef CONFIG_PM_SLEEP + .pm = &pm_ops, +#endif + }, +}; + +static int maxim_sti_init(void) +{ + INIT_LIST_HEAD(&dev_list); + spin_lock_init(&dev_lock); + return spi_register_driver(&driver); +} + +static void maxim_sti_exit(void) +{ + spi_unregister_driver(&driver); +} + +module_param(panel_id, ushort, S_IRUGO); +MODULE_PARM_DESC(panel_id, "Touch Panel ID Configuration"); + +module_init(maxim_sti_init); +module_exit(maxim_sti_exit); + +MODULE_AUTHOR("Maxim Integrated Products, Inc."); +MODULE_DESCRIPTION("Maxim SmartTouch Imager Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/input/touchscreen/st/Kconfig b/drivers/input/touchscreen/st/Kconfig new file mode 100644 index 000000000000..817faea01742 --- /dev/null +++ b/drivers/input/touchscreen/st/Kconfig @@ -0,0 +1,9 @@ +# +# STMicroelectronics touchscreen driver configuration +# + +config TOUCHSCREEN_ST_I2C + tristate "STMicroelectronics i2c touchscreen" + depends on TOUCHSCREEN_ST + help + This enables support for ST touch panel over I2C based touchscreens. diff --git a/drivers/input/touchscreen/st/Makefile b/drivers/input/touchscreen/st/Makefile new file mode 100644 index 000000000000..0aa7b4a364da --- /dev/null +++ b/drivers/input/touchscreen/st/Makefile @@ -0,0 +1,5 @@ +# +## Makefile for the STMicroelectronics touchscreen driver. +# + +obj-$(CONFIG_TOUCHSCREEN_ST_I2C) += fts.o fts_gui.o fts_driver_test.o fts_lib/ diff --git a/drivers/input/touchscreen/st/fts.c b/drivers/input/touchscreen/st/fts.c new file mode 100644 index 000000000000..08bfb83a9447 --- /dev/null +++ b/drivers/input/touchscreen/st/fts.c @@ -0,0 +1,2361 @@ +/* + * fts.c + * + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * marco.cali@st.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/device.h> + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/hrtimer.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/completion.h> + +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> + +#include <linux/notifier.h> +#include <linux/fb.h> + +#ifdef KERNEL_ABOVE_2_6_38 +#include <linux/input/mt.h> +#endif + +#include "fts.h" +#include "fts_lib/ftsCompensation.h" +#include "fts_lib/ftsIO.h" +#include "fts_lib/ftsError.h" +#include "fts_lib/ftsFlash.h" +#include "fts_lib/ftsFrame.h" +#include "fts_lib/ftsGesture.h" +#include "fts_lib/ftsTest.h" +#include "fts_lib/ftsTime.h" +#include "fts_lib/ftsTool.h" + +#define LINK_KOBJ_NAME "tp" + +/* + * Uncomment to use polling mode instead of interrupt mode. + * + */ +/* #define FTS_USE_POLLING_MODE */ + +/* + * Event installer helpers + */ +#define event_id(_e) EVENTID_##_e +#define handler_name(_h) fts_##_h##_event_handler + +#define install_handler(_i, _evt, _hnd) \ +do { \ + _i->event_dispatch_table[event_id(_evt)] = handler_name(_hnd); \ +} while (0) + +/* + * Asyncronouns command helper + */ +#define WAIT_WITH_TIMEOUT(_info, _timeout, _command) \ +do { \ + if (wait_for_completion_timeout(&_info->cmd_done, _timeout) == 0) { \ + dev_warn(_info->dev, "Waiting for %s command: timeout\n", \ + #_command); \ + } \ +} while (0) + +#ifdef KERNEL_ABOVE_2_6_38 +#define TYPE_B_PROTOCOL +#endif + +#if defined(SCRIPTLESS) || defined(DRIVER_TEST) +static struct class *fts_cmd_class; +#endif + +extern chipInfo ftsInfo; + +unsigned char tune_version_same; + +char tag[8] = "[ FTS ]\0"; + +static u32 *typeOfComand; +static int numberParameters; +static int feature_feasibility = ERROR_OP_NOT_ALLOW; +#ifdef PHONE_GESTURE +static u8 mask[GESTURE_MASK_SIZE+2]; +#endif +static void fts_interrupt_enable(struct fts_ts_info *info); +static int fts_init_hw(struct fts_ts_info *info); +static int fts_mode_handler(struct fts_ts_info *info, int force); +static int fts_command(struct fts_ts_info *info, unsigned char cmd); + +static int fts_chip_initialization(struct fts_ts_info *info); + +void touch_callback(unsigned int status) +{ + /* Empty */ +} + +unsigned int le_to_uint(const unsigned char *ptr) +{ + return (unsigned int) ptr[0] + (unsigned int) ptr[1] * 0x100; +} + +unsigned int be_to_uint(const unsigned char *ptr) +{ + return (unsigned int) ptr[1] + (unsigned int) ptr[0] * 0x100; +} + +/* force update firmware*/ +static ssize_t fts_fw_control_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count){ + int ret, mode; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /* reading out firmware upgrade mode */ + sscanf(buf, "%d", &mode); +#ifdef FTM3_CHIP + ret = flashProcedure(PATH_FILE_FW, mode, !mode); +#else + ret = flashProcedure(PATH_FILE_FW, mode, 1); +#endif + info->fwupdate_stat = ret; + + if (ret < OK) + logError(1, "%s %s :Unable to upgrade firmware\n", tag, __func__); + return count; +} + +static ssize_t fts_sysfs_config_id_show(struct device *dev, struct device_attribute *attr, + char *buf) { + int error; + + error = snprintf(buf, TSP_BUF_SIZE, "%x.%x\n", ftsInfo.u16_fwVer, ftsInfo.u16_cfgId); + return error; +} + +static ssize_t fts_sysfs_fwupdate_show(struct device *dev, struct device_attribute *attr, + char *buf) { + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /* fwupdate_stat: ERROR code Returned by flashProcedure. */ + return snprintf(buf, TSP_BUF_SIZE, "%08X\n", info->fwupdate_stat); +} + +static ssize_t fts_fw_test_show(struct device *dev, struct device_attribute *attr, + char *buf) { + + Firmware fw; + int ret; + + fw.data = NULL; + ret = readFwFile(PATH_FILE_FW, &fw, 0); + + if (ret < OK) { + logError(1, "%s: Error during reading FW file! ERROR %08X\n", tag, ret); + } else + logError(1, "%s: fw_version = %04X, config_version = %04X, size = %d bytes\n", + tag, fw.fw_ver, fw.config_id, fw.data_size); + + kfree(fw.data); + return 0; +} + +/* TODO: edit this function according to the features policy to allow during the screen on/off */ +int check_feature_feasibility(struct fts_ts_info *info, unsigned int feature) +{ + int res = ERROR_OP_NOT_ALLOW; + + if (info->resume_bit == 0) { + switch (feature) { +#ifdef PHONE_GESTURE + case FEAT_GESTURE: + res = OK; + break; +#endif + default: + logError(1, "%s %s: Feature not allowed in this operating mode! ERROR %08X\n", tag, __func__, res); + break; + + } + } else{ + switch (feature) { +#ifdef PHONE_GESTURE + case FEAT_GESTURE: +#endif + case FEAT_GLOVE: + /* glove mode can only activate during sense on */ + res = OK; + break; + + default: + logError(1, "%s %s: Feature not allowed in this operating mode! ERROR %08X\n", tag, __func__, res); + break; + + } + } + + return res; + +} + +static ssize_t fts_feature_enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + char *p = (char *)buf; + unsigned int temp; + int res = OK; + + if ((count + 1) / 3 != 2) { + logError(1, "%s fts_feature_enable: Number of parameter wrong! %d > %d\n", + tag, (count + 1) / 3, 2); + } else{ + sscanf(p, "%02X ", &temp); + p += 3; + res = check_feature_feasibility(info, temp); + if (res >= OK) { + switch (temp) { +#ifdef PHONE_GESTURE + case FEAT_GESTURE: + sscanf(p, "%02X ", &info->gesture_enabled); + logError(1, "%s fts_feature_enable: Gesture Enabled = %d\n", tag, + info->gesture_enabled); + break; +#endif + case FEAT_GLOVE: + sscanf(p, "%02X ", &info->glove_enabled); + logError(1, "%s fts_feature_enable: Glove Enabled = %d\n", + tag, info->glove_enabled); + + break; + + default: + logError(1, "%s fts_feature_enable: Feature %02X not valid! ERROR %08X\n", tag, temp, ERROR_OP_NOT_ALLOW); + + } + feature_feasibility = res; + } + + } + return count; +} + +static ssize_t fts_feature_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0, res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + if (feature_feasibility >= OK) + res = fts_mode_handler(info, 1); + else{ + res = feature_feasibility; + logError(1, "%s %s: Call before echo xx xx > feature_enable with a correct feature! ERROR %08X\n", tag, __func__, res); + } + + all_strbuff = (u8 *) kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else{ + logError(1, "%s fts_feature_enable_show: Unable to allocate all_strbuff! ERROR %08X\n", tag, ERROR_ALLOC); + } + + feature_feasibility = ERROR_OP_NOT_ALLOW; + return count; +} + +#ifdef PHONE_GESTURE +static ssize_t fts_gesture_mask_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0, res; + + if (mask[0] == 0) { + res = ERROR_OP_NOT_ALLOW; + logError(1, "%s %s: Call before echo enable/disable xx xx .... > gesture_mask with a correct number of parameters! ERROR %08X\n", tag, __func__, res); + + } else{ + res = fts_disableInterrupt(); + if (res >= OK) { + if (mask[1] == FEAT_ENABLE) + res = enableGesture(&mask[2], mask[0]); + else{ + if (mask[1] == FEAT_DISABLE) + res = disableGesture(&mask[2], mask[0]); + else + res = ERROR_OP_NOT_ALLOW; + } + if (res < OK) { + logError(1, "%s fts_gesture_mask_store: ERROR %08X\n", tag, res); + } + } + res |= fts_enableInterrupt(); + } + + all_strbuff = (u8 *) kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else{ + logError(1, "%s fts_gesture_mask_show: Unable to allocate all_strbuff! ERROR %08X\n", tag, ERROR_ALLOC); + } + + mask[0] = 0; + return count; +} + +static ssize_t fts_gesture_mask_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + char *p = (char *)buf; + int n; + unsigned int temp; + + if ((count + 1) / 3 > GESTURE_MASK_SIZE+1) { + logError(1, "%s fts_gesture_mask_store: Number of bytes of parameter wrong! %d > (enable/disable + %d )\n", tag, (count + 1) / 3, GESTURE_MASK_SIZE); + mask[0] = 0; + } else { + mask[0] = ((count + 1) / 3) - 1; + for (n = 1; n <= (count + 1) / 3; n++) { + sscanf(p, "%02X ", &temp); + p += 3; + mask[n] = (u8)temp; + logError(1, "%s mask[%d] = %02X\n", tag, n, mask[n]); + + } + + } + + return count; +} +#endif + +/************************ PRODUCTION TEST **********************************/ +static ssize_t stm_fts_cmd_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { + int n; + char *p = (char *) buf; + + typeOfComand = (u32 *) kmalloc(8 * sizeof (u32), GFP_KERNEL); + if (typeOfComand == NULL) { + logError(1, "%s impossible to allocate typeOfComand!\n", tag); + return count; + } + memset(typeOfComand, 0, 8 * sizeof (u32)); + + logError(1, "%s\n", tag); + for (n = 0; n < (count + 1) / 3; n++) { + sscanf(p, "%02X ", &typeOfComand[n]); + p += 3; + logError(1, "%s typeOfComand[%d] = %02X\n", tag, n, typeOfComand[n]); + + } + + numberParameters = n; + logError(1, "%s Number of Parameters = %d\n", tag, numberParameters); + return count; +} + +static ssize_t stm_fts_cmd_show(struct device *dev, struct device_attribute *attr, + char *buf) { + char buff[CMD_STR_LEN] = {0}; + int res, j, doClean = 0, count; + + int size = 6 * 2; + u8 *all_strbuff = NULL; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + MutualSenseData compData; + SelfSenseData comData; + MutualSenseFrame frameMS; + SelfSenseFrame frameSS; + + /* struct used for defining which test + *perform during the production test + */ + TestToDo todoDefault; + + todoDefault.MutualRaw = 1; + todoDefault.MutualRawGap = 1; + todoDefault.MutualCx1 = 0; + todoDefault.MutualCx2 = 1; + todoDefault.MutualCx2Adj = 1; + todoDefault.MutualCxTotal = 0; + todoDefault.MutualCxTotalAdj = 0; + + todoDefault.MutualKeyRaw = 0; + todoDefault.MutualKeyCx1 = 0; + todoDefault.MutualKeyCx2 = 0; + todoDefault.MutualKeyCxTotal = 0; + + todoDefault.SelfForceRaw = 1; + todoDefault.SelfForceRawGap = 0; + todoDefault.SelfForceIx1 = 0; + todoDefault.SelfForceIx2 = 0; + todoDefault.SelfForceIx2Adj = 0; + todoDefault.SelfForceIxTotal = 1; + todoDefault.SelfForceIxTotalAdj = 0; + todoDefault.SelfForceCx1 = 0; + todoDefault.SelfForceCx2 = 0; + todoDefault.SelfForceCx2Adj = 0; + todoDefault.SelfForceCxTotal = 0; + todoDefault.SelfForceCxTotalAdj = 0; + + todoDefault.SelfSenseRaw = 1; + todoDefault.SelfSenseRawGap = 0; + todoDefault.SelfSenseIx1 = 0; + todoDefault.SelfSenseIx2 = 0; + todoDefault.SelfSenseIx2Adj = 0; + todoDefault.SelfSenseIxTotal = 1; + todoDefault.SelfSenseIxTotalAdj = 0; + todoDefault.SelfSenseCx1 = 0; + todoDefault.SelfSenseCx2 = 0; + todoDefault.SelfSenseCx2Adj = 0; + todoDefault.SelfSenseCxTotal = 0; + todoDefault.SelfSenseCxTotalAdj = 0; + + if (numberParameters >= 1 && typeOfComand != NULL) { + res = fts_disableInterrupt(); + if (res < 0) { + logError(0, "%s fts_disableInterrupt: ERROR %08X\n", tag, res); + res = (res | ERROR_DISABLE_INTER); + goto END; + } + + res = fb_unregister_client(&info->notifier); + if (res < 0) { + logError(1, "%s ERROR: unregister notifier failed!\n", tag); + goto END; + } + + switch (typeOfComand[0]) { + /*ITO TEST*/ + case 0x01: + res = production_test_ito(); + break; + /*PRODUCTION TEST*/ + case 0x00: + if (ftsInfo.u32_mpPassFlag != INIT_MP) { + logError(0, "%s MP Flag not set!\n", tag, res); + res = production_test_main(LIMITS_FILE, 1, 1, &todoDefault, INIT_MP); + } else{ + logError(0, "%s MP Flag set!\n", tag, res); + res = production_test_main(LIMITS_FILE, 1, 0, &todoDefault, INIT_MP); + } + break; + /*read mutual raw*/ + case 0x13: + logError(0, "%s Get 1 MS Frame\n", tag); + /* res = getMSFrame(ADDR_RAW_TOUCH, &frame, 0); */ + res = getMSFrame2(MS_TOUCH_ACTIVE, &frameMS); + if (res < 0) { + logError(0, "%s Error while taking the MS frame... ERROR %02X\n", tag, res); + + } else { + logError(0, "%s The frame size is %d words\n", tag, res); + size = (res * sizeof (short) + 8)*2; + /* set res to OK because if getMSFrame is + * successful res = number of words read + */ + res = OK; + } + break; + /*read self raw*/ + case 0x15: + logError(0, "%s Get 1 SS Frame\n", tag); + res = getSSFrame2(SS_TOUCH, &frameSS); + + if (res < OK) { + logError(0, "%s Error while taking the SS frame... ERROR %02X\n", tag, res); + + } else { + logError(0, "%s The frame size is %d words\n", tag, res); + size = (res * sizeof (short) + 8)*2+1; + /* set res to OK because if getMSFrame is + * successful res = number of words read + */ + res = OK; + } + + break; + + case 0x14: /*read mutual comp data */ + logError(0, "%s Get MS Compensation Data\n", tag); + res = readMutualSenseCompensationData(MS_TOUCH_ACTIVE, &compData); + + if (res < 0) { + logError(0, "%s Error reading MS compensation data ERROR %02X\n", tag, res); + } else { + logError(0, "%s MS Compensation Data Reading Finished!\n", tag); + size = ((compData.node_data_size + 9) * sizeof (u8))*2; + } + break; + + case 0x16: /* read self comp data */ + logError(0, "%s Get SS Compensation Data...\n", tag); + res = readSelfSenseCompensationData(SS_TOUCH, &comData); + if (res < 0) { + logError(0, "%s Error reading SS compensation data ERROR %02X\n", tag, res); + } else { + logError(0, "%s SS Compensation Data Reading Finished!\n", tag); + size = ((comData.header.force_node + comData.header.sense_node)*2 + 12) * sizeof (u8)*2; + } + break; + + case 0x03: /* MS Raw DATA TEST */ + res = fts_system_reset(); + if (res >= OK) + res = production_test_ms_raw(LIMITS_FILE, 1, &todoDefault); + break; + + case 0x04: /* MS CX DATA TEST */ + res = fts_system_reset(); + if (res >= OK) + res = production_test_ms_cx(LIMITS_FILE, 1, &todoDefault); + break; + + case 0x05: /* SS RAW DATA TEST */ + res = fts_system_reset(); + if (res >= OK) + res = production_test_ss_raw(LIMITS_FILE, 1, &todoDefault); + break; + + case 0x06: /* SS IX CX DATA TEST */ + res = fts_system_reset(); + if (res >= OK) + res = production_test_ss_ix_cx(LIMITS_FILE, 1, &todoDefault); + break; + + case 0xF0: + case 0xF1: /* TOUCH ENABLE/DISABLE */ + doClean = (int) (typeOfComand[0]&0x01); + res = cleanUp(doClean); + + break; + + default: + logError(1, "%s COMMAND NOT VALID!! Insert a proper value ...\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + doClean = fts_enableInterrupt(); + if (doClean < 0) { + logError(0, "%s fts_enableInterrupt: ERROR %08X\n", tag, (doClean|ERROR_ENABLE_INTER)); + } + } else { + logError(1, "%s NO COMMAND SPECIFIED!!! do: 'echo [cmd_code] [args] > stm_fts_cmd' before looking for result!\n", tag); + res = ERROR_OP_NOT_ALLOW; + typeOfComand = NULL; + + } + + if (fb_register_client(&info->notifier) < 0) { + logError(1, "%s ERROR: register notifier failed!\n", tag); + } + +END: /* here start the reporting phase, assembling the data to send in the file node */ + all_strbuff = (u8 *) kmalloc(size, GFP_KERNEL); + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + if (res >= OK) { + /*all the other cases are already fine printing only the res.*/ + switch (typeOfComand[0]) { + case 0x13: + snprintf(buff, sizeof (buff), "%02X", (u8) frameMS.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", (u8) frameMS.header.sense_node); + strlcat(all_strbuff, buff, size); + + for (j = 0; j < frameMS.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%04X", frameMS.node_data[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(frameMS.node_data); + break; + + case 0x15: + snprintf(buff, sizeof(buff), "%02X", (u8) frameSS.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", (u8) frameSS.header.sense_node); + strlcat(all_strbuff, buff, size); + + /* Copying self raw data Force */ + for (j = 0; j < frameSS.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%04X", frameSS.force_data[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying self raw data Sense */ + for (j = 0; j < frameSS.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%04X", frameSS.sense_data[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(frameSS.force_data); + kfree(frameSS.sense_data); + break; + + case 0x14: + snprintf(buff, sizeof(buff), "%02X", (u8) compData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", (u8) compData.header.sense_node); + strlcat(all_strbuff, buff, size); + + /* Cpying CX1 value */ + snprintf(buff, sizeof(buff), "%02X", compData.cx1); + strlcat(all_strbuff, buff, size); + + /* Copying CX2 values */ + for (j = 0; j < compData.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%02X", *(compData.node_data + j)); + strlcat(all_strbuff, buff, size); + } + + kfree(compData.node_data); + break; + + case 0x16: + snprintf(buff, sizeof(buff), "%02X", comData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.header.sense_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.f_ix1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.s_ix1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.f_cx1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.s_cx1); + strlcat(all_strbuff, buff, size); + + /* Copying IX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%02X", comData.ix2_fm[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying IX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%02X", comData.ix2_sn[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying CX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%02X", comData.cx2_fm[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying CX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%02X", comData.cx2_sn[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(comData.ix2_fm); + kfree(comData.ix2_sn); + kfree(comData.cx2_fm); + kfree(comData.cx2_sn); + break; + + default: + break; + + } + } + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + numberParameters = 0; /* need to reset the number of parameters + * in order to wait the next command, comment + *if you want to repeat the last command sent + *just doing a cat + */ + /* logError(0,"%s numberParameters = %d\n", tag, numberParameters); */ + kfree(all_strbuff); + + kfree(typeOfComand); + return count; + +} + +static DEVICE_ATTR(fwupdate, (S_IRUGO | S_IWUSR | S_IWGRP), fts_sysfs_fwupdate_show, fts_fw_control_store); +static DEVICE_ATTR(appid, (S_IRUGO), fts_sysfs_config_id_show, NULL); +static DEVICE_ATTR(fw_file_test, (S_IRUGO), fts_fw_test_show, NULL); +static DEVICE_ATTR(stm_fts_cmd, (S_IRUGO | S_IWUSR | S_IWGRP), stm_fts_cmd_show, stm_fts_cmd_store); +static DEVICE_ATTR(feature_enable, (S_IRUGO | S_IWUSR | S_IWGRP), fts_feature_enable_show, fts_feature_enable_store); +#ifdef PHONE_GESTURE +static DEVICE_ATTR(gesture_mask, (S_IRUGO | S_IWUSR | S_IWGRP), fts_gesture_mask_show, fts_gesture_mask_store); +#endif +/* /sys/devices/soc.0/f9928000.i2c/i2c-6/6-0049 */ +static struct attribute *fts_attr_group[] = { + &dev_attr_fwupdate.attr, + &dev_attr_appid.attr, + &dev_attr_fw_file_test.attr, + /* &dev_attr_touch_debug.attr, */ + &dev_attr_stm_fts_cmd.attr, + &dev_attr_feature_enable.attr, +#ifdef PHONE_GESTURE + &dev_attr_gesture_mask.attr, +#endif + NULL, +}; + +static int fts_command(struct fts_ts_info *info, unsigned char cmd) +{ + unsigned char regAdd; + int ret; + + regAdd = cmd; + + ret = fts_writeCmd(®Add, sizeof (regAdd)); /* 0 = ok */ + + logError(0, "%s Issued command 0x%02x, return value %08X\n", cmd, ret); + + return ret; +} + +void fts_input_report_key(struct fts_ts_info *info, int key_code) +{ + mutex_lock(&info->input_report_mutex); + input_report_key(info->input_dev, key_code, 1); + input_sync(info->input_dev); + input_report_key(info->input_dev, key_code, 0); + input_sync(info->input_dev); + mutex_unlock(&info->input_report_mutex); +} + +/* + * New Interrupt handle implementation + */ + +static inline unsigned char *fts_next_event(unsigned char *evt) +{ + /* Nothing to do with this event, moving to the next one */ + evt += FIFO_EVENT_SIZE; + + /* the previous one was the last event ? */ + return (evt[-1] & 0x1F) ? evt : NULL; +} + +/* EventId : 0x00 */ +static unsigned char *fts_nop_event_handler(struct fts_ts_info *info, + unsigned char *event) { + /* logError(1, "%s %s Doing nothing for event = %02X %02X %02X %02X %02X %02X %02X %02X\n", + * tag, __func__, event[0], event[1], event[2], event[3], event[4], event[5], event[6], event[7]); + */ + return fts_next_event(event); +} + +/* EventId : 0x03 */ +static unsigned char *fts_enter_pointer_event_handler(struct fts_ts_info *info, + unsigned char *event) { + unsigned char touchId, touchcount; + int x, y, z; + + if (!info->resume_bit) + goto no_report; + + touchId = event[1] & 0x0F; + touchcount = (event[1] & 0xF0) >> 4; + + __set_bit(touchId, &info->touch_id); + + x = (event[2] << 4) | (event[4] & 0xF0) >> 4; + y = (event[3] << 4) | (event[4] & 0x0F); + z = (event[5] & 0x3F); + + if (x == X_AXIS_MAX) + x--; + + if (y == Y_AXIS_MAX) + y--; + + input_mt_slot(info->input_dev, touchId); + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 1); + logError(0, "%s %s : TouchID = %d,Touchcount = %d\n", tag, __func__, touchId, touchcount); + if (touchcount == 1) { + input_report_key(info->input_dev, BTN_TOUCH, 1); + input_report_key(info->input_dev, BTN_TOOL_FINGER, 1); + } + /* input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, touchId); */ + input_report_abs(info->input_dev, ABS_MT_POSITION_X, x); + input_report_abs(info->input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR, z); + input_report_abs(info->input_dev, ABS_MT_TOUCH_MINOR, z); + input_report_abs(info->input_dev, ABS_MT_PRESSURE, z); + logError(0, "%s %s : Event 0x%02x - ID[%d], (x, y, z) = (%3d, %3d, %3d)\n", tag, __func__, *event, touchId, x, y, z); + +no_report: + return fts_next_event(event); +} + +/* EventId : 0x04 */ +static unsigned char *fts_leave_pointer_event_handler(struct fts_ts_info *info, + unsigned char *event) { + unsigned char touchId, touchcount; + + touchId = event[1] & 0x0F; + touchcount = (event[1] & 0xF0) >> 4; + + __clear_bit(touchId, &info->touch_id); + + input_mt_slot(info->input_dev, touchId); + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 0); + logError(0, "%s %s : TouchID = %d, Touchcount = %d\n", tag, __func__, touchId, touchcount); + if (touchcount == 0) { + input_report_key(info->input_dev, BTN_TOUCH, 0); + input_report_key(info->input_dev, BTN_TOOL_FINGER, 0); + } + + input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, -1); + logError(0, "%s %s : Event 0x%02x - release ID[%d]\n", tag, __func__, event[0], touchId); + + return fts_next_event(event); +} + +/* EventId : 0x05 */ +#define fts_motion_pointer_event_handler fts_enter_pointer_event_handler + +#ifdef PHONE_KEY +/* EventId : 0x0E */ +static unsigned char *fts_key_status_event_handler(struct fts_ts_info *info, unsigned char *event) +{ + int value; + logError(0, "%s %s Received event %02X %02X %02X %02X %02X %02X %02X %02X\n", tag, __func__, event[0], event[1], event[2], event[3], event[4], event[5], event[6], event[7]); + /* TODO: the customer should handle the events coming from the keys according his needs (this is an example that report only the single pressure of one key at time) */ + if (event[2] != 0) { /* event[2] contain the bitmask of the keys that are actually pressed */ + switch (event[2]) { + case KEY1: + value = KEY_HOMEPAGE; + logError(0, "%s %s: Button HOME !\n", tag, __func__); + break; + + case KEY2: + value = KEY_BACK; + logError(0, "%s %s: Button Back !\n", tag, __func__); + break; + + case KEY3: + value = KEY_MENU; + logError(0, "%s %s: Button Menu !\n", tag, __func__); + break; + + default: + logError(0, "%s %s: No valid Button ID or more than one key pressed!\n", tag, __func__); + goto done; + } + + fts_input_report_key(info, value); + } else{ + logError(0, "%s %s: All buttons released!\n", tag, __func__); + } +done: + return fts_next_event(event); +} +#endif + +/* EventId : 0x0F */ +static unsigned char *fts_error_event_handler(struct fts_ts_info *info, + unsigned char *event) { + int error = 0, i = 0; + logError(0, "%s %s Received event 0x%02x 0x%02x\n", tag, __func__, event[0], event[1]); + + switch (event[1]) { + case EVENT_TYPE_ESD_ERROR: + { + for (i = 0; i < TOUCH_ID_MAX; i++) { + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); + } + input_sync(info->input_dev); + + fts_chip_powercycle(info); + + error = fts_system_reset(); + error |= fts_mode_handler(info, 0); + error |= fts_enableInterrupt(); + if (error < OK) { + logError(1, "%s %s Cannot restore the device ERROR %08X\n", tag, __func__, error); + } + } + break; + case EVENT_TYPE_WATCHDOG_ERROR: /* watch dog timer */ + { + if (event[2] == 0) { + /* before reset clear all slot */ + for (i = 0; i < TOUCH_ID_MAX; i++) { + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, + (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); + } + input_sync(info->input_dev); + error = fts_system_reset(); + error |= fts_mode_handler(info, 0); + error |= fts_enableInterrupt(); + if (error < OK) { + logError(1, "%s %s Cannot reset the device ERROR %08X\n", tag, __func__, error); + } + } + } + break; + + } + return fts_next_event(event); +} + +/* EventId : 0x10 */ +static unsigned char *fts_controller_ready_event_handler( + struct fts_ts_info *info, unsigned char *event) { + int error; + logError(0, "%s %s Received event 0x%02x\n", tag, __func__, event[0]); + info->touch_id = 0; + input_sync(info->input_dev); + setSystemResettedUp(1); + setSystemResettedDown(1); + error = fts_mode_handler(info, 0); + if (error < OK) { + logError(1, "%s %s Cannot restore the device status ERROR %08X\n", tag, __func__, error); + } + return fts_next_event(event); +} + +/* EventId : 0x16 */ +static unsigned char *fts_status_event_handler( + struct fts_ts_info *info, unsigned char *event) { + /* logError(1, "%s Received event 0x%02x\n", tag, event[0]); */ + + switch (event[1]) { + case EVENT_TYPE_MS_TUNING_CMPL: + case EVENT_TYPE_SS_TUNING_CMPL: + case FTS_FORCE_CAL_SELF_MUTUAL: + case FTS_FLASH_WRITE_CONFIG: + case FTS_FLASH_WRITE_COMP_MEMORY: + case FTS_FORCE_CAL_SELF: + case FTS_WATER_MODE_ON: + case FTS_WATER_MODE_OFF: + default: + logError(0, + "%s %s Received unhandled status event = %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[0], event[1], event[2], + event[3], event[4], event[5], event[6], event[7]); + break; + } + + return fts_next_event(event); +} + +#ifdef PHONE_GESTURE +static unsigned char *fts_gesture_event_handler(struct fts_ts_info *info, unsigned char *event) +{ + unsigned char touchId; + int value; + + logError(0, "%s gesture event data: %02X %02X %02X %02X %02X %02X %02X %02X\n", tag, event[0], event[1], event[2], event[3], event[4], event[5], event[6], event[7]); + + if (event[1] == 0x03) { + + logError(1, "%s %s: Gesture ID %02X enable_status = %02X\n", tag, __func__, event[2], event[3]); + + } + if (event[1] == EVENT_TYPE_ENB && event[2] == 0x00) { + switch (event[3]) { + case GESTURE_ENABLE: + logError(1, "%s %s: Gesture Enabled! res = %02X\n", tag, __func__, event[4]); + break; + + case GESTURE_DISABLE: + logError(1, "%s %s: Gesture Disabled! res = %02X\n", tag, __func__, event[4]); + break; + + default: + logError(1, "%s %s: Event not Valid!\n", tag, __func__); + + } + + } + + /* always use touchId zero */ + touchId = 0; + __set_bit(touchId, &info->touch_id); + + if (event[0] == EVENTID_GESTURE && (event[1] == EVENT_TYPE_GESTURE_DTC1 || event[1] == EVENT_TYPE_GESTURE_DTC2)) { + + switch (event[2]) { + case GES_ID_DBLTAP: + value = KEY_WAKEUP; + logError(0, "%s %s: double tap !\n", tag, __func__); + break; + + case GES_ID_AT: + value = KEY_WWW; + logError(0, "%s %s: @ !\n", tag, __func__); + break; + + case GES_ID_C: + value = KEY_C; + logError(0, "%s %s: C !\n", tag, __func__); + break; + + case GES_ID_E: + value = KEY_E; + logError(0, "%s %s: e !\n", tag, __func__); + break; + + case GES_ID_F: + value = KEY_F; + logError(0, "%s %s: F !\n", tag, __func__); + break; + + case GES_ID_L: + value = KEY_L; + logError(0, "%s %s: L !\n", tag, __func__); + break; + + case GES_ID_M: + value = KEY_M; + logError(0, "%s %s: M !\n", tag, __func__); + break; + + case GES_ID_O: + value = KEY_O; + logError(0, "%s %s: O !\n", tag, __func__); + break; + + case GES_ID_S: + value = KEY_S; + logError(0, "%s %s: S !\n", tag, __func__); + break; + + case GES_ID_V: + value = KEY_V; + logError(0, "%s %s: V !\n", tag, __func__); + break; + + case GES_ID_W: + value = KEY_W; + logError(0, "%s %s: W !\n", tag, __func__); + break; + + case GES_ID_Z: + value = KEY_Z; + logError(0, "%s %s: Z !\n", tag, __func__); + break; + + case GES_ID_HFLIP_L2R: + value = KEY_RIGHT; + logError(0, "%s %s: -> !\n", tag, __func__); + break; + + case GES_ID_HFLIP_R2L: + value = KEY_LEFT; + logError(0, "%s %s: <- !\n", tag, __func__); + break; + + case GES_ID_VFLIP_D2T: + value = KEY_UP; + logError(0, "%s %s: UP !\n", tag, __func__); + break; + + case GES_ID_VFLIP_T2D: + value = KEY_DOWN; + logError(0, "%s %s: DOWN !\n", tag, __func__); + break; + + case GES_ID_CUST1: + value = KEY_F1; + logError(0, "%s %s: F1 !\n", tag, __func__); + break; + + case GES_ID_CUST2: + value = KEY_F1; + logError(0, "%s %s: F2 !\n", tag, __func__); + break; + + case GES_ID_CUST3: + value = KEY_F3; + logError(0, "%s %s: F3 !\n", tag, __func__); + break; + + case GES_ID_CUST4: + value = KEY_F1; + logError(0, "%s %s: F4 !\n", tag, __func__); + break; + + case GES_ID_CUST5: + value = KEY_F1; + logError(0, "%s %s: F5 !\n", tag, __func__); + break; + + case GES_ID_LEFTBRACE: + value = KEY_LEFTBRACE; + logError(0, "%s %s: < !\n", tag, __func__); + break; + + case GES_ID_RIGHTBRACE: + value = KEY_RIGHTBRACE; + logError(0, "%s %s: > !\n", tag, __func__); + break; + default: + logError(0, "%s %s: No valid GestureID!\n", tag, __func__); + goto gesture_done; + + } + + fts_input_report_key(info, value); + + gesture_done: + /* Done with gesture event, clear bit. */ + __clear_bit(touchId, &info->touch_id); + } + + return fts_next_event(event); +} +#endif + +/* EventId : 0x05 */ +#define fts_motion_pointer_event_handler fts_enter_pointer_event_handler + +/* + * This handler is called each time there is at least + * one new event in the FIFO + */ +static void fts_event_handler(struct work_struct *work) +{ + struct fts_ts_info *info; + int error, error1; + int left_events; + unsigned char regAdd; + unsigned char data[FIFO_EVENT_SIZE * (FIFO_DEPTH)] = {0}; + unsigned char *event = NULL; + unsigned char eventId; + event_dispatch_handler_t event_handler; + + info = container_of(work, struct fts_ts_info, work); + /* + * to avoid reading all FIFO, we read the first event and + * then check how many events left in the FIFO + */ + + + regAdd = FIFO_CMD_READONE; + error = fts_readCmd(®Add, + sizeof (regAdd), data, FIFO_EVENT_SIZE); + + if (!error) { + + left_events = data[7] & 0x1F; + if ((left_events > 0) && (left_events < FIFO_DEPTH)) { + /* + * Read remaining events. + */ + regAdd = FIFO_CMD_READALL; + + error1 = fts_readCmd(®Add, sizeof (regAdd), + &data[FIFO_EVENT_SIZE], + left_events * FIFO_EVENT_SIZE); + /* + * Got an error reading remaining events, + * process at least * the first one that was + * reading fine. + */ + if (error1) + data[7] &= 0xE0; + } + + /* At least one event is available */ + event = data; + do { + eventId = *event; + event_handler = info->event_dispatch_table[eventId]; + + if (eventId < EVENTID_LAST) { + event = event_handler(info, (event)); + } else { + event = fts_next_event(event); + } + input_sync(info->input_dev); + } while (event); + } + + /* + * re-enable interrupts + */ + fts_interrupt_enable(info); +} + +static int cx_crc_check(void) +{ + unsigned char regAdd1[3] = {FTS_CMD_HW_REG_R, ADDR_CRC_BYTE0, ADDR_CRC_BYTE1}; + unsigned char val = 0; + unsigned char crc_status; + unsigned int error; + + error = fts_readCmd(regAdd1, sizeof (regAdd1), &val, 1); + if (error < OK) { + logError(1, "%s %s Cannot read crc status ERROR %08X\n", tag, __func__, error); + return error; + } + + crc_status = val & CRC_MASK; + if (crc_status != OK) { /* CRC error if crc_status!= 0 */ + logError(1, "%s %s CRC ERROR = %X\n", tag, __func__, crc_status); + } + + return crc_status; /* return OK if no CRC error, or a number >OK if crc error */ +} + +static void fts_fw_update_auto(struct work_struct *work) +{ + int retval = 0; + int retval1 = 0; + int ret; + struct fts_ts_info *info; + struct delayed_work *fwu_work = container_of(work, struct delayed_work, work); + int crc_status = 0; + int error = 0; + info = container_of(fwu_work, struct fts_ts_info, fwu_work); + + logError(1, "%s Fw Auto Update is starting...\n", tag); + + /* check CRC status */ + ret = cx_crc_check(); + if (ret > OK && ftsInfo.u16_fwVer == 0x0000) { + logError(1, "%s %s: CRC Error or NO FW!\n", tag, __func__); + crc_status = 1; + } else { + crc_status = 0; + logError(1, "%s %s: NO CRC Error or Impossible to read CRC register!\n", tag, __func__); + } +#ifdef FTM3_CHIP + retval = flashProcedure(PATH_FILE_FW, crc_status, !crc_status); +#else + retval = flashProcedure(PATH_FILE_FW, crc_status, 1); +#endif + if ((retval & 0xFF000000) == ERROR_FLASH_PROCEDURE) { + logError(1, "%s %s: firmware update failed and retry! ERROR %08X\n", tag, __func__, retval); + fts_chip_powercycle(info); /* power reset */ +#ifdef FTM3_CHIP + retval1 = flashProcedure(PATH_FILE_FW, crc_status, !crc_status); +#else + retval1 = flashProcedure(PATH_FILE_FW, crc_status, 1); +#endif + if ((retval1 & 0xFF000000) == ERROR_FLASH_PROCEDURE) { + logError(1, "%s %s: firmware update failed again! ERROR %08X\n", tag, __func__, retval1); + logError(1, "%s Fw Auto Update Failed!\n", tag); + /* return; */ + } + } + + if ((ftsInfo.u32_mpPassFlag != INIT_MP) && (ftsInfo.u32_mpPassFlag != INIT_FIELD)) + ret = ERROR_GET_INIT_STATUS; + else + ret = OK; + + if (ret == ERROR_GET_INIT_STATUS) { /* initialization status not correct or after FW complete update, do initialization. */ + error = fts_chip_initialization(info); + if (error < OK) { + logError(1, "%s %s Cannot initialize the chip ERROR %08X\n", tag, __func__, error); + } + } + error = fts_init_hw(info); + if (error < OK) { + logError(1, "%s Cannot initialize the hardware device ERROR %08X\n", tag, error); + } + + logError(1, "%s Fw Auto Update Finished!\n", tag); +} + +static int fts_chip_initialization(struct fts_ts_info *info) +{ + int ret2 = 0; + int retry; + int initretrycnt = 0; + + /* initialization error, retry initialization */ + for (retry = 0; retry <= INIT_FLAG_CNT; retry++) { + ret2 = production_test_initialization(); + if (ret2 == OK) { + ret2 = save_mp_flag(INIT_FIELD); + if (ret2 == OK) + break; + } + initretrycnt++; + logError(1, "%s initialization cycle count = %04d - ERROR %08X\n", tag, initretrycnt, ret2); + fts_chip_powercycle(info); + } + if (ret2 < OK) { /* initialization error */ + logError(1, "%s fts initialization failed 3 times\n", tag); + } + + return ret2; +} + +#ifdef FTS_USE_POLLING_MODE + +static enum hrtimer_restart fts_timer_func(struct hrtimer *timer) +{ + struct fts_ts_info *info = + container_of(timer, struct fts_ts_info, timer); + + queue_work(info->event_wq, &info->work); + return HRTIMER_NORESTART; +} +#else + +static irqreturn_t fts_interrupt_handler(int irq, void *handle) +{ + struct fts_ts_info *info = handle; + disable_irq_nosync(info->client->irq); + queue_work(info->event_wq, &info->work); + return IRQ_HANDLED; +} +#endif + +static int fts_interrupt_install(struct fts_ts_info *info) +{ + int i, error = 0; + + info->event_dispatch_table = kzalloc( + sizeof (event_dispatch_handler_t) * EVENTID_LAST, GFP_KERNEL); + + if (!info->event_dispatch_table) { + logError(1, "%s OOM allocating event dispatch table\n", tag); + return -ENOMEM; + } + + for (i = 0; i < EVENTID_LAST; i++) + info->event_dispatch_table[i] = fts_nop_event_handler; + + install_handler(info, ENTER_POINTER, enter_pointer); + install_handler(info, LEAVE_POINTER, leave_pointer); + install_handler(info, MOTION_POINTER, motion_pointer); + install_handler(info, ERROR_EVENT, error); + install_handler(info, CONTROL_READY, controller_ready); + install_handler(info, STATUS_UPDATE, status); +#ifdef PHONE_GESTURE + install_handler(info, GESTURE, gesture); +#endif +#ifdef PHONE_KEY + install_handler(info, KEY_STATUS, key_status); +#endif + + /* disable interrupts in any case */ + error = fts_disableInterrupt(); + +#ifdef FTS_USE_POLLING_MODE + logError(1, "%s Polling Mode\n"); + hrtimer_init(&info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + info->timer.function = fts_timer_func; + hrtimer_start(&info->timer, ktime_set(1, 0), HRTIMER_MODE_REL); +#else + logError(1, "%s Interrupt Mode\n", tag); + if (request_irq(info->client->irq, fts_interrupt_handler, + IRQF_TRIGGER_LOW, info->client->name, + info)) { + logError(1, "%s Request irq failed\n", tag); + kfree(info->event_dispatch_table); + error = -EBUSY; + } /*else { + error = fts_enableInterrupt(); + }*/ +#endif + + return error; +} + +static void fts_interrupt_uninstall(struct fts_ts_info *info) +{ + + fts_disableInterrupt(); + + kfree(info->event_dispatch_table); +#ifdef FTS_USE_POLLING_MODE + hrtimer_cancel(&info->timer); +#else + free_irq(info->client->irq, info); +#endif +} + +static void fts_interrupt_enable(struct fts_ts_info *info) +{ +#ifdef FTS_USE_POLLING_MODE + hrtimer_start(&info->timer, + ktime_set(0, 10000000), HRTIMER_MODE_REL); +#else + enable_irq(info->client->irq); +#endif +} + +/* +static void fts_interrupt_disable(struct fts_ts_info *info) +{ +#ifdef FTS_USE_POLLING_MODE + hrtimer_cancel(&info->timer); +#else + disable_irq(info->client->irq); +#endif +} +*/ + +static int fts_init(struct fts_ts_info *info) +{ + int error; + + error = fts_system_reset(); + if (error < OK && error != (ERROR_TIMEOUT | ERROR_SYSTEM_RESET_FAIL)) { + logError(1, "%s Cannot reset the device! ERROR %08X\n", tag, error); + return error; + } + if (error == (ERROR_TIMEOUT | ERROR_SYSTEM_RESET_FAIL)) { + logError(1, "%s Setting default Chip INFO!\n", tag); + defaultChipInfo(0); + } else { + error = readChipInfo(0); /* system reset OK */ + if (error < OK) { + logError(1, "%s Cannot read Chip Info! ERROR %08X\n", tag, error); + } + } + + error = fts_interrupt_install(info); + + if (error != OK) + logError(1, "%s Init (1) error (ERROR = %08X)\n", error); + + return error; +} + +int fts_chip_powercycle(struct fts_ts_info *info) +{ + int error, i; + + logError(1, "%s %s: Power Cycle Starting...\n", tag, __func__); + if (info->pwr_reg) { + error = regulator_disable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable DVDD regulator\n", tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_disable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable AVDD regulator\n", tag, __func__); + } + } + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) + gpio_set_value(info->bdata->reset_gpio, 0); + + msleep(300); + if (info->pwr_reg) { + error = regulator_enable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable AVDD regulator\n", tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_enable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable DVDD regulator\n", tag, __func__); + } + } + msleep(300); /* time needed by the regulators for reaching the regime values */ + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) { + msleep(10); /* time to wait before bring up the reset gpio after the power up of the regulators */ + gpio_set_value(info->bdata->reset_gpio, 1); + /* msleep(300); */ + } + + /* before reset clear all slot */ + for (i = 0; i < TOUCH_ID_MAX; i++) { + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, + (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); + } + input_sync(info->input_dev); + + logError(1, "%s %s: Power Cycle Finished! ERROR CODE = %08x\n", tag, __func__, error); + setSystemResettedUp(1); + setSystemResettedDown(1); + return error; +} + +int fts_chip_powercycle2(struct fts_ts_info *info, unsigned long sleep) +{ + int error, i; + + logError(1, "%s %s: Power Cycle Starting...\n", tag, __func__); + if (info->pwr_reg) { + error = regulator_disable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable DVDD regulator\n", tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_disable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable AVDD regulator\n", tag, __func__); + } + } + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) + gpio_set_value(info->bdata->reset_gpio, 0); + + msleep(sleep); + if (info->pwr_reg) { + error = regulator_enable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable AVDD regulator\n", tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_enable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable DVDD regulator\n", tag, __func__); + } + } + msleep(500); /*time needed by the regulators for reaching the regime values */ + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) { + msleep(10); /*time to wait before bring up the reset gpio after the power up of the regulators */ + gpio_set_value(info->bdata->reset_gpio, 1); + /* msleep(300); */ + } + + /* before reset clear all slot */ + for (i = 0; i < TOUCH_ID_MAX; i++) { + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, + (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); + } + input_sync(info->input_dev); + + logError(1, "%s %s: Power Cycle Finished! ERROR CODE = %08x\n", tag, __func__, error); + setSystemResettedUp(1); + setSystemResettedDown(1); + return error; +} + +static int fts_init_hw(struct fts_ts_info *info) +{ + int error = 0; + + error = cleanUp(1); + if (error < OK) + logError(1, "%s Init (2) error (ERROR = %08X)\n", tag, error); + + info->mode = MODE_NORMAL; + + return error; +} + + /* + * TODO: change this function according with the needs of + *customer in temrs of feature to enable/disable + */ +static int fts_mode_handler(struct fts_ts_info *info, int force) +{ + int res = OK; + int ret = OK; + + logError(0, "%s %s: Mode Handler starting...\n", tag, __func__); + switch (info->resume_bit) { + case 0:/* screen down */ + logError(0, "%s %s: Screen OFF...\n", tag, __func__); +#ifdef PHONE_GESTURE + if (info->gesture_enabled == 1) { + logError(0, "%s %s: enter in gesture mode !\n", tag, __func__); + res = enterGestureMode(isSystemResettedDown()); + if (res >= OK) { + + info->mode = MODE_GESTURE; + /* return OK; */ + } else { + logError(1, "%s %s: enterGestureMode failed! ERROR %08X recovery in senseOff...\n", tag, __func__, res); + } + } +#endif + if (info->mode != MODE_GESTURE || info->gesture_enabled == 0) { + logError(0, "%s %s: Sense OFF!\n", tag, __func__); + res |= fts_command(info, FTS_CMD_MS_MT_SENSE_OFF); /* we need to use fts_command for speed reason (no need to check echo in this case and interrupt can be enabled) */ +#ifdef PHONE_KEY + logError(0, "%s %s: Key OFF!\n", tag, __func__); + res |= fts_command(info, FTS_CMD_MS_KEY_OFF); +#endif + + info->mode = MODE_SENSEOFF; + + } + setSystemResettedDown(0); + break; + + case 1: /* screen up */ + logError(0, "%s %s: Screen ON...\n", tag, __func__); + logError(0, "%s %s: Sense ON!\n", tag, __func__); + res |= fts_command(info, FTS_CMD_MS_MT_SENSE_ON); +#ifdef PHONE_KEY + logError(0, "%s %s: Key ON!\n", tag, __func__); + res |= fts_command(info, FTS_CMD_MS_KEY_ON); +#endif + info->mode = MODE_NORMAL; + + if (info->glove_enabled == FEAT_ENABLE || force == 1) { + if (isSystemResettedUp() || force == 1) { + logError(0, "%s %s: Glove Mode setting...\n", tag, __func__); + ret = featureEnableDisable(info->glove_enabled, FEAT_GLOVE); + if (ret < OK) { + logError(1, "%s %s: error during setting GLOVE_MODE! ERROR %08X\n", tag, __func__, ret); + } + res |= ret; + } + if (ret >= OK && info->glove_enabled == FEAT_ENABLE) { + info->mode = MODE_GLOVE; + logError(1, "%s %s: GLOVE_MODE Enabled!\n", tag, __func__); + } else{ + logError(1, "%s %s: GLOVE_MODE Disabled!\n", tag, __func__); + } + } + + setSystemResettedUp(0); + break; + + default: + logError(1, "%s %s: invalid resume_bit value = %d! ERROR %08X\n", tag, __func__, info->resume_bit, ERROR_OP_NOT_ALLOW); + res = ERROR_OP_NOT_ALLOW; + } + + logError(0, "%s %s: Mode Handler finished! res = %08X\n", tag, __func__, res); + return res; + +} + +static int fts_fb_state_chg_callback(struct notifier_block *nb, unsigned long val, void *data) +{ + struct fts_ts_info *info = container_of(nb, struct fts_ts_info, notifier); + struct fb_event *evdata = data; + int i; + unsigned int blank; + + if (val != FB_EVENT_BLANK) + return 0; + + logError(0, "%s %s: fts notifier begin!\n", tag, __func__); + + if (evdata && evdata->data && val == FB_EVENT_BLANK && info) { + + blank = *(int *) (evdata->data); + + switch (blank) { + case FB_BLANK_POWERDOWN: + if (info->sensor_sleep) + break; + + logError(0, "%s %s: FB_BLANK_POWERDOWN\n", tag, __func__); + + /* Release all slots */ + for (i = 0; i < TOUCH_ID_MAX; i++) { + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, + (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); + } + input_sync(info->input_dev); + + info->resume_bit = 0; + + fts_mode_handler(info, 0); + + info->sensor_sleep = true; + + fts_disableInterrupt(); + + break; + + case FB_BLANK_UNBLANK: + if (!info->sensor_sleep) + break; + + logError(0, "%s %s: FB_BLANK_UNBLANK\n", tag, __func__); + + for (i = 0; i < TOUCH_ID_MAX; i++) { + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, + (i < FINGER_MAX) ? MT_TOOL_FINGER : MT_TOOL_PEN, 0); + } + input_sync(info->input_dev); + + info->resume_bit = 1; + + fts_mode_handler(info, 0); + + info->sensor_sleep = false; + + fts_enableInterrupt(); + break; + default: + break; + + } + } + return NOTIFY_OK; + +} + +static struct notifier_block fts_noti_block = { + .notifier_call = fts_fb_state_chg_callback, +}; + +static int fts_get_reg(struct fts_ts_info *rmi4_data, + bool get) { + int retval; + const struct fts_i2c_platform_data *bdata = + rmi4_data->bdata; + + if (!get) { + retval = 0; + goto regulator_put; + } + + if ((bdata->pwr_reg_name != NULL) && (*bdata->pwr_reg_name != 0)) { + rmi4_data->pwr_reg = regulator_get(rmi4_data->dev, + bdata->pwr_reg_name); + if (IS_ERR(rmi4_data->pwr_reg)) { + logError(1, "%s %s: Failed to get power regulator\n", tag, + __func__); + retval = PTR_ERR(rmi4_data->pwr_reg); + goto regulator_put; + } + } + + if ((bdata->bus_reg_name != NULL) && (*bdata->bus_reg_name != 0)) { + rmi4_data->bus_reg = regulator_get(rmi4_data->dev, + bdata->bus_reg_name); + if (IS_ERR(rmi4_data->bus_reg)) { + logError(1, "%s %s: Failed to get bus pullup regulator\n", tag, + __func__); + retval = PTR_ERR(rmi4_data->bus_reg); + goto regulator_put; + } + } + + return 0; + +regulator_put: + if (rmi4_data->pwr_reg) { + regulator_put(rmi4_data->pwr_reg); + rmi4_data->pwr_reg = NULL; + } + + if (rmi4_data->bus_reg) { + regulator_put(rmi4_data->bus_reg); + rmi4_data->bus_reg = NULL; + } + + return retval; +} + +static int fts_enable_reg(struct fts_ts_info *rmi4_data, + bool enable) { + int retval; + + if (!enable) { + retval = 0; + goto disable_pwr_reg; + } + + if (rmi4_data->bus_reg) { + retval = regulator_enable(rmi4_data->bus_reg); + if (retval < 0) { + logError(1, "%s %s: Failed to enable bus regulator\n", tag, + __func__); + goto exit; + } + } + + if (rmi4_data->pwr_reg) { + retval = regulator_enable(rmi4_data->pwr_reg); + if (retval < 0) { + logError(1, "%s %s: Failed to enable power regulator\n", tag, + __func__); + goto disable_bus_reg; + } + } + + return OK; + +disable_pwr_reg: + if (rmi4_data->pwr_reg) + regulator_disable(rmi4_data->pwr_reg); + +disable_bus_reg: + if (rmi4_data->bus_reg) + regulator_disable(rmi4_data->bus_reg); + +exit: + return retval; +} + +static int fts_gpio_setup(int gpio, bool config, int dir, int state) +{ + int retval = 0; + unsigned char buf[16]; + + if (config) { + snprintf(buf, 16, "fts_gpio_%u\n", gpio); + + retval = gpio_request(gpio, buf); + if (retval) { + logError(1, "%s %s: Failed to get gpio %d (code: %d)", tag, + __func__, gpio, retval); + return retval; + } + + if (dir == 0) + retval = gpio_direction_input(gpio); + else + retval = gpio_direction_output(gpio, state); + if (retval) { + logError(1, "%s %s: Failed to set gpio %d direction", tag, + __func__, gpio); + return retval; + } + } else { + gpio_free(gpio); + } + + return retval; +} + +static int fts_set_gpio(struct fts_ts_info *rmi4_data) +{ + int retval; + const struct fts_i2c_platform_data *bdata = + rmi4_data->bdata; + + retval = fts_gpio_setup(bdata->irq_gpio, true, 0, 0); + if (retval < 0) { + logError(1, "%s %s: Failed to configure irq GPIO\n", tag, __func__); + goto err_gpio_irq; + } + + if (bdata->reset_gpio >= 0) { + retval = fts_gpio_setup(bdata->reset_gpio, true, 1, 0); + if (retval < 0) { + logError(1, "%s %s: Failed to configure reset GPIO\n", tag, __func__); + goto err_gpio_reset; + } + } + if (bdata->reset_gpio >= 0) { + gpio_set_value(bdata->reset_gpio, 0); + msleep(10); + gpio_set_value(bdata->reset_gpio, 1); + } + + setResetGpio(bdata->reset_gpio); + return OK; + +err_gpio_reset: + fts_gpio_setup(bdata->irq_gpio, false, 0, 0); + setResetGpio(GPIO_NOT_DEFINED); +err_gpio_irq: + return retval; +} + +static int parse_dt(struct device *dev, struct fts_i2c_platform_data *bdata) +{ + int retval; + const char *name; + struct device_node *np = dev->of_node; + + bdata->irq_gpio = of_get_named_gpio_flags(np, + "st,irq-gpio", 0, NULL); + + logError(0, "%s irq_gpio = %d\n", tag, bdata->irq_gpio); + + retval = of_property_read_string(np, "st,regulator_dvdd", &name); + if (retval == -EINVAL) + bdata->pwr_reg_name = NULL; + else if (retval < 0) + return retval; + bdata->pwr_reg_name = name; + logError(0, "%s pwr_reg_name = %s\n", tag, name); + + retval = of_property_read_string(np, "st,regulator_avdd", &name); + if (retval == -EINVAL) + bdata->bus_reg_name = NULL; + else if (retval < 0) + return retval; + bdata->bus_reg_name = name; + logError(0, "%s bus_reg_name = %s\n", tag, name); + + if (of_property_read_bool(np, "st,reset-gpio")) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "st,reset-gpio", 0, NULL); + logError(0, "%s reset_gpio =%d\n", tag, bdata->reset_gpio); + } else { + bdata->reset_gpio = GPIO_NOT_DEFINED; + } + + return OK; +} + +static int fts_probe(struct i2c_client *client, + const struct i2c_device_id *idp) { + struct fts_ts_info *info = NULL; + char fts_ts_phys[64]; + int error = 0; + struct device_node *dp = client->dev.of_node; + int retval; + + logError(1, "%s %s: driver probe begin!\n", tag, __func__); + + logError(1, "%s SET I2C Functionality and Dev INFO:\n", tag); + openChannel(client); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + logError(1, "%s Unsupported I2C functionality\n", tag); + error = -EIO; + goto ProbeErrorExit_0; + } + + info = kzalloc(sizeof (struct fts_ts_info), GFP_KERNEL); + if (!info) { + logError(1, "%s Out of memory... Impossible to allocate struct info!\n", tag); + error = -ENOMEM; + goto ProbeErrorExit_0; + } + + info->client = client; + + i2c_set_clientdata(client, info); + logError(1, "%s i2c address: %x\n", tag, client->addr); + info->dev = &info->client->dev; + if (dp) { + info->bdata = devm_kzalloc(&client->dev, sizeof (struct fts_i2c_platform_data), GFP_KERNEL); + if (!info->bdata) { + logError(1, "%s ERROR:info.bdata kzalloc failed\n", tag); + goto ProbeErrorExit_1; + } + parse_dt(&client->dev, info->bdata); + } + + logError(1, "%s SET Regulators:\n", tag); + retval = fts_get_reg(info, true); + if (retval < 0) { + logError(1, "%s ERROR: %s: Failed to get regulators\n", tag, __func__); + goto ProbeErrorExit_1; + } + + retval = fts_enable_reg(info, true); + if (retval < 0) { + logError(1, "%s %s: ERROR Failed to enable regulators\n", tag, __func__); + goto ProbeErrorExit_2; + } + + logError(1, "%s SET GPIOS:\n", tag); + retval = fts_set_gpio(info); + if (retval < 0) { + logError(1, "%s %s: ERROR Failed to set up GPIO's\n", tag, __func__); + goto ProbeErrorExit_2; + } + info->client->irq = gpio_to_irq(info->bdata->irq_gpio); + + logError(1, "%s SET Auto Fw Update:\n", tag); + info->fwu_workqueue = create_singlethread_workqueue("fts-fwu-queue"); + if (!info->fwu_workqueue) { + logError(1, "%s ERROR: Cannot create fwu work thread\n", tag); + goto ProbeErrorExit_3; + } + INIT_DELAYED_WORK(&info->fwu_work, fts_fw_update_auto); + + logError(1, "%s SET Event Handler:\n", tag); + info->event_wq = create_singlethread_workqueue("fts-event-queue"); + if (!info->event_wq) { + logError(1, "%s ERROR: Cannot create work thread\n", tag); + error = -ENOMEM; + goto ProbeErrorExit_4; + } + + INIT_WORK(&info->work, fts_event_handler); + + logError(1, "%s SET Input Device Property:\n", tag); + info->dev = &info->client->dev; + info->input_dev = input_allocate_device(); + if (!info->input_dev) { + logError(1, "%s ERROR: No such input device defined!\n", tag); + error = -ENODEV; + goto ProbeErrorExit_5; + } + info->input_dev->dev.parent = &client->dev; + info->input_dev->name = FTS_TS_DRV_NAME; + snprintf(fts_ts_phys, sizeof (fts_ts_phys), "%s/input0", + info->input_dev->name); + info->input_dev->phys = fts_ts_phys; + info->input_dev->id.bustype = BUS_I2C; + info->input_dev->id.vendor = 0x0001; + info->input_dev->id.product = 0x0002; + info->input_dev->id.version = 0x0100; + + __set_bit(EV_SYN, info->input_dev->evbit); + __set_bit(EV_KEY, info->input_dev->evbit); + __set_bit(EV_ABS, info->input_dev->evbit); + __set_bit(BTN_TOUCH, info->input_dev->keybit); + __set_bit(BTN_TOOL_FINGER, info->input_dev->keybit); + + input_mt_init_slots(info->input_dev, TOUCH_ID_MAX, INPUT_MT_DIRECT); + + /* input_mt_init_slots(info->input_dev, TOUCH_ID_MAX); */ + + /* input_set_abs_params(info->input_dev, ABS_MT_TRACKING_ID, 0, FINGER_MAX, 0, 0); */ + input_set_abs_params(info->input_dev, ABS_MT_POSITION_X, + X_AXIS_MIN, X_AXIS_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_POSITION_Y, + Y_AXIS_MIN, Y_AXIS_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MAJOR, + AREA_MIN, AREA_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MINOR, + AREA_MIN, AREA_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_PRESSURE, + PRESSURE_MIN, PRESSURE_MAX, 0, 0); + +#ifdef PHONE_GESTURE + input_set_capability(info->input_dev, EV_KEY, KEY_WAKEUP); + + input_set_capability(info->input_dev, EV_KEY, KEY_M); + input_set_capability(info->input_dev, EV_KEY, KEY_O); + input_set_capability(info->input_dev, EV_KEY, KEY_E); + input_set_capability(info->input_dev, EV_KEY, KEY_W); + input_set_capability(info->input_dev, EV_KEY, KEY_C); + input_set_capability(info->input_dev, EV_KEY, KEY_L); + input_set_capability(info->input_dev, EV_KEY, KEY_F); + input_set_capability(info->input_dev, EV_KEY, KEY_V); + input_set_capability(info->input_dev, EV_KEY, KEY_S); + input_set_capability(info->input_dev, EV_KEY, KEY_Z); + input_set_capability(info->input_dev, EV_KEY, KEY_WWW); + + input_set_capability(info->input_dev, EV_KEY, KEY_LEFT); + input_set_capability(info->input_dev, EV_KEY, KEY_RIGHT); + input_set_capability(info->input_dev, EV_KEY, KEY_UP); + input_set_capability(info->input_dev, EV_KEY, KEY_DOWN); + + input_set_capability(info->input_dev, EV_KEY, KEY_F1); + input_set_capability(info->input_dev, EV_KEY, KEY_F2); + input_set_capability(info->input_dev, EV_KEY, KEY_F3); + input_set_capability(info->input_dev, EV_KEY, KEY_F4); + input_set_capability(info->input_dev, EV_KEY, KEY_F5); + + input_set_capability(info->input_dev, EV_KEY, KEY_LEFTBRACE); + input_set_capability(info->input_dev, EV_KEY, KEY_RIGHTBRACE); +#endif + +#ifdef PHONE_KEY + /* KEY associated to the touch screen buttons */ + input_set_capability(info->input_dev, EV_KEY, KEY_HOMEPAGE); + input_set_capability(info->input_dev, EV_KEY, KEY_BACK); + input_set_capability(info->input_dev, EV_KEY, KEY_MENU); +#endif + + mutex_init(&(info->input_report_mutex)); + + /* register the multi-touch input device */ + error = input_register_device(info->input_dev); + if (error) { + logError(1, "%s ERROR: No such input device\n", tag); + error = -ENODEV; + goto ProbeErrorExit_5_1; + } + + /* track slots */ + info->touch_id = 0; + + /* init hardware device */ + logError(1, "%s Device Initialization:\n", tag); + error = fts_init(info); + if (error < OK) { + logError(1, "%s Cannot initialize the device ERROR %08X\n", tag, error); + error = -ENODEV; + goto ProbeErrorExit_6; + } + + info->gesture_enabled = 0; + info->glove_enabled = 0; + info->resume_bit = 1; + info->notifier = fts_noti_block; + error = fb_register_client(&info->notifier); + if (error) { + logError(1, "%s ERROR: register notifier failed!\n", tag); + goto ProbeErrorExit_6; + } + + logError(1, "%s SET Device File Nodes:\n", tag); + /* sysfs stuff */ + info->attrs.attrs = fts_attr_group; + error = sysfs_create_group(&client->dev.kobj, &info->attrs); + if (error) { + logError(1, "%s ERROR: Cannot create sysfs structure!\n", tag); + error = -ENODEV; + goto ProbeErrorExit_7; + } + +#ifdef SCRIPTLESS + /*I2C cmd*/ + if (fts_cmd_class == NULL) + fts_cmd_class = class_create(THIS_MODULE, FTS_TS_DRV_NAME); + info->i2c_cmd_dev = device_create(fts_cmd_class, + NULL, DCHIP_ID_0, info, "fts_i2c"); + if (IS_ERR(info->i2c_cmd_dev)) { + logError(1, "%s ERROR: Failed to create device for the sysfs!\n", tag); + goto ProbeErrorExit_8; + } + + dev_set_drvdata(info->i2c_cmd_dev, info); + + error = sysfs_create_group(&info->i2c_cmd_dev->kobj, + &i2c_cmd_attr_group); + if (error) { + logError(1, "%s ERROR: Failed to create sysfs group!\n", tag); + goto ProbeErrorExit_9; + } + +#endif + +#ifdef DRIVER_TEST + if (fts_cmd_class == NULL) + fts_cmd_class = class_create(THIS_MODULE, FTS_TS_DRV_NAME); + info->test_cmd_dev = device_create(fts_cmd_class, + NULL, DCHIP_ID_0, info, "fts_driver_test"); + if (IS_ERR(info->test_cmd_dev)) { + logError(1, "%s ERROR: Failed to create device for the sysfs!\n", tag); + goto ProbeErrorExit_10; + } + + dev_set_drvdata(info->test_cmd_dev, info); + + error = sysfs_create_group(&info->test_cmd_dev->kobj, + &test_cmd_attr_group); + if (error) { + logError(1, "%s ERROR: Failed to create sysfs group!\n", tag); + goto ProbeErrorExit_11; + } + +#endif + /*if wanna auto-update FW when probe, + * please don't comment the following code + */ + + /* queue_delayed_work(info->fwu_workqueue, &info->fwu_work, + * msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + */ + logError(1, "%s Probe Finished!\n", tag); + return OK; + + /* error exit path */ +#ifdef DRIVER_TEST +ProbeErrorExit_11: +#ifndef SCRIPTLESS + device_destroy(fts_cmd_class, DCHIP_ID_0); +#endif + +ProbeErrorExit_10: +#ifndef SCRIPTLESS + sysfs_remove_group(&client->dev.kobj, &info->attrs); +#endif +#endif + +#ifdef SCRIPTLESS +ProbeErrorExit_9: + device_destroy(fts_cmd_class, DCHIP_ID_0); + +ProbeErrorExit_8: + sysfs_remove_group(&client->dev.kobj, &info->attrs); +#endif + +ProbeErrorExit_7: + fb_unregister_client(&info->notifier); + +ProbeErrorExit_6: + input_unregister_device(info->input_dev); + +ProbeErrorExit_5_1: + /* intput_free_device(info->input_dev ); */ + + ProbeErrorExit_5: + destroy_workqueue(info->event_wq); + +ProbeErrorExit_4: + destroy_workqueue(info->fwu_workqueue); + +ProbeErrorExit_3: + fts_enable_reg(info, false); + +ProbeErrorExit_2: + fts_get_reg(info, false); + +ProbeErrorExit_1: + kfree(info); + +ProbeErrorExit_0: + logError(1, "%s Probe Failed!\n", tag); + + return error; +} + +static int fts_remove(struct i2c_client *client) +{ + struct fts_ts_info *info = i2c_get_clientdata(client); + +#ifdef DRIVER_TEST + sysfs_remove_group(&info->test_cmd_dev->kobj, + &test_cmd_attr_group); +#endif + +#ifdef SCRIPTLESS + /*I2C cmd*/ + sysfs_remove_group(&info->i2c_cmd_dev->kobj, + &i2c_cmd_attr_group); + +#endif + +#if defined(SCRIPTLESS) || defined(DRIVER_TEST) + device_destroy(fts_cmd_class, DCHIP_ID_0); +#endif + + /* sysfs stuff */ + sysfs_remove_group(&client->dev.kobj, &info->attrs); + + /* remove interrupt and event handlers */ + fts_interrupt_uninstall(info); + + fb_unregister_client(&info->notifier); + + /* unregister the device */ + input_unregister_device(info->input_dev); + + /* intput_free_device(info->input_dev ); */ + + /* Empty the FIFO buffer */ + fts_command(info, FIFO_CMD_FLUSH); + /* flushFIFO(); */ + + /* Remove the work thread */ + destroy_workqueue(info->event_wq); + destroy_workqueue(info->fwu_workqueue); + + fts_enable_reg(info, false); + fts_get_reg(info, false); + + /* free all */ + kfree(info); + + return OK; +} + +static struct of_device_id fts_of_match_table[] = { + { + .compatible = "st,fts", + }, + {}, +}; +static const struct i2c_device_id fts_device_id[] = { + {FTS_TS_DRV_NAME, 0}, + {} +}; + +static struct i2c_driver fts_i2c_driver = { + .driver = { + .name = FTS_TS_DRV_NAME, + .of_match_table = fts_of_match_table, + }, + .probe = fts_probe, + .remove = fts_remove, + .id_table = fts_device_id, +}; + +static int __init fts_driver_init(void) +{ + return i2c_add_driver(&fts_i2c_driver); +} + +static void __exit fts_driver_exit(void) +{ + i2c_del_driver(&fts_i2c_driver); +} + +MODULE_DESCRIPTION("STMicroelectronics MultiTouch IC Driver"); +MODULE_AUTHOR("STMicroelectronics, Inc."); +MODULE_LICENSE("GPL"); + +late_initcall(fts_driver_init); +module_exit(fts_driver_exit); diff --git a/drivers/input/touchscreen/st/fts.h b/drivers/input/touchscreen/st/fts.h new file mode 100644 index 000000000000..7d72d226349f --- /dev/null +++ b/drivers/input/touchscreen/st/fts.h @@ -0,0 +1,249 @@ +/* + * fts.c + * + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * marco.cali@st.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _LINUX_FTS_I2C_H_ +#define _LINUX_FTS_I2C_H_ + +#include "fts_lib/ftsSoftware.h" +#include "fts_lib/ftsHardware.h" + +#define FTS_POWER_ON 1 +#define FTS_POWER_OFF 0 + +/****************** CONFIGURATION SECTION ******************/ +/* #define PHONE_KEY */ + +/* #define PHONE_GESTURE */ + +#define SCRIPTLESS +#ifdef SCRIPTLESS +#define SCRIPTLESS_DEBUG +/* uncomment this macro definition to print debug +* message for script less support +*/ +#endif + +#define DRIVER_TEST + +#define FW_H_FILE +#ifdef FW_H_FILE +#define FW_SIZE_NAME myArray_size +#define FW_ARRAY_NAME myArray +#endif + +#define LIMITS_H_FILE +#ifdef LIMITS_H_FILE +#define LIMITS_SIZE_NAME myArray2_size +#define LIMITS_ARRAY_NAME myArray2 +#endif + +#define FTS_TS_DRV_NAME "fts" +#define FTS_TS_DRV_VERSION "4.1.0" + +#define X_AXIS_MAX 1440 +#define X_AXIS_MIN 0 +#define Y_AXIS_MAX 2560 +#define Y_AXIS_MIN 0 + +#define PRESSURE_MIN 0 +#define PRESSURE_MAX 127 + +#define FINGER_MAX 10 +#define STYLUS_MAX 1 +#define TOUCH_ID_MAX (FINGER_MAX + STYLUS_MAX) + +#define AREA_MIN PRESSURE_MIN +#define AREA_MAX PRESSURE_MAX + +/*********************************************************/ + +/* Flash programming */ + +#define INIT_FLAG_CNT 3 + +/* KEYS */ +#define KEY1 0x02 +#define KEY2 0x01 +#define KEY3 0x04 + +/* + * Configuration mode + */ +#define MODE_NORMAL 0 +#define MODE_GESTURE 1 +#define MODE_GLOVE 2 +#define MODE_SENSEOFF 3 + +/* + * Status Event Field: + * id of command that triggered the event + */ + +#define FTS_FLASH_WRITE_CONFIG 0x03 +#define FTS_FLASH_WRITE_COMP_MEMORY 0x04 +#define FTS_FORCE_CAL_SELF_MUTUAL 0x05 +#define FTS_FORCE_CAL_SELF 0x06 +#define FTS_WATER_MODE_ON 0x07 +#define FTS_WATER_MODE_OFF 0x08 + +#define EXP_FN_WORK_DELAY_MS 1000 + +#define CMD_STR_LEN 32 + +#ifdef SCRIPTLESS +/* + * I2C Command Read/Write Function + */ + +#define CMD_RESULT_STR_LEN 2048 +#endif + +#define TSP_BUF_SIZE 4096 + +struct fts_i2c_platform_data { + int (*power)(bool on); + int irq_gpio; + int reset_gpio; + const char *pwr_reg_name; + const char *bus_reg_name; + +}; + +/* + * Forward declaration + */ +struct fts_ts_info; +extern char tag[8]; + +/* + * Dispatch event handler + */ +typedef unsigned char * (*event_dispatch_handler_t) +(struct fts_ts_info *info, unsigned char *data); + +/* + * struct fts_ts_info - FTS capacitive touch screen device information + * @dev: Pointer to the structure device + * @client: I2C client structure + * @input_dev Input device structure + * @work Work thread + * @event_wq Event queue for work thread + * @cmd_done Asyncronous command notification + * @event_dispatch_table Event dispatch table handlers + * @fw_version Firmware version + * @attrs SysFS attributes + * @mode Device operating mode + * @touch_id Bitmask for touch id (mapped to input slots) + * @buttons Bitmask for buttons status + * @timer Timer when operating in polling mode + * @early_suspend Structure for early suspend functions + * @power Power on/off routine + */ + +struct fts_ts_info { + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; + + struct work_struct work; + struct workqueue_struct *event_wq; + + struct delayed_work fwu_work; + struct workqueue_struct *fwu_workqueue; + struct completion cmd_done; + + event_dispatch_handler_t *event_dispatch_table; + + unsigned int fw_version; + unsigned int config_id; + + struct attribute_group attrs; + + unsigned int mode; + unsigned long touch_id; + unsigned int buttons; + +#ifdef FTS_USE_POLLING_MODE + struct hrtimer timer; +#endif + +#ifdef SCRIPTLESS + /*I2C cmd*/ + struct device *i2c_cmd_dev; + char cmd_read_result[CMD_RESULT_STR_LEN]; + char cmd_wr_result[CMD_RESULT_STR_LEN]; + char cmd_write_result[20]; +#endif + +#ifdef DRIVER_TEST + struct device *test_cmd_dev; +#endif + + int (*power)(bool on); + + struct fts_i2c_platform_data *bdata; + struct regulator *pwr_reg; + struct regulator *bus_reg; + + bool fw_force; + int debug_enable; + + int resume_bit; + int fwupdate_stat; + int touch_debug; + + struct notifier_block notifier; + bool sensor_sleep; + bool stay_awake; + + /* input lock */ + struct mutex input_report_mutex; + + /* switches */ + int gesture_enabled; + int glove_enabled; + +}; + +typedef enum { + ERR_ITO_NO_ERR, /* < 0 No ITO Error */ + ERR_ITO_PANEL_OPEN_FORCE, /* < 1 Panel Open Force */ + ERR_ITO_PANEL_OPEN_SENSE, /* < 2 Panel Open Sense */ + ERR_ITO_F2G, /* < 3 Force short to ground */ + ERR_ITO_S2G, /* < 4 Sense short to ground */ + ERR_ITO_F2VDD, /* < 5 Force short to VDD */ + ERR_ITO_S2VDD, /* < 6 Sense short to VDD */ + ERR_ITO_P2P_FORCE, /* < 7 Pin to Pin short (Force) */ + ERR_ITO_P2P_SENSE, /* < 8 Pin to Pin short (Sense) */ +} errItoSubTypes_t; + +int fts_chip_powercycle(struct fts_ts_info *info); +int fts_chip_powercycle2(struct fts_ts_info *info, unsigned long sleep); +int fts_get_fw_version(struct fts_ts_info *info); +extern unsigned int le_to_uint(const unsigned char *ptr); +extern unsigned int be_to_uint(const unsigned char *ptr); +extern int input_register_notifier_client(struct notifier_block *nb); +extern int input_unregister_notifier_client(struct notifier_block *nb); + +#ifdef SCRIPTLESS +extern struct attribute_group i2c_cmd_attr_group; +#endif + +#ifdef DRIVER_TEST +extern struct attribute_group test_cmd_attr_group; +#endif + +#endif diff --git a/drivers/input/touchscreen/st/fts_driver_test.c b/drivers/input/touchscreen/st/fts_driver_test.c new file mode 100644 index 000000000000..5c55d8e4ac88 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_driver_test.c @@ -0,0 +1,871 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* API used by Driver Test Apk * +* * +************************************************************************** +************************************************************************** + +*/ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/hrtimer.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/completion.h> + +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> +#include "fts.h" +#include "fts_lib/ftsCompensation.h" +#include "fts_lib/ftsIO.h" +#include "fts_lib/ftsError.h" +#include "fts_lib/ftsFrame.h" +#include "fts_lib/ftsFlash.h" +#include "fts_lib/ftsTest.h" +#include "fts_lib/ftsTime.h" +#include "fts_lib/ftsTool.h" + +#ifdef DRIVER_TEST + +#define MAX_PARAMS 10 + +/* DEFINE COMMANDS TO TEST */ +#define CMD_READ 0x00 +#define CMD_WRITE 0x01 +#define CMD_READU16 0x02 +#define CMD_READB2 0x03 +#define CMD_READB2U16 0x04 +#define CMD_POLLFOREVENT 0x05 +#define CMD_SYSTEMRESET 0x06 +#define CMD_CLEANUP 0x07 +#define CMD_GETFORCELEN 0x08 +#define CMD_GETSENSELEN 0x09 +#define CMD_GETMSFRAME 0x0A +/* #define CMD_GETMSKEYFRAME 0x0B */ +#define CMD_GETSSFRAME 0x0C +#define CMD_REQCOMPDATA 0x0D +#define CMD_READCOMPDATAHEAD 0x0E +#define CMD_READMSCOMPDATA 0x0F +#define CMD_READSSCOMPDATA 0x10 +#define CMD_READGNCOMPDATA 0x11 +#define CMD_GETFWVER 0x12 +#define CMD_FLASHSTATUS 0x13 +#define CMD_FLASHUNLOCK 0x14 +#define CMD_READFWFILE 0x15 +#define CMD_FLASHPROCEDURE 0x16 +#define CMD_ITOTEST 0x17 +#define CMD_INITTEST 0x18 +#define CMD_MSRAWTEST 0x19 +#define CMD_MSINITDATATEST 0x1A +#define CMD_SSRAWTEST 0x1B +#define CMD_SSINITDATATEST 0x1C +#define CMD_MAINTEST 0x1D +#define CMD_POWERCYCLE 0x1E +#define CMD_FWWRITE 0x1F +#define CMD_READCHIPINFO 0x20 +#define CMD_REQFRAME 0x21 + +u32 *functionToTest; +int numberParam; + +static ssize_t stm_driver_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { + int n; + char *p = (char *) buf; + + functionToTest = (u32 *) kmalloc(MAX_PARAMS * sizeof (u32), GFP_KERNEL); + if (functionToTest == NULL) { + logError(1, "%s impossible to allocate functionToTest!\n", tag); + return count; + } + memset(functionToTest, 0, MAX_PARAMS * sizeof (u32)); + + for (n = 0; n < (count + 1) / 3 && n < MAX_PARAMS; n++) { + sscanf(p, "%02X ", &functionToTest[n]); + p += 3; + logError(1, "%s functionToTest[%d] = %02X\n", tag, n, functionToTest[n]); + + } + + numberParam = n; + logError(1, "%s Number of Parameters = %d\n", tag, numberParam); + return count; +} + +static ssize_t stm_driver_test_show(struct device *dev, struct device_attribute *attr, + char *buf) { + char buff[CMD_STR_LEN] = {0}; + int res = -1, j, count; + /* int res2; */ + int size = 6 * 2; + int temp, i, byteToRead; + u8 *readData = NULL; + u8 *all_strbuff = NULL; + u8 *cmd; + + MutualSenseFrame frameMS; + SelfSenseFrame frameSS; + + DataHeader dataHead; + MutualSenseData compData; + SelfSenseData comData; + GeneralData gnData; + + u16 address; + u16 fw_version; + u16 config_id; + + Firmware fw; + + /* struct used for defining which test perform during the MP test */ + + TestToDo todoDefault; + + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + fw.data = NULL; + + todoDefault.MutualRaw = 1; + todoDefault.MutualRawGap = 1; + todoDefault.MutualCx1 = 0; + todoDefault.MutualCx2 = 1; + todoDefault.MutualCx2Adj = 1; + todoDefault.MutualCxTotal = 0; + todoDefault.MutualCxTotalAdj = 0; + + todoDefault.MutualKeyRaw = 0; + todoDefault.MutualKeyCx1 = 0; + todoDefault.MutualKeyCx2 = 0; + todoDefault.MutualKeyCxTotal = 0; + + todoDefault.SelfForceRaw = 1; + todoDefault.SelfForceRawGap = 0; + todoDefault.SelfForceIx1 = 0; + todoDefault.SelfForceIx2 = 0; + todoDefault.SelfForceIx2Adj = 0; + todoDefault.SelfForceIxTotal = 1; + todoDefault.SelfForceIxTotalAdj = 0; + todoDefault.SelfForceCx1 = 0; + todoDefault.SelfForceCx2 = 0; + todoDefault.SelfForceCx2Adj = 0; + todoDefault.SelfForceCxTotal = 0; + todoDefault.SelfForceCxTotalAdj = 0; + + todoDefault.SelfSenseRaw = 1; + todoDefault.SelfSenseRawGap = 0; + todoDefault.SelfSenseIx1 = 0; + todoDefault.SelfSenseIx2 = 0; + todoDefault.SelfSenseIx2Adj = 0; + todoDefault.SelfSenseIxTotal = 1; + todoDefault.SelfSenseIxTotalAdj = 0; + todoDefault.SelfSenseCx1 = 0; + todoDefault.SelfSenseCx2 = 0; + todoDefault.SelfSenseCx2Adj = 0; + todoDefault.SelfSenseCxTotal = 0; + todoDefault.SelfSenseCxTotalAdj = 0; + + if (numberParam >= 1 && functionToTest != NULL) { + res = fts_disableInterrupt(); + if (res < 0) { + logError(0, "%s stm_driver_test_show: ERROR %08X\n", tag, res); + res = (res | ERROR_DISABLE_INTER); + goto END; + } + switch (functionToTest[0]) { + case CMD_READ: + if (numberParam >= 4) { + temp = (int) functionToTest[1]; + if (numberParam == 4 + (temp - 1) && temp != 0) { + cmd = (u8 *) kmalloc(temp * sizeof (u8), GFP_KERNEL); + for (i = 0; i < temp; i++) { + cmd[i] = functionToTest[i + 2]; + } + byteToRead = functionToTest[i + 2]; + readData = (u8 *) kmalloc(byteToRead * sizeof (u8), GFP_KERNEL); + res = fts_readCmd(cmd, temp, readData, byteToRead); + size += (byteToRead * sizeof (u8))*2; + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_WRITE: + if (numberParam >= 3) { /* need to pass: cmdLength cmd[0] cmd[1] … cmd[cmdLength-1] */ + temp = (int) functionToTest[1]; + if (numberParam == 3 + (temp - 1) && temp != 0) { + cmd = (u8 *) kmalloc(temp * sizeof (u8), GFP_KERNEL); + for (i = 0; i < temp; i++) { + cmd[i] = functionToTest[i + 2]; + } + res = fts_writeCmd(cmd, temp); + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_FWWRITE: + if (numberParam >= 3) { /* need to pass: cmdLength cmd[0] cmd[1] … cmd[cmdLength-1] */ + temp = (int) functionToTest[1]; + if (numberParam == 3 + (temp - 1) && temp != 0) { + cmd = (u8 *) kmalloc(temp * sizeof (u8), GFP_KERNEL); + for (i = 0; i < temp; i++) { + cmd[i] = functionToTest[i + 2]; + } + res = fts_writeFwCmd(cmd, temp); + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READU16: + if (numberParam == 6) { /* need to pass: cmd addr[0] addr[1] byteToRead hasDummyByte */ + byteToRead = functionToTest[4]; + readData = (u8 *) kmalloc(byteToRead * sizeof (u8), GFP_KERNEL); + res = readCmdU16((u8) functionToTest[1], (u16) ((((u8) functionToTest[2] & 0x00FF) << 8) + ((u8) functionToTest[3] & 0x00FF)), readData, byteToRead, functionToTest[5]); + size += (byteToRead * sizeof (u8))*2; + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READB2: + if (numberParam == 4) { /* need to pass: addr[0] addr[1] byteToRead */ + byteToRead = functionToTest[3]; + readData = (u8 *) kmalloc(byteToRead * sizeof (u8), GFP_KERNEL); + res = readB2((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), readData, byteToRead); + size += (byteToRead * sizeof (u8))*2; + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READB2U16: + if (numberParam == 4) { /* need to pass: addr[0] addr[1] byteToRead */ + byteToRead = functionToTest[3]; + readData = (u8 *) kmalloc(byteToRead * sizeof (u8), GFP_KERNEL); + res = readB2U16((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), readData, byteToRead); + size += (byteToRead * sizeof (u8))*2; + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_POLLFOREVENT: + if (numberParam >= 5) { /* need to pass: eventLength event[0] event[1] event[eventLength-1] timeTowait */ + temp = (int) functionToTest[1]; + if (numberParam == 5 + (temp - 1) && temp != 0) { + readData = (u8 *) kmalloc(FIFO_EVENT_SIZE * sizeof (u8), GFP_KERNEL); + res = pollForEvent((int *) &functionToTest[2], temp, readData, ((functionToTest[temp + 2] & 0x00FF) << 8)+(functionToTest[temp + 3] & 0x00FF)); + if (res >= OK) + res = OK; /* pollForEvent return the number of error found */ + size += (FIFO_EVENT_SIZE * sizeof (u8))*2; + byteToRead = FIFO_EVENT_SIZE; + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_SYSTEMRESET: + res = fts_system_reset(); + + break; + + case CMD_READCHIPINFO: + if (numberParam == 2) { /* need to pass: doRequest */ + res = readChipInfo(functionToTest[1]); + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + + break; + + case CMD_CLEANUP: /* TOUCH ENABLE/DISABLE */ + if (numberParam == 2) { /* need to pass: enableTouch */ + res = cleanUp(functionToTest[1]); + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + + break; + + case CMD_GETFORCELEN: /* read number Tx channels */ + temp = getForceLen(); + if (temp < OK) + res = temp; + else { + size += (1 * sizeof (u8))*2; + res = OK; + } + break; + + case CMD_GETSENSELEN: /* read number Rx channels */ + temp = getSenseLen(); + if (temp < OK) + res = temp; + else { + size += (1 * sizeof (u8))*2; + res = OK; + } + break; + + case CMD_REQFRAME: /* request a frame */ + if (numberParam == 3) { + logError(0, "%s Requesting Frame\n", tag); + res = requestFrame((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF))); + + if (res < OK) { + logError(0, "%s Error requesting frame ERROR %02X\n", tag, res); + } else { + logError(0, "%s Requesting Frame Finished!\n", tag); + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_GETMSFRAME: + if (numberParam == 3) { + logError(0, "%s Get 1 MS Frame\n", tag); + flushFIFO(); /* delete the events related to some touch (allow to call this function while touching the sreen without having a flooding of the FIFO) */ + res = getMSFrame2((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &frameMS); + if (res < 0) { + logError(0, "%s Error while taking the MS frame... ERROR %02X\n", tag, res); + + } else { + logError(0, "%s The frame size is %d words\n", tag, res); + size = (res * sizeof (short) + 8)*2; + /* set res to OK because if getMSFrame is + successful res = number of words read + */ + res = OK; + print_frame_short("MS frame =", array1dTo2d_short(frameMS.node_data, frameMS.node_data_size, frameMS.header.sense_node), frameMS.header.force_node, frameMS.header.sense_node); + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + /*read self raw*/ + case CMD_GETSSFRAME: + if (numberParam == 3) { + logError(0, "%s Get 1 SS Frame\n", tag); + flushFIFO(); /* delete the events related to some touch (allow to call this function while touching the sreen without having a flooding of the FIFO) */ + res = getSSFrame2((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &frameSS); + + if (res < OK) { + logError(0, "%s Error while taking the SS frame... ERROR %02X\n", tag, res); + + } else { + logError(0, "%s The frame size is %d words\n", tag, res); + size = (res * sizeof (short) + 8)*2+1; + /* set res to OK because if getMSFrame is + successful res = number of words read + */ + res = OK; + print_frame_short("SS force frame =", array1dTo2d_short(frameSS.force_data, frameSS.header.force_node, 1), frameSS.header.force_node, 1); + print_frame_short("SS sense frame =", array1dTo2d_short(frameSS.sense_data, frameSS.header.sense_node, frameSS.header.sense_node), 1, frameSS.header.sense_node); + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_REQCOMPDATA: /* request comp data */ + if (numberParam == 3) { + logError(0, "%s Requesting Compensation Data\n", tag); + res = requestCompensationData((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF))); + + if (res < OK) { + logError(0, "%s Error requesting compensation data ERROR %02X\n", tag, res); + } else { + logError(0, "%s Requesting Compensation Data Finished!\n", tag); + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READCOMPDATAHEAD: /* read comp data header */ + if (numberParam == 3) { + logError(0, "%s Requesting Compensation Data\n", tag); + res = requestCompensationData((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF))); + if (res < OK) { + logError(0, "%s Error requesting compensation data ERROR %02X\n", tag, res); + } else { + logError(0, "%s Requesting Compensation Data Finished!\n", tag); + res = readCompensationDataHeader((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &dataHead, &address); + if (res < OK) { + logError(0, "%s Read Compensation Data Header ERROR %02X\n", tag, res); + } else { + logError(0, "%s Read Compensation Data Header OK!\n", tag); + size += (2 * sizeof (u8))*2; + } + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READMSCOMPDATA: /* read mutual comp data */ + if (numberParam == 3) { + logError(0, "%s Get MS Compensation Data\n", tag); + res = readMutualSenseCompensationData((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &compData); + + if (res < OK) { + logError(0, "%s Error reading MS compensation data ERROR %02X\n", tag, res); + } else { + logError(0, "%s MS Compensation Data Reading Finished!\n", tag); + size = ((compData.node_data_size + 9) * sizeof (u8))*2; + print_frame_u8("MS Data (Cx2) =", array1dTo2d_u8(compData.node_data, compData.node_data_size, compData.header.sense_node), compData.header.force_node, compData.header.sense_node); + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READSSCOMPDATA: + if (numberParam == 3) { /* read self comp data */ + logError(0, "%s Get SS Compensation Data...\n", tag); + res = readSelfSenseCompensationData((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &comData); + if (res < OK) { + logError(0, "%s Error reading SS compensation data ERROR %02X\n", tag, res); + } else { + logError(0, "%s SS Compensation Data Reading Finished!\n", tag); + size = ((comData.header.force_node + comData.header.sense_node)*2 + 12) * sizeof (u8)*2; + print_frame_u8("SS Data Ix2_fm = ", array1dTo2d_u8(comData.ix2_fm, comData.header.force_node, comData.header.force_node), 1, comData.header.force_node); + print_frame_u8("SS Data Cx2_fm = ", array1dTo2d_u8(comData.cx2_fm, comData.header.force_node, comData.header.force_node), 1, comData.header.force_node); + print_frame_u8("SS Data Ix2_sn = ", array1dTo2d_u8(comData.ix2_sn, comData.header.sense_node, comData.header.sense_node), 1, comData.header.sense_node); + print_frame_u8("SS Data Cx2_sn = ", array1dTo2d_u8(comData.cx2_sn, comData.header.sense_node, comData.header.sense_node), 1, comData.header.sense_node); + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READGNCOMPDATA: + if (numberParam == 3) { /* read self comp data */ + logError(0, "%s Get General Compensation Data...\n", tag); + res = readGeneralCompensationData((u16) ((((u8) functionToTest[1] & 0x00FF) << 8) + ((u8) functionToTest[2] & 0x00FF)), &gnData); + if (res < OK) { + logError(0, "%s Error reading General compensation data ERROR %02X\n", tag, res); + } else { + logError(0, "%s General Compensation Data Reading Finished!\n", tag); + size = (14) * sizeof (u8)*2; + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_GETFWVER: + res = getFirmwareVersion(&fw_version, &config_id); + if (res < OK) { + logError(1, "%s Error reading firmware version and config id ERROR %02X\n", tag, res); + } else { + logError(0, "%s getFirmwareVersion Finished!\n", tag); + size += (4) * sizeof (u8)*2; + } + break; +#ifdef FTM3_CHIP + case CMD_FLASHSTATUS: + res = flash_status(); /* return 0 = flash ready, 1 = flash busy, <0 error */ + if (res < OK) { + logError(1, "%s Error reading flash status ERROR %02X\n", tag, res); + } else { + logError(0, "%s Flash Status: %d\n", tag, res); + size += (1 * sizeof (u8))*2; + temp = res; /* need to store the value for further display */ + res = OK; /* set res =ok for returning code */ + } + break; +#endif + + case CMD_FLASHUNLOCK: + res = flash_unlock(); + if (res < OK) { + logError(1, "%s Impossible Unlock Flash ERROR %02X\n", tag, res); + } else { + logError(0, "%s Flash Unlock OK!\n", tag); + } + break; + + case CMD_READFWFILE: + if (numberParam == 2) { /* read fw file */ + logError(0, "%s Reading FW File...\n", tag); + res = readFwFile(PATH_FILE_FW, &fw, functionToTest[1]); + if (res < OK) { + logError(0, "%s Error reading FW File ERROR %02X\n", tag, res); + } else { + logError(0, "%s Read FW File Finished!\n", tag); + } + kfree(fw.data); + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_FLASHPROCEDURE: + if (numberParam == 3) { /* flashing procedure */ + logError(0, "%s Starting Flashing Procedure...\n", tag); + res = flashProcedure(PATH_FILE_FW, functionToTest[1], functionToTest[2]); + if (res < OK) { + logError(0, "%s Error during flash procedure ERROR %02X\n", tag, res); + } else { + logError(0, "%s Flash Procedure Finished!\n", tag); + } + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + /*ITO TEST*/ + case CMD_ITOTEST: + res = production_test_ito(); + break; + + /*Initialization*/ + case CMD_INITTEST: + if (numberParam == 2) { /* need to specify if if save value on Flash */ + if (functionToTest[1] == 0x01) + res = production_test_initialization(); + else + res = production_test_splited_initialization(false); + } else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_MSRAWTEST: /* MS Raw DATA TEST */ + if (numberParam == 2) /* need to specify if stopOnFail */ + res = production_test_ms_raw(LIMITS_FILE, functionToTest[1], &todoDefault); + else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_MSINITDATATEST: /* MS CX DATA TEST */ + if (numberParam == 2) /* need to specify if stopOnFail */ + res = production_test_ms_cx(LIMITS_FILE, functionToTest[1], &todoDefault); + else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_SSRAWTEST: /* SS RAW DATA TEST */ + if (numberParam == 2) /* need to specify if stopOnFail */ + res = production_test_ss_raw(LIMITS_FILE, functionToTest[1], &todoDefault); + else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_SSINITDATATEST: /* SS IX CX DATA TEST */ + if (numberParam == 2) /* need to specify if stopOnFail */ + res = production_test_ss_ix_cx(LIMITS_FILE, functionToTest[1], &todoDefault); + else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + /*PRODUCTION TEST*/ + case CMD_MAINTEST: + if (numberParam == 3) /* need to specify if stopOnFail and saveInit */ + res = production_test_main(LIMITS_FILE, functionToTest[1], functionToTest[2], &todoDefault, INIT_FIELD); + else { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_POWERCYCLE: + res = fts_chip_powercycle(info); + break; + + default: + logError(1, "%s COMMAND ID NOT VALID!! Inset a value between 00 and 1E..\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + /*res2 = fts_enableInterrupt(); enabling the interrupt was disabled on purpose in this node because it can be used for testing procedure and between one step and another the interrupt wan to be kept disabled + if (res2 < 0) { + logError(0, "%s stm_driver_test_show: ERROR %08X\n", tag, (res2 | ERROR_ENABLE_INTER)); + }*/ + } else { + logError(1, "%s NO COMMAND SPECIFIED!!! do: 'echo [cmd_code] [args] > stm_fts_cmd' before looking for result!\n", tag); + res = ERROR_OP_NOT_ALLOW; + functionToTest = NULL; + } + +END: /* here start the reporting phase, assembling the data to send in the file node */ + all_strbuff = (u8 *) kmalloc(size, GFP_KERNEL); + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof (buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + if (res >= OK) { + /*all the other cases are already fine printing only the res.*/ + switch (functionToTest[0]) { + case CMD_READ: + case CMD_READU16: + case CMD_READB2: + case CMD_READB2U16: + case CMD_POLLFOREVENT: + for (j = 0; j < byteToRead; j++) { + snprintf(buff, sizeof (buff), "%02X", readData[j]); + strlcat(all_strbuff, buff, size); + } + break; + + case CMD_GETFORCELEN: + case CMD_GETSENSELEN: + case CMD_FLASHSTATUS: + snprintf(buff, sizeof (buff), "%02X", (u8) temp); + strlcat(all_strbuff, buff, size); + break; + + case CMD_GETMSFRAME: + snprintf(buff, sizeof (buff), "%02X", (u8) frameMS.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", (u8) frameMS.header.sense_node); + strlcat(all_strbuff, buff, size); + + for (j = 0; j < frameMS.node_data_size; j++) { + snprintf(buff, sizeof (buff), "%04X", frameMS.node_data[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(frameMS.node_data); + break; + + case CMD_GETSSFRAME: + snprintf(buff, sizeof (buff), "%02X", (u8) frameSS.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", (u8) frameSS.header.sense_node); + strlcat(all_strbuff, buff, size); + + /* Copying self raw data Force */ + for (j = 0; j < frameSS.header.force_node; j++) { + snprintf(buff, sizeof (buff), "%04X", frameSS.force_data[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying self raw data Sense */ + for (j = 0; j < frameSS.header.sense_node; j++) { + snprintf(buff, sizeof (buff), "%04X", frameSS.sense_data[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(frameSS.force_data); + kfree(frameSS.sense_data); + break; + + case CMD_READMSCOMPDATA: + snprintf(buff, sizeof (buff), "%02X", (u8) compData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", (u8) compData.header.sense_node); + strlcat(all_strbuff, buff, size); + + /* Cpying CX1 value */ + snprintf(buff, sizeof (buff), "%02X", compData.cx1); + strlcat(all_strbuff, buff, size); + + /* Copying CX2 values */ + for (j = 0; j < compData.node_data_size; j++) { + snprintf(buff, sizeof (buff), "%02X", *(compData.node_data + j)); + strlcat(all_strbuff, buff, size); + } + + kfree(compData.node_data); + break; + + case CMD_READSSCOMPDATA: + snprintf(buff, sizeof (buff), "%02X", comData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", comData.header.sense_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", comData.f_ix1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", comData.s_ix1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", comData.f_cx1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", comData.s_cx1); + strlcat(all_strbuff, buff, size); + + /* Copying IX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof (buff), "%02X", comData.ix2_fm[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying IX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof (buff), "%02X", comData.ix2_sn[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying CX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof (buff), "%02X", comData.cx2_fm[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying CX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof (buff), "%02X", comData.cx2_sn[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(comData.ix2_fm); + kfree(comData.ix2_sn); + kfree(comData.cx2_fm); + kfree(comData.cx2_sn); + break; + + case CMD_READGNCOMPDATA: + snprintf(buff, sizeof (buff), "%02X", gnData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", gnData.header.sense_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", gnData.ftsd_lp_timer_cal0); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", gnData.ftsd_lp_timer_cal1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", gnData.ftsd_lp_timer_cal2); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", gnData.ftsd_lp_timer_cal3); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", gnData.ftsa_lp_timer_cal0); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", gnData.ftsa_lp_timer_cal1); + strlcat(all_strbuff, buff, size); + break; + + case CMD_GETFWVER: + snprintf(buff, sizeof (buff), "%04X", fw_version); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%04X", config_id); + strlcat(all_strbuff, buff, size); + break; + + case CMD_READCOMPDATAHEAD: + snprintf(buff, sizeof (buff), "%02X", dataHead.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof (buff), "%02X", dataHead.sense_node); + strlcat(all_strbuff, buff, size); + break; + + default: + break; + + } + } + + snprintf(buff, sizeof (buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + numberParam = 0; /* need to reset the number of parameters in order to wait the next comand, comment if you want to repeat the last comand sent just doing a cat */ + /* logError(0,"%s numberParameters = %d\n",tag, numberParam); */ + kfree(all_strbuff); + kfree(functionToTest); + return count; + +} + +/*static DEVICE_ATTR(stm_driver_test, (S_IRWXU|S_IRWXG), stm_driver_test_show, stm_driver_test_store);*/ +static DEVICE_ATTR(stm_driver_test, (S_IRUGO | S_IWUSR | S_IWGRP), stm_driver_test_show, stm_driver_test_store); + +static struct attribute *test_cmd_attributes[] = { + &dev_attr_stm_driver_test.attr, + NULL, +}; + +struct attribute_group test_cmd_attr_group = { + .attrs = test_cmd_attributes, +}; + +#endif diff --git a/drivers/input/touchscreen/st/fts_fw.h b/drivers/input/touchscreen/st/fts_fw.h new file mode 100644 index 000000000000..d928a06951ea --- /dev/null +++ b/drivers/input/touchscreen/st/fts_fw.h @@ -0,0 +1,10 @@ +#ifndef FTS_FW_H +#define FTS_FW_H +/* This is an auto generated header file +* --->Remember to change the name of the two variables!<--- */ +const uint32_t myArray_size; + +const uint8_t myArray[] = { +}; + +#endif diff --git a/drivers/input/touchscreen/st/fts_gui.c b/drivers/input/touchscreen/st/fts_gui.c new file mode 100644 index 000000000000..f695137ada09 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_gui.c @@ -0,0 +1,359 @@ +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/hrtimer.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/completion.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> +#include "fts.h" +#include "fts_lib/ftsCompensation.h" +#include "fts_lib/ftsIO.h" +#include "fts_lib/ftsError.h" +#include "fts_lib/ftsFrame.h" +#include "fts_lib/ftsTest.h" +#include "fts_lib/ftsTime.h" +#include "fts_lib/ftsTool.h" + +#ifdef SCRIPTLESS + +unsigned int data[CMD_RESULT_STR_LEN] = {0}; +unsigned char pAddress_i2c[CMD_RESULT_STR_LEN] = {0}; +int byte_count_read; +char Out_buff[TSP_BUF_SIZE]; + +/*I2C CMd functions: functions to interface with GUI without script */ + +ssize_t fts_i2c_wr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + int i; + char buff[16]; + memset(Out_buff, 0x00, ARRAY_SIZE(Out_buff)); + if (byte_count_read == 0) { + snprintf(Out_buff, sizeof(Out_buff), "{FAILED}"); + return snprintf(buf, TSP_BUF_SIZE, "{%s}\n", Out_buff); + } +#ifdef SCRIPTLESS_DEBUG + printk("%s:DATA READ {", __func__); + for (i = 0; i < byte_count_read; i++) { + printk(" %02X", (unsigned int)info->cmd_wr_result[i]); + if (i < (byte_count_read-1)) { + printk(" "); + } + } + printk("}\n"); +#endif + snprintf(buff, sizeof(buff), "{"); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + for (i = 0; i < (byte_count_read+2); i++) { + if ((i == 0)) { + char temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + snprintf(buff, sizeof(buff), "%02X", temp_byte_count_read); + } else if (i == 1) { + char temp_byte_count_read = (byte_count_read) & 0xFF; + snprintf(buff, sizeof(buff), "%02X", temp_byte_count_read); + + } else { + snprintf(buff, sizeof(buff), "%02X", info->cmd_wr_result[i-2]); + } + /* snprintf(buff, sizeof(buff), "%02X", info->cmd_wr_result[i]); */ + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + if (i < (byte_count_read+1)) { + snprintf(buff, sizeof(buff), " "); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + } + } + snprintf(buff, sizeof(buff), "}"); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); +} + +ssize_t fts_i2c_wr_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + unsigned char pAddress[8] = {0}; + unsigned int byte_count = 0 ; + int i ; + + unsigned int data[8] = {0}; + memset(data, 0x00, ARRAY_SIZE(data)); + memset(info->cmd_wr_result, 0x00, ARRAY_SIZE(info->cmd_wr_result)); + sscanf(buf, "%x %x %x %x %x %x %x %x ", (data+7), (data), (data+1), + (data+2), (data+3), (data+4), (data+5), (data+6)); + + byte_count = data[7]; + + /*if (sizeof(buf) != byte_count ) + { + printk("%s : Byte count is wrong\n",__func__); + return count; + }*/ +#ifdef SCRIPTLESS_DEBUG + printk("\n"); + printk("%s: Input Data 1:", __func__); + + for (i = 0 ; i < 7; i++) { + printk(" %02X", data[i]); + pAddress[i] = (unsigned char)data[i]; + } + printk("\n"); +#else + for (i = 0 ; i < 7; i++) { + pAddress[i] = (unsigned char)data[i]; + } +#endif + byte_count_read = data[byte_count-1]; + ret = fts_writeCmd(pAddress, 3); + msleep(20); + ret = fts_readCmd(&pAddress[3], (byte_count-4), info->cmd_wr_result, + byte_count_read); +#ifdef SCRIPTLESS_DEBUG + printk("%s:DATA READ\n{", __func__); + for (i = 0; i < (2+byte_count_read); i++) { + if ((i == 0)) { + char temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + printk("%02X", (unsigned int)temp_byte_count_read); + } else if (i == 1) { + char temp_byte_count_read = (byte_count_read) & 0xFF; + printk("%02X", (unsigned int)temp_byte_count_read); + + } else { + printk("%02X", (unsigned int)info->cmd_read_result[i-2]); + } + if (i < (byte_count_read+1)) { + printk(" "); + } + + } + printk("}\n"); +#endif + if (ret) + dev_err(dev, "Unable to read register\n"); + return count; +} + +ssize_t fts_i2c_read_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + int i ; + char buff[16]; + + memset(Out_buff, 0x00, ARRAY_SIZE(Out_buff)); + if (byte_count_read == 0) { + snprintf(Out_buff, sizeof(Out_buff), "{FAILED}"); + return snprintf(buf, TSP_BUF_SIZE, "{%s}\n", Out_buff); + } +#ifdef SCRIPTLESS_DEBUG + printk("%s:DATA READ {", __func__); + for (i = 0; i < byte_count_read; i++) { + printk("%02X", (unsigned int)info->cmd_read_result[i]); + if (i < (byte_count_read-1)) { + printk(" "); + } + } + printk("}\n"); +#endif + snprintf(buff, sizeof(buff), "{"); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + for (i = 0; i < (byte_count_read+2); i++) { + if ((i == 0)) { + char temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + snprintf(buff, sizeof(buff), "%02X", temp_byte_count_read); + } else if (i == 1) { + char temp_byte_count_read = (byte_count_read) & 0xFF; + snprintf(buff, sizeof(buff), "%02X", temp_byte_count_read); + + } else { + snprintf(buff, sizeof(buff), "%02X", info->cmd_read_result[i-2]); + } + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + if (i < (byte_count_read+1)) { + snprintf(buff, sizeof(buff), " "); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + } + } + snprintf(buff, sizeof(buff), "}"); + strlcat(Out_buff, buff, ARRAY_SIZE(Out_buff)); + + return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); +} + +ssize_t fts_i2c_read_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + unsigned char pAddress[8] = {0}; + unsigned int byte_count = 0; + int i ; + unsigned int data[8] = {0}; + + byte_count_read = 0; + memset(data, 0x00, ARRAY_SIZE(data)); + memset(info->cmd_read_result, 0x00, ARRAY_SIZE(info->cmd_read_result)); + sscanf(buf, "%x %x %x %x %x %x %x %x ", (data+7), (data), (data+1), (data+2), (data+3), (data+4), (data+5), (data+6)); + byte_count = data[7]; + + if (byte_count > 7) { +#ifdef SCRIPTLESS_DEBUG + printk("%s : Byte count is more than 7\n", __func__); +#endif + return count; + } + /*if (sizeof(buf) != byte_count ) + { + printk("%s : Byte count is wrong\n",__func__); + return count; + }*/ +#ifdef SCRIPTLESS_DEBUG + printk("\n"); + printk("%s: Input Data 1:", __func__); + for (i = 0 ; i < byte_count; i++) { + printk(" %02X", data[i]); + pAddress[i] = (unsigned char)data[i]; + } + printk("\n"); +#else + for (i = 0 ; i < byte_count; i++) { + pAddress[i] = (unsigned char)data[i]; + } +#endif + byte_count_read = data[byte_count-1]; + ret = fts_readCmd(pAddress, (byte_count-1), info->cmd_read_result, byte_count_read); +#ifdef SCRIPTLESS_DEBUG + printk("%s:DATA READ\n{", __func__); + for (i = 0; i < (byte_count_read+2); i++) { + if ((i == 0)) { + char temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + printk("%02X", (unsigned int)temp_byte_count_read); + } else if (i == 1) { + char temp_byte_count_read = (byte_count_read) & 0xFF; + printk("%02X", (unsigned int)temp_byte_count_read); + + } else { + printk("%02X", (unsigned int)info->cmd_read_result[i-2]); + } + if (i < (byte_count_read+1)) { + printk(" "); + } + } + printk("}\n"); +#endif + if (ret) + dev_err(dev, "Unable to read register\n"); + return count; +} + +ssize_t fts_i2c_write_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + return snprintf(buf, TSP_BUF_SIZE, "%s", info->cmd_write_result); + +} + +ssize_t fts_i2c_write_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + unsigned int byte_count = 0; + int i ; + memset(data, 0x00, ARRAY_SIZE(data)); + memset(pAddress_i2c, 0x00, ARRAY_SIZE(pAddress_i2c)); + memset(info->cmd_write_result, 0x00, ARRAY_SIZE(info->cmd_write_result)); + sscanf(buf, "%x %x", data, (data + 1)); + byte_count = data[0] << 8 | data[1]; + + if (byte_count <= ARRAY_SIZE(pAddress_i2c)) { + for (i = 0; i < (byte_count); i++) { + sscanf(&buf[3*(i+2)], "%x ", (data+i)); + } } else { +#ifdef SCRIPTLESS_DEBUG + printk("%s : message size is more than allowed limit of 512 bytes\n", __func__); +#endif + snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), "{Write NOT OK}\n"); + } +#ifdef SCRIPTLESS_DEBUG + printk("\n"); + printk("%s: Byte_count= %02d | Count = %02d | size of buf:%02d\n", __func__, byte_count, (int)count, (int)sizeof(buf)); + printk("%s: Input Data 1:", __func__); + for (i = 0 ; i < byte_count; i++) { + printk("%02X", data[i]); + pAddress_i2c[i] = (unsigned char)data[i]; + } + printk("\n"); +#else + for (i = 0; i < byte_count; i++) { + pAddress_i2c[i] = (unsigned char)data[i]; + } +#endif + if ((pAddress_i2c[0] == 0xb3) && (pAddress_i2c[3] == 0xb1)) { + ret = fts_writeCmd(pAddress_i2c, 3); + msleep(20); + ret = fts_writeCmd(&pAddress_i2c[3], byte_count-3); + } else { + ret = fts_writeCmd(pAddress_i2c, byte_count); + } + +#ifdef SCRIPTLESS_DEBUG + printk("%s:DATA :", __func__); + for (i = 0; i < byte_count; i++) { + printk(" %02X", (unsigned int)pAddress_i2c[i]); + } + printk(" byte_count: %02X\n", byte_count); +#endif + if (ret < 0) { + dev_err(dev, "{Write NOT OK}\n"); + snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), "{Write NOT OK}\n"); + } else { + snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), "{Write OK}\n"); +#ifdef SCRIPTLESS_DEBUG + printk("%s : {Write OK}\n", __func__); +#endif + } + return count; +} + +static DEVICE_ATTR(iread, (S_IWUSR|S_IWGRP), NULL, fts_i2c_read_store); +static DEVICE_ATTR(iread_result, (S_IRUSR|S_IRGRP), fts_i2c_read_show, NULL); +static DEVICE_ATTR(iwr, (S_IWUSR|S_IWGRP), NULL, fts_i2c_wr_store); +static DEVICE_ATTR(iwr_result, (S_IRUSR|S_IRGRP), fts_i2c_wr_show, NULL); +static DEVICE_ATTR(iwrite, (S_IWUSR|S_IWGRP), NULL, fts_i2c_write_store); +static DEVICE_ATTR(iwrite_result, (S_IRUSR|S_IRGRP), fts_i2c_write_show, NULL); + +static struct attribute *i2c_cmd_attributes[] = { + &dev_attr_iread.attr, + &dev_attr_iread_result.attr, + &dev_attr_iwr.attr, + &dev_attr_iwr_result.attr, + &dev_attr_iwrite.attr, + &dev_attr_iwrite_result.attr, + NULL, +}; + +struct attribute_group i2c_cmd_attr_group = { + .attrs = i2c_cmd_attributes, +}; + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/Makefile b/drivers/input/touchscreen/st/fts_lib/Makefile new file mode 100644 index 000000000000..24eca48fc3cd --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the FTS touchscreen driver. +# + +obj-$(CONFIG_TOUCHSCREEN_ST_I2C) += ftsCompensation.o \ + ftsCrossCompile.o ftsError.o ftsFrame.o ftsIO.o ftsTest.o \ + ftsTime.o ftsTool.o ftsFlash.o ftsGesture.o diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCompensation.c b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.c new file mode 100644 index 000000000000..2ca0067f34b0 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.c @@ -0,0 +1,591 @@ +/* +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS functions for getting Initialization Data * +* * +************************************************************************** +************************************************************************** +*/ + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTool.h" + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <stdarg.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/time.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ + +static char tag[8] = "[ FTS ]\0"; + +chipInfo ftsInfo; + +int requestCompensationData(u16 type) +{ + int retry = 0; + int ret; + u16 answer; + + int event_to_search[3]; + u8 readEvent[FIFO_EVENT_SIZE]; + + u8 cmd[3] = { FTS_CMD_REQU_COMP_DATA, 0x00, 0x00 }; + /* B8 is the command for asking compensation data */ + u16ToU8(type, &cmd[1]); + + event_to_search[0] = (int)EVENTID_COMP_DATA_READ; + event_to_search[1] = cmd[1]; + event_to_search[2] = cmd[2]; + + while (retry < COMP_DATA_READ_RETRY) { + logError(0, "%s %s", tag, printHex("Command = ", cmd, 3)); + ret = fts_writeFwCmd(cmd, 3); + /* send the request to the chip to load in memory the Compensation Data */ + if (ret < OK) { + logError(1, "%s requestCompensationData: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + ret = pollForEvent(event_to_search, 3, readEvent, TIMEOUT_REQU_COMP_DATA); + if (ret < OK) { + logError(0, "%s Event did not Found at %d attemp! \n", tag, retry+1); + retry += 1; + } else { + retry = 0; + break; + } + } + + if (retry == COMP_DATA_READ_RETRY) { + logError(1, "%s requestCompensationData: ERROR %02X\n", tag, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + + u8ToU16_le(&readEvent[1], &answer); + + if (answer == type) + return OK; + logError(1, "%s The event found has a different type of Compensation data ERROR %02X \n", tag, ERROR_DIFF_COMP_TYPE); + return ERROR_DIFF_COMP_TYPE; + +} + +int readCompensationDataHeader(u16 type, DataHeader *header, u16 *address) +{ + + u16 offset = ADDR_FRAMEBUFFER_DATA; + u16 answer; + u8 data[COMP_DATA_HEADER]; + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, offset, data, COMP_DATA_HEADER, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readCompensationDataHeader: ERROR %02X \n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + logError(0, "%s Read Data Header done! \n", tag); + + if (data[0] != HEADER_SIGNATURE) { + logError(1, "%s readCompensationDataHeader: ERROR %02X The Header Signature was wrong! %02X != %02X \n", tag, ERROR_WRONG_COMP_SIGN, data[0], HEADER_SIGNATURE); + return ERROR_WRONG_COMP_SIGN; + } + + u8ToU16_le(&data[1], &answer); + + if (answer != type) { + logError(1, "%s readCompensationDataHeader: ERROR %02X\n", tag, ERROR_DIFF_COMP_TYPE); + return ERROR_DIFF_COMP_TYPE; + } + + logError(0, "%s Type of Compensation data OK! \n", tag); + + header->type = type; + header->force_node = (int)data[4]; + header->sense_node = (int)data[5]; + + *address = offset + COMP_DATA_HEADER; + + return OK; + +} + +int readMutualSenseGlobalData(u16 *address, MutualSenseData *global) +{ + + u8 data[COMP_DATA_GLOBAL]; + + logError(0, "%s Address for Global data= %02X \n", tag, *address); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, *address, data, COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readMutualSenseGlobalData: ERROR %02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + logError(0, "%s Global data Read !\n", tag); + + global->tuning_ver = data[0]; + global->cx1 = data[1]; + + logError(0, "%s tuning_ver = %d CX1 = %d \n", tag, global->tuning_ver, global->cx1); + + *address += COMP_DATA_GLOBAL; + return OK; + +} + +int readMutualSenseNodeData(u16 address, MutualSenseData *node) +{ + + int size = node->header.force_node*node->header.sense_node; + + logError(0, "%s Address for Node data = %02X \n", tag, address); + + node->node_data = (u8 *)kmalloc(size*(sizeof(u8)), GFP_KERNEL); + + if (node->node_data == NULL) { + logError(1, "%s readMutualSenseNodeData: ERROR %02X", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + logError(0, "%s Node Data to read %d bytes \n", tag, size); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, node->node_data, size, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readMutualSenseNodeData: ERROR %02X \n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + node->node_data_size = size; + + logError(0, "%s Read node data ok! \n", tag); + + return size; + +} + +int readMutualSenseCompensationData(u16 type, MutualSenseData *data) +{ + + int ret; + u16 address; + + if (!(type == MS_TOUCH_ACTIVE || type == MS_TOUCH_LOW_POWER || + type == MS_TOUCH_ULTRA_LOW_POWER || type == MS_KEY)) { + logError(1, "%s readMutualSenseCompensationData: Choose a MS type of compensation data ERROR %02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensationData: ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + return (ret|ERROR_REQU_COMP_DATA); + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + return (ret|ERROR_COMP_DATA_HEADER); + } + + ret = readMutualSenseGlobalData(&address, data); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensationData: ERROR %02X \n", tag, ERROR_COMP_DATA_GLOBAL); + return (ret|ERROR_COMP_DATA_GLOBAL); + } + + ret = readMutualSenseNodeData(address, data); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_NODE); + return (ret|ERROR_COMP_DATA_NODE); + } + + return OK; + +} + +int readSelfSenseGlobalData(u16 *address, SelfSenseData *global) +{ + + u8 data[COMP_DATA_GLOBAL]; + + logError(0, "%s Address for Global data= %02X \n", tag, *address); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, *address, data, COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readSelfSenseGlobalData: ERROR %02X \n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + + logError(0, "%s Global data Read !\n", tag); + + global->tuning_ver = data[0]; + global->f_ix1 = data[1]; + global->s_ix1 = data[2]; + global->f_cx1 = data[3]; + global->s_cx1 = data[4]; + global->f_max_n = data[5]; + global->s_max_n = data[6]; + + logError(0, "%s tuning_ver = %d f_ix1 = %d s_ix1 = %d f_cx1 = %d s_cx1 = %d \n", tag, global->tuning_ver, global->f_ix1, global->s_ix1, global->f_cx1, global->s_cx1); + logError(0, "%s max_n = %d s_max_n = %d \n", tag, global->f_max_n, global->s_max_n); + + *address += COMP_DATA_GLOBAL; + + return OK; + +} + +int readSelfSenseNodeData(u16 address, SelfSenseData *node) +{ + + int size = node->header.force_node*2+node->header.sense_node*2; + u8 data[size]; + + node->ix2_fm = (u8 *)kmalloc(node->header.force_node*(sizeof(u8)), GFP_KERNEL); + node->cx2_fm = (u8 *)kmalloc(node->header.force_node*(sizeof(u8)), GFP_KERNEL); + node->ix2_sn = (u8 *)kmalloc(node->header.sense_node*(sizeof(u8)), GFP_KERNEL); + node->cx2_sn = (u8 *)kmalloc(node->header.sense_node*(sizeof(u8)), GFP_KERNEL); + + if (node->ix2_fm == NULL || node->cx2_fm == NULL || node->ix2_sn == NULL + || node->cx2_sn == NULL) { + logError(1, "%s readSelfSenseNodeData: ERROR %02X", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + logError(0, "%s Address for Node data = %02X \n", tag, address); + + logError(0, "%s Node Data to read %d bytes \n", tag, size); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, data, size, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readSelfSenseNodeData: ERROR %02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + + logError(0, "%s Read node data ok! \n", tag); + + memcpy(node->ix2_fm, data, node->header.force_node); + memcpy(node->ix2_sn, &data[node->header.force_node], node->header.sense_node); + memcpy(node->cx2_fm, &data[node->header.force_node + node->header.sense_node], node->header.force_node); + memcpy(node->cx2_sn, &data[node->header.force_node*2 + node->header.sense_node], node->header.sense_node); + + return OK; + +} + +int readSelfSenseCompensationData(u16 type, SelfSenseData *data) +{ + + int ret; + u16 address; + + if (!(type == SS_TOUCH || type == SS_KEY || type == SS_HOVER || type == SS_PROXIMITY)) { + logError(1, "%s readSelfSenseCompensationData: Choose a SS type of compensation data ERROR %02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, "%s readSelfSenseCompensationData: ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + return (ret|ERROR_REQU_COMP_DATA); + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, "%s readSelfSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + return (ret|ERROR_COMP_DATA_HEADER); + } + + ret = readSelfSenseGlobalData(&address, data); + if (ret < 0) { + logError(1, "%s readSelfSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_GLOBAL); + return (ret|ERROR_COMP_DATA_GLOBAL); + } + + ret = readSelfSenseNodeData(address, data); + if (ret < 0) { + logError(1, "%s readSelfSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_NODE); + return (ret|ERROR_COMP_DATA_NODE); + } + + return OK; + +} + +int readGeneralGlobalData(u16 address, GeneralData *global) +{ + u8 data[COMP_DATA_GLOBAL]; + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, data, COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readGeneralGlobalData: ERROR %02X \n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + + global->ftsd_lp_timer_cal0 = data[0]; + global->ftsd_lp_timer_cal1 = data[1]; + global->ftsd_lp_timer_cal2 = data[2]; + global->ftsd_lp_timer_cal3 = data[3]; + global->ftsa_lp_timer_cal0 = data[4]; + global->ftsa_lp_timer_cal1 = data[5]; + + return OK; + +} + +int readGeneralCompensationData(u16 type, GeneralData *data) +{ + + int ret; + u16 address; + + if (!(type == GENERAL_TUNING)) { + logError(1, "%s readGeneralCompensationData: Choose a GENERAL type of compensation data ERROR %02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, "%s readGeneralCompensationData: ERROR %02X \n", tag, ERROR_REQU_COMP_DATA); + return ERROR_REQU_COMP_DATA; + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, "%s readGeneralCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + return ERROR_COMP_DATA_HEADER; + } + + ret = readGeneralGlobalData(address, data); + if (ret < 0) { + logError(1, "%s readGeneralCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_GLOBAL); + return ERROR_COMP_DATA_GLOBAL; + } + + return OK; + +} + +int defaultChipInfo(int i2cError) +{ + int i; + logError(0, "%s Setting default Chip Info... \n", tag); + ftsInfo.u32_echoEn = 0x00000000; + ftsInfo.u8_msScrConfigTuneVer = 0; + ftsInfo.u8_ssTchConfigTuneVer = 0; + ftsInfo.u8_msScrCxmemTuneVer = 0; + ftsInfo.u8_ssTchCxmemTuneVer = 0; + if (i2cError == 1) { + ftsInfo.u16_fwVer = 0xFFFF; + ftsInfo.u16_cfgId = 0xFFFF; + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { + ftsInfo.u8_extReleaseInfo[i] = 0xFF; + } + } else { + ftsInfo.u16_fwVer = 0x0000; + ftsInfo.u16_cfgId = 0x0000; + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { + ftsInfo.u8_extReleaseInfo[i] = 0x00; + } + } + ftsInfo.u32_mpPassFlag = INIT_FIELD; + logError(0, "%s default Chip Info DONE! \n", tag); + return OK; + +} + +int readChipInfo(int doRequest) +{ + int ret, i; + u16 answer; + u8 data[CHIP_INFO_SIZE+3]; + /* +3 because need to read all the field of the struct plus the signature and 2 address bytes */ + int index = 0; + + logError(0, "%s Starting Read Chip Info... \n", tag); + if (doRequest == 1) { + ret = requestCompensationData(CHIP_INFO); + if (ret < 0) { + logError(1, "%s readChipInfo: ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + ret = (ret | ERROR_REQU_COMP_DATA); + goto FAIL; + } + } + + logError(0, "%s Byte to read = %d bytes \n", tag, CHIP_INFO_SIZE+3); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, ADDR_FRAMEBUFFER_DATA, data, CHIP_INFO_SIZE+3, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readChipInfo: ERROR %02X\n", tag, ERROR_I2C_R); + ret = ERROR_I2C_R; + goto FAIL; + } + + logError(0, "%s Read data ok! \n", tag); + + logError(0, "%s Starting parsing of data... \n", tag); + + if (data[0] != HEADER_SIGNATURE) { + logError(1, "%s readChipInfo: ERROR %02X The Header Signature was wrong! %02X != %02X \n", tag, ERROR_WRONG_COMP_SIGN, data[0], HEADER_SIGNATURE); + ret = ERROR_WRONG_COMP_SIGN; + goto FAIL; + } + + u8ToU16_le(&data[1], &answer); + + if (answer != CHIP_INFO) { + logError(1, "%s readChipInfo: ERROR %02X\n", tag, ERROR_DIFF_COMP_TYPE); + ret = ERROR_DIFF_COMP_TYPE; + goto FAIL; + } + + index += 3; + ftsInfo.u8_loadCnt = data[index++]; + ftsInfo.u8_infoVer = data[index++]; + u8ToU16(&data[index], &ftsInfo.u16_ftsdId); + index += 2; + ftsInfo.u8_ftsdVer = data[index++]; + ftsInfo.u8_ftsaId = data[index++]; + ftsInfo.u8_ftsaVer = data[index++]; + ftsInfo.u8_tchRptVer = data[index++]; + + logError(1, "%s External Release = ", tag); + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { + ftsInfo.u8_extReleaseInfo[i] = data[index++]; + logError(1, "%02X ", ftsInfo.u8_extReleaseInfo[i]); + } + logError(1, "\n"); + + for (i = 0; i < sizeof(ftsInfo.u8_custInfo); i++) { + ftsInfo.u8_custInfo[i] = data[index++]; + } + + u8ToU16(&data[index], &ftsInfo.u16_fwVer); + index += 2; + logError(1, "%s FW VERSION = %04X \n", tag, ftsInfo.u16_fwVer); + + u8ToU16(&data[index], &ftsInfo.u16_cfgId); + index += 2; + logError(1, "%s CONFIG ID = %04X \n", tag, ftsInfo.u16_cfgId); + + ftsInfo.u32_projId = ((data[index + 3] & 0x000000FF) << 24) + ((data[index + 2] & 0x000000FF) << 16) + ((data[index + 1] & 0x000000FF) << 8) + (data[index] & 0x000000FF); + index += 4; + + u8ToU16(&data[index], &ftsInfo.u16_scrXRes); + index += 2; + + u8ToU16(&data[index], &ftsInfo.u16_scrYRes); + index += 2; + + ftsInfo.u8_scrForceLen = data[index++]; + logError(1, "%s Force Len = %d \n", tag, ftsInfo.u8_scrForceLen); + + ftsInfo.u8_scrSenseLen = data[index++]; + logError(1, "%s Sense Len = %d \n", tag, ftsInfo.u8_scrSenseLen); + + for (i = 0; i < 8; i++) { + ftsInfo.u64_scrForceEn[i] = data[index++]; + } + + for (i = 0; i < 8; i++) { + ftsInfo.u64_scrSenseEn[i] = data[index++]; + } + + ftsInfo.u8_msKeyLen = data[index++]; + logError(1, "%s MS Key Len = %d \n", tag, ftsInfo.u8_msKeyLen); + + for (i = 0; i < 8; i++) { + ftsInfo.u64_msKeyForceEn[i] = data[index++]; + } + + for (i = 0; i < 8; i++) { + ftsInfo.u64_msKeySenseEn[i] = data[index++]; + } + + ftsInfo.u8_ssKeyLen = data[index++]; + logError(1, "%s SS Key Len = %d \n", tag, ftsInfo.u8_ssKeyLen); + + for (i = 0; i < 8; i++) { + ftsInfo.u64_ssKeyForceEn[i] = data[index++]; + } + + for (i = 0; i < 8; i++) { + ftsInfo.u64_ssKeySenseEn[i] = data[index++]; + } + + ftsInfo.u8_frcTchXLen = data[index++]; + + ftsInfo.u8_frcTchYLen = data[index++]; + + for (i = 0; i < 8; i++) { + ftsInfo.u64_frcTchForceEn[i] = data[index++]; + } + + for (i = 0; i < 8; i++) { + ftsInfo.u64_frcTchSenseEn[i] = data[index++]; + } + + ftsInfo.u8_msScrConfigTuneVer = data[index++]; + logError(1, "%s CFG MS TUNING VERSION = %02X \n", tag, ftsInfo.u8_msScrConfigTuneVer); + ftsInfo.u8_msScrLpConfigTuneVer = data[index++]; + ftsInfo.u8_msScrHwulpConfigTuneVer = data[index++]; + ftsInfo.u8_msKeyConfigTuneVer = data[index++]; + ftsInfo.u8_ssTchConfigTuneVer = data[index++]; + logError(1, "%s CFG SS TUNING VERSION = %02X \n", tag, ftsInfo.u8_ssTchConfigTuneVer); + ftsInfo.u8_ssKeyConfigTuneVer = data[index++]; + ftsInfo.u8_ssHvrConfigTuneVer = data[index++]; + ftsInfo.u8_frcTchConfigTuneVer = data[index++]; + ftsInfo.u8_msScrCxmemTuneVer = data[index++]; + logError(1, "%s CX MS TUNING VERSION = %02X \n", tag, ftsInfo.u8_msScrCxmemTuneVer); + ftsInfo.u8_msScrLpCxmemTuneVer = data[index++]; + ftsInfo.u8_msScrHwulpCxmemTuneVer = data[index++]; + ftsInfo.u8_msKeyCxmemTuneVer = data[index++]; + ftsInfo.u8_ssTchCxmemTuneVer = data[index++]; + logError(1, "%s CX SS TUNING VERSION = %02X \n", tag, ftsInfo.u8_ssTchCxmemTuneVer); + ftsInfo.u8_ssKeyCxmemTuneVer = data[index++]; + ftsInfo.u8_ssHvrCxmemTuneVer = data[index++]; + ftsInfo.u8_frcTchCxmemTuneVer = data[index++]; + ftsInfo.u32_mpPassFlag = ((data[index + 3] & 0x000000FF) << 24) + ((data[index + 2] & 0x000000FF) << 16) + ((data[index + 1] & 0x000000FF) << 8) + (data[index] & 0x000000FF); + index += 4; + logError(1, "%s MP SIGNATURE = %08X \n", tag, ftsInfo.u32_mpPassFlag); + ftsInfo.u32_featEn = ((data[index + 3] & 0x000000FF) << 24) + ((data[index + 2] & 0x000000FF) << 16) + ((data[index + 1] & 0x000000FF) << 8) + (data[index] & 0x000000FF); + index += 4; + ftsInfo.u32_echoEn = ((data[index + 3] & 0x000000FF) << 24) + ((data[index + 2] & 0x000000FF) << 16) + ((data[index + 1] & 0x000000FF) << 8) + (data[index] & 0x000000FF); + index += 4; + logError(1, "%s FEATURES = %08X \n", tag, ftsInfo.u32_echoEn); + + logError(1, "%s Parsed %d bytes! \n", tag, index); + + if (index != CHIP_INFO_SIZE + 3) { + logError(1, "%s readChipInfo: index = %d different from %d ERROR %02X\n", tag, index, CHIP_INFO_SIZE+3, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + logError(1, "%s Chip Info Read DONE!\n", tag); + return OK; + +FAIL: + defaultChipInfo(isI2cError(ret)); + return ret; + +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCompensation.h b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.h new file mode 100644 index 000000000000..c4cc5913fc18 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsCompensation.h @@ -0,0 +1,146 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS functions for getting Initialization Data * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsCrossCompile.h" +#include "ftsSoftware.h" + +#define COMP_DATA_READ_RETRY 2 + +/* Bytes dimension of Compensation Data Format */ + +#define COMP_DATA_HEADER 8 +#define COMP_DATA_GLOBAL 8 + +#define HEADER_SIGNATURE 0xA5 + +/* Possible Compensation/Frame Data Type */ +#define GENERAL_TUNING 0x0100 +#define MS_TOUCH_ACTIVE 0x0200 +#define MS_TOUCH_LOW_POWER 0x0400 +#define MS_TOUCH_ULTRA_LOW_POWER 0x0800 +#define MS_KEY 0x1000 +#define SS_TOUCH 0x2000 +#define SS_KEY 0x4000 +#define SS_HOVER 0x8000 +#define SS_PROXIMITY 0x0001 +#define CHIP_INFO 0xFFFF + +#define TIMEOUT_REQU_COMP_DATA 1000 /* ms */ + +/* CHIP INFO */ +#define CHIP_INFO_SIZE 138/* bytes to read from framebuffer (exclude the signature and the type because already checked during the reading) */ +#define EXTERNAL_RELEASE_INFO_SIZE 8/* bytes */ + +typedef struct { + int force_node, sense_node; + u16 type; +} DataHeader; + +typedef struct { + DataHeader header; + u8 tuning_ver; + u8 cx1; + u8 *node_data; + int node_data_size; +} MutualSenseData; + +typedef struct { + DataHeader header; + u8 tuning_ver; + u8 f_ix1, s_ix1; + u8 f_cx1, s_cx1; + u8 f_max_n, s_max_n; + + u8 *ix2_fm; + u8 *ix2_sn; + u8 *cx2_fm; + u8 *cx2_sn; + +} SelfSenseData; + +typedef struct { + DataHeader header; + u8 ftsd_lp_timer_cal0; + u8 ftsd_lp_timer_cal1; + u8 ftsd_lp_timer_cal2; + + u8 ftsd_lp_timer_cal3; + u8 ftsa_lp_timer_cal0; + u8 ftsa_lp_timer_cal1; + +} GeneralData; + +typedef struct { + u8 u8_loadCnt; /* 03 - Load Counter */ + u8 u8_infoVer; /* 04 - New chip info version */ + u16 u16_ftsdId; /* 05 - FTSD ID */ + u8 u8_ftsdVer; /* 07 - FTSD version */ + u8 u8_ftsaId; /* 08 - FTSA ID */ + u8 u8_ftsaVer; /* 09 - FTSA version */ + u8 u8_tchRptVer; /* 0A - Touch report version (e.g. ST, Samsung etc) */ + u8 u8_extReleaseInfo[EXTERNAL_RELEASE_INFO_SIZE]; /* 0B - External release information */ + u8 u8_custInfo[12]; /* 13 - Customer information */ + u16 u16_fwVer; /* 1F - Firmware version */ + u16 u16_cfgId; /* 21 - Configuration ID */ + u32 u32_projId; /* 23 - Project ID */ + u16 u16_scrXRes; /* 27 - X resolution on main screen */ + u16 u16_scrYRes; /* 29 - Y resolution on main screen */ + u8 u8_scrForceLen; /* 2B - Number of force channel on main screen */ + u8 u8_scrSenseLen; /* 2C - Number of sense channel on main screen */ + u8 u64_scrForceEn[8]; /* 2D - Force channel enabled on main screen */ + u8 u64_scrSenseEn[8]; /* 35 - Sense channel enabled on main screen */ + u8 u8_msKeyLen; /* 3D - Number of MS Key channel */ + u8 u64_msKeyForceEn[8]; /* 3E - MS Key force channel enable */ + u8 u64_msKeySenseEn[8]; /* 46 - MS Key sense channel enable */ + u8 u8_ssKeyLen; /* 4E - Number of SS Key channel */ + u8 u64_ssKeyForceEn[8]; /* 4F - SS Key force channel enable */ + u8 u64_ssKeySenseEn[8]; /* 57 - SS Key sense channel enable */ + u8 u8_frcTchXLen; /* 5F - Number of force touch force channel */ + u8 u8_frcTchYLen; /* 60 - Number of force touch sense channel */ + u8 u64_frcTchForceEn[8]; /* 61 - Force touch force channel enable */ + u8 u64_frcTchSenseEn[8]; /* 69 - Force touch sense channel enable */ + u8 u8_msScrConfigTuneVer; /* 71 - MS screen tuning version in config */ + u8 u8_msScrLpConfigTuneVer; /* 72 - MS screen LP mode tuning version in config */ + u8 u8_msScrHwulpConfigTuneVer; /* 73 - MS screen ultra low power mode tuning version in config */ + u8 u8_msKeyConfigTuneVer; /* 74 - MS Key tuning version in config */ + u8 u8_ssTchConfigTuneVer; /* 75 - SS touch tuning version in config */ + u8 u8_ssKeyConfigTuneVer; /* 76 - SS Key tuning version in config */ + u8 u8_ssHvrConfigTuneVer; /* 77 - SS hover tuning version in config */ + u8 u8_frcTchConfigTuneVer; /* 78 - Force touch tuning version in config */ + u8 u8_msScrCxmemTuneVer; /* 79 - MS screen tuning version in cxmem */ + u8 u8_msScrLpCxmemTuneVer; /* 7A - MS screen LP mode tuning version in cxmem */ + u8 u8_msScrHwulpCxmemTuneVer; /* 7B - MS screen ultra low power mode tuning version in cxmem */ + u8 u8_msKeyCxmemTuneVer; /* 7C - MS Key tuning version in cxmem */ + u8 u8_ssTchCxmemTuneVer; /* 7D - SS touch tuning version in cxmem */ + u8 u8_ssKeyCxmemTuneVer; /* 7E - SS Key tuning version in cxmem */ + u8 u8_ssHvrCxmemTuneVer; /* 7F - SS hover tuning version in cxmem */ + u8 u8_frcTchCxmemTuneVer; /* 80 - Force touch tuning version in cxmem */ + u32 u32_mpPassFlag; /* 81 - Mass production pass flag */ + u32 u32_featEn; /* 85 - Supported features */ + u32 u32_echoEn; /* 89 - enable of particular features: first bit is Echo Enables */ +} chipInfo; + +int requestCompensationData(u16 type); +int readCompensationDataHeader(u16 type, DataHeader *header, u16 *address); +int readMutualSenseGlobalData(u16 *address, MutualSenseData *global); +int readMutualSenseNodeData(u16 address, MutualSenseData *node); +int readMutualSenseCompensationData(u16 type, MutualSenseData *data); +int readSelfSenseGlobalData(u16 *address, SelfSenseData *global); +int readSelfSenseNodeData(u16 address, SelfSenseData *node); +int readSelfSenseCompensationData(u16 type, SelfSenseData *data); +int readGeneralGlobalData(u16 address, GeneralData *global); +int readGeneralCompensationData(u16 type, GeneralData *data); +int defaultChipInfo(int i2cError); +int readChipInfo(int doRequest); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.c b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.c new file mode 100644 index 000000000000..502dace75e4f --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.c @@ -0,0 +1,43 @@ +#include "ftsCrossCompile.h" +#include "ftsError.h" + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/spi/spidev.h> +#include <linux/fcntl.h> +#include <linux/syscalls.h> + +/* static char tag[8]="[ FTS ]\0"; */ +void *stmalloc(size_t size) +{ + return kmalloc(size, GFP_KERNEL); + +} + +void stfree(void *ptr) +{ + kfree(ptr); +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.h b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.h new file mode 100644 index 000000000000..8b287dd342f8 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsCrossCompile.h @@ -0,0 +1,34 @@ +/* #define NDK */ +/* #define DEBUG */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/spi/spidev.h> +#include <linux/fcntl.h> +#include <linux/syscalls.h> + +void *stmalloc(size_t size); +void stfree(void *ptr); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsError.c b/drivers/input/touchscreen/st/fts_lib/ftsError.c new file mode 100644 index 000000000000..844e5019fec6 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsError.c @@ -0,0 +1,105 @@ +/* +*************************************************************************** +* STMicroelectronics +************************************************************************** +* marco.cali@st.com +************************************************************************** +* +* FTS error/info kernel log reporting +* +************************************************************************** +************************************************************************** +*/ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/hrtimer.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/completion.h> + +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> + +#include "../fts.h" +#include "ftsCrossCompile.h" +#include "ftsError.h" +#include "ftsIO.h" +#include "ftsTool.h" + +void logError(int force, const char *msg, ...) +{ + if (force == 1 +#ifdef DEBUG + || 1 +#endif + ) { + va_list args; + va_start(args, msg); + vprintk(msg, args); + va_end(args); + } +} + +int isI2cError(int error) +{ + if (((error & 0x000000FF) >= (ERROR_I2C_R & 0x000000FF)) && ((error & 0x000000FF) <= (ERROR_I2C_O & 0x000000FF))) + return 1; + else + return 0; +} + +int errorHandler(u8 *event, int size) +{ + int res = OK; + struct fts_ts_info *info = NULL; + + if (getClient() != NULL) + info = i2c_get_clientdata(getClient()); + + if (info != NULL && event != NULL && size > 1 && event[0] == EVENTID_ERROR_EVENT) { + logError(1, "%s errorHandler: Starting handling...\n", tag); + switch (event[1]) + /* TODO: write an error log for undefinied command subtype 0xBA*/ + { + case EVENT_TYPE_ESD_ERROR: /* esd */ + res = fts_chip_powercycle(info); + if (res < OK) { + logError(1, "%s errorHandler: Error performing powercycle ERROR %08X\n", tag, res); + } + + res = fts_system_reset(); + if (res < OK) { + logError(1, "%s errorHandler: Cannot reset the device ERROR %08X\n", tag, res); + } + res = (ERROR_HANDLER_STOP_PROC|res); + break; + + case EVENT_TYPE_WATCHDOG_ERROR: /* watchdog */ + res = fts_system_reset(); + if (res < OK) { + logError(1, "%s errorHandler: Cannot reset the device ERROR %08X\n", tag, res); + } + res = (ERROR_HANDLER_STOP_PROC|res); + break; + + default: + logError(1, "%s errorHandler: No Action taken! \n", tag); + break; + + } + logError(1, "%s errorHandler: handling Finished! res = %08X\n", tag, res); + return res; + } + logError(1, "%s errorHandler: event Null or not correct size! ERROR %08X \n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsError.h b/drivers/input/touchscreen/st/fts_lib/ftsError.h new file mode 100644 index 000000000000..fc8fa5003158 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsError.h @@ -0,0 +1,75 @@ +/* +************************************************************************** +** STMicroelectronics +************************************************************************** +** marco.cali@st.com +************************************************************************** +* +* FTS error/info kernel log reporting +* +************************************************************************** +************************************************************************** +*/ + + +/*FIRST LEVEL ERROR CODE*/ +#define OK ((int)0x00000000) /*No ERROR*/ +#define ERROR_ALLOC ((int)0x80000001) /*allocation of memory failed*/ +#define ERROR_I2C_R ((int)0x80000002) /*i2c read failed*/ +#define ERROR_I2C_W ((int)0x80000003) /*i2c write failed*/ +#define ERROR_I2C_WR ((int)0x80000004) /*i2c write/read failed*/ +#define ERROR_I2C_O ((int)0x80000005) /*error during opening a i2c device*/ +#define ERROR_OP_NOT_ALLOW ((int)0x80000006) /*operation not allowed*/ +#define ERROR_TIMEOUT ((int)0x80000007) /*timeout expired! exceed the max number of retries or the max waiting time*/ +#define ERROR_FILE_NOT_FOUND ((int)0x80000008) /*the file that i want to open is not found*/ +#define ERROR_FILE_PARSE ((int)0x80000009) /*error during parsing the file*/ +#define ERROR_FILE_READ ((int)0x8000000A) /*error during reading the file*/ +#define ERROR_LABEL_NOT_FOUND ((int)0x8000000B) /*label not found*/ +#define ERROR_FW_NO_UPDATE ((int)0x8000000C) /*fw in the chip newer than the one in the memmh*/ +#define ERROR_FLASH_UNKNOWN ((int)0x8000000D) /*flash status busy or unknown*/ + +/*SECOND LEVEL ERROR CODE*/ +#define ERROR_DISABLE_INTER ((int)0x80000200) /*unable to disable the interrupt*/ +#define ERROR_ENABLE_INTER ((int)0x80000300) /*unable to activate the interrup*/ +#define ERROR_READ_B2 ((int)0x80000400) /*B2 command failed*/ +#define ERROR_GET_OFFSET ((int)0x80000500) /*unable to read an offset from memory*/ +#define ERROR_GET_FRAME_DATA ((int)0x80000600) /*unable to retrieve the data of a required frame*/ +#define ERROR_DIFF_COMP_TYPE ((int)0x80000700) /*FW answers with an event that has a different address respect the request done*/ +#define ERROR_WRONG_COMP_SIGN ((int)0x80000800) /*the signature of the compensation data is not A5*/ +#define ERROR_SENSE_ON_FAIL ((int)0x80000900) /*the command Sense On failed*/ +#define ERROR_SENSE_OFF_FAIL ((int)0x80000A00) /*the command Sense Off failed*/ +#define ERROR_SYSTEM_RESET_FAIL ((int)0x80000B00) /*the command SYSTEM RESET failed*/ +#define ERROR_FLASH_NOT_READY ((int)0x80000C00) /*flash status not ready within a timeout*/ +#define ERROR_FW_VER_READ ((int)0x80000D00) /*unable to retrieve fw_vers or the config_id*/ +#define ERROR_GESTURE_ENABLE_FAIL ((int)0x80000E00) /*unable to enable/disable the gesture*/ +#define ERROR_GESTURE_START_ADD ((int)0x80000F00) /*unable to start to add custom gesture*/ +#define ERROR_GESTURE_FINISH_ADD ((int)0x80001000) /*unable to finish to add custom gesture*/ +#define ERROR_GESTURE_DATA_ADD ((int)0x80001100) /*unable to add custom gesture data*/ +#define ERROR_GESTURE_REMOVE ((int)0x80001200) /*unable to remove custom gesture data*/ +#define ERROR_FEATURE_ENABLE_DISABLE ((int)0x80001300) /*unable to enable/disable a feature mode in the IC*/ +/*THIRD LEVEL ERROR CODE*/ +#define ERROR_CH_LEN ((int)0x80010000) /*unable to retrieve the force and/or sense length*/ +#define ERROR_REQU_COMP_DATA ((int)0x80020000) /*compensation data request failed*/ +#define ERROR_COMP_DATA_HEADER ((int)0x80030000) /*unable to retrieve the compensation data header*/ +#define ERROR_COMP_DATA_GLOBAL ((int)0x80040000) /*unable to retrieve the global compensation data*/ +#define ERROR_COMP_DATA_NODE ((int)0x80050000) /*unable to retrieve the compensation data for each node*/ +#define ERROR_TEST_CHECK_FAIL ((int)0x80060000) /*check of production limits or of fw answers failed*/ +#define ERROR_MEMH_READ ((int)0x80070000) /*memh reading failed*/ +#define ERROR_FLASH_BURN_FAILED ((int)0x80080000) /*flash burn failed*/ +#define ERROR_MS_TUNING ((int)0x80090000) /*ms tuning failed*/ +#define ERROR_SS_TUNING ((int)0x800A0000) /*ss tuning failed*/ +#define ERROR_LP_TIMER_TUNING ((int)0x800B0000) /*lp timer calibration failed*/ +#define ERROR_SAVE_CX_TUNING ((int)0x800C0000) /*save cx data to flash failed*/ +#define ERROR_HANDLER_STOP_PROC ((int)0x800D0000) /*stop the poll of the FIFO if particular errors are found*/ +#define ERROR_CHECK_ECHO_FAIL ((int)0x800E0000) /*unable to retrieve echo event*/ + +/*FOURTH LEVEL ERROR CODE*/ +#define ERROR_PROD_TEST_DATA ((int)0x81000000) /*production data test failed*/ +#define ERROR_FLASH_PROCEDURE ((int)0x82000000) /*complete flash procedure failed*/ +#define ERROR_PROD_TEST_ITO ((int)0x83000000) /*production ito test failed*/ +#define ERROR_PROD_TEST_INITIALIZATION ((int)0x84000000) /*production initialization test failed*/ +#define ERROR_GET_INIT_STATUS ((int)0x85000000) /*mismatch of the MS or SS tuning_version*/ + +void logError(int force, const char *msg, ...); +int isI2cError(int error); +int errorHandler(u8 *event, int size); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFlash.c b/drivers/input/touchscreen/st/fts_lib/ftsFlash.c new file mode 100644 index 000000000000..f3becac79102 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsFlash.c @@ -0,0 +1,1071 @@ +/* + + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for Flashing the IC * + * * + ************************************************************************** + ************************************************************************** + + */ + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFlash.h" +#include "ftsFrame.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTest.h" +#include "ftsTime.h" +#include "ftsTool.h" +#include "../fts.h" /* needed for the FW_H_FILE define */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <stdarg.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/time.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ + +#ifdef FW_H_FILE +#include <../fts_fw.h> +#endif + +/* static char tag[8] = "[ FTS ]\0"; */ +extern chipInfo ftsInfo; + +int getFirmwareVersion(u16 *fw_vers, u16 *config_id) +{ + u8 fwvers[DCHIP_FW_VER_BYTE]; + u8 confid[CONFIG_ID_BYTE]; + int res; + + res = readCmdU16(FTS_CMD_HW_REG_R, DCHIP_FW_VER_ADDR, fwvers, DCHIP_FW_VER_BYTE, DUMMY_HW_REG); + if (res < OK) { + logError(1, "%s getFirmwareVersion: unable to read fw_version ERROR %02X\n", tag, ERROR_FW_VER_READ); + return (res | ERROR_FW_VER_READ); + } + + u8ToU16(fwvers, fw_vers); /* fw version use big endian */ + if (*fw_vers != 0) { /* if fw_version is 00 00 means that there is no firmware running in the chip therefore will be impossible find the config_id */ + res = readB2(CONFIG_ID_ADDR, confid, CONFIG_ID_BYTE); + if (res < OK) { + logError(1, "%s getFirmwareVersion: unable to read config_id ERROR %02X\n", tag, ERROR_FW_VER_READ); + return (res | ERROR_FW_VER_READ); + } + u8ToU16(confid, config_id); /* config id use little endian */ + } else { + *config_id = 0x0000; + } + + logError(0, "%s FW VERS = %04X\n", tag, *fw_vers); + logError(0, "%s CONFIG ID = %04X\n", tag, *config_id); + return OK; + +} + +#ifdef FTM3_CHIP + +int flash_status(void) +{ + u8 cmd[2] = {FLASH_CMD_READSTATUS, 0x00}; + u8 readData = 0; + + logError(0, "%s Reading flash_status...\n", tag); + if (fts_readCmd(cmd, 2, &readData, FLASH_STATUS_BYTES) < 0) { + logError(1, "%s flash_status: ERROR % 02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + + readData &= 0x01; + /* logError(0, "%s flash_status = %d\n", tag,readData); */ + return (int) readData; + +} + +int flash_status_ready(void) +{ + + int status = flash_status(); + + if (status == ERROR_I2C_R) { + logError(1, "%s flash_status_ready: ERROR % 02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + + if (status != FLASH_READY) { + logError(1, "%s flash_status_ready: flash busy or unknown STATUS = % 02X\n", tag, status); + return ERROR_FLASH_UNKNOWN; + } + + return FLASH_READY; + +} + +int wait_for_flash_ready(void) +{ + int status; + int(*code)(void); + + code = flash_status_ready; + + logError(0, "%s Waiting for flash ready...\n", tag); + status = attempt_function(code, FLASH_WAIT_BEFORE_RETRY, FLASH_RETRY_COUNT); + + if (status != FLASH_READY) { + logError(1, "%s wait_for_flash_ready: ERROR % 02X\n", tag, ERROR_FLASH_NOT_READY); + return (status | ERROR_FLASH_NOT_READY); + } + + logError(0, "%s Flash ready!\n", tag); + return OK; +} + +int flash_unlock(void) +{ + + int status; + u8 cmd[3] = {FLASH_CMD_UNLOCK, FLASH_UNLOCK_CODE0, FLASH_UNLOCK_CODE1}; /* write the comand to perform the unlock */ + + logError(0, "%s Try to unlock flash...\n", tag); + status = wait_for_flash_ready(); + + if (status != OK) { + logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_FLASH_NOT_READY); + return (status | ERROR_FLASH_NOT_READY); /* Flash not ready within the choosen time, better exit! */ + } + + logError(0, "%s Command unlock ...\n", tag); + if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { + logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + status = wait_for_flash_ready(); + + if (status != OK) { + logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_FLASH_NOT_READY); + return (status | ERROR_FLASH_NOT_READY); /* Flash not ready within the choosen time, better exit! */ + } + + logError(0, "%s Unlock flash DONE!\n", tag); + + return OK; + +} + +/*int parseMemhFile(const char *pathToFile, u8** data, int* length, int dimension) +{ + + int i = 0; + unsigned long ul; + u8* buff = NULL; + int fd = -1; + int n, size, pointer = 0; + char *data_file, *line; + const struct firmware *fw = NULL; + struct device *dev = NULL; + + line = (char *) kmalloc(11 * sizeof (char), GFP_KERNEL); + if (line == NULL) { + logError(1, "%s parseMemhFile: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + logError(0, "%s parseMemhFile: allocating %d bytes\n", tag, dimension); + buff = (u8*) kmalloc(dimension * sizeof (u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s parseMemhFile: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + dev = getDev(); + if (dev != NULL) + fd = request_firmware(&fw, pathToFile, dev); + + if (fd == 0) { + size = fw->size; + logError(0, "%s The size of the firmware file is %d bytes...\n", tag, size); + data_file = (char *) fw->data; + logError(0, "%s Start to reading %s...\n", tag, pathToFile); + + while (size - pointer > 0 && (i * 4 + 4) <= dimension) { + if (readLine(&data_file[pointer], &line, size - pointer, &n) < 0) { + break; + } + pointer += n; + logError(0, "%s Pointer= %d riga = %s\n", tag, pointer, line); + ul = simple_strtoul(line, NULL, 16); + + buff[i * 4] = (u8) ((ul & 0x000000FF) >> 0); + buff[i * 4 + 1] = (u8) ((ul & 0x0000FF00) >> 8); + buff[i * 4 + 2] = (u8) ((ul & 0x00FF0000) >> 16); + buff[i * 4 + 3] = (u8) ((ul & 0xFF000000) >> 24); + i++; + } + + kfree(line); + + *length = i * 4; + if (*length < dimension) { + logError(1, "%s parseMemhFile: Read only %d instead of %d... ERROR %02X\n", tag, *length, dimension, ERROR_FILE_PARSE); + release_firmware(fw); + return ERROR_FILE_PARSE; + } + *data = buff; + + logError(0, "%s READ DONE %d bytes!\n", tag, *length); + release_firmware(fw); + return OK; + } else { + logError(1, "%s parseProductionTestLimits: ERROR %02X\n", tag, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + +}*/ + +int parseBinFile(const char *pathToFile, u8 **data, int *length, int dimension) +{ + + int fd = -1; + int fw_size = 0; + u8 *fw_data = NULL; + +#ifndef FW_H_FILE + const struct firmware *fw = NULL; + struct device *dev = NULL; + dev = getDev(); + + if (dev != NULL) + fd = request_firmware(&fw, pathToFile, dev); + else { + logError(1, "%s parseBinFile: No device found! ERROR %02X\n", ERROR_FILE_PARSE); + return ERROR_FILE_PARSE; + } +#else + fd = 0; +#endif + + if (fd == 0) { +#ifndef FW_H_FILE + fw_size = fw->size; + fw_data = (u8 *) (fw->data); +#else + fw_size = FW_SIZE_NAME; + fw_data = (u8 *) FW_ARRAY_NAME; +#endif + if (fw_size - FW_HEADER_SIZE != FW_SIZE) { + logError(1, "%s parseBinFile: Read only %d instead of %d... ERROR %02X\n", tag, fw_size, FW_SIZE, ERROR_FILE_PARSE); +#ifndef FW_H_FILE + release_firmware(fw); +#endif + return ERROR_FILE_PARSE; + } + *data = (u8 *) kmalloc(dimension * sizeof (u8), GFP_KERNEL); + if (*data == NULL) { + logError(1, "%s parseBinFile: ERROR %02X\n", tag, ERROR_ALLOC); +#ifndef FW_H_FILE + release_firmware(fw); +#endif + return ERROR_ALLOC; + } + + memcpy(*data, ((u8 *) (fw_data) + FW_HEADER_SIZE), dimension); + *length = dimension; + + logError(0, "%s READ FW DONE %d bytes!\n", tag, *length); +#ifndef FW_H_FILE + release_firmware(fw); +#endif + return OK; + } + logError(1, "%s parseBinFile: File Not Found! ERROR %02X\n", tag, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; +} + +int readFwFile(const char *path, Firmware *fw, int keep_cx) +{ + int res; + int size; + + if (keep_cx) { + size = FW_SIZE - FW_CX_SIZE; + logError(1, "%s readFwFile: Selected 124k Configuration!\n", tag); + } else { + size = FW_SIZE; + logError(1, "%s readFwFile: Selected 128k Configuration!\n", tag); + } + + /* res = parseMemhFile(path, &(fw->data), &(fw->data_size), size); */ + res = parseBinFile(path, &(fw->data), &(fw->data_size), size); + if (res < OK) { + logError(1, "%s readFwFile: ERROR %02X\n", tag, ERROR_MEMH_READ); + return (res | ERROR_MEMH_READ); + } + + fw->fw_ver = (u16) (((fw->data[FW_VER_MEMH_BYTE1] & 0x00FF) << 8) + (fw->data[FW_VER_MEMH_BYTE0] & 0x00FF)); + fw->config_id = (u16) (((fw->data[(FW_CODE_SIZE) + FW_OFF_CONFID_MEMH_BYTE1] & 0x00FF) << 8) + (fw->data[(FW_CODE_SIZE) + FW_OFF_CONFID_MEMH_BYTE0] & 0x00FF)); + + logError(0, "%s FW VERS File = %04X\n", tag, fw->fw_ver); + logError(0, "%s CONFIG ID File = %04X\n", tag, fw->config_id); + return OK; + +} + +int fillMemory(u32 address, u8 *data, int size) +{ + + int remaining = size; + int toWrite = 0; + + u8 *buff = (u8 *) kmalloc((MEMORY_CHUNK + 3) * sizeof (u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s fillMemory: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + if (remaining >= MEMORY_CHUNK) { + if ((address + MEMORY_CHUNK) < FLASH_ADDR_SWITCH_CMD) { + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = MEMORY_CHUNK; + remaining -= MEMORY_CHUNK; + } else { + if (address < FLASH_ADDR_SWITCH_CMD) { + int delta = FLASH_ADDR_SWITCH_CMD - address; + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = delta; + remaining -= delta; + } else { + buff[0] = FLASH_CMD_WRITE_UPPER_64; + toWrite = MEMORY_CHUNK; + remaining -= MEMORY_CHUNK; + } + } + } else { + if ((address + remaining) < FLASH_ADDR_SWITCH_CMD) { + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = remaining; + remaining = 0; + } else { + if (address < FLASH_ADDR_SWITCH_CMD) { + int delta = FLASH_ADDR_SWITCH_CMD - address; + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = delta; + remaining -= delta; + } else { + buff[0] = FLASH_CMD_WRITE_UPPER_64; + toWrite = remaining; + remaining = 0; + } + } + } + + buff[1] = (u8) ((address & 0x0000FF00) >> 8); + buff[2] = (u8) (address & 0x000000FF); + memcpy(buff + 3, data, toWrite); + logError(0, "%s Command = %02X , address = %02X %02X, bytes = %d\n", tag, buff[0], buff[1], buff[2], toWrite); + if (fts_writeCmd(buff, 3 + toWrite) < 0) { + logError(1, "%s fillMemory: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + address += toWrite; + data += toWrite; + + } + return OK; +} + +int flash_burn(Firmware fw, int force_burn) +{ + u8 cmd; + int res; + + if (!force_burn && (ftsInfo.u16_fwVer >= fw.fw_ver) && (ftsInfo.u16_cfgId >= fw.config_id)) { + logError(1, "%s flash_burn: Firmware in the chip newer or equal to the one to burn! NO UPDATE ERROR %02X\n", tag, ERROR_FW_NO_UPDATE); + return (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED); + } + + /* programming procedure start */ + + logError(0, "%s Programming Procedure for flashing started:\n\n", tag); + + logError(0, "%s 1) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED!\n", tag); + if (res != (ERROR_SYSTEM_RESET_FAIL | ERROR_TIMEOUT)) /* if there is no firmware i will not get the controller ready event and there will be a timeout but i can keep going, but if there is an I2C error i have to exit */ + return (res | ERROR_FLASH_BURN_FAILED); + } else + logError(0, "%s system reset COMPLETED!\n\n", tag); + + logError(0, "%s 2) FLASH UNLOCK:\n", tag); + res = flash_unlock(); + if (res < 0) { + logError(1, "%s flash unlock FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash unlock COMPLETED!\n\n", tag); + + /* Write the lower part of the Program RAM */ + logError(0, "%s 3) PREPARING DATA FOR FLASH BURN:\n", tag); + + res = fillMemory(FLASH_ADDR_CODE, fw.data, fw.data_size); + if (res < 0) { + logError(1, "%s Error During filling the memory! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s Data copy COMPLETED!\n\n", tag); + + logError(0, "%s 4) ERASE FLASH:\n", tag); + res = wait_for_flash_ready(); + if (res < 0) { + logError(1, "%s Flash not ready! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + logError(0, "%s Command erase ...\n", tag); + cmd = FLASH_CMD_ERASE; + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s Error during erasing flash! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (ERROR_I2C_W | ERROR_FLASH_BURN_FAILED); + } + + res = wait_for_flash_ready(); + if (res < 0) { + logError(1, "%s Flash not ready 2! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + logError(0, "%s Flash erase COMPLETED!\n\n", tag); + + logError(0, "%s 5) BURN FLASH:\n", tag); + logError(0, "%s Command burn ...\n", tag); + cmd = FLASH_CMD_BURN; + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s Error during burning data! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (ERROR_I2C_W | ERROR_FLASH_BURN_FAILED); + } + + res = wait_for_flash_ready(); + if (res < 0) { + logError(1, "%s Flash not ready! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + logError(0, "%s Flash burn COMPLETED!\n\n", tag); + + logError(0, "%s 6) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s system reset COMPLETED!\n\n", tag); + + logError(0, "%s 7) FINAL CHECK:\n", tag); + res = readChipInfo(0); + if (res < 0) { + logError(1, "%s flash_burn: Unable to retrieve Chip INFO! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + if ((ftsInfo.u16_fwVer != fw.fw_ver) && (ftsInfo.u16_cfgId != fw.config_id)) { + logError(1, "%s Firmware in the chip different from the one that was burn! fw: %x != %x , conf: %x != %x\n", tag, ftsInfo.u16_fwVer, fw.fw_ver, ftsInfo.u16_cfgId, fw.config_id); + return ERROR_FLASH_BURN_FAILED; + } + + logError(0, "%s Final check OK! fw: %02X , conf: %02X\n", tag, ftsInfo.u16_fwVer, ftsInfo.u16_cfgId); + + return OK; +} + +int flashProcedure(const char *path, int force, int keep_cx) +{ + Firmware fw; + int res; + + fw.data = NULL; + logError(0, "%s Reading Fw file...\n", tag); + res = readFwFile(path, &fw, keep_cx); + if (res < OK) { + logError(1, "%s flashProcedure: ERROR %02X\n", tag, (res | ERROR_FLASH_PROCEDURE)); + kfree(fw.data); + return (res | ERROR_FLASH_PROCEDURE); + } + logError(0, "%s Fw file read COMPLETED!\n", tag); + + logError(0, "%s Starting flashing procedure...\n", tag); + res = flash_burn(fw, force); + if (res < OK && res != (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED)) { + logError(1, "%s flashProcedure: ERROR %02X\n", tag, ERROR_FLASH_PROCEDURE); + kfree(fw.data); + return (res | ERROR_FLASH_PROCEDURE); + } + logError(0, "%s flashing procedure Finished!\n", tag); + kfree(fw.data); + + /* cleanUp(0); //after talking with Kusuma, the SenseOn should be issued only at the very end of the initialization process, if senso on here it can trigger autotune protection */ + return res; +} + +#else + +int wait_for_flash_ready(u8 type) +{ + u8 cmd[2] = {FLASH_CMD_READ_REGISTER, type}; + u8 readData; + int i, res = -1; + + logError(0, "%s Waiting for flash ready ...\n", tag); + for (i = 0; i < FLASH_RETRY_COUNT && res != 0; i++) { + if (fts_readCmd(cmd, sizeof (cmd), &readData, 1) < 0) { + logError(1, "%s wait_for_flash_ready: ERROR % 02X\n", tag, ERROR_I2C_W); + } else{ + res = readData & 0x80; + /* logError(0, "%s flash status = %d \n", tag, res); */ + } + msleep(FLASH_WAIT_BEFORE_RETRY); + } + + if (i == FLASH_RETRY_COUNT && res != 0) { + logError(1, "%s Wait for flash TIMEOUT! ERROR %02X\n", tag, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + + logError(0, "%s Flash READY!\n", tag); + return OK; +} + +int fts_warm_boot(void) +{ + + u8 cmd[4] = {FTS_CMD_HW_REG_W, 0x00, 0x00, WARM_BOOT_VALUE}; /* write the command to perform the warm boot */ + u16ToU8_be(ADDR_WARM_BOOT, &cmd[1]); + + logError(0, "%s Command warm boot ...\n", tag); + if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { + logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + logError(0, "%s Warm boot DONE!\n", tag); + + return OK; +} + +int parseBinFile(const char *pathToFile, Firmware *fwData, int keep_cx) +{ + + int fd = -1; + int dimension, index = 0; + u32 temp; + u8 *data; + int res, i, fw_size; + +#ifndef FW_H_FILE + const struct firmware *fw = NULL; + struct device *dev = NULL; + dev = getDev(); + + if (dev != NULL) + fd = request_firmware(&fw, pathToFile, dev); + else { + logError(1, "%s parseBinFile: No device found! ERROR %02X\n", ERROR_FILE_PARSE); + return ERROR_FILE_PARSE; + } + + fw_size = fw->size; +#else +fd = 0; +fw_size = SIZE_NAME; +#endif + + if (fd == 0 && fw_size > 0) { /* the file should contain at least the header plus the content_crc */ + if (fw_size < FW_HEADER_SIZE+FW_BYTES_ALIGN) { + logError(1, "%s parseBinFile: Read only %d instead of %d... ERROR %02X\n", tag, fw_size, FW_HEADER_SIZE+FW_BYTES_ALIGN, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } else { + /* start parsing of bytes */ +#ifndef FW_H_FILE + data = (u8 *) (fw->data); +#else + data = (u8 *) (ARRAY_NAME); +#endif + u8ToU32(&data[index], &temp); + if (temp != FW_HEADER_SIGNATURE) { + logError(1, "%s parseBinFile: Wrong Signature %08X ... ERROR %02X\n", tag, temp, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + logError(0, "%s parseBinFile: Fw Signature OK!\n", tag); + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + if (temp != FW_FTB_VER) { + logError(1, "%s parseBinFile: Wrong ftb_version %08X ... ERROR %02X\n", tag, temp, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + logError(0, "%s parseBinFile: ftb_version OK!\n", tag); + index += FW_BYTES_ALIGN; + if (data[index] != DCHIP_ID_0 || data[index+1] != DCHIP_ID_1) { + logError(1, "%s parseBinFile: Wrong target %02X != %02X %02X != %02X ... ERROR %08X\n", tag, data[index], DCHIP_ID_0, data[index+1], DCHIP_ID_1, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + logError(1, "%s parseBinFile: Fw ID = %08X\n", tag, temp); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->fw_ver = temp; + logError(1, "%s parseBinFile: FILE Fw Version = %04X\n", tag, fwData->fw_ver); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->config_id = temp; + logError(1, "%s parseBinFile: FILE Config ID = %08X\n", tag, temp); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + logError(1, "%s parseBinFile: Config Version = %08X\n", tag, temp); + + index += FW_BYTES_ALIGN*2; /* skip reserved data */ + + index += FW_BYTES_ALIGN; + logError(1, "%s parseBinFile: File External Release = ", tag); + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { + fwData->externalRelease[i] = data[index++]; + logError(1, "%02X ", fwData->externalRelease[i]); + } + logError(1, "\n"); + + /* index += FW_BYTES_ALIGN; */ + u8ToU32(&data[index], &temp); + fwData->sec0_size = temp; + logError(1, "%s parseBinFile: sec0_size = %08X (%d bytes)\n", tag, fwData->sec0_size, fwData->sec0_size); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->sec1_size = temp; + logError(1, "%s parseBinFile: sec1_size = %08X (%d bytes)\n", tag, fwData->sec1_size, fwData->sec1_size); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->sec2_size = temp; + logError(1, "%s parseBinFile: sec2_size = %08X (%d bytes)\n", tag, fwData->sec2_size, fwData->sec2_size); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->sec3_size = temp; + logError(1, "%s parseBinFile: sec3_size = %08X (%d bytes)\n", tag, fwData->sec3_size, fwData->sec3_size); + + index += FW_BYTES_ALIGN; /* skip header crc */ + + if (!keep_cx) { + dimension = fwData->sec0_size + fwData->sec1_size + fwData->sec2_size + fwData->sec3_size; + temp = fw_size; + } else { + dimension = fwData->sec0_size + fwData->sec1_size; /* sec2 may contain cx data (future implementation) sec3 atm not used */ + temp = fw_size - fwData->sec2_size - fwData->sec3_size; + } + + if (dimension+FW_HEADER_SIZE+FW_BYTES_ALIGN != temp) { + logError(1, "%s parseBinFile: Read only %d instead of %d... ERROR %02X\n", tag, fw_size, dimension+FW_HEADER_SIZE+FW_BYTES_ALIGN, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + + fwData->data = (u8 *) kmalloc(dimension * sizeof (u8), GFP_KERNEL); + if (fwData->data == NULL) { + logError(1, "%s parseBinFile: ERROR %02X\n", tag, ERROR_ALLOC); + res = ERROR_ALLOC; + goto END; + } + + index += FW_BYTES_ALIGN; + memcpy(fwData->data, &data[index], dimension); + fwData->data_size = dimension; + + logError(0, "%s READ FW DONE %d bytes!\n", tag, fwData->data_size); + res = OK; + goto END; + } + } else { + logError(1, "%s parseBinFile: File Not Found! ERROR %02X\n", tag, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + +END: +#ifndef FW_H_FILE + release_firmware(fw); +#endif + return res; +} + +int readFwFile(const char *path, Firmware *fw, int keep_cx) +{ + int res; + + res = parseBinFile(path, fw, keep_cx); + if (res < OK) { + logError(1, "%s readFwFile: ERROR %02X\n", tag, ERROR_MEMH_READ); + return (res | ERROR_MEMH_READ); + } + + return OK; + +} + +int flash_unlock(void) +{ + u8 cmd[3] = {FLASH_CMD_UNLOCK, FLASH_UNLOCK_CODE0, FLASH_UNLOCK_CODE1}; /* write the command to perform the unlock */ + + logError(0, "%s Command unlock ...\n", tag); + if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { + logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + /* msleep(FLASH_WAIT_TIME); */ + logError(0, "%s Unlock flash DONE!\n", tag); + + return OK; + +} + +int flash_erase_unlock(void) +{ + u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_UNLOCK_CODE0, FLASH_ERASE_UNLOCK_CODE1}; /* write the command to perform the unlock for erasing the flash */ + + logError(0, "%s Try to erase unlock flash...\n", tag); + + logError(0, "%s Command erase unlock ...\n", tag); + if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { + logError(1, "%s flash_erase_unlock: ERROR % 02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + logError(0, "%s Erase Unlock flash DONE!\n", tag); + + return OK; + +} + +int flash_full_erase(void) +{ + + int status; + u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_CODE0, FLASH_ERASE_CODE1}; + /* write the command to erase the flash */ + + logError(0, "%s Command full erase sent ...\n", tag); + if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { + logError(1, "%s flash_full_erase: ERROR % 02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + status = wait_for_flash_ready(FLASH_ERASE_CODE0); + + if (status != OK) { + logError(1, "%s flash_full_erase: ERROR % 02X\n", tag, ERROR_FLASH_NOT_READY); + return (status | ERROR_FLASH_NOT_READY); + /* Flash not ready within the chosen time, better exit! */ + } + + logError(0, "%s Full Erase flash DONE!\n", tag); + + return OK; + +} + +int start_flash_dma(void) +{ + int status; + u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_DMA_CODE0, FLASH_DMA_CODE1}; + /* write the command to erase the flash */ + + logError(0, "%s Command flash DMA ...\n", tag); + if (fts_writeCmd(cmd, sizeof (cmd)) < 0) { + logError(1, "%s start_flash_dma: ERROR % 02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + status = wait_for_flash_ready(FLASH_DMA_CODE0); + + if (status != OK) { + logError(1, "%s start_flash_dma: ERROR % 02X\n", tag, ERROR_FLASH_NOT_READY); + return (status | ERROR_FLASH_NOT_READY); + /* Flash not ready within the chosen time, better exit! */ + } + + logError(0, "%s flash DMA DONE!\n", tag); + + return OK; +} + +int fillFlash(u32 address, u8 *data, int size) +{ + + int remaining = size; + int toWrite = 0; + int byteBlock = 0; + int wheel = 0; + u32 addr = 0; + int res; + int delta; + + u8 *buff = (u8 *) kmalloc((DMA_CHUNK + 3) * sizeof (u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s fillFlash: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + byteBlock = 0; + addr = 0; + while (byteBlock < FLASH_CHUNK && remaining > 0) { + buff[0] = FLASH_CMD_WRITE_64K; + if (remaining >= DMA_CHUNK) { + if ((byteBlock + DMA_CHUNK) <= FLASH_CHUNK) { + /* logError(1, "%s fillFlash: 1\n", tag); */ + toWrite = DMA_CHUNK; + remaining -= DMA_CHUNK; + byteBlock += DMA_CHUNK; + } else { + /* logError(1, "%s fillFlash: 2\n", tag); */ + delta = FLASH_CHUNK - byteBlock; + toWrite = delta; + remaining -= delta; + byteBlock += delta; + } + } else { + if ((byteBlock + remaining) <= FLASH_CHUNK) { + /* logError(1, "%s fillFlash: 3\n", tag); */ + toWrite = remaining; + byteBlock += remaining; + remaining = 0; + + } else { + /* logError(1, "%s fillFlash: 4\n", tag); */ + delta = FLASH_CHUNK - byteBlock; + toWrite = delta; + remaining -= delta; + byteBlock += delta; + } + } + + buff[1] = (u8) ((addr & 0x0000FF00) >> 8); + buff[2] = (u8) (addr & 0x000000FF); + memcpy(&buff[3], data, toWrite); + if (fts_writeCmd(buff, 3 + toWrite) < 0) { + logError(1, "%s fillFlash: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + addr += toWrite; + data += toWrite; + } + + kfree(buff); + + /* configuring the DMA */ + byteBlock = byteBlock / 4 - 1; + + buff = (u8 *) kmalloc((9) * sizeof (u8), GFP_KERNEL); + buff[0] = FLASH_CMD_WRITE_REGISTER; + buff[1] = FLASH_DMA_CONFIG; + buff[2] = 0x00; + buff[3] = 0x00; + + addr = address + ((wheel * FLASH_CHUNK)/4); + buff[4] = (u8) ((addr & 0x000000FF)); + buff[5] = (u8) ((addr & 0x0000FF00) >> 8); + buff[6] = (u8) (byteBlock & 0x000000FF); + buff[7] = (u8) ((byteBlock & 0x0000FF00) >> 8); + buff[8] = 0x00; + + logError(0, "%s Command = %02X , address = %02X %02X, words = %02X %02X\n", tag, buff[0], buff[5], buff[4], buff[7], buff[6]); + if (fts_writeCmd(buff, 9) < OK) { + logError(1, "%s Error during filling Flash! ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + /* msleep(FLASH_WAIT_TIME); */ + res = start_flash_dma(); + if (res < OK) { + logError(1, "%s Error during flashing DMA! ERROR %02X\n", tag, res); + return res; + } + wheel++; + } + return OK; +} + +int flash_burn(Firmware fw, int force_burn) +{ + int res; + + if (!force_burn) { + for (res = EXTERNAL_RELEASE_INFO_SIZE-1; res >= 0; res--) { + if (fw.externalRelease[res] > ftsInfo.u8_extReleaseInfo[res]) + goto start; + } + logError(1, "%s flash_burn: Firmware in the chip newer or equal to the one to burn! NO UPDATE ERROR %02X\n", tag, ERROR_FW_NO_UPDATE); + return (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED); + } + + /* programming procedure start */ +start: + logError(0, "%s Programming Procedure for flashing started:\n\n", tag); + + logError(0, "%s 1) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED!\n", tag); + if (res != (ERROR_SYSTEM_RESET_FAIL | ERROR_TIMEOUT)) + /* if there is no firmware i will not get the controller + *ready event and there will be a timeout but i can keep going, + *but if there is an I2C error i have to exit + */ + return (res | ERROR_FLASH_BURN_FAILED); + } else + logError(0, "%s system reset COMPLETED!\n\n", tag); + + logError(0, "%s 2) WARM BOOT:\n", tag); + res = fts_warm_boot(); + if (res < OK) { + logError(1, "%s warm boot FAILED!\n", tag); + return (res | ERROR_FLASH_BURN_FAILED); + } else + logError(0, "%s warm boot COMPLETED!\n\n", tag); + + /* msleep(FLASH_WAIT_TIME); */ + logError(0, "%s 3) FLASH UNLOCK:\n", tag); + res = flash_unlock(); + if (res < OK) { + logError(1, "%s flash unlock FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash unlock COMPLETED!\n\n", tag); + + /* msleep(200); */ + logError(0, "%s 4) FLASH ERASE UNLOCK:\n", tag); + res = flash_erase_unlock(); + if (res < 0) { + logError(1, "%s flash unlock FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash unlock COMPLETED!\n\n", tag); + + /* msleep(FLASH_WAIT_TIME); */ + logError(0, "%s 5) FLASH ERASE:\n", tag); + res = flash_full_erase(); + if (res < 0) { + logError(1, "%s flash erase FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash erase COMPLETED!\n\n", tag); + + /* msleep(FLASH_WAIT_TIME); */ + logError(0, "%s 6) LOAD PROGRAM:\n", tag); + res = fillFlash(FLASH_ADDR_CODE, &fw.data[0], fw.sec0_size); + if (res < OK) { + logError(1, "%s load program ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(1, "%s load program DONE!\n", tag); + + logError(0, "%s 7) LOAD CONFIG:\n", tag); + res = fillFlash(FLASH_ADDR_CONFIG, &(fw.data[fw.sec0_size]), fw.sec1_size); + if (res < OK) { + logError(1, "%s load config ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(1, "%s load config DONE!\n", tag); + + logError(0, "%s Flash burn COMPLETED!\n\n", tag); + + logError(0, "%s 8) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s system reset COMPLETED!\n\n", tag); + + logError(0, "%s 9) FINAL CHECK:\n", tag); + res = readChipInfo(0); + if (res < 0) { + logError(1, "%s flash_burn: Unable to retrieve Chip INFO! ERROR %02X\n", tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + for (res = 0; res < EXTERNAL_RELEASE_INFO_SIZE; res++) { + if (fw.externalRelease[res] != ftsInfo.u8_extReleaseInfo[res]) { + /* external release is prined during readChipInfo */ + logError(1, "%s Firmware in the chip different from the one that was burn! fw: %x != %x , conf: %x != %x\n", tag, ftsInfo.u16_fwVer, fw.fw_ver, ftsInfo.u16_cfgId, fw.config_id); + return ERROR_FLASH_BURN_FAILED; + } + } + + logError(0, "%s Final check OK! fw: %02X, conf: %02X\n", tag, ftsInfo.u16_fwVer, ftsInfo.u16_cfgId); + + return OK; +} + +int flashProcedure(const char *path, int force, int keep_cx) +{ + Firmware fw; + int res; + + fw.data = NULL; + logError(0, "%s Reading Fw file...\n", tag); + res = readFwFile(path, &fw, keep_cx); + if (res < OK) { + logError(1, "%s flashProcedure: ERROR %02X\n", tag, (res | ERROR_FLASH_PROCEDURE)); + kfree(fw.data); + return (res | ERROR_FLASH_PROCEDURE); + } + logError(0, "%s Fw file read COMPLETED!\n", tag); + + logError(0, "%s Starting flashing procedure...\n", tag); + res = flash_burn(fw, force); + if (res < OK && res != (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED)) { + logError(1, "%s flashProcedure: ERROR %02X\n", tag, ERROR_FLASH_PROCEDURE); + kfree(fw.data); + return (res | ERROR_FLASH_PROCEDURE); + } + logError(0, "%s flashing procedure Finished!\n", tag); + kfree(fw.data); + return res; +} + +#endif diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFlash.h b/drivers/input/touchscreen/st/fts_lib/ftsFlash.h new file mode 100644 index 000000000000..69635e07a9f4 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsFlash.h @@ -0,0 +1,79 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS API for Flashing the IC * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsSoftware.h" + +/* Flash possible status */ +#define FLASH_READY 0 +#define FLASH_BUSY 1 +#define FLASH_UNKNOWN -1 + +#define FLASH_STATUS_BYTES 1 + +/* Flash timing parameters */ +#define FLASH_RETRY_COUNT 1000 +#define FLASH_WAIT_BEFORE_RETRY 50 /* ms */ + +#define FLASH_WAIT_TIME 200 /* ms */ + +/* PATHS FW FILES */ +/* #define PATH_FILE_FW "fw.memh" */ +#ifdef FTM3_CHIP +#define PATH_FILE_FW "st_fts.bin" +#else +#define PATH_FILE_FW "st_fts.ftb" /* new bin file structure */ +#endif + +#ifndef FTM3_CHIP +#define FLASH_CHUNK (64*1024) +#define DMA_CHUNK 32 +#endif + +typedef struct { + u8 *data; + u16 fw_ver; + u16 config_id; + u8 externalRelease[EXTERNAL_RELEASE_INFO_SIZE]; + int data_size; +#ifndef FTM3_CHIP + u32 sec0_size; + u32 sec1_size; + u32 sec2_size; + u32 sec3_size; +#endif +} Firmware; + +#ifdef FTM3_CHIP +int flash_status(void); +int flash_status_ready(void); +int wait_for_flash_ready(void); +int parseBinFile(const char *pathToFile, u8 **data, int *length, int dimension); +/* int parseMemhFile(const char* pathToFile, u8** data, int* length, int dimension); */ +#else +int wait_for_flash_ready(u8 type); +int fts_warm_boot(void); +int parseBinFile(const char *pathToFile, Firmware *fw, int keep_cx); +int flash_erase_unlock(void); +int flash_full_erase(void); +int start_flash_dma(void); +int fillFlash(u32 address, u8 *data, int size); +#endif + +int flash_unlock(void); +int fillMemory(u32 address, u8 *data, int size); +int getFirmwareVersion(u16 *fw_vers, u16 *config_id); +int readFwFile(const char *path, Firmware *fw, int keep_cx); +int flash_burn(Firmware fw, int force_burn); +int flashProcedure(const char *path, int force, int keep_cx); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFrame.c b/drivers/input/touchscreen/st/fts_lib/ftsFrame.c new file mode 100644 index 000000000000..c502559319a0 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsFrame.c @@ -0,0 +1,569 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS functions for getting frames * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTool.h" +#include "ftsTime.h" + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <stdarg.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/time.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ + +static char tag[8] = "[ FTS ]\0"; +static int sense_len, force_len; + +/*int getOffsetFrame(u16 address, u16 *offset) +{ + + u8 data[2]; + u8 cmd = { FTS_CMD_FRAMEBUFFER_R }; + + if (readCmdU16(cmd, address, data, OFFSET_LENGTH, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s getOffsetFrame: ERROR %02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + else { + u8ToU16(data, offset); + logError(0, "%s %s", tag, printHex("Offest = ", data, OFFSET_LENGTH)); + return OK; + } + +}*/ + +int getChannelsLength(void) +{ + + int ret; + u8 *data = (u8 *)kmalloc(2*sizeof(u8), GFP_KERNEL); + + if (data == NULL) { + logError(1, "%s getChannelsLength: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = readB2(ADDR_SENSE_LEN, data, 2); + if (ret < OK) { + logError(1, "%s getChannelsLength: ERROR %02X\n", tag, ERROR_READ_B2); + return (ret|ERROR_READ_B2); + } + + sense_len = (int)data[0]; + force_len = (int)data[1]; + + logError(0, "%s Force_len = %d Sense_Len = %d\n", tag, force_len, sense_len); + + kfree(data); + + return OK; +} + +int getFrameData(u16 address, int size, short **frame) +{ + int i, j, ret; + u8 *data = (u8 *)kmalloc(size*sizeof(u8), GFP_KERNEL); + if (data == NULL) { + logError(1, "%s getFrameData: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, data, size, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, "%s getFrameData: ERROR %02X\n", tag, ERROR_I2C_R); + kfree(data); + return ERROR_I2C_R; + } + j = 0; + for (i = 0; i < size; i += 2) { + (*frame)[j] = (short)((data[i + 1] << 8) + data[i]); + j++; + } + kfree(data); + return OK; +} + +/*int getMSFrame(u16 type, short **frame, int keep_first_row) +{ + u16 offset; + int size, ret; + + if (getSenseLen() == 0 || getForceLen() == 0) { + ret=getChannelsLength(); + if (ret<OK) { + logError(1, "%s getMSFrame: ERROR %02X\n", tag,ERROR_CH_LEN); + return (ret|ERROR_CH_LEN); + } + } + + ret = getOffsetFrame(type, &offset); + if (ret<OK) { + logError(1, "%s getMSFrame: ERROR %02X\n", tag, ERROR_GET_OFFSET); + return (ret | ERROR_GET_OFFSET); + } + + switch (type) { + case ADDR_RAW_TOUCH: + case ADDR_FILTER_TOUCH: + case ADDR_NORM_TOUCH: + case ADDR_CALIB_TOUCH: + if (keep_first_row ==1) + size = ((force_len+1)*sense_len); + else { + size = ((force_len)*sense_len); + offset+= (sense_len * BYTES_PER_NODE); + } + break; + + default: + logError(1, "%s getMSFrame: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *frame = (short*)kmalloc(size*sizeof(short), GFP_KERNEL); + if (*frame==NULL) { + logError(1, "%s getMSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret=getFrameData(offset, size*BYTES_PER_NODE, frame); + if (ret<OK) { + logError(1, "%s getMSFrame: ERROR %02X\n", tag, ERROR_GET_FRAME_DATA); + return (ret|ERROR_GET_FRAME_DATA); + } + + logError(0, "%s Frame acquired!\n", tag); + return size; + +} + +int getMSKeyFrame(u16 type, short **frame) { + u16 offset; + int size, ret; + u16 address; + MutualSenseData data; + + if (type != ADDR_RAW_MS_KEY) { + logError(1, "%s getMSKeyFrame: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = getOffsetFrame(type, &offset); + if (ret<OK) { + logError(1, "%s getMSKeyFrame: ERROR %02X\n", tag, ERROR_GET_OFFSET); + return (ret | ERROR_GET_OFFSET); + } + + ret = requestCompensationData(MS_KEY); + if (ret < OK) { + logError(1, "%s getMSKeyFrame: readMutualSenseCompensationData ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + return (ret | ERROR_REQU_COMP_DATA); + } + + ret = readCompensationDataHeader(MS_KEY, &(data.header), &address); + if (ret < OK) { + logError(1, "%s getMSKeyFrame: readMutualSenseCompensationData ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + return (ret | ERROR_COMP_DATA_HEADER); + } + + if (data.header.force_node>data.header.sense_node) + size = data.header.force_node; + else + size = data.header.sense_node; + + *frame = (short*)kmalloc(size*sizeof(short), GFP_KERNEL); + if (frame == NULL) { + logError(1, "%s getMSKeyFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = getFrameData(offset, size*BYTES_PER_NODE, frame); + if (ret<OK) { + logError(1, "%s getMSKeyFrame: ERROR %02X\n", tag, ERROR_GET_FRAME_DATA); + return (ret | ERROR_GET_FRAME_DATA); + } + + logError(0, "%s Frame acquired!\n", tag); +} + +int getSSFrame(u16 type, short **frame) { + u16 offset; + int size, ret; + + if (getSenseLen() == 0 || getForceLen() == 0) { + ret = getChannelsLength(); + if (ret<0) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_CH_LEN); + return (ret | ERROR_CH_LEN); + } + } + + switch (type) { + case ADDR_RAW_HOVER_FORCE: + case ADDR_FILTER_HOVER_FORCE: + case ADDR_NORM_HOVER_FORCE: + case ADDR_CALIB_HOVER_FORCE: + case ADDR_RAW_PRX_FORCE: + case ADDR_FILTER_PRX_FORCE: + case ADDR_NORM_PRX_FORCE: + case ADDR_CALIB_PRX_FORCE: + size = ((force_len)* 1); + break; + + case ADDR_RAW_HOVER_SENSE: + case ADDR_FILTER_HOVER_SENSE: + case ADDR_NORM_HOVER_SENSE: + case ADDR_CALIB_HOVER_SENSE: + case ADDR_RAW_PRX_SENSE: + case ADDR_FILTER_PRX_SENSE: + case ADDR_NORM_PRX_SENSE: + case ADDR_CALIB_PRX_SENSE: + size = ((1)*sense_len); + break; + + default: + logError(1, "%s getSSFrame: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + + } + + ret = getOffsetFrame(type, &offset); + if (ret<OK) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_GET_OFFSET); + return (ret | ERROR_GET_OFFSET); + } + + *frame = (short*)kmalloc(size*sizeof(short), GFP_KERNEL); + if (*frame == NULL) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = getFrameData(offset, size*BYTES_PER_NODE, frame); + if (ret<OK) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_GET_FRAME_DATA); + return (ret | ERROR_GET_FRAME_DATA); + } + + logError(0, "%s Frame acquired!\n", tag); + return size; + +} + +int getNmsFrame(u16 type, short ***frames, int *size, int keep_first_row, int fs, int n) { + int i; + StopWatch global, local; + int temp; + + *frames = (short **)kmalloc(n*sizeof(short *), GFP_KERNEL); + + if (*frames == NULL) { + logError(1, "%s getNmsFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + fs = (1*1000 / fs) ; + + startStopWatch(&global); + for (i = 0; i < n; i++) { + + startStopWatch(&local); + + *size = getMSFrame(type, ((*frames)+i), keep_first_row); + if (*size < OK) { + logError(1, "%s getNFrame: getFrame failed\n",tag); + return *size; + } + + stopStopWatch(&local); + temp = elapsedMillisecond(&local); + logError(0, "%s Iteration %d performed in %d ms... the process wait for %ld ms\n\n", tag, i, temp, (unsigned long)(fs - temp)); + + if (temp < fs) + msleep((unsigned long)(fs - temp)); + + } + + stopStopWatch(&global); + temp = elapsedMillisecond(&global); + logError(0, "%s Global Iteration performed in %d ms\n", tag, temp); + temp /= n; + logError(0, "%s Mean Iteration performed in %d ms\n", tag, temp); + return (1000 / (temp)); + +}*/ + +int getSenseLen(void) +{ + int ret; + if (sense_len != 0) + return sense_len; + ret = getChannelsLength(); + if (ret < OK) + return ret; + else + return sense_len; +} + +int getForceLen(void) +{ + int ret; + if (force_len != 0) + return force_len; + ret = getChannelsLength(); + if (ret < OK) + return ret; + else + return force_len; +} + +int requestFrame(u16 type) +{ + int retry = 0; + int ret; + u16 answer; + + int event_to_search[1]; + u8 readEvent[FIFO_EVENT_SIZE]; + + u8 cmd[3] = { FTS_CMD_REQU_FRAME_DATA, 0x00, 0x00 }; + /* B7 is the command for asking frame data */ + event_to_search[0] = (int)EVENTID_FRAME_DATA_READ; + + u16ToU8(type, &cmd[1]); + + while (retry < FRAME_DATA_READ_RETRY) { + logError(0, "%s %s", tag, printHex("Command = ", cmd, 3)); + ret = fts_writeFwCmd(cmd, 3); + /* send the request to the chip to load in memory the Frame Data */ + if (ret < OK) { + logError(1, "%s requestFrame: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + ret = pollForEvent(event_to_search, 1, readEvent, TIMEOUT_REQU_COMP_DATA); + if (ret < OK) { + logError(0, "%s Event did not Found at %d attemp!\n", tag, retry + 1); + retry += 1; + } else { + retry = 0; + break; + } + } + + if (retry == FRAME_DATA_READ_RETRY) { + logError(1, "%s requestFrame: ERROR %02X\n", tag, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + + u8ToU16_le(&readEvent[1], &answer); + + if (answer == type) + return OK; + logError(1, "%s The event found has a different type of Frame data ERROR %02X\n", tag, ERROR_DIFF_COMP_TYPE); + return ERROR_DIFF_COMP_TYPE; + +} + +int readFrameDataHeader(u16 type, DataHeader *header) +{ + + u16 offset = ADDR_FRAMEBUFFER_DATA; + u16 answer; + u8 data[FRAME_DATA_HEADER]; + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, offset, data, FRAME_DATA_HEADER, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s readFrameDataHeader: ERROR %02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + logError(0, "%s Read Data Header done!\n", tag); + + if (data[0] != FRAME_HEADER_SIGNATURE) { + logError(1, "%s readFrameDataHeader: ERROR %02X The Header Signature was wrong! %02X != %02X\n", tag, ERROR_WRONG_COMP_SIGN, data[0], HEADER_SIGNATURE); + return ERROR_WRONG_COMP_SIGN; + } + + u8ToU16_le(&data[1], &answer); + + if (answer != type) { + logError(1, "%s readFrameDataHeader: ERROR %02X\n", tag, ERROR_DIFF_COMP_TYPE); + return ERROR_DIFF_COMP_TYPE; + } + + logError(0, "%s Type of Frame data OK!\n", tag); + + header->type = type; + header->force_node = (int)data[4]; + header->sense_node = (int)data[5]; + + return OK; + +} + +int getMSFrame2(u16 type, MutualSenseFrame *frame) +{ + u16 offset = ADDR_FRAMEBUFFER_DATA+FRAME_DATA_HEADER; + int size, ret; + + if (!(type == MS_TOUCH_ACTIVE || type == MS_TOUCH_LOW_POWER || type == MS_TOUCH_ULTRA_LOW_POWER || type == MS_KEY)) { + logError(1, "%s getMSFrame: Choose a MS type of frame data ERROR %02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestFrame(type); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensationData: ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + return (ret | ERROR_REQU_COMP_DATA); + } + + ret = readFrameDataHeader(type, &(frame->header)); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensationData: ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + return (ret | ERROR_COMP_DATA_HEADER); + } + + switch (type) { + case MS_TOUCH_ACTIVE: + case MS_TOUCH_LOW_POWER: + case MS_TOUCH_ULTRA_LOW_POWER: + size = frame->header.force_node*frame->header.sense_node; + break; + case MS_KEY: + if (frame->header.force_node > frame->header.sense_node) + /* or use directly the number in the ftsChip */ + size = frame->header.force_node; + else + size = frame->header.sense_node; + frame->header.force_node = 1; + frame->header.sense_node = size; + break; + + default: + logError(1, "%s getMSFrame: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + frame->node_data = (short *)kmalloc(size*sizeof(short), GFP_KERNEL); + if (frame->node_data == NULL) { + logError(1, "%s getMSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = getFrameData(offset, size*BYTES_PER_NODE, &(frame->node_data)); + if (ret < OK) { + logError(1, "%s getMSFrame: ERROR %02X\n", tag, ERROR_GET_FRAME_DATA); + return (ret | ERROR_GET_FRAME_DATA); + } + /* if you want to access one node i,j, you should compute the offset like: offset = i*columns + j = > frame[i, j] */ + + logError(0, "%s Frame acquired!\n", tag); + frame->node_data_size = size; + return size; /* return the number of data put inside frame */ + +} + +int getSSFrame2(u16 type, SelfSenseFrame *frame) +{ + u16 offset = ADDR_FRAMEBUFFER_DATA + FRAME_DATA_HEADER; + int size, ret; + short *temp = NULL; + + if (!(type == SS_TOUCH || type == SS_KEY || type == SS_HOVER || type == SS_PROXIMITY)) { + logError(1, "%s getSSFrame: Choose a SS type of frame data ERROR %02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestFrame(type); + if (ret < 0) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_REQU_COMP_DATA); + return (ret | ERROR_REQU_COMP_DATA); + } + + ret = readFrameDataHeader(type, &(frame->header)); + if (ret < 0) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_COMP_DATA_HEADER); + return (ret | ERROR_COMP_DATA_HEADER); + } + + switch (type) { + case SS_TOUCH: + case SS_HOVER: + case SS_PROXIMITY: + size = frame->header.force_node+frame->header.sense_node; + break; + + default: + logError(1, "%s getSSFrame: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + temp = (short *)kmalloc(size*sizeof(short), GFP_KERNEL); + if (temp == NULL) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = getFrameData(offset, size*BYTES_PER_NODE, &temp); + if (ret < OK) { + logError(1, "%s getMSFrame: ERROR %02X\n", tag, ERROR_GET_FRAME_DATA); + return (ret | ERROR_GET_FRAME_DATA); + } + + frame->force_data = (short *)kmalloc(frame->header.force_node*sizeof(short), GFP_KERNEL); + if (frame->force_data == NULL) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + memcpy(frame->force_data, temp, frame->header.force_node*sizeof(short)); + + frame->sense_data = (short *)kmalloc(frame->header.sense_node*sizeof(short), GFP_KERNEL); + if (frame->sense_data == NULL) { + logError(1, "%s getSSFrame: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + memcpy(frame->sense_data, &temp[frame->header.force_node], frame->header.sense_node*sizeof(short)); + + logError(0, "%s Frame acquired!\n", tag); + kfree(temp); + return size; /* return the number of data put inside frame */ + +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsFrame.h b/drivers/input/touchscreen/st/fts_lib/ftsFrame.h new file mode 100644 index 000000000000..89f4e5080a53 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsFrame.h @@ -0,0 +1,49 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS functions for getting frames * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsSoftware.h" + +/* Number of data bytes for each node */ +#define BYTES_PER_NODE 2 +#define OFFSET_LENGTH 2 +#define FRAME_DATA_HEADER 8 +#define FRAME_HEADER_SIGNATURE 0xB5 +#define FRAME_DATA_READ_RETRY 2 + +typedef struct { + DataHeader header; + short *node_data; + int node_data_size; +} MutualSenseFrame; + +typedef struct { + DataHeader header; + short *force_data; + short *sense_data; +} SelfSenseFrame; + +/* int getOffsetFrame(u16 address, u16 *offset); */ +int getChannelsLength(void); +int getFrameData(u16 address, int size, short **frame); +/* int getMSFrame(u16 type, short **frame, int keep_first_row); */ +/* int getMSKeyFrame(u16 type, short **frame); */ +/* int getSSFrame(u16 type, short **frame); */ +/* int getNmsFrame(u16 type, short ***frames, int * sizes, int keep_first_row, int fs, int n); */ +int getSenseLen(void); +int getForceLen(void); +int requestFrame(u16 type); +int readFrameDataHeader(u16 type, DataHeader *header); +int getMSFrame2(u16 type, MutualSenseFrame *frame); +int getSSFrame2(u16 type, SelfSenseFrame *frame); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsGesture.c b/drivers/input/touchscreen/st/fts_lib/ftsGesture.c new file mode 100644 index 000000000000..ee97a417d4cb --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsGesture.c @@ -0,0 +1,393 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS Gesture Utilities * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsSoftware.h" +#include "ftsError.h" +#include "ftsGesture.h" +#include "ftsIO.h" +#include "ftsTool.h" + +static char tag[8] = "[ FTS ]\0"; + +static u8 gesture_mask[GESTURE_MASK_SIZE] = { 0 }; +static u8 custom_gestures[GESTURE_CUSTOM_NUMBER][GESTURE_CUSTOM_POINTS]; +static u8 custom_gesture_index[GESTURE_CUSTOM_NUMBER] = { 0 }; + +int enableGesture(u8 *mask, int size) +{ + u8 cmd[size+2]; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int i, res; + int event_to_search[4] = {EVENTID_GESTURE, EVENT_TYPE_ENB, 0x00, GESTURE_ENABLE}; + + logError(0, "%s Trying to enable gesture...\n", tag); + cmd[0] = FTS_CMD_GESTURE_CMD; + cmd[1] = GESTURE_ENABLE; + + if (size <= GESTURE_MASK_SIZE) { + if (mask != NULL) { + for (i = 0; i < size; i++) { + cmd[i + 2] = mask[i]; + gesture_mask[i] = gesture_mask[i]|mask[i]; + /* back up of the gesture enabled */ + } + while (i < GESTURE_MASK_SIZE) { + cmd[i + 2] = gesture_mask[i]; + i++; + } + } else { + for (i = 0; i < GESTURE_MASK_SIZE; i++) { + cmd[i + 2] = gesture_mask[i]; + } + } + + res = fts_writeFwCmd(cmd, GESTURE_MASK_SIZE + 2); + if (res < OK) { + logError(1, "%s enableGesture: ERROR %08X\n", tag, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s enableGesture: pollForEvent ERROR %08X\n", tag, res); + return res; + } + + if (readData[4] != 0x00) { + logError(1, "%s enableGesture: ERROR %08X\n", tag, ERROR_GESTURE_ENABLE_FAIL); + return ERROR_GESTURE_ENABLE_FAIL; + } + + logError(0, "%s enableGesture DONE!\n", tag); + return OK; + } else { + logError(1, "%s enableGesture: Size not valid! %d > %d ERROR %08X\n", tag, size, GESTURE_MASK_SIZE); + return ERROR_OP_NOT_ALLOW; + } + +} + +int disableGesture(u8 *mask, int size) +{ + u8 cmd[2+GESTURE_MASK_SIZE]; + u8 readData[FIFO_EVENT_SIZE] = {0}; + u8 temp; + int i, res; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, 0x00, GESTURE_DISABLE }; + + logError(0, "%s Trying to disable gesture...\n", tag); + cmd[0] = FTS_CMD_GESTURE_CMD; + cmd[1] = GESTURE_DISABLE; + + if (size <= GESTURE_MASK_SIZE) { + if (mask != NULL) { + for (i = 0; i < size; i++) { + cmd[i + 2] = mask[i]; + temp = gesture_mask[i] ^ mask[i]; + /* enabled XOR disabled */ + gesture_mask[i] = temp & gesture_mask[i]; + /* disable the gestures that were enabled */ + } + while (i < GESTURE_MASK_SIZE) { + cmd[i + 2] = gesture_mask[i]; + /* disable all the other gesture not specified */ + gesture_mask[i] = 0x00; + i++; + } + } else { + for (i = 0; i < GESTURE_MASK_SIZE; i++) { + cmd[i + 2] = gesture_mask[i]; + } + } + + res = fts_writeFwCmd(cmd, 2 + GESTURE_MASK_SIZE); + if (res < OK) { + logError(1, "%s disableGesture: ERROR %08X\n", tag, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s disableGesture: pollForEvent ERROR %08X\n", tag, res); + return res; + } + + if (readData[4] != 0x00) { + logError(1, "%s disableGesture: ERROR %08X\n", tag, ERROR_GESTURE_ENABLE_FAIL); + return ERROR_GESTURE_ENABLE_FAIL; + } + + logError(0, "%s disableGesture DONE!\n", tag); + return OK; + } else { + logError(1, "%s disableGesture: Size not valid! %d > %d ERROR %08X\n", tag, size, GESTURE_MASK_SIZE); + return ERROR_OP_NOT_ALLOW; + } +} + +int startAddCustomGesture(u8 gestureID) +{ + u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GESTURE_START_ADD, gestureID }; + int res; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GESTURE_START_ADD }; + + res = fts_writeFwCmd(cmd, 3); + if (res < OK) { + logError(1, "%s startAddCustomGesture: Impossible to start adding custom gesture ID = %02X! ERROR %08X\n", tag, gestureID, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s startAddCustomGesture: start add event not found! ERROR %08X\n", tag, res); + return res; + } + + if (readData[2] != gestureID || readData[4] != 0x00) { /* check of gestureID is redundant */ + logError(1, "%s startAddCustomGesture: start add event status not OK! ERROR %08X\n", tag, readData[4]); + return ERROR_GESTURE_START_ADD; + } + + return OK; +} + +int finishAddCustomGesture(u8 gestureID) +{ + u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GESTURE_FINISH_ADD, gestureID }; + int res; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GESTURE_FINISH_ADD }; + + res = fts_writeFwCmd(cmd, 3); + if (res < OK) { + logError(1, "%s finishAddCustomGesture: Impossible to finish adding custom gesture ID = %02X! ERROR %08X\n", tag, gestureID, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s finishAddCustomGesture: finish add event not found! ERROR %08X\n", tag, res); + return res; + } + + if (readData[2] != gestureID || readData[4] != 0x00) { /* check of gestureID is redundant */ + logError(1, "%s finishAddCustomGesture: finish add event status not OK! ERROR %08X\n", tag, readData[4]); + return ERROR_GESTURE_FINISH_ADD; + } + + return OK; + +} + +int loadCustomGesture(u8 *template, u8 gestureID) +{ + int res, i; + int remaining = GESTURE_CUSTOM_POINTS; + int toWrite, offset = 0; + u8 cmd[TEMPLATE_CHUNK + 5]; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GESTURE_DATA_ADD }; + u8 readData[FIFO_EVENT_SIZE] = {0}; + + logError(0, "%s Starting adding custom gesture procedure...\n", tag); + + res = startAddCustomGesture(gestureID); + if (res < OK) { + logError(1, "%s loadCustomGesture: unable to start adding procedure! ERROR %08X\n", tag, res); + return res; + } + + cmd[0] = FTS_CMD_GESTURE_CMD; + cmd[1] = GESTURE_DATA_ADD; + cmd[2] = gestureID; + while (remaining > 0) { + if (remaining > TEMPLATE_CHUNK) { + toWrite = TEMPLATE_CHUNK; + } else { + toWrite = remaining; + } + + cmd[3] = toWrite; + cmd[4] = offset; + for (i = 0; i < toWrite; i++) { + cmd[i + 5] = template[i]; + } + + res = fts_writeFwCmd(cmd, toWrite + 5); + if (res < OK) { + logError(1, "%s loadCustomGesture: unable to start adding procedure! ERROR %08X\n", tag, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s loadCustomGesture: add event not found! ERROR %08X\n", tag, res); + return res; + } + + if (readData[2] != gestureID || readData[4] != 0x00) { /* check of gestureID is redundant */ + logError(1, "%s loadCustomGesture: add event status not OK! ERROR %08X\n", tag, readData[4]); + return ERROR_GESTURE_DATA_ADD; + } + + remaining -= toWrite; + offset += toWrite / 2; + } + + res = finishAddCustomGesture(gestureID); + if (res < OK) { + logError(1, "%s loadCustomGesture: unable to finish adding procedure! ERROR %08X\n", tag, res); + return res; + } + + logError(0, "%s Adding custom gesture procedure DONE!\n", tag); + return OK; + +} + +int reloadCustomGesture(void) +{ + int res, i; + + logError(0, "%s Starting reload Gesture Template...\n", tag); + + for (i = 0; i < GESTURE_CUSTOM_NUMBER; i++) { + if (custom_gesture_index[i] == 1) { + res = loadCustomGesture(custom_gestures[i], GESTURE_CUSTOM_OFFSET+i); + if (res < OK) { + logError(1, "%s reloadCustomGesture: Impossible to load custom gesture ID = %02X! ERROR %08X\n", tag, GESTURE_CUSTOM_OFFSET + i, res); + return res; + } + } + } + + logError(0, "%s Reload Gesture Template DONE!\n", tag); + return OK; + +} + +int enterGestureMode(int reload) +{ + u8 cmd = FTS_CMD_GESTURE_MODE; + int res, ret; + + res = fts_disableInterrupt(); + if (res < OK) { + logError(1, "%s enterGestureMode: ERROR %08X\n", tag, res|ERROR_DISABLE_INTER); + return res | ERROR_DISABLE_INTER; + } + + if (reload == 1) { + + res = reloadCustomGesture(); + if (res < OK) { + logError(1, "%s enterGestureMode: impossible reload custom gesture! ERROR %08X\n", tag, res); + goto END; + } + + res = disableGesture(NULL, 0); + if (res < OK) { + logError(1, "%s enterGestureMode: disableGesture ERROR %08X\n", tag, res); + goto END; + } + + res = enableGesture(NULL, 0); + if (res < OK) { + logError(1, "%s enterGestureMode: enableGesture ERROR %08X\n", tag, res); + goto END; + } + } + + res = fts_writeFwCmd(&cmd, 1); + if (res < OK) { + logError(1, "%s enterGestureMode: enter gesture mode ERROR %08X\n", tag, res); + goto END; + } + + res = OK; +END: + ret = fts_enableInterrupt(); + if (ret < OK) { + logError(1, "%s enterGestureMode: fts_enableInterrupt ERROR %08X\n", tag, res | ERROR_ENABLE_INTER); + res |= ret | ERROR_ENABLE_INTER; + } + + return res; +} + +int addCustomGesture(u8 *data, int size, u8 gestureID) +{ + int index, res, i; + + index = gestureID - GESTURE_CUSTOM_OFFSET; + + logError(0, "%s Starting Custom Gesture Adding procedure...\n", tag); + if (size != GESTURE_CUSTOM_POINTS && gestureID != GES_ID_CUST1 && gestureID != GES_ID_CUST2 && gestureID != GES_ID_CUST3 && gestureID != GES_ID_CUST4 && gestureID && GES_ID_CUST5) { + logError(1, "%s addCustomGesture: Invalid size (%d) or Custom GestureID (%02X)! ERROR %08X\n", tag, size, gestureID, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + for (i = 0; i < GESTURE_CUSTOM_POINTS; i++) { + custom_gestures[index][i] = data[i]; + } + + res = loadCustomGesture(custom_gestures[index], gestureID); + if (res < OK) { + logError(1, "%s addCustomGesture: impossible to load the custom gesture! ERROR %08X\n", tag, res); + return res; + } + + custom_gesture_index[index] = 1; + logError(0, "%s Custom Gesture Adding procedure DONE!\n", tag); + return OK; +} + +int removeCustomGesture(u8 gestureID) +{ + int res, index; + u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GETURE_REMOVE_CUSTOM, gestureID }; + int event_to_search[4] = {EVENTID_GESTURE, EVENT_TYPE_ENB, gestureID, GETURE_REMOVE_CUSTOM }; + u8 readData[FIFO_EVENT_SIZE] = {0}; + + index = gestureID - GESTURE_CUSTOM_OFFSET; + + logError(0, "%s Starting Custom Gesture Removing procedure...\n", tag); + if (gestureID != GES_ID_CUST1 && gestureID != GES_ID_CUST2 && gestureID != GES_ID_CUST3 && gestureID != GES_ID_CUST4 && gestureID && GES_ID_CUST5) { + logError(1, "%s removeCustomGesture: Invalid size (%d) or Custom GestureID (%02X)! ERROR %08X\n", tag, gestureID, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + res = fts_writeFwCmd(cmd, 3);/* when a gesture is removed, it is also disabled automatically */ + if (res < OK) { + logError(1, "%s removeCustomGesture: Impossible to remove custom gesture ID = %02X! ERROR %08X\n", tag, gestureID, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s removeCustomGesture: remove event not found! ERROR %08X\n", tag, res); + return res; + } + + if (readData[2] != gestureID || readData[4] != 0x00) { /* check of gestureID is redundant */ + logError(1, "%s removeCustomGesture: remove event status not OK! ERROR %08X\n", tag, readData[4]); + return ERROR_GESTURE_REMOVE; + } + + custom_gesture_index[index] = 0; + logError(0, "%s Custom Gesture Remove procedure DONE!\n", tag); + return OK; + +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsGesture.h b/drivers/input/touchscreen/st/fts_lib/ftsGesture.h new file mode 100644 index 000000000000..a9c3e3c05573 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsGesture.h @@ -0,0 +1,74 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS Gesture Utilities * +* * +************************************************************************** +************************************************************************** + +*/ + +#define GESTURE_MASK_SIZE 8 + +#define GESTURE_CUSTOM_POINTS (30*2) +/* for each custom gesture should be provided 30 points (each point is a couple of x,y) */ +#define GESTURE_CUSTOM_NUMBER 5 /* fw support up to 5 custom gestures */ + +#define TEMPLATE_CHUNK (10*2) +/* number of points to transfer with each I2C transaction */ + +/* Gesture IDs */ +#define GES_ID_DBLTAP 0x01 /* Double Tap */ +#define GES_ID_O 0x02 /* 'O' */ +#define GES_ID_C 0x03 /* 'C' */ +#define GES_ID_M 0x04 /* 'M' */ +#define GES_ID_W 0x05 /* 'W' */ +#define GES_ID_E 0x06 /* 'e' */ +#define GES_ID_HFLIP_L2R 0x07 /* Left to right line */ +#define GES_ID_HFLIP_R2L 0x08 /* Right to left line */ +#define GES_ID_VFLIP_T2D 0x09 /* Top to bottom line */ +#define GES_ID_VFLIP_D2T 0x0A /* Bottom to Top line */ +#define GES_ID_L 0x0B /* 'L' */ +#define GES_ID_F 0x0C /* 'F' */ +#define GES_ID_V 0x0D /* 'V' */ +#define GES_ID_AT 0x0E /* '@' */ +#define GES_ID_S 0x0F /* 'S' */ +#define GES_ID_Z 0x10 /* 'Z' */ +#define GES_ID_CUST1 0x11 /* Custom gesture 1 */ +#define GES_ID_CUST2 0x12 /* Custom gesture 2 */ +#define GES_ID_CUST3 0x13 /* Custom gesture 3 */ +#define GES_ID_CUST4 0x14 /* Custom gesture 4 */ +#define GES_ID_CUST5 0x15 /* Custom gesture 5 */ +#define GES_ID_LEFTBRACE 0x20 /* '<' */ +#define GES_ID_RIGHTBRACE 0x21 /* '>' */ + +#define GESTURE_CUSTOM_OFFSET GES_ID_CUST1 + +/* Command sub-type */ +#define GESTURE_ENABLE 0x01 +#define GESTURE_DISABLE 0x02 +#define GESTURE_ENB_CHECK 0x03 +#define GESTURE_START_ADD 0x10 +#define GESTURE_DATA_ADD 0x11 +#define GESTURE_FINISH_ADD 0x12 +#define GETURE_REMOVE_CUSTOM 0x13 +#define GESTURE_CHECK_CUSTOM 0x14 + +/* Event sub-type */ +#define EVENT_TYPE_ENB 0x04 +#define EVENT_TYPE_CHECK_ENB 0x03 +#define EVENT_TYPE_GESTURE_DTC1 0x01 +#define EVENT_TYPE_GESTURE_DTC2 0x02 + +int disableGesture(u8 *mask, int size); +int enableGesture(u8 *mask, int size); +int startAddCustomGesture(u8 gestureID); +int finishAddCustomGesture(u8 gestureID); +int loadCustomGesture(u8 *template, u8 gestureID); +int reloadCustomGesture(void); +int enterGestureMode(int reload); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsHardware.h b/drivers/input/touchscreen/st/fts_lib/ftsHardware.h new file mode 100644 index 000000000000..933059e671b4 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsHardware.h @@ -0,0 +1,177 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* HW related data * +* * +************************************************************************** +************************************************************************** + +*/ + +#define FTM3_CHIP + +/* DUMMY BYTES DATA */ +#define DUMMY_HW_REG 1 +#define DUMMY_FRAMEBUFFER 1 +#define DUMMY_MEMORY 1 + +/* DIGITAL CHIP INFO */ +#ifdef FTM3_CHIP +#define DCHIP_ID_0 0x39 +#define DCHIP_ID_1 0x6C +#else +#define DCHIP_ID_0 0x36 +#define DCHIP_ID_1 0x70 +#endif + +#ifdef FTM3_CHIP +#define DCHIP_ID_ADDR 0x0007 +#define DCHIP_FW_VER_ADDR 0x000A +#else +#define DCHIP_ID_ADDR 0x0004 +#define DCHIP_FW_VER_ADDR 0x000B +#endif + +#define DCHIP_FW_VER_BYTE 2 + +/* CHUNKS */ +#define READ_CHUNK (2*1024) +#define WRITE_CHUNK (2*1024) +#define MEMORY_CHUNK (2*1024) + +/* PROTOCOL INFO */ +#ifdef FTM3_CHIP +#define I2C_SAD 0x49 +#else +#define I2C_SAD 0x48 +#endif + +#define I2C_INTERFACE /* comment if the chip use SPI */ +#define ICR_ADDR 0x0024 +#define ICR_SPI_VALUE 0x02 + +/* SYSTEM RESET INFO */ +#ifdef FTM3_CHIP +#define SYSTEM_RESET_ADDRESS 0x0023 +#define SYSTEM_RESET_VALUE 0x01 +#else +#define SYSTEM_RESET_ADDRESS 0x0028 +#define SYSTEM_RESET_VALUE 0x80 +#endif + +/* INTERRUPT INFO */ +#ifdef FTM3_CHIP +#define IER_ADDR 0x001C +#else +#define IER_ADDR 0x002C +#endif + +#define IER_ENABLE 0x41 +#define IER_DISABLE 0x00 + +/* FLASH COMMAND */ + +#define FLASH_CMD_UNLOCK 0xF7 + +#ifdef FTM3_CHIP +#define FLASH_CMD_WRITE_LOWER_64 0xF0 +#define FLASH_CMD_WRITE_UPPER_64 0xF1 +#define FLASH_CMD_BURN 0xF2 +#define FLASH_CMD_ERASE 0xF3 +#define FLASH_CMD_READSTATUS 0xF4 +#else +#define FLASH_CMD_WRITE_64K 0xF8 +#define FLASH_CMD_READ_REGISTER 0xF9 +#define FLASH_CMD_WRITE_REGISTER 0xFA +#endif + +/* FLASH UNLOCK PARAMETER */ +#define FLASH_UNLOCK_CODE0 0x74 +#define FLASH_UNLOCK_CODE1 0x45 + +#ifndef FTM3_CHIP +/* FLASH ERASE and DMA PARAMETER */ +#define FLASH_ERASE_UNLOCK_CODE0 0x72 +#define FLASH_ERASE_UNLOCK_CODE1 0x03 +#define FLASH_ERASE_UNLOCK_CODE2 0x02 +#define FLASH_ERASE_CODE0 0x02 +#define FLASH_ERASE_CODE1 0xC0 +#define FLASH_DMA_CODE0 0x05 +#define FLASH_DMA_CODE1 0xC0 +#define FLASH_DMA_CONFIG 0x06 +#endif + +/* FLASH ADDRESS */ +#ifdef FTM3_CHIP +#define FLASH_ADDR_SWITCH_CMD 0x00010000 +#define FLASH_ADDR_CODE 0x00000000 +#define FLASH_ADDR_CONFIG 0x0001E800 +#define FLASH_ADDR_CX 0x0001F000 +#else +#define ADDR_WARM_BOOT 0x001E +#define WARM_BOOT_VALUE 0x38 +#define FLASH_ADDR_CODE 0x00000000 +#define FLASH_ADDR_CONFIG 0x0000FC00 +#endif + +/* CRC ADDR */ +#ifdef FTM3_CHIP +#define ADDR_CRC_BYTE0 0x00 +#define ADDR_CRC_BYTE1 0x86 +#define CRC_MASK 0x02 +#else +#define ADDR_CRC_BYTE0 0x00 +#define ADDR_CRC_BYTE1 0x74 +#define CRC_MASK 0x03 +#endif + +/* SIZES FW, CODE, CONFIG, MEMH */ +#ifdef FTM3_CHIP +#define FW_HEADER_SIZE 32 +#define FW_SIZE (int)(128*1024) +#define FW_CODE_SIZE (int)(122*1024) +#define FW_CONFIG_SIZE (int)(2*1024) +#define FW_CX_SIZE (int)(FW_SIZE-FW_CODE_SIZE-FW_CONFIG_SIZE) +#define FW_VER_MEMH_BYTE1 193 +#define FW_VER_MEMH_BYTE0 192 +#define FW_OFF_CONFID_MEMH_BYTE1 2 +#define FW_OFF_CONFID_MEMH_BYTE0 1 +#define FW_BIN_VER_OFFSET 4 +#define FW_BIN_CONFIG_VER_OFFSET (FW_HEADER_SIZE+FW_CODE_SIZE+1) +#else +#define FW_HEADER_SIZE 64 +#define FW_HEADER_SIGNATURE 0xAA55AA55 +#define FW_FTB_VER 0x00000001 +#define FW_BYTES_ALIGN 4 +#define FW_BIN_VER_OFFSET 16 +#define FW_BIN_CONFIG_VER_OFFSET 20 +#endif + +/* FIFO */ +#define FIFO_EVENT_SIZE 8 +#ifdef FTM3_CHIP +#define FIFO_DEPTH 32 +#else +#define FIFO_DEPTH 64 +#endif + +#define FIFO_CMD_READONE 0x85 +#define FIFO_CMD_READALL 0x86 +#define FIFO_CMD_LAST 0x87 +#define FIFO_CMD_FLUSH 0xA1 + +/* CONSTANT TOTAL CX */ +#define CX1_WEIGHT 4 +#define CX2_WEIGHT 1 + +/* OP CODES FOR MEMORY (based on protocol) */ + +#define FTS_CMD_HW_REG_R 0xB6 +#define FTS_CMD_HW_REG_W 0xB6 +#define FTS_CMD_FRAMEBUFFER_R 0xD0 +#define FTS_CMD_FRAMEBUFFER_W 0xD0 diff --git a/drivers/input/touchscreen/st/fts_lib/ftsIO.c b/drivers/input/touchscreen/st/fts_lib/ftsIO.c new file mode 100644 index 000000000000..96ca8cfecd8a --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsIO.c @@ -0,0 +1,403 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* I2C/SPI Communication * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsSoftware.h" +#include "ftsCrossCompile.h" + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <stdarg.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/spi/spidev.h> +static struct i2c_client *client; +static u16 I2CSAD; + +#include "ftsError.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsTool.h" + +static char tag[8] = "[ FTS ]\0"; + +int openChannel(struct i2c_client *clt) +{ + client = clt; + I2CSAD = clt->addr; + logError(1, "%s openChannel: SAD: %02X\n", tag, I2CSAD); + return OK; +} + +struct device *getDev(void) +{ + if (client != NULL) + return &(client->dev); + else + return NULL; +} + +struct i2c_client *getClient(void) +{ + if (client != NULL) + return client; + else + return NULL; +} + +int fts_readCmd(u8 *cmd, int cmdLength, u8 *outBuf, int byteToRead) +{ + int ret = -1; + int retry = 0; + struct i2c_msg I2CMsg[2]; + + /* write msg */ + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)cmdLength; + I2CMsg[0].buf = (__u8 *)cmd; + + /* read msg */ + I2CMsg[1].addr = (__u16)I2CSAD; + I2CMsg[1].flags = I2C_M_RD; + I2CMsg[1].len = byteToRead; + I2CMsg[1].buf = (__u8 *)outBuf; + + if (client == NULL) + return ERROR_I2C_O; + while (retry < I2C_RETRY && ret < OK) { + ret = i2c_transfer(client->adapter, I2CMsg, 2); + if (ret >= OK) + break; + retry++; + msleep(I2C_WAIT_BEFORE_RETRY); + } + if (ret < 0) { + logError(1, "%s fts_readCmd: ERROR %02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + return OK; +} + +int fts_writeCmd(u8 *cmd, int cmdLength) +{ + int ret = -1; + int retry = 0; + struct i2c_msg I2CMsg[2]; + + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)cmdLength; + I2CMsg[0].buf = (__u8 *)cmd; + + if (client == NULL) +return ERROR_I2C_O; + while (retry < I2C_RETRY && ret < OK) { + ret = i2c_transfer(client->adapter, I2CMsg, 1); + if (ret >= OK) + break; + retry++; + msleep(I2C_WAIT_BEFORE_RETRY); + /* logError(1, "%s fts_writeCmd: attempt %d\n", tag, retry); */ + } + if (ret < 0) { + logError(1, "%s fts_writeCmd: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + return OK; +} + +int fts_writeFwCmd(u8 *cmd, int cmdLength) +{ + int ret = -1; + int ret2 = -1; + int retry = 0; + struct i2c_msg I2CMsg[2]; + + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)cmdLength; + I2CMsg[0].buf = (__u8 *)cmd; + + if (client == NULL) +return ERROR_I2C_O; + while (retry < I2C_RETRY && (ret < OK || ret2 < OK)) { + ret = i2c_transfer(client->adapter, I2CMsg, 1); + retry++; + if (ret >= 0) { + ret2 = checkEcho(cmd, cmdLength); + break; + } + msleep(I2C_WAIT_BEFORE_RETRY); + /* logError(1, "%s fts_writeCmd: attempt %d\n", tag, retry); */ + } + if (ret < 0) { + logError(1, "%s fts_writeFwCmd: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + if (ret2 < OK) { + logError(1, "%s fts_writeFwCmd: check echo ERROR %02X\n", tag, ret2); + return (ret|ERROR_I2C_W); + } + return OK; +} + +int writeReadCmd(u8 *writeCmd1, int writeCmdLength, u8 *readCmd1, + int readCmdLength, u8 *outBuf, int byteToRead) +{ + int ret = -1; + int retry = 0; + struct i2c_msg I2CMsg[3]; + + /* write msg */ + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)writeCmdLength; + I2CMsg[0].buf = (__u8 *)writeCmd1; + + /* write msg */ + I2CMsg[1].addr = (__u16)I2CSAD; + I2CMsg[1].flags = (__u16)0; + I2CMsg[1].len = (__u16)readCmdLength; + I2CMsg[1].buf = (__u8 *)readCmd1; + + /* read msg */ + I2CMsg[2].addr = (__u16)I2CSAD; + I2CMsg[2].flags = I2C_M_RD; + I2CMsg[2].len = byteToRead; + I2CMsg[2].buf = (__u8 *)outBuf; + + if (client == NULL) + return ERROR_I2C_O; + while (retry < I2C_RETRY && ret < OK) { + ret = i2c_transfer(client->adapter, I2CMsg, 3); + if (ret >= OK) + break; + retry++; + msleep(I2C_WAIT_BEFORE_RETRY); + } + + if (ret < 0) { + logError(1, "%s writeReadCmd: ERROR %02X\n", tag, ERROR_I2C_WR); + return ERROR_I2C_WR; + } + return OK; + +} + +int readCmdU16(u8 cmd, u16 address, u8 *outBuf, int byteToRead, int hasDummyByte) +{ + + int remaining = byteToRead; + int toRead = 0; + u8 rCmd[3] = { cmd, 0x00, 0x00 }; + + u8 *buff = (u8 *)kmalloc((READ_CHUNK + 1)*sizeof(u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s readCmdU16: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + if (remaining >= READ_CHUNK) { + toRead = READ_CHUNK; + remaining -= READ_CHUNK; + } else { + toRead = remaining; + remaining = 0; + } + + rCmd[1] = (u8)((address & 0xFF00) >> 8); + rCmd[2] = (u8)(address & 0xFF); + + if (hasDummyByte) { + if (fts_readCmd(rCmd, 3, buff, toRead + 1) < 0) { + logError(1, "%s readCmdU16: ERROR %02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; + } + memcpy(outBuf, buff + 1, toRead); + } else { + if (fts_readCmd(rCmd, 3, buff, toRead) < 0) + return ERROR_I2C_R; + memcpy(outBuf, buff, toRead); + } + + address += toRead; + + outBuf += toRead; + + } + kfree(buff); + + return OK; +} + +int writeCmdU16(u8 WriteCmd, u16 address, u8 *dataToWrite, int byteToWrite) +{ + + int remaining = byteToWrite; + int toWrite = 0; + + u8 *buff = (u8 *)kmalloc((WRITE_CHUNK + 3)*sizeof(u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s writeCmdU16: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + buff[0] = WriteCmd; + + while (remaining > 0) { + if (remaining >= WRITE_CHUNK) { + toWrite = WRITE_CHUNK; + remaining -= WRITE_CHUNK; + } else { + toWrite = remaining; + remaining = 0; + } + + buff[1] = (u8)((address & 0xFF00) >> 8); + buff[2] = (u8)(address & 0xFF); + memcpy(buff + 3, dataToWrite, toWrite); + if (fts_writeCmd(buff, 3 + toWrite) < 0) { + logError(1, "%s writeCmdU16: ERROR %02\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + address += toWrite; + dataToWrite += toWrite; + + } + + return OK; +} + +int writeCmdU32(u8 writeCmd1, u8 writeCmd2, u32 address, u8 *dataToWrite, int byteToWrite) +{ + + int remaining = byteToWrite; + int toWrite = 0; + + u8 buff1[3] = { writeCmd1, 0x00, 0x00 }; + u8 *buff2 = (u8 *)kmalloc((WRITE_CHUNK + 3)*sizeof(u8), GFP_KERNEL); + if (buff2 == NULL) { + logError(1, "%s writeCmdU32: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + buff2[0] = writeCmd2; + + while (remaining > 0) { + if (remaining >= WRITE_CHUNK) { + toWrite = WRITE_CHUNK; + remaining -= WRITE_CHUNK; + } else { + toWrite = remaining; + remaining = 0; + } + + buff1[1] = (u8)((address & 0xFF000000) >> 24); + buff1[2] = (u8)((address & 0x00FF0000) >> 16); + buff2[1] = (u8)((address & 0x0000FF00) >> 8); + buff2[2] = (u8)(address & 0xFF); + memcpy(buff2 + 3, dataToWrite, toWrite); + + if (fts_writeCmd(buff1, 3) < 0) { + logError(1, "%s writeCmdU32: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + if (fts_writeCmd(buff2, 3 + toWrite) < 0) { + logError(1, "%s writeCmdU32: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + address += toWrite; + dataToWrite += toWrite; + + } + + return OK; +} + +int writeReadCmdU32(u8 wCmd, u8 rCmd, u32 address, u8 *outBuf, int byteToRead, int hasDummyByte) +{ + + int remaining = byteToRead; + int toRead = 0; + u8 reaCmd[3]; + u8 wriCmd[3]; + + u8 *buff = (u8 *)kmalloc((READ_CHUNK + 1)*sizeof(u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s writereadCmd32: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + reaCmd[0] = rCmd; + wriCmd[0] = wCmd; + + while (remaining > 0) { + if (remaining >= READ_CHUNK) { + toRead = READ_CHUNK; + remaining -= READ_CHUNK; + } else { + toRead = remaining; + remaining = 0; + } + + wriCmd[1] = (u8)((address & 0xFF000000) >> 24); + wriCmd[2] = (u8)((address & 0x00FF0000) >> 16); + + reaCmd[1] = (u8)((address & 0x0000FF00) >> 8); + reaCmd[2] = (u8)(address & 0x000000FF); + + if (hasDummyByte) { + if (writeReadCmd(wriCmd, 3, reaCmd, 3, buff, toRead + 1) < 0) { + logError(1, "%s writeCmdU32: ERROR %02X\n", tag, ERROR_I2C_WR); + return ERROR_I2C_WR; + } + memcpy(outBuf, buff + 1, toRead); + } else { + if (writeReadCmd(wriCmd, 3, reaCmd, 3, buff, toRead) < 0) + return ERROR_I2C_WR; + memcpy(outBuf, buff, toRead); + } + + address += toRead; + + outBuf += toRead; + + } + + return OK; +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsIO.h b/drivers/input/touchscreen/st/fts_lib/ftsIO.h new file mode 100644 index 000000000000..7bdeda2f9a2d --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsIO.h @@ -0,0 +1,35 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* I2C/SPI Communication * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsSoftware.h" +#include "ftsCrossCompile.h" + +#include <linux/i2c.h> +#include <linux/i2c-dev.h> + +#define I2C_RETRY 3 /* number */ +#define I2C_WAIT_BEFORE_RETRY 10 /* ms */ + +int openChannel(struct i2c_client *clt); +struct device *getDev(void); +struct i2c_client *getClient(void); +int fts_readCmd(u8 *cmd, int cmdLenght, u8 *outBuf, int byteToRead); +int fts_writeCmd(u8 *cmd, int cmdLenght); +int fts_writeFwCmd(u8 *cmd, int cmdLenght); +int writeReadCmd(u8 *writeCmd, int writeCmdLenght, u8 *readCmd, int readCmdLenght, u8 *outBuf, int byteToRead); +int readCmdU16(u8 cmd, u16 address, u8 *outBuf, int byteToRead, int hasDummyByte); +int writeCmdU16(u8 WriteCmd, u16 address, u8 *dataToWrite, int byteToWrite); +int writeCmdU32(u8 writeCmd1, u8 writeCmd2, u32 address, u8 *dataToWrite, int byteToWrite); +int writeReadCmdU32(u8 wCmd, u8 rCmd, u32 address, u8 *outBuf, int byteToRead, int hasDummyByte); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsSoftware.h b/drivers/input/touchscreen/st/fts_lib/ftsSoftware.h new file mode 100644 index 000000000000..c8bbc8e18d28 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsSoftware.h @@ -0,0 +1,131 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FW related data * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsHardware.h" + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + +#define ECHO_ENABLED 0x00000001 + +/* chipInfo ftsInfo; */ + +/* FTS FW COMAND */ +#define FTS_CMD_MS_MT_SENSE_OFF 0x92 +#define FTS_CMD_MS_MT_SENSE_ON 0x93 +#define FTS_CMD_SS_HOVER_OFF 0x94 +#define FTS_CMD_SS_HOVER_ON 0x95 +#define FTS_CMD_LP_TIMER_CALIB 0x97 +#define FTS_CMD_MS_KEY_OFF 0x9A +#define FTS_CMD_MS_KEY_ON 0x9B +#define FTS_CMD_MS_COMP_TUNING 0xA3 +#define FTS_CMD_SS_COMP_TUNING 0xA4 +#define FTS_CMD_FULL_INITIALIZATION 0xA5 +#define FTS_CMD_ITO_CHECK 0xA7 +#define FTS_CMD_RELEASE_INFO 0xAA +#define FTS_CMD_GESTURE_MODE 0xAD +#define FTS_CMD_REQU_FW_CONF 0xB2 +#define FTS_CMD_REQU_FRAME_DATA 0xB7 +#define FTS_CMD_REQU_COMP_DATA 0xB8 +#define FTS_CMD_WRITE_MP_FLAG 0xC0 +#define FTS_CMD_FEATURE_ENABLE 0xC1 +#define FTS_CMD_FEATURE_DISABLE 0xC2 +#define FTS_CMD_GESTURE_CMD 0xC3 +#define FTS_CMD_SAVE_CX_TUNING 0xFC + +/* Event ID */ +#define EVENTID_NO_EVENT 0x00 +#define EVENTID_ERROR_EVENT 0x0F +#define EVENTID_CONTROL_READY 0x10 +#define EVENTID_FW_CONFIGURATION 0x12 +#define EVENTID_COMP_DATA_READ 0x13 +#define EVENTID_STATUS_UPDATE 0x16 +#define EVENTID_RELEASE_INFO 0x1C +#define EVENTID_ENTER_POINTER 0x03 +#define EVENTID_LEAVE_POINTER 0x04 +#define EVENTID_MOTION_POINTER 0x05 +#define EVENTID_HOVER_ENTER_POINTER 0x07 +#define EVENTID_HOVER_LEAVE_POINTER 0x08 +#define EVENTID_HOVER_MOTION_POINTER 0x09 +#define EVENTID_PROXIMITY_ENTER 0x0B +#define EVENTID_PROXIMITY_LEAVE 0x0C +#define EVENTID_KEY_STATUS 0x0E +#define EVENTID_GESTURE 0x22 +#define EVENTID_FRAME_DATA_READ 0x25 +#define EVENTID_ECHO 0xEC +#define EVENTID_LAST (EVENTID_FRAME_DATA_READ+1) + +/* EVENT TYPE */ +#define EVENT_TYPE_MS_TUNING_CMPL 0x01 +#define EVENT_TYPE_SS_TUNING_CMPL 0x02 +#define EVENT_TYPE_COMP_DATA_SAVED 0x04 +#define EVENT_TYPE_ITO 0x05 +#define EVENT_TYPE_FULL_INITIALIZATION 0x07 +#define EVENT_TYPE_LPTIMER_TUNING_CMPL 0x20 +#define EVENT_TYPE_ESD_ERROR 0x0A +#define EVENT_TYPE_WATCHDOG_ERROR 0x01 + +/* CONFIG ID INFO */ +#define CONFIG_ID_ADDR 0x0001 +#define CONFIG_ID_BYTE 2 + +/* ADDRESS OFFSET IN SYSINFO */ +#define ADDR_RAW_TOUCH 0x0000 +#define ADDR_FILTER_TOUCH 0x0002 +#define ADDR_NORM_TOUCH 0x0004 +#define ADDR_CALIB_TOUCH 0x0006 +#define ADDR_RAW_HOVER_FORCE 0x000A +#define ADDR_RAW_HOVER_SENSE 0x000C +#define ADDR_FILTER_HOVER_FORCE 0x000E +#define ADDR_FILTER_HOVER_SENSE 0x0010 +#define ADDR_NORM_HOVER_FORCE 0x0012 +#define ADDR_NORM_HOVER_SENSE 0x0014 +#define ADDR_CALIB_HOVER_FORCE 0x0016 +#define ADDR_CALIB_HOVER_SENSE 0x0018 +#define ADDR_RAW_PRX_FORCE 0x001A +#define ADDR_RAW_PRX_SENSE 0x001C +#define ADDR_FILTER_PRX_FORCE 0x001E +#define ADDR_FILTER_PRX_SENSE 0x0020 +#define ADDR_NORM_PRX_FORCE 0x0022 +#define ADDR_NORM_PRX_SENSE 0x0024 +#define ADDR_CALIB_PRX_FORCE 0x0026 +#define ADDR_CALIB_PRX_SENSE 0x0028 +#define ADDR_RAW_MS_KEY 0x0032 +#define ADDR_COMP_DATA 0x0050 +#define ADDR_FRAMEBUFFER_DATA 0x8000 + +/* ADDRESS FW REGISTER */ +#define ADDR_SENSE_LEN 0x0014 +#define ADDR_FORCE_LEN 0x0015 +#define ADDR_MS_TUNING_VER 0x0729 +#define ADDR_SS_TUNING_VER 0x074E + +/* B2 INFO */ +#define B2_DATA_BYTES 4 +#define B2_CHUNK ((FIFO_DEPTH/2)*B2_DATA_BYTES) /* number of bytes */ + +/* FEATURES */ +#define FEAT_GESTURE 0x00 +#define FEAT_GLOVE 0x01 +#define FEAT_STYLUS 0x02 +#define FEAT_COVER 0x04 +#define FEAT_CHARGER 0x08 +#define FEAT_VR 0x10 +#define FEAT_EDGE_REJECTION 0x20 + +/* MP_FLAG_VALUE */ +#define INIT_MP 0xA5A5A501 +#define INIT_FIELD 0xA5A5A502 diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTest.c b/drivers/input/touchscreen/st/fts_lib/ftsTest.c new file mode 100644 index 000000000000..3810fd02001a --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTest.c @@ -0,0 +1,2324 @@ +/* + + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for MP test * + * * + ************************************************************************** + ************************************************************************** + + */ + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTest.h" +#include "ftsTime.h" +#include "ftsTool.h" +#include "../fts.h" + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <stdarg.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/time.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ + +#ifdef LIMITS_H_FILE +#include <../fts_limits.h> +#endif + +/* static char tag[8] = "[ FTS ]\0"; */ + +int computeAdjHoriz(u8 *data, int row, int column, u8 **result) +{ + int i, j; + int size = row * (column - 1); + + if (column < 2) { + logError(1, "%s computeAdjHoriz: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u8 *) kmalloc(size * sizeof (u8), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjHoriz: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) { + for (j = 1; j < column; j++) { + *(*result + (i * (column - 1) + (j - 1))) = abs(data[i * column + j] - data[i * column + (j - 1)]); + } + } + + return OK; + +} + +int computeAdjHorizTotal(u16 *data, int row, int column, u16 **result) +{ + int i, j; + int size = row * (column - 1); + + if (column < 2) { + logError(1, "%s computeAdjHorizTotal: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u16 *) kmalloc(size * sizeof (u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjHorizTotal: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) { + for (j = 1; j < column; j++) { + *(*result + (i * (column - 1) + (j - 1))) = abs(data[i * column + j] - data[i * column + (j - 1)]); + } + } + + return OK; + +} + +int computeAdjVert(u8 *data, int row, int column, u8 **result) +{ + int i, j; + int size = (row - 1)*(column); + + if (row < 2) { + logError(1, "%s computeAdjVert: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u8 *) kmalloc(size * sizeof (u8), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjVert: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 1; i < row; i++) { + for (j = 0; j < column; j++) { + *(*result + ((i - 1) * column + j)) = abs(data[i * column + j] - data[(i - 1) * column + j]); + } + } + + return OK; +} + +int computeAdjVertTotal(u16 *data, int row, int column, u16 **result) +{ + int i, j; + int size = (row - 1)*(column); + + if (row < 2) { + logError(1, "%s computeAdjVertTotal: ERROR % 02X\n", tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u16 *) kmalloc(size * sizeof (u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjVertTotal: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 1; i < row; i++) { + for (j = 0; j < column; j++) { + *(*result + ((i - 1) * column + j)) = abs(data[i * column + j] - data[(i - 1) * column + j]); + } + } + + return OK; +} + +int computeTotal(u8 *data, u8 main, int row, int column, int m, int n, u16 **result) +{ + int i, j; + int size = (row)*(column); + + *result = (u16 *) kmalloc(size * sizeof (u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeTotal : ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + *(*result + (i * column + j)) = m * main + n * data[i * column + j]; + } + } + + return OK; +} + +int checkLimitsMinMax(short *data, int row, int column, int min, int max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min || data[i * column + j] > max) { + logError(1, "%s checkLimitsMinMax: Node[%d,%d] = %d exceed limit [%d, %d]\n", tag, i, j, data[i * column + j], min, max); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +int checkLimitsGap(short *data, int row, int column, int threshold) +{ + int i, j; + int min_node; + int max_node; + + if (row == 0 || column == 0) { + logError(1, "%s checkLimitsGap: invalid number of rows = %d or columns = %d ERROR %02\n", tag, row, column, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + min_node = data[0]; + max_node = data[0]; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min_node) { + min_node = data[i * column + j]; + } else { + if (data[i * column + j] > max_node) + max_node = data[i * column + j]; + } + } + } + + if (max_node - min_node > threshold) { + logError(1, "%s checkLimitsGap: GAP = %d exceed limit %d\n", tag, max_node - min_node, threshold); + return ERROR_TEST_CHECK_FAIL; + } else + return OK; + +} + +int checkLimitsMap(u8 *data, int row, int column, int *min, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min[i * column + j] || data[i * column + j] > max[i * column + j]) { + logError(1, "%s checkLimitsMap: Node[%d,%d] = %d exceed limit [%d, %d]\n", tag, i, j, data[i * column + j], min[i * column + j], max[i * column + j]); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +int checkLimitsMapTotal(u16 *data, int row, int column, int *min, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min[i * column + j] || data[i * column + j] > max[i * column + j]) { + logError(1, "%s checkLimitsMapTotal: Node[%d,%d] = %d exceed limit [%d, %d]\n", tag, i, j, data[i * column + j], min[i * column + j], max[i * column + j]); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +int checkLimitsMapAdj(u8 *data, int row, int column, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] > max[i * column + j]) { + logError(1, "%s checkLimitsMapAdj: Node[%d,%d] = %d exceed limit > %d\n", tag, i, j, data[i * column + j], max[i * column + j]); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +int checkLimitsMapAdjTotal(u16 *data, int row, int column, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] > max[i * column + j]) { + logError(1, "%s checkLimitsMapAdjTotal: Node[%d,%d] = %d exceed limit > %d\n", tag, i, j, data[i * column + j], max[i * column + j]); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +int production_test_ito(void) +{ + int res = OK; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int eventToSearch[2] = {EVENTID_ERROR_EVENT, EVENT_TYPE_ITO}; /* look for ito event */ + + logError(0, "%s ITO Production test is starting...\n", tag); + + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s production_test_ito: ERROR %02X\n", tag, ERROR_PROD_TEST_ITO); + return (res | ERROR_PROD_TEST_ITO); + } + + cmd = FTS_CMD_ITO_CHECK; + + logError(0, "%s ITO Check command sent...\n", tag); + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s production_test_ito: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_PROD_TEST_ITO)); + return (ERROR_I2C_W | ERROR_PROD_TEST_ITO); + } + + logError(0, "%s Looking for ITO Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_ITO_TEST_RESULT); + if (res < 0) { + logError(1, "%s production_test_ito: ITO Production test failed... ERROR %02X\n", tag, ERROR_PROD_TEST_ITO); + return (res | ERROR_PROD_TEST_ITO); + } + + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s ITO Production testes finished!.................FAILED ERROR %02X\n", tag, (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_ITO)); + res = (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_ITO); + } else { + logError(0, "%s ITO Production test finished!.................OK\n", tag); + res = OK; + } + + res |= fts_system_reset(); + if (res < 0) { + logError(1, "%s production_test_ito: ERROR %02X\n", tag, ERROR_PROD_TEST_ITO); + res = (res | ERROR_PROD_TEST_ITO); + } + return res; +} + +int production_test_initialization(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_FULL_INITIALIZATION}; + + logError(0, "%s INITIALIZATION Production test is starting...\n", tag); + + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s production_test_initialization: ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s INITIALIZATION command sent...\n", tag); + cmd = FTS_CMD_FULL_INITIALIZATION; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s production_test_initialization: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_PROD_TEST_INITIALIZATION)); + return (ERROR_I2C_W | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s Looking for INITIALIZATION Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s production_test_initialization: INITIALIZATION Production test failed... ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + if (readData[2] != 0x00) { + logError(0, "%s INITIALIZATION Production testes finished!.................FAILED ERROR %02X\n", tag, (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_INITIALIZATION)); + res = (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_INITIALIZATION); + } else { + logError(0, "%s INITIALIZATION Production test.................OK\n", tag); + res = OK; + } + + logError(0, "%s Refresh Chip Info...\n", tag); + res |= readChipInfo(1); + /* need to update the chipInfo in order to refresh the tuning_versione */ + + if (res < 0) { + logError(1, "%s production_test_initialization: read chip info ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + res = (res | ERROR_PROD_TEST_INITIALIZATION); + } + + return res; + +} + +int ms_compensation_tuning(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_MS_TUNING_CMPL}; + + logError(0, "%s MS INITIALIZATION command sent...\n", tag); + cmd = FTS_CMD_MS_COMP_TUNING; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s ms_compensation_tuning 2: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_MS_TUNING)); + return (ERROR_I2C_W | ERROR_MS_TUNING); + } + + logError(0, "%s Looking for MS INITIALIZATION Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s ms_compensation_tuning: MS INITIALIZATION Production test failed... ERROR %02X\n", tag, ERROR_MS_TUNING); + return (res | ERROR_MS_TUNING); + } + + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s MS INITIALIZATION Production test finished!.................FAILED ERROR %02X\n", tag, ERROR_MS_TUNING); + res = ERROR_MS_TUNING; + } else { + logError(0, "%s MS INITIALIZATION Production test finished!.................OK\n", tag); + res = OK; + } + + return res; +} + +int ss_compensation_tuning(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_SS_TUNING_CMPL}; + + logError(0, "%s SS INITIALIZATION command sent...\n", tag); + cmd = FTS_CMD_SS_COMP_TUNING; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s ss_compensation_tuning 2: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_SS_TUNING)); + return (ERROR_I2C_W | ERROR_SS_TUNING); + } + + logError(0, "%s Looking for SS INITIALIZATION Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s ms_compensation_tuning: SS INITIALIZATION Production test failed... ERROR %02X\n", tag, ERROR_SS_TUNING); + return (res | ERROR_SS_TUNING); + } + + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s SS INITIALIZATION Production test finished!.................FAILED ERROR %02X\n", tag, ERROR_SS_TUNING); + res = ERROR_SS_TUNING; + } else { + logError(0, "%s SS INITIALIZATION Production test finished!.................OK\n", tag); + res = OK; + } + + return res; +} + +int lp_timer_calibration(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_LPTIMER_TUNING_CMPL}; + + logError(0, "%s LP TIMER CALIBRATION command sent...\n", tag); + cmd = FTS_CMD_LP_TIMER_CALIB; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s lp_timer_calibration 2: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_LP_TIMER_TUNING)); + return (ERROR_I2C_W | ERROR_LP_TIMER_TUNING); + } + + logError(0, "%s Looking for LP TIMER CALIBRATION Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s lp_timer_calibration: LP TIMER CALIBRATION Production test failed... ERROR %02X\n", tag, ERROR_LP_TIMER_TUNING); + return (res | ERROR_LP_TIMER_TUNING); + } + + if (readData[2] != 0x00 || readData[3] != 0x01) { + logError(0, "%s LP TIMER CALIBRATION Production test finished!.................FAILED ERROR %02X\n", tag, ERROR_LP_TIMER_TUNING); + res = ERROR_LP_TIMER_TUNING; + } else { + logError(0, "%s LP TIMER CALIBRATION Production test finished!.................OK\n", tag); + res = OK; + } + + return res; +} + +int save_cx_tuning(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, EVENT_TYPE_COMP_DATA_SAVED}; + + logError(0, "%s SAVE CX command sent...\n", tag); + cmd = FTS_CMD_SAVE_CX_TUNING; + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s save_cx_tuning 2: ERROR %02X\n", tag, (ERROR_I2C_W | ERROR_SAVE_CX_TUNING)); + return (ERROR_I2C_W | ERROR_SAVE_CX_TUNING); + } + + logError(0, "%s Looking for SAVE CX Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s save_cx_tuning: SAVE CX failed... ERROR %02X\n", tag, ERROR_SAVE_CX_TUNING); + return (res | ERROR_SAVE_CX_TUNING); + } + + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s SAVE CX finished!.................FAILED ERROR %02X\n", tag, ERROR_SAVE_CX_TUNING); + res = ERROR_SAVE_CX_TUNING; + } else { + logError(0, "%s SAVE CX finished!.................OK\n", tag); + res = OK; + } + + return res; +} + +int production_test_splited_initialization(int saveToFlash) +{ + int res; + + logError(0, "%s Splitted Initialization test is starting...\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s production_test_initialization: ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + logError(0, "%s LP INITIALIZATION TEST:\n", tag); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s MS INITIALIZATION TEST:\n", tag); + res = ms_compensation_tuning(); + if (res < 0) { + logError(0, "%s production_test_splited_initialization: MS INITIALIZATION TEST FAILED! ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + logError(0, "%s MS INITIALIZATION TEST OK!\n", tag); + + logError(0, "%s\n", tag); + + logError(0, "%s SS INITIALIZATION TEST:\n", tag); + res = ss_compensation_tuning(); + if (res < 0) { + logError(0, "%s production_test_splited_initialization: SS INITIALIZATION TEST FAILED! ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + logError(0, "%s SS INITIALIZATION TEST OK!\n", tag); + + logError(0, "%s\n", tag); + + logError(0, "%s LP INITIALIZATION TEST:\n", tag); + res = lp_timer_calibration(); + if (res < 0) { + logError(0, "%s production_test_splited_initialization: LP INITIALIZATION TEST FAILED! ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + logError(0, "%s LP INITIALIZATION TEST OK!\n", tag); + if (saveToFlash) { + logError(0, "%s\n", tag); + logError(0, "%s SAVE CX TEST:\n", tag); + res = save_cx_tuning(); + if (res < 0) { + logError(0, "%s production_test_splited_initialization: SAVE CX TEST FAILED! ERROR %02X\n", tag, res); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + logError(0, "%s SAVE CX TEST OK!\n", tag); + } + logError(0, "%s Refresh Chip Info...\n", tag); + res |= readChipInfo(1); + if (res < 0) { + logError(1, "%s production_test_initialization: read chip info ERROR %02X\n", tag, ERROR_PROD_TEST_INITIALIZATION); + res = (res | ERROR_PROD_TEST_INITIALIZATION); + } else + logError(0, "%s Splited Initialization test finished!.................OK\n", tag); + return res; +} + +int production_test_main(char *pathThresholds, int stop_on_fail, + int saveInit, TestToDo *todo, u32 signature) +{ + int res, ret; + + logError(0, "%s MAIN Production test is starting...\n", tag); + + logError(0, "%s\n", tag); + + logError(0, "%s ITO TEST:\n", tag); + res = production_test_ito(); + if (res < 0) { + logError(0, "%s Error during ITO TEST! ERROR %02X\n", tag, (u8) res); + goto END; /* in case of ITO TEST failure is no sense keep going */ + } else { + logError(0, "%s ITO TEST OK!\n", tag); + } + + logError(0, "%s\n", tag); + + logError(0, "%s INITIALIZATION TEST :\n", tag); + if (saveInit == 1) { + res = production_test_initialization(); + if (res < 0) { + logError(0, "%s Error during INITIALIZATION TEST! ERROR %02X\n", tag, (u8) res); + if (stop_on_fail) + goto END; + } else { + logError(0, "%s INITIALIZATION TEST OK!\n", tag); + } + } else + logError(0, "%s INITIALIZATION TEST :................. SKIPPED\n", tag); + + logError(0, "%s\n", tag); + + if (saveInit == 1) { + logError(0, "%s Cleaning up...\n", tag); + ret = cleanUp(0); + if (ret < 0) { + logError(1, "%s production_test_main: clean up ERROR %02X\n", tag, ret); + res |= ret; + if (stop_on_fail) + goto END; + } + logError(0, "%s\n", tag); + } + + logError(0, "%s PRODUCTION DATA TEST:\n", tag); + ret = production_test_data(pathThresholds, stop_on_fail, todo); + if (ret < 0) { + logError(0, "%s Error during PRODUCTION DATA TEST! ERROR %02X\n", tag, ret); + } else { + logError(0, "%s PRODUCTION DATA TEST OK!\n", tag); + } + + res |= ret; + /* the OR is important because if the data test is OK but the inizialization test fail, + * the main production test result should = FAIL + */ + + if (ret == OK && saveInit == 1) { + logError(0, "%s SAVE FLAG:\n", tag); + ret = save_mp_flag(signature); + if (ret < OK) + logError(0, "%s SAVE FLAG:................. FAIL! ERROR %08X\n", tag, ret); + else + logError(0, "%s SAVE FLAG:................. OK!\n", tag); + res |= ret; + } + + logError(0, "%s\n", tag); +END: + if (res < 0) { + logError(0, "%s MAIN Production test finished.................FAILED\n", tag); + return res; + } + logError(0, "%s MAIN Production test finished.................OK\n", tag); + return OK; +} + +int production_test_ms_raw(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + + int ret, count_fail = 0; + MutualSenseFrame msRawFrame; + + int *thresholds = NULL; + int trows, tcolumns; + + /*********************** Mutual Sense Test **********************/ + logError(0, "%s\n", tag); + logError(0, "%s MS RAW DATA TEST is starting...\n", tag); + if (todo->MutualRaw == 1 || todo->MutualRawGap == 1) { + + ret = getMSFrame2(MS_TOUCH_ACTIVE, &msRawFrame); + if (ret < 0) { + logError(1, "%s production_test_data: getMSFrame failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s MS RAW MIN MAX TEST:\n", tag); + if (todo->MutualRaw == 1) { + ret = parseProductionTestLimits(path_limits, MS_RAW_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_RAW_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMinMax(msRawFrame.node_data, msRawFrame.header.force_node, msRawFrame.header.sense_node, thresholds[0], thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax MS RAW failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS RAW MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } + kfree(thresholds); + logError(0, "%s MS RAW MIN MAX TEST:.................OK\n", tag); + } else + logError(0, "%s MS RAW MIN MAX TEST:.................SKIPPED\n", tag); + + logError(0, "%s\n", tag); + logError(0, "%s MS RAW GAP TEST:\n", tag); + if (todo->MutualRawGap == 1) { + ret = parseProductionTestLimits(path_limits, MS_RAW_GAP, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_RAW_GAP failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsGap(msRawFrame.node_data, msRawFrame.header.force_node, msRawFrame.header.sense_node, thresholds[0]); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsGap MS RAW failed... ERROR = %02X\n", tag, ret); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + + } else + logError(0, "%s MS RAW GAP TEST:.................OK\n\n", tag); + kfree(thresholds); + } else + logError(0, "%s MS RAW GAP TEST:.................SKIPPED\n", tag); + + kfree(msRawFrame.node_data); + } else + logError(0, "%s MS RAW FRAME TEST:.................SKIPPED\n", tag); + + logError(0, "%s\n", tag); + logError(0, "%s MS KEY RAW TEST:\n", tag); + if (todo->MutualKeyRaw == 1) { + ret = production_test_ms_key_raw(path_limits); + if (ret < 0) { + logError(1, "%s production_test_data: production_test_ms_key_raw failed... ERROR = %02X\n", tag, ret); + count_fail += 1; + } + } else + logError(0, "%s MS KEY RAW TEST:.................SKIPPED\n", tag); + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s MS RAW DATA TEST finished!.................OK\n", tag); + return OK; + } + print_frame_short("MS Raw frame =", array1dTo2d_short(msRawFrame.node_data, msRawFrame.node_data_size, msRawFrame.header.sense_node), msRawFrame.header.force_node, msRawFrame.header.sense_node); + logError(0, "%s MS RAW DATA TEST:.................FAIL fails_count = %d\n\n", tag, count_fail); + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); +} + +int production_test_ms_key_raw(char *path_limits) +{ + + int ret; + MutualSenseFrame msRawFrame; + + int *thresholds = NULL; + int trows, tcolumns; + + /******************************* Mutual Sense Test *******************************/ + logError(0, "%s MS KEY RAW DATA TEST is starting...\n", tag); + + ret = getMSFrame2(MS_KEY, &msRawFrame); + if (ret < 0) { + logError(1, "%s production_test_data: getMSKeyFrame failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, MS_KEY_RAW_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_RAW_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMinMax(msRawFrame.node_data, msRawFrame.header.force_node, msRawFrame.header.sense_node, thresholds[0], thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax MS KEY RAW failed... ERROR COUNT = %d\n", tag, ret); + goto ERROR; + } else + logError(0, "%s MS KEY RAW TEST:.................OK\n\n", tag); + + kfree(thresholds); + + kfree(msRawFrame.node_data); + + return OK; + +ERROR: + print_frame_short("MS Key Raw frame =", array1dTo2d_short(msRawFrame.node_data, msRawFrame.node_data_size, msRawFrame.header.sense_node), msRawFrame.header.force_node, msRawFrame.header.sense_node); + logError(0, "%s MS KEY RAW TEST:.................FAIL\n\n", tag); + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); + +} + +int production_test_ms_cx(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + + int ret; + int count_fail = 0; + + int *thresholds = NULL; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + int trows, tcolumns; + + MutualSenseData msCompData; + + u8 *adjhor = NULL; + + u8 *adjvert = NULL; + + u16 container; + u16 *total_cx = NULL; + u16 *total_adjhor = NULL; + u16 *total_adjvert = NULL; + + /* MS CX TEST */ + logError(0, "%s\n", tag); + logError(0, "%s MS CX Testes are starting...\n", tag); + + ret = readMutualSenseCompensationData(MS_TOUCH_ACTIVE, &msCompData); /* read MS compensation data */ + if (ret < 0) { + logError(1, "%s production_test_data: readMutualSenseCompensationData failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s MS CX1 TEST:\n", tag); + if (todo->MutualCx1 == 1) { + + ret = parseProductionTestLimits(path_limits, MS_CX1_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_CX1_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + container = (u16) msCompData.cx1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax MS CX1 failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS CX1 TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX1 TEST:.................OK\n\n", tag); + } else + logError(0, "%s MS CX1 TEST:.................SKIPPED\n\n", tag); + + kfree(thresholds); + + logError(0, "%s MS CX2 MIN MAX TEST:\n", tag); + if (todo->MutualCx2 == 1) { + ret = parseProductionTestLimits(path_limits, MS_CX2_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load min thresholds */ + if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_CX2_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, MS_CX2_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load max thresholds */ + if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_CX2_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMap(msCompData.node_data, msCompData.header.force_node, msCompData.header.sense_node, thresholds_min, thresholds_max); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap MS CX2 MIN MAX failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS CX2 MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX2 MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s MS CX2 MIN MAX TEST:.................SKIPPED\n\n", tag); + + logError(0, "%s MS CX2 ADJ TEST:\n", tag); + if (todo->MutualCx2Adj == 1) { + /* MS CX2 ADJ HORIZ */ + logError(0, "%s MS CX2 ADJ HORIZ TEST:\n", tag); + + ret = computeAdjHoriz(msCompData.node_data, msCompData.header.force_node, msCompData.header.sense_node, &adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjHoriz failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s MS CX2 ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, MS_CX2_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_CX2_ADJH_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdj(adjhor, msCompData.header.force_node, msCompData.header.sense_node - 1, thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj CX2 ADJH failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS CX2 ADJ HORIZ TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX2 ADJ HORIZ TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(adjhor); + + /* MS CX2 ADJ VERT */ + logError(0, "%s MS CX2 ADJ VERT TEST:\n", tag); + + ret = computeAdjVert(msCompData.node_data, msCompData.header.force_node, msCompData.header.sense_node, &adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjVert failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s MS CX2 ADJ VERT computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, MS_CX2_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node - 1 || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_CX2_ADJV_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdj(adjvert, msCompData.header.force_node - 1, msCompData.header.sense_node - 1, thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj CX2 ADJV failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS CX2 ADJ HORIZ TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX2 ADJ VERT TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(adjvert); + } else + logError(0, "%s MS CX2 ADJ TEST:.................SKIPPED\n\n", tag); + + /* START OF TOTAL CHECK */ + logError(0, "%s MS TOTAL CX TEST:\n", tag); + + if (todo->MutualCxTotal == 1 || todo->MutualCxTotalAdj == 1) { + ret = computeTotal(msCompData.node_data, msCompData.cx1, msCompData.header.force_node, msCompData.header.sense_node, CX1_WEIGHT, CX2_WEIGHT, &total_cx); + if (ret < 0) { + logError(1, "%s production_test_data: computeTotalCx failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s MS TOTAL CX MIN MAX TEST:\n", tag); + if (todo->MutualCxTotal == 1) { + ret = parseProductionTestLimits(path_limits, MS_TOTAL_CX_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load min thresholds */ + if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, MS_TOTAL_CX_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load max thresholds */ + if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapTotal(total_cx, msCompData.header.force_node, msCompData.header.sense_node, thresholds_min, thresholds_max); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap MS TOTAL CX TEST failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS TOTAL CX MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS TOTAL CX MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s MS TOTAL CX MIN MAX TEST:.................SKIPPED\n\n", tag); + + logError(0, "%s MS TOTAL CX ADJ TEST:\n", tag); + if (todo->MutualCxTotalAdj == 1) { + /* MS TOTAL CX ADJ HORIZ */ + logError(0, "%s MS TOTAL CX ADJ HORIZ TEST:\n", tag); + + /* thresholds_max = NULL; */ + ret = computeAdjHorizTotal(total_cx, msCompData.header.force_node, msCompData.header.sense_node, &total_adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjHoriz failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s MS TOTAL CX ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, MS_TOTAL_CX_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node || tcolumns != msCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_ADJH_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdjTotal(total_adjhor, msCompData.header.force_node, msCompData.header.sense_node - 1, thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj MS TOTAL CX ADJH failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS TOTAL CX ADJ HORIZ TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS TOTAL CX ADJ HORIZ TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(total_adjhor); + + /* MS TOTAL CX ADJ VERT */ + logError(0, "%s MS TOTAL CX ADJ VERT TEST:\n", tag); + + ret = computeAdjVertTotal(total_cx, msCompData.header.force_node, msCompData.header.sense_node, &total_adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjVert failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s MS TOTAL CX ADJ VERT computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, MS_TOTAL_CX_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node - 1 || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_ADJV_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdjTotal(total_adjvert, msCompData.header.force_node - 1, msCompData.header.sense_node - 1, thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj MS TOTAL CX ADJV failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS TOTAL CX ADJ HORIZ TEST:.................FAIL\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS TOTAL CX ADJ VERT TEST:.................OK\n", tag); + + kfree(thresholds_max); + kfree(total_adjvert); + } else + logError(0, "%s MS TOTAL CX ADJ TEST:.................SKIPPED\n", tag); + + kfree(total_cx); + } else + logError(0, "%s MS TOTAL CX TEST:.................SKIPPED\n", tag); + + kfree(msCompData.node_data); + + if ((todo->MutualKeyCx1 | todo->MutualKeyCx2 | todo->MutualKeyCxTotal) == 1) { + ret = production_test_ms_key_cx(path_limits, stop_on_fail, todo); + if (ret < 0) { + count_fail += 1; + logError(1, "%s production_test_data: production_test_ms_key_cx failed... ERROR = %02X\n", tag, ret); + logError(0, "%s MS CX testes finished!.................FAILED fails_count = %d\n\n", tag, count_fail); + return ret; + } + } else + logError(0, "%s MS KEY CX TEST:.................SKIPPED\n", tag); + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s MS CX testes finished!.................OK\n", tag); + return OK; + } + print_frame_u8("MS Init Data (Cx2) =", array1dTo2d_u8(msCompData.node_data, msCompData.node_data_size, msCompData.header.sense_node), msCompData.header.force_node, msCompData.header.sense_node); + logError(0, "%s MS CX testes finished!.................FAILED fails_count = %d\n\n", tag, count_fail); + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); + +} + +int production_test_ms_key_cx(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + + int ret; + int count_fail = 0; + int num_keys = 0; + + int *thresholds = NULL; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + int trows, tcolumns; + + MutualSenseData msCompData; + + u16 container; + u16 *total_cx = NULL; + + /* MS CX TEST */ + logError(0, "%s MS KEY CX Testes are starting...\n", tag); + + ret = readMutualSenseCompensationData(MS_KEY, &msCompData); /* read MS compensation data */ + if (ret < 0) { + logError(1, "%s production_test_data: readMutualSenseCompensationData failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + if (msCompData.header.force_node > msCompData.header.sense_node) +/* the meaningful data are only in the first row, the other rows are only a copy of the first one */ + num_keys = msCompData.header.force_node; + else + num_keys = msCompData.header.sense_node; + + logError(0, "%s MS KEY CX1 TEST:\n", tag); + if (todo->MutualKeyCx1 == 1) { + + ret = parseProductionTestLimits(path_limits, MS_KEY_CX1_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_CX1_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + container = (u16) msCompData.cx1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax MS CX1 failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS KEY CX1 TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS KEY CX1 TEST:.................OK\n\n", tag); + } else + logError(0, "%s MS KEY CX1 TEST:.................SKIPPED\n\n", tag); + + kfree(thresholds); + + logError(0, "%s MS KEY CX2 TEST:\n", tag); + if (todo->MutualKeyCx2 == 1) { + ret = parseProductionTestLimits(path_limits, MS_KEY_CX2_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_CX2_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, MS_KEY_CX2_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_CX2_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMap(msCompData.node_data, 1, num_keys, thresholds_min, thresholds_max); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap MS KEY CX2 failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS KEY CX2 TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS KEY CX2 TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s MS CX2 TEST:.................SKIPPED\n\n", tag); + + /* START OF TOTAL CHECK */ + logError(0, "%s MS KEY TOTAL CX TEST:\n", tag); + + if (todo->MutualKeyCxTotal == 1) { + ret = computeTotal(msCompData.node_data, msCompData.cx1, 1, num_keys, CX1_WEIGHT, CX2_WEIGHT, &total_cx); + if (ret < 0) { + logError(1, "%s production_test_data: computeTotalCx failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, MS_KEY_TOTAL_CX_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_TOTAL_CX_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, MS_KEY_TOTAL_CX_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: parseProductionTestLimits MS_KEY_TOTAL_CX_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapTotal(total_cx, 1, num_keys, thresholds_min, thresholds_max); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap MS TOTAL KEY CX TEST failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s MS KEY TOTAL CX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS KEY TOTAL CX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + + kfree(total_cx); + } else + logError(0, "%s MS KEY TOTAL CX TEST:.................SKIPPED\n", tag); + + kfree(msCompData.node_data); +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s MS KEY CX testes finished!.................OK\n", tag); + return OK; + } + print_frame_u8("MS Key Init Data (Cx2) =", array1dTo2d_u8(msCompData.node_data, msCompData.node_data_size, msCompData.header.sense_node), 1, msCompData.header.sense_node); + logError(0, "%s MS Key CX testes finished!.................FAILED fails_count = %d\n\n", tag, count_fail); + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); +} + +int production_test_ss_raw(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + int ret; + int count_fail = 0; + int rows, columns; + + /* short *ssRawFrame = NULL; */ + SelfSenseFrame ssRawFrame; + + int *thresholds = NULL; + int trows, tcolumns; + + /* MS SS TEST */ + logError(0, "%s\n", tag); + logError(0, "%s SS RAW Testes are starting...\n", tag); + + /******************************* Self Sense Test *******************************/ + + logError(0, "%s Getting SS Frame...\n", tag); + ret = getSSFrame2(SS_TOUCH, &ssRawFrame); + if (ret < 0) { + logError(1, "%s production_test_data: getSSFrame failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + /* SS RAW (PROXIMITY) FORCE TEST */ + logError(0, "%s SS RAW (PROXIMITY) FORCE TEST:\n", tag); + + if (todo->SelfForceRaw == 1 || todo->SelfForceRawGap == 1) { + + columns = 1; + /* there are no data for the sense channels due to the fact that the force frame is analized */ + rows = ssRawFrame.header.force_node; + + logError(0, "%s SS RAW (PROXIMITY) FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceRaw == 1) { + + ret = parseProductionTestLimits(path_limits, SS_RAW_FORCE_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_RAW_FORCE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMinMax(ssRawFrame.force_data, rows, columns, thresholds[0], thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax SS RAW (PROXIMITY) FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS RAW (PROXIMITY) FORCE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + print_frame_short("SS Raw force frame =", array1dTo2d_short(ssRawFrame.force_data, rows*columns, columns), rows, columns); + if (stop_on_fail) + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); + } else + logError(0, "%s SS RAW (PROXIMITY) FORCE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds); + } else + logError(0, "%s SS RAW (PROXIMITY) FORCE MIN MAX TEST:.................SKIPPED\n\n", tag); + + logError(0, "%s\n", tag); + logError(0, "%s SS RAW (PROXIMITY) FORCE GAP TEST:\n", tag); + if (todo->SelfForceRawGap == 1) { + + ret = parseProductionTestLimits(path_limits, SS_RAW_FORCE_GAP, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_RAW_FORCE_GAP failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsGap(ssRawFrame.force_data, rows, columns, thresholds[0]); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsGap SS RAW (PROXIMITY) FORCE GAP failed... ERROR = %02X\n", tag, ret); + logError(0, "%s SS RAW (PROXIMITY) FORCE GAP TEST:.................FAIL\n\n", tag); + count_fail += 1; + print_frame_short("SS Raw force frame =", array1dTo2d_short(ssRawFrame.force_data, rows*columns, columns), rows, columns); + if (stop_on_fail) + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); + } else + logError(0, "%s SS RAW (PROXIMITY) FORCE GAP TEST:.................OK\n\n", tag); + + kfree(thresholds); + } else + logError(0, "%s SS RAW (PROXIMITY) FORCE GAP TEST:.................SKIPPED\n\n", tag); + + kfree(ssRawFrame.force_data); + } else + logError(0, "%s SS RAW (PROXIMITY) FORCE TEST:.................SKIPPED\n\n", tag); + + logError(0, "%s\n", tag); + /* SS RAW (PROXIMITY) SENSE TEST */ + logError(0, "%s SS RAW (PROXIMITY) SENSE TEST:\n", tag); + + if (todo->SelfSenseRaw == 1 || todo->SelfSenseRawGap == 1) { + columns = ssRawFrame.header.sense_node; + rows = 1; /* there are no data for the force channels due to the fact that the sense frame is analized */ + + logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseRaw == 1) { + ret = parseProductionTestLimits(path_limits, SS_RAW_SENSE_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_RAW_SENSE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMinMax(ssRawFrame.sense_data, rows, columns, thresholds[0], thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax SS RAW (PROXIMITY) SENSE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX TEST:.................FAIL\n", tag); + count_fail += 1; + print_frame_short("SS Raw sense frame =", array1dTo2d_short(ssRawFrame.sense_data, rows*columns, columns), rows, columns); + if (stop_on_fail) + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); + } else + logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX TEST:.................OK\n", tag); + + kfree(thresholds); + } else + logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX TEST:.................SKIPPED\n", tag); + + logError(0, "%s\n", tag); + logError(0, "%s SS RAW (PROXIMITY) SENSE GAP TEST:\n", tag); + if (todo->SelfSenseRawGap == 1) { + ret = parseProductionTestLimits(path_limits, SS_RAW_SENSE_GAP, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_RAW_SENSE_GAP failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsGap(ssRawFrame.sense_data, rows, columns, thresholds[0]); + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsGap SS RAW (PROXIMITY) SENSE GAP failed... ERROR = %02X\n", tag, ret); + logError(0, "%s SS RAW (PROXIMITY) SENSE GAP TEST:.................FAIL\n", tag); + count_fail += 1; + print_frame_short("SS Raw sense frame =", array1dTo2d_short(ssRawFrame.sense_data, rows*columns, columns), rows, columns); + if (stop_on_fail) + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); + } else + logError(0, "%s SS RAW (PROXIMITY) SENSE GAP TEST:.................OK\n", tag); + + kfree(thresholds); + } else + logError(0, "%s SS RAW (PROXIMITY) SENSE GAP TEST:.................SKIPPED\n", tag); + + kfree(ssRawFrame.sense_data); + } + + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s SS RAW testes finished!.................OK\n\n", tag); + return OK; + } + logError(0, "%s SS RAW testes finished!.................FAILED fails_count = %d\n\n", tag, count_fail); + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); +} + +int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + + int ret; + int count_fail = 0; + + int *thresholds = NULL; + int trows, tcolumns; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + + SelfSenseData ssCompData; + + u8 *adjhor = NULL; + u8 *adjvert = NULL; + + u16 container; + int *ix1_w, *ix2_w; + u16 *total_ix = NULL; + u16 *total_cx = NULL; + + u16 *total_adjhor = NULL; + u16 *total_adjvert = NULL; + + logError(0, "%s\n", tag); + logError(0, "%s SS IX CX testes are starting...\n", tag); + ret = readSelfSenseCompensationData(SS_TOUCH, &ssCompData); /* read the SS compensation data */ + if (ret < 0) { + logError(1, "%s production_test_data: readSelfSenseCompensationData failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + /*************************** SS FORCE IX *************************************/ + /* SS IX1 FORCE TEST */ + logError(0, "%s SS IX1 FORCE TEST:\n", tag); + if (todo->SelfForceIx1 == 1) { + + ret = parseProductionTestLimits(path_limits, SS_IX1_FORCE_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_FORCE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + container = (u16) ssCompData.f_ix1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax SS IX1 FORCE TEST failed... ERROR COUNT = %d\n", tag, ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS IX1 FORCE TEST:.................OK\n\n", tag); + } else + logError(0, "%s SS IX1 FORCE TEST:.................SKIPPED\n\n", tag); + + kfree(thresholds); + /* SS IX2 FORCE TEST */ + logError(0, "%s SS IX2 FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceIx2 == 1) { + ret = parseProductionTestLimits(path_limits, SS_IX2_FORCE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_FORCE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_IX2_FORCE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_FORCE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMap(ssCompData.ix2_fm, ssCompData.header.force_node, 1, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS IX2 FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS IX2 FORCE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS IX2 FORCE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS IX2 FORCE MIN MAX TEST:.................SKIPPED\n\n", tag); + + logError(0, "%s SS IX2 FORCE ADJ TEST:\n", tag); + if (todo->SelfForceIx2Adj == 1) { + /* SS IX2 FORCE ADJV TEST */ + logError(0, "%s SS IX2 FORCE ADJVERT TEST:\n", tag); + ret = computeAdjVert(ssCompData.ix2_fm, ssCompData.header.force_node, 1, &adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjVert SS IX2 FORCE ADJV failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS IX2 FORCE ADJV computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_IX2_FORCE_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node - 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_FORCE_ADJV_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdj(adjvert, ssCompData.header.force_node - 1, 1, thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS IX2 FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS IX2 FORCE ADJV TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS IX2 FORCE ADJV TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(adjvert); + + } else + logError(0, "%s SS IX2 FORCE ADJ TEST:.................SKIPPED\n\n", tag); + + /* SS TOTAL FORCE IX */ + logError(0, "%s SS TOTAL IX FORCE TEST:\n", tag); + if (todo->SelfForceIxTotal == 1 || todo->SelfForceIxTotalAdj == 1) { + logError(0, "%s Reading TOTAL IX FORCE Weights...\n", tag); + ret = parseProductionTestLimits(path_limits, SS_IX1_FORCE_W, &ix1_w, &trows, &tcolumns); /* load the IX1 weight */ + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_FORCE_W failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_IX2_FORCE_W, &ix2_w, &trows, &tcolumns); /* load the IX2 weight */ + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_FORCE_W failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s Weights: IX1_W = %d IX2_W = %d\n", tag, *ix1_w, *ix2_w); + + ret = computeTotal(ssCompData.ix2_fm, ssCompData.f_ix1, ssCompData.header.force_node, 1, *ix1_w, *ix2_w, &total_ix); + if (ret < 0) { + logError(1, "%s production_test_data: computeTotal Ix Force failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceIxTotal == 1) { + ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_FORCE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_FORCE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_FORCE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_FORCE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapTotal(total_ix, ssCompData.header.force_node, 1, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS TOTAL IX FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:.................SKIPPED\n", tag); + + logError(0, "%s SS TOTAL IX FORCE ADJ TEST:\n", tag); + if (todo->SelfForceIxTotalAdj == 1) { + /* SS TOTAL IX FORCE ADJV TEST */ + logError(0, "%s SS TOTAL IX FORCE ADJVERT TEST:\n", tag); + ret = computeAdjVertTotal(total_ix, ssCompData.header.force_node, 1, &total_adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjVert SS TOTAL IX FORCE ADJV failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS TOTAL IX FORCE ADJV computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_FORCE_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node - 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_FORCE_ADJV_MAP_MAX... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdjTotal(total_adjvert, ssCompData.header.force_node - 1, 1, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS TOTAL IX FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL IX FORCE ADJV TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL IX FORCE ADJV TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(total_adjvert); + } else + logError(0, "%s SS TOTAL IX FORCE ADJ TEST:.................SKIPPED\n", tag); + + kfree(total_ix); + } else + logError(0, "%s SS TOTAL IX FORCE TEST:.................SKIPPED\n\n", tag); + + /******************* SS SENSE IX **************************/ + /* SS IX1 SENSE TEST */ + logError(0, "%s SS IX1 SENSE TEST:\n", tag); + if (todo->SelfSenseIx1 == 1) { + + ret = parseProductionTestLimits(path_limits, SS_IX1_SENSE_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_SENSE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + container = (u16) ssCompData.s_ix1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax SS IX1 SENSE TEST failed... ERROR COUNT = %d\n", tag, ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS IX1 SENSE TEST:.................OK\n\n", tag); + } else + logError(0, "%s SS IX1 SENSE TEST:.................SKIPPED\n\n", tag); + + kfree(thresholds); + /* SS IX2 SENSE TEST */ + logError(0, "%s SS IX2 SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseIx2 == 1) { + ret = parseProductionTestLimits(path_limits, SS_IX2_SENSE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_IX2_SENSE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMap(ssCompData.ix2_sn, 1, ssCompData.header.sense_node, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS IX2 SENSE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS IX2 SENSE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS IX2 SENSE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS IX2 SENSE MIN MAX TEST:.................SKIPPED\n\n", tag); + + logError(0, "%s SS IX2 SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseIx2Adj == 1) { + /* SS IX2 SENSE ADJH TEST */ + logError(0, "%s SS IX2 SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHoriz(ssCompData.ix2_sn, 1, ssCompData.header.sense_node, &adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjHoriz SS IX2 SENSE ADJH failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS IX2 SENSE ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_IX2_SENSE_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_ADJH_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdj(adjhor, 1, ssCompData.header.sense_node - 1, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj SS IX2 SENSE ADJH failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS IX2 SENSE ADJH TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS IX2 SENSE ADJH TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(adjhor); + } else + logError(0, "%s SS IX2 SENSE ADJ TEST:.................SKIPPED\n", tag); + + /* SS TOTAL IX SENSE */ + logError(0, "%s SS TOTAL IX SENSE TEST:\n", tag); + if (todo->SelfSenseIxTotal == 1 || todo->SelfSenseIxTotalAdj == 1) { + logError(0, "%s Reading TOTAL IX SENSE Weights...\n", tag); + ret = parseProductionTestLimits(path_limits, SS_IX1_SENSE_W, &ix1_w, &trows, &tcolumns); /* load the IX1 weight */ + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_SENSE_W failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_IX2_SENSE_W, &ix2_w, &trows, &tcolumns); /* load the IX2 weight */ + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX1_SENSE_W failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s Weights: IX1_W = %d IX2_W = %d\n", tag, *ix1_w, *ix2_w); + + ret = computeTotal(ssCompData.ix2_sn, ssCompData.s_ix1, 1, ssCompData.header.sense_node, *ix1_w, *ix2_w, &total_ix); + if (ret < 0) { + logError(1, "%s production_test_data: computeTotal Ix Sense failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s SS TOTAL IX SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseIxTotal == 1) { + ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_SENSE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_SENSE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_SENSE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_SENSE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapTotal(total_ix, 1, ssCompData.header.sense_node, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS TOTAL IX SENSE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL IX SENSE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL IX SENSE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS TOTAL IX SENSE MIN MAX TEST:.................SKIPPED\n", tag); + + logError(0, "%s SS TOTAL IX SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseIxTotalAdj == 1) { + /* SS TOTAL IX SENSE ADJH TEST */ + logError(0, "%s SS TOTAL IX SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHorizTotal(total_ix, 1, ssCompData.header.sense_node, &total_adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjHoriz SS TOTAL IX SENSE ADJH failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS TOTAL IX SENSE ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_IX_SENSE_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_SENSE_ADJH_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdjTotal(total_adjhor, 1, ssCompData.header.sense_node - 1, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj SS TOTAL IX SENSE ADJH failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL IX SENSE ADJH TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL IX SENSE ADJH TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(total_adjhor); + } else + logError(0, "%s SS TOTAL IX SENSE ADJ TEST:.................SKIPPED\n", tag); + kfree(total_ix); + } else + logError(0, "%s SS TOTAL IX SENSE TEST:.................SKIPPED\n", tag); + + /*********************** SS SENSE CX ******************************/ + /* SS CX1 FORCE TEST */ + logError(0, "%s SS CX1 FORCE TEST:\n", tag); + if (todo->SelfForceCx1 == 1) { + + ret = parseProductionTestLimits(path_limits, SS_CX1_FORCE_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_CX1_FORCE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + container = (u16) ssCompData.f_cx1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax SS CX1 FORCE TEST failed... ERROR COUNT = %d\n", tag, ret); + count_fail += 1; + /* if (stop_on_fail) return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); */ + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS CX1 FORCE TEST:.................OK\n\n", tag); + kfree(thresholds); + } else + logError(0, "%s SS CX1 FORCE TEST:.................SKIPPED\n\n", tag); + + /* SS CX2 FORCE TEST */ + logError(0, "%s SS CX2 FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceCx2 == 1) { + ret = parseProductionTestLimits(path_limits, SS_CX2_FORCE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_CX2_FORCE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_CX2_FORCE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_CX2_FORCE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMap(ssCompData.cx2_fm, ssCompData.header.force_node, 1, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS CX2 FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS CX2 FORCE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS CX2 FORCE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS CX2 FORCE MIN MAX TEST:.................SKIPPED\n", tag); + + logError(0, "%s SS CX2 FORCE ADJ TEST:\n", tag); + if (todo->SelfForceCx2Adj == 1) { + /* SS CX2 FORCE ADJV TEST */ + logError(0, "%s SS CX2 FORCE ADJVERT TEST:\n", tag); + ret = computeAdjVert(ssCompData.cx2_fm, ssCompData.header.force_node, + 1, &adjvert); /* comepute the ADJV for CX2 FORCE */ + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjVert SS CX2 FORCE ADJV failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS CX2 FORCE ADJV computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_CX2_FORCE_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node - 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_CX2_FORCE_ADJV_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdj(adjvert, ssCompData.header.force_node - 1, 1, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS IX2 FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS CX2 FORCE ADJV TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS CX2 FORCE ADJV TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(adjvert); + } else + logError(0, "%s SS CX2 FORCE ADJ TEST:.................SKIPPED\n\n", tag); + + /* SS TOTAL CX FORCE */ + logError(0, "%s SS TOTAL CX FORCE TEST:\n", tag); + if (todo->SelfForceCxTotal == 1 || todo->SelfForceCxTotalAdj == 1) { + ret = computeTotal(ssCompData.cx2_fm, ssCompData.f_cx1, ssCompData.header.force_node, 1, CX1_WEIGHT, CX2_WEIGHT, &total_cx); + if (ret < 0) { + logError(1, "%s production_test_data: computeTotal Cx Force failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s SS TOTAL CX FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceCxTotal == 1) { + ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_FORCE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_FORCE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_FORCE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_FORCE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapTotal(total_cx, ssCompData.header.force_node, 1, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS TOTAL FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL FORCE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL FORCE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS TOTAL CX FORCE MIN MAX TEST:.................SKIPPED\n", tag); + + /* SS TOTAL CX FORCE ADJV TEST */ + logError(0, "%s SS TOTAL CX FORCE ADJ TEST:\n", tag); + if (todo->SelfForceCxTotalAdj == 1) { + logError(0, "%s SS TOTAL CX FORCE ADJVERT TEST:\n", tag); + ret = computeAdjVertTotal(total_cx, ssCompData.header.force_node, 1, &total_adjvert); /* comepute the ADJV for CX2 FORCE */ + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjVert SS TOTAL CX FORCE ADJV failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS TOTAL CX FORCE ADJV computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_FORCE_ADJV_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node - 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_FORCE_ADJV_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdjTotal(total_adjvert, ssCompData.header.force_node - 1, 1, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS TOTAL CX FORCE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL CX FORCE ADJV TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL CX FORCE ADJV TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(total_adjvert); + + } else + logError(0, "%s SS TOTAL CX FORCE ADJ TEST:.................SKIPPED\n", tag); + kfree(total_cx); + } else + logError(0, "%s SS TOTAL CX FORCE TEST:.................SKIPPED\n\n", tag); + + /* ********************** SS SENSE CX ************************************/ + /* SS CX1 SENSE TEST */ + logError(0, "%s SS CX1 SENSE TEST:\n", tag); + if (todo->SelfSenseCx1 == 1) { + + ret = parseProductionTestLimits(path_limits, SS_CX1_SENSE_MIN_MAX, &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_CX1_SENSE_MIN_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + container = (u16) ssCompData.s_cx1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], thresholds[1]); /* check the limits */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMinMax SS CX1 SENSE TEST failed... ERROR COUNT = %d\n", tag, ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS CX1 SENSE TEST:.................OK\n\n", tag); + kfree(thresholds); + } else + logError(0, "%s SS CX1 SENSE TEST:.................SKIPPED\n\n", tag); + + /* SS CX2 SENSE TEST */ + logError(0, "%s SS CX2 SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseCx2 == 1) { + ret = parseProductionTestLimits(path_limits, SS_CX2_SENSE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_CX2_SENSE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_CX2_SENSE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_CX2_SENSE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMap(ssCompData.cx2_sn, 1, ssCompData.header.sense_node, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS CX2 SENSE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS CX2 SENSE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS CX2 SENSE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS CX2 SENSE MIN MAX TEST:.................SKIPPED\n", tag); + + logError(0, "%s SS CX2 SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseCx2Adj == 1) { + /* SS CX2 SENSE ADJH TEST */ + logError(0, "%s SS CX2 SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHoriz(ssCompData.ix2_sn, 1, ssCompData.header.sense_node, &adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjHoriz SS CX2 SENSE ADJH failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS CX2 SENSE ADJH computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_CX2_SENSE_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdj(adjhor, 1, ssCompData.header.sense_node - 1, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj SS CX2 SENSE ADJH failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS CX2 SENSE ADJH TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS CX2 SENSE ADJH TEST:.................OK\n", tag); + + kfree(thresholds_max); + kfree(adjhor); + } else + logError(0, "%s SS CX2 SENSE ADJ TEST:.................SKIPPED\n\n", tag); + + /* SS TOTAL CX SENSE */ + logError(0, "%s SS TOTAL CX SENSE TEST:\n", tag); + if (todo->SelfSenseCxTotal == 1 || todo->SelfSenseCxTotalAdj == 1) { + ret = computeTotal(ssCompData.cx2_sn, ssCompData.s_cx1, 1, ssCompData.header.sense_node, CX1_WEIGHT, CX2_WEIGHT, &total_cx); + if (ret < 0) { + logError(1, "%s production_test_data: computeTotal Cx Sense failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseCxTotal == 1) { + ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_SENSE_MAP_MIN, &thresholds_min, &trows, &tcolumns); /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_SENSE_MAP_MIN failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_SENSE_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_SENSE_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapTotal(total_cx, 1, ssCompData.header.sense_node, thresholds_min, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMap SS TOTAL CX SENSE failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:.................OK\n\n", tag); + + kfree(thresholds_min); + kfree(thresholds_max); + } else + logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:.................SKIPPED\n", tag); + + /* SS TOTAL IX SENSE ADJH TEST */ + logError(0, "%s SS TOTAL CX SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseCxTotalAdj == 1) { + logError(0, "%s SS TOTAL CX SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHorizTotal(total_cx, 1, ssCompData.header.sense_node, &total_adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: computeAdjHoriz SS TOTAL CX SENSE ADJH failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + logError(0, "%s SS TOTAL CX SENSE ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, SS_TOTAL_CX_SENSE_ADJH_MAP_MAX, &thresholds_max, &trows, &tcolumns); /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_SENSE_ADJH_MAP_MAX failed... ERROR %02X\n", tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = checkLimitsMapAdjTotal(total_adjhor, 1, ssCompData.header.sense_node - 1, thresholds_max); /* check the values with thresholds */ + if (ret != OK) { + logError(1, "%s production_test_data: checkLimitsMapAdj SS TOTAL CX SENSE ADJH failed... ERROR COUNT = %d\n", tag, ret); + logError(0, "%s SS TOTAL CX SENSE ADJH TEST:.................FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS TOTAL CX SENSE ADJH TEST:.................OK\n\n", tag); + + kfree(thresholds_max); + kfree(total_adjhor); + } else + logError(0, "%s SS TOTAL CX SENSE ADJ TEST:.................SKIPPED\n", tag); + kfree(total_cx); + } else + logError(0, "%s SS TOTAL CX SENSE TEST:.................SKIPPED\n", tag); + + /* SS compensation data are no usefull anymore */ + + kfree(ssCompData.ix2_fm); + kfree(ssCompData.ix2_sn); + kfree(ssCompData.cx2_fm); + kfree(ssCompData.cx2_sn); + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s SS IX CX testes finished!.................OK\n\n", tag); + return OK; + } + /* print all kind of data in just one row for readability reason */ + print_frame_u8("SS Init Data Ix2_fm = ", array1dTo2d_u8(ssCompData.ix2_fm, ssCompData.header.force_node, ssCompData.header.force_node), 1, ssCompData.header.force_node); + print_frame_u8("SS Init Data Cx2_fm = ", array1dTo2d_u8(ssCompData.cx2_fm, ssCompData.header.force_node, ssCompData.header.force_node), 1, ssCompData.header.force_node); + print_frame_u8("SS Init Data Ix2_sn = ", array1dTo2d_u8(ssCompData.ix2_sn, ssCompData.header.sense_node, ssCompData.header.sense_node), 1, ssCompData.header.sense_node); + print_frame_u8("SS Init Data Cx2_sn = ", array1dTo2d_u8(ssCompData.cx2_sn, ssCompData.header.sense_node, ssCompData.header.sense_node), 1, ssCompData.header.sense_node); + logError(0, "%s SS IX CX testes finished!.................FAILED fails_count = %d\n\n", tag, count_fail); + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); +} + +int production_test_data(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + int res = OK, ret; + + if (todo == NULL) { + logError(1, "%s production_test_data: No TestToDo specified!! ERROR = %02X\n", tag, (ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_DATA)); + return (ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s DATA Production test is starting...\n", tag); + + ret = production_test_ms_raw(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(1, "%s production_test_data: production_test_ms_raw failed... ERROR = %02X\n", tag, ret); + if (stop_on_fail == 1) + goto END; + } + + ret = production_test_ms_cx(path_limits, stop_on_fail, todo); + res |= ret; + if (res < 0) { + logError(1, "%s production_test_data: production_test_ms_cx failed... ERROR = %02X\n", tag, ret); + if (stop_on_fail == 1) + goto END; + } + + ret = production_test_ss_raw(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(1, "%s production_test_data: production_test_ss_raw failed... ERROR = %02X\n", tag, ret); + if (stop_on_fail == 1) + goto END; + } + + ret = production_test_ss_ix_cx(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(1, "%s production_test_data: production_test_ss_ix_cx failed... ERROR = %02X\n", tag, ret); + if (stop_on_fail == 1) + goto END; + } + +END: + if (res < OK) + logError(0, "%s DATA Production test failed!\n", tag); + else + logError(0, "%s DATA Production test finished!\n", tag); + return res; +} + +int save_mp_flag(u32 signature) +{ + int res = -1, ret; + int i; + u8 cmd[6] = {FTS_CMD_WRITE_MP_FLAG, 0x00, 0x00, 0x00, 0x00, 0x00}; + + u32ToU8(signature, &cmd[2]); + + logError(0, "%s Starting Saving Flag with signature = %08X ...\n", tag, signature); + + for (i = 0; i < SAVE_FLAG_RETRY && res < OK; i++) { + logError(0, "%s Attempt number %d to save mp flag !\n", tag, i+1); + logError(0, "%s Command write flag sent...\n", tag); + res = fts_writeFwCmd(cmd, 6); + if (res >= OK) + res = save_cx_tuning(); + + } + + ret = readChipInfo(1); /* need to update the MP Flag */ + if (ret < OK) { + logError(1, "%s save_mp_flag: read chip info ERROR %08X\n", tag, ret); + } + + res |= ret; + if (res < OK) { + logError(1, "%s save_mp_flag: ERROR %08X ...\n", tag, res); + return res; + } + logError(1, "%s Saving Flag DONE!\n", tag); + return OK; +} + +int parseProductionTestLimits(char *path, char *label, int **data, int *row, int *column) +{ + + int find = 0; + char *token = NULL; + int i = 0; + int j = 0; + int z = 0; + + char *line = NULL; + int fd = -1; + char *buf = NULL; + int n, size, pointer = 0, ret; + char *data_file = NULL; +#ifndef LIMITS_H_FILE + const struct firmware *fw = NULL; + struct device *dev = NULL; + + dev = getDev(); + if (dev != NULL) + fd = request_firmware(&fw, path, dev); +#else + fd = 0; +#endif + + line = (char *)kmalloc(1800*sizeof(char), GFP_KERNEL); + buf = line; + + if (fd == 0) { +#ifndef LIMITS_H_FILE + size = fw->size; + data_file = (char *)fw->data; + logError(0, "%s Start to reading %s...\n", tag, path); +#else + size = LIMITS_SIZE_NAME; + data_file = (char *)(LIMITS_ARRAY_NAME); +#endif + + logError(0, "%s The size of the limits file is %d bytes...\n", tag, size); + + while (find == 0) { + /* start to look for the wanted label */ + if (readLine(&data_file[pointer], &line, size-pointer, &n) < 0) { + find = -1; + break; + } + pointer += n; + if (line[0] == '*') { /* each header row start with * ex. *label,n_row,n_colum */ + buf = line; + line += 1; + token = strsep(&line, ","); + if (strcmp(token, label) == 0) { + /* if the row is the wanted one i retrieve rows and columns info */ + find = 1; + token = strsep(&line, ","); + if (token != NULL) { + sscanf(token, "%d", row); + logError(0, "%s Row = %d\n", tag, *row); + } else { + logError(1, "%s parseProductionTestLimits 1: ERROR %02X\n", tag, ERROR_FILE_PARSE); + /* release_firmware(fw); */ + /* return ERROR_FILE_PARSE; */ + ret = ERROR_FILE_PARSE; + goto END; + } + token = strsep(&line, ","); + if (token != NULL) { + sscanf(token, "%d", column); + logError(0, "%s Column = %d\n", tag, *column); + } else { + logError(1, "%s parseProductionTestLimits 2: ERROR %02X\n", tag, ERROR_FILE_PARSE); + /* release_firmware(fw); */ + /* return ERROR_FILE_PARSE; */ + ret = ERROR_FILE_PARSE; + goto END; + } + + *data = (int *)kmalloc(((*row)*(*column))*sizeof(int), GFP_KERNEL); + /* allocate the memory for containing the data */ + j = 0; + if (*data == NULL) { + logError(1, "%s parseProductionTestLimits: ERROR %02X\n", tag, ERROR_ALLOC); + /* release_firmware(fw); */ + /* return ERROR_ALLOC; */ + ret = ERROR_ALLOC; + goto END; + } + + /* start to read the data */ + for (i = 0; i < *row; i++) { + line = buf; + if (readLine(&data_file[pointer], &line, size-pointer, &n) < 0) { + logError(1, "%s parseProductionTestLimits : ERROR %02X\n", tag, ERROR_FILE_READ); + /* release_firmware(fw); */ + /* return ERROR_FILE_READ; */ + ret = ERROR_FILE_READ; + goto END; + } + pointer += n; + token = strsep(&line, ","); + for (z = 0; (z < *column) && (token != NULL); z++) { + sscanf(token, "%d", ((*data) + j)); + j++; + token = strsep(&line, ","); + } + } + if (j == ((*row)*(*column))) { /* check that all the data are read */ + logError(0, "%s READ DONE!\n", tag); + /* release_firmware(fw); */ + /* return OK; */ + ret = OK; + goto END; + } + logError(1, "%s parseProductionTestLimits 3: ERROR %02X\n", tag, ERROR_FILE_PARSE); + /* release_firmware(fw); */ + /* return ERROR_FILE_PARSE; */ + ret = ERROR_FILE_PARSE; + goto END; + } + } + + } + logError(1, "%s parseProductionTestLimits: ERROR %02X\n", tag, ERROR_LABEL_NOT_FOUND); + ret = ERROR_LABEL_NOT_FOUND; +END: +#ifndef LIMITS_H_FILE + release_firmware(fw); +#endif + return ret; + + } else { + logError(1, "%s parseProductionTestLimits: ERROR %02X\n", tag, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + +} + +int readLine(char *data, char **line, int size, int *n) +{ + int i = 0; + if (size < 1) + return 1; + + while (data[i] != '\n' && i < size) { + *(*line + i) = data[i]; + i++; + } + *n = i+1; + *(*line + i) = '\0'; + + return OK; + +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTest.h b/drivers/input/touchscreen/st/fts_lib/ftsTest.h new file mode 100644 index 000000000000..93da901c9f42 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTest.h @@ -0,0 +1,158 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS API for MP test * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsSoftware.h" + +#define LIMITS_FILE "stm_fts_production_limits.csv" + +#define WAIT_FOR_FRESH_FRAMES 100 /* ms */ +#define WAIT_AFTER_SENSEOFF 50 /* ms */ +#define TIMEOUT_ITO_TEST_RESULT 200 /* ms */ +#define TIMEOUT_INITIALIZATION_TEST_RESULT 5000 /* ms */ + +/* LABELS PRODUCTION TEST LIMITS FILE */ +#define MS_RAW_MIN_MAX "MS_RAW_DATA_MIN_MAX" +#define MS_RAW_GAP "MS_RAW_DATA_GAP" +#define MS_CX1_MIN_MAX "MS_TOUCH_ACTIVE_CX1_MIN_MAX" +#define MS_CX2_MAP_MIN "MS_TOUCH_ACTIVE_CX2_MIN" +#define MS_CX2_MAP_MAX "MS_TOUCH_ACTIVE_CX2_MAX" +#define MS_CX2_ADJH_MAP_MAX "MS_TOUCH_ACTIVE_CX2_ADJ_HORIZONTAL" +#define MS_CX2_ADJV_MAP_MAX "MS_TOUCH_ACTIVE_CX2_ADJ_VERTICAL" +#define MS_TOTAL_CX_MAP_MIN "MS_TOUCH_ACTIVE_TOTAL_CX_MIN" +#define MS_TOTAL_CX_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_MAX" +#define MS_TOTAL_CX_ADJH_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_ADJ_HORIZONTAL" +#define MS_TOTAL_CX_ADJV_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_ADJ_VERTICAL" +#define SS_RAW_FORCE_MIN_MAX "SS_RAW_DATA_FORCE_MIN_MAX" +#define SS_RAW_SENSE_MIN_MAX "SS_RAW_DATA_SENSE_MIN_MAX" +#define SS_RAW_FORCE_GAP "SS_RAW_DATA_FORCE_GAP" +#define SS_RAW_SENSE_GAP "SS_RAW_DATA_SENSE_GAP" +#define SS_IX1_FORCE_MIN_MAX "SS_TOUCH_ACTIVE_IX1_FORCE_MIN_MAX" +#define SS_IX1_SENSE_MIN_MAX "SS_TOUCH_ACTIVE_IX1_SENSE_MIN_MAX" +#define SS_CX1_FORCE_MIN_MAX "SS_TOUCH_ACTIVE_CX1_FORCE_MIN_MAX" +#define SS_CX1_SENSE_MIN_MAX "SS_TOUCH_ACTIVE_CX1_SENSE_MIN_MAX" +#define SS_IX2_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_IX2_FORCE_MIN" +#define SS_IX2_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_IX2_FORCE_MAX" +#define SS_IX2_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_IX2_SENSE_MIN" +#define SS_IX2_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_IX2_SENSE_MAX" +#define SS_IX2_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_IX2_ADJ_VERTICAL" +#define SS_IX2_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_IX2_ADJ_HORIZONTAL" +#define SS_CX2_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_CX2_FORCE_MIN" +#define SS_CX2_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_CX2_FORCE_MAX" +#define SS_CX2_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_CX2_SENSE_MIN" +#define SS_CX2_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_CX2_SENSE_MAX" +#define SS_CX2_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_CX2_ADJ_VERTICAL" +#define SS_CX2_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_CX2_ADJ_HORIZONTAL" + +/* TOTAL SS */ +#define SS_TOTAL_IX_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_IX_FORCE_MIN" +#define SS_TOTAL_IX_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_FORCE_MAX" +#define SS_TOTAL_IX_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_IX_SENSE_MIN" +#define SS_TOTAL_IX_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_SENSE_MAX" +#define SS_TOTAL_IX_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_ADJ_VERTICAL" +#define SS_TOTAL_IX_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_ADJ_HORIZONTAL" +#define SS_TOTAL_CX_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_CX_FORCE_MIN" +#define SS_TOTAL_CX_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_FORCE_MAX" +#define SS_TOTAL_CX_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_CX_SENSE_MIN" +#define SS_TOTAL_CX_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_SENSE_MAX" +#define SS_TOTAL_CX_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_ADJ_VERTICAL" +#define SS_TOTAL_CX_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_ADJ_HORIZONTAL" + +/* KEYS */ +#define MS_KEY_RAW_MIN_MAX "MS_KEY_RAW_DATA_MIN_MAX" +#define MS_KEY_CX1_MIN_MAX "MS_KEY_CX1_MIN_MAX" +#define MS_KEY_CX2_MAP_MIN "MS_KEY_CX2_MIN" +#define MS_KEY_CX2_MAP_MAX "MS_KEY_CX2_MAX" +#define MS_KEY_TOTAL_CX_MAP_MIN "MS_KEY_TOTAL_CX_MIN" +#define MS_KEY_TOTAL_CX_MAP_MAX "MS_KEY_TOTAL_CX_MAX" + +/* CONSTANT TOTAL IX */ +#define SS_IX1_FORCE_W "SS_IX1_FORCE_W" +#define SS_IX2_FORCE_W "SS_IX2_FORCE_W" +#define SS_IX1_SENSE_W "SS_IX1_SENSE_W" +#define SS_IX2_SENSE_W "SS_IX2_SENSE_W" + +#define SAVE_FLAG_RETRY 3 + +typedef struct { + int MutualRaw; + int MutualRawGap; + int MutualCx1; + int MutualCx2; + int MutualCx2Adj; + int MutualCxTotal; + int MutualCxTotalAdj; + + int MutualKeyRaw; + int MutualKeyCx1; + int MutualKeyCx2; + int MutualKeyCxTotal; + + int SelfForceRaw; + int SelfForceRawGap; + int SelfForceIx1; + int SelfForceIx2; + int SelfForceIx2Adj; + int SelfForceIxTotal; + int SelfForceIxTotalAdj; + int SelfForceCx1; + int SelfForceCx2; + int SelfForceCx2Adj; + int SelfForceCxTotal; + int SelfForceCxTotalAdj; + + int SelfSenseRaw; + int SelfSenseRawGap; + int SelfSenseIx1; + int SelfSenseIx2; + int SelfSenseIx2Adj; + int SelfSenseIxTotal; + int SelfSenseIxTotalAdj; + int SelfSenseCx1; + int SelfSenseCx2; + int SelfSenseCx2Adj; + int SelfSenseCxTotal; + int SelfSenseCxTotalAdj; + +} TestToDo; + +int computeAdjHoriz(u8 *data, int row, int column, u8 **result); +int computeAdjHorizTotal(u16 *data, int row, int column, u16 **result); +int computeAdjVert(u8 *data, int row, int column, u8 **result); +int computeAdjVertTotal(u16 *data, int row, int column, u16 **result); +int computeTotal(u8 *data, u8 main, int row, int column, int m, int n, u16 **result); +int checkLimitsMinMax(short *data, int row, int column, int min, int max); +int checkLimitsMap(u8 *data, int row, int column, int *min, int *max); +int checkLimitsMapTotal(u16 *data, int row, int column, int *min, int *max); +int checkLimitsMapAdj(u8 *data, int row, int column, int *max); +int checkLimitsMapAdjTotal(u16 *data, int row, int column, int *max); +int production_test_ito(void); +int production_test_initialization(void); +int ms_compensation_tuning(void); +int ss_compensation_tuning(void); +int lp_timer_calibration(void); +int save_cx_tuning(void); +int production_test_splited_initialization(int saveToFlash); +int production_test_main(char *pathThresholds, int stop_on_fail, + int saveInit, TestToDo *todo, u32 signature); +int production_test_ms_raw(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_ms_cx(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_ss_raw(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_data(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_ms_key_cx(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_ms_key_raw(char *path_limits); +int save_mp_flag(u32 signature); +int parseProductionTestLimits(char *path, char *label, int **data, int *row, int *column); +int readLine(char *data, char **line, int size, int *n); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTime.c b/drivers/input/touchscreen/st/fts_lib/ftsTime.c new file mode 100644 index 000000000000..03a2a39fe10b --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTime.c @@ -0,0 +1,84 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS Utility for mesuring/handling the time * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsCrossCompile.h" +#include "ftsTime.h" + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <stdarg.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/time.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +/* #include <linux/sec_sysfs.h> */ + +void startStopWatch(StopWatch *w) +{ + w->start = current_kernel_time(); +} + +void stopStopWatch(StopWatch *w) +{ + w->end = current_kernel_time(); +} + +int elapsedMillisecond(StopWatch *w) +{ + int result; + + result = ((w->end.tv_sec - w->start.tv_sec)*1000) + (w->end.tv_nsec - w->start.tv_nsec) / 1000000; + return result; +} + +int elapsedNanosecond(StopWatch *w) +{ + int result; + + result = ((w->end.tv_sec - w->start.tv_sec)*1000000000) + (w->end.tv_nsec - w->start.tv_nsec); + return result; +} + +char *timestamp(void) +{ + char *result = NULL; + result = (char *)kmalloc((1)*sizeof(char), GFP_KERNEL); + if (result == NULL) + return NULL; + result[0] = ' '; + return result; +} + +void stdelay(unsigned long ms) +{ + msleep(ms); +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTime.h b/drivers/input/touchscreen/st/fts_lib/ftsTime.h new file mode 100644 index 000000000000..5eeca6eace97 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTime.h @@ -0,0 +1,29 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS Utility for mesuring/handling the time * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsCrossCompile.h" + +#include <linux/time.h> + +typedef struct { + struct timespec start, end; +} StopWatch; + +void startStopWatch(StopWatch *w); +void stopStopWatch(StopWatch *w); +int elapsedMillisecond(StopWatch *w); +int elapsedNanosecond(StopWatch *w); +char *timestamp(void); +void stdelay(unsigned long ms); diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTool.c b/drivers/input/touchscreen/st/fts_lib/ftsTool.c new file mode 100644 index 000000000000..4c5d54f17ea7 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTool.c @@ -0,0 +1,706 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS Utility Functions * +* * +************************************************************************** +************************************************************************** + +*/ + +#include "ftsCompensation.h" +#include "ftsCrossCompile.h" +#include "ftsError.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTime.h" +#include "ftsTool.h" +#include "../fts.h" /* needed for the PHONE_KEY define */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <stdarg.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/serio.h> +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/power_supply.h> +#include <linux/firmware.h> +#include <linux/gpio.h> + +/* static char tag[8]="[ FTS ]\0"; */ +static int reset_gpio = GPIO_NOT_DEFINED; +static int system_resetted_up; +static int system_resetted_down; +extern chipInfo ftsInfo; + +int readB2(u16 address, u8 *outBuf, int len) +{ + int remaining = len; + int toRead = 0; + int retry = 0; + int ret; + int event_to_search[3]; + u8 *readEvent = (u8 *)kmalloc(FIFO_EVENT_SIZE*sizeof(u8), GFP_KERNEL); + u8 cmd[4] = { FTS_CMD_REQU_FW_CONF, 0x00, 0x00, (u8)len }; + + if (readEvent == NULL) { + logError(1, "%s readB2: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + u16ToU8_be(address, &cmd[1]); + + logError(0, "%s %s", tag, printHex("Command B2 = ", cmd, 4)); + do { + remaining = len; + ret = fts_writeFwCmd(cmd, 4); + if (ret < 0) { + logError(1, "%s readB2: ERROR %02X\n", tag, ERROR_I2C_W); + return ret; + } /* ask to the FW the data */ + logError(0, "%s Command to FW sent!\n", tag); + event_to_search[0] = (int)EVENTID_FW_CONFIGURATION; + + while (remaining > OK) { + event_to_search[1] = (int)((address & 0xFF00)>>8); + event_to_search[2] = (int) (address & 0x00FF); + if (remaining > B2_DATA_BYTES) { + toRead = B2_DATA_BYTES; + remaining -= B2_DATA_BYTES; + } else { + toRead = remaining; + remaining = 0; + } + + ret = pollForEvent(event_to_search, 3, readEvent, GENERAL_TIMEOUT); + if (ret >= OK) { /* start the polling for reading the reply */ + memcpy(outBuf, &readEvent[3], toRead); + retry = 0; + outBuf += toRead; + + } else { + retry += 1; + break; + } + address += B2_DATA_BYTES; + } + + } while (retry < B2_RETRY && retry != 0); + + kfree(readEvent); + if (retry == B2_RETRY) { + logError(1, "%s readB2: ERROR %02X\n", tag, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + logError(0, "%s B2 read %d bytes\n", tag, len); + + return OK; +} + +int readB2U16(u16 address, u8 *outBuf, int byteToRead) +{ + + int remaining = byteToRead; + int toRead = 0; + int ret; + + u8 *buff = (u8 *)kmalloc((B2_CHUNK + 1)*sizeof(u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s readB2U16: ERROR %02X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + if (remaining >= B2_CHUNK) { + toRead = B2_CHUNK; + remaining -= B2_CHUNK; + } else { + toRead = remaining; + remaining = 0; + } + + ret = readB2(address, buff, toRead); + if (ret < 0) + return ret; + memcpy(outBuf, buff, toRead); + + address += toRead; + + outBuf += toRead; + + } + + kfree(buff); + return OK; +} + +int releaseInformation(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_RELEASE_INFO }; + int event_to_search[1]; + u8 readEvent[FIFO_EVENT_SIZE]; + + event_to_search[0] = (int)EVENTID_RELEASE_INFO; + + logError(0, "%s releaseInformation started... Chip INFO:\n", tag); + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s releaseInformation: ERROR %02X\n", tag, ret); + return ret; + } + + ret = pollForEvent(event_to_search, 1, &readEvent[0], RELEASE_INFO_TIMEOUT); + /* start the polling for reading the reply */ + if (ret < OK) { + logError(1, "%s releaseInformation: ERROR %02X\n", tag, ret); + return ret; + } + + logError(0, "%s releaseInformation: Finished!\n", tag, ret); + return OK; + +} + +char *printHex(char *label, u8 *buff, int count) +{ + int i, offset; + char *result = NULL; + + offset = strlen(label); + result = (char *)kmalloc(((offset + 3 * count) + 1)*sizeof(char), GFP_KERNEL); + if (result != NULL) { + strlcpy(result, label, sizeof(result)); + + for (i = 0; i < count; i++) { + snprintf(&result[offset + i * 3], 4, "%02X ", buff[i]); + } + strlcat(result, "\n", sizeof(result)); + } + return result; +} + +int pollForEvent(int *event_to_search, int event_bytes, u8 *readData, int time_to_wait) +{ + int i, find, retry, count_err; + int time_to_count; + int err_handling = OK; + StopWatch clock; + + u8 cmd[1] = { FIFO_CMD_READONE }; + char *temp = NULL; + + find = 0; + retry = 0; + count_err = 0; + time_to_count = time_to_wait / TIMEOUT_RESOLUTION; + + startStopWatch(&clock); + while (find != 1 && retry < time_to_count && fts_readCmd(cmd, 1, readData, FIFO_EVENT_SIZE) >= 0) { + /* Log of errors */ + if (readData[0] == EVENTID_ERROR_EVENT) { + logError(1, "%s %s", tag, printHex("ERROR EVENT = ", readData, FIFO_EVENT_SIZE)); + count_err++; + err_handling = errorHandler(readData, FIFO_EVENT_SIZE); + if ((err_handling&0xF0FF0000) == ERROR_HANDLER_STOP_PROC) { + logError(1, "%s pollForEvent: forced to be stopped! ERROR %08X\n", tag, err_handling); + return err_handling; + } + } else { + if (readData[0] != EVENTID_NO_EVENT) { + logError(1, "%s %s", tag, printHex("READ EVENT = ", readData, FIFO_EVENT_SIZE)); + } + if (readData[0] == EVENTID_CONTROL_READY && event_to_search[0] != EVENTID_CONTROL_READY) { + logError(1, "%s pollForEvent: Unmanned Controller Ready Event! Setting reset flags...\n", tag); + setSystemResettedUp(1); + setSystemResettedDown(1); + } + } + + find = 1; + + for (i = 0; i < event_bytes; i++) { + + if (event_to_search[i] != -1 && (int)readData[i] != event_to_search[i]) { + find = 0; + break; + } + } + + retry++; + msleep(TIMEOUT_RESOLUTION); + } + stopStopWatch(&clock); + if ((retry >= time_to_count) && find != 1) { + logError(1, "%s pollForEvent: ERROR %02X\n", tag, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } else if (find == 1) { + temp = printHex("FOUND EVENT = ", readData, FIFO_EVENT_SIZE); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + logError(0, "%s Event found in %d ms (%d iterations)! Number of errors found = %d\n", tag, elapsedMillisecond(&clock), retry, count_err); + return count_err; + } + logError(1, "%s pollForEvent: ERROR %02X\n", tag, ERROR_I2C_R); + return ERROR_I2C_R; +} + +int flushFIFO(void) +{ + + u8 cmd = FIFO_CMD_FLUSH; /* flush the FIFO */ + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s flushFIFO: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + logError(0, "%s FIFO flushed!\n", tag); + return OK; + +} + +int fts_disableInterrupt(void) +{ + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, IER_DISABLE }; /* disable interrupt */ + u16ToU8_be(IER_ADDR, &cmd[1]); + + if (fts_writeCmd(cmd, 4) < OK) { + logError(1, "%s fts_disableInterrupt: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + logError(0, "%s Interrupt Disabled!\n", tag); + return OK; +} + +int fts_enableInterrupt(void) +{ + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, IER_ENABLE }; /* enable interrupt */ + u16ToU8_be(IER_ADDR, &cmd[1]); + if (fts_writeCmd(cmd, 4) < 0) { + logError(1, "%s fts_enableInterrupt: ERROR %02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + logError(0, "%s Interrupt Enabled!\n", tag); + return OK; +} + +int u8ToU16n(u8 *src, int src_length, u16 *dst) +{ + int i, j; + + if (src_length % 2 != 0) { + return 0; + } + j = 0; + dst = (u16 *)kmalloc((src_length / 2)*sizeof(u16), GFP_KERNEL); + for (i = 0; i < src_length; i += 2) { + dst[j] = ((src[i+1] & 0x00FF) << 8) + (src[i] & 0x00FF); + j++; + } + + return (src_length / 2); +} + +int u8ToU16(u8 *src, u16 *dst) +{ + *dst = (u16)(((src[1] & 0x00FF) << 8) + (src[0] & 0x00FF)); + return 0; +} + +int u8ToU16_le(u8 *src, u16 *dst) +{ + *dst = (u16)(((src[0] & 0x00FF) << 8) + (src[1] & 0x00FF)); + return 0; +} + +int u16ToU8n(u16 *src, int src_length, u8 *dst) +{ + int i, j; + dst = (u8 *)kmalloc((2 * src_length)*sizeof(u8), GFP_KERNEL); + j = 0; + for (i = 0; i < src_length; i++) { + dst[j] = (u8) (src[i] & 0xFF00)>>8; + dst[j+1] = (u8) (src[i] & 0x00FF); + j += 2; + } + + return src_length * 2; + +} + +int u16ToU8(u16 src, u8 *dst) +{ + dst[0] = (u8)((src & 0xFF00) >> 8); + dst[1] = (u8)(src & 0x00FF); + return 0; +} + +int u16ToU8_be(u16 src, u8 *dst) +{ + dst[0] = (u8)((src & 0xFF00) >> 8); + dst[1] = (u8)(src & 0x00FF); + return 0; +} + +int u16ToU8_le(u16 src, u8 *dst) +{ + dst[1] = (u8)((src & 0xFF00) >> 8); + dst[0] = (u8)(src & 0x00FF); + return 0; +} + +int u8ToU32(u8 *src, u32 *dst) +{ + *dst = (u32)(((src[3] & 0x000000FF) << 24) + ((src[2] & 0x000000FF) << 16) + ((src[1] & 0x000000FF) << 8) + (src[0] & 0x000000FF)); + return 0; +} + +int u32ToU8(u32 src, u8 *dst) +{ + dst[3] = (u8)((src & 0xFF000000) >> 24); + dst[2] = (u8)((src & 0x00FF0000) >> 16); + dst[1] = (u8)((src & 0x0000FF00) >> 8); + dst[0] = (u8)(src & 0x000000FF); + return 0; +} + +int attempt_function(int(*code)(void), unsigned long wait_before_retry, int retry_count) +{ + int result; + int count = 0; + + do { + result = code(); + count++; + msleep(wait_before_retry); + } while (count < retry_count && result < 0); + + if (count == retry_count) + return (result | ERROR_TIMEOUT); + else + return result; + +} + +void setResetGpio(int gpio) +{ + reset_gpio = gpio; + logError(1, "%s setResetGpio: reset_gpio = %d\n", tag, reset_gpio); +} + +int fts_system_reset(void) +{ + u8 readData[FIFO_EVENT_SIZE]; + int event_to_search; + int res = -1; + int i; + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, SYSTEM_RESET_VALUE }; + event_to_search = (int)EVENTID_CONTROL_READY; + + u16ToU8_be(SYSTEM_RESET_ADDRESS, &cmd[1]); + logError(0, "%s System resetting...\n", tag); + for (i = 0; i < SYSTEM_RESET_RETRY && res < 0; i++) { + + if (reset_gpio == GPIO_NOT_DEFINED) { + res = fts_writeCmd(cmd, 4); + } else { + gpio_set_value(reset_gpio, 0); + msleep(10); + gpio_set_value(reset_gpio, 1); + res = OK; + } + if (res < OK) { + logError(1, "%s fts_system_reset: ERROR %02X\n", tag, ERROR_I2C_W); + } else { + res = pollForEvent(&event_to_search, 1, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s fts_system_reset: ERROR %02X\n", tag, res); + } + } + } + if (res < OK) { + logError(1, "%s fts_system_reset...failed after 3 attempts: ERROR %02X\n", tag, (res | ERROR_SYSTEM_RESET_FAIL)); + return (res | ERROR_SYSTEM_RESET_FAIL); + } + logError(0, "%s System reset DONE!\n", tag); + system_resetted_down = 1; + system_resetted_up = 1; + return OK; + +} + +int isSystemResettedDown(void) +{ + return system_resetted_down; +} + +int isSystemResettedUp(void) +{ + return system_resetted_up; +} + +void setSystemResettedDown(int val) +{ + system_resetted_down = val; +} + +void setSystemResettedUp(int val) +{ + system_resetted_up = val; +} + +int senseOn(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_MT_SENSE_ON }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s senseOn: ERROR %02X\n", tag, ERROR_SENSE_ON_FAIL); + return (ret|ERROR_SENSE_ON_FAIL); + } + + logError(0, "%s senseOn: SENSE ON\n", tag); + return OK; +} + +int senseOff(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_MT_SENSE_OFF }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s senseOff: ERROR %02X\n", tag, ERROR_SENSE_OFF_FAIL); + return (ret | ERROR_SENSE_OFF_FAIL); + } + + logError(0, "%s senseOff: SENSE OFF\n", tag); + return OK; + +} + +int keyOn(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_KEY_ON }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s keyOn: ERROR %02X\n", tag, ERROR_SENSE_ON_FAIL); + return (ret | ERROR_SENSE_ON_FAIL); + } + + logError(0, "%s keyOn: KEY ON\n", tag); + return OK; + +} + +int keyOff(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_KEY_OFF }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s keyOff: ERROR %02X\n", tag, ERROR_SENSE_OFF_FAIL); + return (ret | ERROR_SENSE_OFF_FAIL); + } + + logError(0, "%s keyOff: KEY OFF\n", tag); + return OK; + +} + +int cleanUp(int enableTouch) +{ + int res; + + logError(0, "%s cleanUp: system reset...\n", tag); + res = fts_system_reset(); + if (res < OK) + return res; + if (enableTouch) { + logError(0, "%s cleanUp: enabling touches...\n", tag); + res = senseOn(); + if (res < OK) + return res; +#ifdef PHONE_KEY + res = keyOn(); + if (res < OK) + return res; +#endif + logError(0, "%s cleanUp: enabling interrupts...\n", tag); + res = fts_enableInterrupt(); + if (res < OK) + return res; + } + return OK; + +} + +int checkEcho(u8 *cmd, int size) +{ + int ret, i; + int event_to_search[size+1]; + u8 readData[FIFO_EVENT_SIZE]; + + if ((ftsInfo.u32_echoEn & 0x00000001) != ECHO_ENABLED) { + logError(1, "%s ECHO Not Enabled!\n", tag); + return OK; + } + if (size < 1) { + logError(1, "%s checkEcho: Error Size = %d not valid! or ECHO not Enabled! ERROR %08X\n", tag, size, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + if ((size+2) > FIFO_EVENT_SIZE) + size = FIFO_EVENT_SIZE-2; + /* Echo event EC xx xx xx xx xx xx fifo_status therefore for command + *with more than 6 bytes will echo only the first 6 + */ + event_to_search[0] = EVENTID_ECHO; + for (i = 1; i <= size; i++) { + event_to_search[i] = cmd[i-1]; + } + ret = pollForEvent(event_to_search, size+1, readData, GENERAL_TIMEOUT); + if (ret < OK) { + logError(1, "%s checkEcho: Echo Event not found! ERROR %02X\n", tag, ret); + return (ret | ERROR_CHECK_ECHO_FAIL); + } + + logError(0, "%s ECHO OK!\n", tag); + return OK; +} + +int featureEnableDisable(int on_off, u8 feature) +{ + int ret; + u8 cmd[2] = { 0x00, feature }; + + if (on_off == FEAT_ENABLE) { + cmd[0] = FTS_CMD_FEATURE_ENABLE; + logError(0, "%s featureEnableDisable: Enabling feature %02X ...\n", tag, feature); + } else { + cmd[0] = FTS_CMD_FEATURE_DISABLE; + logError(0, "%s featureEnableDisable: Disabling feature %02X ...\n", tag, feature); + } + + ret = fts_writeCmd(cmd, 2); /* not use writeFwCmd because this function can be called also during interrupt enable and should be fast */ + if (ret < OK) { + logError(1, "%s featureEnableDisable: ERROR %02X\n", tag, ret); + return (ret | ERROR_FEATURE_ENABLE_DISABLE); + } + + logError(0, "%s featureEnableDisable: DONE!\n", tag); + return OK; + +} + +short **array1dTo2d_short(short *data, int size, int columns) +{ + + int i; + short **matrix = (short **)kmalloc(((int)(size / columns))*sizeof(short *), GFP_KERNEL); + if (matrix != NULL) { + for (i = 0; i < (int)(size / columns); i++) { + matrix[i] = (short *)kmalloc(columns*sizeof(short), GFP_KERNEL); + } + + for (i = 0; i < size; i++) + matrix[i / columns][i % columns] = data[i]; + } + + return matrix; +} + +u8 **array1dTo2d_u8(u8 *data, int size, int columns) +{ + + int i; + u8 **matrix = (u8 **)kmalloc(((int)(size / columns))*sizeof(u8 *), GFP_KERNEL); + if (matrix != NULL) { + for (i = 0; i < (int)(size / columns); i++) { + matrix[i] = (u8 *)kmalloc(columns*sizeof(u8), GFP_KERNEL); + } + + for (i = 0; i < size; i++) + matrix[i / columns][i % columns] = data[i]; + } + + return matrix; +} + +void print_frame_short(char *label, short **matrix, int row, int column) +{ + int i, j; + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) { + printk("%d ", matrix[i][j]); + } + logError(0, "\n"); + kfree(matrix[i]); + } +} + +void print_frame_u8(char *label, u8 **matrix, int row, int column) +{ + int i, j; + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) { + printk("%d ", matrix[i][j]); + } + logError(0, "\n"); + kfree(matrix[i]); + } +} + +void print_frame_u32(char *label, u32 **matrix, int row, int column) +{ + int i, j; + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) { + printk("%d ", matrix[i][j]); + } + logError(0, "\n"); + kfree(matrix[i]); + } +} + +void print_frame_int(char *label, int **matrix, int row, int column) +{ + int i, j; + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) { + printk("%d ", matrix[i][j]); + } + logError(0, "\n"); + kfree(matrix[i]); + } +} diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTool.h b/drivers/input/touchscreen/st/fts_lib/ftsTool.h new file mode 100644 index 000000000000..a90e79fc5607 --- /dev/null +++ b/drivers/input/touchscreen/st/fts_lib/ftsTool.h @@ -0,0 +1,64 @@ +/* + +************************************************************************** +** STMicroelectronics ** +************************************************************************** +** marco.cali@st.com ** +************************************************************************** +* * +* FTS Utility Functions * +* * +************************************************************************** +************************************************************************** + +*/ +#define GPIO_NOT_DEFINED -1 + +#define TIMEOUT_RESOLUTION 10 /* ms */ +#define GENERAL_TIMEOUT (50*TIMEOUT_RESOLUTION) /* ms */ +#define RELEASE_INFO_TIMEOUT (15*TIMEOUT_RESOLUTION) /* ms */ + +#define FEAT_ENABLE 1 +#define FEAT_DISABLE 0 + +#define SYSTEM_RESET_RETRY 3 + +#define B2_RETRY 2 + +int readB2(u16 address, u8 *outBuf, int len); +int readB2U16(u16 address, u8 *outBuf, int byteToRead); +int releaseInformation(void); +char *printHex(char *label, u8 *buff, int count); +int pollForEvent(int *event_to_search, int event_bytes, u8 *readData, int time_to_wait); +int fts_disableInterrupt(void); +int fts_enableInterrupt(void); +int u8ToU16(u8 *src, u16 *dst); +int u8ToU16_le(u8 *src, u16 *dst); +int u8ToU16n(u8 *src, int src_length, u16 *dst); +int u16ToU8(u16 src, u8 *dst); +int u16ToU8_le(u16 src, u8 *dst); +int u16ToU8_be(u16 src, u8 *dst); +int u16ToU8n(u16 *src, int src_length, u8 *dst); +int u8ToU32(u8 *src, u32 *dst); +int u32ToU8(u32 src, u8 *dst); +int attempt_function(int(*code)(void), unsigned long wait_before_retry, int retry_count); +void setResetGpio(int gpio); +int fts_system_reset(void); +int isSystemResettedUp(void); +int isSystemResettedDown(void); +void setSystemResettedUp(int val); +void setSystemResettedDown(int val); +int senseOn(void); +int senseOff(void); +int keyOn(void); +int keyOff(void); +int featureEnableDisable(int on_off, u8 feature); +int checkEcho(u8 *cmd, int size); +void print_frame_short(char *label, short **matrix, int row, int column); +short **array1dTo2d_short(short *data, int size, int columns); +u8 **array1dTo2d_u8(u8 *data, int size, int columns); +void print_frame_u8(char *label, u8 **matrix, int row, int column); +void print_frame_u32(char *label, u32 **matrix, int row, int column); +void print_frame_int(char *label, int **matrix, int row, int column); +int cleanUp(int enableTouch); +int flushFIFO(void); diff --git a/drivers/input/touchscreen/st/fts_limits.h b/drivers/input/touchscreen/st/fts_limits.h new file mode 100644 index 000000000000..d3be1a2b1e1a --- /dev/null +++ b/drivers/input/touchscreen/st/fts_limits.h @@ -0,0 +1,10 @@ +#ifndef FTS_LIMITS_H +#define FTS_LIMITS_H +/* This is an auto generated header file +* --->Remember to change the name of the two variables!<--- */ +const uint32_t myArray2_size; + +const uint8_t myArray2[] = { +}; + +#endif diff --git a/drivers/input/touchscreen/synaptics_dsx/Kconfig b/drivers/input/touchscreen/synaptics_dsx/Kconfig new file mode 100644 index 000000000000..18d473969261 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_dsx/Kconfig @@ -0,0 +1,64 @@ +# +# Synaptics DSX touchscreen driver configuration +# +menuconfig TOUCHSCREEN_SYNAPTICS_DSX_v21 + bool "Synaptics DSX touchscreen" + default y + help + Say Y here if you have a Synaptics DSX touchscreen connected + to your system. + + If unsure, say N. + +if TOUCHSCREEN_SYNAPTICS_DSX_v21 + +choice + default TOUCHSCREEN_SYNAPTICS_DSX_I2C_v21 + prompt "Synaptics DSX touchscreen bus interface" + +config TOUCHSCREEN_SYNAPTICS_DSX_I2C_v21 + bool "I2C" + depends on I2C + help + Say Y here if you have a Synaptics DSX touchscreen interfaced + to the host processor over I2C + + If unsure, say N. + + This module uses the services of DSX CORE + +config TOUCHSCREEN_SYNAPTICS_DSX_SPI_v21 + bool "SPI" + depends on SPI_MASTER + help + Say Y here if you have a Synaptics DSX touchscreen interfaced + to the host processor over SPI + + If unsure, say N. + + This module uses the services of DSX CORE +endchoice + +config TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21 + tristate "Synaptics DSX core driver module" + depends on TOUCHSCREEN_SYNAPTICS_DSX_I2C_v21 || TOUCHSCREEN_SYNAPTICS_DSX_SPI_v21 + help + Say Y here to enable basic touch reporting functionalities. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called synaptics_dsx_core. + +config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21 + tristate "Synaptics DSX touchscreen firmware update module" + depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21 + help + Say Y here to enable support for carrying out firmware update. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called synaptics_dsx_fw_update. + +endif diff --git a/drivers/input/touchscreen/synaptics_dsx/Makefile b/drivers/input/touchscreen/synaptics_dsx/Makefile new file mode 100644 index 000000000000..0bffb8da94ea --- /dev/null +++ b/drivers/input/touchscreen/synaptics_dsx/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the Synaptics DSX touchscreen driver. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_I2C_v21) += synaptics_dsx_i2c.o +obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_SPI_v21) += synaptics_dsx_spi.o +obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21) += synaptics_dsx_core.o +obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21) += synaptics_dsx_fw_update.o diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c new file mode 100644 index 000000000000..93c9c3c373b8 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c @@ -0,0 +1,4500 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com> + * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com> + * Copyright (c) 2014-2017, The Linux Foundation. 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/input/synaptics_dsx_v2.h> +#include "synaptics_dsx_core.h" +#ifdef KERNEL_ABOVE_2_6_38 +#include <linux/input/mt.h> +#endif +#if defined(CONFIG_SECURE_TOUCH) +#include <linux/errno.h> +#include <soc/qcom/scm.h> +enum subsystem { + TZ = 1, + APSS = 3 +}; + +#define TZ_BLSP_MODIFY_OWNERSHIP_ID 3 +#endif + +#define INPUT_PHYS_NAME "synaptics_dsx/input0" +#define DEBUGFS_DIR_NAME "ts_debug" + +#ifdef KERNEL_ABOVE_2_6_38 +#define TYPE_B_PROTOCOL +#endif + +#define NO_0D_WHILE_2D +#define REPORT_2D_Z +#define REPORT_2D_W + +#define F12_DATA_15_WORKAROUND + +/* +#define IGNORE_FN_INIT_FAILURE +*/ + +#define RPT_TYPE (1 << 0) +#define RPT_X_LSB (1 << 1) +#define RPT_X_MSB (1 << 2) +#define RPT_Y_LSB (1 << 3) +#define RPT_Y_MSB (1 << 4) +#define RPT_Z (1 << 5) +#define RPT_WX (1 << 6) +#define RPT_WY (1 << 7) +#define RPT_DEFAULT (RPT_TYPE | RPT_X_LSB | RPT_X_MSB | RPT_Y_LSB | RPT_Y_MSB) + +#define EXP_FN_WORK_DELAY_MS 1000 /* ms */ +#define MAX_F11_TOUCH_WIDTH 15 + +#define CHECK_STATUS_TIMEOUT_MS 100 + +#define F01_STD_QUERY_LEN 21 +#define F01_BUID_ID_OFFSET 18 +#define F11_STD_QUERY_LEN 9 +#define F11_STD_CTRL_LEN 10 +#define F11_STD_DATA_LEN 12 + +#define STATUS_NO_ERROR 0x00 +#define STATUS_RESET_OCCURRED 0x01 +#define STATUS_INVALID_CONFIG 0x02 +#define STATUS_DEVICE_FAILURE 0x03 +#define STATUS_CONFIG_CRC_FAILURE 0x04 +#define STATUS_FIRMWARE_CRC_FAILURE 0x05 +#define STATUS_CRC_IN_PROGRESS 0x06 + +#define NORMAL_OPERATION (0 << 0) +#define SENSOR_SLEEP (1 << 0) +#define NO_SLEEP_OFF (0 << 2) +#define NO_SLEEP_ON (1 << 2) +#define CONFIGURED (1 << 7) + +#define SYNA_F11_MAX 4096 +#define SYNA_F12_MAX 65536 + +#define SYNA_S332U_PACKAGE_ID 332 +#define SYNA_S332U_PACKAGE_ID_REV 85 + +static int synaptics_rmi4_f12_set_enables(struct synaptics_rmi4_data *rmi4_data, + unsigned short ctrl28); + +static int synaptics_rmi4_free_fingers(struct synaptics_rmi4_data *rmi4_data); +static int synaptics_rmi4_reinit_device(struct synaptics_rmi4_data *rmi4_data); +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data); + +static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_full_pm_cycle_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +#if defined(CONFIG_FB) +static void fb_notify_resume_work(struct work_struct *work); +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void synaptics_rmi4_early_suspend(struct early_suspend *h); + +static void synaptics_rmi4_late_resume(struct early_suspend *h); +#endif + +static int synaptics_rmi4_suspend(struct device *dev); + +static int synaptics_rmi4_resume(struct device *dev); + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_set_abs_x_axis(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_set_abs_y_axis(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static irqreturn_t synaptics_rmi4_irq(int irq, void *data); + +#if defined(CONFIG_SECURE_TOUCH) +static ssize_t synaptics_secure_touch_enable_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_secure_touch_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_secure_touch_show(struct device *dev, + struct device_attribute *attr, char *buf); +#endif + +struct synaptics_rmi4_f01_device_status { + union { + struct { + unsigned char status_code:4; + unsigned char reserved:2; + unsigned char flash_prog:1; + unsigned char unconfigured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f12_query_5 { + union { + struct { + unsigned char size_of_query6; + struct { + unsigned char ctrl0_is_present:1; + unsigned char ctrl1_is_present:1; + unsigned char ctrl2_is_present:1; + unsigned char ctrl3_is_present:1; + unsigned char ctrl4_is_present:1; + unsigned char ctrl5_is_present:1; + unsigned char ctrl6_is_present:1; + unsigned char ctrl7_is_present:1; + } __packed; + struct { + unsigned char ctrl8_is_present:1; + unsigned char ctrl9_is_present:1; + unsigned char ctrl10_is_present:1; + unsigned char ctrl11_is_present:1; + unsigned char ctrl12_is_present:1; + unsigned char ctrl13_is_present:1; + unsigned char ctrl14_is_present:1; + unsigned char ctrl15_is_present:1; + } __packed; + struct { + unsigned char ctrl16_is_present:1; + unsigned char ctrl17_is_present:1; + unsigned char ctrl18_is_present:1; + unsigned char ctrl19_is_present:1; + unsigned char ctrl20_is_present:1; + unsigned char ctrl21_is_present:1; + unsigned char ctrl22_is_present:1; + unsigned char ctrl23_is_present:1; + } __packed; + struct { + unsigned char ctrl24_is_present:1; + unsigned char ctrl25_is_present:1; + unsigned char ctrl26_is_present:1; + unsigned char ctrl27_is_present:1; + unsigned char ctrl28_is_present:1; + unsigned char ctrl29_is_present:1; + unsigned char ctrl30_is_present:1; + unsigned char ctrl31_is_present:1; + } __packed; + }; + unsigned char data[5]; + }; +}; + +struct synaptics_rmi4_f12_query_8 { + union { + struct { + unsigned char size_of_query9; + struct { + unsigned char data0_is_present:1; + unsigned char data1_is_present:1; + unsigned char data2_is_present:1; + unsigned char data3_is_present:1; + unsigned char data4_is_present:1; + unsigned char data5_is_present:1; + unsigned char data6_is_present:1; + unsigned char data7_is_present:1; + } __packed; + struct { + unsigned char data8_is_present:1; + unsigned char data9_is_present:1; + unsigned char data10_is_present:1; + unsigned char data11_is_present:1; + unsigned char data12_is_present:1; + unsigned char data13_is_present:1; + unsigned char data14_is_present:1; + unsigned char data15_is_present:1; + } __packed; + }; + unsigned char data[3]; + }; +}; + +struct synaptics_rmi4_f12_ctrl_8 { + union { + struct { + unsigned char max_x_coord_lsb; + unsigned char max_x_coord_msb; + unsigned char max_y_coord_lsb; + unsigned char max_y_coord_msb; + unsigned char rx_pitch_lsb; + unsigned char rx_pitch_msb; + unsigned char tx_pitch_lsb; + unsigned char tx_pitch_msb; + unsigned char low_rx_clip; + unsigned char high_rx_clip; + unsigned char low_tx_clip; + unsigned char high_tx_clip; + unsigned char num_of_rx; + unsigned char num_of_tx; + }; + unsigned char data[14]; + }; +}; + +struct synaptics_rmi4_f12_ctrl_23 { + union { + struct { + unsigned char obj_type_enable; + unsigned char max_reported_objects; + }; + unsigned char data[2]; + }; +}; + +struct synaptics_rmi4_f12_finger_data { + unsigned char object_type_and_status; + unsigned char x_lsb; + unsigned char x_msb; + unsigned char y_lsb; + unsigned char y_msb; +#ifdef REPORT_2D_Z + unsigned char z; +#endif +#ifdef REPORT_2D_W + unsigned char wx; + unsigned char wy; +#endif +}; + +struct synaptics_rmi4_f1a_query { + union { + struct { + unsigned char max_button_count:3; + unsigned char reserved:5; + unsigned char has_general_control:1; + unsigned char has_interrupt_enable:1; + unsigned char has_multibutton_select:1; + unsigned char has_tx_rx_map:1; + unsigned char has_perbutton_threshold:1; + unsigned char has_release_threshold:1; + unsigned char has_strongestbtn_hysteresis:1; + unsigned char has_filter_strength:1; + } __packed; + unsigned char data[2]; + }; +}; + +struct synaptics_rmi4_f1a_control_0 { + union { + struct { + unsigned char multibutton_report:2; + unsigned char filter_mode:2; + unsigned char reserved:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f1a_control { + struct synaptics_rmi4_f1a_control_0 general_control; + unsigned char button_int_enable; + unsigned char multi_button; + unsigned char *txrx_map; + unsigned char *button_threshold; + unsigned char button_release_threshold; + unsigned char strongest_button_hysteresis; + unsigned char filter_strength; +}; + +struct synaptics_rmi4_f1a_handle { + int button_bitmask_size; + unsigned char max_count; + unsigned char valid_button_count; + unsigned char *button_data_buffer; + unsigned char *button_map; + struct synaptics_rmi4_f1a_query button_query; + struct synaptics_rmi4_f1a_control button_control; +}; + +struct synaptics_rmi4_exp_fhandler { + struct synaptics_rmi4_exp_fn *exp_fn; + bool insert; + bool remove; + struct list_head link; +}; + +struct synaptics_rmi4_exp_fn_data { + bool initialized; + bool queue_work; + struct mutex mutex; + struct list_head list; + struct delayed_work work; + struct workqueue_struct *workqueue; + struct synaptics_rmi4_data *rmi4_data; +}; + +static struct synaptics_rmi4_exp_fn_data exp_data; + +static struct device_attribute attrs[] = { + __ATTR(full_pm_cycle, (S_IRUGO | S_IWUSR | S_IWGRP), + synaptics_rmi4_full_pm_cycle_show, + synaptics_rmi4_full_pm_cycle_store), + __ATTR(reset, (S_IWUSR | S_IWGRP), + NULL, + synaptics_rmi4_f01_reset_store), + __ATTR(set_abs_x_axis, (S_IWUSR | S_IWGRP), + NULL, + synaptics_rmi4_set_abs_x_axis), + __ATTR(set_abs_y_axis, (S_IWUSR | S_IWGRP), + NULL, + synaptics_rmi4_set_abs_y_axis), + __ATTR(productinfo, S_IRUGO, + synaptics_rmi4_f01_productinfo_show, + synaptics_rmi4_store_error), + __ATTR(buildid, S_IRUGO, + synaptics_rmi4_f01_buildid_show, + synaptics_rmi4_store_error), + __ATTR(flashprog, S_IRUGO, + synaptics_rmi4_f01_flashprog_show, + synaptics_rmi4_store_error), + __ATTR(0dbutton, (S_IRUGO | S_IWUSR | S_IWGRP), + synaptics_rmi4_0dbutton_show, + synaptics_rmi4_0dbutton_store), +#if defined(CONFIG_SECURE_TOUCH) + __ATTR(secure_touch_enable, (S_IRUGO | S_IWUSR | S_IWGRP), + synaptics_secure_touch_enable_show, + synaptics_secure_touch_enable_store), + __ATTR(secure_touch, S_IRUGO , + synaptics_secure_touch_show, + NULL), +#endif +}; + +#define MAX_BUF_SIZE 256 +#define VKEY_VER_CODE "0x01" + +#define HEIGHT_SCALE_NUM 8 +#define HEIGHT_SCALE_DENOM 10 + +/* numerator and denomenator for border equations */ +#define BORDER_ADJUST_NUM 3 +#define BORDER_ADJUST_DENOM 4 + +static struct kobject *vkey_kobj; +static char *vkey_buf; + +static ssize_t vkey_show(struct kobject *obj, + struct kobj_attribute *attr, char *buf) +{ + strlcpy(buf, vkey_buf, MAX_BUF_SIZE); + return strnlen(buf, MAX_BUF_SIZE); +} + +static struct kobj_attribute vkey_obj_attr = { + .attr = { + .mode = S_IRUGO, + .name = "virtualkeys."PLATFORM_DRIVER_NAME, + }, + .show = vkey_show, +}; + +static struct attribute *vkey_attr[] = { + &vkey_obj_attr.attr, + NULL, +}; + +static struct attribute_group vkey_grp = { + .attrs = vkey_attr, +}; + +static int synaptics_rmi4_debug_suspend_set(void *_data, u64 val) +{ + struct synaptics_rmi4_data *rmi4_data = _data; + + if (val) + synaptics_rmi4_suspend(&rmi4_data->input_dev->dev); + else + synaptics_rmi4_resume(&rmi4_data->input_dev->dev); + + return 0; +} + +static int synaptics_rmi4_debug_suspend_get(void *_data, u64 *val) +{ + struct synaptics_rmi4_data *rmi4_data = _data; + + *val = rmi4_data->suspended; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, synaptics_rmi4_debug_suspend_get, + synaptics_rmi4_debug_suspend_set, "%lld\n"); + +#if defined(CONFIG_SECURE_TOUCH) +static int synaptics_i2c_change_pipe_owner( + struct synaptics_rmi4_data *rmi4_data, enum subsystem subsystem) +{ + /*scm call descriptor */ + struct scm_desc desc; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + int ret = 0; + + /* number of arguments */ + desc.arginfo = SCM_ARGS(2); + /* BLSPID (1-12) */ + desc.args[0] = i2c->adapter->nr - 1; + /* Owner if TZ or APSS */ + desc.args[1] = subsystem; + ret = scm_call2(SCM_SIP_FNID(SCM_SVC_TZ, TZ_BLSP_MODIFY_OWNERSHIP_ID), + &desc); + if (ret) + return ret; + + return desc.ret[0]; +} + +static void synaptics_secure_touch_init(struct synaptics_rmi4_data *data) +{ + data->st_initialized = 0; + init_completion(&data->st_powerdown); + init_completion(&data->st_irq_processed); + /* Get clocks */ + data->core_clk = clk_get(data->pdev->dev.parent, "core_clk"); + if (IS_ERR(data->core_clk)) { + data->core_clk = NULL; + dev_warn(data->pdev->dev.parent, + "%s: core_clk is not defined\n", __func__); + } + + data->iface_clk = clk_get(data->pdev->dev.parent, "iface_clk"); + if (IS_ERR(data->iface_clk)) { + data->iface_clk = NULL; + dev_warn(data->pdev->dev.parent, + "%s: iface_clk is not defined\n", __func__); + } + + data->st_initialized = 1; +} +static void synaptics_secure_touch_notify(struct synaptics_rmi4_data *rmi4_data) +{ + sysfs_notify(&rmi4_data->input_dev->dev.kobj, NULL, "secure_touch"); +} +static irqreturn_t synaptics_filter_interrupt( + struct synaptics_rmi4_data *rmi4_data) +{ + if (atomic_read(&rmi4_data->st_enabled)) { + if (atomic_cmpxchg(&rmi4_data->st_pending_irqs, 0, 1) == 0) { + reinit_completion(&rmi4_data->st_irq_processed); + synaptics_secure_touch_notify(rmi4_data); + wait_for_completion_interruptible( + &rmi4_data->st_irq_processed); + } + return IRQ_HANDLED; + } + return IRQ_NONE; +} +static void synaptics_secure_touch_stop( + struct synaptics_rmi4_data *rmi4_data, + int blocking) +{ + if (atomic_read(&rmi4_data->st_enabled)) { + atomic_set(&rmi4_data->st_pending_irqs, -1); + synaptics_secure_touch_notify(rmi4_data); + if (blocking) + wait_for_completion_interruptible( + &rmi4_data->st_powerdown); + } +} +#else +static void synaptics_secure_touch_init(struct synaptics_rmi4_data *rmi4_data) +{ +} +static irqreturn_t synaptics_filter_interrupt( + struct synaptics_rmi4_data *rmi4_data) +{ + return IRQ_NONE; +} +static void synaptics_secure_touch_stop( + struct synaptics_rmi4_data *rmi4_data, + int blocking) +{ +} +#endif + +#if defined(CONFIG_SECURE_TOUCH) +static ssize_t synaptics_secure_touch_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + return scnprintf( + buf, + PAGE_SIZE, + "%d", + atomic_read(&rmi4_data->st_enabled)); +} +/* + * Accept only "0" and "1" valid values. + * "0" will reset the st_enabled flag, then wake up the reading process and + * the interrupt handler. + * The bus driver is notified via pm_runtime that it is not required to stay + * awake anymore. + * It will also make sure the queue of events is emptied in the controller, + * in case a touch happened in between the secure touch being disabled and + * the local ISR being ungated. + * "1" will set the st_enabled flag and clear the st_pending_irqs flag. + * The bus driver is requested via pm_runtime to stay awake. + */ +static ssize_t synaptics_secure_touch_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + unsigned long value; + int err = 0; + + if (count > 2) + return -EINVAL; + + err = kstrtoul(buf, 10, &value); + if (err != 0) + return err; + + if (!rmi4_data->st_initialized) + return -EIO; + + err = count; + + switch (value) { + case 0: + if (atomic_read(&rmi4_data->st_enabled) == 0) + break; + + synaptics_i2c_change_pipe_owner(rmi4_data, APSS); + synaptics_rmi4_bus_put(rmi4_data); + atomic_set(&rmi4_data->st_enabled, 0); + synaptics_secure_touch_notify(rmi4_data); + complete(&rmi4_data->st_irq_processed); + synaptics_rmi4_irq(rmi4_data->irq, rmi4_data); + complete(&rmi4_data->st_powerdown); + + break; + case 1: + if (atomic_read(&rmi4_data->st_enabled)) { + err = -EBUSY; + break; + } + + synchronize_irq(rmi4_data->irq); + + if (synaptics_rmi4_bus_get(rmi4_data) < 0) { + dev_err( + rmi4_data->pdev->dev.parent, + "synaptics_rmi4_bus_get failed\n"); + err = -EIO; + break; + } + synaptics_i2c_change_pipe_owner(rmi4_data, TZ); + reinit_completion(&rmi4_data->st_powerdown); + reinit_completion(&rmi4_data->st_irq_processed); + atomic_set(&rmi4_data->st_enabled, 1); + atomic_set(&rmi4_data->st_pending_irqs, 0); + break; + default: + dev_err( + rmi4_data->pdev->dev.parent, + "unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + return err; +} + +/* + * This function returns whether there are pending interrupts, or + * other error conditions that need to be signaled to the userspace library, + * according tot he following logic: + * - st_enabled is 0 if secure touch is not enabled, returning -EBADF + * - st_pending_irqs is -1 to signal that secure touch is in being stopped, + * returning -EINVAL + * - st_pending_irqs is 1 to signal that there is a pending irq, returning + * the value "1" to the sysfs read operation + * - st_pending_irqs is 0 (only remaining case left) if the pending interrupt + * has been processed, so the interrupt handler can be allowed to continue. + */ +static ssize_t synaptics_secure_touch_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + int val = 0; + if (atomic_read(&rmi4_data->st_enabled) == 0) + return -EBADF; + + if (atomic_cmpxchg(&rmi4_data->st_pending_irqs, -1, 0) == -1) + return -EINVAL; + + if (atomic_cmpxchg(&rmi4_data->st_pending_irqs, 1, 0) == 1) + val = 1; + else + complete(&rmi4_data->st_irq_processed); + + return scnprintf(buf, PAGE_SIZE, "%u", val); + +} +#endif + +static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + rmi4_data->full_pm_cycle); +} + +static ssize_t synaptics_rmi4_full_pm_cycle_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + rmi4_data->full_pm_cycle = input > 0 ? 1 : 0; + + return count; +} + +static ssize_t synaptics_rmi4_set_abs_x_axis(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input == 0) + return -EINVAL; + + input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_X, + 0, input, 0, 0); + + return count; +} + +static ssize_t synaptics_rmi4_set_abs_y_axis(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input == 0) + return -EINVAL; + + input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_Y, + 0, input, 0, 0); + + return count; +} + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int reset; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + if (sscanf(buf, "%u", &reset) != 1) + return -EINVAL; + + if (reset != 1) + return -EINVAL; + + retval = synaptics_rmi4_reset_device(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue reset command, error = %d\n", + __func__, retval); + return retval; + } + + return count; +} + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n", + (rmi4_data->rmi4_mod_info.product_info[0]), + (rmi4_data->rmi4_mod_info.product_info[1])); +} + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + rmi4_data->firmware_id); +} + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct synaptics_rmi4_f01_device_status device_status; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr, + device_status.data, + sizeof(device_status.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read device status, error = %d\n", + __func__, retval); + return retval; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + device_status.flash_prog); +} + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + rmi4_data->button_0d_enabled); +} + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + unsigned char ii; + unsigned char intr_enable; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + input = input > 0 ? 1 : 0; + + if (rmi4_data->button_0d_enabled == input) + return count; + + if (list_empty(&rmi->support_fn_list)) + return -ENODEV; + + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) { + ii = fhandler->intr_reg_num; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + + if (input == 1) + intr_enable |= fhandler->intr_mask; + else + intr_enable &= ~fhandler->intr_mask; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + } + } + + rmi4_data->button_0d_enabled = input; + + return count; +} + + /** + * synaptics_rmi4_f11_abs_report() + * + * Called by synaptics_rmi4_report_touch() when valid Function $11 + * finger data has been detected. + * + * This function reads the Function $11 data registers, determines the + * status of each finger supported by the Function, processes any + * necessary coordinate manipulation, reports the finger data to + * the input subsystem, and returns the number of fingers detected. + */ +static int synaptics_rmi4_f11_abs_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; /* number of touch points */ + unsigned char reg_index; + unsigned char finger; + unsigned char fingers_supported; + unsigned char num_of_finger_status_regs; + unsigned char finger_shift; + unsigned char finger_status; + unsigned char data_reg_blk_size; + unsigned char finger_status_reg[3]; + unsigned char data[F11_STD_DATA_LEN]; + unsigned short data_addr; + unsigned short data_offset; + int x; + int y; + int wx; + int wy; + int temp; + + /* + * The number of finger status registers is determined by the + * maximum number of fingers supported - 2 bits per finger. So + * the number of finger status registers to read is: + * register_count = ceil(max_num_of_fingers / 4) + */ + fingers_supported = fhandler->num_of_data_points; + num_of_finger_status_regs = (fingers_supported + 3) / 4; + data_addr = fhandler->full_addr.data_base; + data_reg_blk_size = fhandler->size_of_data_register_block; + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr, + finger_status_reg, + num_of_finger_status_regs); + if (retval < 0) + return 0; + + for (finger = 0; finger < fingers_supported; finger++) { + reg_index = finger / 4; + finger_shift = (finger % 4) * 2; + finger_status = (finger_status_reg[reg_index] >> finger_shift) + & MASK_2BIT; + + /* + * Each 2-bit finger status field represents the following: + * 00 = finger not present + * 01 = finger present and data accurate + * 10 = finger present but data may be inaccurate + * 11 = reserved + */ +#ifdef TYPE_B_PROTOCOL + input_mt_slot(rmi4_data->input_dev, finger); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, finger_status); +#endif + + if (finger_status) { + data_offset = data_addr + + num_of_finger_status_regs + + (finger * data_reg_blk_size); + retval = synaptics_rmi4_reg_read(rmi4_data, + data_offset, + data, + data_reg_blk_size); + if (retval < 0) + return 0; + + x = (data[0] << 4) | (data[2] & MASK_4BIT); + y = (data[1] << 4) | ((data[2] >> 4) & MASK_4BIT); + wx = (data[3] & MASK_4BIT); + wy = (data[3] >> 4) & MASK_4BIT; + + if (rmi4_data->hw_if->board_data->swap_axes) { + temp = x; + x = y; + y = temp; + temp = wx; + wx = wy; + wy = temp; + } + + if (rmi4_data->hw_if->board_data->x_flip) + x = rmi4_data->sensor_max_x - x; + if (rmi4_data->hw_if->board_data->y_flip) + y = rmi4_data->sensor_max_y - y; + + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 1); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 1); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_Y, y); +#ifdef REPORT_2D_W + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, max(wx, wy)); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, min(wx, wy)); +#endif +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Finger %d:\n" + "status = 0x%02x\n" + "x = %d\n" + "y = %d\n" + "wx = %d\n" + "wy = %d\n", + __func__, finger, + finger_status, + x, y, wx, wy); + + touch_count++; + } + } + + if (touch_count == 0) { + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 0); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + } + + input_sync(rmi4_data->input_dev); + + return touch_count; +} + + /** + * synaptics_rmi4_f12_abs_report() + * + * Called by synaptics_rmi4_report_touch() when valid Function $12 + * finger data has been detected. + * + * This function reads the Function $12 data registers, determines the + * status of each finger supported by the Function, processes any + * necessary coordinate manipulation, reports the finger data to + * the input subsystem, and returns the number of fingers detected. + */ +static int synaptics_rmi4_f12_abs_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; /* number of touch points */ + unsigned char finger; + unsigned char fingers_to_process; + unsigned char finger_status; + unsigned char size_of_2d_data; + unsigned short data_addr; + int x; + int y; + int wx; + int wy; + int temp; + struct synaptics_rmi4_f12_extra_data *extra_data; + struct synaptics_rmi4_f12_finger_data *data; + struct synaptics_rmi4_f12_finger_data *finger_data; +#ifdef F12_DATA_15_WORKAROUND + static unsigned char fingers_already_present; +#endif + + fingers_to_process = fhandler->num_of_data_points; + data_addr = fhandler->full_addr.data_base; + extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra; + size_of_2d_data = sizeof(struct synaptics_rmi4_f12_finger_data); + + + /* Determine the total number of fingers to process */ + if (extra_data->data15_size) { + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr + extra_data->data15_offset, + extra_data->data15_data, + extra_data->data15_size); + if (retval < 0) + return 0; + + /* Start checking from the highest bit */ + temp = extra_data->data15_size - 1; /* Highest byte */ + if ((temp >= 0) && (fingers_to_process > 0) && + (temp < ((F12_FINGERS_TO_SUPPORT + 7) / 8))) { + finger = (fingers_to_process - 1) % 8; /* Highest bit */ + do { + if (extra_data->data15_data[temp] + & (1 << finger)) + break; + + if (finger) { + finger--; + } else { + /* Move to the next lower byte */ + temp--; + finger = 7; + } + + fingers_to_process--; + } while (fingers_to_process && (temp >= 0)); + } + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Number of fingers to process = %d\n", + __func__, fingers_to_process); + } + +#ifdef F12_DATA_15_WORKAROUND + fingers_to_process = max(fingers_to_process, fingers_already_present); +#endif + + if (!fingers_to_process) { + synaptics_rmi4_free_fingers(rmi4_data); + return 0; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr + extra_data->data1_offset, + (unsigned char *)fhandler->data, + fingers_to_process * size_of_2d_data); + if (retval < 0) + return 0; + + data = (struct synaptics_rmi4_f12_finger_data *)fhandler->data; + + for (finger = 0; finger < fingers_to_process; finger++) { + finger_data = data + finger; + finger_status = finger_data->object_type_and_status; + + if (finger_status == F12_FINGER_STATUS) { +#ifdef TYPE_B_PROTOCOL + input_mt_slot(rmi4_data->input_dev, finger); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, 1); +#endif + +#ifdef F12_DATA_15_WORKAROUND + fingers_already_present = finger + 1; +#endif + + x = (finger_data->x_msb << 8) | (finger_data->x_lsb); + y = (finger_data->y_msb << 8) | (finger_data->y_lsb); +#ifdef REPORT_2D_W + wx = finger_data->wx; + wy = finger_data->wy; +#endif + + if (rmi4_data->hw_if->board_data->swap_axes) { + temp = x; + x = y; + y = temp; + temp = wx; + wx = wy; + wy = temp; + } + + if (rmi4_data->hw_if->board_data->x_flip) + x = rmi4_data->sensor_max_x - x; + if (rmi4_data->hw_if->board_data->y_flip) + y = rmi4_data->sensor_max_y - y; + + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 1); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 1); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_Y, y); +#ifdef REPORT_2D_W + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, max(wx, wy)); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, min(wx, wy)); +#endif +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Finger %d:\n" + "status = 0x%02x\n" + "x = %d\n" + "y = %d\n" + "wx = %d\n" + "wy = %d\n", + __func__, finger, + finger_status, + x, y, wx, wy); + + touch_count++; + } else { +#ifdef TYPE_B_PROTOCOL + input_mt_slot(rmi4_data->input_dev, finger); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, 0); +#endif + } + } + + if (touch_count == 0) { + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 0); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + } + + input_sync(rmi4_data->input_dev); + + return touch_count; +} + +static void synaptics_rmi4_f1a_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; + unsigned char button; + unsigned char index; + unsigned char shift; + unsigned char status; + unsigned char *data; + unsigned short data_addr = fhandler->full_addr.data_base; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + static unsigned char do_once = 1; + static bool current_status[MAX_NUMBER_OF_BUTTONS]; +#ifdef NO_0D_WHILE_2D + static bool before_2d_status[MAX_NUMBER_OF_BUTTONS]; + static bool while_2d_status[MAX_NUMBER_OF_BUTTONS]; +#endif + + if (do_once) { + memset(current_status, 0, sizeof(current_status)); +#ifdef NO_0D_WHILE_2D + memset(before_2d_status, 0, sizeof(before_2d_status)); + memset(while_2d_status, 0, sizeof(while_2d_status)); +#endif + do_once = 0; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr, + f1a->button_data_buffer, + f1a->button_bitmask_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read button data registers\n", + __func__); + return; + } + + data = f1a->button_data_buffer; + + for (button = 0; button < f1a->valid_button_count; button++) { + index = button / 8; + shift = button % 8; + status = ((data[index] >> shift) & MASK_1BIT); + + if (current_status[button] == status) + continue; + else + current_status[button] = status; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Button %d (code %d) ->%d\n", + __func__, button, + f1a->button_map[button], + status); +#ifdef NO_0D_WHILE_2D + if (rmi4_data->fingers_on_2d == false) { + if (status == 1) { + before_2d_status[button] = 1; + } else { + if (while_2d_status[button] == 1) { + while_2d_status[button] = 0; + continue; + } else { + before_2d_status[button] = 0; + } + } + touch_count++; + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + } else { + if (before_2d_status[button] == 1) { + before_2d_status[button] = 0; + touch_count++; + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + } else { + if (status == 1) + while_2d_status[button] = 1; + else + while_2d_status[button] = 0; + } + } +#else + touch_count++; + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); +#endif + } + + if (touch_count) + input_sync(rmi4_data->input_dev); + + return; +} + + /** + * synaptics_rmi4_report_touch() + * + * Called by synaptics_rmi4_sensor_report(). + * + * This function calls the appropriate finger data reporting function + * based on the function handler it receives and returns the number of + * fingers detected. + */ +static void synaptics_rmi4_report_touch(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + unsigned char touch_count_2d; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Function %02x reporting\n", + __func__, fhandler->fn_number); + + switch (fhandler->fn_number) { + case SYNAPTICS_RMI4_F11: + touch_count_2d = synaptics_rmi4_f11_abs_report(rmi4_data, + fhandler); + + if (touch_count_2d) + rmi4_data->fingers_on_2d = true; + else + rmi4_data->fingers_on_2d = false; + break; + case SYNAPTICS_RMI4_F12: + touch_count_2d = synaptics_rmi4_f12_abs_report(rmi4_data, + fhandler); + + if (touch_count_2d) + rmi4_data->fingers_on_2d = true; + else + rmi4_data->fingers_on_2d = false; + break; + case SYNAPTICS_RMI4_F1A: + synaptics_rmi4_f1a_report(rmi4_data, fhandler); + break; + default: + break; + } + + return; +} + + /** + * synaptics_rmi4_sensor_report() + * + * Called by synaptics_rmi4_irq(). + * + * This function determines the interrupt source(s) from the sensor + * and calls synaptics_rmi4_report_touch() with the appropriate + * function handler for each function with valid data inputs. + */ +static void synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char data[MAX_INTR_REGISTERS + 1]; + unsigned char *intr = &data[1]; + struct synaptics_rmi4_f01_device_status status; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + /* + * Get interrupt status information from F01 Data1 register to + * determine the source(s) that are flagging the interrupt. + */ + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr, + data, + rmi4_data->num_of_intr_regs + 1); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read interrupt status\n", + __func__); + return; + } + + status.data[0] = data[0]; + if (status.unconfigured && !status.flash_prog) { + pr_notice("%s: spontaneous reset detected\n", __func__); + retval = synaptics_rmi4_reinit_device(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to reinit device\n", + __func__); + } + return; + } + + /* + * Traverse the function handler list and service the source(s) + * of the interrupt accordingly. + */ + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + if (fhandler->intr_mask & + intr[fhandler->intr_reg_num]) { + synaptics_rmi4_report_touch(rmi4_data, + fhandler); + } + } + } + } + + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) { + if (!exp_fhandler->insert && + !exp_fhandler->remove && + (exp_fhandler->exp_fn->attn != NULL)) + exp_fhandler->exp_fn->attn(rmi4_data, intr[0]); + } + } + mutex_unlock(&exp_data.mutex); + + return; +} + + /** + * synaptics_rmi4_irq() + * + * Called by the kernel when an interrupt occurs (when the sensor + * asserts the attention irq). + * + * This function is the ISR thread and handles the acquisition + * and the reporting of finger data when the presence of fingers + * is detected. + */ +static irqreturn_t synaptics_rmi4_irq(int irq, void *data) +{ + struct synaptics_rmi4_data *rmi4_data = data; + + if (IRQ_HANDLED == synaptics_filter_interrupt(data)) + return IRQ_HANDLED; + + synaptics_rmi4_sensor_report(rmi4_data); + + return IRQ_HANDLED; +} + + /** + * synaptics_rmi4_irq_enable() + * + * Called by synaptics_rmi4_probe() and the power management functions + * in this driver and also exported to other expansion Function modules + * such as rmi_dev. + * + * This function handles the enabling and disabling of the attention + * irq including the setting up of the ISR thread. + */ +static int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval = 0; + unsigned char intr_status[MAX_INTR_REGISTERS]; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + if (enable) { + if (rmi4_data->irq_enabled) + return retval; + + /* Clear interrupts first */ + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + intr_status, + rmi4_data->num_of_intr_regs); + if (retval < 0) + return retval; + + retval = request_threaded_irq(rmi4_data->irq, NULL, + synaptics_rmi4_irq, bdata->irq_flags, + PLATFORM_DRIVER_NAME, rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create irq thread\n", + __func__); + return retval; + } + + rmi4_data->irq_enabled = true; + } else { + if (rmi4_data->irq_enabled) { + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, rmi4_data); + rmi4_data->irq_enabled = false; + } + } + + return retval; +} + +static int synaptics_rmi4_set_intr_mask(struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + unsigned char ii; + unsigned char intr_offset; + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num >= MAX_INTR_REGISTERS) { + fhandler->intr_reg_num = 0; + fhandler->num_of_data_sources = 0; + fhandler->intr_mask = 0; + return -EINVAL; + } + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + + return 0; +} + +static int synaptics_rmi4_f01_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = fd->intr_src_count; + fhandler->data = NULL; + fhandler->extra = NULL; + + retval = synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + if (retval < 0) + return retval; + + + rmi4_data->f01_query_base_addr = fd->query_base_addr; + rmi4_data->f01_ctrl_base_addr = fd->ctrl_base_addr; + rmi4_data->f01_data_base_addr = fd->data_base_addr; + rmi4_data->f01_cmd_base_addr = fd->cmd_base_addr; + + return 0; +} + + /** + * synaptics_rmi4_f11_set_coords() + * + * Set panel resolution for f11 to match display resolution. + * + */ +static int synaptics_rmi4_f11_set_coords(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char control[F11_STD_CTRL_LEN]; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + if (!rmi4_data->update_coords) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: No need to update panel resolution\n", __func__); + return 0; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base, + control, + sizeof(control)); + if (retval < 0) + return retval; + + /* Maximum x and y */ + rmi4_data->sensor_max_x = ((control[6] & MASK_8BIT) << 0) | + ((control[7] & MASK_4BIT) << 8); + rmi4_data->sensor_max_y = ((control[8] & MASK_8BIT) << 0) | + ((control[9] & MASK_4BIT) << 8); + + if (bdata->panel_maxx && bdata->panel_maxy && + (rmi4_data->sensor_max_x != bdata->panel_maxx || + rmi4_data->sensor_max_y != bdata->panel_maxy)) { + if (bdata->panel_maxx > SYNA_F11_MAX || + bdata->panel_maxy > SYNA_F11_MAX) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid panel resolution\n", __func__); + return -EINVAL; + } + + rmi4_data->sensor_max_x = bdata->panel_maxx; + rmi4_data->sensor_max_y = bdata->panel_maxy; + control[6] = rmi4_data->sensor_max_x & MASK_8BIT; + control[7] = (rmi4_data->sensor_max_x >> 8) & MASK_4BIT; + control[8] = rmi4_data->sensor_max_y & MASK_8BIT; + control[9] = (rmi4_data->sensor_max_y >> 8) & MASK_4BIT; + + retval = synaptics_rmi4_reg_write(rmi4_data, + fhandler->full_addr.ctrl_base, + control, + sizeof(control)); + if (retval < 0) + return retval; + rmi4_data->update_coords = true; + } else { + rmi4_data->update_coords = false; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Function %02x max x = %d max y = %d\n", + __func__, fhandler->fn_number, + rmi4_data->sensor_max_x, + rmi4_data->sensor_max_y); + + rmi4_data->max_touch_width = MAX_F11_TOUCH_WIDTH; + + return 0; +} + + /** + * synaptics_rmi4_f11_init() + * + * Called by synaptics_rmi4_query_device(). + * + * This funtion parses information from the Function 11 registers + * and determines the number of fingers supported, x and y data ranges, + * offset to the associated interrupt status register, interrupt bit + * mask, and gathers finger data acquisition capabilities from the query + * registers. + */ +static int synaptics_rmi4_f11_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char abs_data_size; + unsigned char abs_data_blk_size; + unsigned char query[F11_STD_QUERY_LEN]; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = fd->intr_src_count; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base, + query, + sizeof(query)); + if (retval < 0) + return retval; + + /* Maximum number of fingers supported */ + if ((query[1] & MASK_3BIT) <= 4) + fhandler->num_of_data_points = (query[1] & MASK_3BIT) + 1; + else if ((query[1] & MASK_3BIT) == 5) + fhandler->num_of_data_points = 10; + + rmi4_data->num_of_fingers = fhandler->num_of_data_points; + + retval = synaptics_rmi4_f11_set_coords(rmi4_data, fhandler); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + if (retval < 0) + return retval; + + abs_data_size = query[5] & MASK_2BIT; + abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0)); + fhandler->size_of_data_register_block = abs_data_blk_size; + fhandler->data = NULL; + fhandler->extra = NULL; + + return retval; +} + +static int synaptics_rmi4_f12_set_enables(struct synaptics_rmi4_data *rmi4_data, + unsigned short ctrl28) +{ + int retval; + static unsigned short ctrl_28_address; + + if (ctrl28) + ctrl_28_address = ctrl28; + + retval = synaptics_rmi4_reg_write(rmi4_data, + ctrl_28_address, + &rmi4_data->report_enable, + sizeof(rmi4_data->report_enable)); + if (retval < 0) + return retval; + + return retval; +} + + /** + * synaptics_rmi4_f12_set_coords() + * + * Set panel resolution for f12 to match display resolution. + * + */ +static int synaptics_rmi4_f12_set_coords(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + struct synaptics_rmi4_f12_query_5 query_5; + struct synaptics_rmi4_f12_ctrl_8 ctrl_8; + unsigned char ctrl_8_offset; + int retval; + + if (!rmi4_data->update_coords) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: No need to update panel resolution\n", __func__); + return 0; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + 5, + query_5.data, + sizeof(query_5.data)); + if (retval < 0) + return retval; + + ctrl_8_offset = query_5.ctrl0_is_present + + query_5.ctrl1_is_present + + query_5.ctrl2_is_present + + query_5.ctrl3_is_present + + query_5.ctrl4_is_present + + query_5.ctrl5_is_present + + query_5.ctrl6_is_present + + query_5.ctrl7_is_present; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + ctrl_8_offset, + ctrl_8.data, + sizeof(ctrl_8.data)); + if (retval < 0) + return retval; + + /* Maximum x and y */ + rmi4_data->sensor_max_x = + ((unsigned short)ctrl_8.max_x_coord_lsb << 0) | + ((unsigned short)ctrl_8.max_x_coord_msb << 8); + rmi4_data->sensor_max_y = + ((unsigned short)ctrl_8.max_y_coord_lsb << 0) | + ((unsigned short)ctrl_8.max_y_coord_msb << 8); + + if (bdata->panel_maxx && bdata->panel_maxy && + (rmi4_data->sensor_max_x != bdata->panel_maxx || + rmi4_data->sensor_max_y != bdata->panel_maxy)) { + + if (bdata->panel_maxx > SYNA_F12_MAX || + bdata->panel_maxy > SYNA_F12_MAX) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid panel resolution\n", __func__); + retval = -EINVAL; + return retval; + } + + rmi4_data->sensor_max_x = bdata->panel_maxx; + rmi4_data->sensor_max_y = bdata->panel_maxy; + ctrl_8.max_x_coord_lsb = rmi4_data->sensor_max_x & MASK_8BIT; + ctrl_8.max_x_coord_msb = (rmi4_data->sensor_max_x >> 8) & + MASK_4BIT; + ctrl_8.max_y_coord_lsb = rmi4_data->sensor_max_y & MASK_8BIT; + ctrl_8.max_y_coord_msb = (rmi4_data->sensor_max_y >> 8) & + MASK_4BIT; + + retval = synaptics_rmi4_reg_write(rmi4_data, + fhandler->full_addr.ctrl_base + ctrl_8_offset, + ctrl_8.data, + sizeof(ctrl_8.data)); + if (retval < 0) + return retval; + rmi4_data->update_coords = true; + } else { + rmi4_data->update_coords = false; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Function %02x max x = %d max y = %d\n", + __func__, fhandler->fn_number, + rmi4_data->sensor_max_x, + rmi4_data->sensor_max_y); + + rmi4_data->num_of_rx = ctrl_8.num_of_rx; + rmi4_data->num_of_tx = ctrl_8.num_of_tx; + rmi4_data->max_touch_width = max(rmi4_data->num_of_rx, + rmi4_data->num_of_tx); + + return 0; +} + + /** + * synaptics_rmi4_f12_init() + * + * Called by synaptics_rmi4_query_device(). + * + * This funtion parses information from the Function 12 registers and + * determines the number of fingers supported, offset to the data1 + * register, x and y data ranges, offset to the associated interrupt + * status register, interrupt bit mask, and allocates memory resources + * for finger data acquisition. + */ +static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char size_of_2d_data; + unsigned char size_of_query8; + unsigned char ctrl_8_offset; + unsigned char ctrl_23_offset; + unsigned char ctrl_28_offset; + unsigned char num_of_fingers; + struct synaptics_rmi4_f12_extra_data *extra_data; + struct synaptics_rmi4_f12_query_5 query_5; + struct synaptics_rmi4_f12_query_8 query_8; + struct synaptics_rmi4_f12_ctrl_23 ctrl_23; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = fd->intr_src_count; + size_of_2d_data = sizeof(struct synaptics_rmi4_f12_finger_data); + + fhandler->extra = kmalloc(sizeof(*extra_data), GFP_KERNEL); + if (!fhandler->extra) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for function handler\n", + __func__); + return -ENOMEM; + } + extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + 5, + query_5.data, + sizeof(query_5.data)); + if (retval < 0) + goto free_function_handler_mem; + + ctrl_8_offset = query_5.ctrl0_is_present + + query_5.ctrl1_is_present + + query_5.ctrl2_is_present + + query_5.ctrl3_is_present + + query_5.ctrl4_is_present + + query_5.ctrl5_is_present + + query_5.ctrl6_is_present + + query_5.ctrl7_is_present; + + ctrl_23_offset = ctrl_8_offset + + query_5.ctrl8_is_present + + query_5.ctrl9_is_present + + query_5.ctrl10_is_present + + query_5.ctrl11_is_present + + query_5.ctrl12_is_present + + query_5.ctrl13_is_present + + query_5.ctrl14_is_present + + query_5.ctrl15_is_present + + query_5.ctrl16_is_present + + query_5.ctrl17_is_present + + query_5.ctrl18_is_present + + query_5.ctrl19_is_present + + query_5.ctrl20_is_present + + query_5.ctrl21_is_present + + query_5.ctrl22_is_present; + + ctrl_28_offset = ctrl_23_offset + + query_5.ctrl23_is_present + + query_5.ctrl24_is_present + + query_5.ctrl25_is_present + + query_5.ctrl26_is_present + + query_5.ctrl27_is_present; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + ctrl_23_offset, + ctrl_23.data, + sizeof(ctrl_23.data)); + if (retval < 0) + goto free_function_handler_mem; + + /* Maximum number of fingers supported */ + fhandler->num_of_data_points = min(ctrl_23.max_reported_objects, + (unsigned char)F12_FINGERS_TO_SUPPORT); + + num_of_fingers = fhandler->num_of_data_points; + rmi4_data->num_of_fingers = num_of_fingers; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + 7, + &size_of_query8, + sizeof(size_of_query8)); + if (retval < 0) + goto free_function_handler_mem; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + 8, + query_8.data, + size_of_query8); + if (retval < 0) + goto free_function_handler_mem; + + /* Determine the presence of the Data0 register */ + extra_data->data1_offset = query_8.data0_is_present; + + if ((size_of_query8 >= 3) && (query_8.data15_is_present)) { + extra_data->data15_offset = query_8.data0_is_present + + query_8.data1_is_present + + query_8.data2_is_present + + query_8.data3_is_present + + query_8.data4_is_present + + query_8.data5_is_present + + query_8.data6_is_present + + query_8.data7_is_present + + query_8.data8_is_present + + query_8.data9_is_present + + query_8.data10_is_present + + query_8.data11_is_present + + query_8.data12_is_present + + query_8.data13_is_present + + query_8.data14_is_present; + extra_data->data15_size = (num_of_fingers + 7) / 8; + } else { + extra_data->data15_size = 0; + } + + rmi4_data->report_enable = RPT_DEFAULT; +#ifdef REPORT_2D_Z + rmi4_data->report_enable |= RPT_Z; +#endif +#ifdef REPORT_2D_W + rmi4_data->report_enable |= (RPT_WX | RPT_WY); +#endif + + retval = synaptics_rmi4_f12_set_enables(rmi4_data, + fhandler->full_addr.ctrl_base + ctrl_28_offset); + if (retval < 0) + goto free_function_handler_mem; + + + retval = synaptics_rmi4_f12_set_coords(rmi4_data, fhandler); + if (retval < 0) + goto free_function_handler_mem; + + retval = synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + if (retval < 0) + return retval; + + /* Allocate memory for finger data storage space */ + fhandler->data_size = num_of_fingers * size_of_2d_data; + fhandler->data = kmalloc(fhandler->data_size, GFP_KERNEL); + if (!fhandler->data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for function handler data\n", + __func__); + retval = -ENOMEM; + goto free_function_handler_mem; + } + + return retval; + +free_function_handler_mem: + kfree(fhandler->extra); + return retval; +} + +static int synaptics_rmi4_f1a_alloc_mem(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + struct synaptics_rmi4_f1a_handle *f1a; + + f1a = kzalloc(sizeof(*f1a), GFP_KERNEL); + if (!f1a) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for function handle\n", + __func__); + return -ENOMEM; + } + + fhandler->data = (void *)f1a; + fhandler->extra = NULL; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base, + f1a->button_query.data, + sizeof(f1a->button_query.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read query registers\n", + __func__); + return retval; + } + + f1a->max_count = f1a->button_query.max_button_count + 1; + + f1a->button_control.txrx_map = kzalloc(f1a->max_count * 2, GFP_KERNEL); + if (!f1a->button_control.txrx_map) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for tx rx mapping\n", + __func__); + return -ENOMEM; + } + + f1a->button_bitmask_size = (f1a->max_count + 7) / 8; + + f1a->button_data_buffer = kcalloc(f1a->button_bitmask_size, + sizeof(*(f1a->button_data_buffer)), GFP_KERNEL); + if (!f1a->button_data_buffer) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for data buffer\n", + __func__); + return -ENOMEM; + } + + f1a->button_map = kcalloc(f1a->max_count, + sizeof(*(f1a->button_map)), GFP_KERNEL); + if (!f1a->button_map) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for button map\n", + __func__); + return -ENOMEM; + } + + return 0; +} + +static int synaptics_rmi4_f1a_button_map(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char ii; + unsigned char mapping_offset = 0; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + mapping_offset = f1a->button_query.has_general_control + + f1a->button_query.has_interrupt_enable + + f1a->button_query.has_multibutton_select; + + if (f1a->button_query.has_tx_rx_map) { + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + mapping_offset, + f1a->button_control.txrx_map, + sizeof(f1a->button_control.txrx_map)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read tx rx mapping\n", + __func__); + return retval; + } + + rmi4_data->button_txrx_mapping = f1a->button_control.txrx_map; + } + + if (!bdata->cap_button_map) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: cap_button_map is NULL in board file\n", + __func__); + } else if (!bdata->cap_button_map->map) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Button map is missing in board file\n", + __func__); + } else { + if (bdata->cap_button_map->nbuttons != f1a->max_count) { + f1a->valid_button_count = min(f1a->max_count, + bdata->cap_button_map->nbuttons); + } else { + f1a->valid_button_count = f1a->max_count; + } + + for (ii = 0; ii < f1a->valid_button_count; ii++) + f1a->button_map[ii] = bdata->cap_button_map->map[ii]; + } + + return 0; +} + +static void synaptics_rmi4_f1a_kfree(struct synaptics_rmi4_fn *fhandler) +{ + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + + if (f1a) { + kfree(f1a->button_control.txrx_map); + kfree(f1a->button_data_buffer); + kfree(f1a->button_map); + kfree(f1a); + fhandler->data = NULL; + } + + return; +} + +static int synaptics_rmi4_f1a_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = fd->intr_src_count; + + retval = synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_f1a_alloc_mem(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + retval = synaptics_rmi4_f1a_button_map(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + rmi4_data->button_0d_enabled = 1; + + return 0; + +error_exit: + synaptics_rmi4_f1a_kfree(fhandler); + + return retval; +} + +static void synaptics_rmi4_empty_fn_list(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_fn *fhandler_temp; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry_safe(fhandler, + fhandler_temp, + &rmi->support_fn_list, + link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) { + synaptics_rmi4_f1a_kfree(fhandler); + } else { + kfree(fhandler->extra); + kfree(fhandler->data); + } + list_del(&fhandler->link); + kfree(fhandler); + } + } + INIT_LIST_HEAD(&rmi->support_fn_list); + + return; +} + +static int synaptics_rmi4_check_status(struct synaptics_rmi4_data *rmi4_data, + bool *was_in_bl_mode) +{ + int retval; + int timeout = CHECK_STATUS_TIMEOUT_MS; + unsigned char command = 0x01; + unsigned char intr_status; + struct synaptics_rmi4_f01_device_status status; + + /* Do a device reset first */ + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_cmd_base_addr, + &command, + sizeof(command)); + if (retval < 0) + return retval; + + msleep(rmi4_data->hw_if->board_data->reset_delay_ms); + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + if (retval < 0) + return retval; + + while (status.status_code == STATUS_CRC_IN_PROGRESS) { + if (timeout > 0) + msleep(20); + else + return -1; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + if (retval < 0) + return retval; + + timeout -= 20; + } + + if (timeout != CHECK_STATUS_TIMEOUT_MS) + *was_in_bl_mode = true; + + if (status.flash_prog == 1) { + rmi4_data->flash_prog_mode = true; + pr_notice("%s: In flash prog mode, status = 0x%02x\n", + __func__, + status.status_code); + } else { + rmi4_data->flash_prog_mode = false; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + &intr_status, + sizeof(intr_status)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read interrupt status\n", + __func__); + return retval; + } + + return 0; +} + +static void synaptics_rmi4_set_configured(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char device_ctrl; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set configured\n", + __func__); + return; + } + + rmi4_data->no_sleep_setting = device_ctrl & NO_SLEEP_ON; + device_ctrl |= CONFIGURED; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set configured\n", + __func__); + } + + return; +} + +static int synaptics_rmi4_alloc_fh(struct synaptics_rmi4_fn **fhandler, + struct synaptics_rmi4_fn_desc *rmi_fd, int page_number) +{ + *fhandler = kmalloc(sizeof(**fhandler), GFP_KERNEL); + if (!(*fhandler)) + return -ENOMEM; + + (*fhandler)->full_addr.data_base = + (rmi_fd->data_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.ctrl_base = + (rmi_fd->ctrl_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.cmd_base = + (rmi_fd->cmd_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.query_base = + (rmi_fd->query_base_addr | + (page_number << 8)); + + return 0; +} + +static int synaptics_rmi4_read_configid(struct synaptics_rmi4_data *rmi4_data, + unsigned char ctrl_base_addr) +{ + unsigned int device_config_id; + + /* + * We may get an error while trying to read config id if it is + * not provisioned by vendor + */ + if (synaptics_rmi4_reg_read(rmi4_data, ctrl_base_addr, + (unsigned char *)(&device_config_id), + sizeof(device_config_id)) < 0) + dev_err(rmi4_data->pdev->dev.parent, "Failed to read device config ID from CTP\n"); + + if (rmi4_data->hw_if->board_data->config_id) + dev_info(rmi4_data->pdev->dev.parent, + "CTP Config ID=%pI4\tDT Config ID=%pI4\n", + &device_config_id, + &rmi4_data->hw_if->board_data->config_id); + else + dev_info(rmi4_data->pdev->dev.parent, + "CTP Config ID=%pI4\n", &device_config_id); + + return 0; +} + + /** + * synaptics_rmi4_query_device() + * + * Called by synaptics_rmi4_probe(). + * + * This funtion scans the page description table, records the offsets + * to the register types of Function $01, sets up the function handlers + * for Function $11 and Function $12, determines the number of interrupt + * sources from the sensor, adds valid Functions with data inputs to the + * Function linked list, parses information from the query registers of + * Function $01, and enables the interrupt sources from the valid Functions + * with data inputs. + */ +static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char ii; + unsigned char page_number; + unsigned char intr_count; + unsigned char f01_query[F01_STD_QUERY_LEN]; + unsigned short pdt_entry_addr; + unsigned short intr_addr; + bool f01found; + bool was_in_bl_mode; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + unsigned char pkg_id[PACKAGE_ID_SIZE]; + rmi = &(rmi4_data->rmi4_mod_info); + +rescan_pdt: + f01found = false; + was_in_bl_mode = false; + intr_count = 0; + INIT_LIST_HEAD(&rmi->support_fn_list); + + /* Scan the page description tables of the pages to service */ + for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) { + for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END; + pdt_entry_addr -= PDT_ENTRY_SIZE) { + pdt_entry_addr |= (page_number << 8); + + retval = synaptics_rmi4_reg_read(rmi4_data, + pdt_entry_addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + pdt_entry_addr &= ~(MASK_8BIT << 8); + + fhandler = NULL; + + if (rmi_fd.fn_number == 0) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Reached end of PDT\n", + __func__); + break; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: F%02x found (page %d)\n", + __func__, rmi_fd.fn_number, + page_number); + + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F34: + /* + * Though function F34 is an interrupt source, + * but it is not a data source, hence do not + * add its handler to support_fn_list + */ + synaptics_rmi4_read_configid(rmi4_data, + rmi_fd.ctrl_base_addr); + break; + case SYNAPTICS_RMI4_F01: + if (rmi_fd.intr_src_count == 0) + break; + + f01found = true; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f01_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_check_status(rmi4_data, + &was_in_bl_mode); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to check status\n", + __func__); + return retval; + } + + if (was_in_bl_mode) { + kfree(fhandler); + fhandler = NULL; + goto rescan_pdt; + } + + if (rmi4_data->flash_prog_mode) + goto flash_prog_mode; + + break; + case SYNAPTICS_RMI4_F11: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f11_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) + return retval; + break; + case SYNAPTICS_RMI4_F12: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f12_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) + return retval; + break; + case SYNAPTICS_RMI4_F1A: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f1a_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) { +#ifdef IGNORE_FN_INIT_FAILURE + kfree(fhandler); + fhandler = NULL; +#else + return retval; +#endif + } + break; + } + + /* Accumulate the interrupt count */ + intr_count += (rmi_fd.intr_src_count & MASK_3BIT); + + if (fhandler && rmi_fd.intr_src_count) { + list_add_tail(&fhandler->link, + &rmi->support_fn_list); + } + } + } + + if (!f01found) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F01\n", + __func__); + return -EINVAL; + } + +flash_prog_mode: + rmi4_data->num_of_intr_regs = (intr_count + 7) / 8; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Number of interrupt registers = %d\n", + __func__, rmi4_data->num_of_intr_regs); + if (rmi4_data->num_of_intr_regs >= MAX_INTR_REGISTERS) + return -EINVAL; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_query_base_addr, + f01_query, + sizeof(f01_query)); + if (retval < 0) + return retval; + + /* RMI Version 4.0 currently supported */ + rmi->version_major = 4; + rmi->version_minor = 0; + + rmi->manufacturer_id = f01_query[0]; + rmi->product_props = f01_query[1]; + rmi->product_info[0] = f01_query[2] & MASK_7BIT; + rmi->product_info[1] = f01_query[3] & MASK_7BIT; + rmi->date_code[0] = f01_query[4] & MASK_5BIT; + rmi->date_code[1] = f01_query[5] & MASK_4BIT; + rmi->date_code[2] = f01_query[6] & MASK_5BIT; + rmi->tester_id = ((f01_query[7] & MASK_7BIT) << 8) | + (f01_query[8] & MASK_7BIT); + rmi->serial_number = ((f01_query[9] & MASK_7BIT) << 8) | + (f01_query[10] & MASK_7BIT); + memcpy(rmi->product_id_string, &f01_query[11], 10); + + if (rmi->manufacturer_id != 1) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Non-Synaptics device found, manufacturer ID = %d\n", + __func__, rmi->manufacturer_id); + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_query_base_addr + F01_PACKAGE_ID_OFFSET, + pkg_id, sizeof(pkg_id)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read device package id (code %d)\n", + __func__, retval); + return retval; + } + + rmi->package_id = (pkg_id[1] << 8) | pkg_id[0]; + rmi->package_id_rev = (pkg_id[3] << 8) | pkg_id[2]; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_query_base_addr + F01_BUID_ID_OFFSET, + rmi->build_id, + sizeof(rmi->build_id)); + if (retval < 0) + return retval; + + rmi4_data->firmware_id = (unsigned int)rmi->build_id[0] + + (unsigned int)rmi->build_id[1] * 0x100 + + (unsigned int)rmi->build_id[2] * 0x10000; + + memset(rmi4_data->intr_mask, 0x00, sizeof(rmi4_data->intr_mask)); + + /* + * Map out the interrupt bit masks for the interrupt sources + * from the registered function handlers. + */ + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + rmi4_data->intr_mask[fhandler->intr_reg_num] |= + fhandler->intr_mask; + } + } + } + + /* Enable the interrupt sources */ + for (ii = 0; ii < rmi4_data->num_of_intr_regs; ii++) { + if (rmi4_data->intr_mask[ii] != 0x00) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Interrupt enable mask %d = 0x%02x\n", + __func__, ii, rmi4_data->intr_mask[ii]); + intr_addr = rmi4_data->f01_ctrl_base_addr + 1 + ii; + retval = synaptics_rmi4_reg_write(rmi4_data, + intr_addr, + &(rmi4_data->intr_mask[ii]), + sizeof(rmi4_data->intr_mask[ii])); + if (retval < 0) + return retval; + } + } + + synaptics_rmi4_set_configured(rmi4_data); + + return 0; +} + +static void synaptics_rmi4_set_params(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char ii; + struct synaptics_rmi4_f1a_handle *f1a; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (bdata->disp_maxx && bdata->disp_maxy) { + input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_X, + 0, bdata->disp_maxx, 0, 0); + input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_Y, + 0, bdata->disp_maxy, 0, 0); + } else { + input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_X, + 0, rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_Y, + 0, rmi4_data->sensor_max_y, 0, 0); + } + +#ifdef REPORT_2D_W + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, 0, + rmi4_data->max_touch_width, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, 0, + rmi4_data->max_touch_width, 0, 0); +#endif + +#ifdef TYPE_B_PROTOCOL + input_mt_init_slots(rmi4_data->input_dev, + rmi4_data->num_of_fingers, 0); +#endif + + f1a = NULL; + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) + f1a = fhandler->data; + } + } + + if (f1a) { + for (ii = 0; ii < f1a->valid_button_count; ii++) { + set_bit(f1a->button_map[ii], + rmi4_data->input_dev->keybit); + input_set_capability(rmi4_data->input_dev, + EV_KEY, f1a->button_map[ii]); + } + } + + return; +} + +static int synaptics_dsx_virtual_keys_init(struct device *dev, + struct synaptics_dsx_board_data *rmi4_pdata) +{ + int width, height, center_x, center_y; + int x1 = 0, x2 = 0, i, c = 0, rc = 0, border; + + vkey_buf = devm_kzalloc(dev, MAX_BUF_SIZE, GFP_KERNEL); + if (!vkey_buf) { + dev_err(dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + border = (rmi4_pdata->panel_maxx - rmi4_pdata->disp_maxx) * 2; + width = ((rmi4_pdata->disp_maxx - + (border * (rmi4_pdata->virtual_key_map->nkeys - 1))) + / rmi4_pdata->virtual_key_map->nkeys); + height = (rmi4_pdata->panel_maxy - rmi4_pdata->disp_maxy); + center_y = rmi4_pdata->disp_maxy + (height / 2); + height = height * HEIGHT_SCALE_NUM / HEIGHT_SCALE_DENOM; + + x2 -= border * BORDER_ADJUST_NUM / BORDER_ADJUST_DENOM; + + for (i = 0; i < rmi4_pdata->virtual_key_map->nkeys; i++) { + x1 = x2 + border; + x2 = x2 + border + width; + center_x = x1 + (x2 - x1) / 2; + c += snprintf(vkey_buf + c, MAX_BUF_SIZE - c, + "%s:%d:%d:%d:%d:%d\n", VKEY_VER_CODE, + rmi4_pdata->virtual_key_map->map[i], + center_x, center_y, width, height); + } + + vkey_buf[c] = '\0'; + + vkey_kobj = kobject_create_and_add("board_properties", NULL); + if (!vkey_kobj) { + dev_err(dev, "unable to create kobject\n"); + return -ENOMEM; + } + + rc = sysfs_create_group(vkey_kobj, &vkey_grp); + if (rc) { + dev_err(dev, "failed to create attributes\n"); + kobject_put(vkey_kobj); + } + + return rc; +} + +static int synaptics_dsx_get_virtual_keys(struct device *dev, + struct property *prop, char *name, + struct synaptics_dsx_board_data *rmi4_pdata, + struct device_node *np) +{ + u32 num_keys; + int rc; + + num_keys = prop->length / sizeof(u32); + + rmi4_pdata->virtual_key_map = devm_kzalloc(dev, + sizeof(*rmi4_pdata->virtual_key_map), + GFP_KERNEL); + if (!rmi4_pdata->virtual_key_map) + return -ENOMEM; + + rmi4_pdata->virtual_key_map->map = devm_kzalloc(dev, + sizeof(*rmi4_pdata->virtual_key_map->map) * + num_keys, GFP_KERNEL); + if (!rmi4_pdata->virtual_key_map->map) + return -ENOMEM; + + rc = of_property_read_u32_array(np, name, + rmi4_pdata->virtual_key_map->map, + num_keys); + if (rc) { + dev_err(dev, "Failed to read key codes\n"); + return -EINVAL; + } + rmi4_pdata->virtual_key_map->nkeys = num_keys; + + return 0; +} + +static int synaptics_dsx_get_button_map(struct device *dev, + struct property *prop, char *name, + struct synaptics_dsx_board_data *rmi4_pdata, + struct device_node *np) +{ + int rc, i; + u32 num_buttons; + u32 button_map[MAX_NUMBER_OF_BUTTONS]; + + num_buttons = prop->length / sizeof(u32); + + rmi4_pdata->cap_button_map = devm_kzalloc(dev, + sizeof(*rmi4_pdata->cap_button_map), + GFP_KERNEL); + if (!rmi4_pdata->cap_button_map) + return -ENOMEM; + + rmi4_pdata->cap_button_map->map = devm_kzalloc(dev, + sizeof(*rmi4_pdata->cap_button_map->map) * + num_buttons, GFP_KERNEL); + if (!rmi4_pdata->cap_button_map->map) + return -ENOMEM; + + if (num_buttons <= MAX_NUMBER_OF_BUTTONS) { + rc = of_property_read_u32_array(np, + name, button_map, num_buttons); + if (rc) { + dev_err(dev, "Unable to read key codes\n"); + return rc; + } + for (i = 0; i < num_buttons; i++) + rmi4_pdata->cap_button_map->map[i] = + button_map[i]; + rmi4_pdata->cap_button_map->nbuttons = num_buttons; + } else { + return -EINVAL; + } + + return 0; +} + +static int synaptics_rmi4_parse_dt_children(struct device *dev, + struct synaptics_dsx_board_data *rmi4_pdata, + struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_device_info *rmi = &rmi4_data->rmi4_mod_info; + struct device_node *node = dev->of_node, *child = NULL; + int rc = 0; + struct synaptics_rmi4_fn *fhandler = NULL; + struct property *prop; + + for_each_child_of_node(node, child) { + rc = of_property_read_u32(child, "synaptics,package-id", + &rmi4_pdata->package_id); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read package_id\n"); + return rc; + } else if (rc == -EINVAL) { + rmi4_pdata->package_id = 0x00; + } + + if (rmi4_pdata->package_id) { + if (rmi4_pdata->package_id != rmi->package_id) { + dev_err(dev, + "%s: Synaptics package id don't match %d %d\n", + __func__, + rmi4_pdata->package_id, rmi->package_id); + /* + * Iterate over next child if package + * id does not match + */ + continue; + } else if (of_property_read_bool(child, + "synaptics,bypass-sensor-coords-check") && + of_find_property(child, + "synaptics,panel-coords", NULL)) { + /* + * Some unprogrammed panels from touch vendor + * and wrongly programmed panels from factory + * may return incorrect sensor coordinate range + * when their query registers are read, but + * they normally work fine in field. In such + * a scenario, driver can bypass the comparison + * of coordinate range read from sensor and read + * from DT and continue normal operation. + */ + synaptics_dsx_get_dt_coords(dev, + "synaptics,panel-coords", + rmi4_pdata, child); + dev_info(dev, + "%s Synaptics package id matches %d %d," + "but bypassing the comparison of sensor" + "coordinates.\n", __func__, + rmi4_pdata->package_id, + rmi->package_id); + dev_info(dev, "Pmax_x Pmax_y = %d:%d\n", + rmi4_pdata->panel_maxx, + rmi4_pdata->panel_maxy); + dev_info(dev, "Smax_x Smax_y = %d:%d\n", + rmi4_data->sensor_max_x, + rmi4_data->sensor_max_y); + } else { + /* + * If package id read from DT matches the + * package id value read from touch controller, + * also check if sensor dimensions read from DT + * match those read from controller, before + * moving further. For this first check if touch + * panel coordinates are defined in DT or not. + */ + if (of_find_property(child, + "synaptics,panel-coords", NULL)) { + synaptics_dsx_get_dt_coords(dev, + "synaptics,panel-coords", + rmi4_pdata, child); + dev_info(dev, "Pmax_x Pmax_y = %d:%d\n", + rmi4_pdata->panel_maxx, + rmi4_pdata->panel_maxy); + dev_info(dev, "Smax_x Smax_y = %d:%d\n", + rmi4_data->sensor_max_x, + rmi4_data->sensor_max_y); + if ((rmi4_pdata->panel_maxx != + rmi4_data->sensor_max_x) || + (rmi4_pdata->panel_maxy != + rmi4_data->sensor_max_y)) + continue; + } else { + dev_info(dev, "Smax_x Smax_y = %d:%d\n", + rmi4_data->sensor_max_x, + rmi4_data->sensor_max_y); + } + } + } + + rc = synaptics_dsx_get_dt_coords(dev, + "synaptics,display-coords", rmi4_pdata, child); + if (rc && (rc != -EINVAL)) + return rc; + + prop = of_find_property(child, "synaptics,button-map", NULL); + if (prop) { + rc = synaptics_dsx_get_button_map(dev, prop, + "synaptics,button-map", rmi4_pdata, child); + if (rc < 0) { + dev_err(dev, "Unable to read button map\n"); + return rc; + } + + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, + &rmi->support_fn_list, link) { + if (fhandler->fn_number == + SYNAPTICS_RMI4_F1A) + break; + } + } + + if (fhandler && fhandler->fn_number == + SYNAPTICS_RMI4_F1A) { + rc = synaptics_rmi4_f1a_button_map(rmi4_data, + fhandler); + if (rc < 0) { + dev_err(dev, + "Fail to register F1A %d\n", + rc); + return rc; + } + } + } + + prop = of_find_property(child, "synaptics,key-codes", NULL); + if (prop) { + rc = synaptics_dsx_get_virtual_keys(dev, prop, + "synaptics,key-codes", rmi4_pdata, child); + if (!rc) { + rc = synaptics_dsx_virtual_keys_init(dev, + rmi4_pdata); + if (!rc) + rmi4_data->support_vkeys = true; + + } else { + dev_err(dev, + "Unable to read virtual key codes\n"); + return rc; + } + } + + break; + } + + return 0; +} + +static int synaptics_rmi4_set_input_dev(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + int temp; + + rmi4_data->input_dev = input_allocate_device(); + if (rmi4_data->input_dev == NULL) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate input device\n", + __func__); + retval = -ENOMEM; + goto err_input_device; + } + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to query device\n", + __func__); + goto err_query_device; + } + + if (rmi4_data->hw_if->board_data->detect_device) { + retval = synaptics_rmi4_parse_dt_children( + rmi4_data->pdev->dev.parent, + rmi4_data->hw_if->board_data, + rmi4_data); + if (retval < 0) + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to parse device tree property\n", + __func__); + } + + rmi4_data->input_dev->name = PLATFORM_DRIVER_NAME; + rmi4_data->input_dev->phys = INPUT_PHYS_NAME; + rmi4_data->input_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT; + rmi4_data->input_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION; + rmi4_data->input_dev->dev.parent = rmi4_data->pdev->dev.parent; + input_set_drvdata(rmi4_data->input_dev, rmi4_data); + + set_bit(EV_SYN, rmi4_data->input_dev->evbit); + set_bit(EV_KEY, rmi4_data->input_dev->evbit); + set_bit(EV_ABS, rmi4_data->input_dev->evbit); + set_bit(BTN_TOUCH, rmi4_data->input_dev->keybit); + set_bit(BTN_TOOL_FINGER, rmi4_data->input_dev->keybit); +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit); +#endif + + if (rmi4_data->hw_if->board_data->swap_axes) { + temp = rmi4_data->sensor_max_x; + rmi4_data->sensor_max_x = rmi4_data->sensor_max_y; + rmi4_data->sensor_max_y = temp; + } + + synaptics_rmi4_set_params(rmi4_data); + + retval = input_register_device(rmi4_data->input_dev); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to register input device\n", + __func__); + goto err_register_input; + } + + return 0; + +err_register_input: + if (rmi4_data->support_vkeys) { + sysfs_remove_group(vkey_kobj, &vkey_grp); + kobject_put(vkey_kobj); + } +err_query_device: + synaptics_rmi4_empty_fn_list(rmi4_data); + input_free_device(rmi4_data->input_dev); + +err_input_device: + return retval; +} + +static int synaptics_rmi4_set_gpio(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + int power_on; + int reset_on; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + power_on = bdata->power_on_state; + reset_on = bdata->reset_on_state; + + retval = bdata->gpio_config( + bdata->irq_gpio, + true, 0, 0); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to configure attention GPIO\n", + __func__); + goto err_gpio_irq; + } + + if (bdata->power_gpio >= 0) { + retval = bdata->gpio_config( + bdata->power_gpio, + true, 1, !power_on); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to configure power GPIO\n", + __func__); + goto err_gpio_power; + } + } + + if (bdata->reset_gpio >= 0) { + retval = bdata->gpio_config( + bdata->reset_gpio, + true, 1, !reset_on); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to configure reset GPIO\n", + __func__); + goto err_gpio_reset; + } + } + + if (bdata->power_gpio >= 0) { + gpio_set_value(bdata->power_gpio, power_on); + msleep(bdata->power_delay_ms); + } + + if (bdata->reset_gpio >= 0) { + gpio_set_value(bdata->reset_gpio, reset_on); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->reset_gpio, !reset_on); + msleep(bdata->reset_delay_ms); + } + + return 0; + +err_gpio_reset: + if (bdata->power_gpio >= 0) { + bdata->gpio_config( + bdata->power_gpio, + false, 0, 0); + } + +err_gpio_power: + bdata->gpio_config( + bdata->irq_gpio, + false, 0, 0); + +err_gpio_irq: + return retval; +} + +static int synaptics_dsx_pinctrl_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + rmi4_data->ts_pinctrl = devm_pinctrl_get((rmi4_data->pdev->dev.parent)); + if (IS_ERR_OR_NULL(rmi4_data->ts_pinctrl)) { + retval = PTR_ERR(rmi4_data->ts_pinctrl); + dev_dbg(rmi4_data->pdev->dev.parent, + "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + rmi4_data->pinctrl_state_active + = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_active"); + if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_active)) { + retval = PTR_ERR(rmi4_data->pinctrl_state_active); + dev_err(rmi4_data->pdev->dev.parent, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + rmi4_data->pinctrl_state_suspend + = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_suspend"); + if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_suspend)) { + retval = PTR_ERR(rmi4_data->pinctrl_state_suspend); + dev_dbg(rmi4_data->pdev->dev.parent, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + rmi4_data->pinctrl_state_release + = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_release"); + if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) { + retval = PTR_ERR(rmi4_data->pinctrl_state_release); + dev_dbg(rmi4_data->pdev->dev.parent, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(rmi4_data->ts_pinctrl); +err_pinctrl_get: + rmi4_data->ts_pinctrl = NULL; + return retval; +} + +static int synaptics_dsx_gpio_configure(struct synaptics_rmi4_data *rmi4_data, + bool on) +{ + int retval = 0; + struct synaptics_rmi4_device_info *rmi; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + rmi = &(rmi4_data->rmi4_mod_info); + + if (on) { + if (gpio_is_valid(bdata->irq_gpio)) { + /* configure touchscreen irq gpio */ + retval = gpio_request(bdata->irq_gpio, + "rmi4_irq_gpio"); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "unable to request gpio [%d]\n", + bdata->irq_gpio); + goto err_irq_gpio_req; + } + retval = gpio_direction_input(bdata->irq_gpio); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "unable to set dir for gpio[%d]\n", + bdata->irq_gpio); + goto err_irq_gpio_dir; + } + } else { + dev_err(rmi4_data->pdev->dev.parent, + "irq gpio not provided\n"); + goto err_irq_gpio_req; + } + + if (gpio_is_valid(bdata->reset_gpio)) { + /* configure touchscreen reset out gpio */ + retval = gpio_request(bdata->reset_gpio, + "rmi4_reset_gpio"); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "unable to request gpio [%d]\n", + bdata->reset_gpio); + goto err_irq_gpio_dir; + } + + retval = gpio_direction_output(bdata->reset_gpio, 1); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "unable to set dir for gpio [%d]\n", + bdata->reset_gpio); + goto err_reset_gpio_dir; + } + + gpio_set_value(bdata->reset_gpio, 1); + msleep(bdata->reset_delay_ms); + } + + return 0; + } else { + if (bdata->disable_gpios) { + if (gpio_is_valid(bdata->irq_gpio)) + gpio_free(bdata->irq_gpio); + if (gpio_is_valid(bdata->reset_gpio)) { + /* + * This is intended to save leakage current + * only. Even if the call(gpio_direction_input) + * fails, only leakage current will be more but + * functionality will not be affected. + */ + if (rmi->package_id == + SYNA_S332U_PACKAGE_ID && + rmi->package_id_rev == + SYNA_S332U_PACKAGE_ID_REV) { + gpio_set_value(bdata-> + reset_gpio, + 0); + } else { + retval = gpio_direction_input( + bdata->reset_gpio); + if (retval) { + dev_err(rmi4_data->pdev-> + dev.parent, + "unable to set direction for gpio [%d]\n", + bdata->irq_gpio); + } + } + gpio_free(bdata->reset_gpio); + } + } + + return 0; + } + +err_reset_gpio_dir: + if (gpio_is_valid(bdata->reset_gpio)) + gpio_free(bdata->reset_gpio); +err_irq_gpio_dir: + if (gpio_is_valid(bdata->irq_gpio)) + gpio_free(bdata->irq_gpio); +err_irq_gpio_req: + return retval; +} + +static int synaptics_rmi4_free_fingers(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char ii; + +#ifdef TYPE_B_PROTOCOL + for (ii = 0; ii < rmi4_data->num_of_fingers; ii++) { + input_mt_slot(rmi4_data->input_dev, ii); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, 0); + } +#endif + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 0); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + input_sync(rmi4_data->input_dev); + + rmi4_data->fingers_on_2d = false; + + return 0; +} + +static int synaptics_rmi4_reinit_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char ii; + unsigned short intr_addr; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + mutex_lock(&(rmi4_data->rmi4_reset_mutex)); + + synaptics_rmi4_free_fingers(rmi4_data); + + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F12) { + synaptics_rmi4_f12_set_coords(rmi4_data, + fhandler); + synaptics_rmi4_f12_set_enables(rmi4_data, 0); + break; + } else if (fhandler->fn_number == SYNAPTICS_RMI4_F11) { + synaptics_rmi4_f11_set_coords(rmi4_data, + fhandler); + break; + } + + } + } + + for (ii = 0; ii < rmi4_data->num_of_intr_regs; ii++) { + if (rmi4_data->intr_mask[ii] != 0x00) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Interrupt enable mask %d = 0x%02x\n", + __func__, ii, rmi4_data->intr_mask[ii]); + intr_addr = rmi4_data->f01_ctrl_base_addr + 1 + ii; + retval = synaptics_rmi4_reg_write(rmi4_data, + intr_addr, + &(rmi4_data->intr_mask[ii]), + sizeof(rmi4_data->intr_mask[ii])); + if (retval < 0) + goto exit; + } + } + + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->reinit != NULL) + exp_fhandler->exp_fn->reinit(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + synaptics_rmi4_set_configured(rmi4_data); + + retval = 0; + +exit: + mutex_unlock(&(rmi4_data->rmi4_reset_mutex)); + return retval; +} + +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + int temp; + unsigned char command = 0x01; + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + + mutex_lock(&(rmi4_data->rmi4_reset_mutex)); + + rmi4_data->touch_stopped = true; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_cmd_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue reset command, error = %d\n", + __func__, retval); + mutex_unlock(&(rmi4_data->rmi4_reset_mutex)); + return retval; + } + + msleep(rmi4_data->hw_if->board_data->reset_delay_ms); + + synaptics_rmi4_free_fingers(rmi4_data); + + synaptics_rmi4_empty_fn_list(rmi4_data); + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to query device\n", + __func__); + mutex_unlock(&(rmi4_data->rmi4_reset_mutex)); + return retval; + } + + if (rmi4_data->hw_if->board_data->swap_axes) { + temp = rmi4_data->sensor_max_x; + rmi4_data->sensor_max_x = rmi4_data->sensor_max_y; + rmi4_data->sensor_max_y = temp; + } + + synaptics_rmi4_set_params(rmi4_data); + + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->reset != NULL) + exp_fhandler->exp_fn->reset(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + rmi4_data->touch_stopped = false; + + mutex_unlock(&(rmi4_data->rmi4_reset_mutex)); + + return 0; +} + +/** +* synaptics_rmi4_exp_fn_work() +* +* Called by the kernel at the scheduled time. +* +* This function is a work thread that checks for the insertion and +* removal of other expansion Function modules such as rmi_dev and calls +* their initialization and removal callback functions accordingly. +*/ +static void synaptics_rmi4_exp_fn_work(struct work_struct *work) +{ + int retval; + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_exp_fhandler *exp_fhandler_temp; + struct synaptics_rmi4_data *rmi4_data = exp_data.rmi4_data; + + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry_safe(exp_fhandler, + exp_fhandler_temp, + &exp_data.list, + link) { + if ((exp_fhandler->exp_fn->init != NULL) && + exp_fhandler->insert) { + retval = exp_fhandler->exp_fn->init(rmi4_data); + if (retval < 0) { + list_del(&exp_fhandler->link); + kfree(exp_fhandler); + } else { + exp_fhandler->insert = false; + } + } else if ((exp_fhandler->exp_fn->remove != NULL) && + exp_fhandler->remove) { + exp_fhandler->exp_fn->remove(rmi4_data); + list_del(&exp_fhandler->link); + kfree(exp_fhandler); + } + } + } + mutex_unlock(&exp_data.mutex); + + return; +} + +/** +* synaptics_rmi4_dsx_new_function() +* +* Called by other expansion Function modules in their module init and +* module exit functions. +* +* This function is used by other expansion Function modules such as +* rmi_dev to register themselves with the driver by providing their +* initialization and removal callback function pointers so that they +* can be inserted or removed dynamically at module init and exit times, +* respectively. +*/ +void synaptics_rmi4_dsx_new_function(struct synaptics_rmi4_exp_fn *exp_fn, + bool insert) +{ + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + + if (!exp_data.initialized) { + mutex_init(&exp_data.mutex); + INIT_LIST_HEAD(&exp_data.list); + exp_data.initialized = true; + } + + mutex_lock(&exp_data.mutex); + if (insert) { + exp_fhandler = kzalloc(sizeof(*exp_fhandler), GFP_KERNEL); + if (!exp_fhandler) { + pr_err("%s: Failed to alloc mem for expansion function\n", + __func__); + goto exit; + } + exp_fhandler->exp_fn = exp_fn; + exp_fhandler->insert = true; + exp_fhandler->remove = false; + list_add_tail(&exp_fhandler->link, &exp_data.list); + } else if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) { + if (exp_fhandler->exp_fn->fn_type == exp_fn->fn_type) { + exp_fhandler->insert = false; + exp_fhandler->remove = true; + goto exit; + } + } + } + +exit: + mutex_unlock(&exp_data.mutex); + + if (exp_data.queue_work) { + queue_delayed_work(exp_data.workqueue, + &exp_data.work, + msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + } + + return; +} +EXPORT_SYMBOL(synaptics_rmi4_dsx_new_function); + +static int synaptics_dsx_regulator_configure(struct synaptics_rmi4_data + *rmi4_data) +{ + int retval; + u32 voltage_supply[2]; + u32 current_supply; + + /* Regulator VDD */ + rmi4_data->regulator_vdd = regulator_get(rmi4_data->pdev->dev.parent, + "vdd"); + if (IS_ERR(rmi4_data->regulator_vdd)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to get regulator vdd\n", + __func__); + retval = PTR_ERR(rmi4_data->regulator_vdd); + return retval; + } + + /* Read and set vdd regulator voltage and current */ + retval = of_property_read_u32(rmi4_data->pdev->dev.parent->of_node, + "synaptics,vdd-current", ¤t_supply); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to get regulator vdd current\n", + __func__); + goto err_vdd_regulator; + } + rmi4_data->regulator_vdd_current = current_supply; + + retval = regulator_set_load(rmi4_data->regulator_vdd, + rmi4_data->regulator_vdd_current); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set regulator current vdd\n", + __func__); + goto err_vdd_regulator; + } + + retval = of_property_read_u32_array( + rmi4_data->pdev->dev.parent->of_node, + "synaptics,vdd-voltage", voltage_supply, 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to get regulator vdd voltage\n", + __func__); + goto err_vdd_regulator; + } + rmi4_data->regulator_vdd_vmin = voltage_supply[0]; + rmi4_data->regulator_vdd_vmax = voltage_supply[1]; + + retval = regulator_set_voltage(rmi4_data->regulator_vdd, + rmi4_data->regulator_vdd_vmin, + rmi4_data->regulator_vdd_vmax); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set regulator voltage vdd\n", + __func__); + goto err_vdd_regulator; + } + + /* Regulator AVDD */ + rmi4_data->regulator_avdd = regulator_get(rmi4_data->pdev->dev.parent, + "avdd"); + if (IS_ERR(rmi4_data->regulator_avdd)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to get regulator avdd\n", + __func__); + retval = PTR_ERR(rmi4_data->regulator_avdd); + goto err_vdd_regulator; + } + + /* Read and set avdd regulator voltage and current */ + retval = of_property_read_u32(rmi4_data->pdev->dev.parent->of_node, + "synaptics,avdd-current", ¤t_supply); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to get regulator avdd current\n", + __func__); + goto err_avdd_regulator; + } + rmi4_data->regulator_avdd_current = current_supply; + + retval = regulator_set_load(rmi4_data->regulator_avdd, + rmi4_data->regulator_avdd_current); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set regulator current avdd\n", + __func__); + goto err_avdd_regulator; + } + + retval = of_property_read_u32_array( + rmi4_data->pdev->dev.parent->of_node, + "synaptics,avdd-voltage", voltage_supply, 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to get regulator avdd voltage\n", + __func__); + goto err_avdd_regulator; + } + rmi4_data->regulator_avdd_vmin = voltage_supply[0]; + rmi4_data->regulator_avdd_vmax = voltage_supply[1]; + + retval = regulator_set_voltage(rmi4_data->regulator_avdd, + rmi4_data->regulator_avdd_vmin, + rmi4_data->regulator_avdd_vmax); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set regultor voltage avdd\n", + __func__); + goto err_avdd_regulator; + } + + return 0; + +err_avdd_regulator: + regulator_put(rmi4_data->regulator_avdd); +err_vdd_regulator: + regulator_put(rmi4_data->regulator_vdd); + + return retval; +}; + +static int synaptics_dsx_regulator_enable(struct synaptics_rmi4_data + *rmi4_data, bool on) +{ + int retval; + + if (on) { + retval = regulator_enable(rmi4_data->regulator_vdd); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to enable regulator vdd\n", + __func__); + return retval; + } + retval = regulator_enable(rmi4_data->regulator_avdd); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to enable regulator avdd\n", + __func__); + regulator_disable(rmi4_data->regulator_vdd); + return retval; + } + msleep(rmi4_data->hw_if->board_data->power_delay_ms); + } else { + regulator_disable(rmi4_data->regulator_vdd); + regulator_disable(rmi4_data->regulator_avdd); + } + + return 0; +} + + /** + * synaptics_rmi4_probe() + * + * Called by the kernel when an association with an I2C device of the + * same name is made (after doing i2c_add_driver). + * + * This funtion allocates and initializes the resources for the driver + * as an input driver, turns on the power to the sensor, queries the + * sensor for its supported Functions and characteristics, registers + * the driver to the input subsystem, sets up the interrupt, handles + * the registration of the early_suspend and late_resume functions, + * and creates a work queue for detection of other expansion Function + * modules. + */ +static int synaptics_rmi4_probe(struct platform_device *pdev) +{ + int retval, len; + unsigned char attr_count; + struct synaptics_rmi4_data *rmi4_data; + const struct synaptics_dsx_hw_interface *hw_if; + const struct synaptics_dsx_board_data *bdata; + struct dentry *temp; + + hw_if = pdev->dev.platform_data; + if (!hw_if) { + dev_err(&pdev->dev, + "%s: No hardware interface found\n", + __func__); + return -EINVAL; + } + + bdata = hw_if->board_data; + if (!bdata) { + dev_err(&pdev->dev, + "%s: No board data found\n", + __func__); + return -EINVAL; + } + + rmi4_data = kzalloc(sizeof(*rmi4_data), GFP_KERNEL); + if (!rmi4_data) { + dev_err(&pdev->dev, + "%s: Failed to alloc mem for rmi4_data\n", + __func__); + return -ENOMEM; + } + + rmi4_data->pdev = pdev; + rmi4_data->current_page = MASK_8BIT; + rmi4_data->hw_if = hw_if; + rmi4_data->touch_stopped = false; + rmi4_data->sensor_sleep = false; + rmi4_data->irq_enabled = false; + rmi4_data->fw_updating = false; + rmi4_data->fingers_on_2d = false; + rmi4_data->update_coords = true; + + rmi4_data->irq_enable = synaptics_rmi4_irq_enable; + rmi4_data->reset_device = synaptics_rmi4_reset_device; + + mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex)); + mutex_init(&(rmi4_data->rmi4_reset_mutex)); + + retval = synaptics_dsx_regulator_configure(rmi4_data); + if (retval) { + dev_err(&pdev->dev, + "%s: regulator configuration failed\n", __func__); + goto err_regulator_configure; + } + retval = synaptics_dsx_regulator_enable(rmi4_data, true); + if (retval) { + dev_err(&pdev->dev, + "%s: regulator enable failed\n", __func__); + goto err_regulator_enable; + } + + platform_set_drvdata(pdev, rmi4_data); + + if (bdata->gpio_config) { + retval = synaptics_rmi4_set_gpio(rmi4_data); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to set up GPIO's\n", + __func__); + goto err_set_gpio; + } + } else { + retval = synaptics_dsx_pinctrl_init(rmi4_data); + if (!retval && rmi4_data->ts_pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is found + * let pins to be configured in active state. If not + * found continue further without error. + */ + retval = pinctrl_select_state(rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_active); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to select %s pinstate %d\n", + __func__, PINCTRL_STATE_ACTIVE, retval); + } + } + + retval = synaptics_dsx_gpio_configure(rmi4_data, true); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to set up GPIO's\n", + __func__); + goto err_config_gpio; + } + } + + if (bdata->fw_name) { + len = strlen(bdata->fw_name); + if (len > SYNA_FW_NAME_MAX_LEN - 1) { + dev_err(&pdev->dev, "Invalid firmware name\n"); + goto err_set_input_dev; + } + + strlcpy(rmi4_data->fw_name, bdata->fw_name, len + 1); + } + + retval = synaptics_rmi4_set_input_dev(rmi4_data); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to set up input device\n", + __func__); + goto err_set_input_dev; + } + +#ifdef CONFIG_FB + INIT_WORK(&rmi4_data->fb_notify_work, fb_notify_resume_work); + rmi4_data->fb_notif.notifier_call = fb_notifier_callback; + + retval = fb_register_client(&rmi4_data->fb_notif); + if (retval) + dev_err(rmi4_data->pdev->dev.parent, + "Unable to register fb_notifier: %d\n", retval); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + rmi4_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + rmi4_data->early_suspend.suspend = synaptics_rmi4_early_suspend; + rmi4_data->early_suspend.resume = synaptics_rmi4_late_resume; + register_early_suspend(&rmi4_data->early_suspend); +#endif + + rmi4_data->irq = gpio_to_irq(bdata->irq_gpio); + + if (!exp_data.initialized) { + mutex_init(&exp_data.mutex); + INIT_LIST_HEAD(&exp_data.list); + exp_data.initialized = true; + } + + retval = synaptics_rmi4_irq_enable(rmi4_data, true); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to enable attention interrupt\n", + __func__); + goto err_enable_irq; + } + + exp_data.workqueue = create_singlethread_workqueue("dsx_exp_workqueue"); + INIT_DELAYED_WORK(&exp_data.work, synaptics_rmi4_exp_fn_work); + exp_data.rmi4_data = rmi4_data; + exp_data.queue_work = true; + queue_delayed_work(exp_data.workqueue, + &exp_data.work, + msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + + rmi4_data->dir = debugfs_create_dir(DEBUGFS_DIR_NAME, NULL); + if (rmi4_data->dir == NULL || IS_ERR(rmi4_data->dir)) { + retval = rmi4_data->dir ? PTR_ERR(rmi4_data->dir) : -EIO; + dev_err(&pdev->dev, + "%s: Failed to create debugfs directory, rc = %d\n", + __func__, retval); + goto err_create_debugfs_dir; + } + + temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, rmi4_data->dir, + rmi4_data, &debug_suspend_fops); + if (temp == NULL || IS_ERR(temp)) { + retval = temp ? PTR_ERR(temp) : -EIO; + dev_err(&pdev->dev, + "%s: Failed to create suspend debugfs file, rc = %d\n", + __func__, retval); + goto err_create_debugfs_file; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto err_sysfs; + } + } + + synaptics_secure_touch_init(rmi4_data); + synaptics_secure_touch_stop(rmi4_data, 1); + + return retval; + +err_sysfs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } +err_create_debugfs_file: + debugfs_remove_recursive(rmi4_data->dir); +err_create_debugfs_dir: + cancel_delayed_work_sync(&exp_data.work); + if (exp_data.workqueue != NULL) { + flush_workqueue(exp_data.workqueue); + destroy_workqueue(exp_data.workqueue); + } + synaptics_rmi4_irq_enable(rmi4_data, false); + free_irq(rmi4_data->irq, rmi4_data); + +err_enable_irq: +#if defined(CONFIG_FB) + fb_unregister_client(&rmi4_data->fb_notif); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&rmi4_data->early_suspend); +#endif + + synaptics_rmi4_empty_fn_list(rmi4_data); + input_unregister_device(rmi4_data->input_dev); + rmi4_data->input_dev = NULL; + +err_set_input_dev: + if (bdata->gpio_config) { + bdata->gpio_config( + bdata->irq_gpio, + false, 0, 0); + + if (bdata->reset_gpio >= 0) { + bdata->gpio_config( + bdata->reset_gpio, + false, 0, 0); + } + + if (bdata->power_gpio >= 0) { + bdata->gpio_config( + bdata->power_gpio, + false, 0, 0); + } + } else { + synaptics_dsx_gpio_configure(rmi4_data, false); + } +err_config_gpio: + if (rmi4_data->ts_pinctrl) { + if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) { + devm_pinctrl_put(rmi4_data->ts_pinctrl); + rmi4_data->ts_pinctrl = NULL; + } else { + retval = pinctrl_select_state( + rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_release); + if (retval) + dev_err(&pdev->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + } + } + +err_set_gpio: + regulator_disable(rmi4_data->regulator_vdd); + regulator_disable(rmi4_data->regulator_avdd); +err_regulator_enable: + regulator_put(rmi4_data->regulator_vdd); + regulator_put(rmi4_data->regulator_avdd); +err_regulator_configure: + kfree(rmi4_data); + + return retval; +} + + /** + * synaptics_rmi4_remove() + * + * Called by the kernel when the association with an I2C device of the + * same name is broken (when the driver is unloaded). + * + * This funtion terminates the work queue, stops sensor data acquisition, + * frees the interrupt, unregisters the driver from the input subsystem, + * turns off the power to the sensor, and frees other allocated resources. + */ +static int synaptics_rmi4_remove(struct platform_device *pdev) +{ + unsigned char attr_count; + struct synaptics_rmi4_data *rmi4_data = platform_get_drvdata(pdev); + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + int err; + + if (rmi4_data->support_vkeys) { + sysfs_remove_group(vkey_kobj, &vkey_grp); + kobject_put(vkey_kobj); + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + + debugfs_remove_recursive(rmi4_data->dir); + cancel_delayed_work_sync(&exp_data.work); + flush_workqueue(exp_data.workqueue); + destroy_workqueue(exp_data.workqueue); + + synaptics_rmi4_irq_enable(rmi4_data, false); + +#if defined(CONFIG_FB) + fb_unregister_client(&rmi4_data->fb_notif); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&rmi4_data->early_suspend); +#endif + + synaptics_rmi4_empty_fn_list(rmi4_data); + input_unregister_device(rmi4_data->input_dev); + rmi4_data->input_dev = NULL; + + if (bdata->gpio_config) { + bdata->gpio_config( + bdata->irq_gpio, + false, 0, 0); + + if (bdata->reset_gpio >= 0) { + bdata->gpio_config( + bdata->reset_gpio, + false, 0, 0); + } + + if (bdata->power_gpio >= 0) { + bdata->gpio_config( + bdata->power_gpio, + false, 0, 0); + } + } else { + synaptics_dsx_gpio_configure(rmi4_data, false); + if (rmi4_data->ts_pinctrl) { + if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) { + devm_pinctrl_put(rmi4_data->ts_pinctrl); + rmi4_data->ts_pinctrl = NULL; + } else { + err = pinctrl_select_state( + rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_release); + if (err) + dev_err(&pdev->dev, + "Failed to select release pinctrl state %d\n", + err); + } + } + } + + if (rmi4_data->regulator_vdd) { + regulator_disable(rmi4_data->regulator_vdd); + regulator_put(rmi4_data->regulator_vdd); + } + + if (rmi4_data->regulator_avdd) { + regulator_disable(rmi4_data->regulator_avdd); + regulator_put(rmi4_data->regulator_avdd); + } + + kfree(rmi4_data); + + return 0; +} + +#if defined(CONFIG_FB) +static void fb_notify_resume_work(struct work_struct *work) +{ + struct synaptics_rmi4_data *rmi4_data = + container_of(work, struct synaptics_rmi4_data, fb_notify_work); + synaptics_rmi4_resume(&(rmi4_data->input_dev->dev)); +} + +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct synaptics_rmi4_data *rmi4_data = + container_of(self, struct synaptics_rmi4_data, fb_notif); + + if (evdata && evdata->data && rmi4_data) { + blank = evdata->data; + if (rmi4_data->hw_if->board_data->resume_in_workqueue) { + if (event == FB_EARLY_EVENT_BLANK) { + synaptics_secure_touch_stop(rmi4_data, 0); + if (*blank == FB_BLANK_UNBLANK) + schedule_work( + &(rmi4_data->fb_notify_work)); + } else if (event == FB_EVENT_BLANK && + *blank == FB_BLANK_POWERDOWN) { + flush_work( + &(rmi4_data->fb_notify_work)); + synaptics_rmi4_suspend( + &(rmi4_data->input_dev->dev)); + } + } else { + if (event == FB_EARLY_EVENT_BLANK) { + synaptics_secure_touch_stop(rmi4_data, 0); + } else if (event == FB_EVENT_BLANK) { + if (*blank == FB_BLANK_UNBLANK) + synaptics_rmi4_resume( + &(rmi4_data->input_dev->dev)); + else if (*blank == FB_BLANK_POWERDOWN) + synaptics_rmi4_suspend( + &(rmi4_data->input_dev->dev)); + } + } + } + + return 0; +} +#elif defined(CONFIG_HAS_EARLYSUSPEND) + /** + * synaptics_rmi4_early_suspend() + * + * Called by the kernel during the early suspend phase when the system + * enters suspend. + * + * This function calls synaptics_rmi4_sensor_sleep() to stop finger + * data acquisition and put the sensor to sleep. + */ +static void synaptics_rmi4_early_suspend(struct early_suspend *h) +{ + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_data *rmi4_data = + container_of(h, struct synaptics_rmi4_data, + early_suspend); + + if (rmi4_data->stay_awake) { + rmi4_data->staying_awake = true; + return; + } else { + rmi4_data->staying_awake = false; + } + + synaptics_secure_touch_stop(rmi4_data, 0); + + rmi4_data->touch_stopped = true; + synaptics_rmi4_irq_enable(rmi4_data, false); + synaptics_rmi4_sensor_sleep(rmi4_data); + synaptics_rmi4_free_fingers(rmi4_data); + + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->early_suspend != NULL) + exp_fhandler->exp_fn->early_suspend(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + if (rmi4_data->full_pm_cycle) + synaptics_rmi4_suspend(&(rmi4_data->input_dev->dev)); + + return; +} + + /** + * synaptics_rmi4_late_resume() + * + * Called by the kernel during the late resume phase when the system + * wakes up from suspend. + * + * This function goes through the sensor wake process if the system wakes + * up from early suspend (without going into suspend). + */ +static void synaptics_rmi4_late_resume(struct early_suspend *h) +{ + int retval; + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_data *rmi4_data = + container_of(h, struct synaptics_rmi4_data, + early_suspend); + + if (rmi4_data->staying_awake) + return; + + synaptics_secure_touch_stop(rmi4_data, 0); + + if (rmi4_data->full_pm_cycle) + synaptics_rmi4_resume(&(rmi4_data->input_dev->dev)); + + if (rmi4_data->sensor_sleep == true) { + synaptics_rmi4_sensor_wake(rmi4_data); + synaptics_rmi4_irq_enable(rmi4_data, true); + retval = synaptics_rmi4_reinit_device(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to reinit device\n", + __func__); + } + } + + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->late_resume != NULL) + exp_fhandler->exp_fn->late_resume(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + rmi4_data->touch_stopped = false; + + return; +} +#endif + +#ifdef CONFIG_PM + /** + * synaptics_rmi4_sensor_sleep() + * + * Called by synaptics_rmi4_early_suspend() and synaptics_rmi4_suspend(). + * + * This function stops finger data acquisition and puts the sensor to sleep. + */ +static void synaptics_rmi4_sensor_sleep(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char device_ctrl; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to enter sleep mode\n", + __func__); + rmi4_data->sensor_sleep = false; + return; + } + + device_ctrl = (device_ctrl & ~MASK_3BIT); + device_ctrl = (device_ctrl | NO_SLEEP_OFF | SENSOR_SLEEP); + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to enter sleep mode\n", + __func__); + rmi4_data->sensor_sleep = false; + return; + } else { + rmi4_data->sensor_sleep = true; + } + + return; +} + + /** + * synaptics_rmi4_sensor_wake() + * + * Called by synaptics_rmi4_resume() and synaptics_rmi4_late_resume(). + * + * This function wakes the sensor from sleep. + */ +static void synaptics_rmi4_sensor_wake(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char device_ctrl; + unsigned char no_sleep_setting = rmi4_data->no_sleep_setting; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to wake from sleep mode\n", + __func__); + rmi4_data->sensor_sleep = true; + return; + } + + device_ctrl = (device_ctrl & ~MASK_3BIT); + device_ctrl = (device_ctrl | no_sleep_setting | NORMAL_OPERATION); + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to wake from sleep mode\n", + __func__); + rmi4_data->sensor_sleep = true; + return; + } else { + rmi4_data->sensor_sleep = false; + } + + return; +} + + /** + * synaptics_rmi4_suspend() + * + * Called by the kernel during the suspend phase when the system + * enters suspend. + * + * This function stops finger data acquisition and puts the sensor to + * sleep (if not already done so during the early suspend phase), + * disables the interrupt, and turns off the power to the sensor. + */ +static int synaptics_rmi4_suspend(struct device *dev) +{ + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + int retval; + + if (rmi4_data->stay_awake) { + rmi4_data->staying_awake = true; + return 0; + } else { + rmi4_data->staying_awake = false; + } + + if (rmi4_data->suspended) { + dev_info(dev, "Already in suspend state\n"); + return 0; + } + + synaptics_secure_touch_stop(rmi4_data, 1); + + if (!rmi4_data->fw_updating) { + if (!rmi4_data->sensor_sleep) { + rmi4_data->touch_stopped = true; + synaptics_rmi4_irq_enable(rmi4_data, false); + synaptics_rmi4_sensor_sleep(rmi4_data); + synaptics_rmi4_free_fingers(rmi4_data); + } + + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->suspend != NULL) + exp_fhandler->exp_fn->suspend(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + retval = synaptics_dsx_regulator_enable(rmi4_data, false); + if (retval < 0) { + dev_err(dev, "failed to enter low power mode\n"); + goto err_lpm_regulator; + } + } else { + dev_err(dev, + "Firmware updating, cannot go into suspend mode\n"); + return 0; + } + + if (bdata->disable_gpios) { + if (rmi4_data->ts_pinctrl) { + retval = pinctrl_select_state(rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_suspend); + if (retval < 0) { + dev_err(dev, "Cannot get idle pinctrl state\n"); + goto err_pinctrl_select_suspend; + } + } + + retval = synaptics_dsx_gpio_configure(rmi4_data, false); + if (retval < 0) { + dev_err(dev, "failed to put gpios in suspend state\n"); + goto err_gpio_configure; + } + } + + rmi4_data->suspended = true; + + return 0; + +err_gpio_configure: + if (rmi4_data->ts_pinctrl) { + retval = pinctrl_select_state(rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_active); + if (retval < 0) + dev_err(dev, "Cannot get default pinctrl state\n"); + } +err_pinctrl_select_suspend: + synaptics_dsx_regulator_enable(rmi4_data, true); +err_lpm_regulator: + if (rmi4_data->sensor_sleep) { + synaptics_rmi4_sensor_wake(rmi4_data); + synaptics_rmi4_irq_enable(rmi4_data, true); + rmi4_data->touch_stopped = false; + } + + return retval; +} + + /** + * synaptics_rmi4_resume() + * + * Called by the kernel during the resume phase when the system + * wakes up from suspend. + * + * This function turns on the power to the sensor, wakes the sensor + * from sleep, enables the interrupt, and starts finger data + * acquisition. + */ +static int synaptics_rmi4_resume(struct device *dev) +{ + int retval; + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_device_info *rmi; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + rmi = &(rmi4_data->rmi4_mod_info); + if (rmi->package_id == SYNA_S332U_PACKAGE_ID && + rmi->package_id_rev == SYNA_S332U_PACKAGE_ID_REV) { + synaptics_rmi4_reset_device(rmi4_data); + } + + if (rmi4_data->staying_awake) + return 0; + + if (!rmi4_data->suspended) + return 0; + + synaptics_secure_touch_stop(rmi4_data, 1); + + synaptics_dsx_regulator_enable(rmi4_data, true); + + if (bdata->disable_gpios) { + if (rmi4_data->ts_pinctrl) { + retval = pinctrl_select_state(rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_active); + if (retval < 0) + dev_err(dev, "Cannot get default pinctrl state\n"); + } + + retval = synaptics_dsx_gpio_configure(rmi4_data, true); + if (retval < 0) + dev_err(dev, "Failed to put gpios in active state\n"); + } + + synaptics_rmi4_sensor_wake(rmi4_data); + retval = synaptics_rmi4_reinit_device(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to reinit device\n", + __func__); + return retval; + } + + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->resume != NULL) + exp_fhandler->exp_fn->resume(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + rmi4_data->touch_stopped = false; + rmi4_data->suspended = false; + + synaptics_rmi4_irq_enable(rmi4_data, true); + + return 0; +} + +static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = { +#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) + .suspend = synaptics_rmi4_suspend, + .resume = synaptics_rmi4_resume, +#endif +}; +#else +static int synaptics_rmi4_suspend(struct device *dev) +{ + dev_err(dev, "PM not supported\n"); + return -EINVAL; +} + +static int synaptics_rmi4_resume(struct device *dev) +{ + dev_err(dev, "PM not supported\n"); + return -EINVAL; +} +#endif + +static struct platform_driver synaptics_rmi4_driver = { + .driver = { + .name = PLATFORM_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &synaptics_rmi4_dev_pm_ops, +#endif + }, + .probe = synaptics_rmi4_probe, + .remove = synaptics_rmi4_remove, +}; + + /** + * synaptics_rmi4_init() + * + * Called by the kernel during do_initcalls (if built-in) + * or when the driver is loaded (if a module). + * + * This function registers the driver to the I2C subsystem. + * + */ +static int __init synaptics_rmi4_init(void) +{ + int retval; + + retval = synaptics_rmi4_bus_init(); + if (retval) + return retval; + + return platform_driver_register(&synaptics_rmi4_driver); +} + + /** + * synaptics_rmi4_exit() + * + * Called by the kernel when the driver is unloaded. + * + * This funtion unregisters the driver from the I2C subsystem. + * + */ +static void __exit synaptics_rmi4_exit(void) +{ + platform_driver_unregister(&synaptics_rmi4_driver); + + synaptics_rmi4_bus_exit(); + + return; +} + +module_init(synaptics_rmi4_init); +module_exit(synaptics_rmi4_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX I2C Touch Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h new file mode 100644 index 000000000000..7d7e045d7917 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h @@ -0,0 +1,405 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com> + * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com> + * Copyright (c) 2014, 2016, The Linux Foundation. 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. + */ +#ifndef _SYNAPTICS_DSX_RMI4_H_ +#define _SYNAPTICS_DSX_RMI4_H_ + +#define SYNAPTICS_DS4 (1 << 0) +#define SYNAPTICS_DS5 (1 << 1) +#define SYNAPTICS_DSX_DRIVER_PRODUCT (SYNAPTICS_DS4 | SYNAPTICS_DS5) +#define SYNAPTICS_DSX_DRIVER_VERSION 0x2001 + +#include <linux/version.h> +#include <linux/debugfs.h> + +#if defined(CONFIG_FB) +#include <linux/notifier.h> +#include <linux/fb.h> +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include <linux/earlysuspend.h> +#endif +#if defined(CONFIG_SECURE_TOUCH) +#include <linux/completion.h> +#include <linux/atomic.h> +#include <linux/clk.h> +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)) +#define KERNEL_ABOVE_2_6_38 +#endif + +#ifdef KERNEL_ABOVE_2_6_38 +#define sstrtoul(...) kstrtoul(__VA_ARGS__) +#else +#define sstrtoul(...) strict_strtoul(__VA_ARGS__) +#endif + +#define PDT_PROPS (0X00EF) +#define PDT_START (0x00E9) +#define PDT_END (0x00D0) +#define PDT_ENTRY_SIZE (0x0006) +#define PAGES_TO_SERVICE (10) +#define PAGE_SELECT_LEN (2) +#define ADDRESS_WORD_LEN (2) + +#define SYNAPTICS_RMI4_F01 (0x01) +#define SYNAPTICS_RMI4_F11 (0x11) +#define SYNAPTICS_RMI4_F12 (0x12) +#define SYNAPTICS_RMI4_F1A (0x1a) +#define SYNAPTICS_RMI4_F34 (0x34) +#define SYNAPTICS_RMI4_F51 (0x51) +#define SYNAPTICS_RMI4_F54 (0x54) +#define SYNAPTICS_RMI4_F55 (0x55) + +#define SYNAPTICS_RMI4_PRODUCT_INFO_SIZE 2 +#define SYNAPTICS_RMI4_DATE_CODE_SIZE 3 +#define SYNAPTICS_RMI4_PRODUCT_ID_SIZE 10 +#define SYNAPTICS_RMI4_BUILD_ID_SIZE 3 + +#define F01_PACKAGE_ID_OFFSET 17 +#define PACKAGE_ID_SIZE 4 + +#define F12_FINGERS_TO_SUPPORT 10 +#define F12_NO_OBJECT_STATUS 0x00 +#define F12_FINGER_STATUS 0x01 +#define F12_STYLUS_STATUS 0x02 +#define F12_PALM_STATUS 0x03 +#define F12_HOVERING_FINGER_STATUS 0x05 +#define F12_GLOVED_FINGER_STATUS 0x06 + +#define MAX_NUMBER_OF_BUTTONS 4 +#define MAX_INTR_REGISTERS 4 + +#define MASK_16BIT 0xFFFF +#define MASK_8BIT 0xFF +#define MASK_7BIT 0x7F +#define MASK_6BIT 0x3F +#define MASK_5BIT 0x1F +#define MASK_4BIT 0x0F +#define MASK_3BIT 0x07 +#define MASK_2BIT 0x03 +#define MASK_1BIT 0x01 + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" + +#define SYNA_FW_NAME_MAX_LEN 50 + +enum exp_fn { + RMI_DEV = 0, + RMI_F54, + RMI_FW_UPDATER, + RMI_PROXIMITY, + RMI_ACTIVE_PEN, + RMI_LAST, +}; + +struct synaptics_dsx_hw_interface { + struct synaptics_dsx_board_data *board_data; + const struct synaptics_dsx_bus_access *bus_access; +}; + +/* + * struct synaptics_rmi4_fn_desc - function descriptor fields in PDT + * @query_base_addr: base address for query registers + * @cmd_base_addr: base address for command registers + * @ctrl_base_addr: base address for control registers + * @data_base_addr: base address for data registers + * @intr_src_count: number of interrupt sources + * @fn_number: function number + */ +struct synaptics_rmi4_fn_desc { + unsigned char query_base_addr; + unsigned char cmd_base_addr; + unsigned char ctrl_base_addr; + unsigned char data_base_addr; + unsigned char intr_src_count; + unsigned char fn_number; +}; + +/* + * synaptics_rmi4_fn_full_addr - full 16-bit base addresses + * @query_base: 16-bit base address for query registers + * @cmd_base: 16-bit base address for data registers + * @ctrl_base: 16-bit base address for command registers + * @data_base: 16-bit base address for control registers + */ +struct synaptics_rmi4_fn_full_addr { + unsigned short query_base; + unsigned short cmd_base; + unsigned short ctrl_base; + unsigned short data_base; +}; + +struct synaptics_rmi4_f12_extra_data { + unsigned char data1_offset; + unsigned char data15_offset; + unsigned char data15_size; + unsigned char data15_data[(F12_FINGERS_TO_SUPPORT + 7) / 8]; +}; + +/* + * struct synaptics_rmi4_fn - function handler data structure + * @fn_number: function number + * @num_of_data_sources: number of data sources + * @num_of_data_points: maximum number of fingers supported + * @size_of_data_register_block: data register block size + * @intr_reg_num: index to associated interrupt register + * @intr_mask: interrupt mask + * @full_addr: full 16-bit base addresses of function registers + * @link: linked list for function handlers + * @data_size: size of private data + * @data: pointer to private data + */ +struct synaptics_rmi4_fn { + unsigned char fn_number; + unsigned char num_of_data_sources; + unsigned char num_of_data_points; + unsigned char size_of_data_register_block; + unsigned char intr_reg_num; + unsigned char intr_mask; + struct synaptics_rmi4_fn_full_addr full_addr; + struct list_head link; + int data_size; + void *data; + void *extra; +}; + +/* + * struct synaptics_rmi4_device_info - device information + * @version_major: rmi protocol major version number + * @version_minor: rmi protocol minor version number + * @manufacturer_id: manufacturer id + * @product_props: product properties information + * @product_info: product info array + * @date_code: device manufacture date + * @tester_id: tester id array + * @serial_number: device serial number + * @product_id_string: device product id + * @support_fn_list: linked list for function handlers + */ +struct synaptics_rmi4_device_info { + unsigned int version_major; + unsigned int version_minor; + unsigned char manufacturer_id; + unsigned char product_props; + unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE]; + unsigned char date_code[SYNAPTICS_RMI4_DATE_CODE_SIZE]; + unsigned short tester_id; + unsigned short serial_number; + unsigned char product_id_string[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; + unsigned char build_id[SYNAPTICS_RMI4_BUILD_ID_SIZE]; + struct list_head support_fn_list; + unsigned int package_id; + unsigned int package_id_rev; +}; + +/* + * struct synaptics_rmi4_data - rmi4 device instance data + * @pdev: pointer to platform device + * @input_dev: pointer to associated input device + * @hw_if: pointer to hardware interface data + * @rmi4_mod_info: device information + * @regulator_vdd: pointer to associated vdd regulator + * @regulator_add: pointer to associated avdd regulator + * @regulator_vdd_vmin: minimum vdd regulator voltage + * @regulator_vdd_vmax: maximum vdd regulator voltage + * @regulator_vdd_current: vdd regulator current load + * @regulator_avdd_vmin: minimum avdd regulator voltage + * @regulator_avdd_vmax: maximum avdd regulator voltage + * @regulator_avdd_current: avdd regulator current load + * @rmi4_io_ctrl_mutex: mutex for i2c i/o control + * @early_suspend: instance to support early suspend power management + * @current_page: current page in sensor to acess + * @button_0d_enabled: flag for 0d button support + * @full_pm_cycle: flag for full power management cycle in early suspend stage + * @num_of_intr_regs: number of interrupt registers + * @f01_query_base_addr: query base address for f01 + * @f01_cmd_base_addr: command base address for f01 + * @f01_ctrl_base_addr: control base address for f01 + * @f01_data_base_addr: data base address for f01 + * @irq: attention interrupt + * @sensor_max_x: sensor maximum x value + * @sensor_max_y: sensor maximum y value + * @irq_enabled: flag for indicating interrupt enable status + * @fingers_on_2d: flag to indicate presence of fingers in 2d area + * @sensor_sleep: flag to indicate sleep state of sensor + * @wait: wait queue for touch data polling in interrupt thread + * @irq_enable: pointer to irq enable function + */ +struct synaptics_rmi4_data { + struct platform_device *pdev; + struct input_dev *input_dev; + const struct synaptics_dsx_hw_interface *hw_if; + struct synaptics_rmi4_device_info rmi4_mod_info; + struct regulator *regulator_vdd; + struct regulator *regulator_avdd; + int regulator_vdd_vmin; + int regulator_vdd_vmax; + int regulator_vdd_current; + int regulator_avdd_vmin; + int regulator_avdd_vmax; + int regulator_avdd_current; + struct mutex rmi4_reset_mutex; + struct mutex rmi4_io_ctrl_mutex; +#if defined(CONFIG_FB) + struct work_struct fb_notify_work; + struct notifier_block fb_notif; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + struct dentry *dir; + unsigned char current_page; + unsigned char button_0d_enabled; + unsigned char full_pm_cycle; + unsigned char num_of_rx; + unsigned char num_of_tx; + unsigned char num_of_fingers; + unsigned char max_touch_width; + unsigned char report_enable; + unsigned char no_sleep_setting; + unsigned char intr_mask[MAX_INTR_REGISTERS]; + unsigned char *button_txrx_mapping; + unsigned short num_of_intr_regs; + unsigned short f01_query_base_addr; + unsigned short f01_cmd_base_addr; + unsigned short f01_ctrl_base_addr; + unsigned short f01_data_base_addr; + unsigned int firmware_id; + int irq; + int sensor_max_x; + int sensor_max_y; + bool flash_prog_mode; + bool irq_enabled; + bool touch_stopped; + bool fingers_on_2d; + bool sensor_sleep; + bool stay_awake; + bool staying_awake; + bool fw_updating; + bool support_vkeys; + bool update_coords; + int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable); + int (*reset_device)(struct synaptics_rmi4_data *rmi4_data); + + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; + char fw_name[SYNA_FW_NAME_MAX_LEN]; + bool suspended; +#if defined(CONFIG_SECURE_TOUCH) + atomic_t st_enabled; + atomic_t st_pending_irqs; + bool st_initialized; + struct completion st_powerdown; + struct completion st_irq_processed; + struct clk *core_clk; + struct clk *iface_clk; +#endif +}; + +struct synaptics_dsx_bus_access { + unsigned char type; + int (*read)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned short length); + int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned short length); +#if defined(CONFIG_SECURE_TOUCH) + int (*get)(struct synaptics_rmi4_data *rmi4_data); + void (*put)(struct synaptics_rmi4_data *rmi4_data); +#endif +}; + +struct synaptics_rmi4_exp_fn { + enum exp_fn fn_type; + int (*init)(struct synaptics_rmi4_data *rmi4_data); + void (*remove)(struct synaptics_rmi4_data *rmi4_data); + void (*reset)(struct synaptics_rmi4_data *rmi4_data); + void (*reinit)(struct synaptics_rmi4_data *rmi4_data); + void (*early_suspend)(struct synaptics_rmi4_data *rmi4_data); + void (*suspend)(struct synaptics_rmi4_data *rmi4_data); + void (*resume)(struct synaptics_rmi4_data *rmi4_data); + void (*late_resume)(struct synaptics_rmi4_data *rmi4_data); + void (*attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask); +}; + +int synaptics_rmi4_bus_init(void); + +void synaptics_rmi4_bus_exit(void); + +void synaptics_rmi4_dsx_new_function(struct synaptics_rmi4_exp_fn *exp_fn_mod, + bool insert); + +int synaptics_dsx_fw_updater(unsigned char *fw_data); + +int synaptics_dsx_get_dt_coords(struct device *dev, char *name, + struct synaptics_dsx_board_data *pdata, + struct device_node *node); + +static inline int synaptics_rmi4_reg_read( + struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, + unsigned char *data, + unsigned short len) +{ + return rmi4_data->hw_if->bus_access->read(rmi4_data, addr, data, len); +} + +static inline int synaptics_rmi4_reg_write( + struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, + unsigned char *data, + unsigned short len) +{ + return rmi4_data->hw_if->bus_access->write(rmi4_data, addr, data, len); +} + +#if defined(CONFIG_SECURE_TOUCH) +static inline int synaptics_rmi4_bus_get(struct synaptics_rmi4_data *rmi4_data) +{ + return rmi4_data->hw_if->bus_access->get(rmi4_data); +} +static inline void synaptics_rmi4_bus_put(struct synaptics_rmi4_data *rmi4_data) +{ + rmi4_data->hw_if->bus_access->put(rmi4_data); +} +#endif + +static inline ssize_t synaptics_rmi4_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + dev_warn(dev, "%s Attempted to write to read-only attribute %s\n", + __func__, attr->attr.name); + return -EPERM; +} + +static inline void batohs(unsigned short *dest, unsigned char *src) +{ + *dest = src[1] * 0x100 + src[0]; +} + +static inline void hstoba(unsigned char *dest, unsigned short src) +{ + dest[0] = src % 0x100; + dest[1] = src / 0x100; +} +#endif diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c new file mode 100644 index 000000000000..4787f2bcd768 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c @@ -0,0 +1,2161 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com> + * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com> + * + * 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/firmware.h> +#include <linux/platform_device.h> +#include <linux/input/synaptics_dsx_v2.h> +#include "synaptics_dsx_core.h" + +#define STARTUP_FW_UPDATE_DELAY_MS 1000 /* ms */ +#define FORCE_UPDATE false +#define DO_LOCKDOWN false + +#define MAX_IMAGE_NAME_LEN 256 +#define MAX_FIRMWARE_ID_LEN 10 + +#define LOCKDOWN_OFFSET 0xb0 +#define FW_IMAGE_OFFSET 0x100 + +#define BOOTLOADER_ID_OFFSET 0 +#define BLOCK_NUMBER_OFFSET 0 + +#define V5_PROPERTIES_OFFSET 2 +#define V5_BLOCK_SIZE_OFFSET 3 +#define V5_BLOCK_COUNT_OFFSET 5 +#define V5_BLOCK_DATA_OFFSET 2 + +#define V6_PROPERTIES_OFFSET 1 +#define V6_BLOCK_SIZE_OFFSET 2 +#define V6_BLOCK_COUNT_OFFSET 3 +#define V6_BLOCK_DATA_OFFSET 1 +#define V6_FLASH_COMMAND_OFFSET 2 +#define V6_FLASH_STATUS_OFFSET 3 + +#define LOCKDOWN_BLOCK_COUNT 5 + +#define REG_MAP (1 << 0) +#define UNLOCKED (1 << 1) +#define HAS_CONFIG_ID (1 << 2) +#define HAS_PERM_CONFIG (1 << 3) +#define HAS_BL_CONFIG (1 << 4) +#define HAS_DISP_CONFIG (1 << 5) +#define HAS_CTRL1 (1 << 6) + +#define UI_CONFIG_AREA 0x00 +#define PERM_CONFIG_AREA 0x01 +#define BL_CONFIG_AREA 0x02 +#define DISP_CONFIG_AREA 0x03 + +#define CMD_WRITE_FW_BLOCK 0x2 +#define CMD_ERASE_ALL 0x3 +#define CMD_WRITE_LOCKDOWN_BLOCK 0x4 +#define CMD_READ_CONFIG_BLOCK 0x5 +#define CMD_WRITE_CONFIG_BLOCK 0x6 +#define CMD_ERASE_CONFIG 0x7 +#define CMD_ERASE_BL_CONFIG 0x9 +#define CMD_ERASE_DISP_CONFIG 0xa +#define CMD_ENABLE_FLASH_PROG 0xf + +#define SLEEP_MODE_NORMAL (0x00) +#define SLEEP_MODE_SENSOR_SLEEP (0x01) +#define SLEEP_MODE_RESERVED0 (0x02) +#define SLEEP_MODE_RESERVED1 (0x03) + +#define ENABLE_WAIT_MS (1 * 1000) +#define WRITE_WAIT_MS (3 * 1000) +#define ERASE_WAIT_MS (5 * 1000) + +#define MIN_SLEEP_TIME_US 50 +#define MAX_SLEEP_TIME_US 100 + +#define SYN_FW_CFG_GREATER(fwu, config_id) \ + ((fwu->config_data[0] == 0) && (config_id[0] == 0) && \ + (fwu->config_data[1] == config_id[1]) && \ + (((fwu->config_data[2] == config_id[2]) && \ + (fwu->config_data[3] > config_id[3])) || \ + (fwu->config_data[2] > config_id[2]))) + +#define SYN_FW_CFG_EQUAL(fwu, config_id) \ + ((fwu->config_data[0] == 0) && (config_id[0] == 0) && \ + (fwu->config_data[1] == config_id[1]) && \ + (fwu->config_data[2] == config_id[2]) && \ + (fwu->config_data[3] == config_id[3])) + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static ssize_t fwu_sysfs_show_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t fwu_sysfs_store_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t fwu_sysfs_force_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_write_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_read_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_config_area_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_image_name_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_image_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_image_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_block_size_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_config_id_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_package_id_show(struct device *dev, + struct device_attribute *attr, char *buf); +#endif + +enum bl_version { + V5 = 5, + V6 = 6, +}; + +enum flash_area { + NONE, + UI_FIRMWARE, + CONFIG_AREA, +}; + +enum update_mode { + NORMAL = 1, + FORCE = 2, + LOCKDOWN = 8, +}; + +struct image_header { + /* 0x00 - 0x0f */ + unsigned char checksum[4]; + unsigned char reserved_04; + unsigned char reserved_05; + unsigned char options_firmware_id:1; + unsigned char options_contain_bootloader:1; + unsigned char options_reserved:6; + unsigned char bootloader_version; + unsigned char firmware_size[4]; + unsigned char config_size[4]; + /* 0x10 - 0x1f */ + unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE]; + unsigned char package_id[2]; + unsigned char package_id_revision[2]; + unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE]; + /* 0x20 - 0x2f */ + unsigned char reserved_20_2f[16]; + /* 0x30 - 0x3f */ + unsigned char ds_id[16]; + /* 0x40 - 0x4f */ + unsigned char ds_info[10]; + unsigned char reserved_4a_4f[6]; + /* 0x50 - 0x53 */ + unsigned char firmware_id[4]; +}; + +struct image_header_data { + bool contains_firmware_id; + unsigned int firmware_id; + unsigned int checksum; + unsigned int firmware_size; + unsigned int config_size; + unsigned char bootloader_version; + unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; + unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE]; +}; + +struct pdt_properties { + union { + struct { + unsigned char reserved_1:6; + unsigned char has_bsr:1; + unsigned char reserved_2:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f01_device_status { + union { + struct { + unsigned char status_code:4; + unsigned char reserved:2; + unsigned char flash_prog:1; + unsigned char unconfigured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f01_device_control { + union { + struct { + unsigned char sleep_mode:2; + unsigned char nosleep:1; + unsigned char reserved:2; + unsigned char charger_connected:1; + unsigned char report_rate:1; + unsigned char configured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_fwu_handle { + enum bl_version bl_version; + bool initialized; + bool program_enabled; + bool has_perm_config; + bool has_bl_config; + bool has_disp_config; + bool force_update; + bool in_flash_prog_mode; + bool do_lockdown; + unsigned int data_pos; + unsigned int image_size; + unsigned char *image_name; + unsigned char *ext_data_source; + unsigned char *read_config_buf; + unsigned char intr_mask; + unsigned char command; + unsigned char bootloader_id[2]; + unsigned char flash_properties; + unsigned char flash_status; + unsigned char productinfo1; + unsigned char productinfo2; + unsigned char properties_off; + unsigned char blk_size_off; + unsigned char blk_count_off; + unsigned char blk_data_off; + unsigned char flash_cmd_off; + unsigned char flash_status_off; + unsigned short block_size; + unsigned short fw_block_count; + unsigned short config_block_count; + unsigned short lockdown_block_count; + unsigned short perm_config_block_count; + unsigned short bl_config_block_count; + unsigned short disp_config_block_count; + unsigned short config_size; + unsigned short config_area; + char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; + const unsigned char *firmware_data; + const unsigned char *config_data; + const unsigned char *lockdown_data; + struct delayed_work fwu_work; + struct synaptics_rmi4_fn_desc f34_fd; + struct synaptics_rmi4_data *rmi4_data; +}; + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static struct bin_attribute dev_attr_data = { + .attr = { + .name = "data", + .mode = (S_IRUGO | S_IWUSR), + }, + .size = 0, + .read = fwu_sysfs_show_image, + .write = fwu_sysfs_store_image, +}; +#endif + + +static struct device_attribute attrs[] = { +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS + __ATTR(force_update_fw, S_IWUSR | S_IWGRP, + NULL, + fwu_sysfs_force_reflash_store), + __ATTR(update_fw, S_IWUSR | S_IWGRP, + NULL, + fwu_sysfs_do_reflash_store), + __ATTR(writeconfig, S_IWUSR | S_IWGRP, + NULL, + fwu_sysfs_write_config_store), + __ATTR(readconfig, S_IWUSR | S_IWGRP, + NULL, + fwu_sysfs_read_config_store), + __ATTR(configarea, S_IWUSR | S_IWGRP, + NULL, + fwu_sysfs_config_area_store), + __ATTR(fw_name, S_IRUGO | S_IWUSR | S_IWGRP, + fwu_sysfs_image_name_show, + fwu_sysfs_image_name_store), + __ATTR(imagesize, S_IWUSR | S_IWGRP, + NULL, + fwu_sysfs_image_size_store), + __ATTR(blocksize, S_IRUGO, + fwu_sysfs_block_size_show, + synaptics_rmi4_store_error), + __ATTR(fwblockcount, S_IRUGO, + fwu_sysfs_firmware_block_count_show, + synaptics_rmi4_store_error), + __ATTR(configblockcount, S_IRUGO, + fwu_sysfs_configuration_block_count_show, + synaptics_rmi4_store_error), + __ATTR(permconfigblockcount, S_IRUGO, + fwu_sysfs_perm_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(blconfigblockcount, S_IRUGO, + fwu_sysfs_bl_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(dispconfigblockcount, S_IRUGO, + fwu_sysfs_disp_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(config_id, S_IRUGO, + fwu_sysfs_config_id_show, + synaptics_rmi4_store_error), + __ATTR(package_id, S_IRUGO, + fwu_sysfs_package_id_show, + synaptics_rmi4_store_error), +#endif +}; + +static struct synaptics_rmi4_fwu_handle *fwu; + +DECLARE_COMPLETION(fwu_dsx_remove_complete); +DEFINE_MUTEX(dsx_fwu_sysfs_mutex); + +static unsigned int extract_uint_le(const unsigned char *ptr) +{ + return (unsigned int)ptr[0] + + (unsigned int)ptr[1] * 0x100 + + (unsigned int)ptr[2] * 0x10000 + + (unsigned int)ptr[3] * 0x1000000; +} + +static void parse_header(struct image_header_data *header, + const unsigned char *fw_image) +{ + struct image_header *data = (struct image_header *)fw_image; + + header->checksum = extract_uint_le(data->checksum); + + header->bootloader_version = data->bootloader_version; + + header->firmware_size = extract_uint_le(data->firmware_size); + + header->config_size = extract_uint_le(data->config_size); + + memcpy(header->product_id, data->product_id, sizeof(data->product_id)); + header->product_id[sizeof(data->product_id)] = 0; + + memcpy(header->product_info, data->product_info, + sizeof(data->product_info)); + + header->contains_firmware_id = data->options_firmware_id; + if (header->contains_firmware_id) + header->firmware_id = extract_uint_le(data->firmware_id); + + return; +} + +static int fwu_read_f01_device_status(struct f01_device_status *status) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status->data, + sizeof(status->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F01 device status\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_read_f34_queries(void) +{ + int retval; + unsigned char count; + unsigned char buf[10]; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.query_base_addr + BOOTLOADER_ID_OFFSET, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read bootloader ID\n", + __func__); + return retval; + } + + if (fwu->bootloader_id[1] == '5') { + fwu->bl_version = V5; + } else if (fwu->bootloader_id[1] == '6') { + fwu->bl_version = V6; + } else { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Unrecognized bootloader version\n", + __func__); + return -EINVAL; + } + + if (fwu->bl_version == V5) { + fwu->properties_off = V5_PROPERTIES_OFFSET; + fwu->blk_size_off = V5_BLOCK_SIZE_OFFSET; + fwu->blk_count_off = V5_BLOCK_COUNT_OFFSET; + fwu->blk_data_off = V5_BLOCK_DATA_OFFSET; + } else if (fwu->bl_version == V6) { + fwu->properties_off = V6_PROPERTIES_OFFSET; + fwu->blk_size_off = V6_BLOCK_SIZE_OFFSET; + fwu->blk_count_off = V6_BLOCK_COUNT_OFFSET; + fwu->blk_data_off = V6_BLOCK_DATA_OFFSET; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.query_base_addr + fwu->properties_off, + &fwu->flash_properties, + sizeof(fwu->flash_properties)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash properties\n", + __func__); + return retval; + } + + count = 4; + + if (fwu->flash_properties & HAS_PERM_CONFIG) { + fwu->has_perm_config = 1; + count += 2; + } + + if (fwu->flash_properties & HAS_BL_CONFIG) { + fwu->has_bl_config = 1; + count += 2; + } + + if (fwu->flash_properties & HAS_DISP_CONFIG) { + fwu->has_disp_config = 1; + count += 2; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.query_base_addr + fwu->blk_size_off, + buf, + 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read block size info\n", + __func__); + return retval; + } + + batohs(&fwu->block_size, &(buf[0])); + + if (fwu->bl_version == V5) { + fwu->flash_cmd_off = fwu->blk_data_off + fwu->block_size; + fwu->flash_status_off = fwu->flash_cmd_off; + } else if (fwu->bl_version == V6) { + fwu->flash_cmd_off = V6_FLASH_COMMAND_OFFSET; + fwu->flash_status_off = V6_FLASH_STATUS_OFFSET; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.query_base_addr + fwu->blk_count_off, + buf, + count); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read block count info\n", + __func__); + return retval; + } + + batohs(&fwu->fw_block_count, &(buf[0])); + batohs(&fwu->config_block_count, &(buf[2])); + + count = 4; + + if (fwu->has_perm_config) { + batohs(&fwu->perm_config_block_count, &(buf[count])); + count += 2; + } + + if (fwu->has_bl_config) { + batohs(&fwu->bl_config_block_count, &(buf[count])); + count += 2; + } + + if (fwu->has_disp_config) + batohs(&fwu->disp_config_block_count, &(buf[count])); + + return 0; +} + +static int fwu_read_f34_flash_status(void) +{ + int retval; + unsigned char status; + unsigned char command; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->flash_status_off, + &status, + sizeof(status)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash status\n", + __func__); + return retval; + } + + fwu->program_enabled = status >> 7; + + if (fwu->bl_version == V5) + fwu->flash_status = (status >> 4) & MASK_3BIT; + else if (fwu->bl_version == V6) + fwu->flash_status = status & MASK_3BIT; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->flash_cmd_off, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash command\n", + __func__); + return retval; + } + + fwu->command = command & MASK_4BIT; + + return 0; +} + +static int fwu_write_f34_command(unsigned char cmd) +{ + int retval; + unsigned char command = cmd & MASK_4BIT; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + fwu->command = cmd; + + retval = synaptics_rmi4_reg_write(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->flash_cmd_off, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write command 0x%02x\n", + __func__, command); + return retval; + } + + return 0; +} + +static int fwu_wait_for_idle(int timeout_ms) +{ + int count = 0; + int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + do { + usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US); + + count++; + if (count == timeout_count) + fwu_read_f34_flash_status(); + + if ((fwu->command == 0x00) && (fwu->flash_status == 0x00)) + return 0; + } while (count < timeout_count); + + dev_err(rmi4_data->pdev->dev.parent, + "%s: Timed out waiting for idle status\n", + __func__); + + return -ETIMEDOUT; +} + +static enum flash_area fwu_go_nogo(struct image_header_data *header) +{ + int retval; + enum flash_area flash_area = NONE; + unsigned char index = 0; + unsigned char config_id[4]; + unsigned int device_fw_id; + unsigned long image_fw_id; + char *strptr; + char *firmware_id; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (fwu->force_update) { + flash_area = UI_FIRMWARE; + goto exit; + } + + /* Update both UI and config if device is in bootloader mode */ + if (fwu->in_flash_prog_mode) { + flash_area = UI_FIRMWARE; + goto exit; + } + + /* Get device firmware ID */ + device_fw_id = rmi4_data->firmware_id; + dev_info(rmi4_data->pdev->dev.parent, + "%s: Device firmware ID = %d\n", + __func__, device_fw_id); + + /* Get image firmware ID */ + if (header->contains_firmware_id) { + image_fw_id = header->firmware_id; + } else { + strptr = strnstr(fwu->image_name, "PR", + sizeof(fwu->image_name)); + if (!strptr) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: No valid PR number (PRxxxxxxx) found in image file name (%s)\n", + __func__, fwu->image_name); + flash_area = NONE; + goto exit; + } + + strptr += 2; + firmware_id = kzalloc(MAX_FIRMWARE_ID_LEN, GFP_KERNEL); + if (!firmware_id) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for firmware id\n", + __func__); + flash_area = NONE; + goto exit; + } + + while (strptr[index] >= '0' && strptr[index] <= '9') { + firmware_id[index] = strptr[index]; + index++; + } + + retval = sstrtoul(firmware_id, 10, &image_fw_id); + kfree(firmware_id); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to obtain image firmware ID\n", + __func__); + flash_area = NONE; + goto exit; + } + } + dev_info(rmi4_data->pdev->dev.parent, + "%s: Image firmware ID = %d\n", + __func__, (unsigned int)image_fw_id); + + if (!rmi4_data->hw_if->board_data->bypass_packrat_id_check) { + if (image_fw_id > device_fw_id) { + flash_area = UI_FIRMWARE; + goto exit; + } else if (image_fw_id < device_fw_id) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Image firmware ID older than device firmware ID\n", + __func__); + flash_area = NONE; + goto exit; + } + } + + /* Get device config ID */ + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.ctrl_base_addr, + config_id, + sizeof(config_id)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read device config ID\n", + __func__); + flash_area = NONE; + goto exit; + } + dev_info(rmi4_data->pdev->dev.parent, + "%s: Device config ID = 0x%02x 0x%02x 0x%02x 0x%02x\n", + __func__, + config_id[0], + config_id[1], + config_id[2], + config_id[3]); + + /* Get image config ID */ + dev_info(rmi4_data->pdev->dev.parent, + "%s: Image config ID = 0x%02x 0x%02x 0x%02x 0x%02x\n", + __func__, + fwu->config_data[0], + fwu->config_data[1], + fwu->config_data[2], + fwu->config_data[3]); + + if (SYN_FW_CFG_GREATER(fwu, config_id)) { + if (image_fw_id > device_fw_id) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Image file has higher packrat id than device\n", + __func__); + /* + * If packrat id of the firmware file is greater than + * the firmware build id in the device(same as packrat + * id), then both firmware and config area need to be + * upgraded. + */ + flash_area = UI_FIRMWARE; + goto exit; + } else if (image_fw_id == device_fw_id) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Image file has equal packrat id as is in device\n", + __func__); + /* + * If packrat id of the firmware file equals the + * firmware build id in the device(same as packrat id), + * then only config area needs to be upgraded. + */ + flash_area = CONFIG_AREA; + goto exit; + } else if (image_fw_id < device_fw_id) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Image file has lesser packrat id than device, even though config id is greater\n", + __func__); + /* + * If packrat id of the firmware file is lesser than + * the firmware build id in the device(same as packrat + * id), then it is treated as an error + */ + flash_area = NONE; + goto exit; + } + } else if (SYN_FW_CFG_EQUAL(fwu, config_id)) { + if (image_fw_id > device_fw_id) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Image file has higher packrat id than device, though config id is equal\n", + __func__); + /* + * If config id of the firmware file equals the config + * id in the device, but packrat id of the firmware is + * greater than the firmware build id in the device + * (same as packrat id), then both firmware and config + * area need to be upgraded. + */ + flash_area = UI_FIRMWARE; + goto exit; + } else if (image_fw_id == device_fw_id) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Image file has equal packrat id and config id as are in device\n", + __func__); + /* + * If config id of the firmware file equals the config + * id in the device and if packrat id of the firmware + * is also equal to the firmware build id in the device + * (same as packrat id), then no update is needed. + */ + flash_area = NONE; + goto exit; + } else if (image_fw_id < device_fw_id) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Image file has lesser packrat id than device, though config id is equal\n", + __func__); + /* + * If config id of the firmware file equals the config + * id in the device, but the packrat id of the firmware + * file is lesser than the firmware build id in the + * device(same as packrat id), then it is treated as an + * error and no update is needed. + */ + flash_area = NONE; + goto exit; + } + } + + flash_area = NONE; + +exit: + if (flash_area == NONE) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: No need to do reflash\n", + __func__); + } else { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Updating %s\n", + __func__, + flash_area == UI_FIRMWARE ? + "UI firmware" : + "config only"); + } + + return flash_area; +} + +static int fwu_scan_pdt(void) +{ + int retval; + unsigned char ii; + unsigned char intr_count = 0; + unsigned char intr_off; + unsigned char intr_src; + unsigned short addr; + bool f01found = false; + bool f34found = false; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + if (rmi_fd.fn_number) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Found F%02x\n", + __func__, rmi_fd.fn_number); + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F01: + f01found = true; + + rmi4_data->f01_query_base_addr = + rmi_fd.query_base_addr; + rmi4_data->f01_ctrl_base_addr = + rmi_fd.ctrl_base_addr; + rmi4_data->f01_data_base_addr = + rmi_fd.data_base_addr; + rmi4_data->f01_cmd_base_addr = + rmi_fd.cmd_base_addr; + break; + case SYNAPTICS_RMI4_F34: + f34found = true; + fwu->f34_fd.query_base_addr = + rmi_fd.query_base_addr; + fwu->f34_fd.ctrl_base_addr = + rmi_fd.ctrl_base_addr; + fwu->f34_fd.data_base_addr = + rmi_fd.data_base_addr; + + fwu->intr_mask = 0; + intr_src = rmi_fd.intr_src_count; + intr_off = intr_count % 8; + for (ii = intr_off; + ii < ((intr_src & MASK_3BIT) + + intr_off); + ii++) { + fwu->intr_mask |= 1 << ii; + } + break; + } + } else { + break; + } + + intr_count += (rmi_fd.intr_src_count & MASK_3BIT); + } + + if (!f01found || !f34found) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find both F01 and F34\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char block_offset[] = {0, 0}; + unsigned short block_num; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + unsigned int progress; + unsigned char command_str[10]; + + switch (command) { + case CMD_WRITE_CONFIG_BLOCK: + progress = 10; + strlcpy(command_str, "config", 10); + break; + case CMD_WRITE_FW_BLOCK: + progress = 100; + strlcpy(command_str, "firmware", 10); + break; + case CMD_WRITE_LOCKDOWN_BLOCK: + progress = 1; + strlcpy(command_str, "lockdown", 10); + break; + default: + progress = 1; + strlcpy(command_str, "unknown", 10); + break; + } + + block_offset[1] |= (fwu->config_area << 5); + + retval = synaptics_rmi4_reg_write(rmi4_data, + fwu->f34_fd.data_base_addr + BLOCK_NUMBER_OFFSET, + block_offset, + sizeof(block_offset)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write to block number registers\n", + __func__); + return retval; + } + + for (block_num = 0; block_num < block_cnt; block_num++) { + if (block_num % progress == 0) + dev_info(rmi4_data->pdev->dev.parent, + "%s: update %s %3d / %3d\n", + __func__, command_str, block_num, block_cnt); + + retval = synaptics_rmi4_reg_write(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->blk_data_off, + block_ptr, + fwu->block_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write block data (block %d)\n", + __func__, block_num); + return retval; + } + + retval = fwu_write_f34_command(command); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write command for block %d\n", + __func__, block_num); + return retval; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to wait for idle status (block %d)\n", + __func__, block_num); + return retval; + } + + block_ptr += fwu->block_size; + } + + dev_info(rmi4_data->pdev->dev.parent, + "updated %d/%d blocks\n", block_num, block_cnt); + + return 0; +} + +static int fwu_write_firmware(void) +{ + return fwu_write_blocks((unsigned char *)fwu->firmware_data, + fwu->fw_block_count, CMD_WRITE_FW_BLOCK); +} + +static int fwu_write_configuration(void) +{ + return fwu_write_blocks((unsigned char *)fwu->config_data, + fwu->config_block_count, CMD_WRITE_CONFIG_BLOCK); +} + +static int fwu_write_lockdown(void) +{ + return fwu_write_blocks((unsigned char *)fwu->lockdown_data, + fwu->lockdown_block_count, CMD_WRITE_LOCKDOWN_BLOCK); +} + +static int fwu_write_bootloader_id(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = synaptics_rmi4_reg_write(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->blk_data_off, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write bootloader ID\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_enter_flash_prog(void) +{ + int retval; + struct f01_device_status f01_device_status; + struct f01_device_control f01_device_control; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_write_bootloader_id(); + if (retval < 0) + return retval; + + retval = fwu_write_f34_command(CMD_ENABLE_FLASH_PROG); + if (retval < 0) + return retval; + + retval = fwu_wait_for_idle(ENABLE_WAIT_MS); + if (retval < 0) + return retval; + + if (!fwu->program_enabled) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Program enabled bit not set\n", + __func__); + return -EINVAL; + } + + retval = fwu_scan_pdt(); + if (retval < 0) + return retval; + + retval = fwu_read_f01_device_status(&f01_device_status); + if (retval < 0) + return retval; + + if (!f01_device_status.flash_prog) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not in flash prog mode\n", + __func__); + return -EINVAL; + } + + retval = fwu_read_f34_queries(); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F01 device control\n", + __func__); + return retval; + } + + f01_device_control.nosleep = true; + f01_device_control.sleep_mode = SLEEP_MODE_NORMAL; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write F01 device control\n", + __func__); + return retval; + } + + return retval; +} + +static int fwu_do_reflash(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_enter_flash_prog(); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Entered flash prog mode\n", + __func__); + + retval = fwu_write_bootloader_id(); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Bootloader ID written\n", + __func__); + + retval = fwu_write_f34_command(CMD_ERASE_ALL); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Erase all command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Idle status detected\n", + __func__); + + if (fwu->firmware_data) { + retval = fwu_write_firmware(); + if (retval < 0) + return retval; + pr_notice("%s: Firmware programmed\n", __func__); + } + + if (fwu->config_data) { + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + pr_notice("%s: Configuration programmed\n", __func__); + } + + return retval; +} + +static int fwu_do_write_config(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_enter_flash_prog(); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Entered flash prog mode\n", + __func__); + + if (fwu->config_area == PERM_CONFIG_AREA) { + fwu->config_block_count = fwu->perm_config_block_count; + goto write_config; + } + + retval = fwu_write_bootloader_id(); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Bootloader ID written\n", + __func__); + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_CONFIG); + break; + case BL_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_BL_CONFIG); + fwu->config_block_count = fwu->bl_config_block_count; + break; + case DISP_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_DISP_CONFIG); + fwu->config_block_count = fwu->disp_config_block_count; + break; + } + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Idle status detected\n", + __func__); + +write_config: + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + + pr_notice("%s: Config written\n", __func__); + + return retval; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static int fwu_start_write_config(void) +{ + int retval; + unsigned short block_count; + struct image_header_data header; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + block_count = fwu->config_block_count; + break; + case PERM_CONFIG_AREA: + if (!fwu->has_perm_config) + return -EINVAL; + block_count = fwu->perm_config_block_count; + break; + case BL_CONFIG_AREA: + if (!fwu->has_bl_config) + return -EINVAL; + block_count = fwu->bl_config_block_count; + break; + case DISP_CONFIG_AREA: + if (!fwu->has_disp_config) + return -EINVAL; + block_count = fwu->disp_config_block_count; + break; + default: + return -EINVAL; + } + + if (fwu->ext_data_source) + fwu->config_data = fwu->ext_data_source; + else + return -EINVAL; + + fwu->config_size = fwu->block_size * block_count; + + /* Jump to the config area if given a packrat image */ + if ((fwu->config_area == UI_CONFIG_AREA) && + (fwu->config_size != fwu->image_size)) { + parse_header(&header, fwu->ext_data_source); + + if (header.config_size) { + fwu->config_data = fwu->ext_data_source + + FW_IMAGE_OFFSET + + header.firmware_size; + } else { + return -EINVAL; + } + } + + pr_notice("%s: Start of write config process\n", __func__); + + retval = fwu_do_write_config(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write config\n", + __func__); + } + + rmi4_data->reset_device(rmi4_data); + + pr_notice("%s: End of write config process\n", __func__); + + return retval; +} + +static int fwu_do_read_config(void) +{ + int retval; + unsigned char block_offset[] = {0, 0}; + unsigned short block_num; + unsigned short block_count; + unsigned short index = 0; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_enter_flash_prog(); + if (retval < 0) + goto exit; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Entered flash prog mode\n", + __func__); + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + block_count = fwu->config_block_count; + break; + case PERM_CONFIG_AREA: + if (!fwu->has_perm_config) { + retval = -EINVAL; + goto exit; + } + block_count = fwu->perm_config_block_count; + break; + case BL_CONFIG_AREA: + if (!fwu->has_bl_config) { + retval = -EINVAL; + goto exit; + } + block_count = fwu->bl_config_block_count; + break; + case DISP_CONFIG_AREA: + if (!fwu->has_disp_config) { + retval = -EINVAL; + goto exit; + } + block_count = fwu->disp_config_block_count; + break; + default: + retval = -EINVAL; + goto exit; + } + + fwu->config_size = fwu->block_size * block_count; + + kfree(fwu->read_config_buf); + fwu->read_config_buf = kzalloc(fwu->config_size, GFP_KERNEL); + if (!fwu->read_config_buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc memory for config buffer\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + block_offset[1] |= (fwu->config_area << 5); + + retval = synaptics_rmi4_reg_write(rmi4_data, + fwu->f34_fd.data_base_addr + BLOCK_NUMBER_OFFSET, + block_offset, + sizeof(block_offset)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write to block number registers\n", + __func__); + goto exit; + } + + for (block_num = 0; block_num < block_count; block_num++) { + retval = fwu_write_f34_command(CMD_READ_CONFIG_BLOCK); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write read config command\n", + __func__); + goto exit; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to wait for idle status\n", + __func__); + goto exit; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->blk_data_off, + &fwu->read_config_buf[index], + fwu->block_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read block data (block %d)\n", + __func__, block_num); + goto exit; + } + + index += fwu->block_size; + } + +exit: + rmi4_data->reset_device(rmi4_data); + + return retval; +} +#endif + +static int fwu_do_lockdown(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_enter_flash_prog(); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.query_base_addr + fwu->properties_off, + &fwu->flash_properties, + sizeof(fwu->flash_properties)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash properties\n", + __func__); + return retval; + } + + if ((fwu->flash_properties & UNLOCKED) == 0) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Device already locked down\n", + __func__); + return retval; + } + + retval = fwu_write_lockdown(); + if (retval < 0) + return retval; + + pr_notice("%s: Lockdown programmed\n", __func__); + + return retval; +} + +static int fwu_start_reflash(void) +{ + int retval = 0; + enum flash_area flash_area; + struct image_header_data header; + struct f01_device_status f01_device_status; + const unsigned char *fw_image; + const struct firmware *fw_entry = NULL; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (rmi4_data->sensor_sleep) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Sensor sleeping\n", + __func__); + return -ENODEV; + } + + rmi4_data->stay_awake = true; + + pr_notice("%s: Start of reflash process\n", __func__); + + if (fwu->ext_data_source) { + fw_image = fwu->ext_data_source; + } else { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Requesting firmware image %s\n", + __func__, fwu->image_name); + + retval = request_firmware(&fw_entry, fwu->image_name, + rmi4_data->pdev->dev.parent); + if (retval != 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Firmware image %s not available\n", + __func__, fwu->image_name); + rmi4_data->stay_awake = false; + return retval; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Firmware image size = %zu\n", + __func__, fw_entry->size); + + fw_image = fw_entry->data; + } + + parse_header(&header, fw_image); + + if (fwu->bl_version != header.bootloader_version) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Bootloader version mismatch\n", + __func__); + retval = -EINVAL; + goto exit; + } + + retval = fwu_read_f01_device_status(&f01_device_status); + if (retval < 0) + goto exit; + + if (f01_device_status.flash_prog) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: In flash prog mode\n", + __func__); + fwu->in_flash_prog_mode = true; + } else { + fwu->in_flash_prog_mode = false; + } + + if (fwu->do_lockdown) { + switch (fwu->bl_version) { + case V5: + case V6: + fwu->lockdown_data = fw_image + LOCKDOWN_OFFSET; + fwu->lockdown_block_count = LOCKDOWN_BLOCK_COUNT; + retval = fwu_do_lockdown(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do lockdown\n", + __func__); + } + default: + break; + } + } + + if (header.firmware_size) + fwu->firmware_data = fw_image + FW_IMAGE_OFFSET; + if (header.config_size) { + fwu->config_data = fw_image + FW_IMAGE_OFFSET + + header.firmware_size; + } + + flash_area = fwu_go_nogo(&header); + switch (flash_area) { + case UI_FIRMWARE: + retval = fwu_do_reflash(); + break; + case CONFIG_AREA: + retval = fwu_do_write_config(); + break; + case NONE: + default: + goto exit; + } + + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do reflash\n", + __func__); + } + +exit: + rmi4_data->reset_device(rmi4_data); + + if (fw_entry) + release_firmware(fw_entry); + + pr_notice("%s: End of reflash process\n", __func__); + + rmi4_data->stay_awake = false; + + return retval; +} + +int synaptics_dsx_fw_updater(unsigned char *fw_data) +{ + int retval; + + if (!fwu) + return -ENODEV; + + if (!fwu->initialized) + return -ENODEV; + + fwu->rmi4_data->fw_updating = true; + if (fwu->rmi4_data->suspended == true) { + fwu->rmi4_data->fw_updating = false; + dev_err(fwu->rmi4_data->pdev->dev.parent, + "Cannot start fw upgrade: Device is in suspend\n"); + return -EBUSY; + } + + fwu->ext_data_source = fw_data; + fwu->config_area = UI_CONFIG_AREA; + + retval = fwu_start_reflash(); + + fwu->rmi4_data->fw_updating = false; + + return retval; +} +EXPORT_SYMBOL(synaptics_dsx_fw_updater); + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static ssize_t fwu_sysfs_show_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + ssize_t retval; + + if (!mutex_trylock(&dsx_fwu_sysfs_mutex)) + return -EBUSY; + + if (count < fwu->config_size) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not enough space (%zu bytes) in buffer\n", + __func__, count); + retval = -EINVAL; + goto show_image_exit; + } + + memcpy(buf, fwu->read_config_buf, fwu->config_size); + retval = fwu->config_size; +show_image_exit: + mutex_unlock(&dsx_fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_store_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + ssize_t retval; + + if (!mutex_trylock(&dsx_fwu_sysfs_mutex)) + return -EBUSY; + + if (count > (fwu->image_size - fwu->data_pos)) { + dev_err(fwu->rmi4_data->pdev->dev.parent, + "%s: Not enough space in buffer\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) { + dev_err(fwu->rmi4_data->pdev->dev.parent, + "%s: Need to set imagesize\n", + __func__); + retval = -EINVAL; + goto exit; + } + + memcpy((void *)(&fwu->ext_data_source[fwu->data_pos]), + (const void *)buf, + count); + + fwu->data_pos += count; + +exit: + mutex_unlock(&dsx_fwu_sysfs_mutex); + return count; +} + +static ssize_t fwu_sysfs_force_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + ssize_t retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&dsx_fwu_sysfs_mutex)) + return -EBUSY; + + if (sscanf(buf, "%u", &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (input != 1) { + retval = -EINVAL; + goto exit; + } + + if (LOCKDOWN) + fwu->do_lockdown = true; + + fwu->force_update = true; + retval = synaptics_dsx_fw_updater(fwu->ext_data_source); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do reflash\n", + __func__); + goto exit; + } + + retval = count; +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->force_update = FORCE_UPDATE; + fwu->do_lockdown = DO_LOCKDOWN; + fwu->data_pos = 0; + fwu->image_size = 0; + mutex_unlock(&dsx_fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&dsx_fwu_sysfs_mutex)) + return -EBUSY; + + if (sscanf(buf, "%u", &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (input & LOCKDOWN) { + fwu->do_lockdown = true; + input &= ~LOCKDOWN; + } + + if ((input != NORMAL) && (input != FORCE)) { + retval = -EINVAL; + goto exit; + } + + if (input == FORCE) + fwu->force_update = true; + + retval = synaptics_dsx_fw_updater(fwu->ext_data_source); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do reflash\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->force_update = FORCE_UPDATE; + fwu->do_lockdown = DO_LOCKDOWN; + fwu->data_pos = 0; + fwu->image_size = 0; + mutex_unlock(&dsx_fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_write_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&dsx_fwu_sysfs_mutex)) + return -EBUSY; + + if (sscanf(buf, "%u", &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (input != 1) { + retval = -EINVAL; + goto exit; + } + + retval = fwu_start_write_config(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write config\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->data_pos = 0; + fwu->image_size = 0; + mutex_unlock(&dsx_fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_read_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + if (!mutex_trylock(&dsx_fwu_sysfs_mutex)) + return -EBUSY; + retval = fwu_do_read_config(); + mutex_unlock(&dsx_fwu_sysfs_mutex); + + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read config\n", + __func__); + return retval; + } + + return count; +} + +static ssize_t fwu_sysfs_config_area_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long config_area; + + retval = sstrtoul(buf, 10, &config_area); + if (retval) + return retval; + + if (!mutex_trylock(&dsx_fwu_sysfs_mutex)) + return -EBUSY; + fwu->config_area = config_area; + mutex_unlock(&dsx_fwu_sysfs_mutex); + + return count; +} + +static ssize_t fwu_sysfs_image_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t retval; + + if (!mutex_trylock(&dsx_fwu_sysfs_mutex)) + return -EBUSY; + if (strnlen(fwu->rmi4_data->fw_name, SYNA_FW_NAME_MAX_LEN) > 0) + retval = snprintf(buf, PAGE_SIZE, "%s\n", + fwu->rmi4_data->fw_name); + else + retval = snprintf(buf, PAGE_SIZE, "No firmware name given\n"); + mutex_unlock(&dsx_fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_image_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + ssize_t retval; + + if (!mutex_trylock(&dsx_fwu_sysfs_mutex)) + return -EBUSY; + retval = sscanf(buf, "%49s", fwu->image_name); + mutex_unlock(&dsx_fwu_sysfs_mutex); + + if (retval != 1) + return -EINVAL; + + return count; +} + +static ssize_t fwu_sysfs_image_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long size; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&dsx_fwu_sysfs_mutex)) + return -EBUSY; + + retval = sstrtoul(buf, 10, &size); + if (retval) + goto exit; + + fwu->image_size = size; + fwu->data_pos = 0; + + kfree(fwu->ext_data_source); + fwu->ext_data_source = kzalloc(fwu->image_size, GFP_KERNEL); + if (!fwu->ext_data_source) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for image data\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + retval = count; +exit: + mutex_unlock(&dsx_fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_block_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->block_size); +} + +static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->fw_block_count); +} + +static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->config_block_count); +} + +static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->perm_config_block_count); +} + +static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->bl_config_block_count); +} + +static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->disp_config_block_count); +} + +static ssize_t fwu_sysfs_config_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + unsigned char config_id[4]; + int retval; + + /* device config id */ + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.ctrl_base_addr, + config_id, + sizeof(config_id)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read device config ID\n", + __func__); + return retval; + } + + return snprintf(buf, PAGE_SIZE, "%d.%d.%d.%d\n", + config_id[0], config_id[1], config_id[2], config_id[3]); +} + +static ssize_t fwu_sysfs_package_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned char package_id[PACKAGE_ID_SIZE]; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + /* read device package id */ + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_query_base_addr + F01_PACKAGE_ID_OFFSET, + package_id, + sizeof(package_id)); + + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read device package ID\n", + __func__); + return retval; + } + + return snprintf(buf, PAGE_SIZE, "%d rev %d\n", + (package_id[1] << 8) | package_id[0], + (package_id[3] << 8) | package_id[2]); +} +#endif + +static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + if (!fwu) + return; + + if (fwu->intr_mask & intr_mask) + fwu_read_f34_flash_status(); + + return; +} + +static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char attr_count; + struct pdt_properties pdt_props; + + fwu = kzalloc(sizeof(*fwu), GFP_KERNEL); + if (!fwu) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for fwu\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + fwu->image_name = rmi4_data->fw_name; + + fwu->rmi4_data = rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + PDT_PROPS, + pdt_props.data, + sizeof(pdt_props.data)); + if (retval < 0) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Failed to read PDT properties, assuming 0x00\n", + __func__); + } else if (pdt_props.has_bsr) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Reflash for LTS not currently supported\n", + __func__); + retval = -ENODEV; + goto exit_free_fwu; + } + + retval = fwu_scan_pdt(); + if (retval < 0) + goto exit_free_fwu; + + fwu->productinfo1 = rmi4_data->rmi4_mod_info.product_info[0]; + fwu->productinfo2 = rmi4_data->rmi4_mod_info.product_info[1]; + memcpy(fwu->product_id, rmi4_data->rmi4_mod_info.product_id_string, + SYNAPTICS_RMI4_PRODUCT_ID_SIZE); + fwu->product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE] = 0; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: F01 product info: 0x%04x 0x%04x\n", + __func__, fwu->productinfo1, fwu->productinfo2); + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: F01 product ID: %s\n", + __func__, fwu->product_id); + + retval = fwu_read_f34_queries(); + if (retval < 0) + goto exit_free_fwu; + + fwu->force_update = FORCE_UPDATE; + fwu->do_lockdown = DO_LOCKDOWN; + fwu->initialized = true; + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS + retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj, + &dev_attr_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs bin file\n", + __func__); + goto exit_free_fwu; + } +#endif + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto exit_remove_attrs; + } + } + + return 0; + +exit_remove_attrs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS + sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data); +#endif + +exit_free_fwu: + kfree(fwu); + fwu = NULL; + +exit: + return retval; +} + +static void synaptics_rmi4_fwu_remove(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char attr_count; + + if (!fwu) + goto exit; + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS + sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data); +#endif + + kfree(fwu->read_config_buf); + kfree(fwu); + fwu = NULL; + +exit: + complete(&fwu_dsx_remove_complete); + + return; +} + +static struct synaptics_rmi4_exp_fn fwu_module = { + .fn_type = RMI_FW_UPDATER, + .init = synaptics_rmi4_fwu_init, + .remove = synaptics_rmi4_fwu_remove, + .reset = NULL, + .reinit = NULL, + .early_suspend = NULL, + .suspend = NULL, + .resume = NULL, + .late_resume = NULL, + .attn = synaptics_rmi4_fwu_attn, +}; + +static int __init rmi4_fw_update_module_init(void) +{ + synaptics_rmi4_dsx_new_function(&fwu_module, true); + + return 0; +} + +static void __exit rmi4_fw_update_module_exit(void) +{ + synaptics_rmi4_dsx_new_function(&fwu_module, false); + + wait_for_completion(&fwu_dsx_remove_complete); + + return; +} + +module_init(rmi4_fw_update_module_init); +module_exit(rmi4_fw_update_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX FW Update Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c new file mode 100644 index 000000000000..0b3fbaf9f462 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c @@ -0,0 +1,530 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * + * Linux foundation chooses to take subject only to the GPLv2 license terms, + * and distributes only under these terms. + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com> + * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com> + * + * 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/input/synaptics_dsx_v2.h> +#include "synaptics_dsx_core.h" +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#if defined(CONFIG_SECURE_TOUCH) +#include <linux/pm_runtime.h> +#endif + +#define SYN_I2C_RETRY_TIMES 10 +#define RESET_DELAY 100 +#define DSX_COORDS_ARR_SIZE 4 + +static int synaptics_rmi4_i2c_set_page(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr) +{ + int retval; + unsigned char retry; + unsigned char buf[PAGE_SELECT_LEN]; + unsigned char page; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + + page = ((addr >> 8) & MASK_8BIT); + if (page != rmi4_data->current_page) { + buf[0] = MASK_8BIT; + buf[1] = page; + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + retval = i2c_master_send(i2c, buf, PAGE_SELECT_LEN); + if (retval != PAGE_SELECT_LEN) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + } else { + rmi4_data->current_page = page; + break; + } + } + } else { + retval = PAGE_SELECT_LEN; + } + + return retval; +} + +static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned short length) +{ + int retval; + unsigned char retry; + unsigned char buf; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + struct i2c_msg msg[] = { + { + .addr = i2c->addr, + .flags = 0, + .len = 1, + .buf = &buf, + }, + { + .addr = i2c->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + }, + }; + + buf = addr & MASK_8BIT; + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) { + retval = -EIO; + goto exit; + } + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(i2c->adapter, msg, 2) == 2) { + retval = length; + break; + } + dev_err(rmi4_data->pdev->dev.parent, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + } + + if (retry == SYN_I2C_RETRY_TIMES) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: I2C read over retry limit\n", + __func__); + retval = -EIO; + } + +exit: + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + return retval; +} + +static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned short length) +{ + int retval; + unsigned char retry; + unsigned char buf[length + 1]; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + struct i2c_msg msg[] = { + { + .addr = i2c->addr, + .flags = 0, + .len = length + 1, + .buf = buf, + } + }; + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) { + retval = -EIO; + goto exit; + } + + buf[0] = addr & MASK_8BIT; + memcpy(&buf[1], &data[0], length); + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(i2c->adapter, msg, 1) == 1) { + retval = length; + break; + } + dev_err(rmi4_data->pdev->dev.parent, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + } + + if (retry == SYN_I2C_RETRY_TIMES) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: I2C write over retry limit\n", + __func__); + retval = -EIO; + } + +exit: + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + return retval; +} + +#if defined(CONFIG_SECURE_TOUCH) +static int synaptics_rmi4_clk_prepare_enable( + struct synaptics_rmi4_data *rmi4_data) +{ + int ret; + ret = clk_prepare_enable(rmi4_data->iface_clk); + if (ret) { + dev_err(rmi4_data->pdev->dev.parent, + "error on clk_prepare_enable(iface_clk):%d\n", ret); + return ret; + } + + ret = clk_prepare_enable(rmi4_data->core_clk); + if (ret) { + clk_disable_unprepare(rmi4_data->iface_clk); + dev_err(rmi4_data->pdev->dev.parent, + "error clk_prepare_enable(core_clk):%d\n", ret); + } + return ret; +} + +static void synaptics_rmi4_clk_disable_unprepare( + struct synaptics_rmi4_data *rmi4_data) +{ + clk_disable_unprepare(rmi4_data->core_clk); + clk_disable_unprepare(rmi4_data->iface_clk); +} + +static int synaptics_rmi4_i2c_get(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + retval = pm_runtime_get_sync(i2c->adapter->dev.parent); + if (retval >= 0) { + retval = synaptics_rmi4_clk_prepare_enable(rmi4_data); + if (retval) + pm_runtime_put_sync(i2c->adapter->dev.parent); + } + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + return retval; +} + +static void synaptics_rmi4_i2c_put(struct synaptics_rmi4_data *rmi4_data) +{ + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + synaptics_rmi4_clk_disable_unprepare(rmi4_data); + pm_runtime_put_sync(i2c->adapter->dev.parent); + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); +} +#endif + +static struct synaptics_dsx_bus_access bus_access = { + .type = BUS_I2C, + .read = synaptics_rmi4_i2c_read, + .write = synaptics_rmi4_i2c_write, +#if defined(CONFIG_SECURE_TOUCH) + .get = synaptics_rmi4_i2c_get, + .put = synaptics_rmi4_i2c_put, +#endif +}; + +static struct synaptics_dsx_hw_interface hw_if; + +static struct platform_device *synaptics_dsx_i2c_device; + +static void synaptics_rmi4_i2c_dev_release(struct device *dev) +{ + kfree(synaptics_dsx_i2c_device); + + return; +} +#ifdef CONFIG_OF +int synaptics_dsx_get_dt_coords(struct device *dev, char *name, + struct synaptics_dsx_board_data *pdata, + struct device_node *node) +{ + u32 coords[DSX_COORDS_ARR_SIZE]; + struct property *prop; + struct device_node *np = (node == NULL) ? (dev->of_node) : (node); + int coords_size, rc; + + prop = of_find_property(np, name, NULL); + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + coords_size = prop->length / sizeof(u32); + if (coords_size != DSX_COORDS_ARR_SIZE) { + dev_err(dev, "invalid %s\n", name); + return -EINVAL; + } + + rc = of_property_read_u32_array(np, name, coords, coords_size); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read %s\n", name); + return rc; + } + + if (strcmp(name, "synaptics,panel-coords") == 0) { + pdata->panel_minx = coords[0]; + pdata->panel_miny = coords[1]; + pdata->panel_maxx = coords[2]; + pdata->panel_maxy = coords[3]; + } else if (strcmp(name, "synaptics,display-coords") == 0) { + pdata->disp_minx = coords[0]; + pdata->disp_miny = coords[1]; + pdata->disp_maxx = coords[2]; + pdata->disp_maxy = coords[3]; + } else { + dev_err(dev, "unsupported property %s\n", name); + return -EINVAL; + } + + return 0; +} + +static int synaptics_dsx_parse_dt(struct device *dev, + struct synaptics_dsx_board_data *rmi4_pdata) +{ + struct device_node *np = dev->of_node; + struct property *prop; + u32 temp_val, num_buttons; + u32 button_map[MAX_NUMBER_OF_BUTTONS]; + int rc, i; + + rmi4_pdata->x_flip = of_property_read_bool(np, "synaptics,x-flip"); + rmi4_pdata->y_flip = of_property_read_bool(np, "synaptics,y-flip"); + + rmi4_pdata->disable_gpios = of_property_read_bool(np, + "synaptics,disable-gpios"); + + rmi4_pdata->bypass_packrat_id_check = of_property_read_bool(np, + "synaptics,bypass-packrat-id-check"); + + rmi4_pdata->resume_in_workqueue = of_property_read_bool(np, + "synaptics,resume-in-workqueue"); + + rmi4_pdata->reset_delay_ms = RESET_DELAY; + rc = of_property_read_u32(np, "synaptics,reset-delay-ms", &temp_val); + if (!rc) + rmi4_pdata->reset_delay_ms = temp_val; + else if (rc != -EINVAL) { + dev_err(dev, "Unable to read reset delay\n"); + return rc; + } + + rc = of_property_read_u32(np, "synaptics,config-id", + &rmi4_pdata->config_id); + if (rc && (rc != -EINVAL)) + dev_err(dev, "Unable to read config id from DT\n"); + + rmi4_pdata->fw_name = "PRXXX_fw.img"; + rc = of_property_read_string(np, "synaptics,fw-name", + &rmi4_pdata->fw_name); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw name\n"); + return rc; + } + + /* reset, irq gpio info */ + rmi4_pdata->reset_gpio = of_get_named_gpio_flags(np, + "synaptics,reset-gpio", 0, &rmi4_pdata->reset_flags); + rmi4_pdata->irq_gpio = of_get_named_gpio_flags(np, + "synaptics,irq-gpio", 0, &rmi4_pdata->irq_flags); + + rc = synaptics_dsx_get_dt_coords(dev, "synaptics,display-coords", + rmi4_pdata, NULL); + if (rc && (rc != -EINVAL)) + return rc; + + rc = synaptics_dsx_get_dt_coords(dev, "synaptics,panel-coords", + rmi4_pdata, NULL); + if (rc && (rc != -EINVAL)) + return rc; + + rmi4_pdata->detect_device = of_property_read_bool(np, + "synaptics,detect-device"); + + if (rmi4_pdata->detect_device) + return 0; + + prop = of_find_property(np, "synaptics,button-map", NULL); + if (prop) { + num_buttons = prop->length / sizeof(temp_val); + + rmi4_pdata->cap_button_map = devm_kzalloc(dev, + sizeof(*rmi4_pdata->cap_button_map), + GFP_KERNEL); + if (!rmi4_pdata->cap_button_map) + return -ENOMEM; + + rmi4_pdata->cap_button_map->map = devm_kzalloc(dev, + sizeof(*rmi4_pdata->cap_button_map->map) * + MAX_NUMBER_OF_BUTTONS, GFP_KERNEL); + if (!rmi4_pdata->cap_button_map->map) + return -ENOMEM; + + if (num_buttons <= MAX_NUMBER_OF_BUTTONS) { + rc = of_property_read_u32_array(np, + "synaptics,button-map", button_map, + num_buttons); + if (rc) { + dev_err(dev, "Unable to read key codes\n"); + return rc; + } + for (i = 0; i < num_buttons; i++) + rmi4_pdata->cap_button_map->map[i] = + button_map[i]; + rmi4_pdata->cap_button_map->nbuttons = + num_buttons; + } else { + return -EINVAL; + } + } + return 0; +} +#else +static inline int synaptics_dsx_parse_dt(struct device *dev, + struct synaptics_dsx_board_data *rmi4_pdata) +{ + return 0; +} +#endif + +static int synaptics_rmi4_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int retval; + struct synaptics_dsx_board_data *platform_data; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "%s: SMBus byte data commands not supported by host\n", + __func__); + return -EIO; + } + + if (client->dev.of_node) { + platform_data = devm_kzalloc(&client->dev, + sizeof(struct synaptics_dsx_board_data), + GFP_KERNEL); + if (!platform_data) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + retval = synaptics_dsx_parse_dt(&client->dev, platform_data); + if (retval) + return retval; + } else { + platform_data = client->dev.platform_data; + } + + if (!platform_data) { + dev_err(&client->dev, + "%s: No platform data found\n", + __func__); + return -EINVAL; + } + + synaptics_dsx_i2c_device = kzalloc( + sizeof(struct platform_device), + GFP_KERNEL); + if (!synaptics_dsx_i2c_device) { + dev_err(&client->dev, + "%s: Failed to allocate memory for synaptics_dsx_i2c_device\n", + __func__); + return -ENOMEM; + } + + hw_if.board_data = platform_data; + hw_if.bus_access = &bus_access; + + synaptics_dsx_i2c_device->name = PLATFORM_DRIVER_NAME; + synaptics_dsx_i2c_device->id = 0; + synaptics_dsx_i2c_device->num_resources = 0; + synaptics_dsx_i2c_device->dev.parent = &client->dev; + synaptics_dsx_i2c_device->dev.platform_data = &hw_if; + synaptics_dsx_i2c_device->dev.release = synaptics_rmi4_i2c_dev_release; + + retval = platform_device_register(synaptics_dsx_i2c_device); + if (retval) { + dev_err(&client->dev, + "%s: Failed to register platform device\n", + __func__); + return -ENODEV; + } + + return 0; +} + +static int synaptics_rmi4_i2c_remove(struct i2c_client *client) +{ + platform_device_unregister(synaptics_dsx_i2c_device); + + return 0; +} + +static const struct i2c_device_id synaptics_rmi4_id_table[] = { + {I2C_DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table); + +#ifdef CONFIG_OF +static struct of_device_id dsx_match_table[] = { + { .compatible = "synaptics,dsx",}, + { }, +}; +#else +#define dsx_match_table NULL +#endif + +static struct i2c_driver synaptics_rmi4_i2c_driver = { + .driver = { + .name = I2C_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = dsx_match_table, + }, + .probe = synaptics_rmi4_i2c_probe, + .remove = synaptics_rmi4_i2c_remove, + .id_table = synaptics_rmi4_id_table, +}; + +int synaptics_rmi4_bus_init(void) +{ + return i2c_add_driver(&synaptics_rmi4_i2c_driver); +} +EXPORT_SYMBOL(synaptics_rmi4_bus_init); + +void synaptics_rmi4_bus_exit(void) +{ + i2c_del_driver(&synaptics_rmi4_i2c_driver); + + return; +} +EXPORT_SYMBOL(synaptics_rmi4_bus_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX I2C Bus Support Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_spi.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_spi.c new file mode 100755 index 000000000000..dd797eef3be1 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_spi.c @@ -0,0 +1,335 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com> + * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com> + * + * 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/input/synaptics_dsx.h> +#include "synaptics_dsx_core.h" + +#define SPI_READ 0x80 +#define SPI_WRITE 0x00 + +static int synaptics_rmi4_spi_set_page(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr) +{ + int retval; + unsigned int index; + unsigned int xfer_count = PAGE_SELECT_LEN + 1; + unsigned char txbuf[xfer_count]; + unsigned char page; + struct spi_message msg; + struct spi_transfer xfers[xfer_count]; + struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent); + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + page = ((addr >> 8) & ~MASK_7BIT); + if (page != rmi4_data->current_page) { + spi_message_init(&msg); + + txbuf[0] = SPI_WRITE; + txbuf[1] = MASK_8BIT; + txbuf[2] = page; + + for (index = 0; index < xfer_count; index++) { + memset(&xfers[index], 0, sizeof(struct spi_transfer)); + xfers[index].len = 1; + xfers[index].delay_usecs = bdata->byte_delay_us; + xfers[index].tx_buf = &txbuf[index]; + spi_message_add_tail(&xfers[index], &msg); + } + + if (bdata->block_delay_us) + xfers[index - 1].delay_usecs = bdata->block_delay_us; + + retval = spi_sync(spi, &msg); + if (retval == 0) { + rmi4_data->current_page = page; + retval = PAGE_SELECT_LEN; + } else { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to complete SPI transfer, error = %d\n", + __func__, retval); + } + } else { + retval = PAGE_SELECT_LEN; + } + + return retval; +} + +static int synaptics_rmi4_spi_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned short length) +{ + int retval; + unsigned int index; + unsigned int xfer_count = length + ADDRESS_WORD_LEN; + unsigned char txbuf[ADDRESS_WORD_LEN]; + unsigned char *rxbuf = NULL; + struct spi_message msg; + struct spi_transfer *xfers = NULL; + struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent); + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + spi_message_init(&msg); + + xfers = kcalloc(xfer_count, sizeof(struct spi_transfer), GFP_KERNEL); + if (!xfers) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate memory for xfers\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + txbuf[0] = (addr >> 8) | SPI_READ; + txbuf[1] = addr & MASK_8BIT; + + rxbuf = kmalloc(length, GFP_KERNEL); + if (!rxbuf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate memory for rxbuf\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + retval = synaptics_rmi4_spi_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) { + retval = -EIO; + goto exit; + } + + for (index = 0; index < xfer_count; index++) { + xfers[index].len = 1; + xfers[index].delay_usecs = bdata->byte_delay_us; + if (index < ADDRESS_WORD_LEN) + xfers[index].tx_buf = &txbuf[index]; + else + xfers[index].rx_buf = &rxbuf[index - ADDRESS_WORD_LEN]; + spi_message_add_tail(&xfers[index], &msg); + } + + if (bdata->block_delay_us) + xfers[index - 1].delay_usecs = bdata->block_delay_us; + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + memcpy(data, rxbuf, length); + } else { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to complete SPI transfer, error = %d\n", + __func__, retval); + } + + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + +exit: + kfree(rxbuf); + kfree(xfers); + + return retval; +} + +static int synaptics_rmi4_spi_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned short length) +{ + int retval; + unsigned int index; + unsigned int xfer_count = length + ADDRESS_WORD_LEN; + unsigned char *txbuf = NULL; + struct spi_message msg; + struct spi_transfer *xfers = NULL; + struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent); + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + spi_message_init(&msg); + + xfers = kcalloc(xfer_count, sizeof(struct spi_transfer), GFP_KERNEL); + if (!xfers) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate memory for xfers\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + txbuf = kmalloc(xfer_count, GFP_KERNEL); + if (!txbuf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate memory for txbuf\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + txbuf[0] = (addr >> 8) & ~SPI_READ; + txbuf[1] = addr & MASK_8BIT; + memcpy(&txbuf[ADDRESS_WORD_LEN], data, length); + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + retval = synaptics_rmi4_spi_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) { + retval = -EIO; + goto exit; + } + + for (index = 0; index < xfer_count; index++) { + xfers[index].len = 1; + xfers[index].delay_usecs = bdata->byte_delay_us; + xfers[index].tx_buf = &txbuf[index]; + spi_message_add_tail(&xfers[index], &msg); + } + + if (bdata->block_delay_us) + xfers[index - 1].delay_usecs = bdata->block_delay_us; + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + } else { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to complete SPI transfer, error = %d\n", + __func__, retval); + } + + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + +exit: + kfree(txbuf); + kfree(xfers); + + return retval; +} + +static struct synaptics_dsx_bus_access bus_access = { + .type = BUS_SPI, + .read = synaptics_rmi4_spi_read, + .write = synaptics_rmi4_spi_write, +}; + +static struct synaptics_dsx_hw_interface hw_if; + +static struct platform_device *synaptics_dsx_spi_device; + +static void synaptics_rmi4_spi_dev_release(struct device *dev) +{ + kfree(synaptics_dsx_spi_device); + + return; +} + +static int synaptics_rmi4_spi_probe(struct spi_device *spi) +{ + int retval; + + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + dev_err(&spi->dev, + "%s: Full duplex not supported by host\n", + __func__); + return -EIO; + } + + synaptics_dsx_spi_device = kzalloc( + sizeof(struct platform_device), + GFP_KERNEL); + if (!synaptics_dsx_spi_device) { + dev_err(&spi->dev, + "%s: Failed to allocate memory for synaptics_dsx_spi_device\n", + __func__); + return -ENOMEM; + } + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_3; + + retval = spi_setup(spi); + if (retval < 0) { + dev_err(&spi->dev, + "%s: Failed to perform SPI setup\n", + __func__); + return retval; + } + + hw_if.board_data = spi->dev.platform_data; + hw_if.bus_access = &bus_access; + + synaptics_dsx_spi_device->name = PLATFORM_DRIVER_NAME; + synaptics_dsx_spi_device->id = 0; + synaptics_dsx_spi_device->num_resources = 0; + synaptics_dsx_spi_device->dev.parent = &spi->dev; + synaptics_dsx_spi_device->dev.platform_data = &hw_if; + synaptics_dsx_spi_device->dev.release = synaptics_rmi4_spi_dev_release; + + retval = platform_device_register(synaptics_dsx_spi_device); + if (retval) { + dev_err(&spi->dev, + "%s: Failed to register platform device\n", + __func__); + return -ENODEV; + } + + return 0; +} + +static int synaptics_rmi4_spi_remove(struct spi_device *spi) +{ + platform_device_unregister(synaptics_dsx_spi_device); + + return 0; +} + +static struct spi_driver synaptics_rmi4_spi_driver = { + .driver = { + .name = SPI_DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = synaptics_rmi4_spi_probe, + .remove = __devexit_p(synaptics_rmi4_spi_remove), +}; + + +int synaptics_rmi4_bus_init(void) +{ + return spi_register_driver(&synaptics_rmi4_spi_driver); +} +EXPORT_SYMBOL(synaptics_rmi4_bus_init); + +void synaptics_rmi4_bus_exit(void) +{ + spi_unregister_driver(&synaptics_rmi4_spi_driver); + + return; +} +EXPORT_SYMBOL(synaptics_rmi4_bus_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX SPI Bus Support Module"); +MODULE_LICENSE("GPL v2"); |
