summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorGary King <gking@nvidia.com>2010-05-28 14:49:26 -0700
committerGary King <gking@nvidia.com>2010-05-28 15:01:01 -0700
commit7aabd805f4869ad992bb0506ea98b30d26195cef (patch)
treed6f77e87ba6d4507bee95a5eebebd70c12584204 /drivers
parent50d0593fb729f5995d07f47a69593743d9010646 (diff)
power: add battery driver for tegra PMU ODM kit
registers itself with the same "tegra_battery" driver name as the NvEc battery, so disallow platforms from selecting both the ODM battery and the NvEc battery fix the nvec_battery driver compilation to only occur when it is selected, rather than whenever the NvEc infrastructure is enabled Change-Id: I9716824d0882d8cd8e693ebdad6bcc96c3f035c5 Reviewed-on: http://git-master/r/1822 Reviewed-by: Gary King <gking@nvidia.com> Tested-by: Gary King <gking@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/power/Kconfig9
-rw-r--r--drivers/power/Makefile3
-rw-r--r--drivers/power/tegra_odm_battery.c492
3 files changed, 502 insertions, 2 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index e89c62ef7214..51e9fa59d629 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -104,9 +104,16 @@ config BATTERY_MAX17040
in handheld and portable equipment. The MAX17040 is configured
to operate with a single lithium cell
+config TEGRA_BATTERY_ODM
+ boolean "NVIDIA Tegra ODM kit battery driver (PMU controlled)"
+ depends on ARCH_TEGRA && TEGRA_ODM_KIT
+ help
+ Say Y to enable battery support for Tegra using the NVIDIA ODM kit
+ PMU adaptation interface
+
config TEGRA_BATTERY_NVEC
boolean "Battery connected to NVIDIA Tegra NvEc embedded controller"
- depends on ARCH_TEGRA && TEGRA_NVEC
+ depends on ARCH_TEGRA && TEGRA_NVEC && !TEGRA_BATTERY_ODM
help
Say Y to include battery support for batteries connected to an
NVIDIA Tegra NvEc-compliant embedded controller
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 552ae086893c..8d1f27f358fb 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -28,5 +28,6 @@ obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
-obj-$(CONFIG_TEGRA_NVEC) += nvec_battery.o
+obj-$(CONFIG_TEGRA_BATTERY_ODM) += tegra_odm_battery.o
+obj-$(CONFIG_TEGRA_BATTERY_NVEC) += nvec_battery.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
diff --git a/drivers/power/tegra_odm_battery.c b/drivers/power/tegra_odm_battery.c
new file mode 100644
index 000000000000..9f77f1ff0702
--- /dev/null
+++ b/drivers/power/tegra_odm_battery.c
@@ -0,0 +1,492 @@
+/*
+ * drivers/power/tegra_odm_battery.c
+ *
+ * Battery driver for batteries implemented using NVIDIA Tegra ODM kit PMU
+ * adaptation interface
+ *
+ * Copyright (c) 2009, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#define NV_DEBUG 0
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/power_supply.h>
+#include <linux/wakelock.h>
+
+#include "nvcommon.h"
+#include "nvos.h"
+#include "nvrm_pmu.h"
+#include "mach/nvrm_linux.h" // for s_hRmGlobal
+
+#define NVBATTERY_POLLING_INTERVAL 30000 /* 30 Seconds */
+
+typedef enum {
+ NvCharger_Type_Battery = 0,
+ NvCharger_Type_USB,
+ NvCharger_Type_AC,
+ NvCharger_Type_Num,
+ NvCharger_Type_Force32 = 0x7FFFFFFF
+} NvCharger_Type;
+
+typedef enum {
+ NvCharge_Control_Charging_Disable = 0,
+ NvCharge_Control_Charging_Enable,
+ NvCharge_Control_Num,
+ NvCharge_Control_Force32 = 0x7FFFFFFF
+} NvCharge_Control;
+
+static enum power_supply_property tegra_battery_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_TEMP
+};
+
+static enum power_supply_property tegra_power_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *supply_list[] = {
+ "battery",
+};
+
+static int tegra_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp, union power_supply_propval *val);
+
+static int tegra_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp, union power_supply_propval *val);
+
+static struct power_supply tegra_supplies[] = {
+ {
+ .name = "battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = tegra_battery_properties,
+ .num_properties = ARRAY_SIZE(tegra_battery_properties),
+ .get_property = tegra_battery_get_property,
+ },
+ {
+ .name = "usb",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .supplied_to = supply_list,
+ .num_supplicants = ARRAY_SIZE(supply_list),
+ .properties = tegra_power_properties,
+ .num_properties = ARRAY_SIZE(tegra_power_properties),
+ .get_property = tegra_power_get_property,
+ },
+ {
+ .name = "ac",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .supplied_to = supply_list,
+ .num_supplicants = ARRAY_SIZE(supply_list),
+ .properties = tegra_power_properties,
+ .num_properties = ARRAY_SIZE(tegra_power_properties),
+ .get_property = tegra_power_get_property,
+ },
+};
+
+typedef struct tegra_battery_dev {
+ struct timer_list battery_poll_timer;
+ NvU32 batt_id; /* battery ID from ADC */
+ NvU32 batt_vol; /* voltage from ADC */
+ NvU32 batt_temp; /* temperature (degrees C) */
+ NvU32 batt_current; /* current from ADC */
+ NvU32 charging_source; /* 0: no cable, 1:usb, 2:AC */
+ NvU32 charging_enabled; /* 0: Disable, 1: Enable */
+ NvU32 full_bat; /* max capacity of battery (mAh) */
+ NvU32 BatteryLifePercent;
+ NvU32 BatteryLifeTime;
+ NvU32 BatteryMahConsumed;
+ NvU32 ACLineStatus;
+ NvU32 batt_status_poll_period;
+ NvBool present;
+} tegra_battery_dev;
+
+static tegra_battery_dev *batt_dev;
+
+static ssize_t tegra_battery_show_property(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", batt_dev->batt_status_poll_period);
+}
+
+static ssize_t tegra_battery_store_property(
+ struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ unsigned int value = 0;
+ value = simple_strtoul(buf, NULL, 0);
+ batt_dev->batt_status_poll_period = value;
+ mod_timer(&(batt_dev->battery_poll_timer),
+ jiffies + msecs_to_jiffies(batt_dev->batt_status_poll_period));
+ return count;
+}
+
+static struct device_attribute tegra_battery_attr = {
+ .attr = { .name = "status_poll_period", .mode = S_IRUGO | S_IWUSR,
+ .owner = THIS_MODULE },
+ .show = tegra_battery_show_property,
+ .store = tegra_battery_store_property,
+};
+
+static void tegra_get_battery_tech(int *Value,
+ NvRmPmuBatteryInstance NvBatteryInst)
+{
+ NvRmPmuBatteryChemistry Chemistry = {0};
+
+ NvRmPmuGetBatteryChemistry(s_hRmGlobal, NvBatteryInst, &Chemistry);
+
+ switch(Chemistry)
+ {
+ case NvRmPmuBatteryChemistry_NICD:
+ *Value = POWER_SUPPLY_TECHNOLOGY_NiCd;
+ break;
+
+ case NvRmPmuBatteryChemistry_NIMH:
+ *Value = POWER_SUPPLY_TECHNOLOGY_NiMH;
+ break;
+
+ case NvRmPmuBatteryChemistry_LION:
+ *Value = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+
+ case NvRmPmuBatteryChemistry_LIPOLY:
+ *Value = POWER_SUPPLY_TECHNOLOGY_LIPO;
+ break;
+
+ default:
+ *Value = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+ break;
+ }
+}
+
+static void tegra_battery_convert(NvRmPmuBatteryData *pPmuData)
+{
+ if (pPmuData->batteryLifePercent == NVODM_BATTERY_DATA_UNKNOWN)
+ pPmuData->batteryLifePercent = 0;
+
+ if (pPmuData->batteryLifeTime == NVODM_BATTERY_DATA_UNKNOWN)
+ pPmuData->batteryLifeTime = 0;
+
+ if (pPmuData->batteryVoltage == NVODM_BATTERY_DATA_UNKNOWN)
+ pPmuData->batteryVoltage = 0;
+
+ if (pPmuData->batteryCurrent == NVODM_BATTERY_DATA_UNKNOWN)
+ pPmuData->batteryCurrent = 0;
+
+ if (pPmuData->batteryMahConsumed == NVODM_BATTERY_DATA_UNKNOWN)
+ pPmuData->batteryMahConsumed = 0;
+
+ if (pPmuData->batteryTemperature == NVODM_BATTERY_DATA_UNKNOWN)
+ pPmuData->batteryTemperature = 0;
+}
+
+static int tegra_battery_data(NvRmPmuBatteryInstance NvBatteryInst)
+{
+ NvRmPmuBatteryData pPmuData = {0};
+
+ if (!NvRmPmuGetBatteryData(s_hRmGlobal, NvBatteryInst, &pPmuData))
+ return -1;
+
+ if (NvBatteryInst == NvRmPmuBatteryInst_Main) {
+ tegra_battery_convert(&pPmuData);
+ batt_dev->batt_vol = pPmuData.batteryVoltage;
+ batt_dev->batt_vol *= 1000; // Convert volt to mV
+ batt_dev->batt_current = pPmuData.batteryCurrent;
+ batt_dev->batt_temp = pPmuData.batteryTemperature;
+ batt_dev->batt_temp *= 10; // FIXME : Why is this here?
+ batt_dev->BatteryLifePercent = pPmuData.batteryLifePercent;
+ batt_dev->BatteryLifeTime = pPmuData.batteryLifeTime;
+ batt_dev->BatteryMahConsumed = pPmuData.batteryMahConsumed;
+ }
+
+ return 0;
+}
+
+static int tegra_get_ac_status(NvCharger_Type *charger)
+{
+ NvRmPmuAcLineStatus ACStatus = NvRmPmuAcLine_Offline;
+
+ if (!NvRmPmuGetAcLineStatus(s_hRmGlobal, &ACStatus))
+ return -1;
+
+ if (ACStatus == NvRmPmuAcLine_Offline)
+ batt_dev->ACLineStatus = NV_FALSE;
+ else if (ACStatus == NvRmPmuAcLine_Online) {
+ batt_dev->ACLineStatus = NV_TRUE;
+ batt_dev->charging_source = NvCharger_Type_AC;
+ if (charger)
+ *charger = batt_dev->charging_source;
+ } else
+ batt_dev->ACLineStatus = NV_FALSE;
+
+ return 0;
+}
+
+static int tegra_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp, union power_supply_propval *val)
+{
+ NvCharger_Type charger;
+
+ switch (psp) {
+
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (tegra_get_ac_status(&charger))
+ return -EINVAL;
+
+ if (psy->type == POWER_SUPPLY_TYPE_MAINS)
+ val->intval = (charger == NvCharger_Type_AC);
+ else if (psy->type == POWER_SUPPLY_TYPE_USB)
+ val->intval = (charger == NvCharger_Type_USB);
+ else
+ val->intval = 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int tegra_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp, union power_supply_propval *val)
+{
+ NvU8 state[4] = { 0 };
+ int batt_tech = 0;
+
+ switch (psp) {
+
+ case POWER_SUPPLY_PROP_STATUS:
+ if (!NvRmPmuGetBatteryStatus(s_hRmGlobal,
+ NvRmPmuBatteryInst_Main, state))
+ return -EINVAL;
+
+ switch(state[0]) {
+
+ case NVODM_BATTERY_STATUS_HIGH:
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ batt_dev->present = NV_TRUE;
+ break;
+
+ case NVODM_BATTERY_STATUS_NO_BATTERY:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ batt_dev->present = NV_FALSE;
+ break;
+
+ case NVODM_BATTERY_STATUS_CHARGING:
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ batt_dev->present = NV_TRUE;
+ batt_dev->charging_enabled = NV_TRUE;
+ batt_dev->charging_source = NvCharger_Type_AC;
+ break;
+
+ case NVODM_BATTERY_STATUS_UNKNOWN:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ batt_dev->present = NV_FALSE;
+ break;
+
+ case NVODM_BATTERY_STATUS_LOW:
+ case NVODM_BATTERY_STATUS_CRITICAL:
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ batt_dev->present = NV_TRUE;
+ break;
+
+ default:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ batt_dev->present = NV_FALSE;
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = batt_dev->present;
+ break;
+
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ if (batt_dev->present)
+ tegra_get_battery_tech(&batt_tech,
+ NvRmPmuBatteryInst_Main);
+ else
+ batt_tech = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+
+ val->intval = batt_tech;
+ break;
+
+ case POWER_SUPPLY_PROP_HEALTH:
+ if (batt_dev->present)
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ else
+ val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ case POWER_SUPPLY_PROP_CAPACITY:
+ case POWER_SUPPLY_PROP_TEMP:
+ if (!batt_dev->present) {
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ return 0;
+ }
+ if (tegra_battery_data(NvRmPmuBatteryInst_Main))
+ return -EINVAL;
+
+ if (psp == POWER_SUPPLY_PROP_CAPACITY)
+ val->intval = batt_dev->BatteryLifePercent;
+ else if (psp == POWER_SUPPLY_PROP_VOLTAGE_NOW)
+ val->intval = batt_dev->batt_vol;
+ if (psp == POWER_SUPPLY_PROP_TEMP)
+ val->intval = batt_dev->batt_temp;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void tegra_battery_poll_timer_func(unsigned long unused)
+{
+ power_supply_changed(&tegra_supplies[NvCharger_Type_Battery]);
+ power_supply_changed(&tegra_supplies[NvCharger_Type_USB]);
+ power_supply_changed(&tegra_supplies[NvCharger_Type_AC]);
+
+ mod_timer(&(batt_dev->battery_poll_timer),
+ jiffies + msecs_to_jiffies(batt_dev->batt_status_poll_period));
+}
+
+static int tegra_battery_probe(struct platform_device *pdev)
+{
+ int i, rc;
+
+
+ batt_dev = kzalloc(sizeof(*batt_dev), GFP_KERNEL);
+ if (!batt_dev) {
+ return -ENOMEM;
+ }
+ memset(batt_dev, 0, sizeof(*batt_dev));
+
+ /* Assume battery is present at start */
+ batt_dev->present = 1;
+ batt_dev->batt_id = 0;
+ batt_dev->charging_source = NvCharger_Type_AC;
+ batt_dev->charging_enabled = NvCharge_Control_Charging_Enable;
+
+ for (i = 0; i < ARRAY_SIZE(tegra_supplies); i++) {
+ rc = power_supply_register(&pdev->dev, &tegra_supplies[i]);
+ if (rc) {
+ printk(KERN_ERR "Failed to register power supply\n");
+ while (i--)
+ power_supply_unregister(&tegra_supplies[i]);
+ kfree(batt_dev);
+ return rc;
+ }
+ }
+
+ printk(KERN_INFO "%s: battery driver registered\n", pdev->name);
+
+ batt_dev->batt_status_poll_period = NVBATTERY_POLLING_INTERVAL;
+ setup_timer(&(batt_dev->battery_poll_timer), tegra_battery_poll_timer_func, 0);
+ mod_timer(&(batt_dev->battery_poll_timer),
+ jiffies + msecs_to_jiffies(batt_dev->batt_status_poll_period));
+
+ rc = device_create_file(&pdev->dev, &tegra_battery_attr);
+ if (rc) {
+ for (i = 0; i < ARRAY_SIZE(tegra_supplies); i++) {
+ power_supply_unregister(&tegra_supplies[i]);
+ }
+
+ del_timer_sync(&(batt_dev->battery_poll_timer));
+
+ pr_err("tegra_battery_probe:device_create_file FAILED");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int tegra_battery_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tegra_supplies); i++) {
+ power_supply_unregister(&tegra_supplies[i]);
+ }
+
+ if (batt_dev) {
+ device_remove_file(&pdev->dev, &tegra_battery_attr);
+
+ del_timer_sync(&(batt_dev->battery_poll_timer));
+ kfree(batt_dev);
+ batt_dev = NULL;
+ }
+
+ return 0;
+}
+
+static int tegra_battery_suspend(struct platform_device *dev,
+ pm_message_t state)
+{
+ /* Kill the Battery Polling timer */
+ del_timer_sync(&batt_dev->battery_poll_timer);
+ return 0;
+}
+
+static int tegra_battery_resume(struct platform_device *dev)
+{
+ /*Create Battery Polling timer */
+ setup_timer(&batt_dev->battery_poll_timer, tegra_battery_poll_timer_func, 0);
+ mod_timer(&batt_dev->battery_poll_timer,
+ jiffies + msecs_to_jiffies(batt_dev->batt_status_poll_period));
+ return 0;
+}
+
+static struct platform_driver tegra_battery_driver =
+{
+ .probe = tegra_battery_probe,
+ .remove = tegra_battery_remove,
+ .suspend= tegra_battery_suspend,
+ .resume = tegra_battery_resume,
+ .driver = {
+ .name = "tegra_battery",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init tegra_battery_init(void)
+{
+ platform_driver_register(&tegra_battery_driver);
+ return 0;
+}
+
+static void __exit tegra_battery_exit(void)
+{
+ platform_driver_unregister(&tegra_battery_driver);
+}
+
+module_init(tegra_battery_init);
+module_exit(tegra_battery_exit);
+MODULE_DESCRIPTION("TEGRA Battery Driver");