Commit 16bb85bc12e7d0b066b1471185f3889cb1ceb382

Authored by Linus Torvalds

Merge git://git.infradead.org/battery-2.6

* git://git.infradead.org/battery-2.6:
  intel_mid_battery: Fix battery scaling
  intel_mid_battery: Fix the argument order to intel_scu_ipc_command
  olpc_battery: Fix build failure caused by sysfs changes
  Add s3c-adc-battery driver
  Intel MID platform battery driver

Fix up trivial conflicts (battery drivers added from different branches)
in drivers/power/{Kconfig,Makefile}

Showing 6 changed files Side-by-side Diff

drivers/power/Kconfig
... ... @@ -136,6 +136,12 @@
136 136 help
137 137 Say Y to include support for the battery on the Zipit Z2.
138 138  
  139 +config BATTERY_S3C_ADC
  140 + tristate "Battery driver for Samsung ADC based monitoring"
  141 + depends on S3C_ADC
  142 + help
  143 + Say Y here to enable support for iPAQ h1930/h1940/rx1950 battery
  144 +
139 145 config CHARGER_PCF50633
140 146 tristate "NXP PCF50633 MBC"
141 147 depends on MFD_PCF50633
... ... @@ -152,6 +158,13 @@
152 158  
153 159 This driver can be build as a module. If so, the module will be
154 160 called jz4740-battery.
  161 +
  162 +config BATTERY_INTEL_MID
  163 + tristate "Battery driver for Intel MID platforms"
  164 + depends on INTEL_SCU_IPC && SPI
  165 + help
  166 + Say Y here to enable the battery driver on Intel MID
  167 + platforms.
155 168  
156 169 endif # POWER_SUPPLY
drivers/power/Makefile
... ... @@ -33,6 +33,8 @@
33 33 obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
34 34 obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
35 35 obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
  36 +obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
36 37 obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
37 38 obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
  39 +obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
drivers/power/intel_mid_battery.c
  1 +/*
  2 + * intel_mid_battery.c - Intel MID PMIC Battery Driver
  3 + *
  4 + * Copyright (C) 2009 Intel Corporation
  5 + *
  6 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  7 + *
  8 + * This program is free software; you can redistribute it and/or modify
  9 + * it under the terms of the GNU General Public License as published by
  10 + * the Free Software Foundation; version 2 of the License.
  11 + *
  12 + * This program is distributed in the hope that it will be useful, but
  13 + * WITHOUT ANY WARRANTY; without even the implied warranty of
  14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15 + * General Public License for more details.
  16 + *
  17 + * You should have received a copy of the GNU General Public License along
  18 + * with this program; if not, write to the Free Software Foundation, Inc.,
  19 + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  20 + *
  21 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  22 + * Author: Nithish Mahalingam <nithish.mahalingam@intel.com>
  23 + */
  24 +
  25 +#include <linux/module.h>
  26 +#include <linux/init.h>
  27 +#include <linux/err.h>
  28 +#include <linux/interrupt.h>
  29 +#include <linux/workqueue.h>
  30 +#include <linux/jiffies.h>
  31 +#include <linux/param.h>
  32 +#include <linux/device.h>
  33 +#include <linux/spi/spi.h>
  34 +#include <linux/platform_device.h>
  35 +#include <linux/power_supply.h>
  36 +
  37 +#include <asm/intel_scu_ipc.h>
  38 +
  39 +#define DRIVER_NAME "pmic_battery"
  40 +
  41 +/*********************************************************************
  42 + * Generic defines
  43 + *********************************************************************/
  44 +
  45 +static int debug;
  46 +module_param(debug, int, 0444);
  47 +MODULE_PARM_DESC(debug, "Flag to enable PMIC Battery debug messages.");
  48 +
  49 +#define PMIC_BATT_DRV_INFO_UPDATED 1
  50 +#define PMIC_BATT_PRESENT 1
  51 +#define PMIC_BATT_NOT_PRESENT 0
  52 +#define PMIC_USB_PRESENT PMIC_BATT_PRESENT
  53 +#define PMIC_USB_NOT_PRESENT PMIC_BATT_NOT_PRESENT
  54 +
  55 +/* pmic battery register related */
  56 +#define PMIC_BATT_CHR_SCHRGINT_ADDR 0xD2
  57 +#define PMIC_BATT_CHR_SBATOVP_MASK (1 << 1)
  58 +#define PMIC_BATT_CHR_STEMP_MASK (1 << 2)
  59 +#define PMIC_BATT_CHR_SCOMP_MASK (1 << 3)
  60 +#define PMIC_BATT_CHR_SUSBDET_MASK (1 << 4)
  61 +#define PMIC_BATT_CHR_SBATDET_MASK (1 << 5)
  62 +#define PMIC_BATT_CHR_SDCLMT_MASK (1 << 6)
  63 +#define PMIC_BATT_CHR_SUSBOVP_MASK (1 << 7)
  64 +#define PMIC_BATT_CHR_EXCPT_MASK 0xC6
  65 +#define PMIC_BATT_ADC_ACCCHRG_MASK (1 << 31)
  66 +#define PMIC_BATT_ADC_ACCCHRGVAL_MASK 0x7FFFFFFF
  67 +
  68 +/* pmic ipc related */
  69 +#define PMIC_BATT_CHR_IPC_FCHRG_SUBID 0x4
  70 +#define PMIC_BATT_CHR_IPC_TCHRG_SUBID 0x6
  71 +
  72 +/* types of battery charging */
  73 +enum batt_charge_type {
  74 + BATT_USBOTG_500MA_CHARGE,
  75 + BATT_USBOTG_TRICKLE_CHARGE,
  76 +};
  77 +
  78 +/* valid battery events */
  79 +enum batt_event {
  80 + BATT_EVENT_BATOVP_EXCPT,
  81 + BATT_EVENT_USBOVP_EXCPT,
  82 + BATT_EVENT_TEMP_EXCPT,
  83 + BATT_EVENT_DCLMT_EXCPT,
  84 + BATT_EVENT_EXCPT
  85 +};
  86 +
  87 +
  88 +/*********************************************************************
  89 + * Battery properties
  90 + *********************************************************************/
  91 +
  92 +/*
  93 + * pmic battery info
  94 + */
  95 +struct pmic_power_module_info {
  96 + bool is_dev_info_updated;
  97 + struct device *dev;
  98 + /* pmic battery data */
  99 + unsigned long update_time; /* jiffies when data read */
  100 + unsigned int usb_is_present;
  101 + unsigned int batt_is_present;
  102 + unsigned int batt_health;
  103 + unsigned int usb_health;
  104 + unsigned int batt_status;
  105 + unsigned int batt_charge_now; /* in mAS */
  106 + unsigned int batt_prev_charge_full; /* in mAS */
  107 + unsigned int batt_charge_rate; /* in units per second */
  108 +
  109 + struct power_supply usb;
  110 + struct power_supply batt;
  111 + int irq; /* GPE_ID or IRQ# */
  112 + struct workqueue_struct *monitor_wqueue;
  113 + struct delayed_work monitor_battery;
  114 + struct work_struct handler;
  115 +};
  116 +
  117 +static unsigned int delay_time = 2000; /* in ms */
  118 +
  119 +/*
  120 + * pmic ac properties
  121 + */
  122 +static enum power_supply_property pmic_usb_props[] = {
  123 + POWER_SUPPLY_PROP_PRESENT,
  124 + POWER_SUPPLY_PROP_HEALTH,
  125 +};
  126 +
  127 +/*
  128 + * pmic battery properties
  129 + */
  130 +static enum power_supply_property pmic_battery_props[] = {
  131 + POWER_SUPPLY_PROP_STATUS,
  132 + POWER_SUPPLY_PROP_HEALTH,
  133 + POWER_SUPPLY_PROP_PRESENT,
  134 + POWER_SUPPLY_PROP_CHARGE_NOW,
  135 + POWER_SUPPLY_PROP_CHARGE_FULL,
  136 +};
  137 +
  138 +
  139 +/*
  140 + * Glue functions for talking to the IPC
  141 + */
  142 +
  143 +struct battery_property {
  144 + u32 capacity; /* Charger capacity */
  145 + u8 crnt; /* Quick charge current value*/
  146 + u8 volt; /* Fine adjustment of constant charge voltage */
  147 + u8 prot; /* CHRGPROT register value */
  148 + u8 prot2; /* CHRGPROT1 register value */
  149 + u8 timer; /* Charging timer */
  150 +};
  151 +
  152 +#define IPCMSG_BATTERY 0xEF
  153 +
  154 +/* Battery coulomb counter accumulator commands */
  155 +#define IPC_CMD_CC_WR 0 /* Update coulomb counter value */
  156 +#define IPC_CMD_CC_RD 1 /* Read coulomb counter value */
  157 +#define IPC_CMD_BATTERY_PROPERTY 2 /* Read Battery property */
  158 +
  159 +/**
  160 + * pmic_scu_ipc_battery_cc_read - read battery cc
  161 + * @value: battery coulomb counter read
  162 + *
  163 + * Reads the battery couloumb counter value, returns 0 on success, or
  164 + * an error code
  165 + *
  166 + * This function may sleep. Locking for SCU accesses is handled for
  167 + * the caller.
  168 + */
  169 +static int pmic_scu_ipc_battery_cc_read(u32 *value)
  170 +{
  171 + return intel_scu_ipc_command(IPCMSG_BATTERY, IPC_CMD_CC_RD,
  172 + NULL, 0, value, 1);
  173 +}
  174 +
  175 +/**
  176 + * pmic_scu_ipc_battery_property_get - fetch properties
  177 + * @prop: battery properties
  178 + *
  179 + * Retrieve the battery properties from the power management
  180 + *
  181 + * This function may sleep. Locking for SCU accesses is handled for
  182 + * the caller.
  183 + */
  184 +static int pmic_scu_ipc_battery_property_get(struct battery_property *prop)
  185 +{
  186 + u32 data[3];
  187 + u8 *p = (u8 *)&data[1];
  188 + int err = intel_scu_ipc_command(IPC_CMD_BATTERY_PROPERTY,
  189 + IPCMSG_BATTERY, NULL, 0, data, 3);
  190 +
  191 + prop->capacity = data[0];
  192 + prop->crnt = *p++;
  193 + prop->volt = *p++;
  194 + prop->prot = *p++;
  195 + prop->prot2 = *p++;
  196 + prop->timer = *p++;
  197 +
  198 + return err;
  199 +}
  200 +
  201 +/**
  202 + * pmic_scu_ipc_set_charger - set charger
  203 + * @charger: charger to select
  204 + *
  205 + * Switch the charging mode for the SCU
  206 + */
  207 +
  208 +static int pmic_scu_ipc_set_charger(int charger)
  209 +{
  210 + return intel_scu_ipc_simple_command(charger, IPCMSG_BATTERY);
  211 +}
  212 +
  213 +/**
  214 + * pmic_battery_log_event - log battery events
  215 + * @event: battery event to be logged
  216 + * Context: can sleep
  217 + *
  218 + * There are multiple battery events which may be of interest to users;
  219 + * this battery function logs the different battery events onto the
  220 + * kernel log messages.
  221 + */
  222 +static void pmic_battery_log_event(enum batt_event event)
  223 +{
  224 + printk(KERN_WARNING "pmic-battery: ");
  225 + switch (event) {
  226 + case BATT_EVENT_BATOVP_EXCPT:
  227 + printk(KERN_CONT "battery overvoltage condition\n");
  228 + break;
  229 + case BATT_EVENT_USBOVP_EXCPT:
  230 + printk(KERN_CONT "usb charger overvoltage condition\n");
  231 + break;
  232 + case BATT_EVENT_TEMP_EXCPT:
  233 + printk(KERN_CONT "high battery temperature condition\n");
  234 + break;
  235 + case BATT_EVENT_DCLMT_EXCPT:
  236 + printk(KERN_CONT "over battery charge current condition\n");
  237 + break;
  238 + default:
  239 + printk(KERN_CONT "charger/battery exception %d\n", event);
  240 + break;
  241 + }
  242 +}
  243 +
  244 +/**
  245 + * pmic_battery_read_status - read battery status information
  246 + * @pbi: device info structure to update the read information
  247 + * Context: can sleep
  248 + *
  249 + * PMIC power source information need to be updated based on the data read
  250 + * from the PMIC battery registers.
  251 + *
  252 + */
  253 +static void pmic_battery_read_status(struct pmic_power_module_info *pbi)
  254 +{
  255 + unsigned int update_time_intrvl;
  256 + unsigned int chrg_val;
  257 + u32 ccval;
  258 + u8 r8;
  259 + struct battery_property batt_prop;
  260 + int batt_present = 0;
  261 + int usb_present = 0;
  262 + int batt_exception = 0;
  263 +
  264 + /* make sure the last batt_status read happened delay_time before */
  265 + if (pbi->update_time && time_before(jiffies, pbi->update_time +
  266 + msecs_to_jiffies(delay_time)))
  267 + return;
  268 +
  269 + update_time_intrvl = jiffies_to_msecs(jiffies - pbi->update_time);
  270 + pbi->update_time = jiffies;
  271 +
  272 + /* read coulomb counter registers and schrgint register */
  273 + if (pmic_scu_ipc_battery_cc_read(&ccval)) {
  274 + dev_warn(pbi->dev, "%s(): ipc config cmd failed\n",
  275 + __func__);
  276 + return;
  277 + }
  278 +
  279 + if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) {
  280 + dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
  281 + __func__);
  282 + return;
  283 + }
  284 +
  285 + /*
  286 + * set pmic_power_module_info members based on pmic register values
  287 + * read.
  288 + */
  289 +
  290 + /* set batt_is_present */
  291 + if (r8 & PMIC_BATT_CHR_SBATDET_MASK) {
  292 + pbi->batt_is_present = PMIC_BATT_PRESENT;
  293 + batt_present = 1;
  294 + } else {
  295 + pbi->batt_is_present = PMIC_BATT_NOT_PRESENT;
  296 + pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
  297 + pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN;
  298 + }
  299 +
  300 + /* set batt_health */
  301 + if (batt_present) {
  302 + if (r8 & PMIC_BATT_CHR_SBATOVP_MASK) {
  303 + pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
  304 + pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
  305 + pmic_battery_log_event(BATT_EVENT_BATOVP_EXCPT);
  306 + batt_exception = 1;
  307 + } else if (r8 & PMIC_BATT_CHR_SDCLMT_MASK) {
  308 + pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
  309 + pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
  310 + pmic_battery_log_event(BATT_EVENT_DCLMT_EXCPT);
  311 + batt_exception = 1;
  312 + } else if (r8 & PMIC_BATT_CHR_STEMP_MASK) {
  313 + pbi->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT;
  314 + pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
  315 + pmic_battery_log_event(BATT_EVENT_TEMP_EXCPT);
  316 + batt_exception = 1;
  317 + } else {
  318 + pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD;
  319 + }
  320 + }
  321 +
  322 + /* set usb_is_present */
  323 + if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) {
  324 + pbi->usb_is_present = PMIC_USB_PRESENT;
  325 + usb_present = 1;
  326 + } else {
  327 + pbi->usb_is_present = PMIC_USB_NOT_PRESENT;
  328 + pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
  329 + }
  330 +
  331 + if (usb_present) {
  332 + if (r8 & PMIC_BATT_CHR_SUSBOVP_MASK) {
  333 + pbi->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
  334 + pmic_battery_log_event(BATT_EVENT_USBOVP_EXCPT);
  335 + } else {
  336 + pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD;
  337 + }
  338 + }
  339 +
  340 + chrg_val = ccval & PMIC_BATT_ADC_ACCCHRGVAL_MASK;
  341 +
  342 + /* set batt_prev_charge_full to battery capacity the first time */
  343 + if (!pbi->is_dev_info_updated) {
  344 + if (pmic_scu_ipc_battery_property_get(&batt_prop)) {
  345 + dev_warn(pbi->dev, "%s(): ipc config cmd failed\n",
  346 + __func__);
  347 + return;
  348 + }
  349 + pbi->batt_prev_charge_full = batt_prop.capacity;
  350 + }
  351 +
  352 + /* set batt_status */
  353 + if (batt_present && !batt_exception) {
  354 + if (r8 & PMIC_BATT_CHR_SCOMP_MASK) {
  355 + pbi->batt_status = POWER_SUPPLY_STATUS_FULL;
  356 + pbi->batt_prev_charge_full = chrg_val;
  357 + } else if (ccval & PMIC_BATT_ADC_ACCCHRG_MASK) {
  358 + pbi->batt_status = POWER_SUPPLY_STATUS_DISCHARGING;
  359 + } else {
  360 + pbi->batt_status = POWER_SUPPLY_STATUS_CHARGING;
  361 + }
  362 + }
  363 +
  364 + /* set batt_charge_rate */
  365 + if (pbi->is_dev_info_updated && batt_present && !batt_exception) {
  366 + if (pbi->batt_status == POWER_SUPPLY_STATUS_DISCHARGING) {
  367 + if (pbi->batt_charge_now - chrg_val) {
  368 + pbi->batt_charge_rate = ((pbi->batt_charge_now -
  369 + chrg_val) * 1000 * 60) /
  370 + update_time_intrvl;
  371 + }
  372 + } else if (pbi->batt_status == POWER_SUPPLY_STATUS_CHARGING) {
  373 + if (chrg_val - pbi->batt_charge_now) {
  374 + pbi->batt_charge_rate = ((chrg_val -
  375 + pbi->batt_charge_now) * 1000 * 60) /
  376 + update_time_intrvl;
  377 + }
  378 + } else
  379 + pbi->batt_charge_rate = 0;
  380 + } else {
  381 + pbi->batt_charge_rate = -1;
  382 + }
  383 +
  384 + /* batt_charge_now */
  385 + if (batt_present && !batt_exception)
  386 + pbi->batt_charge_now = chrg_val;
  387 + else
  388 + pbi->batt_charge_now = -1;
  389 +
  390 + pbi->is_dev_info_updated = PMIC_BATT_DRV_INFO_UPDATED;
  391 +}
  392 +
  393 +/**
  394 + * pmic_usb_get_property - usb power source get property
  395 + * @psy: usb power supply context
  396 + * @psp: usb power source property
  397 + * @val: usb power source property value
  398 + * Context: can sleep
  399 + *
  400 + * PMIC usb power source property needs to be provided to power_supply
  401 + * subsytem for it to provide the information to users.
  402 + */
  403 +static int pmic_usb_get_property(struct power_supply *psy,
  404 + enum power_supply_property psp,
  405 + union power_supply_propval *val)
  406 +{
  407 + struct pmic_power_module_info *pbi = container_of(psy,
  408 + struct pmic_power_module_info, usb);
  409 +
  410 + /* update pmic_power_module_info members */
  411 + pmic_battery_read_status(pbi);
  412 +
  413 + switch (psp) {
  414 + case POWER_SUPPLY_PROP_PRESENT:
  415 + val->intval = pbi->usb_is_present;
  416 + break;
  417 + case POWER_SUPPLY_PROP_HEALTH:
  418 + val->intval = pbi->usb_health;
  419 + break;
  420 + default:
  421 + return -EINVAL;
  422 + }
  423 +
  424 + return 0;
  425 +}
  426 +
  427 +static inline unsigned long mAStouAh(unsigned long v)
  428 +{
  429 + /* seconds to hours, mA to ยตA */
  430 + return (v * 1000) / 3600;
  431 +}
  432 +
  433 +/**
  434 + * pmic_battery_get_property - battery power source get property
  435 + * @psy: battery power supply context
  436 + * @psp: battery power source property
  437 + * @val: battery power source property value
  438 + * Context: can sleep
  439 + *
  440 + * PMIC battery power source property needs to be provided to power_supply
  441 + * subsytem for it to provide the information to users.
  442 + */
  443 +static int pmic_battery_get_property(struct power_supply *psy,
  444 + enum power_supply_property psp,
  445 + union power_supply_propval *val)
  446 +{
  447 + struct pmic_power_module_info *pbi = container_of(psy,
  448 + struct pmic_power_module_info, batt);
  449 +
  450 + /* update pmic_power_module_info members */
  451 + pmic_battery_read_status(pbi);
  452 +
  453 + switch (psp) {
  454 + case POWER_SUPPLY_PROP_STATUS:
  455 + val->intval = pbi->batt_status;
  456 + break;
  457 + case POWER_SUPPLY_PROP_HEALTH:
  458 + val->intval = pbi->batt_health;
  459 + break;
  460 + case POWER_SUPPLY_PROP_PRESENT:
  461 + val->intval = pbi->batt_is_present;
  462 + break;
  463 + case POWER_SUPPLY_PROP_CHARGE_NOW:
  464 + val->intval = mAStouAh(pbi->batt_charge_now);
  465 + break;
  466 + case POWER_SUPPLY_PROP_CHARGE_FULL:
  467 + val->intval = mAStouAh(pbi->batt_prev_charge_full);
  468 + break;
  469 + default:
  470 + return -EINVAL;
  471 + }
  472 +
  473 + return 0;
  474 +}
  475 +
  476 +/**
  477 + * pmic_battery_monitor - monitor battery status
  478 + * @work: work structure
  479 + * Context: can sleep
  480 + *
  481 + * PMIC battery status needs to be monitored for any change
  482 + * and information needs to be frequently updated.
  483 + */
  484 +static void pmic_battery_monitor(struct work_struct *work)
  485 +{
  486 + struct pmic_power_module_info *pbi = container_of(work,
  487 + struct pmic_power_module_info, monitor_battery.work);
  488 +
  489 + /* update pmic_power_module_info members */
  490 + pmic_battery_read_status(pbi);
  491 + queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 10);
  492 +}
  493 +
  494 +/**
  495 + * pmic_battery_set_charger - set battery charger
  496 + * @pbi: device info structure
  497 + * @chrg: charge mode to set battery charger in
  498 + * Context: can sleep
  499 + *
  500 + * PMIC battery charger needs to be enabled based on the usb charge
  501 + * capabilities connected to the platform.
  502 + */
  503 +static int pmic_battery_set_charger(struct pmic_power_module_info *pbi,
  504 + enum batt_charge_type chrg)
  505 +{
  506 + int retval;
  507 +
  508 + /* set usblmt bits and chrgcntl register bits appropriately */
  509 + switch (chrg) {
  510 + case BATT_USBOTG_500MA_CHARGE:
  511 + retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_FCHRG_SUBID);
  512 + break;
  513 + case BATT_USBOTG_TRICKLE_CHARGE:
  514 + retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_TCHRG_SUBID);
  515 + break;
  516 + default:
  517 + dev_warn(pbi->dev, "%s(): out of range usb charger "
  518 + "charge detected\n", __func__);
  519 + return -EINVAL;
  520 + }
  521 +
  522 + if (retval) {
  523 + dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
  524 + __func__);
  525 + return retval;;
  526 + }
  527 +
  528 + return 0;
  529 +}
  530 +
  531 +/**
  532 + * pmic_battery_interrupt_handler - pmic battery interrupt handler
  533 + * Context: interrupt context
  534 + *
  535 + * PMIC battery interrupt handler which will be called with either
  536 + * battery full condition occurs or usb otg & battery connect
  537 + * condition occurs.
  538 + */
  539 +static irqreturn_t pmic_battery_interrupt_handler(int id, void *dev)
  540 +{
  541 + struct pmic_power_module_info *pbi = dev;
  542 +
  543 + schedule_work(&pbi->handler);
  544 +
  545 + return IRQ_HANDLED;
  546 +}
  547 +
  548 +/**
  549 + * pmic_battery_handle_intrpt - pmic battery service interrupt
  550 + * @work: work structure
  551 + * Context: can sleep
  552 + *
  553 + * PMIC battery needs to either update the battery status as full
  554 + * if it detects battery full condition caused the interrupt or needs
  555 + * to enable battery charger if it detects usb and battery detect
  556 + * caused the source of interrupt.
  557 + */
  558 +static void pmic_battery_handle_intrpt(struct work_struct *work)
  559 +{
  560 + struct pmic_power_module_info *pbi = container_of(work,
  561 + struct pmic_power_module_info, handler);
  562 + enum batt_charge_type chrg;
  563 + u8 r8;
  564 +
  565 + if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) {
  566 + dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
  567 + __func__);
  568 + return;
  569 + }
  570 + /* find the cause of the interrupt */
  571 + if (r8 & PMIC_BATT_CHR_SBATDET_MASK) {
  572 + pbi->batt_is_present = PMIC_BATT_PRESENT;
  573 + } else {
  574 + pbi->batt_is_present = PMIC_BATT_NOT_PRESENT;
  575 + pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
  576 + pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN;
  577 + return;
  578 + }
  579 +
  580 + if (r8 & PMIC_BATT_CHR_EXCPT_MASK) {
  581 + pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
  582 + pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
  583 + pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
  584 + pmic_battery_log_event(BATT_EVENT_EXCPT);
  585 + return;
  586 + } else {
  587 + pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD;
  588 + pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD;
  589 + }
  590 +
  591 + if (r8 & PMIC_BATT_CHR_SCOMP_MASK) {
  592 + u32 ccval;
  593 + pbi->batt_status = POWER_SUPPLY_STATUS_FULL;
  594 +
  595 + if (pmic_scu_ipc_battery_cc_read(&ccval)) {
  596 + dev_warn(pbi->dev, "%s(): ipc config cmd "
  597 + "failed\n", __func__);
  598 + return;
  599 + }
  600 + pbi->batt_prev_charge_full = ccval &
  601 + PMIC_BATT_ADC_ACCCHRGVAL_MASK;
  602 + return;
  603 + }
  604 +
  605 + if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) {
  606 + pbi->usb_is_present = PMIC_USB_PRESENT;
  607 + } else {
  608 + pbi->usb_is_present = PMIC_USB_NOT_PRESENT;
  609 + pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
  610 + return;
  611 + }
  612 +
  613 + /* setup battery charging */
  614 +
  615 +#if 0
  616 + /* check usb otg power capability and set charger accordingly */
  617 + retval = langwell_udc_maxpower(&power);
  618 + if (retval) {
  619 + dev_warn(pbi->dev,
  620 + "%s(): usb otg power query failed with error code %d\n",
  621 + __func__, retval);
  622 + return;
  623 + }
  624 +
  625 + if (power >= 500)
  626 + chrg = BATT_USBOTG_500MA_CHARGE;
  627 + else
  628 +#endif
  629 + chrg = BATT_USBOTG_TRICKLE_CHARGE;
  630 +
  631 + /* enable battery charging */
  632 + if (pmic_battery_set_charger(pbi, chrg)) {
  633 + dev_warn(pbi->dev,
  634 + "%s(): failed to set up battery charging\n", __func__);
  635 + return;
  636 + }
  637 +
  638 + dev_dbg(pbi->dev,
  639 + "pmic-battery: %s() - setting up battery charger successful\n",
  640 + __func__);
  641 +}
  642 +
  643 +/**
  644 + * pmic_battery_probe - pmic battery initialize
  645 + * @irq: pmic battery device irq
  646 + * @dev: pmic battery device structure
  647 + * Context: can sleep
  648 + *
  649 + * PMIC battery initializes its internal data structue and other
  650 + * infrastructure components for it to work as expected.
  651 + */
  652 +static __devinit int probe(int irq, struct device *dev)
  653 +{
  654 + int retval = 0;
  655 + struct pmic_power_module_info *pbi;
  656 +
  657 + dev_dbg(dev, "pmic-battery: found pmic battery device\n");
  658 +
  659 + pbi = kzalloc(sizeof(*pbi), GFP_KERNEL);
  660 + if (!pbi) {
  661 + dev_err(dev, "%s(): memory allocation failed\n",
  662 + __func__);
  663 + return -ENOMEM;
  664 + }
  665 +
  666 + pbi->dev = dev;
  667 + pbi->irq = irq;
  668 + dev_set_drvdata(dev, pbi);
  669 +
  670 + /* initialize all required framework before enabling interrupts */
  671 + INIT_WORK(&pbi->handler, pmic_battery_handle_intrpt);
  672 + INIT_DELAYED_WORK(&pbi->monitor_battery, pmic_battery_monitor);
  673 + pbi->monitor_wqueue =
  674 + create_singlethread_workqueue(dev_name(dev));
  675 + if (!pbi->monitor_wqueue) {
  676 + dev_err(dev, "%s(): wqueue init failed\n", __func__);
  677 + retval = -ESRCH;
  678 + goto wqueue_failed;
  679 + }
  680 +
  681 + /* register interrupt */
  682 + retval = request_irq(pbi->irq, pmic_battery_interrupt_handler,
  683 + 0, DRIVER_NAME, pbi);
  684 + if (retval) {
  685 + dev_err(dev, "%s(): cannot get IRQ\n", __func__);
  686 + goto requestirq_failed;
  687 + }
  688 +
  689 + /* register pmic-batt with power supply subsystem */
  690 + pbi->batt.name = "pmic-batt";
  691 + pbi->batt.type = POWER_SUPPLY_TYPE_BATTERY;
  692 + pbi->batt.properties = pmic_battery_props;
  693 + pbi->batt.num_properties = ARRAY_SIZE(pmic_battery_props);
  694 + pbi->batt.get_property = pmic_battery_get_property;
  695 + retval = power_supply_register(dev, &pbi->batt);
  696 + if (retval) {
  697 + dev_err(dev,
  698 + "%s(): failed to register pmic battery device with power supply subsystem\n",
  699 + __func__);
  700 + goto power_reg_failed;
  701 + }
  702 +
  703 + dev_dbg(dev, "pmic-battery: %s() - pmic battery device "
  704 + "registration with power supply subsystem successful\n",
  705 + __func__);
  706 +
  707 + queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 1);
  708 +
  709 + /* register pmic-usb with power supply subsystem */
  710 + pbi->usb.name = "pmic-usb";
  711 + pbi->usb.type = POWER_SUPPLY_TYPE_USB;
  712 + pbi->usb.properties = pmic_usb_props;
  713 + pbi->usb.num_properties = ARRAY_SIZE(pmic_usb_props);
  714 + pbi->usb.get_property = pmic_usb_get_property;
  715 + retval = power_supply_register(dev, &pbi->usb);
  716 + if (retval) {
  717 + dev_err(dev,
  718 + "%s(): failed to register pmic usb device with power supply subsystem\n",
  719 + __func__);
  720 + goto power_reg_failed_1;
  721 + }
  722 +
  723 + if (debug)
  724 + printk(KERN_INFO "pmic-battery: %s() - pmic usb device "
  725 + "registration with power supply subsystem successful\n",
  726 + __func__);
  727 +
  728 + return retval;
  729 +
  730 +power_reg_failed_1:
  731 + power_supply_unregister(&pbi->batt);
  732 +power_reg_failed:
  733 + cancel_rearming_delayed_workqueue(pbi->monitor_wqueue,
  734 + &pbi->monitor_battery);
  735 +requestirq_failed:
  736 + destroy_workqueue(pbi->monitor_wqueue);
  737 +wqueue_failed:
  738 + kfree(pbi);
  739 +
  740 + return retval;
  741 +}
  742 +
  743 +static int __devinit platform_pmic_battery_probe(struct platform_device *pdev)
  744 +{
  745 + return probe(pdev->id, &pdev->dev);
  746 +}
  747 +
  748 +/**
  749 + * pmic_battery_remove - pmic battery finalize
  750 + * @dev: pmic battery device structure
  751 + * Context: can sleep
  752 + *
  753 + * PMIC battery finalizes its internal data structue and other
  754 + * infrastructure components that it initialized in
  755 + * pmic_battery_probe.
  756 + */
  757 +
  758 +static int __devexit platform_pmic_battery_remove(struct platform_device *pdev)
  759 +{
  760 + struct pmic_power_module_info *pbi = dev_get_drvdata(&pdev->dev);
  761 +
  762 + free_irq(pbi->irq, pbi);
  763 + cancel_rearming_delayed_workqueue(pbi->monitor_wqueue,
  764 + &pbi->monitor_battery);
  765 + destroy_workqueue(pbi->monitor_wqueue);
  766 +
  767 + power_supply_unregister(&pbi->usb);
  768 + power_supply_unregister(&pbi->batt);
  769 +
  770 + flush_scheduled_work();
  771 + kfree(pbi);
  772 + return 0;
  773 +}
  774 +
  775 +static struct platform_driver platform_pmic_battery_driver = {
  776 + .driver = {
  777 + .name = DRIVER_NAME,
  778 + .owner = THIS_MODULE,
  779 + },
  780 + .probe = platform_pmic_battery_probe,
  781 + .remove = __devexit_p(platform_pmic_battery_remove),
  782 +};
  783 +
  784 +static int __init platform_pmic_battery_module_init(void)
  785 +{
  786 + return platform_driver_register(&platform_pmic_battery_driver);
  787 +}
  788 +
  789 +static void __exit platform_pmic_battery_module_exit(void)
  790 +{
  791 + platform_driver_unregister(&platform_pmic_battery_driver);
  792 +}
  793 +
  794 +module_init(platform_pmic_battery_module_init);
  795 +module_exit(platform_pmic_battery_module_exit);
  796 +
  797 +MODULE_AUTHOR("Nithish Mahalingam <nithish.mahalingam@intel.com>");
  798 +MODULE_DESCRIPTION("Intel Moorestown PMIC Battery Driver");
  799 +MODULE_LICENSE("GPL");
drivers/power/olpc_battery.c
1 1 /*
2 2 * Battery driver for One Laptop Per Child board.
3 3 *
4   - * Copyright ยฉ 2006 David Woodhouse <dwmw2@infradead.org>
  4 + * Copyright ยฉ 2006-2010 David Woodhouse <dwmw2@infradead.org>
5 5 *
6 6 * This program is free software; you can redistribute it and/or modify
7 7 * it under the terms of the GNU General Public License version 2 as
drivers/power/s3c_adc_battery.c
  1 +/*
  2 + * iPAQ h1930/h1940/rx1950 battery controler driver
  3 + * Copyright (c) Vasily Khoruzhick
  4 + * Based on h1940_battery.c by Arnaud Patard
  5 + *
  6 + * This file is subject to the terms and conditions of the GNU General Public
  7 + * License. See the file COPYING in the main directory of this archive for
  8 + * more details.
  9 + *
  10 + */
  11 +
  12 +#include <linux/interrupt.h>
  13 +#include <linux/platform_device.h>
  14 +#include <linux/power_supply.h>
  15 +#include <linux/leds.h>
  16 +#include <linux/gpio.h>
  17 +#include <linux/err.h>
  18 +#include <linux/timer.h>
  19 +#include <linux/jiffies.h>
  20 +#include <linux/s3c_adc_battery.h>
  21 +#include <linux/errno.h>
  22 +#include <linux/init.h>
  23 +
  24 +#include <plat/adc.h>
  25 +
  26 +#define BAT_POLL_INTERVAL 10000 /* ms */
  27 +#define JITTER_DELAY 500 /* ms */
  28 +
  29 +struct s3c_adc_bat {
  30 + struct power_supply psy;
  31 + struct s3c_adc_client *client;
  32 + struct s3c_adc_bat_pdata *pdata;
  33 + int volt_value;
  34 + int cur_value;
  35 + unsigned int timestamp;
  36 + int level;
  37 + int status;
  38 + int cable_plugged:1;
  39 +};
  40 +
  41 +static struct delayed_work bat_work;
  42 +
  43 +static void s3c_adc_bat_ext_power_changed(struct power_supply *psy)
  44 +{
  45 + schedule_delayed_work(&bat_work,
  46 + msecs_to_jiffies(JITTER_DELAY));
  47 +}
  48 +
  49 +static enum power_supply_property s3c_adc_backup_bat_props[] = {
  50 + POWER_SUPPLY_PROP_VOLTAGE_NOW,
  51 + POWER_SUPPLY_PROP_VOLTAGE_MIN,
  52 + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
  53 +};
  54 +
  55 +static int s3c_adc_backup_bat_get_property(struct power_supply *psy,
  56 + enum power_supply_property psp,
  57 + union power_supply_propval *val)
  58 +{
  59 + struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy);
  60 +
  61 + if (!bat) {
  62 + dev_err(psy->dev, "%s: no battery infos ?!\n", __func__);
  63 + return -EINVAL;
  64 + }
  65 +
  66 + if (bat->volt_value < 0 ||
  67 + jiffies_to_msecs(jiffies - bat->timestamp) >
  68 + BAT_POLL_INTERVAL) {
  69 + bat->volt_value = s3c_adc_read(bat->client,
  70 + bat->pdata->backup_volt_channel);
  71 + bat->volt_value *= bat->pdata->backup_volt_mult;
  72 + bat->timestamp = jiffies;
  73 + }
  74 +
  75 + switch (psp) {
  76 + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  77 + val->intval = bat->volt_value;
  78 + return 0;
  79 + case POWER_SUPPLY_PROP_VOLTAGE_MIN:
  80 + val->intval = bat->pdata->backup_volt_min;
  81 + return 0;
  82 + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
  83 + val->intval = bat->pdata->backup_volt_max;
  84 + return 0;
  85 + default:
  86 + return -EINVAL;
  87 + }
  88 +}
  89 +
  90 +static struct s3c_adc_bat backup_bat = {
  91 + .psy = {
  92 + .name = "backup-battery",
  93 + .type = POWER_SUPPLY_TYPE_BATTERY,
  94 + .properties = s3c_adc_backup_bat_props,
  95 + .num_properties = ARRAY_SIZE(s3c_adc_backup_bat_props),
  96 + .get_property = s3c_adc_backup_bat_get_property,
  97 + .use_for_apm = 1,
  98 + },
  99 +};
  100 +
  101 +static enum power_supply_property s3c_adc_main_bat_props[] = {
  102 + POWER_SUPPLY_PROP_STATUS,
  103 + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
  104 + POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
  105 + POWER_SUPPLY_PROP_CHARGE_NOW,
  106 + POWER_SUPPLY_PROP_VOLTAGE_NOW,
  107 + POWER_SUPPLY_PROP_CURRENT_NOW,
  108 +};
  109 +
  110 +static int calc_full_volt(int volt_val, int cur_val, int impedance)
  111 +{
  112 + return volt_val + cur_val * impedance / 1000;
  113 +}
  114 +
  115 +static int s3c_adc_bat_get_property(struct power_supply *psy,
  116 + enum power_supply_property psp,
  117 + union power_supply_propval *val)
  118 +{
  119 + struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy);
  120 +
  121 + int new_level;
  122 + int full_volt;
  123 + const struct s3c_adc_bat_thresh *lut = bat->pdata->lut_noac;
  124 + unsigned int lut_size = bat->pdata->lut_noac_cnt;
  125 +
  126 + if (!bat) {
  127 + dev_err(psy->dev, "no battery infos ?!\n");
  128 + return -EINVAL;
  129 + }
  130 +
  131 + if (bat->volt_value < 0 || bat->cur_value < 0 ||
  132 + jiffies_to_msecs(jiffies - bat->timestamp) >
  133 + BAT_POLL_INTERVAL) {
  134 + bat->volt_value = s3c_adc_read(bat->client,
  135 + bat->pdata->volt_channel) * bat->pdata->volt_mult;
  136 + bat->cur_value = s3c_adc_read(bat->client,
  137 + bat->pdata->current_channel) * bat->pdata->current_mult;
  138 + bat->timestamp = jiffies;
  139 + }
  140 +
  141 + if (bat->cable_plugged &&
  142 + ((bat->pdata->gpio_charge_finished < 0) ||
  143 + !gpio_get_value(bat->pdata->gpio_charge_finished))) {
  144 + lut = bat->pdata->lut_acin;
  145 + lut_size = bat->pdata->lut_acin_cnt;
  146 + }
  147 +
  148 + new_level = 100000;
  149 + full_volt = calc_full_volt((bat->volt_value / 1000),
  150 + (bat->cur_value / 1000), bat->pdata->internal_impedance);
  151 +
  152 + if (full_volt < calc_full_volt(lut->volt, lut->cur,
  153 + bat->pdata->internal_impedance)) {
  154 + lut_size--;
  155 + while (lut_size--) {
  156 + int lut_volt1;
  157 + int lut_volt2;
  158 +
  159 + lut_volt1 = calc_full_volt(lut[0].volt, lut[0].cur,
  160 + bat->pdata->internal_impedance);
  161 + lut_volt2 = calc_full_volt(lut[1].volt, lut[1].cur,
  162 + bat->pdata->internal_impedance);
  163 + if (full_volt < lut_volt1 && full_volt >= lut_volt2) {
  164 + new_level = (lut[1].level +
  165 + (lut[0].level - lut[1].level) *
  166 + (full_volt - lut_volt2) /
  167 + (lut_volt1 - lut_volt2)) * 1000;
  168 + break;
  169 + }
  170 + new_level = lut[1].level * 1000;
  171 + lut++;
  172 + }
  173 + }
  174 +
  175 + bat->level = new_level;
  176 +
  177 + switch (psp) {
  178 + case POWER_SUPPLY_PROP_STATUS:
  179 + if (bat->pdata->gpio_charge_finished < 0)
  180 + val->intval = bat->level == 100000 ?
  181 + POWER_SUPPLY_STATUS_FULL : bat->status;
  182 + else
  183 + val->intval = bat->status;
  184 + return 0;
  185 + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
  186 + val->intval = 100000;
  187 + return 0;
  188 + case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
  189 + val->intval = 0;
  190 + return 0;
  191 + case POWER_SUPPLY_PROP_CHARGE_NOW:
  192 + val->intval = bat->level;
  193 + return 0;
  194 + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  195 + val->intval = bat->volt_value;
  196 + return 0;
  197 + case POWER_SUPPLY_PROP_CURRENT_NOW:
  198 + val->intval = bat->cur_value;
  199 + return 0;
  200 + default:
  201 + return -EINVAL;
  202 + }
  203 +}
  204 +
  205 +static struct s3c_adc_bat main_bat = {
  206 + .psy = {
  207 + .name = "main-battery",
  208 + .type = POWER_SUPPLY_TYPE_BATTERY,
  209 + .properties = s3c_adc_main_bat_props,
  210 + .num_properties = ARRAY_SIZE(s3c_adc_main_bat_props),
  211 + .get_property = s3c_adc_bat_get_property,
  212 + .external_power_changed = s3c_adc_bat_ext_power_changed,
  213 + .use_for_apm = 1,
  214 + },
  215 +};
  216 +
  217 +static void s3c_adc_bat_work(struct work_struct *work)
  218 +{
  219 + struct s3c_adc_bat *bat = &main_bat;
  220 + int is_charged;
  221 + int is_plugged;
  222 + static int was_plugged;
  223 +
  224 + is_plugged = power_supply_am_i_supplied(&bat->psy);
  225 + bat->cable_plugged = is_plugged;
  226 + if (is_plugged != was_plugged) {
  227 + was_plugged = is_plugged;
  228 + if (is_plugged) {
  229 + if (bat->pdata->enable_charger)
  230 + bat->pdata->enable_charger();
  231 + bat->status = POWER_SUPPLY_STATUS_CHARGING;
  232 + } else {
  233 + if (bat->pdata->disable_charger)
  234 + bat->pdata->disable_charger();
  235 + bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
  236 + }
  237 + } else {
  238 + if ((bat->pdata->gpio_charge_finished >= 0) && is_plugged) {
  239 + is_charged = gpio_get_value(
  240 + main_bat.pdata->gpio_charge_finished);
  241 + if (is_charged) {
  242 + if (bat->pdata->disable_charger)
  243 + bat->pdata->disable_charger();
  244 + bat->status = POWER_SUPPLY_STATUS_FULL;
  245 + } else {
  246 + if (bat->pdata->enable_charger)
  247 + bat->pdata->enable_charger();
  248 + bat->status = POWER_SUPPLY_STATUS_CHARGING;
  249 + }
  250 + }
  251 + }
  252 +
  253 + power_supply_changed(&bat->psy);
  254 +}
  255 +
  256 +static irqreturn_t s3c_adc_bat_charged(int irq, void *dev_id)
  257 +{
  258 + schedule_delayed_work(&bat_work,
  259 + msecs_to_jiffies(JITTER_DELAY));
  260 + return IRQ_HANDLED;
  261 +}
  262 +
  263 +static int __init s3c_adc_bat_probe(struct platform_device *pdev)
  264 +{
  265 + struct s3c_adc_client *client;
  266 + struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
  267 + int ret;
  268 +
  269 + client = s3c_adc_register(pdev, NULL, NULL, 0);
  270 + if (IS_ERR(client)) {
  271 + dev_err(&pdev->dev, "cannot register adc\n");
  272 + return PTR_ERR(client);
  273 + }
  274 +
  275 + platform_set_drvdata(pdev, client);
  276 +
  277 + main_bat.client = client;
  278 + main_bat.pdata = pdata;
  279 + main_bat.volt_value = -1;
  280 + main_bat.cur_value = -1;
  281 + main_bat.cable_plugged = 0;
  282 + main_bat.status = POWER_SUPPLY_STATUS_DISCHARGING;
  283 +
  284 + ret = power_supply_register(&pdev->dev, &main_bat.psy);
  285 + if (ret)
  286 + goto err_reg_main;
  287 + if (pdata->backup_volt_mult) {
  288 + backup_bat.client = client;
  289 + backup_bat.pdata = pdev->dev.platform_data;
  290 + backup_bat.volt_value = -1;
  291 + ret = power_supply_register(&pdev->dev, &backup_bat.psy);
  292 + if (ret)
  293 + goto err_reg_backup;
  294 + }
  295 +
  296 + INIT_DELAYED_WORK(&bat_work, s3c_adc_bat_work);
  297 +
  298 + if (pdata->gpio_charge_finished >= 0) {
  299 + ret = gpio_request(pdata->gpio_charge_finished, "charged");
  300 + if (ret)
  301 + goto err_gpio;
  302 +
  303 + ret = request_irq(gpio_to_irq(pdata->gpio_charge_finished),
  304 + s3c_adc_bat_charged,
  305 + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
  306 + "battery charged", NULL);
  307 + if (ret)
  308 + goto err_irq;
  309 + }
  310 +
  311 + if (pdata->init) {
  312 + ret = pdata->init();
  313 + if (ret)
  314 + goto err_platform;
  315 + }
  316 +
  317 + dev_info(&pdev->dev, "successfully loaded\n");
  318 + device_init_wakeup(&pdev->dev, 1);
  319 +
  320 + /* Schedule timer to check current status */
  321 + schedule_delayed_work(&bat_work,
  322 + msecs_to_jiffies(JITTER_DELAY));
  323 +
  324 + return 0;
  325 +
  326 +err_platform:
  327 + if (pdata->gpio_charge_finished >= 0)
  328 + free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
  329 +err_irq:
  330 + if (pdata->gpio_charge_finished >= 0)
  331 + gpio_free(pdata->gpio_charge_finished);
  332 +err_gpio:
  333 + if (pdata->backup_volt_mult)
  334 + power_supply_unregister(&backup_bat.psy);
  335 +err_reg_backup:
  336 + power_supply_unregister(&main_bat.psy);
  337 +err_reg_main:
  338 + return ret;
  339 +}
  340 +
  341 +static int s3c_adc_bat_remove(struct platform_device *pdev)
  342 +{
  343 + struct s3c_adc_client *client = platform_get_drvdata(pdev);
  344 + struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
  345 +
  346 + power_supply_unregister(&main_bat.psy);
  347 + if (pdata->backup_volt_mult)
  348 + power_supply_unregister(&backup_bat.psy);
  349 +
  350 + s3c_adc_release(client);
  351 +
  352 + if (pdata->gpio_charge_finished >= 0) {
  353 + free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
  354 + gpio_free(pdata->gpio_charge_finished);
  355 + }
  356 +
  357 + cancel_delayed_work(&bat_work);
  358 +
  359 + if (pdata->exit)
  360 + pdata->exit();
  361 +
  362 + return 0;
  363 +}
  364 +
  365 +#ifdef CONFIG_PM
  366 +static int s3c_adc_bat_suspend(struct platform_device *pdev,
  367 + pm_message_t state)
  368 +{
  369 + struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
  370 +
  371 + if (pdata->gpio_charge_finished >= 0) {
  372 + if (device_may_wakeup(&pdev->dev))
  373 + enable_irq_wake(
  374 + gpio_to_irq(pdata->gpio_charge_finished));
  375 + else {
  376 + disable_irq(gpio_to_irq(pdata->gpio_charge_finished));
  377 + main_bat.pdata->disable_charger();
  378 + }
  379 + }
  380 +
  381 + return 0;
  382 +}
  383 +
  384 +static int s3c_adc_bat_resume(struct platform_device *pdev)
  385 +{
  386 + struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
  387 +
  388 + if (pdata->gpio_charge_finished >= 0) {
  389 + if (device_may_wakeup(&pdev->dev))
  390 + disable_irq_wake(
  391 + gpio_to_irq(pdata->gpio_charge_finished));
  392 + else
  393 + enable_irq(gpio_to_irq(pdata->gpio_charge_finished));
  394 + }
  395 +
  396 + /* Schedule timer to check current status */
  397 + schedule_delayed_work(&bat_work,
  398 + msecs_to_jiffies(JITTER_DELAY));
  399 +
  400 + return 0;
  401 +}
  402 +#else
  403 +#define s3c_adc_battery_suspend NULL
  404 +#define s3c_adc_battery_resume NULL
  405 +#endif
  406 +
  407 +static struct platform_driver s3c_adc_bat_driver = {
  408 + .driver = {
  409 + .name = "s3c-adc-battery",
  410 + },
  411 + .probe = s3c_adc_bat_probe,
  412 + .remove = s3c_adc_bat_remove,
  413 + .suspend = s3c_adc_bat_suspend,
  414 + .resume = s3c_adc_bat_resume,
  415 +};
  416 +
  417 +static int __init s3c_adc_bat_init(void)
  418 +{
  419 + return platform_driver_register(&s3c_adc_bat_driver);
  420 +}
  421 +module_init(s3c_adc_bat_init);
  422 +
  423 +static void __exit s3c_adc_bat_exit(void)
  424 +{
  425 + platform_driver_unregister(&s3c_adc_bat_driver);
  426 +}
  427 +module_exit(s3c_adc_bat_exit);
  428 +
  429 +MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
  430 +MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controler driver");
  431 +MODULE_LICENSE("GPL");
include/linux/s3c_adc_battery.h
  1 +#ifndef _S3C_ADC_BATTERY_H
  2 +#define _S3C_ADC_BATTERY_H
  3 +
  4 +struct s3c_adc_bat_thresh {
  5 + int volt; /* mV */
  6 + int cur; /* mA */
  7 + int level; /* percent */
  8 +};
  9 +
  10 +struct s3c_adc_bat_pdata {
  11 + int (*init)(void);
  12 + void (*exit)(void);
  13 + void (*enable_charger)(void);
  14 + void (*disable_charger)(void);
  15 +
  16 + int gpio_charge_finished;
  17 +
  18 + const struct s3c_adc_bat_thresh *lut_noac;
  19 + unsigned int lut_noac_cnt;
  20 + const struct s3c_adc_bat_thresh *lut_acin;
  21 + unsigned int lut_acin_cnt;
  22 +
  23 + const unsigned int volt_channel;
  24 + const unsigned int current_channel;
  25 + const unsigned int backup_volt_channel;
  26 +
  27 + const unsigned int volt_mult;
  28 + const unsigned int current_mult;
  29 + const unsigned int backup_volt_mult;
  30 + const unsigned int internal_impedance;
  31 +
  32 + const unsigned int backup_volt_max;
  33 + const unsigned int backup_volt_min;
  34 +};
  35 +
  36 +#endif