summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-02-08 00:57:23 -0500
committerTom Rini <trini@konsulko.com>2020-02-08 00:57:33 -0500
commitea5ccefdd7c0bd07cedd5c489ad3ee62f8cb9152 (patch)
treef387c6f7550e2720cc5b7490e82c0b52178a9042
parente1dff2d69e5a21a61c3eb28e5d230a6d48749b6c (diff)
parent0f6a70e971b2d87de3e58e8f0b51b0cd6723bc96 (diff)
downloadu-boot-WIP/08Feb2020.tar.gz
Merge https://gitlab.denx.de/u-boot/custodians/u-boot-x86WIP/08Feb2020
- Move P2SB from Apollo Lake to a more generic location - Add a function to find a device by drvdata in DM core - Enhancement of DM IRQ uclass driver - Add a clock driver for Intel devices - Add support for ACPI general-purpose events - Add a TPM driver for H1/Cr50 - Enable TPM on Google Chromebook Coral
-rw-r--r--arch/sandbox/dts/test.dts5
-rw-r--r--arch/sandbox/include/asm/test.h4
-rw-r--r--arch/x86/Kconfig40
-rw-r--r--arch/x86/cpu/Makefile1
-rw-r--r--arch/x86/cpu/acpi_gpe.c85
-rw-r--r--arch/x86/cpu/apollolake/Kconfig5
-rw-r--r--arch/x86/cpu/apollolake/Makefile1
-rw-r--r--arch/x86/cpu/apollolake/fsp_s.c62
-rw-r--r--arch/x86/cpu/i386/interrupt.c3
-rw-r--r--arch/x86/cpu/intel_common/Makefile1
-rw-r--r--arch/x86/cpu/intel_common/itss.c12
-rw-r--r--arch/x86/cpu/intel_common/p2sb.c (renamed from arch/x86/cpu/apollolake/p2sb.c)0
-rw-r--r--arch/x86/cpu/irq.c3
-rw-r--r--arch/x86/dts/chromebook_coral.dts93
-rw-r--r--configs/chromebook_coral_defconfig3
-rw-r--r--doc/board/google/chromebook_coral.rst2
-rw-r--r--doc/device-tree-bindings/interrupt-controller/intel,acpi-gpe.txt30
-rw-r--r--doc/device-tree-bindings/interrupt-controller/interrupts.txt131
-rw-r--r--drivers/clk/Kconfig10
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/intel/Makefile6
-rw-r--r--drivers/clk/intel/clk_intel.c41
-rw-r--r--drivers/core/syscon-uclass.c15
-rw-r--r--drivers/core/uclass.c17
-rw-r--r--drivers/i2c/tegra_i2c.c13
-rw-r--r--drivers/misc/irq-uclass.c131
-rw-r--r--drivers/misc/irq_sandbox.c43
-rw-r--r--drivers/pinctrl/intel/pinctrl.c2
-rw-r--r--drivers/tpm/Kconfig10
-rw-r--r--drivers/tpm/Makefile1
-rw-r--r--drivers/tpm/cr50_i2c.c659
-rw-r--r--include/dm/test.h2
-rw-r--r--include/dm/uclass.h31
-rw-r--r--include/dt-bindings/clock/intel-clock.h15
-rw-r--r--include/irq.h138
-rw-r--r--include/tpm-v2.h31
-rw-r--r--test/dm/irq.c45
-rw-r--r--test/dm/test-fdt.c42
38 files changed, 1640 insertions, 94 deletions
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index e529c54d8d..c228447431 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -93,6 +93,7 @@
<&gpio_b 9 0xc 3 2 1>;
int-value = <1234>;
uint-value = <(-1234)>;
+ interrupts-extended = <&irq 3 0>;
};
junk {
@@ -357,8 +358,10 @@
vss-microvolts = <0>;
};
- irq {
+ irq: irq {
compatible = "sandbox,irq";
+ interrupt-controller;
+ #interrupt-cells = <2>;
};
lcd {
diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h
index 2421922c9e..7775794eaa 100644
--- a/arch/sandbox/include/asm/test.h
+++ b/arch/sandbox/include/asm/test.h
@@ -45,6 +45,10 @@
#define PCI_EA_BAR2_MAGIC 0x72727272
#define PCI_EA_BAR4_MAGIC 0x74747474
+enum {
+ SANDBOX_IRQN_PEND = 1, /* Interrupt number for 'pending' test */
+};
+
/* System controller driver data */
enum {
SYSCON0 = 32,
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index b733d2264e..c8eae24c07 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -715,6 +715,13 @@ config HAVE_ITSS
Select this to include the driver for the Interrupt Timer
Subsystem (ITSS) which is found on several Intel devices.
+config HAVE_P2SB
+ bool "Enable P2SB"
+ help
+ Select this to include the driver for the Primary to
+ Sideband Bridge (P2SB) which is found on several Intel
+ devices.
+
menu "System tables"
depends on !EFI && !SYS_COREBOOT
@@ -916,4 +923,37 @@ config X86_OFFSET_SPL
depends on SPL && X86
default SPL_TEXT_BASE
+config ACPI_GPE
+ bool "Support ACPI general-purpose events"
+ help
+ Enable a driver for ACPI GPEs to allow peripherals to send interrupts
+ via ACPI to the OS. In U-Boot this is only used when U-Boot itself
+ needs access to these interrupts. This can happen when it uses a
+ peripheral that is set up to use GPEs and so cannot use the normal
+ GPIO mechanism for polling an input.
+
+ See https://queue.acm.org/blogposting.cfm?id=18977 for more info
+
+config SPL_ACPI_GPE
+ bool "Support ACPI general-purpose events in SPL"
+ help
+ Enable a driver for ACPI GPEs to allow peripherals to send interrupts
+ via ACPI to the OS. In U-Boot this is only used when U-Boot itself
+ needs access to these interrupts. This can happen when it uses a
+ peripheral that is set up to use GPEs and so cannot use the normal
+ GPIO mechanism for polling an input.
+
+ See https://queue.acm.org/blogposting.cfm?id=18977 for more info
+
+config TPL_ACPI_GPE
+ bool "Support ACPI general-purpose events in TPL"
+ help
+ Enable a driver for ACPI GPEs to allow peripherals to send interrupts
+ via ACPI to the OS. In U-Boot this is only used when U-Boot itself
+ needs access to these interrupts. This can happen when it uses a
+ peripheral that is set up to use GPEs and so cannot use the normal
+ GPIO mechanism for polling an input.
+
+ See https://queue.acm.org/blogposting.cfm?id=18977 for more info
+
endmenu
diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile
index 5b40838e60..307267a8fb 100644
--- a/arch/x86/cpu/Makefile
+++ b/arch/x86/cpu/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_INTEL_QUEENSBAY) += queensbay/
obj-$(CONFIG_INTEL_TANGIER) += tangier/
obj-$(CONFIG_APIC) += lapic.o ioapic.o
obj-$(CONFIG_$(SPL_TPL_)X86_32BIT_INIT) += irq.o
+obj-$(CONFIG_$(SPL_TPL_)ACPI_GPE) += acpi_gpe.o
obj-$(CONFIG_QFW) += qfw_cpu.o
ifndef CONFIG_$(SPL_)X86_64
obj-$(CONFIG_SMP) += mp_init.o
diff --git a/arch/x86/cpu/acpi_gpe.c b/arch/x86/cpu/acpi_gpe.c
new file mode 100644
index 0000000000..55005455c0
--- /dev/null
+++ b/arch/x86/cpu/acpi_gpe.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2019 Google, LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <irq.h>
+#include <asm/io.h>
+
+/**
+ * struct acpi_gpe_priv - private driver information
+ *
+ * @acpi_base: Base I/O address of ACPI registers
+ */
+struct acpi_gpe_priv {
+ ulong acpi_base;
+};
+
+#define GPE0_STS(x) (0x20 + ((x) * 4))
+
+static int acpi_gpe_read_and_clear(struct irq *irq)
+{
+ struct acpi_gpe_priv *priv = dev_get_priv(irq->dev);
+ u32 mask, sts;
+ ulong start;
+ int ret = 0;
+ int bank;
+
+ bank = irq->id / 32;
+ mask = 1 << (irq->id % 32);
+
+ /* Wait up to 1ms for GPE status to clear */
+ start = get_timer(0);
+ do {
+ if (get_timer(start) > 1)
+ return ret;
+
+ sts = inl(priv->acpi_base + GPE0_STS(bank));
+ if (sts & mask) {
+ outl(mask, priv->acpi_base + GPE0_STS(bank));
+ ret = 1;
+ }
+ } while (sts & mask);
+
+ return ret;
+}
+
+static int acpi_gpe_ofdata_to_platdata(struct udevice *dev)
+{
+ struct acpi_gpe_priv *priv = dev_get_priv(dev);
+
+ priv->acpi_base = dev_read_addr(dev);
+ if (!priv->acpi_base || priv->acpi_base == FDT_ADDR_T_NONE)
+ return log_msg_ret("acpi_base", -EINVAL);
+
+ return 0;
+}
+
+static int acpi_gpe_of_xlate(struct irq *irq, struct ofnode_phandle_args *args)
+{
+ irq->id = args->args[0];
+
+ return 0;
+}
+
+static const struct irq_ops acpi_gpe_ops = {
+ .read_and_clear = acpi_gpe_read_and_clear,
+ .of_xlate = acpi_gpe_of_xlate,
+};
+
+static const struct udevice_id acpi_gpe_ids[] = {
+ { .compatible = "intel,acpi-gpe", .data = X86_IRQT_ACPI_GPE },
+ { }
+};
+
+U_BOOT_DRIVER(acpi_gpe_drv) = {
+ .name = "acpi_gpe",
+ .id = UCLASS_IRQ,
+ .of_match = acpi_gpe_ids,
+ .ops = &acpi_gpe_ops,
+ .ofdata_to_platdata = acpi_gpe_ofdata_to_platdata,
+ .priv_auto_alloc_size = sizeof(struct acpi_gpe_priv),
+};
diff --git a/arch/x86/cpu/apollolake/Kconfig b/arch/x86/cpu/apollolake/Kconfig
index a760e0ac68..942f11f566 100644
--- a/arch/x86/cpu/apollolake/Kconfig
+++ b/arch/x86/cpu/apollolake/Kconfig
@@ -40,6 +40,11 @@ config INTEL_APOLLOLAKE
imply INTEL_GPIO
imply SMP
imply HAVE_ITSS
+ imply HAVE_P2SB
+ imply CLK
+ imply CMD_CLK
+ imply CLK_INTEL
+ imply ACPI_GPE
if INTEL_APOLLOLAKE
diff --git a/arch/x86/cpu/apollolake/Makefile b/arch/x86/cpu/apollolake/Makefile
index f99f2c6473..578e15c4bf 100644
--- a/arch/x86/cpu/apollolake/Makefile
+++ b/arch/x86/cpu/apollolake/Makefile
@@ -20,7 +20,6 @@ endif
obj-y += hostbridge.o
obj-y += lpc.o
-obj-y += p2sb.o
obj-y += pch.o
obj-y += pmc.o
obj-y += uart.o
diff --git a/arch/x86/cpu/apollolake/fsp_s.c b/arch/x86/cpu/apollolake/fsp_s.c
index 9804227f80..b2d9130841 100644
--- a/arch/x86/cpu/apollolake/fsp_s.c
+++ b/arch/x86/cpu/apollolake/fsp_s.c
@@ -24,7 +24,6 @@
#define HIDE_BIT BIT(0)
#define INTEL_GSPI_MAX 3
-#define INTEL_I2C_DEV_MAX 8
#define MAX_USB2_PORTS 8
enum {
@@ -32,36 +31,6 @@ enum {
CHIPSET_LOCKDOWN_COREBOOT, /* coreboot handles locking */
};
-enum i2c_speed {
- I2C_SPEED_STANDARD = 100000,
- I2C_SPEED_FAST = 400000,
- I2C_SPEED_FAST_PLUS = 1000000,
- I2C_SPEED_HIGH = 3400000,
- I2C_SPEED_FAST_ULTRA = 5000000,
-};
-
-/*
- * Timing values are in units of clock period, with the clock speed
- * provided by the SOC
- *
- * TODO(sjg@chromium.org): Connect this up to the I2C driver
- */
-struct dw_i2c_speed_config {
- enum i2c_speed speed;
- /* SCL high and low period count */
- u16 scl_lcnt;
- u16 scl_hcnt;
- /*
- * SDA hold time should be 300ns in standard and fast modes
- * and long enough for deterministic logic level change in
- * fast-plus and high speed modes.
- *
- * [15:0] SDA TX Hold Time
- * [23:16] SDA RX Hold Time
- */
- u32 sda_hold;
-};
-
/* Serial IRQ control. SERIRQ_QUIET is the default (0) */
enum serirq_mode {
SERIRQ_QUIET,
@@ -69,32 +38,6 @@ enum serirq_mode {
SERIRQ_OFF,
};
-/*
- * This I2C controller has support for 3 independent speed configs but can
- * support both FAST_PLUS and HIGH speeds through the same set of speed
- * config registers. These are treated separately so the speed config values
- * can be provided via ACPI to the OS.
- */
-#define DW_I2C_SPEED_CONFIG_COUNT 4
-
-struct dw_i2c_bus_config {
- /* Bus should be enabled in TPL with temporary base */
- int early_init;
- /* Bus speed in Hz, default is I2C_SPEED_FAST (400 KHz) */
- enum i2c_speed speed;
- /*
- * If rise_time_ns is non-zero the calculations for lcnt and hcnt
- * registers take into account the times of the bus. However, if
- * there is a match in speed_config those register values take
- * precedence
- */
- int rise_time_ns;
- int fall_time_ns;
- int data_hold_time_ns;
- /* Specific bus speed configuration */
- struct dw_i2c_speed_config speed_config[DW_I2C_SPEED_CONFIG_COUNT];
-};
-
struct gspi_cfg {
/* Bus speed in MHz */
u32 speed_mhz;
@@ -110,7 +53,6 @@ struct gspi_cfg {
struct soc_intel_common_config {
int chipset_lockdown;
struct gspi_cfg gspi[INTEL_GSPI_MAX];
- struct dw_i2c_bus_config i2c[INTEL_I2C_DEV_MAX];
};
enum pnp_settings {
@@ -593,7 +535,7 @@ int arch_fsps_preinit(void)
struct udevice *itss;
int ret;
- ret = uclass_first_device_err(UCLASS_IRQ, &itss);
+ ret = irq_first_device_type(X86_IRQT_ITSS, &itss);
if (ret)
return log_msg_ret("no itss", ret);
/*
@@ -634,7 +576,7 @@ int arch_fsp_init_r(void)
if (ret)
return ret;
- ret = uclass_first_device_err(UCLASS_IRQ, &itss);
+ ret = irq_first_device_type(X86_IRQT_ITSS, &itss);
if (ret)
return log_msg_ret("no itss", ret);
/* Restore GPIO IRQ polarities back to previous settings */
diff --git a/arch/x86/cpu/i386/interrupt.c b/arch/x86/cpu/i386/interrupt.c
index 70edbe06e4..4c7e9ea215 100644
--- a/arch/x86/cpu/i386/interrupt.c
+++ b/arch/x86/cpu/i386/interrupt.c
@@ -15,6 +15,7 @@
#include <efi_loader.h>
#include <hang.h>
#include <init.h>
+#include <irq.h>
#include <irq_func.h>
#include <asm/control_regs.h>
#include <asm/i8259.h>
@@ -264,7 +265,7 @@ int interrupt_init(void)
int ret;
/* Try to set up the interrupt router, but don't require one */
- ret = uclass_first_device_err(UCLASS_IRQ, &dev);
+ ret = irq_first_device_type(X86_IRQT_BASE, &dev);
if (ret && ret != -ENODEV)
return ret;
diff --git a/arch/x86/cpu/intel_common/Makefile b/arch/x86/cpu/intel_common/Makefile
index e22c70781d..1736bd2b53 100644
--- a/arch/x86/cpu/intel_common/Makefile
+++ b/arch/x86/cpu/intel_common/Makefile
@@ -28,6 +28,7 @@ endif
endif
obj-y += pch.o
obj-$(CONFIG_HAVE_ITSS) += itss.o
+obj-$(CONFIG_HAVE_P2SB) += p2sb.o
ifdef CONFIG_SPL
ifndef CONFIG_SPL_BUILD
diff --git a/arch/x86/cpu/intel_common/itss.c b/arch/x86/cpu/intel_common/itss.c
index 9df51adecc..b6b57cc9a0 100644
--- a/arch/x86/cpu/intel_common/itss.c
+++ b/arch/x86/cpu/intel_common/itss.c
@@ -146,6 +146,15 @@ static int route_pmc_gpio_gpe(struct udevice *dev, uint pmc_gpe_num)
return -ENOENT;
}
+static int itss_bind(struct udevice *dev)
+{
+ /* This is not set with of-platdata, so set it manually */
+ if (CONFIG_IS_ENABLED(OF_PLATDATA))
+ dev->driver_data = X86_IRQT_ITSS;
+
+ return 0;
+}
+
static int itss_ofdata_to_platdata(struct udevice *dev)
{
struct itss_priv *priv = dev_get_priv(dev);
@@ -199,7 +208,7 @@ static const struct irq_ops itss_ops = {
};
static const struct udevice_id itss_ids[] = {
- { .compatible = "intel,itss"},
+ { .compatible = "intel,itss", .data = X86_IRQT_ITSS },
{ }
};
@@ -208,6 +217,7 @@ U_BOOT_DRIVER(itss_drv) = {
.id = UCLASS_IRQ,
.of_match = itss_ids,
.ops = &itss_ops,
+ .bind = itss_bind,
.ofdata_to_platdata = itss_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct itss_platdata),
.priv_auto_alloc_size = sizeof(struct itss_priv),
diff --git a/arch/x86/cpu/apollolake/p2sb.c b/arch/x86/cpu/intel_common/p2sb.c
index b72f50a627..b72f50a627 100644
--- a/arch/x86/cpu/apollolake/p2sb.c
+++ b/arch/x86/cpu/intel_common/p2sb.c
diff --git a/arch/x86/cpu/irq.c b/arch/x86/cpu/irq.c
index ed9938f7f7..bafa031082 100644
--- a/arch/x86/cpu/irq.c
+++ b/arch/x86/cpu/irq.c
@@ -7,6 +7,7 @@
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
+#include <irq.h>
#include <malloc.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -351,7 +352,7 @@ int irq_router_probe(struct udevice *dev)
}
static const struct udevice_id irq_router_ids[] = {
- { .compatible = "intel,irq-router" },
+ { .compatible = "intel,irq-router", .data = X86_IRQT_BASE },
{ }
};
diff --git a/arch/x86/dts/chromebook_coral.dts b/arch/x86/dts/chromebook_coral.dts
index a1820fa187..44a4619a66 100644
--- a/arch/x86/dts/chromebook_coral.dts
+++ b/arch/x86/dts/chromebook_coral.dts
@@ -20,6 +20,7 @@
#include <asm/arch-apollolake/gpio.h>
#include <asm/arch-apollolake/iomap.h>
#include <asm/arch-apollolake/pm.h>
+#include <dt-bindings/clock/intel-clock.h>
/ {
model = "Google Coral";
@@ -29,6 +30,14 @@
cros-ec0 = &cros_ec;
fsp = &fsp_s;
spi0 = &spi;
+ i2c0 = &i2c_0;
+ i2c1 = &i2c_1;
+ i2c2 = &i2c_2;
+ i2c3 = &i2c_3;
+ i2c4 = &i2c_4;
+ i2c5 = &i2c_5;
+ i2c6 = &i2c_6;
+ i2c7 = &i2c_7;
};
config {
@@ -39,6 +48,11 @@
stdout-path = &serial;
};
+ clk: clock {
+ compatible = "intel,apl-clk";
+ #clock-cells = <1>;
+ };
+
cpus {
u-boot,dm-pre-reloc;
#address-cells = <1>;
@@ -75,6 +89,13 @@
};
+ acpi_gpe: general-purpose-events {
+ reg = <IOMAP_ACPI_BASE IOMAP_ACPI_SIZE>;
+ compatible = "intel,acpi-gpe";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
keyboard {
intel,duplicate-por;
};
@@ -243,6 +264,78 @@
};
};
+ i2c_0: i2c2@16,0 {
+ compatible = "intel,apl-i2c";
+ reg = <0x0200b010 0 0 0 0>;
+ clocks = <&clk CLK_I2C>;
+ i2c-scl-rising-time-ns = <104>;
+ i2c-scl-falling-time-ns = <52>;
+ };
+
+ i2c_1: i2c2@16,1 {
+ compatible = "intel,apl-i2c";
+ reg = <0x0200b110 0 0 0 0>;
+ clocks = <&clk CLK_I2C>;
+ status = "disabled";
+ };
+
+ i2c_2: i2c2@16,2 {
+ compatible = "intel,apl-i2c";
+ reg = <0x0200b210 0 0 0 0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clock-frequency = <400000>;
+ clocks = <&clk CLK_I2C>;
+ i2c-scl-rising-time-ns = <57>;
+ i2c-scl-falling-time-ns = <28>;
+ tpm@50 {
+ reg = <0x50>;
+ compatible = "google,cr50";
+ u-boot,i2c-offset-len = <0>;
+ ready-gpio = <&gpio_n 28 GPIO_ACTIVE_LOW>;
+ interrupts-extended = <&acpi_gpe 0x3c 0>;
+ };
+ };
+
+ i2c_3: i2c2@16,3 {
+ compatible = "intel,apl-i2c";
+ reg = <0x0200b110 0 0 0 0>;
+ clocks = <&clk CLK_I2C>;
+ i2c-scl-rising-time-ns = <76>;
+ i2c-scl-falling-time-ns = <164>;
+ };
+
+ i2c_4: i2c2@17,0 {
+ compatible = "intel,apl-i2c";
+ reg = <0x0200b110 0 0 0 0>;
+ clocks = <&clk CLK_I2C>;
+ i2c-sda-hold-time-ns = <350>;
+ i2c-scl-rising-time-ns = <114>;
+ i2c-scl-falling-time-ns = <164>;
+ };
+
+ i2c_5: i2c2@17,1 {
+ compatible = "intel,apl-i2c";
+ reg = <0x0200b110 0 0 0 0>;
+ clocks = <&clk CLK_I2C>;
+ i2c-scl-rising-time-ns = <76>;
+ i2c-scl-falling-time-ns = <164>;
+ };
+
+ i2c_6: i2c2@17,2 {
+ compatible = "intel,apl-i2c";
+ reg = <0x0200b110 0 0 0 0>;
+ clocks = <&clk CLK_I2C>;
+ status = "disabled";
+ };
+
+ i2c_7: i2c2@17,3 {
+ compatible = "intel,apl-i2c";
+ reg = <0x0200b110 0 0 0 0>;
+ clocks = <&clk CLK_I2C>;
+ status = "disabled";
+ };
+
serial: serial@18,2 {
reg = <0x0200c210 0 0 0 0>;
u-boot,dm-pre-reloc;
diff --git a/configs/chromebook_coral_defconfig b/configs/chromebook_coral_defconfig
index b156e837ee..a7b71d99a1 100644
--- a/configs/chromebook_coral_defconfig
+++ b/configs/chromebook_coral_defconfig
@@ -53,7 +53,6 @@ CONFIG_CMD_TIME=y
CONFIG_CMD_SOUND=y
CONFIG_CMD_BOOTSTAGE=y
CONFIG_CMD_TPM=y
-CONFIG_CMD_TPM_TEST=y
CONFIG_CMD_EXT2=y
CONFIG_CMD_EXT4=y
CONFIG_CMD_EXT4_WRITE=y
@@ -90,6 +89,8 @@ CONFIG_SPI=y
CONFIG_ICH_SPI=y
CONFIG_TPL_SYSRESET=y
CONFIG_TPM_TIS_LPC=y
+# CONFIG_TPM_V1 is not set
+CONFIG_TPM2_CR50_I2C=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_STORAGE=y
CONFIG_USB_KEYBOARD=y
diff --git a/doc/board/google/chromebook_coral.rst b/doc/board/google/chromebook_coral.rst
index 5dc3c97c3d..d10e0c4954 100644
--- a/doc/board/google/chromebook_coral.rst
+++ b/doc/board/google/chromebook_coral.rst
@@ -213,9 +213,7 @@ To do
- left-side USB
- USB-C
- Cr50 (security chip: a basic driver is running but not included here)
- - I2C (driver exists but not enabled in device tree)
- Sound (Intel I2S support exists, but need da7219 driver)
- - RTC (driver exists but not enabled in device tree)
- Various minor features supported by LPC, etc.
- Booting Chrome OS, e.g. with verified boot
- Integrate with Chrome OS vboot
diff --git a/doc/device-tree-bindings/interrupt-controller/intel,acpi-gpe.txt b/doc/device-tree-bindings/interrupt-controller/intel,acpi-gpe.txt
new file mode 100644
index 0000000000..d9252bf29f
--- /dev/null
+++ b/doc/device-tree-bindings/interrupt-controller/intel,acpi-gpe.txt
@@ -0,0 +1,30 @@
+* Intel Advanced Configuration and Power Interface General Purpose Events
+
+This describes an interrupt controller which provides access to GPEs supported
+by the SoC.
+
+Required properties:
+
+- compatible : "intel,acpi-gpe"
+- interrupt-controller : Identifies the node as an interrupt controller
+- #interrupt-cells : The number of cells to define the interrupts. Must be 2:
+ cell 0: interrupt number (normally >=32 since GPEs below that are reserved)
+ cell 1: 0 (flags, but none are currently defined)
+- reg : The register bank for the controller (set this to the ACPI base).
+
+Example:
+
+ general-purpose-events {
+ reg = <IOMAP_ACPI_BASE IOMAP_ACPI_SIZE>;
+ compatible = "intel,acpi-gpe";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ ...
+ tpm@50 {
+ reg = <0x50>;
+ compatible = "google,cr50";
+ ready-gpio = <&gpio_n 0x1c GPIO_ACTIVE_LOW>;
+ interrupts-extended = <&acpi_gpe 0x3c 0>;
+ };
diff --git a/doc/device-tree-bindings/interrupt-controller/interrupts.txt b/doc/device-tree-bindings/interrupt-controller/interrupts.txt
new file mode 100644
index 0000000000..38a399a6b1
--- /dev/null
+++ b/doc/device-tree-bindings/interrupt-controller/interrupts.txt
@@ -0,0 +1,131 @@
+Specifying interrupt information for devices
+============================================
+
+1) Interrupt client nodes
+-------------------------
+
+Nodes that describe devices which generate interrupts must contain an
+"interrupts" property, an "interrupts-extended" property, or both. If both are
+present, the latter should take precedence; the former may be provided simply
+for compatibility with software that does not recognize the latter. These
+properties contain a list of interrupt specifiers, one per output interrupt. The
+format of the interrupt specifier is determined by the interrupt controller to
+which the interrupts are routed; see section 2 below for details.
+
+ Example:
+ interrupt-parent = <&intc1>;
+ interrupts = <5 0>, <6 0>;
+
+The "interrupt-parent" property is used to specify the controller to which
+interrupts are routed and contains a single phandle referring to the interrupt
+controller node. This property is inherited, so it may be specified in an
+interrupt client node or in any of its parent nodes. Interrupts listed in the
+"interrupts" property are always in reference to the node's interrupt parent.
+
+The "interrupts-extended" property is a special form; useful when a node needs
+to reference multiple interrupt parents or a different interrupt parent than
+the inherited one. Each entry in this property contains both the parent phandle
+and the interrupt specifier.
+
+ Example:
+ interrupts-extended = <&intc1 5 1>, <&intc2 1 0>;
+
+(NOTE: only this 'special form' is supported in U-Boot)
+
+
+2) Interrupt controller nodes
+-----------------------------
+
+A device is marked as an interrupt controller with the "interrupt-controller"
+property. This is a empty, boolean property. An additional "#interrupt-cells"
+property defines the number of cells needed to specify a single interrupt.
+
+It is the responsibility of the interrupt controller's binding to define the
+length and format of the interrupt specifier. The following two variants are
+commonly used:
+
+ a) one cell
+ -----------
+ The #interrupt-cells property is set to 1 and the single cell defines the
+ index of the interrupt within the controller.
+
+ Example:
+
+ vic: intc@10140000 {
+ compatible = "arm,versatile-vic";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ reg = <0x10140000 0x1000>;
+ };
+
+ sic: intc@10003000 {
+ compatible = "arm,versatile-sic";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ reg = <0x10003000 0x1000>;
+ interrupt-parent = <&vic>;
+ interrupts = <31>; /* Cascaded to vic */
+ };
+
+ b) two cells
+ ------------
+ The #interrupt-cells property is set to 2 and the first cell defines the
+ index of the interrupt within the controller, while the second cell is used
+ to specify any of the following flags:
+ - bits[3:0] trigger type and level flags
+ 1 = low-to-high edge triggered
+ 2 = high-to-low edge triggered
+ 4 = active high level-sensitive
+ 8 = active low level-sensitive
+
+ Example:
+
+ i2c@7000c000 {
+ gpioext: gpio-adnp@41 {
+ compatible = "ad,gpio-adnp";
+ reg = <0x41>;
+
+ interrupt-parent = <&gpio>;
+ interrupts = <160 1>;
+
+ gpio-controller;
+ #gpio-cells = <1>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ nr-gpios = <64>;
+ };
+
+ sx8634@2b {
+ compatible = "smtc,sx8634";
+ reg = <0x2b>;
+
+ interrupt-parent = <&gpioext>;
+ interrupts = <3 0x8>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ threshold = <0x40>;
+ sensitivity = <7>;
+ };
+ };
+
+
+Example of special form (supported by U-Boot):
+
+ acpi_gpe: general-purpose-events {
+ reg = <IOMAP_ACPI_BASE IOMAP_ACPI_SIZE>;
+ compatible = "intel,acpi-gpe";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ tpm@50 {
+ reg = <0x50>;
+ compatible = "google,cr50";
+ u-boot,i2c-offset-len = <0>;
+ ready-gpio = <&gpio_n 28 GPIO_ACTIVE_LOW>;
+ interrupts-extended = <&acpi_gpe 0x3c 0>;
+ };
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 16d4237f89..1992d4a4b4 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -73,6 +73,16 @@ config CLK_COMPOSITE_CCF
Enable this option if you want to (re-)use the Linux kernel's Common
Clock Framework [CCF] composite code in U-Boot's clock driver.
+config CLK_INTEL
+ bool "Enable clock driver for Intel x86"
+ depends on CLK && X86
+ help
+ This provides very basic support for clocks on Intel SoCs. The driver
+ is barely used at present but could be expanded as needs arise.
+ Much clock configuration in U-Boot is either set up by the FSP, or
+ set up by U-Boot itself but only statically. Thus the driver does not
+ support changing clock rates, only querying them.
+
config CLK_STM32F
bool "Enable clock driver support for STM32F family"
depends on CLK && (STM32F7 || STM32F4)
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 06131edb9f..e01783391d 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_CLK_MVEBU) += mvebu/
obj-$(CONFIG_CLK_BCM6345) += clk_bcm6345.o
obj-$(CONFIG_CLK_BOSTON) += clk_boston.o
obj-$(CONFIG_CLK_EXYNOS) += exynos/
+obj-$(CONFIG_$(SPL_TPL_)CLK_INTEL) += intel/
obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o
obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o
obj-$(CONFIG_CLK_OWL) += owl/
diff --git a/drivers/clk/intel/Makefile b/drivers/clk/intel/Makefile
new file mode 100644
index 0000000000..45e93d7024
--- /dev/null
+++ b/drivers/clk/intel/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright 2010 Google LLC
+#
+
+obj-y += clk_intel.o
diff --git a/drivers/clk/intel/clk_intel.c b/drivers/clk/intel/clk_intel.c
new file mode 100644
index 0000000000..d2e15491a3
--- /dev/null
+++ b/drivers/clk/intel/clk_intel.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2019 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <clk-uclass.h>
+#include <dt-bindings/clock/intel-clock.h>
+
+static ulong intel_clk_get_rate(struct clk *clk)
+{
+ ulong rate;
+
+ switch (clk->id) {
+ case CLK_I2C:
+ /* Hard-coded to 133MHz on current platforms */
+ return 133333333;
+ default:
+ return -ENODEV;
+ }
+
+ return rate;
+}
+
+static struct clk_ops intel_clk_ops = {
+ .get_rate = intel_clk_get_rate,
+};
+
+static const struct udevice_id intel_clk_ids[] = {
+ { .compatible = "intel,apl-clk" },
+ { }
+};
+
+U_BOOT_DRIVER(clk_intel) = {
+ .name = "clk_intel",
+ .id = UCLASS_CLK,
+ .of_match = intel_clk_ids,
+ .ops = &intel_clk_ops,
+};
diff --git a/drivers/core/syscon-uclass.c b/drivers/core/syscon-uclass.c
index 5bb38e329c..b9ae82174e 100644
--- a/drivers/core/syscon-uclass.c
+++ b/drivers/core/syscon-uclass.c
@@ -128,22 +128,15 @@ struct regmap *syscon_regmap_lookup_by_phandle(struct udevice *dev,
int syscon_get_by_driver_data(ulong driver_data, struct udevice **devp)
{
- struct udevice *dev;
- struct uclass *uc;
int ret;
*devp = NULL;
- ret = uclass_get(UCLASS_SYSCON, &uc);
+
+ ret = uclass_first_device_drvdata(UCLASS_SYSCON, driver_data, devp);
if (ret)
- return ret;
- uclass_foreach_dev(dev, uc) {
- if (dev->driver_data == driver_data) {
- *devp = dev;
- return device_probe(dev);
- }
- }
+ return log_msg_ret("find", ret);
- return -ENODEV;
+ return 0;
}
struct regmap *syscon_get_regmap_by_driver_data(ulong driver_data)
diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c
index c520ef113a..61192d8a9f 100644
--- a/drivers/core/uclass.c
+++ b/drivers/core/uclass.c
@@ -629,6 +629,23 @@ int uclass_next_device_check(struct udevice **devp)
return device_probe(*devp);
}
+int uclass_first_device_drvdata(enum uclass_id id, ulong driver_data,
+ struct udevice **devp)
+{
+ struct udevice *dev;
+ struct uclass *uc;
+
+ uclass_id_foreach_dev(id, dev, uc) {
+ if (dev_get_driver_data(dev) == driver_data) {
+ *devp = dev;
+
+ return device_probe(dev);
+ }
+ }
+
+ return -ENODEV;
+}
+
int uclass_bind_device(struct udevice *dev)
{
struct uclass *uc;
diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c
index 4be41ddbf0..142463ef44 100644
--- a/drivers/i2c/tegra_i2c.c
+++ b/drivers/i2c/tegra_i2c.c
@@ -499,18 +499,7 @@ static int tegra_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
int tegra_i2c_get_dvc_bus(struct udevice **busp)
{
- struct udevice *bus;
-
- for (uclass_first_device(UCLASS_I2C, &bus);
- bus;
- uclass_next_device(&bus)) {
- if (dev_get_driver_data(bus) == TYPE_DVC) {
- *busp = bus;
- return 0;
- }
- }
-
- return -ENODEV;
+ return uclass_first_device_drvdata(UCLASS_I2C, TYPE_DVC, busp);
}
static const struct dm_i2c_ops tegra_i2c_ops = {
diff --git a/drivers/misc/irq-uclass.c b/drivers/misc/irq-uclass.c
index d5182cf149..61aa10e465 100644
--- a/drivers/misc/irq-uclass.c
+++ b/drivers/misc/irq-uclass.c
@@ -1,11 +1,16 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
+ * Copyright 2019 Google, LLC
+ * Written by Simon Glass <sjg@chromium.org>
*/
+#define LOG_CATEGORY UCLASS_IRQ
+
#include <common.h>
#include <dm.h>
+#include <dt-structs.h>
#include <irq.h>
+#include <dm/device-internal.h>
int irq_route_pmc_gpio_gpe(struct udevice *dev, uint pmc_gpe_num)
{
@@ -47,6 +52,130 @@ int irq_restore_polarities(struct udevice *dev)
return ops->restore_polarities(dev);
}
+int irq_read_and_clear(struct irq *irq)
+{
+ const struct irq_ops *ops = irq_get_ops(irq->dev);
+
+ if (!ops->read_and_clear)
+ return -ENOSYS;
+
+ return ops->read_and_clear(irq);
+}
+
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+int irq_get_by_index_platdata(struct udevice *dev, int index,
+ struct phandle_1_arg *cells, struct irq *irq)
+{
+ int ret;
+
+ if (index != 0)
+ return -ENOSYS;
+ ret = uclass_get_device(UCLASS_IRQ, 0, &irq->dev);
+ if (ret)
+ return ret;
+ irq->id = cells[0].arg[0];
+
+ return 0;
+}
+#else
+static int irq_of_xlate_default(struct irq *irq,
+ struct ofnode_phandle_args *args)
+{
+ log_debug("(irq=%p)\n", irq);
+
+ if (args->args_count > 1) {
+ log_debug("Invaild args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ if (args->args_count)
+ irq->id = args->args[0];
+ else
+ irq->id = 0;
+
+ return 0;
+}
+
+static int irq_get_by_index_tail(int ret, ofnode node,
+ struct ofnode_phandle_args *args,
+ const char *list_name, int index,
+ struct irq *irq)
+{
+ struct udevice *dev_irq;
+ const struct irq_ops *ops;
+
+ assert(irq);
+ irq->dev = NULL;
+ if (ret)
+ goto err;
+
+ ret = uclass_get_device_by_ofnode(UCLASS_IRQ, args->node, &dev_irq);
+ if (ret) {
+ log_debug("uclass_get_device_by_ofnode failed: err=%d\n", ret);
+ return ret;
+ }
+
+ irq->dev = dev_irq;
+
+ ops = irq_get_ops(dev_irq);
+
+ if (ops->of_xlate)
+ ret = ops->of_xlate(irq, args);
+ else
+ ret = irq_of_xlate_default(irq, args);
+ if (ret) {
+ log_debug("of_xlate() failed: %d\n", ret);
+ return ret;
+ }
+
+ return irq_request(dev_irq, irq);
+err:
+ log_debug("Node '%s', property '%s', failed to request IRQ index %d: %d\n",
+ ofnode_get_name(node), list_name, index, ret);
+ return ret;
+}
+
+int irq_get_by_index(struct udevice *dev, int index, struct irq *irq)
+{
+ struct ofnode_phandle_args args;
+ int ret;
+
+ ret = dev_read_phandle_with_args(dev, "interrupts-extended",
+ "#interrupt-cells", 0, index, &args);
+
+ return irq_get_by_index_tail(ret, dev_ofnode(dev), &args,
+ "interrupts-extended", index > 0, irq);
+}
+#endif /* OF_PLATDATA */
+
+int irq_request(struct udevice *dev, struct irq *irq)
+{
+ const struct irq_ops *ops;
+
+ log_debug("(dev=%p, irq=%p)\n", dev, irq);
+ if (!irq)
+ return 0;
+ ops = irq_get_ops(dev);
+
+ irq->dev = dev;
+
+ if (!ops->request)
+ return 0;
+
+ return ops->request(irq);
+}
+
+int irq_first_device_type(enum irq_dev_t type, struct udevice **devp)
+{
+ int ret;
+
+ ret = uclass_first_device_drvdata(UCLASS_IRQ, type, devp);
+ if (ret)
+ return log_msg_ret("find", ret);
+
+ return 0;
+}
+
UCLASS_DRIVER(irq) = {
.id = UCLASS_IRQ,
.name = "irq",
diff --git a/drivers/misc/irq_sandbox.c b/drivers/misc/irq_sandbox.c
index 6dda1a4c44..54bc47c8d8 100644
--- a/drivers/misc/irq_sandbox.c
+++ b/drivers/misc/irq_sandbox.c
@@ -8,6 +8,18 @@
#include <common.h>
#include <dm.h>
#include <irq.h>
+#include <asm/test.h>
+
+/**
+ * struct sandbox_irq_priv - private data for this driver
+ *
+ * @count: Counts the number calls to the read_and_clear() method
+ * @pending: true if an interrupt is pending, else false
+ */
+struct sandbox_irq_priv {
+ int count;
+ bool pending;
+};
static int sandbox_set_polarity(struct udevice *dev, uint irq, bool active_low)
{
@@ -35,15 +47,43 @@ static int sandbox_restore_polarities(struct udevice *dev)
return 0;
}
+static int sandbox_irq_read_and_clear(struct irq *irq)
+{
+ struct sandbox_irq_priv *priv = dev_get_priv(irq->dev);
+
+ if (irq->id != SANDBOX_IRQN_PEND)
+ return -EINVAL;
+ priv->count++;
+ if (priv->pending) {
+ priv->pending = false;
+ return 1;
+ }
+
+ if (!(priv->count % 3))
+ priv->pending = true;
+
+ return 0;
+}
+
+static int sandbox_irq_of_xlate(struct irq *irq,
+ struct ofnode_phandle_args *args)
+{
+ irq->id = args->args[0];
+
+ return 0;
+}
+
static const struct irq_ops sandbox_irq_ops = {
.route_pmc_gpio_gpe = sandbox_route_pmc_gpio_gpe,
.set_polarity = sandbox_set_polarity,
.snapshot_polarities = sandbox_snapshot_polarities,
.restore_polarities = sandbox_restore_polarities,
+ .read_and_clear = sandbox_irq_read_and_clear,
+ .of_xlate = sandbox_irq_of_xlate,
};
static const struct udevice_id sandbox_irq_ids[] = {
- { .compatible = "sandbox,irq"},
+ { .compatible = "sandbox,irq", SANDBOX_IRQT_BASE },
{ }
};
@@ -52,4 +92,5 @@ U_BOOT_DRIVER(sandbox_irq_drv) = {
.id = UCLASS_IRQ,
.of_match = sandbox_irq_ids,
.ops = &sandbox_irq_ops,
+ .priv_auto_alloc_size = sizeof(struct sandbox_irq_priv),
};
diff --git a/drivers/pinctrl/intel/pinctrl.c b/drivers/pinctrl/intel/pinctrl.c
index 5bf5d8b0e2..f4cc55aa3b 100644
--- a/drivers/pinctrl/intel/pinctrl.c
+++ b/drivers/pinctrl/intel/pinctrl.c
@@ -613,7 +613,7 @@ int intel_pinctrl_ofdata_to_platdata(struct udevice *dev,
log_err("Cannot find community for pid %d\n", pplat->pid);
return -EDOM;
}
- ret = uclass_first_device_err(UCLASS_IRQ, &priv->itss);
+ ret = irq_first_device_type(X86_IRQT_ITSS, &priv->itss);
if (ret)
return log_msg_ret("Cannot find ITSS", ret);
priv->comm = comm;
diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig
index 94629dffd2..555a76bb1e 100644
--- a/drivers/tpm/Kconfig
+++ b/drivers/tpm/Kconfig
@@ -127,6 +127,16 @@ config TPM_V2
if TPM_V2
+config TPM2_CR50_I2C
+ bool "Enable support for Google cr50 TPM"
+ depends on DM_I2C
+ help
+ Cr50 is an implementation of a TPM on Google's H1 security chip.
+ This uses the same open-source firmware as the Chromium OS EC.
+ While Cr50 has other features, its primary role is as the root of
+ trust for a device, It operates like a TPM and can be used with
+ verified boot. Cr50 is used on recent Chromebooks (since 2017).
+
config TPM2_TIS_SANDBOX
bool "Enable sandbox TPMv2.x driver"
depends on TPM_V2 && SANDBOX
diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile
index 94c337b8ed..4c866b37c5 100644
--- a/drivers/tpm/Makefile
+++ b/drivers/tpm/Makefile
@@ -10,5 +10,6 @@ obj-$(CONFIG_TPM_TIS_SANDBOX) += tpm_tis_sandbox.o
obj-$(CONFIG_TPM_ST33ZP24_I2C) += tpm_tis_st33zp24_i2c.o
obj-$(CONFIG_TPM_ST33ZP24_SPI) += tpm_tis_st33zp24_spi.o
+obj-$(CONFIG_TPM2_CR50_I2C) += cr50_i2c.o
obj-$(CONFIG_TPM2_TIS_SANDBOX) += tpm2_tis_sandbox.o
obj-$(CONFIG_TPM2_TIS_SPI) += tpm2_tis_spi.o
diff --git a/drivers/tpm/cr50_i2c.c b/drivers/tpm/cr50_i2c.c
new file mode 100644
index 0000000000..b904a7d426
--- /dev/null
+++ b/drivers/tpm/cr50_i2c.c
@@ -0,0 +1,659 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cr50 / H1 TPM support
+ *
+ * Copyright 2018 Google LLC
+ */
+
+#define LOG_CATEGORY UCLASS_TPM
+
+#include <common.h>
+#include <dm.h>
+#include <i2c.h>
+#include <irq.h>
+#include <spl.h>
+#include <tpm-v2.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/arch/iomap.h>
+#include <asm/arch/pm.h>
+
+enum {
+ TIMEOUT_INIT_MS = 30000, /* Very long timeout for TPM init */
+ TIMEOUT_LONG_US = 2 * 1000 * 1000,
+ TIMEOUT_SHORT_US = 2 * 1000,
+ TIMEOUT_NO_IRQ_US = 20 * 1000,
+ TIMEOUT_IRQ_US = 100 * 1000,
+};
+
+enum {
+ CR50_DID_VID = 0x00281ae0L
+};
+
+enum {
+ CR50_MAX_BUF_SIZE = 63,
+};
+
+struct cr50_priv {
+ struct gpio_desc ready_gpio;
+ struct irq irq;
+ int locality;
+ uint vendor;
+ bool use_irq;
+};
+
+/* Wait for interrupt to indicate TPM is ready */
+static int cr50_i2c_wait_tpm_ready(struct udevice *dev)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+ ulong timeout, base;
+ int i;
+
+ if (!priv->use_irq && !dm_gpio_is_valid(&priv->ready_gpio)) {
+ /* Fixed delay if interrupt not supported */
+ udelay(TIMEOUT_NO_IRQ_US);
+ return 0;
+ }
+
+ base = timer_get_us();
+ timeout = base + TIMEOUT_IRQ_US;
+
+ i = 0;
+ while (priv->use_irq ? !irq_read_and_clear(&priv->irq) :
+ !dm_gpio_get_value(&priv->ready_gpio)) {
+ i++;
+ if ((int)(timer_get_us() - timeout) >= 0) {
+ log_warning("Timeout\n");
+ /* Use this instead of the -ETIMEDOUT used by i2c */
+ return -ETIME;
+ }
+ }
+ log_debug("i=%d\n", i);
+
+ return 0;
+}
+
+/* Clear pending interrupts */
+static void cr50_i2c_clear_tpm_irq(struct udevice *dev)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+
+ if (priv->use_irq)
+ irq_read_and_clear(&priv->irq);
+}
+
+/*
+ * cr50_i2c_read() - read from TPM register
+ *
+ * @dev: TPM chip information
+ * @addr: register address to read from
+ * @buffer: provided by caller
+ * @len: number of bytes to read
+ *
+ * 1) send register address byte 'addr' to the TPM
+ * 2) wait for TPM to indicate it is ready
+ * 3) read 'len' bytes of TPM response into the provided 'buffer'
+ *
+ * Return 0 on success. -ve on error
+ */
+static int cr50_i2c_read(struct udevice *dev, u8 addr, u8 *buffer,
+ size_t len)
+{
+ int ret;
+
+ /* Clear interrupt before starting transaction */
+ cr50_i2c_clear_tpm_irq(dev);
+
+ /* Send the register address byte to the TPM */
+ ret = dm_i2c_write(dev, 0, &addr, 1);
+ if (ret) {
+ log_err("Address write failed (err=%d)\n", ret);
+ return ret;
+ }
+
+ /* Wait for TPM to be ready with response data */
+ ret = cr50_i2c_wait_tpm_ready(dev);
+ if (ret)
+ return ret;
+
+ /* Read response data frrom the TPM */
+ ret = dm_i2c_read(dev, 0, buffer, len);
+ if (ret) {
+ log_err("Read response failed (err=%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * cr50_i2c_write() - write to TPM register
+ *
+ * @dev: TPM chip information
+ * @addr: register address to write to
+ * @buffer: data to write
+ * @len: number of bytes to write
+ *
+ * 1) prepend the provided address to the provided data
+ * 2) send the address+data to the TPM
+ * 3) wait for TPM to indicate it is done writing
+ *
+ * Returns -1 on error, 0 on success.
+ */
+static int cr50_i2c_write(struct udevice *dev, u8 addr, const u8 *buffer,
+ size_t len)
+{
+ u8 buf[len + 1];
+ int ret;
+
+ if (len > CR50_MAX_BUF_SIZE) {
+ log_err("Length %zd is too large\n", len);
+ return -E2BIG;
+ }
+
+ /* Prepend the 'register address' to the buffer */
+ buf[0] = addr;
+ memcpy(buf + 1, buffer, len);
+
+ /* Clear interrupt before starting transaction */
+ cr50_i2c_clear_tpm_irq(dev);
+
+ /* Send write request buffer with address */
+ ret = dm_i2c_write(dev, 0, buf, len + 1);
+ if (ret) {
+ log_err("Error writing to TPM (err=%d)\n", ret);
+ return ret;
+ }
+
+ /* Wait for TPM to be ready */
+ return cr50_i2c_wait_tpm_ready(dev);
+}
+
+static inline u8 tpm_access(u8 locality)
+{
+ return 0x0 | (locality << 4);
+}
+
+static inline u8 tpm_sts(u8 locality)
+{
+ return 0x1 | (locality << 4);
+}
+
+static inline u8 tpm_data_fifo(u8 locality)
+{
+ return 0x5 | (locality << 4);
+}
+
+static inline u8 tpm_did_vid(u8 locality)
+{
+ return 0x6 | (locality << 4);
+}
+
+static int release_locality(struct udevice *dev, int force)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+ u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_REQUEST_PENDING;
+ u8 addr = tpm_access(priv->locality);
+ int ret;
+ u8 buf;
+
+ ret = cr50_i2c_read(dev, addr, &buf, 1);
+ if (ret)
+ return ret;
+
+ if (force || (buf & mask) == mask) {
+ buf = TPM_ACCESS_ACTIVE_LOCALITY;
+ cr50_i2c_write(dev, addr, &buf, 1);
+ }
+
+ priv->locality = 0;
+
+ return 0;
+}
+
+/* cr50 requires all 4 bytes of status register to be read */
+static int cr50_i2c_status(struct udevice *dev)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+ u8 buf[4];
+ int ret;
+
+ ret = cr50_i2c_read(dev, tpm_sts(priv->locality), buf, sizeof(buf));
+ if (ret) {
+ log_warning("%s: Failed to read status\n", __func__);
+ return ret;
+ }
+
+ return buf[0];
+}
+
+/* cr50 requires all 4 bytes of status register to be written */
+static int cr50_i2c_ready(struct udevice *dev)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+ u8 buf[4] = { TPM_STS_COMMAND_READY };
+ int ret;
+
+ ret = cr50_i2c_write(dev, tpm_sts(priv->locality), buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ udelay(TIMEOUT_SHORT_US);
+
+ return 0;
+}
+
+static int cr50_i2c_wait_burststs(struct udevice *dev, u8 mask,
+ size_t *burst, int *status)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+ ulong timeout;
+ u32 buf;
+
+ /*
+ * cr50 uses bytes 3:2 of status register for burst count and all 4
+ * bytes must be read
+ */
+ timeout = timer_get_us() + TIMEOUT_LONG_US;
+ while (timer_get_us() < timeout) {
+ if (cr50_i2c_read(dev, tpm_sts(priv->locality),
+ (u8 *)&buf, sizeof(buf)) < 0) {
+ udelay(TIMEOUT_SHORT_US);
+ continue;
+ }
+
+ *status = buf & 0xff;
+ *burst = le16_to_cpu((buf >> 8) & 0xffff);
+
+ if ((*status & mask) == mask &&
+ *burst > 0 && *burst <= CR50_MAX_BUF_SIZE)
+ return 0;
+
+ udelay(TIMEOUT_SHORT_US);
+ }
+
+ log_warning("Timeout reading burst and status\n");
+
+ return -ETIMEDOUT;
+}
+
+static int cr50_i2c_recv(struct udevice *dev, u8 *buf, size_t buf_len)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+ size_t burstcnt, expected, current, len;
+ u8 addr = tpm_data_fifo(priv->locality);
+ u8 mask = TPM_STS_VALID | TPM_STS_DATA_AVAIL;
+ u32 expected_buf;
+ int status;
+ int ret;
+
+ log_debug("%s: len=%x\n", __func__, buf_len);
+ if (buf_len < TPM_HEADER_SIZE)
+ return -E2BIG;
+
+ ret = cr50_i2c_wait_burststs(dev, mask, &burstcnt, &status);
+ if (ret < 0) {
+ log_warning("First chunk not available\n");
+ goto out_err;
+ }
+
+ /* Read first chunk of burstcnt bytes */
+ if (cr50_i2c_read(dev, addr, buf, burstcnt) < 0) {
+ log_warning("Read failed\n");
+ goto out_err;
+ }
+
+ /* Determine expected data in the return buffer */
+ memcpy(&expected_buf, buf + TPM_CMD_COUNT_OFFSET, sizeof(expected_buf));
+ expected = be32_to_cpu(expected_buf);
+ if (expected > buf_len) {
+ log_warning("Too much data: %zu > %zu\n", expected, buf_len);
+ goto out_err;
+ }
+
+ /* Now read the rest of the data */
+ current = burstcnt;
+ while (current < expected) {
+ /* Read updated burst count and check status */
+ if (cr50_i2c_wait_burststs(dev, mask, &burstcnt, &status) < 0) {
+ log_warning("- burst failure1\n");
+ goto out_err;
+ }
+
+ len = min(burstcnt, expected - current);
+ if (cr50_i2c_read(dev, addr, buf + current, len) != 0) {
+ log_warning("Read failed\n");
+ goto out_err;
+ }
+
+ current += len;
+ }
+
+ if (cr50_i2c_wait_burststs(dev, TPM_STS_VALID, &burstcnt,
+ &status) < 0) {
+ log_warning("- burst failure2\n");
+ goto out_err;
+ }
+ if (status & TPM_STS_DATA_AVAIL) {
+ log_warning("Data still available\n");
+ goto out_err;
+ }
+
+ return current;
+
+out_err:
+ /* Abort current transaction if still pending */
+ ret = cr50_i2c_status(dev);
+ if (ret < 0)
+ return ret;
+ if (ret & TPM_STS_COMMAND_READY) {
+ ret = cr50_i2c_ready(dev);
+ if (ret)
+ return ret;
+ }
+
+ return -EIO;
+}
+
+static int cr50_i2c_send(struct udevice *dev, const u8 *buf, size_t len)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+
+ int status;
+ size_t burstcnt, limit, sent = 0;
+ u8 tpm_go[4] = { TPM_STS_GO };
+ ulong timeout;
+ int ret;
+
+ log_debug("%s: len=%x\n", __func__, len);
+ timeout = timer_get_us() + TIMEOUT_LONG_US;
+ do {
+ ret = cr50_i2c_status(dev);
+ if (ret < 0)
+ goto out_err;
+ if (ret & TPM_STS_COMMAND_READY)
+ break;
+
+ if (timer_get_us() > timeout)
+ goto out_err;
+
+ ret = cr50_i2c_ready(dev);
+ if (ret)
+ goto out_err;
+ } while (1);
+
+ while (len > 0) {
+ u8 mask = TPM_STS_VALID;
+
+ /* Wait for data if this is not the first chunk */
+ if (sent > 0)
+ mask |= TPM_STS_DATA_EXPECT;
+
+ if (cr50_i2c_wait_burststs(dev, mask, &burstcnt, &status) < 0)
+ goto out_err;
+
+ /*
+ * Use burstcnt - 1 to account for the address byte
+ * that is inserted by cr50_i2c_write()
+ */
+ limit = min(burstcnt - 1, len);
+ if (cr50_i2c_write(dev, tpm_data_fifo(priv->locality),
+ &buf[sent], limit) != 0) {
+ log_warning("Write failed\n");
+ goto out_err;
+ }
+
+ sent += limit;
+ len -= limit;
+ }
+
+ /* Ensure TPM is not expecting more data */
+ if (cr50_i2c_wait_burststs(dev, TPM_STS_VALID, &burstcnt, &status) < 0)
+ goto out_err;
+ if (status & TPM_STS_DATA_EXPECT) {
+ log_warning("Data still expected\n");
+ goto out_err;
+ }
+
+ /* Start the TPM command */
+ ret = cr50_i2c_write(dev, tpm_sts(priv->locality), tpm_go,
+ sizeof(tpm_go));
+ if (ret) {
+ log_warning("Start command failed\n");
+ goto out_err;
+ }
+
+ return sent;
+
+out_err:
+ /* Abort current transaction if still pending */
+ ret = cr50_i2c_status(dev);
+
+ if (ret < 0 || (ret & TPM_STS_COMMAND_READY)) {
+ ret = cr50_i2c_ready(dev);
+ if (ret)
+ return ret;
+ }
+
+ return -EIO;
+}
+
+/**
+ * process_reset() - Wait for the Cr50 to reset
+ *
+ * Cr50 processes reset requests asynchronously and conceivably could be busy
+ * executing a long command and not reacting to the reset pulse for a while.
+ *
+ * This function will make sure that the AP does not proceed with boot until
+ * TPM finished reset processing.
+ *
+ * @dev: Cr50 device
+ * @return 0 if OK, -EPERM if locality could not be taken
+ */
+static int process_reset(struct udevice *dev)
+{
+ const int loc = 0;
+ u8 access;
+ ulong start;
+
+ /*
+ * Locality is released by TPM reset.
+ *
+ * If locality is taken at this point, this could be due to the fact
+ * that the TPM is performing a long operation and has not processed
+ * reset request yet. We'll wait up to CR50_TIMEOUT_INIT_MS and see if
+ * it releases locality when reset is processed.
+ */
+ start = get_timer(0);
+ do {
+ const u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY;
+ int ret;
+
+ ret = cr50_i2c_read(dev, tpm_access(loc),
+ &access, sizeof(access));
+ if (ret || ((access & mask) == mask)) {
+ /*
+ * Don't bombard the chip with traffic; let it keep
+ * processing the command.
+ */
+ mdelay(2);
+ continue;
+ }
+
+ log_warning("TPM ready after %ld ms\n", get_timer(start));
+
+ return 0;
+ } while (get_timer(start) < TIMEOUT_INIT_MS);
+
+ log_warning("TPM failed to reset after %ld ms, status: %#x\n",
+ get_timer(start), access);
+
+ return -EPERM;
+}
+
+/*
+ * Locality could be already claimed (if this is a later U-Boot phase and the
+ * read-only U-Boot did not release it), or not yet claimed, if this is TPL or
+ * the older read-only U-Boot did release it.
+ */
+static int claim_locality(struct udevice *dev, int loc)
+{
+ const u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY;
+ u8 access;
+ int ret;
+
+ ret = cr50_i2c_read(dev, tpm_access(loc), &access, sizeof(access));
+ if (ret)
+ return log_msg_ret("read1", ret);
+
+ if ((access & mask) == mask) {
+ log_warning("Locality already claimed\n");
+ return 0;
+ }
+
+ access = TPM_ACCESS_REQUEST_USE;
+ ret = cr50_i2c_write(dev, tpm_access(loc), &access, sizeof(access));
+ if (ret)
+ return log_msg_ret("write", ret);
+
+ ret = cr50_i2c_read(dev, tpm_access(loc), &access, sizeof(access));
+ if (ret)
+ return log_msg_ret("read2", ret);
+
+ if ((access & mask) != mask) {
+ log_err("Failed to claim locality\n");
+ return -EPERM;
+ }
+ log_info("Claimed locality %d\n", loc);
+
+ return 0;
+}
+
+static int cr50_i2c_get_desc(struct udevice *dev, char *buf, int size)
+{
+ struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
+ struct cr50_priv *priv = dev_get_priv(dev);
+
+ return snprintf(buf, size, "cr50 TPM 2.0 (i2c %02x id %x) irq=%d",
+ chip->chip_addr, priv->vendor >> 16, priv->use_irq);
+}
+
+static int cr50_i2c_open(struct udevice *dev)
+{
+ char buf[80];
+ int ret;
+
+ ret = process_reset(dev);
+ if (ret)
+ return log_msg_ret("reset", ret);
+
+ ret = claim_locality(dev, 0);
+ if (ret)
+ return log_msg_ret("claim", ret);
+
+ cr50_i2c_get_desc(dev, buf, sizeof(buf));
+ log_debug("%s\n", buf);
+
+ return 0;
+}
+
+static int cr50_i2c_cleanup(struct udevice *dev)
+{
+ release_locality(dev, 1);
+
+ return 0;
+}
+
+enum {
+ TPM_TIMEOUT_MS = 5,
+ SHORT_TIMEOUT_MS = 750,
+ LONG_TIMEOUT_MS = 2000,
+};
+
+static int cr50_i2c_ofdata_to_platdata(struct udevice *dev)
+{
+ struct tpm_chip_priv *upriv = dev_get_uclass_priv(dev);
+ struct cr50_priv *priv = dev_get_priv(dev);
+ struct irq irq;
+ int ret;
+
+ upriv->version = TPM_V2;
+ upriv->duration_ms[TPM_SHORT] = SHORT_TIMEOUT_MS;
+ upriv->duration_ms[TPM_MEDIUM] = LONG_TIMEOUT_MS;
+ upriv->duration_ms[TPM_LONG] = LONG_TIMEOUT_MS;
+ upriv->retry_time_ms = TPM_TIMEOUT_MS;
+
+ upriv->pcr_count = 32;
+ upriv->pcr_select_min = 2;
+
+ /* Optional GPIO to track when cr50 is ready */
+ ret = irq_get_by_index(dev, 0, &irq);
+ if (!ret) {
+ priv->irq = irq;
+ priv->use_irq = true;
+ } else {
+ ret = gpio_request_by_name(dev, "ready-gpio", 0,
+ &priv->ready_gpio, GPIOD_IS_IN);
+ if (ret) {
+ log_warning("Cr50 does not have an ready GPIO/interrupt (err=%d)\n",
+ ret);
+ }
+ }
+
+ return 0;
+}
+
+static int cr50_i2c_probe(struct udevice *dev)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+ u32 vendor = 0;
+ ulong start;
+
+ /*
+ * 150ms should be enough to synchronise with the TPM even under the
+ * worst nested-reset-request conditions. In the vast majority of cases
+ * there will be no wait at all.
+ */
+ start = get_timer(0);
+ while (get_timer(start) < 150) {
+ int ret;
+
+ /* Exit once DID and VID verified */
+ ret = cr50_i2c_read(dev, tpm_did_vid(0), (u8 *)&vendor, 4);
+ if (!ret && vendor == CR50_DID_VID)
+ break;
+
+ /* TPM might be resetting; let's retry in a bit */
+ mdelay(10);
+ }
+ if (vendor != CR50_DID_VID) {
+ log_debug("DID_VID %08x not recognised\n", vendor);
+ return log_msg_ret("vendor-id", -EXDEV);
+ }
+ priv->vendor = vendor;
+
+ return 0;
+}
+
+static const struct tpm_ops cr50_i2c_ops = {
+ .open = cr50_i2c_open,
+ .get_desc = cr50_i2c_get_desc,
+ .send = cr50_i2c_send,
+ .recv = cr50_i2c_recv,
+ .cleanup = cr50_i2c_cleanup,
+};
+
+static const struct udevice_id cr50_i2c_ids[] = {
+ { .compatible = "google,cr50" },
+ { }
+};
+
+U_BOOT_DRIVER(cr50_i2c) = {
+ .name = "cr50_i2c",
+ .id = UCLASS_TPM,
+ .of_match = cr50_i2c_ids,
+ .ops = &cr50_i2c_ops,
+ .ofdata_to_platdata = cr50_i2c_ofdata_to_platdata,
+ .probe = cr50_i2c_probe,
+ .priv_auto_alloc_size = sizeof(struct cr50_priv),
+};
diff --git a/include/dm/test.h b/include/dm/test.h
index 07385cd531..f0f36624ce 100644
--- a/include/dm/test.h
+++ b/include/dm/test.h
@@ -56,6 +56,8 @@ enum {
enum {
DM_TEST_TYPE_FIRST = 0,
DM_TEST_TYPE_SECOND,
+
+ DM_TEST_TYPE_COUNT,
};
/* The number added to the ping total on each probe */
diff --git a/include/dm/uclass.h b/include/dm/uclass.h
index 484d166013..70fca79b44 100644
--- a/include/dm/uclass.h
+++ b/include/dm/uclass.h
@@ -351,6 +351,20 @@ int uclass_first_device_check(enum uclass_id id, struct udevice **devp);
int uclass_next_device_check(struct udevice **devp);
/**
+ * uclass_first_device_drvdata() - Find the first device with given driver data
+ *
+ * This searches through the devices for a particular uclass looking for one
+ * that has the given driver data.
+ *
+ * @id: Uclass ID to check
+ * @driver_data: Driver data to search for
+ * @devp: Returns pointer to the first matching device in that uclass, if found
+ * @return 0 if found, -ENODEV if not found, other -ve on error
+ */
+int uclass_first_device_drvdata(enum uclass_id id, ulong driver_data,
+ struct udevice **devp);
+
+/**
* uclass_resolve_seq() - Resolve a device's sequence number
*
* On entry dev->seq is -1, and dev->req_seq may be -1 (to allocate a
@@ -366,6 +380,23 @@ int uclass_next_device_check(struct udevice **devp);
int uclass_resolve_seq(struct udevice *dev);
/**
+ * uclass_id_foreach_dev() - Helper function to iteration through devices
+ *
+ * This creates a for() loop which works through the available devices in
+ * a uclass ID in order from start to end.
+ *
+ * If for some reason the uclass cannot be found, this does nothing.
+ *
+ * @id: enum uclass_id ID to use
+ * @pos: struct udevice * to hold the current device. Set to NULL when there
+ * are no more devices.
+ * @uc: temporary uclass variable (struct udevice *)
+ */
+#define uclass_id_foreach_dev(id, pos, uc) \
+ if (!uclass_get(id, &uc)) \
+ list_for_each_entry(pos, &uc->dev_head, uclass_node)
+
+/**
* uclass_foreach_dev() - Helper function to iteration through devices
*
* This creates a for() loop which works through the available devices in
diff --git a/include/dt-bindings/clock/intel-clock.h b/include/dt-bindings/clock/intel-clock.h
new file mode 100644
index 0000000000..e1edd3c71d
--- /dev/null
+++ b/include/dt-bindings/clock/intel-clock.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * This header provides constants for Intel clocks.
+ *
+ * The constants defined in this header are used in the device tree
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#ifndef _DT_BINDINGS_CLK_INTEL_H
+#define _DT_BINDINGS_CLK_INTEL_H
+
+#define CLK_I2C 1
+
+#endif
diff --git a/include/irq.h b/include/irq.h
index 01ded64f16..b71afe9bee 100644
--- a/include/irq.h
+++ b/include/irq.h
@@ -8,8 +8,32 @@
#ifndef __irq_H
#define __irq_H
+/*
+ * Interrupt controller types available. You can find a particular one with
+ * irq_first_device_type()
+ */
+enum irq_dev_t {
+ X86_IRQT_BASE, /* Base controller */
+ X86_IRQT_ITSS, /* ITSS controller, e.g. on APL */
+ X86_IRQT_ACPI_GPE, /* ACPI General-Purpose Events controller */
+ SANDBOX_IRQT_BASE, /* Sandbox testing */
+};
+
+/**
+ * struct irq - A single irq line handled by an interrupt controller
+ *
+ * @dev: IRQ device that handles this irq
+ * @id: ID to identify this irq with the device
+ */
+struct irq {
+ struct udevice *dev;
+ ulong id;
+};
+
/**
* struct irq_ops - Operations for the IRQ
+ *
+ * Each IRQ device can handle mulitple IRQ lines
*/
struct irq_ops {
/**
@@ -46,6 +70,55 @@ struct irq_ops {
* @return 0
*/
int (*restore_polarities)(struct udevice *dev);
+
+ /**
+ * read_and_clear() - get the value of an interrupt and clear it
+ *
+ * Clears the interrupt if pending
+ *
+ * @irq: IRQ line
+ * @return 0 if interrupt is not pending, 1 if it was (and so has been
+ * cleared), -ve on error
+ */
+ int (*read_and_clear)(struct irq *irq);
+ /**
+ * of_xlate - Translate a client's device-tree (OF) irq specifier.
+ *
+ * The irq core calls this function as the first step in implementing
+ * a client's irq_get_by_*() call.
+ *
+ * If this function pointer is set to NULL, the irq core will use a
+ * default implementation, which assumes #interrupt-cells = <1>, and
+ * that the DT cell contains a simple integer irq ID.
+ *
+ * @irq: The irq struct to hold the translation result.
+ * @args: The irq specifier values from device tree.
+ * @return 0 if OK, or a negative error code.
+ */
+ int (*of_xlate)(struct irq *irq, struct ofnode_phandle_args *args);
+ /**
+ * request - Request a translated irq.
+ *
+ * The irq core calls this function as the second step in
+ * implementing a client's irq_get_by_*() call, following a successful
+ * xxx_xlate() call, or as the only step in implementing a client's
+ * irq_request() call.
+ *
+ * @irq: The irq struct to request; this has been fille in by
+ * a previoux xxx_xlate() function call, or by the caller
+ * of irq_request().
+ * @return 0 if OK, or a negative error code.
+ */
+ int (*request)(struct irq *irq);
+ /**
+ * free - Free a previously requested irq.
+ *
+ * This is the implementation of the client irq_free() API.
+ *
+ * @irq: The irq to free.
+ * @return 0 if OK, or a negative error code.
+ */
+ int (*free)(struct irq *irq);
};
#define irq_get_ops(dev) ((struct irq_ops *)(dev)->driver->ops)
@@ -85,4 +158,69 @@ int irq_snapshot_polarities(struct udevice *dev);
*/
int irq_restore_polarities(struct udevice *dev);
+/**
+ * read_and_clear() - get the value of an interrupt and clear it
+ *
+ * Clears the interrupt if pending
+ *
+ * @dev: IRQ device
+ * @return 0 if interrupt is not pending, 1 if it was (and so has been
+ * cleared), -ve on error
+ */
+int irq_read_and_clear(struct irq *irq);
+
+/**
+ * irq_get_by_index - Get/request an irq by integer index.
+ *
+ * This looks up and requests an irq. The index is relative to the client
+ * device; each device is assumed to have n irqs associated with it somehow,
+ * and this function finds and requests one of them. The mapping of client
+ * device irq indices to provider irqs may be via device-tree
+ * properties, board-provided mapping tables, or some other mechanism.
+ *
+ * @dev: The client device.
+ * @index: The index of the irq to request, within the client's list of
+ * irqs.
+ * @irq: A pointer to a irq struct to initialise.
+ * @return 0 if OK, or a negative error code.
+ */
+int irq_get_by_index(struct udevice *dev, int index, struct irq *irq);
+
+/**
+ * irq_request - Request a irq by provider-specific ID.
+ *
+ * This requests a irq using a provider-specific ID. Generally, this function
+ * should not be used, since irq_get_by_index/name() provide an interface that
+ * better separates clients from intimate knowledge of irq providers.
+ * However, this function may be useful in core SoC-specific code.
+ *
+ * @dev: The irq provider device.
+ * @irq: A pointer to a irq struct to initialise. The caller must
+ * have already initialised any field in this struct which the
+ * irq provider uses to identify the irq.
+ * @return 0 if OK, or a negative error code.
+ */
+int irq_request(struct udevice *dev, struct irq *irq);
+
+/**
+ * irq_free - Free a previously requested irq.
+ *
+ * @irq: A irq struct that was previously successfully requested by
+ * irq_request/get_by_*().
+ * @return 0 if OK, or a negative error code.
+ */
+int irq_free(struct irq *irq);
+
+/**
+ * irq_first_device_type() - Get a particular interrupt controller
+ *
+ * On success this returns an activated interrupt device.
+ *
+ * @type: Type to find
+ * @devp: Returns the device, if found
+ * @return 0 if OK, -ENODEV if not found, other -ve error if uclass failed to
+ * probe
+ */
+int irq_first_device_type(enum irq_dev_t type, struct udevice **devp);
+
#endif
diff --git a/include/tpm-v2.h b/include/tpm-v2.h
index ae00803f6d..d53d2e4023 100644
--- a/include/tpm-v2.h
+++ b/include/tpm-v2.h
@@ -161,6 +161,37 @@ enum tpm_index_attrs {
TPMA_NV_AUTHWRITE | TPMA_NV_POLICYWRITE,
};
+enum {
+ TPM_ACCESS_VALID = 1 << 7,
+ TPM_ACCESS_ACTIVE_LOCALITY = 1 << 5,
+ TPM_ACCESS_REQUEST_PENDING = 1 << 2,
+ TPM_ACCESS_REQUEST_USE = 1 << 1,
+ TPM_ACCESS_ESTABLISHMENT = 1 << 0,
+};
+
+enum {
+ TPM_STS_FAMILY_SHIFT = 26,
+ TPM_STS_FAMILY_MASK = 0x3 << TPM_STS_FAMILY_SHIFT,
+ TPM_STS_FAMILY_TPM2 = 1 << TPM_STS_FAMILY_SHIFT,
+ TPM_STS_RESE_TESTABLISMENT_BIT = 1 << 25,
+ TPM_STS_COMMAND_CANCEL = 1 << 24,
+ TPM_STS_BURST_COUNT_SHIFT = 8,
+ TPM_STS_BURST_COUNT_MASK = 0xffff << TPM_STS_BURST_COUNT_SHIFT,
+ TPM_STS_VALID = 1 << 7,
+ TPM_STS_COMMAND_READY = 1 << 6,
+ TPM_STS_GO = 1 << 5,
+ TPM_STS_DATA_AVAIL = 1 << 4,
+ TPM_STS_DATA_EXPECT = 1 << 3,
+ TPM_STS_SELF_TEST_DONE = 1 << 2,
+ TPM_STS_RESPONSE_RETRY = 1 << 1,
+};
+
+enum {
+ TPM_CMD_COUNT_OFFSET = 2,
+ TPM_CMD_ORDINAL_OFFSET = 6,
+ TPM_MAX_BUF_SIZE = 1260,
+};
+
/**
* Issue a TPM2_Startup command.
*
diff --git a/test/dm/irq.c b/test/dm/irq.c
index 726189c59f..192d80d7e1 100644
--- a/test/dm/irq.c
+++ b/test/dm/irq.c
@@ -8,6 +8,7 @@
#include <common.h>
#include <dm.h>
#include <irq.h>
+#include <asm/test.h>
#include <dm/test.h>
#include <test/ut.h>
@@ -30,3 +31,47 @@ static int dm_test_irq_base(struct unit_test_state *uts)
return 0;
}
DM_TEST(dm_test_irq_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test of irq_first_device_type() */
+static int dm_test_irq_type(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(irq_first_device_type(SANDBOX_IRQT_BASE, &dev));
+ ut_asserteq(-ENODEV, irq_first_device_type(X86_IRQT_BASE, &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_irq_type, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test of irq_read_and_clear() */
+static int dm_test_read_and_clear(struct unit_test_state *uts)
+{
+ struct irq irq;
+
+ ut_assertok(irq_first_device_type(SANDBOX_IRQT_BASE, &irq.dev));
+ irq.id = SANDBOX_IRQN_PEND;
+ ut_asserteq(0, irq_read_and_clear(&irq));
+ ut_asserteq(0, irq_read_and_clear(&irq));
+ ut_asserteq(0, irq_read_and_clear(&irq));
+ ut_asserteq(1, irq_read_and_clear(&irq));
+ ut_asserteq(0, irq_read_and_clear(&irq));
+
+ return 0;
+}
+DM_TEST(dm_test_read_and_clear, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test of irq_request() */
+static int dm_test_request(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct irq irq;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(irq_get_by_index(dev, 0, &irq));
+ ut_asserteq(3, irq.id);
+
+ return 0;
+}
+DM_TEST(dm_test_request, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c
index d59c449ce0..698ca0e7cf 100644
--- a/test/dm/test-fdt.c
+++ b/test/dm/test-fdt.c
@@ -448,6 +448,27 @@ static int dm_test_first_next_device(struct unit_test_state *uts)
}
DM_TEST(dm_test_first_next_device, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+/* Test iteration through devices in a uclass */
+static int dm_test_uclass_foreach(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct uclass *uc;
+ int count;
+
+ count = 0;
+ uclass_id_foreach_dev(UCLASS_TEST_FDT, dev, uc)
+ count++;
+ ut_asserteq(8, count);
+
+ count = 0;
+ uclass_foreach_dev(dev, uc)
+ count++;
+ ut_asserteq(8, count);
+
+ return 0;
+}
+DM_TEST(dm_test_uclass_foreach, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
/**
* check_devices() - Check return values and pointers
*
@@ -872,3 +893,24 @@ static int dm_test_read_int(struct unit_test_state *uts)
return 0;
}
DM_TEST(dm_test_read_int, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test iteration through devices by drvdata */
+static int dm_test_uclass_drvdata(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_first_device_drvdata(UCLASS_TEST_FDT,
+ DM_TEST_TYPE_FIRST, &dev));
+ ut_asserteq_str("a-test", dev->name);
+
+ ut_assertok(uclass_first_device_drvdata(UCLASS_TEST_FDT,
+ DM_TEST_TYPE_SECOND, &dev));
+ ut_asserteq_str("d-test", dev->name);
+
+ ut_asserteq(-ENODEV, uclass_first_device_drvdata(UCLASS_TEST_FDT,
+ DM_TEST_TYPE_COUNT,
+ &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_uclass_drvdata, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);