#include <sys/mman.h>
#include <sys/stat.h>
#include <pci/pci.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
  struct pci_access *pacc = pci_alloc();
  struct pci_dev *dev;

  // Find the FSB Registers of the Intel 5000 chipset. It's always on
  // bus 0, device 16, function 0.
  // I have no idea what the domain argument is...
  pci_init(pacc);
  dev = pci_get_dev(pacc, 0, 0, 16, 0);
  if (dev == NULL) {
    error(1, 0, "Unsupported memory controller");
  }

  pci_fill_info(dev, PCI_FILL_IDENT);
  if(dev->vendor_id != PCI_VENDOR_ID_INTEL || dev->device_id != 0x25F0) {
    error(1, 0, "Unsupported memory controller");
  }

  u64 am_base = (u64) pci_read_long(dev, 0x48) | 
                (u64) pci_read_long(dev, 0x4C) << 32;
  u32 am_range = pci_read_long(dev, 0x50);
  pci_cleanup(pacc);

  int fd = open("/dev/mem", O_RDONLY);
  if (fd == -1) {
    error(1, errno, "Cannot open /dev/mem");
  }
  
  u8 * addr = mmap(0, am_range, PROT_READ, MAP_PRIVATE, fd, am_base);
  if (addr == MAP_FAILED) {
    error(1, errno, "Cannot mmap AMB registers");
  }

  unsigned idx;
  for (idx = 0;  idx < am_range;  idx += 0x800 /* 2KB per AMB */) {

    // Check for Intel 6400/6402
    if ((addr[idx  ] | addr[idx+1] << 8) != PCI_VENDOR_ID_INTEL) continue;
    if ((addr[idx+2] | addr[idx+3] << 8) != 0xA620) continue;

    // function 3
    int func3 = idx + 0x300;
    printf("DIMM_%1d_%1X Temp: %5.1f C     "
           "(low = %5.1f C, mid = %5.1f C, high = %5.1f C)\n",
           idx >> 15, (idx >> 11) & 0x0F,
           addr[func3 + 0x85] / 2.0,
           addr[func3 + 0x80] / 2.0,
           addr[func3 + 0x81] / 2.0,
           addr[func3 + 0x82] / 2.0);
  }
  
  munmap(addr, am_range);
  close(fd);
  return 0;
}

