summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBengt Jonsson <bengt.g.jonsson@stericsson.com>2011-10-04 11:40:10 +0200
committerPhilippe Langlais <philippe.langlais@stericsson.com>2012-05-22 11:07:16 +0200
commit1f89a49680e3a9d341bd2497af4a5f3cd21d6e69 (patch)
tree360ccb67dcda4104af7e0d370d9395e4701c1223
parentcc63a79965890121c217828fec54be7fec83d662 (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");