first commit
This commit is contained in:
351
include/nva/nva.c
Normal file
351
include/nva/nva.c
Normal file
@@ -0,0 +1,351 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <pciaccess.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "nva.h"
|
||||
#include "util.h"
|
||||
#include "chipset.h"
|
||||
|
||||
#define PLATFORM_DEVICES_DIR "/sys/bus/platform/devices"
|
||||
|
||||
struct nva_card **nva_cards = 0;
|
||||
int nva_cardsnum = 0;
|
||||
int nva_cardsmax = 0;
|
||||
int nva_vgaarberr = 0;
|
||||
|
||||
struct pci_id_match nv_gpu_match[] = {
|
||||
{0x104a, 0x0009, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0},
|
||||
{0x12d2, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, 0x30000, 0xffff0000},
|
||||
{0x10de, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, 0x30000, 0xffff0000},
|
||||
{0x10de, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, 0x48000, 0xffffff00},
|
||||
};
|
||||
|
||||
struct pci_id_match nv_apu_match[] = {
|
||||
{0x10de, 0x01b0, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0},
|
||||
{0x10de, 0x006b, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0},
|
||||
};
|
||||
|
||||
struct pci_id_match nv_smu_match[] = {
|
||||
{0x10de, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, 0xb4000, 0xffffff00},
|
||||
};
|
||||
|
||||
struct pci_id_match nv_eth_match[] = {
|
||||
{0x10de, 0x01c3, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP */
|
||||
{0x10de, 0x0066, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP2 */
|
||||
{0x10de, 0x0086, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP2A - network class */
|
||||
{0x10de, 0x008c, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP2A - bridge class */
|
||||
{0x10de, 0x00d6, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* CK8 */
|
||||
{0x10de, 0x00e6, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* CK8S - network class */
|
||||
{0x10de, 0x00df, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* CK8S - bridge class */
|
||||
{0x10de, 0x0056, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* CK804 - network class */
|
||||
{0x10de, 0x0057, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* CK804 - bridge class */
|
||||
{0x10de, 0x0037, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP04 - network class */
|
||||
{0x10de, 0x0038, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP04 - bridge class */
|
||||
{0x10de, 0x0268, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP51 - network class */
|
||||
{0x10de, 0x0269, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP51 - bridge class */
|
||||
{0x10de, 0x0372, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP55 - network class */
|
||||
{0x10de, 0x0373, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP55 - bridge class */
|
||||
{0x10de, 0x03e5, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP61 ? */
|
||||
{0x10de, 0x03e6, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP61 ? */
|
||||
{0x10de, 0x03ee, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP61 ? */
|
||||
{0x10de, 0x03ef, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP61 - bridge class */
|
||||
{0x10de, 0x0450, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP65 - network class */
|
||||
{0x10de, 0x0451, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP65 ? */
|
||||
{0x10de, 0x0452, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP65 - bridge class */
|
||||
{0x10de, 0x0453, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP65 ? */
|
||||
{0x10de, 0x054c, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP67 - network class */
|
||||
{0x10de, 0x054d, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP67 ? */
|
||||
{0x10de, 0x054e, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP67 ? */
|
||||
{0x10de, 0x054f, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP67 ? */
|
||||
{0x10de, 0x07dc, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP73 - network class */
|
||||
{0x10de, 0x07dd, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP73 ? */
|
||||
{0x10de, 0x07de, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP73 ? */
|
||||
{0x10de, 0x07df, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP73 ? */
|
||||
{0x10de, 0x0760, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77 - network class */
|
||||
{0x10de, 0x0761, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77 ? */
|
||||
{0x10de, 0x0762, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77 ? */
|
||||
{0x10de, 0x0763, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77 ? */
|
||||
{0x10de, 0x0764, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77 - bridge class */
|
||||
{0x10de, 0x0765, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77 ? */
|
||||
{0x10de, 0x0766, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77 ? */
|
||||
{0x10de, 0x0767, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77 ? */
|
||||
{0x10de, 0x0ab0, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP79 - network class */
|
||||
{0x10de, 0x0ab1, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP79 ? */
|
||||
{0x10de, 0x0ab2, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP79 ? */
|
||||
{0x10de, 0x0ab3, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP79 ? */
|
||||
{0x10de, 0x0570, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 0 */
|
||||
{0x10de, 0x0571, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 1 */
|
||||
{0x10de, 0x0572, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 2 */
|
||||
{0x10de, 0x0573, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 3 */
|
||||
{0x10de, 0x0574, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 4 */
|
||||
{0x10de, 0x0575, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 5 */
|
||||
{0x10de, 0x0576, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 6 */
|
||||
{0x10de, 0x0577, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 7 */
|
||||
{0x10de, 0x0578, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 8 */
|
||||
{0x10de, 0x0579, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 9 */
|
||||
{0x10de, 0x057a, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 10 */
|
||||
{0x10de, 0x057b, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 11 */
|
||||
{0x10de, 0x057c, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 12 */
|
||||
{0x10de, 0x057d, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 13 */
|
||||
{0x10de, 0x057e, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 14 */
|
||||
{0x10de, 0x057f, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP77/MCP79 alt 15 */
|
||||
{0x10de, 0x0d7d, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0}, /* MCP89 */
|
||||
};
|
||||
|
||||
struct nva_card *nva_init_gpu(struct pci_device *dev) {
|
||||
struct nva_card *card = calloc(sizeof *card, 1);
|
||||
if (!card)
|
||||
return 0;
|
||||
card->type = NVA_DEVICE_GPU;
|
||||
card->bus_type = NVA_BUS_PCI;
|
||||
card->bus.pci = dev;
|
||||
int ret = pci_device_map_range(dev, dev->regions[0].base_addr, dev->regions[0].size, PCI_DEV_MAP_FLAG_WRITABLE, &card->bar0);
|
||||
if (ret) {
|
||||
fprintf (stderr, "WARN: Can't probe %04x:%02x:%02x.%x\n", dev->domain, dev->bus, dev->dev, dev->func);
|
||||
free(card);
|
||||
return 0;
|
||||
}
|
||||
card->bar0len = dev->regions[0].size;
|
||||
if (dev->regions[1].size) {
|
||||
card->hasbar1 = 1;
|
||||
card->bar1len = dev->regions[1].size;
|
||||
ret = pci_device_map_range(dev, dev->regions[1].base_addr, dev->regions[1].size, PCI_DEV_MAP_FLAG_WRITABLE, &card->bar1);
|
||||
if (ret) {
|
||||
card->bar1 = 0;
|
||||
}
|
||||
}
|
||||
if (dev->regions[2].size && !dev->regions[2].is_IO) {
|
||||
card->hasbar2 = 1;
|
||||
card->bar2len = dev->regions[2].size;
|
||||
ret = pci_device_map_range(dev, dev->regions[2].base_addr, dev->regions[2].size, PCI_DEV_MAP_FLAG_WRITABLE, &card->bar2);
|
||||
if (ret) {
|
||||
card->bar2 = 0;
|
||||
}
|
||||
} else if (dev->regions[3].size) {
|
||||
card->hasbar2 = 1;
|
||||
card->bar2len = dev->regions[3].size;
|
||||
ret = pci_device_map_range(dev, dev->regions[3].base_addr, dev->regions[3].size, PCI_DEV_MAP_FLAG_WRITABLE, &card->bar2);
|
||||
if (ret) {
|
||||
card->bar2 = 0;
|
||||
}
|
||||
}
|
||||
/* ignore errors */
|
||||
pci_device_map_legacy(dev, 0, 0x100000, PCI_DEV_MAP_FLAG_WRITABLE, &card->rawmem);
|
||||
card->rawio = pci_legacy_open_io(dev, 0, 0x10000);
|
||||
int iobar = -1;
|
||||
if (dev->regions[2].size && dev->regions[2].is_IO)
|
||||
iobar = 2;
|
||||
if (dev->regions[5].size && dev->regions[5].is_IO)
|
||||
iobar = 5;
|
||||
if (iobar != -1) {
|
||||
card->iobar = pci_device_open_io(dev, dev->regions[iobar].base_addr, dev->regions[iobar].size);
|
||||
card->iobarlen = dev->regions[iobar].size;
|
||||
}
|
||||
uint32_t pmc_id = nva_grd32(card->bar0, 0);
|
||||
parse_pmc_id(pmc_id, &card->chipset);
|
||||
return card;
|
||||
}
|
||||
|
||||
struct nva_card *nva_init_smu(struct pci_device *dev) {
|
||||
struct nva_card *card = calloc(sizeof *card, 1);
|
||||
if (!card)
|
||||
return 0;
|
||||
card->type = NVA_DEVICE_SMU;
|
||||
card->bus_type = NVA_BUS_PCI;
|
||||
card->bus.pci = dev;
|
||||
int ret = pci_device_map_range(dev, dev->regions[0].base_addr, dev->regions[0].size, PCI_DEV_MAP_FLAG_WRITABLE, &card->bar0);
|
||||
if (ret) {
|
||||
fprintf (stderr, "WARN: Can't probe %04x:%02x:%02x.%x\n", dev->domain, dev->bus, dev->dev, dev->func);
|
||||
free(card);
|
||||
return 0;
|
||||
}
|
||||
card->bar0len = dev->regions[0].size;
|
||||
return card;
|
||||
}
|
||||
|
||||
struct nva_card *nva_init_apu(struct pci_device *dev) {
|
||||
struct nva_card *card = calloc(sizeof *card, 1);
|
||||
if (!card)
|
||||
return 0;
|
||||
card->type = NVA_DEVICE_APU;
|
||||
card->bus_type = NVA_BUS_PCI;
|
||||
card->bus.pci = dev;
|
||||
int ret = pci_device_map_range(dev, dev->regions[0].base_addr, dev->regions[0].size, PCI_DEV_MAP_FLAG_WRITABLE, &card->bar0);
|
||||
if (ret) {
|
||||
fprintf (stderr, "WARN: Can't probe %04x:%02x:%02x.%x\n", dev->domain, dev->bus, dev->dev, dev->func);
|
||||
free(card);
|
||||
return 0;
|
||||
}
|
||||
card->bar0len = dev->regions[0].size;
|
||||
return card;
|
||||
}
|
||||
|
||||
struct nva_card *nva_init_eth(struct pci_device *dev) {
|
||||
struct nva_card *card = calloc(sizeof *card, 1);
|
||||
if (!card)
|
||||
return 0;
|
||||
card->type = NVA_DEVICE_ETH;
|
||||
card->bus_type = NVA_BUS_PCI;
|
||||
card->bus.pci = dev;
|
||||
int ret = pci_device_map_range(dev, dev->regions[0].base_addr, dev->regions[0].size, PCI_DEV_MAP_FLAG_WRITABLE, &card->bar0);
|
||||
if (ret) {
|
||||
fprintf (stderr, "WARN: Can't probe %04x:%02x:%02x.%x\n", dev->domain, dev->bus, dev->dev, dev->func);
|
||||
free(card);
|
||||
return 0;
|
||||
}
|
||||
card->bar0len = dev->regions[0].size;
|
||||
return card;
|
||||
}
|
||||
|
||||
static int check_modalias(const char *node_name) {
|
||||
char path[PATH_MAX];
|
||||
char buffer[255] = { 0 };
|
||||
FILE *file;
|
||||
|
||||
/* Check if the GPU is indeed made by NVIDIA by checking the compatibility
|
||||
* field of the device tree, exposed to the userspace via modalias. As
|
||||
* we are lazy, let's not parse the modalias string but just check if
|
||||
* it mentions nvidia at all. If it does, return 0.
|
||||
*/
|
||||
|
||||
snprintf(path, PATH_MAX, "%s/%s/modalias", PLATFORM_DEVICES_DIR, node_name);
|
||||
if (!(file = fopen(path, "r"))) {
|
||||
return 2;
|
||||
}
|
||||
fread(buffer, 1, sizeof(buffer), file);
|
||||
fclose(file);
|
||||
return strstr(buffer, "nvidia") == NULL;
|
||||
}
|
||||
|
||||
struct nva_card *nva_init_platform() {
|
||||
uint32_t tmp, bar0 = 0xffffffff;
|
||||
struct nva_card *card;
|
||||
struct dirent *dent;
|
||||
DIR *dir;
|
||||
int fd;
|
||||
|
||||
/* Look for a gpu on the "platform" bus by looking at the platform bus,
|
||||
* looking for an entry such as '/sys/bus/platform/devices/57000000.gpu'.
|
||||
* We only expect up to one entry. This will not pick up the GPUs older
|
||||
* than the tk1 because there was no "gpu" node before that, only gr2d
|
||||
* and gr3d.
|
||||
*/
|
||||
if (!(dir = opendir(PLATFORM_DEVICES_DIR)))
|
||||
return NULL;
|
||||
while ((dent = readdir(dir))) {
|
||||
/* FIXME: this may not work on downstream kernels */
|
||||
const char *last_four = dent->d_name + strlen(dent->d_name) - 4;
|
||||
if (strcmp(last_four, ".gpu") == 0) {
|
||||
if (sscanf(dent->d_name, "%x.gpu", &tmp) == 1) {
|
||||
/* Check if the modalias contains nvidia */
|
||||
if (check_modalias(dent->d_name))
|
||||
continue;
|
||||
|
||||
/* We have found the gpu, exit the loop */
|
||||
bar0 = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
if (bar0 == 0xffffffff)
|
||||
return NULL;
|
||||
|
||||
/* we have found a gpu */
|
||||
card = calloc(sizeof *card, 1);
|
||||
if (!card)
|
||||
return 0;
|
||||
card->type = NVA_DEVICE_GPU;
|
||||
card->bus_type = NVA_BUS_PLATFORM;
|
||||
card->bus.platform_address = bar0;
|
||||
|
||||
if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) {
|
||||
fprintf (stderr, "WARN: Can't open /dev/mem. Are you root?\n");
|
||||
free(card);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf (stdout, "bar0: 0x%x\n", bar0);
|
||||
|
||||
card->bar0 = mmap(0, 0x1000000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, bar0);
|
||||
if (!card->bar0) {
|
||||
fprintf (stderr, "WARN: Can't map the physical address 0x%x\n", bar0);
|
||||
free(card);
|
||||
return 0;
|
||||
}
|
||||
card->bar0len = 0x1000000;
|
||||
|
||||
uint32_t pmc_id = nva_grd32(card->bar0, 0);
|
||||
parse_pmc_id(pmc_id, &card->chipset);
|
||||
|
||||
/* Enable bar1 for the devices we know because I cannot figure out how
|
||||
* to read the relevant part of the DT via sysfs...
|
||||
*/
|
||||
if (card->chipset.chipset == 0xea) {
|
||||
card->hasbar1 = 1;
|
||||
card->bar1len = 0x1000000;
|
||||
card->bar1 = mmap(0, 0x1000000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x58000000);
|
||||
}
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
struct {
|
||||
struct pci_id_match *match;
|
||||
size_t len;
|
||||
struct nva_card *(*func) (struct pci_device *dev);
|
||||
} nva_types[] = {
|
||||
{ nv_gpu_match, ARRAY_SIZE(nv_gpu_match), nva_init_gpu },
|
||||
{ nv_smu_match, ARRAY_SIZE(nv_smu_match), nva_init_smu },
|
||||
{ nv_apu_match, ARRAY_SIZE(nv_apu_match), nva_init_apu },
|
||||
{ nv_eth_match, ARRAY_SIZE(nv_eth_match), nva_init_eth },
|
||||
};
|
||||
|
||||
int nva_init() {
|
||||
int ret;
|
||||
ret = pci_system_init();
|
||||
if (ret)
|
||||
return -1;
|
||||
nva_vgaarberr = pci_device_vgaarb_init();
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nva_types); i++) {
|
||||
for (j = 0; j < nva_types[i].len; j++) {
|
||||
struct pci_device_iterator* it = pci_id_match_iterator_create(&nva_types[i].match[j]);
|
||||
if (!it) {
|
||||
pci_system_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct pci_device *dev;
|
||||
while ((dev = pci_device_next(it))) {
|
||||
ret = pci_device_probe(dev);
|
||||
if (ret) {
|
||||
fprintf (stderr, "WARN: Can't probe %04x:%02x:%02x.%x\n", dev->domain, dev->bus, dev->dev, dev->func);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
struct nva_card *card = nva_types[i].func(dev);
|
||||
if (card)
|
||||
ADDARRAY(nva_cards, card);
|
||||
}
|
||||
pci_iterator_destroy(it);
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally, try reading the platform */
|
||||
struct nva_card *card = nva_init_platform();
|
||||
if (card)
|
||||
ADDARRAY(nva_cards, card);
|
||||
|
||||
return (nva_cardsnum == 0);
|
||||
}
|
||||
Reference in New Issue
Block a user