blob: 3bd1f5cd6d912e2f5155fa2d5bb662af08404a48 [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>
Simon Glassbdd5f812023-09-14 18:21:46 -060015#include <linux/printk.h>
liu hao1c4a2c42019-10-31 07:51:08 +000016
17/**
18 * struct phytium_pcie - phytium PCIe controller state
19 * @cfg_base: The base address of memory mapped configuration space
20 */
21struct phytium_pcie {
22 void *cfg_base;
23};
24
25/*
26 * phytium_pci_skip_dev()
27 * @parent: Identifies the PCIe device to access
28 *
29 * Checks whether the parent of the PCIe device is bridge
30 *
31 * Return: true if it is bridge, else false.
32 */
33static int phytium_pci_skip_dev(pci_dev_t parent)
34{
35 unsigned char pos, id;
36 unsigned long addr = 0x40000000;
37 unsigned short capreg;
38 unsigned char port_type;
39
Pali Rohár23769352021-11-03 01:01:05 +010040 addr += PCIE_ECAM_OFFSET(PCI_BUS(parent), PCI_DEV(parent), PCI_FUNC(parent), 0);
liu hao1c4a2c42019-10-31 07:51:08 +000041
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 */
Simon Glass2a311e82020-01-27 08:49:37 -070078static int pci_phytium_conf_address(const struct udevice *bus, pci_dev_t bdf,
79 uint offset, void **paddress)
liu hao1c4a2c42019-10-31 07:51:08 +000080{
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;
Pali Rohár23769352021-11-03 01:01:05 +010091 addr += PCIE_ECAM_OFFSET(PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), 0);
liu hao1c4a2c42019-10-31 07:51:08 +000092
93 if (bus_no > 0 && dev_no > 0) {
94 if ((readb(addr + PCI_HEADER_TYPE) & 0x7f) !=
95 PCI_HEADER_TYPE_BRIDGE)
96 return -ENODEV;
97 if (phytium_pci_skip_dev(bdf_parent))
98 return -ENODEV;
99 }
100
101 addr += offset;
102 *paddress = addr;
103
104 return 0;
105}
106
107/**
108 * pci_phytium_read_config() - Read from configuration space
109 * @bus: Pointer to the PCI bus
110 * @bdf: Identifies the PCIe device to access
111 * @offset: The offset into the device's configuration space
112 * @valuep: A pointer at which to store the read value
113 * @size: Indicates the size of access to perform
114 *
115 * Read a value of size @size from offset @offset within the configuration
116 * space of the device identified by the bus, device & function numbers in @bdf
117 * on the PCI bus @bus.
118 */
Simon Glass2a311e82020-01-27 08:49:37 -0700119static int pci_phytium_read_config(const struct udevice *bus, pci_dev_t bdf,
liu hao1c4a2c42019-10-31 07:51:08 +0000120 uint offset, ulong *valuep,
121 enum pci_size_t size)
122{
123 return pci_generic_mmap_read_config(bus, pci_phytium_conf_address,
124 bdf, offset, valuep, size);
125}
126
127/**
128 * pci_phytium_write_config() - Write to configuration space
129 * @bus: Pointer to the PCI bus
130 * @bdf: Identifies the PCIe device to access
131 * @offset: The offset into the device's configuration space
132 * @value: The value to write
133 * @size: Indicates the size of access to perform
134 *
135 * Write the value @value of size @size from offset @offset within the
136 * configuration space of the device identified by the bus, device & function
137 * numbers in @bdf on the PCI bus @bus.
138 */
139static int pci_phytium_write_config(struct udevice *bus, pci_dev_t bdf,
140 uint offset, ulong value,
141 enum pci_size_t size)
142{
143 return pci_generic_mmap_write_config(bus, pci_phytium_conf_address,
144 bdf, offset, value, size);
145}
146
147/**
Simon Glassaad29ae2020-12-03 16:55:21 -0700148 * pci_phytium_of_to_plat() - Translate from DT to device state
liu hao1c4a2c42019-10-31 07:51:08 +0000149 * @dev: A pointer to the device being operated on
150 *
151 * Translate relevant data from the device tree pertaining to device @dev into
152 * state that the driver will later make use of. This state is stored in the
153 * device's private data structure.
154 *
155 * Return: 0 on success, else -EINVAL
156 */
Simon Glassaad29ae2020-12-03 16:55:21 -0700157static int pci_phytium_of_to_plat(struct udevice *dev)
liu hao1c4a2c42019-10-31 07:51:08 +0000158{
159 struct phytium_pcie *pcie = dev_get_priv(dev);
160 struct fdt_resource reg_res;
161
162 DECLARE_GLOBAL_DATA_PTR;
163
164 int err;
165
166 err = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), "reg",
167 0, &reg_res);
168 if (err < 0) {
169 pr_err("\"reg\" resource not found\n");
170 return err;
171 }
172
173 pcie->cfg_base = map_physmem(reg_res.start,
174 fdt_resource_size(&reg_res),
175 MAP_NOCACHE);
176
177 return 0;
178}
179
180static const struct dm_pci_ops pci_phytium_ops = {
181 .read_config = pci_phytium_read_config,
182 .write_config = pci_phytium_write_config,
183};
184
185static const struct udevice_id pci_phytium_ids[] = {
186 { .compatible = "phytium,pcie-host-1.0" },
187 { }
188};
189
190U_BOOT_DRIVER(pci_phytium) = {
191 .name = "pci_phytium",
192 .id = UCLASS_PCI,
193 .of_match = pci_phytium_ids,
194 .ops = &pci_phytium_ops,
Simon Glassaad29ae2020-12-03 16:55:21 -0700195 .of_to_plat = pci_phytium_of_to_plat,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700196 .priv_auto = sizeof(struct phytium_pcie),
liu hao1c4a2c42019-10-31 07:51:08 +0000197};