diff options
Diffstat (limited to 'drivers/platform/sprd/vibrator.c')
| -rw-r--r-- | drivers/platform/sprd/vibrator.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/drivers/platform/sprd/vibrator.c b/drivers/platform/sprd/vibrator.c new file mode 100644 index 00000000..680d72ea --- /dev/null +++ b/drivers/platform/sprd/vibrator.c @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/hrtimer.h> +#include <../../../drivers/staging/android/timed_output.h> +#include <linux/sched.h> +#if (defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8)) +#include <soc/sprd/adi.h> +#include <soc/sprd/adc.h> +#else +#include <soc/sprd/hardware.h> +#include <soc/sprd/adi.h> +#include <soc/sprd/adc.h> +#endif + +#ifdef CONFIG_SC_VIBRATOR_GPIO +#include <linux/gpio.h> +#include <soc/sprd/board.h> +#elif defined(CONFIG_SC_VIBRATOR_POWER) +#include <linux/regulator/consumer.h> +#endif + +#ifdef CONFIG_ARCH_SCX35 +#include <soc/sprd/sci_glb_regs.h> + +#define ANA_VIBRATOR_CTRL0 (ANA_REG_GLB_VIBR_CTRL0) +#if defined(CONFIG_ADIE_SC2723) +#define BIT_VIBRATOR_PD BIT_LDO_VIBR_PD +#else +#define BIT_VIBRATOR_PD BIT_VIBR_PON +#define ANA_VIBRATOR_CTRL1 (ANA_REG_GLB_VIBR_CTRL1) +#define ANA_VIBRATOR_CTRL2 (ANA_REG_GLB_VIBR_CTRL2) +#define ANA_VIBR_WR_PROT (ANA_REG_GLB_VIBR_WR_PROT_VALUE) +#endif +#define VIBRATOR_REG_UNLOCK (0x1A2B) +#else +#define SPRD_ANA_BASE (SPRD_MISC_BASE + 0x600) +#define ANA_REG_BASE (SPRD_ANA_BASE) +#define ANA_VIBR_WR_PROT (ANA_REG_BASE + 0x90) +#define ANA_VIBRATOR_CTRL0 (ANA_REG_BASE + 0x74) +#define ANA_VIBRATOR_CTRL1 (ANA_REG_BASE + 0x78) +#define VIBRATOR_REG_UNLOCK (0xA1B2) +#endif +#define VIBRATOR_REG_LOCK ((~VIBRATOR_REG_UNLOCK) & 0xffff) +#define VIBRATOR_STABLE_LEVEL (4) +#ifdef CONFIG_ARCH_SCX15 +#define VIBRATOR_INIT_LEVEL (0x0f) +#define VIBRATOR_INIT_STATE_CNT (0x600) +#else +#define VIBRATOR_INIT_LEVEL (4) +#define VIBRATOR_INIT_STATE_CNT (10) +#endif + +#define VIBR_STABLE_V_SHIFT (12) +#define VIBR_STABLE_V_MSK (0x0f << VIBR_STABLE_V_SHIFT) +#define VIBR_INIT_V_SHIFT (8) +#define VIBR_INIT_V_MSK (0x0f << VIBR_INIT_V_SHIFT) +#define VIBR_V_BP_SHIFT (4) +#define VIBR_V_BP_MSK (0x0f << VIBR_V_BP_SHIFT) +#define VIBR_PD_RST (1 << 3) +#define VIBR_PD_SET (1 << 2) +#define VIBR_BP_EN (1 << 1) +#define VIBR_RTC_EN (1 << 0) + +#ifdef CONFIG_SS_FUNCTION +#define VIBR_PULLDOWN_EN (1 << 1) +#define LDO_VIBR_PD (1 << 8) +#endif + +#ifdef CONFIG_SC_VIBRATOR_POWER +static struct regulator *vibrator_regulator = NULL; +#endif + +static struct work_struct vibrator_work; +static struct hrtimer vibe_timer; +static int vibe_state = 0; + +#ifdef CONFIG_SS_FUNCTION +static void set_vibrator(int on) +{ + if (on) + { + printk("###############vibrator on##################\n"); + sci_adi_set((unsigned long)ANA_REG_GLB_BA_CTRL3, VIBR_PULLDOWN_EN); + sci_adi_clr((unsigned long)ANA_REG_GLB_BA_CTRL2, LDO_VIBR_PD); + } + else + { + printk("###############vibrator off##################\n"); + sci_adi_set((unsigned long)ANA_REG_GLB_BA_CTRL2, LDO_VIBR_PD); + sci_adi_clr((unsigned long)ANA_REG_GLB_BA_CTRL3, VIBR_PULLDOWN_EN); + } +} +static void vibrator_hw_init(void) +{ + return; +} +#else +static void set_vibrator(int on) +{ +#ifdef CONFIG_SC_VIBRATOR_GPIO + gpio_set_value(GPIO_VIBRATOR_INT, on); +#elif defined(CONFIG_SC_VIBRATOR_POWER) + if (on) { + regulator_enable(vibrator_regulator); + } + else { + regulator_disable(vibrator_regulator); + } +#else +#if !defined(CONFIG_ADIE_SC2723) + /* unlock vibrator registor */ + sci_adi_write((unsigned long)ANA_VIBR_WR_PROT, VIBRATOR_REG_UNLOCK, 0xffff); +#endif +#ifdef CONFIG_ARCH_SCX35 + if (on) + sci_adi_write((unsigned long)ANA_VIBRATOR_CTRL0, BIT_VIBRATOR_PD, BIT_VIBRATOR_PD); + else + sci_adi_write((unsigned long)ANA_VIBRATOR_CTRL0, 0, BIT_VIBRATOR_PD); +#else + sci_adi_clr((unsigned long)ANA_VIBRATOR_CTRL0, VIBR_PD_SET | VIBR_PD_RST); + if (on) + sci_adi_set((unsigned long)ANA_VIBRATOR_CTRL0, VIBR_PD_RST); + else + sci_adi_set((unsigned long)ANA_VIBRATOR_CTRL0, VIBR_PD_SET); +#endif +#if !defined(CONFIG_ADIE_SC2723) + /* lock vibrator registor */ + sci_adi_write((unsigned long)ANA_VIBR_WR_PROT, VIBRATOR_REG_LOCK, 0xffff); +#endif +#endif +} + +static void vibrator_hw_init(void) +{ +#ifdef CONFIG_SC_VIBRATOR_GPIO + gpio_request(GPIO_VIBRATOR_INT, "vibrator"); + gpio_direction_output(GPIO_VIBRATOR_INT, 0); +#elif defined(CONFIG_SC_VIBRATOR_POWER) + vibrator_regulator = regulator_get(NULL, "vddcammot"); + regulator_set_voltage(vibrator_regulator, 3300000, 3300000); +#else +#if !defined(CONFIG_ADIE_SC2723) + sci_adi_write((unsigned long)ANA_VIBR_WR_PROT, VIBRATOR_REG_UNLOCK, 0xffff); +#endif +#ifdef CONFIG_ARCH_SCX35 + sci_adi_write((unsigned long)ANA_REG_GLB_RTC_CLK_EN, BIT_RTC_VIBR_EN, BIT_RTC_VIBR_EN); +#else + sci_adi_set((unsigned long)ANA_VIBRATOR_CTRL0, VIBR_RTC_EN); + sci_adi_clr((unsigned long)ANA_VIBRATOR_CTRL0, VIBR_BP_EN); +#endif + /* set init current level */ + sci_adi_write((unsigned long)ANA_VIBRATOR_CTRL0, + (VIBRATOR_INIT_LEVEL << VIBR_INIT_V_SHIFT), + VIBR_INIT_V_MSK); + sci_adi_write((unsigned long)ANA_VIBRATOR_CTRL0, + (VIBRATOR_STABLE_LEVEL << VIBR_STABLE_V_SHIFT), + VIBR_STABLE_V_MSK); +#if !defined(CONFIG_ADIE_SC2723) + /* set stable current level */ + sci_adi_write((unsigned long)ANA_VIBRATOR_CTRL1, VIBRATOR_INIT_STATE_CNT, 0xffff); + sci_adi_write((unsigned long)ANA_VIBR_WR_PROT, VIBRATOR_REG_LOCK, 0xffff); +#endif +#endif +} + +#endif + +static void update_vibrator(struct work_struct *work) +{ + set_vibrator(vibe_state); +} + +static void vibrator_enable(struct timed_output_dev *dev, int value) +{ + + hrtimer_cancel(&vibe_timer); + + if (value == 0) + vibe_state = 0; + else { + value = (value > 15000) ? 15000 : value; + vibe_state = 1; + hrtimer_start(&vibe_timer, + ktime_set(value / 1000, (value % 1000) * 1000000), + HRTIMER_MODE_REL); + } + + schedule_work(&vibrator_work); +} + +static int vibrator_get_time(struct timed_output_dev *dev) +{ + ktime_t re; + + if (hrtimer_active(&vibe_timer)) { + re = hrtimer_get_remaining(&vibe_timer); + return ktime_to_ns(re); + } else + return 0; +} + +static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) +{ + vibe_state = 0; + schedule_work(&vibrator_work); + + return HRTIMER_NORESTART; +} + +static struct timed_output_dev sprd_vibrator = { + .name = "vibrator", + .get_time = vibrator_get_time, + .enable = vibrator_enable, +}; + +static int __init sprd_init_vibrator(void) +{ + vibrator_hw_init(); + + INIT_WORK(&vibrator_work, update_vibrator); + vibe_state = 0; + hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + vibe_timer.function = vibrator_timer_func; + + timed_output_dev_register(&sprd_vibrator); + + return 0; +} +#ifdef CONFIG_OF +late_initcall(sprd_init_vibrator); +#else +module_init(sprd_init_vibrator); +#endif +MODULE_DESCRIPTION("vibrator driver for spreadtrum Processors"); +MODULE_LICENSE("GPL"); |
