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