summaryrefslogtreecommitdiff
path: root/drivers/power
diff options
context:
space:
mode:
authorShunqing Chen <csq@rock-chips.com>2017-12-11 11:31:21 +0800
committerTao Huang <huangtao@rock-chips.com>2018-06-07 09:32:42 +0800
commit72159f903889f90af01151058de9a8f65234c684 (patch)
tree9dd822f2efad608624af4fc754d7b08d1ef39637 /drivers/power
parentf6fac8b68ad84f5d2bb87d8d5fa8471f65960b91 (diff)
power: charger: add dc detect support for sy6982c
Change-Id: I4050eb8b8760a12a2a2df6ce3c38dc801e822b9f Signed-off-by: Shunqing Chen <csq@rock-chips.com>
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/sy6982c_charger.c110
1 files changed, 109 insertions, 1 deletions
diff --git a/drivers/power/sy6982c_charger.c b/drivers/power/sy6982c_charger.c
index 630729fca11e..dfa82c3f8d27 100644
--- a/drivers/power/sy6982c_charger.c
+++ b/drivers/power/sy6982c_charger.c
@@ -15,12 +15,16 @@
*/
#include <linux/extcon.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/power/rk_usbbc.h>
#include <linux/property.h>
+#include <linux/rk_keys.h>
#include <linux/workqueue.h>
enum charger_t {
@@ -29,6 +33,8 @@ enum charger_t {
USB_TYPE_USB_CHARGER,
USB_TYPE_AC_CHARGER,
USB_TYPE_CDP_CHARGER,
+ DC_TYPE_DC_CHARGER,
+ DC_TYPE_NONE_CHARGER,
};
struct sy6982c_charger {
@@ -36,13 +42,18 @@ struct sy6982c_charger {
struct power_supply *usb_psy;
struct workqueue_struct *usb_charger_wq;
struct delayed_work usb_work;
+ struct workqueue_struct *dc_charger_wq;
+ struct delayed_work dc_work;
struct delayed_work discnt_work;
struct notifier_block cable_cg_nb;
struct notifier_block cable_discnt_nb;
unsigned int bc_event;
enum charger_t usb_charger;
+ enum charger_t dc_charger;
bool extcon;
struct extcon_dev *cable_edev;
+ struct gpio_desc *dc_det_pin;
+ bool support_dc_det;
};
static void sy6982c_cg_bc_evt_worker(struct work_struct *work)
@@ -190,6 +201,8 @@ static int sy6982c_cg_usb_get_property(struct power_supply *psy,
if (cg->usb_charger != USB_TYPE_UNKNOWN_CHARGER &&
cg->usb_charger != USB_TYPE_NONE_CHARGER)
online = 1;
+ if (cg->dc_charger != DC_TYPE_NONE_CHARGER)
+ online = 1;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
val->intval = online;
@@ -234,6 +247,97 @@ static int sy6982c_cg_init_power_supply(struct sy6982c_charger *cg)
return 0;
}
+#ifdef CONFIG_OF
+static int sy6982c_charger_parse_dt(struct sy6982c_charger *cg)
+{
+ struct device *dev = cg->dev;
+
+ cg->dc_det_pin = devm_gpiod_get_optional(dev, "dc-det",
+ GPIOD_IN);
+ if (!IS_ERR_OR_NULL(cg->dc_det_pin)) {
+ cg->support_dc_det = true;
+ } else {
+ dev_err(dev, "invalid dc det gpio!\n");
+ cg->support_dc_det = false;
+ }
+
+ return 0;
+}
+#else
+static int sy6982c_charger_parse_dt(struct sy6982c_charger *cg)
+{
+ return -ENODEV;
+}
+#endif
+
+static enum charger_t sy6982c_charger_get_dc_state(struct sy6982c_charger *cg)
+{
+ return (gpiod_get_value(cg->dc_det_pin)) ?
+ DC_TYPE_DC_CHARGER : DC_TYPE_NONE_CHARGER;
+}
+
+static void sy6982c_charger_dc_det_worker(struct work_struct *work)
+{
+ enum charger_t charger;
+ struct sy6982c_charger *cg = container_of(work,
+ struct sy6982c_charger, dc_work.work);
+
+ charger = sy6982c_charger_get_dc_state(cg);
+ if (charger == DC_TYPE_DC_CHARGER)
+ cg->dc_charger = charger;
+ else
+ cg->dc_charger = DC_TYPE_NONE_CHARGER;
+
+ rk_send_wakeup_key();
+}
+
+static irqreturn_t sy6982c_charger_dc_det_isr(int irq, void *charger)
+{
+ struct sy6982c_charger *cg = (struct sy6982c_charger *)charger;
+
+ queue_delayed_work(cg->dc_charger_wq, &cg->dc_work,
+ msecs_to_jiffies(10));
+
+ return IRQ_HANDLED;
+}
+
+static int sy6982c_charger_init_dc(struct sy6982c_charger *cg)
+{
+ int ret;
+ unsigned long irq_flags;
+ unsigned int dc_det_irq;
+
+ if (!cg->support_dc_det)
+ return 0;
+
+ cg->dc_charger_wq = alloc_ordered_workqueue("%s",
+ WQ_MEM_RECLAIM | WQ_FREEZABLE,
+ "sy6982c-dc-wq");
+ if (!cg->dc_charger_wq)
+ return -EINVAL;
+
+ INIT_DELAYED_WORK(&cg->dc_work, sy6982c_charger_dc_det_worker);
+ cg->dc_charger = DC_TYPE_NONE_CHARGER;
+
+ if (gpiod_get_value(cg->dc_det_pin))
+ cg->dc_charger = DC_TYPE_DC_CHARGER;
+ else
+ cg->dc_charger = DC_TYPE_NONE_CHARGER;
+
+ irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ dc_det_irq = gpiod_to_irq(cg->dc_det_pin);
+ ret = devm_request_irq(cg->dev, dc_det_irq, sy6982c_charger_dc_det_isr,
+ irq_flags, "sy6982c_dc_det", cg);
+ if (ret != 0) {
+ dev_err(cg->dev, "sy6982c_dc_det_irq request failed!\n");
+ return ret;
+ }
+
+ enable_irq_wake(dc_det_irq);
+
+ return 0;
+}
+
static int sy6982c_charger_probe(struct platform_device *pdev)
{
struct sy6982c_charger *cg;
@@ -244,6 +348,8 @@ static int sy6982c_charger_probe(struct platform_device *pdev)
return -ENOMEM;
cg->dev = &pdev->dev;
+ sy6982c_charger_parse_dt(cg);
+ sy6982c_charger_init_dc(cg);
cg->extcon = device_property_read_bool(cg->dev, "extcon");
ret = sy6982c_cg_init_usb(cg);
if (ret) {
@@ -262,12 +368,14 @@ static int sy6982c_charger_probe(struct platform_device *pdev)
return 0;
}
-static void sy6982c_charger_remove(struct platform_device *pdev)
+static int sy6982c_charger_remove(struct platform_device *pdev)
{
struct sy6982c_charger *cg = platform_get_drvdata(pdev);
if (cg->usb_charger_wq)
destroy_workqueue(cg->usb_charger_wq);
+
+ return 0;
}
static const struct of_device_id sy6982c_charger_match[] = {