├── .github └── workflows │ └── build.yml ├── .gitmodules ├── Makefile ├── README.md ├── amdgpu.c ├── amdgpu.h ├── amdi2c.c ├── amdi2c.h ├── amdi2cdbg.c ├── amdi2cdbg.h ├── amdpmbus.c ├── amdpmbus.h ├── amdsmbus.c ├── amdsmbus.h ├── atillk64.h ├── driverbin.h ├── ir35217.c ├── ir35217.h ├── ir356xx.c ├── ir356xx.h ├── ir3xxxx.c ├── ir3xxxx.h ├── libpci.a ├── main.c ├── ncp81022.c ├── ncp81022.h ├── rt8894a.c ├── rt8894a.h ├── up1801.c ├── up1801.h ├── up9505.c ├── up9505.h ├── vbios-tables.h ├── vbios.c ├── vbios.h ├── vrm.c ├── vrm.h ├── wolfamdbg-args.c ├── wolfamdbg.c ├── wolfamdbg.h ├── wolfamdvolt-args.c └── wolfamdvolt.h /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | name: build 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | - name: Checkout submodules 17 | uses: textbook/git-checkout-submodule-action@master 18 | - name: Make libpci 19 | run: sudo make && sudo make install-lib 20 | working-directory: ./pciutils 21 | - name: Make wolfamdvolt 22 | run: make 23 | - name: Zip build artifacts 24 | uses: montudor/action-zip@v0.1.0 25 | with: 26 | args: zip -qq -r ./build.zip wolfamdvolt wolfamdbg 27 | - name: Upload artifacts 28 | uses: actions/upload-artifact@v2 29 | with: 30 | name: build 31 | path: build.zip 32 | - name: Create release 33 | id: create_release 34 | uses: actions/create-release@v1 35 | env: 36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | with: 38 | tag_name: ${{ github.sha }} 39 | release_name: Release ${{ github.sha }} 40 | draft: true 41 | prerelease: false 42 | - name: Download artifact for release 43 | uses: actions/download-artifact@v2 44 | id: download_artifact 45 | with: 46 | name: build 47 | path: ./artifacts 48 | - name: Upload Release Asset 49 | uses: actions/upload-release-asset@v1 50 | env: 51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | with: 53 | upload_url: ${{ steps.create_release.outputs.upload_url }} 54 | asset_path: ${{ steps.download_artifact.outputs.download-path }}/build.zip 55 | asset_name: build.zip 56 | asset_content_type: application/zip 57 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "pciutils"] 2 | path = pciutils 3 | url = https://github.com/pciutils/pciutils 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | WINCC = x86_64-w64-mingw32-gcc 3 | CFLAGS = -O3 -s -static 4 | LIBS = libpci.a -lm 5 | WINLIBS = -lm 6 | 7 | all: wolfamdvolt wolfamdbg 8 | 9 | wolfamdvolt: main.c amdi2c.c amdi2cdbg.c amdgpu.c amdgpu.h amdi2c.h amdi2cdbg.h vbios-tables.h amdsmbus.c amdsmbus.h amdpmbus.c amdpmbus.h ir356xx.c ir356xx.h ir35217.c ir35217.h ncp81022.c ncp81022.h up9505.c up9505.h up1801.c up1801.h rt8894a.c rt8894a.h vrm.c vrm.h wolfamdvolt.h wolfamdvolt-args.c 10 | $(CC) $(CFLAGS) main.c amdi2c.c amdi2cdbg.c amdgpu.c wolfamdvolt-args.c ir356xx.c ir35217.c amdsmbus.c amdpmbus.c ncp81022.c up9505.c up1801.c rt8894a.c vrm.c $(LIBS) -o wolfamdvolt 11 | 12 | wolfamdbg: wolfamdbg.c wolfamdbg.h wolfamdbg-args.c amdi2c.c amdi2cdbg.c amdgpu.c amdgpu.h amdi2c.h amdi2cdbg.h wolfamdbg.h amdsmbus.c amdsmbus.h amdpmbus.c amdpmbus.h 13 | $(CC) $(CFLAGS) wolfamdbg.c wolfamdbg-args.c amdi2c.c amdi2cdbg.c amdgpu.c amdsmbus.c amdpmbus.c $(LIBS) -o wolfamdbg 14 | 15 | ad527xdbg: ad527xdbg.c ad527xdbg.h ad527xdbg-args.c amdi2c.c amdi2cdbg.c amdgpu.c amdgpu.h amdi2c.h amdi2cdbg.h ad527xdbg.h 16 | $(CC) $(CFLAGS) ad527xdbg.c ad527xdbg-args.c amdi2c.c amdi2cdbg.c amdgpu.c $(LIBS) -o ad527xdbg 17 | 18 | wolfir35217dbg: wolfir35217dbg.c wolfamdbg.h wolfamdbg-args.c amdi2c.c amdi2cdbg.c amdgpu.c amdgpu.h amdi2c.h amdi2cdbg.h wolfamdbg.h amdsmbus.c amdsmbus.h amdpmbus.c amdpmbus.h vrm.c vrm.h ir356xx.c ir356xx.h ir35217.c ir35217.h ncp81022.c up9505.c up9505.h up1801.c up1801.h rt8894a.c rt8894a.h 19 | $(CC) $(CFLAGS) wolfir35217dbg.c wolfamdbg-args.c amdi2c.c amdi2cdbg.c amdgpu.c amdsmbus.c amdpmbus.c vrm.c ir356xx.c ir35217.c ncp81022.c up9505.c up1801.c rt8894a.c $(LIBS) -o wolfir35217dbg 20 | 21 | windows-all: win-wolfamdvolt win-wolfamdbg 22 | 23 | win-wolfamdvolt: main.c amdi2c.c amdi2cdbg.c amdgpu.c amdgpu.h amdi2c.h amdi2cdbg.h vbios-tables.h wolfamdvolt-args.c 24 | $(WINCC) $(CFLAGS) main.c amdi2c.c amdi2cdbg.c amdgpu.c wolfamdvolt-args.c IR356XX.c amdsmbus.c amdpmbus.c ncp81022.c up9505.c up1801.c rt8894a.c vrm.c $(WINLIBS) -o wolfamdvolt 25 | 26 | win-wolfamdbg: wolfamdbg.c wolfamdbg.h wolfamdbg-args.c amdi2c.c amdi2cdbg.c amdgpu.c amdgpu.h amdi2c.h amdi2cdbg.h wolfamdbg.h amdsmbus.c amdsmbus.h amdpmbus.c amdpmbus.h 27 | $(WINCC) $(CFLAGS) wolfamdbg.c wolfamdbg-args.c amdi2c.c amdi2cdbg.c amdgpu.c amdsmbus.c amdpmbus.c $(WINLIBS) -o wolfamdbg 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wolfamdvolt 2 | 3 | ## Background 4 | 5 | I wanted to have low-level fine-grained control of power delivery on my AMD GPUs, and I was unwilling to actually touch the cards for it, in part because I cannot solder for shit. This, at first glance, is impossible - the I2C bus with the VRM controller is simply not connected to anything off the GPU. However... the GPU itself will happily listen/speak on the I2C bus on your behalf if you know how to ask it... 6 | 7 | The driver controls and configures the GPU via registers accessible over PCIe. Basically, a PCIe BAR (Base Address Register) is a memory-mapped region where all reads query the device, and all writes are sent to it. Functionality is exposed via 32-bit registers (rarely, they can be larger.) With knowledge of the internal registers, one can do MANY fun things with an AMD GPU, and one of them is using the internal I2C buses. 8 | 9 | I bought one of every Polaris card with a unique VRM controller (this was back before AMD mandated the use of the IR36217 with Vega's debut) to make this project. I reverse engineered the internal GPU control registers (accessible over PCIe - this is how the driver controls it) responsible for giving access to the GPU's internal I2C bus, which isn't connected to any other input/output. On this bus is the VRM controller (there's sometimes other goodies like fan controllers, temp sensors, RGB light controls.) 10 | 11 | From there, we can tweak the very lowest level details of power delivery - not just the voltage itself, but current/voltage/temp limits, load-line calibration, and more. I believe it will work on any Polaris AMD GPU, but the specific chips it supports are the Infineon/International Rectifier IR356XX and IR35217, the ON Semi NCP81022, the Realtek RT8894A, and the uPI Semiconductor uP1801 and uP9505P. 12 | 13 | ## Features 14 | 15 | - Supports temp monitoring on IR3567B 16 | - Supports voltage getting and setting on IR3567B, NCP81022, RT8894A, uP1801, and uP9505 17 | - Supports voltage getting on IR35217 (RX Vega 56/64) 18 | 19 | ## Sample output from an RX Vega 64 with no arguments: 20 | 21 | ``` 22 | [root@artica ~]# wolfamdvolt -i 0 23 | wolfamdvolt v0.95 24 | GPU 0: 25 | Number of VRMs: 2 26 | VRM 0: IR35217 27 | Number of outputs: 2 28 | Output 0: 29 | Voltage: 1.1000 30 | Offset: 0.0000 31 | Output 1: 32 | Voltage: 1.3500 33 | Offset: 0.0000 34 | VRM 1: uP1801 35 | Number of outputs: 1 36 | Output 0: 37 | Voltage: 1.2500 38 | ``` 39 | 40 | ## Donations accepted! 41 | 42 | - ETH: `0xCED1D126181874124A5Cb05563A4A8879D1CeC85` 43 | -------------------------------------------------------------------------------- /amdgpu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef __linux__ 9 | #include 10 | #include 11 | #endif 12 | 13 | #include "amdgpu.h" 14 | #include "vrm.h" 15 | 16 | void InsertAfter(AMDGPU **List, AMDGPU *Node, AMDGPU *Prev) 17 | { 18 | if(Prev) 19 | { 20 | Node->next = Prev->next; 21 | Prev->next = Node; 22 | } 23 | else 24 | { 25 | Node->next = *List; 26 | (*List)->prev = Node; 27 | *List = Node; 28 | } 29 | 30 | Node->prev = Prev; 31 | if(Node->next) Node->next->prev = Node; 32 | } 33 | 34 | void RemoveFromList(AMDGPU **List, AMDGPU *Node) 35 | { 36 | if(Node->prev) Node->prev->next = Node->next; 37 | else *List = Node->next; 38 | 39 | if(Node->next) Node->next->prev = Node->prev; 40 | else Node->prev->next = NULL; 41 | 42 | Node->prev = Node->next = NULL; 43 | } 44 | 45 | void ListSwapEntries(AMDGPU **GPUList, AMDGPU *CurGPU, AMDGPU *TestGPU) 46 | { 47 | AMDGPU *CurPrev = CurGPU->prev, *TestPrev = TestGPU->prev; 48 | 49 | if(CurPrev == TestGPU) 50 | { 51 | RemoveFromList(GPUList, CurGPU); 52 | InsertAfter(GPUList, CurGPU, TestPrev); 53 | } 54 | else if(TestPrev == CurGPU) 55 | { 56 | RemoveFromList(GPUList, TestGPU); 57 | InsertAfter(GPUList, TestGPU, CurPrev); 58 | } 59 | else 60 | { 61 | RemoveFromList(GPUList, CurGPU); 62 | RemoveFromList(GPUList, TestGPU); 63 | 64 | InsertAfter(GPUList, CurGPU, TestPrev); 65 | InsertAfter(GPUList, TestGPU, CurPrev); 66 | } 67 | } 68 | 69 | #ifdef __linux__ 70 | 71 | // Returns number indicating number of GPUs in list 72 | // Returns number of GPUs, or negative on failure 73 | int FindAMDGPUs(AMDGPU **GPUList, HANDLE DrvHandle) 74 | { 75 | struct pci_access *PCI; 76 | struct pci_dev *Device; 77 | int GPUCount = 0; 78 | uint32_t tmp; 79 | AMDGPU *CurGPU; 80 | 81 | PCI = pci_alloc(); 82 | pci_init(PCI); 83 | 84 | PCI->method = PCI_ACCESS_SYS_BUS_PCI; 85 | 86 | *GPUList = NULL; 87 | 88 | pci_scan_bus(PCI); 89 | 90 | for(Device = PCI->devices; Device; Device = Device->next) 91 | { 92 | // We only care about display adapters with AMD's vendor ID (0x1002) 93 | if(((Device->device_class & 0xFF00) >> 8) == PCI_BASE_CLASS_DISPLAY && Device->vendor_id == 0x1002) 94 | { 95 | if(!(*GPUList)) 96 | { 97 | CurGPU = *GPUList = (AMDGPU *)calloc(1, sizeof(AMDGPU)); 98 | CurGPU->prev = NULL; 99 | } 100 | else 101 | { 102 | 103 | CurGPU->next = (AMDGPU *)calloc(1, sizeof(AMDGPU)); 104 | CurGPU->next->prev = CurGPU; 105 | CurGPU = CurGPU->next; 106 | } 107 | 108 | CurGPU->SubVendor = pci_read_word(Device, PCI_SUBSYSTEM_VENDOR_ID); 109 | CurGPU->SubDevice = pci_read_word(Device, PCI_SUBSYSTEM_ID); 110 | CurGPU->Revision = pci_read_byte(Device, PCI_REVISION_ID); 111 | CurGPU->MMIOBaseAddr = Device->base_addr[5] & 0xFFFFFFF0; 112 | CurGPU->MMIOSize = Device->size[5]; 113 | CurGPU->VendorID = pci_read_word(Device, PCI_VENDOR_ID); 114 | CurGPU->DeviceID = pci_read_word(Device, PCI_DEVICE_ID); 115 | CurGPU->PCIDomain = Device->domain; 116 | CurGPU->PCIBus = Device->bus; 117 | CurGPU->PCIDevice = Device->dev; 118 | CurGPU->PCIFunction = Device->func; 119 | CurGPU->MMIOBasePtr = NULL; 120 | CurGPU->fd = -1; 121 | 122 | /* 123 | tmp = pci_read_long(Device, PCI_ROM_ADDRESS); 124 | tmp &= ~PCI_ROM_ADDRESS_MASK; 125 | tmp |= Device->rom_base_addr | PCI_ROM_ADDRESS_ENABLE; 126 | pci_write_long(Device, PCI_ROM_ADDRESS, tmp); 127 | 128 | GetVBIOSImage(CurGPU, Device->rom_size, Device->rom_base_addr & PCI_ROM_ADDRESS_MASK); 129 | 130 | tmp = pci_read_long(Device, PCI_ROM_ADDRESS); 131 | tmp &= ~PCI_ROM_ADDRESS_ENABLE; 132 | pci_write_long(Device, PCI_ROM_ADDRESS, Device->rom_base_addr); 133 | 134 | GetI2CInfo(CurGPU); 135 | */ 136 | 137 | GPUCount++; 138 | } 139 | } 140 | 141 | CurGPU->next = NULL; 142 | 143 | //ReorderListByHash(GPUList); 144 | //ReorderListByBus(GPUList); 145 | 146 | pci_cleanup(PCI); 147 | 148 | return(GPUCount); 149 | } 150 | 151 | #elif defined(_WIN32) 152 | 153 | int FindAMDGPUs(AMDGPU **GPUList, HANDLE DrvHandle) 154 | { 155 | WritePCIConfigData wrargs; 156 | ReadPCIConfigData args; 157 | int GPUCount = 0; 158 | 159 | args.Padding = 0x00; 160 | args.ReadSize = 4; 161 | 162 | for(int bus = 0; bus < 256; ++bus) 163 | { 164 | args.BusNumber = bus; 165 | 166 | for(int slot = 0; slot < 32; ++slot) 167 | { 168 | uint32_t BAR, BARSize; 169 | AMDGPU *CurrentGPU; 170 | uint16_t IDs[2]; 171 | DWORD RetSize; 172 | BOOL ret; 173 | 174 | args.Offset = 0x00; 175 | args.SlotNumber = slot; 176 | 177 | ret = DeviceIoControl(DrvHandle, 0x9C40253C, (LPVOID)&args, (DWORD)sizeof(args), (LPVOID)IDs, (DWORD)sizeof(uint32_t), &RetSize, NULL); 178 | 179 | if(!ret) 180 | { 181 | char ErrorMsg[256]; 182 | printf("DeviceIoControl returned error.\n"); 183 | 184 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ErrorMsg, sizeof(ErrorMsg), NULL); 185 | 186 | printf("Details: %s\n", ErrorMsg); 187 | continue; 188 | } 189 | else if((IDs[0] != 0x1002) && (IDs[0] != 0xFFFF)) 190 | { 191 | //printf("Device with vendor ID 0x%02X at bus %d, slot %d.\n", IDs[0], bus, slot); 192 | continue; 193 | } 194 | else if(IDs[0] == 0xFFFF) continue; 195 | else if((IDs[1] >= 0xAAF0) && (IDs[1] <= 0xAAFF)) continue; 196 | 197 | //printf("Found device with ID 0x1002:0x%04X (AMD) at bus %d, slot %d.\n", IDs[1], bus, slot); 198 | 199 | if(!(*GPUList)) 200 | *GPUList = CurrentGPU = (AMDGPU *)calloc(sizeof(AMDGPU), 1); 201 | else 202 | CurrentGPU = CurrentGPU->next = (AMDGPU *)calloc(sizeof(AMDGPU), 1); 203 | 204 | CurrentGPU->PCIBus = bus; 205 | CurrentGPU->PCISlot = slot; 206 | CurrentGPU->VendorID = IDs[0]; 207 | CurrentGPU->DeviceID = IDs[1]; 208 | 209 | args.Offset = 0x2C; 210 | DeviceIoControl(DrvHandle, 0x9C40253C, (LPVOID)&args, (DWORD)sizeof(args), (LPVOID)IDs, (DWORD)sizeof(uint32_t), &RetSize, NULL); 211 | 212 | CurrentGPU->SubVendor = IDs[0]; 213 | CurrentGPU->SubDevice = IDs[1]; 214 | 215 | args.Offset = 0x24; 216 | DeviceIoControl(DrvHandle, 0x9C40253C, (LPVOID)&args, (DWORD)sizeof(args), (LPVOID)&BAR, (DWORD)sizeof(uint32_t), &RetSize, NULL); 217 | 218 | CurrentGPU->MMIOBaseAddr = BAR & 0xFFFFFFF0UL; 219 | CurrentGPU->MMIOHandle = DrvHandle; 220 | CurrentGPU->next = NULL; 221 | 222 | wrargs.Offset = 0x24; 223 | wrargs.SlotNumber = slot; 224 | wrargs.BusNumber = bus; 225 | wrargs.Padding = 0x00; 226 | wrargs.WriteSize = 4; 227 | wrargs.Value = 0xFFFFFFFFUL; 228 | GPUCount++; 229 | 230 | ret = DeviceIoControl(DrvHandle, 0x9C402540, (LPVOID)&wrargs, (DWORD)sizeof(wrargs), (LPVOID)&BARSize, (DWORD)sizeof(uint32_t), &RetSize, NULL); 231 | 232 | if(!ret) 233 | { 234 | char ErrorMsg[256]; 235 | printf("DeviceIoControl returned error.\n"); 236 | 237 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ErrorMsg, sizeof(ErrorMsg), NULL); 238 | 239 | printf("Details: %s\n", ErrorMsg); 240 | continue; 241 | } 242 | 243 | DeviceIoControl(DrvHandle, 0x9C40253C, (LPVOID)&args, (DWORD)sizeof(args), (LPVOID)&BARSize, (DWORD)sizeof(uint32_t), &RetSize, NULL); 244 | 245 | BARSize &= 0xFFFFFFF8; 246 | CurrentGPU->MMIOSize = (~BARSize) + 1; 247 | 248 | wrargs.Value = BAR; 249 | ret = DeviceIoControl(DrvHandle, 0x9C402540, (LPVOID)&wrargs, (DWORD)sizeof(wrargs), (LPVOID)&BARSize, (DWORD)sizeof(uint32_t), &RetSize, NULL); 250 | } 251 | } 252 | 253 | return(GPUCount); 254 | } 255 | 256 | #endif 257 | 258 | #ifdef __linux__ 259 | 260 | // Returns 0 on success, negative on failure 261 | int InitAMDGPUMMIO(AMDGPU *GPU) 262 | { 263 | struct pci_access *PCI = NULL; 264 | struct pci_dev *Device = NULL; 265 | 266 | PCI = pci_alloc(); 267 | pci_init(PCI); 268 | 269 | PCI->method = PCI_ACCESS_SYS_BUS_PCI; 270 | 271 | Device = pci_get_dev(PCI, GPU->PCIDomain, GPU->PCIBus, GPU->PCIDevice, GPU->PCIFunction); 272 | if (Device) 273 | { 274 | uint16_t cmd_state = pci_read_word(Device, PCI_COMMAND); 275 | if (0 == (cmd_state & PCI_COMMAND_MEMORY)) 276 | { 277 | //No application or driver enabled PCI memory space for this device yet. Enable it now. 278 | pci_write_word(Device, PCI_COMMAND, cmd_state | PCI_COMMAND_MEMORY); 279 | } 280 | pci_free_dev(Device); 281 | } 282 | pci_cleanup(PCI); 283 | 284 | if(GPU->MMIOBasePtr) return(0); // Check if already initialized 285 | 286 | GPU->fd = open("/dev/mem", O_RDWR | O_SYNC); 287 | 288 | if(GPU->fd == -1) return(-1); 289 | 290 | GPU->MMIOBasePtr = (void *)mmap(NULL, GPU->MMIOSize, PROT_READ | PROT_WRITE, MAP_SHARED, GPU->fd, GPU->MMIOBaseAddr); 291 | 292 | if(GPU->MMIOBasePtr == MAP_FAILED) 293 | { 294 | close(GPU->fd); 295 | GPU->fd = -1; 296 | GPU->MMIOBasePtr = NULL; 297 | return(-2); 298 | } 299 | 300 | return(0); 301 | } 302 | 303 | #elif defined(_WIN32) 304 | 305 | int InitAMDGPUMMIO(AMDGPU *GPU) 306 | { 307 | return(0); 308 | } 309 | 310 | #endif 311 | 312 | bool AMDGPUIsHawaii(AMDGPU *GPU) 313 | { 314 | for(int i = 0; i < (sizeof(HAWAII_PCI_DEVICE_IDS) / sizeof(HAWAII_PCI_DEVICE_IDS[0])); ++i) 315 | { 316 | if(HAWAII_PCI_DEVICE_IDS[i] == GPU->DeviceID) return(true); 317 | } 318 | 319 | return(false); 320 | } 321 | 322 | bool AMDGPUIsBonaire(AMDGPU *GPU) 323 | { 324 | for(int i = 0; i < (sizeof(BONAIRE_PCI_DEVICE_IDS) / sizeof(BONAIRE_PCI_DEVICE_IDS[0])); ++i) 325 | { 326 | if(BONAIRE_PCI_DEVICE_IDS[i] == GPU->DeviceID) return(true); 327 | } 328 | 329 | return(false); 330 | } 331 | 332 | bool AMDGPUIsVega(AMDGPU *GPU) 333 | { 334 | for(int i = 0; i < (sizeof(VEGA_PCI_DEVICE_IDS) / sizeof(VEGA_PCI_DEVICE_IDS[0])); ++i) 335 | { 336 | if(VEGA_PCI_DEVICE_IDS[i] == GPU->DeviceID) return(true); 337 | } 338 | 339 | return(false); 340 | } 341 | 342 | #ifdef __linux__ 343 | 344 | void CloseAMDGPUMMIO(AMDGPU *GPU) 345 | { 346 | munmap(GPU->MMIOBasePtr, GPU->MMIOSize); 347 | GPU->MMIOBasePtr = NULL; 348 | GPU->MMIOSize = 0; 349 | 350 | close(GPU->fd); 351 | GPU->fd = -1; 352 | } 353 | 354 | #elif defined(_WIN32) 355 | 356 | void CloseAMDGPUMMIO(AMDGPU *GPU) 357 | { 358 | if(GPU->MMIOHandle != INVALID_HANDLE_VALUE) CloseHandle(GPU->MMIOHandle); 359 | GPU->MMIOHandle = INVALID_HANDLE_VALUE; 360 | } 361 | 362 | #endif 363 | 364 | void ReleaseAMDGPUs(AMDGPU *GPUList) 365 | { 366 | AMDGPU *NextGPU; 367 | 368 | for(AMDGPU *GPU = GPUList; GPU; GPU = NextGPU) 369 | { 370 | #ifdef __linux__ 371 | if(GPU->MMIOBasePtr) CloseAMDGPUMMIO(GPU); 372 | #elif defined(_WIN32) 373 | if(GPU->MMIOHandle != INVALID_HANDLE_VALUE) CloseAMDGPUMMIO(GPU); 374 | #endif 375 | 376 | if(GPU->VRMs) 377 | { 378 | VRMController *NextVRM; 379 | for(VRMController *VRM = GPU->VRMs; VRM; VRM = NextVRM) 380 | { 381 | NextVRM = VRM->next; 382 | free(VRM); 383 | } 384 | 385 | GPU->VRMs = NULL; 386 | GPU->VRMCount = 0; 387 | } 388 | 389 | NextGPU = GPU->next; 390 | free(GPU); 391 | } 392 | } 393 | 394 | void AMDGPUFreeVRMs(AMDGPU *GPU) 395 | { 396 | VRMController *NextVRM; 397 | 398 | if(!GPU->VRMs) return; 399 | 400 | for(VRMController *VRM = GPU->VRMs; VRM; VRM = NextVRM) 401 | { 402 | NextVRM = VRM->next; 403 | free(VRM); 404 | } 405 | 406 | GPU->VRMs = NULL; 407 | GPU->VRMCount = 0; 408 | } 409 | 410 | #ifdef __linux__ 411 | 412 | uint32_t _ReadMMIOReg(AMDGPU *GPU, uint32_t reg) 413 | { 414 | // MMIO not initialized 415 | if(!GPU->MMIOBasePtr) return(0); 416 | 417 | // Register index is fucked 418 | if((reg << 2) >= GPU->MMIOSize) return(0); 419 | 420 | return(((uint32_t *)GPU->MMIOBasePtr)[reg]); 421 | } 422 | 423 | uint32_t ReadMMIOReg(AMDGPU *GPU, uint32_t reg) 424 | { 425 | // MMIO not initialized 426 | if(!GPU->MMIOBasePtr) return(0); 427 | 428 | // Register index is fucked 429 | if((reg << 2) >= GPU->MMIOSize) 430 | { 431 | WriteMMIOReg(GPU, 0x00 * 4, reg); 432 | return(_ReadMMIOReg(GPU, 1)); 433 | } 434 | else 435 | { 436 | return(_ReadMMIOReg(GPU, reg)); 437 | } 438 | } 439 | 440 | void _WriteMMIOReg(AMDGPU *GPU, uint32_t reg, uint32_t value) 441 | { 442 | // MMIO not initialized 443 | if(!GPU->MMIOBasePtr) return; 444 | 445 | // Register index is fucked 446 | if((reg << 2) >= GPU->MMIOSize) return; 447 | 448 | ((uint32_t *)GPU->MMIOBasePtr)[reg] = value; 449 | } 450 | 451 | void WriteMMIOReg(AMDGPU *GPU, uint32_t reg, uint32_t value) 452 | { 453 | // MMIO not initialized 454 | if(!GPU->MMIOBasePtr) return; 455 | 456 | // Register index is fucked 457 | if((reg << 2) >= GPU->MMIOSize) 458 | { 459 | _WriteMMIOReg(GPU, 0x00 * 4, reg); 460 | _WriteMMIOReg(GPU, 1, value); 461 | } 462 | else 463 | { 464 | _WriteMMIOReg(GPU, reg, value); 465 | } 466 | usleep(1); 467 | } 468 | 469 | #elif defined(_WIN32) 470 | 471 | void WriteMMIOReg(AMDGPU *GPU, uint32_t reg, uint32_t value) 472 | { 473 | WritePhysMemData wrargs; 474 | BOOL ret; 475 | 476 | if((GPU->MMIOHandle == INVALID_HANDLE_VALUE) || (!GPU->MMIOBaseAddr)) return; 477 | 478 | wrargs.PhysMemAddr.QuadPart = (uint64_t)GPU->MMIOBaseAddr; // MM_INDEX 479 | wrargs.WriteSize = 4; 480 | wrargs.Value = reg << 2; 481 | 482 | ret = DeviceIoControl(GPU->MMIOHandle, 0x9C402538, (LPVOID)&wrargs, (DWORD)sizeof(wrargs), NULL, 0UL, NULL, NULL); 483 | 484 | if(!ret) 485 | { 486 | char ErrorMsg[256]; 487 | printf("DeviceIoControl returned error.\n"); 488 | 489 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ErrorMsg, sizeof(ErrorMsg), NULL); 490 | 491 | printf("Details: %s\n", ErrorMsg); 492 | } 493 | 494 | wrargs.PhysMemAddr.QuadPart = (uint64_t)(GPU->MMIOBaseAddr + 4); 495 | wrargs.Value = value; 496 | 497 | ret = DeviceIoControl(GPU->MMIOHandle, 0x9C402538, (LPVOID)&wrargs, (DWORD)sizeof(wrargs), NULL, 0UL, NULL, NULL); 498 | 499 | if(!ret) 500 | { 501 | char ErrorMsg[256]; 502 | printf("DeviceIoControl returned error.\n"); 503 | 504 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ErrorMsg, sizeof(ErrorMsg), NULL); 505 | 506 | printf("Details: %s\n", ErrorMsg); 507 | } 508 | 509 | Sleep(2); 510 | } 511 | 512 | uint32_t ReadMMIOReg(AMDGPU *GPU, uint32_t reg) 513 | { 514 | WritePhysMemData wrargs; 515 | ReadPhysMemData rdargs; 516 | uint32_t retval; 517 | BOOL ret; 518 | 519 | if((GPU->MMIOHandle == INVALID_HANDLE_VALUE) || (!GPU->MMIOBaseAddr)) return(0); 520 | 521 | wrargs.PhysMemAddr.QuadPart = (uint64_t)GPU->MMIOBaseAddr; // MM_INDEX 522 | wrargs.WriteSize = 4; 523 | wrargs.Value = reg << 2; 524 | 525 | ret = DeviceIoControl(GPU->MMIOHandle, 0x9C402538, (LPVOID)&wrargs, (DWORD)sizeof(wrargs), NULL, 0UL, NULL, NULL); 526 | 527 | if(!ret) 528 | { 529 | char ErrorMsg[256]; 530 | printf("DeviceIoControl returned error.\n"); 531 | 532 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ErrorMsg, sizeof(ErrorMsg), NULL); 533 | 534 | printf("Details: %s\n", ErrorMsg); 535 | } 536 | 537 | Sleep(2); 538 | 539 | rdargs.PhysMemAddr.QuadPart = GPU->MMIOBaseAddr + 4; // MM_DATA 540 | rdargs.ReadSize = 4; 541 | 542 | ret = DeviceIoControl(GPU->MMIOHandle, 0x9C402534, (LPVOID)&rdargs, (DWORD)sizeof(rdargs), (LPVOID)&retval, (DWORD)sizeof(uint32_t), NULL, NULL); 543 | 544 | if(!ret) 545 | { 546 | char ErrorMsg[256]; 547 | printf("DeviceIoControl returned error.\n"); 548 | 549 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ErrorMsg, sizeof(ErrorMsg), NULL); 550 | 551 | printf("Details: %s\n", ErrorMsg); 552 | } 553 | 554 | return(retval); 555 | } 556 | 557 | #endif 558 | -------------------------------------------------------------------------------- /amdgpu.h: -------------------------------------------------------------------------------- 1 | #ifndef __AMDGPU_H 2 | #define __AMDGPU_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef _WIN32 8 | #include 9 | #endif 10 | 11 | #ifdef __linux__ 12 | typedef int HANDLE; 13 | #define INVALID_HANDLE_VALUE -1 14 | #endif 15 | 16 | typedef struct _AMDGPU AMDGPU; 17 | 18 | #include "vrm.h" 19 | 20 | // No support for mobile GPUs 21 | static const uint16_t HAWAII_PCI_DEVICE_IDS[] = { 0x67A0, 0x67A1, 0x67A2, 0x67A8, 22 | 0x67A9, 0x67AA, 0x67B0, 0x67B1, 23 | 0x67B8, 0x67B9, 0x67BA, 0x67BE }; 24 | 25 | static const uint16_t BONAIRE_PCI_DEVICE_IDS[] = { 0x6649, 0x6650, 0x6651, 0x6658, 26 | 0x665c, 0x665d, 0x665f }; 27 | 28 | static const uint16_t VEGA_PCI_DEVICE_IDS[] = { 0x6863, 0x687F }; 29 | 30 | typedef struct _I2CPINS 31 | { 32 | uint16_t SDA; 33 | uint16_t SCL; 34 | } I2CPINS; 35 | 36 | typedef struct _I2CGPIO 37 | { 38 | uint16_t mmGENERIC_I2C_CONTROL; 39 | uint16_t mmGENERIC_I2C_INTERRUPT_CONTROL; 40 | uint16_t mmGENERIC_I2C_STATUS; 41 | uint16_t mmGENERIC_I2C_SPEED; 42 | uint16_t mmGENERIC_I2C_SETUP; 43 | uint16_t mmGENERIC_I2C_TRANSACTION; 44 | uint16_t mmGENERIC_I2C_DATA; 45 | uint16_t mmGENERIC_I2C_PIN_SELECTION; 46 | uint16_t mmGENERIC_I2C_PIN_DEBUG; 47 | } I2CGPIO; 48 | 49 | struct _AMDGPU 50 | { 51 | int fd; 52 | bool I2CHas16BitEntries; 53 | void *MMIOBasePtr; 54 | 55 | #ifdef _WIN32 56 | HANDLE MMIOHandle; 57 | #endif 58 | 59 | I2CPINS *I2CPins; 60 | I2CGPIO I2CGPIOAddrs; 61 | VRMController *VRMs; 62 | 63 | uint32_t MMIOBaseAddr, MMIOSize, VRMCount; 64 | uint8_t Revision, I2CAddress, I2CLine, I2CControlOffset; 65 | uint16_t VendorID, DeviceID, SubVendor, SubDevice, PCIBus, PCIDevice, PCISlot, PCIFunction; 66 | int PCIDomain; 67 | struct _AMDGPU *next, *prev; 68 | }; 69 | 70 | #ifdef _WIN32 71 | 72 | #pragma pack(push, 1) 73 | 74 | typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS; 75 | 76 | typedef struct _ReadPCIConfigData 77 | { 78 | uint8_t Offset; 79 | uint8_t SlotNumber; 80 | uint8_t BusNumber; 81 | uint8_t Padding; 82 | uint32_t ReadSize; 83 | } ReadPCIConfigData; 84 | 85 | typedef struct _WritePCIConfigData 86 | { 87 | uint8_t Offset; 88 | uint8_t SlotNumber; 89 | uint8_t BusNumber; 90 | uint8_t Padding; 91 | uint32_t WriteSize; 92 | uint32_t Value; 93 | } WritePCIConfigData; 94 | 95 | typedef struct _ReadPhysMemData 96 | { 97 | PHYSICAL_ADDRESS PhysMemAddr; 98 | uint64_t ReadSize; // Only 1, 2 or 4 allowed. 99 | } ReadPhysMemData; 100 | 101 | typedef struct _WritePhysMemData 102 | { 103 | PHYSICAL_ADDRESS PhysMemAddr; 104 | uint64_t WriteSize; // Only 1, 2, or 4 allowed. 105 | uint64_t Value; 106 | } WritePhysMemData; 107 | 108 | #pragma pack(pop) 109 | 110 | #endif 111 | 112 | int FindAMDGPUs(AMDGPU **GPUList, HANDLE DrvHandle); 113 | int InitAMDGPUMMIO(AMDGPU *GPU); 114 | void AMDGPUFreeVRMs(AMDGPU *GPU); 115 | void CloseAMDGPUMMIO(AMDGPU *GPU); 116 | void ReleaseAMDGPUs(AMDGPU *GPUList); 117 | 118 | uint32_t ReadMMIOReg(AMDGPU *GPU, uint32_t reg); 119 | void WriteMMIOReg(AMDGPU *GPU, uint32_t reg, uint32_t value); 120 | 121 | bool AMDGPUIsHawaii(AMDGPU *GPU); 122 | bool AMDGPUIsBonaire(AMDGPU *GPU); 123 | bool AMDGPUIsVega(AMDGPU *GPU); 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /amdi2c.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "amdgpu.h" 7 | #include "amdi2c.h" 8 | #include "amdi2cdbg.h" 9 | 10 | void AMDI2CSoftReset(AMDGPU *GPU) 11 | { 12 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_CONTROL, I2C_ENABLE | I2C_SOFT_RESET); 13 | usleep(1000); 14 | 15 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_CONTROL, I2C_ENABLE); 16 | usleep(1000); 17 | } 18 | 19 | void AMDI2CReset(AMDGPU *GPU) 20 | { 21 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_CONTROL, I2C_ENABLE | I2C_RESET | I2C_GO); 22 | usleep(500); 23 | 24 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_CONTROL, I2C_ENABLE); 25 | usleep(500); 26 | } 27 | 28 | // SetI2CSpeedValues sets the parameters for the 29 | // speed-related I2C bus AMD GPU access. 30 | // Params: 31 | // GPU: 32 | // This MUST have its GPIO addresses and pins set. 33 | // It is only used to write the settings to the AMD 34 | // GPU control register. 35 | // Threshold: 36 | // This is a two-bit field which is interpreted as so: 37 | // 0x00 - Greater than zero 38 | // 0x01 - 1/4 of total samples 39 | // 0x02 - 1/2 of total samples 40 | // 0x03 - 3/4 of total samples 41 | // DisableFilterWhileStalling: 42 | // Boolean which does as it says on the tin: 43 | // false: Disable 44 | // true: Enable 45 | // StartAndStopTiming: 46 | // Field two bits wide that is interpreted as follows: 47 | // 0x00: 1/4 I2C data bit period 48 | // 0x01: 1/2 I2C data bit period 49 | // 0x02: 3/4 I2C data bit period 50 | // 0x03: Reserved 51 | // Prescale: 52 | // Full 16 bit prescale value. 53 | static void SetI2CSpeedValues(AMDGPU *GPU, uint8_t Threshold, bool DisableFilterWhileStalling, uint8_t StartAndStopTiming, uint16_t Prescale) 54 | { 55 | uint32_t I2CSpeedCfgDword = 0UL; 56 | 57 | I2CSpeedCfgDword |= (Threshold & 0x03); 58 | I2CSpeedCfgDword |= ((DisableFilterWhileStalling) ? (1UL << 4UL) : 0UL); 59 | I2CSpeedCfgDword |= (((uint32_t)(StartAndStopTiming & 0x03)) << 8UL); 60 | I2CSpeedCfgDword |= (((uint32_t)Prescale) << 16UL); 61 | 62 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_SPEED, I2CSpeedCfgDword); 63 | } 64 | 65 | // SetI2CSetupParams configures the basic options of the line to access. 66 | // Params: 67 | // GPU: 68 | // This MUST have its GPIO addresses and pins set. 69 | // It is only used to write the settings to the AMD 70 | // GPU control register. 71 | // DataDriveEn: 72 | // false: SDA line is pulled up by an external resistor. 73 | // true: SDA line is driven by I2C pads. 74 | // DataDriveSel: 75 | // false: Drive for 10 memory clocks 76 | // true: Drive for 20 memory clocks. 77 | // ClkDriveEn: 78 | // false: SCL line is pulled up by an external resistor. 79 | // true: I2C pads drive SCL 80 | // IntraByteDelay: 81 | // Does what it says on the tin - 8 bit field. 82 | // TimeoutLimit: 83 | // Ditto - timeout limit. 8 bit field. 84 | static void SetI2CSetupParams(AMDGPU *GPU, bool DataDriveEn, bool DataDriveSel, bool ClkDriveEn, uint8_t IntraByteDelay, uint8_t TimeoutLimit) 85 | { 86 | uint32_t SetupDword = 0UL; 87 | 88 | SetupDword |= ((DataDriveEn) ? 1UL : 0UL); 89 | SetupDword |= ((DataDriveSel) ? (1UL << 1UL) : 0); 90 | SetupDword |= ((ClkDriveEn) ? (1UL << 7UL) : 0); 91 | SetupDword |= (((uint32_t)IntraByteDelay) << 8UL); 92 | SetupDword |= (((uint32_t)TimeoutLimit) << 24UL); 93 | 94 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_SETUP, SetupDword); 95 | } 96 | 97 | 98 | void AMDGPUI2CInit(AMDGPU *GPU, uint8_t bus, uint8_t addr) 99 | { 100 | GPU->I2CAddress = addr; 101 | 102 | if(AMDGPUIsHawaii(GPU) || AMDGPUIsBonaire(GPU)) 103 | { 104 | GPU->I2CGPIOAddrs = GCN2_I2C_GPIO_ADDRS; 105 | } 106 | else if(AMDGPUIsVega(GPU)) 107 | { 108 | GPU->I2CGPIOAddrs = GCN5_I2C_GPIO_ADDRS; 109 | } 110 | else 111 | { 112 | GPU->I2CGPIOAddrs = GCN3_I2C_GPIO_ADDRS; 113 | } 114 | 115 | if(bus > 0x07) GPU->I2CPins = GCN3_I2C_BUS_LINES[0]; // HWI2C 116 | else GPU->I2CPins = GCN3_I2C_BUS_LINES[bus]; 117 | 118 | AMDI2CReset(GPU); 119 | 120 | bool VegaGPU = AMDGPUIsVega(GPU); 121 | 122 | uint32_t tmp = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_INTERRUPT_CONTROL); 123 | if(!(tmp & 0x02)) WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_INTERRUPT_CONTROL, tmp | 0x02); 124 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_INTERRUPT_CONTROL, 0x00); 125 | 126 | // Select pins 127 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_PIN_SELECTION, (GPU->I2CPins->SDA << 8) | GPU->I2CPins->SCL); 128 | 129 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_SPEED, ((0x021C << 16) | 0x02)); 130 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_SPEED, ((0x010E << 16) | 0x02)); 131 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_SPEED, ((0x87 << 16) | 0x02)); 132 | SetI2CSpeedValues(GPU, 0x02, false, 0x00, 0x01F4); // 0x01F4 == 50Khz; 0x01E8 == 100Khz 133 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_SPEED, ((0x43 << 16) | 0x03)); 134 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_SETUP, 0x32000107); 135 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_SETUP, 0x28010007); 136 | if(VegaGPU) SetI2CSetupParams(GPU, false, false, false, 1, 0xFF); 137 | else SetI2CSetupParams(GPU, true, true, true, 1, 0xFF); 138 | //SetI2CSetupParams(GPU, false, false, false, 1, 0xFF); 139 | //AMDI2CReset(GPU); 140 | 141 | 142 | } 143 | 144 | void I2CExecTX(AMDGPU *GPU) 145 | { 146 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_CONTROL, I2C_ENABLE | I2C_GO); 147 | usleep(2500); 148 | } 149 | 150 | // Parameter ret is for I2C status return; optional 151 | uint8_t AMDI2CReadByte(AMDGPU *GPU, uint8_t reg, uint32_t *ret) 152 | { 153 | uint8_t Byte; 154 | uint32_t Status; 155 | char *DbgStr = NULL; 156 | 157 | AMDI2CSoftReset(GPU); 158 | 159 | // Number of transactions is shifted up by 16 160 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (1 << 16) | I2C_START | I2C_STOP); 161 | 162 | // Select address to read from 163 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | ((GPU->I2CAddress << 1) << 8)); 164 | 165 | // Indicate register to read from 166 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | (reg << 8)); 167 | 168 | I2CExecTX(GPU); 169 | 170 | /* 171 | do 172 | { 173 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 174 | if((Status & I2C_STATUS_MASK) & I2C_DONE) break; 175 | usleep(5); 176 | 177 | } while(!(((Status) & I2C_STATUS_MASK) & I2C_TIMEOUT)); 178 | */ 179 | if((((Status & I2C_STATUS_MASK) & I2C_ABORTED)) || ((Status & I2C_STATUS_MASK) & I2C_NACK)) 180 | { 181 | AMDI2CDbgGetStatusString(&DbgStr, Status); 182 | if(DbgStr) 183 | { 184 | if(I2CDebugOutput) printf("AMDI2CReadByte: First TX errored with %s.\n", DbgStr); 185 | free(DbgStr); 186 | } 187 | } 188 | 189 | if(ret) *ret = Status; 190 | 191 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (3 << 16) | I2C_START | I2C_STOP | I2C_RW); 192 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | (((GPU->I2CAddress << 1) | 0x01) << 8)); 193 | 194 | I2CExecTX(GPU); 195 | 196 | /* 197 | do 198 | { 199 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 200 | if((Status & I2C_STATUS_MASK) & I2C_DONE) break; 201 | usleep(5); 202 | 203 | } while(!(((Status) & I2C_STATUS_MASK) & I2C_TIMEOUT)); 204 | */ 205 | 206 | if((((Status & I2C_STATUS_MASK) & I2C_ABORTED)) || ((Status & I2C_STATUS_MASK) & I2C_NACK)) 207 | { 208 | AMDI2CDbgGetStatusString(&DbgStr, Status); 209 | if(DbgStr) 210 | { 211 | if(I2CDebugOutput) printf("AMDI2CReadByte: Second TX errored with %s.\n", DbgStr); 212 | free(DbgStr); 213 | } 214 | } 215 | 216 | if(ret) *ret |= Status; 217 | 218 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | I2C_DATA_RW); 219 | 220 | Byte = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 221 | 222 | return(Byte); 223 | } 224 | 225 | // Parameter ret is for I2C status return; optional 226 | uint16_t AMDI2CReadWord(AMDGPU *GPU, uint32_t *ret) 227 | { 228 | uint16_t Word; 229 | uint32_t Status; 230 | char *DbgStr = NULL; 231 | 232 | AMDI2CSoftReset(GPU); 233 | 234 | // Number of transactions is shifted up by 16 235 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (1 << 16) | I2C_START | I2C_STOP | I2C_ACK_ON_READ | I2C_STOP_ON_NACK | I2C_RW); 236 | 237 | // Select address to read from 238 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | (((GPU->I2CAddress << 1) | 0x01) << 8)); 239 | 240 | I2CExecTX(GPU); 241 | 242 | /* 243 | do 244 | { 245 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 246 | if((Status & I2C_STATUS_MASK) & I2C_DONE) break; 247 | usleep(5); 248 | 249 | } while(!(((Status) & I2C_STATUS_MASK) & I2C_TIMEOUT)); 250 | */ 251 | if((((Status & I2C_STATUS_MASK) & I2C_ABORTED)) || ((Status & I2C_STATUS_MASK) & I2C_NACK)) 252 | { 253 | AMDI2CDbgGetStatusString(&DbgStr, Status); 254 | if(DbgStr) 255 | { 256 | if(I2CDebugOutput) printf("AMDI2CReadByte: First TX errored with %s.\n", DbgStr); 257 | free(DbgStr); 258 | } 259 | } 260 | 261 | if(ret) *ret = Status; 262 | 263 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(2) | I2C_DATA_RW); 264 | 265 | Word = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 266 | Word = (Word << 8) | ((ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF); 267 | 268 | return(Word); 269 | } 270 | 271 | #if 1 272 | 273 | int AMDI2CWriteByte(AMDGPU *GPU, uint8_t reg, uint8_t data) 274 | { 275 | uint32_t Status; 276 | char *DbgStr = NULL; 277 | 278 | AMDI2CSoftReset(GPU); 279 | 280 | // Length of write is 2 - register, then data 281 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (2 << 16) | I2C_START | I2C_STOP); 282 | 283 | // Write in address 284 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | ((GPU->I2CAddress << 1) << 8)); 285 | 286 | // Select register 287 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | (reg << 8)); 288 | 289 | // Now write in data byte 290 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(2) | (data << 8)); 291 | 292 | I2CExecTX(GPU); 293 | 294 | do 295 | { 296 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 297 | if((Status & I2C_STATUS_MASK) & I2C_DONE) break; 298 | usleep(5); 299 | 300 | } while(!(((Status) & I2C_STATUS_MASK) & I2C_TIMEOUT)); 301 | 302 | if((((Status & I2C_STATUS_MASK) & I2C_ABORTED)) || ((Status & I2C_STATUS_MASK) & I2C_NACK)) 303 | { 304 | AMDI2CDbgGetStatusString(&DbgStr, Status); 305 | if(DbgStr) 306 | { 307 | if(I2CDebugOutput) printf("AMDI2CWriteByte: First TX errored with %s.\n", DbgStr); 308 | free(DbgStr); 309 | } 310 | } 311 | 312 | return(ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS) & I2C_STATUS_MASK); 313 | } 314 | 315 | uint32_t AMDI2CWriteWord(AMDGPU *GPU, uint16_t data) 316 | { 317 | uint32_t Status; 318 | char *DbgStr = NULL; 319 | 320 | AMDI2CSoftReset(GPU); 321 | 322 | // Length of write is 2 - register, then data 323 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (1 << 16) | I2C_START | I2C_STOP | I2C_STOP_ON_NACK); 324 | 325 | // Write in address 326 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | ((GPU->I2CAddress << 1) << 8)); 327 | 328 | // Write MSB 329 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | (data & 0xFF00)); 330 | 331 | // Write LSB 332 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(2) | (data << 8)); 333 | 334 | I2CExecTX(GPU); 335 | 336 | do 337 | { 338 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 339 | if((Status & I2C_STATUS_MASK) & I2C_DONE) break; 340 | usleep(5); 341 | 342 | } while(!(((Status) & I2C_STATUS_MASK) & I2C_TIMEOUT)); 343 | 344 | if((((Status & I2C_STATUS_MASK) & I2C_ABORTED)) || ((Status & I2C_STATUS_MASK) & I2C_NACK)) 345 | { 346 | AMDI2CDbgGetStatusString(&DbgStr, Status); 347 | if(DbgStr) 348 | { 349 | if(I2CDebugOutput) printf("AMDI2CWriteWord: First TX errored with %s.\n", DbgStr); 350 | free(DbgStr); 351 | } 352 | } 353 | 354 | return(Status); 355 | } 356 | 357 | #else 358 | 359 | // Length is IGNORING the fact the reg is written! 360 | uint8_t AMDI2CWrite(AMDGPU *GPU, uint8_t reg, uint8_t *data, uint8_t len) 361 | { 362 | uint32_t Status; 363 | char *DbgStr = NULL; 364 | 365 | AMDI2CSoftReset(GPU); 366 | 367 | // Length of write, add one because we must write the register to access 368 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, ((len) << 16) | I2C_START | I2C_STOP | I2C_STOP_ON_NACK); 369 | 370 | // Write in address 371 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, ((GPU->I2CAddress << 1) << 8)); 372 | 373 | // Select register 374 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | (reg << 8)); 375 | 376 | // Now put in the data 377 | for(int i = 0; i < len; ++i) 378 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(i + 1) | (data[i] << 8)); 379 | 380 | I2CExecTX(GPU); 381 | 382 | do 383 | { 384 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 385 | if((Status & I2C_STATUS_MASK) & I2C_DONE) break; 386 | usleep(5); 387 | 388 | } while(!(((Status) & I2C_STATUS_MASK) & I2C_TIMEOUT)); 389 | 390 | if((((Status & I2C_STATUS_MASK) & I2C_ABORTED)) || ((Status & I2C_STATUS_MASK) & I2C_NACK)) 391 | { 392 | AMDI2CDbgGetStatusString(&DbgStr, Status); 393 | if(DbgStr) 394 | { 395 | if(I2CDebugOutput) printf("AMDI2CWrite: TX errored with %s.\n", DbgStr); 396 | free(DbgStr); 397 | } 398 | } 399 | 400 | return(Status); 401 | } 402 | 403 | int AMDI2CWriteByte(AMDGPU *GPU, uint8_t reg, uint8_t data) 404 | { 405 | return(AMDI2CWrite(GPU, reg, &data, 2)); 406 | } 407 | 408 | #endif 409 | 410 | /* 411 | uint8_t AMDI2CWriteRaw(AMDGPU *GPU, uint8_t *data, uint8_t len) 412 | { 413 | uint8_t ret; 414 | 415 | AMDI2CSoftReset(GPU); 416 | 417 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, I2C_START | I2C_STOP_ON_NACK); 418 | 419 | // Write in address 420 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | ((GPU->I2CAddress << 1) << 8)); 421 | 422 | I2CExecTX(GPU); 423 | 424 | ret = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS) & I2C_STATUS_MASK); 425 | 426 | if(ret & I2C_STOPPED_ON_NACK) 427 | { 428 | printf("\nError: NACK\n"); 429 | return(ret); 430 | } 431 | 432 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (len << 16) | I2C_STOP_ON_NACK | I2C_STOP); 433 | 434 | // Now put in the data 435 | for(int i = 0; i < len; ++i) 436 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, (data[i] << 8)); 437 | 438 | I2CExecTX(GPU); 439 | 440 | ret |= (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS) & I2C_STATUS_MASK); 441 | 442 | if(ret & I2C_STOPPED_ON_NACK) 443 | { 444 | printf("\nError: NACK\n"); 445 | return(ret); 446 | } 447 | 448 | return(ret); 449 | } 450 | */ 451 | 452 | uint32_t AMDI2CWriteRaw(AMDGPU *GPU, uint8_t *data, uint8_t len) 453 | { 454 | uint32_t Status; 455 | char *DbgStr = NULL; 456 | 457 | AMDI2CSoftReset(GPU); 458 | 459 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, I2C_INDEX(len + 1) | I2C_START | I2C_STOP_ON_NACK | I2C_STOP); 460 | 461 | // Write in address 462 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | ((GPU->I2CAddress << 1) << 8)); 463 | 464 | for(int i = 0; i < len; ++i) 465 | { 466 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, data[i] << 8); 467 | } 468 | 469 | I2CExecTX(GPU); 470 | 471 | do 472 | { 473 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 474 | if((Status & I2C_STATUS_MASK) & I2C_DONE) break; 475 | usleep(5); 476 | 477 | } while(!(((Status) & I2C_STATUS_MASK) & I2C_TIMEOUT)); 478 | 479 | if((((Status & I2C_STATUS_MASK) & I2C_ABORTED)) || ((Status & I2C_STATUS_MASK) & I2C_NACK)) 480 | { 481 | AMDI2CDbgGetStatusString(&DbgStr, Status); 482 | if(DbgStr) 483 | { 484 | if(I2CDebugOutput) printf("AMDI2CWriteWord: First TX errored with %s.\n", DbgStr); 485 | free(DbgStr); 486 | } 487 | } 488 | 489 | return(Status); 490 | } 491 | 492 | uint8_t AMDI2CReadRaw(AMDGPU *GPU, uint8_t *dout, uint8_t len) 493 | { 494 | uint8_t ret; 495 | 496 | AMDI2CSoftReset(GPU); 497 | 498 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, I2C_INDEX(len + 1) | I2C_ACK_ON_READ | I2C_START | I2C_STOP | I2C_RW); 499 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | (((GPU->I2CAddress << 1) | 0x01) << 8)); 500 | 501 | I2CExecTX(GPU); 502 | 503 | ret = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 504 | 505 | if(ret & I2C_STOPPED_ON_NACK) 506 | { 507 | if(I2CDebugOutput) printf("\nError: NACK\n"); 508 | return(ret); 509 | } 510 | 511 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(2) | I2C_DATA_RW); 512 | 513 | for(int i = 0; i < len; ++i) 514 | { 515 | dout[i] = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 516 | } 517 | 518 | return(ret); 519 | } 520 | 521 | -------------------------------------------------------------------------------- /amdi2c.h: -------------------------------------------------------------------------------- 1 | #ifndef __AMDI2C_H 2 | #define __AMDI2C_H 3 | 4 | #include 5 | #include "amdgpu.h" 6 | 7 | //#define GPIO_PIN_SCL 0x29 //0x4D 8 | //#define GPIO_PIN_SDA 0x28 //0x4C 9 | 10 | static const I2CPINS GCN3_I2C_BUS_LINES[8][2] = 11 | { 12 | { 0x28, 0x29 }, // HWI2C pins 13 | { 0x00, 0x01 }, // DDC1 pins 14 | { 0x02, 0x03 }, // DDC2 pins 15 | { 0x04, 0x05 }, // DDC3 pins 16 | { 0x41, 0x42 }, // DDC4 pins 17 | { 0x48, 0x49 }, // DDC5 pins 18 | { 0x4A, 0x4B }, // DDC6 pins 19 | { 0x4C, 0x4D } 20 | }; 21 | 22 | /* 23 | #define GCN3_mmGENERIC_I2C_CONTROL 0x16F4 24 | #define GCN3_mmGENERIC_I2C_INTERRUPT_CONTROL 0x16F5 25 | #define GCN3_mmGENERIC_I2C_STATUS 0x16F6 26 | #define GCN3_mmGENERIC_I2C_SPEED 0x16F7 27 | #define GCN3_mmGENERIC_I2C_SETUP 0x16F8 28 | #define GCN3_mmGENERIC_I2C_TRANSACTION 0x16F9 29 | #define GCN3_mmGENERIC_I2C_DATA 0x16FA 30 | #define GCN3_mmGENERIC_I2C_PIN_SELECTION 0x16FB 31 | #define GCN3_mmGENERIC_I2C_PIN_DEBUG 0x16FC 32 | 33 | #define GCN2_mmGENERIC_I2C_CONTROL 0x1834 34 | #define GCN2_mmGENERIC_I2C_INTERRUPT_CONTROL 0x1835 35 | #define GCN2_mmGENERIC_I2C_STATUS 0x1836 36 | #define GCN2_mmGENERIC_I2C_SPEED 0x1837 37 | #define GCN2_mmGENERIC_I2C_SETUP 0x1838 38 | #define GCN2_mmGENERIC_I2C_TRANSACTION 0x1839 39 | #define GCN2_mmGENERIC_I2C_DATA 0x183A 40 | #define GCN2_mmGENERIC_I2C_PIN_SELECTION 0x183B 41 | #define GCN2_mmGENERIC_I2C_PIN_DEBUG 0x183C 42 | */ 43 | 44 | static const I2CGPIO GCN5_I2C_GPIO_ADDRS = { 0x4A64, 0x4A65, 0x4A66, 0x4A67, 0x4A68, 0x4A69, 0x4A6A, 0x4A6B, 0x4A6C }; 45 | static const I2CGPIO GCN3_I2C_GPIO_ADDRS = { 0x16F4, 0x16F5, 0x16F6, 0x16F7, 0x16F8, 0x16F9, 0x16FA, 0x16FB, 0x16FC }; 46 | static const I2CGPIO GCN2_I2C_GPIO_ADDRS = { 0x1834, 0x1835, 0x1836, 0x1837, 0x1838, 0x1839, 0x183A, 0x183B, 0x183C }; 47 | 48 | // The following only applies to wolfamdvolt: 49 | // FIXME: Add an option to make the I2C line selectable 50 | // In order to make all areas requiring a change for this 51 | // to be done, this macro will temporarily serve as a 52 | // static bus number, that being the HWI2C line. 53 | #define STATIC_I2C_LINE_FIXME ((uint8_t)0U) 54 | 55 | #define I2C_DONE_INT 0x01 56 | #define I2C_DONE_ACK (0x01 << 1) 57 | #define I2C_DONE_MASK (0x01 << 2) 58 | 59 | // Speed constants for mmGENERIC_I2C_SPEED 60 | #define I2C_THRESHOLD 0x03 61 | #define I2C DISABLE_FILTER_WHILE_STALLING 0x04 62 | #define I2C_START_STOP_TIMING_CNTL 0x300 63 | #define I2C_PRESCALE (0xFFFF << 16) 64 | 65 | // Control constants for mmGENERIC_I2C_CONTROL 66 | #define I2C_GO 1 67 | #define I2C_SOFT_RESET (1 << 1) 68 | #define I2C_RESET (1 << 2) 69 | #define I2C_ENABLE (1 << 3) 70 | 71 | // Status constants for mmGENERIC_I2C_STATUS 72 | #define I2C_STATUS_MASK (~(0x0F)) 73 | #define I2C_DONE (1 << 4) 74 | #define I2C_ABORTED (1 << 5) 75 | #define I2C_TIMEOUT (1 << 6) 76 | #define I2C_STOPPED_ON_NACK (1 << 9) 77 | #define I2C_NACK (1 << 10) 78 | 79 | // Pin constants for mmGENERIC_I2C_PIN_SELECTION 80 | #define I2C_SCL_PIN_MASK 0x7F 81 | #define I2C_SDA_PIN_MASK (0x7F << 8) 82 | 83 | // Transaction constants for mmGENERIC_I2C_TRANSACTION 84 | #define I2C_RW 1 85 | #define I2C_STOP_ON_NACK (1 << 8) 86 | #define I2C_ACK_ON_READ (1 << 9) 87 | #define I2C_START (1 << 12) 88 | #define I2C_STOP (1 << 13) 89 | 90 | // Data constants for mmGENERIC_I2C_DATA 91 | #define I2C_DATA_RW 1 92 | #define I2C_INDEX(x) ((x) << 16) 93 | #define I2C_INDEX_WRITE (1 << 31) 94 | 95 | extern bool I2CDebugOutput; 96 | 97 | void AMDGPUI2CInit(AMDGPU *GPU, uint8_t bus, uint8_t addr); 98 | uint8_t AMDI2CReadByte(AMDGPU *GPU, uint8_t reg, uint32_t *ret); 99 | uint16_t AMDI2CReadWord(AMDGPU *GPU, uint32_t *ret); 100 | uint32_t AMDI2CWriteWord(AMDGPU *GPU, uint16_t data); 101 | 102 | int AMDI2CWriteByte(AMDGPU *GPU, uint8_t reg, uint8_t data); 103 | uint32_t AMDI2CWriteRaw(AMDGPU *GPU, uint8_t *data, uint8_t len); 104 | uint8_t AMDI2CReadRaw(AMDGPU *GPU, uint8_t *dout, uint8_t len); 105 | void AMDI2CSoftReset(AMDGPU *GPU); 106 | void I2CExecTX(AMDGPU *GPU); 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /amdi2cdbg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "amdgpu.h" 9 | #include "amdi2c.h" 10 | #include "amdi2cdbg.h" 11 | 12 | #ifdef DEBUG 13 | bool I2CDebugOutput = true; 14 | #else 15 | bool I2CDebugOutput = false; 16 | #endif 17 | 18 | static const char DoneStr[] = "I2C_DONE"; 19 | static const char AbortedStr[] = "I2C_ABORTED"; 20 | static const char TimeoutStr[] = "I2C_TIMEOUT"; 21 | static const char StopNACKStr[] = "I2C_STOPPED_ON_NACK"; 22 | static const char NACKStr[] = "I2C_NACK"; 23 | 24 | // Caller is responsible for freeing returned pointer! 25 | // If NULL is returned - none of the status flags were set. 26 | void AMDI2CDbgGetStatusString(char **DbgStrIn, uint32_t I2CStatusVal) 27 | { 28 | char *DbgStrBuf; 29 | if(!DbgStrIn) return; 30 | 31 | *DbgStrIn = (char *)malloc(AMDI2CDBG_STATUS_STR_MAX_LEN); 32 | DbgStrBuf = *DbgStrIn; 33 | 34 | bool AppendSuffix = false; 35 | 36 | DbgStrBuf[0] = 0x00; 37 | /* 38 | if(I2CStatusVal & I2C_DONE) 39 | { 40 | strcat(DbgStrBuf, DoneStr); 41 | AppendSuffix = true; 42 | } 43 | */ 44 | 45 | if(I2CStatusVal & I2C_ABORTED) 46 | { 47 | if(AppendSuffix) strcat(DbgStrBuf, ", "); 48 | strcat(DbgStrBuf, AbortedStr); 49 | AppendSuffix = true; 50 | } 51 | 52 | if(I2CStatusVal & I2C_TIMEOUT) 53 | { 54 | if(AppendSuffix) strcat(DbgStrBuf, ", "); 55 | strcat(DbgStrBuf, TimeoutStr); 56 | AppendSuffix = true; 57 | } 58 | 59 | if(I2CStatusVal & I2C_STOPPED_ON_NACK) 60 | { 61 | if(AppendSuffix) strcat(DbgStrBuf, ", "); 62 | strcat(DbgStrBuf, StopNACKStr); 63 | AppendSuffix = true; 64 | } 65 | 66 | if(I2CStatusVal & I2C_NACK) 67 | { 68 | if(AppendSuffix) strcat(DbgStrBuf, ", "); 69 | strcat(DbgStrBuf, NACKStr); 70 | } 71 | 72 | if(DbgStrBuf[0] == 0x00) 73 | { 74 | free(*DbgStrIn); 75 | *DbgStrIn = NULL; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /amdi2cdbg.h: -------------------------------------------------------------------------------- 1 | #ifndef __AMDI2CDBG_H 2 | #define __AMDI2CDBG_H 3 | 4 | #include 5 | #include 6 | 7 | #include "amdi2c.h" 8 | 9 | void AMDI2CDbgGetStatusString(char **DbgStrIn, uint32_t I2CStatusVal); 10 | 11 | #define AMDI2CDBG_STATUS_STR_MAX_LEN 0x80 12 | 13 | #define I2CTXERRORCHK(I2CStatus) ((((I2CStatus) & I2C_STATUS_MASK) & (I2C_ABORTED | I2C_TIMEOUT | I2C_NACK)) || (!(((I2CStatus) & I2C_STATUS_MASK) & I2C_DONE))) 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /amdpmbus.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "amdgpu.h" 6 | #include "amdi2c.h" 7 | #include "amdsmbus.h" 8 | #include "amdi2cdbg.h" 9 | 10 | float PMBusDecodeLinearValueWithExponent(int16_t mantissa, int8_t exp) 11 | { 12 | float Exponent; 13 | 14 | // Extract sign bit and convert if needed 15 | //Exponent = (exp & 0x10) ? -((int)((~exp) & 0x0F) + 1) : (exp & 0xF); 16 | //Exponent = (exp & 0x10) ? -(~((exp & 0x0F) + 1) & 0x0F) : (exp & 0x0F); 17 | //float Value = (mantissa & 0x400) ? -(~((mantissa & 0x3FF) + 1) & 0x3FF) : (mantissa & 0x3FF); 18 | 19 | exp &= 0x1F; 20 | mantissa &= 0x7FF; 21 | if(exp > 0x0F) exp |= 0xE0; 22 | if(mantissa > 0x3FF) mantissa |= 0xF800; 23 | 24 | Exponent = (float)exp; 25 | float Value = (float)mantissa; 26 | 27 | //printf("Exponent: 0x%02X (%f)\n", exp, Exponent); 28 | //printf("Value: 0x%02X (%f)\n", mantissa, Value); 29 | 30 | // TODO: Maybe clean this so we don't need math libs? 31 | return(Value * powf(2.0, Exponent)); 32 | } 33 | 34 | float PMBusDecodeLinearValue(uint16_t Input) 35 | { 36 | float Exponent, Value; 37 | int8_t exp = Input >> 11; 38 | int16_t mantissa = Input & 0x7FF; 39 | 40 | if(exp > 0x0F) exp |= 0xE0; 41 | if(mantissa > 0x3FF) mantissa |= 0xF800; 42 | 43 | Exponent = (float)exp; 44 | Value = (float)mantissa; 45 | 46 | // TODO: Maybe clean this so we don't need math libs? 47 | return(Value * powf(2.0, Exponent)); 48 | } 49 | 50 | // TODO: I feel like this needs more sanity checking. 51 | uint16_t PMBusEncodeValueToLinearWithExponent(uint8_t RawExp, float Value) 52 | { 53 | float Exponent = (float)((int8_t)((RawExp & 0x10) ? (RawExp | 0xE0) : RawExp)); 54 | float ScaledVoltage = Value / powf(2.0, Exponent); 55 | //printf("Scaled voltage: %f\n", ScaledVoltage); 56 | uint16_t EncodedVoltage = (ScaledVoltage < 0) ? ((((uint16_t)ScaledVoltage) & 0x3FF) | 0x400) : (((uint16_t)ScaledVoltage) & 0x3FF); 57 | //printf("Encoded exponent: 0x%02X\n", RawExp); 58 | //printf("Encoded value: 0x%04X\n", EncodedVoltage); 59 | return(((uint16_t)EncodedVoltage) | (((uint16_t)RawExp) << 11)); 60 | //return(EncodedVoltage); 61 | } 62 | 63 | // TODO: I feel like this needs more sanity checking. 64 | uint16_t PMBusEncodeValueToLinear(float Value, uint8_t RawExp) 65 | { 66 | float Exponent = (float)((int8_t)((RawExp & 0x10) ? (RawExp | 0xE0) : RawExp)); 67 | float ScaledVoltage = Value / powf(2.0, Exponent); 68 | //printf("Scaled voltage: %f\n", ScaledVoltage); 69 | uint16_t EncodedVoltage = (ScaledVoltage < 0) ? ((((uint16_t)ScaledVoltage) & 0x3FF) | 0x400) : (((uint16_t)ScaledVoltage) & 0x3FF); 70 | //printf("Encoded exponent: 0x%02X\n", RawExp); 71 | //printf("Encoded value: 0x%04X\n", EncodedVoltage); 72 | return(((uint16_t)EncodedVoltage)); 73 | //return(EncodedVoltage); 74 | } 75 | 76 | uint32_t IR3XXXMFR_READ_REG(AMDGPU *GPU, uint8_t I2CAddr, int *ret) 77 | { 78 | uint32_t Dword; 79 | int Status; 80 | char *DbgStr; 81 | AMDI2CSoftReset(GPU); 82 | 83 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (1 << 16) | I2C_START | I2C_STOP_ON_NACK); 84 | 85 | // Write in address 86 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | ((GPU->I2CAddress << 1) << 8)); 87 | 88 | // Select PMBus address 89 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | (0xD0 << 8)); 90 | 91 | // Select I2C address 92 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(2) | (I2CAddr << 8)); 93 | 94 | I2CExecTX(GPU); 95 | 96 | do 97 | { 98 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 99 | if((Status & I2C_STATUS_MASK) & I2C_DONE) break; 100 | usleep(5); 101 | 102 | } while(!(((Status) & I2C_STATUS_MASK) & I2C_TIMEOUT)); 103 | 104 | if((((Status & I2C_STATUS_MASK) & I2C_ABORTED)) || ((Status & I2C_STATUS_MASK) & I2C_NACK)) 105 | { 106 | AMDI2CDbgGetStatusString(&DbgStr, Status); 107 | if(DbgStr) 108 | { 109 | if(I2CDebugOutput) printf("IR3XXXMFR_READ_REG: First TX errored with %s.\n", DbgStr); 110 | free(DbgStr); 111 | } 112 | } 113 | 114 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (4 << 16) | I2C_START | I2C_ACK_ON_READ | I2C_STOP | I2C_RW); 115 | 116 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | (((GPU->I2CAddress << 1) | 0x01) << 8)); 117 | 118 | I2CExecTX(GPU); 119 | 120 | do 121 | { 122 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 123 | if((Status & I2C_STATUS_MASK) & I2C_DONE) break; 124 | usleep(5); 125 | 126 | } while(!(((Status) & I2C_STATUS_MASK) & I2C_TIMEOUT)); 127 | 128 | if((((Status & I2C_STATUS_MASK) & I2C_ABORTED)) || ((Status & I2C_STATUS_MASK) & I2C_NACK)) 129 | { 130 | AMDI2CDbgGetStatusString(&DbgStr, Status); 131 | if(DbgStr) 132 | { 133 | if(I2CDebugOutput) printf("IR3XXXMFR_READ_REG: Second TX errored with %s.\n", DbgStr); 134 | free(DbgStr); 135 | } 136 | } 137 | 138 | if(ret) *ret |= Status; 139 | 140 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | I2C_DATA_RW); 141 | 142 | Dword = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 143 | 144 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(2) | I2C_DATA_RW); 145 | 146 | Dword |= ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) & 0xFF; 147 | 148 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(3) | I2C_DATA_RW); 149 | 150 | Dword |= (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) << 8) & 0xFF; 151 | 152 | if(*ret) *ret |= ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 153 | 154 | return(Dword); 155 | } 156 | -------------------------------------------------------------------------------- /amdpmbus.h: -------------------------------------------------------------------------------- 1 | #ifndef __AMDPMBUS_H 2 | #define __AMDPMBUS_H 3 | 4 | #include 5 | 6 | #define PMBUS_PAGE 0x00 7 | #define PMBUS_OPERATION 0x01 8 | #define PMBUS_ON_OFF_CONFIG 0x02 9 | #define PMBUS_CLEAR_FAULTS 0x03 10 | #define PMBUS_PHASE 0x04 11 | 12 | #define PMBUS_CAPABILITY 0x19 13 | #define PMBUS_QUERY 0x1A 14 | 15 | #define PMBUS_VOUT_MODE 0x20 16 | #define PMBUS_VOUT_COMMAND 0x21 17 | #define PMBUS_VOUT_TRIM 0x22 18 | #define PMBUS_VOUT_CAL_OFFSET 0x23 19 | #define PMBUS_VOUT_MAX 0x24 20 | #define PMBUS_VOUT_MARGIN_HIGH 0x25 21 | #define PMBUS_VOUT_MARGIN_LOW 0x26 22 | #define PMBUS_VOUT_TRANSITION_RATE 0x27 23 | #define PMBUS_VOUT_DROOP 0x28 24 | #define PMBUS_VOUT_SCALE_LOOP 0x29 25 | #define PMBUS_VOUT_SCALE_MONITOR 0x2A 26 | 27 | #define PMBUS_COEFFICIENTS 0x30 28 | #define PMBUS_POUT_MAX 0x31 29 | 30 | #define PMBUS_FAN_CONFIG_12 0x3A 31 | #define PMBUS_FAN_COMMAND_1 0x3B 32 | #define PMBUS_FAN_COMMAND_2 0x3C 33 | #define PMBUS_FAN_CONFIG_34 0x3D 34 | #define PMBUS_FAN_COMMAND_3 0x3E 35 | #define PMBUS_FAN_COMMAND_4 0x3F 36 | 37 | #define PMBUS_VOUT_OV_FAULT_LIMIT 0x40 38 | #define PMBUS_VOUT_OV_FAULT_RESPONSE 0x41 39 | #define PMBUS_VOUT_OV_WARN_LIMIT 0x42 40 | #define PMBUS_VOUT_UV_WARN_LIMIT 0x43 41 | #define PMBUS_VOUT_UV_FAULT_LIMIT 0x44 42 | #define PMBUS_VOUT_UV_FAULT_RESPONSE 0x45 43 | #define PMBUS_IOUT_OC_FAULT_LIMIT 0x46 44 | #define PMBUS_IOUT_OC_FAULT_RESPONSE 0x47 45 | #define PMBUS_IOUT_OC_LV_FAULT_LIMIT 0x48 46 | #define PMBUS_IOUT_OC_LV_FAULT_RESPONSE 0x49 47 | #define PMBUS_IOUT_OC_WARN_LIMIT 0x4A 48 | #define PMBUS_IOUT_UC_FAULT_LIMIT 0x4B 49 | #define PMBUS_IOUT_UC_FAULT_RESPONSE 0x4C 50 | 51 | #define PMBUS_OT_FAULT_LIMIT 0x4F 52 | #define PMBUS_OT_FAULT_RESPONSE 0x50 53 | #define PMBUS_OT_WARN_LIMIT 0x51 54 | #define PMBUS_UT_WARN_LIMIT 0x52 55 | #define PMBUS_UT_FAULT_LIMIT 0x53 56 | #define PMBUS_UT_FAULT_RESPONSE 0x54 57 | #define PMBUS_VIN_OV_FAULT_LIMIT 0x55 58 | #define PMBUS_VIN_OV_FAULT_RESPONSE 0x56 59 | #define PMBUS_VIN_OV_WARN_LIMIT 0x57 60 | #define PMBUS_VIN_UV_WARN_LIMIT 0x58 61 | #define PMBUS_VIN_UV_FAULT_LIMIT 0x59 62 | 63 | #define PMBUS_IIN_OC_FAULT_LIMIT 0x5B 64 | #define PMBUS_IIN_OC_WARN_LIMIT 0x5D 65 | 66 | #define PMBUS_POUT_OP_FAULT_LIMIT 0x68 67 | #define PMBUS_POUT_OP_WARN_LIMIT 0x6A 68 | #define PMBUS_PIN_OP_WARN_LIMIT 0x6B 69 | 70 | #define PMBUS_STATUS_BYTE 0x78 71 | #define PMBUS_STATUS_WORD 0x79 72 | #define PMBUS_STATUS_VOUT 0x7A 73 | #define PMBUS_STATUS_IOUT 0x7B 74 | #define PMBUS_STATUS_INPUT 0x7C 75 | #define PMBUS_STATUS_TEMPERATURE 0x7D 76 | #define PMBUS_STATUS_CML 0x7E 77 | #define PMBUS_STATUS_OTHER 0x7F 78 | #define PMBUS_STATUS_MFR_SPECIFIC 0x80 79 | #define PMBUS_STATUS_FAN_12 0x81 80 | #define PMBUS_STATUS_FAN_34 0x82 81 | 82 | #define PMBUS_READ_VIN 0x88 83 | #define PMBUS_READ_IIN 0x89 84 | #define PMBUS_READ_VCAP 0x8A 85 | #define PMBUS_READ_VOUT 0x8B 86 | #define PMBUS_READ_IOUT 0x8C 87 | #define PMBUS_READ_TEMPERATURE_1 0x8D 88 | #define PMBUS_READ_TEMPERATURE_2 0x8E 89 | #define PMBUS_READ_TEMPERATURE_3 0x8F 90 | #define PMBUS_READ_FAN_SPEED_1 0x90 91 | #define PMBUS_READ_FAN_SPEED_2 0x91 92 | #define PMBUS_READ_FAN_SPEED_3 0x92 93 | #define PMBUS_READ_FAN_SPEED_4 0x93 94 | #define PMBUS_READ_DUTY_CYCLE 0x94 95 | #define PMBUS_READ_FREQUENCY 0x95 96 | #define PMBUS_READ_POUT 0x96 97 | #define PMBUS_READ_PIN 0x97 98 | 99 | #define PMBUS_REVISION 0x98 100 | #define PMBUS_MFR_ID 0x99 101 | #define PMBUS_MFR_MODEL 0x9A 102 | #define PMBUS_MFR_REVISION 0x9B 103 | #define PMBUS_MFR_LOCATION 0x9C 104 | #define PMBUS_MFR_DATE 0x9D 105 | #define PMBUS_MFR_SERIAL 0x9E 106 | 107 | float PMBusDecodeLinearValue(uint16_t Input); 108 | float PMBusDecodeLinearValueWithExponent(uint16_t mantissa, uint8_t exp); 109 | uint16_t PMBusEncodeValueToLinearWithExponent(uint8_t RawExp, float Value); 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /amdsmbus.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "amdgpu.h" 5 | #include "amdi2c.h" 6 | #include "amdsmbus.h" 7 | #include "amdi2cdbg.h" 8 | 9 | int AMDSMBusSendByte(AMDGPU *GPU, uint8_t cmd) 10 | { 11 | AMDI2CSoftReset(GPU); 12 | 13 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (1 << 16) | I2C_START | I2C_STOP); 14 | 15 | // Write in address 16 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | ((GPU->I2CAddress << 1) << 8)); 17 | 18 | // Select register 19 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | (cmd << 8)); 20 | 21 | I2CExecTX(GPU); 22 | 23 | return(ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS) & I2C_STATUS_MASK); 24 | } 25 | 26 | int AMDSMBusWriteWord(AMDGPU *GPU, uint8_t cmd, uint16_t data) 27 | { 28 | uint32_t Status; 29 | char *DbgStr = NULL; 30 | 31 | AMDI2CSoftReset(GPU); 32 | 33 | // Number of transactions is shifted up by 16 34 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (1 << 16) | I2C_START | I2C_STOP | I2C_STOP_ON_NACK); 35 | 36 | // Select address to write to 37 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | ((GPU->I2CAddress << 1) << 8)); 38 | 39 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, (cmd << 8)); 40 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, data & 0x0000FF00); 41 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, (data & 0xFF) << 8); 42 | 43 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, data & 0x0000FF00); 44 | 45 | I2CExecTX(GPU); 46 | 47 | do 48 | { 49 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 50 | 51 | if((!(Status & I2C_STATUS_MASK) & I2C_DONE) || ((Status & I2C_STATUS_MASK) & I2C_NACK)) 52 | { 53 | AMDI2CDbgGetStatusString(&DbgStr, Status); 54 | if(DbgStr) 55 | { 56 | if(I2CDebugOutput) printf("AMDSMBusWriteWord: First TX returned with %s.\n", DbgStr); 57 | free(DbgStr); 58 | } 59 | } 60 | } while(!(Status & I2C_DONE) && !(Status & I2C_NACK)); 61 | 62 | return(Status); 63 | } 64 | 65 | /* 66 | // Parameter ret is for I2C status return; optional 67 | uint8_t AMDSMBusReadByte(AMDGPU *GPU, uint8_t cmd, int *ret) 68 | { 69 | uint8_t Byte; 70 | int tmpret; 71 | AMDI2CSoftReset(GPU); 72 | 73 | // Number of transactions is shifted up by 16 74 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (1 << 16) | I2C_START | I2C_STOP_ON_NACK); 75 | 76 | // Select address to read from 77 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | ((GPU->I2CAddress << 1) << 8)); 78 | 79 | // Indicate register to read from 80 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | (cmd << 8)); 81 | 82 | I2CExecTX(GPU); 83 | 84 | tmpret = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 85 | if(ret) *ret = tmpret; 86 | 87 | if(tmpret & I2C_STOPPED_ON_NACK) 88 | { 89 | printf("\nError: NACK\n"); 90 | return(0); 91 | } 92 | 93 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (1 << 16) | I2C_ACK_ON_READ | I2C_STOP | I2C_RW); 94 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | (((GPU->I2CAddress << 1) | 0x01) << 8)); 95 | 96 | I2CExecTX(GPU); 97 | 98 | tmpret |= ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 99 | if(ret) *ret = tmpret; 100 | 101 | if(tmpret & I2C_STOPPED_ON_NACK) 102 | { 103 | printf("\nError: NACK\n"); 104 | return(ret); 105 | } 106 | 107 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | I2C_DATA_RW); 108 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_DATA_RW); 109 | 110 | Byte = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 111 | 112 | return(Byte); 113 | } 114 | 115 | // According to the spec, this is supposed to send a block length 116 | // before the actual block, but the IR35??B doesn't seem to? 117 | int AMDSMBusReadBlock(AMDGPU *GPU, uint8_t cmd, uint8_t *len, uint8_t *ret) 118 | { 119 | AMDI2CSoftReset(GPU); 120 | 121 | // Number of transactions is shifted up by 16 122 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (1 << 16) | I2C_START); 123 | 124 | // Select address to read from 125 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | ((GPU->I2CAddress << 1) << 8)); 126 | 127 | // Indicate register to read from 128 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | (cmd << 8)); 129 | 130 | I2CExecTX(GPU); 131 | 132 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (3 << 16) | I2C_START | I2C_RW | I2C_STOP); 133 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | (((GPU->I2CAddress << 1) | 0x01) << 8)); 134 | 135 | I2CExecTX(GPU); 136 | 137 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | I2C_DATA_RW); 138 | 139 | *len = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 140 | 141 | if(*len >= 0xFF) return(-4); 142 | 143 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (*len << 16) | I2C_RW | I2C_ACK_ON_READ | I2C_STOP); 144 | 145 | //I2CExecTX(GPU); 146 | 147 | for(int i = 0; i < *len; ++i) 148 | { 149 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(2 + i) | I2C_DATA_RW); 150 | ret[i] = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 151 | } 152 | 153 | return(ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS) & I2C_STATUS_MASK); 154 | }*/ 155 | 156 | /* 157 | // Parameter ret is for I2C status return; optional 158 | uint16_t AMDSMBusReadWord(AMDGPU *GPU, uint8_t cmd, uint32_t *ret) 159 | { 160 | uint16_t Word = 0x0000; 161 | uint32_t Status; 162 | char *DbgStr = NULL; 163 | 164 | AMDI2CSoftReset(GPU); 165 | 166 | // Number of transactions is shifted up by 16 167 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (3 << 16) | I2C_START | I2C_ACK_ON_READ); 168 | 169 | // Select address to read from 170 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(2) | ((GPU->I2CAddress << 1) << 8)); 171 | 172 | // Indicate register to read from 173 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | (cmd << 8)); 174 | 175 | // Read from address zero. 176 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1)); 177 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, 0x00UL); 178 | 179 | I2CExecTX(GPU); 180 | 181 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 182 | 183 | 184 | if((Status & I2C_STATUS_MASK) & (~I2C_DONE)) 185 | { 186 | AMDI2CDbgGetStatusString(&DbgStr, Status); 187 | if(DbgStr) 188 | { 189 | printf("AMDSMBusReadWord: First TX errored with %s.\n", DbgStr); 190 | free(DbgStr); 191 | } 192 | } 193 | 194 | if(*ret) *ret = Status; 195 | 196 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (2 << 16) | I2C_START | I2C_STOP | I2C_RW | I2C_ACK_ON_READ); 197 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | (((GPU->I2CAddress << 1) | 0x01) << 8)); 198 | 199 | 200 | 201 | I2CExecTX(GPU); 202 | 203 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 204 | 205 | if(!((Status & I2C_STATUS_MASK) == I2C_DONE)) 206 | { 207 | AMDI2CDbgGetStatusString(&DbgStr, Status); 208 | if(DbgStr) 209 | { 210 | printf("AMDSMBusReadWord: Second TX errored with %s.\n", DbgStr); 211 | free(DbgStr); 212 | } 213 | } 214 | 215 | if(ret) *ret |= Status; 216 | 217 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_DATA_RW); 218 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(2) | I2C_DATA_RW); 219 | 220 | // Read out the data - first low byte, then high. 221 | Word = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 222 | 223 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(2) | I2C_DATA_RW); 224 | 225 | Word |= ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) & 0x0000FF00; 226 | 227 | return(Word); 228 | } 229 | */ 230 | 231 | // Parameter ret is for I2C status return; optional 232 | uint16_t AMDSMBusReadWord(AMDGPU *GPU, uint8_t cmd, uint32_t *ret) 233 | { 234 | uint16_t Word; 235 | uint32_t Status; 236 | char *DbgStr = NULL; 237 | 238 | AMDI2CSoftReset(GPU); 239 | 240 | // Number of transactions is shifted up by 16 241 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (1 << 16) | I2C_START); 242 | 243 | // Select address to read from 244 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | ((GPU->I2CAddress << 1) << 8)); 245 | 246 | // Indicate register to read from 247 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | (cmd << 8)); 248 | 249 | I2CExecTX(GPU); 250 | 251 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 252 | 253 | if((!(Status & I2C_STATUS_MASK) & I2C_DONE) || ((Status & I2C_STATUS_MASK) & I2C_NACK)) 254 | { 255 | AMDI2CDbgGetStatusString(&DbgStr, Status); 256 | if(DbgStr) 257 | { 258 | if(I2CDebugOutput) printf("AMDSMBusReadWord: First TX returned with %s.\n", DbgStr); 259 | free(DbgStr); 260 | } 261 | } 262 | 263 | if(ret) *ret = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS) & I2C_STATUS_MASK; 264 | 265 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (3 << 16) | I2C_START | I2C_ACK_ON_READ | I2C_STOP | I2C_RW); 266 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | (((GPU->I2CAddress << 1) | 0x01) << 8)); 267 | 268 | I2CExecTX(GPU); 269 | 270 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 271 | if((!(Status & I2C_STATUS_MASK) & I2C_DONE) || ((Status & I2C_STATUS_MASK) & I2C_NACK)) 272 | { 273 | AMDI2CDbgGetStatusString(&DbgStr, Status); 274 | if(DbgStr) 275 | { 276 | if(I2CDebugOutput) printf("AMDSMBusReadWord: Second TX returned with %s.\n", DbgStr); 277 | free(DbgStr); 278 | } 279 | } 280 | 281 | if(ret) *ret |= ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS) & I2C_STATUS_MASK; 282 | 283 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_DATA_RW); 284 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | I2C_DATA_RW); 285 | 286 | // Read out the data - first low byte, then high. 287 | Word = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 288 | 289 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(2) | I2C_DATA_RW); 290 | 291 | Word |= ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) & 0x0000FF00; 292 | 293 | return(Word); 294 | } 295 | 296 | #if 1 297 | 298 | // Parameter ret is for I2C status return; optional 299 | uint8_t AMDSMBusReadByte(AMDGPU *GPU, uint8_t cmd, uint32_t *ret) 300 | { 301 | uint8_t Byte; 302 | uint32_t Status; 303 | char *DbgStr = NULL; 304 | 305 | AMDI2CSoftReset(GPU); 306 | 307 | // Number of transactions is shifted up by 16 308 | // That is, number of bytes to read, plus one. 309 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (1 << 16) | I2C_START); 310 | 311 | // Select address to read from 312 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | ((GPU->I2CAddress << 1) << 8)); 313 | 314 | // Indicate register to read from 315 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | (cmd << 8)); 316 | 317 | I2CExecTX(GPU); 318 | 319 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 320 | 321 | 322 | if(!((Status & I2C_STATUS_MASK) == I2C_DONE)) 323 | { 324 | AMDI2CDbgGetStatusString(&DbgStr, Status); 325 | if(DbgStr) 326 | { 327 | if(I2CDebugOutput) printf("AMDSMBusReadByte: First TX errored with %s.\n", DbgStr); 328 | free(DbgStr); 329 | } 330 | } 331 | 332 | if(ret) *ret = Status; 333 | 334 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (1 << 16) | I2C_START | I2C_STOP | I2C_RW); 335 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | (((GPU->I2CAddress << 1) | 0x01) << 8)); 336 | 337 | I2CExecTX(GPU); 338 | 339 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 340 | 341 | if(!((Status & I2C_STATUS_MASK) == I2C_DONE)) 342 | { 343 | AMDI2CDbgGetStatusString(&DbgStr, Status); 344 | if(DbgStr) 345 | { 346 | if(I2CDebugOutput) printf("AMDSMBusReadByte: Second TX errored with %s.\n", DbgStr); 347 | free(DbgStr); 348 | } 349 | } 350 | 351 | 352 | if(ret) *ret |= Status; 353 | 354 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | I2C_DATA_RW); 355 | 356 | Byte = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 357 | 358 | return(Byte); 359 | } 360 | 361 | #elif 0 362 | 363 | // Parameter ret is for I2C status return; optional 364 | uint8_t AMDSMBusReadByte(AMDGPU *GPU, uint8_t cmd, uint32_t *ret) 365 | { 366 | uint8_t Byte; 367 | uint32_t Status; 368 | char *DbgStr = NULL; 369 | 370 | AMDI2CSoftReset(GPU); 371 | 372 | // Number of transactions is shifted up by 16 373 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, I2C_INDEX(1) | I2C_START); 374 | 375 | // Select address to read from 376 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | ((GPU->I2CAddress << 1) << 8)); 377 | 378 | // Indicate register to read from 379 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | (((uint32_t)cmd) << 8)); 380 | 381 | 382 | I2CExecTX(GPU); 383 | 384 | if(!((Status & I2C_STATUS_MASK) == I2C_DONE)) 385 | { 386 | AMDI2CDbgGetStatusString(&DbgStr, Status); 387 | if(DbgStr) 388 | { 389 | printf("AMDSMBusReadByte: First TX errored with %s.\n", DbgStr); 390 | free(DbgStr); 391 | } 392 | } 393 | 394 | if(*ret) *ret = Status; 395 | 396 | 397 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, I2C_INDEX(2) | I2C_START | I2C_RW | I2C_STOP); 398 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(2) | (((GPU->I2CAddress << 1) | 0x01) << 8)); 399 | 400 | I2CExecTX(GPU); 401 | 402 | Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 403 | 404 | if(!((Status & I2C_STATUS_MASK) == I2C_DONE)) 405 | { 406 | AMDI2CDbgGetStatusString(&DbgStr, Status); 407 | if(DbgStr) 408 | { 409 | printf("AMDSMBusReadByte: Second TX errored with %s.\n", DbgStr); 410 | free(DbgStr); 411 | } 412 | } 413 | 414 | if(ret) *ret |= Status; 415 | 416 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_DATA_RW); 417 | 418 | // Read out the data 419 | Byte = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 420 | 421 | return(Byte); 422 | } 423 | 424 | #else 425 | 426 | uint8_t AMDSMBusReadByte(AMDGPU *GPU, uint8_t cmd, uint32_t *ret) 427 | { 428 | return(AMDI2CReadByte(GPU, cmd, ret)); 429 | } 430 | 431 | #endif 432 | 433 | // According to the spec, this is supposed to send a block length 434 | // before the actual block, but the IR35??B doesn't seem to? 435 | int AMDSMBusReadBlock(AMDGPU *GPU, uint8_t cmd, uint8_t *len, uint8_t *ret) 436 | { 437 | AMDI2CSoftReset(GPU); 438 | 439 | // Number of transactions is shifted up by 16 440 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (1 << 16) | I2C_START | I2C_STOP_ON_NACK); 441 | 442 | // Select address to read from 443 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | ((GPU->I2CAddress << 1) << 8)); 444 | 445 | // Indicate register to read from 446 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | (cmd << 8)); 447 | 448 | I2CExecTX(GPU); 449 | 450 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (3 << 16) | I2C_START | I2C_ACK_ON_READ | I2C_RW | I2C_STOP); 451 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | (((GPU->I2CAddress << 1) | 0x01) << 8)); 452 | 453 | I2CExecTX(GPU); 454 | 455 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | I2C_DATA_RW); 456 | 457 | *len = 0; 458 | *len = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 459 | 460 | if(*len >= 0xFF) return(-4); 461 | 462 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (*len << 16) | I2C_RW | I2C_ACK_ON_READ | I2C_STOP); 463 | 464 | //I2CExecTX(GPU); 465 | 466 | for(int i = 0; i < *len; ++i) 467 | { 468 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(2 + i) | I2C_DATA_RW); 469 | ret[i] = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 470 | } 471 | 472 | return(ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS) & I2C_STATUS_MASK); 473 | } 474 | 475 | 476 | int AMDSMBusBlockWriteBlockReadProcessCall(AMDGPU *GPU, uint8_t cmd, uint8_t inlen, uint8_t *indata, uint8_t *outlen, uint8_t *ret) 477 | { 478 | if(inlen < 1 || inlen > 0xFF) return(-4); 479 | 480 | AMDI2CSoftReset(GPU); 481 | 482 | // Number of transactions is shifted up by 16 483 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, ((3 + inlen) << 16) | I2C_START | I2C_STOP); 484 | 485 | // Select address to read from 486 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | ((GPU->I2CAddress << 1) << 8)); 487 | 488 | // Indicate register to read from 489 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | (cmd << 8)); 490 | 491 | // Write byte count of data we're sending 492 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(2) | (inlen << 8)); 493 | 494 | // Write input data 495 | for(int i = 0; i < inlen; ++i) WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(3 + i) | (indata[i] << 8)); 496 | 497 | I2CExecTX(GPU); 498 | 499 | // Number of transactions is shifted up by 16 500 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_TRANSACTION, (3 << 16) | I2C_START | I2C_RW | I2C_STOP); 501 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | (((GPU->I2CAddress << 1) | 0x01) << 8)); 502 | 503 | I2CExecTX(GPU); 504 | 505 | char *DbgStr; 506 | uint32_t Status = ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS); 507 | if((!(Status & I2C_STATUS_MASK) & I2C_DONE) || ((Status & I2C_STATUS_MASK) & I2C_NACK)) 508 | { 509 | AMDI2CDbgGetStatusString(&DbgStr, Status); 510 | if(DbgStr) 511 | { 512 | printf("AMDSMBusReadWord: Second TX returned with %s.\n", DbgStr); 513 | free(DbgStr); 514 | } 515 | } 516 | 517 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | I2C_DATA_RW); 518 | 519 | /* 520 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_DATA_RW); 521 | WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(1) | I2C_DATA_RW); 522 | 523 | // Read out the data - first low byte, then high. 524 | Word = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 525 | 526 | //WriteMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA, I2C_INDEX_WRITE | I2C_INDEX(2) | I2C_DATA_RW); 527 | 528 | Word |= ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) & 0x0000FF00; 529 | */ 530 | 531 | *outlen = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 532 | printf("outlen = %d\n", *outlen); 533 | if((*outlen < 1) || (*outlen + inlen) > 0xFF) return(-4); 534 | 535 | for(int i = 0; i < *outlen; ++i) ret[i] = (ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_DATA) >> 8) & 0xFF; 536 | 537 | return(ReadMMIOReg(GPU, GPU->I2CGPIOAddrs.mmGENERIC_I2C_STATUS) & I2C_STATUS_MASK); 538 | } 539 | 540 | static uint8_t CalcCRC8(uint8_t Fcs, uint8_t in) 541 | { 542 | for(int k = 7; k >= 0; k--) 543 | { 544 | int j = Fcs >> 7 ^ in >> k & 1; 545 | Fcs = Fcs << 1 & 0xff; 546 | 547 | Fcs ^= j; j <<= 1; 548 | Fcs ^= j; j <<= 1; 549 | Fcs ^= j; j <<= 1; 550 | } 551 | } 552 | 553 | uint32_t PECVal(const uint8_t *Packet, uint32_t PacketLen) 554 | { 555 | uint8_t PEC = 0x00; 556 | 557 | for(int i = 0; i < PacketLen; ++i) PEC = CalcCRC8(PEC, Packet[i]); 558 | 559 | return(PEC); 560 | } 561 | -------------------------------------------------------------------------------- /amdsmbus.h: -------------------------------------------------------------------------------- 1 | #ifndef __AMDSMBUS_H 2 | #define __AMDSMBUS_H 3 | 4 | #include 5 | #include "amdgpu.h" 6 | 7 | #define SMBUS_BLOCK_TRANSFER_MAX 32 8 | 9 | int AMDSMBusSendByte(AMDGPU *GPU, uint8_t cmd); 10 | uint8_t AMDSMBusReadByte(AMDGPU *GPU, uint8_t cmd, uint32_t *ret); 11 | uint16_t AMDSMBusReadWord(AMDGPU *GPU, uint8_t cmd, uint32_t *ret); 12 | int AMDSMBusWriteWord(AMDGPU *GPU, uint8_t cmd, uint16_t data); 13 | int AMDSMBusReadBlock(AMDGPU *GPU, uint8_t cmd, uint8_t *len, uint8_t *ret); 14 | int AMDSMBusBlockProcessCall(AMDGPU *GPU, uint8_t cmd, uint8_t inlen, uint8_t *indata, uint8_t *outlen, uint8_t *ret); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /atillk64.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #pragma pack(push, 1) 7 | 8 | typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS; 9 | 10 | typedef struct _AMDIOPortReadArgs 11 | { 12 | uint32_t SrcPort; 13 | uint32_t ReadSize; 14 | } AMDIOPortReadArgs; 15 | 16 | typedef struct _AMDMemRWArgs 17 | { 18 | LARGE_INTEGER PhysMemAddr; 19 | uint64_t OperationSize; // Only 1, 2, or 4 allowed 20 | uint64_t DestAddr; 21 | } AMDMemRWArgs; 22 | 23 | typedef struct _AMDIOPortWriteArgs 24 | { 25 | uint32_t DestPort; 26 | uint32_t WriteSize; 27 | uint32_t WriteValue; 28 | } AMDIOPortWriteArgs; 29 | 30 | typedef struct _AMDMemRegionRWArgs 31 | { 32 | LARGE_INTEGER PhysMemAddr; 33 | uint64_t MemRegionSize; 34 | uint64_t DestAddr; 35 | uint64_t CachingType; 36 | } AMDMemRegionRWArgs; 37 | 38 | typedef struct _AMDMSRAccessArgs 39 | { 40 | uint32_t TargetMSR; 41 | uint64_t WriteValue; 42 | } AMDMSRAccessArgs; 43 | 44 | typedef struct _AMDMDLIOCTLRetVal 45 | { 46 | void *MDLHandle; 47 | uint64_t MmMapLockedPagesRetVal; 48 | } AMDMDLIOCTLRetVal; 49 | 50 | typedef struct _AMDMapMDLIOCTLArgs 51 | { 52 | LARGE_INTEGER PhysMemAddr; 53 | uint64_t MemRegionSize; 54 | uint64_t CachingType; 55 | } AMDMapMDLIOCTLArgs; 56 | 57 | typedef struct _AMDFreeMDLIOCTLArgs 58 | { 59 | LARGE_INTEGER PhysMemAddr; 60 | uint64_t MemRegionSize; 61 | uint64_t MDLAddr; 62 | } AMDFreeMDLIOCTLArgs; 63 | 64 | typedef struct _AMDReadMemRegionWithCustomCachingType 65 | { 66 | LARGE_INTEGER PhysMemAddr; 67 | uint64_t MemRegionSize; 68 | uint64_t CachingType; 69 | } AMDReadMemRegionWithCustomCachingType; 70 | 71 | union _AMDRetVal 72 | { 73 | uint8_t ByteReturn; 74 | uint16_t WordReturn; 75 | DWORD DwordReturn; 76 | uint64_t QwordReturn; 77 | } AMDRetVal; 78 | 79 | typedef struct _AMDPCIConfigSpaceReadArgs 80 | { 81 | uint8_t Offset; 82 | uint8_t SlotNum; 83 | uint8_t BusNum; 84 | uint8_t Padding; 85 | uint32_t AccessSize; 86 | } AMDPCIConfigSpaceReadArgs; 87 | 88 | typedef struct _AMDPCIConfigSpaceWriteArgs 89 | { 90 | uint8_t Offset; 91 | uint8_t SlotNum; 92 | uint8_t BusNum; 93 | uint8_t Padding; 94 | uint32_t AccessSize; 95 | uint32_t WriteValue; 96 | } AMDPCIConfigSpaceWriteArgs; 97 | 98 | enum AMD_ATILLK64_IOCTL_CODES 99 | { 100 | ATILLK64_IOCTL_READ_MEM_REGION_WITH_CUSTOM_CACHING_ARGS = 0x9C402568, 101 | ATILLK64_IOCTL_WRITE_MEM_REGION_WITH_CUSTOM_CACHING_ARGS = 0x9C40256C, 102 | ATILLK64_IOCTL_X86_IN_FROM_PORT_INSTRUCTION = 0x9C40252C, 103 | ATILLK64_IOCTL_X86_OUT_TO_PORT_INSTRUCTION = 0x9C402530, 104 | ATILLK64_IOCTL_READ_PHYS_MEM = 0x9C402534, 105 | ATILLK64_IOCTL_WRITE_PHYS_MEM = 0x9C402538, 106 | ATILLK64_IOCTL_READ_PCI_CONFIG_SPACE = 0x9C40253C, 107 | ATILLK64_IOCTL_WRITE_PCI_CONFIG_SPACE = 0x9C402540, 108 | ATILLK64_IOCTL_READ_MEM_REGION = 0x9C402544, 109 | ATILLK64_IOCTL_WRITE_MEM_REGION = 0x9C402548, 110 | ATILLK64_IOCTL_MAP_FROM_NON_PAGED_POOL = 0x9C40254C, 111 | ATILLK64_IOCTL_RDMSR = 0x9C402550, 112 | ATILLK64_IOCTL_WRMSR = 0x9C402554, 113 | ATILLK64_IOCTL_MDL_FROM_NON_PAGED_POOL_MAPPING = 0x9C402558, 114 | ATILLK64_IOCTL_FREE_MDL = 0x9C40255C, 115 | ATILLK64_IOCTL_MAP_FROM_NON_PAGED_POOL_WITH_CUSTOM_CACHING_ARGS = 0x9C402560, 116 | ATILLK64_IOCTL_LOCK_REGION_TO_NON_PAGED_POOL = 0x9C402564 117 | }; 118 | 119 | 120 | #pragma pack(pop) 121 | -------------------------------------------------------------------------------- /ir35217.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "amdgpu.h" 7 | #include "amdi2c.h" 8 | #include "amdsmbus.h" 9 | #include "amdpmbus.h" 10 | #include "ir35217.h" 11 | #include "vrm.h" 12 | 13 | #define IR35217_VID_STEP_MV 5 14 | 15 | /* 16 | // PMBus method 17 | uint32_t IR35217GetVoltage(VRMController *VRM, float *VDDC) 18 | { 19 | int16_t EncodedVolts, VOUTMode; 20 | uint8_t Addr = VRM->I2CAddressList[VRM->SelectedOutput + 1]; 21 | 22 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, Addr); 23 | 24 | // VID Mode 25 | VOUTMode = AMDSMBusReadByte(VRM->ParentGPU, PMBUS_VOUT_MODE, NULL); 26 | //printf("VOUTMode = %d\n", VOUTMode); 27 | // If it's not in linear format, it should be. 28 | if(VOUTMode >> 5) return(VRM_ERROR_UNKNOWN); 29 | 30 | EncodedVolts = AMDSMBusReadWord(VRM->ParentGPU, PMBUS_READ_VOUT, NULL); 31 | *VDDC = PMBusDecodeLinearValueWithExponent(EncodedVolts, VOUTMode); 32 | 33 | //*VDDC = EncodedVolts * 0.0005; 34 | return(VRM_ERROR_SUCCESS); 35 | } 36 | */ 37 | 38 | uint32_t IR35217GetVoltage(VRMController *VRM, float *VDDC) 39 | { 40 | uint8_t VID; 41 | 42 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 43 | 44 | VID = AMDI2CReadByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? IR35217_GET_VOLTAGE_LOOP1_REG : IR35217_GET_VOLTAGE_LOOP2_REG, NULL); 45 | 46 | *VDDC = 1.55 - (VID * 0.00625); 47 | 48 | return(VRM_ERROR_SUCCESS); 49 | } 50 | 51 | /* 52 | uint32_t IR35217SetVoltage(VRMController *VRM, float Voltage) 53 | { 54 | uint8_t VID = (uint8_t)((Voltage / 0.00625) + 0.5); 55 | 56 | AMDGPUI2CInit(VRM->ParentGPU, VRM->I2CAddressList[0], NULL); 57 | AMDI2CWriteByte(VRM->ParentGPU, IR35217_SET_VOLTAGE_LOOP1_REG, VID); 58 | 59 | return(VRM_ERROR_SUCCESS); 60 | }*/ 61 | 62 | 63 | uint32_t IR35217SetVoltage(VRMController *VRM, float Voltage) 64 | { 65 | int16_t EncodedVolts, VOUTMode; 66 | uint8_t Addr = VRM->I2CAddressList[VRM->SelectedOutput + 1]; 67 | 68 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, Addr); 69 | 70 | // VID Mode 71 | VOUTMode = AMDSMBusReadByte(VRM->ParentGPU, PMBUS_VOUT_MODE, NULL); 72 | 73 | // If it's not in linear format, it should be. 74 | if(VOUTMode >> 5) return(VRM_ERROR_UNKNOWN); 75 | uint16_t RawVolts = PMBusEncodeValueToLinearWithExponent(VOUTMode, Voltage); 76 | 77 | //uint16_t RawVolts = (uint16_t)((Voltage * 0.005) + 0.5); 78 | 79 | AMDSMBusWriteWord(VRM->ParentGPU, PMBUS_VOUT_COMMAND, 0x0083); 80 | 81 | 82 | return(VRM_ERROR_SUCCESS); 83 | } 84 | 85 | uint32_t IR35217GetOutputIdx(VRMController *VRM, uint32_t *Idx) 86 | { 87 | *Idx = VRM->SelectedOutput; 88 | return(VRM_ERROR_SUCCESS); 89 | } 90 | 91 | uint32_t IR35217SetOutputIdx(VRMController *VRM, uint32_t Idx) 92 | { 93 | if(Idx >= VRM->OutputCount) return(VRM_ERROR_RANGE); 94 | 95 | VRM->SelectedOutput = Idx; 96 | return(VRM_ERROR_SUCCESS); 97 | } 98 | 99 | uint32_t IR35217GetOffset(VRMController *VRM, float *VoltOffset) 100 | { 101 | int8_t VID; 102 | 103 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 104 | 105 | VID = AMDI2CReadByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? IR35217_SET_VOLTAGE_OFFSET_LOOP1_REG : IR35217_SET_VOLTAGE_OFFSET_LOOP2_REG, NULL); 106 | 107 | *VoltOffset = VID * 0.00625; 108 | return(VRM_ERROR_SUCCESS); 109 | } 110 | 111 | uint32_t IR35217SetOffset(VRMController *VRM, float Voltage) 112 | { 113 | if((Voltage >= VRM->MaxOffset) || (Voltage <= VRM->MinOffset)) return(VRM_ERROR_RANGE); 114 | 115 | uint8_t VOffset = (Voltage > 0) ? (Voltage / 0.00625) + 0.5 : (Voltage / 0.00625) - 0.5; 116 | 117 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 118 | 119 | AMDI2CWriteByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? IR35217_SET_VOLTAGE_OFFSET_LOOP1_REG : IR35217_SET_VOLTAGE_OFFSET_LOOP2_REG, VOffset); 120 | 121 | return(VRM_ERROR_SUCCESS); 122 | } 123 | 124 | // Return value is how many devices were found. 125 | // IR35217 can be found ANYWHERE in the I2C address space 126 | // BUT - the PMBus interface can only be found in certain 127 | // I2C locations - addresses 0x40 - 0x47, 0x70 - 0x77, 128 | // and 0x0D. From the PMBus interface, we can read the 129 | // I2C interface's location. 130 | 131 | uint32_t IR35217Detect(AMDGPU *GPU, VRMController **VRMs) 132 | { 133 | uint8_t Addresses[17] = { 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 134 | 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x0D }; 135 | 136 | uint32_t DevicesFound = 0; 137 | for(int i = 0; i < 17; ++i) 138 | { 139 | uint8_t len, blk[0x100], Model; 140 | AMDGPUI2CInit(GPU, STATIC_I2C_LINE_FIXME, Addresses[i]); 141 | 142 | // First things first - a block read could confuse some 143 | // devices that may be on the bus. Let's verify it's a 144 | // PMBus device before proceeding. 145 | uint32_t myret; 146 | blk[0] = AMDSMBusReadByte(GPU, PMBUS_REVISION, &myret); 147 | 148 | // IR35217 will support at LEAST PMBus 149 | // Part I rev. 1.1 and PMBus Part II rev. 1.2. 150 | if(((blk[0] & 0x0F) < 0x02) && ((blk[0] >> 4) < 0x01)) continue; 151 | 152 | // PMBUS_MFR_ID isn't set on all IR35217-compatibles. 153 | // So, we'll do a (hopefully non-intrusive) poke - 154 | // doing a block read on PMBUS_MFR_MODEL. 155 | 156 | AMDSMBusReadBlock(GPU, PMBUS_MFR_MODEL, &len, blk); 157 | 158 | if(len == 1) 159 | { 160 | int idx = 0; 161 | Model = blk[0]; 162 | 163 | // Compare it to the IR35217 model... 164 | if(Model != MODEL_IR35217) continue; 165 | 166 | // Highest bit is a toggle for enabling/disabling the I2C bus 167 | // so it needs to be masked to get the address. 168 | uint8_t I2CAddr = AMDSMBusReadByte(GPU, IR35217_PMBUS_SET_I2C, NULL) & 0x7F; 169 | VRMController *CurrentVRM; 170 | 171 | // If the address is 0x00, then it's tied to the PMBus address 172 | if(!I2CAddr) 173 | { 174 | switch(Addresses[i]) 175 | { 176 | case 0x40: 177 | case 0x41: 178 | case 0x42: 179 | case 0x43: 180 | case 0x44: 181 | case 0x45: 182 | case 0x46: 183 | case 0x47: 184 | I2CAddr = Addresses[i] - 24; 185 | break; 186 | case 0x70: 187 | case 0x71: 188 | case 0x72: 189 | case 0x73: 190 | case 0x74: 191 | case 0x75: 192 | case 0x76: 193 | case 0x77: 194 | I2CAddr = Addresses[i] - 64; 195 | break; 196 | case 0x0D: 197 | I2CAddr = 0x0A; 198 | break; 199 | } 200 | } 201 | 202 | if(!*VRMs) 203 | { 204 | CurrentVRM = *VRMs = (VRMController *)calloc(1, sizeof(VRMController)); 205 | } 206 | else 207 | { 208 | for(CurrentVRM = *VRMs; CurrentVRM->next; CurrentVRM = CurrentVRM->next); 209 | CurrentVRM = CurrentVRM->next = (VRMController *)calloc(1, sizeof(VRMController)); 210 | } 211 | 212 | CurrentVRM->ParentGPU = GPU; 213 | CurrentVRM->VRMType = VRM_CONTROLLER_TYPE_IR35217; 214 | CurrentVRM->Capabilities = 0; 215 | 216 | CurrentVRM->MinOffset = -0.8; 217 | CurrentVRM->MaxOffset = 0.8; 218 | 219 | CurrentVRM->OutputCount = 2; 220 | CurrentVRM->SelectedOutput = 0; 221 | 222 | CurrentVRM->Model = Model; 223 | 224 | // I2C one first, then PMBus ones 225 | CurrentVRM->I2CAddressList[0] = I2CAddr; 226 | CurrentVRM->I2CAddressList[1] = Addresses[i]; 227 | CurrentVRM->I2CAddressList[2] = Addresses[i] + 1; 228 | 229 | // Skip past our secondary output. 230 | ++i; 231 | 232 | if(I2CDebugOutput) 233 | printf("IR35217 found with I2C at 0x%02X and PMBus at 0x%02X and 0x%02X.\n", I2CAddr, Addresses[i], Addresses[i] + 1); 234 | 235 | CurrentVRM->GetVoltage = IR35217GetVoltage; 236 | CurrentVRM->SetVoltage = IR35217SetVoltage; 237 | CurrentVRM->GetOutputIdx = IR35217GetOutputIdx; 238 | CurrentVRM->SetOutputIdx = IR35217SetOutputIdx; 239 | 240 | CurrentVRM->next = NULL; 241 | DevicesFound++; 242 | } 243 | } 244 | 245 | return(DevicesFound); 246 | } 247 | -------------------------------------------------------------------------------- /ir35217.h: -------------------------------------------------------------------------------- 1 | #ifndef __IR35217_H 2 | #define __IR35217_H 3 | 4 | #include "vrm.h" 5 | 6 | #define MODEL_IR35217 0x5F 7 | 8 | #define IR35217_PMBUS_SET_I2C 0x50 9 | #define IR35217_GET_VOLTAGE_LOOP1_REG 0xB0 10 | #define IR35217_GET_VOLTAGE_LOOP2_REG 0xB1 11 | #define IR35217_SET_VOLTAGE_LOOP1_REG 0xE6 12 | #define IR35217_SET_VOLTAGE_OFFSET_LOOP1_REG 0xE1 13 | #define IR35217_SET_VOLTAGE_OFFSET_LOOP2_REG 0xE2 14 | 15 | uint32_t IR35217Detect(AMDGPU *GPU, VRMController **VRMs); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /ir356xx.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "amdgpu.h" 7 | #include "amdi2c.h" 8 | #include "amdsmbus.h" 9 | #include "amdpmbus.h" 10 | #include "ir356xx.h" 11 | #include "vrm.h" 12 | 13 | int IR356XXToggleVdroop(AMDGPU *GPU, bool Vdroop) 14 | { 15 | return(AMDI2CWriteByte(GPU, IR356XX_LOADLINE_CALIBRATION_REG, ((Vdroop) ? 0x80 : 0x01))); 16 | } 17 | 18 | uint32_t IR356XXGetCurrent(VRMController *VRM, float *Current) 19 | { 20 | uint16_t EncodedAmps; 21 | uint8_t Addr = ((VRM->Model == IR356XX_MODEL_IR35217) ? VRM->I2CAddressList[VRM->SelectedOutput + 1] : VRM->I2CAddressList[1]); 22 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, Addr); 23 | 24 | EncodedAmps = AMDSMBusReadWord(VRM->ParentGPU, PMBUS_READ_IOUT, NULL); 25 | 26 | *Current = PMBusDecodeLinearValue(EncodedAmps); 27 | return(VRM_ERROR_SUCCESS); 28 | } 29 | 30 | uint32_t IR356XXGetVoltage(VRMController *VRM, float *VDDC) 31 | { 32 | uint8_t VID; 33 | 34 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 35 | 36 | VID = AMDI2CReadByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? IR356XX_GET_VOLTAGE_LOOP1_REG : IR356XX_GET_VOLTAGE_LOOP2_REG, NULL); 37 | 38 | *VDDC = VID * 0.0078125; 39 | 40 | return(VRM_ERROR_SUCCESS); 41 | } 42 | 43 | uint32_t IR356XXSetVoltage(VRMController *VRM, float Voltage) 44 | { 45 | uint8_t VID; 46 | 47 | // Ensure voltage is in range 48 | if(Voltage < 0.25 || Voltage > 1.52) return(VRM_ERROR_RANGE); 49 | 50 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 51 | 52 | Voltage = (1.55 - Voltage) / 0.00625; 53 | 54 | // Round to nearest VID and save 55 | VID = (uint8_t)(Voltage + 0.5); 56 | 57 | AMDI2CWriteByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? IR356XX_SET_VOLTAGE_LOOP1_REG : IR356XX_SET_VOLTAGE_LOOP2_REG, VID); 58 | 59 | return(VRM_ERROR_SUCCESS); 60 | } 61 | 62 | uint32_t IR356XXGetOutputIdx(VRMController *VRM, uint32_t *Idx) 63 | { 64 | *Idx = VRM->SelectedOutput; 65 | return(VRM_ERROR_SUCCESS); 66 | } 67 | 68 | uint32_t IR356XXSetOutputIdx(VRMController *VRM, uint32_t Idx) 69 | { 70 | if(Idx >= VRM->OutputCount) return(VRM_ERROR_RANGE); 71 | 72 | VRM->SelectedOutput = Idx; 73 | return(VRM_ERROR_SUCCESS); 74 | } 75 | 76 | uint32_t IR356XXGetOffset(VRMController *VRM, float *VoltOffset) 77 | { 78 | int8_t VID; 79 | 80 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 81 | 82 | VID = AMDI2CReadByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? IR356XX_VOLTAGE_OFFSET_LOOP1_REG : IR356XX_VOLTAGE_OFFSET_LOOP2_REG, NULL); 83 | 84 | *VoltOffset = VID * 0.00625; 85 | return(VRM_ERROR_SUCCESS); 86 | } 87 | 88 | uint32_t IR356XXSetOffset(VRMController *VRM, float Voltage) 89 | { 90 | if((Voltage >= VRM->MaxOffset) || (Voltage <= VRM->MinOffset)) return(VRM_ERROR_RANGE); 91 | 92 | uint8_t VOffset = (Voltage > 0) ? (Voltage / 0.00625) + 0.5 : (Voltage / 0.00625) - 0.5; 93 | 94 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 95 | 96 | AMDI2CWriteByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? IR356XX_VOLTAGE_OFFSET_LOOP1_REG : IR356XX_VOLTAGE_OFFSET_LOOP2_REG, VOffset); 97 | 98 | return(VRM_ERROR_SUCCESS); 99 | } 100 | 101 | uint32_t IR356XXGetTemp(VRMController *VRM, uint32_t *Temp) 102 | { 103 | uint16_t EncodedDegrees; 104 | uint8_t Addr = ((VRM->Model == IR356XX_MODEL_IR35217) ? VRM->I2CAddressList[VRM->SelectedOutput + 1] : VRM->I2CAddressList[1]); 105 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, Addr); 106 | 107 | EncodedDegrees = AMDSMBusReadWord(VRM->ParentGPU, PMBUS_READ_TEMPERATURE_1, NULL); 108 | 109 | *Temp = (uint32_t)truncf(PMBusDecodeLinearValue(EncodedDegrees)); 110 | 111 | return(VRM_ERROR_SUCCESS); 112 | } 113 | 114 | // Return value is how many devices were found. 115 | // IR356XX can be found ANYWHERE in the I2C address space 116 | // BUT - the PMBus interface can only be found in certain 117 | // I2C locations - addresses 0x40 - 0x47, 0x70 - 0x77, 118 | // and 0x0D. From the PMBus interface, we can read the 119 | // I2C interface's location. 120 | 121 | uint32_t IR356XXDetect(AMDGPU *GPU, VRMController **VRMs) 122 | { 123 | uint8_t Addresses[17] = { 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 124 | 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x0D }; 125 | 126 | uint32_t DevicesFound = 0; 127 | for(int i = 0; i < 17; ++i) 128 | { 129 | uint8_t len, blk[0x100], Model; 130 | AMDGPUI2CInit(GPU, STATIC_I2C_LINE_FIXME, Addresses[i]); 131 | 132 | // First things first - a block read could confuse some 133 | // devices that may be on the bus. Let's verify it's a 134 | // PMBus device before proceeding. 135 | uint32_t myret; 136 | blk[0] = AMDSMBusReadByte(GPU, PMBUS_REVISION, &myret); 137 | 138 | // IR356XX-compatibles will support at LEAST PMBus 139 | // Part I rev. 1.1 and PMBus Part II rev. 1.2. 140 | if(((blk[0] & 0x0F) < 0x02) && ((blk[0] >> 4) < 0x01)) continue; 141 | 142 | // PMBUS_MFR_ID isn't set on all IR356XX-compatibles. 143 | // So, we'll do a (hopefully non-intrusive) poke - 144 | // doing a block read on PMBUS_MFR_MODEL. 145 | 146 | AMDSMBusReadBlock(GPU, PMBUS_MFR_MODEL, &len, blk); 147 | 148 | if(len == 1) 149 | { 150 | int idx = 0; 151 | Model = blk[0]; 152 | 153 | // Compare it to all models we know and support... 154 | do 155 | { 156 | if(Model == IR356XX_SUPPORTED_MODELS[idx]) break; 157 | } while(++idx < IR356XX_SUPPORTED_MODEL_COUNT); 158 | 159 | // TODO/FIXME: Add more checks! Model number is NOT enough! 160 | // Highest bit is a toggle for enabling/disabling the I2C bus 161 | // so it needs to be masked to get the address. 162 | if(idx < IR356XX_SUPPORTED_MODEL_COUNT) 163 | { 164 | uint8_t I2CAddr = AMDSMBusReadByte(GPU, IR356XX_PMBUS_SET_I2C, NULL) & 0x7F; 165 | VRMController *CurrentVRM; 166 | 167 | // If the address is 0x00, then it's tied to the PMBus address 168 | if(!I2CAddr) 169 | { 170 | switch(Addresses[i]) 171 | { 172 | case 0x40: 173 | case 0x41: 174 | case 0x42: 175 | case 0x43: 176 | case 0x44: 177 | case 0x45: 178 | case 0x46: 179 | case 0x47: 180 | I2CAddr = Addresses[i] - 24; 181 | break; 182 | case 0x70: 183 | case 0x71: 184 | case 0x72: 185 | case 0x73: 186 | case 0x74: 187 | case 0x75: 188 | case 0x76: 189 | case 0x77: 190 | I2CAddr = Addresses[i] - 64; 191 | break; 192 | case 0x0D: 193 | I2CAddr = 0x0A; 194 | break; 195 | } 196 | } 197 | 198 | if(!*VRMs) 199 | { 200 | CurrentVRM = *VRMs = (VRMController *)calloc(1, sizeof(VRMController)); 201 | } 202 | else 203 | { 204 | for(CurrentVRM = *VRMs; CurrentVRM->next; CurrentVRM = CurrentVRM->next); 205 | CurrentVRM = CurrentVRM->next = (VRMController *)calloc(1, sizeof(VRMController)); 206 | } 207 | 208 | CurrentVRM->ParentGPU = GPU; 209 | CurrentVRM->VRMType = VRM_CONTROLLER_TYPE_IR356XX; 210 | CurrentVRM->Capabilities = VRM_CAPABILITY_OFFSET | VRM_CAPABILITY_TEMP | VRM_CAPABILITY_CURRENT; 211 | 212 | CurrentVRM->MinOffset = -0.8; 213 | CurrentVRM->MaxOffset = 0.8; 214 | 215 | CurrentVRM->OutputCount = 2U; 216 | CurrentVRM->SelectedOutput = 0; 217 | 218 | CurrentVRM->Model = Model; 219 | 220 | // I2C one first, then PMBus one 221 | CurrentVRM->I2CAddressList[0] = I2CAddr; 222 | CurrentVRM->I2CAddressList[1] = Addresses[i]; 223 | 224 | if(I2CDebugOutput) 225 | printf("IR356XX found with I2C at 0x%02X and PMBus at 0x%02X.\n", I2CAddr, Addresses[i]); 226 | 227 | CurrentVRM->GetVoltage = IR356XXGetVoltage; 228 | CurrentVRM->SetVoltage = IR356XXSetVoltage; 229 | CurrentVRM->GetVoltageOffset = IR356XXGetOffset; 230 | CurrentVRM->SetVoltageOffset = IR356XXSetOffset; 231 | CurrentVRM->GetOutputIdx = IR356XXGetOutputIdx; 232 | CurrentVRM->SetOutputIdx = IR356XXSetOutputIdx; 233 | CurrentVRM->GetTemp = IR356XXGetTemp; 234 | CurrentVRM->GetCurrent = IR356XXGetCurrent; 235 | 236 | CurrentVRM->next = NULL; 237 | DevicesFound++; 238 | } 239 | } 240 | } 241 | 242 | return(DevicesFound); 243 | } 244 | -------------------------------------------------------------------------------- /ir356xx.h: -------------------------------------------------------------------------------- 1 | #ifndef __IR356XX_H 2 | #define __IR356XX_H 3 | 4 | #include 5 | #include 6 | 7 | #include "vrm.h" 8 | 9 | #define IR356XX_I2C_BASE_ADDRESS_REG 0x12 10 | #define IR356XX_LOADLINE_CALIBRATION_REG 0x38 11 | #define IR356XX_SET_VOLTAGE_LOOP1_REG 0x6A 12 | #define IR356XX_SET_VOLTAGE_LOOP2_REG 0x6C 13 | #define IR356XX_VOLTAGE_OFFSET_LOOP1_REG 0x8D 14 | #define IR356XX_VOLTAGE_OFFSET_LOOP2_REG 0x8E 15 | #define IR356XX_IDENTIFICATION_REG 0x92 16 | #define IR356XX_GET_VOLTAGE_LOOP1_REG 0x9A 17 | #define IR356XX_GET_VOLTAGE_LOOP2_REG 0x9B 18 | #define IR356XX_GET_CURRENT_LOOP1_REG 0x9C 19 | #define IR356XX_GET_CURRENT_LOOP2_REG 0x9D 20 | #define IR356XX_GET_TEMP_LOOP1_REG 0x9E 21 | #define IR356XX_GET_TEMP_LOOP2_REG 0x9F 22 | 23 | #define IR356XX_PMBUS_TEMP_CURLOOP 0x8D 24 | #define IR356XX_PMBUS_TEMP_AUXLOOP 0x8E 25 | #define IR356XX_PMBUS_SET_I2C 0xD6 26 | #define IR356XX_PMBUS_READ_EFFICIENCY 0xD7 27 | 28 | 29 | #define IR356XX_IDENTIFICATION_VALUE 0x43 30 | 31 | #define IR356XX_MODEL_IR3563B 0x40 32 | #define IR356XX_MODEL_IR3564B 0x41 33 | #define IR356XX_MODEL_IR3565B 0x42 34 | #define IR356XX_MODEL_IR3566B 0x43 35 | #define IR356XX_MODEL_IR3567B 0x44 36 | #define IR356XX_MODEL_IR3570B 0x45 37 | #define IR356XX_MODEL_IR35201 0x4D 38 | #define IR356XX_MODEL_IR35203 0x4F 39 | #define IR356XX_MODEL_IR35204 0x50 40 | #define IR356XX_MODEL_IR35211 0x54 41 | #define IR356XX_MODEL_IR35217 0x5F 42 | #define IR356XX_MODEL_IR38163 0x63 43 | #define IR356XX_MODEL_IR38164 0x6D 44 | #define IR356XX_MODEL_IR38165 0x64 45 | #define IR356XX_MODEL_IR38263 0x65 46 | #define IR356XX_MODEL_IR38363 0x67 47 | #define IR356XX_MODEL_IR38365 0x68 48 | 49 | #define IR356XX_SUPPORTED_MODEL_COUNT 0x03 50 | 51 | const static uint8_t IR356XX_SUPPORTED_MODELS[IR356XX_SUPPORTED_MODEL_COUNT] = 52 | { 53 | IR356XX_MODEL_IR3565B, IR356XX_MODEL_IR3567B 54 | }; 55 | 56 | bool IR356XXDetectOld(AMDGPU *GPU, int *ret); 57 | uint32_t IR356XXDetect(AMDGPU *GPU, VRMController **VRMs); 58 | uint32_t IR356XXGetTemp(VRMController *VRM, uint32_t *Temp); 59 | uint32_t IR356XXSetVoltage(VRMController *VRM, float Voltage); 60 | uint32_t IR356XXGetVoltage(VRMController *VRM, float *VDDC); 61 | uint32_t IR356XXSetOutputIdx(VRMController *VRM, uint32_t Idx); 62 | uint32_t IR356XXGetOutputIdx(VRMController *VRM, uint32_t *Idx); 63 | uint32_t IR356XXSetOffset(VRMController *VRM, float Voltage); 64 | uint32_t IR356XXGetOffset(VRMController *VRM, float *VoltOffset); 65 | int IR356XXToggleVdroop(AMDGPU *GPU, bool Vdroop); 66 | #endif 67 | -------------------------------------------------------------------------------- /ir3xxxx.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "amdgpu.h" 7 | #include "amdi2c.h" 8 | #include "amdsmbus.h" 9 | #include "amdpmbus.h" 10 | #include "ir356xx.h" 11 | #include "vrm.h" 12 | 13 | int IR356XXToggleVdroop(AMDGPU *GPU, bool Vdroop) 14 | { 15 | return(AMDI2CWriteByte(GPU, IR356XX_LOADLINE_CALIBRATION_REG, ((Vdroop) ? 0x80 : 0x01))); 16 | } 17 | 18 | uint32_t IR356XXGetCurrent(VRMController *VRM, float *Current) 19 | { 20 | uint16_t EncodedAmps; 21 | uint8_t Addr = ((VRM->Model == IR356XX_MODEL_IR35217) ? VRM->I2CAddressList[VRM->SelectedOutput + 1] : VRM->I2CAddressList[1]); 22 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, Addr); 23 | 24 | EncodedAmps = AMDSMBusReadWord(VRM->ParentGPU, PMBUS_READ_IOUT, NULL); 25 | 26 | *Current = PMBusDecodeLinearValue(EncodedAmps); 27 | return(VRM_ERROR_SUCCESS); 28 | } 29 | 30 | uint32_t IR356XXGetVoltage(VRMController *VRM, float *VDDC) 31 | { 32 | uint8_t VID; 33 | 34 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 35 | 36 | VID = AMDI2CReadByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? IR356XX_GET_VOLTAGE_LOOP1_REG : IR356XX_GET_VOLTAGE_LOOP2_REG, NULL); 37 | 38 | *VDDC = VID * 0.0078125; 39 | 40 | return(VRM_ERROR_SUCCESS); 41 | } 42 | 43 | uint32_t IR356XXSetVoltage(VRMController *VRM, float Voltage) 44 | { 45 | uint8_t VID; 46 | 47 | // Ensure voltage is in range 48 | if(Voltage < 0.25 || Voltage > 1.52) return(VRM_ERROR_RANGE); 49 | 50 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 51 | 52 | Voltage = (1.55 - Voltage) / 0.00625; 53 | 54 | // Round to nearest VID and save 55 | VID = (uint8_t)(Voltage + 0.5); 56 | 57 | AMDI2CWriteByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? IR356XX_SET_VOLTAGE_LOOP1_REG : IR356XX_SET_VOLTAGE_LOOP2_REG, VID); 58 | 59 | return(VRM_ERROR_SUCCESS); 60 | } 61 | 62 | uint32_t IR356XXGetOutputIdx(VRMController *VRM, uint32_t *Idx) 63 | { 64 | *Idx = VRM->SelectedOutput; 65 | return(VRM_ERROR_SUCCESS); 66 | } 67 | 68 | uint32_t IR356XXSetOutputIdx(VRMController *VRM, uint32_t Idx) 69 | { 70 | if(Idx >= VRM->OutputCount) return(VRM_ERROR_RANGE); 71 | 72 | VRM->SelectedOutput = Idx; 73 | return(VRM_ERROR_SUCCESS); 74 | } 75 | 76 | uint32_t IR356XXGetOffset(VRMController *VRM, float *VoltOffset) 77 | { 78 | int8_t VID; 79 | 80 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 81 | 82 | VID = AMDI2CReadByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? IR356XX_VOLTAGE_OFFSET_LOOP1_REG : IR356XX_VOLTAGE_OFFSET_LOOP2_REG, NULL); 83 | 84 | *VoltOffset = VID * 0.00625; 85 | return(VRM_ERROR_SUCCESS); 86 | } 87 | 88 | uint32_t IR356XXSetOffset(VRMController *VRM, float Voltage) 89 | { 90 | if((Voltage >= VRM->MaxOffset) || (Voltage <= VRM->MinOffset)) return(VRM_ERROR_RANGE); 91 | 92 | uint8_t VOffset = (Voltage > 0) ? (Voltage / 0.00625) + 0.5 : (Voltage / 0.00625) - 0.5; 93 | 94 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 95 | 96 | AMDI2CWriteByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? IR356XX_VOLTAGE_OFFSET_LOOP1_REG : IR356XX_VOLTAGE_OFFSET_LOOP2_REG, VOffset); 97 | 98 | return(VRM_ERROR_SUCCESS); 99 | } 100 | 101 | uint32_t IR356XXGetTemp(VRMController *VRM, uint32_t *Temp) 102 | { 103 | uint16_t EncodedDegrees; 104 | uint8_t Addr = ((VRM->Model == IR356XX_MODEL_IR35217) ? VRM->I2CAddressList[VRM->SelectedOutput + 1] : VRM->I2CAddressList[1]); 105 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, Addr); 106 | 107 | EncodedDegrees = AMDSMBusReadWord(VRM->ParentGPU, PMBUS_READ_TEMPERATURE_1, NULL); 108 | 109 | *Temp = (uint32_t)truncf(PMBusDecodeLinearValue(EncodedDegrees)); 110 | 111 | return(VRM_ERROR_SUCCESS); 112 | } 113 | 114 | // Return value is how many devices were found. 115 | // IR356XX can be found ANYWHERE in the I2C address space 116 | // BUT - the PMBus interface can only be found in certain 117 | // I2C locations - addresses 0x40 - 0x47, 0x70 - 0x77, 118 | // and 0x0D. From the PMBus interface, we can read the 119 | // I2C interface's location. 120 | 121 | uint32_t IR356XXDetect(AMDGPU *GPU, VRMController **VRMs) 122 | { 123 | uint8_t Addresses[17] = { 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 124 | 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x0D }; 125 | 126 | uint32_t DevicesFound = 0; 127 | for(int i = 0; i < 17; ++i) 128 | { 129 | uint8_t len, blk[0x100], Model; 130 | AMDGPUI2CInit(GPU, STATIC_I2C_LINE_FIXME, Addresses[i]); 131 | 132 | // First things first - a block read could confuse some 133 | // devices that may be on the bus. Let's verify it's a 134 | // PMBus device before proceeding. 135 | uint32_t myret; 136 | blk[0] = AMDSMBusReadByte(GPU, PMBUS_REVISION, &myret); 137 | 138 | // IR356XX-compatibles will support at LEAST PMBus 139 | // Part I rev. 1.1 and PMBus Part II rev. 1.2. 140 | if(((blk[0] & 0x0F) < 0x02) && ((blk[0] >> 4) < 0x01)) continue; 141 | 142 | // PMBUS_MFR_ID isn't set on all IR356XX-compatibles. 143 | // So, we'll do a (hopefully non-intrusive) poke - 144 | // doing a block read on PMBUS_MFR_MODEL. 145 | 146 | AMDSMBusReadBlock(GPU, PMBUS_MFR_MODEL, &len, blk); 147 | 148 | if(len == 1) 149 | { 150 | int idx = 0; 151 | Model = blk[0]; 152 | 153 | // Compare it to all models we know and support... 154 | do 155 | { 156 | if(Model == IR356XX_SUPPORTED_MODELS[idx]) break; 157 | } while(++idx < IR356XX_SUPPORTED_MODEL_COUNT); 158 | 159 | // TODO/FIXME: Add more checks! Model number is NOT enough! 160 | // Highest bit is a toggle for enabling/disabling the I2C bus 161 | // so it needs to be masked to get the address. 162 | if(idx < IR356XX_SUPPORTED_MODEL_COUNT) 163 | { 164 | uint8_t I2CAddr = AMDSMBusReadByte(GPU, IR356XX_PMBUS_SET_I2C, NULL) & 0x7F; 165 | VRMController *CurrentVRM; 166 | 167 | // If the address is 0x00, then it's tied to the PMBus address 168 | if(!I2CAddr) 169 | { 170 | switch(Addresses[i]) 171 | { 172 | case 0x40: 173 | case 0x41: 174 | case 0x42: 175 | case 0x43: 176 | case 0x44: 177 | case 0x45: 178 | case 0x46: 179 | case 0x47: 180 | I2CAddr = Addresses[i] - 24; 181 | break; 182 | case 0x70: 183 | case 0x71: 184 | case 0x72: 185 | case 0x73: 186 | case 0x74: 187 | case 0x75: 188 | case 0x76: 189 | case 0x77: 190 | I2CAddr = Addresses[i] - 64; 191 | break; 192 | case 0x0D: 193 | I2CAddr = 0x0A; 194 | break; 195 | } 196 | } 197 | 198 | if(!*VRMs) 199 | { 200 | CurrentVRM = *VRMs = (VRMController *)calloc(1, sizeof(VRMController)); 201 | } 202 | else 203 | { 204 | for(CurrentVRM = *VRMs; CurrentVRM->next; CurrentVRM = CurrentVRM->next); 205 | CurrentVRM = CurrentVRM->next = (VRMController *)calloc(1, sizeof(VRMController)); 206 | } 207 | 208 | CurrentVRM->ParentGPU = GPU; 209 | CurrentVRM->VRMType = VRM_CONTROLLER_TYPE_IR356XX; 210 | CurrentVRM->Capabilities = VRM_CAPABILITY_OFFSET | VRM_CAPABILITY_TEMP | VRM_CAPABILITY_CURRENT; 211 | 212 | CurrentVRM->MinOffset = -0.8; 213 | CurrentVRM->MaxOffset = 0.8; 214 | 215 | CurrentVRM->OutputCount = 2U; 216 | CurrentVRM->SelectedOutput = 0; 217 | 218 | CurrentVRM->Model = Model; 219 | 220 | // I2C one first, then PMBus one 221 | CurrentVRM->I2CAddressList[0] = I2CAddr; 222 | CurrentVRM->I2CAddressList[1] = Addresses[i]; 223 | 224 | if(I2CDebugOutput) 225 | printf("IR356XX found with I2C at 0x%02X and PMBus at 0x%02X.\n", I2CAddr, Addresses[i]); 226 | 227 | CurrentVRM->GetVoltage = IR356XXGetVoltage; 228 | CurrentVRM->SetVoltage = IR356XXSetVoltage; 229 | CurrentVRM->GetVoltageOffset = IR356XXGetOffset; 230 | CurrentVRM->SetVoltageOffset = IR356XXSetOffset; 231 | CurrentVRM->GetOutputIdx = IR356XXGetOutputIdx; 232 | CurrentVRM->SetOutputIdx = IR356XXSetOutputIdx; 233 | CurrentVRM->GetTemp = IR356XXGetTemp; 234 | CurrentVRM->GetCurrent = IR356XXGetCurrent; 235 | 236 | CurrentVRM->next = NULL; 237 | DevicesFound++; 238 | } 239 | } 240 | } 241 | 242 | return(DevicesFound); 243 | } 244 | -------------------------------------------------------------------------------- /ir3xxxx.h: -------------------------------------------------------------------------------- 1 | #ifndef __IR356XX_H 2 | #define __IR356XX_H 3 | 4 | #include 5 | #include 6 | 7 | #include "vrm.h" 8 | 9 | #define IR356XX_I2C_BASE_ADDRESS_REG 0x12 10 | #define IR356XX_LOADLINE_CALIBRATION_REG 0x38 11 | #define IR356XX_SET_VOLTAGE_LOOP1_REG 0x6A 12 | #define IR356XX_SET_VOLTAGE_LOOP2_REG 0x6C 13 | #define IR356XX_VOLTAGE_OFFSET_LOOP1_REG 0x8D 14 | #define IR356XX_VOLTAGE_OFFSET_LOOP2_REG 0x8E 15 | #define IR356XX_IDENTIFICATION_REG 0x92 16 | #define IR356XX_GET_VOLTAGE_LOOP1_REG 0x9A 17 | #define IR356XX_GET_VOLTAGE_LOOP2_REG 0x9B 18 | #define IR356XX_GET_CURRENT_LOOP1_REG 0x9C 19 | #define IR356XX_GET_CURRENT_LOOP2_REG 0x9D 20 | #define IR356XX_GET_TEMP_LOOP1_REG 0x9E 21 | #define IR356XX_GET_TEMP_LOOP2_REG 0x9F 22 | 23 | #define IR356XX_PMBUS_TEMP_CURLOOP 0x8D 24 | #define IR356XX_PMBUS_TEMP_AUXLOOP 0x8E 25 | #define IR356XX_PMBUS_SET_I2C 0xD6 26 | #define IR356XX_PMBUS_READ_EFFICIENCY 0xD7 27 | 28 | 29 | #define IR356XX_IDENTIFICATION_VALUE 0x43 30 | 31 | #define IR356XX_MODEL_IR3563B 0x40 32 | #define IR356XX_MODEL_IR3564B 0x41 33 | #define IR356XX_MODEL_IR3565B 0x42 34 | #define IR356XX_MODEL_IR3566B 0x43 35 | #define IR356XX_MODEL_IR3567B 0x44 36 | #define IR356XX_MODEL_IR3570B 0x45 37 | #define IR356XX_MODEL_IR35201 0x4D 38 | #define IR356XX_MODEL_IR35203 0x4F 39 | #define IR356XX_MODEL_IR35204 0x50 40 | #define IR356XX_MODEL_IR35211 0x54 41 | #define IR356XX_MODEL_IR35217 0x5F 42 | #define IR356XX_MODEL_IR38163 0x63 43 | #define IR356XX_MODEL_IR38164 0x6D 44 | #define IR356XX_MODEL_IR38165 0x64 45 | #define IR356XX_MODEL_IR38263 0x65 46 | #define IR356XX_MODEL_IR38363 0x67 47 | #define IR356XX_MODEL_IR38365 0x68 48 | 49 | #define IR356XX_SUPPORTED_MODEL_COUNT 0x03 50 | 51 | const static uint8_t IR356XX_SUPPORTED_MODELS[IR356XX_SUPPORTED_MODEL_COUNT] = 52 | { 53 | IR356XX_MODEL_IR3565B, IR356XX_MODEL_IR3567B 54 | }; 55 | 56 | bool IR356XXDetectOld(AMDGPU *GPU, int *ret); 57 | uint32_t IR356XXDetect(AMDGPU *GPU, VRMController **VRMs); 58 | uint32_t IR356XXGetTemp(VRMController *VRM, uint32_t *Temp); 59 | uint32_t IR356XXSetVoltage(VRMController *VRM, float Voltage); 60 | uint32_t IR356XXGetVoltage(VRMController *VRM, float *VDDC); 61 | uint32_t IR356XXSetOutputIdx(VRMController *VRM, uint32_t Idx); 62 | uint32_t IR356XXGetOutputIdx(VRMController *VRM, uint32_t *Idx); 63 | uint32_t IR356XXSetOffset(VRMController *VRM, float Voltage); 64 | uint32_t IR356XXGetOffset(VRMController *VRM, float *VoltOffset); 65 | int IR356XXToggleVdroop(AMDGPU *GPU, bool Vdroop); 66 | #endif 67 | -------------------------------------------------------------------------------- /libpci.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OhGodAPet/wolfamdvolt/517e014da354cfed315e36d553c7f3e1eb94c1ae/libpci.a -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "wolfamdvolt.h" 6 | #include "amdgpu.h" 7 | #include "amdi2c.h" 8 | #include "amdsmbus.h" 9 | #include "amdpmbus.h" 10 | #include "ir3xxxx.h" 11 | #include "ncp81022.h" 12 | #include "up9505.h" 13 | #include "up1801.h" 14 | #include "rt8894a.h" 15 | 16 | #ifdef _WIN32 17 | #include "atillk64.h" 18 | #include "driverbin.h" 19 | #include 20 | #include 21 | #endif 22 | 23 | /* 24 | void DumpGPUInfo(AMDGPU *GPU) 25 | { 26 | int ret; 27 | int8_t off; 28 | uint8_t byte; 29 | 30 | if(IR3XXXXDetect(GPU, NULL)) 31 | { 32 | uint8_t incmdcode, outlen, out[0x100]; 33 | 34 | printf("\tDetected IR3XXXX!\n"); 35 | 36 | printf("\tLoop 1 voltage: %.4fV\n", IR3XXXXGetVoltage(GPU, NULL)); 37 | printf("\tLoop 1 current: %dA\n", IR3XXXXGetCurrent(GPU, NULL)); 38 | printf("\tLoop 1 temp: %dC\n", IR3XXXXGetTemp(GPU, NULL)); 39 | printf("\tLoop 2 voltage: %.4fV\n", AMDI2CReadByte(GPU, IR3XXXX_GET_VOLTAGE_LOOP2_REG, NULL) * 0.0078125); 40 | 41 | printf("\tLoop 1 voltage offset: %.4fV\n\n", IR3XXXXGetOffset(GPU, NULL)); 42 | } 43 | 44 | if(NCP81022Detect(GPU, NULL)) 45 | { 46 | printf("\tDetected NCP81022!\n"); 47 | 48 | //NCP81022SwitchControls(GPU, true); 49 | NCP81022SelectRail(GPU, 0); 50 | 51 | printf("\tLoop 1 voltage: %.4fV\n", NCP81022GetVoltage(GPU, NULL)); 52 | NCP81022SelectRail(GPU, 1); 53 | 54 | printf("\tLoop 2 voltage: %.4fV\n", NCP81022GetVoltage(GPU, NULL)); 55 | NCP81022SelectRail(GPU, 0); 56 | 57 | printf("\tIOUT_CAL_GAIN: 0x%04X\n", AMDSMBusReadWord(GPU, NCP81022_PMBUS_IOUT_CAL_GAIN, NULL)); 58 | printf("\tIOUT_OFFSET: 0x%04X\n", AMDSMBusReadWord(GPU, NCP81022_PMBUS_IOUT_OFFSET, NULL)); 59 | printf("\tInput voltage: %.4fV\n", NCP81022GetInputVoltage(GPU, NULL)); 60 | 61 | printf("\tLoop 1 Output Power: %.4fW\n\n", NCP81022GetOutputPower(GPU, NULL)); 62 | printf("\tLoop 1 Output Current: %.4fA\n", NCP81022GetOutputCurrent(GPU, NULL)); 63 | printf("\tLoop 1 Input Voltage: %.4fV\n", NCP81022GetInputVoltage(GPU, NULL)); 64 | //NCP81022SwitchControls(GPU, false); 65 | } 66 | 67 | if(uP9505Detect(GPU, NULL)) 68 | { 69 | printf("\tDetected uP9505!\n"); 70 | printf("\tLoop 1 voltage: %.4f\n", uP9505GetVoltage(GPU, NULL)); 71 | printf("\tLoop 2 voltage: %.4f\n", uP9505GetVDDAVoltage(GPU, NULL)); 72 | printf("\tLoop 1 temp: %dC\n\n", uP9505GetTemp(GPU, NULL)); 73 | } 74 | 75 | if(RT8894ADetect(GPU, NULL)) 76 | { 77 | printf("\tDetected RT8894A!\n"); 78 | printf("\tLoop 1 voltage: %.4f\n", RT8894AGetVoltage(GPU, 0, NULL)); 79 | printf("\tLoop 2 voltage: %.4f\n", RT8894AGetVoltage(GPU, 1, NULL)); 80 | } 81 | 82 | if(uP1801Detect(GPU, NULL)) 83 | { 84 | printf("\tDetected uP1801!\n"); 85 | printf("\tVoltage: %.4f\n\n", uP1801GetVoltage(GPU, NULL)); 86 | } 87 | 88 | } 89 | */ 90 | 91 | void DumpGPUInfo(AMDGPU *GPU, ArgsObj Config) 92 | { 93 | VRMController *CurrentVRM; 94 | uint32_t VRMIdx = 0; 95 | 96 | printf("\tNumber of VRMs: %d\n", GPU->VRMCount); 97 | 98 | if(!GPU->VRMCount) return; 99 | 100 | for(CurrentVRM = GPU->VRMs; CurrentVRM; CurrentVRM = CurrentVRM->next, VRMIdx++) 101 | { 102 | if(!Config.Debug) printf("\tVRM %d: %s\n", VRMIdx, GetVRMName(CurrentVRM)); 103 | else printf("\tVRM %d: %s (at 0x%02X)\n", VRMIdx, GetVRMName(CurrentVRM), CurrentVRM->I2CAddressList[0]); 104 | 105 | printf("\t\tNumber of outputs: %d\n", CurrentVRM->OutputCount); 106 | 107 | for(int OutputIdx = 0; OutputIdx < CurrentVRM->OutputCount; ++OutputIdx) 108 | { 109 | float CurVoltage; 110 | 111 | printf("\t\tOutput %d:\n", OutputIdx); 112 | 113 | CurrentVRM->SetOutputIdx(CurrentVRM, OutputIdx); 114 | 115 | if(CurrentVRM->GetVoltage(CurrentVRM, &CurVoltage) != VRM_ERROR_SUCCESS) 116 | { 117 | printf("\t\t\tFailure getting voltage from VRM.\n"); 118 | continue; 119 | } 120 | 121 | printf("\t\t\tVoltage: %.4f\n", CurVoltage); 122 | 123 | if(CurrentVRM->Capabilities & VRM_CAPABILITY_OFFSET) 124 | { 125 | float CurOffset; 126 | 127 | if(CurrentVRM->GetVoltageOffset(CurrentVRM, &CurOffset) != VRM_ERROR_SUCCESS) 128 | { 129 | printf("\t\t\tFailure getting offset from VRM.\n"); 130 | continue; 131 | } 132 | 133 | printf("\t\t\tOffset: %.4f\n", CurOffset); 134 | } 135 | 136 | if(CurrentVRM->Capabilities & VRM_CAPABILITY_TEMP) 137 | { 138 | uint32_t Temp; 139 | 140 | if(CurrentVRM->GetTemp(CurrentVRM, &Temp) != VRM_ERROR_SUCCESS) 141 | { 142 | printf("\t\t\tFailure getting temperature from VRM.\n"); 143 | continue; 144 | } 145 | 146 | printf("\t\t\tTemp: %dC\n", Temp); 147 | } 148 | 149 | if(CurrentVRM->Capabilities & VRM_CAPABILITY_CURRENT) 150 | { 151 | float Amps; 152 | if(CurrentVRM->GetCurrent(CurrentVRM, &Amps) != VRM_ERROR_SUCCESS) 153 | { 154 | printf("\t\t\tFailure getting current from VRM.\n"); 155 | continue; 156 | } 157 | 158 | printf("\t\t\tAmps: %.2fA\n\t\t\tWatts: %.4fW\n", Amps, ((float)Amps) * CurVoltage); 159 | } 160 | 161 | if(CurrentVRM->Capabilities & VRM_CAPABILITY_LOADLINE) 162 | { 163 | if(CurrentVRM->VRMType == VRM_CONTROLLER_TYPE_NCP81022 && VRMIdx == 3) 164 | CurrentVRM->SetLoadLine(CurrentVRM, 0x00); 165 | } 166 | } 167 | } 168 | } 169 | 170 | #ifdef __linux__ 171 | HANDLE OpenDriverHandle(void) 172 | { 173 | return(INVALID_HANDLE_VALUE); 174 | } 175 | #elif defined(_WIN32) 176 | bool IsDriverServiceInstalled(HANDLE SCManager, HANDLE *Service) 177 | { 178 | HANDLE ServiceHandle; 179 | 180 | ServiceHandle = OpenService(SCManager, "atillk64", SERVICE_START | DELETE | SERVICE_STOP); 181 | 182 | if(ServiceHandle) 183 | { 184 | *Service = ServiceHandle; 185 | return(true); 186 | } 187 | 188 | *Service = NULL; 189 | return(false); 190 | } 191 | bool LoadDriver(HANDLE Service) 192 | { 193 | return(StartService(Service, 0, NULL)); 194 | } 195 | HANDLE InstallDriverService(HANDLE SCManager) 196 | { 197 | HANDLE Service, DriverBin; 198 | DWORD BytesWritten; 199 | char PathBuf[256]; 200 | 201 | GetTempPathA(255, PathBuf); 202 | strncat(PathBuf, "\\atillk64.sys", 255); 203 | 204 | DriverBin = CreateFileA(PathBuf, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 205 | 206 | if((!DriverBin) && (GetLastError() == ERROR_ALREADY_EXISTS)) 207 | DriverBin = CreateFileA(PathBuf, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 208 | 209 | WriteFile(DriverBin, atillk64_bin, DRIVER_BIN_SIZE, &BytesWritten, NULL); 210 | 211 | if(BytesWritten != DRIVER_BIN_SIZE) 212 | { 213 | CloseHandle(DriverBin); 214 | return(NULL); 215 | } 216 | 217 | Service = CreateService(SCManager, "atillk64", "AMD Diagnostics Utility", SERVICE_START | DELETE | SERVICE_STOP, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, PathBuf, NULL, NULL, NULL, NULL, NULL); 218 | 219 | return(Service); 220 | } 221 | HANDLE OpenDriverHandle(void) 222 | { 223 | HANDLE SCManager; 224 | HANDLE DrvService; 225 | SCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); 226 | 227 | if(!IsDriverServiceInstalled(SCManager, &DrvService)) 228 | { 229 | printf("Driver is not installed. Installing driver...\n"); 230 | DrvService = InstallDriverService(SCManager); 231 | } 232 | 233 | if(!DrvService) abort(); 234 | 235 | printf("LoadDriver returned %d.\n", LoadDriver(DrvService)); 236 | return(CreateFileA("\\\\.\\atillk64", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL)); 237 | } 238 | #endif 239 | 240 | int main(int argc, char **argv) 241 | { 242 | uint8_t byte; 243 | int ret, i, GPUCount, VRMIdx, VRMOutputIdx; 244 | ArgsObj Config; 245 | AMDGPU *GPUList, *CurGPU; 246 | HANDLE DrvHandle = INVALID_HANDLE_VALUE; 247 | 248 | printf("wolfamdvolt %s\n", WOLFAMDVOLT_VERSION_STR); 249 | 250 | memset(&Config, 0x00, sizeof(ArgsObj)); 251 | if(!ParseCmdLine(&Config, argc, argv)) return(-1); 252 | 253 | VRMIdx = Config.VRMIdxProvided ? Config.VRMIdx : 0; 254 | VRMOutputIdx = Config.VRMOutputIdxProvided ? Config.VRMOutputIdx : 0; 255 | 256 | GPUList = NULL; 257 | I2CDebugOutput = Config.Debug; 258 | DrvHandle = OpenDriverHandle(); 259 | 260 | #ifdef _WIN32 261 | if(DrvHandle == INVALID_HANDLE_VALUE) 262 | { 263 | char ErrorMsg[256]; 264 | printf("Unable to open handle.\n"); 265 | 266 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ErrorMsg, sizeof(ErrorMsg), NULL); 267 | printf("Details: %s\n", ErrorMsg); 268 | 269 | return(-1); 270 | } 271 | #endif 272 | 273 | GPUCount = FindAMDGPUs(&GPUList, DrvHandle); 274 | 275 | if(!Config.GPUIdxProvided) 276 | { 277 | for(CurGPU = GPUList, i = 0; CurGPU; CurGPU = CurGPU->next, ++i) 278 | { 279 | ret = InitAMDGPUMMIO(CurGPU); 280 | 281 | if(ret < 0) 282 | { 283 | printf("Unable to initialize MMIO for GPU - failed with %d.\n", ret); 284 | return(-1); 285 | } 286 | 287 | DetectVRMControllers(CurGPU); 288 | 289 | if(!Config.Debug) printf("GPU %d:\n", i); 290 | else printf("GPU %d - (%02X:%02X:%02X - DID: 0x%04X - SSVID: 0x%04X - SSDID: 0x%04X):\n", i, CurGPU->PCIBus, CurGPU->PCIDevice, CurGPU->PCIFunction, CurGPU->DeviceID, CurGPU->SubVendor, CurGPU->SubDevice); 291 | DumpGPUInfo(CurGPU, Config); 292 | } 293 | } 294 | 295 | if(Config.GPUIdxProvided && (!Config.SetVoltage && !Config.SetVoltageOffset)) 296 | { 297 | uint32_t idx = Config.GPUIdx; 298 | 299 | if(idx >= GPUCount) 300 | { 301 | printf("GPU index out of range.\n"); 302 | ReleaseAMDGPUs(GPUList); 303 | return(-2); 304 | } 305 | 306 | CurGPU = GPUList; 307 | for(int i = 0; i < idx; ++i) CurGPU = CurGPU->next; 308 | 309 | ret = InitAMDGPUMMIO(CurGPU); 310 | 311 | if(ret < 0) 312 | { 313 | printf("Unable to initialize MMIO for GPU - failed with %d.\n", ret); 314 | return(-1); 315 | } 316 | 317 | DetectVRMControllers(CurGPU); 318 | 319 | if(!Config.Debug) printf("GPU %d:\n", idx); 320 | else printf("GPU %d - (%02X:%02X:%02X - DID: 0x%04X - SSVID: 0x%04X - SSDID: 0x%04X):\n", idx, CurGPU->PCIBus, CurGPU->PCIDevice, CurGPU->PCIFunction, CurGPU->DeviceID, CurGPU->SubVendor, CurGPU->SubDevice); 321 | 322 | DumpGPUInfo(CurGPU, Config); 323 | } 324 | 325 | if(Config.GPUIdxProvided && Config.SetVoltage) 326 | { 327 | uint32_t idx = Config.GPUIdx; 328 | float CurVoltage; 329 | 330 | if(idx >= GPUCount) 331 | { 332 | printf("GPU index out of range.\n"); 333 | ReleaseAMDGPUs(GPUList); 334 | return(-2); 335 | } 336 | 337 | CurGPU = GPUList; 338 | 339 | for(int i = 0; i < idx; ++i) CurGPU = CurGPU->next; 340 | 341 | ret = InitAMDGPUMMIO(CurGPU); 342 | 343 | if(ret < 0) 344 | { 345 | printf("Unable to initialize MMIO for GPU - failed with %d.\n", ret); 346 | return(-1); 347 | } 348 | 349 | DetectVRMControllers(CurGPU); 350 | 351 | if(!CurGPU->VRMCount) 352 | { 353 | printf("No recognized/supported VRMs found.\n"); 354 | ReleaseAMDGPUs(GPUList); 355 | return(0); 356 | } 357 | 358 | if(VRMIdx >= CurGPU->VRMCount) 359 | { 360 | printf("Selected VRM index does not exist.\n"); 361 | ReleaseAMDGPUs(GPUList); 362 | return(0); 363 | } 364 | 365 | VRMController *CurVRM = CurGPU->VRMs; 366 | 367 | for(int i = 0; i < VRMIdx && CurVRM; ++i) CurVRM = CurVRM->next; 368 | 369 | if(CurVRM->VRMType == VRM_CONTROLLER_TYPE_NCP81022) 370 | NCP81022SwitchControls(CurVRM, true); 371 | else if(CurVRM->VRMType == VRM_CONTROLLER_TYPE_UP9505) 372 | uP9505Acquire(CurVRM); 373 | 374 | if(VRMOutputIdx >= CurVRM->OutputCount) 375 | { 376 | printf("Selected VRM output does not exist on selected VRM.\n"); 377 | ReleaseAMDGPUs(GPUList); 378 | return(0); 379 | } 380 | 381 | if(!(CurVRM->Capabilities & VRM_CAPABILITY_SET_VOLTAGE)) 382 | { 383 | printf("Selected VRM does not support direct voltage setting. Try using an offset instead.\n"); 384 | ReleaseAMDGPUs(GPUList); 385 | return(-1); 386 | } 387 | 388 | CurVRM->SetOutputIdx(CurVRM, VRMOutputIdx); 389 | 390 | if(CurVRM->GetVoltage(CurVRM, &CurVoltage) != VRM_ERROR_SUCCESS) 391 | { 392 | printf("Failure getting voltage from VRM.\n"); 393 | ReleaseAMDGPUs(GPUList); 394 | return(-1); 395 | } 396 | 397 | printf("Voltage is %.4f - attempting to set %.4f.\n", CurVoltage, Config.RequestedVoltage); 398 | 399 | if(CurVRM->SetVoltage(CurVRM, Config.RequestedVoltage) != VRM_ERROR_SUCCESS) 400 | { 401 | printf("Failure setting voltage.\n"); 402 | ReleaseAMDGPUs(GPUList); 403 | return(-1); 404 | } 405 | 406 | if(CurVRM->GetVoltage(CurVRM, &CurVoltage) != VRM_ERROR_SUCCESS) 407 | { 408 | printf("Failure getting voltage from VRM.\n"); 409 | ReleaseAMDGPUs(GPUList); 410 | return(-1); 411 | } 412 | 413 | printf("Voltage is now %.4f.\n", CurVoltage); 414 | 415 | CloseAMDGPUMMIO(CurGPU); 416 | } 417 | 418 | if(Config.GPUIdxProvided && Config.SetVoltageOffset) 419 | { 420 | uint32_t idx = Config.GPUIdx; 421 | float CurVoltage; 422 | 423 | if(idx >= GPUCount) 424 | { 425 | printf("GPU index out of range.\n"); 426 | ReleaseAMDGPUs(GPUList); 427 | return(-2); 428 | } 429 | 430 | CurGPU = GPUList; 431 | 432 | for(int i = 0; i < idx; ++i) CurGPU = CurGPU->next; 433 | 434 | ret = InitAMDGPUMMIO(CurGPU); 435 | 436 | if(ret < 0) 437 | { 438 | printf("Unable to initialize MMIO for GPU - failed with %d.\n", ret); 439 | return(-1); 440 | } 441 | 442 | DetectVRMControllers(CurGPU); 443 | 444 | if(!CurGPU->VRMCount) 445 | { 446 | printf("No recognized/supported VRMs found.\n"); 447 | ReleaseAMDGPUs(GPUList); 448 | return(0); 449 | } 450 | 451 | if(VRMIdx >= CurGPU->VRMCount) 452 | { 453 | printf("Selected VRM index does not exist.\n"); 454 | ReleaseAMDGPUs(GPUList); 455 | return(0); 456 | } 457 | 458 | VRMController *CurVRM = CurGPU->VRMs; 459 | 460 | for(int i = 0; i < VRMIdx && CurVRM; ++i) CurVRM = CurVRM->next; 461 | 462 | if(CurGPU->VRMs->VRMType == VRM_CONTROLLER_TYPE_UP9505) 463 | uP9505Acquire(CurGPU->VRMs); 464 | 465 | if(VRMOutputIdx >= CurVRM->OutputCount) 466 | { 467 | printf("Selected VRM output does not exist on selected VRM.\n"); 468 | ReleaseAMDGPUs(GPUList); 469 | return(0); 470 | } 471 | 472 | CurVRM->SetOutputIdx(CurVRM, VRMOutputIdx); 473 | 474 | if(CurVRM->GetVoltageOffset(CurVRM, &CurVoltage) != VRM_ERROR_SUCCESS) 475 | { 476 | printf("Failure getting voltage offset from VRM.\n"); 477 | ReleaseAMDGPUs(GPUList); 478 | return(-1); 479 | } 480 | 481 | printf("Voltage offset is %.4f. Attempting to set %.4f...\n", CurVoltage, Config.RequestedVoltageOffset); 482 | 483 | if(CurVRM->SetVoltageOffset(CurVRM, Config.RequestedVoltageOffset) != VRM_ERROR_SUCCESS) 484 | { 485 | printf("Failure setting voltage.\n"); 486 | ReleaseAMDGPUs(GPUList); 487 | return(-1); 488 | } 489 | 490 | if(CurVRM->GetVoltageOffset(CurVRM, &CurVoltage) != VRM_ERROR_SUCCESS) 491 | { 492 | printf("Failure getting voltage offset from VRM.\n"); 493 | ReleaseAMDGPUs(GPUList); 494 | return(-1); 495 | } 496 | 497 | printf("Voltage offset is %.4f.\n", CurVoltage); 498 | CloseAMDGPUMMIO(CurGPU); 499 | } 500 | 501 | ReleaseAMDGPUs(GPUList); 502 | 503 | return(0); 504 | } 505 | -------------------------------------------------------------------------------- /ncp81022.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "amdgpu.h" 6 | #include "amdi2c.h" 7 | #include "amdsmbus.h" 8 | #include "amdpmbus.h" 9 | #include "ncp81022.h" 10 | 11 | /* 12 | // Returns true if an NCP81022 was found, false if not 13 | bool NCP81022Detect(AMDGPU *GPU, int *ret) 14 | { 15 | uint16_t out; 16 | 17 | // NCP81022 SMBus address can only be at 0x20 - 0x27 18 | for(int addr = 0x20; addr < 0x28; ++addr) 19 | { 20 | AMDGPUI2CInit(GPU, addr); 21 | out = AMDSMBusReadWord(GPU, PMBUS_MFR_ID, ret); 22 | 23 | if(out == 0x001A) 24 | { 25 | out = AMDSMBusReadWord(GPU, PMBUS_MFR_MODEL, ret); 26 | if(out == 0x1022) return(true); 27 | } 28 | } 29 | 30 | return(false); 31 | } 32 | */ 33 | 34 | uint32_t NCP81022GetOutputCurrent(VRMController *VRM, float *Current) 35 | { 36 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 37 | uint16_t Iout = AMDSMBusReadWord(VRM->ParentGPU, PMBUS_READ_IOUT, NULL); 38 | uint16_t Offset = AMDSMBusReadWord(VRM->ParentGPU, NCP81022_PMBUS_IOUT_OFFSET, NULL); 39 | uint16_t Gain = AMDSMBusReadWord(VRM->ParentGPU, NCP81022_PMBUS_IOUT_CAL_GAIN, NULL); 40 | //float IOUTGain = PMBusDecodeLinearValue(Gain); 41 | //float IOUTOffset = PMBusDecodeLinearValue(Offset); 42 | float IOUT = PMBusDecodeLinearValue(Iout); 43 | 44 | *Current = (float)IOUT; 45 | 46 | return(VRM_ERROR_SUCCESS); 47 | } 48 | 49 | uint32_t NCP81022GetVoltage(VRMController *VRM, float *VDDC) 50 | { 51 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 52 | 53 | //uint16_t VID = AMDSMBusReadWord(VRM->ParentGPU, PMBUS_READ_VOUT, NULL); 54 | //*VDDC = ((VID & 0xFF) >= 0xF7) ? 0.0 : 1.55 - ((VID & 0xFF) * 0.00625); 55 | 56 | int16_t val = AMDSMBusReadWord(VRM->ParentGPU, NCP81022_READ_VOUT_LINEAR_REG, NULL); 57 | *VDDC = PMBusDecodeLinearValueWithExponent(val, -9); 58 | 59 | return(VRM_ERROR_SUCCESS); 60 | } 61 | 62 | uint32_t NCP81022SetVoltage(VRMController *VRM, float Voltage) 63 | { 64 | uint16_t VID; 65 | 66 | // Ensure voltage is in range 67 | if(Voltage < 0.25 || Voltage > 1.55) return(VRM_ERROR_RANGE); 68 | 69 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 70 | 71 | Voltage = (1.55 - Voltage) / 0.00625; 72 | 73 | // Get VID from GPU 74 | VID = AMDSMBusReadWord(VRM->ParentGPU, PMBUS_READ_VOUT, NULL) & 0xFF00; 75 | 76 | // Round to nearest VID and save 77 | VID |= (uint8_t)(Voltage + 0.5); 78 | 79 | AMDSMBusWriteWord(VRM->ParentGPU, PMBUS_VOUT_COMMAND, VID); 80 | 81 | return(VRM_ERROR_SUCCESS); 82 | } 83 | 84 | uint32_t NCP81022GetOutputIdx(VRMController *VRM, uint32_t *Idx) 85 | { 86 | *Idx = VRM->SelectedOutput; 87 | return(VRM_ERROR_SUCCESS); 88 | } 89 | 90 | uint32_t NCP81022SetOutputIdx(VRMController *VRM, uint32_t Idx) 91 | { 92 | if(Idx >= VRM->OutputCount) return(VRM_ERROR_RANGE); 93 | 94 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 95 | 96 | uint8_t reg = AMDSMBusReadByte(VRM->ParentGPU, NCP81022_PMBUS_VRCONFIG1_REG, NULL); 97 | 98 | if(Idx) reg |= 0x04; 99 | else reg &= 0xFB; 100 | 101 | AMDI2CWriteByte(VRM->ParentGPU, NCP81022_PMBUS_VRCONFIG1_REG, reg); 102 | 103 | VRM->SelectedOutput = Idx; 104 | return(VRM_ERROR_SUCCESS); 105 | } 106 | 107 | uint32_t NCP81022GetOffset(VRMController *VRM, float *VoltOffset) 108 | { 109 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 110 | 111 | int8_t VID = AMDSMBusReadByte(VRM->ParentGPU, NCP81022_PMBUS_SPOFFSET_REG, NULL); 112 | *VoltOffset = VID * 0.00625; 113 | 114 | return(VRM_ERROR_SUCCESS); 115 | } 116 | 117 | uint32_t NCP81022SetOffset(VRMController *VRM, float Voltage) 118 | { 119 | if((Voltage >= VRM->MaxOffset) || (Voltage <= VRM->MinOffset)) return(VRM_ERROR_RANGE); 120 | 121 | uint8_t VOffset = (Voltage > 0) ? (Voltage / 0.00625) + 0.5 : (Voltage / 0.00625) - 0.5; 122 | 123 | AMDI2CWriteByte(VRM->ParentGPU, NCP81022_PMBUS_SPOFFSET_REG, VOffset); 124 | 125 | return(VRM_ERROR_SUCCESS); 126 | } 127 | 128 | uint32_t NCP81022SetLoadLine(VRMController *VRM, uint8_t Setting) 129 | { 130 | return(AMDI2CWriteByte(VRM->ParentGPU, NCP81022_PMBUS_LOADLINE_REG, Setting)); 131 | } 132 | 133 | // Return value is how many devices were found. 134 | uint32_t NCP81022Detect(AMDGPU *GPU, VRMController **VRMs) 135 | { 136 | uint16_t out; 137 | uint32_t DevicesFound = 0; 138 | VRMController *CurrentVRM; 139 | 140 | // NCP81022 SMBus address can only be at 0x20 - 0x27 141 | for(int addr = 0x20; addr < 0x28; ++addr) 142 | { 143 | uint32_t tmp; 144 | AMDGPUI2CInit(GPU, STATIC_I2C_LINE_FIXME, addr); 145 | //tmp = AMDSMBusReadByte(GPU, PMBUS_REVISION, NULL); 146 | 147 | // NCP81022 will support at LEAST PMBus Part I 148 | // rev. 1.1 and PMBus Part II rev. 1.2. 149 | //if(((tmp & 0x0F) < 0x02) && ((tmp >> 4) < 0x01)) continue; 150 | 151 | //tmp = AMDSMBusReadByte(GPU, PMBUS_CAPABILITY, NULL); 152 | 153 | // Lowest 5 bits should be zero. 154 | //if(tmp & 0x1F) continue; 155 | //else if(!(tmp & 0x80)) continue; 156 | //else if(!(tmp & 0x20)) continue; 157 | 158 | out = AMDSMBusReadWord(GPU, PMBUS_MFR_ID, NULL); 159 | 160 | if(out == 0x001A) 161 | { 162 | out = AMDSMBusReadWord(GPU, PMBUS_MFR_MODEL, NULL); 163 | if(out == 0x1022) 164 | { 165 | if(!*VRMs) 166 | { 167 | CurrentVRM = *VRMs = (VRMController *)calloc(1, sizeof(VRMController)); 168 | } 169 | else 170 | { 171 | for(CurrentVRM = *VRMs; CurrentVRM->next; CurrentVRM = CurrentVRM->next); 172 | CurrentVRM = CurrentVRM->next = (VRMController *)calloc(1, sizeof(VRMController)); 173 | } 174 | DevicesFound++; 175 | 176 | CurrentVRM->ParentGPU = GPU; 177 | CurrentVRM->VRMType = VRM_CONTROLLER_TYPE_NCP81022; 178 | CurrentVRM->Capabilities = VRM_CAPABILITY_OFFSET | VRM_CAPABILITY_LOADLINE; 179 | 180 | CurrentVRM->MinOffset = -793.75; 181 | CurrentVRM->MaxOffset = 793.75; 182 | 183 | CurrentVRM->OutputCount = 2; 184 | CurrentVRM->SelectedOutput = 0; 185 | 186 | CurrentVRM->I2CAddressList[0] = addr; 187 | 188 | CurrentVRM->GetVoltage = NCP81022GetVoltage; 189 | CurrentVRM->SetVoltage = NCP81022SetVoltage; 190 | CurrentVRM->GetOutputIdx = NCP81022GetOutputIdx; 191 | CurrentVRM->SetOutputIdx = NCP81022SetOutputIdx; 192 | CurrentVRM->GetVoltageOffset = NCP81022GetOffset; 193 | CurrentVRM->SetVoltageOffset = NCP81022SetOffset; 194 | CurrentVRM->SetLoadLine = NCP81022SetLoadLine; 195 | //CurrentVRM->GetCurrent = NCP81022GetOutputCurrent; 196 | CurrentVRM->next = NULL; 197 | } 198 | } 199 | } 200 | 201 | return(DevicesFound); 202 | } 203 | // Register format: 204 | // Bit 0: 205 | // 0 - SVI2 controls shit 206 | // 1 - SMBus controls shit 207 | // Bit 1: Reserved 208 | // Bit 2: Rail select 209 | // 0 - Main rail 210 | // 1 - Other rail 211 | // Bits 3 - 5: Reserved 212 | // Bit 6: 213 | // 0 - CLIM latch-off enabled 214 | // 1 - CLIM latch-off disabled 215 | // Bit 7: Reserved 216 | uint32_t NCP81022SwitchControls(VRMController *VRM, bool SMBusControl) 217 | { 218 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 219 | 220 | uint8_t reg = AMDSMBusReadByte(VRM->ParentGPU, NCP81022_PMBUS_VRCONFIG1_REG, NULL); 221 | 222 | if(SMBusControl) reg |= 0x01; 223 | else reg &= 0xFE; 224 | 225 | AMDI2CWriteByte(VRM->ParentGPU, NCP81022_PMBUS_VRCONFIG1_REG, reg); 226 | 227 | return(VRM_ERROR_SUCCESS); 228 | } 229 | 230 | float NCP81022GetInputVoltage(AMDGPU *GPU, int *ret) 231 | { 232 | uint16_t EncodedVolts = AMDSMBusReadWord(GPU, PMBUS_READ_VIN, ret); 233 | return(PMBusDecodeLinearValue(EncodedVolts)); 234 | } 235 | 236 | // Broke atm 237 | float NCP81022GetOutputPower(AMDGPU *GPU, int *ret) 238 | { 239 | uint16_t EncodedWatts = AMDSMBusReadWord(GPU, PMBUS_READ_POUT, ret); 240 | return(PMBusDecodeLinearValue(EncodedWatts)); 241 | } 242 | -------------------------------------------------------------------------------- /ncp81022.h: -------------------------------------------------------------------------------- 1 | #ifndef __NCP81022_H 2 | #define __NCP81022_H 3 | 4 | #include "vrm.h" 5 | 6 | #define NCP81022_PMBUS_IOUT_CAL_GAIN 0x38 7 | #define NCP81022_PMBUS_IOUT_OFFSET 0x39 8 | #define NCP81022_PMBUS_VRCONFIG1_REG 0xD2 9 | #define NCP81022_PMBUS_VRCONFIG2_REG 0xD3 10 | #define NCP81022_PMBUS_LOADLINE_REG 0xE4 11 | #define NCP81022_PMBUS_SPOFFSET_REG 0xE6 12 | #define NCP81022_READ_VOUT_LINEAR_REG 0xD4 13 | 14 | uint32_t NCP81022SwitchControls(VRMController *VRM, bool SMBusControl); 15 | uint32_t NCP81022Detect(AMDGPU *GPU, VRMController **VRMs); 16 | uint32_t NCP81022GetOffset(VRMController *VRM, float *VoltOffset); 17 | uint32_t NCP81022SetOffset(VRMController *VRM, float Voltage); 18 | uint32_t NCP81022GetOutputIdx(VRMController *VRM, uint32_t *Idx); 19 | uint32_t NCP81022SetOutputIdx(VRMController *VRM, uint32_t Idx); 20 | uint32_t NCP81022GetVoltage(VRMController *VRM, float *VDDC); 21 | uint32_t NCP81022SetVoltage(VRMController *VRM, float Voltage); 22 | float NCP81022GetInputVoltage(AMDGPU *GPU, int *ret); 23 | float NCP81022GetOutputPower(AMDGPU *GPU, int *ret); 24 | uint32_t NCP81022GetOutputCurrent(VRMController *VRM, float *Current); 25 | uint32_t NCP81022SetLoadLine(VRMController *VRM, uint8_t Setting); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /rt8894a.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "amdgpu.h" 6 | #include "amdi2c.h" 7 | #include "amdsmbus.h" 8 | #include "amdpmbus.h" 9 | #include "rt8894a.h" 10 | 11 | // It is important that before we update any register at runtime 12 | // that we write 0x5A to RT8894A_I2C_LOCK_IND, and that we write 13 | // 0xFF back to RT8894A_I2C_LOCK_IND when our work is complete. 14 | 15 | void RT8894AUnlockI2C(AMDGPU *GPU) 16 | { 17 | AMDI2CWriteByte(GPU, RT8894A_I2C_LOCK_IND, RT8894A_UNLOCK_VALUE); 18 | } 19 | 20 | void RT8894ALockI2C(AMDGPU *GPU) 21 | { 22 | AMDI2CWriteByte(GPU, RT8894A_I2C_LOCK_IND, RT8894A_LOCK_VALUE); 23 | } 24 | 25 | uint32_t RT8894AGetVoltage(VRMController *VRM, float *VDDC) 26 | { 27 | uint8_t VID = AMDSMBusReadByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? RT8894A_I2C_READ_VDD : RT8894A_I2C_READ_VDDNB, NULL) << 1; 28 | 29 | *VDDC = (VID > 0xF8) ? 0.0 : 1.55 - ((float)VID * 0.00625); 30 | 31 | return(VRM_ERROR_SUCCESS); 32 | } 33 | 34 | uint32_t RT8894AGetOutputIdx(VRMController *VRM, uint32_t *Idx) 35 | { 36 | *Idx = VRM->SelectedOutput; 37 | return(VRM_ERROR_SUCCESS); 38 | } 39 | 40 | uint32_t RT8894ASetOutputIdx(VRMController *VRM, uint32_t Idx) 41 | { 42 | if(Idx >= VRM->OutputCount) return(VRM_ERROR_RANGE); 43 | 44 | VRM->SelectedOutput = Idx; 45 | return(VRM_ERROR_SUCCESS); 46 | } 47 | 48 | // TODO/FIXME: Macro away the magic constants in here. 49 | uint32_t RT8894ASetVoltage(VRMController *VRM, float Voltage) 50 | { 51 | uint8_t VID, reg; 52 | 53 | if(Voltage > 1.55 || Voltage <= 0.0) return(VRM_ERROR_RANGE); 54 | 55 | VID = (uint8_t)(((1.55 - Voltage) / 0.00625) + 0.5); 56 | 57 | 58 | 59 | RT8894AUnlockI2C(VRM->ParentGPU); 60 | 61 | AMDI2CWriteByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? RT8894A_I2C_FIX_VDD : RT8894A_I2C_FIX_NB, VID); 62 | 63 | reg = AMDSMBusReadByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? RT8894A_I2C_AUTO_ZLL_VDD : RT8894A_I2C_AUTO_ZLL_NB, NULL); 64 | // If VFIX mode for the chosen rail isn't enabled, we need to enable it 65 | // 0x2E means set VDD to VID + RT8894A_I2C_OFS_VDD (ignoring all SVI2 offset trim commands) 66 | // Enable VDD rail auto down phase mode, enable VDD rail zero load-line, disable VDD power 67 | // scenario load-line, and enable VDD rail VFIX mode. Only set it if we haven't yet. 68 | if(reg != 0x26) AMDI2CWriteByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? RT8894A_I2C_AUTO_ZLL_VDD : RT8894A_I2C_AUTO_ZLL_NB, 0x26); 69 | 70 | 71 | RT8894ALockI2C(VRM->ParentGPU); 72 | 73 | return(VRM_ERROR_SUCCESS); 74 | } 75 | 76 | 77 | // RT8894A can only reside at a single address: 0x20 78 | // TODO/FIXME: This could use more checks... 79 | 80 | // Return value is how many devices were found. 81 | uint32_t RT8894ADetect(AMDGPU *GPU, VRMController **VRMs) 82 | { 83 | VRMController *CurrentVRM; 84 | 85 | AMDGPUI2CInit(GPU, STATIC_I2C_LINE_FIXME, 0x20); 86 | 87 | if(AMDSMBusReadByte(GPU, RT8894A_I2C_READ_PROD_ID, NULL) == 0x01) 88 | { 89 | if(!*VRMs) 90 | { 91 | CurrentVRM = *VRMs = (VRMController *)calloc(1, sizeof(VRMController)); 92 | } 93 | else 94 | { 95 | for(CurrentVRM = *VRMs; CurrentVRM->next; CurrentVRM = CurrentVRM->next); 96 | CurrentVRM = CurrentVRM->next = (VRMController *)calloc(1, sizeof(VRMController)); 97 | } 98 | 99 | CurrentVRM->ParentGPU = GPU; 100 | CurrentVRM->VRMType = VRM_CONTROLLER_TYPE_RT8894A; 101 | CurrentVRM->Capabilities = 0; 102 | 103 | CurrentVRM->OutputCount = 2; 104 | CurrentVRM->SelectedOutput = 0; 105 | 106 | CurrentVRM->I2CAddressList[0] = 0x20; 107 | 108 | CurrentVRM->GetVoltage = RT8894AGetVoltage; 109 | CurrentVRM->SetVoltage = RT8894ASetVoltage; 110 | CurrentVRM->GetOutputIdx = RT8894AGetOutputIdx; 111 | CurrentVRM->SetOutputIdx = RT8894ASetOutputIdx; 112 | 113 | CurrentVRM->next = NULL; 114 | return(1); 115 | } 116 | 117 | return(0); 118 | } 119 | 120 | -------------------------------------------------------------------------------- /rt8894a.h: -------------------------------------------------------------------------------- 1 | #ifndef __RT8894A_H 2 | #define __RT8894A_H 3 | 4 | #define RT8894A_I2C_AUTO_ZLL_VDD 0x00 5 | #define RT8894A_I2C_PS_TH1_HYS1_VDD 0x01 6 | #define RT8894A_I2C_PS_TH2_HYS2_VDD 0x02 7 | #define RT8894A_I2C_PS0_OFS_VDD 0x03 8 | #define RT8894A_I2C_PS0_FSW_LL_VDD 0x04 9 | #define RT8894A_I2C_PS1_OFS_VDD 0x05 10 | #define RT8894A_I2C_PS1_FSW_LL_VDD 0x06 11 | #define RT8894A_I2C_PS2_OFS_VDD 0x07 12 | #define RT8894A_I2C_PS2_FSW_LL_VDD 0x08 13 | #define RT8894A_I2C_PS_PH_SEL_VDD 0x09 14 | #define RT8894A_I2C_OVP_UVP_VDD 0x0A 15 | #define RT8894A_I2C_OCP_VDD 0x0B 16 | #define RT8894A_I2C_RSET_VDD 0x0C 17 | #define RT8894A_I2C_QR_WIDTH_TH_VDD 0x0D 18 | #define RT8894A_I2C_DVID_CHEAT_VDD 0x0E 19 | #define RT8894A_I2C_CBG12_VDD 0x0F 20 | #define RT8894A_I2C_CBG34_VDD 0x10 21 | #define RT8894A_I2C_OFS_VDD 0x11 22 | #define RT8894A_I2C_INI_OFS_VDD 0x12 23 | #define RT8894A_I2C_FIX_VDD 0x13 24 | 25 | #define RT8894A_I2C_AUTO_ZLL_NB 0x20 26 | #define RT8894A_I2C_PS_TH1_HYS1_NB 0x21 27 | #define RT8894A_I2C_PS_TH2_HYS2_NB 0x22 28 | #define RT8894A_I2C_PS0_OFS_NB 0x23 29 | #define RT8894A_I2C_PS0_FSW_LL_NB 0x24 30 | #define RT8894A_I2C_PS1_OFS_NB 0x25 31 | #define RT8894A_I2C_PS1_FSW_LL_NB 0x26 32 | #define RT8894A_I2C_PS2_OFS_NB 0x27 33 | #define RT8894A_I2C_PS2_FSW_LL_NB 0x28 34 | #define RT8894A_I2C_PS_PH_SEL_NB 0x29 35 | #define RT8894A_I2C_OVP_UVP_NB 0x2A 36 | #define RT8894A_I2C_OCP_NB 0x2B 37 | #define RT8894A_I2C_CBG_RSET_NB 0x2C 38 | #define RT8894A_I2C_QR_WIDTH_TH_NB 0x2D 39 | #define RT8894A_I2C_DVID_CHEAT_NB 0x2E 40 | #define RT8894A_I2C_CBG12_NB 0x2F 41 | #define RT8894A_I2C_OFS_NB 0x30 42 | #define RT8894A_I2C_INI_OFS_NB 0x31 43 | #define RT8894A_I2C_FIX_NB 0x32 44 | 45 | #define RT8894A_I2C_READ_IDD 0x40 46 | #define RT8894A_I2C_READ_IDDNB 0x41 47 | #define RT8894A_I2C_READ_VDD 0x42 48 | #define RT8894A_I2C_READ_VDDNB 0x43 49 | #define RT8894A_I2C_READ_TZ_VDD 0x44 50 | #define RT8894A_I2C_READ_PS 0x45 51 | #define RT8894A_I2C_READ_PROD_ID 0x46 52 | #define RT8894A_I2C_READ_PROD_REV 0x47 53 | #define RT8894A_I2C_READ_CPU_PSI 0x48 54 | #define RT8894A_I2C_READ_VR_PROC 0x49 55 | #define RT8894A_I2C_OCP_OVP_UVP_EN 0x50 56 | #define RT8894A_I2C_VRHOT_WDT_EN 0x51 57 | #define RT8894A_I2C_VRHOTSHDN 0x52 58 | #define RT8894A_I2C_LOCK_IND 0x53 59 | 60 | #define RT8894A_UNLOCK_VALUE 0x5A 61 | #define RT8894A_LOCK_VALUE 0xFF 62 | 63 | uint32_t RT8894ADetect(AMDGPU *GPU, VRMController **VRMs); 64 | uint32_t RT8894AGetVoltage(VRMController *VRM, float *VDDC); 65 | uint32_t RT8894ASetVoltage(VRMController *VRM, float Voltage); 66 | #endif 67 | -------------------------------------------------------------------------------- /up1801.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "amdgpu.h" 6 | #include "amdi2c.h" 7 | #include "up1801.h" 8 | 9 | uint32_t uP1801GetVoltage(VRMController *VRM, float *VDDC) 10 | { 11 | uint8_t Mode, VID; 12 | 13 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 14 | 15 | // First - check what mode we're in 16 | 17 | Mode = AMDI2CReadByte(VRM->ParentGPU, UP1801_ILIM_REG, NULL); 18 | 19 | if(Mode & UP1801_ILIM_DISABLED_OUTPUT) 20 | { 21 | *VDDC = 0.0; 22 | return(VRM_ERROR_SUCCESS); 23 | } 24 | 25 | if(Mode & UP1801_ILIM_SVI_SETTING) 26 | VID = AMDI2CReadByte(VRM->ParentGPU, UP1801_SVI_REG, NULL); 27 | else if(Mode & UP1801_ILIM_INIT_SETTING) 28 | VID = AMDI2CReadByte(VRM->ParentGPU, UP1801_INI_REG, NULL); 29 | else if(Mode & UP1801_ILIM_PVI_SETTING) // This is safe because we know SVI is not set 30 | { 31 | // I know since the regs are contiguous, this could 32 | // be a LOT simpler, but doing it this way for readability 33 | switch(Mode & 0x03) 34 | { 35 | case 0: 36 | VID = AMDI2CReadByte(VRM->ParentGPU, UP1801_LEVEL1_REG, NULL); 37 | break; 38 | case 1: 39 | VID = AMDI2CReadByte(VRM->ParentGPU, UP1801_LEVEL2_REG, NULL); 40 | break; 41 | case 2: 42 | VID = AMDI2CReadByte(VRM->ParentGPU, UP1801_LEVEL3_REG, NULL); 43 | break; 44 | case 3: 45 | VID = AMDI2CReadByte(VRM->ParentGPU, UP1801_LEVEL4_REG, NULL); 46 | break; 47 | } 48 | } 49 | 50 | *VDDC = 0.6 + (VID * 0.005); 51 | 52 | return(VRM_ERROR_SUCCESS); 53 | } 54 | 55 | uint32_t uP1801SetVoltage(VRMController *VRM, float Voltage) 56 | { 57 | uint8_t Mode, VID; 58 | 59 | if(Voltage > 1.875 || Voltage < 0.6) return(VRM_ERROR_RANGE); 60 | 61 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 62 | 63 | // Program the voltage first, so that we don't have the 64 | // possibility of dropping it to 0.6V accidentally. 65 | 66 | VID = ((Voltage - 0.6) / 0.005) + 0.5; 67 | 68 | AMDI2CWriteByte(VRM->ParentGPU, UP1801_SVI_REG, VID); 69 | 70 | // Ensure the controller is in SVI mode 71 | Mode = AMDI2CReadByte(VRM->ParentGPU, UP1801_ILIM_REG, NULL); 72 | 73 | if(!(Mode & UP1801_ILIM_SVI_SETTING)) 74 | AMDI2CWriteByte(VRM->ParentGPU, UP1801_ILIM_REG, Mode | UP1801_ILIM_SVI_SETTING); 75 | 76 | return(VRM_ERROR_SUCCESS); 77 | } 78 | 79 | uint32_t uP1801GetOutputIdx(struct _VRMController *VRM, uint32_t *idx) 80 | { 81 | *idx = VRM->SelectedOutput; 82 | return(VRM_ERROR_SUCCESS); 83 | } 84 | 85 | uint32_t uP1801SetOutputIdx(struct _VRMController *VRM, uint32_t idx) 86 | { 87 | if(idx >= VRM->OutputCount) return(VRM_ERROR_RANGE); 88 | 89 | VRM->SelectedOutput = idx; 90 | return(VRM_ERROR_SUCCESS); 91 | } 92 | 93 | // uP1801 can only reside at addresses 0x51, 0x52, or 0x53 94 | uint32_t uP1801Detect(AMDGPU *GPU, VRMController **VRMs) 95 | { 96 | uint8_t Addresses[3] = { 0x51, 0x52, 0x53 }; 97 | uint32_t DevicesFound = 0; 98 | VRMController *CurrentVRM; 99 | 100 | for(int i = 0; i < 3; ++i) 101 | { 102 | AMDGPUI2CInit(GPU, STATIC_I2C_LINE_FIXME, Addresses[i]); 103 | 104 | if(AMDI2CReadByte(GPU, UP1801_CHIP_ID_REG, NULL) == UP1801_CHIP_ID_VALUE) 105 | { 106 | if(!*VRMs) 107 | { 108 | CurrentVRM = *VRMs = (VRMController *)calloc(1, sizeof(VRMController)); 109 | } 110 | else 111 | { 112 | for(CurrentVRM = *VRMs; CurrentVRM->next; CurrentVRM = CurrentVRM->next); 113 | CurrentVRM = CurrentVRM->next = (VRMController *)calloc(1, sizeof(VRMController)); 114 | } 115 | 116 | CurrentVRM->ParentGPU = GPU; 117 | CurrentVRM->VRMType = VRM_CONTROLLER_TYPE_UP1801; 118 | CurrentVRM->Capabilities = 0; 119 | 120 | CurrentVRM->OutputCount = 1; 121 | CurrentVRM->SelectedOutput = 0; 122 | 123 | CurrentVRM->I2CAddressList[0] = Addresses[i]; 124 | 125 | CurrentVRM->GetVoltage = uP1801GetVoltage; 126 | CurrentVRM->SetVoltage = uP1801SetVoltage; 127 | CurrentVRM->GetOutputIdx = uP1801GetOutputIdx; 128 | CurrentVRM->SetOutputIdx = uP1801SetOutputIdx; 129 | CurrentVRM->next = NULL; 130 | 131 | DevicesFound++; 132 | } 133 | } 134 | 135 | return(DevicesFound); 136 | } 137 | -------------------------------------------------------------------------------- /up1801.h: -------------------------------------------------------------------------------- 1 | #ifndef __UP1801_H 2 | #define __UP1801_H 3 | 4 | // NOTE: No support for programming initial voltage at this time 5 | // TODO/FIXME: Add current limit and VID1/VID0 readbacks 6 | // TODO/FIXME: MISC reg and its definitions 7 | 8 | #define UP1801_CHIP_ID_VALUE 0x12 9 | #define UP1801_ILIM_INIT_SETTING 0x00 10 | 11 | #define UP1801_ILIM_DISABLED_OUTPUT (1 << 5) 12 | #define UP1801_ILIM_PVI_SETTING (1 << 6) 13 | #define UP1801_ILIM_SVI_SETTING (1 << 7) 14 | 15 | #define UP1801_INI_REG 0x8B 16 | #define UP1801_CHIP_ID_REG 0xD0 17 | #define UP1801_SVI_REG 0xD2 18 | #define UP1801_ILIM_REG 0xD3 19 | #define UP1801_LEVEL1_REG 0xD4 20 | #define UP1801_LEVEL2_REG 0xD5 21 | #define UP1801_LEVEL3_REG 0xD6 22 | #define UP1801_LEVEL4_REG 0xD7 23 | 24 | 25 | uint32_t uP1801Detect(AMDGPU *GPU, VRMController **VRMs); 26 | uint32_t uP1801GetVoltage(VRMController *VRM, float *VDDC); 27 | uint32_t uP1801SetVoltage(VRMController *VRM, float Voltage); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /up9505.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "amdgpu.h" 6 | #include "amdi2c.h" 7 | #include "amdsmbus.h" 8 | #include "amdpmbus.h" 9 | #include "up9505.h" 10 | 11 | uint32_t uP9505GetVoltage(VRMController *VRM, float *VDDC) 12 | { 13 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 14 | 15 | uint8_t VID = AMDI2CReadByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? UP9505_VFB_REG : UP9505_VFBA_REG, NULL); 16 | *VDDC = (VID >= 0xF7) ? 0.0 : 1.55 - ((float)VID * 0.00625); 17 | 18 | return(VRM_ERROR_SUCCESS); 19 | } 20 | 21 | uint32_t uP9505SetVoltage(VRMController *VRM, float Voltage) 22 | { 23 | uint8_t VID; 24 | 25 | if(Voltage > 1.55 || Voltage < 0.0) return(VRM_ERROR_RANGE); 26 | 27 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 28 | 29 | VID = (uint8_t)(((1.55 - Voltage) / 0.00625) + 0.5); 30 | 31 | AMDI2CWriteByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? UP9505_LCHVID_REG : UP9505_ALCHVID, VID); 32 | 33 | return(VRM_ERROR_SUCCESS); 34 | } 35 | 36 | // In the offset, it is NOT two's complement, but instead, 37 | // bit 7 is sign, and the rest are as if it was signed. 38 | // 6.25mV steps - 10000000b is -0mV, 11111000 is -750mV, 39 | // 01111000 is +750mV, 00000001 is +6.25mV, and so on. 40 | // No matter what you put, it won't go higher than +750mV, 41 | // or lower than -750mV - 11111111b is still -750mV. 42 | 43 | // Right now - just set them all. 44 | uint32_t uP9505SetOffset(VRMController *VRM, float Voltage) 45 | { 46 | if((Voltage >= VRM->MaxOffset) || (Voltage <= VRM->MinOffset)) return(VRM_ERROR_RANGE); 47 | 48 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 49 | 50 | uint8_t VDDOffset = (Voltage > 0) ? (Voltage / 0.00625) + 0.5 : (Voltage / 0.00625) - 0.5; 51 | 52 | if(VDDOffset & 0x80) 53 | { 54 | VDDOffset = -VDDOffset; 55 | VDDOffset |= 0x80; 56 | } 57 | 58 | if(!VRM->SelectedOutput) 59 | { 60 | AMDI2CWriteByte(VRM->ParentGPU, UP9505_VOFS0_REG, VDDOffset); 61 | AMDI2CWriteByte(VRM->ParentGPU, UP9505_VOFS1_REG, VDDOffset); 62 | AMDI2CWriteByte(VRM->ParentGPU, UP9505_VOFS2_REG, VDDOffset); 63 | AMDI2CWriteByte(VRM->ParentGPU, UP9505_VOFS3_REG, VDDOffset); 64 | AMDI2CWriteByte(VRM->ParentGPU, UP9505_VOFS4_REG, VDDOffset); 65 | AMDI2CWriteByte(VRM->ParentGPU, UP9505_VOFS5_REG, VDDOffset); 66 | } 67 | else 68 | { 69 | AMDI2CWriteByte(VRM->ParentGPU, UP9505_AVOFS0_REG, VDDOffset); 70 | AMDI2CWriteByte(VRM->ParentGPU, UP9505_AVOFS1_REG, VDDOffset); 71 | } 72 | 73 | return(VRM_ERROR_SUCCESS); 74 | } 75 | 76 | // Since we set them all... just get the first. 77 | uint32_t uP9505GetOffset(VRMController *VRM, float *VDDCOff) 78 | { 79 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 80 | 81 | uint8_t VID = AMDI2CReadByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? UP9505_VOFS0_REG : UP9505_AVOFS0_REG, NULL); 82 | 83 | *VDDCOff = (VID & 0x80) ? -((VID & 0x7F) * 0.00625) : VID * 0.00625; 84 | 85 | return(VRM_ERROR_SUCCESS); 86 | } 87 | 88 | uint32_t uP9505GetOutputIdx(VRMController *VRM, uint32_t *Idx) 89 | { 90 | *Idx = VRM->SelectedOutput; 91 | return(VRM_ERROR_SUCCESS); 92 | } 93 | 94 | uint32_t uP9505SetOutputIdx(VRMController *VRM, uint32_t Idx) 95 | { 96 | if(Idx >= VRM->OutputCount) return(VRM_ERROR_RANGE); 97 | 98 | VRM->SelectedOutput = Idx; 99 | return(VRM_ERROR_SUCCESS); 100 | } 101 | 102 | // TODO/FIXME: Replace magic numbers with macros, and some 103 | // comments on the register layouts for MISC1 & MISC2 104 | uint32_t uP9505Acquire(VRMController *VRM) 105 | { 106 | uint8_t MISC1, MISC2; 107 | 108 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 109 | 110 | // Clear OFS control 2, so SVI has ZERO say 111 | MISC1 = AMDI2CReadByte(VRM->ParentGPU, UP9505_MISC1_REG, NULL) & 0x7F; 112 | MISC2 = AMDI2CReadByte(VRM->ParentGPU, UP9505_MISC2_REG, NULL); 113 | 114 | AMDI2CWriteByte(VRM->ParentGPU, UP9505_MISC1_REG, MISC1 | 0x70); 115 | AMDI2CWriteByte(VRM->ParentGPU, UP9505_MISC2_REG, MISC2 | 0x80); 116 | 117 | return(VRM_ERROR_SUCCESS); 118 | } 119 | 120 | // TODO/FIXME: Replace magic numbers with macros, and some 121 | // comments on the register layouts for MISC1 & MISC2 122 | 123 | // WARNING: NOT IMPLEMENTED RIGHT. 124 | uint32_t uP9505Release(VRMController *VRM) 125 | { 126 | uint8_t MISC1, MISC2; 127 | 128 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 129 | 130 | MISC1 = AMDI2CReadByte(VRM->ParentGPU, UP9505_MISC1_REG, NULL); 131 | MISC2 = AMDI2CReadByte(VRM->ParentGPU, UP9505_MISC2_REG, NULL); 132 | 133 | AMDI2CWriteByte(VRM->ParentGPU, UP9505_MISC1_REG, MISC1 & 0x8F); 134 | AMDI2CWriteByte(VRM->ParentGPU, UP9505_MISC2_REG, MISC2 & 0x7F); 135 | 136 | return(VRM_ERROR_SUCCESS); 137 | } 138 | 139 | uint32_t uP9505GetTemp(VRMController *VRM, uint32_t *Temp) 140 | { 141 | AMDGPUI2CInit(VRM->ParentGPU, STATIC_I2C_LINE_FIXME, VRM->I2CAddressList[0]); 142 | *Temp = (uint32_t)AMDSMBusReadByte(VRM->ParentGPU, (!VRM->SelectedOutput) ? UP9505_TM_REG : UP9505_ATM_REG, NULL); 143 | 144 | return(VRM_ERROR_SUCCESS); 145 | } 146 | 147 | // uP9505 can only reside at addresses 0x44 or 0x46 148 | // Returns number of devices found. 149 | uint32_t uP9505Detect(AMDGPU *GPU, VRMController **VRMs) 150 | { 151 | uint8_t byte; 152 | uint32_t DevicesFound = 0; 153 | VRMController *CurrentVRM; 154 | 155 | for(int i = 0; i < 2; ++i) 156 | { 157 | AMDGPUI2CInit(GPU, STATIC_I2C_LINE_FIXME, (i) ? 0x46 : 0x44); 158 | 159 | if(AMDI2CReadByte(GPU, UP9505_CHIP_ID_REG, NULL) == UP9505_CHIP_ID_VALUE) 160 | { 161 | if(!*VRMs) 162 | { 163 | CurrentVRM = *VRMs = (VRMController *)calloc(1, sizeof(VRMController)); 164 | } 165 | else 166 | { 167 | for(CurrentVRM = *VRMs; CurrentVRM->next; CurrentVRM = CurrentVRM->next); 168 | CurrentVRM = CurrentVRM->next = (VRMController *)calloc(1, sizeof(VRMController)); 169 | } 170 | 171 | CurrentVRM->ParentGPU = GPU; 172 | CurrentVRM->VRMType = VRM_CONTROLLER_TYPE_UP9505; 173 | CurrentVRM->Capabilities = VRM_CAPABILITY_OFFSET | VRM_CAPABILITY_TEMP; 174 | 175 | CurrentVRM->MinOffset = -0.75; 176 | CurrentVRM->MaxOffset = 0.75; 177 | 178 | CurrentVRM->OutputCount = 2; 179 | CurrentVRM->SelectedOutput = 0; 180 | 181 | CurrentVRM->I2CAddressList[0] = (i) ? 0x46 : 0x44; 182 | 183 | CurrentVRM->GetVoltage = uP9505GetVoltage; 184 | CurrentVRM->SetVoltage = uP9505SetVoltage; 185 | CurrentVRM->GetVoltageOffset = uP9505GetOffset; 186 | CurrentVRM->SetVoltageOffset = uP9505SetOffset; 187 | CurrentVRM->GetOutputIdx = uP9505GetOutputIdx; 188 | CurrentVRM->SetOutputIdx = uP9505SetOutputIdx; 189 | CurrentVRM->GetTemp = uP9505GetTemp; 190 | 191 | CurrentVRM->next = NULL; 192 | DevicesFound++; 193 | } 194 | } 195 | 196 | return(DevicesFound); 197 | 198 | } 199 | 200 | int uP9505SetVDDLoadLineAllPhases(AMDGPU *GPU, uint8_t Setting) 201 | { 202 | int ret; 203 | 204 | // LL setting is a 4-bit value, each register controls 205 | // two phases - duplicate the low nibble. 206 | Setting &= 0x0F; 207 | Setting = (Setting << 4) | Setting; 208 | 209 | ret = AMDI2CWriteByte(GPU, UP9505_IICLL01_REG, Setting); 210 | ret |= AMDI2CWriteByte(GPU, UP9505_IICLL23_REG, Setting); 211 | ret |= AMDI2CWriteByte(GPU, UP9505_IICLL45_REG, Setting); 212 | 213 | return(ret); 214 | } 215 | 216 | -------------------------------------------------------------------------------- /up9505.h: -------------------------------------------------------------------------------- 1 | #ifndef __UP9505_H 2 | #define __UP9505_H 3 | 4 | #define UP9505_CHIP_ID_VALUE 0x29 5 | 6 | #define UP9505_VOFS0_REG 0x0C 7 | #define UP9505_VOFS1_REG 0x0D 8 | #define UP9505_VOFS2_REG 0x0E 9 | #define UP9505_VOFS3_REG 0x0F 10 | #define UP9505_VOFS4_REG 0x10 11 | #define UP9505_VOFS5_REG 0x11 12 | #define UP9505_IICLL01_REG 0x15 13 | #define UP9505_IICLL23_REG 0x16 14 | #define UP9505_IICLL45_REG 0x17 15 | #define UP9505_LCHVID_REG 0x20 16 | #define UP9505_IMON_REG 0x21 17 | #define UP9505_VFB_REG 0x22 18 | #define UP9505_MISC1_REG 0x27 19 | #define UP9505_MISC2_REG 0x28 20 | #define UP9505_TM_REG 0x2F 21 | #define UP9505_AVOFS0_REG 0x31 22 | #define UP9505_AVOFS1_REG 0x32 23 | #define UP9505_IMONA_REG 0x3A 24 | #define UP9505_VFBA_REG 0x3B 25 | #define UP9505_ALCHVID 0x3F 26 | #define UP9505_ATM_REG 0x45 27 | #define UP9505_VERSION_ID_REG 0x49 28 | #define UP9505_CHIP_ID_REG 0x4A 29 | 30 | uint32_t uP9505Acquire(VRMController *VRM); 31 | uint32_t uP9505Detect(AMDGPU *GPU, VRMController **VRMs); 32 | int uP9505SetVDDLoadLineAllPhases(AMDGPU *GPU, uint8_t Setting); 33 | #endif 34 | -------------------------------------------------------------------------------- /vbios-tables.h: -------------------------------------------------------------------------------- 1 | #ifndef __VBIOS_TABLES_H 2 | #define __VBIOS_TABLES_H 3 | 4 | #include 5 | 6 | #define OFFSET_TO_POINTER_TO_ATOM_ROM_HEADER 0x00000048L 7 | 8 | typedef struct _ATOM_COMMON_TABLE_HEADER 9 | { 10 | uint16_t usStructureSize; 11 | uint8_t ucTableFormatRevision; /*Change it when the Parser is not backward compatible */ 12 | uint8_t ucTableContentRevision; /*Change it only when the table needs to change but the firmware */ 13 | /*Image can't be updated, while Driver needs to carry the new table! */ 14 | }ATOM_COMMON_TABLE_HEADER; 15 | 16 | /****************************************************************************/ 17 | // Structure stores the ROM header. 18 | /****************************************************************************/ 19 | typedef struct _ATOM_ROM_HEADER 20 | { 21 | ATOM_COMMON_TABLE_HEADER sHeader; 22 | uint8_t uaFirmWareSignature[4]; /*Signature to distinguish between Atombios and non-atombios, 23 | atombios should init it as "ATOM", don't change the position */ 24 | uint16_t usBiosRuntimeSegmentAddress; 25 | uint16_t usProtectedModeInfoOffset; 26 | uint16_t usConfigFilenameOffset; 27 | uint16_t usCRC_BlockOffset; 28 | uint16_t usBIOS_BootupMessageOffset; 29 | uint16_t usInt10Offset; 30 | uint16_t usPciBusDevInitCode; 31 | uint16_t usIoBaseAddress; 32 | uint16_t usSubsystemVendorID; 33 | uint16_t usSubsystemID; 34 | uint16_t usPCI_InfoOffset; 35 | uint16_t usMasterCommandTableOffset; /*Offset for SW to get all command table offsets, Don't change the position */ 36 | uint16_t usMasterDataTableOffset; /*Offset for SW to get all data table offsets, Don't change the position */ 37 | uint8_t ucExtendedFunctionCode; 38 | uint8_t ucReserved; 39 | }ATOM_ROM_HEADER; 40 | 41 | typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES 42 | { 43 | uint16_t ASIC_Init; //Function Table, used by various SW components,latest version 1.1 44 | uint16_t GetDisplaySurfaceSize; //Atomic Table, Used by Bios when enabling HW ICON 45 | uint16_t ASIC_RegistersInit; //Atomic Table, indirectly used by various SW components,called from ASIC_Init 46 | uint16_t VRAM_BlockVenderDetection; //Atomic Table, used only by Bios 47 | uint16_t DIGxEncoderControl; //Only used by Bios 48 | uint16_t MemoryControllerInit; //Atomic Table, indirectly used by various SW components,called from ASIC_Init 49 | uint16_t EnableCRTCMemReq; //Function Table,directly used by various SW components,latest version 2.1 50 | uint16_t MemoryParamAdjust; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock if needed 51 | uint16_t DVOEncoderControl; //Function Table,directly used by various SW components,latest version 1.2 52 | uint16_t GPIOPinControl; //Atomic Table, only used by Bios 53 | uint16_t SetEngineClock; //Function Table,directly used by various SW components,latest version 1.1 54 | uint16_t SetMemoryClock; //Function Table,directly used by various SW components,latest version 1.1 55 | uint16_t SetPixelClock; //Function Table,directly used by various SW components,latest version 1.2 56 | uint16_t DynamicClockGating; //Atomic Table, indirectly used by various SW components,called from ASIC_Init 57 | uint16_t ResetMemoryDLL; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock 58 | uint16_t ResetMemoryDevice; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock 59 | uint16_t MemoryPLLInit; 60 | uint16_t AdjustDisplayPll; //only used by Bios 61 | uint16_t AdjustMemoryController; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock 62 | uint16_t EnableASIC_StaticPwrMgt; //Atomic Table, only used by Bios 63 | uint16_t ASIC_StaticPwrMgtStatusChange; //Obsolete , only used by Bios 64 | uint16_t DAC_LoadDetection; //Atomic Table, directly used by various SW components,latest version 1.2 65 | uint16_t LVTMAEncoderControl; //Atomic Table,directly used by various SW components,latest version 1.3 66 | uint16_t LCD1OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 67 | uint16_t DAC1EncoderControl; //Atomic Table, directly used by various SW components,latest version 1.1 68 | uint16_t DAC2EncoderControl; //Atomic Table, directly used by various SW components,latest version 1.1 69 | uint16_t DVOOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 70 | uint16_t CV1OutputControl; //Atomic Table, Atomic Table, Obsolete from Ry6xx, use DAC2 Output instead 71 | uint16_t GetConditionalGoldenSetting; //only used by Bios 72 | uint16_t TVEncoderControl; //Function Table,directly used by various SW components,latest version 1.1 73 | uint16_t TMDSAEncoderControl; //Atomic Table, directly used by various SW components,latest version 1.3 74 | uint16_t LVDSEncoderControl; //Atomic Table, directly used by various SW components,latest version 1.3 75 | uint16_t TV1OutputControl; //Atomic Table, Obsolete from Ry6xx, use DAC2 Output instead 76 | uint16_t EnableScaler; //Atomic Table, used only by Bios 77 | uint16_t BlankCRTC; //Atomic Table, directly used by various SW components,latest version 1.1 78 | uint16_t EnableCRTC; //Atomic Table, directly used by various SW components,latest version 1.1 79 | uint16_t GetPixelClock; //Atomic Table, directly used by various SW components,latest version 1.1 80 | uint16_t EnableVGA_Render; //Function Table,directly used by various SW components,latest version 1.1 81 | uint16_t GetSCLKOverMCLKRatio; //Atomic Table, only used by Bios 82 | uint16_t SetCRTC_Timing; //Atomic Table, directly used by various SW components,latest version 1.1 83 | uint16_t SetCRTC_OverScan; //Atomic Table, used by various SW components,latest version 1.1 84 | uint16_t SetCRTC_Replication; //Atomic Table, used only by Bios 85 | uint16_t SelectCRTC_Source; //Atomic Table, directly used by various SW components,latest version 1.1 86 | uint16_t EnableGraphSurfaces; //Atomic Table, used only by Bios 87 | uint16_t UpdateCRTC_DoubleBufferRegisters; 88 | uint16_t LUT_AutoFill; //Atomic Table, only used by Bios 89 | uint16_t EnableHW_IconCursor; //Atomic Table, only used by Bios 90 | uint16_t GetMemoryClock; //Atomic Table, directly used by various SW components,latest version 1.1 91 | uint16_t GetEngineClock; //Atomic Table, directly used by various SW components,latest version 1.1 92 | uint16_t SetCRTC_UsingDTDTiming; //Atomic Table, directly used by various SW components,latest version 1.1 93 | uint16_t ExternalEncoderControl; //Atomic Table, directly used by various SW components,latest version 2.1 94 | uint16_t LVTMAOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 95 | uint16_t VRAM_BlockDetectionByStrap; //Atomic Table, used only by Bios 96 | uint16_t MemoryCleanUp; //Atomic Table, only used by Bios 97 | uint16_t ProcessI2cChannelTransaction; //Function Table,only used by Bios 98 | uint16_t WriteOneByteToHWAssistedI2C; //Function Table,indirectly used by various SW components 99 | uint16_t ReadHWAssistedI2CStatus; //Atomic Table, indirectly used by various SW components 100 | uint16_t SpeedFanControl; //Function Table,indirectly used by various SW components,called from ASIC_Init 101 | uint16_t PowerConnectorDetection; //Atomic Table, directly used by various SW components,latest version 1.1 102 | uint16_t MC_Synchronization; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock 103 | uint16_t ComputeMemoryEnginePLL; //Atomic Table, indirectly used by various SW components,called from SetMemory/EngineClock 104 | uint16_t MemoryRefreshConversion; //Atomic Table, indirectly used by various SW components,called from SetMemory or SetEngineClock 105 | uint16_t VRAM_GetCurrentInfoBlock; //Atomic Table, used only by Bios 106 | uint16_t DynamicMemorySettings; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock 107 | uint16_t MemoryTraining; //Atomic Table, used only by Bios 108 | uint16_t EnableSpreadSpectrumOnPPLL; //Atomic Table, directly used by various SW components,latest version 1.2 109 | uint16_t TMDSAOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 110 | uint16_t SetVoltage; //Function Table,directly and/or indirectly used by various SW components,latest version 1.1 111 | uint16_t DAC1OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 112 | uint16_t DAC2OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 113 | uint16_t SetupHWAssistedI2CStatus; //Function Table,only used by Bios, obsolete soon.Switch to use "ReadEDIDFromHWAssistedI2C" 114 | uint16_t ClockSource; //Atomic Table, indirectly used by various SW components,called from ASIC_Init 115 | uint16_t MemoryDeviceInit; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock 116 | uint16_t EnableYUV; //Atomic Table, indirectly used by various SW components,called from EnableVGARender 117 | uint16_t DIG1EncoderControl; //Atomic Table,directly used by various SW components,latest version 1.1 118 | uint16_t DIG2EncoderControl; //Atomic Table,directly used by various SW components,latest version 1.1 119 | uint16_t DIG1TransmitterControl; //Atomic Table,directly used by various SW components,latest version 1.1 120 | uint16_t DIG2TransmitterControl; //Atomic Table,directly used by various SW components,latest version 1.1 121 | uint16_t ProcessAuxChannelTransaction; //Function Table,only used by Bios 122 | uint16_t DPEncoderService; //Function Table,only used by Bios 123 | uint16_t GetVoltageInfo; 124 | }ATOM_MASTER_LIST_OF_COMMAND_TABLES; 125 | 126 | typedef struct _ATOM_MASTER_LIST_OF_DATA_TABLES 127 | { 128 | uint16_t UtilityPipeLine; // Offest for the utility to get parser info,Don't change this position! 129 | uint16_t MultimediaCapabilityInfo; // Only used by MM Lib,latest version 1.1, not configuable from Bios, need to include the table to build Bios 130 | uint16_t MultimediaConfigInfo; // Only used by MM Lib,latest version 2.1, not configuable from Bios, need to include the table to build Bios 131 | uint16_t StandardVESA_Timing; // Only used by Bios 132 | uint16_t FirmwareInfo; // Shared by various SW components,latest version 1.4 133 | uint16_t DAC_Info; // Will be obsolete from R600 134 | uint16_t LCD_Info; // Shared by various SW components,latest version 1.3, was called LVDS_Info 135 | uint16_t TMDS_Info; // Will be obsolete from R600 136 | uint16_t AnalogTV_Info; // Shared by various SW components,latest version 1.1 137 | uint16_t SupportedDevicesInfo; // Will be obsolete from R600 138 | uint16_t GPIO_I2C_Info; // Shared by various SW components,latest version 1.2 will be used from R600 139 | uint16_t VRAM_UsageByFirmware; // Shared by various SW components,latest version 1.3 will be used from R600 140 | uint16_t GPIO_Pin_LUT; // Shared by various SW components,latest version 1.1 141 | uint16_t VESA_ToInternalModeLUT; // Only used by Bios 142 | uint16_t ComponentVideoInfo; // Shared by various SW components,latest version 2.1 will be used from R600 143 | uint16_t PowerPlayInfo; // Shared by various SW components,latest version 2.1,new design from R600 144 | uint16_t CompassionateData; // Will be obsolete from R600 145 | uint16_t SaveRestoreInfo; // Only used by Bios 146 | uint16_t PPLL_SS_Info; // Shared by various SW components,latest version 1.2, used to call SS_Info, change to new name because of int ASIC SS info 147 | uint16_t OemInfo; // Defined and used by external SW, should be obsolete soon 148 | uint16_t XTMDS_Info; // Will be obsolete from R600 149 | uint16_t MclkSS_Info; // Shared by various SW components,latest version 1.1, only enabled when ext SS chip is used 150 | uint16_t Object_Header; // Shared by various SW components,latest version 1.1 151 | uint16_t IndirectIOAccess; // Only used by Bios,this table position can't change at all!! 152 | uint16_t MC_InitParameter; // Only used by command table 153 | uint16_t ASIC_VDDC_Info; // Will be obsolete from R600 154 | uint16_t ASIC_InternalSS_Info; // New tabel name from R600, used to be called "ASIC_MVDDC_Info" 155 | uint16_t TV_VideoMode; // Only used by command table 156 | uint16_t VRAM_Info; // Only used by command table, latest version 1.3 157 | uint16_t MemoryTrainingInfo; // Used for VBIOS and Diag utility for memory training purpose since R600. the new table rev start from 2.1 158 | uint16_t IntegratedSystemInfo; // Shared by various SW components 159 | uint16_t ASIC_ProfilingInfo; // New table name from R600, used to be called "ASIC_VDDCI_Info" for pre-R600 160 | uint16_t VoltageObjectInfo; // Shared by various SW components, latest version 1.1 161 | uint16_t PowerSourceInfo; // Shared by various SW components, latest versoin 1.1 162 | uint16_t ServiceInfo; 163 | }ATOM_MASTER_LIST_OF_DATA_TABLES; 164 | 165 | typedef struct _ATOM_MASTER_COMMAND_TABLE 166 | { 167 | ATOM_COMMON_TABLE_HEADER sHeader; 168 | ATOM_MASTER_LIST_OF_COMMAND_TABLES ListOfCommandTables; 169 | }ATOM_MASTER_COMMAND_TABLE; 170 | 171 | typedef struct _ATOM_MASTER_DATA_TABLE 172 | { 173 | ATOM_COMMON_TABLE_HEADER sHeader; 174 | ATOM_MASTER_LIST_OF_DATA_TABLES ListOfDataTables; 175 | }ATOM_MASTER_DATA_TABLE; 176 | 177 | #endif 178 | -------------------------------------------------------------------------------- /vbios.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "amdgpu.h" 8 | #include "vbios-tables.h" 9 | 10 | // If this fails, GPU->VBIOS should be NULL, and VBIOSSize should be zero. 11 | void GetVBIOSImage(AMDGPU *GPU, uint32_t ROMSize, uint32_t ROMBase) 12 | { 13 | void *VBIOSImg; 14 | int fd = open("/dev/mem", O_RDWR | O_SYNC); 15 | 16 | if(fd == -1) return; 17 | 18 | VBIOSImg = (void *)mmap(NULL, ROMSize, PROT_READ, MAP_PRIVATE, fd, ROMBase); 19 | 20 | if(VBIOSImg == MAP_FAILED) 21 | { 22 | close(fd); 23 | return; 24 | } 25 | 26 | GPU->VBIOSSize = ROMSize; 27 | GPU->VBIOS = malloc(ROMSize); 28 | memcpy(GPU->VBIOS, VBIOSImg, ROMSize); 29 | 30 | munmap(VBIOSImg, ROMSize); 31 | close(fd); 32 | } 33 | 34 | // TODO: This structure needs to be adapted for ALL entry types, as well as moved somewhere else. 35 | // TODO: We should also care about the format and content revisions... 36 | typedef struct _VOIEntry 37 | { 38 | uint8_t VoltageType; 39 | uint8_t VoltageMode; 40 | uint16_t Size; 41 | uint8_t RegulatorID; 42 | uint8_t I2CLine; 43 | uint8_t I2CAddress; 44 | uint8_t ControlOffset; 45 | uint8_t VoltageControlFlag; 46 | uint8_t Reserved[3]; 47 | } VOIEntry; 48 | 49 | // TODO: Extend this to allow for multiple controllers, etc 50 | // The information this provides should stay zero in the case this fails 51 | void GetI2CInfo(AMDGPU *GPU) 52 | { 53 | uint16_t VOISize, Position = 0; 54 | 55 | if(!GPU->VBIOS) return; 56 | 57 | ATOM_ROM_HEADER *hdr = GPU->VBIOS + ((uint16_t *)(GPU->VBIOS + OFFSET_TO_POINTER_TO_ATOM_ROM_HEADER))[0]; 58 | ATOM_MASTER_LIST_OF_DATA_TABLES *DataTblList = &((ATOM_MASTER_DATA_TABLE *)(GPU->VBIOS + hdr->usMasterDataTableOffset))->ListOfDataTables; 59 | 60 | VOISize = *((uint16_t *)(GPU->VBIOS + DataTblList->VoltageObjectInfo)); 61 | VOIEntry *Entry = (VOIEntry *)(GPU->VBIOS + DataTblList->VoltageObjectInfo + 4); 62 | 63 | while(Position < VOISize) 64 | { 65 | if(Entry->VoltageMode == 3) // INIT_REGULATOR 66 | { 67 | GPU->I2CLine = Entry->I2CLine; 68 | GPU->I2CAddress = Entry->I2CAddress >> 1; 69 | GPU->I2CControlOffset = Entry->ControlOffset; 70 | GPU->I2CHas16BitEntries = (bool)Entry->VoltageControlFlag; 71 | break; 72 | } 73 | 74 | Position += Entry->Size; 75 | Entry = (VOIEntry *)(((uint8_t *)Entry) + Entry->Size); 76 | } 77 | } 78 | 79 | -------------------------------------------------------------------------------- /vbios.h: -------------------------------------------------------------------------------- 1 | #ifndef __VBIOS_H 2 | #define __VBIOS_H 3 | 4 | void GetVBIOSImage(AMDGPU *GPU, uint32_t ROMSize, uint32_t ROMBase); 5 | void GetI2CInfo(AMDGPU *GPU); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /vrm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "amdgpu.h" 6 | #include "amdi2c.h" 7 | #include "amdsmbus.h" 8 | #include "amdpmbus.h" 9 | #include "ir3xxxx.h" 10 | #include "ncp81022.h" 11 | #include "up9505.h" 12 | #include "up1801.h" 13 | #include "rt8894a.h" 14 | #include "ir35217.h" 15 | #include "vrm.h" 16 | 17 | // Detect functions need to be rewritten to support 18 | // returning multiple VRMs of the same type! 19 | 20 | uint32_t DetectVRMControllers(AMDGPU *GPU) 21 | { 22 | // Free any old data, if existing. 23 | if(GPU->VRMs) AMDGPUFreeVRMs(GPU); 24 | 25 | GPU->VRMCount += IR356XXDetect(GPU, &GPU->VRMs); 26 | GPU->VRMCount += IR35217Detect(GPU, &GPU->VRMs); 27 | GPU->VRMCount += RT8894ADetect(GPU, &GPU->VRMs); 28 | GPU->VRMCount += NCP81022Detect(GPU, &GPU->VRMs); 29 | GPU->VRMCount += uP9505Detect(GPU, &GPU->VRMs); 30 | GPU->VRMCount += uP1801Detect(GPU, &GPU->VRMs); 31 | 32 | return(GPU->VRMCount); 33 | } 34 | 35 | static const char *VRMNames[8] = 36 | { 37 | "INVALID", 38 | "IR356XX", 39 | "uP6266", 40 | "uP9505", 41 | "NCP81022", 42 | "uP1801", 43 | "RT8894A", 44 | "IR35217" 45 | }; 46 | 47 | const char *GetVRMName(VRMController *VRM) 48 | { 49 | return(VRMNames[VRM->VRMType]); 50 | } 51 | -------------------------------------------------------------------------------- /vrm.h: -------------------------------------------------------------------------------- 1 | #ifndef __VRM_H 2 | #define __VRM_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct _VRMController VRMController; 8 | 9 | #define VRM_CONTROLLER_TYPE_INVALID 0x00 10 | #define VRM_CONTROLLER_TYPE_IR356XX 0x01 11 | #define VRM_CONTROLLER_TYPE_UP6266 0x02 12 | 13 | // This (probably) covers the uP9503P, uP9504, and uP9505 14 | #define VRM_CONTROLLER_TYPE_UP9505 0x03 15 | 16 | // NCP81022 17 | #define VRM_CONTROLLER_TYPE_NCP81022 0x04 18 | 19 | // uP1801, unlikely to be found alone, but usually a secondary controller 20 | #define VRM_CONTROLLER_TYPE_UP1801 0x05 21 | 22 | #define VRM_CONTROLLER_TYPE_RT8894A 0x06 23 | 24 | // The pain-in-the-ass IR35217, found on current Vega (codename Vega10) GPUs. 25 | #define VRM_CONTROLLER_TYPE_IR35217 0x07 26 | 27 | // VRM Capabilities - these are masks, of course 28 | 29 | // If offsets are supported, max/min offset is required. 30 | #define VRM_CAPABILITY_SET_VOLTAGE 0x01 31 | #define VRM_CAPABILITY_OFFSET 0x02 32 | #define VRM_CAPABILITY_TEMP 0x04 33 | #define VRM_CAPABILITY_CURRENT 0x08 34 | #define VRM_CAPABILITY_LOADLINE 0x10 35 | 36 | // VRM Errors - if an error occurs, the VRM controller driver code should 37 | // return a non-success (nonzero) value 38 | #define VRM_ERROR_SUCCESS 0x00 39 | #define VRM_ERROR_RANGE 0x01 // Input value out of range 40 | #define VRM_ERROR_I2C 0x02 // I2C failure 41 | #define VRM_ERROR_UNSUPPORTED 0x03 // Unsupported feature 42 | #define VRM_ERROR_UNKNOWN 0xFF 43 | 44 | struct _VRMController 45 | { 46 | // Filled in by caller of VRM init controller function, 47 | // that is, controller driver code 48 | AMDGPU *ParentGPU; 49 | 50 | uint8_t VRMType; 51 | uint32_t Capabilities; 52 | 53 | uint8_t OutputCount; 54 | uint8_t SelectedOutput; 55 | 56 | // TODO/FIXME: Min/max voltage entries 57 | float MaxOffset; 58 | float MinOffset; 59 | 60 | uint8_t I2CAddressList[8]; // This had better be enough, I don't want dynamic allocation in here. 61 | uint8_t Model; 62 | 63 | // Required. 64 | uint32_t (*GetVoltage)(struct _VRMController *VRM, float *VDDC); 65 | 66 | // MUST not be NULL if VRM_CAPABILITY_SET_VOLTAGE is supported. 67 | uint32_t (*SetVoltage)(struct _VRMController *VRM, float VDDC); 68 | 69 | // Required to support - even if they are no-ops. 70 | uint32_t (*GetOutputIdx)(struct _VRMController *VRM, uint32_t *idx); 71 | uint32_t (*SetOutputIdx)(struct _VRMController *VRM, uint32_t idx); 72 | 73 | // These two MUST be supported if offset and/or negative offset 74 | // support is signaled in the capabilities. 75 | 76 | // They MUST get the offset of the currently selected output. 77 | // If the output selected does not support it (but another does) 78 | // these functions must return VRM_ERROR_UNSUPPORTED. 79 | uint32_t (*GetVoltageOffset)(struct _VRMController *VRM, float *VDDCOff); 80 | uint32_t (*SetVoltageOffset)(struct _VRMController *VRM, float VDDCOff); 81 | 82 | // This MUST be supported if temperature support is signaled 83 | // in the capabilities dword. 84 | uint32_t (*GetTemp)(struct _VRMController *VRM, uint32_t *Temp); 85 | 86 | // MUST be supported if VRM_CAPABILITY_CURRENT is signaled 87 | uint32_t (*GetCurrent)(struct _VRMController *VRM, float *Current); 88 | 89 | // MUST be supported if VRM_CAPABILITY_LOADLINE is signaled 90 | uint32_t (*SetLoadLine)(struct _VRMController *VRM, uint8_t LoadLine); 91 | 92 | // Pointer to next in list 93 | struct _VRMController *next; 94 | }; 95 | 96 | uint32_t DetectVRMControllers(AMDGPU *GPU); 97 | const char *GetVRMName(VRMController *VRM); 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /wolfamdbg-args.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "wolfamdbg.h" 8 | 9 | void PrintUsage(char *BinName) 10 | { 11 | printf("Usage: %s [Selection Options] \n", BinName); 12 | printf("Selection Options:\n"); 13 | printf("\t-i \n\t-b (0 is HWI2C, 1-6 is DDC1-6, and 7 is DDCVGA - default 0.)\n"); 14 | printf("Action Options (one must be specified, and only one):\n"); 15 | printf("\t--scan\n\t--dump \n\t--dumpsmbus \n\t--dumpword \n\t--dumpwordsmbus \n"); 16 | printf("\t--read \n\t--readsmbus \n"); 17 | printf("\t--readword \n\t--readwordsmbus \n"); 18 | printf("\t--write \n\t--writeword \n"); 19 | printf("\nA valid byte input ranges 0x00 - 0xFF, valid word inputs from 0x0000 - 0xFFFF.\n"); 20 | printf("Valid I2C addresses are 0x08 - 0x77. All ranges specified are inclusive.\n"); 21 | } 22 | 23 | #define NEXT_ARG_CHECK() do { if(i == (argc - 1)) { printf("Argument \"%s\" requires a parameter.\n", argv[i]); PrintUsage(argv[0]); return(false); } } while(0) 24 | #define NEXT_TWO_ARGS_CHECK() do { if((i == (argc - 1)) || (i == (argc - 2))) { printf("Argument \"%s\" requires two parameters.\n", argv[i]); PrintUsage(argv[0]); return(false); } } while(0) 25 | #define NEXT_THREE_ARGS_CHECK() do { if((i == (argc - 1)) || (i == (argc - 2)) || (i == (argc - 3))) { printf("Argument \"%s\" requires three parameters.\n", argv[i]); PrintUsage(argv[0]); return(false); } } while(0) 26 | #define VALID_ADDR(in) (((in) > 0x03) && ((in) < 0x78)) 27 | #define VALID_BYTE(in) (((in) >= 0x00) && ((in) < 0x100)) 28 | #define VALID_WORD(in) (((in) >= 0x00) && ((in) < 0x10000)) 29 | #define CONVERT(val) ((((val)[0] == '0') && ((val)[1] == 'x')) ? ((uint32_t)strtoul((val) + 2, NULL, 16)) : ((uint32_t)strtoul((val), NULL, 16))) 30 | 31 | static bool SetAction(ArgsObj *Args, uint32_t NewAction) 32 | { 33 | if(Args->Action != ACTION_INVALID) 34 | { 35 | printf("Only one action may be used at a time.\n"); 36 | return(false); 37 | } 38 | 39 | Args->Action = NewAction; 40 | return(true); 41 | } 42 | 43 | bool ParseCmdLine(ArgsObj *Args, int argc, char **argv) 44 | { 45 | memset(Args, 0x00, sizeof(ArgsObj)); 46 | 47 | if(argc < 2) 48 | { 49 | PrintUsage(argv[0]); 50 | return(false); 51 | } 52 | 53 | for(int i = 1; i < argc; ++i) 54 | { 55 | if(!strcmp("-i", argv[i])) 56 | { 57 | NEXT_ARG_CHECK(); 58 | Args->GPUIdx = strtoul(argv[++i], NULL, 10); 59 | 60 | if(errno == EINVAL || errno == ERANGE || Args->GPUIdx > 15) 61 | { 62 | printf("Invalid GPU index specified.\n"); 63 | return(false); 64 | } 65 | 66 | Args->GPUIdxProvided = true; 67 | } 68 | else if(!strcmp("-b", argv[i])) 69 | { 70 | NEXT_ARG_CHECK(); 71 | Args->I2CBusIdx = strtoul(argv[++i], NULL, 10); 72 | 73 | if(errno == EINVAL || errno == ERANGE || Args->I2CBusIdx > 7U) 74 | { 75 | printf("Invalid I2C bus index specified.\n"); 76 | return(false); 77 | } 78 | 79 | Args->I2CBusIdxProvided = true; 80 | } 81 | else if(!strcmp("--scan", argv[i])) 82 | { 83 | if(!SetAction(Args, ACTION_SCAN)) return(false); 84 | } 85 | else if(!strcmp("--dump", argv[i])) 86 | { 87 | NEXT_ARG_CHECK(); 88 | 89 | // WARNING: Doing this in the macro invocation 90 | // will cause its duplication! 91 | ++i; 92 | Args->I2CAddress = CONVERT(argv[i]); 93 | 94 | if(!VALID_ADDR(Args->I2CAddress)) 95 | { 96 | printf("Invalid I2C address specified.\n"); 97 | return(false); 98 | } 99 | 100 | if(!SetAction(Args, ACTION_DUMP)) return(false); 101 | } 102 | else if(!strcmp("--dumpsmbus", argv[i])) 103 | { 104 | NEXT_ARG_CHECK(); 105 | 106 | // WARNING: Doing this in the macro invocation 107 | // will cause its duplication! 108 | ++i; 109 | Args->I2CAddress = CONVERT(argv[i]); 110 | 111 | if(!VALID_ADDR(Args->I2CAddress)) 112 | { 113 | printf("Invalid I2C address specified.\n"); 114 | return(false); 115 | } 116 | 117 | if(!SetAction(Args, ACTION_DUMP_SMBUS)) return(false); 118 | } 119 | else if(!strcmp("--dumpword", argv[i])) 120 | { 121 | NEXT_ARG_CHECK(); 122 | 123 | // WARNING: Doing this in the macro invocation 124 | // will cause its duplication! 125 | ++i; 126 | Args->I2CAddress = CONVERT(argv[i]); 127 | 128 | if(errno == EINVAL || errno == ERANGE || !VALID_ADDR(Args->I2CAddress)) 129 | { 130 | printf("Invalid I2C address specified.\n"); 131 | return(false); 132 | } 133 | 134 | Args->WordOperation = true; 135 | if(!SetAction(Args, ACTION_DUMP)) return(false); 136 | } 137 | else if(!strcmp("--dumpwordsmbus", argv[i])) 138 | { 139 | NEXT_ARG_CHECK(); 140 | 141 | // WARNING: Doing this in the macro invocation 142 | // will cause its duplication! 143 | ++i; 144 | Args->I2CAddress = CONVERT(argv[i]); 145 | 146 | if(!VALID_ADDR(Args->I2CAddress)) 147 | { 148 | printf("Invalid I2C address specified.\n"); 149 | return(false); 150 | } 151 | 152 | Args->WordOperation = true; 153 | if(!SetAction(Args, ACTION_DUMP_SMBUS)) return(false); 154 | } 155 | else if(!strcmp("--read", argv[i])) 156 | { 157 | NEXT_TWO_ARGS_CHECK(); 158 | 159 | // WARNING: Doing this in the macro invocation 160 | // will cause its duplication! 161 | ++i; 162 | 163 | Args->I2CAddress = CONVERT(argv[i]); 164 | 165 | if(errno == EINVAL || errno == ERANGE || !VALID_ADDR(Args->I2CAddress)) 166 | { 167 | printf("Invalid I2C address specified.\n"); 168 | return(false); 169 | } 170 | 171 | // See warning above. 172 | ++i; 173 | 174 | Args->I2CRegister = CONVERT(argv[i]); 175 | 176 | if(errno == EINVAL || errno == ERANGE || !VALID_BYTE(Args->I2CRegister)) 177 | { 178 | printf("Invalid I2C register specified.\n"); 179 | return(false); 180 | } 181 | 182 | if(!SetAction(Args, ACTION_READ)) return(false); 183 | } 184 | else if(!strcmp("--readsmbus", argv[i])) 185 | { 186 | NEXT_TWO_ARGS_CHECK(); 187 | 188 | // WARNING: Doing this in the macro invocation 189 | // will cause its duplication! 190 | ++i; 191 | 192 | Args->I2CAddress = CONVERT(argv[i]); 193 | 194 | if(errno == EINVAL || errno == ERANGE || !VALID_ADDR(Args->I2CAddress)) 195 | { 196 | printf("Invalid I2C address specified.\n"); 197 | return(false); 198 | } 199 | 200 | // See warning above. 201 | ++i; 202 | 203 | Args->I2CRegister = CONVERT(argv[i]); 204 | 205 | if(errno == EINVAL || errno == ERANGE || !VALID_BYTE(Args->I2CRegister)) 206 | { 207 | printf("Invalid I2C register specified.\n"); 208 | return(false); 209 | } 210 | 211 | if(!SetAction(Args, ACTION_READ_SMBUS)) return(false); 212 | } 213 | else if(!strcmp("--readword", argv[i])) 214 | { 215 | NEXT_TWO_ARGS_CHECK(); 216 | 217 | // WARNING: Doing this in the macro invocation 218 | // will cause its duplication! 219 | ++i; 220 | 221 | Args->I2CAddress = CONVERT(argv[i]); 222 | 223 | if(errno == EINVAL || errno == ERANGE || !VALID_ADDR(Args->I2CAddress)) 224 | { 225 | printf("Invalid I2C address specified.\n"); 226 | return(false); 227 | } 228 | 229 | // See warning above. 230 | ++i; 231 | 232 | Args->I2CRegister = CONVERT(argv[i]); 233 | 234 | if(errno == EINVAL || errno == ERANGE || !VALID_BYTE(Args->I2CRegister)) 235 | { 236 | printf("Invalid I2C register specified.\n"); 237 | return(false); 238 | } 239 | 240 | Args->WordOperation = true; 241 | if(!SetAction(Args, ACTION_READ)) return(false); 242 | } 243 | else if(!strcmp("--readwordsmbus", argv[i])) 244 | { 245 | NEXT_TWO_ARGS_CHECK(); 246 | 247 | // WARNING: Doing this in the macro invocation 248 | // will cause its duplication! 249 | ++i; 250 | 251 | Args->I2CAddress = CONVERT(argv[i]); 252 | 253 | if(errno == EINVAL || errno == ERANGE || !VALID_ADDR(Args->I2CAddress)) 254 | { 255 | printf("Invalid I2C address specified.\n"); 256 | return(false); 257 | } 258 | 259 | // See warning above. 260 | ++i; 261 | 262 | Args->I2CRegister = CONVERT(argv[i]); 263 | 264 | if(errno == EINVAL || errno == ERANGE || !VALID_BYTE(Args->I2CRegister)) 265 | { 266 | printf("Invalid I2C register specified.\n"); 267 | return(false); 268 | } 269 | 270 | Args->WordOperation = true; 271 | if(!SetAction(Args, ACTION_READ_SMBUS)) return(false); 272 | } 273 | else if(!strcmp("--write", argv[i])) 274 | { 275 | NEXT_THREE_ARGS_CHECK(); 276 | 277 | // WARNING: Doing this in the macro invocation 278 | // will cause its duplication! 279 | ++i; 280 | 281 | Args->I2CAddress = CONVERT(argv[i]); 282 | 283 | if(errno == EINVAL || errno == ERANGE || !VALID_ADDR(Args->I2CAddress)) 284 | { 285 | printf("Invalid I2C address specified.\n"); 286 | return(false); 287 | } 288 | 289 | // See warning above. 290 | ++i; 291 | 292 | Args->I2CRegister = CONVERT(argv[i]); 293 | 294 | if(errno == EINVAL || errno == ERANGE || !VALID_BYTE(Args->I2CRegister)) 295 | { 296 | printf("Invalid I2C register specified.\n"); 297 | return(false); 298 | } 299 | 300 | // See warning above. 301 | ++i; 302 | 303 | Args->WriteValue = CONVERT(argv[i]); 304 | 305 | if(errno == EINVAL || errno == ERANGE || !VALID_BYTE(Args->WriteValue)) 306 | { 307 | printf("Invalid value to write specified (must be a single byte.)\n"); 308 | return(false); 309 | } 310 | 311 | if(!SetAction(Args, ACTION_WRITE)) return(false); 312 | } 313 | else if(!strcmp("--writeword", argv[i])) 314 | { 315 | NEXT_THREE_ARGS_CHECK(); 316 | 317 | // WARNING: Doing this in the macro invocation 318 | // will cause its duplication! 319 | ++i; 320 | 321 | Args->I2CAddress = CONVERT(argv[i]); 322 | 323 | if(errno == EINVAL || errno == ERANGE || !VALID_ADDR(Args->I2CAddress)) 324 | { 325 | printf("Invalid I2C address specified.\n"); 326 | return(false); 327 | } 328 | 329 | // See warning above 330 | ++i; 331 | 332 | Args->I2CRegister = CONVERT(argv[i]); 333 | 334 | if(errno == EINVAL || errno == ERANGE || !VALID_BYTE(Args->I2CRegister)) 335 | { 336 | printf("Invalid I2C register specified.\n"); 337 | return(false); 338 | } 339 | 340 | // See warning above. 341 | ++i; 342 | 343 | Args->WriteValue = CONVERT(argv[i]); 344 | 345 | if(errno == EINVAL || errno == ERANGE || !VALID_WORD(Args->WriteValue)) 346 | { 347 | printf("Invalid value to write specified (must be a single byte.)\n"); 348 | return(false); 349 | } 350 | 351 | Args->WordOperation = true; 352 | if(!SetAction(Args, ACTION_WRITE)) return(false); 353 | } 354 | else 355 | { 356 | PrintUsage(argv[0]); 357 | printf("\nUnknown option: \"%s\"\n", argv[i]); 358 | return(false); 359 | } 360 | } 361 | 362 | // Now ensure there was an action to perform set... 363 | if(Args->Action == ACTION_INVALID) 364 | { 365 | printf("No action option set; nothing to do.\n"); 366 | PrintUsage(argv[0]); 367 | return(false); 368 | } 369 | 370 | return(true); 371 | } 372 | -------------------------------------------------------------------------------- /wolfamdbg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "amdgpu.h" 6 | #include "amdi2c.h" 7 | #include "amdi2cdbg.h" 8 | #include "amdsmbus.h" 9 | #include "amdpmbus.h" 10 | #include "wolfamdbg.h" 11 | 12 | #define CONVERT(val) ((val[0] == '0' && val[1] == 'x') ? strtoul(val + 2, NULL, 16) : strtoul(val, NULL, 16)) 13 | 14 | #define AD527XMAKEWORD(cmd, data) ((uint16_t)((((uint16_t)(cmd)) << 10)) | (((uint16_t)(data)) & 0x3FF)) 15 | #define AD527XWORD2CMD(val) (((val) >> 10) & 0xF) 16 | #define AD527XWORD2DATA(val) ((val) & 0x3FF) 17 | 18 | // Parameter len is the size in bytes of asciistr, meaning rawstr 19 | // must have (len >> 1) bytes allocated 20 | // Maybe asciistr just NULL terminated? 21 | // Returns length of rawstr in bytes 22 | int ASCIIHexToBinary(void *restrict rawstr, const char *restrict asciistr, size_t len) 23 | { 24 | for(int i = 0, j = 0; i < len; ++i) 25 | { 26 | char tmp = asciistr[i]; 27 | if(tmp < 'A') tmp -= '0'; 28 | else if(tmp < 'a') tmp = (tmp - 'A') + 10; 29 | else tmp = (tmp - 'a') + 10; 30 | 31 | if(i & 1) ((uint8_t *)rawstr)[j++] |= tmp & 0x0F; 32 | else ((uint8_t *)rawstr)[j] = tmp << 4; 33 | } 34 | 35 | return(len >> 1); 36 | } 37 | 38 | uint32_t IR3XXXMFR_READ_REG(AMDGPU *GPU, uint8_t I2CAddr, int *ret); 39 | 40 | #ifdef __linux__ 41 | HANDLE OpenDriverHandle(void) 42 | { 43 | return(INVALID_HANDLE_VALUE); 44 | } 45 | #elif defined(_WIN32) 46 | HANDLE OpenDriverHandle(void) 47 | { 48 | return(CreateFileA("\\\\.\\atillk64", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL)); 49 | } 50 | #endif 51 | 52 | int main(int argc, char **argv) 53 | { 54 | int Status; 55 | ArgsObj Config; 56 | uint32_t ret, outval; 57 | AMDGPU *GPUList, *CurGPU; 58 | HANDLE DrvHandle = INVALID_HANDLE_VALUE; 59 | 60 | printf("wolfamdbg %s\n", WOLFAMDBG_VERSION_STR); 61 | memset(&Config, 0x00, sizeof(Config)); 62 | 63 | if(!ParseCmdLine(&Config, argc, argv)) return(1); 64 | 65 | DrvHandle = OpenDriverHandle(); 66 | 67 | int GPUCount = FindAMDGPUs(&GPUList, DrvHandle); 68 | 69 | // Remember that we're counting from one, but they 70 | // are indexing from zero in this case. Adjusting 71 | // the value keeps things simpler. 72 | 73 | if(!GPUCount) 74 | { 75 | printf("Found no AMD GPUs at all.\n"); 76 | return(0); 77 | } 78 | 79 | GPUCount--; 80 | 81 | if(Config.GPUIdx > GPUCount) 82 | { 83 | printf("GPU index does not exist.\n"); 84 | ReleaseAMDGPUs(GPUList); 85 | return(2); 86 | } 87 | 88 | CurGPU = GPUList; 89 | for(int i = 0; i < Config.GPUIdx; ++i) CurGPU = CurGPU->next; 90 | 91 | Status = InitAMDGPUMMIO(CurGPU); 92 | 93 | if(Status < 0) 94 | { 95 | printf("Unable to initialize MMIO for GPU - failed with %d.\n", Status); 96 | return(-1); 97 | } 98 | 99 | switch(Config.Action) 100 | { 101 | case ACTION_SCAN: 102 | { 103 | bool DevicesFound[117] = { false }; 104 | 105 | for(uint8_t addr = 0x03; addr < 0x78; ++addr) 106 | { 107 | bool bad = true; 108 | uint8_t reg = 0x00; 109 | 110 | AMDGPUI2CInit(CurGPU, Config.I2CBusIdx, addr); 111 | printf("Scanning address 0x%02X.\n", addr); 112 | Status = 0; 113 | 114 | do 115 | { 116 | outval = (uint8_t)AMDI2CReadByte(CurGPU, reg, &Status); 117 | if(((Status & I2C_STATUS_MASK) & I2C_DONE) && (!((Status & I2C_STATUS_MASK) & I2C_NACK))) 118 | { 119 | bad = false; 120 | break; 121 | } 122 | } while(reg++ < 0xFF); 123 | 124 | if(bad) printf("Appears nothing interesting at 0x%02X. (Status: 0x%04X)\n", addr, Status); 125 | else printf("Address 0x%02X looks like it has something!\nOffset 0x%02X returned 0x%02X! (Status: 0x%04X)\n", addr, reg, (uint8_t)outval, Status); 126 | 127 | DevicesFound[addr - 0x03] = !bad; 128 | } 129 | 130 | printf("Summary - devices found:"); 131 | for(int i = 0; i < 117; ++i) 132 | { 133 | if(!(i & 0x0F)) putchar('\n'); 134 | if(DevicesFound[i]) printf("0x%02X ", i); 135 | } 136 | 137 | break; 138 | } 139 | case ACTION_DUMP: 140 | { 141 | AMDGPUI2CInit(CurGPU, Config.I2CBusIdx, Config.I2CAddress); 142 | 143 | for(uint16_t reg = 0x00; reg < 0x100; reg += 0x02) 144 | { 145 | if(Config.WordOperation) 146 | { 147 | if(!(reg & 0x0F)) putchar('\n'); 148 | 149 | outval = (uint16_t)AMDSMBusReadWord(CurGPU, reg, &Status); 150 | 151 | if(((Status & I2C_STATUS_MASK) & I2C_DONE) && (!((Status & I2C_STATUS_MASK) & I2C_NACK))) 152 | printf("0x%04X ", (uint16_t)outval); 153 | else 154 | printf("0xXXXX "); 155 | } 156 | else 157 | { 158 | if(!(reg & 0x0F)) putchar('\n'); 159 | 160 | outval = (uint8_t)AMDI2CReadByte(CurGPU, reg, &Status); 161 | 162 | if(((Status & I2C_STATUS_MASK) & I2C_DONE) && (!((Status & I2C_STATUS_MASK) & I2C_NACK))) 163 | printf("0x%02X ", (uint8_t)outval); 164 | else 165 | printf("0xXX (0x%04X)", Status); 166 | 167 | outval = (uint8_t)AMDI2CReadByte(CurGPU, reg + 0x01, &Status); 168 | 169 | if(((Status & I2C_STATUS_MASK) & I2C_DONE) && (!((Status & I2C_STATUS_MASK) & I2C_NACK))) 170 | printf("0x%02X ", (uint8_t)outval); 171 | else 172 | printf("0xXX (0x%04X)", Status); 173 | } 174 | 175 | } 176 | 177 | putchar('\n'); 178 | break; 179 | } 180 | case ACTION_DUMP_SMBUS: 181 | { 182 | AMDGPUI2CInit(CurGPU, Config.I2CBusIdx, Config.I2CAddress); 183 | 184 | for(uint16_t reg = 0x00; reg < 0x100; reg += 0x02) 185 | { 186 | if(Config.WordOperation) 187 | { 188 | if(!(reg & 0x0F)) putchar('\n'); 189 | 190 | outval = (uint16_t)AMDSMBusReadWord(CurGPU, reg, &Status); 191 | 192 | if(((Status & I2C_STATUS_MASK) & I2C_DONE) && (!((Status & I2C_STATUS_MASK) & I2C_NACK))) 193 | printf("0x%04X ", (uint16_t)outval); 194 | else 195 | printf("0xXXXX "); 196 | } 197 | else 198 | { 199 | if(!(reg & 0x0F)) putchar('\n'); 200 | 201 | outval = (uint8_t)AMDSMBusReadByte(CurGPU, reg, &Status); 202 | 203 | if(((Status & I2C_STATUS_MASK) & I2C_DONE) && (!((Status & I2C_STATUS_MASK) & I2C_NACK))) 204 | printf("0x%02X ", (uint8_t)outval); 205 | else 206 | printf("0xXX "); 207 | 208 | // Second read in the same loop iter - keeping pace with the version for word-sized reads 209 | outval = (uint8_t)AMDSMBusReadByte(CurGPU, reg + 0x01, &Status); 210 | 211 | if(((Status & I2C_STATUS_MASK) & I2C_DONE) && (!((Status & I2C_STATUS_MASK) & I2C_NACK))) 212 | printf("0x%02X ", (uint8_t)outval); 213 | else 214 | printf("0xXX "); 215 | } 216 | 217 | } 218 | 219 | putchar('\n'); 220 | break; 221 | } 222 | case ACTION_READ: 223 | { 224 | uint8_t reg = Config.I2CRegister; 225 | AMDGPUI2CInit(CurGPU, Config.I2CBusIdx, Config.I2CAddress); 226 | 227 | /*if(Config.WordOperation) 228 | { 229 | outval = (uint16_t)AMDI2CReadWord(CurGPU, reg, &Status); 230 | printf("Returned 0x%04X (status 0x%04X).\n", (uint16_t)outval, Status); 231 | } 232 | else 233 | { 234 | outval = (uint8_t)AMDI2CReadByte(CurGPU, reg, &Status); 235 | printf("Returned 0x%02X (status 0x%04X).\n", (uint8_t)outval, Status); 236 | }*/ 237 | break; 238 | } 239 | 240 | case ACTION_READ_SMBUS: 241 | { 242 | uint8_t reg = Config.I2CRegister; 243 | AMDGPUI2CInit(CurGPU, Config.I2CBusIdx, Config.I2CAddress); 244 | 245 | if(Config.WordOperation) 246 | { 247 | outval = (uint16_t)AMDSMBusReadWord(CurGPU, reg, &Status); 248 | printf("Returned 0x%04X (status 0x%04X).\n", (uint16_t)outval, Status); 249 | } 250 | else 251 | { 252 | outval = (uint8_t)AMDSMBusReadByte(CurGPU, reg, &Status); 253 | printf("Returned 0x%02X (status 0x%04X).\n", (uint8_t)outval, Status); 254 | } 255 | break; 256 | } 257 | case ACTION_WRITE: 258 | { 259 | uint8_t reg = Config.I2CRegister; 260 | AMDGPUI2CInit(CurGPU, Config.I2CBusIdx, Config.I2CAddress); 261 | 262 | if(Config.WordOperation) Status = AMDSMBusWriteWord(CurGPU, reg, (uint16_t)Config.WriteValue); 263 | else Status = AMDI2CWriteByte(CurGPU, reg, (uint8_t)Config.WriteValue); 264 | printf("Write done (status 0x%04X)\n", Status); 265 | break; 266 | } 267 | } 268 | 269 | CloseAMDGPUMMIO(CurGPU); 270 | ReleaseAMDGPUs(GPUList); 271 | return(0); 272 | } 273 | 274 | -------------------------------------------------------------------------------- /wolfamdbg.h: -------------------------------------------------------------------------------- 1 | #ifndef __WOLFAMDBG_H 2 | #define __WOLFAMDBG_H 3 | 4 | #include 5 | #include 6 | 7 | #define WOLFAMDBG_VERSION_STR "v0.91" 8 | #define WOLFAMDBG_VERSION 0.91 9 | 10 | #define ACTION_INVALID 0x00UL 11 | #define ACTION_SCAN 0x01UL 12 | #define ACTION_DUMP 0x02UL 13 | #define ACTION_READ 0x03UL 14 | #define ACTION_WRITE 0x04UL 15 | #define ACTION_DUMP_SMBUS 0x05UL 16 | #define ACTION_READ_SMBUS 0x06UL 17 | 18 | typedef struct _ArgsObj 19 | { 20 | bool GPUIdxProvided, I2CBusIdxProvided; 21 | 22 | uint32_t Action; 23 | 24 | bool WordOperation; 25 | 26 | uint32_t GPUIdx, I2CBusIdx, I2CAddress, I2CRegister, WriteValue; 27 | } ArgsObj; 28 | 29 | bool ParseCmdLine(ArgsObj *Args, int argc, char **argv); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /wolfamdvolt-args.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "wolfamdvolt.h" 8 | 9 | void PrintUsage(char *BinName) 10 | { 11 | printf("Usage: %s [Global Options] [Selection Options] [Modification Options]\n", BinName); 12 | printf("Global Options:\n"); 13 | printf("\t--debug\n"); 14 | printf("Selection Options:\n"); 15 | printf("\t-i GPUIdx\n\t-v VRMIdx\n\t-o VRMOutputIdx\n"); 16 | printf("Modification options (require a GPU index to be specified):\n"); 17 | printf("\t--vddc \n\t--offset \n"); 18 | printf("If the selection options are used without modification options, then info is displayed.\n"); 19 | } 20 | 21 | #define NEXT_ARG_CHECK() do { if(i == (argc - 1)) { printf("Argument \"%s\" requires a parameter.\n", argv[i]); return(false); } } while(0) 22 | 23 | bool ParseCmdLine(ArgsObj *Args, int argc, char **argv) 24 | { 25 | memset(Args, 0x00, sizeof(ArgsObj)); 26 | 27 | if(argc < 2) return(true); 28 | 29 | for(int i = 1; i < argc; ++i) 30 | { 31 | if(!strcmp("-i", argv[i])) 32 | { 33 | NEXT_ARG_CHECK(); 34 | Args->GPUIdx = strtoul(argv[++i], NULL, 10); 35 | 36 | if(errno == EINVAL || errno == ERANGE || Args->GPUIdx > 15) 37 | { 38 | printf("Invalid GPU index specified.\n"); 39 | return(false); 40 | } 41 | 42 | Args->GPUIdxProvided = true; 43 | } 44 | else if(!strcmp("-v", argv[i])) 45 | { 46 | NEXT_ARG_CHECK(); 47 | Args->VRMIdx = strtoul(argv[++i], NULL, 10); 48 | 49 | if(errno == EINVAL || errno == ERANGE) 50 | { 51 | printf("Invalid VRM index specified.\n"); 52 | return(false); 53 | } 54 | 55 | Args->VRMIdxProvided = true; 56 | } 57 | else if(!strcmp("-o", argv[i])) 58 | { 59 | NEXT_ARG_CHECK(); 60 | Args->VRMOutputIdx = strtoul(argv[++i], NULL, 10); 61 | 62 | if(errno == EINVAL || errno == ERANGE) 63 | { 64 | printf("Invalid VRM output index specified.\n"); 65 | return(false); 66 | } 67 | 68 | Args->VRMOutputIdxProvided = true; 69 | } 70 | else if(!strcmp("--vddc", argv[i])) 71 | { 72 | NEXT_ARG_CHECK(); 73 | sscanf(argv[++i], "%f", &Args->RequestedVoltage); 74 | 75 | Args->SetVoltage = true; 76 | } 77 | else if(!strcmp("--offset", argv[i])) 78 | { 79 | NEXT_ARG_CHECK(); 80 | sscanf(argv[++i], "%f", &Args->RequestedVoltageOffset); 81 | 82 | Args->SetVoltageOffset = true; 83 | } 84 | else if(!strcmp("--debug", argv[i])) 85 | { 86 | Args->Debug = true; 87 | } 88 | else 89 | { 90 | PrintUsage(argv[0]); 91 | printf("\nUnknown option: \"%s\"\n", argv[i]); 92 | return(false); 93 | } 94 | } 95 | 96 | return(true); 97 | } 98 | -------------------------------------------------------------------------------- /wolfamdvolt.h: -------------------------------------------------------------------------------- 1 | #ifndef __WOLFAMDVOLT_H 2 | #define __WOLFAMDVOLT_H 3 | 4 | #include 5 | #include 6 | 7 | #define WOLFAMDVOLT_VERSION_STR "v0.95" 8 | #define WOLFAMDVOLT_VERSION 0.95 9 | 10 | typedef struct _ArgsObj 11 | { 12 | bool GPUIdxProvided, VRMIdxProvided, VRMOutputIdxProvided; 13 | bool SetVoltage, SetVoltageOffset; 14 | bool Debug; 15 | 16 | uint32_t GPUIdx, VRMIdx, VRMOutputIdx; 17 | float RequestedVoltage, RequestedVoltageOffset; 18 | } ArgsObj; 19 | 20 | bool ParseCmdLine(ArgsObj *Args, int argc, char **argv); 21 | 22 | #endif 23 | --------------------------------------------------------------------------------