/* drivers/staging/android/rk_timed_gpio.c
|
*
|
* Copyright (C) 2012-2016 ROCKCHIP.
|
* Author: jerry <jerry.zhang@rock-chips.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/clk.h>
|
#include <linux/err.h>
|
#include <linux/gpio.h>
|
#include <linux/hrtimer.h>
|
#include <linux/kernel.h>
|
#include <linux/module.h>
|
#include <linux/mutex.h>
|
#include <linux/of_gpio.h>
|
#include <linux/of_platform.h>
|
#include <linux/wakelock.h>
|
#include <linux/workqueue.h>
|
#include "timed_output.h"
|
|
#define MAX_TIMEOUT 10000 /* 10s */
|
|
static struct vibrator {
|
int gpio;
|
struct wake_lock wklock;
|
struct hrtimer timer;
|
struct mutex lock; /* mutex lock */
|
struct work_struct work;
|
} vibdata;
|
|
static void rk_vibrator_off(void)
|
{
|
gpio_direction_output(vibdata.gpio, 0);
|
gpio_set_value(vibdata.gpio, 0);
|
|
wake_unlock(&vibdata.wklock);
|
}
|
|
static void rk_vibrator_enable(struct timed_output_dev *sdev, int value)
|
{
|
mutex_lock(&vibdata.lock);
|
|
/* cancel previous timer and set GPIO according to value */
|
hrtimer_cancel(&vibdata.timer);
|
cancel_work_sync(&vibdata.work);
|
if (value) {
|
wake_lock(&vibdata.wklock);
|
gpio_direction_output(vibdata.gpio, 1);
|
gpio_set_value(vibdata.gpio, 1);
|
|
if (value > 0) {
|
if (value > MAX_TIMEOUT)
|
value = MAX_TIMEOUT;
|
value += 45;
|
hrtimer_start(&vibdata.timer,
|
ns_to_ktime((u64)value * NSEC_PER_MSEC),
|
HRTIMER_MODE_REL);
|
}
|
} else {
|
rk_vibrator_off();
|
}
|
|
mutex_unlock(&vibdata.lock);
|
}
|
|
static int rk_vibrator_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;
|
}
|
|
static enum hrtimer_restart rk_vibrator_timer_func(struct hrtimer *timer)
|
{
|
schedule_work(&vibdata.work);
|
return HRTIMER_NORESTART;
|
}
|
|
static void rk_vibrator_work(struct work_struct *work)
|
{
|
rk_vibrator_off();
|
}
|
|
struct timed_output_dev rk_vibrator_driver = {
|
.name = "vibrator",
|
.enable = rk_vibrator_enable,
|
.get_time = rk_vibrator_get_time,
|
};
|
|
static int vibrator_probe(struct platform_device *pdev)
|
{
|
int ret = 0;
|
struct device_node *np = pdev->dev.of_node;
|
|
vibdata.gpio = of_get_named_gpio_flags(np, "vibrator-gpio", 0, NULL);
|
if (!gpio_is_valid(vibdata.gpio))
|
return -1;
|
|
hrtimer_init(&vibdata.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
vibdata.timer.function = rk_vibrator_timer_func;
|
INIT_WORK(&vibdata.work, rk_vibrator_work);
|
|
ret = devm_gpio_request(&pdev->dev, vibdata.gpio, "vibrator");
|
if (ret < 0)
|
return ret;
|
|
wake_lock_init(&vibdata.wklock, WAKE_LOCK_SUSPEND, "vibrator");
|
mutex_init(&vibdata.lock);
|
ret = timed_output_dev_register(&rk_vibrator_driver);
|
if (ret < 0)
|
goto err_to_dev_reg;
|
|
return 0;
|
|
err_to_dev_reg:
|
mutex_destroy(&vibdata.lock);
|
wake_lock_destroy(&vibdata.wklock);
|
|
return ret;
|
}
|
|
static int vibrator_remove(struct platform_device *pdev)
|
{
|
mutex_destroy(&vibdata.lock);
|
wake_lock_destroy(&vibdata.wklock);
|
timed_output_dev_unregister(&rk_vibrator_driver);
|
|
return 0;
|
}
|
|
static const struct of_device_id vibrator_of_match[] = {
|
{ .compatible = "rk-vibrator-gpio" },
|
{ }
|
};
|
|
static struct platform_driver vibrator_driver = {
|
.probe = vibrator_probe,
|
.remove = vibrator_remove,
|
.driver = {
|
.name = "rk-vibrator",
|
.of_match_table = of_match_ptr(vibrator_of_match),
|
},
|
};
|
|
module_platform_driver(vibrator_driver);
|
|
MODULE_AUTHOR("jerry.zhang@rock-chips.com");
|
MODULE_DESCRIPTION("RK Vibrator driver");
|
MODULE_LICENSE("GPL");
|