diff options
author | Johan Palsson <johan.palsson@stericsson.com> | 2011-01-19 10:02:21 +0100 |
---|---|---|
committer | Henrik CARLING <henrik.carling@stericsson.com> | 2011-01-19 10:20:09 +0100 |
commit | b72f843bb3a01ed92352bf919b2bf30b7c362068 (patch) | |
tree | c14153e797f3336babdb7984f02bd593eb283571 /drivers/power | |
parent | ef67a2d28e45a62fa05e72fc26621987c18e0dc4 (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.c | 152 |
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); |