| #include <linux/platform_device.h> |
| #include <linux/init.h> |
| #include <linux/module.h> |
| #include <linux/hrtimer.h> |
| #include <linux/err.h> |
| #include <linux/gpio.h> |
| #include <linux/wakelock.h> |
| #include <linux/mutex.h> |
| #include <linux/clk.h> |
| #include <linux/workqueue.h> |
| #include <asm/mach-types.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/regulator/consumer.h> |
| #include<../drivers/staging/android/timed_output.h> |
| #include <linux/slab.h> |
| |
| #define DRV2603_VIBRATOR_EN 86 |
| #define DRV2603_VIBRATOR_PWM 85 |
| #define MAX_TIMEOUT 10000 /* 10s */ |
| #define DRV2603_VTG_MIN_UV 2850000 |
| #define DRV2603_VTG_MAX_UV 2850000 |
| #define DRV2603_VTG_CURR_UA 150000 |
| |
| struct drv2603_data { |
| struct platform_device *pdev; |
| struct regulator *drv2603_vcc; |
| }; |
| struct drv2603_data *drv2603; |
| |
| static struct vibrator{ |
| struct wake_lock wklock; |
| struct hrtimer timer; |
| struct mutex lock; |
| struct work_struct work; |
| }vibdata; |
| |
| static void drv2603_vibrator_off(void) |
| { |
| gpio_direction_output (DRV2603_VIBRATOR_EN, 0); |
| wake_unlock(&vibdata.wklock); |
| } |
| |
| void drv2603_motor_enable(struct timed_output_dev *sdev, int value) |
| { |
| mutex_lock(&vibdata.lock); |
| hrtimer_cancel(&vibdata.timer); |
| cancel_work_sync(&vibdata.work); |
| if (value) |
| { |
| wake_lock(&vibdata.wklock); |
| gpio_direction_output(DRV2603_VIBRATOR_EN, 1); |
| if (value > 0) |
| { |
| if(value > MAX_TIMEOUT) |
| value= MAX_TIMEOUT; |
| hrtimer_start(&vibdata.timer, |
| ns_to_ktime((u64)value* NSEC_PER_MSEC), |
| HRTIMER_MODE_REL); |
| } |
| } |
| else |
| drv2603_vibrator_off(); |
| |
| mutex_unlock(&vibdata.lock); |
| } |
| |
| int drv2603_get_time(struct timed_output_dev *sdev) |
| { |
| if(hrtimer_active(&vibdata.timer)) |
| { |
| ktime_t r = hrtimer_get_remaining(&vibdata.timer); |
| return ktime_to_ms(r); |
| } |
| return 0; |
| } |
| |
| struct timed_output_dev drv2603_motot_driver={ |
| .name ="vibrator", |
| .enable= drv2603_motor_enable, |
| .get_time= drv2603_get_time, |
| }; |
| |
| static enum hrtimer_restart drv2603_vibrator_timer_func(struct hrtimer * timer) |
| { |
| schedule_work(&vibdata.work); |
| return HRTIMER_NORESTART; |
| } |
| |
| static void drv2603_vibrator_work(struct work_struct *work) |
| { |
| drv2603_vibrator_off(); |
| } |
| |
| static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA) |
| { |
| return (regulator_count_voltages(reg) > 0) ? |
| regulator_set_optimum_mode(reg, load_uA) : 0; |
| } |
| |
| static int drv2603_pdev_power_on(struct drv2603_data *hbtp, bool on) |
| { |
| int ret, error; |
| |
| if (!hbtp->drv2603_vcc) { |
| pr_err("%s: regulator is not available\n", __func__); |
| return -EINVAL; |
| } |
| |
| if (!on) |
| goto reg_off; |
| |
| ret = reg_set_optimum_mode_check(hbtp->drv2603_vcc, DRV2603_VTG_CURR_UA); |
| if (ret < 0) { |
| pr_err("%s: Regulator drv2603_vcc set_opt failed rc=%d\n", |
| __func__, ret); |
| return -EINVAL; |
| } |
| |
| ret = regulator_enable(hbtp->drv2603_vcc); |
| if (ret) { |
| pr_err("%s: Regulator drv2603_vcc enable failed rc=%d\n", |
| __func__, ret); |
| error = -EINVAL; |
| goto error_reg_en_drv2603_vcc; |
| } |
| |
| return 0; |
| error_reg_en_drv2603_vcc: |
| reg_set_optimum_mode_check(hbtp->drv2603_vcc, 0); |
| return error; |
| |
| reg_off: |
| reg_set_optimum_mode_check(hbtp->drv2603_vcc, 0); |
| regulator_disable(hbtp->drv2603_vcc); |
| return 0; |
| } |
| |
| #ifdef CONFIG_PM |
| |
| static int ti_drv2603_suspend(struct platform_device *pdev, pm_message_t state) |
| { |
| hrtimer_cancel(&vibdata.timer); |
| cancel_work_sync(&vibdata.work); |
| |
| /* turn regulators off */ |
| drv2603_pdev_power_on(drv2603,false); |
| |
| return 0; |
| } |
| |
| static int ti_drv2603_resume(struct platform_device *pdev) |
| { |
| int rc; |
| |
| /* turn regulators on */ |
| rc = drv2603_pdev_power_on(drv2603, true); |
| if (rc < 0) { |
| pr_err("%s: drv2603 unable to turn regulators on rc=%d\n", |
| __func__, rc); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| #endif /* CONFIG_PM */ |
| |
| static int ti_drv2603_probe(struct platform_device *pdev) |
| { |
| int rc=0; |
| int ret =0; |
| |
| struct regulator *drv2603_vcc; |
| |
| printk("ti_drv2603 probe\n"); |
| drv2603_vcc = regulator_get(&pdev->dev, "vdd-drv2603"); |
| if (IS_ERR(drv2603_vcc)) { |
| rc = PTR_ERR(drv2603_vcc); |
| pr_err("%s: Regulator get failed vcc_drv2603 rc=%d\n", |
| __func__, rc); |
| return -EINVAL; |
| } |
| |
| if (regulator_count_voltages(drv2603_vcc) > 0) { |
| ret = regulator_set_voltage(drv2603_vcc, |
| DRV2603_VTG_MIN_UV, DRV2603_VTG_MAX_UV); |
| if (ret) { |
| pr_err("%s: regulator set_vtg failed rc=%d\n", |
| __func__, ret); |
| ret = -EINVAL; |
| goto error_set_vtg_drv2603_vcc; |
| } |
| } |
| |
| drv2603->drv2603_vcc = drv2603_vcc; |
| drv2603->pdev= pdev; |
| drv2603_pdev_power_on(drv2603,true); |
| INIT_WORK(&vibdata.work, drv2603_vibrator_work); |
| hrtimer_init(&vibdata.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
| vibdata.timer.function= drv2603_vibrator_timer_func; |
| |
| ret =gpio_request(DRV2603_VIBRATOR_EN, "vibrator-en"); |
| if (ret< 0) |
| { |
| printk("vibratorrequest en IO err!:%d\n",ret); |
| return ret; |
| } |
| ret = gpio_request(DRV2603_VIBRATOR_PWM, "vibrator-pwm"); |
| if (ret< 0) |
| { |
| printk("vibratorrequest pwm IO err!:%d\n",ret); |
| return ret; |
| } |
| gpio_direction_output(DRV2603_VIBRATOR_EN, 0); |
| gpio_direction_input(DRV2603_VIBRATOR_PWM); |
| wake_lock_init(&vibdata.wklock,WAKE_LOCK_SUSPEND, "vibrator"); |
| mutex_init(&vibdata.lock); |
| ret=timed_output_dev_register(&drv2603_motot_driver); |
| if (ret< 0) |
| goto err_to_dev_reg; |
| |
| return 0; |
| |
| err_to_dev_reg: |
| mutex_destroy(&vibdata.lock); |
| wake_lock_destroy(&vibdata.wklock); |
| gpio_free(DRV2603_VIBRATOR_EN); |
| gpio_free(DRV2603_VIBRATOR_PWM); |
| printk("vibrator err!:%d\n",ret); |
| return ret; |
| |
| error_set_vtg_drv2603_vcc: |
| regulator_put(drv2603_vcc); |
| return ret; |
| } |
| |
| static int ti_drv2603_remove(struct platform_device *pdev) |
| { |
| if (drv2603->drv2603_vcc) { |
| drv2603_pdev_power_on(drv2603, false); |
| regulator_put(drv2603->drv2603_vcc); |
| } |
| return 0; |
| } |
| |
| static const struct of_device_id drv2603_of_id_table[] = { |
| {.compatible = "drv2603"}, |
| { }, |
| }; |
| |
| static struct platform_driver ti_drv2603_driver = { |
| .probe = ti_drv2603_probe, |
| .remove = ti_drv2603_remove, |
| .driver = { |
| .name = "drv2603", |
| .owner = THIS_MODULE, |
| .of_match_table = drv2603_of_id_table, |
| }, |
| #ifdef CONFIG_PM |
| .suspend = ti_drv2603_suspend, |
| .resume = ti_drv2603_resume, |
| #endif |
| }; |
| |
| static int ti_drv2603_init(void) |
| { |
| drv2603 = kzalloc(sizeof(struct drv2603_data), GFP_KERNEL); |
| if (!drv2603) |
| return -ENOMEM; |
| return platform_driver_register(&ti_drv2603_driver); |
| } |
| |
| static void ti_drv2603_exit(void) |
| { |
| mutex_destroy(&vibdata.lock); |
| wake_lock_destroy(&vibdata.wklock); |
| gpio_free(DRV2603_VIBRATOR_EN); |
| gpio_free(DRV2603_VIBRATOR_PWM); |
| timed_output_dev_register(&drv2603_motot_driver); |
| platform_driver_unregister(&ti_drv2603_driver); |
| kfree(drv2603); |
| } |
| |
| module_init(ti_drv2603_init); |
| module_exit(ti_drv2603_exit); |
| |
| MODULE_AUTHOR("Godfather.cxl"); |
| MODULE_DESCRIPTION("Drv2603 Vibrator driver"); |
| MODULE_LICENSE("GPL"); |
| MODULE_ALIAS("ti_drv2603"); |