]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0 |
3675cb04 TT |
2 | /* |
3 | * Generic PCIE host provided by e.g. QEMU | |
4 | * | |
5 | * Heavily based on drivers/pci/pcie_xilinx.c | |
6 | * | |
7 | * Copyright (C) 2016 Imagination Technologies | |
3675cb04 TT |
8 | */ |
9 | ||
d678a59d | 10 | #include <common.h> |
3675cb04 TT |
11 | #include <dm.h> |
12 | #include <pci.h> | |
67c7f14a | 13 | #include <linux/ioport.h> |
1e94b46f | 14 | #include <linux/printk.h> |
3675cb04 TT |
15 | |
16 | #include <asm/io.h> | |
17 | ||
4f2e2280 AD |
18 | #define TYPE_PCI 0x1 |
19 | ||
3675cb04 TT |
20 | /** |
21 | * struct generic_ecam_pcie - generic_ecam PCIe controller state | |
22 | * @cfg_base: The base address of memory mapped configuration space | |
23 | */ | |
24 | struct generic_ecam_pcie { | |
25 | void *cfg_base; | |
f83567e0 VO |
26 | pci_size_t size; |
27 | int first_busno; | |
3675cb04 TT |
28 | }; |
29 | ||
30 | /** | |
31 | * pci_generic_ecam_conf_address() - Calculate the address of a config access | |
32 | * @bus: Pointer to the PCI bus | |
33 | * @bdf: Identifies the PCIe device to access | |
34 | * @offset: The offset into the device's configuration space | |
35 | * @paddress: Pointer to the pointer to write the calculates address to | |
36 | * | |
37 | * Calculates the address that should be accessed to perform a PCIe | |
38 | * configuration space access for a given device identified by the PCIe | |
39 | * controller device @pcie and the bus, device & function numbers in @bdf. If | |
40 | * access to the device is not valid then the function will return an error | |
41 | * code. Otherwise the address to access will be written to the pointer pointed | |
42 | * to by @paddress. | |
43 | */ | |
c4e72c4a SG |
44 | static int pci_generic_ecam_conf_address(const struct udevice *bus, |
45 | pci_dev_t bdf, uint offset, | |
46 | void **paddress) | |
3675cb04 TT |
47 | { |
48 | struct generic_ecam_pcie *pcie = dev_get_priv(bus); | |
49 | void *addr; | |
50 | ||
51 | addr = pcie->cfg_base; | |
4f2e2280 AD |
52 | |
53 | if (dev_get_driver_data(bus) == TYPE_PCI) { | |
54 | addr += ((PCI_BUS(bdf) - pcie->first_busno) << 16) | | |
55 | (PCI_DEV(bdf) << 11) | (PCI_FUNC(bdf) << 8) | offset; | |
56 | } else { | |
57 | addr += PCIE_ECAM_OFFSET(PCI_BUS(bdf) - pcie->first_busno, | |
58 | PCI_DEV(bdf), PCI_FUNC(bdf), offset); | |
59 | } | |
3675cb04 TT |
60 | *paddress = addr; |
61 | ||
62 | return 0; | |
63 | } | |
64 | ||
f83567e0 VO |
65 | static bool pci_generic_ecam_addr_valid(const struct udevice *bus, |
66 | pci_dev_t bdf) | |
67 | { | |
68 | struct generic_ecam_pcie *pcie = dev_get_priv(bus); | |
69 | int num_buses = DIV_ROUND_UP(pcie->size, 1 << 16); | |
70 | ||
71 | return (PCI_BUS(bdf) >= pcie->first_busno && | |
72 | PCI_BUS(bdf) < pcie->first_busno + num_buses); | |
73 | } | |
74 | ||
3675cb04 TT |
75 | /** |
76 | * pci_generic_ecam_read_config() - Read from configuration space | |
77 | * @bus: Pointer to the PCI bus | |
78 | * @bdf: Identifies the PCIe device to access | |
79 | * @offset: The offset into the device's configuration space | |
80 | * @valuep: A pointer at which to store the read value | |
81 | * @size: Indicates the size of access to perform | |
82 | * | |
83 | * Read a value of size @size from offset @offset within the configuration | |
84 | * space of the device identified by the bus, device & function numbers in @bdf | |
85 | * on the PCI bus @bus. | |
86 | */ | |
c4e72c4a SG |
87 | static int pci_generic_ecam_read_config(const struct udevice *bus, |
88 | pci_dev_t bdf, uint offset, | |
89 | ulong *valuep, enum pci_size_t size) | |
3675cb04 | 90 | { |
f83567e0 VO |
91 | if (!pci_generic_ecam_addr_valid(bus, bdf)) { |
92 | *valuep = pci_get_ff(size); | |
93 | return 0; | |
94 | } | |
95 | ||
3675cb04 TT |
96 | return pci_generic_mmap_read_config(bus, pci_generic_ecam_conf_address, |
97 | bdf, offset, valuep, size); | |
98 | } | |
99 | ||
100 | /** | |
101 | * pci_generic_ecam_write_config() - Write to configuration space | |
102 | * @bus: Pointer to the PCI bus | |
103 | * @bdf: Identifies the PCIe device to access | |
104 | * @offset: The offset into the device's configuration space | |
105 | * @value: The value to write | |
106 | * @size: Indicates the size of access to perform | |
107 | * | |
108 | * Write the value @value of size @size from offset @offset within the | |
109 | * configuration space of the device identified by the bus, device & function | |
110 | * numbers in @bdf on the PCI bus @bus. | |
111 | */ | |
112 | static int pci_generic_ecam_write_config(struct udevice *bus, pci_dev_t bdf, | |
113 | uint offset, ulong value, | |
114 | enum pci_size_t size) | |
115 | { | |
f83567e0 VO |
116 | if (!pci_generic_ecam_addr_valid(bus, bdf)) |
117 | return 0; | |
118 | ||
3675cb04 TT |
119 | return pci_generic_mmap_write_config(bus, pci_generic_ecam_conf_address, |
120 | bdf, offset, value, size); | |
121 | } | |
122 | ||
123 | /** | |
d1998a9f | 124 | * pci_generic_ecam_of_to_plat() - Translate from DT to device state |
3675cb04 TT |
125 | * @dev: A pointer to the device being operated on |
126 | * | |
127 | * Translate relevant data from the device tree pertaining to device @dev into | |
128 | * state that the driver will later make use of. This state is stored in the | |
129 | * device's private data structure. | |
130 | * | |
131 | * Return: 0 on success, else -EINVAL | |
132 | */ | |
d1998a9f | 133 | static int pci_generic_ecam_of_to_plat(struct udevice *dev) |
3675cb04 TT |
134 | { |
135 | struct generic_ecam_pcie *pcie = dev_get_priv(dev); | |
67c7f14a MK |
136 | ofnode node = dev_ofnode(dev); |
137 | struct resource reg_res; | |
3675cb04 TT |
138 | int err; |
139 | ||
67c7f14a | 140 | err = ofnode_read_resource(node, 0, ®_res); |
3675cb04 TT |
141 | if (err < 0) { |
142 | pr_err("\"reg\" resource not found\n"); | |
143 | return err; | |
144 | } | |
145 | ||
67c7f14a | 146 | pcie->size = resource_size(®_res); |
f83567e0 VO |
147 | pcie->cfg_base = map_physmem(reg_res.start, pcie->size, MAP_NOCACHE); |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
152 | static int pci_generic_ecam_probe(struct udevice *dev) | |
153 | { | |
154 | struct generic_ecam_pcie *pcie = dev_get_priv(dev); | |
155 | ||
8b85dfc6 | 156 | pcie->first_busno = dev_seq(dev); |
3675cb04 TT |
157 | |
158 | return 0; | |
159 | } | |
160 | ||
161 | static const struct dm_pci_ops pci_generic_ecam_ops = { | |
162 | .read_config = pci_generic_ecam_read_config, | |
163 | .write_config = pci_generic_ecam_write_config, | |
164 | }; | |
165 | ||
166 | static const struct udevice_id pci_generic_ecam_ids[] = { | |
4f2e2280 AD |
167 | { .compatible = "pci-host-ecam-generic" /* PCI-E */ }, |
168 | { .compatible = "pci-host-cam-generic", .data = TYPE_PCI }, | |
3675cb04 TT |
169 | { } |
170 | }; | |
171 | ||
172 | U_BOOT_DRIVER(pci_generic_ecam) = { | |
173 | .name = "pci_generic_ecam", | |
174 | .id = UCLASS_PCI, | |
175 | .of_match = pci_generic_ecam_ids, | |
176 | .ops = &pci_generic_ecam_ops, | |
f83567e0 | 177 | .probe = pci_generic_ecam_probe, |
d1998a9f | 178 | .of_to_plat = pci_generic_ecam_of_to_plat, |
41575d8e | 179 | .priv_auto = sizeof(struct generic_ecam_pcie), |
3675cb04 | 180 | }; |