diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/hcd.c | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/inode.c | 21 | ||||
-rw-r--r-- | drivers/usb/host/ehci-pci.c | 4 | ||||
-rw-r--r-- | drivers/usb/host/hwa-hc.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ohci-pci.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/pci-quirks.c | 45 | ||||
-rw-r--r-- | drivers/usb/host/pci-quirks.h | 2 | ||||
-rw-r--r-- | drivers/usb/host/xhci-pci.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 3 | ||||
-rw-r--r-- | drivers/usb/misc/usb251xb.c | 15 | ||||
-rw-r--r-- | drivers/usb/storage/scsiglue.c | 11 | ||||
-rw-r--r-- | drivers/usb/typec/ucsi/ucsi.c | 10 | ||||
-rw-r--r-- | drivers/usb/typec/ucsi/ucsi.h | 1 | ||||
-rw-r--r-- | drivers/usb/typec/ucsi/ucsi_ccg.c | 118 |
14 files changed, 195 insertions, 45 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 88533938ce19..9320787ac2e6 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -3052,8 +3052,8 @@ int usb_hcd_setup_local_mem(struct usb_hcd *hcd, phys_addr_t phys_addr, local_mem = devm_memremap(hcd->self.sysdev, phys_addr, size, MEMREMAP_WC); - if (!local_mem) - return -ENOMEM; + if (IS_ERR(local_mem)) + return PTR_ERR(local_mem); /* * Here we pass a dma_addr_t but the arg type is a phys_addr_t. diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 249277d0e53f..b47938dff1a2 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -12,6 +12,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> +#include <linux/fs_context.h> #include <linux/pagemap.h> #include <linux/uts.h> #include <linux/wait.h> @@ -1990,7 +1991,7 @@ static const struct super_operations gadget_fs_operations = { }; static int -gadgetfs_fill_super (struct super_block *sb, void *opts, int silent) +gadgetfs_fill_super (struct super_block *sb, struct fs_context *fc) { struct inode *inode; struct dev_data *dev; @@ -2044,11 +2045,19 @@ Enomem: } /* "mount -t gadgetfs path /dev/gadget" ends up here */ -static struct dentry * -gadgetfs_mount (struct file_system_type *t, int flags, - const char *path, void *opts) +static int gadgetfs_get_tree(struct fs_context *fc) { - return mount_single (t, flags, opts, gadgetfs_fill_super); + return get_tree_single(fc, gadgetfs_fill_super); +} + +static const struct fs_context_operations gadgetfs_context_ops = { + .get_tree = gadgetfs_get_tree, +}; + +static int gadgetfs_init_fs_context(struct fs_context *fc) +{ + fc->ops = &gadgetfs_context_ops; + return 0; } static void @@ -2068,7 +2077,7 @@ gadgetfs_kill_sb (struct super_block *sb) static struct file_system_type gadgetfs_type = { .owner = THIS_MODULE, .name = shortname, - .mount = gadgetfs_mount, + .init_fs_context = gadgetfs_init_fs_context, .kill_sb = gadgetfs_kill_sb, }; MODULE_ALIAS_FS("gadgetfs"); diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index fe9422d3bcdc..b0882c13a1d1 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -149,7 +149,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd) break; case PCI_VENDOR_ID_AMD: /* AMD PLL quirk */ - if (usb_amd_find_chipset_info()) + if (usb_amd_quirk_pll_check()) ehci->amd_pll_fix = 1; /* AMD8111 EHCI doesn't work, according to AMD errata */ if (pdev->device == 0x7463) { @@ -186,7 +186,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd) break; case PCI_VENDOR_ID_ATI: /* AMD PLL quirk */ - if (usb_amd_find_chipset_info()) + if (usb_amd_quirk_pll_check()) ehci->amd_pll_fix = 1; /* diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index 09a8ebd95588..6968b9f2b76b 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/usb/host/hwa-hc.c @@ -159,7 +159,7 @@ out: return result; error_set_cluster_id: - wusb_cluster_id_put(wusbhc->cluster_id); + wusb_cluster_id_put(addr); error_cluster_id_get: goto out; diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index a033f7d855e0..f4e13a3fddee 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -152,7 +152,7 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci(hcd); - if (usb_amd_find_chipset_info()) + if (usb_amd_quirk_pll_check()) ohci->flags |= OHCI_QUIRK_AMD_PLL; /* SB800 needs pre-fetch fix */ diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 3ce71cbfbb58..f6d04491df60 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -132,7 +132,7 @@ static struct amd_chipset_info { struct amd_chipset_type sb_type; int isoc_reqs; int probe_count; - int probe_result; + bool need_pll_quirk; } amd_chipset; static DEFINE_SPINLOCK(amd_lock); @@ -201,11 +201,11 @@ void sb800_prefetch(struct device *dev, int on) } EXPORT_SYMBOL_GPL(sb800_prefetch); -int usb_amd_find_chipset_info(void) +static void usb_amd_find_chipset_info(void) { unsigned long flags; struct amd_chipset_info info; - int ret; + info.need_pll_quirk = 0; spin_lock_irqsave(&amd_lock, flags); @@ -213,27 +213,34 @@ int usb_amd_find_chipset_info(void) if (amd_chipset.probe_count > 0) { amd_chipset.probe_count++; spin_unlock_irqrestore(&amd_lock, flags); - return amd_chipset.probe_result; + return; } memset(&info, 0, sizeof(info)); spin_unlock_irqrestore(&amd_lock, flags); if (!amd_chipset_sb_type_init(&info)) { - ret = 0; goto commit; } - /* Below chipset generations needn't enable AMD PLL quirk */ - if (info.sb_type.gen == AMD_CHIPSET_UNKNOWN || - info.sb_type.gen == AMD_CHIPSET_SB600 || - info.sb_type.gen == AMD_CHIPSET_YANGTZE || - (info.sb_type.gen == AMD_CHIPSET_SB700 && - info.sb_type.rev > 0x3b)) { + switch (info.sb_type.gen) { + case AMD_CHIPSET_SB700: + info.need_pll_quirk = info.sb_type.rev <= 0x3B; + break; + case AMD_CHIPSET_SB800: + case AMD_CHIPSET_HUDSON2: + case AMD_CHIPSET_BOLTON: + info.need_pll_quirk = 1; + break; + default: + info.need_pll_quirk = 0; + break; + } + + if (!info.need_pll_quirk) { if (info.smbus_dev) { pci_dev_put(info.smbus_dev); info.smbus_dev = NULL; } - ret = 0; goto commit; } @@ -252,7 +259,6 @@ int usb_amd_find_chipset_info(void) } } - ret = info.probe_result = 1; printk(KERN_DEBUG "QUIRK: Enable AMD PLL fix\n"); commit: @@ -263,7 +269,6 @@ commit: /* Mark that we where here */ amd_chipset.probe_count++; - ret = amd_chipset.probe_result; spin_unlock_irqrestore(&amd_lock, flags); @@ -276,10 +281,7 @@ commit: amd_chipset = info; spin_unlock_irqrestore(&amd_lock, flags); } - - return ret; } -EXPORT_SYMBOL_GPL(usb_amd_find_chipset_info); int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev) { @@ -315,6 +317,13 @@ bool usb_amd_prefetch_quirk(void) } EXPORT_SYMBOL_GPL(usb_amd_prefetch_quirk); +bool usb_amd_quirk_pll_check(void) +{ + usb_amd_find_chipset_info(); + return amd_chipset.need_pll_quirk; +} +EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_check); + /* * The hardware normally enables the A-link power management feature, which * lets the system lower the power consumption in idle states. @@ -520,7 +529,7 @@ void usb_amd_dev_put(void) amd_chipset.nb_type = 0; memset(&amd_chipset.sb_type, 0, sizeof(amd_chipset.sb_type)); amd_chipset.isoc_reqs = 0; - amd_chipset.probe_result = 0; + amd_chipset.need_pll_quirk = 0; spin_unlock_irqrestore(&amd_lock, flags); diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h index 63c633077d9e..e729de21fad7 100644 --- a/drivers/usb/host/pci-quirks.h +++ b/drivers/usb/host/pci-quirks.h @@ -5,11 +5,11 @@ #ifdef CONFIG_USB_PCI void uhci_reset_hc(struct pci_dev *pdev, unsigned long base); int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base); -int usb_amd_find_chipset_info(void); int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev); bool usb_amd_hang_symptom_quirk(void); bool usb_amd_prefetch_quirk(void); void usb_amd_dev_put(void); +bool usb_amd_quirk_pll_check(void); void usb_amd_quirk_pll_disable(void); void usb_amd_quirk_pll_enable(void); void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index c2fe218e051f..1e0236e90687 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -130,7 +130,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_AMD_0x96_HOST; /* AMD PLL quirk */ - if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info()) + if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_quirk_pll_check()) xhci->quirks |= XHCI_AMD_PLL_FIX; if (pdev->vendor == PCI_VENDOR_ID_AMD && diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 7a264962a1a9..f5c41448d067 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -2175,7 +2175,8 @@ static inline bool xhci_urb_suitable_for_idt(struct urb *urb) if (!usb_endpoint_xfer_isoc(&urb->ep->desc) && usb_urb_dir_out(urb) && usb_endpoint_maxp(&urb->ep->desc) >= TRB_IDT_MAX_SIZE && urb->transfer_buffer_length <= TRB_IDT_MAX_SIZE && - !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) + !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) && + !urb->num_sgs) return true; return false; diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index 4d6ae3795a88..6ca9111d150a 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -375,7 +375,8 @@ out_err: #ifdef CONFIG_OF static void usb251xb_get_ports_field(struct usb251xb *hub, - const char *prop_name, u8 port_cnt, u8 *fld) + const char *prop_name, u8 port_cnt, + bool ds_only, u8 *fld) { struct device *dev = hub->dev; struct property *prop; @@ -383,7 +384,7 @@ static void usb251xb_get_ports_field(struct usb251xb *hub, u32 port; of_property_for_each_u32(dev->of_node, prop_name, prop, p, port) { - if ((port >= 1) && (port <= port_cnt)) + if ((port >= ds_only ? 1 : 0) && (port <= port_cnt)) *fld |= BIT(port); else dev_warn(dev, "port %u doesn't exist\n", port); @@ -501,15 +502,15 @@ static int usb251xb_get_ofdata(struct usb251xb *hub, hub->non_rem_dev = USB251XB_DEF_NON_REMOVABLE_DEVICES; usb251xb_get_ports_field(hub, "non-removable-ports", data->port_cnt, - &hub->non_rem_dev); + true, &hub->non_rem_dev); hub->port_disable_sp = USB251XB_DEF_PORT_DISABLE_SELF; usb251xb_get_ports_field(hub, "sp-disabled-ports", data->port_cnt, - &hub->port_disable_sp); + true, &hub->port_disable_sp); hub->port_disable_bp = USB251XB_DEF_PORT_DISABLE_BUS; usb251xb_get_ports_field(hub, "bp-disabled-ports", data->port_cnt, - &hub->port_disable_bp); + true, &hub->port_disable_bp); hub->max_power_sp = USB251XB_DEF_MAX_POWER_SELF; if (!of_property_read_u32(np, "sp-max-total-current-microamp", @@ -573,9 +574,7 @@ static int usb251xb_get_ofdata(struct usb251xb *hub, */ hub->port_swap = USB251XB_DEF_PORT_SWAP; usb251xb_get_ports_field(hub, "swap-dx-lanes", data->port_cnt, - &hub->port_swap); - if (of_get_property(np, "swap-us-lanes", NULL)) - hub->port_swap |= BIT(0); + false, &hub->port_swap); /* The following parameters are currently not exposed to devicetree, but * may be as soon as needed. diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 30790240aec6..05b80211290d 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -28,6 +28,8 @@ * status of a command. */ +#include <linux/blkdev.h> +#include <linux/dma-mapping.h> #include <linux/module.h> #include <linux/mutex.h> @@ -99,6 +101,7 @@ static int slave_alloc (struct scsi_device *sdev) static int slave_configure(struct scsi_device *sdev) { struct us_data *us = host_to_us(sdev->host); + struct device *dev = us->pusb_dev->bus->sysdev; /* * Many devices have trouble transferring more than 32KB at a time, @@ -129,6 +132,14 @@ static int slave_configure(struct scsi_device *sdev) } /* + * The max_hw_sectors should be up to maximum size of a mapping for + * the device. Otherwise, a DMA API might fail on swiotlb environment. + */ + blk_queue_max_hw_sectors(sdev->request_queue, + min_t(size_t, queue_max_hw_sectors(sdev->request_queue), + dma_max_mapping_size(dev) >> SECTOR_SHIFT)); + + /* * Some USB host controllers can't do DMA; they have to use PIO. * They indicate this by setting their dma_mask to NULL. For * such controllers we need to make sure the block layer sets diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 7850b851cecd..ba288b964dc8 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -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) diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 1e2981aef629..de87d0b8319d 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -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 * diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index bf63074675fc..f7a79a23ebed 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -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, |