From 4af8850368fc894f920e504e5569779d179940f6 Mon Sep 17 00:00:00 2001 From: Shreshtha Kumar Sahu Date: Tue, 30 Aug 2011 08:58:33 +0100 Subject: u5500: timed output driver for ab5500 vibrator Simple timed output vibrator driver for AB5500 MFD chips. This chip supports Rotary and Linear vibrator hardware types. Resonance frequency of 100-198 Hz is supported. ST-Ericsson ID: WP 256408 Signed-off-by: Avinash A Signed-off-by: Robert Marklund u5500: enable vibrator when new timeout is requested with this patch new timeout value will be accepted/updated even if vibrator is already executing. ST-Ericsson ID: 360178 Signed-off-by: Avinash A vibra:Remove unnecessary assignment of dev->parent Remove unnecessary assignment of dev->parent, because of this during timed out class unregister it is trying to free resources of parent which does not exist ST-Ericsson ID: 410065 Signed-off-by: Naga Radhesh u5500: Add vibra end of life support Detect and disable vibra when vibra end of life(eol) condition is detected at boot sequence. ST-Ericsson ID: 265890 Signed-off-by: Rajagopala V --- drivers/mfd/ab5500-gpadc.c | 15 + drivers/staging/android/Kconfig | 10 + drivers/staging/android/Makefile | 1 + drivers/staging/android/ab5500-timed-vibra.c | 490 +++++++++++++++++++++++++++ include/linux/ab5500-vibra.h | 57 ++++ include/linux/mfd/abx500/ab5500-gpadc.h | 1 + 6 files changed, 574 insertions(+) create mode 100644 drivers/staging/android/ab5500-timed-vibra.c create mode 100644 include/linux/ab5500-vibra.h diff --git a/drivers/mfd/ab5500-gpadc.c b/drivers/mfd/ab5500-gpadc.c index 4718e26bee8..fe05ffbadfd 100644 --- a/drivers/mfd/ab5500-gpadc.c +++ b/drivers/mfd/ab5500-gpadc.c @@ -99,6 +99,7 @@ #define MIN_INDEX 0x02 #define MAX_INDEX 0x03 #define CTRL_INDEX 0x01 +#define KELVIN_SCALE_VOL45 0x00 /* GPADC constants from AB5500 spec */ #define GPADC0_MIN 0 @@ -129,6 +130,8 @@ #define USBID_MAX 1800 #define KELVIN_MIN 0 #define KELVIN_MAX 4500 +#define VIBRA_MIN 0 +#define VIBRA_MAX 4500 /* This is used for calibration */ #define ADC_RESOLUTION 1023 @@ -253,7 +256,9 @@ struct adc_data adc_tab[] = { ADC_DATA(ACC_DETECT3, 0x19, ACCDET3_MIN, ACCDET3_MAX, 0), ADC_DATA(MAIN_BAT_V_TRIG_MIN, 0x0C, VBAT_MIN, VBAT_MAX, 0), ADC_DATA(MAIN_BAT_V_TXON_TRIG_MIN, 0x0C, VBAT_MIN, VBAT_MAX, 0), + ADC_DATA(VIBRA_KELVIN, 0x16, VIBRA_MIN, VIBRA_MAX, 0), }; + /** * ab5500_gpadc_get() - returns a reference to the primary AB5500 GPADC * (i.e. the first GPADC in the instance list) @@ -279,6 +284,7 @@ static int ab5500_gpadc_ad_to_voltage(struct ab5500_gpadc *gpadc, int res; switch (in) { + case VIBRA_KELVIN: case GPADC0_V: case PCB_TEMP: case BTEMP_BALL: @@ -404,6 +410,15 @@ int ab5500_gpadc_convert(struct ab5500_gpadc *gpadc, u8 input) goto out; } break; + case VIBRA_KELVIN: + ret = abx500_set_register_interruptible(gpadc->dev, + AB5500_BANK_ADC, AB5500_GPADC_KELVIN_CTRL, + KELVIN_SCALE_VOL45); + if (ret < 0) { + dev_err(gpadc->dev, "gpadc: fail to set kelv scale\n"); + goto out; + } + break; case USB_CHARGER_C: case VBUS_V: case BK_BAT_V: diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 7823c2a19ed..a4495997d1b 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -8,6 +8,16 @@ config ANDROID if ANDROID +config ANDROID_AB5500_TIMED_VIBRA + bool "AB5500 Timed Output Vibrator" + depends on AB5500_CORE + depends on ANDROID_TIMED_OUTPUT + default y + help + Say Y here to enable linear/rotary vibrator driver using timed + output class device for ST-Ericsson's based on ST-Ericsson's + AB5500 Mix-Sig PMIC + config ANDROID_BINDER_IPC bool "Android Binder IPC Driver" default n diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index e529c63da57..0d642936a0b 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -1,3 +1,4 @@ +obj-$(CONFIG_ANDROID_AB5500_TIMED_VIBRA) += ab5500-timed-vibra.o obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o obj-$(CONFIG_ASHMEM) += ashmem.o obj-$(CONFIG_ANDROID_LOGGER) += logger.o diff --git a/drivers/staging/android/ab5500-timed-vibra.c b/drivers/staging/android/ab5500-timed-vibra.c new file mode 100644 index 00000000000..35c627a6285 --- /dev/null +++ b/drivers/staging/android/ab5500-timed-vibra.c @@ -0,0 +1,490 @@ +/* + * ab5500-vibra.c - driver for vibrator in ST-Ericsson AB5500 chip + * + * Copyright (C) 2011 ST-Ericsson SA. + * + * License Terms: GNU General Public License v2 + * + * Author: Shreshtha Kumar SAHU + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "timed_output.h" + +#include /* abx500_* */ +#include +#include +#include + +#define AB5500_VIBRA_DEV_NAME "ab5500:vibra" +#define AB5500_VIBRA_DRV_NAME "ab5500-vibrator" + +/* Vibrator Register Address Offsets */ +#define AB5500_VIB_CTRL 0x10 +#define AB5500_VIB_VOLT 0x11 +#define AB5500_VIB_FUND_FREQ 0x12 /* Linear vibra resonance freq. */ +#define AB5500_VIB_FUND_DUTY 0x13 +#define AB5500_KELVIN_ANA 0xB1 +#define AB5500_VIBRA_KELVIN 0xFE + +/* Vibrator Control */ +#define AB5500_VIB_DISABLE (0x80) +#define AB5500_VIB_PWR_ON (0x40) +#define AB5500_VIB_FUND_EN (0x20) +#define AB5500_VIB_FREQ_SHIFT (0) +#define AB5500_VIB_DUTY_SHIFT (3) +#define AB5500_VIB_VOLT_SHIFT (0) +#define AB5500_VIB_PULSE_SHIFT (4) +#define VIBRA_KELVIN_ENABLE (0x90) +#define VIBRA_KELVIN_VOUT (0x20) + +/* Vibrator Freq. (in HZ) and Duty */ +enum ab5500_vibra_freq { + AB5500_VIB_FREQ_1HZ = 1, + AB5500_VIB_FREQ_2HZ, + AB5500_VIB_FREQ_4HZ, + AB5500_VIB_FREQ_8HZ, +}; + +enum ab5500_vibra_duty { + AB5500_VIB_DUTY_100 = 0, + AB5500_VIB_DUTY_75 = 8, + AB5500_VIB_DUTY_50 = 16, + AB5500_VIB_DUTY_25 = 24, +}; + +/* Linear vibrator resonance freq. duty */ +#define AB5500_VIB_RDUTY_50 (0x7F) + +/* Vibration magnitudes */ +#define AB5500_VIB_FREQ_MAX (4) +#define AB5500_VIB_DUTY_MAX (4) + +static u8 vib_freq[AB5500_VIB_FREQ_MAX] = { + AB5500_VIB_FREQ_1HZ, + AB5500_VIB_FREQ_2HZ, + AB5500_VIB_FREQ_4HZ, + AB5500_VIB_FREQ_8HZ, +}; + +static u8 vib_duty[AB5500_VIB_DUTY_MAX] = { + AB5500_VIB_DUTY_100, + AB5500_VIB_DUTY_75, + AB5500_VIB_DUTY_50, + AB5500_VIB_DUTY_25, +}; + +/** + * struct ab5500_vibra - Vibrator driver interal info. + * @tdev: Pointer to timed output device structure + * @dev: Reference to vibra device structure + * @vibra_workqueue: Pointer to vibrator workqueue structure + * @vibra_work: Vibrator work + * @gpadc: Gpadc instance + * @vibra_wait: Vibrator wait queue head + * @vibra_lock: Vibrator lock + * @timeout_ms: Indicates how long time the vibrator will be enabled + * @timeout_start: Start of vibrator in jiffies + * @pdata: Local pointer to platform data with vibrator parameters + * @magnitude: required vibration strength + * @enable: Vibrator running status + * @eol: Vibrator end of life(eol) status + **/ +struct ab5500_vibra { + struct timed_output_dev tdev; + struct device *dev; + struct workqueue_struct *vibra_workqueue; + struct work_struct vibra_work; + struct ab5500_gpadc *gpadc; + wait_queue_head_t vibra_wait; + spinlock_t vibra_lock; + unsigned int timeout_ms; + unsigned long timeout_start; + struct ab5500_vibra_platform_data *pdata; + u8 magnitude; + bool enable; + bool eol; +}; + +static inline u8 vibra_magnitude(u8 mag) +{ + mag /= (AB5500_VIB_FREQ_MAX * AB5500_VIB_DUTY_MAX); + mag = vib_freq[mag / AB5500_VIB_FREQ_MAX] << AB5500_VIB_FREQ_SHIFT; + mag |= vib_duty[mag % AB5500_VIB_DUTY_MAX] << AB5500_VIB_DUTY_SHIFT; + + return mag; +} + +static int ab5500_setup_vibra_kelvin(struct ab5500_vibra* vibra) +{ + int ret; + + /* Establish the kelvin IP connection to be measured */ + ret = abx500_set_register_interruptible(vibra->dev, + AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, + AB5500_KELVIN_ANA, VIBRA_KELVIN_ENABLE); + if (ret < 0) { + dev_err(vibra->dev, "failed to set kelvin network\n"); + return ret; + } + + /* Select vibra parameter to be measured */ + ret = abx500_set_register_interruptible(vibra->dev, AB5500_BANK_VIBRA, + AB5500_VIBRA_KELVIN, VIBRA_KELVIN_VOUT); + if (ret < 0) + dev_err(vibra->dev, "failed to select the kelvin param\n"); + + return ret; +} + +static int ab5500_vibra_start(struct ab5500_vibra* vibra) +{ + u8 ctrl = 0; + + ctrl = AB5500_VIB_PWR_ON | + vibra_magnitude(vibra->magnitude); + + if (vibra->pdata->type == AB5500_VIB_LINEAR) + ctrl |= AB5500_VIB_FUND_EN; + + return abx500_set_register_interruptible(vibra->dev, + AB5500_BANK_VIBRA, AB5500_VIB_CTRL, ctrl); +} + +static int ab5500_vibra_stop(struct ab5500_vibra* vibra) +{ + return abx500_mask_and_set_register_interruptible(vibra->dev, + AB5500_BANK_VIBRA, AB5500_VIB_CTRL, + AB5500_VIB_PWR_ON, 0); +} + +static int ab5500_vibra_eol_check(struct ab5500_vibra* vibra) +{ + int ret, vout; + + ret = ab5500_setup_vibra_kelvin(vibra); + if (ret < 0) { + dev_err(vibra->dev, "failed to setup kelvin network\n"); + return ret; + } + + /* Start vibra to measure voltage */ + ret = ab5500_vibra_start(vibra); + if (ret < 0) { + dev_err(vibra->dev, "failed to start vibra\n"); + return ret; + } + /* 20ms delay required for voltage rampup */ + wait_event_interruptible_timeout(vibra->vibra_wait, + 0, msecs_to_jiffies(20)); + + vout = ab5500_gpadc_convert(vibra->gpadc, VIBRA_KELVIN); + if (vout < 0) { + dev_err(vibra->dev, "failed to read gpadc vibra\n"); + return vout; + } + + /* Stop vibra after measuring voltage */ + ret = ab5500_vibra_stop(vibra); + if (ret < 0) { + dev_err(vibra->dev, "failed to stop vibra\n"); + return ret; + } + /* Check for vibra eol condition */ + if (vout < vibra->pdata->eol_voltage) { + vibra->eol = true; + dev_err(vibra->dev, "Vibra eol detected. Disabling vibra!\n"); + } + + return ret; +} + +/** + * ab5500_vibra_work() - Vibrator work, turns on/off vibrator + * @work: Pointer to work structure + * + * This function is called from workqueue, turns on/off vibrator + **/ +static void ab5500_vibra_work(struct work_struct *work) +{ + struct ab5500_vibra *vibra = container_of(work, + struct ab5500_vibra, vibra_work); + unsigned long flags; + int ret; + + ret = ab5500_vibra_start(vibra); + if (ret < 0) + dev_err(vibra->dev, "reg[%d] w failed: %d\n", + AB5500_VIB_CTRL, ret); + + wait_event_interruptible_timeout(vibra->vibra_wait, + 0, msecs_to_jiffies(vibra->timeout_ms)); + + ret = ab5500_vibra_stop(vibra); + if (ret < 0) + dev_err(vibra->dev, "reg[%d] w failed: %d\n", + AB5500_VIB_CTRL, ret); + + spin_lock_irqsave(&vibra->vibra_lock, flags); + + vibra->timeout_start = 0; + vibra->enable = false; + + spin_unlock_irqrestore(&vibra->vibra_lock, flags); +} + +/** + * vibra_enable() - Enables vibrator + * @tdev: Pointer to timed output device structure + * @timeout: Time indicating how long vibrator will be enabled + * + * This function enables vibrator + **/ +static void vibra_enable(struct timed_output_dev *tdev, int timeout) +{ + struct ab5500_vibra *vibra = dev_get_drvdata(tdev->dev); + unsigned long flags; + + spin_lock_irqsave(&vibra->vibra_lock, flags); + + if ((!vibra->enable || timeout) && !vibra->eol) { + vibra->enable = true; + + vibra->timeout_ms = timeout; + vibra->timeout_start = jiffies; + queue_work(vibra->vibra_workqueue, &vibra->vibra_work); + } + + spin_unlock_irqrestore(&vibra->vibra_lock, flags); +} + +/** + * vibra_get_time() - Returns remaining time to disabling vibration + * @tdev: Pointer to timed output device structure + * + * This function returns time remaining to disabling vibration + * + * Returns: + * Returns remaining time to disabling vibration + **/ +static int vibra_get_time(struct timed_output_dev *tdev) +{ + struct ab5500_vibra *vibra = dev_get_drvdata(tdev->dev); + unsigned int ms; + unsigned long flags; + + spin_lock_irqsave(&vibra->vibra_lock, flags); + + if (vibra->enable) + ms = jiffies_to_msecs(vibra->timeout_start + + msecs_to_jiffies(vibra->timeout_ms) - jiffies); + else + ms = 0; + + spin_unlock_irqrestore(&vibra->vibra_lock, flags); + + return ms; +} + +static int ab5500_vibra_reg_init(struct ab5500_vibra *vibra) +{ + int ret = 0; + u8 ctrl = 0; + u8 pulse = 0; + + ctrl = (AB5500_VIB_DUTY_50 << AB5500_VIB_DUTY_SHIFT) | + (AB5500_VIB_FREQ_8HZ << AB5500_VIB_FREQ_SHIFT); + + if (vibra->pdata->type == AB5500_VIB_LINEAR) { + ctrl |= AB5500_VIB_FUND_EN; + + if (vibra->pdata->voltage > AB5500_VIB_VOLT_MAX) + vibra->pdata->voltage = AB5500_VIB_VOLT_MAX; + + pulse = (vibra->pdata->pulse << AB5500_VIB_PULSE_SHIFT) | + (vibra->pdata->voltage << AB5500_VIB_VOLT_SHIFT); + ret = abx500_set_register_interruptible(vibra->dev, + AB5500_BANK_VIBRA, AB5500_VIB_VOLT, + pulse); + if (ret < 0) { + dev_err(vibra->dev, + "reg[%#x] w %#x failed: %d\n", + AB5500_VIB_VOLT, vibra->pdata->voltage, ret); + return ret; + } + + ret = abx500_set_register_interruptible(vibra->dev, + AB5500_BANK_VIBRA, AB5500_VIB_FUND_FREQ, + vibra->pdata->res_freq); + if (ret < 0) { + dev_err(vibra->dev, "reg[%#x] w %#x failed: %d\n", + AB5500_VIB_FUND_FREQ, + vibra->pdata->res_freq, ret); + return ret; + } + + ret = abx500_set_register_interruptible(vibra->dev, + AB5500_BANK_VIBRA, AB5500_VIB_FUND_DUTY, + AB5500_VIB_RDUTY_50); + if (ret < 0) { + dev_err(vibra->dev, "reg[%#x] w %#x failed: %d\n", + AB5500_VIB_FUND_DUTY, + AB5500_VIB_RDUTY_50, ret); + return ret; + } + } + + ret = abx500_set_register_interruptible(vibra->dev, + AB5500_BANK_VIBRA, AB5500_VIB_CTRL, ctrl); + if (ret < 0) { + dev_err(vibra->dev, "reg[%#x] w %#x failed: %d\n", + AB5500_VIB_CTRL, ctrl, ret); + return ret; + } + + return ret; +} + +static int ab5500_vibra_register_dev(struct ab5500_vibra *vibra, + struct platform_device *pdev) +{ + int ret = 0; + + ret = timed_output_dev_register(&vibra->tdev); + if (ret) { + dev_err(&pdev->dev, "failed to register timed output device\n"); + goto err_out; + } + + dev_set_drvdata(vibra->tdev.dev, vibra); + + + /* Create workqueue just for timed output vibrator */ + vibra->vibra_workqueue = + create_singlethread_workqueue("ste-timed-output-vibra"); + if (!vibra->vibra_workqueue) { + dev_err(&pdev->dev, "failed to allocate workqueue\n"); + ret = -ENOMEM; + goto exit_output_unregister; + } + + init_waitqueue_head(&vibra->vibra_wait); + INIT_WORK(&vibra->vibra_work, ab5500_vibra_work); + spin_lock_init(&vibra->vibra_lock); + + platform_set_drvdata(pdev, vibra); + + return ret; + +exit_output_unregister: + timed_output_dev_unregister(&vibra->tdev); +err_out: + return ret; +} + +static int __devinit ab5500_vibra_probe(struct platform_device *pdev) +{ + struct ab5500_vibra_platform_data *pdata = pdev->dev.platform_data; + struct ab5500_vibra *vibra = NULL; + int ret = 0; + + if (pdata == NULL) { + dev_err(&pdev->dev, "platform data required. Quitting...\n"); + return -ENODEV; + } + + vibra = kzalloc(sizeof(struct ab5500_vibra), GFP_KERNEL); + if (vibra == NULL) + return -ENOMEM; + + vibra->tdev.name = "vibrator"; + vibra->tdev.enable = vibra_enable; + vibra->tdev.get_time = vibra_get_time; + vibra->timeout_start = 0; + vibra->enable = false; + vibra->magnitude = pdata->magnitude; + vibra->pdata = pdata; + vibra->dev = &pdev->dev; + + if (vibra->pdata->eol_voltage) { + vibra->gpadc = ab5500_gpadc_get("ab5500-adc.0"); + if (IS_ERR(vibra->gpadc)) + goto err_alloc; + } + + if (vibra->pdata->type == AB5500_VIB_LINEAR) + dev_info(&pdev->dev, "Linear Type Vibrators\n"); + else + dev_info(&pdev->dev, "Rotary Type Vibrators\n"); + + ret = ab5500_vibra_reg_init(vibra); + if (ret < 0) + goto err_alloc; + + ret = ab5500_vibra_register_dev(vibra, pdev); + if (ret < 0) + goto err_alloc; + + /* Perform vibra eol diagnostics if eol_voltage is set */ + if (vibra->pdata->eol_voltage) { + ret = ab5500_vibra_eol_check(vibra); + if (ret < 0) + dev_warn(&pdev->dev, "EOL check failed\n"); + } + + dev_info(&pdev->dev, "initialization success\n"); + + return ret; + +err_alloc: + kfree(vibra); + + return ret; +} + +static int __devexit ab5500_vibra_remove(struct platform_device *pdev) +{ + struct ab5500_vibra *vibra = platform_get_drvdata(pdev); + + timed_output_dev_unregister(&vibra->tdev); + destroy_workqueue(vibra->vibra_workqueue); + kfree(vibra); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver ab5500_vibra_driver = { + .probe = ab5500_vibra_probe, + .remove = __devexit_p(ab5500_vibra_remove), + .driver = { + .name = AB5500_VIBRA_DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init ab5500_vibra_module_init(void) +{ + return platform_driver_register(&ab5500_vibra_driver); +} + +static void __exit ab5500_vibra_module_exit(void) +{ + platform_driver_unregister(&ab5500_vibra_driver); +} + +module_init(ab5500_vibra_module_init); +module_exit(ab5500_vibra_module_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Shreshtha Kumar SAHU "); +MODULE_DESCRIPTION("Timed Output Driver for AB5500 Vibrator"); + diff --git a/include/linux/ab5500-vibra.h b/include/linux/ab5500-vibra.h new file mode 100644 index 00000000000..e85048dd801 --- /dev/null +++ b/include/linux/ab5500-vibra.h @@ -0,0 +1,57 @@ +/*---------------------------------------------------------------------------- + * ab5500-vibra.h header file for ab5500 vibrator driver + * + * Copyright (C) 2011 ST-Ericsson SA. + * + * License Terms: GNU General Public License v2 + * + * Author: Shreshtha Kumar SAHU + * + */ + +#ifndef __AB5500_VIBRA_H__ +#define __AB5500_VIBRA_H__ + +enum ab5500_vibra_type { + AB5500_VIB_ROTARY, + AB5500_VIB_LINEAR, +}; + +/* Vibrator Voltage */ +#define AB5500_VIB_VOLT_MIN (0x00) /* 1.3 Volt */ +#define AB5500_VIB_VOLT_MAX (0x0A) /* 3.5 Volt */ +#define AB5500_VIB_VOLT_STEP (0x01) /* 0.2 Volt */ + + +/* Linear Vibrator Resonance Frequncy */ +#define AB5500_VIB_RFREQ_100HZ (0xFB) +#define AB5500_VIB_RFREQ_150HZ (0x52) +#define AB5500_VIB_RFREQ_196HZ (0x03) + +/* Vibrator pulse duration in milliseconds */ +enum ab5500_vibra_pulse { + AB5500_VIB_PULSE_OFF, + AB5500_VIB_PULSE_20ms, + AB5500_VIB_PULSE_75ms, + AB5500_VIB_PULSE_130ms, + AB5500_VIB_PULSE_170ms, +}; + +/** + * struct ab5500_vibra_platform_data + * @voltage: Vibra output voltage + * @res_freq: Linear vibra resonance freq. + * @type: Vibra HW type + * @pulse: Vibra pulse duration in ms + * @eol_voltage: EOL voltage in mV + */ +struct ab5500_vibra_platform_data { + u8 voltage; + u8 res_freq; + u8 magnitude; + enum ab5500_vibra_type type; + enum ab5500_vibra_pulse pulse; + int eol_voltage; +}; + +#endif /* __AB5500_VIBRA_H__ */ diff --git a/include/linux/mfd/abx500/ab5500-gpadc.h b/include/linux/mfd/abx500/ab5500-gpadc.h index 67dc3cc9034..8bddafc7f79 100644 --- a/include/linux/mfd/abx500/ab5500-gpadc.h +++ b/include/linux/mfd/abx500/ab5500-gpadc.h @@ -34,6 +34,7 @@ /* VBAT with TX off only min trigger */ #define MAIN_BAT_V_TRIG_MIN 14 #define GPADC0_V 15 +#define VIBRA_KELVIN 16 /* * Frequency of auto adc conversion -- cgit v1.2.3