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 "type1.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 #include "pci_priv.h"
21
22 #if ARCH_X86
23 // Only supported on x86
24
25 #include <arch/x86/descriptor.h>
26 #include <arch/x86.h>
27
28 #define LOCAL_TRACE 0
29
type1_read_byte(uint8_t bus,uint8_t slot,uint8_t func,uint8_t offset)30 static uint16_t type1_read_byte(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
31 /* create configuration address as per Figure 1 */
32 uint32_t address = (uint32_t)((bus << 16) | (slot << 11) |
33 (func << 8) | (offset & 0xfc) | ((uint32_t)0x80000000));
34
35 /* write out the address */
36 outpd(0xCF8, address);
37
38 /* read in the data 32 bits at a time and then shift over our byte */
39 uint8_t tmp = ((inpd(0xCFC) >> ((offset & 3) * 8)) & 0xffff);
40 return tmp;
41 }
42
type1_read_half(uint8_t bus,uint8_t slot,uint8_t func,uint8_t offset)43 static uint16_t type1_read_half(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
44 /* create configuration address as per Figure 1 */
45 uint32_t address = (uint32_t)((bus << 16) | (slot << 11) |
46 (func << 8) | (offset & 0xfc) | ((uint32_t)0x80000000));
47
48 /* write out the address */
49 outpd(0xCF8, address);
50
51 /* read in the data 32 bits at a time */
52 /* (offset & 2) * 8) = 0 will choose the first word of the 32 bits register */
53 uint16_t tmp = ((inpd(0xCFC) >> ((offset & 2) * 8)) & 0xffff);
54 return tmp;
55 }
56
type1_read_word(uint8_t bus,uint8_t slot,uint8_t func,uint8_t offset)57 static uint32_t type1_read_word(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
58 /* create configuration address as per Figure 1 */
59 uint32_t address = (uint32_t)((bus << 16) | (slot << 11) |
60 (func << 8) | (offset & 0xfc) | ((uint32_t)0x80000000));
61
62 /* write out the address */
63 outpd(0xCF8, address);
64
65 /* read in the data 32 bits at a time */
66 /* (offset & 2) * 8) = 0 will choose the first word of the 32 bits register */
67 uint32_t tmp = inpd(0xCFC);
68 return tmp;
69 }
70
71 // new C++ version
detect()72 pci_type1 *pci_type1::detect() {
73 LTRACE_ENTRY;
74
75 auto t1 = new pci_type1;
76
77 /* we don't know how many busses there are */
78 t1->set_last_bus(32);
79
80 return t1;
81 }
82
read_config_byte(const pci_location_t * state,uint32_t reg,uint8_t * value)83 int pci_type1::read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) {
84 LTRACEF("state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
85 *value = type1_read_half(state->bus, state->dev_fn >> 3, state->dev_fn & 0x7, reg);
86 return NO_ERROR;
87 }
88
read_config_half(const pci_location_t * state,uint32_t reg,uint16_t * value)89 int pci_type1::read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) {
90 LTRACEF("state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
91 *value = type1_read_half(state->bus, state->dev_fn >> 3, state->dev_fn & 0x7, reg);
92 return NO_ERROR;
93 }
94
read_config_word(const pci_location_t * state,uint32_t reg,uint32_t * value)95 int pci_type1::read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) {
96 LTRACEF("state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
97 *value = type1_read_word(state->bus, state->dev_fn >> 3, state->dev_fn & 0x7, reg);
98 return NO_ERROR;
99 }
100
101 #endif
102