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