/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Note about accesses to the AST2400 internal memory map: * * There are two ways to genrate accesses to the AHB bus of the AST2400 * from the host. The LPC->AHB bridge and the iLPC->AHB bridge. * * LPC->AHB bridge * --------------- * * This bridge directly converts memory or firmware accesses using * a set of registers for establishing a remapping window. We prefer * using FW space as normal memory space is limited to byte accesses * to a fixed 256M window, while FW space allows us to use different * access sizes and to control the IDSEL bits which essentially enable * a full 4G address space. * * The way FW accesses map onto AHB is controlled via two registers * in the BMC's LPC host controller: * * HICR7 at 0x1e789088 [31:16] : ADRBASE * [15:00] : HWMBASE * * HICR8 at 0x1e78908c [31:16] : ADRMASK * [15:00] : HWNCARE * * All decoding/remapping happens on the top 16 bits of the LPC address * named LPC_ADDR as follow: * * - For decoding, LPC_ADDR bits are compared with HWMBASE if the * corresponding bit in HWNCARE is 0. * * - For remapping, the AHB address is constructed by taking bits * from LPC_ADDR if the corresponding bit in ADRMASK is 0 or in * ADRBASE if the corresponding bit in ADRMASK is 1 * * Example of 2MB SPI flash, LPC 0xFCE00000~0xFCFFFFFF onto * AHB 0x30000000~0x301FFFFF (SPI flash) * * ADRBASE=0x3000 HWMBASE=0xFCE0 * ADRMASK=0xFFE0 HWNCARE=0x001F * * This comes pre-configured by the BMC or HostBoot to access the PNOR * flash from IDSEL 0 as follow: * * ADRBASE=0x3000 HWMBASE=0x0e00 for 32MB * ADRMASK=0xfe00 HWNCARE=0x01ff * * Which means mapping of LPC 0x0e000000..0x0fffffff onto * AHB 0x30000000..0x31ffffff * * iLPC->AHB bridge * --------------- * * This bridge is hosted in the SuperIO part of the BMC and is * controlled by a series of byte-sized registers accessed indirectly * via IO ports 0x2e and 0x2f. * * Via these, byte by byte, we can construct an AHB address and * fill a data buffer to trigger a write cycle, or we can do a * read cycle and read back the data, byte after byte. * * This is fairly convoluted and slow but works regardless of what * mapping was established in the LPC->AHB bridge. * * For the time being, we use the iLPC->AHB for everything except * pnor accesses. In the long run, we will reconfigure the LPC->AHB * to provide more direct access to all of the BMC address space but * we'll only do that after the boot script/program on the BMC is * updated to restore the bridge to a state compatible with the SBE * expectations on boot. */ #include #include #include #include #include "ast.h" #define BMC_SIO_SCR28 0x28 #define BOOT_FLAGS_VERSION 0x42 /* * SIO Register 0x29: Boot Flags (normal bit ordering) * * [7:6] Hostboot Boot mode: * 00 : Normal * 01 : Terminate on first error * 10 : istep mode * 11 : reserved * [5:4] Boot options * 00 : reserved * 01 : Memboot * 10 : Clear gard * 11 : reserved * [ 3 ] BMC mbox PNOR driver * [2:0] Hostboot Log level: * 000 : Normal * 001 : Enable Scan trace * xxx : reserved */ #define BMC_SIO_SCR29 0x29 #define BMC_SIO_SCR29_MBOX 0x08 #define BMC_SIO_SCR29_MEMBOOT 0x10 /* * SIO Register 0x2d: Platform Flags (normal bit ordering) * * [ 7 ] Hostboot configures SUART * [ 6 ] Hostboot configures VUART * [5:1] Reserved * [ 0 ] Isolate Service Processor */ #define BMC_SIO_PLAT_FLAGS 0x2d #define BMC_SIO_PLAT_ISOLATE_SP 0x01 enum { BMC_SIO_DEV_NONE = -1, BMC_SIO_DEV_UART1 = 2, BMC_SIO_DEV_UART2 = 3, BMC_SIO_DEV_SWC = 4, BMC_SIO_DEV_KBC = 5, BMC_SIO_DEV_P80 = 7, BMC_SIO_DEV_UART3 = 0xb, BMC_SIO_DEV_UART4 = 0xc, BMC_SIO_DEV_LPC2AHB = 0xd, BMC_SIO_DEV_MBOX = 0xe, }; static struct lock bmc_sio_lock = LOCK_UNLOCKED; static int bmc_sio_cur_dev = BMC_SIO_DEV_NONE; /* * SuperIO indirect accesses */ static void bmc_sio_outb(uint8_t val, uint8_t reg) { lpc_outb(reg, 0x2e); lpc_outb(val, 0x2f); } static uint8_t bmc_sio_inb(uint8_t reg) { lpc_outb(reg, 0x2e); return lpc_inb(0x2f); } static void bmc_sio_get(int dev) { lock(&bmc_sio_lock); if (bmc_sio_cur_dev == dev || dev < 0) return; if (bmc_sio_cur_dev == BMC_SIO_DEV_NONE) { /* Send SuperIO password */ lpc_outb(0xa5, 0x2e); lpc_outb(0xa5, 0x2e); } /* Select logical dev */ bmc_sio_outb(dev, 0x07); bmc_sio_cur_dev = dev; } static void bmc_sio_put(bool lock_sio) { if (lock_sio) { /* Re-lock SuperIO */ lpc_outb(0xaa, 0x2e); bmc_sio_cur_dev = BMC_SIO_DEV_NONE; } unlock(&bmc_sio_lock); } /* * AHB accesses via iLPC->AHB in SuperIO. Works on byteswapped * values (ie. Little Endian registers) */ static void bmc_sio_ahb_prep(uint32_t reg, uint8_t type) { /* Enable iLPC->AHB */ bmc_sio_outb(0x01, 0x30); /* Address */ bmc_sio_outb((reg >> 24) & 0xff, 0xf0); bmc_sio_outb((reg >> 16) & 0xff, 0xf1); bmc_sio_outb((reg >> 8) & 0xff, 0xf2); bmc_sio_outb((reg ) & 0xff, 0xf3); /* bytes cycle type */ bmc_sio_outb(type, 0xf8); } static void bmc_sio_ahb_writel(uint32_t val, uint32_t reg) { bmc_sio_get(BMC_SIO_DEV_LPC2AHB); bmc_sio_ahb_prep(reg, 2); /* Write data */ bmc_sio_outb(val >> 24, 0xf4); bmc_sio_outb(val >> 16, 0xf5); bmc_sio_outb(val >> 8, 0xf6); bmc_sio_outb(val , 0xf7); /* Trigger */ bmc_sio_outb(0xcf, 0xfe); bmc_sio_put(false); } static uint32_t bmc_sio_ahb_readl(uint32_t reg) { uint32_t val = 0; bmc_sio_get(BMC_SIO_DEV_LPC2AHB); bmc_sio_ahb_prep(reg, 2); /* Trigger */ bmc_sio_inb(0xfe); /* Read results */ val = (val << 8) | bmc_sio_inb(0xf4); val = (val << 8) | bmc_sio_inb(0xf5); val = (val << 8) | bmc_sio_inb(0xf6); val = (val << 8) | bmc_sio_inb(0xf7); bmc_sio_put(false); return val; } /* * External API * * We only support 4-byte accesses to all of AHB. We additionally * support 1-byte accesses to the flash area only. * * We could support all access sizes via iLPC but we don't need * that for now. */ void ast_ahb_writel(uint32_t val, uint32_t reg) { /* For now, always use iLPC->AHB, it will byteswap */ bmc_sio_ahb_writel(val, reg); } uint32_t ast_ahb_readl(uint32_t reg) { /* For now, always use iLPC->AHB, it will byteswap */ return bmc_sio_ahb_readl(reg); } static void ast_setup_sio_irq_polarity(void) { /* Select logical dev 2 */ bmc_sio_get(BMC_SIO_DEV_UART1); bmc_sio_outb(0x01, 0x71); /* level low */ bmc_sio_put(false); /* Select logical dev 3 */ bmc_sio_get(BMC_SIO_DEV_UART2); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_put(false); /* Select logical dev 4 */ bmc_sio_get(BMC_SIO_DEV_SWC); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_put(false); /* Select logical dev 5 */ bmc_sio_get(BMC_SIO_DEV_KBC); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_outb(0x01, 0x73); /* irq level low */ bmc_sio_put(false); /* Select logical dev 7 */ bmc_sio_get(BMC_SIO_DEV_P80); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_put(false); /* Select logical dev d */ bmc_sio_get(BMC_SIO_DEV_UART3); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_put(false); /* Select logical dev c */ bmc_sio_get(BMC_SIO_DEV_UART4); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_put(false); /* Select logical dev d */ bmc_sio_get(BMC_SIO_DEV_LPC2AHB); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_put(false); /* Select logical dev e */ bmc_sio_get(BMC_SIO_DEV_MBOX); bmc_sio_outb(0x01, 0x71); /* irq level low */ bmc_sio_put(true); } bool ast_sio_is_enabled(void) { bool enabled; int64_t rc; lock(&bmc_sio_lock); /* * Probe by attempting to lock the SIO device, this way the * post-condition is that the SIO device is locked or not able to be * unlocked. This turns out neater than trying to use the unlock code. */ rc = lpc_probe_write(OPAL_LPC_IO, 0x2e, 0xaa, 1); if (rc) { enabled = false; /* If we can't lock it, then we can't unlock it either */ goto out; } /* * Now that we know that is locked and able to be unlocked, unlock it * if skiboot's recorded device state indicates it was previously * unlocked. */ if (bmc_sio_cur_dev != BMC_SIO_DEV_NONE) { /* Send SuperIO password */ lpc_outb(0xa5, 0x2e); lpc_outb(0xa5, 0x2e); /* Ensure the previously selected logical dev is selected */ bmc_sio_outb(bmc_sio_cur_dev, 0x07); } enabled = true; out: unlock(&bmc_sio_lock); return enabled; } bool ast_sio_init(void) { bool enabled = ast_sio_is_enabled(); /* Configure all AIO interrupts to level low */ if (enabled) ast_setup_sio_irq_polarity(); return enabled; } bool ast_io_is_rw(void) { return !(ast_ahb_readl(LPC_HICRB) & LPC_HICRB_ILPC_DISABLE); } bool ast_io_init(void) { return ast_io_is_rw(); } bool ast_lpc_fw_ipmi_hiomap(void) { return platform.bmc->sw->ipmi_oem_hiomap_cmd != 0; } bool ast_lpc_fw_mbox_hiomap(void) { struct dt_node *n; n = dt_find_compatible_node(dt_root, NULL, "mbox"); return n != NULL; } bool ast_lpc_fw_maps_flash(void) { uint8_t boot_version; uint8_t boot_flags; boot_version = bmc_sio_inb(BMC_SIO_SCR28); if (boot_version != BOOT_FLAGS_VERSION) return true; boot_flags = bmc_sio_inb(BMC_SIO_SCR29); return !(boot_flags & BMC_SIO_SCR29_MEMBOOT); } bool ast_scratch_reg_is_mbox(void) { uint8_t boot_version; uint8_t boot_flags; boot_version = bmc_sio_inb(BMC_SIO_SCR28); if (boot_version != BOOT_FLAGS_VERSION) return false; boot_flags = bmc_sio_inb(BMC_SIO_SCR29); return boot_flags & BMC_SIO_SCR29_MBOX; } void ast_setup_ibt(uint16_t io_base, uint8_t irq) { uint32_t v; v = bmc_sio_ahb_readl(LPC_iBTCR0); v = v & ~(0xfffffc00u); v = v | (((uint32_t)io_base) << 16); v = v | (((uint32_t)irq) << 12); bmc_sio_ahb_writel(v, LPC_iBTCR0); } bool ast_is_vuart1_enabled(void) { uint32_t v; v = bmc_sio_ahb_readl(VUART1_GCTRLA); return !!(v & 1); } void ast_setup_vuart1(uint16_t io_base, uint8_t irq) { uint32_t v; /* IRQ level low */ v = bmc_sio_ahb_readl(VUART1_GCTRLA); v = v & ~2u; bmc_sio_ahb_writel(v, VUART1_GCTRLA); v = bmc_sio_ahb_readl(VUART1_GCTRLA); /* IRQ number */ v = bmc_sio_ahb_readl(VUART1_GCTRLB); v = (v & ~0xf0u) | (irq << 4); bmc_sio_ahb_writel(v, VUART1_GCTRLB); /* Address */ bmc_sio_ahb_writel(io_base & 0xff, VUART1_ADDRL); bmc_sio_ahb_writel(io_base >> 8, VUART1_ADDRH); } /* Setup SuperIO UART 1 */ void ast_setup_sio_uart1(uint16_t io_base, uint8_t irq) { bmc_sio_get(BMC_SIO_DEV_UART1); /* Disable UART1 for configuration */ bmc_sio_outb(0x00, 0x30); /* Configure base and interrupt */ bmc_sio_outb(io_base >> 8, 0x60); bmc_sio_outb(io_base & 0xff, 0x61); bmc_sio_outb(irq, 0x70); bmc_sio_outb(0x01, 0x71); /* level low */ /* Enable UART1 */ bmc_sio_outb(0x01, 0x30); bmc_sio_put(true); } void ast_disable_sio_uart1(void) { bmc_sio_get(BMC_SIO_DEV_UART1); /* Disable UART1 */ bmc_sio_outb(0x00, 0x30); bmc_sio_put(true); } void ast_setup_sio_mbox(uint16_t io_base, uint8_t irq) { bmc_sio_get(BMC_SIO_DEV_MBOX); /* Disable for configuration */ bmc_sio_outb(0x00, 0x30); bmc_sio_outb(io_base >> 8, 0x60); bmc_sio_outb(io_base & 0xff, 0x61); bmc_sio_outb(irq, 0x70); bmc_sio_outb(0x01, 0x71); /* level low */ /* Enable MailBox */ bmc_sio_outb(0x01, 0x30); bmc_sio_put(true); }