From: Ajay Gupta <ajaykuee@gmail.com>
To: heikki.krogerus@linux.intel.com
Cc: linux-usb@vger.kernel.org, linux-i2c@vger.kernel.org,
Ajay Gupta <ajayg@nvidia.com>
Subject: [PATCH 4/4] usb: typec: ucsi: ccg: add runtime pm workaround
Date: Fri, 17 May 2019 09:38:18 -0700 [thread overview]
Message-ID: <20190517163818.5007-5-ajayg@nvidia.com> (raw)
In-Reply-To: <20190517163818.5007-1-ajayg@nvidia.com>
From: Ajay Gupta <ajayg@nvidia.com>
Cypress USB Type-C CCGx controller firmware verson 3.1.10
(which is being used in many NVIDIA GPU cards) has known issue of
not triggering interrupt when a USB device is hot plugged to runtime
resume the controller. Many of these cards may get latest kernel but
may not get latest fixed firmware so a workaround is required to
check for any connector change event.
The workaround is that i2c bus driver will call pm_request_resume()
to runtime resume ucsi_ccg driver. CCG driver will call the ISR
for any connector change event only if NVIDIA GPU has old
CCG firmware with the known issue.
Signed-off-by: Ajay Gupta <ajayg@nvidia.com>
---
drivers/usb/typec/ucsi/ucsi_ccg.c | 78 +++++++++++++++++++++++++++++--
1 file changed, 74 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index cc7094ecda2d..3457e112fdbc 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -109,6 +109,8 @@ struct version_format {
__le16 build;
u8 patch;
u8 ver;
+#define CCG_VERSION_PATCH(x) ((x) << 16)
+#define CCG_VERSION(x) ((x) << 24)
#define CCG_VERSION_MIN_SHIFT (0)
#define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT)
#define CCG_VERSION_MAJ_SHIFT (4)
@@ -172,6 +174,7 @@ struct ucsi_ccg {
struct ccg_dev_info info;
/* version info for boot, primary and secondary */
struct version_info version[FW2 + 1];
+ u32 fw_version;
/* CCG HPI communication flags */
unsigned long flags;
#define RESET_PENDING 0
@@ -185,6 +188,10 @@ struct ucsi_ccg {
/* fw build with vendor information */
u16 fw_build;
+ bool run_isr; /* flag to call ISR routine during resume */
+ struct work_struct pm_work;
+ u16 old_fw_build;
+ u32 old_fw_version;
};
static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
@@ -212,6 +219,17 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
if (quirks && quirks->max_read_len)
max_read_len = quirks->max_read_len;
+ if (uc->old_fw_build == uc->fw_build &&
+ uc->fw_version <= uc->old_fw_version) {
+ mutex_lock(&uc->lock);
+ /* do not schedule pm_work to run ISR in
+ * ucsi_ccg_runtime_resume() after pm_runtime_get_sync()
+ * since we are already in ISR path.
+ */
+ uc->run_isr = false;
+ mutex_unlock(&uc->lock);
+ }
+
pm_runtime_get_sync(uc->dev);
while (rem_len > 0) {
msgs[1].buf = &data[len - rem_len];
@@ -254,6 +272,17 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
msgs[0].len = len + sizeof(rab);
msgs[0].buf = buf;
+ if (uc->old_fw_build == uc->fw_build &&
+ uc->fw_version <= uc->old_fw_version) {
+ mutex_lock(&uc->lock);
+ /* do not schedule pm_work to run ISR in
+ * ucsi_ccg_runtime_resume() after pm_runtime_get_sync()
+ * since we are already in ISR path.
+ */
+ uc->run_isr = false;
+ mutex_unlock(&uc->lock);
+ }
+
pm_runtime_get_sync(uc->dev);
status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (status < 0) {
@@ -383,6 +412,13 @@ static irqreturn_t ccg_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
+static void ccg_pm_workaround_work(struct work_struct *pm_work)
+{
+ struct ucsi_ccg *uc = container_of(pm_work, struct ucsi_ccg, pm_work);
+
+ ucsi_notify(uc->ucsi);
+}
+
static int get_fw_info(struct ucsi_ccg *uc)
{
int err;
@@ -392,6 +428,9 @@ static int get_fw_info(struct ucsi_ccg *uc)
if (err < 0)
return err;
+ uc->fw_version = CCG_VERSION(uc->version[FW2].app.ver) |
+ CCG_VERSION_PATCH(uc->version[FW2].app.patch);
+
err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info),
sizeof(uc->info));
if (err < 0)
@@ -740,11 +779,12 @@ static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char *fw_name,
}
/* compare input version with FWCT version */
- cur_version = le16_to_cpu(app->build) | app->patch << 16 |
- app->ver << 24;
+ cur_version = le16_to_cpu(app->build) | CCG_VERSION_PATCH(app->patch) |
+ CCG_VERSION(app->ver);
- new_version = le16_to_cpu(fw_cfg.app.build) | fw_cfg.app.patch << 16 |
- fw_cfg.app.ver << 24;
+ new_version = le16_to_cpu(fw_cfg.app.build) |
+ CCG_VERSION_PATCH(fw_cfg.app.patch) |
+ CCG_VERSION(fw_cfg.app.ver);
if (!ccg_check_vendor_version(uc, app, &fw_cfg))
goto out_release_firmware;
@@ -1084,8 +1124,16 @@ static int ucsi_ccg_probe(struct i2c_client *client,
uc->ppm.sync = ucsi_ccg_sync;
uc->dev = dev;
uc->client = client;
+ uc->run_isr = true;
mutex_init(&uc->lock);
INIT_WORK(&uc->work, ccg_update_firmware);
+ INIT_WORK(&uc->pm_work, ccg_pm_workaround_work);
+
+ /* Firmware version 3.1.10 or earlier, built for NVIDIA has known issue
+ * of missing interrupt when a device is connected for runtime resume
+ */
+ uc->old_fw_build = ('n' << 8) | 'v';
+ uc->old_fw_version = CCG_VERSION(0x31) | CCG_VERSION_PATCH(10);
/* Only fail FW flashing when FW build information is not provided */
status = device_property_read_u16(dev, "ccgx,firmware-build",
@@ -1153,6 +1201,7 @@ static int ucsi_ccg_remove(struct i2c_client *client)
{
struct ucsi_ccg *uc = i2c_get_clientdata(client);
+ cancel_work_sync(&uc->pm_work);
cancel_work_sync(&uc->work);
ucsi_unregister_ppm(uc->ucsi);
pm_runtime_disable(uc->dev);
@@ -1198,6 +1247,27 @@ static int ucsi_ccg_runtime_suspend(struct device *dev)
static int ucsi_ccg_runtime_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ucsi_ccg *uc = i2c_get_clientdata(client);
+ bool schedule = true;
+
+ /* Firmware version "old_fw_version" or earlier, built for NVIDIA has
+ * known issue of missing interrupt when a device is connected for
+ * runtime resume. Schedule a work to call ISR as a workaround.
+ */
+ if (uc->old_fw_build == uc->fw_build &&
+ uc->fw_version <= uc->old_fw_version) {
+ mutex_lock(&uc->lock);
+ if (!uc->run_isr) {
+ uc->run_isr = true;
+ schedule = false;
+ }
+ mutex_unlock(&uc->lock);
+
+ if (schedule)
+ schedule_work(&uc->pm_work);
+ }
+
return 0;
}
--
2.17.1
prev parent reply other threads:[~2019-05-17 16:39 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-05-17 16:38 [PATCH 0/4] usb: typec: ucsi: ccg: add runtime pm support Ajay Gupta
2019-05-17 16:38 ` [PATCH 1/4] i2c: nvidia-gpu: " Ajay Gupta
2019-05-19 14:47 ` Wolfram Sang
2019-05-20 17:02 ` Ajay Gupta
2019-05-20 17:17 ` Wolfram Sang
2019-05-17 16:38 ` [PATCH 2/4] usb: typec: ucsi: ccg: enable " Ajay Gupta
2019-05-17 16:38 ` [PATCH 3/4] i2c: nvidia-gpu: resume ccgx i2c client Ajay Gupta
2019-05-19 14:48 ` Wolfram Sang
2019-05-20 17:08 ` Ajay Gupta
2019-05-17 16:38 ` Ajay Gupta [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190517163818.5007-5-ajayg@nvidia.com \
--to=ajaykuee@gmail.com \
--cc=ajayg@nvidia.com \
--cc=heikki.krogerus@linux.intel.com \
--cc=linux-i2c@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).