From 9d65615c150e67e7f9cb7a489f23016af72e78cc Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Mon, 30 Mar 2020 12:50:18 -0700 Subject: [PATCH 1/5] soc: qcom: pmic_glink: rename callback function pointer In preparation for subsystem restart (SSR) and protection domain restart (PDR) where subsystem state transitions would be sent to PMIC Glink clients via another callback function, change the client callback function pointer name to msg_cb. This would convey that this callback is for delivering the messages to a client upon its registration. Change-Id: I584d6c5d3898fe54e6928b25502d6382ce6ccbc4 Signed-off-by: Subbaraman Narayanamurthy --- drivers/power/supply/qti_battery_charger.c | 2 +- drivers/soc/qcom/altmode-glink.c | 2 +- drivers/soc/qcom/pmic_glink.c | 13 +++++++------ drivers/soc/qcom/qti_battery_debug.c | 2 +- drivers/spmi/spmi-glink-debug.c | 2 +- drivers/usb/typec/ucsi/ucsi_glink.c | 2 +- include/linux/soc/qcom/pmic_glink.h | 7 ++++--- 7 files changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/power/supply/qti_battery_charger.c b/drivers/power/supply/qti_battery_charger.c index 94efdc19cb87..205147504130 100644 --- a/drivers/power/supply/qti_battery_charger.c +++ b/drivers/power/supply/qti_battery_charger.c @@ -1016,7 +1016,7 @@ static int battery_chg_probe(struct platform_device *pdev) client_data.id = MSG_OWNER_BC; client_data.name = "battery_charger"; - client_data.callback = battery_chg_callback; + client_data.msg_cb = battery_chg_callback; client_data.priv = bcdev; bcdev->client = pmic_glink_register_client(dev, &client_data); diff --git a/drivers/soc/qcom/altmode-glink.c b/drivers/soc/qcom/altmode-glink.c index bf994c404f36..9a5f674803bd 100644 --- a/drivers/soc/qcom/altmode-glink.c +++ b/drivers/soc/qcom/altmode-glink.c @@ -497,7 +497,7 @@ static int altmode_probe(struct platform_device *pdev) pgclient_data.id = MSG_OWNER_USBC_PAN; pgclient_data.name = "altmode"; - pgclient_data.callback = altmode_callback; + pgclient_data.msg_cb = altmode_callback; pgclient_data.priv = amdev; amdev->pgclient = pmic_glink_register_client(amdev->dev, diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c index 8fc6f37ee218..f2d6bcd46a78 100644 --- a/drivers/soc/qcom/pmic_glink.c +++ b/drivers/soc/qcom/pmic_glink.c @@ -63,7 +63,8 @@ struct pmic_glink_dev { * @id: Unique id for client for communication * @lock: lock for sending data * @priv: private data for client - * @callback: callback function for client + * @msg_cb: callback function for client to receive the messages that + * are intended to be delivered to it over PMIC Glink */ struct pmic_glink_client { struct pmic_glink_dev *pgdev; @@ -71,7 +72,7 @@ struct pmic_glink_client { u32 id; struct mutex lock; void *priv; - int (*callback)(void *priv, void *data, size_t len); + int (*msg_cb)(void *priv, void *data, size_t len); }; struct pmic_glink_buf { @@ -177,7 +178,7 @@ struct pmic_glink_client *pmic_glink_register_client(struct device *dev, if (!dev || !dev->parent) return ERR_PTR(-ENODEV); - if (!client_data->id || !client_data->callback || !client_data->name) + if (!client_data->id || !client_data->msg_cb || !client_data->name) return ERR_PTR(-EINVAL); pgdev = get_pmic_glink_from_dev(dev->parent); @@ -204,7 +205,7 @@ struct pmic_glink_client *pmic_glink_register_client(struct device *dev, mutex_init(&client->lock); client->id = client_data->id; - client->callback = client_data->callback; + client->msg_cb = client_data->msg_cb; client->priv = client_data->priv; client->pgdev = pgdev; @@ -263,7 +264,7 @@ static void pmic_glink_rx_callback(struct pmic_glink_dev *pgdev, client = idr_find(&pgdev->client_idr, hdr->owner); mutex_unlock(&pgdev->client_lock); - if (!client || !client->callback) { + if (!client || !client->msg_cb) { pr_err("No client present for %u\n", hdr->owner); return; } @@ -276,7 +277,7 @@ static void pmic_glink_rx_callback(struct pmic_glink_dev *pgdev, pbuf->buf); } - client->callback(client->priv, pbuf->buf, pbuf->len); + client->msg_cb(client->priv, pbuf->buf, pbuf->len); } static void pmic_glink_rx_work(struct work_struct *work) diff --git a/drivers/soc/qcom/qti_battery_debug.c b/drivers/soc/qcom/qti_battery_debug.c index e655a81698a2..8e464d1af1ed 100644 --- a/drivers/soc/qcom/qti_battery_debug.c +++ b/drivers/soc/qcom/qti_battery_debug.c @@ -683,7 +683,7 @@ static int battery_dbg_probe(struct platform_device *pdev) bd->dev = &pdev->dev; client_data.id = MSG_OWNER_BD; client_data.name = "battery_debug"; - client_data.callback = battery_dbg_callback; + client_data.msg_cb = battery_dbg_callback; client_data.priv = bd; bd->client = pmic_glink_register_client(bd->dev, &client_data); diff --git a/drivers/spmi/spmi-glink-debug.c b/drivers/spmi/spmi-glink-debug.c index b0ebb1918103..1c6520a91a8a 100644 --- a/drivers/spmi/spmi-glink-debug.c +++ b/drivers/spmi/spmi-glink-debug.c @@ -321,7 +321,7 @@ static int spmi_glink_probe(struct platform_device *pdev) client_data.id = MSG_OWNER_REG_DUMP; client_data.name = "spmi_register_debug"; - client_data.callback = spmi_glink_callback; + client_data.msg_cb = spmi_glink_callback; client_data.priv = gd; gd->client = pmic_glink_register_client(&pdev->dev, &client_data); diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index e35757f902dd..aa5302d1eb84 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -464,7 +464,7 @@ static int ucsi_probe(struct platform_device *pdev) client_data.id = MSG_OWNER_UC; client_data.name = "ucsi"; - client_data.callback = ucsi_callback; + client_data.msg_cb = ucsi_callback; client_data.priv = udev; udev->client = pmic_glink_register_client(dev, &client_data); diff --git a/include/linux/soc/qcom/pmic_glink.h b/include/linux/soc/qcom/pmic_glink.h index fe3039b12f50..24d1e47d5006 100644 --- a/include/linux/soc/qcom/pmic_glink.h +++ b/include/linux/soc/qcom/pmic_glink.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */ #ifndef _PMIC_GLINK_H @@ -16,13 +16,14 @@ struct device; * @name: Client name * @id: Unique id for client for communication * @priv: private data for client - * @callback: callback function for client + * @msg_cb: callback function for client to receive the messages that + * are intended to be delivered to it over PMIC Glink */ struct pmic_glink_client_data { const char *name; u32 id; void *priv; - int (*callback)(void *priv, void *data, size_t len); + int (*msg_cb)(void *priv, void *data, size_t len); }; /** From 04d7c9d33243bd5414bd0c8339899609ecf64355 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Tue, 17 Mar 2020 20:36:56 -0700 Subject: [PATCH 2/5] soc: qcom: pmic_glink: add subsystem restart (SSR) support Add support for PMIC Glink clients to receive notification when the subsystem goes down and comes up again. PMIC Glink client can pass a function pointer state_cb via which they would like to receive when PMIC Glink state transitions so that they can unregister from the framework (e.g. UCSI) and clean up their state as needed. PMIC Glink device supports SSR for the subsystem specified under "qcom,subsys-name" property. Change-Id: I0f32f638d72a13e231e6813fb3d782c8fa7a4af0 Signed-off-by: Subbaraman Narayanamurthy --- drivers/soc/qcom/pmic_glink.c | 111 +++++++++++++++++++++++++++- include/linux/soc/qcom/pmic_glink.h | 8 ++ 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c index f2d6bcd46a78..f3b5167bb8a3 100644 --- a/drivers/soc/qcom/pmic_glink.c +++ b/drivers/soc/qcom/pmic_glink.c @@ -6,6 +6,7 @@ #define pr_fmt(fmt) "PMIC_GLINK: %s: " fmt, __func__ #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include /** @@ -33,9 +35,16 @@ * @rx_list: list for rx messages * @dev_list: list for pmic_glink_dev_list * @state: indicates when remote subsystem is up/down + * @prev_state: previous state of remote subsystem * @child_probed: indicates when the children are probed * @log_filter: message owner filter for logging * @log_enable: enables message logging + * @client_dev_list: list of client devices to be notified on state + * transition during an SSR or PDR + * @ssr_nb: notifier block for subsystem notifier + * @subsys_name: subsystem name from which SSR notifications should + * be handled and notified to the clients + * @subsys_handle: handle to subsystem notifier */ struct pmic_glink_dev { struct rpmsg_device *rpdev; @@ -51,9 +60,14 @@ struct pmic_glink_dev { struct list_head rx_list; struct list_head dev_list; atomic_t state; + atomic_t prev_state; bool child_probed; u32 log_filter; bool log_enable; + struct list_head client_dev_list; + struct notifier_block ssr_nb; + const char *subsys_name; + void *subsys_handle; }; /** @@ -65,6 +79,9 @@ struct pmic_glink_dev { * @priv: private data for client * @msg_cb: callback function for client to receive the messages that * are intended to be delivered to it over PMIC Glink + * @node: list node to be added in client_dev_list of pmic_glink device + * @state_cb: callback function to notify pmic glink state in the event of + * a subsystem restart (SSR) or a protection domain restart (PDR) */ struct pmic_glink_client { struct pmic_glink_dev *pgdev; @@ -73,6 +90,9 @@ struct pmic_glink_client { struct mutex lock; void *priv; int (*msg_cb)(void *priv, void *data, size_t len); + struct list_head node; + void (*state_cb)(void *priv, + enum pmic_glink_state state); }; struct pmic_glink_buf { @@ -84,6 +104,50 @@ struct pmic_glink_buf { static LIST_HEAD(pmic_glink_dev_list); static DEFINE_MUTEX(pmic_glink_dev_lock); +static void pmic_glink_notify_clients(struct pmic_glink_dev *pgdev, + enum pmic_glink_state state) +{ + struct pmic_glink_client *pos; + + pm_stay_awake(pgdev->dev); + + mutex_lock(&pgdev->client_lock); + list_for_each_entry(pos, &pgdev->client_dev_list, node) + pos->state_cb(pos->priv, state); + mutex_unlock(&pgdev->client_lock); + + pm_relax(pgdev->dev); + + pr_debug("state_cb done %d\n", state); +} + +static int pmic_glink_ssr_notifier_cb(struct notifier_block *nb, + unsigned long code, void *data) +{ + struct pmic_glink_dev *pgdev = container_of(nb, struct pmic_glink_dev, + ssr_nb); + + pr_debug("code: %lu\n", code); + + switch (code) { + case SUBSYS_BEFORE_SHUTDOWN: + atomic_set(&pgdev->prev_state, code); + pmic_glink_notify_clients(pgdev, PMIC_GLINK_STATE_DOWN); + break; + case SUBSYS_AFTER_POWERUP: + /* + * Do not notify PMIC Glink clients here but rather from + * pmic_glink_init_work which will be run only after rpmsg + * driver is probed and Glink communication is up. + */ + break; + default: + break; + } + + return NOTIFY_DONE; +} + static struct pmic_glink_dev *get_pmic_glink_from_dev(struct device *dev) { struct pmic_glink_dev *tmp, *pos; @@ -208,6 +272,7 @@ struct pmic_glink_client *pmic_glink_register_client(struct device *dev, client->msg_cb = client_data->msg_cb; client->priv = client_data->priv; client->pgdev = pgdev; + client->state_cb = client_data->state_cb; mutex_lock(&pgdev->client_lock); rc = idr_alloc(&pgdev->client_idr, client, client->id, client->id + 1, @@ -221,6 +286,10 @@ struct pmic_glink_client *pmic_glink_register_client(struct device *dev, return ERR_PTR(rc); } + if (client->state_cb) { + INIT_LIST_HEAD(&client->node); + list_add_tail(&client->node, &pgdev->client_dev_list); + } mutex_unlock(&pgdev->client_lock); return client; @@ -239,10 +308,17 @@ EXPORT_SYMBOL(pmic_glink_register_client); */ int pmic_glink_unregister_client(struct pmic_glink_client *client) { + struct pmic_glink_client *pos, *tmp; + if (!client || !client->pgdev) return -ENODEV; mutex_lock(&client->pgdev->client_lock); + list_for_each_entry_safe(pos, tmp, &client->pgdev->client_dev_list, + node) { + if (pos == client) + list_del(&client->node); + } idr_remove(&client->pgdev->client_idr, client->id); mutex_unlock(&client->pgdev->client_lock); @@ -412,6 +488,11 @@ static void pmic_glink_init_work(struct work_struct *work) struct device *dev = pgdev->dev; int rc; + if (atomic_read(&pgdev->prev_state) == SUBSYS_BEFORE_SHUTDOWN) { + pmic_glink_notify_clients(pgdev, PMIC_GLINK_STATE_UP); + atomic_set(&pgdev->prev_state, SUBSYS_AFTER_POWERUP); + } + if (pgdev->child_probed) return; @@ -464,34 +545,60 @@ static int pmic_glink_probe(struct platform_device *pdev) return -EINVAL; } + of_property_read_string(dev->of_node, "qcom,subsys-name", + &pgdev->subsys_name); + pgdev->rx_wq = create_singlethread_workqueue("pmic_glink_rx"); if (!pgdev->rx_wq) { pr_err("Failed to create pmic_glink_rx wq\n"); return -ENOMEM; } - dev_set_drvdata(dev, pgdev); - INIT_WORK(&pgdev->rx_work, pmic_glink_rx_work); INIT_WORK(&pgdev->init_work, pmic_glink_init_work); + INIT_LIST_HEAD(&pgdev->client_dev_list); INIT_LIST_HEAD(&pgdev->rx_list); INIT_LIST_HEAD(&pgdev->dev_list); spin_lock_init(&pgdev->rx_lock); mutex_init(&pgdev->client_lock); idr_init(&pgdev->client_idr); + atomic_set(&pgdev->prev_state, SUBSYS_BEFORE_POWERUP); + + if (pgdev->subsys_name) { + pgdev->ssr_nb.notifier_call = pmic_glink_ssr_notifier_cb; + pgdev->subsys_handle = subsys_notif_register_notifier( + pgdev->subsys_name, + &pgdev->ssr_nb); + if (IS_ERR(pgdev->subsys_handle)) { + rc = PTR_ERR(pgdev->subsys_handle); + pr_err("Failed in subsys_notif_register_notifier %d\n", + rc); + goto error_subsys; + } + } + + dev_set_drvdata(dev, pgdev); pgdev->dev = dev; pmic_glink_dev_add(pgdev); pmic_glink_add_debugfs(pgdev); + device_init_wakeup(pgdev->dev, true); pr_debug("%s probed successfully\n", pgdev->channel_name); return 0; + +error_subsys: + idr_destroy(&pgdev->client_idr); + destroy_workqueue(pgdev->rx_wq); + return rc; } static int pmic_glink_remove(struct platform_device *pdev) { struct pmic_glink_dev *pgdev = dev_get_drvdata(&pdev->dev); + subsys_notif_unregister_notifier(pgdev->subsys_handle, &pgdev->ssr_nb); + device_init_wakeup(pgdev->dev, false); debugfs_remove_recursive(pgdev->debugfs_dir); flush_workqueue(pgdev->rx_wq); destroy_workqueue(pgdev->rx_wq); diff --git a/include/linux/soc/qcom/pmic_glink.h b/include/linux/soc/qcom/pmic_glink.h index 24d1e47d5006..ea0b102a484b 100644 --- a/include/linux/soc/qcom/pmic_glink.h +++ b/include/linux/soc/qcom/pmic_glink.h @@ -11,6 +11,11 @@ struct pmic_glink_client; struct device; +enum pmic_glink_state { + PMIC_GLINK_STATE_DOWN, + PMIC_GLINK_STATE_UP, +}; + /** * struct pmic_glink_client_data - pmic_glink client data * @name: Client name @@ -18,12 +23,15 @@ struct device; * @priv: private data for client * @msg_cb: callback function for client to receive the messages that * are intended to be delivered to it over PMIC Glink + * @state_cb: callback function to notify pmic glink state in the event of + * a subsystem restart (SSR) or a protection domain restart (PDR) */ struct pmic_glink_client_data { const char *name; u32 id; void *priv; int (*msg_cb)(void *priv, void *data, size_t len); + void (*state_cb)(void *priv, enum pmic_glink_state state); }; /** From 2965150685be2dce8aeb8a1923801095e44913e1 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Fri, 13 Mar 2020 16:56:39 -0700 Subject: [PATCH 3/5] ucsi: ucsi_glink: add SSR support Add subsystem restart (SSR) support to UCSI Glink driver so that it can unregister from UCSI framework when subsystem goes down and register with UCSI framework when it comes up again. When subsystem goes down, it is not possible for UCSI stack to send or receive data to and from PPM (i.e. remote subsystem) over PMIC Glink. Hence, return immediately in ucsi_qti_{read,write}() to avoid error logs and handle cleanup easily. Change-Id: I711920222bd531af27b87337cad7db8aabbaa466 Signed-off-by: Subbaraman Narayanamurthy --- drivers/usb/typec/ucsi/ucsi_glink.c | 101 ++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 20 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index aa5302d1eb84..d5340da33205 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -84,6 +84,8 @@ struct ucsi_dev { unsigned long cmd_requested_flags; struct ucsi_glink_constat_info constat_info; struct work_struct notify_work; + struct work_struct setup_work; + atomic_t state; }; static void *ucsi_ipc_log; @@ -267,6 +269,9 @@ static int ucsi_qti_glink_write(struct ucsi_dev *udev, unsigned int offset, if (!validate_ucsi_msg(offset, val_len)) return -EINVAL; + if (atomic_read(&udev->state) == PMIC_GLINK_STATE_DOWN) + return 0; + ucsi_buf.hdr.owner = MSG_OWNER_UC; ucsi_buf.hdr.type = MSG_TYPE_REQ_RESP; ucsi_buf.hdr.opcode = UC_UCSI_WRITE_BUF_REQ; @@ -395,6 +400,9 @@ static int ucsi_qti_read(struct ucsi *ucsi, unsigned int offset, if (!validate_ucsi_msg(offset, val_len)) return -EINVAL; + if (atomic_read(&udev->state) == PMIC_GLINK_STATE_DOWN) + return 0; + ucsi_buf.hdr.owner = MSG_OWNER_UC; ucsi_buf.hdr.type = MSG_TYPE_REQ_RESP; ucsi_buf.hdr.opcode = UC_UCSI_READ_BUF_REQ; @@ -442,6 +450,71 @@ static const struct ucsi_operations ucsi_qti_ops = { .async_write = ucsi_qti_async_write }; +static int ucsi_setup(struct ucsi_dev *udev) +{ + int rc; + + if (udev->ucsi) { + dev_err(udev->dev, "ucsi is not NULL\n"); + return -EINVAL; + } + + udev->ucsi = ucsi_create(udev->dev, &ucsi_qti_ops); + if (IS_ERR(udev->ucsi)) { + rc = PTR_ERR(udev->ucsi); + dev_err(udev->dev, "ucsi_create failed rc=%d\n", rc); + udev->ucsi = NULL; + return rc; + } + + ucsi_set_drvdata(udev->ucsi, udev); + + rc = ucsi_register(udev->ucsi); + if (rc) { + dev_err(udev->dev, "ucsi_register failed rc=%d\n", rc); + ucsi_destroy(udev->ucsi); + udev->ucsi = NULL; + return rc; + } + + return 0; +} + +static void ucsi_qti_setup_work(struct work_struct *work) +{ + struct ucsi_dev *udev = container_of(work, struct ucsi_dev, + setup_work); + + ucsi_setup(udev); +} + +static void ucsi_qti_state_cb(void *priv, enum pmic_glink_state state) +{ + struct ucsi_dev *udev = priv; + + dev_dbg(udev->dev, "state: %d\n", state); + + atomic_set(&udev->state, state); + + switch (state) { + case PMIC_GLINK_STATE_DOWN: + if (!udev->ucsi) { + dev_err(udev->dev, "ucsi is NULL\n"); + return; + } + + ucsi_unregister(udev->ucsi); + ucsi_destroy(udev->ucsi); + udev->ucsi = NULL; + break; + case PMIC_GLINK_STATE_UP: + schedule_work(&udev->setup_work); + break; + default: + break; + } +} + static int ucsi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -454,6 +527,7 @@ static int ucsi_probe(struct platform_device *pdev) return -ENOMEM; INIT_WORK(&udev->notify_work, ucsi_qti_notify_work); + INIT_WORK(&udev->setup_work, ucsi_qti_setup_work); mutex_init(&udev->read_lock); mutex_init(&udev->write_lock); mutex_init(&udev->notify_lock); @@ -461,11 +535,13 @@ static int ucsi_probe(struct platform_device *pdev) init_completion(&udev->write_ack); init_completion(&udev->sync_write_ack); atomic_set(&udev->rx_valid, 0); + atomic_set(&udev->state, PMIC_GLINK_STATE_UP); client_data.id = MSG_OWNER_UC; client_data.name = "ucsi"; client_data.msg_cb = ucsi_callback; client_data.priv = udev; + client_data.state_cb = ucsi_qti_state_cb; udev->client = pmic_glink_register_client(dev, &client_data); if (IS_ERR(udev->client)) { @@ -477,34 +553,19 @@ static int ucsi_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, udev); + udev->dev = dev; - udev->ucsi = ucsi_create(dev, &ucsi_qti_ops); - if (IS_ERR(udev->ucsi)) { - rc = PTR_ERR(udev->ucsi); - dev_err(dev, "ucsi_create failed rc=%d\n", rc); - return rc; - } - - ucsi_set_drvdata(udev->ucsi, udev); ucsi_ipc_log = ipc_log_context_create(NUM_LOG_PAGES, "ucsi", 0); if (!ucsi_ipc_log) dev_warn(dev, "Error in creating ipc_log_context\n"); - rc = ucsi_register(udev->ucsi); + rc = ucsi_setup(udev); if (rc) { - dev_err(dev, "ucsi_register failed rc=%d\n", rc); - goto out; + ipc_log_context_destroy(ucsi_ipc_log); + ucsi_ipc_log = NULL; + pmic_glink_unregister_client(udev->client); } - pr_debug("driver probed\n"); - - return 0; -out: - ipc_log_context_destroy(ucsi_ipc_log); - ucsi_ipc_log = NULL; - ucsi_destroy(udev->ucsi); - pmic_glink_unregister_client(udev->client); - return rc; } From 4f24711c2653f66b3ab5df7f5f9e45e1e4b43df5 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Wed, 18 Mar 2020 21:00:19 -0700 Subject: [PATCH 4/5] power: supply: qti_battery_charger: add SSR support Add subsystem restart (SSR) support to battery charger driver so that it can stop read/write properties when subsystem goes down. Also, send message to enable notification again once subsystem comes up. Change-Id: I64f3abfa1d12c4e6a65fd01d7f66155c8741e68a Signed-off-by: Subbaraman Narayanamurthy --- drivers/power/supply/qti_battery_charger.c | 59 ++++++++++++++++++---- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/drivers/power/supply/qti_battery_charger.c b/drivers/power/supply/qti_battery_charger.c index 205147504130..6110b9f78e76 100644 --- a/drivers/power/supply/qti_battery_charger.c +++ b/drivers/power/supply/qti_battery_charger.c @@ -148,6 +148,8 @@ struct battery_chg_dev { u32 *thermal_levels; int curr_thermal_level; int num_thermal_levels; + atomic_t state; + struct work_struct subsys_up_work; }; static const int battery_prop_map[BATT_PROP_MAX] = { @@ -198,6 +200,16 @@ static int battery_chg_write(struct battery_chg_dev *bcdev, void *data, { int rc; + /* + * When the subsystem goes down, it's better to return the last + * known values until it comes back up. Hence, return 0 so that + * pmic_glink_write() is not attempted until pmic glink is up. + */ + if (atomic_read(&bcdev->state) == PMIC_GLINK_STATE_DOWN) { + pr_debug("glink state is down\n"); + return 0; + } + mutex_lock(&bcdev->rw_lock); reinit_completion(&bcdev->ack); rc = pmic_glink_write(bcdev->client, data, len); @@ -268,6 +280,40 @@ static int get_property_id(struct psy_state *pst, return -ENOENT; } +static void battery_chg_notify_enable(struct battery_chg_dev *bcdev) +{ + struct battery_charger_set_notify_msg req_msg = { { 0 } }; + int rc; + + /* Send request to enable notification */ + req_msg.hdr.owner = MSG_OWNER_BC; + req_msg.hdr.type = MSG_TYPE_NOTIFY; + req_msg.hdr.opcode = BC_SET_NOTIFY_REQ; + + rc = battery_chg_write(bcdev, &req_msg, sizeof(req_msg)); + if (rc < 0) + pr_err("Failed to enable notification rc=%d\n", rc); +} + +static void battery_chg_subsys_up_work(struct work_struct *work) +{ + struct battery_chg_dev *bcdev = container_of(work, + struct battery_chg_dev, subsys_up_work); + + battery_chg_notify_enable(bcdev); +} + +static void battery_chg_state_cb(void *priv, enum pmic_glink_state state) +{ + struct battery_chg_dev *bcdev = priv; + + pr_debug("state: %d\n", state); + + atomic_set(&bcdev->state, state); + if (state == PMIC_GLINK_STATE_UP) + schedule_work(&bcdev->subsys_up_work); +} + /** * qti_battery_charger_get_prop() - Gets the property being requested * @@ -977,7 +1023,6 @@ static int battery_chg_probe(struct platform_device *pdev) struct battery_chg_dev *bcdev; struct device *dev = &pdev->dev; struct pmic_glink_client_data client_data = { }; - struct battery_charger_set_notify_msg req_msg = { { 0 } }; int rc, i; bcdev = devm_kzalloc(&pdev->dev, sizeof(*bcdev), GFP_KERNEL); @@ -1012,12 +1057,15 @@ static int battery_chg_probe(struct platform_device *pdev) mutex_init(&bcdev->rw_lock); init_completion(&bcdev->ack); + INIT_WORK(&bcdev->subsys_up_work, battery_chg_subsys_up_work); + atomic_set(&bcdev->state, PMIC_GLINK_STATE_UP); bcdev->dev = dev; client_data.id = MSG_OWNER_BC; client_data.name = "battery_charger"; client_data.msg_cb = battery_chg_callback; client_data.priv = bcdev; + client_data.state_cb = battery_chg_state_cb; bcdev->client = pmic_glink_register_client(dev, &client_data); if (IS_ERR(bcdev->client)) { @@ -1045,14 +1093,7 @@ static int battery_chg_probe(struct platform_device *pdev) goto error; } - /* Send request to enable notification */ - req_msg.hdr.owner = MSG_OWNER_BC; - req_msg.hdr.type = MSG_TYPE_NOTIFY; - req_msg.hdr.opcode = BC_SET_NOTIFY_REQ; - - rc = battery_chg_write(bcdev, &req_msg, sizeof(req_msg)); - if (rc < 0) - pr_err("Failed to enable notification rc=%d\n", rc); + battery_chg_notify_enable(bcdev); return 0; error: From d32781847d620596112b69feab8198865e2587df Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Tue, 24 Mar 2020 18:41:51 -0700 Subject: [PATCH 5/5] soc: qcom: altmode-glink: add SSR support Add subsystem restart (SSR) support to altmode glink driver so that when subsystem goes down and comes up again, it can send PAN_EN to charger firmware to receive notifications about altmode interface. Change-Id: Ie011d5110b6c26790fe1dd6149115019f8bc261c Signed-off-by: Subbaraman Narayanamurthy --- drivers/soc/qcom/altmode-glink.c | 41 ++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/drivers/soc/qcom/altmode-glink.c b/drivers/soc/qcom/altmode-glink.c index 9a5f674803bd..bca5359e8704 100644 --- a/drivers/soc/qcom/altmode-glink.c +++ b/drivers/soc/qcom/altmode-glink.c @@ -381,6 +381,28 @@ static int altmode_send_ack(struct altmode_dev *amdev, u8 port_index) return rc; } +static void altmode_state_cb(void *priv, enum pmic_glink_state state) +{ + struct altmode_dev *amdev = priv; + + pr_debug("state: %d\n", state); + + switch (state) { + case PMIC_GLINK_STATE_DOWN: + /* As of now, nothing to do */ + break; + case PMIC_GLINK_STATE_UP: + mutex_lock(&amdev->client_lock); + if (!list_empty(&amdev->client_list)) + schedule_delayed_work(&amdev->send_pan_en_work, + msecs_to_jiffies(20)); + mutex_unlock(&amdev->client_lock); + break; + default: + return; + } +} + #define USBC_NOTIFY_IND_MASK GENMASK(7, 0) #define GET_OP(opcode) (opcode & USBC_NOTIFY_IND_MASK) #define GET_SVID(opcode) (opcode >> 16) @@ -495,10 +517,17 @@ static int altmode_probe(struct platform_device *pdev) RAW_INIT_NOTIFIER_HEAD(&amdev->probe_notifier); + mutex_init(&amdev->client_lock); + idr_init(&amdev->client_idr); + INIT_DELAYED_WORK(&amdev->send_pan_en_work, altmode_send_pan_en); + INIT_LIST_HEAD(&amdev->d_node); + INIT_LIST_HEAD(&amdev->client_list); + pgclient_data.id = MSG_OWNER_USBC_PAN; pgclient_data.name = "altmode"; pgclient_data.msg_cb = altmode_callback; pgclient_data.priv = amdev; + pgclient_data.state_cb = altmode_state_cb; amdev->pgclient = pmic_glink_register_client(amdev->dev, &pgclient_data); @@ -507,21 +536,19 @@ static int altmode_probe(struct platform_device *pdev) if (rc != -EPROBE_DEFER) dev_err(dev, "Error in pmic_glink registration: %d\n", rc); - return rc; + goto error_register; } platform_set_drvdata(pdev, amdev); - mutex_init(&amdev->client_lock); - idr_init(&amdev->client_idr); - INIT_DELAYED_WORK(&amdev->send_pan_en_work, altmode_send_pan_en); - INIT_LIST_HEAD(&amdev->d_node); - INIT_LIST_HEAD(&amdev->client_list); - altmode_device_add(amdev); altmode_notify_clients(amdev, pdev); return 0; + +error_register: + idr_destroy(&amdev->client_idr); + return rc; } static void altmode_device_remove(struct altmode_dev *amdev)