diff options
19 files changed, 1532 insertions, 804 deletions
diff --git a/meta-baldeagle/recipes-applications/spi-test/files/spirom-test.c b/meta-baldeagle/recipes-applications/spi-test/files/spirom-test.c index e7dfe6f1..9006c130 100644 --- a/meta-baldeagle/recipes-applications/spi-test/files/spirom-test.c +++ b/meta-baldeagle/recipes-applications/spi-test/files/spirom-test.c @@ -31,39 +31,34 @@ #include <unistd.h> #include <stdio.h> #include <stdlib.h> -#include <getopt.h> #include <fcntl.h> #include <string.h> -#include <errno.h> +#include <dirent.h> +#include <signal.h> + +#include <sys/types.h> #include <sys/ioctl.h> -#include <linux/types.h> +#include <sys/stat.h> + +#include <readline/readline.h> #include "spirom.h" -#define SPI_APP_VERSION "0.1" +#define SPI_APP_VERSION "1.0" -#define WREN 0x06 -#define WRDI 0x04 -#define RDSR 0x05 -#define RDID 0x9F -#define CHIP_ERASE 0x60 -#define SECTOR_ERASE 0x20 -#define BLOCK_ERASE 0xD8 -#define READ 0x03 -#define WRITE 0x02 +static int device_opened = 0; +static char filename[20]; +static int fd = -1; -static void pabort(const char *s) +char *show_prompt(void) { - perror(s); - abort(); + return "$ "; } -static const char *device = "/dev/spirom0.0"; -static char command[20]; -static int inputfile_fd; -static int outfile_fd;; -static unsigned long address; -static unsigned int num_bytes; +void sighandler(int sig) +{ + /* Do nothing. That is the idea. */ +} void show_license(void) { @@ -72,7 +67,7 @@ void show_license(void) "* Copyright (c) 2014, Advanced Micro Devices, Inc.\n" "* All rights reserved.\n" "*\n" - "* Redistribution and use in source and binary forms, with or without\n" + "* Redistribution and use in source and binary forms, with or without\n" "* modification, are permitted provided that the following conditions are met:\n" "* * Redistributions of source code must retain the above copyright\n" "* notice, this list of conditions and the following disclaimer.\n" @@ -98,188 +93,302 @@ void show_license(void) "***************************************************************************/\n"); } -void parse_command(int fd) +void print_usage(void) +{ + printf("\nCommands Supported ->\n"); + printf(" enumerate : List all SPI device nodes available\n"); + printf(" setdevice <dev_id> : Set the SPI device number to access\n"); + printf(" wren : Enable Write operation on SPI device\n"); + printf(" wrdi : Disable Write operation on SPI device\n"); + printf(" chiperase : Erase entire ROM chip\n"); + printf(" rdsr : Read status register of ROM device\n"); + printf(" rdid : Read device identification string\n"); + printf(" sectorerase <addr> <num_sectors> : Erase a fixed number of sectors starting at the address\n" + " specified\n"); + printf(" blockerase <addr> <num_blocks> : Erase a fixed number of blocks starting at the address\n" + " specified\n"); + printf(" read <addr> <num_bytes> <filename> : Read a fixed number of bytes starting at address\n" + " specified, and output the contents into file\n"); + printf(" write <addr> <num_bytes> <filename> : Read a fixed number of bytes from file and output\n" + " the contents to the device starting at the address\n" + " specified\n"); + printf(" license : Displays the terms of LICENSE for this application\n"); + printf(" help : Displays help text\n"); + printf(" exit : Exits the application\n\n"); +} + +void parse_cmd(const char *cmdline) { - uint8_t cmd_byte; struct spi_ioc_transfer tr; unsigned int bytes_chunks; unsigned int remaining_bytes; + int addr; int ret; - /* Zero initialize spi_ioc_transfer */ - memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - if ((strncmp(command, "WREN", 4) == 0) || - (strncmp(command, "wren", 4) == 0)) { - /* Command without data */ - tr.buf[0] = WREN; + if (strncmp(cmdline, "enumerate", 9) == 0) { + DIR *dir; + struct dirent *dir_entry; + int device_found = 0; + + /* Get the directory handle */ + if ((dir = opendir("/dev")) == NULL) { + printf("\n\nFailed to open directory /dev. Probably you " + "do not have right privilege!\n\n"); + exit(EXIT_FAILURE); + } + + /* Iterate over all the directory entries */ + while ((dir_entry = readdir(dir)) != NULL) { + /* + * If the file is a character device, and its signature + * matches spirom, then we print the corresponding file. + */ + if ((dir_entry->d_type == DT_CHR) && + (strncmp(dir_entry->d_name, "spirom", 6) == 0)) { + printf("/dev/%s\n", dir_entry->d_name); + device_found = 1; + } + } + + printf("\n"); + + /* + * In case we did not find even a single entry, we print a + * message and exit. + */ + if (!device_found) { + printf("\n\nNo spirom device nodes found, load spirom " + "kernel module and try again\n\n"); + exit(EXIT_FAILURE); + } + } else if (strncmp(cmdline, "setdevice", 9) == 0) { + char input[2 + 1]; + int file_desc; + + cmdline += 10; + memset(input, 0, 3); + if (sscanf(cmdline, "%s", input) < 1) { + printf("\nInvalid inputs, please try again\n\n"); + return; + } + + memset(filename, 0, 20); + snprintf(filename, 19, "/dev/spirom%s", input); + file_desc = open(filename, O_RDWR); + if (file_desc < 0) { + printf("\nError opening file %s\n\n", filename); + return; + } + + /* Once we have validated inputs, we store them into the global + * variables used at other places in the program. + */ + fd = file_desc; + device_opened = 1; + printf("\nSPI device set to /dev/spirom%s\n\n", input); + } else if (strncmp(cmdline, "wren", 4) == 0) { + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + + /* command without data */ + tr.buf[0] = ROM_WREN; tr.direction = 0; tr.len = 0; tr.addr_present = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); if (ret < 1) - pabort("can't send spi message"); - } else if ((strncmp(command, "WRDI", 4) == 0) || - (strncmp(command, "wrdi", 4) == 0)) { - /* Command without data */ - tr.buf[0] = WRDI; + printf("\nError executing WREN command\n\n"); + else + printf("\n...WREN completed successfully\n\n"); + } else if (strncmp(cmdline, "wrdi", 4) == 0) { + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + + /* command without data */ + tr.buf[0] = ROM_WRDI; tr.direction = 0; tr.len = 0; tr.addr_present = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); if (ret < 1) - pabort("can't send spi message"); - } else if ((strncmp(command, "CHIPERASE", 4) == 0) || - (strncmp(command, "chiperase", 4) == 0)) { + printf("\nError executing WRDI command\n\n"); + else + printf("\n...WRDI completed successfully\n\n"); + } else if (strncmp(cmdline, "chiperase", 9) == 0) { + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); - else if ((tr.buf[1] & 0x02) == 0x00) { - printf("cannot execute command, write is disabled\n"); - exit(1); + if (ret < 1) { + printf("\nError executing RDSR command\n\n");; + return; + } else if ((tr.buf[1] & 0x02) == 0x00) { + printf("\nCannot execute RDSR command, write is disabled\n\n"); + return; } /* Command without data */ - tr.buf[0] = CHIP_ERASE; + tr.buf[0] = ROM_CHIP_ERASE; tr.direction = 0; tr.len = 0; tr.addr_present = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); if (ret < 1) { - if (errno == EPERM) - printf("\n\nIMC is enabled in your platform. " - "Trying to perform CHIPERASE is not " - "safe. If you really want to perform " - "this operation, either disable IMC in " - "BIOS, or set jumper JU105 to 2-3 " - "position to disable IMC and then " - "try again. Do not forget to remove " - "power to the platform before changing " - "jumper settings\n\n"); - - pabort("can't send spi message"); + printf("\nError executing CHIPERASE command\n\n"); + return; } + printf("\n\nCHIPERASE operation in progress, please do not " + " stop in between.\n\n"); + /* Make sure WIP has been reset */ while (1) { memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } if ((tr.buf[1] & 0x01) == 0x00) break; } - } else if ((strncmp(command, "RDSR", 4) == 0) || - (strncmp(command, "rdsr", 4) == 0)) { + + printf("\n\n...CHIPERASE completed successfully\n\n"); + /* Restore signal handler to default */ + } else if (strncmp(cmdline, "rdsr", 4) == 0) { + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + /* Command with response */ - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } /* * The 1-byte response will be stored in tr.buf, * so print it out */ - printf("command: 0x%.2x response: 0x%.2x\n", tr.buf[0], - tr.buf[1]); - } else if ((strncmp(command, "RDID", 4) == 0) || - (strncmp(command, "rdid", 4) == 0)) { + printf("\nRDSR command returned: 0x%.2x\n\n", tr.buf[1]); + } else if (strncmp(cmdline, "rdid", 4) == 0) { + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + /* Command with response */ - tr.buf[0] = RDID; + tr.buf[0] = ROM_RDID; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 3; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing RDID command\n\n"); + return; + } /* * The 3-bytes response will be stored in tr.buf, * so print it out */ - printf("command: 0x%.2x response: 0x%.2x%.2x%.2x\n", tr.buf[0], - tr.buf[1], tr.buf[2], tr.buf[3]); - } else if ((strncmp(command, "SECTORERASE", 6) ==0) || - (strncmp(command, "sectorerase", 6) ==0)) { + printf("\nRDID command returned: 0x%.2x%.2x%.2x\n", tr.buf[1], + tr.buf[2], tr.buf[3]); + } else if (strncmp(cmdline, "sectorerase", 11) == 0) { + int nsectors; int i; - tr.buf[0] = RDSR; + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + + cmdline += 12; + if (sscanf(cmdline, "0x%x 0x%x", &addr, &nsectors) < 2) { + printf("\nInvalid inputs, please try again\n\n"); + return; + } + + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); - else if ((tr.buf[1] & 0x02) == 0x00) { - printf("cannot execute command, write is disabled\n"); - exit(1); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } else if ((tr.buf[1] & 0x02) == 0x00) { + printf("\nCannot execute SECTORERASE command, write is disabled\n\n"); + return; } - /* - * num_bytes here is a little bit of misnomer, it indicates the - * number of sectors to be erased, rather than the number of - * bytes to be erased. - */ - for (i = 0; i < num_bytes; i++) { + printf("\n\nSECTORERASE operation in progress, please do not " + " stop in between.\n\n"); + + for (i = 0; i < nsectors; i++) { /* Write Enable before Sector Erase */ - tr.buf[0] = WREN; + tr.buf[0] = ROM_WREN; tr.direction = 0; tr.len = 0; tr.addr_present = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing WREN command\n\n"); + return; + } /* Command with address but no data */ memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = SECTOR_ERASE; - tr.buf[3] = address & 0xff; - tr.buf[2] = (address >> 8) & 0xff; - tr.buf[1] = (address >> 16) & 0xff; + tr.buf[0] = ROM_SECTOR_ERASE; + tr.buf[3] = addr & 0xff; + tr.buf[2] = (addr >> 8) & 0xff; + tr.buf[1] = (addr >> 16) & 0xff; tr.addr_present = 1; tr.direction = 0; tr.len = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); if (ret < 1) { - if (errno == EPERM) - printf("\n\nIMC is enabled in your " - "platform. Trying to perform " - "SECTORERASE is not safe. If you " - "really want to perform this " - "operation, either disable IMC " - "in BIOS, or set jumper JU105 " - "to 2-3 position to disable IMC " - "and then try again. Do not " - "forget to remove power to the " - "platform before changing jumper " - "settings.\n\n"); - - pabort("can't send spi message"); + printf("\nError executing SECTORERASE command\n\n"); + return; } - /* point to the next 4k block */ - address += 4 * 1024; + /* point to the next 4k sector */ + addr += 4 * 1024; /* * Before the next loop, we need to make sure that WIP @@ -287,80 +396,86 @@ void parse_command(int fd) */ while (1) { memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } if ((tr.buf[1] & 0x01) == 0x00) break; } } - } else if ((strncmp(command, "BLOCKERASE", 5) == 0) || - (strncmp(command, "blockerase", 5) == 0)) { + + printf("\n\n...SECTORERASE completed successfully\n\n"); + } else if (strncmp(cmdline, "blockerase", 10) == 0) { + int nblocks; int i; - tr.buf[0] = RDSR; + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + + cmdline += 11; + if (sscanf(cmdline, "0x%x 0x%x", &addr, &nblocks) < 2) { + printf("\nInvalid inputs, please try again\n\n"); + return; + } + + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); - else if ((tr.buf[1] & 0x02) == 0x00) { - printf("cannot execute command, write is disabled\n"); - exit(1); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } else if ((tr.buf[1] & 0x02) == 0x00) { + printf("\nError executing BLOCKERASE command, write is disabled\n\n"); + return; } - /* - * num_bytes indicates the number of blocks to be erased, - * rather than the number of bytes to be erased. - */ - for (i = 0; i < num_bytes; i++) { + printf("\n\nBLOCKERASE operation in progress, please do not " + " stop in between.\n\n"); + + for (i = 0; i < nblocks; i++) { /* Write Enable before Block Erase */ - tr.buf[0] = WREN; + tr.buf[0] = ROM_WREN; tr.direction = 0; tr.len = 0; tr.addr_present = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing WREN command\n\n"); + return; + } /* Command with address but no data */ memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = BLOCK_ERASE; - tr.buf[3] = address & 0xff; - tr.buf[2] = (address >> 8) & 0xff; - tr.buf[1] = (address >> 16) & 0xff; + tr.buf[0] = ROM_BLOCK_ERASE; + tr.buf[3] = addr & 0xff; + tr.buf[2] = (addr >> 8) & 0xff; + tr.buf[1] = (addr >> 16) & 0xff; tr.addr_present = 1; tr.direction = 0; tr.len = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); if (ret < 1) { - if (errno == EPERM) - printf("\n\nIMC is enabled in your " - "platform. Trying to perform " - "BLOCKERASE is not safe. If " - "you really want to perform " - "this operation, either disable " - "IMC in BIOS, or set jumper " - "JU105 to 2-3 position to " - "disable IMC and then try " - "again. Do not forget to remove " - "power to the platform before " - "changing jumper settings.\n\n"); - - pabort("can't send spi message"); + printf("\nError executing BLOCKERASE command\n\n"); + return; } /* point to the next 64k block */ - address += 64 * 1024; + addr += 64 * 1024; /* * Before the next loop, we need to make sure that WIP @@ -368,151 +483,205 @@ void parse_command(int fd) */ while (1) { memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } if ((tr.buf[1] & 0x01) == 0x00) break; } } - } else if ((strncmp(command, "READ", 4) == 0) || - (strncmp(command, "read", 4) ==0)) { + + printf("\n\n...BLOCKERASE completed successfully\n\n"); + } else if (strncmp(cmdline, "read", 4) == 0) { + int nbytes; + int outfile_fd; int i; + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + + cmdline += 5; + memset(filename, 0, 20); + if (sscanf(cmdline, "0x%x 0x%x %s", &addr, &nbytes, filename) < 3) { + printf("\nInvalid inputs, please try again\n\n"); + return; + } + + /* + * Open the output file for writing. Create a new file if not + * there, and empty the file before writing if file already + * exists. + */ + outfile_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (outfile_fd < 0) { + printf("\nError opening file %s for writing\n\n", filename); + return; + } + /* * We will break down the bytes to be received in chunks of * of 4-bytes. Data might not be a even multiple of 4. So * in that case, we will have some remaining bytes <4. We * handle that separately. */ - bytes_chunks = num_bytes / 4; - remaining_bytes = num_bytes % 4; + bytes_chunks = nbytes / 4; + remaining_bytes = nbytes % 4; + + printf("\n\nREAD operation in progress.\n\n"); for (i = 0; i < bytes_chunks; i++) { /* Command with address and data */ memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = READ; + tr.buf[0] = ROM_READ; tr.direction = RECEIVE; /* * We will store the address into the buffer in little * endian order. */ - tr.buf[3] = address & 0xff; - tr.buf[2] = (address >> 8) & 0xff; - tr.buf[1] = (address >> 16) & 0xff; + tr.buf[3] = addr & 0xff; + tr.buf[2] = (addr >> 8) & 0xff; + tr.buf[1] = (addr >> 16) & 0xff; tr.len = 4; tr.addr_present = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing READ command\n\n"); + return; + } /* Write the data read to output file */ if (write(outfile_fd, &tr.buf[4], tr.len) < 0) { - perror("write error"); - exit(1); + printf("\nError writing to file %s\n\n", filename); + return; } - address += 4; + addr += 4; } if (remaining_bytes) { memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = READ; + tr.buf[0] = ROM_READ; tr.direction = RECEIVE; - tr.buf[3] = address & 0xff; - tr.buf[2] = (address >> 8) & 0xff; - tr.buf[1] = (address >> 16) & 0xff; + tr.buf[3] = addr & 0xff; + tr.buf[2] = (addr >> 8) & 0xff; + tr.buf[1] = (addr >> 16) & 0xff; tr.len = remaining_bytes; tr.addr_present = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing READ command\n\n"); + return; + } if (write(outfile_fd, &tr.buf[4], tr.len) < 0) { - perror("write error"); - exit(1); + printf("\nError writing to file %s\n\n", filename); + return; } } - } else if ((strncmp(command, "WRITE", 5) == 0) || - (strncmp(command, "write", 5) ==0)) { + + printf("\n\n...READ completed successfully\n\n"); + close(outfile_fd); + } else if (strncmp(cmdline, "write", 5) == 0) { + int nbytes; + int infile_fd; int i; + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + + cmdline += 6; + memset(filename, 0, 20); + if (sscanf(cmdline, "0x%x 0x%x %s", &addr, &nbytes, filename) < 3) { + printf("\nInvalid inputs, please try again\n\n"); + return; + } + + /* Open the input file for reading*/ + infile_fd = open(filename, O_RDONLY); + if (infile_fd < 0) { + printf("\nError opening file %s for reading\n\n", filename); + return; + } + /* * We will break down the bytes to be transmitted in chunks of * of 4-bytes. Like for read, we might not have data in an * even multiple of 4 bytes. So we will handle the remaining * bytes in the end. */ - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); - else if ((tr.buf[1] & 0x02) == 0x00) { - printf("cannot execute command, write is disabled\n"); - exit(1); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } else if ((tr.buf[1] & 0x02) == 0x00) { + printf("\nCannot execute WRITE command, write is disabled\n\n"); + return; } - bytes_chunks = num_bytes / 4; - remaining_bytes = num_bytes % 4; + bytes_chunks = nbytes / 4; + remaining_bytes = nbytes % 4; + + printf("\n\nWRITE operation in progress, please do not " + " stop in between.\n\n"); for (i = 0; i < bytes_chunks; i++) { - tr.buf[0] = WREN; + tr.buf[0] = ROM_WREN; tr.direction = 0; tr.len = 0; tr.addr_present = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing WREN command\n\n"); + return; + } /* Command with data and address */ memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = WRITE; + tr.buf[0] = ROM_WRITE; tr.direction = TRANSMIT; /* * We will store the address into the buffer in little * endian order. */ - tr.buf[3] = address & 0xff; - tr.buf[2] = (address >> 8) & 0xff; - tr.buf[1] = (address >> 16) & 0xff; + tr.buf[3] = addr & 0xff; + tr.buf[2] = (addr >> 8) & 0xff; + tr.buf[1] = (addr >> 16) & 0xff; tr.len = 4; tr.addr_present = 1; - /* Read 4 bytes from inputfile to buffer */ - if (read(inputfile_fd, &tr.buf[4], tr.len) < 0) { - perror("read error"); - exit(1); + /* Read 4 bytes from input file to buffer */ + if (read(infile_fd, &tr.buf[4], tr.len) < 0) { + printf("\nError reading from file %s\n\n", filename); + return; } + ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); if (ret < 1) { - if (errno == EPERM) - printf("\n\nIMC is enabled in your " - "platform. Trying to perform " - "WRITE is not safe. If you " - "really want to perform this " - "operation, either disable IMC " - "in BIOS, or set jumper JU105 " - "to 2-3 position to disable IMC " - "and then try again. Do not " - "forget to remove power to the " - "platform before changing jumper " - "settings.\n\n"); - - pabort("can't send spi message"); + printf("\nError executing WRITE command\n\n"); + return; } - address += 4; + addr += 4; /* * Before the next loop, we need to make sure that WIP @@ -520,14 +689,16 @@ void parse_command(int fd) */ while (1) { memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } if ((tr.buf[1] & 0x01) == 0x00) break; @@ -535,161 +706,93 @@ void parse_command(int fd) } if (remaining_bytes) { - tr.buf[0] = WREN; + tr.buf[0] = ROM_WREN; tr.direction = 0; tr.len = 0; tr.addr_present = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing WREN command\n\n"); + return; + } memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = WRITE; + tr.buf[0] = ROM_WRITE; tr.direction = TRANSMIT; - tr.buf[3] = address & 0xff; - tr.buf[2] = (address >> 8) & 0xff; - tr.buf[1] = (address >> 16) & 0xff; + tr.buf[3] = addr & 0xff; + tr.buf[2] = (addr >> 8) & 0xff; + tr.buf[1] = (addr >> 16) & 0xff; tr.len = remaining_bytes; tr.addr_present = 1; - if (read(inputfile_fd, &tr.buf[4], tr.len) < 0) { - perror("read error"); - exit(1); + if (read(infile_fd, &tr.buf[4], tr.len) < 0) { + printf("\nError reading from file %s\n\n", filename); + return; } + ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing WRITE command\n\n"); + return; + } while (1) { memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } if ((tr.buf[1] & 0x01) == 0x00) break; } } - } else - pabort("Unrecognized command, please try again.\n"); -} - -static void print_usage(const char *prog) -{ - printf("\nUsage: sudo %s [-DCAnio] [arguments]\n\n", prog); - puts(" -D --device SPI ROM device to use\n" - " (default /dev/spirom0.0)\n\n" - " -C --command command to send to the device\n" - " (WREN/WRDI/RDSR/RDID/CHIPERASE/SECTORERASE/\n" - " BLOCKERASE/READ/WRITE)\n\n" - " -A --address offset in decimal, into the device to read\n" - " from or write to. For a ROM size of 8MB,\n" - " address can go from 0 to 8388608. Negative\n" - " offsets are not valid, but the program\n" - " won't complain and convert it to its\n" - " unsigned equivalent.\n\n" - " -n --num-bytes number of bytes to be read from or written\n" - " to. Depending on the address, this can\n" - " take values from 0 to 8388608 for a ROM\n" - " size of 8MB.\n\n" - " In case of SECTORERASE and BLOCKERASE\n" - " commands, num-bytes actually takes the\n" - " number of sectors and blocks to be erased\n" - " respectively, rather than the number of\n" - " bytes.\n\n" - " -i --input-file file to be used as input.\n\n" - " -o --output-file file to be used for output. Remember that if\n" - " an existing filename is given, its contents\n" - " will be overwritten.\n" - " -l --license displays the terms of LICENSE for this application\n\n"); - exit(1); -} - -static void parse_opts(int argc, char *argv[]) -{ - if (argc == 1) - print_usage(argv[0]); - while (1) { - static const struct option lopts[] = { - { "device", 1, 0, 'D' }, - { "command", 1, 0, 'C' }, - { "address", 1, 0, 'A' }, - { "num-bytes", 1, 0, 'n' }, - { "input-file", 1, 0, 'i' }, - { "output-file", 1, 0, 'o' }, - { "license", 0, 0, 'l' }, - { NULL, 0, 0, 0 }, - }; - int c; - - c = getopt_long(argc, argv, "D:C:A:n:i:o:l", lopts, NULL); - - if (c == -1) - break; - - switch (c) { - case 'D': - device = optarg; - break; - case 'C': - memset(command, sizeof(command), 0); - strncpy(command, optarg, sizeof(command)); - break; - case 'A': - address = atol(optarg); - break; - case 'n': - num_bytes = atoi(optarg); - break; - case 'i': - inputfile_fd = open(optarg, O_RDONLY); - if (inputfile_fd < 0) { - printf("Error opening %s\n", optarg); - exit(1); - } - break; - case 'o': - outfile_fd = open(optarg, O_WRONLY | O_CREAT | - O_TRUNC, 0644); - if(outfile_fd < 0) { - printf("Error opening %s\n", optarg); - exit(1); - } - break; - case 'l': - show_license(); - exit(0);; - default: - print_usage(argv[0]); - break; - } + printf("\n\n...WRITE completed successfully\n\n"); + close(infile_fd); + } else if (strncmp(cmdline, "license", 7) == 0) { + show_license(); + } else if (strncmp(cmdline, "exit", 4) == 0) { + printf("\nExiting...\n"); + close(fd); + exit(EXIT_SUCCESS); + } else if (strncmp(cmdline, "help", 4) == 0) { + print_usage(); + } else { + printf("\nUnknown command\n"); + print_usage(); } } -int main(int argc, char *argv[]) +int main(void) { - int ret = 0; - int fd; + char *cmdline= NULL; printf("SPI sample application version: %s\n", SPI_APP_VERSION); printf("Copyright (c) 2014, Advanced Micro Devices, Inc.\n" "This sample application comes with ABSOLUTELY NO WARRANTY;\n" "This is free software, and you are welcome to redistribute it\n" - "under certain conditions; type `license' for details.\n\n"); + "under certain conditions; type `license` for details.\n\n"); - parse_opts(argc, argv); + /* Set the signal handler */ + signal(SIGINT, sighandler); - fd = open(device, O_RDWR); - if (fd < 0) - pabort("can't open device"); + while (1) { + cmdline = readline(show_prompt()); + parse_cmd(cmdline); + /* Free the memory malloc'ed by readline */ + free(cmdline); + } - parse_command(fd); + /* Restore the default signal handler */ + signal(SIGINT, SIG_DFL); - return ret; + /* Should never reach here */ + return 0; } diff --git a/meta-baldeagle/recipes-applications/spi-test/files/spirom.h b/meta-baldeagle/recipes-applications/spi-test/files/spirom.h index fe142a31..1b0caafa 100644 --- a/meta-baldeagle/recipes-applications/spi-test/files/spirom.h +++ b/meta-baldeagle/recipes-applications/spi-test/files/spirom.h @@ -39,24 +39,6 @@ struct spi_ioc_transfer { ? ((N)*(sizeof (struct spi_ioc_transfer))) : 0) #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)]) -#ifdef __KERNEL__ - -/* IO port address for indirect access using the ACPI PM registers */ -#define AMD_IO_PM_INDEX_REG 0xCD6 -#define AMD_IO_PM_DATA_REG 0xCD7 - -#define AMD_PM_ACPI_MMIO_BASE0 0x24 -#define AMD_PM_ACPI_MMIO_BASE1 0x25 -#define AMD_PM_ACPI_MMIO_BASE2 0x26 -#define AMD_PM_ACPI_MMIO_BASE3 0x27 - -#define AMD_ACPI_MMIO_ADDR_MASK ~0x1FFF - -/* Offset of IMC Strap Status register in the ACPI MMIO region */ -#define AMD_IMC_STRAP_STATUS_OFFSET 0xE80 - #define AMD_IMC_ENABLED 0x4 -#define AMD_IMC_STRAP_STATUS_SIZE 4 - /* SPI ROM command codes */ #define ROM_WREN 0x06 #define ROM_WRDI 0x04 @@ -68,6 +50,4 @@ struct spi_ioc_transfer { #define ROM_READ 0x03 #define ROM_WRITE 0x02 -#endif /* __KERNEL__ */ - #endif /* SPIROM_H */ diff --git a/meta-baldeagle/recipes-applications/spi-test/spi-test_1.0.bb b/meta-baldeagle/recipes-applications/spi-test/spi-test_1.0.bb index 8cb251c9..2d75661b 100644 --- a/meta-baldeagle/recipes-applications/spi-test/spi-test_1.0.bb +++ b/meta-baldeagle/recipes-applications/spi-test/spi-test_1.0.bb @@ -2,8 +2,8 @@ DESCRIPTION = "Sample application for AMD SPI driver" SECTION = "applications" LICENSE = "BSD" DEPENDS = "readline" -LIC_FILES_CHKSUM = "file://spirom-test.c;md5=a8b8f295bacb7eaffb0981efe09ca1bb \ - file://spirom.h;md5=332ef940b1cb318fc8ecd9b1ba5940be \ +LIC_FILES_CHKSUM = "file://spirom-test.c;md5=3065341fac5fc1255711c219f00f7324 \ + file://spirom.h;md5=dad84d1bbdd9852dde1fdf1ea3a014d6 \ " PR = "r1" diff --git a/meta-baldeagle/recipes-kernel/amd-spi/amd-spi_1.0.bb b/meta-baldeagle/recipes-kernel/amd-spi/amd-spi_1.0.bb index c25a9a81..850a0e42 100644 --- a/meta-baldeagle/recipes-kernel/amd-spi/amd-spi_1.0.bb +++ b/meta-baldeagle/recipes-kernel/amd-spi/amd-spi_1.0.bb @@ -1,9 +1,9 @@ DESCRIPTION = "This kernel module provides support for AMD SPI controller driver" LICENSE = "BSD | GPLv2" -LIC_FILES_CHKSUM = "file://spi_amd.c;md5=e70d62038c566061b8fea14bb3d31c39 \ - file://spi_amd.h;md5=5400c6b24b00e86e5930fdccc058dabb \ - file://spirom.c;md5=558c5e03489d2d7792c03dcf7e2539ab \ - file://spirom.h;md5=332ef940b1cb318fc8ecd9b1ba5940be \ +LIC_FILES_CHKSUM = "file://spi_amd.c;md5=bca93a3bd8cbdabafd4089daf5109768 \ + file://spi_amd.h;md5=67ba12b2bcfda1402435aebf18cda6bd \ + file://spirom.c;md5=f1cd805abcc598247c197aed08ec4a5d \ + file://spirom.h;md5=dad84d1bbdd9852dde1fdf1ea3a014d6 \ file://Makefile;md5=8ea80a6d4ae15bcf922d090df6cfdd4c \ " diff --git a/meta-baldeagle/recipes-kernel/amd-spi/files/spi_amd.c b/meta-baldeagle/recipes-kernel/amd-spi/files/spi_amd.c index 6af2c6db..1ca9b69a 100644 --- a/meta-baldeagle/recipes-kernel/amd-spi/files/spi_amd.c +++ b/meta-baldeagle/recipes-kernel/amd-spi/files/spi_amd.c @@ -45,9 +45,9 @@ struct amd_spi { u32 rom_addr; struct spi_master *master; struct amd_platform_data controller_data; - spinlock_t lock; struct task_struct *kthread_spi; struct list_head msg_queue; + wait_queue_head_t wq; }; static struct pci_device_id amd_spi_pci_device_id[] = { @@ -155,7 +155,9 @@ static void amd_spi_execute_opcode(struct spi_master *master) spi_busy = (ioread32((u8 *)amd_spi->io_remap_addr + AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; while (spi_busy) { + set_current_state(TASK_INTERRUPTIBLE); schedule(); + set_current_state(TASK_RUNNING); spi_busy = (ioread32((u8 *)amd_spi->io_remap_addr + AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; } @@ -199,29 +201,17 @@ static int amd_spi_master_setup(struct spi_device *spi) return 0; } -static int amd_spi_master_transfer(struct spi_device *spi, +static int amd_spi_master_transfer(struct spi_master *master, struct spi_message *msg) { - struct spi_master *master = spi->master; struct amd_spi *amd_spi = spi_master_get_devdata(master); /* - * We will just add this message to the message queue set up by - * the controller, and let the kernel thread handle it later. - */ - msg->status = -EINPROGRESS; - msg->actual_length = 0; - msg->spi = spi; - /* - * There could be a situation when we are running on this processor - * and trying to add element to the end of the message queue, but - * at the same time the kernel thread is running on another processor - * and trying to find out if the list is empty or not. So protect - * against such contention. Simple spin_lock() should do. + * Add new message to the queue and let the kernel thread know + * about it. */ - spin_lock(&amd_spi->lock); list_add_tail(&msg->queue, &amd_spi->msg_queue); - spin_unlock(&amd_spi->lock); + wake_up_interruptible(&amd_spi->wq); return 0; } @@ -249,25 +239,19 @@ static int amd_spi_thread(void *t) * 4-bytes of data and 3-bytes of address. */ while (1) { - /* break condition */ + /* + * Let us wait on a wait queue till the message queue is empty. + */ + wait_event_interruptible(amd_spi->wq, + !list_empty(&amd_spi->msg_queue)); + + /* stop condition */ if (kthread_should_stop()) { set_current_state(TASK_RUNNING); break; } /* - * If the message queue is empty, then there is no need to waste - * CPU cycles. So we let other processes execute, and continue - * from the beginning of the loop when we next get to run. - */ - spin_lock(&amd_spi->lock); - if (list_empty(&amd_spi->msg_queue)) { - spin_unlock(&amd_spi->lock); - schedule(); - continue; - } - - /* * Else, pull the very first message from the queue and process * all transfers within that message. And process the messages * in a pure linear fashion. We also remove the spi_message @@ -276,7 +260,6 @@ static int amd_spi_thread(void *t) message = list_entry(amd_spi->msg_queue.next, struct spi_message, queue); list_del_init(&message->queue); - spin_unlock(&amd_spi->lock); /* We store the CS# line to be used for this spi_message */ amd_spi->controller_data.chip_select = @@ -396,7 +379,7 @@ static int amd_spi_thread(void *t) message->actual_length = tx_len + rx_len + 1; /* complete the transaction */ message->status = 0; - message->complete(message->context); + spi_finalize_current_message(master); } return 0; @@ -441,8 +424,8 @@ static int amd_spi_pci_probe(struct pci_dev *pdev, dev_dbg(dev, "io_base_addr: 0x%.8lx, io_remap_address: %p\n", amd_spi->io_base_addr, amd_spi->io_remap_addr); - spin_lock_init(&amd_spi->lock); INIT_LIST_HEAD(&amd_spi->msg_queue); + init_waitqueue_head(&amd_spi->wq); amd_spi->kthread_spi = kthread_run(amd_spi_thread, amd_spi, "amd_spi_thread"); @@ -455,7 +438,7 @@ static int amd_spi_pci_probe(struct pci_dev *pdev, master->mode_bits = 0; master->flags = 0; master->setup = amd_spi_master_setup; - master->transfer = amd_spi_master_transfer; + master->transfer_one_message = amd_spi_master_transfer; /* Register the controller with SPI framework */ err = spi_register_master(master); if (err) { diff --git a/meta-baldeagle/recipes-kernel/amd-spi/files/spi_amd.h b/meta-baldeagle/recipes-kernel/amd-spi/files/spi_amd.h index bc0bd6e8..b766d15c 100644 --- a/meta-baldeagle/recipes-kernel/amd-spi/files/spi_amd.h +++ b/meta-baldeagle/recipes-kernel/amd-spi/files/spi_amd.h @@ -2,7 +2,7 @@ #define SPI_AMD_H #define DRIVER_NAME "spi_amd" -#define SPI_VERSION "0.1" +#define SPI_VERSION "1.0" #define PCI_DEVICE_ID_AMD_LPC_BRIDGE 0x780E diff --git a/meta-baldeagle/recipes-kernel/amd-spi/files/spirom.c b/meta-baldeagle/recipes-kernel/amd-spi/files/spirom.c index 99d28d31..4df6c70c 100644 --- a/meta-baldeagle/recipes-kernel/amd-spi/files/spirom.c +++ b/meta-baldeagle/recipes-kernel/amd-spi/files/spirom.c @@ -32,11 +32,13 @@ #include <linux/spi/spi.h> #include <linux/pci.h> #include <linux/delay.h> +#include <linux/amd_imc.h> + #include <asm/uaccess.h> #include "spirom.h" -#define SPIROM_VERSION "0.1" +#define SPIROM_VERSION "0.2" /* * SPI has a character major number assigned. We allocate minor numbers @@ -52,7 +54,6 @@ #define SPI_BUS_CS1 0 static unsigned long minors[N_SPI_MINORS / BITS_PER_LONG]; -static int imc_enabled; struct spirom_data { dev_t devt; @@ -84,7 +85,6 @@ spirom_sync(struct spirom_data *spirom, struct spi_message *message) { int status; - INIT_COMPLETION(spirom->done); message->complete = spirom_complete; message->context = &spirom->done; @@ -128,18 +128,6 @@ static int spirom_message(struct spirom_data *spirom, return -ENOMEM; transfer->tx_buf = buffer; - - /* - * In case IMC is enabled, and an erase or write operation is requested - * on the ROM, we simply print a message and return from here. Rather - * than letting the user corrupt his system never to boot again, it is - * better to play safe. - */ - if (imc_enabled && ((*buffer == ROM_CHIP_ERASE) || - (*buffer == ROM_SECTOR_ERASE) || (*buffer == ROM_BLOCK_ERASE) || - (*buffer == ROM_WRITE))) - return -EPERM; - transfer->len = 1; buffer += transfer->len; spi_message_add_tail(transfer, &msg); @@ -318,6 +306,14 @@ static int spirom_open(struct inode *inode, struct file *filp) mutex_unlock(&device_list_lock); + /* + * In case IMC is enabled, we need to inform IMC to stop + * fetching code from the BIOS ROM. We will inform IMC when + * it is safe to start fetching from ROM again once we are + * done with our SPI transactions. + */ + amd_imc_enter_scratch_ram(); + return status; } @@ -345,6 +341,14 @@ static int spirom_release(struct inode *inode, struct file *filp) } mutex_unlock(&device_list_lock); + /* + * In case IMC is enabled, we would have instructed IMC to stop + * fetching from ROM BIOS earlier in the code path. Now that we + * are done, we can safely inform IMC to start fetching from ROM + * again. + */ + amd_imc_exit_scratch_ram(); + return status; } @@ -396,57 +400,12 @@ static int spirom_probe(struct spi_device *spi) struct spirom_data *spirom; int status; unsigned long minor; - u32 val; - u8 *byte; - u32 imc_strap_status_phys; - void __iomem *imcstrapstatus; /* Allocate driver data */ spirom = kzalloc(sizeof(*spirom), GFP_KERNEL); if (!spirom) return -ENOMEM; - /* Locate ACPI MMIO Base Address */ - byte = (u8 *)&val; - - outb(AMD_PM_ACPI_MMIO_BASE0, AMD_IO_PM_INDEX_REG); - byte[0] = inb(AMD_IO_PM_DATA_REG); - outb(AMD_PM_ACPI_MMIO_BASE1, AMD_IO_PM_INDEX_REG); - byte[1] = inb(AMD_IO_PM_DATA_REG); - outb(AMD_PM_ACPI_MMIO_BASE2, AMD_IO_PM_INDEX_REG); - byte[2] = inb(AMD_IO_PM_DATA_REG); - outb(AMD_PM_ACPI_MMIO_BASE3, AMD_IO_PM_INDEX_REG); - byte[3] = inb(AMD_IO_PM_DATA_REG); - - /* Bits 31:13 is the actual ACPI MMIO Base Address */ - val &= AMD_ACPI_MMIO_ADDR_MASK; - - /* IMCStrapStatus is located at ACPI MMIO Base Address + 0xE80 */ - if (!request_mem_region_exclusive(val + AMD_IMC_STRAP_STATUS_OFFSET, - AMD_IMC_STRAP_STATUS_SIZE, "IMC Strap Status")) - pr_err("spirom: MMIO address 0x%04x already in use\n", val + - AMD_IMC_STRAP_STATUS_OFFSET); - - imc_strap_status_phys = val + AMD_IMC_STRAP_STATUS_OFFSET; - - imcstrapstatus = ioremap(imc_strap_status_phys, AMD_IMC_STRAP_STATUS_SIZE); - if (!imcstrapstatus) - pr_err("spirom: failed to map IMC Strap Status address\n"); - - /* Check if IMC is enabled */ - val = ioread32(imcstrapstatus); - if ((val & AMD_IMC_ENABLED) == AMD_IMC_ENABLED) { - pr_info("spirom: IMC is enabled\n"); - imc_enabled = 1; - } else { - pr_info("spirom: IMC is disabled\n"); - imc_enabled = 0; - } - - /* Release the region occupied by IMC Strap Status register */ - iounmap(imcstrapstatus); - release_mem_region(imc_strap_status_phys, AMD_IMC_STRAP_STATUS_SIZE); - /* Initialize the driver data */ spirom->spi = spi; spin_lock_init(&spirom->spi_lock); diff --git a/meta-baldeagle/recipes-kernel/amd-spi/files/spirom.h b/meta-baldeagle/recipes-kernel/amd-spi/files/spirom.h index fe142a31..1b0caafa 100644 --- a/meta-baldeagle/recipes-kernel/amd-spi/files/spirom.h +++ b/meta-baldeagle/recipes-kernel/amd-spi/files/spirom.h @@ -39,24 +39,6 @@ struct spi_ioc_transfer { ? ((N)*(sizeof (struct spi_ioc_transfer))) : 0) #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)]) -#ifdef __KERNEL__ - -/* IO port address for indirect access using the ACPI PM registers */ -#define AMD_IO_PM_INDEX_REG 0xCD6 -#define AMD_IO_PM_DATA_REG 0xCD7 - -#define AMD_PM_ACPI_MMIO_BASE0 0x24 -#define AMD_PM_ACPI_MMIO_BASE1 0x25 -#define AMD_PM_ACPI_MMIO_BASE2 0x26 -#define AMD_PM_ACPI_MMIO_BASE3 0x27 - -#define AMD_ACPI_MMIO_ADDR_MASK ~0x1FFF - -/* Offset of IMC Strap Status register in the ACPI MMIO region */ -#define AMD_IMC_STRAP_STATUS_OFFSET 0xE80 - #define AMD_IMC_ENABLED 0x4 -#define AMD_IMC_STRAP_STATUS_SIZE 4 - /* SPI ROM command codes */ #define ROM_WREN 0x06 #define ROM_WRDI 0x04 @@ -68,6 +50,4 @@ struct spi_ioc_transfer { #define ROM_READ 0x03 #define ROM_WRITE 0x02 -#endif /* __KERNEL__ */ - #endif /* SPIROM_H */ diff --git a/meta-baldeagle/recipes-kernel/linux/linux-yocto/0046-yocto-poky-dora-10.0.0-amd-staging-add-support-to-enable-and-disable-IMC-to-fetch-BIOS-code.patch b/meta-baldeagle/recipes-kernel/linux/linux-yocto/0046-yocto-poky-dora-10.0.0-amd-staging-add-support-to-enable-and-disable-IMC-to-fetch-BIOS-code.patch new file mode 100644 index 00000000..5b4cb7a2 --- /dev/null +++ b/meta-baldeagle/recipes-kernel/linux/linux-yocto/0046-yocto-poky-dora-10.0.0-amd-staging-add-support-to-enable-and-disable-IMC-to-fetch-BIOS-code.patch @@ -0,0 +1,455 @@ +From 370d48b29a69a2e38d3092058413b22f040203de Mon Sep 17 00:00:00 2001 +From: Arindam Nath <arindam.nath@amd.com> +Date: Mon, 4 Aug 2014 19:16:53 +0530 +Subject: [PATCH 1/2] yocto: amd: staging: add support to enable and disable + IMC to fetch BIOS code + +The patch essentially adds support for two functions: +amd_imc_enter_scratch_ram() and amd_imc_exit_scratch_ram(). These +functions instruct IMC to stop and start fetching code from BIOS +ROM respectively. These functions are needed where IMC is trying +to fetch code on a shared bus when some other transaction is already +occuring. To prevent IMC to fetch incorrect data from ROM while it +is still being updated, we instruct IMC to temporarily stop fetching +code from BIOS, and then start fetching again when it is safe to do +so. + +Upstream Status: None + +Signed-off-by: Arindam Nath <arindam.nath@amd.com> +--- + drivers/staging/Kconfig | 2 + + drivers/staging/Makefile | 1 + + drivers/staging/amd_imc/Kconfig | 9 ++ + drivers/staging/amd_imc/Makefile | 1 + + drivers/staging/amd_imc/amd_imc.c | 298 ++++++++++++++++++++++++++++++++++++++ + include/linux/amd_imc.h | 68 +++++++++ + 6 files changed, 379 insertions(+) + create mode 100644 drivers/staging/amd_imc/Kconfig + create mode 100644 drivers/staging/amd_imc/Makefile + create mode 100644 drivers/staging/amd_imc/amd_imc.c + create mode 100644 include/linux/amd_imc.h + +diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig +index 3626dbc8..0a95d6d 100644 +--- a/drivers/staging/Kconfig ++++ b/drivers/staging/Kconfig +@@ -148,4 +148,6 @@ source "drivers/staging/dgnc/Kconfig" + + source "drivers/staging/dgap/Kconfig" + ++source "drivers/staging/amd_imc/Kconfig" ++ + endif # STAGING +diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile +index d1b4b80..2be3a91 100644 +--- a/drivers/staging/Makefile ++++ b/drivers/staging/Makefile +@@ -66,3 +66,4 @@ obj-$(CONFIG_USB_BTMTK) += btmtk_usb/ + obj-$(CONFIG_XILLYBUS) += xillybus/ + obj-$(CONFIG_DGNC) += dgnc/ + obj-$(CONFIG_DGAP) += dgap/ ++obj-$(CONFIG_AMD_IMC) += amd_imc/ +diff --git a/drivers/staging/amd_imc/Kconfig b/drivers/staging/amd_imc/Kconfig +new file mode 100644 +index 0000000..abfb724 +--- /dev/null ++++ b/drivers/staging/amd_imc/Kconfig +@@ -0,0 +1,9 @@ ++config AMD_IMC ++ bool "AMD Integrated Micro Controller support" ++ depends on PCI && X86_64 ++ default y ++ ---help--- ++ This driver supports AMD Integrated Micro Controller. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called amd_imc. +diff --git a/drivers/staging/amd_imc/Makefile b/drivers/staging/amd_imc/Makefile +new file mode 100644 +index 0000000..c4837f8 +--- /dev/null ++++ b/drivers/staging/amd_imc/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_AMD_IMC) += amd_imc.o +diff --git a/drivers/staging/amd_imc/amd_imc.c b/drivers/staging/amd_imc/amd_imc.c +new file mode 100644 +index 0000000..c6c6074 +--- /dev/null ++++ b/drivers/staging/amd_imc/amd_imc.c +@@ -0,0 +1,298 @@ ++/***************************************************************************** ++* ++* Copyright (c) 2014, Advanced Micro Devices, Inc. ++* All rights reserved. ++* ++* Redistribution and use in source and binary forms, with or without ++* modification, are permitted provided that the following conditions are met: ++* * Redistributions of source code must retain the above copyright ++* notice, this list of conditions and the following disclaimer. ++* * Redistributions in binary form must reproduce the above copyright ++* notice, this list of conditions and the following disclaimer in the ++* documentation and/or other materials provided with the distribution. ++* * Neither the name of Advanced Micro Devices, Inc. nor the names of ++* its contributors may be used to endorse or promote products derived ++* from this software without specific prior written permission. ++* ++* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ++* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++* DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY ++* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ++* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ++* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++* ++* ++***************************************************************************/ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/pci.h> ++#include <linux/ioport.h> ++#include <linux/platform_device.h> ++#include <linux/uaccess.h> ++#include <linux/io.h> ++#include <linux/delay.h> ++#include <linux/amd_imc.h> ++ ++static int imc_enabled; ++static u16 imc_port_addr; ++static u8 msg_reg_base_hi; ++static u8 msg_reg_base_lo; ++static u16 msg_reg_base; ++ ++static struct pci_dev *amd_imc_pci; ++static struct platform_device *amd_imc_platform_device; ++ ++static DEFINE_PCI_DEVICE_TABLE(amd_lpc_pci_tbl) = { ++ {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LPC_BRIDGE, PCI_ANY_ID, ++ PCI_ANY_ID,}, ++ {} ++}; ++ ++void amd_imc_enter_scratch_ram(void) ++{ ++ u8 byte; ++ ++ if (!imc_enabled) ++ return; ++ ++ /* Instruct IMC to enter scratch RAM */ ++ outb(AMD_MSG_REG0, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); ++ outb(0, msg_reg_base + AMD_MSG_DATA_REG_OFFSET); ++ ++ outb(AMD_MSG_REG1, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); ++ outb(AMD_IMC_ENTER_SCRATCH_RAM, msg_reg_base + AMD_MSG_DATA_REG_OFFSET); ++ ++ outb(AMD_MSG_SYS_TO_IMC, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); ++ outb(AMD_IMC_ROM_OWNERSHIP_SEM, msg_reg_base + ++ AMD_MSG_DATA_REG_OFFSET); ++ ++ /* As per the spec, the firmware may take up to 50ms */ ++ msleep(50); ++ ++ /* read message registger 0 to confirm function completion */ ++ outb(AMD_MSG_REG0, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); ++ byte = inb(msg_reg_base + AMD_MSG_DATA_REG_OFFSET); ++ ++ if (byte == AMD_IMC_FUNC_NOT_SUPP) ++ pr_info("amd_imc: %s not supported\n", __func__); ++ else if (byte == AMD_IMC_FUNC_COMPLETED) ++ pr_info("amd_imc: %s completed\n", __func__); ++} ++EXPORT_SYMBOL_GPL(amd_imc_enter_scratch_ram); ++ ++void amd_imc_exit_scratch_ram(void) ++{ ++ u8 byte; ++ ++ if (!imc_enabled) ++ return; ++ ++ /* Instruct IMC to exit scratch RAM */ ++ outb(AMD_MSG_REG0, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); ++ outb(0, msg_reg_base + AMD_MSG_DATA_REG_OFFSET); ++ ++ outb(AMD_MSG_REG1, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); ++ outb(AMD_IMC_ENTER_SCRATCH_RAM, msg_reg_base + AMD_MSG_DATA_REG_OFFSET); ++ ++ outb(AMD_MSG_SYS_TO_IMC, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); ++ outb(AMD_IMC_ROM_OWNERSHIP_SEM, msg_reg_base + ++ AMD_MSG_DATA_REG_OFFSET); ++ ++ /* As per the spec, the firmware may take up to 50ms */ ++ msleep(50); ++ ++ /* read message registger 0 to confirm function completion */ ++ outb(AMD_MSG_REG0, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); ++ byte = inb(msg_reg_base + AMD_MSG_DATA_REG_OFFSET); ++ ++ if (byte == AMD_IMC_FUNC_NOT_SUPP) ++ pr_info("amd_imc: %s not supported\n", __func__); ++ else if (byte == AMD_IMC_FUNC_COMPLETED) ++ pr_info("amd_imc: %s completed\n", __func__); ++} ++EXPORT_SYMBOL_GPL(amd_imc_exit_scratch_ram); ++ ++/* ++* The PCI Device ID table below is used to identify the platform ++* the driver is supposed to work for. Since this is a platform ++* driver, we need a way for us to be able to find the correct ++* platform when the driver gets loaded, otherwise we should ++* bail out. ++*/ ++static DEFINE_PCI_DEVICE_TABLE(amd_imc_pci_tbl) = { ++ { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, PCI_ANY_ID, ++ PCI_ANY_ID, }, ++ { 0, }, ++}; ++ ++static int amd_imc_init(struct platform_device *pdev) ++{ ++ struct pci_dev *dev = NULL; ++ static u32 imc_strap_status_phys; ++ void __iomem *imcstrapstatus; ++ u32 val; ++ u8 *byte; ++ ++ /* Match the PCI device */ ++ for_each_pci_dev(dev) { ++ if (pci_match_id(amd_imc_pci_tbl, dev) != NULL) { ++ amd_imc_pci = dev; ++ break; ++ } ++ } ++ ++ if (!amd_imc_pci) ++ return -ENODEV; ++ ++ /* Locate ACPI MMIO Base Address. */ ++ byte = (u8 *)&val; ++ ++ outb(AMD_PM_ACPI_MMIO_BASE0, AMD_IO_PM_INDEX_REG); ++ byte[0] = inb(AMD_IO_PM_DATA_REG); ++ outb(AMD_PM_ACPI_MMIO_BASE1, AMD_IO_PM_INDEX_REG); ++ byte[1] = inb(AMD_IO_PM_DATA_REG); ++ outb(AMD_PM_ACPI_MMIO_BASE2, AMD_IO_PM_INDEX_REG); ++ byte[2] = inb(AMD_IO_PM_DATA_REG); ++ outb(AMD_PM_ACPI_MMIO_BASE3, AMD_IO_PM_INDEX_REG); ++ byte[3] = inb(AMD_IO_PM_DATA_REG); ++ ++ /* Bits 31:13 is the actual ACPI MMIO Base Address */ ++ val &= AMD_ACPI_MMIO_ADDR_MASK; ++ ++ /* IMCStrapStatus is located at ACPI MMIO Base Address + 0xE80 */ ++ if (!request_mem_region_exclusive(val + AMD_IMC_STRAP_STATUS_OFFSET, ++ AMD_IMC_STRAP_STATUS_SIZE, "IMC Strap Status")) { ++ pr_err("amd_imc: MMIO address 0x%04x already in use\n", ++ val + AMD_IMC_STRAP_STATUS_OFFSET); ++ goto exit; ++ } ++ ++ imc_strap_status_phys = val + AMD_IMC_STRAP_STATUS_OFFSET; ++ ++ imcstrapstatus = ioremap(imc_strap_status_phys, ++ AMD_IMC_STRAP_STATUS_SIZE); ++ if (!imcstrapstatus) { ++ pr_err("amd_imc: failed to get IMC Strap Status address\n"); ++ goto unreg_imc_region; ++ } ++ ++ /* Check if IMC is enabled */ ++ val = ioread32(imcstrapstatus); ++ if ((val & AMD_IMC_ENABLED) == AMD_IMC_ENABLED) { ++ struct pci_dev *pdev = NULL; ++ ++ pr_info("amd_imc: IMC is enabled\n"); ++ imc_enabled = 1; ++ ++ /* ++ * In case IMC is enabled, we need to find the IMC port address ++ * which will be used to send messages to the IMC. The IMC port ++ * address is stored in bits 1:15 of PCI device 20, function 3, ++ * offset 0xA4. PCI device 20, function 3 is actually the LPC ++ * ISA bridge. ++ */ ++ for_each_pci_dev(pdev) { ++ if (pci_match_id(amd_lpc_pci_tbl, pdev) != NULL) ++ break; ++ } ++ ++ /* Match found. Get the IMC port address */ ++ if (pdev) { ++ pci_read_config_word(pdev, AMD_PCI_IMC_PORT_ADDR_REG, ++ &imc_port_addr); ++ ++ /* The actual IMC port address has bit 0 masked out */ ++ imc_port_addr &= ~AMD_IMC_PORT_ACTIVE; ++ } ++ ++ /* Put device into configuration state */ ++ outb(AMD_DEVICE_ENTER_CONFIG_STATE, imc_port_addr + ++ AMD_IMC_INDEX_REG_OFFSET); ++ ++ /* Select logical device number 9 */ ++ outb(AMD_SET_LOGICAL_DEVICE, imc_port_addr + ++ AMD_IMC_INDEX_REG_OFFSET); ++ outb(AMD_SET_DEVICE_9, imc_port_addr + ++ AMD_IMC_DATA_REG_OFFSET); ++ ++ /* read high byte of message register base address */ ++ outb(AMD_MSG_REG_HIGH, imc_port_addr + ++ AMD_IMC_INDEX_REG_OFFSET); ++ msg_reg_base_hi = inb(imc_port_addr + AMD_IMC_DATA_REG_OFFSET); ++ ++ /* read low byte of message register base address */ ++ outb(AMD_MSG_REG_LOW, imc_port_addr + ++ AMD_IMC_INDEX_REG_OFFSET); ++ msg_reg_base_lo = inb(imc_port_addr + AMD_IMC_DATA_REG_OFFSET); ++ ++ msg_reg_base = msg_reg_base_lo | (msg_reg_base_hi << 8); ++ ++ /* Get device out of configuration state */ ++ outb(AMD_DEVICE_EXIT_CONFIG_STATE, imc_port_addr + ++ AMD_IMC_INDEX_REG_OFFSET); ++ } else { ++ pr_info("amd_imc: IMC is disabled\n"); ++ imc_enabled = 0; ++ } ++ ++ /* Release the region occupied by IMC Strap Status register */ ++ iounmap(imcstrapstatus); ++ release_mem_region(imc_strap_status_phys, AMD_IMC_STRAP_STATUS_SIZE); ++ ++ return 0; ++ ++unreg_imc_region: ++ release_mem_region(imc_strap_status_phys, AMD_IMC_STRAP_STATUS_SIZE); ++exit: ++ return -ENODEV; ++} ++ ++static struct platform_driver amd_imc_driver = { ++ .probe = amd_imc_init, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = IMC_MODULE_NAME, ++ }, ++}; ++ ++static int __init amd_imc_init_module(void) ++{ ++ int err; ++ ++ pr_info("AMD IMC Driver v%s\n", IMC_VERSION); ++ ++ err = platform_driver_register(&amd_imc_driver); ++ if (err) ++ return err; ++ ++ amd_imc_platform_device = platform_device_register_simple( ++ IMC_MODULE_NAME, -1, NULL, 0); ++ if (IS_ERR(amd_imc_platform_device)) { ++ err = PTR_ERR(amd_imc_platform_device); ++ goto unreg_platform_driver; ++ } ++ ++ return 0; ++ ++unreg_platform_driver: ++ platform_driver_unregister(&amd_imc_driver); ++ return err; ++} ++ ++static void __exit amd_imc_cleanup_module(void) ++{ ++ platform_device_unregister(amd_imc_platform_device); ++ platform_driver_unregister(&amd_imc_driver); ++ pr_info("AMD IMC Module Unloaded\n"); ++} ++ ++module_init(amd_imc_init_module); ++module_exit(amd_imc_cleanup_module); ++ ++MODULE_AUTHOR("Arindam Nath <arindam.nath@amd.com>"); ++MODULE_DESCRIPTION("AMD IMC driver"); ++MODULE_LICENSE("Dual BSD/GPL"); +diff --git a/include/linux/amd_imc.h b/include/linux/amd_imc.h +new file mode 100644 +index 0000000..4b4b7b8 +--- /dev/null ++++ b/include/linux/amd_imc.h +@@ -0,0 +1,68 @@ ++#ifndef _AMD_IMC_H_ ++#define _AMD_IMC_H_ ++ ++/* Module and version information */ ++#define IMC_VERSION "0.1" ++#define IMC_MODULE_NAME "AMD IMC" ++#define IMC_DRIVER_NAME IMC_MODULE_NAME ", v" IMC_VERSION ++ ++#define DRV_NAME "amd_imc" ++ ++/* IO port address for indirect access using the ACPI PM registers */ ++#define AMD_IO_PM_INDEX_REG 0xCD6 ++#define AMD_IO_PM_DATA_REG 0xCD7 ++ ++#define AMD_PM_ACPI_MMIO_BASE0 0x24 ++#define AMD_PM_ACPI_MMIO_BASE1 0x25 ++#define AMD_PM_ACPI_MMIO_BASE2 0x26 ++#define AMD_PM_ACPI_MMIO_BASE3 0x27 ++ ++#define AMD_ACPI_MMIO_ADDR_MASK ~0x1FFF ++ ++/* Offset of IMC Strap Status register in the ACPI MMIO region */ ++#define AMD_IMC_STRAP_STATUS_OFFSET 0xE80 ++ #define AMD_IMC_ENABLED 0x4 ++#define AMD_IMC_STRAP_STATUS_SIZE 4 ++ ++#define PCI_DEVICE_ID_AMD_LPC_BRIDGE 0x780E ++ #define AMD_PCI_IMC_PORT_ADDR_REG 0xA4 ++ #define AMD_IMC_PORT_ACTIVE 0x0001 ++ ++/* Device configuration state fields */ ++#define AMD_DEVICE_ENTER_CONFIG_STATE 0x5A ++#define AMD_DEVICE_EXIT_CONFIG_STATE 0xA5 ++ ++/* Global configuration registers */ ++#define AMD_SET_LOGICAL_DEVICE 0x07 ++ #define AMD_SET_DEVICE_9 0x09 ++#define AMD_MSG_REG_HIGH 0x60 ++#define AMD_MSG_REG_LOW 0x61 ++ ++/* IMC index and data port offsets for indirect access */ ++#define AMD_IMC_INDEX_REG_OFFSET 0x00 ++#define AMD_IMC_DATA_REG_OFFSET 0x01 ++ ++/* Message register index and data port offsets for indirect access */ ++#define AMD_MSG_INDEX_REG_OFFSET 0x00 ++#define AMD_MSG_DATA_REG_OFFSET 0x01 ++ ++/* IMC message registers */ ++#define AMD_MSG_SYS_TO_IMC 0x80 ++ #define AMD_IMC_ROM_OWNERSHIP_SEM 0x96 ++#define AMD_MSG_REG0 0x82 ++ #define AMD_IMC_FUNC_NOT_SUPP 0x00 ++ #define AMD_IMC_FUNC_COMPLETED 0xFA ++#define AMD_MSG_REG1 0x83 ++ #define AMD_IMC_ENTER_SCRATCH_RAM 0xB4 ++ #define AMD_IMC_EXIT_SCRATCH_RAM 0xB5 ++ ++/* Extern functions */ ++#ifdef CONFIG_AMD_IMC ++extern void amd_imc_enter_scratch_ram(void); ++extern void amd_imc_exit_scratch_ram(void); ++#else ++void amd_imc_enter_scratch_ram(void) {} ++void amd_imc_exit_scratch_ram(void) {} ++#endif ++ ++#endif /* _AMD_IMC_H_ */ +-- +1.9.1 + diff --git a/meta-baldeagle/recipes-kernel/linux/linux-yocto/0047-yocto-poky-dora-10.0.0-amd-i2c-dev-add-calls-to-enable-and-disable-IMC-from-fetching-BIOS-code.patch b/meta-baldeagle/recipes-kernel/linux/linux-yocto/0047-yocto-poky-dora-10.0.0-amd-i2c-dev-add-calls-to-enable-and-disable-IMC-from-fetching-BIOS-code.patch new file mode 100644 index 00000000..6287c826 --- /dev/null +++ b/meta-baldeagle/recipes-kernel/linux/linux-yocto/0047-yocto-poky-dora-10.0.0-amd-i2c-dev-add-calls-to-enable-and-disable-IMC-from-fetching-BIOS-code.patch @@ -0,0 +1,51 @@ +From 38595e633e5a1237bdea9cb0aa9193aea40542a8 Mon Sep 17 00:00:00 2001 +From: Arindam Nath <arindam.nath@amd.com> +Date: Mon, 4 Aug 2014 19:21:44 +0530 +Subject: [PATCH 2/2] yocto: amd: i2c-dev: add calls to enable and disable IMC + from fetching BIOS code + +The patch adds support to disable IMC from fetching BIOS code when +we first open the SMBus device. We can perform SMBus transaction +safely once IMC is disabled. Then when we close the device after +the operation, we enable IMC to start fetching from BIOS ROM again. + +Upstream Status: None + +Signed-off-by: Arindam Nath <arindam.nath@amd.com> +--- + drivers/i2c/i2c-dev.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c +index c3ccdea..c92ec4c 100644 +--- a/drivers/i2c/i2c-dev.c ++++ b/drivers/i2c/i2c-dev.c +@@ -39,6 +39,7 @@ + #include <linux/i2c-dev.h> + #include <linux/jiffies.h> + #include <linux/uaccess.h> ++#include <linux/amd_imc.h> + + /* + * An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a +@@ -512,6 +513,8 @@ static int i2cdev_open(struct inode *inode, struct file *file) + client->adapter = adap; + file->private_data = client; + ++ amd_imc_enter_scratch_ram(); ++ + return 0; + } + +@@ -523,6 +526,8 @@ static int i2cdev_release(struct inode *inode, struct file *file) + kfree(client); + file->private_data = NULL; + ++ amd_imc_exit_scratch_ram(); ++ + return 0; + } + +-- +1.9.1 + diff --git a/meta-baldeagle/recipes-kernel/linux/linux-yocto/defconfig b/meta-baldeagle/recipes-kernel/linux/linux-yocto/defconfig index fef542cd..177c63c9 100644 --- a/meta-baldeagle/recipes-kernel/linux/linux-yocto/defconfig +++ b/meta-baldeagle/recipes-kernel/linux/linux-yocto/defconfig @@ -3454,7 +3454,61 @@ CONFIG_VIRTIO_MMIO=y # # Microsoft Hyper-V guest support # -# CONFIG_STAGING is not set +CONFIG_STAGING=y +# CONFIG_ET131X is not set +# CONFIG_SLICOSS is not set +# CONFIG_USBIP_CORE is not set +# CONFIG_W35UND is not set +# CONFIG_PRISM2_USB is not set +# CONFIG_ECHO is not set +# CONFIG_COMEDI is not set +# CONFIG_PANEL is not set +# CONFIG_R8187SE is not set +# CONFIG_RTL8192U is not set +# CONFIG_RTLLIB is not set +# CONFIG_R8712U is not set +# CONFIG_R8188EU is not set +# CONFIG_RTS5139 is not set +# CONFIG_TRANZPORT is not set +# CONFIG_IDE_PHISON is not set +# CONFIG_LINE6_USB is not set +# CONFIG_USB_SERIAL_QUATECH2 is not set +# CONFIG_VT6655 is not set +# CONFIG_VT6656 is not set +# CONFIG_DX_SEP is not set +# CONFIG_ZSMALLOC is not set +# CONFIG_FB_SM7XX is not set +# CONFIG_CRYSTALHD is not set +# CONFIG_FB_XGI is not set +# CONFIG_ACPI_QUICKSTART is not set +# CONFIG_USB_ENESTORAGE is not set +# CONFIG_BCM_WIMAX is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_SPEAKUP is not set +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set +# CONFIG_STAGING_MEDIA is not set + +# +# Android +# +# CONFIG_ANDROID is not set +# CONFIG_USB_WPAN_HCD is not set +# CONFIG_WIMAX_GDM72XX is not set +# CONFIG_LTE_GDM724X is not set +# CONFIG_NET_VENDOR_SILICOM is not set +# CONFIG_CED1401 is not set +# CONFIG_DGRP is not set +# CONFIG_USB_DWC2 is not set +# CONFIG_LUSTRE_FS is not set +# CONFIG_XILLYBUS is not set +# CONFIG_DGNC is not set +# CONFIG_DGAP is not set +CONFIG_AMD_IMC=y CONFIG_X86_PLATFORM_DEVICES=y # CONFIG_ACERHDF is not set # CONFIG_ASUS_LAPTOP is not set @@ -3595,6 +3649,7 @@ CONFIG_BTRFS_FS_POSIX_ACL=y # CONFIG_BTRFS_ASSERT is not set # CONFIG_NILFS2_FS is not set CONFIG_FS_POSIX_ACL=y +# CONFIG_EXPORTFS is not set CONFIG_FILE_LOCKING=y CONFIG_FSNOTIFY=y CONFIG_DNOTIFY=y diff --git a/meta-baldeagle/recipes-kernel/linux/linux-yocto_3.12.bb b/meta-baldeagle/recipes-kernel/linux/linux-yocto_3.12.bb index 7d895ef2..bcb9692e 100644 --- a/meta-baldeagle/recipes-kernel/linux/linux-yocto_3.12.bb +++ b/meta-baldeagle/recipes-kernel/linux/linux-yocto_3.12.bb @@ -53,7 +53,9 @@ SRC_URI = "http://git.yoctoproject.org/cgit/cgit.cgi/linux-yocto-dev/snapshot/li file://0042-yocto-poky-dora-10.0.0-amd-drm-radeon-cik-enable-disable-vce-cg-when-encoding.patch \ file://0043-yocto-poky-dora-10.0.0-amd-drm-radeon-fix-CP-semaphores-on-CIK.patch \ file://0044-yocto-poky-dora-10.0.0-amd-drm-radeon-disable-dynamic-powering-vce.patch \ - file://0045-yocto-poky-dora-10.0.0-amd-clear-exceptions-in-AMD-FXSAVE-workaround.patch" + file://0045-yocto-poky-dora-10.0.0-amd-clear-exceptions-in-AMD-FXSAVE-workaround.patch \ + file://0046-yocto-poky-dora-10.0.0-amd-staging-add-support-to-enable-and-disable-IMC-to-fetch-BIOS-code.patch \ + file://0047-yocto-poky-dora-10.0.0-amd-i2c-dev-add-calls-to-enable-and-disable-IMC-from-fetching-BIOS-code.patch" S = "${WORKDIR}/linux-yocto-dev-${PV}" diff --git a/meta-steppeeagle/recipes-applications/spi-test/files/spirom-test.c b/meta-steppeeagle/recipes-applications/spi-test/files/spirom-test.c index 1c48f7c4..9006c130 100644 --- a/meta-steppeeagle/recipes-applications/spi-test/files/spirom-test.c +++ b/meta-steppeeagle/recipes-applications/spi-test/files/spirom-test.c @@ -31,38 +31,34 @@ #include <unistd.h> #include <stdio.h> #include <stdlib.h> -#include <getopt.h> #include <fcntl.h> #include <string.h> +#include <dirent.h> +#include <signal.h> + +#include <sys/types.h> #include <sys/ioctl.h> -#include <linux/types.h> +#include <sys/stat.h> + +#include <readline/readline.h> #include "spirom.h" -#define SPI_APP_VERSION "0.1" +#define SPI_APP_VERSION "1.0" -#define WREN 0x06 -#define WRDI 0x04 -#define RDSR 0x05 -#define RDID 0x9F -#define CHIP_ERASE 0x60 -#define SECTOR_ERASE 0x20 -#define BLOCK_ERASE 0xD8 -#define READ 0x03 -#define WRITE 0x02 +static int device_opened = 0; +static char filename[20]; +static int fd = -1; -static void pabort(const char *s) +char *show_prompt(void) { - perror(s); - abort(); + return "$ "; } -static const char *device = "/dev/spirom0.0"; -static char command[20]; -static int inputfile_fd; -static int outfile_fd;; -static unsigned long address; -static unsigned int num_bytes; +void sighandler(int sig) +{ + /* Do nothing. That is the idea. */ +} void show_license(void) { @@ -97,162 +93,302 @@ void show_license(void) "***************************************************************************/\n"); } -void parse_command(int fd) +void print_usage(void) +{ + printf("\nCommands Supported ->\n"); + printf(" enumerate : List all SPI device nodes available\n"); + printf(" setdevice <dev_id> : Set the SPI device number to access\n"); + printf(" wren : Enable Write operation on SPI device\n"); + printf(" wrdi : Disable Write operation on SPI device\n"); + printf(" chiperase : Erase entire ROM chip\n"); + printf(" rdsr : Read status register of ROM device\n"); + printf(" rdid : Read device identification string\n"); + printf(" sectorerase <addr> <num_sectors> : Erase a fixed number of sectors starting at the address\n" + " specified\n"); + printf(" blockerase <addr> <num_blocks> : Erase a fixed number of blocks starting at the address\n" + " specified\n"); + printf(" read <addr> <num_bytes> <filename> : Read a fixed number of bytes starting at address\n" + " specified, and output the contents into file\n"); + printf(" write <addr> <num_bytes> <filename> : Read a fixed number of bytes from file and output\n" + " the contents to the device starting at the address\n" + " specified\n"); + printf(" license : Displays the terms of LICENSE for this application\n"); + printf(" help : Displays help text\n"); + printf(" exit : Exits the application\n\n"); +} + +void parse_cmd(const char *cmdline) { - uint8_t cmd_byte; struct spi_ioc_transfer tr; unsigned int bytes_chunks; unsigned int remaining_bytes; + int addr; int ret; - /* Zero initialize spi_ioc_transfer */ - memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - if ((strncmp(command, "WREN", 4) == 0) || - (strncmp(command, "wren", 4) == 0)) { - /* Command without data */ - tr.buf[0] = WREN; + if (strncmp(cmdline, "enumerate", 9) == 0) { + DIR *dir; + struct dirent *dir_entry; + int device_found = 0; + + /* Get the directory handle */ + if ((dir = opendir("/dev")) == NULL) { + printf("\n\nFailed to open directory /dev. Probably you " + "do not have right privilege!\n\n"); + exit(EXIT_FAILURE); + } + + /* Iterate over all the directory entries */ + while ((dir_entry = readdir(dir)) != NULL) { + /* + * If the file is a character device, and its signature + * matches spirom, then we print the corresponding file. + */ + if ((dir_entry->d_type == DT_CHR) && + (strncmp(dir_entry->d_name, "spirom", 6) == 0)) { + printf("/dev/%s\n", dir_entry->d_name); + device_found = 1; + } + } + + printf("\n"); + + /* + * In case we did not find even a single entry, we print a + * message and exit. + */ + if (!device_found) { + printf("\n\nNo spirom device nodes found, load spirom " + "kernel module and try again\n\n"); + exit(EXIT_FAILURE); + } + } else if (strncmp(cmdline, "setdevice", 9) == 0) { + char input[2 + 1]; + int file_desc; + + cmdline += 10; + memset(input, 0, 3); + if (sscanf(cmdline, "%s", input) < 1) { + printf("\nInvalid inputs, please try again\n\n"); + return; + } + + memset(filename, 0, 20); + snprintf(filename, 19, "/dev/spirom%s", input); + file_desc = open(filename, O_RDWR); + if (file_desc < 0) { + printf("\nError opening file %s\n\n", filename); + return; + } + + /* Once we have validated inputs, we store them into the global + * variables used at other places in the program. + */ + fd = file_desc; + device_opened = 1; + printf("\nSPI device set to /dev/spirom%s\n\n", input); + } else if (strncmp(cmdline, "wren", 4) == 0) { + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + + /* command without data */ + tr.buf[0] = ROM_WREN; tr.direction = 0; tr.len = 0; tr.addr_present = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); if (ret < 1) - pabort("can't send spi message"); - } else if ((strncmp(command, "WRDI", 4) == 0) || - (strncmp(command, "wrdi", 4) == 0)) { - /* Command without data */ - tr.buf[0] = WRDI; + printf("\nError executing WREN command\n\n"); + else + printf("\n...WREN completed successfully\n\n"); + } else if (strncmp(cmdline, "wrdi", 4) == 0) { + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + + /* command without data */ + tr.buf[0] = ROM_WRDI; tr.direction = 0; tr.len = 0; tr.addr_present = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); if (ret < 1) - pabort("can't send spi message"); - } else if ((strncmp(command, "CHIPERASE", 4) == 0) || - (strncmp(command, "chiperase", 4) == 0)) { + printf("\nError executing WRDI command\n\n"); + else + printf("\n...WRDI completed successfully\n\n"); + } else if (strncmp(cmdline, "chiperase", 9) == 0) { + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); - else if ((tr.buf[1] & 0x02) == 0x00) { - printf("cannot execute command, write is disabled\n"); - exit(1); + if (ret < 1) { + printf("\nError executing RDSR command\n\n");; + return; + } else if ((tr.buf[1] & 0x02) == 0x00) { + printf("\nCannot execute RDSR command, write is disabled\n\n"); + return; } /* Command without data */ - tr.buf[0] = CHIP_ERASE; + tr.buf[0] = ROM_CHIP_ERASE; tr.direction = 0; tr.len = 0; tr.addr_present = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing CHIPERASE command\n\n"); + return; + } + + printf("\n\nCHIPERASE operation in progress, please do not " + " stop in between.\n\n"); /* Make sure WIP has been reset */ while (1) { memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } if ((tr.buf[1] & 0x01) == 0x00) break; } - } else if ((strncmp(command, "RDSR", 4) == 0) || - (strncmp(command, "rdsr", 4) == 0)) { + + printf("\n\n...CHIPERASE completed successfully\n\n"); + /* Restore signal handler to default */ + } else if (strncmp(cmdline, "rdsr", 4) == 0) { + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + /* Command with response */ - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } /* * The 1-byte response will be stored in tr.buf, * so print it out */ - printf("command: 0x%.2x response: 0x%.2x\n", tr.buf[0], - tr.buf[1]); - } else if ((strncmp(command, "RDID", 4) == 0) || - (strncmp(command, "rdid", 4) == 0)) { + printf("\nRDSR command returned: 0x%.2x\n\n", tr.buf[1]); + } else if (strncmp(cmdline, "rdid", 4) == 0) { + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + /* Command with response */ - tr.buf[0] = RDID; + tr.buf[0] = ROM_RDID; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 3; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing RDID command\n\n"); + return; + } /* * The 3-bytes response will be stored in tr.buf, * so print it out */ - printf("command: 0x%.2x response: 0x%.2x%.2x%.2x\n", tr.buf[0], - tr.buf[1], tr.buf[2], tr.buf[3]); - } else if ((strncmp(command, "SECTORERASE", 6) ==0) || - (strncmp(command, "sectorerase", 6) ==0)) { + printf("\nRDID command returned: 0x%.2x%.2x%.2x\n", tr.buf[1], + tr.buf[2], tr.buf[3]); + } else if (strncmp(cmdline, "sectorerase", 11) == 0) { + int nsectors; int i; - tr.buf[0] = RDSR; + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + + cmdline += 12; + if (sscanf(cmdline, "0x%x 0x%x", &addr, &nsectors) < 2) { + printf("\nInvalid inputs, please try again\n\n"); + return; + } + + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); - else if ((tr.buf[1] & 0x02) == 0x00) { - printf("cannot execute command, write is disabled\n"); - exit(1); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } else if ((tr.buf[1] & 0x02) == 0x00) { + printf("\nCannot execute SECTORERASE command, write is disabled\n\n"); + return; } - /* - * num_bytes here is a little bit of misnomer, it indicates the - * number of sectors to be erased, rather than the number of - * bytes to be erased. - */ - for (i = 0; i < num_bytes; i++) { + printf("\n\nSECTORERASE operation in progress, please do not " + " stop in between.\n\n"); + + for (i = 0; i < nsectors; i++) { /* Write Enable before Sector Erase */ - tr.buf[0] = WREN; + tr.buf[0] = ROM_WREN; tr.direction = 0; tr.len = 0; tr.addr_present = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing WREN command\n\n"); + return; + } /* Command with address but no data */ memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = SECTOR_ERASE; - tr.buf[3] = address & 0xff; - tr.buf[2] = (address >> 8) & 0xff; - tr.buf[1] = (address >> 16) & 0xff; + tr.buf[0] = ROM_SECTOR_ERASE; + tr.buf[3] = addr & 0xff; + tr.buf[2] = (addr >> 8) & 0xff; + tr.buf[1] = (addr >> 16) & 0xff; tr.addr_present = 1; tr.direction = 0; tr.len = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing SECTORERASE command\n\n"); + return; + } - /* point to the next 4k block */ - address += 4 * 1024; + /* point to the next 4k sector */ + addr += 4 * 1024; /* * Before the next loop, we need to make sure that WIP @@ -260,66 +396,86 @@ void parse_command(int fd) */ while (1) { memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } if ((tr.buf[1] & 0x01) == 0x00) break; } } - } else if ((strncmp(command, "BLOCKERASE", 5) == 0) || - (strncmp(command, "blockerase", 5) == 0)) { + + printf("\n\n...SECTORERASE completed successfully\n\n"); + } else if (strncmp(cmdline, "blockerase", 10) == 0) { + int nblocks; int i; - tr.buf[0] = RDSR; + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + + cmdline += 11; + if (sscanf(cmdline, "0x%x 0x%x", &addr, &nblocks) < 2) { + printf("\nInvalid inputs, please try again\n\n"); + return; + } + + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); - else if ((tr.buf[1] & 0x02) == 0x00) { - printf("cannot execute command, write is disabled\n"); - exit(1); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } else if ((tr.buf[1] & 0x02) == 0x00) { + printf("\nError executing BLOCKERASE command, write is disabled\n\n"); + return; } - /* - * num_bytes indicates the number of blocks to be erased, - * rather than the number of bytes to be erased. - */ - for (i = 0; i < num_bytes; i++) { + printf("\n\nBLOCKERASE operation in progress, please do not " + " stop in between.\n\n"); + + for (i = 0; i < nblocks; i++) { /* Write Enable before Block Erase */ - tr.buf[0] = WREN; + tr.buf[0] = ROM_WREN; tr.direction = 0; tr.len = 0; tr.addr_present = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing WREN command\n\n"); + return; + } /* Command with address but no data */ memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = BLOCK_ERASE; - tr.buf[3] = address & 0xff; - tr.buf[2] = (address >> 8) & 0xff; - tr.buf[1] = (address >> 16) & 0xff; + tr.buf[0] = ROM_BLOCK_ERASE; + tr.buf[3] = addr & 0xff; + tr.buf[2] = (addr >> 8) & 0xff; + tr.buf[1] = (addr >> 16) & 0xff; tr.addr_present = 1; tr.direction = 0; tr.len = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing BLOCKERASE command\n\n"); + return; + } /* point to the next 64k block */ - address += 64 * 1024; + addr += 64 * 1024; /* * Before the next loop, we need to make sure that WIP @@ -327,138 +483,205 @@ void parse_command(int fd) */ while (1) { memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } if ((tr.buf[1] & 0x01) == 0x00) break; } } - } else if ((strncmp(command, "READ", 4) == 0) || - (strncmp(command, "read", 4) ==0)) { + + printf("\n\n...BLOCKERASE completed successfully\n\n"); + } else if (strncmp(cmdline, "read", 4) == 0) { + int nbytes; + int outfile_fd; int i; + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + + cmdline += 5; + memset(filename, 0, 20); + if (sscanf(cmdline, "0x%x 0x%x %s", &addr, &nbytes, filename) < 3) { + printf("\nInvalid inputs, please try again\n\n"); + return; + } + + /* + * Open the output file for writing. Create a new file if not + * there, and empty the file before writing if file already + * exists. + */ + outfile_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (outfile_fd < 0) { + printf("\nError opening file %s for writing\n\n", filename); + return; + } + /* * We will break down the bytes to be received in chunks of - * of 64-bytes. Data might not be a even multiple of 64. So - * in that case, we will have some remaining bytes <64. We + * of 4-bytes. Data might not be a even multiple of 4. So + * in that case, we will have some remaining bytes <4. We * handle that separately. */ - bytes_chunks = num_bytes / 64; - remaining_bytes = num_bytes % 64; + bytes_chunks = nbytes / 4; + remaining_bytes = nbytes % 4; + + printf("\n\nREAD operation in progress.\n\n"); for (i = 0; i < bytes_chunks; i++) { /* Command with address and data */ memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = READ; + tr.buf[0] = ROM_READ; tr.direction = RECEIVE; /* * We will store the address into the buffer in little * endian order. */ - tr.buf[3] = address & 0xff; - tr.buf[2] = (address >> 8) & 0xff; - tr.buf[1] = (address >> 16) & 0xff; - tr.len = 64; + tr.buf[3] = addr & 0xff; + tr.buf[2] = (addr >> 8) & 0xff; + tr.buf[1] = (addr >> 16) & 0xff; + tr.len = 4; tr.addr_present = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing READ command\n\n"); + return; + } /* Write the data read to output file */ if (write(outfile_fd, &tr.buf[4], tr.len) < 0) { - perror("write error"); - exit(1); + printf("\nError writing to file %s\n\n", filename); + return; } - address += 64; + addr += 4; } if (remaining_bytes) { memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = READ; + tr.buf[0] = ROM_READ; tr.direction = RECEIVE; - tr.buf[3] = address & 0xff; - tr.buf[2] = (address >> 8) & 0xff; - tr.buf[1] = (address >> 16) & 0xff; + tr.buf[3] = addr & 0xff; + tr.buf[2] = (addr >> 8) & 0xff; + tr.buf[1] = (addr >> 16) & 0xff; tr.len = remaining_bytes; tr.addr_present = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing READ command\n\n"); + return; + } if (write(outfile_fd, &tr.buf[4], tr.len) < 0) { - perror("write error"); - exit(1); + printf("\nError writing to file %s\n\n", filename); + return; } } - } else if ((strncmp(command, "WRITE", 5) == 0) || - (strncmp(command, "write", 5) ==0)) { + + printf("\n\n...READ completed successfully\n\n"); + close(outfile_fd); + } else if (strncmp(cmdline, "write", 5) == 0) { + int nbytes; + int infile_fd; int i; + if (!device_opened) { + printf("\nSPI device needs to be set before you can " + "perform this operation\n\n"); + return; + } + + cmdline += 6; + memset(filename, 0, 20); + if (sscanf(cmdline, "0x%x 0x%x %s", &addr, &nbytes, filename) < 3) { + printf("\nInvalid inputs, please try again\n\n"); + return; + } + + /* Open the input file for reading*/ + infile_fd = open(filename, O_RDONLY); + if (infile_fd < 0) { + printf("\nError opening file %s for reading\n\n", filename); + return; + } + /* * We will break down the bytes to be transmitted in chunks of - * of 64-bytes. Like for read, we might not have data in an - * even multiple of 64 bytes. So we will handle the remaining + * of 4-bytes. Like for read, we might not have data in an + * even multiple of 4 bytes. So we will handle the remaining * bytes in the end. */ - - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); - else if ((tr.buf[1] & 0x02) == 0x00) { - printf("cannot execute command, write is disabled\n"); - exit(1); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } else if ((tr.buf[1] & 0x02) == 0x00) { + printf("\nCannot execute WRITE command, write is disabled\n\n"); + return; } - bytes_chunks = num_bytes / 64; - remaining_bytes = num_bytes % 64; + bytes_chunks = nbytes / 4; + remaining_bytes = nbytes % 4; + + printf("\n\nWRITE operation in progress, please do not " + " stop in between.\n\n"); for (i = 0; i < bytes_chunks; i++) { - tr.buf[0] = WREN; + tr.buf[0] = ROM_WREN; tr.direction = 0; tr.len = 0; tr.addr_present = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing WREN command\n\n"); + return; + } /* Command with data and address */ memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = WRITE; + tr.buf[0] = ROM_WRITE; tr.direction = TRANSMIT; /* * We will store the address into the buffer in little * endian order. */ - tr.buf[3] = address & 0xff; - tr.buf[2] = (address >> 8) & 0xff; - tr.buf[1] = (address >> 16) & 0xff; - tr.len = 64; + tr.buf[3] = addr & 0xff; + tr.buf[2] = (addr >> 8) & 0xff; + tr.buf[1] = (addr >> 16) & 0xff; + tr.len = 4; tr.addr_present = 1; - /* Read 64 bytes from inputfile to buffer */ - if (read(inputfile_fd, &tr.buf[4], tr.len) < 0) { - perror("read error"); - exit(1); + /* Read 4 bytes from input file to buffer */ + if (read(infile_fd, &tr.buf[4], tr.len) < 0) { + printf("\nError reading from file %s\n\n", filename); + return; } + ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing WRITE command\n\n"); + return; + } - address += 64; + addr += 4; /* * Before the next loop, we need to make sure that WIP @@ -466,14 +689,16 @@ void parse_command(int fd) */ while (1) { memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } if ((tr.buf[1] & 0x01) == 0x00) break; @@ -481,161 +706,93 @@ void parse_command(int fd) } if (remaining_bytes) { - tr.buf[0] = WREN; + tr.buf[0] = ROM_WREN; tr.direction = 0; tr.len = 0; tr.addr_present = 0; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing WREN command\n\n"); + return; + } memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = WRITE; + tr.buf[0] = ROM_WRITE; tr.direction = TRANSMIT; - tr.buf[3] = address & 0xff; - tr.buf[2] = (address >> 8) & 0xff; - tr.buf[1] = (address >> 16) & 0xff; + tr.buf[3] = addr & 0xff; + tr.buf[2] = (addr >> 8) & 0xff; + tr.buf[1] = (addr >> 16) & 0xff; tr.len = remaining_bytes; tr.addr_present = 1; - if (read(inputfile_fd, &tr.buf[4], tr.len) < 0) { - perror("read error"); - exit(1); + if (read(infile_fd, &tr.buf[4], tr.len) < 0) { + printf("\nError reading from file %s\n\n", filename); + return; } + ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing WRITE command\n\n"); + return; + } while (1) { memset(&tr, 0, sizeof(struct spi_ioc_transfer)); - tr.buf[0] = RDSR; + tr.buf[0] = ROM_RDSR; tr.direction = RECEIVE; tr.addr_present = 0; tr.len = 1; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); - if (ret < 1) - pabort("can't send spi message"); + if (ret < 1) { + printf("\nError executing RDSR command\n\n"); + return; + } if ((tr.buf[1] & 0x01) == 0x00) break; } } - } else - pabort("Unrecognized command, please try again.\n"); -} - -static void print_usage(const char *prog) -{ - printf("\nUsage: sudo %s [-DCAnio] [arguments]\n\n", prog); - puts(" -D --device SPI ROM device to use\n" - " (default /dev/spirom0.0)\n\n" - " -C --command command to send to the device\n" - " (WREN/WRDI/RDSR/RDID/CHIPERASE/SECTORERASE/\n" - " BLOCKERASE/READ/WRITE)\n\n" - " -A --address offset in decimal, into the device to read\n" - " from or write to. For a ROM size of 8MB,\n" - " address can go from 0 to 8388608. Negative\n" - " offsets are not valid, but the program\n" - " won't complain and convert it to its\n" - " unsigned equivalent.\n\n" - " -n --num-bytes number of bytes to be read from or written\n" - " to. Depending on the address, this can\n" - " take values from 0 to 8388608 for a ROM\n" - " size of 8MB.\n\n" - " In case of SECTORERASE and BLOCKERASE\n" - " commands, num-bytes actually takes the\n" - " number of sectors and blocks to be erased\n" - " respectively, rather than the number of\n" - " bytes.\n\n" - " -i --input-file file to be used as input.\n\n" - " -o --output-file file to be used for output. Remember that if\n" - " an existing filename is given, its contents\n" - " will be overwritten.\n" - " -l --license displays the terms of LICENSE for this application\n\n"); - exit(1); -} - -static void parse_opts(int argc, char *argv[]) -{ - if (argc == 1) - print_usage(argv[0]); - while (1) { - static const struct option lopts[] = { - { "device", 1, 0, 'D' }, - { "command", 1, 0, 'C' }, - { "address", 1, 0, 'A' }, - { "num-bytes", 1, 0, 'n' }, - { "input-file", 1, 0, 'i' }, - { "output-file", 1, 0, 'o' }, - { "license", 0, 0, 'l' }, - { NULL, 0, 0, 0 }, - }; - int c; - - c = getopt_long(argc, argv, "D:C:A:n:i:o:l", lopts, NULL); - - if (c == -1) - break; - - switch (c) { - case 'D': - device = optarg; - break; - case 'C': - memset(command, sizeof(command), 0); - strncpy(command, optarg, sizeof(command)); - break; - case 'A': - address = atol(optarg); - break; - case 'n': - num_bytes = atoi(optarg); - break; - case 'i': - inputfile_fd = open(optarg, O_RDONLY); - if (inputfile_fd < 0) { - printf("Error opening %s\n", optarg); - exit(1); - } - break; - case 'o': - outfile_fd = open(optarg, O_WRONLY | O_CREAT | - O_TRUNC, 0644); - if(outfile_fd < 0) { - printf("Error opening %s\n", optarg); - exit(1); - } - break; - case 'l': - show_license(); - exit(0);; - default: - print_usage(argv[0]); - break; - } + printf("\n\n...WRITE completed successfully\n\n"); + close(infile_fd); + } else if (strncmp(cmdline, "license", 7) == 0) { + show_license(); + } else if (strncmp(cmdline, "exit", 4) == 0) { + printf("\nExiting...\n"); + close(fd); + exit(EXIT_SUCCESS); + } else if (strncmp(cmdline, "help", 4) == 0) { + print_usage(); + } else { + printf("\nUnknown command\n"); + print_usage(); } } -int main(int argc, char *argv[]) +int main(void) { - int ret = 0; - int fd; + char *cmdline= NULL; printf("SPI sample application version: %s\n", SPI_APP_VERSION); printf("Copyright (c) 2014, Advanced Micro Devices, Inc.\n" "This sample application comes with ABSOLUTELY NO WARRANTY;\n" "This is free software, and you are welcome to redistribute it\n" - "under certain conditions; type `license' for details.\n\n"); + "under certain conditions; type `license` for details.\n\n"); - parse_opts(argc, argv); + /* Set the signal handler */ + signal(SIGINT, sighandler); - fd = open(device, O_RDWR); - if (fd < 0) - pabort("can't open device"); + while (1) { + cmdline = readline(show_prompt()); + parse_cmd(cmdline); + /* Free the memory malloc'ed by readline */ + free(cmdline); + } - parse_command(fd); + /* Restore the default signal handler */ + signal(SIGINT, SIG_DFL); - return ret; + /* Should never reach here */ + return 0; } diff --git a/meta-steppeeagle/recipes-applications/spi-test/files/spirom.h b/meta-steppeeagle/recipes-applications/spi-test/files/spirom.h index 750719a6..941b357a 100644 --- a/meta-steppeeagle/recipes-applications/spi-test/files/spirom.h +++ b/meta-steppeeagle/recipes-applications/spi-test/files/spirom.h @@ -39,5 +39,15 @@ struct spi_ioc_transfer { ? ((N)*(sizeof (struct spi_ioc_transfer))) : 0) #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)]) +/* SPI ROM command codes */ +#define ROM_WREN 0x06 +#define ROM_WRDI 0x04 +#define ROM_RDSR 0x05 +#define ROM_RDID 0x9F +#define ROM_CHIP_ERASE 0x60 +#define ROM_SECTOR_ERASE 0x20 +#define ROM_BLOCK_ERASE 0xD8 +#define ROM_READ 0x03 +#define ROM_WRITE 0x02 #endif /* SPIROM_H */ diff --git a/meta-steppeeagle/recipes-applications/spi-test/spi-test_1.0.bb b/meta-steppeeagle/recipes-applications/spi-test/spi-test_1.0.bb index 83593cb4..b374d9f8 100644 --- a/meta-steppeeagle/recipes-applications/spi-test/spi-test_1.0.bb +++ b/meta-steppeeagle/recipes-applications/spi-test/spi-test_1.0.bb @@ -2,8 +2,8 @@ DESCRIPTION = "Sample application for AMD SPI driver" SECTION = "applications" LICENSE = "BSD" DEPENDS = "readline" -LIC_FILES_CHKSUM = "file://spirom-test.c;md5=c6d80587d583668ffbfb5828abd58878 \ - file://spirom.h;md5=56f117ed31b82b02182c7a491364d112 \ +LIC_FILES_CHKSUM = "file://spirom-test.c;md5=3065341fac5fc1255711c219f00f7324 \ + file://spirom.h;md5=8de0c535224dbd8ecd2f40ef29c15d0a \ " PR = "r1" diff --git a/meta-steppeeagle/recipes-kernel/amd-spi/amd-spi_1.0.bb b/meta-steppeeagle/recipes-kernel/amd-spi/amd-spi_1.0.bb index f4304d4d..608b99c6 100644 --- a/meta-steppeeagle/recipes-kernel/amd-spi/amd-spi_1.0.bb +++ b/meta-steppeeagle/recipes-kernel/amd-spi/amd-spi_1.0.bb @@ -1,9 +1,9 @@ DESCRIPTION = "This kernel module provides support for AMD SPI controller driver" LICENSE = "BSD | GPLv2" -LIC_FILES_CHKSUM = "file://spi_amd.c;md5=3cbc6410f1e2b6009f1a74731f6fc557 \ - file://spi_amd.h;md5=2233c2a926f120b07153e3ea0ba7474f \ +LIC_FILES_CHKSUM = "file://spi_amd.c;md5=053ef6a02a8242fbb536a45df556c7a7 \ + file://spi_amd.h;md5=b73106fb4d18369d420b9de1d1406b3a \ file://spirom.c;md5=1f5bba5ab39fb0759286aab09b55bc84 \ - file://spirom.h;md5=56f117ed31b82b02182c7a491364d112 \ + file://spirom.h;md5=8de0c535224dbd8ecd2f40ef29c15d0a \ file://Makefile;md5=8ea80a6d4ae15bcf922d090df6cfdd4c \ " diff --git a/meta-steppeeagle/recipes-kernel/amd-spi/files/spi_amd.c b/meta-steppeeagle/recipes-kernel/amd-spi/files/spi_amd.c index 554bd352..52e1b1a5 100644 --- a/meta-steppeeagle/recipes-kernel/amd-spi/files/spi_amd.c +++ b/meta-steppeeagle/recipes-kernel/amd-spi/files/spi_amd.c @@ -45,9 +45,9 @@ struct amd_spi { u32 rom_addr; struct spi_master *master; struct amd_platform_data controller_data; - spinlock_t lock; struct task_struct *kthread_spi; struct list_head msg_queue; + wait_queue_head_t wq; }; static struct pci_device_id amd_spi_pci_device_id[] = { @@ -149,7 +149,9 @@ static void amd_spi_execute_opcode(struct spi_master *master) spi_busy = (ioread32((u8 *)amd_spi->io_remap_addr + AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; while (spi_busy) { + set_current_state(TASK_INTERRUPTIBLE); schedule(); + set_current_state(TASK_RUNNING); spi_busy = (ioread32((u8 *)amd_spi->io_remap_addr + AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; } @@ -193,29 +195,17 @@ static int amd_spi_master_setup(struct spi_device *spi) return 0; } -static int amd_spi_master_transfer(struct spi_device *spi, +static int amd_spi_master_transfer(struct spi_master *master, struct spi_message *msg) { - struct spi_master *master = spi->master; struct amd_spi *amd_spi = spi_master_get_devdata(master); /* - * We will just add this message to the message queue set up by - * the controller, and let the kernel thread handle it later. - */ - msg->status = -EINPROGRESS; - msg->actual_length = 0; - msg->spi = spi; - /* - * There could be a situation when we are running on this processor - * and trying to add element to the end of the message queue, but - * at the same time the kernel thread is running on another processor - * and trying to find out if the list is empty or not. So protect - * against such contention. Simple spin_lock() should do. + * Add new message to the queue and let the kernel thread know + * about it. */ - spin_lock(&amd_spi->lock); list_add_tail(&msg->queue, &amd_spi->msg_queue); - spin_unlock(&amd_spi->lock); + wake_up_interruptible(&amd_spi->wq); return 0; } @@ -241,25 +231,19 @@ static int amd_spi_thread(void *t) * 64-bytes of data and 3-bytes of address. */ while (1) { - /* break condition */ + /* + * Let us wait on a wait queue till the message queue is empty. + */ + wait_event_interruptible(amd_spi->wq, + !list_empty(&amd_spi->msg_queue)); + + /* stop condition */ if (kthread_should_stop()) { set_current_state(TASK_RUNNING); break; } /* - * If the message queue is empty, then there is no need to waste - * CPU cycles. So we let other processes execute, and continue - * from the beginning of the loop when we next get to run. - */ - spin_lock(&amd_spi->lock); - if (list_empty(&amd_spi->msg_queue)) { - spin_unlock(&amd_spi->lock); - schedule(); - continue; - } - - /* * Else, pull the very first message from the queue and process * all transfers within that message. And process the messages * in a pure linear fashion. We also remove the spi_message @@ -268,7 +252,6 @@ static int amd_spi_thread(void *t) message = list_entry(amd_spi->msg_queue.next, struct spi_message, queue); list_del_init(&message->queue); - spin_unlock(&amd_spi->lock); /* We store the CS# line to be used for this spi_message */ amd_spi->controller_data.chip_select = @@ -372,7 +355,7 @@ static int amd_spi_thread(void *t) message->actual_length = tx_len + rx_len + 1 ; /* complete the transaction */ message->status = 0; - message->complete(message->context); + spi_finalize_current_message(master); } return 0; @@ -416,8 +399,8 @@ static int amd_spi_pci_probe(struct pci_dev *pdev, } dev_dbg(dev, "io_base_addr: 0x%.8lx, io_remap_address: %p\n", amd_spi->io_base_addr, amd_spi->io_remap_addr); - spin_lock_init(&amd_spi->lock); INIT_LIST_HEAD(&amd_spi->msg_queue); + init_waitqueue_head(&amd_spi->wq); amd_spi->kthread_spi = kthread_run(amd_spi_thread, amd_spi, "amd_spi_thread"); @@ -430,7 +413,7 @@ static int amd_spi_pci_probe(struct pci_dev *pdev, master->mode_bits = 0; master->flags = 0; master->setup = amd_spi_master_setup; - master->transfer = amd_spi_master_transfer; + master->transfer_one_message = amd_spi_master_transfer; /* Register the controller with SPI framework */ err = spi_register_master(master); if (err) { diff --git a/meta-steppeeagle/recipes-kernel/amd-spi/files/spi_amd.h b/meta-steppeeagle/recipes-kernel/amd-spi/files/spi_amd.h index ea5181d7..21fa972f 100644 --- a/meta-steppeeagle/recipes-kernel/amd-spi/files/spi_amd.h +++ b/meta-steppeeagle/recipes-kernel/amd-spi/files/spi_amd.h @@ -2,7 +2,7 @@ #define SPI_AMD_H #define DRIVER_NAME "spi_amd" -#define SPI_VERSION "0.1" +#define SPI_VERSION "1.0" #define AMD_SPI_CTRL0_REG 0x00 #define AMD_SPI_EXEC_CMD (0x1 << 16) diff --git a/meta-steppeeagle/recipes-kernel/amd-spi/files/spirom.h b/meta-steppeeagle/recipes-kernel/amd-spi/files/spirom.h index 750719a6..941b357a 100644 --- a/meta-steppeeagle/recipes-kernel/amd-spi/files/spirom.h +++ b/meta-steppeeagle/recipes-kernel/amd-spi/files/spirom.h @@ -39,5 +39,15 @@ struct spi_ioc_transfer { ? ((N)*(sizeof (struct spi_ioc_transfer))) : 0) #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)]) +/* SPI ROM command codes */ +#define ROM_WREN 0x06 +#define ROM_WRDI 0x04 +#define ROM_RDSR 0x05 +#define ROM_RDID 0x9F +#define ROM_CHIP_ERASE 0x60 +#define ROM_SECTOR_ERASE 0x20 +#define ROM_BLOCK_ERASE 0xD8 +#define ROM_READ 0x03 +#define ROM_WRITE 0x02 #endif /* SPIROM_H */ |