From 396d412d2b1260904e7f5a62b28184f119f1d8c8 Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Mon, 23 May 2011 11:43:41 +0530 Subject: U8500 : driver for awaking modem on sim hot swap activity This driver uses GPIO to detect SIM hot swap and uses Modem Access Framework to wake up modem. ST-Ericsson Linux next: 336280 ST-Ericsson ID: 329459 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ib4b1f67422317850f5d859ddf4c5e457f4cb616c Signed-off-by: Bibek Basu Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23543 Reviewed-by: QATEST Reviewed-by: Jonas ABERG --- drivers/misc/sim_detect.c | 163 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 drivers/misc/sim_detect.c (limited to 'drivers/misc/sim_detect.c') diff --git a/drivers/misc/sim_detect.c b/drivers/misc/sim_detect.c new file mode 100644 index 00000000000..24862edfca7 --- /dev/null +++ b/drivers/misc/sim_detect.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: BIBEK BASU + * License terms: GNU General Public License (GPL) version 2 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* time in millisec */ +#define TIMER_DELAY 10 +struct sim_detect{ + struct work_struct timer_expired; + struct device *dev; + struct modem *modem; + struct hrtimer timer; +}; + +static void inform_modem_release(struct work_struct *work) +{ + struct sim_detect *sim_detect = + container_of(work, struct sim_detect, timer_expired); + + /* call Modem Access Framework api to release modem */ + modem_release(sim_detect->modem); +} + +static enum hrtimer_restart timer_callback(struct hrtimer *timer) +{ + struct sim_detect *sim_detect = + container_of(timer, struct sim_detect, timer); + + schedule_work(&sim_detect->timer_expired); + return HRTIMER_NORESTART; +} + +static irqreturn_t sim_activity_irq(int irq, void *dev) +{ + struct sim_detect *sim_detect = dev; + + /* call Modem Access Framework api to acquire modem */ + modem_request(sim_detect->modem); + /* start the timer for 10ms */ + hrtimer_start(&sim_detect->timer, + ktime_set(0, TIMER_DELAY*NSEC_PER_MSEC), + HRTIMER_MODE_REL); + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM +/** + * sim_detect_suspend() - This routine puts the Sim detect in to sustend state. + * @dev: pointer to device structure. + * + * This routine checks the current ongoing communication with Modem by + * examining the modem_get_usage and work_pending state. + * accordingly prevents suspend if modem communication + * is on-going. + */ +int sim_detect_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sim_detect *sim_detect = platform_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "%s called...\n", __func__); + /* if modem is accessed, event system suspend */ + if (modem_get_usage(sim_detect->modem) + || work_pending(&sim_detect->timer_expired)) + return -EBUSY; + else + return 0; +} + +static const struct dev_pm_ops sim_detect_dev_pm_ops = { + .suspend = sim_detect_suspend, +}; +#endif + +static int __devinit sim_detect_probe(struct platform_device *pdev) +{ + struct sim_detect_platform_data *plat = dev_get_platdata(&pdev->dev); + struct sim_detect *sim_detect; + int ret; + + sim_detect = kzalloc(sizeof(struct sim_detect), GFP_KERNEL); + if (sim_detect == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + sim_detect->dev = &pdev->dev; + INIT_WORK(&sim_detect->timer_expired, inform_modem_release); + hrtimer_init(&sim_detect->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + sim_detect->timer.function = timer_callback; + sim_detect->modem = modem_get(sim_detect->dev, "u8500-shrm-modem"); + if (IS_ERR(sim_detect->modem)) { + ret = PTR_ERR(sim_detect->modem); + dev_err(sim_detect->dev, "Could not retrieve the modem\n"); + goto out_free; + } + platform_set_drvdata(pdev, sim_detect); + ret = request_threaded_irq(plat->irq_num, + NULL, sim_activity_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "sim activity", sim_detect); + if (ret < 0) + goto out_free_irq; +out_free_irq: + modem_put(sim_detect->modem); + platform_set_drvdata(pdev, NULL); +out_free: + kfree(sim_detect); + return ret; +} + +static int __devexit sim_detect_remove(struct platform_device *pdev) +{ + struct sim_detect *sim_detect = platform_get_drvdata(pdev); + + modem_put(sim_detect->modem); + platform_set_drvdata(pdev, NULL); + kfree(sim_detect); + return 0; +} + +static struct platform_driver sim_detect_driver = { + .driver = { + .name = "sim_detect", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &sim_detect_dev_pm_ops, +#endif + }, + .probe = sim_detect_probe, + .remove = __devexit_p(sim_detect_remove), +}; + +static int __init sim_detect_init(void) +{ + return platform_driver_register(&sim_detect_driver); +} +module_init(sim_detect_init); + +static void __exit sim_detect_exit(void) +{ + platform_driver_unregister(&sim_detect_driver); +} +module_exit(sim_detect_exit); + +MODULE_AUTHOR("BIBEK BASU "); +MODULE_DESCRIPTION("Detects SIM Hot Swap and wakes modem"); +MODULE_ALIAS("SIM DETECT INTERRUPT driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From ea854ecfa34be44d61d9eac56b7f0bdf58ed0cee Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Thu, 23 Jun 2011 15:33:21 +0530 Subject: U8500: change GPIO for SIM activity detection driver This patch solves two things. One is to move to AB8500 GPIO for sim activity detection and another is to fix the missing return in driver probe. ST-Ericsson Linux next: 336280 ST-Ericsson ID: 349042 ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Bibek Basu Change-Id: Idd4c2b0877c9d8a1590b4e866efa61377963fb93 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25770 Reviewed-by: QATEST Reviewed-by: Jonas ABERG --- drivers/misc/sim_detect.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/misc/sim_detect.c') diff --git a/drivers/misc/sim_detect.c b/drivers/misc/sim_detect.c index 24862edfca7..c267fe95ee3 100644 --- a/drivers/misc/sim_detect.c +++ b/drivers/misc/sim_detect.c @@ -115,6 +115,7 @@ static int __devinit sim_detect_probe(struct platform_device *pdev) "sim activity", sim_detect); if (ret < 0) goto out_free_irq; + return 0; out_free_irq: modem_put(sim_detect->modem); platform_set_drvdata(pdev, NULL); -- cgit v1.2.3 From 696e439e1b69d6ac1ddbd2edada316df8e159bfb Mon Sep 17 00:00:00 2001 From: Bengt Jonsson Date: Tue, 4 Oct 2011 11:40:10 +0200 Subject: regulators: ab8500: Add support of low voltage battery Low voltage batteries have a wider voltage range with lower operating voltages. Some consumers in the platform may not work with the lower voltages and therefore need an extra regulator to boost the voltage in this case. This driver adds support for checking the consumers that need higher voltage (Vaux1, 2 and 3 regulators, 3 V SIM) and control the external buck/boost regulator accordingly. Note that to utilize the low voltage battery support, the battery voltage thresholds must be changed. This applies for the low battery voltage threshold of the battery manager and the OTP setting for the AB8500 BattOk levels. ST-Ericsson ID: 282517, 363432 ST-Ericsson Linux next: - ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ife07a622ec9748c027dbbd78b01e4ee7e92629ec Signed-off-by: Bengt Jonsson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33616 Reviewed-by: QABUILD --- drivers/misc/sim_detect.c | 144 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 2 deletions(-) (limited to 'drivers/misc/sim_detect.c') diff --git a/drivers/misc/sim_detect.c b/drivers/misc/sim_detect.c index c267fe95ee3..213645ca46d 100644 --- a/drivers/misc/sim_detect.c +++ b/drivers/misc/sim_detect.c @@ -2,29 +2,130 @@ * Copyright (C) ST-Ericsson SA 2011 * * Author: BIBEK BASU + * * License terms: GNU General Public License (GPL) version 2 */ + #include #include #include #include #include +#include #include #include #include #include #include #include +#include #include #include +#include /* time in millisec */ #define TIMER_DELAY 10 + struct sim_detect{ struct work_struct timer_expired; struct device *dev; struct modem *modem; struct hrtimer timer; + struct mutex lock; + int voltage; + struct regulator *vinvsim_regulator; + bool regulator_enabled; +}; + +static ssize_t show_voltage(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sim_detect *data = dev_get_drvdata(dev); + int ret, len; + + ret = mutex_lock_interruptible(&data->lock); + if (ret < 0) + return ret; + + len = sprintf(buf, "%i\n", data->voltage); + + mutex_unlock(&data->lock); + + return len; +} + +static ssize_t write_voltage(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sim_detect *sim_detect = dev_get_drvdata(dev); + long val; + int ret; + + /* check input */ + if (strict_strtol(buf, 0, &val) != 0) { + dev_err(dev, "Invalid voltage class configured.\n"); + return count; + } + + switch (val) { + case -1: + case 0: + case 1800000: + case 3000000: + break; + default: + dev_err(dev, "Invalid voltage class configured.\n"); + return count; + } + + /* lock */ + ret = mutex_lock_interruptible(&sim_detect->lock); + if (ret < 0) + return ret; + + /* update state */ + sim_detect->voltage = val; + + /* call regulator */ + switch (sim_detect->voltage) { + case 0: + /* SIM voltage is unknown, turn on regulator for 3 V SIM */ + case 3000000: + /* Vinvsim supply is used only for 3 V SIM */ + if (!sim_detect->regulator_enabled) { + ret = regulator_enable(sim_detect->vinvsim_regulator); + if (ret) { + dev_err(dev, "Failed to enable regulator.\n"); + goto out_unlock; + } + sim_detect->regulator_enabled = true; + } + break; + case 1800000: + case -1: + /* Vbatvsim is used otherwise */ + if (sim_detect->regulator_enabled) { + regulator_disable(sim_detect->vinvsim_regulator); + sim_detect->regulator_enabled = false; + } + } + +out_unlock: + /* unlock and return */ + mutex_unlock(&sim_detect->lock); + + return count; +} + +static DEVICE_ATTR(voltage, 0666, show_voltage, write_voltage); + +static struct attribute *sim_attributes[] = { + &dev_attr_voltage.attr, + NULL +}; + +static const struct attribute_group sim_attr_group = { + .attrs = sim_attributes, }; static void inform_modem_release(struct work_struct *work) @@ -87,6 +188,7 @@ static const struct dev_pm_ops sim_detect_dev_pm_ops = { }; #endif + static int __devinit sim_detect_probe(struct platform_device *pdev) { struct sim_detect_platform_data *plat = dev_get_platdata(&pdev->dev); @@ -98,25 +200,61 @@ static int __devinit sim_detect_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to allocate memory\n"); return -ENOMEM; } + + /* initialize data */ + mutex_init(&sim_detect->lock); + sim_detect->voltage = 0; + sim_detect->dev = &pdev->dev; INIT_WORK(&sim_detect->timer_expired, inform_modem_release); hrtimer_init(&sim_detect->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); sim_detect->timer.function = timer_callback; + sim_detect->modem = modem_get(sim_detect->dev, "u8500-shrm-modem"); if (IS_ERR(sim_detect->modem)) { ret = PTR_ERR(sim_detect->modem); dev_err(sim_detect->dev, "Could not retrieve the modem\n"); goto out_free; } + + /* set drvdata */ platform_set_drvdata(pdev, sim_detect); + + /* request irq */ ret = request_threaded_irq(plat->irq_num, NULL, sim_activity_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "sim activity", sim_detect); if (ret < 0) + goto out_put_modem; + + /* get regulator */ + sim_detect->regulator_enabled = false; + sim_detect->vinvsim_regulator = regulator_get(sim_detect->dev, + "vinvsim"); + if (IS_ERR(sim_detect->vinvsim_regulator)) { + dev_err(&pdev->dev, + "Failed to get regulator. (dev_name %s).\n", + dev_name(sim_detect->dev)); + ret = PTR_ERR(sim_detect->vinvsim_regulator); goto out_free_irq; + } + + /* register sysfs entry */ + ret = sysfs_create_group(&pdev->dev.kobj, &sim_attr_group); + if (ret != 0) { + dev_err(&pdev->dev, + "Failed to create attribute group: %d\n", ret); + goto out_free_regulator; + } + return 0; + +out_free_regulator: + regulator_put(sim_detect->vinvsim_regulator); out_free_irq: + free_irq(plat->irq_num, sim_detect); +out_put_modem: modem_put(sim_detect->modem); platform_set_drvdata(pdev, NULL); out_free: @@ -128,6 +266,8 @@ static int __devexit sim_detect_remove(struct platform_device *pdev) { struct sim_detect *sim_detect = platform_get_drvdata(pdev); + sysfs_remove_group(&pdev->dev.kobj, &sim_attr_group); + regulator_put(sim_detect->vinvsim_regulator); modem_put(sim_detect->modem); platform_set_drvdata(pdev, NULL); kfree(sim_detect); @@ -136,7 +276,7 @@ static int __devexit sim_detect_remove(struct platform_device *pdev) static struct platform_driver sim_detect_driver = { .driver = { - .name = "sim_detect", + .name = "sim-detect", .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &sim_detect_dev_pm_ops, @@ -160,5 +300,5 @@ module_exit(sim_detect_exit); MODULE_AUTHOR("BIBEK BASU "); MODULE_DESCRIPTION("Detects SIM Hot Swap and wakes modem"); -MODULE_ALIAS("SIM DETECT INTERRUPT driver"); +MODULE_ALIAS("platform:sim-detect"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3