Stefan Roese | 2f5ad77 | 2023-05-25 11:49:18 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * PCIe host bridge driver for Xilinx / AMD ZynqMP NWL PCIe Bridge |
| 4 | * |
| 5 | * Based on the Linux driver which is: |
| 6 | * (C) Copyright 2014 - 2015, Xilinx, Inc. |
| 7 | * |
| 8 | * Author: Stefan Roese <sr@denx.de> |
| 9 | */ |
| 10 | |
| 11 | #include <clk.h> |
| 12 | #include <dm.h> |
| 13 | #include <dm/device_compat.h> |
| 14 | #include <dm/devres.h> |
| 15 | #include <mapmem.h> |
| 16 | #include <pci.h> |
| 17 | #include <linux/delay.h> |
| 18 | #include <linux/io.h> |
| 19 | #include <linux/ioport.h> |
| 20 | |
| 21 | /* Bridge core config registers */ |
| 22 | #define BRCFG_PCIE_RX0 0x00000000 |
| 23 | #define BRCFG_PCIE_RX1 0x00000004 |
| 24 | #define BRCFG_INTERRUPT 0x00000010 |
| 25 | #define BRCFG_PCIE_RX_MSG_FILTER 0x00000020 |
| 26 | |
| 27 | /* Egress - Bridge translation registers */ |
| 28 | #define E_BREG_CAPABILITIES 0x00000200 |
| 29 | #define E_BREG_CONTROL 0x00000208 |
| 30 | #define E_BREG_BASE_LO 0x00000210 |
| 31 | #define E_BREG_BASE_HI 0x00000214 |
| 32 | #define E_ECAM_CAPABILITIES 0x00000220 |
| 33 | #define E_ECAM_CONTROL 0x00000228 |
| 34 | #define E_ECAM_BASE_LO 0x00000230 |
| 35 | #define E_ECAM_BASE_HI 0x00000234 |
| 36 | |
| 37 | #define I_ISUB_CONTROL 0x000003E8 |
| 38 | #define SET_ISUB_CONTROL BIT(0) |
| 39 | /* Rxed msg fifo - Interrupt status registers */ |
| 40 | #define MSGF_MISC_STATUS 0x00000400 |
| 41 | #define MSGF_MISC_MASK 0x00000404 |
| 42 | #define MSGF_LEG_STATUS 0x00000420 |
| 43 | #define MSGF_LEG_MASK 0x00000424 |
| 44 | #define MSGF_MSI_STATUS_LO 0x00000440 |
| 45 | #define MSGF_MSI_STATUS_HI 0x00000444 |
| 46 | #define MSGF_MSI_MASK_LO 0x00000448 |
| 47 | #define MSGF_MSI_MASK_HI 0x0000044C |
| 48 | |
| 49 | /* Msg filter mask bits */ |
| 50 | #define CFG_ENABLE_PM_MSG_FWD BIT(1) |
| 51 | #define CFG_ENABLE_INT_MSG_FWD BIT(2) |
| 52 | #define CFG_ENABLE_ERR_MSG_FWD BIT(3) |
| 53 | #define CFG_ENABLE_MSG_FILTER_MASK (CFG_ENABLE_PM_MSG_FWD | \ |
| 54 | CFG_ENABLE_INT_MSG_FWD | \ |
| 55 | CFG_ENABLE_ERR_MSG_FWD) |
| 56 | |
| 57 | /* Misc interrupt status mask bits */ |
| 58 | #define MSGF_MISC_SR_RXMSG_AVAIL BIT(0) |
| 59 | #define MSGF_MISC_SR_RXMSG_OVER BIT(1) |
| 60 | #define MSGF_MISC_SR_SLAVE_ERR BIT(4) |
| 61 | #define MSGF_MISC_SR_MASTER_ERR BIT(5) |
| 62 | #define MSGF_MISC_SR_I_ADDR_ERR BIT(6) |
| 63 | #define MSGF_MISC_SR_E_ADDR_ERR BIT(7) |
| 64 | #define MSGF_MISC_SR_FATAL_AER BIT(16) |
| 65 | #define MSGF_MISC_SR_NON_FATAL_AER BIT(17) |
| 66 | #define MSGF_MISC_SR_CORR_AER BIT(18) |
| 67 | #define MSGF_MISC_SR_UR_DETECT BIT(20) |
| 68 | #define MSGF_MISC_SR_NON_FATAL_DEV BIT(22) |
| 69 | #define MSGF_MISC_SR_FATAL_DEV BIT(23) |
| 70 | #define MSGF_MISC_SR_LINK_DOWN BIT(24) |
| 71 | #define MSGF_MSIC_SR_LINK_AUTO_BWIDTH BIT(25) |
| 72 | #define MSGF_MSIC_SR_LINK_BWIDTH BIT(26) |
| 73 | |
| 74 | #define MSGF_MISC_SR_MASKALL (MSGF_MISC_SR_RXMSG_AVAIL | \ |
| 75 | MSGF_MISC_SR_RXMSG_OVER | \ |
| 76 | MSGF_MISC_SR_SLAVE_ERR | \ |
| 77 | MSGF_MISC_SR_MASTER_ERR | \ |
| 78 | MSGF_MISC_SR_I_ADDR_ERR | \ |
| 79 | MSGF_MISC_SR_E_ADDR_ERR | \ |
| 80 | MSGF_MISC_SR_FATAL_AER | \ |
| 81 | MSGF_MISC_SR_NON_FATAL_AER | \ |
| 82 | MSGF_MISC_SR_CORR_AER | \ |
| 83 | MSGF_MISC_SR_UR_DETECT | \ |
| 84 | MSGF_MISC_SR_NON_FATAL_DEV | \ |
| 85 | MSGF_MISC_SR_FATAL_DEV | \ |
| 86 | MSGF_MISC_SR_LINK_DOWN | \ |
| 87 | MSGF_MSIC_SR_LINK_AUTO_BWIDTH | \ |
| 88 | MSGF_MSIC_SR_LINK_BWIDTH) |
| 89 | |
| 90 | /* Legacy interrupt status mask bits */ |
| 91 | #define MSGF_LEG_SR_INTA BIT(0) |
| 92 | #define MSGF_LEG_SR_INTB BIT(1) |
| 93 | #define MSGF_LEG_SR_INTC BIT(2) |
| 94 | #define MSGF_LEG_SR_INTD BIT(3) |
| 95 | #define MSGF_LEG_SR_MASKALL (MSGF_LEG_SR_INTA | MSGF_LEG_SR_INTB | \ |
| 96 | MSGF_LEG_SR_INTC | MSGF_LEG_SR_INTD) |
| 97 | |
| 98 | /* MSI interrupt status mask bits */ |
| 99 | #define MSGF_MSI_SR_LO_MASK GENMASK(31, 0) |
| 100 | #define MSGF_MSI_SR_HI_MASK GENMASK(31, 0) |
| 101 | |
| 102 | /* Bridge config interrupt mask */ |
| 103 | #define BRCFG_INTERRUPT_MASK BIT(0) |
| 104 | #define BREG_PRESENT BIT(0) |
| 105 | #define BREG_ENABLE BIT(0) |
| 106 | #define BREG_ENABLE_FORCE BIT(1) |
| 107 | |
| 108 | /* E_ECAM status mask bits */ |
| 109 | #define E_ECAM_PRESENT BIT(0) |
| 110 | #define E_ECAM_CR_ENABLE BIT(0) |
| 111 | #define E_ECAM_SIZE_LOC GENMASK(20, 16) |
| 112 | #define E_ECAM_SIZE_SHIFT 16 |
| 113 | #define NWL_ECAM_VALUE_DEFAULT 12 |
| 114 | |
| 115 | #define CFG_DMA_REG_BAR GENMASK(2, 0) |
| 116 | #define CFG_PCIE_CACHE GENMASK(7, 0) |
| 117 | |
| 118 | /* Readin the PS_LINKUP */ |
| 119 | #define PS_LINKUP_OFFSET 0x00000238 |
| 120 | #define PCIE_PHY_LINKUP_BIT BIT(0) |
| 121 | #define PHY_RDY_LINKUP_BIT BIT(1) |
| 122 | |
| 123 | /* Parameters for the waiting for link up routine */ |
| 124 | #define LINK_WAIT_MAX_RETRIES 10 |
| 125 | #define LINK_WAIT_USLEEP_MIN 90000 |
| 126 | #define LINK_WAIT_USLEEP_MAX 100000 |
| 127 | |
| 128 | struct nwl_pcie { |
| 129 | struct udevice *dev; |
| 130 | void __iomem *breg_base; |
| 131 | void __iomem *pcireg_base; |
| 132 | void __iomem *ecam_base; |
| 133 | phys_addr_t phys_breg_base; /* Physical Bridge Register Base */ |
| 134 | phys_addr_t phys_ecam_base; /* Physical Configuration Base */ |
| 135 | u32 ecam_value; |
| 136 | }; |
| 137 | |
| 138 | static int nwl_pcie_config_address(const struct udevice *bus, |
| 139 | pci_dev_t bdf, uint offset, |
| 140 | void **paddress) |
| 141 | { |
| 142 | struct nwl_pcie *pcie = dev_get_priv(bus); |
| 143 | void *addr; |
| 144 | |
| 145 | addr = pcie->ecam_base; |
| 146 | addr += PCIE_ECAM_OFFSET(PCI_BUS(bdf) - dev_seq(bus), |
| 147 | PCI_DEV(bdf), PCI_FUNC(bdf), offset); |
| 148 | *paddress = addr; |
| 149 | |
| 150 | return 0; |
| 151 | } |
| 152 | |
| 153 | static int nwl_pcie_read_config(const struct udevice *bus, pci_dev_t bdf, |
| 154 | uint offset, ulong *valuep, |
| 155 | enum pci_size_t size) |
| 156 | { |
| 157 | return pci_generic_mmap_read_config(bus, nwl_pcie_config_address, |
| 158 | bdf, offset, valuep, size); |
| 159 | } |
| 160 | |
| 161 | static int nwl_pcie_write_config(struct udevice *bus, pci_dev_t bdf, |
| 162 | uint offset, ulong value, |
| 163 | enum pci_size_t size) |
| 164 | { |
| 165 | return pci_generic_mmap_write_config(bus, nwl_pcie_config_address, |
| 166 | bdf, offset, value, size); |
| 167 | } |
| 168 | |
| 169 | static const struct dm_pci_ops nwl_pcie_ops = { |
| 170 | .read_config = nwl_pcie_read_config, |
| 171 | .write_config = nwl_pcie_write_config, |
| 172 | }; |
| 173 | |
| 174 | static inline u32 nwl_bridge_readl(struct nwl_pcie *pcie, u32 off) |
| 175 | { |
| 176 | return readl(pcie->breg_base + off); |
| 177 | } |
| 178 | |
| 179 | static inline void nwl_bridge_writel(struct nwl_pcie *pcie, u32 val, u32 off) |
| 180 | { |
| 181 | writel(val, pcie->breg_base + off); |
| 182 | } |
| 183 | |
| 184 | static bool nwl_pcie_link_up(struct nwl_pcie *pcie) |
| 185 | { |
| 186 | if (readl(pcie->pcireg_base + PS_LINKUP_OFFSET) & PCIE_PHY_LINKUP_BIT) |
| 187 | return true; |
| 188 | return false; |
| 189 | } |
| 190 | |
| 191 | static bool nwl_phy_link_up(struct nwl_pcie *pcie) |
| 192 | { |
| 193 | if (readl(pcie->pcireg_base + PS_LINKUP_OFFSET) & PHY_RDY_LINKUP_BIT) |
| 194 | return true; |
| 195 | return false; |
| 196 | } |
| 197 | |
| 198 | static int nwl_wait_for_link(struct nwl_pcie *pcie) |
| 199 | { |
| 200 | struct udevice *dev = pcie->dev; |
| 201 | int retries; |
| 202 | |
| 203 | /* check if the link is up or not */ |
| 204 | for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { |
| 205 | if (nwl_phy_link_up(pcie)) |
| 206 | return 0; |
| 207 | udelay(LINK_WAIT_USLEEP_MIN); |
| 208 | } |
| 209 | |
| 210 | dev_warn(dev, "PHY link never came up\n"); |
| 211 | return -ETIMEDOUT; |
| 212 | } |
| 213 | |
| 214 | static int nwl_pcie_bridge_init(struct nwl_pcie *pcie) |
| 215 | { |
| 216 | struct udevice *dev = pcie->dev; |
| 217 | u32 breg_val, ecam_val; |
| 218 | int err; |
| 219 | |
| 220 | breg_val = nwl_bridge_readl(pcie, E_BREG_CAPABILITIES) & BREG_PRESENT; |
| 221 | if (!breg_val) { |
| 222 | dev_err(dev, "BREG is not present\n"); |
| 223 | return breg_val; |
| 224 | } |
| 225 | |
| 226 | /* Write bridge_off to breg base */ |
| 227 | nwl_bridge_writel(pcie, lower_32_bits(pcie->phys_breg_base), |
| 228 | E_BREG_BASE_LO); |
| 229 | nwl_bridge_writel(pcie, upper_32_bits(pcie->phys_breg_base), |
| 230 | E_BREG_BASE_HI); |
| 231 | |
| 232 | /* Enable BREG */ |
| 233 | nwl_bridge_writel(pcie, ~BREG_ENABLE_FORCE & BREG_ENABLE, |
| 234 | E_BREG_CONTROL); |
| 235 | |
| 236 | /* Disable DMA channel registers */ |
| 237 | nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, BRCFG_PCIE_RX0) | |
| 238 | CFG_DMA_REG_BAR, BRCFG_PCIE_RX0); |
| 239 | |
| 240 | /* Enable Ingress subtractive decode translation */ |
| 241 | nwl_bridge_writel(pcie, SET_ISUB_CONTROL, I_ISUB_CONTROL); |
| 242 | |
| 243 | /* Enable msg filtering details */ |
| 244 | nwl_bridge_writel(pcie, CFG_ENABLE_MSG_FILTER_MASK, |
| 245 | BRCFG_PCIE_RX_MSG_FILTER); |
| 246 | |
| 247 | err = nwl_wait_for_link(pcie); |
| 248 | if (err) |
| 249 | return err; |
| 250 | |
| 251 | ecam_val = nwl_bridge_readl(pcie, E_ECAM_CAPABILITIES) & E_ECAM_PRESENT; |
| 252 | if (!ecam_val) { |
| 253 | dev_err(dev, "ECAM is not present\n"); |
| 254 | return ecam_val; |
| 255 | } |
| 256 | |
| 257 | /* Enable ECAM */ |
| 258 | nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, E_ECAM_CONTROL) | |
| 259 | E_ECAM_CR_ENABLE, E_ECAM_CONTROL); |
| 260 | |
| 261 | nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, E_ECAM_CONTROL) | |
| 262 | (pcie->ecam_value << E_ECAM_SIZE_SHIFT), |
| 263 | E_ECAM_CONTROL); |
| 264 | |
| 265 | nwl_bridge_writel(pcie, lower_32_bits(pcie->phys_ecam_base), |
| 266 | E_ECAM_BASE_LO); |
| 267 | nwl_bridge_writel(pcie, upper_32_bits(pcie->phys_ecam_base), |
| 268 | E_ECAM_BASE_HI); |
| 269 | |
| 270 | if (nwl_pcie_link_up(pcie)) |
| 271 | dev_info(dev, "Link is UP\n"); |
| 272 | else |
| 273 | dev_info(dev, "Link is DOWN\n"); |
| 274 | |
| 275 | /* Disable all misc interrupts */ |
| 276 | nwl_bridge_writel(pcie, (u32)~MSGF_MISC_SR_MASKALL, MSGF_MISC_MASK); |
| 277 | |
| 278 | /* Clear pending misc interrupts */ |
| 279 | nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MISC_STATUS) & |
| 280 | MSGF_MISC_SR_MASKALL, MSGF_MISC_STATUS); |
| 281 | |
| 282 | /* Disable all legacy interrupts */ |
| 283 | nwl_bridge_writel(pcie, (u32)~MSGF_LEG_SR_MASKALL, MSGF_LEG_MASK); |
| 284 | |
| 285 | /* Clear pending legacy interrupts */ |
| 286 | nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_LEG_STATUS) & |
| 287 | MSGF_LEG_SR_MASKALL, MSGF_LEG_STATUS); |
| 288 | |
| 289 | return 0; |
| 290 | } |
| 291 | |
| 292 | static int nwl_pcie_parse_dt(struct nwl_pcie *pcie) |
| 293 | { |
| 294 | struct udevice *dev = pcie->dev; |
| 295 | struct resource res; |
| 296 | int ret; |
| 297 | |
| 298 | ret = dev_read_resource_byname(dev, "breg", &res); |
| 299 | if (ret) |
| 300 | return ret; |
| 301 | pcie->breg_base = devm_ioremap(dev, res.start, resource_size(&res)); |
| 302 | if (IS_ERR(pcie->breg_base)) |
| 303 | return PTR_ERR(pcie->breg_base); |
| 304 | pcie->phys_breg_base = res.start; |
| 305 | |
| 306 | ret = dev_read_resource_byname(dev, "cfg", &res); |
| 307 | if (ret) |
| 308 | return ret; |
| 309 | pcie->ecam_base = devm_ioremap(dev, res.start, resource_size(&res)); |
| 310 | if (IS_ERR(pcie->ecam_base)) |
| 311 | return PTR_ERR(pcie->ecam_base); |
| 312 | pcie->phys_ecam_base = res.start; |
| 313 | |
| 314 | return 0; |
| 315 | } |
| 316 | |
| 317 | static int nwl_pcie_probe(struct udevice *dev) |
| 318 | { |
| 319 | struct nwl_pcie *pcie = dev_get_priv(dev); |
| 320 | int err; |
| 321 | |
| 322 | pcie->dev = dev; |
| 323 | pcie->ecam_value = NWL_ECAM_VALUE_DEFAULT; |
| 324 | |
| 325 | err = nwl_pcie_parse_dt(pcie); |
| 326 | if (err) { |
| 327 | dev_err(dev, "Parsing DT failed\n"); |
| 328 | return err; |
| 329 | } |
| 330 | |
| 331 | err = nwl_pcie_bridge_init(pcie); |
| 332 | if (err) { |
| 333 | dev_err(dev, "HW Initialization failed\n"); |
| 334 | return err; |
| 335 | } |
| 336 | |
| 337 | return 0; |
| 338 | } |
| 339 | |
| 340 | static const struct udevice_id nwl_pcie_of_match[] = { |
| 341 | { .compatible = "xlnx,nwl-pcie-2.11", }, |
| 342 | { /* sentinel */ } |
| 343 | }; |
| 344 | |
| 345 | U_BOOT_DRIVER(nwl_pcie) = { |
| 346 | .name = "nwl-pcie", |
| 347 | .id = UCLASS_PCI, |
| 348 | .of_match = nwl_pcie_of_match, |
| 349 | .probe = nwl_pcie_probe, |
| 350 | .priv_auto = sizeof(struct nwl_pcie), |
| 351 | .ops = &nwl_pcie_ops, |
| 352 | }; |