kconfig / kbuild: Re-sync with Linux 4.19
[oweals/u-boot.git] / drivers / pci / pcie_phytium.c
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  */
78 static int pci_phytium_conf_address(const struct udevice *bus, pci_dev_t bdf,
79                                     uint offset, void **paddress)
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  */
121 static int pci_phytium_read_config(const struct udevice *bus, pci_dev_t bdf,
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 /**
150  * pci_phytium_ofdata_to_platdata() - Translate from DT to device state
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  */
159 static int pci_phytium_ofdata_to_platdata(struct udevice *dev)
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, &reg_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(&reg_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,
197         .ofdata_to_platdata     = pci_phytium_ofdata_to_platdata,
198         .priv_auto_alloc_size   = sizeof(struct phytium_pcie),
199 };