// SPDX-License-Identifier: GPL-2.0 /* * Sophgo DesignWare based PCIe host controller driver */ #include #include #include #include #include #include #include #include "pcie-designware.h" #define to_sophgo_pcie(x) dev_get_drvdata((x)->dev) #define PCIE_INT_SIGNAL 0xc48 #define PCIE_INT_EN 0xca0 #define PCIE_INT_SIGNAL_INTX GENMASK(8, 5) #define PCIE_INT_EN_INTX GENMASK(4, 1) #define PCIE_INT_EN_INT_MSI BIT(5) struct sophgo_pcie { struct dw_pcie pci; void __iomem *app_base; struct clk_bulk_data *clks; unsigned int clk_cnt; struct irq_domain *irq_domain; }; static int sophgo_pcie_readl_app(struct sophgo_pcie *sophgo, u32 reg) { return readl_relaxed(sophgo->app_base + reg); } static void sophgo_pcie_writel_app(struct sophgo_pcie *sophgo, u32 val, u32 reg) { writel_relaxed(val, sophgo->app_base + reg); } static void sophgo_pcie_intx_handler(struct irq_desc *desc) { struct dw_pcie_rp *pp = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct sophgo_pcie *sophgo = to_sophgo_pcie(pci); unsigned long hwirq, reg; chained_irq_enter(chip, desc); reg = sophgo_pcie_readl_app(sophgo, PCIE_INT_SIGNAL); reg = FIELD_GET(PCIE_INT_SIGNAL_INTX, reg); for_each_set_bit(hwirq, ®, PCI_NUM_INTX) generic_handle_domain_irq(sophgo->irq_domain, hwirq); chained_irq_exit(chip, desc); } static void sophgo_intx_irq_mask(struct irq_data *d) { struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct sophgo_pcie *sophgo = to_sophgo_pcie(pci); unsigned long flags; u32 val; raw_spin_lock_irqsave(&pp->lock, flags); val = sophgo_pcie_readl_app(sophgo, PCIE_INT_EN); val &= ~FIELD_PREP(PCIE_INT_EN_INTX, BIT(d->hwirq)); sophgo_pcie_writel_app(sophgo, val, PCIE_INT_EN); raw_spin_unlock_irqrestore(&pp->lock, flags); }; static void sophgo_intx_irq_unmask(struct irq_data *d) { struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct sophgo_pcie *sophgo = to_sophgo_pcie(pci); unsigned long flags; u32 val; raw_spin_lock_irqsave(&pp->lock, flags); val = sophgo_pcie_readl_app(sophgo, PCIE_INT_EN); val |= FIELD_PREP(PCIE_INT_EN_INTX, BIT(d->hwirq)); sophgo_pcie_writel_app(sophgo, val, PCIE_INT_EN); raw_spin_unlock_irqrestore(&pp->lock, flags); }; static struct irq_chip sophgo_intx_irq_chip = { .name = "INTx", .irq_mask = sophgo_intx_irq_mask, .irq_unmask = sophgo_intx_irq_unmask, }; static int sophgo_pcie_intx_map(struct irq_domain *domain, unsigned int irq, irq_hw_number_t hwirq) { irq_set_chip_and_handler(irq, &sophgo_intx_irq_chip, handle_level_irq); irq_set_chip_data(irq, domain->host_data); return 0; } static const struct irq_domain_ops intx_domain_ops = { .map = sophgo_pcie_intx_map, }; static int sophgo_pcie_init_irq_domain(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct sophgo_pcie *sophgo = to_sophgo_pcie(pci); struct device *dev = sophgo->pci.dev; struct fwnode_handle *intc; int irq; intc = device_get_named_child_node(dev, "interrupt-controller"); if (!intc) { dev_err(dev, "missing child interrupt-controller node\n"); return -ENODEV; } irq = fwnode_irq_get(intc, 0); if (irq < 0) { dev_err(dev, "failed to get INTx irq number\n"); fwnode_handle_put(intc); return irq; } sophgo->irq_domain = irq_domain_create_linear(intc, PCI_NUM_INTX, &intx_domain_ops, pp); fwnode_handle_put(intc); if (!sophgo->irq_domain) { dev_err(dev, "failed to get a INTx irq domain\n"); return -EINVAL; } return irq; } static void sophgo_pcie_msi_enable(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct sophgo_pcie *sophgo = to_sophgo_pcie(pci); unsigned long flags; u32 val; raw_spin_lock_irqsave(&pp->lock, flags); val = sophgo_pcie_readl_app(sophgo, PCIE_INT_EN); val |= PCIE_INT_EN_INT_MSI; sophgo_pcie_writel_app(sophgo, val, PCIE_INT_EN); raw_spin_unlock_irqrestore(&pp->lock, flags); } static int sophgo_pcie_host_init(struct dw_pcie_rp *pp) { int irq; irq = sophgo_pcie_init_irq_domain(pp); if (irq < 0) return irq; irq_set_chained_handler_and_data(irq, sophgo_pcie_intx_handler, pp); sophgo_pcie_msi_enable(pp); return 0; } static const struct dw_pcie_host_ops sophgo_pcie_host_ops = { .init = sophgo_pcie_host_init, }; static int sophgo_pcie_clk_init(struct sophgo_pcie *sophgo) { struct device *dev = sophgo->pci.dev; int ret; ret = devm_clk_bulk_get_all_enabled(dev, &sophgo->clks); if (ret < 0) return dev_err_probe(dev, ret, "failed to get clocks\n"); sophgo->clk_cnt = ret; return 0; } static int sophgo_pcie_resource_get(struct platform_device *pdev, struct sophgo_pcie *sophgo) { sophgo->app_base = devm_platform_ioremap_resource_byname(pdev, "app"); if (IS_ERR(sophgo->app_base)) return dev_err_probe(&pdev->dev, PTR_ERR(sophgo->app_base), "failed to map app registers\n"); return 0; } static int sophgo_pcie_configure_rc(struct sophgo_pcie *sophgo) { struct dw_pcie_rp *pp; pp = &sophgo->pci.pp; pp->ops = &sophgo_pcie_host_ops; return dw_pcie_host_init(pp); } static int sophgo_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct sophgo_pcie *sophgo; int ret; sophgo = devm_kzalloc(dev, sizeof(*sophgo), GFP_KERNEL); if (!sophgo) return -ENOMEM; platform_set_drvdata(pdev, sophgo); sophgo->pci.dev = dev; ret = sophgo_pcie_resource_get(pdev, sophgo); if (ret) return ret; ret = sophgo_pcie_clk_init(sophgo); if (ret) return ret; return sophgo_pcie_configure_rc(sophgo); } static const struct of_device_id sophgo_pcie_of_match[] = { { .compatible = "sophgo,sg2044-pcie" }, { } }; MODULE_DEVICE_TABLE(of, sophgo_pcie_of_match); static struct platform_driver sophgo_pcie_driver = { .driver = { .name = "sophgo-pcie", .of_match_table = sophgo_pcie_of_match, .suppress_bind_attrs = true, }, .probe = sophgo_pcie_probe, }; builtin_platform_driver(sophgo_pcie_driver);