Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d8b549ff authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

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

parents d83df737 d3278184
Loading
Loading
Loading
Loading
+51 −10
Original line number Diff line number Diff line
@@ -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:
+35 −8
Original line number Diff line number Diff line
@@ -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)
+116 −8
Original line number Diff line number Diff line
@@ -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);
+1 −1
Original line number Diff line number Diff line
@@ -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);
+1 −1
Original line number Diff line number Diff line
@@ -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);
Loading