Merge "soc: qcom: altmode-glink: add SSR support"

This commit is contained in:
qctecmdr 2020-04-04 01:02:04 -07:00 committed by Gerrit - the friendly Code Review server
commit d8b549ff22
7 changed files with 298 additions and 52 deletions

View File

@ -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.callback = battery_chg_callback;
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:

View File

@ -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.callback = altmode_callback;
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)

View File

@ -6,6 +6,7 @@
#define pr_fmt(fmt) "PMIC_GLINK: %s: " fmt, __func__
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/idr.h>
#include <linux/list.h>
@ -16,6 +17,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <soc/qcom/subsystem_notif.h>
#include <linux/soc/qcom/pmic_glink.h>
/**
@ -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;
};
/**
@ -63,7 +77,11 @@ 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
* @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;
@ -71,7 +89,10 @@ 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 list_head node;
void (*state_cb)(void *priv,
enum pmic_glink_state state);
};
struct pmic_glink_buf {
@ -83,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;
@ -177,7 +242,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,9 +269,10 @@ 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;
client->state_cb = client_data->state_cb;
mutex_lock(&pgdev->client_lock);
rc = idr_alloc(&pgdev->client_idr, client, client->id, client->id + 1,
@ -220,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;
@ -238,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);
@ -263,7 +340,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 +353,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)
@ -411,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;
@ -463,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);

View File

@ -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);

View File

@ -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);

View File

@ -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.callback = ucsi_callback;
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;
}

View File

@ -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
@ -11,18 +11,27 @@
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
* @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
* @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 (*callback)(void *priv, void *data, size_t len);
int (*msg_cb)(void *priv, void *data, size_t len);
void (*state_cb)(void *priv, enum pmic_glink_state state);
};
/**