diff options
Diffstat (limited to 'drivers/pci/host/pcie-designware.c')
-rw-r--r-- | drivers/pci/host/pcie-designware.c | 257 |
1 files changed, 8 insertions, 249 deletions
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index e33b68b..c10e9ac 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -11,11 +11,8 @@ * published by the Free Software Foundation. */ -#include <linux/irq.h> -#include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/msi.h> #include <linux/of_address.h> #include <linux/pci.h> #include <linux/pci_regs.h> @@ -67,7 +64,7 @@ static struct hw_pci dw_pci; -static unsigned long global_io_offset; +unsigned long global_io_offset; static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys) { @@ -118,8 +115,8 @@ static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg) writel(val, pp->dbi_base + reg); } -static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, - u32 *val) +int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, + u32 *val) { int ret; @@ -131,8 +128,8 @@ static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, return ret; } -static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, - u32 val) +int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, + u32 val) { int ret; @@ -145,205 +142,6 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, return ret; } -static struct irq_chip dw_msi_irq_chip = { - .name = "PCI-MSI", - .irq_enable = unmask_msi_irq, - .irq_disable = mask_msi_irq, - .irq_mask = mask_msi_irq, - .irq_unmask = unmask_msi_irq, -}; - -/* MSI int handler */ -void dw_handle_msi_irq(struct pcie_port *pp) -{ - unsigned long val; - int i, pos, irq; - - for (i = 0; i < MAX_MSI_CTRLS; i++) { - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, - (u32 *)&val); - if (val) { - pos = 0; - while ((pos = find_next_bit(&val, 32, pos)) != 32) { - irq = irq_find_mapping(pp->irq_domain, - i * 32 + pos); - generic_handle_irq(irq); - pos++; - } - } - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, val); - } -} - -void dw_pcie_msi_init(struct pcie_port *pp) -{ - pp->msi_data = __get_free_pages(GFP_KERNEL, 0); - - /* program the msi_data */ - dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4, - virt_to_phys((void *)pp->msi_data)); - dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0); -} - -static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) -{ - int flag = 1; - - do { - pos = find_next_zero_bit(pp->msi_irq_in_use, - MAX_MSI_IRQS, pos); - /*if you have reached to the end then get out from here.*/ - if (pos == MAX_MSI_IRQS) - return -ENOSPC; - /* - * Check if this position is at correct offset.nvec is always a - * power of two. pos0 must be nvec bit aligned. - */ - if (pos % msgvec) - pos += msgvec - (pos % msgvec); - else - flag = 0; - } while (flag); - - *pos0 = pos; - return 0; -} - -static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) -{ - int res, bit, irq, pos0, pos1, i; - u32 val; - struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata); - - if (!pp) { - BUG(); - return -EINVAL; - } - - pos0 = find_first_zero_bit(pp->msi_irq_in_use, - MAX_MSI_IRQS); - if (pos0 % no_irqs) { - if (find_valid_pos0(pp, no_irqs, pos0, &pos0)) - goto no_valid_irq; - } - if (no_irqs > 1) { - pos1 = find_next_bit(pp->msi_irq_in_use, - MAX_MSI_IRQS, pos0); - /* there must be nvec number of consecutive free bits */ - while ((pos1 - pos0) < no_irqs) { - if (find_valid_pos0(pp, no_irqs, pos1, &pos0)) - goto no_valid_irq; - pos1 = find_next_bit(pp->msi_irq_in_use, - MAX_MSI_IRQS, pos0); - } - } - - irq = irq_find_mapping(pp->irq_domain, pos0); - if (!irq) - goto no_valid_irq; - - i = 0; - while (i < no_irqs) { - set_bit(pos0 + i, pp->msi_irq_in_use); - irq_alloc_descs((irq + i), (irq + i), 1, 0); - irq_set_msi_desc(irq + i, desc); - /*Enable corresponding interrupt in MSI interrupt controller */ - res = ((pos0 + i) / 32) * 12; - bit = (pos0 + i) % 32; - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); - val |= 1 << bit; - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); - i++; - } - - *pos = pos0; - return irq; - -no_valid_irq: - *pos = pos0; - return -ENOSPC; -} - -static void clear_irq(unsigned int irq) -{ - int res, bit, val, pos; - struct irq_desc *desc; - struct msi_desc *msi; - struct pcie_port *pp; - struct irq_data *data = irq_get_irq_data(irq); - - /* get the port structure */ - desc = irq_to_desc(irq); - msi = irq_desc_get_msi_desc(desc); - pp = sys_to_pcie(msi->dev->bus->sysdata); - if (!pp) { - BUG(); - return; - } - - pos = data->hwirq; - - irq_free_desc(irq); - - clear_bit(pos, pp->msi_irq_in_use); - - /* Disable corresponding interrupt on MSI interrupt controller */ - res = (pos / 32) * 12; - bit = pos % 32; - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); - val &= ~(1 << bit); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); -} - -static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, - struct msi_desc *desc) -{ - int irq, pos, msgvec; - u16 msg_ctr; - struct msi_msg msg; - struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata); - - if (!pp) { - BUG(); - return -EINVAL; - } - - pci_read_config_word(pdev, desc->msi_attrib.pos+PCI_MSI_FLAGS, - &msg_ctr); - msgvec = (msg_ctr&PCI_MSI_FLAGS_QSIZE) >> 4; - if (msgvec == 0) - msgvec = (msg_ctr & PCI_MSI_FLAGS_QMASK) >> 1; - if (msgvec > 5) - msgvec = 0; - - irq = assign_irq((1 << msgvec), desc, &pos); - if (irq < 0) - return irq; - - msg_ctr &= ~PCI_MSI_FLAGS_QSIZE; - msg_ctr |= msgvec << 4; - pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, - msg_ctr); - desc->msi_attrib.multiple = msgvec; - - msg.address_lo = virt_to_phys((void *)pp->msi_data); - msg.address_hi = 0x0; - msg.data = pos; - write_msi_msg(irq, &msg); - - return 0; -} - -static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) -{ - clear_irq(irq); -} - -static struct msi_chip dw_pcie_msi_chip = { - .setup_irq = dw_msi_setup_irq, - .teardown_irq = dw_msi_teardown_irq, -}; - int dw_pcie_link_up(struct pcie_port *pp) { if (pp->ops->link_up) @@ -352,27 +150,12 @@ int dw_pcie_link_up(struct pcie_port *pp) return 0; } -static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) -{ - irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); - set_irq_flags(irq, IRQF_VALID); - - return 0; -} - -static const struct irq_domain_ops msi_domain_ops = { - .map = dw_pcie_msi_map, -}; - int __init dw_pcie_host_init(struct pcie_port *pp) { struct device_node *np = pp->dev->of_node; struct of_pci_range range; struct of_pci_range_parser parser; u32 val; - int i; if (of_pci_range_parser_init(&parser, np)) { dev_err(pp->dev, "missing ranges property\n"); @@ -440,19 +223,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp) return -EINVAL; } - if (IS_ENABLED(CONFIG_PCI_MSI)) { - pp->irq_domain = irq_domain_add_linear(pp->dev->of_node, - MAX_MSI_IRQS, &msi_domain_ops, - &dw_pcie_msi_chip); - if (!pp->irq_domain) { - dev_err(pp->dev, "irq domain init failed\n"); - return -ENXIO; - } - - for (i = 0; i < MAX_MSI_IRQS; i++) - irq_create_mapping(pp->irq_domain, i); - } - if (pp->ops->host_init) pp->ops->host_init(pp); @@ -668,7 +438,7 @@ static struct pci_ops dw_pcie_ops = { .write = dw_pcie_wr_conf, }; -static int dw_pcie_setup(int nr, struct pci_sys_data *sys) +int dw_pcie_setup(int nr, struct pci_sys_data *sys) { struct pcie_port *pp; @@ -691,7 +461,7 @@ static int dw_pcie_setup(int nr, struct pci_sys_data *sys) return 1; } -static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) +struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) { struct pci_bus *bus; struct pcie_port *pp = sys_to_pcie(sys); @@ -708,28 +478,17 @@ static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) return bus; } -static int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { struct pcie_port *pp = sys_to_pcie(dev->bus->sysdata); return pp->irq; } -static void dw_pcie_add_bus(struct pci_bus *bus) -{ - if (IS_ENABLED(CONFIG_PCI_MSI)) { - struct pcie_port *pp = sys_to_pcie(bus->sysdata); - - dw_pcie_msi_chip.dev = pp->dev; - bus->msi = &dw_pcie_msi_chip; - } -} - static struct hw_pci dw_pci = { .setup = dw_pcie_setup, .scan = dw_pcie_scan_bus, .map_irq = dw_pcie_map_irq, - .add_bus = dw_pcie_add_bus, }; void dw_pcie_setup_rc(struct pcie_port *pp) |