| /* |
| * Copyright (c) 2009 Corey Tabaka |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files |
| * (the "Software"), to deal in the Software without restriction, |
| * including without limitation the rights to use, copy, modify, merge, |
| * publish, distribute, sublicense, and/or sell copies of the Software, |
| * and to permit persons to whom the Software is furnished to do so, |
| * subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| #include <app.h> |
| #include <debug.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <compiler.h> |
| #include <platform.h> |
| #include <dev/pci.h> |
| |
| #if defined(WITH_LIB_CONSOLE) |
| #include <lib/console.h> |
| |
| /* |
| * enumerates pci devices |
| */ |
| static void pci_list(void) |
| { |
| pci_location_t state; |
| uint16_t device_id, vendor_id; |
| uint8_t header_type; |
| int busses = 0, devices = 0, lines = 0, devfn, ret; |
| char c; |
| |
| printf("Scanning...\n"); |
| |
| for (state.bus = 0; state.bus <= pci_get_last_bus(); state.bus++) { |
| busses++; |
| |
| for (devfn = 0; devfn < 256; devfn++) { |
| state.dev_fn = devfn; |
| |
| ret = pci_read_config_half(&state, PCI_CONFIG_VENDOR_ID, &vendor_id); |
| if (ret != _PCI_SUCCESSFUL) goto error; |
| |
| ret = pci_read_config_half(&state, PCI_CONFIG_DEVICE_ID, &device_id); |
| if (ret != _PCI_SUCCESSFUL) goto error; |
| |
| ret = pci_read_config_byte(&state, PCI_CONFIG_HEADER_TYPE, &header_type); |
| if (ret != _PCI_SUCCESSFUL) goto error; |
| |
| if (vendor_id != 0xffff) { |
| printf("%02x:%02x vendor_id=%04x device_id=%04x, header_type=%02x\n", state.bus, state.dev_fn, |
| vendor_id, device_id, header_type); |
| devices++; |
| lines++; |
| } |
| |
| if (~header_type & PCI_HEADER_TYPE_MULTI_FN) { |
| // this is not a multi-function device, so advance to the next device |
| devfn |= 7; |
| } |
| |
| if (lines == 23) { |
| printf("... press any key to continue, q to quit ..."); |
| while(getc(&c) < 0); |
| printf("\n"); |
| lines = 0; |
| |
| if (c == 'q' || c == 'Q') goto quit; |
| } |
| } |
| } |
| |
| printf("... done. Scanned %d busses, %d device/functions\n", busses, devices); |
| quit: |
| return; |
| |
| error: |
| printf("Error while reading PCI config space: %02x\n", ret); |
| } |
| |
| /* |
| * a somewhat fugly pci config space examine/modify command. this should probably |
| * be broken up a bit. |
| */ |
| static int pci_config(int argc, const cmd_args *argv) |
| { |
| pci_location_t loc; |
| pci_config_t config; |
| uint32_t offset; |
| unsigned int i; |
| int ret; |
| |
| if (argc < 5) { |
| return -1; |
| } |
| |
| if (!strcmp(argv[2].str, "dump")) { |
| loc.bus = atoui(argv[3].str); |
| loc.dev_fn = atoui(argv[4].str); |
| |
| for (i=0; i < sizeof(pci_config_t); i++) { |
| ret = pci_read_config_byte(&loc, i, (uint8_t *) &config + i); |
| if (ret != _PCI_SUCCESSFUL) goto error; |
| } |
| |
| printf("Device at %02x:%02x vendor id=%04x device id=%04x\n", loc.bus, |
| loc.dev_fn, config.vendor_id, config.device_id); |
| printf("command=%04x status=%04x pi=%02x sub cls=%02x base cls=%02x\n", |
| config.command, config.status, config.program_interface, |
| config.sub_class, config.base_class); |
| |
| for (i=0; i < 6; i+=2) { |
| printf("bar%d=%08x bar%d=%08x\n", i, config.base_addresses[i], |
| i+1, config.base_addresses[i+1]); |
| } |
| } else if (!strcmp(argv[2].str, "rb") || !strcmp(argv[2].str, "rh") || !strcmp(argv[2].str, "rw")) { |
| if (argc != 6) { |
| return -1; |
| } |
| |
| loc.bus = atoui(argv[3].str); |
| loc.dev_fn = atoui(argv[4].str); |
| offset = atoui(argv[5].str); |
| |
| switch (argv[2].str[1]) { |
| case 'b': { |
| uint8_t value; |
| ret = pci_read_config_byte(&loc, offset, &value); |
| if (ret != _PCI_SUCCESSFUL) goto error; |
| |
| printf("byte at device %02x:%02x config offset %04x: %02x\n", loc.bus, loc.dev_fn, offset, value); |
| } |
| break; |
| |
| case 'h': { |
| uint16_t value; |
| ret = pci_read_config_half(&loc, offset, &value); |
| if (ret != _PCI_SUCCESSFUL) goto error; |
| |
| printf("half at device %02x:%02x config offset %04x: %04x\n", loc.bus, loc.dev_fn, offset, value); |
| } |
| break; |
| |
| case 'w': { |
| uint32_t value; |
| ret = pci_read_config_word(&loc, offset, &value); |
| if (ret != _PCI_SUCCESSFUL) goto error; |
| |
| printf("word at device %02x:%02x config offset %04x: %08x\n", loc.bus, loc.dev_fn, offset, value); |
| } |
| break; |
| } |
| } else if (!strcmp(argv[2].str, "mb") || !strcmp(argv[2].str, "mh") || !strcmp(argv[2].str, "mw")) { |
| if (argc != 7) { |
| return -1; |
| } |
| |
| loc.bus = atoui(argv[3].str); |
| loc.dev_fn = atoui(argv[4].str); |
| offset = atoui(argv[5].str); |
| |
| switch (argv[2].str[1]) { |
| case 'b': { |
| uint8_t value = atoui(argv[6].str); |
| ret = pci_write_config_byte(&loc, offset, value); |
| if (ret != _PCI_SUCCESSFUL) goto error; |
| |
| printf("byte to device %02x:%02x config offset %04x: %02x\n", loc.bus, loc.dev_fn, offset, value); |
| } |
| break; |
| |
| case 'h': { |
| uint16_t value = atoui(argv[6].str); |
| ret = pci_write_config_half(&loc, offset, value); |
| if (ret != _PCI_SUCCESSFUL) goto error; |
| |
| printf("half to device %02x:%02x config offset %04x: %04x\n", loc.bus, loc.dev_fn, offset, value); |
| } |
| break; |
| |
| case 'w': { |
| uint32_t value = atoui(argv[6].str); |
| ret = pci_write_config_word(&loc, offset, value); |
| if (ret != _PCI_SUCCESSFUL) goto error; |
| |
| printf("word to device %02x:%02x config offset %04x: %08x\n", loc.bus, loc.dev_fn, offset, value); |
| } |
| break; |
| } |
| } else { |
| return -1; |
| } |
| |
| return 0; |
| |
| error: |
| printf("Error while reading PCI config space: %02x\n", ret); |
| return -2; |
| } |
| |
| static int pci_cmd(int argc, const cmd_args *argv) |
| { |
| if (argc < 2) { |
| printf("pci commands:\n"); |
| usage: |
| printf("%s list\n", argv[0].str); |
| printf("%s config dump <bus> <devfn>\n", argv[0].str); |
| printf("%s config <rb|rh|rw> <bus> <devfn> <offset>\n", argv[0].str); |
| printf("%s config <mb|mh|mw> <bus> <devfn> <offset> <value>\n", argv[0].str); |
| goto out; |
| } |
| |
| if (!strcmp(argv[1].str, "list")) { |
| pci_list(); |
| } else if (!strcmp(argv[1].str, "config")) { |
| if (pci_config(argc, argv)) { |
| goto usage; |
| } |
| } else { |
| goto usage; |
| } |
| |
| out: |
| return 0; |
| } |
| |
| STATIC_COMMAND_START |
| { "pci", "pci toolbox", &pci_cmd }, |
| STATIC_COMMAND_END(pcitests); |
| |
| #endif |
| |
| APP_START(pcitests) |
| APP_END |
| |