| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Host side endpoint driver to implement Non-Transparent Bridge functionality |
| * |
| * Copyright (C) 2020 Texas Instruments |
| * Author: Kishon Vijay Abraham I <kishon@ti.com> |
| */ |
| |
| #include <linux/delay.h> |
| #include <linux/module.h> |
| #include <linux/pci.h> |
| #include <linux/slab.h> |
| #include <linux/ntb.h> |
| |
| #define NTB_EPF_COMMAND 0x0 |
| #define CMD_CONFIGURE_DOORBELL 1 |
| #define CMD_TEARDOWN_DOORBELL 2 |
| #define CMD_CONFIGURE_MW 3 |
| #define CMD_TEARDOWN_MW 4 |
| #define CMD_LINK_UP 5 |
| #define CMD_LINK_DOWN 6 |
| |
| #define NTB_EPF_ARGUMENT 0x4 |
| #define MSIX_ENABLE BIT(16) |
| |
| #define NTB_EPF_CMD_STATUS 0x8 |
| #define COMMAND_STATUS_OK 1 |
| #define COMMAND_STATUS_ERROR 2 |
| |
| #define NTB_EPF_LINK_STATUS 0x0A |
| #define LINK_STATUS_UP BIT(0) |
| |
| #define NTB_EPF_TOPOLOGY 0x0C |
| #define NTB_EPF_LOWER_ADDR 0x10 |
| #define NTB_EPF_UPPER_ADDR 0x14 |
| #define NTB_EPF_LOWER_SIZE 0x18 |
| #define NTB_EPF_UPPER_SIZE 0x1C |
| #define NTB_EPF_MW_COUNT 0x20 |
| #define NTB_EPF_MW1_OFFSET 0x24 |
| #define NTB_EPF_SPAD_OFFSET 0x28 |
| #define NTB_EPF_SPAD_COUNT 0x2C |
| #define NTB_EPF_DB_ENTRY_SIZE 0x30 |
| #define NTB_EPF_DB_DATA(n) (0x34 + (n) * 4) |
| #define NTB_EPF_DB_OFFSET(n) (0xB4 + (n) * 4) |
| |
| #define NTB_EPF_MIN_DB_COUNT 3 |
| #define NTB_EPF_MAX_DB_COUNT 31 |
| |
| #define NTB_EPF_COMMAND_TIMEOUT 1000 /* 1 Sec */ |
| |
| enum pci_barno { |
| BAR_0, |
| BAR_1, |
| BAR_2, |
| BAR_3, |
| BAR_4, |
| BAR_5, |
| }; |
| |
| struct ntb_epf_dev { |
| struct ntb_dev ntb; |
| struct device *dev; |
| /* Mutex to protect providing commands to NTB EPF */ |
| struct mutex cmd_lock; |
| |
| enum pci_barno ctrl_reg_bar; |
| enum pci_barno peer_spad_reg_bar; |
| enum pci_barno db_reg_bar; |
| enum pci_barno mw_bar; |
| |
| unsigned int mw_count; |
| unsigned int spad_count; |
| unsigned int db_count; |
| |
| void __iomem *ctrl_reg; |
| void __iomem *db_reg; |
| void __iomem *peer_spad_reg; |
| |
| unsigned int self_spad; |
| unsigned int peer_spad; |
| |
| int db_val; |
| u64 db_valid_mask; |
| }; |
| |
| #define ntb_ndev(__ntb) container_of(__ntb, struct ntb_epf_dev, ntb) |
| |
| struct ntb_epf_data { |
| /* BAR that contains both control region and self spad region */ |
| enum pci_barno ctrl_reg_bar; |
| /* BAR that contains peer spad region */ |
| enum pci_barno peer_spad_reg_bar; |
| /* BAR that contains Doorbell region and Memory window '1' */ |
| enum pci_barno db_reg_bar; |
| /* BAR that contains memory windows*/ |
| enum pci_barno mw_bar; |
| }; |
| |
| static int ntb_epf_send_command(struct ntb_epf_dev *ndev, u32 command, |
| u32 argument) |
| { |
| ktime_t timeout; |
| bool timedout; |
| int ret = 0; |
| u32 status; |
| |
| mutex_lock(&ndev->cmd_lock); |
| writel(argument, ndev->ctrl_reg + NTB_EPF_ARGUMENT); |
| writel(command, ndev->ctrl_reg + NTB_EPF_COMMAND); |
| |
| timeout = ktime_add_ms(ktime_get(), NTB_EPF_COMMAND_TIMEOUT); |
| while (1) { |
| timedout = ktime_after(ktime_get(), timeout); |
| status = readw(ndev->ctrl_reg + NTB_EPF_CMD_STATUS); |
| |
| if (status == COMMAND_STATUS_ERROR) { |
| ret = -EINVAL; |
| break; |
| } |
| |
| if (status == COMMAND_STATUS_OK) |
| break; |
| |
| if (WARN_ON(timedout)) { |
| ret = -ETIMEDOUT; |
| break; |
| } |
| |
| usleep_range(5, 10); |
| } |
| |
| writew(0, ndev->ctrl_reg + NTB_EPF_CMD_STATUS); |
| mutex_unlock(&ndev->cmd_lock); |
| |
| return ret; |
| } |
| |
| static int ntb_epf_mw_to_bar(struct ntb_epf_dev *ndev, int idx) |
| { |
| struct device *dev = ndev->dev; |
| |
| if (idx < 0 || idx > ndev->mw_count) { |
| dev_err(dev, "Unsupported Memory Window index %d\n", idx); |
| return -EINVAL; |
| } |
| |
| return idx + 2; |
| } |
| |
| static int ntb_epf_mw_count(struct ntb_dev *ntb, int pidx) |
| { |
| struct ntb_epf_dev *ndev = ntb_ndev(ntb); |
| struct device *dev = ndev->dev; |
| |
| if (pidx != NTB_DEF_PEER_IDX) { |
| dev_err(dev, "Unsupported Peer ID %d\n", pidx); |
| return -EINVAL; |
| } |
| |
| return ndev->mw_count; |
| } |
| |
| static int ntb_epf_mw_get_align(struct ntb_dev *ntb, int pidx, int idx, |
| resource_size_t *addr_align, |
| resource_size_t *size_align, |
| resource_size_t *size_max) |
| { |
| struct ntb_epf_dev *ndev = ntb_ndev(ntb); |
| struct device *dev = ndev->dev; |
| int bar; |
| |
| if (pidx != NTB_DEF_PEER_IDX) { |
| dev_err(dev, "Unsupported Peer ID %d\n", pidx); |
| return -EINVAL; |
| } |
| |
| bar = ntb_epf_mw_to_bar(ndev, idx); |
| if (bar < 0) |
| return bar; |
| |
| if (addr_align) |
| *addr_align = SZ_4K; |
| |
| if (size_align) |
| *size_align = 1; |
| |
| if (size_max) |
| *size_max = pci_resource_len(ndev->ntb.pdev, bar); |
| |
| return 0; |
| } |
| |
| static u64 ntb_epf_link_is_up(struct ntb_dev *ntb, |
| enum ntb_speed *speed, |
| enum ntb_width *width) |
| { |
| struct ntb_epf_dev *ndev = ntb_ndev(ntb); |
| u32 status; |
| |
| status = readw(ndev->ctrl_reg + NTB_EPF_LINK_STATUS); |
| |
| return status & LINK_STATUS_UP; |
| } |
| |
| static u32 ntb_epf_spad_read(struct ntb_dev *ntb, int idx) |
| { |
| struct ntb_epf_dev *ndev = ntb_ndev(ntb); |
| struct device *dev = ndev->dev; |
| u32 offset; |
| |
| if (idx < 0 || idx >= ndev->spad_count) { |
| dev_err(dev, "READ: Invalid ScratchPad Index %d\n", idx); |
| return 0; |
| } |
| |
| offset = readl(ndev->ctrl_reg + NTB_EPF_SPAD_OFFSET); |
| offset += (idx << 2); |
| |
| return readl(ndev->ctrl_reg + offset); |
| } |
| |
| static int ntb_epf_spad_write(struct ntb_dev *ntb, |
| int idx, u32 val) |
| { |
| struct ntb_epf_dev *ndev = ntb_ndev(ntb); |
| struct device *dev = ndev->dev; |
| u32 offset; |
| |
| if (idx < 0 || idx >= ndev->spad_count) { |
| dev_err(dev, "WRITE: Invalid ScratchPad Index %d\n", idx); |
| return -EINVAL; |
| } |
| |
| offset = readl(ndev->ctrl_reg + NTB_EPF_SPAD_OFFSET); |
| offset += (idx << 2); |
| writel(val, ndev->ctrl_reg + offset); |
| |
| return 0; |
| } |
| |
| static u32 ntb_epf_peer_spad_read(struct ntb_dev *ntb, int pidx, int idx) |
| { |
| struct ntb_epf_dev *ndev = ntb_ndev(ntb); |
| struct device *dev = ndev->dev; |
| u32 offset; |
| |
| if (pidx != NTB_DEF_PEER_IDX) { |
| dev_err(dev, "Unsupported Peer ID %d\n", pidx); |
| return -EINVAL; |
| } |
| |
| if (idx < 0 || idx >= ndev->spad_count) { |
| dev_err(dev, "WRITE: Invalid Peer ScratchPad Index %d\n", idx); |
| return -EINVAL; |
| } |
| |
| offset = (idx << 2); |
| return readl(ndev->peer_spad_reg + offset); |
| } |
| |
| static int ntb_epf_peer_spad_write(struct ntb_dev *ntb, int pidx, |
| int idx, u32 val) |
| { |
| struct ntb_epf_dev *ndev = ntb_ndev(ntb); |
| struct device *dev = ndev->dev; |
| u32 offset; |
| |
| if (pidx != NTB_DEF_PEER_IDX) { |
| dev_err(dev, "Unsupported Peer ID %d\n", pidx); |
| return -EINVAL; |
| } |
| |
| if (idx < 0 || idx >= ndev->spad_count) { |
| dev_err(dev, "WRITE: Invalid Peer ScratchPad Index %d\n", idx); |
| return -EINVAL; |
| } |
| |
| offset = (idx << 2); |
| writel(val, ndev->peer_spad_reg + offset); |
| |
| return 0; |
| } |
| |
| static int ntb_epf_link_enable(struct ntb_dev *ntb, |
| enum ntb_speed max_speed, |
| enum ntb_width max_width) |
| { |
| struct ntb_epf_dev *ndev = ntb_ndev(ntb); |
| struct device *dev = ndev->dev; |
| int ret; |
| |
| ret = ntb_epf_send_command(ndev, CMD_LINK_UP, 0); |
| if (ret) { |
| dev_err(dev, "Fail to enable link\n"); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int ntb_epf_link_disable(struct ntb_dev *ntb) |
| { |
| struct ntb_epf_dev *ndev = ntb_ndev(ntb); |
| struct device *dev = ndev->dev; |
| int ret; |
| |
| ret = ntb_epf_send_command(ndev, CMD_LINK_DOWN, 0); |
| if (ret) { |
| dev_err(dev, "Fail to disable link\n"); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static irqreturn_t ntb_epf_vec_isr(int irq, void *dev) |
| { |
| struct ntb_epf_dev *ndev = dev; |
| int irq_no; |
| |
| irq_no = irq - pci_irq_vector(ndev->ntb.pdev, 0); |
| ndev->db_val = irq_no + 1; |
| |
| if (irq_no == 0) |
| ntb_link_event(&ndev->ntb); |
| else |
| ntb_db_event(&ndev->ntb, irq_no); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) |
| { |
| struct pci_dev *pdev = ndev->ntb.pdev; |
| struct device *dev = ndev->dev; |
| u32 argument = MSIX_ENABLE; |
| int irq; |
| int ret; |
| int i; |
| |
| irq = pci_alloc_irq_vectors(pdev, msi_min, msi_max, PCI_IRQ_MSIX); |
| if (irq < 0) { |
| dev_dbg(dev, "Failed to get MSIX interrupts\n"); |
| irq = pci_alloc_irq_vectors(pdev, msi_min, msi_max, |
| PCI_IRQ_MSI); |
| if (irq < 0) { |
| dev_err(dev, "Failed to get MSI interrupts\n"); |
| return irq; |
| } |
| argument &= ~MSIX_ENABLE; |
| } |
| |
| for (i = 0; i < irq; i++) { |
| ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr, |
| 0, "ntb_epf", ndev); |
| if (ret) { |
| dev_err(dev, "Failed to request irq\n"); |
| goto err_request_irq; |
| } |
| } |
| |
| ndev->db_count = irq - 1; |
| |
| ret = ntb_epf_send_command(ndev, CMD_CONFIGURE_DOORBELL, |
| argument | irq); |
| if (ret) { |
| dev_err(dev, "Failed to configure doorbell\n"); |
| goto err_configure_db; |
| } |
| |
| return 0; |
| |
| err_configure_db: |
| for (i = 0; i < ndev->db_count + 1; i++) |
| free_irq(pci_irq_vector(pdev, i), ndev); |
| |
| err_request_irq: |
| pci_free_irq_vectors(pdev); |
| |
| return ret; |
| } |
| |
| static int ntb_epf_peer_mw_count(struct ntb_dev *ntb) |
| { |
| return ntb_ndev(ntb)->mw_count; |
| } |
| |
| static int ntb_epf_spad_count(struct ntb_dev *ntb) |
| { |
| return ntb_ndev(ntb)->spad_count; |
| } |
| |
| static u64 ntb_epf_db_valid_mask(struct ntb_dev *ntb) |
| { |
| return ntb_ndev(ntb)->db_valid_mask; |
| } |
| |
| static int ntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits) |
| { |
| return 0; |
| } |
| |
| static int ntb_epf_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx, |
| dma_addr_t addr, resource_size_t size) |
| { |
| struct ntb_epf_dev *ndev = ntb_ndev(ntb); |
| struct device *dev = ndev->dev; |
| resource_size_t mw_size; |
| int bar; |
| |
| if (pidx != NTB_DEF_PEER_IDX) { |
| dev_err(dev, "Unsupported Peer ID %d\n", pidx); |
| return -EINVAL; |
| } |
| |
| bar = idx + ndev->mw_bar; |
| |
| mw_size = pci_resource_len(ntb->pdev, bar); |
| |
| if (size > mw_size) { |
| dev_err(dev, "Size:%pa is greater than the MW size %pa\n", |
| &size, &mw_size); |
| return -EINVAL; |
| } |
| |
| writel(lower_32_bits(addr), ndev->ctrl_reg + NTB_EPF_LOWER_ADDR); |
| writel(upper_32_bits(addr), ndev->ctrl_reg + NTB_EPF_UPPER_ADDR); |
| writel(lower_32_bits(size), ndev->ctrl_reg + NTB_EPF_LOWER_SIZE); |
| writel(upper_32_bits(size), ndev->ctrl_reg + NTB_EPF_UPPER_SIZE); |
| ntb_epf_send_command(ndev, CMD_CONFIGURE_MW, idx); |
| |
| return 0; |
| } |
| |
| static int ntb_epf_mw_clear_trans(struct ntb_dev *ntb, int pidx, int idx) |
| { |
| struct ntb_epf_dev *ndev = ntb_ndev(ntb); |
| struct device *dev = ndev->dev; |
| int ret = 0; |
| |
| ntb_epf_send_command(ndev, CMD_TEARDOWN_MW, idx); |
| if (ret) |
| dev_err(dev, "Failed to teardown memory window\n"); |
| |
| return ret; |
| } |
| |
| static int ntb_epf_peer_mw_get_addr(struct ntb_dev *ntb, int idx, |
| phys_addr_t *base, resource_size_t *size) |
| { |
| struct ntb_epf_dev *ndev = ntb_ndev(ntb); |
| u32 offset = 0; |
| int bar; |
| |
| if (idx == 0) |
| offset = readl(ndev->ctrl_reg + NTB_EPF_MW1_OFFSET); |
| |
| bar = idx + ndev->mw_bar; |
| |
| if (base) |
| *base = pci_resource_start(ndev->ntb.pdev, bar) + offset; |
| |
| if (size) |
| *size = pci_resource_len(ndev->ntb.pdev, bar) - offset; |
| |
| return 0; |
| } |
| |
| static int ntb_epf_peer_db_set(struct ntb_dev *ntb, u64 db_bits) |
| { |
| struct ntb_epf_dev *ndev = ntb_ndev(ntb); |
| u32 interrupt_num = ffs(db_bits) + 1; |
| struct device *dev = ndev->dev; |
| u32 db_entry_size; |
| u32 db_offset; |
| u32 db_data; |
| |
| if (interrupt_num > ndev->db_count) { |
| dev_err(dev, "DB interrupt %d greater than Max Supported %d\n", |
| interrupt_num, ndev->db_count); |
| return -EINVAL; |
| } |
| |
| db_entry_size = readl(ndev->ctrl_reg + NTB_EPF_DB_ENTRY_SIZE); |
| |
| db_data = readl(ndev->ctrl_reg + NTB_EPF_DB_DATA(interrupt_num)); |
| db_offset = readl(ndev->ctrl_reg + NTB_EPF_DB_OFFSET(interrupt_num)); |
| writel(db_data, ndev->db_reg + (db_entry_size * interrupt_num) + |
| db_offset); |
| |
| return 0; |
| } |
| |
| static u64 ntb_epf_db_read(struct ntb_dev *ntb) |
| { |
| struct ntb_epf_dev *ndev = ntb_ndev(ntb); |
| |
| return ndev->db_val; |
| } |
| |
| static int ntb_epf_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) |
| { |
| return 0; |
| } |
| |
| static int ntb_epf_db_clear(struct ntb_dev *ntb, u64 db_bits) |
| { |
| struct ntb_epf_dev *ndev = ntb_ndev(ntb); |
| |
| ndev->db_val = 0; |
| |
| return 0; |
| } |
| |
| static const struct ntb_dev_ops ntb_epf_ops = { |
| .mw_count = ntb_epf_mw_count, |
| .spad_count = ntb_epf_spad_count, |
| .peer_mw_count = ntb_epf_peer_mw_count, |
| .db_valid_mask = ntb_epf_db_valid_mask, |
| .db_set_mask = ntb_epf_db_set_mask, |
| .mw_set_trans = ntb_epf_mw_set_trans, |
| .mw_clear_trans = ntb_epf_mw_clear_trans, |
| .peer_mw_get_addr = ntb_epf_peer_mw_get_addr, |
| .link_enable = ntb_epf_link_enable, |
| .spad_read = ntb_epf_spad_read, |
| .spad_write = ntb_epf_spad_write, |
| .peer_spad_read = ntb_epf_peer_spad_read, |
| .peer_spad_write = ntb_epf_peer_spad_write, |
| .peer_db_set = ntb_epf_peer_db_set, |
| .db_read = ntb_epf_db_read, |
| .mw_get_align = ntb_epf_mw_get_align, |
| .link_is_up = ntb_epf_link_is_up, |
| .db_clear_mask = ntb_epf_db_clear_mask, |
| .db_clear = ntb_epf_db_clear, |
| .link_disable = ntb_epf_link_disable, |
| }; |
| |
| static inline void ntb_epf_init_struct(struct ntb_epf_dev *ndev, |
| struct pci_dev *pdev) |
| { |
| ndev->ntb.pdev = pdev; |
| ndev->ntb.topo = NTB_TOPO_NONE; |
| ndev->ntb.ops = &ntb_epf_ops; |
| } |
| |
| static int ntb_epf_init_dev(struct ntb_epf_dev *ndev) |
| { |
| struct device *dev = ndev->dev; |
| int ret; |
| |
| /* One Link interrupt and rest doorbell interrupt */ |
| ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1, |
| NTB_EPF_MAX_DB_COUNT + 1); |
| if (ret) { |
| dev_err(dev, "Failed to init ISR\n"); |
| return ret; |
| } |
| |
| ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1; |
| ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT); |
| ndev->spad_count = readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT); |
| |
| return 0; |
| } |
| |
| static int ntb_epf_init_pci(struct ntb_epf_dev *ndev, |
| struct pci_dev *pdev) |
| { |
| struct device *dev = ndev->dev; |
| size_t spad_sz, spad_off; |
| int ret; |
| |
| pci_set_drvdata(pdev, ndev); |
| |
| ret = pci_enable_device(pdev); |
| if (ret) { |
| dev_err(dev, "Cannot enable PCI device\n"); |
| goto err_pci_enable; |
| } |
| |
| ret = pci_request_regions(pdev, "ntb"); |
| if (ret) { |
| dev_err(dev, "Cannot obtain PCI resources\n"); |
| goto err_pci_regions; |
| } |
| |
| pci_set_master(pdev); |
| |
| ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); |
| if (ret) { |
| ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); |
| if (ret) { |
| dev_err(dev, "Cannot set DMA mask\n"); |
| goto err_pci_regions; |
| } |
| dev_warn(&pdev->dev, "Cannot DMA highmem\n"); |
| } |
| |
| ndev->ctrl_reg = pci_iomap(pdev, ndev->ctrl_reg_bar, 0); |
| if (!ndev->ctrl_reg) { |
| ret = -EIO; |
| goto err_pci_regions; |
| } |
| |
| if (ndev->peer_spad_reg_bar) { |
| ndev->peer_spad_reg = pci_iomap(pdev, ndev->peer_spad_reg_bar, 0); |
| if (!ndev->peer_spad_reg) { |
| ret = -EIO; |
| goto err_pci_regions; |
| } |
| } else { |
| spad_sz = 4 * readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT); |
| spad_off = readl(ndev->ctrl_reg + NTB_EPF_SPAD_OFFSET); |
| ndev->peer_spad_reg = ndev->ctrl_reg + spad_off + spad_sz; |
| } |
| |
| ndev->db_reg = pci_iomap(pdev, ndev->db_reg_bar, 0); |
| if (!ndev->db_reg) { |
| ret = -EIO; |
| goto err_pci_regions; |
| } |
| |
| return 0; |
| |
| err_pci_regions: |
| pci_disable_device(pdev); |
| |
| err_pci_enable: |
| pci_set_drvdata(pdev, NULL); |
| |
| return ret; |
| } |
| |
| static void ntb_epf_deinit_pci(struct ntb_epf_dev *ndev) |
| { |
| struct pci_dev *pdev = ndev->ntb.pdev; |
| |
| pci_iounmap(pdev, ndev->ctrl_reg); |
| pci_iounmap(pdev, ndev->peer_spad_reg); |
| pci_iounmap(pdev, ndev->db_reg); |
| |
| pci_release_regions(pdev); |
| pci_disable_device(pdev); |
| pci_set_drvdata(pdev, NULL); |
| } |
| |
| static void ntb_epf_cleanup_isr(struct ntb_epf_dev *ndev) |
| { |
| struct pci_dev *pdev = ndev->ntb.pdev; |
| int i; |
| |
| ntb_epf_send_command(ndev, CMD_TEARDOWN_DOORBELL, ndev->db_count + 1); |
| |
| for (i = 0; i < ndev->db_count + 1; i++) |
| free_irq(pci_irq_vector(pdev, i), ndev); |
| pci_free_irq_vectors(pdev); |
| } |
| |
| static int ntb_epf_pci_probe(struct pci_dev *pdev, |
| const struct pci_device_id *id) |
| { |
| enum pci_barno peer_spad_reg_bar = BAR_1; |
| enum pci_barno ctrl_reg_bar = BAR_0; |
| enum pci_barno db_reg_bar = BAR_2; |
| enum pci_barno mw_bar = BAR_2; |
| struct device *dev = &pdev->dev; |
| struct ntb_epf_data *data; |
| struct ntb_epf_dev *ndev; |
| int ret; |
| |
| if (pci_is_bridge(pdev)) |
| return -ENODEV; |
| |
| ndev = devm_kzalloc(dev, sizeof(*ndev), GFP_KERNEL); |
| if (!ndev) |
| return -ENOMEM; |
| |
| data = (struct ntb_epf_data *)id->driver_data; |
| if (data) { |
| peer_spad_reg_bar = data->peer_spad_reg_bar; |
| ctrl_reg_bar = data->ctrl_reg_bar; |
| db_reg_bar = data->db_reg_bar; |
| mw_bar = data->mw_bar; |
| } |
| |
| ndev->peer_spad_reg_bar = peer_spad_reg_bar; |
| ndev->ctrl_reg_bar = ctrl_reg_bar; |
| ndev->db_reg_bar = db_reg_bar; |
| ndev->mw_bar = mw_bar; |
| ndev->dev = dev; |
| |
| ntb_epf_init_struct(ndev, pdev); |
| mutex_init(&ndev->cmd_lock); |
| |
| ret = ntb_epf_init_pci(ndev, pdev); |
| if (ret) { |
| dev_err(dev, "Failed to init PCI\n"); |
| return ret; |
| } |
| |
| ret = ntb_epf_init_dev(ndev); |
| if (ret) { |
| dev_err(dev, "Failed to init device\n"); |
| goto err_init_dev; |
| } |
| |
| ret = ntb_register_device(&ndev->ntb); |
| if (ret) { |
| dev_err(dev, "Failed to register NTB device\n"); |
| goto err_register_dev; |
| } |
| |
| return 0; |
| |
| err_register_dev: |
| ntb_epf_cleanup_isr(ndev); |
| |
| err_init_dev: |
| ntb_epf_deinit_pci(ndev); |
| |
| return ret; |
| } |
| |
| static void ntb_epf_pci_remove(struct pci_dev *pdev) |
| { |
| struct ntb_epf_dev *ndev = pci_get_drvdata(pdev); |
| |
| ntb_unregister_device(&ndev->ntb); |
| ntb_epf_cleanup_isr(ndev); |
| ntb_epf_deinit_pci(ndev); |
| } |
| |
| static const struct ntb_epf_data j721e_data = { |
| .ctrl_reg_bar = BAR_0, |
| .peer_spad_reg_bar = BAR_1, |
| .db_reg_bar = BAR_2, |
| .mw_bar = BAR_2, |
| }; |
| |
| static const struct ntb_epf_data mx8_data = { |
| .ctrl_reg_bar = BAR_0, |
| .peer_spad_reg_bar = BAR_0, |
| .db_reg_bar = BAR_2, |
| .mw_bar = BAR_4, |
| }; |
| |
| static const struct pci_device_id ntb_epf_pci_tbl[] = { |
| { |
| PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J721E), |
| .class = PCI_CLASS_MEMORY_RAM << 8, .class_mask = 0xffff00, |
| .driver_data = (kernel_ulong_t)&j721e_data, |
| }, |
| { |
| PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x0809), |
| .class = PCI_CLASS_MEMORY_RAM << 8, .class_mask = 0xffff00, |
| .driver_data = (kernel_ulong_t)&mx8_data, |
| }, |
| { }, |
| }; |
| |
| static struct pci_driver ntb_epf_pci_driver = { |
| .name = KBUILD_MODNAME, |
| .id_table = ntb_epf_pci_tbl, |
| .probe = ntb_epf_pci_probe, |
| .remove = ntb_epf_pci_remove, |
| }; |
| module_pci_driver(ntb_epf_pci_driver); |
| |
| MODULE_DESCRIPTION("PCI ENDPOINT NTB HOST DRIVER"); |
| MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>"); |
| MODULE_LICENSE("GPL v2"); |