diff options
author | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
---|---|---|
committer | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
commit | c6da2cfeb05178a11c6d062a06f8078150ee492f (patch) | |
tree | f3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/motor/isa1200_vibrator.c | |
parent | c6d7c4dbff353eac7919342ae6b3299a378160a6 (diff) | |
download | kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2 |
samsung update 1
Diffstat (limited to 'drivers/motor/isa1200_vibrator.c')
-rw-r--r-- | drivers/motor/isa1200_vibrator.c | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/drivers/motor/isa1200_vibrator.c b/drivers/motor/isa1200_vibrator.c new file mode 100644 index 0000000..97ccfe1 --- /dev/null +++ b/drivers/motor/isa1200_vibrator.c @@ -0,0 +1,386 @@ +/* drivers/motor/isa1200_vibrator.c + * + * Copyright (C) 2011 Samsung Electronics Co. Ltd. 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/hrtimer.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/pwm.h> +#include <linux/wakelock.h> +#include <linux/mutex.h> +#include <linux/clk.h> +#include <linux/pwm.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/timed_output.h> +#include <linux/isa1200_vibrator.h> +#include <linux/fs.h> +#include <asm/uaccess.h> + +#if 0 +#define MOTOR_DEBUG +#endif + +struct isa1200_vibrator_drvdata { + struct timed_output_dev dev; + struct hrtimer timer; + struct work_struct work; + struct clk *vib_clk; + struct pwm_device *pwm; + struct i2c_client *client; + int (*gpio_en) (bool) ; + spinlock_t lock; + bool running; + int timeout; + int max_timeout; + u8 ctrl0; + u8 ctrl1; + u8 ctrl2; + u8 ctrl4; + u8 pll; + u8 duty; + u8 period; + int pwm_id; + u16 pwm_duty; + u16 pwm_period; +}; + + +static struct isa1200_vibrator_drvdata *g_drvdata; + +static int isa1200_vibrator_i2c_write(struct i2c_client *client, + u8 addr, u8 val) +{ + int error = 0; + error = i2c_smbus_write_byte_data(client, addr, val); + if (error) + printk(KERN_ERR "[VIB] Failed to write addr=[0x%x], val=[0x%x]\n", + addr, val); + + return error; +} + +int vibtonz_i2c_write(u8 addr, int length, u8 *data) +{ + if (NULL == g_drvdata) { + printk(KERN_ERR "[VIB] driver is not ready\n"); + return -EFAULT; + } + + if (2 != length) + printk(KERN_ERR "[VIB] length should be 2(len:%d)\n", + length); + +#ifdef MOTOR_DEBUG + printk(KERN_DEBUG "[VIB] data : %x, %x\n", + data[0], data[1]); +#endif + + return isa1200_vibrator_i2c_write(g_drvdata->client, + data[0], data[1]); +} + +void vibtonz_clk_enable(bool en) +{ + if (NULL == g_drvdata) { + printk(KERN_ERR "[VIB] driver is not ready\n"); + return ; + } + + if (en) { + if (g_drvdata->vib_clk) + clk_enable(g_drvdata->vib_clk); + else + pwm_enable(g_drvdata->pwm); + } else { + if (g_drvdata->vib_clk) + clk_disable(g_drvdata->vib_clk); + else + pwm_disable(g_drvdata->pwm); + } +} + +void vibtonz_clk_config(int duty) +{ + u32 duty_ns = 0; + + duty_ns = (u32)(g_drvdata->pwm_period * duty) + / 1000; + + pwm_config(g_drvdata->pwm, + duty_ns, + g_drvdata->pwm_period); + +} + +void vibtonz_chip_enable(bool en) +{ + if (NULL == g_drvdata) { + printk(KERN_ERR "[VIB] driver is not ready\n"); + return ; + } + g_drvdata->gpio_en(en ? true : false); +} + +static void isa1200_vibrator_hw_init(struct isa1200_vibrator_drvdata *data) +{ + data->gpio_en(true); + msleep(20); + isa1200_vibrator_i2c_write(data->client, + HAPTIC_CONTROL_REG0, data->ctrl0); + isa1200_vibrator_i2c_write(data->client, + HAPTIC_CONTROL_REG1, data->ctrl1); + isa1200_vibrator_i2c_write(data->client, + HAPTIC_CONTROL_REG2, data->ctrl2); + isa1200_vibrator_i2c_write(data->client, + HAPTIC_PLL_REG, data->pll); + isa1200_vibrator_i2c_write(data->client, + HAPTIC_CONTROL_REG4, data->ctrl4); + isa1200_vibrator_i2c_write(data->client, + HAPTIC_PWM_DUTY_REG, data->period/2); + isa1200_vibrator_i2c_write(data->client, + HAPTIC_PWM_PERIOD_REG, data->period); + +#ifdef MOTOR_DEBUG + printk(KERN_DEBUG "[VIB] ctrl0 = 0x%x\n", data->ctrl0); + printk(KERN_DEBUG "[VIB] ctrl1 = 0x%x\n", data->ctrl1); + printk(KERN_DEBUG "[VIB] ctrl2 = 0x%x\n", data->ctrl2); + printk(KERN_DEBUG "[VIB] pll = 0x%x\n", data->pll); + printk(KERN_DEBUG "[VIB] ctrl4 = 0x%x\n", data->ctrl4); + printk(KERN_DEBUG "[VIB] duty = 0x%x\n", data->period/2); + printk(KERN_DEBUG "[VIB] period = 0x%x\n", data->period); +#endif + +} + +static void isa1200_vibrator_on(struct isa1200_vibrator_drvdata *data) +{ + isa1200_vibrator_i2c_write(data->client, + HAPTIC_CONTROL_REG0, data->ctrl0 | CTL0_NORMAL_OP); + isa1200_vibrator_i2c_write(data->client, + HAPTIC_PWM_DUTY_REG, data->duty - 3); +#ifdef MOTOR_DEBUG + printk(KERN_DEBUG "[VIB] ctrl0 = 0x%x\n", data->ctrl0 | CTL0_NORMAL_OP); + printk(KERN_DEBUG "[VIB] duty = 0x%x\n", data->duty); +#endif +} + +static void isa1200_vibrator_off(struct isa1200_vibrator_drvdata *data) +{ + isa1200_vibrator_i2c_write(data->client, + HAPTIC_PWM_DUTY_REG, data->period / 2); + isa1200_vibrator_i2c_write(data->client, + HAPTIC_CONTROL_REG0, data->ctrl0); +} + +static enum hrtimer_restart isa1200_vibrator_timer_func(struct hrtimer *_timer) +{ + struct isa1200_vibrator_drvdata *data = + container_of(_timer, struct isa1200_vibrator_drvdata, timer); + + data->timeout = 0; + + schedule_work(&data->work); + return HRTIMER_NORESTART; +} + +static void isa1200_vibrator_work(struct work_struct *_work) +{ + struct isa1200_vibrator_drvdata *data = + container_of(_work, struct isa1200_vibrator_drvdata, work); + + if (0 == data->timeout) { + if (!data->running) + return ; + + data->running = false; + isa1200_vibrator_off(data); + vibtonz_clk_config(500); + vibtonz_clk_enable(false); + } else { + if (data->running) + return ; + + data->running = true; + vibtonz_clk_config(999); + vibtonz_clk_enable(true); + mdelay(1); + isa1200_vibrator_on(data); + } +} + +static int isa1200_vibrator_get_time(struct timed_output_dev *_dev) +{ + struct isa1200_vibrator_drvdata *data = + container_of(_dev, struct isa1200_vibrator_drvdata, dev); + + if (hrtimer_active(&data->timer)) { + ktime_t r = hrtimer_get_remaining(&data->timer); + struct timeval t = ktime_to_timeval(r); + return t.tv_sec * 1000 + t.tv_usec / 1000; + } else + return 0; +} + +static void isa1200_vibrator_enable(struct timed_output_dev *_dev, int value) +{ + struct isa1200_vibrator_drvdata *data = + container_of(_dev, struct isa1200_vibrator_drvdata, dev); + unsigned long flags; + +#ifdef MOTOR_DEBUG + printk(KERN_DEBUG "[VIB] time = %dms\n", value); +#endif + cancel_work_sync(&data->work); + hrtimer_cancel(&data->timer); + data->timeout = value; + schedule_work(&data->work); + spin_lock_irqsave(&data->lock, flags); + if (value > 0) { + if (value > data->max_timeout) + value = data->max_timeout; + + hrtimer_start(&data->timer, + ns_to_ktime((u64)value * NSEC_PER_MSEC), + HRTIMER_MODE_REL); + } + spin_unlock_irqrestore(&data->lock, flags); +} + +static int __devinit isa1200_vibrator_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct isa1200_vibrator_platform_data *pdata = + client->dev.platform_data; + struct isa1200_vibrator_drvdata *ddata; + int ret = 0; + + ddata = kzalloc(sizeof(struct isa1200_vibrator_drvdata), GFP_KERNEL); + if (NULL == ddata) { + printk(KERN_ERR "[VIB] Failed to alloc memory\n"); + ret = -ENOMEM; + goto err_free_mem; + } + + ddata->client = client; + ddata->ctrl0 = pdata->ctrl0; + ddata->ctrl1 = pdata->ctrl1; + ddata->ctrl2 = pdata->ctrl2; + ddata->ctrl4 = pdata->ctrl4; + ddata->gpio_en = pdata->gpio_en; + if (pdata->get_clk) { + ddata->vib_clk = pdata->get_clk(); + if (IS_ERR(ddata->vib_clk)) { + pr_err("[VIB] Failed to request vib_clk.\n"); + ret = -EFAULT; + goto err_request_clk; + } + } else { + ddata->vib_clk = NULL; + ddata->pwm = pwm_request(pdata->pwm_id, "vibrator"); + if (IS_ERR(ddata->pwm)) { + pr_err("[VIB] Failed to request pwm.\n"); + ret = -EFAULT; + goto err_request_clk; + } + ddata->pwm_id = pdata->pwm_id; + ddata->pwm_duty = pdata->pwm_duty; + ddata->pwm_period = pdata->pwm_period; + } + ddata->max_timeout = pdata->max_timeout; + ddata->pll = pdata->pll; + ddata->duty = pdata->duty; + ddata->period = pdata->period; + + ddata->dev.name = "vibrator"; + ddata->dev.get_time = isa1200_vibrator_get_time; + ddata->dev.enable = isa1200_vibrator_enable; + + hrtimer_init(&ddata->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ddata->timer.function = isa1200_vibrator_timer_func; + INIT_WORK(&ddata->work, isa1200_vibrator_work); + spin_lock_init(&ddata->lock); + + i2c_set_clientdata(client, ddata); + isa1200_vibrator_hw_init(ddata); + + ret = timed_output_dev_register(&ddata->dev); + if (ret < 0) { + printk(KERN_ERR "[VIB] Failed to register timed_output : -%d\n", ret); + goto err_to_dev_reg; + } + + g_drvdata = ddata; + + return 0; + +err_to_dev_reg: +err_request_clk: + kfree(ddata); +err_free_mem: + return ret; + +} + +static int isa1200_vibrator_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct isa1200_vibrator_drvdata *ddata = i2c_get_clientdata(client); + ddata->gpio_en(false); + return 0; +} + +static int isa1200_vibrator_resume(struct i2c_client *client) +{ + struct isa1200_vibrator_drvdata *ddata = i2c_get_clientdata(client); + isa1200_vibrator_hw_init(ddata); + return 0; +} + +static const struct i2c_device_id isa1200_vibrator_device_id[] = { + {"isa1200_vibrator", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, isa1200_vibrator_device_id); + +static struct i2c_driver isa1200_vibrator_i2c_driver = { + .driver = { + .name = "isa1200_vibrator", + .owner = THIS_MODULE, + }, + .probe = isa1200_vibrator_i2c_probe, + .id_table = isa1200_vibrator_device_id, + .suspend = isa1200_vibrator_suspend, + .resume = isa1200_vibrator_resume, +}; + +static int __init isa1200_vibrator_init(void) +{ + return i2c_add_driver(&isa1200_vibrator_i2c_driver); +} + +static void __exit isa1200_vibrator_exit(void) +{ + i2c_del_driver(&isa1200_vibrator_i2c_driver); +} + +module_init(isa1200_vibrator_init); +module_exit(isa1200_vibrator_exit); |