1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * PCIe controller driver for CIX's sky1 SoCs 4 * 5 * Copyright 2025 Cix Technology Group Co., Ltd. 6 * Author: Hans Zhang <hans.zhang@cixtech.com> 7 */ 8 9 #include <linux/kernel.h> 10 #include <linux/module.h> 11 #include <linux/of.h> 12 #include <linux/of_device.h> 13 #include <linux/pci.h> 14 #include <linux/pci-ecam.h> 15 #include <linux/pci_ids.h> 16 17 #include "pcie-cadence.h" 18 #include "pcie-cadence-host-common.h" 19 20 #define PCI_VENDOR_ID_CIX 0x1f6c 21 #define PCI_DEVICE_ID_CIX_SKY1 0x0001 22 23 #define STRAP_REG(n) ((n) * 0x04) 24 #define STATUS_REG(n) ((n) * 0x04) 25 #define LINK_TRAINING_ENABLE BIT(0) 26 #define LINK_COMPLETE BIT(0) 27 28 #define SKY1_IP_REG_BANK 0x1000 29 #define SKY1_IP_CFG_CTRL_REG_BANK 0x4c00 30 #define SKY1_IP_AXI_MASTER_COMMON 0xf000 31 #define SKY1_AXI_SLAVE 0x9000 32 #define SKY1_AXI_MASTER 0xb000 33 #define SKY1_AXI_HLS_REGISTERS 0xc000 34 #define SKY1_AXI_RAS_REGISTERS 0xe000 35 #define SKY1_DTI_REGISTERS 0xd000 36 37 #define IP_REG_I_DBG_STS_0 0x420 38 39 struct sky1_pcie { 40 struct cdns_pcie *cdns_pcie; 41 struct cdns_pcie_rc *cdns_pcie_rc; 42 43 struct resource *cfg_res; 44 struct resource *msg_res; 45 struct pci_config_window *cfg; 46 void __iomem *strap_base; 47 void __iomem *status_base; 48 void __iomem *reg_base; 49 void __iomem *cfg_base; 50 void __iomem *msg_base; 51 }; 52 53 static int sky1_pcie_resource_get(struct platform_device *pdev, 54 struct sky1_pcie *pcie) 55 { 56 struct device *dev = &pdev->dev; 57 struct resource *res; 58 void __iomem *base; 59 60 base = devm_platform_ioremap_resource_byname(pdev, "reg"); 61 if (IS_ERR(base)) 62 return dev_err_probe(dev, PTR_ERR(base), 63 "unable to find \"reg\" registers\n"); 64 pcie->reg_base = base; 65 66 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); 67 if (!res) 68 return dev_err_probe(dev, -ENODEV, "unable to get \"cfg\" resource\n"); 69 pcie->cfg_res = res; 70 71 base = devm_platform_ioremap_resource_byname(pdev, "rcsu_strap"); 72 if (IS_ERR(base)) 73 return dev_err_probe(dev, PTR_ERR(base), 74 "unable to find \"rcsu_strap\" registers\n"); 75 pcie->strap_base = base; 76 77 base = devm_platform_ioremap_resource_byname(pdev, "rcsu_status"); 78 if (IS_ERR(base)) 79 return dev_err_probe(dev, PTR_ERR(base), 80 "unable to find \"rcsu_status\" registers\n"); 81 pcie->status_base = base; 82 83 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "msg"); 84 if (!res) 85 return dev_err_probe(dev, -ENODEV, "unable to get \"msg\" resource\n"); 86 pcie->msg_res = res; 87 pcie->msg_base = devm_ioremap_resource(dev, res); 88 if (IS_ERR(pcie->msg_base)) { 89 return dev_err_probe(dev, PTR_ERR(pcie->msg_base), 90 "unable to ioremap msg resource\n"); 91 } 92 93 return 0; 94 } 95 96 static int sky1_pcie_start_link(struct cdns_pcie *cdns_pcie) 97 { 98 struct sky1_pcie *pcie = dev_get_drvdata(cdns_pcie->dev); 99 u32 val; 100 101 val = readl(pcie->strap_base + STRAP_REG(1)); 102 val |= LINK_TRAINING_ENABLE; 103 writel(val, pcie->strap_base + STRAP_REG(1)); 104 105 return 0; 106 } 107 108 static void sky1_pcie_stop_link(struct cdns_pcie *cdns_pcie) 109 { 110 struct sky1_pcie *pcie = dev_get_drvdata(cdns_pcie->dev); 111 u32 val; 112 113 val = readl(pcie->strap_base + STRAP_REG(1)); 114 val &= ~LINK_TRAINING_ENABLE; 115 writel(val, pcie->strap_base + STRAP_REG(1)); 116 } 117 118 static bool sky1_pcie_link_up(struct cdns_pcie *cdns_pcie) 119 { 120 u32 val; 121 122 val = cdns_pcie_hpa_readl(cdns_pcie, REG_BANK_IP_REG, 123 IP_REG_I_DBG_STS_0); 124 return val & LINK_COMPLETE; 125 } 126 127 static const struct cdns_pcie_ops sky1_pcie_ops = { 128 .start_link = sky1_pcie_start_link, 129 .stop_link = sky1_pcie_stop_link, 130 .link_up = sky1_pcie_link_up, 131 }; 132 133 static int sky1_pcie_probe(struct platform_device *pdev) 134 { 135 struct cdns_plat_pcie_of_data *reg_off; 136 struct device *dev = &pdev->dev; 137 struct pci_host_bridge *bridge; 138 struct cdns_pcie *cdns_pcie; 139 struct resource_entry *bus; 140 struct cdns_pcie_rc *rc; 141 struct sky1_pcie *pcie; 142 int ret; 143 144 pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); 145 if (!pcie) 146 return -ENOMEM; 147 148 bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); 149 if (!bridge) 150 return -ENOMEM; 151 152 ret = sky1_pcie_resource_get(pdev, pcie); 153 if (ret < 0) 154 return ret; 155 156 bus = resource_list_first_type(&bridge->windows, IORESOURCE_BUS); 157 if (!bus) 158 return -ENODEV; 159 160 pcie->cfg = pci_ecam_create(dev, pcie->cfg_res, bus->res, 161 &pci_generic_ecam_ops); 162 if (IS_ERR(pcie->cfg)) 163 return PTR_ERR(pcie->cfg); 164 165 bridge->ops = (struct pci_ops *)&pci_generic_ecam_ops.pci_ops; 166 rc = pci_host_bridge_priv(bridge); 167 rc->ecam_supported = 1; 168 rc->cfg_base = pcie->cfg->win; 169 rc->cfg_res = &pcie->cfg->res; 170 171 cdns_pcie = &rc->pcie; 172 cdns_pcie->dev = dev; 173 cdns_pcie->ops = &sky1_pcie_ops; 174 cdns_pcie->reg_base = pcie->reg_base; 175 cdns_pcie->msg_res = pcie->msg_res; 176 cdns_pcie->is_rc = 1; 177 178 reg_off = devm_kzalloc(dev, sizeof(*reg_off), GFP_KERNEL); 179 if (!reg_off) 180 return -ENOMEM; 181 182 reg_off->ip_reg_bank_offset = SKY1_IP_REG_BANK; 183 reg_off->ip_cfg_ctrl_reg_offset = SKY1_IP_CFG_CTRL_REG_BANK; 184 reg_off->axi_mstr_common_offset = SKY1_IP_AXI_MASTER_COMMON; 185 reg_off->axi_slave_offset = SKY1_AXI_SLAVE; 186 reg_off->axi_master_offset = SKY1_AXI_MASTER; 187 reg_off->axi_hls_offset = SKY1_AXI_HLS_REGISTERS; 188 reg_off->axi_ras_offset = SKY1_AXI_RAS_REGISTERS; 189 reg_off->axi_dti_offset = SKY1_DTI_REGISTERS; 190 cdns_pcie->cdns_pcie_reg_offsets = reg_off; 191 192 pcie->cdns_pcie = cdns_pcie; 193 pcie->cdns_pcie_rc = rc; 194 pcie->cfg_base = rc->cfg_base; 195 bridge->sysdata = pcie->cfg; 196 197 rc->vendor_id = PCI_VENDOR_ID_CIX; 198 rc->device_id = PCI_DEVICE_ID_CIX_SKY1; 199 rc->no_inbound_map = 1; 200 201 dev_set_drvdata(dev, pcie); 202 203 ret = cdns_pcie_hpa_host_setup(rc); 204 if (ret < 0) { 205 pci_ecam_free(pcie->cfg); 206 return ret; 207 } 208 209 return 0; 210 } 211 212 static const struct of_device_id of_sky1_pcie_match[] = { 213 { .compatible = "cix,sky1-pcie-host", }, 214 {}, 215 }; 216 MODULE_DEVICE_TABLE(of, of_sky1_pcie_match); 217 218 static void sky1_pcie_remove(struct platform_device *pdev) 219 { 220 struct sky1_pcie *pcie = platform_get_drvdata(pdev); 221 222 pci_ecam_free(pcie->cfg); 223 } 224 225 static struct platform_driver sky1_pcie_driver = { 226 .probe = sky1_pcie_probe, 227 .remove = sky1_pcie_remove, 228 .driver = { 229 .name = "sky1-pcie", 230 .of_match_table = of_sky1_pcie_match, 231 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 232 }, 233 }; 234 module_platform_driver(sky1_pcie_driver); 235 236 MODULE_LICENSE("GPL"); 237 MODULE_DESCRIPTION("PCIe controller driver for CIX's sky1 SoCs"); 238 MODULE_AUTHOR("Hans Zhang <hans.zhang@cixtech.com>"); 239