Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions drivers/usb/typec/ucsi/ucsi_glink.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,23 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/pm_wakeup.h>
#include <linux/property.h>
#include <linux/soc/qcom/pdr.h>
#include <linux/usb/typec_mux.h>
#include <linux/gpio/consumer.h>
#include <linux/soc/qcom/pmic_glink.h>
#include "ucsi.h"

/*
* Wakeup timeout covering two async hops:
* 1. pmic_glink_ucsi_notify() work runs ucsi_notify_common()
* 2. ucsi_handle_connector_change() work runs and notifies USB
* ucsi_handle_connector_change() involves GLINK round-trips with up to 5s
* timeouts, so 10s gives sufficient margin for the full chain to complete.
*/
#define UCSI_GLINK_WAKEUP_TIMEOUT_MS 200

#define PMIC_GLINK_MAX_PORTS 3

#define UCSI_BUF_V1_SIZE (UCSI_MESSAGE_OUT + (UCSI_MESSAGE_OUT - UCSI_MESSAGE_IN))
Expand Down Expand Up @@ -79,6 +89,9 @@ struct pmic_glink_ucsi {
bool pd_running;

u8 read_buf[UCSI_BUF_V2_SIZE];

/* Wakeup timeout in ms; configurable via sysfs wakeup_timeout_ms */
unsigned int wakeup_timeout_ms;
};

static int pmic_glink_ucsi_read(struct ucsi *__ucsi, unsigned int offset,
Expand Down Expand Up @@ -329,6 +342,41 @@ static void pmic_glink_ucsi_register(struct work_struct *work)
}
}

static ssize_t wakeup_timeout_ms_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pmic_glink_ucsi *ucsi = dev_get_drvdata(dev);

return sysfs_emit(buf, "%u\n", READ_ONCE(ucsi->wakeup_timeout_ms));
}

static ssize_t wakeup_timeout_ms_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct pmic_glink_ucsi *ucsi = dev_get_drvdata(dev);
unsigned int val;
int ret;

ret = kstrtouint(buf, 10, &val);
if (ret)
return ret;

WRITE_ONCE(ucsi->wakeup_timeout_ms, val);
return count;
}

static DEVICE_ATTR_RW(wakeup_timeout_ms);

static struct attribute *pmic_glink_ucsi_attrs[] = {
&dev_attr_wakeup_timeout_ms.attr,
NULL,
};

static const struct attribute_group pmic_glink_ucsi_attr_group = {
.attrs = pmic_glink_ucsi_attrs,
};

static void pmic_glink_ucsi_callback(const void *data, size_t len, void *priv)
{
struct pmic_glink_ucsi *ucsi = priv;
Expand All @@ -342,6 +390,7 @@ static void pmic_glink_ucsi_callback(const void *data, size_t len, void *priv)
pmic_glink_ucsi_write_ack(ucsi, data, len);
break;
case UC_UCSI_USBC_NOTIFY_IND:
pm_wakeup_event(ucsi->dev, READ_ONCE(ucsi->wakeup_timeout_ms));
schedule_work(&ucsi->notify_work);
break;
}
Expand Down Expand Up @@ -401,6 +450,13 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev,
ucsi->dev = dev;
dev_set_drvdata(dev, ucsi);

device_init_wakeup(dev, true);
ucsi->wakeup_timeout_ms = UCSI_GLINK_WAKEUP_TIMEOUT_MS;

ret = devm_device_add_group(dev, &pmic_glink_ucsi_attr_group);
if (ret)
return ret;

INIT_WORK(&ucsi->notify_work, pmic_glink_ucsi_notify);
INIT_WORK(&ucsi->register_work, pmic_glink_ucsi_register);
init_completion(&ucsi->read_ack);
Expand Down