1// SPDX-License-Identifier: GPL-2.0
2/*
3 * PCIe host controller driver for HiSilicon SoCs
4 *
5 * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com
6 *
7 * Authors: Zhou Wang <wangzhou1@hisilicon.com>
8 *          Dacai Zhu <zhudacai@hisilicon.com>
9 *          Gabriele Paoloni <gabriele.paoloni@huawei.com>
10 */
11#include <linux/interrupt.h>
12#include <linux/init.h>
13#include <linux/platform_device.h>
14#include <linux/pci.h>
15#include <linux/pci-acpi.h>
16#include <linux/pci-ecam.h>
17#include "../../pci.h"
18
19#if defined(CONFIG_PCI_HISI) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS))
20
21static int hisi_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
22			     int size, u32 *val)
23{
24	struct pci_config_window *cfg = bus->sysdata;
25	int dev = PCI_SLOT(devfn);
26
27	if (bus->number == cfg->busr.start) {
28		/* access only one slot on each root port */
29		if (dev > 0)
30			return PCIBIOS_DEVICE_NOT_FOUND;
31		else
32			return pci_generic_config_read32(bus, devfn, where,
33							 size, val);
34	}
35
36	return pci_generic_config_read(bus, devfn, where, size, val);
37}
38
39static int hisi_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
40			     int where, int size, u32 val)
41{
42	struct pci_config_window *cfg = bus->sysdata;
43	int dev = PCI_SLOT(devfn);
44
45	if (bus->number == cfg->busr.start) {
46		/* access only one slot on each root port */
47		if (dev > 0)
48			return PCIBIOS_DEVICE_NOT_FOUND;
49		else
50			return pci_generic_config_write32(bus, devfn, where,
51							  size, val);
52	}
53
54	return pci_generic_config_write(bus, devfn, where, size, val);
55}
56
57static void __iomem *hisi_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
58				       int where)
59{
60	struct pci_config_window *cfg = bus->sysdata;
61	void __iomem *reg_base = cfg->priv;
62
63	if (bus->number == cfg->busr.start)
64		return reg_base + where;
65	else
66		return pci_ecam_map_bus(bus, devfn, where);
67}
68
69#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
70
71static int hisi_pcie_init(struct pci_config_window *cfg)
72{
73	struct device *dev = cfg->parent;
74	struct acpi_device *adev = to_acpi_device(dev);
75	struct acpi_pci_root *root = acpi_driver_data(adev);
76	struct resource *res;
77	void __iomem *reg_base;
78	int ret;
79
80	/*
81	 * Retrieve RC base and size from a HISI0081 device with _UID
82	 * matching our segment.
83	 */
84	res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL);
85	if (!res)
86		return -ENOMEM;
87
88	ret = acpi_get_rc_resources(dev, "HISI0081", root->segment, res);
89	if (ret) {
90		dev_err(dev, "can't get rc base address\n");
91		return -ENOMEM;
92	}
93
94	reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res));
95	if (!reg_base)
96		return -ENOMEM;
97
98	cfg->priv = reg_base;
99	return 0;
100}
101
102const struct pci_ecam_ops hisi_pcie_ops = {
103	.bus_shift    = 20,
104	.init         =  hisi_pcie_init,
105	.pci_ops      = {
106		.map_bus    = hisi_pcie_map_bus,
107		.read       = hisi_pcie_rd_conf,
108		.write      = hisi_pcie_wr_conf,
109	}
110};
111
112#endif
113
114#ifdef CONFIG_PCI_HISI
115
116static int hisi_pcie_platform_init(struct pci_config_window *cfg)
117{
118	struct device *dev = cfg->parent;
119	struct platform_device *pdev = to_platform_device(dev);
120	struct resource *res;
121	void __iomem *reg_base;
122
123	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
124	if (!res) {
125		dev_err(dev, "missing \"reg[1]\"property\n");
126		return -EINVAL;
127	}
128
129	reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res));
130	if (!reg_base)
131		return -ENOMEM;
132
133	cfg->priv = reg_base;
134	return 0;
135}
136
137static const struct pci_ecam_ops hisi_pcie_platform_ops = {
138	.bus_shift    = 20,
139	.init         =  hisi_pcie_platform_init,
140	.pci_ops      = {
141		.map_bus    = hisi_pcie_map_bus,
142		.read       = hisi_pcie_rd_conf,
143		.write      = hisi_pcie_wr_conf,
144	}
145};
146
147static const struct of_device_id hisi_pcie_almost_ecam_of_match[] = {
148	{
149		.compatible =  "hisilicon,hip06-pcie-ecam",
150		.data	    =  &hisi_pcie_platform_ops,
151	},
152	{
153		.compatible =  "hisilicon,hip07-pcie-ecam",
154		.data       =  &hisi_pcie_platform_ops,
155	},
156	{},
157};
158
159static struct platform_driver hisi_pcie_almost_ecam_driver = {
160	.probe  = pci_host_common_probe,
161	.driver = {
162		   .name = "hisi-pcie-almost-ecam",
163		   .of_match_table = hisi_pcie_almost_ecam_of_match,
164		   .suppress_bind_attrs = true,
165	},
166};
167builtin_platform_driver(hisi_pcie_almost_ecam_driver);
168
169#endif
170#endif
171