174 lines
5.2 KiB
C
174 lines
5.2 KiB
C
#include "type.h"
|
|
#include "assert.h"
|
|
#include "console.h"
|
|
#include "const.h"
|
|
#include "keyboard.h"
|
|
#include "memman.h"
|
|
#include "proc.h"
|
|
#include "protect.h"
|
|
#include "proto.h"
|
|
#include "stdio.h"
|
|
#include "string.h"
|
|
#include "x86.h"
|
|
#include "global.h"
|
|
#include "tty.h"
|
|
#include "pci.h"
|
|
|
|
/*
|
|
* Copyright (C) 2020-2022 The opuntiaOS Project Authors.
|
|
* + Contributed by Nikita Melekhin <nimelehin@gmail.com>
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#define DEBUG_PCI
|
|
|
|
pci_dev_t pci_devs[NR_PCI_DEV];
|
|
|
|
static uint32_t _pci_do_read_bar(uint8_t bus, uint8_t device, uint8_t function,
|
|
uint8_t bar_id) {
|
|
uint32_t header_type = pci_read(bus, device, function, 0x0e) & 0x7f;
|
|
uint8_t max_bars = 6 - (header_type * 4);
|
|
if (bar_id >= max_bars) return 0;
|
|
uint32_t bar_val = pci_read(bus, device, function, 0x10 + 4 * bar_id);
|
|
return bar_val;
|
|
}
|
|
|
|
static bar_t _pci_get_bar(uint8_t bus, uint8_t device, uint8_t function,
|
|
uint8_t bar_id) {
|
|
bar_t result;
|
|
|
|
uint32_t bar_val = _pci_do_read_bar(bus, device, function, bar_id);
|
|
result.type = (bar_val & 0x1) ? INPUT_OUTPUT : MEMORY_MAPPED;
|
|
|
|
if (result.type == MEMORY_MAPPED) {
|
|
} else {
|
|
result.address = (uint32_t)((bar_val >> 2) << 2);
|
|
result.prefetchable = 0;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
uint32_t pci_read(uint16_t bus, uint16_t device, uint16_t function,
|
|
uint32_t offset) {
|
|
uint32_t id = (0x1 << 31) | ((bus & 0xFF) << 16) | ((device & 0x1F) << 11) |
|
|
((function & 0x07) << 8) | (offset & 0xFC);
|
|
outl(0xCF8, id);
|
|
uint32_t tmp = (uint32_t)(inl(0xCFC) >> (8 * (offset % 4)));
|
|
return tmp;
|
|
}
|
|
|
|
void pci_write(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset,
|
|
uint32_t data) {
|
|
uint32_t bus32 = bus;
|
|
uint32_t device32 = device;
|
|
uint16_t function16 = function;
|
|
uint32_t address = (1 << 31) | (bus32 << 16) | (device32 << 11) |
|
|
(function16 << 8) | (offset & 0xFC);
|
|
outl(0xCF8, address);
|
|
outl(0xCFC, data);
|
|
}
|
|
|
|
static int dev_type_by_class(pci_dev_t* dd) {
|
|
switch (dd->class_id) {
|
|
case 0x1:
|
|
switch (dd->subclass_id) {
|
|
case 0x1:
|
|
case 0x3:
|
|
case 0x4:
|
|
return DEVICE_BUS_CONTROLLER;
|
|
default:
|
|
return DEVICE_STORAGE;
|
|
}
|
|
case 0x2:
|
|
return DEVICE_NETWORK;
|
|
case 0x3:
|
|
return DEVICE_DISPLAY;
|
|
case 0x6:
|
|
return DEVICE_BRIDGE;
|
|
default:
|
|
#ifdef DEBUG_PCI
|
|
kprintf("PCI: DEVICE_UNKNOWN: Class %d subclass %d", dd->class_id,
|
|
dd->subclass_id);
|
|
#endif
|
|
return DEVICE_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
static char pci_has_device_functions(uint8_t bus, uint8_t device) {
|
|
return pci_read(bus, device, 0, 0x0e) & (1 << 7);
|
|
}
|
|
|
|
int pci_find_devices() {
|
|
#ifdef DEBUG_PCI
|
|
kprintf("PCI: scanning\n");
|
|
#endif
|
|
uint8_t bus, device, function;
|
|
int count = 0;
|
|
for (bus = 0; bus < 8; bus++) {
|
|
for (device = 0; device < 32; device++) {
|
|
uint8_t functions_count =
|
|
pci_has_device_functions(bus, device) == 0 ? 8 : 1;
|
|
for (function = 0; function < functions_count; function++) {
|
|
pci_dev_t dev =
|
|
pci_get_device_desriptor(bus, device, function);
|
|
if (dev.vendor_id == 0x0000 || dev.vendor_id == 0xffff) {
|
|
continue;
|
|
}
|
|
|
|
for (uint8_t bar_id = 0; bar_id < 6; bar_id++) {
|
|
bar_t bar = _pci_get_bar(bus, device, function, bar_id);
|
|
if (bar.address && (bar.type == INPUT_OUTPUT)) {
|
|
dev.port_base = (uint32_t)bar.address;
|
|
}
|
|
}
|
|
dev.type = dev_type_by_class(&dev);
|
|
pci_devs[count ++] = dev;
|
|
#ifdef DEBUG_PCI
|
|
kprintf("PCI: Vendor %x, devID %x, cl %x scl %x\n",
|
|
dev.vendor_id, dev.device_id, dev.class_id,
|
|
dev.subclass_id);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
pci_dev_t pci_get_device_desriptor(uint8_t bus, uint8_t device,
|
|
uint8_t function) {
|
|
pci_dev_t new_device = {0};
|
|
|
|
new_device.bus = bus;
|
|
new_device.device = device;
|
|
new_device.function = function;
|
|
|
|
new_device.vendor_id = pci_read(bus, device, function, 0x00);
|
|
new_device.device_id = pci_read(bus, device, function, 0x02);
|
|
|
|
new_device.class_id = pci_read(bus, device, function, 0x0b);
|
|
new_device.subclass_id = pci_read(bus, device, function, 0x0a);
|
|
new_device.interface_id = pci_read(bus, device, function, 0x09);
|
|
new_device.revision_id = pci_read(bus, device, function, 0x08);
|
|
|
|
new_device.interrupt = pci_read(bus, device, function, 0x3c);
|
|
|
|
return new_device;
|
|
}
|
|
|
|
uint32_t pci_read_bar(pci_dev_t* pci_dev, int bar_id) {
|
|
return _pci_do_read_bar(pci_dev->bus, pci_dev->device, pci_dev->function,
|
|
bar_id);
|
|
}
|
|
|
|
void init_pci() { pci_find_devices(); }
|
|
|
|
pci_dev_t* get_pci_bga()
|
|
{
|
|
for (int i = 0; i < NR_PCI_DEV; ++ i) {
|
|
if (pci_devs[i].type == DEVICE_DISPLAY) return &pci_devs[i];
|
|
}
|
|
return NULL;
|
|
} |