x86: broadwell: Add support for serial I/O devices
authorSimon Glass <sjg@chromium.org>
Sun, 17 Feb 2019 03:25:03 +0000 (20:25 -0700)
committerBin Meng <bmeng.cn@gmail.com>
Wed, 20 Feb 2019 07:27:10 +0000 (15:27 +0800)
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>
arch/x86/cpu/broadwell/pch.c
arch/x86/include/asm/arch-broadwell/serialio.h [new file with mode: 0644]

index 82506ba..73d3d3b 100644 (file)
@@ -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 @@ static void systemagent_init(void)
        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 @@ static int broadwell_pch_init(struct udevice *dev)
                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 (file)
index 0000000..5e98eaf
--- /dev/null
@@ -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_ */