1// Copyright 2017 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7#include <dev/pci_config.h>
8#include <lib/pci/pio.h>
9
10#include <assert.h>
11#include <debug.h>
12#include <inttypes.h>
13#include <trace.h>
14
15#include <fbl/alloc_checker.h>
16
17#define LOCAL_TRACE 0
18
19// Storage for register constexprs
20constexpr PciReg16 PciConfig::kVendorId;
21constexpr PciReg16 PciConfig::kDeviceId;
22constexpr PciReg16 PciConfig::kCommand;
23constexpr PciReg16 PciConfig::kStatus;
24constexpr PciReg8 PciConfig::kRevisionId;
25constexpr PciReg8 PciConfig::kProgramInterface;
26constexpr PciReg8 PciConfig::kSubClass;
27constexpr PciReg8 PciConfig::kBaseClass;
28constexpr PciReg8 PciConfig::kCacheLineSize;
29constexpr PciReg8 PciConfig::kLatencyTimer;
30constexpr PciReg8 PciConfig::kHeaderType;
31constexpr PciReg8 PciConfig::kBist;
32constexpr PciReg32 PciConfig::kCardbusCisPtr;
33constexpr PciReg16 PciConfig::kSubsystemVendorId;
34constexpr PciReg16 PciConfig::kSubsystemId;
35constexpr PciReg32 PciConfig::kExpansionRomAddress;
36constexpr PciReg8 PciConfig::kCapabilitiesPtr;
37constexpr PciReg8 PciConfig::kInterruptLine;
38constexpr PciReg8 PciConfig::kInterruptPin;
39constexpr PciReg8 PciConfig::kMinGrant;
40constexpr PciReg8 PciConfig::kMaxLatency;
41constexpr PciReg8 PciConfig::kPrimaryBusId;
42constexpr PciReg8 PciConfig::kSecondaryBusId;
43constexpr PciReg8 PciConfig::kSubordinateBusId;
44constexpr PciReg8 PciConfig::kSecondaryLatencyTimer;
45constexpr PciReg8 PciConfig::kIoBase;
46constexpr PciReg8 PciConfig::kIoLimit;
47constexpr PciReg16 PciConfig::kSecondaryStatus;
48constexpr PciReg16 PciConfig::kMemoryBase;
49constexpr PciReg16 PciConfig::kMemoryLimit;
50constexpr PciReg16 PciConfig::kPrefetchableMemoryBase;
51constexpr PciReg16 PciConfig::kPrefetchableMemoryLimit;
52constexpr PciReg32 PciConfig::kPrefetchableMemoryBaseUpper;
53constexpr PciReg32 PciConfig::kPrefetchableMemoryLimitUpper;
54constexpr PciReg16 PciConfig::kIoBaseUpper;
55constexpr PciReg16 PciConfig::kIoLimitUpper;
56constexpr PciReg32 PciConfig::kBridgeExpansionRomAddress;
57constexpr PciReg16 PciConfig::kBridgeControl;
58
59/*
60 * Derived classes should not be in the global namespace, all users
61 * of PciConfig should only have the base interface available
62 */
63namespace {
64
65class PciPioConfig : public PciConfig {
66public:
67    PciPioConfig(uintptr_t base)
68        : PciConfig(base, PciAddrSpace::PIO) {}
69    uint8_t Read(const PciReg8 addr) const override;
70    uint16_t Read(const PciReg16 addr) const override;
71    uint32_t Read(const PciReg32 addr) const override;
72    void Write(const PciReg8 addr, uint8_t val) const override;
73    void Write(const PciReg16 addr, uint16_t val) const override;
74    void Write(const PciReg32 addr, uint32_t val) const override;
75
76private:
77    friend PciConfig;
78};
79
80uint8_t PciPioConfig::Read(const PciReg8 addr) const {
81    uint32_t val;
82    zx_status_t status = Pci::PioCfgRead(static_cast<uint32_t>(base_ + addr.offset()), &val, 8u);
83    DEBUG_ASSERT(status == ZX_OK);
84    return static_cast<uint8_t>(val & 0xFF);
85}
86uint16_t PciPioConfig::Read(const PciReg16 addr) const {
87    uint32_t val;
88    zx_status_t status = Pci::PioCfgRead(static_cast<uint32_t>(base_ + addr.offset()), &val, 16u);
89    DEBUG_ASSERT(status == ZX_OK);
90    return static_cast<uint16_t>(val & 0xFFFF);
91}
92uint32_t PciPioConfig::Read(const PciReg32 addr) const {
93    uint32_t val;
94    zx_status_t status = Pci::PioCfgRead(static_cast<uint32_t>(base_ + addr.offset()), &val, 32u);
95    DEBUG_ASSERT(status == ZX_OK);
96    return val;
97}
98void PciPioConfig::Write(const PciReg8 addr, uint8_t val) const {
99    zx_status_t status = Pci::PioCfgWrite(static_cast<uint32_t>(base_ + addr.offset()), val, 8u);
100    DEBUG_ASSERT(status == ZX_OK);
101}
102void PciPioConfig::Write(const PciReg16 addr, uint16_t val) const {
103    zx_status_t status = Pci::PioCfgWrite(static_cast<uint32_t>(base_ + addr.offset()), val, 16u);
104    DEBUG_ASSERT(status == ZX_OK);
105}
106void PciPioConfig::Write(const PciReg32 addr, uint32_t val) const {
107    zx_status_t status = Pci::PioCfgWrite(static_cast<uint32_t>(base_ + addr.offset()), val, 32u);
108    DEBUG_ASSERT(status == ZX_OK);
109}
110
111class PciMmioConfig : public PciConfig {
112public:
113    PciMmioConfig(uintptr_t base)
114        : PciConfig(base, PciAddrSpace::MMIO) {}
115    uint8_t Read(const PciReg8 addr) const override;
116    uint16_t Read(const PciReg16 addr) const override;
117    uint32_t Read(const PciReg32 addr) const override;
118    void Write(const PciReg8 addr, uint8_t val) const override;
119    void Write(const PciReg16 addr, uint16_t val) const override;
120    void Write(const PciReg32 addr, uint32_t val) const override;
121
122private:
123    friend PciConfig;
124};
125
126// MMIO Config Implementation
127uint8_t PciMmioConfig::Read(const PciReg8 addr) const {
128    auto reg = reinterpret_cast<const volatile uint8_t*>(base_ + addr.offset());
129    return *reg;
130}
131
132uint16_t PciMmioConfig::Read(const PciReg16 addr) const {
133    auto reg = reinterpret_cast<const volatile uint16_t*>(base_ + addr.offset());
134    return LE16(*reg);
135}
136
137uint32_t PciMmioConfig::Read(const PciReg32 addr) const {
138    auto reg = reinterpret_cast<const volatile uint32_t*>(base_ + addr.offset());
139    return LE32(*reg);
140}
141
142void PciMmioConfig::Write(PciReg8 addr, uint8_t val) const {
143    auto reg = reinterpret_cast<volatile uint8_t*>(base_ + addr.offset());
144    *reg = val;
145}
146
147void PciMmioConfig::Write(PciReg16 addr, uint16_t val) const {
148    auto reg = reinterpret_cast<volatile uint16_t*>(base_ + addr.offset());
149    *reg = LE16(val);
150}
151
152void PciMmioConfig::Write(PciReg32 addr, uint32_t val) const {
153    auto reg = reinterpret_cast<volatile uint32_t*>(base_ + addr.offset());
154    *reg = LE32(val);
155}
156
157} // anon namespace
158
159fbl::RefPtr<PciConfig> PciConfig::Create(uintptr_t base, PciAddrSpace addr_type) {
160    fbl::AllocChecker ac;
161    fbl::RefPtr<PciConfig> cfg = nullptr;
162
163    LTRACEF("base %#" PRIxPTR ", type %s\n", base, (addr_type == PciAddrSpace::PIO) ? "PIO" : "MIO");
164
165    if (addr_type == PciAddrSpace::PIO) {
166        cfg = fbl::AdoptRef(new (&ac) PciPioConfig(base));
167    } else {
168        cfg = fbl::AdoptRef(new (&ac) PciMmioConfig(base));
169    }
170
171    if (!ac.check()) {
172        TRACEF("failed to allocate memory for PciConfig!\n");
173    }
174
175    return cfg;
176}
177
178void PciConfig::DumpConfig(uint16_t len) const {
179    printf("%u bytes of raw config (base %s:%#" PRIxPTR ")\n",
180           len, (addr_space_ == PciAddrSpace::MMIO) ? "MMIO" : "PIO", base_);
181    if (addr_space_ == PciAddrSpace::MMIO) {
182        hexdump8(reinterpret_cast<const void*>(base_), len);
183    } else {
184        // PIO space can't be dumped directly so we read a row at a time
185        constexpr uint8_t row_len = 16;
186        uint32_t pos = 0;
187        uint8_t buf[row_len];
188
189        do {
190            for (uint16_t i = 0; i < row_len; i++) {
191                buf[i] = Read(PciReg8(static_cast<uint8_t>(pos + i)));
192            }
193
194            hexdump8_ex(buf, row_len, base_ + pos);
195            pos += row_len;
196        } while (pos < PCIE_BASE_CONFIG_SIZE);
197    }
198}
199