| /* |
| * Copyright (C) 2014 Damien Zammit <damien@zamaudio.com> |
| * Copyright (C) 2017 Patrick Rudolph <siro@das-labor.org> |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <stdio.h> |
| #include <inttypes.h> |
| |
| #include "intelmetool.h" |
| #include "mmap.h" |
| #include "rcba.h" |
| |
| static const int size = 0x4000; |
| |
| /* Returns the physical RCBA base address or zero on error. */ |
| u32 get_rcba_phys(void) |
| { |
| struct pci_access *pacc; |
| struct pci_dev *sb; |
| uint32_t rcba_phys; |
| |
| pacc = pci_alloc(); |
| pacc->method = PCI_ACCESS_I386_TYPE1; |
| |
| pci_init(pacc); |
| pci_scan_bus(pacc); |
| |
| sb = pci_get_dev(pacc, 0, 0, 0x1f, 0); |
| if (!sb) { |
| printf("Uh oh, southbridge not on BDF(0:31:0), please report " |
| "this error, exiting.\n"); |
| pci_cleanup(pacc); |
| return 0; |
| } |
| pci_fill_info(sb, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_SIZES | |
| PCI_FILL_CLASS); |
| |
| rcba_phys = pci_read_long(sb, 0xf0) & 0xfffffffe; |
| |
| pci_free_dev(sb); |
| pci_cleanup(pacc); |
| |
| return rcba_phys; |
| } |
| |
| /* |
| * Writes 'val' to RCBA register at address 'addr'. |
| * Returns 1 on error and 0 on success. |
| */ |
| int write_rcba32(uint32_t addr, uint32_t val) |
| { |
| volatile uint8_t *rcba; |
| const uint32_t rcba_phys = get_rcba_phys(); |
| |
| if (!rcba_phys) { |
| printf("Could not get RCBA address\n"); |
| return 1; |
| } |
| |
| rcba = map_physical((off_t)rcba_phys, size); |
| if (rcba == NULL) { |
| printf("Could not map RCBA\n" |
| "Do you have kernel cmdline argument 'iomem=relaxed' set ?\n"); |
| return 1; |
| } |
| *(uint32_t *)(rcba + addr) = val; |
| |
| munmap((void *)rcba, size); |
| return 0; |
| } |
| |
| /* |
| * Reads RCBA register at address 'addr' and stores it in 'val'. |
| * Returns 1 on error and 0 on success. |
| */ |
| int read_rcba32(uint32_t addr, uint32_t *val) |
| { |
| volatile uint8_t *rcba; |
| const uint32_t rcba_phys = get_rcba_phys(); |
| |
| if (!rcba_phys) { |
| printf("Could not get RCBA address\n"); |
| return 1; |
| } |
| |
| rcba = map_physical((off_t)rcba_phys, size); |
| if (rcba == NULL) { |
| printf("Could not map RCBA\n" |
| "Do you have kernel cmdline argument 'iomem=relaxed' set ?\n"); |
| return 1; |
| } |
| |
| *val = *(uint32_t *)(rcba + addr); |
| |
| munmap((void *)rcba, size); |
| return 0; |
| } |