From 6dcf0f0f24f757b6bf921c87fad36c6e4d6967cc Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Mon, 6 Apr 2020 19:19:30 -0700 Subject: [PATCH 1/8] power: supply: qti-battery-charger: Fix buffer handling in handle_message() Currently, prop_id (u32) and val (u32) are obtained for all messages in handle_message(). However, the response for notification request doesn't have the same payload but only an u32 return code. Because of this, an unintentional memory read for u32 is being made even though it is not used. Fix this by getting prop_id and val only for response messages that are intended to get battery/USB/wireless power supply properties. CRs-Fixed: 2655408 Change-Id: I4d17d965f911eb6a4357e29117191f78830e9e5c Signed-off-by: Subbaraman Narayanamurthy --- drivers/power/supply/qti_battery_charger.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/power/supply/qti_battery_charger.c b/drivers/power/supply/qti_battery_charger.c index cc5ea9963513..9bb362f73652 100644 --- a/drivers/power/supply/qti_battery_charger.c +++ b/drivers/power/supply/qti_battery_charger.c @@ -364,10 +364,9 @@ int qti_battery_charger_get_prop(const char *name, } EXPORT_SYMBOL(qti_battery_charger_get_prop); -static bool validate_message(void *data, size_t len) +static bool validate_message(struct battery_charger_resp_msg *resp_msg, + size_t len) { - struct battery_charger_resp_msg *resp_msg = data; - if (len != sizeof(*resp_msg)) { pr_err("Incorrect response length %zu for opcode %#x\n", len, resp_msg->hdr.opcode); @@ -389,7 +388,6 @@ static void handle_message(struct battery_chg_dev *bcdev, void *data, { struct battery_charger_resp_msg *resp_msg = data; struct battery_model_resp_msg *model_resp_msg = data; - u32 prop_id = resp_msg->property_id, val = resp_msg->value; struct psy_state *pst; bool ack_set = false; @@ -405,24 +403,27 @@ static void handle_message(struct battery_chg_dev *bcdev, void *data, } /* Other response should be of same type as they've u32 value */ - if (validate_message(data, len) && prop_id < pst->prop_count) { - pst->prop[prop_id] = val; + if (validate_message(resp_msg, len) && + resp_msg->property_id < pst->prop_count) { + pst->prop[resp_msg->property_id] = resp_msg->value; ack_set = true; } break; case BC_USB_STATUS_GET: pst = &bcdev->psy_list[PSY_TYPE_USB]; - if (validate_message(data, len) && prop_id < pst->prop_count) { - pst->prop[prop_id] = val; + if (validate_message(resp_msg, len) && + resp_msg->property_id < pst->prop_count) { + pst->prop[resp_msg->property_id] = resp_msg->value; ack_set = true; } break; case BC_WLS_STATUS_GET: pst = &bcdev->psy_list[PSY_TYPE_WLS]; - if (validate_message(data, len) && prop_id < pst->prop_count) { - pst->prop[prop_id] = val; + if (validate_message(resp_msg, len) && + resp_msg->property_id < pst->prop_count) { + pst->prop[resp_msg->property_id] = resp_msg->value; ack_set = true; } From 70b2ee4bf0eb5035f1d1dadb89c92529674d8c8d Mon Sep 17 00:00:00 2001 From: Guru Das Srinagesh Date: Fri, 3 Apr 2020 18:50:55 -0700 Subject: [PATCH 2/8] power: qti_battery_charger: Block PMIC GLINK Tx for debug battery The remote subsystem does not transmit PMIC GLINK notifications triggered by charging parameter changes when a debug battery (one whose battery ID is set to 7.5 Kohms) is detected. This allows it to enter low power mode provided no queries for individual power supply (PSY) properties are made of it. Power consumption monitoring tests require it to enter low power mode and stay there, which can happen only if it is not disturbed by having to respond to queries made by HLOS for PSY properties. This change enables the user to block all PMIC GLINK Tx messages from this driver to remote subsystem by setting the boolean debugfs property "block_tx". Once this is done, all subsequent reads of PSY properties will return not the instantaneous values of those quantities, but the last (stale) values obtained during the last read that was made prior to Tx being blocked. To return to normal behaviour and obtain instantaneous values from the remote subsystem, the debugfs property must be cleared. Change-Id: Ie9b6ec78dcb8ec00102e9d7d23e238445045206f Signed-off-by: Guru Das Srinagesh --- drivers/power/supply/qti_battery_charger.c | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/drivers/power/supply/qti_battery_charger.c b/drivers/power/supply/qti_battery_charger.c index 9bb362f73652..fa99698080b7 100644 --- a/drivers/power/supply/qti_battery_charger.c +++ b/drivers/power/supply/qti_battery_charger.c @@ -5,6 +5,7 @@ #define pr_fmt(fmt) "BATTERY_CHG: %s: " fmt, __func__ +#include #include #include #include @@ -145,12 +146,15 @@ struct battery_chg_dev { struct mutex rw_lock; struct completion ack; struct psy_state psy_list[PSY_TYPE_MAX]; + struct dentry *debugfs_dir; u32 *thermal_levels; int curr_thermal_level; int num_thermal_levels; atomic_t state; struct work_struct subsys_up_work; int fake_soc; + bool block_tx; + bool debug_battery_detected; }; static const int battery_prop_map[BATT_PROP_MAX] = { @@ -211,6 +215,9 @@ static int battery_chg_write(struct battery_chg_dev *bcdev, void *data, return 0; } + if (bcdev->debug_battery_detected && bcdev->block_tx) + return 0; + mutex_lock(&bcdev->rw_lock); reinit_completion(&bcdev->ack); rc = pmic_glink_write(bcdev->client, data, len); @@ -383,6 +390,7 @@ static bool validate_message(struct battery_charger_resp_msg *resp_msg, return true; } +#define MODEL_DEBUG_BOARD "Debug_Board" static void handle_message(struct battery_chg_dev *bcdev, void *data, size_t len) { @@ -399,6 +407,8 @@ static void handle_message(struct battery_chg_dev *bcdev, void *data, if (pst->model && len == sizeof(*model_resp_msg)) { memcpy(pst->model, model_resp_msg->model, MAX_STR_LEN); ack_set = true; + bcdev->debug_battery_detected = !strcmp(pst->model, + MODEL_DEBUG_BOARD); break; } @@ -1008,6 +1018,38 @@ static struct attribute *battery_class_attrs[] = { }; ATTRIBUTE_GROUPS(battery_class); +#ifdef CONFIG_DEBUG_FS +static void battery_chg_add_debugfs(struct battery_chg_dev *bcdev) +{ + int rc; + struct dentry *dir, *file; + + dir = debugfs_create_dir("battery_charger", NULL); + if (IS_ERR(dir)) { + rc = PTR_ERR(dir); + pr_err("Failed to create charger debugfs directory, rc=%d\n", + rc); + return; + } + + file = debugfs_create_bool("block_tx", 0600, dir, &bcdev->block_tx); + if (IS_ERR(file)) { + rc = PTR_ERR(file); + pr_err("Failed to create block_tx debugfs file, rc=%d\n", + rc); + goto error; + } + + bcdev->debugfs_dir = dir; + + return; +error: + debugfs_remove_recursive(dir); +} +#else +static void battery_chg_add_debugfs(struct battery_chg_dev *bcdev) { } +#endif + static int battery_chg_parse_dt(struct battery_chg_dev *bcdev) { struct device_node *node = bcdev->dev->of_node; @@ -1144,6 +1186,7 @@ static int battery_chg_probe(struct platform_device *pdev) goto error; } + battery_chg_add_debugfs(bcdev); battery_chg_notify_enable(bcdev); return 0; @@ -1157,6 +1200,7 @@ static int battery_chg_remove(struct platform_device *pdev) struct battery_chg_dev *bcdev = platform_get_drvdata(pdev); int rc; + debugfs_remove_recursive(bcdev->debugfs_dir); class_unregister(&bcdev->battery_class); rc = pmic_glink_unregister_client(bcdev->client); if (rc < 0) { From de1b3815186b73710bfad77fb792f9971722ca91 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Wed, 10 Jun 2020 15:51:04 -0700 Subject: [PATCH 3/8] power: qti_battery_charger: call power_supply_changed() if fake_soc is set There are clients who are waiting for power supply change events when fake soc is set during testing. Call power_supply_changed() to support them. Change-Id: I6a1f4e7daa136b3783f5ed35f37abafad7447378 Signed-off-by: Subbaraman Narayanamurthy --- drivers/power/supply/qti_battery_charger.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/power/supply/qti_battery_charger.c b/drivers/power/supply/qti_battery_charger.c index fa99698080b7..4ab2b430c165 100644 --- a/drivers/power/supply/qti_battery_charger.c +++ b/drivers/power/supply/qti_battery_charger.c @@ -864,6 +864,7 @@ static ssize_t fake_soc_store(struct class *c, struct class_attribute *attr, { struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, battery_class); + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_BATTERY]; int val; if (kstrtoint(buf, 0, &val)) @@ -872,6 +873,9 @@ static ssize_t fake_soc_store(struct class *c, struct class_attribute *attr, bcdev->fake_soc = val; pr_debug("Set fake soc to %d\n", val); + if (IS_ENABLED(CONFIG_QTI_PMIC_GLINK_CLIENT_DEBUG) && pst->psy) + power_supply_changed(pst->psy); + return count; } From 53f3a0ff88181f528414f202b7e28a257d3abe88 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Thu, 4 Jun 2020 18:09:38 -0700 Subject: [PATCH 4/8] power: supply: qti_battery_charger: support wireless firmware update Add support for wireless charger firmware update in battery charger driver through "wireless_fw_update" and "wireless_fw_force_update" properties under "/sys/class/qcom-battery". Support firmware update for wireless charger (IDT9412) for which firmware is split into multiple chunks (128 bytes each) and sent over PMIC Glink. Charger firmware running on the remote subsystem does the FW update by communicating with IDT9412. Wireless charger firmware name should be specified via a device tree property "qcom,wireless-fw-name". To update wireless firmware, user has to push the FW binary to a firmware partition (e.g. /vendor/firmware) and then do one of the following. Normal update for which FW version check is made: # echo 1 > /sys/class/qcom-battery/wireless_fw_update Forced update for which FW version check is skipped: # echo 1 > /sys/class/qcom-battery/wireless_fw_force_update To read back FW version: # cat /sys/class/qcom-battery/wireless_fw_version Change-Id: Id565e7d5a73041d6e0340edc66eb8b1615396435 Signed-off-by: Subbaraman Narayanamurthy --- drivers/power/supply/qti_battery_charger.c | 358 +++++++++++++++++++++ 1 file changed, 358 insertions(+) diff --git a/drivers/power/supply/qti_battery_charger.c b/drivers/power/supply/qti_battery_charger.c index 4ab2b430c165..7ec5c7bca3c1 100644 --- a/drivers/power/supply/qti_battery_charger.c +++ b/drivers/power/supply/qti_battery_charger.c @@ -6,7 +6,9 @@ #define pr_fmt(fmt) "BATTERY_CHG: %s: " fmt, __func__ #include +#include #include +#include #include #include #include @@ -29,11 +31,19 @@ #define BC_USB_STATUS_SET 0x33 #define BC_WLS_STATUS_GET 0x34 #define BC_WLS_STATUS_SET 0x35 +#define BC_WLS_FW_CHECK_UPDATE 0x40 +#define BC_WLS_FW_PUSH_BUF_REQ 0x41 +#define BC_WLS_FW_UPDATE_STATUS_RESP 0x42 +#define BC_WLS_FW_PUSH_BUF_RESP 0x43 +#define BC_WLS_FW_GET_VERSION 0x44 #define BC_GENERIC_NOTIFY 0x80 /* Generic definitions */ #define MAX_STR_LEN 128 #define BC_WAIT_TIME_MS 1000 +#define WLS_FW_PREPARE_TIME_MS 300 +#define WLS_FW_WAIT_TIME_MS 500 +#define WLS_FW_BUF_SIZE 128 enum psy_type { PSY_TYPE_BATTERY, @@ -129,6 +139,42 @@ struct battery_model_resp_msg { char model[MAX_STR_LEN]; }; +struct wireless_fw_check_req { + struct pmic_glink_hdr hdr; + u32 fw_version; + u32 fw_size; +}; + +struct wireless_fw_check_resp { + struct pmic_glink_hdr hdr; + u32 ret_code; +}; + +struct wireless_fw_push_buf_req { + struct pmic_glink_hdr hdr; + u8 buf[WLS_FW_BUF_SIZE]; + u32 fw_chunk_id; +}; + +struct wireless_fw_push_buf_resp { + struct pmic_glink_hdr hdr; + u32 fw_update_status; +}; + +struct wireless_fw_update_status { + struct pmic_glink_hdr hdr; + u32 fw_update_done; +}; + +struct wireless_fw_get_version_req { + struct pmic_glink_hdr hdr; +}; + +struct wireless_fw_get_version_resp { + struct pmic_glink_hdr hdr; + u32 fw_version; +}; + struct psy_state { struct power_supply *psy; char *model; @@ -145,9 +191,12 @@ struct battery_chg_dev { struct pmic_glink_client *client; struct mutex rw_lock; struct completion ack; + struct completion fw_buf_ack; + struct completion fw_update_ack; struct psy_state psy_list[PSY_TYPE_MAX]; struct dentry *debugfs_dir; u32 *thermal_levels; + const char *wls_fw_name; int curr_thermal_level; int num_thermal_levels; atomic_t state; @@ -155,6 +204,8 @@ struct battery_chg_dev { int fake_soc; bool block_tx; bool debug_battery_detected; + bool wls_fw_update_reqd; + u32 wls_fw_version; }; static const int battery_prop_map[BATT_PROP_MAX] = { @@ -200,6 +251,32 @@ static const int wls_prop_map[WLS_PROP_MAX] = { [WLS_CURR_MAX] = POWER_SUPPLY_PROP_CURRENT_MAX, }; +static int battery_chg_fw_write(struct battery_chg_dev *bcdev, void *data, + int len) +{ + int rc; + + if (atomic_read(&bcdev->state) == PMIC_GLINK_STATE_DOWN) { + pr_debug("glink state is down\n"); + return -ENOTCONN; + } + + reinit_completion(&bcdev->fw_buf_ack); + rc = pmic_glink_write(bcdev->client, data, len); + if (!rc) { + rc = wait_for_completion_timeout(&bcdev->fw_buf_ack, + msecs_to_jiffies(WLS_FW_WAIT_TIME_MS)); + if (!rc) { + pr_err("Error, timed out sending message\n"); + return -ETIMEDOUT; + } + + rc = 0; + } + + return rc; +} + static int battery_chg_write(struct battery_chg_dev *bcdev, void *data, int len) { @@ -396,6 +473,10 @@ static void handle_message(struct battery_chg_dev *bcdev, void *data, { struct battery_charger_resp_msg *resp_msg = data; struct battery_model_resp_msg *model_resp_msg = data; + struct wireless_fw_check_resp *fw_check_msg; + struct wireless_fw_push_buf_resp *fw_resp_msg; + struct wireless_fw_update_status *fw_update_msg; + struct wireless_fw_get_version_resp *fw_ver_msg; struct psy_state *pst; bool ack_set = false; @@ -449,6 +530,47 @@ static void handle_message(struct battery_chg_dev *bcdev, void *data, /* Always ACK response for notify request */ ack_set = true; break; + case BC_WLS_FW_CHECK_UPDATE: + if (len == sizeof(*fw_check_msg)) { + fw_check_msg = data; + if (fw_check_msg->ret_code == 1) + bcdev->wls_fw_update_reqd = true; + ack_set = true; + } else { + pr_err("Incorrect response length %zu for wls_fw_check_update\n", + len); + } + break; + case BC_WLS_FW_PUSH_BUF_RESP: + if (len == sizeof(*fw_resp_msg)) { + fw_resp_msg = data; + if (fw_resp_msg->fw_update_status == 1) + complete(&bcdev->fw_buf_ack); + } else { + pr_err("Incorrect response length %zu for wls_fw_push_buf_resp\n", + len); + } + break; + case BC_WLS_FW_UPDATE_STATUS_RESP: + if (len == sizeof(*fw_update_msg)) { + fw_update_msg = data; + if (fw_update_msg->fw_update_done == 1) + complete(&bcdev->fw_update_ack); + } else { + pr_err("Incorrect response length %zu for wls_fw_update_status_resp\n", + len); + } + break; + case BC_WLS_FW_GET_VERSION: + if (len == sizeof(*fw_ver_msg)) { + fw_ver_msg = data; + bcdev->wls_fw_version = fw_ver_msg->fw_version; + ack_set = true; + } else { + pr_err("Incorrect response length %zu for wls_fw_get_version\n", + len); + } + break; default: pr_err("Unknown opcode: %u\n", resp_msg->hdr.opcode); break; @@ -859,6 +981,232 @@ static int battery_chg_init_psy(struct battery_chg_dev *bcdev) return 0; } +static int wireless_fw_send_firmware(struct battery_chg_dev *bcdev, + const struct firmware *fw) +{ + struct wireless_fw_push_buf_req msg = {}; + const u8 *ptr; + u32 i, num_chunks, partial_chunk_size; + int rc; + + num_chunks = fw->size / WLS_FW_BUF_SIZE; + partial_chunk_size = fw->size % WLS_FW_BUF_SIZE; + + if (!num_chunks) + return -EINVAL; + + pr_debug("Updating FW...\n"); + + ptr = fw->data; + msg.hdr.owner = MSG_OWNER_BC; + msg.hdr.type = MSG_TYPE_REQ_RESP; + msg.hdr.opcode = BC_WLS_FW_PUSH_BUF_REQ; + + for (i = 0; i < num_chunks; i++, ptr += WLS_FW_BUF_SIZE) { + msg.fw_chunk_id = i + 1; + memcpy(msg.buf, ptr, WLS_FW_BUF_SIZE); + + pr_debug("sending FW chunk %u\n", i + 1); + rc = battery_chg_fw_write(bcdev, &msg, sizeof(msg)); + if (rc < 0) + return rc; + } + + if (partial_chunk_size) { + msg.fw_chunk_id = i + 1; + memset(msg.buf, 0, WLS_FW_BUF_SIZE); + memcpy(msg.buf, ptr, partial_chunk_size); + + pr_debug("sending partial FW chunk %u\n", i + 1); + rc = battery_chg_fw_write(bcdev, &msg, sizeof(msg)); + if (rc < 0) + return rc; + } + + return 0; +} + +static int wireless_fw_check_for_update(struct battery_chg_dev *bcdev, + u32 version, size_t size) +{ + struct wireless_fw_check_req req_msg = {}; + + bcdev->wls_fw_update_reqd = false; + + req_msg.hdr.owner = MSG_OWNER_BC; + req_msg.hdr.type = MSG_TYPE_REQ_RESP; + req_msg.hdr.opcode = BC_WLS_FW_CHECK_UPDATE; + req_msg.fw_version = version; + req_msg.fw_size = size; + + return battery_chg_write(bcdev, &req_msg, sizeof(req_msg)); +} + +#define IDT_FW_MAJOR_VER_OFFSET 0x94 +#define IDT_FW_MINOR_VER_OFFSET 0x96 +static int wireless_fw_update(struct battery_chg_dev *bcdev, bool force) +{ + const struct firmware *fw; + struct psy_state *pst; + u32 version; + u16 maj_ver, min_ver; + int rc; + + pm_stay_awake(bcdev->dev); + + /* + * Check for USB presence. If nothing is connected, check whether + * battery SOC is at least 50% before allowing FW update. + */ + pst = &bcdev->psy_list[PSY_TYPE_USB]; + rc = read_property_id(bcdev, pst, USB_ONLINE); + if (rc < 0) + goto out; + + if (!pst->prop[USB_ONLINE]) { + pst = &bcdev->psy_list[PSY_TYPE_BATTERY]; + rc = read_property_id(bcdev, pst, BATT_CAPACITY); + if (rc < 0) + goto out; + + if ((pst->prop[BATT_CAPACITY] / 100) < 50) { + pr_err("Battery SOC should be at least 50%% or connect charger\n"); + rc = -EINVAL; + goto out; + } + } + + rc = firmware_request_nowarn(&fw, bcdev->wls_fw_name, bcdev->dev); + if (rc) { + pr_err("Couldn't get firmware rc=%d\n", rc); + goto out; + } + + if (!fw || !fw->data || !fw->size) { + pr_err("Invalid firmware\n"); + rc = -EINVAL; + goto release_fw; + } + + if (fw->size < SZ_16K) { + pr_err("Invalid firmware size %zu\n", fw->size); + rc = -EINVAL; + goto release_fw; + } + + maj_ver = le16_to_cpu(*(__le16 *)(fw->data + IDT_FW_MAJOR_VER_OFFSET)); + min_ver = le16_to_cpu(*(__le16 *)(fw->data + IDT_FW_MINOR_VER_OFFSET)); + version = maj_ver << 16 | min_ver; + + if (force) + version = UINT_MAX; + + pr_debug("FW size: %zu version: %#x\n", fw->size, version); + + rc = wireless_fw_check_for_update(bcdev, version, fw->size); + if (rc < 0) { + pr_err("Wireless FW update not needed, rc=%d\n", rc); + goto release_fw; + } + + if (!bcdev->wls_fw_update_reqd) { + pr_warn("Wireless FW update not required\n"); + goto release_fw; + } + + /* Wait for IDT to be setup by charger firmware */ + msleep(WLS_FW_PREPARE_TIME_MS); + + reinit_completion(&bcdev->fw_update_ack); + rc = wireless_fw_send_firmware(bcdev, fw); + if (rc < 0) { + pr_err("Failed to send FW chunk, rc=%d\n", rc); + goto release_fw; + } + + rc = wait_for_completion_timeout(&bcdev->fw_update_ack, + msecs_to_jiffies(WLS_FW_WAIT_TIME_MS)); + if (!rc) { + pr_err("Error, timed out updating firmware\n"); + rc = -ETIMEDOUT; + goto release_fw; + } else { + rc = 0; + } + + pr_info("Wireless FW update done\n"); + +release_fw: + release_firmware(fw); +out: + pm_relax(bcdev->dev); + + return rc; +} + +static ssize_t wireless_fw_version_show(struct class *c, + struct class_attribute *attr, + char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + struct wireless_fw_get_version_req req_msg = {}; + int rc; + + req_msg.hdr.owner = MSG_OWNER_BC; + req_msg.hdr.type = MSG_TYPE_REQ_RESP; + req_msg.hdr.opcode = BC_WLS_FW_GET_VERSION; + + rc = battery_chg_write(bcdev, &req_msg, sizeof(req_msg)); + if (rc < 0) { + pr_err("Failed to get FW version rc=%d\n", rc); + return rc; + } + + return scnprintf(buf, PAGE_SIZE, "%#x\n", bcdev->wls_fw_version); +} +static CLASS_ATTR_RO(wireless_fw_version); + +static ssize_t wireless_fw_force_update_store(struct class *c, + struct class_attribute *attr, + const char *buf, size_t count) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + bool val; + int rc; + + if (kstrtobool(buf, &val) || !val) + return -EINVAL; + + rc = wireless_fw_update(bcdev, true); + if (rc < 0) + return rc; + + return count; +} +static CLASS_ATTR_WO(wireless_fw_force_update); + +static ssize_t wireless_fw_update_store(struct class *c, + struct class_attribute *attr, + const char *buf, size_t count) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + bool val; + int rc; + + if (kstrtobool(buf, &val) || !val) + return -EINVAL; + + rc = wireless_fw_update(bcdev, false); + if (rc < 0) + return rc; + + return count; +} +static CLASS_ATTR_WO(wireless_fw_update); + static ssize_t fake_soc_store(struct class *c, struct class_attribute *attr, const char *buf, size_t count) { @@ -1018,6 +1366,9 @@ static struct attribute *battery_class_attrs[] = { &class_attr_moisture_detection_en.attr, &class_attr_wireless_boost_en.attr, &class_attr_fake_soc.attr, + &class_attr_wireless_fw_update.attr, + &class_attr_wireless_fw_force_update.attr, + &class_attr_wireless_fw_version.attr, NULL, }; ATTRIBUTE_GROUPS(battery_class); @@ -1061,6 +1412,9 @@ static int battery_chg_parse_dt(struct battery_chg_dev *bcdev) int i, rc, len; u32 prev, val; + of_property_read_string(node, "qcom,wireless-fw-name", + &bcdev->wls_fw_name); + rc = of_property_count_elems_of_size(node, "qcom,thermal-mitigation", sizeof(u32)); if (rc <= 0) @@ -1153,6 +1507,8 @@ static int battery_chg_probe(struct platform_device *pdev) mutex_init(&bcdev->rw_lock); init_completion(&bcdev->ack); + init_completion(&bcdev->fw_buf_ack); + init_completion(&bcdev->fw_update_ack); INIT_WORK(&bcdev->subsys_up_work, battery_chg_subsys_up_work); atomic_set(&bcdev->state, PMIC_GLINK_STATE_UP); bcdev->dev = dev; @@ -1192,6 +1548,7 @@ 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); return 0; error: @@ -1204,6 +1561,7 @@ static int battery_chg_remove(struct platform_device *pdev) struct battery_chg_dev *bcdev = platform_get_drvdata(pdev); int rc; + device_init_wakeup(bcdev->dev, false); debugfs_remove_recursive(bcdev->debugfs_dir); class_unregister(&bcdev->battery_class); rc = pmic_glink_unregister_client(bcdev->client); From 640c25fb237c807af8a52f8140dc8eeeda6578d1 Mon Sep 17 00:00:00 2001 From: Guru Das Srinagesh Date: Fri, 12 Jun 2020 19:53:00 -0700 Subject: [PATCH 5/8] power: supply: qti_battery_charger: Enable Ship mode shutdown This feature is to enable the disconnection of the battery from the device as the final step in the assembly of a phone on the factory floor to prevent battery rundown while in transit to the hands of the final consumer. Usage: # echo 1 > /sys/class/qcom-battery/ship_mode_en Following this, either manually shut down using power key, or: # reboot -p Change-Id: Ifef053c6ebb3b86c10f9dbe3c9c47d7c3548d95b Signed-off-by: Guru Das Srinagesh --- drivers/power/supply/qti_battery_charger.c | 68 ++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/drivers/power/supply/qti_battery_charger.c b/drivers/power/supply/qti_battery_charger.c index 7ec5c7bca3c1..61ffc2282a0f 100644 --- a/drivers/power/supply/qti_battery_charger.c +++ b/drivers/power/supply/qti_battery_charger.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #define BC_USB_STATUS_SET 0x33 #define BC_WLS_STATUS_GET 0x34 #define BC_WLS_STATUS_SET 0x35 +#define BC_SHIP_MODE_REQ_SET 0x36 #define BC_WLS_FW_CHECK_UPDATE 0x40 #define BC_WLS_FW_PUSH_BUF_REQ 0x41 #define BC_WLS_FW_UPDATE_STATUS_RESP 0x42 @@ -52,6 +54,11 @@ enum psy_type { PSY_TYPE_MAX, }; +enum ship_mode_type { + SHIP_MODE_PMIC, + SHIP_MODE_PACK_SIDE, +}; + /* property ids */ enum battery_property_id { BATT_STATUS, @@ -175,6 +182,11 @@ struct wireless_fw_get_version_resp { u32 fw_version; }; +struct battery_charger_ship_mode_req_msg { + struct pmic_glink_hdr hdr; + u32 ship_mode_type; +}; + struct psy_state { struct power_supply *psy; char *model; @@ -203,9 +215,11 @@ struct battery_chg_dev { struct work_struct subsys_up_work; int fake_soc; bool block_tx; + bool ship_mode_en; bool debug_battery_detected; bool wls_fw_update_reqd; u32 wls_fw_version; + struct notifier_block reboot_notifier; }; static const int battery_prop_map[BATT_PROP_MAX] = { @@ -1359,6 +1373,28 @@ static ssize_t soh_show(struct class *c, struct class_attribute *attr, } static CLASS_ATTR_RO(soh); +static ssize_t ship_mode_en_store(struct class *c, struct class_attribute *attr, + const char *buf, size_t count) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + + if (kstrtobool(buf, &bcdev->ship_mode_en)) + return -EINVAL; + + return count; +} + +static ssize_t ship_mode_en_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + + return scnprintf(buf, PAGE_SIZE, "%d\n", bcdev->ship_mode_en); +} +static CLASS_ATTR_RW(ship_mode_en); + static struct attribute *battery_class_attrs[] = { &class_attr_soh.attr, &class_attr_resistance.attr, @@ -1369,6 +1405,7 @@ static struct attribute *battery_class_attrs[] = { &class_attr_wireless_fw_update.attr, &class_attr_wireless_fw_force_update.attr, &class_attr_wireless_fw_version.attr, + &class_attr_ship_mode_en.attr, NULL, }; ATTRIBUTE_GROUPS(battery_class); @@ -1468,6 +1505,31 @@ static int battery_chg_parse_dt(struct battery_chg_dev *bcdev) return 0; } +static int battery_chg_ship_mode(struct notifier_block *nb, unsigned long code, + void *unused) +{ + struct battery_charger_ship_mode_req_msg msg = { { 0 } }; + struct battery_chg_dev *bcdev = container_of(nb, struct battery_chg_dev, + reboot_notifier); + int rc; + + if (!bcdev->ship_mode_en) + return NOTIFY_DONE; + + msg.hdr.owner = MSG_OWNER_BC; + msg.hdr.type = MSG_TYPE_REQ_RESP; + msg.hdr.opcode = BC_SHIP_MODE_REQ_SET; + msg.ship_mode_type = SHIP_MODE_PMIC; + + if (code == SYS_POWER_OFF) { + rc = battery_chg_write(bcdev, &msg, sizeof(msg)); + if (rc < 0) + pr_emerg("Failed to write ship mode: %d\n", rc); + } + + return NOTIFY_DONE; +} + static int battery_chg_probe(struct platform_device *pdev) { struct battery_chg_dev *bcdev; @@ -1528,6 +1590,10 @@ static int battery_chg_probe(struct platform_device *pdev) return rc; } + bcdev->reboot_notifier.notifier_call = battery_chg_ship_mode; + bcdev->reboot_notifier.priority = 255; + register_reboot_notifier(&bcdev->reboot_notifier); + rc = battery_chg_parse_dt(bcdev); if (rc < 0) goto error; @@ -1553,6 +1619,7 @@ static int battery_chg_probe(struct platform_device *pdev) return 0; error: pmic_glink_unregister_client(bcdev->client); + unregister_reboot_notifier(&bcdev->reboot_notifier); return rc; } @@ -1564,6 +1631,7 @@ static int battery_chg_remove(struct platform_device *pdev) device_init_wakeup(bcdev->dev, false); debugfs_remove_recursive(bcdev->debugfs_dir); class_unregister(&bcdev->battery_class); + unregister_reboot_notifier(&bcdev->reboot_notifier); rc = pmic_glink_unregister_client(bcdev->client); if (rc < 0) { pr_err("Error unregistering from pmic_glink, rc=%d\n", rc); From 4540348af3336e5b4fd4c8adacc54d0ca6cdf0ea Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Thu, 16 Jul 2020 16:11:14 -0700 Subject: [PATCH 6/8] power: supply: qti_battery_charger: Keep device awake during notification With charger mode enabled in android, device boots into charger mode when it is powered on with USB insertion. In this mode, health HAL and charger service handles the power supply uevents sent from power supply framework to take certain actions. For example, when the USB or travel adapter (TA) is removed, device would shut down in 10 seconds. With a TA connected, device can enter suspend whenever possible during charging. When the TA is removed after the device has entered suspend, device comes out of suspend upon the notification received from charger firmware (CHGFW) over PMIC Glink. Battery charger driver handles this and calls power_supply_changed on one of the power supply devices. However, before this uevent is received and handled by charger service in the userspace, device can enter suspend immediately. In a low power discharging state, device stays in this state for minutes until it gets another notification from CHGFW for battery power supply, a periodic event which makes charger service to initiate a shutdown. This conflicts with the user expectation where the device has to shutdown in 10 seconds upon TA removal. Workaround this problem by keeping the device awake for at least 50 ms when a notification is received from CHGFW. This way, userspace process like charger could get a chance to receive and handle the power supply uevents. Change-Id: I9ea78eb6565988ffac0e6c5eee1ccae8dc7e3e3a Signed-off-by: Subbaraman Narayanamurthy --- drivers/power/supply/qti_battery_charger.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/qti_battery_charger.c b/drivers/power/supply/qti_battery_charger.c index 61ffc2282a0f..08a7c5c27532 100644 --- a/drivers/power/supply/qti_battery_charger.c +++ b/drivers/power/supply/qti_battery_charger.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -622,8 +623,18 @@ static void handle_notification(struct battery_chg_dev *bcdev, void *data, break; } - if (pst && pst->psy) + if (pst && pst->psy) { + /* + * For charger mode, keep the device awake at least for 50 ms + * so that device won't enter suspend when a non-SDP charger + * is removed. This would allow the userspace process like + * "charger" to be able to read power supply uevents to take + * appropriate actions (e.g. shutting down when the charger is + * unplugged). + */ power_supply_changed(pst->psy); + pm_wakeup_dev_event(bcdev->dev, 50, true); + } } static int battery_chg_callback(void *priv, void *data, size_t len) From 70dba80995414f41c6e789b05a59ab946319c655 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Mon, 13 Jul 2020 12:43:13 -0700 Subject: [PATCH 7/8] power: supply: qti_battery_charger: add some more properties To support userspace application and testing, add some more properties under USB power supply and qcom-battery class. To show USB connector temperature, following property is added. - /sys/class/power_supply/usb/temp To support restricted charging similar to legacy targets, following parameters are added. - /sys/class/qcom-battery/restrict_cur - /sys/class/qcom-battery/restrict_chg To show whether the Type-C sink/source connected is compliant, following parameter is added. - /sys/class/qcom-battery/usb_typec_compliant To show the custom USB_TYPE for Quick charge adapters like HVDCP, HVDCP3 etc., following parameter is added. - /sys/class/qcom-battery/usb_real_type. Change-Id: Ia8dfba41c6ed3056dda588a1955a60ef19568112 Signed-off-by: Subbaraman Narayanamurthy --- drivers/power/supply/qti_battery_charger.c | 177 ++++++++++++++++++++- 1 file changed, 174 insertions(+), 3 deletions(-) diff --git a/drivers/power/supply/qti_battery_charger.c b/drivers/power/supply/qti_battery_charger.c index 08a7c5c27532..6917808719ca 100644 --- a/drivers/power/supply/qti_battery_charger.c +++ b/drivers/power/supply/qti_battery_charger.c @@ -47,6 +47,7 @@ #define WLS_FW_PREPARE_TIME_MS 300 #define WLS_FW_WAIT_TIME_MS 500 #define WLS_FW_BUF_SIZE 128 +#define DEFAULT_RESTRICT_FCC_UA 1000000 enum psy_type { PSY_TYPE_BATTERY, @@ -100,6 +101,9 @@ enum usb_property_id { USB_ADAP_TYPE, USB_MOISTURE_DET_EN, USB_MOISTURE_DET_STS, + USB_TEMP, + USB_REAL_TYPE, + USB_TYPEC_COMPLIANT, USB_PROP_MAX, }; @@ -114,6 +118,12 @@ enum wireless_property_id { WLS_PROP_MAX, }; +enum { + QTI_POWER_SUPPLY_USB_TYPE_HVDCP = 0x80, + QTI_POWER_SUPPLY_USB_TYPE_HVDCP_3, + QTI_POWER_SUPPLY_USB_TYPE_HVDCP_3P5, +}; + struct battery_charger_set_notify_msg { struct pmic_glink_hdr hdr; u32 battery_id; @@ -221,6 +231,9 @@ struct battery_chg_dev { bool wls_fw_update_reqd; u32 wls_fw_version; struct notifier_block reboot_notifier; + u32 thermal_fcc_ua; + u32 restrict_fcc_ua; + bool restrict_chg_en; }; static const int battery_prop_map[BATT_PROP_MAX] = { @@ -256,6 +269,7 @@ static const int usb_prop_map[USB_PROP_MAX] = { [USB_CURR_MAX] = POWER_SUPPLY_PROP_CURRENT_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, }; static const int wls_prop_map[WLS_PROP_MAX] = { @@ -266,6 +280,17 @@ static const int wls_prop_map[WLS_PROP_MAX] = { [WLS_CURR_MAX] = POWER_SUPPLY_PROP_CURRENT_MAX, }; +/* 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", + "PD", "PD_DRP", "PD_PPS", "BrickID" +}; + +/* Custom usb_type definitions */ +static const char * const qc_power_supply_usb_type_text[] = { + "HVDCP", "HVDCP_3", "HVDCP_3P5" +}; + static int battery_chg_fw_write(struct battery_chg_dev *bcdev, void *data, int len) { @@ -707,6 +732,28 @@ static const struct power_supply_desc wls_psy_desc = { .property_is_writeable = wls_psy_prop_is_writeable, }; +static const char *get_usb_type_name(u32 usb_type) +{ + u32 i; + + if (usb_type >= QTI_POWER_SUPPLY_USB_TYPE_HVDCP && + usb_type <= QTI_POWER_SUPPLY_USB_TYPE_HVDCP_3P5) { + for (i = 0; i < ARRAY_SIZE(qc_power_supply_usb_type_text); + i++) { + if (i == (usb_type - QTI_POWER_SUPPLY_USB_TYPE_HVDCP)) + return qc_power_supply_usb_type_text[i]; + } + return "Unknown"; + } + + for (i = 0; i < ARRAY_SIZE(power_supply_usb_type_text); i++) { + if (i == usb_type) + return power_supply_usb_type_text[i]; + } + + return "Unknown"; +} + static int usb_psy_set_icl(struct battery_chg_dev *bcdev, u32 prop_id, int val) { struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_USB]; @@ -758,6 +805,8 @@ static int usb_psy_get_prop(struct power_supply *psy, return rc; pval->intval = pst->prop[prop_id]; + if (prop == POWER_SUPPLY_PROP_TEMP) + pval->intval = DIV_ROUND_CLOSEST((int)pval->intval, 10); return 0; } @@ -806,6 +855,7 @@ static enum power_supply_property usb_props[] = { POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_TEMP, }; static enum power_supply_usb_type usb_psy_supported_types[] = { @@ -833,6 +883,26 @@ static const struct power_supply_desc usb_psy_desc = { .property_is_writeable = usb_psy_prop_is_writeable, }; +static int __battery_psy_set_charge_current(struct battery_chg_dev *bcdev, + u32 fcc_ua) +{ + int rc; + + if (bcdev->restrict_chg_en) + fcc_ua = min_t(u32, fcc_ua, bcdev->thermal_fcc_ua); + else + fcc_ua = bcdev->thermal_fcc_ua; + + rc = write_property_id(bcdev, &bcdev->psy_list[PSY_TYPE_BATTERY], + BATT_CHG_CTRL_LIM, fcc_ua); + if (rc < 0) + pr_err("Failed to set FCC %u, rc=%d\n", fcc_ua, rc); + else + pr_debug("Set FCC to %u uA\n", fcc_ua); + + return rc; +} + static int battery_psy_set_charge_current(struct battery_chg_dev *bcdev, int val) { @@ -852,11 +922,10 @@ static int battery_psy_set_charge_current(struct battery_chg_dev *bcdev, fcc_ua = bcdev->thermal_levels[val]; - rc = write_property_id(bcdev, &bcdev->psy_list[PSY_TYPE_BATTERY], - BATT_CHG_CTRL_LIM, fcc_ua); + rc = __battery_psy_set_charge_current(bcdev, fcc_ua); if (!rc) { bcdev->curr_thermal_level = val; - pr_debug("Set FCC to %u uA\n", fcc_ua); + bcdev->thermal_fcc_ua = fcc_ua; } return rc; @@ -1232,6 +1301,102 @@ static ssize_t wireless_fw_update_store(struct class *c, } static CLASS_ATTR_WO(wireless_fw_update); +static ssize_t usb_typec_compliant_show(struct class *c, + struct class_attribute *attr, char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_USB]; + int rc; + + rc = read_property_id(bcdev, pst, USB_TYPEC_COMPLIANT); + if (rc < 0) + return rc; + + return scnprintf(buf, PAGE_SIZE, "%d\n", + (int)pst->prop[USB_TYPEC_COMPLIANT]); +} +static CLASS_ATTR_RO(usb_typec_compliant); + +static ssize_t usb_real_type_show(struct class *c, + struct class_attribute *attr, char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_USB]; + int rc; + + rc = read_property_id(bcdev, pst, USB_REAL_TYPE); + if (rc < 0) + return rc; + + return scnprintf(buf, PAGE_SIZE, "%s\n", + get_usb_type_name(pst->prop[USB_REAL_TYPE])); +} +static CLASS_ATTR_RO(usb_real_type); + +static ssize_t restrict_cur_store(struct class *c, struct class_attribute *attr, + const char *buf, size_t count) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + int rc; + u32 val; + + if (kstrtou32(buf, 0, &val) || val > bcdev->thermal_fcc_ua) + return -EINVAL; + + if (bcdev->restrict_chg_en) { + rc = __battery_psy_set_charge_current(bcdev, val); + if (rc < 0) + return rc; + } + + bcdev->restrict_fcc_ua = val; + + return count; +} + +static ssize_t restrict_cur_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + + return scnprintf(buf, PAGE_SIZE, "%u\n", bcdev->restrict_fcc_ua); +} +static CLASS_ATTR_RW(restrict_cur); + +static ssize_t restrict_chg_store(struct class *c, struct class_attribute *attr, + const char *buf, size_t count) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + int rc; + bool val; + + if (kstrtobool(buf, &val)) + return -EINVAL; + + bcdev->restrict_chg_en = val; + + rc = __battery_psy_set_charge_current(bcdev, bcdev->restrict_fcc_ua); + if (rc < 0) + return rc; + + return count; +} + +static ssize_t restrict_chg_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + + return scnprintf(buf, PAGE_SIZE, "%d\n", bcdev->restrict_chg_en); +} +static CLASS_ATTR_RW(restrict_chg); + static ssize_t fake_soc_store(struct class *c, struct class_attribute *attr, const char *buf, size_t count) { @@ -1417,6 +1582,10 @@ static struct attribute *battery_class_attrs[] = { &class_attr_wireless_fw_force_update.attr, &class_attr_wireless_fw_version.attr, &class_attr_ship_mode_en.attr, + &class_attr_restrict_chg.attr, + &class_attr_restrict_cur.attr, + &class_attr_usb_real_type.attr, + &class_attr_usb_typec_compliant.attr, NULL, }; ATTRIBUTE_GROUPS(battery_class); @@ -1512,6 +1681,7 @@ static int battery_chg_parse_dt(struct battery_chg_dev *bcdev) } bcdev->num_thermal_levels = len; + bcdev->thermal_fcc_ua = pst->prop[BATT_CHG_CTRL_LIM_MAX]; return 0; } @@ -1609,6 +1779,7 @@ static int battery_chg_probe(struct platform_device *pdev) if (rc < 0) goto error; + bcdev->restrict_fcc_ua = DEFAULT_RESTRICT_FCC_UA; platform_set_drvdata(pdev, bcdev); bcdev->fake_soc = -EINVAL; rc = battery_chg_init_psy(bcdev); From ec2657eb96c23b8679deeec54b56ad0c7131718f Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Thu, 23 Jul 2020 20:02:45 -0700 Subject: [PATCH 8/8] power: supply: qti_battery_charger: update USB power supply type runtime Currently, USB power supply type is set to POWER_SUPPLY_TYPE_USB in power supply descriptor. power_supply_show_property() would show POWER_SUPPLY_PROP_TYPE based on psy->desc->type. Userspace process like healthd would read POWER_SUPPLY_PROP_TYPE to print the adapter type in logs (e.g. "chg=u" for SDP, "chg=a" for DCP). Hence update usb_psy_desc type runtime to show the USB power supply type accordingly. This is useful for testing. Change-Id: Icd88098f97beba053105d826614b4182f3716144 Signed-off-by: Subbaraman Narayanamurthy --- drivers/power/supply/qti_battery_charger.c | 53 +++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/qti_battery_charger.c b/drivers/power/supply/qti_battery_charger.c index 6917808719ca..e32940879993 100644 --- a/drivers/power/supply/qti_battery_charger.c +++ b/drivers/power/supply/qti_battery_charger.c @@ -224,6 +224,7 @@ struct battery_chg_dev { int num_thermal_levels; atomic_t state; struct work_struct subsys_up_work; + struct work_struct usb_type_work; int fake_soc; bool block_tx; bool ship_mode_en; @@ -620,6 +621,54 @@ static void handle_message(struct battery_chg_dev *bcdev, void *data, complete(&bcdev->ack); } +static struct power_supply_desc usb_psy_desc; + +static void battery_chg_update_usb_type_work(struct work_struct *work) +{ + struct battery_chg_dev *bcdev = container_of(work, + struct battery_chg_dev, usb_type_work); + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_USB]; + int rc; + + rc = read_property_id(bcdev, pst, USB_ADAP_TYPE); + if (rc < 0) { + pr_err("Failed to read USB_ADAP_TYPE rc=%d\n", rc); + return; + } + + pr_debug("usb_adap_type: %u\n", pst->prop[USB_ADAP_TYPE]); + + switch (pst->prop[USB_ADAP_TYPE]) { + case POWER_SUPPLY_USB_TYPE_SDP: + usb_psy_desc.type = POWER_SUPPLY_TYPE_USB; + break; + case POWER_SUPPLY_USB_TYPE_DCP: + case POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID: + case QTI_POWER_SUPPLY_USB_TYPE_HVDCP: + case QTI_POWER_SUPPLY_USB_TYPE_HVDCP_3: + case QTI_POWER_SUPPLY_USB_TYPE_HVDCP_3P5: + usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP; + break; + case POWER_SUPPLY_USB_TYPE_CDP: + usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_CDP; + break; + case POWER_SUPPLY_USB_TYPE_ACA: + usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_ACA; + break; + case POWER_SUPPLY_USB_TYPE_C: + usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_TYPE_C; + break; + case POWER_SUPPLY_USB_TYPE_PD: + case POWER_SUPPLY_USB_TYPE_PD_DRP: + case POWER_SUPPLY_USB_TYPE_PD_PPS: + usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD; + break; + default: + usb_psy_desc.type = POWER_SUPPLY_TYPE_USB; + break; + } +} + static void handle_notification(struct battery_chg_dev *bcdev, void *data, size_t len) { @@ -640,6 +689,7 @@ static void handle_notification(struct battery_chg_dev *bcdev, void *data, break; case BC_USB_STATUS_GET: pst = &bcdev->psy_list[PSY_TYPE_USB]; + schedule_work(&bcdev->usb_type_work); break; case BC_WLS_STATUS_GET: pst = &bcdev->psy_list[PSY_TYPE_WLS]; @@ -871,7 +921,7 @@ static enum power_supply_usb_type usb_psy_supported_types[] = { POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID, }; -static const struct power_supply_desc usb_psy_desc = { +static struct power_supply_desc usb_psy_desc = { .name = "usb", .type = POWER_SUPPLY_TYPE_USB, .properties = usb_props, @@ -1753,6 +1803,7 @@ static int battery_chg_probe(struct platform_device *pdev) init_completion(&bcdev->fw_buf_ack); init_completion(&bcdev->fw_update_ack); INIT_WORK(&bcdev->subsys_up_work, battery_chg_subsys_up_work); + INIT_WORK(&bcdev->usb_type_work, battery_chg_update_usb_type_work); atomic_set(&bcdev->state, PMIC_GLINK_STATE_UP); bcdev->dev = dev;