]>
Commit | Line | Data |
---|---|---|
e3aafef4 | 1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Phytium PCIE host driver | |
4 | * | |
5 | * Heavily based on drivers/pci/pcie_xilinx.c | |
6 | * | |
7 | * Copyright (C) 2019 | |
8 | */ | |
9 | ||
10 | #include <common.h> | |
11 | #include <dm.h> | |
12 | #include <pci.h> | |
13 | #include <asm/io.h> | |
14 | ||
15 | /** | |
16 | * struct phytium_pcie - phytium PCIe controller state | |
17 | * @cfg_base: The base address of memory mapped configuration space | |
18 | */ | |
19 | struct phytium_pcie { | |
20 | void *cfg_base; | |
21 | }; | |
22 | ||
23 | /* | |
24 | * phytium_pci_skip_dev() | |
25 | * @parent: Identifies the PCIe device to access | |
26 | * | |
27 | * Checks whether the parent of the PCIe device is bridge | |
28 | * | |
29 | * Return: true if it is bridge, else false. | |
30 | */ | |
31 | static int phytium_pci_skip_dev(pci_dev_t parent) | |
32 | { | |
33 | unsigned char pos, id; | |
34 | unsigned long addr = 0x40000000; | |
35 | unsigned short capreg; | |
36 | unsigned char port_type; | |
37 | ||
38 | addr += PCI_BUS(parent) << 20; | |
39 | addr += PCI_DEV(parent) << 15; | |
40 | addr += PCI_FUNC(parent) << 12; | |
41 | ||
42 | pos = 0x34; | |
43 | while (1) { | |
44 | pos = readb(addr + pos); | |
45 | if (pos < 0x40) | |
46 | break; | |
47 | pos &= ~3; | |
48 | id = readb(addr + pos); | |
49 | if (id == 0xff) | |
50 | break; | |
51 | if (id == 0x10) { | |
52 | capreg = readw(addr + pos + 2); | |
53 | port_type = (capreg >> 4) & 0xf; | |
54 | if (port_type == 0x6 || port_type == 0x4) | |
55 | return 1; | |
56 | else | |
57 | return 0; | |
58 | } | |
59 | pos += 1; | |
60 | } | |
61 | return 0; | |
62 | } | |
63 | ||
64 | /** | |
65 | * pci_phytium_conf_address() - Calculate the address of a config access | |
66 | * @bus: Pointer to the PCI bus | |
67 | * @bdf: Identifies the PCIe device to access | |
68 | * @offset: The offset into the device's configuration space | |
69 | * @paddress: Pointer to the pointer to write the calculates address to | |
70 | * | |
71 | * Calculates the address that should be accessed to perform a PCIe | |
72 | * configuration space access for a given device identified by the PCIe | |
73 | * controller device @pcie and the bus, device & function numbers in @bdf. If | |
74 | * access to the device is not valid then the function will return an error | |
75 | * code. Otherwise the address to access will be written to the pointer pointed | |
76 | * to by @paddress. | |
77 | */ | |
c4e72c4a SG |
78 | static int pci_phytium_conf_address(const struct udevice *bus, pci_dev_t bdf, |
79 | uint offset, void **paddress) | |
e3aafef4 | 80 | { |
81 | struct phytium_pcie *pcie = dev_get_priv(bus); | |
82 | void *addr; | |
83 | pci_dev_t bdf_parent; | |
84 | ||
85 | unsigned int bus_no = PCI_BUS(bdf); | |
86 | unsigned int dev_no = PCI_DEV(bdf); | |
87 | ||
88 | bdf_parent = PCI_BDF((bus_no - 1), 0, 0); | |
89 | ||
90 | addr = pcie->cfg_base; | |
91 | addr += PCI_BUS(bdf) << 20; | |
92 | addr += PCI_DEV(bdf) << 15; | |
93 | addr += PCI_FUNC(bdf) << 12; | |
94 | ||
95 | if (bus_no > 0 && dev_no > 0) { | |
96 | if ((readb(addr + PCI_HEADER_TYPE) & 0x7f) != | |
97 | PCI_HEADER_TYPE_BRIDGE) | |
98 | return -ENODEV; | |
99 | if (phytium_pci_skip_dev(bdf_parent)) | |
100 | return -ENODEV; | |
101 | } | |
102 | ||
103 | addr += offset; | |
104 | *paddress = addr; | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
109 | /** | |
110 | * pci_phytium_read_config() - Read from configuration space | |
111 | * @bus: Pointer to the PCI bus | |
112 | * @bdf: Identifies the PCIe device to access | |
113 | * @offset: The offset into the device's configuration space | |
114 | * @valuep: A pointer at which to store the read value | |
115 | * @size: Indicates the size of access to perform | |
116 | * | |
117 | * Read a value of size @size from offset @offset within the configuration | |
118 | * space of the device identified by the bus, device & function numbers in @bdf | |
119 | * on the PCI bus @bus. | |
120 | */ | |
c4e72c4a | 121 | static int pci_phytium_read_config(const struct udevice *bus, pci_dev_t bdf, |
e3aafef4 | 122 | uint offset, ulong *valuep, |
123 | enum pci_size_t size) | |
124 | { | |
125 | return pci_generic_mmap_read_config(bus, pci_phytium_conf_address, | |
126 | bdf, offset, valuep, size); | |
127 | } | |
128 | ||
129 | /** | |
130 | * pci_phytium_write_config() - Write to configuration space | |
131 | * @bus: Pointer to the PCI bus | |
132 | * @bdf: Identifies the PCIe device to access | |
133 | * @offset: The offset into the device's configuration space | |
134 | * @value: The value to write | |
135 | * @size: Indicates the size of access to perform | |
136 | * | |
137 | * Write the value @value of size @size from offset @offset within the | |
138 | * configuration space of the device identified by the bus, device & function | |
139 | * numbers in @bdf on the PCI bus @bus. | |
140 | */ | |
141 | static int pci_phytium_write_config(struct udevice *bus, pci_dev_t bdf, | |
142 | uint offset, ulong value, | |
143 | enum pci_size_t size) | |
144 | { | |
145 | return pci_generic_mmap_write_config(bus, pci_phytium_conf_address, | |
146 | bdf, offset, value, size); | |
147 | } | |
148 | ||
149 | /** | |
d1998a9f | 150 | * pci_phytium_of_to_plat() - Translate from DT to device state |
e3aafef4 | 151 | * @dev: A pointer to the device being operated on |
152 | * | |
153 | * Translate relevant data from the device tree pertaining to device @dev into | |
154 | * state that the driver will later make use of. This state is stored in the | |
155 | * device's private data structure. | |
156 | * | |
157 | * Return: 0 on success, else -EINVAL | |
158 | */ | |
d1998a9f | 159 | static int pci_phytium_of_to_plat(struct udevice *dev) |
e3aafef4 | 160 | { |
161 | struct phytium_pcie *pcie = dev_get_priv(dev); | |
162 | struct fdt_resource reg_res; | |
163 | ||
164 | DECLARE_GLOBAL_DATA_PTR; | |
165 | ||
166 | int err; | |
167 | ||
168 | err = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), "reg", | |
169 | 0, ®_res); | |
170 | if (err < 0) { | |
171 | pr_err("\"reg\" resource not found\n"); | |
172 | return err; | |
173 | } | |
174 | ||
175 | pcie->cfg_base = map_physmem(reg_res.start, | |
176 | fdt_resource_size(®_res), | |
177 | MAP_NOCACHE); | |
178 | ||
179 | return 0; | |
180 | } | |
181 | ||
182 | static const struct dm_pci_ops pci_phytium_ops = { | |
183 | .read_config = pci_phytium_read_config, | |
184 | .write_config = pci_phytium_write_config, | |
185 | }; | |
186 | ||
187 | static const struct udevice_id pci_phytium_ids[] = { | |
188 | { .compatible = "phytium,pcie-host-1.0" }, | |
189 | { } | |
190 | }; | |
191 | ||
192 | U_BOOT_DRIVER(pci_phytium) = { | |
193 | .name = "pci_phytium", | |
194 | .id = UCLASS_PCI, | |
195 | .of_match = pci_phytium_ids, | |
196 | .ops = &pci_phytium_ops, | |
d1998a9f | 197 | .of_to_plat = pci_phytium_of_to_plat, |
41575d8e | 198 | .priv_auto = sizeof(struct phytium_pcie), |
e3aafef4 | 199 | }; |