1 /*
2  * Copyright (c) 2021 Travis Geiseblrecht
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 #pragma once
9 
10 #include <sys/types.h>
11 #include <dev/bus/pci.h>
12 #include <lk/cpp.h>
13 #include <lk/err.h>
14 #include <lk/list.h>
15 
16 namespace pci {
17 
18 class bus;
19 struct capability;
20 
21 // generic pci device
22 class device {
23 public:
24     device(pci_location_t loc, bus *bus);
25     virtual ~device();
26 
27     DISALLOW_COPY_ASSIGN_AND_MOVE(device);
28 
29     static status_t probe(pci_location_t loc, bus *bus, device **out_device);
30 
31     status_t probe_capabilities();
32     status_t init_msi_capability(capability *cap);
33     status_t init_msix_capability(capability *cap);
34 
35     status_t allocate_irq(uint *irq);
36     status_t allocate_msi(size_t num_requested, uint *msi_base);
37     status_t load_config();
38     status_t load_bars();
39 
40     status_t enable();
41 
42     // ask the device to add up the sizes of all its bars and return the sum
43     // bridges will be expected to recurse into sub-bridges
44     struct bar_sizes {
45         uint32_t io_size;
46         uint32_t mmio_size;
47         uint64_t mmio64_size;
48         uint64_t prefetchable_size;
49         uint64_t prefetchable64_size;
50 
51         uint8_t io_align;
52         uint8_t mmio_align;
53         uint8_t mmio64_align;
54         uint8_t prefetchable_align;
55         uint8_t prefetchable64_align;
56 
57         bar_sizes &operator+=(const bar_sizes &a);
58     };
59     virtual status_t compute_bar_sizes(bar_sizes *sizes);
60 
61     struct bar_alloc_request {
62         // linked list node
63         list_node node;
64 
65         // requst for allocation of this type
66         pci_resource_type type;
67         uint64_t size;
68         uint8_t align; // power of 2
69 
70         bool bridge; // either a bridge request or a bar
71         bool prefetchable; // prefetchable request (only makes sense for mmio or mmio64)
72 
73         device *dev;
74         uint8_t bar_num;
75 
76         void dump();
77     };
78     virtual status_t get_bar_alloc_requests(list_node *bar_alloc_requests);
79     virtual status_t assign_resource(bar_alloc_request *request, uint64_t address);
assign_child_resources()80     virtual status_t assign_child_resources() { return NO_ERROR; }
81 
loc()82     pci_location_t loc() const { return loc_; }
get_bus()83     const bus *get_bus() const { return bus_; }
84 
device_id()85     uint16_t device_id() const { return config_.device_id; }
vendor_id()86     uint16_t vendor_id() const { return config_.vendor_id; }
base_class()87     uint8_t base_class() const { return config_.base_class; }
sub_class()88     uint8_t sub_class() const { return config_.sub_class; }
interface()89     uint8_t interface() const { return config_.program_interface; }
header_type()90     uint8_t header_type() const { return config_.header_type & PCI_HEADER_TYPE_MASK; }
91 
92     status_t read_bars(pci_bar_t bar[6]);
93 
has_msi()94     bool has_msi() const { return msi_cap_; }
has_msix()95     bool has_msix() const { return msix_cap_; }
96 
97     virtual void dump(size_t indent = 0);
98 
99 protected:
100     // let the bus device directly manipulate our list node
101     friend class bus;
102     list_node node = LIST_INITIAL_CLEARED_VALUE;
103 
104     pci_location_t loc_ = {};
105     bus *bus_ = nullptr;
106 
107     pci_config_t config_ = {};
108     pci_bar_t bars_[6] = {};
109 
110     // capability list
111     list_node capability_list_ = LIST_INITIAL_VALUE(capability_list_);
112     capability *msi_cap_ = nullptr;
113     capability *msix_cap_ = nullptr;
114 };
115 
116 struct capability {
117     list_node node = LIST_INITIAL_CLEARED_VALUE;
118     uint16_t config_offset = 0;
119     uint16_t id = 0;
120 
121     // simple accessors
is_msicapability122     bool is_msi() const { return id == 0x5; }
is_msixcapability123     bool is_msix() const { return id == 0x11; }
124 };
125 
126 inline device::bar_sizes operator+(const device::bar_sizes &a, const device::bar_sizes &b) {
127     device::bar_sizes result;
128 
129     result.io_size = a.io_size + b.io_size;
130     result.mmio_size = a.mmio_size + b.mmio_size;
131     result.mmio64_size = a.mmio64_size + b.mmio64_size;
132     result.prefetchable_size = a.prefetchable_size + b.prefetchable_size;
133     result.prefetchable64_size = a.prefetchable64_size + b.prefetchable64_size;
134 
135     result.io_align = (a.io_align > b.io_align) ? a.io_align : b.io_align;
136     result.mmio_align = (a.mmio_align > b.mmio_align) ? a.mmio_align : b.mmio_align;
137     result.mmio64_align = (a.mmio64_align > b.mmio64_align) ? a.mmio64_align : b.mmio64_align;
138     result.prefetchable_align = (a.prefetchable_align > b.prefetchable_align) ? a.prefetchable_align : b.prefetchable_align;
139     result.prefetchable64_align = (a.prefetchable64_align > b.prefetchable64_align) ? a.prefetchable64_align : b.prefetchable64_align;
140 
141     return result;
142 }
143 
144 inline device::bar_sizes &device::bar_sizes::operator+=(const device::bar_sizes &a) {
145     io_size += a.io_size;
146     mmio_size += a.mmio_size;
147     mmio64_size += a.mmio64_size;
148     prefetchable_size += a.prefetchable_size;
149     prefetchable64_size += a.prefetchable64_size;
150 
151     io_align = (io_align > a.io_align) ? io_align : a.io_align;
152     mmio_align = (mmio_align > a.mmio_align) ? mmio_align : a.mmio_align;
153     mmio64_align = (mmio64_align > a.mmio64_align) ? mmio64_align : a.mmio64_align;
154     prefetchable_align = (prefetchable_align > a.prefetchable_align) ? prefetchable_align : a.prefetchable_align;
155     prefetchable64_align = (prefetchable64_align > a.prefetchable64_align) ? prefetchable64_align : a.prefetchable64_align;
156 
157     return *this;
158 }
159 
160 } // pci
161