aboutsummaryrefslogtreecommitdiff
path: root/drivers/power
diff options
context:
space:
mode:
authorJohan Palsson <johan.palsson@stericsson.com>2011-01-19 10:02:21 +0100
committerHenrik CARLING <henrik.carling@stericsson.com>2011-01-19 10:20:09 +0100
commitb72f843bb3a01ed92352bf919b2bf30b7c362068 (patch)
treec14153e797f3336babdb7984f02bd593eb283571 /drivers/power
parentef67a2d28e45a62fa05e72fc26621987c18e0dc4 (diff)
USB: USB driver will inform Battery Manager of the maximum allowed current
The USB driver will notify Battery Manager of the USB state and maximum allowed current to draw from a standard host to be able to stop charging when the device is suspended. ST-Ericsson ID: ER319317 Change-Id: I03ec612dde641d0658fa1f1b1c0267fee91aa103 Signed-off-by: Johan Palsson <johan.palsson@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/12839 Reviewed-by: Praveena NADAHALLY <praveen.nadahally@stericsson.com> Reviewed-by: Henrik CARLING <henrik.carling@stericsson.com>
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/ab8500_charger.c152
1 files changed, 151 insertions, 1 deletions
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index d868b8c84e9..c75eed3c775 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -87,6 +87,15 @@ enum ab8500_charger_link_status {
USB_STAT_NOT_VALID_LINK,
};
+enum ab8500_usb_state {
+ AB8500_BM_USB_STATE_RESET_HS, /* HighSpeed Reset */
+ AB8500_BM_USB_STATE_RESET_FS, /* FullSpeed/LowSpeed Reset */
+ AB8500_BM_USB_STATE_CONFIGURED,
+ AB8500_BM_USB_STATE_SUSPEND,
+ AB8500_BM_USB_STATE_RESUME,
+ AB8500_BM_USB_STATE_MAX,
+};
+
#define to_ab8500_charger_usb_device_info(x) container_of((x), \
struct ab8500_charger, usb_psy);
#define to_ab8500_charger_ac_device_info(x) container_of((x), \
@@ -119,6 +128,13 @@ struct ab8500_charger_event_flags {
bool chgwdexp;
};
+struct ab8500_charger_usb_state {
+ bool usb_changed;
+ int usb_current;
+ enum ab8500_usb_state state;
+ spinlock_t usb_lock;
+};
+
/**
* struct ab8500_charger - ab8500 Charger device information
* @dev: Pointer to the structure device
@@ -132,6 +148,7 @@ struct ab8500_charger_event_flags {
* @pdata: Pointer to the ab8500_charger platform data
* @bat: Pointer to the ab8500_bm platform data
* @flags: Structure for information about events triggered
+ * @usb_state: Structure for usb stack information
* @ac_psy: AC charger power supply
* @usb_psy: USB charger power supply
* @ac: Structure that holds the AC charger properties
@@ -143,6 +160,7 @@ struct ab8500_charger_event_flags {
* @ac_work: Work for checking AC charger connection
* @detect_usb_type_work: Work for detecting the USB type connected
* @usb_link_status_work: Work for checking the new USB link status
+ * @usb_state_changed_work: Work for checking USB state
* @check_usbchgnotok_work: Work for checking USB charger not ok status
* @check_main_thermal_prot_work:
* Work for checking Main thermal status
@@ -160,6 +178,7 @@ struct ab8500_charger {
struct ab8500_charger_platform_data *pdata;
struct ab8500_bm_data *bat;
struct ab8500_charger_event_flags flags;
+ struct ab8500_charger_usb_state usb_state;
struct power_supply ac_psy;
struct power_supply usb_psy;
struct ab8500_charger_info ac;
@@ -170,11 +189,19 @@ struct ab8500_charger {
struct work_struct ac_work;
struct work_struct detect_usb_type_work;
struct work_struct usb_link_status_work;
+ struct work_struct usb_state_changed_work;
struct work_struct check_usbchgnotok_work;
struct work_struct check_main_thermal_prot_work;
struct work_struct check_usb_thermal_prot_work;
};
+/*
+ * TODO: This variable is static in order to get information
+ * about maximum current and USB state from the USB driver
+ * This should be solved in a better way
+ */
+static struct ab8500_charger *static_di;
+
/* AC properties */
static enum power_supply_property ab8500_charger_ac_props[] = {
POWER_SUPPLY_PROP_HEALTH,
@@ -386,6 +413,12 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
switch (link_status) {
case USB_STAT_STD_HOST_NC:
case USB_STAT_STD_HOST_C_NS:
+ case USB_STAT_STD_HOST_C_S:
+ dev_dbg(di->dev, "USB Type - Standard host is "
+ "detected through USB driver\n");
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+ ret = -1;
+ break;
case USB_STAT_HOST_CHG_HS_CHIRP:
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
break;
@@ -403,7 +436,6 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
break;
case USB_STAT_HM_IDGND:
ret = USB_STAT_HM_IDGND;
- case USB_STAT_STD_HOST_C_S:
case USB_STAT_NOT_CONFIGURED:
case USB_STAT_RESERVED:
case USB_STAT_NOT_VALID_LINK:
@@ -655,6 +687,41 @@ static int ab8500_current_to_regval(int curr)
}
/**
+ * ab8500_charger_get_usb_cur() - get usb current
+ * @di: pointer to the ab8500_charger structre
+ *
+ * The usb stack provides the maximum current that can be drawn from
+ * the standard usb host. This will be in mA.
+ * This function converts current in mA to a value that can be written
+ * to the register. Returns -1 if charging is not allowed
+ */
+static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
+{
+ switch (di->usb_state.usb_current) {
+ case 100:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
+ break;
+ case 200:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P19;
+ break;
+ case 300:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P29;
+ break;
+ case 400:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P38;
+ break;
+ case 500:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+ break;
+ default:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+ return -1;
+ break;
+ };
+ return 0;
+}
+
+/**
* ab8500_charger_led_en() - turn on/off chargign led
* @di: pointer to the ab8500_charger structure
* @on: flag to turn on/off the chargign led
@@ -1271,6 +1338,61 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
}
}
+static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
+{
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, usb_state_changed_work);
+
+ if (!di->vbus_detected)
+ return;
+
+ spin_lock(&di->usb_state.usb_lock);
+ di->usb_state.usb_changed = false;
+ spin_unlock(&di->usb_state.usb_lock);
+
+ /*
+ * wait for some time until you get updates from the usb stack
+ * and negotiations are completed
+ */
+ msleep(250);
+
+ if (di->usb_state.usb_changed)
+ return;
+
+ dev_dbg(di->dev, "%s USB state: 0x%02x mA: %d\n",
+ __func__, di->usb_state.state, di->usb_state.usb_current);
+
+ switch (di->usb_state.state) {
+ case AB8500_BM_USB_STATE_RESET_HS:
+ case AB8500_BM_USB_STATE_RESET_FS:
+ case AB8500_BM_USB_STATE_SUSPEND:
+ case AB8500_BM_USB_STATE_MAX:
+ di->usb.charger_connected = 0;
+ power_supply_changed(&di->usb_psy);
+ break;
+
+ case AB8500_BM_USB_STATE_RESUME:
+ /*
+ * when suspend->resume there should be delay
+ * of 1sec for enabling charging
+ */
+ msleep(1000);
+ case AB8500_BM_USB_STATE_CONFIGURED:
+ /*
+ * USB is configured, enable charging with the charging
+ * input current obtained from USB driver
+ */
+ if (!ab8500_charger_get_usb_cur(di)) {
+ di->usb.charger_connected = 1;
+ power_supply_changed(&di->usb_psy);
+ }
+ break;
+
+ default:
+ break;
+ };
+}
+
/**
* ab8500_charger_check_usbchargernotok_work() - check USB chg not ok status
* @work: pointer to the work_struct structure
@@ -1861,6 +1983,26 @@ static struct ab8500_charger_interrupts ab8500_charger_irq[] = {
{"CH_WD_EXP", ab8500_charger_chwdexp_handler},
};
+void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA)
+{
+ struct ab8500_charger *di = static_di;
+
+ dev_err(di->dev, "%s bm_usb_state: 0x%02x mA: %d\n",
+ __func__, bm_usb_state, mA);
+
+ spin_lock(&di->usb_state.usb_lock);
+ di->usb_state.usb_changed = true;
+ spin_unlock(&di->usb_state.usb_lock);
+
+ di->usb_state.state = bm_usb_state;
+ di->usb_state.usb_current = mA;
+
+ queue_work(di->charger_wq, &di->usb_state_changed_work);
+
+ return;
+}
+EXPORT_SYMBOL(ab8500_charger_usb_state_changed);
+
#if defined(CONFIG_PM)
static int ab8500_charger_resume(struct platform_device *pdev)
{
@@ -1961,10 +2103,15 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
if (!di)
return -ENOMEM;
+ static_di = di;
+
/* get parent data */
di->dev = &pdev->dev;
di->parent = dev_get_drvdata(pdev->dev.parent);
+ /* initialize lock */
+ spin_lock_init(&di->usb_state.usb_lock);
+
plat = dev_get_platdata(di->parent->dev);
/* get charger specific platform data */
@@ -2035,6 +2182,9 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
INIT_WORK(&di->detect_usb_type_work,
ab8500_charger_detect_usb_type_work);
+ INIT_WORK(&di->usb_state_changed_work,
+ ab8500_charger_usb_state_changed_work);
+
/* Init work for checking HW status */
INIT_WORK(&di->check_usbchgnotok_work,
ab8500_charger_check_usbchargernotok_work);