From 71f1e3f19d26c7956de5c37035478fcaa9e979e7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 11 Aug 2015 01:09:43 +0900 Subject: dm: simplify uclass_foreach_dev() implementation This can be simply written with list_for_each_entry(), maybe this macro was not necessary in the first place. Signed-off-by: Masahiro Yamada Acked-by: Simon Glass --- include/dm/uclass.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/dm/uclass.h b/include/dm/uclass.h index d56877c8982..d214b887341 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -240,12 +240,7 @@ int uclass_resolve_seq(struct udevice *dev); * are no more devices. * @uc: uclass to scan */ -#define uclass_foreach_dev(pos, uc) \ - for (pos = list_entry((&(uc)->dev_head)->next, typeof(*pos), \ - uclass_node); \ - prefetch(pos->uclass_node.next), \ - &pos->uclass_node != (&(uc)->dev_head); \ - pos = list_entry(pos->uclass_node.next, typeof(*pos), \ - uclass_node)) +#define uclass_foreach_dev(pos, uc) \ + list_for_each_entry(pos, &uc->dev_head, uclass_node) #endif -- cgit v1.3.1 From afc84dec1c2721269c8eaad13a938fa18360dbc7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 22 Aug 2015 18:31:18 -0600 Subject: tpm: Drop two unused options The address of the I2C TPM is now defined in the device tree so there is no need for the CONFIG options. Remove them from the README and board config to avoid confusion. Signed-off-by: Simon Glass Acked-by: Christophe Ricard Reviewed-by: Heiko Schocher --- README | 6 ------ include/configs/exynos5-common.h | 2 -- 2 files changed, 8 deletions(-) (limited to 'include') diff --git a/README b/README index a52ff46c213..1acc3559680 100644 --- a/README +++ b/README @@ -1494,12 +1494,6 @@ The following options need to be configured: Support for i2c bus TPM devices. Only one device per system is supported at this time. - CONFIG_TPM_TIS_I2C_BUS_NUMBER - Define the the i2c bus number for the TPM device - - CONFIG_TPM_TIS_I2C_SLAVE_ADDRESS - Define the TPM's address on the i2c bus - CONFIG_TPM_TIS_I2C_BURST_LIMITATION Define the burst count bytes upper limit diff --git a/include/configs/exynos5-common.h b/include/configs/exynos5-common.h index e710f41f79e..fb5ee0d39a0 100644 --- a/include/configs/exynos5-common.h +++ b/include/configs/exynos5-common.h @@ -58,8 +58,6 @@ #define CONFIG_TPM #define CONFIG_CMD_TPM #define CONFIG_TPM_TIS_I2C -#define CONFIG_TPM_TIS_I2C_BUS_NUMBER 3 -#define CONFIG_TPM_TIS_I2C_SLAVE_ADDR 0x20 /* MMC SPL */ #define COPY_BL2_FNPTR_ADDR 0x02020030 -- cgit v1.3.1 From 26468d5186b437eba0c4baf9b7ac0e4b9b18ad39 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 22 Aug 2015 18:31:20 -0600 Subject: tpm: Convert board config TPM options to Kconfig Convert all TPM options to Kconfig and tidy up. Signed-off-by: Simon Glass Acked-by: Christophe Ricard Reviewed-by: Heiko Schocher --- configs/chromebook_link_defconfig | 2 ++ configs/chromebox_panther_defconfig | 2 ++ configs/controlcenterd_36BIT_SDCARD_DEVELOP_defconfig | 4 ++++ configs/controlcenterd_36BIT_SDCARD_defconfig | 4 ++++ configs/controlcenterd_TRAILBLAZER_DEVELOP_defconfig | 4 ++++ configs/controlcenterd_TRAILBLAZER_defconfig | 4 ++++ configs/coreboot-x86_defconfig | 2 ++ configs/peach-pi_defconfig | 3 +++ configs/peach-pit_defconfig | 3 +++ configs/sandbox_defconfig | 2 ++ configs/snow_defconfig | 3 +++ configs/spring_defconfig | 3 +++ include/configs/controlcenterd.h | 7 ------- include/configs/efi-x86.h | 2 -- include/configs/exynos5-common.h | 5 ----- include/configs/x86-common.h | 2 -- 16 files changed, 36 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/configs/chromebook_link_defconfig b/configs/chromebook_link_defconfig index b987f3f52f6..b0fc5ca8677 100644 --- a/configs/chromebook_link_defconfig +++ b/configs/chromebook_link_defconfig @@ -17,9 +17,11 @@ CONFIG_SPI_FLASH=y CONFIG_CMD_CROS_EC=y CONFIG_CROS_EC=y CONFIG_CROS_EC_LPC=y +CONFIG_TPM_TIS_LPC=y CONFIG_VIDEO_VESA=y CONFIG_FRAMEBUFFER_SET_VESA_MODE=y CONFIG_FRAMEBUFFER_VESA_MODE_11A=y CONFIG_DM_RTC=y CONFIG_USE_PRIVATE_LIBGCC=y CONFIG_SYS_VSNPRINTF=y +CONFIG_TPM=y diff --git a/configs/chromebox_panther_defconfig b/configs/chromebox_panther_defconfig index e82c8ecf64e..e7ef8feb668 100644 --- a/configs/chromebox_panther_defconfig +++ b/configs/chromebox_panther_defconfig @@ -17,9 +17,11 @@ CONFIG_SPI_FLASH=y CONFIG_CMD_CROS_EC=y CONFIG_CROS_EC=y CONFIG_CROS_EC_LPC=y +CONFIG_TPM_TIS_LPC=y CONFIG_VIDEO_VESA=y CONFIG_FRAMEBUFFER_SET_VESA_MODE=y CONFIG_FRAMEBUFFER_VESA_MODE_11A=y CONFIG_DM_RTC=y CONFIG_USE_PRIVATE_LIBGCC=y CONFIG_SYS_VSNPRINTF=y +CONFIG_TPM=y diff --git a/configs/controlcenterd_36BIT_SDCARD_DEVELOP_defconfig b/configs/controlcenterd_36BIT_SDCARD_DEVELOP_defconfig index 37ead03a432..c8ab862edcc 100644 --- a/configs/controlcenterd_36BIT_SDCARD_DEVELOP_defconfig +++ b/configs/controlcenterd_36BIT_SDCARD_DEVELOP_defconfig @@ -4,4 +4,8 @@ CONFIG_TARGET_CONTROLCENTERD=y CONFIG_SYS_EXTRA_OPTIONS="36BIT,SDCARD,DEVELOP" # CONFIG_CMD_IMLS is not set # CONFIG_CMD_FLASH is not set +CONFIG_CMD_TPM=y CONFIG_SPI_FLASH=y +CONFIG_TPM_ATMEL_TWI=y +CONFIG_TPM_AUTH_SESSIONS=y +CONFIG_TPM=y diff --git a/configs/controlcenterd_36BIT_SDCARD_defconfig b/configs/controlcenterd_36BIT_SDCARD_defconfig index 7166edbf9d4..21c0eab53dc 100644 --- a/configs/controlcenterd_36BIT_SDCARD_defconfig +++ b/configs/controlcenterd_36BIT_SDCARD_defconfig @@ -4,4 +4,8 @@ CONFIG_TARGET_CONTROLCENTERD=y CONFIG_SYS_EXTRA_OPTIONS="36BIT,SDCARD" # CONFIG_CMD_IMLS is not set # CONFIG_CMD_FLASH is not set +CONFIG_CMD_TPM=y CONFIG_SPI_FLASH=y +CONFIG_TPM_ATMEL_TWI=y +CONFIG_TPM_AUTH_SESSIONS=y +CONFIG_TPM=y diff --git a/configs/controlcenterd_TRAILBLAZER_DEVELOP_defconfig b/configs/controlcenterd_TRAILBLAZER_DEVELOP_defconfig index d99fcd45135..c3a0920d7cd 100644 --- a/configs/controlcenterd_TRAILBLAZER_DEVELOP_defconfig +++ b/configs/controlcenterd_TRAILBLAZER_DEVELOP_defconfig @@ -6,3 +6,7 @@ CONFIG_SYS_EXTRA_OPTIONS="TRAILBLAZER,SPIFLASH,DEVELOP" # CONFIG_CMD_IMLS is not set # CONFIG_CMD_FLASH is not set # CONFIG_CMD_SETEXPR is not set +CONFIG_CMD_TPM=y +CONFIG_TPM_ATMEL_TWI=y +CONFIG_TPM_AUTH_SESSIONS=y +CONFIG_TPM=y diff --git a/configs/controlcenterd_TRAILBLAZER_defconfig b/configs/controlcenterd_TRAILBLAZER_defconfig index 3557aea1ebb..08aae9ae2db 100644 --- a/configs/controlcenterd_TRAILBLAZER_defconfig +++ b/configs/controlcenterd_TRAILBLAZER_defconfig @@ -6,3 +6,7 @@ CONFIG_SYS_EXTRA_OPTIONS="TRAILBLAZER,SPIFLASH" # CONFIG_CMD_IMLS is not set # CONFIG_CMD_FLASH is not set # CONFIG_CMD_SETEXPR is not set +CONFIG_CMD_TPM=y +CONFIG_TPM_ATMEL_TWI=y +CONFIG_TPM_AUTH_SESSIONS=y +CONFIG_TPM=y diff --git a/configs/coreboot-x86_defconfig b/configs/coreboot-x86_defconfig index df3a624ecd1..66b8caa30ff 100644 --- a/configs/coreboot-x86_defconfig +++ b/configs/coreboot-x86_defconfig @@ -14,5 +14,7 @@ CONFIG_DM_PCI=y CONFIG_SPI_FLASH=y CONFIG_NETDEVICES=y CONFIG_E1000=y +CONFIG_TPM_TIS_LPC=y CONFIG_USE_PRIVATE_LIBGCC=y CONFIG_SYS_VSNPRINTF=y +CONFIG_TPM=y diff --git a/configs/peach-pi_defconfig b/configs/peach-pi_defconfig index 323a2d38b3d..2be74fd1fde 100644 --- a/configs/peach-pi_defconfig +++ b/configs/peach-pi_defconfig @@ -7,11 +7,13 @@ CONFIG_SPL=y # CONFIG_CMD_SETEXPR is not set CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y +CONFIG_CMD_TPM=y CONFIG_SPI_FLASH=y CONFIG_CMD_CROS_EC=y CONFIG_CROS_EC=y CONFIG_CROS_EC_SPI=y CONFIG_CROS_EC_KEYB=y +CONFIG_TPM_TIS_I2C=y CONFIG_DM_I2C=y CONFIG_DM_I2C_COMPAT=y CONFIG_I2C_CROS_EC_TUNNEL=y @@ -30,5 +32,6 @@ CONFIG_SOUND_MAX98095=y CONFIG_SOUND_WM8994=y CONFIG_USB=y CONFIG_DM_USB=y +CONFIG_TPM=y CONFIG_ERRNO_STR=y CONFIG_SYS_PROMPT="Peach-Pi # " diff --git a/configs/peach-pit_defconfig b/configs/peach-pit_defconfig index 6a082968b9e..875ddd19419 100644 --- a/configs/peach-pit_defconfig +++ b/configs/peach-pit_defconfig @@ -7,11 +7,13 @@ CONFIG_SPL=y # CONFIG_CMD_SETEXPR is not set CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y +CONFIG_CMD_TPM=y CONFIG_SPI_FLASH=y CONFIG_CMD_CROS_EC=y CONFIG_CROS_EC=y CONFIG_CROS_EC_SPI=y CONFIG_CROS_EC_KEYB=y +CONFIG_TPM_TIS_I2C=y CONFIG_DM_I2C=y CONFIG_DM_I2C_COMPAT=y CONFIG_I2C_CROS_EC_TUNNEL=y @@ -30,5 +32,6 @@ CONFIG_SOUND_MAX98095=y CONFIG_SOUND_WM8994=y CONFIG_USB=y CONFIG_DM_USB=y +CONFIG_TPM=y CONFIG_ERRNO_STR=y CONFIG_SYS_PROMPT="Peach-Pit # " diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 85ff95df796..b68d6887668 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -11,6 +11,7 @@ CONFIG_BOOTSTAGE=y CONFIG_BOOTSTAGE_REPORT=y CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y +CONFIG_CMD_TPM=y CONFIG_OF_CONTROL=y CONFIG_OF_HOSTFILE=y CONFIG_CLK=y @@ -49,6 +50,7 @@ CONFIG_DM_MMC=y CONFIG_DM_RTC=y CONFIG_SYS_VSNPRINTF=y CONFIG_CMD_DHRYSTONE=y +CONFIG_TPM=y CONFIG_ERRNO_STR=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y diff --git a/configs/snow_defconfig b/configs/snow_defconfig index f5decd5c69f..f59aa3ff5e4 100644 --- a/configs/snow_defconfig +++ b/configs/snow_defconfig @@ -8,6 +8,7 @@ CONFIG_SPL=y CONFIG_CMD_SOUND=y CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y +CONFIG_CMD_TPM=y CONFIG_SPI_FLASH=y CONFIG_CMD_CROS_EC=y CONFIG_CROS_EC=y @@ -17,6 +18,7 @@ CONFIG_DEBUG_UART=y CONFIG_DEBUG_UART_S5P=y CONFIG_DEBUG_UART_BASE=0x12c30000 CONFIG_DEBUG_UART_CLOCK=100000000 +CONFIG_TPM_TIS_I2C=y CONFIG_DM_I2C=y CONFIG_DM_I2C_COMPAT=y CONFIG_I2C_CROS_EC_LDO=y @@ -41,4 +43,5 @@ CONFIG_SOUND_MAX98095=y CONFIG_SOUND_WM8994=y CONFIG_USB=y CONFIG_DM_USB=y +CONFIG_TPM=y CONFIG_ERRNO_STR=y diff --git a/configs/spring_defconfig b/configs/spring_defconfig index f1d9a58ad15..6144f03b311 100644 --- a/configs/spring_defconfig +++ b/configs/spring_defconfig @@ -8,6 +8,7 @@ CONFIG_SPL=y CONFIG_CMD_SOUND=y CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y +CONFIG_CMD_TPM=y CONFIG_SPI_FLASH=y CONFIG_CMD_CROS_EC=y CONFIG_CROS_EC=y @@ -17,6 +18,7 @@ CONFIG_DEBUG_UART=y CONFIG_DEBUG_UART_S5P=y CONFIG_DEBUG_UART_BASE=0x12c30000 CONFIG_DEBUG_UART_CLOCK=100000000 +CONFIG_TPM_TIS_I2C=y CONFIG_DM_I2C=y CONFIG_DM_I2C_COMPAT=y CONFIG_I2C_CROS_EC_LDO=y @@ -40,4 +42,5 @@ CONFIG_SOUND_MAX98095=y CONFIG_SOUND_WM8994=y CONFIG_USB=y CONFIG_DM_USB=y +CONFIG_TPM=y CONFIG_ERRNO_STR=y diff --git a/include/configs/controlcenterd.h b/include/configs/controlcenterd.h index 8f829eddb1d..9a1f6d0782c 100644 --- a/include/configs/controlcenterd.h +++ b/include/configs/controlcenterd.h @@ -226,14 +226,7 @@ #define CONFIG_SF_DEFAULT_MODE 0 #endif -/* - * TPM - */ -#define CONFIG_TPM_ATMEL_TWI -#define CONFIG_TPM -#define CONFIG_TPM_AUTH_SESSIONS #define CONFIG_SHA1 -#define CONFIG_CMD_TPM /* * MMC diff --git a/include/configs/efi-x86.h b/include/configs/efi-x86.h index 5779cfdb66b..1c955d9e37a 100644 --- a/include/configs/efi-x86.h +++ b/include/configs/efi-x86.h @@ -11,8 +11,6 @@ #undef CONFIG_CMD_SF_TEST -#undef CONFIG_TPM -#undef CONFIG_TPM_TIS_LPC #undef CONFIG_TPM_TIS_BASE_ADDRESS #undef CONFIG_CMD_IMLS diff --git a/include/configs/exynos5-common.h b/include/configs/exynos5-common.h index fb5ee0d39a0..4866836d552 100644 --- a/include/configs/exynos5-common.h +++ b/include/configs/exynos5-common.h @@ -54,11 +54,6 @@ #define CONFIG_CMD_DTT #define CONFIG_TMU_CMD_DTT -/* TPM */ -#define CONFIG_TPM -#define CONFIG_CMD_TPM -#define CONFIG_TPM_TIS_I2C - /* MMC SPL */ #define COPY_BL2_FNPTR_ADDR 0x02020030 #define CONFIG_SUPPORT_EMMC_BOOT diff --git a/include/configs/x86-common.h b/include/configs/x86-common.h index 217312e5b72..3f153f24aea 100644 --- a/include/configs/x86-common.h +++ b/include/configs/x86-common.h @@ -47,8 +47,6 @@ #endif /* Generic TPM interfaced through LPC bus */ -#define CONFIG_TPM -#define CONFIG_TPM_TIS_LPC #define CONFIG_TPM_TIS_BASE_ADDRESS 0xfed40000 /*----------------------------------------------------------------------- -- cgit v1.3.1 From f255d31f9063e50b56208fff439b63039cfd7ac6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 22 Aug 2015 18:31:31 -0600 Subject: dm: tpm: Add a uclass for Trusted Platform Modules Add a new uclass for TPMs which uses almost the same TIS (TPM Interface Specification) as is currently implemented. Since init() is handled by the normal driver model probe() method, we don't need to implement that. Also rename the transfer method to xfer() which is a less clumbsy name. Once all drivers and users are converted to driver model we can remove the old code. Signed-off-by: Simon Glass Acked-by: Christophe Ricard Reviewed-by: Heiko Schocher --- drivers/tpm/Kconfig | 9 ++ drivers/tpm/Makefile | 2 + drivers/tpm/tpm-uclass.c | 133 +++++++++++++++++++++ drivers/tpm/tpm_internal.h | 287 +++++++++++++++++++++++++++++++++++++++++++++ drivers/tpm/tpm_tis_i2c.c | 2 + drivers/tpm/tpm_tis_i2c.h | 283 -------------------------------------------- drivers/tpm/tpm_tis_lpc.c | 4 +- include/dm/uclass-id.h | 1 + include/tis.h | 3 + include/tpm.h | 192 ++++++++++++++++++++++++++++++ 10 files changed, 630 insertions(+), 286 deletions(-) create mode 100644 drivers/tpm/tpm-uclass.c create mode 100644 drivers/tpm/tpm_internal.h (limited to 'include') diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig index 9101fc26b9d..6bc8fddbd16 100644 --- a/drivers/tpm/Kconfig +++ b/drivers/tpm/Kconfig @@ -4,6 +4,15 @@ menu "TPM support" +config DM_TPM + bool "Enable driver model for Trusted Platform Module drivers" + depends on DM && TPM + help + Enable driver model for TPMs. The TIS interface (tis_open(), + tis_sendrecv(), etc.) is then implemented by the TPM uclass. Note + that even with driver model only a single TPM is currently + supported, since the tpm library assumes this. + config TPM_TIS_SANDBOX bool "Enable sandbox TPM driver" depends on SANDBOX diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile index 597966ce1fa..0d328f8d9d3 100644 --- a/drivers/tpm/Makefile +++ b/drivers/tpm/Makefile @@ -3,6 +3,8 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_DM_TPM) += tpm-uclass.o + obj-$(CONFIG_TPM_ATMEL_TWI) += tpm_atmel_twi.o obj-$(CONFIG_TPM_TIS_I2C) += tpm_tis_i2c.o obj-$(CONFIG_TPM_TIS_LPC) += tpm_tis_lpc.o diff --git a/drivers/tpm/tpm-uclass.c b/drivers/tpm/tpm-uclass.c new file mode 100644 index 00000000000..b6e1fc5e628 --- /dev/null +++ b/drivers/tpm/tpm-uclass.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include "tpm_internal.h" + +int tpm_open(struct udevice *dev) +{ + struct tpm_ops *ops = tpm_get_ops(dev); + + if (!ops->open) + return -ENOSYS; + + return ops->open(dev); +} + +int tpm_close(struct udevice *dev) +{ + struct tpm_ops *ops = tpm_get_ops(dev); + + if (!ops->close) + return -ENOSYS; + + return ops->close(dev); +} + +int tpm_get_desc(struct udevice *dev, char *buf, int size) +{ + struct tpm_ops *ops = tpm_get_ops(dev); + + if (!ops->get_desc) + return -ENOSYS; + + return ops->get_desc(dev, buf, size); +} + +/* Returns max number of milliseconds to wait */ +static ulong tpm_tis_i2c_calc_ordinal_duration(struct tpm_chip_priv *priv, + u32 ordinal) +{ + int duration_idx = TPM_UNDEFINED; + int duration = 0; + + if (ordinal < TPM_MAX_ORDINAL) { + duration_idx = tpm_ordinal_duration[ordinal]; + } else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) < + TPM_MAX_PROTECTED_ORDINAL) { + duration_idx = tpm_protected_ordinal_duration[ + ordinal & TPM_PROTECTED_ORDINAL_MASK]; + } + + if (duration_idx != TPM_UNDEFINED) + duration = priv->duration_ms[duration_idx]; + + if (duration <= 0) + return 2 * 60 * 1000; /* Two minutes timeout */ + else + return duration; +} + +int tpm_xfer(struct udevice *dev, const uint8_t *sendbuf, size_t send_size, + uint8_t *recvbuf, size_t *recv_size) +{ + struct tpm_chip_priv *priv = dev_get_uclass_priv(dev); + struct tpm_ops *ops = tpm_get_ops(dev); + ulong start, stop; + uint count, ordinal; + int ret, ret2; + + if (ops->xfer) + return ops->xfer(dev, sendbuf, send_size, recvbuf, recv_size); + + if (!ops->send || !ops->recv) + return -ENOSYS; + + /* switch endianess: big->little */ + count = get_unaligned_be32(sendbuf + TPM_CMD_COUNT_BYTE); + ordinal = get_unaligned_be32(sendbuf + TPM_CMD_ORDINAL_BYTE); + + if (count == 0) { + debug("no data\n"); + return -ENODATA; + } + if (count > send_size) { + debug("invalid count value %x %zx\n", count, send_size); + return -E2BIG; + } + + debug("%s: Calling send\n", __func__); + ret = ops->send(dev, sendbuf, send_size); + if (ret < 0) + return ret; + + start = get_timer(0); + stop = tpm_tis_i2c_calc_ordinal_duration(priv, ordinal); + do { + ret = ops->recv(dev, priv->buf, sizeof(priv->buf)); + if (ret >= 0) { + if (ret > *recv_size) + return -ENOSPC; + memcpy(recvbuf, priv->buf, ret); + *recv_size = ret; + ret = 0; + break; + } else if (ret != -EAGAIN) { + return ret; + } + + mdelay(priv->retry_time_ms); + if (get_timer(start) > stop) { + ret = -ETIMEDOUT; + break; + } + } while (ret); + + ret2 = ops->cleanup ? ops->cleanup(dev) : 0; + + return ret2 ? ret2 : ret; +} + +UCLASS_DRIVER(tpm) = { + .id = UCLASS_TPM, + .name = "tpm", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .per_device_auto_alloc_size = sizeof(struct tpm_chip_priv), +}; diff --git a/drivers/tpm/tpm_internal.h b/drivers/tpm/tpm_internal.h new file mode 100644 index 00000000000..cd29dba0b68 --- /dev/null +++ b/drivers/tpm/tpm_internal.h @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __tpm_internal_h +#define __tpm_internal_h + +enum { + TPM_MAX_ORDINAL = 243, + TPM_MAX_PROTECTED_ORDINAL = 12, + TPM_PROTECTED_ORDINAL_MASK = 0xff, + TPM_CMD_COUNT_BYTE = 2, + TPM_CMD_ORDINAL_BYTE = 6, +}; + +/* + * Array with one entry per ordinal defining the maximum amount + * of time the chip could take to return the result. The ordinal + * designation of short, medium or long is defined in a table in + * TCG Specification TPM Main Part 2 TPM Structures Section 17. The + * values of the SHORT, MEDIUM, and LONG durations are retrieved + * from the chip during initialization with a call to tpm_get_timeouts. + */ +static const u8 tpm_protected_ordinal_duration[TPM_MAX_PROTECTED_ORDINAL] = { + TPM_UNDEFINED, /* 0 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 5 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 10 */ + TPM_SHORT, +}; + +static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = { + TPM_UNDEFINED, /* 0 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 5 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 10 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_LONG, + TPM_LONG, + TPM_MEDIUM, /* 15 */ + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, + TPM_LONG, + TPM_SHORT, /* 20 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_SHORT, /* 25 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, /* 30 */ + TPM_LONG, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 35 */ + TPM_MEDIUM, + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 40 */ + TPM_LONG, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 45 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_LONG, + TPM_MEDIUM, /* 50 */ + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 55 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 60 */ + TPM_MEDIUM, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, /* 65 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 70 */ + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 75 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_LONG, /* 80 */ + TPM_UNDEFINED, + TPM_MEDIUM, + TPM_LONG, + TPM_SHORT, + TPM_UNDEFINED, /* 85 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 90 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, /* 95 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 100 */ + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 105 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 110 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 115 */ + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_LONG, /* 120 */ + TPM_LONG, + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_SHORT, + TPM_SHORT, /* 125 */ + TPM_SHORT, + TPM_LONG, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 130 */ + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_SHORT, + TPM_MEDIUM, + TPM_UNDEFINED, /* 135 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 140 */ + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 145 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 150 */ + TPM_MEDIUM, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, /* 155 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 160 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 165 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_LONG, /* 170 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 175 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 180 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, /* 185 */ + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 190 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 195 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 200 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, + TPM_SHORT, /* 205 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, /* 210 */ + TPM_UNDEFINED, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_UNDEFINED, /* 215 */ + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, + TPM_SHORT, /* 220 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, /* 225 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 230 */ + TPM_LONG, + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 235 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 240 */ + TPM_UNDEFINED, + TPM_MEDIUM, +}; + +#endif diff --git a/drivers/tpm/tpm_tis_i2c.c b/drivers/tpm/tpm_tis_i2c.c index 4b2ef94aa7d..645f7022154 100644 --- a/drivers/tpm/tpm_tis_i2c.c +++ b/drivers/tpm/tpm_tis_i2c.c @@ -25,12 +25,14 @@ #include #include #include +#include #include #include #include #include #include "tpm_tis_i2c.h" +#include "tpm_internal.h" DECLARE_GLOBAL_DATA_PTR; diff --git a/drivers/tpm/tpm_tis_i2c.h b/drivers/tpm/tpm_tis_i2c.h index ecdaf0cbd95..02cc2eba425 100644 --- a/drivers/tpm/tpm_tis_i2c.h +++ b/drivers/tpm/tpm_tis_i2c.h @@ -155,293 +155,10 @@ enum tis_status { #define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4)) #define TPM_DID_VID(l) (0x0006 | ((l) << 4)) -enum tpm_duration { - TPM_SHORT = 0, - TPM_MEDIUM = 1, - TPM_LONG = 2, - TPM_UNDEFINED, -}; - /* Extended error numbers from linux (see errno.h) */ #define ECANCELED 125 /* Operation Canceled */ /* Timer frequency. Corresponds to msec timer resolution */ #define HZ 1000 -#define TPM_MAX_ORDINAL 243 -#define TPM_MAX_PROTECTED_ORDINAL 12 -#define TPM_PROTECTED_ORDINAL_MASK 0xFF - -#define TPM_CMD_COUNT_BYTE 2 -#define TPM_CMD_ORDINAL_BYTE 6 - -/* - * Array with one entry per ordinal defining the maximum amount - * of time the chip could take to return the result. The ordinal - * designation of short, medium or long is defined in a table in - * TCG Specification TPM Main Part 2 TPM Structures Section 17. The - * values of the SHORT, MEDIUM, and LONG durations are retrieved - * from the chip during initialization with a call to tpm_get_timeouts. - */ -static const u8 tpm_protected_ordinal_duration[TPM_MAX_PROTECTED_ORDINAL] = { - TPM_UNDEFINED, /* 0 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, /* 5 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_SHORT, /* 10 */ - TPM_SHORT, -}; - -static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = { - TPM_UNDEFINED, /* 0 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, /* 5 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_SHORT, /* 10 */ - TPM_SHORT, - TPM_MEDIUM, - TPM_LONG, - TPM_LONG, - TPM_MEDIUM, /* 15 */ - TPM_SHORT, - TPM_SHORT, - TPM_MEDIUM, - TPM_LONG, - TPM_SHORT, /* 20 */ - TPM_SHORT, - TPM_MEDIUM, - TPM_MEDIUM, - TPM_MEDIUM, - TPM_SHORT, /* 25 */ - TPM_SHORT, - TPM_MEDIUM, - TPM_SHORT, - TPM_SHORT, - TPM_MEDIUM, /* 30 */ - TPM_LONG, - TPM_MEDIUM, - TPM_SHORT, - TPM_SHORT, - TPM_SHORT, /* 35 */ - TPM_MEDIUM, - TPM_MEDIUM, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_MEDIUM, /* 40 */ - TPM_LONG, - TPM_MEDIUM, - TPM_SHORT, - TPM_SHORT, - TPM_SHORT, /* 45 */ - TPM_SHORT, - TPM_SHORT, - TPM_SHORT, - TPM_LONG, - TPM_MEDIUM, /* 50 */ - TPM_MEDIUM, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, /* 55 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_MEDIUM, /* 60 */ - TPM_MEDIUM, - TPM_MEDIUM, - TPM_SHORT, - TPM_SHORT, - TPM_MEDIUM, /* 65 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_SHORT, /* 70 */ - TPM_SHORT, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, /* 75 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_LONG, /* 80 */ - TPM_UNDEFINED, - TPM_MEDIUM, - TPM_LONG, - TPM_SHORT, - TPM_UNDEFINED, /* 85 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_SHORT, /* 90 */ - TPM_SHORT, - TPM_SHORT, - TPM_SHORT, - TPM_SHORT, - TPM_UNDEFINED, /* 95 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_MEDIUM, /* 100 */ - TPM_SHORT, - TPM_SHORT, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, /* 105 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_SHORT, /* 110 */ - TPM_SHORT, - TPM_SHORT, - TPM_SHORT, - TPM_SHORT, - TPM_SHORT, /* 115 */ - TPM_SHORT, - TPM_SHORT, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_LONG, /* 120 */ - TPM_LONG, - TPM_MEDIUM, - TPM_UNDEFINED, - TPM_SHORT, - TPM_SHORT, /* 125 */ - TPM_SHORT, - TPM_LONG, - TPM_SHORT, - TPM_SHORT, - TPM_SHORT, /* 130 */ - TPM_MEDIUM, - TPM_UNDEFINED, - TPM_SHORT, - TPM_MEDIUM, - TPM_UNDEFINED, /* 135 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_SHORT, /* 140 */ - TPM_SHORT, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, /* 145 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_SHORT, /* 150 */ - TPM_MEDIUM, - TPM_MEDIUM, - TPM_SHORT, - TPM_SHORT, - TPM_UNDEFINED, /* 155 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_SHORT, /* 160 */ - TPM_SHORT, - TPM_SHORT, - TPM_SHORT, - TPM_UNDEFINED, - TPM_UNDEFINED, /* 165 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_LONG, /* 170 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, /* 175 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_MEDIUM, /* 180 */ - TPM_SHORT, - TPM_MEDIUM, - TPM_MEDIUM, - TPM_MEDIUM, - TPM_MEDIUM, /* 185 */ - TPM_SHORT, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, /* 190 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, /* 195 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_SHORT, /* 200 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_SHORT, - TPM_SHORT, /* 205 */ - TPM_SHORT, - TPM_SHORT, - TPM_SHORT, - TPM_SHORT, - TPM_MEDIUM, /* 210 */ - TPM_UNDEFINED, - TPM_MEDIUM, - TPM_MEDIUM, - TPM_MEDIUM, - TPM_UNDEFINED, /* 215 */ - TPM_MEDIUM, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_SHORT, - TPM_SHORT, /* 220 */ - TPM_SHORT, - TPM_SHORT, - TPM_SHORT, - TPM_SHORT, - TPM_UNDEFINED, /* 225 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_SHORT, /* 230 */ - TPM_LONG, - TPM_MEDIUM, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, /* 235 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_SHORT, /* 240 */ - TPM_UNDEFINED, - TPM_MEDIUM, -}; - #endif diff --git a/drivers/tpm/tpm_tis_lpc.c b/drivers/tpm/tpm_tis_lpc.c index d09f8cee05b..3109c504f3f 100644 --- a/drivers/tpm/tpm_tis_lpc.c +++ b/drivers/tpm/tpm_tis_lpc.c @@ -15,6 +15,7 @@ #include #include +#include #include #define PREFIX "lpc_tpm: " @@ -426,9 +427,6 @@ int tis_open(void) { u8 locality = 0; /* we use locality zero for everything. */ - if (tis_close()) - return TPM_DRIVER_ERR; - /* now request access to locality. */ tpm_write_word(TIS_ACCESS_REQUEST_USE, &lpc_tpm_dev[locality].access); diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index c744044bb8a..3eff895ab7a 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -54,6 +54,7 @@ enum uclass_id { UCLASS_SPI_GENERIC, /* Generic SPI flash target */ UCLASS_SYSCON, /* System configuration device */ UCLASS_THERMAL, /* Thermal sensor */ + UCLASS_TPM, /* Trusted Platform Module TIS interface */ UCLASS_USB, /* USB bus */ UCLASS_USB_DEV_GENERIC, /* USB generic device */ UCLASS_USB_HUB, /* USB hub */ diff --git a/include/tis.h b/include/tis.h index 40a1f867627..1985d9e60e6 100644 --- a/include/tis.h +++ b/include/tis.h @@ -7,6 +7,8 @@ #ifndef __TIS_H #define __TIS_H +#ifndef CONFIG_DM_TPM + #include /* Low-level interface to access TPM */ @@ -53,5 +55,6 @@ int tis_close(void); */ int tis_sendrecv(const uint8_t *sendbuf, size_t send_size, uint8_t *recvbuf, size_t *recv_len); +#endif #endif /* __TIS_H */ diff --git a/include/tpm.h b/include/tpm.h index 88aeba28e33..e4472d3ed4e 100644 --- a/include/tpm.h +++ b/include/tpm.h @@ -15,6 +15,17 @@ * Specification for definitions of TPM commands. */ +#define TPM_HEADER_SIZE 10 + +enum tpm_duration { + TPM_SHORT = 0, + TPM_MEDIUM = 1, + TPM_LONG = 2, + TPM_UNDEFINED, + + TPM_DURATION_COUNT, +}; + enum tpm_startup_type { TPM_ST_CLEAR = 0x0001, TPM_ST_STATE = 0x0002, @@ -152,6 +163,187 @@ enum tpm_return_code { TPM_DEFEND_LOCK_RUNNING = TPM_BASE + TPM_NON_FATAL + 3, }; +#ifdef CONFIG_DM_TPM + +/* Max buffer size supported by our tpm */ +#define TPM_DEV_BUFSIZE 1260 + +/** + * struct tpm_chip_priv - Information about a TPM, stored by the uclass + * + * These values must be set up by the device's probe() method before + * communcation is attempted. If the device has an xfer() method, this is + * not needed. There is no need to set up @buf. + * + * @duration_ms: Length of each duration type in milliseconds + * @retry_time_ms: Time to wait before retrying receive + */ +struct tpm_chip_priv { + uint duration_ms[TPM_DURATION_COUNT]; + uint retry_time_ms; + u8 buf[TPM_DEV_BUFSIZE + sizeof(u8)]; /* Max buffer size + addr */ +}; + +/** + * struct tpm_ops - low-level TPM operations + * + * These are designed to avoid loops and delays in the driver itself. These + * should be handled in the uclass. + * + * In gneral you should implement everything except xfer(). Where you need + * complete control of the transfer, then xfer() can be provided and will + * override the other methods. + * + * This interface is for low-level TPM access. It does not understand the + * concept of localities or the various TPM messages. That interface is + * defined in the functions later on in this file, but they all translate + * to bytes which are sent and received. + */ +struct tpm_ops { + /** + * open() - Request access to locality 0 for the caller + * + * After all commands have been completed the caller should call + * close(). + * + * @dev: Device to close + * @return 0 ok OK, -ve on error + */ + int (*open)(struct udevice *dev); + + /** + * close() - Close the current session + * + * Releasing the locked locality. Returns 0 on success, -ve 1 on + * failure (in case lock removal did not succeed). + * + * @dev: Device to close + * @return 0 ok OK, -ve on error + */ + int (*close)(struct udevice *dev); + + /** + * get_desc() - Get a text description of the TPM + * + * @dev: Device to check + * @buf: Buffer to put the string + * @size: Maximum size of buffer + * @return length of string, or -ENOSPC it no space + */ + int (*get_desc)(struct udevice *dev, char *buf, int size); + + /** + * send() - send data to the TPM + * + * @dev: Device to talk to + * @sendbuf: Buffer of the data to send + * @send_size: Size of the data to send + * + * Returns 0 on success or -ve on failure. + */ + int (*send)(struct udevice *dev, const uint8_t *sendbuf, + size_t send_size); + + /** + * recv() - receive a response from the TPM + * + * @dev: Device to talk to + * @recvbuf: Buffer to save the response to + * @max_size: Maximum number of bytes to receive + * + * Returns number of bytes received on success, -EAGAIN if the TPM + * response is not ready, -EINTR if cancelled, or other -ve value on + * failure. + */ + int (*recv)(struct udevice *dev, uint8_t *recvbuf, size_t max_size); + + /** + * cleanup() - clean up after an operation in progress + * + * This is called if receiving times out. The TPM may need to abort + * the current transaction if it did not complete, and make itself + * ready for another. + * + * @dev: Device to talk to + */ + int (*cleanup)(struct udevice *dev); + + /** + * xfer() - send data to the TPM and get response + * + * This method is optional. If it exists it is used in preference + * to send(), recv() and cleanup(). It should handle all aspects of + * TPM communication for a single transfer. + * + * @dev: Device to talk to + * @sendbuf: Buffer of the data to send + * @send_size: Size of the data to send + * @recvbuf: Buffer to save the response to + * @recv_size: Pointer to the size of the response buffer + * + * Returns 0 on success (and places the number of response bytes at + * recv_size) or -ve on failure. + */ + int (*xfer)(struct udevice *dev, const uint8_t *sendbuf, + size_t send_size, uint8_t *recvbuf, size_t *recv_size); +}; + +#define tpm_get_ops(dev) ((struct tpm_ops *)device_get_ops(dev)) + +/** + * tpm_open() - Request access to locality 0 for the caller + * + * After all commands have been completed the caller is supposed to + * call tpm_close(). + * + * Returns 0 on success, -ve on failure. + */ +int tpm_open(struct udevice *dev); + +/** + * tpm_close() - Close the current session + * + * Releasing the locked locality. Returns 0 on success, -ve 1 on + * failure (in case lock removal did not succeed). + */ +int tpm_close(struct udevice *dev); + +/** + * tpm_get_desc() - Get a text description of the TPM + * + * @dev: Device to check + * @buf: Buffer to put the string + * @size: Maximum size of buffer + * @return length of string, or -ENOSPC it no space + */ +int tpm_get_desc(struct udevice *dev, char *buf, int size); + +/** + * tpm_xfer() - send data to the TPM and get response + * + * This first uses the device's send() method to send the bytes. Then it calls + * recv() to get the reply. If recv() returns -EAGAIN then it will delay a + * short time and then call recv() again. + * + * Regardless of whether recv() completes successfully, it will then call + * cleanup() to finish the transaction. + * + * Note that the outgoing data is inspected to determine command type + * (ordinal) and a timeout is used for that command type. + * + * @sendbuf - buffer of the data to send + * @send_size size of the data to send + * @recvbuf - memory to save the response to + * @recv_len - pointer to the size of the response buffer + * + * Returns 0 on success (and places the number of response bytes at + * recv_len) or -ve on failure. + */ +int tpm_xfer(struct udevice *dev, const uint8_t *sendbuf, size_t send_size, + uint8_t *recvbuf, size_t *recv_size); + +#endif /* CONFIG_DM_TPM */ + /** * Initialize TPM device. It must be called before any TPM commands. * -- cgit v1.3.1 From c8a8c51039d83149a93fccb6e325bfdb8f63fa66 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 22 Aug 2015 18:31:32 -0600 Subject: dm: tpm: Convert the TPM command and library to driver model Add driver model support to the TPM command and the TPM library. Both support only a single TPM at present. Signed-off-by: Simon Glass Acked-by: Christophe Ricard Reviewed-by: Heiko Schocher --- common/cmd_tpm.c | 26 ++++++++++++++++++++++++++ include/tpm.h | 2 +- lib/tpm.c | 31 ++++++++++++++++++++++++++----- 3 files changed, 53 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/common/cmd_tpm.c b/common/cmd_tpm.c index 0294952e69a..bad20066ef8 100644 --- a/common/cmd_tpm.c +++ b/common/cmd_tpm.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -438,6 +439,21 @@ TPM_COMMAND_NO_ARG(tpm_force_clear) TPM_COMMAND_NO_ARG(tpm_physical_enable) TPM_COMMAND_NO_ARG(tpm_physical_disable) +#ifdef CONFIG_DM_TPM +static int get_tpm(struct udevice **devp) +{ + int rc; + + rc = uclass_first_device(UCLASS_TPM, devp); + if (rc) { + printf("Could not find TPM (ret=%d)\n", rc); + return CMD_RET_FAILURE; + } + + return 0; +} +#endif + static int do_tpm_raw_transfer(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { @@ -452,7 +468,17 @@ static int do_tpm_raw_transfer(cmd_tbl_t *cmdtp, int flag, return CMD_RET_FAILURE; } +#ifdef CONFIG_DM_TPM + struct udevice *dev; + + rc = get_tpm(&dev); + if (rc) + return rc; + + rc = tpm_xfer(dev, command, count, response, &response_length); +#else rc = tis_sendrecv(command, count, response, &response_length); +#endif free(command); if (!rc) { puts("tpm response:\n"); diff --git a/include/tpm.h b/include/tpm.h index e4472d3ed4e..445952b055d 100644 --- a/include/tpm.h +++ b/include/tpm.h @@ -349,7 +349,7 @@ int tpm_xfer(struct udevice *dev, const uint8_t *sendbuf, size_t send_size, * * @return 0 on success, non-0 on error. */ -uint32_t tpm_init(void); +int tpm_init(void); /** * Issue a TPM_Startup command. diff --git a/lib/tpm.c b/lib/tpm.c index d9789b022ab..19bf0b59905 100644 --- a/lib/tpm.c +++ b/lib/tpm.c @@ -6,10 +6,11 @@ */ #include -#include -#include +#include +#include #include #include +#include /* Internal error of TPM command library */ #define TPM_LIB_ERROR ((uint32_t)~0u) @@ -240,9 +241,20 @@ static uint32_t tpm_sendrecv_command(const void *command, response = response_buffer; response_length = sizeof(response_buffer); } +#ifdef CONFIG_DM_TPM + struct udevice *dev; + int ret; + + ret = uclass_first_device(UCLASS_TPM, &dev); + if (ret) + return ret; + err = tpm_xfer(dev, command, tpm_command_size(command), + response, &response_length); +#else err = tis_sendrecv(command, tpm_command_size(command), response, &response_length); - if (err) +#endif + if (err < 0) return TPM_LIB_ERROR; if (size_ptr) *size_ptr = response_length; @@ -250,15 +262,24 @@ static uint32_t tpm_sendrecv_command(const void *command, return tpm_return_code(response); } -uint32_t tpm_init(void) +int tpm_init(void) { - uint32_t err; + int err; +#ifdef CONFIG_DM_TPM + struct udevice *dev; + + err = uclass_first_device(UCLASS_TPM, &dev); + if (err) + return err; + return tpm_open(dev); +#else err = tis_init(); if (err) return err; return tis_open(); +#endif } uint32_t tpm_startup(enum tpm_startup_type mode) -- cgit v1.3.1 From b697e0ff5b8d92c9544960fd99dbfd9558c0ee6f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 22 Aug 2015 18:31:38 -0600 Subject: dm: tpm: Convert I2C driver to driver model Convert the tpm_tis_i2c driver to use driver model and update boards which use it. Signed-off-by: Simon Glass Acked-by: Christophe Ricard Reviewed-by: Heiko Schocher --- configs/peach-pi_defconfig | 1 + configs/peach-pit_defconfig | 1 + configs/snow_defconfig | 1 + configs/spring_defconfig | 1 + drivers/tpm/tpm_tis_i2c.c | 529 +++++++++++++++++--------------------------- drivers/tpm/tpm_tis_i2c.h | 20 +- include/fdtdec.h | 2 - lib/fdtdec.c | 2 - 8 files changed, 207 insertions(+), 350 deletions(-) (limited to 'include') diff --git a/configs/peach-pi_defconfig b/configs/peach-pi_defconfig index 2be74fd1fde..6416f16986c 100644 --- a/configs/peach-pi_defconfig +++ b/configs/peach-pi_defconfig @@ -13,6 +13,7 @@ CONFIG_CMD_CROS_EC=y CONFIG_CROS_EC=y CONFIG_CROS_EC_SPI=y CONFIG_CROS_EC_KEYB=y +CONFIG_DM_TPM=y CONFIG_TPM_TIS_I2C=y CONFIG_DM_I2C=y CONFIG_DM_I2C_COMPAT=y diff --git a/configs/peach-pit_defconfig b/configs/peach-pit_defconfig index 875ddd19419..d0646ab0f56 100644 --- a/configs/peach-pit_defconfig +++ b/configs/peach-pit_defconfig @@ -13,6 +13,7 @@ CONFIG_CMD_CROS_EC=y CONFIG_CROS_EC=y CONFIG_CROS_EC_SPI=y CONFIG_CROS_EC_KEYB=y +CONFIG_DM_TPM=y CONFIG_TPM_TIS_I2C=y CONFIG_DM_I2C=y CONFIG_DM_I2C_COMPAT=y diff --git a/configs/snow_defconfig b/configs/snow_defconfig index f59aa3ff5e4..7624ad2c244 100644 --- a/configs/snow_defconfig +++ b/configs/snow_defconfig @@ -18,6 +18,7 @@ CONFIG_DEBUG_UART=y CONFIG_DEBUG_UART_S5P=y CONFIG_DEBUG_UART_BASE=0x12c30000 CONFIG_DEBUG_UART_CLOCK=100000000 +CONFIG_DM_TPM=y CONFIG_TPM_TIS_I2C=y CONFIG_DM_I2C=y CONFIG_DM_I2C_COMPAT=y diff --git a/configs/spring_defconfig b/configs/spring_defconfig index 6144f03b311..3b109fadcd9 100644 --- a/configs/spring_defconfig +++ b/configs/spring_defconfig @@ -18,6 +18,7 @@ CONFIG_DEBUG_UART=y CONFIG_DEBUG_UART_S5P=y CONFIG_DEBUG_UART_BASE=0x12c30000 CONFIG_DEBUG_UART_CLOCK=100000000 +CONFIG_DM_TPM=y CONFIG_TPM_TIS_I2C=y CONFIG_DM_I2C=y CONFIG_DM_I2C_COMPAT=y diff --git a/drivers/tpm/tpm_tis_i2c.c b/drivers/tpm/tpm_tis_i2c.c index 645f7022154..9afe46c1e90 100644 --- a/drivers/tpm/tpm_tis_i2c.c +++ b/drivers/tpm/tpm_tis_i2c.c @@ -23,11 +23,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include @@ -42,8 +42,6 @@ static const char * const chip_name[] = { [UNKNOWN] = "unknown/fallback to slb9635", }; -static struct tpm_chip g_chip; - /* * tpm_tis_i2c_read() - read from TPM register * @addr: register address to read from @@ -58,22 +56,24 @@ static struct tpm_chip g_chip; * * Return -EIO on error, 0 on success. */ -static int tpm_tis_i2c_read(u8 addr, u8 *buffer, size_t len) +static int tpm_tis_i2c_read(struct udevice *dev, u8 addr, u8 *buffer, + size_t len) { + struct tpm_chip *chip = dev_get_priv(dev); int rc; int count; uint32_t addrbuf = addr; - if ((g_chip.chip_type == SLB9635) || (g_chip.chip_type == UNKNOWN)) { + if ((chip->chip_type == SLB9635) || (chip->chip_type == UNKNOWN)) { /* slb9635 protocol should work in both cases */ for (count = 0; count < MAX_COUNT; count++) { - rc = dm_i2c_write(g_chip.dev, 0, (uchar *)&addrbuf, 1); + rc = dm_i2c_write(dev, 0, (uchar *)&addrbuf, 1); if (rc == 0) break; /* Success, break to skip sleep */ udelay(SLEEP_DURATION_US); } if (rc) - return -rc; + return rc; /* After the TPM has successfully received the register address * it needs some time, thus we're sleeping here again, before @@ -81,7 +81,7 @@ static int tpm_tis_i2c_read(u8 addr, u8 *buffer, size_t len) */ for (count = 0; count < MAX_COUNT; count++) { udelay(SLEEP_DURATION_US); - rc = dm_i2c_read(g_chip.dev, 0, buffer, len); + rc = dm_i2c_read(dev, 0, buffer, len); if (rc == 0) break; /* success, break to skip sleep */ } @@ -94,7 +94,7 @@ static int tpm_tis_i2c_read(u8 addr, u8 *buffer, size_t len) * be safe on the safe side. */ for (count = 0; count < MAX_COUNT; count++) { - rc = dm_i2c_read(g_chip.dev, addr, buffer, len); + rc = dm_i2c_read(dev, addr, buffer, len); if (rc == 0) break; /* break here to skip sleep */ udelay(SLEEP_DURATION_US); @@ -104,19 +104,31 @@ static int tpm_tis_i2c_read(u8 addr, u8 *buffer, size_t len) /* Take care of 'guard time' */ udelay(SLEEP_DURATION_US); if (rc) - return -rc; + return rc; return 0; } -static int tpm_tis_i2c_write_generic(u8 addr, u8 *buffer, size_t len, +static int tpm_tis_i2c_write_generic(struct udevice *dev, u8 addr, + const u8 *buffer, size_t len, unsigned int sleep_time_us, u8 max_count) { + struct tpm_chip_priv *priv = dev_get_uclass_priv(dev); + struct tpm_chip *chip = dev_get_priv(dev); int rc = 0; int count; + if (chip->chip_type == SLB9635) { + /* Prepare send buffer to include the address */ + priv->buf[0] = addr; + memcpy(&(priv->buf[1]), buffer, len); + buffer = priv->buf; + len++; + addr = 0; + } + for (count = 0; count < max_count; count++) { - rc = dm_i2c_write(g_chip.dev, addr, buffer, len); + rc = dm_i2c_write(dev, addr, buffer, len); if (rc == 0) break; /* Success, break to skip sleep */ udelay(sleep_time_us); @@ -125,7 +137,7 @@ static int tpm_tis_i2c_write_generic(u8 addr, u8 *buffer, size_t len, /* take care of 'guard time' */ udelay(sleep_time_us); if (rc) - return -rc; + return rc; return 0; } @@ -146,30 +158,33 @@ static int tpm_tis_i2c_write_generic(u8 addr, u8 *buffer, size_t len, * * Return -EIO on error, 0 on success */ -static int tpm_tis_i2c_write(u8 addr, u8 *buffer, size_t len) +static int tpm_tis_i2c_write(struct udevice *dev, u8 addr, const u8 *buffer, + size_t len) { - return tpm_tis_i2c_write_generic(addr, buffer, len, SLEEP_DURATION_US, - MAX_COUNT); + return tpm_tis_i2c_write_generic(dev, addr, buffer, len, + SLEEP_DURATION_US, MAX_COUNT); } /* * This function is needed especially for the cleanup situation after * sending TPM_READY */ -static int tpm_tis_i2c_write_long(u8 addr, u8 *buffer, size_t len) +static int tpm_tis_i2c_write_long(struct udevice *dev, u8 addr, u8 *buffer, + size_t len) { - return tpm_tis_i2c_write_generic(addr, buffer, len, + return tpm_tis_i2c_write_generic(dev, addr, buffer, len, SLEEP_DURATION_LONG_US, MAX_COUNT_LONG); } -static int tpm_tis_i2c_check_locality(struct tpm_chip *chip, int loc) +static int tpm_tis_i2c_check_locality(struct udevice *dev, int loc) { const u8 mask = TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID; + struct tpm_chip *chip = dev_get_priv(dev); u8 buf; int rc; - rc = tpm_tis_i2c_read(TPM_ACCESS(loc), &buf, 1); + rc = tpm_tis_i2c_read(dev, TPM_ACCESS(loc), &buf, 1); if (rc < 0) return rc; @@ -178,75 +193,96 @@ static int tpm_tis_i2c_check_locality(struct tpm_chip *chip, int loc) return loc; } - return -1; + return -ENOENT; } -static void tpm_tis_i2c_release_locality(struct tpm_chip *chip, int loc, +static void tpm_tis_i2c_release_locality(struct udevice *dev, int loc, int force) { const u8 mask = TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID; u8 buf; - if (tpm_tis_i2c_read(TPM_ACCESS(loc), &buf, 1) < 0) + if (tpm_tis_i2c_read(dev, TPM_ACCESS(loc), &buf, 1) < 0) return; if (force || (buf & mask) == mask) { buf = TPM_ACCESS_ACTIVE_LOCALITY; - tpm_tis_i2c_write(TPM_ACCESS(loc), &buf, 1); + tpm_tis_i2c_write(dev, TPM_ACCESS(loc), &buf, 1); } } -static int tpm_tis_i2c_request_locality(struct tpm_chip *chip, int loc) +static int tpm_tis_i2c_request_locality(struct udevice *dev, int loc) { + struct tpm_chip *chip = dev_get_priv(dev); unsigned long start, stop; u8 buf = TPM_ACCESS_REQUEST_USE; int rc; - if (tpm_tis_i2c_check_locality(chip, loc) >= 0) + rc = tpm_tis_i2c_check_locality(dev, loc); + if (rc >= 0) { + debug("%s: Already have locality\n", __func__); return loc; /* We already have the locality */ + } else if (rc != -ENOENT) { + debug("%s: Failed to get locality: %d\n", __func__, rc); + return rc; + } - rc = tpm_tis_i2c_write(TPM_ACCESS(loc), &buf, 1); - if (rc) + rc = tpm_tis_i2c_write(dev, TPM_ACCESS(loc), &buf, 1); + if (rc) { + debug("%s: Failed to write to TPM: %d\n", __func__, rc); return rc; + } /* Wait for burstcount */ start = get_timer(0); stop = chip->timeout_a; do { - if (tpm_tis_i2c_check_locality(chip, loc) >= 0) + rc = tpm_tis_i2c_check_locality(dev, loc); + if (rc >= 0) { + debug("%s: Have locality\n", __func__); return loc; + } else if (rc != -ENOENT) { + debug("%s: Failed to get locality: %d\n", __func__, rc); + return rc; + } mdelay(TPM_TIMEOUT_MS); } while (get_timer(start) < stop); + debug("%s: Timeout getting locality: %d\n", __func__, rc); - return -1; + return rc; } -static u8 tpm_tis_i2c_status(struct tpm_chip *chip) +static u8 tpm_tis_i2c_status(struct udevice *dev) { + struct tpm_chip *chip = dev_get_priv(dev); /* NOTE: Since i2c read may fail, return 0 in this case --> time-out */ u8 buf; - if (tpm_tis_i2c_read(TPM_STS(chip->locality), &buf, 1) < 0) + if (tpm_tis_i2c_read(dev, TPM_STS(chip->locality), &buf, 1) < 0) return 0; else return buf; } -static void tpm_tis_i2c_ready(struct tpm_chip *chip) +static int tpm_tis_i2c_ready(struct udevice *dev) { + struct tpm_chip *chip = dev_get_priv(dev); int rc; /* This causes the current command to be aborted */ u8 buf = TPM_STS_COMMAND_READY; debug("%s\n", __func__); - rc = tpm_tis_i2c_write_long(TPM_STS(chip->locality), &buf, 1); + rc = tpm_tis_i2c_write_long(dev, TPM_STS(chip->locality), &buf, 1); if (rc) debug("%s: rc=%d\n", __func__, rc); + + return rc; } -static ssize_t tpm_tis_i2c_get_burstcount(struct tpm_chip *chip) +static ssize_t tpm_tis_i2c_get_burstcount(struct udevice *dev) { + struct tpm_chip *chip = dev_get_priv(dev); unsigned long start, stop; ssize_t burstcnt; u8 addr, buf[3]; @@ -258,7 +294,7 @@ static ssize_t tpm_tis_i2c_get_burstcount(struct tpm_chip *chip) do { /* Note: STS is little endian */ addr = TPM_STS(chip->locality) + 1; - if (tpm_tis_i2c_read(addr, buf, 3) < 0) + if (tpm_tis_i2c_read(dev, addr, buf, 3) < 0) burstcnt = 0; else burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0]; @@ -271,13 +307,13 @@ static ssize_t tpm_tis_i2c_get_burstcount(struct tpm_chip *chip) return -EBUSY; } -static int tpm_tis_i2c_wait_for_stat(struct tpm_chip *chip, u8 mask, +static int tpm_tis_i2c_wait_for_stat(struct udevice *dev, u8 mask, unsigned long timeout, int *status) { unsigned long start, stop; /* Check current status */ - *status = tpm_tis_i2c_status(chip); + *status = tpm_tis_i2c_status(dev); if ((*status & mask) == mask) return 0; @@ -285,22 +321,23 @@ static int tpm_tis_i2c_wait_for_stat(struct tpm_chip *chip, u8 mask, stop = timeout; do { mdelay(TPM_TIMEOUT_MS); - *status = tpm_tis_i2c_status(chip); + *status = tpm_tis_i2c_status(dev); if ((*status & mask) == mask) return 0; } while (get_timer(start) < stop); - return -ETIME; + return -ETIMEDOUT; } -static int tpm_tis_i2c_recv_data(struct tpm_chip *chip, u8 *buf, size_t count) +static int tpm_tis_i2c_recv_data(struct udevice *dev, u8 *buf, size_t count) { + struct tpm_chip *chip = dev_get_priv(dev); size_t size = 0; ssize_t burstcnt; int rc; while (size < count) { - burstcnt = tpm_tis_i2c_get_burstcount(chip); + burstcnt = tpm_tis_i2c_get_burstcount(dev); /* burstcount < 0 -> tpm is busy */ if (burstcnt < 0) @@ -310,8 +347,8 @@ static int tpm_tis_i2c_recv_data(struct tpm_chip *chip, u8 *buf, size_t count) if (burstcnt > (count - size)) burstcnt = count - size; - rc = tpm_tis_i2c_read(TPM_DATA_FIFO(chip->locality), - &(buf[size]), burstcnt); + rc = tpm_tis_i2c_read(dev, TPM_DATA_FIFO(chip->locality), + &(buf[size]), burstcnt); if (rc == 0) size += burstcnt; } @@ -319,61 +356,58 @@ static int tpm_tis_i2c_recv_data(struct tpm_chip *chip, u8 *buf, size_t count) return size; } -static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count) +static int tpm_tis_i2c_recv(struct udevice *dev, u8 *buf, size_t count) { + struct tpm_chip *chip = dev_get_priv(dev); int size = 0; int expected, status; + int rc; - if (count < TPM_HEADER_SIZE) { - size = -EIO; - goto out; - } + status = tpm_tis_i2c_status(dev); + if (status == TPM_STS_COMMAND_READY) + return -EINTR; + if ((status & (TPM_STS_DATA_AVAIL | TPM_STS_VALID)) != + (TPM_STS_DATA_AVAIL | TPM_STS_VALID)) + return -EAGAIN; + + debug("...got it;\n"); /* Read first 10 bytes, including tag, paramsize, and result */ - size = tpm_tis_i2c_recv_data(chip, buf, TPM_HEADER_SIZE); + size = tpm_tis_i2c_recv_data(dev, buf, TPM_HEADER_SIZE); if (size < TPM_HEADER_SIZE) { - error("Unable to read header\n"); - goto out; + debug("Unable to read header\n"); + return size < 0 ? size : -EIO; } expected = get_unaligned_be32(buf + TPM_RSP_SIZE_BYTE); if ((size_t)expected > count) { - error("Error size=%x, expected=%x, count=%x\n", size, expected, + debug("Error size=%x, expected=%x, count=%x\n", size, expected, count); - size = -EIO; - goto out; + return -ENOSPC; } - size += tpm_tis_i2c_recv_data(chip, &buf[TPM_HEADER_SIZE], + size += tpm_tis_i2c_recv_data(dev, &buf[TPM_HEADER_SIZE], expected - TPM_HEADER_SIZE); if (size < expected) { - error("Unable to read remainder of result\n"); - size = -ETIME; - goto out; + debug("Unable to read remainder of result\n"); + return -ETIMEDOUT; } - tpm_tis_i2c_wait_for_stat(chip, TPM_STS_VALID, chip->timeout_c, - &status); + rc = tpm_tis_i2c_wait_for_stat(dev, TPM_STS_VALID, chip->timeout_c, + &status); + if (rc) + return rc; if (status & TPM_STS_DATA_AVAIL) { /* Retry? */ - error("Error left over data\n"); - size = -EIO; - goto out; + debug("Error left over data\n"); + return -EIO; } -out: - tpm_tis_i2c_ready(chip); - /* - * The TPM needs some time to clean up here, - * so we sleep rather than keeping the bus busy - */ - mdelay(2); - tpm_tis_i2c_release_locality(chip, chip->locality, 0); - return size; } -static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int tpm_tis_i2c_send(struct udevice *dev, const u8 *buf, size_t len) { + struct tpm_chip *chip = dev_get_priv(dev); int rc, status; size_t burstcnt; size_t count = 0; @@ -384,20 +418,21 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) if (len > TPM_DEV_BUFSIZE) return -E2BIG; /* Command is too long for our tpm, sorry */ - if (tpm_tis_i2c_request_locality(chip, 0) < 0) + if (tpm_tis_i2c_request_locality(dev, 0) < 0) return -EBUSY; - status = tpm_tis_i2c_status(chip); + status = tpm_tis_i2c_status(dev); if ((status & TPM_STS_COMMAND_READY) == 0) { - tpm_tis_i2c_ready(chip); - if (tpm_tis_i2c_wait_for_stat(chip, TPM_STS_COMMAND_READY, - chip->timeout_b, &status) < 0) { - rc = -ETIME; - goto out_err; - } + rc = tpm_tis_i2c_ready(dev); + if (rc) + return rc; + rc = tpm_tis_i2c_wait_for_stat(dev, TPM_STS_COMMAND_READY, + chip->timeout_b, &status); + if (rc) + return rc; } - burstcnt = tpm_tis_i2c_get_burstcount(chip); + burstcnt = tpm_tis_i2c_get_burstcount(dev); /* burstcount < 0 -> tpm is busy */ if (burstcnt < 0) @@ -409,98 +444,79 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) burstcnt = len - count; #ifdef CONFIG_TPM_TIS_I2C_BURST_LIMITATION - if (retry && burstcnt > CONFIG_TPM_TIS_I2C_BURST_LIMITATION) - burstcnt = CONFIG_TPM_TIS_I2C_BURST_LIMITATION; + if (retry && burstcnt > CONFIG_TPM_TIS_I2C_BURST_LIMITATION_LEN) + burstcnt = CONFIG_TPM_TIS_I2C_BURST_LIMITATION_LEN; #endif /* CONFIG_TPM_TIS_I2C_BURST_LIMITATION */ - rc = tpm_tis_i2c_write(TPM_DATA_FIFO(chip->locality), - &(buf[count]), burstcnt); + rc = tpm_tis_i2c_write(dev, TPM_DATA_FIFO(chip->locality), + &(buf[count]), burstcnt); if (rc == 0) count += burstcnt; else { debug("%s: error\n", __func__); - if (retry++ > 10) { - rc = -EIO; - goto out_err; - } - rc = tpm_tis_i2c_wait_for_stat(chip, TPM_STS_VALID, + if (retry++ > 10) + return -EIO; + rc = tpm_tis_i2c_wait_for_stat(dev, TPM_STS_VALID, chip->timeout_c, &status); if (rc) - goto out_err; + return rc; - if ((status & TPM_STS_DATA_EXPECT) == 0) { - rc = -EIO; - goto out_err; - } + if ((status & TPM_STS_DATA_EXPECT) == 0) + return -EIO; } } /* Go and do it */ - tpm_tis_i2c_write(TPM_STS(chip->locality), &sts, 1); - debug("done\n"); + rc = tpm_tis_i2c_write(dev, TPM_STS(chip->locality), &sts, 1); + if (rc < 0) + return rc; + debug("%s: done, rc=%d\n", __func__, rc); return len; +} + +static int tpm_tis_i2c_cleanup(struct udevice *dev) +{ + struct tpm_chip *chip = dev_get_priv(dev); -out_err: - debug("%s: out_err\n", __func__); - tpm_tis_i2c_ready(chip); + tpm_tis_i2c_ready(dev); /* * The TPM needs some time to clean up here, * so we sleep rather than keeping the bus busy */ mdelay(2); - tpm_tis_i2c_release_locality(chip, chip->locality, 0); + tpm_tis_i2c_release_locality(dev, chip->locality, 0); - return rc; -} - -static enum i2c_chip_type tpm_tis_i2c_chip_type(void) -{ -#if CONFIG_IS_ENABLED(OF_CONTROL) - const void *blob = gd->fdt_blob; - - if (fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9645_TPM) >= 0) - return SLB9645; - - if (fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9635_TPM) >= 0) - return SLB9635; -#endif - return UNKNOWN; + return 0; } static int tpm_tis_i2c_init(struct udevice *dev) { - struct tpm_chip *chip = &g_chip; + struct tpm_chip *chip = dev_get_priv(dev); u32 vendor; u32 expected_did_vid; + int rc; - g_chip.dev = dev; - g_chip.chip_type = tpm_tis_i2c_chip_type(); chip->is_open = 1; - /* Disable interrupts (not supported) */ - chip->irq = 0; - /* Default timeouts - these could move to the device tree */ chip->timeout_a = TIS_SHORT_TIMEOUT_MS; chip->timeout_b = TIS_LONG_TIMEOUT_MS; chip->timeout_c = TIS_SHORT_TIMEOUT_MS; chip->timeout_d = TIS_SHORT_TIMEOUT_MS; - chip->req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID; - chip->req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID; - chip->req_canceled = TPM_STS_COMMAND_READY; - if (tpm_tis_i2c_request_locality(chip, 0) < 0) - return -ENODEV; + rc = tpm_tis_i2c_request_locality(dev, 0); + if (rc < 0) + return rc; /* Read four bytes from DID_VID register */ - if (tpm_tis_i2c_read(TPM_DID_VID(0), (uchar *)&vendor, 4) < 0) { - tpm_tis_i2c_release_locality(chip, 0, 1); + if (tpm_tis_i2c_read(dev, TPM_DID_VID(0), (uchar *)&vendor, 4) < 0) { + tpm_tis_i2c_release_locality(dev, 0, 1); return -EIO; } - if (g_chip.chip_type == SLB9635) { + if (chip->chip_type == SLB9635) { vendor = be32_to_cpu(vendor); expected_did_vid = TPM_TIS_I2C_DID_VID_9635; } else { @@ -508,13 +524,14 @@ static int tpm_tis_i2c_init(struct udevice *dev) expected_did_vid = TPM_TIS_I2C_DID_VID_9645; } - if (g_chip.chip_type != UNKNOWN && vendor != expected_did_vid) { + if (chip->chip_type != UNKNOWN && vendor != expected_did_vid) { error("Vendor id did not match! ID was %08x\n", vendor); return -ENODEV; } + chip->vend_dev = vendor; debug("1.2 TPM (chip type %s device-id 0x%X)\n", - chip_name[g_chip.chip_type], vendor >> 16); + chip_name[chip->chip_type], vendor >> 16); /* * A timeout query to TPM can be placed here. @@ -524,225 +541,83 @@ static int tpm_tis_i2c_init(struct udevice *dev) return 0; } -/* Returns max number of milliseconds to wait */ -static unsigned long tpm_tis_i2c_calc_ordinal_duration(struct tpm_chip *chip, - u32 ordinal) -{ - int duration_idx = TPM_UNDEFINED; - int duration = 0; - - if (ordinal < TPM_MAX_ORDINAL) { - duration_idx = tpm_ordinal_duration[ordinal]; - } else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) < - TPM_MAX_PROTECTED_ORDINAL) { - duration_idx = tpm_protected_ordinal_duration[ - ordinal & TPM_PROTECTED_ORDINAL_MASK]; - } - - if (duration_idx != TPM_UNDEFINED) - duration = chip->duration[duration_idx]; - - if (duration <= 0) - return 2 * 60 * HZ; /* Two minutes timeout */ - else - return duration; -} - -static ssize_t tpm_tis_i2c_transmit(const unsigned char *buf, size_t bufsiz) +static int tpm_tis_i2c_open(struct udevice *dev) { + struct tpm_chip *chip = dev_get_priv(dev); int rc; - u32 count, ordinal; - unsigned long start, stop; - - struct tpm_chip *chip = &g_chip; - - /* switch endianess: big->little */ - count = get_unaligned_be32(buf + TPM_CMD_COUNT_BYTE); - ordinal = get_unaligned_be32(buf + TPM_CMD_ORDINAL_BYTE); - if (count == 0) { - error("no data\n"); - return -ENODATA; - } - if (count > bufsiz) { - error("invalid count value %x %zx\n", count, bufsiz); - return -E2BIG; - } - - debug("Calling send\n"); - rc = tpm_tis_i2c_send(chip, (u8 *)buf, count); - debug(" ... done calling send\n"); - if (rc < 0) { - error("tpm_transmit: tpm_send: error %d\n", rc); - goto out; - } - - if (chip->irq) - goto out_recv; - - start = get_timer(0); - stop = tpm_tis_i2c_calc_ordinal_duration(chip, ordinal); - do { - debug("waiting for status... %ld %ld\n", start, stop); - u8 status = tpm_tis_i2c_status(chip); - if ((status & chip->req_complete_mask) == - chip->req_complete_val) { - debug("...got it;\n"); - goto out_recv; - } - - if (status == chip->req_canceled) { - error("Operation Canceled\n"); - rc = -ECANCELED; - goto out; - } - mdelay(TPM_TIMEOUT_MS); - } while (get_timer(start) < stop); - - tpm_tis_i2c_ready(chip); - error("Operation Timed out\n"); - rc = -ETIME; - goto out; - -out_recv: - debug("out_recv: reading response...\n"); - rc = tpm_tis_i2c_recv(chip, (u8 *)buf, TPM_BUFSIZE); + debug("%s: start\n", __func__); + if (chip->is_open) + return -EBUSY; + rc = tpm_tis_i2c_init(dev); if (rc < 0) - error("tpm_transmit: tpm_recv: error %d\n", rc); + chip->is_open = 0; -out: return rc; } -/** - * Decode TPM configuration. - * - * @param dev Returns a configuration of TPM device - * @return 0 if ok, -1 on error - */ -static int tpm_tis_i2c_decode_config(struct tpm_chip *chip) +static int tpm_tis_i2c_close(struct udevice *dev) { - const void *blob = gd->fdt_blob; - struct udevice *bus; - int chip_addr; - int parent; - int node; - int ret; - - node = fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9635_TPM); - if (node < 0) { - node = fdtdec_next_compatible(blob, 0, - COMPAT_INFINEON_SLB9645_TPM); - } - if (node < 0) { - debug("%s: Node not found\n", __func__); - return -1; - } - parent = fdt_parent_offset(blob, node); - if (parent < 0) { - debug("%s: Cannot find node parent\n", __func__); - return -1; - } + struct tpm_chip *chip = dev_get_priv(dev); - /* - * TODO(sjg@chromium.org): Remove this when driver model supports - * TPMs - */ - ret = uclass_get_device_by_of_offset(UCLASS_I2C, parent, &bus); - if (ret) { - debug("Cannot find bus for node '%s: ret=%d'\n", - fdt_get_name(blob, parent, NULL), ret); - return ret; - } - - chip_addr = fdtdec_get_int(blob, node, "reg", -1); - if (chip_addr == -1) { - debug("Cannot find reg property for node '%s: ret=%d'\n", - fdt_get_name(blob, node, NULL), ret); - return ret; - } - /* - * TODO(sjg@chromium.org): Older TPMs will need to use the older method - * in tpm_tis_i2c_read() so the offset length needs to be 0 here. - */ - ret = i2c_get_chip(bus, chip_addr, 1, &chip->dev); - if (ret) { - debug("Cannot find device for node '%s: ret=%d'\n", - fdt_get_name(blob, node, NULL), ret); - return ret; + if (chip->is_open) { + tpm_tis_i2c_release_locality(dev, chip->locality, 1); + chip->is_open = 0; + chip->vend_dev = 0; } return 0; } -int tis_init(void) +static int tpm_tis_get_desc(struct udevice *dev, char *buf, int size) { - if (g_chip.inited) - return 0; - - if (tpm_tis_i2c_decode_config(&g_chip)) - return -1; - - debug("%s: done\n", __func__); + struct tpm_chip *chip = dev_get_priv(dev); - g_chip.inited = 1; + if (size < 50) + return -ENOSPC; - return 0; + return snprintf(buf, size, "1.2 TPM (%s, chip type %s device-id 0x%x)", + chip->is_open ? "open" : "closed", + chip_name[chip->chip_type], + chip->vend_dev >> 16); } -int tis_open(void) +static int tpm_tis_i2c_probe(struct udevice *dev) { - int rc; - - if (!g_chip.inited) - return -1; - - debug("%s: start\n", __func__); - if (g_chip.is_open) - return -EBUSY; - rc = tpm_tis_i2c_init(g_chip.dev); - if (rc < 0) - g_chip.is_open = 0; - - return rc; -} + struct tpm_chip_priv *uc_priv = dev_get_uclass_priv(dev); + struct tpm_chip *chip = dev_get_priv(dev); -int tis_close(void) -{ - if (!g_chip.inited) - return -1; + chip->chip_type = dev_get_driver_data(dev); - if (g_chip.is_open) { - tpm_tis_i2c_release_locality(&g_chip, g_chip.locality, 1); - g_chip.is_open = 0; - } + /* TODO: These need to be checked and tuned */ + uc_priv->duration_ms[TPM_SHORT] = TIS_SHORT_TIMEOUT_MS; + uc_priv->duration_ms[TPM_MEDIUM] = TIS_LONG_TIMEOUT_MS; + uc_priv->duration_ms[TPM_LONG] = TIS_LONG_TIMEOUT_MS; + uc_priv->retry_time_ms = TPM_TIMEOUT_MS; return 0; } -int tis_sendrecv(const uint8_t *sendbuf, size_t sbuf_size, - uint8_t *recvbuf, size_t *rbuf_len) -{ - int len; - uint8_t buf[4096]; - - if (!g_chip.inited) - return -1; - - if (sizeof(buf) < sbuf_size) - return -1; - - memcpy(buf, sendbuf, sbuf_size); - - len = tpm_tis_i2c_transmit(buf, sbuf_size); - - if (len < 10) { - *rbuf_len = 0; - return -1; - } +static const struct tpm_ops tpm_tis_i2c_ops = { + .open = tpm_tis_i2c_open, + .close = tpm_tis_i2c_close, + .get_desc = tpm_tis_get_desc, + .send = tpm_tis_i2c_send, + .recv = tpm_tis_i2c_recv, + .cleanup = tpm_tis_i2c_cleanup, +}; - memcpy(recvbuf, buf, len); - *rbuf_len = len; +static const struct udevice_id tpm_tis_i2c_ids[] = { + { .compatible = "infineon,slb9635tt", .data = SLB9635 }, + { .compatible = "infineon,slb9645tt", .data = SLB9645 }, + { } +}; - return 0; -} +U_BOOT_DRIVER(tpm_tis_i2c) = { + .name = "tpm_tis_i2c", + .id = UCLASS_TPM, + .of_match = tpm_tis_i2c_ids, + .ops = &tpm_tis_i2c_ops, + .probe = tpm_tis_i2c_probe, + .priv_auto_alloc_size = sizeof(struct tpm_chip), +}; diff --git a/drivers/tpm/tpm_tis_i2c.h b/drivers/tpm/tpm_tis_i2c.h index 02cc2eba425..3b510d101e7 100644 --- a/drivers/tpm/tpm_tis_i2c.h +++ b/drivers/tpm/tpm_tis_i2c.h @@ -37,9 +37,6 @@ enum tpm_timeout { #define TPM_RSP_SIZE_BYTE 2 #define TPM_RSP_RC_BYTE 6 -/* Max buffer size supported by our tpm */ -#define TPM_DEV_BUFSIZE 1260 - enum i2c_chip_type { SLB9635, SLB9645, @@ -47,17 +44,10 @@ enum i2c_chip_type { }; struct tpm_chip { - bool inited; int is_open; - u8 req_complete_mask; - u8 req_complete_val; - u8 req_canceled; - int irq; int locality; + u32 vend_dev; unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* msec */ - unsigned long duration[3]; /* msec */ - struct udevice *dev; - u8 buf[TPM_DEV_BUFSIZE + sizeof(u8)]; /* Max buffer size + addr */ enum i2c_chip_type chip_type; }; @@ -129,8 +119,6 @@ struct tpm_cmd_t { */ #define MAX_COUNT_LONG 50 -#define TPM_HEADER_SIZE 10 - enum tis_access { TPM_ACCESS_VALID = 0x80, TPM_ACCESS_ACTIVE_LOCALITY = 0x20, @@ -155,10 +143,4 @@ enum tis_status { #define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4)) #define TPM_DID_VID(l) (0x0006 | ((l) << 4)) -/* Extended error numbers from linux (see errno.h) */ -#define ECANCELED 125 /* Operation Canceled */ - -/* Timer frequency. Corresponds to msec timer resolution */ -#define HZ 1000 - #endif diff --git a/include/fdtdec.h b/include/fdtdec.h index 0cb6fa00750..3e237312a72 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -154,8 +154,6 @@ enum fdt_compat_id { COMPAT_MAXIM_MAX77686_PMIC, /* MAX77686 PMIC */ COMPAT_GENERIC_SPI_FLASH, /* Generic SPI Flash chip */ COMPAT_MAXIM_98095_CODEC, /* MAX98095 Codec */ - COMPAT_INFINEON_SLB9635_TPM, /* Infineon SLB9635 TPM */ - COMPAT_INFINEON_SLB9645_TPM, /* Infineon SLB9645 TPM */ COMPAT_SAMSUNG_EXYNOS5_I2C, /* Exynos5 High Speed I2C Controller */ COMPAT_SANDBOX_LCD_SDL, /* Sandbox LCD emulation with SDL */ COMPAT_SAMSUNG_EXYNOS_SYSMMU, /* Exynos sysmmu */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 81b54f88e88..29c5ccb2144 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -58,8 +58,6 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(MAXIM_MAX77686_PMIC, "maxim,max77686"), COMPAT(GENERIC_SPI_FLASH, "spi-flash"), COMPAT(MAXIM_98095_CODEC, "maxim,max98095-codec"), - COMPAT(INFINEON_SLB9635_TPM, "infineon,slb9635-tpm"), - COMPAT(INFINEON_SLB9645_TPM, "infineon,slb9645tt"), COMPAT(SAMSUNG_EXYNOS5_I2C, "samsung,exynos5-hsi2c"), COMPAT(SANDBOX_LCD_SDL, "sandbox,lcd-sdl"), COMPAT(SAMSUNG_EXYNOS_SYSMMU, "samsung,sysmmu-v3.3"), -- cgit v1.3.1 From 2132f971ba2443bc31046cbbf18bbf5e7c017b50 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 22 Aug 2015 18:31:41 -0600 Subject: tpm: Add functions to access flags and permissions Add a few new functions which will be used by the test command in a future patch. Signed-off-by: Simon Glass Acked-by: Christophe Ricard Reviewed-by: Heiko Schocher --- include/tpm.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/tpm.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/tpm.h b/include/tpm.h index 445952b055d..086b672718e 100644 --- a/include/tpm.h +++ b/include/tpm.h @@ -49,6 +49,15 @@ enum tpm_nv_index { TPM_NV_INDEX_DIR = 0x10000001, }; +#define TPM_NV_PER_GLOBALLOCK (1U << 15) +#define TPM_NV_PER_PPWRITE (1U << 0) +#define TPM_NV_PER_READ_STCLEAR (1U << 31) +#define TPM_NV_PER_WRITE_STCLEAR (1U << 14) + +enum { + TPM_PUBEK_SIZE = 256, +}; + /** * TPM return codes as defined in the TCG Main specification * (TPM Main Part 2 Structures; Specification version 1.2) @@ -163,6 +172,30 @@ enum tpm_return_code { TPM_DEFEND_LOCK_RUNNING = TPM_BASE + TPM_NON_FATAL + 3, }; +struct tpm_permanent_flags { + __be16 tag; + u8 disable; + u8 ownership; + u8 deactivated; + u8 read_pubek; + u8 disable_owner_clear; + u8 allow_maintenance; + u8 physical_presence_lifetime_lock; + u8 physical_presence_hw_enable; + u8 physical_presence_cmd_enable; + u8 cekp_used; + u8 tpm_post; + u8 tpm_post_lock; + u8 fips; + u8 operator; + u8 enable_revoke_ek; + u8 nv_locked; + u8 read_srk_pub; + u8 tpm_established; + u8 maintenance_done; + u8 disable_full_da_logic_info; +} __packed; + #ifdef CONFIG_DM_TPM /* Max buffer size supported by our tpm */ @@ -551,4 +584,20 @@ uint32_t tpm_load_key2_oiap(uint32_t parent_handle, uint32_t tpm_get_pub_key_oiap(uint32_t key_handle, const void *usage_auth, void *pubkey, size_t *pubkey_len); +/** + * Get the TPM permanent flags value + * + * @param pflags Place to put permanent flags + * @return return code of the operation + */ +uint32_t tpm_get_permanent_flags(struct tpm_permanent_flags *pflags); + +/** + * Get the TPM permissions + * + * @param perm Returns permissions value + * @return return code of the operation + */ +uint32_t tpm_get_permissions(uint32_t index, uint32_t *perm); + #endif /* __TPM_H */ diff --git a/lib/tpm.c b/lib/tpm.c index 19bf0b59905..5d5f707e372 100644 --- a/lib/tpm.c +++ b/lib/tpm.c @@ -18,7 +18,6 @@ /* Useful constants */ enum { COMMAND_BUFFER_SIZE = 256, - TPM_PUBEK_SIZE = 256, TPM_REQUEST_HEADER_LENGTH = 10, TPM_RESPONSE_HEADER_LENGTH = 10, PCR_DIGEST_LENGTH = 20, @@ -610,6 +609,56 @@ uint32_t tpm_get_capability(uint32_t cap_area, uint32_t sub_cap, return 0; } +uint32_t tpm_get_permanent_flags(struct tpm_permanent_flags *pflags) +{ + const uint8_t command[22] = { + 0x0, 0xc1, /* TPM_TAG */ + 0x0, 0x0, 0x0, 0x16, /* parameter size */ + 0x0, 0x0, 0x0, 0x65, /* TPM_COMMAND_CODE */ + 0x0, 0x0, 0x0, 0x4, /* TPM_CAP_FLAG_PERM */ + 0x0, 0x0, 0x0, 0x4, /* subcap size */ + 0x0, 0x0, 0x1, 0x8, /* subcap value */ + }; + uint8_t response[COMMAND_BUFFER_SIZE]; + size_t response_length = sizeof(response); + uint32_t err; + + err = tpm_sendrecv_command(command, response, &response_length); + if (err) + return err; + memcpy(pflags, response + TPM_HEADER_SIZE, sizeof(*pflags)); + + return 0; +} + +uint32_t tpm_get_permissions(uint32_t index, uint32_t *perm) +{ + const uint8_t command[22] = { + 0x0, 0xc1, /* TPM_TAG */ + 0x0, 0x0, 0x0, 0x16, /* parameter size */ + 0x0, 0x0, 0x0, 0x65, /* TPM_COMMAND_CODE */ + 0x0, 0x0, 0x0, 0x11, + 0x0, 0x0, 0x0, 0x4, + }; + const size_t index_offset = 18; + const size_t perm_offset = 60; + uint8_t buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE]; + size_t response_length = sizeof(response); + uint32_t err; + + if (pack_byte_string(buf, sizeof(buf), "d", 0, command, sizeof(command), + index_offset, index)) + return TPM_LIB_ERROR; + err = tpm_sendrecv_command(buf, response, &response_length); + if (err) + return err; + if (unpack_byte_string(response, response_length, "d", + perm_offset, perm)) + return TPM_LIB_ERROR; + + return 0; +} + #ifdef CONFIG_TPM_AUTH_SESSIONS /** -- cgit v1.3.1 From e6cabe4a6d142ab981bd31bff0795ed230caf151 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 27 Aug 2015 12:44:28 +0900 Subject: dm: core: allow device_bind() to not return a device pointer This is useful when we want to bind a device, but do not need the pointer to the device. Signed-off-by: Masahiro Yamada Acked-by: Simon Glass --- drivers/core/device.c | 6 ++++-- include/dm/device-internal.h | 4 ++-- include/dm/lists.h | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/core/device.c b/drivers/core/device.c index 01f664796d8..afa4b4fda97 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -32,7 +32,8 @@ int device_bind(struct udevice *parent, const struct driver *drv, struct uclass *uc; int size, ret = 0; - *devp = NULL; + if (devp) + *devp = NULL; if (!name) return -EINVAL; @@ -133,7 +134,8 @@ int device_bind(struct udevice *parent, const struct driver *drv, if (parent) dm_dbg("Bound device %s to %s\n", dev->name, parent->name); - *devp = dev; + if (devp) + *devp = dev; dev->flags |= DM_FLAG_BOUND; diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h index 04884f18057..322d35a4789 100644 --- a/include/dm/device-internal.h +++ b/include/dm/device-internal.h @@ -31,7 +31,7 @@ struct udevice; * devices which use device tree. * @of_offset: Offset of device tree node for this device. This is -1 for * devices which don't use device tree. - * @devp: Returns a pointer to the bound device + * @devp: if non-NULL, returns a pointer to the bound device * @return 0 if OK, -ve on error */ int device_bind(struct udevice *parent, const struct driver *drv, @@ -48,7 +48,7 @@ int device_bind(struct udevice *parent, const struct driver *drv, * @pre_reloc_only: If true, bind the driver only if its DM_INIT_F flag is set. * If false bind the driver always. * @info: Name and platdata for this device - * @devp: Returns a pointer to the bound device + * @devp: if non-NULL, returns a pointer to the bound device * @return 0 if OK, -ve on error */ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, diff --git a/include/dm/lists.h b/include/dm/lists.h index 61610e69aa5..4513d6a311a 100644 --- a/include/dm/lists.h +++ b/include/dm/lists.h @@ -68,7 +68,7 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset, * @parent: Parent device * @drv_name: Name of driver to attach to this parent * @dev_name: Name of the new device thus created - * @devp: Returns the newly bound device + * @devp: If non-NULL, returns the newly bound device */ int device_bind_driver(struct udevice *parent, const char *drv_name, const char *dev_name, struct udevice **devp); @@ -83,7 +83,7 @@ int device_bind_driver(struct udevice *parent, const char *drv_name, * @drv_name: Name of driver to attach to this parent * @dev_name: Name of the new device thus created * @node: Device tree node - * @devp: Returns the newly bound device + * @devp: If non-NULL, returns the newly bound device */ int device_bind_driver_to_node(struct udevice *parent, const char *drv_name, const char *dev_name, int node, -- cgit v1.3.1 From d90a5a30dec16442fc64154e40018bdbb37776a9 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 27 Aug 2015 12:44:29 +0900 Subject: pinctrl: add pin control uclass support This creates a new framework for handling of pin control devices, i.e. devices that control different aspects of package pins. This uclass handles pinmuxing and pin configuration; pinmuxing controls switching among silicon blocks that share certain physical pins, pin configuration handles electronic properties such as pin- biasing, load capacitance etc. This framework can support the same device tree bindings, but if you do not need full interface support, you can disable some features to reduce memory foot print. Typically around 1.5KB is necessary to include full-featured uclass support on ARM board (CONFIG_PINCTRL + CONFIG_PINCTRL_FULL + CONFIG_PINCTRL_GENERIC + CONFIG_PINCTRL_PINMUX), for example. We are often limited on code size for SPL. Besides, we still have many boards that do not support device tree configuration. The full pinctrl, which requires OF_CONTROL, does not make sense for those boards. So, this framework also has a Do-It-Yourself (let's say simple pinctrl) interface. With CONFIG_PINCTRL_FULL disabled, the uclass itself provides no systematic mechanism for identifying the peripheral device, applying pinctrl settings, etc. They must be done in each low-level driver. In return, you can save much memory footprint and it might be useful especially for SPL. Signed-off-by: Masahiro Yamada Acked-by: Simon Glass --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/core/device.c | 4 + drivers/pinctrl/Kconfig | 101 +++++++++++ drivers/pinctrl/Makefile | 2 + drivers/pinctrl/pinctrl-generic.c | 359 ++++++++++++++++++++++++++++++++++++++ drivers/pinctrl/pinctrl-uclass.c | 240 +++++++++++++++++++++++++ include/dm/pinctrl.h | 227 ++++++++++++++++++++++++ include/dm/uclass-id.h | 2 + 9 files changed, 938 insertions(+) create mode 100644 drivers/pinctrl/Kconfig create mode 100644 drivers/pinctrl/Makefile create mode 100644 drivers/pinctrl/pinctrl-generic.c create mode 100644 drivers/pinctrl/pinctrl-uclass.c create mode 100644 include/dm/pinctrl.h (limited to 'include') diff --git a/drivers/Kconfig b/drivers/Kconfig index 524d73e64c8..63c92c594a7 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -40,6 +40,8 @@ source "drivers/pci/Kconfig" source "drivers/pcmcia/Kconfig" +source "drivers/pinctrl/Kconfig" + source "drivers/power/Kconfig" source "drivers/ram/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index a721ec86dfe..9d0a5959a8e 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_$(SPL_)DM) += core/ obj-$(CONFIG_$(SPL_)CLK) += clk/ obj-$(CONFIG_$(SPL_)LED) += led/ +obj-$(CONFIG_$(SPL_)PINCTRL) += pinctrl/ obj-$(CONFIG_$(SPL_)RAM) += ram/ ifdef CONFIG_SPL_BUILD diff --git a/drivers/core/device.c b/drivers/core/device.c index afa4b4fda97..a6cd93698f5 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -286,6 +287,9 @@ int device_probe_child(struct udevice *dev, void *parent_priv) dev->flags |= DM_FLAG_ACTIVATED; + /* continue regardless of the result of pinctrl */ + pinctrl_select_state(dev, "default"); + ret = uclass_pre_probe_device(dev); if (ret) goto fail; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig new file mode 100644 index 00000000000..6ac56ebe7d0 --- /dev/null +++ b/drivers/pinctrl/Kconfig @@ -0,0 +1,101 @@ +# +# PINCTRL infrastructure and drivers +# + +menu "Pin controllers" + +config PINCTRL + bool "Support pin controllers" + depends on DM + help + This enables the basic support for pinctrl framework. You may want + to enable some more options depending on what you want to do. + +config PINCTRL_FULL + bool "Support full pin controllers" + depends on PINCTRL && OF_CONTROL + default y + help + This provides Linux-compatible device tree interface for the pinctrl + subsystem. This feature depends on device tree configuration because + it parses a device tree to look for the pinctrl device which the + peripheral device is associated with. + + If this option is disabled (it is the only possible choice for non-DT + boards), the pinctrl core provides no systematic mechanism for + identifying peripheral devices, applying needed pinctrl settings. + It is totally up to the implementation of each low-level driver. + You can save memory footprint in return for some limitations. + +config PINCTRL_GENERIC + bool "Support generic pin controllers" + depends on PINCTRL_FULL + default y + help + Say Y here if you want to use the pinctrl subsystem through the + generic DT interface. If enabled, some functions become available + to parse common properties such as "pins", "groups", "functions" and + some pin configuration parameters. It would be easier if you only + need the generic DT interface for pin muxing and pin configuration. + If you need to handle vendor-specific DT properties, you can disable + this option and implement your own set_state callback in the pinctrl + operations. + +config PINMUX + bool "Support pin multiplexing controllers" + depends on PINCTRL_GENERIC + default y + help + This option enables pin multiplexing through the generic pinctrl + framework. + +config PINCONF + bool "Support pin configuration controllers" + depends on PINCTRL_GENERIC + help + This option enables pin configuration through the generic pinctrl + framework. + +config SPL_PINCTRL + bool "Support pin controlloers in SPL" + depends on SPL && SPL_DM + help + This option is an SPL-variant of the PINCTRL option. + See the help of PINCTRL for details. + +config SPL_PINCTRL_FULL + bool "Support full pin controllers in SPL" + depends on SPL_PINCTRL && SPL_OF_CONTROL + default y + help + This option is an SPL-variant of the PINCTRL_FULL option. + See the help of PINCTRL_FULL for details. + +config SPL_PINCTRL_GENERIC + bool "Support generic pin controllers in SPL" + depends on SPL_PINCTRL_FULL + default y + help + This option is an SPL-variant of the PINCTRL_GENERIC option. + See the help of PINCTRL_GENERIC for details. + +config SPL_PINMUX + bool "Support pin multiplexing controllers in SPL" + depends on SPL_PINCTRL_GENERIC + default y + help + This option is an SPL-variant of the PINMUX option. + See the help of PINMUX for details. + +config SPL_PINCONF + bool "Support pin configuration controllers in SPL" + depends on SPL_PINCTRL_GENERIC + help + This option is an SPL-variant of the PINCONF option. + See the help of PINCONF for details. + +if PINCTRL || SPL_PINCTRL + +endif + +endmenu diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile new file mode 100644 index 00000000000..a713c7db905 --- /dev/null +++ b/drivers/pinctrl/Makefile @@ -0,0 +1,2 @@ +obj-y += pinctrl-uclass.o +obj-$(CONFIG_$(SPL_)PINCTRL_GENERIC) += pinctrl-generic.o diff --git a/drivers/pinctrl/pinctrl-generic.c b/drivers/pinctrl/pinctrl-generic.c new file mode 100644 index 00000000000..e86b72a8dee --- /dev/null +++ b/drivers/pinctrl/pinctrl-generic.c @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2015 Masahiro Yamada + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/** + * pinctrl_pin_name_to_selector() - return the pin selector for a pin + * + * @dev: pin controller device + * @pin: the pin name to look up + * @return: pin selector, or negative error code on failure + */ +static int pinctrl_pin_name_to_selector(struct udevice *dev, const char *pin) +{ + const struct pinctrl_ops *ops = pinctrl_get_ops(dev); + unsigned npins, selector; + + if (!ops->get_pins_count || !ops->get_pin_name) { + dev_dbg(dev, "get_pins_count or get_pin_name missing\n"); + return -ENOSYS; + } + + npins = ops->get_pins_count(dev); + + /* See if this pctldev has this pin */ + for (selector = 0; selector < npins; selector++) { + const char *pname = ops->get_pin_name(dev, selector); + + if (!strcmp(pin, pname)) + return selector; + } + + return -ENOSYS; +} + +/** + * pinctrl_group_name_to_selector() - return the group selector for a group + * + * @dev: pin controller device + * @group: the pin group name to look up + * @return: pin group selector, or negative error code on failure + */ +static int pinctrl_group_name_to_selector(struct udevice *dev, + const char *group) +{ + const struct pinctrl_ops *ops = pinctrl_get_ops(dev); + unsigned ngroups, selector; + + if (!ops->get_groups_count || !ops->get_group_name) { + dev_dbg(dev, "get_groups_count or get_group_name missing\n"); + return -ENOSYS; + } + + ngroups = ops->get_groups_count(dev); + + /* See if this pctldev has this group */ + for (selector = 0; selector < ngroups; selector++) { + const char *gname = ops->get_group_name(dev, selector); + + if (!strcmp(group, gname)) + return selector; + } + + return -ENOSYS; +} + +#if CONFIG_IS_ENABLED(PINMUX) +/** + * pinmux_func_name_to_selector() - return the function selector for a function + * + * @dev: pin controller device + * @function: the function name to look up + * @return: function selector, or negative error code on failure + */ +static int pinmux_func_name_to_selector(struct udevice *dev, + const char *function) +{ + const struct pinctrl_ops *ops = pinctrl_get_ops(dev); + unsigned nfuncs, selector = 0; + + if (!ops->get_functions_count || !ops->get_function_name) { + dev_dbg(dev, + "get_functions_count or get_function_name missing\n"); + return -ENOSYS; + } + + nfuncs = ops->get_functions_count(dev); + + /* See if this pctldev has this function */ + for (selector = 0; selector < nfuncs; selector++) { + const char *fname = ops->get_function_name(dev, selector); + + if (!strcmp(function, fname)) + return selector; + } + + return -ENOSYS; +} + +/** + * pinmux_enable_setting() - enable pin-mux setting for a certain pin/group + * + * @dev: pin controller device + * @is_group: target of operation (true: pin group, false: pin) + * @selector: pin selector or group selector, depending on @is_group + * @func_selector: function selector + * @return: 0 on success, or negative error code on failure + */ +static int pinmux_enable_setting(struct udevice *dev, bool is_group, + unsigned selector, unsigned func_selector) +{ + const struct pinctrl_ops *ops = pinctrl_get_ops(dev); + + if (is_group) { + if (!ops->pinmux_group_set) { + dev_dbg(dev, "pinmux_group_set op missing\n"); + return -ENOSYS; + } + + return ops->pinmux_group_set(dev, selector, func_selector); + } else { + if (!ops->pinmux_set) { + dev_dbg(dev, "pinmux_set op missing\n"); + return -ENOSYS; + } + return ops->pinmux_set(dev, selector, func_selector); + } +} +#else +static int pinmux_func_name_to_selector(struct udevice *dev, + const char *function) +{ + return 0; +} + +static int pinmux_enable_setting(struct udevice *dev, bool is_group, + unsigned selector, unsigned func_selector) +{ + return 0; +} +#endif + +#if CONFIG_IS_ENABLED(PINCONF) +/** + * pinconf_prop_name_to_param() - return parameter ID for a property name + * + * @dev: pin controller device + * @property: property name in DTS, such as "bias-pull-up", "slew-rate", etc. + * @default_value: return default value in case no value is specified in DTS + * @return: return pamater ID, or negative error code on failure + */ +static int pinconf_prop_name_to_param(struct udevice *dev, + const char *property, u32 *default_value) +{ + const struct pinctrl_ops *ops = pinctrl_get_ops(dev); + const struct pinconf_param *p, *end; + + if (!ops->pinconf_num_params || !ops->pinconf_params) { + dev_dbg(dev, "pinconf_num_params or pinconf_params missing\n"); + return -ENOSYS; + } + + p = ops->pinconf_params; + end = p + ops->pinconf_num_params; + + /* See if this pctldev supports this parameter */ + for (; p < end; p++) { + if (!strcmp(property, p->property)) { + *default_value = p->default_value; + return p->param; + } + } + + return -ENOSYS; +} + +/** + * pinconf_enable_setting() - apply pin configuration for a certain pin/group + * + * @dev: pin controller device + * @is_group: target of operation (true: pin group, false: pin) + * @selector: pin selector or group selector, depending on @is_group + * @param: configuration paramter + * @argument: argument taken by some configuration parameters + * @return: 0 on success, or negative error code on failure + */ +static int pinconf_enable_setting(struct udevice *dev, bool is_group, + unsigned selector, unsigned param, + u32 argument) +{ + const struct pinctrl_ops *ops = pinctrl_get_ops(dev); + + if (is_group) { + if (!ops->pinconf_group_set) { + dev_dbg(dev, "pinconf_group_set op missing\n"); + return -ENOSYS; + } + + return ops->pinconf_group_set(dev, selector, param, + argument); + } else { + if (!ops->pinconf_set) { + dev_dbg(dev, "pinconf_set op missing\n"); + return -ENOSYS; + } + return ops->pinconf_set(dev, selector, param, argument); + } +} +#else +static int pinconf_prop_name_to_param(struct udevice *dev, + const char *property, u32 *default_value) +{ + return -ENOSYS; +} + +static int pinconf_enable_setting(struct udevice *dev, bool is_group, + unsigned selector, unsigned param, + u32 argument) +{ + return 0; +} +#endif + +/** + * pinctrl_generic_set_state_one() - set state for a certain pin/group + * Apply all pin multiplexing and pin configurations specified by @config + * for a given pin or pin group. + * + * @dev: pin controller device + * @config: pseudo device pointing to config node + * @is_group: target of operation (true: pin group, false: pin) + * @selector: pin selector or group selector, depending on @is_group + * @return: 0 on success, or negative error code on failure + */ +static int pinctrl_generic_set_state_one(struct udevice *dev, + struct udevice *config, + bool is_group, unsigned selector) +{ + const void *fdt = gd->fdt_blob; + int node_offset = config->of_offset; + const char *propname; + const void *value; + int prop_offset, len, func_selector, param, ret; + u32 arg, default_val; + + for (prop_offset = fdt_first_property_offset(fdt, node_offset); + prop_offset > 0; + prop_offset = fdt_next_property_offset(fdt, prop_offset)) { + value = fdt_getprop_by_offset(fdt, prop_offset, + &propname, &len); + if (!value) + return -EINVAL; + + if (!strcmp(propname, "function")) { + func_selector = pinmux_func_name_to_selector(dev, + value); + if (func_selector < 0) + return func_selector; + ret = pinmux_enable_setting(dev, is_group, + selector, + func_selector); + } else { + param = pinconf_prop_name_to_param(dev, propname, + &default_val); + if (param < 0) + continue; /* just skip unknown properties */ + + if (len >= sizeof(fdt32_t)) + arg = fdt32_to_cpu(*(fdt32_t *)value); + else + arg = default_val; + + ret = pinconf_enable_setting(dev, is_group, + selector, param, arg); + } + + if (ret) + return ret; + } + + return 0; +} + +/** + * pinctrl_generic_set_state_subnode() - apply all settings in config node + * + * @dev: pin controller device + * @config: pseudo device pointing to config node + * @return: 0 on success, or negative error code on failure + */ +static int pinctrl_generic_set_state_subnode(struct udevice *dev, + struct udevice *config) +{ + const void *fdt = gd->fdt_blob; + int node = config->of_offset; + const char *subnode_target_type = "pins"; + bool is_group = false; + const char *name; + int strings_count, selector, i, ret; + + strings_count = fdt_count_strings(fdt, node, subnode_target_type); + if (strings_count < 0) { + subnode_target_type = "groups"; + is_group = true; + strings_count = fdt_count_strings(fdt, node, + subnode_target_type); + if (strings_count < 0) + return -EINVAL; + } + + for (i = 0; i < strings_count; i++) { + ret = fdt_get_string_index(fdt, node, subnode_target_type, + i, &name); + if (ret < 0) + return -EINVAL; + + if (is_group) + selector = pinctrl_group_name_to_selector(dev, name); + else + selector = pinctrl_pin_name_to_selector(dev, name); + if (selector < 0) + return selector; + + ret = pinctrl_generic_set_state_one(dev, config, + is_group, selector); + if (ret) + return ret; + } + + return 0; +} + +int pinctrl_generic_set_state(struct udevice *dev, struct udevice *config) +{ + struct udevice *child; + int ret; + + ret = pinctrl_generic_set_state_subnode(dev, config); + if (ret) + return ret; + + for (device_find_first_child(config, &child); + child; + device_find_next_child(&child)) { + ret = pinctrl_generic_set_state_subnode(dev, child); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c new file mode 100644 index 00000000000..d96c201e831 --- /dev/null +++ b/drivers/pinctrl/pinctrl-uclass.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2015 Masahiro Yamada + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#if CONFIG_IS_ENABLED(PINCTRL_FULL) +/** + * pinctrl_config_one() - apply pinctrl settings for a single node + * + * @config: pin configuration node + * @return: 0 on success, or negative error code on failure + */ +static int pinctrl_config_one(struct udevice *config) +{ + struct udevice *pctldev; + const struct pinctrl_ops *ops; + + pctldev = config; + for (;;) { + pctldev = dev_get_parent(pctldev); + if (!pctldev) { + dev_err(config, "could not find pctldev\n"); + return -EINVAL; + } + if (pctldev->uclass->uc_drv->id == UCLASS_PINCTRL) + break; + } + + ops = pinctrl_get_ops(pctldev); + return ops->set_state(pctldev, config); +} + +/** + * pinctrl_select_state_full() - full implementation of pinctrl_select_state + * + * @dev: peripheral device + * @statename: state name, like "default" + * @return: 0 on success, or negative error code on failure + */ +static int pinctrl_select_state_full(struct udevice *dev, const char *statename) +{ + const void *fdt = gd->fdt_blob; + int node = dev->of_offset; + char propname[32]; /* long enough */ + const fdt32_t *list; + uint32_t phandle; + int config_node; + struct udevice *config; + int state, size, i, ret; + + state = fdt_find_string(fdt, node, "pinctrl-names", statename); + if (state < 0) { + char *end; + /* + * If statename is not found in "pinctrl-names", + * assume statename is just the integer state ID. + */ + state = simple_strtoul(statename, &end, 10); + if (*end) + return -EINVAL; + } + + snprintf(propname, sizeof(propname), "pinctrl-%d", state); + list = fdt_getprop(fdt, node, propname, &size); + if (!list) + return -EINVAL; + + size /= sizeof(*list); + for (i = 0; i < size; i++) { + phandle = fdt32_to_cpu(*list++); + + config_node = fdt_node_offset_by_phandle(fdt, phandle); + if (config_node < 0) { + dev_err(dev, "prop %s index %d invalid phandle\n", + propname, i); + return -EINVAL; + } + ret = uclass_get_device_by_of_offset(UCLASS_PINCONFIG, + config_node, &config); + if (ret) + return ret; + + ret = pinctrl_config_one(config); + if (ret) + return ret; + } + + return 0; +} + +/** + * pinconfig_post-bind() - post binding for PINCONFIG uclass + * Recursively bind its children as pinconfig devices. + * + * @dev: pinconfig device + * @return: 0 on success, or negative error code on failure + */ +static int pinconfig_post_bind(struct udevice *dev) +{ + const void *fdt = gd->fdt_blob; + int offset = dev->of_offset; + const char *name; + int ret; + + for (offset = fdt_first_subnode(fdt, offset); + offset > 0; + offset = fdt_next_subnode(fdt, offset)) { + /* + * If this node has "compatible" property, this is not + * a pin configuration node, but a normal device. skip. + */ + fdt_get_property(fdt, offset, "compatible", &ret); + if (ret >= 0) + continue; + + if (ret != -FDT_ERR_NOTFOUND) + return ret; + + name = fdt_get_name(fdt, offset, NULL); + if (!name) + return -EINVAL; + ret = device_bind_driver_to_node(dev, "pinconfig", name, + offset, NULL); + if (ret) + return ret; + } + + return 0; +} + +UCLASS_DRIVER(pinconfig) = { + .id = UCLASS_PINCONFIG, + .post_bind = pinconfig_post_bind, + .name = "pinconfig", +}; + +U_BOOT_DRIVER(pinconfig_generic) = { + .name = "pinconfig", + .id = UCLASS_PINCONFIG, +}; + +#else +static int pinctrl_select_state_full(struct udevice *dev, const char *statename) +{ + return -ENODEV; +} + +static int pinconfig_post_bind(struct udevice *dev) +{ + return 0; +} +#endif + +/** + * pinctrl_select_state_simple() - simple implementation of pinctrl_select_state + * + * @dev: peripheral device + * @return: 0 on success, or negative error code on failure + */ +static int pinctrl_select_state_simple(struct udevice *dev) +{ + struct udevice *pctldev; + struct pinctrl_ops *ops; + int ret; + + /* + * For simplicity, assume the first device of PINCTRL uclass + * is the correct one. This is most likely OK as there is + * usually only one pinctrl device on the system. + */ + ret = uclass_get_device(UCLASS_PINCTRL, 0, &pctldev); + if (ret) + return ret; + + ops = pinctrl_get_ops(pctldev); + if (!ops->set_state_simple) { + dev_dbg(dev, "set_state_simple op missing\n"); + return -ENOSYS; + } + + return ops->set_state_simple(pctldev, dev); +} + +int pinctrl_select_state(struct udevice *dev, const char *statename) +{ + /* + * Try full-implemented pinctrl first. + * If it fails or is not implemented, try simple one. + */ + if (pinctrl_select_state_full(dev, statename)) + return pinctrl_select_state_simple(dev); + + return 0; +} + +/** + * pinconfig_post-bind() - post binding for PINCTRL uclass + * Recursively bind child nodes as pinconfig devices in case of full pinctrl. + * + * @dev: pinctrl device + * @return: 0 on success, or negative error code on failure + */ +static int pinctrl_post_bind(struct udevice *dev) +{ + const struct pinctrl_ops *ops = pinctrl_get_ops(dev); + + if (!ops) { + dev_dbg(dev, "ops is not set. Do not bind.\n"); + return -EINVAL; + } + + /* + * If set_state callback is set, we assume this pinctrl driver is the + * full implementation. In this case, its child nodes should be bound + * so that peripheral devices can easily search in parent devices + * during later DT-parsing. + */ + if (ops->set_state) + return pinconfig_post_bind(dev); + + return 0; +} + +UCLASS_DRIVER(pinctrl) = { + .id = UCLASS_PINCTRL, + .post_bind = pinctrl_post_bind, + .name = "pinctrl", +}; diff --git a/include/dm/pinctrl.h b/include/dm/pinctrl.h new file mode 100644 index 00000000000..bc6fdb4a1f1 --- /dev/null +++ b/include/dm/pinctrl.h @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2015 Masahiro Yamada + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __PINCTRL_H +#define __PINCTRL_H + +/** + * struct pinconf_param - pin config parameters + * + * @property: property name in DT nodes + * @param: ID for this config parameter + * @default_value: default value for this config parameter used in case + * no value is specified in DT nodes + */ +struct pinconf_param { + const char * const property; + unsigned int param; + u32 default_value; +}; + +/** + * struct pinctrl_ops - pin control operations, to be implemented by + * pin controller drivers. + * + * The @set_state is the only mandatory operation. You can implement your + * pinctrl driver with its own @set_state. In this case, the other callbacks + * are not required. Otherwise, generic pinctrl framework is also available; + * use pinctrl_generic_set_state for @set_state, and implement other operations + * depending on your necessity. + * + * @get_pins_count: return number of selectable named pins available + * in this driver. (necessary to parse "pins" property in DTS) + * @get_pin_name: return the pin name of the pin selector, + * called by the core to figure out which pin it shall do + * operations to. (necessary to parse "pins" property in DTS) + * @get_groups_count: return number of selectable named groups available + * in this driver. (necessary to parse "groups" property in DTS) + * @get_group_name: return the group name of the group selector, + * called by the core to figure out which pin group it shall do + * operations to. (necessary to parse "groups" property in DTS) + * @get_functions_count: return number of selectable named functions available + * in this driver. (necessary for pin-muxing) + * @get_function_name: return the function name of the muxing selector, + * called by the core to figure out which mux setting it shall map a + * certain device to. (necessary for pin-muxing) + * @pinmux_set: enable a certain muxing function with a certain pin. + * The @func_selector selects a certain function whereas @pin_selector + * selects a certain pin to be used. On simple controllers one of them + * may be ignored. (necessary for pin-muxing against a single pin) + * @pinmux_group_set: enable a certain muxing function with a certain pin + * group. The @func_selector selects a certain function whereas + * @group_selector selects a certain set of pins to be used. On simple + * controllers one of them may be ignored. + * (necessary for pin-muxing against a pin group) + * @pinconf_num_params: number of driver-specific parameters to be parsed + * from device trees (necessary for pin-configuration) + * @pinconf_params: list of driver_specific parameters to be parsed from + * device trees (necessary for pin-configuration) + * @pinconf_set: configure an individual pin with a given parameter. + * (necessary for pin-configuration against a single pin) + * @pinconf_group_set: configure all pins in a group with a given parameter. + * (necessary for pin-configuration against a pin group) + * @set_state: do pinctrl operations specified by @config, a pseudo device + * pointing a config node. (necessary for pinctrl_full) + * @set_state_simple: do needed pinctrl operations for a peripherl @periph. + * (necessary for pinctrl_simple) + */ +struct pinctrl_ops { + int (*get_pins_count)(struct udevice *dev); + const char *(*get_pin_name)(struct udevice *dev, unsigned selector); + int (*get_groups_count)(struct udevice *dev); + const char *(*get_group_name)(struct udevice *dev, unsigned selector); + int (*get_functions_count)(struct udevice *dev); + const char *(*get_function_name)(struct udevice *dev, + unsigned selector); + int (*pinmux_set)(struct udevice *dev, unsigned pin_selector, + unsigned func_selector); + int (*pinmux_group_set)(struct udevice *dev, unsigned group_selector, + unsigned func_selector); + unsigned int pinconf_num_params; + const struct pinconf_param *pinconf_params; + int (*pinconf_set)(struct udevice *dev, unsigned pin_selector, + unsigned param, unsigned argument); + int (*pinconf_group_set)(struct udevice *dev, unsigned group_selector, + unsigned param, unsigned argument); + int (*set_state)(struct udevice *dev, struct udevice *config); + int (*set_state_simple)(struct udevice *dev, struct udevice *periph); +}; + +#define pinctrl_get_ops(dev) ((struct pinctrl_ops *)(dev)->driver->ops) + +/** + * Generic pin configuration paramters + * + * @PIN_CONFIG_BIAS_DISABLE: disable any pin bias on the pin, a + * transition from say pull-up to pull-down implies that you disable + * pull-up in the process, this setting disables all biasing. + * @PIN_CONFIG_BIAS_HIGH_IMPEDANCE: the pin will be set to a high impedance + * mode, also know as "third-state" (tristate) or "high-Z" or "floating". + * On output pins this effectively disconnects the pin, which is useful + * if for example some other pin is going to drive the signal connected + * to it for a while. Pins used for input are usually always high + * impedance. + * @PIN_CONFIG_BIAS_BUS_HOLD: the pin will be set to weakly latch so that it + * weakly drives the last value on a tristate bus, also known as a "bus + * holder", "bus keeper" or "repeater". This allows another device on the + * bus to change the value by driving the bus high or low and switching to + * tristate. The argument is ignored. + * @PIN_CONFIG_BIAS_PULL_UP: the pin will be pulled up (usually with high + * impedance to VDD). If the argument is != 0 pull-up is enabled, + * if it is 0, pull-up is total, i.e. the pin is connected to VDD. + * @PIN_CONFIG_BIAS_PULL_DOWN: the pin will be pulled down (usually with high + * impedance to GROUND). If the argument is != 0 pull-down is enabled, + * if it is 0, pull-down is total, i.e. the pin is connected to GROUND. + * @PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: the pin will be pulled up or down based + * on embedded knowledge of the controller hardware, like current mux + * function. The pull direction and possibly strength too will normally + * be decided completely inside the hardware block and not be readable + * from the kernel side. + * If the argument is != 0 pull up/down is enabled, if it is 0, the + * configuration is ignored. The proper way to disable it is to use + * @PIN_CONFIG_BIAS_DISABLE. + * @PIN_CONFIG_DRIVE_PUSH_PULL: the pin will be driven actively high and + * low, this is the most typical case and is typically achieved with two + * active transistors on the output. Setting this config will enable + * push-pull mode, the argument is ignored. + * @PIN_CONFIG_DRIVE_OPEN_DRAIN: the pin will be driven with open drain (open + * collector) which means it is usually wired with other output ports + * which are then pulled up with an external resistor. Setting this + * config will enable open drain mode, the argument is ignored. + * @PIN_CONFIG_DRIVE_OPEN_SOURCE: the pin will be driven with open source + * (open emitter). Setting this config will enable open source mode, the + * argument is ignored. + * @PIN_CONFIG_DRIVE_STRENGTH: the pin will sink or source at most the current + * passed as argument. The argument is in mA. + * @PIN_CONFIG_INPUT_ENABLE: enable the pin's input. Note that this does not + * affect the pin's ability to drive output. 1 enables input, 0 disables + * input. + * @PIN_CONFIG_INPUT_SCHMITT_ENABLE: control schmitt-trigger mode on the pin. + * If the argument != 0, schmitt-trigger mode is enabled. If it's 0, + * schmitt-trigger mode is disabled. + * @PIN_CONFIG_INPUT_SCHMITT: this will configure an input pin to run in + * schmitt-trigger mode. If the schmitt-trigger has adjustable hysteresis, + * the threshold value is given on a custom format as argument when + * setting pins to this mode. + * @PIN_CONFIG_INPUT_DEBOUNCE: this will configure the pin to debounce mode, + * which means it will wait for signals to settle when reading inputs. The + * argument gives the debounce time in usecs. Setting the + * argument to zero turns debouncing off. + * @PIN_CONFIG_POWER_SOURCE: if the pin can select between different power + * supplies, the argument to this parameter (on a custom format) tells + * the driver which alternative power source to use. + * @PIN_CONFIG_SLEW_RATE: if the pin can select slew rate, the argument to + * this parameter (on a custom format) tells the driver which alternative + * slew rate to use. + * @PIN_CONFIG_LOW_POWER_MODE: this will configure the pin for low power + * operation, if several modes of operation are supported these can be + * passed in the argument on a custom form, else just use argument 1 + * to indicate low power mode, argument 0 turns low power mode off. + * @PIN_CONFIG_OUTPUT: this will configure the pin as an output. Use argument + * 1 to indicate high level, argument 0 to indicate low level. (Please + * see Documentation/pinctrl.txt, section "GPIO mode pitfalls" for a + * discussion around this parameter.) + * @PIN_CONFIG_END: this is the last enumerator for pin configurations, if + * you need to pass in custom configurations to the pin controller, use + * PIN_CONFIG_END+1 as the base offset. + */ +#define PIN_CONFIG_BIAS_DISABLE 0 +#define PIN_CONFIG_BIAS_HIGH_IMPEDANCE 1 +#define PIN_CONFIG_BIAS_BUS_HOLD 2 +#define PIN_CONFIG_BIAS_PULL_UP 3 +#define PIN_CONFIG_BIAS_PULL_DOWN 4 +#define PIN_CONFIG_BIAS_PULL_PIN_DEFAULT 5 +#define PIN_CONFIG_DRIVE_PUSH_PULL 6 +#define PIN_CONFIG_DRIVE_OPEN_DRAIN 7 +#define PIN_CONFIG_DRIVE_OPEN_SOURCE 8 +#define PIN_CONFIG_DRIVE_STRENGTH 9 +#define PIN_CONFIG_INPUT_ENABLE 10 +#define PIN_CONFIG_INPUT_SCHMITT_ENABLE 11 +#define PIN_CONFIG_INPUT_SCHMITT 12 +#define PIN_CONFIG_INPUT_DEBOUNCE 13 +#define PIN_CONFIG_POWER_SOURCE 14 +#define PIN_CONFIG_SLEW_RATE 15 +#define PIN_CONFIG_LOW_POWER_MODE 16 +#define PIN_CONFIG_OUTPUT 17 +#define PIN_CONFIG_END 0x7FFF + +#if CONFIG_IS_ENABLED(PINCTRL_GENERIC) +/** + * pinctrl_generic_set_state() - generic set_state operation + * Parse the DT node of @config and its children and handle generic properties + * such as "pins", "groups", "functions", and pin configuration parameters. + * + * @pctldev: pinctrl device + * @config: config device (pseudo device), pointing a config node in DTS + * @return: 0 on success, or negative error code on failure + */ +int pinctrl_generic_set_state(struct udevice *pctldev, struct udevice *config); +#else +static inline int pinctrl_generic_set_state(struct udevice *pctldev, + struct udevice *config) +{ + return -EINVAL; +} +#endif + +#if CONFIG_IS_ENABLED(PINCTRL) +/** + * pinctrl_select_state() - set a device to a given state + * + * @dev: peripheral device + * @statename: state name, like "default" + * @return: 0 on success, or negative error code on failure + */ +int pinctrl_select_state(struct udevice *dev, const char *statename); +#else +static inline int pinctrl_select_state(struct udevice *dev, + const char *statename) +{ + return -EINVAL; +} +#endif + +#endif /* __PINCTRL_H */ diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 3eff895ab7a..1eeec749643 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -44,6 +44,8 @@ enum uclass_id { UCLASS_PCH, /* x86 platform controller hub */ UCLASS_PCI, /* PCI bus */ UCLASS_PCI_GENERIC, /* Generic PCI bus device */ + UCLASS_PINCTRL, /* Pinctrl (pin muxing/configuration) device */ + UCLASS_PINCONFIG, /* Pin configuration node device */ UCLASS_PMIC, /* PMIC I/O device */ UCLASS_REGULATOR, /* Regulator device */ UCLASS_RESET, /* Reset device */ -- cgit v1.3.1