ab8500_charger: Detect charger removal
Add two new work queues to provide USB and AC charger disconnect detection. Signed-off-by: Lee Jones <lee.jones@linaro.org> Signed-off-by: Anton Vorontsov <anton@enomsg.org>
This commit is contained in:
Родитель
3988a4df34
Коммит
b269fff4f9
|
@ -31,6 +31,7 @@
|
|||
#include <linux/mfd/abx500/ab8500-gpadc.h>
|
||||
#include <linux/mfd/abx500/ux500_chargalg.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
/* Charger constants */
|
||||
#define NO_PW_CONN 0
|
||||
|
@ -68,6 +69,11 @@
|
|||
#define MAIN_CH_NOK 0x01
|
||||
#define VBUS_DET 0x80
|
||||
|
||||
#define MAIN_CH_STATUS2_MAINCHGDROP 0x80
|
||||
#define MAIN_CH_STATUS2_MAINCHARGERDETDBNC 0x40
|
||||
#define USB_CH_VBUSDROP 0x40
|
||||
#define USB_CH_VBUSDETDBNC 0x01
|
||||
|
||||
/* UsbLineStatus register bit masks */
|
||||
#define AB8500_USB_LINK_STATUS 0x78
|
||||
#define AB8500_STD_HOST_SUSP 0x18
|
||||
|
@ -82,6 +88,8 @@
|
|||
/* Step up/down delay in us */
|
||||
#define STEP_UDELAY 1000
|
||||
|
||||
#define CHARGER_STATUS_POLL 10 /* in ms */
|
||||
|
||||
/* UsbLineStatus register - usb types */
|
||||
enum ab8500_charger_link_status {
|
||||
USB_STAT_NOT_CONFIGURED,
|
||||
|
@ -203,6 +211,10 @@ struct ab8500_charger_usb_state {
|
|||
* @check_usbchgnotok_work: Work for checking USB charger not ok status
|
||||
* @kick_wd_work: Work for kicking the charger watchdog in case
|
||||
* of ABB rev 1.* due to the watchog logic bug
|
||||
* @ac_charger_attached_work: Work for checking if AC charger is still
|
||||
* connected
|
||||
* @usb_charger_attached_work: Work for checking if USB charger is still
|
||||
* connected
|
||||
* @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
|
||||
|
@ -211,6 +223,7 @@ struct ab8500_charger_usb_state {
|
|||
* Work for checking Main thermal status
|
||||
* @check_usb_thermal_prot_work:
|
||||
* Work for checking USB thermal status
|
||||
* @charger_attached_mutex: For controlling the wakelock
|
||||
*/
|
||||
struct ab8500_charger {
|
||||
struct device *dev;
|
||||
|
@ -239,6 +252,8 @@ struct ab8500_charger {
|
|||
struct delayed_work check_hw_failure_work;
|
||||
struct delayed_work check_usbchgnotok_work;
|
||||
struct delayed_work kick_wd_work;
|
||||
struct delayed_work ac_charger_attached_work;
|
||||
struct delayed_work usb_charger_attached_work;
|
||||
struct work_struct ac_work;
|
||||
struct work_struct detect_usb_type_work;
|
||||
struct work_struct usb_link_status_work;
|
||||
|
@ -247,6 +262,7 @@ struct ab8500_charger {
|
|||
struct work_struct check_usb_thermal_prot_work;
|
||||
struct usb_phy *usb_phy;
|
||||
struct notifier_block nb;
|
||||
struct mutex charger_attached_mutex;
|
||||
};
|
||||
|
||||
/* AC properties */
|
||||
|
@ -349,6 +365,19 @@ static void ab8500_charger_set_usb_connected(struct ab8500_charger *di,
|
|||
dev_dbg(di->dev, "USB connected:%i\n", connected);
|
||||
di->usb.charger_connected = connected;
|
||||
sysfs_notify(&di->usb_chg.psy.dev->kobj, NULL, "present");
|
||||
|
||||
if (connected) {
|
||||
mutex_lock(&di->charger_attached_mutex);
|
||||
mutex_unlock(&di->charger_attached_mutex);
|
||||
|
||||
queue_delayed_work(di->charger_wq,
|
||||
&di->usb_charger_attached_work,
|
||||
HZ);
|
||||
} else {
|
||||
cancel_delayed_work_sync(&di->usb_charger_attached_work);
|
||||
mutex_lock(&di->charger_attached_mutex);
|
||||
mutex_unlock(&di->charger_attached_mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1706,6 +1735,84 @@ static void ab8500_charger_ac_work(struct work_struct *work)
|
|||
sysfs_notify(&di->ac_chg.psy.dev->kobj, NULL, "present");
|
||||
}
|
||||
|
||||
static void ab8500_charger_usb_attached_work(struct work_struct *work)
|
||||
{
|
||||
struct ab8500_charger *di = container_of(work,
|
||||
struct ab8500_charger,
|
||||
usb_charger_attached_work.work);
|
||||
int usbch = (USB_CH_VBUSDROP | USB_CH_VBUSDETDBNC);
|
||||
int ret, i;
|
||||
u8 statval;
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
ret = abx500_get_register_interruptible(di->dev,
|
||||
AB8500_CHARGER,
|
||||
AB8500_CH_USBCH_STAT1_REG,
|
||||
&statval);
|
||||
if (ret < 0) {
|
||||
dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
|
||||
goto reschedule;
|
||||
}
|
||||
if ((statval & usbch) != usbch)
|
||||
goto reschedule;
|
||||
|
||||
msleep(CHARGER_STATUS_POLL);
|
||||
}
|
||||
|
||||
ab8500_charger_usb_en(&di->usb_chg, 0, 0, 0);
|
||||
|
||||
mutex_lock(&di->charger_attached_mutex);
|
||||
mutex_unlock(&di->charger_attached_mutex);
|
||||
|
||||
return;
|
||||
|
||||
reschedule:
|
||||
queue_delayed_work(di->charger_wq,
|
||||
&di->usb_charger_attached_work,
|
||||
HZ);
|
||||
}
|
||||
|
||||
static void ab8500_charger_ac_attached_work(struct work_struct *work)
|
||||
{
|
||||
|
||||
struct ab8500_charger *di = container_of(work,
|
||||
struct ab8500_charger,
|
||||
ac_charger_attached_work.work);
|
||||
int mainch = (MAIN_CH_STATUS2_MAINCHGDROP |
|
||||
MAIN_CH_STATUS2_MAINCHARGERDETDBNC);
|
||||
int ret, i;
|
||||
u8 statval;
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
ret = abx500_get_register_interruptible(di->dev,
|
||||
AB8500_CHARGER,
|
||||
AB8500_CH_STATUS2_REG,
|
||||
&statval);
|
||||
if (ret < 0) {
|
||||
dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
|
||||
goto reschedule;
|
||||
}
|
||||
|
||||
if ((statval & mainch) != mainch)
|
||||
goto reschedule;
|
||||
|
||||
msleep(CHARGER_STATUS_POLL);
|
||||
}
|
||||
|
||||
ab8500_charger_ac_en(&di->ac_chg, 0, 0, 0);
|
||||
queue_work(di->charger_wq, &di->ac_work);
|
||||
|
||||
mutex_lock(&di->charger_attached_mutex);
|
||||
mutex_unlock(&di->charger_attached_mutex);
|
||||
|
||||
return;
|
||||
|
||||
reschedule:
|
||||
queue_delayed_work(di->charger_wq,
|
||||
&di->ac_charger_attached_work,
|
||||
HZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* ab8500_charger_detect_usb_type_work() - work to detect USB type
|
||||
* @work: Pointer to the work_struct structure
|
||||
|
@ -1986,6 +2093,10 @@ static irqreturn_t ab8500_charger_mainchunplugdet_handler(int irq, void *_di)
|
|||
dev_dbg(di->dev, "Main charger unplugged\n");
|
||||
queue_work(di->charger_wq, &di->ac_work);
|
||||
|
||||
cancel_delayed_work_sync(&di->ac_charger_attached_work);
|
||||
mutex_lock(&di->charger_attached_mutex);
|
||||
mutex_unlock(&di->charger_attached_mutex);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -2003,6 +2114,11 @@ static irqreturn_t ab8500_charger_mainchplugdet_handler(int irq, void *_di)
|
|||
dev_dbg(di->dev, "Main charger plugged\n");
|
||||
queue_work(di->charger_wq, &di->ac_work);
|
||||
|
||||
mutex_lock(&di->charger_attached_mutex);
|
||||
mutex_unlock(&di->charger_attached_mutex);
|
||||
queue_delayed_work(di->charger_wq,
|
||||
&di->ac_charger_attached_work,
|
||||
HZ);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -2634,7 +2750,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
|
|||
struct device_node *np = pdev->dev.of_node;
|
||||
struct abx500_bm_data *plat = pdev->dev.platform_data;
|
||||
struct ab8500_charger *di;
|
||||
int irq, i, charger_status, ret = 0;
|
||||
int irq, i, charger_status, ret = 0, ch_stat;
|
||||
|
||||
di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
|
||||
if (!di) {
|
||||
|
@ -2713,12 +2829,19 @@ static int ab8500_charger_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_init(&di->charger_attached_mutex);
|
||||
|
||||
/* Init work for HW failure check */
|
||||
INIT_DEFERRABLE_WORK(&di->check_hw_failure_work,
|
||||
ab8500_charger_check_hw_failure_work);
|
||||
INIT_DEFERRABLE_WORK(&di->check_usbchgnotok_work,
|
||||
ab8500_charger_check_usbchargernotok_work);
|
||||
|
||||
INIT_DELAYED_WORK(&di->ac_charger_attached_work,
|
||||
ab8500_charger_ac_attached_work);
|
||||
INIT_DELAYED_WORK(&di->usb_charger_attached_work,
|
||||
ab8500_charger_usb_attached_work);
|
||||
|
||||
/*
|
||||
* For ABB revision 1.0 and 1.1 there is a bug in the watchdog
|
||||
* logic. That means we have to continously kick the charger
|
||||
|
@ -2832,6 +2955,23 @@ static int ab8500_charger_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, di);
|
||||
|
||||
mutex_lock(&di->charger_attached_mutex);
|
||||
|
||||
ch_stat = ab8500_charger_detect_chargers(di);
|
||||
|
||||
if ((ch_stat & AC_PW_CONN) == AC_PW_CONN) {
|
||||
queue_delayed_work(di->charger_wq,
|
||||
&di->ac_charger_attached_work,
|
||||
HZ);
|
||||
}
|
||||
if ((ch_stat & USB_PW_CONN) == USB_PW_CONN) {
|
||||
queue_delayed_work(di->charger_wq,
|
||||
&di->usb_charger_attached_work,
|
||||
HZ);
|
||||
}
|
||||
|
||||
mutex_unlock(&di->charger_attached_mutex);
|
||||
|
||||
return ret;
|
||||
|
||||
free_irq:
|
||||
|
|
Загрузка…
Ссылка в новой задаче