summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBengt Jonsson <bengt.g.jonsson@stericsson.com>2011-10-04 11:40:10 +0200
committerMathieu J. Poirier <mathieu.poirier@linaro.org>2011-11-10 11:12:19 -0700
commitefb0869affb8282b5ac574e20ecd8856f123f430 (patch)
tree6f79edb9ca252af6093f55d7235a1336c515a012
parentb2d1f46c3cf65a49af1d59a2ba5274305364976e (diff)
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 <bengt.g.jonsson@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33616 Reviewed-by: QABUILD
-rw-r--r--drivers/misc/sim_detect.c144
1 files changed, 142 insertions, 2 deletions
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 <bibek.basu@stericsson.com>
+ *
* License terms: GNU General Public License (GPL) version 2
*/
+
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/err.h>
+#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/hrtimer.h>
#include <linux/workqueue.h>
+#include <linux/uaccess.h>
#include <linux/modem/modem_client.h>
#include <mach/sim_detect.h>
+#include <linux/regulator/consumer.h>
/* 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 <bibek.basu@stericsson.com>");
MODULE_DESCRIPTION("Detects SIM Hot Swap and wakes modem");
-MODULE_ALIAS("SIM DETECT INTERRUPT driver");
+MODULE_ALIAS("platform:sim-detect");
MODULE_LICENSE("GPL v2");