summaryrefslogtreecommitdiff
path: root/src/include/device/pci_mmio_cfg.h
blob: 8798405776ed4f9b24cb17fd323d03844b4aec10 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/* SPDX-License-Identifier: GPL-2.0-only */

#ifndef _PCI_MMIO_CFG_H
#define _PCI_MMIO_CFG_H

#include <stdint.h>
#include <device/mmio.h>
#include <device/pci_type.h>

/* By not assigning this to CONFIG_MMCONF_BASE_ADDRESS here we
 * prevent some sub-optimal constant folding. */
extern u8 *const pci_mmconf;

/* Using a unique datatype for MMIO writes makes the pointers to _not_
 * qualify for pointer aliasing with any other objects in memory.
 *
 * MMIO offset is a value originally derived from 'struct device *'
 * in ramstage. For the compiler to not discard this MMIO offset value
 * from CPU registers after any MMIO writes, -fstrict-aliasing has to
 * be also set for the build.
 *
 * Bottom 12 bits (4 KiB) are reserved to address the registers of a
 * single PCI function. Declare the bank as a union to avoid some casting
 * in the functions below.
 */
union pci_bank {
	uint8_t reg8[4096];
	uint16_t reg16[4096 / sizeof(uint16_t)];
	uint32_t reg32[4096 / sizeof(uint32_t)];
};

static __always_inline
volatile union pci_bank *pcicfg(pci_devfn_t dev)
{
	return (void *)&pci_mmconf[PCI_DEVFN_OFFSET(dev)];
}

static __always_inline
uint8_t pci_mmio_read_config8(pci_devfn_t dev, uint16_t reg)
{
	return pcicfg(dev)->reg8[reg];
}

static __always_inline
uint16_t pci_mmio_read_config16(pci_devfn_t dev, uint16_t reg)
{
	return pcicfg(dev)->reg16[reg / sizeof(uint16_t)];
}

static __always_inline
uint32_t pci_mmio_read_config32(pci_devfn_t dev, uint16_t reg)
{
	return pcicfg(dev)->reg32[reg / sizeof(uint32_t)];
}

static __always_inline
void pci_mmio_write_config8(pci_devfn_t dev, uint16_t reg, uint8_t value)
{
	pcicfg(dev)->reg8[reg] = value;
}

static __always_inline
void pci_mmio_write_config16(pci_devfn_t dev, uint16_t reg, uint16_t value)
{
	pcicfg(dev)->reg16[reg / sizeof(uint16_t)] = value;
}

static __always_inline
void pci_mmio_write_config32(pci_devfn_t dev, uint16_t reg, uint32_t value)
{
	pcicfg(dev)->reg32[reg / sizeof(uint32_t)] = value;
}

/*
 * The functions pci_mmio_config*_addr provide a way to determine the MMIO address of a PCI
 * config register. The address returned is dependent of both the MMCONF base address and the
 * assigned PCI bus number of the requested device, which both can change during the boot
 * process. Thus, the pointer returned here must not be cached!
 */
static __always_inline
uint8_t *pci_mmio_config8_addr(pci_devfn_t dev, uint16_t reg)
{
	return (uint8_t *)&pcicfg(dev)->reg8[reg];
}

static __always_inline
uint16_t *pci_mmio_config16_addr(pci_devfn_t dev, uint16_t reg)
{
	return (uint16_t *)&pcicfg(dev)->reg16[reg / sizeof(uint16_t)];
}

static __always_inline
uint32_t *pci_mmio_config32_addr(pci_devfn_t dev, uint16_t reg)
{
	return (uint32_t *)&pcicfg(dev)->reg32[reg / sizeof(uint32_t)];
}

#if CONFIG(MMCONF_SUPPORT)

#if CONFIG_MMCONF_BASE_ADDRESS == 0
#error "CONFIG_MMCONF_BASE_ADDRESS undefined!"
#endif

#if CONFIG_MMCONF_BUS_NUMBER * MiB != CONFIG_MMCONF_LENGTH
#error "CONFIG_MMCONF_LENGTH does not correspond with CONFIG_MMCONF_BUS_NUMBER!"
#endif

/* Avoid name collisions as different stages have different signature
 * for these functions. The _s_ stands for simple, fundamental IO or
 * MMIO variant.
 */

static __always_inline
uint8_t pci_s_read_config8(pci_devfn_t dev, uint16_t reg)
{
	return pci_mmio_read_config8(dev, reg);
}

static __always_inline
uint16_t pci_s_read_config16(pci_devfn_t dev, uint16_t reg)
{
	return pci_mmio_read_config16(dev, reg);
}

static __always_inline
uint32_t pci_s_read_config32(pci_devfn_t dev, uint16_t reg)
{
	return pci_mmio_read_config32(dev, reg);
}

static __always_inline
void pci_s_write_config8(pci_devfn_t dev, uint16_t reg, uint8_t value)
{
	pci_mmio_write_config8(dev, reg, value);
}

static __always_inline
void pci_s_write_config16(pci_devfn_t dev, uint16_t reg, uint16_t value)
{
	pci_mmio_write_config16(dev, reg, value);
}

static __always_inline
void pci_s_write_config32(pci_devfn_t dev, uint16_t reg, uint32_t value)
{
	pci_mmio_write_config32(dev, reg, value);
}

#endif

#endif /* _PCI_MMIO_CFG_H */