blob: 2e089b0e0332e0ac983bd806e777cb5f227d66fc [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0
Tuomas Tynkkynena765f712017-09-19 23:18:06 +03002/*
3 * Generic PCIE host provided by e.g. QEMU
4 *
5 * Heavily based on drivers/pci/pcie_xilinx.c
6 *
7 * Copyright (C) 2016 Imagination Technologies
Tuomas Tynkkynena765f712017-09-19 23:18:06 +03008 */
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>
Simon Glassbdd5f812023-09-14 18:21:46 -060014#include <linux/printk.h>
Tuomas Tynkkynena765f712017-09-19 23:18:06 +030015
16#include <asm/io.h>
17
Alistair Delva2f285cd2021-10-20 21:31:34 +000018#define TYPE_PCI 0x1
19
Tuomas Tynkkynena765f712017-09-19 23:18:06 +030020/**
21 * struct generic_ecam_pcie - generic_ecam PCIe controller state
22 * @cfg_base: The base address of memory mapped configuration space
23 */
24struct generic_ecam_pcie {
25 void *cfg_base;
Vladimir Oltean34e46e02020-03-13 16:53:06 +020026 pci_size_t size;
27 int first_busno;
Tuomas Tynkkynena765f712017-09-19 23:18:06 +030028};
29
30/**
31 * pci_generic_ecam_conf_address() - Calculate the address of a config access
32 * @bus: Pointer to the PCI bus
33 * @bdf: Identifies the PCIe device to access
34 * @offset: The offset into the device's configuration space
35 * @paddress: Pointer to the pointer to write the calculates address to
36 *
37 * Calculates the address that should be accessed to perform a PCIe
38 * configuration space access for a given device identified by the PCIe
39 * controller device @pcie and the bus, device & function numbers in @bdf. If
40 * access to the device is not valid then the function will return an error
41 * code. Otherwise the address to access will be written to the pointer pointed
42 * to by @paddress.
43 */
Simon Glass2a311e82020-01-27 08:49:37 -070044static int pci_generic_ecam_conf_address(const struct udevice *bus,
45 pci_dev_t bdf, uint offset,
46 void **paddress)
Tuomas Tynkkynena765f712017-09-19 23:18:06 +030047{
48 struct generic_ecam_pcie *pcie = dev_get_priv(bus);
49 void *addr;
50
51 addr = pcie->cfg_base;
Alistair Delva2f285cd2021-10-20 21:31:34 +000052
53 if (dev_get_driver_data(bus) == TYPE_PCI) {
54 addr += ((PCI_BUS(bdf) - pcie->first_busno) << 16) |
55 (PCI_DEV(bdf) << 11) | (PCI_FUNC(bdf) << 8) | offset;
56 } else {
57 addr += PCIE_ECAM_OFFSET(PCI_BUS(bdf) - pcie->first_busno,
58 PCI_DEV(bdf), PCI_FUNC(bdf), offset);
59 }
Tuomas Tynkkynena765f712017-09-19 23:18:06 +030060 *paddress = addr;
61
62 return 0;
63}
64
Vladimir Oltean34e46e02020-03-13 16:53:06 +020065static bool pci_generic_ecam_addr_valid(const struct udevice *bus,
66 pci_dev_t bdf)
67{
68 struct generic_ecam_pcie *pcie = dev_get_priv(bus);
69 int num_buses = DIV_ROUND_UP(pcie->size, 1 << 16);
70
71 return (PCI_BUS(bdf) >= pcie->first_busno &&
72 PCI_BUS(bdf) < pcie->first_busno + num_buses);
73}
74
Tuomas Tynkkynena765f712017-09-19 23:18:06 +030075/**
76 * pci_generic_ecam_read_config() - Read from configuration space
77 * @bus: Pointer to the PCI bus
78 * @bdf: Identifies the PCIe device to access
79 * @offset: The offset into the device's configuration space
80 * @valuep: A pointer at which to store the read value
81 * @size: Indicates the size of access to perform
82 *
83 * Read a value of size @size from offset @offset within the configuration
84 * space of the device identified by the bus, device & function numbers in @bdf
85 * on the PCI bus @bus.
86 */
Simon Glass2a311e82020-01-27 08:49:37 -070087static int pci_generic_ecam_read_config(const struct udevice *bus,
88 pci_dev_t bdf, uint offset,
89 ulong *valuep, enum pci_size_t size)
Tuomas Tynkkynena765f712017-09-19 23:18:06 +030090{
Vladimir Oltean34e46e02020-03-13 16:53:06 +020091 if (!pci_generic_ecam_addr_valid(bus, bdf)) {
92 *valuep = pci_get_ff(size);
93 return 0;
94 }
95
Tuomas Tynkkynena765f712017-09-19 23:18:06 +030096 return pci_generic_mmap_read_config(bus, pci_generic_ecam_conf_address,
97 bdf, offset, valuep, size);
98}
99
100/**
101 * pci_generic_ecam_write_config() - Write to configuration space
102 * @bus: Pointer to the PCI bus
103 * @bdf: Identifies the PCIe device to access
104 * @offset: The offset into the device's configuration space
105 * @value: The value to write
106 * @size: Indicates the size of access to perform
107 *
108 * Write the value @value of size @size from offset @offset within the
109 * configuration space of the device identified by the bus, device & function
110 * numbers in @bdf on the PCI bus @bus.
111 */
112static int pci_generic_ecam_write_config(struct udevice *bus, pci_dev_t bdf,
113 uint offset, ulong value,
114 enum pci_size_t size)
115{
Vladimir Oltean34e46e02020-03-13 16:53:06 +0200116 if (!pci_generic_ecam_addr_valid(bus, bdf))
117 return 0;
118
Tuomas Tynkkynena765f712017-09-19 23:18:06 +0300119 return pci_generic_mmap_write_config(bus, pci_generic_ecam_conf_address,
120 bdf, offset, value, size);
121}
122
123/**
Simon Glassaad29ae2020-12-03 16:55:21 -0700124 * pci_generic_ecam_of_to_plat() - Translate from DT to device state
Tuomas Tynkkynena765f712017-09-19 23:18:06 +0300125 * @dev: A pointer to the device being operated on
126 *
127 * Translate relevant data from the device tree pertaining to device @dev into
128 * state that the driver will later make use of. This state is stored in the
129 * device's private data structure.
130 *
131 * Return: 0 on success, else -EINVAL
132 */
Simon Glassaad29ae2020-12-03 16:55:21 -0700133static int pci_generic_ecam_of_to_plat(struct udevice *dev)
Tuomas Tynkkynena765f712017-09-19 23:18:06 +0300134{
135 struct generic_ecam_pcie *pcie = dev_get_priv(dev);
136 struct fdt_resource reg_res;
137 DECLARE_GLOBAL_DATA_PTR;
138 int err;
139
140 err = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), "reg",
141 0, &reg_res);
142 if (err < 0) {
143 pr_err("\"reg\" resource not found\n");
144 return err;
145 }
146
Vladimir Oltean34e46e02020-03-13 16:53:06 +0200147 pcie->size = fdt_resource_size(&reg_res);
148 pcie->cfg_base = map_physmem(reg_res.start, pcie->size, MAP_NOCACHE);
Tuomas Tynkkynena765f712017-09-19 23:18:06 +0300149
150 return 0;
151}
152
Vladimir Oltean34e46e02020-03-13 16:53:06 +0200153static int pci_generic_ecam_probe(struct udevice *dev)
154{
155 struct generic_ecam_pcie *pcie = dev_get_priv(dev);
156
Simon Glass75e534b2020-12-16 21:20:07 -0700157 pcie->first_busno = dev_seq(dev);
Vladimir Oltean34e46e02020-03-13 16:53:06 +0200158
159 return 0;
160}
161
Tuomas Tynkkynena765f712017-09-19 23:18:06 +0300162static const struct dm_pci_ops pci_generic_ecam_ops = {
163 .read_config = pci_generic_ecam_read_config,
164 .write_config = pci_generic_ecam_write_config,
165};
166
167static const struct udevice_id pci_generic_ecam_ids[] = {
Alistair Delva2f285cd2021-10-20 21:31:34 +0000168 { .compatible = "pci-host-ecam-generic" /* PCI-E */ },
169 { .compatible = "pci-host-cam-generic", .data = TYPE_PCI },
Tuomas Tynkkynena765f712017-09-19 23:18:06 +0300170 { }
171};
172
173U_BOOT_DRIVER(pci_generic_ecam) = {
174 .name = "pci_generic_ecam",
175 .id = UCLASS_PCI,
176 .of_match = pci_generic_ecam_ids,
177 .ops = &pci_generic_ecam_ops,
Vladimir Oltean34e46e02020-03-13 16:53:06 +0200178 .probe = pci_generic_ecam_probe,
Simon Glassaad29ae2020-12-03 16:55:21 -0700179 .of_to_plat = pci_generic_ecam_of_to_plat,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700180 .priv_auto = sizeof(struct generic_ecam_pcie),
Tuomas Tynkkynena765f712017-09-19 23:18:06 +0300181};