x86: broadwell: Add support for serial I/O devices

Add support for initing the I2C device and ADSP on broadwell. These are
needed for sound to work.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
diff --git a/arch/x86/cpu/broadwell/pch.c b/arch/x86/cpu/broadwell/pch.c
index 82506ba..73d3d3b 100644
--- a/arch/x86/cpu/broadwell/pch.c
+++ b/arch/x86/cpu/broadwell/pch.c
@@ -20,7 +20,9 @@
 #include <asm/arch/pch.h>
 #include <asm/arch/pm.h>
 #include <asm/arch/rcb.h>
+#include <asm/arch/serialio.h>
 #include <asm/arch/spi.h>
+#include <dm/uclass-internal.h>
 
 #define BIOS_CTRL	0xdc
 
@@ -456,6 +458,111 @@
 	cpu_set_power_limits(28);
 }
 
+/* Enable LTR Auto Mode for D21:F1-F6 */
+static void serialio_d21_ltr(u32 bar0)
+{
+	/* 1. Program BAR0 + 808h[2] = 0b */
+	clrbits_le32(bar0 + SIO_REG_PPR_GEN, SIO_REG_PPR_GEN_LTR_MODE_MASK);
+
+	/* 2. Program BAR0 + 804h[1:0] = 00b */
+	clrbits_le32(bar0 + SIO_REG_PPR_RST, SIO_REG_PPR_RST_ASSERT);
+
+	/* 3. Program BAR0 + 804h[1:0] = 11b */
+	setbits_le32(bar0 + SIO_REG_PPR_RST, SIO_REG_PPR_RST_ASSERT);
+
+	/* 4. Program BAR0 + 814h[31:0] = 00000000h */
+	writel(0, bar0 + SIO_REG_AUTO_LTR);
+}
+
+/* Select I2C voltage of 1.8V or 3.3V */
+static void serialio_i2c_voltage_sel(u32 bar0, uint voltage)
+{
+	clrsetbits_le32(bar0 + SIO_REG_PPR_GEN, SIO_REG_PPR_GEN_VOLTAGE_MASK,
+			SIO_REG_PPR_GEN_VOLTAGE(voltage));
+}
+
+/* Put Serial IO D21:F0-F6 device into desired mode */
+static void serialio_d21_mode(int sio_index, int int_pin, bool acpi_mode)
+{
+	u32 portctrl = SIO_IOBP_PORTCTRL_PM_CAP_PRSNT;
+
+	/* Snoop select 1 */
+	portctrl |= SIO_IOBP_PORTCTRL_SNOOP_SELECT(1);
+
+	/* Set interrupt pin */
+	portctrl |= SIO_IOBP_PORTCTRL_INT_PIN(int_pin);
+
+	if (acpi_mode) {
+		/* Enable ACPI interrupt mode */
+		portctrl |= SIO_IOBP_PORTCTRL_ACPI_IRQ_EN;
+	}
+
+	pch_iobp_update(SIO_IOBP_PORTCTRLX(sio_index), 0, portctrl);
+}
+
+/* Init sequence to be run once, done as part of D21:F0 (SDMA) init */
+static void serialio_init_once(bool acpi_mode)
+{
+	if (acpi_mode) {
+		/* Enable ACPI IRQ for IRQ13, IRQ7, IRQ6, IRQ5 in RCBA */
+		setbits_le32(RCB_REG(ACPIIRQEN),
+			     1 << 13 | 1 << 7 | 1 << 6 | 1 << 5);
+	}
+
+	/* Program IOBP CB000154h[12,9:8,4:0] = 1001100011111b */
+	pch_iobp_update(SIO_IOBP_GPIODF, ~0x0000131f, 0x0000131f);
+
+	/* Program IOBP CB000180h[5:0] = 111111b (undefined register) */
+	pch_iobp_update(0xcb000180, ~0x0000003f, 0x0000003f);
+}
+
+/**
+ * pch_serialio_init() - set up serial I/O devices
+ *
+ * @return 0 if OK, -ve on error
+ */
+static int pch_serialio_init(void)
+{
+	struct udevice *dev, *hda;
+	bool acpi_mode = true;
+	u32 bar0, bar1;
+	int ret;
+
+	ret = uclass_find_first_device(UCLASS_I2C, &dev);
+	if (ret)
+		return ret;
+	bar0 = dm_pci_read_bar32(dev, 0);
+	if (!bar0)
+		return -EINVAL;
+	bar1 = dm_pci_read_bar32(dev, 1);
+	if (!bar1)
+		return -EINVAL;
+
+	serialio_init_once(acpi_mode);
+	serialio_d21_mode(SIO_ID_SDMA, SIO_PIN_INTB, acpi_mode);
+
+	serialio_d21_ltr(bar0);
+	serialio_i2c_voltage_sel(bar0, 1); /* Select 1.8V always */
+	serialio_d21_mode(SIO_ID_I2C0, SIO_PIN_INTC, acpi_mode);
+	setbits_le32(bar1 + PCH_PCS, PCH_PCS_PS_D3HOT);
+
+	clrbits_le32(bar1 + PCH_PCS, PCH_PCS_PS_D3HOT);
+
+	setbits_le32(bar0 + SIO_REG_PPR_CLOCK, SIO_REG_PPR_CLOCK_EN);
+
+	/* Manually find the High-definition audio, to turn it off */
+	ret = dm_pci_bus_find_bdf(PCI_BDF(0, 0x1b, 0), &hda);
+	if (ret)
+		return -ENOENT;
+	dm_pci_clrset_config8(hda, 0x43, 0, 0x6f);
+
+	/* Route I/O buffers to ADSP function */
+	dm_pci_clrset_config8(hda, 0x42, 0, 1 << 7 | 1 << 6);
+	log_debug("HDA disabled, I/O buffers routed to ADSP\n");
+
+	return 0;
+}
+
 static int broadwell_pch_init(struct udevice *dev)
 {
 	int ret;
@@ -482,6 +589,9 @@
 		return ret;
 	pch_pm_init(dev);
 	pch_cg_init(dev);
+	ret = pch_serialio_init();
+	if (ret)
+		return ret;
 	systemagent_init();
 
 	return 0;
diff --git a/arch/x86/include/asm/arch-broadwell/serialio.h b/arch/x86/include/asm/arch-broadwell/serialio.h
new file mode 100644
index 0000000..5e98eaf
--- /dev/null
+++ b/arch/x86/include/asm/arch-broadwell/serialio.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Serial IO defintiions (taken from coreboot file of same name)
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#ifndef __ARCH_BROADWELL_SERIALIO_H_
+#define __ARCH_BROADWELL_SERIALIO_H_
+
+/* Serial IO IOBP Registers */
+#define SIO_IOBP_PORTCTRL0	0xcb000000	/* SDIO D23:F0 */
+#define  SIO_IOBP_PORTCTRL0_ACPI_IRQ_EN		BIT(5)
+#define  SIO_IOBP_PORTCTRL0_PCI_CONF_DIS	BIT(4)
+#define SIO_IOBP_PORTCTRL1	0xcb000014	/* SDIO D23:F0 */
+#define  SIO_IOBP_PORTCTRL1_SNOOP_SELECT(x)	(((x) & 3) << 13)
+#define SIO_IOBP_GPIODF		0xcb000154
+#define  SIO_IOBP_GPIODF_SDIO_IDLE_DET_EN	BIT(4)
+#define  SIO_IOBP_GPIODF_DMA_IDLE_DET_EN	BIT(3)
+#define  SIO_IOBP_GPIODF_UART_IDLE_DET_EN	BIT(2)
+#define  SIO_IOBP_GPIODF_I2C_IDLE_DET_EN	BIT(1)
+#define  SIO_IOBP_GPIODF_SPI_IDLE_DET_EN	BIT(0)
+#define  SIO_IOBP_GPIODF_UART0_BYTE_ACCESS	BIT(10)
+#define  SIO_IOBP_GPIODF_UART1_BYTE_ACCESS	BIT(11)
+#define SIO_IOBP_PORTCTRL2	0xcb000240	/* DMA D21:F0 */
+#define SIO_IOBP_PORTCTRL3	0xcb000248	/* I2C0 D21:F1 */
+#define SIO_IOBP_PORTCTRL4	0xcb000250	/* I2C1 D21:F2 */
+#define SIO_IOBP_PORTCTRL5	0xcb000258	/* SPI0 D21:F3 */
+#define SIO_IOBP_PORTCTRL6	0xcb000260	/* SPI1 D21:F4 */
+#define SIO_IOBP_PORTCTRL7	0xcb000268	/* UART0 D21:F5 */
+#define SIO_IOBP_PORTCTRL8	0xcb000270	/* UART1 D21:F6 */
+#define SIO_IOBP_PORTCTRLX(x)	(0xcb000240 + ((x) * 8))
+/* PORTCTRL 2-8 have the same layout */
+#define  SIO_IOBP_PORTCTRL_ACPI_IRQ_EN		BIT(21)
+#define  SIO_IOBP_PORTCTRL_PCI_CONF_DIS		BIT(20)
+#define  SIO_IOBP_PORTCTRL_SNOOP_SELECT(x)	(((x) & 3) << 18)
+#define  SIO_IOBP_PORTCTRL_INT_PIN(x)		(((x) & 0xf) << 2)
+#define  SIO_IOBP_PORTCTRL_PM_CAP_PRSNT		BIT(1)
+#define SIO_IOBP_FUNCDIS0	0xce00aa07	/* DMA D21:F0 */
+#define SIO_IOBP_FUNCDIS1	0xce00aa47	/* I2C0 D21:F1 */
+#define SIO_IOBP_FUNCDIS2	0xce00aa87	/* I2C1 D21:F2 */
+#define SIO_IOBP_FUNCDIS3	0xce00aac7	/* SPI0 D21:F3 */
+#define SIO_IOBP_FUNCDIS4	0xce00ab07	/* SPI1 D21:F4 */
+#define SIO_IOBP_FUNCDIS5	0xce00ab47	/* UART0 D21:F5 */
+#define SIO_IOBP_FUNCDIS6	0xce00ab87	/* UART1 D21:F6 */
+#define SIO_IOBP_FUNCDIS7	0xce00ae07	/* SDIO D23:F0 */
+#define  SIO_IOBP_FUNCDIS_DIS			BIT(8)
+
+/* Serial IO Devices */
+#define SIO_ID_SDMA		0 /* D21:F0 */
+#define SIO_ID_I2C0		1 /* D21:F1 */
+#define SIO_ID_I2C1		2 /* D21:F2 */
+#define SIO_ID_SPI0		3 /* D21:F3 */
+#define SIO_ID_SPI1		4 /* D21:F4 */
+#define SIO_ID_UART0		5 /* D21:F5 */
+#define SIO_ID_UART1		6 /* D21:F6 */
+#define SIO_ID_SDIO		7 /* D23:F0 */
+
+#define SIO_REG_PPR_CLOCK		0x800
+#define  SIO_REG_PPR_CLOCK_EN		 BIT(0)
+#define  SIO_REG_PPR_CLOCK_UPDATE	 BIT(31)
+#define  SIO_REG_PPR_CLOCK_M_DIV	 0x25a
+#define  SIO_REG_PPR_CLOCK_N_DIV	 0x7fff
+#define SIO_REG_PPR_RST			0x804
+#define  SIO_REG_PPR_RST_ASSERT		 0x3
+#define SIO_REG_PPR_GEN			0x808
+#define  SIO_REG_PPR_GEN_LTR_MODE_MASK	 BIT(2)
+#define  SIO_REG_PPR_GEN_VOLTAGE_MASK	 BIT(3)
+#define  SIO_REG_PPR_GEN_VOLTAGE(x)	 ((x & 1) << 3)
+#define SIO_REG_AUTO_LTR		0x814
+
+#define SIO_REG_SDIO_PPR_GEN		0x1008
+#define SIO_REG_SDIO_PPR_SW_LTR		0x1010
+#define SIO_REG_SDIO_PPR_CMD12		0x3c
+#define  SIO_REG_SDIO_PPR_CMD12_B30	 BIT(30)
+
+#define SIO_PIN_INTA 1 /* IRQ5 in ACPI mode */
+#define SIO_PIN_INTB 2 /* IRQ6 in ACPI mode */
+#define SIO_PIN_INTC 3 /* IRQ7 in ACPI mode */
+#define SIO_PIN_INTD 4 /* IRQ13 in ACPI mode */
+
+#endif /* __ARCH_BROADWELL_SERIALIO_H_ */