mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 03:40:35 +09:00
power_supply: bq25700: add dual type-c charge
Change-Id: I97e4962229ed337752134aaf31b3f3e75b113440 Signed-off-by: Shunqing Chen <csq@rock-chips.com>
This commit is contained in:
committed by
Huang, Tao
parent
606a3d4179
commit
ca14fcd7f8
@@ -27,6 +27,8 @@
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
static int dbg_enable;
|
||||
module_param_named(dbg_level, dbg_enable, int, 0644);
|
||||
@@ -109,6 +111,19 @@ enum charger_t {
|
||||
DC_TYPE_NONE_CHARGER,
|
||||
};
|
||||
|
||||
enum usb_status_t {
|
||||
USB_STATUS_NONE,
|
||||
USB_STATUS_USB,
|
||||
USB_STATUS_AC,
|
||||
USB_STATUS_PD,
|
||||
USB_STATUS_OTG,
|
||||
};
|
||||
|
||||
enum tpyec_port_t {
|
||||
USB_TYPEC_0,
|
||||
USB_TYPEC_1,
|
||||
};
|
||||
|
||||
/* initial field values, converted to register values */
|
||||
struct bq25700_init_data {
|
||||
u32 ichg; /* charge current */
|
||||
@@ -158,18 +173,34 @@ struct bq25700_device {
|
||||
struct delayed_work pd_work;
|
||||
struct delayed_work host_work;
|
||||
struct delayed_work discnt_work;
|
||||
struct delayed_work usb_work1;
|
||||
struct delayed_work pd_work1;
|
||||
struct delayed_work host_work1;
|
||||
struct delayed_work discnt_work1;
|
||||
struct delayed_work irq_work;
|
||||
struct notifier_block cable_cg_nb;
|
||||
struct notifier_block cable_pd_nb;
|
||||
struct notifier_block cable_host_nb;
|
||||
struct notifier_block cable_discnt_nb;
|
||||
struct notifier_block cable_cg_nb1;
|
||||
struct notifier_block cable_pd_nb1;
|
||||
struct notifier_block cable_host_nb1;
|
||||
struct notifier_block cable_discnt_nb1;
|
||||
struct extcon_dev *cable_edev;
|
||||
struct extcon_dev *cable_edev_1;
|
||||
int typec0_status;
|
||||
int typec1_status;
|
||||
struct gpio_desc *typec0_enable_io;
|
||||
struct gpio_desc *typec1_enable_io;
|
||||
struct gpio_desc *typec0_discharge_io;
|
||||
struct gpio_desc *typec1_discharge_io;
|
||||
|
||||
struct regmap *regmap;
|
||||
struct regmap_field *rmap_fields[F_MAX_FIELDS];
|
||||
int chip_id;
|
||||
struct bq25700_init_data init_data;
|
||||
struct bq25700_state state;
|
||||
int pd_charge_only;
|
||||
};
|
||||
|
||||
static const struct reg_field bq25700_reg_fields[] = {
|
||||
@@ -446,24 +477,136 @@ static int bq25700_get_chip_state(struct bq25700_device *charger,
|
||||
*state_fields[i].data = ret;
|
||||
}
|
||||
|
||||
DBG("status:\n");
|
||||
DBG("AC_STAT: %d\n", state->ac_stat);
|
||||
DBG("ICO_DONE: %d\n", state->ico_done);
|
||||
DBG("IN_VINDPM: %d\n", state->in_vindpm);
|
||||
DBG("IN_IINDPM: %d\n", state->in_iindpm);
|
||||
DBG("IN_FCHRG: %d\n", state->in_fchrg);
|
||||
DBG("IN_PCHRG: %d\n", state->in_pchrg);
|
||||
DBG("IN_OTG: %d\n", state->in_otg);
|
||||
DBG("F_ACOV: %d\n", state->fault_acov);
|
||||
DBG("F_BATOC: %d\n", state->fault_batoc);
|
||||
DBG("F_ACOC: %d\n", state->fault_acoc);
|
||||
DBG("SYSOVP_STAT: %d\n", state->sysovp_stat);
|
||||
DBG("F_LATCHOFF: %d\n", state->fault_latchoff);
|
||||
DBG("F_OTGOVP: %d\n", state->fault_otg_ovp);
|
||||
DBG("F_OTGOCP: %d\n", state->fault_otg_ocp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq25700_dump_regs(struct bq25700_device *charger)
|
||||
{
|
||||
u32 val = 0;
|
||||
struct bq25700_state state;
|
||||
int ret = 0;
|
||||
|
||||
ret = bq25700_field_write(charger, ADC_START, 1);
|
||||
if (ret < 0) {
|
||||
DBG("error: ADC_START\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
DBG("\n==================================\n");
|
||||
regmap_read(charger->regmap, 0x12, &val);
|
||||
DBG("REG0x12 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x14, &val);
|
||||
DBG("REG0x14 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x15, &val);
|
||||
DBG("REG0x15 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x30, &val);
|
||||
DBG("REG0x30 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x31, &val);
|
||||
DBG("REG0x31 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x32, &val);
|
||||
DBG("REG0x32 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x33, &val);
|
||||
DBG("REG0x33 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x34, &val);
|
||||
DBG("REG0x34 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x35, &val);
|
||||
DBG("REG0x35 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x20, &val);
|
||||
DBG("REG0x20 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x21, &val);
|
||||
DBG("REG0x21 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x22, &val);
|
||||
DBG("REG0x22 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x23, &val);
|
||||
DBG("REG0x23 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x24, &val);
|
||||
DBG("REG0x24 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x25, &val);
|
||||
DBG("REG0x25 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x26, &val);
|
||||
DBG("REG0x26 : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x3b, &val);
|
||||
DBG("REG0x3b : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x3c, &val);
|
||||
DBG("REG0x3c : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x3d, &val);
|
||||
DBG("REG0x3d : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x3e, &val);
|
||||
DBG("REG0x3e : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0x3f, &val);
|
||||
DBG("REG0x3f : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0xfe, &val);
|
||||
DBG("REG0xfe : 0x%x\n", val);
|
||||
regmap_read(charger->regmap, 0xff, &val);
|
||||
DBG("REG0xff : 0x%x\n", val);
|
||||
|
||||
DBG("battery charge current: %dmA\n",
|
||||
bq25700_field_read(charger, OUTPUT_DSG_CUR) * 64);
|
||||
DBG("battery discharge current: %dmA\n",
|
||||
bq25700_field_read(charger, OUTPUT_CHG_CUR) * 256);
|
||||
DBG("VSYS volatge: %dmV\n",
|
||||
2880 + bq25700_field_read(charger, OUTPUT_SYS_VOL) * 64);
|
||||
DBG("BAT volatge: %dmV\n",
|
||||
2880 + bq25700_field_read(charger, OUTPUT_BAT_VOL) * 64);
|
||||
|
||||
DBG("SET CHARGE_CURRENT: %dmA\n",
|
||||
bq25700_field_read(charger, CHARGE_CURRENT) * 64);
|
||||
DBG("MAX_CHARGE_VOLTAGE: %dmV\n",
|
||||
bq25700_field_read(charger, MAX_CHARGE_VOLTAGE) * 16);
|
||||
DBG(" INPUT_VOLTAGE: %dmV\n",
|
||||
3200 + bq25700_field_read(charger, INPUT_VOLTAGE) * 64);
|
||||
DBG(" INPUT_CURRENT: %dmA\n",
|
||||
bq25700_field_read(charger, INPUT_CURRENT) * 50);
|
||||
DBG(" MIN_SYS_VOTAGE: %dmV\n",
|
||||
1024 + bq25700_field_read(charger, MIN_SYS_VOTAGE) * 256);
|
||||
bq25700_get_chip_state(charger, &state);
|
||||
DBG("status:\n");
|
||||
DBG("AC_STAT: %d\n", state.ac_stat);
|
||||
DBG("ICO_DONE: %d\n", state.ico_done);
|
||||
DBG("IN_VINDPM: %d\n", state.in_vindpm);
|
||||
DBG("IN_IINDPM: %d\n", state.in_iindpm);
|
||||
DBG("IN_FCHRG: %d\n", state.in_fchrg);
|
||||
DBG("IN_PCHRG: %d\n", state.in_pchrg);
|
||||
DBG("IN_OTG: %d\n", state.in_otg);
|
||||
DBG("F_ACOV: %d\n", state.fault_acov);
|
||||
DBG("F_BATOC: %d\n", state.fault_batoc);
|
||||
DBG("F_ACOC: %d\n", state.fault_acoc);
|
||||
DBG("SYSOVP_STAT: %d\n", state.sysovp_stat);
|
||||
DBG("F_LATCHOFF: %d\n", state.fault_latchoff);
|
||||
DBG("F_OTGOVP: %d\n", state.fault_otg_ovp);
|
||||
DBG("F_OTGOCP: %d\n", state.fault_otg_ocp);
|
||||
|
||||
DBG("\n+++++++++++++++++++++++++++++++++++++++++++++++++\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t bq25700_charge_info_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct bq25700_device *charger = dev_get_drvdata(dev);
|
||||
|
||||
bq25700_dump_regs(charger);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct device_attribute bq25700_charger_attr[] = {
|
||||
__ATTR(charge_info, 0664, bq25700_charge_info_show, NULL),
|
||||
};
|
||||
|
||||
static void bq25700_init_sysfs(struct bq25700_device *charger)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bq25700_charger_attr); i++) {
|
||||
ret = sysfs_create_file(&charger->dev->kobj,
|
||||
&bq25700_charger_attr[i].attr);
|
||||
if (ret)
|
||||
dev_err(charger->dev, "create charger node(%s) error\n",
|
||||
bq25700_charger_attr[i].attr.name);
|
||||
}
|
||||
}
|
||||
|
||||
static u32 bq25700_find_idx(u32 value, enum bq25700_table_ids id)
|
||||
{
|
||||
u32 idx;
|
||||
@@ -867,16 +1010,62 @@ handled:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void bq25700_pd_evt_worker(struct work_struct *work)
|
||||
static void bq25700_enable_typec0(struct bq25700_device *charger)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(charger->typec0_enable_io))
|
||||
gpiod_direction_output(charger->typec0_enable_io, 1);
|
||||
if (!IS_ERR_OR_NULL(charger->typec1_enable_io))
|
||||
gpiod_direction_output(charger->typec1_enable_io, 0);
|
||||
}
|
||||
|
||||
static void bq25700_enable_typec1(struct bq25700_device *charger)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(charger->typec0_enable_io))
|
||||
gpiod_direction_output(charger->typec0_enable_io, 0);
|
||||
if (!IS_ERR_OR_NULL(charger->typec1_enable_io))
|
||||
gpiod_direction_output(charger->typec1_enable_io, 1);
|
||||
}
|
||||
|
||||
static void bq25700_disable_charge(struct bq25700_device *charger)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(charger->typec0_enable_io))
|
||||
gpiod_direction_output(charger->typec0_enable_io, 0);
|
||||
if (!IS_ERR_OR_NULL(charger->typec1_enable_io))
|
||||
gpiod_direction_output(charger->typec1_enable_io, 0);
|
||||
}
|
||||
|
||||
static void bq25700_typec0_discharge(struct bq25700_device *charger)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(charger->typec0_discharge_io))
|
||||
gpiod_direction_output(charger->typec0_discharge_io, 1);
|
||||
msleep(20);
|
||||
if (!IS_ERR_OR_NULL(charger->typec0_discharge_io))
|
||||
gpiod_direction_output(charger->typec0_discharge_io, 0);
|
||||
}
|
||||
|
||||
static void bq25700_typec1_discharge(struct bq25700_device *charger)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(charger->typec1_discharge_io))
|
||||
gpiod_direction_output(charger->typec1_discharge_io, 1);
|
||||
msleep(20);
|
||||
if (!IS_ERR_OR_NULL(charger->typec1_discharge_io))
|
||||
gpiod_direction_output(charger->typec1_discharge_io, 0);
|
||||
}
|
||||
|
||||
static void bq25700_pd_connect(struct bq25700_device *charger,
|
||||
struct extcon_dev *edev,
|
||||
enum tpyec_port_t port)
|
||||
{
|
||||
struct bq25700_device *charger =
|
||||
container_of(work, struct bq25700_device, pd_work.work);
|
||||
struct extcon_dev *edev = charger->cable_edev;
|
||||
union extcon_property_value prop_val;
|
||||
struct bq25700_state state;
|
||||
int ret;
|
||||
int vol, cur;
|
||||
int vol_idx, cur_idx;
|
||||
int i;
|
||||
|
||||
if (charger->typec0_status == USB_STATUS_PD ||
|
||||
charger->typec1_status == USB_STATUS_PD)
|
||||
return;
|
||||
|
||||
if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_FAST) > 0) {
|
||||
ret = extcon_get_property(edev, EXTCON_CHG_USB_FAST,
|
||||
@@ -886,6 +1075,19 @@ static void bq25700_pd_evt_worker(struct work_struct *work)
|
||||
vol = prop_val.intval & 0xffff;
|
||||
cur = prop_val.intval >> 15;
|
||||
if (ret == 0) {
|
||||
if (port == USB_TYPEC_0) {
|
||||
charger->typec0_status = USB_STATUS_PD;
|
||||
bq25700_enable_typec0(charger);
|
||||
} else {
|
||||
charger->typec1_status = USB_STATUS_PD;
|
||||
bq25700_enable_typec1(charger);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (!bq25700_field_read(charger, AC_STAT) && i < 5) {
|
||||
msleep(1000);
|
||||
i++;
|
||||
}
|
||||
vol_idx = bq25700_find_idx((vol - 1280) * 1000,
|
||||
TBL_INPUTVOL);
|
||||
cur_idx = bq25700_find_idx(cur * 1000, TBL_INPUTCUR);
|
||||
@@ -901,6 +1103,24 @@ static void bq25700_pd_evt_worker(struct work_struct *work)
|
||||
}
|
||||
}
|
||||
|
||||
static void bq25700_pd_evt_worker(struct work_struct *work)
|
||||
{
|
||||
struct bq25700_device *charger = container_of(work,
|
||||
struct bq25700_device, pd_work.work);
|
||||
struct extcon_dev *edev = charger->cable_edev;
|
||||
|
||||
bq25700_pd_connect(charger, edev, USB_TYPEC_0);
|
||||
}
|
||||
|
||||
static void bq25700_pd_evt_worker1(struct work_struct *work)
|
||||
{
|
||||
struct bq25700_device *charger = container_of(work,
|
||||
struct bq25700_device, pd_work1.work);
|
||||
struct extcon_dev *edev = charger->cable_edev_1;
|
||||
|
||||
bq25700_pd_connect(charger, edev, USB_TYPEC_1);
|
||||
}
|
||||
|
||||
static int bq25700_pd_evt_notifier(struct notifier_block *nb,
|
||||
unsigned long event,
|
||||
void *ptr)
|
||||
@@ -914,15 +1134,28 @@ static int bq25700_pd_evt_notifier(struct notifier_block *nb,
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static void bq25700_charger_evt_worker(struct work_struct *work)
|
||||
static int bq25700_pd_evt_notifier1(struct notifier_block *nb,
|
||||
unsigned long event,
|
||||
void *ptr)
|
||||
{
|
||||
struct bq25700_device *charger =
|
||||
container_of(work, struct bq25700_device, usb_work.work);
|
||||
struct extcon_dev *edev = charger->cable_edev;
|
||||
container_of(nb, struct bq25700_device, cable_pd_nb1);
|
||||
|
||||
queue_delayed_work(charger->usb_charger_wq, &charger->pd_work1,
|
||||
msecs_to_jiffies(10));
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static void bq25700_charger_evt_handel(struct bq25700_device *charger,
|
||||
struct extcon_dev *edev,
|
||||
enum tpyec_port_t port)
|
||||
{
|
||||
struct bq25700_state state;
|
||||
enum charger_t charger_state = USB_TYPE_UNKNOWN_CHARGER;
|
||||
|
||||
if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_FAST) > 0)
|
||||
if (charger->typec0_status == USB_STATUS_PD ||
|
||||
charger->typec1_status == USB_STATUS_PD)
|
||||
return;
|
||||
|
||||
/* Determine cable/charger type */
|
||||
@@ -943,12 +1176,43 @@ static void bq25700_charger_evt_worker(struct work_struct *work)
|
||||
charger->init_data.input_current_cdp);
|
||||
DBG("USB_TYPE_CDP_CHARGER\n");
|
||||
}
|
||||
if (port == USB_TYPEC_0) {
|
||||
if (charger_state == USB_TYPE_USB_CHARGER)
|
||||
charger->typec0_status = USB_STATUS_USB;
|
||||
else
|
||||
charger->typec0_status = USB_STATUS_AC;
|
||||
bq25700_enable_typec0(charger);
|
||||
} else {
|
||||
if (charger_state == USB_TYPE_USB_CHARGER)
|
||||
charger->typec1_status = USB_STATUS_USB;
|
||||
else
|
||||
charger->typec1_status = USB_STATUS_AC;
|
||||
bq25700_enable_typec1(charger);
|
||||
}
|
||||
|
||||
bq25700_get_chip_state(charger, &state);
|
||||
charger->state = state;
|
||||
power_supply_changed(charger->supply_charger);
|
||||
}
|
||||
|
||||
static void bq25700_charger_evt_worker(struct work_struct *work)
|
||||
{
|
||||
struct bq25700_device *charger = container_of(work,
|
||||
struct bq25700_device, usb_work.work);
|
||||
struct extcon_dev *edev = charger->cable_edev;
|
||||
|
||||
bq25700_charger_evt_handel(charger, edev, USB_TYPEC_0);
|
||||
}
|
||||
|
||||
static void bq25700_charger_evt_worker1(struct work_struct *work)
|
||||
{
|
||||
struct bq25700_device *charger = container_of(work,
|
||||
struct bq25700_device, usb_work1.work);
|
||||
struct extcon_dev *edev = charger->cable_edev_1;
|
||||
|
||||
bq25700_charger_evt_handel(charger, edev, USB_TYPEC_1);
|
||||
}
|
||||
|
||||
static int bq25700_charger_evt_notifier(struct notifier_block *nb,
|
||||
unsigned long event,
|
||||
void *ptr)
|
||||
@@ -962,6 +1226,19 @@ static int bq25700_charger_evt_notifier(struct notifier_block *nb,
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int bq25700_charger_evt_notifier1(struct notifier_block *nb,
|
||||
unsigned long event,
|
||||
void *ptr)
|
||||
{
|
||||
struct bq25700_device *charger =
|
||||
container_of(nb, struct bq25700_device, cable_cg_nb1);
|
||||
|
||||
queue_delayed_work(charger->usb_charger_wq, &charger->usb_work1,
|
||||
msecs_to_jiffies(10));
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static void bq25700_host_evt_worker(struct work_struct *work)
|
||||
{
|
||||
struct bq25700_device *charger =
|
||||
@@ -978,6 +1255,22 @@ static void bq25700_host_evt_worker(struct work_struct *work)
|
||||
}
|
||||
}
|
||||
|
||||
static void bq25700_host_evt_worker1(struct work_struct *work)
|
||||
{
|
||||
struct bq25700_device *charger =
|
||||
container_of(work, struct bq25700_device, host_work1.work);
|
||||
struct extcon_dev *edev = charger->cable_edev_1;
|
||||
|
||||
/* Determine cable/charger type */
|
||||
if (extcon_get_cable_state_(edev, EXTCON_USB_VBUS_EN) > 0) {
|
||||
bq25700_field_write(charger, EN_OTG, 1);
|
||||
DBG("OTG enable\n");
|
||||
} else if (extcon_get_cable_state_(edev, EXTCON_USB_VBUS_EN) == 0) {
|
||||
bq25700_field_write(charger, EN_OTG, 0);
|
||||
DBG("OTG disable\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int bq25700_host_evt_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
@@ -990,14 +1283,34 @@ static int bq25700_host_evt_notifier(struct notifier_block *nb,
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static void bq25700_discnt_evt_worker(struct work_struct *work)
|
||||
static int bq25700_host_evt_notifier1(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct bq25700_device *charger =
|
||||
container_of(nb, struct bq25700_device, cable_host_nb1);
|
||||
|
||||
queue_delayed_work(charger->usb_charger_wq, &charger->host_work1,
|
||||
msecs_to_jiffies(10));
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static void bq25700_discnt(struct bq25700_device *charger,
|
||||
enum tpyec_port_t port)
|
||||
{
|
||||
int vol_idx;
|
||||
struct bq25700_device *charger =
|
||||
container_of(work, struct bq25700_device, discnt_work.work);
|
||||
struct bq25700_state state;
|
||||
|
||||
if (extcon_get_cable_state_(charger->cable_edev, EXTCON_USB) == 0) {
|
||||
if (bq25700_field_read(charger, AC_STAT) == 0) {
|
||||
bq25700_disable_charge(charger);
|
||||
if (port == USB_TYPEC_0) {
|
||||
bq25700_typec0_discharge(charger);
|
||||
charger->typec0_status = USB_STATUS_NONE;
|
||||
} else {
|
||||
bq25700_typec1_discharge(charger);
|
||||
charger->typec1_status = USB_STATUS_NONE;
|
||||
}
|
||||
|
||||
vol_idx = bq25700_find_idx(DEFAULT_INPUTVOL, TBL_INPUTVOL);
|
||||
bq25700_field_write(charger, INPUT_VOLTAGE, vol_idx);
|
||||
bq25700_field_write(charger, INPUT_CURRENT,
|
||||
@@ -1008,6 +1321,23 @@ static void bq25700_discnt_evt_worker(struct work_struct *work)
|
||||
}
|
||||
}
|
||||
|
||||
static void bq25700_discnt_evt_worker(struct work_struct *work)
|
||||
{
|
||||
struct bq25700_device *charger = container_of(work,
|
||||
struct bq25700_device,
|
||||
discnt_work.work);
|
||||
|
||||
bq25700_discnt(charger, USB_TYPEC_0);
|
||||
}
|
||||
|
||||
static void bq25700_discnt_evt_worker1(struct work_struct *work)
|
||||
{
|
||||
struct bq25700_device *charger = container_of(work,
|
||||
struct bq25700_device,
|
||||
discnt_work1.work);
|
||||
bq25700_discnt(charger, USB_TYPEC_1);
|
||||
}
|
||||
|
||||
static int bq25700_discnt_evt_notfier(struct notifier_block *nb,
|
||||
unsigned long event,
|
||||
void *ptr)
|
||||
@@ -1017,122 +1347,256 @@ static int bq25700_discnt_evt_notfier(struct notifier_block *nb,
|
||||
|
||||
queue_delayed_work(charger->usb_charger_wq,
|
||||
&charger->discnt_work,
|
||||
msecs_to_jiffies(10));
|
||||
msecs_to_jiffies(0));
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int bq25700_discnt_evt_notfier1(struct notifier_block *nb,
|
||||
unsigned long event,
|
||||
void *ptr)
|
||||
{
|
||||
struct bq25700_device *charger =
|
||||
container_of(nb, struct bq25700_device, cable_discnt_nb1);
|
||||
|
||||
queue_delayed_work(charger->usb_charger_wq,
|
||||
&charger->discnt_work1,
|
||||
msecs_to_jiffies(0));
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int bq25700_register_cg_extcon(struct bq25700_device *charger,
|
||||
struct extcon_dev *edev,
|
||||
struct notifier_block *able_cg_nb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = extcon_register_notifier(edev,
|
||||
EXTCON_CHG_USB_SDP,
|
||||
able_cg_nb);
|
||||
if (ret < 0) {
|
||||
dev_err(charger->dev, "failed to register notifier for SDP\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = extcon_register_notifier(edev,
|
||||
EXTCON_CHG_USB_DCP,
|
||||
able_cg_nb);
|
||||
if (ret < 0) {
|
||||
dev_err(charger->dev, "failed to register notifier for DCP\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = extcon_register_notifier(edev,
|
||||
EXTCON_CHG_USB_CDP,
|
||||
able_cg_nb);
|
||||
if (ret < 0) {
|
||||
dev_err(charger->dev, "failed to register notifier for CDP\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq25700_register_cg_nb(struct bq25700_device *charger)
|
||||
{
|
||||
if (charger->cable_edev) {
|
||||
/* Register chargers */
|
||||
INIT_DELAYED_WORK(&charger->usb_work,
|
||||
bq25700_charger_evt_worker);
|
||||
charger->cable_cg_nb.notifier_call =
|
||||
bq25700_charger_evt_notifier;
|
||||
bq25700_register_cg_extcon(charger, charger->cable_edev,
|
||||
&charger->cable_cg_nb);
|
||||
}
|
||||
|
||||
if (charger->cable_edev_1) {
|
||||
INIT_DELAYED_WORK(&charger->usb_work1,
|
||||
bq25700_charger_evt_worker1);
|
||||
charger->cable_cg_nb1.notifier_call =
|
||||
bq25700_charger_evt_notifier1;
|
||||
bq25700_register_cg_extcon(charger, charger->cable_edev_1,
|
||||
&charger->cable_cg_nb1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq25700_register_discnt_nb(struct bq25700_device *charger)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Register discnt usb */
|
||||
if (charger->cable_edev) {
|
||||
INIT_DELAYED_WORK(&charger->discnt_work,
|
||||
bq25700_discnt_evt_worker);
|
||||
charger->cable_discnt_nb.notifier_call =
|
||||
bq25700_discnt_evt_notfier;
|
||||
ret = extcon_register_notifier(charger->cable_edev,
|
||||
EXTCON_USB,
|
||||
&charger->cable_discnt_nb);
|
||||
if (ret < 0) {
|
||||
dev_err(charger->dev,
|
||||
"failed to register notifier for HOST\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (charger->cable_edev_1) {
|
||||
INIT_DELAYED_WORK(&charger->discnt_work1,
|
||||
bq25700_discnt_evt_worker1);
|
||||
charger->cable_discnt_nb1.notifier_call =
|
||||
bq25700_discnt_evt_notfier1;
|
||||
ret = extcon_register_notifier(charger->cable_edev_1,
|
||||
EXTCON_USB,
|
||||
&charger->cable_discnt_nb1);
|
||||
if (ret < 0) {
|
||||
dev_err(charger->dev,
|
||||
"failed to register notifier for HOST\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq25700_register_pd_nb(struct bq25700_device *charger)
|
||||
{
|
||||
if (charger->cable_edev) {
|
||||
INIT_DELAYED_WORK(&charger->pd_work, bq25700_pd_evt_worker);
|
||||
charger->cable_pd_nb.notifier_call = bq25700_pd_evt_notifier;
|
||||
extcon_register_notifier(charger->cable_edev,
|
||||
EXTCON_CHG_USB_FAST,
|
||||
&charger->cable_pd_nb);
|
||||
}
|
||||
|
||||
if (charger->cable_edev_1) {
|
||||
INIT_DELAYED_WORK(&charger->pd_work1, bq25700_pd_evt_worker1);
|
||||
charger->cable_pd_nb1.notifier_call = bq25700_pd_evt_notifier1;
|
||||
extcon_register_notifier(charger->cable_edev_1,
|
||||
EXTCON_CHG_USB_FAST,
|
||||
&charger->cable_pd_nb1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq25700_register_host_nb(struct bq25700_device *charger)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Register host */
|
||||
if (charger->cable_edev) {
|
||||
INIT_DELAYED_WORK(&charger->host_work, bq25700_host_evt_worker);
|
||||
charger->cable_host_nb.notifier_call =
|
||||
bq25700_host_evt_notifier;
|
||||
ret = extcon_register_notifier(charger->cable_edev,
|
||||
EXTCON_USB_HOST,
|
||||
&charger->cable_host_nb);
|
||||
if (ret < 0) {
|
||||
dev_err(charger->dev,
|
||||
"failed to register notifier for HOST\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (charger->cable_edev_1) {
|
||||
INIT_DELAYED_WORK(&charger->host_work1,
|
||||
bq25700_host_evt_worker1);
|
||||
charger->cable_host_nb1.notifier_call =
|
||||
bq25700_host_evt_notifier1;
|
||||
ret = extcon_register_notifier(charger->cable_edev_1,
|
||||
EXTCON_USB_HOST,
|
||||
&charger->cable_host_nb1);
|
||||
if (ret < 0) {
|
||||
dev_err(charger->dev,
|
||||
"failed to register notifier for HOST\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long bq25700_init_usb(struct bq25700_device *charger)
|
||||
{
|
||||
struct extcon_dev *edev;
|
||||
struct extcon_dev *edev, *edev1;
|
||||
struct device *dev = charger->dev;
|
||||
int ret;
|
||||
|
||||
charger->usb_charger_wq = alloc_ordered_workqueue("%s",
|
||||
WQ_MEM_RECLAIM |
|
||||
WQ_FREEZABLE,
|
||||
"bq25700-usb-wq");
|
||||
|
||||
/* type-C */
|
||||
edev = extcon_get_edev_by_phandle(dev, 0);
|
||||
if (IS_ERR(edev)) {
|
||||
if (PTR_ERR(edev) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Invalid or missing extcon\n");
|
||||
return PTR_ERR(edev);
|
||||
charger->cable_edev = NULL;
|
||||
} else {
|
||||
charger->cable_edev = edev;
|
||||
}
|
||||
|
||||
/* Register chargers */
|
||||
INIT_DELAYED_WORK(&charger->usb_work, bq25700_charger_evt_worker);
|
||||
charger->cable_cg_nb.notifier_call = bq25700_charger_evt_notifier;
|
||||
edev1 = extcon_get_edev_by_phandle(dev, 1);
|
||||
if (IS_ERR(edev1)) {
|
||||
if (PTR_ERR(edev1) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Invalid or missing extcon\n");
|
||||
charger->cable_edev_1 = NULL;
|
||||
} else {
|
||||
charger->cable_edev_1 = edev1;
|
||||
}
|
||||
if (!charger->pd_charge_only)
|
||||
bq25700_register_cg_nb(charger);
|
||||
bq25700_register_host_nb(charger);
|
||||
bq25700_register_discnt_nb(charger);
|
||||
bq25700_register_pd_nb(charger);
|
||||
|
||||
ret = extcon_register_notifier(edev,
|
||||
EXTCON_CHG_USB_SDP,
|
||||
&charger->cable_cg_nb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to register notifier for SDP\n");
|
||||
return ret;
|
||||
if (charger->cable_edev) {
|
||||
schedule_delayed_work(&charger->host_work, 0);
|
||||
schedule_delayed_work(&charger->pd_work, 0);
|
||||
if (!charger->pd_charge_only)
|
||||
schedule_delayed_work(&charger->usb_work, 0);
|
||||
}
|
||||
if (charger->cable_edev_1) {
|
||||
schedule_delayed_work(&charger->host_work1, 0);
|
||||
schedule_delayed_work(&charger->pd_work1, 0);
|
||||
if (!charger->pd_charge_only)
|
||||
schedule_delayed_work(&charger->usb_work1, 0);
|
||||
}
|
||||
|
||||
ret = extcon_register_notifier(edev,
|
||||
EXTCON_CHG_USB_DCP,
|
||||
&charger->cable_cg_nb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to register notifier for DCP\n");
|
||||
extcon_unregister_notifier(edev,
|
||||
EXTCON_CHG_USB_SDP,
|
||||
&charger->cable_cg_nb);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = extcon_register_notifier(edev,
|
||||
EXTCON_CHG_USB_CDP,
|
||||
&charger->cable_cg_nb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to register notifier for CDP\n");
|
||||
extcon_unregister_notifier(edev,
|
||||
EXTCON_CHG_USB_SDP,
|
||||
&charger->cable_cg_nb);
|
||||
extcon_unregister_notifier(edev,
|
||||
EXTCON_CHG_USB_DCP,
|
||||
&charger->cable_cg_nb);
|
||||
return ret;
|
||||
}
|
||||
static int bq25700_parse_dt(struct bq25700_device *charger)
|
||||
{
|
||||
int ret;
|
||||
struct device_node *np = charger->dev->of_node;
|
||||
|
||||
/* Register host */
|
||||
INIT_DELAYED_WORK(&charger->host_work, bq25700_host_evt_worker);
|
||||
charger->cable_host_nb.notifier_call = bq25700_host_evt_notifier;
|
||||
ret = extcon_register_notifier(edev,
|
||||
EXTCON_USB_HOST,
|
||||
&charger->cable_host_nb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to register notifier for HOST\n");
|
||||
extcon_unregister_notifier(edev,
|
||||
EXTCON_CHG_USB_SDP,
|
||||
&charger->cable_cg_nb);
|
||||
extcon_unregister_notifier(edev,
|
||||
EXTCON_CHG_USB_DCP,
|
||||
&charger->cable_cg_nb);
|
||||
extcon_unregister_notifier(edev,
|
||||
EXTCON_CHG_USB_CDP,
|
||||
&charger->cable_cg_nb);
|
||||
charger->typec0_enable_io = devm_gpiod_get_optional(charger->dev,
|
||||
"typec0-enable",
|
||||
GPIOD_IN);
|
||||
if (!IS_ERR_OR_NULL(charger->typec0_enable_io))
|
||||
gpiod_direction_output(charger->typec0_enable_io, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
charger->typec1_enable_io = devm_gpiod_get_optional(charger->dev,
|
||||
"typec1-enable",
|
||||
GPIOD_IN);
|
||||
if (!IS_ERR_OR_NULL(charger->typec1_enable_io))
|
||||
gpiod_direction_output(charger->typec1_enable_io, 0);
|
||||
|
||||
/* Register discnt usb */
|
||||
INIT_DELAYED_WORK(&charger->discnt_work, bq25700_discnt_evt_worker);
|
||||
charger->cable_discnt_nb.notifier_call = bq25700_discnt_evt_notfier;
|
||||
ret = extcon_register_notifier(edev,
|
||||
EXTCON_USB,
|
||||
&charger->cable_discnt_nb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to register notifier for HOST\n");
|
||||
extcon_unregister_notifier(edev,
|
||||
EXTCON_CHG_USB_SDP,
|
||||
&charger->cable_cg_nb);
|
||||
extcon_unregister_notifier(edev,
|
||||
EXTCON_CHG_USB_DCP,
|
||||
&charger->cable_cg_nb);
|
||||
extcon_unregister_notifier(edev,
|
||||
EXTCON_CHG_USB_CDP,
|
||||
&charger->cable_cg_nb);
|
||||
extcon_unregister_notifier(edev,
|
||||
EXTCON_USB_VBUS_EN,
|
||||
&charger->cable_host_nb);
|
||||
charger->typec0_discharge_io =
|
||||
devm_gpiod_get_optional(charger->dev, "typec0-discharge",
|
||||
GPIOD_IN);
|
||||
|
||||
return ret;
|
||||
}
|
||||
charger->typec1_discharge_io =
|
||||
devm_gpiod_get_optional(charger->dev, "typec1-discharge",
|
||||
GPIOD_IN);
|
||||
|
||||
charger->cable_edev = edev;
|
||||
|
||||
schedule_delayed_work(&charger->host_work, 0);
|
||||
schedule_delayed_work(&charger->usb_work, 0);
|
||||
|
||||
INIT_DELAYED_WORK(&charger->pd_work, bq25700_pd_evt_worker);
|
||||
charger->cable_pd_nb.notifier_call = bq25700_pd_evt_notifier;
|
||||
ret = extcon_register_notifier(edev,
|
||||
EXTCON_CHG_USB_FAST,
|
||||
&charger->cable_pd_nb);
|
||||
ret = of_property_read_u32(np, "pd-charge-only",
|
||||
&charger->pd_charge_only);
|
||||
if (ret < 0)
|
||||
dev_err(charger->dev, "pd-charge-only!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1202,7 +1666,10 @@ static int bq25700_probe(struct i2c_client *client,
|
||||
return ret;
|
||||
}
|
||||
|
||||
bq25700_parse_dt(charger);
|
||||
|
||||
bq25700_init_usb(charger);
|
||||
bq25700_init_sysfs(charger);
|
||||
|
||||
if (client->irq < 0) {
|
||||
dev_err(dev, "No irq resource found.\n");
|
||||
|
||||
Reference in New Issue
Block a user