diff options
author | Simon Glass <sjg@chromium.org> | 2016-03-12 05:07:19 (GMT) |
---|---|---|
committer | Bin Meng <bmeng.cn@gmail.com> | 2016-03-17 02:27:25 (GMT) |
commit | 1e6f4e58862088f31cae350d73f0b1162d0ceb46 (patch) | |
tree | 7e8a990f9adc382bd285ea33a443b9becd8835de /arch/x86/cpu/broadwell/iobp.c | |
parent | 2f3f477b77d3a528de41e52a8ba874fd47fb6513 (diff) | |
download | u-boot-1e6f4e58862088f31cae350d73f0b1162d0ceb46.tar.xz |
x86: broadwell: Add a PCH driver
Add a driver for the broadwell low-power platform controller hub.
Signed-off-by: Simon Glass <sjg@chromium.org>
Acked-by: Bin Meng <bmeng.cn@gmail.com>
Diffstat (limited to 'arch/x86/cpu/broadwell/iobp.c')
-rw-r--r-- | arch/x86/cpu/broadwell/iobp.c | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/arch/x86/cpu/broadwell/iobp.c b/arch/x86/cpu/broadwell/iobp.c new file mode 100644 index 0000000..5eed849 --- /dev/null +++ b/arch/x86/cpu/broadwell/iobp.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * Modified from coreboot + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <errno.h> +#include <asm/intel_regs.h> +#include <asm/io.h> +#include <asm/arch/pch.h> + +#define IOBP_RETRY 1000 + +/* IO Buffer Programming */ +#define IOBPIRI 0x2330 +#define IOBPD 0x2334 +#define IOBPS 0x2338 +#define IOBPS_READY 0x0001 +#define IOBPS_TX_MASK 0x0006 +#define IOBPS_MASK 0xff00 +#define IOBPS_READ 0x0600 +#define IOBPS_WRITE 0x0700 +#define IOBPU 0x233a +#define IOBPU_MAGIC 0xf000 +#define IOBP_PCICFG_READ 0x0400 +#define IOBP_PCICFG_WRITE 0x0500 + +static inline int iobp_poll(void) +{ + unsigned try; + + for (try = IOBP_RETRY; try > 0; try--) { + u16 status = readw(RCB_REG(IOBPS)); + if ((status & IOBPS_READY) == 0) + return 1; + udelay(10); + } + + printf("IOBP: timeout waiting for transaction to complete\n"); + return 0; +} + +int pch_iobp_trans_start(u32 address, int op) +{ + if (!iobp_poll()) + return 0; + + /* Set the address */ + writel(address, RCB_REG(IOBPIRI)); + + /* READ OPCODE */ + clrsetbits_le16(RCB_REG(IOBPS), IOBPS_MASK, op); + + return 1; +} + +int pch_iobp_trans_finish(void) +{ + u16 status; + + /* Undocumented magic */ + writew(IOBPU_MAGIC, RCB_REG(IOBPU)); + + /* Set ready bit */ + setbits_le16(RCB_REG(IOBPS), IOBPS_READY); + + if (!iobp_poll()) + return 1; + + /* Check for successful transaction */ + status = readw(RCB_REG(IOBPS)); + if (status & IOBPS_TX_MASK) + return 1; + + return 0; +} + +u32 pch_iobp_read(u32 address) +{ + if (!pch_iobp_trans_start(address, IOBPS_READ)) + return 0; + if (pch_iobp_trans_finish()) { + printf("IOBP: read 0x%08x failed\n", address); + return 0; + } + + /* Read IOBP data */ + return readl(RCB_REG(IOBPD)); +} + +int pch_iobp_write(u32 address, u32 data) +{ + if (!pch_iobp_trans_start(address, IOBPS_WRITE)) + return -EIO; + + writel(data, RCB_REG(IOBPD)); + + if (pch_iobp_trans_finish()) { + printf("IOBP: write 0x%08x failed\n", address); + return -EIO; + } + + return 0; +} + +int pch_iobp_update(u32 address, u32 andvalue, u32 orvalue) +{ + u32 data = pch_iobp_read(address); + + /* Update the data */ + data &= andvalue; + data |= orvalue; + + return pch_iobp_write(address, data); +} + +int pch_iobp_exec(u32 addr, u16 op_code, u8 route_id, u32 *data, u8 *resp) +{ + if (!data || !resp) + return 0; + + *resp = -1; + if (!iobp_poll()) + return -EIO; + + writel(addr, RCB_REG(IOBPIRI)); + clrsetbits_le16(RCB_REG(IOBPS), 0xff00, op_code); + writew(IOBPU_MAGIC | route_id, RCB_REG(IOBPU)); + + writel(*data, RCB_REG(IOBPD)); + /* Set IOBPS[0] to trigger IOBP transaction*/ + setbits_le16(RCB_REG(IOBPS), 1); + + if (!iobp_poll()) + return -EIO; + + *resp = (readw(RCB_REG(IOBPS)) & IOBPS_TX_MASK) >> 1; + *data = readl(RCB_REG(IOBPD)); + + return 0; +} |