FILE
pcie-nxp-s32g.c
File preview with syntax highlighting.
linux-6.19.14/drivers/pci/controller/dwc/pcie-nxp-s32g.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * PCIe host controller driver for NXP S32G SoCs 4 * 5 * Copyright 2019-2025 NXP 6 */ 7 8#include <linux/interrupt.h> 9#include <linux/io.h> 10#include <linux/module.h> 11#include <linux/of_device.h> 12#include <linux/of_address.h> 13#include <linux/pci.h> 14#include <linux/phy/phy.h> 15#include <linux/platform_device.h> 16#include <linux/pm_runtime.h> 17#include <linux/sizes.h> 18#include <linux/types.h> 19 20#include "pcie-designware.h" 21 22/* PCIe controller Sub-System */ 23 24/* PCIe controller 0 General Control 1 */ 25#define PCIE_S32G_PE0_GEN_CTRL_1 0x50 26#define DEVICE_TYPE_MASK GENMASK(3, 0) 27#define SRIS_MODE BIT(8) 28 29/* PCIe controller 0 General Control 3 */ 30#define PCIE_S32G_PE0_GEN_CTRL_3 0x58 31#define LTSSM_EN BIT(0) 32 33/* PCIe Controller 0 Interrupt Status */ 34#define PCIE_S32G_PE0_INT_STS 0xE8 35#define HP_INT_STS BIT(6) 36 37/* Boundary between peripheral space and physical memory space */ 38#define S32G_MEMORY_BOUNDARY_ADDR 0x80000000 39 40struct s32g_pcie_port { 41 struct list_head list; 42 struct phy *phy; 43}; 44 45struct s32g_pcie { 46 struct dw_pcie pci; 47 void __iomem *ctrl_base; 48 struct list_head ports; 49}; 50 51#define to_s32g_from_dw_pcie(x) \ 52 container_of(x, struct s32g_pcie, pci) 53 54static void s32g_pcie_writel_ctrl(struct s32g_pcie *s32g_pp, u32 reg, u32 val) 55{ 56 writel(val, s32g_pp->ctrl_base + reg); 57} 58 59static u32 s32g_pcie_readl_ctrl(struct s32g_pcie *s32g_pp, u32 reg) 60{ 61 return readl(s32g_pp->ctrl_base + reg); 62} 63 64static void s32g_pcie_enable_ltssm(struct s32g_pcie *s32g_pp) 65{ 66 u32 reg; 67 68 reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3); 69 reg |= LTSSM_EN; 70 s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg); 71} 72 73static void s32g_pcie_disable_ltssm(struct s32g_pcie *s32g_pp) 74{ 75 u32 reg; 76 77 reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3); 78 reg &= ~LTSSM_EN; 79 s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg); 80} 81 82static int s32g_pcie_start_link(struct dw_pcie *pci) 83{ 84 struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); 85 86 s32g_pcie_enable_ltssm(s32g_pp); 87 88 return 0; 89} 90 91static void s32g_pcie_stop_link(struct dw_pcie *pci) 92{ 93 struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); 94 95 s32g_pcie_disable_ltssm(s32g_pp); 96} 97 98static struct dw_pcie_ops s32g_pcie_ops = { 99 .start_link = s32g_pcie_start_link, 100 .stop_link = s32g_pcie_stop_link, 101}; 102 103/* Configure the AMBA AXI Coherency Extensions (ACE) interface */ 104static void s32g_pcie_reset_mstr_ace(struct dw_pcie *pci) 105{ 106 u32 ddr_base_low = lower_32_bits(S32G_MEMORY_BOUNDARY_ADDR); 107 u32 ddr_base_high = upper_32_bits(S32G_MEMORY_BOUNDARY_ADDR); 108 109 dw_pcie_dbi_ro_wr_en(pci); 110 dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_3_OFF, 0x0); 111 112 /* 113 * Ncore is a cache-coherent interconnect module that enables the 114 * integration of heterogeneous coherent and non-coherent agents in 115 * the chip. Ncore transactions to peripheral should be non-coherent 116 * or it might drop them. 117 * 118 * One example where this is needed are PCIe MSIs, which use NoSnoop=0 119 * and might end up routed to Ncore. PCIe coherent traffic (e.g. MSIs) 120 * that targets peripheral space will be dropped by Ncore because 121 * peripherals on S32G are not coherent as slaves. We add a hard 122 * boundary in the PCIe controller coherency control registers to 123 * separate physical memory space from peripheral space. 124 * 125 * Define the start of DDR as seen by Linux as this boundary between 126 * "memory" and "peripherals", with peripherals being below. 127 */ 128 dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_1_OFF, 129 (ddr_base_low & CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK)); 130 dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_2_OFF, ddr_base_high); 131 dw_pcie_dbi_ro_wr_dis(pci); 132} 133 134static int s32g_init_pcie_controller(struct dw_pcie_rp *pp) 135{ 136 struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 137 struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci); 138 u32 val; 139 140 /* Set RP mode */ 141 val = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1); 142 val &= ~DEVICE_TYPE_MASK; 143 val |= FIELD_PREP(DEVICE_TYPE_MASK, PCI_EXP_TYPE_ROOT_PORT); 144 145 /* Use default CRNS */ 146 val &= ~SRIS_MODE; 147 148 s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1, val); 149 150 /* 151 * Make sure we use the coherency defaults (just in case the settings 152 * have been changed from their reset values) 153 */ 154 s32g_pcie_reset_mstr_ace(pci); 155 156 dw_pcie_dbi_ro_wr_en(pci); 157 158 val = dw_pcie_readl_dbi(pci, PCIE_PORT_FORCE); 159 val |= PORT_FORCE_DO_DESKEW_FOR_SRIS; 160 dw_pcie_writel_dbi(pci, PCIE_PORT_FORCE, val); 161 162 val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); 163 val |= GEN3_RELATED_OFF_EQ_PHASE_2_3; 164 dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); 165 166 dw_pcie_dbi_ro_wr_dis(pci); 167 168 return 0; 169} 170 171static const struct dw_pcie_host_ops s32g_pcie_host_ops = { 172 .init = s32g_init_pcie_controller, 173}; 174 175static int s32g_init_pcie_phy(struct s32g_pcie *s32g_pp) 176{ 177 struct dw_pcie *pci = &s32g_pp->pci; 178 struct device *dev = pci->dev; 179 struct s32g_pcie_port *port, *tmp; 180 int ret; 181 182 list_for_each_entry(port, &s32g_pp->ports, list) { 183 ret = phy_init(port->phy); 184 if (ret) { 185 dev_err(dev, "Failed to init serdes PHY\n"); 186 goto err_phy_revert; 187 } 188 189 ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, 0); 190 if (ret) { 191 dev_err(dev, "Failed to set mode on serdes PHY\n"); 192 goto err_phy_exit; 193 } 194 195 ret = phy_power_on(port->phy); 196 if (ret) { 197 dev_err(dev, "Failed to power on serdes PHY\n"); 198 goto err_phy_exit; 199 } 200 } 201 202 return 0; 203 204err_phy_exit: 205 phy_exit(port->phy); 206 207err_phy_revert: 208 list_for_each_entry_continue_reverse(port, &s32g_pp->ports, list) { 209 phy_power_off(port->phy); 210 phy_exit(port->phy); 211 } 212 213 list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) 214 list_del(&port->list); 215 216 return ret; 217} 218 219static void s32g_deinit_pcie_phy(struct s32g_pcie *s32g_pp) 220{ 221 struct s32g_pcie_port *port, *tmp; 222 223 list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) { 224 phy_power_off(port->phy); 225 phy_exit(port->phy); 226 list_del(&port->list); 227 } 228} 229 230static int s32g_pcie_init(struct device *dev, struct s32g_pcie *s32g_pp) 231{ 232 s32g_pcie_disable_ltssm(s32g_pp); 233 234 return s32g_init_pcie_phy(s32g_pp); 235} 236 237static void s32g_pcie_deinit(struct s32g_pcie *s32g_pp) 238{ 239 s32g_pcie_disable_ltssm(s32g_pp); 240 241 s32g_deinit_pcie_phy(s32g_pp); 242} 243 244static int s32g_pcie_parse_port(struct s32g_pcie *s32g_pp, struct device_node *node) 245{ 246 struct device *dev = s32g_pp->pci.dev; 247 struct s32g_pcie_port *port; 248 int num_lanes; 249 250 port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); 251 if (!port) 252 return -ENOMEM; 253 254 port->phy = devm_of_phy_get(dev, node, NULL); 255 if (IS_ERR(port->phy)) 256 return dev_err_probe(dev, PTR_ERR(port->phy), 257 "Failed to get serdes PHY\n"); 258 259 INIT_LIST_HEAD(&port->list); 260 list_add_tail(&port->list, &s32g_pp->ports); 261 262 /* 263 * The DWC core initialization code cannot yet parse the num-lanes 264 * attribute in the Root Port node. The S32G only supports one Root 265 * Port for now so its driver can parse the node and set the num_lanes 266 * field of struct dwc_pcie before calling dw_pcie_host_init(). 267 */ 268 if (!of_property_read_u32(node, "num-lanes", &num_lanes)) 269 s32g_pp->pci.num_lanes = num_lanes; 270 271 return 0; 272} 273 274static int s32g_pcie_parse_ports(struct device *dev, struct s32g_pcie *s32g_pp) 275{ 276 struct s32g_pcie_port *port, *tmp; 277 int ret = -ENOENT; 278 279 for_each_available_child_of_node_scoped(dev->of_node, of_port) { 280 if (!of_node_is_type(of_port, "pci")) 281 continue; 282 283 ret = s32g_pcie_parse_port(s32g_pp, of_port); 284 if (ret) 285 break; 286 } 287 288 if (ret) 289 list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) 290 list_del(&port->list); 291 292 return ret; 293} 294 295static int s32g_pcie_get_resources(struct platform_device *pdev, 296 struct s32g_pcie *s32g_pp) 297{ 298 struct dw_pcie *pci = &s32g_pp->pci; 299 struct device *dev = &pdev->dev; 300 int ret; 301 302 pci->dev = dev; 303 pci->ops = &s32g_pcie_ops; 304 305 s32g_pp->ctrl_base = devm_platform_ioremap_resource_byname(pdev, "ctrl"); 306 if (IS_ERR(s32g_pp->ctrl_base)) 307 return PTR_ERR(s32g_pp->ctrl_base); 308 309 INIT_LIST_HEAD(&s32g_pp->ports); 310 311 ret = s32g_pcie_parse_ports(dev, s32g_pp); 312 if (ret) 313 return dev_err_probe(dev, ret, 314 "Failed to parse Root Port: %d\n", ret); 315 316 platform_set_drvdata(pdev, s32g_pp); 317 318 return 0; 319} 320 321static int s32g_pcie_probe(struct platform_device *pdev) 322{ 323 struct device *dev = &pdev->dev; 324 struct s32g_pcie *s32g_pp; 325 struct dw_pcie_rp *pp; 326 int ret; 327 328 s32g_pp = devm_kzalloc(dev, sizeof(*s32g_pp), GFP_KERNEL); 329 if (!s32g_pp) 330 return -ENOMEM; 331 332 ret = s32g_pcie_get_resources(pdev, s32g_pp); 333 if (ret) 334 return ret; 335 336 pm_runtime_no_callbacks(dev); 337 devm_pm_runtime_enable(dev); 338 ret = pm_runtime_get_sync(dev); 339 if (ret < 0) 340 goto err_pm_runtime_put; 341 342 ret = s32g_pcie_init(dev, s32g_pp); 343 if (ret) 344 goto err_pm_runtime_put; 345 346 pp = &s32g_pp->pci.pp; 347 pp->ops = &s32g_pcie_host_ops; 348 pp->use_atu_msg = true; 349 350 ret = dw_pcie_host_init(pp); 351 if (ret) 352 goto err_pcie_deinit; 353 354 return 0; 355 356err_pcie_deinit: 357 s32g_pcie_deinit(s32g_pp); 358err_pm_runtime_put: 359 pm_runtime_put(dev); 360 361 return ret; 362} 363 364static int s32g_pcie_suspend_noirq(struct device *dev) 365{ 366 struct s32g_pcie *s32g_pp = dev_get_drvdata(dev); 367 struct dw_pcie *pci = &s32g_pp->pci; 368 369 return dw_pcie_suspend_noirq(pci); 370} 371 372static int s32g_pcie_resume_noirq(struct device *dev) 373{ 374 struct s32g_pcie *s32g_pp = dev_get_drvdata(dev); 375 struct dw_pcie *pci = &s32g_pp->pci; 376 377 return dw_pcie_resume_noirq(pci); 378} 379 380static const struct dev_pm_ops s32g_pcie_pm_ops = { 381 NOIRQ_SYSTEM_SLEEP_PM_OPS(s32g_pcie_suspend_noirq, 382 s32g_pcie_resume_noirq) 383}; 384 385static const struct of_device_id s32g_pcie_of_match[] = { 386 { .compatible = "nxp,s32g2-pcie" }, 387 { /* sentinel */ }, 388}; 389MODULE_DEVICE_TABLE(of, s32g_pcie_of_match); 390 391static struct platform_driver s32g_pcie_driver = { 392 .driver = { 393 .name = "s32g-pcie", 394 .of_match_table = s32g_pcie_of_match, 395 .suppress_bind_attrs = true, 396 .pm = pm_sleep_ptr(&s32g_pcie_pm_ops), 397 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 398 }, 399 .probe = s32g_pcie_probe, 400}; 401 402builtin_platform_driver(s32g_pcie_driver); 403 404MODULE_AUTHOR("Ionut Vicovan <Ionut.Vicovan@nxp.com>"); 405MODULE_DESCRIPTION("NXP S32G PCIe Host controller driver"); 406MODULE_LICENSE("GPL");
~/
Read-only file preview.