diff options
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/Kconfig | 6 | ||||
-rw-r--r-- | drivers/pci/iov.c | 48 | ||||
-rw-r--r-- | drivers/pci/p2pdma.c | 14 | ||||
-rw-r--r-- | drivers/pci/pci-driver.c | 27 | ||||
-rw-r--r-- | drivers/pci/pci.h | 2 | ||||
-rw-r--r-- | drivers/pci/pcie/aspm.c | 6 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv.h | 16 | ||||
-rw-r--r-- | drivers/pci/quirks.c | 24 | ||||
-rw-r--r-- | drivers/pci/switch/switchtec.c | 154 |
9 files changed, 229 insertions, 68 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 2dcc30429e8b..ffe7895aa880 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -73,9 +73,9 @@ config PCI_PF_STUB depends on PCI_IOV help Say Y or M here if you want to enable support for devices that - require SR-IOV support, while at the same time the PF itself is - not providing any actual services on the host itself such as - storage or networking. + require SR-IOV support, while at the same time the PF (Physical + Function) itself is not providing any actual services on the + host itself such as storage or networking. When in doubt, say N. diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 9616eca3182f..3aa115ed3a65 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -252,6 +252,27 @@ int __weak pcibios_sriov_disable(struct pci_dev *pdev) return 0; } +static int sriov_add_vfs(struct pci_dev *dev, u16 num_vfs) +{ + unsigned int i; + int rc; + + if (dev->no_vf_scan) + return 0; + + for (i = 0; i < num_vfs; i++) { + rc = pci_iov_add_virtfn(dev, i); + if (rc) + goto failed; + } + return 0; +failed: + while (i--) + pci_iov_remove_virtfn(dev, i); + + return rc; +} + static int sriov_enable(struct pci_dev *dev, int nr_virtfn) { int rc; @@ -337,21 +358,15 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) msleep(100); pci_cfg_access_unlock(dev); - for (i = 0; i < initial; i++) { - rc = pci_iov_add_virtfn(dev, i); - if (rc) - goto failed; - } + rc = sriov_add_vfs(dev, initial); + if (rc) + goto err_pcibios; kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); iov->num_VFs = nr_virtfn; return 0; -failed: - while (i--) - pci_iov_remove_virtfn(dev, i); - err_pcibios: iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); pci_cfg_access_lock(dev); @@ -368,17 +383,26 @@ err_pcibios: return rc; } -static void sriov_disable(struct pci_dev *dev) +static void sriov_del_vfs(struct pci_dev *dev) { - int i; struct pci_sriov *iov = dev->sriov; + int i; - if (!iov->num_VFs) + if (dev->no_vf_scan) return; for (i = 0; i < iov->num_VFs; i++) pci_iov_remove_virtfn(dev, i); +} + +static void sriov_disable(struct pci_dev *dev) +{ + struct pci_sriov *iov = dev->sriov; + + if (!iov->num_VFs) + return; + sriov_del_vfs(dev); iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); pci_cfg_access_lock(dev); pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index ae3c5b25dcc7..c25a69fc4fc7 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -422,7 +422,7 @@ static int upstream_bridge_distance_warn(struct pci_dev *provider, * * Returns -1 if any of the clients are not compatible (behind the same * root port as the provider), otherwise returns a positive number where - * a lower number is the preferrable choice. (If there's one client + * a lower number is the preferable choice. (If there's one client * that's the same as the provider it will return 0, which is best choice). * * For now, "compatible" means the provider and the clients are all behind @@ -493,7 +493,7 @@ EXPORT_SYMBOL_GPL(pci_has_p2pmem); * @num_clients: number of client devices in the list * * If multiple devices are behind the same switch, the one "closest" to the - * client devices in use will be chosen first. (So if one of the providers are + * client devices in use will be chosen first. (So if one of the providers is * the same as one of the clients, that provider will be used ahead of any * other providers that are unrelated). If multiple providers are an equal * distance away, one will be chosen at random. @@ -580,7 +580,7 @@ EXPORT_SYMBOL_GPL(pci_alloc_p2pmem); * pci_free_p2pmem - free peer-to-peer DMA memory * @pdev: the device the memory was allocated from * @addr: address of the memory that was allocated - * @size: number of bytes that was allocated + * @size: number of bytes that were allocated */ void pci_free_p2pmem(struct pci_dev *pdev, void *addr, size_t size) { @@ -617,7 +617,7 @@ EXPORT_SYMBOL_GPL(pci_p2pmem_virt_to_bus); * @nents: the number of SG entries in the list * @length: number of bytes to allocate * - * Returns 0 on success + * Return: %NULL on error or &struct scatterlist pointer and @nents on success */ struct scatterlist *pci_p2pmem_alloc_sgl(struct pci_dev *pdev, unsigned int *nents, u32 length) @@ -673,7 +673,7 @@ EXPORT_SYMBOL_GPL(pci_p2pmem_free_sgl); * * Published memory can be used by other PCI device drivers for * peer-2-peer DMA operations. Non-published memory is reserved for - * exlusive use of the device driver that registers the peer-to-peer + * exclusive use of the device driver that registers the peer-to-peer * memory. */ void pci_p2pmem_publish(struct pci_dev *pdev, bool publish) @@ -733,7 +733,7 @@ EXPORT_SYMBOL_GPL(pci_p2pdma_map_sg); * @use_p2pdma: returns whether to enable p2pdma or not * * Parses an attribute value to decide whether to enable p2pdma. - * The value can select a PCI device (using it's full BDF device + * The value can select a PCI device (using its full BDF device * name) or a boolean (in any format strtobool() accepts). A false * value disables p2pdma, a true value expects the caller * to automatically find a compatible device and specifying a PCI device @@ -784,7 +784,7 @@ EXPORT_SYMBOL_GPL(pci_p2pdma_enable_store); * whether p2pdma is enabled * @page: contents of the stored value * @p2p_dev: the selected p2p device (NULL if no device is selected) - * @use_p2pdma: whether p2pdme has been enabled + * @use_p2pdma: whether p2pdma has been enabled * * Attributes that use pci_p2pdma_enable_store() should use this function * to show the value of the attribute. diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index bef17c3fca67..33f3f475e5c6 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1251,30 +1251,29 @@ static int pci_pm_runtime_suspend(struct device *dev) return 0; } - if (!pm || !pm->runtime_suspend) - return -ENOSYS; - pci_dev->state_saved = false; - error = pm->runtime_suspend(dev); - if (error) { + if (pm && pm->runtime_suspend) { + error = pm->runtime_suspend(dev); /* * -EBUSY and -EAGAIN is used to request the runtime PM core * to schedule a new suspend, so log the event only with debug * log level. */ - if (error == -EBUSY || error == -EAGAIN) + if (error == -EBUSY || error == -EAGAIN) { dev_dbg(dev, "can't suspend now (%pf returned %d)\n", pm->runtime_suspend, error); - else + return error; + } else if (error) { dev_err(dev, "can't suspend (%pf returned %d)\n", pm->runtime_suspend, error); - - return error; + return error; + } } pci_fixup_device(pci_fixup_suspend, pci_dev); - if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0 + if (pm && pm->runtime_suspend + && !pci_dev->state_saved && pci_dev->current_state != PCI_D0 && pci_dev->current_state != PCI_UNKNOWN) { WARN_ONCE(pci_dev->current_state != prev, "PCI PM: State of device not saved by %pF\n", @@ -1292,7 +1291,7 @@ static int pci_pm_runtime_suspend(struct device *dev) static int pci_pm_runtime_resume(struct device *dev) { - int rc; + int rc = 0; struct pci_dev *pci_dev = to_pci_dev(dev); const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; @@ -1306,14 +1305,12 @@ static int pci_pm_runtime_resume(struct device *dev) if (!pci_dev->driver) return 0; - if (!pm || !pm->runtime_resume) - return -ENOSYS; - pci_fixup_device(pci_fixup_resume_early, pci_dev); pci_enable_wake(pci_dev, PCI_D0, false); pci_fixup_device(pci_fixup_resume, pci_dev); - rc = pm->runtime_resume(dev); + if (pm && pm->runtime_resume) + rc = pm->runtime_resume(dev); pci_dev->runtime_d3cold = false; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 662b7457db23..224d88634115 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -2,6 +2,8 @@ #ifndef DRIVERS_PCI_H #define DRIVERS_PCI_H +#include <linux/pci.h> + #define PCI_FIND_CAP_TTL 48 #define PCI_VSEC_ID_INTEL_TBT 0x1234 /* Thunderbolt */ diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index dcb29cb76dc6..2330ebf6df72 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -53,8 +53,6 @@ struct pcie_link_state { struct pcie_link_state *root; /* pointer to the root port link */ struct pcie_link_state *parent; /* pointer to the parent Link state */ struct list_head sibling; /* node in link_list */ - struct list_head children; /* list of child link states */ - struct list_head link; /* node in parent's children list */ /* ASPM state */ u32 aspm_support:7; /* Supported ASPM state */ @@ -850,8 +848,6 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev) return NULL; INIT_LIST_HEAD(&link->sibling); - INIT_LIST_HEAD(&link->children); - INIT_LIST_HEAD(&link->link); link->pdev = pdev; link->downstream = pci_function_0(pdev->subordinate); @@ -877,7 +873,6 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev) link->parent = parent; link->root = link->parent->root; - list_add(&link->link, &parent->children); } list_add(&link->sibling, &link_list); @@ -1001,7 +996,6 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) /* All functions are removed, so just disable ASPM for the link */ pcie_config_aspm_link(link, 0); list_del(&link->sibling); - list_del(&link->link); /* Clock PM is for endpoint device */ free_link_state(link); diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index e495f04394d0..fbbf00b0992e 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -71,19 +71,19 @@ static inline void *get_service_data(struct pcie_device *dev) struct pcie_port_service_driver { const char *name; - int (*probe) (struct pcie_device *dev); - void (*remove) (struct pcie_device *dev); - int (*suspend) (struct pcie_device *dev); - int (*resume_noirq) (struct pcie_device *dev); - int (*resume) (struct pcie_device *dev); - int (*runtime_suspend) (struct pcie_device *dev); - int (*runtime_resume) (struct pcie_device *dev); + int (*probe)(struct pcie_device *dev); + void (*remove)(struct pcie_device *dev); + int (*suspend)(struct pcie_device *dev); + int (*resume_noirq)(struct pcie_device *dev); + int (*resume)(struct pcie_device *dev); + int (*runtime_suspend)(struct pcie_device *dev); + int (*runtime_resume)(struct pcie_device *dev); /* Device driver may resume normal operations */ void (*error_resume)(struct pci_dev *dev); /* Link Reset Capability - AER service driver specific */ - pci_ers_result_t (*reset_link) (struct pci_dev *dev); + pci_ers_result_t (*reset_link)(struct pci_dev *dev); int port_type; /* Type of the port this driver can handle */ u32 service; /* Port service this device represents */ diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 4700d24e5d55..b0a413f3f7ca 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -619,6 +619,30 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB, quirk_amd_nl_class); /* + * Synopsys USB 3.x host HAPS platform has a class code of + * PCI_CLASS_SERIAL_USB_XHCI, and xhci driver can claim it. However, these + * devices should use dwc3-haps driver. Change these devices' class code to + * PCI_CLASS_SERIAL_USB_DEVICE to prevent the xhci-pci driver from claiming + * them. + */ +static void quirk_synopsys_haps(struct pci_dev *pdev) +{ + u32 class = pdev->class; + + switch (pdev->device) { + case PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3: + case PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI: + case PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31: + pdev->class = PCI_CLASS_SERIAL_USB_DEVICE; + pci_info(pdev, "PCI class overridden (%#08x -> %#08x) so dwc3 driver can claim this instead of xhci\n", + class, pdev->class); + break; + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SYNOPSYS, PCI_ANY_ID, + quirk_synopsys_haps); + +/* * Let's make the southbridge information explicit instead of having to * worry about people probing the ACPI areas, for example.. (Yes, it * happens, and if you read the wrong ACPI register it will put the machine diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 54a8b30dda38..6c5536d3d42a 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -13,7 +13,7 @@ #include <linux/uaccess.h> #include <linux/poll.h> #include <linux/wait.h> - +#include <linux/io-64-nonatomic-lo-hi.h> #include <linux/nospec.h> MODULE_DESCRIPTION("Microsemi Switchtec(tm) PCIe Management Driver"); @@ -25,6 +25,11 @@ static int max_devices = 16; module_param(max_devices, int, 0644); MODULE_PARM_DESC(max_devices, "max number of switchtec device instances"); +static bool use_dma_mrpc = 1; +module_param(use_dma_mrpc, bool, 0644); +MODULE_PARM_DESC(use_dma_mrpc, + "Enable the use of the DMA MRPC feature"); + static dev_t switchtec_devt; static DEFINE_IDA(switchtec_minor_ida); @@ -113,6 +118,19 @@ static void stuser_set_state(struct switchtec_user *stuser, static void mrpc_complete_cmd(struct switchtec_dev *stdev); +static void flush_wc_buf(struct switchtec_dev *stdev) +{ + struct ntb_dbmsg_regs __iomem *mmio_dbmsg; + + /* + * odb (outbound doorbell) register is processed by low latency + * hardware and w/o side effect + */ + mmio_dbmsg = (void __iomem *)stdev->mmio_ntb + + SWITCHTEC_NTB_REG_DBMSG_OFFSET; + ioread32(&mmio_dbmsg->odb); +} + static void mrpc_cmd_submit(struct switchtec_dev *stdev) { /* requires the mrpc_mutex to already be held when called */ @@ -128,16 +146,18 @@ static void mrpc_cmd_submit(struct switchtec_dev *stdev) stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user, list); + if (stdev->dma_mrpc) { + stdev->dma_mrpc->status = SWITCHTEC_MRPC_STATUS_INPROGRESS; + memset(stdev->dma_mrpc->data, 0xFF, SWITCHTEC_MRPC_PAYLOAD_SIZE); + } + stuser_set_state(stuser, MRPC_RUNNING); stdev->mrpc_busy = 1; memcpy_toio(&stdev->mmio_mrpc->input_data, stuser->data, stuser->data_len); + flush_wc_buf(stdev); iowrite32(stuser->cmd, &stdev->mmio_mrpc->cmd); - stuser->status = ioread32(&stdev->mmio_mrpc->status); - if (stuser->status != SWITCHTEC_MRPC_STATUS_INPROGRESS) - mrpc_complete_cmd(stdev); - schedule_delayed_work(&stdev->mrpc_timeout, msecs_to_jiffies(500)); } @@ -170,7 +190,11 @@ static void mrpc_complete_cmd(struct switchtec_dev *stdev) stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user, list); - stuser->status = ioread32(&stdev->mmio_mrpc->status); + if (stdev->dma_mrpc) + stuser->status = stdev->dma_mrpc->status; + else + stuser->status = ioread32(&stdev->mmio_mrpc->status); + if (stuser->status == SWITCHTEC_MRPC_STATUS_INPROGRESS) return; @@ -180,13 +204,19 @@ static void mrpc_complete_cmd(struct switchtec_dev *stdev) if (stuser->status != SWITCHTEC_MRPC_STATUS_DONE) goto out; - stuser->return_code = ioread32(&stdev->mmio_mrpc->ret_value); + if (stdev->dma_mrpc) + stuser->return_code = stdev->dma_mrpc->rtn_code; + else + stuser->return_code = ioread32(&stdev->mmio_mrpc->ret_value); if (stuser->return_code != 0) goto out; - memcpy_fromio(stuser->data, &stdev->mmio_mrpc->output_data, - stuser->read_len); - + if (stdev->dma_mrpc) + memcpy(stuser->data, &stdev->dma_mrpc->data, + stuser->read_len); + else + memcpy_fromio(stuser->data, &stdev->mmio_mrpc->output_data, + stuser->read_len); out: complete_all(&stuser->comp); list_del_init(&stuser->list); @@ -221,7 +251,10 @@ static void mrpc_timeout_work(struct work_struct *work) mutex_lock(&stdev->mrpc_mutex); - status = ioread32(&stdev->mmio_mrpc->status); + if (stdev->dma_mrpc) + status = stdev->dma_mrpc->status; + else + status = ioread32(&stdev->mmio_mrpc->status); if (status == SWITCHTEC_MRPC_STATUS_INPROGRESS) { schedule_delayed_work(&stdev->mrpc_timeout, msecs_to_jiffies(500)); @@ -229,7 +262,6 @@ static void mrpc_timeout_work(struct work_struct *work) } mrpc_complete_cmd(stdev); - out: mutex_unlock(&stdev->mrpc_mutex); } @@ -800,6 +832,7 @@ static int ioctl_event_ctl(struct switchtec_dev *stdev, { int ret; int nr_idxs; + unsigned int event_flags; struct switchtec_ioctl_event_ctl ctl; if (copy_from_user(&ctl, uctl, sizeof(ctl))) @@ -821,7 +854,9 @@ static int ioctl_event_ctl(struct switchtec_dev *stdev, else return -EINVAL; + event_flags = ctl.flags; for (ctl.index = 0; ctl.index < nr_idxs; ctl.index++) { + ctl.flags = event_flags; ret = event_ctl(stdev, &ctl); if (ret < 0) return ret; @@ -1017,10 +1052,24 @@ static void enable_link_state_events(struct switchtec_dev *stdev) } } +static void enable_dma_mrpc(struct switchtec_dev *stdev) +{ + writeq(stdev->dma_mrpc_dma_addr, &stdev->mmio_mrpc->dma_addr); + flush_wc_buf(stdev); + iowrite32(SWITCHTEC_DMA_MRPC_EN, &stdev->mmio_mrpc->dma_en); +} + static void stdev_release(struct device *dev) { struct switchtec_dev *stdev = to_stdev(dev); + if (stdev->dma_mrpc) { + iowrite32(0, &stdev->mmio_mrpc->dma_en); + flush_wc_buf(stdev); + writeq(0, &stdev->mmio_mrpc->dma_addr); + dma_free_coherent(&stdev->pdev->dev, sizeof(*stdev->dma_mrpc), + stdev->dma_mrpc, stdev->dma_mrpc_dma_addr); + } kfree(stdev); } @@ -1176,10 +1225,27 @@ static irqreturn_t switchtec_event_isr(int irq, void *dev) return ret; } + +static irqreturn_t switchtec_dma_mrpc_isr(int irq, void *dev) +{ + struct switchtec_dev *stdev = dev; + irqreturn_t ret = IRQ_NONE; + + iowrite32(SWITCHTEC_EVENT_CLEAR | + SWITCHTEC_EVENT_EN_IRQ, + &stdev->mmio_part_cfg->mrpc_comp_hdr); + schedule_work(&stdev->mrpc_work); + + ret = IRQ_HANDLED; + return ret; +} + static int switchtec_init_isr(struct switchtec_dev *stdev) { int nvecs; int event_irq; + int dma_mrpc_irq; + int rc; nvecs = pci_alloc_irq_vectors(stdev->pdev, 1, 4, PCI_IRQ_MSIX | PCI_IRQ_MSI); @@ -1194,9 +1260,29 @@ static int switchtec_init_isr(struct switchtec_dev *stdev) if (event_irq < 0) return event_irq; - return devm_request_irq(&stdev->pdev->dev, event_irq, + rc = devm_request_irq(&stdev->pdev->dev, event_irq, switchtec_event_isr, 0, KBUILD_MODNAME, stdev); + + if (rc) + return rc; + + if (!stdev->dma_mrpc) + return rc; + + dma_mrpc_irq = ioread32(&stdev->mmio_mrpc->dma_vector); + if (dma_mrpc_irq < 0 || dma_mrpc_irq >= nvecs) + return -EFAULT; + + dma_mrpc_irq = pci_irq_vector(stdev->pdev, dma_mrpc_irq); + if (dma_mrpc_irq < 0) + return dma_mrpc_irq; + + rc = devm_request_irq(&stdev->pdev->dev, dma_mrpc_irq, + switchtec_dma_mrpc_isr, 0, + KBUILD_MODNAME, stdev); + + return rc; } static void init_pff(struct switchtec_dev *stdev) @@ -1232,19 +1318,38 @@ static int switchtec_init_pci(struct switchtec_dev *stdev, struct pci_dev *pdev) { int rc; + void __iomem *map; + unsigned long res_start, res_len; rc = pcim_enable_device(pdev); if (rc) return rc; - rc = pcim_iomap_regions(pdev, 0x1, KBUILD_MODNAME); + rc = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)); if (rc) return rc; pci_set_master(pdev); - stdev->mmio = pcim_iomap_table(pdev)[0]; - stdev->mmio_mrpc = stdev->mmio + SWITCHTEC_GAS_MRPC_OFFSET; + res_start = pci_resource_start(pdev, 0); + res_len = pci_resource_len(pdev, 0); + + if (!devm_request_mem_region(&pdev->dev, res_start, + res_len, KBUILD_MODNAME)) + return -EBUSY; + + stdev->mmio_mrpc = devm_ioremap_wc(&pdev->dev, res_start, + SWITCHTEC_GAS_TOP_CFG_OFFSET); + if (!stdev->mmio_mrpc) + return -ENOMEM; + + map = devm_ioremap(&pdev->dev, + res_start + SWITCHTEC_GAS_TOP_CFG_OFFSET, + res_len - SWITCHTEC_GAS_TOP_CFG_OFFSET); + if (!map) + return -ENOMEM; + + stdev->mmio = map - SWITCHTEC_GAS_TOP_CFG_OFFSET; stdev->mmio_sw_event = stdev->mmio + SWITCHTEC_GAS_SW_EVENT_OFFSET; stdev->mmio_sys_info = stdev->mmio + SWITCHTEC_GAS_SYS_INFO_OFFSET; stdev->mmio_flash_info = stdev->mmio + SWITCHTEC_GAS_FLASH_INFO_OFFSET; @@ -1262,6 +1367,19 @@ static int switchtec_init_pci(struct switchtec_dev *stdev, pci_set_drvdata(pdev, stdev); + if (!use_dma_mrpc) + return 0; + + if (ioread32(&stdev->mmio_mrpc->dma_ver) == 0) + return 0; + + stdev->dma_mrpc = dma_zalloc_coherent(&stdev->pdev->dev, + sizeof(*stdev->dma_mrpc), + &stdev->dma_mrpc_dma_addr, + GFP_KERNEL); + if (stdev->dma_mrpc == NULL) + return -ENOMEM; + return 0; } @@ -1293,6 +1411,9 @@ static int switchtec_pci_probe(struct pci_dev *pdev, &stdev->mmio_part_cfg->mrpc_comp_hdr); enable_link_state_events(stdev); + if (stdev->dma_mrpc) + enable_dma_mrpc(stdev); + rc = cdev_device_add(&stdev->cdev, &stdev->dev); if (rc) goto err_devadd; @@ -1318,7 +1439,6 @@ static void switchtec_pci_remove(struct pci_dev *pdev) cdev_device_del(&stdev->cdev, &stdev->dev); ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt)); dev_info(&stdev->dev, "unregistered.\n"); - stdev_kill(stdev); put_device(&stdev->dev); } |