The patch exposes function pci_dev_add_specific_reset() to allow registering PCI device specific reset methods. Signed-off-by: Gavin Shan <gwshan@xxxxxxxxxxxxxxxxxx> --- drivers/pci/quirks.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 9 ++++++++ 2 files changed, 73 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 37f4c5d..ef811ec 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3534,6 +3534,70 @@ static struct pci_dev_reset_method pci_dev_reset_methods[] = { } }; +static bool pci_dev_reset_match(struct pci_dev_reset_method *m, + u16 vendor, u16 device, + int (*reset)(struct pci_dev *, int)) +{ + if (m->vendor != (u16)PCI_ANY_ID && + vendor != (u16)PCI_ANY_ID && + m->vendor != vendor) + return false; + + if (m->device != (u16)PCI_ANY_ID && + device != (u16)PCI_ANY_ID && + m->device != device) + return false; + + if (m->reset != reset) + return false; + + return true; +} + +int pci_dev_add_specific_reset(u16 vendor, u16 device, + int (*reset)(struct pci_dev *, int)) +{ + struct pci_dev_reset_method *m, *method; + int i; + + /* Always expect valid handler */ + if (!reset) + return -EINVAL; + + method = kzalloc(sizeof(*method), GFP_KERNEL); + if (!method) + return -ENOMEM; + + /* Check if we have conflicting method */ + mutex_lock(&pci_dev_reset_mutex); + if (!pci_dev_reset_populated) { + for (i = 0; i < ARRAY_SIZE(pci_dev_reset_methods); i++) { + m = &pci_dev_reset_methods[i]; + if (pci_dev_reset_match(m, vendor, device, reset)) + goto found; + } + } else { + list_for_each_entry(m, &pci_dev_reset_list, node) { + if (pci_dev_reset_match(m, vendor, device, reset)) + goto found; + } + } + + /* Populate it */ + method->vendor = vendor; + method->device = device; + method->reset = reset; + INIT_LIST_HEAD(&method->node); + list_add_tail(&method->node, &pci_dev_reset_list); + mutex_unlock(&pci_dev_reset_mutex); + return 0; +found: + mutex_unlock(&pci_dev_reset_mutex); + kfree(method); + return -EEXIST; +} +EXPORT_SYMBOL(pci_dev_add_specific_reset); + static void pci_dev_populate_reset(void) { struct pci_dev_reset_method *m; diff --git a/include/linux/pci.h b/include/linux/pci.h index 211e9da..b98be1a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1605,6 +1605,8 @@ enum pci_fixup_pass { #ifdef CONFIG_PCI_QUIRKS void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); +int pci_dev_add_specific_reset(u16 vendor, u16 device, + int (*reset)(struct pci_dev *, int)); int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags); void pci_dev_specific_enable_acs(struct pci_dev *dev); #else @@ -1615,6 +1617,13 @@ static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev, { return -ENOTTY; } + +int pci_dev_add_specific_reset(u16 vendor, u16 device, + int (*reset)(struct pci_dev *, int)) +{ + return -ENOTTY; +} + static inline void pci_dev_specific_enable_acs(struct pci_dev *dev) { } #endif -- 1.8.3.2 -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html