1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12#include <autoconf.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <pci/pci.h>
17#include <pci/helper.h>
18#include <pci/ioreg.h>
19#include <utils/attribute.h>
20#include <utils/zf_log.h>
21
22#define PCI_DISPLAY_FOUND_DEVICES
23
24libpci_device_t libpci_device_list[PCI_MAX_DEVICES];
25uint32_t libpci_num_devices = 0;
26static ps_io_port_ops_t global_port_ops;
27
28uint32_t libpci_ioread(uint32_t port_no, uint32_t* val, uint32_t size) {
29    return (uint32_t)ps_io_port_in(&global_port_ops, port_no, (int)size, val);
30}
31
32uint32_t libpci_iowrite(uint32_t port_no, uint32_t val, uint32_t size) {
33    return (uint32_t)ps_io_port_out(&global_port_ops, port_no, (int)size, val);
34}
35
36libpci_device_t* libpci_find_device(uint16_t vendor_id, uint16_t device_id) {
37    for (uint32_t i = 0; i < libpci_num_devices; i++) {
38        if (libpci_device_list[i].vendor_id == vendor_id &&
39            libpci_device_list[i].device_id == device_id) {
40            return &libpci_device_list[i];
41        }
42    }
43    return NULL;
44}
45
46int libpci_find_device_all(uint16_t vendor_id, uint16_t device_id, libpci_device_t** out) {
47    assert(out);
48    int n = 0;
49    for (uint32_t i = 0; i < libpci_num_devices; i++) {
50        if (libpci_device_list[i].vendor_id == vendor_id &&
51            libpci_device_list[i].device_id == device_id) {
52            out[n++] = &libpci_device_list[i];
53        }
54    }
55    return n;
56}
57
58libpci_device_t* libpci_find_device_matching(libpci_device_t *device) {
59    for (uint32_t i = 0; i < libpci_num_devices; i++) {
60        if (libpci_device_list[i].bus == device->bus &&
61            libpci_device_list[i].dev == device->dev &&
62            libpci_device_list[i].fun == device->fun &&
63            libpci_device_list[i].vendor_id == device->vendor_id &&
64            libpci_device_list[i].device_id == device->device_id) {
65            return &libpci_device_list[i];
66        }
67    }
68    return NULL;
69}
70
71libpci_device_t* libpci_find_device_bdf(uint8_t bus, uint8_t dev, uint8_t fun) {
72    for (uint32_t i = 0; i < libpci_num_devices; i++) {
73        if (libpci_device_list[i].bus == bus &&
74            libpci_device_list[i].dev == dev &&
75            libpci_device_list[i].fun == fun) {
76            return &libpci_device_list[i];
77        }
78    }
79    return NULL;
80}
81
82static int libpci_add_fun(uint8_t bus, uint8_t dev, uint8_t fun) {
83    uint16_t vendor_id = libpci_read_reg16(bus, dev, fun, PCI_VENDOR_ID);
84
85    if (vendor_id == PCI_VENDOR_ID_INVALID) {
86        /* No device here. */
87        return 0;
88    }
89
90    ZF_LOGD("PCI :: Device found at BUS %d DEV %d FUN %d:\n", (int)bus, (int)dev, (int)fun);
91    ZF_LOGD("    vendorID = %s [0x%x]\n", libpci_vendorID_str(vendor_id), vendor_id);
92
93    uint16_t device_id = libpci_read_reg16(bus, dev, fun, PCI_DEVICE_ID);
94    ZF_LOGD("    deviceID = %s [0x%x]\n", libpci_deviceID_str(vendor_id, device_id), device_id);
95
96    assert(libpci_num_devices + 1<= PCI_MAX_DEVICES);
97    libpci_device_list[libpci_num_devices].bus = bus;
98    libpci_device_list[libpci_num_devices].dev = dev;
99    libpci_device_list[libpci_num_devices].fun = fun;
100    libpci_device_list[libpci_num_devices].vendor_id = vendor_id;
101    libpci_device_list[libpci_num_devices].device_id = device_id;
102    libpci_device_list[libpci_num_devices].vendor_name = libpci_vendorID_str(vendor_id);
103    libpci_device_list[libpci_num_devices].device_name = libpci_deviceID_str(vendor_id, device_id);
104    libpci_device_list[libpci_num_devices].interrupt_line = libpci_read_reg8(bus, dev, fun, PCI_INTERRUPT_LINE);
105    libpci_device_list[libpci_num_devices].interrupt_pin = libpci_read_reg8(bus, dev, fun, PCI_INTERRUPT_PIN);
106    libpci_device_list[libpci_num_devices].subsystem_id = libpci_read_reg16(bus, dev, fun, PCI_SUBSYSTEM_ID);
107    libpci_read_ioconfig(&libpci_device_list[libpci_num_devices].cfg, bus, dev, fun);
108
109#if (ZF_LOG_LEVEL == ZF_LOG_VERBOSE)
110    libpci_device_iocfg_debug_print(&libpci_device_list[libpci_num_devices].cfg, false);
111#endif
112
113    #ifdef PCI_DISPLAY_FOUND_DEVICES
114    printf("PCI :: %.2x.%.2x.%.2x : %s %s (vid 0x%x did 0x%x) line%d pin%d\n", bus, dev, fun,
115        libpci_vendorID_str(vendor_id), libpci_deviceID_str(vendor_id, device_id),
116        vendor_id, device_id,
117        libpci_read_reg8(bus, dev, fun, PCI_INTERRUPT_LINE),
118        libpci_read_reg8(bus, dev, fun, PCI_INTERRUPT_PIN)
119    );
120    libpci_device_iocfg_debug_print(&libpci_device_list[libpci_num_devices].cfg, true);
121    #endif
122
123    libpci_num_devices++;
124
125    return 1;
126}
127
128static void lib_pci_scan_bus(int bus);
129
130static void lib_pci_scan_fun(int bus, int dev, int fun) {
131    libpci_add_fun(bus, dev, fun);
132    if ( libpci_read_reg16(bus, dev, fun, PCI_CLASS_DEVICE) == 0x0604) {
133        int new_bus = libpci_read_reg8(bus, dev, fun, PCI_SECONDARY_BUS);
134        printf("%s found additional bus %d from %d %d %d\n", __FUNCTION__, new_bus, bus, dev, fun);
135        lib_pci_scan_bus(new_bus);
136    }
137}
138
139static void lib_pci_scan_dev(int bus, int dev) {
140    uint16_t vendor_id = libpci_read_reg16(bus, dev, 0, PCI_VENDOR_ID);
141    if (vendor_id == PCI_VENDOR_ID_INVALID) {
142        return;
143    }
144    printf("%s found pci device %d %d\n", __FUNCTION__, bus, dev);
145    lib_pci_scan_fun(bus, dev, 0);
146    if ( (libpci_read_reg8(bus, dev, 0, PCI_HEADER_TYPE) & 0x80) != 0) {
147        printf("%s found multi function device %d %d\n", __FUNCTION__, bus, dev);
148        for (int function = 1; function < 8; function++) {
149            if (libpci_read_reg16(bus, dev, function, PCI_VENDOR_ID) != PCI_VENDOR_ID_INVALID) {
150                lib_pci_scan_fun(bus, dev, function);
151            }
152        }
153    }
154}
155
156static void lib_pci_scan_bus(int bus) {
157    for (int dev = 0; dev < 32; dev++) {
158        lib_pci_scan_dev(bus, dev);
159    }
160}
161
162void libpci_scan(ps_io_port_ops_t port_ops) {
163    global_port_ops = port_ops;
164    ZF_LOGD("PCI :: Scanning...\n");
165    if ( (libpci_read_reg8(0, 0, 0, PCI_HEADER_TYPE) & 0x80) == 0) {
166        printf("Single bus detected\n");
167        lib_pci_scan_bus(0);
168    } else {
169        for (int function = 0; function < 8; function++) {
170            if (libpci_read_reg16(0, 0, function, PCI_VENDOR_ID) != PCI_VENDOR_ID_INVALID) {
171                printf("%s detected bus %d\n", __FUNCTION__, function);
172                lib_pci_scan_bus(function);
173            }
174        }
175    }
176}
177
178void libpci_read_ioconfig(libpci_device_iocfg_t *cfg, uint8_t bus, uint8_t dev, uint8_t fun) {
179    assert(cfg);
180    memset(cfg, 0, sizeof(libpci_device_iocfg_t));
181
182    for (int i = 0; i < 6; i++) {
183        // Read and save the base address assigned by the BIOS.
184        uint32_t bios_base_addr = libpci_read_reg32(bus, dev, fun, PCI_BASE_ADDRESS_0 + (i * 4));
185        cfg->base_addr_raw[i] = bios_base_addr;
186
187        if (cfg->base_addr_64H[i]) {
188            // Don't bother processing further if this is already part of a 64-bit address.
189            cfg->base_addr[i] = cfg->base_addr_raw[i];
190            cfg->base_addr_size_mask[i] = 0xFFFFFFFF;
191            cfg->base_addr_size[i] = 0;
192            continue;
193        }
194
195        // Write 0xFFFFFFFF to read the configs.
196        libpci_write_reg32(bus, dev, fun, PCI_BASE_ADDRESS_0 + (i * 4), 0xFFFFFFFF);
197        uint32_t cfg_base_addr = libpci_read_reg32(bus, dev, fun, PCI_BASE_ADDRESS_0 + (i * 4));
198
199        if (cfg_base_addr == 0)
200            /* no device here. */
201            continue;
202
203        cfg->base_addr_space[i] = cfg_base_addr & PCI_BASE_ADDRESS_SPACE;
204        if (cfg->base_addr_space[i] == PCI_BASE_ADDRESS_SPACE_MEMORY) {
205            cfg->base_addr_type[i] = (cfg_base_addr & PCI_BASE_ADDRESS_MEM_TYPE_MASK);
206            cfg->base_addr_prefetchable[i] = (cfg_base_addr & PCI_BASE_ADDRESS_MEM_PREFETCH) > 0;
207            cfg->base_addr_size_mask[i] = cfg_base_addr & PCI_BASE_ADDRESS_MEM_MASK;
208            if (cfg->base_addr_type[i] == PCI_BASE_ADDRESS_MEM_TYPE_64) {
209                /* Handle 64-bit addresses. */
210                assert(i < 5);
211                // Set up the next BAR entry to be 64H mode.
212                cfg->base_addr_64H[i + 1] = true;
213                // Set up this BAR entry to be in 64L mode.
214                cfg->base_addr[i] = bios_base_addr & PCI_BASE_ADDRESS_MEM_MASK;
215            } else {
216                cfg->base_addr[i] = bios_base_addr & PCI_BASE_ADDRESS_MEM_MASK;
217            }
218        } else  /* PCI_BASE_ADDRESS_SPACE_IO */ {
219            cfg->base_addr[i] = bios_base_addr & PCI_BASE_ADDRESS_IO_MASK;
220            cfg->base_addr_size_mask[i] = cfg_base_addr & PCI_BASE_ADDRESS_IO_MASK;
221            cfg->base_addr_type[i] = PCI_BASE_ADDRESS_MEM_TYPE_32;
222        }
223
224        /* Calculate size from size_mask. */
225        cfg->base_addr_size[i] = BIT(CTZ(cfg->base_addr_size_mask[i]));
226
227        // Write back the address set by the BIOS.
228        libpci_write_reg32(bus, dev, fun, PCI_BASE_ADDRESS_0 + (i * 4), bios_base_addr);
229    }
230}
231