1 /*
2 * Copyright (c) 2009 Corey Tabaka
3 * Copyright (c) 2020 Travis Geiselbrecht
4 *
5 * Use of this source code is governed by a MIT-style
6 * license that can be found in the LICENSE file or at
7 * https://opensource.org/licenses/MIT
8 */
9 #include "ecam.h"
10
11 #include <lk/debug.h>
12 #include <lk/err.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <kernel/thread.h>
16 #include <kernel/spinlock.h>
17 #include <dev/bus/pci.h>
18 #include <lk/trace.h>
19
20 #if WITH_KERNEL_VM
21 #include <kernel/vm.h>
22 #endif
23
24 #include "pci_priv.h"
25
26 #define LOCAL_TRACE 0
27
pci_ecam(paddr_t base,uint16_t segment,uint8_t start_bus,uint8_t end_bus)28 pci_ecam::pci_ecam(paddr_t base, uint16_t segment, uint8_t start_bus, uint8_t end_bus) :
29 base_(base), segment_(segment), start_bus_(start_bus), end_bus_(end_bus) {}
30
~pci_ecam()31 pci_ecam::~pci_ecam() {
32 LTRACE_ENTRY;
33 #if WITH_KERNEL_VM
34 if (ecam_ptr_) {
35 vmm_free_region(vmm_get_kernel_aspace(), (vaddr_t)ecam_ptr_);
36 }
37 #endif
38 }
39
detect(paddr_t base,uint16_t segment,uint8_t start_bus,uint8_t end_bus)40 pci_ecam *pci_ecam::detect(paddr_t base, uint16_t segment, uint8_t start_bus, uint8_t end_bus) {
41 LTRACEF("base %#lx, segment %hu, bus [%hhu...%hhu]\n", base, segment, start_bus, end_bus);
42
43 // we only support a limited configuration at the moment
44 if (segment != 0 || start_bus != 0) {
45 return nullptr;
46 }
47
48 auto ecam = new pci_ecam(base, segment, start_bus, end_bus);
49
50 // initialize the object, which may fail
51 status_t err = ecam->initialize();
52 if (err != NO_ERROR) {
53 delete ecam;
54 return nullptr;
55 }
56
57 return ecam;
58 }
59
initialize()60 status_t pci_ecam::initialize() {
61 // compute the aperture size of this
62 size_t size = ((size_t)end_bus_ - (size_t)start_bus_ + 1) << 20; // each bus occupies 20 bits of address space
63 LTRACEF("aperture size %#zx\n", size);
64
65
66 #if WITH_KERNEL_VM
67 // try to map the aperture
68 //status_t vmm_alloc_physical(vmm_aspace_t *aspace, const char *name, size_t size, void **ptr, uint8_t align_log2, paddr_t paddr, uint vmm_flags, uint arch_mmu_flags)
69 status_t err = vmm_alloc_physical(vmm_get_kernel_aspace(), "pci_ecam", size, (void **)&ecam_ptr_, 0, base_, 0, ARCH_MMU_FLAG_UNCACHED_DEVICE);
70 LTRACEF("vmm_alloc_physical returns %d, ptr %p\n", err, ecam_ptr_);
71
72 if (err != NO_ERROR) {
73 ecam_ptr_ = nullptr;
74 return err;
75 }
76 #else
77 // no vm, so can directly access the aperture
78 ecam_ptr_ = (uint8_t *)base_;
79 #endif
80
81 set_last_bus(end_bus_);
82
83 return NO_ERROR;
84 }
85
86 // compute the offset into the ecam given the location and register offset
location_to_offset(const pci_location_t * state,uint32_t reg)87 inline size_t location_to_offset(const pci_location_t *state, uint32_t reg) {
88 // TODO: clamp or assert on invalid offset
89 size_t offset = (size_t)state->bus << 20;
90 offset += (size_t)state->dev_fn << 12;
91 offset += reg;
92 return offset;
93 }
94
95 // templatized routines to access the pci config space using a specific type
96 template <typename T>
read_config(const pci_location_t * state,uint32_t reg,T * value,const uint8_t * ecam_ptr)97 inline int read_config(const pci_location_t *state, uint32_t reg, T *value, const uint8_t *ecam_ptr) {
98 auto off = location_to_offset(state, reg);
99
100 *value = *reinterpret_cast<const volatile T *>(&ecam_ptr[off]);
101
102 return NO_ERROR;
103 }
104
105 template <typename T>
write_config(const pci_location_t * state,uint32_t reg,T value,uint8_t * ecam_ptr)106 inline int write_config(const pci_location_t *state, uint32_t reg, T value, uint8_t *ecam_ptr) {
107 auto off = location_to_offset(state, reg);
108
109 *reinterpret_cast<volatile T *>(&ecam_ptr[off]) = value;
110
111 return NO_ERROR;
112 }
113
read_config_byte(const pci_location_t * state,uint32_t reg,uint8_t * value)114 int pci_ecam::read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) {
115 LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
116 return read_config(state, reg, value, ecam_ptr_);
117 }
118
read_config_half(const pci_location_t * state,uint32_t reg,uint16_t * value)119 int pci_ecam::read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) {
120 LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
121 return read_config(state, reg, value, ecam_ptr_);
122 }
123
read_config_word(const pci_location_t * state,uint32_t reg,uint32_t * value)124 int pci_ecam::read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) {
125 LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
126 return read_config(state, reg, value, ecam_ptr_);
127 }
128
write_config_byte(const pci_location_t * state,uint32_t reg,uint8_t value)129 int pci_ecam::write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value) {
130 LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
131 return write_config(state, reg, value, ecam_ptr_);
132 }
133
write_config_half(const pci_location_t * state,uint32_t reg,uint16_t value)134 int pci_ecam::write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value) {
135 LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
136 return write_config(state, reg, value, ecam_ptr_);
137 }
138
write_config_word(const pci_location_t * state,uint32_t reg,uint32_t value)139 int pci_ecam::write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value) {
140 LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
141 return write_config(state, reg, value, ecam_ptr_);
142 }
143