Merge tag 'u-boot-at91-2022.04-a' of https://source.denx.de/u-boot/custodians/u-boot...
[platform/kernel/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/global_data.h>
14 #include <asm/io.h>
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
39         addr += PCIE_ECAM_OFFSET(PCI_BUS(parent), PCI_DEV(parent), PCI_FUNC(parent), 0);
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  */
77 static int pci_phytium_conf_address(const struct udevice *bus, pci_dev_t bdf,
78                                     uint offset, void **paddress)
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;
90         addr += PCIE_ECAM_OFFSET(PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), 0);
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  */
118 static int pci_phytium_read_config(const struct udevice *bus, pci_dev_t bdf,
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 /**
147  * pci_phytium_of_to_plat() - Translate from DT to device state
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  */
156 static int pci_phytium_of_to_plat(struct udevice *dev)
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, &reg_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(&reg_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,
194         .of_to_plat     = pci_phytium_of_to_plat,
195         .priv_auto      = sizeof(struct phytium_pcie),
196 };