blob: a80727625428c30445c7722c48781169607eab70 [file] [log] [blame]
liu hao1c4a2c42019-10-31 07:51:08 +00001// 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>
Simon Glass3ba929a2020-10-30 21:38:53 -060013#include <asm/global_data.h>
liu hao1c4a2c42019-10-31 07:51:08 +000014#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 */
20struct 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 */
32static 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
Pali Rohár23769352021-11-03 01:01:05 +010039 addr += PCIE_ECAM_OFFSET(PCI_BUS(parent), PCI_DEV(parent), PCI_FUNC(parent), 0);
liu hao1c4a2c42019-10-31 07:51:08 +000040
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 */
Simon Glass2a311e82020-01-27 08:49:37 -070077static int pci_phytium_conf_address(const struct udevice *bus, pci_dev_t bdf,
78 uint offset, void **paddress)
liu hao1c4a2c42019-10-31 07:51:08 +000079{
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;
Pali Rohár23769352021-11-03 01:01:05 +010090 addr += PCIE_ECAM_OFFSET(PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), 0);
liu hao1c4a2c42019-10-31 07:51:08 +000091
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 */
Simon Glass2a311e82020-01-27 08:49:37 -0700118static int pci_phytium_read_config(const struct udevice *bus, pci_dev_t bdf,
liu hao1c4a2c42019-10-31 07:51:08 +0000119 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 */
138static 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/**
Simon Glassaad29ae2020-12-03 16:55:21 -0700147 * pci_phytium_of_to_plat() - Translate from DT to device state
liu hao1c4a2c42019-10-31 07:51:08 +0000148 * @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 */
Simon Glassaad29ae2020-12-03 16:55:21 -0700156static int pci_phytium_of_to_plat(struct udevice *dev)
liu hao1c4a2c42019-10-31 07:51:08 +0000157{
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
179static const struct dm_pci_ops pci_phytium_ops = {
180 .read_config = pci_phytium_read_config,
181 .write_config = pci_phytium_write_config,
182};
183
184static const struct udevice_id pci_phytium_ids[] = {
185 { .compatible = "phytium,pcie-host-1.0" },
186 { }
187};
188
189U_BOOT_DRIVER(pci_phytium) = {
190 .name = "pci_phytium",
191 .id = UCLASS_PCI,
192 .of_match = pci_phytium_ids,
193 .ops = &pci_phytium_ops,
Simon Glassaad29ae2020-12-03 16:55:21 -0700194 .of_to_plat = pci_phytium_of_to_plat,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700195 .priv_auto = sizeof(struct phytium_pcie),
liu hao1c4a2c42019-10-31 07:51:08 +0000196};