Merge branch 'i2c/for-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: "New stuff from the I2C world: - in the core, getting irqs from ACPI is now similar to OF - new driver for MediaTek MT7621/7628/7688 SoCs - bcm2835, i801, and tegra drivers got some more attention - GPIO API cleanups - cleanups in the core headers - lots of usual driver updates" * 'i2c/for-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (74 commits) i2c: mt7621: Fix platform_no_drv_owner.cocci warnings i2c: cpm: remove casting dma_alloc dt-bindings: i2c: sun6i-p2wi: Fix the binding example dt-bindings: i2c: mv64xxx: Fix the example compatible i2c: i801: Documentation update i2c: i801: Add support for Intel Tiger Lake i2c: i801: Fix PCI ID sorting dt-bindings: i2c-stm32: document optional dmas i2c: i2c-stm32f7: Add I2C_SMBUS_I2C_BLOCK_DATA support i2c: core: Tidy up handling of init_irq i2c: core: Move ACPI gpio IRQ handling into i2c_acpi_get_irq i2c: core: Move ACPI IRQ handling to probe time i2c: acpi: Factor out getting the IRQ from ACPI i2c: acpi: Use available IRQ helper functions i2c: core: Allow whole core to use i2c_dev_irq_from_resources eeprom: at24: modify a comment referring to platform data dt-bindings: i2c: omap: Add new compatible for J721E SoCs dt-bindings: i2c: mv64xxx: Add YAML schemas dt-bindings: i2c: sun6i-p2wi: Add YAML schemas i2c: mt7621: Add MediaTek MT7621/7628/7688 I2C driver ...
This commit is contained in:
@@ -206,7 +206,17 @@ int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ucsi_send_command);
|
||||
|
||||
int ucsi_resume(struct ucsi *ucsi)
|
||||
{
|
||||
struct ucsi_control ctrl;
|
||||
|
||||
/* Restore UCSI notification enable mask after system resume */
|
||||
UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_ALL);
|
||||
return ucsi_send_command(ucsi, &ctrl, NULL, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ucsi_resume);
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
void ucsi_altmode_update_active(struct ucsi_connector *con)
|
||||
|
@@ -430,6 +430,7 @@ int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
|
||||
void *retval, size_t size);
|
||||
|
||||
void ucsi_altmode_update_active(struct ucsi_connector *con);
|
||||
int ucsi_resume(struct ucsi *ucsi);
|
||||
|
||||
#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
|
||||
struct typec_altmode *
|
||||
|
@@ -14,6 +14,8 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include "ucsi.h"
|
||||
@@ -107,12 +109,21 @@ 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)
|
||||
#define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT)
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Firmware version 3.1.10 or earlier, built for NVIDIA has known issue
|
||||
* of missing interrupt when a device is connected for runtime resume
|
||||
*/
|
||||
#define CCG_FW_BUILD_NVIDIA (('n' << 8) | 'v')
|
||||
#define CCG_OLD_FW_VERSION (CCG_VERSION(0x31) | CCG_VERSION_PATCH(10))
|
||||
|
||||
struct version_info {
|
||||
struct version_format base;
|
||||
struct version_format app;
|
||||
@@ -170,6 +181,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
|
||||
@@ -183,6 +195,8 @@ 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;
|
||||
};
|
||||
|
||||
static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
|
||||
@@ -210,6 +224,19 @@ 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->fw_build == CCG_FW_BUILD_NVIDIA &&
|
||||
uc->fw_version <= CCG_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];
|
||||
rlen = min_t(u16, rem_len, max_read_len);
|
||||
@@ -218,12 +245,14 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
|
||||
status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (status < 0) {
|
||||
dev_err(uc->dev, "i2c_transfer failed %d\n", status);
|
||||
pm_runtime_put_sync(uc->dev);
|
||||
return status;
|
||||
}
|
||||
rab += rlen;
|
||||
rem_len -= rlen;
|
||||
}
|
||||
|
||||
pm_runtime_put_sync(uc->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -249,13 +278,28 @@ 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->fw_build == CCG_FW_BUILD_NVIDIA &&
|
||||
uc->fw_version <= CCG_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) {
|
||||
dev_err(uc->dev, "i2c_transfer failed %d\n", status);
|
||||
pm_runtime_put_sync(uc->dev);
|
||||
kfree(buf);
|
||||
return status;
|
||||
}
|
||||
|
||||
pm_runtime_put_sync(uc->dev);
|
||||
kfree(buf);
|
||||
return 0;
|
||||
}
|
||||
@@ -375,6 +419,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;
|
||||
@@ -384,6 +435,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)
|
||||
@@ -732,11 +786,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;
|
||||
@@ -1078,8 +1133,10 @@ 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);
|
||||
|
||||
/* Only fail FW flashing when FW build information is not provided */
|
||||
status = device_property_read_u16(dev, "ccgx,firmware-build",
|
||||
@@ -1136,6 +1193,10 @@ static int ucsi_ccg_probe(struct i2c_client *client,
|
||||
if (status)
|
||||
dev_err(uc->dev, "cannot create sysfs group: %d\n", status);
|
||||
|
||||
pm_runtime_set_active(uc->dev);
|
||||
pm_runtime_enable(uc->dev);
|
||||
pm_runtime_idle(uc->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1143,8 +1204,10 @@ 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);
|
||||
free_irq(uc->irq, uc);
|
||||
sysfs_remove_group(&uc->dev->kobj, &ucsi_ccg_attr_group);
|
||||
|
||||
@@ -1157,9 +1220,56 @@ static const struct i2c_device_id ucsi_ccg_device_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id);
|
||||
|
||||
static int ucsi_ccg_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ucsi_ccg *uc = i2c_get_clientdata(client);
|
||||
|
||||
return ucsi_resume(uc->ucsi);
|
||||
}
|
||||
|
||||
static int ucsi_ccg_runtime_suspend(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 3.1.10 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->fw_build == CCG_FW_BUILD_NVIDIA &&
|
||||
uc->fw_version <= CCG_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;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops ucsi_ccg_pm = {
|
||||
.resume = ucsi_ccg_resume,
|
||||
.runtime_suspend = ucsi_ccg_runtime_suspend,
|
||||
.runtime_resume = ucsi_ccg_runtime_resume,
|
||||
};
|
||||
|
||||
static struct i2c_driver ucsi_ccg_driver = {
|
||||
.driver = {
|
||||
.name = "ucsi_ccg",
|
||||
.pm = &ucsi_ccg_pm,
|
||||
},
|
||||
.probe = ucsi_ccg_probe,
|
||||
.remove = ucsi_ccg_remove,
|
||||
|
Reference in New Issue
Block a user