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>
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;
+}