File Index
Files and directories
Sign in
FILE

pci-sky1.c

File preview with syntax highlighting.

linux-6.19.14/drivers/pci/controller/cadence/pci-sky1.c C
  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
 39struct 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
 53static 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
 96static 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
108static 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
118static 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
127static 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
133static 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
212static const struct of_device_id of_sky1_pcie_match[] = {
213	{ .compatible = "cix,sky1-pcie-host", },
214	{},
215};
216MODULE_DEVICE_TABLE(of, of_sky1_pcie_match);
217
218static 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
225static 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};
234module_platform_driver(sky1_pcie_driver);
235
236MODULE_LICENSE("GPL");
237MODULE_DESCRIPTION("PCIe controller driver for CIX's sky1 SoCs");
238MODULE_AUTHOR("Hans Zhang <hans.zhang@cixtech.com>");
~/ Read-only file preview.