1/* @title: PCI */
2#pragma once
3#include <asm.h>
4#include <compiler.h>
5#include <device.h>
6#include <linker/symbols.h>
7#include <log.h>
8#include <stdint.h>
9
10#define PCI_CLASS_MASS_STORAGE 0x01
11#define PCI_SUBCLASS_NVM 0x08
12#define PCI_PROGIF_NVME 0x02
13
14struct pci_device {
15 struct device device;
16 uint8_t bus;
17 uint8_t dev;
18 uint8_t function;
19
20 uint16_t vendor_id;
21 uint16_t device_id;
22
23 uint8_t class_code;
24 uint8_t subclass;
25 uint8_t prog_if;
26 uint8_t revision;
27};
28
29struct pci_driver {
30 struct device_driver driver;
31 uint8_t class_code;
32 uint8_t subclass;
33 uint8_t prog_if;
34 uint16_t vendor_id;
35};
36
37LINKER_SECTION_DEFINE(struct pci_driver, pci_devices);
38
39#define PCI_DEV_REGISTER(n, cc, sc, pi, vi, init) \
40 LINKER_SECTION_OBJECT(struct pci_driver, pci_devices) \
41 pci_device_##n = {.driver.name = #n, \
42 .class_code = cc, \
43 .subclass = sc, \
44 .prog_if = pi, \
45 .vendor_id = vi, \
46 .driver.probe = init};
47
48union pci_command_reg {
49 uint16_t value;
50 struct {
51 uint16_t io_space : 1; // Bit 0
52 uint16_t memory_space : 1; // Bit 1
53 uint16_t bus_master : 1; // Bit 2
54 uint16_t special_cycles : 1; // Bit 3
55 uint16_t mem_write_inv : 1; // Bit 4
56 uint16_t vga_snoop : 1; // Bit 5
57 uint16_t parity_error : 1; // Bit 6
58 uint16_t reserved0 : 1; // Bit 7
59 uint16_t serr_enable : 1; // Bit 8
60 uint16_t fast_back : 1; // Bit 9
61 uint16_t interrupt_disable : 1; // Bit 10
62 uint16_t reserved1 : 5; // Bits 11–15
63 };
64};
65
66struct pci_msix_table_entry {
67 uint32_t msg_addr_low;
68 uint32_t msg_addr_high;
69 uint32_t msg_data;
70 uint32_t vector_ctrl; // Bit 0 = Mask
71};
72
73struct pci_msix_cap {
74 uint8_t cap_id; // 0x0
75 uint8_t next_ptr; // 0x1
76 uint16_t msg_ctl; // 0x2
77 uint32_t table_offset_bir; // 0x4
78 uint32_t pba_offset_bir; // 0x8
79} __packed;
80
81#define PCI_BAR0 0x10
82#define PCI_BAR1 0x14
83#define PCI_BAR2 0x18
84#define PCI_BAR3 0x1C
85#define PCI_BAR4 0x20
86#define PCI_BAR5 0x24
87
88#define PCI_CAP_PTR 0x34
89#define PCI_CAP_ID_MSIX 0x11
90#define PCI_CONFIG_ADDRESS 0xCF8
91#define PCI_CONFIG_DATA 0xCFC
92#define PCI_INTERRUPT_LINE 0x3C
93#define PCI_PROG_IF 0x09
94
95static inline uint16_t pci_read_config16(uint8_t bus, uint8_t device,
96 uint8_t function, uint8_t offset) {
97 uint32_t address = (1U << 31) // enable bit
98 | ((uint32_t) bus << 16) | ((uint32_t) device << 11) |
99 ((uint32_t) function << 8) |
100 (offset & 0xFC); // aligned to 4 bytes
101 outl(PCI_CONFIG_ADDRESS, value: address);
102 uint32_t data = inl(PCI_CONFIG_DATA);
103
104 if (offset & 2)
105 return (uint16_t) (data >> 16);
106 else
107 return (uint16_t) (data & 0xFFFF);
108}
109
110static inline uint8_t pci_read_config8(uint8_t bus, uint8_t device,
111 uint8_t function, uint8_t offset) {
112 uint32_t address = (1U << 31) | ((uint32_t) bus << 16) |
113 ((uint32_t) device << 11) | ((uint32_t) function << 8) |
114 (offset & 0xFC); // 4-byte aligned
115 outl(PCI_CONFIG_ADDRESS, value: address);
116 uint32_t data = inl(PCI_CONFIG_DATA);
117
118 return (uint8_t) ((data >> ((offset & 3) * 8)) & 0xFF);
119}
120
121static inline void pci_write_config16(uint8_t bus, uint8_t device,
122 uint8_t function, uint8_t offset,
123 uint16_t value) {
124 uint32_t address = (1U << 31) | ((uint32_t) bus << 16) |
125 ((uint32_t) device << 11) | ((uint32_t) function << 8) |
126 (offset & 0xFC);
127 outl(PCI_CONFIG_ADDRESS, value: address);
128 uint32_t old_data = inl(PCI_CONFIG_DATA);
129
130 uint32_t new_data;
131 if (offset & 2)
132 new_data = (old_data & 0x0000FFFF) | ((uint32_t) value << 16);
133 else
134 new_data = (old_data & 0xFFFF0000) | value;
135
136 outl(PCI_CONFIG_ADDRESS, value: address);
137 outl(PCI_CONFIG_DATA, value: new_data);
138}
139
140static inline uint32_t pci_config_address(uint8_t bus, uint8_t slot,
141 uint8_t func, uint8_t offset) {
142 return (uint32_t) ((1U << 31) | (bus << 16) | (slot << 11) | (func << 8) |
143 (offset & 0xFC));
144}
145
146static inline uint32_t pci_read(uint8_t bus, uint8_t slot, uint8_t func,
147 uint8_t offset) {
148 outl(PCI_CONFIG_ADDRESS, value: pci_config_address(bus, slot, func, offset));
149 return inl(PCI_CONFIG_DATA);
150}
151
152static inline uint16_t pci_read_word(uint8_t bus, uint8_t slot, uint8_t func,
153 uint8_t offset) {
154 uint32_t value = pci_read(bus, slot, func, offset: offset & 0xFC);
155 return (value >> ((offset & 2) * 8)) & 0xFFFF;
156}
157
158static inline uint8_t pci_read_byte(uint8_t bus, uint8_t slot, uint8_t func,
159 uint8_t offset) {
160 uint32_t value = pci_read(bus, slot, func, offset: offset & 0xFC);
161 return (value >> ((offset & 3) * 8)) & 0xFF;
162}
163
164static inline void pci_write(uint8_t bus, uint8_t slot, uint8_t func,
165 uint8_t offset, uint32_t value) {
166 outl(PCI_CONFIG_ADDRESS, value: pci_config_address(bus, slot, func, offset));
167 outl(PCI_CONFIG_DATA, value);
168}
169
170static inline void pci_write_word(uint8_t bus, uint8_t slot, uint8_t func,
171 uint8_t offset, uint16_t value) {
172 uint32_t tmp = pci_read(bus, slot, func, offset: offset & 0xFCU);
173 uint32_t shift = (offset & 2) * 8;
174 tmp = (tmp & ~(0xFFFFU << shift)) | ((uint32_t) value << shift);
175 pci_write(bus, slot, func, offset: offset & 0xFCU, value: tmp);
176}
177
178static inline void pci_write_byte(uint8_t bus, uint8_t slot, uint8_t func,
179 uint8_t offset, uint8_t value) {
180 uint32_t tmp = pci_read(bus, slot, func, offset: offset & 0xFC);
181 uint32_t shift = (offset & 3) * 8;
182 tmp = (tmp & ~(0xFF << shift)) | ((uint32_t) value << shift);
183 pci_write(bus, slot, func, offset: offset & 0xFC, value: tmp);
184}
185
186LOG_HANDLE_EXTERN(pci);
187LOG_SITE_EXTERN(pci);
188#define pci_log(log_level, fmt, ...) \
189 log(LOG_SITE(pci), LOG_HANDLE(pci), log_level, fmt, ##__VA_ARGS__)
190
191const char *pci_class_name(uint8_t class_code, uint8_t subclass);
192
193void pci_scan_devices(struct pci_device **devices_out, uint64_t *count_out);
194uint32_t pci_read_bar(uint8_t bus, uint8_t device, uint8_t function,
195 uint8_t bar_index);
196void pci_enable_msix(uint8_t bus, uint8_t slot, uint8_t func);
197void pci_enable_msix_on_core(uint8_t bus, uint8_t slot, uint8_t func,
198 uint8_t vector, uint8_t core);
199void pci_init_devices(struct pci_device *devices, uint64_t count);
200uint8_t pci_find_capability(uint8_t bus, uint8_t slot, uint8_t func,
201 uint8_t cap_id);
202void pci_program_msix_entry(uint8_t bus, uint8_t slot, uint8_t func,
203 uint32_t table_index, uint8_t vector,
204 uint8_t apic_id);
205