#define _GNU_SOURCE /* for strcasestr */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "io.h" /* Big endian warning/note: * * The register accessors return byteswapped data for registers */ uint32_t (*ast_ahb_readl)(uint32_t offset); void (*ast_ahb_writel)(uint32_t val, uint32_t offset); int (*ast_copy_to_ahb)(uint32_t reg, const void *src, uint32_t len); int (*ast_copy_from_ahb)(void *dst, uint32_t reg, uint32_t len); static enum ppc_platform { plat_unknown, plat_rhesus, plat_ast_bmc, } ppc_platform; static int lpc_io_fd = -1, lpc_fw_fd = -1; static uint32_t lpc_old_flash_reg; static uint32_t ahb_flash_base, ahb_flash_size, lpc_flash_offset; static void lpc_outb(uint8_t val, uint16_t port) { int rc; lseek(lpc_io_fd, port, SEEK_SET); rc = write(lpc_io_fd, &val, 1); if (rc != 1) { perror("Can't write to LPC IO"); exit(1); } } static uint8_t lpc_inb(uint16_t port) { uint8_t val; int rc; lseek(lpc_io_fd, port, SEEK_SET); rc = read(lpc_io_fd, &val, 1); if (rc != 1) { perror("Can't read from LPC IO"); exit(1); } return val; } int lpc_fw_write32(uint32_t val, uint32_t addr) { int rc; /* The value passed in is in big endian always */ lseek(lpc_fw_fd, addr, SEEK_SET); rc = write(lpc_fw_fd, &val, 4); if (rc != 4) { perror("Can't write to LPC FW"); exit(1); } return 0; } int lpc_fw_read32(uint32_t *val, uint32_t addr) { int rc; lseek(lpc_fw_fd, addr, SEEK_SET); rc = read(lpc_fw_fd, val, 4); if (rc != 4) { perror("Can't read from LPC FW"); exit(1); } return 0; } static void lpc_sio_outb(uint8_t val, uint8_t reg) { lpc_outb(reg, 0x2e); lpc_outb(val, 0x2f); } static uint8_t lpc_sio_inb(uint8_t reg) { lpc_outb(reg, 0x2e); return lpc_inb(0x2f); } static void lpc_ahb_prep(uint32_t reg, uint8_t type) { /* Address */ lpc_sio_outb((reg >> 24) & 0xff, 0xf0); lpc_sio_outb((reg >> 16) & 0xff, 0xf1); lpc_sio_outb((reg >> 8) & 0xff, 0xf2); lpc_sio_outb((reg ) & 0xff, 0xf3); /* 4 bytes cycle */ lpc_sio_outb(type, 0xf8); } static void lpc_ahb_writel(uint32_t val, uint32_t reg) { lpc_ahb_prep(reg, 2); /* Write data */ lpc_sio_outb(val >> 24, 0xf4); lpc_sio_outb(val >> 16, 0xf5); lpc_sio_outb(val >> 8, 0xf6); lpc_sio_outb(val , 0xf7); /* Trigger */ lpc_sio_outb(0xcf, 0xfe); } static uint32_t lpc_ahb_readl(uint32_t reg) { uint32_t val = 0; lpc_ahb_prep(reg, 2); /* Trigger */ lpc_sio_inb(0xfe); /* Read results */ val = (val << 8) | lpc_sio_inb(0xf4); val = (val << 8) | lpc_sio_inb(0xf5); val = (val << 8) | lpc_sio_inb(0xf6); val = (val << 8) | lpc_sio_inb(0xf7); return val; } static void lpc_ahb_init(bool bmc_flash) { uint32_t b; /* Send SuperIO password */ lpc_outb(0xa5, 0x2e); lpc_outb(0xa5, 0x2e); /* Select logical dev d */ lpc_sio_outb(0x0d, 0x07); /* Enable iLPC->AHB */ lpc_sio_outb(0x01, 0x30); /* Save flash base */ lpc_old_flash_reg = b = lpc_ahb_readl(LPC_CTRL_BASE + 0x88); /* Upate flash base */ if (bmc_flash) { ahb_flash_base = BMC_FLASH_BASE; ahb_flash_size = BMC_FLASH_SIZE; } else { ahb_flash_base = PNOR_FLASH_BASE; ahb_flash_size = PNOR_FLASH_SIZE; } lpc_flash_offset = 0x0e000000; b = (b & 0x0000ffff) | ahb_flash_base; lpc_ahb_writel(b, LPC_CTRL_BASE + 0x88); b = lpc_ahb_readl(LPC_CTRL_BASE + 0x88); } static int lpc_ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len) { int rc; if (reg < ahb_flash_base || (reg + len) > (ahb_flash_base + ahb_flash_size)) return -1; reg = (reg - ahb_flash_base) + lpc_flash_offset; lseek(lpc_fw_fd, reg, SEEK_SET); rc = read(lpc_fw_fd, dst, len); if (rc != len) { perror("Can't read bulk from LPC FW"); exit(1); } return 0; } static int lpc_ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len) { int rc; if (reg < ahb_flash_base || (reg + len) > (ahb_flash_base + ahb_flash_size)) return -1; reg = (reg - ahb_flash_base) + lpc_flash_offset; lseek(lpc_fw_fd, reg, SEEK_SET); rc = write(lpc_fw_fd, src, len); if (rc != len) { perror("Can't write bulk from LPC FW"); exit(1); } return 0; } /* * Write protect: TODO use custom IPMI to control lock from BMC */ static uint32_t lpc_gpio_ctl_readl(uint32_t offset) { return lpc_ahb_readl(GPIO_CTRL_BASE + offset); } static void lpc_gpio_ctl_writel(uint32_t val, uint32_t offset) { lpc_ahb_writel(val, GPIO_CTRL_BASE + offset); } bool set_wrprotect(bool protect) { uint32_t reg; bool was_protected; if (ppc_platform != plat_ast_bmc) return false; reg = lpc_gpio_ctl_readl(0x20); was_protected = !!(reg & 0x00004000); if (protect) reg |= 0x00004000; /* GPIOF[6] value */ else reg &= ~0x00004000; /* GPIOF[6] value */ lpc_gpio_ctl_writel(reg, 0x20); reg = lpc_gpio_ctl_readl(0x24); reg |= 0x00004000; /* GPIOF[6] direction */ lpc_gpio_ctl_writel(reg, 0x24); return was_protected; } static void open_lpc(bool bmc_flash) { lpc_fw_fd = open("/sys/kernel/debug/powerpc/lpc/fw", O_RDWR); if (lpc_fw_fd < 0) { perror("can't open LPC MEM"); exit(1); } if (ppc_platform != plat_ast_bmc) return; lpc_io_fd = open("/sys/kernel/debug/powerpc/lpc/io", O_RDWR); if (lpc_io_fd < 0) { perror("can't open LPC IO"); exit(1); } ast_ahb_readl = lpc_ahb_readl; ast_ahb_writel = lpc_ahb_writel; ast_copy_to_ahb = lpc_ast_copy_to_ahb; ast_copy_from_ahb = lpc_ast_copy_from_ahb; lpc_ahb_init(bmc_flash); } void close_devs(void) { if (lpc_io_fd < 0 || lpc_fw_fd < 0) return; if (ppc_platform != plat_ast_bmc) return; /* Restore flash base */ lpc_ahb_writel(lpc_old_flash_reg, LPC_CTRL_BASE + 0x88); } static void open_pci(bool bmc_flash) { /* XXX */ fprintf(stderr, "WARNING: PCI access method not implemented !\n"); fprintf(stderr, " Use -l or --lpc\n"); exit(1); } static void identify_platform(void) { FILE *cpuinfo; char *lptr = NULL; size_t lsize = 0; bool found = false; ppc_platform = plat_unknown; cpuinfo = fopen("/proc/cpuinfo", "r"); if (!cpuinfo) { perror("Can't open /proc/cpuinfo"); exit(1); } while(!found && getline(&lptr, &lsize, cpuinfo) >= 0) { if (!strncmp(lptr, "model", 5)) { if (strcasestr(lptr, "rhesus")) ppc_platform = plat_rhesus; else if (strcasestr(lptr, "palmetto")) ppc_platform = plat_ast_bmc; found = true; } free(lptr); lptr = NULL; lsize = 0; } } void open_devs(bool use_lpc, bool bmc_flash) { if (ppc_platform == plat_unknown) { fprintf(stderr, "Unsupported platform !\n"); exit(1); } if (use_lpc) open_lpc(bmc_flash); else open_pci(bmc_flash); } void check_platform(bool *has_sfc, bool *has_ast) { identify_platform(); *has_sfc = ppc_platform == plat_rhesus; *has_ast = ppc_platform == plat_ast_bmc; }