Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI: rockchip-dwc: Add legacy interrupt support

The legacy interrupts on the rk356x PCIe controller are handled by a
single muxed interrupt. Add IRQ domain support to the pcie-dw-rockchip
driver to support the virtual domain.

Link: https://lore.kernel.org/r/20220429123832.2376381-4-pgwipeout@gmail.com
Signed-off-by: Peter Geis <pgwipeout@gmail.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Reviewed-by: Marc Zyngier <maz@kernel.org>

authored by

Peter Geis and committed by
Lorenzo Pieralisi
e8aae154 431e7d2e

+94 -2
+94 -2
drivers/pci/controller/dwc/pcie-dw-rockchip.c
··· 10 10 11 11 #include <linux/clk.h> 12 12 #include <linux/gpio/consumer.h> 13 + #include <linux/irqchip/chained_irq.h> 14 + #include <linux/irqdomain.h> 13 15 #include <linux/mfd/syscon.h> 14 16 #include <linux/module.h> 15 17 #include <linux/of_device.h> 18 + #include <linux/of_irq.h> 16 19 #include <linux/phy/phy.h> 17 20 #include <linux/platform_device.h> 18 21 #include <linux/regmap.h> ··· 29 26 */ 30 27 #define HIWORD_UPDATE(mask, val) (((mask) << 16) | (val)) 31 28 #define HIWORD_UPDATE_BIT(val) HIWORD_UPDATE(val, val) 29 + #define HIWORD_DISABLE_BIT(val) HIWORD_UPDATE(val, ~val) 32 30 33 31 #define to_rockchip_pcie(x) dev_get_drvdata((x)->dev) 34 32 ··· 40 36 #define PCIE_LINKUP (PCIE_SMLH_LINKUP | PCIE_RDLH_LINKUP) 41 37 #define PCIE_L0S_ENTRY 0x11 42 38 #define PCIE_CLIENT_GENERAL_CONTROL 0x0 39 + #define PCIE_CLIENT_INTR_STATUS_LEGACY 0x8 40 + #define PCIE_CLIENT_INTR_MASK_LEGACY 0x1c 43 41 #define PCIE_CLIENT_GENERAL_DEBUG 0x104 44 - #define PCIE_CLIENT_HOT_RESET_CTRL 0x180 42 + #define PCIE_CLIENT_HOT_RESET_CTRL 0x180 45 43 #define PCIE_CLIENT_LTSSM_STATUS 0x300 46 - #define PCIE_LTSSM_ENABLE_ENHANCE BIT(4) 44 + #define PCIE_LTSSM_ENABLE_ENHANCE BIT(4) 47 45 #define PCIE_LTSSM_STATUS_MASK GENMASK(5, 0) 48 46 49 47 struct rockchip_pcie { ··· 57 51 struct reset_control *rst; 58 52 struct gpio_desc *rst_gpio; 59 53 struct regulator *vpcie3v3; 54 + struct irq_domain *irq_domain; 60 55 }; 61 56 62 57 static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip, ··· 70 63 u32 val, u32 reg) 71 64 { 72 65 writel_relaxed(val, rockchip->apb_base + reg); 66 + } 67 + 68 + static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc) 69 + { 70 + struct irq_chip *chip = irq_desc_get_chip(desc); 71 + struct rockchip_pcie *rockchip = irq_desc_get_handler_data(desc); 72 + unsigned long reg, hwirq; 73 + 74 + chained_irq_enter(chip, desc); 75 + 76 + reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_LEGACY); 77 + 78 + for_each_set_bit(hwirq, &reg, 4) 79 + generic_handle_domain_irq(rockchip->irq_domain, hwirq); 80 + 81 + chained_irq_exit(chip, desc); 82 + } 83 + 84 + static void rockchip_intx_mask(struct irq_data *data) 85 + { 86 + rockchip_pcie_writel_apb(irq_data_get_irq_chip_data(data), 87 + HIWORD_UPDATE_BIT(BIT(data->hwirq)), 88 + PCIE_CLIENT_INTR_MASK_LEGACY); 89 + }; 90 + 91 + static void rockchip_intx_unmask(struct irq_data *data) 92 + { 93 + rockchip_pcie_writel_apb(irq_data_get_irq_chip_data(data), 94 + HIWORD_DISABLE_BIT(BIT(data->hwirq)), 95 + PCIE_CLIENT_INTR_MASK_LEGACY); 96 + }; 97 + 98 + static struct irq_chip rockchip_intx_irq_chip = { 99 + .name = "INTx", 100 + .irq_mask = rockchip_intx_mask, 101 + .irq_unmask = rockchip_intx_unmask, 102 + .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, 103 + }; 104 + 105 + static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq, 106 + irq_hw_number_t hwirq) 107 + { 108 + irq_set_chip_and_handler(irq, &rockchip_intx_irq_chip, handle_level_irq); 109 + irq_set_chip_data(irq, domain->host_data); 110 + 111 + return 0; 112 + } 113 + 114 + static const struct irq_domain_ops intx_domain_ops = { 115 + .map = rockchip_pcie_intx_map, 116 + }; 117 + 118 + static int rockchip_pcie_init_irq_domain(struct rockchip_pcie *rockchip) 119 + { 120 + struct device *dev = rockchip->pci.dev; 121 + struct device_node *intc; 122 + 123 + intc = of_get_child_by_name(dev->of_node, "legacy-interrupt-controller"); 124 + if (!intc) { 125 + dev_err(dev, "missing child interrupt-controller node\n"); 126 + return -EINVAL; 127 + } 128 + 129 + rockchip->irq_domain = irq_domain_add_linear(intc, PCI_NUM_INTX, 130 + &intx_domain_ops, rockchip); 131 + of_node_put(intc); 132 + if (!rockchip->irq_domain) { 133 + dev_err(dev, "failed to get a INTx IRQ domain\n"); 134 + return -EINVAL; 135 + } 136 + 137 + return 0; 73 138 } 74 139 75 140 static void rockchip_pcie_enable_ltssm(struct rockchip_pcie *rockchip) ··· 190 111 { 191 112 struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 192 113 struct rockchip_pcie *rockchip = to_rockchip_pcie(pci); 114 + struct device *dev = rockchip->pci.dev; 193 115 u32 val = HIWORD_UPDATE_BIT(PCIE_LTSSM_ENABLE_ENHANCE); 116 + int irq, ret; 117 + 118 + irq = of_irq_get_byname(dev->of_node, "legacy"); 119 + if (irq < 0) 120 + return irq; 121 + 122 + ret = rockchip_pcie_init_irq_domain(rockchip); 123 + if (ret < 0) 124 + dev_err(dev, "failed to init irq domain\n"); 125 + 126 + irq_set_chained_handler_and_data(irq, rockchip_pcie_legacy_int_handler, 127 + rockchip); 194 128 195 129 /* LTSSM enable control mode */ 196 130 rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL);