diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 45ff11afd8c4..14fff27eb0e5 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -407,7 +407,7 @@ config BATTERY_TWL4030_MADC config QTI_BATTERY_CHARGER tristate "QTI Glink battery charger" - depends on QTI_PMIC_GLINK + depends on QTI_PMIC_GLINK && EXTCON help Say Y here to enable Qualcomm Technologies, Inc. battery charger driver which interfaces with the charger firmware running on the diff --git a/drivers/power/supply/qti_battery_charger.c b/drivers/power/supply/qti_battery_charger.c index 77c480f3996a..ab0dbd6300be 100644 --- a/drivers/power/supply/qti_battery_charger.c +++ b/drivers/power/supply/qti_battery_charger.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "BATTERY_CHG: %s: " fmt, __func__ @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,11 @@ #define WLS_FW_BUF_SIZE 128 #define DEFAULT_RESTRICT_FCC_UA 1000000 +enum usb_connector_type { + USB_CONNECTOR_TYPE_TYPEC, + USB_CONNECTOR_TYPE_MICRO_USB, +}; + enum psy_type { PSY_TYPE_BATTERY, PSY_TYPE_USB, @@ -106,6 +112,8 @@ enum usb_property_id { USB_TEMP, USB_REAL_TYPE, USB_TYPEC_COMPLIANT, + USB_SCOPE, + USB_CONNECTOR_TYPE, USB_PROP_MAX, }; @@ -221,6 +229,8 @@ struct battery_chg_dev { struct completion fw_update_ack; struct psy_state psy_list[PSY_TYPE_MAX]; struct dentry *debugfs_dir; + /* extcon for VBUS/ID notification for USB for micro USB */ + struct extcon_dev *extcon; u32 *thermal_levels; const char *wls_fw_name; int curr_thermal_level; @@ -240,6 +250,8 @@ struct battery_chg_dev { u32 restrict_fcc_ua; u32 last_fcc_ua; u32 usb_icl_ua; + u32 connector_type; + u32 usb_prev_mode; bool restrict_chg_en; /* To track the driver initialization status */ bool initialized; @@ -279,6 +291,7 @@ static const int usb_prop_map[USB_PROP_MAX] = { [USB_INPUT_CURR_LIMIT] = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, [USB_ADAP_TYPE] = POWER_SUPPLY_PROP_USB_TYPE, [USB_TEMP] = POWER_SUPPLY_PROP_TEMP, + [USB_SCOPE] = POWER_SUPPLY_PROP_SCOPE, }; static const int wls_prop_map[WLS_PROP_MAX] = { @@ -289,6 +302,12 @@ static const int wls_prop_map[WLS_PROP_MAX] = { [WLS_CURR_MAX] = POWER_SUPPLY_PROP_CURRENT_MAX, }; +static const unsigned int bcdev_usb_extcon_cable[] = { + EXTCON_USB, + EXTCON_USB_HOST, + EXTCON_NONE, +}; + /* Standard usb_type definitions similar to power_supply_sysfs.c */ static const char * const power_supply_usb_type_text[] = { "Unknown", "SDP", "DCP", "CDP", "ACA", "C", @@ -624,6 +643,48 @@ static void handle_message(struct battery_chg_dev *bcdev, void *data, complete(&bcdev->ack); } +static void battery_chg_update_uusb_type(struct battery_chg_dev *bcdev, + u32 adap_type) +{ + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_USB]; + int rc; + + /* Handle the extcon notification for uUSB case only */ + if (bcdev->connector_type != USB_CONNECTOR_TYPE_MICRO_USB) + return; + + rc = read_property_id(bcdev, pst, USB_SCOPE); + if (rc < 0) { + pr_err("Failed to read USB_SCOPE rc=%d\n", rc); + return; + } + + switch (pst->prop[USB_SCOPE]) { + case POWER_SUPPLY_SCOPE_DEVICE: + if (adap_type == POWER_SUPPLY_USB_TYPE_SDP || + adap_type == POWER_SUPPLY_USB_TYPE_CDP) { + /* Device mode connect notification */ + extcon_set_state_sync(bcdev->extcon, EXTCON_USB, 1); + bcdev->usb_prev_mode = EXTCON_USB; + } + break; + case POWER_SUPPLY_SCOPE_SYSTEM: + /* Host mode connect notification */ + extcon_set_state_sync(bcdev->extcon, EXTCON_USB_HOST, 1); + bcdev->usb_prev_mode = EXTCON_USB_HOST; + break; + default: + if (bcdev->usb_prev_mode == EXTCON_USB || + bcdev->usb_prev_mode == EXTCON_USB_HOST) { + /* Disconnect notification */ + extcon_set_state_sync(bcdev->extcon, + bcdev->usb_prev_mode, 0); + bcdev->usb_prev_mode = EXTCON_NONE; + } + break; + } +} + static struct power_supply_desc usb_psy_desc; static void battery_chg_update_usb_type_work(struct work_struct *work) @@ -675,6 +736,8 @@ static void battery_chg_update_usb_type_work(struct work_struct *work) usb_psy_desc.type = POWER_SUPPLY_TYPE_USB; break; } + + battery_chg_update_uusb_type(bcdev, pst->prop[USB_ADAP_TYPE]); } static void handle_notification(struct battery_chg_dev *bcdev, void *data, @@ -927,6 +990,7 @@ static enum power_supply_property usb_props[] = { POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, POWER_SUPPLY_PROP_USB_TYPE, POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_SCOPE, }; static enum power_supply_usb_type usb_psy_supported_types[] = { @@ -1860,6 +1924,47 @@ static int battery_chg_ship_mode(struct notifier_block *nb, unsigned long code, return NOTIFY_DONE; } +static int register_extcon_conn_type(struct battery_chg_dev *bcdev) +{ + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_USB]; + int rc; + + rc = read_property_id(bcdev, pst, USB_CONNECTOR_TYPE); + if (rc < 0) { + pr_err("Failed to read prop USB_CONNECTOR_TYPE, rc=%d\n", + rc); + return rc; + } + + if (pst->prop[USB_CONNECTOR_TYPE] != USB_CONNECTOR_TYPE_MICRO_USB) + return 0; + + bcdev->connector_type = USB_CONNECTOR_TYPE_MICRO_USB; + bcdev->usb_prev_mode = EXTCON_NONE; + + bcdev->extcon = devm_extcon_dev_allocate(bcdev->dev, + bcdev_usb_extcon_cable); + if (IS_ERR(bcdev->extcon)) { + rc = PTR_ERR(bcdev->extcon); + pr_err("Failed to allocate extcon device rc=%d\n", rc); + return rc; + } + + rc = devm_extcon_dev_register(bcdev->dev, bcdev->extcon); + if (rc < 0) { + pr_err("Failed to register extcon device rc=%d\n", rc); + return rc; + } + rc = extcon_set_property_capability(bcdev->extcon, EXTCON_USB, + EXTCON_PROP_USB_SS); + rc |= extcon_set_property_capability(bcdev->extcon, + EXTCON_USB_HOST, EXTCON_PROP_USB_SS); + if (rc < 0) + pr_err("failed to configure extcon capabilities rc=%d\n", rc); + + return rc; +} + static int battery_chg_probe(struct platform_device *pdev) { struct battery_chg_dev *bcdev; @@ -1950,6 +2055,10 @@ static int battery_chg_probe(struct platform_device *pdev) battery_chg_add_debugfs(bcdev); battery_chg_notify_enable(bcdev); device_init_wakeup(bcdev->dev, true); + rc = register_extcon_conn_type(bcdev); + if (rc < 0) + dev_warn(dev, "Failed to register extcon rc=%d\n", rc); + schedule_work(&bcdev->usb_type_work); return 0;