| /* drivers/misc/vib-gpio.c |
| * |
| * Copyright (C) 2013 Motorola, Inc. |
| * Copyright (C) 2008 Google, Inc. |
| * Author: Mike Lockwood <lockwood@android.com> |
| * |
| * 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/err.h> |
| #include <linux/gpio.h> |
| #include <linux/hrtimer.h> |
| #include <linux/module.h> |
| #include <linux/platform_device.h> |
| #include <linux/workqueue.h> |
| #include <linux/slab.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/of.h> |
| #include <linux/wakelock.h> |
| |
| /* TODO: replace with correct header */ |
| #include "../staging/android/timed_output.h" |
| |
| struct vib_gpio_data { |
| struct timed_output_dev dev; |
| struct work_struct vib_work; |
| struct hrtimer timer; |
| spinlock_t lock; |
| struct mutex io_mutex; /* protect GPIO & regulator operations */ |
| struct wake_lock wake_lock; |
| |
| struct regulator *reg; |
| int gpio; |
| int max_timeout; |
| bool active_low; |
| int initial_vibrate; |
| |
| int vib_power_state; |
| int vib_state; |
| }; |
| |
| static int power_on(struct vib_gpio_data *vib_data) |
| { |
| if (vib_data->reg) { |
| dev_dbg(vib_data->dev.dev, "enable regulator\n"); |
| return regulator_enable(vib_data->reg); |
| } |
| return 0; |
| } |
| |
| static int power_off(struct vib_gpio_data *vib_data) |
| { |
| if (vib_data->reg) { |
| dev_dbg(vib_data->dev.dev, "disable regulator\n"); |
| return regulator_disable(vib_data->reg); |
| } |
| return 0; |
| } |
| |
| static void vib_gpio_set(struct vib_gpio_data *vib_data, int on) |
| { |
| dev_dbg(vib_data->dev.dev, "%s(%d)\n", __func__, on); |
| |
| mutex_lock(&(vib_data->io_mutex)); |
| |
| if (on) { |
| if (!wake_lock_active(&vib_data->wake_lock)) |
| wake_lock(&vib_data->wake_lock); |
| |
| if (!vib_data->vib_power_state) { |
| power_on(vib_data); |
| vib_data->vib_power_state = 1; |
| } |
| if (vib_data->gpio >= 0) |
| gpio_direction_output(vib_data->gpio, |
| vib_data->active_low ? 0 : 1); |
| } else { |
| if (vib_data->gpio >= 0) |
| gpio_direction_output(vib_data->gpio, |
| vib_data->active_low ? 1 : 0); |
| |
| if (vib_data->vib_power_state) { |
| power_off(vib_data); |
| vib_data->vib_power_state = 0; |
| } |
| |
| wake_unlock(&vib_data->wake_lock); |
| } |
| |
| mutex_unlock(&(vib_data->io_mutex)); |
| } |
| |
| static void vib_gpio_update(struct work_struct *work) |
| { |
| struct vib_gpio_data *vib_data; |
| |
| vib_data = container_of(work, struct vib_gpio_data, vib_work); |
| if (vib_data) |
| vib_gpio_set(vib_data, vib_data->vib_state); |
| } |
| |
| static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer) |
| { |
| struct vib_gpio_data *vib_data = |
| container_of(timer, struct vib_gpio_data, timer); |
| dev_dbg(vib_data->dev.dev, "Timer expired: disabling vibrator\n"); |
| vib_data->vib_state = 0; |
| schedule_work(&vib_data->vib_work); |
| return HRTIMER_NORESTART; |
| } |
| |
| static int vib_gpio_get_time(struct timed_output_dev *dev) |
| { |
| struct vib_gpio_data *vib_data = |
| container_of(dev, struct vib_gpio_data, dev); |
| |
| if (hrtimer_active(&vib_data->timer)) { |
| ktime_t r = hrtimer_get_remaining(&vib_data->timer); |
| struct timeval t = ktime_to_timeval(r); |
| return t.tv_sec * 1000 + t.tv_usec / 1000; |
| } else |
| return 0; |
| } |
| |
| static void vib_gpio_enable(struct timed_output_dev *dev, int value) |
| { |
| struct vib_gpio_data *vib_data = |
| container_of(dev, struct vib_gpio_data, dev); |
| unsigned long flags; |
| |
| dev_dbg(dev->dev, "Enable vibrator for %dms\n", value); |
| |
| spin_lock_irqsave(&vib_data->lock, flags); |
| hrtimer_cancel(&vib_data->timer); |
| |
| if (value == 0) |
| vib_data->vib_state = 0; |
| else { |
| value = (value > vib_data->max_timeout ? |
| vib_data->max_timeout : value); |
| vib_data->vib_state = 1; |
| hrtimer_start(&vib_data->timer, |
| ktime_set(value / 1000, (value % 1000) * 1000000), |
| HRTIMER_MODE_REL); |
| } |
| |
| spin_unlock_irqrestore(&vib_data->lock, flags); |
| |
| schedule_work(&vib_data->vib_work); |
| } |
| |
| static int vib_gpio_probe(struct platform_device *pdev) |
| { |
| struct vib_gpio_data *vib_data; |
| struct device_node *np; |
| unsigned int prop; |
| int ret = 0; |
| |
| vib_data = kzalloc(sizeof(struct vib_gpio_data), GFP_KERNEL); |
| if (!vib_data) { |
| ret = -ENOMEM; |
| goto err; |
| } |
| |
| mutex_init(&(vib_data->io_mutex)); |
| |
| wake_lock_init(&vib_data->wake_lock, WAKE_LOCK_SUSPEND, "vibrator"); |
| |
| platform_set_drvdata(pdev, vib_data); |
| |
| vib_data->gpio = -1; |
| vib_data->active_low = 0; |
| vib_data->initial_vibrate = 0; |
| vib_data->max_timeout = 1500; |
| vib_data->reg = NULL; |
| #ifdef CONFIG_OF |
| np = pdev->dev.of_node; |
| if (!np) { |
| dev_err(&pdev->dev, "required device_tree entry not found\n"); |
| goto destroy_wakelock; |
| } |
| |
| if (!of_property_read_u32(np, "gpio", &prop)) |
| vib_data->gpio = prop; |
| |
| if (!of_property_read_u32(np, "max-timeout", &prop)) |
| vib_data->max_timeout = prop; |
| |
| if (!of_property_read_u32(np, "active-low", &prop)) |
| vib_data->active_low = prop; |
| |
| if (!of_property_read_u32(np, "initial-vibrate", &prop)) |
| vib_data->initial_vibrate = prop; |
| |
| #endif |
| vib_data->reg = regulator_get(&pdev->dev, "vib-gpio"); |
| if (IS_ERR(vib_data->reg)) { |
| ret = PTR_ERR(vib_data->reg); |
| goto destroy_wakelock; |
| } |
| |
| INIT_WORK(&vib_data->vib_work, vib_gpio_update); |
| |
| hrtimer_init(&vib_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
| |
| vib_data->timer.function = gpio_timer_func; |
| spin_lock_init(&vib_data->lock); |
| |
| vib_data->dev.name = "vibrator"; |
| vib_data->dev.get_time = vib_gpio_get_time; |
| vib_data->dev.enable = vib_gpio_enable; |
| ret = timed_output_dev_register(&vib_data->dev); |
| if (ret < 0) |
| goto reg_put; |
| |
| if (vib_data->gpio >= 0) |
| gpio_direction_output(vib_data->gpio, |
| vib_data->active_low); |
| |
| vib_gpio_enable(&vib_data->dev, vib_data->initial_vibrate); |
| |
| pr_info("vib gpio probe done\n"); |
| return 0; |
| |
| reg_put: |
| regulator_put(vib_data->reg); |
| destroy_wakelock: |
| wake_lock_destroy(&vib_data->wake_lock); |
| mutex_destroy(&(vib_data->io_mutex)); |
| kfree(vib_data); |
| err: |
| return ret; |
| } |
| |
| static int vib_gpio_remove(struct platform_device *pdev) |
| { |
| struct vib_gpio_data *vib_data = platform_get_drvdata(pdev); |
| |
| timed_output_dev_unregister(&vib_data->dev); |
| regulator_put(vib_data->reg); |
| if (wake_lock_active(&vib_data->wake_lock)) |
| wake_unlock(&vib_data->wake_lock); |
| wake_lock_destroy(&vib_data->wake_lock); |
| mutex_destroy(&(vib_data->io_mutex)); |
| kfree(vib_data); |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_OF |
| static struct of_device_id vib_gpio_of_match[] = { |
| { .compatible = "mot,vib-gpio" }, |
| { }, }; |
| MODULE_DEVICE_TABLE(of, vib_gpio_of_match); |
| #endif |
| |
| static struct platform_driver vib_gpio_driver = { |
| .probe = vib_gpio_probe, |
| .remove = vib_gpio_remove, |
| .driver = { |
| .name = "vib-gpio", |
| .owner = THIS_MODULE, |
| #ifdef CONFIG_OF |
| .of_match_table = of_match_ptr(vib_gpio_of_match), |
| #endif |
| }, |
| }; |
| |
| static int __init vib_gpio_init(void) |
| { |
| return platform_driver_register(&vib_gpio_driver); |
| } |
| |
| static void __exit vib_gpio_exit(void) |
| { |
| platform_driver_unregister(&vib_gpio_driver); |
| } |
| |
| late_initcall(vib_gpio_init); |
| module_exit(vib_gpio_exit); |
| |
| MODULE_AUTHOR("Motorola"); |
| MODULE_DESCRIPTION("vib gpio driver"); |
| MODULE_LICENSE("GPL"); |