From 0dd39eea44a72a55ab844184b89636d3236e5c49 Mon Sep 17 00:00:00 2001 From: huangjq9 Date: Tue, 16 Jul 2019 13:54:25 +0800 Subject: [PATCH] hall-sensor: add Stylus Detector driver Stylus Detector is a hall sensor. Input_Name: pen_detect Event_type: EV_SW Event: SW_PEN_INSERTED When stylus is off, it will report 0(stylus off); When stylus is on, it will report 1(stylus on); Change-Id: I7836c19f1142e9112b42d7d12ad788d19163d497 Signed-off-by: huangjq9 Reviewed-on: https://gerrit.mot.com/1385729 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Hujun Liao Reviewed-by: Weilong Wu Submit-Approved: Jira Key --- drivers/misc/pen/Android.mk | 8 + drivers/misc/pen/Kbuild | 9 + drivers/misc/pen/Makefile | 11 + drivers/misc/pen/bu520xx_pen.c | 463 +++++++++++++++++++++++++++++++++ 4 files changed, 491 insertions(+) create mode 100644 drivers/misc/pen/Android.mk create mode 100644 drivers/misc/pen/Kbuild create mode 100644 drivers/misc/pen/Makefile create mode 100644 drivers/misc/pen/bu520xx_pen.c diff --git a/drivers/misc/pen/Android.mk b/drivers/misc/pen/Android.mk new file mode 100644 index 000000000000..dc99d96d50e8 --- /dev/null +++ b/drivers/misc/pen/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := bu520xx_pen.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/pen/Kbuild b/drivers/misc/pen/Kbuild new file mode 100644 index 000000000000..0a5589aefc09 --- /dev/null +++ b/drivers/misc/pen/Kbuild @@ -0,0 +1,9 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/Module.symvers + +bu520xx-y := bu520xx_pen.o + +obj-m += bu520xx_pen.o diff --git a/drivers/misc/pen/Makefile b/drivers/misc/pen/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/misc/pen/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/misc/pen/bu520xx_pen.c b/drivers/misc/pen/bu520xx_pen.c new file mode 100644 index 000000000000..7593bc6ddd39 --- /dev/null +++ b/drivers/misc/pen/bu520xx_pen.c @@ -0,0 +1,463 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_WAKELOCK +#include +#else +#include +#endif + +#define BU520XX_DBG_ENABLE +#ifdef BU520XX_DBG_ENABLE //BU52055 for sofiap +#define pen_dbg(fmt, args...) pr_info("[DBG] %s(L%04d) : " fmt "\n", __func__, __LINE__, ##args) +#else +#define pen_dbg(fmt, args...) +#endif // BU520XX_DBG_ENABLE +#define pen_dbg_func_in() pen_dbg("[BU520XX-DBG-F.IN] %s", __func__) +#define pen_dbg_func_out() pen_dbg("[BU520XX-DBG-F.OUT] %s", __func__) +#define pen_dbg_line() pen_dbg("[LINE] %d(%s)", __LINE__, __func__) + +#define pen_err(fmt, args...) printk("[err] %s(L%04d) : " fmt "\n", __func__, __LINE__, ##args) +#define pen_info(fmt, args...) printk(fmt, ##args) + +#define DRIVER_NAME "pen_detect" +static struct workqueue_struct *hall_sensor_wq; +static struct workqueue_struct *hall_sensor_do_wq; +static struct kobject *hall_sensor_kobj; + +static int bu520xx_pen_probe(struct platform_device *pdev); +static int bu520xx_pen_remove(struct platform_device *pdev); + +static struct input_device_id mID[] = { + { .driver_info = 1 }, //scan all device to match hall sensor + { }, +}; + +static struct hall_sensor_str { + int irq; + int status; + int gpio; + int enable; + spinlock_t mHallSensorLock; + #ifdef CONFIG_HAS_WAKELOCK + struct wake_lock wake_lock; + #else + struct wakeup_source wake_lock; + #endif + struct input_dev *pen_indev; + struct input_handler pen_handler; + struct input_handle pen_handle; + struct delayed_work hall_sensor_work; + struct delayed_work hall_sensor_dowork; +}* hall_sensor_dev; + +//static void hall_sensor_shutdown(struct platform_device *pdev); +int pen_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id){ + pen_dbg_func_in(); + pen_info("[%s] hall_sensor connect to handler\n", DRIVER_NAME); + return 0; +} + +void pen_event(struct input_handle *handle, unsigned int type, unsigned int code, int value){ + pen_dbg_func_in(); + if(type==EV_SW && code==SW_PEN_INSERTED ){ + if(value != 2 && !!test_bit(code, hall_sensor_dev->pen_indev->sw) != !hall_sensor_dev->status){ + __change_bit(code, hall_sensor_dev->pen_indev->sw); + pen_info("[%s] reset dev->sw=%d \n", DRIVER_NAME,!hall_sensor_dev->status); + } + } +} + +bool pen_match(struct input_handler *handler, struct input_dev *dev){ + pen_dbg_func_in(); + if( (dev->name && handler->name) && + !strcmp(dev->name,"pen_input") && + !strcmp(handler->name,"pen_input_handler")) + return true; + + return false; +} + +static ssize_t show_action_status(struct device *dev,struct device_attribute *attr, char *buf) +{ + pen_dbg_func_in(); + if(!hall_sensor_dev) + return sprintf(buf, "Hall sensor does not exist!\n"); + return sprintf(buf, "%d\n",hall_sensor_dev->status); +} +static ssize_t store_action_status(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int request; + unsigned long flags; + + pen_dbg_func_in(); + if(!hall_sensor_dev){ + pen_err("Hall sensor does not exist!\n"); + return 0; + //return sprintf(buf, "Hall sensor does not exist!\n"); + } + + sscanf(buf, "%du", &request); + spin_lock_irqsave(&hall_sensor_dev->mHallSensorLock, flags); + if (!request) + hall_sensor_dev->status = 0; + else + hall_sensor_dev->status = 1; + spin_unlock_irqrestore(&hall_sensor_dev->mHallSensorLock, flags); + pen_info("[%s] SW_PEN_INSERTED rewite value = %d\n", DRIVER_NAME,!hall_sensor_dev->status); + return count; +} + +static ssize_t show_hall_sensor_enable(struct device *dev, struct device_attribute *attr, char *buf) +{ + pen_dbg_func_in(); + if(!hall_sensor_dev) + return sprintf(buf, "Hall sensor does not exist!\n"); + return sprintf(buf, "%d\n",hall_sensor_dev->enable); +} + +static ssize_t store_hall_sensor_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int request; + + pen_dbg_func_in(); + if(!hall_sensor_dev){ + pen_err("Hall sensor does not exist!\n"); + return 0; + //return sprintf(buf, "Hall sensor does not exist!\n"); + } + sscanf(buf, "%du", &request); + if(request==hall_sensor_dev->enable){ + return count; + } + else { + unsigned long flags; + spin_lock_irqsave(&hall_sensor_dev->mHallSensorLock, flags); + if (hall_sensor_dev->enable==0){ + enable_irq(hall_sensor_dev->irq); + hall_sensor_dev->enable=1; + } + else if (hall_sensor_dev->enable==1){ + disable_irq(hall_sensor_dev->irq); + hall_sensor_dev->enable=0; + } + spin_unlock_irqrestore(&hall_sensor_dev->mHallSensorLock, flags); + } + return count; +} + + +static SENSOR_DEVICE_ATTR_2(action_status, S_IRUGO|S_IWUSR, show_action_status, store_action_status, 0, 0); +static SENSOR_DEVICE_ATTR_2(activity, S_IRUGO|S_IWUSR,show_hall_sensor_enable, store_hall_sensor_enable, 0, 0); + +static struct attribute *hall_sensor_attrs[] = { + &sensor_dev_attr_action_status.dev_attr.attr, + &sensor_dev_attr_activity.dev_attr.attr, + NULL +}; + +static struct attribute_group hall_sensor_group = { + .name = "hall_sensor", + .attrs = hall_sensor_attrs +}; + +static int pen_input_device_create(void) +{ + int err = 0; + + pen_dbg_func_in(); + hall_sensor_dev->pen_indev = input_allocate_device(); + if(!hall_sensor_dev->pen_indev){ + pen_err("[%s] pen_indev allocation fails\n", DRIVER_NAME); + err = -ENOMEM; + goto exit; + } + + hall_sensor_dev->pen_indev->name = "pen_detect"; + hall_sensor_dev->pen_indev->phys= "/dev/input/pen_detect"; + hall_sensor_dev->pen_indev->dev.parent= NULL; + input_set_capability(hall_sensor_dev->pen_indev, EV_SW, SW_PEN_INSERTED); + + err = input_register_device(hall_sensor_dev->pen_indev); + if (err) { + pen_err("[%s] input registration fails\n", DRIVER_NAME); + err = -1; + goto exit_input_free; + } + hall_sensor_dev->pen_handler.match=pen_match; + hall_sensor_dev->pen_handler.connect=pen_connect; + hall_sensor_dev->pen_handler.event=pen_event; + hall_sensor_dev->pen_handler.name="pen_detect_handler"; + hall_sensor_dev->pen_handler.id_table = mID; + err=input_register_handler(& hall_sensor_dev->pen_handler); + if(err){ + pen_err("[%s] handler registration fails\n", DRIVER_NAME); + err = -1; + goto exit_unregister_input_dev; + } + hall_sensor_dev->pen_handle.name="pen_detect_handle"; + hall_sensor_dev->pen_handle.open=1; //receive any event from hall sensor + hall_sensor_dev->pen_handle.dev=hall_sensor_dev->pen_indev; + hall_sensor_dev->pen_handle.handler=&hall_sensor_dev->pen_handler; + err=input_register_handle(& hall_sensor_dev->pen_handle); + if(err){ + pen_err("[%s] handle registration fails\n", DRIVER_NAME); + err = -1; + goto exit_unregister_handler; + } + return 0; + +exit_unregister_handler: + input_unregister_handler(& hall_sensor_dev->pen_handler); +exit_unregister_input_dev: + input_unregister_device(hall_sensor_dev->pen_indev); +exit_input_free: + input_free_device(hall_sensor_dev->pen_indev); + hall_sensor_dev->pen_indev = NULL; +exit: + return err; +} + + + +static void pen_do_work_function(struct work_struct *dat) +{ + pen_dbg_func_in(); + pen_info("[%s] hall_sensor_interrupt = %d\n", DRIVER_NAME,hall_sensor_dev->irq); + cancel_delayed_work(&hall_sensor_dev->hall_sensor_work); + queue_delayed_work(hall_sensor_wq, &hall_sensor_dev->hall_sensor_work, 0); +} + + +static void pen_report_function(struct work_struct *dat) +{ + unsigned long flags; + int counter, status = 0, initial_status; + + pen_dbg_func_in(); + initial_status = hall_sensor_dev->status; + for (counter = 0;counter < 3;counter++){ + msleep(50); + spin_lock_irqsave(&hall_sensor_dev->mHallSensorLock, flags); + if (gpio_get_value(hall_sensor_dev->gpio) > 0){ + hall_sensor_dev->status = 1; + status++; + }else{ + hall_sensor_dev->status = 0; + } + pen_info("[%s] SW_PEN_INSERTED check value = %d\n", DRIVER_NAME,!hall_sensor_dev->status); + spin_unlock_irqrestore(&hall_sensor_dev->mHallSensorLock, flags); + } + if((status > 0) && (status < 3)){ + pen_info("[%s] SW_PEN_INSERTED do not report to framework.\n", DRIVER_NAME); + hall_sensor_dev->status = initial_status; + input_report_switch(hall_sensor_dev->pen_indev, SW_PEN_INSERTED, 2); + input_sync(hall_sensor_dev->pen_indev); + //wake_unlock(&hall_sensor_dev->wake_lock); + return; + } + + input_report_switch(hall_sensor_dev->pen_indev, SW_PEN_INSERTED, !hall_sensor_dev->status); + input_sync(hall_sensor_dev->pen_indev); +#ifdef CONFIG_HAS_WAKELOCK + wake_unlock(&hall_sensor_dev->wake_lock); +#else +#endif + +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&hall_sensor_dev->wake_lock, msecs_to_jiffies(3000)); +#else + __pm_wakeup_event(&hall_sensor_dev->wake_lock, msecs_to_jiffies(3000)); +#endif + + pen_info("[%s] SW_pen report value = %d\n", DRIVER_NAME,!hall_sensor_dev->status); +} + +static irqreturn_t hall_sensor_interrupt_handler(int irq, void *dev_id) +{ + pen_dbg_func_in(); + pen_info("[%s] hall_sensor_interrupt_handler = %d\n", DRIVER_NAME,hall_sensor_dev->irq); + queue_delayed_work(hall_sensor_do_wq, &hall_sensor_dev->hall_sensor_dowork, msecs_to_jiffies(0)); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock(&hall_sensor_dev->wake_lock); +#else +#endif + return IRQ_HANDLED; +} + +static int set_irq_hall_sensor(void) +{ + int rc = 0 ; + + pen_dbg_func_in(); + hall_sensor_dev->irq = gpio_to_irq(hall_sensor_dev->gpio); + pen_info("[%s] hall_sensor irq = %d\n", DRIVER_NAME,hall_sensor_dev->irq); + rc = request_irq(hall_sensor_dev->irq,hall_sensor_interrupt_handler, + IRQF_SHARED|IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,"hall_sensor_irq",hall_sensor_dev); + if (rc<0) { + pen_info("[%s] Could not register for hall sensor interrupt, irq = %d, rc = %d\n", DRIVER_NAME,hall_sensor_dev->irq,rc); + rc = -EIO; + goto err_gpio_request_irq_fail ; + } + + enable_irq_wake(hall_sensor_dev->irq); + + return 0; + +err_gpio_request_irq_fail: + return rc; +} + +#ifdef CONFIG_OF +static const struct of_device_id bu520xx_pen_match[] = { + { .compatible = "rohm,bu520xx_pen_detect", }, + {} +}; +#else +#define bu520xx_pen_match NULL +#endif +MODULE_DEVICE_TABLE(of, bu520xx_pen_match); + +static struct platform_driver bu520xx_pen_driver = { + .probe = bu520xx_pen_probe, + .remove = bu520xx_pen_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = bu520xx_pen_match, + }, +}; + +static int bu520xx_pen_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + enum of_gpio_flags flags; + int ret; + + pen_dbg_func_in(); + + //set file node + hall_sensor_kobj = kobject_create_and_add("hall_sensor_kobject", kernel_kobj); + if (!hall_sensor_kobj){ + pen_err("[%s] hall_sensor_kobject fails for hall sensor\n", DRIVER_NAME); + platform_device_unregister(pdev); + platform_driver_unregister(&bu520xx_pen_driver); + return -ENOMEM; + } + + ret = sysfs_create_group(hall_sensor_kobj, &hall_sensor_group); + if (ret){ + goto fail_for_hall_sensor; + } + + //Memory allocation + hall_sensor_dev = kzalloc(sizeof (struct hall_sensor_str), GFP_KERNEL); + if (!hall_sensor_dev) { + pen_err("[%s] Memory allocation fails for hall sensor\n", DRIVER_NAME); + ret = -ENOMEM; + goto fail_for_hall_sensor; + } + + spin_lock_init(&hall_sensor_dev->mHallSensorLock); + hall_sensor_dev->enable = 1; + + //set gpio + hall_sensor_dev->gpio = of_get_named_gpio_flags(np, + "rohm,nirq-gpio", 0, &flags); + if (hall_sensor_dev->gpio < 0) + { + pen_err("[%s] GPIO for hall sensor does not exist.\n", DRIVER_NAME); + ret= -1; + goto fail_for_set_gpio_hall_sensor; + } + + gpio_request(hall_sensor_dev->gpio,"pen_detect_gpio"); + gpio_direction_input(hall_sensor_dev->gpio); + + //set irq + ret = set_irq_hall_sensor(); + if (ret < 0) + goto fail_for_irq_hall_sensor; + + //create input_dev + hall_sensor_dev->pen_indev = NULL; + ret = pen_input_device_create(); + if (ret < 0) + goto fail_for_create_input_dev; + + //init workqueue & start detect signal + hall_sensor_wq = create_singlethread_workqueue("hall_sensor_wq"); + hall_sensor_do_wq = create_singlethread_workqueue("hall_sensor_do_wq"); + INIT_DELAYED_WORK(&hall_sensor_dev->hall_sensor_work, pen_report_function); + INIT_DELAYED_WORK(&hall_sensor_dev->hall_sensor_dowork, pen_do_work_function); + + + queue_delayed_work(hall_sensor_do_wq, &hall_sensor_dev->hall_sensor_dowork, 0); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&hall_sensor_dev->wake_lock, WAKE_LOCK_SUSPEND, "pen_suspend_blocker"); +#else + wakeup_source_init(&hall_sensor_dev->wake_lock, "pen_suspend_blocker"); +#endif + + pen_info("hall_sensor_init Done.\r\n"); + return 0; + +fail_for_create_input_dev: + free_irq(hall_sensor_dev->irq, hall_sensor_dev); + +fail_for_irq_hall_sensor: + gpio_free(hall_sensor_dev->gpio); +fail_for_set_gpio_hall_sensor: + kfree(hall_sensor_dev); + hall_sensor_dev=NULL; + +fail_for_hall_sensor: + kobject_put(hall_sensor_kobj); + return ret; +} + +static int bu520xx_pen_remove(struct platform_device *pdev) +{ + disable_irq(hall_sensor_dev->irq); + gpio_free(hall_sensor_dev->gpio); + destroy_workqueue(hall_sensor_do_wq); + destroy_workqueue(hall_sensor_wq); + input_free_device(hall_sensor_dev->pen_indev); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&hall_sensor_dev->wake_lock); +#else + wakeup_source_trash(&hall_sensor_dev->wake_lock); +#endif + hall_sensor_dev->pen_indev=NULL; + kfree(hall_sensor_dev); + hall_sensor_dev=NULL; + kobject_put(hall_sensor_kobj); + platform_driver_unregister(&bu520xx_pen_driver); + platform_device_unregister(pdev); + return 1; +} + + +module_platform_driver(bu520xx_pen_driver); + + +MODULE_DESCRIPTION("Rohm_Hall_sensor-bu520xx_pen Driver"); +MODULE_AUTHOR("huangjq9 "); +MODULE_LICENSE("GPL v2");