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

Merge branch 'pci/controller/sophgo'

- Add DT binding and driver for Sophgo SG2044 PCIe controller driver in
Root Complex mode (Inochi Amaoto)

* pci/controller/sophgo:
PCI: dwc: Add Sophgo SG2044 PCIe controller driver in Root Complex mode
dt-bindings: pci: Add Sophgo SG2044 PCIe host

+390
+122
Documentation/devicetree/bindings/pci/sophgo,sg2044-pcie.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/pci/sophgo,sg2044-pcie.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: DesignWare based PCIe Root Complex controller on Sophgo SoCs 8 + 9 + maintainers: 10 + - Inochi Amaoto <inochiama@gmail.com> 11 + 12 + description: 13 + SG2044 SoC PCIe Root Complex controller is based on the Synopsys DesignWare 14 + PCIe IP and thus inherits all the common properties defined in 15 + snps,dw-pcie.yaml. 16 + 17 + allOf: 18 + - $ref: /schemas/pci/pci-host-bridge.yaml# 19 + - $ref: /schemas/pci/snps,dw-pcie.yaml# 20 + 21 + properties: 22 + compatible: 23 + const: sophgo,sg2044-pcie 24 + 25 + reg: 26 + items: 27 + - description: Data Bus Interface (DBI) registers 28 + - description: iATU registers 29 + - description: Config registers 30 + - description: Sophgo designed configuration registers 31 + 32 + reg-names: 33 + items: 34 + - const: dbi 35 + - const: atu 36 + - const: config 37 + - const: app 38 + 39 + clocks: 40 + items: 41 + - description: core clk 42 + 43 + clock-names: 44 + items: 45 + - const: core 46 + 47 + interrupt-controller: 48 + description: Interrupt controller node for handling legacy PCI interrupts. 49 + type: object 50 + 51 + properties: 52 + "#address-cells": 53 + const: 0 54 + 55 + "#interrupt-cells": 56 + const: 1 57 + 58 + interrupt-controller: true 59 + 60 + interrupts: 61 + items: 62 + - description: combined legacy interrupt 63 + 64 + required: 65 + - "#address-cells" 66 + - "#interrupt-cells" 67 + - interrupt-controller 68 + - interrupts 69 + 70 + additionalProperties: false 71 + 72 + msi-parent: true 73 + 74 + ranges: 75 + maxItems: 5 76 + 77 + required: 78 + - compatible 79 + - reg 80 + - clocks 81 + 82 + unevaluatedProperties: false 83 + 84 + examples: 85 + - | 86 + #include <dt-bindings/interrupt-controller/irq.h> 87 + 88 + soc { 89 + #address-cells = <2>; 90 + #size-cells = <2>; 91 + 92 + pcie@6c00400000 { 93 + compatible = "sophgo,sg2044-pcie"; 94 + reg = <0x6c 0x00400000 0x0 0x00001000>, 95 + <0x6c 0x00700000 0x0 0x00004000>, 96 + <0x40 0x00000000 0x0 0x00001000>, 97 + <0x6c 0x00780c00 0x0 0x00000400>; 98 + reg-names = "dbi", "atu", "config", "app"; 99 + #address-cells = <3>; 100 + #size-cells = <2>; 101 + bus-range = <0x00 0xff>; 102 + clocks = <&clk 0>; 103 + clock-names = "core"; 104 + device_type = "pci"; 105 + linux,pci-domain = <0>; 106 + msi-parent = <&msi>; 107 + ranges = <0x01000000 0x0 0x00000000 0x40 0x10000000 0x0 0x00200000>, 108 + <0x42000000 0x0 0x00000000 0x0 0x00000000 0x0 0x04000000>, 109 + <0x02000000 0x0 0x04000000 0x0 0x04000000 0x0 0x04000000>, 110 + <0x43000000 0x42 0x00000000 0x42 0x00000000 0x2 0x00000000>, 111 + <0x03000000 0x41 0x00000000 0x41 0x00000000 0x1 0x00000000>; 112 + 113 + interrupt-controller { 114 + #address-cells = <0>; 115 + #interrupt-cells = <1>; 116 + interrupt-controller; 117 + interrupt-parent = <&intc>; 118 + interrupts = <64 IRQ_TYPE_LEVEL_HIGH>; 119 + }; 120 + }; 121 + }; 122 + ...
+10
drivers/pci/controller/dwc/Kconfig
··· 404 404 Say Y here if you want PCIe endpoint controller support on 405 405 UniPhier SoCs. This driver supports Pro5 SoC. 406 406 407 + config PCIE_SOPHGO_DW 408 + bool "Sophgo DesignWare PCIe controller (host mode)" 409 + depends on ARCH_SOPHGO || COMPILE_TEST 410 + depends on PCI_MSI 411 + depends on OF 412 + select PCIE_DW_HOST 413 + help 414 + Say Y here if you want PCIe host controller support on 415 + Sophgo SoCs. 416 + 407 417 config PCIE_SPEAR13XX 408 418 bool "STMicroelectronics SPEAr PCIe controller" 409 419 depends on ARCH_SPEAR13XX || COMPILE_TEST
+1
drivers/pci/controller/dwc/Makefile
··· 20 20 obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o 21 21 obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o 22 22 obj-$(CONFIG_PCIE_ROCKCHIP_DW) += pcie-dw-rockchip.o 23 + obj-$(CONFIG_PCIE_SOPHGO_DW) += pcie-sophgo.o 23 24 obj-$(CONFIG_PCIE_INTEL_GW) += pcie-intel-gw.o 24 25 obj-$(CONFIG_PCIE_KEEMBAY) += pcie-keembay.o 25 26 obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o
+257
drivers/pci/controller/dwc/pcie-sophgo.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Sophgo DesignWare based PCIe host controller driver 4 + */ 5 + 6 + #include <linux/bits.h> 7 + #include <linux/clk.h> 8 + #include <linux/irqchip/chained_irq.h> 9 + #include <linux/irqdomain.h> 10 + #include <linux/module.h> 11 + #include <linux/property.h> 12 + #include <linux/platform_device.h> 13 + 14 + #include "pcie-designware.h" 15 + 16 + #define to_sophgo_pcie(x) dev_get_drvdata((x)->dev) 17 + 18 + #define PCIE_INT_SIGNAL 0xc48 19 + #define PCIE_INT_EN 0xca0 20 + 21 + #define PCIE_INT_SIGNAL_INTX GENMASK(8, 5) 22 + 23 + #define PCIE_INT_EN_INTX GENMASK(4, 1) 24 + #define PCIE_INT_EN_INT_MSI BIT(5) 25 + 26 + struct sophgo_pcie { 27 + struct dw_pcie pci; 28 + void __iomem *app_base; 29 + struct clk_bulk_data *clks; 30 + unsigned int clk_cnt; 31 + struct irq_domain *irq_domain; 32 + }; 33 + 34 + static int sophgo_pcie_readl_app(struct sophgo_pcie *sophgo, u32 reg) 35 + { 36 + return readl_relaxed(sophgo->app_base + reg); 37 + } 38 + 39 + static void sophgo_pcie_writel_app(struct sophgo_pcie *sophgo, u32 val, u32 reg) 40 + { 41 + writel_relaxed(val, sophgo->app_base + reg); 42 + } 43 + 44 + static void sophgo_pcie_intx_handler(struct irq_desc *desc) 45 + { 46 + struct dw_pcie_rp *pp = irq_desc_get_handler_data(desc); 47 + struct irq_chip *chip = irq_desc_get_chip(desc); 48 + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 49 + struct sophgo_pcie *sophgo = to_sophgo_pcie(pci); 50 + unsigned long hwirq, reg; 51 + 52 + chained_irq_enter(chip, desc); 53 + 54 + reg = sophgo_pcie_readl_app(sophgo, PCIE_INT_SIGNAL); 55 + reg = FIELD_GET(PCIE_INT_SIGNAL_INTX, reg); 56 + 57 + for_each_set_bit(hwirq, &reg, PCI_NUM_INTX) 58 + generic_handle_domain_irq(sophgo->irq_domain, hwirq); 59 + 60 + chained_irq_exit(chip, desc); 61 + } 62 + 63 + static void sophgo_intx_irq_mask(struct irq_data *d) 64 + { 65 + struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); 66 + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 67 + struct sophgo_pcie *sophgo = to_sophgo_pcie(pci); 68 + unsigned long flags; 69 + u32 val; 70 + 71 + raw_spin_lock_irqsave(&pp->lock, flags); 72 + 73 + val = sophgo_pcie_readl_app(sophgo, PCIE_INT_EN); 74 + val &= ~FIELD_PREP(PCIE_INT_EN_INTX, BIT(d->hwirq)); 75 + sophgo_pcie_writel_app(sophgo, val, PCIE_INT_EN); 76 + 77 + raw_spin_unlock_irqrestore(&pp->lock, flags); 78 + }; 79 + 80 + static void sophgo_intx_irq_unmask(struct irq_data *d) 81 + { 82 + struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); 83 + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 84 + struct sophgo_pcie *sophgo = to_sophgo_pcie(pci); 85 + unsigned long flags; 86 + u32 val; 87 + 88 + raw_spin_lock_irqsave(&pp->lock, flags); 89 + 90 + val = sophgo_pcie_readl_app(sophgo, PCIE_INT_EN); 91 + val |= FIELD_PREP(PCIE_INT_EN_INTX, BIT(d->hwirq)); 92 + sophgo_pcie_writel_app(sophgo, val, PCIE_INT_EN); 93 + 94 + raw_spin_unlock_irqrestore(&pp->lock, flags); 95 + }; 96 + 97 + static struct irq_chip sophgo_intx_irq_chip = { 98 + .name = "INTx", 99 + .irq_mask = sophgo_intx_irq_mask, 100 + .irq_unmask = sophgo_intx_irq_unmask, 101 + }; 102 + 103 + static int sophgo_pcie_intx_map(struct irq_domain *domain, unsigned int irq, 104 + irq_hw_number_t hwirq) 105 + { 106 + irq_set_chip_and_handler(irq, &sophgo_intx_irq_chip, handle_level_irq); 107 + irq_set_chip_data(irq, domain->host_data); 108 + 109 + return 0; 110 + } 111 + 112 + static const struct irq_domain_ops intx_domain_ops = { 113 + .map = sophgo_pcie_intx_map, 114 + }; 115 + 116 + static int sophgo_pcie_init_irq_domain(struct dw_pcie_rp *pp) 117 + { 118 + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 119 + struct sophgo_pcie *sophgo = to_sophgo_pcie(pci); 120 + struct device *dev = sophgo->pci.dev; 121 + struct fwnode_handle *intc; 122 + int irq; 123 + 124 + intc = device_get_named_child_node(dev, "interrupt-controller"); 125 + if (!intc) { 126 + dev_err(dev, "missing child interrupt-controller node\n"); 127 + return -ENODEV; 128 + } 129 + 130 + irq = fwnode_irq_get(intc, 0); 131 + if (irq < 0) { 132 + dev_err(dev, "failed to get INTx irq number\n"); 133 + fwnode_handle_put(intc); 134 + return irq; 135 + } 136 + 137 + sophgo->irq_domain = irq_domain_create_linear(intc, PCI_NUM_INTX, 138 + &intx_domain_ops, pp); 139 + fwnode_handle_put(intc); 140 + if (!sophgo->irq_domain) { 141 + dev_err(dev, "failed to get a INTx irq domain\n"); 142 + return -EINVAL; 143 + } 144 + 145 + return irq; 146 + } 147 + 148 + static void sophgo_pcie_msi_enable(struct dw_pcie_rp *pp) 149 + { 150 + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 151 + struct sophgo_pcie *sophgo = to_sophgo_pcie(pci); 152 + unsigned long flags; 153 + u32 val; 154 + 155 + raw_spin_lock_irqsave(&pp->lock, flags); 156 + 157 + val = sophgo_pcie_readl_app(sophgo, PCIE_INT_EN); 158 + val |= PCIE_INT_EN_INT_MSI; 159 + sophgo_pcie_writel_app(sophgo, val, PCIE_INT_EN); 160 + 161 + raw_spin_unlock_irqrestore(&pp->lock, flags); 162 + } 163 + 164 + static int sophgo_pcie_host_init(struct dw_pcie_rp *pp) 165 + { 166 + int irq; 167 + 168 + irq = sophgo_pcie_init_irq_domain(pp); 169 + if (irq < 0) 170 + return irq; 171 + 172 + irq_set_chained_handler_and_data(irq, sophgo_pcie_intx_handler, pp); 173 + 174 + sophgo_pcie_msi_enable(pp); 175 + 176 + return 0; 177 + } 178 + 179 + static const struct dw_pcie_host_ops sophgo_pcie_host_ops = { 180 + .init = sophgo_pcie_host_init, 181 + }; 182 + 183 + static int sophgo_pcie_clk_init(struct sophgo_pcie *sophgo) 184 + { 185 + struct device *dev = sophgo->pci.dev; 186 + int ret; 187 + 188 + ret = devm_clk_bulk_get_all_enabled(dev, &sophgo->clks); 189 + if (ret < 0) 190 + return dev_err_probe(dev, ret, "failed to get clocks\n"); 191 + 192 + sophgo->clk_cnt = ret; 193 + 194 + return 0; 195 + } 196 + 197 + static int sophgo_pcie_resource_get(struct platform_device *pdev, 198 + struct sophgo_pcie *sophgo) 199 + { 200 + sophgo->app_base = devm_platform_ioremap_resource_byname(pdev, "app"); 201 + if (IS_ERR(sophgo->app_base)) 202 + return dev_err_probe(&pdev->dev, PTR_ERR(sophgo->app_base), 203 + "failed to map app registers\n"); 204 + 205 + return 0; 206 + } 207 + 208 + static int sophgo_pcie_configure_rc(struct sophgo_pcie *sophgo) 209 + { 210 + struct dw_pcie_rp *pp; 211 + 212 + pp = &sophgo->pci.pp; 213 + pp->ops = &sophgo_pcie_host_ops; 214 + 215 + return dw_pcie_host_init(pp); 216 + } 217 + 218 + static int sophgo_pcie_probe(struct platform_device *pdev) 219 + { 220 + struct device *dev = &pdev->dev; 221 + struct sophgo_pcie *sophgo; 222 + int ret; 223 + 224 + sophgo = devm_kzalloc(dev, sizeof(*sophgo), GFP_KERNEL); 225 + if (!sophgo) 226 + return -ENOMEM; 227 + 228 + platform_set_drvdata(pdev, sophgo); 229 + 230 + sophgo->pci.dev = dev; 231 + 232 + ret = sophgo_pcie_resource_get(pdev, sophgo); 233 + if (ret) 234 + return ret; 235 + 236 + ret = sophgo_pcie_clk_init(sophgo); 237 + if (ret) 238 + return ret; 239 + 240 + return sophgo_pcie_configure_rc(sophgo); 241 + } 242 + 243 + static const struct of_device_id sophgo_pcie_of_match[] = { 244 + { .compatible = "sophgo,sg2044-pcie" }, 245 + { } 246 + }; 247 + MODULE_DEVICE_TABLE(of, sophgo_pcie_of_match); 248 + 249 + static struct platform_driver sophgo_pcie_driver = { 250 + .driver = { 251 + .name = "sophgo-pcie", 252 + .of_match_table = sophgo_pcie_of_match, 253 + .suppress_bind_attrs = true, 254 + }, 255 + .probe = sophgo_pcie_probe, 256 + }; 257 + builtin_platform_driver(sophgo_pcie_driver);