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