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#pragma once
13
14#include <pci/helper.h>
15#include <stdbool.h>
16#include <inttypes.h>
17
18// ref: http://www.acm.uiuc.edu/sigops/roll_your_own/7.c.0.html
19//      PCI System Architecture, rev 4
20
21#define PCI_CONFIG_HEADER_SIZE_BYTES PCI_STD_HEADER_SIZEOF
22
23/* Detailed base address information about a device. */
24struct libpci_device_iocfg {
25    /* PCI_BASE_ADDRESS_MEM address or
26       PCI_BASE_ADDRESS_IO address */
27    uint32_t base_addr[6];
28    /* PCI_BASE_ADDRESS_SPACE_IO or
29       PCI_BASE_ADDRESS_SPACE_MEMORY */
30    uint8_t base_addr_space[6];
31    /* PCI_BASE_ADDRESS_MEM_TYPE_32 or
32       PCI_BASE_ADDRESS_MEM_TYPE_64 */
33    uint8_t base_addr_type[6];
34    /* PCI_BASE_ADDRESS_MEM_PREFETCH */
35    uint8_t base_addr_prefetchable[6];
36    /* size */
37    uint32_t base_addr_size_mask[6];
38    uint32_t base_addr_size[6];
39    /* raw addr */
40    uint32_t base_addr_raw[6];
41    /* Is this BAR the higher word of a 64-bit address? If true, then this BAR is partial
42       and should not be directly processed in any way. */
43    bool base_addr_64H[6];
44};
45
46typedef struct libpci_device_iocfg libpci_device_iocfg_t;
47
48/* Get the size of a PCI config space element. */
49static inline int libpci_device_cfg_sizeof(int offset) {
50    switch (offset) {
51        case PCI_VENDOR_ID: return 2;
52        case PCI_DEVICE_ID: return 2;
53
54        case PCI_COMMAND: return 2;
55        case PCI_STATUS: return 2;
56
57        case PCI_CLASS_REVISION: return 1;
58        case PCI_CLASS_PROG: return 1;
59        case PCI_CLASS_DEVICE: return 2;
60
61        case PCI_CACHE_LINE_SIZE: return 1;
62        case PCI_LATENCY_TIMER: return 1;
63        case PCI_HEADER_TYPE: return 1;
64        case PCI_BIST: return 1;
65
66        case PCI_BASE_ADDRESS_0:
67        case PCI_BASE_ADDRESS_1:
68        case PCI_BASE_ADDRESS_2:
69        case PCI_BASE_ADDRESS_3:
70        case PCI_BASE_ADDRESS_4:
71        case PCI_BASE_ADDRESS_5:
72            return 4;
73
74        case PCI_CARDBUS_CIS: return 4;
75        case PCI_SUBSYSTEM_VENDOR_ID: return 2;
76        case PCI_SUBSYSTEM_ID: return 2;
77        case PCI_ROM_ADDRESS: return 4;
78
79        case PCI_INTERRUPT_LINE: return 1;
80        case PCI_INTERRUPT_PIN: return 1;
81        case PCI_MIN_GNT: return 1;
82        case PCI_MAX_LAT: return 1;
83    }
84    return 0;
85}
86
87/* Get the base address at a given index. Will automatically handle split 64-bit addreses. */
88static inline uint64_t libpci_device_iocfg_get_baseaddr(libpci_device_iocfg_t *cfg, int index) {
89    assert(cfg && index >= 0 && index < 6);
90    if (cfg->base_addr_type[index] != PCI_BASE_ADDRESS_MEM_TYPE_64)
91           return (uint64_t) cfg->base_addr[index];
92    /* 64-bit mode BARs must have a word after it. */
93    assert(index < 5);
94    /* And the word before it better be set to 64L mode. */
95    assert(cfg->base_addr_64H[index + 1]);
96    return ((uint64_t) cfg->base_addr[index]) | (((uint64_t) cfg->base_addr[index + 1]) << 32);
97}
98
99/* Get the 32-bit base address at given index. Will automatically handle split 64-bit addresses,
100 * and cast them (with an assert check that the upper 32-bits are zero). */
101static inline uint32_t libpci_device_iocfg_get_baseaddr32(libpci_device_iocfg_t *cfg, int index) {
102    uint64_t baddr = libpci_device_iocfg_get_baseaddr(cfg, index);
103    assert((baddr & 0xFFFFFFFFUL) == baddr);
104    if ((baddr & 0xFFFFFFFFUL) != baddr) {
105        printf("WARNING: get_baseaddr32 called for 64-bit address. Address will be truncated.\n");
106        printf("         This will most likely lead to problems.\n");
107        assert(!"WARNING. Zap this assert to ignore.");
108    }
109    return (uint32_t)(baddr & 0xFFFFFFFFUL);
110}
111
112/* Returns true if the given device has at least one IO port base addr associated,
113 * false otherwise. */
114static inline bool libpci_device_iocfg_uses_iomem(libpci_device_iocfg_t *cfg) {
115   assert(cfg);
116   for (int i = 0; i < 6; i++) {
117        if (cfg->base_addr[i] == 0 || cfg->base_addr_64H[i]) continue;
118        return true;
119   }
120   return false;
121}
122
123/* Print out detailed info about a device's base addresses. */
124static inline void libpci_device_iocfg_debug_print(libpci_device_iocfg_t *cfg, bool compact) {
125    for(int i = 0; i < 6; i++) {
126        if (compact) {
127            /* Display in compact space mode, shoving as much information as possible in a few
128             * lines. This is similar to how the Linux kernel PCI debug displays in dmesg. */
129            if (cfg->base_addr[i] == 0 || cfg->base_addr_64H[i]) continue;
130            if (cfg->base_addr_space[i] == PCI_BASE_ADDRESS_SPACE_IO) {
131                printf("    BAR%d : [ io 0x%"PRIx64" sz 0x%x szmask 0x%x ]\n", i,
132                       libpci_device_iocfg_get_baseaddr(cfg, i),
133                       cfg->base_addr_size[i],
134                       cfg->base_addr_size_mask[i]);
135            } else {
136                printf("    BAR%d : [ mem 0x%"PRIx64" sz 0x%x szmask 0x%x %s %s ]\n", i,
137                       libpci_device_iocfg_get_baseaddr(cfg, i),
138                       cfg->base_addr_size[i],
139                       cfg->base_addr_size_mask[i],
140                       cfg->base_addr_type[i] == PCI_BASE_ADDRESS_MEM_TYPE_64 ? "64bit" : "",
141                       cfg->base_addr_prefetchable[i] ? "prefetch" : "");
142            }
143        } else {
144            /* Very verbose and space wasting debug output. */
145            printf("    BASE_ADDR[%d] ----\n", i);
146            if (cfg->base_addr[i] == 0 || cfg->base_addr_64H[i]) continue;
147            printf("        base_addr_space[%d]: 0x%x [%s]\n", i, cfg->base_addr_space[i],
148                   cfg->base_addr_space[i] ? "PCI_BASE_ADDRESS_SPACE_IO" :
149                                                 "PCI_BASE_ADDRESS_SPACE_MEMORY");
150            printf("        base_addr_type[%d]: 0x%x [ ", i, cfg->base_addr_type[i]);
151            if (cfg->base_addr_type[i] == PCI_BASE_ADDRESS_MEM_TYPE_32) printf("32bit ");
152            if (cfg->base_addr_type[i] == PCI_BASE_ADDRESS_MEM_TYPE_64) printf("64bit ");
153            if (cfg->base_addr_type[i] == PCI_BASE_ADDRESS_MEM_TYPE_1M) printf("<1M ");
154            printf("]\n");
155            printf("        base_addr_prefetchable[%d]: %s\n", i, cfg->base_addr_prefetchable[i]
156                   ? "yes" : "no");
157            printf("        base_addr[%d]: 0x%"PRIx64"\n", i, libpci_device_iocfg_get_baseaddr(cfg, i));
158            printf("        base_addr_size_mask[%d]: 0x%x\n", i, cfg->base_addr_size_mask[i]);
159        }
160    }
161}
162