From 3f3411ebf864f6aa656cc8788ef1a7d2270b8e7a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 16 Feb 2019 20:25:03 -0700 Subject: [PATCH] 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 Reviewed-by: Bin Meng --- arch/x86/cpu/broadwell/pch.c | 110 +++++++++++++++++++++++++ arch/x86/include/asm/arch-broadwell/serialio.h | 82 ++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 arch/x86/include/asm/arch-broadwell/serialio.h 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 #include #include +#include #include +#include #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 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_ */ -- 2.7.4