- 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
This commit is contained in:
Tom Rini 2020-02-10 12:27:31 -05:00
commit 4e5c4683b7
38 changed files with 1641 additions and 95 deletions

View File

@ -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 {

View File

@ -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,

View File

@ -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

View File

@ -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

85
arch/x86/cpu/acpi_gpe.c Normal file
View File

@ -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),
};

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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;

View File

@ -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

View File

@ -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),

View File

@ -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 },
{ }
};

View File

@ -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;

View File

@ -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

View File

@ -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

View File

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

View File

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

View File

@ -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)

View File

@ -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/

View File

@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright 2010 Google LLC
#
obj-y += clk_intel.o

View File

@ -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,
};

View File

@ -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);
if (ret)
return ret;
uclass_foreach_dev(dev, uc) {
if (dev->driver_data == driver_data) {
*devp = dev;
return device_probe(dev);
}
}
return -ENODEV;
ret = uclass_first_device_drvdata(UCLASS_SYSCON, driver_data, devp);
if (ret)
return log_msg_ret("find", ret);
return 0;
}
struct regmap *syscon_get_regmap_by_driver_data(ulong driver_data)

View File

@ -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;

View File

@ -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 = {

View File

@ -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",

View File

@ -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),
};

View File

@ -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;

View File

@ -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

View File

@ -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

659
drivers/tpm/cr50_i2c.c Normal file
View File

@ -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),
};

View File

@ -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 */

View File

@ -350,6 +350,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
*
@ -365,6 +379,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
*

View File

@ -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

View File

@ -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

View File

@ -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.
*

View File

@ -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);

View File

@ -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);